Files
hfb_sys/frontend/src/features/admin/views/AdminFinanceDashboardView.vue
T
yml2213 9ef0843eb0 feat(pickup): 新增管理员线下提号功能
- 新建 admin_pickups 表独立于 rental_orders,不污染订单状态机与财务统计口径
- 提号中/已完成/已取消三态:创建锁定账号,完成时给卖家钱包加可用余额并下架 listing,取消解锁账号
- 完成时 listing 置 completed,复用现有 listingLockedForOwnerMutation 拦截再上架
- 管理端提号管理页(列表/创建/完成/取消/可提号账号搜索)+ 卖家端提号记录页
- 财务仪表盘新增线下提号独立统计块,与正常订单口径分离
- 钱包流水 label、状态标签、菜单入口同步补齐
- 优化卖家服务悬浮菜单为一行四个,尺寸与配色对齐买家服务
2026-07-03 19:04:35 +08:00

223 lines
7.8 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'
: ''
}
</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>{{ dashboard.summary.settled_order_count }} 个已结算订单</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_wallet_income_amount_cent) }}</strong>
<small>钱包流水口径</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?.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>{{ 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_wallet_income_amount_cent) }}</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;
}
.pickup-grid {
margin-top: 12px;
}
.pickup-card {
border-left: 3px solid #6366f1;
}
.generated-at {
margin: 12px 0 0;
color: #64748b;
font-size: 13px;
}
</style>