227 lines
5.8 KiB
Vue
227 lines
5.8 KiB
Vue
<template>
|
||
<section class="AiEchart">
|
||
<div ref="AiEchart" class="chart" :style="{minWidth:grid.width,minHeight:grid.height}"/>
|
||
<slot v-if="$slots.default"/>
|
||
<render-component v-else-if="ops.render" :render="ops.render" :options="chartOptions" :data="data"/>
|
||
</section>
|
||
</template>
|
||
|
||
<script>
|
||
import * as echarts from 'echarts'
|
||
|
||
export default {
|
||
name: "AiEchartV2",
|
||
props: {
|
||
data: {default: () => []},
|
||
ops: {default: () => ({})},
|
||
type: {default: "line"},
|
||
series: Object,
|
||
theme: {
|
||
default: '0'
|
||
}
|
||
},
|
||
components: {
|
||
renderComponent: {
|
||
functional: true,
|
||
props: {
|
||
render: Function,
|
||
options: {default: () => ({})},
|
||
data: {default: () => []}
|
||
},
|
||
render(h, ins) {
|
||
let {options = {}, data} = ins.props
|
||
|
||
return ins.props.render(h, {...options, data})
|
||
}
|
||
}
|
||
},
|
||
|
||
data() {
|
||
return {
|
||
chart: null,
|
||
timer: null
|
||
}
|
||
},
|
||
computed: {
|
||
colors() {
|
||
if (this.theme === '0') {
|
||
return ['#00F9FF', '#1890FF', '#B13BFF', '#FC3BFF', '#95FF44', '#ea7ccc']
|
||
}
|
||
|
||
return ['#FFBA44', '#EC6666', '#FF3E18', '#C9FF82', '#29D7FA', '#ea7ccc']
|
||
},
|
||
|
||
chartOptions() {
|
||
let {type, data, ops: options = {}} = this,
|
||
style = this.series ? this.series : this.ops.daemon ? this.ops.daemon : {},
|
||
colors = this.theme === '1' ? this.colors : (options.color || this.colors),
|
||
legend = {textStyle: {color: '#fff', padding: [0, 0, 0, 8], fontSize: 14}, show: false},
|
||
series = data?.[0] ? Array(Object.keys(data?.[0]).length - 1).fill(1)
|
||
.map((e, i) => ({type, ...(typeof style == 'object' ? style : style(colors[i]))})) : []
|
||
let ops = {
|
||
tooltip: {},
|
||
xAxis: {
|
||
type: 'category', nameGap: 20, axisTick: false,
|
||
axisLabel: {
|
||
color: '#C3C4C4',
|
||
interval: 0,
|
||
rotate: this.data.length > 10 ? 40 : 0
|
||
},
|
||
axisLine: {
|
||
lineStyle: {
|
||
color: this.theme === '1' ? 'rgba(239, 163, 51, 0.8)' : 'rgba(255,255,255,.5)',
|
||
width: 1
|
||
}
|
||
}
|
||
},
|
||
// 声明一个 Y 轴,数值轴。
|
||
yAxis: {
|
||
nameGap: 23, minInterval: 1,
|
||
splitLine: {
|
||
lineStyle: {
|
||
color: this.theme === '1' ? 'rgba(255, 197, 52, 0.4)' : 'rgba(255, 255, 255, .2)',
|
||
type: 'dashed'
|
||
}
|
||
},
|
||
axisLabel: { color: '#C3C4C4' },
|
||
axisLine: {
|
||
lineStyle: {
|
||
color: '#C3C4C4'
|
||
}
|
||
}
|
||
},
|
||
legend, series, ...options,
|
||
color: colors,
|
||
grid: {
|
||
left: '0%',
|
||
right: '0%',
|
||
bottom: '0%',
|
||
top: '40px',
|
||
containLabel: true
|
||
}
|
||
}
|
||
return ops
|
||
},
|
||
grid() {
|
||
let {width, height} = this.chartOptions.grid || {}
|
||
return {
|
||
width: width ? (width - this.legend === '1' ? 160 : 0) + 'px' : 'auto',
|
||
height: height ? height + 'px' : 'auto'
|
||
}
|
||
}
|
||
},
|
||
watch: {
|
||
data: {
|
||
deep: true, handler(v, old) {
|
||
let oldDims = Object.keys(old?.[0] || {})?.toString(),
|
||
current = Object.keys(v?.[0] || {})?.toString()
|
||
this.getChartData(oldDims != current)
|
||
}
|
||
},
|
||
|
||
theme() {
|
||
this.refresh()
|
||
}
|
||
},
|
||
methods: {
|
||
deepAssign() {
|
||
let name, options, src, copy
|
||
let length = arguments.length
|
||
// 记录要复制的对象的下标
|
||
let i = 1
|
||
// target默认是第一个参数
|
||
let target = arguments[0] || {}
|
||
// 如果target不是对象,我们是无法进行复制的,所以设为{}
|
||
if (typeof target !== 'object') {
|
||
target = {}
|
||
}
|
||
// 循环遍历要复制的对象
|
||
for (; i < length; i++) {
|
||
// 获取当前对象
|
||
options = arguments[i]
|
||
// 要求不能为空 避免extend(a,,b)这种情况
|
||
if (options != null) {
|
||
for (name in options) {
|
||
// 目标属性值
|
||
src = target[name]
|
||
// 要复制的对象的属性值
|
||
copy = options[name]
|
||
|
||
if (copy && typeof copy == 'object') {
|
||
// 递归调用
|
||
target[name] = this.deepAssign( src, copy)
|
||
} else if (copy !== undefined) {
|
||
target[name] = copy
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return target
|
||
},
|
||
getChartData(render) {
|
||
if (!render) {
|
||
this.chart?.setOption({
|
||
dataset: {
|
||
source: this.data || []
|
||
}
|
||
})
|
||
} else {
|
||
this.chart?.setOption({
|
||
...this.chartOptions,
|
||
dataset: {
|
||
source: this.data || []
|
||
}
|
||
}, true)
|
||
}
|
||
},
|
||
initChart() {
|
||
this.chart = echarts.init(this.$refs.AiEchart)
|
||
this.chart.setOption(this.chartOptions || {})
|
||
},
|
||
watchResize() {
|
||
this.timer && clearInterval(this.timer)
|
||
this.timer = setInterval(() => {
|
||
if (this.chart?.getHeight() != this.$refs.AiEchart?.clientHeight ||
|
||
this.chart?.getWidth() != this.$refs.AiEchart?.clientWidth) {
|
||
this.chart?.resize()
|
||
}
|
||
}, 1000)
|
||
//5分钟后停止监听
|
||
setTimeout(() => this.timer && clearInterval(this.timer), 5 * 60 * 1000)
|
||
},
|
||
refresh() {
|
||
this.chart.setOption(this.chartOptions || {})
|
||
}
|
||
},
|
||
mounted() {
|
||
this.$nextTick(() => {
|
||
this.watchResize()
|
||
this.initChart()
|
||
this.getChartData()
|
||
})
|
||
},
|
||
beforeDestroy() {
|
||
this.timer && clearInterval(this.timer)
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.AiEchart {
|
||
display: flex;
|
||
width: 100%;
|
||
height: 100%;
|
||
flex: 1;
|
||
min-width: 100px;
|
||
min-height: 0;
|
||
position: relative;
|
||
|
||
.chart {
|
||
flex: 1;
|
||
height: 100%;
|
||
min-height: 100px;
|
||
}
|
||
}
|
||
</style>
|