vue2-client 1.18.7 → 1.18.9

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 (109) 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/assets/svg/female.svg +1 -1
  10. package/src/assets/svg/male.svg +1 -1
  11. package/src/base-client/components/common/AmapMarker/AmapPointRendering.vue +120 -120
  12. package/src/base-client/components/common/CitySelect/index.js +3 -3
  13. package/src/base-client/components/common/CitySelect/index.md +109 -109
  14. package/src/base-client/components/common/CreateQuery/CreateQuery.vue +669 -669
  15. package/src/base-client/components/common/CreateQuery/index.js +3 -3
  16. package/src/base-client/components/common/CreateQuery/index.md +42 -42
  17. package/src/base-client/components/common/CreateSimpleFormQuery/index.js +3 -3
  18. package/src/base-client/components/common/CreateSimpleFormQuery/index.md +42 -42
  19. package/src/base-client/components/common/FormGroupEdit/index.js +3 -3
  20. package/src/base-client/components/common/FormGroupEdit/index.md +43 -43
  21. package/src/base-client/components/common/FormGroupQuery/FormGroupQuery.vue +166 -166
  22. package/src/base-client/components/common/FormGroupQuery/index.js +3 -3
  23. package/src/base-client/components/common/FormGroupQuery/index.md +43 -43
  24. package/src/base-client/components/common/HIS/HButtons/HButtons.vue +491 -491
  25. package/src/base-client/components/common/HIS/HForm/HForm.vue +32 -10
  26. package/src/base-client/components/common/HIS/HFormGroup/index.js +3 -3
  27. package/src/base-client/components/common/HIS/HTab/HTab.vue +443 -443
  28. package/src/base-client/components/common/JSONToTree/jsontotree.vue +271 -271
  29. package/src/base-client/components/common/PersonSetting/PersonSetting.vue +208 -208
  30. package/src/base-client/components/common/PersonSetting/index.js +3 -3
  31. package/src/base-client/components/common/Tree/Tree.vue +149 -149
  32. package/src/base-client/components/common/Tree/index.js +2 -2
  33. package/src/base-client/components/common/Upload/index.js +3 -3
  34. package/src/base-client/components/common/XAddNativeForm/index.md +146 -146
  35. package/src/base-client/components/common/XCard/XCard.vue +64 -64
  36. package/src/base-client/components/common/XCollapse/XCollapse.vue +830 -830
  37. package/src/base-client/components/common/XDataDrawer/XDataDrawer.vue +180 -180
  38. package/src/base-client/components/common/XDataDrawer/index.js +3 -3
  39. package/src/base-client/components/common/XDataDrawer/index.md +41 -41
  40. package/src/base-client/components/common/XDescriptions/index.js +3 -3
  41. package/src/base-client/components/common/XDescriptions/index.md +83 -83
  42. package/src/base-client/components/common/XForm/index.md +178 -178
  43. package/src/base-client/components/common/XInput/XInput.vue +12 -6
  44. package/src/base-client/components/common/XStepView/XStepView.vue +252 -252
  45. package/src/base-client/components/common/XStepView/index.js +3 -3
  46. package/src/base-client/components/common/XStepView/index.md +31 -31
  47. package/src/base-client/components/common/XTable/index.md +255 -255
  48. package/src/base-client/components/common/XTimeline/XTimeline.vue +477 -477
  49. package/src/base-client/components/his/HChart/HChart.vue +500 -53
  50. package/src/base-client/components/his/XHDescriptions/XHDescriptions.vue +44 -78
  51. package/src/base-client/components/his/XHisEditor/XHisEditor.vue +705 -705
  52. package/src/base-client/components/his/XList/XList.vue +938 -938
  53. package/src/base-client/components/his/XTimeSelect/XTimeSelect.vue +354 -354
  54. package/src/base-client/components/his/XTitle/XTitle.vue +314 -314
  55. package/src/base-client/components/his/XTreeRows/XTreeRows.vue +341 -341
  56. package/src/base-client/components/his/threeTestOrders/editor.vue +113 -113
  57. package/src/base-client/components/system/DictionaryDetailsView/DictionaryDetailsView.vue +232 -232
  58. package/src/base-client/plugins/Config.js +19 -19
  59. package/src/base-client/plugins/tabs-page-plugin.js +39 -39
  60. package/src/components/Charts/Bar.vue +62 -62
  61. package/src/components/Charts/ChartCard.vue +134 -134
  62. package/src/components/Charts/Liquid.vue +67 -67
  63. package/src/components/Charts/MiniArea.vue +39 -39
  64. package/src/components/Charts/MiniBar.vue +39 -39
  65. package/src/components/Charts/MiniProgress.vue +75 -75
  66. package/src/components/Charts/MiniSmoothArea.vue +40 -40
  67. package/src/components/Charts/Radar.vue +68 -68
  68. package/src/components/Charts/RankList.vue +77 -77
  69. package/src/components/Charts/TagCloud.vue +113 -113
  70. package/src/components/Charts/TransferBar.vue +64 -64
  71. package/src/components/Charts/Trend.vue +82 -82
  72. package/src/components/Charts/chart.less +12 -12
  73. package/src/components/Charts/smooth.area.less +13 -13
  74. package/src/components/NumberInfo/NumberInfo.vue +54 -54
  75. package/src/components/NumberInfo/index.js +3 -3
  76. package/src/components/NumberInfo/index.less +54 -54
  77. package/src/components/NumberInfo/index.md +43 -43
  78. package/src/components/card/ChartCard.vue +79 -79
  79. package/src/components/chart/Bar.vue +60 -60
  80. package/src/components/chart/MiniArea.vue +67 -67
  81. package/src/components/chart/MiniBar.vue +59 -59
  82. package/src/components/chart/MiniProgress.vue +57 -57
  83. package/src/components/chart/Radar.vue +80 -80
  84. package/src/components/chart/RankingList.vue +60 -60
  85. package/src/components/chart/Trend.vue +79 -79
  86. package/src/components/chart/index.less +9 -9
  87. package/src/components/checkbox/ColorCheckbox.vue +157 -157
  88. package/src/components/index.js +36 -36
  89. package/src/components/input/IInput.vue +66 -66
  90. package/src/components/menu/SideMenu.vue +75 -75
  91. package/src/components/menu/menu.js +273 -273
  92. package/src/components/tool/AStepItem.vue +60 -60
  93. package/src/layouts/CommonLayout.vue +56 -56
  94. package/src/layouts/header/HeaderNotice.vue +177 -177
  95. package/src/lib.js +1 -1
  96. package/src/mock/extend/index.js +84 -84
  97. package/src/mock/goods/index.js +108 -108
  98. package/src/pages/dashboard/workplace/WorkPlace.vue +141 -141
  99. package/src/pages/system/dictionary/index.vue +44 -44
  100. package/src/pages/system/monitor/loginInfor/index.vue +37 -37
  101. package/src/pages/system/monitor/operLog/index.vue +37 -37
  102. package/src/pages/userInfoDetailManage/ExceptionRecordQuery/index.vue +45 -45
  103. package/src/services/api/cas.js +79 -79
  104. package/src/store/modules/setting.js +119 -119
  105. package/src/utils/authority-utils.js +85 -85
  106. package/src/utils/errorCode.js +6 -6
  107. package/src-base-client/components/common/HIS/HForm/HForm.vue +347 -0
  108. package/src-base-client/components/common/XCollapse/XCollapse.vue +0 -0
  109. 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,123 @@ 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
