jvxetable 使用编辑的时候卡顿问题 #8695

This commit is contained in:
JEECG 2026-01-30 14:27:37 +08:00
parent 22666ade0c
commit 80b8573232
6 changed files with 279 additions and 186 deletions

View File

@ -1,4 +1,4 @@
import { defineComponent, h, nextTick, ref, useSlots } from 'vue'; import { defineComponent, h, nextTick, useSlots, shallowRef, markRaw } from 'vue';
import { vxeEmits, vxeProps } from './vxe.data'; import { vxeEmits, vxeProps } from './vxe.data';
import { useData, useRefs, useResolveComponent as rc } from './hooks/useData'; import { useData, useRefs, useResolveComponent as rc } from './hooks/useData';
import { useColumns } from './hooks/useColumns'; import { useColumns } from './hooks/useColumns';
@ -17,7 +17,10 @@ export default defineComponent({
props: vxeProps(), props: vxeProps(),
emits: [...vxeEmits], emits: [...vxeEmits],
setup(props: JVxeTableProps, context) { setup(props: JVxeTableProps, context) {
const instanceRef = ref(); // update-begin--author:liaozhiyang---date:20260130---for:QQYUN-14177online
// 使 shallowRef
const instanceRef = shallowRef();
// update-begin--author:liaozhiyang---date:20260130---for:QQYUN-14177online
const refs = useRefs(); const refs = useRefs();
const slots = useSlots(); const slots = useSlots();
const data = useData(props); const data = useData(props);
@ -33,6 +36,9 @@ export default defineComponent({
const finallyProps = useFinallyProps(props, data, methods); const finallyProps = useFinallyProps(props, data, methods);
// //
const renderComponents = useRenderComponents(props, data, methods, slots); const renderComponents = useRenderComponents(props, data, methods, slots);
// update-begin--author:liaozhiyang---date:20260130---for:QQYUN-14177online
markRaw(renderComponents);
// update-end--author:liaozhiyang---date:20260130---for:QQYUN-14177online
return { return {
instanceRef, instanceRef,
...refs, ...refs,

View File

@ -1,7 +1,7 @@
import type { JVxeColumn, JVxeDataProps, JVxeTableProps } from '../types'; import type { JVxeColumn, JVxeDataProps, JVxeTableProps } from '../types';
import { computed, nextTick, toRaw } from 'vue'; import { computed, nextTick, toRaw, shallowRef, markRaw } from 'vue';
import { isArray, isEmpty, isPromise } from '/@/utils/is'; import { isArray, isEmpty, isPromise } from '/@/utils/is';
import { cloneDeep } from 'lodash-es'; import { cloneDeep, debounce } from 'lodash-es';
import { JVxeTypePrefix, JVxeTypes } from '../types/JVxeTypes'; import { JVxeTypePrefix, JVxeTypes } from '../types/JVxeTypes';
import { initDictOptions } from '/@/utils/dict'; import { initDictOptions } from '/@/utils/dict';
import { pushIfNotExist } from '/@/utils/common/compUtils'; import { pushIfNotExist } from '/@/utils/common/compUtils';
@ -24,97 +24,129 @@ export interface HandleArgs {
} }
export function useColumns(props: JVxeTableProps, data: JVxeDataProps, methods: JVxeTableMethods, slots) { export function useColumns(props: JVxeTableProps, data: JVxeDataProps, methods: JVxeTableMethods, slots) {
// update-begin--author:liaozhiyang---date:20260130---for:QQYUN-14177online
// 使 shallowRef
const columnsCache = shallowRef<JVxeColumn[]>([]);
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---forTV360X-371*
customComponentAddStar(columns);
// update-end--author:liaozhiyang---date:2024-05-30---forTV360X-371*
//
columnsCache.value = markRaw(columns);
}, 16); // 16ms
data.vxeColumns = computed(() => { data.vxeColumns = computed(() => {
// issues/7812linkageConfigvxetable
// linkageConfig // linkageConfig
const linkageConfig = toRaw(props.linkageConfig); const linkageConfig = toRaw(props.linkageConfig);
if (linkageConfig) { if (linkageConfig) {
// console.log(linkageConfig); // console.log(linkageConfig);
} }
let columns: JVxeColumn[] = []; //
if (isArray(props.columns)) { debouncedComputeColumns();
// handle return columnsCache.value;
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---forTV360X-371*
customComponentAddStar(columns);
}
return columns;
}); });
// update-end--author:liaozhiyang---date:20260130---for:QQYUN-14177online
} }
/** /**

View File

@ -1,4 +1,4 @@
import { ref, reactive, provide, resolveComponent } from 'vue'; import { ref, reactive, provide, resolveComponent, shallowRef, markRaw } from 'vue';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { JVxeDataProps, JVxeRefs, JVxeTableProps } from '../types'; import { JVxeDataProps, JVxeRefs, JVxeTableProps } from '../types';
import { VxeGridInstance } from 'vxe-table'; import { VxeGridInstance } from 'vxe-table';
@ -7,90 +7,108 @@ import { randomString } from '/@/utils/common/compUtils';
export function useData(props: JVxeTableProps): JVxeDataProps { export function useData(props: JVxeTableProps): JVxeDataProps {
const { prefixCls } = useDesign('j-vxe-table'); const { prefixCls } = useDesign('j-vxe-table');
provide('prefixCls', prefixCls); provide('prefixCls', prefixCls);
// update-begin--author:liaozhiyang---date:20260130---for:QQYUN-14177online
// 使 shallowRef
const vxeDataSource = shallowRef([]);
//
const defaultVxeProps = markRaw({
// update-begin--author:liaozhiyang---date:20240607---forTV360X-327vxetable
// rowId: props.rowKey,
rowConfig: {
keyField: props.rowKey,
// hover
isHover: true,
},
// update-end--author:liaozhiyang---date:20240607---forTV360X-327vxetable
// --- issues/209tooltiptitle ---
// tooltip
showOverflow: "title",
// tooltip
showHeaderOverflow: "title",
// --- issues/209tooltiptitle ---
showFooterOverflow: true,
//
editConfig: {
trigger: 'click',
mode: 'cell',
// update-begin--author:liaozhiyang---date:20231013---forQQYUN-5133JVxeTable
//activeMethod: () => !props.disabled,
beforeEditMethod: () => !props.disabled,
// update-end--author:liaozhiyang---date:20231013---forQQYUN-5133JVxeTable
},
expandConfig: {
iconClose: 'vxe-icon-arrow-right',
iconOpen: 'vxe-icon-arrow-down',
...props.expandConfig,
},
// yxx
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<any[]>([]);
const selectedRowIds = shallowRef<string[]>([]);
const authsMap = shallowRef(null);
return { return {
prefixCls: prefixCls, prefixCls: prefixCls,
caseId: `j-vxe-${randomString(8)}`, caseId: `j-vxe-${randomString(8)}`,
vxeDataSource: ref([]), vxeDataSource,
scroll: reactive({ top: 0, left: 0 }), scroll: reactive({ top: 0, left: 0 }),
scrolling: ref(false), scrolling: ref(false),
defaultVxeProps: reactive({ defaultVxeProps,
// rowId: props.rowKey, selectedRows,
rowConfig: { selectedRowIds,
keyField: props.rowKey,
// hover
isHover: true,
},
// --- issues/209tooltiptitle ---
// tooltip
showOverflow: "title",
// tooltip
showHeaderOverflow: "title",
// --- issues/209tooltiptitle ---
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,
},
// yxx
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<any[]>([]),
selectedRowIds: ref<string[]>([]),
disabledRowIds: [], disabledRowIds: [],
statistics: reactive({ statistics: reactive({
has: false, has: false,
sum: [], sum: [],
average: [], average: [],
}), }),
authsMap: ref(null), authsMap,
innerEditRules: {}, innerEditRules: {},
innerLinkageConfig: new Map<string, any>(), innerLinkageConfig: new Map<string, any>(),
reloadEffectRowKeysMap: reactive({}), reloadEffectRowKeysMap: reactive({}),
}; };
// update-end--author:liaozhiyang---date:20260130---for:QQYUN-14177online
} }
export function useRefs(): JVxeRefs { export function useRefs(): JVxeRefs {

View File

@ -1,36 +1,55 @@
import { nextTick, watch } from 'vue'; import { nextTick, watch } from 'vue';
import { JVxeDataProps, JVxeRefs, JVxeTableMethods } from '../types'; import { JVxeDataProps, JVxeRefs, JVxeTableMethods } from '../types';
import { cloneDeep } from 'lodash-es'; import { cloneDeep, debounce } from 'lodash-es';
export function useDataSource(props, data: JVxeDataProps, methods: JVxeTableMethods, refs: JVxeRefs) { export function useDataSource(props, data: JVxeDataProps, methods: JVxeTableMethods, refs: JVxeRefs) {
// update-begin--author:liaozhiyang---date:20260130---for:QQYUN-14177online
// 使
const processDataSource = debounce(async (newDataSource) => {
if (!Array.isArray(newDataSource)) {
data.vxeDataSource.value = [];
return;
}
data.vxeDataSource.value = cloneDeep(newDataSource);
//
const disabledRowIds: string[] = [];
data.vxeDataSource.value.forEach((row, rowIndex) => {
//
if (methods.isDisabledRow(row, rowIndex)) {
disabledRowIds.push(row.id);
}
//
methods.handleLinkageBackData(row);
});
data.disabledRowIds = disabledRowIds;
const grid = await waitRef(refs.gridRef);
if (grid?.value) methods.recalcSortNumber();
}, 50); // 50ms
watch( watch(
() => props.dataSource, () => props.dataSource,
async () => { (newDataSource) => {
data.disabledRowIds = []; processDataSource(newDataSource);
data.vxeDataSource.value = cloneDeep(props.dataSource);
data.vxeDataSource.value.forEach((row, rowIndex) => {
//
if (methods.isDisabledRow(row, rowIndex)) {
data.disabledRowIds.push(row.id);
}
//
methods.handleLinkageBackData(row);
});
await waitRef(refs.gridRef);
methods.recalcSortNumber();
}, },
{ immediate: true } { immediate: true }
); );
// update-end--author:liaozhiyang---date:20260130---for:QQYUN-14177online
} }
// update-begin--author:liaozhiyang---date:20260130---for:QQYUN-14177online
function waitRef($ref) { function waitRef($ref, maxTries = 10) {
return new Promise<any>((resolve) => { return new Promise<any>((resolve) => {
let tries = 0;
(function next() { (function next() {
if ($ref.value) { if ($ref.value) {
resolve($ref); resolve($ref);
} else if (tries >= maxTries) {
resolve(null);
} else { } else {
tries++;
nextTick(() => next()); nextTick(() => next());
} }
})(); })();
}); });
} }
// update-end--author:liaozhiyang---date:20260130---for:QQYUN-14177online

View File

@ -1,5 +1,5 @@
import { unref, computed, ref, watch, nextTick } from 'vue'; import { unref, computed, ref, watch, nextTick, shallowRef } from 'vue';
import { merge, debounce } from 'lodash-es'; import { merge, debounce, throttle } from 'lodash-es';
import { isArray } from '/@/utils/is'; import { isArray } from '/@/utils/is';
import { useAttrs } from '/@/hooks/core/useAttrs'; import { useAttrs } from '/@/hooks/core/useAttrs';
import { useKeyboardEdit } from '../hooks/useKeyboardEdit'; import { useKeyboardEdit } from '../hooks/useKeyboardEdit';
@ -11,12 +11,19 @@ export function useFinallyProps(props: JVxeTableProps, data: JVxeDataProps, meth
const { keyboardEditConfig } = useKeyboardEdit(props); const { keyboardEditConfig } = useKeyboardEdit(props);
// vxe editRules // vxe editRules
const vxeEditRules = computed(() => merge({}, props.editRules, data.innerEditRules)); const vxeEditRules = computed(() => merge({}, props.editRules, data.innerEditRules));
// ==================== - ====================
// 使
const throttledScroll = throttle(methods.handleVxeScroll, 16); // 60fps
const throttledCellClick = throttle(methods.handleCellClick, 100);
// vxe events // vxe events
const vxeEvents = computed(() => { const vxeEvents = computed(() => {
let listeners = { ...unref(attrs) }; let listeners = { ...unref(attrs) };
let events = { let events = {
onScroll: methods.handleVxeScroll, // update-begin--author:liaozhiyang---date:20260130---for:QQYUN-14177online
onCellClick: methods.handleCellClick, onScroll: throttledScroll,
onCellClick: throttledCellClick,
// update-end--author:liaozhiyang---date:20260130---for:QQYUN-14177online
onEditClosed: methods.handleEditClosed, onEditClosed: methods.handleEditClosed,
onEditActived: methods.handleEditActived, onEditActived: methods.handleEditActived,
onRadioChange: methods.handleVxeRadioChange, onRadioChange: methods.handleVxeRadioChange,
@ -111,14 +118,20 @@ export function useFinallyProps(props: JVxeTableProps, data: JVxeDataProps, meth
); );
}); });
// : issues/8593 // update-begin--author:liaozhiyang---date:20260130---for:QQYUN-14177online
const vxeColumnsRef = ref(data.vxeColumns!.value || []) // 使 shallowRef
const vxeColumnsRef = shallowRef([])
const watchColumnsDebounce = debounce(async () => { const watchColumnsDebounce = debounce(async () => {
vxeColumnsRef.value = [] vxeColumnsRef.value = []
await nextTick() await nextTick()
vxeColumnsRef.value = data.vxeColumns!.value vxeColumnsRef.value = data.vxeColumns?.value || []
}, 50) }, 16) // 16ms
watch(data.vxeColumns!, watchColumnsDebounce)
//
if (data.vxeColumns) {
watch(data.vxeColumns, watchColumnsDebounce)
}
// update-end--author:liaozhiyang---date:20260130---for:QQYUN-14177online
const vxeProps = computed(() => { const vxeProps = computed(() => {
return { return {

View File

@ -4,7 +4,7 @@ import { simpleDebounce } from '/@/utils/common/compUtils';
import { JVxeDataProps, JVxeRefs, JVxeTableProps, JVxeTypes } from '../types'; import { JVxeDataProps, JVxeRefs, JVxeTableProps, JVxeTypes } from '../types';
import { getEnhanced } from '../utils/enhancedUtils'; import { getEnhanced } from '../utils/enhancedUtils';
import { VxeTableInstance, VxeTablePrivateMethods } from 'vxe-table'; import { VxeTableInstance, VxeTablePrivateMethods } from 'vxe-table';
import { cloneDeep } from 'lodash-es'; import { cloneDeep, throttle } from 'lodash-es';
import { isArray, isEmpty, isNull, isString } from '/@/utils/is'; import { isArray, isEmpty, isNull, isString } from '/@/utils/is';
import { useLinkage } from './useLinkage'; import { useLinkage } from './useLinkage';
import { useWebSocket } from './useWebSocket'; import { useWebSocket } from './useWebSocket';
@ -67,7 +67,7 @@ export function useMethods(props: JVxeTableProps, { emit }, data: JVxeDataProps,
}; };
/** 监听vxe滚动条位置 */ /** 监听vxe滚动条位置 */
function handleVxeScroll(event) { const throttledScroll = throttle((event) => {
let { scroll } = data; let { scroll } = data;
// //
@ -77,7 +77,12 @@ export function useMethods(props: JVxeTableProps, { emit }, data: JVxeDataProps,
refs.subPopoverRef.value?.close(); refs.subPopoverRef.value?.close();
data.scrolling.value = true; data.scrolling.value = true;
closeScrolling(); closeScrolling();
}, 16);
// update-begin--author:liaozhiyang---date:20260130---for:QQYUN-14177online
function handleVxeScroll(event) {
throttledScroll(event);
} }
// update-begin--author:liaozhiyang---date:20260130---for:QQYUN-14177online
// //
function handleVxeRadioChange(event) { function handleVxeRadioChange(event) {