feat(web-admin): finance confirm by transfer and remove refund confirm tab

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-10-03 11:17:19 +08:00
1 parent 4e71a13165
commit c404c168fe
7 files changed
+133 -81

No files matched your search

@@ -52,6 +52,8 @@ export function fetchPaymentTransferReceiptConfirmList(params?: {
page?: number
pageSize?: number
keyword?: string
installmentDueDateStart?: string
installmentDueDateEnd?: string
}) {
return request.get<PageList<Record<string, unknown>>>({
url: '/web/admin/payment-transfers/receipt-confirm',
@@ -63,6 +65,8 @@ export function fetchPaymentTransferReceiptConfirmedList(params?: {
page?: number
pageSize?: number
keyword?: string
installmentDueDateStart?: string
installmentDueDateEnd?: string
}) {
return request.get<PageList<Record<string, unknown>>>({
url: '/web/admin/payment-transfers/receipt-confirmed',
+3 -1
View File
@@ -89,7 +89,7 @@ export const sqxpAdminRoutes: AppRouteRecord[] = [
path: 'payment-confirm',
component: '/cell-storage/payment-confirm/index',
meta: {
title: '缴退费财务确认及开票',
title: '缴费财务确认及开票',
keepAlive: true,
menuCode: 'biz:cellStoragePaymentConfirm:list'
}
@@ -290,6 +290,7 @@ export const sqxpAdminRoutes: AppRouteRecord[] = [
meta: {
title: '保险公司及险种价格',
keepAlive: true,
isHide: true,
menuCode: 'basic-info:insurance-company:list'
}
},
@@ -398,6 +399,7 @@ export const sqxpAdminRoutes: AppRouteRecord[] = [
meta: {
title: '体检机构及项目价格',
keepAlive: true,
isHide: true,
menuCode: 'pilot-service:checkup-institution:list'
}
},
@@ -1,4 +1,4 @@
<!-- 缴退费财务确认 · 退费 Tab -->
<!-- 已停用:按样本缴费后退费改为协议级登记,财务页不再挂载本 Tab(保留文件供旧版对照) -->
<!-- eslint-disable vue/no-v-model-argument -->
<template>
<div class="refund-confirm-tab">
@@ -1,4 +1,4 @@
<!-- 缴退费财务确认 -->
<!-- 缴费财务确认及开票(按转账笔;退费确认 Tab 已下线) -->
<!-- eslint-disable vue/no-v-model-argument -->
<template>
<div class="page-container">
@@ -18,19 +18,7 @@
formatTabBadgeCount(paymentPendingBadge)
}}</span>
</button>
<button
type="button"
role="tab"
class="panel-pill-tabs__btn"
:class="{ 'is-active': mainTab === 'refund' }"
:aria-selected="mainTab === 'refund'"
@click="mainTab = 'refund'"
>
退费确认
<span v-if="refundPendingBadge > 0" class="panel-pill-tabs__badge">{{
formatTabBadgeCount(refundPendingBadge)
}}</span>
</button>
<!-- TODO(P1): 开票改挂 transfer;当前仍走旧 payment_detail 开票 API -->
<button
type="button"
role="tab"
@@ -68,7 +56,7 @@
<el-form-item label="关键词">
<el-input
v-model="pendingSearchForm.keyword"
placeholder="客户姓名/身份证号/新生儿姓名/新生儿身份证号"
placeholder="协议号/样本编码/客户姓名/身份证号"
clearable
@keyup.enter="handlePendingSearch"
/>
@@ -105,20 +93,23 @@
<div class="pending-card__identity">
<div class="pending-card__name">{{ row.motherName || '-' }}</div>
<div class="pending-card__sub">
{{ maskIdCard(row.motherIdCard) }}
<template v-if="row.newbornName"> · 新生儿 {{ row.newbornName }} </template>
{{ row.protocolNo || '-' }}
<template v-if="row.sampleCode"> · {{ row.sampleCode }}</template>
<template v-if="row.motherIdCard">
· {{ maskIdCard(row.motherIdCard) }}
</template>
</div>
</div>
<div class="pending-card__amount">
<div class="pending-card__amount-val">
{{ formatPaymentAmount(row.transferAmount) }}
</div>
<div class="pending-card__amount-label">实缴金额</div>
<div class="pending-card__amount-label">转账金额</div>
</div>
</div>
<div class="pending-card__grid">
<div class="pending-card__field">
<label>期数</label>
<label>期次</label>
<span>第 {{ row.installmentNo ?? '-' }} 期</span>
</div>
<div class="pending-card__field">
@@ -126,7 +117,7 @@
<span>{{ formatPaymentDueDate(row.installmentDueDate) }}</span>
</div>
<div class="pending-card__field">
<label>应缴金额</label>
<label>批次应缴</label>
<span>{{ formatPaymentAmount(row.dueAmount) }}</span>
</div>
<div class="pending-card__field">
@@ -141,6 +132,10 @@
<label>转账时间</label>
<span>{{ formatDisplayOrDash(row.transferTime) }}</span>
</div>
<div class="pending-card__field">
<label>来源</label>
<span>{{ row.source === 'presign' ? '预签约意向金' : '正式协议' }}</span>
</div>
</div>
<div class="pending-card__foot">
<div class="pending-card__meta">
@@ -195,7 +190,7 @@
<el-form-item label="关键词">
<el-input
v-model="confirmedSearchForm.keyword"
placeholder="客户姓名/身份证号/新生儿姓名/新生儿身份证号"
placeholder="协议号/样本编码/客户姓名/身份证号"
clearable
@keyup.enter="handleConfirmedSearch"
/>
@@ -244,6 +239,24 @@
(index) => (confirmedPagination.page - 1) * confirmedPagination.pageSize + index + 1
"
/>
<el-table-column
prop="protocolNo"
label="协议号"
min-width="140"
align="center"
header-align="center"
>
<template #default="{ row }">{{ row.protocolNo || '-' }}</template>
</el-table-column>
<el-table-column
prop="sampleCode"
label="样本编码"
min-width="120"
align="center"
header-align="center"
>
<template #default="{ row }">{{ row.sampleCode || '-' }}</template>
</el-table-column>
<el-table-column
prop="motherName"
label="客户姓名"
@@ -263,22 +276,17 @@
<template #default="{ row }">{{ maskIdCard(row.motherIdCard) }}</template>
</el-table-column>
<el-table-column
prop="newbornName"
label="新生儿姓名"
min-width="110"
prop="source"
label="来源"
min-width="100"
align="center"
header-align="center"
>
<template #default="{ row }">{{ row.newbornName || '-' }}</template>
</el-table-column>
<el-table-column
prop="newbornIdCard"
label="新生儿身份证号"
min-width="170"
align="center"
header-align="center"
>
<template #default="{ row }">{{ maskIdCard(row.newbornIdCard) }}</template>
<template #default="{ row }">
<el-tag size="small" :type="row.source === 'presign' ? 'warning' : 'primary'">
{{ row.source === 'presign' ? '预签约' : '正式' }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="protocolStatus"
@@ -291,7 +299,12 @@
<el-tag
:type="protocolStatusTagType(String(row.protocolStatus || ''))"
size="small"
>{{ protocolStatusDisplayLabel(String(row.protocolStatus || ''), String(row.voidType || '')) }}</el-tag
>{{
protocolStatusDisplayLabel(
String(row.protocolStatus || ''),
String(row.voidType || '')
)
}}</el-tag
>
</template>
</el-table-column>
@@ -378,9 +391,10 @@
label="确认时间"
min-width="170"
align="center"
header-align="center">
<template #default="{ row }">{{ formatDisplayOrDash(row.updateTime) }}</template>
</el-table-column>
header-align="center"
>
<template #default="{ row }">{{ formatDisplayOrDash(row.updateTime) }}</template>
</el-table-column>
<el-table-column
prop="financeRemark"
label="财务备注"
@@ -457,11 +471,7 @@
<el-icon><Close /></el-icon>
取消
</el-button>
<el-button
:loading="rejectLoading"
:disabled="submitLoading"
@click="submitReject"
>
<el-button :loading="rejectLoading" :disabled="submitLoading" @click="submitReject">
<el-icon><RefreshLeft /></el-icon>
退回
</el-button>
@@ -503,7 +513,6 @@
</el-dialog>
</div>
<RefundConfirmTab v-if="mainTab === 'refund'" @action-done="loadTabPendingCounts" />
<InvoiceRequestTab v-if="mainTab === 'invoice'" @action-done="loadTabPendingCounts" />
</section>
</div>
@@ -514,14 +523,13 @@
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Close, Check, RefreshLeft, Search, View } from '@element-plus/icons-vue'
import { fetchProtocolInvoicePendingCount } from '@/api/collection-protocol'
import {
fetchReceiptConfirmList,
fetchReceiptConfirmedList,
fetchReceiptConfirmSubmit,
fetchReceiptConfirmReject,
fetchRefundConfirmList,
fetchProtocolInvoicePendingCount
} from '@/api/collection-protocol'
fetchPaymentTransferReceiptConfirmList,
fetchPaymentTransferReceiptConfirmedList,
fetchConfirmPaymentTransfer,
fetchRejectPaymentTransfer
} from '@/api/protocol-sample-payment'
import ProtocolFinanceConfirmFields from '@/components/business/protocol-finance-confirm-fields/index.vue'
import ClinicalPaymentConfirmTimeline from '@/components/business/clinical-payment-confirm-timeline/index.vue'
import {
@@ -533,14 +541,12 @@
import {
formatPaymentDueDate,
payModeLabel,
protocolStatusLabel,
protocolStatusDisplayLabel,
protocolStatusTagType,
loadTransferTypeDict,
transferTypeLabel
} from '@/utils/protocol-payment-labels'
import { formatMoneyDisplay } from '@/utils/money-format'
import RefundConfirmTab from './RefundConfirmTab.vue'
import InvoiceRequestTab from './InvoiceRequestTab.vue'
defineOptions({ name: 'CellStoragePaymentConfirm' })
@@ -548,14 +554,12 @@
const route = useRoute()
const mainTab = ref('payment')
const paymentPendingBadge = ref(0)
const refundPendingBadge = ref(0)
const invoicePendingBadge = ref(0)
function formatTabBadgeCount(count: number): string {
return count > 99 ? '99+' : String(count)
}
function formatPaymentAmount(v: unknown): string {
if (v == null || v === '') return '-'
return '¥' + formatMoneyDisplay(v)
@@ -591,12 +595,6 @@
return minioBaseUrl ? `${minioBaseUrl}/${url.replace(/^\//, '')}` : url
}
function handleAttachmentPreview(file: Record<string, unknown>) {
const url = String(file.fileUrl || '')
if (!url) return
window.open(fullFileUrl(url), '_blank')
}
function handleAttachmentDownload(file: Record<string, unknown>) {
downloadAttachmentFile(file, { fullFileUrl })
}
@@ -623,13 +621,11 @@
async function loadTabPendingCounts() {
try {
const [paymentRes, refundRes, invRes] = await Promise.all([
fetchReceiptConfirmList({ page: 1, pageSize: 1 }),
fetchRefundConfirmList({ page: 1, pageSize: 1 }),
const [paymentRes, invRes] = await Promise.all([
fetchPaymentTransferReceiptConfirmList({ page: 1, pageSize: 1 }),
fetchProtocolInvoicePendingCount()
])
paymentPendingBadge.value = paymentRes.total ?? 0
refundPendingBadge.value = refundRes.total ?? 0
invoicePendingBadge.value = Number(invRes?.count ?? 0)
} catch {
// 角标加载失败时保留上次数值,避免干扰主流程
@@ -639,7 +635,7 @@
async function loadPending() {
pendingLoading.value = true
try {
const res = await fetchReceiptConfirmList(buildPendingParams())
const res = await fetchPaymentTransferReceiptConfirmList(buildPendingParams())
pendingData.value = res.list || []
pendingPagination.total = res.total ?? 0
} finally {
@@ -650,7 +646,7 @@
async function loadConfirmed() {
confirmedLoading.value = true
try {
const res = await fetchReceiptConfirmedList(buildConfirmedParams())
const res = await fetchPaymentTransferReceiptConfirmedList(buildConfirmedParams())
confirmedData.value = res.list || []
confirmedPagination.total = res.total ?? 0
} finally {
@@ -707,7 +703,7 @@
}
submitLoading.value = true
try {
await fetchReceiptConfirmSubmit(currentDetailId.value, {
await fetchConfirmPaymentTransfer(currentDetailId.value, {
financeConfirmAttachments: financeAttachmentsToUrlString(attachments.value) || undefined,
financeRemark: financeRemark.value.trim() || undefined
})
@@ -730,7 +726,7 @@
}
rejectLoading.value = true
try {
await fetchReceiptConfirmReject(currentDetailId.value)
await fetchRejectPaymentTransfer(currentDetailId.value)
dialogVisible.value = false
await loadAll()
await loadTabPendingCounts()
@@ -760,10 +756,10 @@
onMounted(async () => {
await loadTransferTypeDict()
const tab = route.query.tab
if (tab === 'refund') {
mainTab.value = 'refund'
} else if (tab === 'invoice') {
if (tab === 'invoice') {
mainTab.value = 'invoice'
} else {
mainTab.value = 'payment'
}
loadTabPendingCounts()
if (mainTab.value === 'payment') {