批量改计划

main
鹿和sa0ChunLuyu 6 days ago
parent 45cc94e416
commit 5e62360f35

@ -509,6 +509,155 @@ public function SaveLockedCount(Request $request)
} }
} }
//批量禁用计划模板
public function BatchDisable(Request $request)
{
$userid = $request->get('userid');
$ids = request('ids');
if (empty($ids) || !is_array($ids)) {
return \Yz::echoError1('请选择要禁用的记录');
}
$userInfo = DB::table('users')->where(['id' => $userid])->first();
if (!$userInfo) {
return \Yz::echoError1('用户信息不存在');
}
$department_id = $userInfo->department_id;
$d = DB::table('s_source_roster')
->where(['department_id' => $department_id, 'is_del' => 0])
->whereIn('id', $ids)
->update(['status' => 0]);
if ($d) {
return \Yz::Return(true, '批量禁用成功', []);
} else {
return \Yz::echoError1('操作失败,可能数据不存在或无变化');
}
}
//批量启用计划模板
public function BatchEnable(Request $request)
{
$userid = $request->get('userid');
$ids = request('ids');
if (empty($ids) || !is_array($ids)) {
return \Yz::echoError1('请选择要启用的记录');
}
$userInfo = DB::table('users')->where(['id' => $userid])->first();
if (!$userInfo) {
return \Yz::echoError1('用户信息不存在');
}
$department_id = $userInfo->department_id;
$d = DB::table('s_source_roster')
->where(['department_id' => $department_id, 'is_del' => 0])
->whereIn('id', $ids)
->update(['status' => 1]);
if ($d) {
return \Yz::Return(true, '批量启用成功', []);
} else {
return \Yz::echoError1('操作失败,可能数据不存在或无变化');
}
}
//批量修改计划模板(渠道数量+病人类型)
public function BatchChange(Request $request)
{
$userid = $request->get('userid');
$ids = request('ids');
$max_total = request('max_total');
$coutsInfo = request('coutsInfo');
$patientType = request('patientType');
if (empty($ids) || !is_array($ids)) {
return \Yz::echoError1('请选择要修改的记录');
}
if (empty($coutsInfo) || !is_array($coutsInfo)) {
return \Yz::echoError1('渠道数量不能为空');
}
$userInfo = DB::table('users')->where(['id' => $userid])->first();
if (!$userInfo) {
return \Yz::echoError1('用户信息不存在');
}
$department_id = $userInfo->department_id;
DB::beginTransaction();
try {
$patient_type_str = !empty($patientType) ? implode(',', $patientType) : '';
foreach ($ids as $id) {
// 更新病人类型
if ($patient_type_str !== '') {
DB::table('s_source_roster')
->where(['id' => $id, 'department_id' => $department_id, 'is_del' => 0])
->update(['patient_type' => $patient_type_str]);
}
// 更新渠道数量
foreach ($coutsInfo as $ci) {
DB::table('s_source_roster_count')
->where([
'roster_id' => $id,
'appointment_type_id' => $ci['appointment_type_id']
])
->update([
'max_total' => $max_total,
'count' => $ci['count']
]);
}
}
DB::commit();
return \Yz::Return(true, '批量修改成功', []);
} catch (\Exception $e) {
DB::rollBack();
return \Yz::echoError1('批量修改失败:' . $e->getMessage());
}
}
//批量占用计划模板
public function BatchOccupy(Request $request)
{
$userid = $request->get('userid');
$ids = request('ids');
$coutsInfo = request('coutsInfo');
if (empty($ids) || !is_array($ids)) {
return \Yz::echoError1('请选择要占用的记录');
}
if (empty($coutsInfo) || !is_array($coutsInfo)) {
return \Yz::echoError1('占用数量不能为空');
}
$userInfo = DB::table('users')->where(['id' => $userid])->first();
if (!$userInfo) {
return \Yz::echoError1('用户信息不存在');
}
$department_id = $userInfo->department_id;
DB::beginTransaction();
try {
foreach ($ids as $id) {
foreach ($coutsInfo as $ci) {
DB::table('s_source_roster_count')
->where([
'roster_id' => $id,
'appointment_type_id' => $ci['appointment_type_id']
])
->update([
'locked_count' => $ci['locked_count']
]);
}
}
DB::commit();
return \Yz::Return(true, '批量占用成功', []);
} catch (\Exception $e) {
DB::rollBack();
return \Yz::echoError1('批量占用失败:' . $e->getMessage());
}
}
// ===== 管理员接口(接受 department_id 参数,不依赖用户绑定科室) ===== // ===== 管理员接口(接受 department_id 参数,不依赖用户绑定科室) =====
//管理员保存计划模板 //管理员保存计划模板
@ -715,4 +864,143 @@ public function SaveAppointmentRatioAdmin(Request $request)
return \Yz::echoError1('保存失败,无数据更新'); return \Yz::echoError1('保存失败,无数据更新');
} }
} }
//管理员批量禁用计划模板
public function BatchDisableAdmin(Request $request)
{
$department_id = request('department_id');
if (!$department_id) {
return \Yz::echoError1('科室信息不存在');
}
$ids = request('ids');
if (empty($ids) || !is_array($ids)) {
return \Yz::echoError1('请选择要禁用的记录');
}
$d = DB::table('s_source_roster')
->where(['department_id' => $department_id, 'is_del' => 0])
->whereIn('id', $ids)
->update(['status' => 0]);
if ($d) {
return \Yz::Return(true, '批量禁用成功', []);
} else {
return \Yz::echoError1('操作失败,可能数据不存在或无变化');
}
}
//管理员批量启用计划模板
public function BatchEnableAdmin(Request $request)
{
$department_id = request('department_id');
if (!$department_id) {
return \Yz::echoError1('科室信息不存在');
}
$ids = request('ids');
if (empty($ids) || !is_array($ids)) {
return \Yz::echoError1('请选择要启用的记录');
}
$d = DB::table('s_source_roster')
->where(['department_id' => $department_id, 'is_del' => 0])
->whereIn('id', $ids)
->update(['status' => 1]);
if ($d) {
return \Yz::Return(true, '批量启用成功', []);
} else {
return \Yz::echoError1('操作失败,可能数据不存在或无变化');
}
}
//管理员批量修改计划模板(渠道数量+病人类型)
public function BatchChangeAdmin(Request $request)
{
$department_id = request('department_id');
if (!$department_id) {
return \Yz::echoError1('科室信息不存在');
}
$ids = request('ids');
$max_total = request('max_total');
$coutsInfo = request('coutsInfo');
$patientType = request('patientType');
if (empty($ids) || !is_array($ids)) {
return \Yz::echoError1('请选择要修改的记录');
}
if (empty($coutsInfo) || !is_array($coutsInfo)) {
return \Yz::echoError1('渠道数量不能为空');
}
DB::beginTransaction();
try {
$patient_type_str = !empty($patientType) ? implode(',', $patientType) : '';
foreach ($ids as $id) {
// 更新病人类型
if ($patient_type_str !== '') {
DB::table('s_source_roster')
->where(['id' => $id, 'department_id' => $department_id, 'is_del' => 0])
->update(['patient_type' => $patient_type_str]);
}
// 更新渠道数量
foreach ($coutsInfo as $ci) {
DB::table('s_source_roster_count')
->where([
'roster_id' => $id,
'appointment_type_id' => $ci['appointment_type_id']
])
->update([
'max_total' => $max_total,
'count' => $ci['count']
]);
}
}
DB::commit();
return \Yz::Return(true, '批量修改成功', []);
} catch (\Exception $e) {
DB::rollBack();
return \Yz::echoError1('批量修改失败:' . $e->getMessage());
}
}
//管理员批量占用计划模板
public function BatchOccupyAdmin(Request $request)
{
$department_id = request('department_id');
if (!$department_id) {
return \Yz::echoError1('科室信息不存在');
}
$ids = request('ids');
$coutsInfo = request('coutsInfo');
if (empty($ids) || !is_array($ids)) {
return \Yz::echoError1('请选择要占用的记录');
}
if (empty($coutsInfo) || !is_array($coutsInfo)) {
return \Yz::echoError1('占用数量不能为空');
}
DB::beginTransaction();
try {
foreach ($ids as $id) {
foreach ($coutsInfo as $ci) {
DB::table('s_source_roster_count')
->where([
'roster_id' => $id,
'appointment_type_id' => $ci['appointment_type_id']
])
->update([
'locked_count' => $ci['locked_count']
]);
}
}
DB::commit();
return \Yz::Return(true, '批量占用成功', []);
} catch (\Exception $e) {
DB::rollBack();
return \Yz::echoError1('批量占用失败:' . $e->getMessage());
}
}
} }

