From eb14f73b323abdf4a52dc6cd63e6ceb7539905da Mon Sep 17 00:00:00 2001 From: sa0ChunLuyu Date: Sat, 25 Jul 2026 17:58:24 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A2=84=E7=BA=A6=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../API/Admin/YeWu/PlanListController.php | 3 +- .../Services/Admin/YeWu/PlanListService.php | 26 +- YiJi-admin/index.html | 2 +- .../src/components/Yewu/YuYue202506.vue | 820 ++++++++++++++++-- YiJi-admin/src/views/YeWu/DoctorYuYue.vue | 2 + YiJi-admin/src/views/YeWu/MainList.vue | 14 +- .../src/views/YeWu/MainListWithDept.vue | 14 +- 7 files changed, 783 insertions(+), 98 deletions(-) diff --git a/Laravel/app/Http/Controllers/API/Admin/YeWu/PlanListController.php b/Laravel/app/Http/Controllers/API/Admin/YeWu/PlanListController.php index 59d9f41..615836e 100644 --- a/Laravel/app/Http/Controllers/API/Admin/YeWu/PlanListController.php +++ b/Laravel/app/Http/Controllers/API/Admin/YeWu/PlanListController.php @@ -214,9 +214,10 @@ public function GetOptimalPlan() $episodeid = request('episodeid'); $appointment_type = request('appointment_type'); $items = request('items'); + $occupied_plan_ids = request('occupied_plan_ids', []); $service = new PlanListService(); - return $service->GetOptimalPlan($type, $regnum, $entrustid, $episodeid, $appointment_type, $items); + return $service->GetOptimalPlan($type, $regnum, $entrustid, $episodeid, $appointment_type, $items, 'all', 0, $occupied_plan_ids); } //获取最近可用的,计划 日期 diff --git a/Laravel/app/Services/Admin/YeWu/PlanListService.php b/Laravel/app/Services/Admin/YeWu/PlanListService.php index e1b28c2..2f3a3a8 100644 --- a/Laravel/app/Services/Admin/YeWu/PlanListService.php +++ b/Laravel/app/Services/Admin/YeWu/PlanListService.php @@ -425,7 +425,7 @@ public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_typ } //最优时间/单天全检 分配 - public function GetOptimalPlan($type, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope = 'all', $userDeptId = 0) + public function GetOptimalPlan($type, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope = 'all', $userDeptId = 0, $occupied_plan_ids = []) { date_default_timezone_set('PRC'); $dateRange = config('app.globals.可用号源查询范围'); @@ -435,14 +435,14 @@ public function GetOptimalPlan($type, $regnum, $entrustids, $episodeid, $appoint $endDate->modify('+' . $dateRange . ' day'); if ($type === 'full_day') { - return $this->doFullDayAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId); + return $this->doFullDayAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId, $occupied_plan_ids); } else { - return $this->doOptimalTimeAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId); + return $this->doOptimalTimeAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId, $occupied_plan_ids); } } // 最优时间分配:按科室分组,逐个 item 找最早可用号源,可跨天 - private function doOptimalTimeAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId) + private function doOptimalTimeAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId, $occupied_plan_ids = []) { // 按科室分组 $deptGroups = $this->groupItemsByDept($items); @@ -471,6 +471,14 @@ private function doOptimalTimeAssign($startDate, $endDate, $regnum, $entrustids, $availablePlans = $this->getAvailablePlansForDate($regnum, $groupEntrustids, $episodeid, $appointment_type, $nowdate, $scope, $userDeptId); + // 过滤已占用的号源 + if (!empty($occupied_plan_ids)) { + $availablePlans = array_filter($availablePlans, function($plan) use ($occupied_plan_ids) { + return !in_array($plan->id, $occupied_plan_ids); + }); + $availablePlans = array_values($availablePlans); + } + if (empty($availablePlans)) { // 当天该科室无号源,整组留到下一轮 $newRemainingGroups[$deptCode] = $group; @@ -531,7 +539,7 @@ private function doOptimalTimeAssign($startDate, $endDate, $regnum, $entrustids, } // 单天全检分配:所有 item 必须在同一天完成 - private function doFullDayAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId) + private function doFullDayAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId, $occupied_plan_ids = []) { // 按科室分组 $deptGroups = $this->groupItemsByDept($items); @@ -549,6 +557,14 @@ private function doFullDayAssign($startDate, $endDate, $regnum, $entrustids, $ep foreach ($deptGroups as $deptCode => $group) { $availablePlans = $this->getAvailablePlansForDate($regnum, $group['entrustids'], $episodeid, $appointment_type, $nowdate, $scope, $userDeptId); + // 过滤已占用的号源 + if (!empty($occupied_plan_ids)) { + $availablePlans = array_filter($availablePlans, function($plan) use ($occupied_plan_ids) { + return !in_array($plan->id, $occupied_plan_ids); + }); + $availablePlans = array_values($availablePlans); + } + if (empty($availablePlans)) { $allAssigned = false; break; diff --git a/YiJi-admin/index.html b/YiJi-admin/index.html index 2d50d3a..b30ca9d 100644 --- a/YiJi-admin/index.html +++ b/YiJi-admin/index.html @@ -1,5 +1,5 @@ - + diff --git a/YiJi-admin/src/components/Yewu/YuYue202506.vue b/YiJi-admin/src/components/Yewu/YuYue202506.vue index 2471713..0d72950 100644 --- a/YiJi-admin/src/components/Yewu/YuYue202506.vue +++ b/YiJi-admin/src/components/Yewu/YuYue202506.vue @@ -3,22 +3,33 @@
{{patientInfo.user_name}}({{patientInfo.user_sex_label}} {{patientInfo.age}}岁)
-
登记号:{{patientInfo.reg_num}}
+
{{patientInfo.patient_type_label}}号:{{patientInfo.patient_type == 0 ? patientInfo.episodeid?.slice(-10) : patientInfo.reg_num}}
电话:{{patientInfo.user_phone}}
病区:{{patientInfo.warddesc}}
床号:{{patientInfo.bedname}}
-
诊断:{{patientInfo.diagnosisName}}
+
诊断:{{patientInfo.diagnosisName}}
获取患者信息失败
+ + +
- - - - + + + @@ -31,20 +42,20 @@ 完成 - - + + - + - + - + - - + + - +
-
-
+
+
{{item}}
@@ -81,18 +92,23 @@ 打印申请单隐藏按钮 打印申请单 - - 预约完成后打印申请单 - - - 自动选中能一起预约的检查项目 - +
+ + 预约完成后打印申请单 + + + 自动选中能一起预约的检查项目 + +
推荐最近时间号源 推荐同一天号源 + + 推荐同一时段号源 +
@@ -109,7 +125,7 @@ {{item2}}{{item2.use_count}} @@ -149,6 +165,31 @@
+ +
+ + + +
+

