From a3c508233dd94b50c8005ec3a5d40b91341d6434 Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期四, 29 一月 2026 17:58:30 +0800
Subject: [PATCH] Merge branch 'dev_New' of http://114.132.189.42:9002/r/product-inventory-management into dev_New

---
 src/views/reportAnalysis/financialAnalysis/components/center-top.vue |  308 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 308 insertions(+), 0 deletions(-)

diff --git a/src/views/reportAnalysis/financialAnalysis/components/center-top.vue b/src/views/reportAnalysis/financialAnalysis/components/center-top.vue
new file mode 100644
index 0000000..85f4928
--- /dev/null
+++ b/src/views/reportAnalysis/financialAnalysis/components/center-top.vue
@@ -0,0 +1,308 @@
+<template>
+  <div>
+    <!-- 椤堕儴鏀舵敮鍗$墖 -->
+    <div class="finance-cards">
+      <!-- 鏈堝害鏀跺叆 -->
+      <div class="finance-card income-card">
+        <div class="icon-box">
+          <img src="@/assets/BI/icon@2x.png" alt="鍥炬爣" class="card-icon" />
+        </div>
+        <div class="card-body">
+          <div class="card-left">
+            <div class="card-title">鏈堝害鏀跺叆</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">
+              <span class="metric-label">鍥炴鐜�</span>
+              <span class="metric-value" :class="metricClass(income.repayRate)">
+                {{ formatPercent(income.repayRate.value) }}
+                <span class="arrow">{{ metricArrow(income.repayRate) }}</span>
+              </span>
+            </div>
+            <div class="metric-row">
+              <span class="metric-label">閫炬湡鏁�</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>
+              <span class="metric-value" :class="metricClass(income.overdueRate)">
+                {{ formatPercent(income.overdueRate.value) }}
+                <span class="arrow">{{ metricArrow(income.overdueRate) }}</span>
+              </span>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- 鏈堝害鏀嚭 -->
+      <div class="finance-card expense-card">
+        <div class="icon-box">
+          <img src="@/assets/BI/icon@2x.png" alt="鍥炬爣" class="card-icon" />
+        </div>
+        <div class="card-body">
+          <div class="card-left">
+            <div class="card-title">鏈堝害鏀嚭</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" :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">
+                {{ formatAmountWanNumber(expense.grossProfit) }}
+                <span
+                  v-if="isWanAmount(expense.grossProfit)"
+                  class="metric-unit"
+                >
+                  涓�
+                </span>
+              </span>
+            </div>
+            <div class="metric-row">
+              <span class="metric-label">鍒╂鼎鐜�</span>
+              <span class="metric-value" :class="metricClass(expense.profitRate)">
+                {{ formatPercent(expense.profitRate.value) }}
+                <span class="arrow">{{ metricArrow(expense.profitRate) }}</span>
+              </span>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+  </div>
+</template>
+
+<script setup>
+import { onMounted, ref } from 'vue'
+import { getMonthlyIncome, getMonthlyExpenditure } from '@/api/viewIndex'
+
+const income = ref({
+  amount: 0,
+  repayRate: { value: 0, trend: 0 },
+  overdueCount: 0,
+  overdueRate: { value: 0, trend: 0 },
+})
+
+const expense = ref({
+  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 `${Math.abs(num).toFixed(2)}%`
+}
+
+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) => {
+  if (metric?.trend === undefined || metric?.trend === null) return ''
+  return Number(metric.trend) >= 0 ? '鈫�' : '鈫�'
+}
+
+onMounted(() => {
+  fetchMonthlyIncome()
+  fetchMonthlyExpenditure()
+})
+</script>
+
+<style scoped>
+.finance-cards {
+  display: flex;
+  justify-content: space-between;
+  gap: 16px;
+}
+
+.finance-card {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  padding: 18px 10px;
+  background-image: url('@/assets/BI/border@2x.png');
+  background-size: 100% 100%;
+  background-position: center;
+  background-repeat: no-repeat;
+  min-height: 138px;
+}
+
+.icon-box {
+  width: 92px;
+  height: 92px;
+  /* border: 1px dashed rgba(208, 231, 255, 0.55); */
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 18px;
+}
+
+.card-icon {
+  width: 78px;
+  height: 78px;
+}
+
+.card-body {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 18px;
+}
+
+.card-left {
+  min-width: 90px;
+}
+
+.card-title {
+  font-weight: 400;
+  font-size: 18px;
+  color: rgba(208, 231, 255, 0.7);
+}
+
+.card-amount {
+  font-weight: 500;
+  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 {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+  padding-right: 6px;
+}
+
+.metric-row {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  font-size: 14px;
+  color: #d0e7ff;
+  white-space: nowrap;
+}
+
+.metric-label {
+  margin-right: 12px;
+}
+
+.metric-label {
+  opacity: 0.8;
+}
+
+.metric-value {
+  font-weight: 600;
+  display: inline-flex;
+  align-items: center;
+}
+
+.metric-unit {
+  font-size: 12px;
+  margin-left: 2px;
+}
+
+.metric-value .arrow {
+  font-size: 13px;
+  margin-left: 4px;
+}
+
+.metric-up {
+  color: #00c853;
+}
+
+.metric-down {
+  color: #ff5252;
+}
+
+
+</style>

--
Gitblit v1.9.3