feat(web-admin): payment-info 缴费明细 dialog with list-mode transfers

Open sample-level detail from the list; manage transfers like
SamplePaymentPanel; remove direct PaymentReceiptConfirmDialog path.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-10-10 15:15:56 +08:00
1 parent ea5fba9dc2
commit 842aa0847f
2 files changed
+846 -102

No files matched your search

@@ -169,11 +169,11 @@
<el-icon><Document /></el-icon>协议单
</el-button>
<el-button
v-if="row.canConfirm"
class="btn-op-item primary"
@click="handleConfirm(row)"
:disabled="!row.id"
@click="handleOpenDetail(row)"
>
<el-icon><Check /></el-icon>缴费确认
<el-icon><List /></el-icon>缴费明细
</el-button>
</template>
</el-table-column>
@@ -253,25 +253,13 @@
<el-button class="btn-ghost" @click="periodDialogVisible = false">
<el-icon><Close /></el-icon>关闭
</el-button>
<el-button
v-if="periodCanConfirm"
class="btn-main"
type="primary"
:loading="confirming"
@click="openConfirmFromPeriod"
>
<el-icon><Check /></el-icon>缴费确认
</el-button>
</template>
</el-dialog>
<PaymentReceiptConfirmDialog
v-model="receiptDialogVisible"
:preview-row="receiptPreviewRow"
:is-transfer="true"
:target-id="receiptTargetId"
@saved="onReceiptRefresh"
@submitted="onReceiptRefresh"
<SamplePaymentDetailDialog
v-model="detailVisible"
:sample-payment-id="detailSamplePaymentId"
@changed="loadData"
/>
<el-dialog
@@ -303,16 +291,12 @@
</template>
<script setup lang="ts">
import { Search, RefreshLeft, Check, Document, Close } from '@element-plus/icons-vue'
import { Search, RefreshLeft, Document, Close, List } from '@element-plus/icons-vue'
import ProtocolViewDialog from '../protocol/ProtocolViewDialog.vue'
import ProtocolAttachmentReadonly from '../protocol/ProtocolAttachmentReadonly.vue'
import PaymentReceiptConfirmDialog from '@/components/business/payment-receipt-confirm-dialog/index.vue'
import SamplePaymentDetailDialog from '@/components/business/sample-payment-detail-dialog/index.vue'
import { downloadAttachmentFile } from '@/utils/download-attachment'
import {
fetchSamplePaymentList,
fetchSamplePaymentDetail,
fetchCreatePaymentTransfer
} from '@/api/protocol-sample-payment'
import { fetchSamplePaymentList, fetchSamplePaymentDetail } from '@/api/protocol-sample-payment'
import { type CollectionProtocolRow } from '@/api/collection-protocol'
import { formatDisplayOrDash } from '@/utils/datetime-display'
import { formatMoneyDisplay } from '@/utils/money-format'
@@ -366,14 +350,9 @@
arrearsAmount: null as number | null
})
const periodTransfers = ref<Record<string, any>[]>([])
const periodCanConfirm = ref(false)
const periodSchedule = ref<Record<string, any> | null>(null)
const periodSamplePaymentId = ref('')
const confirming = ref(false)
const receiptDialogVisible = ref(false)
const receiptPreviewRow = ref<Record<string, any> | null>(null)
const receiptTargetId = ref('')
const detailVisible = ref(false)
const detailSamplePaymentId = ref('')
const protocolVisible = ref(false)
const protocolViewId = ref('')
@@ -513,93 +492,32 @@
async function openPeriodDialog(row: Record<string, any>, idx: number) {
const cell = periodRaw(row, idx)
if (!cell?.scheduleId) return
periodSamplePaymentId.value = String(row.id || '')
periodDialogTitle.value = `${row.motherName || ''} · ${row.protocolNo || ''} · 第 ${idx + 1} 期转账记录`
periodDialogVisible.value = true
periodLoading.value = true
periodTransfers.value = []
periodCanConfirm.value = false
periodSchedule.value = null
try {
const detail = await fetchSamplePaymentDetail(String(row.id))
const schedules = ((detail.schedules as any[]) || []) as Record<string, any>[]
const sch = schedules.find((s) => String(s.id) === String(cell.scheduleId))
periodSchedule.value = sch || null
periodSummary.dueAmount = sch?.dueAmount ?? cell.dueAmount ?? null
// 列表 displayPaidAmount / arrearsAmount 与详情 paidAmount 已对齐(含 dfj+jfqrz+yjf)
periodSummary.paidAmount =
sch?.paidAmount ?? cell.displayPaidAmount ?? null
periodSummary.arrearsAmount =
sch?.arrearsAmount ?? cell.arrearsAmount ?? null
periodSummary.paidAmount = sch?.paidAmount ?? cell.displayPaidAmount ?? null
periodSummary.arrearsAmount = sch?.arrearsAmount ?? cell.arrearsAmount ?? null
periodTransfers.value = (sch?.transfers || []) as Record<string, any>[]
const hasJfqrz = periodTransfers.value.some((t) => String(t.payStatus) === 'jfqrz')
const arrears = Number(periodSummary.arrearsAmount)
periodCanConfirm.value = !hasJfqrz && Number.isFinite(arrears) && arrears > 0
} finally {
periodLoading.value = false
}
}
async function openConfirmFromPeriod() {
const schedule = periodSchedule.value
if (!schedule?.id) return
confirming.value = true
try {
const transfers = (schedule.transfers || []) as Record<string, any>[]
const dfj = transfers.find((t) => String(t.payStatus) === 'dfj')
let transferId = dfj?.id ? String(dfj.id) : ''
if (!transferId) {
const arrears = Number(schedule.arrearsAmount)
const due = Number(schedule.dueAmount)
const amount = arrears > 0 ? arrears : due
const created = await fetchCreatePaymentTransfer({
scheduleId: String(schedule.id),
transferAmount: amount
})
transferId = String((created as any)?.id || (created as any)?.data?.id || '')
if (!transferId) {
ElMessage.error('创建转账笔失败')
return
}
}
receiptTargetId.value = transferId
receiptPreviewRow.value = {
motherName: periodDialogTitle.value,
installmentNo: schedule.scheduleNo,
dueAmount: schedule.dueAmount,
installmentDueDate: schedule.dueDate,
scheduleId: schedule.id,
transferAmount: schedule.arrearsAmount || schedule.dueAmount
}
receiptDialogVisible.value = true
periodDialogVisible.value = false
} finally {
confirming.value = false
}
}
async function handleConfirm(row: Record<string, any>) {
const detail = await fetchSamplePaymentDetail(String(row.id))
const schedules = ((detail.schedules as any[]) || []) as Record<string, any>[]
const target = [...schedules]
.sort((a, b) => Number(a.scheduleNo || 0) - Number(b.scheduleNo || 0))
.find((s) => {
const transfers = (s.transfers || []) as any[]
if (transfers.some((t) => String(t.payStatus) === 'jfqrz')) return false
return Number(s.arrearsAmount) > 0
})
if (!target) {
ElMessage.warning('暂无可确认的期次')
function handleOpenDetail(row: Record<string, any>) {
const id = String(row.id || '')
if (!id) {
ElMessage.warning('缺少缴费方案,无法查看明细')
return
}
periodSamplePaymentId.value = String(row.id)
periodSchedule.value = target
periodDialogTitle.value = `${row.motherName || ''} · ${row.protocolNo || ''} · 第 ${target.scheduleNo} 期`
await openConfirmFromPeriod()
}
function onReceiptRefresh() {
loadData()
detailSamplePaymentId.value = id
detailVisible.value = true
}
onMounted(() => {