| | |
| | | <template> |
| | | <div> |
| | | <PanelHeader title="工序产出分析" /> |
| | | <div class="main-panel panel-item-customers"> |
| | | <div class="panel-item-customers bi-panel"> |
| | | <div class="filters-row"> |
| | | <DateTypeSwitch v-model="dateType" @change="handleDateTypeChange" /> |
| | | </div> |
| | | <div class="pie-chart-wrapper" ref="pieWrapperRef"> |
| | | <div class="pie-background" ref="pieBackgroundRef"></div> |
| | | <Echarts |
| | | ref="echartsRef" |
| | | :chartStyle="chartStyle" |
| | | :legend="pieLegend" |
| | | :series="pieSeries" |
| | | :tooltip="pieTooltip" |
| | | :color="pieColors" |
| | | :options="pieOptions" |
| | | style="height: 320px" |
| | | /> |
| | | <div class="pie-chart-wrapper"> |
| | | <template v-if="hasData"> |
| | | <Echarts |
| | | ref="echartsRef" |
| | | :chartStyle="chartStyle" |
| | | :legend="pieLegend" |
| | | :series="pieSeries" |
| | | :tooltip="pieTooltip" |
| | | :color="pieColors" |
| | | :options="pieOptions" |
| | | style="height: 320px" |
| | | /> |
| | | <!-- 环心汇总,替代原玫瑰图边框装饰图 --> |
| | | <div class="pie-center"> |
| | | <span class="center-value">{{ totalValue }}</span> |
| | | <span class="center-label">总产出(件)</span> |
| | | </div> |
| | | </template> |
| | | <div v-else class="pie-empty">暂无数据</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, onBeforeUnmount, computed } from 'vue' |
| | | import { ref, onMounted, computed } from 'vue' |
| | | import { processOutputAnalysis } from '@/api/viewIndex.js' |
| | | import PanelHeader from './PanelHeader.vue' |
| | | import Echarts from '@/components/Echarts/echarts.vue' |
| | | import DateTypeSwitch from '@/views/reportAnalysis/financialAnalysis/components/DateTypeSwitch.vue' |
| | | import { useChartBackground } from '@/hooks/useChartBackground.js' |
| | | import DateTypeSwitch from './DateTypeSwitch.vue' |
| | | import { BI_COLORS, BI_PIE_PALETTE, BI_TOOLTIP } from '../theme.js' |
| | | |
| | | const pieWrapperRef = ref(null) |
| | | const pieBackgroundRef = ref(null) |
| | | |
| | | const dateType = ref(1) // 1=周 2=月 3=季度 |
| | | const dateType = ref(3) // 1=周 2=月 3=季度,默认季度 |
| | | |
| | | function array2obj(array, key) { |
| | | const resObj = {} |
| | |
| | | |
| | | const echartsRef = ref(null) |
| | | const pieDatas = ref([]) |
| | | const pieColors = ['#D1E4F5', '#5782F7', '#2F67EF', '#82BAFF', '#43e8fc', '#27EBE7'] |
| | | const pieColors = BI_PIE_PALETTE |
| | | |
| | | const pieObjData = computed(() => array2obj(pieDatas.value, 'name')) |
| | | |
| | | // 环心总量(空数据兜底 0) |
| | | const totalValue = computed(() => |
| | | pieDatas.value.reduce((sum, d) => sum + (Number(d.value) || 0), 0) |
| | | ) |
| | | |
| | | // 全零数据不渲染扇区:minAngle 会把 0 值也撑成整环,造成误导 |
| | | const hasData = computed(() => pieDatas.value.some((d) => Number(d.value) > 0)) |
| | | |
| | | const pieLegend = computed(() => { |
| | | const data = pieDatas.value.map((d, idx) => ({ |
| | |
| | | textStyle: { |
| | | rich: { |
| | | value: { |
| | | color: '#43e8fc', |
| | | color: BI_COLORS.textStrong, |
| | | fontSize: 14, |
| | | fontWeight: 600, |
| | | padding: [0, 0, 0, 10], |
| | | }, |
| | | unit: { |
| | | color: '#82baff', |
| | | color: BI_COLORS.text, |
| | | fontSize: 12, |
| | | fontWeight: 600, |
| | | padding: [0, 10, 0, 0], |
| | | }, |
| | | percent: { |
| | | color: '#43e8fc', |
| | | color: BI_COLORS.primary, |
| | | fontSize: 14, |
| | | fontWeight: 600, |
| | | padding: [0, 0, 0, 0], |
| | | }, |
| | | title: { |
| | | color: BI_COLORS.text, |
| | | fontSize: 12, |
| | | padding: [0, 0, 0, 0], |
| | | }, |
| | |
| | | const pieTooltip = { |
| | | trigger: 'item', |
| | | formatter: '{a} <br/>{b} : {c}件 ({d}%)', |
| | | ...BI_TOOLTIP, |
| | | } |
| | | |
| | | const pieSeries = computed(() => [ |
| | | { |
| | | name: '工序产出分析', |
| | | type: 'pie', |
| | | radius: '60%', |
| | | center: ['25%', '50%'], |
| | | radius: ['46%', '68%'], |
| | | center: ['27%', '50%'], |
| | | itemStyle: { |
| | | borderColor: '#0a1c3a', |
| | | borderColor: BI_COLORS.surface, |
| | | borderWidth: 2, |
| | | borderRadius: 4, |
| | | }, |
| | | label: { |
| | | show: false |
| | | }, |
| | | minAngle: 15, |
| | | data: pieDatas.value, |
| | | data: hasData.value ? pieDatas.value : [], |
| | | animationType: 'scale', |
| | | animationEasing: 'elasticOut', |
| | | animationDelay: function () { |
| | | return Math.random() * 200 |
| | | }, |
| | | animationEasing: 'cubicOut', |
| | | }, |
| | | ]) |
| | | |
| | | const pieOptions = { |
| | | backgroundColor: 'transparent', |
| | | textStyle: { color: '#B8C8E0' }, |
| | | textStyle: { color: BI_COLORS.text }, |
| | | } |
| | | |
| | | // 使用封装的背景位置调整方法 |
| | | // 图表中心是 ['25%', '50%'],背景需要对齐到这个位置 |
| | | const { init: initBackground, cleanup: cleanupBackground } = useChartBackground({ |
| | | wrapperRef: pieWrapperRef, |
| | | backgroundRef: pieBackgroundRef, |
| | | left: '25%', // 图表中心 X 是 25% |
| | | top: '50%', // 图表中心 Y 是 50% |
| | | offsetX: '-51.5%', // X 轴偏移 |
| | | offsetY: '-50%', // Y 轴偏移 |
| | | watchData: pieDatas // 监听数据变化,自动调整位置 |
| | | }) |
| | | |
| | | const fetchData = () => { |
| | | processOutputAnalysis({ dateType: dateType.value }) |
| | |
| | | |
| | | onMounted(() => { |
| | | fetchData() |
| | | initBackground() |
| | | }) |
| | | |
| | | onBeforeUnmount(() => { |
| | | cleanupBackground() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .main-panel { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | } |
| | | |
| | | .panel-item-customers { |
| | | border: 1px solid #1a58b0; |
| | | padding: 18px; |
| | | width: 100%; |
| | | height: 449px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | } |
| | | |
| | | .filters-row { |
| | |
| | | position: relative; |
| | | width: 100%; |
| | | height: 320px; |
| | | background: transparent; |
| | | } |
| | | |
| | | .pie-background { |
| | | /* 环心数字与饼图圆心(27%,50%)对齐 */ |
| | | .pie-center { |
| | | position: absolute; |
| | | width: 310px; |
| | | height: 310px; |
| | | background-image: url('@/assets/BI/玫瑰图边框.png'); |
| | | background-size: contain; |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | z-index: 1; |
| | | pointer-events: none; |
| | | /* 位置由 JS 动态设置,默认居中 */ |
| | | left: 25%; |
| | | left: 27%; |
| | | top: 50%; |
| | | transform: translate(-51.5%, -50%); |
| | | transform: translate(-50%, -50%); |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | gap: 4px; |
| | | pointer-events: none; |
| | | } |
| | | |
| | | .center-value { |
| | | font-weight: 600; |
| | | font-size: 26px; |
| | | line-height: 1; |
| | | color: var(--bi-text); |
| | | font-variant-numeric: tabular-nums; |
| | | } |
| | | |
| | | .center-label { |
| | | font-size: 12px; |
| | | color: var(--bi-text-secondary); |
| | | } |
| | | |
| | | .pie-empty { |
| | | position: absolute; |
| | | left: 27%; |
| | | top: 50%; |
| | | transform: translate(-50%, -50%); |
| | | font-size: 14px; |
| | | color: var(--bi-text-tertiary); |
| | | pointer-events: none; |
| | | } |
| | | </style> |