@xq-labs/data-ui-v2 0.1.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.
@@ -0,0 +1,1354 @@
1
+ 'use strict';
2
+
3
+ var colors = require('./colors-55265c91.js');
4
+ var echarts = require('echarts/core');
5
+ var charts = require('echarts/charts');
6
+ var components = require('echarts/components');
7
+ var renderers = require('echarts/renderers');
8
+ var withInstall = require('./with-install-6ce12163.js');
9
+
10
+ function _interopNamespaceDefault(e) {
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n.default = e;
24
+ return Object.freeze(n);
25
+ }
26
+
27
+ var echarts__namespace = /*#__PURE__*/_interopNamespaceDefault(echarts);
28
+
29
+ /**
30
+ * 将输入值转换为安全数值。
31
+ * 非数字、空值等异常输入统一回落为 0,避免污染饼图占比计算。
32
+ */
33
+ function toNumber$1(value) {
34
+ var numericValue = Number(value);
35
+ return Number.isFinite(numericValue) ? numericValue : 0;
36
+ }
37
+
38
+ /**
39
+ * 统一归一化饼图输入数据。
40
+ * - name 缺失时自动补默认文案
41
+ * - value 统一转为安全数字
42
+ */
43
+ function normalizePieInput() {
44
+ var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
45
+ data = _ref.data;
46
+ var rawData = Array.isArray(data) ? data : [];
47
+ return rawData.map(function (item, index) {
48
+ return colors._objectSpread2(colors._objectSpread2({}, item), {}, {
49
+ name: item && item.name !== undefined ? item.name : "\u6570\u636E".concat(index + 1),
50
+ value: toNumber$1(item && item.value)
51
+ });
52
+ });
53
+ }
54
+
55
+ var PIE_PRESETS = {
56
+ ratio: {
57
+ legend: {
58
+ show: false
59
+ }
60
+ },
61
+ rank: {
62
+ legend: {
63
+ show: true,
64
+ orient: 'vertical',
65
+ right: 0,
66
+ top: 'middle'
67
+ }
68
+ }
69
+ };
70
+
71
+ /**
72
+ * 获取饼图预设配置。
73
+ * 未命中时统一回退到 `ratio`,避免组件层感知预设异常。
74
+ */
75
+ function getPiePreset() {
76
+ var preset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'ratio';
77
+ return PIE_PRESETS[preset] || PIE_PRESETS.ratio;
78
+ }
79
+
80
+ var PIE_VARIANT = {
81
+ PIE: 'pie',
82
+ RING: 'ring'
83
+ };
84
+ var PIE_DIMENSION = {
85
+ TWO_D: '2d',
86
+ THREE_D: '3d'
87
+ };
88
+ var PIE_HEIGHT_MODE = {
89
+ EQUAL: 'equal',
90
+ VALUE: 'value'
91
+ };
92
+ var DEFAULT_HEIGHT_RANGE = {
93
+ min: 20,
94
+ max: 80
95
+ };
96
+ var DEFAULT_3D_VIEW_ALPHA = 28;
97
+ var DEFAULT_INTERNAL_DIAMETER_RATIO = 0.6;
98
+ var DEFAULT_ROSE_RADIUS_RANGE = [0.75, 1.15];
99
+ var DEFAULT_LABEL_OPTIONS = {
100
+ show: true,
101
+ formatter: '{b}\n{d}%',
102
+ fontSize: 14,
103
+ color: '#333',
104
+ lineColor: null,
105
+ lineWidth: 1,
106
+ lineLength1: 18,
107
+ lineLength2: 24,
108
+ minShowLabelAngle: 0,
109
+ edgePadding: 12,
110
+ textGap: 6,
111
+ verticalGap: 8,
112
+ radialOffset: 0.12
113
+ };
114
+
115
+ /**
116
+ * 将输入值转为安全数字。
117
+ * 当值不可用时回退到指定默认值,供几何计算统一复用。
118
+ */
119
+ function toNumber(value) {
120
+ var fallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
121
+ var numericValue = Number(value);
122
+ return Number.isFinite(numericValue) ? numericValue : fallback;
123
+ }
124
+
125
+ /**
126
+ * 归一化区间数组。
127
+ * 输入非法时回退到默认区间,并自动修正最小值/最大值顺序。
128
+ */
129
+ function normalizeRange(range, fallbackMin, fallbackMax) {
130
+ if (!Array.isArray(range) || range.length < 2) {
131
+ return [fallbackMin, fallbackMax];
132
+ }
133
+ var min = toNumber(range[0], fallbackMin);
134
+ var max = toNumber(range[1], fallbackMax);
135
+ return min <= max ? [min, max] : [max, min];
136
+ }
137
+
138
+ /**
139
+ * 归一化环图内径比例。
140
+ * 统一限制在 [0, 0.9] 区间,避免 3D 几何穿模。
141
+ */
142
+ function normalizeInternalDiameterRatio(ratio) {
143
+ return Math.min(0.9, Math.max(0, toNumber(ratio, DEFAULT_INTERNAL_DIAMETER_RATIO)));
144
+ }
145
+
146
+ /**
147
+ * 将原始数值线性映射到目标区间。
148
+ * 常用于把 value 映射到高度、半径等视觉参数。
149
+ */
150
+ function mapValueToRange(values, targetMin, targetMax) {
151
+ if (!values.length) {
152
+ return [];
153
+ }
154
+ var minValue = Math.min.apply(Math, colors._toConsumableArray(values));
155
+ var maxValue = Math.max.apply(Math, colors._toConsumableArray(values));
156
+ if (minValue === maxValue) {
157
+ return values.map(function () {
158
+ return targetMax;
159
+ });
160
+ }
161
+ return values.map(function (value) {
162
+ return targetMin + (value - minValue) / (maxValue - minValue) * (targetMax - targetMin);
163
+ });
164
+ }
165
+
166
+ /**
167
+ * 为角度分配准备有效值列表。
168
+ * 当总和小于等于 0 时,回退为等分权重,保证饼图仍可渲染。
169
+ */
170
+ function getAngleValueList(values) {
171
+ var positiveValues = values.map(function (value) {
172
+ return Math.max(0, value);
173
+ });
174
+ var total = positiveValues.reduce(function (sum, value) {
175
+ return sum + value;
176
+ }, 0);
177
+ if (total > 0) {
178
+ return positiveValues;
179
+ }
180
+ return positiveValues.map(function () {
181
+ return 1;
182
+ });
183
+ }
184
+
185
+ /**
186
+ * 在 3D 场景下按配置过滤零值扇区。
187
+ * 默认会隐藏数值小于等于 0 的项,避免生成“有高度但没有角度”的不可见切片。
188
+ * 当外部显式关闭该开关时,继续保留旧行为,方便兼容已有展示或对比调试。
189
+ */
190
+ function filterZeroValuePieData(pieData, hideZeroValueSlices) {
191
+ if (!hideZeroValueSlices) {
192
+ return pieData;
193
+ }
194
+ return (pieData || []).filter(function (item) {
195
+ return Math.max(0, toNumber(item && item.value, 0)) > 0;
196
+ });
197
+ }
198
+
199
+ /**
200
+ * 将业务数据转换为饼图系列数据。
201
+ * 该步骤会统一补齐安全数值与默认色板。
202
+ */
203
+ function buildPieChartData(data) {
204
+ var colors$1 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : colors.DEFAULT_COLORS;
205
+ var palette = Array.isArray(colors$1) && colors$1.length > 0 ? colors$1 : colors.DEFAULT_COLORS;
206
+ return (data || []).map(function (item, index) {
207
+ var itemStyle = item.itemStyle || {};
208
+ return colors._objectSpread2(colors._objectSpread2({}, item), {}, {
209
+ value: toNumber(item.value, 0),
210
+ itemStyle: colors._objectSpread2(colors._objectSpread2({}, itemStyle), {}, {
211
+ color: itemStyle.color || palette[index % palette.length],
212
+ opacity: itemStyle.opacity === undefined ? 0.85 : itemStyle.opacity
213
+ })
214
+ });
215
+ });
216
+ }
217
+ function replaceLabelTemplate(template, params) {
218
+ return template.replace(/\{b\}/g, params.name).replace(/\{c\}/g, params.value).replace(/\{d\}/g, params.percent);
219
+ }
220
+ function formatPieLabelText(formatter, params) {
221
+ if (typeof formatter === 'function') {
222
+ return formatter(params);
223
+ }
224
+ if (typeof formatter === 'string' && formatter) {
225
+ return replaceLabelTemplate(formatter, params);
226
+ }
227
+ return "".concat(params.name, "\n").concat(params.percent, "%");
228
+ }
229
+
230
+ /**
231
+ * 格式化 3D 饼图默认 tooltip 文案。
232
+ * surface 系列 hover 时拿到的 `params.value` 实际是参数方程采样值,
233
+ * 不能直接当业务值展示,因此统一回退到扇区自身的 `pieData`。
234
+ */
235
+ function formatPieTooltipText(name, value, percent) {
236
+ return "".concat(name, ": ").concat(value, " (").concat(percent, "%)");
237
+ }
238
+
239
+ /**
240
+ * 为单个 3D 扇区生成专用 tooltip。
241
+ * 这样即使鼠标落在 surface 网格采样点上,展示的仍然是业务名称、原始值与百分比。
242
+ */
243
+ function createPie3DTooltip(pieData, totalValue) {
244
+ var value = toNumber(pieData && pieData.value, 0);
245
+ var percent = totalValue > 0 ? Number((value / totalValue * 100).toFixed(2)) : 0;
246
+ var name = pieData && pieData.name !== undefined ? pieData.name : '';
247
+ return {
248
+ formatter: function formatter() {
249
+ return formatPieTooltipText(name, value, percent);
250
+ }
251
+ };
252
+ }
253
+ function multiplyMat4Vec4(matrix, vector) {
254
+ return [matrix[0] * vector[0] + matrix[4] * vector[1] + matrix[8] * vector[2] + matrix[12] * vector[3], matrix[1] * vector[0] + matrix[5] * vector[1] + matrix[9] * vector[2] + matrix[13] * vector[3], matrix[2] * vector[0] + matrix[6] * vector[1] + matrix[10] * vector[2] + matrix[14] * vector[3], matrix[3] * vector[0] + matrix[7] * vector[1] + matrix[11] * vector[2] + matrix[15] * vector[3]];
255
+ }
256
+ function projectPiePointToPixel(chart, coordSys, camera, dataPoint) {
257
+ var viewport = coordSys && coordSys.viewGL && coordSys.viewGL.viewport;
258
+ var chartHeight = chart && chart.getHeight ? chart.getHeight() : 0;
259
+ if (!viewport || !camera || !camera.viewMatrix || !camera.projectionMatrix) {
260
+ return null;
261
+ }
262
+ var worldPoint = coordSys.dataToPoint(dataPoint);
263
+ if (!worldPoint || worldPoint.length < 3) {
264
+ return null;
265
+ }
266
+ var viewPoint = multiplyMat4Vec4(camera.viewMatrix.array, [worldPoint[0], worldPoint[1], worldPoint[2], 1]);
267
+ var clipPoint = multiplyMat4Vec4(camera.projectionMatrix.array, viewPoint);
268
+ if (!Number.isFinite(clipPoint[3]) || clipPoint[3] === 0) {
269
+ return null;
270
+ }
271
+ var ndcX = clipPoint[0] / clipPoint[3];
272
+ var ndcY = clipPoint[1] / clipPoint[3];
273
+ if (!Number.isFinite(ndcX) || !Number.isFinite(ndcY)) {
274
+ return null;
275
+ }
276
+ return [viewport.x + (ndcX + 1) * viewport.width / 2, chartHeight - (viewport.y + (ndcY + 1) * viewport.height / 2)];
277
+ }
278
+ function normalizeVector(x, y) {
279
+ var fallbackX = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
280
+ var fallbackY = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
281
+ var length = Math.sqrt(x * x + y * y);
282
+ if (!length) {
283
+ return [fallbackX, fallbackY];
284
+ }
285
+ return [x / length, y / length];
286
+ }
287
+ function estimateLabelHeight(text, fontSize) {
288
+ var lineCount = String(text || '').split('\n').length || 1;
289
+ return lineCount * fontSize * 1.4;
290
+ }
291
+ function adjustLabelPositions(labelItems, chartHeight, edgePadding, verticalGap) {
292
+ if (!labelItems.length) {
293
+ return;
294
+ }
295
+ labelItems.sort(function (prevItem, nextItem) {
296
+ return prevItem.targetY - nextItem.targetY;
297
+ });
298
+ var currentTop = edgePadding;
299
+ labelItems.forEach(function (item) {
300
+ var halfHeight = item.textHeight / 2;
301
+ var minCenterY = currentTop + halfHeight;
302
+ item.finalY = Math.max(item.targetY, minCenterY);
303
+ currentTop = item.finalY + halfHeight + verticalGap;
304
+ });
305
+ var currentBottom = chartHeight - edgePadding;
306
+ for (var index = labelItems.length - 1; index >= 0; index -= 1) {
307
+ var item = labelItems[index];
308
+ var halfHeight = item.textHeight / 2;
309
+ var maxCenterY = currentBottom - halfHeight;
310
+ item.finalY = Math.min(item.finalY, maxCenterY);
311
+ currentBottom = item.finalY - halfHeight - verticalGap;
312
+ }
313
+ currentTop = edgePadding;
314
+ labelItems.forEach(function (item) {
315
+ var halfHeight = item.textHeight / 2;
316
+ var minCenterY = currentTop + halfHeight;
317
+ item.finalY = Math.max(item.finalY, minCenterY);
318
+ currentTop = item.finalY + halfHeight + verticalGap;
319
+ });
320
+ }
321
+
322
+ /**
323
+ * 基于真实 3D 扇区投影生成引导线 graphic。
324
+ * 3D surface 不能直接复用 2D pie 的 labelLine,否则引导线会和立体扇区错位。
325
+ */
326
+ function buildPieLabelGraphics(chart) {
327
+ var pieSeries = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
328
+ var customLabelOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
329
+ var labelOptions = colors._objectSpread2(colors._objectSpread2({}, DEFAULT_LABEL_OPTIONS), customLabelOptions || {});
330
+ if (!labelOptions.show || !chart || !pieSeries.length) {
331
+ return [];
332
+ }
333
+ var chartModel = chart.getModel && chart.getModel();
334
+ var grid3DModel = chartModel && chartModel.getComponent('grid3D', 0);
335
+ var coordSys = grid3DModel && grid3DModel.coordinateSystem;
336
+ var camera = coordSys && coordSys.viewGL && coordSys.viewGL.camera;
337
+ if (!coordSys || !camera) {
338
+ return [];
339
+ }
340
+ var renderSeries = pieSeries.filter(function (item) {
341
+ return item && item.type === 'surface' && item.name !== 'mouseoutSeries';
342
+ });
343
+ if (!renderSeries.length) {
344
+ return [];
345
+ }
346
+ var chartWidth = chart.getWidth();
347
+ var chartHeight = chart.getHeight();
348
+ var totalValue = renderSeries.reduce(function (sum, item) {
349
+ return sum + Math.max(0, toNumber(item.pieData && item.pieData.value, 0));
350
+ }, 0);
351
+ var leftItems = [];
352
+ var rightItems = [];
353
+ renderSeries.forEach(function (seriesItem, index) {
354
+ var pieData = seriesItem.pieData || {};
355
+ var pieGeometry = seriesItem.pieGeometry || {};
356
+ var sectorAngle = ((pieData.endRatio || 0) - (pieData.startRatio || 0)) * 360;
357
+ if (sectorAngle < labelOptions.minShowLabelAngle) {
358
+ return;
359
+ }
360
+ var midRatio = (pieData.startRatio + pieData.endRatio) / 2;
361
+ var midRadian = midRatio * Math.PI * 2;
362
+ var outerRadius = Math.max(0, toNumber(pieGeometry.outerRadius, 1));
363
+ var height = toNumber(pieGeometry.height, DEFAULT_HEIGHT_RANGE.max);
364
+ var radialOffset = Math.max(0.02, toNumber(labelOptions.radialOffset, DEFAULT_LABEL_OPTIONS.radialOffset));
365
+ var anchorPoint = projectPiePointToPixel(chart, coordSys, camera, [Math.cos(midRadian) * outerRadius, Math.sin(midRadian) * outerRadius, height]);
366
+ var outwardPoint = projectPiePointToPixel(chart, coordSys, camera, [Math.cos(midRadian) * (outerRadius + radialOffset), Math.sin(midRadian) * (outerRadius + radialOffset), height]);
367
+ var centerPoint = projectPiePointToPixel(chart, coordSys, camera, [0, 0, height]);
368
+ if (!anchorPoint) {
369
+ return;
370
+ }
371
+ var fallbackSide = Math.cos(midRadian) >= 0 ? 1 : -1;
372
+ var directionSource = outwardPoint || centerPoint;
373
+ var rawDirection = directionSource ? [outwardPoint ? outwardPoint[0] - anchorPoint[0] : anchorPoint[0] - centerPoint[0], outwardPoint ? outwardPoint[1] - anchorPoint[1] : anchorPoint[1] - centerPoint[1]] : [fallbackSide, 0];
374
+ var direction = normalizeVector(rawDirection[0], rawDirection[1], fallbackSide, 0);
375
+ var side = direction[0] >= 0 ? 1 : -1;
376
+ var firstLineEnd = [anchorPoint[0] + direction[0] * labelOptions.lineLength1, anchorPoint[1] + direction[1] * labelOptions.lineLength1];
377
+ var value = toNumber(pieData.value, 0);
378
+ var percent = totalValue > 0 ? Number((value / totalValue * 100).toFixed(2)) : 0;
379
+ var text = formatPieLabelText(labelOptions.formatter, {
380
+ name: pieData.name,
381
+ value: value,
382
+ percent: percent,
383
+ data: pieData
384
+ });
385
+ var labelItem = {
386
+ id: "pie-label-".concat(index),
387
+ anchorPoint: anchorPoint,
388
+ firstLineEnd: firstLineEnd,
389
+ side: side,
390
+ lineEndX: Math.min(Math.max(firstLineEnd[0] + side * labelOptions.lineLength2, labelOptions.edgePadding), chartWidth - labelOptions.edgePadding),
391
+ targetY: firstLineEnd[1],
392
+ text: text,
393
+ textHeight: estimateLabelHeight(text, labelOptions.fontSize),
394
+ textColor: labelOptions.color,
395
+ lineColor: labelOptions.lineColor || labelOptions.color,
396
+ lineWidth: labelOptions.lineWidth,
397
+ fontSize: labelOptions.fontSize,
398
+ textGap: labelOptions.textGap
399
+ };
400
+ if (side > 0) {
401
+ rightItems.push(labelItem);
402
+ return;
403
+ }
404
+ leftItems.push(labelItem);
405
+ });
406
+ adjustLabelPositions(leftItems, chartHeight, labelOptions.edgePadding, labelOptions.verticalGap);
407
+ adjustLabelPositions(rightItems, chartHeight, labelOptions.edgePadding, labelOptions.verticalGap);
408
+ return [].concat(leftItems, rightItems).map(function (item) {
409
+ var lineEndPoint = [item.lineEndX, item.finalY];
410
+ var textX = item.lineEndX + (item.side > 0 ? item.textGap : -item.textGap);
411
+ return {
412
+ id: item.id,
413
+ type: 'group',
414
+ silent: true,
415
+ z: 101,
416
+ children: [{
417
+ type: 'polyline',
418
+ shape: {
419
+ points: [item.anchorPoint, item.firstLineEnd, lineEndPoint]
420
+ },
421
+ style: {
422
+ stroke: item.lineColor,
423
+ lineWidth: item.lineWidth,
424
+ fill: null,
425
+ lineJoin: 'round',
426
+ lineCap: 'round'
427
+ }
428
+ }, {
429
+ type: 'text',
430
+ style: {
431
+ x: textX,
432
+ y: item.finalY,
433
+ text: item.text,
434
+ fill: item.textColor,
435
+ font: "".concat(item.fontSize, "px sans-serif"),
436
+ textAlign: item.side > 0 ? 'left' : 'right',
437
+ textVerticalAlign: 'middle',
438
+ lineHeight: Math.round(item.fontSize * 1.4)
439
+ }
440
+ }]
441
+ };
442
+ });
443
+ }
444
+
445
+ /**
446
+ * 根据高度模式为每个扇区生成 3D 高度列表。
447
+ * - equal:全部返回统一高度
448
+ * - value:按数据值线性映射到 [minHeight, maxHeight]
449
+ */
450
+ function createHeightValues(values, mode, minHeight, maxHeight) {
451
+ var safeMinHeight = Math.max(1, toNumber(minHeight, DEFAULT_HEIGHT_RANGE.min));
452
+ var safeMaxHeight = Math.max(safeMinHeight, toNumber(maxHeight, DEFAULT_HEIGHT_RANGE.max));
453
+ if (mode !== PIE_HEIGHT_MODE.VALUE) {
454
+ return values.map(function () {
455
+ return safeMaxHeight;
456
+ });
457
+ }
458
+ return mapValueToRange(values, safeMinHeight, safeMaxHeight);
459
+ }
460
+ function createAdaptiveViewControl(showLabelLine, maxHeight) {
461
+ var baseDistance = showLabelLine ? 175 : 180;
462
+ var extraDistance = Math.max(0, maxHeight - 200) * 0.18;
463
+ return {
464
+ distance: baseDistance + extraDistance,
465
+ alpha: DEFAULT_3D_VIEW_ALPHA
466
+ };
467
+ }
468
+
469
+ /**
470
+ * 在启用玫瑰图时,按数据值计算每个扇区的外半径倍率。
471
+ * 未启用时返回全 1,表示所有扇区半径一致。
472
+ */
473
+ function createRoseOuterRadius(values, enabled, roseRadiusRange) {
474
+ if (!enabled) {
475
+ return values.map(function () {
476
+ return 1;
477
+ });
478
+ }
479
+ var range = normalizeRange(roseRadiusRange, DEFAULT_ROSE_RADIUS_RANGE[0], DEFAULT_ROSE_RADIUS_RANGE[1]);
480
+ return mapValueToRange(values, range[0], range[1]);
481
+ }
482
+
483
+ /**
484
+ * 计算 3D 坐标轴边界。
485
+ * 会基于最大外半径额外留白,避免扇区超出视图裁切。
486
+ */
487
+ function calculate3DAxisLimit(outerRadiusList) {
488
+ var maxOuterRadius = Math.max.apply(Math, [1].concat(colors._toConsumableArray(outerRadiusList)));
489
+ return maxOuterRadius + 0.45;
490
+ }
491
+
492
+ /**
493
+ * 生成单个 3D 扇区的参数方程。
494
+ * 返回的方程会同时编码内径、外径、高度与 hover/selected 状态。
495
+ */
496
+ function getParametricEquation(startRatio, endRatio, isSelected, isHovered) {
497
+ var geometry = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
498
+ var innerRatio = normalizeInternalDiameterRatio(geometry.innerRatio);
499
+ var outerRadius = Math.max(innerRatio + 0.05, toNumber(geometry.outerRadius, 1));
500
+ var height = Math.max(1, toNumber(geometry.height, DEFAULT_HEIGHT_RANGE.max));
501
+ var baseRadius = (outerRadius + innerRatio) / 2;
502
+ var radiusRange = (outerRadius - innerRatio) / 2;
503
+ var midRatio = (startRatio + endRatio) / 2;
504
+ var startRadian = startRatio * Math.PI * 2;
505
+ var endRadian = endRatio * Math.PI * 2;
506
+ var midRadian = midRatio * Math.PI * 2;
507
+ var selected = startRatio === 0 && endRatio === 1 ? false : isSelected;
508
+ var offsetSize = outerRadius * 0.08;
509
+ var offsetX = selected ? Math.cos(midRadian) * offsetSize : 0;
510
+ var offsetY = selected ? Math.sin(midRadian) * offsetSize : 0;
511
+ var hoverScale = isHovered ? 1.05 : 1;
512
+ var getClampedRadian = function getClampedRadian(u) {
513
+ if (u < startRadian) return startRadian;
514
+ if (u > endRadian) return endRadian;
515
+ return u;
516
+ };
517
+ return {
518
+ u: {
519
+ min: -Math.PI,
520
+ max: Math.PI * 3,
521
+ step: Math.PI / 32
522
+ },
523
+ v: {
524
+ min: 0,
525
+ max: Math.PI * 2,
526
+ step: Math.PI / 20
527
+ },
528
+ x: function x(u, v) {
529
+ var radian = getClampedRadian(u);
530
+ var currentRadius = (baseRadius + Math.cos(v) * radiusRange) * hoverScale;
531
+ return offsetX + Math.cos(radian) * currentRadius;
532
+ },
533
+ y: function y(u, v) {
534
+ var radian = getClampedRadian(u);
535
+ var currentRadius = (baseRadius + Math.cos(v) * radiusRange) * hoverScale;
536
+ return offsetY + Math.sin(radian) * currentRadius;
537
+ },
538
+ z: function z(u, v) {
539
+ if (u < -Math.PI * 0.5 || u > Math.PI * 2.5) {
540
+ return Math.sin(u);
541
+ }
542
+ return Math.sin(v) > 0 ? height : 0;
543
+ }
544
+ };
545
+ }
546
+
547
+ /**
548
+ * 创建透明辅助曲面。
549
+ * 主要用于稳定鼠标事件和 3D 场景边界,不承载业务数据。
550
+ */
551
+ function createSupportSeries() {
552
+ var outerRadius = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
553
+ return {
554
+ name: 'mouseoutSeries',
555
+ type: 'surface',
556
+ tooltip: {
557
+ show: false
558
+ },
559
+ parametric: true,
560
+ wireframe: {
561
+ show: false
562
+ },
563
+ itemStyle: {
564
+ opacity: 0
565
+ },
566
+ parametricEquation: {
567
+ u: {
568
+ min: 0,
569
+ max: Math.PI * 2,
570
+ step: Math.PI / 20
571
+ },
572
+ v: {
573
+ min: 0,
574
+ max: Math.PI,
575
+ step: Math.PI / 20
576
+ },
577
+ x: function x(u, v) {
578
+ return (Math.sin(v) * Math.sin(u) + Math.sin(u)) * outerRadius;
579
+ },
580
+ y: function y(u, v) {
581
+ return (Math.sin(v) * Math.cos(u) + Math.cos(u)) * outerRadius;
582
+ },
583
+ z: function z(u, v) {
584
+ return Math.cos(v) > 0 ? 0.1 : 0;
585
+ }
586
+ }
587
+ };
588
+ }
589
+
590
+ /**
591
+ * 生成 3D 饼图所需的 surface 系列集合。
592
+ * 返回值同时携带坐标轴边界,供上层组装 grid3D 使用。
593
+ */
594
+ function buildPie3DSeries(pieData) {
595
+ var chartConfig = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
596
+ var renderPieData = filterZeroValuePieData(pieData, chartConfig.hideZeroValueSlices);
597
+ var series = [];
598
+ var valueList = renderPieData.map(function (item) {
599
+ return Math.max(0, toNumber(item.value, 0));
600
+ });
601
+ var totalValue = valueList.reduce(function (sum, value) {
602
+ return sum + value;
603
+ }, 0);
604
+ var angleValueList = getAngleValueList(valueList);
605
+ var totalAngleValue = angleValueList.reduce(function (sum, value) {
606
+ return sum + value;
607
+ }, 0);
608
+ var heightValues = createHeightValues(valueList, chartConfig.heightMode, chartConfig.minHeight, chartConfig.maxHeight);
609
+ var roseOuterRadiusList = createRoseOuterRadius(valueList, chartConfig.rose, chartConfig.roseRadiusRange);
610
+ var innerRatio = chartConfig.variant === PIE_VARIANT.RING ? normalizeInternalDiameterRatio(chartConfig.internalDiameterRatio) : 0;
611
+ var startValue = 0;
612
+ renderPieData.forEach(function (item, index) {
613
+ var endValue = startValue + angleValueList[index];
614
+ var startRatio = startValue / totalAngleValue;
615
+ var endRatio = endValue / totalAngleValue;
616
+ var geometry = {
617
+ innerRatio: innerRatio,
618
+ outerRadius: roseOuterRadiusList[index],
619
+ height: heightValues[index]
620
+ };
621
+ var seriesItem = {
622
+ name: item.name === undefined ? "series".concat(index) : item.name,
623
+ type: 'surface',
624
+ parametric: true,
625
+ wireframe: {
626
+ show: false
627
+ },
628
+ pieData: colors._objectSpread2(colors._objectSpread2({}, item), {}, {
629
+ startRatio: startRatio,
630
+ endRatio: endRatio
631
+ }),
632
+ pieGeometry: geometry,
633
+ pieStatus: {
634
+ selected: false,
635
+ hovered: false
636
+ },
637
+ tooltip: createPie3DTooltip(item, totalValue),
638
+ itemStyle: colors._objectSpread2({}, item.itemStyle),
639
+ parametricEquation: getParametricEquation(startRatio, endRatio, false, false, geometry)
640
+ };
641
+ series.push(seriesItem);
642
+ startValue = endValue;
643
+ });
644
+ var axisLimit = calculate3DAxisLimit(roseOuterRadiusList);
645
+ series.push(createSupportSeries(axisLimit * 0.75));
646
+ return {
647
+ series: series,
648
+ axisLimit: axisLimit
649
+ };
650
+ }
651
+
652
+ /**
653
+ * 将数字转换为百分比字符串。
654
+ * 主要用于 2D 饼图半径配置。
655
+ */
656
+ function toPercent(value) {
657
+ return "".concat(Math.round(value), "%");
658
+ }
659
+
660
+ /**
661
+ * 根据形态和玫瑰图开关计算 2D 饼图半径。
662
+ * ring 会返回内外半径数组,其余场景返回单半径或玫瑰区间。
663
+ */
664
+ function createPie2DRadius(variant, rose, internalDiameterRatio) {
665
+ if (variant === PIE_VARIANT.RING) {
666
+ var outer = 70;
667
+ var innerRatio = normalizeInternalDiameterRatio(internalDiameterRatio);
668
+ return [toPercent(outer * innerRatio), toPercent(outer)];
669
+ }
670
+ return '70%';
671
+ }
672
+
673
+ /**
674
+ * 生成 2D 饼图 option 片段。
675
+ * 负责组合 pie series、标签与半径等 2D 专属配置。
676
+ */
677
+ function createPie2DOption(input, variant) {
678
+ var data = buildPieChartData(input.data, input.colors);
679
+ var labelOptions = colors._objectSpread2(colors._objectSpread2({}, DEFAULT_LABEL_OPTIONS), input.labelOptions || {});
680
+ var showLabelLine = Boolean(input.showLabelLine);
681
+ var rose = Boolean(input.rose);
682
+ return {
683
+ series: [{
684
+ type: 'pie',
685
+ center: ['50%', '50%'],
686
+ radius: createPie2DRadius(variant, rose, input.internalDiameterRatio),
687
+ roseType: rose ? 'radius' : undefined,
688
+ itemStyle: {
689
+ opacity: 0.9
690
+ },
691
+ label: {
692
+ show: showLabelLine,
693
+ formatter: labelOptions.formatter,
694
+ color: labelOptions.color,
695
+ fontSize: labelOptions.fontSize,
696
+ minShowLabelAngle: labelOptions.minShowLabelAngle
697
+ },
698
+ labelLine: {
699
+ show: showLabelLine,
700
+ length: labelOptions.lineLength1,
701
+ length2: labelOptions.lineLength2,
702
+ lineStyle: {
703
+ color: labelOptions.lineColor || labelOptions.color,
704
+ width: labelOptions.lineWidth
705
+ }
706
+ },
707
+ data: data
708
+ }]
709
+ };
710
+ }
711
+
712
+ /**
713
+ * 生成 3D 饼图 option 片段。
714
+ * 主要负责 grid3D、坐标轴边界和 3D series 组合。
715
+ */
716
+ function createPie3DOption(input, variant) {
717
+ var data = buildPieChartData(input.data, input.colors);
718
+ var safeMaxHeight = Math.max(1, toNumber(input.maxHeight, DEFAULT_HEIGHT_RANGE.max));
719
+ var chartConfig = {
720
+ variant: variant,
721
+ rose: Boolean(input.rose),
722
+ heightMode: input.heightMode,
723
+ minHeight: input.minHeight,
724
+ maxHeight: input.maxHeight,
725
+ internalDiameterRatio: input.internalDiameterRatio,
726
+ roseRadiusRange: input.roseRadiusRange,
727
+ showLabelLine: Boolean(input.showLabelLine),
728
+ labelOptions: input.labelOptions,
729
+ hideZeroValueSlices: input.hideZeroValueSlices !== false
730
+ };
731
+ var pie3D = buildPie3DSeries(data, chartConfig);
732
+ var viewControl = createAdaptiveViewControl(chartConfig.showLabelLine, safeMaxHeight);
733
+ return {
734
+ xAxis3D: {
735
+ min: -pie3D.axisLimit,
736
+ max: pie3D.axisLimit
737
+ },
738
+ yAxis3D: {
739
+ min: -pie3D.axisLimit,
740
+ max: pie3D.axisLimit
741
+ },
742
+ zAxis3D: {
743
+ min: 0,
744
+ max: safeMaxHeight
745
+ },
746
+ grid3D: {
747
+ show: false,
748
+ // 3D 扇区的 height 已经按业务值映射完成,grid3D 这里不能再给极小物理高度,
749
+ // 否则会把本来存在的厚度视觉再次压扁。
750
+ boxHeight: safeMaxHeight,
751
+ top: '0',
752
+ left: '0',
753
+ viewControl: {
754
+ distance: viewControl.distance,
755
+ alpha: viewControl.alpha,
756
+ autoRotate: false,
757
+ damping: 1,
758
+ zoomSensitivity: 0,
759
+ rotateSensitivity: 0,
760
+ panSensitivity: 0
761
+ }
762
+ },
763
+ graphic: [],
764
+ series: pie3D.series
765
+ };
766
+ }
767
+
768
+ /**
769
+ * 生成基础饼图变体 option。
770
+ * 由归一化后的 `dimension` 决定返回 2D 还是 3D 结果。
771
+ */
772
+ function createPieChartOption() {
773
+ var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
774
+ // 分发规则:由 builder 归一后的 dimension 决定走 2D 还是 3D 分支。
775
+ if (input.dimension === PIE_DIMENSION.THREE_D) {
776
+ return createPie3DOption(input, PIE_VARIANT.PIE);
777
+ }
778
+ return createPie2DOption(input, PIE_VARIANT.PIE);
779
+ }
780
+
781
+ /**
782
+ * 生成基础环图变体 option。
783
+ * 由归一化后的 `dimension` 决定返回 2D 还是 3D 结果。
784
+ */
785
+ function createRingChartOption() {
786
+ var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
787
+ // 分发规则:由 builder 归一后的 dimension 决定走 2D 还是 3D 分支。
788
+ if (input.dimension === PIE_DIMENSION.THREE_D) {
789
+ return createPie3DOption(input, PIE_VARIANT.RING);
790
+ }
791
+ return createPie2DOption(input, PIE_VARIANT.RING);
792
+ }
793
+
794
+ var PIE_PUBLIC_VARIANTS = ['pie', 'ring', 'rose-pie', 'rose-ring'];
795
+ var PIE_PUBLIC_DIMENSIONS = [PIE_DIMENSION.TWO_D, PIE_DIMENSION.THREE_D];
796
+ var PIE_PUBLIC_HEIGHT_MODES = [PIE_HEIGHT_MODE.EQUAL, PIE_HEIGHT_MODE.VALUE];
797
+ var DEFAULT_VARIANT = 'pie';
798
+ var DEFAULT_DIMENSION = PIE_DIMENSION.TWO_D;
799
+ var DEFAULT_HEIGHT_MODE = PIE_HEIGHT_MODE.EQUAL;
800
+
801
+ /**
802
+ * 对外公开的 variant 会先映射为内部标准结构:
803
+ * 1) 归一到基础形态 baseVariant(pie/ring)
804
+ * 2) 同时携带 rose 开关
805
+ */
806
+ var PIE_VARIANT_MAPPING = {
807
+ pie: {
808
+ baseVariant: 'pie',
809
+ rose: false
810
+ },
811
+ ring: {
812
+ baseVariant: 'ring',
813
+ rose: false
814
+ },
815
+ 'rose-pie': {
816
+ baseVariant: 'pie',
817
+ rose: true
818
+ },
819
+ 'rose-ring': {
820
+ baseVariant: 'ring',
821
+ rose: true
822
+ }
823
+ };
824
+
825
+ /**
826
+ * 将公开 `variant` 解释为内部统一结构。
827
+ * 输出同时包含基础图形类型和是否启用玫瑰图的开关。
828
+ */
829
+ function normalizePieVariant() {
830
+ var variant = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_VARIANT;
831
+ return PIE_VARIANT_MAPPING[variant] || PIE_VARIANT_MAPPING[DEFAULT_VARIANT];
832
+ }
833
+
834
+ /**
835
+ * 只接受 `2d / 3d` 两种公开维度,其他输入统一回落到 `2d`。
836
+ */
837
+ function normalizePieDimension() {
838
+ var dimension = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_DIMENSION;
839
+ return dimension === PIE_DIMENSION.THREE_D ? PIE_DIMENSION.THREE_D : PIE_DIMENSION.TWO_D;
840
+ }
841
+
842
+ /**
843
+ * 规范要求:dimension 为 2d 时忽略 heightMode。
844
+ * 因此这里只在 3d 场景接受 value,其余统一回落 equal。
845
+ */
846
+ function normalizePieHeightMode() {
847
+ var heightMode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_HEIGHT_MODE;
848
+ var dimension = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_DIMENSION;
849
+ if (dimension !== PIE_DIMENSION.THREE_D) {
850
+ return PIE_HEIGHT_MODE.EQUAL;
851
+ }
852
+ return heightMode === PIE_HEIGHT_MODE.VALUE ? PIE_HEIGHT_MODE.VALUE : PIE_HEIGHT_MODE.EQUAL;
853
+ }
854
+
855
+ /**
856
+ * 汇总饼图构建层真正关心的类型输入。
857
+ * 经过该步骤后,变体层只需要处理标准化后的内部协议。
858
+ */
859
+ function normalizePieBuildInput() {
860
+ var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
861
+ variant = _ref.variant,
862
+ dimension = _ref.dimension,
863
+ heightMode = _ref.heightMode;
864
+ var normalizedVariant = normalizePieVariant(variant);
865
+ var normalizedDimension = normalizePieDimension(dimension);
866
+ return colors._objectSpread2(colors._objectSpread2({}, normalizedVariant), {}, {
867
+ dimension: normalizedDimension,
868
+ heightMode: normalizePieHeightMode(heightMode, normalizedDimension)
869
+ });
870
+ }
871
+ var variantFactory = {
872
+ pie: createPieChartOption,
873
+ ring: createRingChartOption
874
+ };
875
+
876
+ /**
877
+ * 构建 `PieChart` 最终 option。
878
+ * 这里会统一完成:
879
+ * 1. 输入数据归一化
880
+ * 2. 公开类型标准化
881
+ * 3. preset / variant / customOption 合并
882
+ */
883
+ function buildPieOption() {
884
+ var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
885
+ data = _ref2.data,
886
+ _ref2$preset = _ref2.preset,
887
+ preset = _ref2$preset === void 0 ? 'ratio' : _ref2$preset,
888
+ _ref2$variant = _ref2.variant,
889
+ variant = _ref2$variant === void 0 ? DEFAULT_VARIANT : _ref2$variant,
890
+ _ref2$dimension = _ref2.dimension,
891
+ dimension = _ref2$dimension === void 0 ? DEFAULT_DIMENSION : _ref2$dimension,
892
+ _ref2$heightMode = _ref2.heightMode,
893
+ heightMode = _ref2$heightMode === void 0 ? DEFAULT_HEIGHT_MODE : _ref2$heightMode,
894
+ _ref2$minHeight = _ref2.minHeight,
895
+ minHeight = _ref2$minHeight === void 0 ? DEFAULT_HEIGHT_RANGE.min : _ref2$minHeight,
896
+ _ref2$maxHeight = _ref2.maxHeight,
897
+ maxHeight = _ref2$maxHeight === void 0 ? DEFAULT_HEIGHT_RANGE.max : _ref2$maxHeight,
898
+ _ref2$internalDiamete = _ref2.internalDiameterRatio,
899
+ internalDiameterRatio = _ref2$internalDiamete === void 0 ? DEFAULT_INTERNAL_DIAMETER_RATIO : _ref2$internalDiamete,
900
+ _ref2$roseRadiusRange = _ref2.roseRadiusRange,
901
+ roseRadiusRange = _ref2$roseRadiusRange === void 0 ? DEFAULT_ROSE_RADIUS_RANGE : _ref2$roseRadiusRange,
902
+ _ref2$showLabelLine = _ref2.showLabelLine,
903
+ showLabelLine = _ref2$showLabelLine === void 0 ? false : _ref2$showLabelLine,
904
+ _ref2$labelOptions = _ref2.labelOptions,
905
+ labelOptions = _ref2$labelOptions === void 0 ? {} : _ref2$labelOptions,
906
+ _ref2$hideZeroValueSl = _ref2.hideZeroValueSlices,
907
+ hideZeroValueSlices = _ref2$hideZeroValueSl === void 0 ? true : _ref2$hideZeroValueSl,
908
+ customOption = _ref2.customOption;
909
+ var normalizedData = normalizePieInput({
910
+ data: data
911
+ });
912
+ var normalizedInput = normalizePieBuildInput({
913
+ variant: variant,
914
+ dimension: dimension,
915
+ heightMode: heightMode
916
+ });
917
+ var baseOption = {
918
+ color: colors.DEFAULT_COLORS,
919
+ tooltip: {
920
+ trigger: 'item',
921
+ formatter: '{b}: {c} ({d}%)'
922
+ },
923
+ legend: {
924
+ data: normalizedData.map(function (item) {
925
+ return item.name;
926
+ })
927
+ },
928
+ series: []
929
+ };
930
+ var presetOption = getPiePreset(preset);
931
+ var createVariantOption = variantFactory[normalizedInput.baseVariant] || variantFactory.pie;
932
+
933
+ // 变体层只接收已经标准化好的输入,不再关心公开 API 的历史差异。
934
+ var variantOption = createVariantOption({
935
+ data: normalizedData,
936
+ colors: baseOption.color,
937
+ dimension: normalizedInput.dimension,
938
+ rose: normalizedInput.rose,
939
+ heightMode: normalizedInput.heightMode,
940
+ minHeight: minHeight,
941
+ maxHeight: maxHeight,
942
+ internalDiameterRatio: internalDiameterRatio,
943
+ roseRadiusRange: roseRadiusRange,
944
+ showLabelLine: showLabelLine,
945
+ labelOptions: labelOptions,
946
+ hideZeroValueSlices: hideZeroValueSlices
947
+ });
948
+ return colors.mergeChartOption(colors.mergeChartOption(baseOption, presetOption, variantOption), customOption);
949
+ }
950
+
951
+ var runtimePromise = null;
952
+ var ECHARTS_GL_PACKAGE_NAME = 'echarts-gl';
953
+ var MISSING_RUNTIME_MESSAGE = '使用 PieChart 3D 能力需要安装 echarts-gl,请执行 npm install echarts-gl。';
954
+ var createMissingRuntimeError = function createMissingRuntimeError(error) {
955
+ var runtimeError = new Error(MISSING_RUNTIME_MESSAGE);
956
+ runtimeError.cause = error;
957
+ return runtimeError;
958
+ };
959
+ var importRuntime = function importRuntime() {
960
+ var dynamicImport = new Function('packageName', 'return import(packageName)');
961
+ return dynamicImport(ECHARTS_GL_PACKAGE_NAME);
962
+ };
963
+
964
+ /**
965
+ * 按需加载 echarts-gl 运行时。
966
+ *
967
+ * 这里不能在模块顶层静态加载 echarts-gl,否则只使用 2D 饼图的业务方也会被迫安装。
968
+ * 加载结果会被缓存,避免同一个页面内多个 3D 饼图重复触发运行时加载。
969
+ */
970
+ function ensureEchartsGlRuntime() {
971
+ if (!runtimePromise) {
972
+ runtimePromise = importRuntime()["catch"](function (error) {
973
+ runtimePromise = null;
974
+ throw createMissingRuntimeError(error);
975
+ });
976
+ }
977
+ return runtimePromise;
978
+ }
979
+
980
+ echarts__namespace.use([charts.PieChart, components.TooltipComponent, components.LegendComponent, components.GridComponent, components.TitleComponent, components.GraphicComponent, renderers.CanvasRenderer]);
981
+ var script = {
982
+ name: 'PieChart',
983
+ components: {
984
+ BaseChart: withInstall.BaseChart
985
+ },
986
+ props: {
987
+ /**
988
+ * 饼图数据源。
989
+ * 每项至少建议包含:
990
+ * - name: 扇区名称
991
+ * - value: 扇区数值
992
+ * 其余字段会原样透传给内部 builder 使用。
993
+ */
994
+ data: {
995
+ type: Array,
996
+ "default": function _default() {
997
+ return [];
998
+ }
999
+ },
1000
+ /**
1001
+ * 对外公开的形态枚举,统一收口为四种:
1002
+ * - pie / ring:普通饼图与环图
1003
+ * - rose-pie / rose-ring:玫瑰饼图与玫瑰环图
1004
+ */
1005
+ variant: {
1006
+ type: String,
1007
+ "default": 'pie',
1008
+ validator: function validator(value) {
1009
+ return PIE_PUBLIC_VARIANTS.includes(value);
1010
+ }
1011
+ },
1012
+ /**
1013
+ * 维度开关:
1014
+ * - 2d:标准 ECharts 饼图序列
1015
+ * - 3d:基于 echarts-gl 的 surface 序列
1016
+ */
1017
+ dimension: {
1018
+ type: String,
1019
+ "default": '2d',
1020
+ validator: function validator(value) {
1021
+ return PIE_PUBLIC_DIMENSIONS.includes(value);
1022
+ }
1023
+ },
1024
+ /**
1025
+ * 预设配置名称。
1026
+ * 当前主要用于切换图例、tooltip 等通用展示风格,
1027
+ * 不负责决定图形类型本身。
1028
+ */
1029
+ preset: {
1030
+ type: String,
1031
+ "default": 'ratio'
1032
+ },
1033
+ /**
1034
+ * 3D 高度模式:
1035
+ * - equal:所有扇区等高
1036
+ * - value:按数据值映射高度
1037
+ * 注意:当 dimension 为 2d 时会被忽略(内部会归一为 equal)。
1038
+ */
1039
+ heightMode: {
1040
+ type: String,
1041
+ "default": 'equal',
1042
+ validator: function validator(value) {
1043
+ return PIE_PUBLIC_HEIGHT_MODES.includes(value);
1044
+ }
1045
+ },
1046
+ /**
1047
+ * 3D 模式下的最小扇区高度。
1048
+ * 仅在 `dimension="3d" && heightMode="value"` 时参与高度映射。
1049
+ */
1050
+ minHeight: {
1051
+ type: Number,
1052
+ "default": 20
1053
+ },
1054
+ /**
1055
+ * 3D 模式下的最大扇区高度。
1056
+ * `heightMode="equal"` 时会作为统一高度使用。
1057
+ */
1058
+ maxHeight: {
1059
+ type: Number,
1060
+ "default": 80
1061
+ },
1062
+ /**
1063
+ * 环图内径比例。
1064
+ * 仅当基础形态为 ring / rose-ring 时生效,范围由内部统一归一。
1065
+ */
1066
+ internalDiameterRatio: {
1067
+ type: Number,
1068
+ "default": 0.6
1069
+ },
1070
+ /**
1071
+ * 玫瑰图外半径映射范围。
1072
+ * 用于控制 rose 系列在 3D 场景下的扇区外扩比例。
1073
+ */
1074
+ roseRadiusRange: {
1075
+ type: Array,
1076
+ "default": function _default() {
1077
+ return [0.75, 1.15];
1078
+ }
1079
+ },
1080
+ /**
1081
+ * 是否显示标签引导线。
1082
+ * 2D / 3D 都支持,但最终由内部 builder 决定具体实现方式。
1083
+ */
1084
+ showLabelLine: {
1085
+ type: Boolean,
1086
+ "default": false
1087
+ },
1088
+ /**
1089
+ * 标签与引导线配置。
1090
+ * 典型字段包括 formatter、fontSize、lineLength1、lineLength2 等。
1091
+ */
1092
+ labelOptions: {
1093
+ type: Object,
1094
+ "default": function _default() {
1095
+ return {};
1096
+ }
1097
+ },
1098
+ /**
1099
+ * 是否在 3D 模式下自动过滤值为 0 的扇区。
1100
+ * - true:默认过滤,避免生成 0 角度但仍占高度映射的不可见切片
1101
+ * - false:维持旧行为,保留零值扇区占位
1102
+ * 该配置仅对 `dimension="3d"` 生效,2D 饼图仍保持原始数据展示方式。
1103
+ */
1104
+ hideZeroValueSlices: {
1105
+ type: Boolean,
1106
+ "default": true
1107
+ },
1108
+ /**
1109
+ * 额外透传给 ECharts option 的自定义配置。
1110
+ * 最终会在基础 option 之上做合并,可用于覆盖默认展示细节。
1111
+ */
1112
+ customOption: {
1113
+ type: Object,
1114
+ "default": function _default() {
1115
+ return {};
1116
+ }
1117
+ },
1118
+ /**
1119
+ * 图表 loading 状态。
1120
+ * 由底层 BaseChart 统一负责 showLoading / hideLoading。
1121
+ */
1122
+ loading: {
1123
+ type: Boolean,
1124
+ "default": false
1125
+ },
1126
+ /**
1127
+ * 图表容器高度。
1128
+ * 支持数字(按 px 处理)或原样透传的字符串高度值。
1129
+ */
1130
+ height: {
1131
+ type: [String, Number],
1132
+ "default": 320
1133
+ }
1134
+ },
1135
+ data: function data() {
1136
+ return {
1137
+ chart: null,
1138
+ syncingLabelGraphics: false,
1139
+ echartsGlStatus: 'idle',
1140
+ echartsGlErrorMessage: ''
1141
+ };
1142
+ },
1143
+ computed: {
1144
+ /**
1145
+ * 基于数据源判断当前是否为空态。
1146
+ * 真正的空态渲染逻辑由 BaseChart 统一处理。
1147
+ */
1148
+ isEmpty: function isEmpty() {
1149
+ return colors.isEmptyData(this.data);
1150
+ },
1151
+ /**
1152
+ * 判断当前是否进入 3D 饼图能力。
1153
+ * 只有 3D 才需要加载 echarts-gl,2D 场景不应触发任何 3D 运行时依赖。
1154
+ */
1155
+ is3D: function is3D() {
1156
+ return this.dimension === '3d';
1157
+ },
1158
+ /**
1159
+ * 标识 echarts-gl 是否已经加载完成。
1160
+ * 3D option 只有在运行时就绪后才会生成 surface 序列。
1161
+ */
1162
+ isEchartsGlReady: function isEchartsGlReady() {
1163
+ return this.echartsGlStatus === 'ready';
1164
+ },
1165
+ /**
1166
+ * 汇总外部 loading 与 3D 运行时加载状态。
1167
+ * 这样 3D 首次加载 echarts-gl 时,底层 BaseChart 能保持 loading 态。
1168
+ */
1169
+ chartLoading: function chartLoading() {
1170
+ return this.loading || this.is3D && this.echartsGlStatus === 'loading';
1171
+ },
1172
+ /**
1173
+ * 汇总数据空态与 3D 运行时失败状态。
1174
+ * 缺少 echarts-gl 时不让 BaseChart 继续渲染空 surface option。
1175
+ */
1176
+ chartEmpty: function chartEmpty() {
1177
+ return this.isEmpty || this.is3D && this.echartsGlStatus === 'failed';
1178
+ },
1179
+ /**
1180
+ * 统一传给 BaseChart 的空态文案。
1181
+ * 3D 缺少运行时时给出可执行的安装提示,普通空态仍保留默认文案。
1182
+ */
1183
+ chartEmptyText: function chartEmptyText() {
1184
+ if (this.is3D && this.echartsGlStatus === 'failed') {
1185
+ return this.echartsGlErrorMessage || '使用 3D 饼图需要安装 echarts-gl';
1186
+ }
1187
+ return '暂无数据';
1188
+ },
1189
+ /**
1190
+ * 将组件公开 props 统一汇总后交给 builder。
1191
+ * 组件层不参与 2D/3D 分发和类型标准化,只负责透传公开输入。
1192
+ */
1193
+ chartOption: function chartOption() {
1194
+ if (this.is3D && !this.isEchartsGlReady) {
1195
+ return {
1196
+ series: []
1197
+ };
1198
+ }
1199
+ return buildPieOption({
1200
+ data: this.data,
1201
+ variant: this.variant,
1202
+ dimension: this.dimension,
1203
+ preset: this.preset,
1204
+ heightMode: this.heightMode,
1205
+ minHeight: this.minHeight,
1206
+ maxHeight: this.maxHeight,
1207
+ internalDiameterRatio: this.internalDiameterRatio,
1208
+ roseRadiusRange: this.roseRadiusRange,
1209
+ showLabelLine: this.showLabelLine,
1210
+ labelOptions: this.labelOptions,
1211
+ hideZeroValueSlices: this.hideZeroValueSlices,
1212
+ customOption: this.customOption
1213
+ });
1214
+ }
1215
+ },
1216
+ watch: {
1217
+ /**
1218
+ * dimension 是 2D/3D 分发的公开开关。
1219
+ * 只有切到 3D 时才按需加载 echarts-gl,避免 2D 使用方被迫安装 3D 运行时。
1220
+ */
1221
+ dimension: {
1222
+ immediate: true,
1223
+ handler: function handler() {
1224
+ this.ensurePie3DRuntimeIfNeeded();
1225
+ }
1226
+ }
1227
+ },
1228
+ beforeDestroy: function beforeDestroy() {
1229
+ if (this.chart && this.chart.off) {
1230
+ this.chart.off('finished', this.handleChartFinished);
1231
+ }
1232
+ this.chart = null;
1233
+ },
1234
+ methods: {
1235
+ /**
1236
+ * 接收 BaseChart 初始化完成后的 ECharts 实例。
1237
+ * 这里除了继续向外透传 ready,还会挂接 3D 引导线同步逻辑。
1238
+ */
1239
+ handleBaseChartReady: function handleBaseChartReady(chart) {
1240
+ this.chart = chart;
1241
+ this.syncingLabelGraphics = false;
1242
+ if (chart && chart.off) {
1243
+ chart.off('finished', this.handleChartFinished);
1244
+ }
1245
+ if (chart && chart.on) {
1246
+ chart.on('finished', this.handleChartFinished);
1247
+ }
1248
+ this.$emit('ready', chart);
1249
+ },
1250
+ /**
1251
+ * 在真正进入 3D 能力时按需加载 echarts-gl。
1252
+ *
1253
+ * 返回 Promise 是为了让单测和后续业务扩展能准确等待状态变化;
1254
+ * 2D、已就绪、加载中三种场景会直接返回 resolved Promise。
1255
+ */
1256
+ ensurePie3DRuntimeIfNeeded: function ensurePie3DRuntimeIfNeeded() {
1257
+ var _this = this;
1258
+ if (!this.is3D || this.echartsGlStatus === 'ready' || this.echartsGlStatus === 'loading') {
1259
+ return Promise.resolve();
1260
+ }
1261
+ this.echartsGlStatus = 'loading';
1262
+ this.echartsGlErrorMessage = '';
1263
+ return ensureEchartsGlRuntime().then(function () {
1264
+ _this.echartsGlStatus = 'ready';
1265
+ })["catch"](function (error) {
1266
+ _this.echartsGlStatus = 'failed';
1267
+ _this.echartsGlErrorMessage = error && error.message ? error.message : '使用 3D 饼图需要安装 echarts-gl';
1268
+ _this.$emit('runtime-error', error);
1269
+ });
1270
+ },
1271
+ /**
1272
+ * ECharts 完成 3D 渲染后再同步引导线。
1273
+ * 引导线需要依赖 grid3D 相机和投影矩阵,不能在 option 构建阶段静态计算。
1274
+ */
1275
+ handleChartFinished: function handleChartFinished() {
1276
+ if (this.syncingLabelGraphics) {
1277
+ this.syncingLabelGraphics = false;
1278
+ return;
1279
+ }
1280
+ this.syncPieLabelGraphics();
1281
+ },
1282
+ /**
1283
+ * 基于当前 3D surface 扇区生成 graphic 引导线。
1284
+ * 非 3D 或未开启 showLabelLine 时会清空 graphic,避免旧引导线残留。
1285
+ */
1286
+ syncPieLabelGraphics: function syncPieLabelGraphics() {
1287
+ if (!this.chart || !this.chart.setOption) {
1288
+ return;
1289
+ }
1290
+ var shouldShowLabelGraphics = this.dimension === '3d' && this.showLabelLine && !this.chartEmpty && this.isEchartsGlReady;
1291
+ var graphic = shouldShowLabelGraphics ? buildPieLabelGraphics(this.chart, this.chartOption.series, colors._objectSpread2(colors._objectSpread2({}, this.labelOptions), {}, {
1292
+ show: true
1293
+ })) : [];
1294
+ this.syncingLabelGraphics = true;
1295
+ this.chart.setOption({
1296
+ graphic: graphic
1297
+ }, {
1298
+ replaceMerge: ['graphic'],
1299
+ silent: true
1300
+ });
1301
+ }
1302
+ }
1303
+ };
1304
+
1305
+ var css_248z = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
1306
+ withInstall.styleInject(css_248z);
1307
+
1308
+ /* script */
1309
+ var __vue_script__ = script;
1310
+ /* template */
1311
+ var __vue_render__ = function __vue_render__() {
1312
+ var _vm = this;
1313
+ var _h = _vm.$createElement;
1314
+ var _c = _vm._self._c || _h;
1315
+ return _c("BaseChart", {
1316
+ attrs: {
1317
+ option: _vm.chartOption,
1318
+ loading: _vm.chartLoading,
1319
+ empty: _vm.chartEmpty,
1320
+ "empty-text": _vm.chartEmptyText,
1321
+ height: _vm.height
1322
+ },
1323
+ on: {
1324
+ "chart-click": function chartClick($event) {
1325
+ return _vm.$emit("chart-click", $event);
1326
+ },
1327
+ ready: _vm.handleBaseChartReady
1328
+ }
1329
+ });
1330
+ };
1331
+ var __vue_staticRenderFns__ = [];
1332
+ __vue_render__._withStripped = true;
1333
+
1334
+ /* style */
1335
+ var __vue_inject_styles__ = undefined;
1336
+ /* scoped */
1337
+ var __vue_scope_id__ = "data-v-815905b6";
1338
+ /* module identifier */
1339
+ var __vue_module_identifier__ = undefined;
1340
+ /* functional template */
1341
+ var __vue_is_functional_template__ = false;
1342
+ /* style inject */
1343
+
1344
+ /* style inject SSR */
1345
+
1346
+ /* style inject shadow dom */
1347
+
1348
+ var __vue_component__ = /*#__PURE__*/withInstall.normalizeComponent({
1349
+ render: __vue_render__,
1350
+ staticRenderFns: __vue_staticRenderFns__
1351
+ }, __vue_inject_styles__, __vue_script__, __vue_scope_id__, __vue_is_functional_template__, __vue_module_identifier__, false, undefined, undefined, undefined);
1352
+ var PieChart = __vue_component__;
1353
+
1354
+ exports.PieChart = PieChart;