Merge branch 'fork/xingyu4j/antdv-next'

pull/348/MERGE
Jin Mao 2026-05-19 14:14:28 +08:00
commit 76cfcda2e9
168 changed files with 4489 additions and 4284 deletions

View File

@ -1,5 +0,0 @@
---
'@vben/layouts': patch
---
fix: update primary color when toggling dark/light mode with custom theme

View File

@ -1,5 +0,0 @@
---
'@vben/common-ui': patch
---
fix: skip fixed footer height in auto-content-height calculation

View File

@ -1,5 +0,0 @@
---
'@vben/icons': patch
---
fix: guard svg icon loading during docs SSR

View File

@ -1,5 +0,0 @@
---
'@vben-core/shadcn-ui': patch
---
fix: preserve tree default value when treeData starts empty

12
.npmrc
View File

@ -1,13 +1 @@
registry=https://registry.npmmirror.com registry=https://registry.npmmirror.com
public-hoist-pattern[]=lefthook
public-hoist-pattern[]=eslint
public-hoist-pattern[]=oxfmt
public-hoist-pattern[]=oxlint
public-hoist-pattern[]=stylelint
public-hoist-pattern[]=*postcss*
public-hoist-pattern[]=@commitlint/*
public-hoist-pattern[]=czg
strict-peer-dependencies=false
auto-install-peers=true
dedupe-peer-dependents=true

View File

@ -38,7 +38,7 @@
// lint && format // lint && format
"oxc.enable": true, "oxc.enable": true,
"oxc.typeAware": true, "oxc.typeAware": false,
"oxc.configPath": "oxlint.config.ts", "oxc.configPath": "oxlint.config.ts",
"oxc.fmt.configPath": "oxfmt.config.ts", "oxc.fmt.configPath": "oxfmt.config.ts",
"eslint.useFlatConfig": true, "eslint.useFlatConfig": true,

View File

@ -0,0 +1,85 @@
import { faker } from '@faker-js/faker';
import { eventHandler, getQuery } from 'h3';
import { verifyAccessToken } from '~/utils/jwt-utils';
import { unAuthorizedResponse, usePageResponseSuccess } from '~/utils/response';
const formatterCN = new Intl.DateTimeFormat('zh-CN', {
timeZone: 'Asia/Shanghai',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
});
function generateMockDataList(count: number) {
const dataList = [];
for (let i = 0; i < count; i++) {
const dataItem: Record<string, any> = {
id: faker.string.uuid(),
name: faker.commerce.product(),
status: faker.helpers.arrayElement([0, 1]),
createTime: formatterCN.format(
faker.date.between({ from: '2022-01-01', to: '2025-01-01' }),
),
deptId: faker.string.uuid(),
remark: faker.lorem.sentence(),
};
dataList.push(dataItem);
}
return dataList;
}
const mockData = generateMockDataList(100);
export default eventHandler(async (event) => {
const userinfo = verifyAccessToken(event);
if (!userinfo) {
return unAuthorizedResponse(event);
}
const {
page = 1,
pageSize = 20,
name,
id,
remark,
startTime,
endTime,
deptId,
status,
} = getQuery(event);
let listData = structuredClone(mockData);
if (name) {
listData = listData.filter((item) =>
item.name.toLowerCase().includes(String(name).toLowerCase()),
);
}
if (id) {
listData = listData.filter((item) =>
item.id.toLowerCase().includes(String(id).toLowerCase()),
);
}
if (remark) {
listData = listData.filter((item) =>
item.remark?.toLowerCase()?.includes(String(remark).toLowerCase()),
);
}
if (startTime) {
listData = listData.filter((item) => item.createTime >= startTime);
}
if (endTime) {
listData = listData.filter((item) => item.createTime <= endTime);
}
if (['0', '1'].includes(status as string)) {
listData = listData.filter((item) => item.status === Number(status));
}
if (deptId) {
listData = listData.filter((item) => item.deptId === deptId);
}
return usePageResponseSuccess(page as string, pageSize as string, listData);
});

View File

@ -131,8 +131,8 @@ const PreviewGroup = defineAsyncComponent(() =>
import('ant-design-vue/es/image').then((res) => res.ImagePreviewGroup), import('ant-design-vue/es/image').then((res) => res.ImagePreviewGroup),
); );
const withDefaultPlaceholder = <T extends Component>( const withDefaultPlaceholder = (
component: T, component: Component,
type: 'input' | 'select', type: 'input' | 'select',
componentProps: Recordable<any> = {}, componentProps: Recordable<any> = {},
) => { ) => {
@ -702,7 +702,9 @@ async function initComponentAdapter() {
modelValueProp: 'value', modelValueProp: 'value',
}), }),
Input: withDefaultPlaceholder(Input, 'input'), Input: withDefaultPlaceholder(Input, 'input'),
InputNumber: withDefaultPlaceholder(InputNumber, 'input'), InputNumber: withDefaultPlaceholder(InputNumber, 'input', {
style: { width: '100%' },
}),
InputPassword: withDefaultPlaceholder(InputPassword, 'input'), InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
Mentions: withDefaultPlaceholder(Mentions, 'input'), Mentions: withDefaultPlaceholder(Mentions, 'input'),
// 自定义主要按钮 // 自定义主要按钮

View File

@ -136,8 +136,8 @@ const PreviewGroup = defineAsyncComponent(() =>
import('antdv-next/dist/image/index').then((res) => res.ImagePreviewGroup), import('antdv-next/dist/image/index').then((res) => res.ImagePreviewGroup),
); );
const withDefaultPlaceholder = <T extends Component>( const withDefaultPlaceholder = (
component: T, component: Component,
type: 'input' | 'select', type: 'input' | 'select',
componentProps: Recordable<any> = {}, componentProps: Recordable<any> = {},
) => { ) => {
@ -633,7 +633,9 @@ async function initComponentAdapter() {
modelValueProp: 'value', modelValueProp: 'value',
}), }),
Input: withDefaultPlaceholder(Input, 'input'), Input: withDefaultPlaceholder(Input, 'input'),
InputNumber: withDefaultPlaceholder(InputNumber, 'input'), InputNumber: withDefaultPlaceholder(InputNumber, 'input', {
style: { width: '100%' },
}),
InputPassword: withDefaultPlaceholder(InputPassword, 'input'), InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
Mentions: withDefaultPlaceholder(Mentions, 'input'), Mentions: withDefaultPlaceholder(Mentions, 'input'),
// 自定义主要按钮 // 自定义主要按钮

View File

@ -141,8 +141,8 @@ const ElUpload = defineAsyncComponent(() =>
]).then(([res]) => res.ElUpload), ]).then(([res]) => res.ElUpload),
); );
const withDefaultPlaceholder = <T extends Component>( const withDefaultPlaceholder = (
component: T, component: Component,
type: 'input' | 'select', type: 'input' | 'select',
componentProps: Recordable<any> = {}, componentProps: Recordable<any> = {},
) => { ) => {
@ -284,7 +284,9 @@ async function initComponentAdapter() {
inputComponent: ElInput, inputComponent: ElInput,
}), }),
Input: withDefaultPlaceholder(ElInput, 'input'), Input: withDefaultPlaceholder(ElInput, 'input'),
InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'), InputNumber: withDefaultPlaceholder(ElInputNumber, 'input', {
style: { width: '100%' },
}),
RadioGroup: (props, { attrs, slots }) => { RadioGroup: (props, { attrs, slots }) => {
let defaultSlot; let defaultSlot;
if (Reflect.has(slots, 'default')) { if (Reflect.has(slots, 'default')) {

View File

@ -84,8 +84,8 @@ const NUpload = defineAsyncComponent(() =>
import('naive-ui/es/upload').then((res) => res.NUpload), import('naive-ui/es/upload').then((res) => res.NUpload),
); );
const withDefaultPlaceholder = <T extends Component>( const withDefaultPlaceholder = (
component: T, component: Component,
type: 'input' | 'select', type: 'input' | 'select',
componentProps: Recordable<any> = {}, componentProps: Recordable<any> = {},
) => { ) => {
@ -225,7 +225,9 @@ async function initComponentAdapter() {
inputComponent: NInput, inputComponent: NInput,
}), }),
Input: withDefaultPlaceholder(NInput, 'input'), Input: withDefaultPlaceholder(NInput, 'input'),
InputNumber: withDefaultPlaceholder(NInputNumber, 'input'), InputNumber: withDefaultPlaceholder(NInputNumber, 'input', {
style: { width: '100%' },
}),
RadioGroup: (props, { attrs, slots }) => { RadioGroup: (props, { attrs, slots }) => {
let defaultSlot; let defaultSlot;
if (Reflect.has(slots, 'default')) { if (Reflect.has(slots, 'default')) {

View File

@ -89,8 +89,8 @@ const TreeSelect = defineAsyncComponent(
); );
const Upload = defineAsyncComponent(() => import('tdesign-vue-next/es/upload')); const Upload = defineAsyncComponent(() => import('tdesign-vue-next/es/upload'));
const withDefaultPlaceholder = <T extends Component>( const withDefaultPlaceholder = (
component: T, component: Component,
type: 'input' | 'select', type: 'input' | 'select',
componentProps: Recordable<any> = {}, componentProps: Recordable<any> = {},
) => { ) => {
@ -239,7 +239,9 @@ async function initComponentAdapter() {
modelValueProp: 'value', modelValueProp: 'value',
}), }),
Input: withDefaultPlaceholder(Input, 'input'), Input: withDefaultPlaceholder(Input, 'input'),
InputNumber: withDefaultPlaceholder(InputNumber, 'input'), InputNumber: withDefaultPlaceholder(InputNumber, 'input', {
style: { width: '100%' },
}),
// InputPassword: withDefaultPlaceholder(InputPassword, 'input'), // InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
// Mentions: withDefaultPlaceholder(Mentions, 'input'), // Mentions: withDefaultPlaceholder(Mentions, 'input'),
// 自定义主要按钮 // 自定义主要按钮

View File

@ -0,0 +1,2 @@
import { register } from 'node:module';
register('./dayjs-resolve-hook.mjs', import.meta.url);

View File

@ -0,0 +1,10 @@
const DAYJS_SUBPATH_RE = /^dayjs\/(plugin|locale)\/([^./]+)$/;
/** @type {import('node:module').ResolveHook} */
export async function resolve(specifier, context, nextResolve) {
const match = specifier.match(DAYJS_SUBPATH_RE);
if (match) {
return nextResolve(`${specifier}.js`, context);
}
return nextResolve(specifier, context);
}

