Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(table): 列宽调整需要考虑列的最小宽度 #803

Merged
merged 6 commits into from
Sep 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 146 additions & 40 deletions js/table/recalculate-column-width.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,147 @@
import isNumber from 'lodash/isNumber';
import { BaseTableCol } from './types';
import { BaseTableCol, ThMap } from './types';
import { getColWidthAttr } from './utils';

export default function recalculateColumnWidth<T extends BaseTableCol<T>>(
/**
* 填充未设置width属性的列
* @param columns 当前表格所有列
* @param missingWidthCols 未设置width属性的列
* @param thWidthList 列宽记录字典
* @param tableLayout 表格布局
* @param actualWidth 各列实际总列宽
* @param tableWidth 表格总宽度
*/
const setMissingColumnWidth = <T extends BaseTableCol<T>>(
columns: T[],
missingWidthCols: T[],
thWidthList: ThMap,
tableLayout: string,
actualWidth: number,
tableWidth: number
) : void => {
const thMap = thWidthList;
// 当前列宽总宽度小于表宽,将剩余宽度平均分配给未指定宽度的列
if (actualWidth < tableWidth) {
let widthDiff = tableWidth - actualWidth;
const remainCols: T[] = [];
// 优先保证设置了minWidth的列满足最小宽度
missingWidthCols.forEach((col) => {
const minWidth = getColWidthAttr(col, 'minWidth');
if (minWidth) {
thMap[col.colKey] = minWidth;
widthDiff -= minWidth;
} else {
remainCols.push(col);
}
});

// 如果剩余宽度 > 0
if (widthDiff > 0) {
// 如果存在未设置minWidth的列,这些列均分剩余宽度
if (remainCols.length) {
const avgWidth = widthDiff / remainCols.length;
remainCols.forEach((col) => {
thMap[col.colKey] = avgWidth;
});
} else {
// 否则所有列均分剩余宽度
const avgWidth = widthDiff / missingWidthCols.length;
missingWidthCols.forEach((col) => {
thMap[col.colKey] += avgWidth;
});
}
} else {
// 剩余宽度 <= 0, 所有剩余列默认填充100px
remainCols.forEach((col) => {
thMap[col.colKey] = 100;
});
}
} else if (tableLayout === 'fixed') {
// 当前列表总宽度大于等于表宽,且当前排版模式为fixed,默认填充minWidth || 100px
missingWidthCols.forEach((col) => {
thMap[col.colKey] = getColWidthAttr(col, 'minWidth') || 100;
});
} else {
// 当前列表总宽度大于等于表宽,且当前排版模式为aut
// 默认填充minWidth || 100px,然后按比例重新分配各列宽度
let extraWidth = 0;
missingWidthCols.forEach((col) => {
extraWidth += getColWidthAttr(col, 'minWidth') || 100;
});
const totalWidth = extraWidth + actualWidth;
columns.forEach((col) => {
if (!thMap[col.colKey]) {
const colWidth = getColWidthAttr(col, 'minWidth') || 100;
thMap[col.colKey] = (colWidth / totalWidth) * tableWidth;
} else {
thMap[col.colKey] = (thMap[col.colKey] / totalWidth) * tableWidth;
}
});
}
};

/**
* 设置所有列的宽度
* @param columns 当前表格所有列
* @param thWidthList 列宽记录字典
* @param actualWidth 各列实际总列宽
* @param tableWidth 表格总宽度
* @param notCalculateWidthCols 不需要参与计算的列id
*/
const setNormalColumnWidth = <T extends BaseTableCol<T>>(
columns: T[],
thWidthList: ThMap,
actualWidth: number,
tableWidth: number,
notCalculateWidthCols: string[]
) : void => {
const thMap = thWidthList;
columns.forEach((col) => {
if (notCalculateWidthCols.includes(col.colKey)) return;
thMap[col.colKey] = (thMap[col.colKey] / actualWidth) * tableWidth;
});
};

/**
* 表格未初始化时默认填充各列宽度
* @param missingWidthCols 未设置width属性的列
* @param thWidthList 列宽记录字典
*/
const setInitialColumnWidth = <T extends BaseTableCol<T>>(
missingWidthCols: T[],
thWidthList: { [colKey: string]: number },
) : void => {
const thMap = thWidthList;
// 表格宽度未初始化,默认填充minWidth || 100px
missingWidthCols.forEach((col) => {
thMap[col.colKey] = getColWidthAttr(col, 'minWidth') || 100;
});
};

/**
* 重新按规则分配各列宽度
* @param columns 当前表格所有列
* @param thWidthList 列宽记录字典
* @param tableLayout 表格布局
* @param tableElmWidth 表格宽度
* @param notCalculateWidthCols 不需要参与计算的列
* @param callback 回调函数
*/
export default function recalculateColumnWidth<T extends BaseTableCol<T>>(
columns: T[],
thWidthList: ThMap,
tableLayout: string,
tableElmWidth: number,
notCalculateWidthCols: string[],
callback: (widthMap: { [colKey: string]: number }) => void
): void {
let actualWidth = 0;
const missingWidthCols: T[] = [];
const thMap: { [colKey: string]: number } = {};
const thMap: ThMap = {};

// 计算现有列的列宽总和
columns.forEach((col) => {
if (!thWidthList[col.colKey]) {
thMap[col.colKey] = isNumber(col.width) ? col.width : parseFloat(col.width);
thMap[col.colKey] = getColWidthAttr(col, 'width');
} else {
thMap[col.colKey] = thWidthList[col.colKey];
}
Expand All @@ -34,31 +159,14 @@ export default function recalculateColumnWidth<T extends BaseTableCol<T>>(
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;
}
});
}
setMissingColumnWidth(
columns,
missingWidthCols,
thMap,
tableLayout,
actualWidth,
tableWidth
);
needUpdate = true;
} else {
// 所有列都已经指定宽度
Expand All @@ -73,20 +181,18 @@ export default function recalculateColumnWidth<T extends BaseTableCol<T>>(
}
// 重新计算其他列的宽度,按表格剩余宽度进行按比例分配
if (actualWidth !== tableWidth || notCalculateWidthCols.length) {
columns.forEach((col) => {
if (notCalculateWidthCols.includes(col.colKey)) return;
thMap[col.colKey] = (thMap[col.colKey] / actualWidth) * tableWidth;
});
setNormalColumnWidth(
columns,
thMap,
actualWidth,
tableWidth,
notCalculateWidthCols
);
needUpdate = true;
}
}
} else {
// 表格宽度未初始化,默认填充100px
missingWidthCols.forEach((col) => {
const originWidth = thMap[col.colKey] || 100;
thMap[col.colKey] = isNumber(originWidth) ? originWidth : parseFloat(originWidth);
});

setInitialColumnWidth(missingWidthCols, thMap);
needUpdate = true;
}

