@agions/taroviz 1.5.0 → 1.7.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.
- package/README.md +26 -21
- package/dist/cjs/index.js +1 -1
- package/dist/esm/index.js +51271 -3480
- package/package.json +2 -1
- package/src/charts/boxplot/__tests__/index.test.tsx +130 -0
- package/src/charts/boxplot/index.tsx +18 -0
- package/src/charts/boxplot/types.ts +46 -0
- package/src/charts/index.ts +9 -1
- package/src/charts/liquid/index.tsx +226 -0
- package/src/charts/liquid/types.ts +130 -0
- package/src/charts/parallel/__tests__/index.test.tsx +164 -0
- package/src/charts/parallel/index.tsx +18 -0
- package/src/charts/parallel/types.ts +73 -0
- package/src/charts/tree/index.tsx +117 -0
- package/src/charts/tree/types.ts +174 -0
- package/src/components/DataFilter/index.tsx +587 -0
- package/src/core/utils/drillDown.ts +643 -0
- package/src/editor/EnhancedThemeEditor.tsx +624 -0
- package/src/hooks/index.ts +39 -1
- package/src/hooks/useChartConnect.ts +362 -0
- package/src/hooks/useChartDownload.ts +692 -0
- package/src/hooks/useDataZoom.ts +323 -0
- package/src/index.ts +37 -9
- package/src/themes/index.ts +3 -0
- package/src/themes/useAutoTheme.ts +66 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agions/taroviz",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "基于 Taro 和 ECharts 的多端图表组件库",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cjs/index.js",
|
|
@@ -139,6 +139,7 @@
|
|
|
139
139
|
},
|
|
140
140
|
"dependencies": {
|
|
141
141
|
"@babel/runtime": "^7.28.4",
|
|
142
|
+
"echarts-liquidfill": "^3.1.0",
|
|
142
143
|
"tslib": "^2.8.1"
|
|
143
144
|
},
|
|
144
145
|
"peerDependencies": {
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { render } from '@testing-library/react';
|
|
6
|
+
import '@testing-library/jest-dom';
|
|
7
|
+
import BoxplotChart from '../index';
|
|
8
|
+
|
|
9
|
+
// Mock ECharts and adapters
|
|
10
|
+
jest.mock('echarts/core', () => ({
|
|
11
|
+
use: jest.fn(),
|
|
12
|
+
init: jest.fn(() => ({
|
|
13
|
+
setOption: jest.fn(),
|
|
14
|
+
showLoading: jest.fn(),
|
|
15
|
+
hideLoading: jest.fn(),
|
|
16
|
+
on: jest.fn(),
|
|
17
|
+
off: jest.fn(),
|
|
18
|
+
dispose: jest.fn(),
|
|
19
|
+
resize: jest.fn(),
|
|
20
|
+
})),
|
|
21
|
+
getInstanceByDom: jest.fn(),
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
// Mock ECharts components
|
|
25
|
+
jest.mock('echarts/components', () => ({
|
|
26
|
+
GridComponent: jest.fn(),
|
|
27
|
+
TooltipComponent: jest.fn(),
|
|
28
|
+
TitleComponent: jest.fn(),
|
|
29
|
+
LegendComponent: jest.fn(),
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
// Mock BaseChartWrapper
|
|
33
|
+
jest.mock('../../common/BaseChartWrapper', () => ({
|
|
34
|
+
__esModule: true,
|
|
35
|
+
default: (props: any) => (
|
|
36
|
+
<div
|
|
37
|
+
data-testid="boxplot-chart"
|
|
38
|
+
className={`taroviz-boxplot ${props.className || ''}`}
|
|
39
|
+
style={{ width: props.width || '100%', height: props.height || 300, ...props.style }}
|
|
40
|
+
>
|
|
41
|
+
<div data-testid="chart-option">{JSON.stringify(props.option)}</div>
|
|
42
|
+
</div>
|
|
43
|
+
),
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
describe('BoxplotChart', () => {
|
|
47
|
+
const basicOption = {
|
|
48
|
+
title: { text: '箱线图测试' },
|
|
49
|
+
xAxis: { type: 'category' as const, data: ['A', 'B', 'C'] },
|
|
50
|
+
yAxis: { type: 'value' as const },
|
|
51
|
+
series: [{
|
|
52
|
+
type: 'boxplot' as const,
|
|
53
|
+
data: [
|
|
54
|
+
[850, 940, 980, 1050, 1130],
|
|
55
|
+
[920, 1000, 1050, 1150, 1200],
|
|
56
|
+
[780, 850, 920, 1050, 1150],
|
|
57
|
+
],
|
|
58
|
+
}],
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
describe('Basic Rendering', () => {
|
|
62
|
+
it('should render without crashing', () => {
|
|
63
|
+
const { getByTestId } = render(<BoxplotChart option={basicOption} />);
|
|
64
|
+
expect(getByTestId('boxplot-chart')).toBeInTheDocument();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should render with custom width and height', () => {
|
|
68
|
+
const { getByTestId } = render(<BoxplotChart option={basicOption} width={600} height={500} />);
|
|
69
|
+
expect(getByTestId('boxplot-chart')).toBeInTheDocument();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should have correct display name', () => {
|
|
73
|
+
expect(BoxplotChart.displayName).toBe('BoxplotChart');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should pass option to wrapper', () => {
|
|
77
|
+
const { getByTestId } = render(<BoxplotChart option={basicOption} />);
|
|
78
|
+
expect(getByTestId('chart-option')).toHaveTextContent('箱线图测试');
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('Props', () => {
|
|
83
|
+
it('should accept className prop', () => {
|
|
84
|
+
const { getByTestId } = render(<BoxplotChart option={basicOption} className="test-class" />);
|
|
85
|
+
expect(getByTestId('boxplot-chart')).toHaveClass('test-class');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should accept style prop', () => {
|
|
89
|
+
const { getByTestId } = render(<BoxplotChart option={basicOption} style={{ padding: '10px' }} />);
|
|
90
|
+
expect(getByTestId('boxplot-chart')).toBeInTheDocument();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should accept loading prop', () => {
|
|
94
|
+
const { getByTestId } = render(<BoxplotChart option={basicOption} loading={true} />);
|
|
95
|
+
expect(getByTestId('boxplot-chart')).toBeInTheDocument();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should accept theme prop', () => {
|
|
99
|
+
const { getByTestId } = render(<BoxplotChart option={basicOption} theme="dark" />);
|
|
100
|
+
expect(getByTestId('boxplot-chart')).toBeInTheDocument();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('Chart Options', () => {
|
|
105
|
+
it('should render with multiple data series', () => {
|
|
106
|
+
const multiOption = {
|
|
107
|
+
...basicOption,
|
|
108
|
+
series: [
|
|
109
|
+
{ type: 'boxplot' as const, name: '2024', data: [[850, 940, 980, 1050, 1130]] },
|
|
110
|
+
{ type: 'boxplot' as const, name: '2025', data: [[920, 1000, 1050, 1150, 1200]] },
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
const { getByTestId } = render(<BoxplotChart option={multiOption} />);
|
|
114
|
+
expect(getByTestId('boxplot-chart')).toBeInTheDocument();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should render with custom itemStyle', () => {
|
|
118
|
+
const optionWithStyle = {
|
|
119
|
+
...basicOption,
|
|
120
|
+
series: [{
|
|
121
|
+
type: 'boxplot' as const,
|
|
122
|
+
data: [[850, 940, 980, 1050, 1130]],
|
|
123
|
+
itemStyle: { color: '#1890ff', borderColor: '#000' },
|
|
124
|
+
}],
|
|
125
|
+
};
|
|
126
|
+
const { getByTestId } = render(<BoxplotChart option={optionWithStyle} />);
|
|
127
|
+
expect(getByTestId('boxplot-chart')).toBeInTheDocument();
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 箱线图组件
|
|
3
|
+
* 用于展示数据的分布情况,包括最小值、Q1、中位数、Q3、最大值
|
|
4
|
+
*/
|
|
5
|
+
import React, { memo } from 'react';
|
|
6
|
+
import BaseChartWrapper from '../common/BaseChartWrapper';
|
|
7
|
+
import { BoxplotChartProps } from './types';
|
|
8
|
+
|
|
9
|
+
const BoxplotChart: React.FC<BoxplotChartProps> = memo((props) => (
|
|
10
|
+
<BaseChartWrapper {...props} chartType="boxplot" />
|
|
11
|
+
));
|
|
12
|
+
|
|
13
|
+
BoxplotChart.displayName = 'BoxplotChart';
|
|
14
|
+
|
|
15
|
+
export default BoxplotChart;
|
|
16
|
+
|
|
17
|
+
// 导出类型
|
|
18
|
+
export type { BoxplotChartProps, BoxplotOption, BoxplotSeriesItem } from './types';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 箱线图类型定义
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type BoxplotChartProps = {
|
|
6
|
+
option?: BoxplotOption;
|
|
7
|
+
width?: string | number;
|
|
8
|
+
height?: string | number;
|
|
9
|
+
className?: string;
|
|
10
|
+
style?: React.CSSProperties;
|
|
11
|
+
onEvents?: Record<string, (params: any) => void>;
|
|
12
|
+
loading?: boolean;
|
|
13
|
+
loadingOption?: any;
|
|
14
|
+
theme?: string;
|
|
15
|
+
onChartReady?: (chart: any) => void;
|
|
16
|
+
opts?: {
|
|
17
|
+
devicePixelRatio?: number;
|
|
18
|
+
renderer?: 'canvas' | 'svg';
|
|
19
|
+
useDirtyRect?: boolean;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export interface BoxplotOption {
|
|
24
|
+
title?: any;
|
|
25
|
+
legend?: any;
|
|
26
|
+
grid?: any;
|
|
27
|
+
xAxis?: any;
|
|
28
|
+
yAxis?: any;
|
|
29
|
+
tooltip?: any;
|
|
30
|
+
series: BoxplotSeriesItem[];
|
|
31
|
+
dataset?: any;
|
|
32
|
+
color?: string[];
|
|
33
|
+
backgroundColor?: any;
|
|
34
|
+
textStyle?: any;
|
|
35
|
+
[key: string]: any;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface BoxplotSeriesItem {
|
|
39
|
+
type: 'boxplot';
|
|
40
|
+
name?: string;
|
|
41
|
+
data?: any[];
|
|
42
|
+
itemStyle?: any;
|
|
43
|
+
emphasis?: any;
|
|
44
|
+
dimensions?: string[];
|
|
45
|
+
encode?: any;
|
|
46
|
+
}
|
package/src/charts/index.ts
CHANGED
|
@@ -22,10 +22,18 @@ export { default as GraphChart } from './graph';
|
|
|
22
22
|
export { default as CandlestickChart } from './candlestick';
|
|
23
23
|
export { default as WordCloudChart } from './wordcloud';
|
|
24
24
|
|
|
25
|
+
// 导出 v1.6.0 新增图表组件
|
|
26
|
+
export { default as BoxplotChart } from './boxplot';
|
|
27
|
+
export { default as ParallelChart } from './parallel';
|
|
28
|
+
|
|
29
|
+
// 导出 v1.7.0 新增图表组件
|
|
30
|
+
export { default as LiquidChart } from './liquid';
|
|
31
|
+
export { default as TreeChart } from './tree';
|
|
32
|
+
|
|
25
33
|
// 导出类型定义
|
|
26
34
|
export * from './types';
|
|
27
35
|
|
|
28
36
|
/**
|
|
29
37
|
* 版本信息
|
|
30
38
|
*/
|
|
31
|
-
export const version = '1.
|
|
39
|
+
export const version = '1.7.0';
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 水球图组件
|
|
3
|
+
* ECharts 没有内置水球图,使用 echarts-liquidfill 库实现
|
|
4
|
+
*/
|
|
5
|
+
import React, { memo, useEffect, useRef, useMemo } from 'react';
|
|
6
|
+
import { getAdapter } from '../../adapters';
|
|
7
|
+
import { uuid } from '../../core/utils';
|
|
8
|
+
import { processAdapterConfig } from '../utils';
|
|
9
|
+
import { LiquidChartProps } from './types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 水球图组件
|
|
13
|
+
* 使用 echarts-liquidfill 实现
|
|
14
|
+
*/
|
|
15
|
+
const LiquidChart: React.FC<LiquidChartProps> = memo((props) => {
|
|
16
|
+
const {
|
|
17
|
+
option,
|
|
18
|
+
width = '100%',
|
|
19
|
+
height = '300px',
|
|
20
|
+
theme,
|
|
21
|
+
style = {},
|
|
22
|
+
className = '',
|
|
23
|
+
autoResize = true,
|
|
24
|
+
loading = false,
|
|
25
|
+
loadingOption,
|
|
26
|
+
onChartInit,
|
|
27
|
+
onChartReady,
|
|
28
|
+
renderer = 'canvas',
|
|
29
|
+
onEvents = {},
|
|
30
|
+
waveData = [0.6],
|
|
31
|
+
shape = 'circle',
|
|
32
|
+
amplitude,
|
|
33
|
+
waveLength,
|
|
34
|
+
phase,
|
|
35
|
+
period,
|
|
36
|
+
backgroundColor = 'transparent',
|
|
37
|
+
color,
|
|
38
|
+
showLabel = true,
|
|
39
|
+
labelFormatter,
|
|
40
|
+
} = props;
|
|
41
|
+
|
|
42
|
+
const chartId = useRef<string>(`liquid-${uuid()}`);
|
|
43
|
+
const chartInstance = useRef<any>(null);
|
|
44
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
45
|
+
const extensionRegistered = useRef<boolean>(false);
|
|
46
|
+
|
|
47
|
+
// 构建水球图配置
|
|
48
|
+
const liquidOption = useMemo(() => {
|
|
49
|
+
const baseOption = option || {};
|
|
50
|
+
|
|
51
|
+
// 如果用户提供了 option,直接返回
|
|
52
|
+
if (baseOption.series && baseOption.series.length > 0) {
|
|
53
|
+
return baseOption;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 构建水球图 series
|
|
57
|
+
const seriesData = waveData.map((value) => {
|
|
58
|
+
const dataItem: Record<string, unknown> = { value };
|
|
59
|
+
|
|
60
|
+
if (showLabel && labelFormatter) {
|
|
61
|
+
dataItem.label = {
|
|
62
|
+
formatter: labelFormatter(value),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return dataItem;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const seriesConfig: Record<string, unknown> = {
|
|
70
|
+
type: 'liquidFill',
|
|
71
|
+
data: waveData,
|
|
72
|
+
shape,
|
|
73
|
+
backgroundColor,
|
|
74
|
+
color: color || ['#4cabce', '#4cabce'],
|
|
75
|
+
label: showLabel
|
|
76
|
+
? {
|
|
77
|
+
show: true,
|
|
78
|
+
formatter: labelFormatter
|
|
79
|
+
? (params: any) => labelFormatter(params.value)
|
|
80
|
+
: '{d}%',
|
|
81
|
+
textStyle: {
|
|
82
|
+
fontSize: 20,
|
|
83
|
+
fontWeight: 'bold',
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
: { show: false },
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// 添加可选参数
|
|
90
|
+
if (amplitude !== undefined) {
|
|
91
|
+
seriesConfig.amplitude = amplitude;
|
|
92
|
+
}
|
|
93
|
+
if (waveLength !== undefined) {
|
|
94
|
+
seriesConfig.waveLength = waveLength;
|
|
95
|
+
}
|
|
96
|
+
if (phase !== undefined) {
|
|
97
|
+
seriesConfig.phase = phase;
|
|
98
|
+
}
|
|
99
|
+
if (period !== undefined) {
|
|
100
|
+
seriesConfig.period = period;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
...baseOption,
|
|
105
|
+
series: [seriesConfig],
|
|
106
|
+
};
|
|
107
|
+
}, [option, waveData, shape, backgroundColor, color, showLabel, labelFormatter, amplitude, waveLength, phase, period]);
|
|
108
|
+
|
|
109
|
+
// 处理图表初始化
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
let mounted = true;
|
|
112
|
+
|
|
113
|
+
const initChart = async (): Promise<(() => void) | undefined> => {
|
|
114
|
+
// 动态导入 echarts-liquidfill 并注册扩展
|
|
115
|
+
if (!extensionRegistered.current) {
|
|
116
|
+
try {
|
|
117
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
118
|
+
const liquidfill = require('echarts-liquidfill');
|
|
119
|
+
if (liquidfill && mounted) {
|
|
120
|
+
liquidfill.register && liquidfill.register();
|
|
121
|
+
extensionRegistered.current = true;
|
|
122
|
+
}
|
|
123
|
+
} catch (e) {
|
|
124
|
+
console.warn('[TaroViz] LiquidChart: Failed to load echarts-liquidfill extension', e);
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!mounted || !containerRef.current) return undefined;
|
|
130
|
+
|
|
131
|
+
const initConfig = processAdapterConfig({
|
|
132
|
+
canvasId: chartId.current,
|
|
133
|
+
containerRef,
|
|
134
|
+
width,
|
|
135
|
+
height,
|
|
136
|
+
theme,
|
|
137
|
+
autoResize,
|
|
138
|
+
renderer,
|
|
139
|
+
option: liquidOption,
|
|
140
|
+
onInit: (instance: any) => {
|
|
141
|
+
chartInstance.current = instance;
|
|
142
|
+
|
|
143
|
+
// 绑定事件
|
|
144
|
+
if (onEvents) {
|
|
145
|
+
Object.keys(onEvents).forEach((eventName) => {
|
|
146
|
+
instance.on(eventName, (onEvents as any)[eventName]);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (onChartInit) {
|
|
151
|
+
onChartInit(instance);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (onChartReady) {
|
|
155
|
+
onChartReady(instance);
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const adapter = await getAdapter(initConfig);
|
|
161
|
+
adapter.init();
|
|
162
|
+
|
|
163
|
+
return () => {
|
|
164
|
+
if (chartInstance.current) {
|
|
165
|
+
if (onEvents) {
|
|
166
|
+
Object.keys(onEvents).forEach((eventName) => {
|
|
167
|
+
chartInstance.current?.off(eventName);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
chartInstance.current.dispose();
|
|
171
|
+
chartInstance.current = null;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
let cleanupFn: (() => void) | undefined;
|
|
177
|
+
initChart().then((cleanup) => {
|
|
178
|
+
cleanupFn = cleanup;
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
return () => {
|
|
182
|
+
mounted = false;
|
|
183
|
+
cleanupFn?.();
|
|
184
|
+
};
|
|
185
|
+
}, [liquidOption, width, height, theme, autoResize, renderer, onChartInit, onChartReady, onEvents]);
|
|
186
|
+
|
|
187
|
+
// 更新配置
|
|
188
|
+
useEffect(() => {
|
|
189
|
+
if (chartInstance.current && liquidOption) {
|
|
190
|
+
chartInstance.current.setOption(liquidOption, true);
|
|
191
|
+
}
|
|
192
|
+
}, [liquidOption]);
|
|
193
|
+
|
|
194
|
+
// 控制加载状态
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
if (chartInstance.current) {
|
|
197
|
+
if (loading) {
|
|
198
|
+
chartInstance.current.showLoading(loadingOption);
|
|
199
|
+
} else {
|
|
200
|
+
chartInstance.current.hideLoading();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}, [loading, loadingOption]);
|
|
204
|
+
|
|
205
|
+
// 自定义样式
|
|
206
|
+
const mergedStyle = {
|
|
207
|
+
width: typeof width === 'number' ? `${width}px` : width,
|
|
208
|
+
height: typeof height === 'number' ? `${height}px` : height,
|
|
209
|
+
...style,
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<div
|
|
214
|
+
className={`taroviz-liquid ${className}`}
|
|
215
|
+
style={mergedStyle}
|
|
216
|
+
ref={containerRef as React.RefObject<HTMLDivElement>}
|
|
217
|
+
/>
|
|
218
|
+
);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
LiquidChart.displayName = 'LiquidChart';
|
|
222
|
+
|
|
223
|
+
export default LiquidChart;
|
|
224
|
+
|
|
225
|
+
// 导出类型
|
|
226
|
+
export type { LiquidChartProps, LiquidOption, LiquidShape, LiquidSeries, LiquidSeriesDataItem } from './types';
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 水球图类型定义
|
|
3
|
+
* ECharts 没有内置水球图,使用 echarts-liquidfill 库实现
|
|
4
|
+
*/
|
|
5
|
+
import type React from 'react';
|
|
6
|
+
import type { EChartsOption } from 'echarts';
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// 水球图配置类型
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
/** 水球图系列数据项 */
|
|
13
|
+
export interface LiquidSeriesDataItem {
|
|
14
|
+
/** 数据值,范围 [0, 1] */
|
|
15
|
+
value: number;
|
|
16
|
+
/** 数据项名称 */
|
|
17
|
+
name?: string;
|
|
18
|
+
/** 图形样式 */
|
|
19
|
+
itemStyle?: Record<string, unknown>;
|
|
20
|
+
/** 标签配置 */
|
|
21
|
+
label?: Record<string, unknown>;
|
|
22
|
+
/** 强调状态 */
|
|
23
|
+
emphasis?: Record<string, unknown>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 水球图形状类型 */
|
|
27
|
+
export type LiquidShape = 'circle' | 'rect' | 'roundRect';
|
|
28
|
+
|
|
29
|
+
/** 水球图系列配置 */
|
|
30
|
+
export interface LiquidSeries {
|
|
31
|
+
/** 系列类型 */
|
|
32
|
+
type?: 'liquidFill';
|
|
33
|
+
/** 系列名称 */
|
|
34
|
+
name?: string;
|
|
35
|
+
/** 数据数组 */
|
|
36
|
+
data?: (number | LiquidSeriesDataItem)[];
|
|
37
|
+
/** 图形形状 */
|
|
38
|
+
shape?: LiquidShape;
|
|
39
|
+
/** 振幅 (相对于半径的比例) */
|
|
40
|
+
amplitude?: number;
|
|
41
|
+
/** 波长 (相对于画布宽度) */
|
|
42
|
+
waveLength?: number | string;
|
|
43
|
+
/** 相位偏移 */
|
|
44
|
+
phase?: number;
|
|
45
|
+
/** 周期时间 (ms) */
|
|
46
|
+
period?: number;
|
|
47
|
+
/** 波的个数 */
|
|
48
|
+
waveCount?: number;
|
|
49
|
+
/** 是否禁用动画 */
|
|
50
|
+
animationDisabled?: boolean;
|
|
51
|
+
/** 动画时长 */
|
|
52
|
+
animationDuration?: number;
|
|
53
|
+
/** 动画缓动函数 */
|
|
54
|
+
animationEasing?: string;
|
|
55
|
+
/** 动画延迟 */
|
|
56
|
+
animationDelay?: number | ((idx: number) => number);
|
|
57
|
+
/** 颜色数组 */
|
|
58
|
+
color?: string[];
|
|
59
|
+
/** 背景色 */
|
|
60
|
+
backgroundColor?: string;
|
|
61
|
+
/** 图形内标签 */
|
|
62
|
+
label?: Record<string, unknown>;
|
|
63
|
+
/** 图形样式 */
|
|
64
|
+
itemStyle?: Record<string, unknown>;
|
|
65
|
+
/** emphasis状态 */
|
|
66
|
+
emphasis?: Record<string, unknown>;
|
|
67
|
+
/** 浪的样式 */
|
|
68
|
+
waveStyle?: Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** 水球图选项配置 */
|
|
72
|
+
export interface LiquidOption extends Omit<EChartsOption, 'series'> {
|
|
73
|
+
series?: LiquidSeries[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ============================================================================
|
|
77
|
+
// 组件 Props
|
|
78
|
+
// ============================================================================
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 水球图组件属性
|
|
82
|
+
*/
|
|
83
|
+
export interface LiquidChartProps {
|
|
84
|
+
/** 图表配置项 (EChartsOption) */
|
|
85
|
+
option?: LiquidOption;
|
|
86
|
+
/** 宽度 */
|
|
87
|
+
width?: string | number;
|
|
88
|
+
/** 高度 */
|
|
89
|
+
height?: string | number;
|
|
90
|
+
/** 水球数据数组,每个值代表一个波纹,范围 [0, 1] */
|
|
91
|
+
waveData?: number[];
|
|
92
|
+
/** 图形形状 */
|
|
93
|
+
shape?: LiquidShape;
|
|
94
|
+
/** 振幅 (相对于半径的比例) */
|
|
95
|
+
amplitude?: number;
|
|
96
|
+
/** 波长 (相对于画布宽度) */
|
|
97
|
+
waveLength?: number | string;
|
|
98
|
+
/** 相位偏移 */
|
|
99
|
+
phase?: number;
|
|
100
|
+
/** 周期时间 (ms) */
|
|
101
|
+
period?: number;
|
|
102
|
+
/** 背景色 */
|
|
103
|
+
backgroundColor?: string;
|
|
104
|
+
/** 颜色数组 */
|
|
105
|
+
color?: string[];
|
|
106
|
+
/** 是否显示标签 */
|
|
107
|
+
showLabel?: boolean;
|
|
108
|
+
/** 标签格式化 */
|
|
109
|
+
labelFormatter?: (value: number) => string;
|
|
110
|
+
/** 主题 */
|
|
111
|
+
theme?: string | Record<string, unknown>;
|
|
112
|
+
/** 样式 */
|
|
113
|
+
style?: React.CSSProperties;
|
|
114
|
+
/** 类名 */
|
|
115
|
+
className?: string;
|
|
116
|
+
/** 是否自动调整大小 */
|
|
117
|
+
autoResize?: boolean;
|
|
118
|
+
/** 渲染器类型 */
|
|
119
|
+
renderer?: 'canvas' | 'svg';
|
|
120
|
+
/** 加载状态 */
|
|
121
|
+
loading?: boolean;
|
|
122
|
+
/** 加载配置 */
|
|
123
|
+
loadingOption?: Record<string, unknown>;
|
|
124
|
+
/** 图表初始化回调 */
|
|
125
|
+
onChartInit?: (chart: any) => void;
|
|
126
|
+
/** 图表就绪回调 */
|
|
127
|
+
onChartReady?: (chart: any) => void;
|
|
128
|
+
/** 事件回调 */
|
|
129
|
+
onEvents?: Record<string, (params: any) => void>;
|
|
130
|
+
}
|