DESKTOP-USV654P\pc 1 anno fa
parent
commit
458a111dc6
100 ha cambiato i file con 1152 aggiunte e 701 eliminazioni
  1. 1 1
      .github/release-drafter.yml
  2. 5 3
      .vscode/settings.json
  3. 1 1
      README.md
  4. 1 1
      apps/backend-mock/api/auth/login.post.ts
  5. 8 8
      apps/backend-mock/utils/mock-data.ts
  6. 6 3
      apps/backend-mock/utils/response.ts
  7. 1 1
      apps/web-antd/package.json
  8. 127 0
      apps/web-antd/src/adapter/component/index.ts
  9. 6 97
      apps/web-antd/src/adapter/form.ts
  10. 1 1
      apps/web-antd/src/adapter/vxe-table.ts
  11. 9 4
      apps/web-antd/src/api/request.ts
  12. 4 0
      apps/web-antd/src/bootstrap.ts
  13. 2 2
      apps/web-antd/src/layouts/basic.vue
  14. 18 12
      apps/web-antd/src/locales/index.ts
  15. 12 0
      apps/web-antd/src/locales/langs/en-US/demos.json
  16. 14 0
      apps/web-antd/src/locales/langs/en-US/page.json
  17. 12 0
      apps/web-antd/src/locales/langs/zh-CN/demos.json
  18. 14 0
      apps/web-antd/src/locales/langs/zh-CN/page.json
  19. 6 5
      apps/web-antd/src/router/routes/core.ts
  20. 2 2
      apps/web-antd/src/router/routes/modules/demos.ts
  21. 5 5
      apps/web-antd/src/router/routes/modules/vben.ts
  22. 3 4
      apps/web-antd/src/store/auth.ts
  23. 3 2
      apps/web-antd/src/views/_core/authentication/code-login.vue
  24. 2 1
      apps/web-antd/src/views/_core/authentication/forget-password.vue
  25. 6 11
      apps/web-antd/src/views/_core/authentication/register.vue
  26. 1 1
      apps/web-baicai/package.json
  27. 1 1
      apps/web-ele/package.json
  28. 104 0
      apps/web-ele/src/adapter/component/index.ts
  29. 6 74
      apps/web-ele/src/adapter/form.ts
  30. 8 4
      apps/web-ele/src/api/request.ts
  31. 3 0
      apps/web-ele/src/bootstrap.ts
  32. 2 2
      apps/web-ele/src/layouts/basic.vue
  33. 18 12
      apps/web-ele/src/locales/index.ts
  34. 12 0
      apps/web-ele/src/locales/langs/en-US/demos.json
  35. 14 0
      apps/web-ele/src/locales/langs/en-US/page.json
  36. 12 0
      apps/web-ele/src/locales/langs/zh-CN/demos.json
  37. 14 0
      apps/web-ele/src/locales/langs/zh-CN/page.json
  38. 6 5
      apps/web-ele/src/router/routes/core.ts
  39. 2 2
      apps/web-ele/src/router/routes/modules/demos.ts
  40. 5 5
      apps/web-ele/src/router/routes/modules/vben.ts
  41. 3 4
      apps/web-ele/src/store/auth.ts
  42. 3 2
      apps/web-ele/src/views/_core/authentication/code-login.vue
  43. 2 1
      apps/web-ele/src/views/_core/authentication/forget-password.vue
  44. 6 11
      apps/web-ele/src/views/_core/authentication/register.vue
  45. 1 0
      apps/web-ele/src/views/demos/element/index.vue
  46. 1 1
      apps/web-naive/package.json
  47. 103 0
      apps/web-naive/src/adapter/component/index.ts
  48. 6 76
      apps/web-naive/src/adapter/form.ts
  49. 9 5
      apps/web-naive/src/api/request.ts
  50. 3 0
      apps/web-naive/src/bootstrap.ts
  51. 2 2
      apps/web-naive/src/layouts/basic.vue
  52. 10 3
      apps/web-naive/src/locales/index.ts
  53. 13 0
      apps/web-naive/src/locales/langs/en-US/demos.json
  54. 14 0
      apps/web-naive/src/locales/langs/en-US/page.json
  55. 13 0
      apps/web-naive/src/locales/langs/zh-CN/demos.json
  56. 14 0
      apps/web-naive/src/locales/langs/zh-CN/page.json
  57. 1 1
      apps/web-naive/src/router/access.ts
  58. 6 5
      apps/web-naive/src/router/routes/core.ts
  59. 3 3
      apps/web-naive/src/router/routes/modules/demos.ts
  60. 5 5
      apps/web-naive/src/router/routes/modules/vben.ts
  61. 4 5
      apps/web-naive/src/store/auth.ts
  62. 3 2
      apps/web-naive/src/views/_core/authentication/code-login.vue
  63. 2 1
      apps/web-naive/src/views/_core/authentication/forget-password.vue
  64. 6 11
      apps/web-naive/src/views/_core/authentication/register.vue
  65. 4 0
      docs/.vitepress/config/zh.mts
  66. 2 1
      docs/.vitepress/theme/styles/index.ts
  67. 1 1
      docs/package.json
  68. 127 0
      docs/src/_env/adapter/component.ts
  69. 12 90
      docs/src/_env/adapter/form.ts
  70. 1 1
      docs/src/commercial/community.md
  71. 7 1
      docs/src/components/common-ui/vben-drawer.md
  72. 104 61
      docs/src/components/common-ui/vben-form.md
  73. 8 1
      docs/src/components/common-ui/vben-modal.md
  74. 7 0
      docs/src/components/common-ui/vben-vxe-table.md
  75. 79 79
      docs/src/demos/vben-form/api/index.vue
  76. 1 1
      docs/src/demos/vben-form/basic/index.vue
  77. 1 1
      docs/src/demos/vben-form/custom/index.vue
  78. 1 1
      docs/src/demos/vben-form/dynamic/index.vue
  79. 1 1
      docs/src/demos/vben-form/query/index.vue
  80. 1 1
      docs/src/demos/vben-form/rules/index.vue
  81. 9 9
      docs/src/en/guide/essentials/route.md
  82. 8 4
      docs/src/en/guide/essentials/server.md
  83. 2 2
      docs/src/en/guide/essentials/settings.md
  84. 1 1
      docs/src/en/guide/in-depth/locale.md
  85. 8 1
      docs/src/en/guide/other/faq.md
  86. 1 1
      docs/src/friend-links/index.md
  87. 22 9
      docs/src/guide/essentials/route.md
  88. 8 4
      docs/src/guide/essentials/server.md
  89. 2 2
      docs/src/guide/essentials/settings.md
  90. 1 1
      docs/src/guide/in-depth/access.md
  91. 1 1
      docs/src/guide/in-depth/locale.md
  92. 1 1
      docs/src/guide/introduction/thin.md
  93. 9 2
      docs/src/guide/other/faq.md
  94. 1 1
      internal/lint-configs/commitlint-config/package.json
  95. 1 0
      internal/lint-configs/eslint-config/src/custom-config.ts
  96. 1 1
      internal/lint-configs/stylelint-config/package.json
  97. 1 1
      internal/node-utils/package.json
  98. 4 7
      internal/node-utils/src/monorepo.ts
  99. 1 1
      internal/tailwind-config/package.json
  100. 3 4
      internal/tailwind-config/src/index.ts

+ 1 - 1
.github/release-drafter.yml

@@ -16,7 +16,7 @@ categories:
   - title: '🐞 Bug Fixes'
     labels:
       - 'bug'
-  - title: '📈 Performance'
+  - title: '📈 Performance & Enhancement'
     labels:
       - 'perf'
       - 'enhancement'

+ 5 - 3
.vscode/settings.json

@@ -197,11 +197,14 @@
     "playground/src/locales/langs",
     "apps/*/src/locales/langs"
   ],
-  "i18n-ally.pathMatcher": "{locale}.json",
-  "i18n-ally.enabledParsers": ["json", "ts", "js", "yaml"],
+  "i18n-ally.pathMatcher": "{locale}/{namespace}.{ext}",
+  "i18n-ally.enabledParsers": ["json"],
   "i18n-ally.sourceLanguage": "en",
   "i18n-ally.displayLanguage": "zh-CN",
   "i18n-ally.enabledFrameworks": ["vue", "react"],
+  "i18n-ally.keystyle": "nested",
+  "i18n-ally.sortKeys": true,
+  "i18n-ally.namespace": true,
 
   // 控制相关文件嵌套展示
   "explorer.fileNesting.enabled": true,
@@ -216,7 +219,6 @@
     "tailwind.config.mjs": "postcss.*"
   },
   "commentTranslate.hover.enabled": false,
-  "i18n-ally.keystyle": "nested",
   "commentTranslate.multiLineMerge": true,
   "vue.server.hybridMode": true,
   "typescript.tsdk": "node_modules/typescript/lib"

+ 1 - 1
README.md

@@ -134,7 +134,7 @@ If you think this project is helpful to you, you can help the author buy a cup o
 
 ![donate](https://unpkg.com/@vbenjs/static-source@0.1.7/source/sponsor.png)
 
-<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aed;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
+<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aee;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
 
 ## Contributor
 

+ 1 - 1
apps/backend-mock/api/auth/login.post.ts

@@ -21,7 +21,7 @@ export default defineEventHandler(async (event) => {
 
   if (!findUser) {
     clearRefreshTokenCookie(event);
-    return forbiddenResponse(event);
+    return forbiddenResponse(event, 'Username or password is incorrect.');
   }
 
   const accessToken = generateAccessToken(findUser);

+ 8 - 8
apps/backend-mock/utils/mock-data.ts

@@ -86,7 +86,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
       component: '/demos/access/admin-visible',
       meta: {
         icon: 'mdi:button-cursor',
-        title: 'page.demos.access.adminVisible',
+        title: 'demos.access.adminVisible',
       },
       name: 'AccessAdminVisibleDemo',
       path: '/demos/access/admin-visible',
@@ -95,7 +95,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
       component: '/demos/access/super-visible',
       meta: {
         icon: 'mdi:button-cursor',
-        title: 'page.demos.access.superVisible',
+        title: 'demos.access.superVisible',
       },
       name: 'AccessSuperVisibleDemo',
       path: '/demos/access/super-visible',
@@ -104,7 +104,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
       component: '/demos/access/user-visible',
       meta: {
         icon: 'mdi:button-cursor',
-        title: 'page.demos.access.userVisible',
+        title: 'demos.access.userVisible',
       },
       name: 'AccessUserVisibleDemo',
       path: '/demos/access/user-visible',
@@ -118,7 +118,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
         icon: 'ic:baseline-view-in-ar',
         keepAlive: true,
         order: 1000,
-        title: 'page.demos.title',
+        title: 'demos.title',
       },
       name: 'Demos',
       path: '/demos',
@@ -129,7 +129,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
           path: '/demosaccess',
           meta: {
             icon: 'mdi:cloud-key-outline',
-            title: 'page.demos.access.backendPermissions',
+            title: 'demos.access.backendPermissions',
           },
           redirect: '/demos/access/page-control',
           children: [
@@ -139,7 +139,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
               component: '/demos/access/index',
               meta: {
                 icon: 'mdi:page-previous-outline',
-                title: 'page.demos.access.pageAccess',
+                title: 'demos.access.pageAccess',
               },
             },
             {
@@ -148,7 +148,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
               component: '/demos/access/button-control',
               meta: {
                 icon: 'mdi:button-cursor',
-                title: 'page.demos.access.buttonControl',
+                title: 'demos.access.buttonControl',
               },
             },
             {
@@ -159,7 +159,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
                 authority: ['no-body'],
                 icon: 'mdi:button-cursor',
                 menuVisibleWithForbidden: true,
-                title: 'page.demos.access.menuVisible403',
+                title: 'demos.access.menuVisible403',
               },
             },
             roleWithMenus[role],

+ 6 - 3
apps/backend-mock/utils/response.ts

@@ -39,14 +39,17 @@ export function useResponseError(message: string, error: any = null) {
   };
 }
 
