From 3013ecffaa722851fc15f24e2017008e0c2c876f Mon Sep 17 00:00:00 2001 From: sa0ChunLuyu Date: Mon, 27 Jul 2026 20:14:14 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=A0=E7=94=A8=E5=8F=B7=E6=BA=90=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/Admin/YeWu/PlanListService.php | 30 ++++++-- .../src/components/Yewu/YuYue202506.vue | 75 +++++++++++++++++-- YiJi-admin/src/views/YeWu/CheckItemConfig.vue | 8 +- .../src/views/YeWu/DeptCheckItemConfig.vue | 8 +- 4 files changed, 108 insertions(+), 13 deletions(-) diff --git a/Laravel/app/Services/Admin/YeWu/PlanListService.php b/Laravel/app/Services/Admin/YeWu/PlanListService.php index aa1506f..922c6b7 100644 --- a/Laravel/app/Services/Admin/YeWu/PlanListService.php +++ b/Laravel/app/Services/Admin/YeWu/PlanListService.php @@ -434,6 +434,21 @@ public function GetOptimalPlan($type, $regnum, $entrustids, $episodeid, $appoint $endDate = new DateTime(); $endDate->modify('+' . $dateRange . ' day'); + // 批量查询每个检查项目的 use_seats + $entrustIds = array_column($items, 'entrust_id'); + $useSeatsMap = []; + if (!empty($entrustIds)) { + $useSeatsMap = DB::table('s_list') + ->join('s_check_item', 's_list.entrust', '=', 's_check_item.item_name') + ->whereIn('s_list.entrust_id', $entrustIds) + ->pluck('s_check_item.use_seats', 's_list.entrust_id') + ->toArray(); + } + foreach ($items as &$item) { + $item['use_seats'] = $useSeatsMap[$item['entrust_id']] ?? 1; + } + unset($item); + if ($type === 'full_day') { return $this->doFullDayAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId, $occupied_plan_ids); } elseif ($type === 'same_day_slot') { @@ -496,10 +511,11 @@ private function doOptimalTimeAssign($startDate, $endDate, $regnum, $entrustids, $assigned = false; foreach ($availablePlans as $plan) { $planId = $plan->id; + $useSeats = $item['use_seats'] ?? 1; $remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0); $used = $usedCapacity[$planId] ?? 0; - if (($remaining - $used) <= 0) continue; + if (($remaining - $used) < $useSeats) continue; // 冲突检查:同一科室组内,不同排班时间不能重叠 if ($this->isTimeConflict($assignedPlans, $plan)) continue; @@ -514,7 +530,7 @@ private function doOptimalTimeAssign($startDate, $endDate, $regnum, $entrustids, 'begin_time' => $plan->begin_time, 'end_time' => $plan->end_time ]; - $usedCapacity[$planId] = ($usedCapacity[$planId] ?? 0) + 1; + $usedCapacity[$planId] = ($usedCapacity[$planId] ?? 0) + $useSeats; $assignedPlans[] = [ 'resource_name' => $plan->department_resources_name, 'begin_time' => $plan->begin_time, @@ -587,10 +603,11 @@ private function doFullDayAssign($startDate, $endDate, $regnum, $entrustids, $ep $assigned = false; foreach ($availablePlans as $plan) { $planId = $plan->id; + $useSeats = $item['use_seats'] ?? 1; $remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0); $used = $usedCapacity[$planId] ?? 0; - if (($remaining - $used) <= 0) continue; + if (($remaining - $used) < $useSeats) continue; // 冲突检查:同一科室组内,不同排班时间不能重叠 if ($this->isTimeConflict($assignedPlans, $plan)) continue; @@ -603,7 +620,7 @@ private function doFullDayAssign($startDate, $endDate, $regnum, $entrustids, $ep 'begin_time' => $plan->begin_time, 'end_time' => $plan->end_time ]; - $usedCapacity[$planId] = ($usedCapacity[$planId] ?? 0) + 1; + $usedCapacity[$planId] = ($usedCapacity[$planId] ?? 0) + $useSeats; $assignedPlans[] = [ 'resource_name' => $plan->department_resources_name, 'begin_time' => $plan->begin_time, @@ -705,10 +722,11 @@ private function doSameDaySlotAssign($startDate, $endDate, $regnum, $entrustids, $assigned = false; foreach ($availablePlans as $plan) { $planId = $plan->id; + $useSeats = $item['use_seats'] ?? 1; $remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0); $used = $usedCapacity[$planId] ?? 0; - if (($remaining - $used) <= 0) continue; + if (($remaining - $used) < $useSeats) continue; // 冲突检查:同一科室组内,不同排班时间不能重叠 if ($this->isTimeConflict($assignedPlans, $plan)) continue; @@ -721,7 +739,7 @@ private function doSameDaySlotAssign($startDate, $endDate, $regnum, $entrustids, 'begin_time' => $plan->begin_time, 'end_time' => $plan->end_time ]; - $usedCapacity[$planId] = ($usedCapacity[$planId] ?? 0) + 1; + $usedCapacity[$planId] = ($usedCapacity[$planId] ?? 0) + $useSeats; $assignedPlans[] = [ 'resource_name' => $plan->department_resources_name, 'begin_time' => $plan->begin_time, diff --git a/YiJi-admin/src/components/Yewu/YuYue202506.vue b/YiJi-admin/src/components/Yewu/YuYue202506.vue index e6863e8..dba3807 100644 --- a/YiJi-admin/src/components/Yewu/YuYue202506.vue +++ b/YiJi-admin/src/components/Yewu/YuYue202506.vue @@ -206,6 +206,17 @@ 确定 + +
+ + + +
+
+ +
@@ -292,10 +303,13 @@ let crossTimeDialogVisible = ref(false) let crossTimeDialogHtml = ref('') let cachedCrossDialogData = ref(null) // 缓存跨时段 dialog 数据:{ html, idx } + let cachedRecommendResultData = ref(null) // 缓存推荐结果 dialog 数据:{ html, idx } let conflictDialogVisible = ref(false) let conflictDialogHtml = ref('') let resultDialogVisible = ref(false) let resultDialogHtml = ref('') + let recommendResultVisible = ref(false) + let recommendResultHtml = ref('') const getWeekday = (date1) => { let days = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; const date = new Date(date1); // 注意:格式可能因浏览器而异 @@ -406,6 +420,17 @@ cachedCrossDialogData.value = null } } + // 检查缓存中是否有推荐结果 dialog 需要弹出 + if (cachedRecommendResultData.value) { + const currentIdx = entrustTableDate.value.findIndex( + r => selectedMianListId.value.includes(r.id) + ) + 1 + if (currentIdx === cachedRecommendResultData.value.idx) { + recommendResultHtml.value = cachedRecommendResultData.value.html + recommendResultVisible.value = true + cachedRecommendResultData.value = null + } + } checkSameSourceRoom() } const checkSameSourceRoom = () => { @@ -775,6 +800,36 @@ } } + // 首次自动推荐:未匹配检测 + const matchedIds = new Set(res.data.map(v => v.id)) + const unmatched = items.filter(v => !matchedIds.has(v.id)) + if (unmatched.length > 0) { + let html = '' + html += '' + let idx = 1 + res.data.forEach(v => { + const entrustName = entrustTableDate.value.find(r => r.id === v.id)?.entrust || '' + html += '' + }) + unmatched.forEach(v => { + const entrustName = entrustTableDate.value.find(r => r.id === v.id)?.entrust || '' + html += '' + }) + html += '
序号项目名称排班资源日期时间段结果
' + (idx++) + '' + entrustName + '' + v.department_resources_name + '' + v.date + '' + v.begin_time?.substring(0, 5) + '-' + v.end_time?.substring(0, 5) + '✅ 成功匹配
' + (idx++) + '' + entrustName + '❌ 未找到合适号源
' + // 计算未匹配项目的最小序号 + const unmatchedIndices = unmatched.map(v => entrustTableDate.value.findIndex(r => r.id === v.id) + 1).filter(i => i > 0) + const minUnmatchedIdx = Math.min(...unmatchedIndices) + const selectedIdx = entrustTableDate.value.findIndex( + r => selectedMianListId.value.includes(r.id) + ) + 1 + if (selectedIdx === minUnmatchedIdx) { + recommendResultHtml.value = html + recommendResultVisible.value = true + } else { + cachedRecommendResultData.value = { html: html, idx: minUnmatchedIdx } + } + } + // 更新高亮 const newHighlightIds = findHighlightPlanIds() if (newHighlightIds.length > 0) { @@ -1008,11 +1063,21 @@ 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 }) + // 构建推荐结果表格 + let html = '' + html += '' + let idx = 1 + res.data.forEach(v => { + const entrustName = entrustTableDate.value.find(r => r.id === v.id)?.entrust || '' + html += '' + }) + unmatched.forEach(v => { + const entrustName = entrustTableDate.value.find(r => r.id === v.id)?.entrust || '' + html += '' + }) + html += '
序号项目名称排班资源日期时间段结果
' + (idx++) + '' + entrustName + '' + v.department_resources_name + '' + v.date + '' + v.begin_time?.substring(0, 5) + '-' + v.end_time?.substring(0, 5) + '✅ 成功匹配
' + (idx++) + '' + entrustName + '❌ 未找到合适号源
' + recommendResultHtml.value = html + recommendResultVisible.value = true } else if (res.data.length === 0) { ElMessageBox.confirm('所有项目均未匹配到合适号源,请重新选择号源', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'error', showCancelButton: false }) } diff --git a/YiJi-admin/src/views/YeWu/CheckItemConfig.vue b/YiJi-admin/src/views/YeWu/CheckItemConfig.vue index 05611dc..8ff4419 100644 --- a/YiJi-admin/src/views/YeWu/CheckItemConfig.vue +++ b/YiJi-admin/src/views/YeWu/CheckItemConfig.vue @@ -127,6 +127,10 @@
检查时间:
+
占用号源: +
预约时占用的号源数量,默认1
+
等待时间:
医嘱开具后,预约时间需在设定的等待期之后,单位分钟
@@ -350,7 +354,8 @@ check_time: '', check_begin_time: '', check_notice: '', - warm_tips: '' + warm_tips: '', + use_seats: 1 }) //获取检查项目类别list let BigClassList = ref([]); @@ -646,6 +651,7 @@ selectedItemInfo.value.limosis = row.limosis selectedItemInfo.value.reservation_method = row.reservation_method selectedItemInfo.value.check_time = row.check_time + selectedItemInfo.value.use_seats = row.use_seats || 1 if (row.check_begin_time == '' || row.check_begin_time == null) { row.check_begin_time = 0 } diff --git a/YiJi-admin/src/views/YeWu/DeptCheckItemConfig.vue b/YiJi-admin/src/views/YeWu/DeptCheckItemConfig.vue index 8ccf53a..dfadf08 100644 --- a/YiJi-admin/src/views/YeWu/DeptCheckItemConfig.vue +++ b/YiJi-admin/src/views/YeWu/DeptCheckItemConfig.vue @@ -113,6 +113,10 @@
检查时间:
+
占用号源: +
预约时占用的号源数量,默认1
+
等待时间:
医嘱开具后,预约时间需在设定的等待期之后,单位分钟
@@ -323,7 +327,8 @@ check_time: '', check_begin_time: '', check_notice: '', - warm_tips: '' + warm_tips: '', + use_seats: 1 }) //获取检查项目类别list let BigClassList = ref([]); @@ -595,6 +600,7 @@ selectedItemInfo.value.limosis = row.limosis selectedItemInfo.value.reservation_method = row.reservation_method selectedItemInfo.value.check_time = row.check_time + selectedItemInfo.value.use_seats = row.use_seats || 1 if (row.check_begin_time == '' || row.check_begin_time == null) { row.check_begin_time = 0 }