@ -108,11 +108,19 @@
Route::post('admin/PlanModelDel','App\Http\Controllers\API\Admin\YeWu\PlanModelController@Del');//删除计划模板 Route::post('admin/PlanModelDel','App\Http\Controllers\API\Admin\YeWu\PlanModelController@Del');//删除计划模板
Route::post('admin/SaveAppointmentRatio','App\Http\Controllers\API\Admin\YeWu\PlanModelController@SaveAppointmentRatio');//保存渠道比例 Route::post('admin/SaveAppointmentRatio','App\Http\Controllers\API\Admin\YeWu\PlanModelController@SaveAppointmentRatio');//保存渠道比例
Route::post('admin/SavePlanModelLockedCount','App\Http\Controllers\API\Admin\YeWu\PlanModelController@SaveLockedCount');//保存模板占位数量 Route::post('admin/SavePlanModelLockedCount','App\Http\Controllers\API\Admin\YeWu\PlanModelController@SaveLockedCount');//保存模板占位数量
Route::post('admin/PlanModelBatchDisable','App\Http\Controllers\API\Admin\YeWu\PlanModelController@BatchDisable');//批量禁用计划模板
Route::post('admin/PlanModelBatchEnable','App\Http\Controllers\API\Admin\YeWu\PlanModelController@BatchEnable');//批量启用计划模板
Route::post('admin/PlanModelBatchChange','App\Http\Controllers\API\Admin\YeWu\PlanModelController@BatchChange');//批量修改计划模板
Route::post('admin/PlanModelBatchOccupy','App\Http\Controllers\API\Admin\YeWu\PlanModelController@BatchOccupy');//批量占用计划模板
// ===== 管理员接口(接受 department_id 参数) ===== // ===== 管理员接口(接受 department_id 参数) =====
Route::post('admin/PlanModelSaveAdmin','App\Http\Controllers\API\Admin\YeWu\PlanModelController@SaveAdmin');//管理员保存计划模板 Route::post('admin/PlanModelSaveAdmin','App\Http\Controllers\API\Admin\YeWu\PlanModelController@SaveAdmin');//管理员保存计划模板
Route::post('admin/PlanModelDelAdmin','App\Http\Controllers\API\Admin\YeWu\PlanModelController@DelAdmin');//管理员删除计划模板 Route::post('admin/PlanModelDelAdmin','App\Http\Controllers\API\Admin\YeWu\PlanModelController@DelAdmin');//管理员删除计划模板
Route::post('admin/GetAppointmentRatioAdmin','App\Http\Controllers\API\Admin\YeWu\PlanModelController@GetAppointmentRatioAdmin');//管理员获取预约类型比例 Route::post('admin/GetAppointmentRatioAdmin','App\Http\Controllers\API\Admin\YeWu\PlanModelController@GetAppointmentRatioAdmin');//管理员获取预约类型比例
Route::post('admin/SaveAppointmentRatioAdmin','App\Http\Controllers\API\Admin\YeWu\PlanModelController@SaveAppointmentRatioAdmin');//管理员保存渠道比例 Route::post('admin/SaveAppointmentRatioAdmin','App\Http\Controllers\API\Admin\YeWu\PlanModelController@SaveAppointmentRatioAdmin');//管理员保存渠道比例
Route::post('admin/PlanModelBatchDisableAdmin','App\Http\Controllers\API\Admin\YeWu\PlanModelController@BatchDisableAdmin');//管理员批量禁用计划模板
Route::post('admin/PlanModelBatchEnableAdmin','App\Http\Controllers\API\Admin\YeWu\PlanModelController@BatchEnableAdmin');//管理员批量启用计划模板
Route::post('admin/PlanModelBatchChangeAdmin','App\Http\Controllers\API\Admin\YeWu\PlanModelController@BatchChangeAdmin');//管理员批量修改计划模板
Route::post('admin/PlanModelBatchOccupyAdmin','App\Http\Controllers\API\Admin\YeWu\PlanModelController@BatchOccupyAdmin');//管理员批量占用计划模板
Route::post('admin/CreatePlanListAdmin','App\Http\Controllers\API\Admin\YeWu\PlanListController@CreateAdmin');//管理员生成计划明细 Route::post('admin/CreatePlanListAdmin','App\Http\Controllers\API\Admin\YeWu\PlanListController@CreateAdmin');//管理员生成计划明细
Route::post('admin/PlanDetailChangeInfoAdmin','App\Http\Controllers\API\Admin\YeWu\PlanListController@ChangeInfoAdmin');//管理员修改计划详情 Route::post('admin/PlanDetailChangeInfoAdmin','App\Http\Controllers\API\Admin\YeWu\PlanListController@ChangeInfoAdmin');//管理员修改计划详情
Route::post('admin/PlanListDelAdmin','App\Http\Controllers\API\Admin\YeWu\PlanListController@DelAdmin');//管理员删除计划明细 Route::post('admin/PlanListDelAdmin','App\Http\Controllers\API\Admin\YeWu\PlanListController@DelAdmin');//管理员删除计划明细

