From c1fb1c2e97bd29df8100aabeb989853f9ab42a45 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 13:52:14 +0800 Subject: [PATCH 01/22] =?UTF-8?q?=E8=A7=A3=E5=86=B3deleteTableRecord?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=A0=91=E5=BD=A2=E8=A1=A8=E6=A0=BC=E7=9A=84?= =?UTF-8?q?=E8=A1=8C=E8=AE=B0=E5=BD=95=E6=97=B6=EF=BC=8C=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E5=88=A0=E9=99=A4=EF=BC=8C=E6=97=A0=E6=B3=95=E6=89=BE=E5=88=B0?= =?UTF-8?q?=E5=AD=90=E8=8A=82=E7=82=B9=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vue/src/components/Form/src/BasicForm.vue | 7 ++- .../Form/src/components/FormItem.vue | 4 +- .../Form/src/hooks/useFormEvents.ts | 2 +- .../vue/src/components/Form/src/types/form.ts | 2 +- .../src/components/Table/src/BasicTable.vue | 1 + .../Table/src/hooks/useDataSource.ts | 55 +++++++++++-------- 6 files changed, 44 insertions(+), 27 deletions(-) diff --git a/apps/vue/src/components/Form/src/BasicForm.vue b/apps/vue/src/components/Form/src/BasicForm.vue index b9a8c9ee9..99702e5af 100644 --- a/apps/vue/src/components/Form/src/BasicForm.vue +++ b/apps/vue/src/components/Form/src/BasicForm.vue @@ -63,6 +63,7 @@ import { basicProps } from './props'; import { useDesign } from '/@/hooks/web/useDesign'; + import { isFunction, isArray } from '/@/utils/is'; export default defineComponent({ name: 'BasicForm', @@ -90,6 +91,7 @@ // Get the basic configuration of the form const getProps = computed((): FormProps => { + // @ts-ignore return { ...props, ...unref(propsRef) } as FormProps; }); @@ -239,8 +241,11 @@ propsRef.value = deepMerge(unref(propsRef) || {}, formProps); } - function setFormModel(key: string, value: any) { + function setFormModel(key: string, value: any, schema: FormSchema) { formModel[key] = value; + if (isFunction(schema.dynamicRules) || isArray(schema.rules)) { + return; + } const { validateTrigger } = unref(getBindValue); if (!validateTrigger || validateTrigger === 'change') { validateFields([key]).catch((_) => {}); diff --git a/apps/vue/src/components/Form/src/components/FormItem.vue b/apps/vue/src/components/Form/src/components/FormItem.vue index 1d2657ad9..ff92cdc15 100644 --- a/apps/vue/src/components/Form/src/components/FormItem.vue +++ b/apps/vue/src/components/Form/src/components/FormItem.vue @@ -34,7 +34,7 @@ default: () => ({}), }, setFormModel: { - type: Function as PropType<(key: string, value: any) => void>, + type: Function as PropType<(key: string, value: any, schema: FormSchema) => void>, default: null, }, tableAction: { @@ -252,7 +252,7 @@ } const target = e ? e.target : null; const value = target ? (isCheck ? target.checked : target.value) : e; - props.setFormModel(field, value); + props.setFormModel(field, value, props.schema); }, }; const Comp = componentMap.get(component) as ReturnType; diff --git a/apps/vue/src/components/Form/src/hooks/useFormEvents.ts b/apps/vue/src/components/Form/src/hooks/useFormEvents.ts index 973f8c875..4b12ff355 100644 --- a/apps/vue/src/components/Form/src/hooks/useFormEvents.ts +++ b/apps/vue/src/components/Form/src/hooks/useFormEvents.ts @@ -60,7 +60,7 @@ export function useFormEvents({ /** * @description: Set form value */ - async function setFieldsValue(values: Recordable): Promise { + async function setFieldsValue>(values: T): Promise { const fields = unref(getSchema) .map((item) => item.field) .filter(Boolean); diff --git a/apps/vue/src/components/Form/src/types/form.ts b/apps/vue/src/components/Form/src/types/form.ts index 34cf482e8..968af1ce7 100644 --- a/apps/vue/src/components/Form/src/types/form.ts +++ b/apps/vue/src/components/Form/src/types/form.ts @@ -26,7 +26,7 @@ export interface ButtonProps extends AntdButtonProps { export interface FormActionType { submit: () => Promise; - setFieldsValue: (values: T) => Promise; + setFieldsValue: (values: T) => Promise; resetFields: () => Promise; getFieldsValue: () => Recordable; clearValidate: (name?: string | string[]) => Promise; diff --git a/apps/vue/src/components/Table/src/BasicTable.vue b/apps/vue/src/components/Table/src/BasicTable.vue index 7dfe1ee53..07fbdc0dc 100644 --- a/apps/vue/src/components/Table/src/BasicTable.vue +++ b/apps/vue/src/components/Table/src/BasicTable.vue @@ -97,6 +97,7 @@ import { warn } from '/@/utils/log'; export default defineComponent({ + name:'BasicTable', components: { Table, BasicForm, diff --git a/apps/vue/src/components/Table/src/hooks/useDataSource.ts b/apps/vue/src/components/Table/src/hooks/useDataSource.ts index 903b8e9c4..15d3677e7 100644 --- a/apps/vue/src/components/Table/src/hooks/useDataSource.ts +++ b/apps/vue/src/components/Table/src/hooks/useDataSource.ts @@ -165,30 +165,41 @@ export function useDataSource( const rowKeyName = unref(getRowKey); if (!rowKeyName) return; const rowKeys = !Array.isArray(rowKey) ? [rowKey] : rowKey; - for (const key of rowKeys) { - let index: number | undefined = dataSourceRef.value.findIndex((row) => { - let targetKeyName: string; - if (typeof rowKeyName === 'function') { - targetKeyName = rowKeyName(row); - } else { - targetKeyName = rowKeyName as string; - } - return row[targetKeyName] === key; - }); - if (index >= 0) { - dataSourceRef.value.splice(index, 1); + + function deleteRow(data?: Recordable, key?: string | number | string[] | number[]) { + const row: { index: number; data: [] } = findRow(data, key); + if (row === null || row.index === -1) { + return; } - index = unref(propsRef).dataSource?.findIndex((row) => { - let targetKeyName: string; - if (typeof rowKeyName === 'function') { - targetKeyName = rowKeyName(row); - } else { - targetKeyName = rowKeyName as string; + row.data.splice(row.index, 1); + + function findRow(data, key) { + if (data === null || data === undefined) { + return null; } - return row[targetKeyName] === key; - }); - if (typeof index !== 'undefined' && index !== -1) - unref(propsRef).dataSource?.splice(index, 1); + for (let i = 0; i < data.length; i++) { + const row = data[i]; + let targetKeyName: string = rowKeyName as string; + if (isFunction(rowKeyName)) { + targetKeyName = rowKeyName(row); + } + if (row[targetKeyName] === key) { + return { index: i, data }; + } + if (row.children?.length > 0) { + const result = findRow(row.children, key); + if (result != null) { + return result; + } + } + } + return null; + } + } + + for (const key of rowKeys) { + deleteRow(dataSourceRef.value, key); + deleteRow(unref(propsRef).dataSource, key); } setPagination({ total: unref(propsRef).dataSource?.length, From f9f474be022bce321e2c03cbcc71899cad7b043d Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 13:54:26 +0800 Subject: [PATCH 02/22] fix: table cell edit bug. --- .../Table/src/components/editable/EditableCell.vue | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/vue/src/components/Table/src/components/editable/EditableCell.vue b/apps/vue/src/components/Table/src/components/editable/EditableCell.vue index 9533564a0..975a900d0 100644 --- a/apps/vue/src/components/Table/src/components/editable/EditableCell.vue +++ b/apps/vue/src/components/Table/src/components/editable/EditableCell.vue @@ -404,9 +404,8 @@ column: this.column, index: this.index, }) - : this.getValues - ? this.getValues - : '\u00A0'} + : (this.getValues ?? "\u00A0") + } {!this.column.editRow && } From 91a55c9e671c05b4af83600d894c6631090eb721 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 13:59:01 +0800 Subject: [PATCH 03/22] =?UTF-8?q?=E4=BC=98=E5=8C=96appendSchemaByField?= =?UTF-8?q?=E5=8F=AA=E8=83=BD=E5=8D=95=E4=B8=80=E6=B7=BB=E5=8A=A0=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E8=A1=A8=E5=8D=95=E9=A1=B9=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/vue/src/components/Form/src/hooks/useForm.ts | 2 +- .../src/components/Form/src/hooks/useFormEvents.ts | 14 +++++++++----- apps/vue/src/components/Form/src/types/form.ts | 4 ++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/apps/vue/src/components/Form/src/hooks/useForm.ts b/apps/vue/src/components/Form/src/hooks/useForm.ts index ab80409ac..40f246da0 100644 --- a/apps/vue/src/components/Form/src/hooks/useForm.ts +++ b/apps/vue/src/components/Form/src/hooks/useForm.ts @@ -94,7 +94,7 @@ export function useForm(props?: Props): UseFormReturnType { }, appendSchemaByField: async ( - schema: FormSchema, + schema: FormSchema | FormSchema[], prefixField: string | undefined, first: boolean, ) => { diff --git a/apps/vue/src/components/Form/src/hooks/useFormEvents.ts b/apps/vue/src/components/Form/src/hooks/useFormEvents.ts index 4b12ff355..c780a828d 100644 --- a/apps/vue/src/components/Form/src/hooks/useFormEvents.ts +++ b/apps/vue/src/components/Form/src/hooks/useFormEvents.ts @@ -60,7 +60,7 @@ export function useFormEvents({ /** * @description: Set form value */ - async function setFieldsValue>(values: T): Promise { + async function setFieldsValue(values: any): Promise { const fields = unref(getSchema) .map((item) => item.field) .filter(Boolean); @@ -154,19 +154,23 @@ export function useFormEvents({ /** * @description: Insert after a certain field, if not insert the last */ - async function appendSchemaByField(schema: FormSchema, prefixField?: string, first = false) { + async function appendSchemaByField( + schema: FormSchema | FormSchema[], + prefixField?: string, + first = false, + ) { const schemaList: FormSchema[] = cloneDeep(unref(getSchema)); const index = schemaList.findIndex((schema) => schema.field === prefixField); - + const _schemaList = isObject(schema) ? [schema as FormSchema] : (schema as FormSchema[]); if (!prefixField || index === -1 || first) { - first ? schemaList.unshift(schema) : schemaList.push(schema); + first ? schemaList.unshift(..._schemaList) : schemaList.push(..._schemaList); schemaRef.value = schemaList; _setDefaultValue(schema); return; } if (index !== -1) { - schemaList.splice(index + 1, 0, schema); + schemaList.splice(index + 1, 0, ..._schemaList); } _setDefaultValue(schema); diff --git a/apps/vue/src/components/Form/src/types/form.ts b/apps/vue/src/components/Form/src/types/form.ts index 968af1ce7..922be8599 100644 --- a/apps/vue/src/components/Form/src/types/form.ts +++ b/apps/vue/src/components/Form/src/types/form.ts @@ -26,7 +26,7 @@ export interface ButtonProps extends AntdButtonProps { export interface FormActionType { submit: () => Promise; - setFieldsValue: (values: T) => Promise; + setFieldsValue: (values: T) => Promise; resetFields: () => Promise; getFieldsValue: () => Recordable; clearValidate: (name?: string | string[]) => Promise; @@ -35,7 +35,7 @@ export interface FormActionType { setProps: (formProps: Partial) => Promise; removeSchemaByField: (field: string | string[]) => Promise; appendSchemaByField: ( - schema: FormSchema, + schema: FormSchema | FormSchema[], prefixField: string | undefined, first?: boolean | undefined, ) => Promise; From db774b9b38bd00cf6852d12a7043262982d62e55 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:00:24 +0800 Subject: [PATCH 04/22] fix(components): [Table,EditableCell] rename Function --- .../Table/src/components/editable/EditableCell.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/vue/src/components/Table/src/components/editable/EditableCell.vue b/apps/vue/src/components/Table/src/components/editable/EditableCell.vue index 975a900d0..87fa6361e 100644 --- a/apps/vue/src/components/Table/src/components/editable/EditableCell.vue +++ b/apps/vue/src/components/Table/src/components/editable/EditableCell.vue @@ -194,10 +194,10 @@ value: unref(currentValueRef), record: toRaw(props.record), }); - handleSubmiRule(); + handleSubmitRule(); } - async function handleSubmiRule() { + async function handleSubmitRule() { const { column, record } = props; const { editRule } = column; const currentValue = unref(currentValueRef); @@ -227,7 +227,7 @@ async function handleSubmit(needEmit = true, valid = true) { if (valid) { - const isPass = await handleSubmiRule(); + const isPass = await handleSubmitRule(); if (!isPass) return false; } @@ -339,7 +339,7 @@ if (props.record) { initCbs('submitCbs', handleSubmit); - initCbs('validCbs', handleSubmiRule); + initCbs('validCbs', handleSubmitRule); initCbs('cancelCbs', handleCancel); if (props.column.dataIndex) { From 495e1d56ccaf8a70e770ba34b7a3120c3f96a4b5 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:02:19 +0800 Subject: [PATCH 05/22] =?UTF-8?q?perf(component):=20=E4=BC=98=E5=8C=96tabl?= =?UTF-8?q?e=20=E7=BB=84=E4=BB=B6=E7=9A=84insertTableDataRecord=E7=9A=84?= =?UTF-8?q?=E6=96=B9=E6=B3=95=EF=BC=8C=E5=A2=9E=E5=8A=A0=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E6=8F=92=E5=85=A5=E7=9A=84=E5=8A=9F=E8=83=BD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/vue/src/components/Table/src/hooks/useDataSource.ts | 7 ++++--- apps/vue/src/components/Table/src/types/table.ts | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/vue/src/components/Table/src/hooks/useDataSource.ts b/apps/vue/src/components/Table/src/hooks/useDataSource.ts index 15d3677e7..0a9f852b1 100644 --- a/apps/vue/src/components/Table/src/hooks/useDataSource.ts +++ b/apps/vue/src/components/Table/src/hooks/useDataSource.ts @@ -13,7 +13,7 @@ import { } from 'vue'; import { useTimeoutFn } from '/@/hooks/core/useTimeout'; import { buildUUID } from '/@/utils/uuid'; -import { isFunction, isBoolean } from '/@/utils/is'; +import { isFunction, isBoolean, isObject } from '/@/utils/is'; import { get, cloneDeep, merge } from 'lodash-es'; import { FETCH_SETTING, ROW_KEY, PAGE_SIZE } from '../const'; @@ -206,10 +206,11 @@ export function useDataSource( }); } - function insertTableDataRecord(record: Recordable, index: number): Recordable | undefined { + function insertTableDataRecord(record: Recordable | Recordable[], index: number): Recordable | undefined { // if (!dataSourceRef.value || dataSourceRef.value.length == 0) return; index = index ?? dataSourceRef.value?.length; - unref(dataSourceRef).splice(index, 0, record); + const _record = isObject(record) ? [record as Recordable] : (record as Recordable[]); + unref(dataSourceRef).splice(index, 0, ..._record); return unref(dataSourceRef); } diff --git a/apps/vue/src/components/Table/src/types/table.ts b/apps/vue/src/components/Table/src/types/table.ts index d3e9d4fea..6e5915fab 100644 --- a/apps/vue/src/components/Table/src/types/table.ts +++ b/apps/vue/src/components/Table/src/types/table.ts @@ -98,7 +98,7 @@ export interface TableActionType { setTableData: (values: T[]) => void; updateTableDataRecord: (rowKey: string | number, record: Recordable) => Recordable | void; deleteTableDataRecord: (rowKey: string | number | string[] | number[]) => void; - insertTableDataRecord: (record: Recordable, index?: number) => Recordable | void; + insertTableDataRecord: (record: Recordable | Recordable[], index?: number) => Recordable | void; findTableDataRecord: (rowKey: string | number) => Recordable | void; getColumns: (opt?: GetColumnsParams) => BasicColumn[]; setColumns: (columns: BasicColumn[] | string[]) => void; From 2bbf7aa6897cabf3fb6a8cbb27e6a1c3e0bd1111 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:03:48 +0800 Subject: [PATCH 06/22] =?UTF-8?q?fix:=20table-datasource=EF=BC=8C=E5=A6=82?= =?UTF-8?q?=E6=9E=9C=E5=90=8E=E7=AB=AF=E8=BF=94=E5=9B=9E=E7=9A=84page=20to?= =?UTF-8?q?tal=E6=98=AF=E5=AD=97=E7=AC=A6=E4=B8=B2=EF=BC=8C=E4=BC=9A?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E9=A1=B5=E7=A0=81=E4=B9=B1=E6=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/vue/src/components/Table/src/hooks/useDataSource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/vue/src/components/Table/src/hooks/useDataSource.ts b/apps/vue/src/components/Table/src/hooks/useDataSource.ts index 0a9f852b1..d9d140a78 100644 --- a/apps/vue/src/components/Table/src/hooks/useDataSource.ts +++ b/apps/vue/src/components/Table/src/hooks/useDataSource.ts @@ -321,7 +321,7 @@ export function useDataSource( const isArrayResult = Array.isArray(res); let resultItems: Recordable[] = isArrayResult ? res : get(res, listField); - const resultTotal: number = isArrayResult ? res.length : get(res, totalField); + const resultTotal: number = isArrayResult ? res.length : Number(get(res, totalField)); // 假如数据变少,导致总页数变少并小于当前选中页码,通过getPaginationRef获取到的页码是不正确的,需获取正确的页码再次执行 if (resultTotal) { From f2244ffaf3d2847551e0a1d0e60d834dce235055 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:06:20 +0800 Subject: [PATCH 07/22] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E9=9A=90=E8=97=8F?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E7=9B=AE=E5=BD=95=E5=8F=AA=E8=83=BD=E9=9A=90?= =?UTF-8?q?=E8=97=8F=E7=AC=AC=E4=B8=80=E5=B1=82=E7=9B=AE=E5=BD=95=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=8C=E9=80=92=E5=BD=92=E9=9A=90=E8=97=8F?= =?UTF-8?q?=E6=89=80=E6=9C=89=E5=AD=90=E8=8F=9C=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(menu): 解决隐藏菜单目录只能隐藏第一层目录的问题,递归隐藏所有子菜单 --- apps/vue/src/router/menus/index.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/vue/src/router/menus/index.ts b/apps/vue/src/router/menus/index.ts index bf677214d..014d75722 100644 --- a/apps/vue/src/router/menus/index.ts +++ b/apps/vue/src/router/menus/index.ts @@ -53,11 +53,21 @@ const staticMenus: Menu[] = []; async function getAsyncMenus() { const permissionStore = usePermissionStore(); + //递归过滤所有隐藏的菜单 + const menuFilter = (items) => { + return items.filter((item) => { + const show = !item.meta?.hideMenu && !item.hideMenu; + if (show && item.children) { + item.children = menuFilter(item.children); + } + return show; + }); + }; if (isBackMode()) { - return permissionStore.getBackMenuList.filter((item) => !item.meta?.hideMenu && !item.hideMenu); + return menuFilter(permissionStore.getBackMenuList); } if (isRouteMappingMode()) { - return permissionStore.getFrontMenuList.filter((item) => !item.hideMenu); + return menuFilter(permissionStore.getFrontMenuList); } return staticMenus; } From 9ecfdf84a92243cdb85267b3cb3f868d6ebe8e91 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:08:18 +0800 Subject: [PATCH 08/22] upgrade package.json * upgrade vue from 3.2.37 to 3.2.45 --- apps/vue/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/vue/package.json b/apps/vue/package.json index 6793ae97b..16099c79a 100644 --- a/apps/vue/package.json +++ b/apps/vue/package.json @@ -67,9 +67,9 @@ "showdown": "^2.1.0", "simple-uploader.js": "^0.5.6", "sortablejs": "^1.15.0", - "tinymce": "^5.10.3", + "tinymce": "^5.10.7", "vditor": "^3.8.13", - "vue": "^3.2.33", + "vue": "^3.2.45", "vue-cookies": "^1.8.1", "vue-i18n": "^9.1.9", "vue-json-pretty": "^2.0.6", From ec0606f9b8a97312c0429dc816742729e9ac90ea Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:13:05 +0800 Subject: [PATCH 09/22] =?UTF-8?q?feat:=20=E4=BD=BF=E7=94=A8useForm?= =?UTF-8?q?=E6=97=B6=E8=B0=83=E7=94=A8setFieldsValue=E5=90=8E=EF=BC=8C?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E7=9A=84onChange=E6=9C=AA=E4=B8=BB=E5=8A=A8?= =?UTF-8?q?=E8=A7=A6=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/Form/src/hooks/useFormEvents.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/apps/vue/src/components/Form/src/hooks/useFormEvents.ts b/apps/vue/src/components/Form/src/hooks/useFormEvents.ts index c780a828d..4c90df66e 100644 --- a/apps/vue/src/components/Form/src/hooks/useFormEvents.ts +++ b/apps/vue/src/components/Form/src/hooks/useFormEvents.ts @@ -77,6 +77,11 @@ export function useFormEvents({ const hasKey = Reflect.has(values, key); value = handleInputNumberValue(schema?.component, value); + const { componentProps } = schema || {}; + let _props = componentProps as any; + if (typeof componentProps === 'function') { + _props = _props({ formModel: unref(formModel) }); + } // 0| '' is allow if (hasKey && fields.includes(key)) { // time type @@ -86,17 +91,20 @@ export function useFormEvents({ for (const ele of value) { arr.push(ele ? dateUtil(ele) : null); } - formModel[key] = arr; + unref(formModel)[key] = arr; } else { const { componentProps } = schema || {}; let _props = componentProps as any; if (typeof componentProps === 'function') { _props = _props({ formModel }); } - formModel[key] = value ? (_props?.valueFormat ? value : dateUtil(value)) : null; + unref(formModel)[key] = value ? (_props?.valueFormat ? value : dateUtil(value)) : null; } } else { - formModel[key] = value; + unref(formModel)[key] = value; + } + if (_props?.onChange) { + _props?.onChange(value); } validKeys.push(key); } else { @@ -104,14 +112,14 @@ export function useFormEvents({ try { const value = nestKey.split('.').reduce((out, item) => out[item], values); if (isDef(value)) { - formModel[nestKey] = value; + unref(formModel)[nestKey] = unref(value); validKeys.push(nestKey); } } catch (e) { // key not exist if (isDef(defaultValueRef.value[nestKey])) { //formModel[nestKey] = defaultValueRef.value[nestKey]; - formModel[nestKey] = cloneDeep(defaultValueRef.value[nestKey]); + unref(formModel)[nestKey] = cloneDeep(unref(defaultValueRef.value[nestKey])); } } }); From e51ca4832de38fc728ce56074975b036eb799f25 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:14:20 +0800 Subject: [PATCH 10/22] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=9D=83?= =?UTF-8?q?=E9=99=90=E5=88=87=E6=8D=A2=E4=B8=8D=E7=94=9F=E6=95=88=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/vue/src/hooks/web/usePermission.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/vue/src/hooks/web/usePermission.ts b/apps/vue/src/hooks/web/usePermission.ts index cb2d442bd..0ee8bd9b1 100644 --- a/apps/vue/src/hooks/web/usePermission.ts +++ b/apps/vue/src/hooks/web/usePermission.ts @@ -30,7 +30,7 @@ export function usePermission() { async function togglePermissionMode() { appStore.setProjectConfig({ permissionMode: - projectSetting.permissionMode === PermissionModeEnum.BACK + appStore.projectConfig?.permissionMode === PermissionModeEnum.BACK ? PermissionModeEnum.ROUTE_MAPPING : PermissionModeEnum.BACK, }); From 69319847a0efe6a62be964fb7112d7526ac010da Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:15:38 +0800 Subject: [PATCH 11/22] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dtable=E7=BB=84?= =?UTF-8?q?=E4=BB=B6insertTableDataRecord=E6=96=B9=E6=B3=95=E7=9A=84?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/vue/src/components/Table/src/hooks/useDataSource.ts | 2 +- apps/vue/src/components/Table/src/types/table.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/vue/src/components/Table/src/hooks/useDataSource.ts b/apps/vue/src/components/Table/src/hooks/useDataSource.ts index d9d140a78..d4e6bbc78 100644 --- a/apps/vue/src/components/Table/src/hooks/useDataSource.ts +++ b/apps/vue/src/components/Table/src/hooks/useDataSource.ts @@ -206,7 +206,7 @@ export function useDataSource( }); } - function insertTableDataRecord(record: Recordable | Recordable[], index: number): Recordable | undefined { + function insertTableDataRecord(record: Recordable | Recordable[], index: number): Recordable[] | undefined { // if (!dataSourceRef.value || dataSourceRef.value.length == 0) return; index = index ?? dataSourceRef.value?.length; const _record = isObject(record) ? [record as Recordable] : (record as Recordable[]); diff --git a/apps/vue/src/components/Table/src/types/table.ts b/apps/vue/src/components/Table/src/types/table.ts index 6e5915fab..ca4dbd684 100644 --- a/apps/vue/src/components/Table/src/types/table.ts +++ b/apps/vue/src/components/Table/src/types/table.ts @@ -98,7 +98,7 @@ export interface TableActionType { setTableData: (values: T[]) => void; updateTableDataRecord: (rowKey: string | number, record: Recordable) => Recordable | void; deleteTableDataRecord: (rowKey: string | number | string[] | number[]) => void; - insertTableDataRecord: (record: Recordable | Recordable[], index?: number) => Recordable | void; + insertTableDataRecord: (record: Recordable | Recordable[], index?: number) => Recordable[] | void; findTableDataRecord: (rowKey: string | number) => Recordable | void; getColumns: (opt?: GetColumnsParams) => BasicColumn[]; setColumns: (columns: BasicColumn[] | string[]) => void; From 204486ea1efb2c8f90f34403f2bb74890cc798d0 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:18:25 +0800 Subject: [PATCH 12/22] =?UTF-8?q?fix(EditableCell):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=8F=AF=E7=BC=96=E8=BE=91=E8=A1=A8=E6=A0=BC=20=E7=BB=84?= =?UTF-8?q?=E4=BB=B6onChange=E9=87=8D=E5=A4=8D=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Table/src/components/editable/EditableCell.vue | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/vue/src/components/Table/src/components/editable/EditableCell.vue b/apps/vue/src/components/Table/src/components/editable/EditableCell.vue index 87fa6361e..cd4113b79 100644 --- a/apps/vue/src/components/Table/src/components/editable/EditableCell.vue +++ b/apps/vue/src/components/Table/src/components/editable/EditableCell.vue @@ -77,6 +77,11 @@ if (isFunction(compProps)) { compProps = compProps({ text: val, record, column, index }) ?? {}; } + + // 用临时变量存储 onChange方法 用于 handleChange方法 获取,并删除原始onChange, 防止存在两个 onChange + compProps.onChangeTemp = compProps.onChange; + delete compProps.onChange; + const component = unref(getComponent); const apiSelectProps: Recordable = {}; if (component === 'ApiSelect') { @@ -186,7 +191,7 @@ } else if (isString(e) || isBoolean(e) || isNumber(e) || isArray(e)) { currentValueRef.value = e; } - const onChange = unref(getComponentProps)?.onChange; + const onChange = unref(getComponentProps)?.onChangeTemp; if (onChange && isFunction(onChange)) onChange(...arguments); table.emit?.('edit-change', { @@ -404,8 +409,7 @@ column: this.column, index: this.index, }) - : (this.getValues ?? "\u00A0") - } + : (this.getValues ?? "\u00A0")} {!this.column.editRow && } From 49f826f25b454c2442247e61a91cee58809c5691 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:21:14 +0800 Subject: [PATCH 13/22] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dprops=E4=B8=ADAr?= =?UTF-8?q?ray=E7=B1=BB=E5=9E=8B=E5=AE=9A=E4=B9=89=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/vue/src/components/Dropdown/src/Dropdown.vue | 2 +- apps/vue/src/components/Form/src/props.ts | 4 ++-- apps/vue/src/components/Preview/src/Functional.vue | 2 +- apps/vue/src/components/Table/src/props.ts | 2 +- apps/vue/src/components/Upload/src/props.ts | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/vue/src/components/Dropdown/src/Dropdown.vue b/apps/vue/src/components/Dropdown/src/Dropdown.vue index c70a99ef3..afc2b7941 100644 --- a/apps/vue/src/components/Dropdown/src/Dropdown.vue +++ b/apps/vue/src/components/Dropdown/src/Dropdown.vue @@ -57,7 +57,7 @@ * @type string[] */ trigger: { - type: [Array] as PropType<('contextmenu' | 'click' | 'hover')[]>, + type: Array as PropType<('contextmenu' | 'click' | 'hover')[]>, default: () => { return ['contextmenu']; }, diff --git a/apps/vue/src/components/Form/src/props.ts b/apps/vue/src/components/Form/src/props.ts index bb7a7d6ba..e8e5a36d8 100644 --- a/apps/vue/src/components/Form/src/props.ts +++ b/apps/vue/src/components/Form/src/props.ts @@ -23,7 +23,7 @@ export const basicProps = { compact: propTypes.bool, // 表单配置规则 schemas: { - type: [Array] as PropType, + type: Array as PropType, default: () => [], }, mergeDynamicData: { @@ -120,7 +120,7 @@ export const tabProps = { compact: propTypes.bool, // 表单配置规则 schemas: { - type: [Array] as PropType, + type: Array as PropType, default: () => [], }, mergeDynamicData: { diff --git a/apps/vue/src/components/Preview/src/Functional.vue b/apps/vue/src/components/Preview/src/Functional.vue index 2fcf7e39f..1894209c6 100644 --- a/apps/vue/src/components/Preview/src/Functional.vue +++ b/apps/vue/src/components/Preview/src/Functional.vue @@ -30,7 +30,7 @@ default: false, }, imageList: { - type: [Array] as PropType, + type: Array as PropType, default: null, }, index: { diff --git a/apps/vue/src/components/Table/src/props.ts b/apps/vue/src/components/Table/src/props.ts index 01e56530c..df541a443 100644 --- a/apps/vue/src/components/Table/src/props.ts +++ b/apps/vue/src/components/Table/src/props.ts @@ -83,7 +83,7 @@ export const basicProps = { default: null, }, columns: { - type: [Array] as PropType, + type: Array as PropType, default: () => [], }, showIndexColumn: { type: Boolean, default: true }, diff --git a/apps/vue/src/components/Upload/src/props.ts b/apps/vue/src/components/Upload/src/props.ts index c10b7f409..d37f68bd8 100644 --- a/apps/vue/src/components/Upload/src/props.ts +++ b/apps/vue/src/components/Upload/src/props.ts @@ -69,7 +69,7 @@ export const previewProps = { export const fileListProps = { columns: { - type: [Array] as PropType, + type: Array as PropType, default: null, }, actionColumn: { From 51501a8a14b2df28e884bfc03383dbddd2815918 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:23:21 +0800 Subject: [PATCH 14/22] =?UTF-8?q?feat(CopperModal):=20=E5=A2=9E=E5=8A=A0sr?= =?UTF-8?q?c=E5=88=9D=E5=A7=8B=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/vue/src/components/Cropper/src/CopperModal.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/vue/src/components/Cropper/src/CopperModal.vue b/apps/vue/src/components/Cropper/src/CopperModal.vue index f3a971ca9..5546291af 100644 --- a/apps/vue/src/components/Cropper/src/CopperModal.vue +++ b/apps/vue/src/components/Cropper/src/CopperModal.vue @@ -132,9 +132,10 @@ uploadApi: { type: Function as PropType<(params: apiFunParams) => Promise>, }, + src: { type: String }, }); let filename = ''; - const src = ref(''); + const src = ref(props.src || ''); const previewSource = ref(''); const cropper = ref(); const fileList = ref([]); From edc2091acc1912254f8e107655833dfc17d9c520 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:27:21 +0800 Subject: [PATCH 15/22] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20updateSchema=20?= =?UTF-8?q?=E5=A4=9A=E4=B8=AAfield=20=E5=B1=9E=E6=80=A7=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E7=AC=AC=E4=BA=8C=E4=B8=AA=E6=97=A0=E6=95=88=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/Form/src/hooks/useFormEvents.ts | 16 ++++++++++------ apps/vue/src/layouts/page/index.vue | 6 ++++-- apps/vue/src/utils/index.ts | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/apps/vue/src/components/Form/src/hooks/useFormEvents.ts b/apps/vue/src/components/Form/src/hooks/useFormEvents.ts index 4c90df66e..0841f8169 100644 --- a/apps/vue/src/components/Form/src/hooks/useFormEvents.ts +++ b/apps/vue/src/components/Form/src/hooks/useFormEvents.ts @@ -227,15 +227,19 @@ export function useFormEvents({ return; } const schema: FormSchema[] = []; - updateData.forEach((item) => { - unref(getSchema).forEach((val) => { + unref(getSchema).forEach((val) => { + let _val; + updateData.forEach((item) => { if (val.field === item.field) { - const newSchema = deepMerge(val, item); - schema.push(newSchema as FormSchema); - } else { - schema.push(val); + _val = item; } }); + if (_val !== undefined && val.field === _val.field) { + const newSchema = deepMerge(val, _val); + schema.push(newSchema as FormSchema); + } else { + schema.push(val); + } }); _setDefaultValue(schema); diff --git a/apps/vue/src/layouts/page/index.vue b/apps/vue/src/layouts/page/index.vue index d1cd11431..5fd9327ba 100644 --- a/apps/vue/src/layouts/page/index.vue +++ b/apps/vue/src/layouts/page/index.vue @@ -15,9 +15,11 @@ appear > - +
+ +
-
+
diff --git a/apps/vue/src/utils/index.ts b/apps/vue/src/utils/index.ts index 0f5b7631f..5aa306b67 100644 --- a/apps/vue/src/utils/index.ts +++ b/apps/vue/src/utils/index.ts @@ -35,7 +35,7 @@ export function setObjToUrlParams(baseUrl: string, obj: any): string { export function deepMerge(src: any = {}, target: any = {}): T { let key: string; - const res: any = cloneDeep(src) + const res: any = cloneDeep(src); for (key in target) { res[key] = isObject(res[key]) ? deepMerge(res[key], target[key]) : (res[key] = target[key]); } From 87a72b37a050d0c153290259699e98b64ddf5a9e Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:58:25 +0800 Subject: [PATCH 16/22] =?UTF-8?q?feat(components):=20=E6=8E=A5=E5=85=A5vex?= =?UTF-8?q?table=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/vue/build/vite/plugin/styleImport.ts | 3 +- apps/vue/package.json | 5 + apps/vue/src/components/VxeTable/index.ts | 12 + .../components/VxeTable/src/VxeBasicTable.tsx | 108 +++++ .../components/VxeTable/src/componentMap.ts | 59 +++ .../components/VxeTable/src/componentType.ts | 22 + .../VxeTable/src/components/AApiSelect.tsx | 20 + .../VxeTable/src/components/AAutoComplete.tsx | 16 + .../VxeTable/src/components/AButton.tsx | 120 +++++ .../VxeTable/src/components/AButtonGroup.tsx | 59 +++ .../VxeTable/src/components/ACascader.tsx | 42 ++ .../src/components/ACheckboxGroup.tsx | 5 + .../VxeTable/src/components/ADatePicker.tsx | 33 ++ .../VxeTable/src/components/AEmpty.tsx | 27 ++ .../VxeTable/src/components/AInput.tsx | 16 + .../VxeTable/src/components/AInputNumber.tsx | 16 + .../VxeTable/src/components/AInputSearch.tsx | 17 + .../VxeTable/src/components/AMonthPicker.tsx | 18 + .../VxeTable/src/components/ARadioGroup.tsx | 5 + .../VxeTable/src/components/ARangePicker.tsx | 30 ++ .../VxeTable/src/components/ARate.tsx | 15 + .../VxeTable/src/components/ASelect.tsx | 271 +++++++++++ .../VxeTable/src/components/ASwitch.tsx | 53 +++ .../VxeTable/src/components/ATimePicker.tsx | 18 + .../VxeTable/src/components/ATreeSelect.tsx | 35 ++ .../VxeTable/src/components/AWeekPicker.tsx | 18 + .../VxeTable/src/components/AYearPicker.tsx | 18 + .../VxeTable/src/components/common.tsx | 427 ++++++++++++++++++ .../VxeTable/src/components/index.tsx | 112 +++++ apps/vue/src/components/VxeTable/src/const.ts | 4 + .../components/VxeTable/src/css/common.scss | 8 + .../VxeTable/src/css/component.scss | 105 +++++ .../components/VxeTable/src/css/index.scss | 6 + .../VxeTable/src/css/scrollbar.scss | 24 + .../components/VxeTable/src/css/toolbar.scss | 30 ++ .../components/VxeTable/src/css/variable.scss | 6 + apps/vue/src/components/VxeTable/src/emits.ts | 17 + .../vue/src/components/VxeTable/src/helper.ts | 19 + .../src/components/VxeTable/src/methods.ts | 131 ++++++ apps/vue/src/components/VxeTable/src/props.ts | 52 +++ .../src/components/VxeTable/src/setting.ts | 4 + apps/vue/src/components/VxeTable/src/types.ts | 7 + apps/vue/src/components/registerGlobComp.ts | 3 +- apps/vue/src/main.ts | 1 + apps/vue/src/settings/componentSetting.ts | 46 ++ 45 files changed, 2061 insertions(+), 2 deletions(-) create mode 100644 apps/vue/src/components/VxeTable/index.ts create mode 100644 apps/vue/src/components/VxeTable/src/VxeBasicTable.tsx create mode 100644 apps/vue/src/components/VxeTable/src/componentMap.ts create mode 100644 apps/vue/src/components/VxeTable/src/componentType.ts create mode 100644 apps/vue/src/components/VxeTable/src/components/AApiSelect.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/AAutoComplete.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/AButton.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/AButtonGroup.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/ACascader.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/ACheckboxGroup.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/ADatePicker.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/AEmpty.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/AInput.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/AInputNumber.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/AInputSearch.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/AMonthPicker.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/ARadioGroup.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/ARangePicker.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/ARate.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/ASelect.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/ASwitch.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/ATimePicker.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/ATreeSelect.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/AWeekPicker.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/AYearPicker.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/common.tsx create mode 100644 apps/vue/src/components/VxeTable/src/components/index.tsx create mode 100644 apps/vue/src/components/VxeTable/src/const.ts create mode 100644 apps/vue/src/components/VxeTable/src/css/common.scss create mode 100644 apps/vue/src/components/VxeTable/src/css/component.scss create mode 100644 apps/vue/src/components/VxeTable/src/css/index.scss create mode 100644 apps/vue/src/components/VxeTable/src/css/scrollbar.scss create mode 100644 apps/vue/src/components/VxeTable/src/css/toolbar.scss create mode 100644 apps/vue/src/components/VxeTable/src/css/variable.scss create mode 100644 apps/vue/src/components/VxeTable/src/emits.ts create mode 100644 apps/vue/src/components/VxeTable/src/helper.ts create mode 100644 apps/vue/src/components/VxeTable/src/methods.ts create mode 100644 apps/vue/src/components/VxeTable/src/props.ts create mode 100644 apps/vue/src/components/VxeTable/src/setting.ts create mode 100644 apps/vue/src/components/VxeTable/src/types.ts diff --git a/apps/vue/build/vite/plugin/styleImport.ts b/apps/vue/build/vite/plugin/styleImport.ts index 4384583b1..4ad9db3d6 100644 --- a/apps/vue/build/vite/plugin/styleImport.ts +++ b/apps/vue/build/vite/plugin/styleImport.ts @@ -2,7 +2,7 @@ * Introduces component library styles on demand. * https://github.com/anncwb/vite-plugin-style-import */ -import { createStyleImportPlugin } from 'vite-plugin-style-import'; +import { createStyleImportPlugin, VxeTableResolve } from 'vite-plugin-style-import'; export function configStyleImportPlugin(_isBuild: boolean) { // if (!isBuild) { @@ -77,6 +77,7 @@ export function configStyleImportPlugin(_isBuild: boolean) { }, }, ], + resolves: [VxeTableResolve()], }); return styleImportPlugin; } diff --git a/apps/vue/package.json b/apps/vue/package.json index 16099c79a..d8fbd8acb 100644 --- a/apps/vue/package.json +++ b/apps/vue/package.json @@ -53,6 +53,7 @@ "czg": "^1.3.9", "dayjs": "^1.11.1", "echarts": "^5.3.2", + "exceljs": "^4.3.0", "intro.js": "^5.1.0", "lodash-es": "^4.17.21", "mockjs": "^1.1.0", @@ -76,6 +77,9 @@ "vue-router": "^4.0.14", "vue-types": "^4.1.1", "vue3-colorpicker": "^2.0.4", + "vxe-table": "^4.3.9", + "vxe-table-plugin-export-xlsx": "^3.0.4", + "xe-utils": "^3.5.7", "xlsx": "^0.18.5" }, "devDependencies": { @@ -127,6 +131,7 @@ "rimraf": "^3.0.2", "rollup": "^2.70.2", "rollup-plugin-visualizer": "^5.6.0", + "sass": "^1.57.1", "stylelint": "^14.7.1", "stylelint-config-prettier": "^9.0.3", "stylelint-config-recommended": "^7.0.0", diff --git a/apps/vue/src/components/VxeTable/index.ts b/apps/vue/src/components/VxeTable/index.ts new file mode 100644 index 000000000..7cbb38bb3 --- /dev/null +++ b/apps/vue/src/components/VxeTable/index.ts @@ -0,0 +1,12 @@ +import { withInstall } from '/@/utils'; +import vxeBasicTable from './src/VxeBasicTable'; +import { VXETable } from 'vxe-table'; +import VXETablePluginAntd from './src/components'; +import VXETablePluginExportXLSX from 'vxe-table-plugin-export-xlsx'; +import './src/setting'; + +export const VxeBasicTable = withInstall(vxeBasicTable); +export * from 'vxe-table'; +export * from './src/types'; + +VXETable.use(VXETablePluginAntd).use(VXETablePluginExportXLSX); \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/VxeBasicTable.tsx b/apps/vue/src/components/VxeTable/src/VxeBasicTable.tsx new file mode 100644 index 000000000..2194a83ee --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/VxeBasicTable.tsx @@ -0,0 +1,108 @@ +import { defineComponent } from 'vue'; +import { computed, ref } from 'vue'; +import { BasicTableProps } from './types'; +import { omit } from 'lodash'; +import { basicProps } from './props'; +import { ignorePropKeys } from './const'; +import { basicEmits } from './emits'; +import XEUtils from 'xe-utils'; +import type { VxeGridInstance, VxeGridEventProps, GridMethods, TableMethods } from 'vxe-table'; +import { Grid as VxeGrid } from 'vxe-table'; + +import { extendSlots } from '/@/utils/helper/tsxHelper'; +import { gridComponentMethodKeys } from './methods'; + +export default defineComponent({ + name: 'VxeBasicTable', + props: basicProps, + emits: basicEmits, + setup(props, { emit, attrs }) { + const tableElRef = ref(); + const emitEvents: VxeGridEventProps = {}; + + const extendTableMethods = (methodKeys) => { + const funcs: any = {}; + methodKeys.forEach((name) => { + funcs[name] = (...args: any[]) => { + const $vxegrid: any = tableElRef.value; + if ($vxegrid && $vxegrid[name]) { + return $vxegrid[name](...args); + } + }; + }); + + return funcs; + }; + + const gridExtendTableMethods = extendTableMethods(gridComponentMethodKeys) as GridMethods & + TableMethods; + + basicEmits.forEach((name) => { + const type = XEUtils.camelCase(`on-${name}`) as keyof VxeGridEventProps; + + emitEvents[type] = (...args: any[]) => emit(name, ...args); + }); + + /** + * @description: 二次封装需要的所有属性 + * 1.部分属性需要和全局属性进行合并 + */ + const getBindValues = computed(() => { + const propsData: BasicTableProps = { + ...attrs, + ...props, + }; + + return propsData; + }); + + /** + * @description: Table 所有属性 + */ + const getBindGridValues = computed(() => { + const omitProps = omit(getBindValues.value, ignorePropKeys); + + return { + ...omitProps, + ...getBindGridEvent, + }; + }); + + /** + * @description: 组件外层class + */ + const getWrapperClass = computed(() => { + return [attrs.class]; + }); + + /** + * @description: 重写Vxe-table 方法 + */ + const getBindGridEvent: VxeGridEventProps = { + ...emitEvents, + }; + + return { + getWrapperClass, + getBindGridValues, + tableElRef, + ...gridExtendTableMethods, + }; + }, + render() { + const { tableClass, tableStyle } = this.$props; + + return ( +
+ + {extendSlots(this.$slots)} + +
+ ); + }, +}); \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/componentMap.ts b/apps/vue/src/components/VxeTable/src/componentMap.ts new file mode 100644 index 000000000..973ff85c2 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/componentMap.ts @@ -0,0 +1,59 @@ +import type { Component } from 'vue'; + +import type { ComponentType } from './componentType'; +import { ApiSelect, ApiTreeSelect } from '/@/components/Form'; +import { + Input, + Select, + Radio, + Checkbox, + AutoComplete, + Cascader, + DatePicker, + InputNumber, + Switch, + TimePicker, + TreeSelect, + Rate, + Button, + Empty, +} from 'ant-design-vue'; + +const componentMap = new Map(); + +componentMap.set('AButton', Button); + +componentMap.set('AInput', Input); +componentMap.set('AInputSearch', Input.Search); +componentMap.set('AInputNumber', InputNumber); +componentMap.set('AAutoComplete', AutoComplete); + +componentMap.set('ASelect', Select); +componentMap.set('ATreeSelect', TreeSelect); +componentMap.set('ASwitch', Switch); +componentMap.set('ARadioGroup', Radio.Group); +componentMap.set('ACheckboxGroup', Checkbox.Group); +componentMap.set('ACascader', Cascader); +componentMap.set('ARate', Rate); + +componentMap.set('ADatePicker', DatePicker); +componentMap.set('AMonthPicker', DatePicker.MonthPicker); +componentMap.set('ARangePicker', DatePicker.RangePicker); +componentMap.set('AWeekPicker', DatePicker.WeekPicker); +componentMap.set('AYearPicker', DatePicker.YearPicker); +componentMap.set('ATimePicker', TimePicker); + +componentMap.set('AApiSelect', ApiSelect); +componentMap.set('AApiTreeSelect', ApiTreeSelect); + +componentMap.set('AEmpty', Empty); + +export function add(compName: ComponentType, component: Component) { + componentMap.set(compName, component); +} + +export function del(compName: ComponentType) { + componentMap.delete(compName); +} + +export { componentMap }; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/componentType.ts b/apps/vue/src/components/VxeTable/src/componentType.ts new file mode 100644 index 000000000..6ae0741f6 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/componentType.ts @@ -0,0 +1,22 @@ +export type ComponentType = + | 'AInput' + | 'AInputNumber' + | 'ASelect' + | 'AApiSelect' + | 'ATreeSelect' + | 'AApiTreeSelect' + | 'ARadioGroup' + | 'ACheckboxGroup' + | 'AAutoComplete' + | 'ACascader' + | 'ADatePicker' + | 'AMonthPicker' + | 'ARangePicker' + | 'AWeekPicker' + | 'ATimePicker' + | 'AYearPicker' + | 'ASwitch' + | 'ARate' + | 'AInputSearch' + | 'AButton' + | 'AEmpty'; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/AApiSelect.tsx b/apps/vue/src/components/VxeTable/src/components/AApiSelect.tsx new file mode 100644 index 000000000..347504dd7 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/AApiSelect.tsx @@ -0,0 +1,20 @@ +import XEUtils from 'xe-utils'; +import { createDefaultRender, createEditRender, createFormItemRender } from './common'; + +export default { + renderDefault: createDefaultRender({}, (_, params) => { + return { + params: XEUtils.get(params, 'row'), + }; + }), + renderEdit: createEditRender({}, (_, params) => { + return { + params: XEUtils.get(params, 'row'), + }; + }), + renderItemContent: createFormItemRender({}, (_, params) => { + return { + params: XEUtils.get(params, 'row'), + }; + }), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/AAutoComplete.tsx b/apps/vue/src/components/VxeTable/src/components/AAutoComplete.tsx new file mode 100644 index 000000000..9cb0749cc --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/AAutoComplete.tsx @@ -0,0 +1,16 @@ +import { + createEditRender, + createDefaultRender, + createFilterRender, + createDefaultFilterRender, + createFormItemRender, +} from './common'; + +export default { + autofocus: 'input.ant-input', + renderDefault: createDefaultRender(), + renderEdit: createEditRender(), + renderFilter: createFilterRender(), + defaultFilterMethod: createDefaultFilterRender(), + renderItemContent: createFormItemRender(), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/AButton.tsx b/apps/vue/src/components/VxeTable/src/components/AButton.tsx new file mode 100644 index 000000000..c484d4e7e --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/AButton.tsx @@ -0,0 +1,120 @@ +import { h } from 'vue'; +import { + FormItemContentRenderParams, + FormItemRenderOptions, + VxeGlobalRendererHandles, +} from 'vxe-table'; +import XEUtils from 'xe-utils'; +import { cellText, createEvents, createProps, getComponent } from './common'; + +const COMPONENT_NAME = 'AButton'; + +export function createEditRender() { + return function ( + renderOpts: VxeGlobalRendererHandles.RenderEditOptions, + params: VxeGlobalRendererHandles.RenderEditParams, + ) { + const { attrs } = renderOpts; + const Component = getComponent(COMPONENT_NAME); + + return [ + h(Component, { + ...attrs, + ...createProps(renderOpts, null), + ...createEvents(renderOpts, params), + }), + ]; + }; +} + +export function createDefaultRender() { + return function ( + renderOpts: VxeGlobalRendererHandles.RenderEditOptions, + params: VxeGlobalRendererHandles.RenderEditParams, + ) { + const { attrs } = renderOpts; + const Component = getComponent(COMPONENT_NAME); + + return [ + h( + Component, + { + ...attrs, + ...createProps(renderOpts, null), + ...createEvents(renderOpts, params), + }, + cellText(renderOpts.content), + ), + ]; + }; +} + +export function createFormItemRender() { + return function (renderOpts: FormItemRenderOptions, params: FormItemContentRenderParams) { + const { attrs, content } = renderOpts; + const { property, $form, data } = params; + const props = createProps(renderOpts, null); + const Component = getComponent(COMPONENT_NAME); + + return [ + h( + Component, + { + ...attrs, + ...props, + ...createEvents( + renderOpts, + params, + (value: any) => { + // 处理 model 值双向绑定 + XEUtils.set(data, property, value); + }, + () => { + // 处理 change 事件相关逻辑 + $form.updateStatus({ + ...params, + field: property, + }); + }, + ), + }, + { + default: () => cellText(content || props.content), + }, + ), + ]; + }; +} + +function createToolbarButtonRender() { + return function ( + renderOpts: VxeGlobalRendererHandles.RenderToolOptions, + params: VxeGlobalRendererHandles.RenderButtonParams, + ) { + const { attrs } = renderOpts; + const { button } = params; + const props = createProps(renderOpts, null); + const Component = getComponent(COMPONENT_NAME); + + return [ + h( + Component, + { + ...attrs, + ...props, + ...createEvents(renderOpts, params), + }, + { + default: () => cellText(button?.content || props.content), + }, + ), + ]; + }; +} + +export default { + renderEdit: createEditRender(), + renderDefault: createDefaultRender(), + renderItemContent: createFormItemRender(), + renderToolbarButton: createToolbarButtonRender(), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/AButtonGroup.tsx b/apps/vue/src/components/VxeTable/src/components/AButtonGroup.tsx new file mode 100644 index 000000000..7ced11505 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/AButtonGroup.tsx @@ -0,0 +1,59 @@ +import { + FormItemContentRenderParams, + FormItemRenderOptions, + VxeGlobalRendererHandles, +} from 'vxe-table'; +import { createDefaultRender, createEditRender, createFormItemRender } from './AButton'; + +function createEditButtonRender() { + return function ( + renderOpts: VxeGlobalRendererHandles.RenderEditOptions, + params: VxeGlobalRendererHandles.RenderEditParams, + ) { + const buttonEditRender = createEditRender(); + const { children } = renderOpts; + if (children) { + return children.map( + (childRenderOpts: VxeGlobalRendererHandles.RenderEditOptions) => + buttonEditRender(childRenderOpts, params)[0], + ); + } + return []; + }; +} + +function createDefaultButtonRender() { + return function ( + renderOpts: VxeGlobalRendererHandles.RenderDefaultOptions, + params: VxeGlobalRendererHandles.RenderDefaultParams, + ) { + const buttonDefaultRender = createDefaultRender(); + const { children } = renderOpts; + if (children) { + return children.map( + (childRenderOpts: VxeGlobalRendererHandles.RenderDefaultOptions) => + buttonDefaultRender(childRenderOpts, params)[0], + ); + } + return []; + }; +} + +function createButtonItemRender() { + return function (renderOpts: FormItemRenderOptions, params: FormItemContentRenderParams) { + const buttonItemRender = createFormItemRender(); + const { children } = renderOpts; + if (children) { + return children.map( + (childRenderOpts: FormItemRenderOptions) => buttonItemRender(childRenderOpts, params)[0], + ); + } + return []; + }; +} + +export default { + renderEdit: createEditButtonRender(), + renderDefault: createDefaultButtonRender(), + renderItemContent: createButtonItemRender(), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/ACascader.tsx b/apps/vue/src/components/VxeTable/src/components/ACascader.tsx new file mode 100644 index 000000000..92f1c1352 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/ACascader.tsx @@ -0,0 +1,42 @@ +import { VxeGlobalRendererHandles } from 'vxe-table'; +import XEUtils from 'xe-utils'; +import { + createEditRender, + createCellRender, + createFormItemRender, + createExportMethod, +} from './common'; + +function matchCascaderData(index: number, list: any[], values: any[], labels: any[]) { + const val = values[index]; + if (list && values.length > index) { + XEUtils.each(list, (item) => { + if (item.value === val) { + labels.push(item.label); + matchCascaderData(++index, item.children, values, labels); + } + }); + } +} + +function getCascaderCellValue( + renderOpts: VxeGlobalRendererHandles.RenderOptions, + params: VxeGlobalRendererHandles.RenderCellParams, +) { + const { props = {} } = renderOpts; + const { row, column } = params; + const cellValue = XEUtils.get(row, column.field as string); + const values = cellValue || []; + const labels: Array = []; + matchCascaderData(0, props.options, values, labels); + return ( + props.showAllLevels === false ? labels.slice(labels.length - 1, labels.length) : labels + ).join(` ${props.separator || '/'} `); +} + +export default { + renderEdit: createEditRender(), + renderCell: createCellRender(getCascaderCellValue), + renderItemContent: createFormItemRender(), + exportMethod: createExportMethod(getCascaderCellValue), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/ACheckboxGroup.tsx b/apps/vue/src/components/VxeTable/src/components/ACheckboxGroup.tsx new file mode 100644 index 000000000..78477dc50 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/ACheckboxGroup.tsx @@ -0,0 +1,5 @@ +import { createFormItemRender } from './common'; + +export default { + renderItemContent: createFormItemRender(), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/ADatePicker.tsx b/apps/vue/src/components/VxeTable/src/components/ADatePicker.tsx new file mode 100644 index 000000000..afc2e5d64 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/ADatePicker.tsx @@ -0,0 +1,33 @@ +import { VxeGlobalRendererHandles } from 'vxe-table'; +import XEUtils from 'xe-utils'; +import { + createCellRender, + createEditRender, + createExportMethod, + createFormItemRender, +} from './common'; + +export function getDatePickerCellValue( + renderOpts: VxeGlobalRendererHandles.RenderOptions, + params: VxeGlobalRendererHandles.RenderCellParams | VxeGlobalRendererHandles.ExportMethodParams, + defaultFormat: string, +) { + const { props = {} } = renderOpts; + const { row, column } = params; + let cellValue = XEUtils.get(row, column.field as string); + if (cellValue) { + cellValue = cellValue.format(props.format || defaultFormat); + } + return cellValue; +} + +export default { + renderEdit: createEditRender(), + renderCell: createCellRender(getDatePickerCellValue, () => { + return ['YYYY-MM-DD']; + }), + renderItemContent: createFormItemRender(), + exportMethod: createExportMethod(getDatePickerCellValue, () => { + return ['YYYY-MM-DD']; + }), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/AEmpty.tsx b/apps/vue/src/components/VxeTable/src/components/AEmpty.tsx new file mode 100644 index 000000000..8f4f0a618 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/AEmpty.tsx @@ -0,0 +1,27 @@ +import { h } from 'vue'; +import { VxeGlobalRendererHandles } from 'vxe-table'; +import { getComponent } from './common'; + +function createEmptyRender() { + return function (renderOpts: VxeGlobalRendererHandles.RenderEmptyOptions) { + const { name, attrs, props } = renderOpts; + + const Component = getComponent(name); + return [ + h( + 'div', + { + class: 'flex items-center justify-center', + }, + h(Component, { + ...attrs, + ...props, + }), + ), + ]; + }; +} + +export default { + renderEmpty: createEmptyRender(), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/AInput.tsx b/apps/vue/src/components/VxeTable/src/components/AInput.tsx new file mode 100644 index 000000000..9cb0749cc --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/AInput.tsx @@ -0,0 +1,16 @@ +import { + createEditRender, + createDefaultRender, + createFilterRender, + createDefaultFilterRender, + createFormItemRender, +} from './common'; + +export default { + autofocus: 'input.ant-input', + renderDefault: createDefaultRender(), + renderEdit: createEditRender(), + renderFilter: createFilterRender(), + defaultFilterMethod: createDefaultFilterRender(), + renderItemContent: createFormItemRender(), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/AInputNumber.tsx b/apps/vue/src/components/VxeTable/src/components/AInputNumber.tsx new file mode 100644 index 000000000..43c80f554 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/AInputNumber.tsx @@ -0,0 +1,16 @@ +import { + createEditRender, + createFilterRender, + createFormItemRender, + createDefaultFilterRender, + createDefaultRender, +} from './common'; + +export default { + autofocus: 'input.ant-input-number-input', + renderDefault: createDefaultRender(), + renderEdit: createEditRender(), + renderFilter: createFilterRender(), + defaultFilterMethod: createDefaultFilterRender(), + renderItemContent: createFormItemRender(), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/AInputSearch.tsx b/apps/vue/src/components/VxeTable/src/components/AInputSearch.tsx new file mode 100644 index 000000000..bd2303679 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/AInputSearch.tsx @@ -0,0 +1,17 @@ +import { + createEditRender, + createDefaultRender, + createFilterRender, + createDefaultFilterRender, + createFormItemRender, + createToolbarToolRender, +} from './common'; + +export default { + renderDefault: createDefaultRender(), + renderEdit: createEditRender(), + renderFilter: createFilterRender(), + defaultFilterMethod: createDefaultFilterRender(), + renderItemContent: createFormItemRender(), + renderToolbarTool: createToolbarToolRender(), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/AMonthPicker.tsx b/apps/vue/src/components/VxeTable/src/components/AMonthPicker.tsx new file mode 100644 index 000000000..032df9760 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/AMonthPicker.tsx @@ -0,0 +1,18 @@ +import { getDatePickerCellValue } from './ADatePicker'; +import { + createCellRender, + createEditRender, + createExportMethod, + createFormItemRender, +} from './common'; + +export default { + renderEdit: createEditRender(), + renderCell: createCellRender(getDatePickerCellValue, () => { + return ['YYYY-MM']; + }), + renderItemContent: createFormItemRender(), + exportMethod: createExportMethod(getDatePickerCellValue, () => { + return ['YYYY-MM']; + }), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/ARadioGroup.tsx b/apps/vue/src/components/VxeTable/src/components/ARadioGroup.tsx new file mode 100644 index 000000000..78477dc50 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/ARadioGroup.tsx @@ -0,0 +1,5 @@ +import { createFormItemRender } from './common'; + +export default { + renderItemContent: createFormItemRender(), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/ARangePicker.tsx b/apps/vue/src/components/VxeTable/src/components/ARangePicker.tsx new file mode 100644 index 000000000..0f7af4681 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/ARangePicker.tsx @@ -0,0 +1,30 @@ +import { VxeColumnPropTypes, VxeGlobalRendererHandles } from 'vxe-table'; +import XEUtils from 'xe-utils'; +import { + createCellRender, + createEditRender, + createExportMethod, + createFormItemRender, +} from './common'; + +function getRangePickerCellValue( + renderOpts: VxeColumnPropTypes.EditRender, + params: VxeGlobalRendererHandles.RenderCellParams | VxeGlobalRendererHandles.ExportMethodParams, +) { + const { props = {} } = renderOpts; + const { row, column } = params; + let cellValue = XEUtils.get(row, column.field as string); + if (cellValue) { + cellValue = XEUtils.map(cellValue, (date: any) => + date.format(props.format || 'YYYY-MM-DD'), + ).join(' ~ '); + } + return cellValue; +} + +export default { + renderEdit: createEditRender(), + renderCell: createCellRender(getRangePickerCellValue), + renderItemContent: createFormItemRender(), + exportMethod: createExportMethod(getRangePickerCellValue), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/ARate.tsx b/apps/vue/src/components/VxeTable/src/components/ARate.tsx new file mode 100644 index 000000000..6ea19a7ce --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/ARate.tsx @@ -0,0 +1,15 @@ +import { + createEditRender, + createDefaultRender, + createFilterRender, + createDefaultFilterRender, + createFormItemRender, +} from './common'; + +export default { + renderDefault: createDefaultRender(), + renderEdit: createEditRender(), + renderFilter: createFilterRender(), + defaultFilterMethod: createDefaultFilterRender(), + renderItemContent: createFormItemRender(), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/ASelect.tsx b/apps/vue/src/components/VxeTable/src/components/ASelect.tsx new file mode 100644 index 000000000..961a63dd6 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/ASelect.tsx @@ -0,0 +1,271 @@ +import { ComponentOptions, h, resolveComponent } from 'vue'; +import { VxeColumnPropTypes, VxeGlobalRendererHandles } from 'vxe-table'; +import XEUtils from 'xe-utils'; +import { + cellText, + createCellRender, + createEvents, + createProps, + isEmptyValue, + createExportMethod, + createFormItemRender, +} from './common'; + +function renderOptions(options: any[], optionProps: VxeGlobalRendererHandles.RenderOptionProps) { + const labelProp = optionProps.label || 'label'; + const valueProp = optionProps.value || 'value'; + return XEUtils.map(options, (item, oIndex) => { + return h( + resolveComponent('a-select-option') as ComponentOptions, + { + key: oIndex, + value: item[valueProp], + disabled: item.disabled, + }, + { + default: () => cellText(item[labelProp]), + }, + ); + }); +} + +function createEditRender() { + return function ( + renderOpts: VxeColumnPropTypes.EditRender, + params: VxeGlobalRendererHandles.RenderEditParams, + ) { + const { options = [], optionGroups, optionProps = {}, optionGroupProps = {} } = renderOpts; + const { row, column, $table } = params; + const { attrs } = renderOpts; + const cellValue = XEUtils.get(row, column.field as string); + const props = createProps(renderOpts, cellValue); + const ons = createEvents( + renderOpts, + params, + (value: any) => { + // 处理 model 值双向绑定 + XEUtils.set(row, column.field as string, value); + }, + () => { + // 处理 change 事件相关逻辑 + $table.updateStatus(params); + }, + ); + if (optionGroups) { + const groupOptions = optionGroupProps.options || 'options'; + const groupLabel = optionGroupProps.label || 'label'; + return [ + h( + resolveComponent('a-select') as ComponentOptions, + { + ...attrs, + ...props, + ...ons, + }, + { + default: () => { + return XEUtils.map(optionGroups, (group, gIndex) => { + return h( + resolveComponent('a-select-opt-group') as ComponentOptions, + { + key: gIndex, + }, + { + label: () => { + return h('span', {}, group[groupLabel]); + }, + default: () => renderOptions(group[groupOptions], optionProps), + }, + ); + }); + }, + }, + ), + ]; + } + return [ + h( + resolveComponent('a-select') as ComponentOptions, + { + ...props, + ...attrs, + ...ons, + }, + { + default: () => renderOptions(options, optionProps), + }, + ), + ]; + }; +} + +function getSelectCellValue( + renderOpts: VxeGlobalRendererHandles.RenderCellOptions, + params: VxeGlobalRendererHandles.RenderCellParams, +) { + const { + options = [], + optionGroups, + props = {}, + optionProps = {}, + optionGroupProps = {}, + } = renderOpts; + const { row, column } = params; + const labelProp = optionProps.label || 'label'; + const valueProp = optionProps.value || 'value'; + const groupOptions = optionGroupProps.options || 'options'; + const cellValue = XEUtils.get(row, column.field as string); + if (!isEmptyValue(cellValue)) { + return XEUtils.map( + props.mode === 'multiple' ? cellValue : [cellValue], + optionGroups + ? (value) => { + let selectItem; + for (let index = 0; index < optionGroups.length; index++) { + selectItem = XEUtils.find( + optionGroups[index][groupOptions], + (item) => item[valueProp] === value, + ); + if (selectItem) { + break; + } + } + return selectItem ? selectItem[labelProp] : value; + } + : (value) => { + const selectItem = XEUtils.find(options, (item) => item[valueProp] === value); + return selectItem ? selectItem[labelProp] : value; + }, + ).join(', '); + } + return ''; +} + +function createFilterRender() { + return function ( + renderOpts: VxeColumnPropTypes.FilterRender, + params: VxeGlobalRendererHandles.RenderFilterParams, + ) { + const { options = [], optionGroups, optionProps = {}, optionGroupProps = {} } = renderOpts; + const groupOptions = optionGroupProps.options || 'options'; + const groupLabel = optionGroupProps.label || 'label'; + const { column } = params; + const { attrs } = renderOpts; + + return [ + h( + 'div', + { + class: 'vxe-table--filter-antd-wrapper', + }, + optionGroups + ? column.filters.map((option, oIndex) => { + const optionValue = option.data; + const props = createProps(renderOpts, optionValue); + + return h( + resolveComponent('a-select') as ComponentOptions, + { + key: oIndex, + ...attrs, + ...props, + ...createEvents( + renderOpts, + params, + (value: any) => { + // 处理 model 值双向绑定 + option.data = value; + }, + () => { + // 处理 change 事件相关逻辑 + const { $panel } = params; + $panel.changeOption( + null, + props.mode === 'multiple' + ? option.data && option.data.length > 0 + : !XEUtils.eqNull(option.data), + option, + ); + }, + ), + }, + { + default: () => { + return XEUtils.map(optionGroups, (group, gIndex) => { + return h( + resolveComponent('a-select-opt-group') as ComponentOptions, + { + key: gIndex, + }, + { + label: () => { + return h('span', {}, group[groupLabel]); + }, + default: () => renderOptions(group[groupOptions], optionProps), + }, + ); + }); + }, + }, + ); + }) + : column.filters.map((option, oIndex) => { + const optionValue = option.data; + const props = createProps(renderOpts, optionValue); + return h( + resolveComponent('a-select') as ComponentOptions, + { + key: oIndex, + ...attrs, + ...props, + ...createEvents( + renderOpts, + params, + (value: any) => { + // 处理 model 值双向绑定 + option.data = value; + }, + () => { + // 处理 change 事件相关逻辑 + const { $panel } = params; + $panel.changeOption( + null, + props.mode === 'multiple' + ? option.data && option.data.length > 0 + : !XEUtils.eqNull(option.data), + option, + ); + }, + ), + }, + { + default: () => renderOptions(options, optionProps), + }, + ); + }), + ), + ]; + }; +} + +export default { + renderEdit: createEditRender(), + renderCell: createCellRender(getSelectCellValue), + renderFilter: createFilterRender(), + defaultFilterMethod(params) { + const { option, row, column } = params; + const { data } = option; + const { field, filterRender: renderOpts } = column; + const { props = {} } = renderOpts; + const cellValue = XEUtils.get(row, field); + if (props.mode === 'multiple') { + if (XEUtils.isArray(cellValue)) { + return XEUtils.includeArrays(cellValue, data); + } + return data.indexOf(cellValue) > -1; + } + return cellValue == data; + }, + renderItemContent: createFormItemRender(), + exportMethod: createExportMethod(getSelectCellValue), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/ASwitch.tsx b/apps/vue/src/components/VxeTable/src/components/ASwitch.tsx new file mode 100644 index 000000000..3acee2bcb --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/ASwitch.tsx @@ -0,0 +1,53 @@ +import { h } from 'vue'; +import XEUtils from 'xe-utils'; +import { + createEditRender, + createDefaultRender, + createProps, + createEvents, + createDefaultFilterRender, + createFormItemRender, + getComponent, +} from './common'; + +export default { + renderDefault: createDefaultRender(), + renderEdit: createEditRender(), + renderFilter(renderOpts, params) { + const { column } = params; + const { name, attrs } = renderOpts; + const Component = getComponent(name); + + return [ + h( + 'div', + { + class: 'vxe-table--filter-antd-wrapper', + }, + column.filters.map((option, oIndex) => { + const optionValue = option.data; + return h(Component, { + key: oIndex, + ...attrs, + ...createProps(renderOpts, optionValue), + ...createEvents( + renderOpts, + params, + (value: any) => { + // 处理 model 值双向绑定 + option.data = value; + }, + () => { + // 处理 change 事件相关逻辑 + const { $panel } = params; + $panel.changeOption(null, XEUtils.isBoolean(option.data), option); + }, + ), + }); + }), + ), + ]; + }, + defaultFilterMethod: createDefaultFilterRender(), + renderItemContent: createFormItemRender(), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/ATimePicker.tsx b/apps/vue/src/components/VxeTable/src/components/ATimePicker.tsx new file mode 100644 index 000000000..66b2986fe --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/ATimePicker.tsx @@ -0,0 +1,18 @@ +import { getDatePickerCellValue } from './ADatePicker'; +import { + createEditRender, + createCellRender, + createFormItemRender, + createExportMethod, +} from './common'; + +export default { + renderEdit: createEditRender(), + renderCell: createCellRender(getDatePickerCellValue, () => { + return ['HH:mm:ss']; + }), + renderItemContent: createFormItemRender(), + exportMethod: createExportMethod(getDatePickerCellValue, () => { + return ['HH:mm:ss']; + }), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/ATreeSelect.tsx b/apps/vue/src/components/VxeTable/src/components/ATreeSelect.tsx new file mode 100644 index 000000000..3f8f5ae01 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/ATreeSelect.tsx @@ -0,0 +1,35 @@ +import { VxeGlobalRendererHandles } from 'vxe-table'; +import XEUtils from 'xe-utils'; +import { + createEditRender, + createCellRender, + isEmptyValue, + createFormItemRender, + createExportMethod, +} from './common'; + +function getTreeSelectCellValue( + renderOpts: VxeGlobalRendererHandles.RenderOptions, + params: VxeGlobalRendererHandles.RenderCellParams | VxeGlobalRendererHandles.ExportMethodParams, +) { + const { props = {} } = renderOpts; + const { treeData, treeCheckable } = props; + const { row, column } = params; + const cellValue = XEUtils.get(row, column.field as string); + if (!isEmptyValue(cellValue)) { + return XEUtils.map(treeCheckable ? cellValue : [cellValue], (value) => { + const matchObj = XEUtils.findTree(treeData, (item: any) => item.value === value, { + children: 'children', + }); + return matchObj ? matchObj.item.title : value; + }).join(', '); + } + return cellValue; +} + +export default { + renderEdit: createEditRender(), + renderCell: createCellRender(getTreeSelectCellValue), + renderItemContent: createFormItemRender(), + exportMethod: createExportMethod(getTreeSelectCellValue), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/AWeekPicker.tsx b/apps/vue/src/components/VxeTable/src/components/AWeekPicker.tsx new file mode 100644 index 000000000..6e44f42e6 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/AWeekPicker.tsx @@ -0,0 +1,18 @@ +import { getDatePickerCellValue } from './ADatePicker'; +import { + createEditRender, + createCellRender, + createFormItemRender, + createExportMethod, +} from './common'; + +export default { + renderEdit: createEditRender(), + renderCell: createCellRender(getDatePickerCellValue, () => { + return ['YYYY-WW周']; + }), + renderItemContent: createFormItemRender(), + exportMethod: createExportMethod(getDatePickerCellValue, () => { + return ['YYYY-WW周']; + }), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/AYearPicker.tsx b/apps/vue/src/components/VxeTable/src/components/AYearPicker.tsx new file mode 100644 index 000000000..cfd97496d --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/AYearPicker.tsx @@ -0,0 +1,18 @@ +import { getDatePickerCellValue } from './ADatePicker'; +import { + createEditRender, + createCellRender, + createFormItemRender, + createExportMethod, +} from './common'; + +export default { + renderEdit: createEditRender(), + renderCell: createCellRender(getDatePickerCellValue, () => { + return ['YYYY']; + }), + renderItemContent: createFormItemRender(), + exportMethod: createExportMethod(getDatePickerCellValue, () => { + return ['YYYY']; + }), +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/common.tsx b/apps/vue/src/components/VxeTable/src/components/common.tsx new file mode 100644 index 000000000..ea00e1ee3 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/common.tsx @@ -0,0 +1,427 @@ +import { ComponentOptions, h } from 'vue'; +import { + FormItemContentRenderParams, + FormItemRenderOptions, + VxeGlobalRendererHandles, +} from 'vxe-table'; +import XEUtils from 'xe-utils'; +import { componentMap } from '../componentMap'; +import { ComponentType } from '../componentType'; +import { createPlaceholderMessage } from '../helper'; + +/** + * @description: 获取组件 + */ +export function getComponent(componentName) { + const Component = componentMap.get(componentName as ComponentType); + if (!Component) throw `您还没注册此组件 ${componentName}`; + return Component as ComponentOptions; +} + +export function isEmptyValue(cellValue: any) { + return cellValue === null || cellValue === undefined || cellValue === ''; +} + +export function formatText(cellValue: any) { + return '' + (isEmptyValue(cellValue) ? '' : cellValue); +} + +export function cellText(cellValue: any): string[] { + return [formatText(cellValue)]; +} + +/** + * @description: 方法名转换 + */ +export function getOnName(type: string) { + return 'on' + type.substring(0, 1).toLocaleUpperCase() + type.substring(1); +} + +/** + * @description: 获取组件传值所接受的属性 + */ +function getModelKey(renderOpts: VxeGlobalRendererHandles.RenderOptions) { + let prop = 'value'; + switch (renderOpts.name) { + case 'ASwitch': + prop = 'checked'; + break; + } + return prop; +} + +/** + * @description: 回去双向更新的方法 + */ +function getModelEvent(renderOpts: VxeGlobalRendererHandles.RenderOptions) { + let type = 'update:value'; + switch (renderOpts.name) { + case 'ASwitch': + type = 'update:checked'; + break; + } + return type; +} + +/** + * @description: chang值改变方法 + * @param {} + * @return {*} + * @author: * + */ +function getChangeEvent() { + return 'change'; +} + +function getClickEvent() { + return 'click'; +} +/** + * @description: 获取方法 + * @param {} + * @return {*} + * @author: * + */ +export function createEvents( + renderOpts: VxeGlobalRendererHandles.RenderOptions, + params: VxeGlobalRendererHandles.RenderParams, + inputFunc?: Function, + changeFunc?: Function, + clickFunc?: Function, +) { + const { events } = renderOpts; + const modelEvent = getModelEvent(renderOpts); + const changeEvent = getChangeEvent(); + const clickEvent = getClickEvent(); + const isSameEvent = changeEvent === modelEvent; + const ons: { [type: string]: Function } = {}; + + XEUtils.objectEach(events, (func: Function, key: string) => { + ons[getOnName(key)] = function (...args: any[]) { + func(params, ...args); + }; + }); + if (inputFunc) { + ons[getOnName(modelEvent)] = function (targetEvnt: any) { + inputFunc(targetEvnt); + if (events && events[modelEvent]) { + events[modelEvent](params, targetEvnt); + } + if (isSameEvent && changeFunc) { + changeFunc(targetEvnt); + } + }; + } + if (!isSameEvent && changeFunc) { + ons[getOnName(changeEvent)] = function (...args: any[]) { + changeFunc(...args); + if (events && events[changeEvent]) { + events[changeEvent](params, ...args); + } + }; + } + if (clickFunc) { + ons[getOnName(clickEvent)] = function (...args: any[]) { + clickFunc(...args); + if (events && events[clickEvent]) { + events[clickEvent](params, ...args); + } + }; + } + return ons; +} + +/** + * @description: 获取属性 + */ +export function createProps( + renderOpts: VxeGlobalRendererHandles.RenderOptions, + value: any, + defaultProps?: { [prop: string]: any }, +) { + const name = renderOpts.name as ComponentType; + return XEUtils.assign( + { + placeholder: createPlaceholderMessage(name), + allowClear: true, + }, + defaultProps, + renderOpts.props, + { + [getModelKey(renderOpts)]: value, + }, + ); +} + +/** + * @description: 创建单元格默认显示内容 + */ +export function createDefaultRender( + defaultProps?: { [key: string]: any }, + callBack?: ( + renderOpts: VxeGlobalRendererHandles.RenderDefaultOptions, + params: VxeGlobalRendererHandles.RenderDefaultParams, + ) => Record, +) { + return function ( + renderOpts: VxeGlobalRendererHandles.RenderDefaultOptions, + params: VxeGlobalRendererHandles.RenderDefaultParams, + ) { + const { row, column, $table } = params; + const { name, attrs } = renderOpts; + const cellValue = XEUtils.get(row, column.field as string); + const args = (callBack && callBack(renderOpts, params)) ?? {}; + + const Component = getComponent(name); + return [ + h(Component, { + ...attrs, + ...createProps(renderOpts, cellValue, defaultProps), + ...args, + ...createEvents( + renderOpts, + params, + (value: any) => XEUtils.set(row, column.field as string, value), + () => $table.updateStatus(params), + ), + }), + ]; + }; +} + +/** + * @description: 创建编辑单元格 + */ +export function createEditRender( + defaultProps?: { [key: string]: any }, + callBack?: ( + renderOpts: VxeGlobalRendererHandles.RenderEditOptions, + params: VxeGlobalRendererHandles.RenderEditParams, + ) => Record, +) { + return function ( + renderOpts: VxeGlobalRendererHandles.RenderEditOptions, + params: VxeGlobalRendererHandles.RenderEditParams, + ) { + const { row, column, $table } = params; + const { name, attrs } = renderOpts; + const cellValue = XEUtils.get(row, column.field as string); + const args = (callBack && callBack(renderOpts, params)) ?? {}; + + const Component = getComponent(name); + return [ + h(Component, { + ...attrs, + ...createProps(renderOpts, cellValue, defaultProps), + ...args, + ...createEvents( + renderOpts, + params, + (value: any) => XEUtils.set(row, column.field as string, value), + () => $table.updateStatus(params), + ), + }), + ]; + }; +} + +/** + * @description: 创建筛选渲染内容 + */ +export function createFilterRender( + defaultProps?: { [key: string]: any }, + callBack?: ( + renderOpts: VxeGlobalRendererHandles.RenderFilterOptions, + params: VxeGlobalRendererHandles.RenderFilterParams, + ) => Record, +) { + return function ( + renderOpts: VxeGlobalRendererHandles.RenderFilterOptions, + params: VxeGlobalRendererHandles.RenderFilterParams, + ) { + const { column } = params; + const { name, attrs } = renderOpts; + const args = (callBack && callBack(renderOpts, params)) ?? {}; + + const Component = getComponent(name); + return [ + h( + 'div', + { + class: 'vxe-table--filter-antd-wrapper', + }, + column.filters.map((option, oIndex) => { + const optionValue = option.data; + const checked = !!option.data; + + return h(Component, { + key: oIndex, + ...attrs, + ...createProps(renderOpts, optionValue, defaultProps), + ...args, + ...createEvents( + renderOpts, + params, + (value: any) => { + // 处理 model 值双向绑定 + option.data = value; + }, + () => { + // 处理 change 事件相关逻辑 + const { $panel } = params; + $panel.changeOption(null, checked, option); + }, + ), + }); + }), + ), + ]; + }; +} + +/** + * @description: 默认过滤 + * @param {} + * @return {*} + * @author: * + */ + +export function createDefaultFilterRender() { + return function (params: VxeGlobalRendererHandles.FilterMethodParams) { + const { option, row, column } = params; + const { data } = option; + const cellValue = XEUtils.get(row, column.field as string); + return cellValue === data; + }; +} + +/** + * @description: 创建 form表单渲染 + */ +export function createFormItemRender( + defaultProps?: { [key: string]: any }, + callBack?: ( + renderOpts: FormItemRenderOptions, + params: FormItemContentRenderParams, + ) => Record, +) { + return function (renderOpts: FormItemRenderOptions, params: FormItemContentRenderParams) { + const args = (callBack && callBack(renderOpts, params)) ?? {}; + const { data, property, $form } = params; + const { name } = renderOpts; + const { attrs } = renderOpts; + const itemValue = XEUtils.get(data, property); + + const Component = getComponent(name); + return [ + h(Component, { + ...attrs, + ...createProps(renderOpts, itemValue, defaultProps), + ...args, + ...createEvents( + renderOpts, + params, + (value: any) => { + // 处理 model 值双向绑定 + XEUtils.set(data, property, value); + }, + () => { + // 处理 change 事件相关逻辑 + $form.updateStatus({ + ...params, + field: property, + }); + }, + ), + }), + ]; + }; +} + +/** + * @description: cell渲染 + */ +export function createCellRender( + getSelectCellValue: Function, + callBack?: ( + renderOpts: VxeGlobalRendererHandles.RenderCellOptions, + params: VxeGlobalRendererHandles.RenderCellParams, + ) => Array, +) { + return function ( + renderOpts: VxeGlobalRendererHandles.RenderCellOptions, + params: VxeGlobalRendererHandles.RenderCellParams, + ) { + const args = (callBack && callBack(renderOpts, params)) ?? []; + const cellLabel = getSelectCellValue && getSelectCellValue(renderOpts, params, ...args); + const { placeholder } = renderOpts; + + return [ + h( + 'span', + { + class: 'vxe-cell--label', + }, + placeholder && isEmptyValue(cellLabel) + ? [ + h( + 'span', + { + class: 'vxe-cell--placeholder', + }, + formatText(placeholder), + ), + ] + : formatText(cellLabel), + ), + ]; + }; +} + +/** + * @description: 创建 导出渲染 + * @param {} + * @return {*} + * @author: * + */ +export function createExportMethod( + getExportCellValue: Function, + callBack?: (params: VxeGlobalRendererHandles.ExportMethodParams) => Array, +) { + return function (params: VxeGlobalRendererHandles.ExportMethodParams) { + const { row, column, options } = params; + const args = (callBack && callBack(params)) ?? []; + return options && options.original + ? XEUtils.get(row, column.field as string) + : getExportCellValue(column.editRender || column.cellRender, params, ...args); + }; +} + +/** + * @description: 创建单元格默认显示内容 + */ +export function createToolbarToolRender( + defaultProps?: { [key: string]: any }, + callBack?: ( + renderOpts: VxeGlobalRendererHandles.RenderToolOptions, + params: VxeGlobalRendererHandles.RenderToolParams, + ) => Record, +) { + return function ( + renderOpts: VxeGlobalRendererHandles.RenderToolOptions, + params: VxeGlobalRendererHandles.RenderToolParams, + ) { + const { name, attrs } = renderOpts; + const args = (callBack && callBack(renderOpts, params)) ?? {}; + + const Component = getComponent(name); + return [ + h(Component, { + ...attrs, + ...createProps(renderOpts, null, defaultProps), + ...args, + ...createEvents(renderOpts, params), + }), + ]; + }; +} \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/components/index.tsx b/apps/vue/src/components/VxeTable/src/components/index.tsx new file mode 100644 index 000000000..7c162e9e9 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/components/index.tsx @@ -0,0 +1,112 @@ +import { VXETableCore, VxeGlobalInterceptorHandles } from 'vxe-table'; +import AAutoComplete from './AAutoComplete'; +import AInput from './AInput'; +import AInputNumber from './AInputNumber'; +import ASelect from './ASelect'; +import ACascader from './ACascader'; +import ADatePicker from './ADatePicker'; +import AMonthPicker from './AMonthPicker'; +import ARangePicker from './ARangePicker'; +import AWeekPicker from './AWeekPicker'; +import ATreeSelect from './ATreeSelect'; +import ATimePicker from './ATimePicker'; +import ARate from './ARate'; +import ASwitch from './ASwitch'; +import ARadioGroup from './ARadioGroup'; +import ACheckboxGroup from './ACheckboxGroup'; +import AButton from './AButton'; +import AButtonGroup from './AButtonGroup'; +import AApiSelect from './AApiSelect'; +import AEmpty from './AEmpty'; +import AInputSearch from './AInputSearch'; +import AYearPicker from './AYearPicker'; + +/** + * 检查触发源是否属于目标节点 + */ +function getEventTargetNode(evnt: any, container: HTMLElement, className: string) { + let targetElem; + let target = evnt.target; + while (target && target.nodeType && target !== document) { + if ( + className && + target.className && + target.className.split && + target.className.split(' ').indexOf(className) > -1 + ) { + targetElem = target; + } else if (target === container) { + return { flag: className ? !!targetElem : true, container, targetElem: targetElem }; + } + target = target.parentNode; + } + return { flag: false }; +} + +/** + * 事件兼容性处理 + */ +function handleClearEvent( + params: + | VxeGlobalInterceptorHandles.InterceptorClearFilterParams + | VxeGlobalInterceptorHandles.InterceptorClearActivedParams + | VxeGlobalInterceptorHandles.InterceptorClearAreasParams, +) { + const { $event } = params; + const bodyElem = document.body; + if ( + // 下拉框 + getEventTargetNode($event, bodyElem, 'ant-select-dropdown').flag || + // 级联 + getEventTargetNode($event, bodyElem, 'ant-cascader-menus').flag || + // 日期 + getEventTargetNode($event, bodyElem, 'ant-calendar-picker-container').flag || + // 时间选择 + getEventTargetNode($event, bodyElem, 'ant-time-picker-panel').flag + ) { + return false; + } +} + +/** + * 基于 vxe-table 表格的适配插件,用于兼容 ant-design-vue 组件库 + */ +export const VXETablePluginAntd = { + install(vxetablecore: VXETableCore) { + const { interceptor, renderer } = vxetablecore; + + renderer.mixin({ + AAutoComplete, + AInput, + AInputNumber, + ASelect, + ACascader, + ADatePicker, + AMonthPicker, + ARangePicker, + AWeekPicker, + ATimePicker, + ATreeSelect, + ARate, + ASwitch, + ARadioGroup, + ACheckboxGroup, + AButton, + AButtonGroup, + AApiSelect, + AEmpty, + AInputSearch, + AYearPicker, + }); + + interceptor.add('event.clearFilter', handleClearEvent); + interceptor.add('event.clearActived', handleClearEvent); + interceptor.add('event.clearAreas', handleClearEvent); + }, +}; + +if (typeof window !== 'undefined' && window.VXETable && window.VXETable.use) { + window.VXETable.use(VXETablePluginAntd); +} + +export default VXETablePluginAntd; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/const.ts b/apps/vue/src/components/VxeTable/src/const.ts new file mode 100644 index 000000000..a96734d7a --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/const.ts @@ -0,0 +1,4 @@ +/** + * @description: 传给vxe-table 时需要忽略的prop + */ +export const ignorePropKeys = ['tableClass', 'tableStyle']; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/css/common.scss b/apps/vue/src/components/VxeTable/src/css/common.scss new file mode 100644 index 000000000..6e930b1c9 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/css/common.scss @@ -0,0 +1,8 @@ +*, +::before, +::after { + box-sizing: border-box; + border-width: 0; + border-style: solid; + border-color: initial; +} \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/css/component.scss b/apps/vue/src/components/VxeTable/src/css/component.scss new file mode 100644 index 000000000..0341e3a87 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/css/component.scss @@ -0,0 +1,105 @@ +%ResetBorder { + border: 0; + box-shadow: none; +} + +%CompWidth { + & > .ant-input, + & > .ant-input-number, + & > .ant-select, + & > .ant-cascader-picker, + & > .ant-calendar-picker, + & > .ant-time-picker { + width: 100%; + } +} +.vxe-form { + .vxe-form--item-content { + @extend %CompWidth; + } +} +.vxe-table--filter-antd-wrapper { + & > .ant-input, + & > .ant-input-number, + & > .ant-select, + & > .ant-rate { + width: 180px; + } +} +.vxe-cell, +.vxe-tree-cell { + @extend %CompWidth; + & > .ant-rate { + vertical-align: bottom; + .anticon-star { + display: block; + } + } +} +.col--valid-error { + & > .vxe-cell, + & > .vxe-tree-cell { + & > .ant-input, + & > .ant-select .ant-input, + & > .ant-select .ant-select-selection, + & > .ant-input-number, + & > .ant-cascader-picker .ant-cascader-input, + & > .ant-calendar-picker .ant-calendar-picker-input { + // border-color: $vxe-table-validate-error-color; + box-shadow: none; + } + } +} +.vxe-table.cell--highlight { + .vxe-cell, + .vxe-tree-cell { + & > .ant-input, + & > .ant-input-number { + padding: 0; + @extend %ResetBorder; + } + & > .ant-select { + .ant-input { + padding: 0; + @extend %ResetBorder; + } + } + & > .ant-input-number { + .ant-input-number-input { + padding: 0; + } + .ant-input-number-handler-wrap, + .ant-input-number-handler-down { + @extend %ResetBorder; + } + } + & > .ant-select { + .ant-select-selection { + @extend %ResetBorder; + .ant-select-selection__rendered { + margin: 0; + } + } + } + & > .ant-cascader-picker { + .ant-input { + @extend %ResetBorder; + } + .ant-cascader-picker-label { + padding: 0; + } + } + & > .ant-calendar-picker { + .ant-calendar-picker-input { + padding: 0; + @extend %ResetBorder; + } + } + & > .ant-time-picker { + .ant-time-picker-input { + padding: 0; + @extend %ResetBorder; + } + } + } +} \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/css/index.scss b/apps/vue/src/components/VxeTable/src/css/index.scss new file mode 100644 index 000000000..f18c46fd6 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/css/index.scss @@ -0,0 +1,6 @@ +@import './common.scss'; +@import './variable.scss'; +@import './scrollbar.scss'; +@import './toolbar.scss'; +@import './component.scss'; +@import 'vxe-table/styles/index.scss'; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/css/scrollbar.scss b/apps/vue/src/components/VxeTable/src/css/scrollbar.scss new file mode 100644 index 000000000..817a800bd --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/css/scrollbar.scss @@ -0,0 +1,24 @@ +.vxe-grid_scrollbar { + ::-webkit-scrollbar { + width: 8px; + height: 8px; + } + ::-webkit-scrollbar-track { + background-color: #ffffff; + } + ::-webkit-scrollbar-thumb { + background-color: rgba(0, 0, 0, 0.1); + border-radius: 5px; + border: 1px solid #f1f1f1; + box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); + } + ::-webkit-scrollbar-thumb:hover { + background-color: #a8a8a8; + } + ::-webkit-scrollbar-thumb:active { + background-color: #a8a8a8; + } + ::-webkit-scrollbar-corner { + background-color: #ffffff; + } +} \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/css/toolbar.scss b/apps/vue/src/components/VxeTable/src/css/toolbar.scss new file mode 100644 index 000000000..8d2e10ec6 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/css/toolbar.scss @@ -0,0 +1,30 @@ +.vxe-toolbar .vxe-custom--option-wrapper .vxe-custom--footer { + display: flex; +} + +.vxe-toolbar .vxe-tools--wrapper, +.vxe-toolbar .vxe-tools--operate button:first-child { + margin: 0; +} + +.vxe-toolbar .vxe-tools--wrapper, +.vxe-toolbar .vxe-tools--operate .vxe-button { + margin-left: 1px; + border-radius: 0 !important; +} + +.vxe-toolbar .vxe-tools--wrapper, +.vxe-toolbar .vxe-tools--operate button:first-child { + margin-left: 10px; +} + +.vxe-toolbar .vxe-tools--wrapper, +.vxe-toolbar .vxe-tools--operate .vxe-custom--wrapper { + margin-left: 1px; + border-radius: 0 !important; +} + +.vxe-toolbar .vxe-tools--wrapper, +.vxe-toolbar .vxe-tools--operate .vxe-custom--wrapper .vxe-button { + margin-left: 1px; +} \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/css/variable.scss b/apps/vue/src/components/VxeTable/src/css/variable.scss new file mode 100644 index 000000000..2238abde4 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/css/variable.scss @@ -0,0 +1,6 @@ +$vxe-primary-color: rgb(9, 96, 189) !default; +$vxe-table-row-current-background-color: rgba(9, 96, 189, 0.3); +$vxe-table-row-hover-current-background-color: rgba(9, 96, 189, 0.2); +$vxe-table-column-hover-background-color: rgba(9, 96, 189, 0.3); +$vxe-table-column-current-background-color: rgba(9, 96, 189, 0.2); +$vxe-table-validate-error-color: #f56c6c; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/emits.ts b/apps/vue/src/components/VxeTable/src/emits.ts new file mode 100644 index 000000000..bd55d0070 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/emits.ts @@ -0,0 +1,17 @@ +import tableEmits from 'vxe-table/es/table/src/emits'; + +export const basicEmits = [ + ...tableEmits, + 'page-change', + 'form-submit', + 'form-submit-invalid', + 'form-reset', + 'form-collapse', + 'form-toggle-collapse', + 'toolbar-button-click', + 'toolbar-tool-click', + 'zoom', + + //... 如有缺少在此处追加 + // xxx +]; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/helper.ts b/apps/vue/src/components/VxeTable/src/helper.ts new file mode 100644 index 000000000..80372f802 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/helper.ts @@ -0,0 +1,19 @@ +import { ComponentType } from './componentType'; +import { useI18n } from '/@/hooks/web/useI18n'; + +const { t } = useI18n(); + +/** + * @description: 生成placeholder + */ +export function createPlaceholderMessage(component: ComponentType) { + if (!component) return; + if (component.includes('RangePicker')) { + return [t('common.chooseText'), t('common.chooseText')]; + } + if (component.includes('Input') || component.includes('Complete') || component.includes('Rate')) { + return t('common.inputText'); + } else { + return t('common.chooseText'); + } +} \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/methods.ts b/apps/vue/src/components/VxeTable/src/methods.ts new file mode 100644 index 000000000..c42bef1e3 --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/methods.ts @@ -0,0 +1,131 @@ +import { GridMethods, TableMethods } from 'vxe-table'; + +export const gridComponentMethodKeys: (keyof GridMethods | keyof TableMethods)[] = [ + // vxe-grid 部分 + 'dispatchEvent', + 'commitProxy', + 'getFormItems', + 'getPendingRecords', + 'zoom', + 'isMaximized', + 'maximize', + 'revert', + 'getProxyInfo', + + // vxe-table和vxe-grid公共部分 + 'clearAll', + 'syncData', + 'updateData', + 'loadData', + 'reloadData', + 'reloadRow', + 'loadColumn', + 'reloadColumn', + 'getRowNode', + 'getColumnNode', + 'getRowIndex', + 'getVTRowIndex', + 'getVMRowIndex', + 'getColumnIndex', + 'getVTColumnIndex', + 'getVMColumnIndex', + 'createData', + 'createRow', + 'revertData', + 'clearData', + 'isInsertByRow', + 'isUpdateByRow', + 'getColumns', + 'getColumnById', + 'getColumnByField', + 'getTableColumn', + 'getData', + 'getCheckboxRecords', + 'getParentRow', + 'getRowSeq', + 'getRowById', + 'getRowid', + 'getTableData', + 'hideColumn', + 'showColumn', + 'resetColumn', + 'refreshColumn', + 'refreshScroll', + 'recalculate', + 'closeTooltip', + 'isAllCheckboxChecked', + 'isAllCheckboxIndeterminate', + 'getCheckboxIndeterminateRecords', + 'setCheckboxRow', + 'isCheckedByCheckboxRow', + 'isIndeterminateByCheckboxRow', + 'toggleCheckboxRow', + 'setAllCheckboxRow', + 'getRadioReserveRecord', + 'clearRadioReserve', + 'getCheckboxReserveRecords', + 'clearCheckboxReserve', + 'toggleAllCheckboxRow', + 'clearCheckboxRow', + 'setCurrentRow', + 'isCheckedByRadioRow', + 'setRadioRow', + 'clearCurrentRow', + 'clearRadioRow', + 'getCurrentRecord', + 'getRadioRecord', + 'getCurrentColumn', + 'setCurrentColumn', + 'clearCurrentColumn', + 'sort', + 'clearSort', + 'isSort', + 'getSortColumns', + 'closeFilter', + 'isFilter', + 'isRowExpandLoaded', + 'clearRowExpandLoaded', + 'reloadRowExpand', + 'reloadRowExpand', + 'toggleRowExpand', + 'setAllRowExpand', + 'setRowExpand', + 'isExpandByRow', + 'clearRowExpand', + 'clearRowExpandReserve', + 'getRowExpandRecords', + 'getTreeExpandRecords', + 'isTreeExpandLoaded', + 'clearTreeExpandLoaded', + 'reloadTreeExpand', + 'reloadTreeChilds', + 'toggleTreeExpand', + 'setAllTreeExpand', + 'setTreeExpand', + 'isTreeExpandByRow', + 'clearTreeExpand', + 'clearTreeExpandReserve', + 'getScroll', + 'scrollTo', + 'scrollToRow', + 'scrollToColumn', + 'clearScroll', + 'updateFooter', + 'updateStatus', + 'setMergeCells', + 'removeInsertRow', + 'removeMergeCells', + 'getMergeCells', + 'clearMergeCells', + 'setMergeFooterItems', + 'removeMergeFooterItems', + 'getMergeFooterItems', + 'clearMergeFooterItems', + 'openTooltip', + 'focus', + 'blur', + 'connect', + + //... 如有缺少在此处追加 + // xxx +]; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/props.ts b/apps/vue/src/components/VxeTable/src/props.ts new file mode 100644 index 000000000..bd261378f --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/props.ts @@ -0,0 +1,52 @@ +import { VxeGridPropTypes, VxeTablePropTypes } from 'vxe-table'; +import tableProps from 'vxe-table/es/table/src/props'; +import { CSSProperties } from 'vue'; + +/** + * @description: table二次开发需要后,需要接受的所有prop属性 + */ +export const basicProps = { + ...tableProps, + columns: Array as PropType, + pagerConfig: { + type: Object as PropType, + default: () => ({}), + }, + proxyConfig: { + type: Object as PropType, + default: () => ({}), + }, + toolbarConfig: { + type: Object as PropType, + default: () => ({}), + }, + formConfig: { + type: Object as PropType, + default: () => ({}), + }, + zoomConfig: { + type: Object as PropType, + default: () => ({}), + }, + printConfig: { + type: Object as PropType, + default: () => ({}), + }, + exportConfig: { + type: Object as PropType, + default: () => ({}), + }, + importConfig: { + type: Object as PropType, + default: () => ({}), + }, + size: String as PropType, + tableClass: { + type: String, + default: '', + }, + tableStyle: { + type: Object as PropType, + default: () => ({}), + }, +}; \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/setting.ts b/apps/vue/src/components/VxeTable/src/setting.ts new file mode 100644 index 000000000..6024655ea --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/setting.ts @@ -0,0 +1,4 @@ +import { VXETable } from '..'; +import componentSetting from '/@/settings/componentSetting'; + +VXETable.setup(componentSetting.vxeTable); \ No newline at end of file diff --git a/apps/vue/src/components/VxeTable/src/types.ts b/apps/vue/src/components/VxeTable/src/types.ts new file mode 100644 index 000000000..d6f1263ff --- /dev/null +++ b/apps/vue/src/components/VxeTable/src/types.ts @@ -0,0 +1,7 @@ +import { CSSProperties } from 'vue'; +import { VxeGridProps } from 'vxe-table'; + +export type BasicTableProps = VxeGridProps & { + tableClass?: string; + tableStyle?: CSSProperties; +}; \ No newline at end of file diff --git a/apps/vue/src/components/registerGlobComp.ts b/apps/vue/src/components/registerGlobComp.ts index 3788a0b87..b7f51eef0 100644 --- a/apps/vue/src/components/registerGlobComp.ts +++ b/apps/vue/src/components/registerGlobComp.ts @@ -56,6 +56,7 @@ import { Input as AInput, InputNumber as AInputNumber, } from 'ant-design-vue'; +import VXETable from 'vxe-table'; const compList = [ Affix, @@ -157,5 +158,5 @@ export function registerGlobComp(app: App) { app.component(comp.name || comp.displayName, comp); }); - app.use(Input).use(Button).use(Layout); + app.use(Input).use(Button).use(Layout).use(VXETable); } diff --git a/apps/vue/src/main.ts b/apps/vue/src/main.ts index 4d9a2f355..958df3801 100644 --- a/apps/vue/src/main.ts +++ b/apps/vue/src/main.ts @@ -1,6 +1,7 @@ import 'virtual:windi-base.css'; import 'virtual:windi-components.css'; import '/@/design/index.less'; +import '/@/components/VxeTable/src/css/index.scss'; import 'virtual:windi-utilities.css'; // Register icon sprite import 'virtual:svg-icons-register'; diff --git a/apps/vue/src/settings/componentSetting.ts b/apps/vue/src/settings/componentSetting.ts index 0db4be859..a10712add 100644 --- a/apps/vue/src/settings/componentSetting.ts +++ b/apps/vue/src/settings/componentSetting.ts @@ -52,6 +52,52 @@ export default { return data; }, }, + vxeTable: { + table: { + border: true, + stripe: true, + columnConfig: { + resizable: true, + isCurrent: true, + isHover: true, + }, + rowConfig: { + isCurrent: true, + isHover: true, + }, + emptyRender: { + name: 'AEmpty', + }, + printConfig: {}, + exportConfig: {}, + customConfig: { + storage: true, + }, + }, + grid: { + toolbarConfig: { + enabled: true, + export: true, + zoom: true, + print: true, + refresh: true, + custom: true, + }, + pagerConfig: { + pageSizes: [20, 50, 100, 500], + pageSize: 20, + autoHidden: true, + }, + proxyConfig: { + form: true, + props: { + result: 'items', + total: 'total', + }, + }, + zoomConfig: {}, + }, + }, // scrollbar setting scrollbar: { // Whether to use native scroll bar From c7f69b31c0eee0ed5483b449320853f8d414d0a6 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 14:59:25 +0800 Subject: [PATCH 17/22] fix: lodash --- apps/vue/src/components/VxeTable/src/VxeBasicTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/vue/src/components/VxeTable/src/VxeBasicTable.tsx b/apps/vue/src/components/VxeTable/src/VxeBasicTable.tsx index 2194a83ee..b9179d787 100644 --- a/apps/vue/src/components/VxeTable/src/VxeBasicTable.tsx +++ b/apps/vue/src/components/VxeTable/src/VxeBasicTable.tsx @@ -1,7 +1,7 @@ import { defineComponent } from 'vue'; import { computed, ref } from 'vue'; import { BasicTableProps } from './types'; -import { omit } from 'lodash'; +import { omit } from 'lodash-es'; import { basicProps } from './props'; import { ignorePropKeys } from './const'; import { basicEmits } from './emits'; From add956528145ebdaf1f65de0b8b8e7c1f74b4ce6 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 15:01:13 +0800 Subject: [PATCH 18/22] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=A1=A8?= =?UTF-8?q?=E5=8D=95=E8=87=AA=E5=AE=9A=E4=B9=89=E7=BB=84=E4=BB=B6=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E4=B8=8D=E9=80=9A=E8=BF=87=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/vue/src/hooks/component/useFormItem.ts | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/apps/vue/src/hooks/component/useFormItem.ts b/apps/vue/src/hooks/component/useFormItem.ts index f1a9cef63..1296bb14c 100644 --- a/apps/vue/src/hooks/component/useFormItem.ts +++ b/apps/vue/src/hooks/component/useFormItem.ts @@ -1,13 +1,5 @@ import type { UnwrapRef, Ref, WritableComputedRef, DeepReadonly } from 'vue'; -import { - reactive, - readonly, - computed, - getCurrentInstance, - watchEffect, - unref, - toRaw, -} from 'vue'; +import { reactive, readonly, computed, getCurrentInstance, watchEffect, unref, toRaw } from 'vue'; import { isEqual } from 'lodash-es'; @@ -49,7 +41,9 @@ export function useRuleFormItem( if (isEqual(value, defaultState.value)) return; innerState.value = value as T[keyof T]; - emit?.(changeEvent, value, ...(toRaw(unref(emitData)) || [])); + setTimeout(() => { + emit?.(changeEvent, value, ...(toRaw(unref(emitData)) || [])); + }); }, }); From cde5fb492b1beca0207530fe3160e9a7e3816822 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 15:02:18 +0800 Subject: [PATCH 19/22] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96ApiCascader?= =?UTF-8?q?=E5=9B=9E=E8=B0=83=E5=87=BD=E6=95=B0change=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/vue/src/components/Form/src/components/ApiCascader.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/vue/src/components/Form/src/components/ApiCascader.vue b/apps/vue/src/components/Form/src/components/ApiCascader.vue index 0d8cf6354..948386a7c 100644 --- a/apps/vue/src/components/Form/src/components/ApiCascader.vue +++ b/apps/vue/src/components/Form/src/components/ApiCascader.vue @@ -170,7 +170,7 @@ ); function handleChange(keys, args) { - emitData.value = keys; + emitData.value = args; emit('defaultChange', keys, args); } From b1aab35a50b3db7733b3bcc4151cc74984c5f67c Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 1 Feb 2023 15:04:06 +0800 Subject: [PATCH 20/22] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96ApiRadioGroup?= =?UTF-8?q?=E5=9B=9E=E8=B0=83=E5=87=BD=E6=95=B0change=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/Form/src/components/ApiRadioGroup.vue | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/vue/src/components/Form/src/components/ApiRadioGroup.vue b/apps/vue/src/components/Form/src/components/ApiRadioGroup.vue index 712cfba81..e0d1c4d22 100644 --- a/apps/vue/src/components/Form/src/components/ApiRadioGroup.vue +++ b/apps/vue/src/components/Form/src/components/ApiRadioGroup.vue @@ -2,12 +2,12 @@ * @Description:It is troublesome to implement radio button group in the form. So it is extracted independently as a separate component -->