|
|
<template> <!-- 1. 样式抽离到class,提升可读性和维护性 --> <div class="chart-container"> <div ref="todayTypeRef" class="today-type-chart" /> </div></template>
<script>import centerImg from '@/assets/images/circle-bg.png'
export default { name: 'TodayTypePieChart', // 3. 增加组件名,便于调试和复用
props: { todayAllNum: { type: Object, required: true, // 4. 修正拼写错误 require -> required
default: () => ({}) // 5. 简化默认值写法
} }, data() { return { myChart: null, initDebounceTimer: null } }, watch: { todayAllNum: { handler(val) { if (!val) return
clearTimeout(this.initDebounceTimer) this.initDebounceTimer = setTimeout(() => { this.$nextTick(() => { this.initTodayCircle() }) }, 300) }, immediate: true, deep: true } }, mounted() { this.initTodayCircle() }, beforeDestroy() { this.destroyChart() }, methods: { /** * 9. 销毁图表实例和定时器(抽离通用逻辑) */ destroyChart() { clearTimeout(this.initDebounceTimer) if (this.myChart) { this.myChart.dispose() this.myChart = null } },
/** * 10. 计算数组value总和(抽离为独立方法,提升可读性) * @param {Array} arr - 数据数组 * @returns {Number} 总和 */ calculateTotal(arr) { if (!Array.isArray(arr) || arr.length === 0) return 0 return arr.reduce((total, item) => total + (item.value || 0), 0) },
/** * 11. 构建图例格式化文本(抽离独立方法) * @param {String} name - 图例名称 * @param {Array} data - 图表数据 * @returns {Array|String} 格式化后的文本 */ formatLegendName(name, data) { const total = this.calculateTotal(data) const targetItem = data.find(item => item.name === name)
if (!targetItem) return name
const percentage = total > 0 ? `${((targetItem.value / total) * 100).toFixed(0)}%` : '0%' return [`{name|${name}}`, `{num|${percentage}}`] },
/** * 初始化今日类型饼图 */ initTodayCircle() { // 12. 使用ref替代getElementById,更符合Vue规范
const chartDom = this.$refs.todayTypeRef if (!chartDom) { console.warn('图表容器未找到') return }
// 13. 数据处理抽离,提升代码可读性
const chartData = [ { value: this.todayAllNum.headerLib || 0, name: '大湾' }, { value: this.todayAllNum.branchLib || 0, name: '张家湾' } ]
// 14. 销毁旧实例,避免内存泄漏
if (this.myChart) { this.myChart.dispose() }
// 初始化图表
this.myChart = this.$echarts.init(chartDom)
// 15. 抽离option配置,拆分基础配置和媒体查询配置,提升可维护性
const baseOption = { tooltip: { show: false, trigger: 'item', position: 'bottom', textStyle: { color: '#EEF6FF', fontSize: 18 } }, legend: { orient: 'vertical', right: 40, top: 55, itemWidth: 10, itemHeight: 10, icon: 'circle', selectedMode: false, data: ['大湾', '张家湾'], formatter: (name) => this.formatLegendName(name, chartData), textStyle: { rich: { name: { fontSize: 18, color: '#EEF6FF', padding: [20, 0, 20, 4], fontFamily: 'DingTalk_JinBuTi_Regular' }, num: { fontSize: 24, fontWeight: 600, padding: [20, 0, 20, 15], color: '#4C90FF', fontFamily: 'DingTalk_JinBuTi_Regular' } } } }, graphic: [ { type: 'image', id: 'logo', left: '8.5%', top: '22%', z: -10, bounding: 'raw', rotation: 0, origin: [64.5, 32.5], scale: [1.0, 1.0], style: { image: centerImg, width: 132, height: 131, opacity: 1 } } ], series: [ { name: '今日累计借阅', type: 'pie', left: '-50%', radius: ['60%', '70%'], avoidLabelOverlap: false, label: { show: false, position: 'center' }, labelLine: { show: true }, itemStyle: { borderWidth: 2, borderColor: 'rgba(16,16,21,0.4)' }, emphasis: { label: { show: true, formatter: ({ value, name }) => [ // 16. 解构赋值简化代码
`{c| ${value}}`, `{b| ${name}}` ].join('\n'), rich: { c: { fontSize: 24, fontWeight: 600, color: '#317FFF', fontFamily: 'DingTalk_JinBuTi_Regular', lineHeight: 34 }, b: { fontSize: 18, color: '#fff', fontFamily: 'DingTalk_JinBuTi_Regular' } } } }, color: ['#FFD14F', '#317FFF'], data: chartData } ] }
const mediaOption = [ { query: { maxWidth: 317 }, option: { legend: { right: 30, top: 'center', textStyle: { rich: { name: { fontSize: 14 }, num: { fontSize: 16 } } } }, graphic: [ { left: '8%', top: '26%', z: -10, bounding: 'raw', rotation: 0, origin: [64.5, 32.5], scale: [1.0, 1.0], style: { image: centerImg, width: 110, height: 110, opacity: 1 } } ], series: [ { type: 'pie', radius: ['50%', '55%'], emphasis: { label: { rich: { c: { fontSize: 22, lineHeight: 28 }, b: { fontSize: 16 } } } } } ] } } ]
// 合并配置并设置
const option = { baseOption, media: mediaOption } this.myChart.setOption(option)
// 自动轮播tooltip
this.$LoopShowTooltip(this.myChart, baseOption, { loopSeries: true, interval: 4000 })
// 17. 监听窗口大小变化,自适应图表
window.addEventListener('resize', this.handleResize) },
/** * 18. 窗口大小变化时重绘图表 */ handleResize() { if (this.myChart) { this.myChart.resize() } } },
// 19. 移除无用的created钩子
beforeUnmount() { // 20. 移除resize监听,避免内存泄漏
window.removeEventListener('resize', this.handleResize) this.destroyChart() }}</script>
<style lang="scss" scoped> // 21. 添加scoped,避免样式污染
@import "~@/assets/styles/index.scss";@import "~@/assets/styles/font-some.css";
// 22. 样式抽离,便于维护和修改
.chart-container { height: calc(100% - 200px); display: flex; align-items: center; justify-content: flex-start;
.today-type-chart { width: 400px; height: 230px; }}</style>
|