221,222,223,224,227,228,229,230,231

This commit is contained in:
HuangXinQuan
2026-03-19 09:19:03 +08:00
parent 557f626c05
commit d058b30872
12 changed files with 387 additions and 189 deletions

View File

@@ -118,6 +118,7 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService {
newSchedule.setIsStopped(doctorSchedule.getIsStopped() != null ? doctorSchedule.getIsStopped() : false);
newSchedule.setStopReason(doctorSchedule.getStopReason() != null ? doctorSchedule.getStopReason() : "");
newSchedule.setDeptId(doctorSchedule.getDeptId());
newSchedule.setRegType(doctorSchedule.getRegType() != null ? doctorSchedule.getRegType() : 0);
newSchedule.setDoctorId(doctorSchedule.getDoctorId());
// 不设置id字段让数据库自动生成
@@ -183,6 +184,7 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService {
newSchedule.setIsStopped(doctorSchedule.getIsStopped() != null ? doctorSchedule.getIsStopped() : false);
newSchedule.setStopReason(doctorSchedule.getStopReason() != null ? doctorSchedule.getStopReason() : "");
newSchedule.setDeptId(doctorSchedule.getDeptId());
newSchedule.setRegType(doctorSchedule.getRegType() != null ? doctorSchedule.getRegType() : 0);
newSchedule.setDoctorId(doctorSchedule.getDoctorId());
// 不设置id字段让数据库自动生成
@@ -213,14 +215,48 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService {
}
}
@Transactional
@Override
public R<?> updateDoctorSchedule(DoctorSchedule doctorSchedule) {
if (ObjectUtil.isEmpty(doctorSchedule) || ObjectUtil.isEmpty(doctorSchedule.getId())) {
return R.fail("医生排班ID不能为空");
}
// 注意:此为核心更新,暂未处理号源池和号源槽的同步更新
int result = doctorScheduleMapper.updateDoctorSchedule(doctorSchedule);
return result > 0 ? R.ok(result) : R.fail("更新排班信息失败");
if (result <= 0) {
return R.fail("更新排班信息失败");
}
// 同步更新号源池,避免查询联表时医生/诊室等字段看起来“未更新”
boolean needSyncPool = doctorSchedule.getDoctorId() != null
|| doctorSchedule.getDoctor() != null
|| doctorSchedule.getClinic() != null
|| doctorSchedule.getStartTime() != null
|| doctorSchedule.getEndTime() != null
|| doctorSchedule.getLimitNumber() != null
|| doctorSchedule.getStopReason() != null
|| doctorSchedule.getRegType() != null
|| doctorSchedule.getRegisterFee() != null;
if (needSyncPool) {
schedulePoolService.lambdaUpdate()
.eq(SchedulePool::getScheduleId, doctorSchedule.getId())
.set(doctorSchedule.getDoctorId() != null, SchedulePool::getDoctorId, doctorSchedule.getDoctorId())
.set(doctorSchedule.getDoctor() != null, SchedulePool::getDoctorName, doctorSchedule.getDoctor())
.set(doctorSchedule.getClinic() != null, SchedulePool::getClinicRoom, doctorSchedule.getClinic())
.set(doctorSchedule.getStartTime() != null, SchedulePool::getStartTime, doctorSchedule.getStartTime())
.set(doctorSchedule.getEndTime() != null, SchedulePool::getEndTime, doctorSchedule.getEndTime())
.set(doctorSchedule.getLimitNumber() != null, SchedulePool::getTotalQuota,
doctorSchedule.getLimitNumber())
.set(doctorSchedule.getStopReason() != null, SchedulePool::getStopReason, doctorSchedule.getStopReason())
.set(doctorSchedule.getRegType() != null, SchedulePool::getRegType, String.valueOf(doctorSchedule.getRegType()))
.set(doctorSchedule.getRegisterFee() != null, SchedulePool::getFee, doctorSchedule.getRegisterFee() / 100.0)
.set(doctorSchedule.getRegisterFee() != null, SchedulePool::getInsurancePrice,
doctorSchedule.getRegisterFee() / 100.0)
.update();
}
return R.ok(result);
}
/**

View File

@@ -53,7 +53,6 @@ public class DoctorScheduleController {
/*
* 新增医生排班(带具体日期)
*
* */
@PostMapping("/add-with-date")
public R<?> addDoctorScheduleWithDate(@RequestBody DoctorSchedule doctorSchedule) {
@@ -77,7 +76,7 @@ public class DoctorScheduleController {
* */
@DeleteMapping("/delete/{doctorScheduleId}")
public R<?> removeDoctorSchedule(@PathVariable Integer doctorScheduleId){
return R.ok(doctorScheduleAppService.removeDoctorSchedule(doctorScheduleId));
return doctorScheduleAppService.removeDoctorSchedule(doctorScheduleId);
}
/*

View File

@@ -1,12 +1,14 @@
package com.openhis.web.basedatamanage.appservice.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R;
import com.core.common.utils.AssignSeqUtil;
import com.core.common.utils.MessageUtils;
import com.core.common.utils.StringUtils;
import com.openhis.administration.domain.Organization;
import com.openhis.administration.mapper.OrganizationMapper;
import com.openhis.administration.service.IOrganizationService;
import com.openhis.common.constant.CommonConstants;
import com.openhis.common.constant.PromptMsgConstant;
@@ -35,12 +37,15 @@ public class OrganizationAppServiceImpl implements IOrganizationAppService {
@Resource
private AssignSeqUtil assignSeqUtil;
@Resource
private OrganizationMapper organizationMapper;
@Override
public Page<OrganizationDto> getOrganizationTree(Integer pageNo, Integer pageSize, String name, Integer typeEnum,
List<String> classEnumList,
String sortField, String sortOrder, HttpServletRequest request) {
// 使用Page对象进行分页查询
// 使用 Page 对象进行分页查询
Page<Organization> page = new Page<>(pageNo, pageSize);
// 创建查询条件
@@ -54,7 +59,7 @@ public class OrganizationAppServiceImpl implements IOrganizationAppService {
queryWrapper.eq(Organization::getTypeEnum, typeEnum);
}
if (classEnumList != null && !classEnumList.isEmpty()) {
// 使用OR条件来匹配class_enum字段中包含任一值的记录
// 使用 OR 条件来匹配 class_enum 字段中包含任一值的记录
queryWrapper.and(wrapper -> {
for (int i = 0; i < classEnumList.size(); i++) {
String classEnum = classEnumList.get(i);
@@ -63,18 +68,18 @@ public class OrganizationAppServiceImpl implements IOrganizationAppService {
wrapper.and(subWrapper -> {
subWrapper.eq(Organization::getClassEnum, classEnum) // 精确匹配
.or() // 或者
.likeRight(Organization::getClassEnum, classEnum + ",") // 以"值,"开头
.likeRight(Organization::getClassEnum, classEnum + ",") // 以"值"开头
.or() // 或者
.likeLeft(Organization::getClassEnum, "," + classEnum) // 以",值"结尾
.or() // 或者
.like(Organization::getClassEnum, "," + classEnum + ","); // 在中间,被逗号包围
});
} else {
// 后续条件使用OR连接
// 后续条件使用 OR 连接
wrapper.or(subWrapper -> {
subWrapper.eq(Organization::getClassEnum, classEnum) // 精确匹配
.or() // 或者
.likeRight(Organization::getClassEnum, classEnum + ",") // 以"值,"开头
.likeRight(Organization::getClassEnum, classEnum + ",") // 以"值"开头
.or() // 或者
.likeLeft(Organization::getClassEnum, "," + classEnum) // 以",值"结尾
.or() // 或者
@@ -88,7 +93,7 @@ public class OrganizationAppServiceImpl implements IOrganizationAppService {
// 执行分页查询
Page<Organization> resultPage = organizationService.page(page, queryWrapper);
// 将查询结果转为DTO并构建树结构
// 将查询结果转为 DTO 并构建树结构
List<Organization> organizationList = resultPage.getRecords();
List<OrganizationDto> orgTree = buildTree(organizationList);
@@ -109,7 +114,7 @@ public class OrganizationAppServiceImpl implements IOrganizationAppService {
* @return tree
*/
private List<OrganizationDto> buildTree(List<Organization> records) {
// 按b_no的层级排序确保父节点先处理
// 按 b_no 的层级排序,确保父节点先处理
List<Organization> sortedRecords = records.stream()
.sorted(Comparator.comparingInt(r -> r.getBusNo().split("\\.").length)).collect(Collectors.toList());
@@ -131,7 +136,7 @@ public class OrganizationAppServiceImpl implements IOrganizationAppService {
// 根节点
tree.add(node);
} else {
// 获取父节点的b_no去掉最后一部分
// 获取父节点的 b_no去掉最后一部分
String parentBNo = String.join(".", Arrays.copyOf(parts, parts.length - 1));
OrganizationDto parent = nodeMap.get(parentBNo);
@@ -149,7 +154,7 @@ public class OrganizationAppServiceImpl implements IOrganizationAppService {
/**
* 机构信息详情
*
* @param orgId 机构信息id
* @param orgId 机构信息 id
* @return 机构信息详情
*/
@Override
@@ -159,7 +164,7 @@ public class OrganizationAppServiceImpl implements IOrganizationAppService {
return R.fail(MessageUtils.createMessage(PromptMsgConstant.Common.M00006, new Object[] { "机构信息" }));
}
// 转换为DTO对象确保数据格式一致
// 转换为 DTO 对象,确保数据格式一致
OrganizationDto organizationDto = new OrganizationDto();
BeanUtils.copyProperties(organization, organizationDto);
organizationDto
@@ -181,7 +186,7 @@ public class OrganizationAppServiceImpl implements IOrganizationAppService {
@Override
public R<?> addOrEditOrganization(OrganizationDto organizationDto) {
// 新增organization信息
// 新增 organization 信息
Organization organization = new Organization();
BeanUtils.copyProperties(organizationDto, organization);
@@ -191,9 +196,9 @@ public class OrganizationAppServiceImpl implements IOrganizationAppService {
} else {
// 活动标识:有效
organization.setActiveFlag(AccountStatus.ACTIVE.getValue());
// 采番bus_no三位
// 采番 bus_no 三位
String code = assignSeqUtil.getSeq(AssignSeqEnum.ORGANIZATION_BUS_NO.getPrefix(), 3);
// 如果传了上级科室 把当前的code拼到后边
// 如果传了上级科室 把当前的 code 拼到后边
if (StringUtils.isNotEmpty(organization.getBusNo())) {
organization.setBusNo(String.format(CommonConstants.Common.MONTAGE_FORMAT, organization.getBusNo(),
CommonConstants.Common.POINT, code));
@@ -203,7 +208,7 @@ public class OrganizationAppServiceImpl implements IOrganizationAppService {
// 生成待发送的机构信息
organizationService.save(organization);
}
// 返回机构id
// 返回机构 id
return R.ok(organization.getId(),
MessageUtils.createMessage(PromptMsgConstant.Common.M00004, new Object[] { "机构信息更新添加" }));
}
@@ -211,7 +216,7 @@ public class OrganizationAppServiceImpl implements IOrganizationAppService {
/**
* 删除机构
*
* @param orgIds 机构信息id
* @param orgIds 机构信息 id
* @return 操作结果
*/
@Override
@@ -232,7 +237,7 @@ public class OrganizationAppServiceImpl implements IOrganizationAppService {
/**
* 机构启用
*
* @param orgId 机构信息id
* @param orgId 机构信息 id
* @return 操作结果
*/
@Override
@@ -247,7 +252,7 @@ public class OrganizationAppServiceImpl implements IOrganizationAppService {
/**
* 机构停用
*
* @param orgId 机构信息id
* @param orgId 机构信息 id
* @return 操作结果
*/
@Override
@@ -299,38 +304,27 @@ public class OrganizationAppServiceImpl implements IOrganizationAppService {
*/
@Override
public R<?> getRegisterOrganizations(Integer pageNo, Integer pageSize, String name, String orgName) {
// 使用Page对象进行分页查询
// 使用 Page 对象进行分页查询
Page<Organization> page = new Page<>(pageNo != null ? pageNo : 1, pageSize != null ? pageSize : 10);
// 创建查询条件只查询register_flag为1的组织机构
LambdaQueryWrapper<Organization> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Organization::getRegisterFlag, 1); // 只获取挂号科室
queryWrapper.eq(Organization::getDeleteFlag, "0"); // 确保未删除
// 使用 Mapper 方法关联查询 sys_tenant 表获取租户名称
IPage<Organization> resultPage = organizationMapper.selectRegisterOrganizationsWithTenant(
page,
1, // register_flag = 1
"0", // delete_flag = '0'
name,
orgName
);
// 添加名称过滤条件
if (StringUtils.isNotEmpty(name)) {
queryWrapper.like(Organization::getName, name);
}
// 如果有机构名称筛选
if (StringUtils.isNotEmpty(orgName)) {
// 这里假设 orgName 是父机构名称,如果需要更复杂的关联查询可在此扩展
// 当前逻辑暂保持与原逻辑一致的过滤方式或根据需求调整
}
// 按编码排序
queryWrapper.orderByAsc(Organization::getBusNo);
// 执行分页查询
Page<Organization> resultPage = organizationService.page(page, queryWrapper);
// 转换为DTO对象并设置字典文本
// 转换为 DTO 对象并设置字典文本
List<OrganizationDto> organizationDtoList = resultPage.getRecords().stream().map(org -> {
OrganizationDto dto = new OrganizationDto();
BeanUtils.copyProperties(org, dto);
dto.setTypeEnum_dictText(EnumUtils.getInfoByValue(OrganizationType.class, dto.getTypeEnum()));
dto.setClassEnum_dictText(formatClassEnumDictText(dto.getClassEnum()));
dto.setActiveFlag_dictText(EnumUtils.getInfoByValue(AccountStatus.class, dto.getActiveFlag()));
// 设置租户名称
dto.setOrgName(org.getTenantName());
return dto;
}).collect(Collectors.toList());

View File

@@ -8,7 +8,6 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.ToString;
import java.util.ArrayList;
import java.util.List;
@@ -60,18 +59,20 @@ public class OrganizationDto {
private Integer displayOrder;
/** 子集合 */
@ToString.Exclude
private List<OrganizationDto> children = new ArrayList<>();
/** 挂号科室标记 */
private Integer registerFlag;
/** 科室位置 */
private String location;
/** 科室简介 */
private String intro;
/** 备注 */
private String remark;
/** 租户名称 */
private String orgName;
}

View File

@@ -280,24 +280,19 @@ public class DiagTreatMAppServiceImpl implements IDiagTreatMAppService {
}
/**
* 获取诊查项目列表医保类型为02
* 获取诊查项目列表医保类型为02,返回全量数据
*
* @param orgId 科室ID
* @param orgId 科室ID(兼容保留,不参与过滤)
* @return 诊查项目列表
*/
@Override
public R<?> getClinicItems(Long orgId) {
// 构建查询条件只查询医保类型为02费)的项目
// 构建查询条件只查询医保类型为02费)的项目,不按科室过滤
QueryWrapper<DiagnosisTreatmentDto> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("T2.yb_type", "02"); // 使用T2表的yb_type字段避免歧义
queryWrapper.eq("T1.delete_flag", "0"); // 只查询未删除的记录
queryWrapper.eq("T2.instance_table", "wor_activity_definition"); // 确保关联正确
// 如果提供了科室ID则过滤该科室的项目
if (orgId != null) {
queryWrapper.eq("T1.org_id", orgId); // 使用机构ID进行过滤
}
// 分页查询,设置一个较大的页大小以获取所有诊查项目
IPage<DiagnosisTreatmentDto> diseaseTreatmentPage
= activityDefinitionManageMapper.getDiseaseTreatmentPage(new Page<DiagnosisTreatmentDto>(1, 100), queryWrapper);

View File

@@ -23,6 +23,7 @@
is_stopped,
stop_reason,
dept_id,
reg_type,
doctor_id
<if test="createTime != null">, create_time</if>
<if test="updateTime != null">, update_time</if>
@@ -43,6 +44,7 @@
#{isStopped},
#{stopReason},
#{deptId},
#{regType},
#{doctorId}
<if test="createTime != null">, #{createTime}</if>
<if test="updateTime != null">, #{updateTime}</if>
@@ -68,6 +70,7 @@
<if test="isStopped != null">is_stopped = #{isStopped},</if>
<if test="stopReason != null">stop_reason = #{stopReason},</if>
<if test="deptId != null">dept_id = #{deptId},</if>
<if test="regType != null">reg_type = #{regType},</if>
<if test="doctorId != null">doctor_id = #{doctorId},</if>
<if test="updateTime != null">update_time = #{updateTime}</if>
</set>
@@ -97,12 +100,16 @@
ds.is_stopped AS is_stopped,
ds.stop_reason AS stop_reason,
ds.dept_id AS dept_id,
ds.reg_type AS reg_type,
sp.doctor_id AS doctor_id,
sp.schedule_date AS schedule_date
FROM adm_doctor_schedule ds
LEFT JOIN adm_schedule_pool sp ON sp.schedule_id = ds.id
AND sp.delete_flag = '0'
LEFT JOIN adm_organization org ON ds.dept_id = org.id
AND org.delete_flag = '0'
WHERE ds.dept_id = #{deptId}
AND ds.delete_flag = '0'
AND sp.schedule_date BETWEEN #{startDate}::date AND #{endDate}::date
ORDER BY sp.schedule_date, ds.time_period
</select>
@@ -129,12 +136,16 @@
ds.is_stopped AS is_stopped,
ds.stop_reason AS stop_reason,
ds.dept_id AS dept_id,
ds.reg_type AS reg_type,
sp.doctor_id AS doctor_id,
sp.schedule_date AS schedule_date
FROM adm_doctor_schedule ds
INNER JOIN adm_schedule_pool sp ON sp.schedule_id = ds.id
AND sp.delete_flag = '0'
LEFT JOIN adm_organization org ON ds.dept_id = org.id
AND org.delete_flag = '0'
WHERE sp.schedule_date = #{today}::date
AND ds.delete_flag = '0'
AND (ds.is_stopped = false OR ds.is_stopped IS NULL)
ORDER BY ds.time_period
</select>
@@ -161,12 +172,16 @@
ds.is_stopped AS is_stopped,
ds.stop_reason AS stop_reason,
ds.dept_id AS dept_id,
ds.reg_type AS reg_type,
sp.doctor_id AS doctor_id,
sp.schedule_date AS schedule_date
FROM adm_doctor_schedule ds
INNER JOIN adm_schedule_pool sp ON sp.schedule_id = ds.id
AND sp.delete_flag = '0'
LEFT JOIN adm_organization org ON ds.dept_id = org.id
AND org.delete_flag = '0'
WHERE sp.schedule_date = #{today}::date
AND ds.delete_flag = '0'
AND sp.doctor_id = #{doctorId}
AND (ds.is_stopped = false OR ds.is_stopped IS NULL)
ORDER BY ds.time_period

View File

@@ -1,6 +1,7 @@
package com.openhis.administration.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.core.common.core.domain.HisBaseEntity;
@@ -91,4 +92,8 @@ public class Organization extends HisBaseEntity {
/** 备注 */
private String remark;
/** 租户名称(从 sys_tenant 表关联查询,非数据库字段) */
@TableField(exist = false)
private String tenantName;
}

View File

@@ -1,6 +1,8 @@
package com.openhis.administration.mapper;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.openhis.administration.domain.Organization;
import com.openhis.administration.dto.OrgDataDto;
import org.apache.ibatis.annotations.Param;
@@ -9,7 +11,7 @@ import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 机构管理Mapper接口
* 机构管理 Mapper 接口
*
* @author system
* @date 2025-02-20
@@ -25,5 +27,21 @@ public interface OrganizationMapper extends BaseMapper<Organization> {
**/
List<OrgDataDto> searchOrgDataByHealth();
}
/**
* 分页查询挂号科室列表,关联租户表获取租户名称
* @param page 分页对象
* @param registerFlag 挂号标记
* @param deleteFlag 删除标记
* @param name 机构名称
* @param orgName 机构名称筛选
* @return 分页结果
*/
@InterceptorIgnore(tenantLine = "true")
IPage<Organization> selectRegisterOrganizationsWithTenant(
IPage<Organization> page,
@Param("registerFlag") Integer registerFlag,
@Param("deleteFlag") String deleteFlag,
@Param("name") String name,
@Param("orgName") String orgName
);
}

View File

@@ -72,6 +72,9 @@ public class DoctorSchedule extends HisBaseEntity {
/** 关联科室id */
private Long deptId;
/** 号别0=普通1=专家 */
private Integer regType;
/** 医生ID - 不映射到数据库表字段,仅作传输使用 */
private Long doctorId;

View File

@@ -61,6 +61,9 @@ public class DoctorScheduleWithDateDto {
/** 关联科室ID */
private Long deptId;
/** 号别0=普通1=专家 */
private Integer regType;
/** 医生姓名 */
private String doctorName;

View File

@@ -14,5 +14,54 @@
GROUP BY heal.offered_org_id)
</select>
<!-- 分页查询挂号科室列表,关联租户表获取租户名称 -->
<select id="selectRegisterOrganizationsWithTenant" resultType="com.openhis.administration.domain.Organization">
SELECT
org.id,
org.bus_no,
org.name,
org.active_flag,
org.type_enum,
org.class_enum,
org.py_str,
org.wb_str,
org.yb_no,
org.yb_name,
org.caty,
org.display_order,
org.medins_id,
org.medins_admdvs,
org.medins_type,
org.medins_lv,
org.def_doctor_id,
org.register_flag,
org.location,
org.intro,
org.remark,
org.tenant_id,
org.delete_flag,
org.create_by,
org.create_time,
org.update_by,
org.update_time,
st.tenant_name AS tenantName
FROM adm_organization org
LEFT JOIN sys_tenant st ON org.tenant_id = st.id
<where>
<if test="registerFlag != null">
AND org.register_flag = #{registerFlag}
</if>
<if test="deleteFlag != null">
AND org.delete_flag = #{deleteFlag}
</if>
<if test="name != null and name != ''">
AND org.name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="orgName != null and orgName != ''">
AND st.tenant_name = #{orgName}
</if>
</where>
ORDER BY org.bus_no ASC
</select>
</mapper>
</mapper>

View File

@@ -331,7 +331,7 @@
type="danger"
size="small"
@click="handleDeleteSchedule(scope.row)"
:disabled="!isEditMode"
:disabled="!isEditMode || isLastDraftRowInSlot(scope.row)"
>
删除
</el-button>
@@ -430,7 +430,7 @@ const currentDept = ref(null)
// 筛选参数
const filterParams = ref({
orgName: '演示医院',
orgName: '',
deptName: '测试内科',
clinicRoomName: '', // 添加诊室名称过滤字段
startDate: new Date(),
@@ -505,9 +505,9 @@ const fetchDoctorList = async (orgId) => {
console.log('原始医生数据:', doctorList)
// 医生数据按类型分组
const ordinaryDoctors = []
const expertDoctors = []
// 医生下拉统一展示:普通/专家都显示同一批医生
// 排班回显仍按 schedule.regType 过滤,不受这里影响
const allDoctors = []
doctorList.forEach(doctor => {
// 不再单独检查医生的org_id是否与当前科室匹配
@@ -520,29 +520,26 @@ const fetchDoctorList = async (orgId) => {
orgId: doctor.orgId
}
ordinaryDoctors.push(doctorInfo)
// 如果有专家类型的判断逻辑,可以在这里添加
// 示例:如果职称包含"主任"或"教授"等关键词,则归为专家
if (doctor.drProfttlCode && (doctor.drProfttlCode.includes('主任') || doctor.drProfttlCode.includes('教授'))) {
expertDoctors.push(doctorInfo)
}
allDoctors.push(doctorInfo)
})
// 更新医生选项
doctorOptions.value['普通'] = ordinaryDoctors
doctorOptions.value['专家'] = expertDoctors.length > 0 ? expertDoctors : [...ordinaryDoctors]
const uniqueDoctors = Array.from(
new Map(allDoctors.map(doc => [doc.id, doc])).values()
)
// 更新医生选项
doctorOptions.value['普通'] = uniqueDoctors
doctorOptions.value['专家'] = uniqueDoctors
console.log('最终的医生列表:', doctorOptions.value)
} else {
console.error('获取医生列表失败:', response.msg)
doctorOptions.value = {
'普通': [],
'专家': []
}
}
} catch (error) {
console.error('获取医生列表异常:', error)
doctorOptions.value = {
'普通': [],
'专家': []
@@ -693,7 +690,7 @@ const fetchClinicItems = async (deptId = null) => {
// 根据排班类型获取医生选项
const getDoctorOptions = (appointmentType) => {
return doctorOptions.value[appointmentType] || doctorOptions.value['普通']
return doctorOptions.value[appointmentType] || []
}
// 排班列表数据
@@ -772,10 +769,6 @@ const fetchOrganizationList = async () => {
}
});
// 添加默认的机构选项(如"中联医院"、"演示医院"等)
const defaultOrgs = ['中联医院', '演示医院']; // 可以根据需要添加更多默认选项
defaultOrgs.forEach(org => uniqueOrgNames.add(org));
// 将机构名称转换为下拉选项格式
organizationOptions.value = Array.from(uniqueOrgNames).map((orgName, index) => ({
id: index, // 使用索引作为ID因为我们只需要名称
@@ -783,36 +776,27 @@ const fetchOrganizationList = async () => {
orgName: orgName
}));
} else {
console.error('获取卫生机构列表失败:', response.msg)
// 如果获取数据失败,至少显示默认选项
const defaultOrgs = ['中联医院', '演示医院'];
organizationOptions.value = defaultOrgs.map((orgName, index) => ({
id: index,
name: orgName,
orgName: orgName
}));
organizationOptions.value = []
}
} catch (error) {
console.error('获取卫生机构列表失败:', error)
// 如果出现异常,至少显示默认选项
const defaultOrgs = ['中联医院', '演示医院'];
organizationOptions.value = defaultOrgs.map((orgName, index) => ({
id: index,
name: orgName,
orgName: orgName
}));
organizationOptions.value = []
}
}
// 获取科室列表(通用方法)
const fetchDeptList = async (apiFunction) => {
try {
// 复制查询参数
// 统一查询参数口径:后端使用 name 作为科室名称条件
const params = {
...queryParams.value,
pageNum: pagination.value.currentPage, // 修正为 pageNum
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 导致的性能问题,又保证了当前显示行的数据完整性
@@ -899,9 +883,9 @@ const getDeptList = async () => {
}
// 查询
const handleQuery = () => {
const handleQuery = async () => {
pagination.value.currentPage = 1;
fetchDeptList(getRegisterOrganizations); // 使用挂号科室API
await fetchDeptList(getRegisterOrganizations); // 使用挂号科室API
}
// 处理卫生机构选择变化
@@ -914,13 +898,14 @@ const handleOrgChange = async (orgName) => {
}
// 重置
const handleReset = () => {
const handleReset = async () => {
queryParams.value = {
orgName: '',
deptName: ''
}
pagination.value.currentPage = 1
getDeptList()
await fetchDepartmentOptions()
await getDeptList()
}
// 预约设置弹窗显示
@@ -1015,85 +1000,104 @@ const reloadScheduleData = async () => {
const row = currentDept.value
if (!row) return
// 使用组件级别的 workTimeConfig
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)
// 获取该科室在指定日期范围内的排班数据(联表查询 adm_schedule_pool 获取具体日期)
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 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 创建映射表,替代原来的 "星期-时段"
// 日期时段可能存在多条排班,按 key 聚合成数组
const scheduleMap = {}
deptSchedules.forEach(schedule => {
// scheduleDate 来自 adm_schedule_pool.schedule_date是具体的出诊日期
filteredSchedules.forEach(schedule => {
const key = `${schedule.scheduleDate}-${schedule.timePeriod}`
scheduleMap[key] = schedule
})
// 将现有排班数据合并到周计划中(以具体日期匹配)
weekSchedule.forEach(slot => {
// slot.date 是前端生成的具体日期yyyy-MM-dd与后端的 scheduleDate 一致
const key = `${slot.date}-${slot.timeSlot}`
const existingSchedule = scheduleMap[key]
if (existingSchedule) {
// 更新匹配的时段数据
slot.doctorName = existingSchedule.doctor
slot.doctorId = String(existingSchedule.doctorId) // 确保为字符串
// --- 容错逻辑校验医生ID是否在当前医生列表中 ---
const allAvailableDoctors = [...doctorOptions.value['普通'], ...doctorOptions.value['专家']];
const matchedDoctorById = allAvailableDoctors.find(doc => doc.id === slot.doctorId);
if (!matchedDoctorById) {
// 尝试根据医生姓名进行匹配
const matchedDoctorByName = allAvailableDoctors.find(doc => doc.label === slot.doctorName);
if (matchedDoctorByName) {
slot.doctorId = matchedDoctorByName.id;
console.warn(`【调试警告】排班记录doctorId ${existingSchedule.doctorId} (姓名: ${existingSchedule.doctor}) 与当前医生列表不匹配。已根据姓名修正为ID: ${matchedDoctorByName.id}`);
} else {
slot.doctorId = null;
slot.doctorName = '';
console.error(`【调试错误】排班记录doctorId ${existingSchedule.doctorId} 和医生姓名 "${existingSchedule.doctor}" 都未在当前医生列表中找到。`);
}
}
// --- 结束容错逻辑 ---
slot.room = existingSchedule.clinic
slot.startTime = existingSchedule.startTime
slot.endTime = existingSchedule.endTime
slot.maxNumber = existingSchedule.limitNumber
slot.appointmentItem = existingSchedule.registerItem
slot.registrationFee = existingSchedule.registerFee
slot.clinicItem = existingSchedule.diagnosisItem
slot.treatmentFee = existingSchedule.diagnosisFee
slot.online = existingSchedule.isOnline
slot.stopClinic = existingSchedule.isStopped
slot.stopReason = existingSchedule.stopReason
slot.backendId = existingSchedule.id // 保存后端ID
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('获取科室排班数据失败')
}
// 设置排班列表
console.log("【调试信息】即将渲染的排班数据:", JSON.parse(JSON.stringify(weekSchedule.filter(s => s.doctorId))));
scheduleList.value = weekSchedule
}
// 编辑
const handleEdit = async (row) => {
// 设置当前科室和模式
currentDept.value = row
@@ -1101,7 +1105,7 @@ const handleEdit = async (row) => {
scheduleDialogTitle.value = `编辑科室排班 - ${row.name || row.deptName}`
// 动态设置筛选参数
filterParams.value.orgName = row.orgName || '中联医院'
filterParams.value.orgName = row.orgName || row.organizationName || row.org || ''
filterParams.value.deptName = row.name || row.deptName
// 首先获取科室工作时间配置
@@ -1152,7 +1156,7 @@ const handleView = async (row) => {
scheduleDialogTitle.value = `查看科室排班 - ${row.name || row.deptName}`
// 动态设置筛选参数
filterParams.value.orgName = row.orgName || '中联医院'
filterParams.value.orgName = row.orgName || row.organizationName || row.org || ''
filterParams.value.deptName = row.name || row.deptName
// 首先获取科室工作时间配置
@@ -1249,20 +1253,14 @@ const searchClinicRooms = async (query) => {
const doctorMapping = {}
// 排班类型变化处理
const handleAppointmentTypeChange = () => {
// 当排班类型改变时,自动匹配对应的医生
const handleAppointmentTypeChange = async () => {
if (currentDept.value) {
await reloadScheduleData()
return
}
scheduleList.value.forEach(item => {
if (item.doctorName) {
// 获取新类型对应的医生列表
const newTypeDoctors = getDoctorOptions(filterParams.value.appointmentType)
// 检查当前医生是否在新类型列表中
const doctorExists = newTypeDoctors.some(doctor => doctor.value === item.doctorName)
if (!doctorExists) {
// 如果当前医生不在新类型列表中,则清空
item.doctorName = ''
}
}
item.doctorId = null
item.doctorName = ''
})
}
@@ -1302,14 +1300,84 @@ const handleDoctorChange = (selectedId, row) => {
row.doctorName = '';
return;
}
const allDoctors = [...doctorOptions.value['普通'], ...doctorOptions.value['专家']];
const selectedDoctor = allDoctors.find(doc => doc.id === String(selectedId));
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 newSchedule = {
@@ -1346,6 +1414,11 @@ const handleAddSchedule = (row) => {
// 删除排班
const handleDeleteSchedule = (row) => {
if (isLastDraftRowInSlot(row)) {
ElMessage.warning('当前时段需保留最后一条待填写记录,不能删除')
return
}
ElMessageBox.confirm('确定要删除这条排班记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
@@ -1354,15 +1427,17 @@ const handleDeleteSchedule = (row) => {
// 如果是已保存的记录有后端ID调用删除接口
if (row.backendId && !row.isNew) {
deleteDoctorSchedule(row.backendId).then(res => {
if (res.code === 200) {
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 || '删除失败')
ElMessage.error(res.msg || '删除失败,记录可能未真正删除')
}
}).catch(error => {
console.error('删除排班失败:', error)
@@ -1373,6 +1448,7 @@ const handleDeleteSchedule = (row) => {
const index = scheduleList.value.findIndex(item => item.id === row.id)
if (index !== -1) {
scheduleList.value.splice(index, 1)
ensureResetRowAfterDelete(row, index)
ElMessage.success('删除成功')
}
}
@@ -1445,7 +1521,7 @@ const handleEditSchedule = (row) => {
}
// 设置科室信息
filterParams.value.orgName = '中联医院' // 假设固定机构名称
filterParams.value.orgName = currentDept.value?.orgName || currentDept.value?.organizationName || currentDept.value?.org || ''
filterParams.value.deptName = '未知科室' // 这里可能需要根据实际情况设置
// 设置排班列表
@@ -1505,12 +1581,12 @@ const handleSave = async () => {
try {
// 验证必填字段 - 只验证用户真正填写了信息的记录
const filledSchedules = scheduleList.value.filter(item => {
// 只有当用户为某一行选择了医生,我们才认为他打算保存此条记录
return item.doctorName && item.doctorName.trim() !== '';
// 已有排班可能只有 doctorId没有 doctorName二者任一存在都视为可保存记录
return !!resolveDoctorName(item) || !!item.doctorId;
});
const incompleteSchedules = filledSchedules.filter(item => {
const isDoctorValid = item.doctorName && item.doctorName.trim() !== '';
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';
@@ -1526,10 +1602,13 @@ const handleSave = async () => {
// 转换数据格式
const schedulesToProcess = filledSchedules.map(item => {
const regTypeValue = Number(
item.regType ?? (filterParams.value.appointmentType === '专家' ? 1 : 0)
);
const scheduleData = {
weekday: item.weekday,
timePeriod: item.timeSlot,
doctor: item.doctorName,
doctor: resolveDoctorName(item),
doctorId: item.doctorId,
clinic: item.room,
startTime: item.startTime,
@@ -1543,6 +1622,7 @@ const handleSave = async () => {
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) {
@@ -2002,4 +2082,4 @@ onMounted(async () => {
:deep(.el-dialog__body) {
padding: 20px;
}
</style>
</style>