|
|
@@ -0,0 +1,179 @@
|
|
|
+<template>
|
|
|
+ <div class="donut-chart-wrapper">
|
|
|
+ <div class="custom-legend">
|
|
|
+ <div class="legend-box" v-for="(item, index) in chartData" :key="index">
|
|
|
+ <span class="color-dot" :style="{ backgroundColor: item.color }"></span>
|
|
|
+ <span class="legend-name">{{ item.name }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="echarts-container" ref="chartRef"></div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import * as echarts from 'echarts';
|
|
|
+// 1. 引入你的全局自适应 Mixin 和像素转换工具
|
|
|
+import echartsResize, { px2echarts } from '@/mixins/echartsResize.js';
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'DynamicDonutChart',
|
|
|
+ // 2. 注册混入,自动接管自适应逻辑
|
|
|
+ mixins: [echartsResize],
|
|
|
+ props: {
|
|
|
+ // 传入的图表数据:[{ name: '正常', value: 425, color: '#32F6F8' }, ...]
|
|
|
+ chartData: {
|
|
|
+ type: Array,
|
|
|
+ required: true
|
|
|
+ },
|
|
|
+ // 圆环中间的大字(如 98%)
|
|
|
+ centerTitle: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ },
|
|
|
+ // 圆环中间的小字(如 980/1000)
|
|
|
+ centerSubTitle: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ // 深度监听数组变化,触发重绘
|
|
|
+ chartData: {
|
|
|
+ deep: true,
|
|
|
+ handler() {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ if (this.$_chart) {
|
|
|
+ this.$_chart.resize(); // 在更新数据前,先强制重算尺寸
|
|
|
+ }
|
|
|
+ this.updateChart();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 监听标题变化
|
|
|
+ centerTitle() {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ if (this.$_chart) this.$_chart.resize();
|
|
|
+ this.updateChart();
|
|
|
+ });
|
|
|
+ },
|
|
|
+ centerSubTitle() {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ if (this.$_chart) this.$_chart.resize();
|
|
|
+ this.updateChart();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.initChart();
|
|
|
+ // 清理:无需手动监听 resize
|
|
|
+ },
|
|
|
+ // 清理:彻底删除 beforeDestroy
|
|
|
+ methods: {
|
|
|
+ initChart() {
|
|
|
+ // 3. 按照 mixin 约定,将实例挂载到 this.$_chart
|
|
|
+ this.$_chart = echarts.init(this.$refs.chartRef);
|
|
|
+ this.updateChart();
|
|
|
+ },
|
|
|
+
|
|
|
+ // 4. Mixin 会在窗口变化时自动静默调用此方法
|
|
|
+ updateChart() {
|
|
|
+ if (!this.$_chart) return;
|
|
|
+
|
|
|
+ const colorPalette = this.chartData.map(item => item.color);
|
|
|
+
|
|
|
+ const option = {
|
|
|
+ color: colorPalette,
|
|
|
+ title: {
|
|
|
+ // 【核心改造】:使用富文本结构,用 \n 换行
|
|
|
+ text: `{main|${this.centerTitle}}\n{sub|${this.centerSubTitle}}`,
|
|
|
+ left: 'center',
|
|
|
+ top: 'center',
|
|
|
+ textStyle: {
|
|
|
+ rich: {
|
|
|
+ // 全面使用 px2echarts 包裹尺寸数值
|
|
|
+ main: {
|
|
|
+ fontSize: px2echarts(26),
|
|
|
+ color: '#ffffff',
|
|
|
+ fontWeight: 'bold',
|
|
|
+ fontFamily: 'Arial',
|
|
|
+ // 用 padding 的 bottom 值代替原来的 itemGap,实现精准缩放
|
|
|
+ padding: [0, 0, px2echarts(6), 0]
|
|
|
+ },
|
|
|
+ sub: {
|
|
|
+ fontSize: px2echarts(14),
|
|
|
+ lineHeight: px2echarts(18),
|
|
|
+ color: '#cccccc'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ type: 'pie',
|
|
|
+ // 半径原则上也可以写成具体像素如 [px2echarts(60), px2echarts(80)]
|
|
|
+ // 但写成百分比原生就自带外层容器的自适应,通常保留百分比即可
|
|
|
+ radius: ['60%', '80%'],
|
|
|
+ center: ['50%', '50%'],
|
|
|
+ avoidLabelOverlap: false,
|
|
|
+ label: { show: false },
|
|
|
+ labelLine: { show: false },
|
|
|
+ data: this.chartData
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+
|
|
|
+ this.$_chart.setOption(option);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.donut-chart-wrapper {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+/* 左侧图例样式 */
|
|
|
+.custom-legend {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 12px;
|
|
|
+ width: 35%;
|
|
|
+ margin-right: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.legend-box {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ background: linear-gradient( 90deg, #2D426C 0%, rgba(45,66,108,0) 100%);
|
|
|
+ height: 40px;
|
|
|
+}
|
|
|
+
|
|
|
+.color-dot {
|
|
|
+ width: 8px;
|
|
|
+ height: 8px;
|
|
|
+ margin-right: 8px;
|
|
|
+ margin-left: 16px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ border-radius: 2px;
|
|
|
+}
|
|
|
+
|
|
|
+.legend-name {
|
|
|
+ font-weight: 400;
|
|
|
+ font-size: 12px;
|
|
|
+ color: rgba(255, 255, 255, 0.60);
|
|
|
+ line-height: 18px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 右侧图表样式 */
|
|
|
+.echarts-container {
|
|
|
+ width: 65%;
|
|
|
+ height: 100%;
|
|
|
+ min-height: 160px;
|
|
|
+}
|
|
|
+</style>
|