新增
This commit is contained in:
@@ -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>
|
||||
Reference in New Issue
Block a user