Expand Down
45 changes: 31 additions & 14 deletions js/table/set-column-width-by-drag.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import isNumber from 'lodash/isNumber';
import { BaseTableCol } from './types';
import { BaseTableCol, ThMap } from './types';
import { getColWidthAttr } from './utils';

/**
* 获取某一列的所有子列
* @param col 表格某一列
* @returns 当前列的所有子列
*/
const findAllChildren = <T extends BaseTableCol<T>>(col: T): T[] => {
const loopQue: T[] = [];
const result: T[] = [];
Expand All @@ -18,15 +23,23 @@ const findAllChildren = <T extends BaseTableCol<T>>(col: T): T[] => {
return result;
};

/**
* 更新拖动后的列宽记录
* @param dragCol 被拖动的列
* @param dragWidth 拖动大小
* @param effectCol 受影响的列
* @param options 配置参数
* @param callback 回调函数
*/
export default function setThWidthListByColumnDrag<T extends BaseTableCol<T>>(
dragCol: T,
dragWidth: number,
effectCol: T,
options: {
getThWidthList: () => { [colKey: string]: number },
getThWidthList: () => ThMap,
DEFAULT_MIN_WIDTH: number
},
callback: (widthMap: { [colKey: string]: number }, colKeys: string[]) => void
callback: (widthMap: ThMap, colKeys: string[]) => void
): void {
const { getThWidthList, DEFAULT_MIN_WIDTH } = options;
const thWidthList = getThWidthList();
Expand Down Expand Up @@ -54,17 +67,18 @@ export default function setThWidthListByColumnDrag<T extends BaseTableCol<T>>(

// 根据多级表头的叶节点计算实际宽度(拖动列)
dragChildrenCols.forEach((child) => {
const defaultWidth = isNumber(child.width) ? child.width : parseFloat(child.width);
oldWidth += thWidthList[child.colKey] || defaultWidth;
oldWidth += thWidthList[child.colKey] || getColWidthAttr(child, 'width');
notCalculateCols.push(child.colKey);
});

// 根据多级表头的叶节点计算实际宽度(受影响的列)
effectChildrenCols.forEach((child) => {
const defaultWidth = isNumber(child.width) ? child.width : parseFloat(child.width);
oldEffectWidth += thWidthList[child.colKey] || defaultWidth;
oldEffectWidth += thWidthList[child.colKey] || getColWidthAttr(child, 'width');
notCalculateCols.push(child.colKey);
effectColsMinWidth += child.resize?.minWidth || DEFAULT_MIN_WIDTH;
effectColsMinWidth += Math.max(
child.resize?.minWidth || DEFAULT_MIN_WIDTH,
getColWidthAttr(child, 'minWidth') || DEFAULT_MIN_WIDTH
);
});

// 按比例划分新宽度(拖动列)
Expand All @@ -76,27 +90,30 @@ export default function setThWidthListByColumnDrag<T extends BaseTableCol<T>>(
const remainWidth = Math.max(
effectColsMinWidth,
oldWidth + oldEffectWidth - dragWidth,
effectCol.resize?.minWidth || DEFAULT_MIN_WIDTH,
Math.max(
getColWidthAttr(effectCol, 'minWidth') || DEFAULT_MIN_WIDTH,
effectCol.resize?.minWidth || DEFAULT_MIN_WIDTH
),
);
effectChildrenCols.forEach((child) => {
updateMap[child.colKey] = Math.max(
child.resize?.minWidth || DEFAULT_MIN_WIDTH,
getColWidthAttr(child, 'minWidth') || DEFAULT_MIN_WIDTH,
(thWidthList[child.colKey] / oldEffectWidth) * remainWidth,
);
});

// 更新各列宽度
callback(updateMap, notCalculateCols);
} else {
const colWidth = isNumber(dragCol.width) ? dragCol.width : parseFloat(dragCol.width);
const effectWidth = isNumber(effectCol.width) ? effectCol.width : parseFloat(effectCol.width);
const oldWidth = thWidthList[dragCol.colKey] || colWidth;
const oldEffectWidth = thWidthList[effectCol.colKey] || effectWidth;
const oldWidth = thWidthList[dragCol.colKey] || getColWidthAttr(dragCol, 'width');
const oldEffectWidth = thWidthList[effectCol.colKey] || getColWidthAttr(effectCol, 'width');

callback({
[dragCol.colKey]: dragWidth,
[effectCol.colKey]: Math.max(
effectCol.resize?.minWidth || DEFAULT_MIN_WIDTH,
getColWidthAttr(effectCol, 'minWidth') || DEFAULT_MIN_WIDTH,
oldWidth + oldEffectWidth - dragWidth,
),
}, [dragCol.colKey, effectCol.colKey]);
Expand Down
7 changes: 6 additions & 1 deletion js/table/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ export interface BaseTableCol<T> {
children?: T[],
colKey?: string,
resize?: { [attr: string]: any },
width?: number | string
width?: number | string,
minWidth?: number | string,
}

export interface ThMap {
[colKey: string]: number
}

export interface PlainObject {
Expand Down
8 changes: 8 additions & 0 deletions js/table/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import isFunction from 'lodash/isFunction';
import isNumber from 'lodash/isNumber';
import { BaseTableCol } from './types';

export function isRowSelectedDisabled(
selectColumn: { [key: string]: any },
Expand All @@ -15,3 +17,9 @@ export function isRowSelectedDisabled(
}
return !!disabled;
}

// 获取列属性
export function getColWidthAttr<T extends BaseTableCol<T>>(col: T, attrKey: 'width' | 'minWidth') {
const attr = col[attrKey];
return isNumber(attr) ? attr : parseFloat(attr);
}