最近可用的同一时间段号源数量不足,无法把当前全部检查项目预约在同一时间段,系统已经自动拆分。

+
+
+
+ +
+ +
+ + + +
+
+ +
@@ -170,6 +211,7 @@ ElMessage, ElMessageBox } from 'element-plus' + import { WarningFilled } from '@element-plus/icons-vue' import ShenQingDan from '@/components/Yewu/PrintShenQingDan.vue' const props = defineProps({ reg_num: { @@ -206,6 +248,8 @@ let planTableData = ref([]); let loading = ref(false); let planLoading = ref(false); + let print_shenqingdan_button = ref(null) + let highlightedPlanIds = ref([]) let zhenShiList = ref([]); let activeZhenShi = ref(''); let selectedPlanId = ref(0) @@ -214,7 +258,6 @@ let selectedRows = ref([]) let isHandlingSelection = ref(true) //防止冒泡 死循环 let lastSelection = ref([]) //上一次操作勾选的记录 用了比对 是取消还是 勾选 - let print_shenqingdan_button = ref(null) let auto_print = ref(['1']); let shenqingdan_show = ref(false); let buttonsDisabled = ref(true) @@ -230,20 +273,40 @@ 2.已预约状态,预约到同一个时段的同一个检查室的项目,系统会自动选中,更改预约或取消预约只需点击一次 3.不同状态的预约的预约项目无法一起选中` let TanChuangMsgDialogVisible=ref(false) + let crossTimeDialogVisible = ref(false) + let crossTimeDialogHtml = ref('') + let resultDialogVisible = ref(false) + let resultDialogHtml = ref('') const getWeekday = (date1) => { let days = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; const date = new Date(date1); // 注意:格式可能因浏览器而异 const dayOfWeek = date.getDay(); return days[dayOfWeek] } + const sameDeptGroupCount = computed(() => { + const selectedDepts = selectedRows.value + .map(v => v.department_resources?.department_resources_name) + .filter(Boolean) + const uniqueDepts = [...new Set(selectedDepts)] + if (uniqueDepts.length !== 1) return 0 + const deptName = uniqueDepts[0] + return entrustTableDate.value.filter(v => + v.department_resources?.department_resources_name === deptName + ).length + }) + const canEmergency = computed(() => { //是否有紧急权限 return props.special_privileges.includes('emergency'); }); const hasDifferentBookedDates = computed(() => { - const booked = entrustTableDate.value.filter(v => v.list_status === 1 && v.reservation_date); - if (booked.length < 2) return false; - const dates = new Set(booked.map(v => v.reservation_date)); - return dates.size > 1; + const dated = entrustTableDate.value.filter(v => { + if (v.list_status === 1 && v.reservation_date) return true + if (v.list_status === 0 && v.temp_plan_id && v.reservation_date) return true + return false + }) + if (dated.length < 2) return false + const dates = new Set(dated.map(v => v.reservation_date)) + return dates.size > 1 }); const handleSelectionChange = (selection) => { selectedMianListId.value = [] @@ -277,6 +340,17 @@ } } else { selectedPlanId.value = 0; + // 同检查室(同一执行科室)时切换诊室 + if (selectedRows.value.length > 0) { + const firstExecId = selectedRows.value[0]?.department_resources?.execute_department_id + const allSameExecDept = firstExecId && selectedRows.value.every( + v => v.department_resources?.execute_department_id === firstExecId + ) + if (allSameExecDept) { + const firstDept = selectedRows.value[0].department_resources?.department_resources_name + if (firstDept) activeZhenShi.value = firstDept + } + } } //--------------------------------------------------------------^^^^^ @@ -343,7 +417,7 @@ } let TanChuangMsg=ref([]);//第一次进来把所有异常提示一次 - const GetMainInfo = () => { + const GetMainInfo = (callback, skipRestore) => { let tempSelected = selectedMianListId.value entrustTableDate.value = [] loading.value = true @@ -371,7 +445,7 @@ } }) - if (tempSelected.length > 0) { + if (tempSelected.length > 0 && !skipRestore) { tempSelected.forEach((v, i) => { entrustTableDate.value.forEach((v2, i2) => { if (v == v2.id) { @@ -381,19 +455,24 @@ }) }) - } else { //进来后默认选中第一个可以勾选的 + } else { //进来后默认选中第一个未预约的项目及同检查室项目 for (let i = 0; i < entrustTableDate.value.length; i++) { - if (entrustTableDate.value[i].tishi_msg == '') { + if (entrustTableDate.value[i].tishi_msg == '' && entrustTableDate.value[i].list_status === 0) { entrustTableRef.value.toggleRowSelection(entrustTableDate.value[i], true) + const dept = entrustTableDate.value[i].department_resources?.department_resources_name + // 勾选同检查室的其他未预约项目 + if (dept) { + entrustTableDate.value.forEach(v => { + if (v.id !== entrustTableDate.value[i].id && v.list_status === 0 && v.tishi_msg === '' && v.department_resources?.department_resources_name === dept) { + entrustTableRef.value.toggleRowSelection(v, true) + } + }) + } if(entrustTableDate.value[i].roster_id!=null){ selectedPlanId.value=entrustTableDate.value[i].roster_id - // startDate.value=entrustTableDate.value[i].reservation_date - //date_list.value = getDatesAfter(entrustTableDate.value[i].reservation_date) - activeZhenShi.value=entrustTableDate.value[i].department_resources?.department_resources_name + activeZhenShi.value=dept } - - - break; + break } } } @@ -408,6 +487,7 @@ } // 缓存列表原始数据(深拷贝,与显示列表解耦) cachedListData.value = JSON.parse(JSON.stringify(entrustTableDate.value)) + if (callback) callback() } } else { ElMessage.error(res.msg) @@ -561,10 +641,12 @@ // 优先高亮已分配或已预约的号源 selectedPlanId.value = 0 - const highlightPlanId = findHighlightPlanId() - if (highlightPlanId) { - selectedPlanId.value = highlightPlanId + const highlightPlanIds = findHighlightPlanIds() + if (highlightPlanIds.length > 0) { + highlightedPlanIds.value = highlightPlanIds + selectedPlanId.value = highlightPlanIds[0] } else { + highlightedPlanIds.value = [] // 没有匹配的,才高亮第一个可用号源 const firstAvailable = plans.find(v => v.department_resources_name == activeZhenShi.value && @@ -612,10 +694,53 @@ target.period_end_time = item.end_time target.temp_plan_id = item.plan_id }) + + // 跨时段检测 + const roomMap = {} + res.data.forEach(item => { + const room = item.department_resources_name + if (!roomMap[room]) roomMap[room] = { items: [], timeSlots: new Set(), dates: new Set() } + roomMap[room].items.push(item) + roomMap[room].timeSlots.add(item.begin_time?.substring(0, 5) + '-' + item.end_time?.substring(0, 5)) + roomMap[room].dates.add(item.date) + }) + const crossParts = [] + for (const [room, info] of Object.entries(roomMap)) { + if (info.timeSlots.size > 1 || info.dates.size > 1) { + info.items.forEach(v => { + const idx = entrustTableDate.value.findIndex(r => r.id === v.id) + crossParts.push({ + idx: idx !== -1 ? idx + 1 : '', + room: v.department_resources_name, + date: v.date, + timeSlot: v.begin_time?.substring(0, 5) + '-' + v.end_time?.substring(0, 5), + entrust: entrustTableDate.value.find(r => r.id === v.id)?.entrust || '' + }) + }) + } + } + if (crossParts.length > 0) { + let crossMsg = '' + crossMsg += '' + let lastSlot = '' + let bgColor = '#f5f5f5' + crossParts.sort((a, b) => a.idx - b.idx).forEach(p => { + if (p.timeSlot !== lastSlot) { + bgColor = bgColor === '#fff' ? '#f5f5f5' : '#fff' + lastSlot = p.timeSlot + } + crossMsg += '' + }) + crossMsg += '
序号项目名称检查室日期时间段
' + p.idx + '' + p.entrust + '' + p.room + '' + p.date + '' + p.timeSlot + '
' + crossTimeDialogHtml.value = crossMsg + crossTimeDialogVisible.value = true + } + // 更新高亮 - const newHighlightId = findHighlightPlanId() - if (newHighlightId) { - selectedPlanId.value = newHighlightId + const newHighlightIds = findHighlightPlanIds() + if (newHighlightIds.length > 0) { + highlightedPlanIds.value = newHighlightIds + selectedPlanId.value = newHighlightIds[0] } } }) @@ -665,31 +790,35 @@ const zhenshiClick = (zhenshi) => { activeZhenShi.value = zhenshi selectedPlanId.value = 0 + highlightedPlanIds.value = [] GetEnablePlanFunc() } - // 从已分配或已预约的行中,找到在当前排班下可高亮的号源ID - const findHighlightPlanId = () => { - // 优先找申请中行的临时分配号源 + // 从已分配或已预约的行中,收集在当前排班下可高亮的号源ID列表 + const findHighlightPlanIds = () => { + const ids = [] + // 只处理当前勾选的申请中行 for (const row of entrustTableDate.value) { + if (!selectedMianListId.value.includes(row.id)) continue if (row.list_status === 0 && row.temp_plan_id) { const plan = cachedPlans.value.find(v => v.id === row.temp_plan_id && v.department_resources_name === activeZhenShi.value ) - if (plan) return plan.id + if (plan && !ids.includes(plan.id)) ids.push(plan.id) } } - // 再找已预约行的roster_id + // 只处理当前勾选的已预约行 for (const row of entrustTableDate.value) { + if (!selectedMianListId.value.includes(row.id)) continue if (row.list_status === 1 && row.roster_id) { const plan = cachedPlans.value.find(v => v.id === row.roster_id && v.department_resources_name === activeZhenShi.value ) - if (plan) return plan.id + if (plan && !ids.includes(plan.id)) ids.push(plan.id) } } - return 0 + return ids } const PlanClick = (planid) => { console.log(planid) @@ -707,6 +836,7 @@ } selectedPlanId.value = planid + highlightedPlanIds.value = [planid] checkDaijianFuc() // 同步更新显示列表中的排班、预约日期、预约时间 @@ -750,7 +880,7 @@ }) // 收集所有申请中行的当前信息 - const items = entrustTableDate.value + const items = selectedRows.value .filter(v => v.list_status === 0) .map(v => ({ id: v.id, @@ -761,6 +891,11 @@ end_time: v.period_end_time || '' })) + // 收集未勾选但已临时匹配号源的项目ID + const occupiedPlanIds = entrustTableDate.value + .filter(v => !selectedMianListId.value.includes(v.id) && v.list_status === 0 && v.temp_plan_id) + .map(v => v.temp_plan_id) + // 调用后端接口 GetOptimalPlan({ type: 'optimal_time', @@ -769,6 +904,7 @@ episodeid: props.episode_id, appointment_type: props.appointment_type, items: items, + occupied_plan_ids: occupiedPlanIds, scope: props.scope, do_user: props.do_user }).then(res => { @@ -787,8 +923,67 @@ target.period_end_time = item.end_time target.temp_plan_id = item.plan_id }) + + // 跨时段检测 + const roomMap = {} + res.data.forEach(item => { + const room = item.department_resources_name + if (!roomMap[room]) roomMap[room] = { items: [], timeSlots: new Set(), dates: new Set() } + roomMap[room].items.push(item) + roomMap[room].timeSlots.add(item.begin_time?.substring(0, 5) + '-' + item.end_time?.substring(0, 5)) + roomMap[room].dates.add(item.date) + }) + const crossParts = [] + for (const [room, info] of Object.entries(roomMap)) { + if (info.timeSlots.size > 1 || info.dates.size > 1) { + info.items.forEach(v => { + const idx = entrustTableDate.value.findIndex(i => i.id === v.id) + crossParts.push({ + idx: idx !== -1 ? idx + 1 : '', + room: v.department_resources_name, + date: v.date, + timeSlot: v.begin_time?.substring(0, 5) + '-' + v.end_time?.substring(0, 5), + entrust: entrustTableDate.value.find(r => r.id === v.id)?.entrust || '' + }) + }) + } + } + if (crossParts.length > 0) { + let crossMsg = '' + crossMsg += '' + let lastSlot = '' + let bgColor = '#f5f5f5' + crossParts.sort((a, b) => a.idx - b.idx).forEach(p => { + if (p.timeSlot !== lastSlot) { + bgColor = bgColor === '#fff' ? '#f5f5f5' : '#fff' + lastSlot = p.timeSlot + } + crossMsg += '' + }) + crossMsg += '
序号项目名称检查室日期时间段
' + p.idx + '' + p.entrust + '' + p.room + '' + p.date + '' + p.timeSlot + '
' + crossTimeDialogHtml.value = crossMsg + crossTimeDialogVisible.value = true + } + + // 未匹配检测 + const matchedIds = new Set(res.data.map(v => v.id)) + const unmatched = items.filter(v => !matchedIds.has(v.id)) + if (unmatched.length > 0) { + let msg = '✅ 成功匹配 ' + res.data.length + ' 项:\n' + msg += res.data.map(v => ' ' + v.department_resources_name + ' ' + v.date + ' ' + v.begin_time?.substring(0, 5) + '-' + v.end_time?.substring(0, 5)).join('\n') + msg += '\n\n❌ 未匹配 ' + unmatched.length + ' 项:\n' + msg += unmatched.map(v => ' ' + (items.findIndex(i => i.id === v.id) + 1) + '、' + v.entrust_id + ':未找到合适号源').join('\n') + ElMessageBox.confirm(msg, '预约推荐结果', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', showCancelButton: false }) + } else if (res.data.length === 0) { + ElMessageBox.confirm('所有项目均未匹配到合适号源,请重新选择号源', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'error', showCancelButton: false }) + } + // 更新高亮 if (res.data.length > 0) { + // 切换诊室为第一项的排班资源 + activeZhenShi.value = res.data[0].department_resources_name + // 收集所有推荐结果的plan_id用于多高亮 + highlightedPlanIds.value = res.data.map(v => v.plan_id) selectedPlanId.value = res.data[0].plan_id } } else { @@ -818,7 +1013,7 @@ }) // 收集所有申请中行的当前信息 - const items = entrustTableDate.value + const items = selectedRows.value .filter(v => v.list_status === 0) .map(v => ({ id: v.id, @@ -829,6 +1024,11 @@ end_time: v.period_end_time || '' })) + // 收集未勾选但已临时匹配号源的项目ID + const occupiedPlanIds = entrustTableDate.value + .filter(v => !selectedMianListId.value.includes(v.id) && v.list_status === 0 && v.temp_plan_id) + .map(v => v.temp_plan_id) + // 调用后端接口 GetOptimalPlan({ type: 'full_day', @@ -837,6 +1037,7 @@ episodeid: props.episode_id, appointment_type: props.appointment_type, items: items, + occupied_plan_ids: occupiedPlanIds, scope: props.scope, do_user: props.do_user }).then(res => { @@ -855,9 +1056,152 @@ target.period_end_time = item.end_time target.temp_plan_id = item.plan_id }) - // 更新高亮 + if (res.data.length > 0) { + // 跨时段检测 + const roomMap = {} + res.data.forEach(item => { + const room = item.department_resources_name + if (!roomMap[room]) roomMap[room] = { items: [], timeSlots: new Set(), dates: new Set() } + roomMap[room].items.push(item) + roomMap[room].timeSlots.add(item.begin_time?.substring(0, 5) + '-' + item.end_time?.substring(0, 5)) + roomMap[room].dates.add(item.date) + }) + const crossParts = [] + for (const [room, info] of Object.entries(roomMap)) { + if (info.timeSlots.size > 1 || info.dates.size > 1) { + info.items.forEach(v => { + const idx = entrustTableDate.value.findIndex(i => i.id === v.id) + crossParts.push({ + idx: idx !== -1 ? idx + 1 : '', + room: v.department_resources_name, + date: v.date, + timeSlot: v.begin_time?.substring(0, 5) + '-' + v.end_time?.substring(0, 5), + entrust: entrustTableDate.value.find(r => r.id === v.id)?.entrust || '' + }) + }) + } + } + if (crossParts.length > 0) { + let crossMsg = '' + crossMsg += '' + let lastSlot = '' + let bgColor = '#f5f5f5' + crossParts.sort((a, b) => a.idx - b.idx).forEach(p => { + if (p.timeSlot !== lastSlot) { + bgColor = bgColor === '#fff' ? '#f5f5f5' : '#fff' + lastSlot = p.timeSlot + } + crossMsg += '' + }) + crossMsg += '
序号项目名称检查室日期时间段
' + p.idx + '' + p.entrust + '' + p.room + '' + p.date + '' + p.timeSlot + '
' + crossTimeDialogHtml.value = crossMsg + crossTimeDialogVisible.value = true + } + + // 更新高亮 + activeZhenShi.value = res.data[0].department_resources_name + highlightedPlanIds.value = res.data.map(v => v.plan_id) selectedPlanId.value = res.data[0].plan_id + } else { + ElMessageBox.confirm('未找到任何一天可满足所有检查项目的号源', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', showCancelButton: false }) + } + } else { + ElMessage.error(res.msg) + } + }) + } + // 推荐同一时段号源 + const sameTimeSlotClick = () => { + if (selectedEntrustId.value.length === 0) { + ElMessage.error('请选择检查项目') + return + } + planLoading.value = true + + // 清空当前勾选行的临时排班信息(从缓存恢复原始数据) + selectedRows.value.forEach(row => { + const cachedRow = cachedListData.value.find(v => v.id === row.id) + if (!cachedRow) return + const target = entrustTableDate.value.find(v => v.id === row.id) + if (!target) return + target.department_resources = cachedRow.department_resources + target.reservation_date = cachedRow.reservation_date + target.period_begin_time = cachedRow.period_begin_time + target.period_end_time = cachedRow.period_end_time + target.temp_plan_id = undefined + }) + + // 收集勾选行中未预约的项目信息 + const items = selectedRows.value + .filter(v => v.list_status === 0) + .map(v => ({ + id: v.id, + entrust_id: v.entrust_id, + department_resources_name: v.department_resources?.department_resources_name || '', + date: v.reservation_date || '', + begin_time: v.period_begin_time || '', + end_time: v.period_end_time || '' + })) + + // 收集未勾选但已临时匹配号源的项目ID + const occupiedPlanIds = entrustTableDate.value + .filter(v => !selectedMianListId.value.includes(v.id) && v.list_status === 0 && v.temp_plan_id) + .map(v => v.temp_plan_id) + + // 调用后端接口(复用同一天号源接口) + GetOptimalPlan({ + type: 'full_day', + regnum: props.reg_num, + entrustid: selectedEntrustId.value, + episodeid: props.episode_id, + appointment_type: props.appointment_type, + items: items, + occupied_plan_ids: occupiedPlanIds, + scope: props.scope, + do_user: props.do_user + }).then(res => { + planLoading.value = false + if (res.status) { + if (res.data.length > 0) { + // 检查同检查室是否跨时段 + const roomMap = {} + res.data.forEach(item => { + const room = item.department_resources_name + if (!roomMap[room]) roomMap[room] = { timeSlots: new Set() } + roomMap[room].timeSlots.add(item.begin_time?.substring(0, 5) + '-' + item.end_time?.substring(0, 5)) + }) + let hasCrossSlot = false + for (const info of Object.values(roomMap)) { + if (info.timeSlots.size > 1) { + hasCrossSlot = true + break + } + } + + if (hasCrossSlot) { + ElMessageBox.confirm('未找到同一时段号源,无法匹配', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', showCancelButton: false }) + return + } + + // 同一时段,更新显示列表 + res.data.forEach(item => { + const target = entrustTableDate.value.find(v => v.id === item.id) + if (!target) return + if (!target.department_resources) target.department_resources = {} + target.department_resources.department_resources_name = item.department_resources_name + target.reservation_date = item.date + target.period_begin_time = item.begin_time + target.period_end_time = item.end_time + target.temp_plan_id = item.plan_id + }) + // 切换诊室 + activeZhenShi.value = res.data[0].department_resources_name + // 多高亮 + highlightedPlanIds.value = res.data.map(v => v.plan_id) + selectedPlanId.value = res.data[0].plan_id + } else { + ElMessageBox.confirm('未找到同一时段号源,无法匹配', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', showCancelButton: false }) } } else { ElMessage.error(res.msg) @@ -921,46 +1265,252 @@ }) } - const startYuYue = (type) => { + const startYuYue = async (type) => { planLoading.value = true - PlanYuYue({ - mainlistid: selectedMianListId.value, - planid: selectedPlanId.value, - appointment_type: props.appointment_type, - dotype: type, //1预约2改约, - do_user:props.do_user, - is_emergency:type==111?1:null - }).then(res => { + // 缓存未勾选但已有临时匹配号源的项目 + const tempPlanCache = {} + entrustTableDate.value.forEach(v => { + if (!selectedMianListId.value.includes(v.id) && v.temp_plan_id) { + tempPlanCache[v.id] = { + temp_plan_id: v.temp_plan_id, + department_resources_name: v.department_resources?.department_resources_name, + reservation_date: v.reservation_date, + period_begin_time: v.period_begin_time, + period_end_time: v.period_end_time + } + } + }) + console.log('tempPlanCache 缓存:', JSON.parse(JSON.stringify(tempPlanCache))) + + // 按号源分组 + const groupMap = {} + let hasMissingPlan = false + selectedMianListId.value.forEach(id => { + const row = entrustTableDate.value.find(v => v.id === id) + const planId = row?.temp_plan_id || row?.roster_id + if (!planId) { + hasMissingPlan = true + return + } + if (!groupMap[planId]) groupMap[planId] = [] + groupMap[planId].push(id) + }) + + if (hasMissingPlan) { planLoading.value = false - if (res.status) { - systemConfigs.value.forEach((v,i)=>{ - if(v.label=='开启申请单pdf' && v.value==="1"){ - CreateJianChaShenQingDanPdfFunc() + ElMessage.error('存在未分配号源的项目,请先选择号源') + return + } + + const groupKeys = Object.keys(groupMap) + + // 单组:保持现有逻辑 + if (groupKeys.length === 1) { + const planId = Number(groupKeys[0]) + const ids = groupMap[planId] + PlanYuYue({ + mainlistid: ids, + planid: planId, + appointment_type: props.appointment_type, + dotype: type, + do_user: props.do_user, + is_emergency: type == 111 ? 1 : null + }).then(res => { + planLoading.value = false + if (res.status) { + systemConfigs.value.forEach((v, i) => { + if (v.label == '开启申请单pdf' && v.value === "1") { + CreateJianChaShenQingDanPdfFunc() + } + }) + GetMainInfo(() => { + // 勾选所有未预约的项目及同检查室项目 + isHandlingSelection.value = false + const originalSelection = [...selectedMianListId.value] + const unbooked = entrustTableDate.value.filter(v => v.list_status === 0 && v.tishi_msg === '') + if (unbooked.length === 0) { + // 全部已预约,恢复之前选中的项目 + originalSelection.forEach(id => { + const row = entrustTableDate.value.find(v => v.id === id) + if (row) entrustTableRef.value.toggleRowSelection(row, true) + }) + } else { + entrustTableRef.value.clearSelection() + const deptNames = new Set( + unbooked.map(v => v.department_resources?.department_resources_name).filter(Boolean) + ) + entrustTableDate.value.forEach(v => { + if (v.tishi_msg !== '') return + if (v.list_status === 0 || deptNames.has(v.department_resources?.department_resources_name)) { + entrustTableRef.value.toggleRowSelection(v, true) + } + }) } - }) - GetMainInfo() - if (auto_print.value.length > 0) { - print_shenqingdan() - } - } else { - ElMessageBox.confirm( - res.msg, - '提示', { - confirmButtonText: '确定', - cancelButtonText: '取消', - type: 'error', - showCancelButton: false + isHandlingSelection.value = true + // 恢复未勾选项目的临时匹配号源 + entrustTableDate.value.forEach(v => { + if (tempPlanCache[v.id]) { + console.log('回填 temp_plan_id:', v.id, tempPlanCache[v.id].temp_plan_id) + v.temp_plan_id = tempPlanCache[v.id].temp_plan_id + if (!v.department_resources) v.department_resources = {} + v.department_resources.department_resources_name = tempPlanCache[v.id].department_resources_name + v.reservation_date = tempPlanCache[v.id].reservation_date + v.period_begin_time = tempPlanCache[v.id].period_begin_time + v.period_end_time = tempPlanCache[v.id].period_end_time } - ) - .then(() => { - }) - .catch(() => { + console.log('回填后 entrustTableDate:', JSON.parse(JSON.stringify(entrustTableDate.value.map(v => ({id: v.id, temp_plan_id: v.temp_plan_id, list_status: v.list_status}))))) + setTimeout(() => { + console.log('延迟检查 temp_plan_id:', JSON.parse(JSON.stringify(entrustTableDate.value.map(v => ({id: v.id, temp_plan_id: v.temp_plan_id, list_status: v.list_status}))))) + }, 2000) + // 刷新当前选中项目的号源 + if (selectedRows.value.length > 0) { + const firstDept = selectedRows.value[0].department_resources?.department_resources_name + if (firstDept) activeZhenShi.value = firstDept + } + GetEnablePlanFunc() + }, true) + if (auto_print.value.length > 0) { + print_shenqingdan() + } + } else { + ElMessageBox.confirm( + res.msg, + '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'error', + showCancelButton: false + } + ) + .then(() => { + + }) + .catch(() => { + + }) + } + }) + return + } + // 多组:串行调用,汇总结果 + const successItems = [] + const failItems = [] + const successEntrustIds = [] + + for (const planIdStr of groupKeys) { + const planId = Number(planIdStr) + const ids = groupMap[planIdStr] + + try { + const res = await PlanYuYue({ + mainlistid: ids, + planid: planId, + appointment_type: props.appointment_type, + dotype: type, + do_user: props.do_user, + is_emergency: type == 111 ? 1 : null + }) + if (res.status) { + ids.forEach(id => { + const row = entrustTableDate.value.find(v => v.id === id) + if (row) { + const timeStr = (row.period_begin_time ? row.period_begin_time.substring(0, 5) : '') + '-' + (row.period_end_time ? row.period_end_time.substring(0, 5) : '') + successItems.push({ name: row.entrust, room: row.department_resources?.department_resources_name || '', date: row.reservation_date || '', time: timeStr }) + successEntrustIds.push(row.entrust_id) + } }) + } else { + ids.forEach(id => { + const row = entrustTableDate.value.find(v => v.id === id) + if (row) failItems.push({ name: row.entrust, msg: res.msg }) + }) + } + } catch (e) { + ids.forEach(id => { + const row = entrustTableDate.value.find(v => v.id === id) + if (row) failItems.push({ name: row.entrust, msg: '请求异常' }) + }) } + } + + planLoading.value = false + + // 弹窗汇总 + // 构建结果表格 + let html = '' + html += '' + let idx = 1 + successItems.forEach(item => { + html += '' + }) + failItems.forEach(f => { + html += '' }) + html += '
序号项目名称检查室日期时间段结果
' + (idx++) + '' + item.name + '' + item.room + '' + item.date + '' + item.time + '预约成功
' + (idx++) + '' + f.name + '' + f.msg + '
' + resultDialogHtml.value = html + resultDialogVisible.value = true + + // 有成功项则刷新数据、生成PDF、自动打印 + if (successItems.length > 0) { + GetMainInfo(() => { + // 勾选所有未预约的项目及同检查室项目 + isHandlingSelection.value = false + const originalSelection = [...selectedMianListId.value] + const unbooked = entrustTableDate.value.filter(v => v.list_status === 0 && v.tishi_msg === '') + if (unbooked.length === 0) { + // 全部已预约,恢复之前选中的项目 + originalSelection.forEach(id => { + const row = entrustTableDate.value.find(v => v.id === id) + if (row) entrustTableRef.value.toggleRowSelection(row, true) + }) + } else { + entrustTableRef.value.clearSelection() + const deptNames = new Set( + unbooked.map(v => v.department_resources?.department_resources_name).filter(Boolean) + ) + entrustTableDate.value.forEach(v => { + if (v.tishi_msg !== '') return + if (v.list_status === 0 || deptNames.has(v.department_resources?.department_resources_name)) { + entrustTableRef.value.toggleRowSelection(v, true) + } + }) + } + isHandlingSelection.value = true + // 恢复未勾选项目的临时匹配号源 + entrustTableDate.value.forEach(v => { + if (tempPlanCache[v.id]) { + console.log('回填 temp_plan_id:', v.id, tempPlanCache[v.id].temp_plan_id) + v.temp_plan_id = tempPlanCache[v.id].temp_plan_id + if (!v.department_resources) v.department_resources = {} + v.department_resources.department_resources_name = tempPlanCache[v.id].department_resources_name + v.reservation_date = tempPlanCache[v.id].reservation_date + v.period_begin_time = tempPlanCache[v.id].period_begin_time + v.period_end_time = tempPlanCache[v.id].period_end_time + } + }) + console.log('回填后 entrustTableDate:', JSON.parse(JSON.stringify(entrustTableDate.value.map(v => ({id: v.id, temp_plan_id: v.temp_plan_id, list_status: v.list_status}))))) + setTimeout(() => { + console.log('延迟检查 temp_plan_id:', JSON.parse(JSON.stringify(entrustTableDate.value.map(v => ({id: v.id, temp_plan_id: v.temp_plan_id, list_status: v.list_status}))))) + }, 2000) + // 刷新当前选中项目的号源 + if (selectedRows.value.length > 0) { + const firstDept = selectedRows.value[0].department_resources?.department_resources_name + if (firstDept) activeZhenShi.value = firstDept + } + GetEnablePlanFunc() + }, true) + systemConfigs.value.forEach((v, i) => { + if (v.label == '开启申请单pdf' && v.value === "1") { + CreateJianChaShenQingDanPdfFunc(successEntrustIds) + } + }) + if (auto_print.value.length > 0) { + print_shenqingdan(successEntrustIds) + } + } } //取消预约(此界面取消预约,不验证密码) const CancelYuYueFunc = () => { @@ -974,6 +1524,30 @@ } ) .then(async () => { + // 缓存未勾选但已有临时匹配号源的项目 + const tempPlanCache = {} + entrustTableDate.value.forEach(v => { + if (!selectedMianListId.value.includes(v.id) && v.temp_plan_id) { + tempPlanCache[v.id] = { + temp_plan_id: v.temp_plan_id, + department_resources_name: v.department_resources?.department_resources_name, + reservation_date: v.reservation_date, + period_begin_time: v.period_begin_time, + period_end_time: v.period_end_time + } + } + }) + + // 缓存要取消的项目(用于后续推荐) + const cancelledItemsCache = selectedRows.value.map(v => ({ + id: v.id, + entrust_id: v.entrust_id, + department_resources_name: v.department_resources?.department_resources_name || '', + date: v.reservation_date || '', + begin_time: v.period_begin_time || '', + end_time: v.period_end_time || '' + })) + loading.value = true const promises = selectedRows.value.map((v) => { return DoctorCancelYuYue({ @@ -996,7 +1570,59 @@ await Promise.all(promises) loading.value = false - GetMainInfo() + GetMainInfo(() => { + // 回填未勾选项目的临时匹配号源 + entrustTableDate.value.forEach(v => { + if (tempPlanCache[v.id]) { + v.temp_plan_id = tempPlanCache[v.id].temp_plan_id + if (!v.department_resources) v.department_resources = {} + v.department_resources.department_resources_name = tempPlanCache[v.id].department_resources_name + v.reservation_date = tempPlanCache[v.id].reservation_date + v.period_begin_time = tempPlanCache[v.id].period_begin_time + v.period_end_time = tempPlanCache[v.id].period_end_time + } + }) + + // 对已取消的项目(list_status 从 1 → 0)自动执行时间推荐 + const cancelledItems = cancelledItemsCache + + if (cancelledItems.length > 0) { + const occupiedPlanIds = entrustTableDate.value + .filter(v => !cancelledItems.find(c => c.id === v.id) && v.list_status === 0 && v.temp_plan_id) + .map(v => v.temp_plan_id) + + GetOptimalPlan({ + type: 'optimal_time', + regnum: props.reg_num, + entrustid: cancelledItems.map(v => v.entrust_id), + episodeid: props.episode_id, + appointment_type: props.appointment_type, + items: cancelledItems, + occupied_plan_ids: occupiedPlanIds, + scope: props.scope, + do_user: props.do_user + }).then(res => { + if (res.status && res.data.length > 0) { + // 更新显示列表 + res.data.forEach(item => { + const target = entrustTableDate.value.find(v => v.id === item.id) + if (!target) return + if (!target.department_resources) target.department_resources = {} + target.department_resources.department_resources_name = item.department_resources_name + target.reservation_date = item.date + target.period_begin_time = item.begin_time + target.period_end_time = item.end_time + target.temp_plan_id = item.plan_id + }) + // 切换诊室为第一项的排班资源 + activeZhenShi.value = res.data[0].department_resources_name + // 多高亮 + highlightedPlanIds.value = res.data.map(v => v.plan_id) + selectedPlanId.value = res.data[0].plan_id + } + }) + } + }, true) }) @@ -1018,9 +1644,10 @@ }) } //生成pdf文件 - const CreateJianChaShenQingDanPdfFunc=()=>{ + const CreateJianChaShenQingDanPdfFunc=(entrustIds)=>{ + const ids = entrustIds || selectedEntrustId.value CreateJianChaShenQingDanPdf({ - OrderNoList: selectedEntrustId.value, + OrderNoList: ids, }).then(res => { if(res.status){ @@ -1029,10 +1656,11 @@ } //打印申请单 let shenqingdan_list = ref([]) - const print_shenqingdan = () => { + const print_shenqingdan = (entrustIds) => { + const ids = entrustIds || selectedEntrustId.value getMainDetail({ regnum: props.reg_num, - entrustid: selectedEntrustId.value.join(','), + entrustid: ids.join(','), episodeid: props.episode_id, appointment_type: props.appointment_type }).then(res => { @@ -1139,8 +1767,15 @@ \ No newline at end of file diff --git a/YiJi-admin/src/views/YeWu/DoctorYuYue.vue b/YiJi-admin/src/views/YeWu/DoctorYuYue.vue index b5dd251..d62791f 100644 --- a/YiJi-admin/src/views/YeWu/DoctorYuYue.vue +++ b/YiJi-admin/src/views/YeWu/DoctorYuYue.vue @@ -45,6 +45,8 @@ \ No newline at end of file diff --git a/YiJi-admin/src/views/YeWu/MainList.vue b/YiJi-admin/src/views/YeWu/MainList.vue index 6ad1577..c880ebe 100644 --- a/YiJi-admin/src/views/YeWu/MainList.vue +++ b/YiJi-admin/src/views/YeWu/MainList.vue @@ -3,7 +3,7 @@ - +
@@ -910,6 +910,18 @@ display: none; } + .el-dialog.is-fullscreen { + display: flex; + flex-direction: column; + } + + .el-dialog.is-fullscreen .el-dialog__body { + flex: 1; + overflow: hidden; + display: flex; + flex-direction: column; + } +