mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-26 00:26:20 +08:00
Compare commits
29 Commits
v5.4.0-bet
...
v5.4.2
Author | SHA1 | Date | |
---|---|---|---|
![]() |
bfaa2780ab | ||
![]() |
d262b7b6c0 | ||
![]() |
93b48ef244 | ||
![]() |
860fc15ce6 | ||
![]() |
646598afba | ||
![]() |
234544c40d | ||
![]() |
307a71fb35 | ||
![]() |
477a05c26c | ||
![]() |
0df8c5c02c | ||
![]() |
d1ca09c7bb | ||
![]() |
ff3c5f8581 | ||
![]() |
240f0b5f8d | ||
![]() |
6f3d50984f | ||
![]() |
c491b9e021 | ||
![]() |
6cd9937c03 | ||
![]() |
f89f4f32c7 | ||
![]() |
c432e0ac33 | ||
![]() |
719c9a8f2d | ||
![]() |
a0fbe0b21a | ||
![]() |
f7fa69d33b | ||
![]() |
7c45aeb868 | ||
![]() |
850a6af1e0 | ||
![]() |
d5a210f53f | ||
![]() |
6c4a742627 | ||
![]() |
45987fc1e3 | ||
![]() |
ea962e75d0 | ||
![]() |
24d14c2841 | ||
![]() |
8b961a9d7f | ||
![]() |
9856bc88d2 |
2
.github/release-drafter.yml
vendored
2
.github/release-drafter.yml
vendored
@@ -16,7 +16,7 @@ categories:
|
|||||||
- title: '🐞 Bug Fixes'
|
- title: '🐞 Bug Fixes'
|
||||||
labels:
|
labels:
|
||||||
- 'bug'
|
- 'bug'
|
||||||
- title: '📈 Performance'
|
- title: '📈 Performance & Enhancement'
|
||||||
labels:
|
labels:
|
||||||
- 'perf'
|
- 'perf'
|
||||||
- 'enhancement'
|
- 'enhancement'
|
||||||
|
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@@ -197,11 +197,14 @@
|
|||||||
"playground/src/locales/langs",
|
"playground/src/locales/langs",
|
||||||
"apps/*/src/locales/langs"
|
"apps/*/src/locales/langs"
|
||||||
],
|
],
|
||||||
"i18n-ally.pathMatcher": "{locale}.json",
|
"i18n-ally.pathMatcher": "{locale}/{namespace}.{ext}",
|
||||||
"i18n-ally.enabledParsers": ["json", "ts", "js", "yaml"],
|
"i18n-ally.enabledParsers": ["json"],
|
||||||
"i18n-ally.sourceLanguage": "en",
|
"i18n-ally.sourceLanguage": "en",
|
||||||
"i18n-ally.displayLanguage": "zh-CN",
|
"i18n-ally.displayLanguage": "zh-CN",
|
||||||
"i18n-ally.enabledFrameworks": ["vue", "react"],
|
"i18n-ally.enabledFrameworks": ["vue", "react"],
|
||||||
|
"i18n-ally.keystyle": "nested",
|
||||||
|
"i18n-ally.sortKeys": true,
|
||||||
|
"i18n-ally.namespace": true,
|
||||||
|
|
||||||
// 控制相关文件嵌套展示
|
// 控制相关文件嵌套展示
|
||||||
"explorer.fileNesting.enabled": true,
|
"explorer.fileNesting.enabled": true,
|
||||||
@@ -216,7 +219,6 @@
|
|||||||
"tailwind.config.mjs": "postcss.*"
|
"tailwind.config.mjs": "postcss.*"
|
||||||
},
|
},
|
||||||
"commentTranslate.hover.enabled": false,
|
"commentTranslate.hover.enabled": false,
|
||||||
"i18n-ally.keystyle": "nested",
|
|
||||||
"commentTranslate.multiLineMerge": true,
|
"commentTranslate.multiLineMerge": true,
|
||||||
"vue.server.hybridMode": true,
|
"vue.server.hybridMode": true,
|
||||||
"typescript.tsdk": "node_modules/typescript/lib"
|
"typescript.tsdk": "node_modules/typescript/lib"
|
||||||
|
@@ -134,7 +134,7 @@ If you think this project is helpful to you, you can help the author buy a cup o
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
<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
|
## Contributor
|
||||||
|
|
||||||
|
@@ -21,7 +21,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
|
|
||||||
if (!findUser) {
|
if (!findUser) {
|
||||||
clearRefreshTokenCookie(event);
|
clearRefreshTokenCookie(event);
|
||||||
return forbiddenResponse(event);
|
return forbiddenResponse(event, 'Username or password is incorrect.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const accessToken = generateAccessToken(findUser);
|
const accessToken = generateAccessToken(findUser);
|
||||||
|
@@ -86,7 +86,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
|
|||||||
component: '/demos/access/admin-visible',
|
component: '/demos/access/admin-visible',
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'mdi:button-cursor',
|
icon: 'mdi:button-cursor',
|
||||||
title: 'page.demos.access.adminVisible',
|
title: 'demos.access.adminVisible',
|
||||||
},
|
},
|
||||||
name: 'AccessAdminVisibleDemo',
|
name: 'AccessAdminVisibleDemo',
|
||||||
path: '/demos/access/admin-visible',
|
path: '/demos/access/admin-visible',
|
||||||
@@ -95,7 +95,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
|
|||||||
component: '/demos/access/super-visible',
|
component: '/demos/access/super-visible',
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'mdi:button-cursor',
|
icon: 'mdi:button-cursor',
|
||||||
title: 'page.demos.access.superVisible',
|
title: 'demos.access.superVisible',
|
||||||
},
|
},
|
||||||
name: 'AccessSuperVisibleDemo',
|
name: 'AccessSuperVisibleDemo',
|
||||||
path: '/demos/access/super-visible',
|
path: '/demos/access/super-visible',
|
||||||
@@ -104,7 +104,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
|
|||||||
component: '/demos/access/user-visible',
|
component: '/demos/access/user-visible',
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'mdi:button-cursor',
|
icon: 'mdi:button-cursor',
|
||||||
title: 'page.demos.access.userVisible',
|
title: 'demos.access.userVisible',
|
||||||
},
|
},
|
||||||
name: 'AccessUserVisibleDemo',
|
name: 'AccessUserVisibleDemo',
|
||||||
path: '/demos/access/user-visible',
|
path: '/demos/access/user-visible',
|
||||||
@@ -118,7 +118,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
|
|||||||
icon: 'ic:baseline-view-in-ar',
|
icon: 'ic:baseline-view-in-ar',
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
order: 1000,
|
order: 1000,
|
||||||
title: 'page.demos.title',
|
title: 'demos.title',
|
||||||
},
|
},
|
||||||
name: 'Demos',
|
name: 'Demos',
|
||||||
path: '/demos',
|
path: '/demos',
|
||||||
@@ -129,7 +129,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
|
|||||||
path: '/demosaccess',
|
path: '/demosaccess',
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'mdi:cloud-key-outline',
|
icon: 'mdi:cloud-key-outline',
|
||||||
title: 'page.demos.access.backendPermissions',
|
title: 'demos.access.backendPermissions',
|
||||||
},
|
},
|
||||||
redirect: '/demos/access/page-control',
|
redirect: '/demos/access/page-control',
|
||||||
children: [
|
children: [
|
||||||
@@ -139,7 +139,7 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
|
|||||||
component: '/demos/access/index',
|
component: '/demos/access/index',
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'mdi:page-previous-outline',
|
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',
|
component: '/demos/access/button-control',
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'mdi:button-cursor',
|
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'],
|
authority: ['no-body'],
|
||||||
icon: 'mdi:button-cursor',
|
icon: 'mdi:button-cursor',
|
||||||
menuVisibleWithForbidden: true,
|
menuVisibleWithForbidden: true,
|
||||||
title: 'page.demos.access.menuVisible403',
|
title: 'demos.access.menuVisible403',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
roleWithMenus[role],
|
roleWithMenus[role],
|
||||||
|
@@ -39,9 +39,12 @@ 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);
|
setResponseStatus(event, 403);
|
||||||
return useResponseError('Forbidden Exception', 'Forbidden Exception');
|
return useResponseError(message, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function unAuthorizedResponse(event: H3Event<EventHandlerRequest>) {
|
export function unAuthorizedResponse(event: H3Event<EventHandlerRequest>) {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/web-antd",
|
"name": "@vben/web-antd",
|
||||||
"version": "5.4.0-beta.1",
|
"version": "5.4.2",
|
||||||
"homepage": "https://vben.pro",
|
"homepage": "https://vben.pro",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
127
apps/web-antd/src/adapter/component/index.ts
Normal file
127
apps/web-antd/src/adapter/component/index.ts
Normal file
@@ -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 };
|
@@ -1,105 +1,14 @@
|
|||||||
import type {
|
import type {
|
||||||
BaseFormComponentType,
|
|
||||||
VbenFormSchema as FormSchema,
|
VbenFormSchema as FormSchema,
|
||||||
VbenFormProps,
|
VbenFormProps,
|
||||||
} from '@vben/common-ui';
|
} from '@vben/common-ui';
|
||||||
|
|
||||||
import type { Component, SetupContext } from 'vue';
|
import type { ComponentType } from './component';
|
||||||
import { h } from 'vue';
|
|
||||||
|
|
||||||
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
import {
|
setupVbenForm<ComponentType>({
|
||||||
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,
|
|
||||||
},
|
|
||||||
config: {
|
config: {
|
||||||
// ant design vue组件库默认都是 v-model:value
|
// ant design vue组件库默认都是 v-model:value
|
||||||
baseModelPropName: 'value',
|
baseModelPropName: 'value',
|
||||||
@@ -116,23 +25,23 @@ setupVbenForm<FormComponentType>({
|
|||||||
// 输入项目必填国际化适配
|
// 输入项目必填国际化适配
|
||||||
required: (value, _params, ctx) => {
|
required: (value, _params, ctx) => {
|
||||||
if (value === undefined || value === null || value.length === 0) {
|
if (value === undefined || value === null || value.length === 0) {
|
||||||
return $t('formRules.required', [ctx.label]);
|
return $t('ui.formRules.required', [ctx.label]);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
// 选择项目必填国际化适配
|
// 选择项目必填国际化适配
|
||||||
selectRequired: (value, _params, ctx) => {
|
selectRequired: (value, _params, ctx) => {
|
||||||
if (value === undefined || value === null) {
|
if (value === undefined || value === null) {
|
||||||
return $t('formRules.selectRequired', [ctx.label]);
|
return $t('ui.formRules.selectRequired', [ctx.label]);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const useVbenForm = useForm<FormComponentType>;
|
const useVbenForm = useForm<ComponentType>;
|
||||||
|
|
||||||
export { useVbenForm, z };
|
export { useVbenForm, z };
|
||||||
|
|
||||||
export type VbenFormSchema = FormSchema<FormComponentType>;
|
export type VbenFormSchema = FormSchema<ComponentType>;
|
||||||
export type { VbenFormProps };
|
export type { VbenFormProps };
|
||||||
|
@@ -1,2 +0,0 @@
|
|||||||
export * from './form';
|
|
||||||
export * from './vxe-table';
|
|
@@ -74,11 +74,12 @@ function createRequestClient(baseURL: string) {
|
|||||||
fulfilled: (response) => {
|
fulfilled: (response) => {
|
||||||
const { data: responseData, status } = response;
|
const { data: responseData, status } = response;
|
||||||
|
|
||||||
const { code, data, message: msg } = responseData;
|
const { code, data } = responseData;
|
||||||
if (status >= 200 && status < 400 && code === 0) {
|
if (status >= 200 && status < 400 && code === 0) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
throw new Error(`Error ${status}: ${msg}`);
|
|
||||||
|
throw Object.assign({}, response, { response });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -7,10 +7,14 @@ import '@vben/styles/antd';
|
|||||||
|
|
||||||
import { setupI18n } from '#/locales';
|
import { setupI18n } from '#/locales';
|
||||||
|
|
||||||
|
import { initComponentAdapter } from './adapter/component';
|
||||||
import App from './app.vue';
|
import App from './app.vue';
|
||||||
import { router } from './router';
|
import { router } from './router';
|
||||||
|
|
||||||
async function bootstrap(namespace: string) {
|
async function bootstrap(namespace: string) {
|
||||||
|
// 初始化组件适配器
|
||||||
|
await initComponentAdapter();
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
// 国际化 i18n 配置
|
// 国际化 i18n 配置
|
||||||
|
@@ -68,7 +68,7 @@ const menus = computed(() => [
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: BookOpenText,
|
icon: BookOpenText,
|
||||||
text: $t('widgets.document'),
|
text: $t('ui.widgets.document'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
handler: () => {
|
handler: () => {
|
||||||
@@ -86,7 +86,7 @@ const menus = computed(() => [
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: CircleHelp,
|
icon: CircleHelp,
|
||||||
text: $t('widgets.qa'),
|
text: $t('ui.widgets.qa'),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -4,7 +4,11 @@ import type { Locale } from 'ant-design-vue/es/locale';
|
|||||||
import type { App } from 'vue';
|
import type { App } from 'vue';
|
||||||
import { ref } 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 { preferences } from '@vben/preferences';
|
||||||
|
|
||||||
import antdEnLocale from 'ant-design-vue/es/locale/en_US';
|
import antdEnLocale from 'ant-design-vue/es/locale/en_US';
|
||||||
@@ -13,10 +17,12 @@ import dayjs from 'dayjs';
|
|||||||
|
|
||||||
const antdLocale = ref<Locale>(antdDefaultLocale);
|
const antdLocale = ref<Locale>(antdDefaultLocale);
|
||||||
|
|
||||||
const modules = import.meta.glob('./langs/*.json');
|
const modules = import.meta.glob('./langs/**/*.json');
|
||||||
|
|
||||||
const localesMap = loadLocalesMap(modules);
|
|
||||||
|
|
||||||
|
const localesMap = loadLocalesMapFromDir(
|
||||||
|
/\.\/langs\/([^/]+)\/(.*)\.json$/,
|
||||||
|
modules,
|
||||||
|
);
|
||||||
/**
|
/**
|
||||||
* 加载应用特有的语言包
|
* 加载应用特有的语言包
|
||||||
* 这里也可以改造为从服务端获取翻译数据
|
* 这里也可以改造为从服务端获取翻译数据
|
||||||
@@ -45,14 +51,14 @@ async function loadThirdPartyMessage(lang: SupportedLanguagesType) {
|
|||||||
async function loadDayjsLocale(lang: SupportedLanguagesType) {
|
async function loadDayjsLocale(lang: SupportedLanguagesType) {
|
||||||
let locale;
|
let locale;
|
||||||
switch (lang) {
|
switch (lang) {
|
||||||
case 'zh-CN': {
|
|
||||||
locale = await import('dayjs/locale/zh-cn');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'en-US': {
|
case 'en-US': {
|
||||||
locale = await import('dayjs/locale/en');
|
locale = await import('dayjs/locale/en');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'zh-CN': {
|
||||||
|
locale = await import('dayjs/locale/zh-cn');
|
||||||
|
break;
|
||||||
|
}
|
||||||
// 默认使用英语
|
// 默认使用英语
|
||||||
default: {
|
default: {
|
||||||
locale = await import('dayjs/locale/en');
|
locale = await import('dayjs/locale/en');
|
||||||
@@ -71,14 +77,14 @@ async function loadDayjsLocale(lang: SupportedLanguagesType) {
|
|||||||
*/
|
*/
|
||||||
async function loadAntdLocale(lang: SupportedLanguagesType) {
|
async function loadAntdLocale(lang: SupportedLanguagesType) {
|
||||||
switch (lang) {
|
switch (lang) {
|
||||||
case 'zh-CN': {
|
|
||||||
antdLocale.value = antdDefaultLocale;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'en-US': {
|
case 'en-US': {
|
||||||
antdLocale.value = antdEnLocale;
|
antdLocale.value = antdEnLocale;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'zh-CN': {
|
||||||
|
antdLocale.value = antdDefaultLocale;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"page": {
|
|
||||||
"demos": {
|
|
||||||
"title": "Demos",
|
|
||||||
"antd": "Ant Design Vue"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
12
apps/web-antd/src/locales/langs/en-US/demos.json
Normal file
12
apps/web-antd/src/locales/langs/en-US/demos.json
Normal file
@@ -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
apps/web-antd/src/locales/langs/en-US/page.json
Normal file
14
apps/web-antd/src/locales/langs/en-US/page.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"page": {
|
|
||||||
"demos": {
|
|
||||||
"title": "演示",
|
|
||||||
"antd": "Ant Design Vue"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
12
apps/web-antd/src/locales/langs/zh-CN/demos.json
Normal file
12
apps/web-antd/src/locales/langs/zh-CN/demos.json
Normal file
@@ -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
apps/web-antd/src/locales/langs/zh-CN/page.json
Normal file
14
apps/web-antd/src/locales/langs/zh-CN/page.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"auth": {
|
||||||
|
"login": "登录",
|
||||||
|
"register": "注册",
|
||||||
|
"codeLogin": "验证码登录",
|
||||||
|
"qrcodeLogin": "二维码登录",
|
||||||
|
"forgetPassword": "忘记密码"
|
||||||
|
},
|
||||||
|
"dashboard": {
|
||||||
|
"title": "概览",
|
||||||
|
"analytics": "分析页",
|
||||||
|
"workspace": "工作台"
|
||||||
|
}
|
||||||
|
}
|
@@ -32,6 +32,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
{
|
{
|
||||||
component: AuthPageLayout,
|
component: AuthPageLayout,
|
||||||
meta: {
|
meta: {
|
||||||
|
hideInTab: true,
|
||||||
title: 'Authentication',
|
title: 'Authentication',
|
||||||
},
|
},
|
||||||
name: 'Authentication',
|
name: 'Authentication',
|
||||||
@@ -42,7 +43,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
path: 'login',
|
path: 'login',
|
||||||
component: Login,
|
component: Login,
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.core.login'),
|
title: $t('page.auth.login'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -50,7 +51,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
path: 'code-login',
|
path: 'code-login',
|
||||||
component: () => import('#/views/_core/authentication/code-login.vue'),
|
component: () => import('#/views/_core/authentication/code-login.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.core.codeLogin'),
|
title: $t('page.auth.codeLogin'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -59,7 +60,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
component: () =>
|
component: () =>
|
||||||
import('#/views/_core/authentication/qrcode-login.vue'),
|
import('#/views/_core/authentication/qrcode-login.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.core.qrcodeLogin'),
|
title: $t('page.auth.qrcodeLogin'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -68,7 +69,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
component: () =>
|
component: () =>
|
||||||
import('#/views/_core/authentication/forget-password.vue'),
|
import('#/views/_core/authentication/forget-password.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.core.forgetPassword'),
|
title: $t('page.auth.forgetPassword'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -76,7 +77,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
path: 'register',
|
path: 'register',
|
||||||
component: () => import('#/views/_core/authentication/register.vue'),
|
component: () => import('#/views/_core/authentication/register.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.core.register'),
|
title: $t('page.auth.register'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -10,14 +10,14 @@ const routes: RouteRecordRaw[] = [
|
|||||||
icon: 'ic:baseline-view-in-ar',
|
icon: 'ic:baseline-view-in-ar',
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
order: 1000,
|
order: 1000,
|
||||||
title: $t('page.demos.title'),
|
title: $t('demos.title'),
|
||||||
},
|
},
|
||||||
name: 'Demos',
|
name: 'Demos',
|
||||||
path: '/demos',
|
path: '/demos',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.demos.antd'),
|
title: $t('demos.antd'),
|
||||||
},
|
},
|
||||||
name: 'AntDesignDemos',
|
name: 'AntDesignDemos',
|
||||||
path: '/demos/ant-design',
|
path: '/demos/ant-design',
|
||||||
|
@@ -18,7 +18,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
badgeType: 'dot',
|
badgeType: 'dot',
|
||||||
icon: VBEN_LOGO_URL,
|
icon: VBEN_LOGO_URL,
|
||||||
order: 9999,
|
order: 9999,
|
||||||
title: $t('page.vben.title'),
|
title: $t('demos.vben.title'),
|
||||||
},
|
},
|
||||||
name: 'VbenProject',
|
name: 'VbenProject',
|
||||||
path: '/vben-admin',
|
path: '/vben-admin',
|
||||||
@@ -29,7 +29,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
component: () => import('#/views/_core/about/index.vue'),
|
component: () => import('#/views/_core/about/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'lucide:copyright',
|
icon: 'lucide:copyright',
|
||||||
title: $t('page.vben.about'),
|
title: $t('demos.vben.about'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -39,7 +39,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
meta: {
|
meta: {
|
||||||
icon: 'lucide:book-open-text',
|
icon: 'lucide:book-open-text',
|
||||||
link: VBEN_DOC_URL,
|
link: VBEN_DOC_URL,
|
||||||
title: $t('page.vben.document'),
|
title: $t('demos.vben.document'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -60,7 +60,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
badgeType: 'dot',
|
badgeType: 'dot',
|
||||||
icon: 'logos:naiveui',
|
icon: 'logos:naiveui',
|
||||||
link: VBEN_NAIVE_PREVIEW_URL,
|
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',
|
badgeType: 'dot',
|
||||||
icon: 'logos:element',
|
icon: 'logos:element',
|
||||||
link: VBEN_ELE_PREVIEW_URL,
|
link: VBEN_ELE_PREVIEW_URL,
|
||||||
title: $t('page.vben.element-plus'),
|
title: $t('demos.vben.element-plus'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import type { LoginAndRegisterParams } from '@vben/common-ui';
|
import type { Recordable, UserInfo } from '@vben/types';
|
||||||
import type { UserInfo } from '@vben/types';
|
|
||||||
|
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
@@ -26,7 +25,7 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
* @param params 登录表单数据
|
* @param params 登录表单数据
|
||||||
*/
|
*/
|
||||||
async function authLogin(
|
async function authLogin(
|
||||||
params: LoginAndRegisterParams,
|
params: Recordable<any>,
|
||||||
onSuccess?: () => Promise<void> | void,
|
onSuccess?: () => Promise<void> | void,
|
||||||
) {
|
) {
|
||||||
// 异步处理用户登录操作并获取 accessToken
|
// 异步处理用户登录操作并获取 accessToken
|
||||||
@@ -84,7 +83,7 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
resetAllStores();
|
resetAllStores();
|
||||||
accessStore.setLoginExpired(false);
|
accessStore.setLoginExpired(false);
|
||||||
|
|
||||||
// 回登陆页带上当前路由地址
|
// 回登录页带上当前路由地址
|
||||||
await router.replace({
|
await router.replace({
|
||||||
path: LOGIN_PATH,
|
path: LOGIN_PATH,
|
||||||
query: redirect
|
query: redirect
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<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';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ const formSchema = computed((): VbenFormSchema[] => {
|
|||||||
* Asynchronously handle the login process
|
* Asynchronously handle the login process
|
||||||
* @param values 登录表单数据
|
* @param values 登录表单数据
|
||||||
*/
|
*/
|
||||||
async function handleLogin(values: LoginCodeParams) {
|
async function handleLogin(values: Recordable<any>) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(values);
|
console.log(values);
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { VbenFormSchema } from '@vben/common-ui';
|
import type { VbenFormSchema } from '@vben/common-ui';
|
||||||
|
import type { Recordable } from '@vben/types';
|
||||||
|
|
||||||
import { computed, ref } from 'vue';
|
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
|
// eslint-disable-next-line no-console
|
||||||
console.log('reset email:', value);
|
console.log('reset email:', value);
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<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';
|
import { computed, h, ref } from 'vue';
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ const formSchema = computed((): VbenFormSchema[] => {
|
|||||||
rules(values) {
|
rules(values) {
|
||||||
const { password } = values;
|
const { password } = values;
|
||||||
return z
|
return z
|
||||||
.string()
|
.string({ required_error: $t('authentication.passwordTip') })
|
||||||
.min(1, { message: $t('authentication.passwordTip') })
|
.min(1, { message: $t('authentication.passwordTip') })
|
||||||
.refine((value) => value === password, {
|
.refine((value) => value === password, {
|
||||||
message: $t('authentication.confirmPasswordTip'),
|
message: $t('authentication.confirmPasswordTip'),
|
||||||
@@ -55,7 +56,6 @@ const formSchema = computed((): VbenFormSchema[] => {
|
|||||||
},
|
},
|
||||||
fieldName: 'confirmPassword',
|
fieldName: 'confirmPassword',
|
||||||
label: $t('authentication.confirmPassword'),
|
label: $t('authentication.confirmPassword'),
|
||||||
rules: z.string().min(1, { message: $t('authentication.passwordTip') }),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'VbenCheckbox',
|
component: 'VbenCheckbox',
|
||||||
@@ -67,15 +67,10 @@ const formSchema = computed((): VbenFormSchema[] => {
|
|||||||
h(
|
h(
|
||||||
'a',
|
'a',
|
||||||
{
|
{
|
||||||
class:
|
class: 'vben-link ml-1 ',
|
||||||
'cursor-pointer text-primary ml-1 hover:text-primary-hover',
|
|
||||||
href: '',
|
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
|
// eslint-disable-next-line no-console
|
||||||
console.log('register submit:', value);
|
console.log('register submit:', value);
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/web-ele",
|
"name": "@vben/web-ele",
|
||||||
"version": "5.4.0-beta.1",
|
"version": "5.4.2",
|
||||||
"homepage": "https://vben.pro",
|
"homepage": "https://vben.pro",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
104
apps/web-ele/src/adapter/component/index.ts
Normal file
104
apps/web-ele/src/adapter/component/index.ts
Normal file
@@ -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 };
|
@@ -1,82 +1,14 @@
|
|||||||
import type {
|
import type {
|
||||||
BaseFormComponentType,
|
|
||||||
VbenFormSchema as FormSchema,
|
VbenFormSchema as FormSchema,
|
||||||
VbenFormProps,
|
VbenFormProps,
|
||||||
} from '@vben/common-ui';
|
} from '@vben/common-ui';
|
||||||
|
|
||||||
import type { Component, SetupContext } from 'vue';
|
import type { ComponentType } from './component';
|
||||||
import { h } from 'vue';
|
|
||||||
|
|
||||||
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
import {
|
setupVbenForm<ComponentType>({
|
||||||
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,
|
|
||||||
},
|
|
||||||
config: {
|
config: {
|
||||||
modelPropNameMap: {
|
modelPropNameMap: {
|
||||||
Upload: 'fileList',
|
Upload: 'fileList',
|
||||||
@@ -85,22 +17,22 @@ setupVbenForm<FormComponentType>({
|
|||||||
defineRules: {
|
defineRules: {
|
||||||
required: (value, _params, ctx) => {
|
required: (value, _params, ctx) => {
|
||||||
if (value === undefined || value === null || value.length === 0) {
|
if (value === undefined || value === null || value.length === 0) {
|
||||||
return $t('formRules.required', [ctx.label]);
|
return $t('ui.formRules.required', [ctx.label]);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
selectRequired: (value, _params, ctx) => {
|
selectRequired: (value, _params, ctx) => {
|
||||||
if (value === undefined || value === null) {
|
if (value === undefined || value === null) {
|
||||||
return $t('formRules.selectRequired', [ctx.label]);
|
return $t('ui.formRules.selectRequired', [ctx.label]);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const useVbenForm = useForm<FormComponentType>;
|
const useVbenForm = useForm<ComponentType>;
|
||||||
|
|
||||||
export { useVbenForm, z };
|
export { useVbenForm, z };
|
||||||
|
|
||||||
export type VbenFormSchema = FormSchema<FormComponentType>;
|
export type VbenFormSchema = FormSchema<ComponentType>;
|
||||||
export type { VbenFormProps };
|
export type { VbenFormProps };
|
||||||
|
@@ -1,2 +0,0 @@
|
|||||||
export * from './form';
|
|
||||||
export * from './vxe-table';
|
|
@@ -74,11 +74,11 @@ function createRequestClient(baseURL: string) {
|
|||||||
fulfilled: (response) => {
|
fulfilled: (response) => {
|
||||||
const { data: responseData, status } = response;
|
const { data: responseData, status } = response;
|
||||||
|
|
||||||
const { code, data, message: msg } = responseData;
|
const { code, data } = responseData;
|
||||||
if (status >= 200 && status < 400 && code === 0) {
|
if (status >= 200 && status < 400 && code === 0) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
throw new Error(`Error ${status}: ${msg}`);
|
throw Object.assign({}, response, { response });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -7,10 +7,13 @@ import '@vben/styles/ele';
|
|||||||
|
|
||||||
import { setupI18n } from '#/locales';
|
import { setupI18n } from '#/locales';
|
||||||
|
|
||||||
|
import { initComponentAdapter } from './adapter/component';
|
||||||
import App from './app.vue';
|
import App from './app.vue';
|
||||||
import { router } from './router';
|
import { router } from './router';
|
||||||
|
|
||||||
async function bootstrap(namespace: string) {
|
async function bootstrap(namespace: string) {
|
||||||
|
// 初始化组件适配器
|
||||||
|
await initComponentAdapter();
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
// 国际化 i18n 配置
|
// 国际化 i18n 配置
|
||||||
|
@@ -68,7 +68,7 @@ const menus = computed(() => [
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: BookOpenText,
|
icon: BookOpenText,
|
||||||
text: $t('widgets.document'),
|
text: $t('ui.widgets.document'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
handler: () => {
|
handler: () => {
|
||||||
@@ -86,7 +86,7 @@ const menus = computed(() => [
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: CircleHelp,
|
icon: CircleHelp,
|
||||||
text: $t('widgets.qa'),
|
text: $t('ui.widgets.qa'),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -4,7 +4,11 @@ import type { Language } from 'element-plus/es/locale';
|
|||||||
import type { App } from 'vue';
|
import type { App } from 'vue';
|
||||||
import { ref } 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 { preferences } from '@vben/preferences';
|
||||||
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
@@ -13,10 +17,12 @@ import defaultLocale from 'element-plus/es/locale/lang/zh-cn';
|
|||||||
|
|
||||||
const elementLocale = ref<Language>(defaultLocale);
|
const elementLocale = ref<Language>(defaultLocale);
|
||||||
|
|
||||||
const modules = import.meta.glob('./langs/*.json');
|
const modules = import.meta.glob('./langs/**/*.json');
|
||||||
|
|
||||||
const localesMap = loadLocalesMap(modules);
|
|
||||||
|
|
||||||
|
const localesMap = loadLocalesMapFromDir(
|
||||||
|
/\.\/langs\/([^/]+)\/(.*)\.json$/,
|
||||||
|
modules,
|
||||||
|
);
|
||||||
/**
|
/**
|
||||||
* 加载应用特有的语言包
|
* 加载应用特有的语言包
|
||||||
* 这里也可以改造为从服务端获取翻译数据
|
* 这里也可以改造为从服务端获取翻译数据
|
||||||
@@ -45,14 +51,14 @@ async function loadThirdPartyMessage(lang: SupportedLanguagesType) {
|
|||||||
async function loadDayjsLocale(lang: SupportedLanguagesType) {
|
async function loadDayjsLocale(lang: SupportedLanguagesType) {
|
||||||
let locale;
|
let locale;
|
||||||
switch (lang) {
|
switch (lang) {
|
||||||
case 'zh-CN': {
|
|
||||||
locale = await import('dayjs/locale/zh-cn');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'en-US': {
|
case 'en-US': {
|
||||||
locale = await import('dayjs/locale/en');
|
locale = await import('dayjs/locale/en');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'zh-CN': {
|
||||||
|
locale = await import('dayjs/locale/zh-cn');
|
||||||
|
break;
|
||||||
|
}
|
||||||
// 默认使用英语
|
// 默认使用英语
|
||||||
default: {
|
default: {
|
||||||
locale = await import('dayjs/locale/en');
|
locale = await import('dayjs/locale/en');
|
||||||
@@ -71,14 +77,14 @@ async function loadDayjsLocale(lang: SupportedLanguagesType) {
|
|||||||
*/
|
*/
|
||||||
async function loadElementLocale(lang: SupportedLanguagesType) {
|
async function loadElementLocale(lang: SupportedLanguagesType) {
|
||||||
switch (lang) {
|
switch (lang) {
|
||||||
case 'zh-CN': {
|
|
||||||
elementLocale.value = defaultLocale;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'en-US': {
|
case 'en-US': {
|
||||||
elementLocale.value = enLocale;
|
elementLocale.value = enLocale;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'zh-CN': {
|
||||||
|
elementLocale.value = defaultLocale;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"page": {
|
|
||||||
"demos": {
|
|
||||||
"title": "Demos",
|
|
||||||
"element-plus": "Element Plus"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
12
apps/web-ele/src/locales/langs/en-US/demos.json
Normal file
12
apps/web-ele/src/locales/langs/en-US/demos.json
Normal file
@@ -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
apps/web-ele/src/locales/langs/en-US/page.json
Normal file
14
apps/web-ele/src/locales/langs/en-US/page.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"page": {
|
|
||||||
"demos": {
|
|
||||||
"title": "演示",
|
|
||||||
"element-plus": "Element Plus"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
12
apps/web-ele/src/locales/langs/zh-CN/demos.json
Normal file
12
apps/web-ele/src/locales/langs/zh-CN/demos.json
Normal file
@@ -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
apps/web-ele/src/locales/langs/zh-CN/page.json
Normal file
14
apps/web-ele/src/locales/langs/zh-CN/page.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"auth": {
|
||||||
|
"login": "登录",
|
||||||
|
"register": "注册",
|
||||||
|
"codeLogin": "验证码登录",
|
||||||
|
"qrcodeLogin": "二维码登录",
|
||||||
|
"forgetPassword": "忘记密码"
|
||||||
|
},
|
||||||
|
"dashboard": {
|
||||||
|
"title": "概览",
|
||||||
|
"analytics": "分析页",
|
||||||
|
"workspace": "工作台"
|
||||||
|
}
|
||||||
|
}
|
@@ -32,6 +32,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
{
|
{
|
||||||
component: AuthPageLayout,
|
component: AuthPageLayout,
|
||||||
meta: {
|
meta: {
|
||||||
|
hideInTab: true,
|
||||||
title: 'Authentication',
|
title: 'Authentication',
|
||||||
},
|
},
|
||||||
name: 'Authentication',
|
name: 'Authentication',
|
||||||
@@ -42,7 +43,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
path: 'login',
|
path: 'login',
|
||||||
component: Login,
|
component: Login,
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.core.login'),
|
title: $t('page.auth.login'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -50,7 +51,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
path: 'code-login',
|
path: 'code-login',
|
||||||
component: () => import('#/views/_core/authentication/code-login.vue'),
|
component: () => import('#/views/_core/authentication/code-login.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.core.codeLogin'),
|
title: $t('page.auth.codeLogin'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -59,7 +60,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
component: () =>
|
component: () =>
|
||||||
import('#/views/_core/authentication/qrcode-login.vue'),
|
import('#/views/_core/authentication/qrcode-login.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.core.qrcodeLogin'),
|
title: $t('page.auth.qrcodeLogin'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -68,7 +69,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
component: () =>
|
component: () =>
|
||||||
import('#/views/_core/authentication/forget-password.vue'),
|
import('#/views/_core/authentication/forget-password.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.core.forgetPassword'),
|
title: $t('page.auth.forgetPassword'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -76,7 +77,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
path: 'register',
|
path: 'register',
|
||||||
component: () => import('#/views/_core/authentication/register.vue'),
|
component: () => import('#/views/_core/authentication/register.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.core.register'),
|
title: $t('page.auth.register'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -10,14 +10,14 @@ const routes: RouteRecordRaw[] = [
|
|||||||
icon: 'ic:baseline-view-in-ar',
|
icon: 'ic:baseline-view-in-ar',
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
order: 1000,
|
order: 1000,
|
||||||
title: $t('page.demos.title'),
|
title: $t('demos.title'),
|
||||||
},
|
},
|
||||||
name: 'Demos',
|
name: 'Demos',
|
||||||
path: '/demos',
|
path: '/demos',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.demos.element-plus'),
|
title: $t('demos.elementPlus'),
|
||||||
},
|
},
|
||||||
name: 'NaiveDemos',
|
name: 'NaiveDemos',
|
||||||
path: '/demos/element',
|
path: '/demos/element',
|
||||||
|
@@ -19,7 +19,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
badgeType: 'dot',
|
badgeType: 'dot',
|
||||||
icon: VBEN_LOGO_URL,
|
icon: VBEN_LOGO_URL,
|
||||||
order: 9999,
|
order: 9999,
|
||||||
title: $t('page.vben.title'),
|
title: $t('demos.vben.title'),
|
||||||
},
|
},
|
||||||
name: 'VbenProject',
|
name: 'VbenProject',
|
||||||
path: '/vben-admin',
|
path: '/vben-admin',
|
||||||
@@ -30,7 +30,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
component: () => import('#/views/_core/about/index.vue'),
|
component: () => import('#/views/_core/about/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'lucide:copyright',
|
icon: 'lucide:copyright',
|
||||||
title: $t('page.vben.about'),
|
title: $t('demos.vben.about'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -40,7 +40,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
meta: {
|
meta: {
|
||||||
icon: 'lucide:book-open-text',
|
icon: 'lucide:book-open-text',
|
||||||
link: VBEN_DOC_URL,
|
link: VBEN_DOC_URL,
|
||||||
title: $t('page.vben.document'),
|
title: $t('demos.vben.document'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -61,7 +61,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
badgeType: 'dot',
|
badgeType: 'dot',
|
||||||
icon: 'logos:naiveui',
|
icon: 'logos:naiveui',
|
||||||
link: VBEN_NAIVE_PREVIEW_URL,
|
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',
|
badgeType: 'dot',
|
||||||
icon: SvgAntdvLogoIcon,
|
icon: SvgAntdvLogoIcon,
|
||||||
link: VBEN_ANT_PREVIEW_URL,
|
link: VBEN_ANT_PREVIEW_URL,
|
||||||
title: $t('page.vben.antdv'),
|
title: $t('demos.vben.antdv'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import type { LoginAndRegisterParams } from '@vben/common-ui';
|
import type { Recordable, UserInfo } from '@vben/types';
|
||||||
import type { UserInfo } from '@vben/types';
|
|
||||||
|
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
@@ -26,7 +25,7 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
* @param params 登录表单数据
|
* @param params 登录表单数据
|
||||||
*/
|
*/
|
||||||
async function authLogin(
|
async function authLogin(
|
||||||
params: LoginAndRegisterParams,
|
params: Recordable<any>,
|
||||||
onSuccess?: () => Promise<void> | void,
|
onSuccess?: () => Promise<void> | void,
|
||||||
) {
|
) {
|
||||||
// 异步处理用户登录操作并获取 accessToken
|
// 异步处理用户登录操作并获取 accessToken
|
||||||
@@ -85,7 +84,7 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
resetAllStores();
|
resetAllStores();
|
||||||
accessStore.setLoginExpired(false);
|
accessStore.setLoginExpired(false);
|
||||||
|
|
||||||
// 回登陆页带上当前路由地址
|
// 回登录页带上当前路由地址
|
||||||
await router.replace({
|
await router.replace({
|
||||||
path: LOGIN_PATH,
|
path: LOGIN_PATH,
|
||||||
query: redirect
|
query: redirect
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<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';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ const formSchema = computed((): VbenFormSchema[] => {
|
|||||||
* Asynchronously handle the login process
|
* Asynchronously handle the login process
|
||||||
* @param values 登录表单数据
|
* @param values 登录表单数据
|
||||||
*/
|
*/
|
||||||
async function handleLogin(values: LoginCodeParams) {
|
async function handleLogin(values: Recordable<any>) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(values);
|
console.log(values);
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { VbenFormSchema } from '@vben/common-ui';
|
import type { VbenFormSchema } from '@vben/common-ui';
|
||||||
|
import type { Recordable } from '@vben/types';
|
||||||
|
|
||||||
import { computed, ref } from 'vue';
|
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
|
// eslint-disable-next-line no-console
|
||||||
console.log('reset email:', value);
|
console.log('reset email:', value);
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<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';
|
import { computed, h, ref } from 'vue';
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ const formSchema = computed((): VbenFormSchema[] => {
|
|||||||
rules(values) {
|
rules(values) {
|
||||||
const { password } = values;
|
const { password } = values;
|
||||||
return z
|
return z
|
||||||
.string()
|
.string({ required_error: $t('authentication.passwordTip') })
|
||||||
.min(1, { message: $t('authentication.passwordTip') })
|
.min(1, { message: $t('authentication.passwordTip') })
|
||||||
.refine((value) => value === password, {
|
.refine((value) => value === password, {
|
||||||
message: $t('authentication.confirmPasswordTip'),
|
message: $t('authentication.confirmPasswordTip'),
|
||||||
@@ -55,7 +56,6 @@ const formSchema = computed((): VbenFormSchema[] => {
|
|||||||
},
|
},
|
||||||
fieldName: 'confirmPassword',
|
fieldName: 'confirmPassword',
|
||||||
label: $t('authentication.confirmPassword'),
|
label: $t('authentication.confirmPassword'),
|
||||||
rules: z.string().min(1, { message: $t('authentication.passwordTip') }),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'VbenCheckbox',
|
component: 'VbenCheckbox',
|
||||||
@@ -67,15 +67,10 @@ const formSchema = computed((): VbenFormSchema[] => {
|
|||||||
h(
|
h(
|
||||||
'a',
|
'a',
|
||||||
{
|
{
|
||||||
class:
|
class: 'vben-link ml-1 ',
|
||||||
'cursor-pointer text-primary ml-1 hover:text-primary-hover',
|
|
||||||
href: '',
|
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
|
// eslint-disable-next-line no-console
|
||||||
console.log('register submit:', value);
|
console.log('register submit:', value);
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/web-naive",
|
"name": "@vben/web-naive",
|
||||||
"version": "5.4.0-beta.1",
|
"version": "5.4.2",
|
||||||
"homepage": "https://vben.pro",
|
"homepage": "https://vben.pro",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
103
apps/web-naive/src/adapter/component/index.ts
Normal file
103
apps/web-naive/src/adapter/component/index.ts
Normal file
@@ -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: 'info' }, 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 };
|
@@ -1,84 +1,14 @@
|
|||||||
import type {
|
import type {
|
||||||
BaseFormComponentType,
|
|
||||||
VbenFormSchema as FormSchema,
|
VbenFormSchema as FormSchema,
|
||||||
VbenFormProps,
|
VbenFormProps,
|
||||||
} from '@vben/common-ui';
|
} from '@vben/common-ui';
|
||||||
|
|
||||||
import type { Component, SetupContext } from 'vue';
|
import type { ComponentType } from './component';
|
||||||
import { h } from 'vue';
|
|
||||||
|
|
||||||
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
import {
|
setupVbenForm<ComponentType>({
|
||||||
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,
|
|
||||||
},
|
|
||||||
config: {
|
config: {
|
||||||
// naive-ui组件不接受onChang事件,所以需要禁用
|
// naive-ui组件不接受onChang事件,所以需要禁用
|
||||||
disabledOnChangeListener: true,
|
disabledOnChangeListener: true,
|
||||||
@@ -94,22 +24,22 @@ setupVbenForm<FormComponentType>({
|
|||||||
defineRules: {
|
defineRules: {
|
||||||
required: (value, _params, ctx) => {
|
required: (value, _params, ctx) => {
|
||||||
if (value === undefined || value === null || value.length === 0) {
|
if (value === undefined || value === null || value.length === 0) {
|
||||||
return $t('formRules.required', [ctx.label]);
|
return $t('ui.formRules.required', [ctx.label]);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
selectRequired: (value, _params, ctx) => {
|
selectRequired: (value, _params, ctx) => {
|
||||||
if (value === undefined || value === null) {
|
if (value === undefined || value === null) {
|
||||||
return $t('formRules.selectRequired', [ctx.label]);
|
return $t('ui.formRules.selectRequired', [ctx.label]);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const useVbenForm = useForm<FormComponentType>;
|
const useVbenForm = useForm<ComponentType>;
|
||||||
|
|
||||||
export { useVbenForm, z };
|
export { useVbenForm, z };
|
||||||
|
|
||||||
export type VbenFormSchema = FormSchema<FormComponentType>;
|
export type VbenFormSchema = FormSchema<ComponentType>;
|
||||||
export type { VbenFormProps };
|
export type { VbenFormProps };
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
export * from './form';
|
|
||||||
export * from './naive';
|
|
||||||
export * from './vxe-table';
|
|
@@ -12,7 +12,7 @@ import {
|
|||||||
} from '@vben/request';
|
} from '@vben/request';
|
||||||
import { useAccessStore } from '@vben/stores';
|
import { useAccessStore } from '@vben/stores';
|
||||||
|
|
||||||
import { message } from '#/adapter';
|
import { message } from '#/adapter/naive';
|
||||||
import { useAuthStore } from '#/store';
|
import { useAuthStore } from '#/store';
|
||||||
|
|
||||||
import { refreshTokenApi } from './core';
|
import { refreshTokenApi } from './core';
|
||||||
@@ -73,11 +73,11 @@ function createRequestClient(baseURL: string) {
|
|||||||
fulfilled: (response) => {
|
fulfilled: (response) => {
|
||||||
const { data: responseData, status } = response;
|
const { data: responseData, status } = response;
|
||||||
|
|
||||||
const { code, data, message: msg } = responseData;
|
const { code, data } = responseData;
|
||||||
if (status >= 200 && status < 400 && code === 0) {
|
if (status >= 200 && status < 400 && code === 0) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
throw new Error(`Error ${status}: ${msg}`);
|
throw Object.assign({}, response, { response });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -6,10 +6,13 @@ import '@vben/styles';
|
|||||||
|
|
||||||
import { setupI18n } from '#/locales';
|
import { setupI18n } from '#/locales';
|
||||||
|
|
||||||
|
import { initComponentAdapter } from './adapter/component';
|
||||||
import App from './app.vue';
|
import App from './app.vue';
|
||||||
import { router } from './router';
|
import { router } from './router';
|
||||||
|
|
||||||
async function bootstrap(namespace: string) {
|
async function bootstrap(namespace: string) {
|
||||||
|
// 初始化组件适配器
|
||||||
|
initComponentAdapter();
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
// 国际化 i18n 配置
|
// 国际化 i18n 配置
|
||||||
|
@@ -68,7 +68,7 @@ const menus = computed(() => [
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: BookOpenText,
|
icon: BookOpenText,
|
||||||
text: $t('widgets.document'),
|
text: $t('ui.widgets.document'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
handler: () => {
|
handler: () => {
|
||||||
@@ -86,7 +86,7 @@ const menus = computed(() => [
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: CircleHelp,
|
icon: CircleHelp,
|
||||||
text: $t('widgets.qa'),
|
text: $t('ui.widgets.qa'),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -2,12 +2,19 @@ import type { LocaleSetupOptions, SupportedLanguagesType } from '@vben/locales';
|
|||||||
|
|
||||||
import type { App } from 'vue';
|
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';
|
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,
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载应用特有的语言包
|
* 加载应用特有的语言包
|
||||||
|
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"page": {
|
|
||||||
"demos": {
|
|
||||||
"title": "Demos",
|
|
||||||
"naive": "Naive UI",
|
|
||||||
"table": "Table"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
13
apps/web-naive/src/locales/langs/en-US/demos.json
Normal file
13
apps/web-naive/src/locales/langs/en-US/demos.json
Normal file
@@ -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
apps/web-naive/src/locales/langs/en-US/page.json
Normal file
14
apps/web-naive/src/locales/langs/en-US/page.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"page": {
|
|
||||||
"demos": {
|
|
||||||
"title": "演示",
|
|
||||||
"naive": "Naive UI",
|
|
||||||
"table": "Table"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
13
apps/web-naive/src/locales/langs/zh-CN/demos.json
Normal file
13
apps/web-naive/src/locales/langs/zh-CN/demos.json
Normal file
@@ -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
apps/web-naive/src/locales/langs/zh-CN/page.json
Normal file
14
apps/web-naive/src/locales/langs/zh-CN/page.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"auth": {
|
||||||
|
"login": "登录",
|
||||||
|
"register": "注册",
|
||||||
|
"codeLogin": "验证码登录",
|
||||||
|
"qrcodeLogin": "二维码登录",
|
||||||
|
"forgetPassword": "忘记密码"
|
||||||
|
},
|
||||||
|
"dashboard": {
|
||||||
|
"title": "概览",
|
||||||
|
"analytics": "分析页",
|
||||||
|
"workspace": "工作台"
|
||||||
|
}
|
||||||
|
}
|
@@ -6,7 +6,7 @@ import type {
|
|||||||
import { generateAccessible } from '@vben/access';
|
import { generateAccessible } from '@vben/access';
|
||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
|
|
||||||
import { message } from '#/adapter';
|
import { message } from '#/adapter/naive';
|
||||||
import { getAllMenusApi } from '#/api';
|
import { getAllMenusApi } from '#/api';
|
||||||
import { BasicLayout, IFrameView } from '#/layouts';
|
import { BasicLayout, IFrameView } from '#/layouts';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
|
@@ -32,6 +32,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
{
|
{
|
||||||
component: AuthPageLayout,
|
component: AuthPageLayout,
|
||||||
meta: {
|
meta: {
|
||||||
|
hideInTab: true,
|
||||||
title: 'Authentication',
|
title: 'Authentication',
|
||||||
},
|
},
|
||||||
name: 'Authentication',
|
name: 'Authentication',
|
||||||
@@ -42,7 +43,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
path: 'login',
|
path: 'login',
|
||||||
component: Login,
|
component: Login,
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.core.login'),
|
title: $t('page.auth.login'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -50,7 +51,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
path: 'code-login',
|
path: 'code-login',
|
||||||
component: () => import('#/views/_core/authentication/code-login.vue'),
|
component: () => import('#/views/_core/authentication/code-login.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.core.codeLogin'),
|
title: $t('page.auth.codeLogin'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -59,7 +60,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
component: () =>
|
component: () =>
|
||||||
import('#/views/_core/authentication/qrcode-login.vue'),
|
import('#/views/_core/authentication/qrcode-login.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.core.qrcodeLogin'),
|
title: $t('page.auth.qrcodeLogin'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -68,7 +69,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
component: () =>
|
component: () =>
|
||||||
import('#/views/_core/authentication/forget-password.vue'),
|
import('#/views/_core/authentication/forget-password.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.core.forgetPassword'),
|
title: $t('page.auth.forgetPassword'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -76,7 +77,7 @@ const coreRoutes: RouteRecordRaw[] = [
|
|||||||
path: 'register',
|
path: 'register',
|
||||||
component: () => import('#/views/_core/authentication/register.vue'),
|
component: () => import('#/views/_core/authentication/register.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.core.register'),
|
title: $t('page.auth.register'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -10,14 +10,14 @@ const routes: RouteRecordRaw[] = [
|
|||||||
icon: 'ic:baseline-view-in-ar',
|
icon: 'ic:baseline-view-in-ar',
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
order: 1000,
|
order: 1000,
|
||||||
title: $t('page.demos.title'),
|
title: $t('demos.title'),
|
||||||
},
|
},
|
||||||
name: 'Demos',
|
name: 'Demos',
|
||||||
path: '/demos',
|
path: '/demos',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.demos.naive'),
|
title: $t('demos.naive'),
|
||||||
},
|
},
|
||||||
name: 'NaiveDemos',
|
name: 'NaiveDemos',
|
||||||
path: '/demos/naive',
|
path: '/demos/naive',
|
||||||
@@ -25,7 +25,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
meta: {
|
meta: {
|
||||||
title: $t('page.demos.table'),
|
title: $t('demos.table'),
|
||||||
},
|
},
|
||||||
name: 'Table',
|
name: 'Table',
|
||||||
path: '/demos/table',
|
path: '/demos/table',
|
||||||
|
@@ -19,7 +19,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
badgeType: 'dot',
|
badgeType: 'dot',
|
||||||
icon: VBEN_LOGO_URL,
|
icon: VBEN_LOGO_URL,
|
||||||
order: 9999,
|
order: 9999,
|
||||||
title: $t('page.vben.title'),
|
title: $t('demos.vben.title'),
|
||||||
},
|
},
|
||||||
name: 'VbenProject',
|
name: 'VbenProject',
|
||||||
path: '/vben-admin',
|
path: '/vben-admin',
|
||||||
@@ -30,7 +30,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
component: () => import('#/views/_core/about/index.vue'),
|
component: () => import('#/views/_core/about/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'lucide:copyright',
|
icon: 'lucide:copyright',
|
||||||
title: $t('page.vben.about'),
|
title: $t('demos.vben.about'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -40,7 +40,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
meta: {
|
meta: {
|
||||||
icon: 'lucide:book-open-text',
|
icon: 'lucide:book-open-text',
|
||||||
link: VBEN_DOC_URL,
|
link: VBEN_DOC_URL,
|
||||||
title: $t('page.vben.document'),
|
title: $t('demos.vben.document'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -61,7 +61,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
badgeType: 'dot',
|
badgeType: 'dot',
|
||||||
icon: SvgAntdvLogoIcon,
|
icon: SvgAntdvLogoIcon,
|
||||||
link: VBEN_ANT_PREVIEW_URL,
|
link: VBEN_ANT_PREVIEW_URL,
|
||||||
title: $t('page.vben.antdv'),
|
title: $t('demos.vben.antdv'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -72,7 +72,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
badgeType: 'dot',
|
badgeType: 'dot',
|
||||||
icon: 'logos:element',
|
icon: 'logos:element',
|
||||||
link: VBEN_ELE_PREVIEW_URL,
|
link: VBEN_ELE_PREVIEW_URL,
|
||||||
title: $t('page.vben.element-plus'),
|
title: $t('demos.vben.element-plus'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import type { LoginAndRegisterParams } from '@vben/common-ui';
|
import type { Recordable, UserInfo } from '@vben/types';
|
||||||
import type { UserInfo } from '@vben/types';
|
|
||||||
|
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
@@ -9,7 +8,7 @@ import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores';
|
|||||||
|
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
import { notification } from '#/adapter';
|
import { notification } from '#/adapter/naive';
|
||||||
import { getAccessCodesApi, getUserInfoApi, loginApi, logoutApi } from '#/api';
|
import { getAccessCodesApi, getUserInfoApi, loginApi, logoutApi } from '#/api';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
@@ -26,7 +25,7 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
* @param params 登录表单数据
|
* @param params 登录表单数据
|
||||||
*/
|
*/
|
||||||
async function authLogin(
|
async function authLogin(
|
||||||
params: LoginAndRegisterParams,
|
params: Recordable<any>,
|
||||||
onSuccess?: () => Promise<void> | void,
|
onSuccess?: () => Promise<void> | void,
|
||||||
) {
|
) {
|
||||||
// 异步处理用户登录操作并获取 accessToken
|
// 异步处理用户登录操作并获取 accessToken
|
||||||
@@ -85,7 +84,7 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
resetAllStores();
|
resetAllStores();
|
||||||
accessStore.setLoginExpired(false);
|
accessStore.setLoginExpired(false);
|
||||||
|
|
||||||
// 回登陆页带上当前路由地址
|
// 回登录页带上当前路由地址
|
||||||
await router.replace({
|
await router.replace({
|
||||||
path: LOGIN_PATH,
|
path: LOGIN_PATH,
|
||||||
query: redirect
|
query: redirect
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<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';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ const formSchema = computed((): VbenFormSchema[] => {
|
|||||||
* Asynchronously handle the login process
|
* Asynchronously handle the login process
|
||||||
* @param values 登录表单数据
|
* @param values 登录表单数据
|
||||||
*/
|
*/
|
||||||
async function handleLogin(values: LoginCodeParams) {
|
async function handleLogin(values: Recordable<any>) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(values);
|
console.log(values);
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { VbenFormSchema } from '@vben/common-ui';
|
import type { VbenFormSchema } from '@vben/common-ui';
|
||||||
|
import type { Recordable } from '@vben/types';
|
||||||
|
|
||||||
import { computed, ref } from 'vue';
|
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
|
// eslint-disable-next-line no-console
|
||||||
console.log('reset email:', value);
|
console.log('reset email:', value);
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<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';
|
import { computed, h, ref } from 'vue';
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ const formSchema = computed((): VbenFormSchema[] => {
|
|||||||
rules(values) {
|
rules(values) {
|
||||||
const { password } = values;
|
const { password } = values;
|
||||||
return z
|
return z
|
||||||
.string()
|
.string({ required_error: $t('authentication.passwordTip') })
|
||||||
.min(1, { message: $t('authentication.passwordTip') })
|
.min(1, { message: $t('authentication.passwordTip') })
|
||||||
.refine((value) => value === password, {
|
.refine((value) => value === password, {
|
||||||
message: $t('authentication.confirmPasswordTip'),
|
message: $t('authentication.confirmPasswordTip'),
|
||||||
@@ -55,7 +56,6 @@ const formSchema = computed((): VbenFormSchema[] => {
|
|||||||
},
|
},
|
||||||
fieldName: 'confirmPassword',
|
fieldName: 'confirmPassword',
|
||||||
label: $t('authentication.confirmPassword'),
|
label: $t('authentication.confirmPassword'),
|
||||||
rules: z.string().min(1, { message: $t('authentication.passwordTip') }),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'VbenCheckbox',
|
component: 'VbenCheckbox',
|
||||||
@@ -67,15 +67,10 @@ const formSchema = computed((): VbenFormSchema[] => {
|
|||||||
h(
|
h(
|
||||||
'a',
|
'a',
|
||||||
{
|
{
|
||||||
class:
|
class: 'vben-link ml-1',
|
||||||
'cursor-pointer text-primary ml-1 hover:text-primary-hover',
|
|
||||||
href: '',
|
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
|
// eslint-disable-next-line no-console
|
||||||
console.log('register submit:', value);
|
console.log('register submit:', value);
|
||||||
}
|
}
|
||||||
|
@@ -164,6 +164,10 @@ function sidebarComponents(): DefaultTheme.SidebarItem[] {
|
|||||||
link: 'common-ui/vben-form',
|
link: 'common-ui/vben-form',
|
||||||
text: 'Form 表单',
|
text: 'Form 表单',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
link: 'common-ui/vben-vxe-table',
|
||||||
|
text: 'Vxe Table 表格',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
link: 'common-ui/vben-count-to-animator',
|
link: 'common-ui/vben-count-to-animator',
|
||||||
text: 'CountToAnimator 数字动画',
|
text: 'CountToAnimator 数字动画',
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import '@vben/styles';
|
||||||
|
|
||||||
import './variables.css';
|
import './variables.css';
|
||||||
import './base.css';
|
import './base.css';
|
||||||
import '@vben/styles';
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/docs",
|
"name": "@vben/docs",
|
||||||
"version": "5.4.0-beta.1",
|
"version": "5.4.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vitepress build",
|
"build": "vitepress build",
|
||||||
|
127
docs/src/_env/adapter/component.ts
Normal file
127
docs/src/_env/adapter/component.ts
Normal file
@@ -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 };
|
@@ -1,99 +1,23 @@
|
|||||||
import type {
|
import type {
|
||||||
BaseFormComponentType,
|
|
||||||
VbenFormSchema as FormSchema,
|
VbenFormSchema as FormSchema,
|
||||||
VbenFormProps,
|
VbenFormProps,
|
||||||
} from '@vben/common-ui';
|
} from '@vben/common-ui';
|
||||||
|
|
||||||
import { h } from 'vue';
|
import type { ComponentType } from './component';
|
||||||
|
|
||||||
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
import {
|
import { initComponentAdapter } from './component';
|
||||||
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';
|
|
||||||
|
|
||||||
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
|
initComponentAdapter();
|
||||||
export type FormComponentType =
|
setupVbenForm<ComponentType>({
|
||||||
| '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,
|
|
||||||
},
|
|
||||||
config: {
|
config: {
|
||||||
// ant design vue组件库默认都是 v-model:value
|
|
||||||
baseModelPropName: 'value',
|
baseModelPropName: 'value',
|
||||||
|
// naive-ui组件不接受onChang事件,所以需要禁用
|
||||||
// 一些组件是 v-model:checked 或者 v-model:fileList
|
disabledOnChangeListener: true,
|
||||||
|
// naive-ui组件的空值为null,不能是undefined,否则重置表单时不生效
|
||||||
|
emptyStateValue: null,
|
||||||
modelPropNameMap: {
|
modelPropNameMap: {
|
||||||
Checkbox: 'checked',
|
Checkbox: 'checked',
|
||||||
Radio: 'checked',
|
Radio: 'checked',
|
||||||
@@ -102,26 +26,24 @@ setupVbenForm<FormComponentType>({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
defineRules: {
|
defineRules: {
|
||||||
// 输入项目必填国际化适配
|
|
||||||
required: (value, _params, ctx) => {
|
required: (value, _params, ctx) => {
|
||||||
if (value === undefined || value === null || value.length === 0) {
|
if (value === undefined || value === null || value.length === 0) {
|
||||||
return $t('formRules.required', [ctx.label]);
|
return $t('ui.formRules.required', [ctx.label]);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
// 选择项目必填国际化适配
|
|
||||||
selectRequired: (value, _params, ctx) => {
|
selectRequired: (value, _params, ctx) => {
|
||||||
if (value === undefined || value === null) {
|
if (value === undefined || value === null) {
|
||||||
return $t('formRules.selectRequired', [ctx.label]);
|
return $t('ui.formRules.selectRequired', [ctx.label]);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const useVbenForm = useForm<FormComponentType>;
|
const useVbenForm = useForm<ComponentType>;
|
||||||
|
|
||||||
export { useVbenForm, z };
|
export { useVbenForm, z };
|
||||||
|
|
||||||
export type VbenFormSchema = FormSchema<FormComponentType>;
|
export type VbenFormSchema = FormSchema<ComponentType>;
|
||||||
export type { VbenFormProps };
|
export type { VbenFormProps };
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
社区交流群主要是为了方便大家交流,提问,解答问题,分享经验等。偏自助方式,如果你有问题,可以通过以下方式加入社区交流群:
|
社区交流群主要是为了方便大家交流,提问,解答问题,分享经验等。偏自助方式,如果你有问题,可以通过以下方式加入社区交流群:
|
||||||
|
|
||||||
- [QQ频道](https://pd.qq.com/s/16p8lvvob):推荐!!!主要提供问题解答,分享经验等。
|
- [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): 主要提供问题解答,分享经验等。
|
- [Discord](https://discord.com/invite/VU62jTecad): 主要提供问题解答,分享经验等。
|
||||||
|
|
||||||
::: tip
|
::: tip
|
||||||
|
@@ -14,6 +14,12 @@ outline: deep
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
::: tip README
|
||||||
|
|
||||||
|
下方示例代码中的,存在一些国际化、主题色未适配问题,这些问题只在文档内会出现,实际使用并不会有这些问题,可忽略,不必纠结。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
## 基础用法
|
## 基础用法
|
||||||
|
|
||||||
使用 `useVbenDrawer` 创建最基础的模态框。
|
使用 `useVbenDrawer` 创建最基础的模态框。
|
||||||
@@ -47,7 +53,7 @@ Drawer 内的内容一般业务中,会比较复杂,所以我们可以将 dra
|
|||||||
::: info 注意
|
::: info 注意
|
||||||
|
|
||||||
- `VbenDrawer` 组件对与参数的处理优先级是 `slot` > `props` > `state`(通过api更新的状态以及useVbenDrawer参数)。如果你已经传入了 `slot` 或者 `props`,那么 `setState` 将不会生效,这种情况下你可以通过 `slot` 或者 `props` 来更新状态。
|
- `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`事件除外,内外都会触发。
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
@@ -20,116 +20,23 @@ 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
|
```ts
|
||||||
import type {
|
import type {
|
||||||
BaseFormComponentType,
|
|
||||||
VbenFormSchema as FormSchema,
|
VbenFormSchema as FormSchema,
|
||||||
VbenFormProps,
|
VbenFormProps,
|
||||||
} from '@vben/common-ui';
|
} from '@vben/common-ui';
|
||||||
|
|
||||||
import type { Component, SetupContext } from 'vue';
|
import type { ComponentType } from './component';
|
||||||
import { h } from 'vue';
|
|
||||||
|
|
||||||
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
import {
|
setupVbenForm<ComponentType>({
|
||||||
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,
|
|
||||||
},
|
|
||||||
config: {
|
config: {
|
||||||
// 是否禁用onChange事件监听,naive ui组件库默认不需要监听onChange事件,否则会在控制台报错
|
|
||||||
disabledOnChangeListener: true,
|
|
||||||
// ant design vue组件库默认都是 v-model:value
|
// ant design vue组件库默认都是 v-model:value
|
||||||
baseModelPropName: 'value',
|
baseModelPropName: 'value',
|
||||||
// 一些组件是 v-model:checked 或者 v-model:fileList
|
// 一些组件是 v-model:checked 或者 v-model:fileList
|
||||||
@@ -144,30 +51,163 @@ setupVbenForm<FormComponentType>({
|
|||||||
// 输入项目必填国际化适配
|
// 输入项目必填国际化适配
|
||||||
required: (value, _params, ctx) => {
|
required: (value, _params, ctx) => {
|
||||||
if (value === undefined || value === null || value.length === 0) {
|
if (value === undefined || value === null || value.length === 0) {
|
||||||
return $t('formRules.required', [ctx.label]);
|
return $t('ui.formRules.required', [ctx.label]);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
// 选择项目必填国际化适配
|
// 选择项目必填国际化适配
|
||||||
selectRequired: (value, _params, ctx) => {
|
selectRequired: (value, _params, ctx) => {
|
||||||
if (value === undefined || value === null) {
|
if (value === undefined || value === null) {
|
||||||
return $t('formRules.selectRequired', [ctx.label]);
|
return $t('ui.formRules.selectRequired', [ctx.label]);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const useVbenForm = useForm<FormComponentType>;
|
const useVbenForm = useForm<ComponentType>;
|
||||||
|
|
||||||
export { useVbenForm, z };
|
export { useVbenForm, z };
|
||||||
|
export type VbenFormSchema = FormSchema<ComponentType>;
|
||||||
export type VbenFormSchema = FormSchema<FormComponentType>;
|
|
||||||
export type { VbenFormProps };
|
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 { 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 };
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
## 基础用法
|
## 基础用法
|
||||||
|
|
||||||
::: tip README
|
::: tip README
|
||||||
@@ -218,7 +258,7 @@ _注意_ 需要指定 `dependencies` 的 `triggerFields` 属性,设置由谁
|
|||||||
|
|
||||||
```vue
|
```vue
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useVbenForm } from '#/adapter';
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
|
||||||
// Form 为弹窗组件
|
// Form 为弹窗组件
|
||||||
// formApi 为弹窗的方法
|
// formApi 为弹窗的方法
|
||||||
@@ -271,6 +311,7 @@ useVbenForm 返回的第二个参数,是一个对象,包含了一些表单
|
|||||||
| collapsedRows | 折叠时保持的行数 | `number` | `1` |
|
| collapsedRows | 折叠时保持的行数 | `number` | `1` |
|
||||||
| commonConfig | 表单项的通用配置,每个配置都会传递到每个表单项,表单项可覆盖 | `FormCommonConfig` | - |
|
| commonConfig | 表单项的通用配置,每个配置都会传递到每个表单项,表单项可覆盖 | `FormCommonConfig` | - |
|
||||||
| schema | 表单项的每一项配置 | `FormSchema` | - |
|
| schema | 表单项的每一项配置 | `FormSchema` | - |
|
||||||
|
| submitOnEnter | 按下回车健时提交表单 | `boolean` | false |
|
||||||
|
|
||||||
### TS 类型说明
|
### TS 类型说明
|
||||||
|
|
||||||
@@ -435,7 +476,7 @@ rules的值可以是一个字符串,也可以是一个zod的schema。
|
|||||||
rules也支持 zod 的 schema,可以进行更复杂的校验,zod 的使用请查看 [zod文档](https://zod.dev/)。
|
rules也支持 zod 的 schema,可以进行更复杂的校验,zod 的使用请查看 [zod文档](https://zod.dev/)。
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { z } from '#/adapter';
|
import { z } from '#/adapter/form';
|
||||||
|
|
||||||
// 基础类型
|
// 基础类型
|
||||||
{
|
{
|
||||||
|
@@ -14,6 +14,12 @@ outline: deep
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
::: tip README
|
||||||
|
|
||||||
|
下方示例代码中的,存在一些国际化、主题色未适配问题,这些问题只在文档内会出现,实际使用并不会有这些问题,可忽略,不必纠结。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
## 基础用法
|
## 基础用法
|
||||||
|
|
||||||
使用 `useVbenModal` 创建最基础的模态框。
|
使用 `useVbenModal` 创建最基础的模态框。
|
||||||
@@ -53,7 +59,7 @@ Modal 内的内容一般业务中,会比较复杂,所以我们可以将 moda
|
|||||||
::: info 注意
|
::: info 注意
|
||||||
|
|
||||||
- `VbenModal` 组件对与参数的处理优先级是 `slot` > `props` > `state`(通过api更新的状态以及useVbenModal参数)。如果你已经传入了 `slot` 或者 `props`,那么 `setState` 将不会生效,这种情况下你可以通过 `slot` 或者 `props` 来更新状态。
|
- `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` | - |
|
| contentClass | modal内容区域的class | `string` | - |
|
||||||
| footerClass | modal底部区域的class | `string` | - |
|
| footerClass | modal底部区域的class | `string` | - |
|
||||||
| headerClass | modal顶部区域的class | `string` | - |
|
| headerClass | modal顶部区域的class | `string` | - |
|
||||||
|
| bordered | 是否显示border | `boolean` | `false` |
|
||||||
|
|
||||||
### Event
|
### Event
|
||||||
|
|
||||||
|
7
docs/src/components/common-ui/vben-vxe-table.md
Normal file
7
docs/src/components/common-ui/vben-vxe-table.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# Vben Vxe Table 表格
|
||||||
|
|
||||||
|
文档待补充,如果需要使用,可先行查看 vxe-table 文档和 示例代码,内部有部分注释。
|
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Button, message, Space } from 'ant-design-vue';
|
import { Button, message, Space } from 'ant-design-vue';
|
||||||
|
|
||||||
import { useVbenForm } from '#/adapter';
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
|
||||||
const [BaseForm, formApi] = useVbenForm({
|
const [BaseForm, formApi] = useVbenForm({
|
||||||
// 所有表单项共用,可单独在表单内覆盖
|
// 所有表单项共用,可单独在表单内覆盖
|
||||||
@@ -81,6 +81,97 @@ function handleClick(
|
|||||||
| 'updateSubmitButton',
|
| 'updateSubmitButton',
|
||||||
) {
|
) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
|
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: {
|
||||||
|
labelWidth: 150,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'resetDisabled': {
|
||||||
|
formApi.setState({ commonConfig: { disabled: false } });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'resetLabelWidth': {
|
||||||
|
formApi.setState({
|
||||||
|
commonConfig: {
|
||||||
|
labelWidth: 100,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'showAction': {
|
||||||
|
formApi.setState({ showDefaultActions: true });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'showResetButton': {
|
||||||
|
formApi.setState({ resetButtonOptions: { show: true } });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'showSubmitButton': {
|
||||||
|
formApi.setState({ submitButtonOptions: { show: true } });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'updateActionAlign': {
|
||||||
|
formApi.setState({
|
||||||
|
// 可以自行调整class
|
||||||
|
actionWrapperClass: 'text-center',
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'updateResetButton': {
|
||||||
|
formApi.setState({
|
||||||
|
resetButtonOptions: { disabled: true },
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'updateSchema': {
|
case 'updateSchema': {
|
||||||
formApi.updateSchema([
|
formApi.updateSchema([
|
||||||
{
|
{
|
||||||
@@ -106,103 +197,12 @@ function handleClick(
|
|||||||
message.success('字段 `fieldOptions` 下拉选项更新成功。');
|
message.success('字段 `fieldOptions` 下拉选项更新成功。');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'labelWidth': {
|
|
||||||
formApi.setState({
|
|
||||||
commonConfig: {
|
|
||||||
labelWidth: 150,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'resetLabelWidth': {
|
|
||||||
formApi.setState({
|
|
||||||
commonConfig: {
|
|
||||||
labelWidth: 100,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
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': {
|
case 'updateSubmitButton': {
|
||||||
formApi.setState({
|
formApi.setState({
|
||||||
submitButtonOptions: { loading: true },
|
submitButtonOptions: { loading: true },
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'updateActionAlign': {
|
|
||||||
formApi.setState({
|
|
||||||
// 可以自行调整class
|
|
||||||
actionWrapperClass: 'text-center',
|
|
||||||
});
|
|
||||||
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],
|
|
||||||
};
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'batchDeleteSchema': {
|
|
||||||
formApi.setState((prev) => {
|
|
||||||
const currentSchema = prev?.schema ?? [];
|
|
||||||
return {
|
|
||||||
schema: currentSchema.slice(0, -2),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
import { useVbenForm } from '#/adapter';
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
|
||||||
const [BaseForm] = useVbenForm({
|
const [BaseForm] = useVbenForm({
|
||||||
// 所有表单项共用,可单独在表单内覆盖
|
// 所有表单项共用,可单独在表单内覆盖
|
||||||
|
@@ -3,7 +3,7 @@ import { h } from 'vue';
|
|||||||
|
|
||||||
import { Input, message } from 'ant-design-vue';
|
import { Input, message } from 'ant-design-vue';
|
||||||
|
|
||||||
import { useVbenForm } from '#/adapter';
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
|
||||||
const [Form] = useVbenForm({
|
const [Form] = useVbenForm({
|
||||||
// 所有表单项共用,可单独在表单内覆盖
|
// 所有表单项共用,可单独在表单内覆盖
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
import { useVbenForm } from '#/adapter';
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
|
||||||
const [Form] = useVbenForm({
|
const [Form] = useVbenForm({
|
||||||
// 提交函数
|
// 提交函数
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
import { useVbenForm } from '#/adapter';
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
|
||||||
const [QueryForm] = useVbenForm({
|
const [QueryForm] = useVbenForm({
|
||||||
// 默认展开
|
// 默认展开
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
import { useVbenForm, z } from '#/adapter';
|
import { useVbenForm, z } from '#/adapter/form';
|
||||||
|
|
||||||
const [Form] = useVbenForm({
|
const [Form] = useVbenForm({
|
||||||
// 所有表单项共用,可单独在表单内覆盖
|
// 所有表单项共用,可单独在表单内覆盖
|
||||||
|
@@ -106,7 +106,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
icon: 'ic:baseline-view-in-ar',
|
icon: 'ic:baseline-view-in-ar',
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
order: 1000,
|
order: 1000,
|
||||||
title: $t('page.demos.title'),
|
title: $t('demos.title'),
|
||||||
},
|
},
|
||||||
name: 'Demos',
|
name: 'Demos',
|
||||||
path: '/demos',
|
path: '/demos',
|
||||||
@@ -116,7 +116,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
{
|
{
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'ic:round-menu',
|
icon: 'ic:round-menu',
|
||||||
title: $t('page.demos.nested.title'),
|
title: $t('demos.nested.title'),
|
||||||
},
|
},
|
||||||
name: 'NestedDemos',
|
name: 'NestedDemos',
|
||||||
path: '/demos/nested',
|
path: '/demos/nested',
|
||||||
@@ -129,7 +129,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
meta: {
|
meta: {
|
||||||
icon: 'ic:round-menu',
|
icon: 'ic:round-menu',
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
title: $t('page.demos.nested.menu1'),
|
title: $t('demos.nested.menu1'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -138,7 +138,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
meta: {
|
meta: {
|
||||||
icon: 'ic:round-menu',
|
icon: 'ic:round-menu',
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
title: $t('page.demos.nested.menu2'),
|
title: $t('demos.nested.menu2'),
|
||||||
},
|
},
|
||||||
redirect: '/demos/nested/menu2/menu2-1',
|
redirect: '/demos/nested/menu2/menu2-1',
|
||||||
children: [
|
children: [
|
||||||
@@ -149,7 +149,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
meta: {
|
meta: {
|
||||||
icon: 'ic:round-menu',
|
icon: 'ic:round-menu',
|
||||||
keepAlive: true,
|
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',
|
path: '/demos/nested/menu3',
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'ic:round-menu',
|
icon: 'ic:round-menu',
|
||||||
title: $t('page.demos.nested.menu3'),
|
title: $t('demos.nested.menu3'),
|
||||||
},
|
},
|
||||||
redirect: '/demos/nested/menu3/menu3-1',
|
redirect: '/demos/nested/menu3/menu3-1',
|
||||||
children: [
|
children: [
|
||||||
@@ -170,7 +170,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
meta: {
|
meta: {
|
||||||
icon: 'ic:round-menu',
|
icon: 'ic:round-menu',
|
||||||
keepAlive: true,
|
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',
|
path: 'menu3-2',
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'ic:round-menu',
|
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',
|
redirect: '/demos/nested/menu3/menu3-2/menu3-2-1',
|
||||||
children: [
|
children: [
|
||||||
@@ -190,7 +190,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
meta: {
|
meta: {
|
||||||
icon: 'ic:round-menu',
|
icon: 'ic:round-menu',
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
title: $t('page.demos.nested.menu3_2_1'),
|
title: $t('demos.nested.menu3_2_1'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -233,12 +233,12 @@ function createRequestClient(baseURL: string) {
|
|||||||
fulfilled: (response) => {
|
fulfilled: (response) => {
|
||||||
const { data: responseData, status } = response;
|
const { data: responseData, status } = response;
|
||||||
|
|
||||||
const { code, data, message: msg } = responseData;
|
const { code, data } = responseData;
|
||||||
|
|
||||||
if (status >= 200 && status < 400 && code === 0) {
|
if (status >= 200 && status < 400 && code === 0) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
throw new Error(`Error ${status}: ${msg}`);
|
throw Object.assign({}, response, { response });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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:
|
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
|
````ts
|
||||||
```json
|
```json
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# 友情链接
|
# 友情链接
|
||||||
|
|
||||||
如果您的网站是与 Vben Admin 相关的,或者也属于开源项目,欢迎联系我们,我们会将与您的网站加入交换友情链接。
|
如果您的网站是与 Vben Admin 相关的,也属于开源项目,欢迎联系我们,我们会将与您的网站加入交换友情链接。
|
||||||
|
|
||||||
## 交换方式
|
## 交换方式
|
||||||
|
|
||||||
|
@@ -105,7 +105,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
icon: 'ic:baseline-view-in-ar',
|
icon: 'ic:baseline-view-in-ar',
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
order: 1000,
|
order: 1000,
|
||||||
title: $t('page.demos.title'),
|
title: $t('demos.title'),
|
||||||
},
|
},
|
||||||
name: 'Demos',
|
name: 'Demos',
|
||||||
path: '/demos',
|
path: '/demos',
|
||||||
@@ -115,7 +115,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
{
|
{
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'ic:round-menu',
|
icon: 'ic:round-menu',
|
||||||
title: $t('page.demos.nested.title'),
|
title: $t('demos.nested.title'),
|
||||||
},
|
},
|
||||||
name: 'NestedDemos',
|
name: 'NestedDemos',
|
||||||
path: '/demos/nested',
|
path: '/demos/nested',
|
||||||
@@ -128,7 +128,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
meta: {
|
meta: {
|
||||||
icon: 'ic:round-menu',
|
icon: 'ic:round-menu',
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
title: $t('page.demos.nested.menu1'),
|
title: $t('demos.nested.menu1'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -137,7 +137,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
meta: {
|
meta: {
|
||||||
icon: 'ic:round-menu',
|
icon: 'ic:round-menu',
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
title: $t('page.demos.nested.menu2'),
|
title: $t('demos.nested.menu2'),
|
||||||
},
|
},
|
||||||
redirect: '/demos/nested/menu2/menu2-1',
|
redirect: '/demos/nested/menu2/menu2-1',
|
||||||
children: [
|
children: [
|
||||||
@@ -148,7 +148,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
meta: {
|
meta: {
|
||||||
icon: 'ic:round-menu',
|
icon: 'ic:round-menu',
|
||||||
keepAlive: true,
|
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',
|
path: '/demos/nested/menu3',
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'ic:round-menu',
|
icon: 'ic:round-menu',
|
||||||
title: $t('page.demos.nested.menu3'),
|
title: $t('demos.nested.menu3'),
|
||||||
},
|
},
|
||||||
redirect: '/demos/nested/menu3/menu3-1',
|
redirect: '/demos/nested/menu3/menu3-1',
|
||||||
children: [
|
children: [
|
||||||
@@ -169,7 +169,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
meta: {
|
meta: {
|
||||||
icon: 'ic:round-menu',
|
icon: 'ic:round-menu',
|
||||||
keepAlive: true,
|
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',
|
path: 'menu3-2',
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'ic:round-menu',
|
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',
|
redirect: '/demos/nested/menu3/menu3-2/menu3-2-1',
|
||||||
children: [
|
children: [
|
||||||
@@ -189,7 +189,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
meta: {
|
meta: {
|
||||||
icon: 'ic:round-menu',
|
icon: 'ic:round-menu',
|
||||||
keepAlive: true,
|
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;
|
order?: number;
|
||||||
|
/**
|
||||||
|
* 菜单所携带的参数
|
||||||
|
*/
|
||||||
|
query?: Recordable;
|
||||||
/**
|
/**
|
||||||
* 标题名称
|
* 标题名称
|
||||||
*/
|
*/
|
||||||
@@ -542,6 +546,15 @@ interface RouteMeta {
|
|||||||
|
|
||||||
用于配置页面的排序,用于路由到菜单排序。
|
用于配置页面的排序,用于路由到菜单排序。
|
||||||
|
|
||||||
|
_注意:_ 排序仅针对一级菜单有效,二级菜单的排序需要在对应的一级菜单中按代码顺序设置。
|
||||||
|
|
||||||
|
### query
|
||||||
|
|
||||||
|
- 类型:`Recordable`
|
||||||
|
- 默认值:`{}`
|
||||||
|
|
||||||
|
用于配置页面的菜单参数,会在菜单中传递给页面。
|
||||||
|
|
||||||
## 路由刷新
|
## 路由刷新
|
||||||
|
|
||||||
路由刷新方式如下:
|
路由刷新方式如下:
|
||||||
|
@@ -236,12 +236,12 @@ function createRequestClient(baseURL: string) {
|
|||||||
fulfilled: (response) => {
|
fulfilled: (response) => {
|
||||||
const { data: responseData, status } = response;
|
const { data: responseData, status } = response;
|
||||||
|
|
||||||
const { code, data, message: msg } = responseData;
|
const { code, data } = responseData;
|
||||||
|
|
||||||
if (status >= 200 && status < 400 && code === 0) {
|
if (status >= 200 && status < 400 && code === 0) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
throw new Error(`Error ${status}: ${msg}`);
|
throw Object.assign({}, response, { response });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -11,7 +11,7 @@ outline: deep
|
|||||||
|
|
||||||
## 前端访问控制
|
## 前端访问控制
|
||||||
|
|
||||||
**实现原理**: 在前端固定写死路由的权限,指定路由有哪些权限可以查看。只初始化通用的路由,需要权限才能访问的路由没有被加入路由表内。在登陆后或者其他方式获取用户角色后,通过角色去遍历路由表,获取该角色可以访问的路由表,生成路由表,再通过 `router.addRoute` 添加到路由实例,实现权限的过滤。
|
**实现原理**: 在前端固定写死路由的权限,指定路由有哪些权限可以查看。只初始化通用的路由,需要权限才能访问的路由没有被加入路由表内。在登录后或者其他方式获取用户角色后,通过角色去遍历路由表,获取该角色可以访问的路由表,生成路由表,再通过 `router.addRoute` 添加到路由实例,实现权限的过滤。
|
||||||
|
|
||||||
**缺点**: 权限相对不自由,如果后台改动角色,前台也需要跟着改动。适合角色较固定的系统
|
**缺点**: 权限相对不自由,如果后台改动角色,前台也需要跟着改动。适合角色较固定的系统
|
||||||
|
|
||||||
|
@@ -58,7 +58,7 @@ updateLocale('en-US');
|
|||||||
|
|
||||||
新增翻译文本,只需要在对应的应用内,找到 `src/locales/langs/`,新增对应的文本即可,例:
|
新增翻译文本,只需要在对应的应用内,找到 `src/locales/langs/`,新增对应的文本即可,例:
|
||||||
|
|
||||||
**src/locales/langs/zh-CN.ts**
|
**src/locales/langs/zh-CN/\*.json**
|
||||||
|
|
||||||
````ts
|
````ts
|
||||||
```json
|
```json
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/commitlint-config",
|
"name": "@vben/commitlint-config",
|
||||||
"version": "5.4.0-beta.1",
|
"version": "5.4.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
@@ -101,6 +101,7 @@ const customConfig: Linter.Config[] = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
// 不能引入@vben/*里面的包
|
// 不能引入@vben/*里面的包
|
||||||
files: [
|
files: [
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/stylelint-config",
|
"name": "@vben/stylelint-config",
|
||||||
"version": "5.4.0-beta.1",
|
"version": "5.4.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/node-utils",
|
"name": "@vben/node-utils",
|
||||||
"version": "5.4.0-beta.1",
|
"version": "5.4.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
@@ -11,13 +11,10 @@ import { findUpSync } from 'find-up';
|
|||||||
* @param cwd
|
* @param cwd
|
||||||
*/
|
*/
|
||||||
function findMonorepoRoot(cwd: string = process.cwd()) {
|
function findMonorepoRoot(cwd: string = process.cwd()) {
|
||||||
const lockFile = findUpSync(
|
const lockFile = findUpSync('pnpm-lock.yaml', {
|
||||||
['pnpm-lock.yaml', 'yarn.lock', 'package-lock.json'],
|
cwd,
|
||||||
{
|
type: 'file',
|
||||||
cwd,
|
});
|
||||||
type: 'file',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return dirname(lockFile || '');
|
return dirname(lockFile || '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/tailwind-config",
|
"name": "@vben/tailwind-config",
|
||||||
"version": "5.4.0-beta.1",
|
"version": "5.4.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user