From a5378ba9d7f0aac37092c43eecdf54782d714bc5 Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期四, 29 一月 2026 17:58:25 +0800
Subject: [PATCH] fix: 财务大屏接口联调90%
---
/dev/null | 306 --------------------
src/views/reportAnalysis/financialAnalysis/components/left-bottom.vue | 65 ++-
src/views/reportAnalysis/financialAnalysis/components/center-top.vue | 140 +++++++-
src/api/viewIndex.js | 236 ++++++++------
src/views/reportAnalysis/financialAnalysis/components/ProductTypeSwitch.vue | 23 +
src/views/reportAnalysis/financialAnalysis/components/center-center.vue | 22 +
src/views/reportAnalysis/financialAnalysis/components/center-bottom.vue | 19 +
src/views/reportAnalysis/financialAnalysis/components/left-top.vue | 51 ++
8 files changed, 385 insertions(+), 477 deletions(-)
diff --git a/src/api/viewIndex.js b/src/api/viewIndex.js
index b003dbf..8bb9c83 100644
--- a/src/api/viewIndex.js
+++ b/src/api/viewIndex.js
@@ -1,157 +1,189 @@
// 棣栭〉鎺ュ彛
-import request from '@/utils/request'
+import request from "@/utils/request";
// 閿�鍞�-閲囪喘-搴撳瓨鏁版嵁
export const getBusiness = () => {
- return request({
- url: '/home/business',
- method: 'get'
- })
-}
+ return request({
+ url: "/home/business",
+ method: "get",
+ });
+};
// 瀹㈡埛鍚堝悓閲戦鍒嗘瀽
export const analysisCustomerContractAmounts = () => {
- return request({
- url: '/home/analysisCustomerContractAmounts',
- method: 'get'
- })
-}
+ return request({
+ url: "/home/analysisCustomerContractAmounts",
+ method: "get",
+ });
+};
// 璐ㄦ鍒嗘瀽
export const qualityStatistics = () => {
- return request({
- url: '/home/qualityStatistics',
- method: 'get'
- })
-}
+ return request({
+ url: "/home/qualityStatistics",
+ method: "get",
+ });
+};
// 搴旀敹搴斾粯缁熻
export const statisticsReceivablePayable = (query) => {
- return request({
- url: '/home/statisticsReceivablePayable',
- method: 'get',
- params: query
- })
-}
+ return request({
+ url: "/home/statisticsReceivablePayable",
+ method: "get",
+ params: query,
+ });
+};
// 寰呭姙浜嬮」
export const homeTodos = () => {
- return request({
- url: '/home/todos',
- method: 'get'
- })
-}
+ return request({
+ url: "/home/todos",
+ method: "get",
+ });
+};
// 绾垮舰鍥�
export const getAmountHalfYear = () => {
- return request({
- url: '/sales/ledger/getAmountHalfYear',
- method: 'get'
- })
-}
+ return request({
+ url: "/sales/ledger/getAmountHalfYear",
+ method: "get",
+ });
+};
// 鍚勭敓浜ц鍗曠殑瀹屾垚杩涘害缁熻
// /home/progressStatistics
-export const getProgressStatistics = ()=>{
- return request({
- url: '/home/progressStatistics',
- method: 'get'
- })
-}
+export const getProgressStatistics = () => {
+ return request({
+ url: "/home/progressStatistics",
+ method: "get",
+ });
+};
//鍦ㄥ埗鍝佸懆杞儏鍐�
//home/workInProcessTurnover
export const getWorkInProcessTurnover = () => {
- return request({
- url: '/home/workInProcessTurnover',
- method: 'get'
- })
-}
+ return request({
+ url: "/home/workInProcessTurnover",
+ method: "get",
+ });
+};
// 瀹㈡埛钀ユ敹璐$尞鏁板�煎垎鏋�
export const customerRevenueAnalysis = (params) => {
- return request({
- url: '/home/customerRevenueAnalysis',
- method: 'get',
- params
- })
-}
+ return request({
+ url: "/home/customerRevenueAnalysis",
+ method: "get",
+ params,
+ });
+};
// 鍛樺伐-瀹㈡埛-渚涘簲鍟嗘�绘暟
export const summaryStatistics = () => {
- return request({
- url: '/home/summaryStatistics',
- method: 'get'
- })
-}
+ return request({
+ url: "/home/summaryStatistics",
+ method: "get",
+ });
+};
// 鍚勯儴闂ㄤ汉鍛樺垎甯�
export const deptStaffDistribution = () => {
- return request({
- url: '/home/deptStaffDistribution',
- method: 'get'
- })
-}
+ return request({
+ url: "/home/deptStaffDistribution",
+ method: "get",
+ });
+};
// 渚涘簲鍟嗛噰璐帓鍚�
export const supplierPurchaseRanking = (query) => {
- return request({
- url: '/home/supplierPurchaseRanking',
- method: 'get',
- params: query
- })
-}
+ return request({
+ url: "/home/supplierPurchaseRanking",
+ method: "get",
+ params: query,
+ });
+};
// 瀹㈡埛閲戦璐$尞鎺掑悕
export const customerContributionRanking = (query) => {
- return request({
- url: '/home/customerContributionRanking',
- method: 'get',
- params: query
- })
-}
+ return request({
+ url: "/home/customerContributionRanking",
+ method: "get",
+ params: query,
+ });
+};
// 鍚勪骇鍝佸ぇ绫诲垎甯�
export const productCategoryDistribution = () => {
- return request({
- url: '/home/productCategoryDistribution',
- method: 'get'
- })
-}
+ return request({
+ url: "/home/productCategoryDistribution",
+ method: "get",
+ });
+};
// 浜у搧閿�鍞噾棰濆垎鏋�
export const productSalesAnalysis = () => {
- return request({
- url: '/home/productSalesAnalysis',
- method: 'get'
- })
-}
+ return request({
+ url: "/home/productSalesAnalysis",
+ method: "get",
+ });
+};
// 鍘熸潗鏂欓噰璐噾棰濆崰姣�
export const rawMaterialPurchaseAmountRatio = () => {
- return request({
- url: '/home/rawMaterialPurchaseAmountRatio',
- method: 'get'
- })
-}
+ return request({
+ url: "/home/rawMaterialPurchaseAmountRatio",
+ method: "get",
+ });
+};
// 閿�鍞�/閲囪喘/鍌ㄥ瓨浜у搧鏁�
export const salesPurchaseStorageProductCount = () => {
- return request({
- url: '/home/salesPurchaseStorageProductCount',
- method: 'get'
- })
-}
+ return request({
+ url: "/home/salesPurchaseStorageProductCount",
+ method: "get",
+ });
+};
// 浜у搧鍑哄叆搴撳垎鏋愶紙鍙紶 productType: 1 鍘熸潗鏂� 2 鍗婃垚鍝� 3 鎴愬搧锛�
export const productInOutAnalysis = (params) => {
- return request({
- url: '/home/productInOutAnalysis',
- method: 'get',
- params
- })
-}
+ return request({
+ url: "/home/productInOutAnalysis",
+ method: "get",
+ params,
+ });
+};
// 浜у搧鍛ㄨ浆澶╂暟
export const productTurnoverDays = () => {
- return request({
- url: '/home/productTurnoverDays',
- method: 'get'
- })
-}
+ return request({
+ url: "/home/productTurnoverDays",
+ method: "get",
+ });
+};
+
+// 鏀舵敮瀵规瘮鍒嗘瀽
+export const incomeExpenseAnalysis = () => {
+ return request({
+ url: "/home/incomeExpenseAnalysis",
+ method: "get",
+ });
+};
+
+// 鍒╂鼎瓒嬪娍鍒嗘瀽
+export const profitTrendAnalysis = () => {
+ return request({
+ url: "/home/profitTrendAnalysis",
+ method: "get",
+ });
+};
+
+// 鏈堝害鏀跺叆
+export const getMonthlyIncome = () => {
+ return request({
+ url: "/home/monthlyIncome",
+ method: "get",
+ });
+};
+
+// 鏈堝害鏀嚭
+export const getMonthlyExpenditure = () => {
+ return request({
+ url: "/home/monthlyExpenditure",
+ method: "get",
+ });
+};
diff --git a/src/views/reportAnalysis/financialAnalysis/components/CarouselCards.vue b/src/views/reportAnalysis/financialAnalysis/components/CarouselCards.vue
deleted file mode 100644
index 0498824..0000000
--- a/src/views/reportAnalysis/financialAnalysis/components/CarouselCards.vue
+++ /dev/null
@@ -1,306 +0,0 @@
-<template>
- <div class="carousel-cards">
- <button
- v-if="canScrollLeft"
- class="nav-button nav-button-left"
- @click="scrollLeftFn"
- >
- <img src="@/assets/BI/jiantou.png" alt="宸︾澶�" />
- </button>
- <div
- class="cards-container"
- :style="{ '--visible-count': visibleCount }"
- ref="cardsContainerRef"
- >
- <div
- v-for="(item, index) in items"
- :key="index"
- class="card-item"
- >
- <div v-if="item.icon" class="card-icon" :style="{ backgroundImage: `url(${item.icon})` }"></div>
- <div class="card-title">
- <div class="card-label">{{ item.label }}</div>
- <div class="card-value">
- <span class="value-number">{{ item.value }}</span>
- <span class="value-unit">{{ item.unit }}</span>
- </div>
- <div v-if="item.rate ?? item.ratio ?? item.percent" class="card-rate">
- <span class="rate-value">{{ item.rate ?? item.ratio ?? item.percent }}%</span>
- </div>
- </div>
- </div>
- </div>
- <button
- v-if="canScrollRight"
- class="nav-button nav-button-right"
- @click="scrollRightFn"
- >
- <img src="@/assets/BI/jiantou.png" alt="鍙崇澶�" />
- </button>
- </div>
-</template>
-
-<script setup>
-import { ref, onMounted, onBeforeUnmount, nextTick, watch, computed } from 'vue'
-
-const props = defineProps({
- items: {
- type: Array,
- default: () => [],
- validator: (value) => {
- return value.every(item =>
- item && typeof item.label !== 'undefined' &&
- typeof item.value !== 'undefined' &&
- typeof item.unit !== 'undefined'
- )
- }
- },
- visibleCount: {
- type: Number,
- default: 3
- }
-})
-
-const cardsContainerRef = ref(null)
-const currentScrollLeft = ref(0)
-const maxScrollLeft = ref(0)
-
-// 妫�鏌ユ槸鍚﹀彲浠ュ悜宸︽粴鍔�
-const canScrollLeft = computed(() => {
- return currentScrollLeft.value > 0
-})
-
-// 妫�鏌ユ槸鍚﹀彲浠ュ悜鍙虫粴鍔�
-const canScrollRight = computed(() => {
- return currentScrollLeft.value < maxScrollLeft.value
-})
-
-// 鏇存柊婊氬姩鐘舵��
-const updateScrollState = () => {
- const container = cardsContainerRef.value
- if (!container) return
-
- currentScrollLeft.value = container.scrollLeft
- maxScrollLeft.value = container.scrollWidth - container.clientWidth
-}
-
-// 鍚戝乏婊氬姩
-const scrollLeftFn = () => {
- const container = cardsContainerRef.value
- if (!container) return
-
- const scrollItems = Array.from(container.querySelectorAll('.card-item'))
- if (scrollItems.length === 0) return
-
- const itemWidth = scrollItems[0]?.offsetWidth || 0
- const gap = 12
- const scrollDistance = itemWidth + gap
-
- container.scrollBy({
- left: -scrollDistance,
- behavior: 'smooth'
- })
-
- // 寤惰繜鏇存柊鐘舵�侊紝绛夊緟婊氬姩鍔ㄧ敾瀹屾垚
- setTimeout(() => {
- updateScrollState()
- }, 300)
-}
-
-// 鍚戝彸婊氬姩
-const scrollRightFn = () => {
- const container = cardsContainerRef.value
- if (!container) return
-
- const scrollItems = Array.from(container.querySelectorAll('.card-item'))
- if (scrollItems.length === 0) return
-
- const itemWidth = scrollItems[0]?.offsetWidth || 0
- const gap = 12
- const scrollDistance = itemWidth + gap
-
- container.scrollBy({
- left: scrollDistance,
- behavior: 'smooth'
- })
-
- // 寤惰繜鏇存柊鐘舵�侊紝绛夊緟婊氬姩鍔ㄧ敾瀹屾垚
- setTimeout(() => {
- updateScrollState()
- }, 300)
-}
-
-// 鐩戝惉 items 鍙樺寲锛屾洿鏂版粴鍔ㄧ姸鎬�
-watch(() => props.items, () => {
- nextTick(() => {
- updateScrollState()
- })
-}, { deep: true })
-
-onMounted(() => {
- nextTick(() => {
- updateScrollState()
- // 鐩戝惉婊氬姩浜嬩欢
- const container = cardsContainerRef.value
- if (container) {
- container.addEventListener('scroll', updateScrollState)
- }
- })
-})
-
-onBeforeUnmount(() => {
- // 娓呯悊婊氬姩浜嬩欢鐩戝惉鍣�
- const container = cardsContainerRef.value
- if (container) {
- container.removeEventListener('scroll', updateScrollState)
- }
-})
-</script>
-
-<style scoped>
-.carousel-cards {
- width: 100%;
- overflow: hidden;
- position: relative;
- display: flex;
- align-items: center;
-}
-
-.cards-container {
- display: flex;
- gap: 12px;
- width: 100%;
- overflow-x: auto;
- overflow-y: hidden;
- scrollbar-width: none; /* Firefox */
- -ms-overflow-style: none; /* IE and Edge */
- padding-bottom: 4px;
- scroll-behavior: smooth;
-}
-
-.cards-container::-webkit-scrollbar {
- display: none; /* Chrome, Safari, Opera */
-}
-
-.nav-button {
- position: absolute;
- top: 50%;
- transform: translateY(-50%);
- width: 32px;
- height: 32px;
- background: rgba(26, 88, 176, 0.6);
- border: 1px solid rgba(26, 88, 176, 0.8);
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- cursor: pointer;
- z-index: 10;
- transition: all 0.3s ease;
- padding: 0;
-}
-
-.nav-button:hover {
- background: rgba(26, 88, 176, 0.8);
- transform: translateY(-50%) scale(1.1);
-}
-
-.nav-button-left {
- left: -16px;
-}
-
-.nav-button-left img {
- width: 16px;
- height: 16px;
- transform: rotate(180deg);
-}
-
-.nav-button-right {
- right: -16px;
-}
-
-.nav-button-right img {
- width: 16px;
- height: 16px;
-}
-
-.card-item {
- flex: 0 0 calc((100% - (var(--visible-count) - 1) * 12px) / var(--visible-count));
- min-width: calc((100% - (var(--visible-count) - 1) * 12px) / var(--visible-count));
- display: flex;
- align-items: center;
- background: linear-gradient(269deg, rgba(27,57,126,0.13) 0%, rgba(33,137,206,0.33) 98.13%, #24AFF4 100%);
- border-radius: 8px 8px 8px 8px;
- padding: 12px 16px;
- transition: all 0.3s ease;
-}
-
-.card-item:hover {
- transform: translateY(-2px);
-}
-
-.card-icon {
- width: 80px;
- height: 60px;
- background-size: cover;
- background-position: center;
- background-repeat: no-repeat;
- flex-shrink: 0;
- margin-right: 12px;
-}
-
-.card-title {
- display: flex;
- align-items: flex-start;
- flex-direction: column;
- flex: 1;
-}
-
-.card-label {
- font-weight: 400;
- font-size: 14px;
- color: #FFFFFF;
- margin-bottom: 4px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- width: 100%;
-}
-
-.card-value {
- display: flex;
- align-items: baseline;
- gap: 4px;
-}
-
-.card-rate {
- margin-top: 4px;
- display: flex;
- align-items: center;
- gap: 6px;
- font-weight: 400;
- font-size: 12px;
- color: rgba(255, 255, 255, 0.85);
-}
-
-.rate-label {
- opacity: 0.85;
-}
-
-.rate-value {
- font-weight: 500;
-}
-
-.value-number {
- font-weight: 400;
- font-size: 14px;
- color: #FFFFFF;
- line-height: 1;
-}
-
-.value-unit {
- font-size: 14px;
- color: #FFFFFF;
- font-weight: 400;
-}
-</style>
diff --git a/src/views/reportAnalysis/financialAnalysis/components/ProductTypeSwitch.vue b/src/views/reportAnalysis/financialAnalysis/components/ProductTypeSwitch.vue
index 87cde44..98d07eb 100644
--- a/src/views/reportAnalysis/financialAnalysis/components/ProductTypeSwitch.vue
+++ b/src/views/reportAnalysis/financialAnalysis/components/ProductTypeSwitch.vue
@@ -4,9 +4,13 @@
class="product-type-switch"
@change="handleChange"
>
- <el-radio-button :label="1">鍘熸潗鏂�</el-radio-button>
- <el-radio-button :label="3">鍗婃垚鍝�</el-radio-button>
- <el-radio-button :label="2">鎴愬搧</el-radio-button>
+ <el-radio-button
+ v-for="opt in options"
+ :key="opt.label"
+ :label="opt.label"
+ >
+ {{ opt.text }}
+ </el-radio-button>
</el-radio-group>
</template>
@@ -15,8 +19,17 @@
const props = defineProps({
modelValue: {
- type: Number,
- default: 1, // 榛樿閫変腑"鍘熸潗鏂�"
+ type: [Number, String],
+ default: 1, // 榛樿閫変腑绗竴涓�
+ },
+ // 鍙厤缃�夐」锛岄粯璁ゆ槸鍘熺粍浠剁殑銆屽師鏉愭枡 / 鍗婃垚鍝� / 鎴愬搧銆�
+ options: {
+ type: Array,
+ default: () => [
+ { label: 1, text: '鍘熸潗鏂�' },
+ { label: 3, text: '鍗婃垚鍝�' },
+ { label: 2, text: '鎴愬搧' },
+ ],
},
})
diff --git a/src/views/reportAnalysis/financialAnalysis/components/center-bottom.vue b/src/views/reportAnalysis/financialAnalysis/components/center-bottom.vue
index 1d15a39..c8eeeb9 100644
--- a/src/views/reportAnalysis/financialAnalysis/components/center-bottom.vue
+++ b/src/views/reportAnalysis/financialAnalysis/components/center-bottom.vue
@@ -22,6 +22,7 @@
import { ref, onMounted } from 'vue'
import Echarts from '@/components/Echarts/echarts.vue'
import PanelHeader from './PanelHeader.vue'
+import { profitTrendAnalysis } from '@/api/viewIndex.js'
const chartStyle = { width: '100%', height: '150%' }
const grid = { left: '3%', right: '4%', bottom: '3%', top: '4%', containLabel: true }
@@ -70,10 +71,22 @@
const yAxis1 = [{ type: 'value', axisLabel: { color: '#B8C8E0' } }]
+const fetchData = () => {
+ profitTrendAnalysis()
+ .then((res) => {
+ if (res.code === 200 && Array.isArray(res.data)) {
+ const list = res.data
+ xAxis1.value[0].data = list.map((d) => d.name)
+ barSeries1.value[0].data = list.map((d) => parseFloat(d.value) || 0)
+ }
+ })
+ .catch((err) => {
+ console.error('鑾峰彇鍒╂鼎瓒嬪娍鍒嗘瀽澶辫触:', err)
+ })
+}
+
onMounted(() => {
- // 鍏堢敤鏈湴鍋囨暟鎹紙鍚庣画濡傛湁鎺ュ彛鍙浛鎹級
- xAxis1.value[0].data = ['1鏈�', '2鏈�', '3鏈�', '4鏈�', '5鏈�', '6鏈�']
- barSeries1.value[0].data = [12000, 18000, 9000, 16000, 14000, 20000]
+ fetchData()
})
</script>
diff --git a/src/views/reportAnalysis/financialAnalysis/components/center-center.vue b/src/views/reportAnalysis/financialAnalysis/components/center-center.vue
index 7778752..7d32ebd 100644
--- a/src/views/reportAnalysis/financialAnalysis/components/center-center.vue
+++ b/src/views/reportAnalysis/financialAnalysis/components/center-center.vue
@@ -30,6 +30,7 @@
import { ref, onMounted } from 'vue'
import * as echarts from 'echarts'
import Echarts from '@/components/Echarts/echarts.vue'
+import { incomeExpenseAnalysis } from '@/api/viewIndex.js'
const chartStyle = { width: '100%', height: '100%' }
const grid = {
@@ -128,16 +129,23 @@
},
}
-// 鍏堢敤鏈湴鍋囨暟鎹紙鍚庣画濡傛湁鎺ュ彛鍙洿鎺ユ浛鎹㈣繖閲岋級
-const setMockData = () => {
- const dates = ['1/22', '1/23', '1/24', '1/25', '1/26', '1/27', '1/28']
- xAxis1.value[0].data = dates
- lineSeries.value[0].data = [1200, 1800, 900, 1600, 1400, 2000, 1700] // 鏀跺叆
- lineSeries.value[1].data = [800, 1100, 700, 1200, 1000, 1500, 1300] // 鏀嚭
+const fetchData = () => {
+ incomeExpenseAnalysis()
+ .then((res) => {
+ if (res.code === 200 && Array.isArray(res.data)) {
+ const list = res.data
+ xAxis1.value[0].data = list.map((d) => d.date)
+ lineSeries.value[0].data = list.map((d) => Number(d.income) || 0)
+ lineSeries.value[1].data = list.map((d) => Number(d.expense) || 0)
+ }
+ })
+ .catch((err) => {
+ console.error('鑾峰彇鏀舵敮瀵规瘮鍒嗘瀽澶辫触:', err)
+ })
}
onMounted(() => {
- setMockData()
+ fetchData()
})
</script>
diff --git a/src/views/reportAnalysis/financialAnalysis/components/center-top.vue b/src/views/reportAnalysis/financialAnalysis/components/center-top.vue
index d46a0ac..85f4928 100644
--- a/src/views/reportAnalysis/financialAnalysis/components/center-top.vue
+++ b/src/views/reportAnalysis/financialAnalysis/components/center-top.vue
@@ -10,7 +10,10 @@
<div class="card-body">
<div class="card-left">
<div class="card-title">鏈堝害鏀跺叆</div>
- <div class="card-amount">{{ income.amount }}</div>
+ <div class="card-amount">
+ <span>{{ formatAmountWanNumber(income.amount) }}</span>
+ <span v-if="isWanAmount(income.amount)" class="card-amount-unit">涓�</span>
+ </div>
</div>
<div class="card-right">
<div class="metric-row">
@@ -22,7 +25,15 @@
</div>
<div class="metric-row">
<span class="metric-label">閫炬湡鏁�</span>
- <span class="metric-value metric-up">{{ income.overdueCount }}</span>
+ <span class="metric-value metric-up">
+ {{ formatAmountWanNumber(income.overdueCount) }}
+ <span
+ v-if="isWanAmount(income.overdueCount)"
+ class="metric-unit"
+ >
+ 涓�
+ </span>
+ </span>
</div>
<div class="metric-row">
<span class="metric-label">閫炬湡鐜�</span>
@@ -43,16 +54,30 @@
<div class="card-body">
<div class="card-left">
<div class="card-title">鏈堝害鏀嚭</div>
- <div class="card-amount">{{ expense.amount }}</div>
+ <div class="card-amount">
+ <span>{{ formatAmountWanNumber(expense.amount) }}</span>
+ <span v-if="isWanAmount(expense.amount)" class="card-amount-unit">涓�</span>
+ </div>
</div>
<div class="card-right">
<div class="metric-row">
<span class="metric-label">浠樻鐜�</span>
- <span class="metric-value metric-down">{{ expense.netProfit }}</span>
+ <span class="metric-value" :class="metricClass(expense.netProfit)">
+ {{ formatPercent(expense.netProfit.value) }}
+ <span class="arrow">{{ metricArrow(expense.netProfit) }}</span>
+ </span>
</div>
<div class="metric-row">
<span class="metric-label">姣涘埄娑�</span>
- <span class="metric-value metric-down">{{ expense.grossProfit }}</span>
+ <span class="metric-value metric-down">
+ {{ formatAmountWanNumber(expense.grossProfit) }}
+ <span
+ v-if="isWanAmount(expense.grossProfit)"
+ class="metric-unit"
+ >
+ 涓�
+ </span>
+ </span>
</div>
<div class="metric-row">
<span class="metric-label">鍒╂鼎鐜�</span>
@@ -70,33 +95,93 @@
</template>
<script setup>
-import { ref } from 'vue'
+import { onMounted, ref } from 'vue'
+import { getMonthlyIncome, getMonthlyExpenditure } from '@/api/viewIndex'
-// 鏆傛椂浣跨敤鏈湴绀轰緥鏁版嵁锛屽悗缁彲鎺ョ湡瀹炴帴鍙h鐩�
const income = ref({
- amount: 102,
- repayRate: { value: 52, trend: 1 }, // 姝e悜 鈫�
- overdueCount: 10092,
- overdueRate: { value: 12, trend: 1 },
+ amount: 0,
+ repayRate: { value: 0, trend: 0 },
+ overdueCount: 0,
+ overdueRate: { value: 0, trend: 0 },
})
const expense = ref({
- amount: 102,
- netProfit: 291013,
- grossProfit: 10092,
- profitRate: { value: 12, trend: -1 }, // 璐熷悜 鈫�
+ amount: 0,
+ netProfit: { value: 0, trend: 0 },
+ grossProfit: 0,
+ profitRate: { value: 0, trend: 0 },
})
+
+const fetchMonthlyIncome = async () => {
+ const res = await getMonthlyIncome()
+ const data = res?.data || {}
+
+ income.value.amount = data.monthlyIncome ?? 0
+ const collectionRate = Number(data.collectionRate ?? 0)
+ const overdueRate = Number(data.overdueRate ?? 0)
+ income.value.repayRate = {
+ value: collectionRate,
+ trend: collectionRate >= 0 ? 1 : -1,
+ }
+ income.value.overdueCount = data.overdueNum ?? 0
+ income.value.overdueRate = {
+ value: overdueRate,
+ trend: overdueRate >= 0 ? 1 : -1,
+ }
+}
+
+const fetchMonthlyExpenditure = async () => {
+ const res = await getMonthlyExpenditure()
+ const data = res?.data || {}
+
+ expense.value.amount = data.monthlyExpenditure ?? 0
+ const paymentRate = Number(data.paymentRate ?? 0)
+ expense.value.netProfit = {
+ value: paymentRate,
+ trend: paymentRate >= 0 ? 1 : -1,
+ }
+ expense.value.grossProfit = data.grossProfit ?? 0
+
+ const profitMarginRate = Number(data.profitMarginRate ?? 0)
+ expense.value.profitRate = {
+ value: profitMarginRate,
+ trend: profitMarginRate >= 0 ? 1 : -1,
+ }
+}
+
+const isWanAmount = (val) => {
+ const num = Number(val) || 0
+ return Math.abs(num) >= 10000
+}
+
+const formatAmountWanNumber = (val) => {
+ const num = Number(val) || 0
+ if (Math.abs(num) >= 10000) {
+ return (num / 10000).toFixed(2)
+ }
+ return num.toFixed(2)
+}
const formatPercent = (val) => {
const num = Number(val) || 0
- return `${num.toFixed(2)}%`
+ // 鐧惧垎姣斿睍绀哄缁堢敤缁濆鍊硷紝灏忔暟淇濈暀涓や綅
+ return `${Math.abs(num).toFixed(2)}%`
}
-const metricClass = (metric) =>
- Number(metric.trend) >= 0 ? 'metric-up' : 'metric-down'
+const metricClass = (metric) => {
+ if (metric?.trend === undefined || metric?.trend === null) return 'metric-up'
+ return Number(metric.trend) >= 0 ? 'metric-up' : 'metric-down'
+}
-const metricArrow = (metric) =>
- Number(metric.trend) >= 0 ? '鈫�' : '鈫�'
+const metricArrow = (metric) => {
+ if (metric?.trend === undefined || metric?.trend === null) return ''
+ return Number(metric.trend) >= 0 ? '鈫�' : '鈫�'
+}
+
+onMounted(() => {
+ fetchMonthlyIncome()
+ fetchMonthlyExpenditure()
+})
</script>
<style scoped>
@@ -110,7 +195,7 @@
flex: 1;
display: flex;
align-items: center;
- padding: 18px 24px;
+ padding: 18px 10px;
background-image: url('@/assets/BI/border@2x.png');
background-size: 100% 100%;
background-position: center;
@@ -156,10 +241,18 @@
font-size: 36px;
line-height: 1.1;
margin-top: 8px;
+ display: inline-flex;
+ align-items: baseline;
+ white-space: nowrap;
background: linear-gradient(360deg, #008bfd 0%, #ffffff 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
+}
+
+.card-amount-unit {
+ font-size: 20px;
+ margin-left: 4px;
}
.card-right {
@@ -193,6 +286,11 @@
align-items: center;
}
+.metric-unit {
+ font-size: 12px;
+ margin-left: 2px;
+}
+
.metric-value .arrow {
font-size: 13px;
margin-left: 4px;
diff --git a/src/views/reportAnalysis/financialAnalysis/components/left-bottom.vue b/src/views/reportAnalysis/financialAnalysis/components/left-bottom.vue
index 076dcbe..7aa8c4e 100644
--- a/src/views/reportAnalysis/financialAnalysis/components/left-bottom.vue
+++ b/src/views/reportAnalysis/financialAnalysis/components/left-bottom.vue
@@ -1,7 +1,14 @@
<template>
<div>
- <PanelHeader title="鏀嚭鏋勬垚鍒嗘瀽" />
+ <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">
<div class="pie-background"></div>
@@ -25,7 +32,7 @@
import { ref, onMounted, computed } from 'vue'
import Echarts from '@/components/Echarts/echarts.vue'
import PanelHeader from './PanelHeader.vue'
-import CarouselCards from './CarouselCards.vue'
+import ProductTypeSwitch from './ProductTypeSwitch.vue'
import { rawMaterialPurchaseAmountRatio } from '@/api/viewIndex.js'
/**
@@ -41,6 +48,14 @@
}
return resObj
}
+
+// 褰撳墠绫诲瀷锛�1=鏀嚭 2=鏀跺叆
+const amountType = ref(1)
+
+const amountTypeOptions = [
+ { label: 1, text: '鏀嚭' },
+ { label: 2, text: '鏀跺叆' },
+]
// 鏁版嵁鍒楄〃锛堟潵鑷帴鍙o級
const dataList = ref([])
@@ -69,7 +84,7 @@
top: 'center',
left: '52%',
itemGap: 30,
- show: false,
+ show: true,
data: data,
formatter: function (name) {
const item = landObjData.value[name]
@@ -112,12 +127,13 @@
}
// 鍙屽眰鐜舰楗煎浘
+// 鍙屽眰鐜舰楗煎浘
const landSeries = ref([
{
- name: '浜у搧閲囪喘閲戦鍒嗘瀽',
+ name: '鏋勬垚鍒嗘瀽',
type: 'pie',
- radius: ['50%', '75%'],
- center: ['50%', '60%'],
+ radius: ['40%', '60%'],
+ center: ['25%', '50%'],
itemStyle: {
borderColor: '#0a1c3a',
borderWidth: 2,
@@ -126,16 +142,7 @@
},
},
label: {
- show: true,
- position: 'outside',
- color: '#fff',
- fontSize: 12,
- lineHeight: 18,
- // rich: {
- // ...dotRich,
- // parent: { fontSize: 14, fontWeight: 600, color: '#fff', lineHeight: 20, overflow: 'break' },
- // child: { fontSize: 12, color: '#fff', lineHeight: 18 },
- // },
+ show: false
},
minAngle: 15,
data: dataList.value,
@@ -148,8 +155,8 @@
{
// 鍐呭湀
type: 'pie',
- radius: ['50%', '60%'],
- center: ['50%', '60%'],
+ radius: ['40%', '45%'],
+ center: ['25%', '50%'],
silent: true,
label: {
show: false,
@@ -175,6 +182,8 @@
}
const fetchData = () => {
+ // 鐩墠鎺ュ彛鍙湁鏀嚭鏋勬垚鍗犳瘮锛屽厛蹇界暐绫诲瀷鍙傛暟
+ // 棰勭暀鎵╁睍锛氬悗缁彲鏍规嵁 amountType 鍒囦笉鍚屾帴鍙�
rawMaterialPurchaseAmountRatio()
.then((res) => {
if (res.code === 200 && Array.isArray(res.data)) {
@@ -199,6 +208,10 @@
})
}
+const handleTypeChange = () => {
+ fetchData()
+}
+
onMounted(() => {
fetchData()
})
@@ -209,6 +222,14 @@
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 {
@@ -228,11 +249,11 @@
.pie-background {
position: absolute;
- left: 50%;
- top: 60%;
+ left: 25%;
+ top: 50%;
transform: translate(-51.5%, -50%);
- width: 380px;
- height: 380px;
+ width: 310px;
+ height: 310px;
background-image: url('@/assets/BI/鐜懓鍥捐竟妗�.png');
background-size: contain;
background-position: center;
diff --git a/src/views/reportAnalysis/financialAnalysis/components/left-top.vue b/src/views/reportAnalysis/financialAnalysis/components/left-top.vue
index 1c9f085..c735dba 100644
--- a/src/views/reportAnalysis/financialAnalysis/components/left-top.vue
+++ b/src/views/reportAnalysis/financialAnalysis/components/left-top.vue
@@ -20,7 +20,7 @@
<script setup>
import { ref, onMounted } from 'vue'
-import { productSalesAnalysis } from '@/api/viewIndex.js'
+import { getAmountHalfYear } from '@/api/viewIndex.js'
import PanelHeader from './PanelHeader.vue'
import Echarts from '@/components/Echarts/echarts.vue'
@@ -30,13 +30,16 @@
}
const grid = { left: '3%', right: '4%', bottom: '3%', top: '10%', containLabel: true }
-const barLegend = { show: true, textStyle: { color: '#B8C8E0' }, data: ['閲戦'] }
+const barLegend = {
+ show: true,
+ textStyle: { color: '#B8C8E0' },
+ data: ['寮�绁ㄩ噾棰�', '鍥炴閲戦'],
+}
const barSeries1 = ref([
{
- name: '閲戦',
+ name: '寮�绁ㄩ噾棰�',
type: 'bar',
- barGap: 0,
- barWidth: 30,
+ barWidth: 20,
emphasis: { focus: 'series' },
itemStyle: {
color: {
@@ -46,8 +49,29 @@
x2: 0,
y2: 1,
colorStops: [
- { offset: 1, color: '#00A4ED' },
- { offset: 0, color: '#4EE4FF' },
+ { offset: 1, color: 'rgba(0, 164, 237, 0)' },
+ { offset: 0, color: 'rgba(78, 228, 255, 1)' },
+ ],
+ },
+ },
+ data: [],
+ },
+ {
+ name: '鍥炴閲戦',
+ type: 'bar',
+ barGap: '40%',
+ barWidth: 20,
+ emphasis: { focus: 'series' },
+ itemStyle: {
+ color: {
+ type: 'linear',
+ x: 0,
+ y: 0,
+ x2: 0,
+ y2: 1,
+ colorStops: [
+ { offset: 1, color: 'rgba(83, 126, 245, 0.19)' },
+ { offset: 0, color: 'rgba(144, 97, 248, 1)' },
],
},
},
@@ -71,16 +95,21 @@
const yAxis1 = [{ type: 'value', axisLabel: { color: '#B8C8E0' } }]
const fetchData = () => {
- productSalesAnalysis()
+ getAmountHalfYear()
.then((res) => {
if (res.code === 200 && Array.isArray(res.data)) {
const items = res.data
- xAxis1.value[0].data = items.map((item) => item.name)
- barSeries1.value[0].data = items.map((item) => parseFloat(item.value) || 0)
+ xAxis1.value[0].data = items.map((item) => item.month)
+ barSeries1.value[0].data = items.map(
+ (item) => parseFloat(item.invoiceAmount) || 0
+ )
+ barSeries1.value[1].data = items.map(
+ (item) => parseFloat(item.receiptAmount) || 0
+ )
}
})
.catch((err) => {
- console.error('鑾峰彇浜у搧閿�鍞噾棰濆垎鏋愬け璐�:', err)
+ console.error('鑾峰彇杩戝崐骞村洖娆句笌寮�绁ㄦ暟鎹け璐�:', err)
})
}
--
Gitblit v1.9.3