值格式化
列级 format 可以是结构化 FormatSpec(跨线程安全,所有路径可用)或函数(主线程路径执行;浏览器 worker 路径会被剥离,见下文「函数形式」)。
FormatSpec
| 类型 | 参数 | 示例 | 说明 |
|---|---|---|---|
enum | map、fallback? | { type: "enum", map: { paid: "已支付" }, fallback: "未知" } | 枚举值映射;未命中且无 fallback 时原样输出 |
date | pattern?(默认 yyyy-MM-dd) | { type: "date" } | 转 Excel 日期序列并自动注入 numFormat |
datetime | pattern?(默认 yyyy-MM-dd HH:mm) | { type: "datetime" } | 同上,带时间 |
number | decimals?(默认 0)、thousands? | { type: "number", decimals: 2, thousands: true } | 数字语义:Workbook 路径保留完整精度,经 numFormat 渲染 |
padding | fill、length、align?(left/right) | { type: "padding", fill: "0", length: 6, align: "left" } | 左/右补全到固定长度(如工号) |
columns: [
{ prop: "orderId", label: "订单号", width: 12 },
{
prop: "date",
label: "日期",
width: 12,
format: { type: "date", pattern: "yyyy/MM/dd" },
},
{
prop: "amount",
label: "金额",
width: 14,
format: { type: "number", decimals: 2, thousands: true },
},
{
prop: "status",
label: "状态",
width: 10,
format: { type: "enum", map: { paid: "已支付" }, fallback: "未知" },
},
{
prop: "code",
label: "编码",
width: 12,
format: { type: "padding", fill: "0", length: 6, align: "right" },
},
];函数形式
{
prop: "amount",
label: "金额",
width: 14,
format: (value, row) => {
const n = Number(value);
return n >= 1000 ? `大额 ${n.toFixed(2)}` : n.toFixed(2);
},
}函数签名:(value: unknown, row: Record<string, unknown>) => string | number | boolean。函数无法穿过结构化克隆,各路径行为不同:main 路径(浏览器 < 20,000 行 / Node < 50,000 行)、Node 的 stream 路径(≥ 50,000 行,同样在主线程执行)、以及 worker 失败后的主线程重试(原始 options 里的函数仍在——被剥离的只是发给 worker 的副本)都正常执行;浏览器 worker 路径(auto ≥ 20,000 行,或显式 mode: "worker" / mode: "stream")会剥离函数并打印 console.warn,该列以原始值导出(不报错、不回落 main)。需要 worker 路径保留格式时改为 FormatSpec。
跨模式精度注意事项
同一份配置在不同路径下行为有细微差异,务必显式声明 decimals:
- Workbook 路径(main / worker+workbook):
number保留完整精度,显示小数由自动注入的numFormat控制; - Stream 路径(≥ 50,000 行):没有
numFormat支持,会把decimals烧进存储值(如9999.99→10000); - 因此不声明
decimals(默认 0)时,两种路径存进单元格的值可能不同;跨阈值一致性是显式声明decimals的最重要理由。
日期值
date / datetime 接受 Date 对象、可解析字符串或时间戳。Workbook 路径写入日期序列 + numFormat;Stream 路径(无 numFormat 支持)会按 pattern 输出可读字符串(mm 会自动按前后文区分月份与分钟)。
pattern 支持的 token(跨路径有差异):Stream 路径(≥ 50,000 行、显式 mode: "stream" 或兜底路径)只解析 yyyy / MM / dd / HH / mm / ss 六种 token(大小写不敏感)。Workbook 路径把 pattern 作为 numFormat 交给 Excel 渲染,任意合法格式码都生效(yy、单字母 m/d、AM/PM、yyyy"年" 这类字面量等)。六种 token 之外的字符在 Stream 路径「小写后原样」输出——比如 pattern: "yy-MM-dd",阈值以下导出正常的两位年份,阈值以上导出 yy-01-05。有两点需要注意:小写化同样作用于引号字面量(yyyy-MM-dd"T"HH:mm 在阈值以上渲染成 2026-07-01"t"15:30),且引号本身会被输出——Stream 路径不解析引号(yyyy"年"M"月"d"日" 渲染成 2026"年"m"月"d"日",因为 M/d 在那里不是 token)。超集 token 同样只是部分透传:其中的六 token 前缀仍会被解析、剩余字符漏出("mmm" → "09m",而 Excel 侧渲染月份缩写)。为保证跨阈值一致,请只使用这六种 token;确实需要引号字面量时,优先用 numFormat(属于样式,在 Stream 路径整段丢弃)。
时区约定(跨路径一致):date / datetime 统一按值的 UTC 分量解释与输出——Workbook 路径的序列来自 modern-xlsx 的 dateToSerial(UTC 口径),Stream 路径的字符串同样取 UTC 分量,因此同一输入在任何时区、任何路径下显示一致;ISO 日期字符串(如 "2026-07-01")按 ECMA-262 解析为 UTC 午夜,天然符合该口径。注意 new Date(年, 月, 日) 这类本地时间构造的 Date,其 UTC 分量在非零时区可能落到前一天(例如 UTC+8 的本地 0 点 = 前一日 16:00 UTC)。为保证跨时区一致,日期列建议传 ISO 字符串或用 Date.UTC(...) 构造。