图表组件

This commit is contained in:
yanran200730
2023-03-09 18:01:14 +08:00
parent b30ce1c9a2
commit cef67555bf
4 changed files with 314 additions and 42 deletions

View File

@@ -0,0 +1,286 @@
<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"/>
<div class="AiEchart-legend" v-if="legend === '1'">
<div class="AiEchart-legend__item">居民好友5230</div>
<div class="AiEchart-legend__item">居民好友5230</div>
<div class="AiEchart-legend__item">居民好友5230</div>
</div>
</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'
},
legend: {
type: String,
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', '#EC6666', '#FC3BFF', '#1890FF', '#95FF44', '#ea7ccc']
}
return ['#D4380D', '#CF1322', '#D55800', '#FA8C16', '#FFC53D', '#FFA940', '#FFC53D', '#780000']
},
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)'
}
}
},
// 声明一个 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)
console.log(data)
}
},
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()
})
console.log(this.data)
},
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;
}
.AiEchart-legend {
width: 140px;
margin-left: 20px;
padding-top: 20px;
div {
display: flex;
position: relative;
align-items: center;
justify-content: center;
height: 40px;
margin-bottom: 8px;
text-align: center;
color: #fff;
font-size: 14px;
border: 1px solid #717171;
box-sizing: border-box;
&:last-child {
margin-bottom: 0;
}
&::after {
position: absolute;
left: -1px;
top: 50%;
z-index: 11;
width: 1px;
height: 10px;
transform: translateY(-50%);
background: #0c0c0c;
content: ' ';
}
&::before {
position: absolute;
right: - 1px;
top: 50%;
z-index: 11;
width: 1px;
height: 10px;
transform: translateY(-50%);
background: #0c0c0c;
content: ' ';
}
}
}
}
</style>