优化
This commit is contained in:
@@ -113,7 +113,7 @@ describe('FinanceCustomerAccountsView delivery', () => {
|
||||
expect(page).toContain("{ key: 'deduction', label: '扣款', priority: 200")
|
||||
expect(page).toContain("{ key: 'credit', label: '授信', priority: 100")
|
||||
expect(page).toContain("{ key: 'terms', label: '账期', visible: canUpdateTerms.value")
|
||||
expect(page).toContain('<RowActions :actions="accountActions(row as FinanceAccountSummary)"')
|
||||
expect(page).toContain('<RowActions text-actions :actions="accountActions(row as FinanceAccountSummary)"')
|
||||
})
|
||||
|
||||
it('shows authoritative CNY wallet balances and independently permitted actions', () => {
|
||||
@@ -125,7 +125,7 @@ describe('FinanceCustomerAccountsView delivery', () => {
|
||||
it.each([
|
||||
[[], ['详情'], false],
|
||||
[['platform:finance:customer-terms:view', 'platform:finance:customer-terms:update'], ['详情', '账期'], false],
|
||||
[['platform:finance:customer-account:adjust'], ['详情', '充值', '扣款', ''], true],
|
||||
[['platform:finance:customer-account:adjust'], ['详情', '充值', '扣款', '更多'], true],
|
||||
])('folds account operations only when more than three are available: %j', async (extraPermissions, labels, overflow) => {
|
||||
const wrapper = await mountAccountsView(['platform:finance:customer-account:list', ...extraPermissions])
|
||||
const actions = wrapper.get('.el-table__body-wrapper .density-actions')
|
||||
@@ -133,8 +133,8 @@ describe('FinanceCustomerAccountsView delivery', () => {
|
||||
expect(actions.find('.el-dropdown').exists()).toBe(overflow)
|
||||
if (overflow) {
|
||||
const trigger = actions.get('button[aria-label^="更多客户账户操作"]')
|
||||
expect(trigger.text()).toBe('')
|
||||
expect(trigger.find('svg').exists()).toBe(true)
|
||||
expect(trigger.text()).toBe('更多')
|
||||
expect(trigger.find('svg').exists()).toBe(false)
|
||||
}
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
@@ -436,12 +436,16 @@ onBeforeUnmount(clearVoucherPreview)
|
||||
<AdminPage as="div" class="finance-account-page admin-density" spacing="normal">
|
||||
<el-result v-if="!canView" icon="warning" title="无权访问客户账户" sub-title="请联系管理员开通客户账户查看权限" />
|
||||
<ListPageLayout v-else-if="!detailOpen" aria-label="客户账户">
|
||||
<template #toolbar><QueryToolbar v-model="filters" class="surface-panel" ariaLabel="客户账户筛选" :fields="queryFields" preference-key="finance-customer-accounts-query" @search="search" /></template>
|
||||
<template v-if="stateError" #state><LoadErrorState title="客户账户加载失败" :message="stateError.message" :loading="loading || customersLoading" @retry="customersError ? loadCustomers() : load()"><template #details><ApiErrorMeta :details="stateError" /></template></LoadErrorState></template>
|
||||
<section v-if="!stateError" class="surface-panel table-panel">
|
||||
<DataTable v-model:page="page" v-model:page-size="pageSize" :data="rows" :columns="columns" :loading="loading" :total="total" preference-key="finance-customer-accounts-table" aria-label="客户账户列表" row-key="customerId" empty-text="暂无客户账户" @update:page="load()" @update:page-size="page = 1; load()">
|
||||
|
||||
|
||||
<section><DataTable appearance="integrated" :stripe="false" column-settings-mode="show" :results-visible="!stateError" v-model:page="page" v-model:page-size="pageSize" :data="rows" :columns="columns" :loading="loading" :total="total" preference-key="finance-customer-accounts-table" aria-label="客户账户列表" row-key="customerId" empty-text="暂无客户账户" @update:page="load()" @update:page-size="page = 1; load()">
|
||||
<template #toolbar-start><h1>客户账户</h1></template>
|
||||
<template #filters><QueryToolbar v-model="filters" ariaLabel="客户账户筛选" :fields="queryFields" preference-key="finance-customer-accounts-query" @search="search" /></template>
|
||||
<template #notices><template v-if="stateError"><LoadErrorState title="客户账户加载失败" :message="stateError.message" :loading="loading || customersLoading" @retry="customersError ? loadCustomers() : load()"><template #details><ApiErrorMeta :details="stateError" /></template></LoadErrorState></template></template>
|
||||
|
||||
|
||||
<template #toolbar-actions><el-button v-if="canExport" :loading="accountExportBusy" :disabled="loading || accountExportBusy" @click="exportAccounts"><Download :size="16" />导出Excel</el-button></template>
|
||||
<template #actions="{ row }"><RowActions :actions="accountActions(row as FinanceAccountSummary)" :more-label="`更多客户账户操作 · ${(row as FinanceAccountSummary).customerName || '当前客户'}`" @action="handleAccountAction($event, row as FinanceAccountSummary)" /></template>
|
||||
<template #actions="{ row }"><RowActions text-actions :actions="accountActions(row as FinanceAccountSummary)" :more-label="`更多客户账户操作 · ${(row as FinanceAccountSummary).customerName || '当前客户'}`" @action="handleAccountAction($event, row as FinanceAccountSummary)" /></template>
|
||||
<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>
|
||||
@@ -449,8 +453,7 @@ onBeforeUnmount(clearVoucherPreview)
|
||||
<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>
|
||||
</DataTable>
|
||||
</section>
|
||||
</DataTable></section>
|
||||
</ListPageLayout>
|
||||
<AdminPage v-else as="div" class="finance-account-detail admin-density" spacing="normal">
|
||||
<section class="surface-panel finance-account-detail__surface">
|
||||
|
||||
@@ -101,7 +101,7 @@ describe('FinanceCustomerArapView G2 operations', () => {
|
||||
})
|
||||
|
||||
it.each([
|
||||
['DRAFT', ['详情', '编辑', '提交审核', ''], true],
|
||||
['DRAFT', ['详情', '编辑', '提交审核', '更多'], true],
|
||||
['APPROVED', ['详情', '调整', '作废'], false],
|
||||
['PENDING', ['详情', '作废'], false],
|
||||
])('keeps fee-source actions reachable at the three-action boundary: %s', async (reviewStatus, labels, overflow) => {
|
||||
@@ -114,7 +114,7 @@ describe('FinanceCustomerArapView G2 operations', () => {
|
||||
expect(actions.find('.el-dropdown').exists()).toBe(overflow)
|
||||
if (overflow) {
|
||||
const trigger = actions.get('button[title^="更多费用来源操作"]')
|
||||
expect(trigger.text()).toBe('')
|
||||
expect(trigger.text()).toBe('更多')
|
||||
await trigger.trigger('click')
|
||||
await flushPromises()
|
||||
const item = [...document.querySelectorAll<HTMLElement>('.el-dropdown-menu__item')].find(item => item.textContent?.trim() === '作废')
|
||||
@@ -279,7 +279,7 @@ describe('FinanceCustomerArapView G2 operations', () => {
|
||||
expect(page).toContain('!isUpstreamSource(detail)')
|
||||
expect(page).toContain("['INBOUND_FEE', 'CONTAINER_FEE'].includes(type)")
|
||||
expect(page).toContain("=== 'LASTLEG'")
|
||||
expect(page).toContain('<RowActions v-if="row.feeSourceId" :actions="sourceActions(row as ArapRow)"')
|
||||
expect(page).toContain('<RowActions text-actions v-if="row.feeSourceId" :actions="sourceActions(row as ArapRow)"')
|
||||
})
|
||||
|
||||
it('resolves blank detail customer names and keeps source labels business-facing', async () => {
|
||||
|
||||
@@ -400,12 +400,16 @@ watch(() => [route.name, String(route.params.feeSourceId || '')], ([name, feeSou
|
||||
<OperationErrorToast :details="reviewError || mutationError" />
|
||||
<el-result v-if="!canView" icon="warning" title="无权访问客户应收应付" sub-title="请联系管理员开通账单查看权限" />
|
||||
<ListPageLayout v-else-if="!detailOpen" aria-label="客户应收应付">
|
||||
<template #toolbar><QueryToolbar v-model="filters" class="surface-panel" ariaLabel="客户应收应付筛选" :fields="queryFields" preference-key="finance-customer-arap-query" @search="search" /></template>
|
||||
<template v-if="stateError" #state><LoadErrorState title="客户应收应付加载失败" :message="stateError.message" :loading="customersLoading || loading" @retry="customersError ? loadCustomers() : load()"><template #details><ApiErrorMeta :details="stateError" /></template></LoadErrorState></template>
|
||||
<section v-if="!customersError && !loadError" class="surface-panel table-panel">
|
||||
<DataTable v-model:page="page" v-model:page-size="pageSize" :data="rows" :columns="columns" :loading="loading" :total="total" max-height="calc(100vh - 300px)" preference-key="finance-customer-arap-table" aria-label="客户应收应付列表" row-key="id" empty-text="暂无客户应收应付数据" @selection-change="onSelectionChange" @update:page="load()" @update:page-size="page = 1; load()">
|
||||
|
||||
|
||||
<section><DataTable appearance="integrated" :stripe="false" column-settings-mode="show" :results-visible="!customersError && !loadError" v-model:page="page" v-model:page-size="pageSize" :data="rows" :columns="columns" :loading="loading" :total="total" max-height="calc(100vh - 300px)" preference-key="finance-customer-arap-table" aria-label="客户应收应付列表" row-key="id" empty-text="暂无客户应收应付数据" @selection-change="onSelectionChange" @update:page="load()" @update:page-size="page = 1; load()">
|
||||
<template #toolbar-start><h1>客户应收应付</h1></template>
|
||||
<template #filters><QueryToolbar v-model="filters" ariaLabel="客户应收应付筛选" :fields="queryFields" preference-key="finance-customer-arap-query" @search="search" /></template>
|
||||
<template #notices><template v-if="stateError"><LoadErrorState title="客户应收应付加载失败" :message="stateError.message" :loading="customersLoading || loading" @retry="customersError ? loadCustomers() : load()"><template #details><ApiErrorMeta :details="stateError" /></template></LoadErrorState></template></template>
|
||||
|
||||
|
||||
<template #toolbar-actions><div class="generation-actions"><el-button v-if="canExport" :loading="exportBusy" :disabled="loading || exportBusy" @click="exportFeeSources"><Download :size="16" />导出Excel</el-button><el-button v-if="canCreateStatement" type="primary" :disabled="!selectedRows.length" @click="continueSelectedGeneration"><FilePlus2 :size="16" />生成所选对账单</el-button></div></template>
|
||||
<template #actions="{ row }"><RowActions v-if="row.feeSourceId" :actions="sourceActions(row as ArapRow)" :more-label="`更多费用来源操作 · ${(row as ArapRow).documentNo || '当前费用'}`" @action="handleSourceAction($event, row as ArapRow)" /><span v-else class="muted-action">手工成本</span></template>
|
||||
<template #actions="{ row }"><RowActions text-actions v-if="row.feeSourceId" :actions="sourceActions(row as ArapRow)" :more-label="`更多费用来源操作 · ${(row as ArapRow).documentNo || '当前费用'}`" @action="handleSourceAction($event, row as ArapRow)" /><span v-else class="muted-action">手工成本</span></template>
|
||||
<template #customerName="{ row }">{{ customerName(row.customerId, row.customerName) }}</template><template #direction="{ row }"><el-tag :type="row.direction === 'RECEIVABLE' ? 'success' : row.direction === 'PAYABLE' ? 'warning' : 'info'">{{ financeDirectionLabel(row.direction) }}</el-tag></template>
|
||||
<template #businessReference="{ row }">{{ businessReference(row as unknown as ArapRow) }}</template><template #sourceType="{ row }">{{ sourceTypeDisplay(row.sourceType) }}</template><template #supplierName="{ row }">{{ supplierDisplay(row as unknown as ArapRow) }}</template><template #sourceStatus="{ row }">{{ sourceStatusDisplay(row.status) }}</template>
|
||||
<template #status="{ row }"><el-tag :type="row.reviewStatus === 'APPROVED' ? 'success' : row.reviewStatus === 'REJECTED' ? 'danger' : 'warning'">{{ statusLabel(row.reviewStatus) }}</el-tag></template>
|
||||
|
||||
@@ -133,7 +133,7 @@ describe('FinanceCustomerStatementsView', () => {
|
||||
expect(source).toContain("{ key: 'approve', label: '通过', priority: 250")
|
||||
expect(source).toContain("{ key: 'reject', label: '驳回', priority: 200")
|
||||
expect(source).toContain("{ key: 'void', label: '作废', tone: 'danger', divided: true")
|
||||
expect(source).toContain('<RowActions :actions="statementActions(row as FinanceStatementReadModel)"')
|
||||
expect(source).toContain('<RowActions text-actions :actions="statementActions(row as FinanceStatementReadModel)"')
|
||||
})
|
||||
|
||||
it('keeps three available row actions visible with primary and danger semantics', async () => {
|
||||
|
||||
@@ -669,16 +669,19 @@ watch(detailRouteId, (statementId) => {
|
||||
</el-tabs>
|
||||
</DetailPageLayout>
|
||||
<ListPageLayout v-else aria-label="客户对账单">
|
||||
<template #toolbar><QueryToolbar v-model="filters" class="surface-panel" ariaLabel="客户对账单筛选" :fields="fields" :loading="loading" preference-key="finance-statements-query" :search-on-clear="false" @search="load" /></template>
|
||||
<template v-if="customerError || error" #state><LoadErrorState title="客户对账单加载失败" :message="(customerError || error)?.message || ''" :loading="loading" @retry="customerError ? loadCustomers() : load()"><template #details><ApiErrorMeta :details="(customerError || error)!" /></template></LoadErrorState></template>
|
||||
<template v-if="!customerError && !error">
|
||||
<section class="surface-panel table-panel">
|
||||
<DataTable v-model:page="page" v-model:page-size="pageSize" :data="paginatedRows" :columns="columns" :loading="loading" :total="filteredRows.length" aria-label="客户对账单列表" row-key="id" empty-text="暂无客户对账单" preference-key="finance-customer-statements" @row-click="openDetail">
|
||||
|
||||
|
||||
<section><DataTable appearance="integrated" :stripe="false" column-settings-mode="show" :results-visible="!customerError && !error" v-model:page="page" v-model:page-size="pageSize" :data="paginatedRows" :columns="columns" :loading="loading" :total="filteredRows.length" aria-label="客户对账单列表" row-key="id" empty-text="暂无客户对账单" preference-key="finance-customer-statements" @row-click="openDetail">
|
||||
<template #toolbar-start><h1>客户对账单</h1></template>
|
||||
<template #filters><QueryToolbar v-model="filters" ariaLabel="客户对账单筛选" :fields="fields" :loading="loading" preference-key="finance-statements-query" :search-on-clear="false" @search="load" /></template>
|
||||
<template #notices><template v-if="customerError || error"><LoadErrorState title="客户对账单加载失败" :message="(customerError || error)?.message || ''" :loading="loading" @retry="customerError ? loadCustomers() : load()"><template #details><ApiErrorMeta :details="(customerError || error)!" /></template></LoadErrorState></template></template>
|
||||
|
||||
|
||||
<template #toolbar-actions>
|
||||
<el-button v-if="canExport" :loading="exportBusy" :disabled="loading || exportBusy" @click="exportStatements"><Download :size="15" />导出</el-button>
|
||||
<el-button v-if="canCreate" type="primary" :disabled="!canGenerate" :title="canGenerate ? '生成对账单' : '缺少客户列表权限,无法选择客户'" @click="openCreate"><Plus :size="15" />生成对账单</el-button>
|
||||
</template>
|
||||
<template #actions="{ row }"><RowActions :actions="statementActions(row as FinanceStatementReadModel)" :more-label="`更多对账单操作 · ${(row as FinanceStatementReadModel).billNo}`" @action="handleStatementAction($event, row as FinanceStatementReadModel)" /></template>
|
||||
<template #actions="{ row }"><RowActions text-actions :actions="statementActions(row as FinanceStatementReadModel)" :more-label="`更多对账单操作 · ${(row as FinanceStatementReadModel).billNo}`" @action="handleStatementAction($event, row as FinanceStatementReadModel)" /></template>
|
||||
<template #status="{ row }"><el-tag :type="financeStatusTone(row.status)">{{ financeStatusLabel(row.status) }}</el-tag></template>
|
||||
<template #customerConfirmationStatus="{ row }"><el-tag :type="financeStatusTone(row.customerConfirmationStatus || 'NOT_REQUIRED')">{{ financeStatusLabel(row.customerConfirmationStatus || 'NOT_REQUIRED') }}</el-tag></template>
|
||||
<template #customerName="{ row }">{{ customerDisplayName(row.customerId, row.customerName) }}</template>
|
||||
@@ -694,10 +697,7 @@ watch(detailRouteId, (statementId) => {
|
||||
<template #updatedByName="{ row }">{{ operatorDisplayName(row.updatedByName) }}</template>
|
||||
<template #updatedAt="{ row }">{{ formatDateTime(row.updatedAt) }}</template>
|
||||
<template #pagination-left>共 {{ filteredRows.length }} 条 · 币种:CNY</template>
|
||||
</DataTable>
|
||||
</section>
|
||||
|
||||
</template>
|
||||
</DataTable></section>
|
||||
</ListPageLayout>
|
||||
|
||||
<AppDialog class="admin-density-dialog" v-model="createOpen" title="生成客户对账单" width="min(680px, 94vw)" :confirm-loading="createBusy" @confirm="createStatement">
|
||||
|
||||
@@ -6,8 +6,10 @@ const source = readFileSync(resolve(process.cwd(), 'src/modules/finance/pages/Fi
|
||||
const statementsSource = readFileSync(resolve(process.cwd(), 'src/modules/finance/pages/FinanceCustomerStatementsView.vue'), 'utf8')
|
||||
|
||||
describe('Finance formal settlement desktop presentation', () => {
|
||||
it('keeps settlement totals on the left and list actions on the right of the table toolbar', () => {
|
||||
it('keeps the title and actions in the toolbar and settlement totals before results', () => {
|
||||
expect(source).toContain('<template #toolbar-start>')
|
||||
expect(source).toContain('<h1>{{ title }}</h1>')
|
||||
expect(source).toContain('<template #before-results>')
|
||||
expect(source).toContain('<dl class="density-detail-overview settlement-summary" aria-label="结算汇总">')
|
||||
expect(source).toContain('<template #toolbar-actions>')
|
||||
expect(source).toContain('<div class="settlement-list-actions">')
|
||||
@@ -26,9 +28,9 @@ describe('Finance formal settlement desktop presentation', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('renders at most three permitted row actions as shared outlined text buttons', () => {
|
||||
it('renders at most three permitted row actions as shared text buttons', () => {
|
||||
expect(source.match(/key: 'actions', label: '操作', width: 250/g)).toHaveLength(2)
|
||||
expect(source).toContain('class="settlement-row-actions density-actions"')
|
||||
expect(source).toContain('class="settlement-row-actions density-actions row-actions admin-text-actions"')
|
||||
for (const action of ['openDetail(row)', 'openReceipt(row)', 'openPayment(row)']) expect(source).toContain('@click="' + action + '"')
|
||||
expect(source).toContain('data-tone="danger"')
|
||||
expect(source).toContain('class="density-action"')
|
||||
|
||||
@@ -589,37 +589,38 @@ onMounted(() => {
|
||||
</section>
|
||||
</DetailPageLayout>
|
||||
<ListPageLayout v-else :aria-label="title">
|
||||
<template #toolbar>
|
||||
<QueryToolbar v-model="filters" class="surface-panel" :ariaLabel="`${title}筛选`" :fields="fields" :preference-key="`finance-settlement-${kind}-query`" :search-on-clear="false" @search="load" />
|
||||
|
||||
|
||||
<section><DataTable :preference-key="`finance-settlement-${kind}-table`" appearance="integrated" :stripe="false" column-settings-mode="show" :results-visible="!customerError && !error" v-model:page="page" v-model:page-size="pageSize" :data="paginatedRows" :columns="columns" :loading="loading" :total="filteredRows.length" :aria-label="`${title}列表`" row-key="id" empty-text="暂无符合条件的结算单">
|
||||
<template #toolbar-start><h1>{{ title }}</h1></template>
|
||||
<template #filters>
|
||||
<QueryToolbar v-model="filters" :ariaLabel="`${title}筛选`" :fields="fields" :preference-key="`finance-settlement-${kind}-query`" :search-on-clear="false" @search="load" />
|
||||
</template>
|
||||
<template v-if="customerError || error" #state>
|
||||
<template #notices><template v-if="customerError || error">
|
||||
<LoadErrorState :title="`${title}加载失败`" :message="(customerError || error)?.message || ''" :loading="loading" @retry="customerError ? loadCustomers() : load()"><template #details><ApiErrorMeta :details="(customerError || error)!" /></template></LoadErrorState>
|
||||
</template>
|
||||
<template v-if="!customerError && !error">
|
||||
<section class="surface-panel table-panel">
|
||||
<DataTable v-model:page="page" v-model:page-size="pageSize" :data="paginatedRows" :columns="columns" :loading="loading" :total="filteredRows.length" :aria-label="`${title}列表`" row-key="id" empty-text="暂无符合条件的结算单">
|
||||
<template #toolbar-start>
|
||||
</template></template>
|
||||
<template #before-results>
|
||||
<dl class="density-detail-overview settlement-summary" aria-label="结算汇总">
|
||||
<div class="summary-item"><dt>{{ kind === 'receivable' ? '应收总额' : '应付总额' }}</dt><dd>{{ money(totals.amount) }}</dd></div>
|
||||
<div class="summary-item"><dt>{{ kind === 'receivable' ? '已收' : '已付' }}</dt><dd class="is-success">{{ money(totals.paid) }}</dd></div>
|
||||
<div class="summary-item"><dt>{{ kind === 'receivable' ? '待收' : '待付' }}</dt><dd class="is-danger">{{ money(totals.outstanding) }}</dd></div>
|
||||
</dl>
|
||||
</template>
|
||||
|
||||
|
||||
<template #toolbar-actions>
|
||||
<div class="settlement-list-actions">
|
||||
<el-button v-if="canCreate" type="primary" @click="openCreate"><Plus :size="15" />{{ createLabel }}</el-button>
|
||||
<el-button v-if="canExport" :loading="exportSubmitting" :disabled="loading || exportSubmitting" @click="exportRows"><Download :size="15" />导出</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<div class="settlement-row-actions density-actions">
|
||||
<template #actions="{ row }"><div class="settlement-row-actions density-actions row-actions admin-text-actions">
|
||||
<el-button data-tone="neutral" class="density-action" size="small" @click="openDetail(row)">详情</el-button>
|
||||
<el-button data-tone="neutral" class="density-action" v-if="canReceive && ['ISSUED','PARTIALLY_PAID'].includes(String(row.status))" size="small" :loading="receiptBusy" @click="openReceipt(row)">收款</el-button>
|
||||
<el-button data-tone="neutral" class="density-action" v-if="canPay && ['ISSUED','PARTIALLY_PAID'].includes(String(row.status))" size="small" @click="openPayment(row)">付款</el-button>
|
||||
<el-button data-tone="danger" class="density-action" v-if="canVoid && row.status === 'ISSUED'" size="small" :loading="actionBusy" @click="performAction(row as SettlementRow, 'VOID')">作废</el-button>
|
||||
<el-button data-tone="neutral" class="density-action" v-if="canVoid && row.status === 'VOIDED'" size="small" :loading="actionBusy" @click="performAction(row as SettlementRow, 'REOPEN')">重开</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</div></template>
|
||||
<template #customer="{ row }">{{ customerName(String(row.customerId), row.customerName) }}</template>
|
||||
<template #supplier="{ row }">{{ row.supplierName || '—' }}</template>
|
||||
<template #feeItemSummary="{ row }">{{ row.feeItemSummary || '—' }}</template>
|
||||
@@ -636,9 +637,7 @@ onMounted(() => {
|
||||
<template #lastPaymentAt="{ row }">{{ displayDateTime(row.lastPaymentAt) }}</template>
|
||||
<template #settlementStatus="{ row }"><el-tag :type="financeStatusTone(row.status)">{{ financeStatusLabel(row.status) }}</el-tag></template>
|
||||
<template #pagination-left>共 {{ filteredRows.length }} 条 · 币种:CNY</template>
|
||||
</DataTable>
|
||||
</section>
|
||||
</template>
|
||||
</DataTable></section>
|
||||
</ListPageLayout>
|
||||
|
||||
<AppDialog class="admin-density-dialog" v-model="createOpen" :title="`生成${title}`" width="min(600px, 94vw)" :confirm-loading="createBusy" @confirm="submitCreate">
|
||||
|
||||
@@ -92,27 +92,24 @@ onMounted(() => { void loadCustomers(); void load() })
|
||||
<AdminPage class="receivable-statistics-page admin-density" spacing="normal">
|
||||
<el-result v-if="!canView" icon="warning" title="无权访问应收款统计表" sub-title="请联系管理员开通应收款统计查看权限" />
|
||||
<template v-else>
|
||||
<section class="surface-panel statistics-query" aria-label="应收款统计筛选">
|
||||
<DataTable class="statistics-table" appearance="integrated" :stripe="false" column-settings-mode="show" :results-visible="!error" v-model:page="page" v-model:page-size="pageSize" :data="rows" :columns="columns" :loading="loading" :total="result?.totalCustomers ?? 0" :row-key="statisticsRowKey" preference-key="finance-receivable-statistics-table" aria-label="应收款统计列表" empty-text="当前月份暂无应收数据" @update:page="load()" @update:page-size="page = 1; load()">
|
||||
<template #toolbar-start><h1>应收款统计表</h1></template>
|
||||
<template #filters><section class="query-toolbar statistics-query" aria-label="应收款统计筛选">
|
||||
<div class="query-field"><span>统计月份</span><el-date-picker v-model="month" type="month" value-format="YYYY-MM" format="YYYY年MM月" :clearable="false" aria-label="统计月份" /></div>
|
||||
<div class="query-field"><span>应收单位</span><el-select v-model="customerId" clearable filterable placeholder="全部应收单位" aria-label="应收单位"><el-option v-for="customer in customers" :key="String(customer.id)" :label="customer.tenantName" :value="String(customer.id)" /></el-select></div>
|
||||
<div class="query-actions"><el-button type="primary" :loading="loading" @click="search"><Search :size="16" />查询</el-button><el-button @click="reset"><RotateCcw :size="16" />重置</el-button></div>
|
||||
</section>
|
||||
<LoadErrorState v-if="error" title="应收款统计加载失败" :message="error.message" :loading="loading" @retry="load"><template #details><ApiErrorMeta :details="error" /></template></LoadErrorState>
|
||||
<template v-else>
|
||||
<dl class="density-detail-overview statistics-summary" aria-label="CNY应收汇总">
|
||||
</section></template>
|
||||
<template #notices><LoadErrorState v-if="error" title="应收款统计加载失败" :message="error.message" :loading="loading" @retry="load"><template #details><ApiErrorMeta :details="error" /></template></LoadErrorState></template>
|
||||
<template #before-results><dl class="density-detail-overview statistics-summary" aria-label="CNY应收汇总">
|
||||
<div><dt>应收总额</dt><dd>CNY {{ money(result?.totalReceivable) }}</dd></div>
|
||||
<div><dt>已收合计</dt><dd class="is-received">CNY {{ money(result?.totalReceived) }}</dd></div>
|
||||
<div><dt>余额未收</dt><dd class="is-outstanding">CNY {{ money(result?.totalOutstanding) }}</dd></div>
|
||||
</dl>
|
||||
<section class="surface-panel statistics-table">
|
||||
<header><h2>应收款明细</h2><span>共 {{ result?.totalCustomers ?? 0 }} 个应收单位 · 仅统计CNY正式应收结算单</span></header>
|
||||
<DataTable v-model:page="page" v-model:page-size="pageSize" :data="rows" :columns="columns" :loading="loading" :total="result?.totalCustomers ?? 0" :row-key="statisticsRowKey" preference-key="finance-receivable-statistics-table" aria-label="应收款统计列表" empty-text="当前月份暂无应收数据" @update:page="load()" @update:page-size="page = 1; load()">
|
||||
</dl><div class="statistics-report-heading"><h2>应收款明细</h2><span>共 {{ result?.totalCustomers ?? 0 }} 个应收单位 · 仅统计CNY正式应收结算单</span></div></template>
|
||||
|
||||
<template #toolbar-actions><div class="report-actions"><el-button :disabled="loading || !result" @click="printCurrent"><Printer :size="16" />打印</el-button><el-button v-if="canExport" type="primary" :loading="exporting" :disabled="loading || exporting" @click="exportCurrent"><Download :size="16" />导出Excel</el-button></div></template>
|
||||
<template #customerName="{ row }">{{ displayCustomerName(row) }}</template><template #totalReceivable="{ row }">{{ money(row.totalReceivable) }}</template><template #totalReceived="{ row }">{{ money(row.totalReceived) }}</template><template #totalOutstanding="{ row }"><span class="outstanding-cell">{{ money(row.totalOutstanding) }}</span></template>
|
||||
<template #pagination-left>统计月份:{{ result?.statisticsMonth }} · 币种:CNY</template>
|
||||
</DataTable>
|
||||
</section>
|
||||
</template>
|
||||
</template>
|
||||
</AdminPage>
|
||||
</template>
|
||||
@@ -126,9 +123,9 @@ onMounted(() => { void loadCustomers(); void load() })
|
||||
.query-actions :deep(.el-button), .report-actions :deep(.el-button) { margin-left: 0; }
|
||||
.outstanding-cell { color: var(--el-color-danger); font-variant-numeric: tabular-nums; }
|
||||
.statistics-table { overflow: hidden; }
|
||||
.statistics-table > header { display: flex; align-items: center; justify-content: space-between; padding: var(--admin-space-5) var(--admin-space-6); border-bottom: 1px solid var(--cc-border, #e3e8ef); }
|
||||
.statistics-report-heading { display: flex; align-items: center; justify-content: space-between; padding: var(--admin-space-5) var(--admin-space-6); border-bottom: 1px solid var(--cc-border, #e3e8ef); }
|
||||
.statistics-table h2 { margin: 0; font-size: var(--admin-font-section); }
|
||||
.statistics-table header span { color: var(--cc-muted); font-size: var(--admin-font-body); }
|
||||
.statistics-report-heading span { color: var(--cc-muted); font-size: var(--admin-font-body); }
|
||||
@media print {
|
||||
.statistics-query, :deep(.data-table__toolbar), :deep(.data-table__pagination) { display: none !important; }
|
||||
.receivable-statistics-page { gap: 10px; color: #000; }
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
|
||||
export const financeRoutes = [
|
||||
{ path: 'finance/customer-accounts', name: 'finance-customer-accounts', component: () => import('./pages/FinanceCustomerAccountsView.vue'), meta: { title: '客户账户', description: '查看客户 CNY 应收台账并维护独立账期', permission: 'platform:finance:customer-account:list' } },
|
||||
{ path: 'finance/customer-accounts', name: 'finance-customer-accounts', component: () => import('./pages/FinanceCustomerAccountsView.vue'), meta: { showPageHeading: false, title: '客户账户', description: '查看客户 CNY 应收台账并维护独立账期', permission: 'platform:finance:customer-account:list' } },
|
||||
{ path: 'finance/customer-accounts/:customerId', name: 'finance-customer-account-detail', component: () => import('./pages/FinanceCustomerAccountsView.vue'), meta: { title: '客户账户详情', description: '查看客户派生财务汇总与账户流水', permission: 'platform:finance:customer-account:list', showPageHeading: false } },
|
||||
{ path: 'finance/customer-arap', name: 'finance-customer-arap', component: () => import('./pages/FinanceCustomerArapView.vue'), meta: { title: '客户应收应付', description: '按客户追溯入库单、订单和柜次费用来源', permission: 'platform:finance:customer-arap:list' } },
|
||||
{ path: 'finance/customer-arap', name: 'finance-customer-arap', component: () => import('./pages/FinanceCustomerArapView.vue'), meta: { showPageHeading: false, title: '客户应收应付', description: '按客户追溯入库单、订单和柜次费用来源', permission: 'platform:finance:customer-arap:list' } },
|
||||
{ path: 'finance/customer-arap/:feeSourceId', name: 'finance-customer-arap-detail', component: () => import('./pages/FinanceCustomerArapView.vue'), meta: { title: '费用来源详情', description: '查看费用来源、审核进度、关联业务与操作记录', permission: 'platform:finance:customer-arap:list', showPageHeading: false } },
|
||||
{ path: 'finance/receivable-statistics', name: 'finance-receivable-statistics', component: () => import('./pages/FinanceReceivableStatisticsView.vue'), meta: { title: '应收款统计表', description: '按月份和客户统计 CNY 应收、已收与未收余额', permission: 'platform:finance:receivable-statistics:list' } },
|
||||
{ path: 'finance/customer-statements', name: 'finance-customer-statements', component: () => import('./pages/FinanceCustomerStatementsView.vue'), meta: { title: '客户对账单', description: '维护 CNY 客户对账单并在出账后冻结', permission: 'platform:finance:customer-statement:list' } },
|
||||
{ path: 'finance/receivable-statistics', name: 'finance-receivable-statistics', component: () => import('./pages/FinanceReceivableStatisticsView.vue'), meta: { showPageHeading: false, title: '应收款统计表', description: '按月份和客户统计 CNY 应收、已收与未收余额', permission: 'platform:finance:receivable-statistics:list' } },
|
||||
{ path: 'finance/customer-statements', name: 'finance-customer-statements', component: () => import('./pages/FinanceCustomerStatementsView.vue'), meta: { showPageHeading: false, title: '客户对账单', description: '维护 CNY 客户对账单并在出账后冻结', permission: 'platform:finance:customer-statement:list' } },
|
||||
{ path: 'finance/customer-statements/:statementId', name: 'finance-customer-statement-detail', component: () => import('./pages/FinanceCustomerStatementsView.vue'), meta: { title: '客户对账单详情', description: '查看对账单概要、费用明细、支付历史与调整记录', permission: 'platform:finance:customer-statement:list', showPageHeading: false } },
|
||||
{ path: 'finance/settlement-receivables', name: 'finance-settlement-receivables', component: () => import('./pages/FinanceSettlementReceivablesView.vue'), meta: { title: '结算单应收', description: '登记客户收款、凭证及对应对账单金额', permission: 'platform:finance:settlement-ar:list' } },
|
||||
{ path: 'finance/settlement-receivables', name: 'finance-settlement-receivables', component: () => import('./pages/FinanceSettlementReceivablesView.vue'), meta: { showPageHeading: false, title: '结算单应收', description: '登记客户收款、凭证及对应对账单金额', permission: 'platform:finance:settlement-ar:list' } },
|
||||
{ path: 'finance/settlement-receivables/:settlementId', name: 'finance-settlement-receivable-detail', component: () => import('./pages/FinanceSettlementReceivablesView.vue'), meta: { title: '应收结算单详情', description: '查看应收结算信息、费用明细和收款记录', permission: 'platform:finance:settlement-ar:list', showPageHeading: false } },
|
||||
{ path: 'finance/settlement-payables', name: 'finance-settlement-payables', component: () => import('./pages/FinanceSettlementPayablesView.vue'), meta: { title: '结算单应付', description: '登记单票及整柜公共成本并追溯业务来源', permission: 'platform:finance:settlement-ap:list' } },
|
||||
{ path: 'finance/settlement-payables', name: 'finance-settlement-payables', component: () => import('./pages/FinanceSettlementPayablesView.vue'), meta: { showPageHeading: false, title: '结算单应付', description: '登记单票及整柜公共成本并追溯业务来源', permission: 'platform:finance:settlement-ap:list' } },
|
||||
{ path: 'finance/settlement-payables/:settlementId', name: 'finance-settlement-payable-detail', component: () => import('./pages/FinanceSettlementPayablesView.vue'), meta: { title: '应付结算单详情', description: '查看应付结算信息、费用明细和付款记录', permission: 'platform:finance:settlement-ap:list', showPageHeading: false } },
|
||||
] satisfies RouteRecordRaw[]
|
||||
|
||||
Reference in New Issue
Block a user