vue2-client 1.18.7 → 1.18.8

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 (93) hide show
  1. package/.eslintrc.js +90 -90
  2. package/Components.md +60 -60
  3. package/docs/index.md +30 -30
  4. package/index.js +31 -31
  5. package/jest-transform-stub.js +8 -8
  6. package/jest.setup.js +7 -7
  7. package/package.json +1 -1
  8. package/src/assets/img/querySlotDemo.svg +15 -15
  9. package/src/base-client/components/common/AmapMarker/AmapPointRendering.vue +120 -120
  10. package/src/base-client/components/common/CitySelect/index.js +3 -3
  11. package/src/base-client/components/common/CitySelect/index.md +109 -109
  12. package/src/base-client/components/common/CreateQuery/CreateQuery.vue +669 -669
  13. package/src/base-client/components/common/CreateQuery/index.js +3 -3
  14. package/src/base-client/components/common/CreateQuery/index.md +42 -42
  15. package/src/base-client/components/common/CreateSimpleFormQuery/index.js +3 -3
  16. package/src/base-client/components/common/CreateSimpleFormQuery/index.md +42 -42
  17. package/src/base-client/components/common/FormGroupEdit/index.js +3 -3
  18. package/src/base-client/components/common/FormGroupEdit/index.md +43 -43
  19. package/src/base-client/components/common/FormGroupQuery/FormGroupQuery.vue +166 -166
  20. package/src/base-client/components/common/FormGroupQuery/index.js +3 -3
  21. package/src/base-client/components/common/FormGroupQuery/index.md +43 -43
  22. package/src/base-client/components/common/JSONToTree/jsontotree.vue +271 -271
  23. package/src/base-client/components/common/PersonSetting/PersonSetting.vue +208 -208
  24. package/src/base-client/components/common/PersonSetting/index.js +3 -3
  25. package/src/base-client/components/common/Tree/Tree.vue +149 -149
  26. package/src/base-client/components/common/Tree/index.js +2 -2
  27. package/src/base-client/components/common/Upload/index.js +3 -3
  28. package/src/base-client/components/common/XAddNativeForm/index.md +146 -146
  29. package/src/base-client/components/common/XCard/XCard.vue +64 -64
  30. package/src/base-client/components/common/XDataDrawer/XDataDrawer.vue +180 -180
  31. package/src/base-client/components/common/XDataDrawer/index.js +3 -3
  32. package/src/base-client/components/common/XDataDrawer/index.md +41 -41
  33. package/src/base-client/components/common/XDescriptions/index.js +3 -3
  34. package/src/base-client/components/common/XDescriptions/index.md +83 -83
  35. package/src/base-client/components/common/XForm/index.md +178 -178
  36. package/src/base-client/components/common/XFormTable/XFormTable.vue +2 -4
  37. package/src/base-client/components/common/XFormTable/demo.vue +2 -2
  38. package/src/base-client/components/common/XStepView/XStepView.vue +252 -252
  39. package/src/base-client/components/common/XStepView/index.js +3 -3
  40. package/src/base-client/components/common/XStepView/index.md +31 -31
  41. package/src/base-client/components/common/XTable/CustomFuncCel.vue +6 -1
  42. package/src/base-client/components/common/XTable/index.md +255 -255
  43. package/src/base-client/components/his/HChart/HChart.vue +504 -53
  44. package/src/base-client/components/system/DictionaryDetailsView/DictionaryDetailsView.vue +232 -232
  45. package/src/base-client/plugins/Config.js +19 -19
  46. package/src/base-client/plugins/tabs-page-plugin.js +39 -39
  47. package/src/components/Charts/Bar.vue +62 -62
  48. package/src/components/Charts/ChartCard.vue +134 -134
  49. package/src/components/Charts/Liquid.vue +67 -67
  50. package/src/components/Charts/MiniArea.vue +39 -39
  51. package/src/components/Charts/MiniBar.vue +39 -39
  52. package/src/components/Charts/MiniProgress.vue +75 -75
  53. package/src/components/Charts/MiniSmoothArea.vue +40 -40
  54. package/src/components/Charts/Radar.vue +68 -68
  55. package/src/components/Charts/RankList.vue +77 -77
  56. package/src/components/Charts/TagCloud.vue +113 -113
  57. package/src/components/Charts/TransferBar.vue +64 -64
  58. package/src/components/Charts/Trend.vue +82 -82
  59. package/src/components/Charts/chart.less +12 -12
  60. package/src/components/Charts/smooth.area.less +13 -13
  61. package/src/components/NumberInfo/NumberInfo.vue +54 -54
  62. package/src/components/NumberInfo/index.js +3 -3
  63. package/src/components/NumberInfo/index.less +54 -54
  64. package/src/components/NumberInfo/index.md +43 -43
  65. package/src/components/card/ChartCard.vue +79 -79
  66. package/src/components/chart/Bar.vue +60 -60
  67. package/src/components/chart/MiniArea.vue +67 -67
  68. package/src/components/chart/MiniBar.vue +59 -59
  69. package/src/components/chart/MiniProgress.vue +57 -57
  70. package/src/components/chart/Radar.vue +80 -80
  71. package/src/components/chart/RankingList.vue +60 -60
  72. package/src/components/chart/Trend.vue +79 -79
  73. package/src/components/chart/index.less +9 -9
  74. package/src/components/checkbox/ColorCheckbox.vue +157 -157
  75. package/src/components/index.js +36 -36
  76. package/src/components/input/IInput.vue +66 -66
  77. package/src/components/menu/SideMenu.vue +75 -75
  78. package/src/components/menu/menu.js +273 -273
  79. package/src/components/tool/AStepItem.vue +60 -60
  80. package/src/layouts/CommonLayout.vue +56 -56
  81. package/src/layouts/header/HeaderNotice.vue +177 -177
  82. package/src/lib.js +1 -1
  83. package/src/mock/extend/index.js +84 -84
  84. package/src/mock/goods/index.js +108 -108
  85. package/src/pages/dashboard/workplace/WorkPlace.vue +141 -141
  86. package/src/pages/system/dictionary/index.vue +44 -44
  87. package/src/pages/system/monitor/loginInfor/index.vue +37 -37
  88. package/src/pages/system/monitor/operLog/index.vue +37 -37
  89. package/src/services/api/cas.js +79 -79
  90. package/src/store/modules/setting.js +119 -119
  91. package/src/utils/authority-utils.js +85 -85
  92. package/src/utils/errorCode.js +6 -6
  93. package//350/277/201/347/247/273/346/227/245/345/277/227.md +15 -15
