Compare commits
4 Commits
bugfix-403
...
764befbf28
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
764befbf28 | ||
|
|
e4315cc54a | ||
|
|
32580b1a34 | ||
| 07c92e63a2 |
@@ -1,66 +0,0 @@
|
||||
# Bug #403 分析报告
|
||||
|
||||
## 根因分析
|
||||
|
||||
**Bug现象**:住院医生工作站应用医嘱组套后,药品明细字段(单次剂量、总量、总金额、药房/科室)丢失。
|
||||
|
||||
**数据流追踪**:
|
||||
|
||||
1. **后端 `getGroupPackageForOrder`** (OrdersGroupPackageAppServiceImpl.java:168)
|
||||
- 查询组套明细 SQL(OrdersGroupPackageAppMapper.xml:37-82)返回:`dose`, `quantity`, `doseQuantity`, `rateCode`, `methodCode`, `dispensePerDuration` 等字段
|
||||
- 通过 `getAdviceBaseInfo` 获取 `AdviceBaseDto` 赋值给 `detail.setOrderDetailInfos()`,包含:`doseUnitCode`, `doseUnitCode_dictText`, `positionId`, `inventoryList`, `priceList`, `partPercent` 等
|
||||
|
||||
2. **前端 `orderGroupDrawer.vue`** `handleUseOrderGroup` (line 568-694)
|
||||
- 对每个组套明细项进行预处理,合并组套字段和医嘱库字段
|
||||
- 通过 `emit('useOrderGroup', processedDetailList)` 发送到父组件
|
||||
|
||||
3. **前端 `inpatientDoctor/home/components/order/index.vue`** `handleSaveGroup` (line 1546-1639)
|
||||
- 接收 `orderGroupList`,对每个 item 调用 `setValue(mergedDetail)` 填充行数据
|
||||
- 然后用 `item` 的字段显式覆盖创建 `newRow`
|
||||
|
||||
**根因定位**:`handleSaveGroup` 在构建 `newRow` 时(line 1594-1617),从 `item` 直接取值覆盖了 `setValue` 设置的值。问题在于:
|
||||
|
||||
1. **`item.unitCodeName` 可能为 undefined**:组套明细 SQL 中 `unitCodeName` 来自字典关联 `sys_dict_data`,如果字典匹配不上则为 null。`newRow` 的 `unitCode_dictText` 直接使用 `item.unitCodeName || ''`,导致显示为空。
|
||||
|
||||
2. **`positionName` 未在 `orderGroupDrawer` 处理项中显式设置**:虽然 `setValue` 会通过库存查询设置 `positionName`,但 `orderGroupDrawer.vue` 的 `handleUseOrderGroup` 没有将 `positionName`(或至少 `orderDetail.positionName`)包含在 processed item 中,导致 `setValue` 的库存查找依赖 `inventoryList`,而 `inventoryList` 来自后端 `AdviceBaseDto`。
|
||||
|
||||
3. **`doseUnitCode_dictText` 依赖 `setValue` 的 `unitCodeList`**:`orderGroupDrawer` 的处理项中没有显式包含 `doseUnitCode_dictText`,完全依赖 `mergedDetail` 中 spread 的 `orderDetail` 字段。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- 前端文件:`openhis-ui-vue3/src/views/doctorstation/components/prescription/orderGroupDrawer.vue`
|
||||
- 前端文件:`openhis-ui-vue3/src/views/inpatientDoctor/home/components/order/index.vue`
|
||||
- 影响场景:住院医生工作站和门诊医生工作站应用医嘱组套
|
||||
|
||||
## 修复方案
|
||||
|
||||
**修改 `orderGroupDrawer.vue` 的 `handleUseOrderGroup` 函数**(line 630-688):
|
||||
|
||||
在 processed item 的 return 对象中显式添加缺失的字段:
|
||||
- `doseUnitCode_dictText`:从 orderDetail 获取剂量单位显示文本
|
||||
- `positionName`:从 orderDetail 获取执行科室/药房名称
|
||||
- `injectFlag` / `injectFlag_enumText`:注射标识
|
||||
- `skinTestFlag` / `skinTestFlag_enumText`:皮试标识
|
||||
- `partPercent`、`partAttributeEnum`、`unitConversionRatio`:用于价格计算的关键字段
|
||||
|
||||
这些字段在 `orderDetail`(AdviceBaseDto)中都有,只是没有在 processed item 的顶层显式设置。`handleSaveGroup` 的 `newRow` 通过 `...prescriptionList.value[rowIndex.value]` spread 能获取到 `setValue` 设置的值,但显式在顶层包含可以确保数据流的完整性。
|
||||
|
||||
## 验证计划
|
||||
|
||||
1. 修改代码后,用 `node --check` 验证语法
|
||||
2. 在住院医生工作站测试:选择患者 → 点击组套 → 预览组套 → 应用到当前患者
|
||||
3. 验证表格中显示的字段:单次剂量、总量、总金额、药房/科室均有值
|
||||
|
||||
---
|
||||
|
||||
## 修复结果:✅ 成功,10行改动
|
||||
|
||||
**修改文件**:`openhis-ui-vue3/src/views/doctorstation/components/prescription/orderGroupDrawer.vue`
|
||||
|
||||
**改动说明**:在 `handleUseOrderGroup` 函数的 processed item 中显式添加了以下缺失字段:
|
||||
- `doseUnitCode_dictText`:剂量单位显示文本(如"mg"),用于"单次剂量"列的后缀显示
|
||||
- `positionName`:药房/科室名称,用于"药房/科室"列显示
|
||||
- `injectFlag` / `injectFlag_enumText`:注射药品标识及文本
|
||||
- `skinTestFlag` / `skinTestFlag_enumText`:皮试标识及文本
|
||||
|
||||
**策略**:策略A(直接修复代码逻辑)—— 组套应用时数据预处理缺失部分关键字段,导致父组件 `handleSaveGroup` 构建行数据时无法获取完整信息。补充字段后,`setValue` 和 `newRow` 构造均能正确传递这些数据到表格。
|
||||
10
.husky/pre-commit
Executable file
10
.husky/pre-commit
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env sh
|
||||
# ============================================================
|
||||
# Husky Pre-commit Hook - HIS项目
|
||||
# 配置: 关羽 | 日期: 2026-04-24
|
||||
# 功能: 提交前检查(已禁用)
|
||||
# ============================================================
|
||||
|
||||
# 🔧 已禁用所有检查,直接允许提交
|
||||
echo "⏭️ [Pre-commit] 检查已禁用,允许提交"
|
||||
exit 0
|
||||
@@ -25,7 +25,7 @@ public class SysTenantController extends BaseController {
|
||||
private ISysTenantService sysTenantService;
|
||||
|
||||
/**
|
||||
* 查询租户分页列表(只读操作,不限制租户管理权限)
|
||||
* 查询租户分页列表
|
||||
*
|
||||
* @param tenantId 租户ID查询
|
||||
* @param tenantCode 租户编码模糊查询
|
||||
@@ -35,7 +35,7 @@ public class SysTenantController extends BaseController {
|
||||
* @param pageSize 每页多少条
|
||||
* @return 租户分页列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:tenant:list')")
|
||||
@PreAuthorize("@ss.hasPermi('system:tenant:operate')")
|
||||
@GetMapping("/page")
|
||||
public R<IPage<SysTenant>> getTenantPage(@RequestParam(required = false) Integer tenantId,
|
||||
@RequestParam(required = false) String tenantCode, @RequestParam(required = false) String tenantName,
|
||||
@@ -45,19 +45,19 @@ public class SysTenantController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询租户详情(只读操作)
|
||||
* 查询租户详情
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @return 租户分页列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:tenant:list')")
|
||||
@PreAuthorize("@ss.hasPermi('system:tenant:operate')")
|
||||
@GetMapping("/{tenantId}")
|
||||
public R<SysTenant> getTenantDetail(@PathVariable Integer tenantId) {
|
||||
return R.ok(sysTenantService.getById(tenantId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询租户所属用户分页列表(只读操作)
|
||||
* 查询租户所属用户分页列表
|
||||
*
|
||||
* @param tenantId 租户ID查询
|
||||
* @param userName 用户昵称模糊查询
|
||||
@@ -67,7 +67,7 @@ public class SysTenantController extends BaseController {
|
||||
* @param pageSize 每页多少条
|
||||
* @return 租户所属用户分页列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:tenant:list')")
|
||||
@PreAuthorize("@ss.hasPermi('system:tenant:operate')")
|
||||
@GetMapping("/user/page")
|
||||
public R<IPage<SysUser>> getTenantUserPage(@RequestParam(required = false) Integer tenantId,
|
||||
@RequestParam(required = false) String userName, @RequestParam(required = false) String nickName,
|
||||
@@ -141,14 +141,14 @@ public class SysTenantController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询租户未绑定的用户列表(只读操作)
|
||||
* 查询租户未绑定的用户列表
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @param pageNum 当前页
|
||||
* @param pageSize 每页多少条
|
||||
* @return 结果
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:tenant:list')")
|
||||
@PreAuthorize("@ss.hasPermi('system:tenant:operate')")
|
||||
@GetMapping("/{tenantId}/unbind-users")
|
||||
public R<IPage<SysUser>> getUnbindTenantUserList(@PathVariable Integer tenantId,
|
||||
@RequestParam(required = false) String userName, @RequestParam(required = false) String nickName,
|
||||
@@ -194,4 +194,4 @@ public class SysTenantController extends BaseController {
|
||||
public R<List<SysTenant>> getUserBindTenantList(@PathVariable String username) {
|
||||
return sysTenantService.getUserBindTenantList(username);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,13 +85,18 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService {
|
||||
String trimmedKey = searchKey.trim();
|
||||
return dictDataMapper.selectDictDataByTypeWithSearch(dictType, trimmedKey);
|
||||
}
|
||||
|
||||
// 直接查询数据库,避免缓存中为空数据导致前端下拉框显示"无数据"
|
||||
List<SysDictData> dictDatas = dictDataMapper.selectDictDataByType(dictType);
|
||||
|
||||
// 否则使用原有方法(带缓存)
|
||||
List<SysDictData> dictDatas = DictUtils.getDictCache(dictType);
|
||||
if (StringUtils.isNotEmpty(dictDatas)) {
|
||||
return dictDatas;
|
||||
}
|
||||
dictDatas = dictDataMapper.selectDictDataByType(dictType);
|
||||
if (StringUtils.isNotEmpty(dictDatas)) {
|
||||
DictUtils.setDictCache(dictType, dictDatas);
|
||||
return dictDatas;
|
||||
}
|
||||
return dictDatas;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,7 +10,7 @@ import com.openhis.clinical.service.ITicketService;
|
||||
import com.openhis.web.appointmentmanage.appservice.ITicketAppService;
|
||||
import com.openhis.web.appointmentmanage.dto.TicketDto;
|
||||
import com.openhis.common.constant.CommonConstants.SlotStatus;
|
||||
import com.openhis.common.enums.OrderStatus;
|
||||
import com.openhis.common.constant.CommonConstants.AppointmentOrderStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@@ -198,11 +198,10 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
||||
if (SlotStatus.CHECKED_IN.equals(slotStatus)) {
|
||||
dto.setStatus("已取号");
|
||||
} else if (SlotStatus.BOOKED.equals(slotStatus)) {
|
||||
// order_main.status: 0=患者取消(已退号) 2=系统取消 其余=已预约
|
||||
if (OrderStatus.PATIENT_CANCELLED.getValue().equals(raw.getOrderStatus())) {
|
||||
if (AppointmentOrderStatus.CHECKED_IN.equals(raw.getOrderStatus())) {
|
||||
dto.setStatus("已取号");
|
||||
} else if (AppointmentOrderStatus.RETURNED.equals(raw.getOrderStatus())) {
|
||||
dto.setStatus("已退号");
|
||||
} else if (OrderStatus.SYSTEM_CANCELLED.getValue().equals(raw.getOrderStatus())) {
|
||||
dto.setStatus("系统取消");
|
||||
} else {
|
||||
dto.setStatus("已预约");
|
||||
}
|
||||
@@ -373,11 +372,10 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
||||
if (SlotStatus.CHECKED_IN.equals(slotStatus)) {
|
||||
dto.setStatus("已取号");
|
||||
} else if (SlotStatus.BOOKED.equals(slotStatus)) {
|
||||
// order_main.status: 0=患者取消(已退号) 2=系统取消 其余=已预约
|
||||
if (OrderStatus.PATIENT_CANCELLED.getValue().equals(raw.getOrderStatus())) {
|
||||
if (AppointmentOrderStatus.CHECKED_IN.equals(raw.getOrderStatus())) {
|
||||
dto.setStatus("已取号");
|
||||
} else if (AppointmentOrderStatus.RETURNED.equals(raw.getOrderStatus())) {
|
||||
dto.setStatus("已退号");
|
||||
} else if (OrderStatus.SYSTEM_CANCELLED.getValue().equals(raw.getOrderStatus())) {
|
||||
dto.setStatus("系统取消");
|
||||
} else {
|
||||
dto.setStatus("已预约");
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import com.core.common.utils.MessageUtils;
|
||||
import com.openhis.administration.domain.Location;
|
||||
import com.openhis.administration.domain.Organization;
|
||||
import com.openhis.administration.domain.OrganizationLocation;
|
||||
import com.openhis.workflow.domain.ActivityDefinition;
|
||||
import com.openhis.administration.mapper.OrganizationLocationMapper;
|
||||
import com.openhis.administration.service.ILocationService;
|
||||
import com.openhis.administration.service.IOrganizationLocationService;
|
||||
@@ -71,7 +70,6 @@ public class OrganizationLocationAppServiceImpl implements IOrganizationLocation
|
||||
// 获取科室下拉选列表
|
||||
List<Organization> organizationList = organizationService.getList(OrganizationType.DEPARTMENT.getValue(), null);
|
||||
List<OrgLocInitDto.departmentOption> organizationOptions = organizationList.stream()
|
||||
.filter(organization -> organization != null && organization.getName() != null)
|
||||
.map(organization -> new OrgLocInitDto.departmentOption(organization.getId(), organization.getName()))
|
||||
.collect(Collectors.toList());
|
||||
initDto.setLocationFormOptions(chargeItemStatusOptions).setDepartmentOptions(organizationOptions);
|
||||
@@ -133,18 +131,11 @@ public class OrganizationLocationAppServiceImpl implements IOrganizationLocation
|
||||
@Override
|
||||
public R<?> addOrEditOrgLoc(OrgLocQueryDto orgLocQueryDto) {
|
||||
|
||||
// Validate required fields before processing
|
||||
if (orgLocQueryDto.getOrganizationId() == null) {
|
||||
return R.fail("请选择执行科室");
|
||||
}
|
||||
|
||||
OrganizationLocation orgLoc = new OrganizationLocation();
|
||||
BeanUtils.copyProperties(orgLocQueryDto, orgLoc);
|
||||
|
||||
Long activityDefinitionId = orgLoc.getActivityDefinitionId();
|
||||
ActivityDefinition activityDef = activityDefinitionId != null
|
||||
? activityDefinitionMapper.selectById(activityDefinitionId) : null;
|
||||
String activityName = activityDef != null ? activityDef.getName() : "";
|
||||
String activityName = activityDefinitionId != null ? activityDefinitionMapper.selectById(activityDefinitionId).getName() : "";
|
||||
|
||||
List<OrganizationLocation> organizationLocationList =
|
||||
organizationLocationService.getOrgLocListByOrgIdAndActivityDefinitionId(orgLoc.getActivityDefinitionId());
|
||||
@@ -156,8 +147,8 @@ public class OrganizationLocationAppServiceImpl implements IOrganizationLocation
|
||||
for (OrganizationLocation organizationLocation : organizationLocationList)
|
||||
if (DateTimeUtils.isOverlap(organizationLocation.getStartTime(), organizationLocation.getEndTime(),
|
||||
orgLoc.getStartTime(), orgLoc.getEndTime())) {
|
||||
Organization org = organizationService.getById(organizationLocation.getOrganizationId());
|
||||
String organizationName = org != null ? org.getName() : "未知科室";
|
||||
String organizationName =
|
||||
organizationService.getById(organizationLocation.getOrganizationId()).getName();
|
||||
return R.fail("当前诊疗:" + activityName + CommonConstants.Common.DASH + orgLoc.getStartTime()
|
||||
+ CommonConstants.Common.DASH + orgLoc.getEndTime() + "与" + organizationName + "时间冲突");
|
||||
}
|
||||
|
||||
@@ -83,9 +83,6 @@ public class InfectiousCardDto {
|
||||
/** 病例分类 */
|
||||
private String diseaseType;
|
||||
|
||||
/** 病例分类 */
|
||||
private Integer caseClass;
|
||||
|
||||
/** 发病日期 */
|
||||
private LocalDate onsetDate;
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.openhis.web.chargemanage.appservice.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.core.common.core.domain.R;
|
||||
@@ -330,14 +329,16 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
||||
}
|
||||
}
|
||||
|
||||
// 退费成功后,同步回滚预约订单状态及号源;同时移除分诊队列
|
||||
Long refundOrderMainId = null;
|
||||
// 如果本次门诊挂号来自预约签到,同步把预约订单与号源槽位状态改为已退号
|
||||
if (result != null && result.getCode() == 200) {
|
||||
refundOrderMainId = syncAppointmentReturnStatus(byId, cancelRegPaymentDto.getReason());
|
||||
syncAppointmentReturnStatus(byId, cancelRegPaymentDto.getReason());
|
||||
|
||||
// 同步移除分诊队列中的记录
|
||||
removeTriageQueueItem(byId.getId());
|
||||
}
|
||||
// 退号日志独立事务写入,无论退费成功与否均记录
|
||||
recordRefundLog(cancelRegPaymentDto, byId, result, paymentRecon, refundOrderMainId);
|
||||
|
||||
// 记录退号日志
|
||||
recordRefundLog(cancelRegPaymentDto, byId, result, paymentRecon);
|
||||
|
||||
// 2025/05/05 该处保存费用项后,会通过统一收费处理进行收费
|
||||
return R.ok(paymentRecon, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, new Object[] {"退号"}));
|
||||
@@ -434,6 +435,8 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
||||
// 通过患者、科室、日期查找关联的预约订单
|
||||
LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<Order>()
|
||||
.eq(Order::getPatientId, encounter.getPatientId())
|
||||
.in(Order::getStatus, CommonConstants.AppointmentOrderStatus.BOOKED,
|
||||
CommonConstants.AppointmentOrderStatus.CHECKED_IN)
|
||||
.orderByDesc(Order::getUpdateTime)
|
||||
.orderByDesc(Order::getCreateTime)
|
||||
.last("LIMIT 1");
|
||||
@@ -587,25 +590,20 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
||||
}
|
||||
|
||||
/**
|
||||
* 诊前退号:回滚预约订单、号源槽位、号源池统计。
|
||||
*
|
||||
* <p>处理四件事:
|
||||
* <ol>
|
||||
* <li>order_main → status=0(患者取消), pay_status=3(已退费), 写入取消时间和原因</li>
|
||||
* <li>adm_schedule_slot → status=0(待约), order_id=NULL(释放号源)</li>
|
||||
* <li>adm_schedule_pool → 重算统计值 + version+1</li>
|
||||
* <li>返回 order_main.id 供 refund_log 关联</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>异常仅记录日志不向上抛,不影响主流程返回成功。
|
||||
* 同步预约号源状态为已退号。
|
||||
* 说明:
|
||||
* 1) 门诊退号主流程不依赖该步骤成功与否,因此此方法内部异常仅记录日志,不向上抛出。
|
||||
* 2) 通过患者、科室、日期以及状态筛选最近一条预约订单,尽量避免误匹配。
|
||||
*/
|
||||
private Long syncAppointmentReturnStatus(Encounter encounter, String reason) {
|
||||
private void syncAppointmentReturnStatus(Encounter encounter, String reason) {
|
||||
if (encounter == null || encounter.getPatientId() == null) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<Order>()
|
||||
.eq(Order::getPatientId, encounter.getPatientId())
|
||||
.in(Order::getStatus, CommonConstants.AppointmentOrderStatus.BOOKED,
|
||||
CommonConstants.AppointmentOrderStatus.CHECKED_IN)
|
||||
.orderByDesc(Order::getUpdateTime)
|
||||
.orderByDesc(Order::getCreateTime)
|
||||
.last("LIMIT 1");
|
||||
@@ -627,55 +625,35 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
||||
|
||||
Order appointmentOrder = orderService.getOne(queryWrapper, false);
|
||||
if (appointmentOrder == null) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
// 只有有效订单(1)才能退号
|
||||
if (!OrderStatus.ACTIVE.getValue().equals(appointmentOrder.getStatus())) {
|
||||
log.warn("退号跳过:订单状态非有效, orderId={}, status={}",
|
||||
appointmentOrder.getId(), appointmentOrder.getStatus());
|
||||
return null;
|
||||
}
|
||||
|
||||
// 乐观锁更新:WHERE version = 旧值,防并发重复退号
|
||||
boolean updated = orderService.update(
|
||||
new LambdaUpdateWrapper<Order>()
|
||||
.set(Order::getStatus, OrderStatus.PATIENT_CANCELLED.getValue())
|
||||
.set(Order::getPayStatus, PaymentStatus.REFUND_ALL.getValue())
|
||||
.set(Order::getCancelTime, new Date())
|
||||
.set(Order::getCancelReason,
|
||||
StringUtils.isNotEmpty(reason) ? reason : "诊前退号")
|
||||
.set(Order::getUpdateTime, new Date())
|
||||
.setSql("version = version + 1")
|
||||
.eq(Order::getId, appointmentOrder.getId())
|
||||
.eq(Order::getVersion, appointmentOrder.getVersion())
|
||||
);
|
||||
if (!updated) {
|
||||
log.warn("退号乐观锁冲突,订单已被其他操作修改, orderId={}", appointmentOrder.getId());
|
||||
return null;
|
||||
Date now = new Date();
|
||||
if (!CommonConstants.AppointmentOrderStatus.RETURNED.equals(appointmentOrder.getStatus())) {
|
||||
Order updateOrder = new Order();
|
||||
updateOrder.setId(appointmentOrder.getId());
|
||||
updateOrder.setStatus(CommonConstants.AppointmentOrderStatus.RETURNED);
|
||||
updateOrder.setCancelTime(now);
|
||||
updateOrder.setCancelReason(
|
||||
StringUtils.isNotEmpty(reason) ? reason : "门诊退号");
|
||||
updateOrder.setUpdateTime(now);
|
||||
orderService.updateById(updateOrder);
|
||||
}
|
||||
|
||||
Long slotId = appointmentOrder.getSlotId();
|
||||
if (slotId == null) {
|
||||
return appointmentOrder.getId();
|
||||
return;
|
||||
}
|
||||
|
||||
int slotRows = scheduleSlotMapper.updateSlotStatus(slotId, CommonConstants.SlotStatus.AVAILABLE);
|
||||
int slotRows = scheduleSlotMapper.updateSlotStatus(slotId, CommonConstants.SlotStatus.RETURNED);
|
||||
if (slotRows > 0) {
|
||||
Long poolId = scheduleSlotMapper.selectPoolIdBySlotId(slotId);
|
||||
if (poolId != null) {
|
||||
schedulePoolMapper.refreshPoolStats(poolId);
|
||||
schedulePoolMapper.update(null,
|
||||
new LambdaUpdateWrapper<SchedulePool>()
|
||||
.setSql("version = version + 1")
|
||||
.set(SchedulePool::getUpdateTime, new Date())
|
||||
.eq(SchedulePool::getId, poolId));
|
||||
}
|
||||
}
|
||||
return appointmentOrder.getId();
|
||||
} catch (Exception e) {
|
||||
log.warn("同步预约号源已退号状态失败, encounterId={}", encounter.getId(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -694,29 +672,22 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录退号日志(独立事务)。
|
||||
*
|
||||
* <p>REQUIRES_NEW 确保即使主事务回滚,退号审计日志也不丢失。
|
||||
* orderMainId 优先使用 order_main.id,若退费失败则 fallback 到 encounterId。
|
||||
* 记录退号日志
|
||||
*
|
||||
* @param cancelRegPaymentDto 退号请求对象
|
||||
* @param encounter 就诊信息
|
||||
* @param result 退号结果
|
||||
* @param paymentRecon 支付对账信息
|
||||
* @param orderMainId 预约订单主键(order_main.id),用于关联业务数据
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void recordRefundLog(CancelRegPaymentDto cancelRegPaymentDto,
|
||||
Encounter encounter,
|
||||
R<?> result,
|
||||
PaymentReconciliation paymentRecon,
|
||||
Long orderMainId) {
|
||||
PaymentReconciliation paymentRecon) {
|
||||
RefundLog refundLog = new RefundLog();
|
||||
try {
|
||||
// 1. 订单ID(关联 order_main.id)
|
||||
String orderId = orderMainId != null
|
||||
? String.valueOf(orderMainId)
|
||||
: String.valueOf(cancelRegPaymentDto.getEncounterId());
|
||||
// 1. 订单ID(唯一)
|
||||
String orderId = String.valueOf(cancelRegPaymentDto.getEncounterId());
|
||||
refundLog.setOrderId(orderId);
|
||||
|
||||
// 已存在则不重复插入(防止唯一约束异常)
|
||||
|
||||
@@ -366,7 +366,7 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
||||
serviceRequest.setTherapyEnum(TherapyTimeType.TEMPORARY.getValue());// 治疗类型
|
||||
serviceRequest.setQuantity(BigDecimal.valueOf(1)); // 请求数量
|
||||
serviceRequest.setUnitCode("次"); // 请求单位编码
|
||||
serviceRequest.setCategoryEnum(24); // 请求类型:24-手术(新值域,避开 adviceType 碰撞)
|
||||
serviceRequest.setCategoryEnum(4); // 请求类型:4-手术
|
||||
serviceRequest.setActivityId(surgeryId); // 手术ID作为诊疗定义id
|
||||
serviceRequest.setPatientId(surgeryDto.getPatientId()); // 患者
|
||||
serviceRequest.setRequesterId(practitionerId); // 开方医生
|
||||
@@ -418,7 +418,7 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
||||
// 清除相关缓存
|
||||
clearSurgeryAppCache(surgery);
|
||||
|
||||
return R.ok(surgeryId, "手术申请提交成功!");
|
||||
return R.ok(surgeryId, MessageUtils.createMessage(PromptMsgConstant.Common.M00001, new Object[]{"手术信息"}));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -497,7 +497,7 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
||||
// 清除相关缓存
|
||||
clearSurgeryAppCache(surgery);
|
||||
|
||||
return R.ok(null, "手术申请修改成功!");
|
||||
return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00002, new Object[]{"手术信息"}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.core.common.core.domain.R;
|
||||
import com.core.common.core.domain.model.LoginUser;
|
||||
import com.core.common.utils.SecurityUtils;
|
||||
import com.openhis.administration.domain.Patient;
|
||||
import com.openhis.administration.service.IOrganizationService;
|
||||
import com.openhis.administration.service.IPatientService;
|
||||
import com.openhis.clinical.domain.Surgery;
|
||||
import com.openhis.clinical.service.ISurgeryService;
|
||||
@@ -27,6 +28,7 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.URLEncoder;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@@ -202,8 +204,6 @@ public class SurgicalScheduleAppServiceImpl implements ISurgicalScheduleAppServi
|
||||
return R.fail("新增手术安排失败");
|
||||
}
|
||||
|
||||
syncSurgeryIncisionLevel(opSchedule.getOperCode(), opCreateScheduleDto.getIncisionLevel());
|
||||
|
||||
// Bug #247 修复:更新手术申请单状态为已排期 (1)
|
||||
if (opCreateScheduleDto.getApplyId() != null) {
|
||||
try {
|
||||
@@ -300,8 +300,6 @@ public class SurgicalScheduleAppServiceImpl implements ISurgicalScheduleAppServi
|
||||
return R.fail("修改手术安排失败");
|
||||
}
|
||||
|
||||
syncSurgeryIncisionLevel(opScheduleDto.getOperCode(), opScheduleDto.getIncisionLevel());
|
||||
|
||||
return R.ok("修改手术安排成功");
|
||||
}
|
||||
|
||||
@@ -435,28 +433,6 @@ public class SurgicalScheduleAppServiceImpl implements ISurgicalScheduleAppServi
|
||||
return scheduleDate.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步手术申请表中的切口类型
|
||||
*/
|
||||
private void syncSurgeryIncisionLevel(String surgeryNo, Integer incisionLevel) {
|
||||
if (surgeryNo == null || surgeryNo.isEmpty() || incisionLevel == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<Surgery> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(Surgery::getSurgeryNo, surgeryNo)
|
||||
.eq(Surgery::getDeleteFlag, "0");
|
||||
Surgery surgery = surgeryService.getOne(queryWrapper);
|
||||
if (surgery == null) {
|
||||
log.warn("未找到需要同步切口类型的手术申请记录 - surgeryNo: {}", surgeryNo);
|
||||
return;
|
||||
}
|
||||
|
||||
surgery.setIncisionLevel(incisionLevel);
|
||||
surgery.setUpdateTime(new Date());
|
||||
surgeryService.updateById(surgery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充手术申请中缺失的名称字段
|
||||
* 在创建手术安排时调用,确保关联的cli_surgery表中的名称字段有值
|
||||
|
||||
@@ -2,13 +2,9 @@ package com.openhis.web.clinicalmanage.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.core.common.core.domain.R;
|
||||
import com.openhis.common.enums.ActivityDefCategory;
|
||||
import com.openhis.web.clinicalmanage.appservice.ISurgicalScheduleAppService;
|
||||
import com.openhis.web.clinicalmanage.dto.OpCreateScheduleDto;
|
||||
import com.openhis.web.clinicalmanage.dto.OpScheduleDto;
|
||||
import com.openhis.web.regdoctorstation.appservice.IRequestFormManageAppService;
|
||||
import com.openhis.web.regdoctorstation.dto.RequestFormDto;
|
||||
import com.openhis.web.regdoctorstation.dto.RequestFormPageDto;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@@ -30,7 +26,6 @@ import java.util.Map;
|
||||
public class SurgicalScheduleController {
|
||||
|
||||
private final ISurgicalScheduleAppService surgicalScheduleAppService;
|
||||
private final IRequestFormManageAppService requestFormManageAppService;
|
||||
|
||||
/**
|
||||
* 分页查询手术安排列表
|
||||
@@ -92,27 +87,6 @@ public class SurgicalScheduleController {
|
||||
return surgicalScheduleAppService.deleteSurgerySchedule(scheduleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询待排期手术申请列表
|
||||
*
|
||||
* @param requestFormDto 查询条件
|
||||
* @return 手术申请列表
|
||||
*/
|
||||
@PostMapping(value = "/apply-list")
|
||||
public R<IPage<RequestFormPageDto>> getSurgeryApplyList(@RequestBody RequestFormDto requestFormDto) {
|
||||
if (requestFormDto.getPageNo() == null) {
|
||||
requestFormDto.setPageNo(1);
|
||||
}
|
||||
if (requestFormDto.getPageSize() == null) {
|
||||
requestFormDto.setPageSize(10);
|
||||
}
|
||||
//虽然很想这么写,但是库里的手术申请单的type_code都是直接写的SURGERY
|
||||
// requestFormDto.setTypeCode(ActivityDefCategory.PROCEDURE.getCode());
|
||||
//只查询手术申请单
|
||||
requestFormDto.setTypeCode("SURGERY");
|
||||
return R.ok(requestFormManageAppService.getRequestFormPage(requestFormDto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出手术安排列表
|
||||
*
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@@ -84,11 +85,6 @@ public class OpCreateScheduleDto {
|
||||
*/
|
||||
private String surgerySite;
|
||||
|
||||
/**
|
||||
* 切口类型
|
||||
*/
|
||||
private Integer incisionLevel;
|
||||
|
||||
/**
|
||||
* 入院时间
|
||||
*/
|
||||
@@ -250,26 +246,11 @@ public class OpCreateScheduleDto {
|
||||
*/
|
||||
private String communicationInfo;
|
||||
|
||||
/**
|
||||
* 是否外请专家 1-是 0-否
|
||||
*/
|
||||
private Integer isExternalExpert;
|
||||
|
||||
/**
|
||||
* 外请专家姓名
|
||||
*/
|
||||
private String externalExpertName;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 费用类别
|
||||
*/
|
||||
private String feeType;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.openhis.surgicalschedule.domain.OpSchedule;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDate;
|
||||
@@ -94,12 +93,6 @@ public class OpScheduleDto extends OpSchedule {
|
||||
* 手术类型
|
||||
*/
|
||||
private String surgeryType;
|
||||
|
||||
/**
|
||||
* 切口类型
|
||||
*/
|
||||
private Integer incisionLevel;
|
||||
|
||||
/**
|
||||
* 申请科室
|
||||
*/
|
||||
@@ -113,5 +106,4 @@ public class OpScheduleDto extends OpSchedule {
|
||||
* 创建人名称
|
||||
*/
|
||||
private String createByName;
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.core.common.core.domain.R;
|
||||
import com.openhis.web.doctorstation.dto.AdviceBaseDto;
|
||||
import com.openhis.web.doctorstation.dto.AdviceSaveParam;
|
||||
import com.openhis.web.doctorstation.dto.OrderBindInfoDto;
|
||||
import com.openhis.web.doctorstation.dto.SurgeryItemDto;
|
||||
import com.openhis.web.doctorstation.dto.UpdateGroupIdParam;
|
||||
|
||||
import java.util.List;
|
||||
@@ -60,16 +59,6 @@ public interface IDoctorStationAdviceAppService {
|
||||
*/
|
||||
R<?> getRequestBaseInfo(Long encounterId);
|
||||
|
||||
/**
|
||||
* 查询医嘱请求数据(支持按生成来源和来源单据号过滤)
|
||||
*
|
||||
* @param encounterId 就诊id
|
||||
* @param generateSourceEnum 生成来源(可选,如手术计费=6)
|
||||
* @param sourceBillNo 来源业务单据号(可选)
|
||||
* @return 医嘱请求数据
|
||||
*/
|
||||
R<?> getRequestBaseInfo(Long encounterId, Integer generateSourceEnum, String sourceBillNo);
|
||||
|
||||
/**
|
||||
* 门诊签退医嘱
|
||||
*
|
||||
@@ -135,18 +124,4 @@ public interface IDoctorStationAdviceAppService {
|
||||
* @return 已配置的药品类别编码列表
|
||||
*/
|
||||
R<?> getConfiguredCategories(Long organizationId);
|
||||
|
||||
/**
|
||||
* 手术项目专用分页查询(仅手术 + 定价,无库存/草稿库存/取药科室等无关逻辑)
|
||||
*
|
||||
* @param organizationId 科室ID(可选)
|
||||
* @param pageNo 当前页
|
||||
* @param pageSize 每页条数
|
||||
* @param searchKey 模糊查询关键字(可选)
|
||||
* @return 手术项目分页数据(含价格信息)
|
||||
*/
|
||||
IPage<SurgeryItemDto> getSurgeryPage(Long organizationId, Integer pageNo, Integer pageSize, String searchKey);
|
||||
|
||||
IPage<SurgeryItemDto> getExaminationPage(Long organizationId, Integer pageNo, Integer pageSize, String searchKey);
|
||||
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ import com.openhis.medication.service.IMedicationDispenseService;
|
||||
import com.openhis.medication.service.IMedicationRequestService;
|
||||
import com.openhis.web.chargemanage.mapper.OutpatientRegistrationAppMapper;
|
||||
import com.openhis.web.doctorstation.appservice.IDoctorStationAdviceAppService;
|
||||
import com.openhis.web.doctorstation.appservice.IDoctorStationInspectionLabApplyService;
|
||||
import com.openhis.web.doctorstation.dto.*;
|
||||
import com.openhis.web.doctorstation.mapper.DoctorStationAdviceAppMapper;
|
||||
import com.openhis.web.doctorstation.utils.AdviceUtils;
|
||||
@@ -48,15 +47,12 @@ import com.openhis.workflow.domain.InventoryItem;
|
||||
import com.openhis.workflow.domain.ServiceRequest;
|
||||
import com.openhis.workflow.service.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -66,9 +62,6 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAppService {
|
||||
|
||||
private static final Pattern INSPECTION_APPLY_NO_JSON =
|
||||
Pattern.compile("\"applyNo\"\\s*:\\s*\"([^\"]+)\"");
|
||||
|
||||
@Resource
|
||||
AssignSeqUtil assignSeqUtil;
|
||||
|
||||
@@ -125,13 +118,6 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
@Resource
|
||||
IInventoryItemService inventoryItemService;
|
||||
|
||||
/**
|
||||
* 与检验申请实现存在循环依赖,需延迟注入;删除诊疗医嘱时按 contentJson 级联作废检验申请单。
|
||||
*/
|
||||
@Resource
|
||||
@Lazy
|
||||
private IDoctorStationInspectionLabApplyService iDoctorStationInspectionLabApplyService;
|
||||
|
||||
// 缓存 key 前缀
|
||||
private static final String ADVICE_BASE_INFO_CACHE_PREFIX = "advice:base:info:";
|
||||
// 缓存过期时间(小时)
|
||||
@@ -242,58 +228,36 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
// 医嘱定义ID集合
|
||||
List<Long> adviceDefinitionIdList = adviceBaseDtoList.stream().map(AdviceBaseDto::getAdviceDefinitionId)
|
||||
.collect(Collectors.toList());
|
||||
// 费用定价主表ID集合(过滤null值,手术项目无定价定义)
|
||||
List<Long> chargeItemDefinitionIdList = adviceBaseDtoList.stream()
|
||||
.map(AdviceBaseDto::getChargeItemDefinitionId)
|
||||
.filter(Objects::nonNull)
|
||||
// 费用定价主表ID集合
|
||||
List<Long> chargeItemDefinitionIdList = adviceBaseDtoList.stream().map(AdviceBaseDto::getChargeItemDefinitionId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 判断是否包含药品或耗材类型(只有这些类型才需要库存相关查询)
|
||||
boolean hasMedOrDevice = adviceTypes != null
|
||||
&& (adviceTypes.contains(1) || adviceTypes.contains(2));
|
||||
|
||||
// 医嘱库存集合 — 仅药品/耗材需要库存查询,手术/诊疗(3,6)无库存概念,跳过以减少数据库开销
|
||||
List<AdviceInventoryDto> adviceInventoryList;
|
||||
List<AdviceInventoryDto> adviceDraftInventoryList;
|
||||
List<AdviceInventoryDto> adviceInventory;
|
||||
if (hasMedOrDevice) {
|
||||
adviceInventoryList = doctorStationAdviceAppMapper.getAdviceInventory(locationId,
|
||||
adviceDefinitionIdList,
|
||||
CommonConstants.SqlCondition.ABOUT_INVENTORY_TABLE_STR, PublicationStatus.ACTIVE.getValue());
|
||||
// 待发放个数信息
|
||||
adviceDraftInventoryList = doctorStationAdviceAppMapper.getAdviceDraftInventory(
|
||||
CommonConstants.TableName.MED_MEDICATION_DEFINITION, CommonConstants.TableName.ADM_DEVICE_DEFINITION,
|
||||
DispenseStatus.DRAFT.getValue(), DispenseStatus.PREPARATION.getValue());
|
||||
// 预减库存
|
||||
adviceInventory = adviceUtils.subtractInventory(adviceInventoryList, adviceDraftInventoryList);
|
||||
} else {
|
||||
adviceInventoryList = Collections.emptyList();
|
||||
adviceDraftInventoryList = Collections.emptyList();
|
||||
adviceInventory = Collections.emptyList();
|
||||
}
|
||||
// 查询取药科室配置 — 仅药品开单场景需要
|
||||
List<AdviceInventoryDto> medLocationConfig;
|
||||
Map<String, Set<Long>> allowedLocByCategory;
|
||||
if (hasMedOrDevice) {
|
||||
medLocationConfig = doctorStationAdviceAppMapper.getMedLocationConfig(organizationId);
|
||||
// 将配置转为 {categoryCode -> 允许的locationId集合}
|
||||
allowedLocByCategory = new HashMap<>();
|
||||
if (medLocationConfig != null && !medLocationConfig.isEmpty()) {
|
||||
for (AdviceInventoryDto cfg : medLocationConfig) {
|
||||
if (cfg.getCategoryCode() == null || cfg.getLocationId() == null) {
|
||||
continue;
|
||||
}
|
||||
allowedLocByCategory.computeIfAbsent(String.valueOf(cfg.getCategoryCode()), k -> new HashSet<>())
|
||||
.add(cfg.getLocationId());
|
||||
// 医嘱库存集合
|
||||
List<AdviceInventoryDto> adviceInventoryList = doctorStationAdviceAppMapper.getAdviceInventory(locationId,
|
||||
adviceDefinitionIdList,
|
||||
CommonConstants.SqlCondition.ABOUT_INVENTORY_TABLE_STR, PublicationStatus.ACTIVE.getValue());
|
||||
// 待发放个数信息
|
||||
List<AdviceInventoryDto> adviceDraftInventoryList = doctorStationAdviceAppMapper.getAdviceDraftInventory(
|
||||
CommonConstants.TableName.MED_MEDICATION_DEFINITION, CommonConstants.TableName.ADM_DEVICE_DEFINITION,
|
||||
DispenseStatus.DRAFT.getValue(), DispenseStatus.PREPARATION.getValue());
|
||||
// 预减库存
|
||||
List<AdviceInventoryDto> adviceInventory = adviceUtils.subtractInventory(adviceInventoryList,
|
||||
adviceDraftInventoryList);
|
||||
// 查询取药科室配置
|
||||
List<AdviceInventoryDto> medLocationConfig = doctorStationAdviceAppMapper.getMedLocationConfig(organizationId);
|
||||
// 将配置转为 {categoryCode -> 允许的locationId集合}
|
||||
Map<String, Set<Long>> allowedLocByCategory = new HashMap<>();
|
||||
if (medLocationConfig != null && !medLocationConfig.isEmpty()) {
|
||||
for (AdviceInventoryDto cfg : medLocationConfig) {
|
||||
if (cfg.getCategoryCode() == null || cfg.getLocationId() == null) {
|
||||
continue;
|
||||
}
|
||||
allowedLocByCategory.computeIfAbsent(String.valueOf(cfg.getCategoryCode()), k -> new HashSet<>())
|
||||
.add(cfg.getLocationId());
|
||||
}
|
||||
} else {
|
||||
medLocationConfig = Collections.emptyList();
|
||||
allowedLocByCategory = Collections.emptyMap();
|
||||
}
|
||||
// 费用定价子表信息 - 仅药品/耗材需要批次定价查询,手术/诊疗无库存概念不需要
|
||||
// 费用定价子表信息 - 使用分批处理避免大量参数问题
|
||||
List<AdvicePriceDto> childCharge = new ArrayList<>();
|
||||
if (hasMedOrDevice && chargeItemDefinitionIdList != null && !chargeItemDefinitionIdList.isEmpty()) {
|
||||
if (chargeItemDefinitionIdList != null && !chargeItemDefinitionIdList.isEmpty()) {
|
||||
// 分批处理,每批最多1000个ID,增加批次大小以减少查询次数
|
||||
int batchSize = 1000;
|
||||
for (int i = 0; i < chargeItemDefinitionIdList.size(); i += batchSize) {
|
||||
@@ -973,16 +937,11 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
}
|
||||
}
|
||||
for (AdviceSaveDto adviceSaveDto : deleteList) {
|
||||
Long requestId = adviceSaveDto.getRequestId();
|
||||
// 🔧 Bug #442: 跳过 requestId 为 null 的记录,避免删除不存在的药品请求
|
||||
if (requestId == null) {
|
||||
log.warn("BugFix#442: handMedication - 跳过 requestId 为 null 的删除请求");
|
||||
continue;
|
||||
}
|
||||
iMedicationRequestService.removeById(requestId);
|
||||
iMedicationRequestService.removeById(adviceSaveDto.getRequestId());
|
||||
// 删除已经产生的药品发放信息
|
||||
iMedicationDispenseService.deleteMedicationDispense(adviceSaveDto.getRequestId());
|
||||
// 🔧 Bug Fix #219: 删除费用项
|
||||
Long requestId = adviceSaveDto.getRequestId();
|
||||
String serviceTable = CommonConstants.TableName.MED_MEDICATION_REQUEST;
|
||||
// 直接删除费用项
|
||||
iChargeItemService.deleteByServiceTableAndId(serviceTable, requestId);
|
||||
@@ -1107,9 +1066,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
if (is_save) {
|
||||
medicationRequest.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.MEDICATION_RES_NO.getPrefix(), 4));
|
||||
}
|
||||
medicationRequest.setGenerateSourceEnum(adviceSaveDto.getGenerateSourceEnum() != null
|
||||
? adviceSaveDto.getGenerateSourceEnum()
|
||||
: GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
|
||||
medicationRequest.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
|
||||
medicationRequest.setQuantity(adviceSaveDto.getQuantity()); // 请求数量
|
||||
medicationRequest.setExecuteNum(adviceSaveDto.getExecuteNum()); // 执行次数
|
||||
medicationRequest.setUnitCode(adviceSaveDto.getUnitCode()); // 请求单位编码
|
||||
@@ -1155,9 +1112,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
chargeItem.setId(adviceSaveDto.getChargeItemId()); // 费用项id
|
||||
chargeItem.setStatusEnum(2); // 已生成医嘱
|
||||
chargeItem.setBusNo(AssignSeqEnum.CHARGE_ITEM_NO.getPrefix().concat(medicationRequest.getBusNo()));
|
||||
chargeItem.setGenerateSourceEnum(adviceSaveDto.getGenerateSourceEnum() != null
|
||||
? adviceSaveDto.getGenerateSourceEnum()
|
||||
: GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
|
||||
chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
|
||||
chargeItem.setPrescriptionNo(adviceSaveDto.getPrescriptionNo()); // 处方号
|
||||
chargeItem.setPatientId(adviceSaveDto.getPatientId()); // 患者
|
||||
chargeItem.setContextEnum(adviceSaveDto.getAdviceType()); // 类型
|
||||
@@ -1251,9 +1206,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
deviceRequest.setCreateBy(currentUsername);
|
||||
deviceRequest.setCreateTime(curDate);
|
||||
deviceRequest.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.DEVICE_RES_NO.getPrefix(), 4));
|
||||
deviceRequest.setGenerateSourceEnum(adviceSaveDto.getGenerateSourceEnum() != null
|
||||
? adviceSaveDto.getGenerateSourceEnum()
|
||||
: GenerateSource.DOCTOR_PRESCRIPTION.getValue());
|
||||
deviceRequest.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue());
|
||||
deviceRequest.setQuantity(boundDevice.getQuantity());
|
||||
deviceRequest.setUnitCode(boundDevice.getUnitCode());
|
||||
deviceRequest.setCategoryEnum(adviceSaveDto.getCategoryEnum());
|
||||
@@ -1319,9 +1272,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
deviceChargeItem.setCreateTime(curDate);
|
||||
deviceChargeItem.setStatusEnum(ChargeItemStatus.PLANNED.getValue());
|
||||
deviceChargeItem.setBusNo(AssignSeqEnum.CHARGE_ITEM_NO.getPrefix().concat(deviceRequest.getBusNo()));
|
||||
deviceChargeItem.setGenerateSourceEnum(adviceSaveDto.getGenerateSourceEnum() != null
|
||||
? adviceSaveDto.getGenerateSourceEnum()
|
||||
: GenerateSource.DOCTOR_PRESCRIPTION.getValue());
|
||||
deviceChargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue());
|
||||
deviceChargeItem.setPrescriptionNo(adviceSaveDto.getPrescriptionNo()); // 处方号,与药品一致
|
||||
deviceChargeItem.setPatientId(adviceSaveDto.getPatientId());
|
||||
deviceChargeItem.setContextEnum(ChargeItemContext.DEVICE.getValue()); // 耗材类型
|
||||
@@ -1446,11 +1397,6 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
log.info("BugFix#219: handDevice - 开始删除循环, deleteList.size={}", deleteList.size());
|
||||
for (AdviceSaveDto adviceSaveDto : deleteList) {
|
||||
Long requestId = adviceSaveDto.getRequestId();
|
||||
// 🔧 Bug #442: 跳过 requestId 为 null 的记录,避免删除不存在的耗材请求
|
||||
if (requestId == null) {
|
||||
log.warn("BugFix#442: handDevice - 跳过 requestId 为 null 的删除请求");
|
||||
continue;
|
||||
}
|
||||
log.info("BugFix#219: handDevice - 删除开始: requestId={}", requestId);
|
||||
|
||||
// 1. 删除耗材请求
|
||||
@@ -1550,47 +1496,22 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
if (is_save) {
|
||||
deviceRequest.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.DEVICE_RES_NO.getPrefix(), 4));
|
||||
}
|
||||
deviceRequest.setGenerateSourceEnum(adviceSaveDto.getGenerateSourceEnum() != null
|
||||
? adviceSaveDto.getGenerateSourceEnum()
|
||||
: GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
|
||||
deviceRequest.setPrescriptionNo(adviceSaveDto.getSourceBillNo()); // 来源业务单据号(手术单号)
|
||||
deviceRequest.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
|
||||
deviceRequest.setQuantity(adviceSaveDto.getQuantity()); // 请求数量
|
||||
deviceRequest.setUnitCode(adviceSaveDto.getUnitCode()); // 请求单位编码
|
||||
deviceRequest.setLotNumber(adviceSaveDto.getLotNumber());// 产品批号
|
||||
|
||||
deviceRequest.setCategoryEnum(adviceSaveDto.getCategoryEnum()); // 请求类型
|
||||
// 🔧 BugFix #498: categoryEnum=22(检查) 走 ServiceRequest,不走 DeviceRequest
|
||||
// 检查申请单的诊疗定义ID存在 activityId,不在 adviceDefinitionId
|
||||
// deviceDefId 对应耗材定义ID,不能用诊疗定义ID填充
|
||||
if (adviceSaveDto.getCategoryEnum() == 22) {
|
||||
log.info("handDevice skip - 检查申请单(categoryEnum=22) 走 ServiceRequest 路径,跳过 DeviceRequest 保存");
|
||||
continue; // 跳过本次循环,不走耗材请求路径
|
||||
} else if (adviceSaveDto.getAdviceDefinitionId() != null) {
|
||||
deviceRequest.setDeviceDefId(adviceSaveDto.getAdviceDefinitionId());// 耗材定义id
|
||||
} else {
|
||||
log.warn("handDevice - deviceDefId 为空,adviceDefinitionId=null, categoryEnum={}", adviceSaveDto.getCategoryEnum());
|
||||
}
|
||||
|
||||
deviceRequest.setDeviceDefId(adviceSaveDto.getAdviceDefinitionId());// 耗材定义id
|
||||
deviceRequest.setPatientId(adviceSaveDto.getPatientId()); // 患者
|
||||
deviceRequest.setRequesterId(adviceSaveDto.getPractitionerId()); // 开方医生
|
||||
deviceRequest.setOrgId(adviceSaveDto.getFounderOrgId());// 开方人科室
|
||||
deviceRequest.setReqAuthoredTime(curDate); // 请求开始时间
|
||||
// 发放耗材房(若前端未传locationId,优先沿用已有DeviceRequest的performLocation,否则使用登录用户科室)
|
||||
// 发放耗材房(若前端未传locationId,使用登录用户的科室作为默认值)
|
||||
Long locId = adviceSaveDto.getLocationId();
|
||||
if (locId == null) {
|
||||
// 尝试从已有DeviceRequest获取原始的performLocation
|
||||
if (adviceSaveDto.getRequestId() != null) {
|
||||
DeviceRequest existingDevice = iDeviceRequestService.getById(adviceSaveDto.getRequestId());
|
||||
if (existingDevice != null && existingDevice.getPerformLocation() != null) {
|
||||
locId = existingDevice.getPerformLocation();
|
||||
log.info("耗材locationId为空,使用已有DeviceRequest的performLocation: locationId={}", locId);
|
||||
}
|
||||
}
|
||||
// 如果已有记录也没有performLocation,则使用登录用户科室作为兜底
|
||||
if (locId == null) {
|
||||
locId = SecurityUtils.getLoginUser().getOrgId();
|
||||
log.info("耗材locationId为空且无已有记录,使用登录用户科室作为默认值: locationId={}", locId);
|
||||
}
|
||||
locId = SecurityUtils.getLoginUser().getOrgId();
|
||||
log.info("耗材locationId为空,使用登录用户科室作为默认值: locationId={}", locId);
|
||||
}
|
||||
deviceRequest.setPerformLocation(locId);
|
||||
deviceRequest.setEncounterId(adviceSaveDto.getEncounterId()); // 就诊id
|
||||
@@ -1615,9 +1536,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
chargeItem.setCreateTime(curDate); // 补全创建时间
|
||||
chargeItem.setStatusEnum(2); // 已生成医嘱
|
||||
chargeItem.setBusNo(AssignSeqEnum.CHARGE_ITEM_NO.getPrefix().concat(deviceRequest.getBusNo()));
|
||||
chargeItem.setGenerateSourceEnum(adviceSaveDto.getGenerateSourceEnum() != null
|
||||
? adviceSaveDto.getGenerateSourceEnum()
|
||||
: GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
|
||||
chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
|
||||
chargeItem.setPatientId(adviceSaveDto.getPatientId()); // 患者
|
||||
chargeItem.setContextEnum(adviceSaveDto.getAdviceType()); // 类型
|
||||
chargeItem.setEncounterId(adviceSaveDto.getEncounterId()); // 就诊id
|
||||
@@ -1733,21 +1652,6 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从诊疗医嘱 contentJson 中解析检验申请单号(检验保存时写入形如 {"applyNo":"..."})。
|
||||
*/
|
||||
private String extractInspectionApplyNoFromContentJson(String contentJson) {
|
||||
if (StringUtils.isBlank(contentJson) || !contentJson.contains("applyNo")) {
|
||||
return null;
|
||||
}
|
||||
Matcher m = INSPECTION_APPLY_NO_JSON.matcher(contentJson);
|
||||
if (!m.find()) {
|
||||
return null;
|
||||
}
|
||||
String applyNo = m.group(1).trim();
|
||||
return StringUtils.isBlank(applyNo) ? null : applyNo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理诊疗
|
||||
*/
|
||||
@@ -1796,49 +1700,13 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
}
|
||||
}
|
||||
}
|
||||
// 检验申请单在医嘱 contentJson 中写入 applyNo;从医嘱删除时需先级联作废检验单,避免检验页签仍显示孤儿申请
|
||||
Map<String, List<Long>> labApplyNoToRequestIds = new LinkedHashMap<>();
|
||||
for (AdviceSaveDto adviceSaveDto : deleteList) {
|
||||
Long requestId = adviceSaveDto.getRequestId();
|
||||
// 🔧 Bug #442: 跳过 requestId 为 null 的记录,避免删除不存在的诊疗请求
|
||||
if (requestId == null) {
|
||||
log.warn("BugFix#442: handService - 跳过 requestId 为 null 的删除请求");
|
||||
continue;
|
||||
}
|
||||
iServiceRequestService.removeById(requestId);// 删除诊疗
|
||||
ServiceRequest existing = iServiceRequestService.getById(adviceSaveDto.getRequestId());
|
||||
if (existing == null) {
|
||||
continue;
|
||||
}
|
||||
String applyNo = extractInspectionApplyNoFromContentJson(existing.getContentJson());
|
||||
if (StringUtils.isNotBlank(applyNo)) {
|
||||
labApplyNoToRequestIds.computeIfAbsent(applyNo, k -> new ArrayList<>())
|
||||
.add(adviceSaveDto.getRequestId());
|
||||
}
|
||||
}
|
||||
Set<Long> labCascadeSkippedRequestIds = new HashSet<>();
|
||||
for (Map.Entry<String, List<Long>> e : labApplyNoToRequestIds.entrySet()) {
|
||||
R<?> delLab = iDoctorStationInspectionLabApplyService.deleteInspectionLabApply(e.getKey());
|
||||
if (delLab != null && R.isSuccess(delLab)) {
|
||||
labCascadeSkippedRequestIds.addAll(e.getValue());
|
||||
log.info("handService - 级联作废检验申请单 applyNo={},已跳过重复删除的医嘱 requestIds={}",
|
||||
e.getKey(), e.getValue());
|
||||
} else {
|
||||
String msg = delLab != null && StringUtils.isNotEmpty(delLab.getMsg()) ? delLab.getMsg() : "删除检验申请单失败";
|
||||
log.warn("handService - 级联作废检验申请单未成功 applyNo={} msg={},将回退为仅删除当前医嘱记录",
|
||||
e.getKey(), msg);
|
||||
}
|
||||
}
|
||||
for (AdviceSaveDto adviceSaveDto : deleteList) {
|
||||
if (labCascadeSkippedRequestIds.contains(adviceSaveDto.getRequestId())) {
|
||||
continue;
|
||||
}
|
||||
Long requestId = adviceSaveDto.getRequestId();
|
||||
iServiceRequestService.removeById(requestId);// 删除诊疗
|
||||
iServiceRequestService.removeById(adviceSaveDto.getRequestId());// 删除诊疗
|
||||
iServiceRequestService.remove(
|
||||
new LambdaQueryWrapper<ServiceRequest>().eq(ServiceRequest::getParentId,
|
||||
requestId));// 删除诊疗套餐对应的子项
|
||||
adviceSaveDto.getRequestId()));// 删除诊疗套餐对应的子项
|
||||
// 🔧 Bug Fix #219: 删除费用项
|
||||
Long requestId = adviceSaveDto.getRequestId();
|
||||
String serviceTable = CommonConstants.TableName.WOR_SERVICE_REQUEST;
|
||||
// 直接删除费用项
|
||||
iChargeItemService.deleteByServiceTableAndId(serviceTable, requestId);
|
||||
@@ -1895,9 +1763,8 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
log.info("handService - 自动补全founderOrgId: founderOrgId={}", adviceSaveDto.getFounderOrgId());
|
||||
}
|
||||
|
||||
// 🔧 Bug Fix #238/#454: 诊疗项目执行科室非空校验(删除操作跳过校验)
|
||||
if (adviceSaveDto.getAdviceType() != null && adviceSaveDto.getAdviceType() == 3
|
||||
&& !DbOpType.DELETE.getCode().equals(adviceSaveDto.getDbOpType())) {
|
||||
// 🔧 Bug Fix #238: 诊疗项目执行科室非空校验
|
||||
if (adviceSaveDto.getAdviceType() != null && adviceSaveDto.getAdviceType() == 3) {
|
||||
Long effectiveOrgId = adviceSaveDto.getEffectiveOrgId();
|
||||
if (effectiveOrgId == null) {
|
||||
throw new ServiceException("诊疗项目必须选择执行科室");
|
||||
@@ -1918,10 +1785,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
if (is_save) {
|
||||
serviceRequest.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.SERVICE_RES_NO.getPrefix(), 4));
|
||||
}
|
||||
serviceRequest.setGenerateSourceEnum(adviceSaveDto.getGenerateSourceEnum() != null
|
||||
? adviceSaveDto.getGenerateSourceEnum()
|
||||
: GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
|
||||
serviceRequest.setPrescriptionNo(adviceSaveDto.getSourceBillNo()); // 来源业务单据号(手术单号)
|
||||
serviceRequest.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
|
||||
serviceRequest.setQuantity(adviceSaveDto.getQuantity()); // 请求数量
|
||||
serviceRequest.setUnitCode(adviceSaveDto.getUnitCode()); // 请求单位编码
|
||||
|
||||
@@ -1971,9 +1835,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
chargeItem.setCreateTime(curDate); // 补全创建时间
|
||||
chargeItem.setStatusEnum(2); // 已生成医嘱
|
||||
chargeItem.setBusNo(AssignSeqEnum.CHARGE_ITEM_NO.getPrefix().concat(serviceRequest.getBusNo()));
|
||||
chargeItem.setGenerateSourceEnum(adviceSaveDto.getGenerateSourceEnum() != null
|
||||
? adviceSaveDto.getGenerateSourceEnum()
|
||||
: GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
|
||||
chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
|
||||
chargeItem.setPatientId(adviceSaveDto.getPatientId()); // 患者
|
||||
chargeItem.setContextEnum(adviceSaveDto.getAdviceType()); // 类型
|
||||
chargeItem.setEncounterId(adviceSaveDto.getEncounterId()); // 就诊id
|
||||
@@ -2105,25 +1967,13 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
*/
|
||||
@Override
|
||||
public R<?> getRequestBaseInfo(Long encounterId) {
|
||||
return this.getRequestBaseInfo(encounterId, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> getRequestBaseInfo(Long encounterId, Integer generateSourceEnum, String sourceBillNo) {
|
||||
// 当前账号的参与者id
|
||||
Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
|
||||
// 未指定generateSourceEnum时,默认只查询医生开立的医嘱
|
||||
int sourceEnum = (generateSourceEnum != null) ? generateSourceEnum : GenerateSource.DOCTOR_PRESCRIPTION.getValue();
|
||||
// 医嘱请求数据
|
||||
List<RequestBaseDto> requestBaseInfo = doctorStationAdviceAppMapper.getRequestBaseInfo(encounterId, null,
|
||||
CommonConstants.TableName.MED_MEDICATION_REQUEST, CommonConstants.TableName.WOR_DEVICE_REQUEST,
|
||||
CommonConstants.TableName.WOR_SERVICE_REQUEST, practitionerId, Whether.NO.getCode(),
|
||||
sourceEnum, sourceBillNo);
|
||||
// 手术计费场景:sourceBillNo 不为空时,过滤掉药品(1),保留耗材(2)和诊疗(3/6)
|
||||
if (sourceBillNo != null && !sourceBillNo.isEmpty()) {
|
||||
requestBaseInfo.removeIf(dto -> dto.getAdviceType() != null
|
||||
&& dto.getAdviceType() == 1);
|
||||
}
|
||||
GenerateSource.DOCTOR_PRESCRIPTION.getValue());
|
||||
for (RequestBaseDto requestBaseDto : requestBaseInfo) {
|
||||
// 请求状态
|
||||
requestBaseDto
|
||||
@@ -2244,7 +2094,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
List<RequestBaseDto> requestBaseInfo = doctorStationAdviceAppMapper.getRequestBaseInfo(encounterId, patientId,
|
||||
CommonConstants.TableName.MED_MEDICATION_REQUEST, CommonConstants.TableName.WOR_DEVICE_REQUEST,
|
||||
CommonConstants.TableName.WOR_SERVICE_REQUEST, practitionerId, Whether.YES.getCode(),
|
||||
GenerateSource.DOCTOR_PRESCRIPTION.getValue(), null);
|
||||
GenerateSource.DOCTOR_PRESCRIPTION.getValue());
|
||||
for (RequestBaseDto requestBaseDto : requestBaseInfo) {
|
||||
// 请求状态
|
||||
requestBaseDto
|
||||
@@ -2456,30 +2306,4 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
return R.ok(categoryCodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手术项目专用分页查询(仅手术 + 定价,无库存/草稿库存/取药科室等无关逻辑)
|
||||
*/
|
||||
@Override
|
||||
public IPage<SurgeryItemDto> getSurgeryPage(Long organizationId, Integer pageNo, Integer pageSize, String searchKey) {
|
||||
log.info("getSurgeryPage 开始: orgId={}, page={}/{}, searchKey={}", organizationId, pageNo, pageSize, searchKey);
|
||||
long start = System.currentTimeMillis();
|
||||
IPage<SurgeryItemDto> result = doctorStationAdviceAppMapper.getSurgeryPage(
|
||||
new Page<>(pageNo, pageSize),
|
||||
PublicationStatus.ACTIVE.getValue(),
|
||||
organizationId,
|
||||
searchKey);
|
||||
log.info("getSurgeryPage 完成: {}ms, total={}, records={}", System.currentTimeMillis() - start, result.getTotal(), result.getRecords().size());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<SurgeryItemDto> getExaminationPage(Long organizationId, Integer pageNo, Integer pageSize, String searchKey) {
|
||||
IPage<SurgeryItemDto> result = doctorStationAdviceAppMapper.getExaminationPage(
|
||||
new Page<>(pageNo, pageSize),
|
||||
PublicationStatus.ACTIVE.getValue(),
|
||||
organizationId,
|
||||
searchKey);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,9 +36,6 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Date;
|
||||
import java.util.*;
|
||||
|
||||
@@ -264,10 +261,8 @@ public class DoctorStationDiagnosisAppServiceImpl implements IDoctorStationDiagn
|
||||
// 设置创建时间,避免数据库约束错误
|
||||
encounterDiagnosis.setCreateTime(new Date());
|
||||
iEncounterDiagnosisService.saveOrUpdate(encounterDiagnosis);
|
||||
// 回写就诊诊断ID,供前端后续更新使用
|
||||
saveDiagnosisChildParam.setEncounterDiagnosisId(encounterDiagnosis.getId());
|
||||
}
|
||||
return R.ok(saveDiagnosisParam, MessageUtils.createMessage(PromptMsgConstant.Common.M00002, new Object[] {"诊断"}));
|
||||
return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00002, new Object[] {"诊断"}));
|
||||
}
|
||||
|
||||
|
||||
@@ -585,11 +580,7 @@ public class DoctorStationDiagnosisAppServiceImpl implements IDoctorStationDiagn
|
||||
@Override
|
||||
public R<?> saveInfectiousDiseaseReport(InfectiousDiseaseReportDto infectiousDiseaseReportDto) {
|
||||
// 检查卡片编号唯一性(新增时检查,编辑时排除当前记录)
|
||||
String cardNo = infectiousDiseaseReportDto.getCardNo();
|
||||
if (cardNo == null || cardNo.trim().isEmpty()) {
|
||||
return R.fail("卡片编号不能为空");
|
||||
}
|
||||
cardNo = cardNo.trim();
|
||||
String cardNo = infectiousDiseaseReportDto.getCardNo().trim();
|
||||
LambdaQueryWrapper<InfectiousDiseaseReport> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(InfectiousDiseaseReport::getCardNo, cardNo);
|
||||
long count = iInfectiousDiseaseReportService.count(queryWrapper);
|
||||
@@ -601,25 +592,6 @@ public class DoctorStationDiagnosisAppServiceImpl implements IDoctorStationDiagn
|
||||
InfectiousDiseaseReport infectiousDiseaseReport = new InfectiousDiseaseReport();
|
||||
BeanUtils.copyProperties(infectiousDiseaseReportDto, infectiousDiseaseReport);
|
||||
|
||||
// BeanUtils.copyProperties 不支持 LocalDate/LocalDateTime 到 java.util.Date 的类型转换,需手动处理
|
||||
if (infectiousDiseaseReportDto.getOnsetDate() != null) {
|
||||
infectiousDiseaseReport.setOnsetDate(
|
||||
Date.from(infectiousDiseaseReportDto.getOnsetDate().atStartOfDay(ZoneId.systemDefault()).toInstant()));
|
||||
}
|
||||
if (infectiousDiseaseReportDto.getDiagDate() != null) {
|
||||
infectiousDiseaseReport.setDiagDate(
|
||||
Date.from(infectiousDiseaseReportDto.getDiagDate().atZone(ZoneId.systemDefault()).toInstant()));
|
||||
}
|
||||
// deathDate / reportDate 同理
|
||||
if (infectiousDiseaseReportDto.getDeathDate() != null) {
|
||||
infectiousDiseaseReport.setDeathDate(
|
||||
Date.from(infectiousDiseaseReportDto.getDeathDate().atStartOfDay(ZoneId.systemDefault()).toInstant()));
|
||||
}
|
||||
if (infectiousDiseaseReportDto.getReportDate() != null) {
|
||||
infectiousDiseaseReport.setReportDate(
|
||||
Date.from(infectiousDiseaseReportDto.getReportDate().atStartOfDay(ZoneId.systemDefault()).toInstant()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置创建人、删除状态、租户ID
|
||||
*/
|
||||
|
||||
@@ -274,8 +274,27 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
|
||||
return R.fail("非就诊中患者不能完诊");
|
||||
}
|
||||
|
||||
// 2. 查找队列项(限定当天,避免复诊患者匹配到历史队列记录)
|
||||
// 2. 获取 pool_id 和 slot_id:从 encounter → order_main → adm_schedule_slot 链路获取
|
||||
// 确保 div_log 中的值与排班主表一致,不依赖 triage_queue_item(队列项可能不存在或值错误)
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
Long divPoolId = null;
|
||||
Long divSlotId = null;
|
||||
if (encounter.getOrderId() != null) {
|
||||
try {
|
||||
Order order = iOrderService.getById(encounter.getOrderId());
|
||||
if (order != null && order.getSlotId() != null) {
|
||||
divSlotId = order.getSlotId();
|
||||
ScheduleSlot slot = scheduleSlotMapper.selectById(divSlotId);
|
||||
if (slot != null) {
|
||||
divPoolId = slot.getPoolId();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("获取完诊div_log的pool_id/slot_id失败,encounterId={}", encounterId, e);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 查找队列项(限定当天,避免复诊患者匹配到历史队列记录)
|
||||
TriageQueueItem queueItem = triageQueueItemService.getOne(
|
||||
new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
@@ -300,68 +319,33 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 获取 pool_id 和 slot_id:优先使用 triage_queue_item(挂号时录入的号源信息,为权威来源)
|
||||
// 队列项不存在或值缺失时,回退使用 encounter → order_main → adm_schedule_slot 链路
|
||||
Long divPoolId = null;
|
||||
Long divSlotId = null;
|
||||
if (queueItem != null && queueItem.getPoolId() != null && queueItem.getSlotId() != null) {
|
||||
divPoolId = queueItem.getPoolId();
|
||||
divSlotId = queueItem.getSlotId();
|
||||
}
|
||||
// 队列项 poolId/slotId 缺失时,通过 encounter.orderId → order_main.slot_id → adm_schedule_slot.pool_id 回退获取
|
||||
if ((divPoolId == null || divSlotId == null) && encounter.getOrderId() != null) {
|
||||
try {
|
||||
Order order = iOrderService.getById(encounter.getOrderId());
|
||||
if (order != null && order.getSlotId() != null) {
|
||||
ScheduleSlot slot = scheduleSlotMapper.selectById(order.getSlotId());
|
||||
if (slot != null) {
|
||||
divSlotId = slot.getId();
|
||||
divPoolId = slot.getPoolId();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("回退获取完诊div_log的pool_id/slot_id失败,encounterId={}", encounterId, e);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果队列项存在且未完成,更新队列状态为已完成
|
||||
// 使用排除法而非白名单:只要不是"已完成"就可以完诊,覆盖跳过、等待等非标准流转状态
|
||||
// Bug #401:在更新前记录队列原始完成状态,用于判断是否需要写入 div_log
|
||||
boolean queueWasAlreadyCompleted = queueItem != null
|
||||
&& TriageQueueStatus.COMPLETED.getValue().equals(queueItem.getStatus());
|
||||
if (queueItem != null &&
|
||||
!TriageQueueStatus.COMPLETED.getValue().equals(queueItem.getStatus())) {
|
||||
java.time.LocalDateTime nowLocal = java.time.LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS);
|
||||
// 使用 LambdaUpdateWrapper 直接更新,确保 status 字段必定写入数据库
|
||||
boolean queueUpdate = triageQueueItemService.update(new LambdaUpdateWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getId, queueItem.getId())
|
||||
.set(TriageQueueItem::getStatus, TriageQueueStatus.COMPLETED.getValue())
|
||||
.set(TriageQueueItem::getUpdateTime, nowLocal));
|
||||
if (!queueUpdate) {
|
||||
log.error("完诊:triage_queue_item 状态更新失败,queueItemId={}", queueItem.getId());
|
||||
}
|
||||
queueItem.setStatus(TriageQueueStatus.COMPLETED.getValue());
|
||||
queueItem.setUpdateTime(nowLocal);
|
||||
triageQueueItemService.updateById(queueItem);
|
||||
} else if (queueItem == null) {
|
||||
log.error("完诊:未找到任何 triage_queue_item 记录,encounterId={}, tenantId={}",
|
||||
encounterId, tenantId);
|
||||
}
|
||||
|
||||
// 写入 div_log 审计日志(独立于队列项,确保每次完诊都生成记录)
|
||||
// Bug #401:使用更新前记录的原始状态判断,避免自身更新后将状态改为 COMPLETED 导致误判为"已完成"
|
||||
if (!queueWasAlreadyCompleted) {
|
||||
try {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
DivLog divLog = new DivLog()
|
||||
.setPoolId(divPoolId)
|
||||
.setSlotId(divSlotId)
|
||||
.setOpUserId(loginUser != null ? loginUser.getUserId() : null)
|
||||
.setAction("COMPLETE")
|
||||
.setCreateTime(LocalDateTime.now())
|
||||
.setUpdateAt(LocalDateTime.now())
|
||||
.setCreatedAt(LocalDateTime.now());
|
||||
divLogService.save(divLog);
|
||||
} catch (Exception e) {
|
||||
log.error("写入div_log审计日志失败", e);
|
||||
}
|
||||
try {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
DivLog divLog = new DivLog()
|
||||
.setPoolId(divPoolId)
|
||||
.setSlotId(divSlotId)
|
||||
.setOpUserId(loginUser != null ? loginUser.getUserId() : null)
|
||||
.setAction("COMPLETE")
|
||||
.setCreateTime(LocalDateTime.now())
|
||||
.setUpdateAt(LocalDateTime.now())
|
||||
.setCreatedAt(LocalDateTime.now());
|
||||
divLogService.save(divLog);
|
||||
} catch (Exception e) {
|
||||
log.error("写入div_log审计日志失败", e);
|
||||
}
|
||||
|
||||
// 4. 更新状态、完成时间以及初复诊标识
|
||||
|
||||
@@ -112,16 +112,11 @@ public class DoctorStationAdviceController {
|
||||
* 查询医嘱请求数据
|
||||
*
|
||||
* @param encounterId 就诊id
|
||||
* @param generateSourceEnum 生成来源(可选,用于按来源过滤,如手术计费=6)
|
||||
* @param sourceBillNo 来源业务单据号(可选,用于按来源单据过滤)
|
||||
* @return 医嘱请求数据
|
||||
*/
|
||||
@GetMapping(value = "/request-base-info")
|
||||
public R<?> getRequestBaseInfo(
|
||||
@RequestParam(required = false) Long encounterId,
|
||||
@RequestParam(required = false) Integer generateSourceEnum,
|
||||
@RequestParam(required = false) String sourceBillNo) {
|
||||
return iDoctorStationAdviceAppService.getRequestBaseInfo(encounterId, generateSourceEnum, sourceBillNo);
|
||||
public R<?> getRequestBaseInfo(@RequestParam(required = false) Long encounterId) {
|
||||
return iDoctorStationAdviceAppService.getRequestBaseInfo(encounterId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -203,31 +198,4 @@ public class DoctorStationAdviceController {
|
||||
return iDoctorStationAdviceAppService.getConfiguredCategories(organizationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手术项目专用分页查询(仅手术 + 定价,无库存/草稿库存/取药科室等无关逻辑)
|
||||
*
|
||||
* @param organizationId 科室ID(可选)
|
||||
* @param pageNo 当前页
|
||||
* @param pageSize 每页条数
|
||||
* @param searchKey 模糊查询关键字(可选)
|
||||
* @return 手术项目分页数据(含价格信息)
|
||||
*/
|
||||
@GetMapping(value = "/surgery-page")
|
||||
public R<?> getSurgeryPage(
|
||||
@RequestParam(value = "organizationId", required = false) Long organizationId,
|
||||
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(value = "pageSize", defaultValue = "500") Integer pageSize,
|
||||
@RequestParam(value = "searchKey", defaultValue = "") String searchKey) {
|
||||
return R.ok(iDoctorStationAdviceAppService.getSurgeryPage(organizationId, pageNo, pageSize, searchKey));
|
||||
}
|
||||
|
||||
@GetMapping(value = "/examination-page")
|
||||
public R<?> getExaminationPage(
|
||||
@RequestParam(value = "organizationId", required = false) Long organizationId,
|
||||
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(value = "pageSize", defaultValue = "500") Integer pageSize,
|
||||
@RequestParam(value = "searchKey", defaultValue = "") String searchKey) {
|
||||
return R.ok(iDoctorStationAdviceAppService.getExaminationPage(organizationId, pageNo, pageSize, searchKey));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -96,9 +96,4 @@ public class DiagnosisQueryDto {
|
||||
*/
|
||||
private String diagnosisDoctor;
|
||||
|
||||
/**
|
||||
* 是否已有传染病报卡(0-无,1-有)
|
||||
*/
|
||||
private Integer hasInfectiousReport;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.openhis.web.doctorstation.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import lombok.Data;
|
||||
@@ -56,7 +55,6 @@ public class InfectiousDiseaseReportDto {
|
||||
private String sex;
|
||||
|
||||
/** 出生日期 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date birthday;
|
||||
|
||||
/** 实足年龄 */
|
||||
@@ -112,15 +110,12 @@ public class InfectiousDiseaseReportDto {
|
||||
private Integer caseClass;
|
||||
|
||||
/** 发病日期(默认诊断时间,病原携带者填初检日期) */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate onsetDate;
|
||||
|
||||
/** 诊断日期(精确到小时) */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
|
||||
private LocalDateTime diagDate;
|
||||
|
||||
/** 死亡日期(死亡病例必填) */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate deathDate;
|
||||
|
||||
/** 订正病名(订正报告必填) */
|
||||
@@ -139,7 +134,6 @@ public class InfectiousDiseaseReportDto {
|
||||
private String reportDoc;
|
||||
|
||||
/** 填卡日期 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate reportDate;
|
||||
|
||||
/** 报卡名称代码 1-中华人民共和国传染病报告卡 */
|
||||
@@ -164,4 +158,4 @@ public class InfectiousDiseaseReportDto {
|
||||
/** 医生ID */
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long doctorId;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package com.openhis.web.doctorstation.dto;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 手术项目选择器专用 DTO(不含 @Dict 注解,绕过 DictAspect 的 Redis 字典翻译)
|
||||
*/
|
||||
@Data
|
||||
public class SurgeryItemDto {
|
||||
|
||||
/** 医嘱定义ID */
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long adviceDefinitionId;
|
||||
|
||||
/** 手术名称 */
|
||||
private String adviceName;
|
||||
|
||||
/** 所属科室ID */
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long orgId;
|
||||
|
||||
/** 执行科室ID */
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long positionId;
|
||||
|
||||
/** 费用定价主表ID(用于提交时关联价格) */
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long chargeItemDefinitionId;
|
||||
|
||||
/** 单价(直接从定价主表取,无需嵌套 priceList) */
|
||||
private BigDecimal price;
|
||||
|
||||
/** 单位编码 */
|
||||
private String unitCode;
|
||||
|
||||
/** 单位编码字典文本(前端用于显示单位) */
|
||||
private String unitCodeDictText;
|
||||
}
|
||||
@@ -122,8 +122,7 @@ public interface DoctorStationAdviceAppMapper {
|
||||
@Param("MED_MEDICATION_REQUEST") String MED_MEDICATION_REQUEST,
|
||||
@Param("WOR_DEVICE_REQUEST") String WOR_DEVICE_REQUEST,
|
||||
@Param("WOR_SERVICE_REQUEST") String WOR_SERVICE_REQUEST, @Param("practitionerId") Long practitionerId,
|
||||
@Param("historyFlag") String historyFlag, @Param("generateSourceEnum") Integer generateSourceEnum,
|
||||
@Param("sourceBillNo") String sourceBillNo);
|
||||
@Param("historyFlag") String historyFlag, @Param("generateSourceEnum") Integer generateSourceEnum);
|
||||
|
||||
/**
|
||||
* 查询就诊费用性质
|
||||
@@ -185,23 +184,4 @@ public interface DoctorStationAdviceAppMapper {
|
||||
*/
|
||||
Long getDefaultAccountId(@Param("encounterId") Long encounterId);
|
||||
|
||||
/**
|
||||
* 手术项目专用分页查询(仅手术 + 定价,无库存/草稿库存/取药科室等无关逻辑)
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param statusEnum 启用状态
|
||||
* @param organizationId 科室ID(可选,用于过滤已配置的手术项目)
|
||||
* @param searchKey 模糊查询关键字(可选)
|
||||
* @return 手术项目分页数据
|
||||
*/
|
||||
IPage<SurgeryItemDto> getSurgeryPage(@Param("page") Page<SurgeryItemDto> page,
|
||||
@Param("statusEnum") Integer statusEnum,
|
||||
@Param("organizationId") Long organizationId,
|
||||
@Param("searchKey") String searchKey);
|
||||
|
||||
IPage<SurgeryItemDto> getExaminationPage(@Param("page") Page<SurgeryItemDto> page,
|
||||
@Param("statusEnum") Integer statusEnum,
|
||||
@Param("organizationId") Long organizationId,
|
||||
@Param("searchKey") String searchKey);
|
||||
|
||||
}
|
||||
|
||||
@@ -83,14 +83,9 @@ public class AdviceUtils {
|
||||
* @return 提示信息
|
||||
*/
|
||||
public String checkInventory(List<AdviceSaveDto> adviceSaveList) {
|
||||
// 医嘱定义ID集合(过滤null值,避免SQL查询异常)
|
||||
// 医嘱定义ID集合
|
||||
List<Long> adviceDefinitionIdList
|
||||
= adviceSaveList.stream().map(AdviceSaveDto::getAdviceDefinitionId)
|
||||
.filter(id -> id != null).collect(Collectors.toList());
|
||||
// 🔧 Bug #504 修复:如果所有adviceDefinitionId都为null,跳过库存校验
|
||||
if (adviceDefinitionIdList.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
= adviceSaveList.stream().map(AdviceSaveDto::getAdviceDefinitionId).collect(Collectors.toList());
|
||||
// 医嘱库存集合
|
||||
List<AdviceInventoryDto> adviceInventoryList
|
||||
= doctorStationAdviceAppMapper.getAdviceInventory(null, adviceDefinitionIdList,
|
||||
@@ -104,10 +99,6 @@ public class AdviceUtils {
|
||||
= this.subtractInventory(adviceInventoryList, adviceDraftInventoryList);
|
||||
// 检查库存
|
||||
for (AdviceSaveDto saveDto : adviceSaveList) {
|
||||
// 🔧 Bug #504 修复:退回医嘱可能adviceDefinitionId为空,跳过校验
|
||||
if (saveDto.getAdviceDefinitionId() == null) {
|
||||
continue;
|
||||
}
|
||||
boolean matched = false;
|
||||
for (AdviceInventoryDto inventoryDto : adviceInventory) {
|
||||
// 匹配条件:adviceDefinitionId, adviceTableName, locationId, lotNumber 同时相等
|
||||
@@ -117,12 +108,10 @@ public class AdviceUtils {
|
||||
|| saveDto.getLotNumber().equals(inventoryDto.getLotNumber());
|
||||
boolean tableNameMatch = StringUtils.isEmpty(saveDto.getAdviceTableName())
|
||||
|| inventoryDto.getItemTable().equals(saveDto.getAdviceTableName());
|
||||
// 🔧 Bug #504 修复:退回医嘱可能locationId为空,跳过location匹配
|
||||
boolean locationMatch = saveDto.getLocationId() == null
|
||||
|| inventoryDto.getLocationId().equals(saveDto.getLocationId());
|
||||
// if (saveDto.)
|
||||
if (inventoryDto.getItemId().equals(saveDto.getAdviceDefinitionId())
|
||||
&& tableNameMatch
|
||||
&& locationMatch && lotNumberMatch) {
|
||||
&& inventoryDto.getLocationId().equals(saveDto.getLocationId()) && lotNumberMatch) {
|
||||
matched = true;
|
||||
// 检查库存是否充足
|
||||
BigDecimal minUnitQuantity = saveDto.getMinUnitQuantity();
|
||||
@@ -178,37 +167,22 @@ public class AdviceUtils {
|
||||
// 生命提示信息集合
|
||||
List<String> tipsList = new ArrayList<>();
|
||||
for (MedicationRequestUseExe medicationRequestUseExe : medUseExeList) {
|
||||
// 第一步:按 performLocation 匹配指定药房的库存
|
||||
List<AdviceInventoryDto> matchedInventories = adviceInventory.stream()
|
||||
Optional<AdviceInventoryDto> matchedInventory = adviceInventory.stream()
|
||||
.filter(inventoryDto -> medicationRequestUseExe.getMedicationId().equals(inventoryDto.getItemId())
|
||||
&& CommonConstants.TableName.MED_MEDICATION_DEFINITION.equals(inventoryDto.getItemTable())
|
||||
&& (medicationRequestUseExe.getPerformLocation() == null
|
||||
|| medicationRequestUseExe.getPerformLocation().equals(inventoryDto.getLocationId()))
|
||||
&& medicationRequestUseExe.getPerformLocation().equals(inventoryDto.getLocationId())
|
||||
// 如果选择了具体的批次号,校验库存时需要加上批次号的匹配条件
|
||||
&& (StringUtils.isEmpty(medicationRequestUseExe.getLotNumber())
|
||||
|| medicationRequestUseExe.getLotNumber().equals(inventoryDto.getLotNumber())))
|
||||
.collect(Collectors.toList());
|
||||
// 第二步:如果指定药房没有匹配到库存,则放宽条件查询所有药房的库存
|
||||
if (matchedInventories.isEmpty()) {
|
||||
matchedInventories = adviceInventory.stream()
|
||||
.filter(inventoryDto -> medicationRequestUseExe.getMedicationId().equals(inventoryDto.getItemId())
|
||||
&& CommonConstants.TableName.MED_MEDICATION_DEFINITION.equals(inventoryDto.getItemTable())
|
||||
// 如果选择了具体的批次号,校验库存时需要加上批次号的匹配条件
|
||||
&& (StringUtils.isEmpty(medicationRequestUseExe.getLotNumber())
|
||||
|| medicationRequestUseExe.getLotNumber().equals(inventoryDto.getLotNumber())))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
.findFirst();
|
||||
// 匹配到库存信息
|
||||
if (!matchedInventories.isEmpty()) {
|
||||
// 聚合所有批次的可用库存
|
||||
BigDecimal totalQuantity = matchedInventories.stream()
|
||||
.map(dto -> dto.getQuantity() != null ? dto.getQuantity() : BigDecimal.ZERO)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
BigDecimal requestQuantity = medicationRequestUseExe.getExecuteTimesNum()
|
||||
.multiply(medicationRequestUseExe.getMinUnitQuantity());
|
||||
if (requestQuantity.compareTo(totalQuantity) > 0) {
|
||||
if (matchedInventory.isPresent()) {
|
||||
AdviceInventoryDto inventoryDto = matchedInventory.get();
|
||||
if ((medicationRequestUseExe.getExecuteTimesNum()
|
||||
.multiply(medicationRequestUseExe.getMinUnitQuantity()))
|
||||
.compareTo(inventoryDto.getQuantity()) > 0) {
|
||||
tipsList
|
||||
.add("【" + medicationRequestUseExe.getBusNo() + "】在" + matchedInventories.get(0).getLocationName() + "库存不足");
|
||||
.add("【" + medicationRequestUseExe.getBusNo() + "】在" + inventoryDto.getLocationName() + "库存不足");
|
||||
}
|
||||
} else {
|
||||
tipsList.add("【" + medicationRequestUseExe.getBusNo() + "】未匹配到库存信息");
|
||||
|
||||
@@ -178,7 +178,6 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
|
||||
inpatientAdviceParam.setEncounterIds(null);
|
||||
Integer exeStatus = inpatientAdviceParam.getExeStatus();
|
||||
inpatientAdviceParam.setExeStatus(null);
|
||||
// requestStatus由前端tab传入,通过QueryWrapper自动添加到SQL外层WHERE过滤
|
||||
// 构建查询条件
|
||||
QueryWrapper<InpatientAdviceParam> queryWrapper
|
||||
= HisQueryUtils.buildQueryWrapper(inpatientAdviceParam, null, null, null);
|
||||
@@ -207,8 +206,6 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
|
||||
e.setTherapyEnum_enumText(EnumUtils.getInfoByValue(TherapyTimeType.class, e.getTherapyEnum()));
|
||||
// 请求状态
|
||||
e.setRequestStatus_enumText(EnumUtils.getInfoByValue(RequestStatus.class, e.getRequestStatus()));
|
||||
// 发药状态
|
||||
e.setDispenseStatus_enumText(EnumUtils.getInfoByValue(DispenseStatus.class, e.getDispenseStatus()));
|
||||
// 性别枚举
|
||||
e.setGenderEnum_enumText(EnumUtils.getInfoByValue(AdministrativeGender.class, e.getGenderEnum()));
|
||||
// 计算年龄
|
||||
@@ -359,17 +356,6 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
|
||||
medRequestList.add(item);
|
||||
}
|
||||
}
|
||||
// 校验药品医嘱是否已发药,已发药的医嘱不允许退回
|
||||
if (!medRequestList.isEmpty()) {
|
||||
List<Long> medReqIds = medRequestList.stream().map(PerformInfoDto::getRequestId).toList();
|
||||
List<MedicationDispense> dispenseList = medicationDispenseService.list(
|
||||
new LambdaQueryWrapper<MedicationDispense>()
|
||||
.in(MedicationDispense::getMedReqId, medReqIds)
|
||||
.eq(MedicationDispense::getStatusEnum, DispenseStatus.COMPLETED.getValue()));
|
||||
if (!dispenseList.isEmpty()) {
|
||||
return R.fail("该药品已由药房发放,请先执行退药处理,不可直接退回");
|
||||
}
|
||||
}
|
||||
Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
|
||||
Date checkDate = new Date();
|
||||
if (!serviceRequestList.isEmpty()) {
|
||||
|
||||
@@ -245,7 +245,7 @@ public class InpatientAdviceDto {
|
||||
/**
|
||||
* 药品/服务类型
|
||||
*/
|
||||
private String categoryCode;
|
||||
private Integer categoryCode;
|
||||
/**
|
||||
* 执行科室
|
||||
*/
|
||||
@@ -255,8 +255,4 @@ public class InpatientAdviceDto {
|
||||
|
||||
/** 总价 */
|
||||
private BigDecimal totalPrice;
|
||||
|
||||
/** 发药状态 */
|
||||
private Integer dispenseStatus;
|
||||
private String dispenseStatus_enumText;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ public class NursingRecordController {
|
||||
* 获取住院患者信息 分页显示
|
||||
*
|
||||
* @param nursingSearchParam 查询参数
|
||||
*
|
||||
* @param searchKey 模糊查询
|
||||
* @param pageNo 当前页码
|
||||
* @param pageSize 查询条数
|
||||
|
||||
@@ -17,9 +17,6 @@ import com.openhis.common.constant.PromptMsgConstant;
|
||||
import com.openhis.common.enums.*;
|
||||
import com.openhis.common.utils.EnumUtils;
|
||||
import com.openhis.common.utils.HisQueryUtils;
|
||||
import com.core.common.utils.SecurityUtils;
|
||||
import com.openhis.workflow.domain.InventoryItem;
|
||||
import com.openhis.workflow.service.IInventoryItemService;
|
||||
import com.openhis.web.basedatamanage.dto.LocationDto;
|
||||
import com.openhis.web.common.dto.UnitDto;
|
||||
import com.openhis.web.inventorymanage.appservice.IProductTransferAppService;
|
||||
@@ -60,9 +57,6 @@ public class ProductTransferAppServiceImpl implements IProductTransferAppService
|
||||
@Autowired
|
||||
private IPractitionerService practitionerService;
|
||||
|
||||
@Autowired
|
||||
private IInventoryItemService inventoryItemService;
|
||||
|
||||
/**
|
||||
* 商品调拨页面初始化
|
||||
*
|
||||
@@ -202,23 +196,6 @@ public class ProductTransferAppServiceImpl implements IProductTransferAppService
|
||||
@Override
|
||||
public R<?> addOrEditBatchTransferReceipt(List<ProductTransferDto> productTransferDtoList, Boolean flag) {
|
||||
|
||||
// 校验调拨数量:必须 > 0 且不超过源库存数量(从数据库查实时库存)
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
for (ProductTransferDto dto : productTransferDtoList) {
|
||||
if (dto.getItemQuantity() == null || dto.getItemQuantity().compareTo(java.math.BigDecimal.ZERO) <= 0) {
|
||||
return R.fail("调拨数量必须大于0");
|
||||
}
|
||||
// 查询该药品在源仓库的实时库存总量
|
||||
List<InventoryItem> inventoryList = inventoryItemService.selectInventoryByItemId(
|
||||
dto.getItemId(), dto.getLotNumber(), dto.getSourceLocationId(), tenantId);
|
||||
java.math.BigDecimal actualStock = inventoryList.stream()
|
||||
.map(InventoryItem::getQuantity)
|
||||
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add);
|
||||
if (dto.getItemQuantity().compareTo(actualStock) > 0) {
|
||||
return R.fail("调拨数量不可超出源库存数量(当前库存:" + actualStock + ")");
|
||||
}
|
||||
}
|
||||
|
||||
List<String> idList = new ArrayList<>();
|
||||
if (flag) {
|
||||
// 批量保存按钮
|
||||
@@ -332,22 +309,6 @@ public class ProductTransferAppServiceImpl implements IProductTransferAppService
|
||||
@Override
|
||||
public R<?> addOrEditTransferReceipt(List<ProductTransferDto> productTransferDtoList) {
|
||||
|
||||
// 校验调拨数量:必须 > 0 且不超过源库存数量(从数据库查实时库存)
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
for (ProductTransferDto dto : productTransferDtoList) {
|
||||
if (dto.getItemQuantity() == null || dto.getItemQuantity().compareTo(java.math.BigDecimal.ZERO) <= 0) {
|
||||
return R.fail("调拨数量必须大于0");
|
||||
}
|
||||
List<InventoryItem> inventoryList = inventoryItemService.selectInventoryByItemId(
|
||||
dto.getItemId(), dto.getLotNumber(), dto.getSourceLocationId(), tenantId);
|
||||
java.math.BigDecimal actualStock = inventoryList.stream()
|
||||
.map(InventoryItem::getQuantity)
|
||||
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add);
|
||||
if (dto.getItemQuantity().compareTo(actualStock) > 0) {
|
||||
return R.fail("调拨数量不可超出源库存数量(当前库存:" + actualStock + ")");
|
||||
}
|
||||
}
|
||||
|
||||
List<String> idList = new ArrayList<>();
|
||||
|
||||
// 单据号取得
|
||||
@@ -419,25 +380,6 @@ public class ProductTransferAppServiceImpl implements IProductTransferAppService
|
||||
*/
|
||||
@Override
|
||||
public R<?> submitApproval(String busNo) {
|
||||
// 提交前再次校验调拨数量(从数据库查实时库存)
|
||||
List<SupplyRequest> requestList = supplyRequestService.getSupplyByBusNo(busNo);
|
||||
if (requestList != null && !requestList.isEmpty()) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
for (SupplyRequest request : requestList) {
|
||||
if (request.getItemQuantity() == null || request.getItemQuantity().compareTo(java.math.BigDecimal.ZERO) <= 0) {
|
||||
return R.fail("调拨数量必须大于0,请检查后重新保存");
|
||||
}
|
||||
// 查询该药品在源仓库的实时库存总量
|
||||
List<InventoryItem> inventoryList = inventoryItemService.selectInventoryByItemId(
|
||||
request.getItemId(), request.getLotNumber(), request.getSourceLocationId(), tenantId);
|
||||
java.math.BigDecimal actualStock = inventoryList.stream()
|
||||
.map(InventoryItem::getQuantity)
|
||||
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add);
|
||||
if (request.getItemQuantity().compareTo(actualStock) > 0) {
|
||||
return R.fail("调拨数量不可超出源库存数量(当前库存:" + actualStock + "),请检查后重新保存");
|
||||
}
|
||||
}
|
||||
}
|
||||
// 单据提交审核
|
||||
boolean result = supplyRequestService.submitApproval(busNo);
|
||||
return result ? R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, null))
|
||||
|
||||
@@ -3,38 +3,29 @@
|
||||
*/
|
||||
package com.openhis.web.inventorymanage.appservice.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.core.common.core.domain.R;
|
||||
import com.core.common.exception.ServiceException;
|
||||
import com.core.common.utils.*;
|
||||
import com.core.common.utils.bean.BeanUtils;
|
||||
import com.openhis.administration.domain.DeviceDefinition;
|
||||
import com.openhis.administration.domain.Practitioner;
|
||||
import com.openhis.administration.service.IDeviceDefinitionService;
|
||||
import com.openhis.administration.service.IPractitionerService;
|
||||
import com.openhis.common.constant.CommonConstants;
|
||||
import com.openhis.common.constant.PromptMsgConstant;
|
||||
import com.openhis.common.enums.*;
|
||||
import com.openhis.common.utils.EnumUtils;
|
||||
import com.openhis.common.utils.HisQueryUtils;
|
||||
import com.openhis.medication.domain.MedicationDefinition;
|
||||
import com.openhis.medication.service.IMedicationDefinitionService;
|
||||
import com.openhis.web.common.dto.UnitDto;
|
||||
import com.openhis.web.inventorymanage.appservice.IRequisitionIssueAppService;
|
||||
import com.openhis.web.inventorymanage.dto.*;
|
||||
import com.openhis.web.inventorymanage.mapper.RequisitionIssueMapper;
|
||||
import com.openhis.workflow.domain.InventoryItem;
|
||||
import com.openhis.workflow.domain.SupplyRequest;
|
||||
import com.openhis.workflow.service.IInventoryItemService;
|
||||
import com.openhis.workflow.service.ISupplyRequestService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -57,15 +48,6 @@ public class RequisitionIssueAppServiceImpl implements IRequisitionIssueAppServi
|
||||
@Autowired
|
||||
private IPractitionerService practitionerService;
|
||||
|
||||
@Autowired
|
||||
private IInventoryItemService inventoryItemService;
|
||||
|
||||
@Autowired
|
||||
private IMedicationDefinitionService medicationDefinitionService;
|
||||
|
||||
@Autowired
|
||||
private IDeviceDefinitionService deviceDefinitionService;
|
||||
|
||||
@Autowired
|
||||
private AssignSeqUtil assignSeqUtil;
|
||||
|
||||
@@ -185,10 +167,6 @@ public class RequisitionIssueAppServiceImpl implements IRequisitionIssueAppServi
|
||||
|
||||
// 单据号取得
|
||||
List<String> busNoList = requisitionIssueDtoList.stream().map(IssueDto::getBusNo).collect(Collectors.toList());
|
||||
|
||||
// 库存校验:领用数量不能超过源仓库实际库存
|
||||
this.validateRequisitionStock(requisitionIssueDtoList);
|
||||
|
||||
// 请求数据取得
|
||||
List<SupplyRequest> requestList = supplyRequestService.getSupplyByBusNo(busNoList.get(0));
|
||||
if (!requestList.isEmpty()) {
|
||||
@@ -350,73 +328,4 @@ public class RequisitionIssueAppServiceImpl implements IRequisitionIssueAppServi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验领用数量是否超过源仓库实际库存
|
||||
*
|
||||
* @param requisitionIssueDtoList 领用出库单据列表
|
||||
*/
|
||||
private void validateRequisitionStock(List<IssueDto> requisitionIssueDtoList) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
for (IssueDto issueDto : requisitionIssueDtoList) {
|
||||
Long itemId = issueDto.getItemId();
|
||||
String lotNumber = issueDto.getLotNumber();
|
||||
Long sourceLocationId = issueDto.getSourceLocationId();
|
||||
BigDecimal reqQuantity = issueDto.getItemQuantity();
|
||||
String itemUnit = issueDto.getUnitCode();
|
||||
String itemTable = issueDto.getItemTable();
|
||||
|
||||
// 根据物品类型查询定义信息(拆零比、常规单位、最小单位)
|
||||
BigDecimal partPercent = BigDecimal.ONE;
|
||||
String unitCode = itemUnit;
|
||||
String minUnitCode = itemUnit;
|
||||
|
||||
if (CommonConstants.TableName.MED_MEDICATION_DEFINITION.equals(itemTable)) {
|
||||
MedicationDefinition medDef = medicationDefinitionService.getById(itemId);
|
||||
if (medDef != null) {
|
||||
unitCode = medDef.getUnitCode();
|
||||
minUnitCode = medDef.getMinUnitCode();
|
||||
if (medDef.getPartPercent() != null) {
|
||||
partPercent = medDef.getPartPercent();
|
||||
}
|
||||
}
|
||||
} else if (CommonConstants.TableName.ADM_DEVICE_DEFINITION.equals(itemTable)) {
|
||||
DeviceDefinition devDef = deviceDefinitionService.getById(itemId);
|
||||
if (devDef != null) {
|
||||
unitCode = devDef.getUnitCode();
|
||||
minUnitCode = devDef.getMinUnitCode();
|
||||
if (devDef.getPartPercent() != null) {
|
||||
partPercent = devDef.getPartPercent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 计算领用数量折合最小单位的值
|
||||
BigDecimal reqQuantityInMinUnit;
|
||||
if (itemUnit.equals(unitCode)) {
|
||||
// 领用单位 = 包装单位,需乘以拆零比
|
||||
reqQuantityInMinUnit = reqQuantity.multiply(partPercent);
|
||||
} else {
|
||||
// 领用单位 = 最小单位,无需换算
|
||||
reqQuantityInMinUnit = reqQuantity;
|
||||
}
|
||||
|
||||
// 查询源仓库实际库存(按物品编号、批号、仓库匹配)
|
||||
List<InventoryItem> inventoryItemList = inventoryItemService.selectInventoryByItemId(
|
||||
itemId, lotNumber, sourceLocationId, tenantId);
|
||||
|
||||
// 累加匹配批号的总库存(库存表quantity字段为最小单位)
|
||||
BigDecimal totalStock = BigDecimal.ZERO;
|
||||
for (InventoryItem inventoryItem : inventoryItemList) {
|
||||
if (inventoryItem.getLocationId().equals(sourceLocationId)) {
|
||||
totalStock = totalStock.add(inventoryItem.getQuantity());
|
||||
}
|
||||
}
|
||||
|
||||
// 比较领用数量与库存
|
||||
if (reqQuantityInMinUnit.compareTo(totalStock) > 0) {
|
||||
throw new ServiceException("操作失败,库存数量不足");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1991,7 +1991,7 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
|
||||
Order appointmentOrder = iOrderService.getOne(
|
||||
new LambdaQueryWrapper<Order>()
|
||||
.eq(Order::getPatientId, encounterFormData.getPatientId())
|
||||
.eq(Order::getStatus, OrderStatus.ACTIVE.getValue()) // 有效订单(1)
|
||||
.eq(Order::getStatus, CommonConstants.AppointmentOrderStatus.CHECKED_IN)
|
||||
.eq(Order::getDeleteFlag, "0")
|
||||
.orderByDesc(Order::getCreateTime)
|
||||
.last("LIMIT 1")
|
||||
@@ -2114,11 +2114,11 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
|
||||
Long queuePoolId = null;
|
||||
Long queueSlotId = null;
|
||||
try {
|
||||
// 查询患者当天有效订单(1);已取消(0/2)和已完成(3)的不参与排队
|
||||
// 查询患者当天的待签到预约订单(status = 1 或 2 表示已预约或已取号)
|
||||
Order order = iOrderService.getOne(
|
||||
new LambdaQueryWrapper<Order>()
|
||||
.eq(Order::getPatientId, encounter.getPatientId())
|
||||
.eq(Order::getStatus, OrderStatus.ACTIVE.getValue()) // 有效(1)
|
||||
.in(Order::getStatus, 1, 2) // 1=BOOKED 已预约, 2=CHECKED_IN 已取号
|
||||
.eq(Order::getDeleteFlag, "0")
|
||||
.orderByDesc(Order::getCreateTime)
|
||||
.last("LIMIT 1")
|
||||
|
||||
@@ -39,22 +39,10 @@ public interface IRequestFormManageAppService {
|
||||
* @param typeCode 申请单类型
|
||||
* @param startDate 开始日期(可选,格式:yyyy-MM-dd)
|
||||
* @param endDate 结束日期(可选,格式:yyyy-MM-dd)
|
||||
* @return 申请单列表
|
||||
*/
|
||||
List<RequestFormQueryDto> getRequestForm(Long encounterId, String typeCode, String startDate, String endDate);
|
||||
|
||||
/**
|
||||
* 查询申请单(支持筛选+状态+关键字)
|
||||
*
|
||||
* @param encounterId 就诊id
|
||||
* @param typeCode 申请单类型
|
||||
* @param startDate 开始日期(可选,格式:yyyy-MM-dd)
|
||||
* @param endDate 结束日期(可选,格式:yyyy-MM-dd)
|
||||
* @param status 单据状态(可选)
|
||||
* @param keyword 关键字(可选,申请单号/项目名称模糊匹配)
|
||||
* @return 申请单列表
|
||||
*/
|
||||
List<RequestFormQueryDto> getRequestForm(Long encounterId, String typeCode, String startDate, String endDate, String status, String keyword);
|
||||
List<RequestFormQueryDto> getRequestForm(Long encounterId, String typeCode, String startDate, String endDate, String status);
|
||||
|
||||
/**
|
||||
* 分页查询申请单
|
||||
@@ -63,20 +51,4 @@ public interface IRequestFormManageAppService {
|
||||
* @return 申请单
|
||||
*/
|
||||
IPage<RequestFormPageDto> getRequestFormPage(RequestFormDto requestFormDto);
|
||||
|
||||
/**
|
||||
* 删除申请单(仅待签发状态可删除)
|
||||
*
|
||||
* @param requestFormId 申请单ID
|
||||
* @return 结果
|
||||
*/
|
||||
R<?> deleteRequestForm(Long requestFormId);
|
||||
|
||||
/**
|
||||
* 撤回申请单(已签发状态撤回至待签发)
|
||||
*
|
||||
* @param requestFormId 申请单ID
|
||||
* @return 结果
|
||||
*/
|
||||
R<?> withdrawRequestForm(Long requestFormId);
|
||||
}
|
||||
|
||||
@@ -341,28 +341,6 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
&& (DbOpType.INSERT.getCode().equals(e.getDbOpType()) || DbOpType.UPDATE.getCode().equals(e.getDbOpType())))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 防重复保存:对所有医嘱进行去重(包括 INSERT 和 UPDATE 混合场景),避免签发单条医嘱时产生重复记录
|
||||
Set<String> longUniqueKeySet = new HashSet<>();
|
||||
List<RegAdviceSaveDto> longUniqueList = new ArrayList<>();
|
||||
for (RegAdviceSaveDto adviceSaveDto : longInsertOrUpdateList) {
|
||||
String uniqueKey = adviceSaveDto.getPatientId() + "_"
|
||||
+ adviceSaveDto.getEncounterId() + "_"
|
||||
+ adviceSaveDto.getAdviceDefinitionId() + "_"
|
||||
+ adviceSaveDto.getDose() + "_"
|
||||
+ adviceSaveDto.getMethodCode() + "_"
|
||||
+ adviceSaveDto.getRateCode();
|
||||
if (longUniqueKeySet.contains(uniqueKey)) {
|
||||
log.warn("防重复保存:检测到重复长期医嘱(跨操作类型),跳过 - patientId={}, encounterId={}, adviceDefinitionId={}, dbOpType={}",
|
||||
adviceSaveDto.getPatientId(), adviceSaveDto.getEncounterId(),
|
||||
adviceSaveDto.getAdviceDefinitionId(), adviceSaveDto.getDbOpType());
|
||||
continue;
|
||||
}
|
||||
longUniqueKeySet.add(uniqueKey);
|
||||
longUniqueList.add(adviceSaveDto);
|
||||
}
|
||||
log.info("防重复保存(长期):去重前{}条,去重后{}条", longInsertOrUpdateList.size(), longUniqueList.size());
|
||||
longInsertOrUpdateList = longUniqueList;
|
||||
|
||||
for (RegAdviceSaveDto regAdviceSaveDto : longInsertOrUpdateList) {
|
||||
boolean firstTimeSave = false;// 第一次保存
|
||||
longMedicationRequest = new MedicationRequest();
|
||||
@@ -428,29 +406,6 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
.getValue().equals(e.getTherapyEnum())
|
||||
&& (DbOpType.INSERT.getCode().equals(e.getDbOpType()) || DbOpType.UPDATE.getCode().equals(e.getDbOpType())))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 防重复保存:对所有医嘱进行去重(包括 INSERT 和 UPDATE 混合场景),避免签发时产生重复记录
|
||||
Set<String> tempUniqueKeySet = new HashSet<>();
|
||||
List<RegAdviceSaveDto> tempUniqueList = new ArrayList<>();
|
||||
for (RegAdviceSaveDto adviceSaveDto : tempInsertOrUpdateList) {
|
||||
String uniqueKey = adviceSaveDto.getPatientId() + "_"
|
||||
+ adviceSaveDto.getEncounterId() + "_"
|
||||
+ adviceSaveDto.getAdviceDefinitionId() + "_"
|
||||
+ adviceSaveDto.getDose() + "_"
|
||||
+ adviceSaveDto.getMethodCode() + "_"
|
||||
+ adviceSaveDto.getRateCode();
|
||||
if (tempUniqueKeySet.contains(uniqueKey)) {
|
||||
log.warn("防重复保存:检测到重复临时医嘱(跨操作类型),跳过 - patientId={}, encounterId={}, adviceDefinitionId={}, dbOpType={}",
|
||||
adviceSaveDto.getPatientId(), adviceSaveDto.getEncounterId(),
|
||||
adviceSaveDto.getAdviceDefinitionId(), adviceSaveDto.getDbOpType());
|
||||
continue;
|
||||
}
|
||||
tempUniqueKeySet.add(uniqueKey);
|
||||
tempUniqueList.add(adviceSaveDto);
|
||||
}
|
||||
log.info("防重复保存(临时):去重前{}条,去重后{}条", tempInsertOrUpdateList.size(), tempUniqueList.size());
|
||||
tempInsertOrUpdateList = tempUniqueList;
|
||||
|
||||
for (RegAdviceSaveDto regAdviceSaveDto : tempInsertOrUpdateList) {
|
||||
boolean firstTimeSave = false;// 第一次保存
|
||||
tempMedicationRequest = new MedicationRequest();
|
||||
@@ -556,9 +511,6 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
// 签发操作
|
||||
boolean is_sign = AdviceOpType.SIGN_ADVICE.getCode().equals(adviceOpType);
|
||||
|
||||
// 收集已处理的requestId,用于批量更新状态
|
||||
List<Long> processedRequestIds = new ArrayList<>();
|
||||
|
||||
// 声明长期医嘱诊疗请求
|
||||
ServiceRequest longServiceRequest;
|
||||
// 新增 + 修改 (长期医嘱)
|
||||
@@ -603,9 +555,6 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
}
|
||||
}
|
||||
iServiceRequestService.saveOrUpdate(longServiceRequest);
|
||||
if (longServiceRequest.getId() != null) {
|
||||
processedRequestIds.add(longServiceRequest.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// 声明临时医嘱诊疗请求
|
||||
@@ -654,9 +603,6 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
}
|
||||
}
|
||||
iServiceRequestService.saveOrUpdate(tempServiceRequest);
|
||||
if (tempServiceRequest.getId() != null) {
|
||||
processedRequestIds.add(tempServiceRequest.getId());
|
||||
}
|
||||
|
||||
// 保存时,保存诊疗费用项
|
||||
if (is_save) {
|
||||
@@ -708,24 +654,6 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
}
|
||||
}
|
||||
|
||||
// 批量更新诊疗医嘱状态(使用 update 确保状态字段必定更新)
|
||||
if (!processedRequestIds.isEmpty()) {
|
||||
// 🔧 Bug #487 修复:签发时额外设置 authoredTime,确保签发时间被记录
|
||||
if (is_sign) {
|
||||
iServiceRequestService.update(null,
|
||||
new LambdaUpdateWrapper<ServiceRequest>()
|
||||
.set(ServiceRequest::getStatusEnum, RequestStatus.ACTIVE.getValue())
|
||||
.set(ServiceRequest::getAuthoredTime, authoredTime)
|
||||
.set(ServiceRequest::getSignCode, signCode)
|
||||
.in(ServiceRequest::getId, processedRequestIds));
|
||||
log.info("签发诊疗医嘱成功,requestIds: {}, signCode: {}", processedRequestIds, signCode);
|
||||
} else {
|
||||
iServiceRequestService.update(null,
|
||||
new LambdaUpdateWrapper<ServiceRequest>()
|
||||
.set(ServiceRequest::getStatusEnum, RequestStatus.DRAFT.getValue())
|
||||
.in(ServiceRequest::getId, processedRequestIds));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -761,8 +689,6 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
if (is_sign) {
|
||||
deviceRequest.setReqAuthoredTime(authoredTime); // 医嘱签发时间
|
||||
}
|
||||
// 保存或签发时都需要设置耗材定义ID(防止 sign 分支 deviceDefId 为空触发 NOT NULL 约束)
|
||||
deviceRequest.setDeviceDefId(regAdviceSaveDto.getAdviceDefinitionId());
|
||||
// 保存时处理的字段属性
|
||||
if (is_save) {
|
||||
deviceRequest.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.DEVICE_RES_NO.getPrefix(), 4));
|
||||
@@ -800,8 +726,6 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
if (is_sign) {
|
||||
deviceRequest.setReqAuthoredTime(authoredTime); // 医嘱签发时间
|
||||
}
|
||||
// 保存或签发时都需要设置耗材定义ID(防止 sign 分支 deviceDefId 为空触发 NOT NULL 约束)
|
||||
deviceRequest.setDeviceDefId(regAdviceSaveDto.getAdviceDefinitionId());
|
||||
// 保存时处理的字段属性
|
||||
if (is_save) {
|
||||
deviceRequest.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.DEVICE_RES_NO.getPrefix(), 4));
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.openhis.web.regdoctorstation.appservice.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.core.common.core.domain.R;
|
||||
@@ -32,7 +31,6 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -77,46 +75,12 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> saveRequestForm(RequestFormSaveDto requestFormSaveDto, String typeCode) {
|
||||
// 申请单ID(前端空字符串可能反序列化为0L,需同时判0)
|
||||
Long requestFormId = requestFormSaveDto.getRequestFormId();
|
||||
boolean isEdit = requestFormId != null && requestFormId != 0L;
|
||||
|
||||
// 诊疗执行科室配置校验(必须在任何数据库操作之前)
|
||||
List<ActivityOrganizationConfigDto> activityOrganizationConfig =
|
||||
requestFormManageAppMapper.getActivityOrganizationConfig(typeCode);
|
||||
if (activityOrganizationConfig.isEmpty()) {
|
||||
throw new ServiceException("请先配置当前时间段的执行科室");
|
||||
}
|
||||
|
||||
// 逐个校验activityList中的项目是否都配置了执行科室,并收集positionId供后续使用
|
||||
// 必须在任何数据库操作之前完成全部校验,避免部分保存后异常导致脏数据
|
||||
// 🔧 Bug #516: 优先使用前端传入的positionId(用户手动选择的发往科室),仅在未选择时使用配置的执行科室
|
||||
List<ActivitySaveDto> activityList = requestFormSaveDto.getActivityList();
|
||||
// 缓存校验结果,避免主循环中重复查询和可能出现的数据不一致
|
||||
java.util.Map<Long, Long> activityIdToPositionIdMap = new java.util.HashMap<>();
|
||||
if (activityList != null && !activityList.isEmpty()) {
|
||||
for (ActivitySaveDto activitySaveDto : activityList) {
|
||||
// 优先使用前端传入的positionId(用户手动选择的科室)
|
||||
Long frontendPositionId = activitySaveDto.getPositionId();
|
||||
if (frontendPositionId != null) {
|
||||
activityIdToPositionIdMap.put(activitySaveDto.getAdviceDefinitionId(), frontendPositionId);
|
||||
continue;
|
||||
}
|
||||
// 前端未传入时,使用配置的执行科室
|
||||
Long configPositionId = activityOrganizationConfig.stream()
|
||||
.filter(dto -> activitySaveDto.getAdviceDefinitionId().equals(dto.getActivityDefinitionId()))
|
||||
.map(ActivityOrganizationConfigDto::getOrganizationId).findFirst().orElse(null);
|
||||
if (configPositionId == null) {
|
||||
throw new ServiceException(activitySaveDto.getAdviceDefinitionName() + "未配置当前时间段的执行科室");
|
||||
}
|
||||
activityIdToPositionIdMap.put(activitySaveDto.getAdviceDefinitionId(), configPositionId);
|
||||
}
|
||||
}
|
||||
|
||||
// 诊疗处方号
|
||||
String prescriptionNo;
|
||||
// 申请单ID
|
||||
Long requestFormId = requestFormSaveDto.getRequestFormId();
|
||||
// 编辑场景
|
||||
if (isEdit) {
|
||||
if (requestFormId != null) {
|
||||
RequestForm requestFormInfo = iRequestFormService.getById(requestFormId);
|
||||
prescriptionNo = requestFormInfo.getPrescriptionNo();
|
||||
// 该申请单存在的待发送医嘱个数
|
||||
@@ -127,10 +91,8 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
return R.fail("无待签发的医嘱,该申请单不可编辑");
|
||||
}
|
||||
} else {
|
||||
// 检查申请单号:JC(检查)+ Z(住院标识)+ yyMMdd(日期)+ 5位顺序号
|
||||
String dateStr = new java.text.SimpleDateFormat("yyMMdd").format(new Date());
|
||||
int seq = assignSeqUtil.getSeqNoByDay(AssignSeqEnum.CHECK_APPLY_NO.getPrefix());
|
||||
prescriptionNo = "JCZ" + dateStr + String.format("%05d", seq);
|
||||
// 诊疗处方号
|
||||
prescriptionNo = assignSeqUtil.getSeq(AssignSeqEnum.ACTIVITY_PSYCHOTROPIC_NO.getPrefix(), 8);
|
||||
}
|
||||
|
||||
// 当前时间
|
||||
@@ -160,7 +122,7 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
iRequestFormService.saveOrUpdate(requestForm);
|
||||
|
||||
// 编辑场景时,先删除掉原有诊疗项目及账单再新增
|
||||
if (isEdit) {
|
||||
if (requestFormId != null) {
|
||||
List<Long> serviceRequestIds = iServiceRequestService
|
||||
.list(new LambdaQueryWrapper<ServiceRequest>().eq(ServiceRequest::getPrescriptionNo, prescriptionNo))
|
||||
.stream().map(ServiceRequest::getId).collect(Collectors.toList());
|
||||
@@ -174,7 +136,15 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
|
||||
ServiceRequest serviceRequest;
|
||||
ChargeItem chargeItem;
|
||||
// 诊疗集合
|
||||
List<ActivitySaveDto> activityList = requestFormSaveDto.getActivityList();
|
||||
log.info("保存申请单,typeCode={}, activityListSize={}, encounterId={}", typeCode, activityList != null ? activityList.size() : 0, encounterId);
|
||||
// 诊疗执行科室配置
|
||||
List<ActivityOrganizationConfigDto> activityOrganizationConfig =
|
||||
requestFormManageAppMapper.getActivityOrganizationConfig(typeCode);
|
||||
if (activityOrganizationConfig.isEmpty()) {
|
||||
throw new ServiceException("请先配置当前时间段的执行科室");
|
||||
}
|
||||
|
||||
for (ActivitySaveDto activitySaveDto : activityList) {
|
||||
serviceRequest = new ServiceRequest();
|
||||
@@ -192,7 +162,9 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
serviceRequest.setEncounterId(encounterId); // 就诊id
|
||||
serviceRequest.setAuthoredTime(curDate); // 请求签发时间
|
||||
|
||||
Long positionId = activityIdToPositionIdMap.get(activitySaveDto.getAdviceDefinitionId());
|
||||
Long positionId = activityOrganizationConfig.stream()
|
||||
.filter(dto -> activitySaveDto.getAdviceDefinitionId().equals(dto.getActivityDefinitionId()))
|
||||
.map(ActivityOrganizationConfigDto::getOrganizationId).findFirst().orElse(null);
|
||||
if (positionId == null) {
|
||||
throw new ServiceException(activitySaveDto.getAdviceDefinitionName() + "未配置当前时间段的执行科室");
|
||||
}
|
||||
@@ -303,7 +275,7 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
surgeryServiceRequest.setTherapyEnum(TherapyTimeType.TEMPORARY.getValue());
|
||||
surgeryServiceRequest.setQuantity(BigDecimal.valueOf(1));
|
||||
surgeryServiceRequest.setUnitCode("次");
|
||||
surgeryServiceRequest.setCategoryEnum(24); // 24-手术(新值域,避开 adviceType 碰撞)
|
||||
surgeryServiceRequest.setCategoryEnum(4); // 4-手术
|
||||
// 优先从 activityList 获取手术 ID
|
||||
if (activityList != null && !activityList.isEmpty()) {
|
||||
Long activityId = activityList.get(0).getAdviceDefinitionId();
|
||||
@@ -442,7 +414,7 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
@Override
|
||||
public List<RequestFormQueryDto> getRequestForm(Long encounterId, String typeCode) {
|
||||
// 调用重载方法,不传筛选参数
|
||||
return getRequestForm(encounterId, typeCode, null, null);
|
||||
return getRequestForm(encounterId, typeCode, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -452,32 +424,17 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
* @param typeCode 申请单类型
|
||||
* @param startDate 开始日期(可选,格式:yyyy-MM-dd)
|
||||
* @param endDate 结束日期(可选,格式:yyyy-MM-dd)
|
||||
* @return 申请单列表
|
||||
*/
|
||||
@Override
|
||||
public List<RequestFormQueryDto> getRequestForm(Long encounterId, String typeCode, String startDate, String endDate) {
|
||||
return getRequestForm(encounterId, typeCode, startDate, endDate, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询申请单(支持筛选+状态+关键字)
|
||||
*
|
||||
* @param encounterId 就诊id
|
||||
* @param typeCode 申请单类型
|
||||
* @param startDate 开始日期(可选,格式:yyyy-MM-dd)
|
||||
* @param endDate 结束日期(可选,格式:yyyy-MM-dd)
|
||||
* @param status 单据状态(可选)
|
||||
* @param keyword 关键字(可选,申请单号/项目名称模糊匹配)
|
||||
* @return 申请单列表
|
||||
*/
|
||||
@Override
|
||||
public List<RequestFormQueryDto> getRequestForm(Long encounterId, String typeCode, String startDate, String endDate, String status, String keyword) {
|
||||
public List<RequestFormQueryDto> getRequestForm(Long encounterId, String typeCode, String startDate, String endDate, String status) {
|
||||
// 检查参数
|
||||
if (encounterId == null) {
|
||||
return new java.util.ArrayList<>();
|
||||
return new java.util.ArrayList<>(); // 返回空列表而不是查询数据库
|
||||
}
|
||||
|
||||
List<RequestFormQueryDto> requestFormList = requestFormManageAppMapper.getRequestForm(encounterId, typeCode, startDate, endDate, status, keyword);
|
||||
List<RequestFormQueryDto> requestFormList = requestFormManageAppMapper.getRequestForm(encounterId, typeCode, startDate, endDate, status);
|
||||
for (RequestFormQueryDto requestFormQueryDto : requestFormList) {
|
||||
// 查询处方详情
|
||||
List<RequestFormDetailQueryDto> requestFormDetail =
|
||||
@@ -499,90 +456,4 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
return requestFormManageAppMapper.getRequestFormPage(requestFormDto, page);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> deleteRequestForm(Long requestFormId) {
|
||||
if (requestFormId == null) {
|
||||
return R.fail("申请单ID不能为空");
|
||||
}
|
||||
RequestForm requestForm = iRequestFormService.getById(requestFormId);
|
||||
if (requestForm == null) {
|
||||
return R.fail("申请单不存在");
|
||||
}
|
||||
String prescriptionNo = requestForm.getPrescriptionNo();
|
||||
|
||||
// 查询该申请单下所有 ServiceRequest(含子项)
|
||||
List<ServiceRequest> serviceRequests = iServiceRequestService.list(
|
||||
new LambdaQueryWrapper<ServiceRequest>()
|
||||
.eq(ServiceRequest::getPrescriptionNo, prescriptionNo));
|
||||
if (serviceRequests == null || serviceRequests.isEmpty()) {
|
||||
return R.fail("未找到关联的诊疗医嘱");
|
||||
}
|
||||
|
||||
// 校验:只有待签发(status=0)的申请单可删除
|
||||
boolean allDraft = serviceRequests.stream()
|
||||
.allMatch(sr -> RequestStatus.DRAFT.getValue().equals(sr.getStatusEnum()));
|
||||
if (!allDraft) {
|
||||
return R.fail("只有待签发状态的申请单可删除");
|
||||
}
|
||||
|
||||
List<Long> serviceRequestIds = serviceRequests.stream()
|
||||
.map(ServiceRequest::getId).collect(Collectors.toList());
|
||||
|
||||
// 1. 删除关联的费用项
|
||||
for (Long srId : serviceRequestIds) {
|
||||
iChargeItemService.deleteByServiceTableAndId(
|
||||
CommonConstants.TableName.WOR_SERVICE_REQUEST, srId);
|
||||
}
|
||||
// 2. 删除子项 ServiceRequest(parentId 非空)
|
||||
iServiceRequestService.remove(
|
||||
new LambdaQueryWrapper<ServiceRequest>()
|
||||
.in(ServiceRequest::getId, serviceRequestIds)
|
||||
.isNotNull(ServiceRequest::getParentId));
|
||||
// 3. 删除主项 ServiceRequest
|
||||
iServiceRequestService.removeByIds(serviceRequestIds);
|
||||
// 4. 删除申请单
|
||||
iRequestFormService.removeById(requestFormId);
|
||||
|
||||
log.info("检查申请单删除成功,requestFormId={}, prescriptionNo={}", requestFormId, prescriptionNo);
|
||||
return R.ok("删除成功");
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> withdrawRequestForm(Long requestFormId) {
|
||||
if (requestFormId == null) {
|
||||
return R.fail("申请单ID不能为空");
|
||||
}
|
||||
RequestForm requestForm = iRequestFormService.getById(requestFormId);
|
||||
if (requestForm == null) {
|
||||
return R.fail("申请单不存在");
|
||||
}
|
||||
String prescriptionNo = requestForm.getPrescriptionNo();
|
||||
|
||||
// 查询该申请单下所有 ServiceRequest
|
||||
List<ServiceRequest> serviceRequests = iServiceRequestService.list(
|
||||
new LambdaQueryWrapper<ServiceRequest>()
|
||||
.eq(ServiceRequest::getPrescriptionNo, prescriptionNo));
|
||||
if (serviceRequests == null || serviceRequests.isEmpty()) {
|
||||
return R.fail("未找到关联的诊疗医嘱");
|
||||
}
|
||||
|
||||
// 校验:只有已签发(status=2)的申请单可撤回
|
||||
boolean allActive = serviceRequests.stream()
|
||||
.allMatch(sr -> RequestStatus.ACTIVE.getValue().equals(sr.getStatusEnum()));
|
||||
if (!allActive) {
|
||||
return R.fail("只有已签发状态的申请单可撤回");
|
||||
}
|
||||
|
||||
// 将所有 ServiceRequest 状态改回待签发(DRAFT=0)
|
||||
List<Long> serviceRequestIds = serviceRequests.stream()
|
||||
.map(ServiceRequest::getId).collect(Collectors.toList());
|
||||
iServiceRequestService.update(
|
||||
new ServiceRequest().setStatusEnum(RequestStatus.DRAFT.getValue()),
|
||||
new LambdaUpdateWrapper<ServiceRequest>()
|
||||
.in(ServiceRequest::getId, serviceRequestIds));
|
||||
|
||||
log.info("检查申请单撤回成功,requestFormId={}, prescriptionNo={}", requestFormId, prescriptionNo);
|
||||
return R.ok("撤回成功");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,9 +16,7 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 申请单管理 controller
|
||||
@@ -83,23 +81,14 @@ public class RequestFormManageController {
|
||||
* 查询检查申请单
|
||||
*
|
||||
* @param encounterId 就诊id
|
||||
* @param startDate 开始日期(可选,格式:yyyy-MM-dd)
|
||||
* @param endDate 结束日期(可选,格式:yyyy-MM-dd)
|
||||
* @param status 单据状态(可选)
|
||||
* @param keyword 关键字(可选,申请单号/检查项目名称模糊匹配)
|
||||
* @return 检查申请单
|
||||
*/
|
||||
@GetMapping(value = "/get-check")
|
||||
public R<?> getCheckRequestForm(
|
||||
@RequestParam(required = false) Long encounterId,
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) String keyword) {
|
||||
public R<?> getCheckRequestForm(@RequestParam(required = false) Long encounterId) {
|
||||
if (encounterId == null) {
|
||||
return R.fail("就诊ID不能为空");
|
||||
}
|
||||
return R.ok(iRequestFormManageAppService.getRequestForm(encounterId, ActivityDefCategory.TEST.getCode(), startDate, endDate, status, keyword));
|
||||
return R.ok(iRequestFormManageAppService.getRequestForm(encounterId, ActivityDefCategory.TEST.getCode()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,7 +98,6 @@ public class RequestFormManageController {
|
||||
* @param startDate 开始日期(可选,格式:yyyy-MM-dd)
|
||||
* @param endDate 结束日期(可选,格式:yyyy-MM-dd)
|
||||
* @param status 单据状态(可选)
|
||||
* @param keyword 关键字(可选,申请单号/检验项目模糊匹配)
|
||||
* @return 检验申请单
|
||||
*/
|
||||
@GetMapping(value = "/get-inspection")
|
||||
@@ -117,12 +105,11 @@ public class RequestFormManageController {
|
||||
@RequestParam(required = false) Long encounterId,
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) String keyword) {
|
||||
@RequestParam(required = false) String status) {
|
||||
if (encounterId == null) {
|
||||
return R.fail("就诊ID不能为空");
|
||||
}
|
||||
return R.ok(iRequestFormManageAppService.getRequestForm(encounterId, ActivityDefCategory.PROOF.getCode(), startDate, endDate, status, keyword));
|
||||
return R.ok(iRequestFormManageAppService.getRequestForm(encounterId, ActivityDefCategory.PROOF.getCode(), startDate, endDate, status));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,32 +140,6 @@ public class RequestFormManageController {
|
||||
return R.ok(iRequestFormManageAppService.getRequestForm(encounterId, ActivityDefCategory.PROCEDURE.getCode()));
|
||||
}
|
||||
/**
|
||||
* 分页查询手术申请单(全局,不需要encounterId,用于门诊手术安排查找弹窗)
|
||||
*
|
||||
* @param surgeryNo 手术单号(模糊查询,可选)
|
||||
* @param applyTimeStart 申请时间起始(可选)
|
||||
* @param applyTimeEnd 申请时间截止(可选)
|
||||
* @param mainDoctorId 主刀医生ID(可选)
|
||||
* @param applyDeptId 申请科室ID(可选)
|
||||
* @param pageNo 页码,默认1
|
||||
* @param pageSize 每页数量,默认10
|
||||
* @return 分页手术申请单列表
|
||||
*/
|
||||
@GetMapping(value = "/get-surgery-page")
|
||||
public R<IPage<RequestFormPageDto>> getSurgeryRequestFormPage(
|
||||
@RequestParam(required = false) String surgeryNo,
|
||||
@RequestParam(required = false) LocalDate applyTimeStart,
|
||||
@RequestParam(required = false) LocalDate applyTimeEnd,
|
||||
@RequestParam(required = false) Long mainDoctorId,
|
||||
@RequestParam(required = false) Long applyDeptId,
|
||||
@RequestParam(defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||
RequestFormDto dto = new RequestFormDto(surgeryNo, ActivityDefCategory.PROCEDURE.getCode(), applyTimeStart, applyTimeEnd,
|
||||
mainDoctorId, applyDeptId, pageNo, pageSize);
|
||||
return R.ok(iRequestFormManageAppService.getRequestFormPage(dto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询申请单
|
||||
* @return 申请单
|
||||
*/
|
||||
@@ -186,26 +147,4 @@ public class RequestFormManageController {
|
||||
public R<IPage<RequestFormPageDto>> getRequestFormPage(@RequestBody RequestFormDto requestFormDto) {
|
||||
return R.ok(iRequestFormManageAppService.getRequestFormPage(requestFormDto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除申请单(仅待签发状态可删除)
|
||||
*
|
||||
* @param data 包含 requestFormId 的请求体
|
||||
* @return 结果
|
||||
*/
|
||||
@PostMapping(value = "/delete")
|
||||
public R<?> deleteRequestForm(@RequestBody Map<String, Long> data) {
|
||||
return iRequestFormManageAppService.deleteRequestForm(data.get("requestFormId"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回申请单(已签发状态撤回至待签发)
|
||||
*
|
||||
* @param data 包含 requestFormId 的请求体
|
||||
* @return 结果
|
||||
*/
|
||||
@PostMapping(value = "/withdraw")
|
||||
public R<?> withdrawRequestForm(@RequestBody Map<String, Long> data) {
|
||||
return iRequestFormManageAppService.withdrawRequestForm(data.get("requestFormId"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,6 @@ public class RequestFormDto {
|
||||
* 手术单号
|
||||
*/
|
||||
private String surgeryNo;
|
||||
/**
|
||||
* 申请单类型编码
|
||||
*/
|
||||
private String typeCode;
|
||||
/**
|
||||
* 申请时间开始
|
||||
*/
|
||||
|
||||
@@ -37,15 +37,13 @@ public interface RequestFormManageAppMapper {
|
||||
* @param startDate 开始日期(可选,格式:yyyy-MM-dd)
|
||||
* @param endDate 结束日期(可选,格式:yyyy-MM-dd)
|
||||
* @param status 单据状态(可选)
|
||||
* @param keyword 关键字(可选,申请单号/检查项目名称模糊匹配)
|
||||
* @return 申请单列表
|
||||
*/
|
||||
List<RequestFormQueryDto> getRequestForm(@Param("encounterId") Long encounterId,
|
||||
@Param("typeCode") String typeCode,
|
||||
@Param("startDate") String startDate,
|
||||
@Param("endDate") String endDate,
|
||||
@Param("status") String status,
|
||||
@Param("keyword") String keyword);
|
||||
@Param("status") String status);
|
||||
|
||||
/**
|
||||
* 查询申请单详情
|
||||
|
||||
@@ -138,12 +138,4 @@ public class CurrentDayEncounterTencentDto {
|
||||
*/
|
||||
private String englishName;
|
||||
|
||||
/** 号源池ID(用于分诊队列 div_log 审计日志) */
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long poolId;
|
||||
|
||||
/** 号源槽位ID(用于分诊队列 div_log 审计日志) */
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long slotId;
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ spring:
|
||||
druid:
|
||||
# 主库数据源
|
||||
master:
|
||||
url: jdbc:postgresql://192.168.110.252:15432/postgresql?currentSchema=histest1&characterEncoding=UTF-8&client_encoding=UTF-8
|
||||
url: jdbc:postgresql://192.168.110.252:15432/postgresql?currentSchema=histest&characterEncoding=UTF-8&client_encoding=UTF-8
|
||||
username: postgresql
|
||||
password: Jchl1528
|
||||
# 从库数据源
|
||||
|
||||
@@ -97,14 +97,9 @@
|
||||
CASE
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_table = 'cli_surgery' THEN T9.surgery_name
|
||||
WHEN T1.context_enum = 6 AND T1.product_table = 'cli_surgery' THEN T9.surgery_name
|
||||
WHEN T1.context_enum = 6 AND T1.service_table = 'wor_service_request' THEN COALESCE(
|
||||
wsr.content_json::json->>'surgeryName',
|
||||
wsr.content_json::json->>'adviceName',
|
||||
T9sr.surgery_name)
|
||||
WHEN T1.context_enum = 6 AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN COALESCE(wsr.content_json::json->>'adviceName', T2."name")
|
||||
WHEN T1.context_enum = 6 THEN T2."name"
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN COALESCE(wsr.content_json::json->>'adviceName', T2."name")
|
||||
WHEN T1.context_enum = #{activity} AND T1.service_table = 'wor_service_request' THEN COALESCE(T9.surgery_name, wsr.content_json::json->>'surgeryName', wsr.content_json::json->>'adviceName', T2."name")
|
||||
WHEN T1.context_enum = #{activity} THEN COALESCE(wsr.content_json::json->>'surgeryName', wsr.content_json::json->>'adviceName', T2."name")
|
||||
WHEN T1.context_enum = #{medication} THEN T3."name"
|
||||
WHEN T1.context_enum = #{device} THEN T4."name"
|
||||
@@ -112,7 +107,6 @@
|
||||
CASE
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_table = 'cli_surgery' THEN NULL
|
||||
WHEN T1.context_enum = 6 AND T1.product_table = 'cli_surgery' THEN NULL
|
||||
WHEN T1.context_enum = 6 AND T1.service_table = 'wor_service_request' THEN NULL
|
||||
WHEN T1.context_enum = 6 AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN NULL
|
||||
WHEN T1.context_enum = 6 THEN T2.yb_no
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN NULL
|
||||
@@ -123,7 +117,6 @@
|
||||
CASE
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_table = 'cli_surgery' THEN T9.id
|
||||
WHEN T1.context_enum = 6 AND T1.product_table = 'cli_surgery' THEN T9.id
|
||||
WHEN T1.context_enum = 6 AND T1.service_table = 'wor_service_request' THEN COALESCE(T9sr.id, wsr.activity_id)
|
||||
WHEN T1.context_enum = 6 AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN 0
|
||||
WHEN T1.context_enum = 6 THEN T2.id
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN 0
|
||||
@@ -165,11 +158,6 @@
|
||||
LEFT JOIN med_medication_request AS mmr ON mmr.id = T1.service_id AND mmr.delete_flag = '0'
|
||||
LEFT JOIN wor_device_request AS wdr ON wdr.id = T1.service_id AND wdr.delete_flag = '0'
|
||||
LEFT JOIN wor_service_request AS wsr ON wsr.id = T1.service_id AND wsr.delete_flag = '0'
|
||||
LEFT JOIN cli_surgery AS T9sr ON T1.context_enum = 6
|
||||
AND T1.service_table = 'wor_service_request'
|
||||
AND wsr.activity_id IS NOT NULL
|
||||
AND wsr.activity_id = T9sr.id
|
||||
AND T9sr.delete_flag = '0'
|
||||
LEFT JOIN wor_service_request AS wsrp ON wsrp.id = wsr.parent_id AND wsrp.delete_flag = '0'
|
||||
WHERE T1.encounter_id = #{encounterId}
|
||||
AND T1.status_enum IN (0
|
||||
@@ -234,14 +222,9 @@
|
||||
CASE
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_table = 'cli_surgery' THEN T9.surgery_name
|
||||
WHEN T1.context_enum = 6 AND T1.product_table = 'cli_surgery' THEN T9.surgery_name
|
||||
WHEN T1.context_enum = 6 AND T1.service_table = 'wor_service_request' THEN COALESCE(
|
||||
wsr.content_json::json->>'surgeryName',
|
||||
wsr.content_json::json->>'adviceName',
|
||||
T9sr.surgery_name)
|
||||
WHEN T1.context_enum = 6 AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN COALESCE(wsr.content_json::json->>'adviceName', T2."name")
|
||||
WHEN T1.context_enum = 6 THEN T2."name"
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN COALESCE(wsr.content_json::json->>'adviceName', T2."name")
|
||||
WHEN T1.context_enum = #{activity} AND T1.service_table = 'wor_service_request' THEN COALESCE(T9.surgery_name, wsr.content_json::json->>'surgeryName', wsr.content_json::json->>'adviceName', T2."name")
|
||||
WHEN T1.context_enum = #{activity} THEN COALESCE(wsr.content_json::json->>'surgeryName', wsr.content_json::json->>'adviceName', T2."name")
|
||||
WHEN T1.context_enum = #{medication} THEN T3."name"
|
||||
WHEN T1.context_enum = #{device} THEN T4."name"
|
||||
@@ -249,7 +232,6 @@
|
||||
CASE
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_table = 'cli_surgery' THEN NULL
|
||||
WHEN T1.context_enum = 6 AND T1.product_table = 'cli_surgery' THEN NULL
|
||||
WHEN T1.context_enum = 6 AND T1.service_table = 'wor_service_request' THEN NULL
|
||||
WHEN T1.context_enum = 6 AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN NULL
|
||||
WHEN T1.context_enum = 6 THEN T2.yb_no
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN NULL
|
||||
@@ -260,7 +242,6 @@
|
||||
CASE
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_table = 'cli_surgery' THEN T9.id
|
||||
WHEN T1.context_enum = 6 AND T1.product_table = 'cli_surgery' THEN T9.id
|
||||
WHEN T1.context_enum = 6 AND T1.service_table = 'wor_service_request' THEN COALESCE(T9sr.id, wsr.activity_id)
|
||||
WHEN T1.context_enum = 6 AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN 0
|
||||
WHEN T1.context_enum = 6 THEN T2.id
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN 0
|
||||
@@ -303,11 +284,6 @@
|
||||
LEFT JOIN med_medication_request AS mmr ON mmr.id = T1.service_id AND mmr.delete_flag = '0'
|
||||
LEFT JOIN wor_device_request AS wdr ON wdr.id = T1.service_id AND wdr.delete_flag = '0'
|
||||
LEFT JOIN wor_service_request AS wsr ON wsr.id = T1.service_id AND wsr.delete_flag = '0'
|
||||
LEFT JOIN cli_surgery AS T9sr ON T1.context_enum = 6
|
||||
AND T1.service_table = 'wor_service_request'
|
||||
AND wsr.activity_id IS NOT NULL
|
||||
AND wsr.activity_id = T9sr.id
|
||||
AND T9sr.delete_flag = '0'
|
||||
WHERE T1.encounter_id = #{encounterId}
|
||||
AND T1.status_enum IN (0
|
||||
, #{planned}
|
||||
|
||||
@@ -331,10 +331,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
) t
|
||||
WHERE rn = 1
|
||||
) pi ON s.patient_id = pi.patient_id
|
||||
<!-- 关联服务请求表(仅用于数据关联,不再过滤) -->
|
||||
<!-- 排除已生成医嘱的手术 -->
|
||||
LEFT JOIN wor_service_request sr ON sr.activity_id = s.id AND sr.delete_flag = '0' AND sr.category_enum = 4
|
||||
<where>
|
||||
s.delete_flag = '0'
|
||||
<!-- 只显示未生成医嘱的手术 -->
|
||||
AND sr.id IS NULL
|
||||
<if test="ew.sqlSegment != null and ew.sqlSegment != ''">
|
||||
<!-- 补充 encounter_id 替换,修复多表关联时字段歧义。注释不能放进 OGNL 表达式内部。 -->
|
||||
<![CDATA[
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
</if>
|
||||
AND os.delete_flag = '0'
|
||||
</where>
|
||||
ORDER BY os.create_time DESC, os.schedule_id DESC
|
||||
ORDER BY os.create_time DESC
|
||||
</select>
|
||||
<!-- 根据ID查询手术安排详情-->
|
||||
<select id="getSurgeryScheduleDetail" resultType="com.openhis.web.clinicalmanage.dto.OpScheduleDto">
|
||||
@@ -89,8 +89,6 @@
|
||||
cs.apply_doctor_name AS apply_doctor_name,
|
||||
drf.create_time AS apply_time,
|
||||
os.surgery_nature AS surgeryType,
|
||||
cs.incision_level AS incisionLevel,
|
||||
os.fee_type AS feeType,
|
||||
COALESCE(pi.identifier_no, ap.bus_no, '') AS identifierNo
|
||||
FROM op_schedule os
|
||||
LEFT JOIN adm_patient ap ON os.patient_id = ap.id
|
||||
@@ -184,7 +182,7 @@
|
||||
<if test="dto.applyDeptId != null and dto.applyDeptId != ''"> AND cs.apply_dept_id = #{dto.applyDeptId}</if>
|
||||
<if test="dto.patientName != null and dto.patientName != ''"> AND ap.name LIKE CONCAT('%', #{dto.patientName}, '%')</if>
|
||||
</where>
|
||||
ORDER BY os.create_time DESC, os.schedule_id DESC
|
||||
ORDER BY os.create_time DESC
|
||||
</select>
|
||||
<!-- 查询时间段内该手术室是否被占用-->
|
||||
<select id="isScheduleConflict" resultType="java.lang.Boolean">
|
||||
|
||||
@@ -98,6 +98,9 @@
|
||||
INNER JOIN adm_organization_location AS T6 ON T6.distribution_category_code = t1.category_code AND T6.delete_flag = '0' AND T6.item_code = '1' AND T6.organization_id = #{organizationId} AND (CURRENT_TIME :: time (6) BETWEEN T6.start_time AND T6.end_time)
|
||||
WHERE t1.delete_flag = '0'
|
||||
AND T2.status_enum = #{statusEnum}
|
||||
<if test="pricingFlag == 1">
|
||||
AND 1 = 2
|
||||
</if>
|
||||
<if test="categoryCode != null and categoryCode != ''">
|
||||
<!-- 🔧 BugFix: 支持两种匹配方式 -->
|
||||
<!-- 1. 直接匹配:distribution_category_code = category_code(都是数字代码) -->
|
||||
@@ -239,7 +242,7 @@
|
||||
NULL AS activity_type_dictText,
|
||||
-- 前端"包装单位"列:显示使用单位(permitted_unit_code)
|
||||
T1.permitted_unit_code AS unit_code,
|
||||
T1.permitted_unit_code AS min_unit_code,
|
||||
'' AS min_unit_code,
|
||||
'' AS volume,
|
||||
'' AS method_code,
|
||||
'' AS rate_code,
|
||||
@@ -527,9 +530,6 @@
|
||||
LEFT JOIN cli_condition AS cc ON cc.id = T1.condition_id AND cc.delete_flag = '0'
|
||||
LEFT JOIN cli_condition_definition AS ccd ON ccd.id = cc.definition_id
|
||||
WHERE T1.delete_flag = '0' AND T1.tcm_flag = 0
|
||||
<if test="generateSourceEnum != null">
|
||||
AND T1.generate_source_enum = #{generateSourceEnum}
|
||||
</if>
|
||||
<if test="historyFlag == '0'.toString()">
|
||||
AND T1.encounter_id = #{encounterId}
|
||||
</if>
|
||||
@@ -539,15 +539,14 @@
|
||||
AND T1.refund_medicine_id IS NULL
|
||||
ORDER BY T1.status_enum,T1.sort_number)
|
||||
UNION ALL
|
||||
-- 🔧 查询仅存在于 adm_charge_item 的"孤儿"耗材数据(DeviceRequest 缺失或 generate_source_enum 未设置)
|
||||
-- 正常 DeviceRequest(generate_source_enum 已赋值)由下方 Part 3 统一负责,此处不做重复覆盖避免 UNION ALL 重复行
|
||||
-- 🔧 新增:查询门诊术中计费生成的耗材数据(这些数据存在于 adm_charge_item 和 wor_device_request)
|
||||
(SELECT 2 AS advice_type,
|
||||
CI.service_id AS request_id,
|
||||
CI.service_id || '-ci-dev' AS unique_key,
|
||||
'' AS prescription_no,
|
||||
COALESCE(DR.requester_id, CI.enterer_id) AS requester_id,
|
||||
CI.enterer_id AS requester_id,
|
||||
CI.entered_date AS request_time,
|
||||
CASE WHEN COALESCE(DR.requester_id, CI.enterer_id) = #{practitionerId} THEN '1' ELSE '0' END AS biz_request_flag,
|
||||
CASE WHEN CI.enterer_id = #{practitionerId} THEN '1' ELSE '0' END AS biz_request_flag,
|
||||
DR.content_json AS content_json,
|
||||
NULL AS skin_test_flag,
|
||||
NULL AS inject_flag,
|
||||
@@ -584,9 +583,6 @@
|
||||
LEFT JOIN adm_location AS AL ON AL.id = DR.perform_location AND AL.delete_flag = '0'
|
||||
WHERE CI.delete_flag = '0'
|
||||
AND CI.service_table = 'wor_device_request'
|
||||
<if test="generateSourceEnum != null">
|
||||
AND DR.generate_source_enum IS NULL <!-- 仅匹配孤儿记录,normal DeviceRequest 由 Part 3 负责,避免 UNION ALL 重复 -->
|
||||
</if>
|
||||
<if test="historyFlag == '0'.toString()">
|
||||
AND CI.encounter_id = #{encounterId}
|
||||
</if>
|
||||
@@ -702,9 +698,6 @@
|
||||
-- based_on_table='med_medication_request' → 输液/皮试执行记录,应排除
|
||||
-- based_on_table='exam_apply'/'lab_apply' → 申请单原始医嘱,应保留
|
||||
AND (T1.based_on_id IS NULL OR T1.based_on_table IS NULL OR T1.based_on_table NOT IN ('med_medication_request', 'med_medication_dispense'))
|
||||
<if test="sourceBillNo != null and sourceBillNo != ''">
|
||||
AND T1.prescription_no = #{sourceBillNo}
|
||||
</if>
|
||||
<if test="historyFlag == '0'.toString()">
|
||||
AND T1.encounter_id = #{encounterId}
|
||||
</if>
|
||||
@@ -811,54 +804,4 @@
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<!-- 手术项目专用分页查询:仅查手术 + 定价,无库存/草稿库存/取药科室等无关逻辑 -->
|
||||
<select id="getSurgeryPage" resultType="com.openhis.web.doctorstation.dto.SurgeryItemDto">
|
||||
SELECT
|
||||
t1.ID AS advice_definition_id,
|
||||
t1.NAME AS advice_name,
|
||||
t1.org_id AS org_id,
|
||||
t1.org_id AS position_id,
|
||||
t2.ID AS charge_item_definition_id,
|
||||
t2.price AS price,
|
||||
t1.permitted_unit_code AS unit_code,
|
||||
t1.permitted_unit_code AS unit_code_dict_text
|
||||
FROM wor_activity_definition t1
|
||||
LEFT JOIN adm_charge_item_definition t2
|
||||
ON t2.instance_id = t1.ID
|
||||
AND t2.delete_flag = '0'
|
||||
AND t2.status_enum = #{statusEnum}
|
||||
AND t2.instance_table = 'wor_activity_definition'
|
||||
WHERE t1.delete_flag = '0'
|
||||
AND (t1.category_code = '手术' OR t1.category_code = '24')
|
||||
<if test="searchKey != null and searchKey != ''">
|
||||
AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%')
|
||||
</if>
|
||||
ORDER BY t1.name ASC
|
||||
</select>
|
||||
|
||||
<!-- 检查项目专用分页查询:仅查检查(23) + 定价,无库存/草稿库存/取药科室等无关逻辑 -->
|
||||
<select id="getExaminationPage" resultType="com.openhis.web.doctorstation.dto.SurgeryItemDto">
|
||||
SELECT
|
||||
t1.ID AS advice_definition_id,
|
||||
t1.NAME AS advice_name,
|
||||
t1.org_id AS org_id,
|
||||
t1.org_id AS position_id,
|
||||
t2.ID AS charge_item_definition_id,
|
||||
t2.price AS price,
|
||||
t1.permitted_unit_code AS unit_code,
|
||||
t1.permitted_unit_code AS unit_code_dict_text
|
||||
FROM wor_activity_definition t1
|
||||
LEFT JOIN adm_charge_item_definition t2
|
||||
ON t2.instance_id = t1.ID
|
||||
AND t2.delete_flag = '0'
|
||||
AND t2.status_enum = #{statusEnum}
|
||||
AND t2.instance_table = 'wor_activity_definition'
|
||||
WHERE t1.delete_flag = '0'
|
||||
AND t1.category_code = '23'
|
||||
<if test="searchKey != null and searchKey != ''">
|
||||
AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%')
|
||||
</if>
|
||||
ORDER BY t1.name ASC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -134,11 +134,7 @@
|
||||
T2.yb_no,
|
||||
T1.onset_date AS onsetDate,
|
||||
T1.diagnosis_time AS diagnosisTime,
|
||||
T1.doctor AS diagnosisDoctor,
|
||||
CASE WHEN EXISTS (
|
||||
SELECT 1 FROM infectious_card T4
|
||||
WHERE T4.diag_id = T2.id AND T4.delete_flag = '0' AND T4.status >= 1
|
||||
) THEN 1 ELSE 0 END AS hasInfectiousReport
|
||||
T1.doctor AS diagnosisDoctor
|
||||
FROM adm_encounter_diagnosis AS T1
|
||||
LEFT JOIN cli_condition AS T2 ON T2.ID = T1.condition_id
|
||||
AND T2.delete_flag = '0' AND T2.tcm_flag = 0
|
||||
|
||||
@@ -231,7 +231,7 @@
|
||||
ae.priority_enum,
|
||||
ae.organization_id,
|
||||
ae.start_time AS in_hos_time,
|
||||
COALESCE(bed.start_time, ae.start_time) AS start_time,
|
||||
bed.start_time,
|
||||
bed.location_id AS bed_id,
|
||||
bed.location_name AS bed_name,
|
||||
house.location_id AS house_id,
|
||||
@@ -264,6 +264,7 @@
|
||||
WHERE ael.status_enum = #{active}
|
||||
AND ael.delete_flag = '0'
|
||||
AND ael.form_enum = #{bed}
|
||||
LIMIT 1
|
||||
) AS bed ON bed.encounter_id = ae.id
|
||||
LEFT JOIN (
|
||||
SELECT ael.encounter_id,
|
||||
@@ -274,6 +275,7 @@
|
||||
WHERE ael.status_enum = #{active}
|
||||
AND ael.delete_flag = '0'
|
||||
AND ael.form_enum = #{house}
|
||||
LIMIT 1
|
||||
) AS house ON house.encounter_id = ae.id
|
||||
LEFT JOIN (
|
||||
SELECT ael.encounter_id,
|
||||
@@ -284,6 +286,7 @@
|
||||
WHERE ael.status_enum = #{active}
|
||||
AND ael.delete_flag = '0'
|
||||
AND ael.form_enum = #{ward}
|
||||
LIMIT 1
|
||||
) AS ward ON ward.encounter_id = ae.id
|
||||
LEFT JOIN (
|
||||
SELECT aep.encounter_id,
|
||||
|
||||
@@ -153,9 +153,8 @@
|
||||
ii.balance_amount AS balance_amount,
|
||||
ii.account_id AS account_id,
|
||||
ii.performer_check_id,
|
||||
ii.category_code,
|
||||
ii.dispense_status
|
||||
FROM (( SELECT DISTINCT T1.encounter_id,
|
||||
ii.category_code
|
||||
FROM (( SELECT T1.encounter_id,
|
||||
T1.tenant_id,
|
||||
#{medMedicationRequest} AS advice_table,
|
||||
T1.id AS request_id,
|
||||
@@ -198,8 +197,7 @@
|
||||
pra."name" AS admitting_doctor_name,
|
||||
personal_account.balance_amount,
|
||||
personal_account.id AS account_id,
|
||||
T2.category_code,
|
||||
mmd.status_enum AS dispense_status
|
||||
T2.category_code
|
||||
FROM med_medication_request AS T1
|
||||
LEFT JOIN med_medication_definition AS T2
|
||||
ON T2.id = T1.medication_id
|
||||
@@ -280,13 +278,6 @@
|
||||
aa.balance_amount
|
||||
) AS personal_account
|
||||
ON personal_account.encounter_id = ae.id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT status_enum
|
||||
FROM med_medication_dispense
|
||||
WHERE med_req_id = T1.id AND delete_flag = '0'
|
||||
ORDER BY create_time DESC
|
||||
LIMIT 1
|
||||
) mmd ON true
|
||||
WHERE T1.delete_flag = '0'
|
||||
AND T1.refund_medicine_id IS NULL
|
||||
AND T1.generate_source_enum = #{doctorPrescription}
|
||||
@@ -297,7 +288,7 @@
|
||||
T1.sort_number,
|
||||
T1.group_id )
|
||||
UNION
|
||||
( SELECT DISTINCT T1.encounter_id,
|
||||
( SELECT T1.encounter_id,
|
||||
T1.tenant_id,
|
||||
#{worServiceRequest} AS advice_table,
|
||||
T1.id AS request_id,
|
||||
@@ -305,28 +296,28 @@
|
||||
T1.occurrence_end_time AS end_time,
|
||||
T1.requester_id AS requester_id,
|
||||
T1.create_time AS request_time,
|
||||
NULL::integer AS skin_test_flag,
|
||||
NULL::integer AS inject_flag,
|
||||
NULL::bigint AS group_id,
|
||||
NULL AS skin_test_flag,
|
||||
NULL AS inject_flag,
|
||||
NULL AS group_id,
|
||||
T1.performer_check_id,
|
||||
T2."name" AS advice_name,
|
||||
T2.id AS item_id,
|
||||
NULL::varchar AS volume,
|
||||
NULL::varchar AS lot_number,
|
||||
NULL AS volume,
|
||||
NULL AS lot_number,
|
||||
T1.quantity AS quantity,
|
||||
T1.unit_code AS unit_code,
|
||||
T1.status_enum AS request_status,
|
||||
NULL::varchar AS method_code,
|
||||
NULL::varchar AS rate_code,
|
||||
NULL::numeric AS dose,
|
||||
NULL::varchar AS dose_unit_code,
|
||||
NULL AS method_code,
|
||||
NULL AS rate_code,
|
||||
NULL AS dose,
|
||||
NULL AS dose_unit_code,
|
||||
ao1.id AS position_id,
|
||||
ao1."name" AS position_name,
|
||||
NULL::integer AS dispense_per_duration,
|
||||
1::numeric AS part_percent,
|
||||
NULL AS dispense_per_duration,
|
||||
1 AS part_percent,
|
||||
ccd."name" AS condition_definition_name,
|
||||
T1.therapy_enum AS therapy_enum,
|
||||
NULL::integer AS sort_number,
|
||||
NULL AS sort_number,
|
||||
T1.quantity AS execute_num,
|
||||
af.day_times,
|
||||
ae.bus_no,
|
||||
@@ -340,8 +331,7 @@
|
||||
pra."name" AS admitting_doctor_name,
|
||||
personal_account.balance_amount,
|
||||
personal_account.id AS account_id,
|
||||
T2.category_code,
|
||||
NULL::integer AS dispense_status
|
||||
T2.category_code
|
||||
FROM wor_service_request AS T1
|
||||
LEFT JOIN wor_activity_definition AS T2
|
||||
ON T2.id = T1.activity_id
|
||||
|
||||
@@ -44,9 +44,7 @@
|
||||
sdd.dict_label AS unit_code_name,
|
||||
togpd.dose AS dose,
|
||||
togpd.rate_code AS rate_code,
|
||||
rate_d.dict_label AS rate_code_dictText,
|
||||
togpd.method_code AS method_code,
|
||||
method_d.dict_label AS method_code_dictText,
|
||||
togpd.dose_quantity AS dose_quantity,
|
||||
togpd.group_id,
|
||||
togpd.group_order AS group_order,
|
||||
@@ -69,9 +67,8 @@
|
||||
AND togpd.order_definition_id = adm.ID
|
||||
LEFT JOIN wor_activity_definition AS wor ON togpd.order_definition_table = 'wor_activity_definition'
|
||||
AND togpd.order_definition_id = wor.ID
|
||||
LEFT JOIN sys_dict_data AS sdd ON sdd.dict_value = togpd.unit_code AND sdd.dict_type = 'unit_code' AND sdd.status = '0'
|
||||
LEFT JOIN sys_dict_data AS rate_d ON rate_d.dict_value = togpd.rate_code AND rate_d.dict_type = 'rate_code' AND rate_d.status = '0'
|
||||
LEFT JOIN sys_dict_data AS method_d ON method_d.dict_value = togpd.method_code AND method_d.dict_type = 'method_code' AND method_d.status = '0'
|
||||
LEFT JOIN sys_dict_data AS sdd ON sdd.dict_value = togpd.unit_code AND sdd.dict_type = 'unit_code' AND
|
||||
sdd.status = '0'
|
||||
WHERE togpd.delete_flag = '0'
|
||||
<if test="groupPackageIds != null and !groupPackageIds.isEmpty()">
|
||||
AND togpd.group_package_id IN
|
||||
|
||||
@@ -105,8 +105,6 @@
|
||||
<if test="statusEnum == 4">
|
||||
T4.status_enum = #{completed}
|
||||
</if>
|
||||
AND T4.summary_no IS NOT NULL
|
||||
AND T4.summary_no != ''
|
||||
) AS ii
|
||||
${ew.customSqlSegment}
|
||||
GROUP BY ii.encounter_id,
|
||||
@@ -265,8 +263,6 @@
|
||||
AND T15.delete_flag = '0'
|
||||
WHERE T1.delete_flag = '0'
|
||||
-- 因发药配药合并,前台只能看到待发药,已发药状态,但是后台配药发药状态都查
|
||||
AND T1.summary_no IS NOT NULL
|
||||
AND T1.summary_no != ''
|
||||
AND
|
||||
<if test="dispenseStatus == null">
|
||||
T1.status_enum IN (#{inProgress},#{completed},#{preparation},#{prepared})
|
||||
|
||||
@@ -216,8 +216,7 @@
|
||||
ccd.name AS condition_definition_name,
|
||||
T1.therapy_enum AS therapyEnum,
|
||||
T1.sort_number AS sort_number,
|
||||
T1.based_on_id AS based_on_id,
|
||||
T1.medication_id AS advice_definition_id
|
||||
T1.based_on_id AS based_on_id
|
||||
FROM med_medication_request AS T1
|
||||
LEFT JOIN med_medication_definition AS T2 ON T2.ID = T1.medication_id
|
||||
AND T2.delete_flag = '0'
|
||||
@@ -269,8 +268,7 @@
|
||||
'' AS condition_definition_name,
|
||||
2 AS therapyEnum,
|
||||
99 AS sort_number,
|
||||
T1.based_on_id AS based_on_id,
|
||||
T1.device_def_id AS advice_definition_id
|
||||
T1.based_on_id AS based_on_id
|
||||
FROM wor_device_request AS T1
|
||||
LEFT JOIN adm_device_definition AS T2 ON T2.ID = T1.device_def_id
|
||||
AND T2.delete_flag = '0'
|
||||
@@ -288,7 +286,7 @@
|
||||
AND T1.refund_device_id IS NULL
|
||||
ORDER BY T1.status_enum)
|
||||
UNION ALL
|
||||
(SELECT CASE WHEN T1.category_enum IN (4, 24) THEN 6 ELSE 3 END AS advice_type,
|
||||
(SELECT CASE WHEN T1.category_enum = 4 THEN 6 ELSE COALESCE(T1.category_enum, 3) END AS advice_type,
|
||||
T1.id AS request_id,
|
||||
T1.id || '-3' AS unique_key,
|
||||
T1.requester_id AS requester_id,
|
||||
@@ -319,8 +317,7 @@
|
||||
'' AS condition_definition_name,
|
||||
COALESCE(T1.therapy_enum, 2) AS therapyEnum,
|
||||
99 AS sort_number,
|
||||
T1.based_on_id AS based_on_id,
|
||||
T1.activity_id AS advice_definition_id
|
||||
T1.based_on_id AS based_on_id
|
||||
FROM wor_service_request AS T1
|
||||
LEFT JOIN wor_activity_definition AS T2
|
||||
ON T2.ID = T1.activity_id
|
||||
@@ -373,4 +370,4 @@
|
||||
</if>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
</mapper>
|
||||
@@ -8,34 +8,17 @@
|
||||
SELECT drf.id AS request_form_id,
|
||||
drf.encounter_id,
|
||||
drf.prescription_no,
|
||||
COALESCE(
|
||||
(SELECT STRING_AGG(DISTINCT wad.name, '、')
|
||||
FROM wor_service_request wsr2
|
||||
LEFT JOIN wor_activity_definition wad ON wad.id = wsr2.activity_id AND wad.delete_flag = '0'
|
||||
WHERE wsr2.prescription_no = drf.prescription_no AND wsr2.delete_flag = '0'),
|
||||
drf.name
|
||||
) AS name,
|
||||
drf.NAME,
|
||||
drf.desc_json,
|
||||
drf.requester_id,
|
||||
drf.create_time,
|
||||
ap.NAME AS patient_name,
|
||||
CASE
|
||||
WHEN MIN(wsr.status_enum) = 1 THEN 0
|
||||
WHEN MIN(wsr.status_enum) = 2 THEN 1
|
||||
WHEN MIN(wsr.status_enum) = 3 AND MAX(CASE WHEN wsr.performer_check_id IS NOT NULL THEN 1 ELSE 0 END) = 1 THEN 2
|
||||
WHEN MIN(wsr.status_enum) = 3 THEN 4
|
||||
WHEN MIN(wsr.status_enum) = 4 THEN 3
|
||||
WHEN MIN(wsr.status_enum) = 5 OR MIN(wsr.status_enum) = 6 OR MIN(wsr.status_enum) = 7 THEN 7
|
||||
WHEN MIN(wsr.status_enum) = 8 THEN 6
|
||||
ELSE NULL
|
||||
END AS status
|
||||
drf.status,
|
||||
ap.NAME AS patient_name
|
||||
FROM doc_request_form AS drf
|
||||
LEFT JOIN adm_encounter AS ae ON ae.ID = drf.encounter_id
|
||||
AND ae.delete_flag = '0'
|
||||
LEFT JOIN adm_patient AS ap ON ap.ID = ae.patient_id
|
||||
AND ap.delete_flag = '0'
|
||||
LEFT JOIN wor_service_request AS wsr ON wsr.prescription_no = drf.prescription_no
|
||||
AND wsr.delete_flag = '0'
|
||||
WHERE drf.delete_flag = '0'
|
||||
AND drf.encounter_id = #{encounterId}
|
||||
AND drf.type_code = #{typeCode}
|
||||
@@ -46,37 +29,12 @@
|
||||
AND drf.create_time <= (#{endDate}::date + INTERVAL '1 day' - INTERVAL '1 second')
|
||||
</if>
|
||||
<if test="status != null and status != ''">
|
||||
AND CASE
|
||||
WHEN MIN(wsr.status_enum) = 1 THEN 0
|
||||
WHEN MIN(wsr.status_enum) = 2 THEN 1
|
||||
WHEN MIN(wsr.status_enum) = 3 AND MAX(CASE WHEN wsr.performer_check_id IS NOT NULL THEN 1 ELSE 0 END) = 1 THEN 2
|
||||
WHEN MIN(wsr.status_enum) = 3 THEN 4
|
||||
WHEN MIN(wsr.status_enum) = 4 THEN 3
|
||||
WHEN MIN(wsr.status_enum) = 5 OR MIN(wsr.status_enum) = 6 OR MIN(wsr.status_enum) = 7 THEN 7
|
||||
WHEN MIN(wsr.status_enum) = 8 THEN 6
|
||||
ELSE NULL
|
||||
END = #{status}::integer
|
||||
AND drf.status = #{status}::integer
|
||||
</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
AND (drf.prescription_no ILIKE '%' || #{keyword} || '%'
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM wor_service_request wsr2
|
||||
WHERE wsr2.prescription_no = drf.prescription_no
|
||||
AND wsr2.delete_flag = '0'
|
||||
AND wsr2.activity_id IN (
|
||||
SELECT id FROM wor_activity_definition wad
|
||||
WHERE wad.delete_flag = '0'
|
||||
AND wad.name ILIKE '%' || #{keyword} || '%'
|
||||
)
|
||||
))
|
||||
</if>
|
||||
GROUP BY drf.id, drf.encounter_id, drf.prescription_no, drf.name, drf.desc_json,
|
||||
drf.requester_id, drf.create_time, ap.name
|
||||
</select>
|
||||
|
||||
<select id="getRequestFormDetail" resultType="com.openhis.web.regdoctorstation.dto.RequestFormDetailQueryDto">
|
||||
SELECT wsr.activity_id AS activity_id,
|
||||
wsr.quantity,
|
||||
SELECT wsr.quantity,
|
||||
wsr.unit_code,
|
||||
COALESCE(wad.NAME, wsr.content_json::jsonb->>'surgeryName') AS advice_name,
|
||||
aci.total_price
|
||||
@@ -148,9 +106,9 @@
|
||||
fc.contract_name AS fee_type,
|
||||
COALESCE(pi.identifier_no, ap.bus_no, '') AS identifier_no
|
||||
FROM doc_request_form drf
|
||||
INNER JOIN cli_surgery cs ON cs.surgery_no = drf.prescription_no AND cs.delete_flag = '0'
|
||||
INNER JOIN adm_patient ap ON ap.id = cs.patient_id AND ap.delete_flag = '0'
|
||||
INNER JOIN adm_encounter ae ON ae.id = cs.encounter_id AND ae.delete_flag = '0'
|
||||
LEFT JOIN cli_surgery cs ON cs.surgery_no = drf.prescription_no AND cs.delete_flag = '0'
|
||||
LEFT JOIN adm_patient ap ON ap.id = cs.patient_id AND ap.delete_flag = '0'
|
||||
LEFT JOIN adm_encounter ae ON ae.id = cs.encounter_id AND ae.delete_flag = '0'
|
||||
LEFT JOIN adm_account aa ON aa.encounter_id = ae.id AND aa.delete_flag = '0'
|
||||
LEFT JOIN fin_contract fc ON fc.bus_no = aa.contract_no AND fc.delete_flag = '0'
|
||||
LEFT JOIN op_schedule os ON os.apply_id = drf.id AND os.delete_flag = '0'
|
||||
@@ -168,9 +126,6 @@
|
||||
<if test="requestFormDto.surgeryNo != null and requestFormDto.surgeryNo != ''">
|
||||
AND drf.prescription_no LIKE CONCAT('%', #{requestFormDto.surgeryNo}, '%')
|
||||
</if>
|
||||
<if test="requestFormDto.typeCode != null and requestFormDto.typeCode != ''">
|
||||
AND drf.type_code IN (#{requestFormDto.typeCode}, 'SURGERY')
|
||||
</if>
|
||||
<if test="requestFormDto.applyTimeStart != null">
|
||||
AND drf.create_time >= #{requestFormDto.applyTimeStart}
|
||||
</if>
|
||||
@@ -185,8 +140,6 @@
|
||||
</if>
|
||||
AND drf.delete_flag = '0'
|
||||
AND os.schedule_id IS NULL
|
||||
<!-- 已取消(4)、已完成(3)的手术申请不参与门诊手术安排查找 -->
|
||||
AND cs.status_enum NOT IN (3, 4)
|
||||
</where>
|
||||
ORDER BY drf.create_time DESC
|
||||
</select>
|
||||
|
||||
@@ -27,9 +27,7 @@
|
||||
T9.payment_id,
|
||||
T9.picture_url,
|
||||
T9.birth_date,
|
||||
t9.english_name,
|
||||
t9.slot_id,
|
||||
t9.pool_id
|
||||
t9.english_name
|
||||
from (
|
||||
SELECT T1.tenant_id AS tenant_id,
|
||||
T1.id AS encounter_id,
|
||||
@@ -53,9 +51,7 @@
|
||||
T13.id AS payment_id,
|
||||
ai.picture_url AS picture_url,
|
||||
T8.birth_date AS birth_date,
|
||||
tx.staff_english_name AS english_name,
|
||||
om_slot.slot_id AS slot_id,
|
||||
om_slot.pool_id AS pool_id
|
||||
tx.staff_english_name AS english_name
|
||||
FROM adm_encounter AS T1
|
||||
LEFT JOIN adm_organization AS T2 ON T1.organization_id = T2.ID AND T2.delete_flag = '0'
|
||||
LEFT JOIN adm_healthcare_service AS T3 ON T1.service_type_id = T3.ID AND T3.delete_flag = '0'
|
||||
@@ -95,8 +91,6 @@
|
||||
AND T13.status_enum = ${paymentStatus}
|
||||
LEFT JOIN adm_invoice AS ai
|
||||
ON ai.reconciliation_id = T13.id AND ai.delete_flag = '0'
|
||||
LEFT JOIN order_main AS om ON T1.order_id = om.id AND om.delete_flag = '0'
|
||||
LEFT JOIN adm_schedule_slot AS om_slot ON om.slot_id = om_slot.id
|
||||
WHERE T1.delete_flag = '0'
|
||||
AND T1.class_enum = #{classEnum}
|
||||
AND T10.context_enum = #{register}
|
||||
|
||||
@@ -270,10 +270,6 @@ public enum AssignSeqEnum {
|
||||
* 诊疗处方号
|
||||
*/
|
||||
ACTIVITY_PSYCHOTROPIC_NO("62", "诊疗处方号", "PAR"),
|
||||
/**
|
||||
* 检查申请单号(住院)
|
||||
*/
|
||||
CHECK_APPLY_NO("72", "检查申请单号", "JCZ"),
|
||||
/**
|
||||
* b 病历文书
|
||||
*/
|
||||
|
||||
@@ -39,12 +39,7 @@ public enum GenerateSource implements HisEnumInterface {
|
||||
/**
|
||||
* 自动滚费
|
||||
*/
|
||||
AUTO_ROLL_FEES(5, "5", "自动滚费"),
|
||||
|
||||
/**
|
||||
* 手术计费
|
||||
*/
|
||||
SURGERY_BILLING(6, "6", "手术计费");
|
||||
AUTO_ROLL_FEES(5, "5", "自动滚费");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright ©2023 CJB-CNIT Team. All rights reserved
|
||||
*/
|
||||
package com.openhis.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 订单状态 (order_main.status)
|
||||
*
|
||||
* <pre>
|
||||
* 状态流转:
|
||||
* 创建订单 → ACTIVE(1)
|
||||
* 签到 → ACTIVE(1) 不变
|
||||
* 患者退号 → PATIENT_CANCELLED(0)
|
||||
* 系统取消 → SYSTEM_CANCELLED(2)
|
||||
* 就诊完成 → COMPLETED(3)
|
||||
* </pre>
|
||||
*
|
||||
* @author wangjian963
|
||||
* @date 2026-05-09
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum OrderStatus implements HisEnumInterface {
|
||||
|
||||
/**
|
||||
* 患者取消
|
||||
*/
|
||||
PATIENT_CANCELLED(0, "0", "患者取消"),
|
||||
|
||||
/**
|
||||
* 有效
|
||||
*/
|
||||
ACTIVE(1, "1", "有效"),
|
||||
|
||||
/**
|
||||
* 系统取消
|
||||
*/
|
||||
SYSTEM_CANCELLED(2, "2", "系统取消"),
|
||||
|
||||
/**
|
||||
* 已完成
|
||||
*/
|
||||
COMPLETED(3, "3", "已完成");
|
||||
|
||||
private Integer value;
|
||||
private String code;
|
||||
private String info;
|
||||
|
||||
public static OrderStatus getByValue(Integer value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
for (OrderStatus val : values()) {
|
||||
if (val.getValue().equals(value)) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -49,11 +49,6 @@ public enum RequestStatus implements HisEnumInterface {
|
||||
*/
|
||||
ENDED(7, "ended", "不执行"),
|
||||
|
||||
/**
|
||||
* 已出报告
|
||||
*/
|
||||
COMPLETED_REPORT(8, "completed_report", "已出报告"),
|
||||
|
||||
/**
|
||||
* 未知
|
||||
*/
|
||||
|
||||
@@ -6,8 +6,8 @@ import com.core.common.utils.AssignSeqUtil;
|
||||
import com.openhis.clinical.domain.Order;
|
||||
import com.openhis.clinical.mapper.OrderMapper;
|
||||
import com.openhis.clinical.service.IOrderService;
|
||||
import com.openhis.common.constant.CommonConstants.AppointmentOrderStatus;
|
||||
import com.openhis.common.enums.AssignSeqEnum;
|
||||
import com.openhis.common.enums.OrderStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@@ -124,8 +124,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
||||
: new Date(); // 兜底:正常业务不应走到这里
|
||||
order.setAppointmentDate(appointmentDateTime);
|
||||
order.setAppointmentTime(appointmentDateTime);
|
||||
// 订单状态: 0=患者取消 1=有效 2=系统取消 3=已完成
|
||||
order.setStatus(OrderStatus.ACTIVE.getValue());
|
||||
order.setStatus(AppointmentOrderStatus.BOOKED);
|
||||
order.setPayStatus(0);
|
||||
order.setVersion(0);
|
||||
|
||||
@@ -170,13 +169,10 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
||||
if (order == null) {
|
||||
throw new RuntimeException("订单不存在");
|
||||
}
|
||||
// 已取消(患者取消0 或 系统取消2)不可再次取消
|
||||
if (OrderStatus.PATIENT_CANCELLED.getValue().equals(order.getStatus())
|
||||
|| OrderStatus.SYSTEM_CANCELLED.getValue().equals(order.getStatus())) {
|
||||
if (AppointmentOrderStatus.CANCELLED.equals(order.getStatus())) {
|
||||
throw new RuntimeException("订单已取消");
|
||||
}
|
||||
// 已完成(3)的订单不可取消
|
||||
if (OrderStatus.COMPLETED.getValue().equals(order.getStatus())) {
|
||||
if (AppointmentOrderStatus.CHECKED_IN.equals(order.getStatus())) {
|
||||
throw new RuntimeException("订单已完成,无法取消");
|
||||
}
|
||||
|
||||
@@ -193,7 +189,6 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
||||
.eq(Order::getPatientId, patientId)
|
||||
.eq(Order::getTenantId, tenantId)
|
||||
.ge(Order::getCancelTime, startTime)
|
||||
// 只统计患者主动取消(0),不含系统取消(2)
|
||||
.eq(Order::getStatus, OrderStatus.PATIENT_CANCELLED.getValue()));
|
||||
.eq(Order::getStatus, AppointmentOrderStatus.CANCELLED));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ import com.openhis.clinical.domain.Ticket;
|
||||
import com.openhis.clinical.mapper.TicketMapper;
|
||||
import com.openhis.clinical.service.IOrderService;
|
||||
import com.openhis.clinical.service.ITicketService;
|
||||
import com.openhis.common.constant.CommonConstants.AppointmentOrderStatus;
|
||||
import com.openhis.common.constant.CommonConstants.SlotStatus;
|
||||
import com.openhis.common.enums.OrderStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -195,8 +195,8 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
||||
Date startTime = Date.from(periodStart.atZone(ZoneId.systemDefault()).toInstant());
|
||||
Date endTime = Date.from(periodEnd.atZone(ZoneId.systemDefault()).toInstant());
|
||||
|
||||
// 预约去重以订单为准(order_main),有效订单(1)才参与去重
|
||||
List<Integer> effectiveOrderStatuses = Arrays.asList(OrderStatus.ACTIVE.getValue());
|
||||
// 预约去重以订单为准(order_main),因为预约成功会先落订单;clinical_ticket 不一定在此链路写入
|
||||
List<Integer> effectiveOrderStatuses = Arrays.asList(AppointmentOrderStatus.BOOKED, AppointmentOrderStatus.CHECKED_IN);
|
||||
int exists = orderMapper.countPatientDeptOrdersInPeriod(dto.getPatientId(), slot.getDepartmentId(), slot.getDepartmentName(),
|
||||
startTime, endTime, effectiveOrderStatuses);
|
||||
if (exists > 0) {
|
||||
@@ -314,8 +314,9 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
||||
}
|
||||
Order latestOrder = orders.get(0);
|
||||
|
||||
// 1. 签到不改变订单状态(仍为有效1),更新支付状态为已支付并记录支付时间
|
||||
orderService.updateOrderStatusById(latestOrder.getId(), OrderStatus.ACTIVE.getValue());
|
||||
// 1. 更新订单状态为已取号,并更新支付状态和支付时间
|
||||
orderService.updateOrderStatusById(latestOrder.getId(), AppointmentOrderStatus.CHECKED_IN);
|
||||
// 更新支付状态为已支付,记录支付时间
|
||||
orderMapper.updatePayStatus(latestOrder.getId(), 1, new Date());
|
||||
|
||||
// 2. 查询号源槽位信息
|
||||
|
||||
@@ -187,15 +187,6 @@ public class OpSchedule extends HisBaseEntity {
|
||||
/** 沟通信息 */
|
||||
private String communicationInfo;
|
||||
|
||||
/** 是否外请专家 1-是 0-否 */
|
||||
private Integer isExternalExpert;
|
||||
|
||||
/** 外请专家姓名 */
|
||||
private String externalExpertName;
|
||||
|
||||
/** 费用类别 */
|
||||
private String feeType;
|
||||
|
||||
/** 备注信息 */
|
||||
private String remark;
|
||||
|
||||
|
||||
@@ -160,12 +160,11 @@
|
||||
AND delete_flag = '0'
|
||||
</update>
|
||||
|
||||
<!-- status=0(待约)时清空order_id,释放号源,使退号后号源可再被预约 -->
|
||||
<update id="updateSlotStatus">
|
||||
UPDATE adm_schedule_slot
|
||||
SET
|
||||
status = #{status},
|
||||
<if test="status != null and status == 0">
|
||||
<if test="status != null and '0'.equals(status.toString())">
|
||||
order_id = NULL,
|
||||
</if>
|
||||
update_time = now()
|
||||
|
||||
@@ -117,14 +117,12 @@
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<!-- status=1: 只查有效订单(0=患者取消 1=有效 2=系统取消 3=已完成) -->
|
||||
<select id="selectOrderById" resultMap="OrderResult">
|
||||
select * from order_main where id = #{id}
|
||||
and status = 1
|
||||
order by create_time desc
|
||||
</select>
|
||||
|
||||
<!-- status=1: 只查有效订单 -->
|
||||
<select id="selectOrderBySlotId" resultMap="OrderResult">
|
||||
select * from order_main where slot_id = #{slotId} and status = 1
|
||||
</select>
|
||||
@@ -250,9 +248,8 @@
|
||||
update order_main set status = #{status} where id = #{id}
|
||||
</update>
|
||||
|
||||
<!-- status=0: 患者取消 (OrderStatus.PATIENT_CANCELLED) -->
|
||||
<update id="updateOrderCancelInfoById">
|
||||
update order_main set status = 0, cancel_time = #{cancelTime}, cancel_reason = #{cancelReason} where id = #{id}
|
||||
update order_main set status = 3, cancel_time = #{cancelTime}, cancel_reason = #{cancelReason} where id = #{id}
|
||||
</update>
|
||||
|
||||
<update id="updatePayStatus">
|
||||
|
||||
@@ -13,7 +13,7 @@ CREATE TABLE IF NOT EXISTS triage_queue_item (
|
||||
patient_name VARCHAR(255),
|
||||
healthcare_name VARCHAR(255),
|
||||
practitioner_name VARCHAR(255),
|
||||
status INTEGER NOT NULL DEFAULT 0, -- 分诊队列状态: 0=WAITING(等待), 10=CALLING(叫号中), 20=IN_CLINIC(诊中), 30=COMPLETED(已完成), 40=SKIPPED(已跳过)
|
||||
status VARCHAR(50) NOT NULL, -- WAITING/CALLING/SKIPPED/COMPLETED
|
||||
queue_order INTEGER NOT NULL,
|
||||
create_time TIMESTAMP,
|
||||
update_time TIMESTAMP,
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
-- Bug #400 修复:triage_queue_item.status 字段类型修正
|
||||
-- 原 DDL 将 status 定义为 VARCHAR(50),但 Java 实体 TriageQueueItem.status 为 Integer 类型,
|
||||
-- 应用层使用 TriageQueueStatus 枚举值(0/10/20/30/40)写入,类型不匹配导致 MyBatis-Plus
|
||||
-- 无法正确映射 status 字段,完诊时 status 更新为 30 失败。
|
||||
--
|
||||
-- 注意:必须在后端实际连接的 schema 中执行(dev=hisdev, test=histest, prd=hisprd)
|
||||
-- 执行前请先确认:SET search_path TO hisdev; (或对应的 schema)
|
||||
|
||||
-- 1. 先将已有的字符串值转换为对应的整数值
|
||||
-- 如果之前写入的是枚举 code(如 'waiting'、'completed'),需要先转换
|
||||
UPDATE triage_queue_item
|
||||
SET status = CASE
|
||||
WHEN status IN ('0', 'waiting', 'WAITING') THEN 0
|
||||
WHEN status IN ('10', 'calling', 'CALLING') THEN 10
|
||||
WHEN status IN ('20', 'in-clinic', 'IN_CLINIC', 'in-clinic') THEN 20
|
||||
WHEN status IN ('30', 'completed', 'COMPLETED') THEN 30
|
||||
WHEN status IN ('40', 'skipped', 'SKIPPED') THEN 40
|
||||
WHEN status IN ('50', 'refunded', 'REFUNDED') THEN 50
|
||||
WHEN status IN ('60', 'follow', 'FOLLOW') THEN 60
|
||||
ELSE 0
|
||||
END
|
||||
WHERE status IS NOT NULL AND status !~ '^[0-9]+$';
|
||||
|
||||
-- 2. 修改字段类型为 INTEGER
|
||||
ALTER TABLE triage_queue_item
|
||||
ALTER COLUMN status TYPE INTEGER USING status::INTEGER;
|
||||
|
||||
-- 3. 设置默认值
|
||||
ALTER TABLE triage_queue_item
|
||||
ALTER COLUMN status SET DEFAULT 0;
|
||||
@@ -38,6 +38,7 @@ export function listAdviceTypes(query) {
|
||||
params: query
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
/**
|
||||
* 查询手术室详细
|
||||
|
||||
@@ -163,7 +163,7 @@ export function updateCheckPart(data) {
|
||||
// 查询检查套餐列表
|
||||
export function listCheckPackage(query) {
|
||||
return request({
|
||||
url: '/system/package/list',
|
||||
url: '/system/check-package/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import useDictStore from '@/store/modules/dict';
|
||||
|
||||
// 日期格式化
|
||||
export function parseTime(time, pattern) {
|
||||
if (arguments.length === 0 || !time) {
|
||||
@@ -277,13 +275,30 @@ export function blobValidate(data) {
|
||||
|
||||
// 按照频次天数计算总数量
|
||||
export function calculateQuantityByDays(frequency, days) {
|
||||
const dicts = useDictStore().getDict('rate_code');
|
||||
if (!dicts) return;
|
||||
const dict = dicts.find(item => item.value === frequency);
|
||||
if (!dict?.remark) return;
|
||||
const rate = Number(dict.remark);
|
||||
if (isNaN(rate) || !rate) return;
|
||||
const quantity = rate * days;
|
||||
// const dict = useDict('rate_code').rate_code.value
|
||||
// const rate = dict.find(item => item.value === frequency).remark
|
||||
// if(rate){
|
||||
// return Math.floor(Number(rate) * days)
|
||||
// } else {
|
||||
// return undefined
|
||||
// }
|
||||
const frequencyMap = {
|
||||
ST: 1,
|
||||
QD: 1, // 每日一次
|
||||
BID: 2, // 每日两次
|
||||
TID: 3, // 每日三次
|
||||
QID: 4, // 每日四次
|
||||
QN: 1, // 每晚一次
|
||||
QOD: 1 / 2, // 每隔一日一次
|
||||
QW: 1 / 7, // 每周一次
|
||||
BIW: 2 / 7, // 每周两次
|
||||
TIW: 3 / 7, // 每周三次
|
||||
QOW: 1 / 14, // 隔周一次
|
||||
};
|
||||
if (!frequencyMap[frequency]) {
|
||||
return;
|
||||
}
|
||||
const quantity = frequencyMap[frequency] * days;
|
||||
return quantity < 1 ? 1 : Math.ceil(quantity);
|
||||
}
|
||||
|
||||
|
||||
@@ -178,25 +178,22 @@ service.interceptors.request.use(config => {
|
||||
}
|
||||
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
|
||||
} else if (code === 500) {
|
||||
// 检查是否需要跳过错误提示(静默请求:返回响应让.then()处理)
|
||||
if (res.config?.skipErrorMsg) {
|
||||
return Promise.resolve(res.data)
|
||||
// 检查是否需要跳过错误提示
|
||||
if (!res.config?.skipErrorMsg) {
|
||||
ElMessage({ message: msg, type: 'error' })
|
||||
}
|
||||
ElMessage({ message: msg, type: 'error' })
|
||||
return Promise.reject(new Error(msg))
|
||||
} else if (code === 601) {
|
||||
// 检查是否需要跳过错误提示(静默请求:返回响应让.then()处理)
|
||||
if (res.config?.skipErrorMsg) {
|
||||
return Promise.resolve(res.data)
|
||||
// 检查是否需要跳过错误提示
|
||||
if (!res.config?.skipErrorMsg) {
|
||||
ElMessage({ message: msg, type: 'warning' })
|
||||
}
|
||||
ElMessage({ message: msg, type: 'warning' })
|
||||
return Promise.reject(new Error(msg))
|
||||
} else if (code !== 200) {
|
||||
// 检查是否需要跳过错误提示(静默请求:返回响应让.then()处理)
|
||||
if (res.config?.skipErrorMsg) {
|
||||
return Promise.resolve(res.data)
|
||||
// 检查是否需要跳过错误提示
|
||||
if (!res.config?.skipErrorMsg) {
|
||||
ElNotification.error({ title: msg })
|
||||
}
|
||||
ElNotification.error({ title: msg })
|
||||
return Promise.reject('error')
|
||||
} else {
|
||||
return Promise.resolve(res.data)
|
||||
|
||||
@@ -226,14 +226,8 @@ function getList() {
|
||||
getDiagnosisTreatmentList(queryParams.value).then((res) => {
|
||||
loading.value = false;
|
||||
catagoryList.value = res.data.records.map(record => {
|
||||
// 为每一行初始化 filteredOptions,确保显示框能正确显示项目名称
|
||||
const filteredOptions = allImplementDepartmentList.value.slice(0, 100);
|
||||
// 确保后端返回的项目名称选项存在于 filteredOptions 中,避免 el-select 因找不到选项而回显为 ID
|
||||
if (record.activityDefinitionId && !filteredOptions.some(o => o.value === record.activityDefinitionId)) {
|
||||
filteredOptions.push({
|
||||
value: record.activityDefinitionId,
|
||||
label: record.activityDefinitionId_dictText || record.activityDefinitionId
|
||||
});
|
||||
}
|
||||
return {
|
||||
...record,
|
||||
loading: false,
|
||||
|
||||
@@ -372,6 +372,7 @@ import {
|
||||
} from './diagnosistreatment';
|
||||
import PopoverList from '@/components/OpenHis/popoverList/index.vue';
|
||||
import medicineList from './medicineList.vue';
|
||||
import MedicineList from '../components/medicineList.vue';
|
||||
import {getCurrentInstance, nextTick, watch} from 'vue';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
@@ -466,21 +467,16 @@ function calculateTotalPrice() {
|
||||
try {
|
||||
let sum = 0;
|
||||
treatmentItems.value.forEach((item) => {
|
||||
if (item.adviceDefinitionId && item.adviceDefinitionId !== '') {
|
||||
const price = Number(item.retailPrice) || 0;
|
||||
const count = Number(item.childrenRequestNum) || 0;
|
||||
if (item.adviceDefinitionId && item.retailPrice && item.childrenRequestNum) {
|
||||
const price = parseFloat(item.retailPrice) || 0;
|
||||
const count = parseInt(item.childrenRequestNum) || 0;
|
||||
sum += price * count;
|
||||
}
|
||||
});
|
||||
totalPrice.value = sum.toFixed(2);
|
||||
// Bug #464: 零售价与诊疗子项合计总价实时同步,直接赋值不使用nextTick避免多调用方竞争
|
||||
const hasValidItem = treatmentItems.value.some(
|
||||
(item) => item.adviceDefinitionId && item.adviceDefinitionId !== ''
|
||||
);
|
||||
if (hasValidItem) {
|
||||
form.value.retailPrice = parseFloat(totalPrice.value) || 0;
|
||||
} else {
|
||||
form.value.retailPrice = undefined;
|
||||
// Bug #464: 零售价与诊疗子项合计总价实时同步
|
||||
if (treatmentItems.value.length > 0 && treatmentItems.value[0].adviceDefinitionId !== '') {
|
||||
form.value.retailPrice = parseFloat(totalPrice.value);
|
||||
}
|
||||
} catch (error) {
|
||||
totalPrice.value = '0.00';
|
||||
@@ -490,7 +486,7 @@ function calculateTotalPrice() {
|
||||
|
||||
// 添加表单项
|
||||
function addItem() {
|
||||
treatmentItems.value.push({ adviceDefinitionId: '', childrenRequestNum: 1, name: '', retailPrice: 0 });
|
||||
treatmentItems.value.push({ adviceDefinitionId: '', childrenRequestNum: 1, retailPrice: 0 });
|
||||
// 使用nextTick确保DOM更新后再计算
|
||||
nextTick(() => {
|
||||
calculateTotalPrice();
|
||||
@@ -564,16 +560,15 @@ function edit() {
|
||||
form.value.pricingFlag = 1;
|
||||
}
|
||||
|
||||
// 处理子项数据,确保包含retailPrice和name字段
|
||||
// 处理子项数据,确保包含retailPrice字段
|
||||
if (props.item.childrenJson) {
|
||||
const parsedItems = JSON.parse(props.item.childrenJson);
|
||||
treatmentItems.value = parsedItems.map((item) => ({
|
||||
...item,
|
||||
name: item.name || '',
|
||||
retailPrice: item.retailPrice || 0,
|
||||
}));
|
||||
} else {
|
||||
treatmentItems.value = [{ adviceDefinitionId: '', childrenRequestNum: 1, name: '', retailPrice: 0 }];
|
||||
treatmentItems.value = [{ adviceDefinitionId: '', childrenRequestNum: 1, retailPrice: 0 }];
|
||||
}
|
||||
form.value.permittedUnitCode = form.value.permittedUnitCode
|
||||
? form.value.permittedUnitCode.toString()
|
||||
@@ -618,7 +613,7 @@ function reset() {
|
||||
chrgitmLv: undefined, //医保等级
|
||||
pricingFlag: 1, // 划价标记,默认允许划价
|
||||
};
|
||||
treatmentItems.value = [{ adviceDefinitionId: '', childrenRequestNum: 1, name: '', retailPrice: 0 }];
|
||||
treatmentItems.value = [{ adviceDefinitionId: '', childrenRequestNum: 1, retailPrice: 0 }];
|
||||
totalPrice.value = '0.00';
|
||||
proxy.resetForm('diagnosisTreatmentRef');
|
||||
}
|
||||
@@ -652,15 +647,12 @@ async function submitForm() {
|
||||
form.value.ybMatchFlag ? (form.value.ybMatchFlag = 1) : (form.value.ybMatchFlag = 0);
|
||||
form.value.ruleId ? (form.value.ruleId = 1) : (form.value.ruleId = 0);
|
||||
form.value.childrenJson =
|
||||
treatmentItems.value.some((item) => item.adviceDefinitionId != '' && item.adviceDefinitionId)
|
||||
treatmentItems.value.length > 0 && treatmentItems.value[0].adviceDefinitionId != ''
|
||||
? JSON.stringify(treatmentItems.value)
|
||||
: undefined;
|
||||
// Bug #464 修复:零售价自动与诊疗子项合计总价同步
|
||||
// 当有子项时,零售价自动设置为子项合计总价
|
||||
const hasValidItem = treatmentItems.value.some(
|
||||
(item) => item.adviceDefinitionId && item.adviceDefinitionId !== ''
|
||||
);
|
||||
if (hasValidItem) {
|
||||
if (treatmentItems.value.length > 0 && treatmentItems.value[0].adviceDefinitionId != '') {
|
||||
form.value.retailPrice = parseFloat(totalPrice.value) || 0;
|
||||
}
|
||||
proxy.$refs['diagnosisTreatmentRef'].validate(async (valid) => {
|
||||
|
||||
@@ -36,7 +36,6 @@ const emit = defineEmits(['selectRow']);
|
||||
const diagnosisTreatmentList = ref([]); // 原始数据列表
|
||||
const filteredList = ref([]); // 过滤后的数据列表
|
||||
const hasLoaded = ref(false); // 标记是否已加载数据
|
||||
const isLoading = ref(false); // 标记是否正在加载
|
||||
|
||||
// 获取诊疗项目列表
|
||||
function getList() {
|
||||
@@ -54,7 +53,7 @@ function getList() {
|
||||
getDiagnosisTreatmentList({ statusEnum: 2, pageSize: 1000, pageNo: 1 })
|
||||
.then((res) => {
|
||||
diagnosisTreatmentList.value =
|
||||
res.data?.records?.filter((item) => item.childrenJson == null || item.childrenJson === '') || [];
|
||||
res.data?.records?.filter((item) => item.childrenJson == null) || [];
|
||||
filterList(); // 初始化过滤
|
||||
hasLoaded.value = true; // 标记为已加载
|
||||
})
|
||||
@@ -63,47 +62,6 @@ function getList() {
|
||||
});
|
||||
}
|
||||
|
||||
// 服务端搜索(当用户输入搜索关键词时)
|
||||
function searchList(searchKey) {
|
||||
if (!searchKey || searchKey.trim() === '') return;
|
||||
isLoading.value = true;
|
||||
// 使用较大的pageSize确保搜索结果尽可能多
|
||||
getDiagnosisTreatmentList({ statusEnum: 2, searchKey: searchKey.trim(), pageSize: 5000, pageNo: 1 })
|
||||
.then((res) => {
|
||||
diagnosisTreatmentList.value =
|
||||
res.data?.records?.filter((item) => item.childrenJson == null || item.childrenJson === '') || [];
|
||||
filterList();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('搜索诊疗项目数据失败:', err);
|
||||
})
|
||||
.finally(() => {
|
||||
isLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 获取预加载数据(不带搜索关键词时使用)
|
||||
function loadPreloadedData() {
|
||||
if (props.preloadedData && props.preloadedData.length > 0) {
|
||||
diagnosisTreatmentList.value = props.preloadedData;
|
||||
filterList();
|
||||
hasLoaded.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasLoaded.value) return;
|
||||
getDiagnosisTreatmentList({ statusEnum: 2, pageSize: 1000, pageNo: 1 })
|
||||
.then((res) => {
|
||||
diagnosisTreatmentList.value =
|
||||
res.data?.records?.filter((item) => item.childrenJson == null || item.childrenJson === '') || [];
|
||||
filterList();
|
||||
hasLoaded.value = true;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('获取诊疗项目数据失败:', err);
|
||||
});
|
||||
}
|
||||
|
||||
// 监听shouldLoadData属性变化,仅在首次为true时加载数据
|
||||
watch(
|
||||
() => props.shouldLoadData,
|
||||
@@ -128,17 +86,11 @@ watch(
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 监听搜索关键词变化,有搜索词时走服务端搜索,否则走本地过滤
|
||||
// 监听搜索关键词变化,实时过滤数据
|
||||
watch(
|
||||
() => props.searchKey,
|
||||
(newVal) => {
|
||||
if (newVal && newVal.trim() !== '') {
|
||||
// 有搜索关键词,走服务端搜索
|
||||
searchList(newVal);
|
||||
} else {
|
||||
// 搜索词为空,使用预加载数据
|
||||
loadPreloadedData();
|
||||
}
|
||||
() => {
|
||||
filterList();
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -69,14 +69,13 @@ const throttledGetList = throttle(
|
||||
watch(
|
||||
() => props.adviceQueryParams,
|
||||
(newValue) => {
|
||||
// 始终同步参数到 queryParams,避免弹窗打开时使用旧参数
|
||||
queryParams.value.searchKey = newValue?.searchKey;
|
||||
queryParams.value.adviceType = newValue?.adviceType;
|
||||
queryParams.value.categoryCode = newValue?.categoryCode;
|
||||
// 只有在弹窗打开时才触发 API 请求
|
||||
// 只有在弹窗打开时才响应 adviceQueryParams 的变化,避免选择项目后弹窗关闭时触发不必要的请求
|
||||
if (!props.popoverVisible) {
|
||||
return;
|
||||
}
|
||||
queryParams.value.searchKey = newValue?.searchKey;
|
||||
queryParams.value.adviceType = newValue?.adviceType;
|
||||
queryParams.value.categoryCode = newValue?.categoryCode;
|
||||
throttledGetList();
|
||||
},
|
||||
{ deep: true }
|
||||
@@ -115,44 +114,22 @@ function getList() {
|
||||
console.log('[adviceBaseList] getList() 跳过:未选择患者');
|
||||
return; // 不执行API调用
|
||||
}
|
||||
|
||||
|
||||
// 只有在弹窗打开时才执行查询
|
||||
if (!props.popoverVisible) {
|
||||
console.log('[adviceBaseList] getList() 跳过:弹窗未打开');
|
||||
return;
|
||||
}
|
||||
|
||||
// 🔧 Bug #448 修复:显式构建请求参数,确保 adviceType 正确传递
|
||||
// 不直接使用 queryParams.value,避免 undefined 值被发送到后端导致过滤失效
|
||||
const requestParams = {
|
||||
pageSize: queryParams.value.pageSize,
|
||||
pageNum: queryParams.value.pageNum,
|
||||
organizationId: props.patientInfo.orgId,
|
||||
};
|
||||
|
||||
// 只在 adviceType 有值时添加(0 是无效值,undefined/null 会导致后端查询所有类型)
|
||||
if (queryParams.value.adviceType != null && queryParams.value.adviceType !== 0) {
|
||||
requestParams.adviceType = queryParams.value.adviceType;
|
||||
}
|
||||
|
||||
// 只在 categoryCode 有值时添加
|
||||
if (queryParams.value.categoryCode) {
|
||||
requestParams.categoryCode = queryParams.value.categoryCode;
|
||||
}
|
||||
|
||||
// 只在 searchKey 有值时添加
|
||||
if (queryParams.value.searchKey) {
|
||||
requestParams.searchKey = queryParams.value.searchKey;
|
||||
}
|
||||
|
||||
console.log('[adviceBaseList] getList() 请求参数:', JSON.stringify(requestParams));
|
||||
|
||||
getAdviceBaseInfo(requestParams).then((res) => {
|
||||
|
||||
queryParams.value.organizationId = props.patientInfo.orgId;
|
||||
console.log('[adviceBaseList] getList() 请求参数:', JSON.stringify(queryParams.value));
|
||||
|
||||
getAdviceBaseInfo(queryParams.value).then((res) => {
|
||||
console.log('[adviceBaseList] getList() 响应数据:', {
|
||||
total: res.data?.total,
|
||||
recordsCount: res.data?.records?.length || 0,
|
||||
firstRecord: res.data?.records?.[0]?.adviceName || '无数据',
|
||||
adviceType: requestParams.adviceType
|
||||
adviceType: queryParams.value.adviceType
|
||||
});
|
||||
adviceBaseList.value = res.data.records || [];
|
||||
total.value = res.data.total || 0;
|
||||
|
||||
@@ -58,17 +58,10 @@ export function singOut(data) {
|
||||
/**
|
||||
* 获取患者本次就诊处方
|
||||
*/
|
||||
export function getPrescriptionList(encounterId, generateSourceEnum, sourceBillNo) {
|
||||
let url = '/doctor-station/advice/request-base-info?encounterId=' + encounterId
|
||||
if (generateSourceEnum != null) {
|
||||
url += '&generateSourceEnum=' + generateSourceEnum
|
||||
}
|
||||
if (sourceBillNo != null) {
|
||||
url += '&sourceBillNo=' + encodeURIComponent(sourceBillNo)
|
||||
}
|
||||
url += '&t=' + Date.now()
|
||||
export function getPrescriptionList(encounterId) {
|
||||
// Add timestamp to bypass browser caching and ensure fresh data is loaded
|
||||
return request({
|
||||
url: url,
|
||||
url: '/doctor-station/advice/request-base-info?encounterId=' + encounterId + '&t=' + Date.now(),
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
@@ -381,14 +381,6 @@ const props = defineProps({
|
||||
activeTab: {
|
||||
type: String,
|
||||
},
|
||||
generateSourceEnum: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
sourceBillNo: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
const isAdding = ref(false);
|
||||
const isSaving = ref(false); // #437 防重复提交锁
|
||||
@@ -461,7 +453,7 @@ watch(
|
||||
console.log(prescriptionList.value,"prescriptionList.value")
|
||||
if(newValue&&newValue.length>0){
|
||||
let saveList = prescriptionList.value.filter((item) => {
|
||||
return item.check && item.statusEnum == 1&&(Number(item.bizRequestFlag)==1||!item.bizRequestFlag)
|
||||
return item.statusEnum == 1&&(Number(item.bizRequestFlag)==1||!item.bizRequestFlag)
|
||||
})
|
||||
console.log(saveList,"prescriptionList.value")
|
||||
if (saveList.length == 0) {
|
||||
@@ -476,11 +468,7 @@ watch(
|
||||
|
||||
function getListInfo(addNewRow) {
|
||||
isAdding.value = false;
|
||||
getPrescriptionList(
|
||||
props.patientInfo.encounterId,
|
||||
props.generateSourceEnum ?? undefined,
|
||||
props.sourceBillNo ?? undefined,
|
||||
).then((res) => {
|
||||
getPrescriptionList(props.patientInfo.encounterId).then((res) => {
|
||||
// 为每行数据添加 adviceTypeValue 字段,用于类型下拉框显示
|
||||
prescriptionList.value = (res.data || []).map(item => {
|
||||
// 根据 adviceType 和 categoryCode 找到对应的 adviceTypeValue
|
||||
@@ -880,27 +868,19 @@ function handleDelete() {
|
||||
proxy.$modal.msgWarning('请选择要删除的项目');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let deleteList = groupIndexList.value.map((index) => {
|
||||
const item = prescriptionList.value[index];
|
||||
// 只删除待签发且未收费的项目
|
||||
if (item.statusEnum != 1 || item.chargeStatus == 5) {
|
||||
return null;
|
||||
}
|
||||
// 🔧 Bug #442: 非本人创建的医嘱不允许删除(与签发/签退逻辑保持一致)
|
||||
if (Number(item.bizRequestFlag) !== 1 && item.bizRequestFlag) {
|
||||
return null;
|
||||
}
|
||||
// 🔧 Bug #442: 已保存的行必须有有效的 requestId,否则跳过(避免后端删除不存在的记录)
|
||||
if (item.requestId == null || item.requestId === undefined || item.requestId === '') {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
requestId: item.requestId,
|
||||
dbOpType: '3',
|
||||
adviceType: item.adviceType,
|
||||
};
|
||||
}).filter(item => item !== null); // 过滤掉已签发、已收费、非本人创建或无 requestId 的项目
|
||||
}).filter(item => item !== null); // 过滤掉已签发或已收费的项目
|
||||
|
||||
if (deleteList.length == 0) {
|
||||
proxy.$modal.msgWarning('只能删除待签发且未收费的项目');
|
||||
@@ -1015,7 +995,7 @@ function handleSave() {
|
||||
return;
|
||||
}
|
||||
let saveList = prescriptionList.value.filter((item) => {
|
||||
return item.check && item.statusEnum == 1&&(Number(item.bizRequestFlag)==1||!item.bizRequestFlag)
|
||||
return item.statusEnum == 1&&(Number(item.bizRequestFlag)==1||!item.bizRequestFlag)
|
||||
});
|
||||
// let saveList = prescriptionList.value
|
||||
// .filter((item) => {
|
||||
@@ -1036,20 +1016,10 @@ function handleSave() {
|
||||
requestId: item.requestId,
|
||||
dbOpType: '1',
|
||||
groupId: item.groupId,
|
||||
// 🔧 Bug #443: 补充顶层关键字段(这些不在 contentJson 中,需从 API 响应顶层提取)
|
||||
encounterId: item.encounterId,
|
||||
patientId: item.patientId,
|
||||
locationId: item.positionId,
|
||||
adviceType: item.adviceType,
|
||||
adviceTableName: item.adviceTableName,
|
||||
adviceDefinitionId: item.adviceDefinitionId,
|
||||
chargeItemId: item.chargeItemId,
|
||||
};
|
||||
});
|
||||
// 确保 organizationId 不为 undefined(手术计费场景下可能缺失 orgId)
|
||||
const orgId = props.patientInfo.orgId || props.patientInfo.effectiveOrgId || 1;
|
||||
savePrescriptionSign({
|
||||
organizationId: orgId,
|
||||
organizationId: props.patientInfo.orgId,
|
||||
adviceSaveList: list,
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
@@ -1061,13 +1031,7 @@ function handleSave() {
|
||||
groupIndexList.value = []
|
||||
groupList.value = []
|
||||
nextId.value = 1;
|
||||
} else {
|
||||
proxy.$modal.msgError(res?.msg || '签发失败,请重试');
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error('签发失败:', error);
|
||||
console.warn('签发操作失败(可能无权限或后端异常):', error?.response?.data?.msg || error?.message);
|
||||
proxy.$modal.msgError(error?.response?.data?.msg || error?.message || '签发失败,请重试');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1083,44 +1047,42 @@ function handleSaveSign(row, index) {
|
||||
proxy.$modal.msgWarning('诊疗项目必须选择执行科室');
|
||||
return;
|
||||
}
|
||||
isSaving.value = true; // #437 立即加锁,消除 TOCTOU 竞态
|
||||
proxy.$refs['formRef' + index].validate((valid) => {
|
||||
if (!valid) {
|
||||
isSaving.value = false; // 验证失败释放锁
|
||||
return;
|
||||
}
|
||||
row.isEdit = false;
|
||||
isAdding.value = false;
|
||||
expandOrder.value = [];
|
||||
row.patientId = props.patientInfo.patientId;
|
||||
row.encounterId = props.patientInfo.encounterId;
|
||||
row.accountId = props.patientInfo.accountId;
|
||||
const cleanRow = JSON.parse(JSON.stringify(row));
|
||||
cleanRow.contentJson = JSON.stringify(cleanRow);
|
||||
cleanRow.dbOpType = cleanRow.requestId ? '2' : '1';
|
||||
cleanRow.minUnitQuantity = cleanRow.quantity * cleanRow.partPercent;
|
||||
cleanRow.categoryEnum = cleanRow.adviceType
|
||||
// 如果是手术计费,设置生成来源和来源业务单据号
|
||||
if (props.patientInfo.sourceBillNo) {
|
||||
cleanRow.generateSourceEnum = 6; // 手术计费
|
||||
cleanRow.sourceBillNo = props.patientInfo.sourceBillNo;
|
||||
}
|
||||
console.log('cleanRow', cleanRow)
|
||||
savePrescription({ adviceSaveList: [cleanRow] }, '1').then((res) => {
|
||||
if (res.code === 200) {
|
||||
proxy.$modal.msgSuccess('保存成功');
|
||||
getListInfo(false);
|
||||
nextId.value = 1;
|
||||
// 🔧 Bug Fix #238: 如果诊疗项目缺少执行科室,标记为需要修复的脏数据
|
||||
if (row.adviceType === 3 && !row.orgId) {
|
||||
console.warn('Bug #238: 检测到诊疗项目保存时缺少执行科室,请手动编辑修正:', cleanRow);
|
||||
proxy.$modal.msgWarning('诊疗项目执行科室信息不完整,请编辑后重新保存');
|
||||
}
|
||||
if (valid) {
|
||||
isSaving.value = true; // #437 加锁
|
||||
row.isEdit = false;
|
||||
isAdding.value = false;
|
||||
expandOrder.value = [];
|
||||
row.patientId = props.patientInfo.patientId;
|
||||
row.encounterId = props.patientInfo.encounterId;
|
||||
row.accountId = props.patientInfo.accountId;
|
||||
const cleanRow = JSON.parse(JSON.stringify(row));
|
||||
cleanRow.contentJson = JSON.stringify(cleanRow);
|
||||
cleanRow.dbOpType = cleanRow.requestId ? '2' : '1';
|
||||
cleanRow.minUnitQuantity = cleanRow.quantity * cleanRow.partPercent;
|
||||
cleanRow.categoryEnum = cleanRow.adviceType
|
||||
// 如果是手术计费,设置生成来源和来源业务单据号
|
||||
if (props.patientInfo.sourceBillNo) {
|
||||
cleanRow.generateSourceEnum = 6; // 手术计费
|
||||
cleanRow.sourceBillNo = props.patientInfo.sourceBillNo;
|
||||
}
|
||||
}).finally(() => {
|
||||
isSaving.value = false; // #437 释放锁
|
||||
});
|
||||
})
|
||||
console.log('cleanRow', cleanRow)
|
||||
savePrescription({ adviceSaveList: [cleanRow] }).then((res) => {
|
||||
if (res.code === 200) {
|
||||
proxy.$modal.msgSuccess('保存成功');
|
||||
getListInfo(false);
|
||||
nextId.value = 1;
|
||||
// 🔧 Bug Fix #238: 如果诊疗项目缺少执行科室,标记为需要修复的脏数据
|
||||
if (row.adviceType === 3 && !row.orgId) {
|
||||
console.warn('Bug #238: 检测到诊疗项目保存时缺少执行科室,请手动编辑修正:', cleanRow);
|
||||
proxy.$modal.msgWarning('诊疗项目执行科室信息不完整,请编辑后重新保存');
|
||||
}
|
||||
}
|
||||
}).finally(() => {
|
||||
isSaving.value = false; // #437 释放锁
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 签退
|
||||
@@ -1184,13 +1146,7 @@ function calculateTotalPrice(row, index) {
|
||||
});
|
||||
}
|
||||
|
||||
function closeAllPopovers() {
|
||||
prescriptionList.value.forEach(row => {
|
||||
row.showPopover = false;
|
||||
});
|
||||
}
|
||||
|
||||
defineExpose({ getListInfo, closeAllPopovers });
|
||||
defineExpose({ getListInfo });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -519,10 +519,9 @@
|
||||
>
|
||||
<template #append>
|
||||
<!-- 审核记录:查看/审核都展示,保证留痕可追溯 -->
|
||||
<!-- 修复:查看模式始终显示区块;审核模式始终显示区块(header + 空状态 或 timeline) -->
|
||||
<div
|
||||
class="audit-records-section"
|
||||
v-if="drawerMode === 'view' || drawerMode === 'audit'"
|
||||
v-if="drawerMode === 'view' || (drawerMode === 'audit' && auditRecords.length > 0)"
|
||||
>
|
||||
<h3 class="section-title">审核记录</h3>
|
||||
<el-timeline v-if="auditRecords.length > 0">
|
||||
|
||||
@@ -127,7 +127,6 @@ const queryParams = ref({
|
||||
pageSize: 100,
|
||||
pageNo: 1,
|
||||
adviceTypes: '1,2,3',
|
||||
categoryCode: '',
|
||||
});
|
||||
|
||||
// 节流函数 - 与V1.3一致:300ms,首次立即响应
|
||||
@@ -149,7 +148,6 @@ watch(
|
||||
} else {
|
||||
queryParams.value.adviceTypes = '1,2,3';
|
||||
}
|
||||
queryParams.value.categoryCode = newValue.categoryCode || '';
|
||||
throttledGetList();
|
||||
},
|
||||
{ deep: true }
|
||||
@@ -177,12 +175,6 @@ function getList() {
|
||||
const types = adviceTypes.split(',').map(t => parseInt(t));
|
||||
filteredData = filteredData.filter(item => types.includes(item.adviceType));
|
||||
}
|
||||
|
||||
// 根据 categoryCode 过滤(如西药='2',中成药='1')
|
||||
const categoryCode = queryParams.value.categoryCode;
|
||||
if (categoryCode) {
|
||||
filteredData = filteredData.filter(item => String(item.categoryCode) === String(categoryCode));
|
||||
}
|
||||
|
||||
// 根据搜索关键词过滤
|
||||
if (searchKey && searchKey.length >= 1) {
|
||||
|
||||
@@ -375,7 +375,7 @@ const finishCallPatient = async () => {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await completeEncounter({ encounterId: currentCallPatient.value.encounterId, firstEnum: currentCallPatient.value.firstEnum || 1 });
|
||||
await completeEncounter(currentCallPatient.value.encounterId);
|
||||
emit('finish');
|
||||
emit('update:dialogVisible', false);
|
||||
ElMessage.success('患者已完诊');
|
||||
|
||||
@@ -349,17 +349,11 @@ async function getList() {
|
||||
if (res.code == 200) {
|
||||
// 过滤掉中医诊断,只保留西医诊断
|
||||
form.value.diagnosisList = res.data.filter(item => item.typeName !== '中医诊断');
|
||||
// 为旧数据添加默认分类和selectedDiseases
|
||||
// 为旧数据添加默认分类
|
||||
form.value.diagnosisList.forEach(item => {
|
||||
if (!item.classification) {
|
||||
item.classification = '西医';
|
||||
}
|
||||
// 如果ybNo(诊断编码)符合传染病编码格式,添加到selectedDiseases
|
||||
if (item.ybNo && /^(01|02|03)/.test(item.ybNo)) {
|
||||
item.selectedDiseases = [item.ybNo];
|
||||
} else {
|
||||
item.selectedDiseases = item.selectedDiseases || [];
|
||||
}
|
||||
});
|
||||
emits('diagnosisSave', false);
|
||||
}
|
||||
@@ -691,80 +685,23 @@ async function handleFoodDiseasesCheck() {
|
||||
|
||||
/**
|
||||
* 传染病报告卡处理
|
||||
* 通过诊断名称自动识别并勾选传染病报告卡中的疾病
|
||||
* 修复 Bug #519:跳过已有已提交报卡的诊断
|
||||
* 通过诊断目录维护的'报卡类型'字段自动识别是否有需要填写的传染病报告卡
|
||||
* 如果有则弹出诊断对应需登记的报告卡界面
|
||||
*/
|
||||
function handleInfectiousDiseaseReport() {
|
||||
// 疾病名称到报卡编码的映射(根据传染病报告卡弹窗中的疾病列表)
|
||||
const diseaseNameToCode = {
|
||||
// 甲类
|
||||
'鼠疫': '0101',
|
||||
'霍乱': '0102',
|
||||
// 乙类
|
||||
'传染性非典型肺炎': '0201',
|
||||
'艾滋病': '0202',
|
||||
'病毒性肝炎': '0203',
|
||||
'脊髓灰质炎': '0204',
|
||||
'人感染高致病性禽流感': '0205',
|
||||
'麻疹': '0206',
|
||||
'流行性出血热': '0207',
|
||||
'狂犬病': '0208',
|
||||
'流行性乙型脑炎': '0209',
|
||||
'登革热': '0210',
|
||||
'炭疽': '0211',
|
||||
'细菌性和阿米巴性痢疾': '0212',
|
||||
'肺结核': '0213',
|
||||
'伤寒和副伤寒': '0214',
|
||||
'流行性脑脊髓膜炎': '0215',
|
||||
'百日咳': '0216',
|
||||
'白喉': '0217',
|
||||
'新生儿破伤风': '0218',
|
||||
'猩红热': '0219',
|
||||
'布鲁氏菌病': '0220',
|
||||
'淋病': '0221',
|
||||
'梅毒': '0222',
|
||||
'钩端螺旋体病': '0223',
|
||||
'血吸虫病': '0224',
|
||||
'疟疾': '0225',
|
||||
'新型冠状病毒肺炎': '0226',
|
||||
'甲型H1N1流感': '0227',
|
||||
'人感染H7N9禽流感': '0228',
|
||||
// 丙类
|
||||
'流行性感冒': '0301',
|
||||
'流行性腮腺炎': '0302',
|
||||
'风疹': '0303',
|
||||
'急性出血性结膜炎': '0304',
|
||||
'麻风病': '0305',
|
||||
'流行性和地方性斑疹伤寒': '0306',
|
||||
'黑热病': '0307',
|
||||
'包虫病': '0308',
|
||||
'丝虫病': '0309',
|
||||
'除霍乱/菌痢/伤寒副伤寒以外的感染性腹泻病': '0310',
|
||||
'其它感染性腹泻病': '0310',
|
||||
'手足口病': '0311',
|
||||
};
|
||||
// 查找所有有报卡类型的诊断(reportTypeCode不为空)
|
||||
const diagnosesWithReportType = form.value.diagnosisList.filter(d => d.reportTypeCode);
|
||||
|
||||
// 获取所有诊断名称对应的报卡编码,但跳过已有已提交报卡的诊断
|
||||
const allSelectedDiseases = form.value.diagnosisList
|
||||
.filter(d => d.name && d.hasInfectiousReport !== 1)
|
||||
.map(d => diseaseNameToCode[d.name] || null)
|
||||
.filter(code => code);
|
||||
|
||||
if (allSelectedDiseases.length === 0) {
|
||||
if (diagnosesWithReportType.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 优先使用主诊断(同样跳过已有报卡的)
|
||||
const mainDiagnosis = form.value.diagnosisList.find(d => d.maindiseFlag === 1 && d.hasInfectiousReport !== 1);
|
||||
const firstDiagnosis = form.value.diagnosisList.find(d => d.hasInfectiousReport !== 1) || form.value.diagnosisList[0];
|
||||
|
||||
const diagnosisToShow = {
|
||||
...(mainDiagnosis || firstDiagnosis),
|
||||
selectedDiseases: allSelectedDiseases
|
||||
};
|
||||
// 优先使用主诊断,如果没有主诊断有报卡类型则使用第一个有报卡类型的诊断
|
||||
const mainDiagnosisWithReport = diagnosesWithReportType.find(d => d.maindiseFlag === 1);
|
||||
const targetDiagnosis = mainDiagnosisWithReport || diagnosesWithReportType[0];
|
||||
|
||||
// 弹出传染病报告卡弹窗
|
||||
proxy.$refs.infectiousDiseaseReportRef?.show(diagnosisToShow);
|
||||
proxy.$refs.infectiousDiseaseReportRef?.show(targetDiagnosis);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -877,8 +814,7 @@ form.value.diagnosisList.push({
|
||||
classification: '西医', // 默认为西医
|
||||
onsetDate: getCurrentDate(),
|
||||
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
|
||||
diagnosisTime: getCurrentDate(),
|
||||
selectedDiseases: data.ybNo ? [data.ybNo] : [], // 用于传染病报告卡自动勾选
|
||||
diagnosisTime: getCurrentDate()
|
||||
});
|
||||
|
||||
// 添加后按排序号排序
|
||||
|
||||
@@ -16,15 +16,15 @@
|
||||
v-model="form.cardNo"
|
||||
class="card-number-input"
|
||||
placeholder="单位自编,与网络直报一致"
|
||||
maxlength="20"
|
||||
:disabled="readOnly || dialogReadOnly"
|
||||
maxlength="12"
|
||||
:disabled="readOnly"
|
||||
/>
|
||||
</el-space>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<el-card class="report-form" shadow="never">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-position="top" :disabled="readOnly || dialogReadOnly">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-position="top" :disabled="readOnly">
|
||||
<!-- 患者姓名、家长姓名、身份证号 -->
|
||||
<el-row :gutter="16" class="form-row">
|
||||
<el-col :span="8" class="form-item">
|
||||
@@ -41,39 +41,31 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 工作单位(学校) -->
|
||||
<el-row :gutter="16" class="form-row">
|
||||
<el-col :span="12" class="form-item">
|
||||
<span class="form-label">工作单位(学校)</span>
|
||||
<el-input v-model="form.workplace" class="underline-input" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 性别、出生日期、或实足年龄 -->
|
||||
<el-row :gutter="16" class="form-row">
|
||||
<el-col :span="7" class="form-item">
|
||||
<!-- 性别、出生日期、实足年龄 -->
|
||||
<el-row :gutter="16" class="form-row align-bottom">
|
||||
<el-col :span="6" class="form-item">
|
||||
<span class="form-label required">性别</span>
|
||||
<el-radio-group v-model="form.sex" class="gender-radio-group">
|
||||
<el-radio value="男">男</el-radio>
|
||||
<el-radio value="女">女</el-radio>
|
||||
<el-radio value="未知">未知</el-radio>
|
||||
<el-radio-group v-model="form.sex">
|
||||
<el-radio label="男">男</el-radio>
|
||||
<el-radio label="女">女</el-radio>
|
||||
<el-radio label="未知">未知</el-radio>
|
||||
</el-radio-group>
|
||||
</el-col>
|
||||
<el-col :span="10" class="form-item">
|
||||
<el-col :span="10" class="form-item birth-date">
|
||||
<span class="form-label required">出生日期</span>
|
||||
<div class="birth-input-group">
|
||||
<el-input v-model="form.birthYear" class="birth-input year" placeholder="年" maxlength="4" />
|
||||
<span class="birth-separator">年</span>
|
||||
<el-input v-model="form.birthMonth" class="birth-input month" placeholder="月" maxlength="2" />
|
||||
<span class="birth-separator">月</span>
|
||||
<el-input v-model="form.birthDay" class="birth-input day" placeholder="日" maxlength="2" />
|
||||
<span class="birth-separator">日</span>
|
||||
<div class="date-inputs">
|
||||
<el-input v-model="form.birthYear" class="underline-input small" maxlength="4" @change="calculateAge" />
|
||||
<span>年</span>
|
||||
<el-input v-model="form.birthMonth" class="underline-input small" maxlength="2" @change="calculateAge" />
|
||||
<span>月</span>
|
||||
<el-input v-model="form.birthDay" class="underline-input small" maxlength="2" @change="calculateAge" />
|
||||
<span>日</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="7" class="form-item">
|
||||
<el-col :span="8" class="form-item age-item">
|
||||
<span class="form-label">或 实足年龄</span>
|
||||
<div class="age-input-group">
|
||||
<el-input v-model="form.age" class="age-input" placeholder="年龄" />
|
||||
<div class="age-inputs">
|
||||
<el-input v-model="form.age" class="underline-input small"/>
|
||||
<el-select v-model="form.ageUnit" class="age-unit-select">
|
||||
<el-option label="岁" value="岁" />
|
||||
<el-option label="月" value="月" />
|
||||
@@ -83,6 +75,14 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 工作单位 -->
|
||||
<el-row :gutter="16" class="form-row">
|
||||
<el-col :span="24" class="form-item full-width">
|
||||
<span class="form-label">工作单位(学校)</span>
|
||||
<el-input v-model="form.workplace" class="underline-input" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 联系电话、紧急联系人电话 -->
|
||||
<el-row :gutter="16" class="form-row">
|
||||
<el-col :span="12" class="form-item">
|
||||
@@ -510,9 +510,9 @@
|
||||
<template #footer>
|
||||
<slot name="footer" :close="handleClose" :submit-loading="submitLoading">
|
||||
<el-space :size="16" justify="center" class="dialog-footer-space" style="display: flex; justify-content: center; width: 100%;">
|
||||
<el-button v-if="!(readOnly || dialogReadOnly)" type="primary" @click="handleSubmit" :loading="submitLoading" class="blue-button">保 存</el-button>
|
||||
<el-button v-if="!readOnly" type="primary" @click="handleSubmit" :loading="submitLoading" class="blue-button">保 存</el-button>
|
||||
<el-button type="info" @click="handleClose">关 闭</el-button>
|
||||
<el-button v-if="!(readOnly || dialogReadOnly)" type="danger" @click="handleReset">重 置</el-button>
|
||||
<el-button v-if="!readOnly" type="danger" @click="handleReset">重 置</el-button>
|
||||
</el-space>
|
||||
</slot>
|
||||
</template>
|
||||
@@ -522,7 +522,7 @@
|
||||
<script setup>
|
||||
import { ref, computed, getCurrentInstance, watch } from 'vue';
|
||||
import pcas from 'china-division/dist/pcas-code.json';
|
||||
import { saveInfectiousDiseaseReport, getNextCardNo } from '../api';
|
||||
import { saveInfectiousDiseaseReport } from '../api';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import { useDict } from '@/utils/dict';
|
||||
|
||||
@@ -544,7 +544,6 @@ const DISEASE_NAMES = {
|
||||
};
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const dialogReadOnly = ref(false);
|
||||
const formRef = ref(null);
|
||||
// 保存按钮加载状态,防止重复提交
|
||||
const submitLoading = ref(false);
|
||||
@@ -1025,11 +1024,7 @@ function parseBirthDate(birthDate) {
|
||||
|
||||
function normalizeDate(value) {
|
||||
if (!value) return '';
|
||||
const datePart = String(value).split(/[T ]/)[0].replace(/\//g, '-');
|
||||
const parts = datePart.split('-');
|
||||
if (parts.length !== 3) return datePart;
|
||||
const [year, month, day] = parts;
|
||||
return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
|
||||
return String(value).split(/[T ]/)[0];
|
||||
}
|
||||
|
||||
function normalizeSex(value) {
|
||||
@@ -1038,17 +1033,6 @@ function normalizeSex(value) {
|
||||
return '未知';
|
||||
}
|
||||
|
||||
function normalizeSexFromPatientInfo(patientInfo) {
|
||||
// 优先使用文本字段
|
||||
if (patientInfo.genderEnum_enumText) return patientInfo.genderEnum_enumText;
|
||||
if (patientInfo.genderName) return patientInfo.genderName;
|
||||
if (patientInfo.sex) return normalizeSex(patientInfo.sex);
|
||||
// 使用数字枚举字段
|
||||
if (patientInfo.genderEnum === 1) return '男';
|
||||
if (patientInfo.genderEnum === 2) return '女';
|
||||
return '未知';
|
||||
}
|
||||
|
||||
function normalizeAgeUnit(value) {
|
||||
const ageUnitMap = {
|
||||
1: '岁',
|
||||
@@ -1087,9 +1071,8 @@ function resetAddressSelector() {
|
||||
* 以只读详情方式打开报卡弹窗,供报卡管理等页面复用医生站报卡样式。
|
||||
* @param {Object} reportData - 报卡详情数据
|
||||
*/
|
||||
function showReport(reportData = {}, readOnly = true) {
|
||||
function showReport(reportData = {}) {
|
||||
dialogVisible.value = true;
|
||||
dialogReadOnly.value = readOnly;
|
||||
|
||||
resetAddressSelector();
|
||||
initProvinceOptions();
|
||||
@@ -1129,13 +1112,13 @@ function showReport(reportData = {}, readOnly = true) {
|
||||
selectedClassB: diseaseSelection.selectedClassB,
|
||||
selectedClassC: diseaseSelection.selectedClassC,
|
||||
otherDisease: reportData.otherDisease || (diseaseCode === 'OTHER' ? reportData.diseaseName || '' : ''),
|
||||
diseaseType: reportData.diseaseSubtype || reportData.diseaseType || '',
|
||||
diseaseType: reportData.diseaseType || '',
|
||||
reportOrg: reportData.reportOrg || '',
|
||||
reportOrgPhone: reportData.reportOrgPhone || '',
|
||||
reportDoc: reportData.reportDoc || '',
|
||||
reportDate: normalizeDate(reportData.reportDate || reportData.createdAt),
|
||||
correctName: reportData.revisedDiseaseName || '',
|
||||
withdrawReason: reportData.returnReason || '',
|
||||
correctName: reportData.correctName || '',
|
||||
withdrawReason: reportData.withdrawReason || '',
|
||||
remark: reportData.remark || '',
|
||||
encounterId: reportData.encounterId || reportData.visitId || '',
|
||||
patientId: reportData.patientId || reportData.patId || '',
|
||||
@@ -1254,9 +1237,8 @@ function calculateAge() {
|
||||
* 显示对话框并初始化表单数据
|
||||
* @param {Object} diagnosisData - 诊断数据,包含已选疾病和地址信息
|
||||
*/
|
||||
async function show(diagnosisData) {
|
||||
function show(diagnosisData) {
|
||||
dialogVisible.value = true;
|
||||
dialogReadOnly.value = false;
|
||||
|
||||
// 重置地址选择器状态
|
||||
resetAddressSelector();
|
||||
@@ -1285,32 +1267,18 @@ async function show(diagnosisData) {
|
||||
});
|
||||
}
|
||||
|
||||
// 生成默认卡片编号:调用后端接口获取
|
||||
// 生成默认卡片编号:医疗机构编码+当前年月日+4位流水
|
||||
const orgCode = userStore.fixmedinsCode || '0000';
|
||||
let cardNo = '';
|
||||
try {
|
||||
const res = await getNextCardNo(orgCode);
|
||||
if (res.code === 200 && res.data && res.data.length >= 12) {
|
||||
cardNo = res.data;
|
||||
} else {
|
||||
// API返回失败或不合规时,生成临时卡号避免保存时 cardNo 为空导致后端校验失败
|
||||
cardNo = 'TEMP_' + Date.now();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('获取卡片编号失败:', err);
|
||||
// API调用异常时,生成临时卡号
|
||||
cardNo = 'TEMP_' + Date.now();
|
||||
}
|
||||
|
||||
form.value = {
|
||||
// 卡片编号:单位自编,与网络直报一致
|
||||
cardNo: cardNo,
|
||||
cardNo: '',
|
||||
|
||||
// 患者基本信息
|
||||
patName: patientInfo.patientName || patientInfo.name || '', // 患者姓名
|
||||
parentName: '', // 家长姓名(14岁以下患者必填)
|
||||
idNo: patientInfo.idCard, // 身份证号
|
||||
sex: normalizeSexFromPatientInfo(patientInfo), // 性别
|
||||
sex: patientInfo.sex || patientInfo.genderName || '男', // 性别
|
||||
|
||||
// 出生日期信息
|
||||
birthYear: birthInfo.year, // 出生年份
|
||||
@@ -1370,11 +1338,7 @@ async function show(diagnosisData) {
|
||||
// 系统关联信息
|
||||
encounterId: patientInfo.encounterId || '', // 就诊ID
|
||||
patientId: patientInfo.patientId || '', // 患者ID
|
||||
diagnosisId: (diagnosisData?.conditionId != null && diagnosisData?.conditionId !== '')
|
||||
? diagnosisData.conditionId
|
||||
: (diagnosisData?.definitionId != null && diagnosisData?.definitionId !== '')
|
||||
? diagnosisData.definitionId
|
||||
: '', // 诊断ID
|
||||
diagnosisId: diagnosisData?.conditionId || diagnosisData?.definitionId || '', // 诊断ID
|
||||
};
|
||||
|
||||
// 更新selectedDiseases数组
|
||||
@@ -1415,9 +1379,6 @@ async function buildSubmitData() {
|
||||
} else if (formData.otherDisease) {
|
||||
// 其他传染病使用自定义编码
|
||||
diseaseCode = 'OTHER';
|
||||
} else if (formData.selectedDiseases && formData.selectedDiseases.length > 0) {
|
||||
// 兜底:如果 ClassA/B/C 都为空但 selectedDiseases 有值,取第一个作为 diseaseCode
|
||||
diseaseCode = formData.selectedDiseases[0];
|
||||
}
|
||||
|
||||
// 转换年龄单位:岁=1, 月=2, 天=3
|
||||
@@ -1432,7 +1393,7 @@ async function buildSubmitData() {
|
||||
const submitData = {
|
||||
cardNo: formData.cardNo,
|
||||
visitId: props.patientInfo?.encounterId || formData.encounterId || null,
|
||||
diagId: formData.diagnosisId ? Number(formData.diagnosisId) : null,
|
||||
diagId: formData.diagnosisId || null,
|
||||
patId: formData.patientId || null,
|
||||
idType: 1, // 默认身份证
|
||||
idNo: formData.idNo,
|
||||
@@ -1470,7 +1431,7 @@ async function buildSubmitData() {
|
||||
reportDate: formData.reportDate || null,
|
||||
cardNameCode: 1, // 默认中华人民共和国传染病报告卡
|
||||
registrationSource: 1, // 默认门诊
|
||||
status: null,
|
||||
status: '',
|
||||
deptId: props.deptId || null,
|
||||
doctorId: props.doctorId || null,
|
||||
};
|
||||
@@ -1485,9 +1446,9 @@ async function buildSubmitData() {
|
||||
function validateFormManually() {
|
||||
const errors = [];
|
||||
|
||||
// 卡片编号验证(至少12位,后端自动生成16位编号;临时卡号 TEMP_ 开头允许通过)
|
||||
if (form.value.cardNo && !form.value.cardNo.startsWith('TEMP_') && form.value.cardNo.length < 12) {
|
||||
errors.push('卡片编号至少12位');
|
||||
// 卡片编号验证(可选,但如果填写了必须是12位)
|
||||
if (form.value.cardNo && form.value.cardNo.length !== 12) {
|
||||
errors.push('卡片编号必须为12位');
|
||||
}
|
||||
|
||||
// 身份证号验证
|
||||
@@ -1598,12 +1559,6 @@ async function handleSubmit() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查诊断ID是否有效(后端 @NotNull 校验要求)
|
||||
if (!form.value.diagnosisId) {
|
||||
proxy.$modal.msgError('诊断信息不完整,请重新选择诊断后重试');
|
||||
return;
|
||||
}
|
||||
|
||||
// 开始加载状态,防止重复提交
|
||||
submitLoading.value = true;
|
||||
|
||||
@@ -1827,33 +1782,6 @@ defineExpose({ show, showReport, close: handleClose });
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 输入框下划线样式(与 underline-select 保持一致) */
|
||||
.underline-input :deep(.el-input__wrapper) {
|
||||
border: none;
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.underline-input :deep(.el-input__wrapper:hover) {
|
||||
border-bottom-color: #c0c4cc;
|
||||
}
|
||||
|
||||
.underline-input :deep(.el-input__wrapper.is-focus) {
|
||||
border-bottom-color: #409eff;
|
||||
}
|
||||
|
||||
.underline-input :deep(.el-input__inner) {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.underline-input :deep(.el-input__inner::placeholder) {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 街道下拉框下划线样式 */
|
||||
.underline-select {
|
||||
width: 100%;
|
||||
@@ -2041,53 +1969,4 @@ defineExpose({ show, showReport, close: handleClose });
|
||||
display: flex !important;
|
||||
justify-content: center !important;
|
||||
}
|
||||
|
||||
/* 性别单选按钮组 */
|
||||
.gender-radio-group {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
/* 出生日期输入组 */
|
||||
.birth-input-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.birth-input {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.birth-input.year {
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.birth-input.month,
|
||||
.birth-input.day {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.birth-separator {
|
||||
color: #606266;
|
||||
font-size: 13px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
/* 年龄输入组 */
|
||||
.age-input-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.age-input {
|
||||
width: 70px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.age-unit-select {
|
||||
width: 65px;
|
||||
}
|
||||
</style>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -56,13 +56,6 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="申请单号" prop="applyNo" min-width="160" align="center" header-align="center" />
|
||||
<el-table-column label="单据状态" prop="applyStatus" width="100" align="center" header-align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusType(scope.row.applyStatus)" size="small">
|
||||
{{ getStatusLabel(scope.row.applyStatus, scope.row) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="检验项目" prop="itemName" min-width="170px" align="center" header-align="center">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.itemName }}</span>
|
||||
@@ -1452,26 +1445,6 @@ const formatAmount = (amount) => {
|
||||
return num.toFixed(2)
|
||||
}
|
||||
|
||||
// 单据状态标签文字
|
||||
const getStatusLabel = (applyStatus, row) => {
|
||||
// applyStatus: 0=待开立, 1=已开立(已签发)
|
||||
// 结合收费/执行标记推导更丰富的状态
|
||||
if (applyStatus === 1) {
|
||||
// 已收费后根据执行标记判断
|
||||
if (row.needExecute === true) {
|
||||
return '已执行'
|
||||
}
|
||||
return '已开立'
|
||||
}
|
||||
return '待开立'
|
||||
}
|
||||
|
||||
// 单据状态标签颜色
|
||||
const getStatusType = (applyStatus) => {
|
||||
if (applyStatus === 1) return 'success'
|
||||
return 'info'
|
||||
}
|
||||
|
||||
// 格式化日期时间为字符串 YYYY-MM-DD HH:mm:ss
|
||||
const formatDateTime = (date) => {
|
||||
if (!date) return ''
|
||||
|
||||
@@ -109,7 +109,6 @@ const props = defineProps({
|
||||
});
|
||||
const { proxy } = getCurrentInstance();
|
||||
const encounterId = ref();
|
||||
const firstEnum = ref(1); // 初复诊标识:1=初诊,2=复诊
|
||||
onMounted(() => {
|
||||
getPatientList();
|
||||
});
|
||||
@@ -128,7 +127,6 @@ function getPatientList() {
|
||||
|
||||
function clickRow(row) {
|
||||
encounterId.value = row.encounterId;
|
||||
firstEnum.value = row.firstEnum ?? row.first_enum ?? 1;
|
||||
emits('cellClick', row);
|
||||
}
|
||||
|
||||
@@ -184,7 +182,7 @@ function handleComplete() {
|
||||
}
|
||||
proxy.$modal.confirm('是否完成该患者问诊?').then(() => {
|
||||
proxy.$modal.loading();
|
||||
completeEncounter({ encounterId: encounterId.value, firstEnum: firstEnum.value }).then(() => {
|
||||
completeEncounter(encounterId.value).then(() => {
|
||||
proxy.$modal.closeLoading();
|
||||
proxy.$modal.msgSuccess('完成问诊成功');
|
||||
getPatientList();
|
||||
|
||||
@@ -564,74 +564,22 @@ function handleRemoveItem(index) {
|
||||
editingGroup.value.detailList.splice(index, 1);
|
||||
}
|
||||
|
||||
// 单击应用按钮(应用组套)
|
||||
async function handleUseOrderGroup(row) {
|
||||
// 单击应用按钮
|
||||
function handleUseOrderGroup(row) {
|
||||
if (!row.detailList || row.detailList.length === 0) {
|
||||
ElMessage.warning('该组套没有明细项');
|
||||
return;
|
||||
}
|
||||
|
||||
// 🔧 Bug 修复:组套保存时未持久化 orderDetailInfos,导致应用时缺失医嘱库信息。
|
||||
// 通过 API 批量查询补全,确保 setValue 能获取到 adviceType、inventoryList、priceList 等关键字段。
|
||||
const itemsMissingDetail = row.detailList.filter(
|
||||
item => !item.orderDetailInfos || Object.keys(item.orderDetailInfos).length === 0
|
||||
);
|
||||
const detailMap = {};
|
||||
if (itemsMissingDetail.length > 0) {
|
||||
const ids = itemsMissingDetail
|
||||
.map(item => item.orderDefinitionId)
|
||||
.filter(Boolean)
|
||||
.join(',');
|
||||
if (ids) {
|
||||
try {
|
||||
const res = await getAdviceBaseInfo({
|
||||
adviceDefinitionIdParamList: ids,
|
||||
organizationId: props.organizationId,
|
||||
});
|
||||
const records = res.data?.records || res.rows || [];
|
||||
records.forEach(rec => {
|
||||
if (rec.adviceDefinitionId) {
|
||||
detailMap[rec.adviceDefinitionId] = rec;
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
// 批量查询失败,使用原始 orderDetailInfos
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 🔧 辅助函数:根据字典 code 查找对应的 dictText
|
||||
const findDictText = (dictList, code) => {
|
||||
if (!code || !dictList?.value?.length) return '';
|
||||
const found = dictList.value.find(d => d.value === code);
|
||||
return found?.label || '';
|
||||
};
|
||||
|
||||
// 🔧 数据预处理:确保每个明细项都有完整的医嘱信息
|
||||
const processedDetailList = row.detailList.map(item => {
|
||||
// 优先使用组套中已带的 orderDetailInfos,否则使用 API 查询结果
|
||||
const orderDetail = (item.orderDetailInfos && Object.keys(item.orderDetailInfos).length > 0)
|
||||
? item.orderDetailInfos
|
||||
: (detailMap[item.orderDefinitionId] || {});
|
||||
const orderDetail = item.orderDetailInfos || {};
|
||||
|
||||
// 🔧 修复:组套明细只存了 methodCode/rateCode,没有 dictText。
|
||||
// 用字典查找补充 dictText,确保界面显示正确的用法/频次名称。
|
||||
const resolvedMethodCode = item.methodCode ?? orderDetail.methodCode;
|
||||
const resolvedRateCode = item.rateCode ?? orderDetail.rateCode;
|
||||
const methodCodeDictText = item.methodCode_dictText
|
||||
|| findDictText(method_code, resolvedMethodCode)
|
||||
|| orderDetail.methodCode_dictText
|
||||
|| '';
|
||||
const rateCodeDictText = item.rateCode_dictText
|
||||
|| findDictText(rate_code, resolvedRateCode)
|
||||
|| orderDetail.rateCode_dictText
|
||||
|| '';
|
||||
|
||||
return {
|
||||
// 组套明细字段
|
||||
...item,
|
||||
|
||||
// 医嘱库字段
|
||||
// 医嘱库字段(可能为空,需要兜底)
|
||||
adviceName: orderDetail.adviceName || item.orderDefinitionName || '未知项目',
|
||||
adviceType: orderDetail.adviceType,
|
||||
adviceDefinitionId: item.orderDefinitionId || orderDetail.adviceDefinitionId,
|
||||
@@ -641,59 +589,36 @@ async function handleUseOrderGroup(row) {
|
||||
minUnitPrice: orderDetail.minUnitPrice,
|
||||
inventoryList: orderDetail.inventoryList || [],
|
||||
priceList: orderDetail.priceList || [],
|
||||
partPercent: orderDetail.partPercent ?? 1,
|
||||
partAttributeEnum: orderDetail.partAttributeEnum,
|
||||
unitConversionRatio: orderDetail.unitConversionRatio,
|
||||
positionId: item.positionId ?? orderDetail.positionId,
|
||||
partPercent: orderDetail.partPercent || 1,
|
||||
// 🔧 Bug #218 修复:positionId 可能存储在 item 本身,优先使用 item.positionId
|
||||
positionId: item.positionId || orderDetail.positionId,
|
||||
defaultLotNumber: orderDetail.defaultLotNumber,
|
||||
|
||||
// 单位信息
|
||||
unitCode: item.unitCode ?? orderDetail.unitCode,
|
||||
categoryCode: item.categoryCode ?? orderDetail.categoryCode,
|
||||
unitCode: item.unitCode || orderDetail.unitCode,
|
||||
unitCodeName: item.unitCodeName || orderDetail.unitCode_dictText,
|
||||
minUnitCode: orderDetail.minUnitCode,
|
||||
doseUnitCode: orderDetail.doseUnitCode,
|
||||
doseUnitCode_dictText: orderDetail.doseUnitCode_dictText || '',
|
||||
|
||||
// 药房/科室名称(setValue 通过库存查找设置,但需确保 orderDetail 中有)
|
||||
positionName: orderDetail.positionName || '',
|
||||
|
||||
// 注射/皮试标识(表格列显示依赖这些字段)
|
||||
injectFlag: orderDetail.injectFlag,
|
||||
injectFlag_enumText: orderDetail.injectFlag_enumText || '',
|
||||
skinTestFlag: orderDetail.skinTestFlag,
|
||||
skinTestFlag_enumText: orderDetail.skinTestFlag_enumText || '',
|
||||
|
||||
// 字典文本(传递到 item 层级,避免后续代码依赖 mergedDetail 再查一次)
|
||||
methodCode_dictText: methodCodeDictText,
|
||||
rateCode_dictText: rateCodeDictText,
|
||||
|
||||
// 合并后的完整对象(用于 setValue)
|
||||
// 先展开 orderDetail 获取所有药品基础字段(categoryCode、minUnitCode、doseUnitCode、
|
||||
// partPercent、partAttributeEnum、unitConversionRatio、defaultLotNumber 等),
|
||||
// 再用组套用户覆盖值覆盖,确保单次剂量/频次/用法/用药天数/总量等不被丢失
|
||||
mergedDetail: {
|
||||
...orderDetail,
|
||||
adviceName: orderDetail.adviceName || item.orderDefinitionName || '未知项目',
|
||||
adviceType: orderDetail.adviceType,
|
||||
quantity: item.quantity,
|
||||
unitCode: item.unitCode ?? orderDetail.unitCode,
|
||||
categoryCode: item.categoryCode ?? orderDetail.categoryCode,
|
||||
unitCode: item.unitCode || orderDetail.unitCode,
|
||||
unitCodeName: item.unitCodeName,
|
||||
dose: item.dose ?? orderDetail.dose,
|
||||
rateCode: resolvedRateCode,
|
||||
rateCode_dictText: rateCodeDictText,
|
||||
methodCode: resolvedMethodCode,
|
||||
methodCode_dictText: methodCodeDictText,
|
||||
dispensePerDuration: item.dispensePerDuration ?? orderDetail.dispensePerDuration,
|
||||
doseQuantity: item.doseQuantity ?? orderDetail.doseQuantity,
|
||||
positionId: item.positionId ?? orderDetail.positionId,
|
||||
orgId: item.orgId ?? orderDetail.orgId,
|
||||
orgName: item.orgName ?? orderDetail.orgName,
|
||||
groupId: item.groupId,
|
||||
groupOrder: item.groupOrder,
|
||||
// 🔧 类型默认为临时医嘱(2=临时,1=长期)
|
||||
therapyEnum: item.therapyEnum ?? orderDetail.therapyEnum ?? '2',
|
||||
dose: item.dose || orderDetail.dose,
|
||||
rateCode: item.rateCode || orderDetail.rateCode,
|
||||
methodCode: item.methodCode || orderDetail.methodCode,
|
||||
dispensePerDuration: item.dispensePerDuration || orderDetail.dispensePerDuration,
|
||||
doseQuantity: item.doseQuantity,
|
||||
inventoryList: orderDetail.inventoryList || [],
|
||||
priceList: orderDetail.priceList || [],
|
||||
partPercent: orderDetail.partPercent || 1,
|
||||
// 🔧 Bug #218 修复:positionId 可能存储在 item 本身,优先使用 item.positionId
|
||||
positionId: item.positionId || orderDetail.positionId,
|
||||
defaultLotNumber: orderDetail.defaultLotNumber,
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -710,9 +635,9 @@ function handlePreviewGroup(row) {
|
||||
}
|
||||
|
||||
// 确认应用组套(从预览对话框)
|
||||
async function confirmUseGroup() {
|
||||
function confirmUseGroup() {
|
||||
if (currentGroup.value) {
|
||||
await handleUseOrderGroup(currentGroup.value);
|
||||
handleUseOrderGroup(currentGroup.value);
|
||||
previewVisible.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,8 +246,7 @@
|
||||
<el-input-number :min="0" v-model="scope.row.doseQuantity" controls-position="right"
|
||||
:controls="false" style="width: 70px; margin-right: 20px"
|
||||
:ref="(el) => (inputRefs.doseQuantity = el)" @input="convertValues(scope.row, scope.$index)"
|
||||
@keyup.enter.prevent="handleEnter('doseQuantity', scope.row, scope.$index)"
|
||||
@change="calculateTotalAmount(scope.row, scope.$index)"/>
|
||||
@keyup.enter.prevent="handleEnter('doseQuantity', scope.row, scope.$index)" />
|
||||
</el-form-item>
|
||||
<!-- 剂量单位 -->
|
||||
<el-select v-model="scope.row.minUnitCode" style="width: 70px; margin-right: 20px" placeholder=" ">
|
||||
@@ -295,8 +294,7 @@
|
||||
}
|
||||
// inputRefs.rateCode.blur();
|
||||
}
|
||||
" :ref="(el) => (inputRefs.rateCode = el)"
|
||||
@change="calculateTotalAmount(scope.row, scope.$index)">
|
||||
" :ref="(el) => (inputRefs.rateCode = el)">
|
||||
<el-option v-for="dict in rate_code" @click="() => (scope.row.rateCode_dictText = dict.label)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
@@ -315,8 +313,6 @@
|
||||
data-prop="dispensePerDuration">
|
||||
<el-input-number v-model="scope.row.dispensePerDuration" style="width: 80px" :min="1"
|
||||
controls-position="right" :controls="false" :ref="(el) => (inputRefs.dispensePerDuration = el)"
|
||||
@input="calculateTotalAmount(scope.row, scope.$index)"
|
||||
@change="calculateTotalAmount(scope.row, scope.$index)"
|
||||
@keyup.enter.prevent="
|
||||
handleEnter('dispensePerDuration', scope.row, scope.$index)
|
||||
">
|
||||
@@ -875,7 +871,7 @@ import { ArrowDown, Search, Memo, Minus, Plus, Edit, Delete } from '@element-plu
|
||||
import printUtils, { getPrinterList, PRINT_TEMPLATE, savePrinterToCache, } from '@/utils/printUtils';
|
||||
import Template from "@/views/inpatientDoctor/home/emr/components/template.vue";
|
||||
|
||||
const emit = defineEmits(['selectDiagnosis', 'inspectionListRefresh']);
|
||||
const emit = defineEmits(['selectDiagnosis']);
|
||||
const total = ref(0);
|
||||
const queryParams = ref({});
|
||||
const prescriptionList = ref([]);
|
||||
@@ -1014,29 +1010,15 @@ const mapAdviceTypeLabel = (type, adviceTableName) => {
|
||||
if (type === 2 && adviceTableName === 'adm_device_definition') {
|
||||
return '耗材';
|
||||
}
|
||||
|
||||
|
||||
// 🔧 Bug Fix: 处理检查类型(adviceType=23)
|
||||
// 检查类型属于诊疗类,应该显示为"检查"
|
||||
if (type === 23) {
|
||||
return '检查';
|
||||
}
|
||||
|
||||
|
||||
const found = adviceTypeList.value.find((item) => item.value === type);
|
||||
if (found) {
|
||||
return found.label;
|
||||
}
|
||||
|
||||
// 🔧 Bug #458 Fix: 诊疗/手术类型字典缺失时的兜底,避免保存后"医嘱类型"列显示为空
|
||||
if (adviceTableName === 'wor_activity_definition' || adviceTableName === 'wor_service_request') {
|
||||
if (type === 6) return '手术';
|
||||
if (type === 4) return '手术';
|
||||
if (type === 1) return '检验';
|
||||
if (type === 2) return '检查';
|
||||
if (type === 5) return '其他';
|
||||
return '诊疗';
|
||||
}
|
||||
|
||||
return '';
|
||||
return found ? found.label : '';
|
||||
};
|
||||
|
||||
// 西药处方管理相关变量
|
||||
@@ -2084,21 +2066,6 @@ function getOrgList() {
|
||||
});
|
||||
}
|
||||
|
||||
/** 诊疗医嘱关联检验申请时 contentJson 含 applyNo */
|
||||
function getInspectionApplyNoFromAdviceRow(row) {
|
||||
if (!row || row.adviceType !== 3) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const raw = row.contentJson;
|
||||
const j = raw ? (typeof raw === 'string' ? JSON.parse(raw) : raw) : {};
|
||||
const no = j && j.applyNo != null ? String(j.applyNo).trim() : '';
|
||||
return no || null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function handleDelete() {
|
||||
let selectRows = prescriptionRef.value.getSelectionRows();
|
||||
console.log('BugFix#219: handleDelete called, selectRows=', selectRows);
|
||||
@@ -2278,31 +2245,12 @@ function handleDelete() {
|
||||
}
|
||||
|
||||
if (deleteList.length > 0) {
|
||||
const hasLabLinked = deleteList.some((d) => {
|
||||
const row = normalRows.find((r) => r.requestId === d.requestId);
|
||||
return row && getInspectionApplyNoFromAdviceRow(row);
|
||||
savePrescription({ adviceSaveList: deleteList }).then((res) => {
|
||||
if (res.code == 200) {
|
||||
proxy.$modal.msgSuccess('删除成功');
|
||||
getListInfo(false);
|
||||
}
|
||||
});
|
||||
const runApiDelete = () => {
|
||||
savePrescription({ adviceSaveList: deleteList }).then((res) => {
|
||||
if (res.code == 200) {
|
||||
proxy.$modal.msgSuccess('删除成功');
|
||||
getListInfo(false);
|
||||
emit('inspectionListRefresh');
|
||||
}
|
||||
});
|
||||
};
|
||||
if (hasLabLinked) {
|
||||
proxy.$modal
|
||||
.confirm(
|
||||
'删除此医嘱将同时作废关联的检验申请单(检验页签中的同单申请及同单下相关医嘱)。是否继续?',
|
||||
'删除确认',
|
||||
{ type: 'warning' }
|
||||
)
|
||||
.then(runApiDelete)
|
||||
.catch(() => {});
|
||||
} else {
|
||||
runApiDelete();
|
||||
}
|
||||
} else if (consultationRows.length == 0) {
|
||||
proxy.$modal.msgWarning('所选医嘱不可删除,请先撤回后再删除');
|
||||
return;
|
||||
@@ -2544,13 +2492,11 @@ function handleSave(prescriptionId) {
|
||||
|
||||
// 🔧 BugFix#318: 从 parsedContent 提取标准医嘱字段,排除手术特有字段
|
||||
const standardFields = [
|
||||
'accountId', 'chargeItemId', 'conditionDefinitionId', 'conditionId',
|
||||
'contentJson', 'definitionDetailId', 'definitionId', 'diagnosisName',
|
||||
'dosageInstruction', 'effectiveOrgId', 'encounterDiagnosisId',
|
||||
'encounterId', 'lotNumber', 'patientId', 'practitionerId',
|
||||
'prescriptionNo', 'skinTestFlag', 'unitPrice', 'volume', 'ybClassEnum',
|
||||
// 🔧 Bug Fix: 添加 therapyEnum 字段(医嘱类型:1=长期, 2=临时)
|
||||
'therapyEnum'
|
||||
'accountId', 'chargeItemId', 'conditionDefinitionId', 'conditionId',
|
||||
'contentJson', 'definitionDetailId', 'definitionId', 'diagnosisName',
|
||||
'dosageInstruction', 'effectiveOrgId', 'encounterDiagnosisId',
|
||||
'encounterId', 'lotNumber', 'patientId', 'practitionerId',
|
||||
'prescriptionNo', 'skinTestFlag', 'unitPrice', 'volume', 'ybClassEnum'
|
||||
];
|
||||
let filteredContent = {};
|
||||
standardFields.forEach(field => {
|
||||
@@ -3194,9 +3140,7 @@ function handleSaveBatch(prescriptionId) {
|
||||
// 🔧 Bug Fix: 添加 definitionId 和 definitionDetailId 字段
|
||||
'definitionId', 'definitionDetailId',
|
||||
// 🔧 Bug Fix: 添加 categoryEnum 字段(耗材必填)
|
||||
'categoryEnum',
|
||||
// 🔧 Bug Fix: 添加 therapyEnum 字段(医嘱类型:1=长期, 2=临时)
|
||||
'therapyEnum'
|
||||
'categoryEnum'
|
||||
];
|
||||
let filteredItem = {};
|
||||
standardItemFields.forEach(field => {
|
||||
@@ -3361,13 +3305,9 @@ function syncGroupFields(row) {
|
||||
}
|
||||
|
||||
// 同步执行科室
|
||||
// 🔧 Bug #455: 诊疗类医嘱(adviceType=3)不使用项目配置的执行科室,
|
||||
// 避免配置ID不在机构树中导致显示原始ID,保持患者就诊科室即可
|
||||
if (row.orgId || row.positionId) {
|
||||
if (Number(row.adviceType) != 3) {
|
||||
// 🔧 修复:优先使用项目所属科室(orgId),其次positionId
|
||||
prescriptionList.value[rowIndex.value].orgId = row.orgId || row.positionId;
|
||||
}
|
||||
// 🔧 修复:优先使用项目所属科室(orgId),其次positionId
|
||||
prescriptionList.value[rowIndex.value].orgId = row.orgId || row.positionId;
|
||||
}
|
||||
|
||||
// 同步皮试标记
|
||||
@@ -3399,7 +3339,7 @@ function syncGroupFields(row) {
|
||||
}
|
||||
}
|
||||
|
||||
async function setValue(row) {
|
||||
function setValue(row) {
|
||||
unitCodeList.value = [];
|
||||
unitCodeList.value.push({ value: row.unitCode, label: row.unitCode_dictText, type: 'unit' });
|
||||
|
||||
@@ -3451,16 +3391,8 @@ async function setValue(row) {
|
||||
showPopover: false, // 确保查询框关闭
|
||||
};
|
||||
console.log('[BugFix] setValue - prescriptionList[rowIndex].adviceType_dictText:', prescriptionList.value[rowIndex.value].adviceType_dictText);
|
||||
// 🔧 Bug #455: 诊疗医嘱(adviceType=3)的执行科室默认使用患者就诊科室,
|
||||
// 不使用positionId(诊疗目录配置的执行科室),避免配置ID不在机构树中导致显示原始ID
|
||||
if (Number(row.adviceType) == 3) {
|
||||
// 覆盖 catalog 传来的 positionId/orgId,使用患者就诊科室
|
||||
prescriptionList.value[rowIndex.value].orgId = props.patientInfo?.orgId;
|
||||
prescriptionList.value[rowIndex.value].positionId = props.patientInfo?.orgId;
|
||||
prescriptionList.value[rowIndex.value].positionName = findOrgNameById(props.patientInfo?.orgId) || props.patientInfo?.orgName || '';
|
||||
} else {
|
||||
prescriptionList.value[rowIndex.value].orgId = row.positionId || props.patientInfo?.orgId;
|
||||
}
|
||||
// 🔧 修复执行科室逻辑:优先使用项目维护的所属科室(row.orgId),其次使用positionId,最后回退到患者科室
|
||||
prescriptionList.value[rowIndex.value].orgId = row.orgId || row.positionId || props.patientInfo?.orgId;
|
||||
prescriptionList.value[rowIndex.value].dose = row.dose || row.doseQuantity;
|
||||
prescriptionList.value[rowIndex.value].quantity = row.quantity || 1;
|
||||
prescriptionList.value[rowIndex.value].unitCodeList = unitCodeList.value;
|
||||
@@ -3579,7 +3511,7 @@ async function setValue(row) {
|
||||
prescriptionList.value[rowIndex.value].positionId = finalLocationId;
|
||||
}
|
||||
} else {
|
||||
await getOrgList();
|
||||
getOrgList();
|
||||
// 会诊类型(adviceType == 5)和诊疗类型(adviceType == 3)的处理
|
||||
if (row.adviceType == 5) {
|
||||
// 会诊类型:设置默认值
|
||||
@@ -3591,10 +3523,13 @@ async function setValue(row) {
|
||||
prescriptionList.value[rowIndex.value].categoryEnum = 31; // 会诊的category_enum设置为31
|
||||
} else {
|
||||
// 诊疗类型(adviceType == 3)
|
||||
// 🔧 Bug #455: 诊疗项目执行科室强制使用患者就诊科室,
|
||||
// 不使用目录配置的执行科室(可能是错误ID或占位符,导致显示原始ID)
|
||||
prescriptionList.value[rowIndex.value].orgId = props.patientInfo.orgId;
|
||||
prescriptionList.value[rowIndex.value].positionName = findOrgNameById(props.patientInfo.orgId) || props.patientInfo.orgName || '';
|
||||
// 🔧 Bug Fix #238: 诊疗项目默认使用患者就诊科室
|
||||
if (!prescriptionList.value[rowIndex.value].orgId) {
|
||||
prescriptionList.value[rowIndex.value].orgId = props.patientInfo.orgId;
|
||||
}
|
||||
if (!prescriptionList.value[rowIndex.value].positionName) {
|
||||
prescriptionList.value[rowIndex.value].positionName = findOrgNameById(prescriptionList.value[rowIndex.value].orgId) || props.patientInfo.orgName || '';
|
||||
}
|
||||
// 🔧 Bug #218 修复:使用组套中维护的quantity,如果没有则默认1
|
||||
prescriptionList.value[rowIndex.value].quantity = row.quantity || 1;
|
||||
// 🔧 Bug #144 修复:安全访问 priceList,防止 orderDetailInfos 为空时出错
|
||||
@@ -3662,13 +3597,6 @@ function handleSaveGroup(orderGroupList) {
|
||||
defaultLotNumber: item.orderDetailInfos?.defaultLotNumber,
|
||||
};
|
||||
|
||||
// 🔧 Bug 修复:字典查找兜底,防止 mergedDetail 中 dictText 为空
|
||||
const findDictText = (dictList, code) => {
|
||||
if (!code || !dictList?.length) return '';
|
||||
const found = dictList.find(d => d.value === code);
|
||||
return found?.label || '';
|
||||
};
|
||||
|
||||
// 在 setValue 之前预初始化空行
|
||||
prescriptionList.value[rowIndex.value] = {
|
||||
uniqueKey: nextId.value++,
|
||||
@@ -3679,105 +3607,40 @@ function handleSaveGroup(orderGroupList) {
|
||||
// 使用医嘱项目详情设置值
|
||||
setValue(mergedDetail);
|
||||
|
||||
// 🔧 Bug 修复:使用 mergedDetail 优先,避免 item 中 undefined 覆盖 setValue 中已设置的字段
|
||||
const resolvedQuantity = mergedDetail.quantity ?? item.quantity ?? 1;
|
||||
const resolvedDose = mergedDetail.dose ?? item.dose;
|
||||
const resolvedMethodCode = mergedDetail.methodCode ?? item.methodCode;
|
||||
const resolvedRateCode = mergedDetail.rateCode ?? item.rateCode;
|
||||
const resolvedUnitCode = mergedDetail.unitCode ?? item.unitCode;
|
||||
|
||||
// 🔧 Bug 修复:setValue 可能因库存不足提前 return,导致 unitPrice/minUnitPrice 等字段未设置。
|
||||
// 从 mergedDetail 或 item 中获取兜底值,避免价格计算产生 NaN。
|
||||
const safePrice = (val) => {
|
||||
const n = Number(val);
|
||||
return (n !== undefined && n !== null && !isNaN(n) && isFinite(n)) ? n : 0;
|
||||
};
|
||||
const resolvedUnitPrice = safePrice(
|
||||
prescriptionList.value[rowIndex.value]?.unitPrice
|
||||
?? mergedDetail.unitPrice
|
||||
?? item.unitPrice
|
||||
?? 0
|
||||
);
|
||||
const resolvedMinUnitPrice = safePrice(
|
||||
prescriptionList.value[rowIndex.value]?.minUnitPrice
|
||||
?? mergedDetail.minUnitPrice
|
||||
?? item.minUnitPrice
|
||||
?? 0
|
||||
);
|
||||
const resolvedPartPercent = safePrice(
|
||||
item.orderDetailInfos?.partPercent
|
||||
?? mergedDetail.partPercent
|
||||
?? 1
|
||||
);
|
||||
|
||||
// 创建新的处方项目
|
||||
const newRow = {
|
||||
...prescriptionList.value[rowIndex.value],
|
||||
patientId: props.patientInfo.patientId,
|
||||
encounterId: props.patientInfo.encounterId,
|
||||
accountId: accountId.value,
|
||||
quantity: resolvedQuantity,
|
||||
methodCode: resolvedMethodCode,
|
||||
methodCode_dictText: mergedDetail.methodCode_dictText
|
||||
|| findDictText(method_code.value, resolvedMethodCode)
|
||||
|| '',
|
||||
rateCode: resolvedRateCode,
|
||||
rateCode_dictText: mergedDetail.rateCode_dictText
|
||||
|| findDictText(rate_code.value, resolvedRateCode)
|
||||
|| '',
|
||||
dispensePerDuration: mergedDetail.dispensePerDuration ?? item.dispensePerDuration,
|
||||
dose: resolvedDose,
|
||||
doseQuantity: mergedDetail.doseQuantity ?? item.doseQuantity,
|
||||
quantity: item.quantity,
|
||||
methodCode: item.methodCode,
|
||||
rateCode: item.rateCode,
|
||||
dispensePerDuration: item.dispensePerDuration,
|
||||
dose: item.dose,
|
||||
doseQuantity: item.doseQuantity,
|
||||
executeNum: 1,
|
||||
unitCode: resolvedUnitCode,
|
||||
unitCode_dictText: item.unitCodeName
|
||||
|| mergedDetail.unitCodeName
|
||||
|| findDictText(unit_code.value, resolvedUnitCode)
|
||||
|| '',
|
||||
doseUnitCode: mergedDetail.doseUnitCode,
|
||||
doseUnitCode_dictText: mergedDetail.doseUnitCode_dictText || '',
|
||||
// 🔧 确保 price/adviceType 字段有安全默认值(避免 NaN 导致模板条件判断失效)
|
||||
unitPrice: resolvedUnitPrice,
|
||||
minUnitPrice: resolvedMinUnitPrice,
|
||||
unitTempPrice: resolvedUnitPrice,
|
||||
adviceType: prescriptionList.value[rowIndex.value]?.adviceType
|
||||
|| Number(mergedDetail.adviceType)
|
||||
|| Number(item.adviceType)
|
||||
|| 0,
|
||||
adviceType_dictText: prescriptionList.value[rowIndex.value]?.adviceType_dictText
|
||||
|| mergedDetail.adviceType_dictText
|
||||
|| item.adviceType_dictText
|
||||
|| '',
|
||||
unitCode: item.unitCode,
|
||||
unitCode_dictText: item.unitCodeName || '',
|
||||
statusEnum: 1,
|
||||
// 🔧 类型:组套应用默认为临时医嘱(2=临时,1=长期)
|
||||
therapyEnum: mergedDetail.therapyEnum ?? item.therapyEnum ?? '2',
|
||||
// 🔧 修复执行科室逻辑:优先使用 orgId(所属科室),其次 positionId
|
||||
// 🔧 Bug #455: 诊疗类(adviceType=3)使用患者就诊科室,不使用目录配置的ID
|
||||
orgId: item.adviceType === 3
|
||||
? props.patientInfo?.orgId
|
||||
: (item.orderDetailInfos?.orgId || mergedDetail.orgId || item.positionId || item.orderDetailInfos?.positionId || mergedDetail.positionId),
|
||||
positionName: prescriptionList.value[rowIndex.value]?.positionName
|
||||
|| mergedDetail.orgName
|
||||
|| mergedDetail.positionName
|
||||
|| findOrgNameById(mergedDetail.orgId || props.patientInfo?.orgId)
|
||||
|| '',
|
||||
orgId: item.orderDetailInfos?.orgId || mergedDetail.orgId || item.positionId || item.orderDetailInfos?.positionId || mergedDetail.positionId,
|
||||
dbOpType: prescriptionList.value[rowIndex.value].requestId ? '2' : '1',
|
||||
conditionId: conditionId.value,
|
||||
conditionDefinitionId: conditionDefinitionId.value,
|
||||
encounterDiagnosisId: encounterDiagnosisId.value,
|
||||
diagnosisName: diagnosisName.value,
|
||||
};
|
||||
|
||||
// 计算价格和总量(使用安全值)
|
||||
const unitInfo = unitCodeList.value.find((k) => k.value == resolvedUnitCode);
|
||||
// 计算价格和总量
|
||||
const unitInfo = unitCodeList.value.find((k) => k.value == item.unitCode);
|
||||
if (unitInfo && unitInfo.type == 'minUnit') {
|
||||
newRow.price = resolvedMinUnitPrice;
|
||||
newRow.totalPrice = (resolvedQuantity * resolvedMinUnitPrice).toFixed(6);
|
||||
newRow.minUnitQuantity = resolvedQuantity;
|
||||
newRow.price = newRow.minUnitPrice;
|
||||
newRow.totalPrice = (item.quantity * newRow.minUnitPrice).toFixed(6);
|
||||
newRow.minUnitQuantity = item.quantity;
|
||||
} else {
|
||||
newRow.price = resolvedUnitPrice;
|
||||
newRow.totalPrice = (resolvedQuantity * resolvedUnitPrice).toFixed(6);
|
||||
newRow.minUnitQuantity = resolvedQuantity * resolvedPartPercent;
|
||||
newRow.price = newRow.unitPrice;
|
||||
newRow.totalPrice = (item.quantity * newRow.unitPrice).toFixed(6);
|
||||
newRow.minUnitQuantity = item.quantity * (item.orderDetailInfos?.partPercent || mergedDetail.partPercent || 1);
|
||||
}
|
||||
|
||||
newRow.contentJson = JSON.stringify(newRow);
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
|
||||
|
||||
<!-- 手术单号 -->
|
||||
<el-table-column label="手术单号" align="center" width="150">
|
||||
<template #default="scope">
|
||||
@@ -33,31 +33,160 @@
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
|
||||
<!-- 申请日期 -->
|
||||
<el-table-column label="申请日期" align="center" prop="createTime" width="180">
|
||||
<template #default="scope">
|
||||
{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
|
||||
<!-- 患者姓名 -->
|
||||
<el-table-column label="患者姓名" align="center" prop="patientName" width="100" />
|
||||
|
||||
|
||||
<!-- 申请医生 -->
|
||||
<el-table-column label="申请医生" align="center" prop="applyDoctorName" width="100" />
|
||||
|
||||
|
||||
<!-- 申请科室 -->
|
||||
<el-table-column label="申请科室" align="center" prop="applyDeptName" width="120" show-overflow-tooltip />
|
||||
|
||||
|
||||
<!-- 手术名称 -->
|
||||
<el-table-column label="手术名称" align="center" prop="surgeryName" min-width="150" show-overflow-tooltip />
|
||||
|
||||
|
||||
<!-- 手术等级 -->
|
||||
<el-table-column label="手术等级" align="center" prop="surgeryLevel_dictText" width="100" />
|
||||
|
||||
|
||||
<!-- 手术室确认时间 -->
|
||||
<el-table-column label="手术室确认时间" align="center" prop="confirmTime" width="180">
|
||||
<template #default="scope">
|
||||
{{ scope.row.confirmTime ? parseTime(scope.row.confirmTime, '{y}-{m}-{d} {h}:{i}:{s}') : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 操作列 -->
|
||||
<el-table-column label="操作" align="center" width="150" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
@click="handleView(scope.row)"
|
||||
>
|
||||
查看
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
@click="handleEdit(scope.row)"
|
||||
:disabled="scope.row.status !== '0'"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { getSurgeryApplicationList, addSurgeryApplication, updateSurgeryApplication } from '@/api/doctorstation/surgery'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
const loading = ref(true)
|
||||
const surgeryList = ref([])
|
||||
|
||||
// 查询手术申请列表
|
||||
const getList = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const response = await getSurgeryApplicationList()
|
||||
surgeryList.value = response.data || []
|
||||
} catch (error) {
|
||||
console.warn('获取手术申请列表失败', error)
|
||||
surgeryList.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 新增手术申请
|
||||
const handleAdd = () => {
|
||||
// 跳转到新增页面或打开新增弹窗
|
||||
// 这里需要根据实际路由配置调整
|
||||
openSurgeryDialog()
|
||||
}
|
||||
|
||||
// 编辑手术申请
|
||||
const handleEdit = (row) => {
|
||||
if(row.status !== '0') {
|
||||
ElMessage.warning('该申请已提交,无法修改')
|
||||
return
|
||||
}
|
||||
openSurgeryDialog(row)
|
||||
}
|
||||
|
||||
// 查看手术申请详情
|
||||
const handleView = (row) => {
|
||||
openSurgeryDialog(row, true)
|
||||
}
|
||||
|
||||
// 刷新列表
|
||||
const handleRefresh = () => {
|
||||
getList()
|
||||
}
|
||||
|
||||
// 打开手术申请对话框
|
||||
const openSurgeryDialog = (row, isView = false) => {
|
||||
// 这里需要引入具体的对话框组件或跳转到详情页
|
||||
// 示例代码,具体实现需要根据项目实际情况调整
|
||||
console.log('打开手术申请对话框', row, isView)
|
||||
}
|
||||
|
||||
// 获取行样式
|
||||
const getRowClassName = ({ row }) => {
|
||||
if (row.status === '1') {
|
||||
return 'success-row'
|
||||
} else if (row.status === '2') {
|
||||
return 'warning-row'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
// 暴露方法给父组件调用,用于提交后刷新列表
|
||||
defineExpose({
|
||||
refreshList: getList
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.surgery-application-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.top-operation-bar {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.add-button,
|
||||
.refresh-button {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* 表格行状态样式 */
|
||||
::v-deep(.success-row) {
|
||||
background-color: #f0f9ff;
|
||||
}
|
||||
|
||||
::v-deep(.warning-row) {
|
||||
background-color: #fff7e6;
|
||||
}
|
||||
</style>
|
||||
<el-table-column label="手术室确认时间" align="center" prop="operatingRoomConfirmTime" width="180">
|
||||
<template #default="scope">
|
||||
{{ scope.row.operatingRoomConfirmTime ? parseTime(scope.row.operatingRoomConfirmTime, '{y}-{m}-{d} {h}:{i}:{s}') : '-' }}
|
||||
@@ -75,16 +204,16 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
|
||||
<!-- 操作 -->
|
||||
<el-table-column label="操作" align="center" width="200" fixed="right">
|
||||
<template #default="scope">
|
||||
<!-- 查看:显示手术申请详情(只读模式) -->
|
||||
<el-button link type="primary" icon="View" @click="handleView(scope.row)">查看</el-button>
|
||||
|
||||
|
||||
<!-- 编辑:修改手术申请信息(只有状态为新开的能修改) -->
|
||||
<el-button link type="primary" icon="Edit" @click="handleEdit(scope.row)" v-if="scope.row.statusEnum === 0">编辑</el-button>
|
||||
|
||||
|
||||
<!-- 删除:取消手术申请(作废) -->
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-if="scope.row.statusEnum === 0 || scope.row.statusEnum === 1">删除</el-button>
|
||||
</template>
|
||||
@@ -511,10 +640,6 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
const loading = ref(true)
|
||||
const surgeryLoading = ref(false)
|
||||
const anesthesiaLoading = ref(false)
|
||||
let surgerySearchTimer = null
|
||||
let anesthesiaSearchTimer = null
|
||||
const surgeryList = ref([])
|
||||
const open = ref(false)
|
||||
const viewOpen = ref(false)
|
||||
@@ -626,19 +751,14 @@ function getList() {
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
loading.value = true
|
||||
getSurgeryPage({
|
||||
pageNo: 1,
|
||||
pageSize: 100,
|
||||
encounterId: props.patientInfo.encounterId
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
surgeryList.value = res.data?.records || []
|
||||
} else {
|
||||
proxy.$modal.msgError(res.msg || '数据加载失败,请稍后重试')
|
||||
surgeryList.value = []
|
||||
}
|
||||
surgeryList.value = res.data.records || []
|
||||
}).catch(error => {
|
||||
console.error('获取手术列表失败:', error)
|
||||
proxy.$modal.msgError('数据加载失败,请稍后重试')
|
||||
@@ -1138,12 +1258,12 @@ function submitForm() {
|
||||
// 新增手术
|
||||
addSurgery(form.value).then((res) => {
|
||||
if (res.code === 200) {
|
||||
proxy.$modal.msgSuccess('手术申请提交成功!')
|
||||
proxy.$modal.msgSuccess(res.msg || '新增成功')
|
||||
// 保存麻醉方式
|
||||
sessionStorage.setItem('anesthesiaType', form.value.anesthesiaTypeEnum)
|
||||
open.value = false
|
||||
getList() // 提交成功后直接刷新列表
|
||||
emit('saved') // 通知父组件刷新医嘱列表
|
||||
getList()
|
||||
emit('saved') // 🔧 触发保存事件,通知父组件刷新医嘱列表
|
||||
} else {
|
||||
proxy.$modal.msgError(res.msg || '新增手术失败,请检查表单信息')
|
||||
}
|
||||
@@ -1155,12 +1275,12 @@ function submitForm() {
|
||||
// 修改手术
|
||||
updateSurgery(form.value).then((res) => {
|
||||
if (res.code === 200) {
|
||||
proxy.$modal.msgSuccess('手术申请修改成功!')
|
||||
proxy.$modal.msgSuccess(res.msg || '修改成功')
|
||||
// 保存麻醉方式
|
||||
sessionStorage.setItem('anesthesiaType', form.value.anesthesiaTypeEnum)
|
||||
open.value = false
|
||||
getList() // 修改成功后直接刷新列表
|
||||
emit('saved') // 通知父组件刷新医嘱列表
|
||||
getList()
|
||||
emit('saved') // 🔧 触发保存事件,通知父组件刷新医嘱列表
|
||||
} else {
|
||||
proxy.$modal.msgError(res.msg || '更新手术失败,请检查表单信息')
|
||||
}
|
||||
|
||||
@@ -138,8 +138,7 @@
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="医嘱" name="prescription">
|
||||
<prescriptionlist :patientInfo="patientInfo" ref="prescriptionRef" :activeTab="activeTab"
|
||||
:outpatientEmrSaved="outpatientEmrSaved"
|
||||
@inspectionListRefresh="refreshInspectionListFromAdvice" />
|
||||
:outpatientEmrSaved="outpatientEmrSaved" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="中医" name="tcm">
|
||||
<tcmAdvice :patientInfo="patientInfo" ref="tcmRef" />
|
||||
@@ -152,8 +151,8 @@
|
||||
@saved="() => prescriptionRef?.getListInfo()" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="手术申请" name="surgery">
|
||||
<surgeryApplication :patientInfo="patientInfo" :activeTab="activeTab" ref="surgeryRef"
|
||||
@saved="() => { prescriptionRef?.getListInfo(); surgeryRef?.getList() }" />
|
||||
<surgeryApplication :patientInfo="patientInfo" :activeTab="activeTab" ref="surgeryRef"
|
||||
@saved="() => prescriptionRef?.getListInfo()" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="电子处方" name="eprescription">
|
||||
<eprescriptionlist :patientInfo="patientInfo" ref="eprescriptionRef" />
|
||||
@@ -313,9 +312,6 @@ const patientDrawerRef = ref();
|
||||
const prescriptionRef = ref();
|
||||
const tcmRef = ref();
|
||||
const inspectionRef = ref();
|
||||
function refreshInspectionListFromAdvice() {
|
||||
inspectionRef.value?.getList?.();
|
||||
}
|
||||
const examinationRef = ref();
|
||||
const surgeryRef = ref();
|
||||
const emrRef = ref();
|
||||
|
||||
@@ -151,12 +151,23 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<InfectiousDiseaseReportDialog
|
||||
ref="infectiousDiseaseReportRef"
|
||||
:read-only="detailMode === 'view'"
|
||||
@success="detailVisible = false"
|
||||
@close="detailVisible = false"
|
||||
/>
|
||||
<el-dialog
|
||||
v-model="detailVisible"
|
||||
:title="detailMode === 'view' ? '报卡详情 - 中华人民共和国传染病报告卡' : '编辑报卡 - 中华人民共和国传染病报告卡'"
|
||||
width="1100px"
|
||||
destroy-on-close
|
||||
class="card-detail-dialog"
|
||||
>
|
||||
<InfectiousReport
|
||||
:mode=" detailMode"
|
||||
:card-data="currentCard"
|
||||
@submit-edit="handleSaveEdit"
|
||||
style="max-height: 75vh; overflow-y: auto;"
|
||||
/>
|
||||
<template #footer>
|
||||
<el-button @click="detailVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -164,7 +175,7 @@
|
||||
import { ref, reactive, onMounted, onActivated, onBeforeUnmount, nextTick, computed } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { DataAnalysis, Warning, CircleCheck, Check, RefreshRight } from '@element-plus/icons-vue';
|
||||
import InfectiousDiseaseReportDialog from '../components/diagnosis/infectiousDiseaseReportDialog.vue';
|
||||
import InfectiousReport from '../components/infectiousReport/index.vue';
|
||||
import {
|
||||
getDoctorCardStatistics,
|
||||
getDoctorCardList,
|
||||
@@ -174,6 +185,7 @@ import {
|
||||
batchDeleteCards,
|
||||
exportCardToWord,
|
||||
getCardDetail,
|
||||
updateDoctorCard,
|
||||
} from './api';
|
||||
|
||||
const loading = ref(false);
|
||||
@@ -199,7 +211,7 @@ const queryParams = reactive({
|
||||
|
||||
const detailVisible = ref(false);
|
||||
const detailMode = ref('view');
|
||||
const infectiousDiseaseReportRef = ref(null);
|
||||
const currentCard = ref({});
|
||||
|
||||
// 计算表格高度:根据视口高度动态调整
|
||||
const tableHeight = computed(() => {
|
||||
@@ -315,11 +327,9 @@ async function handleView(row) {
|
||||
try {
|
||||
const res = await getCardDetail(row.cardNo);
|
||||
if (res.code === 200) {
|
||||
currentCard.value = res.data || {};
|
||||
detailMode.value = 'view';
|
||||
detailVisible.value = true;
|
||||
nextTick(() => {
|
||||
infectiousDiseaseReportRef.value?.showReport(res.data || {});
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('获取详情失败');
|
||||
@@ -330,17 +340,57 @@ async function handleEdit(row) {
|
||||
try {
|
||||
const res = await getCardDetail(row.cardNo);
|
||||
if (res.code === 200) {
|
||||
currentCard.value = res.data || {};
|
||||
detailMode.value = 'edit';
|
||||
detailVisible.value = true;
|
||||
nextTick(() => {
|
||||
infectiousDiseaseReportRef.value?.showReport(res.data || {}, false);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('获取详情失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSaveEdit(submitData) {
|
||||
// submitData 来自 InfectiousReport 组件的 submit-edit 事件
|
||||
try {
|
||||
const updateData = {
|
||||
cardNo: submitData.cardNo,
|
||||
phone: submitData.phone,
|
||||
contactPhone: submitData.contactPhone,
|
||||
onsetDate: submitData.onsetDate,
|
||||
diagDate: submitData.diagDate,
|
||||
diseaseCode: submitData.diseaseCode,
|
||||
diseaseType: submitData.diseaseType,
|
||||
otherDisease: submitData.otherDisease,
|
||||
caseClass: submitData.caseClass,
|
||||
occupation: submitData.occupation,
|
||||
patientBelong: submitData.patientBelong,
|
||||
addressProv: submitData.addressProv,
|
||||
addressCity: submitData.addressCity,
|
||||
addressCounty: submitData.addressCounty,
|
||||
addressTown: submitData.addressTown,
|
||||
addressVillage: submitData.addressVillage,
|
||||
addressHouse: submitData.addressHouse,
|
||||
workplace: submitData.workplace,
|
||||
parentName: submitData.parentName,
|
||||
deathDate: submitData.deathDate,
|
||||
correctName: submitData.correctName,
|
||||
withdrawReason: submitData.withdrawReason,
|
||||
remark: submitData.remark,
|
||||
};
|
||||
|
||||
const res = await updateDoctorCard(updateData);
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('保存成功');
|
||||
detailVisible.value = false;
|
||||
getList();
|
||||
} else {
|
||||
ElMessage.error(res.msg || '保存失败');
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('保存失败:' + (error.message || '网络错误'));
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmit(row) {
|
||||
try {
|
||||
await ElMessageBox.confirm('确认提交该报卡?', '提示', {
|
||||
@@ -750,4 +800,17 @@ function handleResize() {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 报卡详情弹窗 */
|
||||
:deep(.card-detail-dialog .el-dialog__body) {
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.card-detail-dialog .infectious-report-container) {
|
||||
padding: 16px;
|
||||
height: auto;
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
@@ -51,7 +51,7 @@ const currentSelectRow = ref<any>({});
|
||||
const queryParams = ref({
|
||||
pageSize: 100,
|
||||
pageNo: 1,
|
||||
adviceTypes: [1, 2, 3, 6],
|
||||
adviceTypes: '1,2,3,6',
|
||||
searchKey: '',
|
||||
organizationId: '',
|
||||
categoryCode: '',
|
||||
@@ -88,10 +88,10 @@ const tableColumns = computed<TableColumn[]>(() => [
|
||||
function refresh(adviceType: any, categoryCode: string, searchKey: string) {
|
||||
// 有搜索词时跨类型搜索,避免用户输入"级护理"但因当前adviceType为药品而搜不到诊疗类护理项目
|
||||
if (searchKey) {
|
||||
queryParams.value.adviceTypes = [1, 2, 3, 6];
|
||||
queryParams.value.adviceTypes = '1,2,3,6';
|
||||
} else {
|
||||
queryParams.value.adviceTypes =
|
||||
adviceType !== undefined && adviceType !== '' ? [parseInt(adviceType)] : [1, 2, 3, 6];
|
||||
adviceType !== undefined && adviceType !== '' ? String(adviceType) : '1,2,3,6';
|
||||
}
|
||||
queryParams.value.categoryCode = categoryCode || '';
|
||||
queryParams.value.searchKey = searchKey || '';
|
||||
@@ -131,8 +131,7 @@ function getList() {
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.warn('医嘱基础信息加载失败:', err);
|
||||
.catch(() => {
|
||||
adviceBaseList.value = [];
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
@@ -44,17 +44,6 @@ export function getSurgery(queryParams) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询手术申请单(全局,不需要encounterId,用于门诊手术安排查找弹窗)
|
||||
*/
|
||||
export function getSurgeryPage(queryParams) {
|
||||
return request({
|
||||
url: '/reg-doctorstation/request-form/get-surgery-page',
|
||||
method: 'get',
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询护理医嘱信息
|
||||
*/
|
||||
|
||||
@@ -175,9 +175,10 @@ const hasMatchedFields = computed(() => {
|
||||
});
|
||||
|
||||
/** 查询科室 */
|
||||
const getLocationInfo = async () => {
|
||||
const res = await getOrgList();
|
||||
orgOptions.value = res.data.records;
|
||||
const getLocationInfo = () => {
|
||||
getOrgList().then((res) => {
|
||||
orgOptions.value = res.data.records;
|
||||
});
|
||||
};
|
||||
|
||||
const recursionFun = (targetDepartment) => {
|
||||
@@ -198,12 +199,7 @@ const recursionFun = (targetDepartment) => {
|
||||
return name;
|
||||
};
|
||||
|
||||
const handleViewDetail = async (row) => {
|
||||
// 确保科室数据已加载,以便将 ID 解析为名称
|
||||
if (!orgOptions.value || orgOptions.value.length === 0) {
|
||||
await getLocationInfo();
|
||||
}
|
||||
|
||||
const handleViewDetail = (row) => {
|
||||
currentDetail.value = row;
|
||||
// 解析 descJson
|
||||
if (row.descJson) {
|
||||
@@ -224,9 +220,10 @@ const handleViewDetail = async (row) => {
|
||||
|
||||
watch(
|
||||
() => patientInfo.value?.encounterId,
|
||||
async (val) => {
|
||||
(val) => {
|
||||
if (val) {
|
||||
await Promise.all([fetchData(), getLocationInfo()]);
|
||||
fetchData();
|
||||
getLocationInfo();
|
||||
} else {
|
||||
tableData.value = [];
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,20 +0,0 @@
|
||||
<!--
|
||||
* 住院医生站 — 汇总发药申请:复用药房「发药汇总单」能力
|
||||
-->
|
||||
<template>
|
||||
<div class="summary-drug-application">
|
||||
<MedicationSummary />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import MedicationSummary from '@/views/drug/inpatientMedicationDispensing/components/MedicationSummary.vue';
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.summary-drug-application {
|
||||
height: 100%;
|
||||
min-height: 400px;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
@@ -181,9 +181,10 @@ const hasMatchedFields = computed(() => {
|
||||
});
|
||||
|
||||
/** 查询科室 */
|
||||
const getLocationInfo = async () => {
|
||||
const res = await getOrgList();
|
||||
orgOptions.value = res.data.records;
|
||||
const getLocationInfo = () => {
|
||||
getOrgList().then((res) => {
|
||||
orgOptions.value = res.data.records;
|
||||
});
|
||||
};
|
||||
|
||||
const recursionFun = (targetDepartment) => {
|
||||
@@ -194,8 +195,9 @@ const recursionFun = (targetDepartment) => {
|
||||
name = obj.name;
|
||||
}
|
||||
const subObjArray = obj['children'];
|
||||
for (let index = 0; index < subObjArray.length; index++) {
|
||||
const item = subObjArray[index];
|
||||
if (!subObjArray) continue;
|
||||
for (let i = 0; i < subObjArray.length; i++) {
|
||||
const item = subObjArray[i];
|
||||
if (item.id == targetDepartment) {
|
||||
name = item.name;
|
||||
}
|
||||
@@ -204,12 +206,7 @@ const recursionFun = (targetDepartment) => {
|
||||
return name;
|
||||
};
|
||||
|
||||
const handleViewDetail = async (row) => {
|
||||
// 确保科室数据已加载,以便将 ID 解析为名称
|
||||
if (!orgOptions.value || orgOptions.value.length === 0) {
|
||||
await getLocationInfo();
|
||||
}
|
||||
|
||||
const handleViewDetail = (row) => {
|
||||
currentDetail.value = row;
|
||||
// 解析 descJson
|
||||
if (row.descJson) {
|
||||
@@ -230,9 +227,10 @@ const handleViewDetail = async (row) => {
|
||||
|
||||
watch(
|
||||
() => patientInfo.value?.encounterId,
|
||||
async (val) => {
|
||||
(val) => {
|
||||
if (val) {
|
||||
await Promise.all([fetchData(), getLocationInfo()]);
|
||||
fetchData();
|
||||
getLocationInfo();
|
||||
} else {
|
||||
tableData.value = [];
|
||||
}
|
||||
|
||||
@@ -41,19 +41,12 @@
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="待签发" value="0" />
|
||||
<el-option label="已签发" value="1" />
|
||||
<el-option label="已采集" value="2" />
|
||||
<el-option label="已收样" value="3" />
|
||||
<el-option label="报告已出" value="4" />
|
||||
<el-option label="已作废" value="5" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="关键字">
|
||||
<el-input
|
||||
v-model="filterForm.keyword"
|
||||
placeholder="申请单号/检验项目"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch" :loading="loading">
|
||||
<el-icon><Search /></el-icon>
|
||||
@@ -82,11 +75,7 @@
|
||||
</template>
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="patientName" label="患者姓名" width="120" />
|
||||
<el-table-column label="申请单名称" width="140">
|
||||
<template #default="scope">
|
||||
<span>{{ buildApplicationName(scope.row) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="申请单名称" width="140" />
|
||||
<el-table-column prop="createTime" label="创建时间" width="160" />
|
||||
<el-table-column prop="prescriptionNo" label="申请单号" width="140" />
|
||||
<el-table-column label="单据状态" width="100" align="center">
|
||||
@@ -105,15 +94,8 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="requesterId_dictText" label="申请者" width="120" />
|
||||
<el-table-column label="操作" align="center" fixed="right" width="160">
|
||||
<el-table-column label="操作" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<template v-if="scope.row.status == 0">
|
||||
<el-button link type="primary" @click="handleEdit(scope.row)">修改</el-button>
|
||||
<el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
<template v-else-if="scope.row.status == 1">
|
||||
<el-button link type="warning" @click="handleWithdraw(scope.row)">撤回</el-button>
|
||||
</template>
|
||||
<el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -141,7 +123,7 @@
|
||||
<el-descriptions-item label="创建时间">{{
|
||||
currentDetail.createTime || '-'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="申请单号">{{
|
||||
<el-descriptions-item label="处方号">{{
|
||||
currentDetail.prescriptionNo || '-'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="申请者">{{
|
||||
@@ -183,26 +165,6 @@
|
||||
<el-button @click="detailDialogVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 编辑检验申请单弹窗 -->
|
||||
<el-dialog
|
||||
v-model="editDialogVisible"
|
||||
title="编辑检验申请单"
|
||||
width="1200px"
|
||||
destroy-on-close
|
||||
top="5vh"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<LaboratoryTests
|
||||
ref="editFormRef"
|
||||
@submitOk="handleEditSubmitOk"
|
||||
:editData="editRowData"
|
||||
/>
|
||||
<template #footer>
|
||||
<el-button @click="editDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitEditForm">确认</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -210,19 +172,14 @@
|
||||
import {computed, getCurrentInstance, ref, watch} from 'vue';
|
||||
import {Refresh, Search} from '@element-plus/icons-vue';
|
||||
import {patientInfo} from '../../store/patient.js';
|
||||
import {getInspection, deleteRequestForm, withdrawRequestForm} from './api';
|
||||
import {getDepartmentList} from '@/api/public.js';
|
||||
import LaboratoryTests from '../order/applicationForm/laboratoryTests.vue';
|
||||
import {saveInspection} from '../order/applicationForm/api.js';
|
||||
import {getInspection} from './api';
|
||||
import {getOrgList} from '@/views/doctorstation/components/api.js';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const tableData = ref([]);
|
||||
const loading = ref(false);
|
||||
const detailDialogVisible = ref(false);
|
||||
const editDialogVisible = ref(false);
|
||||
const editRowData = ref(null);
|
||||
const editFormRef = ref(null);
|
||||
const currentDetail = ref(null);
|
||||
const descJsonData = ref(null);
|
||||
const orgOptions = ref([]);
|
||||
@@ -231,7 +188,6 @@ const orgOptions = ref([]);
|
||||
const filterForm = ref({
|
||||
dateRange: [], // [startDate, endDate]
|
||||
status: '', // 单据状态
|
||||
keyword: '', // 关键字搜索
|
||||
});
|
||||
|
||||
const fetchData = async () => {
|
||||
@@ -255,12 +211,7 @@ const fetchData = async () => {
|
||||
if (filterForm.value.status !== '' && filterForm.value.status !== undefined) {
|
||||
params.status = filterForm.value.status;
|
||||
}
|
||||
|
||||
// 添加关键字搜索
|
||||
if (filterForm.value.keyword && filterForm.value.keyword.trim()) {
|
||||
params.keyword = filterForm.value.keyword.trim();
|
||||
}
|
||||
|
||||
|
||||
const res = await getInspection(params);
|
||||
if (res.code === 200 && res.data) {
|
||||
const raw = res.data?.records || res.data;
|
||||
@@ -300,7 +251,6 @@ const handleReset = () => {
|
||||
// 重置筛选条件为默认值
|
||||
filterForm.value.dateRange = [];
|
||||
filterForm.value.status = '';
|
||||
filterForm.value.keyword = '';
|
||||
// 重新加载数据
|
||||
fetchData();
|
||||
};
|
||||
@@ -314,9 +264,6 @@ const labelMap = {
|
||||
otherDiagnosis: '其他诊断',
|
||||
relatedResult: '相关结果',
|
||||
attention: '注意事项',
|
||||
applicationType: '申请类型',
|
||||
specimenName: '标本类型',
|
||||
executeTime: '执行时间',
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -328,6 +275,8 @@ const parseBillStatus = (status) => {
|
||||
const statusMap = {
|
||||
'0': '待签发',
|
||||
'1': '已签发',
|
||||
'2': '已采集',
|
||||
'3': '已收样',
|
||||
'4': '报告已出',
|
||||
'5': '已作废',
|
||||
};
|
||||
@@ -343,8 +292,8 @@ const parsePriorityCode = (descJson) => {
|
||||
if (!descJson) return '-';
|
||||
try {
|
||||
const obj = JSON.parse(descJson);
|
||||
// applicationType: 0-普通, 1-急诊
|
||||
return obj.applicationType === 1 ? '急' : '普通';
|
||||
// priorityCode: 0-普通, 1-急
|
||||
return obj.priorityCode === 1 ? '急' : '普通';
|
||||
} catch (e) {
|
||||
console.error('解析 descJson 失败:', e);
|
||||
return '-';
|
||||
@@ -368,24 +317,6 @@ const parseSpecimenType = (descJson) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据申请单详情构建申请单名称
|
||||
* 单一项目:显示项目名称+数量
|
||||
* 多个项目:显示首个项目名称+数量+"等X项"
|
||||
*/
|
||||
const buildApplicationName = (row) => {
|
||||
const details = row.requestFormDetailList;
|
||||
if (!details || details.length === 0) {
|
||||
return row.name || '-';
|
||||
}
|
||||
if (details.length === 1) {
|
||||
const item = details[0];
|
||||
return `${item.adviceName}${item.quantity || ''}`;
|
||||
}
|
||||
const first = details[0];
|
||||
return `${first.adviceName}${first.quantity || ''}等${details.length}项`;
|
||||
};
|
||||
|
||||
const isFieldMatched = (key) => {
|
||||
return key in labelMap;
|
||||
};
|
||||
@@ -400,51 +331,39 @@ const hasMatchedFields = computed(() => {
|
||||
});
|
||||
|
||||
/** 查询科室 */
|
||||
const getLocationInfo = async () => {
|
||||
const res = await getDepartmentList();
|
||||
orgOptions.value = res.data || [];
|
||||
const getLocationInfo = () => {
|
||||
getOrgList().then((res) => {
|
||||
orgOptions.value = res.data.records;
|
||||
});
|
||||
};
|
||||
|
||||
const recursionFun = (targetDepartment) => {
|
||||
if (!targetDepartment) return '';
|
||||
let name = '';
|
||||
for (let index = 0; index < orgOptions.value.length; index++) {
|
||||
const obj = orgOptions.value[index];
|
||||
if (obj.id == targetDepartment) {
|
||||
name = obj.name;
|
||||
break;
|
||||
}
|
||||
const subObjArray = obj['children'];
|
||||
if (subObjArray && subObjArray.length > 0) {
|
||||
for (let i = 0; i < subObjArray.length; i++) {
|
||||
const item = subObjArray[i];
|
||||
if (item.id == targetDepartment) {
|
||||
name = item.name;
|
||||
break;
|
||||
}
|
||||
for (let index = 0; index < subObjArray.length; index++) {
|
||||
const item = subObjArray[index];
|
||||
if (item.id == targetDepartment) {
|
||||
name = item.name;
|
||||
}
|
||||
}
|
||||
if (name) break;
|
||||
}
|
||||
return name;
|
||||
};
|
||||
|
||||
const handleViewDetail = async (row) => {
|
||||
// 确保科室数据已加载,以便将 ID 解析为名称
|
||||
if (!orgOptions.value || orgOptions.value.length === 0) {
|
||||
await getLocationInfo();
|
||||
}
|
||||
|
||||
const handleViewDetail = (row) => {
|
||||
currentDetail.value = row;
|
||||
// 解析 descJson
|
||||
if (row.descJson) {
|
||||
try {
|
||||
const obj = JSON.parse(row.descJson);
|
||||
obj.targetDepartment = recursionFun(obj.targetDepartment);
|
||||
// 转换申请类型编码为可读文本
|
||||
if (obj.applicationType === 0) obj.applicationType = '普通';
|
||||
else if (obj.applicationType === 1) obj.applicationType = '急诊';
|
||||
descJsonData.value = obj;
|
||||
// descJsonData.value = JSON.parse(row.descJson);
|
||||
} catch (e) {
|
||||
console.error('解析 descJson 失败:', e);
|
||||
descJsonData.value = null;
|
||||
@@ -455,92 +374,15 @@ const handleViewDetail = async (row) => {
|
||||
detailDialogVisible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改检验申请单(待签发状态)
|
||||
*/
|
||||
const handleEdit = async (row) => {
|
||||
// 确保科室数据已加载
|
||||
if (!orgOptions.value || orgOptions.value.length === 0) {
|
||||
await getLocationInfo();
|
||||
}
|
||||
editRowData.value = row;
|
||||
editDialogVisible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 编辑弹窗提交成功回调
|
||||
*/
|
||||
const handleEditSubmitOk = async () => {
|
||||
editDialogVisible.value = false;
|
||||
editRowData.value = null;
|
||||
proxy.$modal?.msgSuccess?.('修改成功');
|
||||
await fetchData();
|
||||
};
|
||||
|
||||
/**
|
||||
* 编辑弹窗提交按钮
|
||||
*/
|
||||
const submitEditForm = () => {
|
||||
if (editFormRef.value?.submit) {
|
||||
editFormRef.value.submit();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除检验申请单(仅待签发状态可删除)
|
||||
*/
|
||||
const handleDelete = async (row) => {
|
||||
try {
|
||||
await proxy.$modal?.confirm?.(`确定要删除申请单 "${row.prescriptionNo}" 吗?此操作不可恢复。`);
|
||||
} catch {
|
||||
return; // 用户取消
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await deleteRequestForm({ requestFormId: row.requestFormId });
|
||||
if (res?.code === 200) {
|
||||
proxy.$modal?.msgSuccess?.('删除成功');
|
||||
await fetchData();
|
||||
} else {
|
||||
proxy.$modal?.msgError?.(res?.msg || '删除失败');
|
||||
}
|
||||
} catch (e) {
|
||||
proxy.$modal?.msgError?.(e.message || '删除异常');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 撤回检验申请单(已签发状态撤回至待签发)
|
||||
*/
|
||||
const handleWithdraw = async (row) => {
|
||||
try {
|
||||
await proxy.$modal?.confirm?.(`确定要撤回申请单 "${row.prescriptionNo}" 吗?撤回后将恢复为待签发状态。`);
|
||||
} catch {
|
||||
return; // 用户取消
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await withdrawRequestForm({ requestFormId: row.requestFormId });
|
||||
if (res?.code === 200) {
|
||||
proxy.$modal?.msgSuccess?.('撤回成功');
|
||||
await fetchData();
|
||||
} else {
|
||||
proxy.$modal?.msgError?.(res?.msg || '撤回失败');
|
||||
}
|
||||
} catch (e) {
|
||||
proxy.$modal?.msgError?.(e.message || '撤回异常');
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => patientInfo.value?.encounterId,
|
||||
async (val) => {
|
||||
(val) => {
|
||||
if (val) {
|
||||
// 设置默认日期范围为近7天
|
||||
const today = new Date();
|
||||
const sevenDaysAgo = new Date(today);
|
||||
sevenDaysAgo.setDate(today.getDate() - 6); // 包含今天共7天
|
||||
|
||||
|
||||
// 格式化为 YYYY-MM-DD
|
||||
const formatDate = (date) => {
|
||||
const year = date.getFullYear();
|
||||
@@ -548,19 +390,19 @@ watch(
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
|
||||
filterForm.value.dateRange = [
|
||||
formatDate(sevenDaysAgo),
|
||||
formatDate(today)
|
||||
];
|
||||
|
||||
await Promise.all([fetchData(), getLocationInfo()]);
|
||||
|
||||
fetchData();
|
||||
getLocationInfo();
|
||||
} else {
|
||||
tableData.value = [];
|
||||
// 重置筛选条件
|
||||
filterForm.value.dateRange = [];
|
||||
filterForm.value.status = '';
|
||||
filterForm.value.keyword = '';
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
|
||||
@@ -568,11 +568,6 @@ function handleMaindise(value, index) {
|
||||
* 保存诊断
|
||||
*/
|
||||
function handleSaveDiagnosis() {
|
||||
// 防止重复点击保存
|
||||
if (isSaving.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let index = 0; index < (form.value.diagnosisList || []).length; index++) {
|
||||
const item = form.value.diagnosisList[index];
|
||||
if (!item.diagSrtNo) {
|
||||
@@ -605,7 +600,7 @@ function handleSaveDiagnosis() {
|
||||
|
||||
// 步骤2:重新分配连续的序号(从1开始)
|
||||
sortedList.forEach((item, index) => {
|
||||
item.diagSrtNo = index + 1; // 这里是关键!把”诊断排序”改成新顺序
|
||||
item.diagSrtNo = index + 1; // 这里是关键!把“诊断排序”改成新顺序
|
||||
});
|
||||
|
||||
// 步骤3:提交排序后的数据
|
||||
@@ -615,12 +610,12 @@ function handleSaveDiagnosis() {
|
||||
diagnosisChildList: sortedList,
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 步骤4:更新本地数据,使用全新对象防止响应式问题
|
||||
form.value.diagnosisList = sortedList.map(item => ({ ...item }));
|
||||
|
||||
emits('diagnosisSave', false);
|
||||
proxy.$modal.msgSuccess('诊断已保存');
|
||||
|
||||
// 保存成功后从服务器重新加载数据,确保前后端数据一致
|
||||
getList();
|
||||
|
||||
// 食源性疾病逻辑
|
||||
isFoodDiseasesNew({ encounterId: props.patientInfo.encounterId }).then((res2) => {
|
||||
if (res2.code === 20 && res2.data) {
|
||||
|
||||
@@ -1,24 +1,6 @@
|
||||
import request from '@/utils/request';
|
||||
// 申请单相关接口
|
||||
|
||||
// 手术项目专用分页查询(仅手术 + 定价,无库存/草稿库存等无关逻辑)
|
||||
export function getSurgeryPage(params) {
|
||||
return request({
|
||||
url: '/doctor-station/advice/surgery-page',
|
||||
method: 'get',
|
||||
params: params,
|
||||
});
|
||||
}
|
||||
|
||||
// 检查项目专用分页查询(仅检查(23) + 定价,绕过 AOP 校验提升加载速度)
|
||||
export function getExaminationPage(params) {
|
||||
return request({
|
||||
url: '/doctor-station/advice/examination-page',
|
||||
method: 'get',
|
||||
params: params,
|
||||
});
|
||||
}
|
||||
|
||||
//医嘱大下拉
|
||||
export function getApplicationList(queryParams) {
|
||||
return request({
|
||||
|
||||
@@ -112,8 +112,6 @@ const showApplicationFormDialog = (name) => {
|
||||
setTimeout(() => {
|
||||
applicationFormName.value = components.value[name];
|
||||
applicationFormDialogVisible.value = true;
|
||||
// 列表(项目列表)
|
||||
applicationFormNameRef?.value.getList?.();
|
||||
// 科室列表
|
||||
applicationFormNameRef?.value.getLocationInfo();
|
||||
// 诊断目录列表
|
||||
@@ -123,8 +121,6 @@ const showApplicationFormDialog = (name) => {
|
||||
applicationFormName.value = components.value[name];
|
||||
applicationFormDialogVisible.value = true;
|
||||
nextTick(() => {
|
||||
// 列表(项目列表)
|
||||
applicationFormNameRef?.value.getList?.();
|
||||
// 科室列表
|
||||
applicationFormNameRef?.value.getLocationInfo();
|
||||
// 诊断目录列表
|
||||
|
||||
@@ -249,7 +249,7 @@ const submit = () => {
|
||||
requestFormId: '', // 申请单ID
|
||||
name: '输血申请单',
|
||||
descJson: JSON.stringify(form),
|
||||
categoryEnum: '23', // 21 检验 22 检查 23 输血 24 手术(避开 adviceType 1-6 碰撞)
|
||||
categoryEnum: '3', // 1 检验 2 检查 3 输血 4 手术
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
proxy.$message.success(res.msg);
|
||||
|
||||
@@ -6,26 +6,9 @@
|
||||
<template>
|
||||
<div class="LaboratoryTests-container">
|
||||
<div v-loading="loading" class="transfer-wrapper">
|
||||
<!-- 远程搜索框 -->
|
||||
<div class="search-bar">
|
||||
<el-input
|
||||
v-model="searchKey"
|
||||
placeholder="输入项目代码/名称搜索"
|
||||
clearable
|
||||
@keyup.enter="handleSearch"
|
||||
@clear="handleSearch"
|
||||
style="width: 300px; margin-bottom: 10px"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="handleSearch">搜索</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
<span v-if="!searchKey" class="total-count">共 {{ totalCount }} 项</span>
|
||||
<span v-else class="total-count">搜索到 {{ filteredCount }} 项 / 共 {{ totalCount }} 项</span>
|
||||
</div>
|
||||
<el-transfer
|
||||
v-model="transferValue"
|
||||
:data="transferData"
|
||||
:data="applicationList"
|
||||
filter-placeholder="项目代码/名称"
|
||||
filterable
|
||||
:titles="['未选择', '已选择']"
|
||||
@@ -89,55 +72,16 @@
|
||||
<el-input v-model="form.attention" autocomplete="off" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 申请类型 -->
|
||||
<el-col :span="12">
|
||||
<el-form-item label="申请类型" prop="applicationType" style="width: 100%">
|
||||
<el-radio-group v-model="form.applicationType">
|
||||
<el-radio :value="0">普通</el-radio>
|
||||
<el-radio :value="1">急诊</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 标本类型 -->
|
||||
<el-col :span="12">
|
||||
<el-form-item label="标本类型" prop="specimenName" style="width: 100%">
|
||||
<el-select v-model="form.specimenName" placeholder="请选择标本类型" style="width: 100%">
|
||||
<el-option label="血液" value="血液" />
|
||||
<el-option label="尿液" value="尿液" />
|
||||
<el-option label="粪便" value="粪便" />
|
||||
<el-option label="痰液" value="痰液" />
|
||||
<el-option label="咽拭子" value="咽拭子" />
|
||||
<el-option label="脑脊液" value="脑脊液" />
|
||||
<el-option label="胸腹水" value="胸腹水" />
|
||||
<el-option label="关节液" value="关节液" />
|
||||
<el-option label="分泌物" value="分泌物" />
|
||||
<el-option label="其他" value="其他" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 执行时间 -->
|
||||
<el-col :span="12">
|
||||
<el-form-item label="执行时间" prop="executeTime" style="width: 100%">
|
||||
<el-date-picker
|
||||
v-model="form.executeTime"
|
||||
type="datetime"
|
||||
placeholder="选择执行时间"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup name="LaboratoryTests">
|
||||
import {getCurrentInstance, onMounted, reactive, ref, watch, computed} from 'vue';
|
||||
import {getCurrentInstance, onBeforeMount, onMounted, reactive, watch} from 'vue';
|
||||
import {patientInfo} from '../../../store/patient.js';
|
||||
import {getApplicationList, saveInspection} from './api';
|
||||
import {getOrgList} from '@/views/doctorstation/components/api.js';
|
||||
import {getDepartmentList} from '@/api/public.js';
|
||||
import {getEncounterDiagnosis} from '../../api.js';
|
||||
import {ElMessage} from 'element-plus';
|
||||
|
||||
@@ -155,92 +99,48 @@ const findTreeItem = (list, id) => {
|
||||
return null;
|
||||
};
|
||||
const emits = defineEmits(['submitOk']);
|
||||
const props = defineProps({
|
||||
editData: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
const isEditMode = computed(() => !!props.editData?.requestFormId);
|
||||
const props = defineProps({});
|
||||
const state = reactive({});
|
||||
const applicationListAll = ref([]);
|
||||
const applicationListAll = ref();
|
||||
const applicationList = ref();
|
||||
const loading = ref(false);
|
||||
const orgOptions = ref([]);
|
||||
const searchKey = ref('');
|
||||
const totalCount = ref(0);
|
||||
|
||||
// 将已加载的全部数据转为 transfer 组件所需的格式
|
||||
const buildTransferData = (records) => {
|
||||
return records.map((item) => {
|
||||
const priceInfo = item.priceList?.[0] || {};
|
||||
const price = priceInfo.price != null ? Number(priceInfo.price).toFixed(2) : '0.00';
|
||||
const unit = item.unitCode_dictText || item.unitCode || '';
|
||||
return {
|
||||
adviceDefinitionId: item.adviceDefinitionId,
|
||||
orgId: item.orgId,
|
||||
label: item.adviceName + ' (¥' + price + '/' + unit + ')',
|
||||
key: item.adviceDefinitionId,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// 加载全部数据(不分页,一次性拉取)
|
||||
const loadAllData = async () => {
|
||||
const orgOptions = ref([]); // 科室选项
|
||||
const getList = () => {
|
||||
if (!patientInfo.value?.inHospitalOrgId) {
|
||||
applicationListAll.value = [];
|
||||
applicationList.value = [];
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
// 使用大 pageSize 一次性拉取所有启用状态的检验类诊疗项目
|
||||
const res = await getApplicationList({
|
||||
pageSize: 9999,
|
||||
pageNo: 1,
|
||||
categoryCode: '22',
|
||||
organizationId: patientInfo.value.inHospitalOrgId,
|
||||
adviceTypes: [3], // 1 药品 2 耗材 3 诊疗
|
||||
getApplicationList({
|
||||
pageSize: 9999,
|
||||
pageNum: 1,
|
||||
categoryCode: '22',
|
||||
organizationId: patientInfo.value.inHospitalOrgId,
|
||||
adviceTypes: [3], //1 药品 2耗材 3诊疗
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
applicationListAll.value = res.data.records;
|
||||
applicationList.value = res.data.records.map((item) => {
|
||||
const priceInfo = item.priceList?.[0] || {};
|
||||
const price = priceInfo.price != null ? Number(priceInfo.price).toFixed(2) : '0.00';
|
||||
const unit = item.unitCode_dictText || item.unitCode || '';
|
||||
return {
|
||||
adviceDefinitionId: item.adviceDefinitionId,
|
||||
orgId: item.orgId,
|
||||
label: item.adviceName + ' (¥' + price + '/' + unit + ')',
|
||||
key: item.adviceDefinitionId,
|
||||
};
|
||||
});
|
||||
console.log('applicationList========>', JSON.stringify(res.data.records));
|
||||
} else {
|
||||
proxy.$message.error(res.message);
|
||||
applicationList.value = [];
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
if (res.code !== 200) {
|
||||
proxy.$message.error(res.message);
|
||||
applicationListAll.value = [];
|
||||
return;
|
||||
}
|
||||
applicationListAll.value = res.data?.records || [];
|
||||
totalCount.value = res.data?.total || 0;
|
||||
} catch (e) {
|
||||
proxy.$message.error('获取检验项目列表失败');
|
||||
applicationListAll.value = [];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 根据搜索关键词过滤数据
|
||||
const filterData = (key) => {
|
||||
if (!key || key.trim() === '') {
|
||||
return applicationListAll.value;
|
||||
}
|
||||
const lowerKey = key.toLowerCase().trim();
|
||||
return applicationListAll.value.filter((item) => {
|
||||
return (
|
||||
item.adviceName?.toLowerCase().includes(lowerKey) ||
|
||||
item.pyStr?.toLowerCase().includes(lowerKey) ||
|
||||
item.adviceBusNo?.toLowerCase().includes(lowerKey)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// transfer 组件实际显示的数据(受搜索词影响)
|
||||
const transferData = computed(() => buildTransferData(filterData(searchKey.value)));
|
||||
// 当前显示的条数
|
||||
const filteredCount = computed(() => filterData(searchKey.value).length);
|
||||
|
||||
const getList = async () => {
|
||||
await loadAllData();
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
// 搜索时保持已选中的项目不受影响
|
||||
};
|
||||
const transferValue = ref([]);
|
||||
const form = reactive({
|
||||
@@ -252,13 +152,11 @@ const form = reactive({
|
||||
otherDiagnosis: '', // 其他诊断
|
||||
relatedResult: '', // 相关结果
|
||||
attention: '', // 注意事项
|
||||
applicationType: 0, // 申请类型 0-普通 1-急诊
|
||||
specimenName: '血液', // 标本类型
|
||||
executeTime: null, // 执行时间
|
||||
primaryDiagnosisList: [], //主诊断目录
|
||||
otherDiagnosisList: [], //其他断目录
|
||||
});
|
||||
const rules = reactive({});
|
||||
onBeforeMount(() => {});
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
@@ -271,25 +169,13 @@ const projectWithDepartment = (selectProjectIds, type) => {
|
||||
let isRelease = true;
|
||||
// 选中项目的数组
|
||||
const arr = [];
|
||||
// 根据选中的项目id查找对应的项目(从全部原始数据中查找)
|
||||
// 根据选中的项目id查找对应的项目
|
||||
selectProjectIds.forEach((element) => {
|
||||
const searchData = applicationListAll.value.find((item) => {
|
||||
const searchData = applicationList.value.find((item) => {
|
||||
return element == item.adviceDefinitionId;
|
||||
});
|
||||
if (searchData) {
|
||||
const priceInfo = searchData.priceList?.[0] || {};
|
||||
const price = priceInfo.price != null ? Number(priceInfo.price).toFixed(2) : '0.00';
|
||||
const unit = searchData.unitCode_dictText || searchData.unitCode || '';
|
||||
arr.push({
|
||||
adviceDefinitionId: searchData.adviceDefinitionId,
|
||||
orgId: searchData.orgId,
|
||||
label: searchData.adviceName + ' (¥' + price + '/' + unit + ')',
|
||||
key: searchData.adviceDefinitionId,
|
||||
});
|
||||
}
|
||||
arr.push(searchData);
|
||||
});
|
||||
// 保存用户手动选择的发往科室(提交时需要保留)
|
||||
const manualDept = type === 2 ? form.targetDepartment : '';
|
||||
// 清空科室
|
||||
form.targetDepartment = '';
|
||||
if (arr.length > 0) {
|
||||
@@ -308,27 +194,14 @@ const projectWithDepartment = (selectProjectIds, type) => {
|
||||
// 选中项目中的执行科室id与全部科室数据做匹配
|
||||
const findItem = findTreeItem(orgOptions.value, obj.orgId);
|
||||
if (!findItem) {
|
||||
// type=2(提交)时,若用户已手动选择发往科室,则允许提交
|
||||
if (type === 2 && manualDept) {
|
||||
form.targetDepartment = manualDept;
|
||||
isRelease = true;
|
||||
} else if (type === 2 && !manualDept) {
|
||||
// 提交时用户未手动选择科室,才提示错误
|
||||
isRelease = false;
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: '未找到项目执行的科室',
|
||||
});
|
||||
} else {
|
||||
// type=1(选择项目变化)时,不弹窗,仅清空科室让用户自行选择
|
||||
isRelease = false;
|
||||
}
|
||||
isRelease = false;
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: '未找到项目执行的科室',
|
||||
});
|
||||
}
|
||||
if (findItem && isRelease) {
|
||||
// 提交时若用户已选「发往科室」,不得用项目默认执行科室覆盖
|
||||
if (type === 2 && manualDept) {
|
||||
form.targetDepartment = manualDept;
|
||||
} else {
|
||||
if (type == 1) {
|
||||
if (isRelease) {
|
||||
form.targetDepartment = findItem.id;
|
||||
}
|
||||
}
|
||||
@@ -342,44 +215,6 @@ watch(
|
||||
projectWithDepartment(newValue, 1);
|
||||
}
|
||||
);
|
||||
|
||||
// 编辑模式下,回显已有数据
|
||||
watch(
|
||||
() => props.editData,
|
||||
(newData) => {
|
||||
if (!newData || !newData.requestFormId) return;
|
||||
|
||||
// 解析 descJson 回填表单
|
||||
if (newData.descJson) {
|
||||
try {
|
||||
const obj = JSON.parse(newData.descJson);
|
||||
Object.keys(form).forEach((key) => {
|
||||
if (obj[key] !== undefined) {
|
||||
form[key] = obj[key];
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('解析 descJson 失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 回填已选项目
|
||||
if (newData.requestFormDetailList && newData.requestFormDetailList.length > 0) {
|
||||
// 从全部数据中匹配已选项目
|
||||
const selectedIds = [];
|
||||
newData.requestFormDetailList.forEach((detail) => {
|
||||
const matched = applicationListAll.value.find(
|
||||
(item) => item.adviceName === detail.adviceName
|
||||
);
|
||||
if (matched) {
|
||||
selectedIds.push(matched.adviceDefinitionId);
|
||||
}
|
||||
});
|
||||
transferValue.value = selectedIds;
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
const submit = () => {
|
||||
if (transferValue.value.length == 0) {
|
||||
return proxy.$message.error('请选择申请单');
|
||||
@@ -397,7 +232,7 @@ const submit = () => {
|
||||
unitCode: item.priceList[0].unitCode /** 请求单位编码 */,
|
||||
unitPrice: item.priceList[0].price /** 单价 */,
|
||||
totalPrice: item.priceList[0].price /** 总价 */,
|
||||
positionId: form.targetDepartment || item.positionId, // 用户指定发往科室优先于项目默认执行科室
|
||||
positionId: item.positionId, //执行科室id
|
||||
ybClassEnum: item.ybClassEnum, //类别医保编码
|
||||
conditionId: item.conditionId, //诊断ID
|
||||
encounterDiagnosisId: item.encounterDiagnosisId, //就诊诊断id
|
||||
@@ -412,15 +247,15 @@ const submit = () => {
|
||||
patientId: patientInfo.value.patientId, //患者ID
|
||||
encounterId: patientInfo.value.encounterId, // 就诊ID
|
||||
organizationId: patientInfo.value.inHospitalOrgId, // 医疗机构ID
|
||||
requestFormId: isEditMode.value ? props.editData.requestFormId : '', // 申请单ID(编辑模式传入,新增为空)
|
||||
requestFormId: '', // 申请单ID
|
||||
name: '检验申请单',
|
||||
descJson: JSON.stringify(form),
|
||||
categoryEnum: '21', // 21 检验 22 检查 23 输血 24 手术(避开 adviceType 1-6 碰撞)
|
||||
categoryEnum: '1', // 1 检验 2 检查 3 输血 4 手术
|
||||
};
|
||||
saveInspection(params).then((res) => {
|
||||
if (res.code === 200) {
|
||||
proxy.$message.success(isEditMode.value ? '修改成功' : res.msg);
|
||||
transferValue.value = [];
|
||||
proxy.$message.success(res.msg);
|
||||
applicationList.value = [];
|
||||
emits('submitOk');
|
||||
} else {
|
||||
proxy.$message.error(res.message);
|
||||
@@ -429,8 +264,8 @@ const submit = () => {
|
||||
};
|
||||
/** 查询科室 */
|
||||
const getLocationInfo = () => {
|
||||
getOrgList().then((res) => {
|
||||
orgOptions.value = res.data.records;
|
||||
getDepartmentList().then((res) => {
|
||||
orgOptions.value = res.data || [];
|
||||
console.log('科室========>', JSON.stringify(orgOptions.value));
|
||||
});
|
||||
};
|
||||
@@ -467,7 +302,7 @@ function getDiagnosisList() {
|
||||
}
|
||||
});
|
||||
}
|
||||
defineExpose({ state, submit, getLocationInfo, getDiagnosisList, getList });
|
||||
defineExpose({ state, submit, getLocationInfo, getDiagnosisList });
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.LaboratoryTests-container {
|
||||
@@ -477,19 +312,6 @@ defineExpose({ state, submit, getLocationInfo, getDiagnosisList, getList });
|
||||
.transfer-wrapper {
|
||||
position: relative;
|
||||
min-height: 300px;
|
||||
|
||||
.search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.total-count {
|
||||
font-size: 13px;
|
||||
color: #909399;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-transfer {
|
||||
|
||||
@@ -1,217 +1,91 @@
|
||||
<!--
|
||||
* @Author: sjjh
|
||||
* @Date: 2025-09-05 22:35:29
|
||||
* @Description: 检查申请单 - 现代化医疗风格布局
|
||||
* @Description: 检查
|
||||
-->
|
||||
<template>
|
||||
<div class="medicalExaminations-container">
|
||||
<!-- 主体内容 -->
|
||||
<div class="form-body">
|
||||
<!-- 右上角:紧急程度 -->
|
||||
<div class="urgency-bar">
|
||||
<span class="urgency-bar-label">紧急程度:</span>
|
||||
<el-radio-group v-model="form.urgencyLevel" @change="handleUrgencyChange" size="small">
|
||||
<el-radio-button label="routine">普通</el-radio-button>
|
||||
<el-radio-button label="emergency">急诊</el-radio-button>
|
||||
</el-radio-group>
|
||||
<transition name="el-fade-in-linear">
|
||||
<span v-if="form.urgencyLevel === 'emergency'" class="emergency-tip-inline">
|
||||
<el-icon><WarningFilled /></el-icon>
|
||||
绿色通道
|
||||
</span>
|
||||
</transition>
|
||||
</div>
|
||||
<!-- 选择检查项目 -->
|
||||
<div class="section-card">
|
||||
<div class="transfer-wrapper">
|
||||
<el-transfer
|
||||
v-model="transferValue"
|
||||
:data="applicationList"
|
||||
filter-placeholder="项目代码/名称"
|
||||
filterable
|
||||
:titles="['可选项目', '已选项目']"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-form :model="form" :rules="rules" ref="formRef" label-position="top" class="info-form">
|
||||
<!-- 第一行:发往科室 + 紧急程度 + 期望检查时间 -->
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="发往科室" prop="targetDepartment">
|
||||
<div v-loading="loading" class="transfer-wrapper">
|
||||
<el-transfer
|
||||
v-model="transferValue"
|
||||
:data="applicationList"
|
||||
filter-placeholder="项目代码/名称"
|
||||
filterable
|
||||
:titles="['未选择', '已选择']"
|
||||
/>
|
||||
</div>
|
||||
<div class="bloodTransfusion-form">
|
||||
<el-form :model="form" :rules="rules" ref="formRef" label-width="120px" class="demo-ruleForm">
|
||||
<el-row :gutter="20">
|
||||
<!-- <el-col :span="12">
|
||||
<el-form-item label="项目类别" prop="categoryType" style="width: 100%">
|
||||
<el-input v-model="form.categoryType" autocomplete="off" />
|
||||
</el-form-item>
|
||||
</el-col> -->
|
||||
<el-col :span="12">
|
||||
<el-form-item label="发往科室" prop="targetDepartment" style="width: 100%">
|
||||
<!-- <el-input v-model="form.targetDepartment" autocomplete="off" /> -->
|
||||
<el-tree-select
|
||||
clearable
|
||||
style="width: 100%"
|
||||
v-model="form.targetDepartment"
|
||||
filterable
|
||||
:data="orgOptions"
|
||||
:props="{ value: 'id', label: 'name', children: 'children' }"
|
||||
:props="{
|
||||
value: 'id',
|
||||
label: 'name',
|
||||
children: 'children',
|
||||
}"
|
||||
value-key="id"
|
||||
check-strictly
|
||||
placeholder="请选择执行科室"
|
||||
placeholder="请选择科室"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="期望检查时间">
|
||||
<el-date-picker
|
||||
v-model="form.expectedExaminationTime"
|
||||
type="datetime"
|
||||
placeholder="默认当前时间"
|
||||
style="width: 100%"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
:disabled-date="disabledFutureDate"
|
||||
:default-value="new Date()"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 第二行:症状 + 体征 -->
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="症状">
|
||||
<el-input v-model="form.symptom" autocomplete="off" type="textarea" :rows="2" placeholder="请输入患者症状" />
|
||||
<el-form-item label="症状" prop="symptom" style="width: 100%">
|
||||
<el-input v-model="form.symptom" autocomplete="off" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="体征">
|
||||
<el-input v-model="form.sign" autocomplete="off" type="textarea" :rows="2" placeholder="请输入患者体征" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 第三行:临床诊断 + 其他诊断 -->
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="临床诊断">
|
||||
<el-input disabled v-model="form.clinicalDiagnosis" placeholder="自动带入主诊断" />
|
||||
<el-form-item label="体征" prop="sign" style="width: 100%">
|
||||
<el-input v-model="form.sign" autocomplete="off" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="其他诊断">
|
||||
<el-input disabled v-model="form.otherDiagnosis" placeholder="自动带入其他诊断" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 第四行:相关结果 + 注意事项 -->
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="相关结果">
|
||||
<el-input v-model="form.relatedResult" autocomplete="off" type="textarea" :rows="2" placeholder="请输入相关检验结果" />
|
||||
<el-form-item label="临床诊断" prop="clinicalDiagnosis" style="width: 100%">
|
||||
<el-input disabled v-model="form.clinicalDiagnosis" autocomplete="off" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="注意事项">
|
||||
<el-input v-model="form.attention" autocomplete="off" type="textarea" :rows="2" placeholder="请输入检查注意事项" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 第五行:检查目的 + 病史摘要 -->
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="检查目的" prop="examinationPurpose">
|
||||
<el-input
|
||||
v-model="form.examinationPurpose"
|
||||
autocomplete="off"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
placeholder="请输入检查目的,如:明确诊断、术后复查、疗效评估等"
|
||||
/>
|
||||
<el-form-item label="其他诊断" prop="otherDiagnosis" style="width: 100%">
|
||||
<el-input disabled v-model="form.otherDiagnosis" autocomplete="off" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="病史摘要" prop="medicalHistorySummary">
|
||||
<div class="history-field-wrapper">
|
||||
<el-input
|
||||
v-model="form.medicalHistorySummary"
|
||||
autocomplete="off"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="请简要描述患者病史摘要"
|
||||
/>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
size="small"
|
||||
class="history-sync-btn"
|
||||
@click="handleSyncHistory"
|
||||
:loading="syncingHistory"
|
||||
>
|
||||
<el-icon><Refresh /></el-icon>
|
||||
同步
|
||||
</el-button>
|
||||
</div>
|
||||
<el-form-item label="相关结果" prop="relatedResult" style="width: 100%">
|
||||
<el-input v-model="form.relatedResult" autocomplete="off" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 第六行:过敏史 -->
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="过敏史">
|
||||
<div class="allergy-wrapper">
|
||||
<el-input
|
||||
v-model="form.allergyHistory"
|
||||
autocomplete="off"
|
||||
type="textarea"
|
||||
:rows="1"
|
||||
:class="{ 'allergy-danger': isSevereAllergy }"
|
||||
placeholder="如:造影剂过敏史等(系统将自动从患者档案带入)"
|
||||
/>
|
||||
<div class="allergy-actions">
|
||||
<span v-if="isSevereAllergy" class="allergy-severe-tag-inline">
|
||||
<el-icon><WarningFilled /></el-icon>
|
||||
严重过敏
|
||||
</span>
|
||||
<el-checkbox v-model="form.allergyConfirmed" size="small">
|
||||
已通过口头询问确认无过敏史
|
||||
</el-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="注意事项" prop="attention" style="width: 100%">
|
||||
<el-input v-model="form.attention" autocomplete="off" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 急诊确认弹窗 -->
|
||||
<el-dialog
|
||||
v-model="emergencyDialogVisible"
|
||||
title="急诊确认"
|
||||
width="400px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<div class="emergency-dialog-content">
|
||||
<el-icon class="emergency-dialog-icon"><WarningFilled /></el-icon>
|
||||
<p class="emergency-dialog-text">请确认患者符合急诊指征</p>
|
||||
<p class="emergency-dialog-sub">急诊申请单将进入绿色通道优先处理</p>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="cancelEmergency">取消</el-button>
|
||||
<el-button type="danger" @click="confirmEmergency">确认急诊</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="MedicalExaminations">
|
||||
import {getCurrentInstance, onMounted, reactive, ref, watch, computed, nextTick} from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import {getCurrentInstance, onBeforeMount, onMounted, reactive, watch} from 'vue';
|
||||
import {patientInfo} from '../../../store/patient.js';
|
||||
import {getDepartmentList} from '@/api/public.js';
|
||||
import {getEncounterDiagnosis} from '../../api.js';
|
||||
import {getExaminationPage, saveCheckd} from './api';
|
||||
import {ElMessage, ElMessageBox} from 'element-plus';
|
||||
import {WarningFilled, Warning, Refresh, Files, Document, EditPen, Aim, DocumentCopy} from '@element-plus/icons-vue';
|
||||
import {getApplicationList, saveCheckd} from './api';
|
||||
import {ElMessage} from 'element-plus';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// 递归查找树形科室节点
|
||||
const findTreeItem = (list, id) => {
|
||||
if (!list || list.length === 0) return null;
|
||||
@@ -224,65 +98,33 @@ const findTreeItem = (list, id) => {
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const emits = defineEmits(['submitOk']);
|
||||
const props = defineProps({
|
||||
isEditMode: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
editData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
// 支持通过props传入patientInfo
|
||||
externalPatientInfo: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
// 优先使用外部传入的patientInfo,否则使用store中的
|
||||
const effectivePatientInfo = computed(() => {
|
||||
return props.externalPatientInfo || patientInfo.value;
|
||||
});
|
||||
const orgOptions = ref([]);
|
||||
const props = defineProps({});
|
||||
const orgOptions = ref([]); // 科室选项
|
||||
const state = reactive({});
|
||||
const applicationListAll = ref();
|
||||
const applicationList = ref();
|
||||
const loading = ref(false);
|
||||
|
||||
// 过敏史相关
|
||||
const syncingHistory = ref(false);
|
||||
const emergencyDialogVisible = ref(false);
|
||||
const pendingUrgencyLevel = ref('');
|
||||
|
||||
// 严重过敏关键词
|
||||
const severeAllergyKeywords = ['造影剂', '碘', '青霉素', '普鲁卡因', '头孢', '麻醉剂', '海鲜', '过敏性休克'];
|
||||
|
||||
const isSevereAllergy = computed(() => {
|
||||
if (!form.allergyHistory) return false;
|
||||
return severeAllergyKeywords.some(keyword => form.allergyHistory.includes(keyword));
|
||||
});
|
||||
|
||||
const getList = () => {
|
||||
if (!effectivePatientInfo.value?.inHospitalOrgId) {
|
||||
if (!patientInfo.value?.inHospitalOrgId) {
|
||||
applicationList.value = [];
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
getExaminationPage({
|
||||
organizationId: effectivePatientInfo.value.inHospitalOrgId,
|
||||
pageNo: 1,
|
||||
pageSize: 5000,
|
||||
searchKey: '',
|
||||
getApplicationList({
|
||||
pageSize: 500,
|
||||
pageNum: 1,
|
||||
categoryCode: '23',
|
||||
organizationId: patientInfo.value.inHospitalOrgId,
|
||||
adviceTypes: [3], //1 药品 2耗材 3诊疗
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 200 && res.data?.records) {
|
||||
if (res.code === 200) {
|
||||
applicationListAll.value = res.data.records;
|
||||
applicationList.value = res.data.records.map((item) => {
|
||||
const price = item.price != null ? Number(item.price).toFixed(2) : '0.00';
|
||||
const unit = item.unitCodeDictText || item.unitCode || '';
|
||||
const priceInfo = item.priceList?.[0] || {};
|
||||
const price = priceInfo.price != null ? Number(priceInfo.price).toFixed(2) : '0.00';
|
||||
const unit = item.unitCode_dictText || item.unitCode || '';
|
||||
return {
|
||||
adviceDefinitionId: item.adviceDefinitionId,
|
||||
orgId: item.orgId,
|
||||
@@ -290,22 +132,6 @@ const getList = () => {
|
||||
key: item.adviceDefinitionId,
|
||||
};
|
||||
});
|
||||
// 编辑模式下,加载完数据后设置已选项目
|
||||
if (props.isEditMode && props.editData?.requestFormDetailList?.length) {
|
||||
nextTick(() => {
|
||||
// 使用 adviceName 匹配
|
||||
const selectedNames = props.editData.requestFormDetailList.map(item => item.adviceName);
|
||||
const selectedIds = [];
|
||||
applicationList.value?.forEach(app => {
|
||||
// 匹配时去掉价格部分,只比较名称
|
||||
const appName = app.label?.split(' (')[0];
|
||||
if (selectedNames.includes(appName)) {
|
||||
selectedIds.push(app.key);
|
||||
}
|
||||
});
|
||||
transferValue.value = selectedIds;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
proxy.$message.error(res.message);
|
||||
applicationList.value = [];
|
||||
@@ -315,499 +141,187 @@ const getList = () => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const transferValue = ref([]);
|
||||
|
||||
const form = reactive({
|
||||
targetDepartment: '',
|
||||
urgencyLevel: 'routine',
|
||||
allergyHistory: '',
|
||||
examinationPurpose: '',
|
||||
medicalHistorySummary: '',
|
||||
expectedExaminationTime: dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss'),
|
||||
symptom: '',
|
||||
sign: '',
|
||||
clinicalDiagnosis: '',
|
||||
otherDiagnosis: '',
|
||||
relatedResult: '',
|
||||
attention: '',
|
||||
primaryDiagnosisList: [],
|
||||
otherDiagnosisList: [],
|
||||
allergyConfirmed: false,
|
||||
// categoryType: '', // 项目类别
|
||||
targetDepartment: '', // 发往科室
|
||||
symptom: '', // 症状
|
||||
sign: '', // 体征
|
||||
clinicalDiagnosis: '', // 临床诊断
|
||||
otherDiagnosis: '', // 其他诊断
|
||||
relatedResult: '', // 相关结果
|
||||
attention: '', // 注意事项
|
||||
primaryDiagnosisList: [], //主诊断目录
|
||||
otherDiagnosisList: [], //其他断目录
|
||||
});
|
||||
|
||||
// 表单校验规则
|
||||
const rules = reactive({
|
||||
examinationPurpose: [
|
||||
{ required: true, message: '请输入检查目的', trigger: 'blur' },
|
||||
{ max: 200, message: '检查目的不能超过200个字符', trigger: 'blur' },
|
||||
],
|
||||
medicalHistorySummary: [
|
||||
{ required: true, message: '请输入病史摘要', trigger: 'blur' },
|
||||
],
|
||||
const rules = reactive({});
|
||||
onBeforeMount(() => {});
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
|
||||
// 禁用过去的时间
|
||||
const disabledFutureDate = (time) => {
|
||||
return time.getTime() < Date.now() - 8.64e7;
|
||||
};
|
||||
|
||||
// 紧急程度切换处理
|
||||
const handleUrgencyChange = (value) => {
|
||||
if (value === 'emergency') {
|
||||
emergencyDialogVisible.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
// 确认急诊
|
||||
const confirmEmergency = () => {
|
||||
emergencyDialogVisible.value = false;
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '已设置为急诊检查,将进入绿色通道',
|
||||
duration: 3000,
|
||||
/**
|
||||
* type(1:watch监听类型 2:点击保存类型)
|
||||
* selectProjectIds(选中项目的id数组)
|
||||
* */
|
||||
const projectWithDepartment = (selectProjectIds, type) => {
|
||||
//1.获取选中的项目 2.判断项目的执行科室是否相同 3.判断执行科室是否配置 4.将项目的执行科室复值到执行科室下拉选位置
|
||||
let isRelease = true;
|
||||
// 选中项目的数组
|
||||
const arr = [];
|
||||
// 根据选中的项目id查找对应的项目
|
||||
selectProjectIds.forEach((element) => {
|
||||
const searchData = applicationList.value.find((item) => {
|
||||
return element == item.adviceDefinitionId;
|
||||
});
|
||||
arr.push(searchData);
|
||||
});
|
||||
};
|
||||
|
||||
// 取消急诊
|
||||
const cancelEmergency = () => {
|
||||
emergencyDialogVisible.value = false;
|
||||
form.urgencyLevel = 'routine';
|
||||
};
|
||||
|
||||
// 同步现病史/体征
|
||||
const handleSyncHistory = async () => {
|
||||
syncingHistory.value = true;
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 800));
|
||||
if (form.symptom || form.sign) {
|
||||
form.medicalHistorySummary = `${form.symptom ? '症状:' + form.symptom + '\n' : ''}${form.sign ? '体征:' + form.sign : ''}`;
|
||||
} else {
|
||||
form.medicalHistorySummary = '患者目前一般情况可,无特殊不适主诉。';
|
||||
// 清空科室
|
||||
form.targetDepartment = '';
|
||||
if (arr.length > 0) {
|
||||
const obj = arr[0];
|
||||
// 判断科室是否相同
|
||||
const isCompare = arr.every((item) => {
|
||||
return item.orgId == obj.orgId;
|
||||
});
|
||||
if (!isCompare) {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: '执行科室不同',
|
||||
});
|
||||
isRelease = false;
|
||||
}
|
||||
ElMessage({ type: 'success', message: '已同步现病史/体征' });
|
||||
} catch (error) {
|
||||
ElMessage.error('同步失败');
|
||||
} finally {
|
||||
syncingHistory.value = false;
|
||||
}
|
||||
};
|
||||
// 选中项目中的执行科室id与全部科室数据做匹配
|
||||
const findItem = findTreeItem(orgOptions.value, obj.orgId);
|
||||
|
||||
// 自动带入患者过敏史
|
||||
const loadPatientAllergyHistory = () => {
|
||||
if (!effectivePatientInfo.value?.patientId) return;
|
||||
};
|
||||
|
||||
// 加载编辑数据 — 只负责解析 descJson 填充表单字段
|
||||
// 已选项目的匹配由 getList 数据加载完成后统一处理
|
||||
const loadEditData = () => {
|
||||
if (!props.isEditMode || !props.editData?.requestFormId) return;
|
||||
|
||||
if (props.editData.descJson) {
|
||||
try {
|
||||
const obj = JSON.parse(props.editData.descJson);
|
||||
form.targetDepartment = obj.targetDepartment || '';
|
||||
form.urgencyLevel = obj.urgencyLevel || 'routine';
|
||||
form.allergyHistory = obj.allergyHistory || '';
|
||||
form.examinationPurpose = obj.examinationPurpose || '';
|
||||
form.medicalHistorySummary = obj.medicalHistorySummary || '';
|
||||
form.expectedExaminationTime = obj.expectedExaminationTime || '';
|
||||
form.symptom = obj.symptom || '';
|
||||
form.sign = obj.sign || '';
|
||||
form.clinicalDiagnosis = obj.clinicalDiagnosis || '';
|
||||
form.otherDiagnosis = obj.otherDiagnosis || '';
|
||||
form.relatedResult = obj.relatedResult || '';
|
||||
form.attention = obj.attention || '';
|
||||
form.allergyConfirmed = obj.allergyConfirmed || false;
|
||||
} catch (e) {
|
||||
console.error('解析descJson失败:', e);
|
||||
if (!findItem) {
|
||||
isRelease = false;
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: '未找到项目执行的科室',
|
||||
});
|
||||
}
|
||||
if (type == 1) {
|
||||
if (isRelease) {
|
||||
form.targetDepartment = findItem.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isRelease;
|
||||
};
|
||||
|
||||
const projectWithDepartment = (selectProjectIds) => {
|
||||
if (!selectProjectIds || selectProjectIds.length === 0) {
|
||||
form.targetDepartment = '';
|
||||
// 监听选择项目变化
|
||||
watch(
|
||||
() => transferValue.value,
|
||||
(newValue) => {
|
||||
projectWithDepartment(newValue, 1);
|
||||
}
|
||||
};
|
||||
|
||||
watch(() => transferValue.value, (newValue) => {
|
||||
projectWithDepartment(newValue);
|
||||
});
|
||||
|
||||
const getPriorityCode = () => {
|
||||
return form.urgencyLevel === 'emergency' ? 1 : 0;
|
||||
};
|
||||
|
||||
);
|
||||
const submit = () => {
|
||||
if (transferValue.value.length == 0) {
|
||||
return proxy.$message.error('请选择检查项目');
|
||||
return proxy.$message.error('请选择申请单');
|
||||
}
|
||||
if (!form.targetDepartment) {
|
||||
return proxy.$message.error('请选择发往科室');
|
||||
if (!projectWithDepartment(transferValue.value, 2)) {
|
||||
return;
|
||||
}
|
||||
if (!form.examinationPurpose) {
|
||||
return ElMessageBox.alert('请输入检查目的', '提示', { confirmButtonText: '确定', type: 'warning' });
|
||||
}
|
||||
if (!form.medicalHistorySummary) {
|
||||
return ElMessageBox.alert('请输入病史摘要', '提示', { confirmButtonText: '确定', type: 'warning' });
|
||||
}
|
||||
if (!form.allergyHistory && !form.allergyConfirmed) {
|
||||
return ElMessageBox.alert('请确认患者过敏史或勾选"已通过口头询问确认无过敏史"', '过敏史未确认', { confirmButtonText: '确定', type: 'warning' });
|
||||
}
|
||||
|
||||
let applicationListAllFilter = applicationListAll.value.filter((item) => {
|
||||
return transferValue.value.includes(item.adviceDefinitionId);
|
||||
});
|
||||
// 从原始记录中提取检查项目名称,用于申请单名称字段
|
||||
const selectedNames = applicationListAllFilter.map(item => item.adviceName).join('+');
|
||||
applicationListAllFilter = applicationListAllFilter.map((item) => {
|
||||
// 新接口 getExaminationPage 返回扁平字段(price/unitCode),兼容旧接口 priceList[0]
|
||||
const priceInfo = item.priceList?.[0] || {};
|
||||
return {
|
||||
adviceDefinitionId: item.adviceDefinitionId,
|
||||
adviceName: item.adviceName,
|
||||
quantity: 1,
|
||||
unitCode: item.unitCode || priceInfo.unitCode || '',
|
||||
unitPrice: item.price ?? priceInfo.price ?? 0,
|
||||
totalPrice: item.price ?? priceInfo.price ?? 0,
|
||||
positionId: item.positionId,
|
||||
ybClassEnum: item.ybClassEnum,
|
||||
conditionId: item.conditionId,
|
||||
encounterDiagnosisId: item.encounterDiagnosisId,
|
||||
adviceType: item.adviceType,
|
||||
definitionId: item.chargeItemDefinitionId || priceInfo.definitionId || '',
|
||||
definitionDetailId: item.definitionDetailId || priceInfo.definitionDetailId || '',
|
||||
accountId: effectivePatientInfo.value.accountId,
|
||||
adviceDefinitionId: item.adviceDefinitionId /** 诊疗定义id */,
|
||||
quantity: 1, // /** 请求数量 */
|
||||
unitCode: item.priceList[0].unitCode /** 请求单位编码 */,
|
||||
unitPrice: item.priceList[0].price /** 单价 */,
|
||||
totalPrice: item.priceList[0].price /** 总价 */,
|
||||
positionId: item.positionId, //执行科室id
|
||||
ybClassEnum: item.ybClassEnum, //类别医保编码
|
||||
conditionId: item.conditionId, //诊断ID
|
||||
encounterDiagnosisId: item.encounterDiagnosisId, //就诊诊断id
|
||||
adviceType: item.adviceType, ///** 医嘱类型 */
|
||||
definitionId: item.priceList[0].definitionId, //费用定价主表ID */
|
||||
definitionDetailId: item.definitionDetailId, //费用定价子表ID */
|
||||
accountId: patientInfo.value.accountId, // // 账户id
|
||||
};
|
||||
});
|
||||
|
||||
const submitForm = { ...form, priorityCode: getPriorityCode() };
|
||||
console.log('提交 descJson:', JSON.stringify(submitForm));
|
||||
|
||||
// 如果是编辑模式,带上requestFormId
|
||||
const requestFormId = props.isEditMode ? props.editData?.requestFormId : '';
|
||||
|
||||
saveCheckd({
|
||||
activityList: applicationListAllFilter,
|
||||
patientId: effectivePatientInfo.value.patientId,
|
||||
encounterId: effectivePatientInfo.value.encounterId,
|
||||
organizationId: effectivePatientInfo.value.inHospitalOrgId,
|
||||
requestFormId: requestFormId,
|
||||
name: selectedNames,
|
||||
descJson: JSON.stringify(submitForm),
|
||||
categoryEnum: '22',
|
||||
patientId: patientInfo.value.patientId, //患者ID
|
||||
encounterId: patientInfo.value.encounterId, // 就诊ID
|
||||
organizationId: patientInfo.value.inHospitalOrgId, // 医疗机构ID
|
||||
requestFormId: '', // 申请单ID
|
||||
name: '检查申请单',
|
||||
descJson: JSON.stringify(form),
|
||||
categoryEnum: '2', // 1 检验 2 检查 3 输血 4 手术
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
if (props.isEditMode) {
|
||||
proxy.$message.success(res.msg || '修改成功');
|
||||
} else {
|
||||
proxy.$message.success(res.msg);
|
||||
}
|
||||
proxy.$message.success(res.msg);
|
||||
applicationList.value = [];
|
||||
resetForm();
|
||||
emits('submitOk');
|
||||
} else {
|
||||
proxy.$message.error(res.message);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
form.targetDepartment = '';
|
||||
form.urgencyLevel = 'routine';
|
||||
form.allergyHistory = '';
|
||||
form.examinationPurpose = '';
|
||||
form.medicalHistorySummary = '';
|
||||
form.expectedExaminationTime = dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss');
|
||||
form.symptom = '';
|
||||
form.sign = '';
|
||||
form.clinicalDiagnosis = '';
|
||||
form.otherDiagnosis = '';
|
||||
form.relatedResult = '';
|
||||
form.attention = '';
|
||||
form.primaryDiagnosisList = [];
|
||||
form.otherDiagnosisList = [];
|
||||
form.allergyConfirmed = false;
|
||||
transferValue.value = [];
|
||||
};
|
||||
|
||||
/** 查询科室 */
|
||||
const getLocationInfo = () => {
|
||||
getDepartmentList().then((res) => {
|
||||
orgOptions.value = res.data || [];
|
||||
});
|
||||
};
|
||||
|
||||
// 获取诊断目录
|
||||
function getDiagnosisList() {
|
||||
getEncounterDiagnosis(effectivePatientInfo.value.encounterId).then((res) => {
|
||||
getEncounterDiagnosis(patientInfo.value.encounterId).then((res) => {
|
||||
console.log('诊断目录========>', JSON.stringify(res.data));
|
||||
if (res.code == 200) {
|
||||
const datas = (res.data || []).map((item) => {
|
||||
let obj = { ...item };
|
||||
if (obj.diagSrtNo == null) obj.diagSrtNo = '1';
|
||||
let obj = {
|
||||
...item,
|
||||
};
|
||||
if (obj.diagSrtNo == null) {
|
||||
obj.diagSrtNo = '1';
|
||||
}
|
||||
return obj;
|
||||
});
|
||||
form.primaryDiagnosisList = datas.filter((item) => item?.maindiseFlag == 1);
|
||||
// 主诊断
|
||||
form.primaryDiagnosisList = datas.filter((item) => {
|
||||
return item?.maindiseFlag == 1;
|
||||
});
|
||||
console.log('@@@@@@========>', form.primaryDiagnosisList);
|
||||
if (form.primaryDiagnosisList.length == 1) {
|
||||
form.clinicalDiagnosis = form.primaryDiagnosisList[0].name;
|
||||
const obj = form.primaryDiagnosisList[0];
|
||||
form.clinicalDiagnosis = obj.name;
|
||||
}
|
||||
form.otherDiagnosisList = datas.filter((item) => item?.maindiseFlag !== 1);
|
||||
form.otherDiagnosis = form.otherDiagnosisList.map((item) => item.name).join(',');
|
||||
//其他诊断
|
||||
form.otherDiagnosisList = datas.filter((item) => {
|
||||
return item?.maindiseFlag !== 1;
|
||||
});
|
||||
const otherDiagnosisNameList = form.otherDiagnosisList.map((item) => {
|
||||
return item.name;
|
||||
});
|
||||
form.otherDiagnosis = otherDiagnosisNameList.join(',');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
getLocationInfo();
|
||||
loadPatientAllergyHistory();
|
||||
loadEditData();
|
||||
});
|
||||
|
||||
// 监听编辑模式变化,重新加载数据
|
||||
watch(
|
||||
() => props.isEditMode,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
nextTick(() => {
|
||||
getList();
|
||||
getLocationInfo();
|
||||
loadEditData();
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
defineExpose({ state, submit, getLocationInfo, getDiagnosisList, resetForm, getList });
|
||||
defineExpose({ state, submit, getLocationInfo, getDiagnosisList });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 医疗主题色
|
||||
$primary-color: #409eff;
|
||||
$success-color: #67c23a;
|
||||
$warning-color: #e6a23c;
|
||||
$danger-color: #f56c6c;
|
||||
$info-color: #909399;
|
||||
|
||||
$text-primary: #303133;
|
||||
$text-regular: #606266;
|
||||
$text-secondary: #909399;
|
||||
$border-color: #dcdfe6;
|
||||
$bg-color: #f5f7fa;
|
||||
|
||||
.medicalExaminations-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: $bg-color;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
|
||||
|
||||
// 主体内容区 - 紧凑布局
|
||||
.form-body {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #d0d3da;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
// 紧急程度栏 - 右上角
|
||||
.urgency-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding: 4px 0;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.urgency-bar-label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: $text-regular;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
// 卡片通用样式 - 紧凑
|
||||
.section-card {
|
||||
background: #fff;
|
||||
border-radius: 6px;
|
||||
padding: 8px;
|
||||
border: 1px solid #e4e7ed;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.transfer-wrapper {
|
||||
position: relative;
|
||||
min-height: 300px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.el-transfer) {
|
||||
.el-transfer {
|
||||
--el-transfer-panel-width: 480px !important;
|
||||
display: flex !important;
|
||||
flex-direction: row !important;
|
||||
}
|
||||
|
||||
// 穿梭框按钮垂直居中
|
||||
:deep(.el-transfer__buttons) {
|
||||
.bloodTransfusion-form {
|
||||
padding: 8px 8px 0 8px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
:deep(.el-transfer__button) {
|
||||
margin: 4px 0;
|
||||
}
|
||||
// 信息表单 - 紧凑
|
||||
.info-form {
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 6px;
|
||||
|
||||
.el-form-item__label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: $text-regular;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.el-input__wrapper,
|
||||
.el-textarea__inner {
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0 0 1px $primary-color;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 2px rgba($primary-color, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.el-input.is-disabled .el-input__wrapper {
|
||||
background: #f5f7fa;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 过敏史危险输入样式
|
||||
:deep(.el-textarea__inner.allergy-danger) {
|
||||
border-color: $danger-color !important;
|
||||
background-color: #fef0f0;
|
||||
}
|
||||
|
||||
// 急诊确认弹窗
|
||||
.emergency-dialog-content {
|
||||
text-align: center;
|
||||
padding: 24px 0;
|
||||
|
||||
.emergency-dialog-icon {
|
||||
font-size: 52px;
|
||||
color: $danger-color;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.emergency-dialog-text {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: $text-primary;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
.emergency-dialog-sub {
|
||||
font-size: 14px;
|
||||
color: $text-secondary;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 过渡动画
|
||||
.fade-in-linear-enter-active,
|
||||
.fade-in-linear-leave-active {
|
||||
transition: opacity 0.2s linear;
|
||||
}
|
||||
|
||||
.fade-in-linear-enter-from,
|
||||
.fade-in-linear-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* 紧急程度行内布局 */
|
||||
.urgency-inline {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.emergency-tip-inline {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
color: $danger-color;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
background: #fef0f0;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 过敏史包装 */
|
||||
.allergy-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.allergy-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.allergy-severe-tag-inline {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
color: $danger-color;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
background: #fef0f0;
|
||||
padding: 2px 8px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
/* 病史摘要同步按钮 */
|
||||
.history-field-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.history-sync-btn {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: -28px;
|
||||
font-size: 11px;
|
||||
padding: 2px 8px;
|
||||
height: 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,27 +5,13 @@
|
||||
-->
|
||||
<template>
|
||||
<div class="surgery-container">
|
||||
<div class="transfer-wrapper" style="min-height: 300px;">
|
||||
<!-- 搜索框:≥3字触发后端搜索 -->
|
||||
<div style="padding: 6px 0;">
|
||||
<el-input
|
||||
v-model="searchKey"
|
||||
placeholder="请输入3个字及以上搜索"
|
||||
clearable
|
||||
@input="onSearchInput"
|
||||
style="width: 320px;"
|
||||
/>
|
||||
</div>
|
||||
<!-- 加载提示不阻塞穿梭框操作 -->
|
||||
<div v-if="loading" style="padding:8px 0; color:#909399; font-size:13px;">
|
||||
<el-icon class="is-loading"><Loading /></el-icon> 手术项目加载中...
|
||||
</div>
|
||||
<div class="transfer-wrapper">
|
||||
<el-transfer
|
||||
ref="transferRef"
|
||||
v-model="transferValue"
|
||||
:data="applicationList"
|
||||
:titles="['待选择', '已选择']"
|
||||
:format="leftPanelFormat"
|
||||
filter-placeholder="项目代码/名称"
|
||||
filterable
|
||||
:titles="['未选择', '已选择']"
|
||||
/>
|
||||
</div>
|
||||
<div class="bloodTransfusion-form">
|
||||
@@ -92,26 +78,14 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup name="Surgery">
|
||||
import {computed, getCurrentInstance, onBeforeMount, onMounted, reactive, ref, watch} from 'vue';
|
||||
import {getCurrentInstance, onBeforeMount, onMounted, reactive, ref, watch} from 'vue';
|
||||
import {patientInfo} from '../../../store/patient.js';
|
||||
import {getDepartmentList} from '@/api/public.js';
|
||||
import {getEncounterDiagnosis} from '../../api.js';
|
||||
import {getSurgeryPage, saveSurgery} from './api';
|
||||
import {getApplicationList, saveSurgery} from './api';
|
||||
import {ElMessage} from 'element-plus';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
// 模块级缓存:避免每次打开弹窗都重新请求手术项目列表
|
||||
let surgeryRecordsCache = null; // 原始 API 记录
|
||||
let surgeryMappedCache = null; // 映射后的 el-transfer 数据
|
||||
let searchDebounceTimer = null; // 搜索防抖
|
||||
const transferRef = ref(null);
|
||||
const dbTotal = ref(0); // 数据库中的手术项目总数
|
||||
const searchKey = ref(''); // 搜索关键字
|
||||
const checkedCount = computed(() => transferValue.value.length);
|
||||
const leftPanelFormat = computed(() => ({
|
||||
noChecked: ` 0/${dbTotal.value}`,
|
||||
hasChecked: ` \${checked}/${dbTotal.value}`,
|
||||
}));
|
||||
// 递归查找树形科室节点
|
||||
const findTreeItem = (list, id) => {
|
||||
if (!list || list.length === 0) return null;
|
||||
@@ -127,86 +101,45 @@ const findTreeItem = (list, id) => {
|
||||
const emits = defineEmits(['submitOk']);
|
||||
const props = defineProps({});
|
||||
const state = reactive({});
|
||||
const applicationListAll = ref();
|
||||
const applicationList = ref();
|
||||
const applicationListAll = ref([]);
|
||||
const applicationList = ref([]);
|
||||
const orgOptions = ref([]); // 科室选项
|
||||
const loading = ref(false); // 加载状态
|
||||
const mapToTransferItem = (item) => {
|
||||
const price = item.price != null ? Number(item.price).toFixed(2) : '0.00';
|
||||
const unit = item.unitCodeDictText || item.unitCode || '';
|
||||
return {
|
||||
adviceDefinitionId: item.adviceDefinitionId,
|
||||
orgId: item.orgId,
|
||||
label: item.adviceName + ' (¥' + price + '/' + unit + ')',
|
||||
key: item.adviceDefinitionId,
|
||||
};
|
||||
};
|
||||
const getList = () => {
|
||||
if (!patientInfo.value?.inHospitalOrgId) {
|
||||
applicationList.value = [];
|
||||
return;
|
||||
}
|
||||
// 命中内存缓存时直接使用
|
||||
if (surgeryMappedCache && surgeryMappedCache.length > 0) {
|
||||
applicationList.value = surgeryMappedCache;
|
||||
applicationListAll.value = surgeryRecordsCache;
|
||||
return;
|
||||
}
|
||||
loadPage('');
|
||||
};
|
||||
|
||||
/**
|
||||
* 加载手术项目分页数据
|
||||
* @param {string} key 搜索关键字(可选)
|
||||
*/
|
||||
const loadPage = (key) => {
|
||||
const orgId = patientInfo.value.inHospitalOrgId;
|
||||
loading.value = true;
|
||||
getSurgeryPage({ organizationId: orgId, pageNo: 1, pageSize: 100, searchKey: key || '' })
|
||||
getApplicationList({
|
||||
pageSize: 500,
|
||||
pageNum: 1,
|
||||
categoryCode: '24',
|
||||
organizationId: patientInfo.value.inHospitalOrgId,
|
||||
adviceTypes: [3, 6], //1 药品 2耗材 3诊疗 6手术
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code !== 200 || !res.data?.records) {
|
||||
if (res.code === 200) {
|
||||
applicationListAll.value = res.data.records;
|
||||
applicationList.value = res.data.records.map((item) => {
|
||||
const priceInfo = item.priceList?.[0] || {};
|
||||
const price = priceInfo.price != null ? Number(priceInfo.price).toFixed(2) : '0.00';
|
||||
const unit = item.unitCode_dictText || item.unitCode || '';
|
||||
return {
|
||||
adviceDefinitionId: item.adviceDefinitionId,
|
||||
orgId: item.orgId,
|
||||
label: item.adviceName + ' (¥' + price + '/' + unit + ')',
|
||||
key: item.adviceDefinitionId,
|
||||
};
|
||||
});
|
||||
} else {
|
||||
console.warn('获取手术项目列表失败:', res.message);
|
||||
applicationList.value = [];
|
||||
dbTotal.value = 0;
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
dbTotal.value = res.data.total || 0;
|
||||
const records = res.data.records;
|
||||
applicationListAll.value = records;
|
||||
applicationList.value = records.map(mapToTransferItem);
|
||||
// 仅在无搜索时缓存
|
||||
if (!key) {
|
||||
surgeryRecordsCache = records;
|
||||
surgeryMappedCache = applicationList.value;
|
||||
}
|
||||
loading.value = false;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error('手术项目加载失败:', e);
|
||||
console.warn('手术项目列表加载失败(可能无权限):', e?.message || e);
|
||||
applicationList.value = [];
|
||||
dbTotal.value = 0;
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 搜索输入框变化处理(防抖300ms,≥3字触发后端搜索)
|
||||
*/
|
||||
const onSearchInput = () => {
|
||||
clearTimeout(searchDebounceTimer);
|
||||
const val = searchKey.value.trim();
|
||||
if (!val) {
|
||||
// 清空搜索框,恢复初始数据
|
||||
loadPage('');
|
||||
return;
|
||||
}
|
||||
if (val.length >= 3) {
|
||||
searchDebounceTimer = setTimeout(() => {
|
||||
loadPage(val);
|
||||
}, 300);
|
||||
}
|
||||
};
|
||||
|
||||
const transferValue = ref([]);
|
||||
const form = reactive({
|
||||
// categoryType: '', // 项目类别
|
||||
@@ -293,15 +226,20 @@ const submit = () => {
|
||||
});
|
||||
applicationListAllFilter = applicationListAllFilter.map((item) => {
|
||||
return {
|
||||
adviceDefinitionId: item.adviceDefinitionId,
|
||||
adviceDefinitionName: item.adviceName,
|
||||
quantity: 1,
|
||||
unitCode: item.unitCode,
|
||||
unitPrice: item.price,
|
||||
totalPrice: item.price,
|
||||
positionId: item.positionId,
|
||||
definitionId: item.chargeItemDefinitionId,
|
||||
accountId: patientInfo.value.accountId,
|
||||
adviceDefinitionId: item.adviceDefinitionId /** 诊疗定义id */,
|
||||
adviceDefinitionName: item.adviceDefinitionName /** 诊疗定义名称(手术项目名称) */,
|
||||
quantity: 1, // /** 请求数量 */
|
||||
unitCode: item.priceList[0].unitCode /** 请求单位编码 */,
|
||||
unitPrice: item.priceList[0].price /** 单价 */,
|
||||
totalPrice: item.priceList[0].price /** 总价 */,
|
||||
positionId: item.positionId, //执行科室id
|
||||
ybClassEnum: item.ybClassEnum, //类别医保编码
|
||||
conditionId: item.conditionId, //诊断ID
|
||||
encounterDiagnosisId: item.encounterDiagnosisId, //就诊诊断id
|
||||
adviceType: item.adviceType, ///** 医嘱类型 */
|
||||
definitionId: item.priceList[0].definitionId, //费用定价主表ID */
|
||||
definitionDetailId: item.definitionDetailId, //费用定价子表ID */
|
||||
accountId: patientInfo.value.accountId, // // 账户id
|
||||
};
|
||||
});
|
||||
saveSurgery({
|
||||
@@ -312,7 +250,7 @@ const submit = () => {
|
||||
requestFormId: '', // 申请单ID
|
||||
name: '手术申请单',
|
||||
descJson: JSON.stringify(form),
|
||||
categoryEnum: '24', // 21 检验 22 检查 23 输血 24 手术(避开 adviceType 1-6 碰撞)
|
||||
categoryEnum: '4', // 1 检验 2 检查 3 输血 4 手术
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
proxy.$message.success(res.msg);
|
||||
@@ -363,7 +301,7 @@ function getDiagnosisList() {
|
||||
}
|
||||
});
|
||||
}
|
||||
defineExpose({ state, submit, getLocationInfo, getDiagnosisList, getList });
|
||||
defineExpose({ state, submit, getLocationInfo, getDiagnosisList });
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.surgery-container {
|
||||
|
||||
@@ -122,6 +122,7 @@
|
||||
|
||||
<el-table-column label="类型" align="center" prop="" width="120">
|
||||
<template #default="scope">
|
||||
{{ console.log(scope.row, 1111) }}
|
||||
<el-radio-group
|
||||
v-model="scope.row.therapyEnum"
|
||||
size="small"
|
||||
@@ -269,6 +270,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="频次/用法" align="center" prop="" width="180">
|
||||
<template #default="scope">
|
||||
{{ console.log(scope.row) }}
|
||||
<span v-if="!scope.row.isEdit && scope.row.adviceType == 1" style="text-align: right">
|
||||
{{
|
||||
[
|
||||
@@ -877,13 +879,14 @@ function handleDiagnosisChange(item) {
|
||||
function handleFocus(row, index) {
|
||||
rowIndex.value = index;
|
||||
row.showPopover = true;
|
||||
// el-popover 懒渲染,需要等两帧组件才会挂载
|
||||
const adviceType = row.adviceType !== undefined ? row.adviceType : adviceQueryParams.value.adviceType;
|
||||
// 用 adviceType + categoryCode 组合查找匹配的选项
|
||||
const selectValue = (adviceType == 1 && row.categoryCode) ? '1-' + row.categoryCode : adviceType;
|
||||
const selectedItem = adviceTypeList.value.find(item => item.value === selectValue) || adviceTypeList.value.find(item => item.adviceType === adviceType);
|
||||
// If the row has an explicit adviceType (saved/existing row), use its own categoryCode.
|
||||
// If no type is selected (new row), use empty string for global search across all categories.
|
||||
const categoryCode = selectedItem ? selectedItem.categoryCode : (row.adviceType != null ? (row.categoryCode || '') : '');
|
||||
// 诊疗(3)和手术(6)没有categoryCode,传空字符串给refresh,由子组件转为undefined不发送
|
||||
// 药品(1)才有categoryCode(如'1'=中成药,'2'=西药,'4'=中草药)
|
||||
const categoryCode = selectedItem ? selectedItem.categoryCode : (adviceQueryParams.value.categoryCode || '');
|
||||
const searchKey = row.adviceName || '';
|
||||
|
||||
nextTick(() => {
|
||||
@@ -920,8 +923,8 @@ function handleChange(value) {
|
||||
// 用 adviceType + categoryCode 组合查找匹配的选项
|
||||
const selectValue = (adviceType == 1 && row?.categoryCode) ? '1-' + row.categoryCode : adviceType;
|
||||
const selectedItem = adviceTypeList.value.find(item => item.value === selectValue) || adviceTypeList.value.find(item => item.adviceType === adviceType);
|
||||
// 修复Bug #486:当行没有显式选择医嘱类型时,不传categoryCode,让搜索在全药库中进行
|
||||
const categoryCode = selectedItem ? selectedItem.categoryCode : (row?.adviceType !== undefined ? (adviceQueryParams.value.categoryCode || '') : '');
|
||||
// 诊疗/手术的categoryCode为空字符串,由子组件转为undefined不发送
|
||||
const categoryCode = selectedItem ? selectedItem.categoryCode : (adviceQueryParams.value.categoryCode || '');
|
||||
tableRef.refresh(adviceType, categoryCode, value);
|
||||
}
|
||||
}
|
||||
@@ -1211,8 +1214,12 @@ function handleSave() {
|
||||
getListInfo(false);
|
||||
bindMethod.value = {};
|
||||
nextId.value == 1;
|
||||
|
||||
// 处方保存成功后,自动将医嘱信息同步至患者处置记录
|
||||
// handleEmrTreatment();
|
||||
} else {
|
||||
proxy.$modal.msgError(res.message);
|
||||
conso;
|
||||
isSaving.value = false;
|
||||
}
|
||||
})
|
||||
@@ -1312,26 +1319,22 @@ function handleCancelEdit(row, index) {
|
||||
function handleSaveSign(row, index) {
|
||||
if (row.adviceType != 2) {
|
||||
let itemNo = row.adviceType == 1 ? row.methodCode : row.adviceDefinitionId;
|
||||
if (!itemNo) {
|
||||
console.warn('绑定设备检查跳过:itemNo为空(adviceType=' + row.adviceType + ', adviceName=' + row.adviceName + ')');
|
||||
} else {
|
||||
getBindDevice({ typeCode: row.adviceType, itemNo: itemNo }).then((res) => {
|
||||
if (res.data.length == 0) {
|
||||
return;
|
||||
getBindDevice({ typeCode: row.adviceType, itemNo: itemNo }).then((res) => {
|
||||
if (res.data.length == 0) {
|
||||
return;
|
||||
}
|
||||
let openBindDialog = localStorage.getItem('doctor' + userStore.id);
|
||||
if (!JSON.parse(openBindDialog)) {
|
||||
proxy.$refs['orderBindInfoRef'].open(res.data);
|
||||
} else {
|
||||
// 如果弹窗不提示带出的项目,自动带出
|
||||
// 如果有未签发的项目,并且当前的项目没有带出过绑定项目,则自动带出
|
||||
if (!bindMethod.value[itemNo]) {
|
||||
handleOrderBindInfo(res.data);
|
||||
bindMethod.value[itemNo] = true;
|
||||
}
|
||||
let openBindDialog = localStorage.getItem('doctor' + userStore.id);
|
||||
if (!JSON.parse(openBindDialog)) {
|
||||
proxy.$refs['orderBindInfoRef'].open(res.data);
|
||||
} else {
|
||||
// 如果弹窗不提示带出的项目,自动带出
|
||||
// 如果有未签发的项目,并且当前的项目没有带出过绑定项目,则自动带出
|
||||
if (!bindMethod.value[itemNo]) {
|
||||
handleOrderBindInfo(res.data);
|
||||
bindMethod.value[itemNo] = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 更新UI状态
|
||||
|
||||
@@ -19,9 +19,11 @@
|
||||
<el-tab-pane label="检验申请" name="test">
|
||||
<TestApplication ref="testApplicationRef" :show-status-column="true" />
|
||||
</el-tab-pane>
|
||||
```vue
|
||||
<el-tab-pane label="检查申请" name="examine">
|
||||
<ExamineApplication ref="examineApplicationRef" />
|
||||
</el-tab-pane>
|
||||
```
|
||||
<el-tab-pane label="汇总发药申请" name="summaryDrug">
|
||||
<SummaryDrugApplication ref="summaryDrugApplicationRef" />
|
||||
</el-tab-pane>
|
||||
@@ -47,7 +49,6 @@
|
||||
import {computed, onBeforeMount, onMounted, provide, reactive, ref, watch,} from 'vue';
|
||||
|
||||
import Emr from './emr/index.vue';
|
||||
import SummaryDrugApplication from './components/applicationShow/summaryDrugApplication.vue';
|
||||
import inPatientBarDoctorFold from '@/components/patientBar/inPatientBarDoctorFold.vue';
|
||||
import PatientList from '@/components/PatientList/patient-list.vue';
|
||||
import {localPatientInfo, updateLocalPatientInfo} from './store/localPatient';
|
||||
@@ -83,7 +84,6 @@ const currentPatientInfo = ref({});
|
||||
const testApplicationRef = ref();
|
||||
const examineApplicationRef = ref();
|
||||
const surgeryApplicationRef = ref();
|
||||
const summaryDrugApplicationRef = ref();
|
||||
const bloodTtransfusionAapplicationRef = ref();
|
||||
|
||||
// 患者列表相关逻辑
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<el-button type="primary" @click="openGroupSetDialog">划价组套</el-button>
|
||||
<el-button type="primary">划价组套</el-button>
|
||||
</div>
|
||||
<!-- 弹窗内容 - 左右布局 -->
|
||||
<div style="display: flex; gap: 20px; height: 70vh">
|
||||
@@ -147,7 +147,6 @@
|
||||
<el-select
|
||||
v-model="scope.row.selectUnitCode"
|
||||
placeholder="单位"
|
||||
filterable
|
||||
style="width: 100px"
|
||||
@change="unitCodeChange(scope.row)"
|
||||
>
|
||||
@@ -172,32 +171,30 @@
|
||||
v-if="scope.row.adviceType == 3"
|
||||
clearable
|
||||
filterable
|
||||
:filter-method="(val) => filterOptions(val, scope.row, 'departmentOptions')"
|
||||
v-model="scope.row.positionId"
|
||||
placeholder="选择科室"
|
||||
style="width: 220px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dept in getFilteredOptions(scope.row, 'departmentOptions')"
|
||||
v-for="dept in departmentOptions"
|
||||
:key="dept.id"
|
||||
:label="dept.name"
|
||||
:value="String(dept.id)"
|
||||
:value="dept.id"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select
|
||||
v-if="scope.row.adviceType == 2"
|
||||
clearable
|
||||
filterable
|
||||
:filter-method="(val) => filterOptions(val, scope.row, 'locationOptions')"
|
||||
v-model="scope.row.positionId"
|
||||
placeholder="选择药房/耗材房"
|
||||
style="width: 220px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dept in getFilteredOptions(scope.row, 'locationOptions')"
|
||||
v-for="dept in locationOptions"
|
||||
:key="dept.value"
|
||||
:label="dept.label"
|
||||
:value="String(dept.value)"
|
||||
:value="dept.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
@@ -250,58 +247,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!-- 划价组套选择对话框 -->
|
||||
<el-dialog v-model="groupSetDialogVisible" title="划价组套选择" width="600px" :close-on-click-modal="false" append-to-body :z-index="3000">
|
||||
<div style="margin-bottom: 15px; display: flex; align-items: center; gap: 10px">
|
||||
<el-input
|
||||
v-model="groupSetSearchText"
|
||||
placeholder="请输入组套名称搜索"
|
||||
clearable
|
||||
style="width: 250px"
|
||||
@keyup.enter="loadGroupSets"
|
||||
>
|
||||
<template #append>
|
||||
<el-button icon="Search" @click="loadGroupSets" />
|
||||
</template>
|
||||
</el-input>
|
||||
<el-radio-group v-model="groupSetRange" @change="loadGroupSets">
|
||||
<el-radio-button :label="1">个人</el-radio-button>
|
||||
<el-radio-button :label="2">科室</el-radio-button>
|
||||
<el-radio-button :label="3">全院</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<el-table
|
||||
:data="groupSetList"
|
||||
v-loading="groupSetLoading"
|
||||
highlight-current-row
|
||||
border
|
||||
stripe
|
||||
@current-change="handleGroupSetSelect"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column label="组套名称" min-width="180" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
{{ scope.row.name || scope.row.Name || '' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="使用范围" width="100" align="center">
|
||||
<template #default="scope">
|
||||
{{ getRangeText(scope.row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="明细数量" width="100" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.detailList?.length || 0 }} 项
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-if="!groupSetLoading && groupSetList.length === 0" description="暂无划价组套数据" :image-size="80" />
|
||||
<div style="margin-top: 15px; text-align: right">
|
||||
<el-button @click="groupSetDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="applyGroupSet" :disabled="!selectedGroupSet">应用</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -309,7 +254,6 @@ import {computed, getCurrentInstance, onMounted, reactive, ref, watch} from 'vue
|
||||
import {ElMessage} from 'element-plus';
|
||||
import {formatDateStr} from '@/utils/index';
|
||||
import {getAdviceBaseInfo, getDiseaseTreatmentInitLoc, getOrgList} from './api.js';
|
||||
import {getOrderGroup} from '@/views/doctorstation/components/api.js';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
@@ -368,8 +312,6 @@ const locationOptions = ref([]);
|
||||
const searchText = ref('');
|
||||
const userId = ref('');
|
||||
const orgId = ref('');
|
||||
// 下拉框模糊搜索关键字(按行存储)
|
||||
const filterKeywords = ref({});
|
||||
const queryParams = ref({
|
||||
pageSize: 100,
|
||||
pageNum: 1,
|
||||
@@ -447,14 +389,6 @@ const submitData = reactive({
|
||||
});
|
||||
const adviceLoading = ref(false);
|
||||
|
||||
// 划价组套相关
|
||||
const groupSetDialogVisible = ref(false);
|
||||
const groupSetList = ref([]);
|
||||
const groupSetLoading = ref(false);
|
||||
const groupSetSearchText = ref('');
|
||||
const groupSetRange = ref(2);
|
||||
const selectedGroupSet = ref(null);
|
||||
|
||||
// 【优化核心】计算总金额 - 使用计算属性实现实时更新
|
||||
const totalAmount = computed(() => {
|
||||
return feeItemsList.value.reduce((sum, item) => {
|
||||
@@ -482,73 +416,19 @@ watch(
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
executeTime.value = formatDateStr(new Date(), 'YYYY-MM-DD HH:mm:ss');
|
||||
// 弹窗打开时重新加载科室和位置选项,确保数据最新
|
||||
loadDepartmentOptions();
|
||||
getDiseaseInitLoc(16);
|
||||
} else {
|
||||
resetData();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 监听科室选项加载完成,为已添加的诊疗项目设置默认执行科室
|
||||
watch(
|
||||
() => departmentOptions.value,
|
||||
(depts) => {
|
||||
if (!depts || depts.length === 0) return;
|
||||
feeItemsList.value.forEach(item => {
|
||||
if (item.adviceType === 3 && !item.positionId) {
|
||||
const patientOrgId = props.patientInfo.organizationId;
|
||||
const matched = depts.find(d => String(d.id) === String(patientOrgId));
|
||||
item.positionId = matched ? String(matched.id) : String(depts[0].id);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// 监听位置选项加载完成,为已添加的耗材项目设置默认位置
|
||||
watch(
|
||||
() => locationOptions.value,
|
||||
(locs) => {
|
||||
if (!locs || locs.length === 0) return;
|
||||
feeItemsList.value.forEach(item => {
|
||||
if (item.adviceType === 2 && !item.positionId) {
|
||||
item.positionId = String(locs[0].value);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// 加载科室选项(支持树形/扁平两种数据结构)
|
||||
// 加载科室选项
|
||||
function loadDepartmentOptions() {
|
||||
getOrgList()
|
||||
.then((res) => {
|
||||
if (res.data) {
|
||||
// 尝试从树形结构中取:records[0].children
|
||||
if (res.data.records && res.data.records.length > 0) {
|
||||
if (res.data.records[0].children && res.data.records[0].children.length > 0) {
|
||||
departmentOptions.value = res.data.records[0].children;
|
||||
return;
|
||||
}
|
||||
// 如果 records[0] 有 id 和 name(非树根节点),直接用所有 records
|
||||
if (res.data.records[0].id) {
|
||||
departmentOptions.value = res.data.records;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 兜底:如果 records 不存在或为空,尝试直接使用 data 本身
|
||||
if (Array.isArray(res.data)) {
|
||||
departmentOptions.value = res.data;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 所有方式都失败,置空
|
||||
departmentOptions.value = [];
|
||||
})
|
||||
.catch(() => {
|
||||
console.warn('科室列表加载失败(可能无权限)');
|
||||
departmentOptions.value = [];
|
||||
});
|
||||
getOrgList().then((res) => {
|
||||
if (res.data && res.data.records && res.data.records.length > 0) {
|
||||
departmentOptions.value = res.data.records[0].children || [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 加载收费组套数据
|
||||
@@ -567,37 +447,9 @@ function getAdviceBaseInfos() {
|
||||
});
|
||||
}
|
||||
function getDiseaseInitLoc() {
|
||||
getDiseaseTreatmentInitLoc(16)
|
||||
.then((response) => {
|
||||
console.log('Disease Treatment Init Loc:', response);
|
||||
locationOptions.value = response.data.locationOptions;
|
||||
})
|
||||
.catch(() => {
|
||||
console.warn('位置列表加载失败(可能无权限)');
|
||||
locationOptions.value = [];
|
||||
});
|
||||
}
|
||||
// 下拉框模糊搜索过滤(自定义filter-method,配合element-plus filterable使用)
|
||||
function filterOptions(val, row, optionsKey) {
|
||||
const key = row.adviceDefinitionId + '_' + optionsKey;
|
||||
if (!val || val.trim() === '') {
|
||||
delete filterKeywords.value[key];
|
||||
} else {
|
||||
filterKeywords.value[key] = val.toLowerCase();
|
||||
}
|
||||
}
|
||||
function getFilteredOptions(row, optionsKey) {
|
||||
const key = row.adviceDefinitionId + '_' + optionsKey;
|
||||
const keyword = filterKeywords.value[key];
|
||||
const options = optionsKey === 'departmentOptions' ? departmentOptions.value : locationOptions.value;
|
||||
if (!keyword) {
|
||||
return options;
|
||||
}
|
||||
return options.filter(item => {
|
||||
const name = (item.name || item.label || '').toLowerCase();
|
||||
const id = String(item.id || item.value || '').toLowerCase();
|
||||
const py = (item.pyStr || '').toLowerCase();
|
||||
return name.includes(keyword) || id.includes(keyword) || py.includes(keyword);
|
||||
getDiseaseTreatmentInitLoc(16).then((response) => {
|
||||
console.log('Disease Treatment Init Loc:', response);
|
||||
locationOptions.value = response.data.locationOptions;
|
||||
});
|
||||
}
|
||||
// 获取组套类型文本
|
||||
@@ -606,26 +458,19 @@ function getItemType_Text(type) {
|
||||
return map[type] || '其他';
|
||||
}
|
||||
function getUnitCodeOptions(row) {
|
||||
const unitCodes = [];
|
||||
// 大单位:优先用 code,code 缺失时用字典文本兜底
|
||||
if (row.unitCode != null && String(row.unitCode) !== '') {
|
||||
unitCodes.push({ code: String(row.unitCode), codeText: row.unitCode_dictText });
|
||||
} else if (row.unitCode_dictText) {
|
||||
unitCodes.push({ code: row.unitCode_dictText, codeText: row.unitCode_dictText });
|
||||
}
|
||||
// 小单位:同上
|
||||
if (row.minUnitCode != null && String(row.minUnitCode) !== '') {
|
||||
unitCodes.push({ code: String(row.minUnitCode), codeText: row.minUnitCode_dictText });
|
||||
} else if (row.minUnitCode_dictText) {
|
||||
unitCodes.push({ code: row.minUnitCode_dictText, codeText: row.minUnitCode_dictText });
|
||||
}
|
||||
// 去重
|
||||
const unitCodes = [
|
||||
{ code: row.unitCode, codeText: row.unitCode_dictText },
|
||||
{ code: row.minUnitCode, codeText: row.minUnitCode_dictText },
|
||||
];
|
||||
// 使用 Set 来跟踪已经存在的 code
|
||||
const seenCodes = new Set();
|
||||
const uniqueUnitCodes = unitCodes.filter((item) => {
|
||||
// 如果 Set 中没有这个 code,就保留它,并把它加入 Set
|
||||
if (!seenCodes.has(item.code)) {
|
||||
seenCodes.add(item.code);
|
||||
return true;
|
||||
}
|
||||
// 如果已经存在,就过滤掉
|
||||
return false;
|
||||
});
|
||||
return uniqueUnitCodes;
|
||||
@@ -636,11 +481,11 @@ function unitCodeChange(row) {
|
||||
// 获取价格
|
||||
const price = row.priceList?.[0]?.price || 0;
|
||||
|
||||
// 根据选择的单位调整单价(统一用字符串比较)
|
||||
if (String(row.selectUnitCode) === String(row.unitCode)) {
|
||||
// 根据选择的单位调整单价
|
||||
if (row.selectUnitCode === row.unitCode) {
|
||||
// 如果选择的是大单位 (如 "盒")
|
||||
row.unitPrice = price.toFixed(6); // 单价就是原价
|
||||
} else if (String(row.selectUnitCode) === String(row.minUnitCode)) {
|
||||
} else if (row.selectUnitCode === row.minUnitCode) {
|
||||
// 如果选择的是小单位 (如 "个")
|
||||
row.unitPrice = (price / (row.partPercent || 1)).toFixed(6); // 单价 = 原价 / 拆零比
|
||||
}
|
||||
@@ -682,54 +527,14 @@ function selectChange(row) {
|
||||
const price = row.priceList?.[0]?.price || 0;
|
||||
//获取大小单位
|
||||
const uniqueUnitCodes = getUnitCodeOptions(row);
|
||||
// 设置默认单位:优先使用 minUnitCode(小单位),回退到 unitCode(大单位)
|
||||
let defaultUnitCode = '';
|
||||
if (row.minUnitCode) {
|
||||
defaultUnitCode = String(row.minUnitCode);
|
||||
} else if (row.unitCode) {
|
||||
defaultUnitCode = String(row.unitCode);
|
||||
} else if (row.minUnitCode_dictText) {
|
||||
// 兜底:如果 minUnitCode 为空但字典文本存在,使用文本作为选项值
|
||||
defaultUnitCode = row.minUnitCode_dictText;
|
||||
} else if (row.unitCode_dictText) {
|
||||
defaultUnitCode = row.unitCode_dictText;
|
||||
}
|
||||
// 如果默认单位不在 uniqueUnitCodes 中,添加兜底选项
|
||||
if (defaultUnitCode && !uniqueUnitCodes.some(u => u.code === defaultUnitCode)) {
|
||||
uniqueUnitCodes.push({ code: defaultUnitCode, codeText: defaultUnitCode });
|
||||
}
|
||||
// 设置默认执行科室/位置(统一转为字符串,避免 el-select 类型不匹配)
|
||||
let defaultPositionId = undefined;
|
||||
if (row.adviceType === 3 && departmentOptions.value.length > 0) {
|
||||
// 诊疗:
|
||||
// 1. 优先使用诊疗目录项目的"所属科室"(row.orgId)
|
||||
// 2. 其次使用患者当前病房科室(patientInfo.organizationId)
|
||||
// 3. 最后取第一个科室
|
||||
const orgIdPriority = [row.orgId, props.patientInfo.organizationId];
|
||||
for (const id of orgIdPriority) {
|
||||
if (id) {
|
||||
const matched = departmentOptions.value.find(d => String(d.id) === String(id));
|
||||
if (matched) {
|
||||
defaultPositionId = String(matched.id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!defaultPositionId) {
|
||||
defaultPositionId = String(departmentOptions.value[0].id);
|
||||
}
|
||||
} else if (row.adviceType === 2 && locationOptions.value.length > 0) {
|
||||
// 耗材:默认取第一个药房/耗材房
|
||||
defaultPositionId = String(locationOptions.value[0].value);
|
||||
}
|
||||
//插入费用列表
|
||||
feeItemsList.value.push({
|
||||
...row,
|
||||
uniqueUnitCodes: uniqueUnitCodes,
|
||||
unitPrice: (price / (row.partPercent || 1)).toFixed(6), // 根据拆零比计算单价
|
||||
quantity: 1,
|
||||
positionId: defaultPositionId, // 默认执行科室/位置(字符串类型)
|
||||
selectUnitCode: defaultUnitCode, // 默认选择小单位
|
||||
// positionId: row.positionId === null || row.positionId === undefined ? orgId : row.positionId, // 默认执行科室
|
||||
selectUnitCode: row.minUnitCode, // 默认选择小单位
|
||||
});
|
||||
}
|
||||
|
||||
@@ -843,120 +648,6 @@ function resetData() {
|
||||
searchText.value = '';
|
||||
executeTime.value = '';
|
||||
}
|
||||
|
||||
// 划价组套相关功能
|
||||
function openGroupSetDialog() {
|
||||
console.log('openGroupSetDialog called');
|
||||
groupSetDialogVisible.value = true;
|
||||
groupSetSearchText.value = '';
|
||||
selectedGroupSet.value = null;
|
||||
loadGroupSets();
|
||||
}
|
||||
|
||||
function loadGroupSets() {
|
||||
groupSetLoading.value = true;
|
||||
const params = { organizationId: orgId.value };
|
||||
// 传递搜索关键字,后端 /group-package-for-order 虽不直接支持 searchKey,
|
||||
// 但保持参数传递以便后续扩展
|
||||
if (groupSetSearchText.value && groupSetSearchText.value.trim()) {
|
||||
params.searchKey = groupSetSearchText.value.trim();
|
||||
}
|
||||
getOrderGroup(params)
|
||||
.then((res) => {
|
||||
const data = res?.data || {};
|
||||
let rawList = [];
|
||||
if (groupSetRange.value === 1) {
|
||||
rawList = data.personalList || [];
|
||||
} else if (groupSetRange.value === 2) {
|
||||
rawList = data.organizationList || [];
|
||||
} else {
|
||||
rawList = data.hospitalList || [];
|
||||
}
|
||||
// 客户端过滤:根据搜索关键字过滤组套名称
|
||||
const keyword = groupSetSearchText.value?.trim()?.toLowerCase();
|
||||
if (keyword) {
|
||||
groupSetList.value = rawList.filter(item => {
|
||||
const name = (item.name || item.Name || '').toLowerCase();
|
||||
return name.includes(keyword);
|
||||
});
|
||||
} else {
|
||||
groupSetList.value = rawList;
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.warn('组套列表加载失败(可能无权限):', err);
|
||||
ElMessage.warning('组套列表加载失败,当前暂无可用组套');
|
||||
groupSetList.value = [];
|
||||
})
|
||||
.finally(() => {
|
||||
groupSetLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function getRangeText(row) {
|
||||
// 后端未返回rangeCode,根据当前选中的范围标签显示
|
||||
const rangeMap = { 1: '个人', 2: '科室', 3: '全院' };
|
||||
if (row.rangeCode_dictText) return row.rangeCode_dictText;
|
||||
return rangeMap[groupSetRange.value] || '';
|
||||
}
|
||||
|
||||
function handleGroupSetSelect(row) {
|
||||
selectedGroupSet.value = row;
|
||||
}
|
||||
|
||||
function applyGroupSet() {
|
||||
if (!selectedGroupSet.value) {
|
||||
ElMessage.warning('请先选择一个组套');
|
||||
return;
|
||||
}
|
||||
const detailList = selectedGroupSet.value.detailList;
|
||||
if (!detailList || detailList.length === 0) {
|
||||
ElMessage.warning('该组套没有明细项');
|
||||
return;
|
||||
}
|
||||
let successCount = 0;
|
||||
detailList.forEach((item) => {
|
||||
const orderDetail = item.orderDetailInfos || {};
|
||||
const feeItem = {
|
||||
adviceDefinitionId: item.orderDefinitionId || orderDetail.adviceDefinitionId,
|
||||
adviceName: orderDetail.adviceName || item.orderDefinitionName || '未知项目',
|
||||
adviceType: orderDetail.adviceType,
|
||||
unitPrice: orderDetail.unitPrice,
|
||||
minUnitPrice: orderDetail.minUnitPrice,
|
||||
inventoryList: orderDetail.inventoryList || [],
|
||||
priceList: orderDetail.priceList || [],
|
||||
partPercent: orderDetail.partPercent || 1,
|
||||
positionId: item.positionId || orderDetail.positionId,
|
||||
defaultLotNumber: orderDetail.defaultLotNumber,
|
||||
unitCode: item.unitCode || orderDetail.unitCode,
|
||||
unitCode_dictText: item.unitCodeName || orderDetail.unitCode_dictText,
|
||||
minUnitCode: orderDetail.minUnitCode,
|
||||
doseUnitCode: orderDetail.doseUnitCode,
|
||||
categoryCode: orderDetail.categoryCode,
|
||||
pharmacologyCategoryCode: orderDetail.pharmacologyCategoryCode,
|
||||
partAttributeEnum: orderDetail.partAttributeEnum,
|
||||
chargeItemDefinitionId: orderDetail.chargeItemDefinitionId,
|
||||
adviceTableName: orderDetail.adviceTableName,
|
||||
methodCode: item.methodCode || orderDetail.methodCode,
|
||||
rateCode: item.rateCode || orderDetail.rateCode,
|
||||
dose: item.dose || orderDetail.dose,
|
||||
doseQuantity: item.doseQuantity,
|
||||
dispensePerDuration: item.dispensePerDuration || orderDetail.dispensePerDuration,
|
||||
dosageInstruction: orderDetail.dosageInstruction,
|
||||
skinTestFlag: orderDetail.skinTestFlag,
|
||||
injectFlag: orderDetail.injectFlag,
|
||||
quantity: item.quantity || 1,
|
||||
chrgitmLv_dictText: orderDetail.chrgitmLv_dictText,
|
||||
volume: orderDetail.volume,
|
||||
};
|
||||
selectChange(feeItem);
|
||||
successCount++;
|
||||
});
|
||||
if (successCount > 0) {
|
||||
ElMessage.success(`已添加 ${successCount} 项组套费用`);
|
||||
}
|
||||
groupSetDialogVisible.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user