echarts优秀使用案例
App.vue
<template>
<div class="box">
<a-card size="small" title="各类型监测设备利用率" class="center">
<!-- color 值由组件外部进行传递(会重复修改的部分尽量减少逻辑复杂程度) -->
<!-- type 值直接传输汉字(没有必要进行判断) -->
<SmallChartVue :data="store.EquUseRatio" type="利用率" :color="['#66a6ff', '#89f7fe']" />
</a-card>
</div>
<div class="box">
<a-card size="small" title="各类型监测站在线率" class="center">
<SmallChartVue :data="store.StationOnlineRatio" type="在线率" :color="['#84fab0', '#8fd3f4']" />
</a-card>
</div>
</template>
<script setup>
import SmallChartVue from './SmallChart.vue';
import { onUnmounted } from 'vue';
import { UseRateStore } from './store.js'
const store = UseRateStore();
// 初始化数据加载及获取
store.initData();
// 实时循环刷新数据
var Timer = setInterval(() => {
store.initData();
}, 2000);
// 注销时,清除循环
onUnmounted(() => {
clearInterval(Timer);
});
</script>
<style scoped>
.box {
width: 310px;
height: 175px;
margin-bottom: 15px;
}
:deep(.ant-card) {
background: #191a23;
}
:deep(.ant-card-head-title) {
color: #fff;
/* 标题内边距 */
padding: 12px 0 !important;
/* 字间距 */
letter-spacing: 2px;
}
:deep(.ant-card) {
border: 1px solid rgba(255, 255, 255, 0.1)
}
:deep(.ant-card-body) {
height: 130px;
padding: 5px;
}
:deep(.params-name) {
text-align: left;
color: rgba(255, 255, 255, 0.5);
}
:deep(.params-value) {
text-align: right;
color: #18be6b;
}
:deep(.w-100) {
width: 100px;
}
</style>
SmallChart.vue
<template>
<div ref="SmallDar" class="wh-100"></div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, watch } from 'vue';
import chart from './SmallChart.js'
var _chart = null;
const props = defineProps({
data: Object,
type: String,
color: Array,
})
const SmallDar = ref();
// 格式化数据方法
const buildChart = (res) => {
// 哪怕 res.length 值为 0 ,只会不渲染数据,并不会阻止程序运行
// 深拷贝位置更改
let data = JSON.parse(JSON.stringify(res));
var xAxisData = [];
var SeriesData = [];
for (var i = 0; i < data.length; i++) {
xAxisData.push(data[i].name)
SeriesData.push(data[i].value)
}
_chart.setData(xAxisData, SeriesData);
}
// 监听数据变化
watch(() => props.data, (res) => {
buildChart(res);
})
// 初始化图表
onMounted(() => {
_chart = new chart(SmallDar.value, {
Grid: {
top: 15,
left: 30,
right: 8,
bottom: 25
},
type: props.type,
Color: props.color,
});
buildChart(props.data);
})
// 组件注销时,删除图表
onUnmounted(() => {
_chart.destroy()
})
</script>
<style scoped>
.wh-100 {
width: 100%;
height: 100%;
}
</style>
SmallChart.js
import * as echarts from 'echarts'
class BarEchart {
constructor(view, option) {
this.chart = echarts.init(view, 'dark');
this.color = option.Color;
this.grid = option.Grid;
// type 值由外部传递
this.type = option.type || '';
// 给最大值最小值一个默认值
this.MAX = option.MAX || 100;
this.MIN = option.MIN || 0;
}
setData(xAxisData, SeriesData) {
// 删除了 UI 中用不到的 Echart 配置项
var option = {
backgroundColor: '#191a23',
tooltip: {
trigger: 'axis',
backgroundColor: "#191a23",
borderColor: "rgba(255, 255, 255, 0.1)",
axisPointer: {
type: 'shadow'
},
formatter: (params) => {
// 使用模板语法,看起来更好看一些
// 使他对其到左侧,为了实际输出代码时,减少左侧的字符串(其实没啥用)
return `
${params[0].name}<br/>
<div class="ant-row w-100">
<div class="ant-col ant-col-12 params-name">${this.type}</div>
<div class="ant-col ant-col-12 params-value">${params[0].value}%</div>
</div>`;
}
},
grid: this.grid,
xAxis: {
type: 'category',
axisLine: {
show: true,
lineStyle: {
// 此处颜色改为实色,挡住最后一个分割中的虚线
color: 'rgba(80, 80, 80)',
},
},
axisTick: {
show: false
},
axisLabel: {
padding: [3, 0, 0, 0],
color: 'rgba(255, 255, 255, 0.5)',
// 修改文字大小
fontSize: 12,
// 强制显示X轴所有标签
interval: 0
},
data: xAxisData,
},
yAxis: {
type: 'value',
min: this.MIN,
max: this.MAX,
// 设置分割间隔
interval: 25,
axisLabel: {
show: true,
// 修改文字大小
fontSize: 11,
color: 'rgba(255, 255, 255, 0.5)'
},
splitLine: {
show: true,
// 虚线
lineStyle: {
type: [2, 1],
}
},
},
series: [{
name: '排名',
type: 'bar',
// 修改柱状条宽度
barWidth: '35%',
data: SeriesData,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: this.color[0] },
{ offset: 1, color: this.color[1] },
])
},
}]
};
this.chart.setOption(option);
}
destroy() {
// 修改了 注销方法
this.chart.dispose();
}
}
export default BarEchart
TestData.js
// 测试数据生成方法
// 用于接口伪装
const random = (min, max) => min + Math.floor(Math.random() * (max - min + 1))
let renderFunction = (label) => {
var total = random(0, 100);
var online = random(0, total);
var working = random(0, online);
return {
label: label,
total: total,
online: online,
working: working
}
}
let LabelArray = ["一类", "二类", "三类", "四类", "监测", "船载", "便携", "传感器"];
const install = () => {
// 数据由同步获取改为异步获取
return new Promise((resolve, reject) => {
let res = [];
LabelArray.forEach(item => {
res.push(renderFunction(item))
});
// 给数组一个随机函数,用来打乱数组顺序(给自己增加难度)
res.sort((a, b) => Math.random() > 0.5 ? 1 : -1)
resolve(res);
});
}
export default install;
store.js
import { defineStore } from 'pinia'
import TestData from './TestData.js';
// 格式化数据方法
const Formatter = (_arr, callback) => {
// 使用Array.prototype.map()方法生成新数组,可以省略创建数组的一步。
// 如果严格要求效率的情况下,仍然建议使用For循环
return _arr.map(item => {
return {
name: item.label,
// 因为是计算属性,在不计较 ∞ 时,直接判断是否为真
value: callback(item) || 0,
}
});
}
// 此处 store ID 是为了区分示例项目模块。请勿效仿
export const UseRateStore = defineStore('901_inspect', {
state: () => {
return {
// 将两份数据分开进行存放,更贴合业务实际情况
EquData: [],
StationData: [],
}
},
getters: {
// 通过Getter 实时刷新数据
EquUseRatio() {
return Formatter(this.EquData, (stat) => {
// 取两位小数并四舍五入(注,不用 Number.toFix(2) 方法的原因是这种方法会出现 20.00 的情况 )
return Math.round(stat.working / stat.total * 10000) / 100;
});
},
StationOnlineRatio() {
return Formatter(this.StationData, (stat) => {
return Math.round(stat.online / stat.total * 10000) / 100;
});
},
},
actions: {
// 初始化数据方法,使数据可以实时刷新
async initData() {
// 假设数组返回值时乱序的,如何根据已有数据顺序排序返回值
// Array 写在这个地方是为了方便大家寻找逻辑,实际情况请将 Array 对象放在全局中,防止重复创建
const LabelArray = ["一类", "二类", "三类", "四类", "监测", "船载", "便携", "传感器"];
// 根据指定的 Key 值,在已经排序好的数组里面取 当前值的下标,
// 下方为 indexOf 方法示例
this.EquData = (await TestData()).sort((a, b) => {
return LabelArray.indexOf(a.label) - LabelArray.indexOf(b.label)
});
// 如果 indexOf 的效率满足不了需求,还可以用 Mapping 结构提升效率查询值过程的效率
// 注:Mapping 写在这个地方是为了方便大家寻找逻辑,实际情况请将 Mapping 对象放在全局中,防止重复创建
const LabelMapping = {
"一类": 0,
"二类": 1,
"三类": 2,
"四类": 3,
"监测": 4,
"船载": 5,
"便携": 6,
"传感器": 7
}
this.StationData = (await TestData()).sort((a, b) => {
return LabelMapping[a.label] - LabelMapping[b.label]
});;
},
}
});