byteplan-cli 1.0.2 → 1.2.0

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.
Files changed (65) hide show
  1. package/package.json +7 -3
  2. package/skills/byteplan-analysis/SKILL.md +1078 -0
  3. package/skills/byteplan-api/API_REFERENCE.md +249 -0
  4. package/skills/byteplan-api/SKILL.md +96 -0
  5. package/skills/byteplan-api/package.json +16 -0
  6. package/skills/byteplan-api/scripts/api.js +973 -0
  7. package/skills/byteplan-excel/SKILL.md +212 -0
  8. package/skills/byteplan-excel/examples/margin-analysis.json +40 -0
  9. package/skills/byteplan-excel/package.json +12 -0
  10. package/skills/byteplan-excel/pnpm-lock.yaml +68 -0
  11. package/skills/byteplan-excel/scripts/generate_excel.js +156 -0
  12. package/skills/byteplan-html/SKILL.md +490 -0
  13. package/skills/byteplan-html/examples/example-output.html +184 -0
  14. package/skills/byteplan-html/examples/generate-ppt-style-html.js +611 -0
  15. package/skills/byteplan-html/examples/margin-contribution-analysis.json +152 -0
  16. package/skills/byteplan-html/package.json +18 -0
  17. package/skills/byteplan-html/scripts/generate_html.js +517 -0
  18. package/skills/byteplan-ppt/SKILL.md +394 -0
  19. package/skills/byteplan-ppt/examples/margin-contribution-analysis.json +152 -0
  20. package/skills/byteplan-ppt/package.json +16 -0
  21. package/skills/byteplan-ppt/pnpm-lock.yaml +138 -0
  22. package/skills/byteplan-ppt/scripts/check_ppt_overlap.js +318 -0
  23. package/skills/byteplan-ppt/scripts/generate_ppt.js +680 -0
  24. package/skills/byteplan-video/SKILL.md +606 -0
  25. package/skills/byteplan-video/examples/sample-video-data.json +82 -0
  26. package/skills/byteplan-video/remotion-project/package.json +22 -0
  27. package/skills/byteplan-video/remotion-project/pnpm-lock.yaml +1646 -0
  28. package/skills/byteplan-video/remotion-project/remotion.config.ts +6 -0
  29. package/skills/byteplan-video/remotion-project/scene_durations.json +32 -0
  30. package/skills/byteplan-video/remotion-project/scripts/generate_audio.js +279 -0
  31. package/skills/byteplan-video/remotion-project/src/DynamicReport.tsx +172 -0
  32. package/skills/byteplan-video/remotion-project/src/Root.tsx +51 -0
  33. package/skills/byteplan-video/remotion-project/src/SalesReport.tsx +107 -0
  34. package/skills/byteplan-video/remotion-project/src/index.tsx +4 -0
  35. package/skills/byteplan-video/remotion-project/src/scenes/ChartSlide.tsx +201 -0
  36. package/skills/byteplan-video/remotion-project/src/scenes/CoverSlide.tsx +61 -0
  37. package/skills/byteplan-video/remotion-project/src/scenes/EndSlide.tsx +60 -0
  38. package/skills/byteplan-video/remotion-project/src/scenes/InsightSlide.tsx +101 -0
  39. package/skills/byteplan-video/remotion-project/src/scenes/KpiSlide.tsx +84 -0
  40. package/skills/byteplan-video/remotion-project/src/scenes/RecommendationSlide.tsx +100 -0
  41. package/skills/byteplan-video/remotion-project/tsconfig.json +13 -0
  42. package/skills/byteplan-video/remotion-project/video_data.json +76 -0
  43. package/skills/byteplan-video/scripts/generate_video.js +270 -0
  44. package/skills/byteplan-video/templates/package.json +31 -0
  45. package/skills/byteplan-video/templates/pnpm-lock.yaml +2200 -0
  46. package/skills/byteplan-video/templates/remotion.config.ts +9 -0
  47. package/skills/byteplan-video/templates/scripts/generate-audio.ts +55 -0
  48. package/skills/byteplan-video/templates/src/components/BarChartScene.tsx +153 -0
  49. package/skills/byteplan-video/templates/src/components/InsightScene.tsx +135 -0
  50. package/skills/byteplan-video/templates/src/components/LineChartScene.tsx +214 -0
  51. package/skills/byteplan-video/templates/src/components/SceneFactory.tsx +34 -0
  52. package/skills/byteplan-video/templates/src/components/SummaryScene.tsx +155 -0
  53. package/skills/byteplan-video/templates/src/components/TitleScene.tsx +130 -0
  54. package/skills/byteplan-video/templates/src/compositions/AnalysisVideo.tsx +39 -0
  55. package/skills/byteplan-video/templates/src/index.tsx +28 -0
  56. package/skills/byteplan-video/templates/src/register-root.tsx +4 -0
  57. package/skills/byteplan-video/templates/src/storyboard/types.ts +46 -0
  58. package/skills/byteplan-video/templates/tsconfig.json +17 -0
  59. package/skills/byteplan-video/templates/tsconfig.scripts.json +13 -0
  60. package/skills/byteplan-word/SKILL.md +233 -0
  61. package/skills/byteplan-word/package.json +12 -0
  62. package/skills/byteplan-word/pnpm-lock.yaml +120 -0
  63. package/skills/byteplan-word/scripts/generate_word.js +548 -0
  64. package/src/cli.js +4 -0
  65. package/src/commands/skills.js +279 -0
