@agions/taroviz 1.11.1 → 2.0.3
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/CHANGELOG.md +245 -0
- package/README.md +104 -302
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/vendors.js +1 -0
- package/dist/cjs/vendors~echarts.js +1 -0
- package/dist/esm/index.js +1 -58151
- package/dist/esm/vendors.js +1 -0
- package/dist/esm/vendors~echarts.js +1 -0
- package/package.json +19 -25
- package/src/adapters/MiniAppAdapter.ts +136 -0
- package/src/adapters/__tests__/index.test.ts +1 -1
- package/src/adapters/h5/__tests__/index.test.ts +4 -2
- package/src/adapters/h5/index.ts +63 -64
- package/src/adapters/harmony/index.ts +23 -245
- package/src/adapters/index.ts +49 -45
- package/src/adapters/swan/index.ts +6 -69
- package/src/adapters/tt/index.ts +7 -70
- package/src/adapters/types.ts +25 -58
- package/src/adapters/weapp/index.ts +6 -69
- package/src/charts/__tests__/testUtils.tsx +87 -0
- package/src/charts/boxplot/__tests__/index.test.tsx +49 -103
- package/src/charts/boxplot/index.tsx +2 -1
- package/src/charts/boxplot/types.ts +17 -16
- package/src/charts/common/BaseChartWrapper.tsx +90 -82
- package/src/charts/common/__mocks__/BaseChartWrapper.tsx +17 -0
- package/src/charts/createChartComponent.tsx +36 -0
- package/src/charts/createOptionChartComponent.tsx +32 -0
- package/src/charts/funnel/__tests__/index.test.tsx +99 -0
- package/src/charts/funnel/index.tsx +60 -10
- package/src/charts/funnel/types.ts +6 -0
- package/src/charts/graph/__tests__/index.test.tsx +102 -33
- package/src/charts/graph/index.tsx +66 -9
- package/src/charts/graph/types.ts +6 -0
- package/src/charts/heatmap/__tests__/index.test.tsx +139 -0
- package/src/charts/heatmap/index.tsx +103 -10
- package/src/charts/heatmap/types.ts +6 -0
- package/src/charts/index.ts +74 -26
- package/src/charts/liquid/__tests__/index.test.tsx +52 -0
- package/src/charts/liquid/index.tsx +239 -182
- package/src/charts/liquid/types.ts +11 -11
- package/src/charts/parallel/__tests__/index.test.tsx +40 -67
- package/src/charts/parallel/index.tsx +2 -1
- package/src/charts/parallel/types.ts +19 -18
- package/src/charts/radar/__tests__/index.test.tsx +210 -0
- package/src/charts/radar/index.tsx +143 -10
- package/src/charts/radar/types.ts +13 -0
- package/src/charts/sankey/__tests__/index.test.tsx +124 -0
- package/src/charts/sankey/index.tsx +62 -10
- package/src/charts/sankey/types.ts +6 -0
- package/src/charts/tree/__tests__/index.test.tsx +71 -0
- package/src/charts/tree/index.tsx +5 -2
- package/src/charts/tree/types.ts +9 -9
- package/src/charts/types.ts +208 -106
- package/src/charts/utils.ts +9 -7
- package/src/charts/wordcloud/__tests__/index.test.tsx +98 -31
- package/src/charts/wordcloud/index.tsx +75 -9
- package/src/charts/wordcloud/types.ts +6 -0
- package/src/components/DataFilter/index.tsx +32 -10
- package/src/core/animation/types.ts +6 -6
- package/src/core/components/Annotation.tsx +6 -7
- package/src/core/components/BaseChart.tsx +110 -168
- package/src/core/components/ErrorBoundary.tsx +17 -4
- package/src/core/components/LazyChart.tsx +54 -55
- package/src/core/components/hooks/index.ts +6 -2
- package/src/core/components/hooks/useChartInit.ts +6 -3
- package/src/core/components/hooks/usePerformance.ts +8 -2
- package/src/core/components/hooks/useVirtualScroll.ts +2 -1
- package/src/core/index.ts +1 -1
- package/src/core/themes/ThemeManager.ts +1 -1
- package/src/core/types/common.ts +2 -1
- package/src/core/types/index.ts +0 -12
- package/src/core/types/platform.ts +3 -5
- package/src/core/utils/__tests__/deepClone.test.ts +317 -0
- package/src/core/utils/__tests__/index.test.ts +2 -1
- package/src/core/utils/chartInstances.ts +13 -0
- package/src/core/utils/common.ts +20 -29
- package/src/core/utils/deepClone.ts +114 -0
- package/src/core/utils/download.ts +128 -0
- package/src/core/utils/drillDown.ts +34 -353
- package/src/core/utils/drillDownHelpers.ts +426 -0
- package/src/core/utils/events.ts +12 -0
- package/src/core/utils/export/ExportUtils.ts +36 -67
- package/src/core/utils/format.ts +44 -0
- package/src/core/utils/index.ts +21 -154
- package/src/core/utils/merge.ts +25 -0
- package/src/core/utils/performance/PerformanceAnalyzer.ts +38 -21
- package/src/core/utils/performance/hooks.ts +7 -0
- package/src/core/utils/performance/index.ts +2 -0
- package/src/{hooks → core/utils/performance}/useAnimation.ts +45 -41
- package/src/core/utils/performance/useDataZoom.ts +324 -0
- package/src/{hooks → core/utils/performance}/usePerformance.ts +49 -41
- package/src/core/utils/performance/usePerformanceHooks.ts +278 -0
- package/src/core/utils/performanceUtils.ts +310 -0
- package/src/core/utils/runtime.ts +190 -0
- package/src/core/utils/setOptionUtils.ts +59 -0
- package/src/core/version.ts +14 -0
- package/src/editor/EnhancedThemeEditor.tsx +362 -540
- package/src/editor/ThemeEditor.tsx +55 -321
- package/src/editor/components/ThemeBasicSettings.tsx +113 -0
- package/src/editor/components/ThemeColorEditor.tsx +105 -0
- package/src/editor/components/ThemeSelector.tsx +70 -0
- package/src/editor/hooks/useThemeEditorState.ts +201 -0
- package/src/editor/index.ts +10 -2
- package/src/hooks/__tests__/index.test.tsx +3 -1
- package/src/hooks/chartConnectHelpers.ts +341 -0
- package/src/hooks/index.ts +55 -660
- package/src/hooks/types.ts +189 -0
- package/src/hooks/useChartAutoResize.ts +73 -0
- package/src/hooks/useChartConnect.ts +92 -238
- package/src/hooks/useChartDownload.ts +25 -27
- package/src/hooks/useChartHistory.ts +34 -49
- package/src/hooks/useChartInit.ts +59 -0
- package/src/hooks/useChartOptions.ts +259 -0
- package/src/hooks/useChartPerformance.ts +109 -0
- package/src/hooks/useChartSelection.ts +52 -49
- package/src/hooks/useChartTheme.ts +51 -0
- package/src/hooks/useDataTransform.ts +19 -4
- package/src/hooks/utils/chartDownloadUtils.ts +40 -53
- package/src/hooks/utils/dataTransformUtils.ts +22 -0
- package/src/index.ts +48 -34
- package/src/main.tsx +4 -9
- package/src/react-dom.d.ts +3 -3
- package/src/themes/index.ts +30 -855
- package/src/themes/palettes/blue-green.ts +13 -0
- package/src/themes/palettes/chalk.ts +13 -0
- package/src/themes/palettes/cyber.ts +44 -0
- package/src/themes/palettes/dark.ts +52 -0
- package/src/themes/palettes/default.ts +52 -0
- package/src/themes/palettes/elegant.ts +34 -0
- package/src/themes/palettes/forest.ts +13 -0
- package/src/themes/palettes/glass.ts +49 -0
- package/src/themes/palettes/golden.ts +13 -0
- package/src/themes/palettes/neon.ts +43 -0
- package/src/themes/palettes/ocean.ts +39 -0
- package/src/themes/palettes/pastel.ts +37 -0
- package/src/themes/palettes/purple-passion.ts +13 -0
- package/src/themes/palettes/retro.ts +33 -0
- package/src/themes/palettes/sunset.ts +40 -0
- package/src/themes/palettes/walden.ts +13 -0
- package/src/themes/registry.ts +184 -0
- package/src/themes/types.ts +213 -0
- package/src/charts/bar/__tests__/index.test.tsx +0 -113
- package/src/charts/bar/index.tsx +0 -14
- package/src/charts/candlestick/__tests__/index.test.tsx +0 -40
- package/src/charts/candlestick/index.tsx +0 -13
- package/src/charts/gauge/index.tsx +0 -14
- package/src/charts/line/__tests__/index.test.tsx +0 -107
- package/src/charts/line/index.tsx +0 -15
- package/src/charts/pie/__tests__/index.test.tsx +0 -112
- package/src/charts/pie/index.tsx +0 -14
- package/src/charts/scatter/index.tsx +0 -14
- package/src/charts/sunburst/index.tsx +0 -18
- package/src/charts/treemap/index.tsx +0 -18
- package/src/core/utils/codeGenerator/CodeGenerator.ts +0 -669
- package/src/core/utils/codeGenerator/index.ts +0 -13
- package/src/core/utils/codeGenerator/types.ts +0 -198
- package/src/core/utils/configGenerator/ConfigGenerator.ts +0 -583
- package/src/core/utils/configGenerator/index.ts +0 -13
- package/src/core/utils/configGenerator/types.ts +0 -445
- package/src/core/utils/debug/DebugPanel.tsx +0 -637
- package/src/core/utils/debug/debugger.ts +0 -322
- package/src/core/utils/debug/index.ts +0 -21
- package/src/core/utils/debug/types.ts +0 -142
- package/src/hooks/useDataZoom.ts +0 -323
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useChartPerformance - 图表性能相关 Hooks
|
|
3
|
+
* 提供数据轮询等功能
|
|
4
|
+
*/
|
|
5
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 使用数据轮询
|
|
9
|
+
* @param fetchFn 数据获取函数
|
|
10
|
+
* @param options 配置选项
|
|
11
|
+
* @returns [数据, 加载状态, 错误, 刷新函数]
|
|
12
|
+
*/
|
|
13
|
+
export function useDataPolling<T>(
|
|
14
|
+
fetchFn: () => Promise<T>,
|
|
15
|
+
options?: {
|
|
16
|
+
/** 轮询间隔 (ms) */
|
|
17
|
+
interval?: number;
|
|
18
|
+
/** 是否自动开始 */
|
|
19
|
+
autoStart?: boolean;
|
|
20
|
+
/** 错误重试次数 */
|
|
21
|
+
retryCount?: number;
|
|
22
|
+
/** 重试延迟 (ms) */
|
|
23
|
+
retryDelay?: number;
|
|
24
|
+
}
|
|
25
|
+
) {
|
|
26
|
+
const { interval = 5000, autoStart = false, retryCount = 3, retryDelay = 1000 } = options || {};
|
|
27
|
+
|
|
28
|
+
const [data, setData] = useState<T | null>(null);
|
|
29
|
+
const [loading, setLoading] = useState(autoStart);
|
|
30
|
+
const [error, setError] = useState<Error | null>(null);
|
|
31
|
+
|
|
32
|
+
// 用于取消进行中的请求
|
|
33
|
+
const abortRef = useRef<{ cancelled: boolean }>({ cancelled: false });
|
|
34
|
+
|
|
35
|
+
const fetchData = useCallback(async () => {
|
|
36
|
+
// 取消之前的请求
|
|
37
|
+
abortRef.current.cancelled = true;
|
|
38
|
+
// 创建新的取消标记
|
|
39
|
+
abortRef.current = { cancelled: false };
|
|
40
|
+
const currentAbort = abortRef.current;
|
|
41
|
+
|
|
42
|
+
let retries = retryCount;
|
|
43
|
+
setLoading(true);
|
|
44
|
+
setError(null);
|
|
45
|
+
|
|
46
|
+
// retryCount < 0 表示不重试,直接一次请求
|
|
47
|
+
if (retryCount < 0) {
|
|
48
|
+
try {
|
|
49
|
+
const result = await fetchFn();
|
|
50
|
+
if (!currentAbort.cancelled) {
|
|
51
|
+
setData(result);
|
|
52
|
+
setLoading(false);
|
|
53
|
+
}
|
|
54
|
+
} catch (e) {
|
|
55
|
+
if (!currentAbort.cancelled) {
|
|
56
|
+
setError(e as Error);
|
|
57
|
+
setLoading(false);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// retryCount >= 0:循环重试
|
|
64
|
+
while (retries > 0 && !currentAbort.cancelled) {
|
|
65
|
+
try {
|
|
66
|
+
const result = await fetchFn();
|
|
67
|
+
if (!currentAbort.cancelled) {
|
|
68
|
+
setData(result);
|
|
69
|
+
setLoading(false);
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
} catch (e) {
|
|
73
|
+
retries--;
|
|
74
|
+
if (retries <= 0 || currentAbort.cancelled) {
|
|
75
|
+
if (!currentAbort.cancelled) {
|
|
76
|
+
setError(e as Error);
|
|
77
|
+
}
|
|
78
|
+
setLoading(false);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}, [fetchFn, retryCount, retryDelay]);
|
|
85
|
+
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (autoStart) {
|
|
88
|
+
fetchData();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (interval > 0) {
|
|
92
|
+
const timer = setInterval(fetchData, interval);
|
|
93
|
+
return () => {
|
|
94
|
+
clearInterval(timer);
|
|
95
|
+
abortRef.current.cancelled = true;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return () => {
|
|
100
|
+
abortRef.current.cancelled = true;
|
|
101
|
+
};
|
|
102
|
+
}, [interval, autoStart, fetchData]);
|
|
103
|
+
|
|
104
|
+
const refresh = useCallback(() => {
|
|
105
|
+
fetchData();
|
|
106
|
+
}, [fetchData]);
|
|
107
|
+
|
|
108
|
+
return { data, loading, error, refresh };
|
|
109
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
1
2
|
/**
|
|
2
3
|
* useChartSelection - 图表数据点选择/高亮 Hook
|
|
3
4
|
* 支持单个/批量选择、反选、清除选择,配合 ECharts select 事件
|
|
@@ -10,7 +11,7 @@
|
|
|
10
11
|
* - 自动绑定图表 select/unselect 事件
|
|
11
12
|
*/
|
|
12
13
|
import { useEffect, useRef, useCallback, useState } from 'react';
|
|
13
|
-
import type { ChartInstance } from './
|
|
14
|
+
import type { ChartInstance } from './types';
|
|
14
15
|
|
|
15
16
|
// ============================================================================
|
|
16
17
|
// 类型定义
|
|
@@ -44,7 +45,7 @@ export interface UseChartSelectionOptions {
|
|
|
44
45
|
/** 是否启用 Shift+Click 范围选择,默认 true */
|
|
45
46
|
enableShiftRangeSelect?: boolean;
|
|
46
47
|
/** 选择变化时的回调 */
|
|
47
|
-
onSelectionChange?: (
|
|
48
|
+
onSelectionChange?: (_event: SelectionEvent) => void;
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
/** 选择返回值 */
|
|
@@ -118,9 +119,6 @@ export function useChartSelection(
|
|
|
118
119
|
const chartRef = useRef<ChartInstance | null>(null);
|
|
119
120
|
chartRef.current = chartInstance;
|
|
120
121
|
|
|
121
|
-
// 上一次 shift+click 的数据索引(用于范围选择)
|
|
122
|
-
const lastShiftIndexRef = useRef<number | null>(null);
|
|
123
|
-
|
|
124
122
|
// 当前模式 ref(用于事件处理)
|
|
125
123
|
const modeRef = useRef(mode);
|
|
126
124
|
modeRef.current = mode;
|
|
@@ -130,16 +128,6 @@ export function useChartSelection(
|
|
|
130
128
|
const chart = chartRef.current;
|
|
131
129
|
if (!chart || !chart.on) return;
|
|
132
130
|
|
|
133
|
-
const handleSelect = (params: { selected: Record<string, boolean>; type: string }) => {
|
|
134
|
-
// ECharts 内置 select 会同步更新 legend
|
|
135
|
-
// 这里我们用 dispatchAction 来实现纯数据点选择
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
// 单击 legend 时清除数据点选择(保持一致性)
|
|
139
|
-
const handleLegendSelectChanged = (params: { name?: string; selected?: Record<string, boolean> }) => {
|
|
140
|
-
// 清除选择时的视觉反馈
|
|
141
|
-
};
|
|
142
|
-
|
|
143
131
|
chart.on('selectchanged', (params: unknown) => {
|
|
144
132
|
// 当图表内部选择变化时同步状态
|
|
145
133
|
const p = params as {
|
|
@@ -185,18 +173,15 @@ export function useChartSelection(
|
|
|
185
173
|
);
|
|
186
174
|
|
|
187
175
|
/** 执行 ECharts dispatchAction */
|
|
188
|
-
const dispatchSelect = useCallback(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
},
|
|
198
|
-
[]
|
|
199
|
-
);
|
|
176
|
+
const dispatchSelect = useCallback((key: DataPointKey, select: boolean) => {
|
|
177
|
+
const chart = chartRef.current;
|
|
178
|
+
if (!chart?.dispatchAction) return;
|
|
179
|
+
chart.dispatchAction({
|
|
180
|
+
type: select ? 'select' : 'unselect',
|
|
181
|
+
seriesIndex: key.seriesIndex,
|
|
182
|
+
dataIndex: key.dataIndex,
|
|
183
|
+
});
|
|
184
|
+
}, []);
|
|
200
185
|
|
|
201
186
|
// ─── Public API ───────────────────────────────────────────────────────────
|
|
202
187
|
|
|
@@ -230,21 +215,34 @@ export function useChartSelection(
|
|
|
230
215
|
const toggle = useCallback(
|
|
231
216
|
(key: DataPointKey) => {
|
|
232
217
|
const str = keyToString(key);
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
218
|
+
let isCurrentlySelected = false;
|
|
219
|
+
setSelectedPoints((prev) => {
|
|
220
|
+
isCurrentlySelected = prev.some((p) => keyToString(p) === str);
|
|
221
|
+
if (isCurrentlySelected) {
|
|
222
|
+
dispatchSelect(key, false);
|
|
223
|
+
notifyChange([], [key]);
|
|
224
|
+
return prev.filter((p) => keyToString(p) !== str);
|
|
225
|
+
} else {
|
|
226
|
+
dispatchSelect(key, true);
|
|
227
|
+
const next = mode === 'single' ? [key] : [...prev, key];
|
|
228
|
+
notifyChange(next, []);
|
|
229
|
+
return next;
|
|
230
|
+
}
|
|
231
|
+
});
|
|
238
232
|
},
|
|
239
|
-
[
|
|
233
|
+
[mode, notifyChange, dispatchSelect]
|
|
240
234
|
);
|
|
241
235
|
|
|
242
236
|
const selectMultiple = useCallback(
|
|
243
237
|
(keys: DataPointKey[]) => {
|
|
244
238
|
setSelectedPoints((prev) => {
|
|
245
|
-
const newPoints =
|
|
246
|
-
|
|
247
|
-
|
|
239
|
+
const newPoints =
|
|
240
|
+
mode === 'single'
|
|
241
|
+
? keys
|
|
242
|
+
: [
|
|
243
|
+
...prev,
|
|
244
|
+
...keys.filter((k) => !prev.some((p) => keyToString(p) === keyToString(k))),
|
|
245
|
+
];
|
|
248
246
|
notifyChange(newPoints, []);
|
|
249
247
|
return newPoints;
|
|
250
248
|
});
|
|
@@ -270,7 +268,9 @@ export function useChartSelection(
|
|
|
270
268
|
const invertSelection = useCallback(
|
|
271
269
|
(seriesIndex: number, dataIndices: number[]) => {
|
|
272
270
|
setSelectedPoints((prev) => {
|
|
273
|
-
const selectedSet = new Set(
|
|
271
|
+
const selectedSet = new Set(
|
|
272
|
+
prev.filter((p) => p.seriesIndex === seriesIndex).map((p) => p.dataIndex)
|
|
273
|
+
);
|
|
274
274
|
const toSelect: DataPointKey[] = [];
|
|
275
275
|
const toDeselect: DataPointKey[] = [];
|
|
276
276
|
|
|
@@ -287,9 +287,9 @@ export function useChartSelection(
|
|
|
287
287
|
toDeselect.forEach((k) => dispatchSelect(k, false));
|
|
288
288
|
toSelect.forEach((k) => dispatchSelect(k, true));
|
|
289
289
|
|
|
290
|
-
const newSelected = prev
|
|
291
|
-
(p) => !(p.seriesIndex === seriesIndex && selectedSet.has(p.dataIndex))
|
|
292
|
-
|
|
290
|
+
const newSelected = prev
|
|
291
|
+
.filter((p) => !(p.seriesIndex === seriesIndex && selectedSet.has(p.dataIndex)))
|
|
292
|
+
.concat(toSelect);
|
|
293
293
|
|
|
294
294
|
notifyChange(toSelect, toDeselect);
|
|
295
295
|
return newSelected;
|
|
@@ -302,9 +302,13 @@ export function useChartSelection(
|
|
|
302
302
|
(seriesIndex: number, dataIndices: number[]) => {
|
|
303
303
|
const keys = dataIndices.map((dataIndex) => ({ seriesIndex, dataIndex }));
|
|
304
304
|
setSelectedPoints((prev) => {
|
|
305
|
-
const newPoints =
|
|
306
|
-
|
|
307
|
-
|
|
305
|
+
const newPoints =
|
|
306
|
+
mode === 'single'
|
|
307
|
+
? keys
|
|
308
|
+
: [
|
|
309
|
+
...prev,
|
|
310
|
+
...keys.filter((k) => !prev.some((p) => keyToString(p) === keyToString(k))),
|
|
311
|
+
];
|
|
308
312
|
notifyChange(newPoints, []);
|
|
309
313
|
return newPoints;
|
|
310
314
|
});
|
|
@@ -318,10 +322,11 @@ export function useChartSelection(
|
|
|
318
322
|
if (chart?.dispatchAction) {
|
|
319
323
|
chart.dispatchAction({ type: 'unselect' });
|
|
320
324
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
+
setSelectedPoints((prev) => {
|
|
326
|
+
notifyChange([], prev);
|
|
327
|
+
return [];
|
|
328
|
+
});
|
|
329
|
+
}, [notifyChange]);
|
|
325
330
|
|
|
326
331
|
const isSelected = useCallback(
|
|
327
332
|
(key: DataPointKey) => {
|
|
@@ -346,5 +351,3 @@ export function useChartSelection(
|
|
|
346
351
|
isSelected,
|
|
347
352
|
};
|
|
348
353
|
}
|
|
349
|
-
|
|
350
|
-
export default useChartSelection;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useChartTheme - 图表主题相关 Hooks
|
|
3
|
+
* 提供主题解析和主题切换功能
|
|
4
|
+
*/
|
|
5
|
+
import { useState, useMemo, useCallback } from 'react';
|
|
6
|
+
import { getThemeByName } from '../themes';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 使用图表主题
|
|
10
|
+
* @param theme 主题名称或配置
|
|
11
|
+
* @param darkMode 是否为暗色模式
|
|
12
|
+
* @returns 处理后的主题
|
|
13
|
+
*/
|
|
14
|
+
export function useChartTheme(theme: string | Record<string, unknown>, darkMode = false) {
|
|
15
|
+
return useMemo(() => {
|
|
16
|
+
if (typeof theme === 'string') {
|
|
17
|
+
// 如果是字符串,尝试获取内置主题配置
|
|
18
|
+
try {
|
|
19
|
+
const builtinTheme = getThemeByName(theme);
|
|
20
|
+
return builtinTheme || (darkMode ? 'dark' : theme);
|
|
21
|
+
} catch {
|
|
22
|
+
return darkMode ? 'dark' : theme;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return theme;
|
|
26
|
+
}, [theme, darkMode]);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 使用主题切换
|
|
31
|
+
* @param initialTheme 初始主题
|
|
32
|
+
* @returns [当前主题, 切换主题函数]
|
|
33
|
+
*/
|
|
34
|
+
export function useThemeSwitcher(initialTheme = 'default') {
|
|
35
|
+
const [theme, setTheme] = useState<string | Record<string, unknown>>(initialTheme);
|
|
36
|
+
const [isDark, setIsDark] = useState(false);
|
|
37
|
+
|
|
38
|
+
const switchTheme = useCallback((newTheme: string | Record<string, unknown>) => {
|
|
39
|
+
setTheme(newTheme);
|
|
40
|
+
if (typeof newTheme === 'string') {
|
|
41
|
+
setIsDark(newTheme === 'dark' || newTheme.includes('dark'));
|
|
42
|
+
}
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
45
|
+
const toggleDark = useCallback(() => {
|
|
46
|
+
setIsDark((prev) => !prev);
|
|
47
|
+
setTheme((prev) => (prev === 'dark' ? 'default' : 'dark'));
|
|
48
|
+
}, []);
|
|
49
|
+
|
|
50
|
+
return { theme, isDark, switchTheme, toggleDark, setTheme };
|
|
51
|
+
}
|
|
@@ -63,7 +63,7 @@ export interface TransformOptions {
|
|
|
63
63
|
order: 'asc' | 'desc';
|
|
64
64
|
};
|
|
65
65
|
/** 过滤配置 */
|
|
66
|
-
filter?: (
|
|
66
|
+
filter?: (_item: Record<string, unknown>) => boolean;
|
|
67
67
|
/** 转换后的额外配置 */
|
|
68
68
|
extraConfig?: Partial<EChartsOption>;
|
|
69
69
|
}
|
|
@@ -109,6 +109,21 @@ export interface TimeSeriesTransformOptions {
|
|
|
109
109
|
// 数据转换 Hook
|
|
110
110
|
// ============================================================================
|
|
111
111
|
|
|
112
|
+
/**
|
|
113
|
+
* 使用图表数据变更
|
|
114
|
+
* @param data 数据源
|
|
115
|
+
* @param transformer 数据转换函数
|
|
116
|
+
* @returns 转换后的图表选项
|
|
117
|
+
*/
|
|
118
|
+
export function useChartData<T = unknown>(data: T | null, transformer: (data: T) => EChartsOption) {
|
|
119
|
+
return useMemo(() => {
|
|
120
|
+
if (!data || (Array.isArray(data) && data.length === 0)) {
|
|
121
|
+
return {};
|
|
122
|
+
}
|
|
123
|
+
return transformer(data);
|
|
124
|
+
}, [data, transformer]);
|
|
125
|
+
}
|
|
126
|
+
|
|
112
127
|
/**
|
|
113
128
|
* 使用数据转换
|
|
114
129
|
* @param options 转换选项
|
|
@@ -174,7 +189,7 @@ export function useTableTransform(options: TableTransformOptions): EChartsOption
|
|
|
174
189
|
name: colConfig?.label || key,
|
|
175
190
|
type: 'bar' as const,
|
|
176
191
|
data: values,
|
|
177
|
-
|
|
192
|
+
_itemStyle: colConfig?.color ? { color: colConfig.color } : undefined,
|
|
178
193
|
};
|
|
179
194
|
});
|
|
180
195
|
|
|
@@ -217,8 +232,8 @@ export function useTimeSeriesTransform(options: TimeSeriesTransformOptions): ECh
|
|
|
217
232
|
const groups = new Set(data.map((d) => String(d[groupField])));
|
|
218
233
|
const series = Array.from(groups).map((group) => {
|
|
219
234
|
const groupValues = categories.map((date) => {
|
|
220
|
-
const
|
|
221
|
-
return aggregateValues(
|
|
235
|
+
const _items = groupedData[date]?.filter((d) => String(d[groupField]) === group) || [];
|
|
236
|
+
return aggregateValues(_items, valueField, aggregation, fillMissing);
|
|
222
237
|
});
|
|
223
238
|
return { name: group, type: 'line', data: groupValues, smooth: true };
|
|
224
239
|
});
|
|
@@ -1,55 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Chart Download Utilities
|
|
3
3
|
* 图表下载工具函数
|
|
4
|
+
* 使用公共下载工具模块
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* 下载 Blob 对象
|
|
16
|
-
*/
|
|
17
|
-
export function downloadBlob(blob: Blob, filename: string): void {
|
|
18
|
-
const url = URL.createObjectURL(blob);
|
|
19
|
-
const link = document.createElement('a');
|
|
20
|
-
link.href = url;
|
|
21
|
-
link.download = filename;
|
|
22
|
-
document.body.appendChild(link);
|
|
23
|
-
link.click();
|
|
24
|
-
document.body.removeChild(link);
|
|
25
|
-
URL.revokeObjectURL(url);
|
|
26
|
-
}
|
|
7
|
+
import {
|
|
8
|
+
generateFilename,
|
|
9
|
+
downloadBlob,
|
|
10
|
+
downloadDataUrl,
|
|
11
|
+
csvToBlob,
|
|
12
|
+
jsonToBlob,
|
|
13
|
+
} from '../../core/utils/download';
|
|
27
14
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
*/
|
|
31
|
-
export function downloadDataUrl(dataUrl: string, filename: string): void {
|
|
32
|
-
const link = document.createElement('a');
|
|
33
|
-
link.href = dataUrl;
|
|
34
|
-
link.download = filename;
|
|
35
|
-
document.body.appendChild(link);
|
|
36
|
-
link.click();
|
|
37
|
-
document.body.removeChild(link);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* CSV 转 Blob
|
|
42
|
-
*/
|
|
43
|
-
export function csvToBlob(csv: string, _filename: string): Blob {
|
|
44
|
-
return new Blob([csv], { type: 'text/csv;charset=utf-8;' });
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* JSON 转 Blob
|
|
49
|
-
*/
|
|
50
|
-
export function jsonToBlob(json: string, _filename: string): Blob {
|
|
51
|
-
return new Blob([json], { type: 'application/json;charset=utf-8;' });
|
|
52
|
-
}
|
|
15
|
+
// 重新导出公共函数
|
|
16
|
+
export { generateFilename, downloadBlob, downloadDataUrl, csvToBlob, jsonToBlob };
|
|
53
17
|
|
|
54
18
|
/**
|
|
55
19
|
* 将数据转换为 CSV 格式
|
|
@@ -59,7 +23,14 @@ export function convertToCSV(data: unknown, options?: { includeLabels?: boolean
|
|
|
59
23
|
|
|
60
24
|
// 处理 ECharts 格式的数据
|
|
61
25
|
if (typeof data === 'object' && (data as { series?: unknown }).series) {
|
|
62
|
-
return convertSeriesToCSV(
|
|
26
|
+
return convertSeriesToCSV(
|
|
27
|
+
data as {
|
|
28
|
+
series?: unknown[];
|
|
29
|
+
xAxis?: { data?: unknown[] };
|
|
30
|
+
dataset?: { dimensions?: string[]; source?: unknown[][] };
|
|
31
|
+
},
|
|
32
|
+
options
|
|
33
|
+
);
|
|
63
34
|
}
|
|
64
35
|
|
|
65
36
|
// 处理数组数据
|
|
@@ -79,7 +50,11 @@ export function convertToCSV(data: unknown, options?: { includeLabels?: boolean
|
|
|
79
50
|
* 将 ECharts series 数据转换为 CSV
|
|
80
51
|
*/
|
|
81
52
|
function convertSeriesToCSV(
|
|
82
|
-
chartData: {
|
|
53
|
+
chartData: {
|
|
54
|
+
series?: unknown[];
|
|
55
|
+
xAxis?: { data?: unknown[] };
|
|
56
|
+
dataset?: { dimensions?: string[]; source?: unknown[][] };
|
|
57
|
+
},
|
|
83
58
|
options?: { includeLabels?: boolean }
|
|
84
59
|
): string {
|
|
85
60
|
const { series = [], xAxis, dataset } = chartData;
|
|
@@ -102,7 +77,14 @@ function convertSeriesToCSV(
|
|
|
102
77
|
|
|
103
78
|
// 构建 CSV 头
|
|
104
79
|
const headers = includeLabels
|
|
105
|
-
? [
|
|
80
|
+
? [
|
|
81
|
+
'Category',
|
|
82
|
+
...series.map(
|
|
83
|
+
(s: unknown) =>
|
|
84
|
+
(s as { name?: string; seriesIndex?: number }).name ||
|
|
85
|
+
(s as { seriesIndex?: number }).seriesIndex
|
|
86
|
+
),
|
|
87
|
+
]
|
|
106
88
|
: [];
|
|
107
89
|
|
|
108
90
|
// 构建 CSV 行
|
|
@@ -175,7 +157,12 @@ function convertObjectToCSV(data: Record<string, unknown>): string {
|
|
|
175
157
|
export function convertToJSON(data: unknown): string {
|
|
176
158
|
if (!data) return '{}';
|
|
177
159
|
|
|
178
|
-
const dataObj = data as {
|
|
160
|
+
const dataObj = data as {
|
|
161
|
+
series?: unknown[];
|
|
162
|
+
title?: { text?: string };
|
|
163
|
+
legend?: { data?: unknown };
|
|
164
|
+
xAxis?: { data?: unknown[] };
|
|
165
|
+
};
|
|
179
166
|
|
|
180
167
|
// 如果是 ECharts 格式,简化数据
|
|
181
168
|
if (dataObj.series) {
|
|
@@ -234,7 +221,7 @@ export async function createPdfFromImage(
|
|
|
234
221
|
// 计算图片位置和尺寸(居中)
|
|
235
222
|
const imgRatio = img.width / img.height;
|
|
236
223
|
const canvasRatio = pdfWidth / pdfHeight;
|
|
237
|
-
let drawWidth: number, drawHeight: number
|
|
224
|
+
let drawWidth: number, drawHeight: number;
|
|
238
225
|
|
|
239
226
|
if (imgRatio > canvasRatio) {
|
|
240
227
|
drawWidth = pdfWidth * 0.8;
|
|
@@ -244,8 +231,8 @@ export async function createPdfFromImage(
|
|
|
244
231
|
drawWidth = drawHeight * imgRatio;
|
|
245
232
|
}
|
|
246
233
|
|
|
247
|
-
offsetX = (pdfWidth - drawWidth) / 2;
|
|
248
|
-
offsetY = (pdfHeight - drawHeight) / 2;
|
|
234
|
+
const offsetX = (pdfWidth - drawWidth) / 2;
|
|
235
|
+
const offsetY = (pdfHeight - drawHeight) / 2;
|
|
249
236
|
|
|
250
237
|
// 绘制图片
|
|
251
238
|
ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
|
|
@@ -44,6 +44,14 @@ export interface TransformMapping {
|
|
|
44
44
|
// 转换函数
|
|
45
45
|
// ============================================================================
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* 将数据转换为折线图或柱状图配置
|
|
49
|
+
* @param data 数据源
|
|
50
|
+
* @param chartType 图表类型 (line 或 bar)
|
|
51
|
+
* @param mapping 字段映射
|
|
52
|
+
* @param extraConfig 额外配置
|
|
53
|
+
* @returns ECharts 配置
|
|
54
|
+
*/
|
|
47
55
|
export function transformLineOrBar(
|
|
48
56
|
data: DataSource,
|
|
49
57
|
chartType: 'line' | 'bar',
|
|
@@ -90,6 +98,13 @@ export function transformLineOrBar(
|
|
|
90
98
|
};
|
|
91
99
|
}
|
|
92
100
|
|
|
101
|
+
/**
|
|
102
|
+
* 将数据转换为饼图配置
|
|
103
|
+
* @param data 数据源
|
|
104
|
+
* @param mapping 字段映射
|
|
105
|
+
* @param extraConfig 额外配置
|
|
106
|
+
* @returns ECharts 配置
|
|
107
|
+
*/
|
|
93
108
|
export function transformPie(
|
|
94
109
|
data: DataSource,
|
|
95
110
|
mapping: TransformMapping,
|
|
@@ -110,6 +125,13 @@ export function transformPie(
|
|
|
110
125
|
};
|
|
111
126
|
}
|
|
112
127
|
|
|
128
|
+
/**
|
|
129
|
+
* 将数据转换为散点图配置
|
|
130
|
+
* @param data 数据源
|
|
131
|
+
* @param mapping 字段映射
|
|
132
|
+
* @param extraConfig 额外配置
|
|
133
|
+
* @returns ECharts 配置
|
|
134
|
+
*/
|
|
113
135
|
export function transformScatter(
|
|
114
136
|
data: DataSource,
|
|
115
137
|
mapping: TransformMapping,
|
package/src/index.ts
CHANGED
|
@@ -35,38 +35,54 @@ export {
|
|
|
35
35
|
prefixedId,
|
|
36
36
|
} from './core/utils';
|
|
37
37
|
|
|
38
|
-
//
|
|
39
|
-
export {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
|
|
38
|
+
// 图表组件(统一从 charts/index.ts 导入)
|
|
39
|
+
export {
|
|
40
|
+
// 基础图表
|
|
41
|
+
LineChart,
|
|
42
|
+
BarChart,
|
|
43
|
+
PieChart,
|
|
44
|
+
ScatterChart,
|
|
45
|
+
RadarChartCustom as RadarChart,
|
|
46
|
+
HeatmapChart,
|
|
47
|
+
FunnelChart,
|
|
48
|
+
// 扩展图表
|
|
49
|
+
TreeMapChart,
|
|
50
|
+
SunburstChart,
|
|
51
|
+
SankeyChart,
|
|
52
|
+
GraphChart,
|
|
53
|
+
WordCloudChart,
|
|
54
|
+
// 特殊图表
|
|
55
|
+
BoxplotChart,
|
|
56
|
+
ParallelChart,
|
|
57
|
+
TreeChart,
|
|
58
|
+
// 类型
|
|
59
|
+
type BaseChartProps,
|
|
60
|
+
type LineChartProps,
|
|
61
|
+
type BarChartProps,
|
|
62
|
+
type PieChartProps,
|
|
63
|
+
type ScatterChartProps,
|
|
64
|
+
type RadarChartProps,
|
|
65
|
+
type FunnelChartProps,
|
|
66
|
+
type HeatmapChartProps,
|
|
67
|
+
type SunburstChartProps,
|
|
68
|
+
type TreeMapChartProps,
|
|
69
|
+
type SankeyChartProps,
|
|
70
|
+
type GraphChartProps,
|
|
71
|
+
type WordCloudChartProps,
|
|
72
|
+
type BoxplotChartProps,
|
|
73
|
+
type ParallelChartProps,
|
|
74
|
+
type TreeChartProps,
|
|
75
|
+
} from './charts';
|
|
61
76
|
|
|
62
77
|
// v1.7.0 新增组件
|
|
63
|
-
export {
|
|
78
|
+
export {
|
|
79
|
+
DataFilter,
|
|
80
|
+
type DataFilterProps,
|
|
81
|
+
type FilterField,
|
|
82
|
+
type FilterValues,
|
|
83
|
+
} from './components/DataFilter';
|
|
64
84
|
export {
|
|
65
85
|
createDrillDown,
|
|
66
|
-
canDrillDown,
|
|
67
|
-
buildHierarchy,
|
|
68
|
-
createRegionDrillDown,
|
|
69
|
-
createCategoryDrillDown,
|
|
70
86
|
type DrillDownConfig,
|
|
71
87
|
type DrillDownSource,
|
|
72
88
|
type DrillDownReturn,
|
|
@@ -74,15 +90,15 @@ export {
|
|
|
74
90
|
type DrillUpEventParams,
|
|
75
91
|
} from './core/utils/drillDown';
|
|
76
92
|
|
|
77
|
-
// v1.7.0 新增图表组件
|
|
78
|
-
export { default as LiquidChart } from './charts/liquid';
|
|
79
|
-
export { default as TreeChart } from './charts/tree';
|
|
80
|
-
|
|
81
93
|
// 适配器
|
|
82
94
|
export { getAdapter, detectPlatform, getEnv } from './adapters';
|
|
83
95
|
export { default as H5Adapter } from './adapters/h5';
|
|
84
96
|
export { default as WeappAdapter } from './adapters/weapp';
|
|
85
97
|
|
|
98
|
+
// 统一运行时检测
|
|
99
|
+
export { detectRuntime, resetRuntimeCache } from './core/utils/runtime';
|
|
100
|
+
export type { RuntimeInfo, MiniAppType } from './core/utils/runtime';
|
|
101
|
+
|
|
86
102
|
// 主题系统
|
|
87
103
|
export type { BuiltinTheme, ThemeOptions } from './themes';
|
|
88
104
|
export {
|
|
@@ -172,7 +188,6 @@ export {
|
|
|
172
188
|
useTableTransform,
|
|
173
189
|
useTimeSeriesTransform,
|
|
174
190
|
// v1.7.0 新增 Hooks
|
|
175
|
-
useDataZoom,
|
|
176
191
|
useChartConnect,
|
|
177
192
|
useChartDownload,
|
|
178
193
|
} from './hooks';
|
|
@@ -181,4 +196,3 @@ export {
|
|
|
181
196
|
* 库信息
|
|
182
197
|
*/
|
|
183
198
|
export const name = 'taroviz';
|
|
184
|
-
export const version = '1.7.0';
|