Merge branch 'develop' of http://192.168.110.253:3000/wangyizhe/his into develop

This commit is contained in:
2026-04-24 17:05:23 +08:00
20 changed files with 1961 additions and 54 deletions

View File

@@ -0,0 +1,134 @@
# CI/CD构建门禁规范
## 🎯 规范目标
建立自动化质量门禁,确保每次代码提交都经过严格验证,防止低质量代码进入主干分支,提升系统稳定性和开发效率。
## 🔒 门禁层级
### 1. 提交前门禁Pre-commit
**触发时机**`git commit` 执行前
**验证内容**
- ESLint 代码规范检查
- Prettier 代码格式化
- 简单的单元测试(快速执行)
**工具配置**
- Husky + lint-staged
- 配置文件:`.husky/pre-commit`
### 2. 推送前门禁Pre-push
**触发时机**`git push` 执行前
**验证内容**
- 完整的单元测试套件
- 构建验证(`npm run build`
- 集成测试(核心流程)
**工具配置**
- Husky pre-push hook
- 配置文件:`.husky/pre-push`
### 3. CI流水线门禁CI Pipeline
**触发时机**:代码推送到远程仓库后
**验证内容**
- 完整的测试套件(单元+集成+端到端)
- 代码覆盖率检查≥80%
- 安全扫描SAST
- 构建产物验证
- 部署到测试环境
**工具配置**
- Spug CI/CD 流水线
- Gitea Webhook 触发
### 4. 发布前门禁Release Gate
**触发时机**:准备发布到生产环境前
**验证内容**
- 生产环境冒烟测试
- 性能基准测试
- 安全合规检查
- 回滚预案验证
## ⚙️ 具体配置要求
### ESLint 配置
```javascript
// .eslintrc.js 关键配置
module.exports = {
plugins: ['import'],
rules: {
// 确保导入的模块实际存在
'import/no-unresolved': 'error',
// 确保导入的成员实际存在
'import/named': 'error',
// 禁止未使用的导入
'import/no-unused-modules': 'warn'
}
};
```
### Husky 配置
```bash
# .husky/pre-commit
#!/bin/sh
npm run lint-staged
# .husky/pre-push
#!/bin/sh
npm run test:unit && npm run build
```
### lint-staged 配置
```json
// package.json
{
"lint-staged": {
"*.{js,vue}": ["eslint --fix", "prettier --write"],
"*.{css,scss}": ["stylelint --fix", "prettier --write"]
}
}
```
## 🚫 失败处理机制
### 自动处理
- **构建失败**:自动阻止 PR 合并
- **测试失败**:标记 PR 为失败状态
- **安全漏洞**:立即通知安全团队
### 人工处理
- **紧急修复**:可申请临时绕过(需架构师批准)
- **误报处理**:提交豁免申请并说明原因
- **规则调整**:通过 RFC 流程申请规则变更
## 📊 监控与度量
### 关键指标
- 门禁通过率 ≥ 95%
- 平均修复时间 ≤ 2小时
- 误报率 ≤ 5%
### 报告机制
- 每日门禁失败统计
- 周度质量趋势报告
- 月度规则优化建议
## 🔄 持续改进
### 规则演进
- 每月评审门禁规则有效性
- 根据项目需求调整检查强度
- 引入新的质量检查工具
### 团队培训
- 新成员入职培训包含门禁规范
- 定期分享最佳实践案例
- 建立常见问题解决方案库
---
**文档版本**v1.0
**最后更新**2026年4月24日
**负责人**:陈琳(文档专家)
**技术方案**:诸葛亮(架构师)
**适用范围**HIS 系统所有项目

View File

@@ -0,0 +1,129 @@
# 代码提交变更说明模板
## 📝 PR/Commit 模板
### 标题格式
```
<类型>(<模块>): <简短描述>
示例:
feat(patient): 添加患者基本信息编辑功能
fix(doctor): 修复医生排班显示异常问题
docs(api): 更新预约挂号接口文档
refactor(nurse): 重构护士站护理记录组件
```
### 正文模板
```markdown
## 🔍 变更背景
- **问题描述**:详细说明要解决的问题或实现的需求
- **影响范围**:列出受影响的模块、页面、功能
- **相关链接**禅道任务ID、需求文档链接等
## 🛠️ 变更内容
- **主要修改**:核心代码变更点
- **技术方案**:采用的技术方案和设计思路
- **兼容性**是否涉及API或数据结构变更
## ✅ 验证情况
- **测试覆盖**:单元测试、集成测试覆盖情况
- **手动验证**:手动测试的场景和结果
- **构建验证**:本地构建截图(必填)
## 📋 检查清单
- [ ] 代码已通过 ESLint 检查
- [ ] 本地构建成功(附截图)
- [ ] 核心功能已测试验证
- [ ] 文档已同步更新
- [ ] Code Review 已完成
## 👥 相关人员
- **开发者**@开发者姓名
- **测试者**@测试者姓名
- **审核人**@架构师姓名
```
## 🏷️ 提交类型说明
| 类型 | 说明 | 示例 |
|------|------|------|
| feat | 新功能 | `feat: 添加用户登录功能` |
| fix | Bug修复 | `fix: 修复表单验证错误` |
| docs | 文档更新 | `docs: 更新API文档` |
| style | 代码格式调整 | `style: 格式化代码` |
| refactor | 代码重构 | `refactor: 重构组件结构` |
| test | 测试相关 | `test: 添加单元测试` |
| chore | 构建/依赖等 | `chore: 升级依赖版本` |
| perf | 性能优化 | `perf: 优化列表加载速度` |
## 📁 模块命名规范
| 模块 | 说明 |
|------|------|
| patient | 患者管理相关 |
| doctor | 医生工作站相关 |
| nurse | 护士站相关 |
| admin | 后台管理相关 |
| common | 公共组件/工具 |
| api | API接口相关 |
| auth | 认证授权相关 |
| payment | 支付相关 |
## 🖼️ 构建验证截图要求
### 必须包含的信息
1. **终端窗口**:显示 `npm run build` 命令执行过程
2. **成功标识**:明确显示构建成功的提示信息
3. **时间戳**:截图包含当前时间,证明是最新构建
4. **分支信息**:显示当前工作分支名称
### 截图示例
```
$ git checkout feature/patient-edit
$ npm run build
> his-system@1.0.0 build
> vue-cli-service build
⠇ Building for production...
DONE Build complete. The dist directory is ready to be deployed.
INFO Check out deployment instructions at https://cli.vuejs.org/guide/deployment.html
✨ Done in 45.23s.
```
## ⚠️ 禁止行为
### 严重违规(直接拒绝合并)
- 无构建验证截图
- 代码存在 ESLint 错误
- 未填写变更说明
- 修改无关代码文件
### 轻微违规(要求修正后重新提交)
- 描述过于简单
- 测试覆盖不完整
- 文档更新滞后
- 格式不符合规范
## 💡 最佳实践
### 高质量提交特征
- **原子性**:每次提交只解决一个问题
- **可追溯**关联具体的需求或Bug ID
- **可验证**:提供完整的验证证据
- **可理解**:描述清晰,他人能快速理解
### 团队协作建议
- 提交前先在本地完整测试
- 复杂变更提前与团队沟通
- 及时更新相关文档
- 主动帮助新人熟悉规范
---
**文档版本**v1.0
**最后更新**2026年4月24日
**负责人**:陈琳(文档专家)
**适用范围**HIS 系统所有开发人员

View File

@@ -0,0 +1,82 @@
# 前端发布前检查清单
## 📋 基础检查项
### 代码质量
- [ ] 代码已通过 ESLint 检查,无警告和错误
- [ ] 代码已通过 Prettier 格式化
- [ ] 无 console.log() 等调试代码残留
- [ ] 变量命名符合规范,语义清晰
- [ ] 函数职责单一,复杂度适中
### 构建验证
- [ ] 本地执行 `npm run build` 成功完成
- [ ] 构建产物无报错,体积合理
- [ ] 静态资源路径正确无404错误
- [ ] 环境变量配置正确(开发/测试/生产)
### 功能验证
- [ ] 核心功能流程完整测试通过
- [ ] 边界条件和异常场景已覆盖
- [ ] 表单验证逻辑正确
- [ ] API 接口调用正常,错误处理完善
- [ ] 路由跳转逻辑正确
## 🔧 技术检查项
### 模块导入检查
- [ ] 所有 import 语句引用的模块实际存在
- [ ] 无未使用的 import 导入
- [ ] 路径别名(@/)配置正确
- [ ] 第三方库版本兼容性确认
### 性能优化
- [ ] 组件按需加载(懒加载)已配置
- [ ] 大数据列表已实现虚拟滚动或分页
- [ ] 图片资源已压缩,格式合适
- [ ] 无内存泄漏风险(事件监听器、定时器等)
### 安全检查
- [ ] 用户输入已做 XSS 防护
- [ ] 敏感信息不在前端硬编码
- [ ] API 请求已做 CSRF 防护
- [ ] 权限控制逻辑正确
## 🌐 兼容性检查
### 浏览器兼容
- [ ] 主流浏览器Chrome、Firefox、Safari、Edge显示正常
- [ ] 移动端适配良好(如适用)
- [ ] 分辨率适配1366x768、1920x1080等
### 设备兼容
- [ ] 触摸设备操作体验良好
- [ ] 键盘导航支持完整
- [ ] 屏幕阅读器兼容性(无障碍)
## 📱 发布准备
### 文档更新
- [ ] 相关 API 文档已同步更新
- [ ] 用户操作手册已更新(如适用)
- [ ] 变更日志已记录
### 回滚预案
- [ ] 回滚方案已准备
- [ ] 数据兼容性已确认
- [ ] 紧急联系人已明确
## ✅ 最终确认
### 发布前最后检查
- [ ] 本地构建截图已附在 PR 中
- [ ] 测试环境部署验证通过
- [ ] Code Review 已完成并获得批准
- [ ] 相关 Bug 已关闭或延期说明
---
**文档版本**v1.0
**最后更新**2026年4月24日
**负责人**:陈琳(文档专家)
**适用范围**HIS 系统所有前端项目

View File

@@ -165,4 +165,9 @@ public class CurrentDayEncounterDto {
@JsonSerialize(using = ToStringSerializer.class)
private Long poolId;
/**
* 诊室名称Bug #410分诊队列需显示诊室而非科室
*/
private String clinicRoom;
}

View File

@@ -136,9 +136,11 @@ public class SurgicalScheduleAppServiceImpl implements ISurgicalScheduleAppServi
}
}
LoginUser loginUser = new LoginUser();
//获取当前登录用户信息
loginUser = SecurityUtils.getLoginUser();
// Bug #432 修复获取当前登录用户信息增加null校验防止NPE
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null) {
return R.fail("用户未登录或登录已过期");
}
// 当前登录用户ID
Long userId = loginUser.getUserId();

