diff --git a/Laravel/app/Services/Admin/YeWu/PlanListService.php b/Laravel/app/Services/Admin/YeWu/PlanListService.php index a7999a9..0245836 100644 --- a/Laravel/app/Services/Admin/YeWu/PlanListService.php +++ b/Laravel/app/Services/Admin/YeWu/PlanListService.php @@ -427,97 +427,189 @@ public function GetOptimalPlan($type, $regnum, $entrustids, $episodeid, $appoint { date_default_timezone_set('PRC'); $dateRange = config('app.globals.可用号源查询范围'); - $result = []; - $usedCapacity = []; // 记录已分配的号源容量,key: roster_detail_id, value: 已分配数 - // 遍历日期范围 $startDate = new DateTime(); $endDate = new DateTime(); $endDate->modify('+' . $dateRange . ' day'); + + if ($type === 'full_day') { + return $this->doFullDayAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId); + } else { + return $this->doOptimalTimeAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId); + } + } + + // 最优时间分配:逐个 item 找最早可用号源,可跨天 + private function doOptimalTimeAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId) + { + $result = []; + $usedCapacity = []; + $assignedPlans = []; // 已分配的时间段 [{resource_name, begin, end}] $currentDate = clone $startDate; + $remainingItems = $items; - while ($currentDate <= $endDate) { + while ($currentDate <= $endDate && !empty($remainingItems)) { $nowdate = $currentDate->format('Y-m-d'); + $availablePlans = $this->getAvailablePlansForDate($regnum, $entrustids, $episodeid, $appointment_type, $nowdate, $scope, $userDeptId); - // 获取当前日期的可用号源 - $s = $this->GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_type, $nowdate, $scope, $userDeptId); - - if (!$s['status']) { + if (empty($availablePlans)) { $currentDate->modify('+1 day'); continue; } - $planList = $s['data']['plan_list'] ?? []; - if (count($planList) == 0) { - $currentDate->modify('+1 day'); - continue; - } + $newRemaining = []; + foreach ($remainingItems as $item) { + $assigned = false; + foreach ($availablePlans as $plan) { + $planId = $plan->id; + $remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0) - ($plan->locked_count ?? 0); + $used = $usedCapacity[$planId] ?? 0; - // 筛选未过期的、可用的号源 - $nowtime = date('Y-m-d H:i:s'); - $availablePlans = []; - foreach ($planList as $plan) { - $planEndTime = $plan->date . ' ' . $plan->end_time; - if ($planEndTime > $nowtime && $plan->plan_enable) { - $availablePlans[] = $plan; + if (($remaining - $used) <= 0) continue; + + // 冲突检查:不同排班且时间段重叠 + if ($this->isTimeConflict($assignedPlans, $plan)) continue; + + // 分配 + $result[] = [ + 'id' => $item['id'], + 'plan_id' => $planId, + 'department_resources_name' => $plan->department_resources_name, + 'date' => $plan->date, + 'begin_time' => $plan->begin_time, + 'end_time' => $plan->end_time + ]; + $usedCapacity[$planId] = ($usedCapacity[$planId] ?? 0) + 1; + $assignedPlans[] = [ + 'resource_name' => $plan->department_resources_name, + 'begin_time' => $plan->begin_time, + 'end_time' => $plan->end_time + ]; + $assigned = true; + break; + } + if (!$assigned) { + $newRemaining[] = $item; } } + $remainingItems = $newRemaining; + $currentDate->modify('+1 day'); + } + + return \Yz::Return(true, '获取成功', $result); + } + + // 单天全检分配:所有 item 必须在同一天完成 + private function doFullDayAssign($startDate, $endDate, $regnum, $entrustids, $episodeid, $appointment_type, $items, $scope, $userDeptId) + { + $currentDate = clone $startDate; + + while ($currentDate <= $endDate) { + $nowdate = $currentDate->format('Y-m-d'); + $availablePlans = $this->getAvailablePlansForDate($regnum, $entrustids, $episodeid, $appointment_type, $nowdate, $scope, $userDeptId); - if (count($availablePlans) == 0) { + if (empty($availablePlans)) { $currentDate->modify('+1 day'); continue; } - // 按日期、时间排序 - usort($availablePlans, function($a, $b) { - $timeA = $a->date . ' ' . ($a->begin_time ?? '00:00:00'); - $timeB = $b->date . ' ' . ($b->begin_time ?? '00:00:00'); - return $timeA <=> $timeB; - }); + $result = []; + $usedCapacity = []; + $assignedPlans = []; + $allAssigned = true; - // 遍历 items,逐个分配 foreach ($items as $item) { - // 如果该 item 已经分配,跳过 - $alreadyAssigned = false; - foreach ($result as $r) { - if ($r['id'] == $item['id']) { - $alreadyAssigned = true; - break; - } - } - if ($alreadyAssigned) continue; - - // 找可用号源 + $assigned = false; foreach ($availablePlans as $plan) { $planId = $plan->id; $remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0) - ($plan->locked_count ?? 0); $used = $usedCapacity[$planId] ?? 0; - if (($remaining - $used) > 0) { - // 分配此号源 - $result[] = [ - 'id' => $item['id'], - 'plan_id' => $planId, - 'department_resources_name' => $plan->department_resources_name, - 'date' => $plan->date, - 'begin_time' => $plan->begin_time, - 'end_time' => $plan->end_time - ]; - $usedCapacity[$planId] = ($usedCapacity[$planId] ?? 0) + 1; - break; - } + if (($remaining - $used) <= 0) continue; + + // 冲突检查:不同排班且时间段重叠 + if ($this->isTimeConflict($assignedPlans, $plan)) continue; + + $result[] = [ + 'id' => $item['id'], + 'plan_id' => $planId, + 'department_resources_name' => $plan->department_resources_name, + 'date' => $plan->date, + 'begin_time' => $plan->begin_time, + 'end_time' => $plan->end_time + ]; + $usedCapacity[$planId] = ($usedCapacity[$planId] ?? 0) + 1; + $assignedPlans[] = [ + 'resource_name' => $plan->department_resources_name, + 'begin_time' => $plan->begin_time, + 'end_time' => $plan->end_time + ]; + $assigned = true; + break; + } + if (!$assigned) { + $allAssigned = false; + break; } } - // 如果所有 items 都已分配,停止遍历 - if (count($result) >= count($items)) { - break; + if ($allAssigned) { + return \Yz::Return(true, '获取成功', $result); } $currentDate->modify('+1 day'); } - return \Yz::Return(true, '获取成功', $result); + return \Yz::Return(true, '获取成功', []); + } + + // 获取指定日期的可用号源(已排序) + private function getAvailablePlansForDate($regnum, $entrustids, $episodeid, $appointment_type, $date, $scope, $userDeptId) + { + $s = $this->GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_type, $date, $scope, $userDeptId); + if (!$s['status']) return []; + + $planList = $s['data']['plan_list'] ?? []; + if (count($planList) == 0) return []; + + $nowtime = date('Y-m-d H:i:s'); + $availablePlans = []; + foreach ($planList as $plan) { + $planEndTime = $plan->date . ' ' . $plan->end_time; + if ($planEndTime > $nowtime && $plan->plan_enable) { + $availablePlans[] = $plan; + } + } + + if (count($availablePlans) == 0) return []; + + usort($availablePlans, function($a, $b) { + $timeA = $a->date . ' ' . ($a->begin_time ?? '00:00:00'); + $timeB = $b->date . ' ' . ($b->begin_time ?? '00:00:00'); + return $timeA <=> $timeB; + }); + + return $availablePlans; + } + + // 冲突检查:同一排班资源允许重叠,不同排班时间不能重叠 + private function isTimeConflict($assignedPlans, $plan) + { + foreach ($assignedPlans as $assigned) { + if ($assigned['resource_name'] === $plan->department_resources_name) { + continue; // 同排班,允许重叠 + } + // 不同排班:检查时间段是否重叠 + $aStart = $assigned['begin_time']; + $aEnd = $assigned['end_time']; + $bStart = $plan->begin_time; + $bEnd = $plan->end_time; + // [aStart, aEnd) ∩ [bStart, bEnd) ≠ ∅ → 冲突 + if ($aStart < $bEnd && $bStart < $aEnd) { + return true; + } + } + return false; } //开始预约占用名额 diff --git a/YiJi-admin/src/components/Yewu/YuYue202506.vue b/YiJi-admin/src/components/Yewu/YuYue202506.vue index a0ebf88..7e0c5e3 100644 --- a/YiJi-admin/src/components/Yewu/YuYue202506.vue +++ b/YiJi-admin/src/components/Yewu/YuYue202506.vue @@ -85,7 +85,7 @@ 预约完成后打印申请单 最优时间 - 单天全检 + 单天全检
@@ -781,6 +781,74 @@ } }) } + // 单天全检按钮点击 + const fullDayClick = () => { + 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 = entrustTableDate.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 || '' + })) + + // 调用后端接口 + GetOptimalPlan({ + type: 'full_day', + regnum: props.reg_num, + entrustid: selectedEntrustId.value, + episodeid: props.episode_id, + appointment_type: props.appointment_type, + items: items, + scope: props.scope, + do_user: props.do_user + }).then(res => { + planLoading.value = false + if (res.status) { + // 更新显示列表 + 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 + }) + // 更新高亮 + if (res.data.length > 0) { + selectedPlanId.value = res.data[0].plan_id + } + } else { + ElMessage.error(res.msg) + } + }) + } const checkDaijianFuc = () => { planLoading.value = true CheckIsDaiJian({