Merge branch 'develop' of https://gitea.gentronhealth.com/Yajentine/his into develop

This commit is contained in:
2025-12-25 14:13:37 +08:00
14 changed files with 832 additions and 39 deletions

View File

@@ -3,6 +3,7 @@ package com.openhis.web.appointmentmanage.appservice.impl;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.core.common.core.domain.R; import com.core.common.core.domain.R;
import com.openhis.appointmentmanage.domain.DoctorSchedule; import com.openhis.appointmentmanage.domain.DoctorSchedule;
import com.openhis.appointmentmanage.mapper.DoctorScheduleMapper;
import com.openhis.appointmentmanage.service.IDoctorScheduleService; import com.openhis.appointmentmanage.service.IDoctorScheduleService;
import com.openhis.web.appointmentmanage.appservice.IDoctorScheduleAppService; import com.openhis.web.appointmentmanage.appservice.IDoctorScheduleAppService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -15,6 +16,9 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService {
@Resource @Resource
private IDoctorScheduleService doctorScheduleService; private IDoctorScheduleService doctorScheduleService;
@Resource
private DoctorScheduleMapper doctorScheduleMapper;
@Override @Override
public R<?> getDoctorScheduleList() { public R<?> getDoctorScheduleList() {
@@ -27,8 +31,35 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService {
if (ObjectUtil.isEmpty(doctorSchedule)) { if (ObjectUtil.isEmpty(doctorSchedule)) {
return R.fail("医生排班不能为空"); return R.fail("医生排班不能为空");
} }
boolean save = doctorScheduleService.save(doctorSchedule); // 创建新对象排除id字段数据库id列是GENERATED ALWAYS由数据库自动生成
return R.ok(save); DoctorSchedule newSchedule = new DoctorSchedule();
newSchedule.setWeekday(doctorSchedule.getWeekday());
newSchedule.setTimePeriod(doctorSchedule.getTimePeriod());
newSchedule.setDoctor(doctorSchedule.getDoctor());
newSchedule.setClinic(doctorSchedule.getClinic());
newSchedule.setStartTime(doctorSchedule.getStartTime());
newSchedule.setEndTime(doctorSchedule.getEndTime());
newSchedule.setLimitNumber(doctorSchedule.getLimitNumber());
// call_sign_record 字段不能为null设置默认值为空字符串
newSchedule.setCallSignRecord(doctorSchedule.getCallSignRecord() != null ? doctorSchedule.getCallSignRecord() : "");
newSchedule.setRegisterItem(doctorSchedule.getRegisterItem() != null ? doctorSchedule.getRegisterItem() : "");
newSchedule.setRegisterFee(doctorSchedule.getRegisterFee() != null ? doctorSchedule.getRegisterFee() : 0);
newSchedule.setDiagnosisItem(doctorSchedule.getDiagnosisItem() != null ? doctorSchedule.getDiagnosisItem() : "");
newSchedule.setDiagnosisFee(doctorSchedule.getDiagnosisFee() != null ? doctorSchedule.getDiagnosisFee() : 0);
newSchedule.setIsOnline(doctorSchedule.getIsOnline() != null ? doctorSchedule.getIsOnline() : false);
newSchedule.setIsStopped(doctorSchedule.getIsStopped() != null ? doctorSchedule.getIsStopped() : false);
newSchedule.setStopReason(doctorSchedule.getStopReason() != null ? doctorSchedule.getStopReason() : "");
newSchedule.setDeptId(doctorSchedule.getDeptId());
// 不设置id字段让数据库自动生成
// 使用自定义的insertWithoutId方法确保INSERT语句不包含id字段
int result = doctorScheduleMapper.insertWithoutId(newSchedule);
boolean save = result > 0;
if (save) {
// 返回保存后的实体对象包含数据库生成的ID
return R.ok(newSchedule);
} else {
return R.fail("保存失败");
}
} }
@Override @Override

View File

@@ -114,8 +114,10 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
Integer pricingFlag, List<Integer> adviceTypes) { Integer pricingFlag, List<Integer> adviceTypes) {
// 医嘱定价来源 // 医嘱定价来源
String orderPricingSource = TenantOptionUtil.getOptionContent(TenantOptionDict.ORDER_PRICING_SOURCE); String orderPricingSource = TenantOptionUtil.getOptionContent(TenantOptionDict.ORDER_PRICING_SOURCE);
// 如果配置为空使用retailPrice作为默认值
if (StringUtils.isEmpty(orderPricingSource)) { if (StringUtils.isEmpty(orderPricingSource)) {
throw new ServiceException("租户配置项【医嘱定价来源】未配置"); // throw new ServiceException("租户配置项【医嘱定价来源】未配置");
orderPricingSource = "retailPrice";
} }
// 构建查询条件 // 构建查询条件
QueryWrapper<AdviceBaseDto> queryWrapper = HisQueryUtils.buildQueryWrapper(adviceBaseDto, searchKey, QueryWrapper<AdviceBaseDto> queryWrapper = HisQueryUtils.buildQueryWrapper(adviceBaseDto, searchKey,

View File

@@ -0,0 +1,10 @@
package com.openhis.web.triageandqueuemanage.appservice;
import com.core.common.core.domain.R;
import com.openhis.triageandqueuemanage.domain.CallNumberVoiceConfig;
public interface CallNumberVoiceConfigAppService {
R<?> addCallNumberVoiceConfig(CallNumberVoiceConfig callNumberVoiceConfig);
R<?> updateCallNumberVoiceConfig(CallNumberVoiceConfig callNumberVoiceConfig);
}

View File

@@ -0,0 +1,35 @@
package com.openhis.web.triageandqueuemanage.appservice.impl;
import cn.hutool.core.util.ObjectUtil;
import com.core.common.core.domain.R;
import com.openhis.triageandqueuemanage.domain.CallNumberVoiceConfig;
import com.openhis.triageandqueuemanage.service.CallNumberVoiceConfigService;
import com.openhis.web.triageandqueuemanage.appservice.CallNumberVoiceConfigAppService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class CallNumberVoiceConfigAppServiceImpl implements CallNumberVoiceConfigAppService {
@Resource
private CallNumberVoiceConfigService callNumberVoiceConfigService;
@Override
public R<?> addCallNumberVoiceConfig(CallNumberVoiceConfig callNumberVoiceConfig) {
if(ObjectUtil.isNull(callNumberVoiceConfig)){
return R.fail("叫号语音设置实体不能为空");
}
boolean save = callNumberVoiceConfigService.save(callNumberVoiceConfig);
return R.ok(save);
}
@Override
public R<?> updateCallNumberVoiceConfig(CallNumberVoiceConfig callNumberVoiceConfig) {
if(ObjectUtil.isNull(callNumberVoiceConfig)){
return R.fail("叫号语音设置实体不能为空");
}
boolean updateById = callNumberVoiceConfigService.updateById(callNumberVoiceConfig);
return R.ok(updateById);
}
}

View File

@@ -0,0 +1,40 @@
package com.openhis.web.triageandqueuemanage.controller;
import com.core.common.core.domain.R;
import com.openhis.triageandqueuemanage.domain.CallNumberVoiceConfig;
import com.openhis.web.triageandqueuemanage.appservice.CallNumberVoiceConfigAppService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@RestController
@Slf4j
@RequestMapping("/CallNumberVoice")
public class CallNumberVoiceConfigController {
@Resource
private CallNumberVoiceConfigAppService callNumberVoiceConfigAppService;
/**
* 新增叫号语音设置实体
*
* @param callNumberVoiceConfig 叫号语音设置实体
* @return 结果
*/
@PostMapping("/add")
public R<?> addCallNumberVoiceConfig(@RequestBody CallNumberVoiceConfig callNumberVoiceConfig) {
return R.ok(callNumberVoiceConfigAppService.addCallNumberVoiceConfig(callNumberVoiceConfig));
}
/**
* 修改叫号语音设置实体
*
* @param callNumberVoiceConfig 叫号语音设置实体
* @return 结果
*/
@PutMapping("/update")
public R<?> updateCallNumberVoiceConfig(@RequestBody CallNumberVoiceConfig callNumberVoiceConfig) {
return R.ok(callNumberVoiceConfigAppService.updateCallNumberVoiceConfig(callNumberVoiceConfig));
}
}

View File

@@ -0,0 +1,7 @@
package com.openhis.web.triageandqueuemanage.mapper;
import org.springframework.stereotype.Repository;
@Repository
public interface CallNumberVoiceConfigAppMapper {
}

View File

@@ -1,5 +1,7 @@
package com.openhis.appointmentmanage.domain; package com.openhis.appointmentmanage.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
@@ -15,7 +17,8 @@ import java.time.LocalTime;
@TableName(value = "adm_doctor_schedule") @TableName(value = "adm_doctor_schedule")
@Accessors(chain = true) @Accessors(chain = true)
public class DoctorSchedule { public class DoctorSchedule {
/** id */ /** id - 数据库GENERATED ALWAYS由数据库自动生成插入时不应包含此字段 */
@TableId(type = IdType.NONE)
private Integer id; private Integer id;
/** 星期 */ /** 星期 */

View File

@@ -2,8 +2,12 @@ package com.openhis.appointmentmanage.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.openhis.appointmentmanage.domain.DoctorSchedule; import com.openhis.appointmentmanage.domain.DoctorSchedule;
import org.springframework.stereotype.Repository; import org.apache.ibatis.annotations.Mapper;
@Repository @Mapper
public interface DoctorScheduleMapper extends BaseMapper<DoctorSchedule> { public interface DoctorScheduleMapper extends BaseMapper<DoctorSchedule> {
/**
* 自定义插入方法排除id字段数据库GENERATED ALWAYS
*/
int insertWithoutId(DoctorSchedule doctorSchedule);
} }

View File

@@ -0,0 +1,47 @@
package com.openhis.triageandqueuemanage.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
@Data
@Accessors(chain = true)
@TableName(value = "call_number_voice")
@EqualsAndHashCode(callSuper = false)
public class CallNumberVoiceConfig {
/** id */
@TableId(type = IdType.AUTO)
private Integer id;
/** 播放次数1-5次 */
private Integer playCount;
/** 叫号前缀 */
private String callPrefix;
/** 叫号后缀 */
private String callSuffix;
/** 语速(较慢/正常/较快) */
private String speed;
/** (音量0-100%) */
private Integer volume;
/** 播报间隔 */
private Integer intervalSeconds;
/** 是否循环播报 */
private Boolean cycleBroadcast;
/** 创建时间 */
private LocalDateTime createTime;
/** 更新时间 */
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,9 @@
package com.openhis.triageandqueuemanage.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.openhis.triageandqueuemanage.domain.CallNumberVoiceConfig;
import org.springframework.stereotype.Repository;
@Repository
public interface CallNumberVoiceConfigMapper extends BaseMapper<CallNumberVoiceConfig> {
}

View File

@@ -0,0 +1,7 @@
package com.openhis.triageandqueuemanage.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.openhis.triageandqueuemanage.domain.CallNumberVoiceConfig;
public interface CallNumberVoiceConfigService extends IService<CallNumberVoiceConfig> {
}

View File

@@ -0,0 +1,11 @@
package com.openhis.triageandqueuemanage.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.openhis.triageandqueuemanage.domain.CallNumberVoiceConfig;
import com.openhis.triageandqueuemanage.mapper.CallNumberVoiceConfigMapper;
import com.openhis.triageandqueuemanage.service.CallNumberVoiceConfigService;
import org.springframework.stereotype.Service;
@Service
public class CallNumberVoiceConfigServiceImpl extends ServiceImpl<CallNumberVoiceConfigMapper, CallNumberVoiceConfig> implements CallNumberVoiceConfigService {
}

View File

@@ -27,8 +27,8 @@
<span class="filter-label">排班类型</span> <span class="filter-label">排班类型</span>
<div class="radio-group"> <div class="radio-group">
<el-radio v-model="filterParams.appointmentType" label="普通">普通</el-radio> <el-radio v-model="filterParams.appointmentType" label="普通" @change="handleAppointmentTypeChange">普通</el-radio>
<el-radio v-model="filterParams.appointmentType" label="专家">专家</el-radio> <el-radio v-model="filterParams.appointmentType" label="专家" @change="handleAppointmentTypeChange">专家</el-radio>
</div> </div>
</div> </div>
@@ -42,7 +42,7 @@
</div> </div>
<el-table :data="dateGroup.items" border style="width: 100%" class="schedule-table"> <el-table :data="dateGroup.items" border style="width: 100%" class="schedule-table">
<el-table-column prop="timeSlot" label="时段" width="100"></el-table-column> <el-table-column prop="timeSlot" label="时段" width="100"></el-table-column>
<el-table-column prop="doctorName" label="医生" width="120"> <el-table-column prop="doctorName" :label="filterParams.appointmentType === '专家' ? '专家' : '医生'" width="150">
<template #default="scope"> <template #default="scope">
<el-select <el-select
v-model="scope.row.doctorName" v-model="scope.row.doctorName"
@@ -50,10 +50,12 @@
class="inline-select" class="inline-select"
:disabled="!isEditMode" :disabled="!isEditMode"
> >
<el-option label="张医生" value="张医生"></el-option> <el-option
<el-option label="李医生" value="李医生"></el-option> v-for="doctor in getDoctorOptions(filterParams.appointmentType)"
<el-option label="王医生" value="王医生"></el-option> :key="doctor.value"
<el-option label="赵医生" value="赵医生"></el-option> :label="doctor.label"
:value="doctor.value"
></el-option>
</el-select> </el-select>
</template> </template>
</el-table-column> </el-table-column>
@@ -125,6 +127,7 @@
placeholder="请选" placeholder="请选"
class="inline-select" class="inline-select"
:disabled="!isEditMode" :disabled="!isEditMode"
@change="handleAppointmentItemChange(scope.row)"
> >
<el-option label="挂号费 50" value="挂号费 50"></el-option> <el-option label="挂号费 50" value="挂号费 50"></el-option>
<el-option label="一般诊疗费 10" value="一般诊疗费 10"></el-option> <el-option label="一般诊疗费 10" value="一般诊疗费 10"></el-option>
@@ -133,7 +136,11 @@
</el-select> </el-select>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="registrationFee" label="挂号费(元)" width="100"></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"> <el-table-column prop="clinicItem" label="诊查项目" width="150">
<template #default="scope"> <template #default="scope">
<el-select <el-select
@@ -141,6 +148,7 @@
placeholder="请选择诊查项目" placeholder="请选择诊查项目"
class="inline-select" class="inline-select"
:disabled="!isEditMode" :disabled="!isEditMode"
@change="handleClinicItemChange(scope.row)"
> >
<el-option label="常规诊查" value="常规诊查"></el-option> <el-option label="常规诊查" value="常规诊查"></el-option>
<el-option label="专科诊查" value="专科诊查"></el-option> <el-option label="专科诊查" value="专科诊查"></el-option>
@@ -149,7 +157,11 @@
</el-select> </el-select>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="treatmentFee" label="诊疗费(元)" width="100"></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"> <el-table-column prop="online" label="线上" width="60">
<template #default="scope"> <template #default="scope">
<el-checkbox v-model="scope.row.online" :disabled="!isEditMode"></el-checkbox> <el-checkbox v-model="scope.row.online" :disabled="!isEditMode"></el-checkbox>
@@ -227,6 +239,7 @@ import { ref, onMounted, computed, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElMessageBox, ElDialog } from 'element-plus' import { ElMessage, ElMessageBox, ElDialog } from 'element-plus'
import { View } from '@element-plus/icons-vue' import { View } from '@element-plus/icons-vue'
import { addDoctorSchedule, deleteDoctorSchedule } from '../api'
// 路由和导航 // 路由和导航
const route = useRoute() const route = useRoute()
@@ -240,6 +253,28 @@ const filterParams = ref({
appointmentType: '普通' appointmentType: '普通'
}) })
// 医生列表数据
const doctorOptions = ref({
'普通': [
{ label: '张医生', value: '张医生' },
{ label: '李医生', value: '李医生' },
{ label: '王医生', value: '王医生' },
{ label: '赵医生', value: '赵医生' }
],
'专家': [
{ label: '请选择专家', value: '' },
{ label: '王教授', value: '王教授' },
{ label: '李主任', value: '李主任' },
{ label: '赵教授', value: '赵教授' },
{ label: '张主任', value: '张主任' }
]
})
// 根据排班类型获取医生选项
const getDoctorOptions = (appointmentType) => {
return doctorOptions.value[appointmentType] || doctorOptions.value['普通']
}
// 编辑模式控制 // 编辑模式控制
const isEditMode = computed(() => { const isEditMode = computed(() => {
return route.query.mode === 'edit' return route.query.mode === 'edit'
@@ -248,6 +283,9 @@ const isEditMode = computed(() => {
// 排班列表数据 // 排班列表数据
const scheduleList = ref([]) const scheduleList = ref([])
// 当前科室信息
const currentDept = ref(null)
// 按日期分组的排班数据 // 按日期分组的排班数据
const groupedSchedule = computed(() => { const groupedSchedule = computed(() => {
// 按日期分组 // 按日期分组
@@ -299,9 +337,9 @@ const generateWeekSchedule = (startDate) => {
room: '', room: '',
maxNumber: '', maxNumber: '',
appointmentItem: '', appointmentItem: '',
registrationFee: '', registrationFee: 0,
clinicItem: '', clinicItem: '',
treatmentFee: '', treatmentFee: 0,
online: true, online: true,
stopClinic: false, stopClinic: false,
stopReason: '' stopReason: ''
@@ -318,18 +356,130 @@ const initData = () => {
const deptId = route.query.deptId const deptId = route.query.deptId
console.log('科室ID:', deptId) console.log('科室ID:', deptId)
// 设置当前科室信息
if (deptId) {
currentDept.value = { id: deptId }
}
// 生成排班数据 // 生成排班数据
scheduleList.value = generateWeekSchedule(filterParams.value.startDate) scheduleList.value = generateWeekSchedule(filterParams.value.startDate)
} }
// 排班类型变化处理
const handleAppointmentTypeChange = () => {
// 当排班类型改变时,清空所有排班记录的医生选择
scheduleList.value.forEach(item => {
// 如果当前选择的医生不在新类型对应的医生列表中,则清空
const currentDoctors = getDoctorOptions(filterParams.value.appointmentType)
const doctorExists = currentDoctors.some(doctor => doctor.value === item.doctorName)
if (!doctorExists) {
item.doctorName = ''
}
})
}
// 挂号项目变化处理
const handleAppointmentItemChange = (row) => {
if (row.appointmentItem) {
// 从挂号项目中提取费用数字,例如 "挂号费 50" -> 50
const feeMatch = row.appointmentItem.match(/(\d+)/)
if (feeMatch) {
row.registrationFee = parseInt(feeMatch[1])
} else {
row.registrationFee = 0
}
} else {
row.registrationFee = 0
}
}
// 诊查项目变化处理
const handleClinicItemChange = (row) => {
// 诊查项目费用映射表
const clinicFeeMap = {
'常规诊查': 10,
'专科诊查': 20,
'特殊诊查': 30,
'专家诊查': 50
}
if (row.clinicItem && clinicFeeMap[row.clinicItem]) {
row.treatmentFee = clinicFeeMap[row.clinicItem]
} else {
row.treatmentFee = 0
}
}
// 添加排班 // 添加排班
const handleAddSchedule = (row) => { const handleAddSchedule = (row) => {
ElMessage.info('添加排班功能待实现') // 创建新的排班记录,基于当前行的日期和时段
const newSchedule = {
id: `new-${Date.now()}-${Math.random()}`,
date: row.date,
weekday: row.weekday,
timeSlot: row.timeSlot,
startTime: row.startTime || '08:00',
endTime: row.endTime || '12:00',
doctorName: '',
room: '',
maxNumber: '',
appointmentItem: '',
registrationFee: '',
clinicItem: '',
treatmentFee: '',
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 handleDeleteSchedule = (row) => { const handleDeleteSchedule = (row) => {
ElMessage.info('删除排班功能待实现') ElMessageBox.confirm('确定要删除这条排班记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// 如果是已保存的记录有后端ID调用删除接口
if (row.backendId && !row.isNew) {
deleteDoctorSchedule(row.backendId).then(res => {
if (res.code === 200) {
// 从列表中移除
const index = scheduleList.value.findIndex(item => item.id === row.id)
if (index !== -1) {
scheduleList.value.splice(index, 1)
}
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)
ElMessage.success('删除成功')
}
}
}).catch(() => {
// 用户取消删除
})
} }
// 号源记录对话框相关 // 号源记录对话框相关
@@ -386,10 +536,139 @@ const handleViewRecord = (row) => {
} }
// 保存排班 // 保存排班
const handleSave = () => { const handleSave = async () => {
ElMessage.success('排班保存成功') try {
// 验证必填字段
const invalidSchedules = scheduleList.value.filter(item => {
// 检查是否有必填字段为空
return !item.doctorName || !item.room || !item.startTime || !item.endTime || !item.maxNumber
})
if (invalidSchedules.length > 0) {
ElMessage.warning('请完善所有排班记录的必填信息(医生、诊室、开始时间、结束时间、限号数量)')
return
}
// 转换数据格式以匹配后端实体,并建立映射关系
const schedulesToSave = scheduleList.value.map((item, index) => {
// 解析挂号项目和诊查项目,提取费用
let registrationFee = 0
let diagnosisFee = 0
if (item.appointmentItem) {
const feeMatch = item.appointmentItem.match(/(\d+)/)
if (feeMatch) {
registrationFee = parseInt(feeMatch[1])
}
}
if (item.clinicItem) {
// 这里可以根据诊查项目设置诊疗费暂时设为0或从其他字段获取
diagnosisFee = item.treatmentFee ? parseInt(item.treatmentFee) : 0
}
return {
localIndex: index, // 保存本地索引,用于更新
localId: item.id, // 保存本地ID
scheduleData: {
id: item.backendId || null, // 如果是新记录id为null
weekday: item.weekday,
timePeriod: item.timeSlot,
doctor: item.doctorName,
clinic: item.room,
startTime: item.startTime,
endTime: item.endTime,
limitNumber: parseInt(item.maxNumber) || 0,
registerItem: item.appointmentItem || '',
registerFee: registrationFee,
diagnosisItem: item.clinicItem || '',
diagnosisFee: diagnosisFee,
isOnline: item.online || false,
isStopped: item.stopClinic || false,
stopReason: item.stopClinic ? (item.stopReason || '') : '',
deptId: currentDept.value?.id || route.query.deptId || null,
}
}
})
// 只保存新添加的记录isNew: true或没有backendId的记录
const newSchedulesToSave = schedulesToSave.filter(({ localIndex }) => {
const item = scheduleList.value[localIndex]
return item.isNew || !item.backendId // 只保存新记录或没有backendId的记录
})
if (newSchedulesToSave.length === 0) {
ElMessage.info('没有需要保存的新排班记录')
// 返回上一页 // 返回上一页
router.back() router.back()
return
}
// 批量保存排班
let successCount = 0
let failCount = 0
const errors = []
for (const { localIndex, localId, scheduleData } of newSchedulesToSave) {
try {
// 确保新记录的id为null
scheduleData.id = null
const res = await addDoctorSchedule(scheduleData)
if (res.code === 200 && res.data) {
successCount++
// 更新本地记录的backendId
const localItem = scheduleList.value[localIndex]
if (localItem) {
// 从响应中获取保存后的ID
if (res.data && typeof res.data === 'object' && res.data.id) {
localItem.backendId = res.data.id
localItem.isNew = false
} else if (res.data && typeof res.data === 'number') {
localItem.backendId = res.data
localItem.isNew = false
} else if (res.data === true) {
// 如果后端返回的是boolean true标记为已保存
localItem.isNew = false
localItem.saved = true
} else {
// 其他情况,标记为已保存
localItem.isNew = false
}
}
} else {
failCount++
const errorMsg = res.msg || '保存失败'
errors.push(errorMsg)
console.error('保存排班失败:', errorMsg, res)
}
} catch (error) {
failCount++
const errorMsg = error.message || '保存异常'
errors.push(errorMsg)
console.error('保存排班异常:', error)
}
}
// 如果有错误,显示详细错误信息
if (errors.length > 0) {
console.error('保存错误详情:', errors)
}
if (failCount === 0) {
ElMessage.success(`排班保存成功,共保存 ${successCount} 条记录`)
// 返回上一页
router.back()
} else {
ElMessage.warning(`部分排班保存失败,成功 ${successCount} 条,失败 ${failCount}`)
if (errors.length > 0) {
console.error('错误详情:', errors.join('; '))
}
}
} catch (error) {
console.error('保存排班失败:', error)
ElMessage.error('保存失败,请稍后重试')
}
} }
// 取消操作 // 取消操作

View File

@@ -153,8 +153,8 @@
<span class="filter-label">排班类型</span> <span class="filter-label">排班类型</span>
<div class="radio-group"> <div class="radio-group">
<el-radio v-model="filterParams.appointmentType" label="普通">普通</el-radio> <el-radio v-model="filterParams.appointmentType" label="普通" @change="handleAppointmentTypeChange">普通</el-radio>
<el-radio v-model="filterParams.appointmentType" label="专家">专家</el-radio> <el-radio v-model="filterParams.appointmentType" label="专家" @change="handleAppointmentTypeChange">专家</el-radio>
</div> </div>
</div> </div>
@@ -168,18 +168,21 @@
</div> </div>
<el-table :data="dateGroup.items" border style="width: 100%" class="schedule-table"> <el-table :data="dateGroup.items" border style="width: 100%" class="schedule-table">
<el-table-column prop="timeSlot" label="时段" width="100"></el-table-column> <el-table-column prop="timeSlot" label="时段" width="100"></el-table-column>
<el-table-column prop="doctorName" label="医生" width="120"> <el-table-column prop="doctorName" :label="filterParams.appointmentType === '专家' ? '专家' : '医生'" width="150">
<template #default="scope"> <template #default="scope">
<el-select <el-select
v-model="scope.row.doctorName" v-model="scope.row.doctorName"
placeholder="请选" placeholder="请选"
class="inline-select" class="inline-select"
:disabled="!isEditMode" :disabled="!isEditMode"
:key="`doctor-${filterParams.appointmentType}-${scope.row.id}`"
> >
<el-option label="张医生" value="张医生"></el-option> <el-option
<el-option label="李医生" value="李医生"></el-option> v-for="doctor in getDoctorOptions(filterParams.appointmentType)"
<el-option label="王医生" value="王医生"></el-option> :key="doctor.value"
<el-option label="赵医生" value="赵医生"></el-option> :label="doctor.label"
:value="doctor.value"
></el-option>
</el-select> </el-select>
</template> </template>
</el-table-column> </el-table-column>
@@ -251,6 +254,7 @@
placeholder="请选" placeholder="请选"
class="inline-select" class="inline-select"
:disabled="!isEditMode" :disabled="!isEditMode"
@change="handleAppointmentItemChange(scope.row)"
> >
<el-option label="挂号费 50" value="挂号费 50"></el-option> <el-option label="挂号费 50" value="挂号费 50"></el-option>
<el-option label="一般诊疗费 10" value="一般诊疗费 10"></el-option> <el-option label="一般诊疗费 10" value="一般诊疗费 10"></el-option>
@@ -259,7 +263,11 @@
</el-select> </el-select>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="registrationFee" label="挂号费(元)" width="100"></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"> <el-table-column prop="clinicItem" label="诊查项目" width="150">
<template #default="scope"> <template #default="scope">
<el-select <el-select
@@ -267,6 +275,7 @@
placeholder="请选择诊查项目" placeholder="请选择诊查项目"
class="inline-select" class="inline-select"
:disabled="!isEditMode" :disabled="!isEditMode"
@change="handleClinicItemChange(scope.row)"
> >
<el-option label="常规诊查" value="常规诊查"></el-option> <el-option label="常规诊查" value="常规诊查"></el-option>
<el-option label="专科诊查" value="专科诊查"></el-option> <el-option label="专科诊查" value="专科诊查"></el-option>
@@ -275,7 +284,11 @@
</el-select> </el-select>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="treatmentFee" label="诊疗费(元)" width="100"></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"> <el-table-column prop="online" label="线上" width="60">
<template #default="scope"> <template #default="scope">
<el-checkbox v-model="scope.row.online" :disabled="!isEditMode"></el-checkbox> <el-checkbox v-model="scope.row.online" :disabled="!isEditMode"></el-checkbox>
@@ -355,6 +368,7 @@ import { ElMessage, ElDialog, ElSelect, ElOption, ElInput, ElForm, ElFormItem, E
import { EditPen, View, DocumentRemove } from '@element-plus/icons-vue' import { EditPen, View, DocumentRemove } from '@element-plus/icons-vue'
import { listDept, searchDept } from '@/api/appoinmentmanage/dept' import { listDept, searchDept } from '@/api/appoinmentmanage/dept'
import { getLocationTree } from '@/views/charge/outpatientregistration/components/outpatientregistration' import { getLocationTree } from '@/views/charge/outpatientregistration/components/outpatientregistration'
import { addDoctorSchedule, deleteDoctorSchedule, batchSaveDoctorSchedule } from './api'
// 查询参数 // 查询参数
const queryParams = ref({ const queryParams = ref({
@@ -402,6 +416,28 @@ const filterParams = ref({
appointmentType: '普通' appointmentType: '普通'
}) })
// 医生列表数据
const doctorOptions = ref({
'普通': [
{ label: '张医生', value: '张医生' },
{ label: '李医生', value: '李医生' },
{ label: '王医生', value: '王医生' },
{ label: '赵医生', value: '赵医生' }
],
'专家': [
{ label: '请选择专家', value: '' },
{ label: '王教授', value: '王教授' },
{ label: '李主任', value: '李主任' },
{ label: '赵教授', value: '赵教授' },
{ label: '张主任', value: '张主任' }
]
})
// 根据排班类型获取医生选项
const getDoctorOptions = (appointmentType) => {
return doctorOptions.value[appointmentType] || doctorOptions.value['普通']
}
// 排班列表数据 // 排班列表数据
const scheduleList = ref([]) const scheduleList = ref([])
@@ -566,9 +602,9 @@ const generateWeekSchedule = (startDate) => {
room: '', room: '',
maxNumber: '', maxNumber: '',
appointmentItem: '', appointmentItem: '',
registrationFee: '', registrationFee: 0,
clinicItem: '', clinicItem: '',
treatmentFee: '', treatmentFee: 0,
online: true, online: true,
stopClinic: false, stopClinic: false,
stopReason: '' stopReason: ''
@@ -615,14 +651,145 @@ const handleView = (row) => {
scheduleDialogVisible.value = true scheduleDialogVisible.value = true
} }
// 医生映射关系(普通医生 -> 专家医生)
const doctorMapping = {
'张医生': '张主任',
'李医生': '李主任',
'王医生': '王教授',
'赵医生': '赵教授',
'张主任': '张医生',
'李主任': '李医生',
'王教授': '王医生',
'赵教授': '赵医生'
}
// 排班类型变化处理
const handleAppointmentTypeChange = () => {
// 当排班类型改变时,自动匹配对应的医生
scheduleList.value.forEach(item => {
if (item.doctorName) {
// 获取新类型对应的医生列表
const newTypeDoctors = getDoctorOptions(filterParams.value.appointmentType)
// 检查当前医生是否在新类型的医生列表中
const doctorExists = newTypeDoctors.some(doctor => doctor.value === item.doctorName)
if (!doctorExists) {
// 如果当前医生不在新类型列表中,尝试通过映射关系找到对应的医生
const mappedDoctor = doctorMapping[item.doctorName]
if (mappedDoctor && newTypeDoctors.some(doctor => doctor.value === mappedDoctor)) {
// 如果找到对应的医生,则更新
item.doctorName = mappedDoctor
} else {
// 如果找不到对应的医生,则清空
item.doctorName = ''
}
}
}
})
}
// 挂号项目变化处理
const handleAppointmentItemChange = (row) => {
if (row.appointmentItem) {
// 从挂号项目中提取费用数字,例如 "挂号费 50" -> 50
const feeMatch = row.appointmentItem.match(/(\d+)/)
if (feeMatch) {
row.registrationFee = parseInt(feeMatch[1])
} else {
row.registrationFee = 0
}
} else {
row.registrationFee = 0
}
}
// 诊查项目变化处理
const handleClinicItemChange = (row) => {
// 诊查项目费用映射表
const clinicFeeMap = {
'常规诊查': 10,
'专科诊查': 20,
'特殊诊查': 30,
'专家诊查': 50
}
if (row.clinicItem && clinicFeeMap[row.clinicItem]) {
row.treatmentFee = clinicFeeMap[row.clinicItem]
} else {
row.treatmentFee = 0
}
}
// 添加排班 // 添加排班
const handleAddSchedule = (row) => { const handleAddSchedule = (row) => {
ElMessage.info('添加排班功能待实现') // 创建新的排班记录,基于当前行的日期和时段
const newSchedule = {
id: `new-${Date.now()}-${Math.random()}`,
date: row.date,
weekday: row.weekday,
timeSlot: row.timeSlot,
startTime: row.startTime || '08:00',
endTime: row.endTime || '12:00',
doctorName: '',
room: '',
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 handleDeleteSchedule = (row) => { const handleDeleteSchedule = (row) => {
ElMessage.info('删除排班功能待实现') ElMessageBox.confirm('确定要删除这条排班记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// 如果是已保存的记录有后端ID调用删除接口
if (row.backendId && !row.isNew) {
deleteDoctorSchedule(row.backendId).then(res => {
if (res.code === 200) {
// 从列表中移除
const index = scheduleList.value.findIndex(item => item.id === row.id)
if (index !== -1) {
scheduleList.value.splice(index, 1)
}
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)
ElMessage.success('删除成功')
}
}
}).catch(() => {
// 用户取消删除
})
} }
// 计算号源记录 // 计算号源记录
@@ -674,9 +841,150 @@ const handleViewRecord = (row) => {
} }
// 保存排班 // 保存排班
const handleSave = () => { const handleSave = async () => {
ElMessage.success('排班保存成功') try {
// 验证必填字段
const invalidSchedules = scheduleList.value.filter(item => {
// 检查是否有必填字段为空
return !item.doctorName || !item.room || !item.startTime || !item.endTime || !item.maxNumber
})
if (invalidSchedules.length > 0) {
ElMessage.warning('请完善所有排班记录的必填信息(医生、诊室、开始时间、结束时间、限号数量)')
return
}
// 转换数据格式以匹配后端实体,并建立映射关系
const schedulesToSave = scheduleList.value.map((item, index) => {
// 解析挂号项目和诊查项目,提取费用
let registrationFee = 0
let diagnosisFee = 0
if (item.appointmentItem) {
const feeMatch = item.appointmentItem.match(/(\d+)/)
if (feeMatch) {
registrationFee = parseInt(feeMatch[1])
}
}
if (item.clinicItem) {
// 这里可以根据诊查项目设置诊疗费暂时设为0或从其他字段获取
diagnosisFee = item.treatmentFee ? parseInt(item.treatmentFee) : 0
}
// 构建排班数据对象
const scheduleData = {
weekday: item.weekday,
timePeriod: item.timeSlot,
doctor: item.doctorName,
clinic: item.room,
startTime: item.startTime,
endTime: item.endTime,
limitNumber: parseInt(item.maxNumber) || 0,
registerItem: item.appointmentItem || '',
registerFee: registrationFee,
diagnosisItem: item.clinicItem || '',
diagnosisFee: diagnosisFee,
isOnline: item.online || false,
isStopped: item.stopClinic || false,
stopReason: item.stopClinic ? (item.stopReason || '') : '',
deptId: currentDept.value?.id || null,
}
// 只有已存在的记录才包含id字段用于更新
if (item.backendId) {
scheduleData.id = item.backendId
}
return {
localIndex: index, // 保存本地索引,用于更新
localId: item.id, // 保存本地ID
scheduleData: scheduleData
}
})
// 只保存新添加的记录isNew: true或已修改的记录
const newSchedulesToSave = schedulesToSave.filter(({ localIndex }) => {
const item = scheduleList.value[localIndex]
return item.isNew || !item.backendId // 只保存新记录或没有backendId的记录
})
if (newSchedulesToSave.length === 0) {
ElMessage.info('没有需要保存的新排班记录')
scheduleDialogVisible.value = false scheduleDialogVisible.value = false
return
}
// 批量保存排班
let successCount = 0
let failCount = 0
const errors = []
for (const { localIndex, localId, scheduleData } of newSchedulesToSave) {
try {
// 新记录不包含id字段数据库id列是GENERATED ALWAYS由数据库自动生成
// 创建新对象明确排除id字段确保JSON序列化时不包含id
const dataToSave = {}
Object.keys(scheduleData).forEach(key => {
// 排除id字段只复制其他字段
if (key !== 'id') {
dataToSave[key] = scheduleData[key]
}
})
const res = await addDoctorSchedule(dataToSave)
if (res.code === 200 && res.data) {
successCount++
// 更新本地记录的backendId
const localItem = scheduleList.value[localIndex]
if (localItem) {
// 从响应中获取保存后的ID
if (res.data && typeof res.data === 'object' && res.data.id) {
// 后端返回了实体对象包含ID
localItem.backendId = res.data.id
localItem.isNew = false
} else if (res.data && typeof res.data === 'number') {
// 如果返回的是数字ID
localItem.backendId = res.data
localItem.isNew = false
} else if (res.data === true) {
// 如果后端返回的是boolean true标记为已保存
localItem.isNew = false
localItem.saved = true
} else {
// 其他情况,标记为已保存
localItem.isNew = false
}
}
} else {
failCount++
const errorMsg = res.msg || '保存失败'
errors.push(errorMsg)
console.error('保存排班失败:', errorMsg, res)
}
} catch (error) {
failCount++
const errorMsg = error.message || '保存异常'
errors.push(errorMsg)
console.error('保存排班异常:', error)
}
}
// 如果有错误,显示详细错误信息
if (errors.length > 0) {
console.error('保存错误详情:', errors)
}
if (failCount === 0) {
ElMessage.success(`排班保存成功,共保存 ${successCount} 条记录`)
scheduleDialogVisible.value = false
} else {
ElMessage.warning(`部分排班保存失败,成功 ${successCount} 条,失败 ${failCount}`)
}
} catch (error) {
console.error('保存排班失败:', error)
ElMessage.error('保存失败,请稍后重试')
}
} }
// 取消操作 // 取消操作