vue2-client 1.17.45 → 1.17.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env +20 -20
- package/package.json +1 -1
- package/src/base-client/components/common/XCollapse/XCollapse.vue +4 -4
- package/src/base-client/components/common/XForm/XFormItem.vue +1 -0
- package/src/base-client/components/common/XForm/XTreeSelect.vue +276 -276
- package/src/base-client/components/his/HChart/HChart.vue +493 -493
- package/src/base-client/components/his/HChart/demo.vue +88 -88
- package/src/base-client/components/his/HChart/index.md +798 -798
- package/src/base-client/components/his/XIcon/XIcon.vue +73 -73
- package/src/base-client/components/his/XIcon/index.js +3 -3
- package/src/base-client/components/his/XIcon/index.md +177 -177
- package/src/base-client/components/his/XSidebar/XSidebar.vue +1 -1
- package/src/pages/WorkflowDetail/WorkFlowDemo4.vue +127 -0
- package/src/pages/WorkflowDetail/WorkflowPageDetail/WorkFlowBaseInformation.vue +417 -417
- package/src/router/async/router.map.js +2 -2
- package/src/services/api/restTools.js +215 -215
- package/src/assets/img/paymentMethod/icon1.png +0 -0
- package/src/assets/img/paymentMethod/icon2.png +0 -0
- package/src/assets/img/paymentMethod/icon3.png +0 -0
- package/src/assets/img/paymentMethod/icon4.png +0 -0
- package/src/assets/img/paymentMethod/icon5.png +0 -0
- package/src/assets/img/paymentMethod/icon6.png +0 -0
- package/src/base-client/components/common/XReport/XReportHospitalizationDemo.vue +0 -45
- package/src-base-client/components/his/XCharge/README.md +0 -0
- package/src-base-client/components/his/XCharge/XCharge.vue +0 -0
|
@@ -1,493 +1,493 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="h-chart-configurable" :class="wrapperClassObject">
|
|
3
|
-
<x-title v-if="chartTitle" v-bind="xTitleAttrs" :title="chartTitle" />
|
|
4
|
-
<!-- 图表挂载容器 -->
|
|
5
|
-
<div ref="chartRef" class="chart-canvas" :style="chartStyle"></div>
|
|
6
|
-
</div>
|
|
7
|
-
</template>
|
|
8
|
-
|
|
9
|
-
<script setup>
|
|
10
|
-
import { ref, watch, onMounted, computed, useAttrs, onBeforeUnmount, nextTick } from 'vue'
|
|
11
|
-
import * as echarts from 'echarts/core'
|
|
12
|
-
import { BarChart, LineChart, PieChart } from 'echarts/charts'
|
|
13
|
-
import { TooltipComponent, LegendComponent, GridComponent } from 'echarts/components'
|
|
14
|
-
import { CanvasRenderer } from 'echarts/renderers'
|
|
15
|
-
import { getConfigByName, runLogic } from '@vue2-client/services/api/common'
|
|
16
|
-
import XTitle from '../XTitle/XTitle.vue'
|
|
17
|
-
// 注册常用图表类型,避免每次重复导入
|
|
18
|
-
echarts.use([BarChart, LineChart, PieChart, TooltipComponent, LegendComponent, GridComponent, CanvasRenderer])
|
|
19
|
-
|
|
20
|
-
const props = defineProps({
|
|
21
|
-
// 配置名称(用于查询配置)
|
|
22
|
-
queryParamsName: {
|
|
23
|
-
type: String,
|
|
24
|
-
default: ''
|
|
25
|
-
},
|
|
26
|
-
// 服务名
|
|
27
|
-
serviceName: {
|
|
28
|
-
type: String,
|
|
29
|
-
default: 'af-his'
|
|
30
|
-
},
|
|
31
|
-
// 固定查询参数
|
|
32
|
-
fixedQueryForm: {
|
|
33
|
-
type: Object,
|
|
34
|
-
default: () => ({ condition: '1=1' })
|
|
35
|
-
},
|
|
36
|
-
config: {
|
|
37
|
-
type: Object,
|
|
38
|
-
default: null
|
|
39
|
-
}
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
const emit = defineEmits(['dataLoaded', 'error', 'update:fixedQueryForm'])
|
|
43
|
-
|
|
44
|
-
const chartRef = ref(null)
|
|
45
|
-
let chartInstance = null
|
|
46
|
-
const chartConfig = ref(null)
|
|
47
|
-
const chartData = ref([])
|
|
48
|
-
const loading = ref(false)
|
|
49
|
-
let resizeObserver = null
|
|
50
|
-
|
|
51
|
-
// 记录上一次的 queryParamsName,用于比较变化
|
|
52
|
-
const lastQueryParamsName = ref('')
|
|
53
|
-
|
|
54
|
-
const chartTitle = computed(() => {
|
|
55
|
-
if (props.config?.xtitle) return props.config.xtitle
|
|
56
|
-
if (chartConfig.value?.xtitle) return chartConfig.value.xtitle
|
|
57
|
-
return ''
|
|
58
|
-
})
|
|
59
|
-
const xTitleAttrs = computed(() => {
|
|
60
|
-
if (props.config?.xtitleAttrs) return props.config.xtitleAttrs
|
|
61
|
-
if (chartConfig.value?.xtitleAttrs) return chartConfig.value.xtitleAttrs
|
|
62
|
-
return {}
|
|
63
|
-
})
|
|
64
|
-
const chartStyle = computed(() => {
|
|
65
|
-
const target = props.config ?? chartConfig.value ?? {}
|
|
66
|
-
if (target.style && typeof target.style === 'object') return target.style
|
|
67
|
-
return {}
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
const attrs = useAttrs()
|
|
71
|
-
const wrapperClassObject = computed(() => {
|
|
72
|
-
const a = attrs
|
|
73
|
-
const classes = {}
|
|
74
|
-
|
|
75
|
-
// 通用布尔样式开关(以存在/空字符串/'true' 为真)
|
|
76
|
-
const booleanStyleKeys = [
|
|
77
|
-
'show-border',
|
|
78
|
-
'border-hide-left',
|
|
79
|
-
'border-hide-right'
|
|
80
|
-
]
|
|
81
|
-
for (const key of booleanStyleKeys) {
|
|
82
|
-
const val = a[key]
|
|
83
|
-
const truthy = val === true || val === '' || val === 'true'
|
|
84
|
-
if (truthy) classes[`h-chart-${key}`] = true
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return classes
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
// 常用图表预设,统一处理 dataset → ECharts option 的映射
|
|
91
|
-
const presetResolvers = {
|
|
92
|
-
bar: ({ dataset }) => ({
|
|
93
|
-
animationDuration: 420,
|
|
94
|
-
animationEasing: 'cubicOut',
|
|
95
|
-
animationDelay: (_, idx) => idx * 60,
|
|
96
|
-
tooltip: {
|
|
97
|
-
trigger: 'axis',
|
|
98
|
-
axisPointer: {
|
|
99
|
-
type: 'shadow'
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
xAxis: { type: 'category', data: dataset.map(item => item.label) },
|
|
103
|
-
yAxis: { type: 'value' },
|
|
104
|
-
series: [
|
|
105
|
-
{
|
|
106
|
-
type: 'bar',
|
|
107
|
-
data: dataset.map(item => item.value),
|
|
108
|
-
itemStyle: {
|
|
109
|
-
color: '#3362DA',
|
|
110
|
-
borderRadius: [4, 4, 0, 0]
|
|
111
|
-
},
|
|
112
|
-
emphasis: {
|
|
113
|
-
itemStyle: {
|
|
114
|
-
color: '#4C7CFF',
|
|
115
|
-
shadowBlur: 18,
|
|
116
|
-
shadowColor: 'rgba(76, 124, 255, 0.45)'
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
]
|
|
121
|
-
}),
|
|
122
|
-
line: ({ dataset }) => ({
|
|
123
|
-
animationDuration: 420,
|
|
124
|
-
animationEasing: 'cubicOut',
|
|
125
|
-
animationDelay: (_, idx) => idx * 50,
|
|
126
|
-
tooltip: {
|
|
127
|
-
trigger: 'axis',
|
|
128
|
-
axisPointer: {
|
|
129
|
-
type: 'line'
|
|
130
|
-
}
|
|
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
|
-
},
|
|
152
|
-
lineStyle: {
|
|
153
|
-
width: 4
|
|
154
|
-
}
|
|
155
|
-
},
|
|
156
|
-
areaStyle: {
|
|
157
|
-
opacity: 0.1,
|
|
158
|
-
color: '#28C8B5'
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
]
|
|
162
|
-
}),
|
|
163
|
-
pie: ({ dataset }) => ({
|
|
164
|
-
animationDuration: 420,
|
|
165
|
-
animationEasing: 'cubicOut',
|
|
166
|
-
tooltip: {
|
|
167
|
-
trigger: 'item',
|
|
168
|
-
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
|
169
|
-
},
|
|
170
|
-
series: [
|
|
171
|
-
{
|
|
172
|
-
type: 'pie',
|
|
173
|
-
radius: '55%',
|
|
174
|
-
data: dataset.map(item => ({ name: item.label, value: item.value })),
|
|
175
|
-
itemStyle: {
|
|
176
|
-
borderColor: '#fff',
|
|
177
|
-
borderWidth: 2
|
|
178
|
-
},
|
|
179
|
-
emphasis: {
|
|
180
|
-
scale: true,
|
|
181
|
-
scaleSize: 6,
|
|
182
|
-
itemStyle: {
|
|
183
|
-
shadowBlur: 16,
|
|
184
|
-
shadowColor: 'rgba(0, 0, 0, 0.25)'
|
|
185
|
-
},
|
|
186
|
-
label: {
|
|
187
|
-
show: true,
|
|
188
|
-
fontWeight: 'bold'
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
]
|
|
193
|
-
})
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// 数据转换:将后端返回的数据转换为图表需要的 dataset 格式
|
|
197
|
-
const transformData = (rawData, dataMapping) => {
|
|
198
|
-
// 增强数据提取:支持从包装对象中提取数组
|
|
199
|
-
let dataArray = rawData
|
|
200
|
-
|
|
201
|
-
// 如果 rawData 是对象但不是数组,尝试提取数组数据
|
|
202
|
-
if (rawData && typeof rawData === 'object' && !Array.isArray(rawData)) {
|
|
203
|
-
// 尝试常见的包装字段
|
|
204
|
-
dataArray = rawData.data || rawData.result || rawData.list || rawData.items || rawData.records
|
|
205
|
-
|
|
206
|
-
// 如果还是找不到数组,检查是否所有值都是数组
|
|
207
|
-
if (!Array.isArray(dataArray)) {
|
|
208
|
-
const arrayValues = Object.values(rawData).filter(val => Array.isArray(val))
|
|
209
|
-
if (arrayValues.length === 1) {
|
|
210
|
-
dataArray = arrayValues[0]
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// 最终检查:必须是数组
|
|
216
|
-
if (!dataArray || !Array.isArray(dataArray)) {
|
|
217
|
-
console.warn('HChart: transformData 接收到的数据不是数组格式:', rawData)
|
|
218
|
-
return []
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// 如果配置了数据映射规则,使用映射规则
|
|
222
|
-
if (dataMapping) {
|
|
223
|
-
const { labelField = 'label', valueField = 'value' } = dataMapping
|
|
224
|
-
return dataArray.map(item => ({
|
|
225
|
-
label: item[labelField] || item.name || item.label || '',
|
|
226
|
-
value: Number(item[valueField] || item.value || 0)
|
|
227
|
-
}))
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// 默认处理:尝试自动识别
|
|
231
|
-
return dataArray.map(item => {
|
|
232
|
-
if (typeof item === 'object' && item !== null) {
|
|
233
|
-
return {
|
|
234
|
-
label: item.label || item.name || item.text || item.x || String(item[Object.keys(item)[0]] || ''),
|
|
235
|
-
value: Number(item.value || item.count || item.y || item[Object.keys(item)[1]] || 0)
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return { label: String(item), value: 0 }
|
|
239
|
-
})
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// 获取配置和数据
|
|
243
|
-
const fetchConfigAndData = async () => {
|
|
244
|
-
if (!props.queryParamsName && !props.config) {
|
|
245
|
-
console.warn('HChart: queryParamsName 或 config 必须提供一个')
|
|
246
|
-
return
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
try {
|
|
250
|
-
loading.value = true
|
|
251
|
-
|
|
252
|
-
// 如果直接提供了 config,直接使用
|
|
253
|
-
if (props.config) {
|
|
254
|
-
chartConfig.value = props.config
|
|
255
|
-
console.log('44444444444444444444444444444444')
|
|
256
|
-
await loadChartData(props.config)
|
|
257
|
-
return
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// 否则通过 queryParamsName 查询配置
|
|
261
|
-
getConfigByName(props.queryParamsName, props.serviceName, async (res) => {
|
|
262
|
-
if (!res) {
|
|
263
|
-
console.error('HChart: 未能获取到配置内容')
|
|
264
|
-
emit('error', new Error('未能获取到配置内容'))
|
|
265
|
-
return
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
chartConfig.value = res
|
|
269
|
-
console.log('3333333333333333333333333')
|
|
270
|
-
await loadChartData(res)
|
|
271
|
-
})
|
|
272
|
-
} catch (error) {
|
|
273
|
-
console.error('HChart: 获取配置或数据时发生错误:', error)
|
|
274
|
-
emit('error', error)
|
|
275
|
-
} finally {
|
|
276
|
-
loading.value = false
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// 加载图表数据
|
|
281
|
-
const loadChartData = async (config) => {
|
|
282
|
-
if (!config) return
|
|
283
|
-
|
|
284
|
-
try {
|
|
285
|
-
// 如果配置中直接提供了 dataset,直接使用
|
|
286
|
-
if (config.dataset && Array.isArray(config.dataset)) {
|
|
287
|
-
chartData.value = config.dataset
|
|
288
|
-
renderChart()
|
|
289
|
-
console.log('1111111111111111111111111')
|
|
290
|
-
emit('dataLoaded', chartData.value)
|
|
291
|
-
return
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// 如果配置中提供了 data(logicName),通过 runLogic 获取数据
|
|
295
|
-
if (config.data) {
|
|
296
|
-
const result = await runLogic(config.data, props.fixedQueryForm, props.serviceName)
|
|
297
|
-
|
|
298
|
-
// 转换数据格式
|
|
299
|
-
const transformedData = transformData(result, config.dataMapping)
|
|
300
|
-
if (transformedData.length === 0) {
|
|
301
|
-
console.warn('HChart: 数据转换后为空数组,原始数据:', result)
|
|
302
|
-
}
|
|
303
|
-
chartData.value = transformedData
|
|
304
|
-
renderChart()
|
|
305
|
-
console.log('22222222222222222222222222')
|
|
306
|
-
emit('dataLoaded', transformedData)
|
|
307
|
-
} else {
|
|
308
|
-
// 没有数据源,使用空数据
|
|
309
|
-
chartData.value = []
|
|
310
|
-
renderChart()
|
|
311
|
-
}
|
|
312
|
-
} catch (error) {
|
|
313
|
-
console.error('HChart: 加载数据时发生错误:', error)
|
|
314
|
-
emit('error', error)
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// 渲染图表
|
|
319
|
-
const renderChart = () => {
|
|
320
|
-
if (!chartRef.value) return
|
|
321
|
-
nextTick(() => {
|
|
322
|
-
if (!chartInstance) {
|
|
323
|
-
chartInstance = echarts.init(chartRef.value)
|
|
324
|
-
// 监听窗口大小变化
|
|
325
|
-
resizeObserver = new ResizeObserver(() => {
|
|
326
|
-
chartInstance?.resize()
|
|
327
|
-
})
|
|
328
|
-
resizeObserver.observe(chartRef.value)
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
const config = chartConfig.value || props.config
|
|
332
|
-
if (!config || !config.type) return
|
|
333
|
-
|
|
334
|
-
const resolver = presetResolvers[config.type]
|
|
335
|
-
if (!resolver) {
|
|
336
|
-
console.warn(`HChart: 不支持的图表类型 ${config.type}`)
|
|
337
|
-
return
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
const preset = resolver({ dataset: chartData.value })
|
|
341
|
-
// 深度合并 series 配置
|
|
342
|
-
let finalOptions = {
|
|
343
|
-
legend: config.legend || {},
|
|
344
|
-
grid: config.grid || {},
|
|
345
|
-
...preset
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// 如果 options 中有 series,进行深度合并
|
|
349
|
-
if (config.options?.series && preset.series) {
|
|
350
|
-
// 分离需要特殊处理的配置项
|
|
351
|
-
const { xAxis: customXAxis, yAxis: customYAxis, series: customSeries, ...restOptions } = config.options
|
|
352
|
-
|
|
353
|
-
finalOptions = {
|
|
354
|
-
...finalOptions,
|
|
355
|
-
...restOptions,
|
|
356
|
-
// 对于有 xAxis 的图表类型(bar, line),智能合并 xAxis
|
|
357
|
-
...(preset.xAxis && {
|
|
358
|
-
xAxis: {
|
|
359
|
-
...preset.xAxis,
|
|
360
|
-
...customXAxis,
|
|
361
|
-
// 如果用户配置的 data 为空数组或未定义,使用预设的 data
|
|
362
|
-
data: (customXAxis?.data && customXAxis.data.length > 0)
|
|
363
|
-
? customXAxis.data
|
|
364
|
-
: preset.xAxis.data
|
|
365
|
-
}
|
|
366
|
-
}),
|
|
367
|
-
// 对于有 yAxis 的图表类型,合并 yAxis
|
|
368
|
-
...(preset.yAxis && {
|
|
369
|
-
yAxis: {
|
|
370
|
-
...preset.yAxis,
|
|
371
|
-
...customYAxis
|
|
372
|
-
}
|
|
373
|
-
}),
|
|
374
|
-
series: [
|
|
375
|
-
{
|
|
376
|
-
...preset.series[0],
|
|
377
|
-
...customSeries[0],
|
|
378
|
-
// 确保 data 来自预设(从 logic 获取的数据)
|
|
379
|
-
data: preset.series[0].data
|
|
380
|
-
}
|
|
381
|
-
]
|
|
382
|
-
}
|
|
383
|
-
} else {
|
|
384
|
-
// 没有自定义 series 的情况
|
|
385
|
-
const { xAxis: customXAxis, yAxis: customYAxis, ...restOptions } = config.options || {}
|
|
386
|
-
|
|
387
|
-
finalOptions = {
|
|
388
|
-
...finalOptions,
|
|
389
|
-
...restOptions,
|
|
390
|
-
// 对于有 xAxis 的图表类型,智能合并
|
|
391
|
-
...(preset.xAxis && {
|
|
392
|
-
xAxis: {
|
|
393
|
-
...preset.xAxis,
|
|
394
|
-
...customXAxis,
|
|
395
|
-
// 如果用户配置的 data 为空数组或未定义,使用预设的 data
|
|
396
|
-
data: (customXAxis?.data && customXAxis.data.length > 0)
|
|
397
|
-
? customXAxis.data
|
|
398
|
-
: preset.xAxis.data
|
|
399
|
-
}
|
|
400
|
-
}),
|
|
401
|
-
// 对于有 yAxis 的图表类型,合并 yAxis
|
|
402
|
-
...(preset.yAxis && {
|
|
403
|
-
yAxis: {
|
|
404
|
-
...preset.yAxis,
|
|
405
|
-
...customYAxis
|
|
406
|
-
}
|
|
407
|
-
})
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
chartInstance.setOption(finalOptions, true)
|
|
411
|
-
})
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// 监听 queryParamsName 变化,只有在名称变化时才重新获取配置
|
|
415
|
-
watch(
|
|
416
|
-
() => props.queryParamsName,
|
|
417
|
-
(newName, oldName) => {
|
|
418
|
-
if (newName !== oldName) {
|
|
419
|
-
console.log('99999999999999999999999999')
|
|
420
|
-
fetchConfigAndData()
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
)
|
|
424
|
-
|
|
425
|
-
// 暴露给外部的方法
|
|
426
|
-
const refresh = () => {
|
|
427
|
-
console.log('88888888888888888888888888')
|
|
428
|
-
fetchConfigAndData()
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
const reload = (newQueryForm) => {
|
|
432
|
-
if (newQueryForm) {
|
|
433
|
-
emit('update:fixedQueryForm', { ...props.fixedQueryForm, ...newQueryForm })
|
|
434
|
-
} else {
|
|
435
|
-
console.log('7777777777777777777777777')
|
|
436
|
-
fetchConfigAndData()
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
defineExpose({
|
|
441
|
-
refresh,
|
|
442
|
-
reload,
|
|
443
|
-
chartInstance
|
|
444
|
-
})
|
|
445
|
-
|
|
446
|
-
onMounted(() => {
|
|
447
|
-
// 初始化时记录 queryParamsName
|
|
448
|
-
lastQueryParamsName.value = props.queryParamsName
|
|
449
|
-
console.log('66666666666666666666666666666')
|
|
450
|
-
fetchConfigAndData()
|
|
451
|
-
})
|
|
452
|
-
|
|
453
|
-
onBeforeUnmount(() => {
|
|
454
|
-
// 清理图表实例和观察器
|
|
455
|
-
if (chartInstance) {
|
|
456
|
-
chartInstance.dispose()
|
|
457
|
-
chartInstance = null
|
|
458
|
-
}
|
|
459
|
-
if (resizeObserver) {
|
|
460
|
-
resizeObserver.disconnect()
|
|
461
|
-
resizeObserver = null
|
|
462
|
-
}
|
|
463
|
-
})
|
|
464
|
-
</script>
|
|
465
|
-
|
|
466
|
-
<style scoped lang="less">
|
|
467
|
-
.h-chart-configurable {
|
|
468
|
-
display: flex;
|
|
469
|
-
flex-direction: column;
|
|
470
|
-
transition: transform 0.22s ease, box-shadow 0.22s ease;
|
|
471
|
-
|
|
472
|
-
&.h-chart-show-border {
|
|
473
|
-
border: 1px solid #E5E9F0;
|
|
474
|
-
}
|
|
475
|
-
&.h-chart-border-hide-right {
|
|
476
|
-
border-right: 0px;
|
|
477
|
-
}
|
|
478
|
-
&.h-chart-border-hide-left {
|
|
479
|
-
border-left: 0px;
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
.h-chart-configurable.hoverable:hover {
|
|
484
|
-
transform: translateY(-4px);
|
|
485
|
-
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.12);
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
.chart-canvas {
|
|
489
|
-
width: 100%;
|
|
490
|
-
margin-top: 10px;
|
|
491
|
-
min-height: 280px;
|
|
492
|
-
}
|
|
493
|
-
</style>
|
|
1
|
+
<template>
|
|
2
|
+
<div class="h-chart-configurable" :class="wrapperClassObject">
|
|
3
|
+
<x-title v-if="chartTitle" v-bind="xTitleAttrs" :title="chartTitle" />
|
|
4
|
+
<!-- 图表挂载容器 -->
|
|
5
|
+
<div ref="chartRef" class="chart-canvas" :style="chartStyle"></div>
|
|
6
|
+
</div>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script setup>
|
|
10
|
+
import { ref, watch, onMounted, computed, useAttrs, onBeforeUnmount, nextTick } from 'vue'
|
|
11
|
+
import * as echarts from 'echarts/core'
|
|
12
|
+
import { BarChart, LineChart, PieChart } from 'echarts/charts'
|
|
13
|
+
import { TooltipComponent, LegendComponent, GridComponent } from 'echarts/components'
|
|
14
|
+
import { CanvasRenderer } from 'echarts/renderers'
|
|
15
|
+
import { getConfigByName, runLogic } from '@vue2-client/services/api/common'
|
|
16
|
+
import XTitle from '../XTitle/XTitle.vue'
|
|
17
|
+
// 注册常用图表类型,避免每次重复导入
|
|
18
|
+
echarts.use([BarChart, LineChart, PieChart, TooltipComponent, LegendComponent, GridComponent, CanvasRenderer])
|
|
19
|
+
|
|
20
|
+
const props = defineProps({
|
|
21
|
+
// 配置名称(用于查询配置)
|
|
22
|
+
queryParamsName: {
|
|
23
|
+
type: String,
|
|
24
|
+
default: ''
|
|
25
|
+
},
|
|
26
|
+
// 服务名
|
|
27
|
+
serviceName: {
|
|
28
|
+
type: String,
|
|
29
|
+
default: 'af-his'
|
|
30
|
+
},
|
|
31
|
+
// 固定查询参数
|
|
32
|
+
fixedQueryForm: {
|
|
33
|
+
type: Object,
|
|
34
|
+
default: () => ({ condition: '1=1' })
|
|
35
|
+
},
|
|
36
|
+
config: {
|
|
37
|
+
type: Object,
|
|
38
|
+
default: null
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const emit = defineEmits(['dataLoaded', 'error', 'update:fixedQueryForm'])
|
|
43
|
+
|
|
44
|
+
const chartRef = ref(null)
|
|
45
|
+
let chartInstance = null
|
|
46
|
+
const chartConfig = ref(null)
|
|
47
|
+
const chartData = ref([])
|
|
48
|
+
const loading = ref(false)
|
|
49
|
+
let resizeObserver = null
|
|
50
|
+
|
|
51
|
+
// 记录上一次的 queryParamsName,用于比较变化
|
|
52
|
+
const lastQueryParamsName = ref('')
|
|
53
|
+
|
|
54
|
+
const chartTitle = computed(() => {
|
|
55
|
+
if (props.config?.xtitle) return props.config.xtitle
|
|
56
|
+
if (chartConfig.value?.xtitle) return chartConfig.value.xtitle
|
|
57
|
+
return ''
|
|
58
|
+
})
|
|
59
|
+
const xTitleAttrs = computed(() => {
|
|
60
|
+
if (props.config?.xtitleAttrs) return props.config.xtitleAttrs
|
|
61
|
+
if (chartConfig.value?.xtitleAttrs) return chartConfig.value.xtitleAttrs
|
|
62
|
+
return {}
|
|
63
|
+
})
|
|
64
|
+
const chartStyle = computed(() => {
|
|
65
|
+
const target = props.config ?? chartConfig.value ?? {}
|
|
66
|
+
if (target.style && typeof target.style === 'object') return target.style
|
|
67
|
+
return {}
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
const attrs = useAttrs()
|
|
71
|
+
const wrapperClassObject = computed(() => {
|
|
72
|
+
const a = attrs
|
|
73
|
+
const classes = {}
|
|
74
|
+
|
|
75
|
+
// 通用布尔样式开关(以存在/空字符串/'true' 为真)
|
|
76
|
+
const booleanStyleKeys = [
|
|
77
|
+
'show-border',
|
|
78
|
+
'border-hide-left',
|
|
79
|
+
'border-hide-right'
|
|
80
|
+
]
|
|
81
|
+
for (const key of booleanStyleKeys) {
|
|
82
|
+
const val = a[key]
|
|
83
|
+
const truthy = val === true || val === '' || val === 'true'
|
|
84
|
+
if (truthy) classes[`h-chart-${key}`] = true
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return classes
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
// 常用图表预设,统一处理 dataset → ECharts option 的映射
|
|
91
|
+
const presetResolvers = {
|
|
92
|
+
bar: ({ dataset }) => ({
|
|
93
|
+
animationDuration: 420,
|
|
94
|
+
animationEasing: 'cubicOut',
|
|
95
|
+
animationDelay: (_, idx) => idx * 60,
|
|
96
|
+
tooltip: {
|
|
97
|
+
trigger: 'axis',
|
|
98
|
+
axisPointer: {
|
|
99
|
+
type: 'shadow'
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
xAxis: { type: 'category', data: dataset.map(item => item.label) },
|
|
103
|
+
yAxis: { type: 'value' },
|
|
104
|
+
series: [
|
|
105
|
+
{
|
|
106
|
+
type: 'bar',
|
|
107
|
+
data: dataset.map(item => item.value),
|
|
108
|
+
itemStyle: {
|
|
109
|
+
color: '#3362DA',
|
|
110
|
+
borderRadius: [4, 4, 0, 0]
|
|
111
|
+
},
|
|
112
|
+
emphasis: {
|
|
113
|
+
itemStyle: {
|
|
114
|
+
color: '#4C7CFF',
|
|
115
|
+
shadowBlur: 18,
|
|
116
|
+
shadowColor: 'rgba(76, 124, 255, 0.45)'
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
}),
|
|
122
|
+
line: ({ dataset }) => ({
|
|
123
|
+
animationDuration: 420,
|
|
124
|
+
animationEasing: 'cubicOut',
|
|
125
|
+
animationDelay: (_, idx) => idx * 50,
|
|
126
|
+
tooltip: {
|
|
127
|
+
trigger: 'axis',
|
|
128
|
+
axisPointer: {
|
|
129
|
+
type: 'line'
|
|
130
|
+
}
|
|
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
|
+
},
|
|
152
|
+
lineStyle: {
|
|
153
|
+
width: 4
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
areaStyle: {
|
|
157
|
+
opacity: 0.1,
|
|
158
|
+
color: '#28C8B5'
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
}),
|
|
163
|
+
pie: ({ dataset }) => ({
|
|
164
|
+
animationDuration: 420,
|
|
165
|
+
animationEasing: 'cubicOut',
|
|
166
|
+
tooltip: {
|
|
167
|
+
trigger: 'item',
|
|
168
|
+
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
|
169
|
+
},
|
|
170
|
+
series: [
|
|
171
|
+
{
|
|
172
|
+
type: 'pie',
|
|
173
|
+
radius: '55%',
|
|
174
|
+
data: dataset.map(item => ({ name: item.label, value: item.value })),
|
|
175
|
+
itemStyle: {
|
|
176
|
+
borderColor: '#fff',
|
|
177
|
+
borderWidth: 2
|
|
178
|
+
},
|
|
179
|
+
emphasis: {
|
|
180
|
+
scale: true,
|
|
181
|
+
scaleSize: 6,
|
|
182
|
+
itemStyle: {
|
|
183
|
+
shadowBlur: 16,
|
|
184
|
+
shadowColor: 'rgba(0, 0, 0, 0.25)'
|
|
185
|
+
},
|
|
186
|
+
label: {
|
|
187
|
+
show: true,
|
|
188
|
+
fontWeight: 'bold'
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
]
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// 数据转换:将后端返回的数据转换为图表需要的 dataset 格式
|
|
197
|
+
const transformData = (rawData, dataMapping) => {
|
|
198
|
+
// 增强数据提取:支持从包装对象中提取数组
|
|
199
|
+
let dataArray = rawData
|
|
200
|
+
|
|
201
|
+
// 如果 rawData 是对象但不是数组,尝试提取数组数据
|
|
202
|
+
if (rawData && typeof rawData === 'object' && !Array.isArray(rawData)) {
|
|
203
|
+
// 尝试常见的包装字段
|
|
204
|
+
dataArray = rawData.data || rawData.result || rawData.list || rawData.items || rawData.records
|
|
205
|
+
|
|
206
|
+
// 如果还是找不到数组,检查是否所有值都是数组
|
|
207
|
+
if (!Array.isArray(dataArray)) {
|
|
208
|
+
const arrayValues = Object.values(rawData).filter(val => Array.isArray(val))
|
|
209
|
+
if (arrayValues.length === 1) {
|
|
210
|
+
dataArray = arrayValues[0]
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// 最终检查:必须是数组
|
|
216
|
+
if (!dataArray || !Array.isArray(dataArray)) {
|
|
217
|
+
console.warn('HChart: transformData 接收到的数据不是数组格式:', rawData)
|
|
218
|
+
return []
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// 如果配置了数据映射规则,使用映射规则
|
|
222
|
+
if (dataMapping) {
|
|
223
|
+
const { labelField = 'label', valueField = 'value' } = dataMapping
|
|
224
|
+
return dataArray.map(item => ({
|
|
225
|
+
label: item[labelField] || item.name || item.label || '',
|
|
226
|
+
value: Number(item[valueField] || item.value || 0)
|
|
227
|
+
}))
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// 默认处理:尝试自动识别
|
|
231
|
+
return dataArray.map(item => {
|
|
232
|
+
if (typeof item === 'object' && item !== null) {
|
|
233
|
+
return {
|
|
234
|
+
label: item.label || item.name || item.text || item.x || String(item[Object.keys(item)[0]] || ''),
|
|
235
|
+
value: Number(item.value || item.count || item.y || item[Object.keys(item)[1]] || 0)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return { label: String(item), value: 0 }
|
|
239
|
+
})
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// 获取配置和数据
|
|
243
|
+
const fetchConfigAndData = async () => {
|
|
244
|
+
if (!props.queryParamsName && !props.config) {
|
|
245
|
+
console.warn('HChart: queryParamsName 或 config 必须提供一个')
|
|
246
|
+
return
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
loading.value = true
|
|
251
|
+
|
|
252
|
+
// 如果直接提供了 config,直接使用
|
|
253
|
+
if (props.config) {
|
|
254
|
+
chartConfig.value = props.config
|
|
255
|
+
console.log('44444444444444444444444444444444')
|
|
256
|
+
await loadChartData(props.config)
|
|
257
|
+
return
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// 否则通过 queryParamsName 查询配置
|
|
261
|
+
getConfigByName(props.queryParamsName, props.serviceName, async (res) => {
|
|
262
|
+
if (!res) {
|
|
263
|
+
console.error('HChart: 未能获取到配置内容')
|
|
264
|
+
emit('error', new Error('未能获取到配置内容'))
|
|
265
|
+
return
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
chartConfig.value = res
|
|
269
|
+
console.log('3333333333333333333333333')
|
|
270
|
+
await loadChartData(res)
|
|
271
|
+
})
|
|
272
|
+
} catch (error) {
|
|
273
|
+
console.error('HChart: 获取配置或数据时发生错误:', error)
|
|
274
|
+
emit('error', error)
|
|
275
|
+
} finally {
|
|
276
|
+
loading.value = false
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// 加载图表数据
|
|
281
|
+
const loadChartData = async (config) => {
|
|
282
|
+
if (!config) return
|
|
283
|
+
|
|
284
|
+
try {
|
|
285
|
+
// 如果配置中直接提供了 dataset,直接使用
|
|
286
|
+
if (config.dataset && Array.isArray(config.dataset)) {
|
|
287
|
+
chartData.value = config.dataset
|
|
288
|
+
renderChart()
|
|
289
|
+
console.log('1111111111111111111111111')
|
|
290
|
+
emit('dataLoaded', chartData.value)
|
|
291
|
+
return
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// 如果配置中提供了 data(logicName),通过 runLogic 获取数据
|
|
295
|
+
if (config.data) {
|
|
296
|
+
const result = await runLogic(config.data, props.fixedQueryForm, props.serviceName)
|
|
297
|
+
|
|
298
|
+
// 转换数据格式
|
|
299
|
+
const transformedData = transformData(result, config.dataMapping)
|
|
300
|
+
if (transformedData.length === 0) {
|
|
301
|
+
console.warn('HChart: 数据转换后为空数组,原始数据:', result)
|
|
302
|
+
}
|
|
303
|
+
chartData.value = transformedData
|
|
304
|
+
renderChart()
|
|
305
|
+
console.log('22222222222222222222222222')
|
|
306
|
+
emit('dataLoaded', transformedData)
|
|
307
|
+
} else {
|
|
308
|
+
// 没有数据源,使用空数据
|
|
309
|
+
chartData.value = []
|
|
310
|
+
renderChart()
|
|
311
|
+
}
|
|
312
|
+
} catch (error) {
|
|
313
|
+
console.error('HChart: 加载数据时发生错误:', error)
|
|
314
|
+
emit('error', error)
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// 渲染图表
|
|
319
|
+
const renderChart = () => {
|
|
320
|
+
if (!chartRef.value) return
|
|
321
|
+
nextTick(() => {
|
|
322
|
+
if (!chartInstance) {
|
|
323
|
+
chartInstance = echarts.init(chartRef.value)
|
|
324
|
+
// 监听窗口大小变化
|
|
325
|
+
resizeObserver = new ResizeObserver(() => {
|
|
326
|
+
chartInstance?.resize()
|
|
327
|
+
})
|
|
328
|
+
resizeObserver.observe(chartRef.value)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const config = chartConfig.value || props.config
|
|
332
|
+
if (!config || !config.type) return
|
|
333
|
+
|
|
334
|
+
const resolver = presetResolvers[config.type]
|
|
335
|
+
if (!resolver) {
|
|
336
|
+
console.warn(`HChart: 不支持的图表类型 ${config.type}`)
|
|
337
|
+
return
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const preset = resolver({ dataset: chartData.value })
|
|
341
|
+
// 深度合并 series 配置
|
|
342
|
+
let finalOptions = {
|
|
343
|
+
legend: config.legend || {},
|
|
344
|
+
grid: config.grid || {},
|
|
345
|
+
...preset
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// 如果 options 中有 series,进行深度合并
|
|
349
|
+
if (config.options?.series && preset.series) {
|
|
350
|
+
// 分离需要特殊处理的配置项
|
|
351
|
+
const { xAxis: customXAxis, yAxis: customYAxis, series: customSeries, ...restOptions } = config.options
|
|
352
|
+
|
|
353
|
+
finalOptions = {
|
|
354
|
+
...finalOptions,
|
|
355
|
+
...restOptions,
|
|
356
|
+
// 对于有 xAxis 的图表类型(bar, line),智能合并 xAxis
|
|
357
|
+
...(preset.xAxis && {
|
|
358
|
+
xAxis: {
|
|
359
|
+
...preset.xAxis,
|
|
360
|
+
...customXAxis,
|
|
361
|
+
// 如果用户配置的 data 为空数组或未定义,使用预设的 data
|
|
362
|
+
data: (customXAxis?.data && customXAxis.data.length > 0)
|
|
363
|
+
? customXAxis.data
|
|
364
|
+
: preset.xAxis.data
|
|
365
|
+
}
|
|
366
|
+
}),
|
|
367
|
+
// 对于有 yAxis 的图表类型,合并 yAxis
|
|
368
|
+
...(preset.yAxis && {
|
|
369
|
+
yAxis: {
|
|
370
|
+
...preset.yAxis,
|
|
371
|
+
...customYAxis
|
|
372
|
+
}
|
|
373
|
+
}),
|
|
374
|
+
series: [
|
|
375
|
+
{
|
|
376
|
+
...preset.series[0],
|
|
377
|
+
...customSeries[0],
|
|
378
|
+
// 确保 data 来自预设(从 logic 获取的数据)
|
|
379
|
+
data: preset.series[0].data
|
|
380
|
+
}
|
|
381
|
+
]
|
|
382
|
+
}
|
|
383
|
+
} else {
|
|
384
|
+
// 没有自定义 series 的情况
|
|
385
|
+
const { xAxis: customXAxis, yAxis: customYAxis, ...restOptions } = config.options || {}
|
|
386
|
+
|
|
387
|
+
finalOptions = {
|
|
388
|
+
...finalOptions,
|
|
389
|
+
...restOptions,
|
|
390
|
+
// 对于有 xAxis 的图表类型,智能合并
|
|
391
|
+
...(preset.xAxis && {
|
|
392
|
+
xAxis: {
|
|
393
|
+
...preset.xAxis,
|
|
394
|
+
...customXAxis,
|
|
395
|
+
// 如果用户配置的 data 为空数组或未定义,使用预设的 data
|
|
396
|
+
data: (customXAxis?.data && customXAxis.data.length > 0)
|
|
397
|
+
? customXAxis.data
|
|
398
|
+
: preset.xAxis.data
|
|
399
|
+
}
|
|
400
|
+
}),
|
|
401
|
+
// 对于有 yAxis 的图表类型,合并 yAxis
|
|
402
|
+
...(preset.yAxis && {
|
|
403
|
+
yAxis: {
|
|
404
|
+
...preset.yAxis,
|
|
405
|
+
...customYAxis
|
|
406
|
+
}
|
|
407
|
+
})
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
chartInstance.setOption(finalOptions, true)
|
|
411
|
+
})
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// 监听 queryParamsName 变化,只有在名称变化时才重新获取配置
|
|
415
|
+
watch(
|
|
416
|
+
() => props.queryParamsName,
|
|
417
|
+
(newName, oldName) => {
|
|
418
|
+
if (newName !== oldName) {
|
|
419
|
+
console.log('99999999999999999999999999')
|
|
420
|
+
fetchConfigAndData()
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
// 暴露给外部的方法
|
|
426
|
+
const refresh = () => {
|
|
427
|
+
console.log('88888888888888888888888888')
|
|
428
|
+
fetchConfigAndData()
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const reload = (newQueryForm) => {
|
|
432
|
+
if (newQueryForm) {
|
|
433
|
+
emit('update:fixedQueryForm', { ...props.fixedQueryForm, ...newQueryForm })
|
|
434
|
+
} else {
|
|
435
|
+
console.log('7777777777777777777777777')
|
|
436
|
+
fetchConfigAndData()
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
defineExpose({
|
|
441
|
+
refresh,
|
|
442
|
+
reload,
|
|
443
|
+
chartInstance
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
onMounted(() => {
|
|
447
|
+
// 初始化时记录 queryParamsName
|
|
448
|
+
lastQueryParamsName.value = props.queryParamsName
|
|
449
|
+
console.log('66666666666666666666666666666')
|
|
450
|
+
fetchConfigAndData()
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
onBeforeUnmount(() => {
|
|
454
|
+
// 清理图表实例和观察器
|
|
455
|
+
if (chartInstance) {
|
|
456
|
+
chartInstance.dispose()
|
|
457
|
+
chartInstance = null
|
|
458
|
+
}
|
|
459
|
+
if (resizeObserver) {
|
|
460
|
+
resizeObserver.disconnect()
|
|
461
|
+
resizeObserver = null
|
|
462
|
+
}
|
|
463
|
+
})
|
|
464
|
+
</script>
|
|
465
|
+
|
|
466
|
+
<style scoped lang="less">
|
|
467
|
+
.h-chart-configurable {
|
|
468
|
+
display: flex;
|
|
469
|
+
flex-direction: column;
|
|
470
|
+
transition: transform 0.22s ease, box-shadow 0.22s ease;
|
|
471
|
+
|
|
472
|
+
&.h-chart-show-border {
|
|
473
|
+
border: 1px solid #E5E9F0;
|
|
474
|
+
}
|
|
475
|
+
&.h-chart-border-hide-right {
|
|
476
|
+
border-right: 0px;
|
|
477
|
+
}
|
|
478
|
+
&.h-chart-border-hide-left {
|
|
479
|
+
border-left: 0px;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
.h-chart-configurable.hoverable:hover {
|
|
484
|
+
transform: translateY(-4px);
|
|
485
|
+
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.12);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.chart-canvas {
|
|
489
|
+
width: 100%;
|
|
490
|
+
margin-top: 10px;
|
|
491
|
+
min-height: 280px;
|
|
492
|
+
}
|
|
493
|
+
</style>
|