+ symbolSize: point.symbolSize,
382
+ itemStyle: point.itemStyle,
383
+ label: {
384
+ show: true,
385
+ fontWeight: idx === activeIndex ? 600 : 500,
386
+ color: idx === activeIndex ? highlightColor : lineColor,
387
+ formatter: `${point.value}${valueSuffix}`,
388
+ position: 'top'
389
+ }
390
+ })),
391
+ lineStyle: {
392
+ width: 3,
393
+ color: lineColor
394
+ },
395
+ emphasis: {
396
+ focus: 'series',
397
+ scale: true
398
+ },
399
+ areaStyle: {
400
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
401
+ { offset: 0, color: areaStart },
402
+ { offset: 1, color: areaEnd }
403
+ ])
404
+ }
405
+ }
406
+ ]
407
+ }
408
+ },
163
409
  pie: ({ dataset }) => ({
164
410
  animationDuration: 420,
165
411
  animationEasing: 'cubicOut',
@@ -171,6 +417,7 @@ const presetResolvers = {
171
417
  {
172
418
  type: 'pie',
173
419
  radius: '55%',
420
+ startAngle: 180,
174
421
  data: dataset.map(item => ({ name: item.label, value: item.value })),
175
422
  itemStyle: {
176
423
  borderColor: '#fff',
@@ -193,6 +440,70 @@ const presetResolvers = {
193
440
  })
194
441
  }
195
442
 
443
+ // 颜色工具函数:将十六进制颜色转换为 RGB
444
+ const hexToRgb = (hex) => {
445
+ // 移除 # 号
446
+ const cleanHex = hex.replace('#', '')
447
+ // 处理 3 位和 6 位十六进制
448
+ const fullHex = cleanHex.length === 3
449
+ ? cleanHex.split('').map(char => char + char).join('')
450
+ : cleanHex
451
+ const r = parseInt(fullHex.substring(0, 2), 16)
452
+ const g = parseInt(fullHex.substring(2, 4), 16)
453
+ const b = parseInt(fullHex.substring(4, 6), 16)
454
+ return { r, g, b }
455
+ }
456
+
457
+ // 颜色工具函数:将 RGB 转换为十六进制颜色
458
+ const rgbToHex = (r, g, b) => {
459
+ const toHex = (n) => {
460
+ const hex = Math.round(n).toString(16)
461
+ return hex.length === 1 ? '0' + hex : hex
462
+ }
463
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`
464
+ }
465
+
466
+ // 生成颜色渐变数组:基于单个颜色生成变淡的颜色数组
467
+ const generateColorGradient = (baseColor, count, fadeRatio = 0.15) => {
468
+ if (!baseColor) return []
469
+ if (count <= 0) return []
470
+ if (count === 1) {
471
+ return Array.isArray(baseColor) ? baseColor : [baseColor]
472
+ }
473
+
474
+ // 如果已经是数组且长度大于1,直接返回
475
+ if (Array.isArray(baseColor) && baseColor.length > 1) {
476
+ return baseColor
477
+ }
478
+
479
+ // 获取基础颜色(如果是数组,取第一个)
480
+ const color = Array.isArray(baseColor) ? baseColor[0] : baseColor
481
+
482
+ // 转换为 RGB
483
+ const baseRgb = hexToRgb(color)
484
+ const white = { r: 255, g: 255, b: 255 }
485
+
486
+ // 生成渐变数组
487
+ const colors = []
488
+ for (let i = 0; i < count; i++) {
489
+ // 第一个颜色保持原色不变,后续逐渐向白色混合
490
+ if (i === 0) {
491
+ colors.push(color)
492
+ continue
493
+ }
494
+ // 计算混合比例:fadeRatio 控制每次变淡的比例(0-1之间,值越大变淡越快)
495
+ const ratio = i * fadeRatio
496
+ const mixRatio = Math.min(ratio, 0.9) // 最多混合到90%,避免完全变白
497
+ const r = baseRgb.r + (white.r - baseRgb.r) * mixRatio
498
+ const g = baseRgb.g + (white.g - baseRgb.g) * mixRatio
499
+ const b = baseRgb.b + (white.b - baseRgb.b) * mixRatio
500
+
501
+ colors.push(rgbToHex(r, g, b))
502
+ }
503
+
504
+ return colors
505
+ }
506
+
196
507
  // 数据转换:将后端返回的数据转换为图表需要的 dataset 格式
197
508
  const transformData = (rawData, dataMapping) => {
198
509
  // 增强数据提取:支持从包装对象中提取数组
@@ -252,7 +563,6 @@ const fetchConfigAndData = async () => {
252
563
  // 如果直接提供了 config,直接使用
253
564
  if (props.config) {
254
565
  chartConfig.value = props.config
255
- console.log('44444444444444444444444444444444')
256
566
  await loadChartData(props.config)
257
567
  return
258
568
  }
@@ -266,7 +576,6 @@ const fetchConfigAndData = async () => {
266
576
  }
267
577
 
268
578
  chartConfig.value = res
269
- console.log('3333333333333333333333333')
270
579
  await loadChartData(res)
271
580
  })
272
581
  } catch (error) {
@@ -280,20 +589,24 @@ const fetchConfigAndData = async () => {
280
589
  // 加载图表数据
281
590
  const loadChartData = async (config) => {
282
591
  if (!config) return
592
+ ensureListSelection()
593
+ ensureFilterSelection()
283
594
 
284
595
  try {
285
- // 如果配置中直接提供了 dataset,直接使用
286
- if (config.dataset && Array.isArray(config.dataset)) {
287
- chartData.value = config.dataset
596
+ // 如果配置中直接提供了 dataset(数组或映射),直接使用
597
+ const staticDataset = resolveStaticDataset(config)
598
+ if (staticDataset) {
599
+ chartData.value = staticDataset
600
+ activePointIndex.value = -1
288
601
  renderChart()
289
- console.log('1111111111111111111111111')
290
602
  emit('dataLoaded', chartData.value)
291
603
  return
292
604
  }
293
605
 
294
606
  // 如果配置中提供了 data(logicName),通过 runLogic 获取数据
295
607
  if (config.data) {
296
- const result = await runLogic(config.data, props.fixedQueryForm, props.serviceName)
608
+ const queryPayload = buildQueryPayload()
609
+ const result = await runLogic(config.data, queryPayload, props.serviceName)
297
610
 
298
611
  // 转换数据格式
299
612
  const transformedData = transformData(result, config.dataMapping)
@@ -301,12 +614,13 @@ const loadChartData = async (config) => {
301
614
  console.warn('HChart: 数据转换后为空数组,原始数据:', result)
302
615
  }
303
616
  chartData.value = transformedData
617
+ activePointIndex.value = -1
304
618
  renderChart()
305
- console.log('22222222222222222222222222')
306
619
  emit('dataLoaded', transformedData)
307
620
  } else {
308
621
  // 没有数据源,使用空数据
309
622
  chartData.value = []
623
+ activePointIndex.value = -1
310
624
  renderChart()
311
625
  }
312
626
  } catch (error) {
@@ -316,6 +630,17 @@ const loadChartData = async (config) => {
316
630
  }
317
631
 
318
632
  // 渲染图表
633
+ const handleChartClick = (params) => {
634
+ const config = chartConfig.value || props.config
635
+ if (config?.type !== 'line') return
636
+ if (params?.componentType !== 'series') return
637
+ if (params.seriesType !== 'line') return
638
+ if (typeof params.dataIndex !== 'number') return
639
+ if (activePointIndex.value === params.dataIndex) return
640
+ activePointIndex.value = params.dataIndex
641
+ renderChart()
642
+ }
643
+
319
644
  const renderChart = () => {
320
645
  if (!chartRef.value) return
321
646
  nextTick(() => {
@@ -326,6 +651,7 @@ const renderChart = () => {
326
651
  chartInstance?.resize()
327
652
  })
328
653
  resizeObserver.observe(chartRef.value)
654
+ chartInstance.on('click', handleChartClick)
329
655
  }
330
656
 
331
657
  const config = chartConfig.value || props.config
@@ -337,7 +663,21 @@ const renderChart = () => {
337
663
  return
338
664
  }
339
665
 
340
- const preset = resolver({ dataset: chartData.value })
666
+ const preset = resolver({
667
+ dataset: chartData.value,
668
+ config,
669
+ state: { activePointIndex: activePointIndex.value }
670
+ })
671
+
672
+ // 处理颜色配置:如果只有一个颜色,生成渐变数组
673
+ let processedColor = config.options?.color
674
+ if (processedColor) {
675
+ const dataCount = chartData.value.length || 12 // 默认生成12个颜色
676
+ // 如果只有一个颜色(字符串或长度为1的数组),生成渐变
677
+ if (typeof processedColor === 'string' || (Array.isArray(processedColor) && processedColor.length === 1)) {
678
+ processedColor = generateColorGradient(processedColor, dataCount)
679
+ }
680
+ }
341
681
  // 深度合并 series 配置
342
682
  let finalOptions = {
343
683
  legend: config.legend || {},
@@ -348,11 +688,13 @@ const renderChart = () => {
348
688
  // 如果 options 中有 series,进行深度合并
349
689
  if (config.options?.series && preset.series) {
350
690
  // 分离需要特殊处理的配置项
351
- const { xAxis: customXAxis, yAxis: customYAxis, series: customSeries, ...restOptions } = config.options
691
+ const { xAxis: customXAxis, yAxis: customYAxis, series: customSeries, color: _, ...restOptions } = config.options
352
692
 
353
693
  finalOptions = {
354
694
  ...finalOptions,
355
695
  ...restOptions,
696
+ // 应用处理后的颜色
697
+ ...(processedColor && { color: processedColor }),
356
698
  // 对于有 xAxis 的图表类型(bar, line),智能合并 xAxis
357
699
  ...(preset.xAxis && {
358
700
  xAxis: {
@@ -382,11 +724,13 @@ const renderChart = () => {
382
724
  }
383
725
  } else {
384
726
  // 没有自定义 series 的情况
385
- const { xAxis: customXAxis, yAxis: customYAxis, ...restOptions } = config.options || {}
727
+ const { xAxis: customXAxis, yAxis: customYAxis, color: _, ...restOptions } = config.options || {}
386
728
 
387
729
  finalOptions = {
388
730
  ...finalOptions,
389
731
  ...restOptions,
732
+ // 应用处理后的颜色
733
+ ...(processedColor && { color: processedColor }),
390
734
  // 对于有 xAxis 的图表类型,智能合并
391
735
  ...(preset.xAxis && {
392
736
  xAxis: {
@@ -416,7 +760,6 @@ watch(
416
760
  () => props.queryParamsName,
417
761
  (newName, oldName) => {
418
762
  if (newName !== oldName) {
419
- console.log('99999999999999999999999999')
420
763
  fetchConfigAndData()
421
764
  }
422
765
  }
@@ -424,7 +767,6 @@ watch(
424
767
 
425
768
  // 暴露给外部的方法
426
769
  const refresh = () => {
427
- console.log('88888888888888888888888888')
428
770
  fetchConfigAndData()
429
771
  }
430
772
 
@@ -432,7 +774,6 @@ const reload = (newQueryForm) => {
432
774
  if (newQueryForm) {
433
775
  emit('update:fixedQueryForm', { ...props.fixedQueryForm, ...newQueryForm })
434
776
  } else {
435
- console.log('7777777777777777777777777')
436
777
  fetchConfigAndData()
437
778
  }
438
779
  }
@@ -446,13 +787,13 @@ defineExpose({
446
787
  onMounted(() => {
447
788
  // 初始化时记录 queryParamsName
448
789
  lastQueryParamsName.value = props.queryParamsName
449
- console.log('66666666666666666666666666666')
450
790
  fetchConfigAndData()
451
791
  })
452
792
 
453
793
  onBeforeUnmount(() => {
454
794
  // 清理图表实例和观察器
455
795
  if (chartInstance) {
796
+ chartInstance.off('click', handleChartClick)
456
797
  chartInstance.dispose()
457
798
  chartInstance = null
458
799
  }
@@ -485,6 +826,112 @@ onBeforeUnmount(() => {
485
826
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.12);
486
827
  }
487
828
 
829
+ .chart-layout {
830
+ display: flex;
831
+ gap: 16px;
832
+ }
833
+
834
+ .chart-side-list {
835
+ width: 180px;
836
+ flex-shrink: 0;
837
+ display: flex;
838
+ flex-direction: column;
839
+ border-right: 1px solid #ECF0F7;
840
+ padding-right: 12px;
841
+ }
842
+
843
+ .side-list-label {
844
+ font-size: 14px;
845
+ color: #4A5875;
846
+ font-weight: 600;
847
+ margin-bottom: 8px;
848
+ }
849
+
850
+ .side-list-menu {
851
+ display: flex;
852
+ flex-direction: column;
853
+ gap: 6px;
854
+ }
855
+
856
+ .side-list-item {
857
+ width: 100%;
858
+ text-align: left;
859
+ border: 0;
860
+ background: transparent;
861
+ padding: 8px 10px;
862
+ border-radius: 8px;
863
+ font-size: 14px;
864
+ color: #4A5875;
865
+ cursor: pointer;
866
+ transition: all 0.15s ease;
867
+ display: flex;
868
+ justify-content: space-between;
869
+ align-items: center;
870
+ }
871
+
872
+ .side-list-item .item-label {
873
+ flex: 1;
874
+ }
875
+
876
+ .side-list-item .item-badge {
877
+ font-size: 12px;
878
+ color: #9AA7C5;
879
+ }
880
+
881
+ .side-list-item.is-active {
882
+ background: rgba(31, 91, 255, 0.08);
883
+ color: #1F5BFF;
884
+ font-weight: 600;
885
+ }
886
+
887
+ .chart-main {
888
+ flex: 1;
889
+ display: flex;
890
+ flex-direction: column;
891
+ }
892
+
893
+ .chart-toolbar {
894
+ display: flex;
895
+ align-items: center;
896
+ gap: 12px;
897
+ margin-top: 4px;
898
+ }
899
+
900
+ .toolbar-label {
901
+ font-size: 14px;
902
+ color: #5C6C8C;
903
+ font-weight: 500;
904
+ }
905
+
906
+ .toolbar-radio-group {
907
+ display: inline-flex;
908
+ gap: 8px;
909
+ }
910
+
911
+ .radio-pill {
912
+ display: inline-flex;
913
+ align-items: center;
914
+ padding: 4px 14px;
915
+ border-radius: 16px;
916
+ border: 1px solid #D5DFF2;
917
+ font-size: 13px;
918
+ color: #5C6C8C;
919
+ cursor: pointer;
920
+ transition: all 0.18s ease;
921
+ user-select: none;
922
+ }
923
+
924
+ .radio-pill-input {
925
+ display: none;
926
+ }
927
+
928
+ .radio-pill.is-active {
929
+ background-color: rgba(31, 91, 255, 0.08);
930
+ border-color: #1F5BFF;
931
+ color: #1F5BFF;
932
+ font-weight: 600;
933
+ }
934
+
488
935
  .chart-canvas {
489
936
  width: 100%;
490
937
  margin-top: 10px;