feat(web-admin): add presign order UI and menu entry

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-10-03 11:21:23 +08:00
1 parent fdd2c1fbc3
commit a9112414c9
7 files changed
+950 -8

No files matched your search

+7
View File
@@ -53,6 +53,13 @@ STATIC_MENU_TREE = [
"component": "/cell-storage/protocol-audit/index",
"meta": {"title": "采集协议单", "keepAlive": True, "menuCode": "biz:cellStorageProtocolAudit:list"},
},
{
"id": "50000000000000000000000000000020",
"name": "CellStoragePresignOrder",
"path": "presign-order",
"component": "/cell-storage/presign-order/index",
"meta": {"title": "预签约订单", "keepAlive": True, "menuCode": "cell-storage:presign-order:list"},
},
{
"id": "50000000000000000000000000000013",
"name": "CellStorageSampleInfo",
+108
View File
@@ -0,0 +1,108 @@
import request from '@/utils/http'
type PageList<T> = {
list: T[]
total: number
page: number
pageSize: number
}
export function fetchPresignOrderList(params?: {
page?: number
pageSize?: number
keyword?: string
orderStatus?: string
}) {
return request.get<PageList<Record<string, unknown>>>({
url: '/web/admin/presign-orders',
params
})
}
export function fetchPresignOrderDetail(id: string) {
return request.get<Record<string, unknown>>({
url: `/web/admin/presign-orders/${id}`
})
}
export function fetchCreatePresignOrder(data: Record<string, unknown>) {
return request.post({
url: '/web/admin/presign-orders',
data,
showSuccessMessage: true
})
}
export function fetchUpdatePresignOrder(id: string, data: Record<string, unknown>) {
return request.put({
url: `/web/admin/presign-orders/${id}`,
data,
showSuccessMessage: true
})
}
export function fetchDeletePresignOrder(id: string) {
return request.del({
url: `/web/admin/presign-orders/${id}`,
showSuccessMessage: true
})
}
export function fetchConvertPresignOrder(id: string) {
return request.post({
url: `/web/admin/presign-orders/${id}/convert`,
showSuccessMessage: true
})
}
export function fetchCreatePresignTransfer(orderId: string, data: Record<string, unknown>) {
return request.post({
url: `/web/admin/presign-orders/${orderId}/transfers`,
data,
showSuccessMessage: true
})
}
export function fetchUpdatePresignTransfer(transferId: string, data: Record<string, unknown>) {
return request.put({
url: `/web/admin/presign-transfers/${transferId}`,
data,
showSuccessMessage: false
})
}
export function fetchSubmitPresignTransferFinance(transferId: string) {
return request.post({
url: `/web/admin/presign-transfers/${transferId}/submit-receipt-finance`,
showSuccessMessage: true
})
}
export function fetchPresignTransferReceiptConfirmList(params?: {
page?: number
pageSize?: number
keyword?: string
}) {
return request.get<PageList<Record<string, unknown>>>({
url: '/web/admin/presign-transfers/receipt-confirm',
params
})
}
export function fetchConfirmPresignTransfer(
transferId: string,
data?: { financeConfirmAttachments?: string; financeRemark?: string }
) {
return request.post({
url: `/web/admin/presign-transfers/${transferId}/receipt-confirm`,
data: data || {},
showSuccessMessage: true
})
}
export function fetchRejectPresignTransfer(transferId: string) {
return request.post({
url: `/web/admin/presign-transfers/${transferId}/receipt-reject`,
showSuccessMessage: true
})
}
+11
View File
@@ -60,6 +60,17 @@ export const sqxpAdminRoutes: AppRouteRecord[] = [
menuCode: 'biz:cellStorageProtocolAudit:list'
}
},
{
id: '50000000000000000000000000000020',
name: 'CellStoragePresignOrder',
path: 'presign-order',
component: '/cell-storage/presign-order/index',
meta: {
title: '预签约订单',
keepAlive: true,
menuCode: 'cell-storage:presign-order:list'
}
},
{
id: '50000000000000000000000000000013',
name: 'CellStorageSampleInfo',
@@ -381,6 +381,16 @@ export const sex: StatusRegistry = {
export const sexLabel = createLabelGetter(sex)
export const sexTagType = createTagTypeGetter(sex)
/** 预签约订单状态 */
export const presignOrderStatus: StatusRegistry = {
draft: { label: '草稿', tagType: 'info' },
active: { label: '进行中', tagType: 'warning' },
converted: { label: '已转正式', tagType: 'success' },
void: { label: '已作废', tagType: 'danger' }
}
export const presignOrderStatusLabel = createLabelGetter(presignOrderStatus)
export const presignOrderStatusTagType = createTagTypeGetter(presignOrderStatus)
/** 请假状态 */
export const leaveStatus: StatusRegistry = {
draft: { label: '草稿', tagType: 'info' },
@@ -530,6 +530,11 @@
fetchConfirmPaymentTransfer,
fetchRejectPaymentTransfer
} from '@/api/protocol-sample-payment'
import {
fetchPresignTransferReceiptConfirmList,
fetchConfirmPresignTransfer,
fetchRejectPresignTransfer
} from '@/api/presign-order'
import ProtocolFinanceConfirmFields from '@/components/business/protocol-finance-confirm-fields/index.vue'
import ClinicalPaymentConfirmTimeline from '@/components/business/clinical-payment-confirm-timeline/index.vue'
import {
@@ -621,11 +626,12 @@
async function loadTabPendingCounts() {
try {
const [paymentRes, invRes] = await Promise.all([
const [paymentRes, presignRes, invRes] = await Promise.all([
fetchPaymentTransferReceiptConfirmList({ page: 1, pageSize: 1 }),
fetchPresignTransferReceiptConfirmList({ page: 1, pageSize: 1 }),
fetchProtocolInvoicePendingCount()
])
paymentPendingBadge.value = paymentRes.total ?? 0
paymentPendingBadge.value = (paymentRes.total ?? 0) + (presignRes.total ?? 0)
invoicePendingBadge.value = Number(invRes?.count ?? 0)
} catch {
// 角标加载失败时保留上次数值,避免干扰主流程
@@ -635,9 +641,18 @@
async function loadPending() {
pendingLoading.value = true
try {
const res = await fetchPaymentTransferReceiptConfirmList(buildPendingParams())
pendingData.value = res.list || []
pendingPagination.total = res.total ?? 0
// 正式 + 预签约意向金待办合并(前端 merge,分页近似:各取一页后合并)
const [formal, presign] = await Promise.all([
fetchPaymentTransferReceiptConfirmList(buildPendingParams()),
fetchPresignTransferReceiptConfirmList({
page: pendingPagination.page,
pageSize: pendingPagination.pageSize,
keyword: pendingSearchForm.keyword || undefined
})
])
const merged = [...(formal.list || []), ...(presign.list || [])]
pendingData.value = merged
pendingPagination.total = (formal.total ?? 0) + (presign.total ?? 0)
} finally {
pendingLoading.value = false
}
@@ -703,10 +718,15 @@
}
submitLoading.value = true
try {
await fetchConfirmPaymentTransfer(currentDetailId.value, {
const payload = {
financeConfirmAttachments: financeAttachmentsToUrlString(attachments.value) || undefined,
financeRemark: financeRemark.value.trim() || undefined
})
}
if (confirmRow.value?.source === 'presign') {
await fetchConfirmPresignTransfer(currentDetailId.value, payload)
} else {
await fetchConfirmPaymentTransfer(currentDetailId.value, payload)
}
dialogVisible.value = false
await loadAll()
await loadTabPendingCounts()
@@ -726,7 +746,11 @@
}
rejectLoading.value = true
try {
await fetchRejectPaymentTransfer(currentDetailId.value)
if (confirmRow.value?.source === 'presign') {
await fetchRejectPresignTransfer(currentDetailId.value)
} else {
await fetchRejectPaymentTransfer(currentDetailId.value)
}
dialogVisible.value = false
await loadAll()
await loadTabPendingCounts()
@@ -0,0 +1,492 @@
<!-- 预签约订单 新增/编辑 -->
<!-- eslint-disable vue/no-v-model-argument -->
<template>
<el-dialog
:model-value="modelValue"
:title="orderId ? '编辑预签约订单' : '新增预签约订单'"
width="920px"
align-center
destroy-on-close
class="dialog-form-layout"
modal-class="order-module-dialog-mask"
@update:model-value="emit('update:modelValue', $event)"
@closed="resetForm"
>
<DialogFormBody v-loading="loading">
<el-form ref="formRef" :model="form" label-position="top" scroll-to-error>
<div class="form-grid-2">
<el-form-item label="业务日期">
<el-date-picker
v-model="form.orderDate"
type="date"
value-format="YYYYMMDD"
format="YYYY-MM-DD"
clearable
style="width: 100%"
/>
</el-form-item>
<el-form-item label="协议编号">
<el-input v-model="form.protocolNo" placeholder="可选,转正式时可预占" clearable />
</el-form-item>
<el-form-item label="母亲姓名" required>
<el-input v-model="form.motherName" placeholder="请输入母亲姓名" clearable />
</el-form-item>
<el-form-item label="预产期">
<el-date-picker
v-model="form.expectedDeliveryDate"
type="date"
value-format="YYYYMMDD"
format="YYYY-MM-DD"
clearable
style="width: 100%"
/>
</el-form-item>
<el-form-item label="采集地区">
<el-select
v-model="form.regionId"
filterable
clearable
placeholder="请选择"
style="width: 100%"
@change="onRegionChange"
>
<el-option
v-for="opt in regionOptions"
:key="opt.id"
:label="opt.regionName"
:value="opt.id"
/>
</el-select>
</el-form-item>
<el-form-item label="医院">
<el-select
v-model="form.hospitalId"
filterable
clearable
placeholder="请选择"
style="width: 100%"
:disabled="!form.regionId"
>
<el-option
v-for="opt in hospitalOptions"
:key="opt.id"
:label="opt.hospitalName"
:value="opt.id"
/>
</el-select>
</el-form-item>
<el-form-item label="采集时间">
<el-date-picker
v-model="form.sampleCollectTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
clearable
style="width: 100%"
/>
</el-form-item>
<el-form-item label="缴费方式">
<el-select
v-model="form.payMode"
clearable
placeholder="整单初步方式"
style="width: 100%"
>
<el-option label="趸交" value="dj" />
<el-option label="分期" value="fq" />
</el-select>
</el-form-item>
<el-form-item label="整单合同金额(元)">
<AmountInput v-model="form.contractAmount" :min="0" style="width: 100%" />
</el-form-item>
<el-form-item label="订单状态">
<el-select v-model="form.orderStatus" style="width: 100%" :disabled="isConverted">
<el-option label="草稿" value="draft" />
<el-option label="进行中" value="active" />
<el-option label="已作废" value="void" />
</el-select>
</el-form-item>
<el-form-item label="样本类型" class="form-col-span-2" required>
<el-select
v-model="form.sampleTypeIds"
multiple
filterable
clearable
placeholder="可多选样本类型"
style="width: 100%"
>
<el-option
v-for="opt in sampleTypeOptions"
:key="opt.id"
:label="opt.typeName || opt.sampleTypeName"
:value="opt.id"
/>
</el-select>
</el-form-item>
<el-form-item label="备注" class="form-col-span-2">
<el-input
v-model="form.remark"
type="textarea"
:rows="2"
maxlength="500"
show-word-limit
/>
</el-form-item>
</div>
<div class="section-subtitle">意向金转账</div>
<div style="margin-bottom: 10px">
<el-button type="primary" size="small" :disabled="isConverted" @click="addTransfer">
<el-icon><Plus /></el-icon>
添加转账笔
</el-button>
<span v-if="confirmedDeposit > 0" class="deposit-hint">
已确认意向金合计 ¥{{ formatMoneyDisplay(confirmedDeposit) }}
</span>
</div>
<div
v-for="(tr, idx) in form.transfers"
:key="tr.id || idx"
class="transfer-card form-grid-2"
>
<el-form-item label="转账方式">
<el-select
v-model="tr.transferType"
:disabled="isTransferLocked(tr) || isConverted"
style="width: 100%"
>
<el-option
v-for="opt in transferTypeOptions"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
</el-form-item>
<el-form-item label="转账金额(元)">
<AmountInput
v-model="tr.transferAmount"
:min="0"
:disabled="isTransferLocked(tr) || isConverted"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="转账时间">
<el-date-picker
v-model="tr.transferTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:disabled="isTransferLocked(tr) || isConverted"
clearable
style="width: 100%"
/>
</el-form-item>
<el-form-item label="状态 / 操作">
<el-tag :type="payStatusTagType(String(tr.payStatus || 'dfj'))" size="small">
{{ payStatusLabel(String(tr.payStatus || 'dfj')) }}
</el-tag>
<el-button
v-if="tr.id && tr.payStatus === 'dfj' && !isConverted"
link
type="primary"
size="small"
style="margin-left: 8px"
@click="submitTransfer(tr)"
>
提交财务确认
</el-button>
<el-button
v-if="!isTransferLocked(tr) && !isConverted"
link
type="danger"
size="small"
style="margin-left: 4px"
@click="form.transfers.splice(idx, 1)"
>
删除
</el-button>
</el-form-item>
</div>
<el-empty v-if="!form.transfers.length" description="暂无意向金转账笔" :image-size="40" />
</el-form>
</DialogFormBody>
<template #footer>
<el-button class="btn-ghost" @click="emit('update:modelValue', false)">
<el-icon><Close /></el-icon>
取消
</el-button>
<el-button
class="btn-main"
type="primary"
:loading="saving"
:disabled="isConverted"
@click="handleSave"
>
<el-icon><Check /></el-icon>
确定
</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { computed, reactive, ref, watch } from 'vue'
import { Plus, Close, Check } from '@element-plus/icons-vue'
import AmountInput from '@/components/common/amount-input/index.vue'
import { formatMoneyDisplay } from '@/utils/money-format'
import {
payStatusLabel,
payStatusTagType,
loadTransferTypeDict,
getTransferTypeOptions
} from '@/utils/protocol-payment-labels'
import DialogFormBody from '@/components/business/dialog-form-body/index.vue'
import {
fetchCellSampleTypeList,
fetchCollectionRegionList,
fetchHospitalList
} from '@/api/admin'
import {
fetchPresignOrderDetail,
fetchCreatePresignOrder,
fetchUpdatePresignOrder,
fetchCreatePresignTransfer,
fetchUpdatePresignTransfer,
fetchSubmitPresignTransferFinance
} from '@/api/presign-order'
const props = defineProps<{
modelValue: boolean
orderId?: string
}>()
const emit = defineEmits<{
'update:modelValue': [boolean]
saved: []
}>()
const formRef = ref()
const loading = ref(false)
const saving = ref(false)
const transferTypeOptions = ref<Api.Dict.DictDataOption[]>([])
const sampleTypeOptions = ref<Array<Record<string, any>>>([])
const regionOptions = ref<Array<Record<string, any>>>([])
const hospitalOptions = ref<Array<Record<string, any>>>([])
const form = reactive({
orderDate: '',
protocolNo: '',
motherName: '',
expectedDeliveryDate: '',
regionId: '',
hospitalId: '',
sampleCollectTime: '',
payMode: 'dj',
contractAmount: null as number | null,
orderStatus: 'active',
sampleTypeIds: [] as string[],
remark: '',
transfers: [] as Array<Record<string, any>>,
confirmedDepositAmount: 0
})
const isConverted = computed(() => form.orderStatus === 'converted')
const confirmedDeposit = computed(() => Number(form.confirmedDepositAmount || 0))
function isTransferLocked(tr: Record<string, any>) {
return tr.payStatus === 'jfqrz' || tr.payStatus === 'yjf'
}
function resetForm() {
form.orderDate = ''
form.protocolNo = ''
form.motherName = ''
form.expectedDeliveryDate = ''
form.regionId = ''
form.hospitalId = ''
form.sampleCollectTime = ''
form.payMode = 'dj'
form.contractAmount = null
form.orderStatus = 'active'
form.sampleTypeIds = []
form.remark = ''
form.transfers = []
form.confirmedDepositAmount = 0
}
function addTransfer() {
form.transfers.push({
transferType: '',
transferAmount: null,
transferTime: '',
payStatus: 'dfj'
})
}
async function loadLookups() {
await loadTransferTypeDict()
transferTypeOptions.value = getTransferTypeOptions()
const [types, regions] = await Promise.all([
fetchCellSampleTypeList({ page: 1, pageSize: 200 }),
fetchCollectionRegionList({ page: 1, pageSize: 200 })
])
sampleTypeOptions.value = (types as any)?.list || (types as any) || []
regionOptions.value = (regions as any)?.list || (regions as any) || []
}
async function loadHospitals() {
hospitalOptions.value = []
if (!form.regionId) return
const res: any = await fetchHospitalList(form.regionId, { page: 1, pageSize: 200 })
hospitalOptions.value = res?.list || []
}
async function onRegionChange() {
form.hospitalId = ''
await loadHospitals()
}
async function loadDetail() {
if (!props.orderId) return
loading.value = true
try {
const data: any = await fetchPresignOrderDetail(props.orderId)
form.orderDate = String(data.orderDate || '')
form.protocolNo = String(data.protocolNo || '')
form.motherName = String(data.motherName || '')
form.expectedDeliveryDate = String(data.expectedDeliveryDate || '')
form.regionId = String(data.regionId || '')
form.hospitalId = String(data.hospitalId || '')
form.sampleCollectTime = String(data.sampleCollectTime || '')
form.payMode = String(data.payMode || 'dj')
form.contractAmount = data.contractAmount ?? null
form.orderStatus = String(data.orderStatus || 'active')
form.sampleTypeIds = Array.isArray(data.sampleTypeIds) ? [...data.sampleTypeIds] : []
form.remark = String(data.remark || '')
form.transfers = Array.isArray(data.transfers) ? [...data.transfers] : []
form.confirmedDepositAmount = Number(data.confirmedDepositAmount || 0)
if (form.regionId) await loadHospitals()
} finally {
loading.value = false
}
}
async function submitTransfer(tr: Record<string, any>) {
if (!props.orderId || !tr.id) {
ElMessage.warning('请先保存订单后再提交财务确认')
return
}
if (tr.payStatus === 'dfj') {
await fetchUpdatePresignTransfer(String(tr.id), {
transferType: tr.transferType,
transferAmount: tr.transferAmount,
transferTime: tr.transferTime || undefined
})
}
await fetchSubmitPresignTransferFinance(String(tr.id))
tr.payStatus = 'jfqrz'
ElMessage.success('已提交财务确认')
}
async function handleSave() {
if (!form.motherName.trim()) {
ElMessage.warning('请填写母亲姓名')
return
}
if (!form.sampleTypeIds.length) {
ElMessage.warning('请至少选择一个样本类型')
return
}
saving.value = true
try {
const payload: Record<string, unknown> = {
orderDate: form.orderDate || undefined,
protocolNo: form.protocolNo || undefined,
motherName: form.motherName,
expectedDeliveryDate: form.expectedDeliveryDate || undefined,
regionId: form.regionId || undefined,
hospitalId: form.hospitalId || undefined,
sampleCollectTime: form.sampleCollectTime || undefined,
payMode: form.payMode || undefined,
contractAmount: form.contractAmount ?? undefined,
orderStatus: form.orderStatus || 'active',
sampleTypeIds: form.sampleTypeIds,
remark: form.remark || undefined
}
if (props.orderId) {
await fetchUpdatePresignOrder(props.orderId, payload)
for (const tr of form.transfers) {
if (!tr.id && tr.payStatus === 'dfj') {
await fetchCreatePresignTransfer(props.orderId, {
transferType: tr.transferType,
transferAmount: tr.transferAmount,
transferTime: tr.transferTime || undefined
})
} else if (tr.id && tr.payStatus === 'dfj') {
await fetchUpdatePresignTransfer(String(tr.id), {
transferType: tr.transferType,
transferAmount: tr.transferAmount,
transferTime: tr.transferTime || undefined
})
}
}
} else {
payload.transfers = form.transfers
.filter((t) => t.payStatus === 'dfj' || !t.payStatus)
.map((t) => ({
transferType: t.transferType,
transferAmount: t.transferAmount,
transferTime: t.transferTime || undefined
}))
await fetchCreatePresignOrder(payload)
}
emit('update:modelValue', false)
emit('saved')
} finally {
saving.value = false
}
}
watch(
() => props.modelValue,
async (open) => {
if (!open) return
resetForm()
await loadLookups()
if (props.orderId) await loadDetail()
}
)
</script>
<style scoped lang="scss">
.form-grid-2 {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px 12px;
}
.form-col-span-2 {
grid-column: 1 / -1;
}
.section-subtitle {
margin: 16px 0 8px;
font-size: 13px;
font-weight: 600;
color: var(--mint-text-secondary, #475569);
}
.deposit-hint {
margin-left: 12px;
font-size: 12px;
color: #047857;
}
.transfer-card {
margin-bottom: 10px;
padding: 10px 12px;
border: 1px solid #e2e8f0;
border-radius: 8px;
background: #f8fafc;
}
</style>
@@ -0,0 +1,290 @@
<!-- 预签约订单 -->
<!-- eslint-disable vue/no-v-model-argument -->
<template>
<div class="page-container">
<section class="panel">
<div class="panel-top">
<h2 class="title-bar-heading">预签约订单</h2>
</div>
<div class="filter-block">
<el-form :model="searchForm" class="filter-form" label-position="top" @submit.prevent>
<div class="filter-grid">
<el-form-item label="关键词">
<el-input
v-model="searchForm.keyword"
placeholder="母亲姓名/协议编号"
clearable
@keyup.enter="handleSearch"
/>
</el-form-item>
<el-form-item label="订单状态">
<el-select
v-model="searchForm.orderStatus"
clearable
placeholder="全部"
style="width: 100%"
>
<el-option label="草稿" value="draft" />
<el-option label="进行中" value="active" />
<el-option label="已转正式" value="converted" />
<el-option label="已作废" value="void" />
</el-select>
</el-form-item>
</div>
<div class="filter-actions-row">
<div class="filter-actions-end">
<el-button class="btn-ghost" @click="handleReset">
<el-icon><RefreshLeft /></el-icon>重置
</el-button>
<el-button class="btn-main" type="primary" @click="handleSearch">
<el-icon><Search /></el-icon>搜索
</el-button>
</div>
</div>
</el-form>
</div>
<div class="list-toolbar">
<div class="list-toolbar-row">
<div class="list-toolbar-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<el-button class="btn-main" type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增预签约
</el-button>
</div>
</div>
<div class="list-body list-wrap">
<el-table
class="list-table"
:data="tableData"
v-loading="loading"
stripe
row-key="id"
@selection-change="(rows: any[]) => (selectedRows = rows)"
@row-dblclick="handleEdit"
>
<el-table-column type="selection" min-width="55" align="center" header-align="center" />
<el-table-column
type="index"
label="序号"
min-width="68"
align="center"
header-align="center"
:index="(index) => (pagination.page - 1) * pagination.pageSize + index + 1"
/>
<el-table-column
prop="orderDate"
label="业务日期"
min-width="120"
align="center"
header-align="center"
>
<template #default="{ row }">{{ formatYmd(row.orderDate) }}</template>
</el-table-column>
<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="motherName"
label="母亲姓名"
min-width="100"
align="center"
header-align="center"
/>
<el-table-column
prop="expectedDeliveryDate"
label="预产期"
min-width="120"
align="center"
header-align="center"
>
<template #default="{ row }">{{ formatYmd(row.expectedDeliveryDate) }}</template>
</el-table-column>
<el-table-column
prop="contractAmount"
label="合同金额"
min-width="120"
align="center"
header-align="center"
>
<template #default="{ row }">{{ fmtMoney(row.contractAmount) }}</template>
</el-table-column>
<el-table-column
prop="confirmedDepositAmount"
label="到账金额"
min-width="120"
align="center"
header-align="center"
>
<template #default="{ row }">{{ fmtMoney(row.confirmedDepositAmount) }}</template>
</el-table-column>
<el-table-column
prop="orderStatus"
label="状态"
min-width="110"
align="center"
header-align="center"
>
<template #default="{ row }">
<el-tag :type="presignOrderStatusTagType(String(row.orderStatus || ''))" size="small">
{{ presignOrderStatusLabel(String(row.orderStatus || '')) }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="createTime"
label="创建时间"
min-width="170"
align="center"
header-align="center"
>
<template #default="{ row }">{{ formatDisplayOrDash(row.createTime) }}</template>
</el-table-column>
<el-table-column
label="操作"
min-width="280"
fixed="right"
align="center"
header-align="center"
>
<template #default="{ row }">
<el-button class="btn-op-item" @click="handleEdit(row)">
<el-icon><Edit /></el-icon>编辑
</el-button>
<el-button
v-if="row.orderStatus !== 'converted' && row.orderStatus !== 'void'"
class="btn-op-item primary"
@click="handleConvert(row)"
>
<el-icon><Check /></el-icon>转正式
</el-button>
<el-button
v-if="row.orderStatus !== 'converted'"
class="btn-op-item"
@click="handleDelete(row)"
>
<el-icon><Delete /></el-icon>删除
</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination list-pager-bar">
<el-pagination
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next"
@size-change="(val) => ((pagination.page = 1), (pagination.pageSize = val), loadData())"
@current-change="(val) => ((pagination.page = val), loadData())"
/>
</div>
</div>
</section>
<PresignOrderFormDialog v-model="dialogVisible" :order-id="editingId" @saved="loadData" />
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { Plus, Search, RefreshLeft, Edit, Delete, Check } from '@element-plus/icons-vue'
import { formatDisplayOrDash } from '@/utils/datetime-display'
import { formatMoneyDisplay } from '@/utils/money-format'
import { presignOrderStatusLabel, presignOrderStatusTagType } from '@/utils/status-tag-registry'
import {
fetchPresignOrderList,
fetchDeletePresignOrder,
fetchConvertPresignOrder
} from '@/api/presign-order'
import PresignOrderFormDialog from './PresignOrderFormDialog.vue'
defineOptions({ name: 'CellStoragePresignOrder' })
const searchForm = reactive({ keyword: '', orderStatus: '' })
const tableData = ref<Record<string, any>[]>([])
const selectedRows = ref<Record<string, any>[]>([])
const loading = ref(false)
const pagination = reactive({ page: 1, pageSize: 10, total: 0 })
const dialogVisible = ref(false)
const editingId = ref<string | undefined>(undefined)
function fmtMoney(v: unknown) {
if (v == null || v === '') return '-'
return '¥' + formatMoneyDisplay(v)
}
function formatYmd(v: unknown) {
const s = String(v || '')
if (s.length === 8) return `${s.slice(0, 4)}-${s.slice(4, 6)}-${s.slice(6, 8)}`
return s || '-'
}
async function loadData() {
loading.value = true
try {
const res = await fetchPresignOrderList({
page: pagination.page,
pageSize: pagination.pageSize,
keyword: searchForm.keyword || undefined,
orderStatus: searchForm.orderStatus || undefined
})
tableData.value = res.list || []
pagination.total = res.total ?? 0
} finally {
loading.value = false
}
}
function handleSearch() {
pagination.page = 1
loadData()
}
function handleReset() {
searchForm.keyword = ''
searchForm.orderStatus = ''
pagination.page = 1
loadData()
}
function handleAdd() {
editingId.value = undefined
dialogVisible.value = true
}
function handleEdit(row: Record<string, any>) {
editingId.value = String(row.id)
dialogVisible.value = true
}
async function handleDelete(row: Record<string, any>) {
await ElMessageBox.confirm('确认删除该预签约订单?', '提示', { type: 'warning' })
await fetchDeletePresignOrder(String(row.id))
loadData()
}
async function handleConvert(row: Record<string, any>) {
await ElMessageBox.confirm(
'确认转为正式采集协议单?意向金不会自动写入样本缴费,仅在正式单中提示已确认合计。',
'转正式协议',
{ type: 'warning' }
)
const res: any = await fetchConvertPresignOrder(String(row.id))
const protocolNo = res?.protocolNo || res?.data?.protocolNo
ElMessage.success(protocolNo ? `已生成正式协议 ${protocolNo}` : '已转正式协议')
loadData()
}
onMounted(loadData)
</script>