fix: 修复设置单元格格式不生效与表格样式覆盖单元格样式

- SetCellFormat V1/V2/V3:格式解析补厂商 JSON 键映射(FontColor/FontSize/
  BoldAndItalic 位标志/BackgroundColor 带透明度/DigitalFormat.formatstring/
  FontFormat 字体名),纯格式代码(如 0.00)回落为数字格式——此前全部被静默忽略
- ShowTableStyle:行级样式(row.font/fill+commit)在 exceljs 中会覆盖该行
  已设置的单元格样式,改为逐单元格设置表头样式
- 经 18 节点覆盖流程实测:V3 红字宋体加粗、V2 蓝字、V1 数字格式 0.00 均落盘
This commit is contained in:
yu 2026-10-29 15:57:56 +08:00
parent 4163b57d38
commit 8cedf45628

View File

@ -625,13 +625,13 @@ function toArgb(color: string): string {
return hex.length === 6 ? "FF" + hex : hex.padStart(8, "F");
}
function parseFormatSpec(raw: unknown): { bold?: boolean; italic?: boolean; underline?: boolean; fontSize?: number; color?: string; bgColor?: string; numFmt?: string } {
if (typeof raw === "object" && raw !== null) return raw as ReturnType<typeof parseFormatSpec>;
function parseFormatSpec(raw: unknown): { bold?: boolean; italic?: boolean; underline?: boolean; fontSize?: number; color?: string; bgColor?: string; numFmt?: string; fontName?: string } {
if (typeof raw === "object" && raw !== null) return normalizeFormatSpec(raw as Record<string, unknown>);
const text = String(raw ?? "").trim();
if (!text) return {};
try {
const parsed = JSON.parse(text) as Record<string, unknown>;
if (typeof parsed === "object" && parsed) return parsed as ReturnType<typeof parseFormatSpec>;
if (typeof parsed === "object" && parsed) return normalizeFormatSpec(parsed);
} catch { /* fall through to keywords */ }
const spec: ReturnType<typeof parseFormatSpec> = {};
const lower = text.toLowerCase();
@ -640,6 +640,26 @@ function parseFormatSpec(raw: unknown): { bold?: boolean; italic?: boolean; unde
if (/下划线|underline/.test(lower)) spec.underline = true;
const sizeMatch = text.match(/(\d+)号|font-size[::]\s*(\d+)/i);
if (sizeMatch) spec.fontSize = Number(sizeMatch[1] || sizeMatch[2]);
// 无关键词命中时视为数字格式代码(如 "0.00"、"G/通用格式"、"yyyy-mm-dd")
if (!Object.keys(spec).length) spec.numFmt = text;
return spec;
}
/** 兼容厂商格式 JSON(FontColor/BoldAndItalic/FontSize/BackgroundColor/DigitalFormat)与内部 camelCase 键 */
function normalizeFormatSpec(parsed: Record<string, unknown>): ReturnType<typeof parseFormatSpec> {
const spec: ReturnType<typeof parseFormatSpec> = {};
const flags = Number(parsed.BoldAndItalic ?? 0);
if (flags & 1) spec.bold = true;
if (flags & 2) spec.italic = true;
if (parsed.FontFormat) spec.fontName = String(parsed.FontFormat);
if (parsed.FontSize !== undefined && parsed.FontSize !== null && String(parsed.FontSize).trim() !== "") spec.fontSize = Number(parsed.FontSize);
if (parsed.FontColor) spec.color = String(parsed.FontColor);
if (parsed.BackgroundColor) spec.bgColor = String(parsed.BackgroundColor).split(",")[0].trim();
const digital = parsed.DigitalFormat as { formatstring?: unknown } | undefined;
if (digital?.formatstring && String(digital.formatstring).trim() !== "") spec.numFmt = String(digital.formatstring);
for (const key of ["bold", "italic", "underline", "fontSize", "color", "bgColor", "numFmt"] as const) {
if (parsed[key] !== undefined) (spec as Record<string, unknown>)[key] = parsed[key];
}
return spec;
}
@ -670,6 +690,7 @@ registerMany(["Excel_Cs_SetCellFormat", "Excel_Cs_SetCellFormat_V2", "Excel_Cs_S
italic: spec.italic,
underline: spec.underline,
size: spec.fontSize,
name: spec.fontName ?? cell.font?.name,
color: spec.color ? { argb: toArgb(spec.color) } : undefined
};
}
@ -697,11 +718,15 @@ register("Excel_Cs_ExcelFilter", async (ctx, node, args) => {
register("Excel_Cs_ShowTableStyle", async (ctx, node, args) => {
const source = await loadWorkbook(args, node.id, node.componentId);
const sheet = worksheetOf(source.workbook, args);
const headerRow = sheet.getRow(1);
headerRow.font = { ...headerRow.font, bold: true };
headerRow.fill = { type: "pattern", pattern: "solid", fgColor: { argb: "FFD9E8F5" } };
headerRow.commit();
sheet.autoFilter = { from: { row: 1, column: 1 }, to: { row: Math.max(1, sheet.rowCount), column: Math.max(1, sheet.columnCount) } };
// 必须逐单元格设置:exceljs 的行级样式(row.font/row.fill + commit)会覆盖
// 该行已设置的单元格样式(如先执行过的设置单元格格式)
const lastCol = Math.max(1, sheet.columnCount);
for (let c = 1; c <= lastCol; c += 1) {
const cell = sheet.getRow(1).getCell(c);
cell.font = { ...cell.font, bold: true };
cell.fill = { type: "pattern", pattern: "solid", fgColor: { argb: "FFD9E8F5" } };
}
sheet.autoFilter = { from: { row: 1, column: 1 }, to: { row: Math.max(1, sheet.rowCount), column: lastCol } };
await saveWhenRequested(source, args, true);
ctx.log("已应用表格样式(表头加粗 + 筛选)", "info", node.id);
return { styled: true };