feat(finance): show customer credit balances

This commit is contained in:
2026-09-05 11:54:55 +08:00
parent 719a6421c9
commit 46a91fbca0
3 changed files with 21 additions and 6 deletions
+9 -2
View File
@@ -13,11 +13,15 @@ export interface FinanceAccount {
actualBalance: string
frozenBalance: string
availableBalance: string
creditLimit: string
creditUsed: string
creditFrozen: string
availableCredit: string
version: number
updatedAt: string
}
export type AccountChangeType = 'RECHARGE' | 'DEDUCTION' | 'FREEZE' | 'UNFREEZE' | 'CONSUMPTION'
export type AccountChangeType = 'RECHARGE' | 'DEDUCTION' | 'FREEZE' | 'UNFREEZE' | 'CONSUMPTION' | 'CREDIT_LIMIT'
export interface AccountLedgerEntry {
ledgerId: string
@@ -27,7 +31,10 @@ export interface AccountLedgerEntry {
actualBalanceAfter: string
frozenBalanceAfter: string
availableBalanceAfter: string
sourceType: 'CLIENT_RECHARGE' | 'MANUAL_RECHARGE' | 'MANUAL_DEDUCTION'
creditLimitAfter: string
creditUsedAfter: string
availableCreditAfter: string
sourceType: 'CLIENT_RECHARGE' | 'MANUAL_RECHARGE' | 'MANUAL_DEDUCTION' | 'CREDIT_LIMIT_ADJUSTMENT' | string
sourceNo: string
operatorName: string | null
remark: string | null
@@ -27,7 +27,10 @@ test('activates the account route and maps read and recharge permissions indepen
})
test('renders server balances, ledger and recharge states without internal identifiers', () => {
for (const field of ['actualBalance', 'frozenBalance', 'availableBalance']) assert.match(viewSource, new RegExp(`account\\?\\.${field}`))
for (const field of ['actualBalance', 'frozenBalance', 'availableBalance', 'creditLimit', 'creditUsed', 'availableCredit']) assert.match(viewSource, new RegExp(`account\\?\\.${field}`))
for (const field of ['creditUsedAfter', 'availableCreditAfter']) assert.match(viewSource, new RegExp(`row\\.${field}`))
assert.match(viewSource, /实际可用资金与可用授信合计/)
assert.match(viewSource, /CREDIT_LIMIT: '授信调整'/)
assert.match(viewSource, /row-key="businessNo"/)
assert.match(viewSource, /row-key="requestNo"/)
assert.match(viewSource, /empty-text="暂无资金流水"/)
@@ -51,7 +51,7 @@ const amountValid = computed(() => {
const canSubmit = computed(() => amountValid.value && channelReady.value && Boolean(voucherFile.value) && !submitting.value)
const changeLabels: Record<AccountChangeType, string> = {
RECHARGE: '充值', DEDUCTION: '扣款', FREEZE: '冻结', UNFREEZE: '解冻', CONSUMPTION: '消费',
RECHARGE: '充值', DEDUCTION: '扣款', FREEZE: '冻结', UNFREEZE: '解冻', CONSUMPTION: '消费', CREDIT_LIMIT: '授信调整',
}
const statusLabels: Record<RechargeStatus, string> = { PENDING: '待审核', APPROVED: '已通过', REJECTED: '已驳回' }
const methodLabels: Record<RechargePaymentMethod, string> = { WECHAT: '微信支付', ALIPAY: '支付宝' }
@@ -208,7 +208,7 @@ onMounted(() => { void loadAccountData() })
<template>
<div v-loading="pageLoading" class="finance-account-page" :aria-busy="pageLoading">
<div class="account-heading">
<div><h2>资金账户</h2><p>查看本企业 CNY 资金余额、账户流水和充值申请状态。</p></div>
<div><h2>资金账户</h2><p>查看本企业 CNY 资金、授信额度、账户流水和充值申请状态。</p></div>
<el-button v-if="canRecharge" type="primary" @click="openRecharge"><CircleDollarSign :size="17" />账户充值</el-button>
</div>
@@ -220,7 +220,10 @@ onMounted(() => { void loadAccountData() })
<section class="balance-grid" aria-label="账户余额">
<article><span>实际余额</span><strong>¥ {{ formatAmount(account?.actualBalance) }}</strong><small>当前账户实际资金</small></article>
<article><span>冻结金额</span><strong>¥ {{ formatAmount(account?.frozenBalance) }}</strong><small>暂不可使用的资金</small></article>
<article class="balance-available"><span>可用余额</span><strong>¥ {{ formatAmount(account?.availableBalance) }}</strong><small>可用于后续业务支付</small></article>
<article class="balance-available"><span>总可用余额</span><strong>¥ {{ formatAmount(account?.availableBalance) }}</strong><small>实际可用资金与可用授信合计</small></article>
<article><span>授信额度</span><strong>¥ {{ formatAmount(account?.creditLimit) }}</strong><small>平台配置的授信上限</small></article>
<article><span>已用授信</span><strong>¥ {{ formatAmount(account?.creditUsed) }}</strong><small>已结算使用的授信</small></article>
<article><span>可用授信</span><strong>¥ {{ formatAmount(account?.availableCredit) }}</strong><small>扣除已用和冻结后的授信</small></article>
</section>
<section class="account-card">
@@ -232,6 +235,8 @@ onMounted(() => { void loadAccountData() })
<el-table-column label="实际余额" width="145" align="right"><template #default="{ row }">¥ {{ formatAmount(row.actualBalanceAfter) }}</template></el-table-column>
<el-table-column label="冻结金额" width="145" align="right"><template #default="{ row }">¥ {{ formatAmount(row.frozenBalanceAfter) }}</template></el-table-column>
<el-table-column label="可用余额" width="145" align="right"><template #default="{ row }">¥ {{ formatAmount(row.availableBalanceAfter) }}</template></el-table-column>
<el-table-column label="已用授信" width="145" align="right"><template #default="{ row }">¥ {{ formatAmount(row.creditUsedAfter) }}</template></el-table-column>
<el-table-column label="可用授信" width="145" align="right"><template #default="{ row }">¥ {{ formatAmount(row.availableCreditAfter) }}</template></el-table-column>
<el-table-column prop="operatorName" label="操作人" min-width="130"><template #default="{ row }">{{ row.operatorName || '—' }}</template></el-table-column>
<el-table-column prop="remark" label="备注" min-width="180"><template #default="{ row }">{{ row.remark || '—' }}</template></el-table-column>
<el-table-column label="发生时间" width="170"><template #default="{ row }">{{ formatDateTime(row.createdAt) }}</template></el-table-column>