From 24d14c2841ac13f00e3711aa3c9f52fd128887be Mon Sep 17 00:00:00 2001 From: Vben Date: Sun, 13 Oct 2024 18:33:43 +0800 Subject: [PATCH] refactor(adapter): separate form and component adapters so that components adapt to components other than the form (#4628) * refactor: global components can be customized * refactor: remove use Toast and reconstruct the form adapter --- README.md | 2 +- apps/web-antd/src/adapter/component/index.ts | 127 ++ apps/web-antd/src/adapter/form.ts | 99 +- apps/web-antd/src/bootstrap.ts | 4 + apps/web-antd/src/router/routes/core.ts | 1 + apps/web-ele/src/adapter/component/index.ts | 104 + apps/web-ele/src/adapter/form.ts | 76 +- apps/web-ele/src/bootstrap.ts | 3 + apps/web-ele/src/router/routes/core.ts | 1 + apps/web-naive/src/adapter/component/index.ts | 103 + apps/web-naive/src/adapter/form.ts | 78 +- apps/web-naive/src/bootstrap.ts | 3 + apps/web-naive/src/router/routes/core.ts | 1 + docs/.vitepress/config/zh.mts | 4 + docs/src/_env/adapter/component.ts | 127 ++ docs/src/_env/adapter/form.ts | 98 +- docs/src/commercial/community.md | 2 +- docs/src/components/common-ui/vben-drawer.md | 6 + docs/src/components/common-ui/vben-form.md | 240 +- docs/src/components/common-ui/vben-modal.md | 6 + .../components/common-ui/vben-vxe-table.md | 7 + docs/src/friend-links/index.md | 2 +- packages/@core/base/design/src/css/global.css | 1 - .../{dark/index.css => dark.css} | 0 .../{default/index.css => default.css} | 0 .../base/design/src/design-tokens/index.ts | 4 +- packages/@core/base/shared/build.config.ts | 1 + packages/@core/base/shared/package.json | 14 +- .../@core/base/shared/src/global-state.ts | 45 + .../@core/composables/src/use-sortable.ts | 7 - .../form-ui/src/components/form-actions.vue | 4 +- packages/@core/ui-kit/form-ui/src/config.ts | 9 +- packages/@core/ui-kit/form-ui/src/types.ts | 5 +- .../ui-kit/popup-ui/src/drawer/drawer.vue | 14 +- .../@core/ui-kit/popup-ui/src/modal/modal.vue | 14 +- .../@core/ui-kit/shadcn-ui/src/ui/index.ts | 1 - .../ui-kit/shadcn-ui/src/ui/toast/Toast.vue | 35 - .../shadcn-ui/src/ui/toast/ToastAction.vue | 29 - .../shadcn-ui/src/ui/toast/ToastClose.vue | 34 - .../src/ui/toast/ToastDescription.vue | 24 - .../shadcn-ui/src/ui/toast/ToastProvider.vue | 11 - .../shadcn-ui/src/ui/toast/ToastTitle.vue | 24 - .../shadcn-ui/src/ui/toast/ToastViewport.vue | 27 - .../ui-kit/shadcn-ui/src/ui/toast/Toaster.vue | 36 - .../ui-kit/shadcn-ui/src/ui/toast/index.ts | 11 - .../ui-kit/shadcn-ui/src/ui/toast/toast.ts | 27 - .../shadcn-ui/src/ui/toast/use-toast.ts | 168 -- .../effects/common-ui/src/components/index.ts | 2 + packages/effects/common-ui/src/index.ts | 1 - packages/effects/layouts/src/basic/layout.vue | 3 +- .../widgets/check-updates/check-updates.vue | 66 +- .../preferences/preferences-drawer.vue | 18 +- .../widgets/user-dropdown/user-dropdown.vue | 2 +- packages/locales/src/i18n.ts | 2 +- packages/locales/src/langs/en-US.json | 1 + packages/locales/src/langs/zh-CN.json | 1 + packages/stores/src/modules/tabbar.ts | 3 +- packages/stores/src/setup.ts | 1 - playground/src/adapter/component/index.ts | 127 ++ playground/src/adapter/form.ts | 101 +- playground/src/bootstrap.ts | 4 + playground/src/router/routes/core.ts | 1 + pnpm-lock.yaml | 1962 ++++++++--------- 63 files changed, 1798 insertions(+), 2136 deletions(-) create mode 100644 apps/web-antd/src/adapter/component/index.ts create mode 100644 apps/web-ele/src/adapter/component/index.ts create mode 100644 apps/web-naive/src/adapter/component/index.ts create mode 100644 docs/src/_env/adapter/component.ts create mode 100644 docs/src/components/common-ui/vben-vxe-table.md rename packages/@core/base/design/src/design-tokens/{dark/index.css => dark.css} (100%) rename packages/@core/base/design/src/design-tokens/{default/index.css => default.css} (100%) create mode 100644 packages/@core/base/shared/src/global-state.ts delete mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/toast/Toast.vue delete mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastAction.vue delete mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastClose.vue delete mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastDescription.vue delete mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastProvider.vue delete mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastTitle.vue delete mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastViewport.vue delete mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/toast/Toaster.vue delete mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/toast/index.ts delete mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/toast/toast.ts delete mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/toast/use-toast.ts create mode 100644 playground/src/adapter/component/index.ts diff --git a/README.md b/README.md index a65213ab..e84c8392 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ If you think this project is helpful to you, you can help the author buy a cup o ![donate](https://unpkg.com/@vbenjs/static-source@0.1.7/source/sponsor.png) -Paypal Me +Paypal Me ## Contributor diff --git a/apps/web-antd/src/adapter/component/index.ts b/apps/web-antd/src/adapter/component/index.ts new file mode 100644 index 00000000..12849dc3 --- /dev/null +++ b/apps/web-antd/src/adapter/component/index.ts @@ -0,0 +1,127 @@ +/** + * 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用 + * 可用于 vben-form、vben-modal、vben-drawer 等组件使用, + */ + +import type { BaseFormComponentType } from '@vben/common-ui'; + +import type { Component, SetupContext } from 'vue'; +import { h } from 'vue'; + +import { globalShareState } from '@vben/common-ui'; +import { $t } from '@vben/locales'; + +import { + AutoComplete, + Button, + Checkbox, + CheckboxGroup, + DatePicker, + Divider, + Input, + InputNumber, + InputPassword, + Mentions, + notification, + Radio, + RadioGroup, + RangePicker, + Rate, + Select, + Space, + Switch, + Textarea, + TimePicker, + TreeSelect, + Upload, +} from 'ant-design-vue'; + +const withDefaultPlaceholder = ( + component: T, + type: 'input' | 'select', +) => { + return (props: any, { attrs, slots }: Omit) => { + const placeholder = props?.placeholder || $t(`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> = { + // 如果你的组件体积比较大,可以使用异步加载 + // 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 }; diff --git a/apps/web-antd/src/adapter/form.ts b/apps/web-antd/src/adapter/form.ts index 48c981f7..9b95ac75 100644 --- a/apps/web-antd/src/adapter/form.ts +++ b/apps/web-antd/src/adapter/form.ts @@ -1,105 +1,14 @@ import type { - BaseFormComponentType, VbenFormSchema as FormSchema, VbenFormProps, } from '@vben/common-ui'; -import type { Component, SetupContext } from 'vue'; -import { h } from 'vue'; +import type { ComponentType } from './component'; import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; import { $t } from '@vben/locales'; -import { - AutoComplete, - Button, - Checkbox, - CheckboxGroup, - DatePicker, - Divider, - Input, - InputNumber, - InputPassword, - Mentions, - Radio, - RadioGroup, - RangePicker, - Rate, - Select, - Space, - Switch, - Textarea, - TimePicker, - TreeSelect, - Upload, -} from 'ant-design-vue'; - -// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明 -export type FormComponentType = - | 'AutoComplete' - | 'Checkbox' - | 'CheckboxGroup' - | 'DatePicker' - | 'Divider' - | 'Input' - | 'InputNumber' - | 'InputPassword' - | 'Mentions' - | 'Radio' - | 'RadioGroup' - | 'RangePicker' - | 'Rate' - | 'Select' - | 'Space' - | 'Switch' - | 'Textarea' - | 'TimePicker' - | 'TreeSelect' - | 'Upload' - | BaseFormComponentType; - -const withDefaultPlaceholder = ( - component: T, - type: 'input' | 'select', -) => { - return (props: any, { attrs, slots }: Omit) => { - const placeholder = props?.placeholder || $t(`placeholder.${type}`); - return h(component, { ...props, ...attrs, placeholder }, slots); - }; -}; - -// 初始化表单组件,并注册到form组件内部 -setupVbenForm({ - components: { - AutoComplete, - Checkbox, - CheckboxGroup, - DatePicker, - // 自定义默认的重置按钮 - DefaultResetActionButton: (props, { attrs, slots }) => { - return h(Button, { ...props, attrs, type: 'default' }, slots); - }, - // 自定义默认的提交按钮 - DefaultSubmitActionButton: (props, { attrs, slots }) => { - return h(Button, { ...props, attrs, type: 'primary' }, slots); - }, - Divider, - Input: withDefaultPlaceholder(Input, 'input'), - InputNumber: withDefaultPlaceholder(InputNumber, 'input'), - InputPassword: withDefaultPlaceholder(InputPassword, 'input'), - Mentions: withDefaultPlaceholder(Mentions, 'input'), - Radio, - RadioGroup, - RangePicker, - Rate, - Select: withDefaultPlaceholder(Select, 'select'), - Space, - Switch, - Textarea: withDefaultPlaceholder(Textarea, 'input'), - TimePicker, - TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'), - Upload, - }, +setupVbenForm({ config: { // ant design vue组件库默认都是 v-model:value baseModelPropName: 'value', @@ -130,9 +39,9 @@ setupVbenForm({ }, }); -const useVbenForm = useForm; +const useVbenForm = useForm; export { useVbenForm, z }; -export type VbenFormSchema = FormSchema; +export type VbenFormSchema = FormSchema; export type { VbenFormProps }; diff --git a/apps/web-antd/src/bootstrap.ts b/apps/web-antd/src/bootstrap.ts index 1be9bbe0..f88b41b1 100644 --- a/apps/web-antd/src/bootstrap.ts +++ b/apps/web-antd/src/bootstrap.ts @@ -7,10 +7,14 @@ import '@vben/styles/antd'; import { setupI18n } from '#/locales'; +import { initComponentAdapter } from './adapter/component'; import App from './app.vue'; import { router } from './router'; async function bootstrap(namespace: string) { + // 初始化组件适配器 + await initComponentAdapter(); + const app = createApp(App); // 国际化 i18n 配置 diff --git a/apps/web-antd/src/router/routes/core.ts b/apps/web-antd/src/router/routes/core.ts index f793a57e..d85de5a4 100644 --- a/apps/web-antd/src/router/routes/core.ts +++ b/apps/web-antd/src/router/routes/core.ts @@ -32,6 +32,7 @@ const coreRoutes: RouteRecordRaw[] = [ { component: AuthPageLayout, meta: { + hideInTab: true, title: 'Authentication', }, name: 'Authentication', diff --git a/apps/web-ele/src/adapter/component/index.ts b/apps/web-ele/src/adapter/component/index.ts new file mode 100644 index 00000000..1d415bc8 --- /dev/null +++ b/apps/web-ele/src/adapter/component/index.ts @@ -0,0 +1,104 @@ +/** + * 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用 + * 可用于 vben-form、vben-modal、vben-drawer 等组件使用, + */ + +import type { BaseFormComponentType } from '@vben/common-ui'; + +import type { Component, SetupContext } from 'vue'; +import { h } from 'vue'; + +import { globalShareState } from '@vben/common-ui'; +import { $t } from '@vben/locales'; + +import { + ElButton, + ElCheckbox, + ElCheckboxGroup, + ElDivider, + ElInput, + ElInputNumber, + ElNotification, + ElRadioGroup, + ElSelect, + ElSpace, + ElSwitch, + ElTimePicker, + ElTreeSelect, + ElUpload, +} from 'element-plus'; + +const withDefaultPlaceholder = ( + component: T, + type: 'input' | 'select', +) => { + return (props: any, { attrs, slots }: Omit) => { + const placeholder = props?.placeholder || $t(`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> = { + // 如果你的组件体积比较大,可以使用异步加载 + // 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 }; diff --git a/apps/web-ele/src/adapter/form.ts b/apps/web-ele/src/adapter/form.ts index 36e20033..832a6342 100644 --- a/apps/web-ele/src/adapter/form.ts +++ b/apps/web-ele/src/adapter/form.ts @@ -1,82 +1,14 @@ import type { - BaseFormComponentType, VbenFormSchema as FormSchema, VbenFormProps, } from '@vben/common-ui'; -import type { Component, SetupContext } from 'vue'; -import { h } from 'vue'; +import type { ComponentType } from './component'; import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; import { $t } from '@vben/locales'; -import { - ElButton, - ElCheckbox, - ElCheckboxGroup, - ElDivider, - ElInput, - ElInputNumber, - ElRadioGroup, - ElSelect, - ElSpace, - ElSwitch, - ElTimePicker, - ElTreeSelect, - ElUpload, -} from 'element-plus'; -// 业务表单组件适配 - -export type FormComponentType = - | 'Checkbox' - | 'CheckboxGroup' - | 'DatePicker' - | 'Divider' - | 'Input' - | 'InputNumber' - | 'RadioGroup' - | 'Select' - | 'Space' - | 'Switch' - | 'TimePicker' - | 'TreeSelect' - | 'Upload' - | BaseFormComponentType; - -const withDefaultPlaceholder = ( - component: T, - type: 'input' | 'select', -) => { - return (props: any, { attrs, slots }: Omit) => { - const placeholder = props?.placeholder || $t(`placeholder.${type}`); - return h(component, { ...props, ...attrs, placeholder }, slots); - }; -}; - -// 初始化表单组件,并注册到form组件内部 -setupVbenForm({ - components: { - Checkbox: ElCheckbox, - CheckboxGroup: ElCheckboxGroup, - // 自定义默认的重置按钮 - DefaultResetActionButton: (props, { attrs, slots }) => { - return h(ElButton, { ...props, attrs, type: 'info' }, slots); - }, - // 自定义默认的提交按钮 - DefaultSubmitActionButton: (props, { attrs, slots }) => { - return h(ElButton, { ...props, attrs, type: 'primary' }, slots); - }, - Divider: ElDivider, - Input: withDefaultPlaceholder(ElInput, 'input'), - InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'), - RadioGroup: ElRadioGroup, - Select: withDefaultPlaceholder(ElSelect, 'select'), - Space: ElSpace, - Switch: ElSwitch, - TimePicker: ElTimePicker, - TreeSelect: withDefaultPlaceholder(ElTreeSelect, 'select'), - Upload: ElUpload, - }, +setupVbenForm({ config: { modelPropNameMap: { Upload: 'fileList', @@ -98,9 +30,9 @@ setupVbenForm({ }, }); -const useVbenForm = useForm; +const useVbenForm = useForm; export { useVbenForm, z }; -export type VbenFormSchema = FormSchema; +export type VbenFormSchema = FormSchema; export type { VbenFormProps }; diff --git a/apps/web-ele/src/bootstrap.ts b/apps/web-ele/src/bootstrap.ts index 5494cfba..7311d665 100644 --- a/apps/web-ele/src/bootstrap.ts +++ b/apps/web-ele/src/bootstrap.ts @@ -7,10 +7,13 @@ import '@vben/styles/ele'; import { setupI18n } from '#/locales'; +import { initComponentAdapter } from './adapter/component'; import App from './app.vue'; import { router } from './router'; async function bootstrap(namespace: string) { + // 初始化组件适配器 + await initComponentAdapter(); const app = createApp(App); // 国际化 i18n 配置 diff --git a/apps/web-ele/src/router/routes/core.ts b/apps/web-ele/src/router/routes/core.ts index f793a57e..d85de5a4 100644 --- a/apps/web-ele/src/router/routes/core.ts +++ b/apps/web-ele/src/router/routes/core.ts @@ -32,6 +32,7 @@ const coreRoutes: RouteRecordRaw[] = [ { component: AuthPageLayout, meta: { + hideInTab: true, title: 'Authentication', }, name: 'Authentication', diff --git a/apps/web-naive/src/adapter/component/index.ts b/apps/web-naive/src/adapter/component/index.ts new file mode 100644 index 00000000..d2b5f192 --- /dev/null +++ b/apps/web-naive/src/adapter/component/index.ts @@ -0,0 +1,103 @@ +/** + * 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用 + * 可用于 vben-form、vben-modal、vben-drawer 等组件使用, + */ + +import type { BaseFormComponentType } from '@vben/common-ui'; + +import type { Component, SetupContext } from 'vue'; +import { h } from 'vue'; + +import { globalShareState } from '@vben/common-ui'; +import { $t } from '@vben/locales'; + +import { + NButton, + NCheckbox, + NCheckboxGroup, + NDatePicker, + NDivider, + NInput, + NInputNumber, + NRadioGroup, + NSelect, + NSpace, + NSwitch, + NTimePicker, + NTreeSelect, + NUpload, +} from 'naive-ui'; + +import { message } from '#/adapter/naive'; + +const withDefaultPlaceholder = ( + component: T, + type: 'input' | 'select', +) => { + return (props: any, { attrs, slots }: Omit) => { + const placeholder = props?.placeholder || $t(`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> = { + // 如果你的组件体积比较大,可以使用异步加载 + // 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 }; diff --git a/apps/web-naive/src/adapter/form.ts b/apps/web-naive/src/adapter/form.ts index f7142e78..511518cc 100644 --- a/apps/web-naive/src/adapter/form.ts +++ b/apps/web-naive/src/adapter/form.ts @@ -1,84 +1,14 @@ import type { - BaseFormComponentType, VbenFormSchema as FormSchema, VbenFormProps, } from '@vben/common-ui'; -import type { Component, SetupContext } from 'vue'; -import { h } from 'vue'; +import type { ComponentType } from './component'; import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; import { $t } from '@vben/locales'; -import { - NButton, - NCheckbox, - NCheckboxGroup, - NDatePicker, - NDivider, - NInput, - NInputNumber, - NRadioGroup, - NSelect, - NSpace, - NSwitch, - NTimePicker, - NTreeSelect, - NUpload, -} from 'naive-ui'; -// 业务表单组件适配 - -export type FormComponentType = - | 'Checkbox' - | 'CheckboxGroup' - | 'DatePicker' - | 'Divider' - | 'Input' - | 'InputNumber' - | 'RadioGroup' - | 'Select' - | 'Space' - | 'Switch' - | 'TimePicker' - | 'TreeSelect' - | 'Upload' - | BaseFormComponentType; - -const withDefaultPlaceholder = ( - component: T, - type: 'input' | 'select', -) => { - return (props: any, { attrs, slots }: Omit) => { - const placeholder = props?.placeholder || $t(`placeholder.${type}`); - return h(component, { ...props, ...attrs, placeholder }, slots); - }; -}; - -// 初始化表单组件,并注册到form组件内部 -setupVbenForm({ - components: { - Checkbox: NCheckbox, - CheckboxGroup: NCheckboxGroup, - DatePicker: NDatePicker, - // 自定义默认的重置按钮 - DefaultResetActionButton: (props, { attrs, slots }) => { - return h(NButton, { ...props, attrs, type: 'info' }, slots); - }, - // 自定义默认的提交按钮 - DefaultSubmitActionButton: (props, { attrs, slots }) => { - return h(NButton, { ...props, attrs, type: 'primary' }, slots); - }, - Divider: NDivider, - Input: withDefaultPlaceholder(NInput, 'input'), - InputNumber: withDefaultPlaceholder(NInputNumber, 'input'), - RadioGroup: NRadioGroup, - Select: withDefaultPlaceholder(NSelect, 'select'), - Space: NSpace, - Switch: NSwitch, - TimePicker: NTimePicker, - TreeSelect: withDefaultPlaceholder(NTreeSelect, 'select'), - Upload: NUpload, - }, +setupVbenForm({ config: { // naive-ui组件不接受onChang事件,所以需要禁用 disabledOnChangeListener: true, @@ -107,9 +37,9 @@ setupVbenForm({ }, }); -const useVbenForm = useForm; +const useVbenForm = useForm; export { useVbenForm, z }; -export type VbenFormSchema = FormSchema; +export type VbenFormSchema = FormSchema; export type { VbenFormProps }; diff --git a/apps/web-naive/src/bootstrap.ts b/apps/web-naive/src/bootstrap.ts index 6604dec2..13ab95cb 100644 --- a/apps/web-naive/src/bootstrap.ts +++ b/apps/web-naive/src/bootstrap.ts @@ -6,10 +6,13 @@ import '@vben/styles'; import { setupI18n } from '#/locales'; +import { initComponentAdapter } from './adapter/component'; import App from './app.vue'; import { router } from './router'; async function bootstrap(namespace: string) { + // 初始化组件适配器 + initComponentAdapter(); const app = createApp(App); // 国际化 i18n 配置 diff --git a/apps/web-naive/src/router/routes/core.ts b/apps/web-naive/src/router/routes/core.ts index f793a57e..d85de5a4 100644 --- a/apps/web-naive/src/router/routes/core.ts +++ b/apps/web-naive/src/router/routes/core.ts @@ -32,6 +32,7 @@ const coreRoutes: RouteRecordRaw[] = [ { component: AuthPageLayout, meta: { + hideInTab: true, title: 'Authentication', }, name: 'Authentication', diff --git a/docs/.vitepress/config/zh.mts b/docs/.vitepress/config/zh.mts index ff0f2256..5c8505ab 100644 --- a/docs/.vitepress/config/zh.mts +++ b/docs/.vitepress/config/zh.mts @@ -164,6 +164,10 @@ function sidebarComponents(): DefaultTheme.SidebarItem[] { link: 'common-ui/vben-form', text: 'Form 表单', }, + { + link: 'common-ui/vben-vxe-table', + text: 'Vxe Table 表格', + }, { link: 'common-ui/vben-count-to-animator', text: 'CountToAnimator 数字动画', diff --git a/docs/src/_env/adapter/component.ts b/docs/src/_env/adapter/component.ts new file mode 100644 index 00000000..12849dc3 --- /dev/null +++ b/docs/src/_env/adapter/component.ts @@ -0,0 +1,127 @@ +/** + * 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用 + * 可用于 vben-form、vben-modal、vben-drawer 等组件使用, + */ + +import type { BaseFormComponentType } from '@vben/common-ui'; + +import type { Component, SetupContext } from 'vue'; +import { h } from 'vue'; + +import { globalShareState } from '@vben/common-ui'; +import { $t } from '@vben/locales'; + +import { + AutoComplete, + Button, + Checkbox, + CheckboxGroup, + DatePicker, + Divider, + Input, + InputNumber, + InputPassword, + Mentions, + notification, + Radio, + RadioGroup, + RangePicker, + Rate, + Select, + Space, + Switch, + Textarea, + TimePicker, + TreeSelect, + Upload, +} from 'ant-design-vue'; + +const withDefaultPlaceholder = ( + component: T, + type: 'input' | 'select', +) => { + return (props: any, { attrs, slots }: Omit) => { + const placeholder = props?.placeholder || $t(`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> = { + // 如果你的组件体积比较大,可以使用异步加载 + // 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 }; diff --git a/docs/src/_env/adapter/form.ts b/docs/src/_env/adapter/form.ts index b55b9b0a..760cf7ce 100644 --- a/docs/src/_env/adapter/form.ts +++ b/docs/src/_env/adapter/form.ts @@ -1,99 +1,23 @@ import type { - BaseFormComponentType, VbenFormSchema as FormSchema, VbenFormProps, } from '@vben/common-ui'; -import { h } from 'vue'; +import type { ComponentType } from './component'; import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; import { $t } from '@vben/locales'; -import { - AutoComplete, - Button, - Checkbox, - CheckboxGroup, - DatePicker, - Divider, - Input, - InputNumber, - InputPassword, - Mentions, - Radio, - RadioGroup, - RangePicker, - Rate, - Select, - Space, - Switch, - Textarea, - TimePicker, - TreeSelect, - Upload, -} from 'ant-design-vue'; +import { initComponentAdapter } from './component'; -// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明 -export type FormComponentType = - | 'AutoComplete' - | 'Checkbox' - | 'CheckboxGroup' - | 'DatePicker' - | 'Divider' - | 'Input' - | 'InputNumber' - | 'InputPassword' - | 'Mentions' - | 'Radio' - | 'RadioGroup' - | 'RangePicker' - | 'Rate' - | 'Select' - | 'Space' - | 'Switch' - | 'Textarea' - | 'TimePicker' - | 'TreeSelect' - | 'Upload' - | BaseFormComponentType; - -// 初始化表单组件,并注册到form组件内部 -setupVbenForm({ - components: { - AutoComplete, - Checkbox, - CheckboxGroup, - DatePicker, - // 自定义默认的重置按钮 - DefaultResetActionButton: (props, { attrs, slots }) => { - return h(Button, { ...props, attrs, type: 'default' }, slots); - }, - // 自定义默认的提交按钮 - DefaultSubmitActionButton: (props, { attrs, slots }) => { - return h(Button, { ...props, attrs, type: 'primary' }, slots); - }, - Divider, - Input, - InputNumber, - InputPassword, - Mentions, - Radio, - RadioGroup, - RangePicker, - Rate, - Select, - Space, - Switch, - Textarea, - TimePicker, - TreeSelect, - Upload, - }, +initComponentAdapter(); +setupVbenForm({ config: { - // ant design vue组件库默认都是 v-model:value baseModelPropName: 'value', - - // 一些组件是 v-model:checked 或者 v-model:fileList + // naive-ui组件不接受onChang事件,所以需要禁用 + disabledOnChangeListener: true, + // naive-ui组件的空值为null,不能是undefined,否则重置表单时不生效 + emptyStateValue: null, modelPropNameMap: { Checkbox: 'checked', Radio: 'checked', @@ -102,14 +26,12 @@ setupVbenForm({ }, }, defineRules: { - // 输入项目必填国际化适配 required: (value, _params, ctx) => { if (value === undefined || value === null || value.length === 0) { return $t('formRules.required', [ctx.label]); } return true; }, - // 选择项目必填国际化适配 selectRequired: (value, _params, ctx) => { if (value === undefined || value === null) { return $t('formRules.selectRequired', [ctx.label]); @@ -119,9 +41,9 @@ setupVbenForm({ }, }); -const useVbenForm = useForm; +const useVbenForm = useForm; export { useVbenForm, z }; -export type VbenFormSchema = FormSchema; +export type VbenFormSchema = FormSchema; export type { VbenFormProps }; diff --git a/docs/src/commercial/community.md b/docs/src/commercial/community.md index b1033454..899d030d 100644 --- a/docs/src/commercial/community.md +++ b/docs/src/commercial/community.md @@ -3,7 +3,7 @@ 社区交流群主要是为了方便大家交流,提问,解答问题,分享经验等。偏自助方式,如果你有问题,可以通过以下方式加入社区交流群: - [QQ频道](https://pd.qq.com/s/16p8lvvob):推荐!!!主要提供问题解答,分享经验等。 -- QQ群:[1群](https://qm.qq.com/q/YacMHPYAMu)、[2群](https://qm.qq.com/q/ajVKZvFICk)、[3群](https://qm.qq.com/q/36zdwThP2E),[4群](https://qm.qq.com/q/sCzSlm3504),[老群](https://qm.qq.com/q/MEmHoCLbG0),主要使用者的交流群。 +- QQ群:[大群](https://qm.qq.com/q/MEmHoCLbG0),[1群](https://qm.qq.com/q/YacMHPYAMu)、[2群](https://qm.qq.com/q/ajVKZvFICk)、[3群](https://qm.qq.com/q/36zdwThP2E),[4群](https://qm.qq.com/q/sCzSlm3504),主要使用者的交流群。 - [Discord](https://discord.com/invite/VU62jTecad): 主要提供问题解答,分享经验等。 ::: tip diff --git a/docs/src/components/common-ui/vben-drawer.md b/docs/src/components/common-ui/vben-drawer.md index cbd92f5d..070e80ab 100644 --- a/docs/src/components/common-ui/vben-drawer.md +++ b/docs/src/components/common-ui/vben-drawer.md @@ -14,6 +14,12 @@ outline: deep ::: +::: tip README + +下方示例代码中的,存在一些国际化、主题色未适配问题,这些问题只在文档内会出现,实际使用并不会有这些问题,可忽略,不必纠结。 + +::: + ## 基础用法 使用 `useVbenDrawer` 创建最基础的模态框。 diff --git a/docs/src/components/common-ui/vben-form.md b/docs/src/components/common-ui/vben-form.md index ccb438c9..e9ca7666 100644 --- a/docs/src/components/common-ui/vben-form.md +++ b/docs/src/components/common-ui/vben-form.md @@ -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 import type { - BaseFormComponentType, VbenFormSchema as FormSchema, VbenFormProps, } from '@vben/common-ui'; -import type { Component, SetupContext } from 'vue'; -import { h } from 'vue'; +import type { ComponentType } from './component'; import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; import { $t } from '@vben/locales'; -import { - AutoComplete, - Button, - Checkbox, - CheckboxGroup, - DatePicker, - Divider, - Input, - InputNumber, - InputPassword, - Mentions, - Radio, - RadioGroup, - RangePicker, - Rate, - Select, - Space, - Switch, - Textarea, - TimePicker, - TreeSelect, - Upload, -} from 'ant-design-vue'; - -// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明 -export type FormComponentType = - | 'AutoComplete' - | 'Checkbox' - | 'CheckboxGroup' - | 'DatePicker' - | 'Divider' - | 'Input' - | 'InputNumber' - | 'InputPassword' - | 'Mentions' - | 'Radio' - | 'RadioGroup' - | 'RangePicker' - | 'Rate' - | 'Select' - | 'Space' - | 'Switch' - | 'Textarea' - | 'TimePicker' - | 'TreeSelect' - | 'Upload' - | BaseFormComponentType; - -const withDefaultPlaceholder = ( - component: T, - type: 'input' | 'select', -) => { - return (props: any, { attrs, slots }: Omit) => { - const placeholder = props?.placeholder || $t(`placeholder.${type}`); - return h(component, { ...props, ...attrs, placeholder }, slots); - }; -}; - -// 初始化表单组件,并注册到form组件内部 -setupVbenForm({ - components: { - AutoComplete, - Checkbox, - CheckboxGroup, - DatePicker, - // 自定义默认的重置按钮 - DefaultResetActionButton: (props, { attrs, slots }) => { - return h(Button, { ...props, attrs, type: 'default' }, slots); - }, - // 自定义默认的提交按钮 - DefaultSubmitActionButton: (props, { attrs, slots }) => { - return h(Button, { ...props, attrs, type: 'primary' }, slots); - }, - Divider, - Input: withDefaultPlaceholder(Input, 'input'), - InputNumber: withDefaultPlaceholder(InputNumber, 'input'), - InputPassword: withDefaultPlaceholder(InputPassword, 'input'), - Mentions: withDefaultPlaceholder(Mentions, 'input'), - Radio, - RadioGroup, - RangePicker, - Rate, - Select: withDefaultPlaceholder(Select, 'select'), - Space, - Switch, - Textarea: withDefaultPlaceholder(Textarea, 'input'), - TimePicker, - TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'), - Upload, - }, +setupVbenForm({ config: { - // 是否禁用onChange事件监听,naive ui组件库默认不需要监听onChange事件,否则会在控制台报错 - disabledOnChangeListener: true, // ant design vue组件库默认都是 v-model:value baseModelPropName: 'value', // 一些组件是 v-model:checked 或者 v-model:fileList @@ -158,16 +65,149 @@ setupVbenForm({ }, }); -const useVbenForm = useForm; +const useVbenForm = useForm; export { useVbenForm, z }; - -export type VbenFormSchema = FormSchema; +export type VbenFormSchema = FormSchema; 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 = ( + component: T, + type: 'input' | 'select', +) => { + return (props: any, { attrs, slots }: Omit) => { + const placeholder = props?.placeholder || $t(`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> = { + // 如果你的组件体积比较大,可以使用异步加载 + // 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 diff --git a/docs/src/components/common-ui/vben-modal.md b/docs/src/components/common-ui/vben-modal.md index 26389fc6..487e5e8a 100644 --- a/docs/src/components/common-ui/vben-modal.md +++ b/docs/src/components/common-ui/vben-modal.md @@ -14,6 +14,12 @@ outline: deep ::: +::: tip README + +下方示例代码中的,存在一些国际化、主题色未适配问题,这些问题只在文档内会出现,实际使用并不会有这些问题,可忽略,不必纠结。 + +::: + ## 基础用法 使用 `useVbenModal` 创建最基础的模态框。 diff --git a/docs/src/components/common-ui/vben-vxe-table.md b/docs/src/components/common-ui/vben-vxe-table.md new file mode 100644 index 00000000..e6f6a8f0 --- /dev/null +++ b/docs/src/components/common-ui/vben-vxe-table.md @@ -0,0 +1,7 @@ +--- +outline: deep +--- + +# Vben Vxe Table 表格 + +TODO diff --git a/docs/src/friend-links/index.md b/docs/src/friend-links/index.md index 59de06c0..84b61906 100644 --- a/docs/src/friend-links/index.md +++ b/docs/src/friend-links/index.md @@ -1,6 +1,6 @@ # 友情链接 -如果您的网站是与 Vben Admin 相关的,或者也属于开源项目,欢迎联系我们,我们会将与您的网站加入交换友情链接。 +如果您的网站是与 Vben Admin 相关的,也属于开源项目,欢迎联系我们,我们会将与您的网站加入交换友情链接。 ## 交换方式 diff --git a/packages/@core/base/design/src/css/global.css b/packages/@core/base/design/src/css/global.css index 8881d3df..2cee217f 100644 --- a/packages/@core/base/design/src/css/global.css +++ b/packages/@core/base/design/src/css/global.css @@ -94,7 +94,6 @@ } /* 只有非mac下才进行调整,mac下使用默认滚动条 */ - html:not([data-platform='macOs']) { ::-webkit-scrollbar { @apply h-[10px] w-[10px]; diff --git a/packages/@core/base/design/src/design-tokens/dark/index.css b/packages/@core/base/design/src/design-tokens/dark.css similarity index 100% rename from packages/@core/base/design/src/design-tokens/dark/index.css rename to packages/@core/base/design/src/design-tokens/dark.css diff --git a/packages/@core/base/design/src/design-tokens/default/index.css b/packages/@core/base/design/src/design-tokens/default.css similarity index 100% rename from packages/@core/base/design/src/design-tokens/default/index.css rename to packages/@core/base/design/src/design-tokens/default.css diff --git a/packages/@core/base/design/src/design-tokens/index.ts b/packages/@core/base/design/src/design-tokens/index.ts index 9d27b4e8..2d031d81 100644 --- a/packages/@core/base/design/src/design-tokens/index.ts +++ b/packages/@core/base/design/src/design-tokens/index.ts @@ -1,4 +1,4 @@ -import './default/index.css'; -import './dark/index.css'; +import './default.css'; +import './dark.css'; export {}; diff --git a/packages/@core/base/shared/build.config.ts b/packages/@core/base/shared/build.config.ts index 9406af74..98e2209f 100644 --- a/packages/@core/base/shared/build.config.ts +++ b/packages/@core/base/shared/build.config.ts @@ -9,5 +9,6 @@ export default defineBuildConfig({ 'src/utils/index', 'src/color/index', 'src/cache/index', + 'src/global-state', ], }); diff --git a/packages/@core/base/shared/package.json b/packages/@core/base/shared/package.json index 54f151b3..134986fb 100644 --- a/packages/@core/base/shared/package.json +++ b/packages/@core/base/shared/package.json @@ -11,7 +11,8 @@ "license": "MIT", "type": "module", "scripts": { - "build": "pnpm unbuild" + "build": "pnpm unbuild", + "stub": "pnpm unbuild --stub" }, "files": [ "dist" @@ -42,6 +43,11 @@ "types": "./src/store.ts", "development": "./src/store.ts", "default": "./dist/store.mjs" + }, + "./global-state": { + "types": "./dist/global-state.d.ts", + "development": "./src/global-state.ts", + "default": "./dist/global-state.mjs" } }, "publishConfig": { @@ -63,8 +69,12 @@ "default": "./dist/cache/index.mjs" }, "./store": { - "types": "./dist/store/index.d.ts", + "types": "./dist/store.d.ts", "default": "./dist/store.mjs" + }, + "./global-state": { + "types": "./dist/global-state.d.ts", + "default": "./dist/global-state.mjs" } } }, diff --git a/packages/@core/base/shared/src/global-state.ts b/packages/@core/base/shared/src/global-state.ts new file mode 100644 index 00000000..2d71356b --- /dev/null +++ b/packages/@core/base/shared/src/global-state.ts @@ -0,0 +1,45 @@ +/** + * 全局复用的变量、组件、配置,各个模块之间共享 + * 通过单例模式实现,单例必须注意不受请求影响,例如用户信息这些需要根据请求获取的。后续如果有ssr需求,也不会影响 + */ + +interface ComponentsState { + [key: string]: any; +} + +interface MessageState { + copyPreferencesSuccess?: (title: string, content?: string) => void; +} + +export interface IGlobalSharedState { + components: ComponentsState; + message: MessageState; +} + +class GlobalShareState { + #components: ComponentsState = {}; + #message: MessageState = {}; + + /** + * 定义框架内部各个场景的消息提示 + */ + public defineMessage({ copyPreferencesSuccess }: MessageState) { + this.#message = { + copyPreferencesSuccess, + }; + } + + public getComponents(): ComponentsState { + return this.#components; + } + + public getMessage(): MessageState { + return this.#message; + } + + public setComponents(value: ComponentsState) { + this.#components = value; + } +} + +export const globalShareState = new GlobalShareState(); diff --git a/packages/@core/composables/src/use-sortable.ts b/packages/@core/composables/src/use-sortable.ts index 6659db83..57f87a64 100644 --- a/packages/@core/composables/src/use-sortable.ts +++ b/packages/@core/composables/src/use-sortable.ts @@ -10,13 +10,6 @@ function useSortable( // @ts-expect-error - This is a dynamic import 'sortablejs/modular/sortable.complete.esm.js' ); - // const { AutoScroll } = await import( - // // @ts-expect-error - This is a dynamic import - // 'sortablejs/modular/sortable.core.esm.js' - // ); - - // Sortable?.default?.mount?.(AutoScroll); - const sortable = Sortable?.default?.create?.(sortableContainer, { animation: 300, delay: 400, diff --git a/packages/@core/ui-kit/form-ui/src/components/form-actions.vue b/packages/@core/ui-kit/form-ui/src/components/form-actions.vue index 0ac5b24e..3e08b471 100644 --- a/packages/@core/ui-kit/form-ui/src/components/form-actions.vue +++ b/packages/@core/ui-kit/form-ui/src/components/form-actions.vue @@ -84,7 +84,7 @@ watch( :style="queryFormStyle" > = { - DefaultResetActionButton: h(VbenButton, { size: 'sm', variant: 'outline' }), - DefaultSubmitActionButton: h(VbenButton, { size: 'sm', variant: 'default' }), + DefaultButton: h(VbenButton, { size: 'sm', variant: 'outline' }), + PrimaryButton: h(VbenButton, { size: 'sm', variant: 'default' }), VbenCheckbox, VbenInput, VbenInputPassword, @@ -41,7 +42,7 @@ export const COMPONENT_BIND_EVENT_MAP: Partial< export function setupVbenForm< T extends BaseFormComponentType = BaseFormComponentType, >(options: VbenFormAdapterOptions) { - const { components, config, defineRules } = options; + const { config, defineRules } = options; const { disabledOnChangeListener = false, emptyStateValue = undefined } = (config || {}) as FormCommonConfig; @@ -63,6 +64,8 @@ export function setupVbenForm< | Record | undefined; + const components = globalShareState.getComponents(); + for (const component of Object.keys(components)) { const key = component as BaseFormComponentType; COMPONENT_MAP[key] = components[component as never]; diff --git a/packages/@core/ui-kit/form-ui/src/types.ts b/packages/@core/ui-kit/form-ui/src/types.ts index 8f082545..3463b779 100644 --- a/packages/@core/ui-kit/form-ui/src/types.ts +++ b/packages/@core/ui-kit/form-ui/src/types.ts @@ -9,8 +9,8 @@ import type { Component, HtmlHTMLAttributes, Ref } from 'vue'; export type FormLayout = 'horizontal' | 'vertical'; export type BaseFormComponentType = - | 'DefaultResetActionButton' - | 'DefaultSubmitActionButton' + | 'DefaultButton' + | 'PrimaryButton' | 'VbenCheckbox' | 'VbenInput' | 'VbenInputPassword' @@ -341,7 +341,6 @@ export type ExtendedFormApi = { export interface VbenFormAdapterOptions< T extends BaseFormComponentType = BaseFormComponentType, > { - components: Partial>; config?: { baseModelPropName?: string; disabledOnChangeListener?: boolean; diff --git a/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue b/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue index 4f80f9e5..4aed25f0 100644 --- a/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue +++ b/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue @@ -23,6 +23,7 @@ import { VbenLoading, VisuallyHidden, } from '@vben-core/shadcn-ui'; +import { globalShareState } from '@vben-core/shared/global-state'; import { cn } from '@vben-core/shared/utils'; interface Props extends DrawerProps { @@ -33,6 +34,8 @@ const props = withDefaults(defineProps(), { drawerApi: undefined, }); +const components = globalShareState.getComponents(); + const id = useId(); provide('DISMISSABLE_DRAWER_ID', id); @@ -187,7 +190,8 @@ function handleFocusOutside(e: Event) { > - {{ cancelText || $t('cancel') }} - - + + {{ confirmText || $t('confirm') }} - + diff --git a/packages/@core/ui-kit/popup-ui/src/modal/modal.vue b/packages/@core/ui-kit/popup-ui/src/modal/modal.vue index 0413ef6d..5d409b94 100644 --- a/packages/@core/ui-kit/popup-ui/src/modal/modal.vue +++ b/packages/@core/ui-kit/popup-ui/src/modal/modal.vue @@ -22,6 +22,7 @@ import { VbenLoading, VisuallyHidden, } from '@vben-core/shadcn-ui'; +import { globalShareState } from '@vben-core/shared/global-state'; import { cn } from '@vben-core/shared/utils'; import { useModalDraggable } from './use-modal-draggable'; @@ -34,6 +35,8 @@ const props = withDefaults(defineProps(), { modalApi: undefined, }); +const components = globalShareState.getComponents(); + const contentRef = ref(); const wrapperRef = ref(); const dialogRef = ref(); @@ -256,7 +259,8 @@ function handleFocusOutside(e: Event) { > - {{ cancelText || $t('cancel') }} - - + + {{ confirmText || $t('confirm') }} - + diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/index.ts b/packages/@core/ui-kit/shadcn-ui/src/ui/index.ts index d9fca452..2711f942 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/ui/index.ts +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/index.ts @@ -23,7 +23,6 @@ export * from './sheet'; export * from './switch'; export * from './tabs'; export * from './textarea'; -export * from './toast'; export * from './toggle'; export * from './toggle-group'; export * from './tooltip'; diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/Toast.vue b/packages/@core/ui-kit/shadcn-ui/src/ui/toast/Toast.vue deleted file mode 100644 index a5b081ef..00000000 --- a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/Toast.vue +++ /dev/null @@ -1,35 +0,0 @@ - - - diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastAction.vue b/packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastAction.vue deleted file mode 100644 index f49edabe..00000000 --- a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastAction.vue +++ /dev/null @@ -1,29 +0,0 @@ - - - diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastClose.vue b/packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastClose.vue deleted file mode 100644 index 9112f95e..00000000 --- a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastClose.vue +++ /dev/null @@ -1,34 +0,0 @@ - - - diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastDescription.vue b/packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastDescription.vue deleted file mode 100644 index c74697f2..00000000 --- a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastDescription.vue +++ /dev/null @@ -1,24 +0,0 @@ - - - diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastProvider.vue b/packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastProvider.vue deleted file mode 100644 index 9e2194fc..00000000 --- a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastProvider.vue +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastTitle.vue b/packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastTitle.vue deleted file mode 100644 index 93a261c5..00000000 --- a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastTitle.vue +++ /dev/null @@ -1,24 +0,0 @@ - - - diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastViewport.vue b/packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastViewport.vue deleted file mode 100644 index d71527b6..00000000 --- a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/ToastViewport.vue +++ /dev/null @@ -1,27 +0,0 @@ - - - diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/Toaster.vue b/packages/@core/ui-kit/shadcn-ui/src/ui/toast/Toaster.vue deleted file mode 100644 index 52c4a244..00000000 --- a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/Toaster.vue +++ /dev/null @@ -1,36 +0,0 @@ - - - diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/index.ts b/packages/@core/ui-kit/shadcn-ui/src/ui/toast/index.ts deleted file mode 100644 index 151e820a..00000000 --- a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -export * from './toast'; -export { default as Toast } from './Toast.vue'; -export { default as ToastAction } from './ToastAction.vue'; -export { default as ToastClose } from './ToastClose.vue'; -export { default as ToastDescription } from './ToastDescription.vue'; -export { default as Toaster } from './Toaster.vue'; -export { default as ToastProvider } from './ToastProvider.vue'; -export { default as ToastTitle } from './ToastTitle.vue'; -export { default as ToastViewport } from './ToastViewport.vue'; - -export { toast, useToast } from './use-toast'; diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/toast.ts b/packages/@core/ui-kit/shadcn-ui/src/ui/toast/toast.ts deleted file mode 100644 index 92f9737b..00000000 --- a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/toast.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { ToastRootProps } from 'radix-vue'; - -import { cva, type VariantProps } from 'class-variance-authority'; - -export const toastVariants = cva( - 'group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border border-border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full', - { - defaultVariants: { - variant: 'default', - }, - variants: { - variant: { - default: 'border bg-background border-border text-foreground', - destructive: - 'destructive group border-destructive bg-destructive text-destructive-foreground', - }, - }, - }, -); - -type ToastVariants = VariantProps; - -export interface ToastProps extends ToastRootProps { - class?: any; - onOpenChange?: ((value: boolean) => void) | undefined; - variant?: ToastVariants['variant']; -} diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/use-toast.ts b/packages/@core/ui-kit/shadcn-ui/src/ui/toast/use-toast.ts deleted file mode 100644 index 96ff92e4..00000000 --- a/packages/@core/ui-kit/shadcn-ui/src/ui/toast/use-toast.ts +++ /dev/null @@ -1,168 +0,0 @@ -import type { ToastProps } from './toast'; - -import { computed, ref } from 'vue'; -import type { Component, VNode } from 'vue'; - -const TOAST_LIMIT = 1; -const TOAST_REMOVE_DELAY = 1_000_000; - -export type StringOrVNode = (() => VNode) | string | VNode; - -type ToasterToast = { - action?: Component; - description?: StringOrVNode; - id: string; - title?: string; -} & ToastProps; - -const actionTypes = { - ADD_TOAST: 'ADD_TOAST', - DISMISS_TOAST: 'DISMISS_TOAST', - REMOVE_TOAST: 'REMOVE_TOAST', - UPDATE_TOAST: 'UPDATE_TOAST', -} as const; - -let count = 0; - -function genId() { - count = (count + 1) % Number.MAX_VALUE; - return count.toString(); -} - -type ActionType = typeof actionTypes; - -type Action = - | { - toast: Partial; - type: ActionType['UPDATE_TOAST']; - } - | { - toast: ToasterToast; - type: ActionType['ADD_TOAST']; - } - | { - toastId?: ToasterToast['id']; - type: ActionType['DISMISS_TOAST']; - } - | { - toastId?: ToasterToast['id']; - type: ActionType['REMOVE_TOAST']; - }; - -interface State { - toasts: ToasterToast[]; -} - -const toastTimeouts = new Map>(); - -function addToRemoveQueue(toastId: string) { - if (toastTimeouts.has(toastId)) return; - - const timeout = setTimeout(() => { - toastTimeouts.delete(toastId); - dispatch({ - toastId, - type: actionTypes.REMOVE_TOAST, - }); - }, TOAST_REMOVE_DELAY); - - toastTimeouts.set(toastId, timeout); -} - -const state = ref({ - toasts: [], -}); - -function dispatch(action: Action) { - switch (action.type) { - case actionTypes.ADD_TOAST: { - state.value.toasts = [action.toast, ...state.value.toasts].slice( - 0, - TOAST_LIMIT, - ); - break; - } - - case actionTypes.UPDATE_TOAST: { - state.value.toasts = state.value.toasts.map((t) => - t.id === action.toast.id ? { ...t, ...action.toast } : t, - ); - break; - } - - case actionTypes.DISMISS_TOAST: { - const { toastId } = action; - - if (toastId) { - addToRemoveQueue(toastId); - } else { - state.value.toasts.forEach((toast) => { - addToRemoveQueue(toast.id); - }); - } - - state.value.toasts = state.value.toasts.map((t) => - t.id === toastId || toastId === undefined - ? { - ...t, - open: false, - } - : t, - ); - break; - } - - case actionTypes.REMOVE_TOAST: { - state.value.toasts = - action.toastId === undefined - ? [] - : state.value.toasts.filter((t) => t.id !== action.toastId); - - break; - } - } -} - -function useToast() { - return { - dismiss: (toastId?: string) => - dispatch({ toastId, type: actionTypes.DISMISS_TOAST }), - toast, - toasts: computed(() => state.value.toasts), - }; -} - -type Toast = Omit; - -function toast(props: Toast) { - const id = genId(); - - const update = (props: ToasterToast) => - dispatch({ - toast: { ...props, id }, - type: actionTypes.UPDATE_TOAST, - }); - - const dismiss = () => - dispatch({ toastId: id, type: actionTypes.DISMISS_TOAST }); - - dispatch({ - toast: { - ...props, - id, - onOpenChange: (open: boolean) => { - if (!open) dismiss(); - }, - open: true, - }, - type: actionTypes.ADD_TOAST, - }); - - return { - dismiss, - id, - update, - }; -} - -export { toast, useToast }; diff --git a/packages/effects/common-ui/src/components/index.ts b/packages/effects/common-ui/src/components/index.ts index fc5825e2..12d1cace 100644 --- a/packages/effects/common-ui/src/components/index.ts +++ b/packages/effects/common-ui/src/components/index.ts @@ -13,3 +13,5 @@ export { VbenPinInput, VbenSpinner, } from '@vben-core/shadcn-ui'; + +export { globalShareState } from '@vben-core/shared/global-state'; diff --git a/packages/effects/common-ui/src/index.ts b/packages/effects/common-ui/src/index.ts index 050c0829..27bd7186 100644 --- a/packages/effects/common-ui/src/index.ts +++ b/packages/effects/common-ui/src/index.ts @@ -1,3 +1,2 @@ export * from './components'; export * from './ui'; -export { useToast } from '@vben-core/shadcn-ui'; diff --git a/packages/effects/layouts/src/basic/layout.vue b/packages/effects/layouts/src/basic/layout.vue index 8b9933c5..0305eed4 100644 --- a/packages/effects/layouts/src/basic/layout.vue +++ b/packages/effects/layouts/src/basic/layout.vue @@ -13,7 +13,7 @@ import { import { useLockStore } from '@vben/stores'; import { deepToRaw, mapTree } from '@vben/utils'; import { VbenAdminLayout } from '@vben-core/layout-ui'; -import { Toaster, VbenBackTop, VbenLogo } from '@vben-core/shadcn-ui'; +import { VbenBackTop, VbenLogo } from '@vben-core/shadcn-ui'; import { Breadcrumb, CheckUpdates, Preferences } from '../widgets'; import { LayoutContent, LayoutContentSpinner } from './content'; @@ -312,7 +312,6 @@ const headerSlots = computed(() => {