This commit is contained in:
iqudoo
2026-06-14 02:52:51 +08:00
parent 6cdae2f69c
commit fff6cb98ae
8 changed files with 325 additions and 256 deletions

View File

@@ -56,18 +56,49 @@ export function getTableColWidths(elem: TemplateElement): number[] {
return Array.from({ length: cols }, () => even);
}
/** 内边距变化后保持行高/列宽不变,自动增大元素尺寸 */
/** 将轨道尺寸总和调整为 targetTotal保持相对比例末项吸收取整误差 */
function scaleTracksToTotal(tracks: number[], targetTotal: number, min = 0.5): number[] {
if (tracks.length === 0) return [];
const safeTotal = Math.max(tracks.length * min, targetTotal);
const current = tracks.reduce((a, b) => a + b, 0);
if (current <= 0) {
return distributeTracks(safeTotal, tracks.length, min);
}
const scaled = tracks.map((t) => round1(Math.max(min, (t / current) * safeTotal)));
const sum = scaled.reduce((a, b) => a + b, 0);
const diff = round1(safeTotal - sum);
if (scaled.length > 0 && diff !== 0) {
scaled[scaled.length - 1] = round1(Math.max(min, scaled[scaled.length - 1] + diff));
}
return scaled;
}
/** 将 inner 区域均分给 count 条轨道(末项吸收取整误差) */
function distributeTracks(total: number, count: number, min = 0.5): number[] {
if (count <= 0) return [];
const safeTotal = Math.max(count * min, total);
const even = round1(safeTotal / count);
const tracks = Array.from({ length: count }, () => even);
const sum = tracks.reduce((a, b) => a + b, 0);
const diff = round1(safeTotal - sum);
if (tracks.length > 0 && diff !== 0) {
tracks[tracks.length - 1] = round1(Math.max(min, tracks[tracks.length - 1] + diff));
}
return tracks;
}
/** 内边距变化后保持元素外框尺寸不变,按比例重算行高/列宽以适配新的内容区 */
export function expandTableForPadding(elem: TemplateElement): TemplateElement {
if (elem.type !== 'table') return elem;
const pad = resolvePaddingMm(elem);
const rowHeights = getTableRowHeights(elem);
const colWidths = getTableColWidths(elem);
const innerW = Math.max(1, elem.width - pad.left - pad.right);
const innerH = Math.max(1, elem.height - pad.top - pad.bottom);
return {
...elem,
tableRowHeights: rowHeights,
tableColWidths: colWidths,
height: round1(rowHeights.reduce((a, b) => a + b, 0) + pad.top + pad.bottom),
width: round1(colWidths.reduce((a, b) => a + b, 0) + pad.left + pad.right),
tableRowHeights: scaleTracksToTotal(getTableRowHeights(elem), innerH),
tableColWidths: scaleTracksToTotal(getTableColWidths(elem), innerW),
width: elem.width,
height: elem.height,
};
}
@@ -183,6 +214,53 @@ export function getCellRectMm(
return { x: round1(x), y: round1(y), width: round1(width), height: round1(height) };
}
/** 表格行列像素边界(累积取整,避免单元格背景/选区出现缝隙) */
export function buildTableGridBoundariesPx(
elem: TemplateElement,
scale: number,
originX = 0,
originY = 0
): { rowBounds: number[]; colBounds: number[] } {
const pad = resolvePaddingMm(elem);
const rowHeights = getTableRowHeights(elem);
const colWidths = getTableColWidths(elem);
const rowBounds: number[] = [originY + Math.round(pad.top * scale)];
for (const h of rowHeights) {
rowBounds.push(rowBounds[rowBounds.length - 1] + Math.round(h * scale));
}
const colBounds: number[] = [originX + Math.round(pad.left * scale)];
for (const w of colWidths) {
colBounds.push(colBounds[colBounds.length - 1] + Math.round(w * scale));
}
return { rowBounds, colBounds };
}
/** 单元格像素矩形,与 canvas 绘制及设计器选区 overlay 对齐 */
export function getCellRectPx(
elem: TemplateElement,
row: number,
col: number,
scale: number,
originX = 0,
originY = 0
): { x: number; y: number; width: number; height: number } {
const { rowBounds, colBounds } = buildTableGridBoundariesPx(elem, scale, originX, originY);
const anchor = getCellAnchor(elem, row, col);
const { rowSpan, colSpan } = getCellSpan(elem, anchor.row, anchor.col);
const x = colBounds[anchor.col];
const y = rowBounds[anchor.row];
return {
x,
y,
width: colBounds[anchor.col + colSpan] - x,
height: rowBounds[anchor.row + rowSpan] - y,
};
}
export function createDefaultTableElement(id: string, index: number): TemplateElement {
const rows = 3;
const cols = 3;
@@ -224,28 +302,6 @@ export function resizeTableGrid(
): TemplateElement {
const rows = Math.max(1, Math.min(50, newRows));
const cols = Math.max(1, Math.min(50, newCols));
const oldRows = getTableRows(elem);
const oldCols = getTableCols(elem);
const { width, height } = getTableInnerSizeMm(elem);
let rowHeights = getTableRowHeights(elem);
let colWidths = getTableColWidths(elem);
if (rows > oldRows) {
const added = rows - oldRows;
const avg = rowHeights.reduce((a, b) => a + b, 0) / oldRows;
rowHeights = [...rowHeights, ...Array.from({ length: added }, () => round1(avg))];
} else if (rows < oldRows) {
rowHeights = rowHeights.slice(0, rows);
}
if (cols > oldCols) {
const added = cols - oldCols;
const avg = colWidths.reduce((a, b) => a + b, 0) / oldCols;
colWidths = [...colWidths, ...Array.from({ length: added }, () => round1(avg))];
} else if (cols < oldCols) {
colWidths = colWidths.slice(0, cols);
}
const newCells: Record<string, TableCellData> = {};
if (elem.tableCells) {
@@ -259,15 +315,18 @@ export function resizeTableGrid(
}
const pad = resolvePaddingMm(elem);
const innerW = Math.max(1, elem.width - pad.left - pad.right);
const innerH = Math.max(1, elem.height - pad.top - pad.bottom);
const next: TemplateElement = {
...elem,
tableRows: rows,
tableCols: cols,
tableRowHeights: rowHeights,
tableColWidths: colWidths,
tableRowHeights: distributeTracks(innerH, rows),
tableColWidths: distributeTracks(innerW, cols),
tableCells: newCells,
width: round1(colWidths.reduce((a, b) => a + b, 0) + pad.left + pad.right),
height: round1(rowHeights.reduce((a, b) => a + b, 0) + pad.top + pad.bottom),
width: elem.width,
height: elem.height,
};
return sanitizeTableMerges(next);
}