@@ -1,8 +1,47 @@
1
1
  <template>
2
2
  <div class="h-chart-configurable" :class="wrapperClassObject">
3
3
  <x-title v-if="chartTitle" v-bind="xTitleAttrs" :title="chartTitle" />
4
- <!-- 图表挂载容器 -->
5
- <div ref="chartRef" class="chart-canvas" :style="chartStyle"></div>
4
+ <div class="chart-layout">
5
+ <div v-if="showSideList" class="chart-side-list">
6
+ <div v-if="sideListLabel" class="side-list-label">{{ sideListLabel }}</div>
7
+ <div class="side-list-menu">
8
+ <button
9
+ v-for="option in sideListOptions"
10
+ :key="option.value"
11
+ type="button"
12
+ class="side-list-item"
13
+ :class="{ 'is-active': option.value === selectedSideListValue }"
14
+ @click="handleListChange(option.value)"
15
+ >
16
+ <span class="item-label">{{ option.label }}</span>
17
+ <span v-if="option.badge" class="item-badge">{{ option.badge }}</span>
18
+ </button>
19
+ </div>
20
+ </div>
21
+ <div class="chart-main">
22
+ <div v-if="showRadioFilter" class="chart-toolbar">
23
+ <span v-if="radioFilterLabel" class="toolbar-label">{{ radioFilterLabel }}</span>
24
+ <div class="toolbar-radio-group">
25
+ <label
26
+ v-for="option in radioFilterOptions"
27
+ :key="option.value"
28
+ class="radio-pill"
29
+ :class="{ 'is-active': option.value === selectedFilterValue }"
30
+ >
31
+ <input
32
+ type="radio"
33
+ class="radio-pill-input"
34
+ :value="option.value"
35
+ :checked="option.value === selectedFilterValue"
36
+ @change="handleFilterChange(option.value)"
37
+ />
38
+ <span>{{ option.label }}</span>
39
+ </label>
40
+ </div>
41
+ </div>
42
+ <div ref="chartRef" class="chart-canvas" :style="chartStyle"></div>
43
+ </div>
44
+ </div>
6
45
  </div>
7
46
  </template>
8
47
 
@@ -47,6 +86,9 @@ const chartConfig = ref(null)
47
86
  const chartData = ref([])
48
87
  const loading = ref(false)
49
88
  let resizeObserver = null
89
+ const activePointIndex = ref(-1)
90
+ const selectedFilterValue = ref('')
91
+ const selectedSideListValue = ref('')
50
92
 
