diff --git a/src/table/base-table.tsx b/src/table/base-table.tsx index 8a544a28b9..28ae557095 100644 --- a/src/table/base-table.tsx +++ b/src/table/base-table.tsx @@ -107,7 +107,7 @@ export default defineComponent({ // 列宽拖拽逻辑 const columnResizeParams = useColumnResize(tableContentRef, refreshTable, getThWidthList, updateThWidthList); - const { resizeLineRef, resizeLineStyle, recalculateColWidth } = columnResizeParams; + const { resizeLineRef, resizeLineStyle, recalculateColWidth, setEffectColMap } = columnResizeParams; setRecalculateColWidthFuncRef(recalculateColWidth); const dynamicBaseTableClasses = computed(() => [ @@ -156,6 +156,16 @@ export default defineComponent({ { immediate: true }, ); + watch( + thList, + () => { + setEffectColMap(thList.value[0], null); + }, + { + immediate: true, + }, + ); + const onFixedChange = () => { nextTick(() => { onHorizontalScroll(); diff --git a/src/table/hooks/useColumnResize.ts b/src/table/hooks/useColumnResize.ts index 0e0db4d911..cb066a9cef 100644 --- a/src/table/hooks/useColumnResize.ts +++ b/src/table/hooks/useColumnResize.ts @@ -2,6 +2,8 @@ import { ref, Ref, reactive, CSSProperties } from 'vue'; import isNumber from 'lodash/isNumber'; import { BaseTableCol, TableRowData } from '../type'; import { RecalculateColumnWidthFunc } from '../interface'; +import setThWidthListByColumnDrag from '../../_common/js/table/set-column-width-by-drag'; +import recalculateColumnWidth from '../../_common/js/table/recalculate-column-width'; const DEFAULT_MIN_WIDTH = 80; const DEFAULT_MAX_WIDTH = 600; @@ -14,6 +16,23 @@ export default function useColumnResize( ) { const resizeLineRef = ref(); const notCalculateWidthCols = ref([]); + const effectColMap = ref<{ [colKey: string]: any }>({}); + + // 递归查找列宽度变化后,受影响的相关列 + const setEffectColMap = (nodes: BaseTableCol[], parent: BaseTableCol | null) => { + if (!nodes) return; + nodes.forEach((n, index) => { + const parentPrevCol = parent ? effectColMap.value[parent.colKey].prev : nodes[index + 1]; + const parentNextCol = parent ? effectColMap.value[parent.colKey].next : nodes[index - 1]; + const prev = index === 0 ? parentPrevCol : nodes[index - 1]; + const next = index === nodes.length - 1 ? parentNextCol : nodes[index + 1]; + effectColMap.value[n.colKey] = { + prev, + next, + }; + setEffectColMap(n.children, n); + }); + }; const resizeLineParams = { isDragging: false, @@ -67,22 +86,32 @@ export default function useColumnResize( }; // 调整表格列宽 - const onColumnMousedown = ( - e: MouseEvent, - col: BaseTableCol, - effectNextCol: BaseTableCol, - effectPrevCol: BaseTableCol, - ) => { + const onColumnMousedown = (e: MouseEvent, col: BaseTableCol) => { // 非 resize 的点击,不做处理 if (!resizeLineParams.draggingCol) return; + const getMinMaxColWidth = (col: BaseTableCol, effectPrevCol: BaseTableCol) => { + let targetCol = null; + if (resizeLineParams.effectCol === 'next') { + targetCol = col; + } else { + targetCol = effectPrevCol; + } + const propMinWidth = isNumber(targetCol.minWidth) ? targetCol.minWidth : parseFloat(targetCol.minWidth); + return { + minColWidth: Math.max(targetCol.resize?.minWidth || DEFAULT_MIN_WIDTH, propMinWidth || DEFAULT_MIN_WIDTH), + maxColWidth: targetCol.resize?.maxWidth || DEFAULT_MAX_WIDTH, + }; + }; + const target = resizeLineParams.draggingCol; const targetBoundRect = target.getBoundingClientRect(); const tableBoundRect = tableContentRef.value?.getBoundingClientRect(); const resizeLinePos = targetBoundRect.right - tableBoundRect.left; const colLeft = targetBoundRect.left - tableBoundRect.left; - const minColWidth = col.resize?.minWidth || DEFAULT_MIN_WIDTH; - const maxColWidth = col.resize?.maxWidth || DEFAULT_MAX_WIDTH; + const effectNextCol = effectColMap.value[col.colKey].next; + const effectPrevCol = effectColMap.value[col.colKey].prev; + const { minColWidth, maxColWidth } = getMinMaxColWidth(col, effectPrevCol); const minResizeLineLeft = colLeft + minColWidth; const maxResizeLineLeft = colLeft + maxColWidth; @@ -99,26 +128,6 @@ export default function useColumnResize( resizeLineStyle.bottom = `${parent.bottom - tableBoundRect.bottom}px`; } - const setThWidthListByColumnDrag = ( - dragCol: BaseTableCol, - dragWidth: number, - nearCol: BaseTableCol, - ) => { - const thWidthList = getThWidthList(); - - const propColWidth = isNumber(dragCol.width) ? dragCol.width : parseFloat(dragCol.width); - const propNearColWidth = isNumber(nearCol.width) ? nearCol.width : parseFloat(nearCol.width); - const oldWidth = thWidthList[dragCol.colKey] || propColWidth; - const oldNearWidth = thWidthList[nearCol.colKey] || propNearColWidth; - - updateThWidthList({ - [dragCol.colKey]: dragWidth, - [nearCol.colKey]: Math.max(nearCol.resize?.minWidth || DEFAULT_MIN_WIDTH, oldWidth + oldNearWidth - dragWidth), - }); - - setNotCalculateWidthCols([dragCol.colKey, nearCol.colKey]); - }; - // 拖拽时鼠标可能会超出 table 范围,需要给 document 绑定拖拽相关事件 const onDragEnd = () => { if (resizeLineParams.isDragging) { @@ -132,9 +141,27 @@ export default function useColumnResize( } // 更新列宽 if (resizeLineParams.effectCol === 'next') { - setThWidthListByColumnDrag(col, width, effectNextCol); + setThWidthListByColumnDrag>( + col, + width, + effectNextCol, + { getThWidthList, DEFAULT_MIN_WIDTH }, + (updateMap, notCalculateCols) => { + updateThWidthList(updateMap); + setNotCalculateWidthCols(notCalculateCols); + }, + ); } else if (resizeLineParams.effectCol === 'prev') { - setThWidthListByColumnDrag(effectPrevCol, width, col); + setThWidthListByColumnDrag>( + effectPrevCol, + width, + col, + { getThWidthList, DEFAULT_MIN_WIDTH }, + (updateMap, notCalculateCols) => { + updateThWidthList(updateMap); + setNotCalculateWidthCols(notCalculateCols); + }, + ); } // 恢复设置 @@ -175,108 +202,19 @@ export default function useColumnResize( tableLayout: string, tableElmWidth: number, ): void => { - let actualWidth = 0; - const missingWidthCols: BaseTableCol[] = []; - const thMap: { [colKey: string]: number } = {}; - - // 计算现有列的列宽总和 - columns.forEach((col) => { - if (!thWidthList[col.colKey]) { - thMap[col.colKey] = isNumber(col.width) ? col.width : parseFloat(col.width); - } else { - thMap[col.colKey] = thWidthList[col.colKey]; - } - const originWidth = thMap[col.colKey]; - if (originWidth) { - actualWidth += originWidth; - } else { - missingWidthCols.push(col); - } - }); - - let tableWidth = tableElmWidth; - let needUpdate = false; - // 表宽没有初始化时,默认给没有指定列宽的列指定宽度为100px - if (tableWidth > 0) { - // 存在没有指定列宽的列 - if (missingWidthCols.length) { - // 当前列宽总宽度小于表宽,将剩余宽度平均分配给未指定宽度的列 - if (actualWidth < tableWidth) { - const widthDiff = tableWidth - actualWidth; - const avgWidth = widthDiff / missingWidthCols.length; - missingWidthCols.forEach((col) => { - thMap[col.colKey] = avgWidth; - }); - } else if (tableLayout === 'fixed') { - // 当前列表总宽度大于等于表宽,且当前排版模式为fixed,默认填充100px - missingWidthCols.forEach((col) => { - const originWidth = thMap[col.colKey] || 100; - thMap[col.colKey] = isNumber(originWidth) ? originWidth : parseFloat(originWidth); - }); - } else { - // 当前列表总宽度大于等于表宽,且当前排版模式为auto,默认填充100px,然后按比例重新分配各列宽度 - const extraWidth = missingWidthCols.length * 100; - const totalWidth = extraWidth + actualWidth; - columns.forEach((col) => { - if (!thMap[col.colKey]) { - thMap[col.colKey] = (100 / totalWidth) * tableWidth; - } else { - thMap[col.colKey] = (thMap[col.colKey] / totalWidth) * tableWidth; - } - }); - } - needUpdate = true; - } else { - // 所有列都已经指定宽度 + recalculateColumnWidth>( + columns, + thWidthList, + tableLayout, + tableElmWidth, + notCalculateWidthCols.value, + (widthMap) => { + updateThWidthList(widthMap); if (notCalculateWidthCols.value.length) { - // 存在不允许重新计算宽度的列(一般是resize后的两列),这些列不参与后续计算 - let sum = 0; - notCalculateWidthCols.value.forEach((colKey) => { - sum += thMap[colKey]; - }); - actualWidth -= sum; - tableWidth -= sum; - } - // 重新计算其他列的宽度,按表格剩余宽度进行按比例分配 - if (actualWidth !== tableWidth || notCalculateWidthCols.value.length) { - columns.forEach((col) => { - if (notCalculateWidthCols.value.includes(col.colKey)) return; - thMap[col.colKey] = (thMap[col.colKey] / actualWidth) * tableWidth; - }); - needUpdate = true; - } - } - } else { - // 表格宽度未初始化,默认填充100px - missingWidthCols.forEach((col) => { - const originWidth = thMap[col.colKey] || 100; - thMap[col.colKey] = isNumber(originWidth) ? originWidth : parseFloat(originWidth); - }); - - needUpdate = true; - } - - // 列宽转为整数 - if (needUpdate) { - let addon = 0; - Object.keys(thMap).forEach((key) => { - const width = thMap[key]; - addon += width - Math.floor(width); - thMap[key] = Math.floor(width) + (addon > 1 ? 1 : 0); - if (addon > 1) { - addon -= 1; + notCalculateWidthCols.value = []; } - }); - if (addon > 0.5) { - thMap[columns[0].colKey] += 1; - } - } - - updateThWidthList(thMap); - - if (notCalculateWidthCols.value.length) { - notCalculateWidthCols.value = []; - } + }, + ); }; return { @@ -285,5 +223,6 @@ export default function useColumnResize( onColumnMouseover, onColumnMousedown, recalculateColWidth, + setEffectColMap, }; } diff --git a/src/table/hooks/useFixed.ts b/src/table/hooks/useFixed.ts index a3e8c22d33..ec14ff4092 100644 --- a/src/table/hooks/useFixed.ts +++ b/src/table/hooks/useFixed.ts @@ -416,6 +416,10 @@ export default function useFixed( }, 0); }; + const resetThWidthList = () => { + thWidthList.value = {}; + }; + const emitScrollEvent = (e: WheelEvent) => { props.onScrollX?.({ e }); props.onScrollY?.({ e }); @@ -479,6 +483,14 @@ export default function useFixed( { immediate: true }, ); + watch(finalColumns, () => { + updateTableWidth(); + resetThWidthList(); + if (columnResizable.value) { + recalculateColWidth.value(finalColumns.value, thWidthList.value, tableLayout.value, tableElmWidth.value); + } + }); + const refreshTable = debounce(() => { updateTableWidth(); updateFixedHeader(); diff --git a/src/table/thead.tsx b/src/table/thead.tsx index a2b0b6683b..d01c8d677c 100644 --- a/src/table/thead.tsx +++ b/src/table/thead.tsx @@ -26,12 +26,7 @@ export interface TheadProps { resizeLineRef: Ref; resizeLineStyle: CSSProperties; onColumnMouseover: (e: MouseEvent) => void; - onColumnMousedown: ( - e: MouseEvent, - col: BaseTableCol, - effectNextCol: BaseTableCol, - effectPrevCol: BaseTableCol, - ) => void; + onColumnMousedown: (e: MouseEvent, col: BaseTableCol) => void; }; resizable: Boolean; } @@ -114,13 +109,7 @@ export default defineComponent({ const innerTh = renderTitle(this.slots, col, index); const resizeColumnListener = this.resizable ? { - onMousedown: (e: MouseEvent) => - this.columnResizeParams?.onColumnMousedown?.( - e, - col, - index < row.length - 1 ? row[index + 1] : row[index - 1], - index > 0 ? row[index - 1] : row[index + 1], - ), + onMousedown: (e: MouseEvent) => this.columnResizeParams?.onColumnMousedown?.(e, col), onMousemove: (e: MouseEvent) => this.columnResizeParams?.onColumnMouseover?.(e), } : {};