jojotaoo_components 0.0.1-beta.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.
Files changed (115) hide show
  1. package/dist/assets/maps/heilongjiang.json +25543 -0
  2. package/dist/components/charts/BackgroundCard.vue.d.ts +17 -0
  3. package/dist/components/charts/BackgroundCard.vue.d.ts.map +1 -0
  4. package/dist/components/charts/BackgroundCard.vue.js +140 -0
  5. package/dist/components/charts/BarChart.vue.d.ts +20 -0
  6. package/dist/components/charts/BarChart.vue.d.ts.map +1 -0
  7. package/dist/components/charts/BarChart.vue.js +109 -0
  8. package/dist/components/charts/CustomLegend.vue.d.ts +28 -0
  9. package/dist/components/charts/CustomLegend.vue.d.ts.map +1 -0
  10. package/dist/components/charts/CustomLegend.vue.js +198 -0
  11. package/dist/components/charts/DataFetchManager.vue.d.ts +10 -0
  12. package/dist/components/charts/DataFetchManager.vue.d.ts.map +1 -0
  13. package/dist/components/charts/DataFetchManager.vue.js +127 -0
  14. package/dist/components/charts/DateTimeDisplay.vue.d.ts +17 -0
  15. package/dist/components/charts/DateTimeDisplay.vue.d.ts.map +1 -0
  16. package/dist/components/charts/DateTimeDisplay.vue.js +112 -0
  17. package/dist/components/charts/GroupComponent.vue.d.ts +13 -0
  18. package/dist/components/charts/GroupComponent.vue.d.ts.map +1 -0
  19. package/dist/components/charts/GroupComponent.vue.js +106 -0
  20. package/dist/components/charts/GroupPreview.vue.d.ts +10 -0
  21. package/dist/components/charts/GroupPreview.vue.d.ts.map +1 -0
  22. package/dist/components/charts/GroupPreview.vue.js +76 -0
  23. package/dist/components/charts/HeaderLineChart.vue.d.ts +17 -0
  24. package/dist/components/charts/HeaderLineChart.vue.d.ts.map +1 -0
  25. package/dist/components/charts/HeaderLineChart.vue.js +62 -0
  26. package/dist/components/charts/ImageDisplay.vue.d.ts +17 -0
  27. package/dist/components/charts/ImageDisplay.vue.d.ts.map +1 -0
  28. package/dist/components/charts/ImageDisplay.vue.js +106 -0
  29. package/dist/components/charts/LineChart.vue.d.ts +20 -0
  30. package/dist/components/charts/LineChart.vue.d.ts.map +1 -0
  31. package/dist/components/charts/LineChart.vue.js +116 -0
  32. package/dist/components/charts/MapChart.vue.d.ts +22 -0
  33. package/dist/components/charts/MapChart.vue.d.ts.map +1 -0
  34. package/dist/components/charts/MapChart.vue.js +680 -0
  35. package/dist/components/charts/PieChart.vue.d.ts +20 -0
  36. package/dist/components/charts/PieChart.vue.d.ts.map +1 -0
  37. package/dist/components/charts/PieChart.vue.js +106 -0
  38. package/dist/components/charts/PieGridChart.vue.d.ts +20 -0
  39. package/dist/components/charts/PieGridChart.vue.d.ts.map +1 -0
  40. package/dist/components/charts/PieGridChart.vue.js +214 -0
  41. package/dist/components/charts/RiskScrollList.vue.d.ts +15 -0
  42. package/dist/components/charts/RiskScrollList.vue.d.ts.map +1 -0
  43. package/dist/components/charts/RiskScrollList.vue.js +294 -0
  44. package/dist/components/charts/ScrollList.vue.d.ts +15 -0
  45. package/dist/components/charts/ScrollList.vue.d.ts.map +1 -0
  46. package/dist/components/charts/ScrollList.vue.js +149 -0
  47. package/dist/components/charts/TextDisplay.vue.d.ts +17 -0
  48. package/dist/components/charts/TextDisplay.vue.d.ts.map +1 -0
  49. package/dist/components/charts/TextDisplay.vue.js +209 -0
  50. package/dist/composables/useChartDataFetch.d.ts +18 -0
  51. package/dist/composables/useChartDataFetch.d.ts.map +1 -0
  52. package/dist/composables/useChartDataFetch.js +131 -0
  53. package/dist/composables/useComponentRender.d.ts +20 -0
  54. package/dist/composables/useComponentRender.d.ts.map +1 -0
  55. package/dist/composables/useComponentRender.js +45 -0
  56. package/dist/composables/useDataPondManager.d.ts +20 -0
  57. package/dist/composables/useDataPondManager.d.ts.map +1 -0
  58. package/dist/composables/useDataPondManager.js +105 -0
  59. package/dist/composables/useECharts.d.ts +9 -0
  60. package/dist/composables/useECharts.d.ts.map +1 -0
  61. package/dist/composables/useECharts.js +118 -0
  62. package/dist/composables/useEventListener.d.ts +2 -0
  63. package/dist/composables/useEventListener.d.ts.map +1 -0
  64. package/dist/composables/useEventListener.js +5 -0
  65. package/dist/composables/useId.d.ts +3 -0
  66. package/dist/composables/useId.d.ts.map +1 -0
  67. package/dist/composables/useId.js +6 -0
  68. package/dist/composables/useInteractClear.d.ts +4 -0
  69. package/dist/composables/useInteractClear.d.ts.map +1 -0
  70. package/dist/composables/useInteractClear.js +26 -0
  71. package/dist/composables/useInteractDispatch.d.ts +5 -0
  72. package/dist/composables/useInteractDispatch.d.ts.map +1 -0
  73. package/dist/composables/useInteractDispatch.js +44 -0
  74. package/dist/composables/useInteractFilter.d.ts +5 -0
  75. package/dist/composables/useInteractFilter.d.ts.map +1 -0
  76. package/dist/composables/useInteractFilter.js +24 -0
  77. package/dist/composables/usePollingManager.d.ts +10 -0
  78. package/dist/composables/usePollingManager.d.ts.map +1 -0
  79. package/dist/composables/usePollingManager.js +42 -0
  80. package/dist/composables/useRequestMerge.d.ts +32 -0
  81. package/dist/composables/useRequestMerge.d.ts.map +1 -0
  82. package/dist/composables/useRequestMerge.js +131 -0
  83. package/dist/config/chartThemes.d.ts +26 -0
  84. package/dist/config/chartThemes.d.ts.map +1 -0
  85. package/dist/config/chartThemes.js +221 -0
  86. package/dist/config/componentDefinitions.d.ts +12 -0
  87. package/dist/config/componentDefinitions.d.ts.map +1 -0
  88. package/dist/config/componentDefinitions.js +503 -0
  89. package/dist/index.cjs +1 -0
  90. package/dist/index.css +2 -0
  91. package/dist/index.d.ts +33 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +38 -0
  94. package/dist/stores/dashboard.d.ts +3240 -0
  95. package/dist/stores/dashboard.d.ts.map +1 -0
  96. package/dist/stores/dashboard.js +666 -0
  97. package/dist/stores/utils.d.ts +15 -0
  98. package/dist/stores/utils.d.ts.map +1 -0
  99. package/dist/stores/utils.js +28 -0
  100. package/dist/types/canvas.d.ts +36 -0
  101. package/dist/types/canvas.d.ts.map +1 -0
  102. package/dist/types/canvas.js +1 -0
  103. package/dist/types/component.d.ts +175 -0
  104. package/dist/types/component.d.ts.map +1 -0
  105. package/dist/types/component.js +63 -0
  106. package/dist/types/events.d.ts +43 -0
  107. package/dist/types/events.d.ts.map +1 -0
  108. package/dist/types/events.js +1 -0
  109. package/dist/types/index.d.ts +5 -0
  110. package/dist/types/index.d.ts.map +1 -0
  111. package/dist/types/index.js +4 -0
  112. package/dist/types/request.d.ts +43 -0
  113. package/dist/types/request.d.ts.map +1 -0
  114. package/dist/types/request.js +18 -0
  115. package/package.json +33 -0