51
93
  // 记录上一次的 queryParamsName,用于比较变化
52
94
  const lastQueryParamsName = ref('')
@@ -66,6 +108,87 @@ const chartStyle = computed(() => {
66
108
  if (target.style && typeof target.style === 'object') return target.style
67
109
  return {}
68
110
  })
111
+ const radioFilterConfig = computed(() => props.config?.radioFilter || chartConfig.value?.radioFilter || null)
112
+ const radioFilterOptions = computed(() => {
113
+ const options = radioFilterConfig.value?.options
114
+ return Array.isArray(options) ? options : []
115
+ })
116
+ const radioFilterLabel = computed(() => radioFilterConfig.value?.label || '')
117
+ const showRadioFilter = computed(() => radioFilterOptions.value.length > 0)
118
+ const filterFieldName = computed(() => radioFilterConfig.value?.field || radioFilterConfig.value?.prop || 'dimension')
119
+ const filterQueryParams = computed(() => {
120
+ if (!showRadioFilter.value) return {}
121
+ const optionValue = selectedFilterValue.value
122
+ if (optionValue === undefined || optionValue === null || optionValue === '') return {}
123
+ return { [filterFieldName.value]: optionValue }
124
+ })
125
+ const sideListConfig = computed(() => props.config?.sideList || chartConfig.value?.sideList || null)
126
+ const sideListOptions = computed(() => {
127
+ const options = sideListConfig.value?.options
128
+ return Array.isArray(options) ? options : []
129
+ })
130
+ const sideListLabel = computed(() => sideListConfig.value?.label || sideListConfig.value?.title || '')
131
+ const showSideList = computed(() => sideListOptions.value.length > 0)
132
+ const sideListFieldName = computed(() => sideListConfig.value?.field || sideListConfig.value?.prop || 'category')
133
+ const listQueryParams = computed(() => {
134
+ if (!showSideList.value) return {}
135
+ const optionValue = selectedSideListValue.value
136
+ if (optionValue === undefined || optionValue === null || optionValue === '') return {}
137
+ return { [sideListFieldName.value]: optionValue }
138
+ })
139
+ const resolveStaticDataset = (config) => {
140
+ const dataset = config?.dataset
141
+ if (Array.isArray(dataset)) return dataset
142
+ if (dataset && typeof dataset === 'object') {
143
+ const pickFirstArray = (source) => {
144
+ if (!source || typeof source !== 'object') return null
145
+ for (const value of Object.values(source)) {
146
+ if (Array.isArray(value)) return value
147
+ if (value && typeof value === 'object') {
148
+ const nested = pickFirstArray(value)
149
+ if (nested) return nested
150
+ }
151
+ }
152
+ return null
153
+ }
154
+ const pickByKeys = (source, keys, allowObjectReturn = false) => {
155
+ if (!source || typeof source !== 'object') return null
156
+ for (const key of keys) {
157
+ if (!key) continue
158
+ const scoped = source[key]
159
+ if (scoped === undefined) continue
160
+ if (Array.isArray(scoped)) return scoped
161
+ if (allowObjectReturn && scoped && typeof scoped === 'object') return scoped
162
+ }
163
+ return null
164
+ }
165
+ const listKeys = [
166
+ selectedSideListValue.value,
167
+ sideListConfig.value?.defaultValue
168
+ ].filter(Boolean)
169
+ let scopedDataset = dataset
170
+ if (showSideList.value) {
171
+ const listScoped = pickByKeys(dataset, listKeys, true)
172
+ if (Array.isArray(listScoped)) return listScoped
173
+ if (listScoped && typeof listScoped === 'object') scopedDataset = listScoped
174
+ }
175
+ if (Array.isArray(scopedDataset)) return scopedDataset
176
+ const filterKeys = [
177
+ selectedFilterValue.value,
178
+ radioFilterConfig.value?.defaultValue
179
+ ].filter(Boolean)
180
+ const filterScoped = pickByKeys(scopedDataset, filterKeys, false)
181
+ if (Array.isArray(filterScoped)) return filterScoped
182
+ const firstArray = pickFirstArray(scopedDataset)
183
+ if (Array.isArray(firstArray)) return firstArray
184
+ }
185
+ return null
186
+ }
187
+ const buildQueryPayload = () => ({
188
+ ...(props.fixedQueryForm || {}),
189
+ ...listQueryParams.value,
190
+ ...filterQueryParams.value
191
+ })
69
192
 
70
193
  const attrs = useAttrs()