View File

@@ -1395,9 +1395,17 @@ public class ConsultationAppServiceImpl implements IConsultationAppService {
}
// 4. 更新邀请记录(存储会诊意见)
// 直接存储用户输入的原始意见内容,不添加医师姓名前缀
invited.setInvitedStatus(ConsultationStatusEnum.CONFIRMED.getCode()); // 已确认
invited.setConfirmOpinion(dto.getConsultationOpinion()); // 直接存储原始意见,不添加前缀
// Bug #388格式化存储确保回显时参加医师和意见完整
invited.setInvitedStatus(ConsultationStatusEnum.CONFIRMED.getCode());
String deptName = StringUtils.hasText(dto.getConfirmingDeptName())
? dto.getConfirmingDeptName() : invited.getInvitedDepartmentName();
String physician = StringUtils.hasText(dto.getConfirmingPhysician())
? dto.getConfirmingPhysician() : currentPhysicianName;
// 格式:科室-参加医师:意见内容
String formattedOpinion = String.format("%s-%s%s", deptName, physician, dto.getConsultationOpinion());
invited.setConfirmOpinion(formattedOpinion);
invited.setConfirmTime(new Date());
consultationInvitedMapper.updateById(invited);

View File

@@ -1144,7 +1144,12 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
chargeItem.setQuantityValue(adviceSaveDto.getQuantity()); // 数量
chargeItem.setQuantityUnit(adviceSaveDto.getUnitCode()); // 单位
chargeItem.setUnitPrice(adviceSaveDto.getUnitPrice()); // 单价
// #415 价格非负验证
BigDecimal unitPrice = adviceSaveDto.getUnitPrice();
if (unitPrice != null && unitPrice.compareTo(BigDecimal.ZERO) < 0) {
unitPrice = unitPrice.abs(); // 负数取绝对值
}
chargeItem.setUnitPrice(unitPrice); // 单价
chargeItem.setTotalPrice(adviceSaveDto.getTotalPrice()); // 总价
// 显式设置tenantId、createBy和createTime字段防止自动填充机制失效
@@ -1616,7 +1621,12 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
chargeItem.setQuantityValue(adviceSaveDto.getQuantity()); // 数量
chargeItem.setQuantityUnit(adviceSaveDto.getUnitCode()); // 单位
chargeItem.setUnitPrice(adviceSaveDto.getUnitPrice()); // 单价
// #415 价格非负验证
BigDecimal unitPrice = adviceSaveDto.getUnitPrice();
if (unitPrice != null && unitPrice.compareTo(BigDecimal.ZERO) < 0) {
unitPrice = unitPrice.abs(); // 负数取绝对值
}
chargeItem.setUnitPrice(unitPrice); // 单价
chargeItem.setTotalPrice(adviceSaveDto.getTotalPrice()); // 总价
// 显式设置审计字段,防止自动填充机制失效
@@ -1842,7 +1852,12 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
chargeItem.setEncounterDiagnosisId(adviceSaveDto.getEncounterDiagnosisId()); // 就诊诊断id
chargeItem.setQuantityValue(adviceSaveDto.getQuantity()); // 数量
chargeItem.setQuantityUnit(adviceSaveDto.getUnitCode()); // 单位
chargeItem.setUnitPrice(adviceSaveDto.getUnitPrice()); // 单价
// #415 价格非负验证
BigDecimal unitPrice = adviceSaveDto.getUnitPrice();
if (unitPrice != null && unitPrice.compareTo(BigDecimal.ZERO) < 0) {
unitPrice = unitPrice.abs(); // 负数取绝对值
}
chargeItem.setUnitPrice(unitPrice); // 单价
chargeItem.setTotalPrice(adviceSaveDto.getTotalPrice()); // 总价
iChargeItemService.saveOrUpdate(chargeItem);

