新增财务仪表盘并统一金额到角
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
<script setup lang="ts">
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
|
||||
import {
|
||||
fetchFinanceDetails,
|
||||
type FinanceDetail,
|
||||
} from '@/features/admin/api/adminFinance'
|
||||
import { formatMoneyWithSymbol } from '@/shared/utils/money'
|
||||
import { orderStatusLabel } from '@/utils/statusLabels'
|
||||
import { formatDateTime } from '@/utils/time'
|
||||
import AdminTablePagination from '../components/AdminTablePagination.vue'
|
||||
|
||||
const loading = ref(false)
|
||||
const details = ref<FinanceDetail[]>([])
|
||||
const currentPage = ref(1)
|
||||
const currentPageSize = ref(20)
|
||||
const total = ref(0)
|
||||
const filters = reactive({
|
||||
start_date: defaultStartDate(),
|
||||
end_date: defaultEndDate(),
|
||||
date_type: 'settled',
|
||||
order_no: '',
|
||||
user_id: '',
|
||||
order_status: '',
|
||||
settlement_status: '',
|
||||
})
|
||||
|
||||
onMounted(loadDetails)
|
||||
|
||||
async function loadDetails() {
|
||||
loading.value = true
|
||||
try {
|
||||
const result = await fetchFinanceDetails({
|
||||
...filters,
|
||||
page: currentPage.value,
|
||||
page_size: currentPageSize.value,
|
||||
})
|
||||
details.value = result.items
|
||||
total.value = result.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function resetFilters() {
|
||||
filters.start_date = defaultStartDate()
|
||||
filters.end_date = defaultEndDate()
|
||||
filters.date_type = 'settled'
|
||||
filters.order_no = ''
|
||||
filters.user_id = ''
|
||||
filters.order_status = ''
|
||||
filters.settlement_status = ''
|
||||
currentPage.value = 1
|
||||
void loadDetails()
|
||||
}
|
||||
|
||||
async function handlePageChange() {
|
||||
await loadDetails()
|
||||
}
|
||||
|
||||
function money(value: number) {
|
||||
return formatMoneyWithSymbol(value)
|
||||
}
|
||||
|
||||
function moneyCent(value: number) {
|
||||
return formatMoneyWithSymbol(Number(value || 0) / 100)
|
||||
}
|
||||
|
||||
function financeStatusLabel(status: string) {
|
||||
const map: Record<string, string> = {
|
||||
normal: '正常',
|
||||
refund_pending: '退款中',
|
||||
refund_failed: '退款失败',
|
||||
settlement_diff: '结算差异',
|
||||
}
|
||||
return map[status] || status
|
||||
}
|
||||
|
||||
function financeStatusType(status: string) {
|
||||
if (status === 'normal') return 'success'
|
||||
if (status === 'settlement_diff' || status === 'refund_failed') return 'danger'
|
||||
if (status === 'refund_pending') return 'warning'
|
||||
return 'info'
|
||||
}
|
||||
|
||||
function settlementStatusLabel(status: string) {
|
||||
const map: Record<string, string> = {
|
||||
unsettled: '未结算',
|
||||
settling: '结算中',
|
||||
settled: '已结算',
|
||||
}
|
||||
return map[status] || status || '-'
|
||||
}
|
||||
|
||||
function defaultStartDate() {
|
||||
const date = new Date()
|
||||
date.setDate(date.getDate() - 29)
|
||||
return formatInputDate(date)
|
||||
}
|
||||
|
||||
function defaultEndDate() {
|
||||
return formatInputDate(new Date())
|
||||
}
|
||||
|
||||
function formatInputDate(date: Date) {
|
||||
return date.toISOString().slice(0, 10)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="page">
|
||||
<div class="page-header-row">
|
||||
<div class="page-header">
|
||||
<p class="eyebrow">Finance Details</p>
|
||||
<h1>财务明细</h1>
|
||||
<p>按订单核对收款、退款、平台收入、号主应得与钱包实际入账。</p>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button @click="resetFilters">重置</el-button>
|
||||
<el-button type="primary" :icon="Search" :loading="loading" @click="loadDetails">
|
||||
查询
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-form class="filter-panel" label-position="top">
|
||||
<el-form-item label="日期类型">
|
||||
<el-select v-model="filters.date_type" class="full-control">
|
||||
<el-option label="结算日期" value="settled" />
|
||||
<el-option label="下单日期" value="created" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="开始日期">
|
||||
<el-date-picker
|
||||
v-model="filters.start_date"
|
||||
class="full-control"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="结束日期">
|
||||
<el-date-picker
|
||||
v-model="filters.end_date"
|
||||
class="full-control"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="订单号">
|
||||
<el-input v-model="filters.order_no" clearable placeholder="按订单号筛选" />
|
||||
</el-form-item>
|
||||
<el-form-item label="用户 ID">
|
||||
<el-input v-model="filters.user_id" clearable placeholder="租客或号主 ID" />
|
||||
</el-form-item>
|
||||
<el-form-item label="订单状态">
|
||||
<el-select v-model="filters.order_status" clearable placeholder="全部状态" class="full-control">
|
||||
<el-option label="待支付" value="pending_payment" />
|
||||
<el-option label="使用中" value="renting" />
|
||||
<el-option label="待结账" value="pending_checkout_confirm" />
|
||||
<el-option label="已完成" value="completed" />
|
||||
<el-option label="已关闭" value="closed" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="结算状态">
|
||||
<el-select
|
||||
v-model="filters.settlement_status"
|
||||
clearable
|
||||
placeholder="全部结算"
|
||||
class="full-control"
|
||||
>
|
||||
<el-option label="未结算" value="unsettled" />
|
||||
<el-option label="已结算" value="settled" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" class="table-panel" :data="details">
|
||||
<el-table-column label="订单" min-width="180">
|
||||
<template #default="{ row }">
|
||||
<RouterLink :to="`/admin/orders/${row.order_id}`">{{ row.order_no }}</RouterLink>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="150">
|
||||
<template #default="{ row }">
|
||||
<div>{{ orderStatusLabel(row.order_status) }}</div>
|
||||
<small>{{ settlementStatusLabel(row.settlement_status) }}</small>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="租客/号主" min-width="170">
|
||||
<template #default="{ row }">
|
||||
<div>{{ row.renter_phone || row.renter_nickname || row.renter_id }}</div>
|
||||
<small>{{ row.owner_phone || row.owner_nickname || row.owner_id }}</small>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="收款" width="110">
|
||||
<template #default="{ row }">{{ moneyCent(row.paid_amount_cent) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已退款" width="110">
|
||||
<template #default="{ row }">{{ moneyCent(row.refunded_amount_cent) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="退款中" width="110">
|
||||
<template #default="{ row }">{{ moneyCent(row.refunding_amount_cent) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="净流入" width="110">
|
||||
<template #default="{ row }">{{ moneyCent(row.channel_net_amount_cent) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="平台收入" width="110">
|
||||
<template #default="{ row }">{{ money(row.checkout_platform_fee) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="号主应得" width="110">
|
||||
<template #default="{ row }">{{ money(row.checkout_owner_income) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="号主入账" width="110">
|
||||
<template #default="{ row }">{{ money(row.owner_wallet_income_amount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="差异" width="110">
|
||||
<template #default="{ row }">
|
||||
<span :class="{ 'amount-danger': Math.abs(row.settlement_diff_amount) >= 0.05 }">
|
||||
{{ money(row.settlement_diff_amount) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="财务状态" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="financeStatusType(row.finance_status)">
|
||||
{{ financeStatusLabel(row.finance_status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结算时间" min-width="170">
|
||||
<template #default="{ row }">{{ row.settled_at ? formatDateTime(row.settled_at) : '-' }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<AdminTablePagination
|
||||
v-if="total > 0"
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="currentPageSize"
|
||||
:total="total"
|
||||
:loading="loading"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.amount-danger {
|
||||
color: #dc2626;
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user