style(frontend): 全项目表格统一百分比布局并居中

This commit is contained in:
yml2213
2026-10-24 14:49:23 +08:00
parent a96229a106
commit ce8efa6bad
52 changed files with 346 additions and 145 deletions
@@ -0,0 +1,94 @@
import type { TableColumnsType } from 'antd'
type Column<RecordType extends object> = TableColumnsType<RecordType>[number]
type WidthContext<RecordType extends object> = {
leafPercent: WeakMap<Column<RecordType>, number>
}
/**
* 把业务列宽(px 数值)按相对权重归一化为百分比。
* rowSelection / expandable 等内置列由调用方传入需要预留的百分比。
*/
export function normalizePercentColumns<RecordType extends object>(
columns: TableColumnsType<RecordType>,
options: { reservedPercent?: number } = {},
): TableColumnsType<RecordType> {
const leaves: Column<RecordType>[] = []
collectLeaves(columns, leaves)
const explicitWeights = leaves
.map((column) => numericWidth(column.width))
.filter((width): width is number => width !== null)
const fallbackWeight =
explicitWeights.length > 0
? explicitWeights.reduce((sum, width) => sum + width, 0) / explicitWeights.length
: 100
const totalWeight = leaves.reduce(
(sum, column) => sum + (numericWidth(column.width) ?? fallbackWeight),
0,
)
const budgetPercent = Math.max(60, 100 - Math.max(0, options.reservedPercent ?? 0))
const leafPercent = new WeakMap<Column<RecordType>, number>()
for (const column of leaves) {
const weight = numericWidth(column.width) ?? fallbackWeight
leafPercent.set(column, (weight / totalWeight) * budgetPercent)
}
return applyPercentWidths(columns, { leafPercent })
}
function applyPercentWidths<RecordType extends object>(
columns: TableColumnsType<RecordType>,
context: WidthContext<RecordType>,
): TableColumnsType<RecordType> {
return columns.map((column) => {
const children = 'children' in column ? column.children : undefined
if (Array.isArray(children) && children.length > 0) {
const normalizedChildren = applyPercentWidths(children, context)
const width = normalizedChildren.reduce((sum, child) => sum + percentOf(child.width), 0)
return {
...column,
width: `${width.toFixed(4)}%`,
align: 'center',
children: normalizedChildren,
}
}
return {
...column,
width: `${(context.leafPercent.get(column) ?? 0).toFixed(4)}%`,
align: 'center',
}
})
}
function collectLeaves<RecordType extends object>(
columns: TableColumnsType<RecordType>,
leaves: Column<RecordType>[],
) {
for (const column of columns) {
const children = 'children' in column ? column.children : undefined
if (Array.isArray(children) && children.length > 0) {
collectLeaves(children, leaves)
} else {
leaves.push(column)
}
}
}
function numericWidth(width: Column<object>['width']) {
if (typeof width === 'number' && Number.isFinite(width) && width > 0) return width
if (typeof width === 'string') {
const parsed = Number.parseFloat(width)
if (Number.isFinite(parsed) && parsed > 0) return parsed
}
return null
}
function percentOf(width: Column<object>['width']) {
if (typeof width !== 'string' || !width.endsWith('%')) return 0
const parsed = Number.parseFloat(width)
return Number.isFinite(parsed) ? parsed : 0
}