@publishfx/publish-chart 1.3.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 +143 -0
- package/dist/adapters/DataAdapter.d.ts +33 -0
- package/dist/adapters/DataAdapter.js +75 -0
- package/dist/adapters/TypeAdapter.d.ts +40 -0
- package/dist/adapters/TypeAdapter.js +26 -0
- package/dist/components/base/BarChart.d.ts +7 -0
- package/dist/components/base/BarChart.js +362 -0
- package/dist/components/base/BarChart.lazy.d.ts +5 -0
- package/dist/components/base/BarChart.lazy.js +2 -0
- package/dist/components/base/LineChart.d.ts +7 -0
- package/dist/components/base/LineChart.js +321 -0
- package/dist/components/base/LineChart.lazy.d.ts +5 -0
- package/dist/components/base/LineChart.lazy.js +2 -0
- package/dist/components/composite/BarLineAdapter.d.ts +22 -0
- package/dist/components/composite/BarLineAdapter.js +61 -0
- package/dist/components/composite/BarLineAdapter.lazy.d.ts +5 -0
- package/dist/components/composite/BarLineAdapter.lazy.js +2 -0
- package/dist/components/composite/BarLineChart.d.ts +7 -0
- package/dist/components/composite/BarLineChart.js +255 -0
- package/dist/components/composite/BarLineChart.lazy.d.ts +5 -0
- package/dist/components/composite/BarLineChart.lazy.js +2 -0
- package/dist/components/composite/BarLineCompareWeekend.d.ts +10 -0
- package/dist/components/composite/BarLineCompareWeekend.js +502 -0
- package/dist/components/composite/GroupBarLine.d.ts +11 -0
- package/dist/components/composite/GroupBarLine.js +546 -0
- package/dist/components/composite/GroupBarLine.lazy.d.ts +5 -0
- package/dist/components/composite/GroupBarLine.lazy.js +2 -0
- package/dist/components/composite/GroupCompare.d.ts +10 -0
- package/dist/components/composite/GroupCompare.js +620 -0
- package/dist/components/shared/AuxiliaryLine.d.ts +8 -0
- package/dist/components/shared/AuxiliaryLine.js +64 -0
- package/dist/components/shared/NodeDetail.d.ts +9 -0
- package/dist/components/shared/NodeDetail.js +110 -0
- package/dist/components/shared/NodeGeom.d.ts +23 -0
- package/dist/components/shared/NodeGeom.js +35 -0
- package/dist/components/shared/NodePopover.d.ts +22 -0
- package/dist/components/shared/NodePopover.js +41 -0
- package/dist/components/shared/NodePopoverContent.d.ts +15 -0
- package/dist/components/shared/NodePopoverContent.js +85 -0
- package/dist/components/shared/XAxisBackground.d.ts +31 -0
- package/dist/components/shared/XAxisBackground.js +93 -0
- package/dist/core/ChartConfig.d.ts +48 -0
- package/dist/core/ChartConfig.js +152 -0
- package/dist/core/ChartContext.d.ts +49 -0
- package/dist/core/ChartContext.js +31 -0
- package/dist/core/ChartTypes.d.ts +119 -0
- package/dist/core/ChartTypes.js +0 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +21 -0
- package/dist/services/DataTransformService.d.ts +22 -0
- package/dist/services/DataTransformService.js +29 -0
- package/dist/services/FormatterService.d.ts +24 -0
- package/dist/services/FormatterService.js +22 -0
- package/dist/utils/__tests__/formatters.test.d.ts +1 -0
- package/dist/utils/__tests__/formatters.test.js +333 -0
- package/dist/utils/chartHelpers.d.ts +52 -0
- package/dist/utils/chartHelpers.js +112 -0
- package/dist/utils/dataTransform.d.ts +12 -0
- package/dist/utils/dataTransform.js +64 -0
- package/dist/utils/formatters.d.ts +37 -0
- package/dist/utils/formatters.js +127 -0
- package/dist/utils/indicatorHelpers.d.ts +16 -0
- package/dist/utils/indicatorHelpers.js +15 -0
- package/dist/utils/lazyHelpers.d.ts +29 -0
- package/dist/utils/lazyHelpers.js +15 -0
- package/package.json +68 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { BigNumberUtil, IndicatorUnit, formatBigNumber, formatIndicatorV2, formatNumberV2, formatPercentage } from "../formatters.js";
|
|
2
|
+
describe('formatters', ()=>{
|
|
3
|
+
describe('formatNumberV2', ()=>{
|
|
4
|
+
it('应该格式化基本数字', ()=>{
|
|
5
|
+
expect(formatNumberV2(1234.56)).toBe('1,234.56');
|
|
6
|
+
expect(formatNumberV2(1000)).toBe('1,000.00');
|
|
7
|
+
expect(formatNumberV2(0)).toBe('0.00');
|
|
8
|
+
});
|
|
9
|
+
it('应该处理负数', ()=>{
|
|
10
|
+
expect(formatNumberV2(-1234.56)).toBe('-1,234.56');
|
|
11
|
+
expect(formatNumberV2(-1000)).toBe('-1,000.00');
|
|
12
|
+
});
|
|
13
|
+
it('应该支持自定义小数位数', ()=>{
|
|
14
|
+
expect(formatNumberV2(1234.5678, {
|
|
15
|
+
fractionDigits: 0
|
|
16
|
+
})).toBe('1,235');
|
|
17
|
+
expect(formatNumberV2(1234.5678, {
|
|
18
|
+
fractionDigits: 1
|
|
19
|
+
})).toBe('1,234.6');
|
|
20
|
+
expect(formatNumberV2(1234.5678, {
|
|
21
|
+
fractionDigits: 4
|
|
22
|
+
})).toBe('1,234.5678');
|
|
23
|
+
});
|
|
24
|
+
it('应该支持前缀和后缀', ()=>{
|
|
25
|
+
expect(formatNumberV2(1000, {
|
|
26
|
+
prefix: '$'
|
|
27
|
+
})).toBe('$1,000.00');
|
|
28
|
+
expect(formatNumberV2(1000, {
|
|
29
|
+
suffix: '%'
|
|
30
|
+
})).toBe('1,000.00%');
|
|
31
|
+
expect(formatNumberV2(1000, {
|
|
32
|
+
prefix: '¥',
|
|
33
|
+
suffix: '元'
|
|
34
|
+
})).toBe('¥1,000.00元');
|
|
35
|
+
});
|
|
36
|
+
it('应该支持长格式(k/m/b)', ()=>{
|
|
37
|
+
expect(formatNumberV2(1000, {
|
|
38
|
+
longFormat: true
|
|
39
|
+
})).toBe('1.00k');
|
|
40
|
+
expect(formatNumberV2(1000000, {
|
|
41
|
+
longFormat: true
|
|
42
|
+
})).toBe('1.00m');
|
|
43
|
+
expect(formatNumberV2(1000000000, {
|
|
44
|
+
longFormat: true
|
|
45
|
+
})).toBe('1.00b');
|
|
46
|
+
});
|
|
47
|
+
it('应该处理空值和无效值', ()=>{
|
|
48
|
+
expect(formatNumberV2(void 0)).toBe('-');
|
|
49
|
+
expect(formatNumberV2(null)).toBe('-');
|
|
50
|
+
expect(formatNumberV2('')).toBe('-');
|
|
51
|
+
expect(formatNumberV2('-')).toBe('-');
|
|
52
|
+
expect(formatNumberV2(NaN)).toBe('-');
|
|
53
|
+
});
|
|
54
|
+
it('应该支持自定义空值占位符', ()=>{
|
|
55
|
+
expect(formatNumberV2(void 0, void 0, 'N/A')).toBe('N/A');
|
|
56
|
+
expect(formatNumberV2(null, void 0, '0')).toBe('0');
|
|
57
|
+
});
|
|
58
|
+
it('应该处理字符串数字', ()=>{
|
|
59
|
+
expect(formatNumberV2('1234.56')).toBe('1,234.56');
|
|
60
|
+
expect(formatNumberV2('1000')).toBe('1,000.00');
|
|
61
|
+
expect(formatNumberV2('-1234.56')).toBe('-1,234.56');
|
|
62
|
+
});
|
|
63
|
+
it('应该处理大数字', ()=>{
|
|
64
|
+
expect(formatNumberV2(1234567890.12)).toBe('1,234,567,890.12');
|
|
65
|
+
expect(formatNumberV2(999999999.99)).toBe('999,999,999.99');
|
|
66
|
+
});
|
|
67
|
+
it('应该处理小数', ()=>{
|
|
68
|
+
expect(formatNumberV2(0.123)).toBe('0.12');
|
|
69
|
+
expect(formatNumberV2(0.001)).toBe('0.00');
|
|
70
|
+
expect(formatNumberV2(0.001, {
|
|
71
|
+
fractionDigits: 3
|
|
72
|
+
})).toBe('0.001');
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
describe('formatPercentage', ()=>{
|
|
76
|
+
it('应该格式化百分比', ()=>{
|
|
77
|
+
expect(formatPercentage(0.1234)).toBe('0.12%');
|
|
78
|
+
expect(formatPercentage(1)).toBe('1.00%');
|
|
79
|
+
expect(formatPercentage(100)).toBe('100.00%');
|
|
80
|
+
});
|
|
81
|
+
it('应该处理负数百分比', ()=>{
|
|
82
|
+
expect(formatPercentage(-0.1234)).toBe('-0.12%');
|
|
83
|
+
expect(formatPercentage(-1)).toBe('-1.00%');
|
|
84
|
+
});
|
|
85
|
+
it('应该处理空值和无效值', ()=>{
|
|
86
|
+
expect(formatPercentage(void 0)).toBe('-');
|
|
87
|
+
expect(formatPercentage(null)).toBe('-');
|
|
88
|
+
expect(formatPercentage('')).toBe('-');
|
|
89
|
+
expect(formatPercentage(NaN)).toBe('-');
|
|
90
|
+
});
|
|
91
|
+
it('应该支持自定义空值占位符', ()=>{
|
|
92
|
+
expect(formatPercentage(void 0, 'N/A')).toBe('N/A');
|
|
93
|
+
expect(formatPercentage(null, '0%')).toBe('0%');
|
|
94
|
+
});
|
|
95
|
+
it('应该处理字符串数字', ()=>{
|
|
96
|
+
expect(formatPercentage('0.1234')).toBe('0.12%');
|
|
97
|
+
expect(formatPercentage('1')).toBe('1.00%');
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
describe('formatBigNumber', ()=>{
|
|
101
|
+
const createIndicatorInfo = (indicatorID, indicatorFormat)=>({
|
|
102
|
+
indicatorID,
|
|
103
|
+
indicatorFormat
|
|
104
|
+
});
|
|
105
|
+
it('应该处理空值和无效值', ()=>{
|
|
106
|
+
expect(formatBigNumber('')).toBe('-');
|
|
107
|
+
expect(formatBigNumber('-')).toBe('-');
|
|
108
|
+
expect(formatBigNumber(void 0)).toBe('-');
|
|
109
|
+
expect(formatBigNumber(null)).toBe('0.00');
|
|
110
|
+
});
|
|
111
|
+
it('应该格式化普通数字(无指标信息)', ()=>{
|
|
112
|
+
expect(formatBigNumber(1234.56)).toBe('1,234.56');
|
|
113
|
+
expect(formatBigNumber(1000)).toBe('1,000.00');
|
|
114
|
+
});
|
|
115
|
+
it('应该格式化百分比类型', ()=>{
|
|
116
|
+
const indicatorInfo = createIndicatorInfo(1, {
|
|
117
|
+
indicatorUnit: IndicatorUnit.Percentage,
|
|
118
|
+
indicatorDecimal: 2
|
|
119
|
+
});
|
|
120
|
+
expect(formatBigNumber(0.1234, indicatorInfo)).toBe('12.34%');
|
|
121
|
+
expect(formatBigNumber(0.5, indicatorInfo)).toBe('50.00%');
|
|
122
|
+
});
|
|
123
|
+
it('应该格式化美元类型', ()=>{
|
|
124
|
+
const indicatorInfo = createIndicatorInfo(1, {
|
|
125
|
+
indicatorUnit: IndicatorUnit.Dollar,
|
|
126
|
+
indicatorDecimal: 2
|
|
127
|
+
});
|
|
128
|
+
expect(formatBigNumber(1234.56, indicatorInfo)).toBe('$1,234.56');
|
|
129
|
+
expect(formatBigNumber(1000, indicatorInfo)).toBe('$1,000.00');
|
|
130
|
+
});
|
|
131
|
+
it('应该格式化人民币类型', ()=>{
|
|
132
|
+
const indicatorInfo = createIndicatorInfo(1, {
|
|
133
|
+
indicatorUnit: IndicatorUnit.RMB,
|
|
134
|
+
indicatorDecimal: 2
|
|
135
|
+
});
|
|
136
|
+
expect(formatBigNumber(1234.56, indicatorInfo)).toBe('¥1,234.56');
|
|
137
|
+
expect(formatBigNumber(1000, indicatorInfo)).toBe('¥1,000.00');
|
|
138
|
+
});
|
|
139
|
+
it('应该自动转换大数字单位(k/m/b)', ()=>{
|
|
140
|
+
const indicatorInfo = createIndicatorInfo(1, {
|
|
141
|
+
indicatorUnit: IndicatorUnit.Number,
|
|
142
|
+
indicatorDecimal: 2
|
|
143
|
+
});
|
|
144
|
+
expect(formatBigNumber(10000, indicatorInfo)).toBe('10.00k');
|
|
145
|
+
expect(formatBigNumber(1000000, indicatorInfo)).toBe('1.00m');
|
|
146
|
+
expect(formatBigNumber(1000000000, indicatorInfo)).toBe('1.00b');
|
|
147
|
+
});
|
|
148
|
+
it('应该支持长格式', ()=>{
|
|
149
|
+
const indicatorInfo = createIndicatorInfo(1, {
|
|
150
|
+
indicatorUnit: IndicatorUnit.Number,
|
|
151
|
+
indicatorDecimal: 2
|
|
152
|
+
});
|
|
153
|
+
expect(formatBigNumber(10000, indicatorInfo, void 0, true)).toBe('10.00k');
|
|
154
|
+
expect(formatBigNumber(1000000, indicatorInfo, void 0, true)).toBe('1.00m');
|
|
155
|
+
});
|
|
156
|
+
it('应该支持自定义小数位数', ()=>{
|
|
157
|
+
const indicatorInfo = createIndicatorInfo(1, {
|
|
158
|
+
indicatorUnit: IndicatorUnit.Number,
|
|
159
|
+
indicatorDecimal: 4
|
|
160
|
+
});
|
|
161
|
+
expect(formatBigNumber(1234.5678, indicatorInfo)).toBe('1,234.5678');
|
|
162
|
+
});
|
|
163
|
+
it('应该处理 NaN 值', ()=>{
|
|
164
|
+
const indicatorInfo = createIndicatorInfo(1, {
|
|
165
|
+
indicatorUnit: IndicatorUnit.Number,
|
|
166
|
+
indicatorDecimal: 2
|
|
167
|
+
});
|
|
168
|
+
expect(formatBigNumber('invalid', indicatorInfo)).toBe('-');
|
|
169
|
+
expect(formatBigNumber(NaN, indicatorInfo)).toBe('-');
|
|
170
|
+
});
|
|
171
|
+
it('应该使用默认格式(当没有提供指标格式时)', ()=>{
|
|
172
|
+
const indicatorInfo = createIndicatorInfo(1);
|
|
173
|
+
expect(formatBigNumber(1234.56, indicatorInfo)).toBe('1,234.56');
|
|
174
|
+
});
|
|
175
|
+
it('应该处理边界值', ()=>{
|
|
176
|
+
const indicatorInfo = createIndicatorInfo(1, {
|
|
177
|
+
indicatorUnit: IndicatorUnit.Number,
|
|
178
|
+
indicatorDecimal: 2
|
|
179
|
+
});
|
|
180
|
+
expect(formatBigNumber(9999, indicatorInfo)).toBe('9,999.00');
|
|
181
|
+
expect(formatBigNumber(10000, indicatorInfo)).toBe('10.00k');
|
|
182
|
+
expect(formatBigNumber(999999, indicatorInfo)).toBe('1,000.00k');
|
|
183
|
+
expect(formatBigNumber(1000000, indicatorInfo)).toBe('1.00m');
|
|
184
|
+
expect(formatBigNumber(999999999, indicatorInfo)).toBe('1,000.00m');
|
|
185
|
+
expect(formatBigNumber(1000000000, indicatorInfo)).toBe('1.00b');
|
|
186
|
+
});
|
|
187
|
+
it('应该支持自定义空值占位符', ()=>{
|
|
188
|
+
const indicatorInfo = createIndicatorInfo(1, {
|
|
189
|
+
indicatorUnit: IndicatorUnit.Number,
|
|
190
|
+
indicatorDecimal: 2
|
|
191
|
+
});
|
|
192
|
+
expect(formatBigNumber(void 0, indicatorInfo, 'N/A')).toBe('N/A');
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
describe('formatIndicatorV2', ()=>{
|
|
196
|
+
const createIndicatorInfo = (indicatorID, indicatorFormat)=>({
|
|
197
|
+
indicatorID,
|
|
198
|
+
indicatorFormat
|
|
199
|
+
});
|
|
200
|
+
it('应该处理空值和无效值', ()=>{
|
|
201
|
+
expect(formatIndicatorV2('')).toBe('-');
|
|
202
|
+
expect(formatIndicatorV2('-')).toBe('-');
|
|
203
|
+
expect(formatIndicatorV2(void 0)).toBe('-');
|
|
204
|
+
expect(formatIndicatorV2(null)).toBe('-');
|
|
205
|
+
});
|
|
206
|
+
it('应该格式化普通数字(无指标信息)', ()=>{
|
|
207
|
+
expect(formatIndicatorV2(1234.56)).toBe('1,234.56');
|
|
208
|
+
expect(formatIndicatorV2(1000)).toBe('1,000.00');
|
|
209
|
+
});
|
|
210
|
+
it('应该格式化百分比类型', ()=>{
|
|
211
|
+
const indicatorInfo = createIndicatorInfo(1, {
|
|
212
|
+
indicatorUnit: IndicatorUnit.Percentage,
|
|
213
|
+
indicatorDecimal: 2
|
|
214
|
+
});
|
|
215
|
+
expect(formatIndicatorV2(0.1234, indicatorInfo)).toBe('12.34%');
|
|
216
|
+
expect(formatIndicatorV2(0.5, indicatorInfo)).toBe('50.00%');
|
|
217
|
+
});
|
|
218
|
+
it('应该格式化美元类型', ()=>{
|
|
219
|
+
const indicatorInfo = createIndicatorInfo(1, {
|
|
220
|
+
indicatorUnit: IndicatorUnit.Dollar,
|
|
221
|
+
indicatorDecimal: 2
|
|
222
|
+
});
|
|
223
|
+
expect(formatIndicatorV2(1234.56, indicatorInfo)).toBe('$1,234.56');
|
|
224
|
+
expect(formatIndicatorV2(1000, indicatorInfo)).toBe('$1,000.00');
|
|
225
|
+
});
|
|
226
|
+
it('应该格式化人民币类型', ()=>{
|
|
227
|
+
const indicatorInfo = createIndicatorInfo(1, {
|
|
228
|
+
indicatorUnit: IndicatorUnit.RMB,
|
|
229
|
+
indicatorDecimal: 2
|
|
230
|
+
});
|
|
231
|
+
expect(formatIndicatorV2(1234.56, indicatorInfo)).toBe('¥1,234.56');
|
|
232
|
+
expect(formatIndicatorV2(1000, indicatorInfo)).toBe('¥1,000.00');
|
|
233
|
+
});
|
|
234
|
+
it('应该支持自定义小数位数', ()=>{
|
|
235
|
+
const indicatorInfo = createIndicatorInfo(1, {
|
|
236
|
+
indicatorUnit: IndicatorUnit.Number,
|
|
237
|
+
indicatorDecimal: 4
|
|
238
|
+
});
|
|
239
|
+
expect(formatIndicatorV2(1234.5678, indicatorInfo)).toBe('1,234.5678');
|
|
240
|
+
});
|
|
241
|
+
it('应该支持长格式', ()=>{
|
|
242
|
+
const indicatorInfo = createIndicatorInfo(1, {
|
|
243
|
+
indicatorUnit: IndicatorUnit.Number,
|
|
244
|
+
indicatorDecimal: 2
|
|
245
|
+
});
|
|
246
|
+
expect(formatIndicatorV2(1000, indicatorInfo, void 0, true)).toBe('1.00k');
|
|
247
|
+
});
|
|
248
|
+
it('应该使用默认格式(当没有提供指标格式时)', ()=>{
|
|
249
|
+
const indicatorInfo = createIndicatorInfo(1);
|
|
250
|
+
expect(formatIndicatorV2(1234.56, indicatorInfo)).toBe('1,234.56');
|
|
251
|
+
});
|
|
252
|
+
it('应该支持自定义空值占位符', ()=>{
|
|
253
|
+
const indicatorInfo = createIndicatorInfo(1, {
|
|
254
|
+
indicatorUnit: IndicatorUnit.Number,
|
|
255
|
+
indicatorDecimal: 2
|
|
256
|
+
});
|
|
257
|
+
expect(formatIndicatorV2(void 0, indicatorInfo, 'N/A')).toBe('N/A');
|
|
258
|
+
});
|
|
259
|
+
it('应该处理字符串数字', ()=>{
|
|
260
|
+
const indicatorInfo = createIndicatorInfo(1, {
|
|
261
|
+
indicatorUnit: IndicatorUnit.Number,
|
|
262
|
+
indicatorDecimal: 2
|
|
263
|
+
});
|
|
264
|
+
expect(formatIndicatorV2('1234.56', indicatorInfo)).toBe('1,234.56');
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
describe('BigNumberUtil', ()=>{
|
|
268
|
+
describe('from', ()=>{
|
|
269
|
+
it('应该从数字创建 BigNumber 实例', ()=>{
|
|
270
|
+
const bn = BigNumberUtil.from(123.45);
|
|
271
|
+
expect(bn.toString()).toBe('123.45');
|
|
272
|
+
});
|
|
273
|
+
it('应该从字符串创建 BigNumber 实例', ()=>{
|
|
274
|
+
const bn = BigNumberUtil.from('123.45');
|
|
275
|
+
expect(bn.toString()).toBe('123.45');
|
|
276
|
+
});
|
|
277
|
+
it('应该处理空值,默认为 0', ()=>{
|
|
278
|
+
const bn1 = BigNumberUtil.from(null);
|
|
279
|
+
expect(bn1.toString()).toBe('0');
|
|
280
|
+
const bn2 = BigNumberUtil.from(void 0);
|
|
281
|
+
expect(bn2.toString()).toBe('0');
|
|
282
|
+
const bn3 = BigNumberUtil.from('');
|
|
283
|
+
expect(bn3.toString()).toBe('0');
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
describe('lt', ()=>{
|
|
287
|
+
it('应该正确比较小于关系', ()=>{
|
|
288
|
+
expect(BigNumberUtil.lt(1, 2)).toBe(true);
|
|
289
|
+
expect(BigNumberUtil.lt(2, 1)).toBe(false);
|
|
290
|
+
expect(BigNumberUtil.lt(1, 1)).toBe(false);
|
|
291
|
+
});
|
|
292
|
+
it('应该处理小数比较', ()=>{
|
|
293
|
+
expect(BigNumberUtil.lt(1.1, 1.2)).toBe(true);
|
|
294
|
+
expect(BigNumberUtil.lt(1.2, 1.1)).toBe(false);
|
|
295
|
+
});
|
|
296
|
+
it('应该处理字符串数字', ()=>{
|
|
297
|
+
expect(BigNumberUtil.lt('1', '2')).toBe(true);
|
|
298
|
+
expect(BigNumberUtil.lt('2', '1')).toBe(false);
|
|
299
|
+
});
|
|
300
|
+
it('应该处理大数字', ()=>{
|
|
301
|
+
expect(BigNumberUtil.lt(999999, 1000000)).toBe(true);
|
|
302
|
+
expect(BigNumberUtil.lt(1000000, 999999)).toBe(false);
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
describe('gt', ()=>{
|
|
306
|
+
it('应该正确比较大于关系', ()=>{
|
|
307
|
+
expect(BigNumberUtil.gt(2, 1)).toBe(true);
|
|
308
|
+
expect(BigNumberUtil.gt(1, 2)).toBe(false);
|
|
309
|
+
expect(BigNumberUtil.gt(1, 1)).toBe(false);
|
|
310
|
+
});
|
|
311
|
+
it('应该处理小数比较', ()=>{
|
|
312
|
+
expect(BigNumberUtil.gt(1.2, 1.1)).toBe(true);
|
|
313
|
+
expect(BigNumberUtil.gt(1.1, 1.2)).toBe(false);
|
|
314
|
+
});
|
|
315
|
+
it('应该处理字符串数字', ()=>{
|
|
316
|
+
expect(BigNumberUtil.gt('2', '1')).toBe(true);
|
|
317
|
+
expect(BigNumberUtil.gt('1', '2')).toBe(false);
|
|
318
|
+
});
|
|
319
|
+
it('应该处理大数字', ()=>{
|
|
320
|
+
expect(BigNumberUtil.gt(1000000, 999999)).toBe(true);
|
|
321
|
+
expect(BigNumberUtil.gt(999999, 1000000)).toBe(false);
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
describe('IndicatorUnit 枚举', ()=>{
|
|
326
|
+
it('应该包含正确的枚举值', ()=>{
|
|
327
|
+
expect(IndicatorUnit.Number).toBe(1);
|
|
328
|
+
expect(IndicatorUnit.Percentage).toBe(2);
|
|
329
|
+
expect(IndicatorUnit.Dollar).toBe(3);
|
|
330
|
+
expect(IndicatorUnit.RMB).toBe(4);
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 图表工具函数
|
|
3
|
+
*/
|
|
4
|
+
import type { G2 } from 'bizcharts';
|
|
5
|
+
import type { IndicatorInfo } from '../core/ChartTypes';
|
|
6
|
+
/**
|
|
7
|
+
* 计算文本宽度
|
|
8
|
+
*/
|
|
9
|
+
export declare const calTextWidth: (text: string, fontSize: number) => number;
|
|
10
|
+
/**
|
|
11
|
+
* 计算柱状图的估算宽度
|
|
12
|
+
*/
|
|
13
|
+
export declare const calculateBarWidth: (chart: G2.Chart, data: any[], xField: string, widthRatio?: number) => number | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* 根据y轴文本宽度计算图表水平方向padding
|
|
16
|
+
*/
|
|
17
|
+
export declare const getAxisHorPaddingByText: (value: number | string, indicatorMap: Record<string, IndicatorInfo>, y: string | number) => number;
|
|
18
|
+
/**
|
|
19
|
+
* 通用处理轴信息格式化
|
|
20
|
+
*/
|
|
21
|
+
export declare const getAxisFormat: (val: string | number, indicatorMap: Record<string, IndicatorInfo>, y: string | number) => string;
|
|
22
|
+
/**
|
|
23
|
+
* 截断文本
|
|
24
|
+
*/
|
|
25
|
+
export declare const truncateText: (text: string, maxWidth: number) => string;
|
|
26
|
+
/**
|
|
27
|
+
* 标记样式(用于图例)
|
|
28
|
+
*/
|
|
29
|
+
export declare const markerStyle: (baseColor: string, isCompare: boolean) => {
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
display: string;
|
|
33
|
+
borderRadius: string;
|
|
34
|
+
marginRight: number;
|
|
35
|
+
backgroundColor: string;
|
|
36
|
+
backgroundImage: string;
|
|
37
|
+
backgroundSize: string;
|
|
38
|
+
} | {
|
|
39
|
+
width: number;
|
|
40
|
+
height: number;
|
|
41
|
+
display: string;
|
|
42
|
+
borderRadius: string;
|
|
43
|
+
marginRight: number;
|
|
44
|
+
backgroundColor: string;
|
|
45
|
+
backgroundImage?: undefined;
|
|
46
|
+
backgroundSize?: undefined;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* 创建斜纹图案 Canvas
|
|
50
|
+
*/
|
|
51
|
+
export declare const createStripePatternCanvas: (baseColor: string, spaceUnit?: number) => HTMLCanvasElement;
|
|
52
|
+
export declare const getChartKey: (config: any) => string;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { formatBigNumber } from "./formatters.js";
|
|
2
|
+
const calTextWidth = (text, fontSize)=>{
|
|
3
|
+
const canvas = document.createElement('canvas');
|
|
4
|
+
const context = canvas.getContext('2d');
|
|
5
|
+
if (!context) return 0;
|
|
6
|
+
context.font = `${fontSize}px PingFangSC-Regular, PingFangSC, 'Helvetica Neue', Arial, 'Lucida Grande', Verdana, 'Hiragino Sans GB', 'Microsoft YaHei', hei`;
|
|
7
|
+
return context.measureText(text).width;
|
|
8
|
+
};
|
|
9
|
+
const calculateBarWidth = (chart, data, xField, widthRatio = 0.5)=>{
|
|
10
|
+
try {
|
|
11
|
+
const coordinateWidth = chart.coordinateBBox?.width || chart.view?.coordinateBBox?.width || 0;
|
|
12
|
+
const uniqueXValues = new Set(data.map((item)=>item[xField]));
|
|
13
|
+
const xCount = uniqueXValues.size;
|
|
14
|
+
if (coordinateWidth > 0 && xCount > 0) {
|
|
15
|
+
const xValueWidth = coordinateWidth / xCount;
|
|
16
|
+
const estimatedBarWidth = xValueWidth * widthRatio;
|
|
17
|
+
return estimatedBarWidth;
|
|
18
|
+
}
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error('计算主图柱状图宽度失败', error);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const getAxisHorPaddingByText = (value, indicatorMap, y)=>7 * getAxisFormat(value, indicatorMap, y).length + 26;
|
|
24
|
+
const getAxisFormat = (val, indicatorMap, y)=>{
|
|
25
|
+
if ('-' === val || '' === val) return val;
|
|
26
|
+
const cur = Number(val) || 0;
|
|
27
|
+
const indicator = indicatorMap && indicatorMap[y];
|
|
28
|
+
return formatBigNumber(cur, indicator);
|
|
29
|
+
};
|
|
30
|
+
const truncateText = (text, maxWidth)=>{
|
|
31
|
+
if (!text) return text;
|
|
32
|
+
const span = document.createElement('span');
|
|
33
|
+
span.style.whiteSpace = 'nowrap';
|
|
34
|
+
span.style.visibility = 'hidden';
|
|
35
|
+
span.style.position = 'absolute';
|
|
36
|
+
span.innerText = text;
|
|
37
|
+
document.body.appendChild(span);
|
|
38
|
+
let result = text;
|
|
39
|
+
if (span.offsetWidth > maxWidth) {
|
|
40
|
+
let low = 0;
|
|
41
|
+
let high = text.length;
|
|
42
|
+
while(low <= high){
|
|
43
|
+
const mid = Math.floor((low + high) / 2);
|
|
44
|
+
span.innerText = text.slice(0, mid) + '...';
|
|
45
|
+
if (span.offsetWidth <= maxWidth) low = mid + 1;
|
|
46
|
+
else high = mid - 1;
|
|
47
|
+
}
|
|
48
|
+
result = text.slice(0, high) + '...';
|
|
49
|
+
}
|
|
50
|
+
document.body.removeChild(span);
|
|
51
|
+
return result;
|
|
52
|
+
};
|
|
53
|
+
const markerStyle = (baseColor, isCompare)=>isCompare ? {
|
|
54
|
+
width: 8,
|
|
55
|
+
height: 8,
|
|
56
|
+
display: 'inline-block',
|
|
57
|
+
borderRadius: '50%',
|
|
58
|
+
marginRight: 8,
|
|
59
|
+
backgroundColor: 'transparent',
|
|
60
|
+
backgroundImage: `repeating-linear-gradient(
|
|
61
|
+
45deg,
|
|
62
|
+
rgba(255,255,255,0.95) 0,
|
|
63
|
+
rgba(255,255,255,0.95) 1px,
|
|
64
|
+
transparent 1px,
|
|
65
|
+
transparent 3px
|
|
66
|
+
)`,
|
|
67
|
+
backgroundSize: '4px 4px'
|
|
68
|
+
} : {
|
|
69
|
+
width: 8,
|
|
70
|
+
height: 8,
|
|
71
|
+
display: 'inline-block',
|
|
72
|
+
borderRadius: '50%',
|
|
73
|
+
marginRight: 8,
|
|
74
|
+
backgroundColor: baseColor
|
|
75
|
+
};
|
|
76
|
+
const createStripePatternCanvas = (baseColor, spaceUnit = 24)=>{
|
|
77
|
+
const patternCanvas = document.createElement('canvas');
|
|
78
|
+
patternCanvas.width = spaceUnit;
|
|
79
|
+
patternCanvas.height = spaceUnit;
|
|
80
|
+
const patternCtx = patternCanvas.getContext('2d', {
|
|
81
|
+
willReadFrequently: false,
|
|
82
|
+
alpha: true
|
|
83
|
+
});
|
|
84
|
+
if (!patternCtx) return patternCanvas;
|
|
85
|
+
patternCtx.imageSmoothingEnabled = true;
|
|
86
|
+
patternCtx.imageSmoothingQuality = 'high';
|
|
87
|
+
patternCtx.fillStyle = baseColor;
|
|
88
|
+
patternCtx.globalAlpha = 0.9;
|
|
89
|
+
patternCtx.fillRect(0, 0, spaceUnit, spaceUnit);
|
|
90
|
+
const lineWidth = spaceUnit <= 12 ? 2 : 4;
|
|
91
|
+
patternCtx.strokeStyle = 'rgba(255, 255, 255, 1)';
|
|
92
|
+
patternCtx.lineWidth = lineWidth;
|
|
93
|
+
patternCtx.lineCap = 'square';
|
|
94
|
+
patternCtx.lineJoin = 'miter';
|
|
95
|
+
const stripeSpacing = spaceUnit / 2;
|
|
96
|
+
const startIndex = -2;
|
|
97
|
+
const endIndex = 4;
|
|
98
|
+
for(let i = startIndex; i <= endIndex; i++){
|
|
99
|
+
const offset = i * stripeSpacing;
|
|
100
|
+
const x1 = offset;
|
|
101
|
+
const y1 = 0;
|
|
102
|
+
const x2 = offset + spaceUnit;
|
|
103
|
+
const y2 = spaceUnit;
|
|
104
|
+
patternCtx.beginPath();
|
|
105
|
+
patternCtx.moveTo(x1, y1);
|
|
106
|
+
patternCtx.lineTo(x2, y2);
|
|
107
|
+
patternCtx.stroke();
|
|
108
|
+
}
|
|
109
|
+
return patternCanvas;
|
|
110
|
+
};
|
|
111
|
+
const getChartKey = (config)=>JSON.stringify(config);
|
|
112
|
+
export { calTextWidth, calculateBarWidth, createStripePatternCanvas, getAxisFormat, getAxisHorPaddingByText, getChartKey, markerStyle, truncateText };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 转换对比指标数据
|
|
3
|
+
*/
|
|
4
|
+
export declare function transformIndicatorList(indicators: Array<string>, indicatorData: any): any;
|
|
5
|
+
/**
|
|
6
|
+
* 变化率
|
|
7
|
+
*/
|
|
8
|
+
export declare const getIntervalRatio: (ratio: string) => string;
|
|
9
|
+
/**
|
|
10
|
+
* 两两分组
|
|
11
|
+
*/
|
|
12
|
+
export declare const groupArray: (arr: Array<any>) => any;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { isNil } from "lodash";
|
|
2
|
+
import { BigNumberUtil, formatPercentage } from "./formatters.js";
|
|
3
|
+
const formatValue = (val)=>{
|
|
4
|
+
if ('' === val || isNaN(Number(val))) return '-';
|
|
5
|
+
return Number(val);
|
|
6
|
+
};
|
|
7
|
+
function transformIndicatorList(indicators, indicatorData) {
|
|
8
|
+
const result = {};
|
|
9
|
+
for (const indicatorType of indicators){
|
|
10
|
+
result[indicatorType] = [];
|
|
11
|
+
for(let i = 0; i < indicatorData.length; i++){
|
|
12
|
+
const groupName = indicatorData[i].groupName;
|
|
13
|
+
const indicatorValue = indicatorData[i][indicatorType];
|
|
14
|
+
const compareKey = `${indicatorType}_compare`;
|
|
15
|
+
const changeKey = `${indicatorType}_change`;
|
|
16
|
+
const compareTimeKey = `${indicatorType}_compare_time`;
|
|
17
|
+
const compareValue = indicatorData[i][compareKey];
|
|
18
|
+
const changeValue = indicatorData[i][changeKey];
|
|
19
|
+
const compareTimeValue = indicatorData[i][compareTimeKey];
|
|
20
|
+
const nodeInfos = indicatorData[i]['nodeInfos'];
|
|
21
|
+
const item = {
|
|
22
|
+
groupName,
|
|
23
|
+
groupType: indicatorType,
|
|
24
|
+
groupValue: formatValue(indicatorValue),
|
|
25
|
+
indicatorType,
|
|
26
|
+
indicatorValue: formatValue(indicatorValue),
|
|
27
|
+
change: formatValue(changeValue),
|
|
28
|
+
nodeInfos
|
|
29
|
+
};
|
|
30
|
+
result[indicatorType].push(item);
|
|
31
|
+
if (!isNil(compareValue)) {
|
|
32
|
+
const compareItem = {
|
|
33
|
+
groupName,
|
|
34
|
+
groupType: compareKey,
|
|
35
|
+
groupValue: formatValue(compareValue),
|
|
36
|
+
change: formatValue(changeValue),
|
|
37
|
+
indicatorType: compareKey,
|
|
38
|
+
indicatorValue: formatValue(compareValue),
|
|
39
|
+
compareTime: compareTimeValue || '对比时间',
|
|
40
|
+
nodeInfos
|
|
41
|
+
};
|
|
42
|
+
result[indicatorType].push(compareItem);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
const getIntervalRatio = (ratio)=>{
|
|
49
|
+
const intervalRatio = formatValue(ratio);
|
|
50
|
+
if ('-' === intervalRatio) return intervalRatio;
|
|
51
|
+
const percentageStr = formatPercentage(100 * intervalRatio, '0');
|
|
52
|
+
const percentage = intervalRatio <= 0 ? percentageStr.split('-')[1] : percentageStr;
|
|
53
|
+
if (BigNumberUtil.lt(intervalRatio, 0)) return `下降${percentage}`;
|
|
54
|
+
if (BigNumberUtil.gt(intervalRatio, 0)) return `上升${percentage}`;
|
|
55
|
+
return '持平';
|
|
56
|
+
};
|
|
57
|
+
const groupArray = (arr)=>arr.reduce((result, current, index)=>{
|
|
58
|
+
if (index % 2 === 0) result.push([
|
|
59
|
+
current
|
|
60
|
+
]);
|
|
61
|
+
else result[result.length - 1].push(current);
|
|
62
|
+
return result;
|
|
63
|
+
}, []);
|
|
64
|
+
export { getIntervalRatio, groupArray, transformIndicatorList };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import BigNumber from 'big.js';
|
|
2
|
+
import type { IndicatorInfo } from '../core/ChartTypes';
|
|
3
|
+
export declare enum IndicatorUnit {
|
|
4
|
+
Number = 1,
|
|
5
|
+
Percentage = 2,
|
|
6
|
+
Dollar = 3,
|
|
7
|
+
RMB = 4
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 格式化数字
|
|
11
|
+
*/
|
|
12
|
+
export declare function formatNumberV2(value?: number | string, options?: {
|
|
13
|
+
fractionDigits?: number;
|
|
14
|
+
prefix?: string;
|
|
15
|
+
suffix?: string;
|
|
16
|
+
longFormat?: boolean;
|
|
17
|
+
}, emptyStr?: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* 格式化百分比
|
|
20
|
+
*/
|
|
21
|
+
export declare function formatPercentage(value?: number | string, emptyStr?: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* 格式化大数(简化版本)
|
|
24
|
+
*/
|
|
25
|
+
export declare function formatBigNumber(value?: number | string, indicatorInfo?: IndicatorInfo, emptyStr?: string, longFormat?: boolean): string;
|
|
26
|
+
/**
|
|
27
|
+
* 格式化指标值(简化版本)
|
|
28
|
+
*/
|
|
29
|
+
export declare function formatIndicatorV2(value?: number | string, indicatorInfo?: IndicatorInfo, emptyStr?: string, longFormat?: boolean): string;
|
|
30
|
+
/**
|
|
31
|
+
* BigNumber 工具(简化版本)
|
|
32
|
+
*/
|
|
33
|
+
export declare class BigNumberUtil {
|
|
34
|
+
static from(value: any): BigNumber;
|
|
35
|
+
static lt(value1: any, value2: any): boolean;
|
|
36
|
+
static gt(value1: any, value2: any): boolean;
|
|
37
|
+
}
|