@liuhange/dsh-data-visualization 2.0.1 → 2.0.2

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.
package/package.json CHANGED
@@ -1,39 +1,53 @@
1
- {
2
- "name": "@liuhange/dsh-data-visualization",
3
- "description": "Data visualization plugin: generate self-contained HTML reports with interactive charts (bar/comparison/pie/radar/flowchart)",
4
- "version": "2.0.1",
5
- "publishConfig": {
6
- "access": "public"
7
- },
8
- "repository": {
9
- "type": "git",
10
- "url": "git+https://github.com/liuhange789/data-asset-inspector.git",
11
- "directory": "packages/data-asset/data-visualization"
12
- },
13
- "type": "module",
14
- "main": "lib/index.js",
15
- "types": "lib/types/index.d.ts",
16
- "exports": {
17
- ".": {
18
- "types": "./lib/types/index.d.ts",
19
- "default": "./lib/index.js"
20
- },
21
- "./invariant": {
22
- "types": "./lib/types/invariant.d.ts",
23
- "default": "./lib/invariant.js"
24
- },
25
- "./src/*": "./src/*",
26
- "./package.json": "./package.json"
27
- },
28
- "files": [
29
- "lib/index.js",
30
- "lib/invariant.js",
31
- "lib/types/**/*.d.ts"
32
- ],
33
- "license": "MIT",
34
- "peerDependencies": {
35
- "@deepseek-ai/cordis": "^0.1.0",
36
- "@deepseek-ai/dsh-tools": "^0.1.0",
37
- "@liuhange/dsh-data-asset-shared": "^2.0.0"
38
- }
39
- }
1
+ {
2
+ "name": "@liuhange/dsh-data-visualization",
3
+ "description": "Data visualization plugin: generate self-contained HTML reports with interactive charts (bar/comparison/pie/radar/flowchart) Also known as @deepseek-ai/dsh-data-visualization.",
4
+ "version": "2.0.2",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/liuhange789/data-asset-inspector.git",
11
+ "directory": "packages/data-asset/data-visualization"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./invariant": {
22
+ "types": "./lib/types/invariant.d.ts",
23
+ "default": "./lib/invariant.js"
24
+ },
25
+ "./src/*": "./src/*",
26
+ "./package.json": "./package.json"
27
+ },
28
+ "files": [
29
+ "lib/index.js",
30
+ "lib/invariant.js",
31
+ "lib/types/**/*.d.ts"
32
+ ],
33
+ "license": "MIT",
34
+ "scripts": {
35
+ "typecheck": "tsc --noEmit",
36
+ "test": "vitest run",
37
+ "build": "tsc"
38
+ },
39
+ "peerDependencies": {
40
+ "@deepseek-ai/cordis": "^4.0.0",
41
+ "@deepseek-ai/dsh-tools": "0.0.1-rc.1",
42
+ "@liuhange/dsh-data-asset-shared": "^2.0.0"
43
+ },
44
+ "keywords": [
45
+ "deepseek-harness",
46
+ "dsh-plugin",
47
+ "data-asset",
48
+ "data-asset-inspector",
49
+ "shujintong",
50
+ "数据资产"
51
+ ],
52
+ "homepage": "https://github.com/liuhange789/data-asset-inspector#readme"
53
+ }
package/lib/index.js DELETED
@@ -1,573 +0,0 @@
1
- import { defineTool } from "@deepseek-ai/dsh-tools";
2
- import * as fs from "fs";
3
- import * as path from "path";
4
- import { AuditLogger, BusinessRulesLoader, PathValidator } from "@liuhange/dsh-data-asset-shared";
5
- //#region lib/types/defaultVisualizationConfig.js
6
- const defaultVisualizationConfig = {
7
- charts: {
8
- bar: {
9
- enabled: true,
10
- title: "脱敏统计"
11
- },
12
- comparison: {
13
- enabled: true,
14
- title: "清洗前后对比"
15
- },
16
- pie: {
17
- enabled: true,
18
- title: "数据资产分布"
19
- },
20
- radar: {
21
- enabled: true,
22
- title: "质量评分"
23
- },
24
- flowchart: {
25
- enabled: true,
26
- title: "血缘追踪"
27
- }
28
- },
29
- layout: {
30
- columns: 2,
31
- responsive: true
32
- },
33
- colorScheme: {
34
- primary: "#4A90D9",
35
- secondary: "#F5A623",
36
- success: "#7ED321",
37
- warning: "#F8E71C",
38
- danger: "#D0021B",
39
- background: "#F5F5F5",
40
- text: "#333333"
41
- },
42
- interactions: {
43
- expandCollapse: true,
44
- chartSwitch: true,
45
- tooltip: true
46
- },
47
- templateVersion: "1.0"
48
- };
49
- //#endregion
50
- //#region lib/types/reportDataLoader.js
51
- var ReportDataLoader = class {
52
- async load(filePath) {
53
- if (!fs.existsSync(filePath)) return {
54
- data: null,
55
- error: "文件不存在"
56
- };
57
- try {
58
- const content = fs.readFileSync(filePath, "utf-8");
59
- return { data: JSON.parse(content) };
60
- } catch {
61
- return {
62
- data: null,
63
- error: "数据格式错误"
64
- };
65
- }
66
- }
67
- };
68
- //#endregion
69
- //#region lib/types/chartRenderers/barChartRenderer.js
70
- var BarChartRenderer = class {
71
- render(data, config) {
72
- const title = config.charts.bar?.title ?? "脱敏统计";
73
- const primary = config.colorScheme.primary;
74
- if (data === null || data === void 0) return {
75
- type: "bar",
76
- title,
77
- svgContent: this.placeholder(title, "数据不可用"),
78
- dataAvailable: false
79
- };
80
- const counts = this.extractCounts(data);
81
- if (counts.length === 0) return {
82
- type: "bar",
83
- title,
84
- svgContent: this.placeholder(title, "无数据"),
85
- dataAvailable: false
86
- };
87
- const maxCount = Math.max(...counts.map((c) => c.value));
88
- const barWidth = 60;
89
- const chartHeight = 200;
90
- const startX = 40;
91
- let svg = `<svg width="${startX + counts.length * 80}" height="260" xmlns="http://www.w3.org/2000/svg">`;
92
- for (let i = 0; i < counts.length; i++) {
93
- const item = counts[i];
94
- const barHeight = maxCount > 0 ? item.value / maxCount * chartHeight : 0;
95
- const x = startX + i * 80;
96
- const y = 220 - barHeight;
97
- svg += `<rect x="${x}" y="${y}" width="${barWidth}" height="${barHeight}" fill="${primary}" />`;
98
- svg += `<text x="${x + barWidth / 2}" y="240" text-anchor="middle" fill="${config.colorScheme.text}" font-size="12">${item.label}</text>`;
99
- svg += `<text x="${x + barWidth / 2}" y="${y - 5}" text-anchor="middle" fill="${config.colorScheme.text}" font-size="12">${item.value}</text>`;
100
- }
101
- svg += "</svg>";
102
- return {
103
- type: "bar",
104
- title,
105
- svgContent: svg,
106
- dataAvailable: true
107
- };
108
- }
109
- extractCounts(data) {
110
- if (typeof data === "object" && data !== null) {
111
- const obj = data;
112
- if ("fieldTypeCounts" in obj) {
113
- const counts = obj.fieldTypeCounts;
114
- return Object.entries(counts).map(([label, value]) => ({
115
- label,
116
- value
117
- }));
118
- }
119
- }
120
- return [];
121
- }
122
- placeholder(title, message) {
123
- return `<svg width="300" height="100" xmlns="http://www.w3.org/2000/svg"><text x="150" y="50" text-anchor="middle" fill="#999" font-size="14">${title}: ${message}</text></svg>`;
124
- }
125
- };
126
- //#endregion
127
- //#region lib/types/chartRenderers/comparisonChartRenderer.js
128
- var ComparisonChartRenderer = class {
129
- render(data, config) {
130
- const title = config.charts.comparison?.title ?? "清洗前后对比";
131
- const primary = config.colorScheme.primary;
132
- const secondary = config.colorScheme.secondary;
133
- if (data === null || data === void 0) return {
134
- type: "comparison",
135
- title,
136
- svgContent: this.placeholder(title, "数据不可用"),
137
- dataAvailable: false
138
- };
139
- const { before, after } = this.extractData(data);
140
- if (before === 0 && after === 0) return {
141
- type: "comparison",
142
- title,
143
- svgContent: this.placeholder(title, "无数据"),
144
- dataAvailable: false
145
- };
146
- const maxValue = Math.max(before, after, 1);
147
- const chartHeight = 200;
148
- const barWidth = 80;
149
- const startX = 40;
150
- const beforeHeight = before / maxValue * chartHeight;
151
- const afterHeight = after / maxValue * chartHeight;
152
- let svg = `<svg width="280" height="260" xmlns="http://www.w3.org/2000/svg">`;
153
- svg += `<rect x="${startX}" y="${220 - beforeHeight}" width="${barWidth}" height="${beforeHeight}" fill="${primary}" />`;
154
- svg += `<text x="80" y="240" text-anchor="middle" fill="${config.colorScheme.text}" font-size="12">清洗前</text>`;
155
- svg += `<text x="80" y="${220 - beforeHeight - 5}" text-anchor="middle" fill="${config.colorScheme.text}" font-size="12">${before}</text>`;
156
- svg += `<rect x="160" y="${220 - afterHeight}" width="${barWidth}" height="${afterHeight}" fill="${secondary}" />`;
157
- svg += `<text x="200" y="240" text-anchor="middle" fill="${config.colorScheme.text}" font-size="12">清洗后</text>`;
158
- svg += `<text x="200" y="${220 - afterHeight - 5}" text-anchor="middle" fill="${config.colorScheme.text}" font-size="12">${after}</text>`;
159
- svg += "</svg>";
160
- return {
161
- type: "comparison",
162
- title,
163
- svgContent: svg,
164
- dataAvailable: true
165
- };
166
- }
167
- extractData(data) {
168
- if (typeof data === "object" && data !== null) {
169
- const obj = data;
170
- return {
171
- before: "originalLineCount" in obj ? Number(obj.originalLineCount) : 0,
172
- after: "cleanedLineCount" in obj ? Number(obj.cleanedLineCount) : 0
173
- };
174
- }
175
- return {
176
- before: 0,
177
- after: 0
178
- };
179
- }
180
- placeholder(title, message) {
181
- return `<svg width="300" height="100" xmlns="http://www.w3.org/2000/svg"><text x="150" y="50" text-anchor="middle" fill="#999" font-size="14">${title}: ${message}</text></svg>`;
182
- }
183
- };
184
- //#endregion
185
- //#region lib/types/chartRenderers/pieChartRenderer.js
186
- var PieChartRenderer = class {
187
- render(data, config) {
188
- const title = config.charts.pie?.title ?? "数据资产分布";
189
- const colors = [
190
- config.colorScheme.primary,
191
- config.colorScheme.secondary,
192
- config.colorScheme.success,
193
- config.colorScheme.warning,
194
- config.colorScheme.danger
195
- ];
196
- if (data === null || data === void 0) return {
197
- type: "pie",
198
- title,
199
- svgContent: this.placeholder(title, "数据不可用"),
200
- dataAvailable: false
201
- };
202
- const items = this.extractData(data);
203
- if (items.length === 0) return {
204
- type: "pie",
205
- title,
206
- svgContent: this.placeholder(title, "无数据"),
207
- dataAvailable: false
208
- };
209
- const total = items.reduce((sum, item) => sum + item.value, 0);
210
- if (total === 0) return {
211
- type: "pie",
212
- title,
213
- svgContent: this.placeholder(title, "无数据"),
214
- dataAvailable: false
215
- };
216
- const cx = 150;
217
- const cy = 100;
218
- const r = 80;
219
- let currentAngle = -Math.PI / 2;
220
- let svg = `<svg width="300" height="200" xmlns="http://www.w3.org/2000/svg">`;
221
- for (let i = 0; i < items.length; i++) {
222
- const angle = items[i].value / total * 2 * Math.PI;
223
- const x1 = cx + r * Math.cos(currentAngle);
224
- const y1 = cy + r * Math.sin(currentAngle);
225
- const x2 = cx + r * Math.cos(currentAngle + angle);
226
- const y2 = cy + r * Math.sin(currentAngle + angle);
227
- const largeArc = angle > Math.PI ? 1 : 0;
228
- const color = colors[i % colors.length];
229
- svg += `<path d="M${cx},${cy} L${x1},${y1} A${r},${r} 0 ${largeArc},1 ${x2},${y2} Z" fill="${color}" />`;
230
- currentAngle += angle;
231
- }
232
- svg += "</svg>";
233
- return {
234
- type: "pie",
235
- title,
236
- svgContent: svg,
237
- dataAvailable: true
238
- };
239
- }
240
- extractData(data) {
241
- if (typeof data === "object" && data !== null) {
242
- const obj = data;
243
- if ("assetItems" in obj && Array.isArray(obj.assetItems)) return obj.assetItems.map((item) => ({
244
- label: String(item.fileName ?? "unknown"),
245
- value: Number(item.size ?? 0)
246
- }));
247
- }
248
- return [];
249
- }
250
- placeholder(title, message) {
251
- return `<svg width="300" height="100" xmlns="http://www.w3.org/2000/svg"><text x="150" y="50" text-anchor="middle" fill="#999" font-size="14">${title}: ${message}</text></svg>`;
252
- }
253
- };
254
- //#endregion
255
- //#region lib/types/chartRenderers/radarChartRenderer.js
256
- var RadarChartRenderer = class {
257
- render(data, config) {
258
- const title = config.charts.radar?.title ?? "质量评分";
259
- const primary = config.colorScheme.primary;
260
- if (data === null || data === void 0) return {
261
- type: "radar",
262
- title,
263
- svgContent: this.placeholder(title, "数据不可用"),
264
- dataAvailable: false
265
- };
266
- const dimensions = this.extractData(data);
267
- if (dimensions.length === 0) return {
268
- type: "radar",
269
- title,
270
- svgContent: this.placeholder(title, "无数据"),
271
- dataAvailable: false
272
- };
273
- const cx = 150;
274
- const cy = 100;
275
- const maxR = 80;
276
- const n = dimensions.length;
277
- let svg = `<svg width="300" height="200" xmlns="http://www.w3.org/2000/svg">`;
278
- for (let i = 0; i < n; i++) {
279
- const angle = i / n * 2 * Math.PI - Math.PI / 2;
280
- const x = cx + maxR * Math.cos(angle);
281
- const y = cy + maxR * Math.sin(angle);
282
- svg += `<line x1="${cx}" y1="${cy}" x2="${x}" y2="${y}" stroke="#ddd" stroke-width="1" />`;
283
- const labelX = cx + 95 * Math.cos(angle);
284
- const labelY = cy + 95 * Math.sin(angle);
285
- svg += `<text x="${labelX}" y="${labelY}" text-anchor="middle" fill="${config.colorScheme.text}" font-size="10">${dimensions[i].label}</text>`;
286
- }
287
- let pathData = "";
288
- for (let i = 0; i < n; i++) {
289
- const angle = i / n * 2 * Math.PI - Math.PI / 2;
290
- const r = dimensions[i].value / 100 * maxR;
291
- const x = cx + r * Math.cos(angle);
292
- const y = cy + r * Math.sin(angle);
293
- pathData += (i === 0 ? "M" : "L") + `${x},${y} `;
294
- }
295
- pathData += "Z";
296
- svg += `<path d="${pathData}" fill="${primary}" fill-opacity="0.3" stroke="${primary}" stroke-width="2" />`;
297
- svg += "</svg>";
298
- return {
299
- type: "radar",
300
- title,
301
- svgContent: svg,
302
- dataAvailable: true
303
- };
304
- }
305
- extractData(data) {
306
- if (typeof data === "object" && data !== null) {
307
- const obj = data;
308
- if ("dimensions" in obj && typeof obj.dimensions === "object" && obj.dimensions !== null) {
309
- const dims = obj.dimensions;
310
- return Object.entries(dims).map(([key, val]) => ({
311
- label: key,
312
- value: Number(val.score ?? 0)
313
- }));
314
- }
315
- }
316
- return [];
317
- }
318
- placeholder(title, message) {
319
- return `<svg width="300" height="100" xmlns="http://www.w3.org/2000/svg"><text x="150" y="50" text-anchor="middle" fill="#999" font-size="14">${title}: ${message}</text></svg>`;
320
- }
321
- };
322
- //#endregion
323
- //#region lib/types/chartRenderers/flowchartRenderer.js
324
- var FlowchartRenderer = class {
325
- render(data, config) {
326
- const title = config.charts.flowchart?.title ?? "血缘追踪";
327
- const primary = config.colorScheme.primary;
328
- if (data === null || data === void 0) return {
329
- type: "flowchart",
330
- title,
331
- svgContent: this.placeholder(title, "数据不可用"),
332
- dataAvailable: false
333
- };
334
- const steps = this.extractData(data);
335
- if (steps.length === 0) return {
336
- type: "flowchart",
337
- title,
338
- svgContent: this.placeholder(title, "无数据"),
339
- dataAvailable: false
340
- };
341
- const nodeWidth = 120;
342
- const nodeHeight = 40;
343
- const gap = 60;
344
- const startX = 20;
345
- const startY = 80;
346
- let svg = `<svg width="${startX + steps.length * 180}" height="160" xmlns="http://www.w3.org/2000/svg">`;
347
- for (let i = 0; i < steps.length; i++) {
348
- const x = startX + i * 180;
349
- svg += `<rect x="${x}" y="${startY}" width="${nodeWidth}" height="${nodeHeight}" rx="5" fill="${primary}" fill-opacity="0.2" stroke="${primary}" stroke-width="2" />`;
350
- svg += `<text x="${x + nodeWidth / 2}" y="105" text-anchor="middle" fill="${config.colorScheme.text}" font-size="12">${steps[i]}</text>`;
351
- if (i < steps.length - 1) {
352
- const arrowX = x + nodeWidth;
353
- svg += `<line x1="${arrowX}" y1="100" x2="${arrowX + gap}" y2="100" stroke="${primary}" stroke-width="2" />`;
354
- svg += `<polygon points="${arrowX + gap},100 ${arrowX + gap - 5},95 ${arrowX + gap - 5},105" fill="${primary}" />`;
355
- }
356
- }
357
- svg += "</svg>";
358
- return {
359
- type: "flowchart",
360
- title,
361
- svgContent: svg,
362
- dataAvailable: true
363
- };
364
- }
365
- extractData(data) {
366
- if (typeof data === "object" && data !== null) {
367
- const obj = data;
368
- if ("steps" in obj && Array.isArray(obj.steps)) return obj.steps.map((step) => String(step.stepName ?? ""));
369
- }
370
- return [];
371
- }
372
- placeholder(title, message) {
373
- return `<svg width="300" height="100" xmlns="http://www.w3.org/2000/svg"><text x="150" y="50" text-anchor="middle" fill="#999" font-size="14">${title}: ${message}</text></svg>`;
374
- }
375
- };
376
- //#endregion
377
- //#region lib/types/interactionScript.js
378
- var InteractionScript = class {
379
- generate(interactions) {
380
- const parts = [];
381
- if (interactions.expandCollapse) parts.push(`
382
- function toggleSection(id) {
383
- const el = document.getElementById(id);
384
- if (el) { el.style.display = el.style.display === 'none' ? 'block' : 'none'; }
385
- }`);
386
- if (interactions.chartSwitch) parts.push(`
387
- function switchChart(selectId, chartContainerId) {
388
- const select = document.getElementById(selectId);
389
- const container = document.getElementById(chartContainerId);
390
- if (select && container) {
391
- const charts = container.querySelectorAll('.chart-item');
392
- charts.forEach(c => c.style.display = 'none');
393
- const selected = container.querySelector('#' + select.value);
394
- if (selected) { selected.style.display = 'block'; }
395
- }
396
- }`);
397
- if (interactions.tooltip) parts.push(`
398
- document.addEventListener('DOMContentLoaded', function() {
399
- const elements = document.querySelectorAll('[data-tooltip]');
400
- elements.forEach(el => {
401
- el.addEventListener('mouseenter', function(e) {
402
- const tooltip = document.createElement('div');
403
- tooltip.textContent = e.target.getAttribute('data-tooltip');
404
- tooltip.style.cssText = 'position:absolute;background:#333;color:#fff;padding:5px;border-radius:3px;font-size:12px;z-index:1000;';
405
- tooltip.id = 'tooltip';
406
- document.body.appendChild(tooltip);
407
- });
408
- el.addEventListener('mouseleave', function() {
409
- const tooltip = document.getElementById('tooltip');
410
- if (tooltip) { tooltip.remove(); }
411
- });
412
- });
413
- });`);
414
- return parts.join("\n");
415
- }
416
- };
417
- //#endregion
418
- //#region lib/types/htmlAssembler.js
419
- var HtmlAssembler = class {
420
- interactionScript = new InteractionScript();
421
- assemble(input) {
422
- const { charts, config, reportData } = input;
423
- const colors = config.colorScheme;
424
- const columns = config.layout.columns;
425
- const chartSections = charts.map((chart, _i) => `
426
- <div class="chart-item" id="chart-${chart.type}" style="display:block;">
427
- <h3>${chart.title}</h3>
428
- ${chart.svgContent}
429
- ${chart.errorMessage ? `<p style="color:${colors.danger};">${chart.errorMessage}</p>` : ""}
430
- </div>`).join("\n");
431
- const script = this.interactionScript.generate(config.interactions);
432
- const dataJson = JSON.stringify(reportData);
433
- return `<!DOCTYPE html>
434
- <html lang="zh-CN">
435
- <head>
436
- <meta charset="UTF-8">
437
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
438
- <title>数据资产可视化报表</title>
439
- <style>
440
- * { margin: 0; padding: 0; box-sizing: border-box; }
441
- body { font-family: 'Microsoft YaHei', sans-serif; background: ${colors.background}; color: ${colors.text}; padding: 20px; }
442
- h1 { text-align: center; margin-bottom: 20px; color: ${colors.primary}; }
443
- .chart-grid { display: grid; grid-template-columns: repeat(${columns}, 1fr); gap: 20px; margin-bottom: 20px; }
444
- .chart-item { background: white; border-radius: 8px; padding: 15px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
445
- .chart-item h3 { margin-bottom: 10px; color: ${colors.primary}; }
446
- .footer { text-align: center; margin-top: 20px; color: #999; font-size: 12px; }
447
- </style>
448
- </head>
449
- <body>
450
- <h1>数据资产可视化报表</h1>
451
- <div class="chart-grid">
452
- ${chartSections}
453
- </div>
454
- <div class="footer">生成时间: ${(/* @__PURE__ */ new Date()).toISOString()} | 模板版本: ${config.templateVersion}</div>
455
- <script>
456
- const reportData = ${dataJson};
457
- ${script}
458
- <\/script>
459
- </body>
460
- </html>`;
461
- }
462
- };
463
- //#endregion
464
- //#region lib/types/templateLoader.js
465
- var TemplateLoader = class {
466
- load(config) {
467
- if (config) return config;
468
- return defaultVisualizationConfig;
469
- }
470
- };
471
- //#endregion
472
- //#region lib/types/index.js
473
- const name = "data-visualization";
474
- const inject = ["tools"];
475
- function apply(ctx) {
476
- const loader = new BusinessRulesLoader();
477
- const pathValidator = new PathValidator();
478
- const auditLogger = new AuditLogger();
479
- const dataLoader = new ReportDataLoader();
480
- const barRenderer = new BarChartRenderer();
481
- const comparisonRenderer = new ComparisonChartRenderer();
482
- const pieRenderer = new PieChartRenderer();
483
- const radarRenderer = new RadarChartRenderer();
484
- const flowchartRenderer = new FlowchartRenderer();
485
- const htmlAssembler = new HtmlAssembler();
486
- const templateLoader = new TemplateLoader();
487
- ctx.tools.register(defineTool({
488
- name: "generate_visualization",
489
- description: "生成可视化HTML报表,含脱敏统计/清洗对比/资产分布/质量评分/血缘追踪图表",
490
- parameters: {
491
- reportPaths: {
492
- type: "string",
493
- required: true,
494
- description: "各报告JSON文件路径(JSON格式: {masking, cleaning, inventory, quality, lineage})"
495
- },
496
- outputPath: {
497
- type: "string",
498
- required: true,
499
- description: "HTML报表输出路径"
500
- }
501
- },
502
- output: {
503
- schema: { type: "string" },
504
- render: (_args, value) => [{
505
- type: "text",
506
- text: value
507
- }]
508
- },
509
- async execute(args) {
510
- const { config, status: configStatus } = loader.load();
511
- const workingDir = process.cwd();
512
- const params = args;
513
- const outputPath = params.outputPath;
514
- try {
515
- pathValidator.validate(outputPath, workingDir);
516
- } catch {
517
- return `错误:路径不合法 - ${outputPath}`;
518
- }
519
- const vizConfig = templateLoader.load(config.visualization ?? defaultVisualizationConfig);
520
- let reportPathsObj;
521
- try {
522
- reportPathsObj = JSON.parse(params.reportPaths);
523
- } catch {
524
- reportPathsObj = {};
525
- }
526
- const reportPaths = reportPathsObj;
527
- const maskingData = reportPaths.masking ? await dataLoader.load(reportPaths.masking) : { data: null };
528
- const cleaningData = reportPaths.cleaning ? await dataLoader.load(reportPaths.cleaning) : { data: null };
529
- const inventoryData = reportPaths.inventory ? await dataLoader.load(reportPaths.inventory) : { data: null };
530
- const qualityData = reportPaths.quality ? await dataLoader.load(reportPaths.quality) : { data: null };
531
- const lineageData = reportPaths.lineage ? await dataLoader.load(reportPaths.lineage) : { data: null };
532
- const charts = [];
533
- if (vizConfig.charts.bar?.enabled) charts.push(barRenderer.render(maskingData.data, vizConfig));
534
- if (vizConfig.charts.comparison?.enabled) charts.push(comparisonRenderer.render(cleaningData.data, vizConfig));
535
- if (vizConfig.charts.pie?.enabled) charts.push(pieRenderer.render(inventoryData.data, vizConfig));
536
- if (vizConfig.charts.radar?.enabled) charts.push(radarRenderer.render(qualityData.data, vizConfig));
537
- if (vizConfig.charts.flowchart?.enabled) charts.push(flowchartRenderer.render(lineageData.data, vizConfig));
538
- const html = htmlAssembler.assemble({
539
- charts,
540
- config: vizConfig,
541
- reportData: {
542
- masking: maskingData.data,
543
- cleaning: cleaningData.data,
544
- inventory: inventoryData.data,
545
- quality: qualityData.data,
546
- lineage: lineageData.data
547
- }
548
- });
549
- const htmlPath = path.resolve(outputPath);
550
- const dir = path.dirname(htmlPath);
551
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
552
- fs.writeFileSync(htmlPath, html, "utf-8");
553
- auditLogger.log({
554
- pluginName: "data-visualization",
555
- operation: "generate_visualization",
556
- inputPath: JSON.stringify(reportPaths),
557
- outputPath: htmlPath,
558
- result: "SUCCESS"
559
- });
560
- const result = {
561
- htmlPath,
562
- chartCount: charts.length,
563
- chartsRendered: charts.filter((c) => c.dataAvailable).map((c) => c.type),
564
- status: "SUCCESS",
565
- configStatus
566
- };
567
- return JSON.stringify(result, null, 2);
568
- }
569
- }));
570
- console.log("[data-visualization] 可视化报表插件已加载");
571
- }
572
- //#endregion
573
- export { apply, inject, name };
package/lib/invariant.js DELETED
@@ -1,5 +0,0 @@
1
- //#region lib/types/invariant.js
2
- const invariant = "data-visualization";
3
- function install() {}
4
- //#endregion
5
- export { install, invariant };
@@ -1,8 +0,0 @@
1
- import type { ChartRenderResult } from '../types.js';
2
- import type { VisualizationConfig } from '@liuhange/dsh-data-asset-shared';
3
- export declare class BarChartRenderer {
4
- render(data: unknown, config: VisualizationConfig): ChartRenderResult;
5
- private extractCounts;
6
- private placeholder;
7
- }
8
- //# sourceMappingURL=barChartRenderer.d.ts.map
@@ -1,8 +0,0 @@
1
- import type { ChartRenderResult } from '../types.js';
2
- import type { VisualizationConfig } from '@liuhange/dsh-data-asset-shared';
3
- export declare class ComparisonChartRenderer {
4
- render(data: unknown, config: VisualizationConfig): ChartRenderResult;
5
- private extractData;
6
- private placeholder;
7
- }
8
- //# sourceMappingURL=comparisonChartRenderer.d.ts.map
@@ -1,8 +0,0 @@
1
- import type { ChartRenderResult } from '../types.js';
2
- import type { VisualizationConfig } from '@liuhange/dsh-data-asset-shared';
3
- export declare class FlowchartRenderer {
4
- render(data: unknown, config: VisualizationConfig): ChartRenderResult;
5
- private extractData;
6
- private placeholder;
7
- }
8
- //# sourceMappingURL=flowchartRenderer.d.ts.map
@@ -1,8 +0,0 @@
1
- import type { ChartRenderResult } from '../types.js';
2
- import type { VisualizationConfig } from '@liuhange/dsh-data-asset-shared';
3
- export declare class PieChartRenderer {
4
- render(data: unknown, config: VisualizationConfig): ChartRenderResult;
5
- private extractData;
6
- private placeholder;
7
- }
8
- //# sourceMappingURL=pieChartRenderer.d.ts.map
@@ -1,8 +0,0 @@
1
- import type { ChartRenderResult } from '../types.js';
2
- import type { VisualizationConfig } from '@liuhange/dsh-data-asset-shared';
3
- export declare class RadarChartRenderer {
4
- render(data: unknown, config: VisualizationConfig): ChartRenderResult;
5
- private extractData;
6
- private placeholder;
7
- }
8
- //# sourceMappingURL=radarChartRenderer.d.ts.map
@@ -1,3 +0,0 @@
1
- import type { VisualizationConfig } from '@liuhange/dsh-data-asset-shared';
2
- export declare const defaultVisualizationConfig: VisualizationConfig;
3
- //# sourceMappingURL=defaultVisualizationConfig.d.ts.map
@@ -1,12 +0,0 @@
1
- import type { VisualizationConfig } from '@liuhange/dsh-data-asset-shared';
2
- import type { ChartRenderResult } from './types.js';
3
- export interface HtmlAssembleInput {
4
- charts: ChartRenderResult[];
5
- config: VisualizationConfig;
6
- reportData: Record<string, unknown>;
7
- }
8
- export declare class HtmlAssembler {
9
- private interactionScript;
10
- assemble(input: HtmlAssembleInput): string;
11
- }
12
- //# sourceMappingURL=htmlAssembler.d.ts.map
@@ -1,5 +0,0 @@
1
- import type { Context } from '@deepseek-ai/cordis';
2
- export declare const name = "data-visualization";
3
- export declare const inject: string[];
4
- export declare function apply(ctx: Context): void;
5
- //# sourceMappingURL=index.d.ts.map
@@ -1,5 +0,0 @@
1
- import type { VisualizationConfig } from '@liuhange/dsh-data-asset-shared';
2
- export declare class InteractionScript {
3
- generate(interactions: VisualizationConfig['interactions']): string;
4
- }
5
- //# sourceMappingURL=interactionScript.d.ts.map
@@ -1,3 +0,0 @@
1
- export declare const invariant = "data-visualization";
2
- export declare function install(): void;
3
- //# sourceMappingURL=invariant.d.ts.map
@@ -1,5 +0,0 @@
1
- import type { ReportDataLoadResult } from './types.js';
2
- export declare class ReportDataLoader {
3
- load(filePath: string): Promise<ReportDataLoadResult>;
4
- }
5
- //# sourceMappingURL=reportDataLoader.d.ts.map
@@ -1,5 +0,0 @@
1
- import type { VisualizationConfig } from '@liuhange/dsh-data-asset-shared';
2
- export declare class TemplateLoader {
3
- load(config: VisualizationConfig | undefined): VisualizationConfig;
4
- }
5
- //# sourceMappingURL=templateLoader.d.ts.map
@@ -1,30 +0,0 @@
1
- export type ChartType = 'bar' | 'comparison' | 'pie' | 'radar' | 'flowchart';
2
- export interface GenerateVisualizationParams {
3
- reportPaths: {
4
- masking?: string;
5
- cleaning?: string;
6
- inventory?: string;
7
- quality?: string;
8
- lineage?: string;
9
- };
10
- outputPath: string;
11
- }
12
- export interface ChartRenderResult {
13
- type: ChartType;
14
- title: string;
15
- svgContent: string;
16
- dataAvailable: boolean;
17
- errorMessage?: string;
18
- }
19
- export interface GenerateVisualizationResult {
20
- htmlPath: string;
21
- chartCount: number;
22
- chartsRendered: string[];
23
- status: 'SUCCESS' | 'FAILED';
24
- configStatus: string;
25
- }
26
- export interface ReportDataLoadResult {
27
- data: unknown;
28
- error?: string;
29
- }
30
- //# sourceMappingURL=types.d.ts.map