feat(web-admin): add presign order UI and menu entry
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
fdd2c1fbc3
commit
a9112414c9
7 files changed
+950
-8
No files matched your search
@@ -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",
|
||||
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
@@ -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>
|
||||
Reference in new issue
Block a user