View File

@@ -59,7 +59,9 @@ import com.openhis.web.personalization.dto.ActivityDeviceDto;
import com.openhis.triageandqueuemanage.domain.TriageQueueItem;
import com.openhis.triageandqueuemanage.service.TriageQueueItemService;
import com.openhis.appointmentmanage.domain.ScheduleSlot;
import com.openhis.appointmentmanage.domain.SchedulePool;
import com.openhis.appointmentmanage.mapper.ScheduleSlotMapper;
import com.openhis.appointmentmanage.mapper.SchedulePoolMapper;
import com.openhis.clinical.domain.Order;
import com.openhis.clinical.service.IOrderService;
import com.openhis.workflow.domain.ServiceRequest;
@@ -196,6 +198,8 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
private IOrderService iOrderService;
@Autowired
private ScheduleSlotMapper scheduleSlotMapper;
@Autowired
private SchedulePoolMapper schedulePoolMapper;
/**
* 【门诊预结算】
@@ -1982,6 +1986,60 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
// Bug #409预约签到挂号时修正 serviceTypeId 为预约类型而非挂号类型
// 数据链Order → ScheduleSlot → SchedulePool → HealthcareService
try {
Order appointmentOrder = iOrderService.getOne(
new LambdaQueryWrapper<Order>()
.eq(Order::getPatientId, encounterFormData.getPatientId())
.eq(Order::getStatus, CommonConstants.AppointmentOrderStatus.CHECKED_IN)
.eq(Order::getDeleteFlag, "0")
.orderByDesc(Order::getCreateTime)
.last("LIMIT 1")
);
if (appointmentOrder != null && appointmentOrder.getSlotId() != null) {
ScheduleSlot slot = scheduleSlotMapper.selectById(appointmentOrder.getSlotId());
if (slot != null && slot.getPoolId() != null) {
SchedulePool pool = schedulePoolMapper.selectById(slot.getPoolId());
if (pool != null && pool.getRegType() != null) {
// pool.getRegType() 存储号别名称如"普通号-预约",精确匹配 HealthcareService
HealthcareService appointmentHealthcareService = healthcareServiceService.getOne(
new LambdaQueryWrapper<HealthcareService>()
.eq(HealthcareService::getOfferedOrgId, encounterFormData.getOrganizationId())
.eq(HealthcareService::getDeleteFlag, "0")
.eq(HealthcareService::getName, pool.getRegType())
.last("LIMIT 1")
);
if (appointmentHealthcareService != null) {
encounterFormData.setServiceTypeId(appointmentHealthcareService.getId());
encounterFormData.setOrderId(appointmentOrder.getId());
logger.info("预约签到挂号修正serviceTypeId={},号别={}",
appointmentHealthcareService.getId(), pool.getRegType());
} else {
// 精确匹配失败,尝试 LIKE 匹配
appointmentHealthcareService = healthcareServiceService.getOne(
new LambdaQueryWrapper<HealthcareService>()
.eq(HealthcareService::getOfferedOrgId, encounterFormData.getOrganizationId())
.eq(HealthcareService::getDeleteFlag, "0")
.like(HealthcareService::getName, pool.getRegType())
.last("LIMIT 1")
);
if (appointmentHealthcareService != null) {
encounterFormData.setServiceTypeId(appointmentHealthcareService.getId());
encounterFormData.setOrderId(appointmentOrder.getId());
logger.info("预约签到挂号(LIKE)serviceTypeId={},号别={}",
appointmentHealthcareService.getId(), pool.getRegType());
}
}
}
}
}
} catch (Exception e) {
logger.warn("修正serviceTypeId失败不影响挂号流程", e);
}
// 保存就诊信息
Encounter encounter = new Encounter();
BeanUtils.copyProperties(encounterFormData, encounter);

View File

@@ -50,6 +50,7 @@
T9.organization_id AS organizationId,
T9.organization_name AS organizationName,
T9.healthcare_name AS healthcareName,
T9.clinic_room AS clinicRoom, -- Bug #410诊室名称
T9.practitioner_user_id AS practitionerUserId,
T9.practitioner_name AS practitionerName,
T9.contract_name AS contractName,
@@ -99,10 +100,12 @@
T18.identifier_no AS identifier_no,
T1.order_id AS order_id,
om.slot_id AS slot_id,
ss.pool_id AS pool_id
ss.pool_id AS pool_id,
sp.clinic_room AS clinic_room -- Bug #410从号源池获取诊室
FROM adm_encounter AS T1
LEFT JOIN order_main AS om ON T1.order_id = om.id AND om.delete_flag = '0'
LEFT JOIN adm_schedule_slot AS ss ON om.slot_id = ss.id AND ss.delete_flag = '0'
LEFT JOIN adm_schedule_pool AS sp ON ss.pool_id = sp.id AND sp.delete_flag = '0' -- Bug #410
LEFT JOIN adm_organization AS T2 ON T1.organization_id = T2.ID AND T2.delete_flag = '0'
LEFT JOIN adm_healthcare_service AS T3 ON T1.service_type_id = T3.ID AND T3.delete_flag = '0'
LEFT JOIN (

View File

@@ -20,6 +20,15 @@ export function advicePrint(data) {
}
// 获取全部科室列表
// 获取科室列表(树形结构)
export function getDepartmentList(data) {
return request({
url: '/app-common/department-list',
method: 'get',
params: data,
});
}
export function getOrgList(data) {
return request({
url: '/app-common/department-list',

View File

@@ -177,7 +177,7 @@
<el-tag v-else type="warning" size="small">已确认</el-tag>
</div>
<div class="opinion-content">
{{ opinion.opinion }}
{{ formatOpinionContent(opinion.opinion) }}
</div>
<div v-if="opinion.signatureTime" class="opinion-footer">
签名时间:{{ formatDateTime(opinion.signatureTime) }}
@@ -301,6 +301,16 @@ const formatDateTime = (date) => {
return `${yyyy}-${mm}-${dd} ${hh}:${mi}:${ss}`
}
// Bug #388去掉"科室-参加医师"前缀,只显示意见内容
const formatOpinionContent = (opinion) => {
if (!opinion) return ''
const colonIndex = opinion.indexOf('')
if (colonIndex > 0) {
return opinion.substring(colonIndex + 1)
}
return opinion // 向后兼容旧数据
}
const getDoctorName = () => userStore.nickName || userStore.name || '当前医生'
const getDoctorDept = () => userStore.orgName || '当前科室'

View File

@@ -306,6 +306,7 @@
v-for="cat in filteredCategoryList"
:key="cat.typeId"
:name="cat.typeId"
@click="handleCategoryExpand(cat)"
>
<template #title>
<span class="cat-title">{{ cat.categoryName }}</span>
@@ -596,6 +597,33 @@ const availableMethods = computed(() => {
});
// 当可选方法列表改变时,如果当前选中的方法不在新列表中,则清空
// #428: 分类展开时联动加载检查方法
async function handleCategoryExpand(cat) {
if (!cat || !cat.typeName) return;
try {
const res = await searchCheckMethod({ checkType: cat.typeName });
let data = res?.data?.data || res?.data || res?.rows || res;
if (!Array.isArray(data) && res?.data && Array.isArray(res.data.data)) {
data = res.data.data;
}
if (Array.isArray(data) && data.length > 0) {
cat.methods = data.map(m => ({
id: m.id,
name: m.name,
code: m.code,
price: m.price || 0,
packageName: m.packageName || '',
packagePrice: m.packagePrice || null,
serviceFee: m.serviceFee || null
}));
}
} catch (err) {
console.error('加载分类检查方法失败', err);
}
}
watch(availableMethods, (newMethods) => {
if (form.inspectionMethod && !newMethods.find(m => m.name === form.inspectionMethod)) {
form.inspectionMethod = '';
@@ -832,11 +860,6 @@ function handleSave() {
const firstCheckType = selectedItems.value[0]?.checkType || 'unknown';
form.examTypeCode = firstCheckType;
// 如果有选中的检查方法,更新表单中的检查方法字段(取第一个选中项目的检查方法)
const firstItemWithMethod = selectedItems.value.find(item => item.selectedMethod);
if (firstItemWithMethod?.selectedMethod) {
form.inspectionMethod = firstItemWithMethod.selectedMethod.name;
}
const payload = {
...form,
@@ -1050,6 +1073,11 @@ function selectMethodCheckbox(checked, item, method) {
}
// 联动更新表单检查方法显示字段
updateMethodDisplay();
// #430: 套餐金额实时同步到申请单
nextTick(() => {
form.totalAmount = totalAmountCalc.value;
});
}
// Bug #384修复: 更新检查方法显示字段(联动)

View File

@@ -80,11 +80,23 @@
<script setup name="BloodTransfusion">
import {getCurrentInstance, onBeforeMount, onMounted, reactive, ref} from 'vue';
import {patientInfo} from '../../../store/patient.js';
import {getOrgList} from '../../../../../basicmanage/ward/components/api.js';
import {getDepartmentList} from '@/api/public.js';
import {getEncounterDiagnosis} from '../../api.js';
import {getApplicationList, saveBloodTransfusio} from './api';
const { proxy } = getCurrentInstance();
// 递归查找树形科室节点
const findTreeItem = (list, id) => {
if (!list || list.length === 0) return null;
for (const item of list) {
if (item.id == id) return item;
if (item.children && item.children.length > 0) {
const found = findTreeItem(item.children, id);
if (found) return found;
}
}
return null;
};
const emits = defineEmits(['submitOk']);
const props = defineProps({});
const state = reactive({});
@@ -175,9 +187,7 @@ const projectWithDepartment = (selectProjectIds, type) => {
isRelease = false;
}
// 选中项目中的执行科室id与全部科室数据做匹配
const findItem = orgOptions.value.find((item) => {
return item.id == obj.orgId;
});
const findItem = findTreeItem(orgOptions.value, obj.orgId);
if (!findItem) {
isRelease = false;
@@ -249,8 +259,8 @@ const submit = () => {
};
/** 查询科室 */
const getLocationInfo = () => {
getOrgList().then((res) => {
orgOptions.value = res.data?.records[0]?.children;
getDepartmentList().then((res) => {
orgOptions.value = res.data || [];
});
};
// 获取诊断目录

View File

@@ -81,11 +81,23 @@
import {getCurrentInstance, onBeforeMount, onMounted, reactive, watch} from 'vue';
import {patientInfo} from '../../../store/patient.js';
import {getApplicationList, saveInspection} from './api';
import {getOrgList} from '../../../../../basicmanage/ward/components/api.js';
import {getDepartmentList} from '@/api/public.js';
import {getEncounterDiagnosis} from '../../api.js';
import {ElMessage} from 'element-plus';
const { proxy } = getCurrentInstance();
// 递归查找树形科室节点
const findTreeItem = (list, id) => {
if (!list || list.length === 0) return null;
for (const item of list) {
if (item.id == id) return item;
if (item.children && item.children.length > 0) {
const found = findTreeItem(item.children, id);
if (found) return found;
}
}
return null;
};
const emits = defineEmits(['submitOk']);
const props = defineProps({});
const state = reactive({});
@@ -100,7 +112,7 @@ const getList = () => {
}
loading.value = true;
getApplicationList({
pageSize: 10000,
pageSize: 500,
pageNum: 1,
categoryCode: '22',
organizationId: patientInfo.value.inHospitalOrgId,
@@ -177,9 +189,7 @@ const projectWithDepartment = (selectProjectIds, type) => {
isRelease = false;
}
// 选中项目中的执行科室id与全部科室数据做匹配
const findItem = orgOptions.value.find((item) => {
return item.id == obj.orgId;
});
const findItem = findTreeItem(orgOptions.value, obj.orgId);
if (!findItem) {
isRelease = false;
ElMessage({
@@ -251,8 +261,8 @@ const submit = () => {
};
/** 查询科室 */
const getLocationInfo = () => {
getOrgList().then((res) => {
orgOptions.value = res.data?.records[0]?.children;
getDepartmentList().then((res) => {
orgOptions.value = res.data || [];
console.log('科室========>', JSON.stringify(orgOptions.value));
});
};

View File

@@ -80,12 +80,24 @@
<script setup name="MedicalExaminations">
import {getCurrentInstance, onBeforeMount, onMounted, reactive, watch} from 'vue';
import {patientInfo} from '../../../store/patient.js';
import {getOrgList} from '../../../../../basicmanage/ward/components/api.js';
import {getDepartmentList} from '@/api/public.js';
import {getEncounterDiagnosis} from '../../api.js';
import {getApplicationList, saveCheckd} from './api';
import {ElMessage} from 'element-plus';
const { proxy } = getCurrentInstance();
// 递归查找树形科室节点
const findTreeItem = (list, id) => {
if (!list || list.length === 0) return null;
for (const item of list) {
if (item.id == id) return item;
if (item.children && item.children.length > 0) {
const found = findTreeItem(item.children, id);
if (found) return found;
}
}
return null;
};
const emits = defineEmits(['submitOk']);
const props = defineProps({});
const orgOptions = ref([]); // 科室选项
@@ -100,7 +112,7 @@ const getList = () => {
}
loading.value = true;
getApplicationList({
pageSize: 10000,
pageSize: 500,
pageNum: 1,
categoryCode: '23',
organizationId: patientInfo.value.inHospitalOrgId,
@@ -176,9 +188,7 @@ const projectWithDepartment = (selectProjectIds, type) => {
isRelease = false;
}
// 选中项目中的执行科室id与全部科室数据做匹配
const findItem = orgOptions.value.find((item) => {
return item.id == obj.orgId;
});
const findItem = findTreeItem(orgOptions.value, obj.orgId);
if (!findItem) {
isRelease = false;
@@ -250,8 +260,8 @@ const submit = () => {
};
/** 查询科室 */
const getLocationInfo = () => {
getOrgList().then((res) => {
orgOptions.value = res.data?.records[0]?.children;
getDepartmentList().then((res) => {
orgOptions.value = res.data || [];
});
};
// 获取诊断目录

View File

@@ -80,12 +80,24 @@
<script setup name="Surgery">
import {getCurrentInstance, onBeforeMount, onMounted, reactive} from 'vue';
import {patientInfo} from '../../../store/patient.js';
import {getOrgList} from '../../../../../basicmanage/ward/components/api.js';
import {getDepartmentList} from '@/api/public.js';
import {getEncounterDiagnosis} from '../../api.js';
import {getApplicationList, saveSurgery} from './api';
import {ElMessage} from 'element-plus';
const { proxy } = getCurrentInstance();
// 递归查找树形科室节点
const findTreeItem = (list, id) => {
if (!list || list.length === 0) return null;
for (const item of list) {
if (item.id == id) return item;
if (item.children && item.children.length > 0) {
const found = findTreeItem(item.children, id);
if (found) return found;
}
}
return null;
};
const emits = defineEmits(['submitOk']);
const props = defineProps({});
const state = reactive({});
@@ -176,9 +188,7 @@ const projectWithDepartment = (selectProjectIds, type) => {
isRelease = false;
}
// 选中项目中的执行科室id与全部科室数据做匹配
const findItem = orgOptions.value.find((item) => {
return item.id == obj.orgId;
});
const findItem = findTreeItem(orgOptions.value, obj.orgId);
if (!findItem) {
isRelease = false;
@@ -251,8 +261,8 @@ const submit = () => {
};
/** 查询科室 */
const getLocationInfo = () => {
getOrgList().then((res) => {
orgOptions.value = res.data?.records[0]?.children;
getDepartmentList().then((res) => {
orgOptions.value = res.data || [];
});
};
// 获取诊断目录

View File

@@ -988,10 +988,13 @@ function selectRow(rowValue, index) {
form.purchaseinventoryList[index].unitList = rowValue.unitList[0];
form.purchaseinventoryList[index].lotNumber = rowValue.lotNumber;
form.purchaseinventoryList[index].ybNo = rowValue.ybNo;
// #439 fix: 不清空sourceLocationId保留handleAddRow设置的仓库ID
if (!form.purchaseinventoryList[index].sourceLocationId) {
form.purchaseinventoryList[index].sourceLocationId = '';
}
getPharmacyCabinetList().then((res) => {
purposeTypeListOptions.value = res.data;
// handleLocationClick(1, row, index)
handleLocationClick(1, rowValue, index)
});
form.purchaseinventoryList[index].itemQuantity = 0;
form.purchaseinventoryList[index].totalPrice = 0;

View File

@@ -710,8 +710,10 @@ const loadQueueFromDb = async () => {
id: it.id,
queueOrder: it.queueOrder,
patientName: it.patientName ?? '-',
appointmentType: it.healthcareName ?? '普通',
room: it.organizationName ?? '-',
// 队列数据已从入队时存储的 healthcareName 读取(包含"预约"或"挂号"后缀)
appointmentType: it.healthcareName ?? '普通号挂号',
// Bug #410使用 roomNo诊室而非 organizationName科室
room: it.roomNo ?? it.organizationName ?? '-',
doctor: it.practitionerName ?? '-',
waitingTime: waitingTime,
createTime: it.createTime, // 保存创建时间,用于定时器计算
@@ -820,15 +822,18 @@ const loadDataFromApi = async () => {
organizationName: item.organizationName,
patientName: item.patientName ?? '-',
age: parseAge(item.age),
appointmentType: item.healthcareName ?? '普通',
room: item.organizationName ? `${item.organizationName}` : '-',
// Bug #409/410根据 isFromAppointment 区分预约/挂号,使用 clinicRoom 诊室
appointmentType: (item.healthcareName || '').replace('挂号', '') + (item.isFromAppointment ? '预约' : '挂号'),
room: item.clinicRoom ?? item.organizationName ?? '-',
doctor: item.practitionerName ?? '-',
// 当前接口返回的是 practitionerUserId保存为 practitionerId 供入队使用
practitionerId: item.practitionerUserId ?? null,
matchingRule: '-', // 这里先不做智能规则匹配
// 号源池和槽位信息(用于分诊队列)
poolId: item.poolId ?? null,
slotId: item.slotId ?? null
slotId: item.slotId ?? null,
// 保存原始 isFromAppointment 用于调试
isFromAppointment: item.isFromAppointment ?? false
}))
console.log('【心内科】候选池已加载', originalCandidatePoolList.value.length, '条今天的数据')
} else {
@@ -1046,7 +1051,8 @@ const handleAddToQueue = async () => {
healthcareName: c.appointmentType,
practitionerName: c.doctor,
practitionerId: c.practitionerId ?? null,
roomNo: c.roomNo ?? c.room ?? null,
// Bug #410c.room 已是诊室名称
roomNo: c.room ?? null,
poolId: c.poolId ?? null,
slotId: c.slotId ?? null
})
@@ -1163,7 +1169,8 @@ const handleAddAllToQueue = async () => {
healthcareName: c.appointmentType,
practitionerName: c.doctor,
practitionerId: c.practitionerId ?? null,
roomNo: c.roomNo ?? c.room ?? null,
// Bug #410c.room 已是诊室名称
roomNo: c.room ?? null,
poolId: c.poolId ?? null,
slotId: c.slotId ?? null
})