优化
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox, type UploadFile } from 'element-plus'
|
||||
import { ArrowLeft, CreditCard, Download, Eye, HandCoins, Plus, RotateCcw, XCircle } from '@lucide/vue'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import { createPlatformExportTask } from '@/modules/downloads'
|
||||
import { listPlatformTenants, type PlatformTenantResource } from '@/modules/iam'
|
||||
import { categoryLabels, listSuppliers, type Supplier } from '@/modules/suppliers'
|
||||
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 +17,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 {
|
||||
actionFinanceSettlement,
|
||||
createFinanceReceipt,
|
||||
@@ -41,6 +44,7 @@ type SettlementRow = FinanceReceivableSettlement | FinancePayableSettlement
|
||||
|
||||
const props = defineProps<{ kind: 'receivable' | 'payable'; settlementId?: string }>()
|
||||
const emit = defineEmits<{ openDetail: [settlementId: string]; back: [] }>()
|
||||
const router = useRouter()
|
||||
const access = useAccessPreviewStore()
|
||||
const customers = ref<PlatformTenantResource[]>([])
|
||||
const filters = ref<QueryModel>({ customerId: '', supplierName: '', settlementNo: '', status: '' })
|
||||
@@ -77,15 +81,18 @@ const receiptValidationError = ref<string | null>(null)
|
||||
const receiptError = ref<ApiErrorDetails | null>(null)
|
||||
const receiptForm = ref({ receivedDate: new Date().toISOString().slice(0, 10), amount: 0, paymentMethod: 'BANK_TRANSFER' as 'BANK_TRANSFER' | 'CASH' | 'OTHER', remark: '' })
|
||||
const actionBusy = ref(false)
|
||||
const exportSubmitting = ref(false)
|
||||
|
||||
const createPermission = computed(() => props.kind === 'receivable' ? 'platform:finance:settlement-ar:create' : 'platform:finance:settlement-ap:create')
|
||||
const listPermission = computed(() => props.kind === 'receivable' ? 'platform:finance:settlement-ar:list' : 'platform:finance:settlement-ap:list')
|
||||
const exportPermission = computed(() => props.kind === 'receivable' ? 'platform:finance:settlement-ar:export' : 'platform:finance:settlement-ap:export')
|
||||
const canView = computed(() => access.can(listPermission.value))
|
||||
const canListCustomers = computed(() => access.can('platform:customer:list'))
|
||||
const canCreate = computed(() => access.can(createPermission.value))
|
||||
const canPay = computed(() => props.kind === 'payable' && access.can('platform:finance:payment:create'))
|
||||
const canReceive = computed(() => props.kind === 'receivable' && access.can('platform:finance:receipt:create'))
|
||||
const canVoid = computed(() => access.can('platform:finance:settlement:void'))
|
||||
const canExport = computed(() => access.can(exportPermission.value) && access.can('platform:download:list'))
|
||||
const title = computed(() => props.kind === 'receivable' ? '结算单应收' : '结算单应付')
|
||||
const detailMode = computed(() => Boolean(props.settlementId))
|
||||
const createLabel = computed(() => props.kind === 'receivable' ? '生成应收结算单' : '生成应付结算单')
|
||||
@@ -397,37 +404,48 @@ async function performAction(row: SettlementRow, action: 'VOID' | 'REOPEN') {
|
||||
finally { actionBusy.value = false }
|
||||
}
|
||||
|
||||
function csvCell(value: unknown) { return `"${String(value ?? '').replaceAll('"', '""')}"` }
|
||||
function exportRows() {
|
||||
if (!filteredRows.value.length) return ElMessage.warning('当前没有可导出的结算单')
|
||||
const header = props.kind === 'receivable'
|
||||
? ['结算单号', '客户', '币种', '应收金额', '已收', '余额', '生成日期', '到期日', '收款状态']
|
||||
: ['结算单号', '供应商', '费用类型', '币种', '应付金额', '已付', '余额', '生成日期', '付款状态']
|
||||
const records = filteredRows.value.map((row) => props.kind === 'receivable'
|
||||
? [row.settlementNo, customerName(row.customerId, (row as FinanceReceivableSettlement).customerName), row.currency, totalAmount(row), paidAmount(row), row.outstandingAmount, (row as FinanceReceivableSettlement).billDate, (row as FinanceReceivableSettlement).dueDate, financeStatusLabel(row.status)]
|
||||
: [row.settlementNo, (row as FinancePayableSettlement).supplierName, (row as FinancePayableSettlement).feeItemName, row.currency, totalAmount(row), paidAmount(row), row.outstandingAmount, (row as FinancePayableSettlement).createdAt, financeStatusLabel(row.status)])
|
||||
const blob = new Blob([`\ufeff${[header, ...records].map((record) => record.map(csvCell).join(',')).join('\r\n')}`], { type: 'text/csv;charset=utf-8' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = `${title.value}-${new Date().toISOString().slice(0, 10)}.csv`
|
||||
link.click()
|
||||
URL.revokeObjectURL(url)
|
||||
async function createSettlementExport(detailId?: string) {
|
||||
const isDetail = Boolean(detailId)
|
||||
const taskName = props.kind === 'receivable'
|
||||
? (isDetail ? '应收结算单详情' : '应收结算单列表')
|
||||
: (isDetail ? '应付结算单详情' : '应付结算单列表')
|
||||
const exportType = props.kind === 'receivable'
|
||||
? (isDetail ? 'FINANCE_RECEIVABLE_SETTLEMENT_DETAIL' : 'FINANCE_RECEIVABLE_SETTLEMENT_LIST')
|
||||
: (isDetail ? 'FINANCE_PAYABLE_SETTLEMENT_DETAIL' : 'FINANCE_PAYABLE_SETTLEMENT_LIST')
|
||||
const settlementId = String(detailId || '')
|
||||
if (isDetail && !/^[1-9]\d*$/.test(settlementId)) return ElMessage.error('结算单业务标识无效,无法创建导出任务')
|
||||
const parameters: Record<string, string> = isDetail ? { settlementId } : {}
|
||||
if (!isDetail) {
|
||||
const customerId = String(filters.value.customerId || '').trim()
|
||||
if (customerId && !/^[1-9]\d*$/.test(customerId)) return ElMessage.error('客户业务标识无效,无法创建导出任务')
|
||||
const status = String(filters.value.status || '').trim()
|
||||
const settlementNo = String(filters.value.settlementNo || '').trim()
|
||||
const supplierName = String(filters.value.supplierName || '').trim()
|
||||
if (customerId) parameters.customerId = customerId
|
||||
if (status) parameters.status = status
|
||||
if (settlementNo) parameters.settlementNo = settlementNo
|
||||
if (props.kind === 'payable' && supplierName) parameters.supplierName = supplierName
|
||||
}
|
||||
|
||||
exportSubmitting.value = true
|
||||
try {
|
||||
const task = await createPlatformExportTask({
|
||||
exportType,
|
||||
format: 'XLSX',
|
||||
taskName,
|
||||
parameters,
|
||||
})
|
||||
const openDownloadCenter = await showExportTaskCreatedDialog({ taskName: task.taskName, taskNo: task.taskNo })
|
||||
if (openDownloadCenter) await router.push({ name: 'download-center' })
|
||||
} catch (cause) {
|
||||
ElMessage.error(getApiErrorMessage(cause, `${taskName}导出任务创建失败`))
|
||||
} finally {
|
||||
exportSubmitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function exportDetail() {
|
||||
if (!detail.value) return
|
||||
const current = detail.value
|
||||
const header = ['结算单号', props.kind === 'receivable' ? '客户' : '供应商', '币种', props.kind === 'receivable' ? '应收金额' : '应付金额', props.kind === 'receivable' ? '已收' : '已付', '余额', '业务期间', '生成日期', '到期日', '状态']
|
||||
const record = [current.settlementNo, props.kind === 'receivable' ? customerName(current.customerId, current.customerName) : current.supplierName, current.currency, current.amount, current.paidAmount, current.outstandingAmount, current.businessPeriod, current.generatedDate, current.dueDate, financeStatusLabel(current.status)]
|
||||
const blob = new Blob([`\ufeff${[header, record].map((line) => line.map(csvCell).join(',')).join('\r\n')}`], { type: 'text/csv;charset=utf-8' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = `${current.settlementNo}.csv`
|
||||
link.click()
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
function exportRows() { return createSettlementExport() }
|
||||
function exportDetail() { return detail.value ? createSettlementExport(detail.value.id) : undefined }
|
||||
|
||||
onMounted(() => {
|
||||
if (canListCustomers.value) void loadCustomers()
|
||||
@@ -448,7 +466,7 @@ onMounted(() => {
|
||||
<el-tag v-if="detail" :type="financeStatusTone(detail.status)">{{ financeStatusLabel(detail.status) }}</el-tag>
|
||||
</div>
|
||||
<div class="settlement-detail-actions">
|
||||
<el-button v-if="detail" @click="exportDetail"><Download :size="15" />导出</el-button>
|
||||
<el-button v-if="detail && canExport" :loading="exportSubmitting" :disabled="exportSubmitting" @click="exportDetail"><Download :size="15" />导出</el-button>
|
||||
<el-button v-if="detail && canReceive && ['ISSUED','PARTIALLY_PAID'].includes(detail.status)" type="primary" :loading="receiptBusy" @click="openReceiptFromDetail"><HandCoins :size="15" />部分收款</el-button>
|
||||
<el-button v-if="detail && canPay && ['ISSUED','PARTIALLY_PAID'].includes(detail.status)" type="primary" @click="openPaymentFromDetail"><CreditCard :size="15" />部分付款</el-button>
|
||||
<el-button v-if="detail && canVoid && detail.status === 'ISSUED'" type="danger" :loading="actionBusy" @click="performAction(detailActionRow(), 'VOID')"><XCircle :size="15" />作废</el-button>
|
||||
@@ -534,7 +552,7 @@ onMounted(() => {
|
||||
</div>
|
||||
<div class="settlement-list-actions">
|
||||
<el-button v-if="canCreate" type="primary" @click="openCreate"><Plus :size="15" />{{ createLabel }}</el-button>
|
||||
<el-button @click="exportRows"><Download :size="15" />导出</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">
|
||||
|
||||
Reference in New Issue
Block a user