This commit is contained in:
Your Name
2026-04-30 14:04:12 +08:00
parent eb782305e6
commit d3003ce0ac
19 changed files with 1560 additions and 313 deletions
+139 -3
View File
@@ -103,6 +103,15 @@
>
批量指派医助
</el-button>
<el-button
v-perms="['tcm.diagnosis/assign']"
type="warning"
plain
@click="handleBatchCancelAssign"
:disabled="!canBatchCancelAssign"
>
批量取消指派
</el-button>
</div>
<div v-if="selectedIds.length > 0" class="list-selected">已选 {{ selectedIds.length }} </div>
</div>
@@ -263,9 +272,16 @@
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-if="hasPermission(['tcm.diagnosis/assign'])" command="assign"><el-icon><User /></el-icon>指派</el-dropdown-item>
<el-dropdown-item
v-if="hasPermission(['tcm.diagnosis/assign']) && hasAssignedAssistant(row)"
command="cancelAssign"
>
<el-icon><Remove /></el-icon>取消指派
</el-dropdown-item>
<el-dropdown-item v-if="isAppointmentActiveForVideo(row) && hasPermission(['tcm.diagnosis/videoQr'])" command="videoQr"><el-icon><Picture /></el-icon>视频二维码</el-dropdown-item>
<el-dropdown-item v-if="hasPermission(['tcm.diagnosis/guahao']) && isAppointmentActiveForVideo(row)" command="confirmQr"><el-icon><Picture /></el-icon>二维码</el-dropdown-item>
<el-dropdown-item v-if="canCancelAppointmentRow(row) && hasPermission(['tcm.diagnosis/guahao'])" command="cancelApt"><el-icon><CircleClose /></el-icon>取消挂号</el-dropdown-item>
<el-dropdown-item v-if="hasPermission(['tcm.diagnosis/guahaoLogList'])" command="guahaoLogs"><el-icon><List /></el-icon>挂号日志</el-dropdown-item>
<el-dropdown-item command="order" v-if="hasPermission(['tcm.diagnosis/order'])"><el-icon><Document /></el-icon>创建订单</el-dropdown-item>
<el-dropdown-item v-if="hasPermission(['tcm.diagnosis/delete'])" command="delete" divided><el-icon><Delete /></el-icon><span class="text-danger">删除</span></el-dropdown-item>
</el-dropdown-menu>
@@ -284,12 +300,37 @@
<edit-popup ref="editRef" @success="refreshListAfterPopupSave" />
<detail-popup ref="detailRef" />
<tcm-prescription ref="prescriptionRef" @success="onPrescriptionSuccess" />
<appointment-popup ref="appointmentRef" @success="refreshListAfterPopupSave" />
<appointment-popup ref="appointmentRef" @success="handleAppointmentSaved" />
<assistant-watch-call-dialog
v-model="watchCallVisible"
:diagnosis-id="watchCallDiagnosisId"
@closed="getLists"
/>
<el-drawer v-model="guahaoLogDrawerVisible" title="挂号操作日志" size="460px" destroy-on-close @closed="resetGuahaoLogDrawer">
<div v-loading="guahaoLogLoading" class="min-h-[100px]">
<div v-if="guahaoLogContextRow" class="text-sm text-gray-600 mb-4 pb-3 border-b border-gray-100">
<span class="font-medium text-gray-800">{{ guahaoLogContextRow.patient_name }}</span>
<span class="mx-2 text-gray-300">|</span>
<span>诊单 #{{ guahaoLogContextRow.id }}</span>
</div>
<el-empty v-if="!guahaoLogLoading && guahaoLogRows.length === 0" description="暂无挂号/取消挂号操作记录" />
<el-timeline v-else-if="guahaoLogRows.length > 0">
<el-timeline-item
v-for="(log, idx) in guahaoLogRows"
:key="log.id ?? idx"
:timestamp="log.create_time_text || '—'"
placement="top"
>
<div class="flex flex-wrap items-center gap-2">
<el-tag size="small" type="info">{{ log.action_desc }}</el-tag>
<span class="text-sm font-medium text-gray-800">{{ formatGuahaoLogOperator(log) }}</span>
</div>
<div class="text-xs text-gray-600 mt-1.5 leading-relaxed">{{ log.summary }}</div>
</el-timeline-item>
</el-timeline>
</div>
</el-drawer>
<!-- 小程序二维码弹窗 -->
<el-dialog
@@ -564,7 +605,7 @@
</template>
<script setup lang="ts" name="tcmDiagnosis">
import { tcmDiagnosisLists, tcmDiagnosisDelete, tcmDiagnosisAssign, getAssistants, generateMiniProgramQrcode, generateOrderQrcode, getWechatChatRecords, addWechatChatRecord, deleteWechatChatRecord, getWechatExternalContact, fillIdCard } from '@/api/tcm'
import { tcmDiagnosisLists, tcmDiagnosisDelete, tcmDiagnosisAssign, tcmDiagnosisGuahaoLogList, getAssistants, generateMiniProgramQrcode, generateOrderQrcode, getWechatChatRecords, addWechatChatRecord, deleteWechatChatRecord, getWechatExternalContact, fillIdCard } from '@/api/tcm'
import { orderCreate } from '@/api/order'
import { cancelAppointment } from '@/api/doctor'
import { usePaging } from '@/hooks/usePaging'
@@ -572,7 +613,7 @@ import { getDictData } from '@/api/app'
import { getWeappConfig } from '@/api/channel/weapp'
import feedback from '@/utils/feedback'
import { hasPermission } from '@/utils/perm'
import { Loading, Warning, List, CircleCheck, Clock, ArrowDown, Picture, User, Document, Delete, CircleClose } from '@element-plus/icons-vue'
import { Loading, Warning, List, CircleCheck, Clock, ArrowDown, Picture, User, Document, Delete, CircleClose, Remove } from '@element-plus/icons-vue'
import useUserStore from '@/stores/modules/user'
import { useRoute } from 'vue-router'
import { computed, defineAsyncComponent, onMounted, onUnmounted, watch } from 'vue'
@@ -1116,6 +1157,19 @@ const handleSelectionChange = (selection: any[]) => {
selectedIds.value = selection.map(item => item.id)
}
/** 当前行是否已指派医助(用于取消指派入口) */
const hasAssignedAssistant = (row: any) => {
const id = Number(row?.assistant_id ?? row?.assistant ?? 0)
return id > 0
}
/** 当前页勾选记录中是否存在已指派医助的诊单(批量取消指派) */
const canBatchCancelAssign = computed(() => {
if (!selectedIds.value.length) return false
const set = new Set(selectedIds.value)
return pager.lists.some((r: any) => set.has(r.id) && hasAssignedAssistant(r))
})
// 单独指派
const handleSingleAssign = (row: any) => {
assignMode.value = 'single'
@@ -1172,6 +1226,34 @@ const handleConfirmAssign = async () => {
}
}
/** 取消指派:将 assistant_id 置为 0(与后端 assign 一致) */
const handleCancelAssign = async (row: any) => {
if (!row?.id || !hasAssignedAssistant(row)) {
feedback.msgWarning('该诊单未指派医助')
return
}
await feedback.confirm('确定取消该诊单的医助指派吗?')
await tcmDiagnosisAssign({ id: row.id, assistant_id: 0 })
getLists()
}
const handleBatchCancelAssign = async () => {
if (!selectedIds.value.length) {
feedback.msgWarning('请先选择诊单')
return
}
const set = new Set(selectedIds.value)
const rows = pager.lists.filter((r: any) => set.has(r.id) && hasAssignedAssistant(r))
if (!rows.length) {
feedback.msgWarning('所选诊单中没有已指派医助的记录')
return
}
await feedback.confirm(`确定取消 ${rows.length} 条诊单的医助指派吗?`)
await Promise.all(rows.map((r: any) => tcmDiagnosisAssign({ id: r.id, assistant_id: 0 })))
selectedIds.value = []
getLists()
}
const handleAdd = () => {
editRef.value.open('add')
}
@@ -1222,6 +1304,7 @@ const handleDelete = async (id: number) => {
const handleRowAction = (cmd: string, row: any) => {
switch (cmd) {
case 'assign': handleSingleAssign(row); break
case 'cancelAssign': handleCancelAssign(row); break
case 'videoQr':
if (!isAppointmentActiveForVideo(row)) {
feedback.msgWarning('仅「已预约」状态可生成视频二维码')
@@ -1231,6 +1314,7 @@ const handleRowAction = (cmd: string, row: any) => {
break
case 'confirmQr': handleMiniProgramQRCode(row); break
case 'cancelApt': handleCancelAppointment(row); break
case 'guahaoLogs': openGuahaoLogDrawer(row); break
case 'order': handleCreateOrder(row); break
case 'delete': handleDelete(row.id); break
}
@@ -1309,11 +1393,63 @@ const handleCancelAppointment = async (row: any) => {
feedback.msgSuccess('取消挂号成功')
getLists()
fetchDateCounts()
if (guahaoLogDrawerVisible.value && guahaoLogContextRow.value?.id === row.id) {
await fetchGuahaoLogs(row.id)
}
} catch (e: any) {
if (e !== 'cancel') feedback.msgError(e?.msg || '取消挂号失败')
}
}
const guahaoLogDrawerVisible = ref(false)
const guahaoLogLoading = ref(false)
const guahaoLogRows = ref<any[]>([])
const guahaoLogContextRow = ref<any>(null)
function resetGuahaoLogDrawer() {
guahaoLogRows.value = []
guahaoLogContextRow.value = null
}
function formatGuahaoLogOperator(log: any) {
const name = String(log?.admin_name || '').trim()
const aid = Number(log?.admin_id)
if (name) return name
if (aid > 0) return `管理员 #${aid}`
return '—'
}
async function fetchGuahaoLogs(diagnosisId: number) {
guahaoLogLoading.value = true
try {
const res: any = await tcmDiagnosisGuahaoLogList({ id: diagnosisId })
guahaoLogRows.value = Array.isArray(res?.data) ? res.data : Array.isArray(res) ? res : []
} catch {
guahaoLogRows.value = []
} finally {
guahaoLogLoading.value = false
}
}
async function openGuahaoLogDrawer(row: any) {
if (!row?.id) {
feedback.msgWarning('诊单信息不完整')
return
}
guahaoLogContextRow.value = row
guahaoLogDrawerVisible.value = true
await fetchGuahaoLogs(Number(row.id))
}
/** 预约抽屉保存成功后:静默刷新列表 + 顶部计数;若挂号日志抽屉打开则同步日志 */
async function handleAppointmentSaved() {
await getLists({ silent: true })
fetchDateCounts()
if (guahaoLogDrawerVisible.value && guahaoLogContextRow.value?.id) {
await fetchGuahaoLogs(Number(guahaoLogContextRow.value.id))
}
}
// 患者挂号
const handleAppointment = (row: any) => {
if (!row.patient_id) {