diff --git a/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/JVxeTable.ts b/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/JVxeTable.ts index e547f3e64..c1b7d3bdc 100644 --- a/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/JVxeTable.ts +++ b/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/JVxeTable.ts @@ -1,4 +1,4 @@ -import { defineComponent, h, nextTick, useSlots, shallowRef, markRaw } from 'vue'; +import { defineComponent, h, nextTick, ref, useSlots } from 'vue'; import { vxeEmits, vxeProps } from './vxe.data'; import { useData, useRefs, useResolveComponent as rc } from './hooks/useData'; import { useColumns } from './hooks/useColumns'; @@ -17,10 +17,7 @@ export default defineComponent({ props: vxeProps(), emits: [...vxeEmits], setup(props: JVxeTableProps, context) { - // update-begin--author:liaozhiyang---date:20260130---for:【QQYUN-14177】online配置界面,字段配置卡顿 - // 使用 shallowRef 优化大型对象响应式性能 - const instanceRef = shallowRef(); - // update-begin--author:liaozhiyang---date:20260130---for:【QQYUN-14177】online配置界面,字段配置卡顿 + const instanceRef = ref(); const refs = useRefs(); const slots = useSlots(); const data = useData(props); @@ -36,9 +33,6 @@ export default defineComponent({ const finallyProps = useFinallyProps(props, data, methods); // 渲染子组件 const renderComponents = useRenderComponents(props, data, methods, slots); - // update-begin--author:liaozhiyang---date:20260130---for:【QQYUN-14177】online配置界面,字段配置卡顿 - markRaw(renderComponents); - // update-end--author:liaozhiyang---date:20260130---for:【QQYUN-14177】online配置界面,字段配置卡顿 return { instanceRef, ...refs, diff --git a/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useColumns.ts b/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useColumns.ts index 77e9fc9bd..9194b572c 100644 --- a/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useColumns.ts +++ b/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useColumns.ts @@ -1,7 +1,7 @@ import type { JVxeColumn, JVxeDataProps, JVxeTableProps } from '../types'; -import { computed, nextTick, toRaw, shallowRef, markRaw } from 'vue'; +import { computed, nextTick, toRaw } from 'vue'; import { isArray, isEmpty, isPromise } from '/@/utils/is'; -import { cloneDeep, debounce } from 'lodash-es'; +import { cloneDeep } from 'lodash-es'; import { JVxeTypePrefix, JVxeTypes } from '../types/JVxeTypes'; import { initDictOptions } from '/@/utils/dict'; import { pushIfNotExist } from '/@/utils/common/compUtils'; @@ -24,129 +24,97 @@ export interface HandleArgs { } export function useColumns(props: JVxeTableProps, data: JVxeDataProps, methods: JVxeTableMethods, slots) { - // update-begin--author:liaozhiyang---date:20260130---for:【QQYUN-14177】online配置界面,字段配置卡顿 - // 使用 shallowRef 优化列数据响应式性能 - const columnsCache = shallowRef([]); - let lastColumnsHash = ''; - - // 计算列哈希值,用于缓存判断 - const getColumnsHash = (columns: JVxeColumn[]) => { - return JSON.stringify(columns.map(col => ({ key: col.key, type: col.type, title: col.title }))); - }; - - // 防抖处理列计算,避免频繁重新计算 - const debouncedComputeColumns = debounce(() => { - if (!isArray(props.columns)) { - columnsCache.value = []; - return; - } - - const currentHash = getColumnsHash(props.columns); - if (currentHash === lastColumnsHash) { - return; // 列没有变化,直接返回缓存 - } - - lastColumnsHash = currentHash; - const columns: JVxeColumn[] = []; - - // handle 方法参数 - const args: HandleArgs = { props, slots, data, methods, columns }; - let seqColumn, selectionColumn, expandColumn, dragSortColumn; - - const handleColumn = (column: JVxeColumn, container: JVxeColumn[]) => { - // 排除未授权的列 1 = 显示/隐藏; 2 = 禁用 - let auth = methods.getColAuth(column.key); - if (auth?.type == '1' && !auth.isAuth) { - return; - } else if (auth?.type == '2' && !auth.isAuth) { - column.disabled = true; - } - // type 不填,默认为 normal - if (column.type == null || isEmpty(column.type)) { - column.type = JVxeTypes.normal; - } - let col: JVxeColumn = cloneDeep(column); - // 处理隐藏列 - if (col.type === JVxeTypes.hidden) { - return handleInnerColumn(args, col, handleHiddenColumn); - } - // 处理子级列 - // 判断是否是分组列,如果当前是父级,则无需处理 render - if (Array.isArray(col.children) && col.children.length > 0) { - const children: JVxeColumn[] = []; - col.children.forEach((child: JVxeColumn) => handleColumn(child, children)); - col.children = children; - container.push(col); - return; - } - // 组件未注册,自动设置为 normal - if (!isRegistered(col.type)) { - col.type = JVxeTypes.normal; - } - args.enhanced = getEnhanced(col.type); - args.col = col; - args.renderOptions = { - bordered: props.bordered, - disabled: props.disabled, - scrolling: data.scrolling, - isDisabledRow: methods.isDisabledRow, - listeners: { - trigger: (name, event) => methods.trigger(name, event), - valueChange: (event) => methods.trigger('valueChange', event), - /** 重新排序行 */ - rowResort: (event) => { - methods.doSort(event.oldIndex, event.newIndex); - methods.trigger('dragged', event); - }, - /** 在当前行下面插入一行 */ - rowInsertDown: (rowIndex) => methods.insertRows({}, rowIndex + 1), - }, - }; - if (col.type === JVxeTypes.rowNumber) { - seqColumn = col; - container.push(col); - } else if (col.type === JVxeTypes.rowRadio || col.type === JVxeTypes.rowCheckbox) { - selectionColumn = col; - container.push(col); - } else if (col.type === JVxeTypes.rowExpand) { - expandColumn = col; - container.push(col); - } else if (col.type === JVxeTypes.rowDragSort) { - dragSortColumn = col; - container.push(col); - } else { - col.params = column; - args.columns = container; - handlerCol(args); - } - } - - props.columns.forEach((column: JVxeColumn) => handleColumn(column, columns)); - - handleInnerColumn(args, seqColumn, handleSeqColumn); - handleInnerColumn(args, selectionColumn, handleSelectionColumn); - handleInnerColumn(args, expandColumn, handleExpandColumn); - handleInnerColumn(args, dragSortColumn, handleDragSortColumn, true); - // update-begin--author:liaozhiyang---date:2024-05-30---for【TV360X-371】不可编辑组件必填缺少*号 - customComponentAddStar(columns); - // update-end--author:liaozhiyang---date:2024-05-30---for:【TV360X-371】不可编辑组件必填缺少*号 - - // 标记为原始对象,避免深度响应式 - columnsCache.value = markRaw(columns); - }, 16); // 16ms 防抖,约等于一帧的时间 - data.vxeColumns = computed(() => { - // 【issues/7812】linkageConfig改变了,vxetable没更新 - // linkageConfig变化时也需要执行 + // linkageConfig变化时也需要执行 const linkageConfig = toRaw(props.linkageConfig); if (linkageConfig) { // console.log(linkageConfig); } - // 触发防抖计算 - debouncedComputeColumns(); - return columnsCache.value; + let columns: JVxeColumn[] = []; + if (isArray(props.columns)) { + // handle 方法参数 + const args: HandleArgs = { props, slots, data, methods, columns }; + let seqColumn, selectionColumn, expandColumn, dragSortColumn; + + const handleColumn = (column: JVxeColumn, container: JVxeColumn[]) => { + // 排除未授权的列 1 = 显示/隐藏; 2 = 禁用 + let auth = methods.getColAuth(column.key); + if (auth?.type == '1' && !auth.isAuth) { + return; + } else if (auth?.type == '2' && !auth.isAuth) { + column.disabled = true; + } + // type 不填,默认为 normal + if (column.type == null || isEmpty(column.type)) { + column.type = JVxeTypes.normal; + } + let col: JVxeColumn = cloneDeep(column); + // 处理隐藏列 + if (col.type === JVxeTypes.hidden) { + return handleInnerColumn(args, col, handleHiddenColumn); + } + // 处理子级列 + // 判断是否是分组列,如果当前是父级,则无需处理 render + if (Array.isArray(col.children) && col.children.length > 0) { + const children: JVxeColumn[] = []; + col.children.forEach((child: JVxeColumn) => handleColumn(child, children)); + col.children = children; + container.push(col); + return; + } + // 组件未注册,自动设置为 normal + if (!isRegistered(col.type)) { + col.type = JVxeTypes.normal; + } + args.enhanced = getEnhanced(col.type); + args.col = col; + args.renderOptions = { + bordered: props.bordered, + disabled: props.disabled, + scrolling: data.scrolling, + isDisabledRow: methods.isDisabledRow, + listeners: { + trigger: (name, event) => methods.trigger(name, event), + valueChange: (event) => methods.trigger('valueChange', event), + /** 重新排序行 */ + rowResort: (event) => { + methods.doSort(event.oldIndex, event.newIndex); + methods.trigger('dragged', event); + }, + /** 在当前行下面插入一行 */ + rowInsertDown: (rowIndex) => methods.insertRows({}, rowIndex + 1), + }, + }; + if (col.type === JVxeTypes.rowNumber) { + seqColumn = col; + container.push(col); + } else if (col.type === JVxeTypes.rowRadio || col.type === JVxeTypes.rowCheckbox) { + selectionColumn = col; + container.push(col); + } else if (col.type === JVxeTypes.rowExpand) { + expandColumn = col; + container.push(col); + } else if (col.type === JVxeTypes.rowDragSort) { + dragSortColumn = col; + container.push(col); + } else { + col.params = column; + args.columns = container; + handlerCol(args); + } + } + + props.columns.forEach((column: JVxeColumn) => handleColumn(column, columns)); + + handleInnerColumn(args, seqColumn, handleSeqColumn); + handleInnerColumn(args, selectionColumn, handleSelectionColumn); + handleInnerColumn(args, expandColumn, handleExpandColumn); + handleInnerColumn(args, dragSortColumn, handleDragSortColumn, true); + // update-begin--author:liaozhiyang---date:2024-05-30---for【TV360X-371】不可编辑组件必填缺少*号 + customComponentAddStar(columns); + } + return columns; }); - // update-end--author:liaozhiyang---date:20260130---for:【QQYUN-14177】online配置界面,字段配置卡顿 } /** diff --git a/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useData.ts b/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useData.ts index cd79b3203..474a13ae4 100644 --- a/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useData.ts +++ b/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useData.ts @@ -1,4 +1,4 @@ -import { ref, reactive, provide, resolveComponent, shallowRef, markRaw } from 'vue'; +import { ref, reactive, provide, resolveComponent } from 'vue'; import { useDesign } from '/@/hooks/web/useDesign'; import { JVxeDataProps, JVxeRefs, JVxeTableProps } from '../types'; import { VxeGridInstance } from 'vxe-table'; @@ -7,108 +7,90 @@ import { randomString } from '/@/utils/common/compUtils'; export function useData(props: JVxeTableProps): JVxeDataProps { const { prefixCls } = useDesign('j-vxe-table'); provide('prefixCls', prefixCls); - - // update-begin--author:liaozhiyang---date:20260130---for:【QQYUN-14177】online配置界面,字段配置卡顿 - // 使用 shallowRef 优化大数据源性能 - const vxeDataSource = shallowRef([]); - - // 标记静态配置对象,避免深度响应式 - const defaultVxeProps = markRaw({ - // update-begin--author:liaozhiyang---date:20240607---for:【TV360X-327】vxetable警告 - // rowId: props.rowKey, - rowConfig: { - keyField: props.rowKey, - // 高亮hover的行 - isHover: true, - }, - // update-end--author:liaozhiyang---date:20240607---for:【TV360X-327】vxetable警告 - - // --- 【issues/209】自带的tooltip会错位,所以替换成原生的title --- - // 溢出隐藏并显示tooltip - showOverflow: "title", - // 表头溢出隐藏并显示tooltip - showHeaderOverflow: "title", - // --- 【issues/209】自带的tooltip会错位,所以替换成原生的title --- - - showFooterOverflow: true, - // 可编辑配置 - editConfig: { - trigger: 'click', - mode: 'cell', - // update-begin--author:liaozhiyang---date:20231013---for:【QQYUN-5133】JVxeTable 行编辑升级 - //activeMethod: () => !props.disabled, - beforeEditMethod: () => !props.disabled, - // update-end--author:liaozhiyang---date:20231013---for:【QQYUN-5133】JVxeTable 行编辑升级 - }, - expandConfig: { - iconClose: 'vxe-icon-arrow-right', - iconOpen: 'vxe-icon-arrow-down', - ...props.expandConfig, - }, - // 虚拟滚动配置,y轴大于xx条数据时启用虚拟滚动 - scrollY: { - gt: 30, - }, - scrollX: { - gt: 20, - // 暂时关闭左右虚拟滚动 - enabled: false, - }, - radioConfig: { - // 保留勾选状态 - reserve: true, - highlight: true, - }, - checkboxConfig: { - // 保留勾选状态 - reserve: true, - highlight: true, - }, - mouseConfig: { selected: false }, - keyboardConfig: { - // 删除键功能 - isDel: false, - // Esc键关闭编辑功能 - isEsc: true, - // Tab 键功能 - isTab: true, - // 任意键进入编辑(功能键除外) - isEdit: true, - // 方向键功能 - isArrow: true, - // 回车键功能 - isEnter: true, - // 如果功能被支持,用于 column.type=checkbox|radio,开启空格键切换复选框或单选框状态功能 - isChecked: true, - }, - }); - - // 使用 shallowRef 优化选中行性能 - const selectedRows = shallowRef([]); - const selectedRowIds = shallowRef([]); - const authsMap = shallowRef(null); - return { prefixCls: prefixCls, caseId: `j-vxe-${randomString(8)}`, - vxeDataSource, + vxeDataSource: ref([]), scroll: reactive({ top: 0, left: 0 }), scrolling: ref(false), - defaultVxeProps, - selectedRows, - selectedRowIds, + defaultVxeProps: reactive({ + // rowId: props.rowKey, + rowConfig: { + keyField: props.rowKey, + // 高亮hover的行 + isHover: true, + }, + + // --- 【issues/209】自带的tooltip会错位,所以替换成原生的title --- + // 溢出隐藏并显示tooltip + showOverflow: "title", + // 表头溢出隐藏并显示tooltip + showHeaderOverflow: "title", + // --- 【issues/209】自带的tooltip会错位,所以替换成原生的title --- + + showFooterOverflow: true, + // 可编辑配置 + editConfig: { + trigger: 'click', + mode: 'cell', + //activeMethod: () => !props.disabled, + beforeEditMethod: () => !props.disabled, + }, + expandConfig: { + iconClose: 'vxe-icon-arrow-right', + iconOpen: 'vxe-icon-arrow-down', + ...props.expandConfig, + }, + // 虚拟滚动配置,y轴大于xx条数据时启用虚拟滚动 + scrollY: { + gt: 30, + }, + scrollX: { + gt: 20, + // 暂时关闭左右虚拟滚动 + enabled: false, + }, + radioConfig: { + // 保留勾选状态 + reserve: true, + highlight: true, + }, + checkboxConfig: { + // 保留勾选状态 + reserve: true, + highlight: true, + }, + mouseConfig: { selected: false }, + keyboardConfig: { + // 删除键功能 + isDel: false, + // Esc键关闭编辑功能 + isEsc: true, + // Tab 键功能 + isTab: true, + // 任意键进入编辑(功能键除外) + isEdit: true, + // 方向键功能 + isArrow: true, + // 回车键功能 + isEnter: true, + // 如果功能被支持,用于 column.type=checkbox|radio,开启空格键切换复选框或单选框状态功能 + isChecked: true, + }, + }), + selectedRows: ref([]), + selectedRowIds: ref([]), disabledRowIds: [], statistics: reactive({ has: false, sum: [], average: [], }), - authsMap, + authsMap: ref(null), innerEditRules: {}, innerLinkageConfig: new Map(), reloadEffectRowKeysMap: reactive({}), }; - // update-end--author:liaozhiyang---date:20260130---for:【QQYUN-14177】online配置界面,字段配置卡顿 } export function useRefs(): JVxeRefs { diff --git a/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useDataSource.ts b/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useDataSource.ts index 7bc9963fe..3810e539f 100644 --- a/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useDataSource.ts +++ b/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useDataSource.ts @@ -22,20 +22,15 @@ export function useDataSource(props, data: JVxeDataProps, methods: JVxeTableMeth { immediate: true } ); } -// update-begin--author:liaozhiyang---date:20260130---for:【QQYUN-14177】online配置界面,字段配置卡顿 -function waitRef($ref, maxTries = 100) { + +function waitRef($ref) { return new Promise((resolve) => { - let tries = 0; (function next() { if ($ref.value) { resolve($ref); - } else if (tries >= maxTries) { - resolve(null); } else { - tries++; nextTick(() => next()); } })(); }); } -// update-end--author:liaozhiyang---date:20260130---for:【QQYUN-14177】online配置界面,字段配置卡顿 diff --git a/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useFinallyProps.ts b/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useFinallyProps.ts index 4a7bf9ad5..90351ef3e 100644 --- a/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useFinallyProps.ts +++ b/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useFinallyProps.ts @@ -1,5 +1,5 @@ -import { unref, computed, ref, watch, nextTick, shallowRef } from 'vue'; -import { merge, debounce, throttle } from 'lodash-es'; +import { unref, computed, ref, watch, nextTick } from 'vue'; +import { merge, debounce } from 'lodash-es'; import { isArray } from '/@/utils/is'; import { useAttrs } from '/@/hooks/core/useAttrs'; import { useKeyboardEdit } from '../hooks/useKeyboardEdit'; @@ -11,19 +11,12 @@ export function useFinallyProps(props: JVxeTableProps, data: JVxeDataProps, meth const { keyboardEditConfig } = useKeyboardEdit(props); // vxe 最终 editRules const vxeEditRules = computed(() => merge({}, props.editRules, data.innerEditRules)); - // ==================== 性能优化 - 开始 ==================== - // 使用节流优化高频事件 - const throttledScroll = throttle(methods.handleVxeScroll, 16); // 约60fps - const throttledCellClick = throttle(methods.handleCellClick, 100); - // vxe 最终 events const vxeEvents = computed(() => { let listeners = { ...unref(attrs) }; let events = { - // update-begin--author:liaozhiyang---date:20260130---for:【QQYUN-14177】online配置界面,字段配置卡顿 - onScroll: throttledScroll, - onCellClick: throttledCellClick, - // update-end--author:liaozhiyang---date:20260130---for:【QQYUN-14177】online配置界面,字段配置卡顿 + onScroll: methods.handleVxeScroll, + onCellClick: methods.handleCellClick, onEditClosed: methods.handleEditClosed, onEditActived: methods.handleEditActived, onRadioChange: methods.handleVxeRadioChange, @@ -118,20 +111,14 @@ export function useFinallyProps(props: JVxeTableProps, data: JVxeDataProps, meth ); }); - // update-begin--author:liaozhiyang---date:20260130---for:【QQYUN-14177】online配置界面,字段配置卡顿 - // 使用 shallowRef 优化列更新性能 - const vxeColumnsRef = shallowRef([]) + // 代码逻辑说明: 【issues/8593】修复列改变后内容不刷新 + const vxeColumnsRef = ref(data.vxeColumns!.value || []) const watchColumnsDebounce = debounce(async () => { vxeColumnsRef.value = [] await nextTick() - vxeColumnsRef.value = data.vxeColumns?.value || [] - }, 16) // 减少防抖时间到16ms,提高响应速度 - - // 安全地监听列变化 - if (data.vxeColumns) { - watch(data.vxeColumns, watchColumnsDebounce) - } - // update-end--author:liaozhiyang---date:20260130---for:【QQYUN-14177】online配置界面,字段配置卡顿 + vxeColumnsRef.value = data.vxeColumns!.value + }, 50) + watch(data.vxeColumns!, watchColumnsDebounce) const vxeProps = computed(() => { return { diff --git a/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useMethods.ts b/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useMethods.ts index 7f3c118f2..b728ccd94 100644 --- a/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useMethods.ts +++ b/jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useMethods.ts @@ -4,7 +4,7 @@ import { simpleDebounce } from '/@/utils/common/compUtils'; import { JVxeDataProps, JVxeRefs, JVxeTableProps, JVxeTypes } from '../types'; import { getEnhanced } from '../utils/enhancedUtils'; import { VxeTableInstance, VxeTablePrivateMethods } from 'vxe-table'; -import { cloneDeep, throttle } from 'lodash-es'; +import { cloneDeep } from 'lodash-es'; import { isArray, isEmpty, isNull, isString } from '/@/utils/is'; import { useLinkage } from './useLinkage'; import { useWebSocket } from './useWebSocket'; @@ -67,7 +67,7 @@ export function useMethods(props: JVxeTableProps, { emit }, data: JVxeDataProps, }; /** 监听vxe滚动条位置 */ - const throttledScroll = throttle((event) => { + function handleVxeScroll(event) { let { scroll } = data; // 记录滚动条的位置 @@ -77,12 +77,7 @@ export function useMethods(props: JVxeTableProps, { emit }, data: JVxeDataProps, refs.subPopoverRef.value?.close(); data.scrolling.value = true; closeScrolling(); - }, 16); - // update-begin--author:liaozhiyang---date:20260130---for:【QQYUN-14177】online配置界面,字段配置卡顿 - function handleVxeScroll(event) { - throttledScroll(event); } - // update-begin--author:liaozhiyang---date:20260130---for:【QQYUN-14177】online配置界面,字段配置卡顿 // 当手动勾选单选时触发的事件 function handleVxeRadioChange(event) {