This commit is contained in:
Your Name
2026-04-07 18:13:03 +08:00
parent a780356908
commit fdf714f833
397 changed files with 15086 additions and 1043 deletions
+544 -14
View File
@@ -81,12 +81,20 @@
<el-card v-loading="pager.loading" class="mt-3 table-card" shadow="never">
<div class="pl-table-head">
<span class="pl-table-head__title">处方列表</span>
<el-button type="primary" @click="handleAdd" v-perms="['cf.prescription/add']">
<template #icon>
<icon name="el-icon-Plus" />
</template>
新增处方
</el-button>
<div class="flex items-center gap-2">
<el-button
v-perms="['tcm.prescriptionOrder/lists']"
@click="goBusinessOrderList"
>
业务订单
</el-button>
<el-button type="primary" @click="handleAdd" v-perms="['cf.prescription/add']">
<template #icon>
<icon name="el-icon-Plus" />
</template>
新增处方
</el-button>
</div>
</div>
<div class="mt-3">
@@ -104,11 +112,31 @@
<el-table-column label="审核状态" min-width="100">
<el-table-column label="审核状态" min-width="200">
<template #default="{ row }">
<el-tag :type="auditStatusTagType(row.audit_status)">
{{ auditStatusLabel(row.audit_status) }}
</el-tag>
<div class="flex flex-col gap-1">
<div class="flex flex-wrap items-center gap-1">
<el-tag :type="listAuditStatusTagType(row)">
{{ listAuditStatusLabel(row) }}
</el-tag>
<span
v-if="Number(row.business_prescription_audit_rejected) === 1"
class="text-xs text-gray-500"
>
业务订单审核
</span>
</div>
<template v-if="listRejectReasonLines(row).length">
<div
v-for="(line, idx) in listRejectReasonLines(row)"
:key="idx"
class="text-xs text-red-600 leading-snug line-clamp-3"
:title="line"
>
{{ line }}
</div>
</template>
</div>
</template>
</el-table-column>
<el-table-column label="作废" min-width="80">
@@ -124,13 +152,22 @@
{{ formatListCreateTime(row.create_time) }}
</template>
</el-table-column>
<el-table-column label="操作" width="260" fixed="right">
<el-table-column label="操作" width="300" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="handleView(row)" v-perms="['cf.prescription/read']">
查看
</el-button>
<el-button
v-if="!isApprovedActivePrescription(row)"
v-if="!Number(row.has_prescription_order)"
type="success"
link
v-perms="['tcm.prescriptionOrder/create']"
@click="openCreateOrderFromPrescription(row)"
>
创建订单
</el-button>
<el-button
v-if="!isApprovedActivePrescription(row) || Number(row.business_prescription_audit_rejected) === 1"
type="primary"
link
@click="handleEdit(row)"
@@ -183,6 +220,12 @@
<h2 class="cf-slip-title">{{ SLIP_HOSPITAL_NAME }}处方笺</h2>
<div class="cf-slip-type">
<el-tag v-if="Number(slipView.void_status) === 1" type="danger" size="small">已作废</el-tag>
<el-tag
v-else-if="Number(slipView.business_prescription_audit_rejected) === 1"
type="danger"
size="small"
class="mr-1"
>已驳回</el-tag>
<el-tag
v-else-if="Number(slipView.audit_status) === 1"
type="success"
@@ -205,6 +248,24 @@
<div v-if="Number(slipView.void_status) === 1" class="cf-slip-void">
作废人{{ slipView.void_by_name || '' }}作废时间{{ formatVoidTime(slipView.void_time) }}
</div>
<div
v-else-if="Number(slipView.business_prescription_audit_rejected) === 1"
class="rounded border border-red-200 bg-red-50 text-red-800 text-sm px-3 py-2 mb-3"
>
<div class="font-medium">审核状态已驳回业务订单·处方审核</div>
<div v-if="slipView.business_prescription_audit_remark" class="mt-1">
驳回意见{{ slipView.business_prescription_audit_remark }}
</div>
<div v-else class="mt-1 text-red-600/80">暂无驳回意见说明</div>
</div>
<div
v-else-if="Number(slipView.audit_status) === 2"
class="rounded border border-red-200 bg-red-50 text-red-800 text-sm px-3 py-2 mb-3"
>
<div class="font-medium">消费者处方审核已驳回</div>
<div v-if="slipView.audit_remark" class="mt-1">驳回意见{{ slipView.audit_remark }}</div>
<div v-else class="mt-1 text-red-600/80">暂无文字意见请联系审核人员</div>
</div>
<div class="cf-slip-info">
<div class="cf-slip-row">
<span class="cf-slip-item"><em>日期</em>{{ slipView.prescription_date || '—' }}</span>
@@ -293,6 +354,42 @@
class="mb-4"
title="保存修改后,该处方将重新进入「待审核」,需审核人员再次通过后方可生效。"
/>
<el-alert
v-if="editMode === 'edit' && Number(editForm.audit_status) === 2"
type="error"
:closable="false"
show-icon
class="mb-4"
>
<template #title>当前消费者处方为已驳回</template>
<div class="text-sm mt-1">
保存修改后将清除驳回并变为待审核{{
editForm.audit_remark ? `上次意见:${editForm.audit_remark}` : ''
}}
</div>
</el-alert>
<el-alert
v-if="
editMode === 'edit' &&
Number(editForm.business_prescription_audit_rejected) === 1 &&
Number(editForm.audit_status) === 1
"
type="warning"
:closable="false"
show-icon
class="mb-4"
>
<template #title>业务订单侧「处方审核」已驳回</template>
<div class="text-sm mt-1">
消费者处方仍为「已通过」;您修改并保存后,将重置关联业务订单的处方/支付单审核为「待审」,请在「处方业务订单」中继续处理。
</div>
<div
v-if="String(editForm.business_prescription_audit_remark || '').trim()"
class="text-sm mt-2 text-gray-800 font-medium"
>
驳回意见:{{ editForm.business_prescription_audit_remark }}
</div>
</el-alert>
<el-alert
v-if="editMode === 'edit' && editReviveHint"
type="warning"
@@ -600,6 +697,183 @@
</template>
</el-dialog>
<!-- 从消费者处方创建业务订单(zyt_tcm_prescription_order,与支付单 zyt_order 分离) -->
<el-dialog
v-model="createOrderVisible"
title="创建业务订单"
width="640px"
:close-on-click-modal="false"
@close="resetCreateOrderForm"
>
<div v-if="createOrderPrescription" class="mb-4 rounded bg-gray-50 px-3 py-2 text-sm text-gray-700">
<div class="font-medium text-gray-900">关联消费者处方</div>
<div>
处方 ID {{ createOrderPrescription.id }}
<span v-if="createOrderPrescription.sn"> · 编号 {{ createOrderPrescription.sn }}</span>
· {{ createOrderPrescription.patient_name || '—' }}
</div>
<div class="text-gray-500 mt-1 line-clamp-2">
{{ createOrderHerbSummary }}
</div>
</div>
<el-form
ref="createOrderFormRef"
:model="createOrderForm"
:rules="createOrderRules"
label-width="120px"
>
<el-divider content-position="left">患者与收货</el-divider>
<el-form-item label="诊单患者" prop="patient_id">
<el-select
v-model="createOrderForm.patient_id"
class="w-full"
placeholder="搜索姓名/手机/身份证"
filterable
remote
:remote-method="searchPatientsForOrder"
:loading="createOrderPatientLoading"
@change="onCreateOrderPatientChange"
>
<el-option
v-for="item in createOrderPatientList"
:key="item.id"
:label="`${item.patient_name} (${item.phone || '无手机'})`"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="收货人" prop="recipient_name">
<el-input v-model="createOrderForm.recipient_name" placeholder="可与诊单姓名一致" maxlength="50" />
</el-form-item>
<el-form-item label="收货手机" prop="recipient_phone">
<el-input v-model="createOrderForm.recipient_phone" placeholder="用于物流联系与单号匹配" maxlength="20" />
</el-form-item>
<el-form-item label="收货地址" prop="shipping_address">
<el-input
v-model="createOrderForm.shipping_address"
type="textarea"
:rows="2"
placeholder="完整收货地址"
maxlength="200"
show-word-limit
/>
</el-form-item>
<el-divider content-position="left">服务信息</el-divider>
<el-form-item label="是否复诊">
<el-switch v-model="createOrderForm.is_follow_up" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item label="用药疗程(天)">
<el-input-number
v-model="createOrderForm.medication_days"
:min="1"
:max="999"
:step="1"
placeholder="天数"
class="w-full"
/>
</el-form-item>
<el-form-item label="上次医生/医助">
<el-input v-model="createOrderForm.prev_staff" placeholder="可选" maxlength="100" />
</el-form-item>
<el-form-item label="服务渠道">
<el-input v-model="createOrderForm.service_channel" placeholder="可选" maxlength="100" />
</el-form-item>
<el-form-item label="服务套餐">
<el-input v-model="createOrderForm.service_package" placeholder="可选" maxlength="100" />
</el-form-item>
<el-divider content-position="left">物流</el-divider>
<el-form-item label="快递公司">
<el-select v-model="createOrderForm.express_company" placeholder="查物流用" class="w-full">
<el-option label="自动识别" value="auto" />
<el-option label="顺丰速运" value="sf" />
<el-option label="京东快递" value="jd" />
</el-select>
</el-form-item>
<el-form-item label="快递单号">
<el-input v-model="createOrderForm.tracking_number" placeholder="可创建时填写或发货前补录" maxlength="80" />
</el-form-item>
<el-divider content-position="left">关联收款</el-divider>
<el-form-item label="已支付单">
<el-select
v-model="createOrderForm.pay_order_ids"
class="w-full"
multiple
collapse-tags
collapse-tags-tooltip
placeholder="多选本诊单下已支付收款单"
:loading="createOrderPaidOrdersLoading"
>
<el-option
v-for="o in createOrderPaidOrders"
:key="o.id"
:label="`${o.order_no} · ¥${o.amount}`"
:value="o.id"
/>
</el-select>
<div class="text-xs text-gray-400 mt-1">
与「订单管理」中已支付订单一致;主管或本诊单医助可见该诊单全部已支付单,否则仅本人创建的收款单。已被其他有效业务订单占用的支付单不会出现在此列表。业务订单撤回后,本处方可再次创建订单并重新关联支付单。
</div>
</el-form-item>
<el-divider content-position="left">金额</el-divider>
<el-form-item label="费用类别" prop="fee_type">
<el-select v-model="createOrderForm.fee_type" placeholder="请选择" class="w-full">
<el-option label="挂号费" :value="1" />
<el-option label="问诊费" :value="2" />
<el-option label="药品费用" :value="3" />
<el-option label="首付费用" :value="4" />
<el-option label="尾款费用" :value="5" />
<el-option label="其他费用" :value="6" />
<el-option label="全部费用" :value="7" />
</el-select>
</el-form-item>
<el-form-item label="订单金额" prop="amount">
<el-input-number
v-model="createOrderForm.amount"
:min="0"
:step="0.01"
:precision="2"
class="w-full"
placeholder="业务订单金额(元)"
/>
</el-form-item>
<el-form-item
v-perms="['finance.account_log/lists']"
label="内部成本"
>
<el-input-number
v-model="createOrderForm.internal_cost"
:min="0"
:step="0.01"
:precision="2"
class="w-full"
placeholder="仅财务角色写入库表"
/>
<div class="text-xs text-gray-400 mt-1">写入业务订单表字段;无财务权限时后端不会保存该值。</div>
</el-form-item>
<el-form-item label="备注补充">
<el-input
v-model="createOrderForm.remark_extra"
type="textarea"
:rows="2"
placeholder="其他说明(可选)"
maxlength="200"
show-word-limit
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="createOrderVisible = false">取消</el-button>
<el-button type="primary" :loading="createOrderSubmitLoading" @click="submitCreateOrderFromPrescription">
创建业务订单
</el-button>
</template>
</el-dialog>
<el-dialog v-model="auditDialogVisible" title="处方审核" width="480px" destroy-on-close @closed="auditRemark = ''">
<p class="text-sm text-gray-600 mb-3">
通过:处方保持有效。驳回:将同时作废此处方(与诊间「驳回/作废处方」一致)。
@@ -633,15 +907,19 @@ import {
prescriptionDelete,
prescriptionAudit,
prescriptionDetail,
prescriptionOrderCreate,
prescriptionOrderPaidPayOrders,
getDoctors
} from '@/api/tcm'
import { searchPatients as searchPatientsAPI } from '@/api/order'
import { roleAll } from '@/api/perms/role'
import { usePaging } from '@/hooks/usePaging'
import useUserStore from '@/stores/modules/user'
import feedback from '@/utils/feedback'
import type { FormInstance, FormRules } from 'element-plus'
import DaterangePicker from '@/components/daterange-picker/index.vue'
import { computed, onMounted, watch } from 'vue'
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
/** 与 server/config/project.php prescription_audit_roles 保持一致 */
const PRESCRIPTION_AUDIT_ROLE_IDS = [0, 3]
@@ -652,6 +930,16 @@ const SLIP_COMPANY_LINE = '成都双流甄养堂互联网医院有限公司 联
const SLIP_ADDRESS_LINE = '地址:四川省成都市双流区黄甲街道黄龙大道二段280号'
const userStore = useUserStore()
const router = useRouter()
function goBusinessOrderList() {
const hit = router.getRoutes().find((r) => r.meta?.perms === 'tcm.prescriptionOrder/lists')
if (hit?.path) {
router.push(hit.path)
} else {
feedback.msgWarning('未配置「处方业务订单」菜单,请在权限管理中执行菜单 SQL 或手动添加路由')
}
}
// 表单数据(与列表接口查询参数一致)
const formData = reactive({
@@ -684,6 +972,212 @@ const auditTargetId = ref(0)
const auditRemark = ref('')
const auditLoading = ref(false)
/** 从消费者处方创建订单 */
const createOrderVisible = ref(false)
const createOrderPrescription = ref<any>(null)
const createOrderFormRef = ref<FormInstance>()
const createOrderSubmitLoading = ref(false)
const createOrderPatientLoading = ref(false)
const createOrderPatientList = ref<any[]>([])
const createOrderPaidOrders = ref<Array<{ id: number; order_no?: string; amount?: number | string }>>([])
const createOrderPaidOrdersLoading = ref(false)
const createOrderForm = reactive({
patient_id: '' as number | string,
recipient_name: '',
recipient_phone: '',
shipping_address: '',
is_follow_up: 0,
medication_days: undefined as number | undefined,
prev_staff: '',
service_channel: '',
service_package: '',
express_company: 'auto',
tracking_number: '',
fee_type: 3,
amount: 0,
internal_cost: undefined as number | undefined,
remark_extra: '',
pay_order_ids: [] as number[]
})
const createOrderRules: FormRules = {
patient_id: [{ required: true, message: '请选择诊单患者', trigger: 'change' }],
recipient_name: [{ required: true, message: '请输入收货人', trigger: 'blur' }],
recipient_phone: [{ required: true, message: '请输入收货手机', trigger: 'blur' }],
shipping_address: [{ required: true, message: '请输入收货地址', trigger: 'blur' }],
fee_type: [{ required: true, message: '请选择费用类别', trigger: 'change' }],
amount: [{ required: true, message: '请输入订单金额', trigger: 'blur' }]
}
const createOrderHerbSummary = computed(() => {
const row = createOrderPrescription.value
const herbs = normalizeSlipHerbs(row?.herbs)
if (!herbs.length) return '暂无药材明细'
const s = herbs.map((h) => `${h.name} ${h.dosage}g`).join('、')
return s.length > 120 ? `${s.slice(0, 120)}…` : s
})
function resetCreateOrderForm() {
createOrderForm.patient_id = ''
createOrderForm.recipient_name = ''
createOrderForm.recipient_phone = ''
createOrderForm.shipping_address = ''
createOrderForm.is_follow_up = 0
createOrderForm.medication_days = undefined
createOrderForm.prev_staff = ''
createOrderForm.service_channel = ''
createOrderForm.service_package = ''
createOrderForm.express_company = 'auto'
createOrderForm.tracking_number = ''
createOrderForm.fee_type = 3
createOrderForm.amount = 0
createOrderForm.internal_cost = undefined
createOrderForm.remark_extra = ''
createOrderForm.pay_order_ids = []
createOrderPatientList.value = []
createOrderPaidOrders.value = []
createOrderPrescription.value = null
createOrderFormRef.value?.clearValidate()
}
async function loadCreateOrderPaidOrders() {
const did = Number(createOrderForm.patient_id)
if (!did) {
createOrderPaidOrders.value = []
return
}
try {
createOrderPaidOrdersLoading.value = true
const res: any = await prescriptionOrderPaidPayOrders({ diagnosis_id: did })
const lists = res?.lists ?? res?.data?.lists ?? []
createOrderPaidOrders.value = Array.isArray(lists) ? lists : []
} catch {
createOrderPaidOrders.value = []
} finally {
createOrderPaidOrdersLoading.value = false
}
}
watch(
() => createOrderForm.patient_id,
() => {
createOrderForm.pay_order_ids = []
void loadCreateOrderPaidOrders()
}
)
async function openCreateOrderFromPrescription(row: any) {
resetCreateOrderForm()
createOrderPrescription.value = row
const did = Number(row.diagnosis_id) || 0
if (did > 0) {
createOrderForm.patient_id = did
createOrderForm.recipient_name = row.patient_name || ''
createOrderForm.recipient_phone = String(row.phone || '').trim()
createOrderPatientList.value = [
{
id: did,
patient_name: row.patient_name || '',
phone: String(row.phone || '').trim()
}
]
}
const ud = Number(row.usage_days)
if (ud > 0) {
createOrderForm.medication_days = ud
}
createOrderVisible.value = true
void loadCreateOrderPaidOrders()
const rxId = row.id
if (rxId && !createOrderForm.recipient_phone) {
try {
const d = (await prescriptionDetail({ id: rxId })) as Record<string, unknown> | null
const phone = d && typeof d.phone === 'string' ? d.phone.trim() : ''
if (phone && createOrderPrescription.value?.id === rxId) {
createOrderForm.recipient_phone = phone
const first = createOrderPatientList.value[0]
if (first && Number(first.id) === did) {
first.phone = phone
}
}
} catch {
/* 列表无电话时尽力补全,失败忽略 */
}
}
}
async function searchPatientsForOrder(query: string) {
if (!query) {
createOrderPatientList.value = []
return
}
try {
createOrderPatientLoading.value = true
const res: any = await searchPatientsAPI({
keyword: query,
page_no: 1,
page_size: 10
})
createOrderPatientList.value = res?.lists || []
} catch {
createOrderPatientList.value = []
} finally {
createOrderPatientLoading.value = false
}
}
function onCreateOrderPatientChange(id: number | string) {
const p = createOrderPatientList.value.find((x) => Number(x.id) === Number(id))
if (p) {
createOrderForm.recipient_name = p.patient_name || ''
createOrderForm.recipient_phone = p.phone || ''
}
}
async function submitCreateOrderFromPrescription() {
if (!createOrderFormRef.value || !createOrderPrescription.value) return
try {
await createOrderFormRef.value.validate()
createOrderSubmitLoading.value = true
const diagnosisId = Number(createOrderForm.patient_id)
const payload: Record<string, unknown> = {
prescription_id: createOrderPrescription.value.id,
diagnosis_id: diagnosisId,
recipient_name: createOrderForm.recipient_name,
recipient_phone: createOrderForm.recipient_phone,
shipping_address: createOrderForm.shipping_address,
is_follow_up: createOrderForm.is_follow_up,
prev_staff: createOrderForm.prev_staff || '',
service_channel: createOrderForm.service_channel || '',
service_package: createOrderForm.service_package || '',
express_company: createOrderForm.express_company || 'auto',
tracking_number: createOrderForm.tracking_number || '',
fee_type: createOrderForm.fee_type,
amount: createOrderForm.amount,
remark_extra: createOrderForm.remark_extra || ''
}
if (createOrderForm.medication_days != null && createOrderForm.medication_days > 0) {
payload.medication_days = createOrderForm.medication_days
}
if (createOrderForm.internal_cost != null && createOrderForm.internal_cost > 0) {
payload.internal_cost = createOrderForm.internal_cost
}
if (createOrderForm.pay_order_ids.length) {
payload.pay_order_ids = [...createOrderForm.pay_order_ids]
}
const res: any = await prescriptionOrderCreate(payload)
const row = res?.data ?? res
const orderNo = row?.order_no
feedback.msgSuccess(orderNo ? `业务订单已创建,单号:${orderNo}` : '业务订单已创建')
createOrderVisible.value = false
} catch (e) {
console.error(e)
} finally {
createOrderSubmitLoading.value = false
}
}
/** 正在编辑的这条是否原为已作废 / 已驳回(用于提示) */
const editingWasVoid = ref(false)
const editingWasRejected = ref(false)
@@ -739,7 +1233,10 @@ const editForm = reactive({
audit_time: null as number | null,
audit_by_name: '',
audit_remark: '',
diagnosis_id: 0
diagnosis_id: 0,
/** 列表带入:业务订单「处方审核」驳回(消费者处方本身可能仍为已通过) */
business_prescription_audit_rejected: 0,
business_prescription_audit_remark: ''
})
// 表单验证规则
@@ -891,6 +1388,35 @@ function auditStatusTagType(v: number | undefined) {
return 'success'
}
/** 业务订单处方审核驳回时,列表「审核状态」主标签显示为已驳回 */
function listAuditStatusLabel(row: { audit_status?: number; business_prescription_audit_rejected?: number }) {
if (Number(row.business_prescription_audit_rejected) === 1) return '已驳回'
return auditStatusLabel(row.audit_status)
}
function listAuditStatusTagType(row: { audit_status?: number; business_prescription_audit_rejected?: number }) {
if (Number(row.business_prescription_audit_rejected) === 1) return 'danger'
return auditStatusTagType(row.audit_status)
}
function listRejectReasonLines(row: {
audit_status?: number
audit_remark?: string
business_prescription_audit_rejected?: number
business_prescription_audit_remark?: string
}): string[] {
const out: string[] = []
if (Number(row.business_prescription_audit_rejected) === 1) {
const t = String(row.business_prescription_audit_remark || '').trim()
out.push(t ? `业务订单审核驳回意见:${t}` : '业务订单审核驳回意见:—')
}
if (Number(row.audit_status) === 2) {
const t = String(row.audit_remark || '').trim()
out.push(t ? `消费者处方审核驳回意见:${t}` : '消费者处方审核驳回意见:—')
}
return out
}
/** 已通过审核且未作废:仅可查看,不可编辑/删除(与预约列表「查看」一致) */
function isApprovedActivePrescription(row: { audit_status?: number; void_status?: number }) {
return Number(row.audit_status) === 1 && Number(row.void_status) !== 1
@@ -1043,6 +1569,8 @@ const resetForm = () => {
editForm.audit_by_name = ''
editForm.audit_remark = ''
editForm.diagnosis_id = 0
editForm.business_prescription_audit_rejected = 0
editForm.business_prescription_audit_remark = ''
}
// 新增处方
@@ -1112,6 +1640,8 @@ const handleEdit = (row: any) => {
editForm.audit_by_name = row.audit_by_name || ''
editForm.audit_remark = row.audit_remark || ''
editForm.diagnosis_id = row.diagnosis_id ?? 0
editForm.business_prescription_audit_rejected = Number(row.business_prescription_audit_rejected) === 1 ? 1 : 0
editForm.business_prescription_audit_remark = String(row.business_prescription_audit_remark || '')
showEdit.value = true
}
@@ -0,0 +1,806 @@
<!-- 处方业务订单zyt_tcm_prescription_order非支付单 -->
<template>
<div class="prescription-order-list px-4 py-4">
<el-card class="!border-none" shadow="never">
<el-form class="ls-form" :model="queryParams" inline>
<el-form-item class="w-[260px]" label="订单号">
<el-input
v-model="queryParams.order_no"
placeholder="业务订单号"
clearable
@keyup.enter="resetPage"
/>
</el-form-item>
<el-form-item class="w-[140px]" label="处方ID">
<el-input
v-model="queryParams.prescription_id"
placeholder="处方ID"
clearable
@keyup.enter="resetPage"
/>
</el-form-item>
<el-form-item class="w-[180px]" label="履约状态">
<el-select v-model="queryParams.fulfillment_status" placeholder="全部" clearable>
<el-option label="待双审通过" :value="1" />
<el-option label="履约中" :value="2" />
<el-option label="已完成" :value="3" />
<el-option label="已取消" :value="4" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="resetPage">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card v-loading="pager.loading" class="!border-none mt-3" shadow="never">
<div class="text-base font-medium mb-3">处方业务订单</div>
<el-table :data="pager.lists" size="default" stripe>
<el-table-column label="订单号" prop="order_no" min-width="150" show-overflow-tooltip />
<el-table-column label="处方ID" prop="prescription_id" width="86" />
<el-table-column label="诊单ID" prop="diagnosis_id" width="86" />
<el-table-column label="收货人" min-width="100">
<template #default="{ row }">
<div>{{ row.recipient_name || '—' }}</div>
<div class="text-xs text-gray-400">{{ row.recipient_phone || '' }}</div>
</template>
</el-table-column>
<el-table-column label="费用类别" width="96">
<template #default="{ row }">
{{ feeTypeText(row.fee_type) }}
</template>
</el-table-column>
<el-table-column label="订单金额" width="100">
<template #default="{ row }">
<span class="text-red-500 font-medium">¥{{ row.amount }}</span>
</template>
</el-table-column>
<el-table-column label="内部成本" width="92">
<template #default="{ row }">
{{ row.internal_cost != null && row.internal_cost !== '' ? `¥${row.internal_cost}` : '—' }}
</template>
</el-table-column>
<el-table-column label="处方审核" width="96">
<template #default="{ row }">
<el-tag :type="auditTagType(row.prescription_audit_status)" size="small">
{{ auditStatusText(row.prescription_audit_status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="支付单审核" width="102">
<template #default="{ row }">
<el-tag :type="auditTagType(row.payment_slip_audit_status)" size="small">
{{ auditStatusText(row.payment_slip_audit_status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="履约" width="96">
<template #default="{ row }">
{{ fulfillmentText(row.fulfillment_status) }}
</template>
</el-table-column>
<el-table-column label="关联支付单" width="120">
<template #default="{ row }">
<span v-if="Number(row.linked_pay_order_count) > 0">
{{ row.linked_pay_order_count }}
<span v-if="row.linked_pay_order_id" class="text-gray-400 text-xs">(#{{ row.linked_pay_order_id }})</span>
</span>
<span v-else></span>
</template>
</el-table-column>
<el-table-column label="创建时间" min-width="148">
<template #default="{ row }">
{{ formatTime(row.create_time) }}
</template>
</el-table-column>
<el-table-column label="操作" width="220" fixed="right">
<template #default="{ row }">
<el-button
v-perms="['tcm.prescriptionOrder/detail']"
type="primary"
link
@click="openDetail(row.id)"
>
详情
</el-button>
<el-button
v-if="canEditRow(row)"
v-perms="['tcm.prescriptionOrder/edit']"
type="primary"
link
@click="openEdit(row)"
>
编辑
</el-button>
<el-button
v-if="canRxAudit(row)"
v-perms="['tcm.prescriptionOrder/auditPrescription']"
type="warning"
link
@click="openAudit(row, 'prescription')"
>
处方审核
</el-button>
<el-button
v-if="canPayAudit(row)"
v-perms="['tcm.prescriptionOrder/auditPayment']"
type="warning"
link
@click="openAudit(row, 'payment')"
>
支付审核
</el-button>
<el-button
v-if="canWithdrawRow(row)"
v-perms="['tcm.prescriptionOrder/withdraw']"
type="danger"
link
@click="confirmWithdraw(row)"
>
撤回
</el-button>
</template>
</el-table-column>
</el-table>
<div class="flex justify-end mt-3">
<pagination v-model="pager" @change="getLists" />
</div>
</el-card>
<!-- 详情 -->
<el-dialog v-model="detailVisible" title="业务订单详情" width="720px" destroy-on-close>
<el-skeleton v-if="detailLoading" :rows="10" animated />
<el-descriptions v-else-if="detailData" :column="2" border>
<el-descriptions-item label="订单号" :span="2">{{ detailData.order_no }}</el-descriptions-item>
<el-descriptions-item label="处方ID">{{ detailData.prescription_id }}</el-descriptions-item>
<el-descriptions-item label="诊单ID">{{ detailData.diagnosis_id }}</el-descriptions-item>
<el-descriptions-item label="收货人">{{ detailData.recipient_name }}</el-descriptions-item>
<el-descriptions-item label="手机">{{ detailData.recipient_phone }}</el-descriptions-item>
<el-descriptions-item label="收货地址" :span="2">{{ detailData.shipping_address }}</el-descriptions-item>
<el-descriptions-item label="复诊">{{ detailData.is_follow_up ? '是' : '否' }}</el-descriptions-item>
<el-descriptions-item label="疗程(天)">{{ detailData.medication_days ?? '—' }}</el-descriptions-item>
<el-descriptions-item label="上次医护" :span="2">{{ detailData.prev_staff || '—' }}</el-descriptions-item>
<el-descriptions-item label="服务渠道">{{ detailData.service_channel || '—' }}</el-descriptions-item>
<el-descriptions-item label="服务套餐">{{ detailData.service_package || '—' }}</el-descriptions-item>
<el-descriptions-item label="快递单号" :span="2">{{ detailData.tracking_number || '—' }}</el-descriptions-item>
<el-descriptions-item label="快递公司" :span="2">
{{ expressCompanyLabel(detailData.express_company) }}
</el-descriptions-item>
<el-descriptions-item label="费用类别">{{ feeTypeText(detailData.fee_type) }}</el-descriptions-item>
<el-descriptions-item label="订单金额">
<span class="text-red-500 font-medium">¥{{ detailData.amount }}</span>
</el-descriptions-item>
<el-descriptions-item v-if="detailData.internal_cost != null && detailData.internal_cost !== ''" label="内部成本">
¥{{ detailData.internal_cost }}
</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">{{ detailData.remark_extra || '—' }}</el-descriptions-item>
<el-descriptions-item label="处方审核">
{{ auditStatusText(detailData.prescription_audit_status) }}
</el-descriptions-item>
<el-descriptions-item label="处方审核意见" :span="2">
{{ detailData.prescription_audit_remark || '—' }}
</el-descriptions-item>
<el-descriptions-item label="支付单审核">
{{ auditStatusText(detailData.payment_slip_audit_status) }}
</el-descriptions-item>
<el-descriptions-item label="支付单审核意见" :span="2">
{{ detailData.payment_slip_audit_remark || '—' }}
</el-descriptions-item>
<el-descriptions-item label="履约状态">{{ fulfillmentText(detailData.fulfillment_status) }}</el-descriptions-item>
<el-descriptions-item label="关联支付单" :span="2">
<div v-if="detailLinkedPayOrders.length" class="space-y-1">
<div
v-for="o in detailLinkedPayOrders"
:key="o.id"
class="text-sm border-b border-gray-100 pb-1 last:border-0"
>
<span class="font-medium">#{{ o.id }}</span>
{{ o.order_no || '—' }} · ¥{{ o.amount }} · {{ orderStatusText(o.status) }}
</div>
</div>
<span v-else></span>
</el-descriptions-item>
<el-descriptions-item label="创建时间" :span="2">{{ formatTime(detailData.create_time) }}</el-descriptions-item>
</el-descriptions>
<div
v-if="detailData && String(detailData.tracking_number || '').trim()"
class="mt-4 border-t border-gray-100 pt-4"
>
<div class="text-sm font-medium text-gray-800 mb-2">物流轨迹</div>
<p class="text-xs text-gray-500 mb-3">
优先从数据库查询历史轨迹快速响应数据库无记录时调用快递100实时查询支持顺丰京东极兔速递等快递公司顺丰查询建议填写正确收货手机后四位本单收货手机已自动传入
</p>
<div class="flex flex-wrap items-center gap-2 mb-3">
<el-select v-model="detailLogisticsExpress" placeholder="承运商" style="width: 140px">
<el-option label="自动识别" value="auto" />
<el-option label="顺丰速运" value="sf" />
<el-option label="京东快递" value="jd" />
<el-option label="极兔速递" value="jt" />
</el-select>
<el-button
v-perms="['tcm.prescriptionOrder/logisticsTrace']"
type="default"
size="small"
:loading="logisticsTraceLoading"
@click="fetchLogisticsTrace"
>
<template #icon>
<el-icon><Refresh /></el-icon>
</template>
刷新轨迹
</el-button>
<el-link :href="detailOfficialUrls.sf" target="_blank" type="primary" class="text-sm">顺丰官网查件</el-link>
<el-link :href="detailOfficialUrls.jd" target="_blank" type="primary" class="text-sm">京东物流查件</el-link>
<el-link :href="detailOfficialUrls.jt" target="_blank" type="primary" class="text-sm">极兔速递查件</el-link>
</div>
<el-alert
v-if="String(logisticsTracePayload?.hint || '').trim()"
:title="String(logisticsTracePayload?.hint || '')"
type="info"
:closable="false"
class="mb-3"
show-icon
/>
<div v-if="logisticsTraceLoading && !logisticsTracePayload" class="text-sm text-gray-500 mb-2">
<el-icon class="is-loading"><Loading /></el-icon>
正在加载物流轨迹...
</div>
<div v-if="logisticsTracePayload?.state_text" class="text-sm mb-2">
<span class="text-gray-700">物流状态{{ logisticsTracePayload.state_text }}</span>
<span v-if="logisticsTracePayload?.carrier_label" class="text-gray-500">
{{ logisticsTracePayload.carrier_label }}
</span>
<span v-if="logisticsTracePayload?.source" class="text-xs text-gray-400 ml-2">
[{{ logisticsTracePayload.source === 'database' ? '数据库' : 'API实时' }}]
</span>
<span v-if="logisticsTracePayload?.last_query_time" class="text-xs text-gray-400 ml-2">
最后更新{{ logisticsTracePayload.last_query_time }}
</span>
</div>
<el-timeline v-if="logisticsTraceList.length">
<el-timeline-item
v-for="(t, idx) in logisticsTraceList"
:key="idx"
:timestamp="t.time"
placement="top"
>
{{ t.context }}
</el-timeline-item>
</el-timeline>
</div>
</el-dialog>
<!-- 编辑 -->
<el-dialog
v-model="editVisible"
title="编辑业务订单"
width="640px"
:close-on-click-modal="false"
destroy-on-close
@closed="editFormRef?.clearValidate()"
>
<el-form
v-loading="editDialogLoading"
ref="editFormRef"
:model="editForm"
:rules="editRules"
label-width="120px"
>
<el-form-item label="收货人" prop="recipient_name">
<el-input v-model="editForm.recipient_name" maxlength="50" />
</el-form-item>
<el-form-item label="收货手机" prop="recipient_phone">
<el-input v-model="editForm.recipient_phone" maxlength="20" />
</el-form-item>
<el-form-item label="收货地址" prop="shipping_address">
<el-input v-model="editForm.shipping_address" type="textarea" :rows="2" maxlength="500" show-word-limit />
</el-form-item>
<el-form-item label="是否复诊">
<el-switch v-model="editForm.is_follow_up" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item label="用药疗程(天)">
<el-input-number v-model="editForm.medication_days" :min="1" :max="999" :step="1" class="w-full" />
</el-form-item>
<el-form-item label="上次医生/医助">
<el-input v-model="editForm.prev_staff" maxlength="100" />
</el-form-item>
<el-form-item label="服务渠道">
<el-input v-model="editForm.service_channel" maxlength="100" />
</el-form-item>
<el-form-item label="服务套餐">
<el-input v-model="editForm.service_package" maxlength="100" />
</el-form-item>
<el-form-item label="快递公司">
<el-select v-model="editForm.express_company" placeholder="用于查物流" class="w-full">
<el-option label="自动识别" value="auto" />
<el-option label="顺丰速运" value="sf" />
<el-option label="京东快递" value="jd" />
<el-option label="极兔速递" value="jt" />
</el-select>
</el-form-item>
<el-form-item label="快递单号">
<el-input v-model="editForm.tracking_number" maxlength="80" />
</el-form-item>
<el-form-item label="费用类别" prop="fee_type">
<el-select v-model="editForm.fee_type" class="w-full">
<el-option label="挂号费" :value="1" />
<el-option label="问诊费" :value="2" />
<el-option label="药品费用" :value="3" />
<el-option label="首付费用" :value="4" />
<el-option label="尾款费用" :value="5" />
<el-option label="其他费用" :value="6" />
<el-option label="全部费用" :value="7" />
</el-select>
</el-form-item>
<el-form-item label="订单金额" prop="amount">
<el-input-number v-model="editForm.amount" :min="0" :step="0.01" :precision="2" class="w-full" />
</el-form-item>
<el-form-item label="关联已支付单">
<el-select
v-model="editForm.pay_order_ids"
class="w-full"
multiple
collapse-tags
collapse-tags-tooltip
placeholder="多选本诊单已支付收款单"
:loading="editPaidOrdersLoading"
>
<el-option
v-for="o in editPaidOrders"
:key="o.id"
:label="`${o.order_no} · ¥${o.amount}`"
:value="o.id"
/>
</el-select>
<div class="text-xs text-gray-400 mt-1">支付单审核时可对照此处关联的收款记录</div>
</el-form-item>
<el-form-item
v-perms="['finance.account_log/lists']"
label="内部成本"
>
<el-input-number v-model="editForm.internal_cost" :min="0" :step="0.01" :precision="2" class="w-full" />
</el-form-item>
<el-form-item label="备注补充">
<el-input v-model="editForm.remark_extra" type="textarea" :rows="2" maxlength="500" show-word-limit />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="editVisible = false">取消</el-button>
<el-button type="primary" :loading="editSaving" @click="submitEdit">保存</el-button>
</template>
</el-dialog>
<!-- 审核 -->
<el-dialog
v-model="auditVisible"
:title="auditTitle"
width="480px"
destroy-on-close
@closed="auditRemark = ''"
>
<p class="text-sm text-gray-600 mb-2">驳回时必须填写意见通过时意见选填</p>
<el-input v-model="auditRemark" type="textarea" :rows="4" maxlength="500" show-word-limit placeholder="审核意见" />
<template #footer>
<el-button @click="auditVisible = false">取消</el-button>
<el-button type="primary" :loading="auditSubmitting" @click="submitAudit('approve')">通过</el-button>
<el-button type="danger" :loading="auditSubmitting" @click="submitAudit('reject')">驳回</el-button>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="prescriptionOrderList">
import { computed, onMounted, reactive, ref } from 'vue'
import { Refresh, Loading } from '@element-plus/icons-vue'
import {
prescriptionOrderAuditPayment,
prescriptionOrderAuditPrescription,
prescriptionOrderDetail,
prescriptionOrderEdit,
prescriptionOrderLists,
prescriptionOrderPaidPayOrders,
prescriptionOrderWithdraw,
prescriptionOrderLogisticsTrace
} from '@/api/tcm'
import { usePaging } from '@/hooks/usePaging'
import feedback from '@/utils/feedback'
import { ElMessageBox } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
const queryParams = reactive({
order_no: '',
prescription_id: '' as string | number,
fulfillment_status: '' as number | ''
})
async function fetchLists(params: Record<string, unknown>) {
const p: Record<string, unknown> = { ...params }
if (p.prescription_id === '' || p.prescription_id === undefined) {
delete p.prescription_id
} else {
p.prescription_id = Number(p.prescription_id)
}
if (p.fulfillment_status === '' || p.fulfillment_status === undefined) {
delete p.fulfillment_status
}
if (!p.order_no) delete p.order_no
return prescriptionOrderLists(p)
}
const { pager, getLists, resetPage, resetParams } = usePaging({
fetchFun: fetchLists,
params: queryParams
})
function handleReset() {
queryParams.order_no = ''
queryParams.prescription_id = ''
queryParams.fulfillment_status = ''
resetParams()
}
function feeTypeText(t: number | undefined) {
const m: Record<number, string> = {
1: '挂号费',
2: '问诊费',
3: '药品费用',
4: '首付费用',
5: '尾款费用',
6: '其他费用',
7: '全部费用'
}
return m[Number(t)] ?? '—'
}
function auditStatusText(s: number | undefined) {
if (s === 1) return '已通过'
if (s === 2) return '已驳回'
return '待审核'
}
function auditTagType(s: number | undefined): 'success' | 'warning' | 'danger' | 'info' {
if (s === 1) return 'success'
if (s === 2) return 'danger'
return 'warning'
}
function fulfillmentText(s: number | undefined) {
const m: Record<number, string> = {
1: '待双审通过',
2: '履约中',
3: '已完成',
4: '已取消'
}
return m[Number(s)] ?? '—'
}
function formatTime(v: unknown) {
if (v === null || v === undefined || v === '') return '—'
if (typeof v === 'number' && v > 1e9 && v < 1e11) {
const d = new Date(v * 1000)
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`
}
return String(v)
}
function canEditRow(row: { fulfillment_status?: number }) {
const fs = Number(row.fulfillment_status)
return fs === 1 || fs === 2
}
function canRxAudit(row: { prescription_audit_status?: number; fulfillment_status?: number }) {
const fs = Number(row.fulfillment_status)
if (fs === 3 || fs === 4) return false
return Number(row.prescription_audit_status) === 0
}
function canPayAudit(row: {
prescription_audit_status?: number
payment_slip_audit_status?: number
fulfillment_status?: number
}) {
const fs = Number(row.fulfillment_status)
if (fs === 3 || fs === 4) return false
return Number(row.prescription_audit_status) === 1 && Number(row.payment_slip_audit_status) === 0
}
function canWithdrawRow(row: { fulfillment_status?: number }) {
return Number(row.fulfillment_status) === 1
}
function orderStatusText(s: number | undefined) {
const m: Record<number, string> = {
1: '待支付',
2: '已支付',
3: '已取消',
4: '已退款'
}
return m[Number(s)] ?? '—'
}
const detailVisible = ref(false)
const detailLoading = ref(false)
const detailData = ref<Record<string, any> | null>(null)
const detailLinkedPayOrders = computed(() => {
const d = detailData.value
if (!d) return []
const arr = d.linked_pay_orders
return Array.isArray(arr) ? arr : []
})
const detailLogisticsExpress = ref<string>('auto')
const logisticsTraceLoading = ref(false)
const logisticsTracePayload = ref<Record<string, any> | null>(null)
const detailOfficialUrls = computed(() => {
const n = String(detailData.value?.tracking_number || '').trim()
if (!n) {
return { sf: 'javascript:void(0)', jd: 'javascript:void(0)', jt: 'javascript:void(0)' }
}
const enc = encodeURIComponent(n)
return {
// 顺丰速运官网查询(新版)
sf: `https://www.sf-express.com/cn/sc/dynamic_function/waybill/#search/bill-number/${enc}`,
// 京东物流官网查询
jd: `https://www.jdl.com/#/trackQuery?waybillCode=${enc}`,
// 极兔速递官网查询
jt: `https://www.jtexpress.com.cn/index/query/gzquery.html?bills=${enc}`
}
})
const logisticsTraceList = computed(() => {
const p = logisticsTracePayload.value
if (!p || !Array.isArray(p.traces)) return []
return p.traces as Array<{ time: string; context: string }>
})
function expressCompanyLabel(v: unknown) {
const s = String(v || '').toLowerCase()
if (s === 'sf') return '顺丰速运'
if (s === 'jd') return '京东快递'
if (s === 'jt' || s === 'jtexpress') return '极兔速递'
return '自动识别'
}
async function fetchLogisticsTrace() {
const id = Number(detailData.value?.id)
if (!id) return
logisticsTraceLoading.value = true
try {
const res: any = await prescriptionOrderLogisticsTrace({
id,
express_company: detailLogisticsExpress.value
})
logisticsTracePayload.value = (res?.data ?? res) as Record<string, any>
} catch {
logisticsTracePayload.value = null
} finally {
logisticsTraceLoading.value = false
}
}
async function openDetail(id: number) {
detailVisible.value = true
detailData.value = null
logisticsTracePayload.value = null
detailLogisticsExpress.value = 'auto'
detailLoading.value = true
try {
const res: any = await prescriptionOrderDetail({ id })
const d = res?.data ?? res ?? null
detailData.value = d
if (d) {
detailLogisticsExpress.value = String(d.express_company || 'auto') || 'auto'
// 如果有快递单号,自动加载物流轨迹
if (String(d.tracking_number || '').trim()) {
fetchLogisticsTrace()
}
}
} catch (e: any) {
feedback.msgError(e?.message || '加载失败')
detailVisible.value = false
} finally {
detailLoading.value = false
}
}
const editVisible = ref(false)
const editDialogLoading = ref(false)
const editSaving = ref(false)
const editFormRef = ref<FormInstance>()
const editForm = reactive({
id: 0,
diagnosis_id: 0,
recipient_name: '',
recipient_phone: '',
shipping_address: '',
is_follow_up: 0,
medication_days: undefined as number | undefined,
prev_staff: '',
service_channel: '',
service_package: '',
express_company: 'auto',
tracking_number: '',
fee_type: 3,
amount: 0,
remark_extra: '',
pay_order_ids: [] as number[],
internal_cost: undefined as number | undefined
})
const editPaidOrders = ref<Array<{ id: number; order_no?: string; amount?: number | string }>>([])
const editPaidOrdersLoading = ref(false)
async function loadEditPaidOrders(diagnosisId: number, prescriptionOrderId?: number) {
if (!diagnosisId) {
editPaidOrders.value = []
return
}
try {
editPaidOrdersLoading.value = true
const res: any = await prescriptionOrderPaidPayOrders({
diagnosis_id: diagnosisId,
...(prescriptionOrderId ? { prescription_order_id: prescriptionOrderId } : {})
})
const lists = res?.lists ?? res?.data?.lists ?? []
editPaidOrders.value = Array.isArray(lists) ? lists : []
} catch {
editPaidOrders.value = []
} finally {
editPaidOrdersLoading.value = false
}
}
const editRules: FormRules = {
recipient_name: [{ required: true, message: '请输入收货人', trigger: 'blur' }],
recipient_phone: [{ required: true, message: '请输入手机', trigger: 'blur' }],
shipping_address: [{ required: true, message: '请输入地址', trigger: 'blur' }],
fee_type: [{ required: true, message: '请选择费用类别', trigger: 'change' }],
amount: [{ required: true, message: '请输入金额', trigger: 'blur' }]
}
async function openEdit(row: { id: number }) {
editVisible.value = true
editDialogLoading.value = true
try {
const res: any = await prescriptionOrderDetail({ id: row.id })
const d = res?.data ?? res
if (!d) {
feedback.msgError('加载失败')
editVisible.value = false
return
}
editForm.id = d.id
editForm.diagnosis_id = Number(d.diagnosis_id) || 0
editForm.recipient_name = d.recipient_name || ''
editForm.recipient_phone = d.recipient_phone || ''
editForm.shipping_address = d.shipping_address || ''
editForm.is_follow_up = d.is_follow_up ? 1 : 0
editForm.medication_days = d.medication_days > 0 ? d.medication_days : undefined
editForm.prev_staff = d.prev_staff || ''
editForm.service_channel = d.service_channel || ''
editForm.service_package = d.service_package || ''
editForm.express_company = String(d.express_company || 'auto') || 'auto'
editForm.tracking_number = d.tracking_number || ''
editForm.fee_type = Number(d.fee_type) || 3
editForm.amount = Number(d.amount) || 0
editForm.remark_extra = d.remark_extra || ''
const pids = d.pay_order_ids
editForm.pay_order_ids = Array.isArray(pids) ? pids.map((x: unknown) => Number(x)).filter((n) => !Number.isNaN(n)) : []
editForm.internal_cost =
d.internal_cost != null && d.internal_cost !== '' ? Number(d.internal_cost) : undefined
await loadEditPaidOrders(editForm.diagnosis_id, editForm.id)
} catch (e: any) {
feedback.msgError(e?.message || '加载失败')
editVisible.value = false
} finally {
editDialogLoading.value = false
}
}
async function submitEdit() {
if (!editFormRef.value) return
await editFormRef.value.validate()
editSaving.value = true
try {
const payload: Record<string, unknown> = {
id: editForm.id,
recipient_name: editForm.recipient_name,
recipient_phone: editForm.recipient_phone,
shipping_address: editForm.shipping_address,
is_follow_up: editForm.is_follow_up,
prev_staff: editForm.prev_staff || '',
service_channel: editForm.service_channel || '',
service_package: editForm.service_package || '',
express_company: editForm.express_company || 'auto',
tracking_number: editForm.tracking_number || '',
fee_type: editForm.fee_type,
amount: editForm.amount,
remark_extra: editForm.remark_extra || ''
}
if (editForm.medication_days != null && editForm.medication_days > 0) {
payload.medication_days = editForm.medication_days
}
payload.pay_order_ids = [...editForm.pay_order_ids]
if (editForm.internal_cost != null && editForm.internal_cost >= 0) {
payload.internal_cost = editForm.internal_cost
}
await prescriptionOrderEdit(payload)
feedback.msgSuccess('保存成功')
editVisible.value = false
getLists()
} catch {
/* 拦截器已提示 */
} finally {
editSaving.value = false
}
}
const auditVisible = ref(false)
const auditKind = ref<'prescription' | 'payment'>('prescription')
const auditTargetId = ref(0)
const auditRemark = ref('')
const auditSubmitting = ref(false)
const auditTitle = computed(() =>
auditKind.value === 'prescription' ? '处方审核' : '关联支付单审核'
)
function openAudit(row: { id: number }, kind: 'prescription' | 'payment') {
auditKind.value = kind
auditTargetId.value = row.id
auditRemark.value = ''
auditVisible.value = true
}
async function submitAudit(action: 'approve' | 'reject') {
if (action === 'reject' && !auditRemark.value.trim()) {
feedback.msgError('驳回时请填写审核意见')
return
}
auditSubmitting.value = true
try {
const params = {
id: auditTargetId.value,
action,
remark: auditRemark.value.trim()
}
if (auditKind.value === 'prescription') {
await prescriptionOrderAuditPrescription(params)
} else {
await prescriptionOrderAuditPayment(params)
}
feedback.msgSuccess('已提交')
auditVisible.value = false
getLists()
} catch {
/* 拦截器已提示 */
} finally {
auditSubmitting.value = false
}
}
async function confirmWithdraw(row: { id: number }) {
try {
await ElMessageBox.confirm(
'确认撤回该业务订单?撤回后状态为「已取消」,未双审通过或双审均驳回等「待双审通过」场景均可撤回。',
'撤回业务订单',
{ type: 'warning', confirmButtonText: '撤回', cancelButtonText: '取消' }
)
await prescriptionOrderWithdraw({ id: row.id })
feedback.msgSuccess('已撤回')
getLists()
} catch (e: any) {
if (e !== 'cancel' && e !== 'close') {
/* 拦截器已提示 */
}
}
}
onMounted(() => {
getLists()
})
</script>