docs(requirement): 添加手术室维护界面需求文档

- 创建手术室维护界面PRD文档
- 定义页面概述、核心功能和用户价值
- 设计整体布局和页面区域详细描述
- 规范交互功能和数据结构说明
- 说明开发实现要点和注意事项
- 移除中医诊断主诊断功能实现说明文档
- 移除公告通知弹窗功能说明文档
- 移除手术人员字段不显示问题解决方案文档
- 移除手术和麻醉信息Redis缓存实现说明文档
- 移除手术室管理添加类型和所属科室字段说明文档
This commit is contained in:
2026-01-13 14:41:27 +08:00
parent e20e2b637f
commit a47306825a
71 changed files with 630 additions and 2519 deletions

View File

@@ -1,179 +0,0 @@
# 中医诊断主诊断功能实现说明
## 问题描述
中医诊断在添加时无法设置主诊断标记,导致保存后无法正确标识主诊断。
## 问题原因
`addDiagnosisDialog.vue` 中保存中医诊断时,没有传递 `maindiseFlag`(主诊断标记)字段到后端。
## 解决方案
### 1. 前端修改
#### 1.1 添加主诊断UIaddDiagnosisDialog.vue
在诊断详情区域为每个中医诊断添加了主诊断复选框:
```vue
<el-checkbox
v-model="item.isMain"
label="主诊断"
:true-label="true"
:false-label="false"
border
size="small"
style="margin-right: 10px;"
@change="(value) => handleMaindise(value, index)"
/>
```
#### 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日

View File

@@ -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

View File

@@ -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
<el-descriptions-item label="主刀医生">{{ viewData.mainSurgeonName }}</el-descriptions-item>
<el-descriptions-item label="麻醉医生">{{ viewData.anesthetistName }}</el-descriptions-item>
<el-descriptions-item label="助手1">{{ viewData.assistant1Name }}</el-descriptions-item>
<el-descriptions-item label="助手2">{{ viewData.assistant2Name }}</el-descriptions-item>
<el-descriptions-item label="手术室">{{ viewData.operatingRoomName }}</el-descriptions-item>
<el-descriptions-item label="执行科室">{{ viewData.orgName }}</el-descriptions-item>
```
**确认**:这些字段名是否正确(注意驼峰命名)。
### 2. 检查浏览器控制台
1. 打开浏览器开发者工具F12
2. 切换到 Console 标签
3. 点击"查看"按钮
4. 查看是否有 JavaScript 错误
### 3. 检查 Network 响应
1. 切换到 Network 标签
2. 点击"查看"按钮
3. 找到 `/clinical-manage/surgery/surgery-detail` 请求
4. 查看响应内容
**检查**:响应数据中是否包含这些字段,值是什么。
## 常见问题
### 问题 1UPDATE SQL 执行失败
**症状**:报错 "relation does not exist" 或 "column does not exist"
**解决**
1. 检查表名是否正确sys_user 或 adm_practitioner
2. 检查字段名是否正确user_id 或 practitioner_id
### 问题 2UPDATE 后字段仍为 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 响应数据**:后端返回的完整数据

View File

@@ -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 缓存未命中)

View File

@@ -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;
```
## 技术支持
如有问题,请联系技术支持团队。

View File

@@ -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. **错误信息**:如果有任何错误提示

View File

@@ -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
<el-table-column label="申请医生" align="center" prop="applyDoctorName" width="100" />
<el-table-column label="申请科室" align="center" prop="applyDeptName" width="120" show-overflow-tooltip />
```
确保 `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 错误
### 问题4MyBatis 映射未生效
**症状**:后端保存成功,但查询时字段为 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 错误信息

View File

@@ -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)
- 初始版本发布
- 实现手术基本管理功能
- 实现手术状态流转
- 实现手术团队管理
## 联系方式
如有问题或建议,请联系开发团队。

View File

@@ -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. 定期维护和优化索引

View File

@@ -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. 性能优化:
- 表格数据分页加载
- 模态框使用懒加载

View File

@@ -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<OperatingRoom> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OperatingRoom::getBusNo, busNo);
if (excludeId != null) {
queryWrapper.ne(OperatingRoom::getId, excludeId);
}
return operatingRoomService.count(queryWrapper) > 0;
}
}

View File

@@ -1,6 +1,7 @@
package com.openhis.administration.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.core.common.core.domain.HisBaseEntity;
@@ -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();
}

View File

@@ -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",

View File

