spring
3 天以前 a5378ba9d7f0aac37092c43eecdf54782d714bc5
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'
// 暂时使用本地示例数据,后续可接真实接口覆盖
const income = ref({
  amount: 102,
  repayRate: { value: 52, trend: 1 }, // 正向 ↑
  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;