<template>
|
<div>
|
<PanelHeader title="不合格检品处理分析" />
|
<div class="panel-item-customers">
|
<div class="pie-chart-wrapper" ref="pieWrapperRef">
|
<div class="pie-background" ref="pieBackgroundRef"></div>
|
<Echarts ref="chart" :chartStyle="chartStyle" :legend="landLegend" :series="computedSeries"
|
:tooltip="landTooltip" :color="landColors" :options="pieOptions" style="height: 100%" class="land-chart" />
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
|
import Echarts from '@/components/Echarts/echarts.vue'
|
import PanelHeader from './PanelHeader.vue'
|
import { unqualifiedProductProcessingAnalysis } from '@/api/viewIndex.js'
|
import { useChartBackground } from '@/hooks/useChartBackground.js'
|
|
const pieWrapperRef = ref(null)
|
const pieBackgroundRef = ref(null)
|
const chart = ref(null)
|
|
// 数据列表
|
const dataList = ref([])
|
|
// 颜色列表
|
const landColors = ['#26FFCB', '#24CBFF', '#35FBF4', '#2651FF', '#D1E4F5', '#5782F7', '#2F67EF', '#82BAFF']
|
|
// label 富文本样式
|
const dotRich = landColors.reduce((acc, color, idx) => {
|
acc[`dot${idx}`] = {
|
width: 8,
|
height: 8,
|
borderRadius: 8,
|
backgroundColor: color,
|
align: 'center',
|
}
|
return acc
|
}, {})
|
|
// 图例配置
|
const landLegend = ref({
|
show: false,
|
icon: 'circle',
|
data: [],
|
right: '8%',
|
top: '40%',
|
orient: 'vertical',
|
textStyle: {
|
color: '#fff',
|
rich: {
|
unit: { color: '#fff', fontSize: 12, padding: [0, 10, 0, 0] },
|
text: { width: 60, color: '#fff', fontSize: 12 },
|
}
|
}
|
})
|
|
// 提示框配置
|
const landTooltip = {
|
trigger: 'item',
|
alwaysShowContent: false,
|
position: function (pt) {
|
return [pt[0], 130]
|
},
|
formatter: function (params) {
|
// 确保 params.data 存在
|
if (!params.data) return ''
|
const { name, value, rate } = params.data
|
return `${name}<br/>数量:${value}个<br/>占比:${rate}%`
|
},
|
}
|
|
// 使用计算属性处理 Series
|
const computedSeries = computed(() => {
|
return [
|
{
|
name: '不合格检品处理分析',
|
type: 'pie',
|
radius: ['35%', '55%'],
|
center: ['50%', '50%'],
|
label: {
|
show: true,
|
position: 'outside',
|
color: '#fff',
|
rich: {
|
...dotRich,
|
parent: { fontSize: 14, fontWeight: 600, color: '#fff', lineHeight: 20 },
|
child: { fontSize: 12, color: '#fff', lineHeight: 18 },
|
},
|
formatter: function (params) {
|
if (!params.data) return ''
|
const dotKey = `dot${params.dataIndex % landColors.length}`
|
return `{${dotKey}|} {parent|${params.data.name} (${params.data.value}个)}`
|
},
|
},
|
labelLine: {
|
show: true,
|
length: 20,
|
lineStyle: { color: '#B8C8E0' },
|
},
|
data: dataList.value,
|
},
|
{
|
// 内圈装饰
|
type: 'pie',
|
radius: ['35%', '40%'],
|
center: ['50%', '50%'],
|
silent: true,
|
label: { show: false },
|
itemStyle: { color: 'rgba(0, 127, 255, 0.25)' },
|
data: [1],
|
},
|
]
|
})
|
|
const chartStyle = { width: '100%', height: '126%' }
|
const pieOptions = { backgroundColor: 'transparent' }
|
|
// 背景处理钩子
|
const { adjustBackgroundPosition, init: initBackground, cleanup: cleanupBackground } = useChartBackground({
|
wrapperRef: pieWrapperRef,
|
backgroundRef: pieBackgroundRef,
|
offsetX: '-51.5%',
|
offsetY: '-39%',
|
watchData: dataList
|
})
|
|
const loadData = async () => {
|
try {
|
const res = await unqualifiedProductProcessingAnalysis()
|
if (res && res.code === 200) {
|
dataList.value = (res.data || []).map((it) => ({
|
name: it.name,
|
value: Number(it.value || 0),
|
rate: it.rate,
|
}))
|
landLegend.value.data = dataList.value.map((d) => d.name)
|
|
// 数据更新后微调背景
|
setTimeout(() => {
|
adjustBackgroundPosition()
|
}, 100)
|
}
|
} catch (e) {
|
console.error('获取数据失败:', e)
|
}
|
}
|
|
onMounted(() => {
|
loadData()
|
initBackground()
|
})
|
|
onBeforeUnmount(() => {
|
cleanupBackground()
|
})
|
</script>
|
|
<style scoped>
|
.panel-item-customers {
|
border: 1px solid #1a58b0;
|
padding: 18px;
|
width: 100%;
|
height: 449px;
|
position: relative;
|
overflow: hidden;
|
}
|
|
/* 面板角落装饰 */
|
.panel-item-customers::before,
|
.panel-item-customers::after {
|
content: '';
|
position: absolute;
|
width: 15px;
|
height: 15px;
|
border-color: rgba(0, 212, 255, 0.5);
|
border-style: solid;
|
pointer-events: none;
|
z-index: 3;
|
}
|
|
.panel-item-customers::before {
|
top: -1px;
|
left: -1px;
|
border-width: 2px 0 0 2px;
|
}
|
|
.panel-item-customers::after {
|
bottom: -1px;
|
right: -1px;
|
border-width: 0 2px 2px 0;
|
}
|
|
/* 面板发光背景 */
|
.panel-item-customers > div:first-child::before {
|
content: '';
|
position: absolute;
|
top: 50%;
|
left: 50%;
|
transform: translate(-50%, -50%);
|
width: 200px;
|
height: 200px;
|
background: radial-gradient(circle, rgba(78, 228, 255, 0.08) 0%, transparent 70%);
|
pointer-events: none;
|
animation: pieGlow 4s ease-in-out infinite;
|
z-index: 0;
|
}
|
|
@keyframes pieGlow {
|
0%, 100% { opacity: 0.5; transform: translate(-50%, -50%) scale(1); }
|
50% { opacity: 0.8; transform: translate(-50%, -50%) scale(1.1); }
|
}
|
|
.pie-chart-wrapper {
|
position: relative;
|
width: 100%;
|
height: 320px;
|
animation: pieFloat 4s ease-in-out infinite;
|
}
|
|
@keyframes pieFloat {
|
0%, 100% { transform: translateY(0); }
|
50% { transform: translateY(-3px); }
|
}
|
|
.pie-background {
|
position: absolute;
|
width: 360px;
|
height: 360px;
|
background-image: url('@/assets/BI/玫瑰图边框.png');
|
background-size: contain;
|
background-position: center;
|
background-repeat: no-repeat;
|
z-index: 1;
|
pointer-events: none;
|
left: 50%;
|
top: 50%;
|
transform: translate(-51.5%, -39%);
|
animation: pieBgRotate 30s linear infinite;
|
}
|
|
@keyframes pieBgRotate {
|
from { transform: translate(-51.5%, -39%) rotate(0deg); }
|
to { transform: translate(-51.5%, -39%) rotate(360deg); }
|
}
|
|
/* 图表发光效果 */
|
.land-chart {
|
position: relative;
|
z-index: 2;
|
}
|
</style>
|