diff --git a/docs/superpowers/plans/2026-10-10-material-bulk-import.md b/docs/superpowers/plans/2026-10-10-material-bulk-import.md index aa85d4af..9f646d25 100644 --- a/docs/superpowers/plans/2026-10-10-material-bulk-import.md +++ b/docs/superpowers/plans/2026-10-10-material-bulk-import.md @@ -2006,18 +2006,22 @@ git commit -m "feat: 物料导入与模板下载接口" import { describe, expect, test } from 'vitest' import { buildCsv } from './exportCsv' +// BOM 用转义写,不内联那个零宽字符:内联的话看不见也改不动, +// 一旦被编辑器或编码转换吃掉,测试就会以一个极难看懂的方式失败。 +const BOM = '\uFEFF' + describe('buildCsv', () => { test('用逗号分隔并在行尾换行', () => { const csv = buildCsv([ ['行号', '失败原因'], [3, '物料名称不能为空'] ]) - expect(csv).toBe('行号,失败原因\r\n3,物料名称不能为空\r\n') + expect(csv).toBe(BOM + '行号,失败原因\r\n3,物料名称不能为空\r\n') }) test('包含逗号、引号、换行的字段用双引号包裹并转义', () => { const csv = buildCsv([['规格', '备注'], ['M8,30', '含"引号"'], ['两\n行', '']]) - expect(csv).toBe('规格,备注\r\n"M8,30","含""引号"""\r\n"两\n行",\r\n') + expect(csv).toBe(BOM + '规格,备注\r\n"M8,30","含""引号"""\r\n"两\n行",\r\n') }) }) ``` @@ -2032,11 +2036,11 @@ Expected: FAIL(模块不存在) 创建 `frontend/src/utils/exportCsv.js`: ```js -// 生成 CSV 文本。带 UTF-8 BOM,Excel 打开中文才不会乱码;字段里的 -// 逗号、引号、换行按 RFC 4180 用双引号包裹并转义。 +// 生成 CSV 文本。带 UTF-8 BOM(写成转义而不是内联零宽字符,源码里才看得见), +// Excel 打开中文才不会乱码;字段里的逗号、引号、换行按 RFC 4180 用双引号包裹并转义。 export function buildCsv(rows) { const lines = rows.map(row => row.map(escapeCsvCell).join(',')) - return '' + lines.map(line => line + '\r\n').join('') + return '\uFEFF' + lines.map(line => line + '\r\n').join('') } function escapeCsvCell(value) {