204 lines
5.4 KiB
Vue
204 lines
5.4 KiB
Vue
<template>
|
|
<section class="AiChartV2">
|
|
<div ref="chart" 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>
|
|
const mergeOps = (ori = {}, ext = {}) => {
|
|
for (const k in ext) {
|
|
if (typeof ext[k] == 'object') {
|
|
ori[k] = mergeOps(ori[k], ext[k])
|
|
} else ori[k] = ext[k]
|
|
}
|
|
return ori
|
|
}
|
|
|
|
export default {
|
|
name: "AiEchartV2",
|
|
props: {
|
|
data: {default: () => []},
|
|
ops: {default: () => ({})},
|
|
tpl: String,
|
|
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() {
|
|
const {daemon = {}} = this.$echartTpls[this.tpl] || {} //js代码进入json编辑器会导致函数丢失,所以在这里重新补齐
|
|
let {data, ops: options = {}} = this,
|
|
style = this.series || options.daemon || {},
|
|
colors = this.theme === '1' ? this.colors : (options.color || this.colors),
|
|
legend = {textStyle: {color: '#fff', padding: [0, 0, 0, 8], fontSize: 14}, show: false}
|
|
const series = data?.[0] ? Array(Object.keys(data?.[0]).length - 1).fill(1)
|
|
.map((e, i) => mergeOps(typeof daemon == 'function' ? daemon(colors[i]) : daemon, style)) : []
|
|
return {
|
|
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'
|
|
}
|
|
}
|
|
},
|
|
grid: {
|
|
left: '0%',
|
|
right: '0%',
|
|
bottom: '0%',
|
|
top: '26px',
|
|
containLabel: true
|
|
},
|
|
legend, series, ...options,
|
|
color: colors
|
|
}
|
|
},
|
|
grid() {
|
|
let {width, height} = this.chartOptions.grid || {}
|
|
return {
|
|
width: width ? (width - this.legend === '1' ? 160 : 0) + 'px' : 'auto',
|
|
height: height ? height + 'px' : 'auto'
|
|
}
|
|
},
|
|
seriesType: v => [...new Set(v.chart.getOption().series?.map(e => e.type) || [])],
|
|
},
|
|
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: {
|
|
getChartData(render) {
|
|
let data = this.data, option = {}
|
|
const canUseDataset = !!["line", "bar", "pie", "scatter", "effectScatter", "parallel", "candlestick", "map", "funnel", "custom"].some(e => this.seriesType.includes(e))
|
|
if (!render) {
|
|
for (const k in this.chartOptions) {
|
|
option[k] = this.chartOptions[k]
|
|
}
|
|
}
|
|
if (canUseDataset) {
|
|
option.dataset = {source: data || []}
|
|
} else {
|
|
option.series = option.series?.map((e = {}) => {
|
|
if (e.dataRender) {
|
|
data = data.map(e.dataRender)
|
|
}
|
|
return {...e, data}
|
|
})
|
|
console.log(option.series)
|
|
}
|
|
this.chart.setOption(option)
|
|
},
|
|
initChart() {
|
|
const {echarts} = window
|
|
this.chart = echarts.init(this.$refs.chart)
|
|
this.chart.setOption(this.chartOptions || {})
|
|
},
|
|
watchResize() {
|
|
this.timer && clearInterval(this.timer)
|
|
this.timer = setInterval(() => {
|
|
if (this.chart?.getHeight() != this.$refs.chart?.clientHeight ||
|
|
this.chart?.getWidth() != this.$refs.chart?.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>
|
|
.AiChartV2 {
|
|
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>
|