Files
hfb_sys/frontend/src/features/admin/views/AdminFinanceDashboardView.vue
T

407 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { Refresh, Search } from '@element-plus/icons-vue'
import { computed, onMounted, reactive, ref } from 'vue'
import {
fetchFinanceDashboard,
SETTLEMENT_DIFF_THRESHOLD_CENT,
type FinanceDashboard,
type FinanceDailyItem,
} from '@/features/admin/api/adminFinance'
import { formatCentWithSymbol } from '@/shared/utils/money'
import { formatDateTime, formatInputDate } from '@/shared/utils/time'
const loading = ref(false)
const dashboard = ref<FinanceDashboard | null>(null)
const filters = reactive({
start_date: defaultStartDate(),
end_date: defaultEndDate(),
})
const dailyItems = computed(() => dashboard.value?.daily_items ?? [])
onMounted(loadDashboard)
async function loadDashboard() {
loading.value = true
try {
dashboard.value = await fetchFinanceDashboard(filters)
} finally {
loading.value = false
}
}
function moneyCent(value: number) {
return formatCentWithSymbol(value)
}
function defaultStartDate() {
const date = new Date()
date.setDate(date.getDate() - 6)
return formatInputDate(date)
}
function defaultEndDate() {
return formatInputDate(new Date())
}
function diffType(value: number) {
return Math.abs(Number(value || 0)) >= SETTLEMENT_DIFF_THRESHOLD_CENT ? 'danger' : 'success'
}
function rowDiffClass(row: FinanceDailyItem) {
return Math.abs(Number(row.settlement_diff_amount_cent || 0)) >= SETTLEMENT_DIFF_THRESHOLD_CENT
? 'amount-danger'
: ''
}
function rowOfflinePendingClass(row: FinanceDailyItem) {
return Number(row.offline_settlement_pending_amount_cent || 0) > 0 ? 'amount-warning' : ''
}
</script>
<template>
<section class="page">
<div class="page-header-row">
<div class="page-header">
<p class="eyebrow">Finance Dashboard</p>
<h1>财务仪表盘</h1>
<p>按天查看收付款平台利润号主结算及全部资金出款</p>
</div>
<div class="toolbar-actions">
<el-date-picker
v-model="filters.start_date"
type="date"
value-format="YYYY-MM-DD"
placeholder="开始日期"
/>
<el-date-picker
v-model="filters.end_date"
type="date"
value-format="YYYY-MM-DD"
placeholder="结束日期"
/>
<el-button type="primary" :icon="Search" :loading="loading" @click="loadDashboard">
查询
</el-button>
<el-button :icon="Refresh" :loading="loading" @click="loadDashboard">刷新</el-button>
</div>
</div>
<div v-if="dashboard" class="metric-grid">
<div class="metric-card">
<span>总流水</span>
<strong>{{ moneyCent(dashboard.summary.total_flow_amount_cent) }}</strong>
<small>{{ dashboard.summary.successful_pay_count }} 笔成功收款</small>
</div>
<div class="metric-card">
<span>总退款</span>
<strong>{{ moneyCent(dashboard.summary.total_refund_amount_cent) }}</strong>
<small
>{{ dashboard.summary.full_refund_count }} 笔全额 /
{{ dashboard.summary.partial_refund_count }} 笔部分</small
>
</div>
<div class="metric-card">
<span>渠道净流入</span>
<strong>{{ moneyCent(dashboard.summary.channel_net_amount_cent) }}</strong>
<small>成功收款 - 成功退款</small>
</div>
<div class="metric-card">
<span>平台利润</span>
<strong>{{ moneyCent(dashboard.summary.platform_income_amount_cent) }}</strong>
<small>
普通订单 {{ moneyCent(dashboard.summary.normal_order_profit_amount_cent) }} + 线下提号
{{ moneyCent(dashboard.summary.pickup_profit_amount_cent) }}
</small>
</div>
<div class="metric-card">
<span>号主应得</span>
<strong>{{ moneyCent(dashboard.summary.owner_should_income_amount_cent) }}</strong>
<small>结账单口径</small>
</div>
<div class="metric-card">
<span>号主应结算金额</span>
<strong>{{ moneyCent(dashboard.summary.owner_effective_settlement_amount_cent) }}</strong>
<small>钱包已入账 + 平台代管应付</small>
</div>
<div class="metric-card">
<span>区间待线下结算</span>
<strong>{{ moneyCent(dashboard.summary.offline_settlement_pending_amount_cent) }}</strong>
<small>{{ dashboard.summary.offline_settlement_pending_count }} 笔平台代管待处理</small>
</div>
<div class="metric-card">
<span>退款中</span>
<strong>{{ moneyCent(dashboard.summary.pending_refund_amount_cent) }}</strong>
<small>{{ dashboard.summary.pending_refund_count }} 笔待回执</small>
</div>
<div class="metric-card">
<span>结算差异</span>
<strong>
<el-tag :type="diffType(dashboard.summary.settlement_diff_amount_cent)">
{{ moneyCent(dashboard.summary.settlement_diff_amount_cent) }}
</el-tag>
</strong>
<small>{{ dashboard.summary.financial_exception_count }} 个异常订单</small>
</div>
<div class="metric-card">
<span>预计利润</span>
<strong>{{ moneyCent(dashboard.summary.estimated_income_amount_cent) }}</strong>
<small>{{ dashboard.summary.pending_settle_order_count }} 个待结算订单</small>
</div>
</div>
<div v-if="dashboard?.mohong_summary" class="section-heading">
<div>
<h2>撞车商城</h2>
<p>商城交易流水单独统计不计入平台利润</p>
</div>
</div>
<div v-if="dashboard?.mohong_summary" class="metric-grid mohong-grid">
<div class="metric-card mohong-card">
<span>商城成交额</span>
<strong>{{ moneyCent(dashboard.mohong_summary.flow_amount_cent) }}</strong>
<small>{{ dashboard.mohong_summary.paid_order_count }} 笔成功订单</small>
</div>
<div class="metric-card mohong-card">
<span>商城退款</span>
<strong>{{ moneyCent(dashboard.mohong_summary.refund_amount_cent) }}</strong>
<small>已完成退款</small>
</div>
<div class="metric-card mohong-card">
<span>商城渠道净流入</span>
<strong>{{ moneyCent(dashboard.mohong_summary.net_amount_cent) }}</strong>
<small>成交额 - 已退款</small>
</div>
</div>
<div v-if="dashboard?.disbursement_summary" class="section-heading">
<div>
<h2>资金出款</h2>
<p>已打款按所选日期统计待处理金额为当前全部待办</p>
</div>
</div>
<div v-if="dashboard?.disbursement_summary" class="metric-grid disbursement-grid">
<div class="metric-card disbursement-card">
<span>全部实际出款</span>
<strong>{{ moneyCent(dashboard.disbursement_summary.paid_amount_cent) }}</strong>
<small>{{ dashboard.disbursement_summary.paid_count }} 笔实际出款合计</small>
</div>
<div class="metric-card disbursement-card">
<span>平台代管打款</span>
<strong>{{
moneyCent(dashboard.disbursement_summary.offline_settlement_paid_amount_cent)
}}</strong>
<small>{{ dashboard.disbursement_summary.offline_settlement_paid_count }} 笔线下结算</small>
</div>
<div class="metric-card disbursement-card">
<span>用户提现打款</span>
<strong>{{ moneyCent(dashboard.disbursement_summary.withdrawal_paid_amount_cent) }}</strong>
<small>
{{ dashboard.disbursement_summary.withdrawal_paid_count }} 钱包核销
{{ moneyCent(dashboard.disbursement_summary.withdrawal_amount_cent) }}手续费
{{ moneyCent(dashboard.disbursement_summary.withdrawal_fee_amount_cent) }}
</small>
</div>
<div class="metric-card disbursement-card">
<span>其他线下出款</span>
<strong>{{ moneyCent(dashboard.disbursement_summary.manual_paid_amount_cent) }}</strong>
<small>{{ dashboard.disbursement_summary.manual_paid_count }} 笔有效记账</small>
</div>
<div class="metric-card pending-card">
<span>当前待出款</span>
<strong>{{ moneyCent(dashboard.disbursement_summary.pending_payment_amount_cent) }}</strong>
<small>{{ dashboard.disbursement_summary.pending_payment_count }} 笔已进入打款阶段</small>
</div>
<div class="metric-card pending-card">
<span>代管待打款</span>
<strong>{{
moneyCent(dashboard.disbursement_summary.offline_settlement_pending_amount_cent)
}}</strong>
<small
>{{ dashboard.disbursement_summary.offline_settlement_pending_count }} 笔待处理</small
>
</div>
<div class="metric-card pending-card">
<span>提现待打款</span>
<strong>{{
moneyCent(dashboard.disbursement_summary.withdrawal_pending_amount_cent)
}}</strong>
<small>{{ dashboard.disbursement_summary.withdrawal_pending_count }} 笔已审核</small>
</div>
<div class="metric-card review-card">
<span>提现待审核</span>
<strong>{{
moneyCent(dashboard.disbursement_summary.withdrawal_review_amount_cent)
}}</strong>
<small>{{ dashboard.disbursement_summary.withdrawal_review_count }} 笔待审核</small>
</div>
</div>
<div v-if="dashboard?.pickup_summary" class="metric-grid pickup-grid">
<div class="metric-card pickup-card">
<span>线下提号结算</span>
<strong>{{ moneyCent(dashboard.pickup_summary.settled_amount_cent) }}</strong>
<small>{{ dashboard.pickup_summary.completed_count }} 笔已完成</small>
</div>
<div class="metric-card pickup-card">
<span>线下提号利润</span>
<strong>{{ moneyCent(dashboard.pickup_summary.profit_amount_cent) }}</strong>
<small>电商平台手工录入利润</small>
</div>
<div class="metric-card pickup-card">
<span>提号中</span>
<strong>{{ dashboard.pickup_summary.in_progress_count }}</strong>
<small>进行中笔数</small>
</div>
</div>
<el-table v-loading="loading" class="table-panel" :data="dailyItems">
<el-table-column prop="date" label="日期" width="130" />
<el-table-column label="总流水" width="130">
<template #default="{ row }">{{ moneyCent(row.total_flow_amount_cent) }}</template>
</el-table-column>
<el-table-column label="总退款" width="130">
<template #default="{ row }">{{ moneyCent(row.total_refund_amount_cent) }}</template>
</el-table-column>
<el-table-column label="渠道净流入" width="140">
<template #default="{ row }">{{ moneyCent(row.channel_net_amount_cent) }}</template>
</el-table-column>
<el-table-column label="平台利润" width="130">
<template #default="{ row }">{{ moneyCent(row.platform_income_amount_cent) }}</template>
</el-table-column>
<el-table-column label="号主应得" width="130">
<template #default="{ row }">{{ moneyCent(row.owner_should_income_amount_cent) }}</template>
</el-table-column>
<el-table-column label="结算金额" width="130">
<template #default="{ row }">{{
moneyCent(row.owner_effective_settlement_amount_cent)
}}</template>
</el-table-column>
<el-table-column label="实际出款" width="130">
<template #default="{ row }">
{{ moneyCent(row.disbursement_paid_amount_cent) }}
</template>
</el-table-column>
<el-table-column label="代管打款" width="130">
<template #default="{ row }">
{{ moneyCent(row.offline_settlement_paid_amount_cent) }}
</template>
</el-table-column>
<el-table-column label="提现打款" width="130">
<template #default="{ row }">
{{ moneyCent(row.withdrawal_paid_amount_cent) }}
</template>
</el-table-column>
<el-table-column label="其他线下" width="130">
<template #default="{ row }">
{{ moneyCent(row.manual_paid_amount_cent) }}
</template>
</el-table-column>
<el-table-column label="待线下" width="130">
<template #default="{ row }">
<span :class="rowOfflinePendingClass(row)">
{{ moneyCent(row.offline_settlement_pending_amount_cent) }}
</span>
</template>
</el-table-column>
<el-table-column label="结算差异" width="130">
<template #default="{ row }">
<span :class="rowDiffClass(row)">{{ moneyCent(row.settlement_diff_amount_cent) }}</span>
</template>
</el-table-column>
<el-table-column label="预计收入" width="130">
<template #default="{ row }">{{ moneyCent(row.estimated_income_amount_cent) }}</template>
</el-table-column>
<el-table-column label="销售数量" width="110">
<template #default="{ row }">{{ row.successful_pay_count }}</template>
</el-table-column>
<el-table-column label="全额退款" width="100">
<template #default="{ row }">{{ row.full_refund_count }}</template>
</el-table-column>
<el-table-column label="部分退款" width="100">
<template #default="{ row }">{{ row.partial_refund_count }}</template>
</el-table-column>
<el-table-column label="结算数量" width="110">
<template #default="{ row }">{{ row.settled_order_count }}</template>
</el-table-column>
</el-table>
<p v-if="dashboard" class="generated-at">
数据生成时间{{ formatDateTime(dashboard.generated_at) }}
</p>
</section>
</template>
<style scoped>
.amount-danger {
color: #dc2626;
font-weight: 700;
}
.amount-warning {
color: #d97706;
font-weight: 700;
}
.pickup-grid {
margin-top: 12px;
}
.mohong-grid {
margin-bottom: 20px;
}
.section-heading {
display: flex;
align-items: flex-end;
justify-content: space-between;
margin: 4px 0 12px;
}
.section-heading h2 {
margin: 0;
color: #1f2937;
font-size: 18px;
letter-spacing: 0;
}
.section-heading p {
margin: 5px 0 0;
color: #7f8aa3;
font-size: 13px;
}
.disbursement-grid {
margin-bottom: 20px;
}
.disbursement-card {
border-left: 3px solid #0f766e;
}
.pending-card {
border-left: 3px solid #d97706;
}
.review-card {
border-left: 3px solid #64748b;
}
.pickup-card {
border-left: 3px solid #6366f1;
}
.mohong-card {
border-left: 3px solid #2563eb;
}
.generated-at {
margin: 12px 0 0;
color: #64748b;
font-size: 13px;
}
</style>