@ -343,6 +343,38 @@ export const PlanModelDel = (data = {}) => {
export const PlanModelDelAdmin = (data = {}) => { export const PlanModelDelAdmin = (data = {}) => {
return axios({ url: import.meta.env.VITE_APP_API + 'v1/admin/PlanModelDelAdmin', data: data }) return axios({ url: import.meta.env.VITE_APP_API + 'v1/admin/PlanModelDelAdmin', data: data })
} }
//admin批量禁用计划模板
export const PlanModelBatchDisable = (data = {}) => {
return axios({ url: import.meta.env.VITE_APP_API + 'v1/admin/PlanModelBatchDisable', data: data })
}
//admin批量禁用计划模板管理员接口
export const PlanModelBatchDisableAdmin = (data = {}) => {
return axios({ url: import.meta.env.VITE_APP_API + 'v1/admin/PlanModelBatchDisableAdmin', data: data })
}
//admin批量启用计划模板
export const PlanModelBatchEnable = (data = {}) => {
return axios({ url: import.meta.env.VITE_APP_API + 'v1/admin/PlanModelBatchEnable', data: data })
}
//admin批量启用计划模板管理员接口
export const PlanModelBatchEnableAdmin = (data = {}) => {
return axios({ url: import.meta.env.VITE_APP_API + 'v1/admin/PlanModelBatchEnableAdmin', data: data })
}
//admin批量修改计划模板渠道数量+病人类型)
export const PlanModelBatchChange = (data = {}) => {
return axios({ url: import.meta.env.VITE_APP_API + 'v1/admin/PlanModelBatchChange', data: data })
}
//admin批量修改计划模板管理员接口
export const PlanModelBatchChangeAdmin = (data = {}) => {
return axios({ url: import.meta.env.VITE_APP_API + 'v1/admin/PlanModelBatchChangeAdmin', data: data })
}
//admin批量占用计划模板
export const PlanModelBatchOccupy = (data = {}) => {
return axios({ url: import.meta.env.VITE_APP_API + 'v1/admin/PlanModelBatchOccupy', data: data })
}
//admin批量占用计划模板管理员接口
export const PlanModelBatchOccupyAdmin = (data = {}) => {
return axios({ url: import.meta.env.VITE_APP_API + 'v1/admin/PlanModelBatchOccupyAdmin', data: data })
}
//admin保存预约类型渠道比例 //admin保存预约类型渠道比例
export const SaveAppointmentRatio = (data = {}) => { export const SaveAppointmentRatio = (data = {}) => {
return axios({ url: import.meta.env.VITE_APP_API + 'v1/admin/SaveAppointmentRatio', data: data }) return axios({ url: import.meta.env.VITE_APP_API + 'v1/admin/SaveAppointmentRatio', data: data })

@ -41,6 +41,10 @@
<el-button type="primary" @click="Add()" style="margin-left: 10px;">添加</el-button> <el-button type="primary" @click="Add()" style="margin-left: 10px;">添加</el-button>
<el-button type="danger" @click="Del()" style="margin-left: 10px;">删除</el-button> <el-button type="danger" @click="Del()" style="margin-left: 10px;">删除</el-button>
<el-button type="success" @click="CreatedPlanClick()" style="margin-left: 20px;">生成选中的计划</el-button> <el-button type="success" @click="CreatedPlanClick()" style="margin-left: 20px;">生成选中的计划</el-button>
<el-button type="warning" @click="BatchDisable()" style="margin-left: 10px;">批量禁用</el-button>
<el-button type="success" @click="BatchEnable()" style="margin-left: 10px;">批量启用</el-button>
<el-button type="primary" @click="openBatchModify()" style="margin-left: 10px;">批量修改</el-button>
<el-button type="warning" @click="openBatchOccupy()" style="margin-left: 10px;">批量占用</el-button>
</el-row> </el-row>
</div> </div>
<el-row> <el-row>
@ -60,7 +64,7 @@
{{item.resources_name}} {{item.resources_name}}
</div> </div>
</div> </div>
<div v-loading="loading" style="border: 1px solid #efefef;width: 100%;"> <div ref="scrollContainer" v-loading="loading" style="border: 1px solid #efefef;width: 100%;position: relative;overflow-x: auto;">
<table class="planTable" v-if="planTableData.length>0" > <table class="planTable" v-if="planTableData.length>0" >
<tr style="background-color: #f1f1f1;position: relative;"> <tr style="background-color: #f1f1f1;position: relative;">
<td style="width: 120px;display: flex;font-size: 12px;"> <td style="width: 120px;display: flex;font-size: 12px;">
@ -73,10 +77,14 @@
<td style="font-weight: 700;" v-for="(item,index) in xingqi">{{item.label}}</td> <td style="font-weight: 700;" v-for="(item,index) in xingqi">{{item.label}}</td>
</tr> </tr>
<tr v-for="(item,index) in planTableData" :key="index"> <tr v-for="(item,index) in planTableData" :key="index">
<td v-for="(item2,index2) in item" :key="index2" @mouseover="showTools(item2.id)" <td v-for="(item2,index2) in item" :key="index2"
@mouseleave="hideTools(item2.id)"> :data-row="index" :data-col="index2"
@mouseover="onCellHover($event, item2, index, index2)"
@mouseleave="onCellLeave(item2?.id)"
@mousedown="onCellMouseDown($event, item2, index, index2)">
<span v-if="index2=='time_range'">{{item2}}</span> <span v-if="index2=='time_range'">{{item2}}</span>
<div v-if="item2.countsInfo && item2.countsInfo.length>0" :class="{'planInfo_k':item2.status==1,'planInfo_k planInfo_k_disabled':item2.status==0}"> <div v-if="item2.countsInfo && item2.countsInfo.length>0"
:class="{'planInfo_k':item2.status==1,'planInfo_k planInfo_k_disabled':item2.status==0,'planInfo_k_selected':isSelected(item2.id) && item2.status==1,'planInfo_k_disabled_selected':isSelected(item2.id) && item2.status==0}">
<div v-if="hoverIndex==item2.id" class="hoverTools"> <div v-if="hoverIndex==item2.id" class="hoverTools">
<div class="icon_k" @click="Add(item2)"> <div class="icon_k" @click="Add(item2)">
<el-icon color="#409eff" size="20"> <el-icon color="#409eff" size="20">
@ -116,6 +124,7 @@
</td> </td>
</tr> </tr>
</table> </table>
<div v-if="isDragging" class="drag-selection-overlay" :style="dragOverlayStyle"></div>
<div v-if="planTableData.length==0" style="width: 100%;background-color: #fff;"> <div v-if="planTableData.length==0" style="width: 100%;background-color: #fff;">
<el-empty description="暂无" /> <el-empty description="暂无" />
</div> </div>
@ -379,13 +388,85 @@
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
<!-- 批量修改弹窗 -->
<el-dialog v-model="BatchModifyDialogVisible" title="批量修改" width="45%" :close-on-click-modal="false">
<div>
<div style="margin-bottom: 16px; color: #666;">已选 {{ selectedPlanArr.length }} 条号源明细</div>
<el-form label-width="100px">
<el-row :gutter="10" style="margin-bottom: 6px; font-weight: 600; color: #333; font-size: 13px;">
<el-col :span="6">渠道名称</el-col>
<el-col :span="8">数量</el-col>
</el-row>
<el-row v-for="(ch, ci) in batchChannels" :key="ci" :gutter="10" style="margin-bottom: 10px;">
<el-col :span="6" style="line-height: 32px;">{{ ch.name }}</el-col>
<el-col :span="8">
<el-input v-model.number="batchForm[ci].count" placeholder="数量" size="default"
oninput="value=value.replace(/[^0-9]/g,'')" />
</el-col>
</el-row>
<el-row :gutter="10" style="border-top: 1px solid #eee; padding-top: 10px;">
<el-col :span="6" style="line-height: 32px; font-weight: 600;">合计</el-col>
<el-col :span="8" style="line-height: 32px; font-weight: 600;">数量: {{ batchTotalCount }}</el-col>
</el-row>
</el-form>
<div style="margin-top: 16px; padding-top: 12px; border-top: 1px solid #eee;">
<div style="margin-bottom: 8px; font-weight: 600; color: #333; font-size: 13px;">病人类型</div>
<el-checkbox-group v-model="batchPatientType">
<el-checkbox label="0">住院</el-checkbox>
<el-checkbox label="1">门诊</el-checkbox>
<el-checkbox label="2">急诊</el-checkbox>
<el-checkbox label="3">体检</el-checkbox>
</el-checkbox-group>
</div>
</div>
<template #footer>
<el-button @click="BatchModifyDialogVisible = false" :disabled="batchModifyRunning">取消</el-button>
<el-button type="primary" @click="execBatchModify()" :disabled="batchModifyRunning">确定修改</el-button>
</template>
</el-dialog>
<!-- 批量占用弹窗 -->
<el-dialog v-model="BatchOccupyDialogVisible" title="批量占用" width="45%" :close-on-click-modal="false">
<div>
<div style="margin-bottom: 16px; color: #666;">已选 {{ selectedPlanArr.length }} 条号源明细</div>
<el-form label-width="100px">
<el-row :gutter="10" style="margin-bottom: 6px; font-weight: 600; color: #333; font-size: 13px;">
<el-col :span="6">渠道名称</el-col>
<el-col :span="8">占用数量</el-col>
<el-col :span="8">提示</el-col>
</el-row>
<el-row v-for="(ch, ci) in batchOccupyChannels" :key="ci" :gutter="10" style="margin-bottom: 10px;">
<el-col :span="6" style="line-height: 32px;">{{ ch.name }}</el-col>
<el-col :span="8">
<el-input v-model.number="batchOccupyForm[ci].locked_count" placeholder="占用数量" size="default"
oninput="value=value.replace(/[^0-9]/g,'')"
@input="onOccupyInput(ci, $event)" />
</el-col>
<el-col :span="8" style="line-height: 32px; font-size: 12px; color: #999;">
<template v-if="batchOccupyLimits[ch.id]?.maxValue !== null">
最大可占: {{ batchOccupyLimits[ch.id].maxValue }}
</template>
</el-col>
</el-row>
<el-row :gutter="10" style="border-top: 1px solid #eee; padding-top: 10px;">
<el-col :span="6" style="line-height: 32px; font-weight: 600;">合计</el-col>
<el-col :span="8" style="line-height: 32px; font-weight: 600;">占用: {{ batchOccupyTotalLocked }}</el-col>
</el-row>
</el-form>
</div>
<template #footer>
<el-button @click="BatchOccupyDialogVisible = false" :disabled="batchOccupyRunning">取消</el-button>
<el-button type="warning" @click="execBatchOccupy()" :disabled="batchOccupyRunning">确定占用</el-button>
</template>
</el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import { import {
ref, ref,
onMounted computed,
onMounted,
onUnmounted
} from 'vue' } from 'vue'
import { import {
PlanModelSave, PlanModelSave,
@ -398,6 +479,10 @@
ResourceGetBindDeviceList, ResourceGetBindDeviceList,
GetPlanModelDetailInfo, GetPlanModelDetailInfo,
PlanModelDel, PlanModelDel,
PlanModelBatchDisable,
PlanModelBatchEnable,
PlanModelBatchChange,
PlanModelBatchOccupy,
GetEnableDeviceList, GetEnableDeviceList,
CreatePlanList, CreatePlanList,
GetServiceDateTime, GetServiceDateTime,
@ -480,12 +565,118 @@
} }
} }
let hoverIndex = ref(null) let hoverIndex = ref(null)
//
let isDragging = ref(false)
let dragStart = ref({ row: 0, col: 0, x: 0, y: 0 })
let dragOverlayStyle = ref({})
let scrollContainer = ref(null)
const showTools = (index) => { const showTools = (index) => {
hoverIndex.value = index hoverIndex.value = index
} }
const hideTools = (index) => { const hideTools = (index) => {
hoverIndex.value = null hoverIndex.value = null
} }
const onCellHover = (event, cellData, rowIdx, colKey) => {
if (isDragging.value) return
showTools(cellData?.id)
}
const onCellLeave = (cellId) => {
if (isDragging.value) return
hideTools(cellId)
}
const onCellMouseDown = (event, cellData, rowIdx, colKey) => {
//
if (event.target.closest('.plan_checkbox')) return
// time_range
if (colKey === 'time_range' || !cellData || !cellData.id) return
const td = event.currentTarget
const tr = td.closest('tr')
if (!tr) return
const container = scrollContainer.value
if (!container) return
const containerRect = container.getBoundingClientRect()
isDragging.value = true
dragStart.value = {
row: tr.rowIndex,
col: td.cellIndex,
x: event.clientX - containerRect.left + container.scrollLeft,
y: event.clientY - containerRect.top + container.scrollTop
}
//
selectedPlanArr.value = [cellData.id]
PlanSelectedAll.value = 0
}
const onDocumentMouseMove = (event) => {
if (!isDragging.value) return
const container = scrollContainer.value
if (!container) return
const containerRect = container.getBoundingClientRect()
const currentX = event.clientX - containerRect.left + container.scrollLeft
const currentY = event.clientY - containerRect.top + container.scrollTop
//
dragOverlayStyle.value = {
left: Math.min(dragStart.value.x, currentX) + 'px',
top: Math.min(dragStart.value.y, currentY) + 'px',
width: Math.abs(currentX - dragStart.value.x) + 'px',
height: Math.abs(currentY - dragStart.value.y) + 'px'
}
// elementFromPoint td
const el = document.elementFromPoint(event.clientX, event.clientY)
if (!el) return
const td = el.closest('td')
if (!td) return
const tr = td.closest('tr')
if (!tr) return
//
if (tr.parentElement?.tagName === 'THEAD') return
const endRow = tr.rowIndex
const endCol = td.cellIndex
if (endCol === 0) return // time_range
const startRow = dragStart.value.row
const startCol = dragStart.value.col
const minRow = Math.min(startRow, endRow)
const maxRow = Math.max(startRow, endRow)
const minCol = Math.min(startCol, endCol)
const maxCol = Math.max(startCol, endCol)
//
selectedPlanArr.value = []
for (let r = minRow; r <= maxRow; r++) {
const dataRow = planTableData.value[r - 1]
if (!dataRow) continue
for (let c = minCol; c <= maxCol; c++) {
if (c === 0) continue
const colKey = xingqi.value[c - 1]?.label
if (!colKey) continue
const cellData = dataRow[colKey]
if (cellData && cellData.id) {
selectedPlanArr.value.push(cellData.id)
}
}
}
}
const onDocumentMouseUp = () => {
if (!isDragging.value) return
isDragging.value = false
dragOverlayStyle.value = {}
}
//
let BatchModifyDialogVisible = ref(false)
let batchModifyRunning = ref(false)
let batchChannels = ref([])
let batchForm = ref([])
let batchPatientType = ref([])
const batchTotalCount = computed(() => {
return batchForm.value.reduce((s, item) => s + (Number(item.count) || 0), 0)
})
//
let BatchOccupyDialogVisible = ref(false)
let batchOccupyRunning = ref(false)
let batchOccupyChannels = ref([])
let batchOccupyForm = ref([])
let batchOccupyLimits = ref({})
const batchOccupyTotalLocked = computed(() => {
return batchOccupyForm.value.reduce((s, item) => s + (Number(item.locked_count) || 0), 0)
})
const isSelected = (id) => { const isSelected = (id) => {
return selectedPlanArr.value.includes(id) return selectedPlanArr.value.includes(id)
} }
@ -497,6 +688,164 @@
selectedPlanArr.value = selectedPlanArr.value.filter(item => item !== id); selectedPlanArr.value = selectedPlanArr.value.filter(item => item !== id);
} }
}; };
//
const BatchDisable = () => {
if (selectedPlanArr.value.length == 0) {
ElMessage.error('请至少勾选1条记录')
return false
}
ElMessageBox.confirm(
'确定批量禁用勾选的计划模板吗?',
'提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
loading.value = true
PlanModelBatchDisable({
ids: selectedPlanArr.value
}).then(res => {
loading.value = false
if (res.status) {
ElMessage({
message: '批量禁用成功',
type: 'success',
})
GetList()
} else {
ElMessage.error(res.msg)
}
})
})
}
//
const BatchEnable = () => {
if (selectedPlanArr.value.length == 0) {
ElMessage.error('请至少勾选1条记录')
return false
}
ElMessageBox.confirm(
'确定批量启用勾选的计划模板吗?',
'提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
loading.value = true
PlanModelBatchEnable({
ids: selectedPlanArr.value
}).then(res => {
loading.value = false
if (res.status) {
ElMessage({
message: '批量启用成功',
type: 'success',
})
GetList()
} else {
ElMessage.error(res.msg)
}
})
})
}
//
const openBatchModify = () => {
if (selectedPlanArr.value.length == 0) {
ElMessage.error('请至少勾选1条记录')
return
}
// planTableData
const selectedIds = new Set(selectedPlanArr.value)
//
const channelMap = new Map() // id -> { name, jiancheng, counts: [] }
const patientTypeSets = [] //
planTableData.value.forEach(row => {
for (const key in row) {
if (key === 'time_range') continue
const cell = row[key]
if (cell && cell.id && selectedIds.has(cell.id) && cell.countsInfo) {
cell.countsInfo.forEach(ci => {
if (!channelMap.has(ci.appointment_type_id)) {
channelMap.set(ci.appointment_type_id, {
name: ci.name,
jiancheng: ci.jiancheng,
counts: []
})
}
channelMap.get(ci.appointment_type_id).counts.push(ci.count)
})
//
if (cell.patient_type) {
patientTypeSets.push(new Set(cell.patient_type))
}
}
}
})
//
batchChannels.value = []
batchForm.value = []
for (const [id, info] of channelMap) {
batchChannels.value.push({ id, name: info.name })
const allSame = info.counts.length > 0 && info.counts.every(v => v === info.counts[0])
batchForm.value.push({
appointment_type_id: id,
count: allSame ? info.counts[0] : ''
})
}
//
if (patientTypeSets.length > 0) {
const commonTypes = patientTypeSets.reduce((acc, set) => {
const result = new Set()
for (const val of acc) {
if (set.has(val)) result.add(val)
}
return result
})
batchPatientType.value = Array.from(commonTypes)
} else {
batchPatientType.value = []
}
batchModifyRunning.value = false
BatchModifyDialogVisible.value = true
}
const execBatchModify = async () => {
//
for (const item of batchForm.value) {
if (Number(item.count) < 0) {
ElMessage.error('数量不能为负数')
return
}
}
const maxTotal = batchTotalCount.value
batchModifyRunning.value = true
const ids = [...selectedPlanArr.value]
const coutsInfo = batchForm.value.map(item => ({
appointment_type_id: item.appointment_type_id,
count: Number(item.count) || 0
}))
try {
const res = await PlanModelBatchChange({
ids: ids,
max_total: maxTotal,
coutsInfo: coutsInfo,
patientType: batchPatientType.value
})
batchModifyRunning.value = false
if (res && res.status) {
BatchModifyDialogVisible.value = false
ElMessage.success('批量修改完成')
GetList()
} else {
ElMessage.error(res?.msg || '批量修改失败')
batchModifyRunning.value = false
}
} catch (e) {
batchModifyRunning.value = false
ElMessage.error('请求异常')
}
}
const CreatedPlanAction = () => { // const CreatedPlanAction = () => { //
if (dateRange.value.length === 0) { if (dateRange.value.length === 0) {
ElMessage.error("请选择日期范围") ElMessage.error("请选择日期范围")
@ -777,6 +1126,90 @@
}) })
}) })
} }
//
const initBatchOccupyForm = () => {
batchOccupyForm.value = batchOccupyChannels.value.map(ch => ({
appointment_type_id: ch.id,
locked_count: ''
}))
}
const onOccupyInput = (ci, val) => {
const ch = batchOccupyChannels.value[ci]
const limit = batchOccupyLimits.value[ch.id]
if (limit?.limited && Number(val) > limit.maxValue) {
batchOccupyForm.value[ci].locked_count = limit.maxValue
}
}
const openBatchOccupy = () => {
if (selectedPlanArr.value.length == 0) {
ElMessage.error('请至少勾选1条记录')
return
}
// planTableData count
const selectedIds = new Set(selectedPlanArr.value)
const channelMap = new Map()
planTableData.value.forEach(row => {
for (const key in row) {
if (key === 'time_range') continue
const cell = row[key]
if (cell && cell.id && selectedIds.has(cell.id) && cell.countsInfo) {
cell.countsInfo.forEach(ci => {
if (!channelMap.has(ci.appointment_type_id)) {
channelMap.set(ci.appointment_type_id, {
name: ci.name,
counts: []
})
}
channelMap.get(ci.appointment_type_id).counts.push(ci.count)
})
}
}
})
// count
batchOccupyChannels.value = []
const limits = {}
for (const [id, info] of channelMap) {
batchOccupyChannels.value.push({ id, name: info.name })
const minVal = Math.min(...info.counts)
limits[id] = { limited: true, maxValue: minVal }
}
batchOccupyLimits.value = limits
initBatchOccupyForm()
batchOccupyRunning.value = false
BatchOccupyDialogVisible.value = true
}
const execBatchOccupy = async () => {
for (const item of batchOccupyForm.value) {
if (Number(item.locked_count) < 0) {
ElMessage.error('占用数量不能为负数')
return
}
}
batchOccupyRunning.value = true
const ids = [...selectedPlanArr.value]
const coutsInfo = batchOccupyForm.value.map(item => ({
appointment_type_id: item.appointment_type_id,
locked_count: Number(item.locked_count) || 0
}))
try {
const res = await PlanModelBatchOccupy({
ids: ids,
coutsInfo: coutsInfo
})
batchOccupyRunning.value = false
if (res && res.status) {
BatchOccupyDialogVisible.value = false
ElMessage.success('批量占用完成')
GetList()
} else {
ElMessage.error(res?.msg || '批量占用失败')
batchOccupyRunning.value = false
}
} catch (e) {
batchOccupyRunning.value = false
ElMessage.error('请求异常')
}
}
// //
let EnableDeviceList = ref([]) let EnableDeviceList = ref([])
const GetEnableDeviceListFunc = () => { const GetEnableDeviceListFunc = () => {
@ -1157,7 +1590,13 @@
getEnableResource() getEnableResource()
GetEnableDeviceListFunc() GetEnableDeviceListFunc()
setMonthRange() setMonthRange()
//
document.addEventListener('mousemove', onDocumentMouseMove)
document.addEventListener('mouseup', onDocumentMouseUp)
})
onUnmounted(() => {
document.removeEventListener('mousemove', onDocumentMouseMove)
document.removeEventListener('mouseup', onDocumentMouseUp)
}) })
</script> </script>
@ -1192,6 +1631,8 @@
border-collapse: collapse; border-collapse: collapse;
/* 合并边框 */ /* 合并边框 */
color: #333; color: #333;
user-select: none;
-webkit-user-select: none;
td { td {
border: 1px solid #f1f1f1; border: 1px solid #f1f1f1;
@ -1371,4 +1812,26 @@
display: flex; display: flex;
} }
/* 拖拽框选 */
.drag-selection-overlay {
position: absolute;
top: 0;
left: 0;
background: rgba(64, 158, 255, 0.12);
border: 1px solid rgba(64, 158, 255, 0.5);
pointer-events: none;
z-index: 10;
}
.planInfo_k_selected {
background-color: #e6f7ff;
outline: 2px solid #409eff;
outline-offset: -2px;
border-radius: 4px;
}
.planInfo_k_disabled_selected {
background-color: #f3e8ff;
outline: 2px solid #7c3aed;
outline-offset: -2px;
border-radius: 4px;
}
</style> </style>

