@vdhewei/xlsx-template-lib 1.5.1 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1016 @@
1
+ # xlsx-template-lib
2
+
3
+ 基于 ExcelJS 的强大 XLSX 模板渲染库,支持基于模板的 Excel 文件生成和数据替换。
4
+
5
+ ## 功能特性
6
+
7
+ - **模板渲染**:使用带有占位符的模板渲染 Excel 文件
8
+ - **数据替换**:支持表达式和函数的动态数据替换
9
+ - **规则配置**:通过专用规则工作表配置渲染规则
10
+ - **自定义命令**:通过自定义命令函数扩展功能(如 `fn:sum`, `fn:sub`)
11
+ - **ZIP 支持**:批量处理 ZIP 压缩包中的多个 Excel 文件
12
+ - **TypeScript 支持**:完整的 TypeScript 支持和类型定义
13
+ - **CLI 工具**:提供命令行工具快速处理
14
+
15
+ ## 安装
16
+
17
+ ```bash
18
+ npm install @vdhewei/xlsx-template-lib
19
+ ```
20
+
21
+ ## 模板语法
22
+
23
+ ### 占位符格式
24
+
25
+ 在 Excel 单元格中使用 `${变量名}` 格式进行数据替换:
26
+
27
+ | 模板(编译前) | 渲染后结果 |
28
+ |:-------------|:-----------|
29
+ | `${contract.contractCode}` | `CTR-2024-001` |
30
+ | `${exportData.LRR.mothOrYear}` | `2024-01` |
31
+ | `${contract.contractTitle}` | `Construction Project A` |
32
+
33
+ ### 占位符类型
34
+
35
+ #### 1. 简单值(标量)
36
+
37
+ 使用单个值替换占位符。
38
+
39
+ **Excel 模板:**
40
+ ```
41
+ A B
42
+ 1 提取日期:${extractDate}
43
+ ```
44
+
45
+ **代码:**
46
+ ```typescript
47
+ const values = {
48
+ extractDate: new Date('2024-01-15')
49
+ };
50
+ template.substitute(1, values);
51
+ ```
52
+
53
+ **结果:**
54
+ ```
55
+ A B
56
+ 1 提取日期:Jan-15-2024
57
+ ```
58
+
59
+ **注意事项:**
60
+ - 占位符可以单独在单元格中,也可以作为文本的一部分:"总计:${amount}"
61
+ - Excel 单元格格式(日期、数字、货币)会被保留
62
+
63
+ #### 2. 数组索引
64
+
65
+ 直接在模板中访问特定数组元素。
66
+
67
+ **Excel 模板:**
68
+ ```
69
+ A B
70
+ 1 第一个日期:${dates[0]}
71
+ 2 第二个日期:${dates[1]}
72
+ ```
73
+
74
+ **代码:**
75
+ ```typescript
76
+ const values = {
77
+ dates: [new Date('2024-01-01'), new Date('2024-02-01')]
78
+ };
79
+ template.substitute(1, values);
80
+ ```
81
+
82
+ **结果:**
83
+ ```
84
+ A B
85
+ 1 第一个日期:Jan-01-2024
86
+ 2 第二个日期:Feb-01-2024
87
+ ```
88
+
89
+ #### 3. 列数组
90
+
91
+ 在列中水平展开数组。
92
+
93
+ **Excel 模板:**
94
+ ```
95
+ A
96
+ 1 ${dates}
97
+ ```
98
+
99
+ **代码:**
100
+ ```typescript
101
+ const values = {
102
+ dates: [
103
+ new Date('2024-01-01'),
104
+ new Date('2024-02-01'),
105
+ new Date('2024-03-01')
106
+ ]
107
+ };
108
+ template.substitute(1, values);
109
+ ```
110
+
111
+ **结果:**
112
+ ```
113
+ A B C
114
+ 1 Jan-01-2024 Feb-01-2024 Mar-01-2024
115
+ ```
116
+
117
+ **注意:** 占位符必须是单元格中的唯一内容
118
+
119
+ #### 4. 表格行
120
+
121
+ 从对象数组生成多行。
122
+
123
+ **Excel 模板:**
124
+ ```
125
+ A B C
126
+ 1 姓名 年龄 部门
127
+ 2 ${team.name} ${team.age} ${team.dept}
128
+ ```
129
+
130
+ **代码:**
131
+ ```typescript
132
+ const values = {
133
+ team: [
134
+ { name: 'Alice Johnson', age: 28, dept: 'Engineering' },
135
+ { name: 'Bob Smith', age: 34, dept: 'Marketing' },
136
+ { name: 'Carol White', age: 25, dept: 'Sales' }
137
+ ]
138
+ };
139
+ template.substitute(1, values);
140
+ ```
141
+
142
+ **结果:**
143
+ ```
144
+ A B C
145
+ 1 姓名 年龄 部门
146
+ 2 Alice Johnson 28 Engineering
147
+ 3 Bob Smith 34 Marketing
148
+ 4 Carol White 25 Sales
149
+ ```
150
+
151
+ **语法:** `${table:数组名.属性名}`
152
+ - 数组中的每个对象创建一个新行
153
+ - 如果属性是数组,则水平展开
154
+
155
+ #### 5. 图片
156
+
157
+ 在单元格中插入图片。
158
+
159
+ **Excel 模板:**
160
+ ```
161
+ A B
162
+ 1 Logo:${image:companyLogo}
163
+ ```
164
+
165
+ **代码:**
166
+ ```typescript
167
+ const values = {
168
+ companyLogo: '/path/to/logo.png' // 或 Base64, Buffer
169
+ };
170
+ template.substitute(1, values);
171
+ ```
172
+
173
+ **结果:**
174
+ ```
175
+ A B
176
+ 1 Logo:🖼️
177
+ ```
178
+
179
+ **支持的图片格式:**
180
+ - 文件路径(绝对或相对):'/path/to/image.png'
181
+ - Base64 字符串:'data:image/png;base64,iVBORw0KG...'
182
+ - Buffer:fs.readFileSync('image.png')
183
+
184
+ **图片选项:**
185
+ ```typescript
186
+ const template = new XlsxTemplate(data, {
187
+ imageRootPath: '/absolute/path/to/images', // 相对图片路径的基础路径
188
+ imageRatio: 75 // 图片缩放比例为 75%
189
+ });
190
+ ```
191
+
192
+ **表格图片:**
193
+ ```
194
+ A B
195
+ 1 产品 照片
196
+ 2 ${products.name} ${products.photo:image}
197
+ ```
198
+
199
+ **代码:**
200
+ ```typescript
201
+ const values = {
202
+ products: [
203
+ { name: 'Product 1', photo: 'product1.jpg' },
204
+ { name: 'Product 2', photo: 'product2.jpg' }
205
+ ]
206
+ };
207
+ ```
208
+
209
+ **结果:**
210
+ ```
211
+ A B
212
+ 1 产品 照片
213
+ 2 Product 1 🖼️
214
+ 3 Product 2 🖼️
215
+ ```
216
+
217
+ | 模板(编译前) | 渲染后结果 |
218
+ |:-------------|:-----------|
219
+ | `${contract.contractCode}` | `CTR-2024-001` |
220
+ | `${exportData.LRR.mothOrYear}` | `2024-01` |
221
+ | `${contract.contractTitle}` | `Construction Project A` |
222
+
223
+ ### 编译规则配置
224
+
225
+ 在规则工作表(如 `export.metadata.config`)中配置渲染规则,支持以下语法:
226
+
227
+ **⚠️ 重要规则:**
228
+ - **同一个工作表中的相同规则不能重复配置,重复配置无效或会导致编译解析异常**
229
+ - 每种规则类型在同一个工作表中必须唯一
230
+
231
+ | 规则类型 | 语法 | 说明 |
232
+ |:---------|:-----|:-----|
233
+ | **alias** | `alias: @#key => use aliasKey: @# => @#` | 字段别名映射 |
234
+ | **rowCell** | `G-AP:12=compile GenCell(...)` | 行规则配置 |
235
+ | **mergeCell** | `G-AQ:13-17=sum(...)` | 合并单元格计算 |
236
+ | **cell** | `D-7=@#[@D.MY]` | 单个单元格值赋值 |
237
+
238
+ #### Alias 规则
239
+
240
+ 为变量名或变量取值表达式定义别名缩写。支持多行配置。
241
+
242
+ **语法:** `alias 缩写=原变量/原表达式`
243
+
244
+ **规则:**
245
+ - 别名缩写在同一工作表中必须唯一
246
+ - 别名可在表达式中使用 `@别名` 引用
247
+
248
+ **示例:**
249
+
250
+ | 别名配置 | 说明 |
251
+ |:---------|:-----|
252
+ | `T=template` | 将 `T` 映射到 `template` |
253
+ | `LLR=exportData.LRR` | 将 `LLR` 映射到 `exportData.LRR` |
254
+
255
+ **在表达式中使用:**
256
+
257
+ ```
258
+ 使用前: ${exportData.LRR.value}
259
+ 使用后: ${@LLR.value}
260
+ ```
261
+
262
+ #### RowCell 规则
263
+
264
+ 配置行规则,为单元格范围赋值。支持多行配置。
265
+
266
+ **语法:** `列起始号-列结束号:行号=值表达式`
267
+
268
+ **范围格式:**
269
+ - `列起始号-列结束号:行号`
270
+ - 示例:`G-AP:12`(G 到 AP 列,第 12 行)
271
+
272
+ **值表达式:**
273
+ - 通常使用 `compile:GenCell` 宏替换或 `compile:Macro` 展开
274
+
275
+ **示例:**
276
+
277
+ | 规则 | 说明 |
278
+ |:-----|:-----|
279
+ | `G-AP:12=compile GenCell(@#item,[compile Macro]#index@0)` | 为第 12 行的 G-AP 列赋值生成的值 |
280
+ | `A-Z:5=compile Macro(@#data,2,5,!!codeKey)` | 为第 5 行的 A-Z 列赋值格式化后的单元格值 |
281
+
282
+ #### MergeCell 规则
283
+
284
+ 合并单元格并应用计算函数。支持多行配置。
285
+
286
+ **语法:** `列起始号-列结束号:行起始号-行结束号=函数表达式`
287
+
288
+ **范围格式:**
289
+ - `列起始号-列结束号:行起始号-行结束号`
290
+ - 示例:`G-AQ:13-17`(G 到 AQ 列,第 13 到 17 行)
291
+
292
+ **函数表达式:**
293
+ - 通常使用 `sum` 或 `sub` 函数配合宏替换
294
+
295
+ **示例:**
296
+
297
+ | 规则 | 说明 |
298
+ |:-----|:-----|
299
+ | `G-AQ:13-17=sum(@LT,[compile:Macro(exprArr,F,13,17,!!codeKey)],compile:Macro(index),0)` | 合并并计算第 13-17 行的和 |
300
+ | `G-AQ:18-35=sub(@LT,[compile:Macro(exprArr,F,18,35,!!codeKey)],compile:Macro(index),0)` | 合并并计算第 18-35 行的差 |
301
+
302
+ #### Cell 规则
303
+
304
+ 为单个单元格赋值。
305
+
306
+ **语法:** `列号:行号=值表达式`
307
+
308
+ **坐标格式:**
309
+ - `列号:行号`
310
+ - 示例:`D:7`(D 列,第 7 行)
311
+
312
+ **值表达式:**
313
+ - `compile:Macro` 展开
314
+ - 变量占位符:`${变量}`
315
+ - 带别名的变量占位符:`${@别名}`
316
+
317
+ **示例:**
318
+
319
+ | 规则 | 说明 |
320
+ |:-----|:-----|
321
+ | `D:7=@#[@D.MY]` | 从表达式赋值 |
322
+ | `A:1=${contractCode}` | 从变量占位符赋值 |
323
+ | `B:1=${@LLR.value}` | 从带别名的变量赋值 |
324
+
325
+ #### 计算函数
326
+
327
+ **sum 函数**
328
+
329
+ 计算多个值的和。
330
+
331
+ **语法:** `sum(值根,[值子项...],值后缀,默认值)`
332
+
333
+ **参数:**
334
+ - `值根`: 所有值表达式的共同父级
335
+ - `值子项`: 各级值子项数组
336
+ - `值后缀`: 每个值的结尾值
337
+ - `默认值`: 当统计值之和为 0 时返回的值(undefined 不会返回默认值)
338
+
339
+ **示例:**
340
+ ```
341
+ sum(orders,[cat,food,game],1,0)
342
+ // 相当于: orders.cat.1 + orders.food.1 + orders.game.1
343
+ ```
344
+
345
+ **sub 函数**
346
+
347
+ 计算多个值的差。
348
+
349
+ **语法:** `sub(值根,[值子项...],值后缀,默认值)`
350
+
351
+ **参数:**
352
+ - `值根`: 所有值表达式的共同父级
353
+ - `值子项`: 各级值子项数组
354
+ - `值后缀`: 每个值的结尾值
355
+ - `默认值`: 当统计值之差为 0 时返回的值(undefined 不会返回默认值)
356
+
357
+ **示例:**
358
+ ```
359
+ sub(orders,[money,food,game],1,0)
360
+ // 相当于: orders.money.1 - orders.food.1 - orders.game.1
361
+ ```
362
+
363
+ ### 宏替换规则
364
+
365
+ 本库支持强大的宏替换函数,用于动态生成单元格内容:
366
+
367
+ #### GenCell 宏
368
+
369
+ 通过连接多个部分生成单元格表达式:
370
+
371
+ | 语法 | 说明 | 示例 | 结果 |
372
+ |:-----|:-----|:-----|:-----|
373
+ | `compile:GenCell(expr1,expr2,...,exprN)` | 使用默认分隔符 `·` 连接 | `GenCell(test,1,2)` | `test·1·2` |
374
+ | `compile:GenCell(expr1,expr2,...,exprN,"sep")` | 使用自定义分隔符连接 | `GenCell(test,1,2,"_")` | `test_1_2` |
375
+ | `compile:GenCell(expr1,expr2,...,exprN,"")` | 不使用分隔符连接 | `GenCell(test,1,2,"")` | `test12` |
376
+
377
+ #### Macro 宏展开
378
+
379
+ ##### 单个单元格宏
380
+
381
+ 展开为单个单元格的值:
382
+
383
+ | 语法 | 说明 | 示例 |
384
+ |:-----|:-----|:-----|
385
+ | `compile:Macro(expr,columnNum,rowNum)` | 获取 (columnNum, rowNum) 位置单元格的值 | `Macro(data,2,5)` |
386
+ | `compile:Macro(expr,columnNum,rowNum,MacroFormatter)` | 获取格式化后的值 | `Macro(data,2,5,!!codeKey)` |
387
+
388
+ **参数说明:**
389
+ - `expr`: 基础表达式
390
+ - `columnNum`: 列号(从 1 开始)
391
+ - `rowNum`: 行号(从 1 开始)
392
+ - `MacroFormatter`: 可选的格式化器(见下方)
393
+
394
+ ##### 多个单元格宏
395
+
396
+ 展开为多个单元格的值:
397
+
398
+ | 语法 | 说明 | 示例 |
399
+ |:-----|:-----|:-----|
400
+ | `compile:Macro(exprArr,columnNum,rowStartNum,rowEndNum)` | 获取单元格范围内的值 | `Macro(data,1,1,5)` |
401
+ | `compile:Macro(exprArr,columnNum,rowStartNum,rowEndNum,MacroFormatter)` | 获取格式化后的值 | `Macro(data,1,1,5,!!number)` |
402
+
403
+ **参数说明:**
404
+ - `exprArr`: 基础表达式数组
405
+ - `columnNum`: 列号(从 1 开始)
406
+ - `rowStartNum`: 起始行号(从 1 开始)
407
+ - `rowEndNum`: 结束行号(从 1 开始)
408
+ - `MacroFormatter`: 可选的格式化器
409
+
410
+ ##### Index 宏
411
+
412
+ 生成从 1 开始的迭代序列:
413
+
414
+ | 语法 | 说明 | 使用示例 | 结果 |
415
+ |:-----|:-----|:---------|:-----|
416
+ | `compile:Macro(index)` | 自动递增索引(从 1 开始) | 第 1 行: `Macro(index)` | `1` |
417
+ | | | 第 2 行: `Macro(index)` | `2` |
418
+ | | | 第 3 行: `Macro(index)` | `3` |
419
+
420
+ #### Macro 格式化器
421
+
422
+ 使用以 `!!` 开头的特殊格式化器格式化宏输出:
423
+
424
+ | 格式化器 | 说明 | 输入 | 输出 |
425
+ |:---------|:-----|:-----|:-----|
426
+ | `!!codeKey` | 将特殊字符(`@-[]{}\/'.`)转换为 `_`,删除多余 `__`,去除首尾 `_`,转为大写 | `test..x` | `TEST_X` |
427
+ | | | `@data-value` | `DATA_VALUE` |
428
+ | | | `[item].name` | `ITEM_NAME` |
429
+ | `!!codeKeyAlias` | 与 `!!codeKey` 相同,但添加前缀(默认 `@`) | `test..x` | `@TEST_X` |
430
+ | | (默认前缀 `@`)| `data.value` | `@DATA_VALUE` |
431
+ | `!!number` | 转换为十进制整数,支持 `0x` 十六进制前缀 | `123` | `123` |
432
+ | | | `0xFF` | `255` |
433
+ | | | `abc` | `abc`(保持不变,NaN) |
434
+
435
+ **CodeKey 转换规则:**
436
+ - 替换的特殊字符:`@`, `-`, 空格, `[`, `]`, `{`, `}`, `\`, `/`, `'`, `.`
437
+ - 连续的多个 `__` 合并为单个 `_`
438
+ - 删除开头和结尾的 `_`
439
+ - 最终结果转为大写
440
+
441
+ #### Macro 使用示例
442
+
443
+ **示例 1: 使用行单元格生成 CodeKey**
444
+
445
+ ```
446
+ 规则: G-AQ:117=compile GenCell(#LT[compile Macro]#err@F118[#codeKey],[compile Macro]#index@0)
447
+ 结果: errValue·1, errValue·2, errValue·3, ...
448
+ ```
449
+
450
+ **示例 2: 使用 CodeKey 格式化单元格值**
451
+
452
+ ```
453
+ 规则: D-7=compile Macro(@#[@D.MY],5,7,!!codeKey)
454
+ 如果 cell(5,7) = "project-alpha-2024"
455
+ 结果: PROJECT_ALPHA_2024
456
+ ```
457
+
458
+ **示例 3: 生成 CodeKeyAlias**
459
+
460
+ ```
461
+ 规则: cell F-10=compile Macro(@#key,3,10,!!codeKeyAlias)
462
+ 如果 cell(3,10) = "test..data"
463
+ 结果: @TEST_DATA
464
+ ```
465
+
466
+ **示例 4: 数字转换**
467
+
468
+ ```
469
+ 规则: row-5=compile Macro(@#value,2,5,!!number)
470
+ 如果 cell(2,5) = "42"
471
+ 结果: 42
472
+
473
+ 规则: row-6=compile Macro(@#hex,4,6,!!number)
474
+ 如果 cell(4,6) = "0x1A"
475
+ 结果: 26
476
+ ```
477
+
478
+ **示例 5: 使用 Index 迭代**
479
+
480
+ ```
481
+ 第 1 行: Code-${compile:Macro(index)} → Code-1
482
+ 第 2 行: Code-${compile:Macro(index)} → Code-2
483
+ 第 3 行: Code-${compile:Macro(index)} → Code-3
484
+ ```
485
+
486
+ #### 完整规则配置示例
487
+
488
+ 规则工作表(`export.metadata.config`)的完整示例,包含所有规则类型:
489
+
490
+ ```
491
+ # Alias 规则(为长表达式定义快捷方式)
492
+ T=template
493
+ LLR=exportData.LRR
494
+ CTR=contract.contractCode
495
+
496
+ # RowCell 规则(为单元格范围赋值)
497
+ G-AQ:12=compile GenCell(@#item,[compile Macro]#index@0)
498
+ A-Z:5=compile Macro(@#data,2,5,!!codeKey)
499
+
500
+ # MergeCell 规则(合并单元格并应用计算)
501
+ G-AQ:13-17=sum(@LT,[compile:Macro(exprArr,F,13,17,!!codeKey)],compile:Macro(index),0)
502
+ G-AQ:18-35=sub(@LT,[compile:Macro(exprArr,F,18,35,!!codeKey)],compile:Macro(index),0)
503
+
504
+ # Cell 规则(为单个单元格赋值)
505
+ D:7=@#[@D.MY]
506
+ A:1=${@CTR}
507
+ B:1=${@LLR.value}
508
+ ```
509
+
510
+ **⚠️ 重要注意事项:**
511
+ - 每种规则类型(alias、rowCell、mergeCell、cell)可以出现多次
512
+ - 但**相同规则的重复配置无效或会导致编译解析异常**
513
+ - 别名缩写在同一工作表中必须唯一
514
+ - 行/列范围不能以冲突的方式重叠
515
+
516
+ ### 渲染函数
517
+
518
+ 内置和自定义的数据处理函数:
519
+
520
+ | 函数 | 语法 | 示例 |
521
+ |:-----|:-----|:-----|
522
+ | **sum** | `fn:sum(...values)` | `fn:sum(10, 20, 30)` => `60` |
523
+ | **sub** | `fn:sub(a, b)` | `fn:sub(100, 30)` => `70` |
524
+ | **自定义函数** | `fn:customName(...args)` | 用户自定义逻辑 |
525
+
526
+ ## 快速开始
527
+
528
+ ### 基础用法
529
+
530
+ ```typescript
531
+ import { ZipXlsxTemplateApp } from '@vdhewei/xlsx-template-lib';
532
+ import * as fs from 'node:fs/promises';
533
+
534
+ // 从 buffer 加载模板
535
+ const templateBuffer = await fs.readFile('template.xlsx');
536
+ const app = new ZipXlsxTemplateApp(templateBuffer);
537
+
538
+ // 使用数据渲染
539
+ const data = {
540
+ contract: {
541
+ contractCode: 'CTR-2024-001',
542
+ contractTitle: 'Construction Project A'
543
+ },
544
+ exportData: {
545
+ LRR: {
546
+ mothOrYear: '2024-01'
547
+ }
548
+ }
549
+ };
550
+
551
+ await app.substituteAll(data);
552
+
553
+ // 生成输出
554
+ const output = await app.generate();
555
+ await fs.writeFile('output.xlsx', output);
556
+ ```
557
+
558
+ ### 使用 XlsxRender
559
+
560
+ ```typescript
561
+ import { XlsxRender } from '@vdhewei/xlsx-template-lib';
562
+
563
+ const templateBuffer = await fs.readFile('template.xlsx');
564
+ const xlsx = await XlsxRender.create(templateBuffer);
565
+
566
+ // 渲染特定工作表
567
+ await xlsx.render({
568
+ contract: { contractCode: 'CTR-001' }
569
+ }, 'Sheet1');
570
+
571
+ // 生成输出
572
+ const buffer = await xlsx.generate();
573
+ ```
574
+
575
+ ### 带规则配置的编译和渲染
576
+
577
+ ```typescript
578
+ import {
579
+ ZipXlsxTemplateApp,
580
+ compileAll,
581
+ AddCommand
582
+ } from '@vdhewei/xlsx-template-lib';
583
+
584
+ // 添加自定义渲染函数
585
+ AddCommand('sum', (obj, args) => {
586
+ return args.groups.reduce((acc, val) => acc + Number(val), 0);
587
+ });
588
+
589
+ // 配置编译选项
590
+ const compileOpts = {
591
+ sheetName: 'export.metadata.config', // 规则配置工作表
592
+ remove: true // 编译后移除规则工作表
593
+ };
594
+
595
+ const zipBuffer = await fs.readFile('template.xlsx');
596
+ const result = await ZipXlsxTemplateApp.compileTo(zipBuffer, {
597
+ checker: async (buf, opts, values, fileName) => {
598
+ // 自定义验证逻辑
599
+ return buf;
600
+ },
601
+ options: compileOpts
602
+ }, renderData);
603
+ ```
604
+
605
+ ### CLI 工具
606
+
607
+ CLI 工具 `xlsx-cli` 提供了命令行接口用于快速处理 Excel 模板。
608
+
609
+ #### 安装
610
+
611
+ ```bash
612
+ npm install -g @vdhewei/xlsx-template-lib
613
+ ```
614
+
615
+ 或直接使用 `npx`:
616
+
617
+ ```bash
618
+ npx @vdhewei/xlsx-template-lib <命令> [选项]
619
+ ```
620
+
621
+ #### 命令
622
+
623
+ ##### 1. compile 命令
624
+
625
+ 编译带有规则配置的 Excel 文件。
626
+
627
+ ```bash
628
+ xlsx-cli compile <xlsx-文件> [选项]
629
+ ```
630
+
631
+ **参数:**
632
+ - `<xlsx-文件>` - Excel 文件路径
633
+
634
+ **选项:**
635
+ - `-s, --save <string>` - 将编译后的文件保存到指定目录(默认:当前目录)
636
+ - `-n, --sheet-name <string>` - 要编译的工作表名称(默认:第一个工作表)
637
+ - `-r, --remove` - 编译后移除配置规则工作表(默认:false)
638
+
639
+ **示例:**
640
+
641
+ ```bash
642
+ # 使用默认设置编译
643
+ xlsx-cli compile template.xlsx
644
+
645
+ # 编译并保存到指定位置
646
+ xlsx-cli compile template.xlsx -s ./output/
647
+
648
+ # 编译指定工作表
649
+ xlsx-cli compile template.xlsx -n Sheet1
650
+
651
+ # 编译并移除配置工作表
652
+ xlsx-cli compile template.xlsx -r
653
+
654
+ # 完整示例
655
+ xlsx-cli compile template.xlsx -s ./output/ -n Sheet1 -r
656
+ ```
657
+
658
+ **输出:**
659
+ - 编译后的 Excel 文件保存为 `<文件名>_<时间戳>.xlsx`
660
+ - 成功消息以绿色显示
661
+ - 错误以红色显示并返回退出码 1
662
+
663
+ ##### 2. render 命令
664
+
665
+ 使用数据替换渲染 Excel 模板。
666
+
667
+ ```bash
668
+ xlsx-cli render <xlsx-文件> [选项]
669
+ ```
670
+
671
+ **参数:**
672
+ - `<xlsx-文件>` - Excel 模板文件路径
673
+
674
+ **选项:**
675
+ - `-c, --compile` - 渲染前自动编译规则(默认:false)
676
+ - `-n, --sheet-name <string>` - 要渲染的工作表名称(默认:第一个工作表)
677
+ - `-s, --save <string>` - 将渲染后的文件保存到指定目录(默认:当前目录)
678
+ - `-d, --data <string>` - 渲染数据源(JSON 字符串、文件路径或 URL)
679
+
680
+ **示例:**
681
+
682
+ ```bash
683
+ # 使用空数据基本渲染
684
+ xlsx-cli render template.xlsx
685
+
686
+ # 使用 JSON 字符串渲染
687
+ xlsx-cli render template.xlsx -d '{"name":"张三","age":30}'
688
+
689
+ # 使用 JSON 文件渲染
690
+ xlsx-cli render template.xlsx -d ./data.json
691
+
692
+ # 使用远程 JSON URL 渲染
693
+ xlsx-cli render template.xlsx -d 'https://api.example.com/data.json'
694
+
695
+ # 渲染并自动编译
696
+ xlsx-cli render template.xlsx -c -d './data.json'
697
+
698
+ # 渲染指定工作表
699
+ xlsx-cli render template.xlsx -n Sheet1 -d './data.json'
700
+
701
+ # 完整示例
702
+ xlsx-cli render template.xlsx -c -n Sheet1 -s ./output/ -d './data.json'
703
+ ```
704
+
705
+ **数据源:**
706
+ - **JSON 字符串**: 直接使用单引号括起来的 JSON 字符串
707
+ - **本地文件**: `.json` 文件的路径(相对或绝对)
708
+ - **远程 URL**: 返回 JSON 的 HTTP/HTTPS URL
709
+
710
+ **输出:**
711
+ - 渲染后的 Excel 文件保存为 `<文件名>_<时间戳>.xlsx`
712
+ - 检查工作表是否存在
713
+ - 使用适当的颜色显示成功/错误消息
714
+
715
+ ##### 3. rules 命令
716
+
717
+ 向 Excel 文件添加规则配置。
718
+
719
+ ```bash
720
+ xlsx-cli rules <xlsx-文件> [选项]
721
+ ```
722
+
723
+ **参数:**
724
+ - `<xlsx-文件>` - Excel 文件路径
725
+
726
+ **选项:**
727
+
728
+ **模式 1:命令行规则**
729
+ - `-t, --type <string>` - 规则类型:`cell`、`alias`、`rowCell`、`mergeCell`(使用 -r 时必需)
730
+ - `-r, --rule <string>` - 规则表达式字符串(可指定多次)
731
+
732
+ **模式 2:文件规则**
733
+ - `-f, --file <string>` - 从文件读取规则(格式:每行 `<类型> 规则表达式`)
734
+ - 以 `#` 开头的行被视为注释
735
+ - 空行将被跳过
736
+ - 规则类型:`cell`、`alias`、`rowCell`、`mergeCell`
737
+
738
+ **通用选项:**
739
+ - `-s, --save <string>` - 保存编译后的文件到指定目录(默认:当前目录)
740
+
741
+ **示例:**
742
+
743
+ **单个规则(命令行):**
744
+ ```bash
745
+ # 添加 alias 规则
746
+ xlsx-cli rules template.xlsx -t alias -r 'T=template'
747
+
748
+ # 添加 cell 规则
749
+ xlsx-cli rules template.xlsx -t cell -r 'D:7=${@LLR.value}'
750
+
751
+ # 添加 rowCell 规则
752
+ xlsx-cli rules template.xlsx -t rowCell -r 'G-AQ:12=compile GenCell(@#item,[compile Macro]#index@0)'
753
+
754
+ # 添加 mergeCell 规则
755
+ xlsx-cli rules template.xlsx -t mergeCell -r 'G-AQ:13-17=sum(@LT,[compile:Macro(exprArr,F,13,17,!!codeKey)],compile:Macro(index),0)'
756
+ ```
757
+
758
+ **多个规则(命令行):**
759
+ ```bash
760
+ # 添加同类型的多个规则
761
+ xlsx-cli rules template.xlsx -t cell -r 'D:7=${@LLR.value}' -r 'A:1=${@T}' -r 'B:1=${@LLR.value}'
762
+ ```
763
+
764
+ **从文件读取规则:**
765
+ ```bash
766
+ # 从文件读取规则
767
+ xlsx-cli rules template.xlsx -f rules.txt
768
+
769
+ # 创建 rules.txt 文件:
770
+ # 这是注释行
771
+ alias T=template
772
+ alias LLR=exportData.LRR
773
+ cell D:7=${@T}
774
+ cell A:1=${@LLR.value}
775
+ rowCell G-AQ:12=compile GenCell(@#item,[compile Macro]#index@0)
776
+ mergeCell G-AQ:13-17=sum(@LT,[compile:Macro(exprArr,F,13,17,!!codeKey)],compile:Macro(index),0)
777
+ ```
778
+
779
+ **保存到指定目录:**
780
+ ```bash
781
+ xlsx-cli rules template.xlsx -f rules.txt -s ./output/
782
+ xlsx-cli rules template.xlsx -t cell -r 'D:7=${@LLR.value}' -s ./output/
783
+ ```
784
+
785
+ **文件格式(-f 模式):**
786
+ ```bash
787
+ # 格式:<类型> 规则表达式
788
+ # 注释行以 # 开头
789
+ # 有效类型:cell、alias、rowCell、mergeCell
790
+
791
+ cell D:7=${@LLR.value}
792
+ alias T=template
793
+ rowCell G-AQ:12=compile GenCell(@#item,[compile Macro]#index@0)
794
+ mergeCell G-AQ:13-17=sum(@LT,[compile:Macro(exprArr,F,13,17,!!codeKey)],compile:Macro(index),0)
795
+ ```
796
+
797
+ **行为:**
798
+ - 如果不存在则创建 `export_metadata.config` 工作表
799
+ - 添加规则并应用样式:类型字段加粗+居中,表达式居中
800
+ - 根据内容自动调整列宽
801
+ - 每种规则类型(cell、alias、rowCell、mergeCell)每行支持最多 4 个规则
802
+ - 如果为同一类型添加超过 4 个规则,自动创建新行
803
+ - 支持从命令行或文件批量添加规则
804
+ - 输出带时间戳的新文件
805
+
806
+ #### 通用特性
807
+
808
+ **环境变量:**
809
+ - CLI 自动从当前目录加载 `.env` 文件(如果存在)
810
+
811
+ **文件路径解析:**
812
+ - 支持绝对和相对路径
813
+ - 解析相对于当前工作目录的路径
814
+ - 处理前验证文件是否存在
815
+
816
+ **错误处理:**
817
+ - 所有错误使用 chalk 以红色显示
818
+ - 出错时返回非零退出码
819
+ - 提供详细的错误消息用于调试
820
+
821
+ **跨平台支持:**
822
+ - 在 Windows、Linux 和 macOS 上运行
823
+ - 使用平台无关的路径处理
824
+
825
+ **输出文件名格式:**
826
+ - 默认:`<输入文件名>_<时间戳>.xlsx`
827
+ - 时间戳为自纪元以来的毫秒数
828
+ - 保留原始文件名
829
+
830
+ **详细日志:**
831
+ - 灰色信息消息显示处理步骤
832
+ - 绿色成功消息
833
+ - 红色错误消息
834
+ - 黄色警告
835
+
836
+ ## 高级功能
837
+
838
+ ### 自定义命令(渲染函数)
839
+
840
+ ```typescript
841
+ import { AddCommand, generateCommandsXlsxTemplate } from '@vdhewei/xlsx-template-lib';
842
+
843
+ // 添加自定义命令
844
+ AddCommand('multiply', (obj, args) => {
845
+ const values = args.groups.map(g => valueDotGet(obj, g));
846
+ return values.reduce((a, b) => a * b, 1);
847
+ });
848
+
849
+ // 使用自定义命令生成模板
850
+ const buffer = await generateCommandsXlsxTemplate(data, options);
851
+ ```
852
+
853
+ ### 批量处理
854
+
855
+ ```typescript
856
+ // 处理包含多个 XLSX 文件的 ZIP 文件
857
+ const zipBuffer = await fs.readFile('templates.zip');
858
+ const app = new ZipXlsxTemplateApp(zipBuffer);
859
+
860
+ const compileOpts = {
861
+ sheetName: 'export.metadata.config',
862
+ remove: true
863
+ };
864
+
865
+ const renderOpts = {
866
+ // 渲染选项
867
+ };
868
+
869
+ await app.substituteAll(renderData, compileOpts, renderOpts);
870
+ const output = await app.generate();
871
+ ```
872
+
873
+ ## API 参考
874
+
875
+ ### ZipXlsxTemplateApp
876
+
877
+ 处理 ZIP 压缩包中 Excel 文件的主类。
878
+
879
+ | 方法 | 说明 |
880
+ |:-----|:-----|
881
+ | `constructor(data?: Buffer)` | 使用 ZIP buffer 初始化 |
882
+ | `loadZipBuffer(data: Buffer)` | 加载 ZIP buffer |
883
+ | `parse(data: Buffer)` | 解析 ZIP 并提取 XLSX 条目 |
884
+ | `getEntries()` | 获取所有 XLSX 文件条目 |
885
+ | `substituteAll(renderData, compileOpts?, renderOpts?)` | 替换所有占位符 |
886
+ | `generate(options?)` | 生成输出 buffer |
887
+ | `static compileAll(files, renderData?, compileOpts?)` | 编译多个文件 |
888
+ | `static compileTo(data, opts, values?)` | 使用自定义检查器编译 ZIP 中的 XLSX |
889
+
890
+ ### XlsxRender
891
+
892
+ 渲染单个 Excel 文件的主类。
893
+
894
+ | 方法 | 说明 |
895
+ |:-----|:-----|
896
+ | `static create(data: Buffer, option?)` | 从 buffer 创建 |
897
+ | `render(values: Object, sheetName: string)` | 渲染特定工作表 |
898
+ | `getSheets()` | 获取所有工作表信息 |
899
+ | `generate(options?)` | 生成输出 buffer |
900
+
901
+ ### 辅助函数
902
+
903
+ | 函数 | 说明 |
904
+ |:-----|:-----|
905
+ | `ExprResolver` | 用于复杂表达式的表达式解析器 |
906
+ | `compileRuleSheetName` | 默认规则工作表名称 |
907
+ | `generateXlsxTemplate` | 生成 XLSX 模板 |
908
+ | `generateCommandsXlsxTemplate` | 使用自定义命令生成 |
909
+ | `AddCommand(name, fn)` | 添加自定义渲染函数 |
910
+
911
+ ## 完整示例
912
+
913
+ ### 模板结构
914
+
915
+ ```
916
+ template.xlsx
917
+ ├── Sheet1 (数据工作表,包含占位符)
918
+ │ ├── A1: ${contract.contractCode}
919
+ │ ├── B1: ${contract.contractTitle}
920
+ │ └── C1: ${exportData.LRR.mothOrYear}
921
+ └── export.metadata.config (规则配置工作表)
922
+ ├── 第1行: alias @#key => use aliasKey: @# => @#
923
+ ├── 第2行: mergeCell G-AQ(1-17)=sum(...)
924
+ └── 第3行: cell D-7=@#[@D.MY]
925
+ ```
926
+
927
+ ### 编译与渲染流程
928
+
929
+ | 步骤 | 输入 | 输出 | 说明 |
930
+ |:-----|:-----|:-----|:-----|
931
+ | 1. 加载 | `template.xlsx` Buffer | `ZipXlsxTemplateApp` | 加载模板文件 |
932
+ | 2. 编译 | 规则配置工作表 | 编译后的规则 | 解析 mergeCell/cell/rowCell 规则 |
933
+ | 3. 替换 | 数据对象 | 渲染后的工作表 | 替换 `${...}` 占位符 |
934
+ | 4. 生成 | - | `output.xlsx` Buffer | 最终输出文件 |
935
+
936
+ ```typescript
937
+ import { ZipXlsxTemplateApp, AddCommand } from '@vdhewei/xlsx-template-lib';
938
+ import * as fs from 'node:fs/promises';
939
+
940
+ // 定义自定义函数
941
+ AddCommand('calculateTotal', (obj, args) => {
942
+ const base = Number(args.root);
943
+ const multiplier = args.groups.length > 0 ? Number(args.groups[0]) : 1;
944
+ return base * multiplier;
945
+ });
946
+
947
+ // 主处理流程
948
+ async function processTemplate() {
949
+ const templateBuffer = await fs.readFile('template.xlsx');
950
+
951
+ const app = new ZipXlsxTemplateApp(templateBuffer);
952
+
953
+ const renderData = {
954
+ contract: {
955
+ contractCode: 'CTR-2024-001',
956
+ contractTitle: 'Monthly Return of Site Labour'
957
+ },
958
+ exportData: {
959
+ LRR: {
960
+ mothOrYear: '2024-01',
961
+ workCode: 'WC-001'
962
+ }
963
+ }
964
+ };
965
+
966
+ const compileOpts = {
967
+ sheetName: 'export.metadata.config',
968
+ remove: true // 在输出中移除规则工作表
969
+ };
970
+
971
+ await app.substituteAll(renderData, compileOpts);
972
+
973
+ const output = await app.generate();
974
+ await fs.writeFile('output.xlsx', output);
975
+ }
976
+
977
+ processTemplate();
978
+ ```
979
+
980
+ ## 注意事项
981
+
982
+ - `test_data` 目录包含内部测试模板,仅供私有使用,不可外传
983
+ - 规则配置工作表支持:`alias`、`mergeCell`、`cell`、`rowCell` 规则类型
984
+ - 可通过 `AddCommand(name, handler)` 注册自定义函数
985
+
986
+ ## 许可证
987
+
988
+ MIT
989
+
990
+ ## 作者
991
+
992
+ VDHewei
993
+
994
+ ## 仓库
995
+
996
+ https://github.com/VDHewei/xlsx-template-lib
997
+
998
+ ## 贡献
999
+
1000
+ 欢迎贡献!请随时提交 Pull Request。
1001
+
1002
+ ## 致谢
1003
+
1004
+ 本项目受到了优秀的开源项目 [xlsx-template](https://github.com/optilude/xlsx-template)(由 optilude 开发)的启发。
1005
+
1006
+ **xlsx-template** 为基于模板的 Excel 报表生成和动态数据替换提供了坚实的基础。本库的许多概念和设计模式都受到了 xlsx-template 的影响,包括:
1007
+
1008
+ - 基于模板的 Excel 文件生成
1009
+ - 占位符替换语法
1010
+ - 数组和表格展开
1011
+ - 图片插入和定位
1012
+ - 单元格格式保留
1013
+
1014
+ 我们向 xlsx-template 团队和贡献者致以诚挚的感谢,感谢他们在开源社区中的宝贵工作。
1015
+
1016
+ **原 xlsx-template 仓库:** https://github.com/optilude/xlsx-template