@@ -0,0 +1,120 @@
1
+ lockfileVersion: '6.0'
2
+
3
+ dependencies:
4
+ docx:
5
+ specifier: ^8.5.0
6
+ version: 8.5.0
7
+
8
+ packages:
9
+
10
+ /@types/node@20.19.37:
11
+ resolution: {integrity: sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==}
12
+ dependencies:
13
+ undici-types: 6.21.0
14
+ dev: false
15
+
16
+ /core-util-is@1.0.3:
17
+ resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
18
+ dev: false
19
+
20
+ /docx@8.5.0:
21
+ resolution: {integrity: sha512-4SbcbedPXTciySXiSnNNLuJXpvxFe5nqivbiEHXyL8P/w0wx2uW7YXNjnYgjW0e2e6vy+L/tMISU/oAiXCl57Q==}
22
+ engines: {node: '>=10'}
23
+ dependencies:
24
+ '@types/node': 20.19.37
25
+ jszip: 3.10.1
26
+ nanoid: 5.1.7
27
+ xml: 1.0.1
28
+ xml-js: 1.6.11
29
+ dev: false
30
+
31
+ /immediate@3.0.6:
32
+ resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
33
+ dev: false
34
+
35
+ /inherits@2.0.4:
36
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
37
+ dev: false
38
+
39
+ /isarray@1.0.0:
40
+ resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
41
+ dev: false
42
+
43
+ /jszip@3.10.1:
44
+ resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
45
+ dependencies:
46
+ lie: 3.3.0
47
+ pako: 1.0.11
48
+ readable-stream: 2.3.8
49
+ setimmediate: 1.0.5
50
+ dev: false
51
+
52
+ /lie@3.3.0:
53
+ resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
54
+ dependencies:
55
+ immediate: 3.0.6
56
+ dev: false
57
+
58
+ /nanoid@5.1.7:
59
+ resolution: {integrity: sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==}
60
+ engines: {node: ^18 || >=20}
61
+ hasBin: true
62
+ dev: false
63
+
64
+ /pako@1.0.11:
65
+ resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
66
+ dev: false
67
+
68
+ /process-nextick-args@2.0.1:
69
+ resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
70
+ dev: false
71
+
72
+ /readable-stream@2.3.8:
73
+ resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
74
+ dependencies:
75
+ core-util-is: 1.0.3
76
+ inherits: 2.0.4
77
+ isarray: 1.0.0
78
+ process-nextick-args: 2.0.1
79
+ safe-buffer: 5.1.2
80
+ string_decoder: 1.1.1
81
+ util-deprecate: 1.0.2
82
+ dev: false
83
+
84
+ /safe-buffer@5.1.2:
85
+ resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
86
+ dev: false
87
+
88
+ /sax@1.6.0:
89
+ resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==}
90
+ engines: {node: '>=11.0.0'}
91
+ dev: false
92
+
93
+ /setimmediate@1.0.5:
94
+ resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
95
+ dev: false
96
+
97
+ /string_decoder@1.1.1:
98
+ resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
99
+ dependencies:
100
+ safe-buffer: 5.1.2
101
+ dev: false
102
+
103
+ /undici-types@6.21.0:
104
+ resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
105
+ dev: false
106
+
107
+ /util-deprecate@1.0.2:
108
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
109
+ dev: false
110
+
111
+ /xml-js@1.6.11:
112
+ resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==}
113
+ hasBin: true
114
+ dependencies:
115
+ sax: 1.6.0
116
+ dev: false
117
+
118
+ /xml@1.0.1:
119
+ resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==}
120
+ dev: false
@@ -0,0 +1,548 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * 数据分析报告 Word 文档生成脚本(通用模板)
4
+ * 使用 docx 库创建专业 Word 文档
5
+ *
6
+ * 设计原则:
7
+ * - 模板与业务解耦,通过数据配置生成内容
8
+ * - 支持任意维度的数据可视化
9
+ * - 生成包含封面、目录、正文、附录等完整结构
10
+ */
11
+
12
+ import { Document, Paragraph, TextRun, Table, TableRow, TableCell,
13
+ HeadingLevel, AlignmentType, BorderStyle, WidthType,
14
+ PageBreak, ShadingType, convertInchesToTwip, Packer } from 'docx';
15
+ import fs from 'fs';
16
+ import path from 'path';
17
+
18
+ // 颜色配置(十六进制)
19
+ const COLORS = {
20
+ primary: '0F172A',
21
+ secondary: '1E293B',
22
+ blue: '3B82F6',
23
+ green: '10B185',
24
+ purple: '8B5CF6',
25
+ red: 'EF4444',
26
+ orange: 'F97316',
27
+ textDark: '0F172A',
28
+ textGray: '64748B',
29
+ white: 'FFFFFF',
30
+ lightGray: 'F1F5F9',
31
+ };
32
+
33
+ // 图表配色循环
34
+ const CHART_COLORS = [COLORS.blue, COLORS.green, COLORS.purple, COLORS.orange, COLORS.red];
35
+
36
+ // ============ 辅助函数 ============
37
+
38
+ function createTextRun(text, options = {}) {
39
+ const { bold = false, size = 24, color = COLORS.textDark, font = 'Microsoft YaHei' } = options;
40
+ return new TextRun({ text, bold, size, color, font });
41
+ }
42
+
43
+ function createParagraph(text, options = {}) {
44
+ const { heading, alignment = AlignmentType.LEFT, spacing = {}, children } = options;
45
+
46
+ const textRuns = children || (text ? [createTextRun(text, options)] : []);
47
+
48
+ return new Paragraph({
49
+ heading,
50
+ alignment,
51
+ spacing,
52
+ children: textRuns,
53
+ });
54
+ }
55
+
56
+ // ============ 封面页 ============
57
+
58
+ function createCoverPage(data) {
59
+ const elements = [];
60
+
61
+ // 空行撑开页面
62
+ for (let i = 0; i < 5; i++) {
63
+ elements.push(createParagraph(''));
64
+ }
65
+
66
+ // 主标题
67
+ elements.push(createParagraph(data.title || '数据分析报告', {
68
+ alignment: AlignmentType.CENTER,
69
+ bold: true,
70
+ size: 72,
71
+ }));
72
+
73
+ // 副标题
74
+ if (data.subtitle) {
75
+ elements.push(createParagraph(data.subtitle, {
76
+ alignment: AlignmentType.CENTER,
77
+ size: 40,
78
+ color: COLORS.textGray,
79
+ }));
80
+ }
81
+
82
+ // 空行
83
+ for (let i = 0; i < 4; i++) {
84
+ elements.push(createParagraph(''));
85
+ }
86
+
87
+ // 数据周期
88
+ if (data.period) {
89
+ elements.push(createParagraph(`数据周期: ${data.period}`, {
90
+ alignment: AlignmentType.CENTER,
91
+ size: 28,
92
+ color: COLORS.textGray,
93
+ }));
94
+ }
95
+
96
+ // 生成时间
97
+ const genTime = new Date().toISOString().slice(0, 10);
98
+ elements.push(createParagraph(`生成时间: ${genTime}`, {
99
+ alignment: AlignmentType.CENTER,
100
+ size: 24,
101
+ color: COLORS.textGray,
102
+ }));
103
+
104
+ // 数据来源
105
+ elements.push(createParagraph(`数据来源: ${data.source || 'BytePlan 数据平台'}`, {
106
+ alignment: AlignmentType.CENTER,
107
+ size: 24,
108
+ color: COLORS.textGray,
109
+ }));
110
+
111
+ // 分页
112
+ elements.push(new Paragraph({ children: [new PageBreak()] }));
113
+
114
+ return elements;
115
+ }
116
+
117
+ // ============ 目录页 ============
118
+
119
+ function createTocPage(sections) {
120
+ const elements = [];
121
+
122
+ elements.push(createParagraph('目录', { heading: HeadingLevel.HEADING_1 }));
123
+
124
+ const tocItems = sections || [
125
+ '一、核心指标概览',
126
+ '二、数据分布分析',
127
+ '三、排行与对比',
128
+ '四、趋势与洞察',
129
+ '五、总结与建议',
130
+ ];
131
+
132
+ tocItems.forEach(section => {
133
+ elements.push(createParagraph(section, { size: 28 }));
134
+ });
135
+
136
+ elements.push(new Paragraph({ children: [new PageBreak()] }));
137
+
138
+ return elements;
139
+ }
140
+
141
+ // ============ 执行摘要 ============
142
+
143
+ function createSummaryPage(summary) {
144
+ const elements = [];
145
+
146
+ elements.push(createParagraph('执行摘要', { heading: HeadingLevel.HEADING_1 }));
147
+
148
+ const items = summary || ['暂无数据摘要'];
149
+
150
+ items.forEach(item => {
151
+ elements.push(createParagraph(`• ${item}`, {
152
+ size: 24,
153
+ color: COLORS.textGray,
154
+ }));
155
+ });
156
+
157
+ elements.push(createParagraph(''));
158
+
159
+ return elements;
160
+ }
161
+
162
+ // ============ 核心 KPI ============
163
+
164
+ function createKpiPage(kpis) {
165
+ const elements = [];
166
+
167
+ elements.push(createParagraph('核心指标概览', { heading: HeadingLevel.HEADING_1 }));
168
+
169
+ const kpiList = kpis || [
170
+ { label: '总数量', value: '0', unit: '' },
171
+ { label: '总金额', value: '0', unit: '' },
172
+ { label: '平均值', value: '0', unit: '' },
173
+ { label: '最大值', value: '0', unit: '' },
174
+ ];
175
+
176
+ // 创建 KPI 表格
177
+ const table = new Table({
178
+ width: { size: 100, type: WidthType.PERCENTAGE },
179
+ rows: [
180
+ // 表头
181
+ new TableRow({
182
+ children: kpiList.map(kpi => new TableCell({
183
+ shading: { fill: COLORS.blue, type: ShadingType.CLEAR, color: COLORS.blue },
184
+ children: [createParagraph(kpi.label || '', {
185
+ alignment: AlignmentType.CENTER,
186
+ bold: true,
187
+ size: 22,
188
+ color: COLORS.white,
189
+ })],
190
+ })),
191
+ }),
192
+ // 数值行
193
+ new TableRow({
194
+ children: kpiList.map(kpi => new TableCell({
195
+ children: [createParagraph(`${kpi.value || ''} ${kpi.unit || ''}`.trim(), {
196
+ alignment: AlignmentType.CENTER,
197
+ bold: true,
198
+ size: 32,
199
+ color: COLORS.blue,
200
+ })],
201
+ })),
202
+ }),
203
+ ],
204
+ });
205
+
206
+ elements.push(table);
207
+ elements.push(createParagraph(''));
208
+
209
+ return elements;
210
+ }
211
+
212
+ // ============ 柱状图数据 ============
213
+
214
+ function createBarChartPage(barChart) {
215
+ const elements = [];
216
+
217
+ const data = barChart?.data || [];
218
+ if (data.length === 0) return elements;
219
+
220
+ elements.push(createParagraph(barChart?.title || '数据分布分析', { heading: HeadingLevel.HEADING_1 }));
221
+
222
+ const maxValue = Math.max(...data.map(d => d.value || 0), 1);
223
+
224
+ data.forEach(item => {
225
+ const value = item.value || 0;
226
+ const barLength = Math.round(30 * value / maxValue);
227
+ const bar = '█'.repeat(barLength);
228
+
229
+ elements.push(createParagraph(`${item.name || ''}: ${bar} ${value}`, {
230
+ size: 22,
231
+ }));
232
+ });
233
+
234
+ elements.push(createParagraph(''));
235
+
236
+ return elements;
237
+ }
238
+
239
+ // ============ 占比分析 ============
240
+
241
+ function createRatioPage(ratioData) {
242
+ const elements = [];
243
+
244
+ const data = ratioData?.data || [];
245
+ if (data.length === 0) return elements;
246
+
247
+ elements.push(createParagraph(ratioData?.title || '占比分析', { heading: HeadingLevel.HEADING_1 }));
248
+
249
+ const total = data.reduce((sum, d) => sum + (d.value || 0), 0);
250
+
251
+ data.forEach((item, i) => {
252
+ const value = item.value || 0;
253
+ const ratio = total > 0 ? (value / total * 100) : 0;
254
+ const barLength = Math.round(30 * ratio / 100);
255
+ const bar = '█'.repeat(barLength) + '░'.repeat(30 - barLength);
256
+
257
+ elements.push(createParagraph(`${item.name || ''}: ${bar} ${ratio.toFixed(1)}%`, {
258
+ size: 22,
259
+ color: CHART_COLORS[i % CHART_COLORS.length],
260
+ }));
261
+ });
262
+
263
+ elements.push(createParagraph(''));
264
+
265
+ return elements;
266
+ }
267
+
268
+ // ============ 排行分析 ============
269
+
270
+ function createRankingPage(rankingData) {
271
+ const elements = [];
272
+
273
+ const data = rankingData?.data || [];
274
+ if (data.length === 0) return elements;
275
+
276
+ elements.push(createParagraph(rankingData?.title || '排行分析', { heading: HeadingLevel.HEADING_1 }));
277
+
278
+ const maxItems = rankingData?.maxItems || 10;
279
+ const sortedData = [...data].sort((a, b) => (b.value || 0) - (a.value || 0)).slice(0, maxItems);
280
+ const maxValue = Math.max(...sortedData.map(d => d.value || 0), 1);
281
+
282
+ sortedData.forEach((item, i) => {
283
+ const value = item.value || 0;
284
+ const barLength = Math.round(30 * value / maxValue);
285
+ const bar = '█'.repeat(barLength);
286
+
287
+ elements.push(createParagraph(`${i + 1}. ${item.name || ''}: ${bar} ${value}`, {
288
+ size: 22,
289
+ }));
290
+ });
291
+
292
+ elements.push(createParagraph(''));
293
+
294
+ return elements;
295
+ }
296
+
297
+ // ============ 数据表格 ============
298
+
299
+ function createTablePage(tableData) {
300
+ const elements = [];
301
+
302
+ const rows = tableData?.rows || [];
303
+ if (rows.length === 0) return elements;
304
+
305
+ elements.push(createParagraph(tableData?.title || '数据明细', { heading: HeadingLevel.HEADING_1 }));
306
+
307
+ const columns = tableData?.columns || Object.keys(rows[0] || {}).map(k => ({ key: k, label: k }));
308
+ const maxRows = tableData?.maxRows || 20;
309
+ const displayRows = rows.slice(0, maxRows);
310
+
311
+ const table = new Table({
312
+ width: { size: 100, type: WidthType.PERCENTAGE },
313
+ rows: [
314
+ // 表头
315
+ new TableRow({
316
+ children: columns.map(col => new TableCell({
317
+ shading: { fill: COLORS.blue, type: ShadingType.CLEAR, color: COLORS.blue },
318
+ children: [createParagraph(col.label || col.key || '', {
319
+ bold: true,
320
+ size: 20,
321
+ color: COLORS.white,
322
+ })],
323
+ })),
324
+ }),
325
+ // 数据行
326
+ ...displayRows.map((rowData, rowIdx) => new TableRow({
327
+ children: columns.map(col => {
328
+ const value = rowData[col.key] ?? '';
329
+ const displayValue = typeof value === 'number' ? value.toFixed(2) : String(value);
330
+ const isEven = rowIdx % 2 === 0;
331
+
332
+ return new TableCell({
333
+ shading: isEven ? { fill: COLORS.lightGray, type: ShadingType.CLEAR, color: COLORS.lightGray } : undefined,
334
+ children: [createParagraph(displayValue, {
335
+ size: 20,
336
+ color: COLORS.textGray,
337
+ })],
338
+ });
339
+ }),
340
+ })),
341
+ ],
342
+ });
343
+
344
+ elements.push(table);
345
+ elements.push(createParagraph(''));
346
+
347
+ return elements;
348
+ }
349
+
350
+ // ============ 洞察发现 ============
351
+
352
+ function createInsightPage(insights) {
353
+ const elements = [];
354
+
355
+ elements.push(createParagraph('关键洞察', { heading: HeadingLevel.HEADING_1 }));
356
+
357
+ const items = insights || [
358
+ { title: '数据特征', content: '本期数据整体表现稳定,呈现上升趋势' },
359
+ { title: '异常发现', content: '部分维度存在显著差异,需关注' },
360
+ ];
361
+
362
+ items.forEach((item, i) => {
363
+ elements.push(createParagraph(`${i + 1}. ${item.title || ''}`, {
364
+ heading: HeadingLevel.HEADING_2,
365
+ color: COLORS.blue,
366
+ }));
367
+
368
+ elements.push(createParagraph(item.content || '', {
369
+ size: 24,
370
+ color: COLORS.textGray,
371
+ }));
372
+ });
373
+
374
+ elements.push(createParagraph(''));
375
+
376
+ return elements;
377
+ }
378
+
379
+ // ============ 建议措施 ============
380
+
381
+ function createRecommendationPage(recommendations) {
382
+ const elements = [];
383
+
384
+ elements.push(createParagraph('总结与建议', { heading: HeadingLevel.HEADING_1 }));
385
+
386
+ const items = recommendations || [
387
+ { title: '短期建议', content: '持续关注数据变化,及时调整策略' },
388
+ { title: '长期规划', content: '建立数据驱动决策机制' },
389
+ ];
390
+
391
+ items.forEach((item, i) => {
392
+ elements.push(createParagraph(`${i + 1}. ${item.title || ''}`, {
393
+ heading: HeadingLevel.HEADING_2,
394
+ color: COLORS.green,
395
+ }));
396
+
397
+ elements.push(createParagraph(item.content || '', {
398
+ size: 24,
399
+ color: COLORS.textGray,
400
+ }));
401
+ });
402
+
403
+ elements.push(createParagraph(''));
404
+
405
+ return elements;
406
+ }
407
+
408
+ // ============ 附录 ============
409
+
410
+ function createAppendixPage(appendix) {
411
+ const elements = [];
412
+
413
+ if (!appendix || Object.keys(appendix).length === 0) return elements;
414
+
415
+ elements.push(createParagraph('附录:补充数据', { heading: HeadingLevel.HEADING_1 }));
416
+
417
+ Object.entries(appendix).forEach(([key, value]) => {
418
+ elements.push(createParagraph('', {
419
+ children: [
420
+ createTextRun(`${key}: `, { bold: true, size: 24 }),
421
+ createTextRun(String(value), { size: 24, color: COLORS.textGray }),
422
+ ],
423
+ }));
424
+ });
425
+
426
+ elements.push(new Paragraph({ children: [new PageBreak()] }));
427
+
428
+ return elements;
429
+ }
430
+
431
+ // ============ 结尾页 ============
432
+
433
+ function createEndingPage(source) {
434
+ const elements = [];
435
+
436
+ for (let i = 0; i < 6; i++) {
437
+ elements.push(createParagraph(''));
438
+ }
439
+
440
+ elements.push(createParagraph('— 报告结束 —', {
441
+ alignment: AlignmentType.CENTER,
442
+ bold: true,
443
+ size: 32,
444
+ }));
445
+
446
+ for (let i = 0; i < 2; i++) {
447
+ elements.push(createParagraph(''));
448
+ }
449
+
450
+ elements.push(createParagraph(`数据来源: ${source || 'BytePlan 数据平台'}`, {
451
+ alignment: AlignmentType.CENTER,
452
+ size: 24,
453
+ color: COLORS.textGray,
454
+ }));
455
+
456
+ const genTime = new Date().toISOString().slice(0, 10);
457
+ elements.push(createParagraph(`生成时间: ${genTime}`, {
458
+ alignment: AlignmentType.CENTER,
459
+ size: 22,
460
+ color: COLORS.textGray,
461
+ }));
462
+
463
+ return elements;
464
+ }
465
+
466
+ // ============ 主函数 ============
467
+
468
+ function generateWord(outputFile, dataFile = null) {
469
+ // 加载数据
470
+ let data = {};
471
+
472
+ if (dataFile) {
473
+ const filePath = path.resolve(dataFile);
474
+ if (fs.existsSync(filePath)) {
475
+ data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
476
+ }
477
+ } else {
478
+ const defaultPath = path.resolve('word_data.json');
479
+ if (fs.existsSync(defaultPath)) {
480
+ data = JSON.parse(fs.readFileSync(defaultPath, 'utf-8'));
481
+ }
482
+ }
483
+
484
+ // 创建文档
485
+ const doc = new Document({
486
+ sections: [{
487
+ properties: {},
488
+ children: [
489
+ // 1. 封面
490
+ ...createCoverPage(data),
491
+
492
+ // 2. 目录
493
+ ...createTocPage(data.sections),
494
+
495
+ // 3. 执行摘要
496
+ ...createSummaryPage(data.summary),
497
+
498
+ // 4. 核心 KPI
499
+ ...createKpiPage(data.kpis),
500
+
501
+ // 5. 柱状图数据
502
+ ...createBarChartPage(data.barChart),
503
+
504
+ // 6. 占比分析
505
+ ...createRatioPage(data.ratioData),
506
+
507
+ // 7. 排行分析
508
+ ...createRankingPage(data.rankingData),
509
+
510
+ // 8. 数据明细表
511
+ ...createTablePage(data.tableData),
512
+
513
+ // 9. 关键洞察
514
+ ...createInsightPage(data.insights),
515
+
516
+ // 10. 总结与建议
517
+ ...createRecommendationPage(data.recommendations),
518
+
519
+ // 11. 附录
520
+ ...createAppendixPage(data.appendix),
521
+
522
+ // 12. 结尾页
523
+ ...createEndingPage(data.source),
524
+ ],
525
+ }],
526
+ });
527
+
528
+ // 保存
529
+ Packer.toBuffer(doc).then(buffer => {
530
+ fs.writeFileSync(outputFile, buffer);
531
+ console.log(`Successfully generated Word document -> ${outputFile}`);
532
+ });
533
+ }
534
+
535
+ // CLI 入口
536
+ const args = process.argv.slice(2);
537
+ let outputFile = 'analysis-report.docx';
538
+ let dataFile = null;
539
+
540
+ for (let i = 0; i < args.length; i++) {
541
+ if (args[i] === '-o' || args[i] === '--output-file') {
542
+ outputFile = args[++i];
543
+ } else if (args[i] === '-d' || args[i] === '--data-file') {
544
+ dataFile = args[++i];
545
+ }
546
+ }
547
+
548
+ generateWord(outputFile, dataFile);
package/src/cli.js CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  getDimValues,
18
18
  getLovValues,
19
19
  } from './api.js';
20
+ import { skillsCmd } from './commands/skills.js';
20
21
 
21
22
  const require = createRequire(import.meta.url);
22
23
  const { version } = require('../package.json');
@@ -298,4 +299,7 @@ program
298
299
  }
299
300
  });
300
301
 
302
+ // Skills commands
303
+ program.addCommand(skillsCmd);
304
+
301
305
  program.parse();