2026-08-11 18:43:56 +08:00
|
|
|
|
<script setup lang="ts">
|
2026-08-11 19:59:15 +08:00
|
|
|
|
import { computed, onMounted, ref } from 'vue'
|
2026-08-12 09:25:55 +08:00
|
|
|
|
import { ElMessage, type UploadFile } from 'element-plus'
|
|
|
|
|
|
import { CreditCard, Eye, HandCoins, Plus } from '@lucide/vue'
|
2026-08-11 18:43:56 +08:00
|
|
|
|
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
|
|
|
|
|
import { listPlatformTenants, type PlatformTenantResource } from '@/modules/iam'
|
2026-08-12 09:25:55 +08:00
|
|
|
|
import { listPlatformPartners, type PlatformPartnerResource } from '@/modules/master-data'
|
2026-08-11 18:43:56 +08:00
|
|
|
|
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
|
|
|
|
|
import AdminPage from '@/shared/components/AdminPage.vue'
|
|
|
|
|
|
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
2026-08-12 09:25:55 +08:00
|
|
|
|
import AppDialog from '@/shared/components/AppDialog.vue'
|
2026-08-11 18:43:56 +08:00
|
|
|
|
import AppDrawer from '@/shared/components/AppDrawer.vue'
|
|
|
|
|
|
import DataTable, { type DataTableColumn } from '@/shared/components/DataTable.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'
|
2026-08-12 09:25:55 +08:00
|
|
|
|
import {
|
|
|
|
|
|
createFinanceSettlementPayment,
|
|
|
|
|
|
createFinanceReceipt,
|
|
|
|
|
|
createPayableSettlement,
|
|
|
|
|
|
createReceivableSettlement,
|
|
|
|
|
|
getFinanceSettlement,
|
|
|
|
|
|
listFinanceFeePool,
|
|
|
|
|
|
listFinancePayableSettlements,
|
|
|
|
|
|
listFinanceReceivableSettlements,
|
|
|
|
|
|
listFinanceStatements,
|
|
|
|
|
|
uploadFinanceVoucher,
|
|
|
|
|
|
} from '../api/finance'
|
2026-08-11 19:35:28 +08:00
|
|
|
|
import { financeStatusLabel, financeStatusTone } from '../status'
|
2026-08-12 09:25:55 +08:00
|
|
|
|
import type {
|
|
|
|
|
|
FinanceFeePoolItem,
|
|
|
|
|
|
FinancePayableSettlement,
|
|
|
|
|
|
FinanceReceivableSettlement,
|
|
|
|
|
|
FinanceSettlementDetail,
|
|
|
|
|
|
FinanceStatementReadModel,
|
|
|
|
|
|
} from '../types'
|
2026-08-11 18:43:56 +08:00
|
|
|
|
|
|
|
|
|
|
const props = defineProps<{ kind: 'receivable' | 'payable' }>()
|
|
|
|
|
|
const access = useAccessPreviewStore()
|
|
|
|
|
|
const customers = ref<PlatformTenantResource[]>([])
|
2026-08-11 19:59:15 +08:00
|
|
|
|
const filters = ref<QueryModel>({ customerId: '' })
|
2026-08-11 18:43:56 +08:00
|
|
|
|
const rows = ref<Array<FinanceReceivableSettlement | FinancePayableSettlement>>([])
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
|
const error = ref<ApiErrorDetails | null>(null)
|
|
|
|
|
|
const customerError = ref<ApiErrorDetails | null>(null)
|
2026-08-12 09:25:55 +08:00
|
|
|
|
const detailOpen = ref(false)
|
|
|
|
|
|
const detail = ref<FinanceSettlementDetail | null>(null)
|
|
|
|
|
|
const detailLoading = ref(false)
|
|
|
|
|
|
const detailError = ref<ApiErrorDetails | null>(null)
|
|
|
|
|
|
const createOpen = ref(false)
|
|
|
|
|
|
const createBusy = ref(false)
|
|
|
|
|
|
const statementOptions = ref<FinanceStatementReadModel[]>([])
|
|
|
|
|
|
const feeOptions = ref<FinanceFeePoolItem[]>([])
|
|
|
|
|
|
const supplierOptions = ref<PlatformPartnerResource[]>([])
|
|
|
|
|
|
const createForm = ref({ reconciliationStatementId: '', supplierId: '', feeSourceIds: [] as string[], dueDate: '', })
|
|
|
|
|
|
const paymentOpen = ref(false)
|
|
|
|
|
|
const paymentBusy = ref(false)
|
|
|
|
|
|
const paymentFile = ref<File | null>(null)
|
|
|
|
|
|
const paymentForm = ref({ transactionDate: new Date().toISOString().slice(0, 10), amount: 0, method: 'BANK_TRANSFER' as 'BANK_TRANSFER' | 'CASH' | 'OTHER', referenceNo: '', remark: '' })
|
|
|
|
|
|
const receiptOpen = ref(false)
|
|
|
|
|
|
const receiptBusy = ref(false)
|
|
|
|
|
|
const receiptFile = ref<File | null>(null)
|
|
|
|
|
|
const receiptForm = ref({ receivedDate: new Date().toISOString().slice(0, 10), amount: 0, paymentMethod: 'BANK_TRANSFER' as 'BANK_TRANSFER' | 'CASH' | 'OTHER', remark: '' })
|
|
|
|
|
|
|
2026-08-11 18:43:56 +08:00
|
|
|
|
const viewPermission = computed(() => props.kind === 'receivable' ? 'platform:finance:settlement-ar:view' : 'platform:finance:settlement-ap:view')
|
2026-08-12 09:25:55 +08:00
|
|
|
|
const createPermission = computed(() => props.kind === 'receivable' ? 'platform:finance:settlement-ar:create' : 'platform:finance:settlement-ap:create')
|
2026-08-11 18:43:56 +08:00
|
|
|
|
const canView = computed(() => access.can(viewPermission.value))
|
2026-08-12 09:25:55 +08:00
|
|
|
|
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'))
|
2026-08-11 18:43:56 +08:00
|
|
|
|
const title = computed(() => props.kind === 'receivable' ? '结算单应收' : '结算单应付')
|
2026-08-12 09:25:55 +08:00
|
|
|
|
const createLabel = computed(() => props.kind === 'receivable' ? '生成应收结算单' : '生成应付结算单')
|
2026-08-11 19:35:28 +08:00
|
|
|
|
const activeCustomers = computed(() => customers.value.filter((item) => item.status === 'ACTIVE'))
|
|
|
|
|
|
const fields = computed<QueryField[]>(() => [{ key: 'customerId', type: 'select', label: '客户', width: 280, placeholder: '请选择客户', props: { filterable: true, loading: !customers.value.length && !customerError.value }, options: activeCustomers.value.map((item) => ({ label: item.tenantName, value: String(item.id) })) }])
|
2026-08-11 18:43:56 +08:00
|
|
|
|
const columns = computed<DataTableColumn[]>(() => props.kind === 'receivable'
|
2026-08-12 09:25:55 +08:00
|
|
|
|
? [{ key: 'actions', label: '操作', width: 190, fixed: 'left', slotName: 'actions' }, { key: 'customerId', label: '客户', minWidth: 160, slotName: 'customer' }, { key: 'settlementNo', label: '应收结算单号', minWidth: 180 }, { key: 'receivableAmount', label: '应收(CNY)', width: 140 }, { key: 'receivedAmount', label: '已收(CNY)', width: 140 }, { key: 'outstandingAmount', label: '未收(CNY)', width: 140 }, { key: 'status', label: '状态', width: 120, slotName: 'status' }]
|
|
|
|
|
|
: [{ key: 'actions', label: '操作', width: 190, fixed: 'left', slotName: 'actions' }, { key: 'customerId', label: '客户', minWidth: 160, slotName: 'customer' }, { key: 'settlementNo', label: '应付结算单号', minWidth: 180 }, { key: 'feeItemName', label: '费用项目', minWidth: 160 }, { key: 'payableAmount', label: '应付(CNY)', width: 140 }, { key: 'paidAmount', label: '已付(CNY)', width: 140 }, { key: 'outstandingAmount', label: '未付(CNY)', width: 140 }, { key: 'status', label: '状态', width: 120, slotName: 'status' }])
|
|
|
|
|
|
|
|
|
|
|
|
async function loadCustomers() {
|
|
|
|
|
|
customerError.value = null
|
|
|
|
|
|
try { customers.value = (await listPlatformTenants({ page: 1, size: 200 })).records }
|
|
|
|
|
|
catch (cause) { customerError.value = getApiErrorDetails(cause, '客户列表加载失败') }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function load(model: QueryModel = filters.value) {
|
|
|
|
|
|
filters.value = { ...model }
|
|
|
|
|
|
rows.value = []
|
|
|
|
|
|
error.value = null
|
|
|
|
|
|
if (!filters.value.customerId) return
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
rows.value = props.kind === 'receivable'
|
|
|
|
|
|
? await listFinanceReceivableSettlements(String(filters.value.customerId))
|
|
|
|
|
|
: await listFinancePayableSettlements(String(filters.value.customerId))
|
|
|
|
|
|
} catch (cause) { error.value = getApiErrorDetails(cause, `${title.value}加载失败,请重试`) }
|
|
|
|
|
|
finally { loading.value = false }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function openDetail(row: unknown) {
|
|
|
|
|
|
const settlement = row as FinanceReceivableSettlement | FinancePayableSettlement
|
|
|
|
|
|
detailOpen.value = true
|
|
|
|
|
|
detail.value = null
|
|
|
|
|
|
detailError.value = null
|
|
|
|
|
|
detailLoading.value = true
|
|
|
|
|
|
try { detail.value = await getFinanceSettlement(settlement.id) }
|
|
|
|
|
|
catch (cause) { detailError.value = getApiErrorDetails(cause, `${title.value}详情加载失败`) }
|
|
|
|
|
|
finally { detailLoading.value = false }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function openCreate() {
|
|
|
|
|
|
const customerId = String(filters.value.customerId || '')
|
|
|
|
|
|
if (!customerId) {
|
|
|
|
|
|
ElMessage.warning('请先选择客户')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
createForm.value = { reconciliationStatementId: '', supplierId: '', feeSourceIds: [], dueDate: '' }
|
|
|
|
|
|
createOpen.value = true
|
|
|
|
|
|
error.value = null
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (props.kind === 'receivable') {
|
|
|
|
|
|
statementOptions.value = (await listFinanceStatements(customerId)).filter((item) => item.status === 'ISSUED')
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const [fees, suppliers] = await Promise.all([
|
|
|
|
|
|
listFinanceFeePool(customerId, 'PAYABLE'),
|
|
|
|
|
|
listPlatformPartners({ page: 1, size: 200, type: 'SUPPLIER', status: 'ACTIVE' }),
|
|
|
|
|
|
])
|
|
|
|
|
|
feeOptions.value = fees.filter((item) => item.feeSourceId && item.status === 'AVAILABLE')
|
|
|
|
|
|
supplierOptions.value = suppliers.records
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (cause) { error.value = getApiErrorDetails(cause, '结算来源加载失败') }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function submitCreate() {
|
|
|
|
|
|
const customerId = String(filters.value.customerId || '')
|
|
|
|
|
|
if (props.kind === 'receivable' && !createForm.value.reconciliationStatementId) return ElMessage.warning('请选择已出账对账单')
|
|
|
|
|
|
if (props.kind === 'payable' && (!createForm.value.supplierId || !createForm.value.dueDate || !createForm.value.feeSourceIds.length)) return ElMessage.warning('请完整填写供应商、到期日和费用来源')
|
|
|
|
|
|
createBusy.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (props.kind === 'receivable') {
|
|
|
|
|
|
await createReceivableSettlement({ reconciliationStatementId: createForm.value.reconciliationStatementId, currency: 'CNY' })
|
|
|
|
|
|
} else {
|
|
|
|
|
|
await createPayableSettlement({ customerId, supplierId: createForm.value.supplierId, feeSourceIds: createForm.value.feeSourceIds, dueDate: createForm.value.dueDate, currency: 'CNY' })
|
|
|
|
|
|
}
|
|
|
|
|
|
createOpen.value = false
|
|
|
|
|
|
ElMessage.success(`${title.value}已生成`)
|
|
|
|
|
|
await load()
|
|
|
|
|
|
} catch (cause) { error.value = getApiErrorDetails(cause, `${title.value}生成失败`) }
|
|
|
|
|
|
finally { createBusy.value = false }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function openPayment(row: unknown) {
|
|
|
|
|
|
const settlement = row as FinancePayableSettlement
|
|
|
|
|
|
detail.value = { id: settlement.id, settlementNo: settlement.settlementNo, customerId: settlement.customerId, direction: 'PAYABLE', amount: settlement.payableAmount, paidAmount: settlement.paidAmount, outstandingAmount: settlement.outstandingAmount, currency: 'CNY', status: settlement.status, version: settlement.version, lines: [], transactions: [], revisions: [] }
|
|
|
|
|
|
paymentFile.value = null
|
|
|
|
|
|
paymentForm.value = { transactionDate: new Date().toISOString().slice(0, 10), amount: settlement.outstandingAmount, method: 'BANK_TRANSFER', referenceNo: '', remark: '' }
|
|
|
|
|
|
paymentOpen.value = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function selectPaymentFile(file: UploadFile) { paymentFile.value = file.raw ?? null }
|
|
|
|
|
|
|
|
|
|
|
|
async function openReceipt(row: unknown) {
|
|
|
|
|
|
const settlement = row as FinanceReceivableSettlement
|
|
|
|
|
|
receiptBusy.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
const current = await getFinanceSettlement(settlement.id)
|
|
|
|
|
|
if (!current.reconciliationStatementId) throw new Error('应收结算单缺少对账单关联')
|
|
|
|
|
|
detail.value = current
|
|
|
|
|
|
receiptFile.value = null
|
|
|
|
|
|
receiptForm.value = { receivedDate: new Date().toISOString().slice(0, 10), amount: current.outstandingAmount, paymentMethod: 'BANK_TRANSFER', remark: '' }
|
|
|
|
|
|
receiptOpen.value = true
|
|
|
|
|
|
} catch (cause) { error.value = getApiErrorDetails(cause, '应收结算单加载失败,无法登记收款') }
|
|
|
|
|
|
finally { receiptBusy.value = false }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function selectReceiptFile(file: UploadFile) { receiptFile.value = file.raw ?? null }
|
|
|
|
|
|
|
|
|
|
|
|
async function submitReceipt() {
|
|
|
|
|
|
const current = detail.value
|
|
|
|
|
|
if (!current?.reconciliationStatementId || !receiptFile.value || receiptForm.value.amount <= 0) return ElMessage.warning('请填写收款金额并选择收款凭证')
|
|
|
|
|
|
receiptBusy.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
const voucher = await uploadFinanceVoucher(current.customerId, receiptFile.value)
|
|
|
|
|
|
await createFinanceReceipt({
|
|
|
|
|
|
customerId: current.customerId,
|
|
|
|
|
|
receivedDate: receiptForm.value.receivedDate,
|
|
|
|
|
|
amount: receiptForm.value.amount,
|
|
|
|
|
|
paymentMethod: receiptForm.value.paymentMethod,
|
|
|
|
|
|
remark: receiptForm.value.remark || undefined,
|
|
|
|
|
|
allocations: [{ billId: current.reconciliationStatementId, amount: receiptForm.value.amount }],
|
|
|
|
|
|
}, [voucher.fileId])
|
|
|
|
|
|
receiptOpen.value = false
|
|
|
|
|
|
ElMessage.success('收款已登记')
|
|
|
|
|
|
await load()
|
|
|
|
|
|
} catch (cause) { error.value = getApiErrorDetails(cause, '收款登记失败') }
|
|
|
|
|
|
finally { receiptBusy.value = false }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function submitPayment() {
|
|
|
|
|
|
if (!detail.value || !paymentFile.value || paymentForm.value.amount <= 0) return ElMessage.warning('请填写付款金额并选择付款凭证')
|
|
|
|
|
|
paymentBusy.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
const voucher = await uploadFinanceVoucher(detail.value.customerId, paymentFile.value)
|
|
|
|
|
|
await createFinanceSettlementPayment(detail.value.id, { ...paymentForm.value, currency: 'CNY', voucherFileIds: [voucher.fileId], expectedVersion: detail.value.version })
|
|
|
|
|
|
paymentOpen.value = false
|
|
|
|
|
|
ElMessage.success('付款已登记')
|
|
|
|
|
|
await load()
|
|
|
|
|
|
} catch (cause) { error.value = getApiErrorDetails(cause, '付款登记失败') }
|
|
|
|
|
|
finally { paymentBusy.value = false }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-11 19:35:28 +08:00
|
|
|
|
function customerName(customerId: string) { return customers.value.find((item) => String(item.id) === String(customerId))?.tenantName || '客户信息暂不可用' }
|
2026-08-12 09:25:55 +08:00
|
|
|
|
function totalAmount(row: FinanceReceivableSettlement | FinancePayableSettlement) { return props.kind === 'receivable' ? (row as FinanceReceivableSettlement).receivableAmount : (row as FinancePayableSettlement).payableAmount }
|
2026-08-11 18:43:56 +08:00
|
|
|
|
onMounted(() => void loadCustomers())
|
|
|
|
|
|
</script>
|
2026-08-12 09:25:55 +08:00
|
|
|
|
|
2026-08-11 18:43:56 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<AdminPage as="div" class="finance-settlement-page" spacing="normal">
|
|
|
|
|
|
<el-result v-if="!canView" icon="warning" :title="`无权访问${title}`" sub-title="请联系管理员开通结算单查看权限" />
|
|
|
|
|
|
<ListPageLayout v-else :aria-label="title">
|
2026-08-12 09:25:55 +08:00
|
|
|
|
<template #toolbar>
|
|
|
|
|
|
<QueryToolbar v-model="filters" class="surface-panel" :ariaLabel="`${title}筛选`" :fields="fields" preference-key="finance-settlement-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>
|
2026-08-11 18:43:56 +08:00
|
|
|
|
<section v-if="!customerError && !error" class="surface-panel table-panel">
|
|
|
|
|
|
<DataTable :data="rows" :columns="columns" :loading="loading" :aria-label="`${title}列表`" row-key="id" empty-text="请选择客户查询结算单">
|
2026-08-12 09:25:55 +08:00
|
|
|
|
<template #toolbar-actions><el-button v-if="canCreate" type="primary" @click="openCreate"><Plus :size="15" />{{ createLabel }}</el-button></template>
|
|
|
|
|
|
<template #actions="{ row }"><el-button link type="primary" @click="openDetail(row)"><Eye :size="15" />详情</el-button><el-button v-if="canReceive && ['ISSUED','PARTIALLY_PAID'].includes(String(row.status))" link :loading="receiptBusy" @click="openReceipt(row)"><HandCoins :size="15" />收款</el-button><el-button v-if="canPay && ['ISSUED','PARTIALLY_PAID'].includes(String(row.status))" link @click="openPayment(row)"><CreditCard :size="15" />付款</el-button></template>
|
2026-08-11 19:35:28 +08:00
|
|
|
|
<template #customer="{ row }">{{ customerName(String(row.customerId)) }}</template>
|
|
|
|
|
|
<template #status="{ row }"><el-tag :type="financeStatusTone(row.status)">{{ financeStatusLabel(row.status) }}</el-tag></template>
|
2026-08-11 19:59:15 +08:00
|
|
|
|
<template #pagination-left>币种:CNY</template>
|
2026-08-11 18:43:56 +08:00
|
|
|
|
</DataTable>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
</ListPageLayout>
|
2026-08-12 09:25:55 +08:00
|
|
|
|
|
|
|
|
|
|
<AppDrawer v-model="detailOpen" :title="`${title}详情`" size="min(760px, 94vw)">
|
|
|
|
|
|
<LoadErrorState v-if="detailError" :title="`${title}详情加载失败`" :message="detailError.message" :loading="detailLoading"><template #details><ApiErrorMeta :details="detailError" /></template></LoadErrorState>
|
|
|
|
|
|
<el-skeleton v-else-if="detailLoading" :rows="6" animated />
|
|
|
|
|
|
<template v-else-if="detail"><el-descriptions :column="2" border><el-descriptions-item label="结算单号">{{ detail.settlementNo }}</el-descriptions-item><el-descriptions-item label="客户">{{ customerName(detail.customerId) }}</el-descriptions-item><el-descriptions-item label="金额">{{ detail.amount }} CNY</el-descriptions-item><el-descriptions-item label="已收付">{{ detail.paidAmount }} CNY</el-descriptions-item><el-descriptions-item label="未收付">{{ detail.outstandingAmount }} CNY</el-descriptions-item><el-descriptions-item label="状态">{{ financeStatusLabel(detail.status) }}</el-descriptions-item></el-descriptions></template>
|
2026-08-11 18:43:56 +08:00
|
|
|
|
</AppDrawer>
|
2026-08-12 09:25:55 +08:00
|
|
|
|
|
|
|
|
|
|
<AppDialog v-model="createOpen" :title="`生成${title}`" width="min(560px, 94vw)" :confirm-loading="createBusy" @confirm="submitCreate">
|
|
|
|
|
|
<el-form label-position="top">
|
|
|
|
|
|
<el-form-item v-if="props.kind === 'receivable'" label="已出账对账单"><el-select v-model="createForm.reconciliationStatementId" filterable><el-option v-for="item in statementOptions" :key="item.id" :label="`${item.billNo} · ${item.outstandingAmount} CNY`" :value="item.id" /></el-select></el-form-item>
|
|
|
|
|
|
<template v-else><el-form-item label="供应商"><el-select v-model="createForm.supplierId" filterable placeholder="请选择供应商"><el-option v-for="item in supplierOptions" :key="String(item.id)" :label="`${item.partnerName} · ${item.partnerCode}`" :value="String(item.id)" /></el-select></el-form-item><el-form-item label="到期日"><el-date-picker v-model="createForm.dueDate" type="date" value-format="YYYY-MM-DD" /></el-form-item><el-form-item label="应付费用来源"><el-select v-model="createForm.feeSourceIds" multiple filterable><el-option v-for="item in feeOptions" :key="item.feeSourceId!" :label="`${item.feeItemName} · ${item.amount} CNY`" :value="item.feeSourceId!" /></el-select></el-form-item></template>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
</AppDialog>
|
|
|
|
|
|
|
|
|
|
|
|
<AppDialog v-model="paymentOpen" title="登记付款" width="min(560px, 94vw)" :confirm-loading="paymentBusy" @confirm="submitPayment">
|
|
|
|
|
|
<el-form label-position="top"><el-form-item label="付款日期"><el-date-picker v-model="paymentForm.transactionDate" type="date" value-format="YYYY-MM-DD" /></el-form-item><el-form-item label="付款金额(CNY)"><el-input-number v-model="paymentForm.amount" :min="0.01" :max="detail?.outstandingAmount || 0.01" :precision="2" /></el-form-item><el-form-item label="付款方式"><el-select v-model="paymentForm.method"><el-option label="银行转账" value="BANK_TRANSFER" /><el-option label="现金" value="CASH" /><el-option label="其他" value="OTHER" /></el-select></el-form-item><el-form-item label="凭证号"><el-input v-model="paymentForm.referenceNo" /></el-form-item><el-form-item label="付款凭证"><el-upload :auto-upload="false" :limit="1" accept="image/*" :on-change="selectPaymentFile"><el-button>选择图片</el-button></el-upload></el-form-item><el-form-item label="备注"><el-input v-model="paymentForm.remark" type="textarea" /></el-form-item></el-form>
|
|
|
|
|
|
</AppDialog>
|
|
|
|
|
|
|
|
|
|
|
|
<AppDialog v-model="receiptOpen" title="登记收款" width="min(560px, 94vw)" :confirm-loading="receiptBusy" @confirm="submitReceipt">
|
|
|
|
|
|
<el-form label-position="top"><el-form-item label="到账日期"><el-date-picker v-model="receiptForm.receivedDate" type="date" value-format="YYYY-MM-DD" /></el-form-item><el-form-item label="收款金额(CNY)"><el-input-number v-model="receiptForm.amount" :min="0.01" :max="detail?.outstandingAmount || 0.01" :precision="2" /></el-form-item><el-form-item label="收款方式"><el-select v-model="receiptForm.paymentMethod"><el-option label="银行转账" value="BANK_TRANSFER" /><el-option label="现金" value="CASH" /><el-option label="其他" value="OTHER" /></el-select></el-form-item><el-form-item label="收款凭证"><el-upload :auto-upload="false" :limit="1" accept="image/*" :on-change="selectReceiptFile"><el-button>选择图片</el-button></el-upload></el-form-item><el-form-item label="备注"><el-input v-model="receiptForm.remark" type="textarea" /></el-form-item></el-form>
|
|
|
|
|
|
</AppDialog>
|
2026-08-11 18:43:56 +08:00
|
|
|
|
</AdminPage>
|
|
|
|
|
|
</template>
|
2026-08-12 09:25:55 +08:00
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.finance-settlement-page { min-width: 0; }
|
|
|
|
|
|
:deep(.el-select), :deep(.el-date-editor) { width: 100%; }
|
|
|
|
|
|
@media (max-width: 720px) { :deep(.el-drawer__footer .el-button) { min-height: 44px; } }
|
|
|
|
|
|
</style>
|