1.图表预览
2.注释说明
Vue 的组件开发
- 通过
props
定义外部传入的属性,例如 className
、width
、height
、autoResize
、chartData
等。 - 使用 Vue 的生命周期钩子函数
mounted
进行 ECharts 的实例化,确保组件加载完毕后才初始化图表。 - 通过
watch
监听 chartData
数据的变化,自动更新图表。
ECharts 实例化和配置
- 使用
echarts.init(this.$el)
创建图表实例,绑定到 Vue 组件的 DOM 元素上。 this.chart.setOption(option, true)
更新 ECharts 图表的配置。- 定义了自适应窗口大小的功能,监听
window.onresize
,动态调整图表大小。
计算方法和自定义配置
fontSize(res)
根据窗口宽度动态计算字体大小,用于图表的响应式布局。getPie3D(pieData, internalDiameterRatio)
和 getParametricEquation()
是生成 3D 饼图的关键函数,使用数学公式构建 3D 饼图的曲面方程,并自定义了饼图的大小、半径、角度、选中效果等。formatter
函数用于自定义图表的 legend
和 tooltip
显示格式,返回带有富文本样式的字符串。
3.页面引入
<div class="com-view-body c-dis-flex-col c-hidden">
<div class="c-width c-flex c-dis-flex-col c-hidden">
<ChartView :chart-data="riskChartData"></ChartView>
</div>
</div>
import { randomNum } from '@/utils/index.js'
import ChartView from '@/components/Charts/ChartView.vue'
riskChartData: {
name: "风险总数",
color: ["#ED6F81", "#F3843D", "#4B64F7", "#34C294"],
legend: ['二级风险', '三级风险', '四级风险', '五级风险'],
data: [
{ name: "二级风险", value: 0, unit: "个" },
{ name: "三级风险", value: 0, unit: "个" },
{ name: "四级风险", value: 0, unit: "个" },
{ name: "五级风险", value: 0, unit: "个" },
],
},
4.图表组件
<template>
<div :class="className" :style="{ height: height, width: width }" />
</template>
<script>
import * as echarts from 'echarts'; // 导入 echarts 库
import "echarts-gl"; // 导入 echarts 的 3D 图表支持
import resize from '@/components/mixin/resize'; // 导入窗口 resize 相关的 mixin
export default {
mixins: [resize], // 引入 mixin
props: {
className: {
type: String,
default: 'chart', // 默认样式名为 'chart'
},
width: {
type: String,
default: '100%', // 默认宽度为 100%
},
height: {
type: String,
default: '100%', // 默认高度为 100%
},
autoResize: {
type: Boolean,
default: true, // 默认启用自动调整大小
},
chartData: {
type: Object,
default: () => { } // 默认空对象,存储图表数据
}
},
data() {
return {
chart: null, // 存储 echart 实例
chartColor: {
text: '#ffffff', // 文字颜色
bg: '#02456b', // 背景色
color: ['#06DEF3', '#1779CE', '#FAD961', '#F76B1C'], // 数据颜色
},
hoveredIndex: '', // 记录鼠标悬浮的索引
option: null // 存储图表配置
}
},
mounted() {
// DOM 渲染后初始化图表
this.$nextTick(() => {
this.chart = echarts.init(this.$el); // 初始化 echart 实例
this.initChart(); // 调用初始化方法
});
},
watch: {
chartData: {
deep: true, // 深度监听 chartData
handler(val) {
this.initChart(); // 当 chartData 发生变化时,重新初始化图表
},
},
},
methods: {
/**
* 根据屏幕大小调整字体大小
* @param {Number} res 原始字体大小
* @return {Number} 调整后的字体大小
*/
fontSize(res) {
let clientWidth =
window.innerWidth ||
document.documentElement.clientWidth ||
document.body.clientWidth;
if (!clientWidth) return;
let fontSize = clientWidth / 1920; // 按 1920 设计稿宽度计算比例
return res * fontSize;
},
/**
* 初始化图表,设置图表配置项
*/
initChart() {
let that = this;
const series = this.getPie3D(this.chartData.data, 0.85); // 生成3D饼图的系列
series.push({
name: "pie2d", // 2D饼图,用于3D饼图视觉对齐
type: "pie",
labelLine: { length: 0, length2: 0 },
startAngle: 2, // 起始角度
clockwise: false, // 逆时针旋转
radius: ["0%", "0%"], // 饼图半径
center: ["0%", "0%"], // 饼图中心位置
data: this.chartData.data,
itemStyle: { opacity: 0 } // 设置透明度
});
let rich = { name: { fontSize: this.fontSize(12), color: "#fff" } };
// 动态生成 rich 配置,用于自定义图例文本样式
for (let i = 0; i < 7; i++) {
rich[`name${i}`] = {
fontSize: this.fontSize(i === 1 ? 16 : 20),
align: 'left',
padding: [0, 0, 0, this.fontSize(10)],
fontWeight: i === 1 ? 900 : 600,
color: i !== 0 ? this.chartData.color[i - 2] : 'rgba(130, 159, 179, 1)'
};
}
// 动态为每个数据项添加样式
this.chartData.data.map((item, index) => {
let style = {
fontSize: this.fontSize(16),
padding: [0, 5, 0, 5],
color: this.chartData.color[index],
};
this.$set(rich, `value${index}`, style);
});
// 定义图表配置项
let option = {
color: this.chartData.color,
legend: {
show: true,
orient: 'vertical',
right: '5%',
top: 'center',
itemWidth: this.fontSize(16),
itemHeight: this.fontSize(4),
textStyle: {
fontWeight: 400,
color: '#CEE8F8',
fontSize: this.fontSize(14),
lineHeight: this.fontSize(26),
rich: rich,
},
formatter: function (name) {
let res = that.chartData.data.find(v => v.name === name);
return `{name1|${res.name}}{name${that.chartData.data.indexOf(res) + 2}|${res.value}}{name0|${res.unit}}`;
},
},
tooltip: {
formatter: function (optionsData) {
let value = that.chartData.data.find(v => v.name === optionsData.seriesName)?.value || 0;
return `${optionsData.seriesName}: ${value}`;
},
borderColor: this.chartColor.bg,
backgroundColor: this.chartColor.bg,
textStyle: {
color: '#fff',
fontSize: this.fontSize(12),
rich: rich
},
},
title: {
text: that.chartData.name,
textAlign: 'center',
left: '30%',
top: '43%',
textStyle: { color: "#fff", fontSize: this.fontSize(14) },
},
series: series,
};
this.chart.setOption(option, true); // 设置图表配置项
this.chart.resize(); // 图表自适应
window.onresize = () => this.chart.resize(); // 监听窗口大小变化
},
/**
* 生成扇形的参数方程
* @param {Number} startRatio 扇形起始比例
* @param {Number} endRatio 扇形结束比例
* @param {Boolean} isSelected 是否选中
* @param {Boolean} isHovered 是否悬浮
* @param {Number} k 辅助参数
* @param {Number} height 扇形高度
* @param {Number} i 当前序号
* @return {Object} 曲面参数方程
*/
getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, height, i) {
// 曲面参数方程的实现,计算每个扇形的坐标和大小
// ...
},
/**
* 生成模拟 3D 饼图的配置项
* @param {Array} pieData 饼图数据
* @param {Number} internalDiameterRatio 内环直径比
* @return {Array} 图表 series 配置项
*/
getPie3D(pieData, internalDiameterRatio) {
let series = [];
let sumValue = pieData.reduce((sum, data) => sum + data.value, 0); // 计算总值
let startValue = 0;
pieData.forEach((data, i) => {
let endValue = startValue + data.value;
let seriesItem = {
name: data.name || `series${i}`,
type: "surface",
parametric: true,
pieData: data,
pieStatus: { selected: false, hovered: false, k: (1 - internalDiameterRatio) / (1 + internalDiameterRatio) }
};
seriesItem.parametricEquation = this.getParametricEquation(
startValue / sumValue,
endValue / sumValue,
false,
false,
seriesItem.pieStatus.k,
10,
i
);
startValue = endValue;
series.push(seriesItem);
});
return series;
},
},
}
</script>