feat: align finance account and overseas actions
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
|
||||
## 客户资金账户
|
||||
|
||||
- `pages/FinanceCustomerAccountsView.vue` 的列表和详情展示实际余额、冻结金额、授信额度、已用授信、冻结授信和总可用余额;操作列固定第一列。客户上传的充值申请在详情内按待审核优先展示,审核、驳回及凭证预览以服务端申请版本和客户归属为准。
|
||||
- `pages/FinanceCustomerAccountsView.vue` 的列表和详情展示实际余额、冻结金额、授信额度、授信有效期、已用授信、冻结授信和总可用余额;操作列固定第一列。授信到期日必须晚于今天且不限制最远日期,到期日当天仍有效。客户上传的充值申请在详情内按待审核优先展示,审核、驳回及凭证预览以服务端申请版本和客户归属为准。
|
||||
- 平台手工充值、扣款和授信调整必须提交账户版本与幂等键;扣款及授信下限由服务端校验。页面只展示服务端余额、授信和流水结果,不在浏览器补算或回退静态数据。
|
||||
|
||||
## 客户筛选与统计
|
||||
|
||||
@@ -79,12 +79,12 @@ describe('finance api', () => {
|
||||
})
|
||||
|
||||
it('uses the formal G1 account list path and fixed page DTO', async () => {
|
||||
const get = vi.spyOn(http, 'get').mockResolvedValue({ code: '200', message: 'success', requestId: 'req-g1', traceId: 'trace-g1', data: [{ customerId: 10001, customerCode: 'C-10001', customerName: '客户A', contactName: '张三', contactPhone: '13800000000', outstandingReceivable: 300, pendingBilling: 50, receivedInPeriod: 100, totalCost: 260, paidCost: 180, outstandingPayable: 80, status: 'ACTIVE', lastChangedAt: '2026-08-11T10:00:00', actualBalance: '126580.50', frozenBalance: '8500.00', availableBalance: '138080.50', creditLimit: '20000.00', creditUsed: '0.00', availableCredit: '20000.00', accountVersion: 7, pendingRechargeCount: 2 }], page: { page: 2, pageSize: 20, total: 21 } })
|
||||
const get = vi.spyOn(http, 'get').mockResolvedValue({ code: '200', message: 'success', requestId: 'req-g1', traceId: 'trace-g1', data: [{ customerId: 10001, customerCode: 'C-10001', customerName: '客户A', contactName: '张三', contactPhone: '13800000000', outstandingReceivable: 300, pendingBilling: 50, receivedInPeriod: 100, totalCost: 260, paidCost: 180, outstandingPayable: 80, status: 'ACTIVE', lastChangedAt: '2026-08-11T10:00:00', actualBalance: '126580.50', frozenBalance: '8500.00', availableBalance: '138080.50', creditLimit: '20000.00', creditUsed: '0.00', availableCredit: '20000.00', creditExpiresOn: '2028-09-10', creditActive: true, accountVersion: 7, pendingRechargeCount: 2 }], page: { page: 2, pageSize: 20, total: 21 } })
|
||||
|
||||
const result = await listFinanceAccounts({ page: 2, size: 20, customerId: '10001', status: 'ACTIVE' })
|
||||
|
||||
expect(get).toHaveBeenCalledWith('/platform/finance/v1/accounts', { params: { page: 2, pageSize: 20, customerId: '10001', status: 'ACTIVE' } })
|
||||
expect(result).toEqual({ records: [{ customerId: '10001', customerCode: 'C-10001', customerName: '客户A', contactName: '张三', contactPhone: '13800000000', outstandingReceivable: 300, pendingBilling: 50, receivedInPeriod: 100, totalCost: 260, paidCost: 180, outstandingPayable: 80, status: 'ACTIVE', lastChangedAt: '2026-08-11T10:00:00', actualBalance: '126580.50', frozenBalance: '8500.00', availableBalance: '138080.50', creditLimit: '20000.00', creditUsed: '0.00', availableCredit: '20000.00', accountVersion: 7, pendingRechargeCount: 2 }], total: 21, pages: 2 })
|
||||
expect(result).toEqual({ records: [{ customerId: '10001', customerCode: 'C-10001', customerName: '客户A', contactName: '张三', contactPhone: '13800000000', outstandingReceivable: 300, pendingBilling: 50, receivedInPeriod: 100, totalCost: 260, paidCost: 180, outstandingPayable: 80, status: 'ACTIVE', lastChangedAt: '2026-08-11T10:00:00', actualBalance: '126580.50', frozenBalance: '8500.00', availableBalance: '138080.50', creditLimit: '20000.00', creditUsed: '0.00', availableCredit: '20000.00', creditExpiresOn: '2028-09-10', creditActive: true, accountVersion: 7, pendingRechargeCount: 2 }], total: 21, pages: 2 })
|
||||
})
|
||||
|
||||
it('uses the formal G1 account ledger path and fixed page DTO', async () => {
|
||||
@@ -116,9 +116,9 @@ describe('finance api', () => {
|
||||
it('updates the customer credit limit with account version and idempotency', async () => {
|
||||
const put = vi.spyOn(http, 'put').mockResolvedValue({ code: '200', message: 'ok', data: { ledgerId: 102, businessNo: 'AC-102', changeType: 'CREDIT_LIMIT', amount: '20000.00', creditLimitAfter: '20000.00', creditUsedAfter: '0.00', availableCreditAfter: '20000.00' } })
|
||||
|
||||
const result = await updateFinanceCreditLimit('10001', { creditLimit: '20000.00', remark: '初始化授信', expectedAccountVersion: 7 })
|
||||
const result = await updateFinanceCreditLimit('10001', { creditLimit: '20000.00', creditExpiresOn: '2028-09-10', remark: '初始化授信', expectedAccountVersion: 7 })
|
||||
|
||||
expect(put).toHaveBeenCalledWith('/platform/finance/v1/accounts/10001/credit-limit', { creditLimit: '20000.00', remark: '初始化授信', expectedAccountVersion: 7 }, { headers: { 'Idempotency-Key': expect.any(String) } })
|
||||
expect(put).toHaveBeenCalledWith('/platform/finance/v1/accounts/10001/credit-limit', { creditLimit: '20000.00', creditExpiresOn: '2028-09-10', remark: '初始化授信', expectedAccountVersion: 7 }, { headers: { 'Idempotency-Key': expect.any(String) } })
|
||||
expect(result).toMatchObject({ ledgerId: 102, changeType: 'CREDIT_LIMIT', creditLimitAfter: '20000.00', availableCreditAfter: '20000.00' })
|
||||
})
|
||||
|
||||
|
||||
@@ -141,7 +141,8 @@ function normalizeFinanceAccount(item: RawResource): FinanceAccountSummary {
|
||||
paidCost: numberValue(item.paidCost), outstandingPayable: numberValue(item.outstandingPayable), status: stringValue(item.status),
|
||||
lastChangedAt: stringValue(item.lastChangedAt), actualBalance: stringValue(item.actualBalance), frozenBalance: stringValue(item.frozenBalance),
|
||||
availableBalance: stringValue(item.availableBalance), creditLimit: stringValue(item.creditLimit), creditUsed: stringValue(item.creditUsed),
|
||||
availableCredit: stringValue(item.availableCredit), accountVersion: numberValue(item.accountVersion), pendingRechargeCount: numberValue(item.pendingRechargeCount),
|
||||
availableCredit: stringValue(item.availableCredit), creditExpiresOn: nullableStringValue(item.creditExpiresOn), creditActive: Boolean(item.creditActive),
|
||||
accountVersion: numberValue(item.accountVersion), pendingRechargeCount: numberValue(item.pendingRechargeCount),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,6 +160,7 @@ export async function listFinanceAccountLedger(customerId: string, params: { pag
|
||||
availableBalanceAfter: nullableStringValue(item.availableBalanceAfter), sourceType: nullableStringValue(item.sourceType),
|
||||
creditLimitAfter: nullableStringValue(item.creditLimitAfter), creditUsedAfter: nullableStringValue(item.creditUsedAfter),
|
||||
availableCreditAfter: nullableStringValue(item.availableCreditAfter),
|
||||
creditExpiresOnAfter: nullableStringValue(item.creditExpiresOnAfter),
|
||||
sourceNo: nullableStringValue(item.sourceNo), occurredDate: nullableStringValue(item.occurredDate),
|
||||
paymentMethod: nullableStringValue(item.paymentMethod), referenceNo: nullableStringValue(item.referenceNo),
|
||||
createdAt: nullableStringValue(item.createdAt),
|
||||
@@ -201,7 +203,7 @@ export async function adjustFinanceAccount(customerId: string, payload: FinanceA
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
export async function updateFinanceCreditLimit(customerId: string, payload: { creditLimit: string; remark?: string; expectedAccountVersion: number }) {
|
||||
export async function updateFinanceCreditLimit(customerId: string, payload: { creditLimit: string; creditExpiresOn: string; remark?: string; expectedAccountVersion: number }) {
|
||||
const response = await http.put<ApiEnvelope<RawResource>, ApiEnvelope<RawResource>>(`${basePath}/accounts/${customerId}/credit-limit`, payload, mutationHeaders('credit-limit'))
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ function memoryStorage(): Storage {
|
||||
const accountFixture = {
|
||||
customerId: '10001', customerCode: 'C-001', customerName: '客户A', contactName: '张三', contactPhone: '13800000000',
|
||||
outstandingReceivable: 100, pendingBilling: 20, receivedInPeriod: 30, totalCost: 90, paidCost: 50, outstandingPayable: 40,
|
||||
actualBalance: '126580.50', frozenBalance: '8500.00', availableBalance: '138080.50', creditLimit: '20000.00', creditUsed: '0.00', availableCredit: '20000.00', accountVersion: 7, pendingRechargeCount: 1,
|
||||
actualBalance: '126580.50', frozenBalance: '8500.00', availableBalance: '138080.50', creditLimit: '20000.00', creditUsed: '0.00', availableCredit: '20000.00', creditExpiresOn: '2028-09-10', creditActive: true, accountVersion: 7, pendingRechargeCount: 1,
|
||||
status: 'ACTIVE', lastChangedAt: '2026-08-13 10:00:00',
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ describe('FinanceCustomerAccountsView delivery', () => {
|
||||
vi.mocked(listFinanceRechargeRequests).mockReset().mockResolvedValue({ records: [{ requestId: '91', requestNo: 'RC-91', amount: '500.00', paymentMethod: 'WECHAT', voucherFileId: '81', voucherFileName: 'receipt.png', status: 'PENDING', version: 3, submittedByName: '客户A', reviewedByName: '', reviewRemark: '', createdAt: '2026-09-04T10:00:00', reviewedAt: '' }], total: 1, pages: 1 })
|
||||
vi.mocked(reviewFinanceRechargeRequest).mockReset().mockResolvedValue({ requestId: '91', requestNo: 'RC-91', amount: '500.00', paymentMethod: 'WECHAT', voucherFileId: '81', voucherFileName: 'receipt.png', status: 'APPROVED', version: 4, submittedByName: '客户A', reviewedByName: '财务A', reviewRemark: '', createdAt: '2026-09-04T10:00:00', reviewedAt: '2026-09-05T10:00:00' })
|
||||
vi.mocked(adjustFinanceAccount).mockReset().mockResolvedValue({ ...accountFixture, actualBalance: '127080.50', availableBalance: '118580.50', accountVersion: 8 })
|
||||
vi.mocked(updateFinanceCreditLimit).mockReset().mockResolvedValue({ ledgerId: '102', businessNo: 'AC-102', changeType: 'CREDIT_LIMIT', amount: '20000.00', actualBalanceAfter: '126580.50', frozenBalanceAfter: '8500.00', availableBalanceAfter: '138080.50', creditLimitAfter: '20000.00', creditUsedAfter: '0.00', availableCreditAfter: '20000.00', sourceType: 'CREDIT_LIMIT_ADJUSTMENT', sourceNo: '', occurredAt: '', occurredDate: '2026-09-05', paymentMethod: '', referenceNo: '', operatorName: '财务A', remark: '', createdAt: '2026-09-05T10:00:00' })
|
||||
vi.mocked(updateFinanceCreditLimit).mockReset().mockResolvedValue({ ledgerId: '102', businessNo: 'AC-102', changeType: 'CREDIT_LIMIT', amount: '20000.00', actualBalanceAfter: '126580.50', frozenBalanceAfter: '8500.00', availableBalanceAfter: '138080.50', creditLimitAfter: '20000.00', creditUsedAfter: '0.00', availableCreditAfter: '20000.00', creditExpiresOnAfter: '2028-09-10', sourceType: 'CREDIT_LIMIT_ADJUSTMENT', sourceNo: '', occurredAt: '', occurredDate: '2026-09-05', paymentMethod: '', referenceNo: '', operatorName: '财务A', remark: '', createdAt: '2026-09-05T10:00:00' })
|
||||
vi.mocked(downloadFinanceRechargeVoucher).mockReset().mockResolvedValue(new Blob(['image'], { type: 'image/png' }))
|
||||
vi.mocked(listPlatformTenants).mockReset().mockResolvedValue({ records: [{ id: '10001', tenantName: '客户A', status: 'ACTIVE' } as PlatformTenantResource], page: 1, size: 200, total: 1, pages: 1 })
|
||||
vi.mocked(createPlatformExportTask).mockReset().mockResolvedValue({ id: 'exp-1', taskNo: 'EXP-1', taskName: '客户账户', status: 'PENDING' } as never)
|
||||
@@ -179,7 +179,7 @@ describe('FinanceCustomerAccountsView delivery', () => {
|
||||
expect(metrics.text()).toContain('实际余额¥126580.50CNY')
|
||||
expect(metrics.text()).toContain('冻结金额¥8500.00CNY')
|
||||
expect(metrics.text()).toContain('总可用余额¥138080.50CNY')
|
||||
expect(metrics.text()).toContain('授信额度¥20000.00CNY')
|
||||
expect(metrics.text()).toContain('授信额度¥20000.002028-09-10 到期')
|
||||
expect(metrics.text()).toContain('已用授信¥0.00CNY')
|
||||
expect(metrics.text()).toContain('可用授信¥20000.00CNY')
|
||||
for (const auxiliary of ['待开账金额', '本期已收', '总成本', '已付成本', '已收客户款', '客户线下支付']) expect(metrics.text()).not.toContain(auxiliary)
|
||||
|
||||
@@ -58,7 +58,7 @@ const adjustmentAccount = ref<FinanceAccountSummary | null>(null)
|
||||
const adjustmentForm = reactive({ type: 'RECHARGE' as 'RECHARGE' | 'DEDUCTION', amount: '', occurredDate: '', paymentMethod: 'BANK_TRANSFER', referenceNo: '', remark: '' })
|
||||
const creditLimitOpen = ref(false)
|
||||
const creditLimitSaving = ref(false)
|
||||
const creditLimitForm = reactive({ amount: '', remark: '' })
|
||||
const creditLimitForm = reactive({ amount: '', expiresOn: '', remark: '' })
|
||||
const reviewOpen = ref(false)
|
||||
const reviewSaving = ref(false)
|
||||
const reviewRequest = ref<FinanceRechargeRequest | null>(null)
|
||||
@@ -90,6 +90,7 @@ const columns: DataTableColumn[] = [
|
||||
{ key: 'frozenBalance', label: '冻结金额(CNY)', width: 150, slotName: 'frozenBalance' },
|
||||
{ key: 'availableBalance', label: '总可用余额(CNY)', width: 155, slotName: 'availableBalance' },
|
||||
{ key: 'creditLimit', label: '授信额度(CNY)', width: 155, slotName: 'creditLimit' },
|
||||
{ key: 'creditExpiresOn', label: '授信有效期', width: 140, slotName: 'creditExpiresOn' },
|
||||
{ key: 'creditUsed', label: '已用授信(CNY)', width: 150, slotName: 'creditUsed' },
|
||||
{ key: 'pendingRechargeCount', label: '待审核充值', width: 120, slotName: 'pendingRechargeCount' },
|
||||
{ key: 'status', label: '客户状态', width: 110, slotName: 'status' },
|
||||
@@ -216,6 +217,15 @@ function today() {
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${date.getFullYear()}-${month}-${day}`
|
||||
}
|
||||
function disableCreditExpiry(date: Date) {
|
||||
const boundary = new Date()
|
||||
boundary.setHours(0, 0, 0, 0)
|
||||
return date.getTime() <= boundary.getTime()
|
||||
}
|
||||
function creditExpiryLabel(account: FinanceAccountSummary) {
|
||||
if (!account.creditExpiresOn) return '未设置'
|
||||
return account.creditActive ? `${account.creditExpiresOn} 到期` : `${account.creditExpiresOn} 已到期`
|
||||
}
|
||||
function paymentMethodLabel(value: unknown) {
|
||||
return ({ BANK_TRANSFER: '银行转账', WECHAT: '微信', ALIPAY: '支付宝', CASH: '现金', OTHER: '其他' } as Record<string, string>)[String(value)] || '其他'
|
||||
}
|
||||
@@ -247,7 +257,11 @@ async function submitAdjustment() {
|
||||
}
|
||||
function openCreditLimit(account: FinanceAccountSummary) {
|
||||
adjustmentAccount.value = account
|
||||
Object.assign(creditLimitForm, { amount: Number(account.creditLimit || 0).toFixed(2), remark: '' })
|
||||
Object.assign(creditLimitForm, {
|
||||
amount: Number(account.creditLimit || 0).toFixed(2),
|
||||
expiresOn: account.creditActive ? account.creditExpiresOn || '' : '',
|
||||
remark: '',
|
||||
})
|
||||
creditLimitOpen.value = true
|
||||
}
|
||||
function handleAccountMore(command: string, account: FinanceAccountSummary) {
|
||||
@@ -262,10 +276,15 @@ async function submitCreditLimit() {
|
||||
ElMessage.warning('授信额度必须是大于或等于 0 且最多两位小数的金额')
|
||||
return
|
||||
}
|
||||
if (!creditLimitForm.expiresOn || creditLimitForm.expiresOn <= today()) {
|
||||
ElMessage.warning('授信到期日必须晚于今天')
|
||||
return
|
||||
}
|
||||
creditLimitSaving.value = true
|
||||
try {
|
||||
await updateFinanceCreditLimit(adjustmentAccount.value.customerId, {
|
||||
creditLimit: amount.toFixed(2), remark: creditLimitForm.remark.trim() || undefined,
|
||||
creditLimit: amount.toFixed(2), creditExpiresOn: creditLimitForm.expiresOn,
|
||||
remark: creditLimitForm.remark.trim() || undefined,
|
||||
expectedAccountVersion: adjustmentAccount.value.accountVersion,
|
||||
})
|
||||
creditLimitOpen.value = false
|
||||
@@ -408,7 +427,7 @@ onBeforeUnmount(clearVoucherPreview)
|
||||
<template #customerName="{ row }">{{ customerName(row.customerId, row.customerName) }}</template>
|
||||
<template #customerCode="{ row }">{{ customerField(row.customerCode) }}</template><template #contactName="{ row }">{{ customerField(row.contactName) }}</template><template #contactPhone="{ row }">{{ customerField(row.contactPhone) }}</template>
|
||||
<template #lastChangedAt="{ row }">{{ formatLastChangedAt(row.lastChangedAt) }}</template>
|
||||
<template #actualBalance="{ row }"><strong class="balance-value is-actual">{{ money(row.actualBalance) }}</strong></template><template #frozenBalance="{ row }"><span class="balance-value is-frozen">{{ money(row.frozenBalance) }}</span></template><template #availableBalance="{ row }"><strong class="balance-value">{{ money(row.availableBalance) }}</strong></template><template #creditLimit="{ row }">{{ money(row.creditLimit) }}</template><template #creditUsed="{ row }"><span class="balance-value is-frozen">{{ money(row.creditUsed) }}</span></template>
|
||||
<template #actualBalance="{ row }"><strong class="balance-value is-actual" :class="{ 'is-negative': Number(row.actualBalance) < 0 }">{{ money(row.actualBalance) }}</strong></template><template #frozenBalance="{ row }"><span class="balance-value is-frozen">{{ money(row.frozenBalance) }}</span></template><template #availableBalance="{ row }"><strong class="balance-value">{{ money(row.availableBalance) }}</strong></template><template #creditLimit="{ row }">{{ money(row.creditLimit) }}</template><template #creditExpiresOn="{ row }"><span :class="{ 'credit-expired': row.creditExpiresOn && !row.creditActive }">{{ creditExpiryLabel(row as FinanceAccountSummary) }}</span></template><template #creditUsed="{ row }"><span class="balance-value is-frozen">{{ money(row.creditUsed) }}</span></template>
|
||||
<template #pendingRechargeCount="{ row }"><el-tag v-if="row.pendingRechargeCount" type="warning">{{ row.pendingRechargeCount }} 笔</el-tag><span v-else>—</span></template>
|
||||
<template #status="{ row }"><el-tag :type="row.status === 'ACTIVE' ? 'success' : 'info'">{{ row.status === 'ACTIVE' ? '正常' : '停用' }}</el-tag></template>
|
||||
<template #pagination-left><span>共 {{ total }} 个客户账户</span></template>
|
||||
@@ -419,7 +438,7 @@ onBeforeUnmount(clearVoucherPreview)
|
||||
<section class="surface-panel finance-account-detail__surface">
|
||||
<DetailPageLayout class="finance-account-detail__layout" :aria-label="editing ? '维护客户账期' : '客户账户详情'">
|
||||
<template #header><div class="detail-heading"><div class="detail-title"><h2>{{ editing ? '维护客户账期' : '客户账户详情' }}</h2><p v-if="selected">{{ customerName(selected.customerId, selected.customerName) }} · CNY 资金账户</p></div><div v-if="selected && !editing" class="detail-actions"><el-button v-if="canAdjust" type="primary" plain @click="openAdjustment(selected, 'RECHARGE')"><PlusCircle :size="16" />手工充值</el-button><el-button v-if="canAdjust" type="danger" plain @click="openAdjustment(selected, 'DEDUCTION')"><MinusCircle :size="16" />手工扣款</el-button><el-button v-if="canAdjust" plain @click="openCreditLimit(selected)"><CreditCard :size="16" />设置授信</el-button></div><el-button class="detail-back" @click="closeDetail"><ArrowLeft :size="16" />返回客户账户</el-button></div></template>
|
||||
<template v-if="selected && !ledgerError" #summary><section class="account-metrics" aria-label="客户资金账户余额"><div class="account-metric is-actual"><span>实际余额</span><strong>{{ money(selected.actualBalance) }}</strong><small>CNY</small></div><div class="account-metric is-frozen"><span>冻结金额</span><strong>{{ money(selected.frozenBalance) }}</strong><small>CNY</small></div><div class="account-metric is-available"><span>总可用余额</span><strong>{{ money(selected.availableBalance) }}</strong><small>CNY</small></div><div class="account-metric"><span>授信额度</span><strong>{{ money(selected.creditLimit) }}</strong><small>CNY</small></div><div class="account-metric is-frozen"><span>已用授信</span><strong>{{ money(selected.creditUsed) }}</strong><small>CNY</small></div><div class="account-metric"><span>可用授信</span><strong>{{ money(selected.availableCredit) }}</strong><small>CNY</small></div></section></template>
|
||||
<template v-if="selected && !ledgerError" #summary><section class="account-metrics" aria-label="客户资金账户余额"><div class="account-metric is-actual"><span>实际余额</span><strong :class="{ 'is-negative': Number(selected.actualBalance) < 0 }">{{ money(selected.actualBalance) }}</strong><small>CNY</small></div><div class="account-metric is-frozen"><span>冻结金额</span><strong>{{ money(selected.frozenBalance) }}</strong><small>CNY</small></div><div class="account-metric is-available"><span>总可用余额</span><strong>{{ money(selected.availableBalance) }}</strong><small>CNY</small></div><div class="account-metric"><span>授信额度</span><strong>{{ money(selected.creditLimit) }}</strong><small>{{ creditExpiryLabel(selected) }}</small></div><div class="account-metric is-frozen"><span>已用授信</span><strong>{{ money(selected.creditUsed) }}</strong><small>CNY</small></div><div class="account-metric"><span>可用授信</span><strong>{{ money(selected.availableCredit) }}</strong><small>CNY</small></div></section></template>
|
||||
<LoadErrorState v-if="ledgerError" :title="editing ? '客户账期加载失败' : '账户流水加载失败'" :message="ledgerError.message" :loading="ledgerLoading" @retry="retryDrawer"><template #details><ApiErrorMeta :details="ledgerError" /></template></LoadErrorState>
|
||||
<template v-else-if="selected">
|
||||
<FormSection class="customer-profile-section" title="客户信息" :bordered="false"><el-descriptions :column="3"><el-descriptions-item label="客户编号">{{ customerField(selected.customerCode) }}</el-descriptions-item><el-descriptions-item label="联系人">{{ customerField(selected.contactName) }}</el-descriptions-item><el-descriptions-item label="联系电话">{{ customerField(selected.contactPhone) }}</el-descriptions-item></el-descriptions></FormSection>
|
||||
@@ -448,8 +467,8 @@ onBeforeUnmount(clearVoucherPreview)
|
||||
<el-form label-position="top" class="wallet-form"><div class="wallet-form__grid"><el-form-item label="客户名称"><el-input :model-value="adjustmentAccount ? customerName(adjustmentAccount.customerId, adjustmentAccount.customerName) : ''" readonly /></el-form-item><el-form-item label="当前实际余额"><el-input :model-value="money(adjustmentAccount?.actualBalance)" readonly /></el-form-item><el-form-item v-if="adjustmentForm.type === 'DEDUCTION'" label="当前可用余额"><el-input :model-value="money(adjustmentAccount?.availableBalance)" readonly /></el-form-item><el-form-item :label="adjustmentForm.type === 'RECHARGE' ? '充值金额' : '扣款金额'" required><el-input v-model="adjustmentForm.amount" inputmode="decimal" placeholder="请输入大于 0 的金额" /></el-form-item><el-form-item :label="adjustmentForm.type === 'RECHARGE' ? '到账日期' : '扣款日期'" required><el-date-picker v-model="adjustmentForm.occurredDate" type="date" value-format="YYYY-MM-DD" /></el-form-item><el-form-item :label="adjustmentForm.type === 'RECHARGE' ? '充值方式' : '扣款方式'" required><el-select v-model="adjustmentForm.paymentMethod"><el-option label="银行转账" value="BANK_TRANSFER" /><el-option label="现金" value="CASH" /><el-option label="其他" value="OTHER" /></el-select></el-form-item><el-form-item label="关联单据"><el-input v-model="adjustmentForm.referenceNo" maxlength="128" placeholder="请输入关联业务单号(选填)" /></el-form-item></div><el-form-item :label="adjustmentForm.type === 'RECHARGE' ? '充值备注' : '扣款备注'"><el-input v-model="adjustmentForm.remark" type="textarea" :rows="3" maxlength="1000" show-word-limit placeholder="请输入备注(选填)" /></el-form-item></el-form>
|
||||
</AppDialog>
|
||||
<AppDialog v-model="creditLimitOpen" title="设置授信额度" width="min(560px, calc(100vw - 28px))" confirm-text="保存授信额度" :confirm-loading="creditLimitSaving" @confirm="submitCreditLimit">
|
||||
<el-alert title="授信额度不能低于已用授信与冻结授信之和;修改后立即影响客户总可用余额。" type="info" :closable="false" />
|
||||
<el-form label-position="top" class="wallet-form"><el-form-item label="客户名称"><el-input :model-value="adjustmentAccount ? customerName(adjustmentAccount.customerId, adjustmentAccount.customerName) : ''" readonly /></el-form-item><el-form-item label="当前已用授信"><el-input :model-value="money(adjustmentAccount?.creditUsed)" readonly /></el-form-item><el-form-item label="授信额度" required><el-input v-model="creditLimitForm.amount" inputmode="decimal" placeholder="请输入大于或等于 0 的金额" /></el-form-item><el-form-item label="调整说明"><el-input v-model="creditLimitForm.remark" type="textarea" :rows="3" maxlength="500" show-word-limit placeholder="请输入授信调整说明(选填)" /></el-form-item></el-form>
|
||||
<el-alert title="授信到期日当天仍有效,次日授信失效;届时负数实际余额将阻止客户提交新订单。" type="warning" :closable="false" />
|
||||
<el-form label-position="top" class="wallet-form"><el-form-item label="客户名称"><el-input :model-value="adjustmentAccount ? customerName(adjustmentAccount.customerId, adjustmentAccount.customerName) : ''" readonly /></el-form-item><el-form-item label="当前已用授信"><el-input :model-value="money(adjustmentAccount?.creditUsed)" readonly /></el-form-item><el-form-item label="授信额度" required><el-input v-model="creditLimitForm.amount" inputmode="decimal" placeholder="请输入大于或等于 0 的金额" /></el-form-item><el-form-item label="授信到期日" required><el-date-picker v-model="creditLimitForm.expiresOn" type="date" value-format="YYYY-MM-DD" :disabled-date="disableCreditExpiry" placeholder="请选择今天以后的日期" /></el-form-item><el-form-item label="调整说明"><el-input v-model="creditLimitForm.remark" type="textarea" :rows="3" maxlength="500" show-word-limit placeholder="请输入授信调整说明(选填)" /></el-form-item></el-form>
|
||||
</AppDialog>
|
||||
<AppDialog v-model="reviewOpen" title="审核充值申请" width="min(620px, calc(100vw - 28px))" confirm-text="确认审核" :confirm-loading="reviewSaving" @confirm="submitReview">
|
||||
<el-descriptions v-if="reviewRequest" :column="2" border><el-descriptions-item label="申请单号">{{ reviewRequest.requestNo }}</el-descriptions-item><el-descriptions-item label="充值金额">{{ money(reviewRequest.amount) }}</el-descriptions-item><el-descriptions-item label="付款方式">{{ paymentMethodLabel(reviewRequest.paymentMethod) }}</el-descriptions-item><el-descriptions-item label="付款凭证">{{ reviewRequest.voucherFileName || '—' }}</el-descriptions-item></el-descriptions>
|
||||
@@ -570,6 +589,12 @@ onBeforeUnmount(clearVoucherPreview)
|
||||
color: var(--el-color-warning);
|
||||
}
|
||||
|
||||
.balance-value.is-negative,
|
||||
.account-metric strong.is-negative,
|
||||
.credit-expired {
|
||||
color: var(--el-color-danger);
|
||||
}
|
||||
|
||||
.voucher-preview {
|
||||
display: grid;
|
||||
min-height: 320px;
|
||||
|
||||
@@ -202,6 +202,8 @@ export interface FinanceAccountSummary {
|
||||
creditLimit: string
|
||||
creditUsed: string
|
||||
availableCredit: string
|
||||
creditExpiresOn: string | null
|
||||
creditActive: boolean
|
||||
accountVersion: number
|
||||
pendingRechargeCount: number
|
||||
}
|
||||
@@ -248,6 +250,7 @@ export interface FinanceLedgerEntry {
|
||||
creditLimitAfter?: string | null
|
||||
creditUsedAfter?: string | null
|
||||
availableCreditAfter?: string | null
|
||||
creditExpiresOnAfter?: string | null
|
||||
sourceType?: string | null
|
||||
sourceNo?: string | null
|
||||
occurredDate?: string | null
|
||||
|
||||
@@ -10,7 +10,7 @@ describe('overseas warehouse 1920px layout acceptance', () => {
|
||||
const widths: Record<string, number> = {
|
||||
'LastMileOrdersView.vue': 178,
|
||||
'InboundOrdersView.vue': 300,
|
||||
'DispatchesView.vue': 270,
|
||||
'DispatchesView.vue': 380,
|
||||
'OutboundsView.vue': 180,
|
||||
'DeliveryOrdersView.vue': 300,
|
||||
'RelabelOrdersView.vue': 180,
|
||||
|
||||
Reference in New Issue
Block a user