From ba9f352496dae21130b4eb63c61bf937ca1713c4 Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 27 Dec 2024 14:08:23 +0800 Subject: [PATCH] =?UTF-8?q?fix(vxe-table):=20=E4=BF=AE=E5=A4=8D=E5=8F=91?= =?UTF-8?q?=E5=B8=83=E5=90=8Evxe-table=E6=97=A0=E6=B3=95=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app-antd/src/adapter/component/index.ts | 5 +- apps/vben5/apps/app-antd/src/adapter/form.ts | 47 --- .../apps/app-antd/src/adapter/vxe-table.ts | 71 ---- apps/vben5/package.json | 1 + .../components/audit-logs/AuditLogTable.vue | 2 + .../@abp/notifications/src/types/index.ts | 1 + .../notifications/src/types/notifications.ts | 84 ++++ apps/vben5/packages/@abp/ui/package.json | 32 +- .../@abp/ui/src/adapter/component/index.ts | 3 + .../packages/@abp/ui/src/adapter/vxe-table.ts | 119 +++--- .../packages/@abp/ui/src/components/index.ts | 1 + .../components/properties/PropertyTable.vue | 6 +- .../@abp/ui/src/components/vxe-table/api.ts | 127 ++++++ .../ui/src/components/vxe-table/extends.ts | 82 ++++ .../@abp/ui/src/components/vxe-table/index.ts | 5 + .../@abp/ui/src/components/vxe-table/init.ts | 131 ++++++ .../ui/src/components/vxe-table/style.css | 104 +++++ .../@abp/ui/src/components/vxe-table/types.ts | 76 ++++ .../src/components/vxe-table/use-vxe-grid.ts | 45 ++ .../src/components/vxe-table/use-vxe-grid.vue | 394 ++++++++++++++++++ apps/vben5/packages/@abp/ui/src/index.ts | 1 - 21 files changed, 1138 insertions(+), 199 deletions(-) delete mode 100644 apps/vben5/apps/app-antd/src/adapter/form.ts delete mode 100644 apps/vben5/apps/app-antd/src/adapter/vxe-table.ts create mode 100644 apps/vben5/packages/@abp/notifications/src/types/notifications.ts create mode 100644 apps/vben5/packages/@abp/ui/src/components/vxe-table/api.ts create mode 100644 apps/vben5/packages/@abp/ui/src/components/vxe-table/extends.ts create mode 100644 apps/vben5/packages/@abp/ui/src/components/vxe-table/index.ts create mode 100644 apps/vben5/packages/@abp/ui/src/components/vxe-table/init.ts create mode 100644 apps/vben5/packages/@abp/ui/src/components/vxe-table/style.css create mode 100644 apps/vben5/packages/@abp/ui/src/components/vxe-table/types.ts create mode 100644 apps/vben5/packages/@abp/ui/src/components/vxe-table/use-vxe-grid.ts create mode 100644 apps/vben5/packages/@abp/ui/src/components/vxe-table/use-vxe-grid.vue diff --git a/apps/vben5/apps/app-antd/src/adapter/component/index.ts b/apps/vben5/apps/app-antd/src/adapter/component/index.ts index a43a8280a..818acdd0d 100644 --- a/apps/vben5/apps/app-antd/src/adapter/component/index.ts +++ b/apps/vben5/apps/app-antd/src/adapter/component/index.ts @@ -21,6 +21,7 @@ import { Input, InputNumber, InputPassword, + InputSearch, Mentions, notification, Radio, @@ -60,6 +61,7 @@ export type ComponentType = | 'Input' | 'InputNumber' | 'InputPassword' + | 'InputSearch' | 'Mentions' | 'PrimaryButton' | 'Radio' @@ -89,8 +91,8 @@ async function initComponentAdapter() { ...attrs, component: Select, loadingSlot: 'suffixIcon', - visibleEvent: 'onDropdownVisibleChange', modelPropName: 'value', + visibleEvent: 'onDropdownVisibleChange', }, slots, ); @@ -131,6 +133,7 @@ async function initComponentAdapter() { Input: withDefaultPlaceholder(Input, 'input'), InputNumber: withDefaultPlaceholder(InputNumber, 'input'), InputPassword: withDefaultPlaceholder(InputPassword, 'input'), + InputSearch: withDefaultPlaceholder(InputSearch, 'input'), Mentions: withDefaultPlaceholder(Mentions, 'input'), // 自定义主要按钮 PrimaryButton: (props, { attrs, slots }) => { diff --git a/apps/vben5/apps/app-antd/src/adapter/form.ts b/apps/vben5/apps/app-antd/src/adapter/form.ts deleted file mode 100644 index 65ff793b6..000000000 --- a/apps/vben5/apps/app-antd/src/adapter/form.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { - VbenFormSchema as FormSchema, - VbenFormProps, -} from '@vben/common-ui'; - -import type { ComponentType } from './component'; - -import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; -import { $t } from '@vben/locales'; - -setupVbenForm({ - config: { - // ant design vue组件库默认都是 v-model:value - baseModelPropName: 'value', - - // 一些组件是 v-model:checked 或者 v-model:fileList - modelPropNameMap: { - Checkbox: 'checked', - Radio: 'checked', - Switch: 'checked', - Upload: 'fileList', - }, - }, - defineRules: { - // 输入项目必填国际化适配 - required: (value, _params, ctx) => { - if (value === undefined || value === null || value.length === 0) { - return $t('ui.formRules.required', [ctx.label]); - } - return true; - }, - // 选择项目必填国际化适配 - selectRequired: (value, _params, ctx) => { - if (value === undefined || value === null) { - return $t('ui.formRules.selectRequired', [ctx.label]); - } - return true; - }, - }, -}); - -const useVbenForm = useForm; - -export { useVbenForm, z }; - -export type VbenFormSchema = FormSchema; -export type { VbenFormProps }; diff --git a/apps/vben5/apps/app-antd/src/adapter/vxe-table.ts b/apps/vben5/apps/app-antd/src/adapter/vxe-table.ts deleted file mode 100644 index d27910397..000000000 --- a/apps/vben5/apps/app-antd/src/adapter/vxe-table.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { h } from 'vue'; - -import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table'; - -import { Button, Image } from 'ant-design-vue'; - -import { useVbenForm } from './form'; - -setupVbenVxeTable({ - configVxeTable: (vxeUI) => { - vxeUI.setConfig({ - grid: { - align: 'center', - border: false, - columnConfig: { - resizable: true, - }, - minHeight: 180, - formConfig: { - // 全局禁用vxe-table的表单配置,使用formOptions - enabled: false, - }, - pagerConfig: { - pageSize: 10, - pageSizes: [10, 15, 25, 50, 100], - }, - proxyConfig: { - autoLoad: true, - response: { - result: 'items', - total: 'total', - list: 'items', - }, - showActiveMsg: true, - showResponseMsg: false, - }, - round: true, - showOverflow: true, - size: 'small', - }, - }); - - // 表格配置项可以用 cellRender: { name: 'CellImage' }, - vxeUI.renderer.add('CellImage', { - renderTableDefault(_renderOpts, params) { - const { column, row } = params; - return h(Image, { src: row[column.field] }); - }, - }); - - // 表格配置项可以用 cellRender: { name: 'CellLink' }, - vxeUI.renderer.add('CellLink', { - renderTableDefault(renderOpts) { - const { props } = renderOpts; - return h( - Button, - { size: 'small', type: 'link' }, - { default: () => props?.text }, - ); - }, - }); - - // 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化 - // vxeUI.formats.add - }, - useVbenForm, -}); - -export { useVbenVxeGrid }; - -export type * from '@vben/plugins/vxe-table'; diff --git a/apps/vben5/package.json b/apps/vben5/package.json index f5a830eb7..97edd5ed5 100644 --- a/apps/vben5/package.json +++ b/apps/vben5/package.json @@ -27,6 +27,7 @@ "scripts": { "build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build", "build:analyze": "turbo build:analyze", + "build:app": "pnpm run build --filter=@vben/app-antd", "build:antd": "pnpm run build --filter=@vben/web-antd", "build:docker": "./scripts/deploy/build-local-docker-image.sh", "build:docs": "pnpm run build --filter=@vben/docs", diff --git a/apps/vben5/packages/@abp/auditing/src/components/audit-logs/AuditLogTable.vue b/apps/vben5/packages/@abp/auditing/src/components/audit-logs/AuditLogTable.vue index fd5abab32..a343888a0 100644 --- a/apps/vben5/packages/@abp/auditing/src/components/audit-logs/AuditLogTable.vue +++ b/apps/vben5/packages/@abp/auditing/src/components/audit-logs/AuditLogTable.vue @@ -235,11 +235,13 @@ const gridOptions: VxeGridProps = { const gridEvents: VxeGridListeners = { sortChange: onSort, }; + const [Grid, gridApi] = useVbenVxeGrid({ formOptions, gridEvents, gridOptions, }); + const { getHttpMethodColor, getHttpStatusCodeColor } = useAuditlogs(); const [AuditLogDrawer, logDrawerApi] = useVbenDrawer({ connectedComponent: defineAsyncComponent( diff --git a/apps/vben5/packages/@abp/notifications/src/types/index.ts b/apps/vben5/packages/@abp/notifications/src/types/index.ts index e69de29bb..9ea5ce778 100644 --- a/apps/vben5/packages/@abp/notifications/src/types/index.ts +++ b/apps/vben5/packages/@abp/notifications/src/types/index.ts @@ -0,0 +1 @@ +export * from './notifications'; diff --git a/apps/vben5/packages/@abp/notifications/src/types/notifications.ts b/apps/vben5/packages/@abp/notifications/src/types/notifications.ts new file mode 100644 index 000000000..bf780d040 --- /dev/null +++ b/apps/vben5/packages/@abp/notifications/src/types/notifications.ts @@ -0,0 +1,84 @@ +import type { Dictionary } from '@abp/core'; + +export enum NotificationLifetime { + OnlyOne = 1, + Persistent = 0, +} + +export enum NotificationType { + Application = 0, + ServiceCallback = 30, + System = 10, + User = 20, +} + +export enum NotificationContentType { + Html = 1, + Json = 3, + Markdown = 2, + Text = 0, +} + +export enum NotificationSeverity { + Error = 30, + Fatal = 40, + Info = 10, + Success = 0, + Warn = 20, +} + +export enum NotificationReadState { + Read = 0, + UnRead = 1, +} + +interface NotificationData { + extraProperties: { [key: string]: any }; + type: string; +} + +interface UserIdentifier { + userId: string; + userName?: string; +} + +interface NotificationSendInput { + culture?: string; + data: Dictionary; + name: string; + severity?: NotificationSeverity; + toUsers?: UserIdentifier[]; +} + +interface NotificationInfo { + contentType: NotificationContentType; + creationTime: Date; + data: NotificationData; + id: string; + lifetime: NotificationLifetime; + name: string; + severity: NotificationSeverity; + type: NotificationType; +} + +interface NotificationDto { + description: string; + displayName: string; + lifetime: NotificationLifetime; + name: string; + type: NotificationType; +} + +interface NotificationGroupDto { + displayName: string; + name: string; + notifications: NotificationDto[]; +} + +export type { + NotificationData, + NotificationDto, + NotificationGroupDto, + NotificationInfo, + NotificationSendInput, +}; diff --git a/apps/vben5/packages/@abp/ui/package.json b/apps/vben5/packages/@abp/ui/package.json index 5d4e72c8a..24d77e978 100644 --- a/apps/vben5/packages/@abp/ui/package.json +++ b/apps/vben5/packages/@abp/ui/package.json @@ -10,43 +10,35 @@ }, "license": "MIT", "type": "module", - "scripts": { - "build": "pnpm vite build", - "prepublishOnly": "npm run build" - }, - "files": [ - "dist", - "src" + "sideEffects": [ + "**/*.css" ], - "main": "./dist/index.mjs", - "module": "./dist/index.mjs", "exports": { ".": { "types": "./src/index.ts", - "development": "./src/index.ts", - "default": "./dist/index.mjs" - } - }, - "publishConfig": { - "exports": { - ".": { - "default": "./dist/index.mjs" - } + "default": "./src/index.ts" } }, "dependencies": { "@abp/core": "workspace:*", + "@vben-core/form-ui": "workspace:*", "@vben-core/preferences": "workspace:*", + "@vben-core/shadcn-ui": "workspace:*", "@vben-core/shared": "workspace:*", "@vben-core/typings": "workspace:*", "@vben/common-ui": "workspace:*", + "@vben/hooks": "workspace:*", "@vben/icons": "workspace:*", "@vben/locales": "workspace:*", - "@vben/plugins": "workspace:*", + "@vben/preferences": "workspace:*", + "@vben/types": "workspace:*", + "@vben/utils": "workspace:*", "@vueuse/core": "catalog:", "ant-design-vue": "catalog:", "codemirror": "catalog:", - "vue": "catalog:*" + "vue": "catalog:*", + "vxe-pc-ui": "catalog:", + "vxe-table": "catalog:" }, "devDependencies": { "@types/codemirror": "catalog:" diff --git a/apps/vben5/packages/@abp/ui/src/adapter/component/index.ts b/apps/vben5/packages/@abp/ui/src/adapter/component/index.ts index 939141ce7..818acdd0d 100644 --- a/apps/vben5/packages/@abp/ui/src/adapter/component/index.ts +++ b/apps/vben5/packages/@abp/ui/src/adapter/component/index.ts @@ -21,6 +21,7 @@ import { Input, InputNumber, InputPassword, + InputSearch, Mentions, notification, Radio, @@ -60,6 +61,7 @@ export type ComponentType = | 'Input' | 'InputNumber' | 'InputPassword' + | 'InputSearch' | 'Mentions' | 'PrimaryButton' | 'Radio' @@ -131,6 +133,7 @@ async function initComponentAdapter() { Input: withDefaultPlaceholder(Input, 'input'), InputNumber: withDefaultPlaceholder(InputNumber, 'input'), InputPassword: withDefaultPlaceholder(InputPassword, 'input'), + InputSearch: withDefaultPlaceholder(InputSearch, 'input'), Mentions: withDefaultPlaceholder(Mentions, 'input'), // 自定义主要按钮 PrimaryButton: (props, { attrs, slots }) => { diff --git a/apps/vben5/packages/@abp/ui/src/adapter/vxe-table.ts b/apps/vben5/packages/@abp/ui/src/adapter/vxe-table.ts index 48dd893bb..5e694916c 100644 --- a/apps/vben5/packages/@abp/ui/src/adapter/vxe-table.ts +++ b/apps/vben5/packages/@abp/ui/src/adapter/vxe-table.ts @@ -1,69 +1,76 @@ -import { h } from 'vue'; +import type { VxeGridProps } from '../components/vxe-table/types'; -import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table'; +import { h } from 'vue'; import { Button, Image } from 'ant-design-vue'; +import { setupVbenVxeTable } from '../components/vxe-table'; +import { useVbenVxeGrid as useVxeGrid } from '../components/vxe-table/use-vxe-grid'; import { useVbenForm } from './form'; -setupVbenVxeTable({ - configVxeTable: (vxeUI) => { - vxeUI.setConfig({ - grid: { - align: 'center', - border: false, - columnConfig: { - resizable: true, - }, - formConfig: { - // 全局禁用vxe-table的表单配置,使用formOptions - enabled: false, - }, - minHeight: 180, - pagerConfig: { - pageSize: 10, - pageSizes: [10, 25, 50, 100], - }, - proxyConfig: { - autoLoad: true, - response: { - result: 'items', - total: 'total', - list: 'items', +function useVbenVxeGrid(options: VxeGridProps) { + setupVbenVxeTable({ + configVxeTable: (vxeUI) => { + vxeUI.setConfig({ + grid: { + align: 'center', + border: false, + columnConfig: { + resizable: true, }, - showActiveMsg: true, - showResponseMsg: false, + formConfig: { + // 全局禁用vxe-table的表单配置,使用formOptions + enabled: false, + }, + minHeight: 180, + pagerConfig: { + pageSize: 10, + pageSizes: [10, 25, 50, 100], + }, + proxyConfig: { + autoLoad: true, + response: { + result: 'items', + total: 'total', + list: 'items', + }, + showActiveMsg: true, + showResponseMsg: false, + }, + round: true, + showOverflow: true, + size: 'small', }, - round: true, - showOverflow: true, - size: 'small', - }, - }); + }); - // 表格配置项可以用 cellRender: { name: 'CellImage' }, - vxeUI.renderer.add('CellImage', { - renderTableDefault(_renderOpts, params) { - const { column, row } = params; - return h(Image, { src: row[column.field] }); - }, - }); + // 表格配置项可以用 cellRender: { name: 'CellImage' }, + !vxeUI.renderer.get('CellImage') && + vxeUI.renderer.add('CellImage', { + renderTableDefault(_renderOpts, params) { + const { column, row } = params; + return h(Image, { src: row[column.field] }); + }, + }); - // 表格配置项可以用 cellRender: { name: 'CellLink' }, - vxeUI.renderer.add('CellLink', { - renderTableDefault(renderOpts) { - const { props } = renderOpts; - return h( - Button, - { size: 'small', type: 'link' }, - { default: () => props?.text }, - ); - }, - }); + // 表格配置项可以用 cellRender: { name: 'CellLink' }, + !vxeUI.renderer.get('CellLink') && + vxeUI.renderer.add('CellLink', { + renderTableDefault(renderOpts) { + const { props } = renderOpts; + return h( + Button, + { size: 'small', type: 'link' }, + { default: () => props?.text }, + ); + }, + }); - // 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化 - // vxeUI.formats.add - }, - useVbenForm, -}); + // 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化 + // vxeUI.formats.add + }, + useVbenForm, + }); + return useVxeGrid(options); +} export { useVbenVxeGrid }; diff --git a/apps/vben5/packages/@abp/ui/src/components/index.ts b/apps/vben5/packages/@abp/ui/src/components/index.ts index 367fce72a..e1fb587a3 100644 --- a/apps/vben5/packages/@abp/ui/src/components/index.ts +++ b/apps/vben5/packages/@abp/ui/src/components/index.ts @@ -3,3 +3,4 @@ export * from './codemirror'; export { default as LocalizableInput } from './localizable-input/LocalizableInput.vue'; export { default as PropertyTable } from './properties/PropertyTable.vue'; export * from './properties/types'; +export type * from './vxe-table'; diff --git a/apps/vben5/packages/@abp/ui/src/components/properties/PropertyTable.vue b/apps/vben5/packages/@abp/ui/src/components/properties/PropertyTable.vue index 8eb920245..d65d17ec4 100644 --- a/apps/vben5/packages/@abp/ui/src/components/properties/PropertyTable.vue +++ b/apps/vben5/packages/@abp/ui/src/components/properties/PropertyTable.vue @@ -39,7 +39,7 @@ const getDataResource = computed((): PropertyInfo[] => { const [PropertyModal, modalApi] = useVbenModal({ connectedComponent: defineAsyncComponent(() => import('./PropertyModal.vue')), }); -const getTableColumns = computed(() => { +const getTableColumns = computed((): TableColumnsType => { const columns: TableColumnsType = [ { align: 'left', @@ -59,7 +59,7 @@ const getTableColumns = computed(() => { ...columns, ...(props.disabled ? [] - : [ + : ([ { align: 'center', dataIndex: 'action', @@ -68,7 +68,7 @@ const getTableColumns = computed(() => { title: $t('component.extra_property_dictionary.actions.title'), width: 150, }, - ]), + ] as TableColumnsType)), ]; }); diff --git a/apps/vben5/packages/@abp/ui/src/components/vxe-table/api.ts b/apps/vben5/packages/@abp/ui/src/components/vxe-table/api.ts new file mode 100644 index 000000000..16cf7d8ec --- /dev/null +++ b/apps/vben5/packages/@abp/ui/src/components/vxe-table/api.ts @@ -0,0 +1,127 @@ +import type { ExtendedFormApi } from '@vben-core/form-ui'; +import type { VxeGridInstance } from 'vxe-table'; + +import type { VxeGridProps } from './types'; + +import { toRaw } from 'vue'; + +import { Store } from '@vben-core/shared/store'; +import { + bindMethods, + isBoolean, + isFunction, + mergeWithArrayOverride, + StateHandler, +} from '@vben-core/shared/utils'; + +function getDefaultState(): VxeGridProps { + return { + class: '', + formOptions: undefined, + gridClass: '', + gridEvents: {}, + gridOptions: {}, + showSearchForm: true, + }; +} + +export class VxeGridApi { + private isMounted = false; + + private stateHandler: StateHandler; + public formApi = {} as ExtendedFormApi; + + // private prevState: null | VxeGridProps = null; + public grid = {} as VxeGridInstance; + + public state: null | VxeGridProps = null; + + public store: Store; + + constructor(options: VxeGridProps = {}) { + const storeState = { ...options }; + + const defaultState = getDefaultState(); + this.store = new Store( + mergeWithArrayOverride(storeState, defaultState), + { + onUpdate: () => { + // this.prevState = this.state; + this.state = this.store.state; + }, + }, + ); + + this.state = this.store.state; + this.stateHandler = new StateHandler(); + bindMethods(this); + } + + mount(instance: null | VxeGridInstance, formApi: ExtendedFormApi) { + if (!this.isMounted && instance) { + this.grid = instance; + this.formApi = formApi; + this.stateHandler.setConditionTrue(); + this.isMounted = true; + } + } + + async query(params: Record = {}) { + try { + await this.grid.commitProxy('query', toRaw(params)); + } catch (error) { + console.error('Error occurred while querying:', error); + } + } + + async reload(params: Record = {}) { + try { + await this.grid.commitProxy('reload', toRaw(params)); + } catch (error) { + console.error('Error occurred while reloading:', error); + } + } + + setGridOptions(options: Partial) { + this.setState({ + gridOptions: options, + }); + } + + setLoading(isLoading: boolean) { + this.setState({ + gridOptions: { + loading: isLoading, + }, + }); + } + + setState( + stateOrFn: + | ((prev: VxeGridProps) => Partial) + | Partial, + ) { + if (isFunction(stateOrFn)) { + this.store.setState((prev) => { + return mergeWithArrayOverride(stateOrFn(prev), prev); + }); + } else { + this.store.setState((prev) => mergeWithArrayOverride(stateOrFn, prev)); + } + } + + toggleSearchForm(show?: boolean) { + this.setState({ + showSearchForm: isBoolean(show) ? show : !this.state?.showSearchForm, + }); + // nextTick(() => { + // this.grid.recalculate(); + // }); + return this.state?.showSearchForm; + } + + unmount() { + this.isMounted = false; + this.stateHandler.reset(); + } +} diff --git a/apps/vben5/packages/@abp/ui/src/components/vxe-table/extends.ts b/apps/vben5/packages/@abp/ui/src/components/vxe-table/extends.ts new file mode 100644 index 000000000..5199c90df --- /dev/null +++ b/apps/vben5/packages/@abp/ui/src/components/vxe-table/extends.ts @@ -0,0 +1,82 @@ +import type { Recordable } from '@vben/types'; +import type { VxeGridProps, VxeUIExport } from 'vxe-table'; + +import type { VxeGridApi } from './api'; + +import { formatDate, formatDateTime, isFunction } from '@vben/utils'; + +export function extendProxyOptions( + api: VxeGridApi, + options: VxeGridProps, + getFormValues: () => Recordable, +) { + [ + 'query', + 'querySuccess', + 'queryError', + 'queryAll', + 'queryAllSuccess', + 'queryAllError', + ].forEach((key) => { + extendProxyOption(key, api, options, getFormValues); + }); +} + +function extendProxyOption( + key: string, + api: VxeGridApi, + options: VxeGridProps, + getFormValues: () => Recordable, +) { + const { proxyConfig } = options; + const configFn = (proxyConfig?.ajax as Recordable)?.[key]; + if (!isFunction(configFn)) { + return options; + } + + const wrapperFn = async ( + params: Recordable, + customValues: Recordable, + ...args: Recordable[] + ) => { + const formValues = getFormValues(); + const data = await configFn( + params, + { + /** + * 开启toolbarConfig.refresh功能 + * 点击刷新按钮 这里的值为PointerEvent 会携带错误参数 + */ + ...(customValues instanceof PointerEvent ? {} : customValues), + ...formValues, + }, + ...args, + ); + return data; + }; + api.setState({ + gridOptions: { + proxyConfig: { + ajax: { + [key]: wrapperFn, + }, + }, + }, + }); +} + +export function extendsDefaultFormatter(vxeUI: VxeUIExport) { + !vxeUI.formats.has('formatDate') && + vxeUI.formats.add('formatDate', { + tableCellFormatMethod({ cellValue }) { + return formatDate(cellValue); + }, + }); + + !vxeUI.formats.has('formatDateTime') && + vxeUI.formats.add('formatDateTime', { + tableCellFormatMethod({ cellValue }) { + return formatDateTime(cellValue); + }, + }); +} diff --git a/apps/vben5/packages/@abp/ui/src/components/vxe-table/index.ts b/apps/vben5/packages/@abp/ui/src/components/vxe-table/index.ts new file mode 100644 index 000000000..a94216fb9 --- /dev/null +++ b/apps/vben5/packages/@abp/ui/src/components/vxe-table/index.ts @@ -0,0 +1,5 @@ +export { setupVbenVxeTable } from './init'; +export type { VxeTableGridOptions } from './types'; + +export { default as VbenVxeGrid } from './use-vxe-grid.vue'; +export type { VxeGridListeners, VxeGridProps } from 'vxe-table'; diff --git a/apps/vben5/packages/@abp/ui/src/components/vxe-table/init.ts b/apps/vben5/packages/@abp/ui/src/components/vxe-table/init.ts new file mode 100644 index 000000000..4c6ad127f --- /dev/null +++ b/apps/vben5/packages/@abp/ui/src/components/vxe-table/init.ts @@ -0,0 +1,131 @@ +import type { SetupVxeTable } from './types'; + +import { defineComponent, watch } from 'vue'; + +import { usePreferences } from '@vben/preferences'; +import { useVbenForm } from '@vben-core/form-ui'; + +import { + VxeButton, + VxeCheckbox, + + // VxeFormGather, + // VxeForm, + // VxeFormItem, + VxeIcon, + VxeInput, + VxeLoading, + VxeModal, + VxeNumberInput, + VxePager, + // VxeList, + // VxeModal, + // VxeOptgroup, + // VxeOption, + // VxePulldown, + // VxeRadio, + // VxeRadioButton, + VxeRadioGroup, + VxeSelect, + VxeTooltip, + VxeUI, + VxeUpload, + // VxeSwitch, + // VxeTextarea, +} from 'vxe-pc-ui'; +import enUS from 'vxe-pc-ui/lib/language/en-US'; + +// 导入默认的语言 +import zhCN from 'vxe-pc-ui/lib/language/zh-CN'; +import { + VxeColgroup, + VxeColumn, + VxeGrid, + VxeTable, + VxeToolbar, +} from 'vxe-table'; + +import { extendsDefaultFormatter } from './extends'; + +// 是否加载过 +let isInit = false; + +// eslint-disable-next-line import/no-mutable-exports +export let useTableForm: typeof useVbenForm; + +// 部分组件,如果没注册,vxe-table 会报错,这里实际没用组件,只是为了不报错,同时可以减少打包体积 +const createVirtualComponent = (name = '') => { + return defineComponent({ + name, + }); +}; + +export function initVxeTable() { + if (isInit) { + return; + } + + VxeUI.component(VxeTable); + VxeUI.component(VxeColumn); + VxeUI.component(VxeColgroup); + VxeUI.component(VxeGrid); + VxeUI.component(VxeToolbar); + + VxeUI.component(VxeButton); + // VxeUI.component(VxeButtonGroup); + VxeUI.component(VxeCheckbox); + // VxeUI.component(VxeCheckboxGroup); + VxeUI.component(createVirtualComponent('VxeForm')); + // VxeUI.component(VxeFormGather); + // VxeUI.component(VxeFormItem); + VxeUI.component(VxeIcon); + VxeUI.component(VxeInput); + // VxeUI.component(VxeList); + VxeUI.component(VxeLoading); + VxeUI.component(VxeModal); + VxeUI.component(VxeNumberInput); + // VxeUI.component(VxeOptgroup); + // VxeUI.component(VxeOption); + VxeUI.component(VxePager); + // VxeUI.component(VxePulldown); + // VxeUI.component(VxeRadio); + // VxeUI.component(VxeRadioButton); + VxeUI.component(VxeRadioGroup); + VxeUI.component(VxeSelect); + // VxeUI.component(VxeSwitch); + // VxeUI.component(VxeTextarea); + VxeUI.component(VxeTooltip); + VxeUI.component(VxeUpload); + + isInit = true; +} + +export function setupVbenVxeTable(setupOptions: SetupVxeTable) { + const { configVxeTable, useVbenForm } = setupOptions; + + initVxeTable(); + useTableForm = useVbenForm; + + const preference = usePreferences(); + + const localMap = { + 'en-US': enUS, + 'zh-CN': zhCN, + }; + + watch( + [() => preference.theme.value, () => preference.locale.value], + ([theme, locale]) => { + VxeUI.setTheme(theme === 'dark' ? 'dark' : 'light'); + VxeUI.setI18n(locale, localMap[locale]); + VxeUI.setLanguage(locale); + }, + { + immediate: true, + }, + ); + + extendsDefaultFormatter(VxeUI); + + configVxeTable(VxeUI); +} diff --git a/apps/vben5/packages/@abp/ui/src/components/vxe-table/style.css b/apps/vben5/packages/@abp/ui/src/components/vxe-table/style.css new file mode 100644 index 000000000..2f8fa8434 --- /dev/null +++ b/apps/vben5/packages/@abp/ui/src/components/vxe-table/style.css @@ -0,0 +1,104 @@ +:root .vxe-grid { + --vxe-ui-font-color: hsl(var(--foreground)); + --vxe-ui-font-primary-color: hsl(var(--primary)); + + /* --vxe-ui-font-lighten-color: #babdc0; + --vxe-ui-font-darken-color: #86898e; */ + --vxe-ui-font-disabled-color: hsl(var(--foreground) / 50%); + + /* base */ + --vxe-ui-base-popup-border-color: hsl(var(--border)); + --vxe-ui-input-disabled-color: hsl(var(--border) / 60%); + + /* --vxe-ui-base-popup-box-shadow: 0px 12px 30px 8px rgb(0 0 0 / 50%); */ + + /* layout */ + --vxe-ui-layout-background-color: hsl(var(--background)); + --vxe-ui-table-resizable-line-color: hsl(var(--heavy)); + + /* --vxe-ui-table-fixed-left-scrolling-box-shadow: 8px 0px 10px -5px hsl(var(--accent)); + --vxe-ui-table-fixed-right-scrolling-box-shadow: -8px 0px 10px -5px hsl(var(--accent)); */ + + /* input */ + --vxe-ui-input-border-color: hsl(var(--border)); + + /* --vxe-ui-input-placeholder-color: #8d9095; */ + + /* --vxe-ui-input-disabled-background-color: #262727; */ + + /* loading */ + --vxe-ui-loading-background-color: hsl(var(--overlay-content)); + + /* table */ + --vxe-ui-table-header-background-color: hsl(var(--accent)); + --vxe-ui-table-border-color: hsl(var(--border)); + --vxe-ui-table-row-hover-background-color: hsl(var(--accent-hover)); + --vxe-ui-table-row-striped-background-color: hsl(var(--accent) / 60%); + --vxe-ui-table-row-hover-striped-background-color: hsl(var(--accent)); + --vxe-ui-table-row-radio-checked-background-color: hsl(var(--accent)); + --vxe-ui-table-row-hover-radio-checked-background-color: hsl( + var(--accent-hover) + ); + --vxe-ui-table-row-checkbox-checked-background-color: hsl(var(--accent)); + --vxe-ui-table-row-hover-checkbox-checked-background-color: hsl( + var(--accent-hover) + ); + --vxe-ui-table-row-current-background-color: hsl(var(--accent)); + --vxe-ui-table-row-hover-current-background-color: hsl(var(--accent-hover)); + + /* --vxe-ui-table-fixed-scrolling-box-shadow-color: rgb(0 0 0 / 80%); */ +} + +.vxe-pager { + .vxe-pager--prev-btn:not(.is--disabled):active, + .vxe-pager--next-btn:not(.is--disabled):active, + .vxe-pager--num-btn:not(.is--disabled):active, + .vxe-pager--jump-prev:not(.is--disabled):active, + .vxe-pager--jump-next:not(.is--disabled):active, + .vxe-pager--prev-btn:not(.is--disabled):focus, + .vxe-pager--next-btn:not(.is--disabled):focus, + .vxe-pager--num-btn:not(.is--disabled):focus, + .vxe-pager--jump-prev:not(.is--disabled):focus, + .vxe-pager--jump-next:not(.is--disabled):focus { + color: hsl(var(--accent-foreground)); + background-color: hsl(var(--accent)); + border: 1px solid hsl(var(--border)); + box-shadow: 0 0 0 1px hsl(var(--border)); + } + + .vxe-pager--wrapper { + display: flex; + align-items: center; + } + + .vxe-pager--sizes { + margin-right: auto; + } +} + +.vxe-pager--wrapper { + @apply justify-center md:justify-end; +} + +.vxe-tools--operate { + margin-right: 0.25rem; + margin-left: 0.75rem; +} + +.vxe-table-custom--checkbox-option:hover { + background: none !important; +} + +.vxe-toolbar { + padding: 0; +} + +.vxe-buttons--wrapper:not(:empty), +.vxe-tools--operate:not(:empty), +.vxe-tools--wrapper:not(:empty) { + padding: 0.6em 0; +} + +.vxe-tools--operate:not(:has(button)) { + margin-left: 0; +} diff --git a/apps/vben5/packages/@abp/ui/src/components/vxe-table/types.ts b/apps/vben5/packages/@abp/ui/src/components/vxe-table/types.ts new file mode 100644 index 000000000..26916e6bd --- /dev/null +++ b/apps/vben5/packages/@abp/ui/src/components/vxe-table/types.ts @@ -0,0 +1,76 @@ +import type { ClassType, DeepPartial } from '@vben/types'; +import type { VbenFormProps } from '@vben-core/form-ui'; +import type { + VxeGridListeners, + VxeGridPropTypes, + VxeGridProps as VxeTableGridProps, + VxeUIExport, +} from 'vxe-table'; + +import type { VxeGridApi } from './api'; + +import type { Ref } from 'vue'; + +import { useVbenForm } from '@vben-core/form-ui'; + +export interface VxePaginationInfo { + currentPage: number; + pageSize: number; + total: number; +} + +interface ToolbarConfigOptions extends VxeGridPropTypes.ToolbarConfig { + /** 是否显示切换搜索表单的按钮 */ + search?: boolean; +} + +export interface VxeTableGridOptions extends VxeTableGridProps { + /** 工具栏配置 */ + toolbarConfig?: ToolbarConfigOptions; +} + +export interface VxeGridProps { + /** + * 组件class + */ + class?: ClassType; + /** + * 表单配置 + */ + formOptions?: VbenFormProps; + /** + * vxe-grid class + */ + gridClass?: ClassType; + /** + * vxe-grid 事件 + */ + gridEvents?: DeepPartial; + /** + * vxe-grid 配置 + */ + gridOptions?: DeepPartial; + /** + * 显示搜索表单 + */ + showSearchForm?: boolean; + /** + * 标题 + */ + tableTitle?: string; + /** + * 标题帮助 + */ + tableTitleHelp?: string; +} + +export type ExtendedVxeGridApi = { + useStore: >( + selector?: (state: NoInfer) => T, + ) => Readonly>; +} & VxeGridApi; + +export interface SetupVxeTable { + configVxeTable: (ui: VxeUIExport) => void; + useVbenForm: typeof useVbenForm; +} diff --git a/apps/vben5/packages/@abp/ui/src/components/vxe-table/use-vxe-grid.ts b/apps/vben5/packages/@abp/ui/src/components/vxe-table/use-vxe-grid.ts new file mode 100644 index 000000000..a309f5ac4 --- /dev/null +++ b/apps/vben5/packages/@abp/ui/src/components/vxe-table/use-vxe-grid.ts @@ -0,0 +1,45 @@ +import type { ExtendedVxeGridApi, VxeGridProps } from './types'; + +import { defineComponent, h, onBeforeUnmount } from 'vue'; + +import { useStore } from '@vben-core/shared/store'; + +import { VxeGridApi } from './api'; +import VxeGrid from './use-vxe-grid.vue'; + +export function useVbenVxeGrid(options: VxeGridProps) { + // const IS_REACTIVE = isReactive(options); + const api = new VxeGridApi(options); + const extendedApi: ExtendedVxeGridApi = api as ExtendedVxeGridApi; + extendedApi.useStore = (selector) => { + return useStore(api.store, selector); + }; + + const Grid = defineComponent( + (props: VxeGridProps, { attrs, slots }) => { + onBeforeUnmount(() => { + api.unmount(); + }); + api.setState({ ...props, ...attrs }); + return () => h(VxeGrid, { ...props, ...attrs, api: extendedApi }, slots); + }, + { + inheritAttrs: false, + name: 'VbenVxeGrid', + }, + ); + // Add reactivity support + // if (IS_REACTIVE) { + // watch( + // () => options, + // () => { + // api.setState(options); + // }, + // { immediate: true }, + // ); + // } + + return [Grid, extendedApi] as const; +} + +export type UseVbenVxeGrid = typeof useVbenVxeGrid; diff --git a/apps/vben5/packages/@abp/ui/src/components/vxe-table/use-vxe-grid.vue b/apps/vben5/packages/@abp/ui/src/components/vxe-table/use-vxe-grid.vue new file mode 100644 index 000000000..be1798c6b --- /dev/null +++ b/apps/vben5/packages/@abp/ui/src/components/vxe-table/use-vxe-grid.vue @@ -0,0 +1,394 @@ + + + diff --git a/apps/vben5/packages/@abp/ui/src/index.ts b/apps/vben5/packages/@abp/ui/src/index.ts index 8a270df36..9bb756bb5 100644 --- a/apps/vben5/packages/@abp/ui/src/index.ts +++ b/apps/vben5/packages/@abp/ui/src/index.ts @@ -2,4 +2,3 @@ export * from './adapter/component'; export * from './adapter/form'; export * from './adapter/vxe-table'; export * from './components'; -export type * from '@vben/plugins/vxe-table';