diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/IDoctorPhraseAppService.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/IDoctorPhraseAppService.java index c9e47334..efe928c1 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/IDoctorPhraseAppService.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/IDoctorPhraseAppService.java @@ -1,16 +1,14 @@ package com.openhis.web.doctorstation.appservice; -import com.core.common.core.domain.R; import com.openhis.template.domain.DoctorPhrase; +import java.util.List; + public interface IDoctorPhraseAppService { - R getDoctorPhraseList(); + List getDoctorPhraseList(); + List searchDoctorPhraseList(String phraseName, Integer phraseType); + Boolean addDoctorPhrase(DoctorPhrase doctorPhrase); + Boolean updateDoctorPhrase(DoctorPhrase doctorPhrase); + Boolean deleteDoctorPhrase(Integer doctorPhraseId); - R searchDoctorPhraseList(String phraseName ,Integer phraseType); - - R addDoctorPhrase(DoctorPhrase doctorPhrase); - - R updateDoctorPhrase(DoctorPhrase doctorPhrase); - - R deleteDoctorPhrase(Integer doctorPhraseId); } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorPhraseAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorPhraseAppServiceImpl.java index a3113190..64ba8ae2 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorPhraseAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorPhraseAppServiceImpl.java @@ -2,30 +2,71 @@ package com.openhis.web.doctorstation.appservice.impl; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -import com.core.common.core.domain.R; +import com.core.common.utils.SecurityUtils; +import com.openhis.common.enums.BindingType; import com.openhis.template.domain.DoctorPhrase; import com.openhis.template.service.IDoctorPhraseService; import com.openhis.web.doctorstation.appservice.IDoctorPhraseAppService; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; +@Slf4j @Service public class DoctorPhraseAppServiceImpl implements IDoctorPhraseAppService { @Resource private IDoctorPhraseService doctorPhraseService; - @Override - public R getDoctorPhraseList() { - List list = doctorPhraseService.list(); - return R.ok(list); +@Override +public List getDoctorPhraseList() { + Long orgId = SecurityUtils.getLoginUser().getOrgId(); + if (log.isDebugEnabled()) { + log.debug("orgId: {}", orgId); } + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + // 1. 获取当前登录用户信息 + Long userId = SecurityUtils.getUserId(); + // 2. 权限判定:非管理员才需要过滤 + // 如果是超级管理员,默认可以看到所有 + if (!SecurityUtils.isAdmin(userId)) { + // 3. 获取当前医生的科室编码 + String deptCode = ""; + if (orgId != null) { + deptCode = String.valueOf(orgId); + } + // final 变量用于 Lambda 表达式 + String finalDeptCode = deptCode; + // 4. 核心逻辑:三级数据共享 + wrapper.and(w -> w + .eq(DoctorPhrase::getStaffId, userId.intValue()) // 1. 个人的:只看 staffId 是我的 + .or(o -> o.eq(DoctorPhrase::getPhraseType, BindingType.HOSPITAL.getValue())) // 2. 全院的:类型为 3 + .or(o -> o.eq(DoctorPhrase::getPhraseType, BindingType.ORGANIZATION.getValue()) // 3. 科室的:类型为 2 且 科室编码匹配 + .eq(DoctorPhrase::getDeptCode, finalDeptCode)) + ); + } + // 5. 按排序号排序(可选优化,让常用语显示更整齐) + List list = doctorPhraseService.list(wrapper); + return list; +} @Override - public R searchDoctorPhraseList(String phraseName,Integer phraseType) { + public List searchDoctorPhraseList(String phraseName,Integer phraseType) { + // 1. 获取当前登录用户信息 + Long userId = SecurityUtils.getUserId(); + //2.获取到当前医生当前科室的id + Long orgId = SecurityUtils.getLoginUser().getOrgId(); + if (log.isDebugEnabled()) { + log.debug("Search phrase - orgId: {}, phraseName: {}, phraseType: {}", orgId, phraseName, phraseType); + } + String deptCode = ""; + if (orgId != null) { + deptCode = String.valueOf(orgId); + } + String finalDeptCode = deptCode; LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); if (phraseName !=null && ObjectUtil.isNotEmpty(phraseName)) { wrapper.like(DoctorPhrase::getPhraseName, phraseName); @@ -33,50 +74,135 @@ public class DoctorPhraseAppServiceImpl implements IDoctorPhraseAppService { if (phraseType !=null && ObjectUtil.isNotEmpty(phraseType)) { wrapper.eq(DoctorPhrase::getPhraseType, phraseType); } + Long currentUserId = SecurityUtils.getUserId(); + if (!SecurityUtils.isAdmin(currentUserId)) { + // 建议统一使用 staffId 进行业务隔离 + wrapper.and(w -> w + .eq(DoctorPhrase::getStaffId, userId.intValue()) // 1. 个人的:只看 staffId 是我的 + .or(o -> o.eq(DoctorPhrase::getPhraseType, BindingType.HOSPITAL.getValue())) // 2. 全院的:类型为 3 + .or(o -> o.eq(DoctorPhrase::getPhraseType, BindingType.ORGANIZATION.getValue()) // 3. 科室的:类型为 2 且 科室编码匹配 + .eq(DoctorPhrase::getDeptCode, finalDeptCode)) + ); + } //2.查询 List list = doctorPhraseService.list(wrapper); - return R.ok(list); + return list; } @Override - public R addDoctorPhrase(DoctorPhrase doctorPhrase) { - //1.数据校验 - if(ObjectUtil.isEmpty(doctorPhrase)){ - return R.fail("医生常用语不能为空"); + public Boolean addDoctorPhrase(DoctorPhrase doctorPhrase) { + // 1. 基础校验 + if (ObjectUtil.isEmpty(doctorPhrase) || ObjectUtil.isEmpty(doctorPhrase.getPhraseName())) { + throw new IllegalArgumentException("新增失败:常用语名称不能为空"); } - //2.名称唯一性校验 - LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); - wrapper.eq(DoctorPhrase::getPhraseName, doctorPhrase.getPhraseName()); - DoctorPhrase one = doctorPhraseService.getOne(wrapper); - if(ObjectUtil.isNotEmpty(one)){ - return R.fail("该名称已经存在"); + + Long currentUserId = SecurityUtils.getUserId(); + + /* + * 如果前端没传类型,必须给个默认值(比如 1-个人) + * 否则存成 NULL,查询列表时会被过滤掉,导致"新增了却看不见" + */ + if (doctorPhrase.getPhraseType() == null) { + doctorPhrase.setPhraseType(BindingType.PERSONAL.getValue()); } - //3.新增 - boolean save = doctorPhraseService.save(doctorPhrase); - System.out.println(save); - return R.ok(save); + + // 2. 注入归属信息 + doctorPhrase.setStaffId(currentUserId.intValue()); + doctorPhrase.setCreatorId(currentUserId.intValue()); + + // 注入科室 (处理 null 情况) + Long orgId = SecurityUtils.getLoginUser().getOrgId(); + if (orgId != null) { + // 检查dept_code字段长度,避免数据库错误 + String deptCode = String.valueOf(orgId); + if (deptCode.length() > 50) { // 假设字段长度为50,根据实际情况调整 + // 如果超过字段长度限制,可以考虑截断或抛出有意义的错误 + throw new IllegalArgumentException("科室ID过长,无法保存"); + } + doctorPhrase.setDeptCode(deptCode); + } + + // =========== 【修复点 2】查重范围限制在"个人" =========== + // 使用 QueryWrapper 而不是 UpdateWrapper + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(DoctorPhrase::getPhraseName, doctorPhrase.getPhraseName()) + .eq(DoctorPhrase::getStaffId, currentUserId.intValue()); // 重点:只查自己名下的! + + if (doctorPhraseService.count(queryWrapper) > 0) { + throw new IllegalArgumentException("新增失败:您已存在同名的常用语"); + } + + return doctorPhraseService.save(doctorPhrase); } @Override - public R updateDoctorPhrase(DoctorPhrase doctorPhrase) { - //1.数据校验 - if(ObjectUtil.isEmpty(doctorPhrase)){ - return R.fail("医生常用语不能为空"); + public Boolean updateDoctorPhrase(DoctorPhrase doctorPhrase) { + // 1. 基础校验 + if (ObjectUtil.isEmpty(doctorPhrase) || doctorPhrase.getId() == null) { + throw new IllegalArgumentException("修改失败:ID不能为空"); } - //2.更新 - boolean updateById = doctorPhraseService.updateById(doctorPhrase); - return R.ok(updateById); + + // 2. 查旧数据 + DoctorPhrase original = doctorPhraseService.getById(doctorPhrase.getId()); + if (original == null) { + throw new IllegalArgumentException("修改失败:该常用语不存在或已被删除"); + } + + Long currentUserId = SecurityUtils.getUserId(); + + // 3. 【权限校验优化】 + if (!SecurityUtils.isAdmin(currentUserId)) { + // 规则 A:严禁修改全院公共模板 (Type=3) + // 这一步是关键!之前你的代码漏了这里,所以普通医生可能改动全院模板 + if (BindingType.HOSPITAL.getValue().equals(original.getPhraseType())) { + throw new SecurityException("无权操作:全院公共常用语仅限管理员修改"); + } + + // 规则 B:严禁修改他人的模板 + if (!original.getStaffId().equals(currentUserId.intValue())) { + throw new SecurityException("无权操作:您只能修改自己创建的常用语"); + } + } + + // 4. 数据保护:防止篡改归属 + doctorPhrase.setStaffId(original.getStaffId()); + doctorPhrase.setCreatorId(original.getCreatorId()); + Long orgId = SecurityUtils.getLoginUser().getOrgId(); + if (orgId != null) { + // 检查dept_code字段长度,避免数据库错误 + String deptCode = String.valueOf(orgId); + if (deptCode.length() > 50) { // 假设字段长度为50,根据实际情况调整 + throw new IllegalArgumentException("科室ID过长,无法保存"); + } + doctorPhrase.setDeptCode(deptCode); + } + + return doctorPhraseService.updateById(doctorPhrase); } @Override - public R deleteDoctorPhrase(Integer doctorPhraseId) { - //1.数据校验 - if(doctorPhraseId == null){ - return R.fail("ID不能为空"); + public Boolean deleteDoctorPhrase(Integer doctorPhraseId) { + if (doctorPhraseId == null) { + throw new IllegalArgumentException("删除失败:ID不能为空"); } - //2.删除 - boolean removeById = doctorPhraseService.removeById(doctorPhraseId); - return R.ok(removeById); + + DoctorPhrase original = doctorPhraseService.getById(doctorPhraseId); + if (original == null) { + throw new IllegalArgumentException("删除失败:数据不存在"); + } + + Long currentUserId = SecurityUtils.getUserId(); + + // 权限校验 + if (!SecurityUtils.isAdmin(currentUserId)) { + if (BindingType.HOSPITAL.getValue().equals(original.getPhraseType())) { + throw new SecurityException("无权操作:全院公共常用语仅限管理员删除"); + } + if (!original.getStaffId().equals(currentUserId.intValue())) { + throw new SecurityException("无权操作:您只能删除自己创建的常用语"); + } + } + return doctorPhraseService.removeById(doctorPhraseId); } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationMainAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationMainAppServiceImpl.java index 7485cd07..75d0422e 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationMainAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationMainAppServiceImpl.java @@ -111,6 +111,8 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer e.setAge(e.getBirthDate() != null ? AgeCalculatorUtil.getAge(e.getBirthDate()) : ""); // 就诊状态 e.setStatusEnum_enumText(EnumUtils.getInfoByValue(EncounterStatus.class, e.getStatusEnum())); + // 初复诊 + e.setFirstEnum_enumText(EnumUtils.getInfoByValue(EncounterType.class, e.getFirstEnum())); }); return patientInfo; } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/controller/DoctorPhraseController.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/controller/DoctorPhraseController.java index 0e4b05a2..a1d959d6 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/controller/DoctorPhraseController.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/controller/DoctorPhraseController.java @@ -1,6 +1,7 @@ package com.openhis.web.doctorstation.controller; import com.core.common.core.domain.R; +import com.openhis.common.enums.BindingType; import com.openhis.template.domain.DoctorPhrase; import com.openhis.web.doctorstation.appservice.IDoctorPhraseAppService; import lombok.extern.slf4j.Slf4j; @@ -23,7 +24,13 @@ public class DoctorPhraseController { */ @GetMapping("/list") public R getDoctorPhraseList(){ - return R.ok(doctorPhraseAppService.getDoctorPhraseList()); + try { + return R.ok(doctorPhraseAppService.getDoctorPhraseList()); + } catch (Exception e) { + // 系统异常,使用error级别日志 + log.error("获取医生常用语列表系统异常", e); + return R.fail("获取医生常用语列表失败: " + e.getMessage()); + } } /** @@ -37,7 +44,13 @@ public class DoctorPhraseController { @RequestParam(required = false) Integer phraseType, @RequestParam(required = false) String phraseName ){ - return R.ok(doctorPhraseAppService.searchDoctorPhraseList(phraseName,phraseType)); + try { + return R.ok(doctorPhraseAppService.searchDoctorPhraseList(phraseName, phraseType)); + } catch (Exception e) { + // 系统异常,使用error级别日志 + log.error("查询医生常用语系统异常", e); + return R.fail("查询医生常用语失败: " + e.getMessage()); + } } /** @@ -48,7 +61,22 @@ public class DoctorPhraseController { */ @PostMapping("/add") public R addDoctorPhrase(@RequestBody DoctorPhrase doctorPhrase){ - return R.ok(doctorPhraseAppService.addDoctorPhrase(doctorPhrase)); + try { + Boolean result = doctorPhraseAppService.addDoctorPhrase(doctorPhrase); + if (result != null && result) { + return R.ok("新增成功"); + } else { + return R.fail("新增失败"); + } + } catch (IllegalArgumentException e) { + // 参数错误异常,使用warn级别日志 + log.warn("新增医生常用语参数错误: {}", e.getMessage()); + return R.fail(e.getMessage()); + } catch (Exception e) { + // 系统异常,使用error级别日志 + log.error("新增医生常用语系统异常", e); + return R.fail("新增失败: " + e.getMessage()); + } } /** @@ -59,7 +87,26 @@ public class DoctorPhraseController { */ @PutMapping("/update") public R updateDoctorPhrase(@RequestBody DoctorPhrase doctorPhrase){ - return R.ok(doctorPhraseAppService.updateDoctorPhrase(doctorPhrase)); + try { + Boolean result = doctorPhraseAppService.updateDoctorPhrase(doctorPhrase); + if (result != null && result) { + return R.ok("更新成功"); + } else { + return R.fail("更新失败"); + } + } catch (IllegalArgumentException e) { + // 参数错误异常,使用warn级别日志 + log.warn("更新医生常用语参数错误: {}", e.getMessage()); + return R.fail(e.getMessage()); + } catch (SecurityException e) { + // 权限相关异常,使用warn级别日志 + log.warn("更新医生常用语权限异常: {}", e.getMessage()); + return R.fail(e.getMessage()); + } catch (Exception e) { + // 系统异常,使用error级别日志 + log.error("更新医生常用语系统异常", e); + return R.fail("更新失败: " + e.getMessage()); + } } /** @@ -70,7 +117,26 @@ public class DoctorPhraseController { */ @DeleteMapping("/delete/{DoctorPhraseId}") public R deleteDoctorPhrase(@PathVariable Integer DoctorPhraseId){ - return R.ok(doctorPhraseAppService.deleteDoctorPhrase(DoctorPhraseId)); + try { + Boolean result = doctorPhraseAppService.deleteDoctorPhrase(DoctorPhraseId); + if (result != null && result) { + return R.ok("删除成功"); + } else { + return R.fail("删除失败"); + } + } catch (IllegalArgumentException e) { + // 参数错误异常,使用warn级别日志 + log.warn("删除医生常用语参数错误: {}", e.getMessage()); + return R.fail(e.getMessage()); + } catch (SecurityException e) { + // 权限相关异常,使用warn级别日志 + log.warn("删除医生常用语权限异常: {}", e.getMessage()); + return R.fail(e.getMessage()); + } catch (Exception e) { + // 系统异常,使用error级别日志 + log.error("删除医生常用语系统异常", e); + return R.fail("删除失败: " + e.getMessage()); + } } } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/PatientInfoDto.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/PatientInfoDto.java index 3968f6d6..b67c4343 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/PatientInfoDto.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/PatientInfoDto.java @@ -133,4 +133,10 @@ public class PatientInfoDto { * 过号时间 */ private Date missedTime; + + /** + * 初复诊标识 + */ + private Integer firstEnum; + private String firstEnum_enumText; } diff --git a/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationMainAppMapper.xml b/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationMainAppMapper.xml index e37cd888..34c04c8e 100644 --- a/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationMainAppMapper.xml +++ b/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationMainAppMapper.xml @@ -25,7 +25,9 @@ T10.jz_practitioner_user_id, T10.bus_no, T10.identifier_no, - T10.missed_time + T10.missed_time, + T10.first_enum, + T10.first_enum_enumText from ( SELECT T1.tenant_id AS tenant_id, @@ -52,7 +54,13 @@ T1.organization_id AS org_id, T8.bus_no AS bus_no, T9.identifier_no AS identifier_no, - T1.missed_time AS missed_time + T1.missed_time AS missed_time, + T1.first_enum AS first_enum, + CASE + WHEN T1.first_enum = 1 THEN '初诊' + WHEN T1.first_enum = 2 THEN '复诊' + ELSE NULL + END AS first_enum_enumText FROM adm_encounter AS T1 LEFT JOIN adm_organization AS T2 ON T1.organization_id = T2.ID AND T2.delete_flag = '0' LEFT JOIN adm_healthcare_service AS T3 ON T1.service_type_id = T3.ID AND T3.delete_flag = '0' diff --git a/openhis-ui-vue3/src/components/PatientList/index.vue b/openhis-ui-vue3/src/components/PatientList/index.vue index 25931875..7b83d75c 100644 --- a/openhis-ui-vue3/src/components/PatientList/index.vue +++ b/openhis-ui-vue3/src/components/PatientList/index.vue @@ -12,7 +12,7 @@ @keyup.enter="handleSearch" :prefix-icon="Search" /> - + @@ -36,7 +36,7 @@
- + {{ item.patientName || '-' }}
diff --git a/openhis-ui-vue3/src/components/TableLayout/Filter.vue b/openhis-ui-vue3/src/components/TableLayout/Filter.vue index a9c1711d..c42fed9f 100644 --- a/openhis-ui-vue3/src/components/TableLayout/Filter.vue +++ b/openhis-ui-vue3/src/components/TableLayout/Filter.vue @@ -23,7 +23,7 @@ 搜索 重置 - + {{ isExpanded ? '收起' : '展开' }} diff --git a/openhis-ui-vue3/src/template/inHospitalRecord.vue b/openhis-ui-vue3/src/template/inHospitalRecord.vue index 0da2a4cb..2b33b258 100644 --- a/openhis-ui-vue3/src/template/inHospitalRecord.vue +++ b/openhis-ui-vue3/src/template/inHospitalRecord.vue @@ -565,6 +565,26 @@ const rules = reactive({ chiefComplaint: [{ required: true, message: '请填写主诉', trigger: ['blur', 'submit'] }], }); +// 监听 patientInfo 变化,更新表单数据 +watch( + () => props.patientInfo, + (newPatientInfo) => { + if (newPatientInfo) { + // 更新基础信息 + formData.patientName = newPatientInfo.patientName || newPatientInfo.name || ''; + formData.hospitalNo = newPatientInfo.busNo || newPatientInfo.hospitalNo || ''; + formData.gender = newPatientInfo.genderEnum_enumText || newPatientInfo.gender || ''; + formData.age = newPatientInfo.age || ''; + formData.nation = newPatientInfo.nation || ''; + formData.occupation = newPatientInfo.profession || ''; + formData.marriage = newPatientInfo.maritalStatus || ''; + formData.birthplace = newPatientInfo.birthPlace || ''; + // 可以根据需要更新更多字段 + } + }, + { deep: true, immediate: true } +); + // 生命周期 onMounted(() => { // 初始化记录时间为当前时间 @@ -578,16 +598,16 @@ onMounted(() => { formData.signDate = formatDateTime(new Date()); } if (!formData.patientName) { - formData.patientName = patient?.patientName || ''; + formData.patientName = patient?.patientName || patient?.name || ''; } if (!formData.gender) { - formData.gender = patient?.genderEnum_enumText || ''; + formData.gender = patient?.genderEnum_enumText || patient?.gender || ''; } if (!formData.age) { formData.age = patient?.age || ''; } if (!formData.hospitalNo) { - formData.hospitalNo = patient?.busNo || ''; + formData.hospitalNo = patient?.busNo || patient?.hospitalNo || ''; } }); diff --git a/openhis-ui-vue3/src/utils/his.js b/openhis-ui-vue3/src/utils/his.js index 48b59c36..afef3ce3 100644 --- a/openhis-ui-vue3/src/utils/his.js +++ b/openhis-ui-vue3/src/utils/his.js @@ -334,11 +334,13 @@ export function handleColor(a, b, status) { */ export function getPrinterList() { try { - const printerList = - window.hiprint && window.hiprint.hiwebSocket - ? window.hiprint.hiwebSocket.getPrinterList() - : []; - return printerList || []; + if (window.hiprint && window.hiprint.hiwebSocket && window.hiprint.hiwebSocket.connected) { + const printerList = window.hiprint.hiwebSocket.getPrinterList(); + return printerList || []; + } else { + console.warn('打印服务未连接,返回空打印机列表'); + return []; + } } catch (error) { console.error('获取打印机列表失败:', error); return []; diff --git a/openhis-ui-vue3/src/utils/printUtils.js b/openhis-ui-vue3/src/utils/printUtils.js index 9a760cbf..0deeb324 100644 --- a/openhis-ui-vue3/src/utils/printUtils.js +++ b/openhis-ui-vue3/src/utils/printUtils.js @@ -151,11 +151,13 @@ export const PRINT_TEMPLATE = { */ export function getPrinterList() { try { - const printerList = - window.hiprint && window.hiprint.hiwebSocket - ? window.hiprint.hiwebSocket.getPrinterList() - : []; - return printerList || []; + if (window.hiprint && window.hiprint.hiwebSocket && window.hiprint.hiwebSocket.connected) { + const printerList = window.hiprint.hiwebSocket.getPrinterList(); + return printerList || []; + } else { + console.warn('打印服务未连接,返回空打印机列表'); + return []; + } } catch (error) { console.error('获取打印机列表失败:', error); return []; diff --git a/openhis-ui-vue3/src/views/basicmanage/implementDepartment/index.vue b/openhis-ui-vue3/src/views/basicmanage/implementDepartment/index.vue index aa16cfa1..60881b37 100644 --- a/openhis-ui-vue3/src/views/basicmanage/implementDepartment/index.vue +++ b/openhis-ui-vue3/src/views/basicmanage/implementDepartment/index.vue @@ -225,7 +225,15 @@ function getList() { loading.value = true; getDiagnosisTreatmentList(queryParams.value).then((res) => { loading.value = false; - catagoryList.value = res.data.records; + catagoryList.value = res.data.records.map(record => { + // 为每一行初始化 filteredOptions,确保显示框能正确显示项目名称 + const filteredOptions = allImplementDepartmentList.value.slice(0, 100); + return { + ...record, + loading: false, + filteredOptions: filteredOptions + }; + }); total.value = res.data.total; }); } diff --git a/openhis-ui-vue3/src/views/basicmanage/ordersCombination/index.vue b/openhis-ui-vue3/src/views/basicmanage/ordersCombination/index.vue index 77bf5fb5..4c70b6a1 100644 --- a/openhis-ui-vue3/src/views/basicmanage/ordersCombination/index.vue +++ b/openhis-ui-vue3/src/views/basicmanage/ordersCombination/index.vue @@ -52,8 +52,8 @@ diff --git a/openhis-ui-vue3/src/views/basicservices/registrationfee/index.vue b/openhis-ui-vue3/src/views/basicservices/registrationfee/index.vue index 74067a23..96f5ed3b 100644 --- a/openhis-ui-vue3/src/views/basicservices/registrationfee/index.vue +++ b/openhis-ui-vue3/src/views/basicservices/registrationfee/index.vue @@ -776,7 +776,8 @@ const transformFormData = (form) => { extraDetails, comment, busNo, - }, + practitionerId: form.value.practitionerId, + }, chargeItemDefinitionFormData: { id, chargeName, @@ -833,7 +834,8 @@ const transformFormEditData = (form) => { extraDetails, comment, busNo, - }, + practitionerId: form.value.practitionerId, + }, }; }; getRegistrationfeeTypeList(); diff --git a/openhis-ui-vue3/src/views/doctorstation/components/hospitalizationEmr/components/history.vue b/openhis-ui-vue3/src/views/doctorstation/components/hospitalizationEmr/components/history.vue index 31c7f375..4608d09f 100644 --- a/openhis-ui-vue3/src/views/doctorstation/components/hospitalizationEmr/components/history.vue +++ b/openhis-ui-vue3/src/views/doctorstation/components/hospitalizationEmr/components/history.vue @@ -42,7 +42,6 @@ import {defineEmits, ref, unref} from 'vue'; import {deleteRecord, getRecordByEncounterIdList} from '../api'; import {ElMessage} from 'element-plus'; import {patientInfo} from '../../store/patient.js'; -import apiRequestManager from '@/utils/apiRequestManager.js'; const emits = defineEmits(['historyClick']); const props = defineProps({ @@ -68,30 +67,15 @@ const queryParams = ref({ isPage: 0, }); const historyData = ref([]); -// 防止重复加载的标志 -let isLoadingHistory = false; - const queryList = async () => { - // 防止重复加载 - if (isLoadingHistory) { - console.log('History data is already loading, skipping duplicate call'); - return; - } - - isLoadingHistory = true; - try { if (patientInfo.value.encounterId && unref(definitionId) && unref(definitionId) !== '') { - const res = await apiRequestManager.execute( - getRecordByEncounterIdList, - '/document/record/getRecordByEncounterIdList', - { - isPage: 0, // 确保参数一致,便于去重 - encounterId: patientInfo.value.encounterId, - patientId: patientInfo.value.patientId, - definitionId: unref(definitionId), - } - ); + const res = await getRecordByEncounterIdList({ + ...queryParams.value, + encounterId: patientInfo.value.encounterId, + patientId: patientInfo.value.patientId, + definitionId: unref(definitionId), + }); historyData.value = res.data || []; } else { historyData.value = []; @@ -99,8 +83,6 @@ const queryList = async () => { } catch (error) { // ElMessage.error(' 获取模板树失败 '); historyData.value = []; - } finally { - isLoadingHistory = false; // 重置加载标志 } }; const handleNodeClick = (data) => { diff --git a/openhis-ui-vue3/src/views/doctorstation/components/hospitalizationEmr/index.vue b/openhis-ui-vue3/src/views/doctorstation/components/hospitalizationEmr/index.vue index f05eeac2..300b6cf5 100644 --- a/openhis-ui-vue3/src/views/doctorstation/components/hospitalizationEmr/index.vue +++ b/openhis-ui-vue3/src/views/doctorstation/components/hospitalizationEmr/index.vue @@ -103,7 +103,6 @@ import dayjs from 'dayjs'; // 打印工具 import {PRINT_TEMPLATE, simplePrint} from '@/utils/printUtils.js'; import {getEncounterDiagnosis} from '../api'; -import apiRequestManager from '@/utils/apiRequestManager.js'; import History from './components/history'; import Template from './components/template'; import TemplateEdit from './components/templateEdit.vue'; @@ -206,7 +205,7 @@ const handleNodeClick = (data, node) => { // 选择任何病历模板后,都加载该病历类型的最新历史记录 if (node.isLeaf && props.patientInfo && props.patientInfo.patientId) { - debouncedLoadLatestMedicalRecord(); + loadLatestMedicalRecord(); } }, 100); }); @@ -280,7 +279,7 @@ const handleSubmitOk = async (data) => { // 等待历史记录列表更新后,重新加载最新病历并更新选中状态 setTimeout(() => { - debouncedLoadLatestMedicalRecord(); + loadLatestMedicalRecord(); }, 100); } catch (error) { ElMessage.error('提交失败'); @@ -411,7 +410,7 @@ const selectOutpatientMedicalRecordTemplate = async () => { // 等待模板加载完成,然后获取并回显最新病历数据 setTimeout(() => { historyRef.value?.queryList(); - debouncedLoadLatestMedicalRecord(); + loadLatestMedicalRecord(); }, 500); }); } else { @@ -422,36 +421,19 @@ const selectOutpatientMedicalRecordTemplate = async () => { // 当前选中的历史病历ID,用于在History组件中高亮显示 const selectedHistoryRecordId = ref(''); -import { debounce } from 'lodash-es'; - -// 防止重复加载的标志 -let isLoadingLatestRecord = false; - // 加载最新的病历数据并回显 const loadLatestMedicalRecord = async () => { if (!patientInfo.value.encounterId || !currentSelectTemplate.value.id) return; - // 防止重复加载 - if (isLoadingLatestRecord) { - console.log('Latest medical record is already loading, skipping duplicate call'); - return; - } - - isLoadingLatestRecord = true; loading.value = true; - try { // 获取患者的历史病历记录 - const res = await apiRequestManager.execute( - getRecordByEncounterIdList, - '/document/record/getRecordByEncounterIdList', - { - isPage: 0, - encounterId: patientInfo.value.encounterId, - patientId: patientInfo.value.patientId, - definitionId: currentSelectTemplate.value.id, - } - ); + const res = await getRecordByEncounterIdList({ + isPage: 0, + encounterId: patientInfo.value.encounterId, + patientId: patientInfo.value.patientId, + definitionId: currentSelectTemplate.value.id, + }); const historyRecords = res.data || []; if (historyRecords.length > 0) { @@ -537,12 +519,8 @@ const loadLatestMedicalRecord = async () => { }); } finally { loading.value = false; - isLoadingLatestRecord = false; // 重置加载标志 } }; - -// 防抖版本的加载最新病历数据函数 -const debouncedLoadLatestMedicalRecord = debounce(loadLatestMedicalRecord, 300); const templateRef = ref(null); const handleTemplateClick = (data) => { @@ -772,7 +750,7 @@ const selectDefaultTemplate = () => { // 直接加载最新病历数据,不再使用额外的setTimeout延迟 // 因为handleNodeClick中已经有nextTick和setTimeout处理组件渲染 - debouncedLoadLatestMedicalRecord(); + loadLatestMedicalRecord(); }); } else { console.log('未找到门诊病历模板'); diff --git a/openhis-ui-vue3/src/views/doctorstation/components/inspection/inspectionApplication.vue b/openhis-ui-vue3/src/views/doctorstation/components/inspection/inspectionApplication.vue index 55cc219c..9d761a01 100644 --- a/openhis-ui-vue3/src/views/doctorstation/components/inspection/inspectionApplication.vue +++ b/openhis-ui-vue3/src/views/doctorstation/components/inspection/inspectionApplication.vue @@ -59,8 +59,8 @@ @@ -379,7 +379,7 @@
已选择 - 清空 + 清空
@@ -393,7 +393,7 @@ {{ item.itemName }} ¥{{ item.itemPrice }} { // 所有数据(用于客户端分页处理) const allData = ref([]) +// 获取医生常用语列表数据 // 获取医生常用语列表数据 const fetchDoctorPhraseList = async () => { try { const response = await getDoctorPhraseList() - // 处理后端返回的数据结构:data.data - if (response.code === 200 && response.data && response.data.data) { + // 【关键修改】去掉 response.data.data,直接取 response.data + if (response.code === 200 && response.data) { // 按照sortNo由小到大排序,保证列表顺序正确 - allData.value = response.data.data.sort((a, b) => a.sortNo - b.sortNo) + allData.value = response.data.sort((a, b) => a.sortNo - b.sortNo) total.value = allData.value.length // 执行客户端分页逻辑 applyPagination() @@ -285,7 +286,7 @@ const fetchDoctorPhraseList = async () => { total.value = 0 } } catch (error) { - console.error('获取列表失败:', error) // 增加控制台日志便于调试 + console.error('获取列表失败:', error) ElMessage.error('获取数据失败: 网络请求错误') allData.value = [] total.value = 0 @@ -322,19 +323,18 @@ const handleCurrentChange = (val) => { applyPagination() } +// 搜索功能核心方法 // 搜索功能核心方法 const handleSearch = async () => { try { - // searchScope可能是null(未选择)、1=个人,2=科室,3=全院 const phraseType = searchScope.value === null ? undefined : searchScope.value - // 调用搜索接口:phraseName, phraseType const response = await searchDoctorPhraseList(searchKeyword.value, phraseType) - if (response.code === 200 && response.data && response.data.data) { - // 按照sortNo由小到大排序 - allData.value = response.data.data.sort((a, b) => a.sortNo - b.sortNo) + // 【关键修改】去掉 response.data.data,直接取 response.data + if (response.code === 200 && response.data) { + allData.value = response.data.sort((a, b) => a.sortNo - b.sortNo) total.value = allData.value.length - currentPage.value = 1 // 搜索后重置到第一页 - applyPagination() // 应用分页 + currentPage.value = 1 + applyPagination() } else { ElMessage.error('搜索失败: ' + (response.msg || '未知错误')) allData.value = [] @@ -349,20 +349,30 @@ const handleSearch = async () => { } // 打开新增模态框方法 +// index.vue + const showAddDialog = () => { - // 重置表单数据 + // 1. 算出当前最大的排序号 + // 如果列表是空的,就从 1 开始;如果不空,取第一条(因为我们排过序了)或遍历找最大值 + let maxSortNo = 0 + if (allData.value && allData.value.length > 0) { + // 既然 allData 已经按 sortNo 排序了,那最后一个就是最大的? + // 或者保险起见,用 Math.max 算一下 + maxSortNo = Math.max(...allData.value.map(item => item.sortNo || 0)) + } + + // 2. 重置表单,并将排序号设为 最大值 + 1 addForm.value = { phraseName: '', phraseContent: '', - sortNo: 1, + sortNo: maxSortNo + 1, // <--- 这样每次打开就是 2, 3, 4... phraseType: 1, phraseCategory: '' } - // 重置表单验证状态 + if (addFormRef.value) { addFormRef.value.clearValidate() } - // 打开模态框 addDialogVisible.value = true } @@ -434,7 +444,6 @@ const handleDelete = async (row) => { // 用户取消删除时不提示错误 if (error !== 'cancel') { console.error('删除失败:', error) - ElMessage.error('删除操作失败: 网络异常或权限不足') } } } @@ -455,39 +464,41 @@ const showEditDialog = (row) => { } // 编辑表单提交保存方法 +// 修改 index.vue 中的 handleEditSave 方法 const handleEditSave = async () => { try { - // 先执行表单验证 + // 1. 表单校验 const validateResult = await editFormRef.value.validate() if (!validateResult) return - // 名称唯一性校验(排除当前编辑的这条记录ID) + // 2. 名称唯一性校验 const nameValidation = validatePhraseName(editForm.value.phraseName, editForm.value.id) if (!nameValidation.valid) { ElMessage.error(nameValidation.message) return } - // 准备更新数据,修复时间格式为ISO字符串,适配后端LocalDateTime + // 3. 准备数据 const updateData = { ...editForm.value, enableFlag: 1, - updateTime: new Date().toISOString() // 前端临时赋值,后端最终以自己的为准 + updateTime: new Date().toISOString() } - // 调用更新接口 + // 4. 调用接口 const response = await updateDoctorPhrase(updateData) + + // 【核心修改】直接判断 code === 200 即可 + // 因为后端现在失败会返回 R.fail (code!=200),所以只要是 200 就是成功 if (response.code === 200) { - ElMessage.success('更新成功') + ElMessage.success(response.msg || '更新成功') // 优先显示后端返回的消息 editDialogVisible.value = false - // 重新拉取数据,保证列表数据最新 fetchDoctorPhraseList() } else { - ElMessage.error('更新失败: ' + (response.msg || '未知错误')) + ElMessage.error(response.msg || '更新失败') } } catch (error) { console.error('更新失败:', error) - ElMessage.error('更新操作失败: 网络请求错误') } } diff --git a/openhis-ui-vue3/src/views/doctorstation/index.vue b/openhis-ui-vue3/src/views/doctorstation/index.vue index 352d55e2..50192ffb 100644 --- a/openhis-ui-vue3/src/views/doctorstation/index.vue +++ b/openhis-ui-vue3/src/views/doctorstation/index.vue @@ -99,6 +99,10 @@ {{ userStore.nickName }} + + 初诊 + 复诊 + 完诊 @@ -209,7 +213,6 @@ import useUserStore from '@/store/modules/user'; import { nextTick } from 'vue'; import { updatePatientInfo } from './components/store/patient.js'; import { ElMessage, ElMessageBox } from 'element-plus'; -import { debounce } from 'lodash-es'; // // 监听路由离开事件 // onBeforeRouteLeave((to, from, next) => { @@ -276,6 +279,7 @@ const loading = ref(false); const { proxy } = getCurrentInstance(); const visitType = ref(''); const firstVisitDate = ref(''); +const firstEnum = ref(1); // 初复诊标识:1=初诊,2=复诊 const disabled = computed(() => { // 只有在有患者信息但某些条件不满足时才启用覆盖层 // 当前逻辑保持不变,但我们将在按钮级别处理禁用状态 @@ -488,8 +492,7 @@ function handleOpen() { patientDrawerRef.value.refreshList(); } -// 原始的handleCardClick函数 -function handleCardClickOriginal(item, index) { +function handleCardClick(item, index) { console.log('handleCardClick 被调用'); console.log('点击的患者项目:', item); console.log('患者项目中的encounterId:', item.encounterId); @@ -506,6 +509,15 @@ function handleCardClickOriginal(item, index) { console.log('patientInfo.value 设置为:', patientInfo.value); console.log('patientInfo.value.encounterId:', patientInfo.value?.encounterId); + // 根据患者信息设置初复诊标识 + const backendValue = item.firstEnum ?? item.first_enum; + + if (backendValue !== undefined && backendValue !== null) { + firstEnum.value = Number(backendValue); // 确保是数字类型 + } else { + firstEnum.value = 1; + } + // 确保患者信息包含必要的字段 if (!patientInfo.value.encounterId) { console.error('患者信息缺少encounterId字段:', patientInfo.value); @@ -546,9 +558,6 @@ function handleCardClickOriginal(item, index) { }); } -// 使用防抖的handleCardClick函数,防止短时间内多次点击 -const handleCardClick = debounce(handleCardClickOriginal, 500); - function handleLeave(encounterId) { leaveEncounter(encounterId).then((res) => { if (res.code == 200) { @@ -566,11 +575,18 @@ function handleFinish(encounterId) { patientInfo.value = {}; visitType.value = ''; // 重置初复诊标识 visitTypeDisabled.value = false; // 重置禁用状态 + firstEnum.value = 1; // 重置为初诊 getPatientList(); } }); } +// 监听初复诊标识变化 +watch(firstEnum, (newValue) => { + // 这里可以添加更新后端的逻辑,如果需要实时同步到后端 + // 例如:updateEncounterFirstEnum(patientInfo.value.encounterId, newValue) +}); + function handleTimeChange(value) { queryParams.value.registerTimeSTime = value + ' 00:00:00'; queryParams.value.registerTimeETime = value + ' 23:59:59'; @@ -594,7 +610,7 @@ function handleHospitalizationClick() { // 接诊回调 function handleReceive(row) { - handleCardClickOriginal(row); + handleCardClick(row); currentEncounterId.value = row.encounterId; drawer.value = false; getPatientList(); @@ -781,7 +797,7 @@ const markSeen = async () => { currentCallPatient.value = {}; }; const callThis = (row) => { - handleCardClickOriginal(row); + handleCardClick(row); currentCallPatient.value = row; dialogVisible.value = false; // 刷新患者列表和候诊列表 diff --git a/openhis-ui-vue3/src/views/hospitalRecord/components/admissionRecord.vue b/openhis-ui-vue3/src/views/hospitalRecord/components/admissionRecord.vue index 9c7818fc..75529d46 100644 --- a/openhis-ui-vue3/src/views/hospitalRecord/components/admissionRecord.vue +++ b/openhis-ui-vue3/src/views/hospitalRecord/components/admissionRecord.vue @@ -699,7 +699,12 @@ const getDom = () => { }; const setData = (data) => { console.log('设置数据=========>', JSON.stringify(data)); - Object.assign(formData, data); + // 仅更新传入的数据,保留未传入字段的原有值 + Object.keys(data).forEach(key => { + if (formData.hasOwnProperty(key)) { + formData[key] = data[key]; + } + }); }; defineExpose({ setData, diff --git a/openhis-ui-vue3/src/views/inpatientDoctor/home/components/adviceBaseList.vue b/openhis-ui-vue3/src/views/inpatientDoctor/home/components/adviceBaseList.vue index 576f9216..2b568c0b 100644 --- a/openhis-ui-vue3/src/views/inpatientDoctor/home/components/adviceBaseList.vue +++ b/openhis-ui-vue3/src/views/inpatientDoctor/home/components/adviceBaseList.vue @@ -106,7 +106,7 @@ watch( getList(); function getList() { loading.value = true; - queryParams.value.organizationId = props.patientInfo.inHospitalOrgId; + queryParams.value.organizationId = props.patientInfo?.inHospitalOrgId || ''; getAdviceBaseInfo(queryParams.value) .then((res) => { console.log(res.data.records); diff --git a/openhis-ui-vue3/src/views/inpatientDoctor/home/components/diagnosis/diagnosis.vue b/openhis-ui-vue3/src/views/inpatientDoctor/home/components/diagnosis/diagnosis.vue index e888fff1..48fd7566 100644 --- a/openhis-ui-vue3/src/views/inpatientDoctor/home/components/diagnosis/diagnosis.vue +++ b/openhis-ui-vue3/src/views/inpatientDoctor/home/components/diagnosis/diagnosis.vue @@ -203,7 +203,8 @@ const form = ref({ const props = defineProps({ patientInfo: { type: Object, - required: true, + required: false, + default: () => ({}), }, }); const emits = defineEmits(['diagnosisSave']); diff --git a/openhis-ui-vue3/src/views/inpatientDoctor/home/components/order/applicationForm/applicationFormBottomBtn.vue b/openhis-ui-vue3/src/views/inpatientDoctor/home/components/order/applicationForm/applicationFormBottomBtn.vue index 38efb596..7b779ec9 100644 --- a/openhis-ui-vue3/src/views/inpatientDoctor/home/components/order/applicationForm/applicationFormBottomBtn.vue +++ b/openhis-ui-vue3/src/views/inpatientDoctor/home/components/order/applicationForm/applicationFormBottomBtn.vue @@ -10,25 +10,25 @@ 检验 检查 输血 手术 @@ -58,14 +58,19 @@