@beppla/tapas-ui 1.4.30 → 1.4.31
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/commonjs/PieChart/BACKWARD_COMPATIBILITY.md +157 -0
- package/commonjs/PieChart/BUGFIX_EMPTY_DATA.md +86 -0
- package/commonjs/PieChart/GroupedLegend.js +240 -0
- package/commonjs/PieChart/GroupedLegend.js.map +1 -0
- package/commonjs/PieChart/IMPLEMENTATION_SUMMARY.md +306 -0
- package/commonjs/PieChart/NestedPieChart.README.md +421 -0
- package/commonjs/PieChart/NestedPieChart.js +567 -0
- package/commonjs/PieChart/NestedPieChart.js.map +1 -0
- package/commonjs/PieChart/NestedPieChart.types.js +2 -0
- package/commonjs/PieChart/NestedPieChart.types.js.map +1 -0
- package/commonjs/PieChart/NestedPieChart.utils.js +360 -0
- package/commonjs/PieChart/NestedPieChart.utils.js.map +1 -0
- package/commonjs/PieChart/index.js +8 -0
- package/commonjs/PieChart/index.js.map +1 -1
- package/module/PieChart/BACKWARD_COMPATIBILITY.md +157 -0
- package/module/PieChart/BUGFIX_EMPTY_DATA.md +86 -0
- package/module/PieChart/GroupedLegend.js +234 -0
- package/module/PieChart/GroupedLegend.js.map +1 -0
- package/module/PieChart/IMPLEMENTATION_SUMMARY.md +306 -0
- package/module/PieChart/NestedPieChart.README.md +421 -0
- package/module/PieChart/NestedPieChart.js +563 -0
- package/module/PieChart/NestedPieChart.js.map +1 -0
- package/module/PieChart/NestedPieChart.types.js +2 -0
- package/module/PieChart/NestedPieChart.types.js.map +1 -0
- package/module/PieChart/NestedPieChart.utils.js +343 -0
- package/module/PieChart/NestedPieChart.utils.js.map +1 -0
- package/module/PieChart/index.js +1 -0
- package/module/PieChart/index.js.map +1 -1
- package/package.json +1 -1
- package/typescript/PieChart/GroupedLegend.d.ts +26 -0
- package/typescript/PieChart/GroupedLegend.d.ts.map +1 -0
- package/typescript/PieChart/NestedPieChart.d.ts +12 -0
- package/typescript/PieChart/NestedPieChart.d.ts.map +1 -0
- package/typescript/PieChart/NestedPieChart.types.d.ts +117 -0
- package/typescript/PieChart/NestedPieChart.types.d.ts.map +1 -0
- package/typescript/PieChart/NestedPieChart.utils.d.ts +57 -0
- package/typescript/PieChart/NestedPieChart.utils.d.ts.map +1 -0
- package/typescript/PieChart/index.d.ts +2 -0
- package/typescript/PieChart/index.d.ts.map +1 -1
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GroupedLegend - 分组图例组件
|
|
5
|
+
* 支持折叠/展开、分组显示、点击交互
|
|
6
|
+
*
|
|
7
|
+
* Note: This component uses inline styles as it's specifically designed for Web platform
|
|
8
|
+
* where CSS-in-JS is the standard approach. RN-specific lint rules are disabled.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/* eslint-disable react-native/no-inline-styles */
|
|
12
|
+
/* eslint-disable react-native/no-color-literals */
|
|
13
|
+
|
|
14
|
+
import React, { useState, useCallback } from 'react';
|
|
15
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
16
|
+
export const GroupedLegend = ({
|
|
17
|
+
groups,
|
|
18
|
+
showValues = true,
|
|
19
|
+
showPercent = true,
|
|
20
|
+
collapsible = true,
|
|
21
|
+
itemGap = 6,
|
|
22
|
+
groupGap = 16,
|
|
23
|
+
labelMinWidth,
|
|
24
|
+
formatValue,
|
|
25
|
+
formatPercent,
|
|
26
|
+
onItemClick,
|
|
27
|
+
onGroupClick,
|
|
28
|
+
highlightedKey,
|
|
29
|
+
hiddenKeys = new Set()
|
|
30
|
+
}) => {
|
|
31
|
+
const [expandedGroups, setExpandedGroups] = useState(new Set(groups.filter(g => g.isExpanded).map(g => g.groupKey)));
|
|
32
|
+
const toggleGroup = useCallback(groupKey => {
|
|
33
|
+
if (!collapsible) return;
|
|
34
|
+
setExpandedGroups(prev => {
|
|
35
|
+
const next = new Set(prev);
|
|
36
|
+
if (next.has(groupKey)) {
|
|
37
|
+
next.delete(groupKey);
|
|
38
|
+
} else {
|
|
39
|
+
next.add(groupKey);
|
|
40
|
+
}
|
|
41
|
+
return next;
|
|
42
|
+
});
|
|
43
|
+
}, [collapsible]);
|
|
44
|
+
const handleGroupClick = useCallback(group => {
|
|
45
|
+
if (collapsible) {
|
|
46
|
+
toggleGroup(group.groupKey);
|
|
47
|
+
}
|
|
48
|
+
if (onGroupClick) {
|
|
49
|
+
onGroupClick({
|
|
50
|
+
key: group.groupKey,
|
|
51
|
+
label: group.groupLabel,
|
|
52
|
+
value: group.groupValue,
|
|
53
|
+
level: 0,
|
|
54
|
+
path: [group.groupKey],
|
|
55
|
+
metadata: group.metadata,
|
|
56
|
+
groupKey: group.groupKey,
|
|
57
|
+
isGroupHeader: true
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}, [collapsible, toggleGroup, onGroupClick]);
|
|
61
|
+
const handleItemClick = useCallback((item, group) => {
|
|
62
|
+
if (onItemClick) {
|
|
63
|
+
onItemClick({
|
|
64
|
+
key: item.key,
|
|
65
|
+
label: item.label,
|
|
66
|
+
value: item.value,
|
|
67
|
+
level: 1,
|
|
68
|
+
path: [group.groupKey, item.key],
|
|
69
|
+
parentKey: group.groupKey,
|
|
70
|
+
metadata: item.metadata,
|
|
71
|
+
groupKey: group.groupKey,
|
|
72
|
+
isGroupHeader: false
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}, [onItemClick]);
|
|
76
|
+
return /*#__PURE__*/_jsx("div", {
|
|
77
|
+
style: {
|
|
78
|
+
display: 'flex',
|
|
79
|
+
flexDirection: 'column',
|
|
80
|
+
gap: `${groupGap}px`
|
|
81
|
+
},
|
|
82
|
+
children: groups.map(group => {
|
|
83
|
+
const isExpanded = expandedGroups.has(group.groupKey);
|
|
84
|
+
const isGroupHidden = hiddenKeys.has(group.groupKey);
|
|
85
|
+
const isGroupHighlighted = highlightedKey === group.groupKey;
|
|
86
|
+
return /*#__PURE__*/_jsxs("div", {
|
|
87
|
+
style: {
|
|
88
|
+
opacity: isGroupHidden ? 0.3 : 1,
|
|
89
|
+
transition: 'opacity 0.2s'
|
|
90
|
+
},
|
|
91
|
+
children: [/*#__PURE__*/_jsxs("div", {
|
|
92
|
+
onClick: () => handleGroupClick(group),
|
|
93
|
+
style: {
|
|
94
|
+
display: 'flex',
|
|
95
|
+
alignItems: 'center',
|
|
96
|
+
marginBottom: isExpanded && group.items.length > 0 ? `${itemGap + 4}px` : '0',
|
|
97
|
+
cursor: collapsible || onGroupClick ? 'pointer' : 'default',
|
|
98
|
+
padding: '4px 0',
|
|
99
|
+
backgroundColor: isGroupHighlighted ? 'rgba(0, 102, 204, 0.1)' : 'transparent',
|
|
100
|
+
borderRadius: '4px',
|
|
101
|
+
transition: 'background-color 0.2s'
|
|
102
|
+
},
|
|
103
|
+
onMouseEnter: e => {
|
|
104
|
+
if (!onGroupClick) return;
|
|
105
|
+
e.currentTarget.style.backgroundColor = 'rgba(0, 102, 204, 0.05)';
|
|
106
|
+
},
|
|
107
|
+
onMouseLeave: e => {
|
|
108
|
+
if (!onGroupClick) return;
|
|
109
|
+
e.currentTarget.style.backgroundColor = isGroupHighlighted ? 'rgba(0, 102, 204, 0.1)' : 'transparent';
|
|
110
|
+
},
|
|
111
|
+
children: [collapsible && group.items.length > 0 && /*#__PURE__*/_jsx("span", {
|
|
112
|
+
style: {
|
|
113
|
+
marginRight: '6px',
|
|
114
|
+
fontSize: '12px',
|
|
115
|
+
color: '#666',
|
|
116
|
+
transition: 'transform 0.2s',
|
|
117
|
+
transform: isExpanded ? 'rotate(90deg)' : 'rotate(0deg)',
|
|
118
|
+
display: 'inline-block'
|
|
119
|
+
},
|
|
120
|
+
children: "\u25B6"
|
|
121
|
+
}), /*#__PURE__*/_jsx("div", {
|
|
122
|
+
style: {
|
|
123
|
+
width: '14px',
|
|
124
|
+
height: '14px',
|
|
125
|
+
borderRadius: '3px',
|
|
126
|
+
backgroundColor: group.groupColor,
|
|
127
|
+
marginRight: '8px',
|
|
128
|
+
flexShrink: 0
|
|
129
|
+
}
|
|
130
|
+
}), /*#__PURE__*/_jsx("span", {
|
|
131
|
+
style: {
|
|
132
|
+
fontSize: '13px',
|
|
133
|
+
fontWeight: 600,
|
|
134
|
+
color: '#2C3E50',
|
|
135
|
+
flex: 1,
|
|
136
|
+
minWidth: labelMinWidth ? `${labelMinWidth}px` : 'auto'
|
|
137
|
+
},
|
|
138
|
+
children: group.groupLabel
|
|
139
|
+
}), /*#__PURE__*/_jsxs("div", {
|
|
140
|
+
style: {
|
|
141
|
+
display: 'flex',
|
|
142
|
+
gap: '8px',
|
|
143
|
+
marginLeft: '12px'
|
|
144
|
+
},
|
|
145
|
+
children: [showValues && formatValue && /*#__PURE__*/_jsx("span", {
|
|
146
|
+
style: {
|
|
147
|
+
fontSize: '13px',
|
|
148
|
+
fontWeight: 600,
|
|
149
|
+
color: '#2C3E50'
|
|
150
|
+
},
|
|
151
|
+
children: formatValue(group.groupValue)
|
|
152
|
+
}), showPercent && formatPercent && /*#__PURE__*/_jsxs("span", {
|
|
153
|
+
style: {
|
|
154
|
+
fontSize: '12px',
|
|
155
|
+
color: '#7F8C8D'
|
|
156
|
+
},
|
|
157
|
+
children: ["(", formatPercent(group.groupPercent), ")"]
|
|
158
|
+
})]
|
|
159
|
+
})]
|
|
160
|
+
}), isExpanded && group.items.length > 0 && /*#__PURE__*/_jsx("div", {
|
|
161
|
+
style: {
|
|
162
|
+
marginLeft: collapsible ? '20px' : '0',
|
|
163
|
+
display: 'flex',
|
|
164
|
+
flexDirection: 'column',
|
|
165
|
+
gap: `${itemGap}px`
|
|
166
|
+
},
|
|
167
|
+
children: group.items.map(item => {
|
|
168
|
+
const isItemHidden = hiddenKeys.has(item.key);
|
|
169
|
+
const isItemHighlighted = highlightedKey === item.key;
|
|
170
|
+
return /*#__PURE__*/_jsxs("div", {
|
|
171
|
+
onClick: () => handleItemClick(item, group),
|
|
172
|
+
style: {
|
|
173
|
+
display: 'flex',
|
|
174
|
+
alignItems: 'center',
|
|
175
|
+
opacity: isItemHidden ? 0.3 : 1,
|
|
176
|
+
cursor: onItemClick ? 'pointer' : 'default',
|
|
177
|
+
padding: '2px 0',
|
|
178
|
+
backgroundColor: isItemHighlighted ? 'rgba(0, 102, 204, 0.1)' : 'transparent',
|
|
179
|
+
borderRadius: '4px',
|
|
180
|
+
transition: 'all 0.2s'
|
|
181
|
+
},
|
|
182
|
+
onMouseEnter: e => {
|
|
183
|
+
if (!onItemClick) return;
|
|
184
|
+
e.currentTarget.style.backgroundColor = 'rgba(0, 102, 204, 0.05)';
|
|
185
|
+
},
|
|
186
|
+
onMouseLeave: e => {
|
|
187
|
+
if (!onItemClick) return;
|
|
188
|
+
e.currentTarget.style.backgroundColor = isItemHighlighted ? 'rgba(0, 102, 204, 0.1)' : 'transparent';
|
|
189
|
+
},
|
|
190
|
+
children: [/*#__PURE__*/_jsx("div", {
|
|
191
|
+
style: {
|
|
192
|
+
width: '10px',
|
|
193
|
+
height: '10px',
|
|
194
|
+
borderRadius: '2px',
|
|
195
|
+
backgroundColor: item.color,
|
|
196
|
+
marginRight: '8px',
|
|
197
|
+
flexShrink: 0
|
|
198
|
+
}
|
|
199
|
+
}), /*#__PURE__*/_jsx("span", {
|
|
200
|
+
style: {
|
|
201
|
+
fontSize: '12px',
|
|
202
|
+
color: '#34495E',
|
|
203
|
+
flex: 1,
|
|
204
|
+
minWidth: labelMinWidth ? `${labelMinWidth - 30}px` : 'auto'
|
|
205
|
+
},
|
|
206
|
+
children: item.label
|
|
207
|
+
}), /*#__PURE__*/_jsxs("div", {
|
|
208
|
+
style: {
|
|
209
|
+
display: 'flex',
|
|
210
|
+
gap: '8px',
|
|
211
|
+
marginLeft: '12px'
|
|
212
|
+
},
|
|
213
|
+
children: [showValues && formatValue && /*#__PURE__*/_jsx("span", {
|
|
214
|
+
style: {
|
|
215
|
+
fontSize: '12px',
|
|
216
|
+
color: '#34495E'
|
|
217
|
+
},
|
|
218
|
+
children: formatValue(item.value)
|
|
219
|
+
}), showPercent && formatPercent && /*#__PURE__*/_jsxs("span", {
|
|
220
|
+
style: {
|
|
221
|
+
fontSize: '11px',
|
|
222
|
+
color: '#95A5A6'
|
|
223
|
+
},
|
|
224
|
+
children: ["(", formatPercent(item.percent), ")"]
|
|
225
|
+
})]
|
|
226
|
+
})]
|
|
227
|
+
}, item.key);
|
|
228
|
+
})
|
|
229
|
+
})]
|
|
230
|
+
}, group.groupKey);
|
|
231
|
+
})
|
|
232
|
+
});
|
|
233
|
+
};
|
|
234
|
+
//# sourceMappingURL=GroupedLegend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["React","useState","useCallback","jsx","_jsx","jsxs","_jsxs","GroupedLegend","groups","showValues","showPercent","collapsible","itemGap","groupGap","labelMinWidth","formatValue","formatPercent","onItemClick","onGroupClick","highlightedKey","hiddenKeys","Set","expandedGroups","setExpandedGroups","filter","g","isExpanded","map","groupKey","toggleGroup","prev","next","has","delete","add","handleGroupClick","group","key","label","groupLabel","value","groupValue","level","path","metadata","isGroupHeader","handleItemClick","item","parentKey","style","display","flexDirection","gap","children","isGroupHidden","isGroupHighlighted","opacity","transition","onClick","alignItems","marginBottom","items","length","cursor","padding","backgroundColor","borderRadius","onMouseEnter","e","currentTarget","onMouseLeave","marginRight","fontSize","color","transform","width","height","groupColor","flexShrink","fontWeight","flex","minWidth","marginLeft","groupPercent","isItemHidden","isItemHighlighted","percent"],"sourceRoot":"../../../components","sources":["PieChart/GroupedLegend.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# NestedPieChart 组件实现总结
|
|
2
|
+
|
|
3
|
+
## 📋 实现概述
|
|
4
|
+
|
|
5
|
+
已成功为 tapas-ui 组件库扩展了 **NestedPieChart(多层级饼图)** 组件,完全满足 Store insights / Summary 等业务场景需求。
|
|
6
|
+
|
|
7
|
+
## ✅ 已完成功能清单
|
|
8
|
+
|
|
9
|
+
### 1. 多层级饼图核心功能 ✅
|
|
10
|
+
|
|
11
|
+
- ✅ **双层嵌套结构**:外层显示支付方式,内层显示税率明细
|
|
12
|
+
- ✅ **可配置层数**:支持 2 层,架构可扩展到 N 层
|
|
13
|
+
- ✅ **树形数据模型**:支持 `payment -> tax -> amount` 树结构
|
|
14
|
+
- ✅ **扁平数据支持**:同时支持 flat 和 tree 两种数据格式
|
|
15
|
+
- ✅ **数据自动处理**:
|
|
16
|
+
- 去重合并(同 key 自动汇总)
|
|
17
|
+
- 过滤 0 值
|
|
18
|
+
- 自动计算父节点值(子节点汇总)
|
|
19
|
+
|
|
20
|
+
### 2. 分组图例 (Grouped Legend) ✅
|
|
21
|
+
|
|
22
|
+
- ✅ **分组展示**:按支付方式分组,显示分组标题和小计
|
|
23
|
+
- ✅ **子项明细**:组内列出税率明细,不重复、顺序稳定
|
|
24
|
+
- ✅ **折叠/展开**:
|
|
25
|
+
- 支持点击分组标题折叠/展开
|
|
26
|
+
- 可配置默认展开/折叠状态
|
|
27
|
+
- 可禁用折叠功能(始终展开)
|
|
28
|
+
- ✅ **显示规则可配**:
|
|
29
|
+
- 同时显示值(€)和占比(%)
|
|
30
|
+
- 可单独控制显示/隐藏值或百分比
|
|
31
|
+
- 支持自定义格式化函数
|
|
32
|
+
- ✅ **排序策略**:
|
|
33
|
+
- 分组排序:按预设顺序/值/标签排序
|
|
34
|
+
- 组内排序:独立的排序策略
|
|
35
|
+
- 支持:none/value-desc/value-asc/label-asc/label-desc
|
|
36
|
+
|
|
37
|
+
### 3. 配色策略 ✅
|
|
38
|
+
|
|
39
|
+
- ✅ **大类固定基色**:支持自定义 baseColors mapping
|
|
40
|
+
- ✅ **子类渐变色生成**:
|
|
41
|
+
- 基于 HSL 色彩空间自动生成同色系渐变
|
|
42
|
+
- 固定色相,调整明度和饱和度
|
|
43
|
+
- 确保"同组同色系"视觉一致性
|
|
44
|
+
- ✅ **颜色冲突处理**:
|
|
45
|
+
- 可配置最小色差阈值
|
|
46
|
+
- 避免相邻子扇区颜色过于接近
|
|
47
|
+
- 限制明度范围避免过淡/过深
|
|
48
|
+
- ✅ **自定义颜色优先**:支持手动指定单个节点颜色
|
|
49
|
+
|
|
50
|
+
### 4. 交互与高亮 ✅
|
|
51
|
+
|
|
52
|
+
- ✅ **Hover 交互**:
|
|
53
|
+
- 悬停扇区高亮对应图例项
|
|
54
|
+
- 悬停图例项高亮对应扇区
|
|
55
|
+
- 其他项透明度降低(0.3)
|
|
56
|
+
- 平滑过渡动画
|
|
57
|
+
- ✅ **点击事件**:
|
|
58
|
+
- `onSliceClick`:扇区点击回调
|
|
59
|
+
- `onLegendClick`:图例点击回调
|
|
60
|
+
- 事件对象包含完整路径和元数据
|
|
61
|
+
- ✅ **筛选功能**:
|
|
62
|
+
- `enableLegendFilter`:点击图例隐藏/显示扇区
|
|
63
|
+
- 支持隐藏整个支付方式或单个税率
|
|
64
|
+
- 状态由组件管理(内部 state)
|
|
65
|
+
- ✅ **Tooltip**:
|
|
66
|
+
- 默认 Tooltip 显示名称、值、百分比
|
|
67
|
+
- 支持 `customTooltip` 自定义渲染
|
|
68
|
+
- 格式化函数可配置
|
|
69
|
+
|
|
70
|
+
### 5. 布局与尺寸 ✅
|
|
71
|
+
|
|
72
|
+
- ✅ **自适应容器**:根据容器宽高自动计算半径
|
|
73
|
+
- ✅ **半径配置**:
|
|
74
|
+
- `outerRadius`:外圆半径(数字或百分比)
|
|
75
|
+
- `innerRadius`:内圆半径(形成 donut)
|
|
76
|
+
- `radiusGap`:层间间隔
|
|
77
|
+
- ✅ **图例位置**:top / bottom / left / right
|
|
78
|
+
- ✅ **长文本处理**:
|
|
79
|
+
- 图例文本支持 `legendLabelMinWidth` 确保对齐
|
|
80
|
+
- 可配置最小宽度
|
|
81
|
+
- ✅ **空数据处理**:
|
|
82
|
+
- 全为 0 时显示 empty state
|
|
83
|
+
- 可自定义 `emptyText`
|
|
84
|
+
|
|
85
|
+
### 6. 跨端支持 ✅
|
|
86
|
+
|
|
87
|
+
- ✅ **Web 端(Recharts)**:完整功能实现
|
|
88
|
+
- ✅ **React Native 端**:降级方案(提示仅 Web 端可用)
|
|
89
|
+
- ✅ **条件渲染**:根据平台自动选择渲染策略
|
|
90
|
+
|
|
91
|
+
### 7. API 设计 ✅
|
|
92
|
+
|
|
93
|
+
- ✅ **数据格式**:
|
|
94
|
+
```typescript
|
|
95
|
+
// 树形(推荐)
|
|
96
|
+
data: NestedPieChartNode[]
|
|
97
|
+
|
|
98
|
+
// 扁平
|
|
99
|
+
data: NestedPieChartFlatData[]
|
|
100
|
+
```
|
|
101
|
+
- ✅ **配置选项**:30+ 个 props,覆盖所有场景
|
|
102
|
+
- ✅ **类型安全**:完整的 TypeScript 类型定义
|
|
103
|
+
- ✅ **格式化函数**:
|
|
104
|
+
- `formatValue`:数值格式化(默认欧元)
|
|
105
|
+
- `formatPercent`:百分比格式化
|
|
106
|
+
|
|
107
|
+
## 📁 文件结构
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
components/PieChart/
|
|
111
|
+
├── PieChart.tsx # 原有基础饼图
|
|
112
|
+
├── NestedPieChart.tsx # 新增:多层级饼图主组件
|
|
113
|
+
├── NestedPieChart.types.ts # 新增:类型定义
|
|
114
|
+
├── NestedPieChart.utils.ts # 新增:工具函数
|
|
115
|
+
├── GroupedLegend.tsx # 新增:分组图例组件
|
|
116
|
+
├── NestedPieChart.README.md # 新增:详细文档
|
|
117
|
+
├── index.tsx # 更新:导出
|
|
118
|
+
├── __tests__/
|
|
119
|
+
│ ├── PieChart.test.tsx
|
|
120
|
+
│ └── NestedPieChart.test.tsx # 新增:测试用例
|
|
121
|
+
└── ...
|
|
122
|
+
|
|
123
|
+
Stories/NestedPieChart/
|
|
124
|
+
└── NestedPieChart.stories.tsx # 新增:20+ 个 Story 示例
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## 🎨 核心技术亮点
|
|
128
|
+
|
|
129
|
+
### 1. 颜色生成算法
|
|
130
|
+
|
|
131
|
+
- **RGB ↔ HSL 转换**:精确的色彩空间转换
|
|
132
|
+
- **渐变生成策略**:
|
|
133
|
+
- 固定色相(H)保持色系一致
|
|
134
|
+
- 动态调整饱和度(S)和明度(L)
|
|
135
|
+
- 反向饱和度梯度(越深越饱和)
|
|
136
|
+
- 限制明度范围(30-85)避免极端颜色
|
|
137
|
+
|
|
138
|
+
### 2. 数据处理流程
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
Raw Data → Validate & Convert → Clean (去重/过滤0) →
|
|
142
|
+
Generate Colors → Process (计算%/路径) → Build Legend → Render
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 3. 交互状态管理
|
|
146
|
+
|
|
147
|
+
- **Hover 状态**:`hoveredKey` 追踪当前高亮项
|
|
148
|
+
- **隐藏状态**:`hiddenKeys` Set 管理筛选状态
|
|
149
|
+
- **展开状态**:图例组件内部管理折叠/展开
|
|
150
|
+
|
|
151
|
+
## 📖 使用示例
|
|
152
|
+
|
|
153
|
+
### 基础用法
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
import { NestedPieChart } from '@beppla/tapas-ui';
|
|
157
|
+
|
|
158
|
+
const storeData = [
|
|
159
|
+
{
|
|
160
|
+
key: 'cash',
|
|
161
|
+
label: 'Cash',
|
|
162
|
+
children: [
|
|
163
|
+
{ key: 'cash-19', label: '19% VAT', value: 1250.50 },
|
|
164
|
+
{ key: 'cash-7', label: '7% VAT', value: 680.30 },
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
// ...
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
<NestedPieChart
|
|
171
|
+
data={storeData}
|
|
172
|
+
height={450}
|
|
173
|
+
colorStrategy={{
|
|
174
|
+
baseColors: {
|
|
175
|
+
'cash': '#00B894',
|
|
176
|
+
'credit-card': '#FF6B00',
|
|
177
|
+
},
|
|
178
|
+
autoGenerate: true,
|
|
179
|
+
}}
|
|
180
|
+
formatValue={(value) => `€${value.toFixed(2)}`}
|
|
181
|
+
/>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### 高级用法
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
<NestedPieChart
|
|
188
|
+
data={storeData}
|
|
189
|
+
height={500}
|
|
190
|
+
outerRadius={160}
|
|
191
|
+
innerRadius={80}
|
|
192
|
+
radiusGap={20}
|
|
193
|
+
legendPosition="right"
|
|
194
|
+
legendCollapsible={true}
|
|
195
|
+
legendDefaultExpanded={false}
|
|
196
|
+
legendSortStrategy="value-desc"
|
|
197
|
+
enableLegendFilter={true}
|
|
198
|
+
centerText="€14,768"
|
|
199
|
+
onSliceClick={(event) => {
|
|
200
|
+
console.log('Clicked:', event.label, event.value);
|
|
201
|
+
}}
|
|
202
|
+
customTooltip={(event) => (
|
|
203
|
+
<div>
|
|
204
|
+
<strong>{event.label}</strong>
|
|
205
|
+
<div>€{event.value.toFixed(2)}</div>
|
|
206
|
+
</div>
|
|
207
|
+
)}
|
|
208
|
+
/>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## 🧪 测试覆盖
|
|
212
|
+
|
|
213
|
+
- ✅ 基础渲染测试
|
|
214
|
+
- ✅ 空数据处理测试
|
|
215
|
+
- ✅ 数据格式转换测试
|
|
216
|
+
- ✅ Props 配置测试
|
|
217
|
+
- ✅ 20+ 个 Storybook 示例(可视化测试)
|
|
218
|
+
|
|
219
|
+
## 📊 Storybook 示例
|
|
220
|
+
|
|
221
|
+
已创建 20+ 个 Story 示例,覆盖:
|
|
222
|
+
|
|
223
|
+
1. **Default** - 默认配置
|
|
224
|
+
2. **CustomSizeAndRadius** - 自定义尺寸
|
|
225
|
+
3. **LegendLeft/Top/Bottom** - 图例位置
|
|
226
|
+
4. **LegendCollapsed/NotCollapsible** - 折叠状态
|
|
227
|
+
5. **HidePercent/HideValues** - 显示配置
|
|
228
|
+
6. **SortByValue** - 排序策略
|
|
229
|
+
7. **WithCenterText** - 中心文本
|
|
230
|
+
8. **CustomFormatters** - 自定义格式化
|
|
231
|
+
9. **WithInteractions** - 交互示例
|
|
232
|
+
10. **WithLegendFilter** - 筛选功能
|
|
233
|
+
11. **CustomTooltip** - 自定义 Tooltip
|
|
234
|
+
12. **SmallDataset/LargeDataset** - 不同数据规模
|
|
235
|
+
13. **EmptyData** - 空数据
|
|
236
|
+
14. **SingleLevel** - 单层数据
|
|
237
|
+
15. **ColorStressTest** - 配色压力测试
|
|
238
|
+
|
|
239
|
+
## 🚀 性能优化
|
|
240
|
+
|
|
241
|
+
1. **useMemo**:数据处理结果缓存
|
|
242
|
+
2. **useCallback**:事件处理函数缓存
|
|
243
|
+
3. **条件渲染**:空数据早期返回
|
|
244
|
+
4. **自动过滤**:0 值在数据处理阶段过滤
|
|
245
|
+
5. **渐进式加载**:大数据集默认折叠
|
|
246
|
+
|
|
247
|
+
## 🔄 后续扩展建议
|
|
248
|
+
|
|
249
|
+
### 短期(可选)
|
|
250
|
+
|
|
251
|
+
1. **N 层支持**:递归渲染支持 3+ 层(当前架构已支持,只需调整 Recharts Pie 层级)
|
|
252
|
+
2. **动画效果**:进入/退出动画
|
|
253
|
+
3. **导出功能**:导出为图片/PDF
|
|
254
|
+
|
|
255
|
+
### 中期(根据需求)
|
|
256
|
+
|
|
257
|
+
1. **RN 端完整实现**:使用 react-native-svg 实现双层饼图
|
|
258
|
+
2. **主题支持**:集成 tapas-ui 主题系统
|
|
259
|
+
3. **国际化**:多语言支持
|
|
260
|
+
|
|
261
|
+
### 长期(高级功能)
|
|
262
|
+
|
|
263
|
+
1. **数据钻取**:点击扇区展开下一层级
|
|
264
|
+
2. **实时更新**:数据流式更新动画
|
|
265
|
+
3. **对比模式**:多个饼图并排对比
|
|
266
|
+
|
|
267
|
+
## 📝 注意事项
|
|
268
|
+
|
|
269
|
+
1. **唯一 key**:每个节点必须有唯一 key
|
|
270
|
+
2. **数据要求**:叶子节点必须有 value
|
|
271
|
+
3. **性能建议**:总节点数建议 < 100
|
|
272
|
+
4. **Web Only**:完整功能目前仅 Web 端(RN 降级)
|
|
273
|
+
5. **依赖要求**:需要 recharts(Web)
|
|
274
|
+
|
|
275
|
+
## 🎯 业务场景适配
|
|
276
|
+
|
|
277
|
+
### Store Insights / Summary
|
|
278
|
+
|
|
279
|
+
完美支持:
|
|
280
|
+
- ✅ 支付方式 → 税率分析
|
|
281
|
+
- ✅ 分组小计 + 明细展示
|
|
282
|
+
- ✅ 欧元格式化
|
|
283
|
+
- ✅ 可折叠图例(数据多时)
|
|
284
|
+
- ✅ 点击筛选分析
|
|
285
|
+
|
|
286
|
+
### 其他场景
|
|
287
|
+
|
|
288
|
+
- 产品类别 → 子类别销售
|
|
289
|
+
- 地区 → 城市业绩分析
|
|
290
|
+
- 部门 → 团队支出统计
|
|
291
|
+
- 任意两层级分类数据可视化
|
|
292
|
+
|
|
293
|
+
## ✨ 总结
|
|
294
|
+
|
|
295
|
+
NestedPieChart 是一个**功能完整、类型安全、高度可配置**的多层级饼图组件,完全满足需求列表的所有要求:
|
|
296
|
+
|
|
297
|
+
- ✅ 多层级嵌套
|
|
298
|
+
- ✅ 分组图例(折叠/排序/筛选)
|
|
299
|
+
- ✅ 智能配色(同色系渐变)
|
|
300
|
+
- ✅ 完整交互(高亮/点击/Tooltip)
|
|
301
|
+
- ✅ 数据处理(去重/合并/过滤)
|
|
302
|
+
- ✅ 响应式布局
|
|
303
|
+
- ✅ 跨端支持
|
|
304
|
+
- ✅ 详细文档和示例
|
|
305
|
+
|
|
306
|
+
**可直接用于生产环境!** 🎉
|