@ -41,6 +41,10 @@
<el-button type="primary" @click="Add()" style="margin-left: 10px;">添加</el-button> <el-button type="primary" @click="Add()" style="margin-left: 10px;">添加</el-button>
<el-button type="danger" @click="Del()" style="margin-left: 10px;">删除</el-button> <el-button type="danger" @click="Del()" style="margin-left: 10px;">删除</el-button>
<el-button type="success" @click="CreatedPlanClick()" style="margin-left: 20px;">生成选中的计划</el-button> <el-button type="success" @click="CreatedPlanClick()" style="margin-left: 20px;">生成选中的计划</el-button>
<el-button type="warning" @click="BatchDisable()" style="margin-left: 10px;">批量禁用</el-button>
<el-button type="success" @click="BatchEnable()" style="margin-left: 10px;">批量启用</el-button>
<el-button type="primary" @click="openBatchModify()" style="margin-left: 10px;">批量修改</el-button>
<el-button type="warning" @click="openBatchOccupy()" style="margin-left: 10px;">批量占用</el-button>
</el-row> </el-row>
</div> </div>
<el-row> <el-row>
@ -60,7 +64,7 @@
{{item.resources_name}} {{item.resources_name}}
</div> </div>
</div> </div>
<div v-loading="loading" style="border: 1px solid #efefef;width: 100%;"> <div ref="scrollContainer" v-loading="loading" style="border: 1px solid #efefef;width: 100%;position: relative;overflow-x: auto;">
<table class="planTable" v-if="planTableData.length>0" > <table class="planTable" v-if="planTableData.length>0" >
<tr style="background-color: #f1f1f1;position: relative;"> <tr style="background-color: #f1f1f1;position: relative;">
<td style="width: 120px;display: flex;font-size: 12px;"> <td style="width: 120px;display: flex;font-size: 12px;">
@ -73,10 +77,14 @@
<td style="font-weight: 700;" v-for="(item,index) in xingqi">{{item.label}}</td> <td style="font-weight: 700;" v-for="(item,index) in xingqi">{{item.label}}</td>
</tr> </tr>
<tr v-for="(item,index) in planTableData" :key="index"> <tr v-for="(item,index) in planTableData" :key="index">
<td v-for="(item2,index2) in item" :key="index2" @mouseover="showTools(item2.id)" <td v-for="(item2,index2) in item" :key="index2"
@mouseleave="hideTools(item2.id)"> :data-row="index" :data-col="index2"
@mouseover="onCellHover($event, item2, index, index2)"
@mouseleave="onCellLeave(item2?.id)"
@mousedown="onCellMouseDown($event, item2, index, index2)">
<span v-if="index2=='time_range'">{{item2}}</span> <span v-if="index2=='time_range'">{{item2}}</span>
<div v-if="item2.countsInfo && item2.countsInfo.length>0" :class="{'planInfo_k':item2.status==1,'planInfo_k planInfo_k_disabled':item2.status==0}"> <div v-if="item2.countsInfo && item2.countsInfo.length>0"
:class="{'planInfo_k':item2.status==1,'planInfo_k planInfo_k_disabled':item2.status==0,'planInfo_k_selected':isSelected(item2.id) && item2.status==1,'planInfo_k_disabled_selected':isSelected(item2.id) && item2.status==0}">
<div v-if="hoverIndex==item2.id" class="hoverTools"> <div v-if="hoverIndex==item2.id" class="hoverTools">
<div class="icon_k" @click="Add(item2)"> <div class="icon_k" @click="Add(item2)">
<el-icon color="#409eff" size="20"> <el-icon color="#409eff" size="20">
@ -116,6 +124,7 @@
</td> </td>
</tr> </tr>
</table> </table>
<div v-if="isDragging" class="drag-selection-overlay" :style="dragOverlayStyle"></div>
<div v-if="planTableData.length==0" style="width: 100%;background-color: #fff;"> <div v-if="planTableData.length==0" style="width: 100%;background-color: #fff;">
<el-empty description="暂无" /> <el-empty description="暂无" />
</div> </div>
@ -379,13 +388,85 @@
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
<!-- 批量修改弹窗 -->
<el-dialog v-model="BatchModifyDialogVisible" title="批量修改" width="45%" :close-on-click-modal="false">
<div>
<div style="margin-bottom: 16px; color: #666;">已选 {{ selectedPlanArr.length }} 条号源明细</div>
<el-form label-width="100px">
<el-row :gutter="10" style="margin-bottom: 6px; font-weight: 600; color: #333; font-size: 13px;">
<el-col :span="6">渠道名称</el-col>
<el-col :span="8">数量</el-col>
</el-row>
<el-row v-for="(ch, ci) in batchChannels" :key="ci" :gutter="10" style="margin-bottom: 10px;">
<el-col :span="6" style="line-height: 32px;">{{ ch.name }}</el-col>
<el-col :span="8">
<el-input v-model.number="batchForm[ci].count" placeholder="数量" size="default"
oninput="value=value.replace(/[^0-9]/g,'')" />
</el-col>
</el-row>
<el-row :gutter="10" style="border-top: 1px solid #eee; padding-top: 10px;">
<el-col :span="6" style="line-height: 32px; font-weight: 600;">合计</el-col>
<el-col :span="8" style="line-height: 32px; font-weight: 600;">数量: {{ batchTotalCount }}</el-col>
</el-row>
</el-form>
<div style="margin-top: 16px; padding-top: 12px; border-top: 1px solid #eee;">
<div style="margin-bottom: 8px; font-weight: 600; color: #333; font-size: 13px;">病人类型</div>
<el-checkbox-group v-model="batchPatientType">
<el-checkbox label="0">住院</el-checkbox>
<el-checkbox label="1">门诊</el-checkbox>
<el-checkbox label="2">急诊</el-checkbox>
<el-checkbox label="3">体检</el-checkbox>
</el-checkbox-group>
</div>
</div>
<template #footer>
<el-button @click="BatchModifyDialogVisible = false" :disabled="batchModifyRunning">取消</el-button>
<el-button type="primary" @click="execBatchModify()" :disabled="batchModifyRunning">确定修改</el-button>
</template>
</el-dialog>
<!-- 批量占用弹窗 -->
<el-dialog v-model="BatchOccupyDialogVisible" title="批量占用" width="45%" :close-on-click-modal="false">
<div>
<div style="margin-bottom: 16px; color: #666;">已选 {{ selectedPlanArr.length }} 条号源明细</div>
<el-form label-width="100px">
<el-row :gutter="10" style="margin-bottom: 6px; font-weight: 600; color: #333; font-size: 13px;">
<el-col :span="6">渠道名称</el-col>
<el-col :span="8">占用数量</el-col>
<el-col :span="8">提示</el-col>
</el-row>
<el-row v-for="(ch, ci) in batchOccupyChannels" :key="ci" :gutter="10" style="margin-bottom: 10px;">
<el-col :span="6" style="line-height: 32px;">{{ ch.name }}</el-col>
<el-col :span="8">
<el-input v-model.number="batchOccupyForm[ci].locked_count" placeholder="占用数量" size="default"
oninput="value=value.replace(/[^0-9]/g,'')"
@input="onOccupyInput(ci, $event)" />
</el-col>
<el-col :span="8" style="line-height: 32px; font-size: 12px; color: #999;">
<template v-if="batchOccupyLimits[ch.id]?.maxValue !== null">
最大可占: {{ batchOccupyLimits[ch.id].maxValue }}
</template>
</el-col>
</el-row>
<el-row :gutter="10" style="border-top: 1px solid #eee; padding-top: 10px;">
<el-col :span="6" style="line-height: 32px; font-weight: 600;">合计</el-col>
<el-col :span="8" style="line-height: 32px; font-weight: 600;">占用: {{ batchOccupyTotalLocked }}</el-col>
</el-row>
</el-form>
</div>
<template #footer>
<el-button @click="BatchOccupyDialogVisible = false" :disabled="batchOccupyRunning">取消</el-button>
<el-button type="warning" @click="execBatchOccupy()" :disabled="batchOccupyRunning">确定占用</el-button>
</template>
</el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import { import {
ref, ref,
onMounted computed,
onMounted,
onUnmounted
} from 'vue' } from 'vue'
import { import {
PlanModelSaveAdmin, PlanModelSaveAdmin,
@ -398,6 +479,10 @@
ResourceGetBindDeviceList, ResourceGetBindDeviceList,
GetPlanModelDetailInfo, GetPlanModelDetailInfo,
PlanModelDelAdmin, PlanModelDelAdmin,
PlanModelBatchDisableAdmin,
PlanModelBatchEnableAdmin,
PlanModelBatchChangeAdmin,
PlanModelBatchOccupyAdmin,
GetEnableDeviceList, GetEnableDeviceList,
CreatePlanListAdmin, CreatePlanListAdmin,
GetServiceDateTime, GetServiceDateTime,
@ -480,12 +565,118 @@
} }
} }
let hoverIndex = ref(null) let hoverIndex = ref(null)
//
let isDragging = ref(false)
let dragStart = ref({ row: 0, col: 0, x: 0, y: 0 })
let dragOverlayStyle = ref({})
let scrollContainer = ref(null)
const showTools = (index) => { const showTools = (index) => {
hoverIndex.value = index hoverIndex.value = index
} }
const hideTools = (index) => { const hideTools = (index) => {
hoverIndex.value = null hoverIndex.value = null
} }
const onCellHover = (event, cellData, rowIdx, colKey) => {
if (isDragging.value) return
showTools(cellData?.id)
}
const onCellLeave = (cellId) => {
if (isDragging.value) return
hideTools(cellId)
}
const onCellMouseDown = (event, cellData, rowIdx, colKey) => {
//
if (event.target.closest('.plan_checkbox')) return
// time_range
if (colKey === 'time_range' || !cellData || !cellData.id) return
const td = event.currentTarget
const tr = td.closest('tr')
if (!tr) return
const container = scrollContainer.value
if (!container) return
const containerRect = container.getBoundingClientRect()
isDragging.value = true
dragStart.value = {
row: tr.rowIndex,
col: td.cellIndex,
x: event.clientX - containerRect.left + container.scrollLeft,
y: event.clientY - containerRect.top + container.scrollTop
}
//
selectedPlanArr.value = [cellData.id]
PlanSelectedAll.value = 0
}
const onDocumentMouseMove = (event) => {
if (!isDragging.value) return
const container = scrollContainer.value
if (!container) return
const containerRect = container.getBoundingClientRect()
const currentX = event.clientX - containerRect.left + container.scrollLeft
const currentY = event.clientY - containerRect.top + container.scrollTop
//
dragOverlayStyle.value = {
left: Math.min(dragStart.value.x, currentX) + 'px',
top: Math.min(dragStart.value.y, currentY) + 'px',
width: Math.abs(currentX - dragStart.value.x) + 'px',
height: Math.abs(currentY - dragStart.value.y) + 'px'
}
// elementFromPoint td
const el = document.elementFromPoint(event.clientX, event.clientY)
if (!el) return
const td = el.closest('td')
if (!td) return
const tr = td.closest('tr')
if (!tr) return
//
if (tr.parentElement?.tagName === 'THEAD') return
const endRow = tr.rowIndex
const endCol = td.cellIndex
if (endCol === 0) return // time_range
const startRow = dragStart.value.row
const startCol = dragStart.value.col
const minRow = Math.min(startRow, endRow)
const maxRow = Math.max(startRow, endRow)
const minCol = Math.min(startCol, endCol)
const maxCol = Math.max(startCol, endCol)
//
selectedPlanArr.value = []
for (let r = minRow; r <= maxRow; r++) {
const dataRow = planTableData.value[r - 1]
if (!dataRow) continue
for (let c = minCol; c <= maxCol; c++) {
if (c === 0) continue
const colKey = xingqi.value[c - 1]?.label
if (!colKey) continue
const cellData = dataRow[colKey]
if (cellData && cellData.id) {
selectedPlanArr.value.push(cellData.id)
}
}
}
}
const onDocumentMouseUp = () => {
if (!isDragging.value) return
isDragging.value = false
dragOverlayStyle.value = {}
}
//
let BatchModifyDialogVisible = ref(false)
let batchModifyRunning = ref(false)
let batchChannels = ref([])
let batchForm = ref([])
let batchPatientType = ref([])
const batchTotalCount = computed(() => {
return batchForm.value.reduce((s, item) => s + (Number(item.count) || 0), 0)
})
//
let BatchOccupyDialogVisible = ref(false)
let batchOccupyRunning = ref(false)
let batchOccupyChannels = ref([])
let batchOccupyForm = ref([])
let batchOccupyLimits = ref({})
const batchOccupyTotalLocked = computed(() => {
return batchOccupyForm.value.reduce((s, item) => s + (Number(item.locked_count) || 0), 0)
})
const isSelected = (id) => { const isSelected = (id) => {
return selectedPlanArr.value.includes(id) return selectedPlanArr.value.includes(id)
} }
@ -497,6 +688,164 @@
selectedPlanArr.value = selectedPlanArr.value.filter(item => item !== id); selectedPlanArr.value = selectedPlanArr.value.filter(item => item !== id);
} }
}; };
//
const BatchDisable = () => {
if (selectedPlanArr.value.length == 0) {
ElMessage.error('请至少勾选1条记录')
return false
}
ElMessageBox.confirm(
'确定批量禁用勾选的计划模板吗?',
'提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
loading.value = true
PlanModelBatchDisableAdmin({
ids: selectedPlanArr.value
}).then(res => {
loading.value = false
if (res.status) {
ElMessage({
message: '批量禁用成功',
type: 'success',
})
GetList()
} else {
ElMessage.error(res.msg)
}
})
})
}
//
const BatchEnable = () => {
if (selectedPlanArr.value.length == 0) {
ElMessage.error('请至少勾选1条记录')
return false
}
ElMessageBox.confirm(
'确定批量启用勾选的计划模板吗?',
'提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
loading.value = true
PlanModelBatchEnableAdmin({
ids: selectedPlanArr.value
}).then(res => {
loading.value = false
if (res.status) {
ElMessage({
message: '批量启用成功',
type: 'success',
})
GetList()
} else {
ElMessage.error(res.msg)
}
})
})
}
//
const openBatchModify = () => {
if (selectedPlanArr.value.length == 0) {
ElMessage.error('请至少勾选1条记录')
return
}
// planTableData
const selectedIds = new Set(selectedPlanArr.value)
//
const channelMap = new Map()
const patientTypeSets = []
planTableData.value.forEach(row => {
for (const key in row) {
if (key === 'time_range') continue
const cell = row[key]
if (cell && cell.id && selectedIds.has(cell.id) && cell.countsInfo) {
cell.countsInfo.forEach(ci => {
if (!channelMap.has(ci.appointment_type_id)) {
channelMap.set(ci.appointment_type_id, {
name: ci.name,
jiancheng: ci.jiancheng,
counts: []
})
}
channelMap.get(ci.appointment_type_id).counts.push(ci.count)
})
//
if (cell.patient_type) {
patientTypeSets.push(new Set(cell.patient_type))
}
}
}
})
//
batchChannels.value = []
batchForm.value = []
for (const [id, info] of channelMap) {
batchChannels.value.push({ id, name: info.name })
const allSame = info.counts.length > 0 && info.counts.every(v => v === info.counts[0])
batchForm.value.push({
appointment_type_id: id,
count: allSame ? info.counts[0] : ''
})
}
//
if (patientTypeSets.length > 0) {
const commonTypes = patientTypeSets.reduce((acc, set) => {
const result = new Set()
for (const val of acc) {
if (set.has(val)) result.add(val)
}
return result
})
batchPatientType.value = Array.from(commonTypes)
} else {
batchPatientType.value = []
}
batchModifyRunning.value = false
BatchModifyDialogVisible.value = true
}
const execBatchModify = async () => {
//
for (const item of batchForm.value) {
if (Number(item.count) < 0) {
ElMessage.error('数量不能为负数')
return
}
}
const maxTotal = batchTotalCount.value
batchModifyRunning.value = true
const ids = [...selectedPlanArr.value]
const coutsInfo = batchForm.value.map(item => ({
appointment_type_id: item.appointment_type_id,
count: Number(item.count) || 0
}))
try {
const res = await PlanModelBatchChangeAdmin({
ids: ids,
max_total: maxTotal,
coutsInfo: coutsInfo,
patientType: batchPatientType.value
})
batchModifyRunning.value = false
if (res && res.status) {
BatchModifyDialogVisible.value = false
ElMessage.success('批量修改完成')
GetList()
} else {
ElMessage.error(res?.msg || '批量修改失败')
batchModifyRunning.value = false
}
} catch (e) {
batchModifyRunning.value = false
ElMessage.error('请求异常')
}
}
const CreatedPlanAction = () => { // const CreatedPlanAction = () => { //
if (dateRange.value.length === 0) { if (dateRange.value.length === 0) {
ElMessage.error("请选择日期范围") ElMessage.error("请选择日期范围")
@ -780,6 +1129,90 @@
}) })
}) })
} }
//
const initBatchOccupyForm = () => {
batchOccupyForm.value = batchOccupyChannels.value.map(ch => ({
appointment_type_id: ch.id,
locked_count: ''
}))
}
const onOccupyInput = (ci, val) => {
const ch = batchOccupyChannels.value[ci]
const limit = batchOccupyLimits.value[ch.id]
if (limit?.limited && Number(val) > limit.maxValue) {
batchOccupyForm.value[ci].locked_count = limit.maxValue
}
}
const openBatchOccupy = () => {
if (selectedPlanArr.value.length == 0) {
ElMessage.error('请至少勾选1条记录')
return
}
// planTableData count
const selectedIds = new Set(selectedPlanArr.value)
const channelMap = new Map()
planTableData.value.forEach(row => {
for (const key in row) {
if (key === 'time_range') continue
const cell = row[key]
if (cell && cell.id && selectedIds.has(cell.id) && cell.countsInfo) {
cell.countsInfo.forEach(ci => {
if (!channelMap.has(ci.appointment_type_id)) {
channelMap.set(ci.appointment_type_id, {
name: ci.name,
counts: []
})
}
channelMap.get(ci.appointment_type_id).counts.push(ci.count)
})
}
}
})
// count
batchOccupyChannels.value = []
const limits = {}
for (const [id, info] of channelMap) {
batchOccupyChannels.value.push({ id, name: info.name })
const minVal = Math.min(...info.counts)
limits[id] = { limited: true, maxValue: minVal }
}
batchOccupyLimits.value = limits
initBatchOccupyForm()
batchOccupyRunning.value = false
BatchOccupyDialogVisible.value = true
}
const execBatchOccupy = async () => {
for (const item of batchOccupyForm.value) {
if (Number(item.locked_count) < 0) {
ElMessage.error('占用数量不能为负数')
return
}
}
batchOccupyRunning.value = true
const ids = [...selectedPlanArr.value]
const coutsInfo = batchOccupyForm.value.map(item => ({
appointment_type_id: item.appointment_type_id,
locked_count: Number(item.locked_count) || 0
}))
try {
const res = await PlanModelBatchOccupyAdmin({
ids: ids,
coutsInfo: coutsInfo
})
batchOccupyRunning.value = false
if (res && res.status) {
BatchOccupyDialogVisible.value = false
ElMessage.success('批量占用完成')
GetList()
} else {
ElMessage.error(res?.msg || '批量占用失败')
batchOccupyRunning.value = false
}
} catch (e) {
batchOccupyRunning.value = false
ElMessage.error('请求异常')
}
}
// //
let EnableDeviceList = ref([]) let EnableDeviceList = ref([])
const GetEnableDeviceListFunc = () => { const GetEnableDeviceListFunc = () => {
@ -1171,7 +1604,13 @@
GetDepartmentEnableList() GetDepartmentEnableList()
GetEnableDeviceListFunc() GetEnableDeviceListFunc()
setMonthRange() setMonthRange()
//
document.addEventListener('mousemove', onDocumentMouseMove)
document.addEventListener('mouseup', onDocumentMouseUp)
})
onUnmounted(() => {
document.removeEventListener('mousemove', onDocumentMouseMove)
document.removeEventListener('mouseup', onDocumentMouseUp)
}) })
</script> </script>
@ -1206,6 +1645,8 @@
border-collapse: collapse; border-collapse: collapse;
/* 合并边框 */ /* 合并边框 */
color: #333; color: #333;
user-select: none;
-webkit-user-select: none;
td { td {
border: 1px solid #f1f1f1; border: 1px solid #f1f1f1;
@ -1386,4 +1827,26 @@
display: flex; display: flex;
} }
/* 拖拽框选 */
.drag-selection-overlay {
position: absolute;
top: 0;
left: 0;
background: rgba(64, 158, 255, 0.12);
border: 1px solid rgba(64, 158, 255, 0.5);
pointer-events: none;
z-index: 10;
}
.planInfo_k_selected {
background-color: #e6f7ff;
outline: 2px solid #409eff;
outline-offset: -2px;
border-radius: 4px;
}
.planInfo_k_disabled_selected {
background-color: #f3e8ff;
outline: 2px solid #7c3aed;
outline-offset: -2px;
border-radius: 4px;
}
</style> </style>
Loading…
Cancel
Save