From 1678465c039ce6255105c1fcdb3ace56860f44f9 Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期一, 08 六月 2026 17:29:25 +0800
Subject: [PATCH] Merge branch 'dev_NEW_pro' of http://114.132.189.42:9002/r/product-inventory-management into dev_NEW_pro
---
src/views/reportAnalysis/financialAnalysis/components/left-bottom.vue | 288 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 288 insertions(+), 0 deletions(-)
diff --git a/src/views/reportAnalysis/financialAnalysis/components/left-bottom.vue b/src/views/reportAnalysis/financialAnalysis/components/left-bottom.vue
new file mode 100644
index 0000000..3fe95d6
--- /dev/null
+++ b/src/views/reportAnalysis/financialAnalysis/components/left-bottom.vue
@@ -0,0 +1,288 @@
+<template>
+ <div>
+ <PanelHeader title="鏋勬垚鍒嗘瀽" />
+ <div class="main-panel panel-item-customers">
+ <div class="filters-row">
+ <ProductTypeSwitch
+ v-model="amountType"
+ :options="amountTypeOptions"
+ @change="handleTypeChange"
+ />
+ </div>
+ <!-- <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 } from 'vue'
+import Echarts from '@/components/Echarts/echarts.vue'
+import PanelHeader from './PanelHeader.vue'
+import ProductTypeSwitch from './ProductTypeSwitch.vue'
+import { expenseCompositionAnalysis } from '@/api/viewIndex.js'
+import { useChartBackground } from '@/hooks/useChartBackground.js'
+
+const pieWrapperRef = ref(null)
+const pieBackgroundRef = ref(null)
+
+/**
+ * @introduction 鎶婃暟缁勪腑key鍊肩浉鍚岀殑閭d竴椤规彁鍙栧嚭鏉ワ紝缁勬垚涓�涓璞�
+ * @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
+}
+
+// 褰撳墠绫诲瀷锛�1=鏀嚭 2=鏀跺叆
+const amountType = ref(1)
+
+const amountTypeOptions = [
+ { label: 1, text: '浜у搧' },
+ { label: 2, text: '瀹㈡埛' },
+]
+
+// 鏁版嵁鍒楄〃锛堟潵鑷帴鍙o級
+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,
+ show: true,
+ data: data,
+ formatter: function (name) {
+ const item = landObjData.value[name]
+ if (!item) return name
+ const num = Number(item.value)
+ const isWan = num > 10000
+ const displayValue = isWan ? (num / 10000).toFixed(2) : num
+ const displayUnit = isWan ? '涓囧厓' : '鍏�'
+ return `{title|${name}}{value|${displayValue}}{unit|${displayUnit}}{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 = () => {
+ expenseCompositionAnalysis({ type: amountType.value })
+ .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 handleTypeChange = () => {
+ fetchData()
+}
+
+onMounted(() => {
+ fetchData()
+ initBackground()
+})
+
+onBeforeUnmount(() => {
+ cleanupBackground()
+})
+</script>
+
+<style scoped>
+.main-panel {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+
+.filters-row {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ gap: 12px;
+ margin-bottom: 10px;
+}
+
+.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>
--
Gitblit v1.9.3