优化
This commit is contained in:
@@ -4,8 +4,9 @@ import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ArrowLeft, Download, Eye, MoreHorizontal, Plus, RotateCcw, Send, ShieldCheck, XCircle } from '@lucide/vue'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import { createPlatformExportTask } from '@/modules/downloads'
|
||||
import { listPlatformTenants, type PlatformTenantResource } from '@/modules/iam'
|
||||
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||||
import { getApiErrorDetails, getApiErrorMessage, type ApiErrorDetails } from '@/shared/api/http'
|
||||
import AdminPage from '@/shared/components/AdminPage.vue'
|
||||
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
||||
import AppDialog from '@/shared/components/AppDialog.vue'
|
||||
@@ -15,6 +16,7 @@ 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'
|
||||
import { showExportTaskCreatedDialog } from '@/shared/components/exportTaskCreatedDialog'
|
||||
import { actionReconciliationStatement, createReconciliationStatement, getFinanceFeeSource, getReconciliationStatement, listFinanceStatements } from '../api/finance'
|
||||
import { financeSourceTypeLabel, financeStatusLabel, financeStatusTone } from '../status'
|
||||
import type { FinanceReconciliationDetail, FinanceStatementReadModel, ReconciliationActionPayload } from '../types'
|
||||
@@ -90,6 +92,7 @@ const canReview = computed(() => access.can('platform:finance:reconciliation:rev
|
||||
const canIssue = computed(() => access.can('platform:finance:reconciliation:issue'))
|
||||
const canAdjust = computed(() => access.can('platform:finance:reconciliation:adjust'))
|
||||
const canVoid = computed(() => access.can('platform:finance:reconciliation:void'))
|
||||
const canExport = computed(() => access.can('platform:finance:customer-statement:export') && access.can('platform:download:list'))
|
||||
const canGenerate = computed(() => canCreate.value && canListCustomers.value)
|
||||
const detailRouteId = computed(() => route.name === 'finance-customer-statement-detail' ? String(route.params.statementId || '') : '')
|
||||
const isDetailRoute = computed(() => Boolean(detailRouteId.value))
|
||||
@@ -318,31 +321,46 @@ function revisionActionLabel(action: string) {
|
||||
return ({ SUBMIT: '提交审核', APPROVE: '审核通过', REJECT: '审核驳回', ISSUE: '出账', ADJUST: '调整', VOID: '作废', REOPEN: '重开' } as Record<string, string>)[action] || action || '—'
|
||||
}
|
||||
|
||||
function csvCell(value: unknown) { return `"${String(value ?? '').replaceAll('"', '""')}"` }
|
||||
async function exportStatements() {
|
||||
const customerId = String(filters.value.customerId || '').trim()
|
||||
if (customerId && !/^[1-9]\d*$/.test(customerId)) return ElMessage.error('客户业务标识无效,无法创建导出任务')
|
||||
const parameters: Record<string, string | boolean> = {}
|
||||
const putText = (key: string) => {
|
||||
const value = String(filters.value[key] || '').trim()
|
||||
if (value) parameters[key] = value
|
||||
}
|
||||
const putRange = (sourceKey: string, fromKey: string, toKey: string) => {
|
||||
const value = filters.value[sourceKey]
|
||||
if (!Array.isArray(value)) return
|
||||
if (value[0]) parameters[fromKey] = String(value[0])
|
||||
if (value[1]) parameters[toKey] = String(value[1])
|
||||
}
|
||||
if (customerId) parameters.customerId = customerId
|
||||
putText('status')
|
||||
putText('billNo')
|
||||
putText('containerNo')
|
||||
putRange('periodStartRange', 'periodStartFrom', 'periodStartTo')
|
||||
putRange('periodEndRange', 'periodEndFrom', 'periodEndTo')
|
||||
putRange('lastPaymentRange', 'lastPaymentFrom', 'lastPaymentTo')
|
||||
putRange('createdRange', 'createdFrom', 'createdTo')
|
||||
const settled = String(filters.value.settled || '')
|
||||
if (settled) parameters.settled = settled === 'true'
|
||||
|
||||
exportBusy.value = true
|
||||
try {
|
||||
const hydrated = await Promise.all(rows.value.map(async (row) => {
|
||||
const current = await fetchDetail(row.id)
|
||||
return detailToRow(row, current)
|
||||
}))
|
||||
rows.value = hydrated
|
||||
const task = await createPlatformExportTask({
|
||||
exportType: 'FINANCE_RECONCILIATION_STATEMENT_LIST',
|
||||
format: 'XLSX',
|
||||
taskName: '客户对账单',
|
||||
parameters,
|
||||
})
|
||||
const openDownloadCenter = await showExportTaskCreatedDialog({ taskName: task.taskName, taskNo: task.taskNo })
|
||||
if (openDownloadCenter) await router.push({ name: 'download-center' })
|
||||
} catch (cause) {
|
||||
error.value = getApiErrorDetails(cause, '导出前加载完整对账单字段失败')
|
||||
ElMessage.error(getApiErrorMessage(cause, '客户对账单导出任务创建失败'))
|
||||
} finally {
|
||||
exportBusy.value = false
|
||||
return
|
||||
}
|
||||
const header = ['审核状态', '对账单号', '客户', '结算起始日', '结算结束日', '账期(天)', '货币', '总应收金额', '已收金额', '未收金额', '最后收款时间', '是否结清', '备注', '创建人', '创建时间', '更新人', '更新时间']
|
||||
const body = filteredRows.value.map((row) => [financeStatusLabel(row.status), row.billNo, customerDisplayName(row.customerId, row.customerName), row.periodStart, row.periodEnd, row.creditDays ?? '', row.currency, row.totalAmount, row.receivedAmount, row.outstandingAmount, formatDateTime(row.lastPaymentAt), row.settled === null ? '' : (row.settled ? '是' : '否'), row.remark, operatorDisplayName(row.createdByName), formatDateTime(row.createdAt), operatorDisplayName(row.updatedByName), formatDateTime(row.updatedAt)])
|
||||
const blob = new Blob([`\uFEFF${[header, ...body].map((line) => line.map(csvCell).join(',')).join('\r\n')}`], { type: 'text/csv;charset=utf-8' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const anchor = document.createElement('a')
|
||||
anchor.href = url
|
||||
anchor.download = `客户对账单-${new Date().toISOString().slice(0, 10)}.csv`
|
||||
anchor.click()
|
||||
URL.revokeObjectURL(url)
|
||||
exportBusy.value = false
|
||||
ElMessage.success(`已导出 ${body.length} 条客户对账单`)
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
@@ -495,7 +513,7 @@ watch(detailRouteId, (statementId) => {
|
||||
<section class="surface-panel table-panel">
|
||||
<DataTable :data="filteredRows" :columns="columns" :loading="loading" aria-label="客户对账单列表" row-key="id" empty-text="暂无客户对账单" preference-key="finance-customer-statements" @row-click="openDetail">
|
||||
<template #toolbar-actions>
|
||||
<el-button :loading="exportBusy" :disabled="loading || exportBusy || !filteredRows.length" @click="exportStatements"><Download :size="15" />导出</el-button>
|
||||
<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 }">
|
||||
|
||||
Reference in New Issue
Block a user