From a47306825af8b75d27349cddb063533342916592 Mon Sep 17 00:00:00 2001 From: chenqi Date: Tue, 13 Jan 2026 14:41:27 +0800 Subject: [PATCH] =?UTF-8?q?docs(requirement):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=89=8B=E6=9C=AF=E5=AE=A4=E7=BB=B4=E6=8A=A4=E7=95=8C=E9=9D=A2?= =?UTF-8?q?=E9=9C=80=E6=B1=82=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 创建手术室维护界面PRD文档 - 定义页面概述、核心功能和用户价值 - 设计整体布局和页面区域详细描述 - 规范交互功能和数据结构说明 - 说明开发实现要点和注意事项 - 移除中医诊断主诊断功能实现说明文档 - 移除公告通知弹窗功能说明文档 - 移除手术人员字段不显示问题解决方案文档 - 移除手术和麻醉信息Redis缓存实现说明文档 - 移除手术室管理添加类型和所属科室字段说明文档 --- md/中医诊断主诊断功能实现说明.md | 179 ------- md/公告通知弹窗功能说明.md | 178 ------- md/手术人员字段不显示问题解决方案.md | 254 ---------- md/手术和麻醉信息Redis缓存实现说明.md | 120 ----- md/手术室管理添加类型和所属科室字段说明.md | 256 ---------- md/手术申请医生科室字段保存问题解决方案.md | 215 --------- md/手术申请医生科室数据保存问题排查指南.md | 194 -------- md/手术管理模块开发说明.md | 351 -------------- md/未读公告弹窗实现方案.md | 0 md/门诊就诊记录SQL优化建议.md | 160 ------- md/需求/94-手术室维护界面_2026-1-9.md | 287 +++++++++++ .../impl/OperatingRoomAppServiceImpl.java | 82 ++-- .../administration/domain/OperatingRoom.java | 8 +- openhis-ui-vue3/package-lock.json | 18 +- .../nurseStation/temperatureSheet/datas.js | 2 +- .../Auto/printBills/exeOrderSheet.vue | 7 +- .../Auto/printBills/injectOrderSheet.vue | 7 +- .../Auto/printBills/triageTicket.vue | 7 +- .../src/components/Print/AdvancePayment.json | 2 +- .../Print/ChineseMedicinePrescription.json | 2 +- .../Print/DailyOutpatientSettlement.json | 2 +- .../src/components/Print/Disposal copy.json | 2 +- .../Print/DocChineseMedicinePrescription.json | 2 +- .../Print/HQOutpatientMedicalRecord.json | 2 +- .../Print/MedicinePrescription.json | 2 +- .../src/components/Print/OperativeRecord.json | 2 +- .../components/Print/OutpatientBilling.json | 2 +- .../Print/OutpatientMedicalRecord.json | 2 +- .../Print/OutpatientRegistration.json | 2 +- .../src/components/Print/Pharmacy copy 2.json | 2 +- .../src/components/Print/Pharmacy copy.json | 2 +- .../src/components/Print/Pharmacy.json | 2 +- .../src/components/Print/Prescription.json | 2 +- openhis-ui-vue3/src/router/index.js | 446 ++++-------------- .../DischargeDiagnosisCertificate.vue | 5 +- .../src/template/ProgressNoteform.vue | 7 +- .../src/template/inHospitalCaseForm.vue | 2 +- .../src/template/inHospitalRecord.vue | 2 +- .../src/template/inHospitalSurgicalRecord.vue | 16 +- .../src/template/inHosptialCommunicate.vue | 15 +- .../src/template/nursingRecordSheet.vue | 6 +- .../template/outpatientMedicalRecord1.1.vue | 2 +- .../src/template/surgicalPatientHandover.vue | 5 +- openhis-ui-vue3/src/utils/printUtils.js | 6 +- .../cliniccharge/components/chargeDialog.vue | 4 +- .../dayEnd/component/template.json | 2 +- .../views/clinicmanagement/dayEnd/index.vue | 2 +- .../clinicmanagement/dayEnd/indexccu.vue | 4 +- .../disposal/components/disposalTemplate.json | 2 +- .../components/prescriptionTemplate.json | 2 +- .../views/clinicmanagement/disposal/index.vue | 12 +- .../component/prescription.vue | 4 +- .../prescription/prescriptionInfo.vue | 5 +- .../components/templateJson.json | 2 +- .../components/admissionRecord.vue | 6 +- .../components/disDiagnMedicalRecord.vue | 4 +- .../components/inAssessmentForm.vue | 8 +- .../components/inCommunicatePrint.vue | 4 +- .../components/intOperRecordSheet.vue | 4 +- .../components/medicalRecordPrint.vue | 2 +- .../feeSettlement/components/chargeDialog.vue | 10 +- .../feeSettlement/components/template.json | 2 +- .../inpatientRecord/index.vue | 5 +- .../views/inpatientNurse/tprChart/index.vue | 1 + .../chkstockPart/components/template.json | 4 +- .../components/templateJson.json | 4 +- .../outpatientDepartmentMetrics.vue | 4 +- .../src/views/operatingroom/index.vue | 73 ++- .../components/disposalTemplate.json | 2 +- .../components/templateJson.json | 2 +- sql/add_operating_room_type_fields.sql | 108 +---- 71 files changed, 630 insertions(+), 2519 deletions(-) delete mode 100644 md/中医诊断主诊断功能实现说明.md delete mode 100644 md/公告通知弹窗功能说明.md delete mode 100644 md/手术人员字段不显示问题解决方案.md delete mode 100644 md/手术和麻醉信息Redis缓存实现说明.md delete mode 100644 md/手术室管理添加类型和所属科室字段说明.md delete mode 100644 md/手术申请医生科室字段保存问题解决方案.md delete mode 100644 md/手术申请医生科室数据保存问题排查指南.md delete mode 100644 md/手术管理模块开发说明.md delete mode 100644 md/未读公告弹窗实现方案.md delete mode 100644 md/门诊就诊记录SQL优化建议.md create mode 100644 md/需求/94-手术室维护界面_2026-1-9.md diff --git a/md/中医诊断主诊断功能实现说明.md b/md/中医诊断主诊断功能实现说明.md deleted file mode 100644 index d807e9b7..00000000 --- a/md/中医诊断主诊断功能实现说明.md +++ /dev/null @@ -1,179 +0,0 @@ -# 中医诊断主诊断功能实现说明 - -## 问题描述 -中医诊断在添加时无法设置主诊断标记,导致保存后无法正确标识主诊断。 - -## 问题原因 -在 `addDiagnosisDialog.vue` 中保存中医诊断时,没有传递 `maindiseFlag`(主诊断标记)字段到后端。 - -## 解决方案 - -### 1. 前端修改 - -#### 1.1 添加主诊断UI(addDiagnosisDialog.vue) -在诊断详情区域为每个中医诊断添加了主诊断复选框: - -```vue - -``` - -#### 1.2 添加主诊断逻辑处理 - -**新增诊断时的默认行为:** -- 第一个添加的中医诊断自动设置为主诊断 -- 后续添加的诊断默认不是主诊断 - -**主诊断唯一性校验:** -```javascript -function handleMaindise(value, index) { - if (value) { - // 检查是否已有其他主诊断 - let mainCount = 0; - tcmDiagonsisList.value.forEach((item, idx) => { - if (item.isMain && idx !== index) { - mainCount++; - } - }); - - if (mainCount > 0) { - // 取消当前选择 - tcmDiagonsisList.value[index].isMain = false; - proxy.$modal.msgWarning('只能有一条主诊断'); - return; - } - - // 更新保存列表中的主诊断标记 - const syndromeGroupNo = tcmDiagonsisList.value[index].syndromeGroupNo; - tcmDiagonsisSaveList.value.forEach((item, idx) => { - if (item.syndromeGroupNo === syndromeGroupNo) { - // 每个证候组有两条记录(病和证),只有第一条(病)设置主诊断标记 - if (idx % 2 === 0 || tcmDiagonsisSaveList.value[idx - 1]?.syndromeGroupNo !== syndromeGroupNo) { - item.maindiseFlag = 1; - } - } - }); - } else { - // 取消主诊断 - const syndromeGroupNo = tcmDiagonsisList.value[index].syndromeGroupNo; - tcmDiagonsisSaveList.value.forEach((item) => { - if (item.syndromeGroupNo === syndromeGroupNo) { - item.maindiseFlag = 0; - } - }); - } -} -``` - -#### 1.3 保存时包含主诊断字段 - -**新增诊断时:** -```javascript -tcmDiagonsisSaveList.value.push({ - definitionId: row.id, - ybNo: row.ybNo, - syndromeGroupNo: timestamp.value, - verificationStatusEnum: 4, - medTypeCode: '11', - maindiseFlag: isFirstDiagnosis ? 1 : 0, // 添加主诊断标记 -}); -``` - -**修改诊断时:** -```javascript -tcmDiagonsisSaveList.value.push({ - conditionId: item.conditionId, - updateId: updateIds[0], - definitionId: item.illnessDefinitionId, - ybNo: item.ybNo, - syndromeGroupNo: item.syndromeGroupNo, - verificationStatusEnum: 4, - medTypeCode: '11', - diagSrtNo: item.diagSrtNo, - maindiseFlag: isMain ? 1 : 0, // 保留原有的主诊断标记 -}); -``` - -#### 1.4 诊断列表显示(diagnosis.vue) - -在获取中医诊断列表时,正确读取并显示主诊断标记: - -```javascript -form.value.diagnosisList.push({ - name: item.name + '-' + res.data.symptom[index].name, - diagSrtNo: item.diagSrtNo, - ybNo: item.ybNo, - medTypeCode: item.medTypeCode, - syndromeGroupNo: item.syndromeGroupNo, - typeName: '中医诊断', - conditionId: item.conditionId, - symptomConditionId: res.data.symptom[index].conditionId, - updateId: item.encounterDiagnosisId + '-' + res.data.symptom[index].encounterDiagnosisId, - illnessDefinitionId: item.definitionId, - symptomDefinitionId: res.data.symptom[index].definitionId, - symptomYbNo: res.data.symptom[index].ybNo, - maindiseFlag: item.maindiseFlag || 0, // 添加主诊断标记 -}); -``` - -### 2. 后端支持 - -后端已经支持 `maindiseFlag` 字段的保存和读取: - -**SaveDiagnosisChildParam.java:** -```java -/** - * 主诊断标记 (1:是,0:否) - */ -private Integer maindiseFlag; -``` - -**DoctorStationChineseMedicalAppServiceImpl.java:** -```java -encounterDiagnosis.setMaindiseFlag(saveDiagnosisChildParam.getMaindiseFlag()); -``` - -## 业务规则 - -1. **主诊断标记位置**:主诊断标记在"病"上(每个病-证组合的第一条记录) -2. **主诊断唯一性**:中医诊断只能有一个主诊断 -3. **与西医诊断的关系**:中医诊断和西医诊断可以各有一个主诊断(互不冲突) -4. **默认行为**:第一个添加的中医诊断自动设置为主诊断 - -## 修改文件清单 - -1. `openhis-ui-vue3/src/views/doctorstation/components/diagnosis/addDiagnosisDialog.vue` - - 添加主诊断复选框UI - - 添加主诊断逻辑处理函数 - - 修改保存数据时包含 maindiseFlag 字段 - -2. `openhis-ui-vue3/src/views/doctorstation/components/diagnosis/diagnosis.vue` - - 修改获取中医诊断列表时读取 maindiseFlag 字段 - - 修改传递给对话框的数据包含 maindiseFlag 字段 - -## 测试要点 - -1. ✅ 新增中医诊断时,第一个诊断自动设置为主诊断 -2. ✅ 可以手动勾选/取消主诊断复选框 -3. ✅ 只能有一个主诊断(尝试勾选第二个时会提示错误) -4. ✅ 保存后主诊断标记正确保存到数据库 -5. ✅ 刷新页面后主诊断标记正确显示 -6. ✅ 修改已有诊断时,主诊断标记正确回显 -7. ✅ 中医诊断和西医诊断的主诊断互不影响 - -## 注意事项 - -1. 中医诊断是"病-证"成对出现的,主诊断标记只设置在"病"上 -2. 证候记录的 maindiseFlag 始终为 0 -3. 主诊断唯一性校验只在中医诊断内部进行,不影响西医诊断 - -## 完成时间 -2026年1月9日 diff --git a/md/公告通知弹窗功能说明.md b/md/公告通知弹窗功能说明.md deleted file mode 100644 index ad99031d..00000000 --- a/md/公告通知弹窗功能说明.md +++ /dev/null @@ -1,178 +0,0 @@ -# 公告通知弹窗功能说明 - -## 功能概述 -用户登录后,系统会自动弹出公告通知窗口,显示未读的系统公告和通知。 - -## 功能特性 - -### 1. 自动弹出 -- 登录后 1 秒自动加载公告列表 -- 如果有未读公告,自动弹出弹窗显示 -- 延迟加载避免影响页面初始渲染 - -### 2. 优先级显示 -- **高优先级**(1):红色背景 `#fff1f0`,红色文字 `#ff4d4f` -- **中优先级**(2):橙色背景 `#fff7e6`,橙色文字 `#faad14` -- **低优先级**(3):灰色背景 `#f0f2f5`,灰色文字 `#909399` - -### 3. 排序规则 -- 按优先级升序排列(高 -> 中 -> 低) -- 相同优先级按创建时间降序排列 - -### 4. 已读状态 -- 未读公告:黄色背景高亮显示 -- 已读公告:默认白色背景 -- 点击公告自动标记为已读 - -### 5. 分类显示 -- **通知**(1):蓝色图标 -- **紧急**(2):红色警告图标 -- **信息**(3):绿色信息图标 -- **成功**(4):紫色成功图标 - -## 文件结构 - -``` -src/ -├── components/ -│ ├── NoticePopup/ -│ │ └── index.vue # 登录后自动弹窗组件 -│ └── NoticePanel/ -│ └── index.vue # 手动打开的公告面板 -├── layout/ -│ └── index.vue # 主布局,引入 NoticePopup -└── views/ - └── system/ - └── notice/ - └── index.vue # 公告管理页面 -``` - -## API 接口 - -### 前端 API -```javascript -// 获取用户公告列表 -getUserNotices() - -// 标记为已读 -markAsRead(noticeId) - -// 全部标记为已读 -markAllAsRead(noticeIds) - -// 获取未读数量 -getUnreadCount() -``` - -### 后端接口 -```java -// GET /system/notice/public/notice -// 获取当前用户的公告列表(含已读状态) - -// GET /system/notice/public/unread/count -// 获取未读公告数量 - -// POST /system/notice/public/read/{noticeId} -// 标记公告为已读 - -// POST /system/notice/public/read/all -// 批量标记为已读 -``` - -## 数据库字段 - -### sys_notice 表新增字段 -```sql --- 优先级字段 -priority VARCHAR(1) DEFAULT '3' - --- 发布状态字段 -publish_status VARCHAR(1) DEFAULT '0' -``` - -## 使用说明 - -### 1. 数据库迁移 -执行 SQL 脚本添加必要的字段: -```bash -# 添加优先级字段 -ALTER TABLE sys_notice ADD COLUMN priority VARCHAR(1) DEFAULT '3'; - -# 添加发布状态字段 -ALTER TABLE sys_notice ADD COLUMN publish_status VARCHAR(1) DEFAULT '0'; -``` - -### 2. 后台管理 -在 `系统管理 > 公告管理` 中: -1. 点击"新增"创建公告 -2. 选择优先级:高/中/低 -3. 选择公告类型:通知/紧急/信息/成功 -4. 填写标题和内容 -5. 点击"发布"按钮 - -### 3. 用户端显示 -- 登录后自动弹出未读公告 -- 点击顶部导航栏铃铛图标可手动打开 -- 点击公告查看详情 -- 支持全部标记为已读 - -## 样式说明 - -### 弹窗样式 -- 宽度:800px -- 高度:最大 600px -- 标题栏:渐变紫色背景 `linear-gradient(135deg, #667eea 0%, #764ba2 100%)` -- 左右分栏布局 - -### 列表样式 -- 列表宽度:380px -- 滚动区域:最大高度 500px -- 未读高亮:黄色背景 `#fffbe6` -- 激活项:蓝色左边框 `#1890ff` - -### 详情样式 -- 自适应宽度 -- 标题字号:18px -- 内容字号:14px -- 行高:1.8 - -## 注意事项 - -1. **权限控制** - - 只有已发布的公告才会显示 - - 只有正常状态的公告才会显示 - -2. **性能优化** - - 使用延迟加载(1秒) - - 使用虚拟滚动(el-scrollbar) - - 按需加载详情 - -3. **用户体验** - - 支持点击空白处关闭 - - 支持ESC键关闭 - - 未读状态醒目标识 - - 优先级颜色区分明显 - -## 故障排查 - -### 弹窗不显示 -1. 检查后端接口 `/system/notice/public/notice` 是否正常返回 -2. 检查是否有未读公告 -3. 检查浏览器控制台是否有错误 -4. 清除浏览器缓存重试 - -### 样式错乱 -1. 检查 Element Plus 版本是否兼容 -2. 检查样式是否被其他 CSS 覆盖 -3. 使用浏览器开发者工具检查样式 - -### 数据不更新 -1. 检查后端返回的数据格式 -2. 检查 API 调用是否成功 -3. 检查响应拦截器处理 - -## 版本信息 -- 创建日期:2025-12-30 -- 功能版本:v1.0 -- 前端框架:Vue 3 + Element Plus -- 后端框架:Spring Boot + MyBatis Plus diff --git a/md/手术人员字段不显示问题解决方案.md b/md/手术人员字段不显示问题解决方案.md deleted file mode 100644 index 708aaf58..00000000 --- a/md/手术人员字段不显示问题解决方案.md +++ /dev/null @@ -1,254 +0,0 @@ -# 手术人员字段不显示问题解决方案 - -## 问题描述 -主刀医生、麻醉医生、助手1、助手2、执行科这些字段在手术查看页面中没有显示数据。 - -## 问题原因 -这些字段在数据库中可能为 **null 或空值**,虽然保存了 ID(如 `main_surgeon_id`),但没有保存对应的姓名(如 `main_surgeon_name`)。 - -## 解决步骤 - -### 步骤 1:检查数据库中字段的实际值 - -执行以下 SQL 查看当前数据: - -```sql -SELECT - id, - surgery_no, - main_surgeon_id, - main_surgeon_name, - anesthetist_id, - anesthetist_name, - assistant_1_id, - assistant_1_name, - assistant_2_id, - assistant_2_name, - operating_room_id, - operating_room_name, - org_id, - org_name -FROM public.cli_surgery -WHERE delete_flag = '0' -ORDER BY create_time DESC -LIMIT 5; -``` - -**请告诉我结果**:特别是 `main_surgeon_name`、`anesthetist_name`、`assistant_1_name`、`assistant_2_name`、`operating_room_name`、`org_name` 这些字段的值。 - -### 步骤 2:检查用户表结构 - -执行以下 SQL 查看用户表的结构: - -```sql -SELECT - column_name, - data_type -FROM information_schema.columns -WHERE table_name = 'sys_user' -AND column_name IN ('user_id', 'nick_name', 'user_name', 'practitioner_id') -ORDER BY column_name; -``` - -**目的**:确定人员ID和姓名的对应关系。 - -### 步骤 3:填充人员姓名字段(推荐方法) - -使用以下 SQL 脚本填充人员姓名: - -```sql --- 填充主刀医生姓名 -UPDATE public.cli_surgery s -SET main_surgeon_name = u.nick_name -FROM public.sys_user u -WHERE s.main_surgeon_id = u.user_id -AND s.main_surgeon_name IS NULL -AND s.delete_flag = '0'; - --- 填充麻醉医生姓名 -UPDATE public.cli_surgery s -SET anesthetist_name = u.nick_name -FROM public.sys_user u -WHERE s.anesthetist_id = u.user_id -AND s.anesthetist_name IS NULL -AND s.delete_flag = '0'; - --- 填充助手1姓名 -UPDATE public.cli_surgery s -SET assistant_1_name = u.nick_name -FROM public.sys_user u -WHERE s.assistant_1_id = u.user_id -AND s.assistant_1_name IS NULL -AND s.delete_flag = '0'; - --- 填充助手2姓名 -UPDATE public.cli_surgery s -SET assistant_2_name = u.nick_name -FROM public.sys_user u -WHERE s.assistant_2_id = u.user_id -AND s.assistant_2_name IS NULL -AND s.delete_flag = '0'; - --- 填充手术室名称 -UPDATE public.cli_surgery s -SET operating_room_name = r.name -FROM public.cli_operating_room r -WHERE s.operating_room_id = r.id -AND s.operating_room_name IS NULL -AND s.delete_flag = '0'; - --- 填充执行科室名称 -UPDATE public.cli_surgery s -SET org_name = o.name -FROM public.adm_organization o -WHERE s.org_id = o.id -AND s.org_name IS NULL -AND s.delete_flag = '0'; -``` - -### 步骤 4:验证更新结果 - -执行以下 SQL 验证是否更新成功: - -```sql -SELECT - id, - surgery_no, - main_surgeon_id, - main_surgeon_name, - anesthetist_id, - anesthetist_name, - assistant_1_id, - assistant_1_name, - assistant_2_id, - assistant_2_name, - operating_room_id, - operating_room_name, - org_id, - org_name -FROM public.cli_surgery -WHERE delete_flag = '0' -ORDER BY create_time DESC -LIMIT 5; -``` - -**预期结果**:所有 `*_name` 字段都应该有值。 - -### 步骤 5:刷新前端页面 - -1. 刷新手术管理页面 -2. 点击某个手术记录的"查看"按钮 -3. 检查详情对话框中是否显示这些字段 - -## 前端代码检查 - -### 1. 检查详情对话框显示 - -打开 `surgerymanage/index.vue` 文件,查看详情对话框部分: - -```vue -{{ viewData.mainSurgeonName }} -{{ viewData.anesthetistName }} -{{ viewData.assistant1Name }} -{{ viewData.assistant2Name }} -{{ viewData.operatingRoomName }} -{{ viewData.orgName }} -``` - -**确认**:这些字段名是否正确(注意驼峰命名)。 - -### 2. 检查浏览器控制台 - -1. 打开浏览器开发者工具(F12) -2. 切换到 Console 标签 -3. 点击"查看"按钮 -4. 查看是否有 JavaScript 错误 - -### 3. 检查 Network 响应 - -1. 切换到 Network 标签 -2. 点击"查看"按钮 -3. 找到 `/clinical-manage/surgery/surgery-detail` 请求 -4. 查看响应内容 - -**检查**:响应数据中是否包含这些字段,值是什么。 - -## 常见问题 - -### 问题 1:UPDATE SQL 执行失败 - -**症状**:报错 "relation does not exist" 或 "column does not exist" - -**解决**: -1. 检查表名是否正确(sys_user 或 adm_practitioner) -2. 检查字段名是否正确(user_id 或 practitioner_id) - -### 问题 2:UPDATE 后字段仍为 null - -**症状**:UPDATE 执行成功,但字段仍为 null - -**原因**:关联表中没有对应的记录 - -**解决**:检查人员ID是否存在于人员表中 - -```sql --- 检查主刀医生ID是否存在 -SELECT s.main_surgeon_id, u.nick_name -FROM public.cli_surgery s -LEFT JOIN public.sys_user u ON s.main_surgeon_id = u.user_id -WHERE s.main_surgeon_id IS NOT NULL -AND u.user_id IS NULL -LIMIT 5; -``` - -### 问题 3:前端仍然不显示 - -**症状**:数据库中有值,但前端不显示 - -**原因**: -1. 前端字段名不匹配 -2. 前端数据绑定有问题 - -**解决**: -1. 检查 MyBatis XML 映射是否正确 -2. 检查后端返回的 JSON 数据结构 -3. 检查前端变量名是否正确 - -## 后续改进建议 - -### 1. 保存时自动填充姓名 - -在前端或后端保存手术信息时,根据选择的医生ID自动查询并填充姓名字段。 - -### 2. 提供人员选择功能 - -在前端提供医生、科室等选择下拉框,而不是手动输入ID。 - -### 3. 添加数据完整性校验 - -在保存前检查:如果选择了人员ID,必须填充对应的姓名字段。 - -## 相关文件 - -1. **检查和填充脚本**:`e:/his/检查和填充手术人员字段.sql` -2. **填充脚本**:`e:/his/填充手术人员字段姓名.sql` -3. **MyBatis 映射**:`e:/his/openhis-server-new/openhis-application/src/main/resources/mapper/clinicalmanage/SurgeryMapper.xml` - -## 验证清单 - -- [ ] 数据库查询显示字段为 null -- [ ] 执行了填充 SQL 脚本 -- [ ] 验证更新后字段有值 -- [ ] 刷新前端页面 -- [ ] 详情对话框中正确显示 - -## 联系支持 - -如果以上步骤都无法解决问题,请提供: - -1. **步骤 1 的查询结果**:当前数据库中这些字段的值 -2. **步骤 2 的查询结果**:sys_user 表的结构 -3. **UPDATE SQL 执行结果**:是否有错误,更新了多少条记录 -4. **步骤 4 的验证结果**:更新后的字段值 -5. **浏览器控制台错误**:是否有 JavaScript 错误 -6. **Network 响应数据**:后端返回的完整数据 diff --git a/md/手术和麻醉信息Redis缓存实现说明.md b/md/手术和麻醉信息Redis缓存实现说明.md deleted file mode 100644 index f26a4b1b..00000000 --- a/md/手术和麻醉信息Redis缓存实现说明.md +++ /dev/null @@ -1,120 +0,0 @@ -# 手术和麻醉信息Redis缓存实现说明 - -## 概述 -为提高手术和麻醉信息的查询性能,已将手术信息缓存到Redis中。接口查询时先从Redis缓存获取,如果没有则从数据库查询并更新到Redis缓存。 - -## 实现细节 - -### 1. Redis缓存Key定义 -在 `openhis-common/src/main/java/com/openhis/common/utils/RedisKeys.java` 中定义了以下缓存Key: - -```java -// 单个手术信息缓存 -public static String getSurgeryKey(Long surgeryId) - -// 按患者ID查询的手术列表缓存 -public static String getSurgeryListByPatientKey(Long patientId) - -// 按就诊ID查询的手术列表缓存 -public static String getSurgeryListByEncounterKey(Long encounterId) -``` - -### 2. 缓存实现 - -#### 2.1 SurgeryServiceImpl (Domain层) -- **getSurgeryById(Long id)**: 根据手术ID查询单个手术信息 - - 先从Redis缓存获取 - - 缓存未命中则从数据库查询 - - 查询结果存入Redis缓存(30分钟过期) - -- **getSurgeryListByPatientId(Long patientId)**: 根据患者ID查询手术列表 - - 先从Redis缓存获取 - - 缓存未命中则从数据库查询 - - 查询结果存入Redis缓存(30分钟过期) - -- **getSurgeryListByEncounterId(Long encounterId)**: 根据就诊ID查询手术列表 - - 先从Redis缓存获取 - - 缓存未命中则从数据库查询 - - 查询结果存入Redis缓存(30分钟过期) - -- **insertSurgery(Surgery surgery)**: 新增手术信息 - - 插入成功后清除相关缓存 - -- **updateSurgery(Surgery surgery)**: 更新手术信息 - - 更新成功后清除相关缓存 - -- **deleteSurgery(Long id)**: 删除手术信息 - - 删除成功后清除相关缓存 - -- **updateSurgeryStatus(Long id, Integer statusEnum)**: 更新手术状态 - - 更新成功后清除相关缓存 - -#### 2.2 SurgeryAppServiceImpl (Application层) -- **getSurgeryDetail(Long id)**: 根据ID查询手术详情 - - 先从Redis缓存获取 - - 缓存未命中则从数据库查询 - - 查询结果存入Redis缓存(30分钟过期) - -- **addSurgery(SurgeryDto surgeryDto)**: 新增手术信息 - - 插入成功后清除相关缓存 - -- **updateSurgery(SurgeryDto surgeryDto)**: 更新手术信息 - - 更新成功后清除相关缓存 - -- **deleteSurgery(Long id)**: 删除手术信息 - - 删除成功后清除相关缓存 - -- **updateSurgeryStatus(Long id, Integer statusEnum)**: 更新手术状态 - - 更新成功后清除相关缓存 - -### 3. 缓存清除策略 -当手术信息发生变化时(新增、更新、删除),会清除以下相关缓存: -1. 单个手术信息缓存 -2. 患者手术列表缓存 -3. 就诊手术列表缓存 - -### 4. 缓存配置 -- **缓存时间**: 30分钟 -- **时间单位**: TimeUnit.MINUTES -- **序列化**: 使用RedisTemplate默认序列化方式 - -## 关于麻醉信息 -当前项目中,麻醉信息是手术表(`cli_surgery`)中的字段,包括: -- 麻醉医生ID (anesthetistId) -- 麻醉医生姓名 (anesthetistName) -- 麻醉方式编码 (anesthesiaTypeEnum) -- 麻醉费用 (anesthesiaFee) - -这些字段已经包含在手术实体的缓存中,无需单独实现麻醉信息的缓存。 - -## 使用示例 - -### 查询手术信息(自动使用缓存) -```java -// 自动从缓存获取,未命中则查询数据库 -Surgery surgery = surgeryService.getSurgeryById(surgeryId); -``` - -### 更新手术信息(自动清除缓存) -```java -// 更新数据库,同时清除相关缓存 -surgeryService.updateSurgery(surgery); -``` - -### 手动清除缓存(如需要) -```java -String cacheKey = RedisKeys.getSurgeryKey(surgeryId); -redisCache.deleteObject(cacheKey); -``` - -## 性能优化建议 -1. 对于频繁访问的手术信息,缓存命中率高,可显著提升查询性能 -2. 对于不常访问的手术信息,30分钟缓存时间可避免占用过多Redis内存 -3. 如需调整缓存时间,可修改代码中的 `30, TimeUnit.MINUTES` 参数 -4. 如需更精细的缓存控制,可考虑使用不同的缓存时间策略(如根据手术状态设置不同过期时间) - -## 监控建议 -建议监控以下指标: -- Redis缓存命中率 -- Redis内存使用情况 -- 查询响应时间对比(缓存命中 vs 缓存未命中) diff --git a/md/手术室管理添加类型和所属科室字段说明.md b/md/手术室管理添加类型和所属科室字段说明.md deleted file mode 100644 index aabd79e4..00000000 --- a/md/手术室管理添加类型和所属科室字段说明.md +++ /dev/null @@ -1,256 +0,0 @@ -# 手术室管理添加类型和所属科室字段功能说明 - -## 概述 - -本次更新为手术室管理模块添加了"类型"和"所属科室"字段,优化了手术室信息的分类管理。 - -**数据库类型**:PostgreSQL - -## 功能特点 - -### 1. 手术室类型 - -支持四种手术室类型: -- **急诊手术室**:用于急诊手术的手术室 -- **择期手术室**:用于择期手术的手术室(默认类型) -- **日间手术室**:用于日间手术的手术室 -- **复合手术室**:用于复合手术的手术室 - -### 2. 所属科室 - -每个手术室可以关联到具体的科室,便于科室级别的资源管理。 - -## 修改内容 - -### 前端修改(Vue3) - -#### 1. 手术室列表页面 (`operatingroom/index.vue`) - -**列表表格新增列**: -- 类型列:显示手术室类型(急诊手术室、择期手术室等) -- 所属科室列:显示手术室所属的科室名称 - -**新增/修改对话框新增字段**: -- 类型选择器:下拉选择手术室类型 -- 所属科室选择器:可搜索的科室下拉框 - -**查询表单保持原样**: -- 仍支持按手术室名称和状态查询 - -**查看对话框新增显示**: -- 类型信息 -- 所属科室信息 - -#### 2. 表单数据结构 - -```javascript -const form = ref({ - id: undefined, - busNo: undefined, - name: undefined, - roomTypeEnum: undefined, // 新增:手术室类型 - organizationId: undefined, // 已有:所属科室ID - organizationName: undefined, // 已有:所属科室名称 - locationDescription: undefined, - equipmentConfig: undefined, - capacity: 1, - statusEnum: 1, - displayOrder: 0, - remark: undefined -}) -``` - -#### 3. 类型选项配置 - -```javascript -const roomTypeOptions = ref([ - { value: 1, label: '急诊手术室' }, - { value: 2, label: '择期手术室' }, - { value: 3, label: '日间手术室' }, - { value: 4, label: '复合手术室' } -]) -``` - -### 后端修改(Java) - -#### 1. 实体类 (`OperatingRoom.java`) - -**新增字段**: -```java -/** 手术室类型 */ -@Dict(dictCode = "operating_room_type") -private Integer roomTypeEnum; -private String roomTypeEnum_dictText; - -/** 所属机构ID */ -private Long organizationId; - -/** 所属机构名称 */ -private String organizationName; -``` - -#### 2. DTO类 (`OperatingRoomDto.java`) - -**新增字段**: -```java -/** 手术室类型 */ -@Dict(dictCode = "operating_room_type") -private Integer roomTypeEnum; -private String roomTypeEnum_dictText; - -/** 所属机构ID */ -@JsonSerialize(using = ToStringSerializer.class) -private Long organizationId; - -/** 机构名称 */ -private String organizationName; -``` - -#### 3. Service实现类 (`OperatingRoomAppServiceImpl.java`) - -**查询列表方法优化**: -- 添加类型字段的枚举值转换逻辑 -- 根据类型编码设置对应的中文描述 - -**详情查询方法优化**: -- 添加类型字段的枚举值转换 -- 查询所属科室的名称并回显 - -### 数据库修改 - -#### SQL脚本文件:`add_operating_room_type_fields.sql`(PostgreSQL版本) - -**1. 添加字段**: -```sql -ALTER TABLE public.adm_operating_room -ADD COLUMN room_type_enum INTEGER DEFAULT 2; - -COMMENT ON COLUMN public.adm_operating_room.room_type_enum IS - '手术室类型:1-急诊手术室,2-择期手术室,3-日间手术室,4-复合手术室'; -``` - -**2. 更新现有数据**: -```sql -UPDATE public.adm_operating_room -SET room_type_enum = 2 -WHERE room_type_enum IS NULL; -``` - -**3. 添加索引**: -```sql -CREATE INDEX idx_room_type ON public.adm_operating_room(room_type_enum); -CREATE INDEX idx_org_id ON public.adm_operating_room(organization_id); -``` - -**4. 字典数据**: -- 新增字典类型:`operating_room_type`(手术室类型) -- 新增字典项: - - 急诊手术室(1) - - 择期手术室(2) - - 日间手术室(3) - - 复合手术室(4) - -**PostgreSQL特定语法说明**: -- 使用 `public.adm_operating_room` 替代 `` `adm_operating_room` `` -- 使用 `COMMENT ON COLUMN` 替代 `COMMENT` 在 `ALTER TABLE` 中 -- 使用 `nextval()` 和序列来生成字典类型ID -- 使用 `information_schema.columns` 获取列信息 -- 使用 `CASE WHEN` 语句进行条件判断 - -## 部署步骤 - -### 1. 数据库部署 - -执行SQL脚本(PostgreSQL): -```bash -psql -U postgres -d his_database -f add_operating_room_type_fields.sql -``` - -或者使用 pgAdmin 等图形化工具执行SQL脚本。 - -### 2. 后端部署 - -重启后端服务,使新的代码生效。 - -### 3. 前端部署 - -重新编译并部署前端代码: -```bash -npm run build -``` - -## 使用说明 - -### 新增手术室 - -1. 进入手术室管理页面 -2. 点击"新增手术室"按钮 -3. 填写手术室信息: - - 手术室名称(必填) - - 类型(可选,默认为择期手术室) - - 所属科室(必填) - - 位置描述 - - 设备配置 - - 容纳人数 - - 状态(默认为启用) - - 显示顺序 - - 备注 -4. 点击"确定"保存 - -### 修改手术室 - -1. 在手术室列表中找到要修改的记录 -2. 点击"编辑"按钮 -3. 修改相关信息(包括类型和所属科室) -4. 点击"确定"保存 - -### 查看手术室详情 - -1. 在手术室列表中点击"查看"按钮 -2. 查看完整的手术室信息,包括类型和所属科室 - -### 查询手术室 - -- 按手术室名称模糊查询 -- 按状态筛选(启用/停用) - -## 注意事项 - -1. **数据迁移**:现有手术室的类型默认设置为"择期手术室"(2),可以根据实际需要调整。 - -2. **科室关联**:所属科室是必填字段,需要在科室管理中先配置好科室信息。 - -3. **类型字典**:手术室类型字典已自动创建,可以在系统字典管理中进行维护。 - -4. **索引优化**:已为类型和科室字段添加索引,提升查询性能。 - -5. **兼容性**:此次修改保持了向后兼容性,不影响现有功能。 - -## 验证清单 - -- [ ] 数据库字段添加成功 -- [ ] 字典数据创建成功 -- [ ] 前端列表正确显示类型和所属科室 -- [ ] 新增手术室时可选择类型和所属科室 -- [ ] 修改手术室时可更新类型和所属科室 -- [ ] 查看手术室详情时正确显示类型和所属科室 -- [ ] 类型下拉选项显示正确 -- [ ] 所属科室选择器可正常搜索和选择 -- [ ] 查询功能正常工作 -- [ ] 没有语法错误和运行时错误 - -## 回滚方案 - -如需撤销本次修改,请执行SQL脚本中的回滚语句(PostgreSQL): - -```sql -DROP INDEX idx_room_type ON public.adm_operating_room; -DROP INDEX idx_org_id ON public.adm_operating_room; -DELETE FROM public.sys_dict_data WHERE dict_type = 'operating_room_type'; -DELETE FROM public.sys_dict_type WHERE dict_type = 'operating_room_type'; -ALTER TABLE public.adm_operating_room DROP COLUMN room_type_enum; -``` - -## 技术支持 - -如有问题,请联系技术支持团队。 diff --git a/md/手术申请医生科室字段保存问题解决方案.md b/md/手术申请医生科室字段保存问题解决方案.md deleted file mode 100644 index 9601ceaa..00000000 --- a/md/手术申请医生科室字段保存问题解决方案.md +++ /dev/null @@ -1,215 +0,0 @@ -# 手术申请医生科室字段保存问题解决方案 - -## 问题确认 -数据库中只保存了 ID 字段(`apply_doctor_id`、`apply_dept_id`),但没有保存名称字段(`apply_doctor_name`、`apply_dept_name`)。 - -## 根本原因 -**数据库表中缺少 `apply_doctor_name` 和 `apply_dept_name` 这两个字段!** - -虽然 MyBatis 映射文件和实体类都配置了这些字段,但如果数据库表中不存在这些列,MyBatis 在插入时会静默忽略这些字段(不会报错),导致只有 ID 被保存。 - -## 解决步骤 - -### 步骤 1:执行数据库迁移脚本(必须!) - -使用 Navicat Premium 17 执行以下 SQL: - -```sql --- 方法1:使用 IF NOT EXISTS 语法(推荐) -ALTER TABLE public.cli_surgery ADD COLUMN IF NOT EXISTS apply_doctor_name VARCHAR(100); -COMMENT ON COLUMN public.cli_surgery.apply_doctor_name IS '申请医生姓名'; - -ALTER TABLE public.cli_surgery ADD COLUMN IF NOT EXISTS apply_dept_name VARCHAR(100); -COMMENT ON COLUMN public.cli_surgery.apply_dept_name IS '申请科室名称'; - --- 验证字段是否添加成功 -SELECT column_name, data_type, character_maximum_length -FROM information_schema.columns -WHERE table_name = 'cli_surgery' -AND column_name IN ('apply_doctor_name', 'apply_dept_name'); -``` - -**预期结果**: -``` -apply_doctor_name | character varying | 100 -apply_dept_name | character varying | 100 -``` - -### 步骤 2:重启后端服务 - -执行数据库迁移后,必须重启后端服务以重新加载表结构。 - -### 步骤 3:新增手术并查看日志 - -1. 打开后端控制台或日志文件 -2. 在前端新增一条手术记录 -3. 查看后端日志,应该能看到: - -``` -设置申请医生信息 - doctorId: 123, doctorName: 张医生, deptId: 456, deptName: 普外科 -前端提交的数据 - applyDoctorId: 123, applyDoctorName: 张医生, applyDeptId: 456, applyDeptName: 普外科 -准备插入手术记录 - applyDoctorId: 123, applyDoctorName: 张医生, applyDeptId: 456, deptName: 普外科 -准备插入手术记录 - applyDoctorId: 123, applyDoctorName: 张医生, applyDeptId: 456, deptName: 普外科 -插入后查询结果 - applyDoctorId: 123, applyDoctorName: 张医生, applyDeptId: 456, deptName: 普外科 -手术记录插入成功 - surgeryId: 1234567890123456789, surgeryNo: OP202501051234 -``` - -**关键检查点**: -- `准备插入手术记录` 这行日志中,`applyDoctorName` 和 `applyDeptName` 必须有值(不能为 null) -- `插入后查询结果` 这行日志中,这两个字段也必须有值 - -### 步骤 4:验证数据库 - -执行以下 SQL 查询最新插入的记录: - -```sql -SELECT - id, - surgery_no, - apply_doctor_id, - apply_doctor_name, - apply_dept_id, - apply_dept_name, - surgery_name, - create_time -FROM public.cli_surgery -WHERE delete_flag = '0' -ORDER BY create_time DESC -LIMIT 1; -``` - -**预期结果**: -- `apply_doctor_id`:有值(例如:123) -- `apply_doctor_name`:有值(例如:张医生) -- `apply_dept_id`:有值(例如:456) -- `apply_dept_name`:有值(例如:普外科) - -### 步骤 5:测试前端显示 - -1. 刷新手术管理页面 -2. 查看列表中是否显示申请医生和申请科室列 -3. 点击"查看"或"编辑"按钮,检查详情对话框是否显示这些信息 - -## 常见问题和解决 - -### 问题 1:执行 SQL 后报错 "column does not exist" - -**原因**:数据库表结构可能不同,或者表名不是 `cli_surgery` - -**解决**:先执行以下 SQL 检查表名: - -```sql -SELECT table_name -FROM information_schema.tables -WHERE table_name LIKE '%surgery%' -AND table_schema = 'public'; -``` - -### 问题 2:执行 SQL 后字段仍然不存在 - -**原因**:可能是权限问题或 SQL 语法问题 - -**解决**:尝试使用更简单的方式: - -```sql --- 先检查表结构 -\d public.cli_surgery - --- 手动添加字段(如果不存在) --- 注意:如果字段已存在,这个语句会报错,这是正常的 -ALTER TABLE public.cli_surgery ADD COLUMN apply_doctor_name VARCHAR(100); -ALTER TABLE public.cli_surgery ADD COLUMN apply_dept_name VARCHAR(100); -``` - -### 问题 3:字段添加成功,但插入时仍然为空 - -**原因**:MyBatis 或 MyBatis-Plus 配置问题 - -**解决**: -1. 检查实体类字段是否有 `@TableField` 注解 -2. 检查字段名是否与数据库列名一致 -3. 查看后端日志中的 `插入后查询结果` - -### 问题 4:后端日志显示字段为 null - -**原因**:后端代码中 `applyDoctorName` 或 `applyDeptName` 被设置为 null - -**解决**: -1. 检查 `SecurityUtils.getLoginUser().getUser().getNickName()` 是否返回 null -2. 检查 `SecurityUtils.getLoginUser().getOrgId()` 是否返回 null -3. 检查 `organizationService.getById(orgId)` 是否返回 null - -## 验证清单 - -- [ ] 数据库迁移脚本已执行 -- [ ] 数据库字段已添加(步骤 1 验证 SQL 有结果) -- [ ] 后端服务已重启 -- [ ] 后端日志显示 `准备插入手术记录` 且字段有值 -- [ ] 后端日志显示 `插入后查询结果` 且字段有值 -- [ ] 数据库查询显示字段有值(步骤 4) -- [ ] 前端列表正确显示 -- [ ] 前端详情正确显示 - -## 调试 SQL 脚本 - -如果需要手动测试插入功能,可以执行: - -```sql --- 测试插入(确保字段存在) -INSERT INTO public.cli_surgery ( - surgery_no, - patient_id, - encounter_id, - apply_doctor_id, - apply_doctor_name, - apply_dept_id, - apply_dept_name, - surgery_name, - status_enum, - delete_flag, - create_time, - update_time -) VALUES ( - 'TEST202501050002', - (SELECT id FROM public.adm_patient WHERE delete_flag = '0' LIMIT 1), - (SELECT id FROM public.adm_encounter WHERE delete_flag = '0' LIMIT 1), - 999, - '手动测试医生', - 999, - '手动测试科室', - '手动测试手术', - 0, - '0', - NOW(), - NOW() -); - --- 查询刚才插入的数据 -SELECT - surgery_no, - apply_doctor_id, - apply_doctor_name, - apply_dept_id, - apply_dept_name, - surgery_name -FROM public.cli_surgery -WHERE surgery_no = 'TEST202501050002'; - --- 清理测试数据 --- DELETE FROM public.cli_surgery WHERE surgery_no = 'TEST202501050002'; -``` - -## 联系支持 - -如果以上步骤都无法解决问题,请提供: - -1. **数据库表结构查询结果**: - ```sql - \d public.cli_surgery - ``` - -2. **后端日志**:特别是 `准备插入手术记录` 和 `插入后查询结果` 这两行 - -3. **数据库查询结果**:执行步骤 4 中的 SQL,告诉我结果 - -4. **错误信息**:如果有任何错误提示 diff --git a/md/手术申请医生科室数据保存问题排查指南.md b/md/手术申请医生科室数据保存问题排查指南.md deleted file mode 100644 index b36dd32c..00000000 --- a/md/手术申请医生科室数据保存问题排查指南.md +++ /dev/null @@ -1,194 +0,0 @@ -# 手术申请医生科室数据保存问题排查指南 - -## 问题现象 -新增手术后,列表页面和编辑查看页面没有显示申请医生名称和科室名称。 - -## 排查步骤 - -### 步骤1:检查数据库字段是否存在 -执行以下 SQL 检查字段是否已添加: - -```sql -SELECT column_name, data_type -FROM information_schema.columns -WHERE table_name = 'cli_surgery' -AND column_name IN ('apply_doctor_name', 'apply_dept_name'); -``` - -**预期结果**:应该返回两条记录 -``` -apply_doctor_name | character varying -apply_dept_name | character varying -``` - -**如果结果为空**:说明字段未添加,需要执行迁移脚本。 - -### 步骤2:检查数据库迁移脚本是否执行 -执行迁移脚本(如果未执行): - -```sql --- 检查并添加申请医生姓名字段 -DO $$ -BEGIN - IF NOT EXISTS ( - SELECT 1 - FROM information_schema.columns - WHERE table_name = 'cli_surgery' - AND column_name = 'apply_doctor_name' - ) THEN - ALTER TABLE public.cli_surgery ADD COLUMN apply_doctor_name VARCHAR(100); - COMMENT ON COLUMN public.cli_surgery.apply_doctor_name IS '申请医生姓名'; - END IF; -END $$; - --- 检查并添加申请科室名称字段 -DO $$ -BEGIN - IF NOT EXISTS ( - SELECT 1 - FROM information_schema.columns - WHERE table_name = 'cli_surgery' - AND column_name = 'apply_dept_name' - ) THEN - ALTER TABLE public.cli_surgery ADD COLUMN apply_dept_name VARCHAR(100); - COMMENT ON COLUMN public.cli_surgery.apply_dept_name IS '申请科室名称'; - END IF; -END $$; -``` - -### 步骤3:重启后端服务 -执行数据库迁移后,必须重启后端服务。 - -### 步骤4:新增手术并查看后端日志 -1. 打开后端控制台或日志文件 -2. 在前端新增一条手术记录 -3. 查看后端日志,应该能看到以下信息: - -``` -设置申请医生信息 - doctorId: 123, doctorName: 张医生, deptId: 456, deptName: 普外科 -前端提交的数据 - applyDoctorId: 123, applyDoctorName: 张医生, applyDeptId: 456, applyDeptName: 普外科 -准备插入手术记录 - applyDoctorId: 123, applyDoctorName: 张医生, applyDeptId: 456, deptName: 普外科 -手术记录插入成功 - surgeryId: 1234567890123456789, surgeryNo: OP202501051234 -``` - -**如果看不到这些日志**:说明代码没有执行到这里,检查是否有异常抛出。 - -**如果看到 "前端提交的数据 - applyDoctorName: null"**:说明前端提交的数据为空,需要检查前端代码。 - -### 步骤5:检查数据库中是否保存成功 -执行以下 SQL 查询最新插入的记录: - -```sql -SELECT - id, - surgery_no, - patient_id, - apply_doctor_id, - apply_doctor_name, - apply_dept_id, - apply_dept_name, - surgery_name, - status_enum, - create_time -FROM public.cli_surgery -WHERE delete_flag = '0' -ORDER BY create_time DESC -LIMIT 5; -``` - -**如果 apply_doctor_name 和 apply_dept_name 字段为空**:说明数据没有保存成功。 - -**如果字段有值**:说明保存成功,问题出在前端显示。 - -### 步骤6:检查前端 API 响应 -1. 打开浏览器开发者工具(F12) -2. 切换到 Network 标签 -3. 新增手术 -4. 找到 `/clinical-manage/surgery/surgery-page` 请求 -5. 点击查看响应内容 - -检查响应数据中是否包含 `applyDoctorName` 和 `applyDeptName` 字段。 - -**如果响应中没有这些字段**:说明 MyBatis 映射有问题,检查 XML 配置。 - -**如果响应中有这些字段但值为 null**:说明数据库中为空,回到步骤5。 - -### 步骤7:检查前端表格显示 -查看前端代码中的表格列配置: - -```vue - - -``` - -确保 `prop` 属性与后端返回的字段名一致(注意大小写)。 - -## 常见问题和解决方案 - -### 问题1:数据库字段未添加 -**症状**:后端报错 "column apply_doctor_name does not exist" -**解决**:执行数据库迁移脚本 - -### 问题2:后端日志显示 applyDoctorName 为 null -**症状**:日志中 "前端提交的数据 - applyDoctorName: null" -**原因**:前端提交数据时,disabled 字段没有被包含 -**解决**:检查前端 submitForm 函数,确保手动设置了这些字段 - -### 问题3:数据库中有值,但前端不显示 -**症状**:数据库查询有值,前端响应也有值,但表格不显示 -**原因**: -1. 前端 prop 属性名与后端字段名不一致(大小写问题) -2. 前端数据未正确绑定 -**解决**: -1. 检查 prop 属性名,确保与后端返回的 JSON 字段名一致 -2. 检查浏览器控制台是否有 JavaScript 错误 - -### 问题4:MyBatis 映射未生效 -**症状**:后端保存成功,但查询时字段为 null -**原因**:XML 映射文件未正确配置 -**解决**: -1. 检查 SurgeryMapper.xml 中的 resultMap 配置 -2. 检查 SQL 查询中是否包含这些字段 -3. 重启后端服务 - -## 验证清单 - -- [ ] 数据库迁移脚本已执行 -- [ ] 数据库字段已添加(步骤1) -- [ ] 后端服务已重启 -- [ ] 后端日志显示申请医生信息(步骤4) -- [ ] 数据库中已保存数据(步骤5) -- [ ] 前端 API 响应包含这些字段(步骤6) -- [ ] 前端表格正确显示(步骤7) - -## 附加 SQL 脚本 - -### 查看统计信息 -```sql -SELECT - COUNT(*) as total_count, - COUNT(apply_doctor_name) as has_doctor_name_count, - COUNT(apply_dept_name) as has_dept_name_count -FROM public.cli_surgery -WHERE delete_flag = '0'; -``` - -### 手动更新测试数据 -如果需要手动更新已有的测试数据: - -```sql -UPDATE public.cli_surgery -SET apply_doctor_name = '测试医生', - apply_dept_name = '测试科室' -WHERE apply_doctor_name IS NULL -AND delete_flag = '0'; -``` - -## 联系支持 - -如果以上步骤都无法解决问题,请提供以下信息: -1. 数据库字段查询结果(步骤1) -2. 后端日志截图(步骤4) -3. 数据库查询结果(步骤5) -4. 浏览器 Network 响应截图(步骤6) -5. 浏览器 Console 错误信息 diff --git a/md/手术管理模块开发说明.md b/md/手术管理模块开发说明.md deleted file mode 100644 index f44e60bf..00000000 --- a/md/手术管理模块开发说明.md +++ /dev/null @@ -1,351 +0,0 @@ -# 手术管理模块开发说明 - -## 模块概述 - -手术管理模块是一个完整的医疗手术管理系统,涵盖从手术排期、执行到记录的全流程管理。本模块基于经典的Spring Boot + Vue3前后端分离架构开发。 - -## 功能特性 - -### 1. 手术信息管理 -- 手术基本信息录入(手术名称、编码、类型、等级) -- 患者信息关联 -- 就诊信息关联 -- 手术部位描述 - -### 2. 手术团队管理 -- 主刀医生选择 -- 麻醉医生选择 -- 助手1/助手2选择 -- 巡回护士选择 -- 麻醉方式选择 - -### 3. 手术状态管理 -- 待排期 -- 已排期 -- 手术中 -- 已完成 -- 已取消 -- 暂停 - -### 4. 手术时间管理 -- 计划手术时间 -- 实际开始时间 -- 实际结束时间 - -### 5. 诊断信息管理 -- 术前诊断 -- 术后诊断 -- 手术经过描述 -- 术后医嘱 -- 并发症描述 - -### 6. 手术费用管理 -- 手术费用 -- 麻醉费用 -- 总费用自动计算 - -### 7. 手术切口管理 -- 切口等级(I级、II级、III级、IV级) -- 愈合等级(甲级、乙级、丙级) - -## 技术架构 - -### 后端技术栈 -- Spring Boot 2.x -- MyBatis Plus -- PostgreSQL 12+ -- JDK 1.8+ - -### 前端技术栈 -- Vue 3.x -- Element Plus -- Axios -- Vite - -## 目录结构 - -``` -openh-is/ -├── openhis-server-new/ # 后端项目 -│ ├── openhis-domain/ # 领域层 -│ │ └── src/main/java/com/openhis/ -│ │ ├── clinical/ -│ │ │ ├── domain/ -│ │ │ │ └── Surgery.java # 手术实体类 -│ │ │ ├── mapper/ -│ │ │ │ └── SurgeryMapper.java # 手术Mapper接口 -│ │ │ └── service/ -│ │ │ ├── ISurgeryService.java # 手术Service接口 -│ │ │ └── impl/ -│ │ │ └── SurgeryServiceImpl.java # 手术Service实现 -│ │ └── common/ # 公共模块 -│ │ └── src/main/java/com/openhis/common/ -│ │ └── enums/ # 枚举类 -│ │ ├── SurgeryTypeEnum.java # 手术类型枚举 -│ │ ├── SurgeryStatusEnum.java # 手术状态枚举 -│ │ ├── SurgeryLevelEnum.java # 手术等级枚举 -│ │ ├── AnesthesiaTypeEnum.java # 麻醉方式枚举 -│ │ ├── IncisionLevelEnum.java # 切口等级枚举 -│ │ └── HealingLevelEnum.java # 愈合等级枚举 -│ │ -│ ├── openhis-application/ # 应用层 -│ │ └── src/main/java/com/openhis/web/clinicalmanage/ -│ │ ├── controller/ -│ │ │ └── SurgeryController.java # 手术控制器 -│ │ ├── dto/ -│ │ │ └── SurgeryDto.java # 手术数据传输对象 -│ │ ├── appservice/ -│ │ │ ├── ISurgeryAppService.java # 手术应用服务接口 -│ │ │ └── impl/ -│ │ │ └── SurgeryAppServiceImpl.java # 手术应用服务实现 -│ │ └── mapper/ -│ │ └── SurgeryAppMapper.java # 手术应用Mapper -│ │ -│ └── src/main/resources/mapper/ -│ ├── clinical/ -│ │ └── SurgeryMapper.xml # 手术Mapper XML -│ └── clinicalmanage/ -│ └── SurgeryMapper.xml # 手术应用Mapper XML -│ -├── openhis-ui-vue3/ # 前端项目 -│ └── src/ -│ ├── api/ -│ │ └── surgerymanage.js # 手术API接口 -│ └── views/ -│ └── surgerymanage/ -│ └── index.vue # 手术管理页面 -│ -└── surgery_manage_init.sql # 数据库初始化脚本 -``` - -## 数据库设计 - -### 主表:cli_surgery(手术管理表) - -| 字段名 | 类型 | 说明 | -|--------|------|------| -| id | bigint | 主键ID | -| surgery_no | varchar(50) | 手术编号(唯一) | -| patient_id | bigint | 患者ID | -| patient_name | varchar(100) | 患者姓名 | -| encounter_id | bigint | 就诊ID | -| surgery_name | varchar(200) | 手术名称 | -| surgery_code | varchar(100) | 手术编码 | -| surgery_type_enum | int2 | 手术类型 | -| surgery_level | int2 | 手术等级 | -| status_enum | int2 | 手术状态 | -| planned_time | timestamp | 计划手术时间 | -| actual_start_time | timestamp | 实际开始时间 | -| actual_end_time | timestamp | 实际结束时间 | -| main_surgeon_id | bigint | 主刀医生ID | -| main_surgeon_name | varchar(100) | 主刀医生姓名 | -| anesthetist_id | bigint | 麻醉医生ID | -| anesthetist_name | varchar(100) | 麻醉医生姓名 | -| anesthesia_type_enum | int2 | 麻醉方式 | -| body_site | varchar(200) | 手术部位 | -| preoperative_diagnosis | text | 术前诊断 | -| postoperative_diagnosis | text | 术后诊断 | -| surgery_fee | numeric(10,2) | 手术费用 | -| anesthesia_fee | numeric(10,2) | 麻醉费用 | -| total_fee | numeric(10,2) | 总费用 | - -### 字典表 - -手术管理模块包含以下字典类型: -- surgery_status(手术状态) -- surgery_type(手术类型) -- surgery_level(手术等级) -- anesthesia_type(麻醉方式) -- incision_level(切口等级) -- healing_level(愈合等级) - -## 安装部署 - -### 1. 数据库初始化 - -执行SQL脚本初始化数据库表和字典数据: - -```bash -psql -U postgres -d his_database -f surgery_manage_init.sql -``` - -或者使用psql客户端执行: - -```sql -\i /path/to/surgery_manage_init.sql -``` - -### 2. 后端配置 - -1. 将后端代码复制到对应目录 -2. 修改数据库连接配置(application.yml) -3. 启动Spring Boot应用 - -### 3. 前端配置 - -1. 将前端代码复制到对应目录 -2. 配置API接口地址(.env.development) -3. 启动前端开发服务器 - -```bash -npm install -npm run dev -``` - -## API接口说明 - -### 1. 分页查询手术列表 - -**接口地址:** `GET /clinical-manage/surgery/surgery-page` - -**请求参数:** -```json -{ - "pageNo": 1, - "pageSize": 10, - "surgeryNo": "SS20251230001", - "surgeryName": "阑尾切除术", - "patientName": "张三", - "statusEnum": 1, - "surgeryTypeEnum": 2 -} -``` - -### 2. 查询手术详情 - -**接口地址:** `GET /clinical-manage/surgery/surgery-detail` - -**请求参数:** -``` -id: 手术ID -``` - -### 3. 新增手术 - -**接口地址:** `POST /clinical-manage/surgery/surgery` - -**请求参数:** -```json -{ - "patientId": 1, - "surgeryName": "阑尾切除术", - "surgeryCode": "ICD-9-CM:47.09", - "surgeryTypeEnum": 2, - "surgeryLevel": 2, - "plannedTime": "2025-12-31 09:00:00", - "mainSurgeonId": 10, - "anesthetistId": 11, - "anesthesiaTypeEnum": 3, - "bodySite": "腹部" -} -``` - -### 4. 修改手术 - -**接口地址:** `PUT /clinical-manage/surgery/surgery` - -**请求参数:** 同新增手术,需包含id - -### 5. 删除手术 - -**接口地址:** `DELETE /clinical-manage/surgery/surgery` - -**请求参数:** -``` -id: 手术ID -``` - -### 6. 更新手术状态 - -**接口地址:** `PUT /clinical-manage/surgery/surgery-status` - -**请求参数:** -``` -id: 手术ID -statusEnum: 状态值 -``` - -## 前端页面功能 - -### 1. 查询功能 -- 支持按手术编号、手术名称、患者姓名模糊查询 -- 支持按手术状态、手术类型精确查询 -- 支持按计划时间范围查询 - -### 2. 新增功能 -- 完整的手术信息录入表单 -- 患者下拉选择 -- 医生/护士下拉选择 -- 费用自动计算 - -### 3. 编辑功能 -- 仅待排期和已排期状态的手术可编辑 -- 手术中或已完成的手术不可编辑 - -### 4. 状态流转 -- 已排期 → 手术中 -- 手术中 → 已完成 -- 待排期/已排期 → 已取消 - -### 5. 删除功能 -- 仅待排期和已排期状态的手术可删除 -- 已完成的手术不能删除 - -## 扩展开发建议 - -### 1. 手术排期管理 -- 可增加手术排期日历视图 -- 手术室资源冲突检测 -- 手术排队优先级管理 - -### 2. 手术统计报表 -- 手术量统计 -- 手术类型分布 -- 手术成功率统计 -- 手术费用统计 - -### 3. 手术文档管理 -- 手术知情同意书 -- 手术安全核查表 -- 手术记录单 -- 麻醉记录单 - -### 4. 手术质控管理 -- 手术质量评估 -- 并发症统计 -- 术后恢复跟踪 -- 手术质量指标管理 - -## 注意事项 - -1. **手术编号生成**:手术编号采用自动生成机制,格式为SS + 10位数字 -2. **权限控制**:需要配置相应的菜单权限和操作权限 -3. **数据校验**:新增手术时必须选择患者和主刀医生 -4. **状态流转**:手术状态的流转需要符合业务逻辑 -5. **费用计算**:总费用自动计算,不允许手动修改 - -## 常见问题 - -### Q1: 手术编号重复怎么办? -A: 手术编号是系统自动生成的唯一编号,不会重复。如果需要自定义编号,需要修改SurgeryServiceImpl中的生成逻辑。 - -### Q2: 如何添加新的手术类型? -A: 在数据库sys_dict_data表中添加新的surgery_type字典数据即可。 - -### Q3: 手术开始后还能修改信息吗? -A: 根据业务规则,手术开始后不允许修改基本信息,但可以补充术后诊断等信息。 - -### Q4: 如何实现手术室资源管理? -A: 可以新增手术室管理模块,建立手术排期与手术室的关联关系,实现资源冲突检测。 - -## 版本历史 - -- v1.0.0 (2025-12-30) - - 初始版本发布 - - 实现手术基本管理功能 - - 实现手术状态流转 - - 实现手术团队管理 - -## 联系方式 - -如有问题或建议,请联系开发团队。 diff --git a/md/未读公告弹窗实现方案.md b/md/未读公告弹窗实现方案.md deleted file mode 100644 index e69de29b..00000000 diff --git a/md/门诊就诊记录SQL优化建议.md b/md/门诊就诊记录SQL优化建议.md deleted file mode 100644 index bffeb5cd..00000000 --- a/md/门诊就诊记录SQL优化建议.md +++ /dev/null @@ -1,160 +0,0 @@ -# 门诊就诊记录SQL查询优化建议 - -## 当前查询分析 - -### 主要查询表 -```sql -SELECT - enc.id as encounterId, - pt.name, - pt.id_card, - pt.bus_no as patientBusNo, - enc.bus_no as encounterBusNo, - pt.gender_enum, - pt.phone, - enc.create_time as encounterTime, - enc.status_enum as subjectStatusEnum, - org.name as organizationName, - prac.name as doctorName -FROM adm_encounter AS enc -LEFT JOIN adm_organization AS org ON enc.organization_id = org.ID AND org.delete_flag = '0' -LEFT JOIN adm_encounter_participant AS ep - ON enc.ID = ep.encounter_id AND ep.type_code = #{participantType} AND ep.delete_flag = '0' -LEFT JOIN adm_practitioner AS prac ON ep.practitioner_id = prac.ID AND prac.delete_flag = '0' -LEFT JOIN adm_patient AS pt ON enc.patient_id = pt.ID AND pt.delete_flag = '0' -``` - -### 常见查询条件 -1. `enc.delete_flag = '0'` -2. `enc.tenant_id = ?` -3. `pt.name LIKE ?` -4. `pt.id_card LIKE ?` -5. `pt.bus_no LIKE ?` -6. `enc.bus_no LIKE ?` -7. `pt.gender_enum = ?` -8. `enc.status_enum = ?` -9. `prac.name LIKE ?` -10. `pt.phone LIKE ?` -11. `enc.create_time BETWEEN ? AND ?` - -## 索引优化建议 - -### 1. adm_encounter 表索引 -```sql --- 复合索引:提高查询性能 -CREATE INDEX idx_encounter_tenant_delete_status ON adm_encounter(tenant_id, delete_flag, status_enum); - --- 时间范围查询索引 -CREATE INDEX idx_encounter_create_time ON adm_encounter(create_time); - --- 业务编号查询索引 -CREATE INDEX idx_encounter_bus_no ON adm_encounter(bus_no); - --- 患者ID关联索引 -CREATE INDEX idx_encounter_patient_id ON adm_encounter(patient_id); -``` - -### 2. adm_patient 表索引 -```sql --- 姓名模糊查询索引 -CREATE INDEX idx_patient_name ON adm_patient(name); - --- 身份证号查询索引 -CREATE INDEX idx_patient_id_card ON adm_patient(id_card); - --- 业务编号查询索引 -CREATE INDEX idx_patient_bus_no ON adm_patient(bus_no); - --- 电话查询索引 -CREATE INDEX idx_patient_phone ON adm_patient(phone); - --- 复合索引:常用查询条件 -CREATE INDEX idx_patient_delete_gender ON adm_patient(delete_flag, gender_enum); -``` - -### 3. adm_encounter_participant 表索引 -```sql --- 复合索引:提高连接性能 -CREATE INDEX idx_ep_encounter_type ON adm_encounter_participant(encounter_id, type_code, delete_flag); - --- 参与者ID索引 -CREATE INDEX idx_ep_practitioner ON adm_encounter_participant(practitioner_id); -``` - -### 4. adm_practitioner 表索引 -```sql --- 姓名查询索引 -CREATE INDEX idx_practitioner_name ON adm_practitioner(name); - --- 复合索引:常用查询条件 -CREATE INDEX idx_practitioner_delete_tenant ON adm_practitioner(delete_flag, tenant_id); -``` - -### 5. adm_organization 表索引 -```sql --- 主键关联索引 -CREATE INDEX idx_organization_id_delete ON adm_organization(id, delete_flag); -``` - -## 查询优化建议 - -### 1. 添加查询统计信息收集 -```sql --- 定期分析表统计信息 -ANALYZE TABLE adm_encounter; -ANALYZE TABLE adm_patient; -ANALYZE TABLE adm_encounter_participant; -ANALYZE TABLE adm_practitioner; -ANALYZE TABLE adm_organization; -``` - -### 2. 考虑分区表(针对大数据量) -如果 `adm_encounter` 表数据量超过100万条,考虑按时间分区: -```sql --- 按月分区 -PARTITION BY RANGE (YEAR(create_time) * 100 + MONTH(create_time)) -( - PARTITION p202501 VALUES LESS THAN (202501), - PARTITION p202502 VALUES LESS THAN (202502), - -- ... 更多分区 -); -``` - -### 3. 添加覆盖索引(Covering Index) -对于常用查询字段,创建覆盖索引避免回表: -```sql -CREATE INDEX idx_encounter_cover ON adm_encounter( - tenant_id, delete_flag, create_time, - status_enum, bus_no, patient_id -) INCLUDE (organization_id); -``` - -## 执行计划检查 - -建议定期检查查询执行计划: -```sql -EXPLAIN ANALYZE -SELECT -- 完整查询语句 -FROM adm_encounter AS enc --- ... 连接条件 -WHERE enc.delete_flag = '0' - AND enc.tenant_id = 1 - -- ... 其他条件 -ORDER BY enc.create_time DESC; -``` - -## 监控建议 - -1. **慢查询监控**:监控执行时间超过1秒的查询 -2. **索引使用监控**:定期检查未使用的索引 -3. **表空间监控**:监控表增长和碎片情况 -4. **连接性能监控**:监控JOIN操作的性能 - -## 实施步骤 - -1. 在测试环境创建建议的索引 -2. 执行查询性能测试 -3. 分析执行计划,确认索引有效性 -4. 在生产环境非高峰期创建索引 -5. 监控生产环境性能变化 -6. 定期维护和优化索引 \ No newline at end of file diff --git a/md/需求/94-手术室维护界面_2026-1-9.md b/md/需求/94-手术室维护界面_2026-1-9.md new file mode 100644 index 00000000..d7a3f408 --- /dev/null +++ b/md/需求/94-手术室维护界面_2026-1-9.md @@ -0,0 +1,287 @@ +## 手术室维护界面PRD文档 + +### 一、页面概述 + +**页面名称**:手术室维护界面 +**页面目标**:提供手术室基础数据的维护功能,包括新增、编辑、启用/停用手术室信息,为手术安排提供基础数据支持 +**适用场景**:医院管理员需要新增、修改、启用/停用手术室信息时使用 +**页面类型**:列表页+表单页(含模态框) + +**原型图地址:**https://static.pm-ai.cn/prototype/20260104/ee5d222231effefcb39624d1646a2e20/index.html + +**核心功能**: + +1. 手术室列表展示与查询 +2. 新增手术室信息 +3. 编辑现有手术室信息 +4. 启用/停用手术室状态 +5. 数据有效性校验 + +**用户价值**: + +- 管理员可集中管理所有手术室基础信息 +- 确保手术安排时能获取准确的手术室数据 +- 通过状态管理控制手术室可用性 + +**流程图:** + +![](media/6a369a41c55f8727aa574bf43fa7500b.png) + +```mermaid +flowchart TD + A[手术室维护界面] --> B[手术室列表展示] + + B --> C[新增手术室] + + B --> D[编辑手术室] + + B --> E[启用/停用手术室] + + B --> F[查询手术室] + + C --> G[点击新增按钮] + + G --> H[打开新增模态框] + + H --> I[填写表单字段] + + I --> J{必填字段校验} + + J -->|通过| K[提交数据] + + J -->|不通过| L[提示请填写所有必填项] + + K --> M[表格新增数据行] + + D --> N[点击修改按钮] + + N --> O[打开编辑模态框] + + O --> P[修改表单字段] + + P --> Q{必填字段校验} + + Q -->|通过| R[保存数据] + + Q -->|不通过| S[提示请填写所有必填项] + + R --> T[更新表格对应行] + + E --> U[点击启用/停用按钮] + + U --> V{二次确认} + + V -->|确认| W[切换状态标签] + + V -->|取消| X[取消操作] + + W --> Y[更新按钮状态] + + F --> Z[输入查询条件] + + Z --> AA[筛选表格数据] + + K --> AB{房间号重复校验} + + AB -->|不重复| AC[提示房间号已存在] + + AB -->|重复| AD[更新按钮状态] +``` + +### 二、整体布局分析 + +**页面宽度**:自适应布局 +**主要区域划分**: + +1. 页头区域(15%高度) +2. 表格展示区(85%高度) + **布局特点**:上下布局,表格采用固定表头+滚动内容区设计 + **响应式要求**:移动端适配时改为纵向堆叠布局,操作按钮组变为纵向排列 + +### 三、页面区域详细描述 + +#### 1. 页头区域 + +**区域位置**:页面顶部 +**区域尺寸**:高度60px,宽度100% +**区域功能**:展示标题和主要操作入口 +**包含元素**: + +- **标题文本** + - 元素类型:H1标题 + - 显示内容:"手术室列表" + - 样式特征:1.75rem/600字重,深灰色(#333) +- **新增按钮** + - 元素类型:主要操作按钮 + - 显示内容:"新增"(带+图标) + - 交互行为:点击触发新增模态框 + - 样式特征:蓝色背景(#5a7cff),白色文字,8px圆角,悬停上浮1px + +#### 2. 表格展示区(手术室列表表格) + +**区域位置**:页头下方 +**区域尺寸**:高度自适应,宽度100% +**区域功能**:展示手术室数据并支持行级操作 +**包含元素**: + +- **数据表格** + - 展示方式:固定表头表格 + - 数据字段: + - 房间号:文本 - OR01 - 不可操作 + - 手术室名称:文本 - 第一手术室 - 不可操作 + - 类型:文本 - 普通/日间/复合 - 不可操作 + - 所属科室:文本 - 外科 - 不可操作 + - 状态:标签 - 有效/无效 - 通过操作按钮切换 + - 操作功能:每行包含"修改"和"状态切换(停用-黄色/启用-绿色)"按钮 +- **表格样式**: + - 表头:浅灰色背景(#f8f9fa),大写字母,14px字号 + - 行悬停:浅灰色背景(#f8f9fa) + - 状态标签: + - 有效:绿色背景+文字(#28a745) + - 无效:灰色背景+文字(#6c757d) + +#### 3. 新增手术室弹窗 + +**区域位置**:页面居中模态弹窗 +**区域功能**:收集新增手术室所需信息 +**包含元素**: + +- 表单字段: + 1. 房间号输入框 + 2. 类型:文本输入 + 3. 必填:是 + 4. 示例值:OR04 + 5. 校验规则:非空校验 + 6. 手术室名称输入框 + - 类型:文本输入 + - 必填:是 + - 示例值:第四手术室 + 1. 手术室类型下拉框 + - 类型:单选下拉 + - 选项:普通/日间/复合/特殊 + - 默认值:普通 + 1. 所属科室下拉框 + - 类型:单选下拉 + - 必填:是 + - 选项:外科/妇产科等8个科室 + - 默认提示:"请选择科室" +- 操作按钮: + - 取消按钮(灰色边框) + - 确认按钮(蓝色填充) + - 校验逻辑:必填字段非空校验 + - 成功反馈:提示"手术室添加成功" + - 失败反馈:提示"请填写所有必填项" + +#### 4. 编辑手术室弹窗 + +**区域位置**:页面居中模态弹窗 +**区域功能**:修改现有手术室信息 +**包含元素**: + +- 表单字段(同新增弹窗,带初始值) +- 操作按钮: + - 取消按钮 + - 保存按钮 + - 校验逻辑:同新增弹窗 + - 成功反馈:提示"手术室信息已更新" + +### 四、交互功能详细说明 + +#### 1. 新增手术室 + +**功能描述**:添加新的手术室记录 +**触发条件**:点击页头"新增"按钮 +**操作流程**: + +1. 打开新增模态框 +2. 填写必填字段(房间号、名称、科室) +3. 点击确认提交(插入his_or_room表) +4. 表格末尾新增数据行 + **异常处理**: +- 必填项为空时弹出"请填写所有必填项"提示 +- 房间号重复需在后端校验并提示 + +#### 2. 编辑手术室 + +**功能描述**:修改现有手术室信息 +**触发条件**:点击行内"修改"按钮 +**操作流程**: + +1. 打开编辑模态框(自动填充当前行数据) +2. 用户修改数据 +3. 点击"保存"时校验并更新对应行数据 + **状态保持**:记录当前编辑行索引确保数据更新准确 + +#### 3. 状态切换 + +**功能描述**:启用/停用手术室 +**触发条件**:点击"停用"或"启用"按钮 +**操作流程**: + +1. 弹出二次确认对话框 +2. 用户确认后切换状态标签 +3. 按钮变为相反操作(停用↔启用) +4. 、停用手术室 +- **步骤**: + 1. 查询需要停用的手术室记录。 + 2. 将 valid_flag 设置为 0(无效)。 +- **示例**: + + UPDATE his_or_room + + SET valid_flag = '0' + + WHERE room_code = 'OR06'; + +5\. 启用手术室 + +**步骤**: + +1. 查询需要启用的手术室记录。 + 1. 将 valid_flag 设置为 1(有效)。 +- **示例**: + + UPDATE his_or_room + + SET valid_flag = '1' + + WHERE room_code = 'OR06'; + + **防误操作**:所有状态变更需二次确认 + +### 五、数据结构说明(HIS_OR_ROOM手术室字典表) + +| **字段名称** | **数据类型** | **是否为空** | **说明/典型值** | **外键/来源** | +|--------------|--------------|--------------|-----------------|-------------------------------| +| room_id | VARCHAR(10) | N | 主键 | 自增主键 | +| room_code | VARCHAR(10) | N | 手术室房间号 | 自定义编码,如 OR01、OR02 | +| room_name | VARCHAR(100) | N | 手术室名称 | 如 "第一手术室"、"第二手术室" | +| room_type | VARCHAR(10) | N | 手术室类型 | 普通、日间、复合 | +| dept_code | VARCHAR(10) | N | 所属科室 | FK → 科室管理的科室代码 | +| valid_flag | CHAR(1) | N | 是否有效 | 1有效,0无效 | +| created_time | DATETIME | N | 创建时间 | 默认当前时间 | +| updated_time | DATETIME | N | 更新时间 | 默认当前时间,自动更新 | + +### 六、开发实现要点 + +**样式规范**: + +- 主色调:#5a7cff(按钮/交互元素) +- 辅助色:#7b8a8b(次要文本) +- 字体: + - 标题:1.75rem/600字重 + - 正文:0.875rem/400字重 +- 间距系统: + - 卡片内边距:24px + - 表单字段间距:16px + +**技术要求**: + +**注意事项**: + +1. 数据安全: + - 所有变更操作需记录操作日志 + - 停用状态的手术室需在前端标记不可预约 +2. 性能优化: + - 表格数据分页加载 + - 模态框使用懒加载 diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/basedatamanage/appservice/impl/OperatingRoomAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/basedatamanage/appservice/impl/OperatingRoomAppServiceImpl.java index 32d4f292..36cb8fb8 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/basedatamanage/appservice/impl/OperatingRoomAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/basedatamanage/appservice/impl/OperatingRoomAppServiceImpl.java @@ -6,6 +6,7 @@ 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.ChineseConvertUtils; +import com.core.common.utils.DictUtils; import com.core.common.utils.StringUtils; import com.openhis.administration.domain.OperatingRoom; import com.openhis.administration.mapper.OperatingRoomMapper; @@ -78,23 +79,12 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService { e.setStatusEnum_dictText(e.getStatusEnum() != null && e.getStatusEnum() == 1 ? "启用" : "停用"); // 类型 if (e.getRoomTypeEnum() != null) { - switch (e.getRoomTypeEnum()) { - case 1: - e.setRoomTypeEnum_dictText("急诊手术室"); - break; - case 2: - e.setRoomTypeEnum_dictText("择期手术室"); - break; - case 3: - e.setRoomTypeEnum_dictText("日间手术室"); - break; - case 4: - e.setRoomTypeEnum_dictText("复合手术室"); - break; - default: - e.setRoomTypeEnum_dictText("未知"); - break; - } + e.setRoomTypeEnum_dictText(DictUtils.getDictLabel("operating_room_type", String.valueOf(e.getRoomTypeEnum()))); + } + // 如果有机构ID,查询机构名称 + if (e.getOrganizationId() != null) { + String orgName = commonService.getOrgNameById(e.getOrganizationId()); + e.setOrganizationName(orgName); } // 拼音码 e.setPyStr(ChineseConvertUtils.toPinyinFirstLetter(e.getName())); @@ -127,23 +117,7 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService { // 类型描述 if (operatingRoom.getRoomTypeEnum() != null) { - switch (operatingRoom.getRoomTypeEnum()) { - case 1: - operatingRoomDto.setRoomTypeEnum_dictText("急诊手术室"); - break; - case 2: - operatingRoomDto.setRoomTypeEnum_dictText("择期手术室"); - break; - case 3: - operatingRoomDto.setRoomTypeEnum_dictText("日间手术室"); - break; - case 4: - operatingRoomDto.setRoomTypeEnum_dictText("复合手术室"); - break; - default: - operatingRoomDto.setRoomTypeEnum_dictText("未知"); - break; - } + operatingRoomDto.setRoomTypeEnum_dictText(DictUtils.getDictLabel("operating_room_type", String.valueOf(operatingRoom.getRoomTypeEnum()))); } // 如果有机构ID,查询机构名称 @@ -168,6 +142,11 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService { return R.fail("手术室名称不能为空"); } + // 校验房间号不能为空 + if (StringUtils.isEmpty(operatingRoomDto.getBusNo())) { + return R.fail("房间号不能为空"); + } + // 去除空格 String name = operatingRoomDto.getName().replaceAll("[  ]", ""); operatingRoomDto.setName(name); @@ -177,13 +156,14 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService { return R.fail("【" + name + "】已存在"); } + // 判断房间号是否已存在 + if (isExistBusNo(operatingRoomDto.getBusNo(), null)) { + return R.fail("房间号【" + operatingRoomDto.getBusNo() + "】已存在"); + } + OperatingRoom operatingRoom = new OperatingRoom(); BeanUtils.copyProperties(operatingRoomDto, operatingRoom); - // 生成编码 - String code = assignSeqUtil.getSeq(AssignSeqEnum.OPERATING_ROOM_BUS_NO.getPrefix(), 3); - operatingRoom.setBusNo(code); - // 拼音码 operatingRoom.setPyStr(ChineseConvertUtils.toPinyinFirstLetter(operatingRoomDto.getName())); // 五笔码 @@ -215,6 +195,11 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService { return R.fail("手术室名称不能为空"); } + // 校验房间号不能为空 + if (StringUtils.isEmpty(operatingRoomDto.getBusNo())) { + return R.fail("房间号不能为空"); + } + // 去除空格 String name = operatingRoomDto.getName().replaceAll("[  ]", ""); operatingRoomDto.setName(name); @@ -224,6 +209,11 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService { return R.fail("【" + name + "】已存在"); } + // 判断房间号是否已存在(排除自己) + if (isExistBusNo(operatingRoomDto.getBusNo(), operatingRoomDto.getId())) { + return R.fail("房间号【" + operatingRoomDto.getBusNo() + "】已存在"); + } + OperatingRoom operatingRoom = new OperatingRoom(); BeanUtils.copyProperties(operatingRoomDto, operatingRoom); @@ -331,4 +321,20 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService { } return operatingRoomService.count(queryWrapper) > 0; } + + /** + * 判断房间号是否已存在 + * + * @param busNo 房间号 + * @param excludeId 排除的ID + * @return 是否存在 + */ + private boolean isExistBusNo(String busNo, Long excludeId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(OperatingRoom::getBusNo, busNo); + if (excludeId != null) { + queryWrapper.ne(OperatingRoom::getId, excludeId); + } + return operatingRoomService.count(queryWrapper) > 0; + } } diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/administration/domain/OperatingRoom.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/administration/domain/OperatingRoom.java index 57573f35..ab10a32d 100644 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/administration/domain/OperatingRoom.java +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/administration/domain/OperatingRoom.java @@ -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; @@ -33,14 +34,16 @@ public class OperatingRoom extends HisBaseEntity { private String name; /** 手术室类型 */ - @Dict(dictCode = "operating_room_type") private Integer roomTypeEnum; + + @TableField(exist = false) private String roomTypeEnum_dictText; /** 所属机构ID */ private Long organizationId; /** 所属机构名称 */ + @TableField(exist = false) private String organizationName; /** 位置描述 */ @@ -64,6 +67,9 @@ public class OperatingRoom extends HisBaseEntity { /** 五笔码 */ private String wbStr; + /** 备注 */ + private String remark; + public OperatingRoom() { this.statusEnum = LocationStatus.ACTIVE.getValue(); } diff --git a/openhis-ui-vue3/package-lock.json b/openhis-ui-vue3/package-lock.json index db9d77ff..0a4c8bed 100644 --- a/openhis-ui-vue3/package-lock.json +++ b/openhis-ui-vue3/package-lock.json @@ -1637,6 +1637,7 @@ "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz", "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/lodash": "*" } @@ -1647,6 +1648,7 @@ "integrity": "sha512-gWEkeiyYE4vqjON/+Obqcoeffmk0NF15WSBwSs7zwVA2bAbTaE0SJ7P0WNGoJn8uE7fiaV5a7dKYIJriEqOrmA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -3333,6 +3335,7 @@ "resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -5575,13 +5578,15 @@ "version": "4.17.21", "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash-es": { "version": "4.17.21", "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash-unified": { "version": "1.0.3", @@ -6534,6 +6539,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -6774,6 +6780,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -6786,6 +6793,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -7037,6 +7045,7 @@ "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -7215,6 +7224,7 @@ "integrity": "sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -7247,6 +7257,7 @@ "version": "2.0.3", "resolved": "https://registry.npmmirror.com/segmentit/-/segmentit-2.0.3.tgz", "integrity": "sha512-7mn2XL3OdTUQ+AhHz7SbgyxLTaQRzTWQNVwiK+UlTO8aePGbSwvKUzTwE4238+OUY9MoR6ksAg35zl8sfTunQQ==", + "peer": true, "dependencies": { "preval.macro": "^4.0.0" } @@ -8446,6 +8457,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8807,6 +8819,7 @@ "integrity": "sha512-RzAr8LSvM8lmhB4tQ5OPcBhpjOZRZjuxv9zO5UcxeoY2bd3kP3Ticd40Qma9/BqZ8JS96Ll/jeBX9u+LJZrhVg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.19.3", "postcss": "^8.4.31", @@ -8911,6 +8924,7 @@ "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.25.tgz", "integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==", "license": "MIT", + "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.25", "@vue/compiler-sfc": "3.5.25", diff --git a/openhis-ui-vue3/src/action/nurseStation/temperatureSheet/datas.js b/openhis-ui-vue3/src/action/nurseStation/temperatureSheet/datas.js index 0e41bfd5..f6149c33 100644 --- a/openhis-ui-vue3/src/action/nurseStation/temperatureSheet/datas.js +++ b/openhis-ui-vue3/src/action/nurseStation/temperatureSheet/datas.js @@ -11,7 +11,7 @@ export default { inDiagName: '急性上呼吸道感染', name: '于浩', officeName: '住院科室', - title: '长春市朝阳区中医院', + title: '', operaDays: null, outdate: null, sex: '女', diff --git a/openhis-ui-vue3/src/components/Auto/printBills/exeOrderSheet.vue b/openhis-ui-vue3/src/components/Auto/printBills/exeOrderSheet.vue index f3c00a7f..d14db5a6 100644 --- a/openhis-ui-vue3/src/components/Auto/printBills/exeOrderSheet.vue +++ b/openhis-ui-vue3/src/components/Auto/printBills/exeOrderSheet.vue @@ -2,7 +2,7 @@
- 长春市朝阳区中医院医嘱执行单 + {{ userStore.hospitalName }}医嘱执行单
床号:{{ printData.patientInfo.encounterLocationName }} @@ -87,8 +87,13 @@ \ No newline at end of file diff --git a/openhis-ui-vue3/src/views/hospitalRecord/components/intOperRecordSheet.vue b/openhis-ui-vue3/src/views/hospitalRecord/components/intOperRecordSheet.vue index c1de82fc..92880ace 100644 --- a/openhis-ui-vue3/src/views/hospitalRecord/components/intOperRecordSheet.vue +++ b/openhis-ui-vue3/src/views/hospitalRecord/components/intOperRecordSheet.vue @@ -11,7 +11,7 @@ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.03); " > -
长春市朝阳区中医院
+
{{ userStore.hospitalName }}
-
长春市朝阳区中医院
+
{{ userStore.hospitalName }}
住院病案首页
diff --git a/openhis-ui-vue3/src/views/inHospitalManagement/charge/feeSettlement/components/chargeDialog.vue b/openhis-ui-vue3/src/views/inHospitalManagement/charge/feeSettlement/components/chargeDialog.vue index a4193357..ee2c4779 100644 --- a/openhis-ui-vue3/src/views/inHospitalManagement/charge/feeSettlement/components/chargeDialog.vue +++ b/openhis-ui-vue3/src/views/inHospitalManagement/charge/feeSettlement/components/chargeDialog.vue @@ -86,7 +86,7 @@ 金额已满足应收,不可继续添加
-
+
折扣:
-
长春市朝阳区中医院
+
{{ userStore.hospitalName }}
入院记录
姓名: [] @@ -98,6 +98,9 @@