2179 lines
69 KiB
Vue
2179 lines
69 KiB
Vue
<template>
|
||
<div class="appoinmentmanage-wrapper">
|
||
<div class="appoinmentmanage-container">
|
||
<div class="appoinmentmanage-header">
|
||
<h2 class="appoinmentmanage-title">科室排班管理</h2>
|
||
</div>
|
||
|
||
<div class="appoinmentmanage-content">
|
||
<!-- 查询条件 -->
|
||
<div class="query-condition">
|
||
<el-select v-model="queryParams.orgName" placeholder="全部机构" class="query-select" clearable @change="handleOrgChange">
|
||
<el-option label="全部机构" value=""></el-option>
|
||
<el-option v-for="org in organizationOptions" :key="org.id || org.code" :label="org.name || org.orgName" :value="org.name || org.orgName"></el-option>
|
||
</el-select>
|
||
|
||
<el-select v-model="queryParams.deptName" placeholder="全部科室" class="query-select">
|
||
<el-option label="全部科室" value=""></el-option>
|
||
<el-option v-for="dept in departmentOptions" :key="dept.id || dept.code" :label="dept.name || dept.deptName" :value="dept.name || dept.deptName"></el-option>
|
||
</el-select>
|
||
|
||
<el-button type="primary" @click="handleQuery" class="query-button">查询</el-button>
|
||
<el-button @click="handleReset" class="reset-button">重置</el-button>
|
||
<el-button type="success" @click="handleAppointmentSetting" class="appointment-setting-button">预约设置</el-button>
|
||
</div>
|
||
|
||
<!-- 科室列表 -->
|
||
<div class="dept-table-container">
|
||
<el-table :data="deptList" border style="width: 100%" class="centered-table">
|
||
<!-- 添加空状态提示 -->
|
||
<template #empty>
|
||
<div style="padding: 20px 0;">
|
||
<el-icon><DocumentRemove /></el-icon>
|
||
<span style="margin-left: 8px;">暂无数据</span>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- 适配常见的后端字段名 -->
|
||
<el-table-column prop="id" label="ID" width="150"></el-table-column>
|
||
<el-table-column prop="orgName" label="卫生机构" width="350">
|
||
<template #default="scope">
|
||
{{ scope.row.orgName || scope.row.organizationName || scope.row.org || '' }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="deptName" label="科室名称" width="350">
|
||
<template #default="scope">
|
||
{{ scope.row.deptName || scope.row.departmentName || scope.row.name || '' }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="remark" label="备注" width="400">
|
||
<template #default="scope">
|
||
{{ scope.row.remark || scope.row.description || scope.row.note || '' }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="status" label="作废标志">
|
||
<template #default="scope">
|
||
<el-tag :type="(scope.row.status === 1 || scope.row.status === true || scope.row.status === '1') ? 'success' : 'danger'">
|
||
{{ (scope.row.status === 1 || scope.row.status === true || scope.row.status === '1') ? '有效' : '无效' }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="350" fixed="right">
|
||
<template #default="scope">
|
||
<el-button type="primary" size="small" @click="handleEdit(scope.row)">
|
||
<el-icon><EditPen /></el-icon>
|
||
</el-button>
|
||
<el-button type="info" size="small" @click="handleView(scope.row)">
|
||
<el-icon><View /></el-icon>
|
||
</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<!-- 分页 -->
|
||
<div class="pagination-container">
|
||
<el-pagination
|
||
v-model:current-page="pagination.currentPage"
|
||
v-model:page-size="pagination.pageSize"
|
||
:page-sizes="[10, 20, 30, 50]"
|
||
layout="total, sizes, prev, pager, next, jumper"
|
||
:total="pagination.total"
|
||
@size-change="handleSizeChange"
|
||
@current-change="handleCurrentChange"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 预约设置弹窗 -->
|
||
<el-dialog
|
||
v-model="appointmentSettingDialog"
|
||
title="预约设置"
|
||
width="400px"
|
||
center
|
||
:close-on-click-modal="false"
|
||
:close-on-press-escape="false"
|
||
top="50%"
|
||
:before-close="handleAppointmentSettingCancel"
|
||
>
|
||
<el-form label-position="top" :model="appointmentSettingForm">
|
||
<el-form-item label="取消预约时间类型">
|
||
<el-select v-model="appointmentSettingForm.cancelAppointmentType" placeholder="请选择" style="width: 200px">
|
||
<el-option label="年" value="YEAR"></el-option>
|
||
<el-option label="月" value="MONTH"></el-option>
|
||
<el-option label="日" value="DAY"></el-option>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="取消预约次数">
|
||
<el-input-number
|
||
v-model="appointmentSettingForm.cancelAppointmentCount"
|
||
:min="0"
|
||
:step="1"
|
||
placeholder="请输入"
|
||
></el-input-number>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button @click="handleAppointmentSettingCancel">取消</el-button>
|
||
<el-button type="primary" @click="handleAppointmentSettingConfirm">确定</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 医生排班弹窗 -->
|
||
<el-dialog
|
||
v-model="scheduleDialogVisible"
|
||
:title="scheduleDialogTitle"
|
||
width="90%"
|
||
:close-on-click-modal="false"
|
||
top="20px"
|
||
fullscreen
|
||
>
|
||
<div class="doctorschedule-content">
|
||
<!-- 筛选条件 -->
|
||
<div class="filter-condition">
|
||
<span class="filter-label">卫生机构</span>
|
||
<el-input v-model="displayedInstitutionName" class="filter-select" disabled></el-input>
|
||
|
||
<span class="filter-label">科室名称</span>
|
||
<el-select v-model="filterParams.deptName" class="filter-select" disabled>
|
||
<el-option :label="filterParams.deptName" :value="filterParams.deptName"></el-option>
|
||
</el-select>
|
||
|
||
<span class="filter-label">开始日期</span>
|
||
<el-date-picker
|
||
v-model="filterParams.startDate"
|
||
type="date"
|
||
placeholder="选择日期"
|
||
class="filter-date-picker"
|
||
/>
|
||
|
||
<span class="filter-label">排班类型</span>
|
||
<div class="radio-group">
|
||
<el-radio v-model="filterParams.appointmentType" label="普通" @change="handleAppointmentTypeChange">普通</el-radio>
|
||
<el-radio v-model="filterParams.appointmentType" label="专家" @change="handleAppointmentTypeChange">专家</el-radio>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 排班表格 -->
|
||
<div class="schedule-table-container">
|
||
<!-- 按日期分组显示排班 -->
|
||
<div v-for="(dateGroup, index) in groupedSchedule" :key="index" class="daily-schedule">
|
||
<div class="daily-header">
|
||
<span class="date-text">{{ dateGroup.date }}</span>
|
||
<span class="weekday-text">{{ dateGroup.weekday }}</span>
|
||
</div>
|
||
<el-table :data="dateGroup.items" border style="width: 100%" class="schedule-table">
|
||
<el-table-column label="时段" width="100">
|
||
<template #default="scope">
|
||
<el-select
|
||
v-model="scope.row.timeSlot"
|
||
placeholder="请选择"
|
||
:disabled="!isEditMode"
|
||
@change="(val) => handleTimeSlotChange(val, scope.row)"
|
||
>
|
||
<el-option label="上午" value="上午"></el-option>
|
||
<el-option label="下午" value="下午"></el-option>
|
||
</el-select>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="doctorName" :label="filterParams.appointmentType === '专家' ? '专家' : '医生'" width="150">
|
||
<template #default="scope">
|
||
<el-select
|
||
v-model="scope.row.doctorId"
|
||
placeholder="请选"
|
||
class="inline-select"
|
||
:disabled="!isEditMode"
|
||
:key="`doctor-${filterParams.appointmentType}-${scope.row.id}`"
|
||
@change="(selectedId) => handleDoctorChange(selectedId, scope.row)"
|
||
>
|
||
<el-option
|
||
v-for="doctor in getDoctorOptions(filterParams.appointmentType)"
|
||
:key="doctor.id"
|
||
:label="doctor.label"
|
||
:value="String(doctor.id)"
|
||
></el-option>
|
||
</el-select>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="room" label="诊室" width="100">
|
||
<template #default="scope">
|
||
<el-select
|
||
v-model="scope.row.room"
|
||
placeholder="请选择"
|
||
filterable
|
||
:remote-method="searchClinicRooms"
|
||
class="inline-select"
|
||
:disabled="!isEditMode"
|
||
>
|
||
<el-option
|
||
v-for="room in filteredClinicRoomOptions"
|
||
:key="room.id"
|
||
:label="room.label"
|
||
:value="room.value"
|
||
></el-option>
|
||
</el-select>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="startTime" label="开始时间" width="120">
|
||
<template #default="scope">
|
||
<el-time-picker
|
||
v-model="scope.row.startTime"
|
||
type="time"
|
||
format="HH:mm"
|
||
value-format="HH:mm"
|
||
placeholder="选择开始时间"
|
||
:disabled="!isEditMode"
|
||
class="time-picker"
|
||
@change="(val) => handleStartTimeChange(val, scope.row)"
|
||
/>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="endTime" label="结束时间" width="120">
|
||
<template #default="scope">
|
||
<el-time-picker
|
||
v-model="scope.row.endTime"
|
||
type="time"
|
||
format="HH:mm"
|
||
value-format="HH:mm"
|
||
placeholder="选择结束时间"
|
||
:disabled="!isEditMode"
|
||
class="time-picker"
|
||
/>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="maxNumber" label="限号数量" width="80">
|
||
<template #default="scope">
|
||
<el-input
|
||
v-model="scope.row.maxNumber"
|
||
type="number"
|
||
:disabled="!isEditMode"
|
||
/>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="record" label="号源记录" width="80">
|
||
<template #default="scope">
|
||
<el-icon
|
||
@click="handleViewRecord(scope.row)"
|
||
class="record-icon"
|
||
title="查看号源记录"
|
||
>
|
||
<View />
|
||
</el-icon>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="appointmentItem" label="挂号项目" width="120">
|
||
<template #default="scope">
|
||
<el-select
|
||
v-model="scope.row.appointmentItem"
|
||
placeholder="请选"
|
||
class="inline-select"
|
||
:disabled="!isEditMode"
|
||
@change="handleAppointmentItemChange(scope.row)"
|
||
>
|
||
<el-option
|
||
v-for="item in registrationItemOptions"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value"
|
||
></el-option>
|
||
</el-select>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="registrationFee" label="挂号费(元)" width="100">
|
||
<template #default="scope">
|
||
<span>{{ scope.row.registrationFee || '0' }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="clinicItem" label="诊查项目" width="150">
|
||
<template #default="scope">
|
||
<el-select
|
||
v-model="scope.row.clinicItem"
|
||
placeholder="请选择诊查项目"
|
||
class="inline-select"
|
||
:disabled="!isEditMode"
|
||
@change="handleClinicItemChange(scope.row)"
|
||
>
|
||
<el-option
|
||
v-for="item in clinicItemOptions"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value"
|
||
></el-option>
|
||
</el-select>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="treatmentFee" label="诊疗费(元)" width="100">
|
||
<template #default="scope">
|
||
<span>{{ scope.row.treatmentFee || '0' }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="online" label="线上" width="60">
|
||
<template #default="scope">
|
||
<el-checkbox v-model="scope.row.online" :disabled="!isEditMode"></el-checkbox>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="stopClinic" label="停诊" width="60">
|
||
<template #default="scope">
|
||
<el-checkbox v-model="scope.row.stopClinic" :disabled="!isEditMode"></el-checkbox>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="stopReason" label="停诊原因" width="150">
|
||
<template #default="scope">
|
||
<el-input
|
||
v-model="scope.row.stopReason"
|
||
:placeholder="scope.row.stopClinic ? '请输入停诊原因' : ''"
|
||
class="inline-input"
|
||
:disabled="!isEditMode || !scope.row.stopClinic"
|
||
></el-input>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="150" fixed="right">
|
||
<template #default="scope">
|
||
<el-button
|
||
type="primary"
|
||
size="small"
|
||
@click="handleAddSchedule(scope.row)"
|
||
:disabled="!isEditMode"
|
||
>
|
||
添加
|
||
</el-button>
|
||
<el-button
|
||
type="danger"
|
||
size="small"
|
||
@click="handleDeleteSchedule(scope.row)"
|
||
:disabled="!isEditMode || isLastDraftRowInSlot(scope.row)"
|
||
>
|
||
删除
|
||
</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 底部操作按钮 -->
|
||
<div class="bottom-buttons">
|
||
<el-button type="primary" @click="handleSave" :disabled="!isEditMode">确定</el-button>
|
||
<el-button @click="handleCancel">取消</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 号源记录对话框 -->
|
||
<el-dialog
|
||
v-model="recordDialogVisible"
|
||
title="号源记录"
|
||
width="30%"
|
||
:close-on-click-modal="true"
|
||
>
|
||
<div class="appointment-records">
|
||
<div class="record-item" v-for="record in appointmentRecords" :key="record.index">
|
||
<span class="record-time">{{ record.time }}</span>
|
||
</div>
|
||
</div>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button @click="recordDialogVisible = false">确定</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup name="DeptManage">
|
||
import {computed, onMounted, ref, watch} from 'vue'
|
||
import { getDeptAppthoursList } from '@/api/appoinmentmanage/deptappthoursManage'
|
||
import {useRouter} from 'vue-router'
|
||
import {ElDialog, ElForm, ElFormItem, ElInput, ElMessage, ElMessageBox, ElOption, ElSelect} from 'element-plus'
|
||
import {DocumentRemove, EditPen, View, Delete} from '@element-plus/icons-vue'
|
||
import {listDept, searchDept} from '@/api/appoinmentmanage/dept'
|
||
import {getAppointmentConfig, saveAppointmentConfig} from '@/api/appoinmentmanage/appointmentConfig'
|
||
import {getLocationTree, getPractitionerMetadata, getHealthcareMetadata} from '@/views/charge/outpatientregistration/components/outpatientregistration'
|
||
import {addDoctorSchedule, addDoctorScheduleWithDate, updateDoctorSchedule, deleteDoctorSchedule, getRegisterOrganizations, getDoctorScheduleListByDeptId, getDoctorScheduleListByDeptIdAndDateRange} from './api'
|
||
import {getClinicRoomList} from '@/api/appoinmentmanage/clinicRoom'
|
||
import {getInitOption, getRegistrationItems, getClinicItems} from '@/views/basicservices/registrationfee/components/registrationfee'
|
||
import { deptTreeSelect } from '@/api/system/user'
|
||
|
||
// 查询参数
|
||
const queryParams = ref({
|
||
orgName: '',
|
||
deptName: ''
|
||
})
|
||
|
||
// 当前视图
|
||
const currentView = ref('list')
|
||
|
||
// 机构选项列表
|
||
const organizationOptions = ref([])
|
||
|
||
// 科室选项列表
|
||
const departmentOptions = ref([])
|
||
|
||
// 科室列表
|
||
const deptList = ref([])
|
||
|
||
|
||
// 分页参数
|
||
const pagination = ref({
|
||
currentPage: 1,
|
||
pageSize: 10,
|
||
total: 0
|
||
})
|
||
|
||
// 预约设置弹窗
|
||
const appointmentSettingDialog = ref(false)
|
||
|
||
// 预约设置表单数据
|
||
const appointmentSettingForm = ref({
|
||
cancelAppointmentType: '年',
|
||
cancelAppointmentCount: 0
|
||
})
|
||
|
||
// 医生排班相关数据
|
||
// 排班弹窗可见性
|
||
const scheduleDialogVisible = ref(false)
|
||
// 排班弹窗标题
|
||
const scheduleDialogTitle = ref('')
|
||
// 当前操作模式(edit/view)
|
||
const currentMode = ref('edit')
|
||
// 当前选中的科室
|
||
const currentDept = ref(null)
|
||
|
||
// 筛选参数
|
||
const filterParams = ref({
|
||
orgName: '',
|
||
deptName: '测试内科',
|
||
clinicRoomName: '', // 添加诊室名称过滤字段
|
||
startDate: new Date(),
|
||
appointmentType: '普通'
|
||
})
|
||
|
||
// 医生列表数据 - 初始化为空,将在获取科室信息后填充
|
||
const doctorOptions = ref({
|
||
'普通': [],
|
||
'专家': []
|
||
})
|
||
|
||
// 诊室选项
|
||
const clinicRoomOptions = ref([])
|
||
|
||
// 挂号项目选项
|
||
const registrationItemOptions = ref([])
|
||
|
||
// 诊查项目选项
|
||
const clinicItemOptions = ref([])
|
||
|
||
// 科室工作时间配置
|
||
const workTimeConfig = ref(null)
|
||
|
||
// 显示的机构名称
|
||
const displayedInstitutionName = ref('');
|
||
|
||
|
||
// 获取医生列表
|
||
const fetchDoctorList = async (orgId) => {
|
||
try {
|
||
console.log('正在获取科室ID为:', orgId, '的医生列表')
|
||
|
||
// 重要:不要将ID转换为数字,以避免精度丢失
|
||
// 直接使用原始ID值(字符串或数字)
|
||
if (!orgId) {
|
||
console.error('无效的科室ID:', orgId)
|
||
doctorOptions.value = {
|
||
'普通': [],
|
||
'专家': []
|
||
}
|
||
return
|
||
}
|
||
|
||
// 后端查询需要orgId和roleCode两个参数
|
||
// 根据后端代码分析,roleCode通常为'doctor'
|
||
// 使用原始ID值,避免精度丢失
|
||
const response = await getPractitionerMetadata({
|
||
orgId: orgId, // 直接使用原始ID值,不进行类型转换
|
||
roleCode: 'doctor' // 添加roleCode参数,确保查询的是医生角色
|
||
})
|
||
console.log('获取医生列表响应:', response)
|
||
|
||
if (response.code === 200) {
|
||
// 适配不同的后端数据结构
|
||
let actualData = response.data
|
||
if (actualData && actualData.code === 200 && actualData.msg) {
|
||
actualData = actualData.data
|
||
}
|
||
|
||
// 确保数据是数组格式
|
||
let doctorList = []
|
||
if (Array.isArray(actualData)) {
|
||
doctorList = actualData
|
||
} else if (actualData && actualData.records) {
|
||
doctorList = actualData.records
|
||
} else if (actualData && actualData.content) {
|
||
doctorList = actualData.content
|
||
} else if (actualData && actualData.list) {
|
||
doctorList = actualData.list
|
||
}
|
||
|
||
console.log('原始医生数据:', doctorList)
|
||
|
||
// 医生下拉统一展示:普通/专家都显示同一批医生
|
||
// 排班回显仍按 schedule.regType 过滤,不受这里影响
|
||
const allDoctors = []
|
||
|
||
doctorList.forEach(doctor => {
|
||
// 不再单独检查医生的org_id是否与当前科室匹配
|
||
// 因为我们已经通过orgId参数向后端请求特定科室的医生
|
||
// 直接添加医生到列表中
|
||
const doctorInfo = {
|
||
label: doctor.name || doctor.practitionerName,
|
||
value: doctor.name || doctor.practitionerName,
|
||
id: String(doctor.id),
|
||
orgId: doctor.orgId
|
||
}
|
||
|
||
allDoctors.push(doctorInfo)
|
||
})
|
||
|
||
const uniqueDoctors = Array.from(
|
||
new Map(allDoctors.map(doc => [doc.id, doc])).values()
|
||
)
|
||
|
||
// 更新医生选项
|
||
doctorOptions.value['普通'] = uniqueDoctors
|
||
doctorOptions.value['专家'] = uniqueDoctors
|
||
|
||
} else {
|
||
|
||
doctorOptions.value = {
|
||
'普通': [],
|
||
'专家': []
|
||
}
|
||
}
|
||
} catch (error) {
|
||
|
||
doctorOptions.value = {
|
||
'普通': [],
|
||
'专家': []
|
||
}
|
||
}
|
||
}
|
||
|
||
// 获取指定科室下的诊室列表
|
||
const fetchClinicRoomsByDept = async (deptName) => {
|
||
try {
|
||
// 调用后端API获取诊室列表(可能包含所有科室的诊室)
|
||
const response = await getClinicRoomList({
|
||
pageNum: 1,
|
||
pageSize: 100
|
||
});
|
||
|
||
if (response.code === 200) {
|
||
let clinicRooms = [];
|
||
|
||
// 适配不同的后端数据结构
|
||
if (response.data && response.data.records) {
|
||
clinicRooms = response.data.records;
|
||
} else if (Array.isArray(response.data)) {
|
||
clinicRooms = response.data;
|
||
} else if (response.data && response.data.content) {
|
||
clinicRooms = response.data.content;
|
||
} else if (response.data && response.data.list) {
|
||
clinicRooms = response.data.list;
|
||
}
|
||
|
||
// 过滤出属于指定科室的诊室(前后端双重保险)
|
||
const filteredClinicRooms = clinicRooms.filter(room =>
|
||
room.department &&
|
||
(room.department.toLowerCase().includes(deptName.toLowerCase()) ||
|
||
(room.deptName && room.deptName.toLowerCase().includes(deptName.toLowerCase())))
|
||
);
|
||
|
||
// 将诊室数据转换为下拉选项格式
|
||
const options = filteredClinicRooms.map(room => ({
|
||
label: room.roomName || room.name || room.room_no,
|
||
value: room.roomName || room.name || room.room_no,
|
||
id: room.id
|
||
}));
|
||
|
||
// 更新诊室选项
|
||
clinicRoomOptions.value = options;
|
||
return options;
|
||
}
|
||
clinicRoomOptions.value = [];
|
||
return [];
|
||
} catch (error) {
|
||
console.error('获取诊室列表失败:', error);
|
||
clinicRoomOptions.value = [];
|
||
return [];
|
||
}
|
||
};
|
||
|
||
// 获取挂号项目选项
|
||
const fetchRegistrationItems = async (deptId = null) => {
|
||
try {
|
||
// 调用后端API获取挂号项目列表 - 使用专门的挂号项目API
|
||
// 如果提供了科室ID,则按科室过滤挂号项目
|
||
const params = deptId ? { deptId } : {};
|
||
const response = await getRegistrationItems(params);
|
||
|
||
if (response.code === 200) {
|
||
let healthcareItems = [];
|
||
|
||
// 适配不同的后端数据结构
|
||
if (response.data && response.data.records) {
|
||
healthcareItems = response.data.records;
|
||
} else if (Array.isArray(response.data)) {
|
||
healthcareItems = response.data;
|
||
} else if (response.data && response.data.content) {
|
||
healthcareItems = response.data.content;
|
||
} else if (response.data && response.data.list) {
|
||
healthcareItems = response.data.list;
|
||
}
|
||
|
||
// 由于后端已经按ybType='13'过滤,这里不再需要前端过滤
|
||
const registrationItems = healthcareItems;
|
||
|
||
// 将挂号项目数据转换为下拉选项格式
|
||
const options = registrationItems.map(item => ({
|
||
label: `${item.name} (${item.retailPrice || '0'}元)`,
|
||
value: item.name,
|
||
price: item.retailPrice || 0
|
||
}));
|
||
|
||
// 更新挂号项目选项
|
||
registrationItemOptions.value = options;
|
||
return options;
|
||
} else {
|
||
console.error('获取挂号项目列表失败:', response.msg || response.message);
|
||
registrationItemOptions.value = [];
|
||
return [];
|
||
}
|
||
} catch (error) {
|
||
console.error('获取挂号项目列表失败:', error);
|
||
registrationItemOptions.value = [];
|
||
return [];
|
||
}
|
||
};
|
||
|
||
// 获取诊查项目选项
|
||
const fetchClinicItems = async (deptId = null) => {
|
||
try {
|
||
// 调用后端API获取诊查项目列表 - 传递科室ID进行过滤
|
||
const params = deptId ? { orgId: deptId } : {};
|
||
const response = await getClinicItems(params);
|
||
|
||
if (response.code === 200) {
|
||
let healthcareItems = [];
|
||
|
||
// 适配不同的后端数据结构
|
||
if (response.data && response.data.records) {
|
||
healthcareItems = response.data.records;
|
||
} else if (Array.isArray(response.data)) {
|
||
healthcareItems = response.data;
|
||
} else if (response.data && response.data.content) {
|
||
healthcareItems = response.data.content;
|
||
} else if (response.data && response.data.list) {
|
||
healthcareItems = response.data.list;
|
||
}
|
||
|
||
// 将诊查项目数据转换为下拉选项格式 [编号 项目名称 单价]
|
||
const options = healthcareItems.map(item => ({
|
||
label: `${item.busNo} ${item.name} ${item.retailPrice || '0'}`,
|
||
value: item.name,
|
||
price: item.retailPrice || 0,
|
||
busNo: item.busNo
|
||
}));
|
||
|
||
// 更新诊查项目选项
|
||
clinicItemOptions.value = options;
|
||
return options;
|
||
} else {
|
||
console.error('获取诊查项目列表失败:', response.msg || response.message);
|
||
clinicItemOptions.value = [];
|
||
return [];
|
||
}
|
||
} catch (error) {
|
||
console.error('获取诊查项目列表失败:', error);
|
||
clinicItemOptions.value = [];
|
||
return [];
|
||
}
|
||
};
|
||
|
||
// 根据排班类型获取医生选项
|
||
const getDoctorOptions = (appointmentType) => {
|
||
return doctorOptions.value[appointmentType] || []
|
||
}
|
||
|
||
// 排班列表数据
|
||
const scheduleList = ref([])
|
||
|
||
// 号源记录对话框相关
|
||
const recordDialogVisible = ref(false)
|
||
const currentRow = ref(null)
|
||
const appointmentRecords = ref([])
|
||
|
||
// 计算属性 - 是否为编辑模式
|
||
const isEditMode = computed(() => {
|
||
return currentMode.value === 'edit'
|
||
})
|
||
|
||
// 按日期分组的排班数据
|
||
const groupedSchedule = computed(() => {
|
||
// 按日期分组
|
||
const groups = {}
|
||
|
||
scheduleList.value.forEach(item => {
|
||
if (!groups[item.date]) {
|
||
groups[item.date] = {
|
||
date: item.date,
|
||
weekday: item.weekday,
|
||
items: []
|
||
}
|
||
}
|
||
groups[item.date].items.push(item)
|
||
})
|
||
|
||
// 转换为数组
|
||
return Object.values(groups)
|
||
})
|
||
|
||
// 过滤后的诊室选项
|
||
const filteredClinicRoomOptions = computed(() => {
|
||
if (!filterParams.value.clinicRoomName) {
|
||
return clinicRoomOptions.value
|
||
}
|
||
return clinicRoomOptions.value.filter(option =>
|
||
option.label.toLowerCase().includes(filterParams.value.clinicRoomName.toLowerCase())
|
||
)
|
||
})
|
||
|
||
// 获取卫生机构列表
|
||
const fetchOrganizationList = async () => {
|
||
try {
|
||
// 从科室列表中提取唯一的机构名称
|
||
const response = await getRegisterOrganizations({})
|
||
if (response.code === 200) {
|
||
let actualData = response.data
|
||
// 处理嵌套data结构
|
||
if (actualData && actualData.code === 200 && actualData.msg) {
|
||
actualData = actualData.data
|
||
}
|
||
|
||
// 确保数据是数组格式
|
||
let deptList = []
|
||
if (Array.isArray(actualData)) {
|
||
deptList = actualData
|
||
} else if (actualData && actualData.records) {
|
||
deptList = actualData.records
|
||
} else if (actualData && actualData.content) {
|
||
deptList = actualData.content
|
||
} else if (actualData && actualData.list) {
|
||
deptList = actualData.list
|
||
}
|
||
|
||
// 提取唯一的机构名称
|
||
const uniqueOrgNames = new Set();
|
||
deptList.forEach(dept => {
|
||
const orgName = dept.orgName || dept.organizationName || dept.org || dept.name;
|
||
if (orgName) {
|
||
uniqueOrgNames.add(orgName);
|
||
}
|
||
});
|
||
|
||
// 将机构名称转换为下拉选项格式
|
||
organizationOptions.value = Array.from(uniqueOrgNames).map((orgName, index) => ({
|
||
id: index, // 使用索引作为ID,因为我们只需要名称
|
||
name: orgName,
|
||
orgName: orgName
|
||
}));
|
||
} else {
|
||
organizationOptions.value = []
|
||
}
|
||
} catch (error) {
|
||
organizationOptions.value = []
|
||
}
|
||
}
|
||
|
||
// 获取科室列表(通用方法)
|
||
const fetchDeptList = async (apiFunction) => {
|
||
try {
|
||
// 统一查询参数口径:后端使用 name 作为科室名称条件
|
||
const params = {
|
||
pageNum: pagination.value.currentPage,
|
||
pageSize: pagination.value.pageSize
|
||
};
|
||
if (queryParams.value.orgName) {
|
||
params.orgName = queryParams.value.orgName;
|
||
}
|
||
if (queryParams.value.deptName) {
|
||
params.name = queryParams.value.deptName;
|
||
}
|
||
|
||
// 同时获取配置数据,分页大小与主列表保持一致
|
||
// 这样既解决了 9999 导致的性能问题,又保证了当前显示行的数据完整性
|
||
const [deptRes, configRes] = await Promise.all([
|
||
apiFunction(params),
|
||
getDeptAppthoursList({
|
||
pageNum: pagination.value.currentPage,
|
||
pageSize: pagination.value.pageSize
|
||
})
|
||
]);
|
||
|
||
if (deptRes.code === 200) {
|
||
let dataList = [];
|
||
let totalCount = 0;
|
||
let actualData = deptRes.data;
|
||
if (actualData && actualData.code === 200 && actualData.msg) {
|
||
actualData = actualData.data;
|
||
}
|
||
|
||
// 提取主数据列表
|
||
if (actualData && actualData.records) {
|
||
dataList = actualData.records;
|
||
totalCount = actualData.total || 0;
|
||
} else if (actualData && actualData.content) {
|
||
dataList = actualData.content;
|
||
totalCount = actualData.totalElements || 0;
|
||
} else if (Array.isArray(actualData)) {
|
||
dataList = actualData;
|
||
totalCount = actualData.length;
|
||
} else if (actualData && actualData.list) {
|
||
dataList = actualData.list;
|
||
totalCount = actualData.total || actualData.list.length;
|
||
} else {
|
||
dataList = actualData || [];
|
||
totalCount = Array.isArray(actualData) ? actualData.length : 0;
|
||
}
|
||
|
||
// 提取并映射配置数据
|
||
const configs = configRes?.data?.records || configRes?.rows || configRes?.data || [];
|
||
const configMap = new Map();
|
||
if (Array.isArray(configs)) {
|
||
configs.forEach(c => {
|
||
// 假设通过机构+科室名进行匹配,或者通过ID(取决于后端模型)
|
||
const key = `${c.institution}-${c.department}`;
|
||
configMap.set(key, c);
|
||
});
|
||
}
|
||
|
||
if (apiFunction === getRegisterOrganizations) {
|
||
deptList.value = dataList.map(org => {
|
||
const key = `${org.orgName || org.organizationName || org.org || ''}-${org.name}`;
|
||
const config = configMap.get(key);
|
||
|
||
return {
|
||
id: org.id,
|
||
orgName: org.orgName || org.organizationName || org.name,
|
||
deptName: org.name,
|
||
name: org.name,
|
||
remark: org.remark || org.intro,
|
||
// 优先使用配置中的状态,作为补充
|
||
status: config ? config.activeFlag !== 0 : (org.activeFlag === 1),
|
||
registerFlag: config ? config.registerFlag : org.registerFlag
|
||
};
|
||
});
|
||
} else {
|
||
deptList.value = dataList;
|
||
}
|
||
|
||
pagination.value.total = totalCount;
|
||
} else {
|
||
ElMessage.error(deptRes.msg || '获取科室列表失败');
|
||
}
|
||
} catch (error) {
|
||
console.error('获取科室列表失败:', error);
|
||
ElMessage.error('获取科室列表失败');
|
||
}
|
||
}
|
||
|
||
// 获取科室列表(默认列表)
|
||
const getDeptList = async () => {
|
||
// 在获取科室列表后,也获取挂号项目选项
|
||
// 优化:挂号项目已移至排班弹窗按需加载,此处不再提前加载。
|
||
await fetchDeptList(getRegisterOrganizations); // 使用挂号科室API
|
||
}
|
||
|
||
// 查询
|
||
const handleQuery = async () => {
|
||
pagination.value.currentPage = 1;
|
||
await fetchDeptList(getRegisterOrganizations); // 使用挂号科室API
|
||
}
|
||
|
||
// 处理卫生机构选择变化
|
||
const handleOrgChange = async (orgName) => {
|
||
// 当卫生机构变化时,重置科室选择
|
||
queryParams.value.deptName = ''
|
||
|
||
// 重新获取科室选项列表,以反映机构筛选
|
||
await fetchDepartmentOptions()
|
||
}
|
||
|
||
// 重置
|
||
const handleReset = async () => {
|
||
queryParams.value = {
|
||
orgName: '',
|
||
deptName: ''
|
||
}
|
||
pagination.value.currentPage = 1
|
||
await fetchDepartmentOptions()
|
||
await getDeptList()
|
||
}
|
||
|
||
// 预约设置弹窗显示
|
||
const handleAppointmentSetting = async () => {
|
||
// 获取当前机构的预约配置
|
||
try {
|
||
const res = await getAppointmentConfig()
|
||
if (res.code === 200 && res.data) {
|
||
// 回显已有配置
|
||
appointmentSettingForm.value = {
|
||
cancelAppointmentType: res.data.cancelAppointmentType || 'YEAR',
|
||
cancelAppointmentCount: res.data.cancelAppointmentCount || 0
|
||
}
|
||
} else {
|
||
// 无配置时使用默认值
|
||
appointmentSettingForm.value = {
|
||
cancelAppointmentType: 'YEAR',
|
||
cancelAppointmentCount: 0
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('获取预约配置失败:', error)
|
||
appointmentSettingForm.value = {
|
||
cancelAppointmentType: 'YEAR',
|
||
cancelAppointmentCount: 0
|
||
}
|
||
}
|
||
appointmentSettingDialog.value = true
|
||
}
|
||
|
||
// 预约设置确定
|
||
const handleAppointmentSettingConfirm = async () => {
|
||
try {
|
||
const res = await saveAppointmentConfig({
|
||
cancelAppointmentType: appointmentSettingForm.value.cancelAppointmentType,
|
||
cancelAppointmentCount: appointmentSettingForm.value.cancelAppointmentCount,
|
||
validFlag: 1
|
||
})
|
||
if (res.code === 200) {
|
||
ElMessage.success('预约设置保存成功')
|
||
appointmentSettingDialog.value = false
|
||
} else {
|
||
ElMessage.error(res.msg || '保存失败')
|
||
}
|
||
} catch (error) {
|
||
console.error('保存预约配置失败:', error)
|
||
ElMessage.error('保存失败')
|
||
}
|
||
}
|
||
|
||
// 预约设置取消
|
||
const handleAppointmentSettingCancel = () => {
|
||
appointmentSettingDialog.value = false
|
||
}
|
||
|
||
// 路由和导航
|
||
const router = useRouter()
|
||
|
||
// 根据开始时间自动判断时段(上午/下午)
|
||
const getTimeSlot = (startTime) => {
|
||
if (!startTime) return '上午';
|
||
const hour = parseInt(startTime.split(':')[0]);
|
||
return hour < 12 ? '上午' : '下午';
|
||
};
|
||
|
||
// 生成一周排班数据
|
||
const generateWeekSchedule = (startDate, workTimeConfig) => {
|
||
const days = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
|
||
const timeSlots = [
|
||
{
|
||
label: getTimeSlot(workTimeConfig?.morningStart || '08:00'),
|
||
startTime: workTimeConfig?.morningStart || '08:00',
|
||
endTime: workTimeConfig?.morningEnd || '12:00'
|
||
},
|
||
{
|
||
label: getTimeSlot(workTimeConfig?.afternoonStart || '14:30'),
|
||
startTime: workTimeConfig?.afternoonStart || '14:30',
|
||
endTime: workTimeConfig?.afternoonEnd || '18:00'
|
||
}
|
||
];
|
||
const defaultQuota = workTimeConfig?.quota || '';
|
||
|
||
const schedule = []
|
||
|
||
// 生成一周7天的数据
|
||
for (let i = 0; i < 7; i++) {
|
||
// 创建新日期对象,避免修改原始日期
|
||
const year = startDate.getFullYear();
|
||
const month = startDate.getMonth();
|
||
const day = startDate.getDate();
|
||
|
||
// 创建新日期,添加偏移天数
|
||
const currentDate = new Date(year, month, day + i);
|
||
|
||
// 使用本地日期格式而不是ISO字符串,避免时区转换问题
|
||
const dateStr = `${currentDate.getFullYear()}-${String(currentDate.getMonth() + 1).padStart(2, '0')}-${String(currentDate.getDate()).padStart(2, '0')}`;
|
||
const weekday = days[currentDate.getDay()];
|
||
|
||
// 每个时间段生成一条记录
|
||
timeSlots.forEach(slot => {
|
||
schedule.push({
|
||
id: `${dateStr}-${slot.label}`,
|
||
date: dateStr,
|
||
weekday: weekday,
|
||
timeSlot: slot.label,
|
||
startTime: slot.startTime,
|
||
endTime: slot.endTime,
|
||
doctorName: '',
|
||
doctorId: null,
|
||
room: '',
|
||
maxNumber: defaultQuota,
|
||
appointmentItem: '',
|
||
registrationFee: 0,
|
||
clinicItem: '',
|
||
treatmentFee: 0,
|
||
online: true,
|
||
stopClinic: false,
|
||
stopReason: ''
|
||
})
|
||
})
|
||
}
|
||
|
||
return schedule
|
||
}
|
||
|
||
// 格式化日期为 yyyy-MM-dd 字符串
|
||
const formatDateStr = (date) => {
|
||
const d = new Date(date)
|
||
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
|
||
}
|
||
|
||
// 重新加载排班数据
|
||
const reloadScheduleData = async () => {
|
||
const row = currentDept.value
|
||
if (!row) return
|
||
|
||
const weekSchedule = generateWeekSchedule(filterParams.value.startDate, workTimeConfig.value)
|
||
const startDateStr = formatDateStr(filterParams.value.startDate)
|
||
const endDate = new Date(filterParams.value.startDate)
|
||
endDate.setDate(endDate.getDate() + 6)
|
||
const endDateStr = formatDateStr(endDate)
|
||
|
||
try {
|
||
const response = await getDoctorScheduleListByDeptIdAndDateRange(row.id, startDateStr, endDateStr)
|
||
if (response.code === 200) {
|
||
const actualData = response.data
|
||
const deptSchedules = Array.isArray(actualData)
|
||
? actualData
|
||
: (actualData && actualData.code === 200 ? actualData.data || [] : [])
|
||
const selectedRegType = filterParams.value.appointmentType === '专家' ? 1 : 0
|
||
const filteredSchedules = deptSchedules.filter(schedule => Number(schedule.regType) === selectedRegType)
|
||
|
||
// 同日期同时段可能存在多条排班,按 key 聚合成数组
|
||
const scheduleMap = {}
|
||
filteredSchedules.forEach(schedule => {
|
||
const key = `${schedule.scheduleDate}-${schedule.timePeriod}`
|
||
if (!scheduleMap[key]) {
|
||
scheduleMap[key] = []
|
||
}
|
||
scheduleMap[key].push(schedule)
|
||
})
|
||
|
||
const allAvailableDoctors = [...doctorOptions.value['普通'], ...doctorOptions.value['专家']]
|
||
const mergedSchedule = []
|
||
|
||
const applyScheduleToSlot = (targetSlot, existingSchedule) => {
|
||
targetSlot.doctorName = existingSchedule.doctorName || existingSchedule.doctor || ''
|
||
targetSlot.doctorId = existingSchedule.doctorId != null ? String(existingSchedule.doctorId) : null
|
||
|
||
const matchedDoctorById = allAvailableDoctors.find(doc => doc.id === targetSlot.doctorId)
|
||
if (!matchedDoctorById && targetSlot.doctorName) {
|
||
const matchedDoctorByName = allAvailableDoctors.find(doc => doc.label === targetSlot.doctorName)
|
||
if (matchedDoctorByName) {
|
||
targetSlot.doctorId = matchedDoctorByName.id
|
||
}
|
||
}
|
||
|
||
targetSlot.room = existingSchedule.clinic || existingSchedule.clinicRoom || ''
|
||
targetSlot.startTime = existingSchedule.startTime
|
||
targetSlot.endTime = existingSchedule.endTime
|
||
targetSlot.maxNumber = existingSchedule.limitNumber
|
||
targetSlot.appointmentItem = existingSchedule.registerItem
|
||
targetSlot.registrationFee = existingSchedule.registerFee
|
||
targetSlot.clinicItem = existingSchedule.diagnosisItem
|
||
targetSlot.treatmentFee = existingSchedule.diagnosisFee
|
||
targetSlot.online = existingSchedule.isOnline
|
||
targetSlot.stopClinic = existingSchedule.isStopped
|
||
targetSlot.stopReason = existingSchedule.stopReason
|
||
targetSlot.regType = existingSchedule.regType
|
||
targetSlot.backendId = existingSchedule.id
|
||
}
|
||
|
||
weekSchedule.forEach(slot => {
|
||
const key = `${slot.date}-${slot.timeSlot}`
|
||
const existingSchedules = scheduleMap[key] || []
|
||
|
||
if (existingSchedules.length === 0) {
|
||
mergedSchedule.push(slot)
|
||
return
|
||
}
|
||
|
||
existingSchedules.forEach((existingSchedule, index) => {
|
||
const targetSlot = index === 0
|
||
? slot
|
||
: {
|
||
...slot,
|
||
id: `${slot.id}-dup-${existingSchedule.id || index}`,
|
||
doctorName: '',
|
||
doctorId: null,
|
||
room: '',
|
||
maxNumber: '',
|
||
appointmentItem: '',
|
||
registrationFee: 0,
|
||
clinicItem: '',
|
||
treatmentFee: 0,
|
||
online: true,
|
||
stopClinic: false,
|
||
stopReason: ''
|
||
}
|
||
applyScheduleToSlot(targetSlot, existingSchedule)
|
||
mergedSchedule.push(targetSlot)
|
||
})
|
||
})
|
||
|
||
scheduleList.value = mergedSchedule
|
||
return
|
||
}
|
||
} catch (error) {
|
||
console.error('获取科室排班数据失败:', error)
|
||
ElMessage.error('获取科室排班数据失败')
|
||
}
|
||
|
||
scheduleList.value = weekSchedule
|
||
}
|
||
const handleEdit = async (row) => {
|
||
// 设置当前科室和模式
|
||
currentDept.value = row
|
||
currentMode.value = 'edit'
|
||
scheduleDialogTitle.value = `编辑科室排班 - ${row.name || row.deptName}`
|
||
|
||
// 动态设置筛选参数
|
||
filterParams.value.orgName = row.orgName || row.organizationName || row.org || ''
|
||
filterParams.value.deptName = row.name || row.deptName
|
||
|
||
// 首先获取科室工作时间配置
|
||
try {
|
||
const res = await getDeptAppthoursList({ deptId: row.id, pageNum: 1, pageSize: 1 });
|
||
console.log("【调试信息】科室工作时间配置原始数据:", res); // Debug log
|
||
const records = res?.data?.records || res?.rows || [];
|
||
if (records.length > 0) {
|
||
workTimeConfig.value = records[0];
|
||
} else {
|
||
workTimeConfig.value = null; // 确保如果没有配置则清空
|
||
}
|
||
} catch (error) {
|
||
console.error("获取科室工作时间配置失败:", error);
|
||
ElMessage.error("获取科室工作时间配置失败");
|
||
workTimeConfig.value = null;
|
||
}
|
||
|
||
// 设置显示机构名称
|
||
if (workTimeConfig.value && workTimeConfig.value.institution) {
|
||
displayedInstitutionName.value = workTimeConfig.value.institution;
|
||
} else {
|
||
displayedInstitutionName.value = '未设置机构';
|
||
}
|
||
|
||
// 并行获取其他相关数据
|
||
await Promise.all([
|
||
fetchDoctorList(row.id),
|
||
fetchClinicRoomsByDept(row.name || row.deptName),
|
||
row && row.id ? fetchRegistrationItems(row.id) : Promise.resolve(),
|
||
fetchClinicItems(row.id) // 获取诊查项目选项,传递科室ID
|
||
])
|
||
|
||
console.log("【调试信息】可供选择的医生列表:", JSON.parse(JSON.stringify(doctorOptions.value)));
|
||
|
||
// 加载排班数据
|
||
await reloadScheduleData()
|
||
|
||
// 显示排班弹窗
|
||
scheduleDialogVisible.value = true
|
||
}
|
||
|
||
// 查看
|
||
const handleView = async (row) => {
|
||
// 设置当前科室和模式
|
||
currentDept.value = row
|
||
currentMode.value = 'view'
|
||
scheduleDialogTitle.value = `查看科室排班 - ${row.name || row.deptName}`
|
||
|
||
// 动态设置筛选参数
|
||
filterParams.value.orgName = row.orgName || row.organizationName || row.org || ''
|
||
filterParams.value.deptName = row.name || row.deptName
|
||
|
||
// 首先获取科室工作时间配置
|
||
try {
|
||
const res = await getDeptAppthoursList({ deptId: row.id, pageNum: 1, pageSize: 1 });
|
||
const records = res?.data?.records || res?.rows || [];
|
||
if (records.length > 0) {
|
||
workTimeConfig.value = records[0];
|
||
} else {
|
||
workTimeConfig.value = null;
|
||
}
|
||
} catch (error) {
|
||
console.error("获取科室工作时间配置失败:", error);
|
||
ElMessage.error("获取科室工作时间配置失败");
|
||
workTimeConfig.value = null;
|
||
}
|
||
|
||
// 并行获取其他相关数据
|
||
await Promise.all([
|
||
fetchDoctorList(row.id),
|
||
row && row.id ? fetchRegistrationItems(row.id) : Promise.resolve(),
|
||
fetchClinicItems(row.id) // 获取诊查项目选项,传递科室ID
|
||
])
|
||
|
||
// 加载排班数据
|
||
await reloadScheduleData()
|
||
|
||
// 显示排班弹窗
|
||
scheduleDialogVisible.value = true
|
||
}
|
||
|
||
// 监听日期变化,重新生成排班数据
|
||
watch(
|
||
() => filterParams.value.startDate,
|
||
(newDate) => {
|
||
if (scheduleDialogVisible.value) { // 只有在排班弹窗打开时才重新生成数据
|
||
reloadScheduleData();
|
||
}
|
||
}
|
||
)
|
||
|
||
// 诊室搜索
|
||
const searchClinicRooms = async (query) => {
|
||
if (!query) {
|
||
// 如果没有查询词,重新获取当前科室的所有诊室
|
||
await fetchClinicRoomsByDept(filterParams.value.deptName);
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 先获取所有诊室,然后在前端进行过滤
|
||
const response = await getClinicRoomList({
|
||
roomName: query,
|
||
pageNum: 1,
|
||
pageSize: 100
|
||
});
|
||
|
||
if (response.code === 200) {
|
||
let clinicRooms = [];
|
||
|
||
// 适配不同的后端数据结构
|
||
if (response.data && response.data.records) {
|
||
clinicRooms = response.data.records;
|
||
} else if (Array.isArray(response.data)) {
|
||
clinicRooms = response.data;
|
||
} else if (response.data && response.data.content) {
|
||
clinicRooms = response.data.content;
|
||
} else if (response.data && response.data.list) {
|
||
clinicRooms = response.data.list;
|
||
}
|
||
|
||
// 过滤出属于当前科室且符合查询条件的诊室
|
||
const filteredClinicRooms = clinicRooms.filter(room =>
|
||
room.department &&
|
||
(room.department.toLowerCase().includes(filterParams.value.deptName.toLowerCase()) ||
|
||
(room.deptName && room.deptName.toLowerCase().includes(filterParams.value.deptName.toLowerCase()))) &&
|
||
(query ? (room.roomName || room.name || room.room_no).toLowerCase().includes(query.toLowerCase()) : true)
|
||
);
|
||
|
||
// 更新诊室选项
|
||
clinicRoomOptions.value = filteredClinicRooms.map(room => ({
|
||
label: room.roomName || room.name || room.room_no,
|
||
value: room.roomName || room.name || room.room_no,
|
||
id: room.id
|
||
}));
|
||
}
|
||
} catch (error) {
|
||
console.error('搜索诊室失败:', error);
|
||
}
|
||
}
|
||
|
||
// 医生映射关系(普通医生 -> 专家医生)
|
||
// 这里可以根据实际的医生职称或类型进行动态映射
|
||
const doctorMapping = {}
|
||
|
||
// 排班类型变化处理
|
||
const handleAppointmentTypeChange = async () => {
|
||
if (currentDept.value) {
|
||
await reloadScheduleData()
|
||
return
|
||
}
|
||
scheduleList.value.forEach(item => {
|
||
item.doctorId = null
|
||
item.doctorName = ''
|
||
})
|
||
}
|
||
|
||
// 挂号项目变化处理
|
||
const handleAppointmentItemChange = (row) => {
|
||
if (row.appointmentItem) {
|
||
// 从挂号项目选项中查找对应的价格
|
||
const selectedItem = registrationItemOptions.value.find(item => item.value === row.appointmentItem);
|
||
if (selectedItem) {
|
||
row.registrationFee = selectedItem.price || 0;
|
||
} else {
|
||
row.registrationFee = 0;
|
||
}
|
||
} else {
|
||
row.registrationFee = 0;
|
||
}
|
||
}
|
||
|
||
// 诊查项目变化处理
|
||
const handleClinicItemChange = (row) => {
|
||
if (row.clinicItem) {
|
||
// 从诊查项目选项中查找对应的价格
|
||
const selectedItem = clinicItemOptions.value.find(item => item.value === row.clinicItem);
|
||
if (selectedItem) {
|
||
row.treatmentFee = selectedItem.price || 0;
|
||
} else {
|
||
row.treatmentFee = 0;
|
||
}
|
||
} else {
|
||
row.treatmentFee = 0;
|
||
}
|
||
}
|
||
|
||
// 医生选择变化处理
|
||
const handleDoctorChange = (selectedId, row) => {
|
||
if (!selectedId) {
|
||
row.doctorName = '';
|
||
return;
|
||
}
|
||
const typeDoctors = getDoctorOptions(filterParams.value.appointmentType)
|
||
const selectedDoctor = typeDoctors.find(doc => doc.id === String(selectedId));
|
||
if (selectedDoctor) {
|
||
row.doctorName = selectedDoctor.label;
|
||
}
|
||
}
|
||
|
||
const resolveDoctorName = (row) => {
|
||
if (row.doctorName && row.doctorName.trim() !== '') {
|
||
return row.doctorName.trim()
|
||
}
|
||
if (!row.doctorId) {
|
||
return ''
|
||
}
|
||
const allDoctors = [...doctorOptions.value['普通'], ...doctorOptions.value['专家']]
|
||
const matched = allDoctors.find(doc => doc.id === String(row.doctorId))
|
||
return matched ? matched.label : ''
|
||
}
|
||
|
||
// 添加排班
|
||
// 删除后兜底:当某天某时段被删空时,补一条重置行,保证可继续填写
|
||
const createResetScheduleRow = (row) => {
|
||
const regTypeValue = Number(row.regType)
|
||
return {
|
||
id: `reset-${Date.now()}-${Math.random()}`,
|
||
date: row.date,
|
||
weekday: row.weekday,
|
||
timeSlot: row.timeSlot,
|
||
startTime: row.startTime || '08:00',
|
||
endTime: row.endTime || '12:00',
|
||
doctorName: '',
|
||
doctorId: null,
|
||
room: '',
|
||
maxNumber: '',
|
||
appointmentItem: '',
|
||
registrationFee: 0,
|
||
clinicItem: '',
|
||
treatmentFee: 0,
|
||
online: true,
|
||
stopClinic: false,
|
||
stopReason: '',
|
||
regType: Number.isNaN(regTypeValue) ? 0 : regTypeValue,
|
||
isNew: true
|
||
}
|
||
}
|
||
|
||
const ensureResetRowAfterDelete = (row, insertIndex) => {
|
||
const hasSameSlot = scheduleList.value.some(item =>
|
||
item.date === row.date && item.timeSlot === row.timeSlot
|
||
)
|
||
if (hasSameSlot) {
|
||
return
|
||
}
|
||
|
||
const resetRow = createResetScheduleRow(row)
|
||
if (insertIndex >= 0 && insertIndex <= scheduleList.value.length) {
|
||
scheduleList.value.splice(insertIndex, 0, resetRow)
|
||
return
|
||
}
|
||
scheduleList.value.push(resetRow)
|
||
}
|
||
|
||
const isDraftScheduleRow = (row) => {
|
||
return !row.backendId || row.isNew
|
||
}
|
||
|
||
const isLastDraftRowInSlot = (row) => {
|
||
if (!isDraftScheduleRow(row)) {
|
||
return false
|
||
}
|
||
|
||
const sameSlotRows = scheduleList.value.filter(item =>
|
||
item.date === row.date && item.timeSlot === row.timeSlot
|
||
)
|
||
const draftRows = sameSlotRows.filter(item => isDraftScheduleRow(item))
|
||
return draftRows.length <= 1
|
||
}
|
||
|
||
const handleAddSchedule = (row) => {
|
||
// 创建新的排班记录,基于当前行的日期和时段
|
||
const startTime = row.startTime || '08:00';
|
||
const newSchedule = {
|
||
id: `new-${Date.now()}-${Math.random()}`,
|
||
date: row.date,
|
||
weekday: row.weekday,
|
||
timeSlot: getTimeSlot(startTime),
|
||
startTime: startTime,
|
||
endTime: row.endTime || '12:00',
|
||
doctorName: '',
|
||
room: '',
|
||
maxNumber: row.maxNumber ?? '',
|
||
appointmentItem: '',
|
||
registrationFee: 0,
|
||
clinicItem: '',
|
||
treatmentFee: 0,
|
||
online: true,
|
||
stopClinic: false,
|
||
stopReason: '',
|
||
isNew: true // 标记为新添加的记录
|
||
}
|
||
|
||
// 找到当前行在列表中的位置,在其后插入新记录
|
||
const currentIndex = scheduleList.value.findIndex(item => item.id === row.id)
|
||
if (currentIndex !== -1) {
|
||
scheduleList.value.splice(currentIndex + 1, 0, newSchedule)
|
||
ElMessage.success('添加排班成功')
|
||
} else {
|
||
// 如果找不到,添加到对应日期组的末尾
|
||
scheduleList.value.push(newSchedule)
|
||
ElMessage.success('添加排班成功')
|
||
}
|
||
}
|
||
|
||
// 时段变化时,更新默认的开始结束时间
|
||
const handleTimeSlotChange = (val, row) => {
|
||
const config = workTimeConfig.value;
|
||
if (val === '上午') {
|
||
if (!row.startTime || row.startTime >= '12:00') {
|
||
row.startTime = config?.morningStart || '08:00';
|
||
row.endTime = config?.morningEnd || '12:00';
|
||
}
|
||
} else if (val === '下午') {
|
||
if (!row.startTime || row.startTime < '12:00') {
|
||
row.startTime = config?.afternoonStart || '14:30';
|
||
row.endTime = config?.afternoonEnd || '18:00';
|
||
}
|
||
}
|
||
};
|
||
|
||
// 开始时间变化时,自动更新时段
|
||
const handleStartTimeChange = (val, row) => {
|
||
if (val) {
|
||
row.timeSlot = getTimeSlot(val);
|
||
}
|
||
};
|
||
|
||
// 删除排班
|
||
const handleDeleteSchedule = (row) => {
|
||
if (isLastDraftRowInSlot(row)) {
|
||
ElMessage.warning('当前时段需保留最后一条待填写记录,不能删除')
|
||
return
|
||
}
|
||
|
||
ElMessageBox.confirm('确定要删除这条排班记录吗?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
// 如果是已保存的记录(有后端ID),调用删除接口
|
||
if (row.backendId && !row.isNew) {
|
||
deleteDoctorSchedule(row.backendId).then(res => {
|
||
const removed = res?.data?.data === true || res?.data === true
|
||
if (res.code === 200 && removed) {
|
||
// 从列表中移除
|
||
const index = scheduleList.value.findIndex(item => item.id === row.id)
|
||
if (index !== -1) {
|
||
scheduleList.value.splice(index, 1)
|
||
ensureResetRowAfterDelete(row, index)
|
||
}
|
||
ElMessage.success('删除成功')
|
||
} else {
|
||
ElMessage.error(res.msg || '删除失败,记录可能未真正删除')
|
||
}
|
||
}).catch(error => {
|
||
console.error('删除排班失败:', error)
|
||
ElMessage.error('删除失败,请稍后重试')
|
||
})
|
||
} else {
|
||
// 如果是新添加的记录(未保存),直接从列表中移除
|
||
const index = scheduleList.value.findIndex(item => item.id === row.id)
|
||
if (index !== -1) {
|
||
scheduleList.value.splice(index, 1)
|
||
ensureResetRowAfterDelete(row, index)
|
||
ElMessage.success('删除成功')
|
||
}
|
||
}
|
||
}).catch(() => {
|
||
// 用户取消删除
|
||
})
|
||
}
|
||
|
||
// 根据ID删除排班
|
||
const handleDeleteScheduleById = (id) => {
|
||
ElMessageBox.confirm('确定要删除这条排班记录吗?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
deleteDoctorSchedule(id).then(res => {
|
||
if (res.code === 200) {
|
||
// 从列表中移除
|
||
const index = doctorScheduleList.value.findIndex(item => item.id === id)
|
||
if (index !== -1) {
|
||
doctorScheduleList.value.splice(index, 1)
|
||
}
|
||
ElMessage.success('删除成功')
|
||
} else {
|
||
ElMessage.error(res.msg || '删除失败')
|
||
}
|
||
}).catch(error => {
|
||
console.error('删除排班失败:', error)
|
||
ElMessage.error('删除失败,请稍后重试')
|
||
})
|
||
}).catch(() => {
|
||
// 用户取消删除
|
||
})
|
||
}
|
||
|
||
// 编辑排班
|
||
const handleEditSchedule = (row) => {
|
||
console.log('编辑排班:', row)
|
||
// 设置当前模式为编辑模式
|
||
currentMode.value = 'edit'
|
||
scheduleDialogTitle.value = `编辑医生排班 - ${row.doctor}`
|
||
|
||
// 显示排班弹窗
|
||
scheduleDialogVisible.value = true
|
||
|
||
// 生成一周的排班数据
|
||
const weekSchedule = generateWeekSchedule(filterParams.value.startDate)
|
||
|
||
// 找到与当前编辑行匹配的排班数据并更新
|
||
const targetSchedule = weekSchedule.find(item =>
|
||
item.weekday === row.weekday &&
|
||
item.timeSlot === row.timePeriod && // 注意:这里应该是timeSlot而不是timePeriod
|
||
item.doctorName === row.doctor
|
||
)
|
||
|
||
if (targetSchedule) {
|
||
// 更新目标排班的属性
|
||
targetSchedule.doctorName = row.doctor
|
||
targetSchedule.room = row.clinic
|
||
targetSchedule.startTime = row.startTime
|
||
targetSchedule.endTime = row.endTime
|
||
targetSchedule.maxNumber = row.limitNumber
|
||
targetSchedule.appointmentItem = row.registerItem
|
||
targetSchedule.registrationFee = row.registerFee
|
||
targetSchedule.clinicItem = row.diagnosisItem
|
||
targetSchedule.treatmentFee = row.diagnosisFee
|
||
targetSchedule.online = row.isOnline
|
||
targetSchedule.stopClinic = row.isStopped
|
||
targetSchedule.stopReason = row.stopReason
|
||
}
|
||
|
||
// 设置科室信息
|
||
filterParams.value.orgName = currentDept.value?.orgName || currentDept.value?.organizationName || currentDept.value?.org || ''
|
||
filterParams.value.deptName = '未知科室' // 这里可能需要根据实际情况设置
|
||
|
||
// 设置排班列表
|
||
scheduleList.value = weekSchedule
|
||
}
|
||
|
||
// 计算号源记录
|
||
const calculateAppointmentRecords = (row) => {
|
||
const { startTime, endTime, maxNumber } = row
|
||
|
||
// 将时间转换为分钟数
|
||
const [startHour, startMinute] = startTime.split(':').map(Number)
|
||
const [endHour, endMinute] = endTime.split(':').map(Number)
|
||
|
||
const startTotalMinutes = startHour * 60 + startMinute
|
||
const endTotalMinutes = endHour * 60 + endMinute
|
||
|
||
// 计算总时长和间隔
|
||
const totalDuration = endTotalMinutes - startTotalMinutes
|
||
const interval = Math.floor(totalDuration / maxNumber)
|
||
|
||
// 生成号源记录
|
||
const records = []
|
||
for (let i = 0; i < maxNumber; i++) {
|
||
const minutes = startTotalMinutes + i * interval
|
||
const hour = Math.floor(minutes / 60).toString().padStart(2, '0')
|
||
const minute = (minutes % 60).toString().padStart(2, '0')
|
||
records.push({
|
||
index: i + 1,
|
||
time: `${hour}:${minute}`
|
||
})
|
||
}
|
||
|
||
return records
|
||
}
|
||
|
||
// 查看号源记录
|
||
const handleViewRecord = (row) => {
|
||
// 验证开始时间、结束时间和限号数量
|
||
if (!row.startTime || !row.endTime || !row.maxNumber) {
|
||
ElMessageBox.confirm('请先设置开始时间、结束时间和限号数量', '', {
|
||
confirmButtonText: '确定',
|
||
type: 'warning',
|
||
showCancelButton: false
|
||
})
|
||
return
|
||
}
|
||
|
||
// 计算号源记录
|
||
currentRow.value = row
|
||
appointmentRecords.value = calculateAppointmentRecords(row)
|
||
recordDialogVisible.value = true
|
||
}
|
||
|
||
// 保存排班
|
||
const handleSave = async () => {
|
||
try {
|
||
// 验证必填字段 - 只验证用户真正填写了信息的记录
|
||
const filledSchedules = scheduleList.value.filter(item => {
|
||
// 已有排班可能只有 doctorId,没有 doctorName,二者任一存在都视为可保存记录
|
||
return !!resolveDoctorName(item) || !!item.doctorId;
|
||
});
|
||
|
||
// 验证结束时间必须大于开始时间
|
||
const invalidTimeSchedules = filledSchedules.filter(item => {
|
||
if (item.startTime && item.endTime) {
|
||
return item.startTime >= item.endTime;
|
||
}
|
||
return false;
|
||
});
|
||
|
||
if (invalidTimeSchedules.length > 0) {
|
||
ElMessage.warning('结束时间必须大于开始时间,请检查排班记录');
|
||
return;
|
||
}
|
||
|
||
const incompleteSchedules = filledSchedules.filter(item => {
|
||
const isDoctorValid = !!resolveDoctorName(item) || !!item.doctorId;
|
||
const isRoomValid = item.room && item.room.trim() !== '';
|
||
const isStartTimeValid = item.startTime && typeof item.startTime === 'string';
|
||
const isEndTimeValid = item.endTime && typeof item.endTime === 'string';
|
||
const isMaxNumberValid = item.maxNumber !== null && item.maxNumber !== undefined &&
|
||
item.maxNumber !== '' && !isNaN(item.maxNumber) && parseInt(item.maxNumber) > 0;
|
||
return !(isDoctorValid && isRoomValid && isStartTimeValid && isEndTimeValid && isMaxNumberValid);
|
||
});
|
||
|
||
if (incompleteSchedules.length > 0) {
|
||
ElMessage.warning('请完善已填写排班记录的必填信息(医生、诊室、开始时间、结束时间、限号数量)');
|
||
return;
|
||
}
|
||
|
||
// 转换数据格式
|
||
const schedulesToProcess = filledSchedules.map(item => {
|
||
const regTypeValue = Number(
|
||
item.regType ?? (filterParams.value.appointmentType === '专家' ? 1 : 0)
|
||
);
|
||
const scheduleData = {
|
||
weekday: item.weekday,
|
||
timePeriod: item.timeSlot,
|
||
doctor: resolveDoctorName(item),
|
||
doctorId: item.doctorId,
|
||
clinic: item.room,
|
||
startTime: item.startTime,
|
||
endTime: item.endTime,
|
||
limitNumber: parseInt(item.maxNumber) || 0,
|
||
registerItem: item.appointmentItem || '',
|
||
registerFee: item.registrationFee || 0,
|
||
diagnosisItem: item.clinicItem || '',
|
||
diagnosisFee: item.treatmentFee || 0,
|
||
isOnline: item.online,
|
||
isStopped: item.stopClinic,
|
||
stopReason: item.stopClinic ? (item.stopReason || '') : '',
|
||
deptId: currentDept.value?.id || null,
|
||
regType: Number.isNaN(regTypeValue) ? 0 : regTypeValue,
|
||
scheduledDate: item.date // 添加具体日期字段
|
||
};
|
||
if (item.backendId) {
|
||
scheduleData.id = item.backendId;
|
||
}
|
||
return { scheduleData, isNew: !item.backendId };
|
||
});
|
||
|
||
if (schedulesToProcess.length === 0) {
|
||
ElMessage.info('没有需要保存的排班记录');
|
||
return;
|
||
}
|
||
|
||
let addSuccess = 0, addFail = 0;
|
||
let updateSuccess = 0, updateFail = 0;
|
||
const errors = [];
|
||
|
||
// 分别处理新增和更新
|
||
const recordsToAdd = schedulesToProcess.filter(p => p.isNew);
|
||
const recordsToUpdate = schedulesToProcess.filter(p => !p.isNew);
|
||
|
||
// 处理新增
|
||
for (const { scheduleData } of recordsToAdd) {
|
||
try {
|
||
const res = await addDoctorScheduleWithDate(scheduleData);
|
||
if (res.code === 200) {
|
||
addSuccess++;
|
||
} else {
|
||
addFail++;
|
||
errors.push(`新增失败: ${res.msg}`);
|
||
}
|
||
} catch (error) {
|
||
addFail++;
|
||
errors.push(`新增异常: ${error.message}`);
|
||
}
|
||
}
|
||
|
||
// 处理更新
|
||
for (const { scheduleData } of recordsToUpdate) {
|
||
try {
|
||
const res = await updateDoctorSchedule(scheduleData);
|
||
if (res.code === 200) {
|
||
updateSuccess++;
|
||
} else {
|
||
updateFail++;
|
||
errors.push(`更新失败: ${res.msg}`);
|
||
}
|
||
} catch (error) {
|
||
updateFail++;
|
||
errors.push(`更新异常: ${error.message}`);
|
||
}
|
||
}
|
||
|
||
// 统一报告结果
|
||
if (errors.length > 0) {
|
||
console.error('保存错误详情:', errors);
|
||
}
|
||
|
||
const messages = [];
|
||
if (addSuccess > 0) messages.push(`新增成功 ${addSuccess} 条`);
|
||
if (updateSuccess > 0) messages.push(`更新成功 ${updateSuccess} 条`);
|
||
|
||
if (addFail > 0 || updateFail > 0) {
|
||
const errorMessages = [];
|
||
if (addFail > 0) errorMessages.push(`新增失败 ${addFail} 条`);
|
||
if (updateFail > 0) errorMessages.push(`更新失败 ${updateFail} 条`);
|
||
ElMessage.warning([...messages, ...errorMessages].join(','));
|
||
} else if (messages.length > 0) {
|
||
ElMessage.success(messages.join(','));
|
||
}
|
||
|
||
// 如果有任何成功操作,则刷新数据
|
||
if (addSuccess > 0 || updateSuccess > 0) {
|
||
await reloadScheduleData();
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('保存排班失败:', error);
|
||
ElMessage.error('保存失败,请稍后重试');
|
||
}
|
||
}
|
||
|
||
// 取消操作
|
||
const handleCancel = () => {
|
||
scheduleDialogVisible.value = false
|
||
}
|
||
|
||
// 分页大小变化
|
||
const handleSizeChange = (size) => {
|
||
pagination.value.pageSize = size
|
||
getDeptList()
|
||
}
|
||
|
||
// 当前页码变化
|
||
const handleCurrentChange = (current) => {
|
||
pagination.value.currentPage = current
|
||
getDeptList()
|
||
}
|
||
|
||
// 获取门诊挂号的就诊科室数据
|
||
const fetchDepartmentOptions = async () => {
|
||
try {
|
||
// 使用当前查询参数进行过滤
|
||
const params = {
|
||
pageNum: 1,
|
||
pageSize: 100 // 下拉菜单加载较多数据以供选择
|
||
}
|
||
if (queryParams.value.deptName) {
|
||
params.name = queryParams.value.deptName
|
||
}
|
||
if (queryParams.value.orgName) {
|
||
params.orgName = queryParams.value.orgName
|
||
}
|
||
|
||
const response = await getRegisterOrganizations(params)
|
||
if (response.code === 200) {
|
||
// 适配不同的后端数据结构
|
||
let actualData = response.data
|
||
// 处理嵌套data结构
|
||
if (actualData && actualData.code === 200 && actualData.msg) {
|
||
actualData = actualData.data
|
||
}
|
||
|
||
// 确保数据是数组格式
|
||
if (Array.isArray(actualData)) {
|
||
// 将组织机构数据转换为科室选项格式
|
||
departmentOptions.value = actualData.map(org => ({
|
||
id: org.id,
|
||
code: org.busNo, // 使用组织机构编码
|
||
name: org.name, // 使用组织机构名称作为科室名称
|
||
deptName: org.name // 保持兼容性
|
||
}))
|
||
} else if (actualData && actualData.records) {
|
||
// 将组织机构数据转换为科室选项格式
|
||
departmentOptions.value = actualData.records.map(org => ({
|
||
id: org.id,
|
||
code: org.busNo, // 使用组织机构编码
|
||
name: org.name, // 使用组织机构名称作为科室名称
|
||
deptName: org.name // 保持兼容性
|
||
}))
|
||
} else if (actualData && actualData.content) {
|
||
// 将组织机构数据转换为科室选项格式
|
||
departmentOptions.value = actualData.content.map(org => ({
|
||
id: org.id,
|
||
code: org.busNo, // 使用组织机构编码
|
||
name: org.name, // 使用组织机构名称作为科室名称
|
||
deptName: org.name // 保持兼容性
|
||
}))
|
||
} else if (actualData && actualData.list) {
|
||
// 将组织机构数据转换为科室选项格式
|
||
departmentOptions.value = actualData.list.map(org => ({
|
||
id: org.id,
|
||
code: org.busNo, // 使用组织机构编码
|
||
name: org.name, // 使用组织机构名称作为科室名称
|
||
deptName: org.name // 保持兼容性
|
||
}))
|
||
} else {
|
||
departmentOptions.value = []
|
||
}
|
||
} else {
|
||
console.error('获取科室列表失败:', response.msg)
|
||
departmentOptions.value = []
|
||
}
|
||
} catch (error) {
|
||
console.error('获取科室列表失败:', error)
|
||
departmentOptions.value = []
|
||
}
|
||
}
|
||
|
||
// 页面加载时获取初始化数据
|
||
onMounted(async () => {
|
||
// 优化:合并初始化请求,减少并发
|
||
await fetchOrganizationList() // 该函数内部已包含 getRegisterOrganizations 调用
|
||
await getDeptList()
|
||
|
||
// 延迟加载非核心选项
|
||
setTimeout(() => {
|
||
fetchDepartmentOptions()
|
||
}, 300)
|
||
})
|
||
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.appoinmentmanage-container {
|
||
width: 100%;
|
||
height: 100%;
|
||
padding: 20px;
|
||
background-color: #f5f7fa;
|
||
}
|
||
|
||
.appoinmentmanage-header {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.appoinmentmanage-title {
|
||
font-size: 20px;
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin: 0;
|
||
}
|
||
|
||
.appoinmentmanage-content {
|
||
background-color: #fff;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||
}
|
||
|
||
.query-condition {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
gap: 16px;
|
||
}
|
||
|
||
.query-select {
|
||
width: 200px;
|
||
}
|
||
|
||
.query-button, .reset-button, .appointment-setting-button {
|
||
min-width: 100px;
|
||
}
|
||
|
||
.dept-table-container {
|
||
width: 100%;
|
||
}
|
||
|
||
.pagination-container {
|
||
margin-top: 20px;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
/* 表格居中样式 */
|
||
.centered-table {
|
||
:deep(.el-table__header-wrapper th.el-table__cell),
|
||
:deep(.el-table__body-wrapper td.el-table__cell) {
|
||
text-align: center;
|
||
}
|
||
}
|
||
|
||
/* 医生排班样式 */
|
||
.doctorschedule-content {
|
||
background-color: #fff;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||
}
|
||
|
||
.filter-condition {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
gap: 16px;
|
||
}
|
||
|
||
.filter-label {
|
||
font-weight: 500;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.filter-select {
|
||
width: 150px;
|
||
}
|
||
|
||
.filter-date-picker {
|
||
width: 200px;
|
||
}
|
||
|
||
.radio-group {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.schedule-table-container {
|
||
margin-bottom: 20px;
|
||
width: 100%;
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.daily-schedule {
|
||
margin-bottom: 20px;
|
||
border: 1px solid #ebeef5;
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
width: 100%;
|
||
}
|
||
|
||
.daily-header {
|
||
background-color: #f5f7fa;
|
||
padding: 10px 15px;
|
||
border-bottom: 1px solid #ebeef5;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 15px;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.date-text {
|
||
color: #333;
|
||
}
|
||
|
||
.weekday-text {
|
||
color: #606266;
|
||
font-weight: normal;
|
||
}
|
||
|
||
.schedule-table {
|
||
width: 100% !important;
|
||
min-width: 100% !important;
|
||
|
||
:deep(.el-table__header-wrapper) {
|
||
width: 100% !important;
|
||
border-top: none;
|
||
}
|
||
|
||
:deep(.el-table__body-wrapper) {
|
||
width: 100% !important;
|
||
}
|
||
|
||
:deep(.el-table__header-wrapper th.el-table__cell),
|
||
:deep(.el-table__body-wrapper td.el-table__cell) {
|
||
text-align: center;
|
||
padding: 8px 0;
|
||
}
|
||
|
||
/* 确保表格容器填满 */
|
||
:deep(.el-table__inner-wrapper) {
|
||
width: 100% !important;
|
||
}
|
||
|
||
/* 确保表格本身填满 */
|
||
:deep(.el-table__body) {
|
||
width: 100% !important;
|
||
}
|
||
|
||
/* 确保表格列正确分配宽度 */
|
||
:deep(.el-table__header) {
|
||
width: 100% !important;
|
||
}
|
||
|
||
:deep(.el-table__header tr),
|
||
:deep(.el-table__body tr) {
|
||
width: 100% !important;
|
||
}
|
||
|
||
/* 确保表格容器的最小宽度与内容匹配 */
|
||
:deep(.el-table) {
|
||
width: 100% !important;
|
||
min-width: 100% !important;
|
||
}
|
||
}
|
||
|
||
.inline-select {
|
||
width: 100%;
|
||
}
|
||
|
||
.inline-input {
|
||
width: 100%;
|
||
}
|
||
|
||
.time-picker {
|
||
width: 100%;
|
||
}
|
||
|
||
.record-icon {
|
||
font-size: 18px;
|
||
color: #409eff;
|
||
cursor: pointer;
|
||
transition: color 0.2s;
|
||
}
|
||
|
||
.record-icon:hover {
|
||
color: #66b1ff;
|
||
}
|
||
|
||
.bottom-buttons {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 16px;
|
||
}
|
||
|
||
/* 号源记录对话框样式 */
|
||
.appointment-records {
|
||
max-height: 300px;
|
||
overflow-y: auto;
|
||
padding: 10px 0;
|
||
}
|
||
|
||
.record-item {
|
||
padding: 8px 0;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
}
|
||
|
||
.record-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.record-time {
|
||
font-size: 16px;
|
||
color: #333;
|
||
}
|
||
|
||
.dialog-footer {
|
||
text-align: center;
|
||
}
|
||
|
||
/* 隐藏数字输入框的增减按钮 */
|
||
:deep(.el-input__inner[type="number"]) {
|
||
appearance: textfield;
|
||
-moz-appearance: textfield;
|
||
}
|
||
:deep(.el-input__inner[type="number"])::-webkit-outer-spin-button,
|
||
:deep(.el-input__inner[type="number"])::-webkit-inner-spin-button {
|
||
appearance: none;
|
||
-webkit-appearance: none;
|
||
margin: 0;
|
||
}
|
||
</style>
|
||
|
||
<style>
|
||
/* 全局样式:增大并突出显示全屏医生排班弹窗的关闭按钮 */
|
||
.el-dialog.is-fullscreen .el-dialog__headerbtn {
|
||
top: 15px !important;
|
||
right: 15px !important;
|
||
height: 50px !important;
|
||
width: 50px !important;
|
||
display: flex !important;
|
||
align-items: center !important;
|
||
justify-content: center !important;
|
||
transition: background-color 0.2s ease-in-out;
|
||
}
|
||
|
||
.el-dialog.is-fullscreen .el-dialog__headerbtn .el-dialog__close {
|
||
font-size: 36px !important;
|
||
width: 36px !important;
|
||
height: 36px !important;
|
||
color: #F56C6C !important; /* 直接设置为红色 */
|
||
transition: all 0.2s ease-in-out;
|
||
}
|
||
|
||
.el-dialog.is-fullscreen .el-dialog__headerbtn:hover {
|
||
background-color: #fef0f0 !important;
|
||
}
|
||
|
||
.el-dialog.is-fullscreen .el-dialog__headerbtn:hover .el-dialog__close {
|
||
color: #dd4a4a !important; /* 悬停时更深的红色 */
|
||
transform: scale(1.2);
|
||
}
|
||
</style>
|
||
|
||
<style scoped>
|
||
/* 确保弹窗内容区占满宽度 */
|
||
:deep(.el-dialog__body) {
|
||
padding: 20px;
|
||
}
|
||
</style>
|