| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <PanelHeader title="éè´ååå¸" /> |
| | | <div class="main-panel panel-item-customers"> |
| | | <CarouselCards :items="cardItems" :visible-count="3" /> |
| | | <div class="pie-chart-wrapper" ref="pieWrapperRef"> |
| | | <div class="pie-background" ref="pieBackgroundRef"></div> |
| | | <Echarts |
| | | ref="chart" |
| | | :chartStyle="chartStyle" |
| | | :legend="landLegend" |
| | | :series="landSeries" |
| | | :tooltip="landTooltip" |
| | | :color="landColors" |
| | | :options="pieOptions" |
| | | style="height: 320px" |
| | | class="land-chart" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, onBeforeUnmount, computed, inject, watch } from 'vue' |
| | | import Echarts from '@/components/Echarts/echarts.vue' |
| | | import PanelHeader from './PanelHeader.vue' |
| | | import CarouselCards from './CarouselCards.vue' |
| | | import { rawMaterialPurchaseAmountRatio } from '@/api/viewIndex.js' |
| | | import { useChartBackground } from '@/hooks/useChartBackground.js' |
| | | |
| | | const pieWrapperRef = ref(null) |
| | | const pieBackgroundRef = ref(null) |
| | | |
| | | /** |
| | | * @introduction ææ°ç»ä¸keyå¼ç¸åçé£ä¸é¡¹æååºæ¥ï¼ç»æä¸ä¸ªå¯¹è±¡ |
| | | * @param {åæ°ç±»å} array ä¼ å
¥çæ°ç» [{a:"1",b:"2"},{a:"2",b:"3"}] |
| | | * @param {åæ°ç±»å} key 屿§å a |
| | | * @return {è¿åç±»å说æ} |
| | | */ |
| | | function array2obj(array, key) { |
| | | const resObj = {} |
| | | for (let i = 0; i < array.length; i++) { |
| | | resObj[array[i][key]] = array[i] |
| | | } |
| | | return resObj |
| | | } |
| | | |
| | | // æ°æ®åè¡¨ï¼æ¥èªæ¥å£ï¼ |
| | | const dataList = ref([]) |
| | | |
| | | // å¡çæ°æ® |
| | | const cardItems = ref([]) |
| | | |
| | | // é¢è²å表 |
| | | const landColors = ['#26FFCB', '#24CBFF', '#35FBF4', '#2651FF', '#D1E4F5', '#5782F7', '#2F67EF', '#82BAFF'] |
| | | |
| | | const landObjData = computed(() => array2obj(dataList.value, 'name')) |
| | | |
| | | // å¾ä¾é
ç½®ï¼å³ä¾§ç«æï¼ |
| | | const landLegend = computed(() => { |
| | | const data = dataList.value.map((d, idx) => ({ |
| | | name: d.name, |
| | | icon: 'circle', |
| | | textStyle: { |
| | | fontSize: 18, |
| | | color: landColors[idx % landColors.length], |
| | | }, |
| | | })) |
| | | |
| | | return { |
| | | orient: 'vertical', |
| | | top: 'center', |
| | | left: '52%', |
| | | itemGap: 30, |
| | | data: data, |
| | | formatter: function (name) { |
| | | const item = landObjData.value[name] |
| | | if (!item) return name |
| | | return `{title|${name}}{value|${item.value}}{unit|å
}{percent|${item.rate}}{unit|%}` |
| | | }, |
| | | textStyle: { |
| | | rich: { |
| | | value: { |
| | | color: '#43e8fc', |
| | | fontSize: 14, |
| | | fontWeight: 600, |
| | | padding: [0, 0, 0, 10], |
| | | }, |
| | | unit: { |
| | | color: '#82baff', |
| | | fontSize: 12, |
| | | fontWeight: 600, |
| | | padding: [0, 10, 0, 0], |
| | | }, |
| | | percent: { |
| | | color: '#43e8fc', |
| | | fontSize: 14, |
| | | fontWeight: 600, |
| | | padding: [0, 0, 0, 0], |
| | | }, |
| | | title: { |
| | | fontSize: 12, |
| | | padding: [0, 0, 0, 0], |
| | | }, |
| | | }, |
| | | }, |
| | | } |
| | | }) |
| | | |
| | | // æç¤ºæ¡ |
| | | const landTooltip = { |
| | | trigger: 'item', |
| | | formatter: '{a} <br/>{b} : {c}å
({d}%)', |
| | | } |
| | | |
| | | // åå±ç¯å½¢é¥¼å¾ |
| | | const landSeries = ref([ |
| | | { |
| | | name: '产åéè´éé¢åæ', |
| | | type: 'pie', |
| | | radius: ['40%', '60%'], |
| | | center: ['25%', '50%'], |
| | | itemStyle: { |
| | | borderColor: '#0a1c3a', |
| | | borderWidth: 2, |
| | | color: function (params) { |
| | | return landColors[params.dataIndex % landColors.length] |
| | | }, |
| | | }, |
| | | label: { |
| | | show: false |
| | | }, |
| | | minAngle: 15, |
| | | data: dataList.value, |
| | | animationType: 'scale', |
| | | animationEasing: 'elasticOut', |
| | | animationDelay: function () { |
| | | return Math.random() * 200 |
| | | }, |
| | | }, |
| | | { |
| | | // å
å |
| | | type: 'pie', |
| | | radius: ['40%', '45%'], |
| | | center: ['25%', '50%'], |
| | | silent: true, |
| | | label: { |
| | | show: false, |
| | | }, |
| | | labelLine: { |
| | | show: false, |
| | | }, |
| | | itemStyle: { |
| | | color: 'rgba(0, 127, 255, 0.25)', |
| | | }, |
| | | data: [1], |
| | | }, |
| | | ]) |
| | | |
| | | const chartStyle = { |
| | | width: '100%', |
| | | height: '100%', |
| | | } |
| | | |
| | | const pieOptions = { |
| | | backgroundColor: 'transparent', |
| | | textStyle: { color: '#B8C8E0' }, |
| | | } |
| | | |
| | | // 使ç¨å°è£
çèæ¯ä½ç½®è°æ´æ¹æ³ |
| | | // å¾è¡¨ä¸å¿æ¯ ['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: dataList // ç嬿°æ®ååï¼èªå¨è°æ´ä½ç½® |
| | | }) |
| | | |
| | | const fetchData = () => { |
| | | rawMaterialPurchaseAmountRatio() |
| | | .then((res) => { |
| | | if (res.code === 200 && Array.isArray(res.data)) { |
| | | const items = res.data |
| | | cardItems.value = items.map((item) => ({ |
| | | label: item.name, |
| | | value: item.value, |
| | | unit: 'å
', |
| | | rate: item.rate, |
| | | })) |
| | | dataList.value = items.map((it) => ({ |
| | | name: it.name, |
| | | value: parseFloat(it.value) || 0, |
| | | rate: it.rate, |
| | | children: [], |
| | | })) |
| | | landSeries.value[0].data = dataList.value |
| | | } |
| | | }) |
| | | .catch((err) => { |
| | | console.error('è·ååææéè´éé¢å æ¯å¤±è´¥:', err) |
| | | }) |
| | | } |
| | | |
| | | const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null) |
| | | if (dataDashboardRefreshTick) { |
| | | watch(dataDashboardRefreshTick, () => { |
| | | fetchData() |
| | | }) |
| | | } |
| | | |
| | | 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; |
| | | } |
| | | |
| | | .pie-chart-wrapper { |
| | | position: relative; |
| | | width: 100%; |
| | | height: 320px; |
| | | background: transparent; |
| | | } |
| | | |
| | | |
| | | .pie-background { |
| | | 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%; |
| | | top: 50%; |
| | | transform: translate(-51.5%, -50%); |
| | | } |
| | | </style> |