前端可视化数据环图封装
- html
<template>
<div class="container">
<div
ref="loop"
style=""
:style="{ width: `${width}`, height: `${height}` }"
></div>
<div
v-if="legendSelect"
:style="{ top: styleData.top, left: styleData.left }"
class="loop_label"
>
<div class="text" v-text="selectedSeries.legend"></div>
<div class="num" v-text="selectedSeries.measure"></div>
</div>
<div class="progress-box" v-if="progress">
<div class="progress-top">
<div class="progress-top-label">
<span>工单总数</span>
<span>单位: 个</span>
</div>
<div class="progress-top-total">
{{ sum }}
</div>
</div>
<div class="progress-bottom">
<ul>
<li v-for="(v, i) in loopData" :key="i">
<div class="progress-name">
<span>{{ v.name }}</span>
<span :style="{ color: v.itemStyle.color }">{{ v.num }}</span>
</div>
<div
class="progress-bar"
:style="{ backgroundColor: v.itemStyle.color, width: v.num }"
></div>
<div class="progress-bar-bgc"></div>
</li>
</ul>
</div>
</div>
<ul class="bfb_list" v-if="!progress">
<li
v-for="(v, i) in loopData"
:key="i"
:style="{ color: v.itemStyle.color }"
@click="tabClick(v, i)"
>
{{ v.num }}
</li>
</ul>
</div>
</template>
2.js
<script>
export default {
props: {
width: {
type: String,
default: "360px",
},
height: {
type: String,
default: "160px",
},
title: {
type: String,
default: "景区资源",
},
nuit: {
type: String,
default: "个",
},
center: {
type: Array,
default: () => {
return ["60%", "50%"];
},
},
styleData: {
type: Object,
default: () => {
return {
textSize: "18px",
textColor: "#BFEBFF",
valueSize: "48px",
valueColor: "#FFFFFF",
top: "34%",
left: "8%",
};
},
},
//图例
legendSelect: { type: Boolean, default: true },
//图例数据
legends: {
type: Array,
default: () => {
return [
"#00FFFF",
"#FCEF03",
"#95FF2B",
"#15BFFF",
"#00DD8A",
"#2872EC",
];
},
},
displayModel: {
type: Number,
default: 0, //0 图例 1 进度条 2 图例+个数
},
rowData: {
type: Array,
default: () => {
return [
{
measure: 3011,
legend: "已处理",
},
{
measure: 3211,
legend: "未处理",
},
{
measure: 2511,
legend: "处理中",
},
];
},
},
progress: {
type: Boolean,
default: true,
},
},
data() {
return {
loopData: [],
num: 0,
//当前选中的数据
selectedSeries: {
legend: "",
measure: "",
},
sum: 0,
myChart: {},
};
},
mounted() {
this.$nextTick(() => {
this.draw();
});
},
methods: {
draw() {
// 基于准备好的dom,初始化echarts实例
this.myChart = this.$echarts.init(this.$refs.loop);
this.option = {
tooltip: {
trigger: "item",
formatter: "{b}:{d}%",
},
series: [
{
type: "pie",
radius: ["75%", "85%"],
center: this.center,
hoverAnimation: false,
selectedMode: "single",
label: {
show: false,
position: "center",
},
labelLine: {
show: false,
},
data: [],
},
],
};
if (this.legendSelect) {
this.option.legend = {
show: !this.progress,
orient: "vertical",
top: 10,
right: 60,
textStyle: {
color: "#BFEBFF",
fontSize: 14,
},
itemWidth: 8,
itemHeight: 8,
selectedMode: false,
};
}
this.loopData = this.convertData(this.rowData, this.legends);
this.option.series[0].data = this.loopData;
this.carousel();
this.myChart.setOption(this.option);
},
//数据转换
convertData(rowData, legends) {
this.sum = 0;
let returnData = [];
if (rowData.length > 0) {
rowData.forEach((d, index) => {
this.sum += d.measure;
returnData.push({
value: d.measure,
name: d.legend,
itemStyle: {
color: legends[index % 10],
},
});
});
returnData.forEach((d) => {
d.num =
this.sum == 0 ? "0" : ((d.value / this.sum) * 100).toFixed(2) + "%";
d.sum = this.sum == 0 ? "0" : this.sum;
});
if (returnData.length > 0 && returnData[0].value == this.sum) {
returnData[0].num = "100%";
}
//进度条模式时 倒序排序
if (this.displayModel == 1) {
returnData.sort((a, b) => {
return b.value - a.value;
});
}
//增加千分位
if (this.displayModel == 2) {
let re = /(?=(?!(\b))(\d{3})+$)/g;
returnData.forEach((r) => {
r.value = String(r.value).replace(re, ",");
});
}
} else {
returnData.push({
name: "暂无数据",
value: "0",
num: "0%",
itemStyle: { color: "#373F55" },
});
}
return returnData;
},
setTimeToDo() {
let that = this;
let data = that.option.series[0].data;
that.selectedSeries.legend = data[that.num].name;
that.selectedSeries.measure = data[that.num].num;
// 必须重新设置option,因为echart没有监听data的变化
if (data.length > 2) {
data.forEach((e) => (e.selected = false));
data[that.num].selected = true;
that.option && that.myChart.setOption(that.option);
}
},
setHighLight() {
let that = this;
clearInterval(that.timer);
const data = that.option.series[0].data;
that.timer = setInterval(() => {
if (that.num < data.length - 1) {
that.num++;
} else {
that.num = 0;
}
that.setTimeToDo();
}, 1000);
},
//轮播
carousel() {
if (this.option.series[0].data.length > 0) {
//开启定时器
this.setTimeToDo();
this.setHighLight();
}
},
tabClick(v, i) {
this.$emit("statusClick", v.name);
},
},
watch: {
rowData: {
handler(neVal) {
let that = this;
that.option.series[0].data = [];
that.loopData = that.convertData(neVal, that.legends);
that.option.series[0].data = that.loopData;
that.$nextTick(() => {
that.num = 0;
that.selectedSeries = {
legend: "",
measure: "",
};
// that.sum = 0;
let chart = that.$echarts.init(that.$refs.loop);
that.carousel();
chart.setOption(that.option);
});
},
deep: false,
},
},
destroyed() {
this.timer && clearInterval(this.timer);
},
};
</script>
3.style基于scss
<style lang="scss" scoped>
.loop_label {
width: 59%;
position: absolute;
top: 29%;
left: 0;
height: 49%;
// text-shadow: 0 0 3px #000000;
font-size: 0px;
.text {
// padding-left: 35px;
font-size: 16px;
font-weight: "400";
// overflow: hidden;
// text-overflow: ellipsis;
// white-space: nowrap;
width: 100%;
text-align: center;
font-family: "微软雅黑";
color: #ECECEC;
// margin: 4px 0;
// background-image: -webkit-linear-gradient(#ffffff, #99ecfb);
// background-clip: text;
// -webkit-text-fill-color: transparent;
}
.num {
font-size: 42px;
font-family: "Voltage";
text-align: center;
font-weight: "400";
color: #FFFFFF;
// background-image: -webkit-linear-gradient(#ffffff, #8a9eb5);
// background-clip: text;
// -webkit-text-fill-color: transparent;
}
}
.bfb_list {
font-family: "DINCond-Medium";
font-size: 20px;
width: 160px;
position: absolute;
top: 16px;
right: 6px;
z-index: 99999999;
color: #00ffff;
li {
list-style: none;
cursor: pointer;
text-align: right;
margin-top: 2px;
}
}
.progress-box {
position: absolute;
right: 10px;
top: 11px;
width: 180px;
height: 136px;
display: flex;
flex-direction: column;
justify-content: space-between;
.progress-top {
width: 100%;
height: 36px;
display: flex;
justify-content: space-between;
.progress-top-label {
width: 65px;
height: 100%;
// background-color: #fff;
span:nth-child(1) {
font-weight: bold;
font-size: 16px;
color: #fff;
}
span:nth-child(2) {
font-size: 14px;
color: #14bfff;
}
}
.progress-top-total {
font-family: "DINCond-Medium";
font-size: 36px;
line-height: 36px;
color: #26c9ff;
}
}
.progress-bottom {
width: 100%;
height: 99px;
li {
list-style: none;
position: relative;
width: 100%;
height: 32px;
.progress-name {
position: relative;
width: 100%;
height: 26px;
line-height: 26px;
span:nth-child(1) {
color: #aae3ff;
font-size: 14px;
}
span:nth-child(2) {
font-family: "DINCond-Medium";
position: absolute;
right: 0;
font-size: 20px;
}
}
.progress-bar {
position: absolute;
bottom: 0;
height: 6px;
z-index: 99999;
}
.progress-bar-bgc {
width: 180px;
height: 6px;
background-color: #373f55;
}
}
}
}
.container {
width: 100%;
height: 100%;
position: relative;
display: flex;
align-items: center;
pointer-events: all;
margin-top: -31px;
.left {
flex: 1;
}
.right {
overflow: auto;
flex: 1;
width: 100%;
height: 80%;
justify-content: center;
align-items: center;
max-height: calc(100% - 20px);
}
}
</style>
4.如图