-export function forbiddenResponse(event: H3Event<EventHandlerRequest>) {
+export function forbiddenResponse(
+  event: H3Event<EventHandlerRequest>,
+  message = 'Forbidden Exception',
+) {
   setResponseStatus(event, 403);
-  return useResponseError('ForbiddenException', 'Forbidden Exception');
+  return useResponseError(message, message);
 }
 
 export function unAuthorizedResponse(event: H3Event<EventHandlerRequest>) {
   setResponseStatus(event, 401);
-  return useResponseError('UnauthorizedException', 'Unauthorized Exception');
+  return useResponseError('Unauthorized Exception', 'Unauthorized Exception');
 }
 
 export function sleep(ms: number) {

+ 1 - 1
apps/web-antd/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vben/web-antd",
-  "version": "5.3.2",
+  "version": "5.4.2",
   "homepage": "https://vben.pro",
   "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
   "repository": {

+ 127 - 0
apps/web-antd/src/adapter/component/index.ts

@@ -0,0 +1,127 @@
+/**
+ * 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用
+ * 可用于 vben-form、vben-modal、vben-drawer 等组件使用,
+ */
+
+import type { BaseFormComponentType } from '@vben/common-ui';
+
+import type { Component, SetupContext } from 'vue';
+import { h } from 'vue';
+
+import { globalShareState } from '@vben/common-ui';
+import { $t } from '@vben/locales';
+
+import {
+  AutoComplete,
+  Button,
+  Checkbox,
+  CheckboxGroup,
+  DatePicker,
+  Divider,
+  Input,
+  InputNumber,
+  InputPassword,
+  Mentions,
+  notification,
+  Radio,
+  RadioGroup,
+  RangePicker,
+  Rate,
+  Select,
+  Space,
+  Switch,
+  Textarea,
+  TimePicker,
+  TreeSelect,
+  Upload,
+} from 'ant-design-vue';
+
+const withDefaultPlaceholder = <T extends Component>(
+  component: T,
+  type: 'input' | 'select',
+) => {
+  return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
+    const placeholder = props?.placeholder || $t(`ui.placeholder.${type}`);
+    return h(component, { ...props, ...attrs, placeholder }, slots);
+  };
+};
+
+// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
+export type ComponentType =
+  | 'AutoComplete'
+  | 'Checkbox'
+  | 'CheckboxGroup'
+  | 'DatePicker'
+  | 'DefaultButton'
+  | 'Divider'
+  | 'Input'
+  | 'InputNumber'
+  | 'InputPassword'
+  | 'Mentions'
+  | 'PrimaryButton'
+  | 'Radio'
+  | 'RadioGroup'
+  | 'RangePicker'
+  | 'Rate'
+  | 'Select'
+  | 'Space'
+  | 'Switch'
+  | 'Textarea'
+  | 'TimePicker'
+  | 'TreeSelect'
+  | 'Upload'
+  | BaseFormComponentType;
+
+async function initComponentAdapter() {
+  const components: Partial<Record<ComponentType, Component>> = {
+    // 如果你的组件体积比较大,可以使用异步加载
+    // Button: () =>
+    // import('xxx').then((res) => res.Button),
+
+    AutoComplete,
+    Checkbox,
+    CheckboxGroup,
+    DatePicker,
+    // 自定义默认按钮
+    DefaultButton: (props, { attrs, slots }) => {
+      return h(Button, { ...props, attrs, type: 'default' }, slots);
+    },
+    Divider,
+    Input: withDefaultPlaceholder(Input, 'input'),
+    InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
+    InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
+    Mentions: withDefaultPlaceholder(Mentions, 'input'),
+    // 自定义主要按钮
+    PrimaryButton: (props, { attrs, slots }) => {
+      return h(Button, { ...props, attrs, type: 'primary' }, slots);
+    },
+    Radio,
+    RadioGroup,
+    RangePicker,
+    Rate,
+    Select: withDefaultPlaceholder(Select, 'select'),
+    Space,
+    Switch,
+    Textarea: withDefaultPlaceholder(Textarea, 'input'),
+    TimePicker,
+    TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'),
+    Upload,
+  };
+
+  // 将组件注册到全局共享状态中
+  globalShareState.setComponents(components);
+
+  // 定义全局共享状态中的消息提示
+  globalShareState.defineMessage({
+    // 复制成功消息提示
+    copyPreferencesSuccess: (title, content) => {
+      notification.success({
+        description: content,
+        message: title,
+        placement: 'bottomRight',
+      });
+    },
+  });
+}
+
+export { initComponentAdapter };

+ 6 - 97
apps/web-antd/src/adapter/form.ts

@@ -1,105 +1,14 @@
 import type {
-  BaseFormComponentType,
   VbenFormSchema as FormSchema,
   VbenFormProps,
 } from '@vben/common-ui';
 
-import type { Component, SetupContext } from 'vue';
-import { h } from 'vue';
+import type { ComponentType } from './component';
 
 import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
 import { $t } from '@vben/locales';
 
-import {
-  AutoComplete,
-  Button,
-  Checkbox,
-  CheckboxGroup,
-  DatePicker,
-  Divider,
-  Input,
-  InputNumber,
-  InputPassword,
-  Mentions,
-  Radio,
-  RadioGroup,
-  RangePicker,
-  Rate,
-  Select,
-  Space,
-  Switch,
-  Textarea,
-  TimePicker,
-  TreeSelect,
-  Upload,
-} from 'ant-design-vue';
-
-// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
-export type FormComponentType =
-  | 'AutoComplete'
-  | 'Checkbox'
-  | 'CheckboxGroup'
-  | 'DatePicker'
-  | 'Divider'
-  | 'Input'
-  | 'InputNumber'
-  | 'InputPassword'
-  | 'Mentions'
-  | 'Radio'
-  | 'RadioGroup'
-  | 'RangePicker'
-  | 'Rate'
-  | 'Select'
-  | 'Space'
-  | 'Switch'
-  | 'Textarea'
-  | 'TimePicker'
-  | 'TreeSelect'
-  | 'Upload'
-  | BaseFormComponentType;
-
-const withDefaultPlaceholder = <T extends Component>(
-  component: T,
-  type: 'input' | 'select',
-) => {
-  return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
-    const placeholder = props?.placeholder || $t(`placeholder.${type}`);
-    return h(component, { ...props, ...attrs, placeholder }, slots);
-  };
-};
-
-// 初始化表单组件,并注册到form组件内部
-setupVbenForm<FormComponentType>({
-  components: {
-    AutoComplete,
-    Checkbox,
-    CheckboxGroup,
-    DatePicker,
-    // 自定义默认的重置按钮
-    DefaultResetActionButton: (props, { attrs, slots }) => {
-      return h(Button, { ...props, attrs, type: 'default' }, slots);
-    },
-    // 自定义默认的提交按钮
-    DefaultSubmitActionButton: (props, { attrs, slots }) => {
-      return h(Button, { ...props, attrs, type: 'primary' }, slots);
-    },
-    Divider,
-    Input: withDefaultPlaceholder(Input, 'input'),
-    InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
-    InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
-    Mentions: withDefaultPlaceholder(Mentions, 'input'),
-    Radio,
-    RadioGroup,
-    RangePicker,
-    Rate,
-    Select: withDefaultPlaceholder(Select, 'select'),
-    Space,
-    Switch,
-    Textarea: withDefaultPlaceholder(Textarea, 'input'),
-    TimePicker,
-    TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'),
-    Upload,
-  },
+setupVbenForm<ComponentType>({
   config: {
     // ant design vue组件库默认都是 v-model:value
     baseModelPropName: 'value',
@@ -116,23 +25,23 @@ setupVbenForm<FormComponentType>({
     // 输入项目必填国际化适配
     required: (value, _params, ctx) => {
       if (value === undefined || value === null || value.length === 0) {
-        return $t('formRules.required', [ctx.label]);
+        return $t('ui.formRules.required', [ctx.label]);
       }
       return true;
     },
     // 选择项目必填国际化适配
     selectRequired: (value, _params, ctx) => {
       if (value === undefined || value === null) {
-        return $t('formRules.selectRequired', [ctx.label]);
+        return $t('ui.formRules.selectRequired', [ctx.label]);
       }
       return true;
     },
   },
 });
 
-const useVbenForm = useForm<FormComponentType>;
+const useVbenForm = useForm<ComponentType>;
 
 export { useVbenForm, z };
 
-export type VbenFormSchema = FormSchema<FormComponentType>;
+export type VbenFormSchema = FormSchema<ComponentType>;
 export type { VbenFormProps };

+ 1 - 1
apps/web-antd/src/adapter/vxe-table.ts

@@ -18,7 +18,7 @@ setupVbenVxeTable({
           response: {
             result: 'items',
             total: 'total',
-            list: 'data',
+            list: 'items',
           },
           showActiveMsg: true,
           showResponseMsg: false,

+ 9 - 4
apps/web-antd/src/api/request.ts

@@ -74,11 +74,12 @@ function createRequestClient(baseURL: string) {
     fulfilled: (response) => {
       const { data: responseData, status } = response;
 
-      const { code, data, message: msg } = responseData;
+      const { code, data } = responseData;
       if (status >= 200 && status < 400 && code === 0) {
         return data;
       }
-      throw new Error(`Error ${status}: ${msg}`);
+
+      throw Object.assign({}, response, { response });
     },
   });
 
@@ -95,9 +96,13 @@ function createRequestClient(baseURL: string) {
 
   // 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
   client.addResponseInterceptor(
-    errorMessageResponseInterceptor((msg: string, _error) => {
+    errorMessageResponseInterceptor((msg: string, error) => {
       // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
-      message.error(msg);
+      // 当前mock接口返回的错误字段是 error 或者 message
+      const responseData = error?.response?.data ?? {};
+      const errorMessage = responseData?.error ?? responseData?.message ?? '';
+      // 如果没有错误信息,则会根据状态码进行提示
+      message.error(errorMessage || msg);
     }),
   );
 

+ 4 - 0
apps/web-antd/src/bootstrap.ts

@@ -7,10 +7,14 @@ import '@vben/styles/antd';
 
 import { setupI18n } from '#/locales';
 
+import { initComponentAdapter } from './adapter/component';
 import App from './app.vue';
 import { router } from './router';
 
 async function bootstrap(namespace: string) {
+  // 初始化组件适配器
+  await initComponentAdapter();
+
   const app = createApp(App);
 
   // 国际化 i18n 配置

+ 2 - 2
apps/web-antd/src/layouts/basic.vue

@@ -68,7 +68,7 @@ const menus = computed(() => [
       });
     },
     icon: BookOpenText,
-    text: $t('widgets.document'),
+    text: $t('ui.widgets.document'),
   },
   {
     handler: () => {
@@ -86,7 +86,7 @@ const menus = computed(() => [
       });
     },
     icon: CircleHelp,
-    text: $t('widgets.qa'),
+    text: $t('ui.widgets.qa'),
   },
 ]);
 

+ 18 - 12
apps/web-antd/src/locales/index.ts

@@ -4,7 +4,11 @@ import type { Locale } from 'ant-design-vue/es/locale';
 import type { App } from 'vue';
 import { ref } from 'vue';
 
-import { $t, setupI18n as coreSetup, loadLocalesMap } from '@vben/locales';
+import {
+  $t,
+  setupI18n as coreSetup,
+  loadLocalesMapFromDir,
+} from '@vben/locales';
 import { preferences } from '@vben/preferences';
 
 import antdEnLocale from 'ant-design-vue/es/locale/en_US';
@@ -13,10 +17,12 @@ import dayjs from 'dayjs';
 
 const antdLocale = ref<Locale>(antdDefaultLocale);
 
-const modules = import.meta.glob('./langs/*.json');
-
-const localesMap = loadLocalesMap(modules);
+const modules = import.meta.glob('./langs/**/*.json');
 
+const localesMap = loadLocalesMapFromDir(
+  /\.\/langs\/([^/]+)\/(.*)\.json$/,
+  modules,
+);
 /**
  * 加载应用特有的语言包
  * 这里也可以改造为从服务端获取翻译数据
@@ -45,14 +51,14 @@ async function loadThirdPartyMessage(lang: SupportedLanguagesType) {
 async function loadDayjsLocale(lang: SupportedLanguagesType) {
   let locale;
   switch (lang) {
-    case 'zh-CN': {
-      locale = await import('dayjs/locale/zh-cn');
-      break;
-    }
     case 'en-US': {
       locale = await import('dayjs/locale/en');
       break;
     }
+    case 'zh-CN': {
+      locale = await import('dayjs/locale/zh-cn');
+      break;
+    }
     // 默认使用英语
     default: {
       locale = await import('dayjs/locale/en');
@@ -71,14 +77,14 @@ async function loadDayjsLocale(lang: SupportedLanguagesType) {
  */
 async function loadAntdLocale(lang: SupportedLanguagesType) {
   switch (lang) {
-    case 'zh-CN': {
-      antdLocale.value = antdDefaultLocale;
-      break;
-    }
     case 'en-US': {
       antdLocale.value = antdEnLocale;
       break;
     }
+    case 'zh-CN': {
+      antdLocale.value = antdDefaultLocale;
+      break;
+    }
   }
 }
 

+ 12 - 0
apps/web-antd/src/locales/langs/en-US/demos.json

@@ -0,0 +1,12 @@
+{
+  "title": "Demos",
+  "antd": "Ant Design Vue",
+  "vben": {
+    "title": "Project",
+    "about": "About",
+    "document": "Document",
+    "antdv": "Ant Design Vue Version",
+    "naive-ui": "Naive UI Version",
+    "element-plus": "Element Plus Version"
+  }
+}

+ 14 - 0
apps/web-antd/src/locales/langs/en-US/page.json

@@ -0,0 +1,14 @@
+{
+  "auth": {
+    "login": "Login",
+    "register": "Register",
+    "codeLogin": "Code Login",
+    "qrcodeLogin": "Qr Code Login",
+    "forgetPassword": "Forget Password"
+  },
+  "dashboard": {
+    "title": "Dashboard",
+    "analytics": "Analytics",
+    "workspace": "Workspace"
+  }
+}

+ 12 - 0
apps/web-antd/src/locales/langs/zh-CN/demos.json

@@ -0,0 +1,12 @@
+{
+  "title": "演示",
+  "antd": "Ant Design Vue",
+  "vben": {
+    "title": "项目",
+    "about": "关于",
+    "document": "文档",
+    "antdv": "Ant Design Vue 版本",
+    "naive-ui": "Naive UI 版本",
+    "element-plus": "Element Plus 版本"
+  }
+}

+ 14 - 0
apps/web-antd/src/locales/langs/zh-CN/page.json

@@ -0,0 +1,14 @@
+{
+  "auth": {
+    "login": "登录",
+    "register": "注册",
+    "codeLogin": "验证码登录",
+    "qrcodeLogin": "二维码登录",
+    "forgetPassword": "忘记密码"
+  },
+  "dashboard": {
+    "title": "概览",
+    "analytics": "分析页",
+    "workspace": "工作台"
+  }
+}

+ 6 - 5
apps/web-antd/src/router/routes/core.ts

@@ -32,6 +32,7 @@ const coreRoutes: RouteRecordRaw[] = [
   {
     component: AuthPageLayout,
     meta: {
+      hideInTab: true,
       title: 'Authentication',
     },
     name: 'Authentication',
@@ -42,7 +43,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'login',
         component: Login,
         meta: {
-          title: $t('page.core.login'),
+          title: $t('page.auth.login'),
         },
       },
       {
@@ -50,7 +51,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'code-login',
         component: () => import('#/views/_core/authentication/code-login.vue'),
         meta: {
-          title: $t('page.core.codeLogin'),
+          title: $t('page.auth.codeLogin'),
         },
       },
       {
@@ -59,7 +60,7 @@ const coreRoutes: RouteRecordRaw[] = [
         component: () =>
           import('#/views/_core/authentication/qrcode-login.vue'),
         meta: {
-          title: $t('page.core.qrcodeLogin'),
+          title: $t('page.auth.qrcodeLogin'),
         },
       },
       {
@@ -68,7 +69,7 @@ const coreRoutes: RouteRecordRaw[] = [
         component: () =>
           import('#/views/_core/authentication/forget-password.vue'),
         meta: {
-          title: $t('page.core.forgetPassword'),
+          title: $t('page.auth.forgetPassword'),
         },
       },
       {
@@ -76,7 +77,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'register',
         component: () => import('#/views/_core/authentication/register.vue'),
         meta: {
-          title: $t('page.core.register'),
+          title: $t('page.auth.register'),
         },
       },
     ],

+ 2 - 2
apps/web-antd/src/router/routes/modules/demos.ts

@@ -10,14 +10,14 @@ const routes: RouteRecordRaw[] = [
       icon: 'ic:baseline-view-in-ar',
       keepAlive: true,
       order: 1000,
-      title: $t('page.demos.title'),
+      title: $t('demos.title'),
     },
     name: 'Demos',
     path: '/demos',
     children: [
       {
         meta: {
-          title: $t('page.demos.antd'),
+          title: $t('demos.antd'),
         },
         name: 'AntDesignDemos',
         path: '/demos/ant-design',

+ 5 - 5
apps/web-antd/src/router/routes/modules/vben.ts

@@ -18,7 +18,7 @@ const routes: RouteRecordRaw[] = [
       badgeType: 'dot',
       icon: VBEN_LOGO_URL,
       order: 9999,
-      title: $t('page.vben.title'),
+      title: $t('demos.vben.title'),
     },
     name: 'VbenProject',
     path: '/vben-admin',
@@ -29,7 +29,7 @@ const routes: RouteRecordRaw[] = [
         component: () => import('#/views/_core/about/index.vue'),
         meta: {
           icon: 'lucide:copyright',
-          title: $t('page.vben.about'),
+          title: $t('demos.vben.about'),
         },
       },
       {
@@ -39,7 +39,7 @@ const routes: RouteRecordRaw[] = [
         meta: {
           icon: 'lucide:book-open-text',
           link: VBEN_DOC_URL,
-          title: $t('page.vben.document'),
+          title: $t('demos.vben.document'),
         },
       },
       {
@@ -60,7 +60,7 @@ const routes: RouteRecordRaw[] = [
           badgeType: 'dot',
           icon: 'logos:naiveui',
           link: VBEN_NAIVE_PREVIEW_URL,
-          title: $t('page.vben.naive-ui'),
+          title: $t('demos.vben.naive-ui'),
         },
       },
       {
@@ -71,7 +71,7 @@ const routes: RouteRecordRaw[] = [
           badgeType: 'dot',
           icon: 'logos:element',
           link: VBEN_ELE_PREVIEW_URL,
-          title: $t('page.vben.element-plus'),
+          title: $t('demos.vben.element-plus'),
         },
       },
     ],

+ 3 - 4
apps/web-antd/src/store/auth.ts

@@ -1,5 +1,4 @@
-import type { LoginAndRegisterParams } from '@vben/common-ui';
-import type { UserInfo } from '@vben/types';
+import type { Recordable, UserInfo } from '@vben/types';
 
 import { ref } from 'vue';
 import { useRouter } from 'vue-router';
@@ -26,7 +25,7 @@ export const useAuthStore = defineStore('auth', () => {
    * @param params 登录表单数据
    */
   async function authLogin(
-    params: LoginAndRegisterParams,
+    params: Recordable<any>,
     onSuccess?: () => Promise<void> | void,
   ) {
     // 异步处理用户登录操作并获取 accessToken
@@ -84,7 +83,7 @@ export const useAuthStore = defineStore('auth', () => {
     resetAllStores();
     accessStore.setLoginExpired(false);
 
-    // 回登页带上当前路由地址
+    // 回登页带上当前路由地址
     await router.replace({
       path: LOGIN_PATH,
       query: redirect

+ 3 - 2
apps/web-antd/src/views/_core/authentication/code-login.vue

@@ -1,5 +1,6 @@
 <script lang="ts" setup>
-import type { LoginCodeParams, VbenFormSchema } from '@vben/common-ui';
+import type { VbenFormSchema } from '@vben/common-ui';
+import type { Recordable } from '@vben/types';
 
 import { computed, ref } from 'vue';
 
@@ -49,7 +50,7 @@ const formSchema = computed((): VbenFormSchema[] => {
  * Asynchronously handle the login process
  * @param values 登录表单数据
  */
-async function handleLogin(values: LoginCodeParams) {
+async function handleLogin(values: Recordable<any>) {
   // eslint-disable-next-line no-console
   console.log(values);
 }

+ 2 - 1
apps/web-antd/src/views/_core/authentication/forget-password.vue

@@ -1,5 +1,6 @@
 <script lang="ts" setup>
 import type { VbenFormSchema } from '@vben/common-ui';
+import type { Recordable } from '@vben/types';
 
 import { computed, ref } from 'vue';
 
@@ -27,7 +28,7 @@ const formSchema = computed((): VbenFormSchema[] => {
   ];
 });
 
-function handleSubmit(value: string) {
+function handleSubmit(value: Recordable<any>) {
   // eslint-disable-next-line no-console
   console.log('reset email:', value);
 }

+ 6 - 11
apps/web-antd/src/views/_core/authentication/register.vue

@@ -1,5 +1,6 @@
 <script lang="ts" setup>
-import type { LoginAndRegisterParams, VbenFormSchema } from '@vben/common-ui';
+import type { VbenFormSchema } from '@vben/common-ui';
+import type { Recordable } from '@vben/types';
 
 import { computed, h, ref } from 'vue';
 
@@ -45,7 +46,7 @@ const formSchema = computed((): VbenFormSchema[] => {
         rules(values) {
           const { password } = values;
           return z
-            .string()
+            .string({ required_error: $t('authentication.passwordTip') })
             .min(1, { message: $t('authentication.passwordTip') })
             .refine((value) => value === password, {
               message: $t('authentication.confirmPasswordTip'),
@@ -55,7 +56,6 @@ const formSchema = computed((): VbenFormSchema[] => {
       },
       fieldName: 'confirmPassword',
       label: $t('authentication.confirmPassword'),
-      rules: z.string().min(1, { message: $t('authentication.passwordTip') }),
     },
     {
       component: 'VbenCheckbox',
@@ -67,15 +67,10 @@ const formSchema = computed((): VbenFormSchema[] => {
             h(
               'a',
               {
-                class:
-                  'cursor-pointer text-primary ml-1 hover:text-primary-hover',
+                class: 'vben-link ml-1 ',
                 href: '',
               },
-              [
-                $t('authentication.privacyPolicy'),
-                '&',
-                $t('authentication.terms'),
-              ],
+              `${$t('authentication.privacyPolicy')} & ${$t('authentication.terms')}`,
             ),
           ]),
       }),
@@ -86,7 +81,7 @@ const formSchema = computed((): VbenFormSchema[] => {
   ];
 });
 
-function handleSubmit(value: LoginAndRegisterParams) {
+function handleSubmit(value: Recordable<any>) {
   // eslint-disable-next-line no-console
   console.log('register submit:', value);
 }

+ 1 - 1
apps/web-baicai/package.json

@@ -48,7 +48,7 @@
     "ant-design-vue": "catalog:",
     "dayjs": "catalog:",
     "pinia": "catalog:",
-    "sm-crypto-v2": "catalog:",
+    "sm-crypto-v2": "^1.9.2",
     "vue": "catalog:",
     "vue-router": "catalog:"
   }

+ 1 - 1
apps/web-ele/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vben/web-ele",
-  "version": "5.3.2",
+  "version": "5.4.2",
   "homepage": "https://vben.pro",
   "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
   "repository": {

+ 104 - 0
apps/web-ele/src/adapter/component/index.ts

@@ -0,0 +1,104 @@
+/**
+ * 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用
+ * 可用于 vben-form、vben-modal、vben-drawer 等组件使用,
+ */
+
+import type { BaseFormComponentType } from '@vben/common-ui';
+
+import type { Component, SetupContext } from 'vue';
+import { h } from 'vue';
+
+import { globalShareState } from '@vben/common-ui';
+import { $t } from '@vben/locales';
+
+import {
+  ElButton,
+  ElCheckbox,
+  ElCheckboxGroup,
+  ElDivider,
+  ElInput,
+  ElInputNumber,
+  ElNotification,
+  ElRadioGroup,
+  ElSelect,
+  ElSpace,
+  ElSwitch,
+  ElTimePicker,
+  ElTreeSelect,
+  ElUpload,
+} from 'element-plus';
+
+const withDefaultPlaceholder = <T extends Component>(
+  component: T,
+  type: 'input' | 'select',
+) => {
+  return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
+    const placeholder = props?.placeholder || $t(`ui.placeholder.${type}`);
+    return h(component, { ...props, ...attrs, placeholder }, slots);
+  };
+};
+
+// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
+export type ComponentType =
+  | 'Checkbox'
+  | 'CheckboxGroup'
+  | 'DatePicker'
+  | 'Divider'
+  | 'Input'
+  | 'InputNumber'
+  | 'RadioGroup'
+  | 'Select'
+  | 'Space'
+  | 'Switch'
+  | 'TimePicker'
+  | 'TreeSelect'
+  | 'Upload'
+  | BaseFormComponentType;
+
+async function initComponentAdapter() {
+  const components: Partial<Record<ComponentType, Component>> = {
+    // 如果你的组件体积比较大,可以使用异步加载
+    // Button: () =>
+    // import('xxx').then((res) => res.Button),
+
+    Checkbox: ElCheckbox,
+    CheckboxGroup: ElCheckboxGroup,
+    // 自定义默认按钮
+    DefaulButton: (props, { attrs, slots }) => {
+      return h(ElButton, { ...props, attrs, type: 'info' }, slots);
+    },
+    // 自定义主要按钮
+    PrimaryButton: (props, { attrs, slots }) => {
+      return h(ElButton, { ...props, attrs, type: 'primary' }, slots);
+    },
+    Divider: ElDivider,
+    Input: withDefaultPlaceholder(ElInput, 'input'),
+    InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'),
+    RadioGroup: ElRadioGroup,
+    Select: withDefaultPlaceholder(ElSelect, 'select'),
+    Space: ElSpace,
+    Switch: ElSwitch,
+    TimePicker: ElTimePicker,
+    TreeSelect: withDefaultPlaceholder(ElTreeSelect, 'select'),
+    Upload: ElUpload,
+  };
+
+  // 将组件注册到全局共享状态中
+  globalShareState.setComponents(components);
+
+  // 定义全局共享状态中的消息提示
+  globalShareState.defineMessage({
+    // 复制成功消息提示
+    copyPreferencesSuccess: (title, content) => {
+      ElNotification({
+        title,
+        message: content,
+        position: 'bottom-right',
+        duration: 0,
+        type: 'success',
+      });
+    },
+  });
+}
+
+export { initComponentAdapter };

+ 6 - 74
apps/web-ele/src/adapter/form.ts

@@ -1,82 +1,14 @@
 import type {
-  BaseFormComponentType,
   VbenFormSchema as FormSchema,
   VbenFormProps,
 } from '@vben/common-ui';
 
-import type { Component, SetupContext } from 'vue';
-import { h } from 'vue';
+import type { ComponentType } from './component';
 
 import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
 import { $t } from '@vben/locales';
 
-import {
-  ElButton,
-  ElCheckbox,
-  ElCheckboxGroup,
-  ElDivider,
-  ElInput,
-  ElInputNumber,
-  ElRadioGroup,
-  ElSelect,
-  ElSpace,
-  ElSwitch,
-  ElTimePicker,
-  ElTreeSelect,
-  ElUpload,
-} from 'element-plus';
-// 业务表单组件适配
-
-export type FormComponentType =
-  | 'Checkbox'
-  | 'CheckboxGroup'
-  | 'DatePicker'
-  | 'Divider'
-  | 'Input'
-  | 'InputNumber'
-  | 'RadioGroup'
-  | 'Select'
-  | 'Space'
-  | 'Switch'
-  | 'TimePicker'
-  | 'TreeSelect'
-  | 'Upload'
-  | BaseFormComponentType;
-
-const withDefaultPlaceholder = <T extends Component>(
-  component: T,
-  type: 'input' | 'select',
-) => {
-  return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
-    const placeholder = props?.placeholder || $t(`placeholder.${type}`);
-    return h(component, { ...props, ...attrs, placeholder }, slots);
-  };
-};
-
-// 初始化表单组件,并注册到form组件内部
-setupVbenForm<FormComponentType>({
-  components: {
-    Checkbox: ElCheckbox,
-    CheckboxGroup: ElCheckboxGroup,
-    // 自定义默认的重置按钮
-    DefaultResetActionButton: (props, { attrs, slots }) => {
-      return h(ElButton, { ...props, attrs, type: 'info' }, slots);
-    },
-    // 自定义默认的提交按钮
-    DefaultSubmitActionButton: (props, { attrs, slots }) => {
-      return h(ElButton, { ...props, attrs, type: 'primary' }, slots);
-    },
-    Divider: ElDivider,
-    Input: withDefaultPlaceholder(ElInput, 'input'),
-    InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'),
-    RadioGroup: ElRadioGroup,
-    Select: withDefaultPlaceholder(ElSelect, 'select'),
-    Space: ElSpace,
-    Switch: ElSwitch,
-    TimePicker: ElTimePicker,
-    TreeSelect: withDefaultPlaceholder(ElTreeSelect, 'select'),
-    Upload: ElUpload,
-  },
+setupVbenForm<ComponentType>({
   config: {
     modelPropNameMap: {
       Upload: 'fileList',
@@ -85,22 +17,22 @@ setupVbenForm<FormComponentType>({
   defineRules: {
     required: (value, _params, ctx) => {
       if (value === undefined || value === null || value.length === 0) {
-        return $t('formRules.required', [ctx.label]);
+        return $t('ui.formRules.required', [ctx.label]);
       }
       return true;
     },
     selectRequired: (value, _params, ctx) => {
       if (value === undefined || value === null) {
-        return $t('formRules.selectRequired', [ctx.label]);
+        return $t('ui.formRules.selectRequired', [ctx.label]);
       }
       return true;
     },
   },
 });
 
-const useVbenForm = useForm<FormComponentType>;
+const useVbenForm = useForm<ComponentType>;
 
 export { useVbenForm, z };
 
-export type VbenFormSchema = FormSchema<FormComponentType>;
+export type VbenFormSchema = FormSchema<ComponentType>;
 export type { VbenFormProps };

+ 8 - 4
apps/web-ele/src/api/request.ts

@@ -74,11 +74,11 @@ function createRequestClient(baseURL: string) {
     fulfilled: (response) => {
       const { data: responseData, status } = response;
 
-      const { code, data, message: msg } = responseData;
+      const { code, data } = responseData;
       if (status >= 200 && status < 400 && code === 0) {
         return data;
       }
-      throw new Error(`Error ${status}: ${msg}`);
+      throw Object.assign({}, response, { response });
     },
   });
 
@@ -95,9 +95,13 @@ function createRequestClient(baseURL: string) {
 
   // 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
   client.addResponseInterceptor(
-    errorMessageResponseInterceptor((msg: string, _error) => {
+    errorMessageResponseInterceptor((msg: string, error) => {
       // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
-      ElMessage.error(msg);
+      // 当前mock接口返回的错误字段是 error 或者 message
+      const responseData = error?.response?.data ?? {};
+      const errorMessage = responseData?.error ?? responseData?.message ?? '';
+      // 如果没有错误信息,则会根据状态码进行提示
+      ElMessage.error(errorMessage || msg);
     }),
   );
 

+ 3 - 0
apps/web-ele/src/bootstrap.ts

@@ -7,10 +7,13 @@ import '@vben/styles/ele';
 
 import { setupI18n } from '#/locales';
 
+import { initComponentAdapter } from './adapter/component';
 import App from './app.vue';
 import { router } from './router';
 
 async function bootstrap(namespace: string) {
+  // 初始化组件适配器
+  await initComponentAdapter();
   const app = createApp(App);
 
   // 国际化 i18n 配置

+ 2 - 2
apps/web-ele/src/layouts/basic.vue

@@ -68,7 +68,7 @@ const menus = computed(() => [
       });
     },
     icon: BookOpenText,
-    text: $t('widgets.document'),
+    text: $t('ui.widgets.document'),
   },
   {
     handler: () => {
@@ -86,7 +86,7 @@ const menus = computed(() => [
       });
     },
     icon: CircleHelp,
-    text: $t('widgets.qa'),
+    text: $t('ui.widgets.qa'),
   },
 ]);
 

+ 18 - 12
apps/web-ele/src/locales/index.ts

@@ -4,7 +4,11 @@ import type { Language } from 'element-plus/es/locale';
 import type { App } from 'vue';
 import { ref } from 'vue';
 
-import { $t, setupI18n as coreSetup, loadLocalesMap } from '@vben/locales';
+import {
+  $t,
+  setupI18n as coreSetup,
+  loadLocalesMapFromDir,
+} from '@vben/locales';
 import { preferences } from '@vben/preferences';
 
 import dayjs from 'dayjs';
@@ -13,10 +17,12 @@ import defaultLocale from 'element-plus/es/locale/lang/zh-cn';
 
 const elementLocale = ref<Language>(defaultLocale);
 
-const modules = import.meta.glob('./langs/*.json');
-
-const localesMap = loadLocalesMap(modules);
+const modules = import.meta.glob('./langs/**/*.json');
 
+const localesMap = loadLocalesMapFromDir(
+  /\.\/langs\/([^/]+)\/(.*)\.json$/,
+  modules,
+);
 /**
  * 加载应用特有的语言包
  * 这里也可以改造为从服务端获取翻译数据
@@ -45,14 +51,14 @@ async function loadThirdPartyMessage(lang: SupportedLanguagesType) {
 async function loadDayjsLocale(lang: SupportedLanguagesType) {
   let locale;
   switch (lang) {
-    case 'zh-CN': {
-      locale = await import('dayjs/locale/zh-cn');
-      break;
-    }
     case 'en-US': {
       locale = await import('dayjs/locale/en');
       break;
     }
+    case 'zh-CN': {
+      locale = await import('dayjs/locale/zh-cn');
+      break;
+    }
     // 默认使用英语
     default: {
       locale = await import('dayjs/locale/en');
@@ -71,14 +77,14 @@ async function loadDayjsLocale(lang: SupportedLanguagesType) {
  */
 async function loadElementLocale(lang: SupportedLanguagesType) {
   switch (lang) {
-    case 'zh-CN': {
-      elementLocale.value = defaultLocale;
-      break;
-    }
     case 'en-US': {
       elementLocale.value = enLocale;
       break;
     }
+    case 'zh-CN': {
+      elementLocale.value = defaultLocale;
+      break;
+    }
   }
 }
 

+ 12 - 0
apps/web-ele/src/locales/langs/en-US/demos.json

@@ -0,0 +1,12 @@
+{
+  "title": "Demos",
+  "elementPlus": "Element Plus",
+  "vben": {
+    "title": "Project",
+    "about": "About",
+    "document": "Document",
+    "antdv": "Ant Design Vue Version",
+    "naive-ui": "Naive UI Version",
+    "element-plus": "Element Plus Version"
+  }
+}

+ 14 - 0
apps/web-ele/src/locales/langs/en-US/page.json

@@ -0,0 +1,14 @@
+{
+  "auth": {
+    "login": "Login",
+    "register": "Register",
+    "codeLogin": "Code Login",
+    "qrcodeLogin": "Qr Code Login",
+    "forgetPassword": "Forget Password"
+  },
+  "dashboard": {
+    "title": "Dashboard",
+    "analytics": "Analytics",
+    "workspace": "Workspace"
+  }
+}

+ 12 - 0
apps/web-ele/src/locales/langs/zh-CN/demos.json

@@ -0,0 +1,12 @@
+{
+  "title": "演示",
+  "elementPlus": "Element Plus",
+  "vben": {
+    "title": "项目",
+    "about": "关于",
+    "document": "文档",
+    "antdv": "Ant Design Vue 版本",
+    "naive-ui": "Naive UI 版本",
+    "element-plus": "Element Plus 版本"
+  }
+}

+ 14 - 0
apps/web-ele/src/locales/langs/zh-CN/page.json

@@ -0,0 +1,14 @@
+{
+  "auth": {
+    "login": "登录",
+    "register": "注册",
+    "codeLogin": "验证码登录",
+    "qrcodeLogin": "二维码登录",
+    "forgetPassword": "忘记密码"
+  },
+  "dashboard": {
+    "title": "概览",
+    "analytics": "分析页",
+    "workspace": "工作台"
+  }
+}

+ 6 - 5
apps/web-ele/src/router/routes/core.ts

@@ -32,6 +32,7 @@ const coreRoutes: RouteRecordRaw[] = [
   {
     component: AuthPageLayout,
     meta: {
+      hideInTab: true,
       title: 'Authentication',
     },
     name: 'Authentication',
@@ -42,7 +43,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'login',
         component: Login,
         meta: {
-          title: $t('page.core.login'),
+          title: $t('page.auth.login'),
         },
       },
       {
@@ -50,7 +51,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'code-login',
         component: () => import('#/views/_core/authentication/code-login.vue'),
         meta: {
-          title: $t('page.core.codeLogin'),
+          title: $t('page.auth.codeLogin'),
         },
       },
       {
@@ -59,7 +60,7 @@ const coreRoutes: RouteRecordRaw[] = [
         component: () =>
           import('#/views/_core/authentication/qrcode-login.vue'),
         meta: {
-          title: $t('page.core.qrcodeLogin'),
+          title: $t('page.auth.qrcodeLogin'),
         },
       },
       {
@@ -68,7 +69,7 @@ const coreRoutes: RouteRecordRaw[] = [
         component: () =>
           import('#/views/_core/authentication/forget-password.vue'),
         meta: {
-          title: $t('page.core.forgetPassword'),
+          title: $t('page.auth.forgetPassword'),
         },
       },
       {
@@ -76,7 +77,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'register',
         component: () => import('#/views/_core/authentication/register.vue'),
         meta: {
-          title: $t('page.core.register'),
+          title: $t('page.auth.register'),
         },
       },
     ],

+ 2 - 2
apps/web-ele/src/router/routes/modules/demos.ts

@@ -10,14 +10,14 @@ const routes: RouteRecordRaw[] = [
       icon: 'ic:baseline-view-in-ar',
       keepAlive: true,
       order: 1000,
-      title: $t('page.demos.title'),
+      title: $t('demos.title'),
     },
     name: 'Demos',
     path: '/demos',
     children: [
       {
         meta: {
-          title: $t('page.demos.element-plus'),
+          title: $t('demos.elementPlus'),
         },
         name: 'NaiveDemos',
         path: '/demos/element',

+ 5 - 5
apps/web-ele/src/router/routes/modules/vben.ts

@@ -19,7 +19,7 @@ const routes: RouteRecordRaw[] = [
       badgeType: 'dot',
       icon: VBEN_LOGO_URL,
       order: 9999,
-      title: $t('page.vben.title'),
+      title: $t('demos.vben.title'),
     },
     name: 'VbenProject',
     path: '/vben-admin',
@@ -30,7 +30,7 @@ const routes: RouteRecordRaw[] = [
         component: () => import('#/views/_core/about/index.vue'),
         meta: {
           icon: 'lucide:copyright',
-          title: $t('page.vben.about'),
+          title: $t('demos.vben.about'),
         },
       },
       {
@@ -40,7 +40,7 @@ const routes: RouteRecordRaw[] = [
         meta: {
           icon: 'lucide:book-open-text',
           link: VBEN_DOC_URL,
-          title: $t('page.vben.document'),
+          title: $t('demos.vben.document'),
         },
       },
       {
@@ -61,7 +61,7 @@ const routes: RouteRecordRaw[] = [
           badgeType: 'dot',
           icon: 'logos:naiveui',
           link: VBEN_NAIVE_PREVIEW_URL,
-          title: $t('page.vben.naive-ui'),
+          title: $t('demos.vben.naive-ui'),
         },
       },
       {
@@ -72,7 +72,7 @@ const routes: RouteRecordRaw[] = [
           badgeType: 'dot',
           icon: SvgAntdvLogoIcon,
           link: VBEN_ANT_PREVIEW_URL,
-          title: $t('page.vben.antdv'),
+          title: $t('demos.vben.antdv'),
         },
       },
     ],

+ 3 - 4
apps/web-ele/src/store/auth.ts

@@ -1,5 +1,4 @@
-import type { LoginAndRegisterParams } from '@vben/common-ui';
-import type { UserInfo } from '@vben/types';
+import type { Recordable, UserInfo } from '@vben/types';
 
 import { ref } from 'vue';
 import { useRouter } from 'vue-router';
@@ -26,7 +25,7 @@ export const useAuthStore = defineStore('auth', () => {
    * @param params 登录表单数据
    */
   async function authLogin(
-    params: LoginAndRegisterParams,
+    params: Recordable<any>,
     onSuccess?: () => Promise<void> | void,
   ) {
     // 异步处理用户登录操作并获取 accessToken
@@ -85,7 +84,7 @@ export const useAuthStore = defineStore('auth', () => {
     resetAllStores();
     accessStore.setLoginExpired(false);
 
-    // 回登页带上当前路由地址
+    // 回登页带上当前路由地址
     await router.replace({
       path: LOGIN_PATH,
       query: redirect

+ 3 - 2
apps/web-ele/src/views/_core/authentication/code-login.vue

@@ -1,5 +1,6 @@
 <script lang="ts" setup>
-import type { LoginCodeParams, VbenFormSchema } from '@vben/common-ui';
+import type { VbenFormSchema } from '@vben/common-ui';
+import type { Recordable } from '@vben/types';
 
 import { computed, ref } from 'vue';
 
@@ -49,7 +50,7 @@ const formSchema = computed((): VbenFormSchema[] => {
  * Asynchronously handle the login process
  * @param values 登录表单数据
  */
-async function handleLogin(values: LoginCodeParams) {
+async function handleLogin(values: Recordable<any>) {
   // eslint-disable-next-line no-console
   console.log(values);
 }

+ 2 - 1
apps/web-ele/src/views/_core/authentication/forget-password.vue

@@ -1,5 +1,6 @@
 <script lang="ts" setup>
 import type { VbenFormSchema } from '@vben/common-ui';
+import type { Recordable } from '@vben/types';
 
 import { computed, ref } from 'vue';
 
@@ -27,7 +28,7 @@ const formSchema = computed((): VbenFormSchema[] => {
   ];
 });
 
-function handleSubmit(value: string) {
+function handleSubmit(value: Recordable<any>) {
   // eslint-disable-next-line no-console
   console.log('reset email:', value);
 }

+ 6 - 11
apps/web-ele/src/views/_core/authentication/register.vue

@@ -1,5 +1,6 @@
 <script lang="ts" setup>
-import type { LoginAndRegisterParams, VbenFormSchema } from '@vben/common-ui';
+import type { VbenFormSchema } from '@vben/common-ui';
+import type { Recordable } from '@vben/types';
 
 import { computed, h, ref } from 'vue';
 
@@ -45,7 +46,7 @@ const formSchema = computed((): VbenFormSchema[] => {
         rules(values) {
           const { password } = values;
           return z
-            .string()
+            .string({ required_error: $t('authentication.passwordTip') })
             .min(1, { message: $t('authentication.passwordTip') })
             .refine((value) => value === password, {
               message: $t('authentication.confirmPasswordTip'),
@@ -55,7 +56,6 @@ const formSchema = computed((): VbenFormSchema[] => {
       },
       fieldName: 'confirmPassword',
       label: $t('authentication.confirmPassword'),
-      rules: z.string().min(1, { message: $t('authentication.passwordTip') }),
     },
     {
       component: 'VbenCheckbox',
@@ -67,15 +67,10 @@ const formSchema = computed((): VbenFormSchema[] => {
             h(
               'a',
               {
-                class:
-                  'cursor-pointer text-primary ml-1 hover:text-primary-hover',
+                class: 'vben-link ml-1 ',
                 href: '',
               },
-              [
-                $t('authentication.privacyPolicy'),
-                '&',
-                $t('authentication.terms'),
-              ],
+              `${$t('authentication.privacyPolicy')} & ${$t('authentication.terms')}`,
             ),
           ]),
       }),
@@ -86,7 +81,7 @@ const formSchema = computed((): VbenFormSchema[] => {
   ];
 });
 
-function handleSubmit(value: LoginAndRegisterParams) {
+function handleSubmit(value: Recordable<any>) {
   // eslint-disable-next-line no-console
   console.log('register submit:', value);
 }

+ 1 - 0
apps/web-ele/src/views/demos/element/index.vue

@@ -57,6 +57,7 @@ const tableData = [
     <ElCard class="mb-5">
       <template #header> 按钮 </template>
       <ElSpace>
+        <ElButton text>Text</ElButton>
         <ElButton>Default</ElButton>
         <ElButton type="primary"> Primary </ElButton>
         <ElButton type="info"> Info </ElButton>

+ 1 - 1
apps/web-naive/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vben/web-naive",
-  "version": "5.3.2",
+  "version": "5.4.2",
   "homepage": "https://vben.pro",
   "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
   "repository": {

+ 103 - 0
apps/web-naive/src/adapter/component/index.ts

@@ -0,0 +1,103 @@
+/**
+ * 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用
+ * 可用于 vben-form、vben-modal、vben-drawer 等组件使用,
+ */
+
+import type { BaseFormComponentType } from '@vben/common-ui';
+
+import type { Component, SetupContext } from 'vue';
+import { h } from 'vue';
+
+import { globalShareState } from '@vben/common-ui';
+import { $t } from '@vben/locales';
+
+import {
+  NButton,
+  NCheckbox,
+  NCheckboxGroup,
+  NDatePicker,
+  NDivider,
+  NInput,
+  NInputNumber,
+  NRadioGroup,
+  NSelect,
+  NSpace,
+  NSwitch,
+  NTimePicker,
+  NTreeSelect,
+  NUpload,
+} from 'naive-ui';
+
+import { message } from '#/adapter/naive';
+
+const withDefaultPlaceholder = <T extends Component>(
+  component: T,
+  type: 'input' | 'select',
+) => {
+  return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
+    const placeholder = props?.placeholder || $t(`ui.placeholder.${type}`);
+    return h(component, { ...props, ...attrs, placeholder }, slots);
+  };
+};
+
+// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
+export type ComponentType =
+  | 'Checkbox'
+  | 'CheckboxGroup'
+  | 'DatePicker'
+  | 'Divider'
+  | 'Input'
+  | 'InputNumber'
+  | 'RadioGroup'
+  | 'Select'
+  | 'Space'
+  | 'Switch'
+  | 'TimePicker'
+  | 'TreeSelect'
+  | 'Upload'
+  | BaseFormComponentType;
+
+async function initComponentAdapter() {
+  const components: Partial<Record<ComponentType, Component>> = {
+    // 如果你的组件体积比较大,可以使用异步加载
+    // Button: () =>
+    // import('xxx').then((res) => res.Button),
+
+    Checkbox: NCheckbox,
+    CheckboxGroup: NCheckboxGroup,
+    DatePicker: NDatePicker,
+    // 自定义默认按钮
+    DefaultButton: (props, { attrs, slots }) => {
+      return h(NButton, { ...props, attrs, type: 'default' }, slots);
+    },
+    // 自定义主要按钮
+    PrimaryButton: (props, { attrs, slots }) => {
+      return h(NButton, { ...props, attrs, type: 'primary' }, slots);
+    },
+    Divider: NDivider,
+    Input: withDefaultPlaceholder(NInput, 'input'),
+    InputNumber: withDefaultPlaceholder(NInputNumber, 'input'),
+    RadioGroup: NRadioGroup,
+    Select: withDefaultPlaceholder(NSelect, 'select'),
+    Space: NSpace,
+    Switch: NSwitch,
+    TimePicker: NTimePicker,
+    TreeSelect: withDefaultPlaceholder(NTreeSelect, 'select'),
+    Upload: NUpload,
+  };
+
+  // 将组件注册到全局共享状态中
+  globalShareState.setComponents(components);
+
+  // 定义全局共享状态中的消息提示
+  globalShareState.defineMessage({
+    // 复制成功消息提示
+    copyPreferencesSuccess: (title, content) => {
+      message.success(content || title, {
+        duration: 0,
+      });
+    },
+  });
+}
+
+export { initComponentAdapter };

+ 6 - 76
apps/web-naive/src/adapter/form.ts

@@ -1,84 +1,14 @@
 import type {
-  BaseFormComponentType,
   VbenFormSchema as FormSchema,
   VbenFormProps,
 } from '@vben/common-ui';
 
-import type { Component, SetupContext } from 'vue';
-import { h } from 'vue';
+import type { ComponentType } from './component';
 
 import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
 import { $t } from '@vben/locales';
 
-import {
-  NButton,
-  NCheckbox,
-  NCheckboxGroup,
-  NDatePicker,
-  NDivider,
-  NInput,
-  NInputNumber,
-  NRadioGroup,
-  NSelect,
-  NSpace,
-  NSwitch,
-  NTimePicker,
-  NTreeSelect,
-  NUpload,
-} from 'naive-ui';
-// 业务表单组件适配
-
-export type FormComponentType =
-  | 'Checkbox'
-  | 'CheckboxGroup'
-  | 'DatePicker'
-  | 'Divider'
-  | 'Input'
-  | 'InputNumber'
-  | 'RadioGroup'
-  | 'Select'
-  | 'Space'
-  | 'Switch'
-  | 'TimePicker'
-  | 'TreeSelect'
-  | 'Upload'
-  | BaseFormComponentType;
-
-const withDefaultPlaceholder = <T extends Component>(
-  component: T,
-  type: 'input' | 'select',
-) => {
-  return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
-    const placeholder = props?.placeholder || $t(`placeholder.${type}`);
-    return h(component, { ...props, ...attrs, placeholder }, slots);
-  };
-};
-
-// 初始化表单组件,并注册到form组件内部
-setupVbenForm<FormComponentType>({
-  components: {
-    Checkbox: NCheckbox,
-    CheckboxGroup: NCheckboxGroup,
-    DatePicker: NDatePicker,
-    // 自定义默认的重置按钮
-    DefaultResetActionButton: (props, { attrs, slots }) => {
-      return h(NButton, { ...props, attrs, type: 'info' }, slots);
-    },
-    // 自定义默认的提交按钮
-    DefaultSubmitActionButton: (props, { attrs, slots }) => {
-      return h(NButton, { ...props, attrs, type: 'primary' }, slots);
-    },
-    Divider: NDivider,
-    Input: withDefaultPlaceholder(NInput, 'input'),
-    InputNumber: withDefaultPlaceholder(NInputNumber, 'input'),
-    RadioGroup: NRadioGroup,
-    Select: withDefaultPlaceholder(NSelect, 'select'),
-    Space: NSpace,
-    Switch: NSwitch,
-    TimePicker: NTimePicker,
-    TreeSelect: withDefaultPlaceholder(NTreeSelect, 'select'),
-    Upload: NUpload,
-  },
+setupVbenForm<ComponentType>({
   config: {
     // naive-ui组件不接受onChang事件,所以需要禁用
     disabledOnChangeListener: true,
@@ -94,22 +24,22 @@ setupVbenForm<FormComponentType>({
   defineRules: {
     required: (value, _params, ctx) => {
       if (value === undefined || value === null || value.length === 0) {
-        return $t('formRules.required', [ctx.label]);
+        return $t('ui.formRules.required', [ctx.label]);
       }
       return true;
     },
     selectRequired: (value, _params, ctx) => {
       if (value === undefined || value === null) {
-        return $t('formRules.selectRequired', [ctx.label]);
+        return $t('ui.formRules.selectRequired', [ctx.label]);
       }
       return true;
     },
   },
 });
 
-const useVbenForm = useForm<FormComponentType>;
+const useVbenForm = useForm<ComponentType>;
 
 export { useVbenForm, z };
 
-export type VbenFormSchema = FormSchema<FormComponentType>;
+export type VbenFormSchema = FormSchema<ComponentType>;
 export type { VbenFormProps };

+ 9 - 5
apps/web-naive/src/api/request.ts

@@ -12,7 +12,7 @@ import {
 } from '@vben/request';
 import { useAccessStore } from '@vben/stores';
 
-import { message } from '#/adapter';
+import { message } from '#/adapter/naive';
 import { useAuthStore } from '#/store';
 
 import { refreshTokenApi } from './core';
@@ -73,11 +73,11 @@ function createRequestClient(baseURL: string) {
     fulfilled: (response) => {
       const { data: responseData, status } = response;
 
-      const { code, data, message: msg } = responseData;
+      const { code, data } = responseData;
       if (status >= 200 && status < 400 && code === 0) {
         return data;
       }
-      throw new Error(`Error ${status}: ${msg}`);
+      throw Object.assign({}, response, { response });
     },
   });
 
@@ -94,9 +94,13 @@ function createRequestClient(baseURL: string) {
 
   // 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
   client.addResponseInterceptor(
-    errorMessageResponseInterceptor((msg: string, _error) => {
+    errorMessageResponseInterceptor((msg: string, error) => {
       // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
-      message.error(msg);
+      // 当前mock接口返回的错误字段是 error 或者 message
+      const responseData = error?.response?.data ?? {};
+      const errorMessage = responseData?.error ?? responseData?.message ?? '';
+      // 如果没有错误信息,则会根据状态码进行提示
+      message.error(errorMessage || msg);
     }),
   );
 

+ 3 - 0
apps/web-naive/src/bootstrap.ts

@@ -6,10 +6,13 @@ import '@vben/styles';
 
 import { setupI18n } from '#/locales';
 
+import { initComponentAdapter } from './adapter/component';
 import App from './app.vue';
 import { router } from './router';
 
 async function bootstrap(namespace: string) {
+  // 初始化组件适配器
+  initComponentAdapter();
   const app = createApp(App);
 
   // 国际化 i18n 配置

+ 2 - 2
apps/web-naive/src/layouts/basic.vue

@@ -68,7 +68,7 @@ const menus = computed(() => [
       });
     },
     icon: BookOpenText,
-    text: $t('widgets.document'),
+    text: $t('ui.widgets.document'),
   },
   {
     handler: () => {
@@ -86,7 +86,7 @@ const menus = computed(() => [
       });
     },
     icon: CircleHelp,
-    text: $t('widgets.qa'),
+    text: $t('ui.widgets.qa'),
   },
 ]);
 

+ 10 - 3
apps/web-naive/src/locales/index.ts

@@ -2,12 +2,19 @@ import type { LocaleSetupOptions, SupportedLanguagesType } from '@vben/locales';
 
 import type { App } from 'vue';
 
-import { $t, setupI18n as coreSetup, loadLocalesMap } from '@vben/locales';
+import {
+  $t,
+  setupI18n as coreSetup,
+  loadLocalesMapFromDir,
+} from '@vben/locales';
 import { preferences } from '@vben/preferences';
 
-const modules = import.meta.glob('./langs/*.json');
+const modules = import.meta.glob('./langs/**/*.json');
 
-const localesMap = loadLocalesMap(modules);
+const localesMap = loadLocalesMapFromDir(
+  /\.\/langs\/([^/]+)\/(.*)\.json$/,
+  modules,
+);
 
 /**
  * 加载应用特有的语言包

+ 13 - 0
apps/web-naive/src/locales/langs/en-US/demos.json

@@ -0,0 +1,13 @@
+{
+  "title": "Demos",
+  "naive": "Naive UI",
+  "table": "Table",
+  "vben": {
+    "title": "Project",
+    "about": "About",
+    "document": "Document",
+    "antdv": "Ant Design Vue Version",
+    "naive-ui": "Naive UI Version",
+    "element-plus": "Element Plus Version"
+  }
+}

+ 14 - 0
apps/web-naive/src/locales/langs/en-US/page.json

@@ -0,0 +1,14 @@
+{
+  "auth": {
+    "login": "Login",
+    "register": "Register",
+    "codeLogin": "Code Login",
+    "qrcodeLogin": "Qr Code Login",
+    "forgetPassword": "Forget Password"
+  },
+  "dashboard": {
+    "title": "Dashboard",
+    "analytics": "Analytics",
+    "workspace": "Workspace"
+  }
+}

+ 13 - 0
apps/web-naive/src/locales/langs/zh-CN/demos.json

@@ -0,0 +1,13 @@
+{
+  "title": "演示",
+  "naive": "Naive UI",
+  "table": "Table",
+  "vben": {
+    "title": "项目",
+    "about": "关于",
+    "document": "文档",
+    "antdv": "Ant Design Vue 版本",
+    "naive-ui": "Naive UI 版本",
+    "element-plus": "Element Plus 版本"
+  }
+}

+ 14 - 0
apps/web-naive/src/locales/langs/zh-CN/page.json

@@ -0,0 +1,14 @@
+{
+  "auth": {
+    "login": "登录",
+    "register": "注册",
+    "codeLogin": "验证码登录",
+    "qrcodeLogin": "二维码登录",
+    "forgetPassword": "忘记密码"
+  },
+  "dashboard": {
+    "title": "概览",
+    "analytics": "分析页",
+    "workspace": "工作台"
+  }
+}

+ 1 - 1
apps/web-naive/src/router/access.ts

@@ -6,7 +6,7 @@ import type {
 import { generateAccessible } from '@vben/access';
 import { preferences } from '@vben/preferences';
 
-import { message } from '#/adapter';
+import { message } from '#/adapter/naive';
 import { getAllMenusApi } from '#/api';
 import { BasicLayout, IFrameView } from '#/layouts';
 import { $t } from '#/locales';

+ 6 - 5
apps/web-naive/src/router/routes/core.ts

@@ -32,6 +32,7 @@ const coreRoutes: RouteRecordRaw[] = [
   {
     component: AuthPageLayout,
     meta: {
+      hideInTab: true,
       title: 'Authentication',
     },
     name: 'Authentication',
@@ -42,7 +43,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'login',
         component: Login,
         meta: {
-          title: $t('page.core.login'),
+          title: $t('page.auth.login'),
         },
       },
       {
@@ -50,7 +51,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'code-login',
         component: () => import('#/views/_core/authentication/code-login.vue'),
         meta: {
-          title: $t('page.core.codeLogin'),
+          title: $t('page.auth.codeLogin'),
         },
       },
       {
@@ -59,7 +60,7 @@ const coreRoutes: RouteRecordRaw[] = [
         component: () =>
           import('#/views/_core/authentication/qrcode-login.vue'),
         meta: {
-          title: $t('page.core.qrcodeLogin'),
+          title: $t('page.auth.qrcodeLogin'),
         },
       },
       {
@@ -68,7 +69,7 @@ const coreRoutes: RouteRecordRaw[] = [
         component: () =>
           import('#/views/_core/authentication/forget-password.vue'),
         meta: {
-          title: $t('page.core.forgetPassword'),
+          title: $t('page.auth.forgetPassword'),
         },
       },
       {
@@ -76,7 +77,7 @@ const coreRoutes: RouteRecordRaw[] = [
         path: 'register',
         component: () => import('#/views/_core/authentication/register.vue'),
         meta: {
-          title: $t('page.core.register'),
+          title: $t('page.auth.register'),
         },
       },
     ],

+ 3 - 3
apps/web-naive/src/router/routes/modules/demos.ts

@@ -10,14 +10,14 @@ const routes: RouteRecordRaw[] = [
       icon: 'ic:baseline-view-in-ar',
       keepAlive: true,
       order: 1000,
-      title: $t('page.demos.title'),
+      title: $t('demos.title'),
     },
     name: 'Demos',
     path: '/demos',
     children: [
       {
         meta: {
-          title: $t('page.demos.naive'),
+          title: $t('demos.naive'),
         },
         name: 'NaiveDemos',
         path: '/demos/naive',
@@ -25,7 +25,7 @@ const routes: RouteRecordRaw[] = [
       },
       {
         meta: {
-          title: $t('page.demos.table'),
+          title: $t('demos.table'),
         },
         name: 'Table',
         path: '/demos/table',

+ 5 - 5
apps/web-naive/src/router/routes/modules/vben.ts

@@ -19,7 +19,7 @@ const routes: RouteRecordRaw[] = [
       badgeType: 'dot',
       icon: VBEN_LOGO_URL,
       order: 9999,
-      title: $t('page.vben.title'),
+      title: $t('demos.vben.title'),
     },
     name: 'VbenProject',
     path: '/vben-admin',
@@ -30,7 +30,7 @@ const routes: RouteRecordRaw[] = [
         component: () => import('#/views/_core/about/index.vue'),
         meta: {
           icon: 'lucide:copyright',
-          title: $t('page.vben.about'),
+          title: $t('demos.vben.about'),
         },
       },
       {
@@ -40,7 +40,7 @@ const routes: RouteRecordRaw[] = [
         meta: {
           icon: 'lucide:book-open-text',
           link: VBEN_DOC_URL,
-          title: $t('page.vben.document'),
+          title: $t('demos.vben.document'),
         },
       },
       {
@@ -61,7 +61,7 @@ const routes: RouteRecordRaw[] = [
           badgeType: 'dot',
           icon: SvgAntdvLogoIcon,
           link: VBEN_ANT_PREVIEW_URL,
-          title: $t('page.vben.antdv'),
+          title: $t('demos.vben.antdv'),
         },
       },
       {
@@ -72,7 +72,7 @@ const routes: RouteRecordRaw[] = [
           badgeType: 'dot',
           icon: 'logos:element',
           link: VBEN_ELE_PREVIEW_URL,
-          title: $t('page.vben.element-plus'),
+          title: $t('demos.vben.element-plus'),
         },
       },
     ],

+ 4 - 5
apps/web-naive/src/store/auth.ts

@@ -1,5 +1,4 @@
-import type { LoginAndRegisterParams } from '@vben/common-ui';
-import type { UserInfo } from '@vben/types';
+import type { Recordable, UserInfo } from '@vben/types';
 
 import { ref } from 'vue';
 import { useRouter } from 'vue-router';
@@ -9,7 +8,7 @@ import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores';
 
 import { defineStore } from 'pinia';
 
-import { notification } from '#/adapter';
+import { notification } from '#/adapter/naive';
 import { getAccessCodesApi, getUserInfoApi, loginApi, logoutApi } from '#/api';
 import { $t } from '#/locales';
 
@@ -26,7 +25,7 @@ export const useAuthStore = defineStore('auth', () => {
    * @param params 登录表单数据
    */
   async function authLogin(
-    params: LoginAndRegisterParams,
+    params: Recordable<any>,
     onSuccess?: () => Promise<void> | void,
   ) {
     // 异步处理用户登录操作并获取 accessToken
@@ -85,7 +84,7 @@ export const useAuthStore = defineStore('auth', () => {
     resetAllStores();
     accessStore.setLoginExpired(false);
 
-    // 回登页带上当前路由地址
+    // 回登页带上当前路由地址
     await router.replace({
       path: LOGIN_PATH,
       query: redirect

+ 3 - 2
apps/web-naive/src/views/_core/authentication/code-login.vue

@@ -1,5 +1,6 @@
 <script lang="ts" setup>
-import type { LoginCodeParams, VbenFormSchema } from '@vben/common-ui';
+import type { VbenFormSchema } from '@vben/common-ui';
+import type { Recordable } from '@vben/types';
 
 import { computed, ref } from 'vue';
 
@@ -49,7 +50,7 @@ const formSchema = computed((): VbenFormSchema[] => {
  * Asynchronously handle the login process
  * @param values 登录表单数据
  */
-async function handleLogin(values: LoginCodeParams) {
+async function handleLogin(values: Recordable<any>) {
   // eslint-disable-next-line no-console
   console.log(values);
 }

+ 2 - 1
apps/web-naive/src/views/_core/authentication/forget-password.vue

@@ -1,5 +1,6 @@
 <script lang="ts" setup>
 import type { VbenFormSchema } from '@vben/common-ui';
+import type { Recordable } from '@vben/types';
 
 import { computed, ref } from 'vue';
 
@@ -27,7 +28,7 @@ const formSchema = computed((): VbenFormSchema[] => {
   ];
 });
 
-function handleSubmit(value: string) {
+function handleSubmit(value: Recordable<any>) {
   // eslint-disable-next-line no-console
   console.log('reset email:', value);
 }

+ 6 - 11
apps/web-naive/src/views/_core/authentication/register.vue

@@ -1,5 +1,6 @@
 <script lang="ts" setup>
-import type { LoginAndRegisterParams, VbenFormSchema } from '@vben/common-ui';
+import type { VbenFormSchema } from '@vben/common-ui';
+import type { Recordable } from '@vben/types';
 
 import { computed, h, ref } from 'vue';
 
@@ -45,7 +46,7 @@ const formSchema = computed((): VbenFormSchema[] => {
         rules(values) {
           const { password } = values;
           return z
-            .string()
+            .string({ required_error: $t('authentication.passwordTip') })
             .min(1, { message: $t('authentication.passwordTip') })
             .refine((value) => value === password, {
               message: $t('authentication.confirmPasswordTip'),
@@ -55,7 +56,6 @@ const formSchema = computed((): VbenFormSchema[] => {
       },
       fieldName: 'confirmPassword',
       label: $t('authentication.confirmPassword'),
-      rules: z.string().min(1, { message: $t('authentication.passwordTip') }),
     },
     {
       component: 'VbenCheckbox',
@@ -67,15 +67,10 @@ const formSchema = computed((): VbenFormSchema[] => {
             h(
               'a',
               {
-                class:
-                  'cursor-pointer text-primary ml-1 hover:text-primary-hover',
+                class: 'vben-link ml-1',
                 href: '',
               },
-              [
-                $t('authentication.privacyPolicy'),
-                '&',
-                $t('authentication.terms'),
-              ],
+              `${$t('authentication.privacyPolicy')} & ${$t('authentication.terms')}`,
             ),
           ]),
       }),
@@ -86,7 +81,7 @@ const formSchema = computed((): VbenFormSchema[] => {
   ];
 });
 
-function handleSubmit(value: LoginAndRegisterParams) {
+function handleSubmit(value: Recordable<any>) {
   // eslint-disable-next-line no-console
   console.log('register submit:', value);
 }

+ 4 - 0
docs/.vitepress/config/zh.mts

@@ -164,6 +164,10 @@ function sidebarComponents(): DefaultTheme.SidebarItem[] {
           link: 'common-ui/vben-form',
           text: 'Form 表单',
         },
+        {
+          link: 'common-ui/vben-vxe-table',
+          text: 'Vxe Table 表格',
+        },
         {
           link: 'common-ui/vben-count-to-animator',
           text: 'CountToAnimator 数字动画',

+ 2 - 1
docs/.vitepress/theme/styles/index.ts

@@ -1,3 +1,4 @@
+import '@vben/styles';
+
 import './variables.css';
 import './base.css';
-import '@vben/styles';

+ 1 - 1
docs/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vben/docs",
-  "version": "5.3.2",
+  "version": "5.4.2",
   "private": true,
   "scripts": {
     "build": "vitepress build",

+ 127 - 0
docs/src/_env/adapter/component.ts

@@ -0,0 +1,127 @@
+/**
+ * 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用
+ * 可用于 vben-form、vben-modal、vben-drawer 等组件使用,
+ */
+
+import type { BaseFormComponentType } from '@vben/common-ui';
+
+import type { Component, SetupContext } from 'vue';
+import { h } from 'vue';
+
+import { globalShareState } from '@vben/common-ui';
+import { $t } from '@vben/locales';
+
+import {
+  AutoComplete,
+  Button,
+  Checkbox,
+  CheckboxGroup,
+  DatePicker,
+  Divider,
+  Input,
+  InputNumber,
+  InputPassword,
+  Mentions,
+  notification,
+  Radio,
+  RadioGroup,
+  RangePicker,
+  Rate,
+  Select,
+  Space,
+  Switch,
+  Textarea,
+  TimePicker,
+  TreeSelect,
+  Upload,
+} from 'ant-design-vue';
+
+const withDefaultPlaceholder = <T extends Component>(
+  component: T,
+  type: 'input' | 'select',
+) => {
+  return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
+    const placeholder = props?.placeholder || $t(`ui.placeholder.${type}`);
+    return h(component, { ...props, ...attrs, placeholder }, slots);
+  };
+};
+
+// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
+export type ComponentType =
+  | 'AutoComplete'
+  | 'Checkbox'
+  | 'CheckboxGroup'
+  | 'DatePicker'
+  | 'DefaultButton'
+  | 'Divider'
+  | 'Input'
+  | 'InputNumber'
+  | 'InputPassword'
+  | 'Mentions'
+  | 'PrimaryButton'
+  | 'Radio'
+  | 'RadioGroup'
+  | 'RangePicker'
+  | 'Rate'
+  | 'Select'
+  | 'Space'
+  | 'Switch'
+  | 'Textarea'
+  | 'TimePicker'
+  | 'TreeSelect'
+  | 'Upload'
+  | BaseFormComponentType;
+
+async function initComponentAdapter() {
+  const components: Partial<Record<ComponentType, Component>> = {
+    // 如果你的组件体积比较大,可以使用异步加载
+    // Button: () =>
+    // import('xxx').then((res) => res.Button),
+
+    AutoComplete,
+    Checkbox,
+    CheckboxGroup,
+    DatePicker,
+    // 自定义默认按钮
+    DefaultButton: (props, { attrs, slots }) => {
+      return h(Button, { ...props, attrs, type: 'default' }, slots);
+    },
+    Divider,
+    Input: withDefaultPlaceholder(Input, 'input'),
+    InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
+    InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
+    Mentions: withDefaultPlaceholder(Mentions, 'input'),
+    // 自定义主要按钮
+    PrimaryButton: (props, { attrs, slots }) => {
+      return h(Button, { ...props, attrs, type: 'primary' }, slots);
+    },
+    Radio,
+    RadioGroup,
+    RangePicker,
+    Rate,
+    Select: withDefaultPlaceholder(Select, 'select'),
+    Space,
+    Switch,
+    Textarea: withDefaultPlaceholder(Textarea, 'input'),
+    TimePicker,
+    TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'),
+    Upload,
+  };
+
+  // 将组件注册到全局共享状态中
+  globalShareState.setComponents(components);
+
+  // 定义全局共享状态中的消息提示
+  globalShareState.defineMessage({
+    // 复制成功消息提示
+    copyPreferencesSuccess: (title, content) => {
+      notification.success({
+        description: content,
+        message: title,
+        placement: 'bottomRight',
+      });
+    },
+  });
+}
+
+export { initComponentAdapter };

+ 12 - 90
docs/src/_env/adapter/form.ts

@@ -1,99 +1,23 @@
 import type {
-  BaseFormComponentType,
   VbenFormSchema as FormSchema,
   VbenFormProps,
 } from '@vben/common-ui';
 
-import { h } from 'vue';
+import type { ComponentType } from './component';
 
 import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
 import { $t } from '@vben/locales';
 
-import {
-  AutoComplete,
-  Button,
-  Checkbox,
-  CheckboxGroup,
-  DatePicker,
-  Divider,
-  Input,
-  InputNumber,
-  InputPassword,
-  Mentions,
-  Radio,
-  RadioGroup,
-  RangePicker,
-  Rate,
-  Select,
-  Space,
-  Switch,
-  Textarea,
-  TimePicker,
-  TreeSelect,
-  Upload,
-} from 'ant-design-vue';
+import { initComponentAdapter } from './component';
 
-// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
-export type FormComponentType =
-  | 'AutoComplete'
-  | 'Checkbox'
-  | 'CheckboxGroup'
-  | 'DatePicker'
-  | 'Divider'
-  | 'Input'
-  | 'InputNumber'
-  | 'InputPassword'
-  | 'Mentions'
-  | 'Radio'
-  | 'RadioGroup'
-  | 'RangePicker'
-  | 'Rate'
-  | 'Select'
-  | 'Space'
-  | 'Switch'
-  | 'Textarea'
-  | 'TimePicker'
-  | 'TreeSelect'
-  | 'Upload'
-  | BaseFormComponentType;
-
-// 初始化表单组件,并注册到form组件内部
-setupVbenForm<FormComponentType>({
-  components: {
-    AutoComplete,
-    Checkbox,
-    CheckboxGroup,
-    DatePicker,
-    // 自定义默认的重置按钮
-    DefaultResetActionButton: (props, { attrs, slots }) => {
-      return h(Button, { ...props, attrs, type: 'default' }, slots);
-    },
-    // 自定义默认的提交按钮
-    DefaultSubmitActionButton: (props, { attrs, slots }) => {
-      return h(Button, { ...props, attrs, type: 'primary' }, slots);
-    },
-    Divider,
-    Input,
-    InputNumber,
-    InputPassword,
-    Mentions,
-    Radio,
-    RadioGroup,
-    RangePicker,
-    Rate,
-    Select,
-    Space,
-    Switch,
-    Textarea,
-    TimePicker,
-    TreeSelect,
-    Upload,
-  },
+initComponentAdapter();
+setupVbenForm<ComponentType>({
   config: {
-    // ant design vue组件库默认都是 v-model:value
     baseModelPropName: 'value',
-
-    // 一些组件是 v-model:checked 或者 v-model:fileList
+    // naive-ui组件不接受onChang事件,所以需要禁用
+    disabledOnChangeListener: true,
+    // naive-ui组件的空值为null,不能是undefined,否则重置表单时不生效
+    emptyStateValue: null,
     modelPropNameMap: {
       Checkbox: 'checked',
       Radio: 'checked',
@@ -102,26 +26,24 @@ setupVbenForm<FormComponentType>({
     },
   },
   defineRules: {
-    // 输入项目必填国际化适配
     required: (value, _params, ctx) => {
       if (value === undefined || value === null || value.length === 0) {
-        return $t('formRules.required', [ctx.label]);
+        return $t('ui.formRules.required', [ctx.label]);
       }
       return true;
     },
-    // 选择项目必填国际化适配
     selectRequired: (value, _params, ctx) => {
       if (value === undefined || value === null) {
-        return $t('formRules.selectRequired', [ctx.label]);
+        return $t('ui.formRules.selectRequired', [ctx.label]);
       }
       return true;
     },
   },
 });
 
-const useVbenForm = useForm<FormComponentType>;
+const useVbenForm = useForm<ComponentType>;
 
 export { useVbenForm, z };
 
-export type VbenFormSchema = FormSchema<FormComponentType>;
+export type VbenFormSchema = FormSchema<ComponentType>;
 export type { VbenFormProps };

+ 1 - 1
docs/src/commercial/community.md

@@ -3,7 +3,7 @@
 社区交流群主要是为了方便大家交流,提问,解答问题,分享经验等。偏自助方式,如果你有问题,可以通过以下方式加入社区交流群:
 
 - [QQ频道](https://pd.qq.com/s/16p8lvvob):推荐!!!主要提供问题解答,分享经验等。
-- QQ群:[1群](https://qm.qq.com/q/YacMHPYAMu)、[2群](https://qm.qq.com/q/ajVKZvFICk)、[3群](https://qm.qq.com/q/36zdwThP2E),[4群](https://qm.qq.com/q/sCzSlm3504),[老群](https://qm.qq.com/q/MEmHoCLbG0),主要使用者的交流群。
+- QQ群:[大群](https://qm.qq.com/q/MEmHoCLbG0),[1群](https://qm.qq.com/q/YacMHPYAMu)、[2群](https://qm.qq.com/q/ajVKZvFICk)、[3群](https://qm.qq.com/q/36zdwThP2E),[4群](https://qm.qq.com/q/sCzSlm3504),主要使用者的交流群。
 - [Discord](https://discord.com/invite/VU62jTecad): 主要提供问题解答,分享经验等。
 
 ::: tip

+ 7 - 1
docs/src/components/common-ui/vben-drawer.md

@@ -14,6 +14,12 @@ outline: deep
 
 :::
 
+::: tip README
+
+下方示例代码中的,存在一些国际化、主题色未适配问题,这些问题只在文档内会出现,实际使用并不会有这些问题,可忽略,不必纠结。
+
+:::
+
 ## 基础用法
 
 使用 `useVbenDrawer` 创建最基础的模态框。
@@ -47,7 +53,7 @@ Drawer 内的内容一般业务中,会比较复杂,所以我们可以将 dra
 ::: info 注意
 
 - `VbenDrawer` 组件对与参数的处理优先级是 `slot` > `props` > `state`(通过api更新的状态以及useVbenDrawer参数)。如果你已经传入了 `slot` 或者 `props`,那么 `setState` 将不会生效,这种情况下你可以通过 `slot` 或者 `props` 来更新状态。
-- 如果你使用到了 `connectedComponent` 参数,那么会存在 2 个`useVbenDrawer`, 此时,如果同时设置了相同的参数,那么以内部为准(也就是没有设置 connectedComponent 的代码)。比如 同时设置了 `onComfirm`,那么以内部的 `onComfirm` 为准。`onOpenChange`事件除外,内外都会触发。
+- 如果你使用到了 `connectedComponent` 参数,那么会存在 2 个`useVbenDrawer`, 此时,如果同时设置了相同的参数,那么以内部为准(也就是没有设置 connectedComponent 的代码)。比如 同时设置了 `onConfirm`,那么以内部的 `onConfirm` 为准。`onOpenChange`事件除外,内外都会触发。
 
 :::
 

+ 104 - 61
docs/src/components/common-ui/vben-form.md

@@ -20,21 +20,74 @@ outline: deep
 
 ### 适配器说明
 
-每个应用都有不同的 UI 框架,所以在应用的 `src/adapter/form` 内部,你可以根据自己的需求,进行组件适配。下面是 `Ant Design Vue` 的适配器示例代码,可根据注释查看说明:
+每个应用都有不同的 UI 框架,所以在应用的 `src/adapter/form` 和 `src/adapter/component` 内部,你可以根据自己的需求,进行组件适配。下面是 `Ant Design Vue` 的适配器示例代码,可根据注释查看说明:
 
-::: details ant design 适配器
+::: details ant design vue 表单适配器
 
 ```ts
 import type {
-  BaseFormComponentType,
   VbenFormSchema as FormSchema,
   VbenFormProps,
 } from '@vben/common-ui';
 
+import type { ComponentType } from './component';
+
+import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
+import { $t } from '@vben/locales';
+
+setupVbenForm<ComponentType>({
+  config: {
+    // ant design vue组件库默认都是 v-model:value
+    baseModelPropName: 'value',
+    // 一些组件是 v-model:checked 或者 v-model:fileList
+    modelPropNameMap: {
+      Checkbox: 'checked',
+      Radio: 'checked',
+      Switch: 'checked',
+      Upload: 'fileList',
+    },
+  },
+  defineRules: {
+    // 输入项目必填国际化适配
+    required: (value, _params, ctx) => {
+      if (value === undefined || value === null || value.length === 0) {
+        return $t('ui.formRules.required', [ctx.label]);
+      }
+      return true;
+    },
+    // 选择项目必填国际化适配
+    selectRequired: (value, _params, ctx) => {
+      if (value === undefined || value === null) {
+        return $t('ui.formRules.selectRequired', [ctx.label]);
+      }
+      return true;
+    },
+  },
+});
+
+const useVbenForm = useForm<ComponentType>;
+
+export { useVbenForm, z };
+export type VbenFormSchema = FormSchema<ComponentType>;
+export type { VbenFormProps };
+```
+
+:::
+
+::: details ant design vue 组件适配器
+
+```ts
+/**
+ * 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用
+ * 可用于 vben-form、vben-modal、vben-drawer 等组件使用,
+ */
+
+import type { BaseFormComponentType } from '@vben/common-ui';
+
 import type { Component, SetupContext } from 'vue';
 import { h } from 'vue';
 
-import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
+import { globalShareState } from '@vben/common-ui';
 import { $t } from '@vben/locales';
 
 import {
@@ -48,6 +101,7 @@ import {
   InputNumber,
   InputPassword,
   Mentions,
+  notification,
   Radio,
   RadioGroup,
   RangePicker,
@@ -61,17 +115,29 @@ import {
   Upload,
 } from 'ant-design-vue';
 
+const withDefaultPlaceholder = <T extends Component>(
+  component: T,
+  type: 'input' | 'select',
+) => {
+  return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
+    const placeholder = props?.placeholder || $t(`ui.placeholder.${type}`);
+    return h(component, { ...props, ...attrs, placeholder }, slots);
+  };
+};
+
 // 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
-export type FormComponentType =
+export type ComponentType =
   | 'AutoComplete'
   | 'Checkbox'
   | 'CheckboxGroup'
   | 'DatePicker'
+  | 'DefaultButton'
   | 'Divider'
   | 'Input'
   | 'InputNumber'
   | 'InputPassword'
   | 'Mentions'
+  | 'PrimaryButton'
   | 'Radio'
   | 'RadioGroup'
   | 'RangePicker'
@@ -85,36 +151,29 @@ export type FormComponentType =
   | 'Upload'
   | BaseFormComponentType;
 
-const withDefaultPlaceholder = <T extends Component>(
-  component: T,
-  type: 'input' | 'select',
-) => {
-  return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
-    const placeholder = props?.placeholder || $t(`placeholder.${type}`);
-    return h(component, { ...props, ...attrs, placeholder }, slots);
-  };
-};
+async function initComponentAdapter() {
+  const components: Partial<Record<ComponentType, Component>> = {
+    // 如果你的组件体积比较大,可以使用异步加载
+    // Button: () =>
+    // import('xxx').then((res) => res.Button),
 
-// 初始化表单组件,并注册到form组件内部
-setupVbenForm<FormComponentType>({
-  components: {
     AutoComplete,
     Checkbox,
     CheckboxGroup,
     DatePicker,
-    // 自定义默认的重置按钮
-    DefaultResetActionButton: (props, { attrs, slots }) => {
+    // 自定义默认按钮
+    DefaultButton: (props, { attrs, slots }) => {
       return h(Button, { ...props, attrs, type: 'default' }, slots);
     },
-    // 自定义默认的提交按钮
-    DefaultSubmitActionButton: (props, { attrs, slots }) => {
-      return h(Button, { ...props, attrs, type: 'primary' }, slots);
-    },
     Divider,
     Input: withDefaultPlaceholder(Input, 'input'),
     InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
     InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
     Mentions: withDefaultPlaceholder(Mentions, 'input'),
+    // 自定义主要按钮
+    PrimaryButton: (props, { attrs, slots }) => {
+      return h(Button, { ...props, attrs, type: 'primary' }, slots);
+    },
     Radio,
     RadioGroup,
     RangePicker,
@@ -126,44 +185,25 @@ setupVbenForm<FormComponentType>({
     TimePicker,
     TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'),
     Upload,
-  },
-  config: {
-    // 是否禁用onChange事件监听,naive ui组件库默认不需要监听onChange事件,否则会在控制台报错
-    disabledOnChangeListener: true,
-    // ant design vue组件库默认都是 v-model:value
-    baseModelPropName: 'value',
-    // 一些组件是 v-model:checked 或者 v-model:fileList
-    modelPropNameMap: {
-      Checkbox: 'checked',
-      Radio: 'checked',
-      Switch: 'checked',
-      Upload: 'fileList',
-    },
-  },
-  defineRules: {
-    // 输入项目必填国际化适配
-    required: (value, _params, ctx) => {
-      if (value === undefined || value === null || value.length === 0) {
-        return $t('formRules.required', [ctx.label]);
-      }
-      return true;
-    },
-    // 选择项目必填国际化适配
-    selectRequired: (value, _params, ctx) => {
-      if (value === undefined || value === null) {
-        return $t('formRules.selectRequired', [ctx.label]);
-      }
-      return true;
-    },
-  },
-});
-
-const useVbenForm = useForm<FormComponentType>;
+  };
 
-export { useVbenForm, z };
+  // 将组件注册到全局共享状态中
+  globalShareState.setComponents(components);
+
+  // 定义全局共享状态中的消息提示
+  globalShareState.defineMessage({
+    // 复制成功消息提示
+    copyPreferencesSuccess: (title, content) => {
+      notification.success({
+        description: content,
+        message: title,
+        placement: 'bottomRight',
+      });
+    },
+  });
+}
 
-export type VbenFormSchema = FormSchema<FormComponentType>;
-export type { VbenFormProps };
+export { initComponentAdapter };
 ```
 
 :::
@@ -218,7 +258,7 @@ _注意_ 需要指定 `dependencies` 的 `triggerFields` 属性,设置由谁
 
 ```vue
 <script setup lang="ts">
-import { useVbenForm } from '#/adapter';
+import { useVbenForm } from '#/adapter/form';
 
 // Form 为弹窗组件
 // formApi 为弹窗的方法
@@ -271,6 +311,7 @@ useVbenForm 返回的第二个参数,是一个对象,包含了一些表单
 | collapsedRows | 折叠时保持的行数 | `number` | `1` |
 | commonConfig | 表单项的通用配置,每个配置都会传递到每个表单项,表单项可覆盖 | `FormCommonConfig` | - |
 | schema | 表单项的每一项配置 | `FormSchema` | - |
+| submitOnEnter | 按下回车健时提交表单 | `boolean` | false |
 
 ### TS 类型说明
 
@@ -292,6 +333,8 @@ export interface ActionButtonOptions {
   show?: boolean;
   /** 按钮文本 */
   text?: string;
+  /** 任意属性 */
+  [key: string]: any;
 }
 ```
 
@@ -435,7 +478,7 @@ rules的值可以是一个字符串,也可以是一个zod的schema。
 rules也支持 zod 的 schema,可以进行更复杂的校验,zod 的使用请查看 [zod文档](https://zod.dev/)。
 
 ```ts
-import { z } from '#/adapter';
+import { z } from '#/adapter/form';
 
 // 基础类型
 {

+ 8 - 1
docs/src/components/common-ui/vben-modal.md

@@ -14,6 +14,12 @@ outline: deep
 
 :::
 
+::: tip README
+
+下方示例代码中的,存在一些国际化、主题色未适配问题,这些问题只在文档内会出现,实际使用并不会有这些问题,可忽略,不必纠结。
+
+:::
+
 ## 基础用法
 
 使用 `useVbenModal` 创建最基础的模态框。
@@ -53,7 +59,7 @@ Modal 内的内容一般业务中,会比较复杂,所以我们可以将 moda
 ::: info 注意
 
 - `VbenModal` 组件对与参数的处理优先级是 `slot` > `props` > `state`(通过api更新的状态以及useVbenModal参数)。如果你已经传入了 `slot` 或者 `props`,那么 `setState` 将不会生效,这种情况下你可以通过 `slot` 或者 `props` 来更新状态。
-- 如果你使用到了 `connectedComponent` 参数,那么会存在 2 个`useVbenModal`, 此时,如果同时设置了相同的参数,那么以内部为准(也就是没有设置 connectedComponent 的代码)。比如 同时设置了 `onComfirm`,那么以内部的 `onComfirm` 为准。`onOpenChange`事件除外,内外都会触发。
+- 如果你使用到了 `connectedComponent` 参数,那么会存在 2 个`useVbenModal`, 此时,如果同时设置了相同的参数,那么以内部为准(也就是没有设置 connectedComponent 的代码)。比如 同时设置了 `onConfirm`,那么以内部的 `onConfirm` 为准。`onOpenChange`事件除外,内外都会触发。
 
 :::
 
@@ -98,6 +104,7 @@ const [Modal, modalApi] = useVbenModal({
 | contentClass | modal内容区域的class | `string` | - |
 | footerClass | modal底部区域的class | `string` | - |
 | headerClass | modal顶部区域的class | `string` | - |
+| bordered | 是否显示border | `boolean` | `false` |
 
 ### Event
 

+ 7 - 0
docs/src/components/common-ui/vben-vxe-table.md

@@ -0,0 +1,7 @@
+---
+outline: deep
+---
+
+# Vben Vxe Table 表格
+
+文档待补充,如果需要使用,可先行查看 vxe-table 文档和 示例代码,内部有部分注释。

+ 79 - 79
docs/src/demos/vben-form/api/index.vue

@@ -1,7 +1,7 @@
 <script lang="ts" setup>
 import { Button, message, Space } from 'ant-design-vue';
 
-import { useVbenForm } from '#/adapter';
+import { useVbenForm } from '#/adapter/form';
 
 const [BaseForm, formApi] = useVbenForm({
   // 所有表单项共用,可单独在表单内覆盖
@@ -81,32 +81,52 @@ function handleClick(
     | 'updateSubmitButton',
 ) {
   switch (action) {
-    case 'updateSchema': {
-      formApi.updateSchema([
-        {
-          componentProps: {
-            options: [
-              {
-                label: '选项1',
-                value: '1',
-              },
-              {
-                label: '选项2',
-                value: '2',
-              },
-              {
-                label: '选项3',
-                value: '3',
-              },
-            ],
-          },
-          fieldName: 'fieldOptions',
-        },
-      ]);
-      message.success('字段 `fieldOptions` 下拉选项更新成功。');
+    case 'batchAddSchema': {
+      formApi.setState((prev) => {
+        const currentSchema = prev?.schema ?? [];
+        const newSchema = [];
+        for (let i = 0; i < 2; i++) {
+          newSchema.push({
+            component: 'Input',
+            componentProps: {
+              placeholder: '请输入',
+            },
+            fieldName: `field${i}${Date.now()}`,
+            label: `field+`,
+          });
+        }
+        return {
+          schema: [...currentSchema, ...newSchema],
+        };
+      });
       break;
     }
 
+    case 'batchDeleteSchema': {
+      formApi.setState((prev) => {
+        const currentSchema = prev?.schema ?? [];
+        return {
+          schema: currentSchema.slice(0, -2),
+        };
+      });
+      break;
+    }
+    case 'disabled': {
+      formApi.setState({ commonConfig: { disabled: true } });
+      break;
+    }
+    case 'hiddenAction': {
+      formApi.setState({ showDefaultActions: false });
+      break;
+    }
+    case 'hiddenResetButton': {
+      formApi.setState({ resetButtonOptions: { show: false } });
+      break;
+    }
+    case 'hiddenSubmitButton': {
+      formApi.setState({ submitButtonOptions: { show: false } });
+      break;
+    }
     case 'labelWidth': {
       formApi.setState({
         commonConfig: {
@@ -115,6 +135,10 @@ function handleClick(
       });
       break;
     }
+    case 'resetDisabled': {
+      formApi.setState({ commonConfig: { disabled: false } });
+      break;
+    }
     case 'resetLabelWidth': {
       formApi.setState({
         commonConfig: {
@@ -123,50 +147,18 @@ function handleClick(
       });
       break;
     }
-    case 'disabled': {
-      formApi.setState({ commonConfig: { disabled: true } });
-      break;
-    }
-    case 'resetDisabled': {
-      formApi.setState({ commonConfig: { disabled: false } });
-      break;
-    }
-    case 'hiddenAction': {
-      formApi.setState({ showDefaultActions: false });
-      break;
-    }
     case 'showAction': {
       formApi.setState({ showDefaultActions: true });
       break;
     }
-    case 'hiddenResetButton': {
-      formApi.setState({ resetButtonOptions: { show: false } });
-      break;
-    }
     case 'showResetButton': {
       formApi.setState({ resetButtonOptions: { show: true } });
       break;
     }
-    case 'hiddenSubmitButton': {
-      formApi.setState({ submitButtonOptions: { show: false } });
-      break;
-    }
     case 'showSubmitButton': {
       formApi.setState({ submitButtonOptions: { show: true } });
       break;
     }
-    case 'updateResetButton': {
-      formApi.setState({
-        resetButtonOptions: { disabled: true },
-      });
-      break;
-    }
-    case 'updateSubmitButton': {
-      formApi.setState({
-        submitButtonOptions: { loading: true },
-      });
-      break;
-    }
     case 'updateActionAlign': {
       formApi.setState({
         // 可以自行调整class
@@ -174,32 +166,40 @@ function handleClick(
       });
       break;
     }
-    case 'batchAddSchema': {
-      formApi.setState((prev) => {
-        const currentSchema = prev?.schema ?? [];
-        const newSchema = [];
-        for (let i = 0; i < 2; i++) {
-          newSchema.push({
-            component: 'Input',
-            componentProps: {
-              placeholder: '请输入',
-            },
-            fieldName: `field${i}${Date.now()}`,
-            label: `field+`,
-          });
-        }
-        return {
-          schema: [...currentSchema, ...newSchema],
-        };
+    case 'updateResetButton': {
+      formApi.setState({
+        resetButtonOptions: { disabled: true },
       });
       break;
     }
-    case 'batchDeleteSchema': {
-      formApi.setState((prev) => {
-        const currentSchema = prev?.schema ?? [];
-        return {
-          schema: currentSchema.slice(0, -2),
-        };
+    case 'updateSchema': {
+      formApi.updateSchema([
+        {
+          componentProps: {
+            options: [
+              {
+                label: '选项1',
+                value: '1',
+              },
+              {
+                label: '选项2',
+                value: '2',
+              },
+              {
+                label: '选项3',
+                value: '3',
+              },
+            ],
+          },
+          fieldName: 'fieldOptions',
+        },
+      ]);
+      message.success('字段 `fieldOptions` 下拉选项更新成功。');
+      break;
+    }
+    case 'updateSubmitButton': {
+      formApi.setState({
+        submitButtonOptions: { loading: true },
       });
       break;
     }

+ 1 - 1
docs/src/demos/vben-form/basic/index.vue

@@ -1,7 +1,7 @@
 <script lang="ts" setup>
 import { message } from 'ant-design-vue';
 
-import { useVbenForm } from '#/adapter';
+import { useVbenForm } from '#/adapter/form';
 
 const [BaseForm] = useVbenForm({
   // 所有表单项共用,可单独在表单内覆盖

+ 1 - 1
docs/src/demos/vben-form/custom/index.vue

@@ -3,7 +3,7 @@ import { h } from 'vue';
 
 import { Input, message } from 'ant-design-vue';
 
-import { useVbenForm } from '#/adapter';
+import { useVbenForm } from '#/adapter/form';
 
 const [Form] = useVbenForm({
   // 所有表单项共用,可单独在表单内覆盖

+ 1 - 1
docs/src/demos/vben-form/dynamic/index.vue

@@ -1,7 +1,7 @@
 <script lang="ts" setup>
 import { message } from 'ant-design-vue';
 
-import { useVbenForm } from '#/adapter';
+import { useVbenForm } from '#/adapter/form';
 
 const [Form] = useVbenForm({
   // 提交函数

+ 1 - 1
docs/src/demos/vben-form/query/index.vue

@@ -1,7 +1,7 @@
 <script lang="ts" setup>
 import { message } from 'ant-design-vue';
 
-import { useVbenForm } from '#/adapter';
+import { useVbenForm } from '#/adapter/form';
 
 const [QueryForm] = useVbenForm({
   // 默认展开

+ 1 - 1
docs/src/demos/vben-form/rules/index.vue

@@ -1,7 +1,7 @@
 <script lang="ts" setup>
 import { message } from 'ant-design-vue';
 
-import { useVbenForm, z } from '#/adapter';
+import { useVbenForm, z } from '#/adapter/form';
 
 const [Form] = useVbenForm({
   // 所有表单项共用,可单独在表单内覆盖

+ 9 - 9
docs/src/en/guide/essentials/route.md

@@ -106,7 +106,7 @@ const routes: RouteRecordRaw[] = [
       icon: 'ic:baseline-view-in-ar',
       keepAlive: true,
       order: 1000,
-      title: $t('page.demos.title'),
+      title: $t('demos.title'),
     },
     name: 'Demos',
     path: '/demos',
@@ -116,7 +116,7 @@ const routes: RouteRecordRaw[] = [
       {
         meta: {
           icon: 'ic:round-menu',
-          title: $t('page.demos.nested.title'),
+          title: $t('demos.nested.title'),
         },
         name: 'NestedDemos',
         path: '/demos/nested',
@@ -129,7 +129,7 @@ const routes: RouteRecordRaw[] = [
             meta: {
               icon: 'ic:round-menu',
               keepAlive: true,
-              title: $t('page.demos.nested.menu1'),
+              title: $t('demos.nested.menu1'),
             },
           },
           {
@@ -138,7 +138,7 @@ const routes: RouteRecordRaw[] = [
             meta: {
               icon: 'ic:round-menu',
               keepAlive: true,
-              title: $t('page.demos.nested.menu2'),
+              title: $t('demos.nested.menu2'),
             },
             redirect: '/demos/nested/menu2/menu2-1',
             children: [
@@ -149,7 +149,7 @@ const routes: RouteRecordRaw[] = [
                 meta: {
                   icon: 'ic:round-menu',
                   keepAlive: true,
-                  title: $t('page.demos.nested.menu2_1'),
+                  title: $t('demos.nested.menu2_1'),
                 },
               },
             ],
@@ -159,7 +159,7 @@ const routes: RouteRecordRaw[] = [
             path: '/demos/nested/menu3',
             meta: {
               icon: 'ic:round-menu',
-              title: $t('page.demos.nested.menu3'),
+              title: $t('demos.nested.menu3'),
             },
             redirect: '/demos/nested/menu3/menu3-1',
             children: [
@@ -170,7 +170,7 @@ const routes: RouteRecordRaw[] = [
                 meta: {
                   icon: 'ic:round-menu',
                   keepAlive: true,
-                  title: $t('page.demos.nested.menu3_1'),
+                  title: $t('demos.nested.menu3_1'),
                 },
               },
               {
@@ -178,7 +178,7 @@ const routes: RouteRecordRaw[] = [
                 path: 'menu3-2',
                 meta: {
                   icon: 'ic:round-menu',
-                  title: $t('page.demos.nested.menu3_2'),
+                  title: $t('demos.nested.menu3_2'),
                 },
                 redirect: '/demos/nested/menu3/menu3-2/menu3-2-1',
                 children: [
@@ -190,7 +190,7 @@ const routes: RouteRecordRaw[] = [
                     meta: {
                       icon: 'ic:round-menu',
                       keepAlive: true,
-                      title: $t('page.demos.nested.menu3_2_1'),
+                      title: $t('demos.nested.menu3_2_1'),
                     },
                   },
                 ],

+ 8 - 4
docs/src/en/guide/essentials/server.md

@@ -233,12 +233,12 @@ function createRequestClient(baseURL: string) {
     fulfilled: (response) => {
       const { data: responseData, status } = response;
 
-      const { code, data, message: msg } = responseData;
+      const { code, data } = responseData;
 
       if (status >= 200 && status < 400 && code === 0) {
         return data;
       }
-      throw new Error(`Error ${status}: ${msg}`);
+      throw Object.assign({}, response, { response });
     },
   });
 
@@ -255,9 +255,13 @@ function createRequestClient(baseURL: string) {
 
   // Generic error handling; if none of the above error handling logic is triggered, it will fall back to this.
   client.addResponseInterceptor(
-    errorMessageResponseInterceptor((msg: string, _error) => {
+    errorMessageResponseInterceptor((msg: string, error) => {
       // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
-      message.error(msg);
+      // 当前mock接口返回的错误字段是 error 或者 message
+      const responseData = error?.response?.data ?? {};
+      const errorMessage = responseData?.error ?? responseData?.message ?? '';
+      // 如果没有错误信息,则会根据状态码进行提示
+      message.error(errorMessage || msg);
     }),
   );
 

+ 2 - 2
docs/src/en/guide/essentials/settings.md

@@ -226,7 +226,7 @@ const defaultPreferences: Preferences = {
     width: 230,
   },
   tabbar: {
-    dragable: true,
+    draggable: true,
     enable: true,
     height: 36,
     keepAlive: true,
@@ -406,7 +406,7 @@ interface ShortcutKeyPreferences {
 
 interface TabbarPreferences {
   /** Whether dragging of multiple tabs is enabled */
-  dragable: boolean;
+  draggable: boolean;
   /** Whether multiple tabs are enabled */
   enable: boolean;
   /** Tab height */

+ 1 - 1
docs/src/en/guide/in-depth/locale.md

@@ -58,7 +58,7 @@ updateLocale('en-US');
 
 To add new translation texts, simply find `src/locales/langs/` in the corresponding application and add the texts accordingly, for example:
 
-**src/locales/langs/zh-CN.ts**
+**src/locales/langs/zh-CN/\*.json**
 
 ````ts
 ```json

+ 8 - 1
docs/src/en/guide/other/faq.md

@@ -141,12 +141,19 @@ After deploying to `nginx`,you might encounter the following error:
 Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "application/octet-stream". Strict MIME type checking is enforced for module scripts per HTML spec.
 ```
 
-Solution:
+Solution 1:
 
 ```bash
 http {
+    #If there is such a configuration, it needs to be commented out
+    #include       mime.types;
+
     types {
       application/javascript js mjs;
     }
 }
 ```
+
+Solution 2:
+
+Open the `mime.types` file under `nginx` and change `application/javascript js;` to `application/javascript js mjs;`

+ 1 - 1
docs/src/friend-links/index.md

@@ -1,6 +1,6 @@
 # 友情链接
 
-如果您的网站是与 Vben Admin 相关的,或者也属于开源项目,欢迎联系我们,我们会将与您的网站加入交换友情链接。
+如果您的网站是与 Vben Admin 相关的,也属于开源项目,欢迎联系我们,我们会将与您的网站加入交换友情链接。
 
 ## 交换方式
 

+ 22 - 9
docs/src/guide/essentials/route.md

@@ -105,7 +105,7 @@ const routes: RouteRecordRaw[] = [
       icon: 'ic:baseline-view-in-ar',
       keepAlive: true,
       order: 1000,
-      title: $t('page.demos.title'),
+      title: $t('demos.title'),
     },
     name: 'Demos',
     path: '/demos',
@@ -115,7 +115,7 @@ const routes: RouteRecordRaw[] = [
       {
         meta: {
           icon: 'ic:round-menu',
-          title: $t('page.demos.nested.title'),
+          title: $t('demos.nested.title'),
         },
         name: 'NestedDemos',
         path: '/demos/nested',
@@ -128,7 +128,7 @@ const routes: RouteRecordRaw[] = [
             meta: {
               icon: 'ic:round-menu',
               keepAlive: true,
-              title: $t('page.demos.nested.menu1'),
+              title: $t('demos.nested.menu1'),
             },
           },
           {
@@ -137,7 +137,7 @@ const routes: RouteRecordRaw[] = [
             meta: {
               icon: 'ic:round-menu',
               keepAlive: true,
-              title: $t('page.demos.nested.menu2'),
+              title: $t('demos.nested.menu2'),
             },
             redirect: '/demos/nested/menu2/menu2-1',
             children: [
@@ -148,7 +148,7 @@ const routes: RouteRecordRaw[] = [
                 meta: {
                   icon: 'ic:round-menu',
                   keepAlive: true,
-                  title: $t('page.demos.nested.menu2_1'),
+                  title: $t('demos.nested.menu2_1'),
                 },
               },
             ],
@@ -158,7 +158,7 @@ const routes: RouteRecordRaw[] = [
             path: '/demos/nested/menu3',
             meta: {
               icon: 'ic:round-menu',
-              title: $t('page.demos.nested.menu3'),
+              title: $t('demos.nested.menu3'),
             },
             redirect: '/demos/nested/menu3/menu3-1',
             children: [
@@ -169,7 +169,7 @@ const routes: RouteRecordRaw[] = [
                 meta: {
                   icon: 'ic:round-menu',
                   keepAlive: true,
-                  title: $t('page.demos.nested.menu3_1'),
+                  title: $t('demos.nested.menu3_1'),
                 },
               },
               {
@@ -177,7 +177,7 @@ const routes: RouteRecordRaw[] = [
                 path: 'menu3-2',
                 meta: {
                   icon: 'ic:round-menu',
-                  title: $t('page.demos.nested.menu3_2'),
+                  title: $t('demos.nested.menu3_2'),
                 },
                 redirect: '/demos/nested/menu3/menu3-2/menu3-2-1',
                 children: [
@@ -189,7 +189,7 @@ const routes: RouteRecordRaw[] = [
                     meta: {
                       icon: 'ic:round-menu',
                       keepAlive: true,
-                      title: $t('page.demos.nested.menu3_2_1'),
+                      title: $t('demos.nested.menu3_2_1'),
                     },
                   },
                 ],
@@ -386,6 +386,10 @@ interface RouteMeta {
    * 用于路由->菜单排序
    */
   order?: number;
+  /**
+   * 菜单所携带的参数
+   */
+  query?: Recordable;
   /**
    * 标题名称
    */
@@ -542,6 +546,15 @@ interface RouteMeta {
 
 用于配置页面的排序,用于路由到菜单排序。
 
+_注意:_ 排序仅针对一级菜单有效,二级菜单的排序需要在对应的一级菜单中按代码顺序设置。
+
+### query
+
+- 类型:`Recordable`
+- 默认值:`{}`
+
+用于配置页面的菜单参数,会在菜单中传递给页面。
+
 ## 路由刷新
 
 路由刷新方式如下:

+ 8 - 4
docs/src/guide/essentials/server.md

@@ -236,12 +236,12 @@ function createRequestClient(baseURL: string) {
     fulfilled: (response) => {
       const { data: responseData, status } = response;
 
-      const { code, data, message: msg } = responseData;
+      const { code, data } = responseData;
 
       if (status >= 200 && status < 400 && code === 0) {
         return data;
       }
-      throw new Error(`Error ${status}: ${msg}`);
+      throw Object.assign({}, response, { response });
     },
   });
 
@@ -258,9 +258,13 @@ function createRequestClient(baseURL: string) {
 
   // 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
   client.addResponseInterceptor(
-    errorMessageResponseInterceptor((msg: string, _error) => {
+    errorMessageResponseInterceptor((msg: string, error) => {
       // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
-      message.error(msg);
+      // 当前mock接口返回的错误字段是 error 或者 message
+      const responseData = error?.response?.data ?? {};
+      const errorMessage = responseData?.error ?? responseData?.message ?? '';
+      // 如果没有错误信息,则会根据状态码进行提示
+      message.error(errorMessage || msg);
     }),
   );
 

+ 2 - 2
docs/src/guide/essentials/settings.md

@@ -248,7 +248,7 @@ const defaultPreferences: Preferences = {
     width: 230,
   },
   tabbar: {
-    dragable: true,
+    draggable: true,
     enable: true,
     height: 36,
     keepAlive: true,
@@ -430,7 +430,7 @@ interface ShortcutKeyPreferences {
 
 interface TabbarPreferences {
   /** 是否开启多标签页拖拽 */
-  dragable: boolean;
+  draggable: boolean;
   /** 是否开启多标签页 */
   enable: boolean;
   /** 标签页高度 */

+ 1 - 1
docs/src/guide/in-depth/access.md

@@ -11,7 +11,7 @@ outline: deep
 
 ## 前端访问控制
 
-**实现原理**: 在前端固定写死路由的权限,指定路由有哪些权限可以查看。只初始化通用的路由,需要权限才能访问的路由没有被加入路由表内。在登后或者其他方式获取用户角色后,通过角色去遍历路由表,获取该角色可以访问的路由表,生成路由表,再通过 `router.addRoute` 添加到路由实例,实现权限的过滤。
+**实现原理**: 在前端固定写死路由的权限,指定路由有哪些权限可以查看。只初始化通用的路由,需要权限才能访问的路由没有被加入路由表内。在登后或者其他方式获取用户角色后,通过角色去遍历路由表,获取该角色可以访问的路由表,生成路由表,再通过 `router.addRoute` 添加到路由实例,实现权限的过滤。
 
 **缺点**: 权限相对不自由,如果后台改动角色,前台也需要跟着改动。适合角色较固定的系统
 

+ 1 - 1
docs/src/guide/in-depth/locale.md

@@ -58,7 +58,7 @@ updateLocale('en-US');
 
 新增翻译文本,只需要在对应的应用内,找到 `src/locales/langs/`,新增对应的文本即可,例:
 
-**src/locales/langs/zh-CN.ts**
+**src/locales/langs/zh-CN/\*.json**
 
 ````ts
 ```json

+ 1 - 1
docs/src/guide/introduction/thin.md

@@ -12,7 +12,7 @@ outline: deep
 
 ```bash
 apps/web-ele
-apps/web-native
+apps/web-naive
 
 ```
 

+ 9 - 2
docs/src/guide/other/faq.md

@@ -81,7 +81,7 @@ getCurrentInstance().ctx.xxxx;
 
 ## 依赖安装问题
 
-- 如果依赖安装不了或者启动报错可以尝试执行`pnpm run resintall`。
+- 如果依赖安装不了或者启动报错可以尝试执行`pnpm run reinstall`。
 - 如果依赖安装不了或者报错,可以尝试切换手机热点来进行依赖安装。
 - 如果还是不行,可以自行配置国内镜像安装。
 - 也可以在项目根目录创建 `.npmrc` 文件,内容如下
@@ -141,12 +141,19 @@ at Extract (vue-vben-admin-main\node_modules@purge-icons\core\dist\index.js:173:
 Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "application/octet-stream". Strict MIME type checking is enforced for module scripts per HTML spec.
 ```
 
-解决方式:
+解决方式
 
 ```bash
 http {
+    #如果有此项配置需要注释掉
+    #include       mime.types;
+
     types {
       application/javascript js mjs;
     }
 }
 ```
+
+解决方式二:
+
+进入 `nginx` 下的`mime.types`文件, 将`application/javascript js;` 修改为 `application/javascript js mjs;`

+ 1 - 1
internal/lint-configs/commitlint-config/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vben/commitlint-config",
-  "version": "5.3.2",
+  "version": "5.4.2",
   "private": true,
   "homepage": "https://github.com/vbenjs/vue-vben-admin",
   "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",

+ 1 - 0
internal/lint-configs/eslint-config/src/custom-config.ts

@@ -101,6 +101,7 @@ const customConfig: Linter.Config[] = [
       ],
     },
   },
+
   {
     // 不能引入@vben/*里面的包
     files: [

+ 1 - 1
internal/lint-configs/stylelint-config/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vben/stylelint-config",
-  "version": "5.3.2",
+  "version": "5.4.2",
   "private": true,
   "homepage": "https://github.com/vbenjs/vue-vben-admin",
   "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",

+ 1 - 1
internal/node-utils/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vben/node-utils",
-  "version": "5.3.2",
+  "version": "5.4.2",
   "private": true,
   "homepage": "https://github.com/vbenjs/vue-vben-admin",
   "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",

+ 4 - 7
internal/node-utils/src/monorepo.ts

@@ -11,13 +11,10 @@ import { findUpSync } from 'find-up';
  * @param cwd
  */
 function findMonorepoRoot(cwd: string = process.cwd()) {
-  const lockFile = findUpSync(
-    ['pnpm-lock.yaml', 'yarn.lock', 'package-lock.json'],
-    {
-      cwd,
-      type: 'file',
-    },
-  );
+  const lockFile = findUpSync('pnpm-lock.yaml', {
+    cwd,
+    type: 'file',
+  });
   return dirname(lockFile || '');
 }
 

+ 1 - 1
internal/tailwind-config/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vben/tailwind-config",
-  "version": "5.3.2",
+  "version": "5.4.2",
   "private": true,
   "homepage": "https://github.com/vbenjs/vue-vben-admin",
   "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",

+ 3 - 4
internal/tailwind-config/src/index.ts

@@ -1,6 +1,5 @@
 import type { Config } from 'tailwindcss';
 
-import fs from 'node:fs';
 import path from 'node:path';
 
 import { getPackagesSync } from '@vben/node-utils';
@@ -19,9 +18,9 @@ const tailwindPackages: string[] = [];
 
 packages.forEach((pkg) => {
   // apps目录下和 @vben-core/tailwind-ui 包需要使用到 tailwindcss ui
-  if (fs.existsSync(path.join(pkg.dir, 'tailwind.config.mjs'))) {
-    tailwindPackages.push(pkg.dir);
-  }
+  // if (fs.existsSync(path.join(pkg.dir, 'tailwind.config.mjs'))) {
+  tailwindPackages.push(pkg.dir);
+  // }
 });
 
 const shadcnUiColors = {

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