71
194
  const wrapperClassObject = computed(() => {
@@ -87,6 +210,53 @@ const wrapperClassObject = computed(() => {
87
210
  return classes
88
211
  })
89
212
 
213
+ const ensureFilterSelection = () => {
214
+ const options = radioFilterOptions.value
215
+ if (!options.length) {
216
+ if (selectedFilterValue.value !== '') selectedFilterValue.value = ''
217
+ return
218
+ }
219
+ const exists = options.some(option => option.value === selectedFilterValue.value)
220
+ if (exists) return
221
+ const fallback = radioFilterConfig.value?.defaultValue ?? options[0]?.value ?? ''
222
+ selectedFilterValue.value = fallback
223
+ }
224
+ const ensureListSelection = () => {
225
+ const options = sideListOptions.value
226
+ if (!options.length) {
227
+ if (selectedSideListValue.value !== '') selectedSideListValue.value = ''
228
+ return
229
+ }
230
+ const exists = options.some(option => option.value === selectedSideListValue.value)
231
+ if (exists) return
232
+ const fallback = sideListConfig.value?.defaultValue ?? options[0]?.value ?? ''
233
+ selectedSideListValue.value = fallback
234
+ }
235
+
236
+ watch([radioFilterOptions, () => radioFilterConfig.value?.defaultValue], () => {
237
+ ensureFilterSelection()
238
+ }, { immediate: true })
239
+ watch([sideListOptions, () => sideListConfig.value?.defaultValue], () => {
240
+ ensureListSelection()
241
+ }, { immediate: true })
242
+
243
+ const handleFilterChange = (value) => {
244
+ if (value === selectedFilterValue.value) return
245
+ selectedFilterValue.value = value
246
+ const config = chartConfig.value || props.config
247
+ if (config) {
248
+ loadChartData(config)
249
+ }
250
+ }
251
+ const handleListChange = (value) => {
252
+ if (value === selectedSideListValue.value) return
253
+ selectedSideListValue.value = value
254
+ const config = chartConfig.value || props.config
255
+ if (config) {
256
+ loadChartData(config)
257
+ }
258
+ }
259
+
90
260
  // 常用图表预设,统一处理 dataset → ECharts option 的映射
91
261
  const presetResolvers = {
92
262
  bar: ({ dataset }) => ({
@@ -119,47 +289,124 @@ const presetResolvers = {
119
289
  }
120
290
  ]
121
291
  }),
122
- line: ({ dataset }) => ({
123
- animationDuration: 420,
124
- animationEasing: 'cubicOut',
125
- animationDelay: (_, idx) => idx * 50,
126
- tooltip: {
127
- trigger: 'axis',
128
- axisPointer: {
129
- type: 'line'
292
+ line: ({ dataset = [], config = {}, state = {} }) => {
293
+ if (!dataset.length) {
294
+ return {
295
+ xAxis: { type: 'category', data: [] },
296
+ yAxis: { type: 'value' },
297
+ series: [{ type: 'line', data: [] }]
130
298
  }
131
- },
132
- xAxis: { type: 'category', data: dataset.map(item => item.label) },
133
- yAxis: { type: 'value' },
134
- series: [
135
- {
136
- type: 'line',
137
- smooth: true,
138
- data: dataset.map(item => item.value),
139
- lineStyle: {
140
- width: 3,
141
- color: '#28C8B5'
142
- },
143
- itemStyle: {
144
- color: '#28C8B5'
145
- },
146
- emphasis: {
147
- itemStyle: {
148
- color: '#4BE1CD',
149
- borderColor: '#D8FFF6',
150
- borderWidth: 6
151
- },
299
+ }
300
+
301
+ const highlightColor = config.highlightColor || '#1F5BFF'
302
+ const lineColor = config.lineColor || '#1F66FF'
303
+ const areaStart = config.areaStartColor || 'rgba(31, 91, 255, 0.25)'
304
+ const areaEnd = config.areaEndColor || 'rgba(31, 91, 255, 0.02)'
305
+ const valueSuffix = config.valueSuffix || ''
306
+ const isSmoothLine = typeof config.smooth === 'boolean' ? config.smooth : false
307
+ const baseSymbolSize = Number(config.symbolSize) || 12
308
+ const activeSymbolSize = Number(config.activeSymbolSize) || baseSymbolSize + 4
309
+ const inactivePointFill = config.inactivePointFill || '#fff'
310
+ const pointBorderWidth = Number(config.pointBorderWidth) || 4
311
+ const activeIndex = typeof state.activePointIndex === 'number' ? state.activePointIndex : -1
312
+
313
+ const seriesData = dataset.map((item, idx) => ({
314
+ value: item.value,
315
+ label: item.label,
316
+ symbol: 'circle',
317
+ symbolSize: idx === activeIndex ? activeSymbolSize : baseSymbolSize,
318
+ itemStyle: {
319
+ color: idx === activeIndex ? highlightColor : inactivePointFill,
320
+ borderColor: idx === activeIndex ? highlightColor : lineColor,
321
+ borderWidth: pointBorderWidth
322
+ }
323
+ }))
324
+
325
+ return {
326
+ animationDuration: 420,
327
+ animationEasing: 'cubicOut',
328
+ tooltip: {
329
+ trigger: 'axis',
330
+ backgroundColor: '#1F5BFF',
331
+ borderWidth: 0,
332
+ textStyle: { color: '#fff' },
333
+ axisPointer: {
334
+ type: 'line',
152
335
  lineStyle: {
153
- width: 4
336
+ color: highlightColor,
337
+ width: 2
154
338
  }
155
339
  },
156
- areaStyle: {
157
- opacity: 0.1,
158
- color: '#28C8B5'
340
+ formatter: (params = []) => {
341
+ if (!params.length) return ''
342
+ const point = params[0]
343
+ return `${point.axisValue}<br/>${point.data.value}${valueSuffix}`
159
344
  }
160
- }
161
- ]
162
- }),
345
+ },
346
+ grid: {
347
+ top: 40,
348
+ left: 60,
349
+ right: 40,
350
+ bottom: 40,
351
+ containLabel: true
352
+ },
353
+ xAxis: {
354
+ type: 'category',
355
+ boundaryGap: false,
356
+ data: dataset.map(item => item.label),
357
+ axisLine: { lineStyle: { color: '#C8D2E8' } },
358
+ axisLabel: { color: '#5C6C8C', fontWeight: 500 },
359
+ axisTick: { show: false },
360
+ splitLine: { show: false }
361
+ },
362
+ yAxis: {
363
+ type: 'value',
364
+ minInterval: 1,
365
+ axisLine: { show: false },
366
+ axisTick: { show: false },
367
+ axisLabel: { color: '#5C6C8C', fontWeight: 500 },
368
+ splitLine: {
369
+ show: true,
370
+ lineStyle: { type: 'dashed', color: '#E0E6F1' }
371
+ }
372
+ },
373
+ series: [
374
+ {
375
+ type: 'line',
376
+ smooth: isSmoothLine,
377
+ showSymbol: true,
378
+ symbol: 'circle',
379
+ data: seriesData.map((point, idx) => ({
380
+ value: point.value,
381
+ label: point.label,
382
+ symbolSize: point.symbolSize,
383
+ itemStyle: point.itemStyle,
384
+ label: {
385
+ show: true,
386
+ fontWeight: idx === activeIndex ? 600 : 500,
387
+ color: idx === activeIndex ? highlightColor : lineColor,
388
+ formatter: `${point.value}${valueSuffix}`,
389
+ position: 'top'
390
+ }
391
+ })),
392
+ lineStyle: {
393
+ width: 3,
394
+ color: lineColor
395
+ },
396
+ emphasis: {
397
+ focus: 'series',
398
+ scale: true
399
+ },
400
+ areaStyle: {
401
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
402
+ { offset: 0, color: areaStart },
403
+ { offset: 1, color: areaEnd }
404
+ ])
405
+ }
406
+ }
407
+ ]
408
+ }
409
+ },
163
410
  pie: ({ dataset }) => ({
164
411
  animationDuration: 420,
165
412
  animationEasing: 'cubicOut',
@@ -171,6 +418,7 @@ const presetResolvers = {
171
418
  {
172
419
  type: 'pie',
173
420
  radius: '55%',
421
+ startAngle: 180,
174
422
  data: dataset.map(item => ({ name: item.label, value: item.value })),
175
423
  itemStyle: {
176
424
  borderColor: '#fff',
@@ -193,6 +441,72 @@ const presetResolvers = {
193
441
  })
194
442
  }
195
443
 
444
+ // 颜色工具函数:将十六进制颜色转换为 RGB
445
+ const hexToRgb = (hex) => {
446
+ // 移除 # 号
447
+ const cleanHex = hex.replace('#', '')
448
+ // 处理 3 位和 6 位十六进制
449
+ const fullHex = cleanHex.length === 3
450
+ ? cleanHex.split('').map(char => char + char).join('')
451
+ : cleanHex
452
+ const r = parseInt(fullHex.substring(0, 2), 16)
453
+ const g = parseInt(fullHex.substring(2, 4), 16)
454
+ const b = parseInt(fullHex.substring(4, 6), 16)
455
+ return { r, g, b }
456
+ }
457
+
458
+ // 颜色工具函数:将 RGB 转换为十六进制颜色
459
+ const rgbToHex = (r, g, b) => {
460
+ const toHex = (n) => {
461
+ const hex = Math.round(n).toString(16)
462
+ return hex.length === 1 ? '0' + hex : hex
463
+ }
464
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`
465
+ }
466
+
467
+ // 生成颜色渐变数组:基于单个颜色生成变淡的颜色数组
468
+ const generateColorGradient = (baseColor, count, fadeRatio = 0.15) => {
469
+ if (!baseColor) return []
470
+ if (count <= 0) return []
471
+ if (count === 1) {
472
+ return Array.isArray(baseColor) ? baseColor : [baseColor]
473
+ }
474
+
475
+ // 如果已经是数组且长度大于1,直接返回
476
+ if (Array.isArray(baseColor) && baseColor.length > 1) {
477
+ return baseColor
478
+ }
479
+
480
+ // 获取基础颜色(如果是数组,取第一个)
481
+ const color = Array.isArray(baseColor) ? baseColor[0] : baseColor
482
+
483
+ // 转换为 RGB
484
+ const baseRgb = hexToRgb(color)
485
+ const white = { r: 255, g: 255, b: 255 }
486
+
487
+ // 生成渐变数组
488
+ const colors = []
489
+ for (let i = 0; i < count; i++) {
490
+ // 第一个颜色保持原色不变,后续逐渐向白色混合
491
+ if (i === 0) {
492
+ colors.push(color)
493
+ continue
494
+ }
495
+
496
+ // 计算混合比例:fadeRatio 控制每次变淡的比例(0-1之间,值越大变淡越快)
497
+ const ratio = i * fadeRatio
498
+ const mixRatio = Math.min(ratio, 0.9) // 最多混合到90%,避免完全变白
499
+
500
+ const r = baseRgb.r + (white.r - baseRgb.r) * mixRatio
501
+ const g = baseRgb.g + (white.g - baseRgb.g) * mixRatio
502
+ const b = baseRgb.b + (white.b - baseRgb.b) * mixRatio
503
+
504
+ colors.push(rgbToHex(r, g, b))
505
+ }
506
+
507
+ return colors
508
+ }
509
+
196
510
  // 数据转换:将后端返回的数据转换为图表需要的 dataset 格式
197
511
  const transformData = (rawData, dataMapping) => {
198
512
  // 增强数据提取:支持从包装对象中提取数组
@@ -252,7 +566,6 @@ const fetchConfigAndData = async () => {
252
566
  // 如果直接提供了 config,直接使用
253
567
  if (props.config) {
254
568
  chartConfig.value = props.config
255
- console.log('44444444444444444444444444444444')
256
569
  await loadChartData(props.config)
257
570
  return
258
571
  }
@@ -266,7 +579,6 @@ const fetchConfigAndData = async () => {
266
579
  }
267
580
 
268
581
  chartConfig.value = res
269
- console.log('3333333333333333333333333')
270
582
  await loadChartData(res)
271
583
  })
272
584
  } catch (error) {
@@ -280,20 +592,24 @@ const fetchConfigAndData = async () => {
280
592
  // 加载图表数据
281
593
  const loadChartData = async (config) => {
282
594
  if (!config) return
595
+ ensureListSelection()
596
+ ensureFilterSelection()
283
597
 
284
598
  try {
285
- // 如果配置中直接提供了 dataset,直接使用
286
- if (config.dataset && Array.isArray(config.dataset)) {
287
- chartData.value = config.dataset
599
+ // 如果配置中直接提供了 dataset(数组或映射),直接使用
600
+ const staticDataset = resolveStaticDataset(config)
601
+ if (staticDataset) {
602
+ chartData.value = staticDataset
603
+ activePointIndex.value = -1
288
604
  renderChart()
289
- console.log('1111111111111111111111111')
290
605
  emit('dataLoaded', chartData.value)
291
606
  return
292
607
  }
293
608
 
294
609
  // 如果配置中提供了 data(logicName),通过 runLogic 获取数据
295
610
  if (config.data) {
296
- const result = await runLogic(config.data, props.fixedQueryForm, props.serviceName)
611
+ const queryPayload = buildQueryPayload()
612
+ const result = await runLogic(config.data, queryPayload, props.serviceName)
297
613
 
298
614
  // 转换数据格式
299
615
  const transformedData = transformData(result, config.dataMapping)
@@ -301,12 +617,13 @@ const loadChartData = async (config) => {
301
617
  console.warn('HChart: 数据转换后为空数组,原始数据:', result)
302
618
  }
303
619
  chartData.value = transformedData
620
+ activePointIndex.value = -1
304
621
  renderChart()
305
- console.log('22222222222222222222222222')
306
622
  emit('dataLoaded', transformedData)
307
623
  } else {
308
624
  // 没有数据源,使用空数据
309
625
  chartData.value = []
626
+ activePointIndex.value = -1
310
627
  renderChart()
311
628
  }
312
629
  } catch (error) {
@@ -316,6 +633,17 @@ const loadChartData = async (config) => {
316
633
  }
317
634
 
318
635
  // 渲染图表
636
+ const handleChartClick = (params) => {
637
+ const config = chartConfig.value || props.config
638
+ if (config?.type !== 'line') return
639
+ if (params?.componentType !== 'series') return
640
+ if (params.seriesType !== 'line') return
641
+ if (typeof params.dataIndex !== 'number') return
642
+ if (activePointIndex.value === params.dataIndex) return
643
+ activePointIndex.value = params.dataIndex
644
+ renderChart()
645
+ }
646
+
319
647
  const renderChart = () => {
320
648
  if (!chartRef.value) return
321
649
  nextTick(() => {
@@ -326,6 +654,7 @@ const renderChart = () => {
326
654
  chartInstance?.resize()
327
655
  })
328
656
  resizeObserver.observe(chartRef.value)
657
+ chartInstance.on('click', handleChartClick)
329
658
  }
330
659
 
331
660
  const config = chartConfig.value || props.config
@@ -337,7 +666,22 @@ const renderChart = () => {
337
666
  return
338
667
  }
339
668
 
340
- const preset = resolver({ dataset: chartData.value })
669
+ const preset = resolver({
670
+ dataset: chartData.value,
671
+ config,
672
+ state: { activePointIndex: activePointIndex.value }
673
+ })
674
+
675
+ // 处理颜色配置:如果只有一个颜色,生成渐变数组
676
+ let processedColor = config.options?.color
677
+ if (processedColor) {
678
+ const dataCount = chartData.value.length || 12 // 默认生成12个颜色
679
+ // 如果只有一个颜色(字符串或长度为1的数组),生成渐变
680
+ if (typeof processedColor === 'string' || (Array.isArray(processedColor) && processedColor.length === 1)) {
681
+ processedColor = generateColorGradient(processedColor, dataCount)
682
+ }
683
+ }
684
+
341
685
  // 深度合并 series 配置
342
686
  let finalOptions = {
343
687
  legend: config.legend || {},
@@ -348,11 +692,13 @@ const renderChart = () => {
348
692
  // 如果 options 中有 series,进行深度合并
349
693
  if (config.options?.series && preset.series) {
350
694
  // 分离需要特殊处理的配置项
351
- const { xAxis: customXAxis, yAxis: customYAxis, series: customSeries, ...restOptions } = config.options
695
+ const { xAxis: customXAxis, yAxis: customYAxis, series: customSeries, color: _, ...restOptions } = config.options
352
696
 
353
697
  finalOptions = {
354
698
  ...finalOptions,
355
699
  ...restOptions,
700
+ // 应用处理后的颜色
701
+ ...(processedColor && { color: processedColor }),
356
702
  // 对于有 xAxis 的图表类型(bar, line),智能合并 xAxis
357
703
  ...(preset.xAxis && {
358
704
  xAxis: {
@@ -382,11 +728,13 @@ const renderChart = () => {
382
728
  }
383
729
  } else {
384
730
  // 没有自定义 series 的情况
385
- const { xAxis: customXAxis, yAxis: customYAxis, ...restOptions } = config.options || {}
731
+ const { xAxis: customXAxis, yAxis: customYAxis, color: _, ...restOptions } = config.options || {}
386
732
 
387
733
  finalOptions = {
388
734
  ...finalOptions,
389
735
  ...restOptions,
736
+ // 应用处理后的颜色
737
+ ...(processedColor && { color: processedColor }),
390
738
  // 对于有 xAxis 的图表类型,智能合并
391
739
  ...(preset.xAxis && {
392
740
  xAxis: {
@@ -416,7 +764,6 @@ watch(
416
764
  () => props.queryParamsName,
417
765
  (newName, oldName) => {
418
766
  if (newName !== oldName) {
419
- console.log('99999999999999999999999999')
420
767
  fetchConfigAndData()
421
768
  }
422
769
  }
@@ -424,7 +771,6 @@ watch(
424
771
 
425
772
  // 暴露给外部的方法
426
773
  const refresh = () => {
427
- console.log('88888888888888888888888888')
428
774
  fetchConfigAndData()
429
775
  }
430
776
 
@@ -432,7 +778,6 @@ const reload = (newQueryForm) => {
432
778
  if (newQueryForm) {
433
779
  emit('update:fixedQueryForm', { ...props.fixedQueryForm, ...newQueryForm })
434
780
  } else {
435
- console.log('7777777777777777777777777')
436
781
  fetchConfigAndData()
437
782
  }
438
783
  }
@@ -446,13 +791,13 @@ defineExpose({
446
791
  onMounted(() => {
447
792
  // 初始化时记录 queryParamsName
448
793
  lastQueryParamsName.value = props.queryParamsName
449
- console.log('66666666666666666666666666666')
450
794
  fetchConfigAndData()
451
795
  })
452
796
 
453
797
  onBeforeUnmount(() => {
454
798
  // 清理图表实例和观察器
455
799
  if (chartInstance) {
800
+ chartInstance.off('click', handleChartClick)
456
801
  chartInstance.dispose()
457
802
  chartInstance = null
458
803
  }
@@ -485,6 +830,112 @@ onBeforeUnmount(() => {
485
830
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.12);
486
831
  }
487
832
 
833
+ .chart-layout {
834
+ display: flex;
835
+ gap: 16px;
836
+ }
837
+
838
+ .chart-side-list {
839
+ width: 180px;
840
+ flex-shrink: 0;
841
+ display: flex;
842
+ flex-direction: column;
843
+ border-right: 1px solid #ECF0F7;
844
+ padding-right: 12px;
845
+ }
846
+
847
+ .side-list-label {
848
+ font-size: 14px;
849
+ color: #4A5875;
850
+ font-weight: 600;
851
+ margin-bottom: 8px;
852
+ }
853
+
854
+ .side-list-menu {
855
+ display: flex;
856
+ flex-direction: column;
857
+ gap: 6px;
858
+ }
859
+
860
+ .side-list-item {
861
+ width: 100%;
862
+ text-align: left;
863
+ border: 0;
864
+ background: transparent;
865
+ padding: 8px 10px;
866
+ border-radius: 8px;
867
+ font-size: 14px;
868
+ color: #4A5875;
869
+ cursor: pointer;
870
+ transition: all 0.15s ease;
871
+ display: flex;
872
+ justify-content: space-between;
873
+ align-items: center;
874
+ }
875
+
876
+ .side-list-item .item-label {
877
+ flex: 1;
878
+ }
879
+
880
+ .side-list-item .item-badge {
881
+ font-size: 12px;
882
+ color: #9AA7C5;
883
+ }
884
+
885
+ .side-list-item.is-active {
886
+ background: rgba(31, 91, 255, 0.08);
887
+ color: #1F5BFF;
888
+ font-weight: 600;
889
+ }
890
+
891
+ .chart-main {
892
+ flex: 1;
893
+ display: flex;
894
+ flex-direction: column;
895
+ }
896
+
897
+ .chart-toolbar {
898
+ display: flex;
899
+ align-items: center;
900
+ gap: 12px;
901
+ margin-top: 4px;
902
+ }
903
+
904
+ .toolbar-label {
905
+ font-size: 14px;
906
+ color: #5C6C8C;
907
+ font-weight: 500;
908
+ }
909
+
910
+ .toolbar-radio-group {
911
+ display: inline-flex;
912
+ gap: 8px;
913
+ }
914
+
915
+ .radio-pill {
916
+ display: inline-flex;
917
+ align-items: center;
918
+ padding: 4px 14px;
919
+ border-radius: 16px;
920
+ border: 1px solid #D5DFF2;
921
+ font-size: 13px;
922
+ color: #5C6C8C;
923
+ cursor: pointer;
924
+ transition: all 0.18s ease;
925
+ user-select: none;
926
+ }
927
+
928
+ .radio-pill-input {
929
+ display: none;
930
+ }
931
+
932
+ .radio-pill.is-active {
933
+ background-color: rgba(31, 91, 255, 0.08);
934
+ border-color: #1F5BFF;
935
+ color: #1F5BFF;
936
+ font-weight: 600;
937
+ }
938
+
488
939
  .chart-canvas {
489
940
  width: 100%;
490
941
  margin-top: 10px;