@@ -0,0 +1,680 @@
1
+ import { ref, computed, toRef, onMounted, onUnmounted, watch, shallowRef, nextTick } from 'vue';
2
+ import * as echarts from 'echarts';
3
+ import { DEFAULT_CHART_STYLE } from '../../types';
4
+ import GeoJSON from '../../assets/maps/heilongjiang.json';
5
+ import { useInteractDispatch } from '../../composables/useInteractDispatch';
6
+ import { useInteractClear } from '../../composables/useInteractClear';
7
+ const props = withDefaults(defineProps(), {
8
+ componentId: '',
9
+ option: () => ({}),
10
+ width: 400,
11
+ height: 300,
12
+ bgColor: '#1e1e2e',
13
+ chartStyle: undefined,
14
+ geoKey: 'heilongjiang',
15
+ });
16
+ const optionRef = toRef(props, 'option');
17
+ const widthRef = toRef(props, 'width');
18
+ const heightRef = toRef(props, 'height');
19
+ const chartStyleRef = computed(() => props.chartStyle ?? DEFAULT_CHART_STYLE);
20
+ const containerBg = computed(() => {
21
+ const cs = chartStyleRef.value;
22
+ if (cs.backgroundOpacity < 1 && cs.backgroundColor !== 'transparent') {
23
+ const h = cs.backgroundColor.replace('#', '');
24
+ const r = parseInt(h.substring(0, 2), 16);
25
+ const g = parseInt(h.substring(2, 4), 16);
26
+ const b = parseInt(h.substring(4, 6), 16);
27
+ return `rgba(${r}, ${g}, ${b}, ${cs.backgroundOpacity})`;
28
+ }
29
+ return props.bgColor || cs.backgroundColor;
30
+ });
31
+ const chartRef = ref();
32
+ const chartInstance = shallowRef();
33
+ const minimapChartRef = ref();
34
+ const minimapInstance = shallowRef();
35
+ const componentIdRef = toRef(props, 'componentId');
36
+ const { dispatch } = useInteractDispatch(componentIdRef);
37
+ const { clearTargetInteractions } = useInteractClear();
38
+ const mapReady = ref(false);
39
+ const isZoomed = ref(false);
40
+ const currentCity = ref('');
41
+ const cityCenterMap = ref(new Map());
42
+ const cityZoomMap = ref(new Map());
43
+ const breathTimer = ref(null);
44
+ const DEFAULT_CENTER = [126.5, 47.5];
45
+ const DEFAULT_ZOOM = 1.2;
46
+ function formatTooltipValue(val, dim) {
47
+ if (val === null || val === undefined || val === '')
48
+ return '--';
49
+ const num = Number(val);
50
+ if (isNaN(num))
51
+ return String(val);
52
+ const formatted = num.toLocaleString();
53
+ const unit = chartStyleRef.value.series.mapTooltipDimensionUnits?.[dim];
54
+ return unit ? formatted + unit : formatted;
55
+ }
56
+ function buildOption() {
57
+ const cs = chartStyleRef.value ?? DEFAULT_CHART_STYLE;
58
+ const ds = optionRef.value.dataset ?? { dimensions: [], source: [] };
59
+ const source = ds.source ?? [];
60
+ const s = cs.series;
61
+ const result = {
62
+ backgroundColor: cs.backgroundOpacity < 1 ? 'transparent' : cs.backgroundColor,
63
+ };
64
+ if (cs.titleStyle.show && optionRef.value.title) {
65
+ result.title = {
66
+ text: optionRef.value.title,
67
+ textStyle: { color: cs.titleStyle.color, fontSize: cs.titleStyle.fontSize, fontWeight: 600 },
68
+ left: cs.titleStyle.left,
69
+ top: cs.titleStyle.top,
70
+ };
71
+ }
72
+ if (cs.tooltip.show && cs.tooltip.trigger !== 'none') {
73
+ result.tooltip = {
74
+ trigger: 'item',
75
+ backgroundColor: 'rgba(0,32,71,0.92)',
76
+ borderColor: 'rgba(0,200,255,0.4)',
77
+ borderWidth: 1,
78
+ borderRadius: 6,
79
+ padding: [12, 16],
80
+ textStyle: { color: '#ffffff', fontSize: 12 },
81
+ extraCssText: 'box-shadow: 0 4px 20px rgba(0,200,255,0.1); backdrop-filter: blur(8px);',
82
+ minWidth: 180,
83
+ formatter: (params) => {
84
+ if (params.seriesType !== 'map')
85
+ return '';
86
+ const row = params.data?.fullRow;
87
+ const dims = ds.dimensions ?? [];
88
+ if (!row || !dims.length)
89
+ return '';
90
+ let html = `<div style="border-bottom: 1px solid rgba(0,200,255,0.2); margin-bottom: 8px; padding-bottom: 6px;">`;
91
+ dims.forEach((dim, idx) => {
92
+ const val = row[idx] ?? '--';
93
+ const formatted = formatTooltipValue(val, dim);
94
+ if (idx === 0) {
95
+ html += `<span style="color: #00c8ff; font-size: 14px; font-weight: 600;">${dim}:${formatted}</span>`;
96
+ }
97
+ });
98
+ html += `</div>`;
99
+ dims.forEach((dim, idx) => {
100
+ if (idx === 0)
101
+ return;
102
+ const val = row[idx] ?? '--';
103
+ const formatted = formatTooltipValue(val, dim);
104
+ html += `<div style="color: rgba(255,255,255,0.7); font-size: 12px; line-height: 1.8;">${dim}:${formatted}</div>`;
105
+ });
106
+ return html;
107
+ },
108
+ };
109
+ }
110
+ if (s.mapVisualMapShow) {
111
+ result.visualMap = {
112
+ min: s.mapVisualMin,
113
+ max: s.mapVisualMax,
114
+ text: ['高', '低'],
115
+ textStyle: { color: s.mapLabelColor },
116
+ inRange: { color: s.mapVisualColors },
117
+ show: true,
118
+ calculable: true,
119
+ itemWidth: 18,
120
+ itemHeight: 120,
121
+ left: 'left',
122
+ bottom: 30,
123
+ };
124
+ }
125
+ result.series = [{
126
+ type: 'map',
127
+ map: props.geoKey,
128
+ roam: false, // 不允许缩放控制,设置为不允许缩放
129
+ scaleLimit: { min: 1, max: 10 },
130
+ selectedMode: 'single',
131
+ zoom: 1.2,
132
+ label: {
133
+ show: s.mapLabelShow,
134
+ color: s.mapLabelColor,
135
+ fontSize: s.mapLabelFontSize,
136
+ fontWeight: s.mapLabelFontWeight ?? 500,
137
+ position: 'top',
138
+ distance: 10,
139
+ },
140
+ select: {
141
+ label: { show: false, color: s.mapSelectLabelColor, fontWeight: 700, fontSize: (s.mapLabelFontSize ?? 11) + 1 },
142
+ itemStyle: {
143
+ areaColor: s.mapSelectColor,
144
+ borderColor: s.mapSelectBorderColor,
145
+ borderWidth: s.mapSelectBorderWidth,
146
+ shadowBlur: s.mapSelectShadowBlur,
147
+ shadowColor: s.mapSelectShadowColor,
148
+ shadowOffsetY: 0,
149
+ shadowOffsetX: 0,
150
+ },
151
+ },
152
+ emphasis: {
153
+ label: { show: false, fontSize: s.mapEmphasisLabelFontSize ?? 12, fontWeight: 700, color: s.mapEmphasisLabelColor },
154
+ itemStyle: {
155
+ areaColor: s.mapRegionHoverColor,
156
+ borderColor: s.mapHoverBorderColor,
157
+ borderWidth: s.mapHoverBorderWidth,
158
+ shadowBlur: s.mapHoverShadowBlur,
159
+ shadowColor: s.mapHoverShadowColor,
160
+ },
161
+ },
162
+ itemStyle: {
163
+ // areaColor: s.mapRegionColor,
164
+ areaColor: {
165
+ type: 'linear',
166
+ x: 0, // 起始点百分比(左下)
167
+ y: 0.5,
168
+ x2: 0, // 结束点百分比(右上)
169
+ y2: 0,
170
+ colorStops: [
171
+ { offset: 0, color: 'rgba(8,39,73,1)' }, // 0% 处为深蓝
172
+ { offset: 1, color: 'rgba(34,69,104,1)' } // 100% 处为浅蓝
173
+ ],
174
+ global: false // 是否使用像素坐标,通常设为false
175
+ }, //s.mapRegionColor,
176
+ borderColor: s.mapRegionBorderColor,
177
+ borderWidth: 1,
178
+ },
179
+ data: source.map((item) => {
180
+ const name = item?.[0] ?? '';
181
+ const isActive = currentCity.value && name === currentCity.value;
182
+ const isDimmed = currentCity.value && name !== currentCity.value;
183
+ return {
184
+ name,
185
+ value: item?.[1] ?? 0,
186
+ fullRow: item,
187
+ selected: isActive ? true : undefined,
188
+ itemStyle: isDimmed ? {
189
+ areaColor: 'rgba(8,22,42,0.9)',
190
+ borderColor: 'rgba(0,60,120,0.15)',
191
+ borderWidth: 1,
192
+ } : undefined,
193
+ };
194
+ }),
195
+ markPoint: s.mapMarkPointShow ? (() => {
196
+ const zoomScale = currentCity.value
197
+ ? Math.min(2.5, (cityZoomMap.value.get(currentCity.value) ?? 2.5) / DEFAULT_ZOOM)
198
+ : 1;
199
+ const baseSize = s.mapMarkPointSymbolSize * zoomScale;
200
+ const baseFontSize = Math.round(s.mapMarkPointLabelFontSize * zoomScale);
201
+ const activeMul = 1.15;
202
+ return {
203
+ symbol: 'circle',
204
+ symbolSize: baseSize,
205
+ z: 100,
206
+ itemStyle: {
207
+ color: s.mapMarkPointColor,
208
+ borderColor: 'rgba(255,255,255,0.3)',
209
+ borderWidth: 1,
210
+ shadowBlur: 8,
211
+ shadowColor: 'rgba(0,128,255,0.4)',
212
+ shadowOffsetY: 2,
213
+ },
214
+ emphasis: {
215
+ itemStyle: {
216
+ color: '#00c8ff',
217
+ shadowBlur: 12,
218
+ shadowColor: 'rgba(0,200,255,0.5)',
219
+ },
220
+ label: {
221
+ color: '#00c8ff',
222
+ fontWeight: 700,
223
+ },
224
+ },
225
+ label: {
226
+ show: s.mapMarkPointLabelShow,
227
+ formatter: '{b}',
228
+ position: 'top',
229
+ fontSize: baseFontSize,
230
+ fontWeight: 500,
231
+ color: 'rgba(255,255,255,0.75)',
232
+ padding: [2, 6],
233
+ borderRadius: 4,
234
+ distance: 12,
235
+ },
236
+ data: GeoJSON.features.map((item) => {
237
+ const isActive = item.properties.name === currentCity.value;
238
+ const isDimmed = currentCity.value && !isActive;
239
+ return {
240
+ name: item.properties.name,
241
+ coord: item.properties.center,
242
+ symbolSize: isActive ? baseSize * activeMul : undefined,
243
+ itemStyle: isDimmed ? {
244
+ color: 'rgba(0,128,255,0.05)',
245
+ } : undefined,
246
+ label: isActive ? {
247
+ fontSize: Math.round(baseFontSize * activeMul),
248
+ color: '#00c8ff',
249
+ fontWeight: 700,
250
+ textShadowColor: 'rgba(0,0,0)',
251
+ textShadowBlur: 4,
252
+ textShadowOffsetX: 1,
253
+ textShadowOffsetY: 1,
254
+ } : (isDimmed ? {
255
+ color: 'rgba(255,255,255,0.04)',
256
+ } : undefined),
257
+ };
258
+ }),
259
+ };
260
+ })() : undefined,
261
+ }];
262
+ if (currentCity.value) {
263
+ const zoom = cityZoomMap.value.get(currentCity.value);
264
+ const center = cityCenterMap.value.get(currentCity.value);
265
+ if (zoom && center) {
266
+ result.series[0].center = center;
267
+ result.series[0].zoom = zoom;
268
+ result.series[0].animationDurationUpdate = 800;
269
+ }
270
+ }
271
+ return result;
272
+ }
273
+ function getCityFeature(name) {
274
+ return GeoJSON.features.find((f) => f.properties?.name === name);
275
+ }
276
+ function computeCityZoom(name) {
277
+ const feature = getCityFeature(name);
278
+ if (!feature?.geometry?.coordinates)
279
+ return 2.5;
280
+ const multiPolygon = feature.geometry.coordinates;
281
+ let minLat = Infinity, maxLat = -Infinity, minLng = Infinity, maxLng = -Infinity;
282
+ multiPolygon.forEach((polygon) => {
283
+ const ring = polygon[0];
284
+ if (!ring)
285
+ return;
286
+ ring.forEach((p) => {
287
+ minLng = Math.min(minLng, p[0]);
288
+ maxLng = Math.max(maxLng, p[0]);
289
+ minLat = Math.min(minLat, p[1]);
290
+ maxLat = Math.max(maxLat, p[1]);
291
+ });
292
+ });
293
+ if (!isFinite(minLng))
294
+ return 2.5;
295
+ const w = Math.max(maxLng - minLng, 0.5), h = Math.max(maxLat - minLat, 0.5);
296
+ const zx = 13.5 / (w / 0.55), zy = 10.2 / (h / 0.55);
297
+ return Math.min(8, Math.max(1.3, Math.round(Math.min(zx, zy) * 10) / 10));
298
+ }
299
+ function buildBreathGraphicElements(name) {
300
+ const feature = getCityFeature(name);
301
+ if (!feature || !chartInstance.value)
302
+ return [];
303
+ const ci = chartInstance.value;
304
+ const multiPolygon = feature.geometry?.coordinates;
305
+ if (!multiPolygon)
306
+ return [];
307
+ const elements = [];
308
+ const zoomScale = Math.min(2.5, (cityZoomMap.value.get(name) ?? 2.5) / DEFAULT_ZOOM);
309
+ multiPolygon.forEach((polygon, polyIdx) => {
310
+ const outerRing = polygon[0];
311
+ if (!outerRing || !outerRing.length)
312
+ return;
313
+ const points = outerRing.map((p) => ci.convertToPixel({ seriesIndex: 0 }, [p[0], p[1]]));
314
+ elements.push({
315
+ id: `breath-poly-${polyIdx}`,
316
+ type: 'polygon',
317
+ z: 100,
318
+ silent: true,
319
+ shape: { points },
320
+ style: {
321
+ fill: 'transparent',
322
+ stroke: '#00c8ff',
323
+ lineWidth: 2 * zoomScale,
324
+ shadowBlur: Math.round(8 * zoomScale),
325
+ shadowColor: 'rgba(0,128,255,0.45)',
326
+ },
327
+ keyframeAnimation: {
328
+ loop: true,
329
+ duration: 500,
330
+ keyframes: [
331
+ {
332
+ percent: 0,
333
+ style: {
334
+ lineWidth: Math.round(4 * zoomScale),
335
+ shadowBlur: Math.round(8 * zoomScale),
336
+ shadowColor: 'rgba(0,128,255,0.45)',
337
+ },
338
+ },
339
+ {
340
+ percent: 0.5,
341
+ style: {
342
+ lineWidth: Math.round(4 * zoomScale),
343
+ shadowBlur: Math.round(30 * zoomScale),
344
+ shadowColor: 'rgba(0,200,255,0.6)',
345
+ },
346
+ },
347
+ {
348
+ percent: 1,
349
+ style: {
350
+ lineWidth: Math.round(4 * zoomScale),
351
+ shadowBlur: Math.round(40 * zoomScale),
352
+ shadowColor: '#00c8ff',
353
+ },
354
+ },
355
+ ],
356
+ },
357
+ });
358
+ });
359
+ const center = cityCenterMap.value.get(name);
360
+ if (center) {
361
+ const px = ci.convertToPixel({ seriesIndex: 0 }, [center[0], center[1]]);
362
+ if (px) {
363
+ const [cx, cy] = px;
364
+ const RIPPLE_R = Math.round(4 * zoomScale);
365
+ const RIPPLE_R_MAX = Math.round(20 * zoomScale);
366
+ for (let i = 0; i < 3; i++) {
367
+ elements.push({
368
+ id: `breath-ripple-${i}`,
369
+ type: 'circle',
370
+ z: 101,
371
+ silent: true,
372
+ shape: { cx, cy, r: RIPPLE_R },
373
+ style: {
374
+ fill: 'none',
375
+ stroke: `rgba(0,200,255,${(0.8 - i * 0.15).toFixed(2)})`,
376
+ lineWidth: (1.5 - i * 0.5) * zoomScale,
377
+ },
378
+ keyframeAnimation: {
379
+ loop: true,
380
+ duration: 2000,
381
+ delay: i * 600,
382
+ keyframes: [
383
+ {
384
+ percent: 0,
385
+ shape: { r: RIPPLE_R },
386
+ style: { lineWidth: (1.5 - i * 0.5) * zoomScale, stroke: `rgba(0,200,255,${(0.8 - i * 0.15).toFixed(2)})` },
387
+ },
388
+ {
389
+ percent: 1,
390
+ shape: { r: RIPPLE_R_MAX },
391
+ style: { lineWidth: 0.3, stroke: 'rgba(0,200,255,0)' },
392
+ },
393
+ ],
394
+ },
395
+ });
396
+ }
397
+ elements.push({
398
+ id: 'breath-pulse-dot',
399
+ type: 'circle',
400
+ z: 102,
401
+ silent: true,
402
+ shape: { cx, cy, r: Math.round(4 * zoomScale) },
403
+ style: {
404
+ fill: '#00c8ff',
405
+ shadowBlur: Math.round(8 * zoomScale),
406
+ shadowColor: 'rgba(0,200,255,0.5)',
407
+ },
408
+ keyframeAnimation: {
409
+ loop: true,
410
+ duration: 1500,
411
+ keyframes: [
412
+ { percent: 0, shape: { r: Math.round(4 * zoomScale) }, style: { opacity: 1, shadowBlur: Math.round(8 * zoomScale), shadowColor: 'rgba(0,200,255,0.5)' } },
413
+ { percent: 0.5, shape: { r: Math.round(5 * zoomScale) }, style: { opacity: 0.8, shadowBlur: Math.round(12 * zoomScale), shadowColor: 'rgba(0,200,255,0.8)' } },
414
+ { percent: 1, shape: { r: Math.round(4 * zoomScale) }, style: { opacity: 1, shadowBlur: Math.round(8 * zoomScale), shadowColor: 'rgba(0,200,255,0.5)' } },
415
+ ],
416
+ },
417
+ });
418
+ }
419
+ }
420
+ return elements;
421
+ }
422
+ function applyBreath(name, delayMs = 0) {
423
+ removeBreath();
424
+ if (!name || !chartInstance.value)
425
+ return;
426
+ const inject = () => {
427
+ const elements = buildBreathGraphicElements(name);
428
+ if (elements.length) {
429
+ chartInstance.value?.setOption({ graphic: { elements } });
430
+ }
431
+ };
432
+ if (delayMs > 0) {
433
+ breathTimer.value = setTimeout(inject, delayMs);
434
+ }
435
+ else {
436
+ inject();
437
+ }
438
+ }
439
+ function removeBreath() {
440
+ if (breathTimer.value !== null) {
441
+ clearTimeout(breathTimer.value);
442
+ breathTimer.value = null;
443
+ }
444
+ chartInstance.value?.setOption({ graphic: { elements: [] } }, { replaceMerge: ['graphic'] });
445
+ }
446
+ function initChart() {
447
+ if (!chartRef.value || !mapReady.value)
448
+ return;
449
+ chartInstance.value?.dispose();
450
+ chartInstance.value = echarts.init(chartRef.value, undefined, { renderer: 'canvas' });
451
+ chartInstance.value.setOption(buildOption());
452
+ chartInstance.value.on('click', handleMapClick);
453
+ }
454
+ function updateChart() {
455
+ if (!chartInstance.value || !mapReady.value)
456
+ return;
457
+ chartInstance.value.setOption(buildOption(), { notMerge: true });
458
+ if (currentCity.value) {
459
+ applyBreath(currentCity.value);
460
+ }
461
+ updateMinimap();
462
+ }
463
+ function handleResize() {
464
+ chartInstance.value?.resize();
465
+ }
466
+ function handleMapClick(params) {
467
+ if (params.seriesType !== 'map')
468
+ return;
469
+ const name = params.name;
470
+ const center = cityCenterMap.value.get(name);
471
+ if (!center)
472
+ return;
473
+ clearTargetInteractions(componentIdRef.value);
474
+ if (currentCity.value === name) {
475
+ currentCity.value = '';
476
+ removeBreath();
477
+ updateChart();
478
+ isZoomed.value = false;
479
+ return;
480
+ }
481
+ else {
482
+ currentCity.value = name;
483
+ updateChart();
484
+ applyBreath(name, 800);
485
+ isZoomed.value = true;
486
+ }
487
+ dispatch('click', { name, value: params.value });
488
+ }
489
+ function handleResetView() {
490
+ currentCity.value = '';
491
+ removeBreath();
492
+ updateChart();
493
+ isZoomed.value = false;
494
+ clearTargetInteractions(componentIdRef.value);
495
+ }
496
+ function buildMinimapOption() {
497
+ const cs = chartStyleRef.value ?? DEFAULT_CHART_STYLE;
498
+ const s = cs.series;
499
+ const source = (optionRef.value.dataset?.source ?? []);
500
+ const isActive = (n) => currentCity.value && n === currentCity.value;
501
+ const isDimmed = (n) => currentCity.value && n !== currentCity.value;
502
+ const series = {
503
+ type: 'map',
504
+ map: props.geoKey,
505
+ roam: false,
506
+ selectedMode: false,
507
+ zoom: DEFAULT_ZOOM,
508
+ animation: false,
509
+ animationDurationUpdate: 0,
510
+ label: { show: false },
511
+ itemStyle: {
512
+ areaColor: {
513
+ type: 'linear', x: 0, y: 0.5, x2: 0, y2: 0,
514
+ colorStops: [
515
+ { offset: 0, color: 'rgba(8,39,73,1)' },
516
+ { offset: 1, color: 'rgba(34,69,104,1)' },
517
+ ],
518
+ global: false,
519
+ },
520
+ borderColor: s.mapRegionBorderColor,
521
+ borderWidth: 0.5,
522
+ },
523
+ data: source.map((item) => {
524
+ const name = item?.[0] ?? '';
525
+ const isThisActive = isActive(name);
526
+ return {
527
+ name,
528
+ value: item?.[1] ?? 0,
529
+ itemStyle: isDimmed(name) ? {
530
+ areaColor: 'rgba(8,22,42,0.9)',
531
+ borderColor: 'rgba(0,60,120,0.15)',
532
+ borderWidth: 0.5,
533
+ } : (isThisActive ? {
534
+ areaColor: s.mapSelectColor,
535
+ borderColor: s.mapSelectBorderColor,
536
+ borderWidth: s.mapSelectBorderWidth,
537
+ shadowBlur: s.mapSelectShadowBlur,
538
+ shadowColor: s.mapSelectShadowColor,
539
+ shadowOffsetY: 0,
540
+ shadowOffsetX: 0,
541
+ } : undefined),
542
+ };
543
+ }),
544
+ markPoint: s.mapMarkPointShow ? {
545
+ symbol: 'circle',
546
+ symbolSize: 5,
547
+ z: 100,
548
+ itemStyle: { color: s.mapMarkPointColor },
549
+ data: GeoJSON.features.map((item) => ({
550
+ name: item.properties.name,
551
+ coord: item.properties.center,
552
+ symbolSize: isActive(item.properties.name) ? 7 : undefined,
553
+ itemStyle: isDimmed(item.properties.name) ? { color: 'rgba(0,128,255,0.05)' } : undefined,
554
+ })),
555
+ } : undefined,
556
+ };
557
+ return { backgroundColor: 'transparent', series: [series] };
558
+ }
559
+ function initMinimap() {
560
+ if (!minimapChartRef.value)
561
+ return;
562
+ minimapInstance.value?.dispose();
563
+ minimapInstance.value = echarts.init(minimapChartRef.value, undefined, { renderer: 'canvas' });
564
+ minimapInstance.value.setOption(buildMinimapOption());
565
+ minimapInstance.value.on('click', () => {
566
+ handleResetView();
567
+ });
568
+ }
569
+ function updateMinimap() {
570
+ if (!minimapInstance.value || !mapReady.value)
571
+ return;
572
+ minimapInstance.value.setOption(buildMinimapOption(), { notMerge: true });
573
+ }
574
+ const resizeObserver = new ResizeObserver(handleResize);
575
+ onMounted(async () => {
576
+ if (chartRef.value) {
577
+ resizeObserver.observe(chartRef.value);
578
+ }
579
+ try {
580
+ if (!echarts.getMap(props.geoKey)) {
581
+ echarts.registerMap(props.geoKey, GeoJSON);
582
+ }
583
+ const features = GeoJSON.features ?? [];
584
+ const map = new Map();
585
+ features.forEach((f) => {
586
+ if (f.properties?.name && f.properties?.center) {
587
+ map.set(f.properties.name, f.properties.center);
588
+ }
589
+ });
590
+ cityCenterMap.value = map;
591
+ const zm = new Map();
592
+ features.forEach((f) => {
593
+ if (f.properties?.name)
594
+ zm.set(f.properties.name, computeCityZoom(f.properties.name));
595
+ });
596
+ cityZoomMap.value = zm;
597
+ mapReady.value = true;
598
+ await nextTick();
599
+ initChart();
600
+ }
601
+ catch (e) {
602
+ console.warn(`[MapChart] GeoJSON for "${props.geoKey}" not found.`, e);
603
+ }
604
+ });
605
+ onUnmounted(() => {
606
+ removeBreath();
607
+ resizeObserver.disconnect();
608
+ chartInstance.value?.dispose();
609
+ minimapInstance.value?.dispose();
610
+ });
611
+ watch(() => [widthRef.value, heightRef.value], () => {
612
+ requestAnimationFrame(() => chartInstance.value?.resize());
613
+ });
614
+ watch(() => optionRef.value, updateChart, { deep: true });
615
+ watch(() => JSON.stringify(chartStyleRef.value), updateChart);
616
+ watch(currentCity, async (val) => {
617
+ if (val) {
618
+ await nextTick();
619
+ if (!minimapInstance.value) {
620
+ initMinimap();
621
+ }
622
+ else {
623
+ updateMinimap();
624
+ }
625
+ }
626
+ else {
627
+ minimapInstance.value?.dispose();
628
+ minimapInstance.value = undefined;
629
+ }
630
+ });
631
+ const __VLS_defaults = {
632
+ componentId: '',
633
+ option: () => ({}),
634
+ width: 400,
635
+ height: 300,
636
+ bgColor: '#1e1e2e',
637
+ chartStyle: undefined,
638
+ geoKey: 'heilongjiang',
639
+ };
640
+ const __VLS_ctx = {
641
+ ...{},
642
+ ...{},
643
+ ...{},
644
+ ...{},
645
+ };
646
+ let __VLS_components;
647
+ let __VLS_intrinsics;
648
+ let __VLS_directives;
649
+ /** @type {__VLS_StyleScopedClasses['map-back-btn']} */ ;
650
+ __VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
651
+ ...{ class: "map-chart" },
652
+ ...{ style: ({ backgroundColor: __VLS_ctx.containerBg }) },
653
+ });
654
+ /** @type {__VLS_StyleScopedClasses['map-chart']} */ ;
655
+ __VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
656
+ ref: "chartRef",
657
+ ...{ class: "map-chart-canvas" },
658
+ });
659
+ /** @type {__VLS_StyleScopedClasses['map-chart-canvas']} */ ;
660
+ if (__VLS_ctx.currentCity) {
661
+ __VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
662
+ ...{ onClick: (__VLS_ctx.handleResetView) },
663
+ ref: "minimapRef",
664
+ ...{ class: "map-minimap" },
665
+ });
666
+ /** @type {__VLS_StyleScopedClasses['map-minimap']} */ ;
667
+ __VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
668
+ ...{ onClick: (__VLS_ctx.handleResetView) },
669
+ ref: "minimapChartRef",
670
+ ...{ class: "minimap-chart-canvas" },
671
+ });
672
+ /** @type {__VLS_StyleScopedClasses['minimap-chart-canvas']} */ ;
673
+ }
674
+ // @ts-ignore
675
+ [containerBg, currentCity, handleResetView, handleResetView,];
676
+ const __VLS_export = (await import('vue')).defineComponent({
677
+ __typeProps: {},
678
+ props: {},
679
+ });
680
+ export default {};
@@ -0,0 +1,20 @@
1
+ import type { ChartStyleConfig } from '../../types';
2
+ type __VLS_Props = {
3
+ componentId?: string;
4
+ option?: Record<string, any>;
5
+ width?: number;
6
+ height?: number;
7
+ bgColor?: string;
8
+ chartStyle?: ChartStyleConfig;
9
+ };
10
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
11
+ bgColor: string;
12
+ height: number;
13
+ option: Record<string, any>;
14
+ chartStyle: ChartStyleConfig;
15
+ width: number;
16
+ componentId: string;
17
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
+ declare const _default: typeof __VLS_export;
19
+ export default _default;
20
+ //# sourceMappingURL=PieChart.vue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PieChart.vue.d.ts","sourceRoot":"","sources":["../../../src/components/charts/PieChart.vue"],"names":[],"mappings":"AAkHA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAGnD,KAAK,WAAW,GAAG;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,gBAAgB,CAAA;CAC9B,CAAC;AAwHF,QAAA,MAAM,YAAY;aA1HN,MAAM;YADP,MAAM;YAFN,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;gBAIf,gBAAgB;WAHrB,MAAM;iBAFA,MAAM;6EAiIpB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}