@@ -11,7 +11,7 @@ export default {
inDiagName: '急性上呼吸道感染',
name: '于浩',
officeName: '住院科室',
title: '长春市朝阳区中医院',
title: '',
operaDays: null,
outdate: null,
sex: '女',

View File

@@ -2,7 +2,7 @@
<div class="recordBill">
<div :id="'exeSheetTitle' + printData.id" class="printView_header">
<div style="text-align: center; height: 60px">
长春市朝阳区中医院医嘱执行单
{{ userStore.hospitalName }}医嘱执行单
</div>
<div>
<span style="display: inline-block; width: 100px">床号{{ printData.patientInfo.encounterLocationName }}</span>
@@ -87,8 +87,13 @@
</template>
<script>
import {getLodop} from '../../../plugins/print/LodopFuncs'
import useUserStore from '@/store/modules/user'
export default {
setup() {
const userStore = useUserStore();
return { userStore };
},
props: {
printData: {
type: Object,

View File

@@ -2,7 +2,7 @@
<div class="recordBill">
<div id="div1" class="printView_header">
<div style="text-align: center; font-size: 20px; height: 40px">
长春市朝阳区中医院输液执行单
{{ userStore.hospitalName }}输液执行单
</div>
<div>
<span>座位{{ printData.patientInfo.encounterLocationName }}</span>
@@ -61,8 +61,13 @@
</template>
<script>
import {getLodop} from '../../../plugins/print/LodopFuncs'
import useUserStore from '@/store/modules/user'
export default {
setup() {
const userStore = useUserStore();
return { userStore };
},
data() {
return {
printData: {

View File

@@ -1,6 +1,6 @@
<template>
<div class="printTicket">
<p>长春市朝阳区中医院</p>
<p>{{ userStore.hospitalName }}</p>
<div>
<span>姓名</span>
<span>{{ printData.patientName }}</span>
@@ -26,9 +26,14 @@
</template>
<script>
import JsBarcode from 'jsbarcode';
import useUserStore from '@/store/modules/user';
export default {
name: 'TriageTicket',
setup() {
const userStore = useUserStore();
return { userStore };
},
props: {
printData: {
type: Object,

View File

@@ -44,7 +44,7 @@
"top": 15,
"height": 16.5,
"width": 792,
"title": "长春市朝阳区中医院预交金收据",
"title": "{{HOSPITAL_NAME}}预交金收据",
"coordinateSync": false,
"widthHeightSync": false,
"fontWeight": "bold",

View File

@@ -44,7 +44,7 @@
"top": 38,
"height": 19.5,
"width": 255,
"title": "中药长春市朝阳区中医院",
"title": "中药{{HOSPITAL_NAME}}",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 11.25,

View File

@@ -108,7 +108,7 @@
"top": 58.5,
"height": 13.5,
"width": 145.5,
"title": "机构名称:长春市朝阳区中医院",
"title": "机构名称:{{HOSPITAL_NAME}}",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 9,

View File

@@ -44,7 +44,7 @@
"top": 22.5,
"height": 19.5,
"width": 420,
"title": "长春市朝阳区中医院",
"title": "{{HOSPITAL_NAME}}",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 20.25,

View File

@@ -43,7 +43,7 @@
"top": 39.5,
"height": 19.5,
"width": 255,
"title": "中药长春市朝阳区中医院",
"title": "中药{{HOSPITAL_NAME}}",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 11.25,

View File

@@ -44,7 +44,7 @@
"top": 22.5,
"height": 19.5,
"width": 595.5,
"title": "长春市朝阳区中医院",
"title": "{{HOSPITAL_NAME}}",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 20.25,

View File

@@ -44,7 +44,7 @@
"top": 22.5,
"height": 19.5,
"width": 420,
"title": "长春市朝阳区中医院",
"title": "{{HOSPITAL_NAME}}",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 20.25,

View File

@@ -44,7 +44,7 @@
"top": 22.5,
"height": 19.5,
"width": 595.5,
"title": "长春市朝阳区中医院",
"title": "{{HOSPITAL_NAME}}",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 20.25,

View File

@@ -44,7 +44,7 @@
"top": 15,
"height": 16.5,
"width": 225,
"title": "长春市朝阳区中医院门诊收费结算单",
"title": "{{HOSPITAL_NAME}}门诊收费结算单",
"coordinateSync": false,
"widthHeightSync": false,
"fontWeight": "bold",

View File

@@ -43,7 +43,7 @@
"top": 22.5,
"height": 19.5,
"width": 595.5,
"title": "长春市朝阳区中医院",
"title": "{{HOSPITAL_NAME}}",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 20.25,

View File

@@ -45,7 +45,7 @@
"top": 15,
"height": 16.5,
"width": 226.5,
"title": "长春市朝阳区中医院挂号收费明细",
"title": "{{HOSPITAL_NAME}}挂号收费明细",
"coordinateSync": false,
"widthHeightSync": false,
"fontWeight": "bold",

View File

@@ -43,7 +43,7 @@
"top": 20,
"height": 19.5,
"width": 228,
"title": "中药长春市朝阳区中医院",
"title": "中药{{HOSPITAL_NAME}}",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 11.25,

View File

@@ -44,7 +44,7 @@
"top": 20,
"height": 19.5,
"width": 420,
"title": "长春市朝阳区中医院",
"title": "{{HOSPITAL_NAME}}",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 11.25,

View File

@@ -44,7 +44,7 @@
"top": 38,
"height": 19.5,
"width": 255,
"title": "中药长春市朝阳区中医院",
"title": "中药{{HOSPITAL_NAME}}",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 11.25,

View File

@@ -43,7 +43,7 @@
"top": 22.5,
"height": 19.5,
"width": 420,
"title": "长春市朝阳区中医院",
"title": "{{HOSPITAL_NAME}}",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 20.25,

View File

@@ -1,4 +1,4 @@
import { createWebHistory, createRouter } from 'vue-router'
import {createWebHistory, createRouter} from 'vue-router'
/* Layout */
import Layout from '@/layout'
@@ -16,366 +16,86 @@ import Layout from '@/layout'
* roles: ['admin', 'common'] // 访问路由的角色权限
* permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限
* meta : {
noCache: true // 如果设置为true则不会被 <keep-alive> 缓存(默认 false)
title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字
icon: 'svg-name' // 设置该路由的图标对应路径src/assets/icons/svg
breadcrumb: false // 如果设置为false则不会在breadcrumb面包屑中显示
activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。
}
noCache: true // 如果设置为true则不会被 <keep-alive> 缓存(默认 false)
title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字
icon: 'svg-name' // 设置该路由的图标对应路径src/assets/icons/svg
breadcrumb: false // 如果设置为false则不会在breadcrumb面包屑中显示
activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。
}
*/
// 公共路由
export const constantRoutes = [
{
path: '/redirect',
component: Layout,
hidden: true,
children: [
{
path: '/redirect/:path(.*)',
component: () => import('@/views/redirect/index.vue')
}
]
},
{
path: '/login',
component: () => import('@/views/login'),
hidden: true
},
{
path: '/register',
component: () => import('@/views/register'),
hidden: true
},
{
path: '/401',
component: () => import('@/views/error/401'),
hidden: true
},
{
path: '',
component: Layout,
redirect: '/index',
children: [
{
path: '/index',
component: () => import('@/views/index'),
name: 'Index',
meta: { title: '首页', icon: 'dashboard', affix: true }
}
]
},
{
path: '/user',
component: Layout,
hidden: true,
redirect: 'noredirect',
children: [
{
path: 'profile',
component: () => import('@/views/system/user/profile/index'),
name: 'Profile',
meta: { title: '个人中心', icon: 'user' }
}
]
},
// 添加套餐管理相关路由到公共路由,确保始终可用
{
path: '/maintainSystem/Inspection/PackageManagement',
component: Layout,
hidden: true,
children: [
{
{
path: '/redirect',
component: Layout,
hidden: true,
children: [
{
path: '/redirect/:path(.*)',
component: () => import('@/views/redirect/index.vue')
}
]
},
{
path: '/login',
component: () => import('@/views/login'),
hidden: true
},
{
path: '/register',
component: () => import('@/views/register'),
hidden: true
},
{
path: '/401',
component: () => import('@/views/error/401'),
hidden: true
},
{
path: '',
component: () => import('@/views/maintainSystem/Inspection/PackageManagement.vue'),
name: 'DirectPackageManagement',
meta: { title: '套餐管理' }
}
]
}
component: Layout,
redirect: '/index',
children: [
{
path: '/index',
component: () => import('@/views/index'),
name: 'Index',
meta: {title: '首页', icon: 'dashboard', affix: true}
}
]
},
{
path: '/user',
component: Layout,
hidden: true,
redirect: 'noredirect',
children: [
{
path: 'profile',
component: () => import('@/views/system/user/profile/index'),
name: 'Profile',
meta: {title: '个人中心', icon: 'user'}
}
]
}
]
// 动态路由,基于用户权限动态去加载
export const dynamicRoutes = [
export const dynamicRoutes = [
{
path: '/basicmanage',
component: Layout,
redirect: '/basicmanage/invoice-management',
name: 'BasicManage',
meta: { title: '基础管理', icon: 'component' },
children: [
{
path: 'invoice-management',
component: () => import('@/views/basicmanage/InvoiceManagement/index.vue'),
name: 'invoice-management',
meta: { title: '发票管理' }
}
]
path: '/help-center',
component: Layout,
hidden: true,
children: [
{
path: '',
component: () => import('@/views/helpcenter/index.vue'),
name: 'HelpCenter',
meta: {title: '帮助中心'},
},
],
},
// 兼容系统业务管理路径
// {
// path: '/system/ywgz',
// component: Layout,
// redirect: '/system/ywgz/InvoiceManagement',
// hidden: true,
// children: [
// {
// path: 'InvoiceManagement',
// component: () => import('@/views/basicmanage/InvoiceManagement/index.vue'),
// name: 'SystemInvoiceManagement',
// meta: { title: '发票管理' }
// }
// ]
// },
// {
// path: '/maintainSystem',
// component: Layout,
// redirect: '/maintainSystem/chargeConfig',
// name: 'MaintainSystem',
// meta: { title: '维护系统', icon: 'system' },
// children: [
// {
// path: '',
// redirect: 'chargeConfig'
// },
// {
// path: 'chargeConfig',
// component: () => import('@/views/maintainSystem/chargeConfig/index.vue'),
// name: 'ChargeConfig',
// meta: { title: '挂号收费系统参数维护', icon: 'config', permissions: ['maintainSystem:chargeConfig:list'] }
// },
// {
// path: 'Inspection',
// component: () => import('@/views/maintainSystem/Inspection/index.vue'),
// name: 'Inspection',
// meta: { title: '检验管理', icon: 'inspection' },
// children: [
// {
// path: 'PackageManagement',
// component: () => import('@/views/maintainSystem/Inspection/PackageManagement.vue'),
// name: 'PackageManagement',
// meta: { title: '套餐管理' }
// }
// ]
// }
// ]
// },
// {
// path: '/system',
// component: Layout,
// redirect: '/system/user',
// name: 'System',
// meta: { title: '系统管理', icon: 'system' },
// children: [
// {
// path: 'user',
// component: () => import('@/views/system/user/index.vue'),
// name: 'User',
// meta: { title: '用户管理', icon: 'user', permissions: ['system:user:list'] }
// },
// {
// path: 'role',
// component: () => import('@/views/system/role/index.vue'),
// name: 'Role',
// meta: { title: '角色管理', icon: 'role', permissions: ['system:role:list'] }
// },
// {
// path: 'menu',
// component: () => import('@/views/system/menu/index.vue'),
// name: 'Menu',
// meta: { title: '菜单管理', icon: 'menu', permissions: ['system:menu:list'] }
// },
// {
// path: 'dept',
// component: () => import('@/views/system/dept/index.vue'),
// name: 'Dept',
// meta: { title: '部门管理', icon: 'dept', permissions: ['system:dept:list'] }
// },
// {
// path: 'post',
// component: () => import('@/views/system/post/index.vue'),
// name: 'Post',
// meta: { title: '岗位管理', icon: 'post', permissions: ['system:post:list'] }
// },
// {
// path: 'dict',
// component: () => import('@/views/system/dict/index.vue'),
// name: 'Dict',
// meta: { title: '字典管理', icon: 'dict', permissions: ['system:dict:list'] }
// },
// {
// path: 'config',
// component: () => import('@/views/system/config/index.vue'),
// name: 'Config',
// meta: { title: '参数配置', icon: 'config', permissions: ['system:config:list'] }
// },
// {
// path: 'notice',
// component: () => import('@/views/system/notice/index.vue'),
// name: 'Notice',
// meta: { title: '通知公告', icon: 'notice', permissions: ['system:notice:list'] }
// },
// {
// path: 'tenant',
// component: () => import('@/views/system/tenant/index.vue'),
// name: 'Tenant',
// meta: { title: '租户管理', icon: 'tenant', permissions: ['system:tenant:list'] }
// }
// ]
// },
{
path: '/system/tenant-user',
component: Layout,
hidden: true,
permissions: ['*:*:*'],
children: [
{
path: 'set/:tenantId(\\d+)',
component: () => import('@/views/system/tenant/setUser'),
name: 'SetUser',
meta: { title: '所属用户', activeMenu: '/system/tenant' }
}
]
},
{
path: '/system/tenant-contract',
component: Layout,
hidden: true,
permissions: ['*:*:*'],
children: [
{
path: 'set/:tenantId(\\d+)',
component: () => import('@/views/system/tenant/setContract'),
name: 'SetContract',
meta: { title: '合同管理', activeMenu: '/system/tenant' }
}
]
},
{
path: '/system/user-auth',
component: Layout,
hidden: true,
permissions: ['system:user:edit'],
children: [
{
path: 'role/:userId(\\d+)',
component: () => import('@/views/system/user/authRole'),
name: 'AuthRole',
meta: { title: '分配角色', activeMenu: '/system/user' }
}
]
},
{
path: '/system/role-auth',
component: Layout,
hidden: true,
permissions: ['system:role:edit'],
children: [
{
path: 'user/:roleId(\\d+)',
component: () => import('@/views/system/role/authUser'),
name: 'AuthUser',
meta: { title: '分配用户', activeMenu: '/system/role' }
}
]
},
{
path: '/system/dict-data',
component: Layout,
hidden: true,
permissions: ['system:dict:list'],
children: [
{
path: 'index/:dictId(\\d+)',
component: () => import('@/views/system/dict/data'),
name: 'Data',
meta: { title: '字典数据', activeMenu: '/system/dict' }
}
]
},
{
path: '/monitor',
component: Layout,
redirect: '/monitor/operlog',
name: 'Monitor',
meta: { title: '系统监控', icon: 'monitor' },
children: [
{
path: 'operlog',
component: () => import('@/views/monitor/operlog/index.vue'),
name: 'Operlog',
meta: { title: '操作日志', icon: 'operlog', permissions: ['monitor:operlog:list'] }
},
{
path: 'logininfor',
component: () => import('@/views/monitor/logininfor/index.vue'),
name: 'Logininfor',
meta: { title: '登录日志', icon: 'logininfor', permissions: ['monitor:logininfor:list'] }
},
{
path: 'job',
component: () => import('@/views/monitor/job/index.vue'),
name: 'Job',
meta: { title: '定时任务', icon: 'job', permissions: ['monitor:job:list'] }
}
]
},
{
path: '/tool',
component: Layout,
redirect: '/tool/gen',
name: 'Tool',
meta: { title: '系统工具', icon: 'tool' },
children: [
{
path: 'gen',
component: () => import('@/views/tool/gen/index.vue'),
name: 'Gen',
meta: { title: '代码生成', icon: 'gen', permissions: ['tool:gen:list'] }
}
]
},
{
path: '/monitor/job-log',
component: Layout,
hidden: true,
permissions: ['monitor:job:list'],
children: [
{
path: 'index/:jobId(\\d+)',
component: () => import('@/views/monitor/job/log'),
name: 'JobLog',
meta: { title: '调度日志', activeMenu: '/monitor/job' }
}
]
},
{
path: '/tool/gen-edit',
component: Layout,
hidden: true,
permissions: ['tool:gen:edit'],
children: [
{
path: 'index/:tableId(\\d+)',
component: () => import('@/views/tool/gen/editTable'),
name: 'GenEdit',
meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
}
]
},
{
path: '/help-center',
component: Layout,
hidden: true,
children: [
{
path: '',
component: () => import('@/views/helpcenter/index.vue'),
name: 'HelpCenter',
meta: { title: '帮助中心'},
},
],
},
];
// 合并常量路由和动态路由,确保所有路由都能被访问
@@ -383,21 +103,21 @@ const allRoutes = [...constantRoutes, ...dynamicRoutes];
// 添加404路由到所有路由的最后
allRoutes.push({
path: "/:pathMatch(.*)*",
component: () => import('@/views/error/404'),
hidden: true
path: "/:pathMatch(.*)*",
component: () => import('@/views/error/404'),
hidden: true
});
const router = createRouter({
history: createWebHistory(),
routes: allRoutes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { top: 0 }
}
},
history: createWebHistory(),
routes: allRoutes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return {top: 0}
}
},
});
export default router;

View File

@@ -5,7 +5,7 @@
patient?.busNo || '未知'
}}
</div>
<h2 style="text-align: center">长春市朝阳区中医院</h2>
<h2 style="text-align: center">{{ userStore.hospitalName }}</h2>
<h2 style="text-align: center">出院诊断病历</h2>
<!-- 滚动内容区域 -->
@@ -143,6 +143,9 @@ import {nextTick, onMounted, reactive, ref} from 'vue';
import {ElMessage} from 'element-plus';
import {previewPrint} from '../utils/printUtils';
import DisDiagnMedicalRecord from '../views/hospitalRecord/components/disDiagnMedicalRecord.vue';
import useUserStore from '@/store/modules/user';
const userStore = useUserStore();
defineOptions({
name: 'DischargeDiagnosisCertificate',

View File

@@ -1,5 +1,8 @@
<script lang="ts" setup>
import {ref} from 'vue';
import useUserStore from '@/store/modules/user';
const userStore = useUserStore();
// 1. 基础信息(复用已有变量,补充一致性格式)
const patientInfo = ref({
@@ -117,8 +120,8 @@ defineExpose({ patientInfo, firstRecordTime, firstRecordIntro, caseFeatures, chi
<div class="medical-record">
<!-- 1. 医院头部每一页PDF均包含复用已有样式 -->
<div class="hospital-header">
<img src="./imgs/logo.png" alt="长春市朝阳区中医院Logo" class="header-logo" />
<h1 class="hospital-name">长春市朝阳区中医院</h1>
<img src="./imgs/logo.png" :alt="userStore.hospitalName + 'Logo'" class="header-logo" />
<h1 class="hospital-name">{{ userStore.hospitalName }}</h1>
</div>
<!-- 2. 患者信息栏每一页PDF均包含下划线样式 -->

View File

@@ -4,7 +4,7 @@
<!-- 医院头部 -->
<div class="hospital-header">
<h1 class="hospital-name">
<span class="hospital-text">长春市朝阳区中医院</span>
<span class="hospital-text">{{ userStore.hospitalName }}</span>
</h1>
</div>
<!-- 页面标题 -->

View File

@@ -1,7 +1,7 @@
<template>
<div class="medical-form">
<h2 style="text-align: center">
{{ userStore.hospitalName || '长春市朝阳区中医院' }} -入院记录
{{ userStore.hospitalName }} -入院记录
</h2>
<!-- 滚动内容区域 -->

View File

@@ -232,23 +232,13 @@
<script setup>
import {onMounted, reactive, ref} from 'vue';
import intOperRecordSheet from '../views/hospitalRecord/components/intOperRecordSheet.vue';
import {
ElButton,
ElDatePicker,
ElForm,
ElFormItem,
ElInput,
ElMessage,
ElMessageBox,
ElOption,
ElSelect,
} from 'element-plus';
import {previewPrint} from '../utils/printUtils';
import useUserStore from '@/store/modules/user';
const userStore = useUserStore();
const isShowprintDom = ref(false);
const recordPrintRef = ref();
// 医院名称
const hospitalName = '长春市朝阳区中医院';
const hospitalName = userStore.hospitalName;
defineOptions({
name: 'iInHospitalSurgicalRecord',
});

View File

@@ -213,20 +213,11 @@
<script setup>
import {onMounted, reactive, ref} from 'vue';
import {
ElButton,
ElDatePicker,
ElForm,
ElFormItem,
ElInput,
ElMessage,
ElMessageBox,
ElOption,
ElSelect,
} from 'element-plus';
import useUserStore from '@/store/modules/user';
const userStore = useUserStore();
// 医院名称
const hospitalName = '长春市朝阳区中医院';
const hospitalName = userStore.hospitalName;
defineOptions({
name: 'InHospitalCommunicate',
});

View File

@@ -6,7 +6,7 @@
<template>
<div class="container">
<div class="header">
<h2 class="title">长春市朝阳区中医院</h2>
<h2 class="title">{{ userStore.hospitalName }}</h2>
<h3 class="subtitle">患者护理记录单</h3>
</div>
@@ -234,8 +234,10 @@
defineOptions({
name: 'NursingRecordSheet',
});
import {getCurrentInstance, onBeforeMount, onMounted} from 'vue';
import {getCurrentInstance, onBeforeMount, onMounted, ref} from 'vue';
import useUserStore from '@/store/modules/user';
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
const emits = defineEmits([]);
const props = defineProps({

View File

@@ -5,7 +5,7 @@
patient?.busNo || '未知'
}}
</div>
<h2 style="text-align: center">{{ userStore.hospitalName || '长春市朝阳区中医院' }}</h2>
<h2 style="text-align: center">{{ userStore.hospitalName }}</h2>
<h2 style="text-align: center">门诊病历</h2>

View File

@@ -7,7 +7,7 @@
<div class="surgicalPatientHandover-container">
<div class="handover-form">
<div class="form-header">
<h1 class="hospital-name">长春市朝阳区中医院</h1>
<h1 class="hospital-name">{{ userStore.hospitalName }}</h1>
<h2 class="form-title">手术患者交接单</h2>
</div>
@@ -562,6 +562,9 @@ defineOptions({
});
import {getCurrentInstance, onBeforeMount, onMounted, reactive} from 'vue';
import useOptionsList from './useOptionsList';
import useUserStore from '@/store/modules/user';
const userStore = useUserStore();
// import { A } from '../../dist/assets/api-DmiMW8YF';
const { statisticsOptionList, getStatisticsOptionList } = useOptionsList();
const { proxy } = getCurrentInstance();

View File

@@ -228,7 +228,11 @@ export function executePrint(data, template, printerName, options = {}, business
throw new Error('打印插件未加载');
}
const hiprintTemplate = new window.hiprint.PrintTemplate({ template });
const userStore = useUserStore();
const processedTemplate = JSON.parse(
JSON.stringify(template).replace(/{{HOSPITAL_NAME}}/g, userStore.hospitalName)
);
const hiprintTemplate = new window.hiprint.PrintTemplate({ template: processedTemplate });
const printOptions = {
title: '打印标题',
height: 210,

View File

@@ -674,7 +674,9 @@ async function print() {
};
console.log(result, '==result.data==');
const printElements = templateJson;
const printElements = JSON.parse(
JSON.stringify(templateJson).replace(/{{HOSPITAL_NAME}}/g, userStore.hospitalName)
);
var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板
const printerList = hiprintTemplate.getPrinterList();
console.log(hiprintTemplate, '打印机列表');

View File

@@ -110,7 +110,7 @@
"top": 57,
"height": 13.5,
"width": 145.5,
"title": "机构名称:长春市朝阳区中医院",
"title": "机构名称:{{HOSPITAL_NAME}}",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 9,

View File

@@ -356,7 +356,7 @@ async function print() {
...reportValue.value, // 将 reportValue.value 中的所有属性展开到 result 中
nickName: userStore.nickName,
orgName: userStore.orgName,
fixmedinsName: '长春市朝阳区中医院医院',
fixmedinsName: userStore.hospitalName,
queryTime: queryTime.value[0] + '~' + queryTime.value[1],
zfAmount: new Decimal(reportValue.value.zhSum || 0).add(reportValue.value.fundSum || 0),
feeAmount: new Decimal(reportValue.value.DIAGNOSTIC_FEE || 0)

View File

@@ -514,7 +514,9 @@ async function print() {
};
console.log(result, '==result.data==');
const printElements = templateJson;
const printElements = JSON.parse(
JSON.stringify(templateJson).replace(/{{HOSPITAL_NAME}}/g, userStore.hospitalName)
);
var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板
const printerList = hiprintTemplate.getPrinterList();
console.log(hiprintTemplate, '打印机列表');

View File

@@ -34,7 +34,7 @@
"top": 22.5,
"height": 12,
"width": 420,
"title": "长春市朝阳区中医院",
"title": "{{HOSPITAL_NAME}}",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 13.5,

View File

@@ -37,7 +37,7 @@
"top": 16.5,
"height": 22.5,
"width": 120,
"title": "长春市朝阳区中医院医院",
"title": "{{HOSPITAL_NAME}}医院",
"coordinateSync": false,
"widthHeightSync": false,
"fontFamily": "Microsoft YaHei",

View File

@@ -660,7 +660,9 @@ function printPrescription() {
// 将对象转换为 JSON 字符串
console.log(result, 'result');
// 模板对象获取
const printElements = prescriptionTemplate;
const printElements = JSON.parse(
JSON.stringify(prescriptionTemplate).replace(/{{HOSPITAL_NAME}}/g, userStore.hospitalName)
);
var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板
hiprintTemplate.print2(result, {
title: '打印标题',
@@ -695,7 +697,9 @@ function printDisposal() {
.join(',');
advicePrint({ requestIds: requestIds }).then((res) => {
const result = res.data;
const printElements = disposalTemplate;
const printElements = JSON.parse(
JSON.stringify(disposalTemplate).replace(/{{HOSPITAL_NAME}}/g, userStore.hospitalName)
);
var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板
hiprintTemplate.print2(result, {
height: 210,
@@ -735,7 +739,9 @@ function printBloodBarcode() {
} else {
printBloodCode({ requestId: selectedRows[0].requestId }).then((res) => {
const result = res.data;
const printElements = bloodTemplate;
const printElements = JSON.parse(
JSON.stringify(bloodTemplate).replace(/{{HOSPITAL_NAME}}/g, userStore.hospitalName)
);
var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板
hiprintTemplate.print2(result, {
height: 210,

View File

@@ -27,7 +27,7 @@
<span>CF0000000001</span>
</div>
<div style="text-align: center">
<h2>长春市朝阳区中医院医院</h2>
<h2>{{ userStore.hospitalName }}</h2>
</div>
<div style="text-align: center">
<h3>处方单</h3>
@@ -130,7 +130,9 @@
<script setup name="historicalPrescriptionDetail">
import {getPrescriptionDetail} from './api';
import useUserStore from '@/store/modules/user';
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
const props = defineProps({

View File

@@ -19,7 +19,7 @@
<span>{{ item.prescriptionNo }}</span>
</div>
<div style="text-align: center">
<h2>长春市朝阳区中医院医院</h2>
<h2>{{ userStore.hospitalName }}</h2>
</div>
<div style="text-align: center">
<h3>处方单</h3>
@@ -144,6 +144,9 @@
import {formatDateStr} from '@/utils/index';
//高精度库
import Decimal from 'decimal.js';
import useUserStore from '@/store/modules/user';
const userStore = useUserStore();
const props = defineProps({
open: {

View File

@@ -15,7 +15,7 @@
"top": 16.5,
"height": 22.5,
"width": 120,
"title": "长春市朝阳区中医院医院",
"title": "{{HOSPITAL_NAME}}医院",
"coordinateSync": false,
"widthHeightSync": false,
"fontFamily": "Microsoft YaHei",

View File

@@ -2,7 +2,7 @@
<div style="padding: 20px; max-width: 1200px; margin: 0 auto" ref="bodyRef">
<!-- 标题区域 - 居中加粗增加层次感 -->
<div style="text-align: center; margin-bottom: 30px">
<div style="font-size: 22px; color: #333; letter-spacing: 1px">长春市朝阳区中医院</div>
<div style="font-size: 22px; color: #333; letter-spacing: 1px">{{ userStore.hospitalName }}</div>
<div
style="
font-size: 32px;
@@ -614,7 +614,7 @@
border-top: 1px solid #eee;
"
>
本记录由长春市朝阳区中医院医师根据患者病情如实记录仅供临床诊疗参考 |
本记录由{{ userStore.hospitalName }}医师根据患者病情如实记录仅供临床诊疗参考 |
地址长春市朝阳区XX街XX号 | 联系电话0431-XXXXXXX
</div>
@@ -629,7 +629,9 @@
<script setup>
import {reactive, ref} from 'vue';
import useUserStore from '@/store/modules/user';
const userStore = useUserStore();
const bodyRef = ref();
// 响应式表单数据

View File

@@ -21,7 +21,7 @@
margin-bottom: 8px;
"
>
长春市朝阳区中医院
{{ userStore.hospitalName }}
</div>
<div
style="
@@ -500,7 +500,9 @@
<script setup>
import {reactive, ref} from 'vue'; // 补充缺失的reactive导入
import useUserStore from '@/store/modules/user';
const userStore = useUserStore();
const bodyRef = ref(null);
const showPrintPreview = ref(false); // 控制弹窗显隐

View File

@@ -50,7 +50,7 @@
word-wrap: break-word;
"
>
长春市朝阳区中医院
{{ userStore.hospitalName }}
</div>
<div
style="
@@ -721,7 +721,7 @@
word-wrap: break-word;
"
>
长春市朝阳区中医院
{{ userStore.hospitalName }}
</div>
<div
style="
@@ -1346,8 +1346,10 @@
</template>
<script setup>
import {reactive, ref} from 'vue';
import {computed, reactive, ref} from 'vue';
import useUserStore from '@/store/modules/user';
const userStore = useUserStore();
const bodyRef = ref();
const printContentRef = ref(); // 新增打印内容的ref
const showPrintPreview = ref(false); // 控制打印预览弹窗显示

View File

@@ -3,7 +3,7 @@
<!-- 标题区域 - 强化正式感 -->
<div
style="text-align: center; margin-bottom: 30px; padding: 20px; background: #fff; border-radius: 12px; box-shadow: 0 2px 6px rgba(0,0,0,0.03);">
<div style="font-size: 22px; color: #2d3748; letter-spacing: 1px;">长春市朝阳区中医院</div>
<div style="font-size: 22px; color: #2d3748; letter-spacing: 1px;">{{ userStore.hospitalName }}</div>
<div
style="font-size: 28px; font-weight: 700; margin: 12px 0; padding: 8px 0; border-bottom: 2px solid #e8f4f8; display: inline-block;">
住院患者入院沟通记录单</div>
@@ -343,5 +343,7 @@
}
</style>
<script setup>
import useUserStore from '@/store/modules/user';
const userStore = useUserStore();
</script>

View File

@@ -11,7 +11,7 @@
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.03);
"
>
<div style="font-size: 22px; color: #2d3748; letter-spacing: 1px">长春市朝阳区中医院</div>
<div style="font-size: 22px; color: #2d3748; letter-spacing: 1px">{{ userStore.hospitalName }}</div>
<div
style="
font-size: 28px;
@@ -823,7 +823,9 @@
<script setup>
import {reactive, ref} from 'vue';
import useUserStore from '@/store/modules/user';
const userStore = useUserStore();
const bodyRef = ref();
// 响应式表单数据
const formData = reactive({

View File

@@ -4,7 +4,7 @@
<div
style="display: flex; flex-direction: column; justify-content: center; align-items: center"
>
<div style="font-size: 20px">长春市朝阳区中医院</div>
<div style="font-size: 20px">{{ userStore.hospitalName }}</div>
<div style="font-size: 30px; font-weight: bold; padding-top: 10px">住院病案首页</div>
</div>
<!-- 线 -->

View File

@@ -86,7 +86,7 @@
金额已满足应收不可继续添加
</el-text>
</div>
<div style="margin-top: 10px" v-if="userStore.hospitalName == '长春市朝阳区中医院'">
<div style="margin-top: 10px" v-if="userStore.fixmedinsCode == 'H22010200672'">
<span>折扣</span>
<el-radio-group v-model="discountRadio" @change="handleDiscountChange">
<el-radio-button
@@ -352,7 +352,9 @@ function printReceipt(param) {
},
],
};
const printElements = templateJson;
const printElements = JSON.parse(
JSON.stringify(templateJson).replace(/{{HOSPITAL_NAME}}/g, userStore.hospitalName)
);
var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板
hiprintTemplate.print2(result.data[0], {
@@ -463,7 +465,9 @@ async function print() {
};
console.log(result, '==result.data==');
const printElements = templateJson;
const printElements = JSON.parse(
JSON.stringify(templateJson).replace(/{{HOSPITAL_NAME}}/g, userStore.hospitalName)
);
var hiprintTemplate = new hiprint.PrintTemplate({ template: printElements }); // 定义模板
const printerList = hiprintTemplate.getPrinterList();
console.log(hiprintTemplate, '打印机列表');

View File

@@ -38,7 +38,7 @@
"top": 15,
"height": 16.5,
"width": 142.5,
"title": "长春市朝阳区中医院门诊收费结算单",
"title": "{{HOSPITAL_NAME}}门诊收费结算单",
"coordinateSync": false,
"widthHeightSync": false,
"fontWeight": "bold",

View File

@@ -2,7 +2,7 @@
<div class="container">
<el-form :model="state.form">
<div class="record-container">
<div class="title">长春市朝阳区中医院</div>
<div class="title">{{ userStore.hospitalName }}</div>
<div class="subtitle">入院记录</div>
<div class="header">
<span>姓名: [<el-input v-model="state.form.name" class="inline-input" />]</span>
@@ -98,6 +98,9 @@
<script setup>
import {getCurrentInstance, onBeforeMount, onMounted, reactive} from 'vue'
import useUserStore from '@/store/modules/user'
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
const emits = defineEmits([]);
const props = defineProps({})

View File

@@ -432,6 +432,7 @@ function sliceData(data) {
const types = data.types.filter((item) => item.weekNo === week.value);
// const datas = JSON.parse(JSON.stringify(data));
const datas = cloneDeep(data);
datas.grParamBOS.title = userStore.hospitalName;
datas.rows = rows;
datas.types = types;
console.log(datas, '666666666666666666');

View File

@@ -21,7 +21,7 @@
"top": 12,
"height": 12,
"width": 115.5,
"title": "长春市朝阳区中医院医院盘点单",
"title": "{{HOSPITAL_NAME}}医院盘点单",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
@@ -72,7 +72,7 @@
"top": 33,
"height": 9.75,
"width": 120,
"title": "机构:长春市朝阳区中医院医院",
"title": "机构:{{HOSPITAL_NAME}}医院",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0

View File

@@ -33,7 +33,7 @@
"top": 13.5,
"height": 12,
"width": 75,
"title": "长春市朝阳区中医院医院",
"title": "{{HOSPITAL_NAME}}医院",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 12,
@@ -84,7 +84,7 @@
"top": 33,
"height": 9.75,
"width": 120,
"title": "机构:长春市朝阳区中医院医院",
"title": "机构:{{HOSPITAL_NAME}}医院",
"coordinateSync": false,
"widthHeightSync": false,
"qrCodeLevel": 0

View File

@@ -26,7 +26,7 @@
placeholder=""
clearable
style="width: 150px">
<el-option label="长春市朝阳区中医院" value="sameHospital" />
<el-option :label="userStore.hospitalName" value="sameHospital" />
</el-select>
</el-form-item>
@@ -120,7 +120,9 @@
<script setup name="OutpatientDepartmentMetrics">
import {getOutpatientDepartmentMetrics} from './statisticalManagent';
import {computed, reactive, ref, toRefs} from 'vue';
import useUserStore from '@/store/modules/user';
const userStore = useUserStore();
const loading = ref(true);
const total = ref(0);
const data = reactive({

View File

@@ -33,8 +33,13 @@
<el-button type="primary" plain icon="Plus" @click="handleAdd">新增手术室</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete">
批量删除
<el-button type="success" plain icon="VideoPlay" :disabled="multiple" @click="handleEnable(ids)">
批量启用
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="VideoPause" :disabled="multiple" @click="handleDisable(ids)">
批量停用
</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
@@ -50,7 +55,7 @@
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" type="index" align="center" width="60" />
<el-table-column label="手术室编码" align="center" prop="busNo" width="120" show-overflow-tooltip />
<el-table-column label="房间号" align="center" prop="busNo" width="120" show-overflow-tooltip />
<el-table-column label="手术室名称" align="center" prop="name" min-width="150" show-overflow-tooltip />
<el-table-column label="类型" align="center" prop="roomTypeEnum_dictText" width="100" />
<el-table-column label="所属科室" align="center" prop="organizationName" width="120" show-overflow-tooltip />
@@ -65,16 +70,24 @@
</template>
</el-table-column>
<el-table-column label="显示顺序" align="center" prop="displayOrder" width="100" />
<el-table-column label="操作" align="center" width="180" fixed="right">
<el-table-column label="操作" align="center" width="220" fixed="right">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleEdit(scope.row)">编辑</el-button>
<el-button link type="primary" icon="View" @click="handleView(scope.row)">查看</el-button>
<el-button
v-if="scope.row.statusEnum === 1"
link
type="danger"
icon="Delete"
@click="handleDelete(scope.row)"
>删除</el-button>
type="warning"
icon="VideoPause"
@click="handleDisable([scope.row.id])"
>停用</el-button>
<el-button
v-else
link
type="success"
icon="VideoPlay"
@click="handleEnable([scope.row.id])"
>启用</el-button>
</template>
</el-table-column>
</el-table>
@@ -92,11 +105,19 @@
<el-dialog :title="title" v-model="open" width="700px" append-to-body>
<el-form ref="operatingRoomRef" :model="form" :rules="rules" label-width="120px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="房间号" prop="busNo">
<el-input v-model="form.busNo" placeholder="请输入房间号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手术室名称" prop="name">
<el-input v-model="form.name" placeholder="请输入手术室名称" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="容纳人数" prop="capacity">
<el-input-number
@@ -231,11 +252,12 @@
</template>
<script setup name="OperatingRoomManage">
import { listOperatingRoom, getOperatingRoom, addOperatingRoom, updateOperatingRoom, deleteOperatingRoom } from '@/api/operatingroom'
import { listOperatingRoom, getOperatingRoom, addOperatingRoom, updateOperatingRoom, deleteOperatingRoom, enableOperatingRoom, disableOperatingRoom } from '@/api/operatingroom'
import { deptTreeSelect } from '@/api/system/user'
import { computed } from 'vue'
const { proxy } = getCurrentInstance()
const { operating_room_type } = proxy.useDict('operating_room_type')
const loading = ref(true)
const showSearch = ref(true)
const operatingRoomList = ref([])
@@ -291,12 +313,7 @@ const form = ref({
const operatingRoomRef = ref()
const viewData = ref({})
const roomTypeOptions = ref([
{ value: 1, label: '急诊手术室' },
{ value: 2, label: '择期手术室' },
{ value: 3, label: '日间手术室' },
{ value: 4, label: '复合手术室' }
])
const roomTypeOptions = operating_room_type
const statusOptions = ref([
{ value: 1, label: '启用' },
@@ -304,6 +321,7 @@ const statusOptions = ref([
])
const rules = ref({
busNo: [{ required: true, message: '房间号不能为空', trigger: 'blur' }],
name: [{ required: true, message: '手术室名称不能为空', trigger: 'blur' }],
capacity: [
{ required: true, message: '容纳人数不能为空', trigger: 'blur' },
@@ -484,17 +502,30 @@ function submitForm() {
})
}
/** 删除按钮操作 */
function handleDelete(row) {
const ids = row.id || ids.value
/** 启用按钮操作 */
function handleEnable(ids) {
proxy.$modal
.confirm('是否确认删除选中的手术室?')
.confirm('是否确认启用选中的手术室?')
.then(function () {
return deleteOperatingRoom(ids)
return enableOperatingRoom(ids)
})
.then(() => {
getList()
proxy.$modal.msgSuccess('删除成功')
proxy.$modal.msgSuccess('启用成功')
})
.catch(() => {})
}
/** 停用按钮操作 */
function handleDisable(ids) {
proxy.$modal
.confirm('是否确认停用选中的手术室?')
.then(function () {
return disableOperatingRoom(ids)
})
.then(() => {
getList()
proxy.$modal.msgSuccess('停用成功')
})
.catch(() => {})
}

View File

@@ -33,7 +33,7 @@
"top": 22.5,
"height": 12,
"width": 88.5,
"title": "长春市朝阳区中医院医院",
"title": "{{HOSPITAL_NAME}}医院",
"coordinateSync": false,
"widthHeightSync": false,
"fontSize": 13.5,

View File

@@ -37,7 +37,7 @@
"top": 16.5,
"height": 22.5,
"width": 120,
"title": "长春市朝阳区中医院医院",
"title": "{{HOSPITAL_NAME}}医院",
"coordinateSync": false,
"widthHeightSync": false,
"fontFamily": "Microsoft YaHei",

View File

@@ -1,82 +1,4 @@
-- 为手术室管理表添加类型和所属科室字段PostgreSQL版本
-- 作者: 系统管理员
-- 日期: 2026-01-09
-- 说明: 添加手术室类型字段,区分不同类型的手术室(急诊、择期、日间、复合手术室)
-- 1. 检查并添加手术室类型字段到 adm_operating_room 表
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'adm_operating_room'
AND column_name = 'room_type_enum'
) THEN
ALTER TABLE adm_operating_room
ADD COLUMN room_type_enum INTEGER DEFAULT 2;
COMMENT ON COLUMN adm_operating_room.room_type_enum IS
'手术室类型1-急诊手术室2-择期手术室3-日间手术室4-复合手术室';
RAISE NOTICE '已添加字段 room_type_enum';
ELSE
RAISE NOTICE '字段 room_type_enum 已存在,跳过添加';
END IF;
END $$;
-- 2. 更新现有数据的默认值为择期手术室2
UPDATE adm_operating_room
SET room_type_enum = 2
WHERE room_type_enum IS NULL;
-- 3. 检查并添加索引以提高查询性能
DO $$
BEGIN
-- 检查并添加 room_type_enum 索引
IF NOT EXISTS (
SELECT 1
FROM pg_indexes
WHERE schemaname = 'public'
AND tablename = 'adm_operating_room'
AND indexname = 'idx_room_type'
) THEN
CREATE INDEX idx_room_type ON adm_operating_room(room_type_enum);
RAISE NOTICE '已创建索引 idx_room_type';
ELSE
RAISE NOTICE '索引 idx_room_type 已存在,跳过创建';
END IF;
-- 检查并添加 organization_id 索引
IF NOT EXISTS (
SELECT 1
FROM pg_indexes
WHERE schemaname = 'public'
AND tablename = 'adm_operating_room'
AND indexname = 'idx_org_id'
) THEN
CREATE INDEX idx_org_id ON adm_operating_room(organization_id);
RAISE NOTICE '已创建索引 idx_org_id';
ELSE
RAISE NOTICE '索引 idx_org_id 已存在,跳过创建';
END IF;
END $$;
-- 4. 检查并插入手术室类型字典数据
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM sys_dict_type
WHERE dict_type = 'operating_room_type'
) THEN
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
VALUES ('手术室类型', 'operating_room_type', '0', 'system', NOW(), '手术室类型字典');
RAISE NOTICE '已添加字典类型 operating_room_type';
ELSE
RAISE NOTICE '字典类型 operating_room_type 已存在,跳过添加';
END IF;
END $$;
-- 5. 检查并插入手术室类型字典项数据
DO $$
@@ -88,8 +10,8 @@ BEGIN
WHERE dict_type = 'operating_room_type'
AND dict_value = '1'
) THEN
INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, status, create_by, create_time, remark)
VALUES ('room_type_emergency', 1, '急诊手术室', '1', 'operating_room_type', '0', 'system', NOW(), '用于急诊手术的手术室');
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, status, create_by, create_time, remark)
VALUES (1, '急诊手术室', '1', 'operating_room_type', '0', 'system', NOW(), '用于急诊手术的手术室');
RAISE NOTICE '已添加字典项:急诊手术室';
ELSE
RAISE NOTICE '字典项:急诊手术室 已存在,跳过添加';
@@ -101,8 +23,8 @@ BEGIN
WHERE dict_type = 'operating_room_type'
AND dict_value = '2'
) THEN
INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, status, create_by, create_time, remark)
VALUES ('room_type_elective', 2, '择期手术室', '2', 'operating_room_type', '0', 'system', NOW(), '用于择期手术的手术室');
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, status, create_by, create_time, remark)
VALUES (2, '择期手术室', '2', 'operating_room_type', '0', 'system', NOW(), '用于择期手术的手术室');
RAISE NOTICE '已添加字典项:择期手术室';
ELSE
RAISE NOTICE '字典项:择期手术室 已存在,跳过添加';
@@ -114,8 +36,8 @@ BEGIN
WHERE dict_type = 'operating_room_type'
AND dict_value = '3'
) THEN
INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, status, create_by, create_time, remark)
VALUES ('room_type_day_surgery', 3, '日间手术室', '3', 'operating_room_type', '0', 'system', NOW(), '用于日间手术的手术室');
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, status, create_by, create_time, remark)
VALUES (3, '日间手术室', '3', 'operating_room_type', '0', 'system', NOW(), '用于日间手术的手术室');
RAISE NOTICE '已添加字典项:日间手术室';
ELSE
RAISE NOTICE '字典项:日间手术室 已存在,跳过添加';
@@ -127,28 +49,14 @@ BEGIN
WHERE dict_type = 'operating_room_type'
AND dict_value = '4'
) THEN
INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, status, create_by, create_time, remark)
VALUES ('room_type_hybrid', 4, '复合手术室', '4', 'operating_room_type', '0', 'system', NOW(), '用于复合手术的手术室');
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, status, create_by, create_time, remark)
VALUES (4, '复合手术室', '4', 'operating_room_type', '0', 'system', NOW(), '用于复合手术的手术室');
RAISE NOTICE '已添加字典项:复合手术室';
ELSE
RAISE NOTICE '字典项:复合手术室 已存在,跳过添加';
END IF;
END $$;
-- 6. 验证修改 - 查看新增的字段
SELECT
table_name,
column_name,
data_type,
is_nullable,
column_default,
col_description(pgc_catalog.oid, pg_namespace.oid, pg_class.oid, pg_attribute.attnum) AS column_comment
FROM
information_schema.columns
WHERE
table_schema = 'public'
AND table_name = 'adm_operating_room'
AND column_name = 'room_type_enum';
-- 7. 显示所有手术室及其类型
SELECT