This commit is contained in:
Your Name
2026-04-22 12:14:39 +08:00
parent a1a0b74596
commit 4872e4d2e6
21 changed files with 1131 additions and 235 deletions
+344 -22
View File
@@ -40,15 +40,21 @@
</el-form-item>
<el-form-item class="w-[280px]" label="订单类型">
<el-select v-model="queryParams.order_type" placeholder="请选择" clearable>
<el-select v-model="queryParams.order_type" class="w-full" placeholder="全部">
<el-option label="全部" value="" />
<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 class="w-[280px]" label="订单状态">
<el-select v-model="queryParams.status" placeholder="请选择" clearable>
<el-select v-model="queryParams.status" class="w-full" placeholder="全部">
<el-option label="全部" value="" />
<el-option label="待支付" :value="1" />
<el-option label="已支付" :value="2" />
<el-option label="已取消" :value="3" />
@@ -67,6 +73,12 @@
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
<el-button
v-perms="['order.order/lists']"
@click="statsDialogVisible = true"
>
操作统计
</el-button>
<export-data
v-perms="['order.order/export']"
class="ml-2.5"
@@ -113,11 +125,23 @@
<!-- 数据表格 -->
<el-card class="!border-none mt-4" shadow="never">
<div v-perms="['order.order/zhipai']" class="mb-3 flex items-center gap-3">
<el-button type="primary" :disabled="!selectedOrderIds.length" @click="openAssignDialog">
将创建人指给医助
</el-button>
<span v-if="selectedOrderIds.length" class="text-sm text-gray-500"
>已选 {{ selectedOrderIds.length }} </span
>
</div>
<el-table
ref="orderTableRef"
v-loading="pager.loading"
:data="pager.lists"
row-key="id"
size="large"
@selection-change="handleOrderSelectionChange"
>
<el-table-column type="selection" width="48" />
<el-table-column label="订单号" prop="order_no" min-width="150" />
<el-table-column label="患者信息" min-width="150">
@@ -202,7 +226,7 @@
小程序码
</el-button>
<el-button
v-if="row.status === 1"
v-if="row.status === 1 || (row.status === 5 && row.payment_method === 'fubei')"
v-perms="['order.order/pay']"
type="success"
link
@@ -220,7 +244,7 @@
退款
</el-button>
<el-button
v-if="row.status === 1"
v-if="row.status === 1 || (row.status === 5 && row.payment_method === 'fubei')"
v-perms="['order.order/cancel']"
type="danger"
link
@@ -249,7 +273,7 @@
<el-dialog
v-model="detailVisible"
title="订单详情"
width="700px"
width="820px"
>
<el-descriptions :column="2" border v-if="detailData">
<el-descriptions-item label="订单号">
@@ -296,6 +320,87 @@
{{ detailData.remark || '无' }}
</el-descriptions-item>
</el-descriptions>
<el-divider content-position="left" v-if="detailData">操作记录</el-divider>
<p v-if="detailData" class="text-xs text-gray-500 mb-2">谁在什么时间对本单做了哪些操作数据来自服务器记录</p>
<el-table
v-if="detailData"
v-loading="actionLogLoading"
:data="actionLogList"
size="small"
max-height="320"
>
<el-table-column label="操作" prop="action_label" min-width="120" show-overflow-tooltip />
<el-table-column label="操作人" prop="admin_name" min-width="100" />
<el-table-column label="操作时间" prop="create_time_text" min-width="160" />
<el-table-column label="说明" prop="summary" min-width="140" show-overflow-tooltip />
</el-table>
<div v-if="detailData" class="flex justify-end mt-3">
<el-pagination
v-model:current-page="actionLogPage"
v-model:page-size="actionLogPageSize"
:total="actionLogTotal"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next"
small
@size-change="onActionLogSizeChange"
@current-change="loadOrderActionLogs"
/>
</div>
</el-dialog>
<!-- 按人操作统计时间范围内 -->
<el-dialog
v-model="statsDialogVisible"
title="支付单操作统计"
width="520px"
@opened="onStatsDialogOpen"
>
<p v-if="statsRangeText" class="text-xs text-gray-500 mb-3">统计范围{{ statsRangeText }}</p>
<el-form class="mb-3" :inline="true" @submit.prevent>
<el-form-item label="时间范围">
<el-date-picker
v-model="statsDateRange"
type="daterange"
value-format="YYYY-MM-DD"
range-separator=""
start-placeholder="开始"
end-placeholder="结束"
class="!w-[260px]"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="statsLoading" @click="fetchActionLogStats">查询</el-button>
</el-form-item>
</el-form>
<el-table v-loading="statsLoading" :data="statsList" size="small" max-height="400">
<el-table-column label="操作人" prop="admin_name" min-width="120" show-overflow-tooltip />
<el-table-column label="操作次数" prop="cnt" width="100" align="right" />
</el-table>
</el-dialog>
<el-dialog v-model="assignDialogVisible" title="变更创建人(医助)" width="440px" destroy-on-close>
<p class="text-sm text-gray-600 mb-3">
{{ selectedOrderIds.length }} 条支付单的创建人改为所选医助并写入各单操作日志
</p>
<el-select
v-model="assignForm.assistant_id"
filterable
placeholder="选择医助"
class="w-full"
>
<el-option
v-for="item in assistantOptions"
:key="item.id"
:label="
item.dept_names ? `${item.name}${item.dept_names}` : item.name
"
:value="Number(item.id)"
/>
</el-select>
<template #footer>
<el-button @click="assignDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="assignLoading" @click="submitBatchAssign">确定</el-button>
</template>
</el-dialog>
<!-- 支付弹窗 -->
@@ -305,7 +410,14 @@
width="500px"
>
<el-form :model="payForm" label-width="100px">
<el-form-item label="支付类型" required>
<el-alert
v-if="payForm.fubeiPendingAudit"
type="warning"
:closable="false"
class="mb-3"
title="该单为付呗·待审核,请选择实际到账方式后确认「已支付」"
/>
<el-form-item v-if="!payForm.fubeiPendingAudit" label="支付类型" required>
<el-radio-group v-model="payForm.payType">
<!-- <el-radio label="normal">正常支付</el-radio> -->
<el-radio label="supplement">补单支付</el-radio>
@@ -321,7 +433,7 @@
</el-form-item>
</template>
<template v-if="payForm.payType === 'supplement'">
<template v-if="!payForm.fubeiPendingAudit && payForm.payType === 'supplement'">
<el-form-item label="支付订单号" required>
<el-input
v-model="payForm.supplement_order_no"
@@ -329,9 +441,18 @@
/>
</el-form-item>
</template>
<template v-if="payForm.fubeiPendingAudit">
<el-form-item label="订单号">
<span>{{ payForm.order_no }}</span>
</el-form-item>
<el-form-item label="金额">
<span class="text-red-500 font-semibold">¥{{ payForm.amount }}</span>
</el-form-item>
</template>
<el-form-item label="支付方式" required>
<el-radio-group v-model="payForm.payment_method">
<el-radio v-if="payForm.fubeiPendingAudit" label="manual">手动确认到账</el-radio>
<el-radio label="alipay">支付宝</el-radio>
<el-radio label="wechat">微信</el-radio>
<el-radio label="wechat_work">企业微信</el-radio>
@@ -361,6 +482,7 @@
<el-radio-group v-model="createForm.createType">
<el-radio label="normal">普通订单</el-radio>
<el-radio label="wechat_work">企业微信对外收款</el-radio>
<el-radio label="fubei">付呗</el-radio>
</el-radio-group>
</el-form-item>
<el-alert
@@ -373,6 +495,26 @@
<template #title>企业微信对外收款</template>
创建后将生成订单号请在企业微信发起收款时将该订单号填写为商户订单号收款成功后系统将自动更新订单状态
</el-alert>
<el-alert
v-if="createForm.createType === 'fubei'"
type="info"
:closable="false"
show-icon
class="mb-4"
>
<template #title>付呗</template>
通过付呗收款的支付单创建后请按实际对账/审核流程在列表中处理
</el-alert>
<el-form-item v-if="createForm.createType === 'fubei'" label="支付单审核">
<el-switch
v-model="createForm.requirePaymentSlipAudit"
active-text="申请审核"
inactive-text="不申请"
/>
<div class="text-xs text-gray-500 mt-1">
开启后新订单的支付状态为待审核未开启则为待支付
</div>
</el-form-item>
<el-form-item label="患者" prop="patient_id">
<el-select
v-model="createForm.patient_id"
@@ -396,6 +538,10 @@
<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>
@@ -538,8 +684,29 @@
</template>
<script setup lang="ts" name="orderList">
import { computed } from 'vue'
import { usePaging } from '@/hooks/usePaging'
import { orderLists, orderDetail, orderPay, orderCancel, orderDelete, orderRefund, orderExport, orderCreate, orderCreateForWechatWork, orderEdit, orderTodayRevenue, syncWechatWorkBills, syncWechatWorkBillsDebug, searchPatients as searchPatientsAPI, alipayPay, wechatPay } from '@/api/order'
import {
orderLists,
orderDetail,
orderPay,
orderCancel,
orderDelete,
orderRefund,
orderExport,
orderCreate,
orderCreateForWechatWork,
orderEdit,
orderTodayRevenue,
syncWechatWorkBills,
syncWechatWorkBillsDebug,
searchPatients as searchPatientsAPI,
alipayPay,
wechatPay,
orderActionLogs,
orderActionLogStats,
orderBatchAssignAssistant
} from '@/api/order'
import { getAssistants } from '@/api/tcm'
import { generateOrderQrcode } from '@/api/tcm'
import { Loading } from '@element-plus/icons-vue'
@@ -556,7 +723,18 @@ const queryParams = reactive({
create_time_end: ''
})
const assistantOptions = ref<Array<{ id: number; name: string }>>([])
const assistantOptions = ref<
Array<{ id: number; name: string; account?: string; dept_names?: string; dept_ids?: number[] }>
>([])
const orderTableRef = ref<any>(null)
const selectedOrderRows = ref<any[]>([])
const selectedOrderIds = computed(() => selectedOrderRows.value.map((r) => r.id as number).filter((id) => id > 0))
const assignDialogVisible = ref(false)
const assignLoading = ref(false)
const assignForm = reactive({
assistant_id: undefined as number | undefined
})
const patientAssociationTab = ref<'' | 'pending' | 'associated'>('')
const todayRevenue = ref({ amount: '0.00', count: 0 })
@@ -566,6 +744,42 @@ const { pager, getLists, resetPage, resetParams } = usePaging({
params: queryParams
})
const handleOrderSelectionChange = (rows: any[]) => {
selectedOrderRows.value = rows || []
}
const openAssignDialog = () => {
if (!selectedOrderIds.value.length) {
feedback.msgWarning('请先勾选要指派的支付单')
return
}
assignForm.assistant_id = undefined
assignDialogVisible.value = true
}
const submitBatchAssign = async () => {
if (assignForm.assistant_id == null) {
feedback.msgWarning('请选择医助')
return
}
assignLoading.value = true
try {
await orderBatchAssignAssistant({
order_ids: [...selectedOrderIds.value],
assistant_id: assignForm.assistant_id
})
feedback.msgSuccess('指派成功')
assignDialogVisible.value = false
orderTableRef.value?.clearSelection()
getLists()
fetchTodayRevenue()
} catch (e: any) {
if (e?.msg) feedback.msgError(e.msg)
} finally {
assignLoading.value = false
}
}
// 搜索
const handleSearch = () => {
resetPage()
@@ -595,14 +809,82 @@ const handleReset = () => {
const detailVisible = ref(false)
const detailData = ref<any>(null)
const currentOrderIdForLog = ref(0)
const actionLogLoading = ref(false)
const actionLogList = ref<any[]>([])
const actionLogTotal = ref(0)
const actionLogPage = ref(1)
const actionLogPageSize = ref(10)
const statsDialogVisible = ref(false)
const statsLoading = ref(false)
const statsList = ref<any[]>([])
const statsDateRange = ref<[string, string] | null>(null)
const statsRangeText = ref('')
const loadOrderActionLogs = async () => {
if (!currentOrderIdForLog.value) {
return
}
actionLogLoading.value = true
try {
const res: any = await orderActionLogs({
order_id: currentOrderIdForLog.value,
page_no: actionLogPage.value,
page_size: actionLogPageSize.value
})
actionLogList.value = res?.lists || []
actionLogTotal.value = res?.count ?? 0
} catch {
actionLogList.value = []
actionLogTotal.value = 0
} finally {
actionLogLoading.value = false
}
}
const onActionLogSizeChange = () => {
actionLogPage.value = 1
loadOrderActionLogs()
}
const onStatsDialogOpen = () => {
statsDateRange.value = null
fetchActionLogStats()
}
const fetchActionLogStats = async () => {
statsLoading.value = true
try {
const params: { start_time?: string; end_time?: string } = {}
if (statsDateRange.value && statsDateRange.value.length === 2) {
params.start_time = statsDateRange.value[0]
params.end_time = statsDateRange.value[1]
}
const res: any = await orderActionLogStats(Object.keys(params).length ? params : undefined)
statsList.value = res?.list || []
if (res?.start_time && res?.end_time) {
statsRangeText.value = `${res.start_time} ${res.end_time}`
} else {
statsRangeText.value = ''
}
} catch {
statsList.value = []
statsRangeText.value = ''
} finally {
statsLoading.value = false
}
}
const payDialogVisible = ref(false)
const payLoading = ref(false)
const payForm = ref({
order_id: 0,
order_no: '',
amount: 0,
payment_method: 'alipay',
payType: 'supplement',
/** 付呗+待审核:简化弹窗,确认后标已付 */
fubeiPendingAudit: false,
payment_method: 'alipay' as 'alipay' | 'wechat' | 'wechat_work' | 'manual',
payType: 'supplement' as 'supplement',
supplement_order_no: ''
})
@@ -614,7 +896,9 @@ const patientLoading = ref(false)
const patientList = ref<any[]>([])
const createForm = reactive({
createType: 'normal' as 'normal' | 'wechat_work',
createType: 'normal' as 'normal' | 'wechat_work' | 'fubei',
/** 仅「付呗」:开启后订单为待审核(5) */
requirePaymentSlipAudit: false,
patient_id: '',
order_type: '',
amount: 0,
@@ -662,6 +946,7 @@ const searchPatients = async (query: string) => {
// 重置创建表单
const resetCreateForm = () => {
createForm.createType = 'normal'
createForm.requirePaymentSlipAudit = false
createForm.patient_id = ''
createForm.order_type = ''
createForm.amount = 0
@@ -774,22 +1059,38 @@ const submitCreateOrder = async () => {
createLoading.value = true
const params = {
patient_id: createForm.patient_id,
order_type: createForm.order_type,
amount: createForm.amount,
remark: createForm.remark
}
if (createForm.createType === 'wechat_work') {
const params = {
patient_id: createForm.patient_id,
order_type: createForm.order_type,
amount: createForm.amount,
remark: createForm.remark
}
const res: any = await orderCreateForWechatWork(params)
const orderNo = res?.order_no ?? res?.data?.order_no
createDialogVisible.value = false
getLists()
feedback.msgSuccess(orderNo ? `订单创建成功,请在企业微信收款时使用订单号:${orderNo}` : '订单创建成功')
} else {
const params: Record<string, unknown> = {
patient_id: createForm.patient_id,
order_type: createForm.order_type,
amount: createForm.amount,
remark: createForm.remark
}
if (createForm.createType === 'fubei') {
params.payment_channel = 'fubei'
params.require_payment_slip_audit = createForm.requirePaymentSlipAudit ? 1 : 0
} else {
params.payment_channel = 'normal'
params.require_payment_slip_audit = 0
}
await orderCreate(params)
feedback.msgSuccess('订单创建成功')
const tip =
createForm.createType === 'fubei' && createForm.requirePaymentSlipAudit
? '订单已创建,支付状态为「待审核」'
: '订单创建成功'
feedback.msgSuccess(tip)
createDialogVisible.value = false
getLists()
}
@@ -854,7 +1155,9 @@ const getPaymentMethodText = (method: string | null) => {
const methodMap: Record<string, string> = {
alipay: '支付宝',
wechat: '微信',
wechat_work: '企业微信'
wechat_work: '企业微信',
fubei: '付呗',
manual: '手动确认'
}
return methodMap[method] || '未知'
}
@@ -912,7 +1215,10 @@ const handleDetail = async (row: any) => {
try {
const res = await orderDetail({ id: row.id })
detailData.value = res
currentOrderIdForLog.value = row.id
actionLogPage.value = 1
detailVisible.value = true
loadOrderActionLogs()
} catch (error) {
console.error('获取详情失败:', error)
}
@@ -920,11 +1226,13 @@ const handleDetail = async (row: any) => {
// 支付订单
const handlePay = (row: any) => {
const fubeiPending = row.status === 5 && row.payment_method === 'fubei'
payForm.value = {
order_id: row.id,
order_no: row.order_no,
amount: row.amount,
payment_method: 'alipay',
fubeiPendingAudit: fubeiPending,
payment_method: fubeiPending ? 'manual' : 'alipay',
payType: 'supplement',
supplement_order_no: ''
}
@@ -934,6 +1242,20 @@ const handlePay = (row: any) => {
// 确认支付
const confirmPay = async () => {
try {
// 付呗待审核:直接标记已支付
if (payForm.value.fubeiPendingAudit) {
payLoading.value = true
await orderPay({
id: payForm.value.order_id,
payment_method: payForm.value.payment_method
})
feedback.msgSuccess('已更新为已支付')
payDialogVisible.value = false
getLists()
fetchTodayRevenue()
return
}
// 验证补单支付的订单号
if (payForm.value.payType === 'supplement' && !payForm.value.supplement_order_no) {
feedback.msgWarning('请输入支付订单号')