This commit is contained in:
2026-08-19 09:14:35 +08:00
parent c188482c24
commit 80cfca5056
36 changed files with 854 additions and 1044 deletions
@@ -50,6 +50,8 @@ const access = useAccessPreviewStore()
const customers = ref<PlatformTenantResource[]>([])
const filters = ref<QueryModel>({ customerId: '', supplierName: '', settlementNo: '', status: '' })
const rows = ref<SettlementRow[]>([])
const page = ref(1)
const pageSize = ref(20)
const loading = ref(false)
const error = ref<ApiErrorDetails | null>(null)
const customerError = ref<ApiErrorDetails | null>(null)
@@ -118,6 +120,10 @@ const filteredRows = computed(() => {
&& (!settlementNo || item.settlementNo.toLowerCase().includes(settlementNo))
&& (!supplierName || ('supplierName' in item && item.supplierName === supplierName)))
})
const paginatedRows = computed(() => {
const start = (page.value - 1) * pageSize.value
return filteredRows.value.slice(start, start + pageSize.value)
})
const totals = computed(() => filteredRows.value.reduce((result, row) => ({
amount: result.amount + totalAmount(row),
paid: result.paid + paidAmount(row),
@@ -224,8 +230,11 @@ async function loadCustomers() {
catch (cause) { customerError.value = getApiErrorDetails(cause, '客户列表加载失败') }
}
async function load(model: QueryModel = filters.value) {
filters.value = { ...model }
async function load(model?: QueryModel) {
if (model) {
filters.value = { ...model }
page.value = 1
}
rows.value = []
error.value = null
loading.value = true
@@ -571,25 +580,27 @@ onMounted(() => {
</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="load" @reset="load" />
<QueryToolbar v-model="filters" class="surface-panel" :ariaLabel="`${title}筛选`" :fields="fields" :preference-key="`finance-settlement-${kind}-query`" @search="load" />
</template>
<template v-if="customerError || error" #state>
<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="settlement-list-toolbar" aria-label="结算汇总与列表操作">
<div class="settlement-summary" aria-label="结算汇总">
<div class="summary-item"><span>{{ kind === 'receivable' ? '应收总额' : '应付总额' }}</span><strong>{{ money(totals.amount) }}</strong></div>
<div class="summary-item"><span>{{ kind === 'receivable' ? '已收' : '已付' }}</span><strong class="is-success">{{ money(totals.paid) }}</strong></div>
<div class="summary-item"><span>{{ kind === 'receivable' ? '待收' : '待付' }}</span><strong class="is-danger">{{ money(totals.outstanding) }}</strong></div>
</div>
<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>
</section>
<section class="surface-panel table-panel">
<DataTable :data="filteredRows" :columns="columns" :loading="loading" :aria-label="`${title}列表`" row-key="id" empty-text="暂无符合条件的结算单">
<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>
<div class="settlement-summary" aria-label="结算汇总">
<div class="summary-item"><span>{{ kind === 'receivable' ? '应收总额' : '应付总额' }}</span><strong>{{ money(totals.amount) }}</strong></div>
<div class="summary-item"><span>{{ kind === 'receivable' ? '已收' : '已付' }}</span><strong class="is-success">{{ money(totals.paid) }}</strong></div>
<div class="summary-item"><span>{{ kind === 'receivable' ? '待收' : '待付' }}</span><strong class="is-danger">{{ money(totals.outstanding) }}</strong></div>
</div>
</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">
<el-button size="small" plain type="primary" @click="openDetail(row)"><Eye :size="15" />详情</el-button>
@@ -644,9 +655,8 @@ onMounted(() => {
<style scoped>
.finance-settlement-page { min-width: 0; }
.settlement-list-toolbar { display: flex; min-height: 54px; align-items: center; justify-content: space-between; gap: 20px; padding: 0 2px 10px; }
.settlement-summary, .settlement-list-actions { display: flex; min-width: 0; align-items: center; flex-wrap: wrap; }
.settlement-summary { flex: 1; gap: 6px 0; }
.settlement-summary { gap: 6px 0; }
.settlement-list-actions { flex: 0 0 auto; justify-content: flex-end; gap: 8px; }
.settlement-list-actions :deep(.el-button + .el-button) { margin-left: 0; }
.settlement-row-actions { display: flex; align-items: center; flex-wrap: nowrap; gap: 6px; white-space: nowrap; }
@@ -695,12 +705,6 @@ onMounted(() => {
row-gap: 8px;
}
.settlement-list-toolbar {
min-height: 48px;
gap: 14px;
padding: 0 0 8px;
}
.settlement-summary .summary-item {
gap: 6px;
}
@@ -771,7 +775,6 @@ onMounted(() => {
.settlement-detail-state { margin: 14px; }
.partial-summary { gap: 10px; padding: 12px 10px; }
.partial-summary div { padding: 0; border-left: 0; }
.settlement-list-toolbar { align-items: stretch; flex-direction: column; padding: 0 0 10px; }
.settlement-summary { align-items: flex-start; flex-direction: column; gap: 6px; }
.settlement-summary .summary-item + .summary-item { margin-left: 0; padding-left: 0; border-left: 0; }
.settlement-list-actions { width: 100%; justify-content: flex-start; }