预约优化

main
鹿和sa0ChunLuyu 2 weeks ago
parent ca22002ca7
commit 73344cea53

@ -200,9 +200,17 @@ public function GetEnablePlan()
$episodeid = request('episodeid'); $episodeid = request('episodeid');
$appointment_type = request('appointment_type'); //预约类型 $appointment_type = request('appointment_type'); //预约类型
$appointment_date = request('date'); //预约日期 $appointment_date = request('date'); //预约日期
$scope = request('scope', 'all');
$do_user = request('do_user', 0);
$userDeptId = 0;
if ($do_user) {
$user = DB::table('users')->find($do_user);
$userDeptId = $user->department_id ?? 0;
}
$service = new PlanListService(); $service = new PlanListService();
return $service->GetEnablePlan($regnum, $entrustid, $episodeid, $appointment_type, $appointment_date); return $service->GetEnablePlan($regnum, $entrustid, $episodeid, $appointment_type, $appointment_date, $scope, $userDeptId);
} }
//最优时间/单天全检 分配 //最优时间/单天全检 分配

@ -223,6 +223,95 @@ public function GetList(Request $request)
return \Yz::Return(true,'查询完成',['list'=>$list,'count'=>$count]); return \Yz::Return(true,'查询完成',['list'=>$list,'count'=>$count]);
} }
//管理员主工作列表(支持科室切换)
public function GetListByDept(Request $request)
{
$searchInfo = request('searchInfo');
$page = request('page');
$pageSize = request('pageSize');
$department_id = request('department_id'); // 0 或空表示全部科室
$list = DB::table('s_list')
->leftJoin('s_period', 's_list.reservation_time', '=', 's_period.id')
->leftJoin('s_department_resources', 's_list.reservation_sources', '=', 's_department_resources.id')
->select('s_list.*', 's_period.period_begin_time', 's_period.period_end_time', 's_department_resources.department_resources_name')
->where(['s_list.is_del' => 0, 's_list.is_nullify' => 0]);
// 选中具体科室时,按科室编号过滤
if (!empty($department_id)) {
$deptNumbers = DB::table('s_department')
->where('is_del', 0)
->where(function ($q) use ($department_id) {
$q->where('id', $department_id)
->orWhere('pid', $department_id);
})
->pluck('department_number');
$list = $list->where(function ($q) use ($deptNumbers) {
$q->whereIn('RISRAcceptDeptCode', $deptNumbers)
->orWhereIn('reservation_department_code', $deptNumbers);
});
}
// 搜索条件(与 GetList 保持一致)
$dateType = $searchInfo['dateType'] ?? 'reservation_date';
if ($searchInfo['dateRange'] != null and count($searchInfo['dateRange']) == 2) {
if ($dateType === 'both') {
$list = $list->where(function ($q) use ($searchInfo) {
$q->whereBetween('s_list.entrust_date', $searchInfo['dateRange'])
->orWhereBetween('s_list.reservation_date', $searchInfo['dateRange']);
});
} elseif ($dateType === 'entrust_date') {
$list = $list->whereBetween('s_list.entrust_date', $searchInfo['dateRange']);
} else {
$list = $list->whereBetween('s_list.reservation_date', $searchInfo['dateRange']);
}
}
if (isset($searchInfo['list_status'])) {
$list = $list->where('s_list.list_status', $searchInfo['list_status']);
}
if (isset($searchInfo['patient_type'])) {
$list = $list->where('s_list.patient_type', $searchInfo['patient_type']);
}
if (!empty($searchInfo['resources'])) {
$list = $list->whereIn('s_list.reservation_sources', $searchInfo['resources']);
}
if (isset($searchInfo['services_group'])) {
$list = $list->whereRaw("FIND_IN_SET(?, s_list.services_group)", [$searchInfo['services_group']]);
}
if (isset($searchInfo['reg_num'])) {
$list = $list->where('s_list.reg_num', $searchInfo['reg_num']);
}
if (isset($searchInfo['user_name'])) {
$list = $list->where('s_list.user_name', 'like', '%' . $searchInfo['user_name'] . '%');
}
if (isset($searchInfo['doctor'])) {
$list = $list->where('s_list.docotr', 'like', '%' . $searchInfo['doctor'] . '%');
}
if (isset($searchInfo['apply_department'])) {
$list = $list->where('s_list.reservation_department', 'like', '%' . $searchInfo['apply_department'] . '%');
}
$count = $list;
$count = $count->count();
$list = $list->orderBy('id', 'desc')->limit($pageSize)->skip(($page - 1) * $pageSize)
->take($pageSize)->get();
// 匹配设备(服务组)
$devices = DB::table('s_devices')->get();
foreach ($list as $key => $value) {
$list[$key]->age = \Tools::calculateAgeText($value->user_brithday);
$list[$key]->devices = [];
$array_device_id = explode(",", $value->services_group);
foreach ($devices as $k => $v) {
if (in_array($v->id, $array_device_id)) {
$list[$key]->devices[] = $v;
}
}
}
return \Yz::Return(true, '查询完成', ['list' => $list, 'count' => $count]);
}
//获取医嘱变更日志 //获取医嘱变更日志
public function GetLoglist() public function GetLoglist()
{ {

@ -65,12 +65,14 @@ public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_typ
} }
$commonDevice = []; //多个检查项目共同的设备id $commonDevice = []; //多个检查项目共同的设备id
$first = true;
foreach ($allDevice as $set) { foreach ($allDevice as $set) {
if (count($commonDevice) == 0) { if ($first) {
// 如果$intersection为空直接将第一个子数组的元素放入 // 首次迭代:直接取第一个项目的设备
$commonDevice = $set; $commonDevice = $set;
$first = false;
} else { } else {
// 使用array_intersect()函数求当前子数组与已有交集的交集 // 后续迭代:取交集,交集为空则保持空,不再重置
$commonDevice = array_intersect($commonDevice, $set); $commonDevice = array_intersect($commonDevice, $set);
} }
} }
@ -319,7 +321,7 @@ public function GetEnablePlan($regnum, $entrustids, $episodeid, $appointment_typ
$grouped[$name]['plans'][] = $plan; $grouped[$name]['plans'][] = $plan;
// 累计全天总剩余 // 累计全天总剩余
$remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0) - ($plan->locked_count ?? 0); $remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0);
$grouped[$name]['remaining'] += max($remaining, 0); $grouped[$name]['remaining'] += max($remaining, 0);
// 判断是否未过期,收集最早开始时间及该时段剩余 // 判断是否未过期,收集最早开始时间及该时段剩余
@ -439,60 +441,89 @@ public function GetOptimalPlan($type, $regnum, $entrustids, $episodeid, $appoint
} }
} }
// 最优时间分配:逐个 item 找最早可用号源,可跨天 // 最优时间分配:按科室分组,逐个 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)
{ {
// 按科室分组
$deptGroups = $this->groupItemsByDept($items);
if (empty($deptGroups)) {
return \Yz::Return(true, '获取成功', []);
}
$result = []; $result = [];
$usedCapacity = [];
$assignedPlans = []; // 已分配的时间段 [{resource_name, begin, end}]
$currentDate = clone $startDate; $currentDate = clone $startDate;
$remainingItems = $items; // 每组待分配的 items
$remainingGroups = [];
foreach ($deptGroups as $deptCode => $group) {
$remainingGroups[$deptCode] = [
'entrustids' => $group['entrustids'],
'items' => $group['items']
];
}
while ($currentDate <= $endDate && !empty($remainingItems)) { while ($currentDate <= $endDate && !empty($remainingGroups)) {
$nowdate = $currentDate->format('Y-m-d'); $nowdate = $currentDate->format('Y-m-d');
$availablePlans = $this->getAvailablePlansForDate($regnum, $entrustids, $episodeid, $appointment_type, $nowdate, $scope, $userDeptId); $newRemainingGroups = [];
if (empty($availablePlans)) { foreach ($remainingGroups as $deptCode => $group) {
$currentDate->modify('+1 day'); $groupEntrustids = $group['entrustids'];
continue; $groupItems = $group['items'];
}
$newRemaining = []; $availablePlans = $this->getAvailablePlansForDate($regnum, $groupEntrustids, $episodeid, $appointment_type, $nowdate, $scope, $userDeptId);
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;
if (($remaining - $used) <= 0) continue; if (empty($availablePlans)) {
// 当天该科室无号源,整组留到下一轮
$newRemainingGroups[$deptCode] = $group;
continue;
}
// 冲突检查:不同排班且时间段重叠 $usedCapacity = [];
if ($this->isTimeConflict($assignedPlans, $plan)) continue; $assignedPlans = [];
$remainingItems = [];
foreach ($groupItems as $item) {
$assigned = false;
foreach ($availablePlans as $plan) {
$planId = $plan->id;
$remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0);
$used = $usedCapacity[$planId] ?? 0;
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) {
$remainingItems[] = $item;
}
}
// 分配 if (!empty($remainingItems)) {
$result[] = [ $newRemainingGroups[$deptCode] = [
'id' => $item['id'], 'entrustids' => $groupEntrustids,
'plan_id' => $planId, 'items' => $remainingItems
'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;
$remainingGroups = $newRemainingGroups;
$currentDate->modify('+1 day'); $currentDate->modify('+1 day');
} }
@ -502,55 +533,64 @@ private function doOptimalTimeAssign($startDate, $endDate, $regnum, $entrustids,
// 单天全检分配:所有 item 必须在同一天完成 // 单天全检分配:所有 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)
{ {
// 按科室分组
$deptGroups = $this->groupItemsByDept($items);
if (empty($deptGroups)) {
return \Yz::Return(true, '获取成功', []);
}
$currentDate = clone $startDate; $currentDate = clone $startDate;
while ($currentDate <= $endDate) { while ($currentDate <= $endDate) {
$nowdate = $currentDate->format('Y-m-d'); $nowdate = $currentDate->format('Y-m-d');
$availablePlans = $this->getAvailablePlansForDate($regnum, $entrustids, $episodeid, $appointment_type, $nowdate, $scope, $userDeptId);
if (empty($availablePlans)) {
$currentDate->modify('+1 day');
continue;
}
$result = [];
$usedCapacity = [];
$assignedPlans = [];
$allAssigned = true; $allAssigned = true;
$result = [];
foreach ($items as $item) { foreach ($deptGroups as $deptCode => $group) {
$assigned = false; $availablePlans = $this->getAvailablePlansForDate($regnum, $group['entrustids'], $episodeid, $appointment_type, $nowdate, $scope, $userDeptId);
foreach ($availablePlans as $plan) {
$planId = $plan->id; if (empty($availablePlans)) {
$remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0) - ($plan->locked_count ?? 0);
$used = $usedCapacity[$planId] ?? 0;
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; $allAssigned = false;
break; break;
} }
$usedCapacity = [];
$assignedPlans = [];
foreach ($group['items'] as $item) {
$assigned = false;
foreach ($availablePlans as $plan) {
$planId = $plan->id;
$remaining = ($plan->count ?? 0) - ($plan->used_count ?? 0);
$used = $usedCapacity[$planId] ?? 0;
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 2;
}
}
} }
if ($allAssigned) { if ($allAssigned) {
@ -612,6 +652,33 @@ private function isTimeConflict($assignedPlans, $plan)
return false; return false;
} }
// 按科室分组 items相同 RISRAcceptDeptCode 的归为一组
private function groupItemsByDept($items)
{
$groups = [];
foreach ($items as $item) {
$entrustId = $item['entrust_id'] ?? null;
if (!$entrustId) continue;
$list = DB::table('s_list')
->where(['entrust_id' => $entrustId, 'is_nullify' => 0])
->first();
if (!$list) continue;
$deptCode = $list->RISRAcceptDeptCode ?: 'unknown';
if (!isset($groups[$deptCode])) {
$groups[$deptCode] = [
'entrustids' => [],
'items' => []
];
}
$groups[$deptCode]['entrustids'][] = $entrustId;
$groups[$deptCode]['items'][] = $item;
}
return $groups;
}
//开始预约占用名额 //开始预约占用名额
public function YuYue($planid, $appointment_type, $mainlistids, $do_type,$is_emergency=0,$do_user=null) public function YuYue($planid, $appointment_type, $mainlistids, $do_type,$is_emergency=0,$do_user=null)
{ {

@ -123,6 +123,7 @@
Route::post('admin/PlanListDel','App\Http\Controllers\API\Admin\YeWu\PlanListController@Del');//删除计划详情 Route::post('admin/PlanListDel','App\Http\Controllers\API\Admin\YeWu\PlanListController@Del');//删除计划详情
Route::post('admin/SaveLockedCount','App\Http\Controllers\API\Admin\YeWu\PlanListController@SaveLockedCount');//保存占位数量 Route::post('admin/SaveLockedCount','App\Http\Controllers\API\Admin\YeWu\PlanListController@SaveLockedCount');//保存占位数量
Route::post('admin/GetMainList','App\Http\Controllers\API\Admin\YeWu\WorkMainController@GetList');//获取主表列表 Route::post('admin/GetMainList','App\Http\Controllers\API\Admin\YeWu\WorkMainController@GetList');//获取主表列表
Route::post('admin/GetMainListByDept','App\Http\Controllers\API\Admin\YeWu\WorkMainController@GetListByDept');//管理员获取主表列表(支持科室切换)
Route::post('admin/GetLoglist','App\Http\Controllers\API\Admin\YeWu\WorkMainController@GetLoglist');//获取日志 Route::post('admin/GetLoglist','App\Http\Controllers\API\Admin\YeWu\WorkMainController@GetLoglist');//获取日志
Route::post('admin/CancelYuYue','App\Http\Controllers\API\Admin\YeWu\PlanListController@CancelYuYue');//取消预约 Route::post('admin/CancelYuYue','App\Http\Controllers\API\Admin\YeWu\PlanListController@CancelYuYue');//取消预约
Route::post('admin/SetHuChi','App\Http\Controllers\API\Admin\YeWu\CheckItemController@SetHuChi');//设置互斥 Route::post('admin/SetHuChi','App\Http\Controllers\API\Admin\YeWu\CheckItemController@SetHuChi');//设置互斥

@ -371,6 +371,10 @@ export const PlanDetailPlanListDelAdmin = (data = {}) => {
export const GetMainList = (data = {}) => { export const GetMainList = (data = {}) => {
return axios({ url: import.meta.env.VITE_APP_API + 'v1/admin/GetMainList', data: data }) return axios({ url: import.meta.env.VITE_APP_API + 'v1/admin/GetMainList', data: data })
} }
//admin获取主表列表支持科室切换
export const GetMainListByDept = (data = {}) => {
return axios({ url: import.meta.env.VITE_APP_API + 'v1/admin/GetMainListByDept', data: data })
}
//模糊搜索科室 //模糊搜索科室
export const SearchDepartment = (data = {}) => { export const SearchDepartment = (data = {}) => {
return axios({ url: import.meta.env.VITE_APP_API + 'v1/admin/SearchDepartment', data: data }) return axios({ url: import.meta.env.VITE_APP_API + 'v1/admin/SearchDepartment', data: data })

@ -23,9 +23,9 @@
<span v-else></span> <span v-else></span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="list_status" label="医嘱状态" width="80"> <el-table-column prop="list_status" label="预约状态" width="80">
<template #default="scope"> <template #default="scope">
<el-tag v-if="scope.row.list_status===0" class="ml-2" type="info"></el-tag> <el-tag v-if="scope.row.list_status===0" class="ml-2" type="info"></el-tag>
<el-tag v-if="scope.row.list_status===1" class="ml-2" type="success"></el-tag> <el-tag v-if="scope.row.list_status===1" class="ml-2" type="success"></el-tag>
<el-tag v-if="scope.row.list_status===2" class="ml-2"></el-tag> <el-tag v-if="scope.row.list_status===2" class="ml-2"></el-tag>
<el-tag v-if="scope.row.list_status===3" class="ml-2" type="warning"></el-tag> <el-tag v-if="scope.row.list_status===3" class="ml-2" type="warning"></el-tag>
@ -84,8 +84,15 @@
<el-checkbox-group v-model="auto_print" style="margin-left: 12px;"> <el-checkbox-group v-model="auto_print" style="margin-left: 12px;">
<el-checkbox label="1">预约完成后打印申请单</el-checkbox> <el-checkbox label="1">预约完成后打印申请单</el-checkbox>
</el-checkbox-group> </el-checkbox-group>
<el-button class="do_button" type="primary" style="margin-left: 20px;" @click="optimalTimeClick"></el-button> <el-tooltip placement="top" :content="autoMatchTip" popper-class="tip-pre-line">
<el-button class="do_button" type="primary" @click="fullDayClick"></el-button> <el-checkbox v-model="autoMatchGroup" style="margin-left: 12px;"></el-checkbox>
</el-tooltip>
<el-tooltip placement="top" content="点击此按钮,系统会推荐最近能用的时间段号源,多个检查项目可能会跨天跨时间段" popper-class="tip-pre-line">
<el-button class="do_button_recommend" type="primary" style="margin-left: 20px;" @click="optimalTimeClick"></el-button>
</el-tooltip>
<el-tooltip placement="top" content="点击此按钮,系统会推荐最近的能在同一天完成检查的号源" popper-class="tip-pre-line">
<el-button class="do_button_recommend" type="primary" @click="fullDayClick"></el-button>
</el-tooltip>
</div> </div>
<div> <div>
<el-date-picker v-model="startDate" type="date" placeholder="跳转日期" @change="DatePickerChange()" /> <el-date-picker v-model="startDate" type="date" placeholder="跳转日期" @change="DatePickerChange()" />
@ -218,6 +225,10 @@
let cachedPlans = ref([]) // GetEnablePlanFunc let cachedPlans = ref([]) // GetEnablePlanFunc
let zhanWeiCount = ref(0) // let zhanWeiCount = ref(0) //
let hasAutoAssignedFirstPlan = ref(false) // let hasAutoAssignedFirstPlan = ref(false) //
let autoMatchGroup = ref(true) //
const autoMatchTip = `1.未预约状态,如果多个检查项目能在同一个检查室进行检查,系统会自动选中能一起预约的项目,只需要点击一次预约按钮即可完成预约
2.已预约状态预约到同一个时段的同一个检查室的项目系统会自动选中更改预约或取消预约只需点击一次
3.不同状态的预约的预约项目无法一起选中`
let TanChuangMsgDialogVisible=ref(false) let TanChuangMsgDialogVisible=ref(false)
const getWeekday = (date1) => { const getWeekday = (date1) => {
let days = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; let days = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
@ -282,7 +293,11 @@
console.log(handleType.value) console.log(handleType.value)
if (selectedEntrustId.value.length > 0 && isHandlingSelection.value) { if (selectedEntrustId.value.length > 0 && isHandlingSelection.value) {
if (handleType.value === 'add' && selectedRows.value[0].list_status==0) { if (handleType.value === 'add' && selectedRows.value[0].list_status==0) {
FindAllMatchItem(); if (autoMatchGroup.value) {
FindAllMatchItem();
} else {
GetEnablePlanFunc();
}
} else { } else {
GetEnablePlanFunc(); GetEnablePlanFunc();
} }
@ -1191,6 +1206,10 @@
margin-right: 40px; margin-right: 40px;
} }
.do_button_recommend {
width: 150px;
}
.do_button { .do_button {
width: 100px; width: 100px;
} }
@ -1226,4 +1245,10 @@
background-color: #9cd0de; background-color: #9cd0de;
} }
</style>
<style>
.tip-pre-line {
white-space: pre-line;
}
</style> </style>

@ -124,6 +124,13 @@ const router = createRouter({
meta: { meta: {
title: '主工作列表' title: '主工作列表'
} }
}, {
path: '/yewu/mainListWithDept',
name: 'YewuMainListWithDept',
component: () => import('../views/YeWu/MainListWithDept.vue'),
meta: {
title: '主工作列表(科室)'
}
}, { }, {
path: '/yewu/CheckItemConfig', path: '/yewu/CheckItemConfig',
name: 'YewuCheckItemConfig', name: 'YewuCheckItemConfig',

@ -102,7 +102,9 @@
</div> </div>
<el-table :data="tableData" id="tablelist" style="width: 100%;" row-key="id" v-loading="loading" ref="tableref" <el-table :data="tableData" id="tablelist" style="width: 100%;" row-key="id" v-loading="loading" ref="tableref"
@select="handleSelect"> @select="handleSelect">
<el-table-column type="selection" width="50" /> <el-table-column type="selection" width="50">
<template #header><span></span></template>
</el-table-column>
<el-table-column prop="list_status" label="状态" width="80"> <el-table-column prop="list_status" label="状态" width="80">
<template #default="scope"> <template #default="scope">
<el-tag v-if="scope.row.list_status===0" class="ml-2" type="info"></el-tag> <el-tag v-if="scope.row.list_status===0" class="ml-2" type="info"></el-tag>
@ -904,6 +906,10 @@
overflow: auto; overflow: auto;
} }
.el-table__header-wrapper .el-checkbox[aria-label="选择所有行"] {
display: none;
}
</style> </style>
<style scoped> <style scoped>

@ -0,0 +1,848 @@
<template>
<div v-loading="loading">
<el-dialog title="预约列表" class="iframeDialog" v-model="iframe_show" @opened="dialogopened" @closed="closeYuYueDialog()">
<iframe id="bdIframe" :src="selectedUrl" class="iframe" :key="YuYueKey" scrolling="auto"></iframe>
</el-dialog>
<el-dialog v-model="YuYueVueDialogVisible" width="100%" @close="closeYuYueDialog()">
<YuYue202506 :reg_num="tableSelected[0].reg_num" :entrust_ids="entrust_ids" :key="YuYueKey" :episode_id="tableSelected[0].episodeid" appointment_type="4" :dotype="do_type" :do_user="loginUserinfo.id" :special_privileges="loginUserinfo.special_privileges" :scope="'department'"></YuYue202506>
</el-dialog>
<div class="head">
<el-row>
<el-form-item>
<el-select :filterable="true" clearable v-model="selectedDeptId" placeholder="选择科室"
@change="deptChange" style="margin-left: 8px;width: 180px;">
<el-option label="全部科室" :value="0" />
<el-option v-for="(item,index) in deptList" :key="index"
:label="item.department_name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="searchInfo.dateType" placeholder="时间筛选类型"
style="margin-left: 8px;width: 150px;">
<el-option label="预约日期" value="reservation_date" />
<el-option label="医嘱日期" value="entrust_date" />
<el-option label="同时生效" value="both" />
</el-select>
</el-form-item>
<el-form-item>
<el-date-picker style="margin-left: 8px; width: 150px" v-model="searchInfo.dateRange[0]"
type="date" placeholder="开始时间" value-format="YYYY-MM-DD" />
<span style="margin: 0 4px;"></span>
<el-date-picker style="width: 150px" v-model="searchInfo.dateRange[1]"
type="date" placeholder="结束时间" value-format="YYYY-MM-DD" />
</el-form-item>
<el-form-item>
<el-select :filterable="true" clearable v-model="searchInfo.list_status" placeholder="状态"
style="margin-left: 8px;width: 150px;">
<el-option label="申请中" value="0" />
<el-option label="已预约" value="1" />
<el-option label="已报道" value="2" />
<el-option label="已结束" value="3" />
</el-select>
</el-form-item>
<el-form-item>
<el-select :filterable="true" clearable v-model="searchInfo.patient_type" placeholder="患者类型"
style="margin-left: 8px;width: 150px;">
<el-option label="住院" value="0" />
<el-option label="门诊" value="1" />
<el-option label="急诊" value="2" />
<el-option label="体检" value="3" />
</el-select>
</el-form-item>
<el-form-item>
<el-select multiple :filterable="true" clearable v-model="searchInfo.resources" placeholder="资源"
style="margin-left: 8px;width: 250px;">
<el-option v-for="(item,index) in enableResourceList" :key="index"
:label="item.department_resources_name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item>
<el-input v-model="searchInfo.reg_num" placeholder="登记号" style="width: 200px;margin-left: 8px;" />
</el-form-item>
<el-form-item>
<el-input v-model="searchInfo.user_name" placeholder="患者姓名"
style="width: 200px;margin-left: 8px;" />
</el-form-item>
<el-form-item>
<el-select
v-model="searchInfo.apply_department"
filterable
remote
clearable
reserve-keyword
placeholder="申请科室"
style="width: 200px;margin-left: 8px;"
:remote-method="debouncedSearchDept"
>
<el-option
v-for="item in deptOptions"
:key="item.id"
:label="item.department_name + ' (' + item.department_number + ')'"
:value="item.department_name"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-input v-model="searchInfo.doctor" placeholder="医生"
style="width: 200px;margin-left: 8px;" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="GetList()" style="margin-left: 10px;">查询</el-button>
</el-form-item>
</el-row>
<el-row style="margin: 0px 0px 8px 8px; border-top: 1px solid #ccc;padding-top: 8px">
<el-button type="success" @click.prevent="openIframe(1)"> </el-button>
<el-button type="danger" @click="cancel()"></el-button>
<el-button type="warning" @click="openIframe(2)"></el-button>
<el-button type="primary" @click="SignInFunc()" style="margin-left: 40px;">报到</el-button>
<el-button style="display: none;" ref="print_shenqingdan_button" v-print="'#shenqingdan'"></el-button>
<el-button @click="print_shenqingdan()" >打印申请单</el-button>
</el-row>
</div>
<el-table :data="tableData" id="tablelist" style="width: 100%;" row-key="id" v-loading="loading" ref="tableref"
@select="handleSelect">
<el-table-column type="selection" width="50" />
<el-table-column prop="list_status" label="状态" width="80">
<template #default="scope">
<el-tag v-if="scope.row.list_status===0" class="ml-2" type="info"></el-tag>
<el-tag v-if="scope.row.list_status===1" class="ml-2" type="success"></el-tag>
<el-tag v-if="scope.row.list_status===2" class="ml-2"></el-tag>
<el-tag v-if="scope.row.list_status===3" class="ml-2" type="warning"></el-tag>
</template>
</el-table-column>
<el-table-column label="门诊号" width="120">
<template #default="scope">
<span v-if="scope.row.patient_type != 0">{{ scope.row.reg_num }}</span>
</template>
</el-table-column>
<el-table-column label="住院号" width="120">
<template #default="scope">
<span v-if="scope.row.patient_type == 0">{{ scope.row.episodeid?.slice(-10) }}</span>
</template>
</el-table-column>
<el-table-column prop="user_name" label="姓名" />
<el-table-column prop="user_sex" label="性别" width="60">
<template #default="scope">
<span v-if="scope.row.user_sex==1"></span>
<span v-if="scope.row.user_sex==2"></span>
</template>
</el-table-column>
<el-table-column prop="age" label="年龄" width="115" />
<el-table-column prop="entrust" label="医嘱" width="200" />
<el-table-column prop="is_pay" label="是否交费" width="80">
<template #default="scope">
<span v-if="scope.row.is_pay==1"></span>
<span v-if="scope.row.is_pay==0"></span>
</template>
</el-table-column>
<el-table-column prop="reservation_date" label="预约日期" width="120" />
<el-table-column prop="check_begin_time" label="预约时间" width="120">
<template #default="scope">
<span
v-if="scope.row.period_begin_time && scope.row.period_end_time ">{{scope.row.period_begin_time.substring(0, 5)}}~{{scope.row.period_end_time.substring(0, 5)}}</span>
</template>
</el-table-column>
<el-table-column prop="department_resources_name" label="排班" width="120" />
<el-table-column prop="reservation_department" label="申请科室" width="120" />
<el-table-column prop="" label="医嘱时间" width="160">
<template #default="scope">
{{scope.row.entrust_date}} {{scope.row.entrust_time}}
</template>
</el-table-column>
<el-table-column prop="docotr" label="申请医生" width="80" />
<el-table-column prop="patient_type" label="病人类型" width="80">
<template #default="scope">
<span v-if="scope.row.patient_type==0"></span>
<span v-if="scope.row.patient_type==1"></span>
<span v-if="scope.row.patient_type==2"></span>
<span v-if="scope.row.patient_type==3"></span>
</template>
</el-table-column>
<el-table-column prop="user_phone" label="电话" width="120" />
<el-table-column prop="department_resources_name" label="操作" width="120">
<template #default="scope">
<el-button @click="showLog(scope.row)" size="small">查看日志</el-button>
</template>
</el-table-column>
</el-table>
<div class="page">
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize"
:page-sizes="[15, 50, 100, 200]" layout="total,sizes, prev, pager, next" :total="total"
@size-change="PageSizeChange" @current-change="PageCurrentChange" />
</div>
<el-dialog v-model="LogShow" title="变更记录" width="50%">
<div>
<el-table :data="LogList" style="width: 100%;" row-key="id">
<el-table-column prop="list_status" label="状态" width="100">
<template #default="scope">
<el-tag v-if="scope.row.new_status==0" class="ml-2" type="info"></el-tag>
<el-tag v-if="scope.row.new_status==1" class="ml-2" type="success"></el-tag>
<el-tag v-if="scope.row.new_status==2" class="ml-2"></el-tag>
<el-tag v-if="scope.row.new_status==3" class="ml-2" type="warning"></el-tag>
</template>
</el-table-column>
<el-table-column prop="reg_num" label="登记号" />
<el-table-column prop="note" label="说明" />
<el-table-column prop="create_user" label="操作人/ID" />
<el-table-column prop="created_at" label="记录时间" />
<el-table-column prop="data" label="状态" width="100">
<template #default="scope">
<el-button @click="showLogJson(scope.row.data)" size="small">查看data</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-dialog>
<el-dialog v-model="LogDataShow" title="查看dataJson" width="40%">
<div style="word-break: break-all">
{{LogDataJson}}
</div>
</el-dialog>
<el-dialog v-model="ItemGroupShow" title="选择可一起预约的项目" width="40%">
<div v-if="ItemGroup.length>0">
<div style="display: flex; border:1px solid #ccc;color: #999;">
<div style="width: 50%; border-right: 1px solid #ccc;padding: 8px; text-align: center;">
可一起预约项目
</div>
<div style="width: 50%; padding: 8px;text-align: center;">
关联的排班
</div>
</div>
<div v-for="(group,index) in ItemGroup" :key="index" class="dialogitem" @click="ClickGroup(group)">
<div style="width: 50%; display: flex;border-right: 1px solid #ccc;padding: 8px;">
<div v-for="(item,i) in group">
<span style="margin-left: 8px; width: 200px;">{{item.item_name}}</span>
</div>
</div>
<div style="margin-left: 8px;padding: 8px;display: flex;">
<div>{{group[0].device_name}}</div>
</div>
</div>
</div>
</el-dialog>
<div id="shenqingdan" v-if="shenqingdan_show" style="position: relative;">
<div v-for="(item,index) in shenqingdan_list" :key="index">
<ShenQingDan :printInfo="item.maininfo"></ShenQingDan>
</div>
</div>
<el-dialog v-model="TiShiShow" title="提示" width="40%" >
<div v-for="(item,index) in successItem" :key="index" style="font-size:16px;">
<table style="margin-top: -30px;">
<tr><td colspan="2" style="font-weight: 700; font-size: 18px;color:#4b6493">{{item.entrust}}<el-tag type="success" size="large">预约成功</el-tag></td></tr>
<tr><td style="font-weight: 700;">体检号</td><td>{{item.signInData[0].checkNo}}</td></tr>
<tr><td style="font-weight: 700;">排队号</td><td>{{item.signInData[0].queueNo}}</td></tr>
<tr><td style="font-weight: 700;">等待人数</td><td>{{item.signInData[0].currentNum}}</td></tr>
</table>
</div>
<div v-for="(item,index) in failItem" :key="index+200" style="color: crimson;">
{{item}}
</div>
</el-dialog>
</div>
</template>
<script setup>
import {
ref,
onMounted,
nextTick,watch
} from 'vue'
import {
GetMainListByDept,
CancelYuYue,
GetEnableDeviceList,
DepartmentResourceGetEnableList,
GetFilteredDepartmentList,
GetServiceDateTime,
GetLoglist,
CheckEntrstItemGroup,
getMainDetail,
SignIn,
CancelSign,
SearchDepartment
} from '@/api/api.js'
import {
usePinia
} from '@/stores/index.js'
import {
ElMessage,
ElMessageBox
} from 'element-plus'
import ShenQingDan from '@/components/Yewu/PrintShenQingDan.vue'
import YuYue202506 from '@/components/Yewu/YuYue202506.vue'
let pinia = usePinia()
let YuYueVueDialogVisible=ref(false);
let YuYueKey=ref(0);
let AutoGroup=ref(true);
let shenqingdan_show=ref(false);
let do_type = ref(0)
let loading = ref(false)
let entrust_ids=ref('')
let searchInfo = ref({
dateType: 'both',
dateRange: [],
list_status: null,
patient_type: null,
resources: [],
services_group: null,
reg_num: null,
user_name: null,
apply_department: null
})
let loginType=ref('');
let tableData = ref([])
let tableref = ref(null)
let currentPage = ref(1)
let pageSize = ref(15)
let total = 0
let deptOptions = ref([])
//
let selectedDeptId = ref(0)
let deptList = ref([])
const debounce = (fn, delay) => {
let timer = null
return function(...args) {
clearTimeout(timer)
timer = setTimeout(() => fn.apply(this, args), delay)
}
}
const searchDept = (keyword) => {
if (!keyword) {
deptOptions.value = []
return
}
SearchDepartment({ keyword }).then(res => {
if (res.status) {
deptOptions.value = res.data.list
} else {
deptOptions.value = []
}
})
}
const debouncedSearchDept = debounce(searchDept, 300)
const GetList = () => {
loading.value = true
GetMainListByDept({
searchInfo: searchInfo.value,
page: currentPage.value,
pageSize: pageSize.value,
department_id: selectedDeptId.value
}).then(res => {
loading.value = false
if (res.status) {
tableData.value = res.data.list
total = res.data.count
} else {
ElMessage.error(res.msg)
}
})
}
const PageSizeChange = (e) => {
pageSize.value = e
GetList()
}
const PageCurrentChange = (e) => {
currentPage.value = e
GetList()
}
let tableSelected = ref([])
const handleSelect = (e) => {
if (e.length > 1) {
const firstStatus = e[0].list_status;
const allEqual = e.every(item => item.list_status === firstStatus);
if (!allEqual) {
SelectClear()
e = [e[e.length - 1]]
}
}
if (e[e.length - 1].list_status != 0) {
SelectClear()
e = [e[e.length - 1]]
}
let select_qian = tableSelected.value ? tableSelected.value.length : 0
autoSeleted(e)
if (select_qian > tableSelected.value.length) {
} else {
FindMatchItem()
}
}
const handleSelectionChange = (e) => {
}
const SelectClear = () => {
tableref.value.clearSelection()
tableSelected.value = []
}
const autoSeleted = (rows) => {
if (rows) {
rows.forEach((row) => {
if(row!=undefined){
tableref.value.toggleRowSelection(row, true)
}
})
tableSelected.value = rows
} else {
SelectClear()
}
}
let ItemGroupShow = ref(false);
let ItemGroup = ref(null);
const FindMatchItem = () => {
console.log(tableSelected.value)
tableSelected.value.reverse();
let temp_reg_num=''
let chongzhi=false;
tableSelected.value.forEach((v, i) => {
if(temp_reg_num==''){
temp_reg_num=v.reg_num
}
if(temp_reg_num!='' && temp_reg_num!=v.reg_num){
chongzhi=true
}
})
if(chongzhi===true){
let a=tableSelected.value[0]
SelectClear();
autoSeleted([a])
}
console.log(tableSelected.value)
let list = []
if(tableSelected.value[0].list_status!=0)return false
tableData.value.forEach((v, i) => {
if (v.list_status == 0 && v.reg_num == tableSelected.value[0].reg_num && v.episodeid ==
tableSelected.value[0].episodeid) {
if (v.id == tableSelected.value[0].id) {
list.push({
name: v.entrust,
rowid: i,
first: 1
})
} else {
list.push({
name: v.entrust,
rowid: i,
first: 0
})
}
}
})
if (list.length >= 2) {
loading.value = true
CheckEntrstItemGroup({
items: list
}).then(res => {
loading.value = false
if (res.status) {
let group = res.data.group
if (group.length > 1000000000) {
ItemGroupShow.value = true;
ItemGroup.value = group
} else {
SelectClear();
let d = []
group[0].forEach((vv, ii) => {
d.push(tableData.value[vv.rowid])
})
autoSeleted(d)
}
} else {
ElMessage.error(res.msg)
}
})
}
}
const ClickGroup = (group) => {
SelectClear();
let d = []
group.forEach((v, i) => {
d.push(tableData.value[v.rowid])
})
autoSeleted(d)
ItemGroupShow.value = false
}
let selectedUrl = ref('')
let fileUrl = import.meta.env.VITE_APP_FILE
let iframe_show = ref(false)
const openIframe = (type) => {
console.log(tableSelected.value)
let entrustids=[]
do_type.value = type
console.log(tableSelected.value);
if (tableSelected.value.length === 0) {
ElMessage.error('请勾选1条记录')
return false
}
let next=true
tableSelected.value.forEach((v,i)=>{
if(type==1 && v.list_status!=0){
ElMessage.error(v.entrust+" 不可进行预约操作")
next=false
}
if(type==2 && v.list_status!=1){
ElMessage.error(v.entrust+" 不可进行改约操作")
next=false
}
entrustids.push(v.entrust_id)
})
if(next==false) return false
YuYueKey.value++
entrust_ids.value=entrustids.join(',')
YuYueVueDialogVisible.value=true
selectedUrl.value = fileUrl + '/jq_page/appointment.html?' + 'regnum=' + encodeURIComponent(tableSelected
.value[0].reg_num) + '&entrustid=' + encodeURIComponent(entrustids) + '&episodeid=' + encodeURIComponent(tableSelected
.value[0].episodeid) + '&dotype=' + do_type.value + '&appointment_type=4'
console.log(selectedUrl.value)
}
const dialogopened = () => {
nextTick(() => {
const oIframe = document.getElementById('bdIframe')
const deviceHeight = document.getElementsByClassName('el-dialog')[0].clientHeight
oIframe.style.height = (deviceHeight - 105) + 'px'
})
}
const cancel = () => {
if (tableSelected.value.length === 0) {
ElMessage.error('请勾选1条记录')
return false
}
let next = true
tableSelected.value.forEach((v) => {
if (v.list_status != 1) {
ElMessage.error(`${v.entrust} 无需取消`)
next = false
}
})
if (!next) return false
loading.value = true;
const handleSuccessAction = (password) => {
CancelYuYue({
MainListId: tableSelected.value[0].id,
reg_num: tableSelected.value[0].reg_num,
do_user: loginUserinfo.value.id,
password: password,
}).then(res => {
loading.value = false
if (res.status) {
ElMessage({
message: res.msg,
type: 'success',
})
GetList()
} else {
ElMessage.error(res.msg)
}
}).catch(err => {
loading.value = false
console.error(err)
})
}
if (loginType.value === 'SysLogin') {
ElMessageBox.prompt('请输入登录密码后再操作', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputType: 'password',
inputPattern: /.+/,
inputErrorMessage: '密码不能为空'
})
.then(({ value }) => {
handleSuccessAction(value)
})
.catch(() => {
loading.value = false
})
} else if (loginType.value === 'HisLogin' || loginType.value === 'CasLogin') {
ElMessageBox.confirm('确定要执行取消操作吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
handleSuccessAction('noPassword')
})
.catch(() => {
loading.value = false
})
} else {
loading.value = false
ElMessage.error('未知的操作类型配置')
}
}
let EnableDeviceList = ref([])
const GetEnableDeviceListFunc = () => {
loading.value = true
GetEnableDeviceList().then(res => {
loading.value = false
if (res.status) {
EnableDeviceList.value = res.data
} else {
ElMessage.error(res.msg)
}
})
}
let enableResourceList = ref([])
const getEnableResource = () => {
loading.value = true
DepartmentResourceGetEnableList({
department_id: selectedDeptId.value || undefined
}).then(res => {
loading.value = false
if (res.status) {
enableResourceList.value = res.data
} else {
}
})
}
//
const getDeptList = () => {
loading.value = true
GetFilteredDepartmentList({}).then(res => {
loading.value = false
if (res.status) {
deptList.value = res.data.list
} else {
ElMessage.error(res.msg)
}
})
}
//
const deptChange = () => {
searchInfo.value.resources = []
enableResourceList.value = []
getEnableResource()
GetList()
}
const GetServiceDate = () => {
GetServiceDateTime().then(res => {
if (res.status) {
let datetime = res.data.datetime.substr(0, 10)
searchInfo.value.dateRange = [datetime, datetime]
GetList()
}
})
}
let LogShow = ref(false);
let LogList = ref(null);
let LogDataShow = ref(false);
const showLog = (row) => {
LogShow.value = true
GetLoglist({
id: row.id
}).then(res => {
if (res.status) {
LogList.value = res.data
}
})
}
let LogDataJson = ref('');
const showLogJson = (data) => {
LogDataShow.value = true
LogDataJson.value = data
}
let loginUserinfo=ref(null);
let print_shenqingdan_button=ref(null);
let shenqingdan_list=ref([])
const print_shenqingdan=()=>{
if(tableSelected.value.length !==1){
ElMessage.error('请选择1项进行打印')
return false
}
getMainDetail({
regnum: tableSelected.value[0].reg_num,
entrustid: tableSelected.value[0].entrust_id,
episodeid: tableSelected.value[0].episodeid,
appointment_type:4
}).then(res => {
if (res.status) {
let enable=true
shenqingdan_list.value=res.data.info
shenqingdan_list.value.forEach((v,i)=>{
if(v.maininfo.list_status!=1){
ElMessage.error(v.maininfo.entrust+" 不可打印,请重新选择")
enable=false
}
})
if(enable){
shenqingdan_show.value=true
setTimeout(function(){
print_shenqingdan_button.value.$el.click();
shenqingdan_show.value=false
},500)
}
}else{
ElMessage.error(res.msg)
}
})
}
const closeYuYueDialog=()=>{
GetList()
}
let TiShiShow=ref(false);
let successItem=ref([]);
let failItem=ref([]);
const SignInFunc=()=>{
if (tableSelected.value.length === 0) {
ElMessage.error('请勾选1条记录')
return false
}
let ids=[];
tableSelected.value.forEach((v,i)=>{
ids.push(v.id)
})
loading.value=true;
SignIn({
MainListIds:ids
}).then(res => {
loading.value=false
if (res.status) {
if(res.data.success.length==ids.length){
ElMessage({
message: "签到成功",
type: 'success',
})
TiShiShow.value=true
successItem.value=res.data.success
failItem.value=res.data.fail
}else{
TiShiShow.value=true
successItem.value=res.data.success
failItem.value=res.data.fail
}
GetList()
}else{
ElMessage.error(res.msg)
}
})
}
const CancelSignFunc=()=>{
if (tableSelected.value.length === 0) {
ElMessage.error('请勾选1条记录')
return false
}
let ids=[];
tableSelected.value.forEach((v,i)=>{
ids.push(v.id)
})
loading.value=true;
CancelSign({
MainListIds:ids
}).then(res => {
loading.value=false
if (res.status) {
if(res.data.success.length==ids.length){
ElMessage({
message: "取消成功",
type: 'success',
})
}else{
TiShiShow.value=true
successItem.value=res.data.success
failItem.value=res.data.fail
}
GetList()
}else{
ElMessage.error(res.msg)
}
})
}
const getStorageData=()=>{
if(sessionStorage.getItem("LoginUserInfo")){
loginUserinfo.value=JSON.parse(sessionStorage.getItem("LoginUserInfo"))
}
if((loginUserinfo.value && loginUserinfo.value.group==7) || loginType.value==='HisLogin'){
searchInfo.value.doctor=loginUserinfo.value.cn_name
let default_reg_num=sessionStorage.getItem("default_reg_num")
if(default_reg_num && default_reg_num!='null' && default_reg_num!=''){
searchInfo.value.reg_num=default_reg_num
}
}
}
onMounted(() => {
loginType.value=sessionStorage.getItem('LoginType')
GetEnableDeviceListFunc()
getDeptList()
if(pinia.baseInfoStatus){
getStorageData()
getEnableResource()
GetServiceDate()
}else{
const unwatch = watch(
() => pinia.baseInfoStatus,
(newVal) => {
if (newVal) {
getStorageData()
getEnableResource()
GetServiceDate()
unwatch();
}
}
);
}
})
</script>
<style >
.iframe {
border: 0px;
width: 100%;
height: 100%;
margin-top: -20px;
}
.iframeDialog {
margin-top: 40px;
height: 90%;
width: 90%;
overflow: auto;
}
</style>
<style scoped>
.dialogitem {
cursor: pointer;
;
display: flex;
border-left: 1px solid #ccc;
border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
.dialogitem:hover {
background-color: darkcyan;
color: #fff;
}
</style>
Loading…
Cancel
Save