优化
This commit is contained in:
@@ -9,6 +9,8 @@ import AdminPage from '@/shared/components/AdminPage.vue'
|
||||
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
||||
import AppDialog from '@/shared/components/AppDialog.vue'
|
||||
import DataTable, { type DataTableColumn } from '@/shared/components/DataTable.vue'
|
||||
import DetailPageLayout from '@/shared/components/DetailPageLayout.vue'
|
||||
import FormSection from '@/shared/components/FormSection.vue'
|
||||
import ListPageLayout from '@/shared/components/ListPageLayout.vue'
|
||||
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
|
||||
import QueryToolbar, { type QueryField, type QueryModel } from '@/shared/components/QueryToolbar.vue'
|
||||
@@ -114,6 +116,30 @@ const columns: DataTableColumn[] = [
|
||||
{ key: 'updatedBy', label: '更新人', width: 130, slotName: 'updatedBy' },
|
||||
{ key: 'updatedAt', label: '更新时间', minWidth: 170, slotName: 'updatedAt' },
|
||||
]
|
||||
const detailLineColumns: DataTableColumn[] = [
|
||||
{ key: 'inboundCode', label: '入仓号', minWidth: 150, slotName: 'inboundCode' },
|
||||
{ key: 'containerNo', label: '柜次号', minWidth: 150, slotName: 'containerNo' },
|
||||
{ key: 'businessDate', label: '业务发生日', width: 130, slotName: 'businessDate' },
|
||||
{ key: 'feeItemName', label: '费用项目', minWidth: 160 },
|
||||
{ key: 'quantity', label: '计费量', width: 110, slotName: 'quantity' },
|
||||
{ key: 'unitPrice', label: '单价(CNY)', width: 120, slotName: 'unitPrice' },
|
||||
{ key: 'sourceAmount', label: '来源金额(CNY)', width: 150 },
|
||||
{ key: 'amount', label: '账单金额(CNY)', width: 150 },
|
||||
{ key: 'adjustmentReason', label: '调整说明', minWidth: 190 },
|
||||
{ key: 'billNo', label: '对账单号', minWidth: 190, slotName: 'billNo' },
|
||||
{ key: 'periodStart', label: '结算起始日', width: 130, slotName: 'periodStart' },
|
||||
{ key: 'periodEnd', label: '结算结束日', width: 130, slotName: 'periodEnd' },
|
||||
]
|
||||
const paymentColumns: DataTableColumn[] = [
|
||||
{ key: 'customerName', label: '客户', minWidth: 200, slotName: 'customerName' },
|
||||
{ key: 'transactionDate', label: '支付日期', width: 130, slotName: 'transactionDate' },
|
||||
{ key: 'amount', label: '支付金额(CNY)', width: 150 },
|
||||
{ key: 'method', label: '支付方式', width: 130, slotName: 'method' },
|
||||
{ key: 'referenceNo', label: '支付流水号', minWidth: 180 },
|
||||
{ key: 'createdBy', label: '登记人', minWidth: 140, slotName: 'createdBy' },
|
||||
{ key: 'occurredAt', label: '登记时间', width: 170, slotName: 'occurredAt' },
|
||||
{ key: 'remark', label: '备注', minWidth: 200 },
|
||||
]
|
||||
|
||||
const customerNameById = computed(() => new Map(customers.value.map((item) => [String(item.id), item.tenantName.trim()])))
|
||||
|
||||
@@ -419,47 +445,67 @@ onMounted(() => { if (canListCustomers.value) void loadCustomers(); void load()
|
||||
</section>
|
||||
|
||||
<section class="surface-panel statement-detail-panel" aria-label="所选对账单联动明细">
|
||||
<header class="statement-detail-header">
|
||||
<div><strong>已选择 {{ selectedId ? 1 : 0 }} 项</strong><span v-if="detail">{{ detail.billNo }} · {{ customerDisplayName(detail.customerId, detail.customerName) }}</span></div>
|
||||
<el-button v-if="selectedId" link @click="clearSelection">清空</el-button>
|
||||
</header>
|
||||
<el-tabs v-model="activeDetailTab">
|
||||
<el-tab-pane label="账单明细表" name="lines">
|
||||
<LoadErrorState v-if="detailError" title="明细加载失败" :message="detailError.message" :loading="detailLoading" @retry="retrySelectedDetail"><template #details><ApiErrorMeta :details="detailError" /></template></LoadErrorState>
|
||||
<el-skeleton v-else-if="detailLoading" :rows="4" animated />
|
||||
<template v-else-if="detail">
|
||||
<el-table :data="detail.lines" size="small" border stripe empty-text="该对账单暂无账单明细">
|
||||
<el-table-column label="入仓号" min-width="140"><template #default="{ row }">{{ lineSourceCache.get(row.sourceFeeId)?.inboundCode || row.inboundId || '—' }}</template></el-table-column>
|
||||
<el-table-column label="柜次号" min-width="140"><template #default="{ row }">{{ lineSourceCache.get(row.sourceFeeId)?.containerNo || (row.containerId ? `柜次ID ${row.containerId}` : '—') }}</template></el-table-column>
|
||||
<el-table-column label="业务发生日" width="130"><template #default="{ row }">{{ lineSourceCache.get(row.sourceFeeId)?.businessDate || '—' }}</template></el-table-column>
|
||||
<el-table-column prop="feeItemName" label="费用项目" min-width="150" />
|
||||
<el-table-column label="计费量" width="110"><template #default="{ row }">{{ lineSourceCache.get(row.sourceFeeId)?.quantity ?? '—' }}</template></el-table-column>
|
||||
<el-table-column label="单价(CNY)" width="120"><template #default="{ row }">{{ lineSourceCache.get(row.sourceFeeId)?.unitPrice ?? '—' }}</template></el-table-column>
|
||||
<el-table-column prop="sourceAmount" label="来源金额(CNY)" width="150" />
|
||||
<el-table-column prop="amount" label="账单金额(CNY)" width="150" />
|
||||
<el-table-column prop="adjustmentReason" label="调整说明" min-width="180" />
|
||||
<el-table-column label="对账单号" min-width="180"><template #default>{{ detail.billNo }}</template></el-table-column>
|
||||
<el-table-column label="结算起始日" width="130"><template #default>{{ detail.periodStart || '—' }}</template></el-table-column>
|
||||
<el-table-column label="结算结束日" width="130"><template #default>{{ detail.periodEnd || '—' }}</template></el-table-column>
|
||||
</el-table>
|
||||
<el-collapse v-if="detail.revisions.length" class="revision-collapse"><el-collapse-item title="调整记录" name="revisions"><el-table :data="detail.revisions" size="small" border stripe><el-table-column prop="revisionNo" label="修订" width="72" /><el-table-column label="动作" width="130"><template #default="{ row }">{{ revisionActionLabel(row.action) }}</template></el-table-column><el-table-column prop="beforeAmount" label="调整前" /><el-table-column prop="afterAmount" label="调整后" /><el-table-column prop="reason" label="原因" /><el-table-column label="发生时间" min-width="170"><template #default="{ row }">{{ formatDateTime(row.occurredAt) }}</template></el-table-column></el-table></el-collapse-item></el-collapse>
|
||||
</template>
|
||||
<el-empty v-else description="请在主表中选择一张对账单" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="账单支付历史表" name="payments">
|
||||
<el-table v-if="detail" :data="detail.paymentHistory" size="small" border stripe empty-text="该对账单暂无有效收款记录">
|
||||
<el-table-column label="客户" min-width="170"><template #default>{{ customerDisplayName(detail.customerId, detail.customerName) }}</template></el-table-column>
|
||||
<el-table-column label="支付日期" min-width="130"><template #default="{ row }">{{ formatDateTime(row.transactionDate).slice(0, 10) }}</template></el-table-column>
|
||||
<el-table-column prop="amount" label="支付金额(CNY)" min-width="150" />
|
||||
<el-table-column label="支付方式" min-width="130"><template #default="{ row }">{{ paymentMethodLabel(row.method) }}</template></el-table-column>
|
||||
<el-table-column prop="referenceNo" label="支付流水号" min-width="170" />
|
||||
<el-table-column label="登记人" min-width="130"><template #default="{ row }">{{ operatorDisplayName(row.createdBy) }}</template></el-table-column>
|
||||
<el-table-column label="登记时间" min-width="170"><template #default="{ row }">{{ formatDateTime(row.occurredAt) }}</template></el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="180" />
|
||||
</el-table>
|
||||
<el-empty v-else description="请在主表中选择一张对账单" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<DetailPageLayout aria-label="客户对账单详情">
|
||||
<template #header>
|
||||
<header class="statement-detail-header">
|
||||
<div class="statement-detail-context"><span>所选对账单</span><strong>{{ selectedId ? '已选择 1 项' : '尚未选择' }}</strong></div>
|
||||
<el-button v-if="selectedId" link @click="clearSelection">清空</el-button>
|
||||
</header>
|
||||
</template>
|
||||
<template v-if="detail" #summary>
|
||||
<div class="statement-detail-head">
|
||||
<div><span>客户对账单</span><h2>{{ detail.billNo }}</h2><p>{{ customerDisplayName(detail.customerId, detail.customerName) }} · {{ detail.periodStart || '—' }} 至 {{ detail.periodEnd || '—' }}</p></div>
|
||||
<el-tag :type="financeStatusTone(detail.status)">{{ financeStatusLabel(detail.status) }}</el-tag>
|
||||
</div>
|
||||
<FormSection class="statement-summary-section" title="对账单概要" description="核对账期、金额和结清状态后查看下方业务明细。" aria-label="所选对账单摘要">
|
||||
<el-descriptions :column="3" border>
|
||||
<el-descriptions-item label="总应收金额">{{ detail.totalAmount.toFixed(2) }} CNY</el-descriptions-item>
|
||||
<el-descriptions-item label="已收金额">{{ detail.paidAmount.toFixed(2) }} CNY</el-descriptions-item>
|
||||
<el-descriptions-item label="未收金额"><strong class="statement-outstanding">{{ detail.outstandingAmount.toFixed(2) }} CNY</strong></el-descriptions-item>
|
||||
<el-descriptions-item label="账期">{{ detail.creditDays }} 天</el-descriptions-item>
|
||||
<el-descriptions-item label="货币">{{ detail.currency }}</el-descriptions-item>
|
||||
<el-descriptions-item label="是否结清">{{ detail.settled ? '是' : '否' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</FormSection>
|
||||
</template>
|
||||
|
||||
<el-tabs v-model="activeDetailTab" class="statement-detail-tabs">
|
||||
<el-tab-pane label="账单明细表" name="lines">
|
||||
<LoadErrorState v-if="detailError" title="明细加载失败" :message="detailError.message" :loading="detailLoading" @retry="retrySelectedDetail"><template #details><ApiErrorMeta :details="detailError" /></template></LoadErrorState>
|
||||
<el-skeleton v-else-if="detailLoading" :rows="4" animated />
|
||||
<FormSection v-else-if="detail" title="账单明细" description="展示当前对账单版本中的业务与计费明细。" :bordered="false">
|
||||
<div class="detail-table-scroll">
|
||||
<DataTable class="statement-detail-table statement-line-table" :data="detail.lines" size="small" border stripe :columns="detailLineColumns" column-settings-mode="hide" row-key="statementLineId" empty-text="该对账单暂无账单明细" aria-label="客户对账单账单明细">
|
||||
<template #inboundCode="{ row }">{{ lineSourceCache.get(row.sourceFeeId)?.inboundCode || row.inboundId || '—' }}</template>
|
||||
<template #containerNo="{ row }">{{ lineSourceCache.get(row.sourceFeeId)?.containerNo || (row.containerId ? `柜次ID ${row.containerId}` : '—') }}</template>
|
||||
<template #businessDate="{ row }">{{ lineSourceCache.get(row.sourceFeeId)?.businessDate || '—' }}</template>
|
||||
<template #quantity="{ row }">{{ lineSourceCache.get(row.sourceFeeId)?.quantity ?? '—' }}</template>
|
||||
<template #unitPrice="{ row }">{{ lineSourceCache.get(row.sourceFeeId)?.unitPrice ?? '—' }}</template>
|
||||
<template #billNo>{{ detail.billNo }}</template>
|
||||
<template #periodStart>{{ detail.periodStart || '—' }}</template>
|
||||
<template #periodEnd>{{ detail.periodEnd || '—' }}</template>
|
||||
</DataTable>
|
||||
</div>
|
||||
<el-collapse v-if="detail.revisions.length" class="revision-collapse"><el-collapse-item title="调整记录" name="revisions"><el-table :data="detail.revisions" size="small" border stripe><el-table-column prop="revisionNo" label="修订" width="72" /><el-table-column label="动作" width="130"><template #default="{ row }">{{ revisionActionLabel(row.action) }}</template></el-table-column><el-table-column prop="beforeAmount" label="调整前" /><el-table-column prop="afterAmount" label="调整后" /><el-table-column prop="reason" label="原因" /><el-table-column label="发生时间" min-width="170"><template #default="{ row }">{{ formatDateTime(row.occurredAt) }}</template></el-table-column></el-table></el-collapse-item></el-collapse>
|
||||
</FormSection>
|
||||
<el-empty v-else description="请在主表中选择一张对账单" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="账单支付历史表" name="payments">
|
||||
<FormSection v-if="detail" title="支付历史" description="只读展示当前对账单关联的有效收款记录。" :bordered="false">
|
||||
<div class="detail-table-scroll">
|
||||
<DataTable class="statement-detail-table statement-payment-table" :data="detail.paymentHistory" size="small" border stripe :columns="paymentColumns" column-settings-mode="hide" row-key="transactionId" empty-text="该对账单暂无有效收款记录" aria-label="客户对账单支付历史">
|
||||
<template #customerName>{{ customerDisplayName(detail.customerId, detail.customerName) }}</template>
|
||||
<template #transactionDate="{ row }">{{ formatDateTime(row.transactionDate).slice(0, 10) }}</template>
|
||||
<template #method="{ row }">{{ paymentMethodLabel(row.method) }}</template>
|
||||
<template #createdBy="{ row }">{{ operatorDisplayName(row.createdBy) }}</template>
|
||||
<template #occurredAt="{ row }">{{ formatDateTime(row.occurredAt) }}</template>
|
||||
</DataTable>
|
||||
</div>
|
||||
</FormSection>
|
||||
<el-empty v-else description="请在主表中选择一张对账单" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</DetailPageLayout>
|
||||
</section>
|
||||
</template>
|
||||
</ListPageLayout>
|
||||
@@ -476,10 +522,25 @@ onMounted(() => { if (canListCustomers.value) void loadCustomers(); void load()
|
||||
<style scoped>
|
||||
.finance-statements-page { min-width: 0; }
|
||||
:deep(.el-select), :deep(.el-date-editor) { width: 100%; }
|
||||
.statement-detail-panel { min-width: 0; padding: 0 16px 16px; }
|
||||
.statement-detail-header { min-height: 48px; display: flex; align-items: center; justify-content: space-between; gap: 12px; border-bottom: 1px solid var(--cc-border-subtle); }
|
||||
.statement-detail-header div { display: flex; align-items: center; gap: 12px; min-width: 0; }
|
||||
.statement-detail-header span { color: var(--cc-text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.statement-detail-panel { min-width: 0; overflow: hidden; }
|
||||
.statement-detail-header { min-height: 56px; display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 0 24px; border-bottom: 1px solid var(--cc-border-subtle); }
|
||||
.statement-detail-context { display: grid; min-width: 0; gap: 3px; }
|
||||
.statement-detail-context span { color: var(--cc-text-secondary); font-size: 11px; }
|
||||
.statement-detail-context strong { overflow: hidden; color: var(--cc-text-primary); font-size: 14px; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.statement-detail-head { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start; gap: 12px; padding: 20px 24px; border-bottom: 1px solid var(--cc-border-subtle); }
|
||||
.statement-detail-head > div > span { color: var(--cc-primary); font-size: 11px; font-weight: 700; }
|
||||
.statement-detail-head h2 { margin: 2px 0 0; font-size: 17px; line-height: 1.4; overflow-wrap: anywhere; }
|
||||
.statement-detail-head p { margin: 5px 0 0; color: var(--cc-text-secondary); font-size: 12px; line-height: 1.6; overflow-wrap: anywhere; }
|
||||
.statement-summary-section { padding: 20px 24px; border-bottom: 1px solid var(--cc-border-subtle); }
|
||||
.statement-outstanding { color: var(--el-color-warning-dark-2); }
|
||||
.statement-detail-tabs { min-width: 0; }
|
||||
.statement-detail-table { min-width: 0; }
|
||||
.detail-table-scroll { min-width: 0; overflow-x: auto; }
|
||||
.statement-detail-tabs :deep(.el-tabs__header) { margin: 0; padding: 0 24px; }
|
||||
.statement-detail-tabs :deep(.el-tabs__content) { min-width: 0; }
|
||||
.statement-detail-tabs :deep(.form-section) { padding: 20px 24px 24px; }
|
||||
.statement-line-table { min-width: 1840px; }
|
||||
.statement-payment-table { min-width: 1300px; }
|
||||
.revision-collapse { margin-top: 12px; }
|
||||
:deep(.is-selected-statement > td.el-table__cell) { background: var(--el-color-primary-light-9) !important; }
|
||||
@media (max-width: 640px) {
|
||||
|
||||
Reference in New Issue
Block a user