View File

@ -1,12 +1,12 @@
import { withPwa } from '@vite-pwa/vitepress'; import { withPwa } from '@vite-pwa/vitepress';
import { defineConfigWithTheme } from 'vitepress'; import { defineConfig } from 'vitepress';
import { en } from './en.mts'; import { en } from './en.mts';
import { shared } from './shared.mts'; import { shared } from './shared.mts';
import { zh } from './zh.mts'; import { zh } from './zh.mts';
export default withPwa( export default withPwa(
defineConfigWithTheme({ defineConfig({
...shared, ...shared,
locales: { locales: {
en: { en: {

View File

@ -5,6 +5,7 @@ import { resolve } from 'node:path';
import { import {
viteArchiverPlugin, viteArchiverPlugin,
viteDayjsPlugin,
viteVxeTableImportsPlugin, viteVxeTableImportsPlugin,
} from '@vben/vite-config'; } from '@vben/vite-config';
@ -61,7 +62,7 @@ export const shared = defineConfig({
}, },
preprocessorOptions: { preprocessorOptions: {
scss: { scss: {
api: 'modern', // api: 'modern',
}, },
}, },
}, },
@ -69,6 +70,7 @@ export const shared = defineConfig({
stringify: true, stringify: true,
}, },
plugins: [ plugins: [
viteDayjsPlugin(),
tailwindcss(), tailwindcss(),
GitChangelog({ GitChangelog({
mapAuthors: [ mapAuthors: [
@ -85,9 +87,23 @@ export const shared = defineConfig({
name: 'Li Kui', name: 'Li Kui',
username: 'likui628', username: 'likui628',
}, },
{
mapByNameAliases: ['Jin Mao', 'jinmao'],
name: 'Jin Mao',
username: 'jinmao88',
},
{
name: 'Netfan',
username: 'mynetfan',
},
{
mapByNameAliases: ['xingyu4j', 'xingyu'],
name: 'xingyu4j',
username: 'xingyu4j',
},
], ],
repoURL: () => 'https://github.com/vbenjs/vue-vben-admin', repoURL: () => 'https://github.com/vbenjs/vue-vben-admin',
}), }) as any,
GitChangelogMarkdownSection(), GitChangelogMarkdownSection(),
viteArchiverPlugin({ outputDir: '.vitepress' }), viteArchiverPlugin({ outputDir: '.vitepress' }),
groupIconVitePlugin(), groupIconVitePlugin(),
@ -103,6 +119,7 @@ export const shared = defineConfig({
ssr: { ssr: {
external: ['@vue/repl'], external: ['@vue/repl'],
noExternal: ['@v-c/picker'],
}, },
}, },
}); });
@ -113,7 +130,7 @@ function head(): HeadConfig[] {
[ [
'meta', 'meta',
{ {
content: 'vben, vitejs, vite, shacdn-ui, vue', content: 'vben, vitejs, vite, shadcn-ui, vue',
name: 'keywords', name: 'keywords',
}, },
], ],

View File

@ -337,8 +337,8 @@ export const search: DefaultTheme.AlgoliaSearchOptions['locales'] = {
footer: { footer: {
closeText: '关闭', closeText: '关闭',
navigateText: '切换', navigateText: '切换',
searchByText: '搜索提供者',
selectText: '选择', selectText: '选择',
poweredByText: '搜索提供者',
}, },
noResultsScreen: { noResultsScreen: {
noResultsText: '无法找到相关结果', noResultsText: '无法找到相关结果',
@ -347,10 +347,10 @@ export const search: DefaultTheme.AlgoliaSearchOptions['locales'] = {
suggestedQueryText: '你可以尝试查询', suggestedQueryText: '你可以尝试查询',
}, },
searchBox: { searchBox: {
cancelButtonAriaLabel: '取消', closeButtonAriaLabel: '取消',
cancelButtonText: '取消', closeButtonText: '取消',
resetButtonAriaLabel: '清除查询条件', clearButtonAriaLabel: '清除查询条件',
resetButtonTitle: '清除查询条件', clearButtonTitle: '清除查询条件',
}, },
startScreen: { startScreen: {
favoriteSearchesTitle: '收藏', favoriteSearchesTitle: '收藏',

View File

@ -10,7 +10,7 @@ import {
// import { useAntdDesignTokens } from '@vben/hooks'; // import { useAntdDesignTokens } from '@vben/hooks';
// import { initPreferences } from '@vben/preferences'; // import { initPreferences } from '@vben/preferences';
import { ConfigProvider, theme } from 'ant-design-vue'; import { ConfigProvider, theme } from 'antdv-next';
import mediumZoom from 'medium-zoom'; import mediumZoom from 'medium-zoom';
import { useRoute } from 'vitepress'; import { useRoute } from 'vitepress';
import DefaultTheme from 'vitepress/theme'; import DefaultTheme from 'vitepress/theme';

View File

@ -2,8 +2,9 @@
"name": "@vben/docs", "name": "@vben/docs",
"version": "5.7.0", "version": "5.7.0",
"private": true, "private": true,
"type": "module",
"scripts": { "scripts": {
"build": "vitepress build", "build": "cross-env NODE_OPTIONS=\"--import ./.vitepress/build/dayjs-loader.mjs\" vitepress build",
"dev": "vitepress dev", "dev": "vitepress dev",
"docs:preview": "vitepress preview" "docs:preview": "vitepress preview"
}, },
@ -19,7 +20,7 @@
"@vben/locales": "workspace:*", "@vben/locales": "workspace:*",
"@vben/plugins": "workspace:*", "@vben/plugins": "workspace:*",
"@vben/styles": "workspace:*", "@vben/styles": "workspace:*",
"ant-design-vue": "catalog:", "antdv-next": "catalog:",
"lucide-vue-next": "catalog:", "lucide-vue-next": "catalog:",
"medium-zoom": "catalog:", "medium-zoom": "catalog:",
"reka-ui": "catalog:", "reka-ui": "catalog:",

View File

@ -26,16 +26,15 @@ import {
notification, notification,
Radio, Radio,
RadioGroup, RadioGroup,
RangePicker,
Rate, Rate,
Select, Select,
Space, Space,
Switch, Switch,
Textarea, TextArea,
TimePicker, TimePicker,
TreeSelect, TreeSelect,
Upload, Upload,
} from 'ant-design-vue'; } from 'antdv-next';
const withDefaultPlaceholder = <T extends Component>( const withDefaultPlaceholder = <T extends Component>(
component: T, component: T,
@ -98,12 +97,12 @@ async function initComponentAdapter() {
}, },
Radio, Radio,
RadioGroup, RadioGroup,
RangePicker, RangePicker: withDefaultPlaceholder(DatePicker.RangePicker, 'select'),
Rate, Rate,
Select: withDefaultPlaceholder(Select, 'select'), Select: withDefaultPlaceholder(Select, 'select'),
Space, Space,
Switch, Switch,
Textarea: withDefaultPlaceholder(Textarea, 'input'), Textarea: withDefaultPlaceholder(TextArea, 'input'),
TimePicker, TimePicker,
TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'), TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'),
Upload, Upload,
@ -118,7 +117,7 @@ async function initComponentAdapter() {
copyPreferencesSuccess: (title, content) => { copyPreferencesSuccess: (title, content) => {
notification.success({ notification.success({
description: content, description: content,
message: title, title,
placement: 'bottomRight', placement: 'bottomRight',
}); });
}, },

View File

@ -2,7 +2,7 @@ import { h } from 'vue';
import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table'; import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table';
import { Button, Image } from 'ant-design-vue'; import { Button, Image } from 'antdv-next';
import { useVbenForm } from './form'; import { useVbenForm } from './form';

View File

@ -22,7 +22,7 @@ outline: deep
<script lang="ts" setup> <script lang="ts" setup>
import { ApiComponent } from '@vben/common-ui'; import { ApiComponent } from '@vben/common-ui';
import { Cascader } from 'ant-design-vue'; import { Cascader } from 'antdv-next';
const treeData: Record<string, any> = [ const treeData: Record<string, any> = [
{ {

View File

@ -117,7 +117,7 @@ import {
TimePicker, TimePicker,
TreeSelect, TreeSelect,
Upload, Upload,
} from 'ant-design-vue'; } from 'antdv-next';
const withDefaultPlaceholder = <T extends Component>( const withDefaultPlaceholder = <T extends Component>(
component: T, component: T,

View File

@ -25,7 +25,7 @@ import { h } from 'vue';
import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table'; import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table';
import { Button, Image } from 'ant-design-vue'; import { Button, Image } from 'antdv-next';
import { useVbenForm } from './form'; import { useVbenForm } from './form';

View File

@ -3,7 +3,7 @@ import { h } from 'vue';
import { alert, VbenButton } from '@vben/common-ui'; import { alert, VbenButton } from '@vben/common-ui';
import { Result } from 'ant-design-vue'; import { Result } from 'antdv-next';
function showAlert() { function showAlert() {
alert('This is an alert message'); alert('This is an alert message');

View File

@ -3,7 +3,7 @@ import { h, ref } from 'vue';
import { alert, confirm, VbenButton } from '@vben/common-ui'; import { alert, confirm, VbenButton } from '@vben/common-ui';
import { Checkbox, message } from 'ant-design-vue'; import { Checkbox, message } from 'antdv-next';
function showConfirm() { function showConfirm() {
confirm('This is an alert message') confirm('This is an alert message')

View File

@ -3,7 +3,7 @@ import { h } from 'vue';
import { alert, prompt, useAlertContext, VbenButton } from '@vben/common-ui'; import { alert, prompt, useAlertContext, VbenButton } from '@vben/common-ui';
import { Input, RadioGroup, Select } from 'ant-design-vue'; import { Input, RadioGroup, Select } from 'antdv-next';
import { BadgeJapaneseYen } from 'lucide-vue-next'; import { BadgeJapaneseYen } from 'lucide-vue-next';
function showPrompt() { function showPrompt() {

View File

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ApiComponent } from '@vben/common-ui'; import { ApiComponent } from '@vben/common-ui';
import { Cascader } from 'ant-design-vue'; import { Cascader } from 'antdv-next';
const treeData: Record<string, any> = [ const treeData: Record<string, any> = [
{ {

View File

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Button, message, Space } from 'ant-design-vue'; import { Button, message, Space } from 'antdv-next';
import { useVbenForm } from '#/adapter/form'; import { useVbenForm } from '#/adapter/form';

View File

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { message } from 'ant-design-vue'; import { message } from 'antdv-next';
import { useVbenForm } from '#/adapter/form'; import { useVbenForm } from '#/adapter/form';

View File

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { h } from 'vue'; import { h } from 'vue';
import { Input, message } from 'ant-design-vue'; import { Input, message } from 'antdv-next';
import { useVbenForm } from '#/adapter/form'; import { useVbenForm } from '#/adapter/form';

View File

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { message } from 'ant-design-vue'; import { message } from 'antdv-next';
import { useVbenForm } from '#/adapter/form'; import { useVbenForm } from '#/adapter/form';

View File

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { message } from 'ant-design-vue'; import { message } from 'antdv-next';
import { useVbenForm } from '#/adapter/form'; import { useVbenForm } from '#/adapter/form';

View File

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { message } from 'ant-design-vue'; import { message } from 'antdv-next';
import { useVbenForm, z } from '#/adapter/form'; import { useVbenForm, z } from '#/adapter/form';

View File

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, nextTick, onMounted, ref, watch } from 'vue'; import { computed, nextTick, onMounted, ref, watch } from 'vue';
import { Button, Card, message, Space, Tag } from 'ant-design-vue'; import { Button, Card, message, Space, Tag } from 'antdv-next';
import { useVbenForm } from '#/adapter/form'; import { useVbenForm } from '#/adapter/form';

View File

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeGridListeners, VxeGridProps } from '#/adapter/vxe-table'; import type { VxeGridListeners, VxeGridProps } from '#/adapter/vxe-table';
import { Button, message } from 'ant-design-vue'; import { Button, message } from 'antdv-next';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { useVbenVxeGrid } from '#/adapter/vxe-table';

View File

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeGridProps } from '#/adapter/vxe-table'; import type { VxeGridProps } from '#/adapter/vxe-table';
import { Button, Image, Switch, Tag } from 'ant-design-vue'; import { Button, Image, Switch, Tag } from 'antdv-next';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { useVbenVxeGrid } from '#/adapter/vxe-table';

View File

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeGridProps } from '#/adapter/vxe-table'; import type { VxeGridProps } from '#/adapter/vxe-table';
import { Button, message } from 'ant-design-vue'; import { Button, message } from 'antdv-next';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { useVbenVxeGrid } from '#/adapter/vxe-table';

View File

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeGridProps } from '#/adapter/vxe-table'; import type { VxeGridProps } from '#/adapter/vxe-table';
import { Button } from 'ant-design-vue'; import { Button } from 'antdv-next';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { useVbenVxeGrid } from '#/adapter/vxe-table';

View File

@ -2,7 +2,7 @@
import type { VbenFormProps } from '#/adapter/form'; import type { VbenFormProps } from '#/adapter/form';
import type { VxeGridProps } from '#/adapter/vxe-table'; import type { VxeGridProps } from '#/adapter/vxe-table';
import { message } from 'ant-design-vue'; import { message } from 'antdv-next';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { useVbenVxeGrid } from '#/adapter/vxe-table';

View File

@ -3,7 +3,7 @@ import type { DemoTableApi } from '../mock-api';
import type { VxeGridProps } from '#/adapter/vxe-table'; import type { VxeGridProps } from '#/adapter/vxe-table';
import { Button } from 'ant-design-vue'; import { Button } from 'antdv-next';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { useVbenVxeGrid } from '#/adapter/vxe-table';

View File

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeGridProps } from '#/adapter/vxe-table'; import type { VxeGridProps } from '#/adapter/vxe-table';
import { Button } from 'ant-design-vue'; import { Button } from 'antdv-next';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { useVbenVxeGrid } from '#/adapter/vxe-table';

View File

@ -20,7 +20,7 @@ The current wrapper flow is:
<script lang="ts" setup> <script lang="ts" setup>
import { ApiComponent } from '@vben/common-ui'; import { ApiComponent } from '@vben/common-ui';
import { Cascader } from 'ant-design-vue'; import { Cascader } from 'antdv-next';
function fetchApi() { function fetchApi() {
return Promise.resolve([ return Promise.resolve([

View File

@ -103,7 +103,7 @@ import {
TimePicker, TimePicker,
TreeSelect, TreeSelect,
Upload, Upload,
} from 'ant-design-vue'; } from 'antdv-next';
const withDefaultPlaceholder = <T extends Component>( const withDefaultPlaceholder = <T extends Component>(
component: T, component: T,

View File

@ -1,6 +1,6 @@
# External Modules # External Modules
In addition to the external modules that are included by default in the project, sometimes we need to import other external modules. Let's take [ant-design-vue](https://antdv.com/components/overview) as an example: In addition to the external modules that are included by default in the project, sometimes we need to import other external modules. Let's take [antdv-next](https://antdv.com/components/overview) as an example:
## Installing Dependencies ## Installing Dependencies
@ -13,7 +13,7 @@ In addition to the external modules that are included by default in the project,
```bash ```bash
# cd /path/to/your/package # cd /path/to/your/package
pnpm add ant-design-vue pnpm add antdv-next
``` ```
## Usage ## Usage
@ -22,9 +22,9 @@ pnpm add ant-design-vue
```ts ```ts
import { createApp } from 'vue'; import { createApp } from 'vue';
import Antd from 'ant-design-vue'; import Antd from 'antdv-next';
import App from './App'; import App from './App';
import 'ant-design-vue/dist/reset.css'; import 'antdv-next/dist/reset.css';
const app = createApp(App); const app = createApp(App);
@ -43,7 +43,7 @@ app.use(Antd).mount('#app');
```vue ```vue
<script setup lang="ts"> <script setup lang="ts">
import { Button } from 'ant-design-vue'; import { Button } from 'antdv-next';
</script> </script>
<template> <template>

View File

@ -174,7 +174,7 @@ import {
} from '@vben/request'; } from '@vben/request';
import { useAccessStore } from '@vben/stores'; import { useAccessStore } from '@vben/stores';
import { message } from 'ant-design-vue'; import { message } from 'antdv-next';
import { useAuthStore } from '#/store'; import { useAuthStore } from '#/store';

View File

@ -110,7 +110,7 @@ registry = https://registry.npmmirror.com/
If you encounter errors similar to the following, please check that the full project path (including all parent paths) does not contain Chinese, Japanese, or Korean characters. Otherwise, you will encounter a 404 error for the path, leading to the following issue: If you encounter errors similar to the following, please check that the full project path (including all parent paths) does not contain Chinese, Japanese, or Korean characters. Otherwise, you will encounter a 404 error for the path, leading to the following issue:
```ts ```ts
[vite] Failed to resolve module import "ant-design-vue/dist/antd.css-vben-adminode_modulesant-design-vuedistantd.css". (imported by /@/setup/ant-design-vue/index.ts) [vite] Failed to resolve module import "antdv-next/dist/antd.css-vben-adminode_modulesantdv-nextdistantd.css". (imported by /@/setup/antdv-next/index.ts)
``` ```
## Console Route Warning Issue ## Console Route Warning Issue

View File

@ -1,6 +1,6 @@
# 外部模块 # 外部模块
除了项目默认引入的外部模块,有时我们还需要引入其他外部模块。我们以 [ant-design-vue](https://antdv.com/components/overview) 为例: 除了项目默认引入的外部模块,有时我们还需要引入其他外部模块。我们以 [antdv-next](https://antdv.com/components/overview) 为例:
## 安装依赖 ## 安装依赖
@ -13,7 +13,7 @@
```bash ```bash
# cd /path/to/your/package # cd /path/to/your/package
pnpm add ant-design-vue pnpm add antdv-next
``` ```
## 使用 ## 使用
@ -22,9 +22,9 @@ pnpm add ant-design-vue
```ts ```ts
import { createApp } from 'vue'; import { createApp } from 'vue';
import Antd from 'ant-design-vue'; import Antd from 'antdv-next';
import App from './App'; import App from './App';
import 'ant-design-vue/dist/reset.css'; import 'antdv-next/dist/reset.css';
const app = createApp(App); const app = createApp(App);
@ -43,7 +43,7 @@ app.use(Antd).mount('#app');
```vue ```vue
<script setup lang="ts"> <script setup lang="ts">
import { Button } from 'ant-design-vue'; import { Button } from 'antdv-next';
</script> </script>
<template> <template>

View File

@ -204,7 +204,7 @@ import {
} from '@vben/request'; } from '@vben/request';
import { useAccessStore } from '@vben/stores'; import { useAccessStore } from '@vben/stores';
import { message } from 'ant-design-vue'; import { message } from 'antdv-next';
import { useAuthStore } from '#/store'; import { useAuthStore } from '#/store';

View File

@ -60,7 +60,7 @@ pnpm add version-polling
```ts ```ts
import { h } from 'vue'; import { h } from 'vue';
import { Button, notification } from 'ant-design-vue'; import { Button, notification } from 'antdv-next';
import { createVersionPolling } from 'version-polling'; import { createVersionPolling } from 'version-polling';
createVersionPolling({ createVersionPolling({

View File

@ -110,7 +110,7 @@ registry = https://registry.npmmirror.com/
如果出现类似以下错误,请检查项目全路径(包含所有父级路径)不能出现中文、日文、韩文。否则将会出现路径访问 404 导致以下问题 如果出现类似以下错误,请检查项目全路径(包含所有父级路径)不能出现中文、日文、韩文。否则将会出现路径访问 404 导致以下问题
```ts ```ts
[vite] Failed to resolve module import "ant-design-vue/dist/antd.css-vben-adminode_modulesant-design-vuedistantd.css". (imported by /@/setup/ant-design-vue/index.ts) [vite] Failed to resolve module import "antdv-next/dist/antd.css-vben-adminode_modulesantdv-nextdistantd.css". (imported by /@/setup/antdv-next/index.ts)
``` ```
## 控制台路由警告问题 ## 控制台路由警告问题

View File

@ -8,6 +8,7 @@ const comments: OxlintConfig = {
}, },
], ],
rules: { rules: {
'eslint/no-underscore-dangle': 'off',
'eslint-comments/no-aggregating-enable': 'error', 'eslint-comments/no-aggregating-enable': 'error',
'eslint-comments/no-duplicate-disable': 'error', 'eslint-comments/no-duplicate-disable': 'error',
'eslint-comments/no-unlimited-disable': 'error', 'eslint-comments/no-unlimited-disable': 'error',

View File

@ -0,0 +1,70 @@
import type { Plugin } from 'vite';
function viteDayjsPlugin(): Plugin {
return {
name: 'vite-dayjs-plugin',
enforce: 'pre',
async resolveId(source, importer, options) {
// 1) 已经使用了 dayjs/esm 的不处理
if (source.startsWith('dayjs/esm')) return null;
// 2) 根入口dayjs -> dayjs/esm
if (source === 'dayjs') {
return await this.resolve('dayjs/esm', importer, {
skipSelf: true,
...options,
});
}
// 3) 插件入口的多种写法
// - dayjs/plugin/xxx.js -> dayjs/esm/plugin/xxx/index.js
// - dayjs/plugin/xxx -> dayjs/esm/plugin/xxx
const pluginWithJs = source.match(/^dayjs\/plugin\/([^/]+)\.js$/);
if (pluginWithJs) {
const target = `dayjs/esm/plugin/${pluginWithJs[1]}/index.js`;
return await this.resolve(target, importer, {
skipSelf: true,
...options,
});
}
const pluginBare = source.match(/^dayjs\/plugin\/([^/]+)$/);
if (pluginBare) {
const target = `dayjs/esm/plugin/${pluginBare[1]}`;
return await this.resolve(target, importer, {
skipSelf: true,
...options,
});
}
// 4) 处理多语言包
// - dayjs/locale/xxx.js -> dayjs/esm/locale/xxx.js
const localeWithJs = source.match(/^dayjs\/locale\/([^/]+)\.js$/);
if (localeWithJs) {
const target = `dayjs/esm/locale/${localeWithJs[1]}.js`;
return await this.resolve(target, importer, {
skipSelf: true,
...options,
});
}
const localeBare = source.match(/^dayjs\/locale\/([^/]+)$/);
if (localeBare) {
const target = `dayjs/esm/locale/${localeBare[1]}`;
return await this.resolve(target, importer, {
skipSelf: true,
...options,
});
}
return null;
},
config() {
return {
optimizeDeps: {
exclude: ['dayjs'],
},
};
},
};
}
export { viteDayjsPlugin };

View File

@ -18,6 +18,7 @@ import { VitePWA } from 'vite-plugin-pwa';
import viteVueDevTools from 'vite-plugin-vue-devtools'; import viteVueDevTools from 'vite-plugin-vue-devtools';
import { viteArchiverPlugin } from './archiver'; import { viteArchiverPlugin } from './archiver';
import { viteDayjsPlugin } from './dayjs';
import { viteExtraAppConfigPlugin } from './extra-app-config'; import { viteExtraAppConfigPlugin } from './extra-app-config';
import { viteHtmlPlugin } from './html'; import { viteHtmlPlugin } from './html';
import { viteImportMapPlugin } from './importmap'; import { viteImportMapPlugin } from './importmap';
@ -105,6 +106,7 @@ async function loadApplicationPlugins(
compressTypes, compressTypes,
extraAppConfig, extraAppConfig,
html, html,
dayjs,
i18n, i18n,
importmap, importmap,
importmapOptions, importmapOptions,
@ -219,6 +221,10 @@ async function loadApplicationPlugins(
return [await viteArchiverPlugin(archiverPluginOptions)]; return [await viteArchiverPlugin(archiverPluginOptions)];
}, },
}, },
{
condition: dayjs,
plugins: () => [viteDayjsPlugin()],
},
]); ]);
} }
@ -247,6 +253,7 @@ export {
loadLibraryPlugins, loadLibraryPlugins,
viteArchiverPlugin, viteArchiverPlugin,
viteCompressPlugin, viteCompressPlugin,
viteDayjsPlugin,
viteDtsPlugin, viteDtsPlugin,
viteHtmlPlugin, viteHtmlPlugin,
viteVisualizerPlugin, viteVisualizerPlugin,

View File

@ -214,6 +214,11 @@ interface ApplicationPluginOptions extends CommonPluginOptions {
* @description * @description
*/ */
compressTypes?: ('brotli' | 'gzip')[]; compressTypes?: ('brotli' | 'gzip')[];
/**
* dayjs
* @default true
*/
dayjs?: boolean;
/** /**
* *
* @default false * @default false

View File

@ -102,8 +102,8 @@
"vue-tsc": "catalog:" "vue-tsc": "catalog:"
}, },
"engines": { "engines": {
"node": "^20.19.0 || ^22.18.0 || ^24.0.0", "node": "^22.18.0 || ^24.0.0",
"pnpm": ">=10.0.0" "pnpm": ">=10.0.0"
}, },
"packageManager": "pnpm@10.33.0" "packageManager": "pnpm@10.33.4"
} }

View File

@ -87,7 +87,7 @@ class IndexedDBDriver implements IStorageDriver {
}); });
} }
async setItem<T>(key: string, value: T): Promise<void> { async setItem(key: string, value: unknown): Promise<void> {
const db = await this.getDB(); const db = await this.getDB();
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const tx = db.transaction(this.storeName, 'readwrite'); const tx = db.transaction(this.storeName, 'readwrite');

View File

@ -62,7 +62,7 @@ class LocalStorageDriver implements IStorageDriver {
this.storage.removeItem(key); this.storage.removeItem(key);
} }
async setItem<T>(key: string, value: T): Promise<void> { async setItem(key: string, value: unknown): Promise<void> {
this.storage.setItem(key, JSON.stringify(value)); this.storage.setItem(key, JSON.stringify(value));
} }
} }

View File

@ -24,7 +24,7 @@ class MemoryStorageDriver implements IStorageDriver {
this.store.delete(key); this.store.delete(key);
} }
async setItem<T>(key: string, value: T): Promise<void> { async setItem(key: string, value: unknown): Promise<void> {
this.store.set(key, value); this.store.set(key, value);
} }
} }

View File

@ -106,10 +106,10 @@ class StorageManager {
* @param value * @param value
* @param ttl * @param ttl
*/ */
async setItem<T>(key: string, value: T, ttl?: number): Promise<void> { async setItem(key: string, value: unknown, ttl?: number): Promise<void> {
const fullKey = this.getFullKey(key); const fullKey = this.getFullKey(key);
const expiry = ttl ? Date.now() + ttl : undefined; const expiry = ttl ? Date.now() + ttl : undefined;
const item: StorageItem<T> = { expiry, value }; const item: StorageItem<unknown> = { expiry, value };
await this.driver.setItem(fullKey, item); await this.driver.setItem(fullKey, item);
} }

View File

@ -17,7 +17,7 @@ interface IStorageDriver {
removeItem(key: string): Promise<void>; removeItem(key: string): Promise<void>;
/** 设置存储项 */ /** 设置存储项 */
setItem<T>(key: string, value: T): Promise<void>; setItem(key: string, value: unknown): Promise<void>;
} }
/** /**

View File

@ -34,7 +34,9 @@ describe('stateHandler', () => {
}, 10); }, 10);
// 等待过程中,期望 Promise 被 reject // 等待过程中,期望 Promise 被 reject
await expect(handler.waitForCondition()).rejects.toThrow(); await expect(handler.waitForCondition()).rejects.toThrow(
'Condition was set to false',
);
expect(handler.isConditionTrue()).toBe(false); expect(handler.isConditionTrue()).toBe(false);
}); });

View File

@ -138,8 +138,10 @@ describe('getNestedValue', () => {
expect(result).toBe(2); expect(result).toBe(2);
}); });
it('should return the entire object if path is empty', () => { it('should throw if path is empty', () => {
expect(() => getNestedValue(data, '')()).toThrow(); expect(() => getNestedValue(data, '')).toThrow(
'Path must be a non-empty string',
);
}); });
it('should handle paths with array indexes', () => { it('should handle paths with array indexes', () => {

View File

@ -1,6 +1,6 @@
export class StateHandler { export class StateHandler {
private condition: boolean = false; private condition: boolean = false;
private rejectCondition: (() => void) | null = null; private rejectCondition: ((reason?: Error) => void) | null = null;
private resolveCondition: (() => void) | null = null; private resolveCondition: (() => void) | null = null;
isConditionTrue(): boolean { isConditionTrue(): boolean {
@ -16,7 +16,7 @@ export class StateHandler {
setConditionFalse() { setConditionFalse() {
this.condition = false; this.condition = false;
if (this.rejectCondition) { if (this.rejectCondition) {
this.rejectCondition(); this.rejectCondition(new Error('Condition was set to false'));
this.clearPromises(); this.clearPromises();
} }
} }

View File

@ -180,11 +180,7 @@ class PreferenceManager {
* *
* @param updates - * @param updates -
*/ */
updateCustomPreferences = < updateCustomPreferences = (updates: DeepPartial<object>) => {
TCustomPreferences extends object = CustomPreferencesRecord,
>(
updates: DeepPartial<TCustomPreferences>,
) => {
if (!this.customPreferencesExtension) { if (!this.customPreferencesExtension) {
return; return;
} }

View File

@ -66,6 +66,7 @@ function findPlaceholderPos(doc: ProseMirrorNode, blobUrl: string): number {
found = offset; found = offset;
return false; return false;
} }
return true;
}); });
return found; return found;
} }

View File

@ -1,3 +1,4 @@
/* eslint-disable unicorn/no-nested-ternary */
import type { VxeGridProps as VxeTableGridProps } from 'vxe-table'; import type { VxeGridProps as VxeTableGridProps } from 'vxe-table';
import type { import type {
@ -182,13 +183,11 @@ export function useViewedRow<T = any>(
options: ViewedRowOptions<T> & { keyField: string }, options: ViewedRowOptions<T> & { keyField: string },
) { ) {
// ========== 解析持久化配置 ========== // ========== 解析持久化配置 ==========
let persistOpts: null | ViewedRowPersistOptions = null; const persistOpts: null | ViewedRowPersistOptions = options.persist
if (options.persist) { ? typeof options.persist === 'string'
persistOpts =
typeof options.persist === 'string'
? { key: options.persist, type: 'localStorage' } ? { key: options.persist, type: 'localStorage' }
: options.persist; : options.persist
} : null;
const adapter = createStorageAdapter(options.persist); const adapter = createStorageAdapter(options.persist);
const maxSize = persistOpts?.maxSize ?? 100; const maxSize = persistOpts?.maxSize ?? 100;
@ -521,12 +520,12 @@ export function applyViewedRowOptions(
}; };
// 拦截 CellOperation columns // 拦截 CellOperation columns
let actionCodes: string[] = []; const actionCodes =
if (!isBoolean(viewedRowConfig) && viewedRowConfig.actionCodes) { !isBoolean(viewedRowConfig) && viewedRowConfig.actionCodes
actionCodes = Array.isArray(viewedRowConfig.actionCodes) ? Array.isArray(viewedRowConfig.actionCodes)
? viewedRowConfig.actionCodes ? viewedRowConfig.actionCodes
: [viewedRowConfig.actionCodes]; : [viewedRowConfig.actionCodes]
} : [];
if (actionCodes.length > 0 && Array.isArray(mergedOptions.columns)) { if (actionCodes.length > 0 && Array.isArray(mergedOptions.columns)) {
mergedOptions.columns = wrapColumnsForViewedRow( mergedOptions.columns = wrapColumnsForViewedRow(

View File

@ -50,24 +50,18 @@ describe('requestClient', () => {
it('should handle network errors', async () => { it('should handle network errors', async () => {
mock.onGet('/test/error').networkError(); mock.onGet('/test/error').networkError();
try { await expect(requestClient.get('/test/error')).rejects.toMatchObject({
await requestClient.get('/test/error'); isAxiosError: true,
expect(true).toBe(false); message: 'Network Error',
} catch (error: any) { });
expect(error.isAxiosError).toBe(true);
expect(error.message).toBe('Network Error');
}
}); });
it('should handle timeout', async () => { it('should handle timeout', async () => {
mock.onGet('/test/timeout').timeout(); mock.onGet('/test/timeout').timeout();
try { await expect(requestClient.get('/test/timeout')).rejects.toMatchObject({
await requestClient.get('/test/timeout'); isAxiosError: true,
expect(true).toBe(false); code: 'ECONNABORTED',
} catch (error: any) { });
expect(error.isAxiosError).toBe(true);
expect(error.code).toBe('ECONNABORTED');
}
}); });
it('should successfully upload a file', async () => { it('should successfully upload a file', async () => {
@ -92,7 +86,7 @@ describe('requestClient', () => {
mock.onGet('/test/download').reply(200, mockFileContent); mock.onGet('/test/download').reply(200, mockFileContent);
const res = await requestClient.download('/test/download'); const res = await requestClient.download<any>('/test/download');
expect(res.data).toBeInstanceOf(Blob); expect(res.data).toBeInstanceOf(Blob);
}); });

View File

@ -48,7 +48,7 @@
"@vben/types": "workspace:*", "@vben/types": "workspace:*",
"@vben/utils": "workspace:*", "@vben/utils": "workspace:*",
"@vueuse/core": "catalog:", "@vueuse/core": "catalog:",
"ant-design-vue": "catalog:", "antdv-next": "catalog:",
"dayjs": "catalog:", "dayjs": "catalog:",
"json-bigint": "catalog:", "json-bigint": "catalog:",
"pinia": "catalog:", "pinia": "catalog:",

View File

@ -18,6 +18,7 @@ import type {
MentionsProps, MentionsProps,
RadioGroupProps, RadioGroupProps,
RadioProps, RadioProps,
RangePickerProps,
RateProps, RateProps,
SelectProps, SelectProps,
SpaceProps, SpaceProps,
@ -28,8 +29,7 @@ import type {
UploadChangeParam, UploadChangeParam,
UploadFile, UploadFile,
UploadProps, UploadProps,
} from 'ant-design-vue'; } from 'antdv-next';
import type { RangePickerProps } from 'ant-design-vue/es/date-picker';
import type { Component, Ref } from 'vue'; import type { Component, Ref } from 'vue';
@ -72,7 +72,7 @@ import { isEmpty } from '@vben/utils';
import { VbenCollapsibleParams } from '@vben-core/shadcn-ui'; import { VbenCollapsibleParams } from '@vben-core/shadcn-ui';
import { message, Modal, notification } from 'ant-design-vue'; import { message, Modal, notification } from 'antdv-next';
import { upload_file } from '#/api/examples/upload'; import { upload_file } from '#/api/examples/upload';
type AdapterUploadProps = UploadProps & { type AdapterUploadProps = UploadProps & {
@ -86,60 +86,72 @@ type AdapterUploadProps = UploadProps & {
}; };
const AutoComplete = defineAsyncComponent( const AutoComplete = defineAsyncComponent(
() => import('ant-design-vue/es/auto-complete'), () => import('antdv-next/dist/auto-complete/index'),
);
const Button = defineAsyncComponent(
() => import('antdv-next/dist/button/index'),
); );
const Button = defineAsyncComponent(() => import('ant-design-vue/es/button'));
const Checkbox = defineAsyncComponent( const Checkbox = defineAsyncComponent(
() => import('ant-design-vue/es/checkbox'), () => import('antdv-next/dist/checkbox/index'),
); );
const CheckboxGroup = defineAsyncComponent(() => const CheckboxGroup = defineAsyncComponent(() =>
import('ant-design-vue/es/checkbox').then((res) => res.CheckboxGroup), import('antdv-next/dist/checkbox/index').then((res) => res.CheckboxGroup),
); );
const DatePicker = defineAsyncComponent( const DatePicker = defineAsyncComponent(
() => import('ant-design-vue/es/date-picker'), () => import('antdv-next/dist/date-picker/index'),
); );
const Divider = defineAsyncComponent(() => import('ant-design-vue/es/divider')); const Divider = defineAsyncComponent(
const Input = defineAsyncComponent(() => import('ant-design-vue/es/input')); () => import('antdv-next/dist/divider/index'),
);
const Input = defineAsyncComponent(() => import('antdv-next/dist/input/index'));
const InputNumber = defineAsyncComponent( const InputNumber = defineAsyncComponent(
() => import('ant-design-vue/es/input-number'), () => import('antdv-next/dist/input-number/index'),
); );
const InputPassword = defineAsyncComponent(() => const InputPassword = defineAsyncComponent(() =>
import('ant-design-vue/es/input').then((res) => res.InputPassword), import('antdv-next/dist/input/index').then((res) => res.InputPassword),
); );
const Mentions = defineAsyncComponent( const Mentions = defineAsyncComponent(
() => import('ant-design-vue/es/mentions'), () => import('antdv-next/dist/mentions/index'),
); );
const Radio = defineAsyncComponent(() => import('ant-design-vue/es/radio')); const Radio = defineAsyncComponent(() => import('antdv-next/dist/radio/index'));
const RadioGroup = defineAsyncComponent(() => const RadioGroup = defineAsyncComponent(() =>
import('ant-design-vue/es/radio').then((res) => res.RadioGroup), import('antdv-next/dist/radio/index').then((res) => res.RadioGroup),
); );
const RangePicker = defineAsyncComponent(() => const RangePicker = defineAsyncComponent(() =>
import('ant-design-vue/es/date-picker').then((res) => res.RangePicker), import('antdv-next/dist/date-picker/index').then(
(res) => res.DateRangePicker,
),
); );
const Rate = defineAsyncComponent(() => import('ant-design-vue/es/rate')); const Rate = defineAsyncComponent(() => import('antdv-next/dist/rate/index'));
const Select = defineAsyncComponent(() => import('ant-design-vue/es/select')); const Select = defineAsyncComponent(
const Space = defineAsyncComponent(() => import('ant-design-vue/es/space')); () => import('antdv-next/dist/select/index'),
const Switch = defineAsyncComponent(() => import('ant-design-vue/es/switch')); );
const Textarea = defineAsyncComponent(() => const Space = defineAsyncComponent(() => import('antdv-next/dist/space/index'));
import('ant-design-vue/es/input').then((res) => res.Textarea), const Switch = defineAsyncComponent(
() => import('antdv-next/dist/switch/index'),
);
const Textarea = defineAsyncComponent(
() => import('antdv-next/dist/input/TextArea'),
); );
const TimePicker = defineAsyncComponent( const TimePicker = defineAsyncComponent(
() => import('ant-design-vue/es/time-picker'), () => import('antdv-next/dist/time-picker/index'),
); );
const TreeSelect = defineAsyncComponent( const TreeSelect = defineAsyncComponent(
() => import('ant-design-vue/es/tree-select'), () => import('antdv-next/dist/tree-select/index'),
); );
const Cascader = defineAsyncComponent( const Cascader = defineAsyncComponent(
() => import('ant-design-vue/es/cascader'), () => import('antdv-next/dist/cascader/index'),
); );
const Upload = defineAsyncComponent(() => import('ant-design-vue/es/upload')); const Upload = defineAsyncComponent(
const Image = defineAsyncComponent(() => import('ant-design-vue/es/image')); () => import('antdv-next/dist/upload/index'),
);
const Image = defineAsyncComponent(() => import('antdv-next/dist/image/index'));
const PreviewGroup = defineAsyncComponent(() => const PreviewGroup = defineAsyncComponent(() =>
import('ant-design-vue/es/image').then((res) => res.ImagePreviewGroup), import('antdv-next/dist/image/index').then((res) => res.ImagePreviewGroup),
); );
const withDefaultPlaceholder = <T extends Component>( const withDefaultPlaceholder = (
component: T, component: Component,
type: 'input' | 'select', type: 'input' | 'select',
componentProps: Recordable<any> = {}, componentProps: Recordable<any> = {},
) => { ) => {
@ -242,7 +254,7 @@ function getBase64(file: File): Promise<string> {
*/ */
async function previewImage( async function previewImage(
file: UploadFile, file: UploadFile,
visible: Ref<boolean>, open: Ref<boolean>,
fileList: Ref<UploadProps['fileList']>, fileList: Ref<UploadProps['fileList']>,
) { ) {
// 非图片文件直接打开链接 // 非图片文件直接打开链接
@ -250,6 +262,8 @@ async function previewImage(
const url = file.url || file.preview; const url = file.url || file.preview;
if (url) { if (url) {
window.open(url, '_blank'); window.open(url, '_blank');
} else if (file.preview) {
window.open(file.preview, '_blank');
} else { } else {
message.error($t('ui.formRules.previewWarning')); message.error($t('ui.formRules.previewWarning'));
} }
@ -285,10 +299,10 @@ async function previewImage(
{ {
class: 'hidden', class: 'hidden',
preview: { preview: {
visible: visible.value, open: open.value,
current: currentIndex, current: currentIndex,
onVisibleChange: (value: boolean) => { onOpenChange: (value: boolean) => {
visible.value = value; open.value = value;
if (!value) { if (!value) {
setTimeout(() => { setTimeout(() => {
if (!isUnmounted && container) { if (!isUnmounted && container) {
@ -330,7 +344,7 @@ function cropImage(file: File, aspectRatio: string | undefined) {
const open = ref<boolean>(true); const open = ref<boolean>(true);
const cropperRef = ref<InstanceType<typeof VCropper> | null>(null); const cropperRef = ref<InstanceType<typeof VCropper> | null>(null);
const closeModal = () => { function closeModal() {
open.value = false; open.value = false;
setTimeout(() => { setTimeout(() => {
if (!isUnmounted && container) { if (!isUnmounted && container) {
@ -342,7 +356,7 @@ function cropImage(file: File, aspectRatio: string | undefined) {
container.remove(); container.remove();
} }
}, 300); }, 300);
}; }
const CropperWrapper = { const CropperWrapper = {
setup() { setup() {
@ -372,7 +386,7 @@ function cropImage(file: File, aspectRatio: string | undefined) {
closable: false, closable: false,
cancelText: $t('common.cancel'), cancelText: $t('common.cancel'),
okText: $t('ui.crop.confirm'), okText: $t('ui.crop.confirm'),
destroyOnClose: true, destroyOnHidden: true,
onOk: async () => { onOk: async () => {
const cropper = cropperRef.value; const cropper = cropperRef.value;
if (!cropper) { if (!cropper) {
@ -416,7 +430,7 @@ function cropImage(file: File, aspectRatio: string | undefined) {
/** /**
* *
*/ */
const withPreviewUpload = () => { function withPreviewUpload() {
return defineComponent({ return defineComponent({
name: Upload.name, name: Upload.name,
emits: ['update:modelValue'], emits: ['update:modelValue'],
@ -436,10 +450,10 @@ const withPreviewUpload = () => {
() => attrs?.aspectRatio ?? attrs?.['aspect-ratio'], () => attrs?.aspectRatio ?? attrs?.['aspect-ratio'],
); );
const handleBeforeUpload = async ( async function handleBeforeUpload(
file: UploadFile, file: UploadFile,
originFileList: Array<File>, originFileList: Array<File>,
) => { ) {
// 文件大小限制 // 文件大小限制
if (maxSize.value && (file.size || 0) / 1024 / 1024 > maxSize.value) { if (maxSize.value && (file.size || 0) / 1024 / 1024 > maxSize.value) {
message.error($t('ui.formRules.sizeLimit', [maxSize.value])); message.error($t('ui.formRules.sizeLimit', [maxSize.value]));
@ -463,9 +477,9 @@ const withPreviewUpload = () => {
} }
return attrs.beforeUpload?.(file) ?? true; return attrs.beforeUpload?.(file) ?? true;
}; }
const handleChange = (event: UploadChangeParam) => { function handleChange(event: UploadChangeParam) {
try { try {
attrs.handleChange?.(event); attrs.handleChange?.(event);
attrs.onHandleChange?.(event); attrs.onHandleChange?.(event);
@ -479,19 +493,19 @@ const withPreviewUpload = () => {
'update:modelValue', 'update:modelValue',
event.fileList?.length ? fileList.value : undefined, event.fileList?.length ? fileList.value : undefined,
); );
}; }
const handlePreview = async (file: UploadFile) => { function handlePreview(file: UploadFile) {
previewVisible.value = true; previewVisible.value = true;
await previewImage(file, previewVisible, fileList); return previewImage(file, previewVisible, fileList);
}; }
const renderUploadButton = () => { function renderUploadButton() {
if (attrs.disabled) return null; if (attrs.disabled) return null;
return isEmpty(slots) return isEmpty(slots)
? createDefaultUploadSlots(listType, placeholder) ? createDefaultUploadSlots(listType, placeholder)
: slots; : slots;
}; }
// 拖拽排序 // 拖拽排序
const draggable = computed( const draggable = computed(
@ -600,7 +614,7 @@ const withPreviewUpload = () => {
); );
}, },
}); });
}; }
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明 // 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
export type ComponentType = export type ComponentType =
@ -681,13 +695,13 @@ async function initComponentAdapter() {
fieldNames: { label: 'label', value: 'value', children: 'children' }, fieldNames: { label: 'label', value: 'value', children: 'children' },
loadingSlot: 'suffixIcon', loadingSlot: 'suffixIcon',
modelPropName: 'value', modelPropName: 'value',
visibleEvent: 'onVisibleChange', visibleEvent: 'onOpenChange',
}), }),
ApiSelect: withDefaultPlaceholder(ApiComponent, 'select', { ApiSelect: withDefaultPlaceholder(ApiComponent, 'select', {
component: Select, component: Select,
loadingSlot: 'suffixIcon', loadingSlot: 'suffixIcon',
modelPropName: 'value', modelPropName: 'value',
visibleEvent: 'onVisibleChange', visibleEvent: 'onOpenChange',
}), }),
ApiTreeSelect: withDefaultPlaceholder(ApiComponent, 'select', { ApiTreeSelect: withDefaultPlaceholder(ApiComponent, 'select', {
component: TreeSelect, component: TreeSelect,
@ -695,7 +709,7 @@ async function initComponentAdapter() {
loadingSlot: 'suffixIcon', loadingSlot: 'suffixIcon',
modelPropName: 'value', modelPropName: 'value',
optionsPropName: 'treeData', optionsPropName: 'treeData',
visibleEvent: 'onVisibleChange', visibleEvent: 'onOpenChange',
}), }),
AutoComplete, AutoComplete,
Cascader, Cascader,
@ -713,7 +727,9 @@ async function initComponentAdapter() {
modelValueProp: 'value', modelValueProp: 'value',
}), }),
Input: withDefaultPlaceholder(Input, 'input'), Input: withDefaultPlaceholder(Input, 'input'),
InputNumber: withDefaultPlaceholder(InputNumber, 'input'), InputNumber: withDefaultPlaceholder(InputNumber, 'input', {
style: { width: '100%' },
}),
InputPassword: withDefaultPlaceholder(InputPassword, 'input'), InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
Mentions: withDefaultPlaceholder(Mentions, 'input'), Mentions: withDefaultPlaceholder(Mentions, 'input'),
// 自定义主要按钮 // 自定义主要按钮
@ -764,7 +780,7 @@ async function initComponentAdapter() {
copyPreferencesSuccess: (title, content) => { copyPreferencesSuccess: (title, content) => {
notification.success({ notification.success({
description: content, description: content,
message: title, title,
placement: 'bottomRight', placement: 'bottomRight',
}); });
}, },

View File

@ -14,7 +14,7 @@ import {
import { get, isFunction, isString } from '@vben/utils'; import { get, isFunction, isString } from '@vben/utils';
import { objectOmit } from '@vueuse/core'; import { objectOmit } from '@vueuse/core';
import { Button, Image, Popconfirm, Switch, Tag } from 'ant-design-vue'; import { Button, Image, Popconfirm, Switch, Tag } from 'antdv-next';
import { $t } from '#/locales'; import { $t } from '#/locales';

View File

@ -14,7 +14,7 @@ import {
import { useAccessStore } from '@vben/stores'; import { useAccessStore } from '@vben/stores';
import { cloneDeep } from '@vben/utils'; import { cloneDeep } from '@vben/utils';
import { message } from 'ant-design-vue'; import { message } from 'antdv-next';
import JSONBigInt from 'json-bigint'; import JSONBigInt from 'json-bigint';
import { useAuthStore } from '#/store'; import { useAuthStore } from '#/store';

View File

@ -1,3 +1,4 @@
export * from './dept'; export * from './dept';
export * from './menu'; export * from './menu';
export * from './role'; export * from './role';
export * from './user';

View File

@ -0,0 +1,55 @@
import type { Recordable } from '@vben/types';
import { requestClient } from '#/api/request';
export namespace SystemUserApi {
export interface SystemUser {
[key: string]: any;
id: string;
name: string;
permissions: string[];
remark?: string;
status: 0 | 1;
}
}
/**
*
*/
async function getUserList(params: Recordable<any>) {
return requestClient.get<Array<SystemUserApi.SystemUser>>(
'/system/user/list',
{ params },
);
}
/**
*
* @param data
*/
async function createUser(data: Omit<SystemUserApi.SystemUser, 'id'>) {
return requestClient.post('/system/user', data);
}
/**
*
*
* @param id ID
* @param data
*/
async function updateUser(
id: string,
data: Omit<SystemUserApi.SystemUser, 'id'>,
) {
return requestClient.put(`/system/user/${id}`, data);
}
/**
*
* @param id ID
*/
async function deleteUser(id: string) {
return requestClient.delete(`/system/user/${id}`);
}
export { createUser, deleteUser, getUserList, updateUser };

View File

@ -1,10 +1,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue'; import { computed, watch } from 'vue';
import { useAntdDesignTokens } from '@vben/hooks'; import { useAntdDesignTokens } from '@vben/hooks';
import { preferences, usePreferences } from '@vben/preferences'; import { preferences, usePreferences } from '@vben/preferences';
import { App, ConfigProvider, theme } from 'ant-design-vue'; import { App, ConfigProvider, theme } from 'antdv-next';
import { antdLocale } from '#/locales'; import { antdLocale } from '#/locales';
@ -28,6 +28,14 @@ const tokenTheme = computed(() => {
token: tokens, token: tokens,
}; };
}); });
watch(
tokenTheme,
(themeConfig) => {
ConfigProvider.config({ theme: themeConfig });
},
{ immediate: true },
);
</script> </script>
<template> <template>

View File

@ -6,7 +6,7 @@ import { providePluginsOptions } from '@vben/plugins';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
import { initStores } from '@vben/stores'; import { initStores } from '@vben/stores';
import '@vben/styles'; import '@vben/styles';
import '@vben/styles/antd'; import '@vben/styles/antdv-next';
import { useTitle } from '@vueuse/core'; import { useTitle } from '@vueuse/core';

View File

@ -1,4 +1,4 @@
import type { Locale } from 'ant-design-vue/es/locale'; import type { Locale } from 'antdv-next/dist/locale/index';
import type { App } from 'vue'; import type { App } from 'vue';
@ -13,8 +13,8 @@ import {
} from '@vben/locales'; } from '@vben/locales';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
import antdEnLocale from 'ant-design-vue/es/locale/en_US'; import antdEnLocale from 'antdv-next/dist/locale/en_US';
import antdDefaultLocale from 'ant-design-vue/es/locale/zh_CN'; import antdDefaultLocale from 'antdv-next/dist/locale/zh_CN';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
const antdLocale = ref<Locale>(antdDefaultLocale); const antdLocale = ref<Locale>(antdDefaultLocale);

View File

@ -1,5 +1,18 @@
{ {
"title": "System Management", "title": "System Management",
"user": {
"title": "User Management",
"list": "User List",
"name": "User",
"userName": "User Name",
"id": "User ID",
"dept": "Department",
"status": "Status",
"remark": "Remark",
"createTime": "Creation Time",
"operation": "Operation",
"placeholder": "Search Department..."
},
"dept": { "dept": {
"name": "Department", "name": "Department",
"title": "Department Management", "title": "Department Management",

View File

@ -1,5 +1,18 @@
{ {
"title": "系统管理", "title": "系统管理",
"user": {
"title": "用户管理",
"list": "用户列表",
"name": "用户名",
"userName": "用户名称",
"id": "用户ID",
"dept": "部门",
"status": "状态",
"remark": "备注",
"createTime": "创建时间",
"operation": "操作",
"placeholder": "搜索部门..."
},
"dept": { "dept": {
"list": "部门列表", "list": "部门列表",
"createTime": "创建时间", "createTime": "创建时间",

View File

@ -29,4 +29,4 @@ async function initApplication() {
unmountGlobalLoading(); unmountGlobalLoading();
} }
initApplication(); void initApplication();

View File

@ -6,7 +6,7 @@ import type {
import { generateAccessible } from '@vben/access'; import { generateAccessible } from '@vben/access';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
import { message } from 'ant-design-vue'; import { message } from 'antdv-next';
import { getAllMenusApi } from '#/api'; import { getAllMenusApi } from '#/api';
import { BasicLayout, IFrameView } from '#/layouts'; import { BasicLayout, IFrameView } from '#/layouts';

View File

@ -12,6 +12,15 @@ const routes: RouteRecordRaw[] = [
name: 'System', name: 'System',
path: '/system', path: '/system',
children: [ children: [
{
path: '/system/user',
name: 'SystemUser',
meta: {
icon: 'mdi:user',
title: $t('system.user.title'),
},
component: () => import('#/views/system/user/list.vue'),
},
{ {
path: '/system/role', path: '/system/role',
name: 'SystemRole', name: 'SystemRole',

View File

@ -7,7 +7,7 @@ import { LOGIN_PATH } from '@vben/constants';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores'; import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores';
import { notification } from 'ant-design-vue'; import { notification } from 'antdv-next';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { getAccessCodesApi, getUserInfoApi, loginApi, logoutApi } from '#/api'; import { getAccessCodesApi, getUserInfoApi, loginApi, logoutApi } from '#/api';
@ -65,7 +65,7 @@ export const useAuthStore = defineStore('auth', () => {
notification.success({ notification.success({
description: `${$t('authentication.loginSuccessDesc')}:${userInfo?.realName}`, description: `${$t('authentication.loginSuccessDesc')}:${userInfo?.realName}`,
duration: 3, duration: 3,
message: $t('authentication.loginSuccess'), title: $t('authentication.loginSuccess'),
}); });
} }
} }

View File

@ -7,7 +7,7 @@ import { computed, ref, useTemplateRef } from 'vue';
import { AuthenticationCodeLogin, z } from '@vben/common-ui'; import { AuthenticationCodeLogin, z } from '@vben/common-ui';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import { message } from 'ant-design-vue'; import { message } from 'antdv-next';
defineOptions({ name: 'CodeLogin' }); defineOptions({ name: 'CodeLogin' });

View File

@ -5,7 +5,7 @@ import { computed } from 'vue';
import { ProfilePasswordSetting, z } from '@vben/common-ui'; import { ProfilePasswordSetting, z } from '@vben/common-ui';
import { message } from 'ant-design-vue'; import { message } from 'antdv-next';
const formSchema = computed((): VbenFormSchema[] => { const formSchema = computed((): VbenFormSchema[] => {
return [ return [

View File

@ -7,7 +7,7 @@ import { AccessControl, useAccess } from '@vben/access';
import { Page } from '@vben/common-ui'; import { Page } from '@vben/common-ui';
import { resetAllStores, useUserStore } from '@vben/stores'; import { resetAllStores, useUserStore } from '@vben/stores';
import { Button, Card } from 'ant-design-vue'; import { Button, Card } from 'antdv-next';
import { useAuthStore } from '#/store'; import { useAuthStore } from '#/store';

View File

@ -7,7 +7,7 @@ import { useAccess } from '@vben/access';
import { Page } from '@vben/common-ui'; import { Page } from '@vben/common-ui';
import { resetAllStores, useUserStore } from '@vben/stores'; import { resetAllStores, useUserStore } from '@vben/stores';
import { Button, Card } from 'ant-design-vue'; import { Button, Card } from 'antdv-next';
import { useAuthStore } from '#/store'; import { useAuthStore } from '#/store';

View File

@ -7,7 +7,7 @@ import { useAccessStore } from '@vben/stores';
import { MenuBadge } from '@vben-core/menu-ui'; import { MenuBadge } from '@vben-core/menu-ui';
import { Button, Card, Radio, RadioGroup } from 'ant-design-vue'; import { Button, Card, Radio, RadioGroup } from 'antdv-next';
import { useVbenForm } from '#/adapter/form'; import { useVbenForm } from '#/adapter/form';

View File

@ -3,7 +3,7 @@ import { useRouter } from 'vue-router';
import { Fallback } from '@vben/common-ui'; import { Fallback } from '@vben/common-ui';
import { Button } from 'ant-design-vue'; import { Button } from 'antdv-next';
const router = useRouter(); const router = useRouter();
</script> </script>

View File

@ -3,7 +3,7 @@ import { useRouter } from 'vue-router';
import { Fallback } from '@vben/common-ui'; import { Fallback } from '@vben/common-ui';
import { Button } from 'ant-design-vue'; import { Button } from 'antdv-next';
const router = useRouter(); const router = useRouter();

View File

@ -4,7 +4,7 @@ import { ref } from 'vue';
import { Page } from '@vben/common-ui'; import { Page } from '@vben/common-ui';
import { useClipboard } from '@vueuse/core'; import { useClipboard } from '@vueuse/core';
import { Button, Card, Input } from 'ant-design-vue'; import { Button, Card, Input } from 'antdv-next';
const source = ref('Hello'); const source = ref('Hello');
const { copy, text } = useClipboard({ legacy: true, source }); const { copy, text } = useClipboard({ legacy: true, source });

View File

@ -9,7 +9,7 @@ import {
downloadFileFromUrl, downloadFileFromUrl,
} from '@vben/utils'; } from '@vben/utils';
import { Button, Card } from 'ant-design-vue'; import { Button, Card } from 'antdv-next';
import { downloadFile1, downloadFile2 } from '#/api/examples/download'; import { downloadFile1, downloadFile2 } from '#/api/examples/download';

View File

@ -4,7 +4,7 @@ import { ref } from 'vue';
import { Page } from '@vben/common-ui'; import { Page } from '@vben/common-ui';
import { useFullscreen } from '@vueuse/core'; import { useFullscreen } from '@vueuse/core';
import { Button, Card } from 'ant-design-vue'; import { Button, Card } from 'antdv-next';
const domRef = ref<HTMLElement>(); const domRef = ref<HTMLElement>();

View File

@ -18,7 +18,7 @@ import {
SvgWeChatIcon, SvgWeChatIcon,
} from '@vben/icons'; } from '@vben/icons';
import { Card, Input } from 'ant-design-vue'; import { Card, Input } from 'antdv-next';
const iconValue1 = ref('ant-design:trademark-outlined'); const iconValue1 = ref('ant-design:trademark-outlined');
const iconValue2 = ref('svg:avatar-1'); const iconValue2 = ref('svg:avatar-1');

View File

@ -3,7 +3,7 @@ import { ref } from 'vue';
import { Page } from '@vben/common-ui'; import { Page } from '@vben/common-ui';
import { Alert, Button, Card } from 'ant-design-vue'; import { Alert, Button, Card } from 'antdv-next';
import { getBigIntData } from '#/api/examples/json-bigint'; import { getBigIntData } from '#/api/examples/json-bigint';
@ -21,7 +21,7 @@ function fetchData() {
> >
<Card> <Card>
<Alert> <Alert>
<template #message> <template #title>
有些后端接口返回的ID是长整数但javascript原生的JSON解析是不支持超过2^53-1的长整数的 有些后端接口返回的ID是长整数但javascript原生的JSON解析是不支持超过2^53-1的长整数的
这种情况可以建议后端返回数据前将长整数转换为字符串类型如果后端不接受我们的建议😡 这种情况可以建议后端返回数据前将长整数转换为字符串类型如果后端不接受我们的建议😡
<br /> <br />

View File

@ -4,7 +4,7 @@ import type { LoginExpiredModeType } from '@vben/types';
import { Page } from '@vben/common-ui'; import { Page } from '@vben/common-ui';
import { preferences, updatePreferences } from '@vben/preferences'; import { preferences, updatePreferences } from '@vben/preferences';
import { Button, Card } from 'ant-design-vue'; import { Button, Card } from 'antdv-next';
import { getMockStatusApi } from '#/api'; import { getMockStatusApi } from '#/api';

View File

@ -9,7 +9,7 @@ import {
updateCustomPreferences, updateCustomPreferences,
} from '@vben/preferences'; } from '@vben/preferences';
import { Alert, Button, Card, Space, Tag } from 'ant-design-vue'; import { Alert, Button, Card, Space, Tag } from 'antdv-next';
import { $t } from '#/locales'; import { $t } from '#/locales';
@ -105,7 +105,7 @@ function applyPreset(type: 'compact' | 'focus' | 'review') {
}, },
}; };
updateCustomPreferences<PlaygroundPreferencesExtension>(presetMap[type]); updateCustomPreferences(presetMap[type]);
} }
function getPriorityColor(priority: DemoTaskItem['priority']) { function getPriorityColor(priority: DemoTaskItem['priority']) {
@ -136,7 +136,7 @@ function getPriorityColor(priority: DemoTaskItem['priority']) {
:title="$t('demos.preferencesExtensionDemo.currentConfig')" :title="$t('demos.preferencesExtensionDemo.currentConfig')"
> >
<Alert :type="toneConfig.alertType" show-icon> <Alert :type="toneConfig.alertType" show-icon>
<template #message> <template #title>
{{ {{
$t('demos.preferencesExtensionDemo.currentTitle', { $t('demos.preferencesExtensionDemo.currentTitle', {
title: playgroundPreferences.reportTitle, title: playgroundPreferences.reportTitle,

View File

@ -3,7 +3,7 @@ import { computed, ref, watchEffect } from 'vue';
import { Page } from '@vben/common-ui'; import { Page } from '@vben/common-ui';
import { Card, Radio, RadioGroup } from 'ant-design-vue'; import { Card, Radio, RadioGroup } from 'antdv-next';
import { getParamsData } from '#/api/examples/params'; import { getParamsData } from '#/api/examples/params';

View File

@ -5,7 +5,7 @@ import { useRouter } from 'vue-router';
import { Page } from '@vben/common-ui'; import { Page } from '@vben/common-ui';
import { useTabs } from '@vben/hooks'; import { useTabs } from '@vben/hooks';
import { Button, Card, Input } from 'ant-design-vue'; import { Button, Card, Input } from 'antdv-next';
const router = useRouter(); const router = useRouter();
const newTabTitle = ref(''); const newTabTitle = ref('');

View File

@ -2,7 +2,7 @@
import { Page } from '@vben/common-ui'; import { Page } from '@vben/common-ui';
import { refAutoReset } from '@vueuse/core'; import { refAutoReset } from '@vueuse/core';
import { Button, Card, Empty } from 'ant-design-vue'; import { Button, Card, Empty } from 'antdv-next';
import ConcurrencyCaching from './concurrency-caching.vue'; import ConcurrencyCaching from './concurrency-caching.vue';
import InfiniteQueries from './infinite-queries.vue'; import InfiniteQueries from './infinite-queries.vue';
@ -27,7 +27,7 @@ const showCaching = refAutoReset(true, 1000);
<Card <Card
title="并发和缓存" title="并发和缓存"
v-spinning="!showCaching" v-spinning="!showCaching"
:body-style="{ minHeight: '330px' }" :styles="{ body: { minHeight: '330px' } }"
> >
<template #extra> <template #extra>
<Button @click="showCaching = false">重新加载</Button> <Button @click="showCaching = false">重新加载</Button>

Some files were not shown because too many files have changed in this diff Show More