Compare commits
29 Commits
f81dd54f0c
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
035738f990 | ||
|
|
d0c6f57f6b | ||
|
|
0d57e984a6 | ||
| 4450e3cc50 | |||
|
|
902ee0587e | ||
| 49550fcc2e | |||
|
|
1dd7ee3428 | ||
|
|
8dff5d466a | ||
|
|
19ada4ace9 | ||
| c92ff38133 | |||
| 1c07108e58 | |||
|
|
34dd969cb4 | ||
| a0b546266d | |||
|
|
fc9ce6241e | ||
|
|
5187ff1ae3 | ||
|
|
73b1d01044 | ||
|
|
b88ad89146 | ||
|
|
de8039c513 | ||
| 3464153d93 | |||
| 6b868e378f | |||
| f6403fa059 | |||
|
|
bc92b9aa62 | ||
|
|
46145ff636 | ||
|
|
3ad32fac9f | ||
| d1223aec07 | |||
| 649f7bcf5b | |||
| a3dce8de60 | |||
|
|
f11b7380a4 | ||
| da17b2b89c |
62
md/需求/99-门诊手术中计费界面PRD_2026-1-22.md
Normal file
62
md/需求/99-门诊手术中计费界面PRD_2026-1-22.md
Normal file
@@ -0,0 +1,62 @@
|
||||
**门诊手术中计费PRD文档**
|
||||
|
||||
**目标:**
|
||||
|
||||
支持手术中追加计费(耗材、药品等)、退费等场景使用
|
||||
|
||||
术后一站式结算(发票、清单、医保等)
|
||||
|
||||
**流程图:**
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["医生开立手术申请单"] --> B{"系统生成计费包"}
|
||||
B --> C["患者缴费"]
|
||||
C --> D["手术室确认"]
|
||||
D --> E{"术中追加/退费?"}
|
||||
|
||||
E -- "是" --> F{"术中计费"}
|
||||
F -- "耗材" --> F2["护士扫码追加耗材\n实时计价 更新库存"]
|
||||
F -- "药品" --> F3["麻醉师追加药品\n实时计价 更新库存"]
|
||||
F -- "诊疗项目" --> F4["追加麻醉时长/项目\n实时计价"]
|
||||
|
||||
|
||||
F2 --> F6["生成术中追加计费单"]
|
||||
F3 --> F6
|
||||
F4 --> F6
|
||||
|
||||
|
||||
F6 --> G{"患者支付?"}
|
||||
G -- "是" --> P["提示支付成功"]--> J
|
||||
G -- "否" --> H["提示支付失败\n保持待支付"]
|
||||
H --> D
|
||||
|
||||
E -- "否" --> I["手术完成"]
|
||||
I --> J["术后统一结算"]
|
||||
J --> K["发票/清单/分割单"]
|
||||
K --> L["财务对账"]
|
||||
```
|
||||
|
||||
**注意:**待门诊手术安排界面(禅道需求编号:93)完成后再执行
|
||||
|
||||

|
||||
|
||||
图1:门诊手术安排界面(禅道需求编号:93)
|
||||
|
||||

|
||||
|
||||
图2:门诊管理-》门诊划价:手术计费界面复制《门诊划价》界面红色框内容
|
||||
|
||||
1、如上图1、2所示:在门诊手术安排界面增加【计费】按钮,实现对门诊手术中追加的费用进行记账,手术计费界面如图2所示复制《门诊划价》界面红色框内容进行个性化改造,患者信息:取值于手术安排界面选中行的患者信息,计费账号为当前系统登录的账号。
|
||||
|
||||
\*比如:在手术计费界面给患者1计费成功后,重新从手术按钮界面选中患者1点击【计费】打开界面时显示当前患者已计费成功的手术费用。
|
||||
|
||||
写入事务注意:
|
||||
|
||||
adm_charge_item费用项管理表
|
||||
|
||||
①、术中费用仍走“门诊就诊管理”的就诊ID(adm_encounter.id = adm_charge_item.encounter_id)。
|
||||
|
||||
2\. 为了事后能追溯“这些费用是术中发生的”,在费用项管理表明细上加一个 “来源业务单据(SourceBillNo)” 字段(adm_charge_item.generate_source_enum = 2(帐单生成来源为手术计费),SourceBillNo = 手术申请单号)。
|
||||
|
||||
3\. 其他内容按照《门诊划价》的业务数据流程走。
|
||||
@@ -1,22 +1,29 @@
|
||||
package com.core.framework.config;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
|
||||
import com.core.common.utils.SecurityUtils;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.LongValue;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -149,4 +156,41 @@ public class MybatisPlusConfig {
|
||||
|
||||
return result != null ? result : 1; // 默认租户ID
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置 SqlSessionFactory
|
||||
* 由于排除了 DataSourceAutoConfiguration,需要手动配置
|
||||
*/
|
||||
@Bean
|
||||
@Primary
|
||||
public SqlSessionFactory sqlSessionFactory(
|
||||
@Qualifier("dynamicDataSource") DataSource dataSource,
|
||||
MybatisPlusInterceptor mybatisPlusInterceptor) throws Exception {
|
||||
MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
|
||||
sessionFactory.setDataSource(dataSource);
|
||||
// 设置 mapper 文件位置
|
||||
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
|
||||
.getResources("classpath*:mapper/**/*Mapper.xml"));
|
||||
// 设置 typeAliases 包路径
|
||||
sessionFactory.setTypeAliasesPackage("com.core.**.domain,com.openhis.**.domain");
|
||||
|
||||
// 配置 MyBatis-Plus
|
||||
MybatisConfiguration configuration = new MybatisConfiguration();
|
||||
// 使用驼峰命名法转换字段
|
||||
configuration.setMapUnderscoreToCamelCase(true);
|
||||
// 开启缓存
|
||||
configuration.setCacheEnabled(true);
|
||||
// 允许JDBC支持自动生成主键
|
||||
configuration.setUseGeneratedKeys(true);
|
||||
// 配置默认的执行器
|
||||
configuration.setDefaultExecutorType(org.apache.ibatis.session.ExecutorType.SIMPLE);
|
||||
// 配置日志实现
|
||||
configuration.setLogImpl(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
|
||||
sessionFactory.setConfiguration(configuration);
|
||||
|
||||
// 设置拦截器(通过参数注入避免循环依赖)
|
||||
sessionFactory.setPlugins(mybatisPlusInterceptor);
|
||||
|
||||
return sessionFactory.getObject();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,14 +13,6 @@ import java.util.Map;
|
||||
*/
|
||||
public interface ITicketAppService {
|
||||
|
||||
/**
|
||||
* 查询号源列表
|
||||
*
|
||||
* @param params 查询参数
|
||||
* @return 号源列表
|
||||
*/
|
||||
R<?> listTicket(Map<String, Object> params);
|
||||
|
||||
/**
|
||||
* 预约号源
|
||||
*
|
||||
|
||||
@@ -4,20 +4,28 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.core.common.core.domain.R;
|
||||
import com.openhis.administration.domain.Patient;
|
||||
import com.openhis.administration.service.IPatientService;
|
||||
import com.openhis.appointmentmanage.domain.DoctorSchedule;
|
||||
import com.openhis.appointmentmanage.mapper.DoctorScheduleMapper;
|
||||
import com.openhis.appointmentmanage.service.IDoctorScheduleService;
|
||||
import com.openhis.clinical.domain.Order;
|
||||
import com.openhis.clinical.domain.Ticket;
|
||||
import com.openhis.clinical.mapper.OrderMapper;
|
||||
import com.openhis.clinical.service.ITicketService;
|
||||
import com.openhis.web.appointmentmanage.appservice.IDoctorScheduleAppService;
|
||||
import com.openhis.web.appointmentmanage.appservice.ITicketAppService;
|
||||
import com.openhis.web.appointmentmanage.dto.TicketDto;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
/**
|
||||
* 号源管理应用服务实现类
|
||||
*
|
||||
@@ -31,145 +39,14 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
||||
|
||||
@Resource
|
||||
private IPatientService patientService;
|
||||
@Resource
|
||||
private IDoctorScheduleAppService doctorScheduleAppService;
|
||||
@Resource
|
||||
private DoctorScheduleMapper doctorScheduleMapper;
|
||||
@Resource
|
||||
private OrderMapper orderMapper;
|
||||
|
||||
/**
|
||||
* 查询号源列表
|
||||
*
|
||||
* @param params 查询参数
|
||||
* @return 号源列表
|
||||
*/
|
||||
@Override
|
||||
public R<?> listTicket(Map<String, Object> params) {
|
||||
// 调试日志:打印所有参数
|
||||
System.out.println("=== listTicket方法收到的所有参数:===");
|
||||
for (Map.Entry<String, Object> entry : params.entrySet()) {
|
||||
System.out.println(entry.getKey() + ": " + entry.getValue());
|
||||
}
|
||||
System.out.println("=================================");
|
||||
// 构建查询条件
|
||||
Ticket ticket = new Ticket();
|
||||
// 设置查询参数
|
||||
// 处理日期参数
|
||||
if (params.containsKey("date")) {
|
||||
String date = (String) params.get("date");
|
||||
try {
|
||||
// 将日期字符串转换为Date类型,设置到appointmentDate字段
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Date appointmentDate = sdf.parse(date);
|
||||
ticket.setAppointmentDate(appointmentDate);
|
||||
System.out.println("设置的appointmentDate:" + appointmentDate);
|
||||
} catch (Exception e) {
|
||||
// 日期格式错误,忽略该参数
|
||||
System.out.println("日期格式错误,忽略该参数:" + date + ",错误信息:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
// 处理状态参数
|
||||
if (params.containsKey("status")) {
|
||||
String status = (String) params.get("status");
|
||||
System.out.println("接收到的status参数:" + status);
|
||||
if (!"all".equals(status) && !"全部".equals(status)) {
|
||||
// 将中文状态转换为英文状态
|
||||
if ("未预约".equals(status)) {
|
||||
ticket.setStatus("unbooked");
|
||||
} else if ("已预约".equals(status)) {
|
||||
ticket.setStatus("booked");
|
||||
} else if ("已取号".equals(status)) {
|
||||
ticket.setStatus("checked");
|
||||
} else if ("已取消".equals(status)) {
|
||||
ticket.setStatus("cancelled");
|
||||
} else if ("已锁定".equals(status)) {
|
||||
ticket.setStatus("locked");
|
||||
} else {
|
||||
ticket.setStatus(status);
|
||||
}
|
||||
System.out.println("设置的status:" + ticket.getStatus());
|
||||
}
|
||||
}
|
||||
if (params.containsKey("name")) {
|
||||
String name = (String) params.get("name");
|
||||
ticket.setPatientName(name);
|
||||
}
|
||||
if (params.containsKey("card")) {
|
||||
String card = (String) params.get("card");
|
||||
ticket.setMedicalCard(card);
|
||||
}
|
||||
if (params.containsKey("phone")) {
|
||||
String phone = (String) params.get("phone");
|
||||
ticket.setPhone(phone);
|
||||
}
|
||||
if (params.containsKey("type")) {
|
||||
String type = (String) params.get("type");
|
||||
System.out.println("前端传递的type参数值:" + type);
|
||||
if (!"all".equals(type)) {
|
||||
// 类型映射转换:前端传递英文类型,数据库存储中文类型
|
||||
if ("general".equals(type)) {
|
||||
ticket.setTicketType("普通");
|
||||
} else if ("expert".equals(type)) {
|
||||
ticket.setTicketType("专家");
|
||||
} else if ("普通".equals(type)) {
|
||||
ticket.setTicketType("普通");
|
||||
} else if ("专家".equals(type)) {
|
||||
ticket.setTicketType("专家");
|
||||
} else {
|
||||
ticket.setTicketType(type);
|
||||
}
|
||||
System.out.println("转换后的ticketType值:" + ticket.getTicketType());
|
||||
}
|
||||
}
|
||||
|
||||
// 手动实现分页查询,避免MyBatis-Plus自动COUNT查询的问题
|
||||
int pageNum = params.get("page") != null ? Integer.valueOf(params.get("page").toString()) : 1;
|
||||
int pageSize = params.get("limit") != null ? Integer.valueOf(params.get("limit").toString()) : 10;
|
||||
|
||||
// 调试:输出构建的查询条件
|
||||
System.out.println("构建的查询条件:ticketType=" + ticket.getTicketType() + ", status=" + ticket.getStatus() + ", appointmentDate=" + ticket.getAppointmentDate());
|
||||
|
||||
// 1. 获取所有符合条件的记录
|
||||
List<Ticket> allTickets = ticketService.selectTicketList(ticket);
|
||||
|
||||
// 调试:输出查询到的所有记录
|
||||
System.out.println("查询到的所有记录:" + allTickets);
|
||||
if (!allTickets.isEmpty()) {
|
||||
for (Ticket t : allTickets) {
|
||||
System.out.println("记录详情:id=" + t.getId() + ", ticketType=" + t.getTicketType() + ", status=" + t.getStatus() + ", appointmentDate=" + t.getAppointmentDate() + ", deleteFlag=" + t.getDeleteFlag());
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 计算总记录数
|
||||
long total = allTickets.size();
|
||||
System.out.println("手动计算的总记录数:" + total);
|
||||
|
||||
// 3. 手动分页
|
||||
int start = (pageNum - 1) * pageSize;
|
||||
int end = Math.min(start + pageSize, allTickets.size());
|
||||
List<Ticket> pageTickets;
|
||||
if (start >= end) {
|
||||
pageTickets = new ArrayList<>();
|
||||
} else {
|
||||
pageTickets = allTickets.subList(start, end);
|
||||
}
|
||||
|
||||
// 4. 转换为DTO
|
||||
List<TicketDto> dtoList = pageTickets.stream().map(this::convertToDto).toList();
|
||||
|
||||
// 5. 构建响应数据,符合前端预期格式
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("list", dtoList);
|
||||
result.put("records", dtoList); // 兼容前端框架(如Element UI)可能使用的records字段
|
||||
result.put("total", total);
|
||||
result.put("page", pageNum);
|
||||
result.put("current", pageNum); // 兼容前端框架可能使用的current字段
|
||||
result.put("limit", pageSize);
|
||||
result.put("pageSize", pageSize); // 兼容前端框架可能使用的pageSize字段
|
||||
result.put("size", pageSize); // 兼容前端框架可能使用的size字段
|
||||
result.put("pageNum", pageNum); // 兼容前端框架可能使用的pageNum字段
|
||||
result.put("pages", (int) Math.ceil((double) total / pageSize)); // 计算总页数
|
||||
|
||||
// 调试:输出响应数据
|
||||
System.out.println("返回的响应数据:" + result);
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
private static final Logger log = LoggerFactory.getLogger(TicketAppServiceImpl.class);
|
||||
|
||||
/**
|
||||
* 预约号源
|
||||
@@ -179,35 +56,73 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
||||
*/
|
||||
@Override
|
||||
public R<?> bookTicket(Map<String, Object> params) {
|
||||
// 1. 获取 ticketId 和 slotId
|
||||
Long ticketId = null;
|
||||
Long slotId = null;
|
||||
if (params.get("ticketId") != null) {
|
||||
ticketId = Long.valueOf(params.get("ticketId").toString());
|
||||
}
|
||||
if (ticketId == null) {
|
||||
return R.fail("参数错误");
|
||||
if (params.get("slotId") != null) {
|
||||
slotId = Long.valueOf(params.get("slotId").toString());
|
||||
}
|
||||
// 2. 参数校验
|
||||
if (ticketId == null || slotId == null) {
|
||||
return R.fail("参数错误:ticketId 或 slotId 不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
// 3. 执行原有的预约逻辑
|
||||
int result = ticketService.bookTicket(params);
|
||||
return R.ok(result > 0 ? "预约成功" : "预约失败");
|
||||
if (result > 0) {
|
||||
// 4. 预约成功后,更新排班表状态
|
||||
DoctorSchedule schedule = new DoctorSchedule();
|
||||
schedule.setId(Math.toIntExact(slotId)); // 对应 XML 中的 WHERE id = #{id}
|
||||
schedule.setIsStopped(true); // 设置为已预约
|
||||
schedule.setStopReason("booked"); // 设置停用原因
|
||||
|
||||
// 执行更新
|
||||
int updateCount = doctorScheduleMapper.updateDoctorSchedule(schedule);
|
||||
|
||||
if (updateCount > 0) {
|
||||
return R.ok("预约成功并已更新排班状态");
|
||||
} else {
|
||||
// 如果更新失败,可能需要根据业务逻辑决定是否回滚预约
|
||||
return R.ok("预约成功,但排班状态更新失败");
|
||||
}
|
||||
} else {
|
||||
return R.fail("预约失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return R.fail(e.getMessage());
|
||||
// e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
return R.fail("系统异常:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消预约
|
||||
*
|
||||
* @param ticketId 号源ID
|
||||
* @param slotId 医生排班ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public R<?> cancelTicket(Long ticketId) {
|
||||
if (ticketId == null) {
|
||||
public R<?> cancelTicket(Long slotId) {
|
||||
if (slotId == null) {
|
||||
return R.fail("参数错误");
|
||||
}
|
||||
try {
|
||||
int result = ticketService.cancelTicket(ticketId);
|
||||
return R.ok(result > 0 ? "取消成功" : "取消失败");
|
||||
ticketService.cancelTicket(slotId);
|
||||
DoctorSchedule schedule = new DoctorSchedule();
|
||||
schedule.setId(Math.toIntExact(slotId)); // 对应 WHERE id = #{id}
|
||||
schedule.setIsStopped(false); // 设置为 false (数据库对应 0)
|
||||
schedule.setStopReason(""); // 将原因清空 (设为空字符串)
|
||||
// 3. 调用自定义更新方法
|
||||
int updateCount = doctorScheduleMapper.updateDoctorSchedule(schedule);
|
||||
if (updateCount > 0) {
|
||||
return R.ok("取消成功");
|
||||
} else {
|
||||
return R.ok("取消成功");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return R.fail(e.getMessage());
|
||||
}
|
||||
@@ -253,31 +168,87 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
||||
|
||||
@Override
|
||||
public R<?> listAllTickets() {
|
||||
// 创建固定的测试数据,用于验证前端是否能展示数据
|
||||
List<TicketDto> testTickets = new ArrayList<>();
|
||||
// 1. 从 AppService 获取排班数据
|
||||
R<?> response = doctorScheduleAppService.getDoctorScheduleList();
|
||||
// 获取返回的 List 数据 (假设 R.ok 里的数据是 List<DoctorSchedule>)
|
||||
List<DoctorSchedule> scheduleList = (List<DoctorSchedule>) response.getData();
|
||||
|
||||
// 创建5条测试数据
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
// 2. 转换数据为 TicketDto
|
||||
List<TicketDto> tickets = new ArrayList<>();
|
||||
|
||||
if (scheduleList != null) {
|
||||
for (DoctorSchedule schedule : scheduleList) {
|
||||
TicketDto dto = new TicketDto();
|
||||
dto.setSlot_id((long) i);
|
||||
dto.setBusNo("TEST0000" + i);
|
||||
dto.setDepartment("内科");
|
||||
dto.setDoctor("张三");
|
||||
|
||||
// 基础信息映射
|
||||
dto.setSlot_id(Long.valueOf(schedule.getId())); // Integer 转 Long
|
||||
dto.setBusNo(String.valueOf(schedule.getId())); // 生成一个业务编号
|
||||
dto.setDepartment(String.valueOf(schedule.getDeptId())); // 如果有科室名建议关联查询,这里暂填ID
|
||||
dto.setDoctor(schedule.getDoctor());
|
||||
|
||||
// 号源类型处理:根据挂号项目判断是普通号还是专家号
|
||||
String registerItem = schedule.getRegisterItem();
|
||||
if (registerItem != null && registerItem.contains("专家")) {
|
||||
dto.setTicketType("expert");
|
||||
dto.setDateTime("08:00-08:50");
|
||||
} else {
|
||||
dto.setTicketType("general");
|
||||
}
|
||||
// 时间处理:格式化为日期+时间范围,如 "2025-12-01 08:00-12:00"
|
||||
String currentDate = LocalDate.now().toString(); // 或者从schedule中获取具体日期
|
||||
String timeRange = schedule.getStartTime() + "-" + schedule.getEndTime();
|
||||
dto.setDateTime(currentDate + " " + timeRange);
|
||||
LocalTime nowTime = LocalTime.now();
|
||||
LocalTime endTime = schedule.getEndTime();
|
||||
String stopReason1 = schedule.getStopReason();
|
||||
if ("cancelled".equals(stopReason1)||(endTime != null && nowTime.isAfter(endTime))) {
|
||||
dto.setStatus("已停诊");
|
||||
}else if (Boolean.TRUE.equals(schedule.getIsStopped())) {
|
||||
// 获取原因并处理可能的空值
|
||||
String stopReason = schedule.getStopReason();
|
||||
// 使用 .equals() 比较内容,并将常量放在前面防止空指针
|
||||
if ("booked".equals(stopReason)) {
|
||||
dto.setStatus("已预约");
|
||||
// --- 新增:获取患者信息 ---
|
||||
List<Order> Order = orderMapper.selectOrderBySlotId(Long.valueOf(schedule.getId()));
|
||||
Order latestOrder=Order.get(0);
|
||||
|
||||
if (latestOrder != null) {
|
||||
dto.setPatientName(latestOrder.getPatientName());
|
||||
dto.setPatientId(String.valueOf(latestOrder.getPatientId()));
|
||||
dto.setPhone(latestOrder.getPhone());
|
||||
}
|
||||
// -----------------------
|
||||
} else if ("checked".equals(stopReason)) {
|
||||
dto.setStatus("已取号");
|
||||
} else {
|
||||
// 兜底逻辑:如果 is_stopped 为 true 但没有匹配到原因
|
||||
dto.setStatus("不可预约");
|
||||
}
|
||||
} else {
|
||||
// is_stopped 为 false 或 null 时
|
||||
dto.setStatus("未预约");
|
||||
dto.setFee("150");
|
||||
dto.setAppointmentDate(new Date());
|
||||
testTickets.add(dto);
|
||||
}
|
||||
|
||||
// 构建响应数据
|
||||
// 费用处理 (挂号费 + 诊疗费)
|
||||
int totalFee = schedule.getRegisterFee() + schedule.getDiagnosisFee();
|
||||
dto.setFee(String.valueOf(totalFee));
|
||||
|
||||
// 日期处理:LocalDateTime 转 Date
|
||||
if (schedule.getCreateTime() != null) {
|
||||
ZonedDateTime zdt = schedule.getCreateTime().atZone(ZoneId.systemDefault());
|
||||
dto.setAppointmentDate(Date.from(zdt.toInstant()));
|
||||
}
|
||||
|
||||
tickets.add(dto);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 封装分页响应结构
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("list", testTickets);
|
||||
result.put("total", testTickets.size());
|
||||
result.put("list", tickets);
|
||||
result.put("total", tickets.size());
|
||||
result.put("page", 1);
|
||||
result.put("limit", 20);
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
@@ -322,9 +293,6 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
||||
case "cancelled":
|
||||
dto.setStatus("已取消");
|
||||
break;
|
||||
case "locked":
|
||||
dto.setStatus("已锁定");
|
||||
break;
|
||||
default:
|
||||
dto.setStatus(status);
|
||||
}
|
||||
|
||||
@@ -22,28 +22,6 @@ public class TicketController {
|
||||
@Resource
|
||||
private ITicketAppService ticketAppService;
|
||||
|
||||
/**
|
||||
* 查询号源列表
|
||||
*
|
||||
* @param params 查询参数
|
||||
* @return 号源列表
|
||||
*/
|
||||
@PostMapping("/list")
|
||||
public R<?> listTicket(@RequestBody Map<String, Object> params) {
|
||||
return ticketAppService.listTicket(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询号源列表(支持GET请求,兼容旧版本)
|
||||
*
|
||||
* @param params 查询参数
|
||||
* @return 号源列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public R<?> listTicketByGet(@RequestParam Map<String, Object> params) {
|
||||
return ticketAppService.listTicket(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有号源(用于测试)
|
||||
*
|
||||
|
||||
@@ -73,8 +73,10 @@ public class OutpatientPricingAppServiceImpl implements IOutpatientPricingAppSer
|
||||
} else {
|
||||
adviceTypes = List.of(1, 2, 3);
|
||||
}
|
||||
// 门诊划价:不要强制 pricingFlag=1 参与过滤(wor_activity_definition.pricing_flag 可能为 0),
|
||||
// 否则会导致诊疗项目(adviceType=3)查询结果为空 records=[]
|
||||
return iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, searchKey, locationId, null,
|
||||
organizationId, pageNo, pageSize, Whether.YES.getValue(), adviceTypes, null);
|
||||
organizationId, pageNo, pageSize, null, adviceTypes, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ import com.openhis.web.chargemanage.dto.PatientMetadata;
|
||||
import com.openhis.web.chargemanage.dto.PractitionerMetadata;
|
||||
import com.openhis.web.chargemanage.dto.ReprintRegistrationDto;
|
||||
import com.openhis.web.chargemanage.mapper.OutpatientRegistrationAppMapper;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageCandidateExclusion;
|
||||
import com.openhis.triageandqueuemanage.service.TriageCandidateExclusionService;
|
||||
import com.openhis.web.paymentmanage.appservice.IPaymentRecService;
|
||||
import com.openhis.web.paymentmanage.dto.CancelPaymentDto;
|
||||
import com.openhis.web.paymentmanage.dto.CancelRegPaymentDto;
|
||||
@@ -38,12 +40,15 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 门诊挂号 应用实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistrationAppService {
|
||||
|
||||
@@ -77,6 +82,9 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
||||
@Resource
|
||||
IPatientIdentifierService patientIdentifierService;
|
||||
|
||||
@Resource
|
||||
TriageCandidateExclusionService triageCandidateExclusionService;
|
||||
|
||||
/**
|
||||
* 门诊挂号 - 查询患者信息
|
||||
*
|
||||
@@ -308,6 +316,47 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
||||
new Page<>(pageNo, pageSize), EncounterClass.AMB.getValue(), EncounterStatus.IN_PROGRESS.getValue(),
|
||||
ParticipantType.ADMITTER.getCode(), ParticipantType.REGISTRATION_DOCTOR.getCode(), queryWrapper,
|
||||
ChargeItemContext.REGISTER.getValue(), PaymentStatus.SUCCESS.getValue());
|
||||
|
||||
// 过滤候选池排除列表(如果是从智能候选池查询,排除已加入队列的患者)
|
||||
// 检查请求参数 excludeFromCandidatePool,如果为 true 或未设置,则过滤排除列表
|
||||
String excludeParam = request.getParameter("excludeFromCandidatePool");
|
||||
boolean shouldExclude = excludeParam == null || "true".equalsIgnoreCase(excludeParam);
|
||||
if (shouldExclude && currentDayEncounter != null && !currentDayEncounter.getRecords().isEmpty()) {
|
||||
try {
|
||||
// 获取当前租户和日期
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
// 查询排除列表
|
||||
List<TriageCandidateExclusion> exclusions = triageCandidateExclusionService.list(
|
||||
new LambdaQueryWrapper<TriageCandidateExclusion>()
|
||||
.eq(TriageCandidateExclusion::getTenantId, tenantId)
|
||||
.eq(TriageCandidateExclusion::getExclusionDate, today)
|
||||
.eq(TriageCandidateExclusion::getDeleteFlag, "0")
|
||||
);
|
||||
|
||||
if (exclusions != null && !exclusions.isEmpty()) {
|
||||
// 构建排除的 encounterId 集合
|
||||
Set<Long> excludedEncounterIds = exclusions.stream()
|
||||
.map(TriageCandidateExclusion::getEncounterId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 过滤结果
|
||||
List<CurrentDayEncounterDto> filteredRecords = currentDayEncounter.getRecords().stream()
|
||||
.filter(e -> e.getEncounterId() == null || !excludedEncounterIds.contains(e.getEncounterId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 更新分页结果
|
||||
currentDayEncounter.setRecords(filteredRecords);
|
||||
currentDayEncounter.setTotal(filteredRecords.size());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 如果过滤失败,记录日志但不影响正常查询
|
||||
log.warn("过滤候选池排除列表失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
currentDayEncounter.getRecords().forEach(e -> {
|
||||
// 性别
|
||||
e.setGenderEnum_enumText(EnumUtils.getInfoByValue(AdministrativeGender.class, e.getGenderEnum()));
|
||||
|
||||
@@ -90,4 +90,10 @@ public interface IDoctorStationMainAppService {
|
||||
*/
|
||||
List<ReceptionStatisticsDto> getReceptionStatistics(String startTime,String endTime,Long practitionerId);
|
||||
|
||||
/**
|
||||
* 过号重排
|
||||
* @param encounterId 就诊ID
|
||||
* @return 操作结果
|
||||
*/
|
||||
R<?> rearrangeMissedEncounter(Long encounterId);
|
||||
}
|
||||
|
||||
@@ -127,8 +127,11 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
|
||||
log.info("从数据库查询医嘱基础信息");
|
||||
|
||||
// 设置默认科室 (不取前端传的了)
|
||||
// 设置默认科室:仅当前端/调用方未传 organizationId 时才回退到登录人科室
|
||||
// 否则会导致门诊划价等场景(按患者挂号科室查询)返回空
|
||||
if (organizationId == null) {
|
||||
organizationId = SecurityUtils.getLoginUser().getOrgId();
|
||||
}
|
||||
|
||||
// 医嘱定价来源
|
||||
String orderPricingSource = TenantOptionUtil.getOptionContent(TenantOptionDict.ORDER_PRICING_SOURCE);
|
||||
|
||||
@@ -26,9 +26,11 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -121,6 +123,8 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
|
||||
*/
|
||||
@Override
|
||||
public R<?> receiveEncounter(Long encounterId) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
String currentUsername = SecurityUtils.getUsername();
|
||||
int update = encounterMapper.update(null,
|
||||
new LambdaUpdateWrapper<Encounter>().eq(Encounter::getId, encounterId)
|
||||
.set(Encounter::getReceptionTime, new Date())
|
||||
@@ -138,6 +142,9 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
|
||||
encounterParticipant.setTypeCode(ParticipantType.ADMITTER.getCode());// 接诊医生
|
||||
encounterParticipant.setPractitionerId(SecurityUtils.getLoginUser().getPractitionerId());
|
||||
encounterParticipant.setStatusEnum(EncounterActivityStatus.ACTIVE.getValue()); // 状态
|
||||
encounterParticipant.setTenantId(tenantId);
|
||||
encounterParticipant.setCreateBy(currentUsername);
|
||||
encounterParticipant.setCreateTime(new Date());
|
||||
iEncounterParticipantService.save(encounterParticipant);
|
||||
return update > 0 ? R.ok() : R.fail();
|
||||
}
|
||||
@@ -323,4 +330,45 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
|
||||
practitionerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 过号重排核心实现
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class) // 事务保证原子性
|
||||
public R<?> rearrangeMissedEncounter(Long encounterId) {
|
||||
// 1. 校验就诊记录是否存在
|
||||
Encounter encounter = encounterMapper.selectById(encounterId);
|
||||
if (encounter == null) {
|
||||
return R.fail("就诊记录不存在");
|
||||
}
|
||||
|
||||
// 2. 校验状态:仅「在诊(IN_PROGRESS=2)」可重排
|
||||
if (!EncounterStatus.IN_PROGRESS.getValue().equals(encounter.getStatusEnum())) {
|
||||
return R.fail("仅「在诊」状态的患者可执行过号重排");
|
||||
}
|
||||
|
||||
// 3. 核心更新:改回待诊+更新missed_time
|
||||
Date now = new Date();
|
||||
Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
|
||||
int updateCount = encounterMapper.update(null,
|
||||
new LambdaUpdateWrapper<Encounter>()
|
||||
.eq(Encounter::getId, encounterId)
|
||||
.set(Encounter::getStatusEnum, EncounterStatus.PLANNED.getValue()) // 改回1-待诊
|
||||
.set(Encounter::getMissedTime, now) // 新增:设置过号时间为当前时间
|
||||
.set(Encounter::getUpdateBy, practitionerId.toString()) // 操作医生ID
|
||||
.eq(Encounter::getStatusEnum, EncounterStatus.IN_PROGRESS.getValue())); // 防并发
|
||||
|
||||
if (updateCount == 0) {
|
||||
return R.fail("过号重排失败:状态更新异常");
|
||||
}
|
||||
|
||||
// 4. 同步更新接诊参与记录
|
||||
iEncounterParticipantService.update(new LambdaUpdateWrapper<EncounterParticipant>()
|
||||
.eq(EncounterParticipant::getEncounterId, encounterId)
|
||||
.eq(EncounterParticipant::getTypeCode, ParticipantType.ADMITTER.getCode())
|
||||
.set(EncounterParticipant::getStatusEnum, EncounterActivityStatus.COMPLETED.getValue()));
|
||||
|
||||
return R.ok("过号重排成功");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -133,6 +133,29 @@ public class DoctorStationMainController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 过号重排
|
||||
*
|
||||
* @param encounterId 就诊id
|
||||
* @return 结果
|
||||
*/
|
||||
@GetMapping(value = "/rearrange-missed-encounter")
|
||||
public R<?> rearrangeMissedEncounter(@RequestParam(value = "encounterId", required = false) String encounterId) {
|
||||
// 1. 空值校验(和现有接口保持一致)
|
||||
if (encounterId == null || "undefined".equals(encounterId) || "null".equals(encounterId)) {
|
||||
return R.fail("就诊ID不能为空");
|
||||
}
|
||||
try {
|
||||
// 2. 字符串转Long(和现有接口保持一致)
|
||||
Long id = Long.parseLong(encounterId);
|
||||
// 3. 调用AppService的过号重排方法
|
||||
return iDoctorStationMainAppService.rearrangeMissedEncounter(id);
|
||||
} catch (NumberFormatException e) {
|
||||
// 4. 格式错误处理(和现有接口保持一致)
|
||||
return R.fail("就诊ID格式错误");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询处方号列表信息
|
||||
*
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.openhis.web.doctorstation.dto;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import com.openhis.common.annotation.Dict;
|
||||
@@ -127,4 +128,9 @@ public class PatientInfoDto {
|
||||
* 就诊卡号
|
||||
*/
|
||||
private String identifierNo;
|
||||
|
||||
/**
|
||||
* 过号时间
|
||||
*/
|
||||
private Date missedTime;
|
||||
}
|
||||
|
||||
@@ -293,11 +293,8 @@ public class ATDManageAppServiceImpl implements IATDManageAppService {
|
||||
if (admissionPatientInfoDto.getPriorityEnum() != null) {
|
||||
// 更新患者病情
|
||||
encounterService.updatePriorityEnumById(encounterId, admissionPatientInfoDto.getPriorityEnum());
|
||||
// 将之前的住院参与者更新为已完成
|
||||
Integer result = encounterParticipantService.updateEncounterParticipantsStatus(encounterId);
|
||||
if (result == 0) {
|
||||
return R.fail("患者信息更新失败,请联系管理员");
|
||||
}
|
||||
// 将之前的住院参与者更新为已完成(如果存在的话)
|
||||
encounterParticipantService.updateEncounterParticipantsStatus(encounterId);
|
||||
// 更新住院参与者
|
||||
// 住院医生
|
||||
encounterParticipantService.creatEncounterParticipants(encounterId, startTime,
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.openhis.web.inventorymanage.dto.*;
|
||||
import com.openhis.web.inventorymanage.mapper.PurchaseInventoryMapper;
|
||||
import com.openhis.workflow.domain.SupplyRequest;
|
||||
import com.openhis.workflow.service.ISupplyRequestService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@@ -40,6 +41,7 @@ import java.util.stream.Stream;
|
||||
* @author zwh
|
||||
* @date 2025-03-08
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class PurchaseInventoryAppServiceImpl implements IPurchaseInventoryAppService {
|
||||
|
||||
@@ -159,6 +161,7 @@ public class PurchaseInventoryAppServiceImpl implements IPurchaseInventoryAppSer
|
||||
@Override
|
||||
public R<List<ReceiptDetailDto>> getDetail(String busNo) {
|
||||
List<ReceiptDetailDto> receiptDetailList = purchaseInventoryMapper.selectDetail(busNo);
|
||||
log.debug("返回查询结果,receiptDetailList:{}", receiptDetailList);
|
||||
if (receiptDetailList.isEmpty()) {
|
||||
return R.fail(MessageUtils.createMessage(PromptMsgConstant.Common.M00006, null));
|
||||
}
|
||||
@@ -182,7 +185,7 @@ public class PurchaseInventoryAppServiceImpl implements IPurchaseInventoryAppSer
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
log.debug("返回查询结果,receiptDetailList:{}", receiptDetailList);
|
||||
return R.ok(receiptDetailList);
|
||||
}
|
||||
|
||||
@@ -194,7 +197,6 @@ public class PurchaseInventoryAppServiceImpl implements IPurchaseInventoryAppSer
|
||||
*/
|
||||
@Override
|
||||
public R<?> addOrEditInventoryReceipt(List<PurchaseInventoryDto> purchaseInventoryDtoList) {
|
||||
|
||||
// 校验(已经审批通过的单号(请求状态是同意),不能再重复编辑请求)
|
||||
boolean validation = supplyRequestService.supplyRequestValidation(purchaseInventoryDtoList.get(0).getBusNo());
|
||||
if (validation) {
|
||||
@@ -232,11 +234,14 @@ public class PurchaseInventoryAppServiceImpl implements IPurchaseInventoryAppSer
|
||||
// 制单人
|
||||
.setApplicantId(SecurityUtils.getLoginUser().getPractitionerId())
|
||||
// 申请时间
|
||||
.setApplyTime(DateUtils.getNowDate());
|
||||
.setApplyTime(DateUtils.getNowDate())
|
||||
|
||||
.setCreateBy(SecurityUtils.getLoginUser().getUsername())
|
||||
.setCreateTime(DateUtils.getNowDate())
|
||||
.setTenantId(SecurityUtils.getLoginUser().getTenantId());
|
||||
supplyRequestList.add(supplyRequest);
|
||||
|
||||
}
|
||||
|
||||
// 保存
|
||||
supplyRequestService.saveOrUpdateBatch(supplyRequestList);
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.openhis.web.triageandqueuemanage.appservice;
|
||||
|
||||
import com.core.common.core.domain.R;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueActionReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAddReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAdjustReq;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public interface TriageQueueAppService {
|
||||
R<?> list(Long organizationId, LocalDate date);
|
||||
R<?> add(TriageQueueAddReq req);
|
||||
R<?> remove(Long id);
|
||||
R<?> adjust(TriageQueueAdjustReq req);
|
||||
/** 选呼:将之前叫号中置为完成,选中的置为叫号中 */
|
||||
R<?> call(TriageQueueActionReq req);
|
||||
/** 完成:叫号中 -> 完成(移出列表),并自动推进下一个等待为叫号中 */
|
||||
R<?> complete(TriageQueueActionReq req);
|
||||
/** 过号重排:叫号中 -> 跳过并移到末尾,并自动推进下一个等待为叫号中 */
|
||||
R<?> requeue(TriageQueueActionReq req);
|
||||
/** 跳过:兼容前端按钮(当前实现等同于过号重排) */
|
||||
R<?> skip(TriageQueueActionReq req);
|
||||
/** 下一患者:当前叫号中 -> 完成,下一位等待 -> 叫号中 */
|
||||
R<?> next(TriageQueueActionReq req);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,556 @@
|
||||
package com.openhis.web.triageandqueuemanage.appservice.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.core.common.core.domain.R;
|
||||
import com.core.common.utils.SecurityUtils;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageQueueItem;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageCandidateExclusion;
|
||||
import com.openhis.triageandqueuemanage.service.TriageQueueItemService;
|
||||
import com.openhis.triageandqueuemanage.service.TriageCandidateExclusionService;
|
||||
import com.openhis.web.triageandqueuemanage.appservice.TriageQueueAppService;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueActionReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAddReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAdjustReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueEncounterItem;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@Service
|
||||
public class TriageQueueAppServiceImpl implements TriageQueueAppService {
|
||||
|
||||
private static final String STATUS_WAITING = "WAITING";
|
||||
private static final String STATUS_CALLING = "CALLING";
|
||||
private static final String STATUS_SKIPPED = "SKIPPED";
|
||||
private static final String STATUS_COMPLETED = "COMPLETED";
|
||||
|
||||
@Resource
|
||||
private TriageQueueItemService triageQueueItemService;
|
||||
|
||||
@Resource
|
||||
private TriageCandidateExclusionService triageCandidateExclusionService;
|
||||
|
||||
@Override
|
||||
public R<?> list(Long organizationId, LocalDate date) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
// 只查询今天的患者
|
||||
LocalDate qd = date != null ? date : LocalDate.now();
|
||||
|
||||
LambdaQueryWrapper<TriageQueueItem> wrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getQueueDate, qd)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.ne(TriageQueueItem::getStatus, STATUS_COMPLETED)
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder);
|
||||
|
||||
// 如果指定了科室,按科室过滤;否则查询所有科室(全科模式)
|
||||
if (organizationId != null) {
|
||||
wrapper.eq(TriageQueueItem::getOrganizationId, organizationId);
|
||||
}
|
||||
|
||||
List<TriageQueueItem> list = triageQueueItemService.list(wrapper);
|
||||
|
||||
// 双重保险:再次过滤掉 COMPLETED 状态的患者(防止数据库中有异常数据)
|
||||
if (list != null && !list.isEmpty()) {
|
||||
int beforeSize = list.size();
|
||||
list = list.stream()
|
||||
.filter(item -> !STATUS_COMPLETED.equals(item.getStatus()))
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
if (beforeSize != list.size()) {
|
||||
System.out.println(">>> [TriageQueue] list() 警告:过滤掉了 " + (beforeSize - list.size()) + " 条 COMPLETED 状态的记录");
|
||||
}
|
||||
}
|
||||
|
||||
// 调试日志:检查状态值
|
||||
if (list != null && !list.isEmpty()) {
|
||||
System.out.println(">>> [TriageQueue] list() 返回 " + list.size() + " 条记录(已排除 COMPLETED)");
|
||||
for (int i = 0; i < Math.min(3, list.size()); i++) {
|
||||
TriageQueueItem item = list.get(i);
|
||||
System.out.println(" [" + i + "] patientName=" + item.getPatientName()
|
||||
+ ", status=" + item.getStatus()
|
||||
+ ", organizationId=" + item.getOrganizationId());
|
||||
}
|
||||
}
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> add(TriageQueueAddReq req) {
|
||||
if (req == null || ObjectUtil.isNull(req.getOrganizationId())) {
|
||||
return R.fail("organizationId 不能为空");
|
||||
}
|
||||
if (CollUtil.isEmpty(req.getItems())) {
|
||||
return R.fail("items 不能为空");
|
||||
}
|
||||
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
LocalDate qd = LocalDate.now();
|
||||
Long orgId = req.getOrganizationId();
|
||||
|
||||
List<TriageQueueItem> existing = triageQueueItemService.list(new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getOrganizationId, orgId)
|
||||
.eq(TriageQueueItem::getQueueDate, qd)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.ne(TriageQueueItem::getStatus, STATUS_COMPLETED));
|
||||
|
||||
int maxOrder = existing.stream().map(TriageQueueItem::getQueueOrder).filter(Objects::nonNull).max(Integer::compareTo).orElse(0);
|
||||
|
||||
int added = 0;
|
||||
for (TriageQueueEncounterItem it : req.getItems()) {
|
||||
if (it == null || it.getEncounterId() == null) continue;
|
||||
boolean exists = existing.stream().anyMatch(e -> Objects.equals(e.getEncounterId(), it.getEncounterId()));
|
||||
if (exists) continue;
|
||||
|
||||
TriageQueueItem qi = new TriageQueueItem()
|
||||
.setTenantId(tenantId)
|
||||
.setQueueDate(qd)
|
||||
.setOrganizationId(orgId)
|
||||
.setOrganizationName(req.getOrganizationName())
|
||||
.setEncounterId(it.getEncounterId())
|
||||
.setPatientId(it.getPatientId())
|
||||
.setPatientName(it.getPatientName())
|
||||
.setHealthcareName(it.getHealthcareName())
|
||||
.setPractitionerName(it.getPractitionerName())
|
||||
.setStatus(STATUS_WAITING)
|
||||
.setQueueOrder(++maxOrder)
|
||||
.setDeleteFlag("0")
|
||||
.setCreateTime(LocalDateTime.now())
|
||||
.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
triageQueueItemService.save(qi);
|
||||
|
||||
// 记录到候选池排除列表(避免刷新后重新出现在候选池)
|
||||
TriageCandidateExclusion exclusion = triageCandidateExclusionService.getOne(
|
||||
new LambdaQueryWrapper<TriageCandidateExclusion>()
|
||||
.eq(TriageCandidateExclusion::getTenantId, tenantId)
|
||||
.eq(TriageCandidateExclusion::getExclusionDate, qd)
|
||||
.eq(TriageCandidateExclusion::getEncounterId, it.getEncounterId())
|
||||
.eq(TriageCandidateExclusion::getDeleteFlag, "0")
|
||||
);
|
||||
if (exclusion == null) {
|
||||
exclusion = new TriageCandidateExclusion()
|
||||
.setTenantId(tenantId)
|
||||
.setExclusionDate(qd)
|
||||
.setEncounterId(it.getEncounterId())
|
||||
.setPatientId(it.getPatientId())
|
||||
.setPatientName(it.getPatientName())
|
||||
.setOrganizationId(orgId)
|
||||
.setOrganizationName(req.getOrganizationName())
|
||||
.setReason("ADDED_TO_QUEUE")
|
||||
.setDeleteFlag("0")
|
||||
.setCreateTime(LocalDateTime.now())
|
||||
.setUpdateTime(LocalDateTime.now());
|
||||
triageCandidateExclusionService.save(exclusion);
|
||||
}
|
||||
|
||||
added++;
|
||||
}
|
||||
|
||||
return R.ok(added);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> remove(Long id) {
|
||||
if (id == null) return R.fail("id 不能为空");
|
||||
TriageQueueItem item = triageQueueItemService.getById(id);
|
||||
if (item == null) return R.fail("队列项不存在");
|
||||
|
||||
// 逻辑删除队列项
|
||||
item.setDeleteFlag("1").setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(item);
|
||||
|
||||
// 从排除列表中删除记录,使患者重新出现在候选池中
|
||||
Integer tenantId = item.getTenantId();
|
||||
LocalDate exclusionDate = item.getQueueDate();
|
||||
Long encounterId = item.getEncounterId();
|
||||
|
||||
if (tenantId != null && exclusionDate != null && encounterId != null) {
|
||||
TriageCandidateExclusion exclusion = triageCandidateExclusionService.getOne(
|
||||
new LambdaQueryWrapper<TriageCandidateExclusion>()
|
||||
.eq(TriageCandidateExclusion::getTenantId, tenantId)
|
||||
.eq(TriageCandidateExclusion::getExclusionDate, exclusionDate)
|
||||
.eq(TriageCandidateExclusion::getEncounterId, encounterId)
|
||||
.eq(TriageCandidateExclusion::getDeleteFlag, "0")
|
||||
);
|
||||
|
||||
if (exclusion != null) {
|
||||
// 逻辑删除排除记录
|
||||
exclusion.setDeleteFlag("1").setUpdateTime(LocalDateTime.now());
|
||||
triageCandidateExclusionService.updateById(exclusion);
|
||||
}
|
||||
}
|
||||
|
||||
recalcOrders(item.getOrganizationId(), item.getQueueDate());
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> adjust(TriageQueueAdjustReq req) {
|
||||
if (req == null || req.getId() == null) return R.fail("id 不能为空");
|
||||
if (!"up".equalsIgnoreCase(req.getDirection()) && !"down".equalsIgnoreCase(req.getDirection())) {
|
||||
return R.fail("direction 只能是 up/down");
|
||||
}
|
||||
TriageQueueItem cur = triageQueueItemService.getById(req.getId());
|
||||
if (cur == null) return R.fail("队列项不存在");
|
||||
|
||||
List<TriageQueueItem> list = listInternal(cur.getOrganizationId(), cur.getQueueDate());
|
||||
list.sort(Comparator.comparing(TriageQueueItem::getQueueOrder).thenComparing(TriageQueueItem::getId));
|
||||
int idx = -1;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (Objects.equals(list.get(i).getId(), cur.getId())) { idx = i; break; }
|
||||
}
|
||||
if (idx == -1) return R.fail("队列项不在当前队列");
|
||||
|
||||
int targetIdx = "up".equalsIgnoreCase(req.getDirection()) ? idx - 1 : idx + 1;
|
||||
if (targetIdx < 0 || targetIdx >= list.size()) return R.ok(false);
|
||||
|
||||
TriageQueueItem other = list.get(targetIdx);
|
||||
Integer tmp = cur.getQueueOrder();
|
||||
cur.setQueueOrder(other.getQueueOrder()).setUpdateTime(LocalDateTime.now());
|
||||
other.setQueueOrder(tmp).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(cur);
|
||||
triageQueueItemService.updateById(other);
|
||||
|
||||
recalcOrders(cur.getOrganizationId(), cur.getQueueDate());
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> call(TriageQueueActionReq req) {
|
||||
if (req == null || req.getId() == null) return R.fail("id 不能为空");
|
||||
TriageQueueItem selected = triageQueueItemService.getById(req.getId());
|
||||
if (selected == null) return R.fail("队列项不存在");
|
||||
|
||||
// 只将"等待"状态的患者转为"叫号中",允许有多个"叫号中"的患者
|
||||
if (STATUS_WAITING.equals(selected.getStatus())) {
|
||||
selected.setStatus(STATUS_CALLING).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(selected);
|
||||
return R.ok(true);
|
||||
} else if (STATUS_CALLING.equals(selected.getStatus())) {
|
||||
// 如果已经是"叫号中"状态,直接返回成功(不做任何操作)
|
||||
return R.ok(true);
|
||||
} else {
|
||||
// 其他状态(如 SKIPPED、COMPLETED)不能选呼
|
||||
return R.fail("只能选呼\"等待\"状态的患者,当前患者状态为:" + selected.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> complete(TriageQueueActionReq req) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
TriageQueueItem calling = null;
|
||||
|
||||
// 关键改进:如果提供了 id,直接通过ID获取(像 call 方法一样),不依赖查询条件
|
||||
if (req != null && req.getId() != null) {
|
||||
calling = triageQueueItemService.getById(req.getId());
|
||||
if (calling == null) {
|
||||
return R.fail("队列项不存在");
|
||||
}
|
||||
// 验证状态
|
||||
if (!STATUS_CALLING.equals(calling.getStatus())) {
|
||||
return R.fail("只能完成\"叫号中\"状态的患者,当前患者状态为:" + calling.getStatus());
|
||||
}
|
||||
} else {
|
||||
// 如果没有提供 id,通过查询条件查找(兼容旧逻辑)
|
||||
Long orgId = req != null && req.getOrganizationId() != null ? req.getOrganizationId() : null;
|
||||
System.out.println(">>> [TriageQueue] complete() 开始执行(不限制日期,通过查询条件), tenantId=" + tenantId + ", orgId=" + orgId);
|
||||
|
||||
LambdaQueryWrapper<TriageQueueItem> callingWrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.eq(TriageQueueItem::getStatus, STATUS_CALLING)
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
.last("LIMIT 1");
|
||||
|
||||
if (orgId != null) {
|
||||
callingWrapper.eq(TriageQueueItem::getOrganizationId, orgId);
|
||||
}
|
||||
|
||||
calling = triageQueueItemService.getOne(callingWrapper, false);
|
||||
|
||||
System.out.println(">>> [TriageQueue] complete() 查询叫号中患者(不限制日期): orgId=" + orgId + ", 结果=" + (calling != null ? calling.getPatientName() + "(status=" + calling.getStatus() + ", queueDate=" + calling.getQueueDate() + ")" : "null"));
|
||||
|
||||
if (calling == null) {
|
||||
return R.fail("当前没有叫号中的患者");
|
||||
}
|
||||
}
|
||||
|
||||
// 使用实际找到的科室ID
|
||||
Long actualOrgId = calling.getOrganizationId();
|
||||
|
||||
// 1) 叫号中 -> 完成(移出列表)
|
||||
calling.setStatus(STATUS_COMPLETED).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(calling);
|
||||
|
||||
// 2) 自动推进下一个等待为叫号中(同一科室,包含跳过状态,不限制日期)
|
||||
LambdaQueryWrapper<TriageQueueItem> nextWrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.and(w -> w.eq(TriageQueueItem::getStatus, STATUS_WAITING)
|
||||
.or()
|
||||
.eq(TriageQueueItem::getStatus, STATUS_SKIPPED))
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
.last("LIMIT 1");
|
||||
|
||||
// 如果指定了科室ID,则按科室过滤;否则查询所有科室(全科模式)
|
||||
if (actualOrgId != null) {
|
||||
nextWrapper.eq(TriageQueueItem::getOrganizationId, actualOrgId);
|
||||
}
|
||||
|
||||
TriageQueueItem next = triageQueueItemService.getOne(nextWrapper, false);
|
||||
|
||||
System.out.println(">>> [TriageQueue] complete() 查询等待患者(不限制日期): actualOrgId=" + actualOrgId + ", 结果=" + (next != null ? next.getPatientName() + "(status=" + next.getStatus() + ")" : "null"));
|
||||
|
||||
if (next != null) {
|
||||
next.setStatus(STATUS_CALLING).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(next);
|
||||
}
|
||||
|
||||
recalcOrders(actualOrgId, null);
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> requeue(TriageQueueActionReq req) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
TriageQueueItem calling = null;
|
||||
|
||||
|
||||
if (req != null && req.getId() != null) {
|
||||
calling = triageQueueItemService.getById(req.getId());
|
||||
if (calling == null) {
|
||||
return R.fail("队列项不存在");
|
||||
}
|
||||
// 验证状态
|
||||
if (!STATUS_CALLING.equals(calling.getStatus())) {
|
||||
return R.fail("只能对\"叫号中\"状态的患者进行过号重排,当前患者状态为:" + calling.getStatus());
|
||||
}
|
||||
} else {
|
||||
// 如果没有提供 id,通过查询条件查找(兼容旧逻辑)
|
||||
Long orgId = req != null && req.getOrganizationId() != null ? req.getOrganizationId() : null;
|
||||
|
||||
LambdaQueryWrapper<TriageQueueItem> callingWrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.eq(TriageQueueItem::getStatus, STATUS_CALLING)
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
.last("LIMIT 1");
|
||||
|
||||
if (orgId != null) {
|
||||
callingWrapper.eq(TriageQueueItem::getOrganizationId, orgId);
|
||||
}
|
||||
|
||||
calling = triageQueueItemService.getOne(callingWrapper, false);
|
||||
|
||||
if (calling == null) return R.fail("当前没有叫号中的患者");
|
||||
}
|
||||
|
||||
// 使用实际找到的科室ID
|
||||
Long actualOrgId = calling.getOrganizationId();
|
||||
|
||||
// 关键改进:在执行"跳过"操作之前,先检查是否有等待中的患者(判断队列状态)
|
||||
// 如果没有等待中的患者,就不应该执行"过号重排"操作
|
||||
LambdaQueryWrapper<TriageQueueItem> nextWrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.and(w -> w.eq(TriageQueueItem::getStatus, STATUS_WAITING)
|
||||
.or()
|
||||
.eq(TriageQueueItem::getStatus, STATUS_SKIPPED))
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
.last("LIMIT 1");
|
||||
|
||||
// 如果指定了科室ID,则按科室过滤;否则查询所有科室(全科模式)
|
||||
if (actualOrgId != null) {
|
||||
nextWrapper.eq(TriageQueueItem::getOrganizationId, actualOrgId);
|
||||
}
|
||||
|
||||
TriageQueueItem next = triageQueueItemService.getOne(nextWrapper, false);
|
||||
|
||||
// 调试日志:检查查询结果
|
||||
System.out.println(">>> [TriageQueue] requeue() 查询等待中的患者:");
|
||||
System.out.println(">>> - 科室ID: " + actualOrgId);
|
||||
System.out.println(">>> - 找到的等待患者: " + (next != null ? next.getPatientName() + " (状态: " + next.getStatus() + ")" : "null"));
|
||||
|
||||
// 如果找不到等待中的患者,直接返回失败(不执行跳过操作)
|
||||
if (next == null) {
|
||||
System.out.println(">>> [TriageQueue] requeue() 失败:没有等待中的患者");
|
||||
return R.fail("当前没有等待中的患者");
|
||||
}
|
||||
|
||||
// 找末尾序号(同一科室,不限制日期)
|
||||
Integer maxOrder = triageQueueItemService.list(new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getOrganizationId, actualOrgId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.ne(TriageQueueItem::getStatus, STATUS_COMPLETED))
|
||||
.stream()
|
||||
.map(TriageQueueItem::getQueueOrder)
|
||||
.filter(Objects::nonNull)
|
||||
.max(Integer::compareTo)
|
||||
.orElse(0);
|
||||
|
||||
// 1) 叫号中 -> 跳过,并移到末尾
|
||||
calling.setStatus(STATUS_SKIPPED).setQueueOrder(maxOrder + 1).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(calling);
|
||||
|
||||
// 2) 自动推进下一个等待为叫号中
|
||||
next.setStatus(STATUS_CALLING).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(next);
|
||||
|
||||
recalcOrders(actualOrgId, null);
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> skip(TriageQueueActionReq req) {
|
||||
// 当前业务“跳过”按“过号重排”处理:叫号中 -> 跳过并移到末尾,自动推进下一等待
|
||||
return requeue(req);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> next(TriageQueueActionReq req) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
TriageQueueItem calling = null;
|
||||
|
||||
System.out.println(">>> [TriageQueue] next() 开始执行(不限制日期), tenantId=" + tenantId);
|
||||
|
||||
// 关键改进:如果提供了 id,优先使用 id 直接查找(像 call 方法一样)
|
||||
if (req != null && req.getId() != null) {
|
||||
calling = triageQueueItemService.getById(req.getId());
|
||||
if (calling == null) {
|
||||
return R.fail("队列项不存在");
|
||||
}
|
||||
// 验证状态:必须是"叫号中"状态
|
||||
if (!STATUS_CALLING.equals(calling.getStatus())) {
|
||||
return R.fail("只能对\"叫号中\"状态的患者执行\"下一患者\"操作,当前患者状态为:" + calling.getStatus());
|
||||
}
|
||||
} else {
|
||||
// 如果没有提供 id,通过查询条件查找(兼容旧逻辑)
|
||||
Long orgId = req != null && req.getOrganizationId() != null ? req.getOrganizationId() : null;
|
||||
|
||||
LambdaQueryWrapper<TriageQueueItem> callingWrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.eq(TriageQueueItem::getStatus, STATUS_CALLING)
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
.last("LIMIT 1");
|
||||
|
||||
if (orgId != null) {
|
||||
callingWrapper.eq(TriageQueueItem::getOrganizationId, orgId);
|
||||
}
|
||||
|
||||
calling = triageQueueItemService.getOne(callingWrapper, false);
|
||||
}
|
||||
|
||||
Long actualOrgId = null;
|
||||
|
||||
// 当前叫号中 -> 完成(如果不存在,就当作从头找第一位等待)
|
||||
if (calling != null) {
|
||||
calling.setStatus(STATUS_COMPLETED).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(calling);
|
||||
actualOrgId = calling.getOrganizationId(); // 使用叫号中患者所在的科室
|
||||
} else {
|
||||
// 如果没有叫号中的患者,使用请求中的 organizationId(如果有)
|
||||
if (req != null && req.getOrganizationId() != null) {
|
||||
actualOrgId = req.getOrganizationId();
|
||||
}
|
||||
}
|
||||
|
||||
// 下一位等待 -> 叫号中(如果之前有叫号中的,就在同一科室找;否则在全科找)
|
||||
// 注意:也包含"跳过"状态的患者,因为跳过后的患者也可以重新叫号(不限制日期)
|
||||
LambdaQueryWrapper<TriageQueueItem> nextWrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.and(w -> w.eq(TriageQueueItem::getStatus, STATUS_WAITING)
|
||||
.or()
|
||||
.eq(TriageQueueItem::getStatus, STATUS_SKIPPED))
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
.last("LIMIT 1");
|
||||
|
||||
if (actualOrgId != null) {
|
||||
nextWrapper.eq(TriageQueueItem::getOrganizationId, actualOrgId);
|
||||
}
|
||||
|
||||
TriageQueueItem next = triageQueueItemService.getOne(nextWrapper, false);
|
||||
|
||||
// 调试日志:打印查询条件和结果
|
||||
System.out.println(">>> [TriageQueue] next() 查询条件(不限制日期): tenantId=" + tenantId
|
||||
+ ", actualOrgId=" + actualOrgId
|
||||
+ ", deleteFlag=0"
|
||||
+ ", status IN (WAITING, SKIPPED)");
|
||||
System.out.println(">>> [TriageQueue] next() 查询结果: calling=" + (calling != null ? calling.getPatientName() + "(status=" + calling.getStatus() + ", queueDate=" + calling.getQueueDate() + ")" : "null")
|
||||
+ ", next=" + (next != null ? next.getPatientName() + "(status=" + next.getStatus() + ", queueDate=" + next.getQueueDate() + ")" : "null"));
|
||||
|
||||
if (next == null) {
|
||||
if (actualOrgId != null) {
|
||||
recalcOrders(actualOrgId, null);
|
||||
}
|
||||
return R.fail("当前没有等待的患者");
|
||||
}
|
||||
|
||||
next.setStatus(STATUS_CALLING).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(next);
|
||||
|
||||
if (next.getOrganizationId() != null) {
|
||||
recalcOrders(next.getOrganizationId(), null);
|
||||
}
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
private List<TriageQueueItem> listInternal(Long orgId, LocalDate qd) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
LambdaQueryWrapper<TriageQueueItem> wrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getOrganizationId, orgId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.ne(TriageQueueItem::getStatus, STATUS_COMPLETED);
|
||||
// 如果 qd 不为 null,才添加日期限制
|
||||
if (qd != null) {
|
||||
wrapper.eq(TriageQueueItem::getQueueDate, qd);
|
||||
}
|
||||
return triageQueueItemService.list(wrapper);
|
||||
}
|
||||
|
||||
private TriageQueueItem findCalling(Long orgId, LocalDate qd) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
return triageQueueItemService.getOne(new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getOrganizationId, orgId)
|
||||
.eq(TriageQueueItem::getQueueDate, qd)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.eq(TriageQueueItem::getStatus, STATUS_CALLING)
|
||||
.last("LIMIT 1"), false);
|
||||
}
|
||||
|
||||
private void recalcOrders(Long orgId, LocalDate qd) {
|
||||
List<TriageQueueItem> list = listInternal(orgId, qd);
|
||||
list.sort(Comparator.comparing(TriageQueueItem::getQueueOrder).thenComparing(TriageQueueItem::getId));
|
||||
int i = 1;
|
||||
for (TriageQueueItem it : list) {
|
||||
if (!Objects.equals(it.getQueueOrder(), i)) {
|
||||
it.setQueueOrder(i).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(it);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.openhis.web.triageandqueuemanage.controller;
|
||||
|
||||
import com.core.common.core.domain.R;
|
||||
import com.openhis.web.triageandqueuemanage.appservice.TriageQueueAppService;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueActionReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAddReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAdjustReq;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDate;
|
||||
|
||||
@RestController
|
||||
@Slf4j
|
||||
@RequestMapping("/triage/queue")
|
||||
public class TriageQueueController {
|
||||
|
||||
@Resource
|
||||
private TriageQueueAppService triageQueueAppService;
|
||||
|
||||
@GetMapping("/list")
|
||||
public R<?> list(@RequestParam(value = "organizationId", required = false) Long organizationId,
|
||||
@RequestParam(value = "date", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) {
|
||||
return triageQueueAppService.list(organizationId, date);
|
||||
}
|
||||
|
||||
@PostMapping("/add")
|
||||
public R<?> add(@RequestBody TriageQueueAddReq req) {
|
||||
return triageQueueAppService.add(req);
|
||||
}
|
||||
|
||||
@DeleteMapping("/remove/{id}")
|
||||
public R<?> remove(@PathVariable("id") Long id) {
|
||||
return triageQueueAppService.remove(id);
|
||||
}
|
||||
|
||||
@PutMapping("/adjust")
|
||||
public R<?> adjust(@RequestBody TriageQueueAdjustReq req) {
|
||||
return triageQueueAppService.adjust(req);
|
||||
}
|
||||
|
||||
@PostMapping("/call")
|
||||
public R<?> call(@RequestBody TriageQueueActionReq req) {
|
||||
return triageQueueAppService.call(req);
|
||||
}
|
||||
|
||||
@PostMapping("/complete")
|
||||
public R<?> complete(@RequestBody(required = false) TriageQueueActionReq req) {
|
||||
return triageQueueAppService.complete(req);
|
||||
}
|
||||
|
||||
@PostMapping("/requeue")
|
||||
public R<?> requeue(@RequestBody(required = false) TriageQueueActionReq req) {
|
||||
return triageQueueAppService.requeue(req);
|
||||
}
|
||||
|
||||
@PostMapping("/skip")
|
||||
public R<?> skip(@RequestBody(required = false) TriageQueueActionReq req) {
|
||||
return triageQueueAppService.skip(req);
|
||||
}
|
||||
|
||||
@PostMapping("/next")
|
||||
public R<?> next(@RequestBody(required = false) TriageQueueActionReq req) {
|
||||
return triageQueueAppService.next(req);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.openhis.web.triageandqueuemanage.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TriageQueueActionReq {
|
||||
/** 目标队列项ID(例如:选呼时选中的患者) */
|
||||
private Long id;
|
||||
/** 科室ID(可选:不传则用当前登录人orgId) */
|
||||
private Long organizationId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.openhis.web.triageandqueuemanage.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class TriageQueueAddReq {
|
||||
/** 科室ID(就诊科室) */
|
||||
private Long organizationId;
|
||||
/** 科室名称(冗余存储,便于展示) */
|
||||
private String organizationName;
|
||||
/** 要加入队列的就诊记录 */
|
||||
private List<TriageQueueEncounterItem> items;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.openhis.web.triageandqueuemanage.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TriageQueueAdjustReq {
|
||||
private Long id;
|
||||
/** up / down */
|
||||
private String direction;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.openhis.web.triageandqueuemanage.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TriageQueueEncounterItem {
|
||||
private Long encounterId;
|
||||
private Long patientId;
|
||||
private String patientName;
|
||||
private String healthcareName;
|
||||
private String practitionerName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
T3.total_volume,
|
||||
T5."name" AS practitioner_name,
|
||||
T6."name" AS purpose_location_name,
|
||||
T6."id" AS purpose_location_id,
|
||||
T1.purpose_location_id AS purpose_location_id,
|
||||
T7."name" AS purpose_location_store_name,
|
||||
T1.occurrence_time,
|
||||
(SELECT SUM(T9.quantity)
|
||||
@@ -119,7 +119,7 @@
|
||||
ON T1.supplier_id = T10.id
|
||||
AND T10.delete_flag = '0'
|
||||
LEFT JOIN sys_dict_data sdt
|
||||
ON sdt.dict_value = T1.unit_code AND dict_type = 'unit_code'
|
||||
ON sdt.dict_value = T1.unit_code AND sdt.dict_type = 'unit_code'
|
||||
WHERE T1.bus_no = #{busNo}
|
||||
AND T1.delete_flag = '0'
|
||||
UNION
|
||||
@@ -145,7 +145,7 @@
|
||||
T8."size" AS total_volume,
|
||||
T5."name" AS practitioner_name,
|
||||
T6."name" AS purpose_location_name,
|
||||
T6."id" AS purpose_location_id,
|
||||
T1.purpose_location_id AS purpose_location_id,
|
||||
T7."name" AS purpose_location_store_name,
|
||||
T1.occurrence_time,
|
||||
(SELECT SUM(T9.quantity)
|
||||
@@ -175,7 +175,7 @@
|
||||
ON T1.supplier_id = T10.id
|
||||
AND T10.delete_flag = '0'
|
||||
LEFT JOIN sys_dict_data sdt
|
||||
ON sdt.dict_value = T1.unit_code AND dict_type = 'unit_code'
|
||||
ON sdt.dict_value = T1.unit_code AND sdt.dict_type = 'unit_code'
|
||||
WHERE T1.bus_no = #{busNo}
|
||||
AND T1.delete_flag = '0'
|
||||
</select>
|
||||
|
||||
@@ -46,6 +46,29 @@
|
||||
<if test="updateTime != null">, #{updateTime}</if>
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!--自定义更新方法-->
|
||||
<update id="updateDoctorSchedule" parameterType="com.openhis.appointmentmanage.domain.DoctorSchedule">
|
||||
UPDATE adm_doctor_schedule
|
||||
<set>
|
||||
<if test="weekday != null">weekday = #{weekday},</if>
|
||||
<if test="timePeriod != null">time_period = #{timePeriod},</if>
|
||||
<if test="doctor != null">doctor = #{doctor},</if>
|
||||
<if test="clinic != null">clinic = #{clinic},</if>
|
||||
<if test="startTime != null">start_time = #{startTime},</if>
|
||||
<if test="endTime != null">end_time = #{endTime},</if>
|
||||
<if test="limitNumber != null">limit_number = #{limitNumber},</if>
|
||||
<if test="callSignRecord != null">call_sign_record = #{callSignRecord},</if>
|
||||
<if test="registerItem != null">register_item = #{registerItem},</if>
|
||||
<if test="registerFee != null">register_fee = #{registerFee},</if>
|
||||
<if test="diagnosisItem != null">diagnosis_item = #{diagnosisItem},</if>
|
||||
<if test="diagnosisFee != null">diagnosis_fee = #{diagnosisFee},</if>
|
||||
<if test="isOnline != null">is_online = #{isOnline},</if>
|
||||
<if test="isStopped != null">is_stopped = #{isStopped},</if>
|
||||
<if test="stopReason != null">stop_reason = #{stopReason},</if>
|
||||
<if test="deptId != null">dept_id = #{deptId},</if>
|
||||
<if test="updateTime != null">update_time = #{updateTime}</if>
|
||||
</set>
|
||||
WHERE id = #{id}
|
||||
</update>
|
||||
</mapper>
|
||||
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
ON T1.context_enum = #{activity}
|
||||
AND T1.product_id = T2.id
|
||||
AND T2.delete_flag = '0'
|
||||
AND T2.status_enum = 1
|
||||
LEFT JOIN med_medication_definition AS T3
|
||||
ON T1.context_enum = #{medication}
|
||||
AND T1.product_id = T3.id
|
||||
@@ -205,6 +206,7 @@
|
||||
ON T1.context_enum = #{activity}
|
||||
AND T1.product_id = T2.id
|
||||
AND T2.delete_flag = '0'
|
||||
AND T2.status_enum = 1
|
||||
LEFT JOIN med_medication_definition AS T3
|
||||
ON T1.context_enum = #{medication}
|
||||
AND T1.product_id = T3.id
|
||||
|
||||
@@ -227,8 +227,11 @@
|
||||
AND T2.instance_table = #{activityTableName}
|
||||
LEFT JOIN adm_organization_location AS T3 ON T3.activity_definition_id = T1.ID
|
||||
AND T3.delete_flag = '0' AND (CURRENT_TIME :: time (6) BETWEEN T3.start_time AND T3.end_time)
|
||||
<if test="organizationId != null">
|
||||
AND T3.organization_id = #{organizationId}
|
||||
</if>
|
||||
WHERE T1.delete_flag = '0'
|
||||
<if test="pricingFlag ==1">
|
||||
<if test="pricingFlag != null and pricingFlag == 1">
|
||||
AND (T1.pricing_flag = #{pricingFlag} OR T1.pricing_flag IS NULL)
|
||||
</if>
|
||||
<if test="adviceDefinitionIdParamList != null and !adviceDefinitionIdParamList.isEmpty()">
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
T10.practitioner_user_id,
|
||||
T10.jz_practitioner_user_id,
|
||||
T10.bus_no,
|
||||
T10.identifier_no
|
||||
T10.identifier_no,
|
||||
T10.missed_time
|
||||
from
|
||||
(
|
||||
SELECT T1.tenant_id AS tenant_id,
|
||||
@@ -50,7 +51,8 @@
|
||||
T1.reception_time AS reception_time,
|
||||
T1.organization_id AS org_id,
|
||||
T8.bus_no AS bus_no,
|
||||
T9.identifier_no AS identifier_no
|
||||
T9.identifier_no AS identifier_no,
|
||||
T1.missed_time AS missed_time
|
||||
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'
|
||||
|
||||
@@ -224,7 +224,7 @@
|
||||
</select>
|
||||
<select id="selectAdmissionPatientInfo"
|
||||
resultType="com.openhis.web.inhospitalnursestation.dto.AdmissionPatientInfoDto">
|
||||
SELECT ae.id AS encounter_id,
|
||||
SELECT DISTINCT ae.id AS encounter_id,
|
||||
ae.bus_no,
|
||||
ae.priority_enum,
|
||||
ae.organization_id,
|
||||
|
||||
@@ -6,6 +6,62 @@
|
||||
<select id="getPage" parameterType="java.util.Map"
|
||||
resultType="com.openhis.web.inpatientmanage.dto.PatientHomeDto">
|
||||
SELECT
|
||||
id,
|
||||
active_flag,
|
||||
temp_flag,
|
||||
patientName,
|
||||
name_json,
|
||||
patientNo,
|
||||
gender_enum,
|
||||
birth_date,
|
||||
deceased_date,
|
||||
marital_status_enum,
|
||||
prfs_enum,
|
||||
phone,
|
||||
address,
|
||||
address_province,
|
||||
address_city,
|
||||
address_district,
|
||||
address_street,
|
||||
address_json,
|
||||
nationality_code,
|
||||
id_card,
|
||||
py_str,
|
||||
wb_str,
|
||||
blood_abo,
|
||||
blood_rh,
|
||||
work_company,
|
||||
native_place,
|
||||
country_code,
|
||||
link_name,
|
||||
link_relation_code,
|
||||
link_telcom,
|
||||
link_jsons,
|
||||
tenant_id,
|
||||
hospitalNo,
|
||||
encounterId,
|
||||
priority_enum,
|
||||
status_enum,
|
||||
organization_id,
|
||||
admissionDate,
|
||||
dischargeDate,
|
||||
class_enum,
|
||||
responsibleDoctor,
|
||||
responsibleNurse,
|
||||
type_code,
|
||||
surgeryStatusEnum,
|
||||
surgeryStartTime,
|
||||
surgeryEndTime,
|
||||
encounterLocationId,
|
||||
location_id,
|
||||
caty,
|
||||
mainDiagnosis
|
||||
FROM (
|
||||
SELECT
|
||||
patient_base.*,
|
||||
ROW_NUMBER() OVER (PARTITION BY patient_base.id ORDER BY patient_base.encounterId) as rn
|
||||
FROM (
|
||||
SELECT DISTINCT
|
||||
T1.id,
|
||||
T1.active_flag,
|
||||
T1.temp_flag,
|
||||
@@ -46,8 +102,24 @@
|
||||
T2.start_time AS admissionDate, -- 入院日期
|
||||
T2.end_time AS dischargeDate, -- 出院日期
|
||||
T2.class_enum, -- 就诊类别
|
||||
Doctor.name AS responsibleDoctor, -- 责任医生
|
||||
Nurse.name AS responsibleNurse, -- 责任护士
|
||||
-- 获取责任医生(使用子查询确保只返回一个值)
|
||||
(SELECT p."name"
|
||||
FROM adm_encounter_participant ep
|
||||
LEFT JOIN adm_practitioner p ON ep.practitioner_id = p.id AND p.delete_flag = '0'
|
||||
WHERE ep.encounter_id = T2.id
|
||||
AND ep.type_code = '9'
|
||||
AND ep.delete_flag = '0'
|
||||
ORDER BY ep.practitioner_id
|
||||
LIMIT 1) AS responsibleDoctor,
|
||||
-- 获取责任护士(使用子查询确保只返回一个值)
|
||||
(SELECT p."name"
|
||||
FROM adm_encounter_participant ep
|
||||
LEFT JOIN adm_practitioner p ON ep.practitioner_id = p.id AND p.delete_flag = '0'
|
||||
WHERE ep.encounter_id = T2.id
|
||||
AND ep.type_code = '2'
|
||||
AND ep.delete_flag = '0'
|
||||
ORDER BY ep.practitioner_id
|
||||
LIMIT 1) AS responsibleNurse,
|
||||
T6.type_code, -- 费别
|
||||
T7.status_enum AS surgeryStatusEnum, -- 手术状态
|
||||
T8.start_time AS surgeryStartTime, -- 手术开始时间
|
||||
@@ -57,50 +129,10 @@
|
||||
T11."name" AS caty, -- 入院科室
|
||||
T12."name" AS mainDiagnosis -- 主要诊断
|
||||
FROM adm_patient AS T1
|
||||
LEFT JOIN adm_encounter T2
|
||||
INNER JOIN adm_encounter T2 -- 改为INNER JOIN确保患者有就诊记录
|
||||
ON T1.id = T2.patient_id -- 患者ID
|
||||
AND T2.delete_flag = '0'
|
||||
AND T2.class_enum = '1' -- 仅选择住院患者
|
||||
-- 获取责任医生
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
encounter_id,
|
||||
practitioner_id,
|
||||
"name",
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY practitioner_id) as rn
|
||||
FROM (
|
||||
SELECT
|
||||
T3.encounter_id,
|
||||
T4."name",
|
||||
T3.practitioner_id
|
||||
FROM adm_encounter_participant T3
|
||||
LEFT JOIN adm_practitioner T4
|
||||
ON T3.practitioner_id = T4.id
|
||||
AND T4.delete_flag = '0'
|
||||
WHERE T3.type_code = '9' -- 责任医生类型
|
||||
AND T3.delete_flag = '0'
|
||||
) sub
|
||||
) Doctor ON T2.id = Doctor.encounter_id AND Doctor.rn = 1
|
||||
-- 获取责任护士
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
encounter_id,
|
||||
practitioner_id,
|
||||
"name",
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY practitioner_id) as rn
|
||||
FROM (
|
||||
SELECT
|
||||
T3.encounter_id,
|
||||
T4."name",
|
||||
T3.practitioner_id
|
||||
FROM adm_encounter_participant T3
|
||||
LEFT JOIN adm_practitioner T4
|
||||
ON T3.practitioner_id = T4.id
|
||||
AND T4.delete_flag = '0'
|
||||
WHERE T3.type_code = '2' -- 责任护士类型
|
||||
AND T3.delete_flag = '0'
|
||||
) sub
|
||||
) Nurse ON T2.id = Nurse.encounter_id AND Nurse.rn = 1
|
||||
LEFT JOIN adm_encounter_diagnosis T5
|
||||
ON T2.id = T5.encounter_id -- 就诊ID
|
||||
AND T5.maindise_flag = 1
|
||||
@@ -108,12 +140,25 @@
|
||||
LEFT JOIN adm_account T6
|
||||
ON T2.id = T6.encounter_id -- 就诊ID
|
||||
AND T6.delete_flag = '0'
|
||||
LEFT JOIN cli_procedure T7
|
||||
ON T1.id = T7.patient_id -- 患者ID
|
||||
AND T7.delete_flag = '0'
|
||||
LEFT JOIN cli_procedure_performer T8
|
||||
ON T7.id = T8.procedure_id -- 手术ID
|
||||
AND T8.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
patient_id,
|
||||
status_enum,
|
||||
id,
|
||||
request_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY id DESC) as rn
|
||||
FROM cli_procedure
|
||||
WHERE delete_flag = '0'
|
||||
) T7 ON T1.id = T7.patient_id AND T7.rn = 1
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
procedure_id,
|
||||
start_time,
|
||||
end_time,
|
||||
ROW_NUMBER() OVER (PARTITION BY procedure_id ORDER BY id DESC) as rn
|
||||
FROM cli_procedure_performer
|
||||
WHERE delete_flag = '0'
|
||||
) T8 ON T7.id = T8.procedure_id AND T8.rn = 1
|
||||
LEFT JOIN adm_encounter_location T9
|
||||
ON T2.id = T9.encounter_id -- 就诊ID
|
||||
AND T9.form_enum = 8
|
||||
@@ -125,16 +170,7 @@
|
||||
LEFT JOIN cli_condition_definition T12
|
||||
ON T5.condition_id = T12.id
|
||||
AND T12.delete_flag = '0'
|
||||
LEFT JOIN wor_service_request T13
|
||||
ON T2.id = T13.encounter_id
|
||||
AND T7.base_service_req_id = T13.id
|
||||
AND T13.delete_flag = '0'
|
||||
LEFT JOIN wor_service_request_detail T14
|
||||
ON T13.id = T14.service_req_id
|
||||
AND T14.type_code = '3'
|
||||
AND T14.delete_flag = '0'
|
||||
<where>
|
||||
T1.delete_flag = '0'
|
||||
WHERE T1.delete_flag = '0'
|
||||
-- 患者ID
|
||||
<if test="patientId != null">
|
||||
AND T1.id = #{patientId}
|
||||
@@ -157,7 +193,15 @@
|
||||
|
||||
-- 手术
|
||||
<if test="statusEnum == 5">
|
||||
AND T14.type_code = '3'
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM wor_service_request_detail wsrd
|
||||
INNER JOIN wor_service_request wsr ON wsrd.service_req_id = wsr.id
|
||||
WHERE wsr.encounter_id = T2.id
|
||||
AND wsrd.type_code = '3'
|
||||
AND wsr.delete_flag = '0'
|
||||
AND wsrd.delete_flag = '0'
|
||||
)
|
||||
</if>
|
||||
|
||||
-- 欠费
|
||||
@@ -176,15 +220,32 @@
|
||||
T2.bus_no = #{searchKey}
|
||||
-- 患者姓名
|
||||
OR T1.name like concat('%', #{searchKey}, '%')
|
||||
-- 责任医生
|
||||
OR Doctor.name like concat('%', #{searchKey}, '%')
|
||||
-- 责任护士
|
||||
OR Nurse.name like concat('%', #{searchKey}, '%')
|
||||
-- 责任医生(在子查询中处理)
|
||||
OR EXISTS (
|
||||
SELECT 1
|
||||
FROM adm_encounter_participant ep
|
||||
LEFT JOIN adm_practitioner p ON ep.practitioner_id = p.id AND p.delete_flag = '0'
|
||||
WHERE ep.encounter_id = T2.id
|
||||
AND ep.type_code = '9'
|
||||
AND ep.delete_flag = '0'
|
||||
AND p."name" like concat('%', #{searchKey}, '%')
|
||||
)
|
||||
-- 责任护士(在子查询中处理)
|
||||
OR EXISTS (
|
||||
SELECT 1
|
||||
FROM adm_encounter_participant ep
|
||||
LEFT JOIN adm_practitioner p ON ep.practitioner_id = p.id AND p.delete_flag = '0'
|
||||
WHERE ep.encounter_id = T2.id
|
||||
AND ep.type_code = '2'
|
||||
AND ep.delete_flag = '0'
|
||||
AND p."name" like concat('%', #{searchKey}, '%')
|
||||
)
|
||||
)
|
||||
</if>
|
||||
</where>
|
||||
|
||||
ORDER BY T9.location_id ASC
|
||||
) patient_base
|
||||
) ranked_result
|
||||
WHERE rn = 1
|
||||
ORDER BY location_id ASC
|
||||
</select>
|
||||
|
||||
<!-- 科室空床查询-->
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
AND T6.delete_flag = '0'
|
||||
LEFT JOIN wor_service_request T7
|
||||
ON T1.encounter_id = T7.encounter_id
|
||||
AND T5.base_service_req_id = T7.id
|
||||
AND T5.request_id = T7.id
|
||||
AND T7.delete_flag = '0'
|
||||
LEFT JOIN wor_service_request_detail T8
|
||||
ON T7.id = T8.service_req_id
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
<mapper namespace="com.openhis.web.regdoctorstation.mapper.AdviceManageAppMapper">
|
||||
|
||||
<select id="getRegPatientMainInfo" resultType="com.openhis.web.regdoctorstation.dto.RegPatientMainInfoDto">
|
||||
SELECT rpmi.tenant_id,
|
||||
SELECT DISTINCT ON (rpmi.patient_id)
|
||||
rpmi.tenant_id,
|
||||
rpmi.encounter_id,
|
||||
rpmi.status_enum,
|
||||
rpmi.bus_no,
|
||||
@@ -26,7 +27,7 @@
|
||||
rpmi.contract_name,
|
||||
rpmi.reg_diagnosis_name,
|
||||
rpmi.account_id
|
||||
from (SELECT ae.tenant_id,
|
||||
FROM (SELECT ae.tenant_id,
|
||||
ae.ID AS encounter_id,
|
||||
ae.status_enum AS status_enum,
|
||||
ae.bus_no AS bus_no,
|
||||
@@ -94,7 +95,90 @@
|
||||
AND practitioner_id = #{practitionerId}
|
||||
AND org_id = ae.organization_id
|
||||
)) AS rpmi
|
||||
${ew.customSqlSegment}
|
||||
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
|
||||
WHERE ${ew.sqlSegment}
|
||||
</if>
|
||||
ORDER BY rpmi.patient_id, rpmi.encounter_id DESC
|
||||
</select>
|
||||
|
||||
<!-- 用于分页的COUNT查询,与主查询保持一致的去重逻辑 -->
|
||||
<select id="getRegPatientMainInfoCount" resultType="java.lang.Long">
|
||||
SELECT COUNT(*) FROM (
|
||||
SELECT DISTINCT ON (rpmi.patient_id)
|
||||
rpmi.patient_id
|
||||
FROM (SELECT ae.tenant_id,
|
||||
ae.ID AS encounter_id,
|
||||
ae.status_enum AS status_enum,
|
||||
ae.bus_no AS bus_no,
|
||||
ae.start_time AS in_hospital_time,
|
||||
(EXTRACT(DAY FROM (CURRENT_DATE - ae.start_time)) :: INTEGER + 1) AS in_hospital_days,
|
||||
ae.end_time AS out_hospital_time,
|
||||
ap.ID AS patient_id,
|
||||
ap.NAME AS patient_name,
|
||||
ap.gender_enum AS gender_enum,
|
||||
ap.birth_date AS birth_date,
|
||||
alw.NAME AS ward_name,
|
||||
alh.NAME AS house_name,
|
||||
alb.NAME AS bed_name,
|
||||
aelb.start_time AS in_org_time,
|
||||
ao.id AS in_hospital_org_id,
|
||||
ao.NAME AS in_hospital_org_name,
|
||||
aa.id AS account_id,
|
||||
aa.contract_no AS contract_no,
|
||||
fc.contract_name AS contract_name,
|
||||
ccd.NAME AS reg_diagnosis_name
|
||||
FROM adm_encounter AS ae
|
||||
LEFT JOIN adm_patient AS ap ON ap.ID = ae.patient_id
|
||||
AND ap.delete_flag = '0'
|
||||
LEFT JOIN adm_organization AS ao ON ao.ID = ae.organization_id
|
||||
AND ao.delete_flag = '0'
|
||||
LEFT JOIN adm_encounter_location AS aelw ON aelw.encounter_id = ae.ID
|
||||
AND aelw.delete_flag = '0'
|
||||
AND aelw.form_enum = #{wardEnum}
|
||||
AND aelw.status_enum = #{encounterActivityStatus}
|
||||
LEFT JOIN adm_location AS alw ON alw.ID = aelw.location_id
|
||||
AND alw.delete_flag = '0'
|
||||
LEFT JOIN adm_encounter_location AS aelh ON aelh.encounter_id = ae.ID
|
||||
AND aelh.delete_flag = '0'
|
||||
AND aelh.form_enum = #{houseEnum}
|
||||
AND aelh.status_enum = #{encounterActivityStatus}
|
||||
LEFT JOIN adm_location AS alh ON alh.ID = aelh.location_id
|
||||
AND alh.delete_flag = '0'
|
||||
LEFT JOIN adm_encounter_location AS aelb ON aelb.encounter_id = ae.ID
|
||||
AND aelb.delete_flag = '0'
|
||||
AND aelb.form_enum = #{bedEnum}
|
||||
AND aelb.status_enum = #{encounterActivityStatus}
|
||||
LEFT JOIN adm_location AS alb ON alb.ID = aelb.location_id
|
||||
AND alb.delete_flag = '0'
|
||||
INNER JOIN adm_account AS aa ON aa.encounter_id = ae.ID
|
||||
AND aa.delete_flag = '0'
|
||||
AND aa.encounter_flag = 1
|
||||
LEFT JOIN fin_contract AS fc ON fc.bus_no = aa.contract_no
|
||||
AND fc.delete_flag = '0'
|
||||
LEFT JOIN adm_encounter_diagnosis AS aed ON aed.encounter_id = ae.ID
|
||||
AND aed.delete_flag = '0'
|
||||
AND aed.maindise_flag = #{maindiseFlag}
|
||||
LEFT JOIN cli_condition AS cc ON cc.ID = aed.condition_id
|
||||
AND cc.delete_flag = '0'
|
||||
LEFT JOIN cli_condition_definition AS ccd ON ccd.ID = cc.definition_id
|
||||
AND ccd.delete_flag = '0'
|
||||
WHERE ae.delete_flag = '0'
|
||||
AND ae.class_enum = #{classEnum}
|
||||
AND ae.status_enum != #{toBeRegistered}
|
||||
AND ae.status_enum != #{alreadySettled}
|
||||
AND ae.status_enum != #{registered}
|
||||
AND EXISTS(
|
||||
SELECT 1
|
||||
FROM adm_practitioner_role
|
||||
WHERE delete_flag = '0'
|
||||
AND practitioner_id = #{practitionerId}
|
||||
AND org_id = ae.organization_id
|
||||
)) AS rpmi
|
||||
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
|
||||
WHERE ${ew.sqlSegment}
|
||||
</if>
|
||||
ORDER BY rpmi.patient_id, rpmi.encounter_id DESC
|
||||
) AS distinct_patients
|
||||
</select>
|
||||
|
||||
<select id="getRegRequestBaseInfo" resultType="com.openhis.web.regdoctorstation.dto.RegRequestBaseDto">
|
||||
@@ -275,7 +359,9 @@
|
||||
WHERE aa.type_code = #{personalCashAccount}
|
||||
AND aa.delete_flag = '0'
|
||||
GROUP BY aa.tenant_id, aa.id, aa.insutype, aa.encounter_id, aa.balance_amount) AS final_res
|
||||
${ew.customSqlSegment}
|
||||
<if test="ew != null and ew.sqlSegment != null and ew.sqlSegment != ''">
|
||||
WHERE ${ew.sqlSegment}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -116,11 +116,16 @@ public class DictAspect {
|
||||
}
|
||||
|
||||
private String queryDictLabel(String dictTable, String dictCode, String dictText, String dictValue) {
|
||||
if (StringUtils.hasText(dictTable)) {
|
||||
// 场景 1:默认字典走DictUtils缓存
|
||||
if (!StringUtils.hasText(dictTable)) {
|
||||
// 场景 1:默认字典走DictUtils缓存(dictTable 为空时)
|
||||
return DictUtils.getDictLabel(dictCode, dictValue);
|
||||
} else {
|
||||
// 场景 2:查询指定表
|
||||
// 场景 2:查询指定表(dictTable 有值时)
|
||||
// 必须同时有 dictTable 和 dictText 才能执行 SQL 查询
|
||||
if (!StringUtils.hasText(dictText)) {
|
||||
// 如果 dictText 为空,回退到字典缓存查询
|
||||
return DictUtils.getDictLabel(dictCode, dictValue);
|
||||
}
|
||||
String sql = String.format("SELECT %s FROM %s WHERE %s::varchar = ? LIMIT 1", dictText, dictTable, dictCode);
|
||||
try {
|
||||
return jdbcTemplate.queryForObject(sql, String.class, dictValue);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package com.openhis.administration.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.core.common.core.domain.HisBaseEntity;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
@@ -145,4 +143,9 @@ public class Encounter extends HisBaseEntity {
|
||||
*/
|
||||
private Long registrarId;
|
||||
|
||||
/**
|
||||
* 过号时间
|
||||
*/
|
||||
@TableField("missed_time")
|
||||
private Date missedTime;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.openhis.administration.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.core.common.core.domain.HisBaseEntity;
|
||||
@@ -43,4 +44,9 @@ public class EncounterParticipant extends HisBaseEntity {
|
||||
|
||||
/** 状态 */
|
||||
private Integer statusEnum;
|
||||
/**
|
||||
* 租户ID(新增字段,和数据库的tenant_id对应)
|
||||
*/
|
||||
@TableField("tenant_id") // 显式映射数据库字段名,避免MyBatis-Plus自动转换出错
|
||||
private Integer tenantId;
|
||||
}
|
||||
@@ -10,4 +10,8 @@ public interface DoctorScheduleMapper extends BaseMapper<DoctorSchedule> {
|
||||
* 自定义插入方法,排除id字段(数据库GENERATED ALWAYS)
|
||||
*/
|
||||
int insertWithoutId(DoctorSchedule doctorSchedule);
|
||||
/**
|
||||
* 自定义更新方法
|
||||
*/
|
||||
int updateDoctorSchedule(DoctorSchedule doctorSchedule);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public interface OrderMapper extends BaseMapper<Order> {
|
||||
|
||||
int countOrders(Map<String, Object> params);
|
||||
|
||||
Order selectOrderBySlotId(Long slotId);
|
||||
List<Order> selectOrderBySlotId(Long slotId);
|
||||
|
||||
int updateOrderStatusById(Long id, Integer status);
|
||||
|
||||
|
||||
@@ -25,8 +25,7 @@ public interface IOrderService extends IService<Order> {
|
||||
|
||||
int countOrders(Map<String, Object> params);
|
||||
|
||||
Order selectOrderBySlotId(Long slotId);
|
||||
|
||||
List<Order> selectOrderBySlotId(Long slotId);
|
||||
int updateOrderStatusById(Long id, Integer status);
|
||||
|
||||
int updateOrderCancelInfoById(Long id, java.util.Date cancelTime, String cancelReason);
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.openhis.clinical.service.impl;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.core.common.utils.AssignSeqUtil;
|
||||
import com.core.common.utils.SecurityUtils;
|
||||
import com.openhis.clinical.domain.Order;
|
||||
import com.openhis.clinical.domain.Ticket;
|
||||
import com.openhis.clinical.mapper.OrderMapper;
|
||||
@@ -71,7 +70,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public Order selectOrderBySlotId(Long slotId) {
|
||||
public List<Order> selectOrderBySlotId(Long slotId) {
|
||||
return orderMapper.selectOrderBySlotId(slotId);
|
||||
}
|
||||
|
||||
|
||||
@@ -197,8 +197,8 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
||||
throw new RuntimeException("号源不可取消预约");
|
||||
}
|
||||
|
||||
Order order = orderService.selectOrderBySlotId(ticketId);
|
||||
if (order != null) {
|
||||
List<Order> orders = orderService.selectOrderBySlotId(ticketId);
|
||||
for(Order order:orders){
|
||||
orderService.cancelAppointmentOrder(order.getId(), "患者取消预约");
|
||||
}
|
||||
|
||||
@@ -249,8 +249,8 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
||||
}
|
||||
|
||||
// 检查是否存在相关订单,如果存在则取消
|
||||
Order order = orderService.selectOrderBySlotId(ticketId);
|
||||
if (order != null) {
|
||||
List<Order> orders = orderService.selectOrderBySlotId(ticketId);
|
||||
for(Order order:orders){
|
||||
orderService.cancelAppointmentOrder(order.getId(), "医生停诊");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.openhis.triageandqueuemanage.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName(value = "hisdev.triage_candidate_exclusion")
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class TriageCandidateExclusion {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private Integer tenantId;
|
||||
private LocalDate exclusionDate;
|
||||
private Long encounterId;
|
||||
private Long patientId;
|
||||
private String patientName;
|
||||
private Long organizationId;
|
||||
private String organizationName;
|
||||
|
||||
/** 排除原因:ADDED_TO_QUEUE(加入队列)、MANUAL_REMOVE(手动移除)等 */
|
||||
private String reason;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
private String deleteFlag;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.openhis.triageandqueuemanage.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName(value = "hisdev.triage_queue_item")
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class TriageQueueItem {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private Integer tenantId;
|
||||
private LocalDate queueDate;
|
||||
private Long organizationId;
|
||||
private String organizationName;
|
||||
|
||||
private Long encounterId;
|
||||
private Long patientId;
|
||||
private String patientName;
|
||||
|
||||
private String healthcareName;
|
||||
private String practitionerName;
|
||||
|
||||
/** WAITING / CALLING / SKIPPED / COMPLETED */
|
||||
private String status;
|
||||
private Integer queueOrder;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
private String deleteFlag;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.openhis.triageandqueuemanage.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageCandidateExclusion;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface TriageCandidateExclusionMapper extends BaseMapper<TriageCandidateExclusion> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.openhis.triageandqueuemanage.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageQueueItem;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface TriageQueueItemMapper extends BaseMapper<TriageQueueItem> {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.openhis.triageandqueuemanage.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageCandidateExclusion;
|
||||
|
||||
public interface TriageCandidateExclusionService extends IService<TriageCandidateExclusion> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.openhis.triageandqueuemanage.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageQueueItem;
|
||||
|
||||
public interface TriageQueueItemService extends IService<TriageQueueItem> {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.openhis.triageandqueuemanage.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageCandidateExclusion;
|
||||
import com.openhis.triageandqueuemanage.mapper.TriageCandidateExclusionMapper;
|
||||
import com.openhis.triageandqueuemanage.service.TriageCandidateExclusionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class TriageCandidateExclusionServiceImpl extends ServiceImpl<TriageCandidateExclusionMapper, TriageCandidateExclusion> implements TriageCandidateExclusionService {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.openhis.triageandqueuemanage.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageQueueItem;
|
||||
import com.openhis.triageandqueuemanage.mapper.TriageQueueItemMapper;
|
||||
import com.openhis.triageandqueuemanage.service.TriageQueueItemService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class TriageQueueItemServiceImpl extends ServiceImpl<TriageQueueItemMapper, TriageQueueItem> implements TriageQueueItemService {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -119,6 +119,8 @@
|
||||
|
||||
<select id="selectOrderById" resultMap="OrderResult">
|
||||
select * from order_main where id = #{id}
|
||||
and status = 1
|
||||
order by create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectOrderBySlotId" resultMap="OrderResult">
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
-- 智能分诊排队:候选池排除记录表
|
||||
-- 用途:记录已从智能候选池中移除的患者(加入队列后不再出现在候选池)
|
||||
-- 注意:必须在后端实际连接的 schema 中执行(dev环境是 hisdev,test环境是 histest,prd环境是 hisprd)
|
||||
-- 执行前请先确认:SET search_path TO hisdev; (或对应的 schema)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS hisdev.triage_candidate_exclusion (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id INTEGER NOT NULL,
|
||||
exclusion_date DATE NOT NULL,
|
||||
encounter_id BIGINT NOT NULL,
|
||||
patient_id BIGINT,
|
||||
patient_name VARCHAR(255),
|
||||
organization_id BIGINT,
|
||||
organization_name VARCHAR(255),
|
||||
reason VARCHAR(100) DEFAULT 'ADDED_TO_QUEUE', -- 排除原因:ADDED_TO_QUEUE(加入队列)、MANUAL_REMOVE(手动移除)等
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
delete_flag CHAR(1) NOT NULL DEFAULT '0'
|
||||
);
|
||||
|
||||
-- 常用查询索引:按租户/日期/就诊记录查询
|
||||
CREATE INDEX IF NOT EXISTS idx_triage_candidate_exclusion_list
|
||||
ON hisdev.triage_candidate_exclusion (tenant_id, exclusion_date, delete_flag, encounter_id);
|
||||
|
||||
-- 防重复:同一天同租户同就诊记录只能有一条排除记录(未删除)
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_triage_candidate_exclusion_encounter
|
||||
ON hisdev.triage_candidate_exclusion (tenant_id, exclusion_date, encounter_id, delete_flag)
|
||||
WHERE delete_flag = '0';
|
||||
|
||||
-- 就诊记录ID索引(用于关联查询)
|
||||
CREATE INDEX IF NOT EXISTS idx_triage_candidate_exclusion_encounter_id
|
||||
ON hisdev.triage_candidate_exclusion (encounter_id);
|
||||
|
||||
-- 注释
|
||||
COMMENT ON TABLE hisdev.triage_candidate_exclusion IS '智能分诊排队候选池排除记录表';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.tenant_id IS '租户ID';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.exclusion_date IS '排除日期(通常为当天)';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.encounter_id IS '就诊记录ID(唯一标识)';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.patient_id IS '患者ID';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.patient_name IS '患者姓名';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.organization_id IS '科室ID';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.organization_name IS '科室名称';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.reason IS '排除原因:ADDED_TO_QUEUE(加入队列)、MANUAL_REMOVE(手动移除)等';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.delete_flag IS '删除标志:0-未删除,1-已删除';
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
-- 智能分诊排队:队列持久化表(与 TriageQueueItem.java 字段一致)
|
||||
-- 注意:必须在后端实际连接的 schema 中执行(dev环境是 hisdev,test环境是 histest,prd环境是 hisprd)
|
||||
-- 执行前请先确认:SET search_path TO hisdev; (或对应的 schema)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS triage_queue_item (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id INTEGER NOT NULL,
|
||||
queue_date DATE NOT NULL,
|
||||
organization_id BIGINT NOT NULL,
|
||||
organization_name VARCHAR(255),
|
||||
encounter_id BIGINT NOT NULL,
|
||||
patient_id BIGINT,
|
||||
patient_name VARCHAR(255),
|
||||
healthcare_name VARCHAR(255),
|
||||
practitioner_name VARCHAR(255),
|
||||
status VARCHAR(50) NOT NULL, -- WAITING/CALLING/SKIPPED/COMPLETED
|
||||
queue_order INTEGER NOT NULL,
|
||||
create_time TIMESTAMP,
|
||||
update_time TIMESTAMP,
|
||||
delete_flag CHAR(1) NOT NULL DEFAULT '0'
|
||||
);
|
||||
|
||||
-- 常用查询索引:按租户/科室/日期/状态/顺序取队列
|
||||
CREATE INDEX IF NOT EXISTS idx_triage_queue_item_list
|
||||
ON triage_queue_item (tenant_id, queue_date, organization_id, delete_flag, status, queue_order);
|
||||
|
||||
-- 防重复:同一天同租户同科室同就诊记录只能在队列里一条(未删除)
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_triage_queue_item_encounter
|
||||
ON triage_queue_item (tenant_id, queue_date, organization_id, encounter_id, delete_flag)
|
||||
WHERE delete_flag = '0';
|
||||
|
||||
-- 就诊记录ID索引(用于关联查询)
|
||||
CREATE INDEX IF NOT EXISTS idx_triage_queue_item_encounter_id
|
||||
ON triage_queue_item (encounter_id);
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
-- ============================================
|
||||
-- 检查门诊划价"诊疗判断"下没有数据的原因
|
||||
-- ============================================
|
||||
|
||||
-- 1. 检查是否有诊疗类型的收费项目(context_enum = 3)
|
||||
SELECT
|
||||
COUNT(*) AS total_activity_charge_items,
|
||||
COUNT(CASE WHEN delete_flag = '0' THEN 1 END) AS active_charge_items
|
||||
FROM adm_charge_item
|
||||
WHERE context_enum = 3; -- ACTIVITY = 3
|
||||
|
||||
-- 2. 检查诊疗类型的收费项目是否能关联到 wor_activity_definition
|
||||
SELECT
|
||||
T1.id AS charge_item_id,
|
||||
T1.encounter_id,
|
||||
T1.context_enum,
|
||||
T1.product_id,
|
||||
T1.status_enum AS charge_status,
|
||||
T2.id AS activity_def_id,
|
||||
T2.name AS activity_name,
|
||||
T2.status_enum AS activity_status,
|
||||
T2.delete_flag AS activity_delete_flag,
|
||||
CASE
|
||||
WHEN T2.id IS NULL THEN '❌ 无法关联到诊疗项目定义'
|
||||
WHEN T2.delete_flag != '0' THEN '❌ 诊疗项目定义已删除'
|
||||
WHEN T2.status_enum != 1 THEN '❌ 诊疗项目定义未激活(status_enum != 1)'
|
||||
ELSE '✅ 正常'
|
||||
END AS match_status
|
||||
FROM adm_charge_item AS T1
|
||||
LEFT JOIN wor_activity_definition AS T2
|
||||
ON T1.context_enum = 3
|
||||
AND T1.product_id = T2.id
|
||||
AND T2.delete_flag = '0'
|
||||
AND T2.status_enum = 1 -- 当前查询条件
|
||||
WHERE T1.context_enum = 3
|
||||
AND T1.delete_flag = '0'
|
||||
LIMIT 20;
|
||||
|
||||
-- 3. 检查如果移除 status_enum = 1 条件,能关联多少条
|
||||
SELECT
|
||||
COUNT(*) AS total_count,
|
||||
COUNT(CASE WHEN T2.id IS NOT NULL AND T2.delete_flag = '0' AND T2.status_enum = 1 THEN 1 END) AS matched_active,
|
||||
COUNT(CASE WHEN T2.id IS NOT NULL AND T2.delete_flag = '0' AND T2.status_enum != 1 THEN 1 END) AS matched_inactive,
|
||||
COUNT(CASE WHEN T2.id IS NULL THEN 1 END) AS unmatched
|
||||
FROM adm_charge_item AS T1
|
||||
LEFT JOIN wor_activity_definition AS T2
|
||||
ON T1.context_enum = 3
|
||||
AND T1.product_id = T2.id
|
||||
AND T2.delete_flag = '0'
|
||||
WHERE T1.context_enum = 3
|
||||
AND T1.delete_flag = '0';
|
||||
|
||||
-- 4. 检查具体某个就诊的诊疗项目(替换 encounterId 为实际值)
|
||||
-- SELECT
|
||||
-- T1.id AS charge_item_id,
|
||||
-- T1.encounter_id,
|
||||
-- T1.product_id,
|
||||
-- T1.status_enum AS charge_status,
|
||||
-- T2.id AS activity_def_id,
|
||||
-- T2.name AS activity_name,
|
||||
-- T2.status_enum AS activity_status,
|
||||
-- CASE
|
||||
-- WHEN T2.id IS NULL THEN '无法关联'
|
||||
-- WHEN T2.status_enum != 1 THEN '诊疗项目未激活'
|
||||
-- ELSE '正常'
|
||||
-- END AS status
|
||||
-- FROM adm_charge_item AS T1
|
||||
-- LEFT JOIN wor_activity_definition AS T2
|
||||
-- ON T1.context_enum = 3
|
||||
-- AND T1.product_id = T2.id
|
||||
-- AND T2.delete_flag = '0'
|
||||
-- AND T2.status_enum = 1
|
||||
-- WHERE T1.encounter_id = :encounterId -- 替换为实际的encounterId
|
||||
-- AND T1.context_enum = 3
|
||||
-- AND T1.delete_flag = '0';
|
||||
|
||||
-- 5. 如果诊疗项目 status_enum != 1,查看这些项目
|
||||
SELECT
|
||||
T1.id AS charge_item_id,
|
||||
T1.encounter_id,
|
||||
T1.product_id,
|
||||
T2.name AS activity_name,
|
||||
T2.status_enum AS activity_status,
|
||||
'诊疗项目未激活,导致无法显示' AS issue
|
||||
FROM adm_charge_item AS T1
|
||||
INNER JOIN wor_activity_definition AS T2
|
||||
ON T1.context_enum = 3
|
||||
AND T1.product_id = T2.id
|
||||
AND T2.delete_flag = '0'
|
||||
AND T2.status_enum != 1 -- 未激活
|
||||
WHERE T1.context_enum = 3
|
||||
AND T1.delete_flag = '0'
|
||||
LIMIT 20;
|
||||
|
||||
142
openhis-server-new/sql/check_pricing_flag_zero_data.sql
Normal file
142
openhis-server-new/sql/check_pricing_flag_zero_data.sql
Normal file
@@ -0,0 +1,142 @@
|
||||
-- ============================================
|
||||
-- 检查 wor_activity_definition 表中 pricing_flag = 0 的数据
|
||||
-- 用途:分析哪些项目被标记为"不允许划价",是否符合业务预期
|
||||
-- ============================================
|
||||
|
||||
-- 1. 查看所有 pricing_flag = 0 的项目详情
|
||||
SELECT
|
||||
id,
|
||||
bus_no,
|
||||
name AS activity_name,
|
||||
category_code,
|
||||
type_enum,
|
||||
pricing_flag,
|
||||
status_enum,
|
||||
org_id,
|
||||
location_id,
|
||||
description_text,
|
||||
create_time,
|
||||
update_time
|
||||
FROM wor_activity_definition
|
||||
WHERE delete_flag = '0'
|
||||
AND pricing_flag = '0' -- 注意:字段类型是 char(1),所以是字符串 '0'
|
||||
ORDER BY id;
|
||||
|
||||
-- 2. 统计 pricing_flag = 0 的项目数量(按状态)
|
||||
SELECT
|
||||
status_enum,
|
||||
COUNT(*) AS count,
|
||||
CASE
|
||||
WHEN status_enum = 1 THEN '激活'
|
||||
WHEN status_enum = 2 THEN '停用'
|
||||
ELSE '其他'
|
||||
END AS status_desc
|
||||
FROM wor_activity_definition
|
||||
WHERE delete_flag = '0'
|
||||
AND pricing_flag = '0'
|
||||
GROUP BY status_enum
|
||||
ORDER BY status_enum;
|
||||
|
||||
-- 3. 检查 pricing_flag = 0 的项目是否有关联的费用定价
|
||||
SELECT
|
||||
T1.id,
|
||||
T1.bus_no,
|
||||
T1.name AS activity_name,
|
||||
T1.pricing_flag,
|
||||
T1.status_enum,
|
||||
T2.id AS charge_item_definition_id,
|
||||
T2.charge_name,
|
||||
T2.price,
|
||||
T2.status_enum AS charge_status,
|
||||
CASE
|
||||
WHEN T2.id IS NOT NULL THEN '有费用定价但不允许划价(可能异常)'
|
||||
ELSE '无费用定价,不允许划价(正常)'
|
||||
END AS analysis
|
||||
FROM wor_activity_definition AS T1
|
||||
LEFT JOIN adm_charge_item_definition AS T2
|
||||
ON T2.instance_id = T1.id
|
||||
AND T2.delete_flag = '0'
|
||||
AND T2.status_enum = 1
|
||||
AND T2.instance_table = 'wor_activity_definition'
|
||||
WHERE T1.delete_flag = '0'
|
||||
AND T1.pricing_flag = '0'
|
||||
ORDER BY T1.id;
|
||||
|
||||
-- 4. 检查 pricing_flag = 0 的项目是否在收费项目表中被使用
|
||||
SELECT
|
||||
T1.id AS activity_id,
|
||||
T1.bus_no,
|
||||
T1.name AS activity_name,
|
||||
T1.pricing_flag,
|
||||
COUNT(T3.id) AS charge_item_count,
|
||||
CASE
|
||||
WHEN COUNT(T3.id) > 0 THEN '已被使用(可能异常:不允许划价但已有收费记录)'
|
||||
ELSE '未被使用(正常)'
|
||||
END AS analysis
|
||||
FROM wor_activity_definition AS T1
|
||||
LEFT JOIN adm_charge_item AS T3
|
||||
ON T3.product_id = T1.id
|
||||
AND T3.context_enum = 3 -- ACTIVITY
|
||||
AND T3.delete_flag = '0'
|
||||
WHERE T1.delete_flag = '0'
|
||||
AND T1.pricing_flag = '0'
|
||||
GROUP BY T1.id, T1.bus_no, T1.name, T1.pricing_flag
|
||||
HAVING COUNT(T3.id) > 0 -- 只显示已被使用的
|
||||
ORDER BY charge_item_count DESC;
|
||||
|
||||
-- 5. 按类别统计 pricing_flag = 0 的项目
|
||||
SELECT
|
||||
category_code,
|
||||
COUNT(*) AS count,
|
||||
STRING_AGG(name, ', ') AS activity_names -- 列出项目名称
|
||||
FROM wor_activity_definition
|
||||
WHERE delete_flag = '0'
|
||||
AND pricing_flag = '0'
|
||||
AND status_enum = 1 -- 只统计激活的
|
||||
GROUP BY category_code
|
||||
ORDER BY category_code;
|
||||
|
||||
-- 6. 对比:pricing_flag = 0 和 pricing_flag = 1 的项目特征
|
||||
SELECT
|
||||
pricing_flag,
|
||||
COUNT(*) AS count,
|
||||
COUNT(DISTINCT category_code) AS category_count,
|
||||
COUNT(DISTINCT org_id) AS org_count,
|
||||
STRING_AGG(DISTINCT category_code::text, ', ') AS categories
|
||||
FROM wor_activity_definition
|
||||
WHERE delete_flag = '0'
|
||||
AND status_enum = 1
|
||||
AND pricing_flag IN ('0', '1')
|
||||
GROUP BY pricing_flag
|
||||
ORDER BY pricing_flag;
|
||||
|
||||
-- 7. 检查是否有子项的项目(childrenJson 不为空)被标记为 pricing_flag = 0
|
||||
SELECT
|
||||
id,
|
||||
bus_no,
|
||||
name AS activity_name,
|
||||
pricing_flag,
|
||||
children_json,
|
||||
CASE
|
||||
WHEN children_json IS NOT NULL AND children_json != '' THEN '有子项但不允许划价(可能是套餐子项)'
|
||||
ELSE '无子项'
|
||||
END AS analysis
|
||||
FROM wor_activity_definition
|
||||
WHERE delete_flag = '0'
|
||||
AND pricing_flag = '0'
|
||||
AND children_json IS NOT NULL
|
||||
AND children_json != ''
|
||||
ORDER BY id;
|
||||
|
||||
-- 8. 查看 pricing_flag 字段的所有可能值(包括 NULL)
|
||||
SELECT
|
||||
COALESCE(pricing_flag::text, 'NULL') AS pricing_flag_value,
|
||||
COUNT(*) AS count,
|
||||
ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (), 2) AS percentage
|
||||
FROM wor_activity_definition
|
||||
WHERE delete_flag = '0'
|
||||
AND status_enum = 1
|
||||
GROUP BY pricing_flag
|
||||
ORDER BY pricing_flag NULLS LAST;
|
||||
|
||||
|
||||
116
openhis-server-new/sql/check_treatment_items_detailed.sql
Normal file
116
openhis-server-new/sql/check_treatment_items_detailed.sql
Normal file
@@ -0,0 +1,116 @@
|
||||
-- 详细检查诊疗项目检索问题
|
||||
-- 已知:wor_activity_definition 表中有4条数据
|
||||
|
||||
-- 1. 检查这4条诊疗项目定义的详细信息
|
||||
SELECT
|
||||
id,
|
||||
name,
|
||||
bus_no,
|
||||
delete_flag,
|
||||
tenant_id,
|
||||
create_time
|
||||
FROM wor_activity_definition
|
||||
ORDER BY id;
|
||||
|
||||
-- 2. 检查收费项目中是否有诊疗项目类型的记录
|
||||
-- 注意:ChargeItemContext.ACTIVITY.getValue() 返回的是 Integer 3
|
||||
-- 但数据库中 context_enum 可能是字符串类型,需要检查是 '3' 还是 3
|
||||
SELECT
|
||||
COUNT(*) as total_activity_charge_items,
|
||||
COUNT(CASE WHEN delete_flag = '0' THEN 1 END) as active_activity_charge_items,
|
||||
COUNT(CASE WHEN delete_flag != '0' THEN 1 END) as deleted_activity_charge_items
|
||||
FROM adm_charge_item
|
||||
WHERE context_enum::text = '3' OR context_enum = 3; -- 诊疗项目类型是3
|
||||
|
||||
-- 3. 检查收费项目中的product_id是否能匹配到诊疗项目定义
|
||||
-- 注意:诊疗项目类型的 context_enum = 3(ChargeItemContext.ACTIVITY.getValue())
|
||||
SELECT
|
||||
aci.id as charge_item_id,
|
||||
aci.encounter_id,
|
||||
aci.context_enum,
|
||||
aci.product_id as charge_product_id,
|
||||
aci.status_enum,
|
||||
aci.delete_flag as charge_delete_flag,
|
||||
wad.id as activity_def_id,
|
||||
wad.name as activity_name,
|
||||
wad.delete_flag as activity_delete_flag,
|
||||
CASE
|
||||
WHEN wad.id IS NULL THEN '❌ 诊疗项目定义不存在'
|
||||
WHEN wad.delete_flag != '0' THEN '❌ 诊疗项目定义已删除'
|
||||
WHEN aci.delete_flag != '0' THEN '❌ 收费项目已删除'
|
||||
ELSE '✅ 正常'
|
||||
END as match_status
|
||||
FROM adm_charge_item aci
|
||||
LEFT JOIN wor_activity_definition wad
|
||||
ON aci.product_id = wad.id
|
||||
WHERE (aci.context_enum::text = '3' OR aci.context_enum = 3) -- 诊疗项目类型是3
|
||||
LIMIT 20;
|
||||
|
||||
-- 4. 检查context_enum的所有可能值(确认诊疗项目的枚举值是什么)
|
||||
SELECT DISTINCT
|
||||
context_enum,
|
||||
COUNT(*) as count
|
||||
FROM adm_charge_item
|
||||
WHERE delete_flag = '0'
|
||||
GROUP BY context_enum
|
||||
ORDER BY context_enum;
|
||||
|
||||
-- 5. 检查是否有收费项目,但无法匹配到诊疗项目定义
|
||||
SELECT
|
||||
COUNT(*) as unmatched_count,
|
||||
STRING_AGG(DISTINCT CAST(product_id AS VARCHAR), ', ') as unmatched_product_ids,
|
||||
STRING_AGG(DISTINCT CAST(id AS VARCHAR), ', ') as unmatched_charge_item_ids
|
||||
FROM adm_charge_item aci
|
||||
WHERE (aci.context_enum::text = '3' OR aci.context_enum = 3) -- 诊疗项目类型是3
|
||||
AND aci.delete_flag = '0'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM wor_activity_definition wad
|
||||
WHERE wad.id = aci.product_id
|
||||
AND wad.delete_flag = '0'
|
||||
);
|
||||
|
||||
-- 6. 检查具体某个就诊的诊疗项目(如果有具体的encounterId)
|
||||
-- SELECT
|
||||
-- T1.encounter_id,
|
||||
-- T1.id as charge_item_id,
|
||||
-- T1.context_enum,
|
||||
-- T1.product_id,
|
||||
-- T1.status_enum,
|
||||
-- T1.delete_flag,
|
||||
-- T2.id as activity_def_id,
|
||||
-- T2.name as activity_name,
|
||||
-- T2.delete_flag as activity_delete_flag
|
||||
-- FROM adm_charge_item AS T1
|
||||
-- LEFT JOIN wor_activity_definition AS T2
|
||||
-- ON T1.product_id = T2.id
|
||||
-- WHERE T1.encounter_id = :encounterId -- 替换为实际的encounterId
|
||||
-- AND T1.context_enum IN ('ACTIVITY', '1', 'ACTIVITY_CODE') -- 尝试多种可能的值
|
||||
-- AND T1.delete_flag = '0';
|
||||
|
||||
-- 7. 检查后端代码中使用的context_enum值
|
||||
-- ChargeItemContext.ACTIVITY.getValue() 的实际返回值需要查看枚举类
|
||||
-- 可能是:'ACTIVITY', '1', 'ACTIVITY_CODE', 或其他值
|
||||
-- 建议先运行查询4,查看数据库中实际使用的context_enum值
|
||||
|
||||
-- 8. 检查status_enum的值(查询条件中需要匹配的状态)
|
||||
-- SQL查询中要求 status_enum IN (1, 2, 3, 4, 5, 6)
|
||||
-- 检查收费项目中的状态值是否在这个范围内
|
||||
SELECT
|
||||
status_enum,
|
||||
COUNT(*) as count,
|
||||
CASE
|
||||
WHEN status_enum IN (1, 2, 3, 4, 5, 6) THEN '✅ 在查询范围内'
|
||||
ELSE '❌ 不在查询范围内'
|
||||
END as status_check
|
||||
FROM adm_charge_item
|
||||
WHERE context_enum IN (
|
||||
SELECT DISTINCT context_enum
|
||||
FROM adm_charge_item
|
||||
WHERE delete_flag = '0'
|
||||
LIMIT 10
|
||||
)
|
||||
AND delete_flag = '0'
|
||||
GROUP BY status_enum
|
||||
ORDER BY status_enum;
|
||||
|
||||
108
openhis-server-new/sql/debug_queue_date_issue.sql
Normal file
108
openhis-server-new/sql/debug_queue_date_issue.sql
Normal file
@@ -0,0 +1,108 @@
|
||||
-- ============================================
|
||||
-- 调试队列日期问题
|
||||
-- 用途:检查数据库中的 queue_date 字段值,找出为什么查询不到数据
|
||||
-- ============================================
|
||||
|
||||
-- 1. 查看所有队列记录的日期分布
|
||||
SELECT
|
||||
queue_date,
|
||||
status,
|
||||
COUNT(*) AS count,
|
||||
STRING_AGG(patient_name, ', ') AS patients
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
GROUP BY queue_date, status
|
||||
ORDER BY queue_date DESC, status;
|
||||
|
||||
-- 2. 查看指定科室的所有记录(不限制日期)
|
||||
SELECT
|
||||
id,
|
||||
queue_date,
|
||||
status,
|
||||
patient_name,
|
||||
organization_id,
|
||||
organization_name,
|
||||
queue_order,
|
||||
create_time
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
AND organization_id = 1995302136614969300
|
||||
ORDER BY queue_date DESC, queue_order ASC;
|
||||
|
||||
-- 3. 查看今天的记录(使用 CURRENT_DATE)
|
||||
SELECT
|
||||
id,
|
||||
queue_date,
|
||||
status,
|
||||
patient_name,
|
||||
organization_id,
|
||||
queue_order
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND organization_id = 1995302136614969300
|
||||
ORDER BY queue_order ASC;
|
||||
|
||||
-- 4. 查看 2026-01-16 的记录
|
||||
SELECT
|
||||
id,
|
||||
queue_date,
|
||||
status,
|
||||
patient_name,
|
||||
organization_id,
|
||||
queue_order
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
AND queue_date = '2026-01-16'::DATE
|
||||
AND organization_id = 1995302136614969300
|
||||
ORDER BY queue_order ASC;
|
||||
|
||||
-- 5. 查看 2025-01-16 的记录(可能是昨天的数据)
|
||||
SELECT
|
||||
id,
|
||||
queue_date,
|
||||
status,
|
||||
patient_name,
|
||||
organization_id,
|
||||
queue_order
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
AND queue_date = '2025-01-16'::DATE
|
||||
AND organization_id = 1995302136614969300
|
||||
ORDER BY queue_order ASC;
|
||||
|
||||
-- 6. 查看最近的记录(最近7天)
|
||||
SELECT
|
||||
queue_date,
|
||||
status,
|
||||
COUNT(*) AS count
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
AND queue_date >= CURRENT_DATE - INTERVAL '7 days'
|
||||
GROUP BY queue_date, status
|
||||
ORDER BY queue_date DESC, status;
|
||||
|
||||
-- 7. 检查系统当前日期(PostgreSQL)
|
||||
SELECT CURRENT_DATE AS current_date_in_db;
|
||||
|
||||
-- 8. 查看所有状态为 WAITING 或 SKIPPED 的记录(不限制日期)
|
||||
SELECT
|
||||
id,
|
||||
queue_date,
|
||||
status,
|
||||
patient_name,
|
||||
organization_id,
|
||||
queue_order
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
AND status IN ('WAITING', 'SKIPPED')
|
||||
AND organization_id = 1995302136614969300
|
||||
ORDER BY queue_date DESC, queue_order ASC;
|
||||
|
||||
92
openhis-server-new/sql/diagnose_treatment_items_issue.sql
Normal file
92
openhis-server-new/sql/diagnose_treatment_items_issue.sql
Normal file
@@ -0,0 +1,92 @@
|
||||
-- 诊断:门诊划价检索不出诊疗项目的问题
|
||||
-- 问题描述:在门诊划价页面,检索不出诊疗项目
|
||||
|
||||
-- 1. 检查是否有诊疗项目定义数据
|
||||
SELECT
|
||||
COUNT(*) as total_count,
|
||||
COUNT(CASE WHEN delete_flag = '0' THEN 1 END) as active_count,
|
||||
COUNT(CASE WHEN delete_flag != '0' THEN 1 END) as deleted_count
|
||||
FROM wor_activity_definition;
|
||||
|
||||
-- 2. 检查是否有收费项目(诊疗项目类型)
|
||||
SELECT
|
||||
T1.id,
|
||||
T1.encounter_id,
|
||||
T1.context_enum,
|
||||
T1.product_id,
|
||||
T1.status_enum,
|
||||
T1.delete_flag,
|
||||
T2.id as activity_def_id,
|
||||
T2.name as activity_name,
|
||||
T2.delete_flag as activity_delete_flag
|
||||
FROM adm_charge_item AS T1
|
||||
LEFT JOIN wor_activity_definition AS T2
|
||||
ON T1.context_enum = 'ACTIVITY' -- 诊疗项目类型
|
||||
AND T1.product_id = T2.id
|
||||
AND T2.delete_flag = '0'
|
||||
WHERE T1.context_enum = 'ACTIVITY'
|
||||
AND T1.delete_flag = '0'
|
||||
LIMIT 20;
|
||||
|
||||
-- 3. 检查是否有诊疗项目定义,但收费项目中的product_id无法匹配
|
||||
SELECT
|
||||
'诊疗项目定义存在,但收费项目无法匹配' as issue_type,
|
||||
COUNT(*) as count
|
||||
FROM wor_activity_definition wad
|
||||
WHERE wad.delete_flag = '0'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM adm_charge_item aci
|
||||
WHERE aci.context_enum = 'ACTIVITY'
|
||||
AND aci.product_id = wad.id
|
||||
AND aci.delete_flag = '0'
|
||||
);
|
||||
|
||||
-- 4. 检查收费项目中的诊疗项目,但定义表中没有对应数据
|
||||
SELECT
|
||||
'收费项目存在,但诊疗项目定义缺失' as issue_type,
|
||||
COUNT(*) as count
|
||||
FROM adm_charge_item aci
|
||||
WHERE aci.context_enum = 'ACTIVITY'
|
||||
AND aci.delete_flag = '0'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM wor_activity_definition wad
|
||||
WHERE wad.id = aci.product_id
|
||||
AND wad.delete_flag = '0'
|
||||
);
|
||||
|
||||
-- 5. 检查某个具体就诊的诊疗项目(替换encounterId为实际值)
|
||||
-- SELECT
|
||||
-- T1.encounter_id,
|
||||
-- T1.id as charge_item_id,
|
||||
-- T1.context_enum,
|
||||
-- T1.product_id,
|
||||
-- T1.status_enum,
|
||||
-- T2.id as activity_def_id,
|
||||
-- T2.name as activity_name,
|
||||
-- T2.delete_flag as activity_delete_flag,
|
||||
-- CASE
|
||||
-- WHEN T2.id IS NULL THEN '诊疗项目定义不存在或已删除'
|
||||
-- WHEN T2.delete_flag != '0' THEN '诊疗项目定义已删除'
|
||||
-- ELSE '正常'
|
||||
-- END as status
|
||||
-- FROM adm_charge_item AS T1
|
||||
-- LEFT JOIN wor_activity_definition AS T2
|
||||
-- ON T1.context_enum = 'ACTIVITY'
|
||||
-- AND T1.product_id = T2.id
|
||||
-- WHERE T1.encounter_id = :encounterId -- 替换为实际的encounterId
|
||||
-- AND T1.context_enum = 'ACTIVITY'
|
||||
-- AND T1.delete_flag = '0'
|
||||
-- AND T1.status_enum IN (1, 2, 3, 4, 5, 6); -- PLANNED, BILLABLE, BILLED, REFUNDING, REFUNDED, PART_REFUND
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
144
openhis-server-new/sql/query_pricing_flag_distribution.sql
Normal file
144
openhis-server-new/sql/query_pricing_flag_distribution.sql
Normal file
@@ -0,0 +1,144 @@
|
||||
-- ============================================
|
||||
-- 查询诊疗项目 pricing_flag 字段分布情况
|
||||
-- 用途:了解哪些项目允许划价,哪些不允许
|
||||
-- ============================================
|
||||
|
||||
-- 1. 统计 pricing_flag 字段的分布情况
|
||||
SELECT
|
||||
COUNT(*) AS total_count,
|
||||
COUNT(CASE WHEN pricing_flag = 1 THEN 1 END) AS pricing_flag_1_count, -- 允许划价
|
||||
COUNT(CASE WHEN pricing_flag = 0 THEN 1 END) AS pricing_flag_0_count, -- 不允许划价
|
||||
COUNT(CASE WHEN pricing_flag IS NULL THEN 1 END) AS pricing_flag_null_count, -- 未设置
|
||||
ROUND(COUNT(CASE WHEN pricing_flag = 1 THEN 1 END) * 100.0 / COUNT(*), 2) AS flag_1_percent,
|
||||
ROUND(COUNT(CASE WHEN pricing_flag = 0 THEN 1 END) * 100.0 / COUNT(*), 2) AS flag_0_percent,
|
||||
ROUND(COUNT(CASE WHEN pricing_flag IS NULL THEN 1 END) * 100.0 / COUNT(*), 2) AS flag_null_percent
|
||||
FROM wor_activity_definition
|
||||
WHERE delete_flag = '0'
|
||||
AND status_enum = 1; -- 只统计激活状态的项目
|
||||
|
||||
-- 2. 查看所有允许划价的项目(pricing_flag = 1 或 NULL)
|
||||
SELECT
|
||||
id,
|
||||
bus_no,
|
||||
name AS activity_name,
|
||||
pricing_flag,
|
||||
status_enum,
|
||||
org_id,
|
||||
category_code,
|
||||
type_enum,
|
||||
CASE
|
||||
WHEN pricing_flag = 1 THEN '允许划价'
|
||||
WHEN pricing_flag = 0 THEN '不允许划价'
|
||||
WHEN pricing_flag IS NULL THEN '未设置(默认允许)'
|
||||
ELSE '未知'
|
||||
END AS pricing_flag_desc
|
||||
FROM wor_activity_definition
|
||||
WHERE delete_flag = '0'
|
||||
AND status_enum = 1
|
||||
AND (pricing_flag = 1 OR pricing_flag IS NULL) -- 之前过滤条件
|
||||
ORDER BY id;
|
||||
|
||||
-- 3. 查看不允许划价的项目(pricing_flag = 0)
|
||||
SELECT
|
||||
id,
|
||||
bus_no,
|
||||
name AS activity_name,
|
||||
pricing_flag,
|
||||
status_enum,
|
||||
org_id,
|
||||
category_code,
|
||||
type_enum,
|
||||
description_text,
|
||||
'不允许划价' AS pricing_flag_desc
|
||||
FROM wor_activity_definition
|
||||
WHERE delete_flag = '0'
|
||||
AND status_enum = 1
|
||||
AND pricing_flag = 0 -- 这些项目之前不会显示
|
||||
ORDER BY id;
|
||||
|
||||
-- 4. 查看未设置 pricing_flag 的项目(NULL)
|
||||
SELECT
|
||||
id,
|
||||
bus_no,
|
||||
name AS activity_name,
|
||||
pricing_flag,
|
||||
status_enum,
|
||||
org_id,
|
||||
category_code,
|
||||
type_enum,
|
||||
'未设置(默认允许)' AS pricing_flag_desc
|
||||
FROM wor_activity_definition
|
||||
WHERE delete_flag = '0'
|
||||
AND status_enum = 1
|
||||
AND pricing_flag IS NULL
|
||||
ORDER BY id;
|
||||
|
||||
-- 5. 按科室统计 pricing_flag 分布
|
||||
SELECT
|
||||
org_id,
|
||||
COUNT(*) AS total_count,
|
||||
COUNT(CASE WHEN pricing_flag = 1 THEN 1 END) AS flag_1_count,
|
||||
COUNT(CASE WHEN pricing_flag = 0 THEN 1 END) AS flag_0_count,
|
||||
COUNT(CASE WHEN pricing_flag IS NULL THEN 1 END) AS flag_null_count
|
||||
FROM wor_activity_definition
|
||||
WHERE delete_flag = '0'
|
||||
AND status_enum = 1
|
||||
GROUP BY org_id
|
||||
ORDER BY org_id;
|
||||
|
||||
-- 6. 按类别统计 pricing_flag 分布
|
||||
SELECT
|
||||
category_code,
|
||||
COUNT(*) AS total_count,
|
||||
COUNT(CASE WHEN pricing_flag = 1 THEN 1 END) AS flag_1_count,
|
||||
COUNT(CASE WHEN pricing_flag = 0 THEN 1 END) AS flag_0_count,
|
||||
COUNT(CASE WHEN pricing_flag IS NULL THEN 1 END) AS flag_null_count
|
||||
FROM wor_activity_definition
|
||||
WHERE delete_flag = '0'
|
||||
AND status_enum = 1
|
||||
GROUP BY category_code
|
||||
ORDER BY category_code;
|
||||
|
||||
-- 7. 检查是否有费用定价关联的项目,但 pricing_flag = 0
|
||||
SELECT
|
||||
T1.id,
|
||||
T1.bus_no,
|
||||
T1.name AS activity_name,
|
||||
T1.pricing_flag,
|
||||
T2.id AS charge_item_definition_id,
|
||||
T2.charge_name,
|
||||
T2.price,
|
||||
'有费用定价但不允许划价' AS issue_desc
|
||||
FROM wor_activity_definition AS T1
|
||||
LEFT JOIN adm_charge_item_definition AS T2
|
||||
ON T2.instance_id = T1.id
|
||||
AND T2.delete_flag = '0'
|
||||
AND T2.status_enum = 1
|
||||
AND T2.instance_table = 'wor_activity_definition'
|
||||
WHERE T1.delete_flag = '0'
|
||||
AND T1.status_enum = 1
|
||||
AND T1.pricing_flag = 0
|
||||
AND T2.id IS NOT NULL -- 有关联的费用定价
|
||||
ORDER BY T1.id;
|
||||
|
||||
-- 8. 检查没有费用定价关联的项目,但 pricing_flag = 1
|
||||
SELECT
|
||||
T1.id,
|
||||
T1.bus_no,
|
||||
T1.name AS activity_name,
|
||||
T1.pricing_flag,
|
||||
T2.id AS charge_item_definition_id,
|
||||
'允许划价但没有费用定价' AS issue_desc
|
||||
FROM wor_activity_definition AS T1
|
||||
LEFT JOIN adm_charge_item_definition AS T2
|
||||
ON T2.instance_id = T1.id
|
||||
AND T2.delete_flag = '0'
|
||||
AND T2.status_enum = 1
|
||||
AND T2.instance_table = 'wor_activity_definition'
|
||||
WHERE T1.delete_flag = '0'
|
||||
AND T1.status_enum = 1
|
||||
AND (T1.pricing_flag = 1 OR T1.pricing_flag IS NULL)
|
||||
AND T2.id IS NULL -- 没有关联的费用定价
|
||||
ORDER BY T1.id;
|
||||
|
||||
|
||||
313
openhis-server-new/sql/query_smart_candidate_pool.sql
Normal file
313
openhis-server-new/sql/query_smart_candidate_pool.sql
Normal file
@@ -0,0 +1,313 @@
|
||||
-- ============================================
|
||||
-- 智能候选池查询SQL(当日已挂号患者)
|
||||
-- 对应接口:GET /charge-manage/register/current-day-encounter
|
||||
-- 用途:查询当日已挂号但未入队的患者,用于智能分诊排队管理
|
||||
-- ============================================
|
||||
|
||||
-- 完整SQL查询(包含所有关联表)
|
||||
SELECT T9.tenant_id AS tenantId,
|
||||
T9.encounter_id AS encounterId,
|
||||
T9.display_order AS displayOrder,
|
||||
T9.organization_id AS organizationId,
|
||||
T9.organization_name AS organizationName,
|
||||
T9.healthcare_name AS healthcareName,
|
||||
T9.practitioner_user_id AS practitionerUserId,
|
||||
T9.practitioner_name AS practitionerName,
|
||||
T9.contract_name AS contractName,
|
||||
T9.patient_id AS patientId,
|
||||
T9.patient_name AS patientName,
|
||||
T9.phone,
|
||||
T9.gender_enum AS genderEnum,
|
||||
T9.id_card AS idCard,
|
||||
T9.status_enum AS statusEnum,
|
||||
T9.register_time AS registerTime,
|
||||
T9.total_price AS totalPrice,
|
||||
T9.account_name AS accountName,
|
||||
T9.enterer_name AS entererName,
|
||||
T9.charge_item_ids AS chargeItemIds,
|
||||
T9.payment_id AS paymentId,
|
||||
T9.picture_url AS pictureUrl,
|
||||
T9.birth_date AS birthDate,
|
||||
COALESCE(T9.identifier_no, T9.patient_bus_no, '') AS identifierNo
|
||||
FROM (
|
||||
SELECT T1.tenant_id AS tenant_id,
|
||||
T1.id AS encounter_id,
|
||||
T1.display_order AS display_order,
|
||||
T1.organization_id AS organization_id,
|
||||
T2.NAME AS organization_name,
|
||||
T3.NAME AS healthcare_name,
|
||||
T5.user_id AS practitioner_user_id,
|
||||
T5.NAME AS practitioner_name,
|
||||
T7.contract_name AS contract_name,
|
||||
T8.id AS patient_id,
|
||||
T8.NAME AS patient_name,
|
||||
T8.phone AS phone,
|
||||
T8.gender_enum AS gender_enum,
|
||||
T8.id_card AS id_card,
|
||||
T1.status_enum AS status_enum,
|
||||
T1.create_time AS register_time,
|
||||
T10.total_price,
|
||||
T11."name" AS account_name,
|
||||
T12."name" AS enterer_name,
|
||||
T13.charge_item_ids,
|
||||
T13.id AS payment_id,
|
||||
ai.picture_url AS picture_url,
|
||||
T8.birth_date AS birth_date,
|
||||
T8.bus_no AS patient_bus_no,
|
||||
T18.identifier_no AS identifier_no
|
||||
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'
|
||||
-- 关联就诊参与者表(医生信息)
|
||||
LEFT JOIN (
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT *,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0'
|
||||
AND (
|
||||
-- 如果就诊状态为"进行中",查找"接诊医生"(admitter)
|
||||
(type_code = 'admitter' AND EXISTS(SELECT 1
|
||||
FROM adm_encounter AS T1
|
||||
WHERE T1.status_enum = 2 -- IN_PROGRESS
|
||||
AND T1.ID = encounter_id))
|
||||
OR
|
||||
-- 如果就诊状态不是"进行中",查找"挂号医生"(registration_doctor)
|
||||
(type_code = 'registration_doctor' AND NOT EXISTS(SELECT 1
|
||||
FROM adm_encounter AS T1
|
||||
WHERE T1.status_enum = 2 -- IN_PROGRESS
|
||||
AND T1.ID = encounter_id))
|
||||
)
|
||||
) t
|
||||
WHERE rn = 1
|
||||
) AS T4 ON T1.ID = T4.encounter_id
|
||||
-- 关联医生表
|
||||
LEFT JOIN adm_practitioner AS T5
|
||||
ON T5.ID = T4.practitioner_id
|
||||
AND T5.delete_flag = '0'
|
||||
-- 关联账户表
|
||||
LEFT JOIN adm_account AS T6
|
||||
ON T1.ID = T6.encounter_id
|
||||
AND T6.delete_flag = '0'
|
||||
AND T6.encounter_flag = '1'
|
||||
-- 关联合同表
|
||||
LEFT JOIN fin_contract AS T7
|
||||
ON T6.contract_no = T7.bus_no
|
||||
AND T7.delete_flag = '0'
|
||||
-- 关联患者表
|
||||
LEFT JOIN adm_patient AS T8
|
||||
ON T1.patient_id = T8.ID
|
||||
AND T8.delete_flag = '0'
|
||||
-- 关联患者标识表(身份证号等)
|
||||
LEFT JOIN (
|
||||
SELECT patient_id,
|
||||
identifier_no
|
||||
FROM (
|
||||
SELECT patient_id,
|
||||
identifier_no,
|
||||
ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_patient_identifier
|
||||
WHERE delete_flag = '0'
|
||||
AND identifier_no IS NOT NULL
|
||||
AND identifier_no != ''
|
||||
) t
|
||||
WHERE rn = 1
|
||||
) AS T18 ON T8.id = T18.patient_id
|
||||
-- 关联收费项目表(挂号费)
|
||||
LEFT JOIN adm_charge_item AS T10
|
||||
ON T1.id = T10.encounter_id
|
||||
AND T10.delete_flag = '0'
|
||||
AND T10.context_enum = 1 -- REGISTER(挂号)
|
||||
-- 关联账户表(收费账户)
|
||||
LEFT JOIN adm_account AS T11
|
||||
ON T10.account_id = T11.id
|
||||
AND T11.delete_flag = '0'
|
||||
-- 关联医生表(收费医生)
|
||||
LEFT JOIN adm_practitioner AS T12
|
||||
ON T12.ID = T10.enterer_id
|
||||
AND T12.delete_flag = '0'
|
||||
-- 关联支付对账表(已支付)
|
||||
LEFT JOIN fin_payment_reconciliation T13
|
||||
ON T10.id::TEXT = ANY(string_to_array(T13.charge_item_ids,','))
|
||||
AND T13.delete_flag = '0'
|
||||
AND T13.status_enum = 1 -- SUCCESS(支付成功)
|
||||
-- 关联退号记录(当状态为退号时,通过relation_id关联原支付记录)
|
||||
LEFT JOIN fin_payment_reconciliation T14
|
||||
ON T13.id = T14.relation_id
|
||||
AND T14.delete_flag = '0'
|
||||
AND T14.status_enum = 3 -- REFUND(退款)
|
||||
AND T14.payment_enum = 1
|
||||
-- 关联退号医生
|
||||
LEFT JOIN adm_practitioner AS T15
|
||||
ON T15.ID = T14.enterer_id
|
||||
AND T15.delete_flag = '0'
|
||||
-- 关联系统用户表
|
||||
LEFT JOIN sys_user AS T17
|
||||
ON T17.user_id = T15.user_id
|
||||
AND T17.delete_flag = '0'
|
||||
-- 关联退号支付详情,获取退款方式(聚合多个支付方式)
|
||||
LEFT JOIN (
|
||||
SELECT reconciliation_id,
|
||||
STRING_AGG(
|
||||
CASE pay_enum
|
||||
WHEN 220400 THEN '现金'
|
||||
WHEN 220100 THEN '微信'
|
||||
WHEN 220200 THEN '支付宝'
|
||||
WHEN 220300 THEN '银联'
|
||||
END,
|
||||
','
|
||||
ORDER BY pay_enum
|
||||
) AS refund_method
|
||||
FROM fin_payment_rec_detail
|
||||
WHERE delete_flag = '0'
|
||||
AND amount < 0
|
||||
AND pay_enum IN (220400, 220100, 220200, 220300)
|
||||
GROUP BY reconciliation_id
|
||||
) AS T16 ON T14.id = T16.reconciliation_id
|
||||
-- 关联发票表
|
||||
LEFT JOIN adm_invoice AS ai
|
||||
ON ai.reconciliation_id = T13.id
|
||||
AND ai.delete_flag = '0'
|
||||
WHERE T1.delete_flag = '0'
|
||||
AND T1.class_enum = 1 -- 门诊(AMB)
|
||||
AND T10.context_enum = 1 -- 挂号(REGISTER)
|
||||
-- 动态条件(由前端传入,通过 ${ew.customSqlSegment} 注入)
|
||||
-- 例如:AND T1.create_time::DATE = CURRENT_DATE -- 当日挂号
|
||||
-- 例如:AND T1.status_enum != 6 -- 排除退号(statusEnum = -1时)
|
||||
-- 例如:AND (T8.name LIKE '%关键词%' OR T2.name LIKE '%关键词%' ...) -- 模糊搜索
|
||||
) AS T9
|
||||
-- 动态查询条件(由 MyBatis-Plus QueryWrapper 生成)
|
||||
-- ${ew.customSqlSegment}
|
||||
ORDER BY T9.register_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 简化版查询(仅查询核心字段,用于快速测试)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
pt.name AS patientName,
|
||||
pt.birth_date AS birthDate,
|
||||
org.id AS organizationId,
|
||||
org.name AS organizationName,
|
||||
hcs.name AS healthcareName,
|
||||
pr.name AS practitionerName,
|
||||
enc.create_time AS registerTime,
|
||||
enc.status_enum AS statusEnum
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt
|
||||
ON enc.patient_id = pt.id
|
||||
AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org
|
||||
ON enc.organization_id = org.id
|
||||
AND org.delete_flag = '0'
|
||||
LEFT JOIN adm_healthcare_service hcs
|
||||
ON enc.service_type_id = hcs.id
|
||||
AND hcs.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT encounter_id, practitioner_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0'
|
||||
AND type_code IN ('admitter', 'registration_doctor')
|
||||
) ep ON enc.id = ep.encounter_id AND ep.rn = 1
|
||||
LEFT JOIN adm_practitioner pr
|
||||
ON ep.practitioner_id = pr.id
|
||||
AND pr.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci
|
||||
ON enc.id = ci.encounter_id
|
||||
AND ci.delete_flag = '0'
|
||||
AND ci.context_enum = 1 -- 挂号
|
||||
INNER JOIN fin_payment_reconciliation prc
|
||||
ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0'
|
||||
AND prc.status_enum = 1 -- 支付成功
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1 -- 门诊
|
||||
AND enc.create_time::DATE = CURRENT_DATE -- 当日挂号
|
||||
AND enc.status_enum != 6 -- 排除退号
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 查询条件说明
|
||||
-- ============================================
|
||||
-- 1. 基础条件:
|
||||
-- - enc.delete_flag = '0' -- 未删除
|
||||
-- - enc.class_enum = 1 -- 门诊(AMB)
|
||||
-- - ci.context_enum = 1 -- 挂号(REGISTER)
|
||||
-- - prc.status_enum = 1 -- 支付成功(SUCCESS)
|
||||
--
|
||||
-- 2. 日期过滤:
|
||||
-- - enc.create_time::DATE = CURRENT_DATE -- 当日挂号(由前端传入 date 参数)
|
||||
--
|
||||
-- 3. 状态过滤:
|
||||
-- - enc.status_enum != 6 -- 排除退号(当 statusEnum = -1 时)
|
||||
-- - enc.status_enum = 2 -- 进行中(IN_PROGRESS,用于判断医生类型)
|
||||
--
|
||||
-- 4. 模糊搜索(由前端传入 searchKey 参数):
|
||||
-- - patient_name(患者姓名)
|
||||
-- - organization_name(科室名称)
|
||||
-- - practitioner_name(医生姓名)
|
||||
-- - healthcare_name(号别名称)
|
||||
-- - identifier_no(身份证号)
|
||||
--
|
||||
-- 5. 租户过滤(由后端自动添加):
|
||||
-- - tenant_id = ? -- 当前登录用户的租户ID
|
||||
--
|
||||
-- 6. 分页(由 MyBatis-Plus Page 对象处理):
|
||||
-- - LIMIT ? OFFSET ?
|
||||
|
||||
-- ============================================
|
||||
-- 常用查询示例
|
||||
-- ============================================
|
||||
|
||||
-- 示例1:查询今天所有已挂号患者(排除退号)
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
pt.name AS patientName,
|
||||
org.name AS organizationName,
|
||||
hcs.name AS healthcareName,
|
||||
pr.name AS practitionerName,
|
||||
enc.create_time AS registerTime
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org ON enc.organization_id = org.id AND org.delete_flag = '0'
|
||||
LEFT JOIN adm_healthcare_service hcs ON enc.service_type_id = hcs.id AND hcs.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT encounter_id, practitioner_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0' AND type_code IN ('admitter', 'registration_doctor')
|
||||
) ep ON enc.id = ep.encounter_id AND ep.rn = 1
|
||||
LEFT JOIN adm_practitioner pr ON ep.practitioner_id = pr.id AND pr.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- 示例2:查询指定科室的已挂号患者
|
||||
-- 在示例1的基础上添加:
|
||||
-- AND enc.organization_id = ? -- 科室ID
|
||||
|
||||
-- 示例3:按患者姓名模糊搜索
|
||||
-- 在示例1的基础上添加:
|
||||
-- AND pt.name LIKE '%关键词%'
|
||||
|
||||
-- 示例4:查询已加入队列的患者(用于去重)
|
||||
SELECT DISTINCT encounter_id
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND status != 'COMPLETED';
|
||||
|
||||
268
openhis-server-new/sql/query_smart_candidate_pool_page.sql
Normal file
268
openhis-server-new/sql/query_smart_candidate_pool_page.sql
Normal file
@@ -0,0 +1,268 @@
|
||||
-- ============================================
|
||||
-- 智能候选池查询SQL(智能分诊排队管理页面专用)
|
||||
-- 页面:智能分诊排队管理 - 心内科
|
||||
-- 用途:查询"已签到未入队"的患者(当日已挂号,但未加入队列)
|
||||
-- ============================================
|
||||
|
||||
-- ============================================
|
||||
-- 方案一:完整查询(包含所有字段,排除已在队列中的患者)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
pt.name AS patientName,
|
||||
pt.birth_date AS birthDate,
|
||||
-- 计算年龄(前端会调用 parseAge 函数)
|
||||
CASE
|
||||
WHEN pt.birth_date IS NOT NULL THEN
|
||||
EXTRACT(YEAR FROM AGE(pt.birth_date))::TEXT || '岁'
|
||||
ELSE ''
|
||||
END AS age,
|
||||
org.id AS organizationId,
|
||||
org.name AS organizationName,
|
||||
hcs.name AS healthcareName,
|
||||
pr.name AS practitionerName,
|
||||
enc.create_time AS registerTime,
|
||||
enc.status_enum AS statusEnum,
|
||||
-- 其他字段(如果需要)
|
||||
pt.phone,
|
||||
pt.gender_enum AS genderEnum,
|
||||
pt.id_card AS idCard
|
||||
FROM adm_encounter enc
|
||||
-- 关联患者表
|
||||
INNER JOIN adm_patient pt
|
||||
ON enc.patient_id = pt.id
|
||||
AND pt.delete_flag = '0'
|
||||
-- 关联科室表
|
||||
LEFT JOIN adm_organization org
|
||||
ON enc.organization_id = org.id
|
||||
AND org.delete_flag = '0'
|
||||
-- 关联医疗服务类型表(号别)
|
||||
LEFT JOIN adm_healthcare_service hcs
|
||||
ON enc.service_type_id = hcs.id
|
||||
AND hcs.delete_flag = '0'
|
||||
-- 关联就诊参与者表(医生信息)
|
||||
LEFT JOIN (
|
||||
SELECT encounter_id, practitioner_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0'
|
||||
AND (
|
||||
-- 如果就诊状态为"进行中",查找"接诊医生"(admitter)
|
||||
(type_code = 'admitter' AND EXISTS(SELECT 1
|
||||
FROM adm_encounter AS e
|
||||
WHERE e.status_enum = 2 -- IN_PROGRESS
|
||||
AND e.id = encounter_id))
|
||||
OR
|
||||
-- 如果就诊状态不是"进行中",查找"挂号医生"(registration_doctor)
|
||||
(type_code = 'registration_doctor' AND NOT EXISTS(SELECT 1
|
||||
FROM adm_encounter AS e
|
||||
WHERE e.status_enum = 2 -- IN_PROGRESS
|
||||
AND e.id = encounter_id))
|
||||
)
|
||||
) ep ON enc.id = ep.encounter_id AND ep.rn = 1
|
||||
-- 关联医生表
|
||||
LEFT JOIN adm_practitioner pr
|
||||
ON ep.practitioner_id = pr.id
|
||||
AND pr.delete_flag = '0'
|
||||
-- 关联收费项目表(挂号费)
|
||||
INNER JOIN adm_charge_item ci
|
||||
ON enc.id = ci.encounter_id
|
||||
AND ci.delete_flag = '0'
|
||||
AND ci.context_enum = 1 -- REGISTER(挂号)
|
||||
-- 关联支付对账表(已支付)
|
||||
INNER JOIN fin_payment_reconciliation prc
|
||||
ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0'
|
||||
AND prc.status_enum = 1 -- SUCCESS(支付成功)
|
||||
-- 排除已在队列中的患者(关键:LEFT JOIN + WHERE IS NULL)
|
||||
LEFT JOIN hisdev.triage_queue_item tqi
|
||||
ON enc.id = tqi.encounter_id
|
||||
AND tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED' -- 排除已完成的状态
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1 -- 门诊(AMB)
|
||||
AND enc.create_time::DATE = CURRENT_DATE -- 当日挂号(前端会过滤 registerTime)
|
||||
AND enc.status_enum != 6 -- 排除退号(statusEnum = -1 时)
|
||||
AND tqi.id IS NULL -- 关键:只查询不在队列中的患者
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 方案二:简化查询(仅核心字段,便于快速测试)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
pt.name AS patientName,
|
||||
org.id AS organizationId,
|
||||
org.name AS organizationName,
|
||||
hcs.name AS healthcareName,
|
||||
pr.name AS practitionerName,
|
||||
enc.create_time AS registerTime
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org ON enc.organization_id = org.id AND org.delete_flag = '0'
|
||||
LEFT JOIN adm_healthcare_service hcs ON enc.service_type_id = hcs.id AND hcs.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT encounter_id, practitioner_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0' AND type_code IN ('admitter', 'registration_doctor')
|
||||
) ep ON enc.id = ep.encounter_id AND ep.rn = 1
|
||||
LEFT JOIN adm_practitioner pr ON ep.practitioner_id = pr.id AND pr.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
-- 排除已在队列中的患者
|
||||
LEFT JOIN hisdev.triage_queue_item tqi
|
||||
ON enc.id = tqi.encounter_id
|
||||
AND tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED'
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6
|
||||
AND tqi.id IS NULL -- 不在队列中
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 方案三:分步查询(便于调试和理解)
|
||||
-- ============================================
|
||||
|
||||
-- 步骤1:查询当日已挂号的患者(不排除队列)
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
pt.name AS patientName,
|
||||
org.name AS organizationName,
|
||||
enc.create_time AS registerTime
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org ON enc.organization_id = org.id AND org.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6;
|
||||
|
||||
-- 步骤2:查询已在队列中的患者
|
||||
SELECT DISTINCT encounter_id
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND status != 'COMPLETED';
|
||||
|
||||
-- 步骤3:合并查询(使用 NOT EXISTS 或 NOT IN)
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
pt.name AS patientName,
|
||||
org.name AS organizationName,
|
||||
enc.create_time AS registerTime
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org ON enc.organization_id = org.id AND org.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6
|
||||
-- 排除已在队列中的患者
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM hisdev.triage_queue_item tqi
|
||||
WHERE tqi.encounter_id = enc.id
|
||||
AND tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED'
|
||||
)
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 查询条件说明
|
||||
-- ============================================
|
||||
-- 1. 基础条件:
|
||||
-- - enc.delete_flag = '0' -- 未删除
|
||||
-- - enc.class_enum = 1 -- 门诊(AMB)
|
||||
-- - ci.context_enum = 1 -- 挂号(REGISTER)
|
||||
-- - prc.status_enum = 1 -- 支付成功(SUCCESS)
|
||||
--
|
||||
-- 2. 日期过滤:
|
||||
-- - enc.create_time::DATE = CURRENT_DATE -- 当日挂号
|
||||
-- 注意:前端还会基于 registerTime 字段再次过滤今天的数据
|
||||
--
|
||||
-- 3. 状态过滤:
|
||||
-- - enc.status_enum != 6 -- 排除退号(对应前端 statusEnum = -1)
|
||||
--
|
||||
-- 4. 队列去重:
|
||||
-- - LEFT JOIN hisdev.triage_queue_item ... WHERE tqi.id IS NULL
|
||||
-- 或者
|
||||
-- - NOT EXISTS (SELECT 1 FROM hisdev.triage_queue_item ...)
|
||||
-- 排除已在队列中且状态不是 COMPLETED 的患者
|
||||
--
|
||||
-- 5. 租户过滤(如果需要):
|
||||
-- - enc.tenant_id = ? -- 当前登录用户的租户ID
|
||||
--
|
||||
-- 6. 模糊搜索(如果需要):
|
||||
-- - pt.name LIKE '%关键词%' -- 患者姓名
|
||||
-- - org.name LIKE '%关键词%' -- 科室名称
|
||||
-- - pr.name LIKE '%关键词%' -- 医生姓名
|
||||
-- - hcs.name LIKE '%关键词%' -- 号别名称
|
||||
|
||||
-- ============================================
|
||||
-- 调试查询(检查数据是否正确)
|
||||
-- ============================================
|
||||
|
||||
-- 1. 检查当日已挂号患者总数
|
||||
SELECT COUNT(*) AS total_registered
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6;
|
||||
|
||||
-- 2. 检查已在队列中的患者数量
|
||||
SELECT COUNT(DISTINCT encounter_id) AS in_queue_count
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND status != 'COMPLETED';
|
||||
|
||||
-- 3. 检查候选池应该显示的患者数量(已挂号 - 已在队列)
|
||||
SELECT
|
||||
(SELECT COUNT(*)
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6)
|
||||
-
|
||||
(SELECT COUNT(DISTINCT encounter_id)
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND status != 'COMPLETED')
|
||||
AS candidate_pool_count;
|
||||
|
||||
-- 4. 查看队列中的患者详情(用于对比)
|
||||
SELECT
|
||||
tqi.encounter_id,
|
||||
tqi.patient_name,
|
||||
tqi.status,
|
||||
tqi.queue_order
|
||||
FROM hisdev.triage_queue_item tqi
|
||||
WHERE tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED'
|
||||
ORDER BY tqi.queue_order;
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
-- ============================================
|
||||
-- 智能候选池查询SQL(完整版 - 包含排除列表)
|
||||
-- 页面:智能分诊排队管理 - 心内科
|
||||
-- 用途:查询"已签到未入队"的患者(当日已挂号,但未加入队列,且未在排除列表中)
|
||||
-- 更新日期:2025-01-15
|
||||
-- ============================================
|
||||
|
||||
-- ============================================
|
||||
-- 方案一:完整查询(包含所有字段,排除已在队列和排除列表中的患者)
|
||||
-- ============================================
|
||||
SELECT
|
||||
T9.tenant_id AS tenantId,
|
||||
T9.encounter_id AS encounterId,
|
||||
T9.display_order AS displayOrder,
|
||||
T9.organization_id AS organizationId,
|
||||
T9.organization_name AS organizationName,
|
||||
T9.healthcare_name AS healthcareName,
|
||||
T9.practitioner_user_id AS practitionerUserId,
|
||||
T9.practitioner_name AS practitionerName,
|
||||
T9.contract_name AS contractName,
|
||||
T9.patient_id AS patientId,
|
||||
T9.patient_name AS patientName,
|
||||
T9.phone,
|
||||
T9.gender_enum AS genderEnum,
|
||||
T9.id_card AS idCard,
|
||||
T9.status_enum AS statusEnum,
|
||||
T9.register_time AS registerTime,
|
||||
T9.total_price AS totalPrice,
|
||||
T9.account_name AS accountName,
|
||||
T9.enterer_name AS entererName,
|
||||
T9.charge_item_ids AS chargeItemIds,
|
||||
T9.payment_id AS paymentId,
|
||||
T9.picture_url AS pictureUrl,
|
||||
T9.birth_date AS birthDate,
|
||||
COALESCE(T9.identifier_no, T9.patient_bus_no, '') AS identifierNo,
|
||||
-- 计算年龄(前端会调用 parseAge 函数)
|
||||
CASE
|
||||
WHEN T9.birth_date IS NOT NULL THEN
|
||||
EXTRACT(YEAR FROM AGE(T9.birth_date))::TEXT || '岁'
|
||||
ELSE ''
|
||||
END AS age
|
||||
FROM (
|
||||
SELECT T1.tenant_id AS tenant_id,
|
||||
T1.id AS encounter_id,
|
||||
T1.display_order AS display_order,
|
||||
T1.organization_id AS organization_id,
|
||||
T2.NAME AS organization_name,
|
||||
T3.NAME AS healthcare_name,
|
||||
T5.user_id AS practitioner_user_id,
|
||||
T5.NAME AS practitioner_name,
|
||||
T7.contract_name AS contract_name,
|
||||
T8.id AS patient_id,
|
||||
T8.NAME AS patient_name,
|
||||
T8.phone AS phone,
|
||||
T8.gender_enum AS gender_enum,
|
||||
T8.id_card AS id_card,
|
||||
T1.status_enum AS status_enum,
|
||||
T1.create_time AS register_time,
|
||||
T10.total_price,
|
||||
T11."name" AS account_name,
|
||||
T12."name" AS enterer_name,
|
||||
T13.charge_item_ids,
|
||||
T13.id AS payment_id,
|
||||
ai.picture_url AS picture_url,
|
||||
T8.birth_date AS birth_date,
|
||||
T8.bus_no AS patient_bus_no,
|
||||
T18.identifier_no AS identifier_no
|
||||
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'
|
||||
LEFT JOIN (
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT *,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0'
|
||||
AND (
|
||||
(type_code = 'admitter' AND EXISTS(SELECT 1
|
||||
FROM adm_encounter AS T1
|
||||
WHERE T1.status_enum = 2 -- IN_PROGRESS
|
||||
AND T1.ID = encounter_id))
|
||||
OR
|
||||
(type_code = 'registration_doctor' AND NOT EXISTS(SELECT 1
|
||||
FROM adm_encounter AS T1
|
||||
WHERE T1.status_enum = 2 -- IN_PROGRESS
|
||||
AND T1.ID = encounter_id))
|
||||
)
|
||||
) t
|
||||
WHERE rn = 1
|
||||
) AS T4 ON T1.ID = T4.encounter_id
|
||||
LEFT JOIN adm_practitioner AS T5 ON T5.ID = T4.practitioner_id AND T5.delete_flag = '0'
|
||||
LEFT JOIN adm_account AS T6
|
||||
ON T1.ID = T6.encounter_id AND T6.delete_flag = '0' AND T6.encounter_flag = '1'
|
||||
LEFT JOIN fin_contract AS T7 ON T6.contract_no = T7.bus_no AND T7.delete_flag = '0'
|
||||
LEFT JOIN adm_patient AS T8 ON T1.patient_id = T8.ID AND T8.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT patient_id,
|
||||
identifier_no
|
||||
FROM (
|
||||
SELECT patient_id,
|
||||
identifier_no,
|
||||
ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_patient_identifier
|
||||
WHERE delete_flag = '0'
|
||||
AND identifier_no IS NOT NULL
|
||||
AND identifier_no != ''
|
||||
) t
|
||||
WHERE rn = 1
|
||||
) AS T18 ON T8.id = T18.patient_id
|
||||
LEFT JOIN adm_charge_item AS T10 ON T1.id = T10.encounter_id AND T10.delete_flag = '0'
|
||||
LEFT JOIN adm_account AS T11 ON T10.account_id = T11.id AND T11.delete_flag = '0'
|
||||
LEFT JOIN adm_practitioner AS T12 ON T12.ID = T10.enterer_id AND T12.delete_flag = '0'
|
||||
LEFT JOIN fin_payment_reconciliation T13
|
||||
ON T10.id::TEXT = ANY(string_to_array(T13.charge_item_ids,','))
|
||||
AND T13.delete_flag = '0'
|
||||
AND T13.status_enum = 1 -- SUCCESS(支付成功)
|
||||
LEFT JOIN adm_invoice AS ai
|
||||
ON ai.reconciliation_id = T13.id AND ai.delete_flag = '0'
|
||||
WHERE T1.delete_flag = '0'
|
||||
AND T1.class_enum = 1 -- 门诊(AMB)
|
||||
AND T1.create_time::DATE = CURRENT_DATE -- 当日挂号
|
||||
AND T1.status_enum != 6 -- 排除退号
|
||||
AND T10.context_enum = 1 -- REGISTER(挂号)
|
||||
) AS T9
|
||||
-- 排除已在队列中的患者(关键:LEFT JOIN + WHERE IS NULL)
|
||||
LEFT JOIN hisdev.triage_queue_item tqi
|
||||
ON T9.encounter_id = tqi.encounter_id
|
||||
AND tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED' -- 排除已完成的状态
|
||||
-- 排除已在排除列表中的患者(新增:关键:LEFT JOIN + WHERE IS NULL)
|
||||
LEFT JOIN hisdev.triage_candidate_exclusion tce
|
||||
ON T9.encounter_id = tce.encounter_id
|
||||
AND tce.delete_flag = '0'
|
||||
AND tce.exclusion_date = CURRENT_DATE
|
||||
AND tce.tenant_id = T9.tenant_id -- 按租户过滤
|
||||
WHERE tqi.id IS NULL -- 关键:只查询不在队列中的患者
|
||||
AND tce.id IS NULL -- 关键:只查询不在排除列表中的患者
|
||||
ORDER BY T9.register_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 方案二:简化查询(仅核心字段,便于快速测试)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
pt.name AS patientName,
|
||||
org.id AS organizationId,
|
||||
org.name AS organizationName,
|
||||
hcs.name AS healthcareName,
|
||||
pr.name AS practitionerName,
|
||||
enc.create_time AS registerTime
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org ON enc.organization_id = org.id AND org.delete_flag = '0'
|
||||
LEFT JOIN adm_healthcare_service hcs ON enc.service_type_id = hcs.id AND hcs.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT encounter_id, practitioner_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0' AND type_code IN ('admitter', 'registration_doctor')
|
||||
) ep ON enc.id = ep.encounter_id AND ep.rn = 1
|
||||
LEFT JOIN adm_practitioner pr ON ep.practitioner_id = pr.id AND pr.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
-- 排除已在队列中的患者
|
||||
LEFT JOIN hisdev.triage_queue_item tqi
|
||||
ON enc.id = tqi.encounter_id
|
||||
AND tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED'
|
||||
-- 排除已在排除列表中的患者(新增)
|
||||
LEFT JOIN hisdev.triage_candidate_exclusion tce
|
||||
ON enc.id = tce.encounter_id
|
||||
AND tce.delete_flag = '0'
|
||||
AND tce.exclusion_date = CURRENT_DATE
|
||||
AND tce.tenant_id = enc.tenant_id
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6
|
||||
AND tqi.id IS NULL -- 不在队列中
|
||||
AND tce.id IS NULL -- 不在排除列表中
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 方案三:使用 NOT EXISTS 子查询(性能可能更好)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
pt.name AS patientName,
|
||||
org.id AS organizationId,
|
||||
org.name AS organizationName,
|
||||
hcs.name AS healthcareName,
|
||||
pr.name AS practitionerName,
|
||||
enc.create_time AS registerTime
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org ON enc.organization_id = org.id AND org.delete_flag = '0'
|
||||
LEFT JOIN adm_healthcare_service hcs ON enc.service_type_id = hcs.id AND hcs.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT encounter_id, practitioner_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0' AND type_code IN ('admitter', 'registration_doctor')
|
||||
) ep ON enc.id = ep.encounter_id AND ep.rn = 1
|
||||
LEFT JOIN adm_practitioner pr ON ep.practitioner_id = pr.id AND pr.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6
|
||||
-- 排除已在队列中的患者
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM hisdev.triage_queue_item tqi
|
||||
WHERE tqi.encounter_id = enc.id
|
||||
AND tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED'
|
||||
)
|
||||
-- 排除已在排除列表中的患者(新增)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM hisdev.triage_candidate_exclusion tce
|
||||
WHERE tce.encounter_id = enc.id
|
||||
AND tce.delete_flag = '0'
|
||||
AND tce.exclusion_date = CURRENT_DATE
|
||||
AND tce.tenant_id = enc.tenant_id
|
||||
)
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 查询条件说明
|
||||
-- ============================================
|
||||
-- 1. 基础条件:
|
||||
-- - enc.delete_flag = '0' -- 未删除
|
||||
-- - enc.class_enum = 1 -- 门诊(AMB)
|
||||
-- - ci.context_enum = 1 -- 挂号(REGISTER)
|
||||
-- - prc.status_enum = 1 -- 支付成功(SUCCESS)
|
||||
--
|
||||
-- 2. 日期过滤:
|
||||
-- - enc.create_time::DATE = CURRENT_DATE -- 当日挂号
|
||||
-- 注意:前端还会基于 registerTime 字段再次过滤今天的数据
|
||||
--
|
||||
-- 3. 状态过滤:
|
||||
-- - enc.status_enum != 6 -- 排除退号(对应前端 statusEnum = -1)
|
||||
--
|
||||
-- 4. 队列去重:
|
||||
-- - LEFT JOIN hisdev.triage_queue_item ... WHERE tqi.id IS NULL
|
||||
-- 或者
|
||||
-- - NOT EXISTS (SELECT 1 FROM hisdev.triage_queue_item ...)
|
||||
-- 排除已在队列中且状态不是 COMPLETED 的患者
|
||||
--
|
||||
-- 5. 排除列表去重(新增):
|
||||
-- - LEFT JOIN hisdev.triage_candidate_exclusion ... WHERE tce.id IS NULL
|
||||
-- 或者
|
||||
-- - NOT EXISTS (SELECT 1 FROM hisdev.triage_candidate_exclusion ...)
|
||||
-- 排除已在排除列表中的患者(已加入队列后被记录)
|
||||
--
|
||||
-- 6. 租户过滤(如果需要):
|
||||
-- - enc.tenant_id = ? -- 当前登录用户的租户ID
|
||||
--
|
||||
-- 7. 模糊搜索(如果需要):
|
||||
-- - pt.name LIKE '%关键词%' -- 患者姓名
|
||||
-- - org.name LIKE '%关键词%' -- 科室名称
|
||||
-- - pr.name LIKE '%关键词%' -- 医生姓名
|
||||
-- - hcs.name LIKE '%关键词%' -- 号别名称
|
||||
|
||||
-- ============================================
|
||||
-- 调试查询(检查数据是否正确)
|
||||
-- ============================================
|
||||
|
||||
-- 1. 检查当日已挂号患者总数
|
||||
SELECT COUNT(*) AS total_registered
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6;
|
||||
|
||||
-- 2. 检查已在队列中的患者数量
|
||||
SELECT COUNT(DISTINCT encounter_id) AS in_queue_count
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND status != 'COMPLETED';
|
||||
|
||||
-- 3. 检查已在排除列表中的患者数量(新增)
|
||||
SELECT COUNT(DISTINCT encounter_id) AS in_exclusion_count
|
||||
FROM hisdev.triage_candidate_exclusion
|
||||
WHERE delete_flag = '0'
|
||||
AND exclusion_date = CURRENT_DATE;
|
||||
|
||||
-- 4. 检查候选池应该显示的患者数量(已挂号 - 已在队列 - 已在排除列表)
|
||||
SELECT
|
||||
(SELECT COUNT(*)
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6)
|
||||
-
|
||||
(SELECT COUNT(DISTINCT encounter_id)
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND status != 'COMPLETED')
|
||||
-
|
||||
(SELECT COUNT(DISTINCT encounter_id)
|
||||
FROM hisdev.triage_candidate_exclusion
|
||||
WHERE delete_flag = '0'
|
||||
AND exclusion_date = CURRENT_DATE)
|
||||
AS candidate_pool_count;
|
||||
|
||||
-- 5. 查看队列中的患者详情(用于对比)
|
||||
SELECT
|
||||
tqi.encounter_id,
|
||||
tqi.patient_name,
|
||||
tqi.status,
|
||||
tqi.queue_order
|
||||
FROM hisdev.triage_queue_item tqi
|
||||
WHERE tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED'
|
||||
ORDER BY tqi.queue_order;
|
||||
|
||||
-- 6. 查看排除列表中的患者详情(新增)
|
||||
SELECT
|
||||
tce.encounter_id,
|
||||
tce.patient_name,
|
||||
tce.reason,
|
||||
tce.create_time
|
||||
FROM hisdev.triage_candidate_exclusion tce
|
||||
WHERE tce.delete_flag = '0'
|
||||
AND tce.exclusion_date = CURRENT_DATE
|
||||
ORDER BY tce.create_time DESC;
|
||||
|
||||
-- 7. 查看重叠的患者(既在队列中,又在排除列表中)
|
||||
SELECT
|
||||
tqi.encounter_id,
|
||||
tqi.patient_name AS queue_patient_name,
|
||||
tce.patient_name AS exclusion_patient_name,
|
||||
tqi.status,
|
||||
tce.reason
|
||||
FROM hisdev.triage_queue_item tqi
|
||||
INNER JOIN hisdev.triage_candidate_exclusion tce
|
||||
ON tqi.encounter_id = tce.encounter_id
|
||||
AND tqi.queue_date = tce.exclusion_date
|
||||
AND tqi.tenant_id = tce.tenant_id
|
||||
WHERE tqi.delete_flag = '0'
|
||||
AND tce.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
ORDER BY tqi.encounter_id;
|
||||
|
||||
86
openhis-server-new/sql/quick_check_pricing_flag_issue.sql
Normal file
86
openhis-server-new/sql/quick_check_pricing_flag_issue.sql
Normal file
@@ -0,0 +1,86 @@
|
||||
-- ============================================
|
||||
-- 快速检查 pricing_flag = '0' 的数据问题
|
||||
-- 注意:pricing_flag 是 char(1) 类型,所以要用字符串 '0'
|
||||
-- ============================================
|
||||
|
||||
-- 问题1:检查是否有费用定价但 pricing_flag = '0' 的项目(可能异常)
|
||||
SELECT
|
||||
'异常:有费用定价但不允许划价' AS issue_type,
|
||||
T1.id,
|
||||
T1.bus_no,
|
||||
T1.name AS activity_name,
|
||||
T1.pricing_flag,
|
||||
T2.charge_name,
|
||||
T2.price
|
||||
FROM wor_activity_definition AS T1
|
||||
INNER JOIN adm_charge_item_definition AS T2
|
||||
ON T2.instance_id = T1.id
|
||||
AND T2.delete_flag = '0'
|
||||
AND T2.status_enum = 1
|
||||
AND T2.instance_table = 'wor_activity_definition'
|
||||
WHERE T1.delete_flag = '0'
|
||||
AND T1.status_enum = 1
|
||||
AND T1.pricing_flag = '0' -- 不允许划价,但有费用定价(矛盾)
|
||||
ORDER BY T1.id;
|
||||
|
||||
-- 问题2:检查是否已被使用但 pricing_flag = '0' 的项目(可能异常)
|
||||
SELECT
|
||||
'异常:已有收费记录但不允许划价' AS issue_type,
|
||||
T1.id,
|
||||
T1.bus_no,
|
||||
T1.name AS activity_name,
|
||||
T1.pricing_flag,
|
||||
COUNT(DISTINCT T3.encounter_id) AS encounter_count,
|
||||
COUNT(T3.id) AS charge_item_count
|
||||
FROM wor_activity_definition AS T1
|
||||
INNER JOIN adm_charge_item AS T3
|
||||
ON T3.product_id = T1.id
|
||||
AND T3.context_enum = 3 -- ACTIVITY
|
||||
AND T3.delete_flag = '0'
|
||||
WHERE T1.delete_flag = '0'
|
||||
AND T1.pricing_flag = '0'
|
||||
GROUP BY T1.id, T1.bus_no, T1.name, T1.pricing_flag
|
||||
ORDER BY charge_item_count DESC;
|
||||
|
||||
-- 问题3:查看所有 pricing_flag = '0' 的项目,判断哪些可能应该改为 '1'
|
||||
SELECT
|
||||
T1.id,
|
||||
T1.bus_no,
|
||||
T1.name AS activity_name,
|
||||
T1.category_code,
|
||||
T1.pricing_flag,
|
||||
T1.status_enum,
|
||||
CASE
|
||||
WHEN T2.id IS NOT NULL THEN '有费用定价'
|
||||
ELSE '无费用定价'
|
||||
END AS has_charge_definition,
|
||||
CASE
|
||||
WHEN EXISTS (
|
||||
SELECT 1 FROM adm_charge_item
|
||||
WHERE product_id = T1.id
|
||||
AND context_enum = 3
|
||||
AND delete_flag = '0'
|
||||
) THEN '已被使用'
|
||||
ELSE '未使用'
|
||||
END AS usage_status,
|
||||
CASE
|
||||
WHEN T2.id IS NOT NULL OR EXISTS (
|
||||
SELECT 1 FROM adm_charge_item
|
||||
WHERE product_id = T1.id
|
||||
AND context_enum = 3
|
||||
AND delete_flag = '0'
|
||||
) THEN '建议改为 pricing_flag = ''1'''
|
||||
ELSE '保持 pricing_flag = ''0''(可能正常)'
|
||||
END AS suggestion
|
||||
FROM wor_activity_definition AS T1
|
||||
LEFT JOIN adm_charge_item_definition AS T2
|
||||
ON T2.instance_id = T1.id
|
||||
AND T2.delete_flag = '0'
|
||||
AND T2.status_enum = 1
|
||||
AND T2.instance_table = 'wor_activity_definition'
|
||||
WHERE T1.delete_flag = '0'
|
||||
AND T1.status_enum = 1
|
||||
AND T1.pricing_flag = '0'
|
||||
ORDER BY T1.id;
|
||||
|
||||
|
||||
123
openhis-server-new/sql/update_pricing_flag_to_one.sql
Normal file
123
openhis-server-new/sql/update_pricing_flag_to_one.sql
Normal file
@@ -0,0 +1,123 @@
|
||||
-- ============================================
|
||||
-- 将 wor_activity_definition 表中的一两条记录的 pricing_flag 改成 '1'
|
||||
-- 注意:pricing_flag 是 char(1) 类型,所以要用字符串 '1'
|
||||
-- ============================================
|
||||
|
||||
-- 方式1:更新前两条 pricing_flag = '0' 的记录(推荐)
|
||||
UPDATE wor_activity_definition
|
||||
SET pricing_flag = '1',
|
||||
update_time = CURRENT_TIMESTAMP
|
||||
WHERE id IN (
|
||||
SELECT id
|
||||
FROM wor_activity_definition
|
||||
WHERE delete_flag = '0'
|
||||
AND pricing_flag = '0'
|
||||
AND status_enum = 1 -- 只更新激活状态的项目
|
||||
ORDER BY id
|
||||
LIMIT 2
|
||||
);
|
||||
|
||||
-- 方式2:更新指定 ID 的记录(更精确,推荐使用)
|
||||
-- 请将下面的 ID 替换为实际要更新的记录 ID
|
||||
UPDATE wor_activity_definition
|
||||
SET pricing_flag = '1',
|
||||
update_time = CURRENT_TIMESTAMP
|
||||
WHERE id IN (1906532604116348929, 1906544768434053121) -- 替换为实际的 ID
|
||||
AND delete_flag = '0';
|
||||
|
||||
-- 方式3:更新前两条记录(不管当前 pricing_flag 值是什么)
|
||||
UPDATE wor_activity_definition
|
||||
SET pricing_flag = '1',
|
||||
update_time = CURRENT_TIMESTAMP
|
||||
WHERE id IN (
|
||||
SELECT id
|
||||
FROM wor_activity_definition
|
||||
WHERE delete_flag = '0'
|
||||
AND status_enum = 1
|
||||
ORDER BY id
|
||||
LIMIT 2
|
||||
);
|
||||
|
||||
-- 方式4:更新有费用定价但 pricing_flag = '0' 的前两条记录(业务逻辑更合理)
|
||||
UPDATE wor_activity_definition
|
||||
SET pricing_flag = '1',
|
||||
update_time = CURRENT_TIMESTAMP
|
||||
WHERE id IN (
|
||||
SELECT wad.id
|
||||
FROM wor_activity_definition wad
|
||||
INNER JOIN adm_charge_item_definition acid
|
||||
ON acid.instance_id = wad.id
|
||||
AND acid.delete_flag = '0'
|
||||
AND acid.status_enum = 1
|
||||
AND acid.instance_table = 'wor_activity_definition'
|
||||
WHERE wad.delete_flag = '0'
|
||||
AND wad.status_enum = 1
|
||||
AND wad.pricing_flag = '0'
|
||||
ORDER BY wad.id
|
||||
LIMIT 2
|
||||
);
|
||||
|
||||
-- ============================================
|
||||
-- 执行前可以先查询要更新的记录(验证用)
|
||||
-- ============================================
|
||||
|
||||
-- 查询前两条 pricing_flag = '0' 的记录
|
||||
SELECT
|
||||
id,
|
||||
bus_no,
|
||||
name AS activity_name,
|
||||
pricing_flag,
|
||||
status_enum,
|
||||
create_time,
|
||||
update_time
|
||||
FROM wor_activity_definition
|
||||
WHERE delete_flag = '0'
|
||||
AND pricing_flag = '0'
|
||||
AND status_enum = 1
|
||||
ORDER BY id
|
||||
LIMIT 2;
|
||||
|
||||
-- 查询有费用定价但 pricing_flag = '0' 的记录
|
||||
SELECT
|
||||
wad.id,
|
||||
wad.bus_no,
|
||||
wad.name AS activity_name,
|
||||
wad.pricing_flag,
|
||||
acid.charge_name,
|
||||
acid.price
|
||||
FROM wor_activity_definition wad
|
||||
INNER JOIN adm_charge_item_definition acid
|
||||
ON acid.instance_id = wad.id
|
||||
AND acid.delete_flag = '0'
|
||||
AND acid.status_enum = 1
|
||||
AND acid.instance_table = 'wor_activity_definition'
|
||||
WHERE wad.delete_flag = '0'
|
||||
AND wad.status_enum = 1
|
||||
AND wad.pricing_flag = '0'
|
||||
ORDER BY wad.id
|
||||
LIMIT 2;
|
||||
|
||||
-- ============================================
|
||||
-- 执行后验证更新结果
|
||||
-- ============================================
|
||||
|
||||
-- 验证更新是否成功
|
||||
SELECT
|
||||
id,
|
||||
bus_no,
|
||||
name AS activity_name,
|
||||
pricing_flag,
|
||||
update_time
|
||||
FROM wor_activity_definition
|
||||
WHERE id IN (
|
||||
-- 这里放刚才更新的 ID,或者用子查询
|
||||
SELECT id
|
||||
FROM wor_activity_definition
|
||||
WHERE delete_flag = '0'
|
||||
AND pricing_flag = '1'
|
||||
AND status_enum = 1
|
||||
ORDER BY update_time DESC
|
||||
LIMIT 2
|
||||
);
|
||||
|
||||
|
||||
86
openhis-server-new/sql/快速诊断诊疗项目问题.sql
Normal file
86
openhis-server-new/sql/快速诊断诊疗项目问题.sql
Normal file
@@ -0,0 +1,86 @@
|
||||
-- 快速诊断诊疗项目检索问题
|
||||
-- 已知:wor_activity_definition 表中有4条数据
|
||||
-- 关键:ChargeItemContext.ACTIVITY.getValue() = 3(整数)
|
||||
|
||||
-- 步骤1:检查这4条诊疗项目定义的delete_flag状态
|
||||
SELECT
|
||||
id,
|
||||
name,
|
||||
delete_flag,
|
||||
CASE WHEN delete_flag = '0' THEN '✅ 可用' ELSE '❌ 已删除' END as status
|
||||
FROM wor_activity_definition
|
||||
ORDER BY id;
|
||||
|
||||
-- 步骤2:检查收费项目中是否有诊疗项目类型(context_enum = 3)的记录
|
||||
SELECT
|
||||
COUNT(*) as total_count,
|
||||
COUNT(CASE WHEN delete_flag = '0' THEN 1 END) as active_count
|
||||
FROM adm_charge_item
|
||||
WHERE (context_enum::text = '3' OR context_enum = 3);
|
||||
|
||||
-- 步骤3:检查收费项目能否匹配到诊疗项目定义(最关键!)
|
||||
SELECT
|
||||
aci.id as charge_item_id,
|
||||
aci.encounter_id,
|
||||
aci.product_id,
|
||||
aci.status_enum,
|
||||
wad.id as activity_def_id,
|
||||
wad.name as activity_name,
|
||||
CASE
|
||||
WHEN wad.id IS NULL THEN '❌ 无法匹配:product_id=' || aci.product_id || ' 在诊疗项目定义表中不存在'
|
||||
WHEN wad.delete_flag != '0' THEN '❌ 无法匹配:诊疗项目定义已删除'
|
||||
WHEN aci.delete_flag != '0' THEN '❌ 无法匹配:收费项目已删除'
|
||||
WHEN aci.status_enum NOT IN (1,2,3,4,5,6) THEN '❌ 无法匹配:状态不在查询范围内 status=' || aci.status_enum
|
||||
ELSE '✅ 可以匹配'
|
||||
END as match_result
|
||||
FROM adm_charge_item aci
|
||||
LEFT JOIN wor_activity_definition wad
|
||||
ON aci.product_id = wad.id AND wad.delete_flag = '0'
|
||||
WHERE (aci.context_enum::text = '3' OR aci.context_enum = 3)
|
||||
AND aci.delete_flag = '0'
|
||||
LIMIT 50;
|
||||
|
||||
-- 步骤4:统计匹配情况
|
||||
SELECT
|
||||
COUNT(*) as total_charge_items,
|
||||
COUNT(CASE WHEN wad.id IS NOT NULL AND wad.delete_flag = '0' THEN 1 END) as matched_count,
|
||||
COUNT(CASE WHEN wad.id IS NULL THEN 1 END) as unmatched_def_count,
|
||||
COUNT(CASE WHEN wad.delete_flag != '0' THEN 1 END) as deleted_def_count,
|
||||
COUNT(CASE WHEN wad.id IS NOT NULL AND wad.delete_flag = '0' AND aci.status_enum IN (1,2,3,4,5,6) THEN 1 END) as final_matched_count
|
||||
FROM adm_charge_item aci
|
||||
LEFT JOIN wor_activity_definition wad
|
||||
ON aci.product_id = wad.id
|
||||
WHERE (aci.context_enum::text = '3' OR aci.context_enum = 3)
|
||||
AND aci.delete_flag = '0';
|
||||
|
||||
-- 步骤5:检查具体就诊的诊疗项目(如果有encounterId,取消注释并替换)
|
||||
-- SELECT
|
||||
-- T1.encounter_id,
|
||||
-- T1.id as charge_item_id,
|
||||
-- T1.product_id,
|
||||
-- T1.status_enum,
|
||||
-- T2.id as activity_def_id,
|
||||
-- T2.name as activity_name,
|
||||
-- CASE
|
||||
-- WHEN T2.id IS NULL THEN '❌ 无法匹配'
|
||||
-- WHEN T2.delete_flag != '0' THEN '❌ 定义已删除'
|
||||
-- WHEN T1.status_enum NOT IN (1,2,3,4,5,6) THEN '❌ 状态不符合'
|
||||
-- ELSE '✅ 正常'
|
||||
-- END as status
|
||||
-- FROM adm_charge_item AS T1
|
||||
-- LEFT JOIN wor_activity_definition AS T2
|
||||
-- ON T1.product_id = T2.id AND T2.delete_flag = '0'
|
||||
-- WHERE T1.encounter_id = :encounterId -- 替换为实际的encounterId
|
||||
-- AND (T1.context_enum::text = '3' OR T1.context_enum = 3)
|
||||
-- AND T1.delete_flag = '0';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
165
openhis-server-new/sql/诊断门诊划价检索不出诊疗项目问题.md
Normal file
165
openhis-server-new/sql/诊断门诊划价检索不出诊疗项目问题.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# 诊断:门诊划价检索不出诊疗项目问题
|
||||
|
||||
## 问题描述
|
||||
在门诊划价页面,检索不出诊疗项目,显示"暂无数据"。
|
||||
|
||||
## 问题分析
|
||||
|
||||
### SQL查询逻辑
|
||||
根据 `OutpatientChargeAppMapper.xml` 中的 `selectEncounterPatientPrescription` 查询:
|
||||
|
||||
1. **查询主表**:`adm_charge_item`(收费项目表)
|
||||
2. **关联诊疗项目定义表**:`wor_activity_definition`
|
||||
- 关联条件:`T1.context_enum = #{activity}` AND `T1.product_id = T2.id` AND `T2.delete_flag = '0'`
|
||||
3. **返回字段**:`item_name` 来自 `wor_activity_definition.name`
|
||||
|
||||
### 可能的原因
|
||||
|
||||
#### 1. 数据库中没有诊疗项目定义数据
|
||||
- **检查**:`wor_activity_definition` 表中是否有数据
|
||||
- **SQL**:
|
||||
```sql
|
||||
SELECT COUNT(*) FROM wor_activity_definition WHERE delete_flag = '0';
|
||||
```
|
||||
|
||||
#### 2. 收费项目中的product_id无法匹配到诊疗项目定义
|
||||
- **检查**:`adm_charge_item` 表中的 `product_id` 是否存在于 `wor_activity_definition` 表中
|
||||
- **SQL**:
|
||||
```sql
|
||||
SELECT
|
||||
aci.id,
|
||||
aci.encounter_id,
|
||||
aci.product_id,
|
||||
aci.context_enum,
|
||||
wad.id as activity_def_id,
|
||||
wad.name as activity_name
|
||||
FROM adm_charge_item aci
|
||||
LEFT JOIN wor_activity_definition wad ON aci.product_id = wad.id AND wad.delete_flag = '0'
|
||||
WHERE aci.context_enum = 'ACTIVITY'
|
||||
AND aci.delete_flag = '0'
|
||||
AND wad.id IS NULL; -- 无法匹配的记录
|
||||
```
|
||||
|
||||
#### 3. 诊疗项目定义被标记为删除
|
||||
- **检查**:`wor_activity_definition` 表中的 `delete_flag` 是否为 '0'
|
||||
- **SQL**:
|
||||
```sql
|
||||
SELECT
|
||||
id,
|
||||
name,
|
||||
delete_flag
|
||||
FROM wor_activity_definition
|
||||
WHERE delete_flag != '0';
|
||||
```
|
||||
|
||||
#### 4. context_enum 值不匹配
|
||||
- **检查**:`adm_charge_item` 表中的 `context_enum` 值是否正确
|
||||
- **SQL**:
|
||||
```sql
|
||||
SELECT DISTINCT context_enum FROM adm_charge_item WHERE delete_flag = '0';
|
||||
```
|
||||
- **注意**:后端代码中传入的 `#{activity}` 参数值应该是 `ChargeItemContext.ACTIVITY.getValue()`
|
||||
|
||||
## 诊断步骤
|
||||
|
||||
### 步骤1:检查诊疗项目定义表是否有数据
|
||||
```sql
|
||||
SELECT
|
||||
COUNT(*) as total_count,
|
||||
COUNT(CASE WHEN delete_flag = '0' THEN 1 END) as active_count,
|
||||
COUNT(CASE WHEN delete_flag != '0' THEN 1 END) as deleted_count
|
||||
FROM wor_activity_definition;
|
||||
```
|
||||
|
||||
**预期结果**:`active_count` 应该 > 0
|
||||
|
||||
### 步骤2:检查收费项目中的诊疗项目是否能匹配到定义
|
||||
```sql
|
||||
SELECT
|
||||
COUNT(*) as total_charge_items,
|
||||
COUNT(CASE WHEN wad.id IS NOT NULL THEN 1 END) as matched_count,
|
||||
COUNT(CASE WHEN wad.id IS NULL THEN 1 END) as unmatched_count
|
||||
FROM adm_charge_item aci
|
||||
LEFT JOIN wor_activity_definition wad
|
||||
ON aci.context_enum = 'ACTIVITY'
|
||||
AND aci.product_id = wad.id
|
||||
AND wad.delete_flag = '0'
|
||||
WHERE aci.context_enum = 'ACTIVITY'
|
||||
AND aci.delete_flag = '0';
|
||||
```
|
||||
|
||||
**预期结果**:`matched_count` 应该 > 0,`unmatched_count` 应该 = 0
|
||||
|
||||
### 步骤3:检查具体就诊的诊疗项目
|
||||
```sql
|
||||
-- 替换 :encounterId 为实际的就诊ID
|
||||
SELECT
|
||||
T1.encounter_id,
|
||||
T1.id as charge_item_id,
|
||||
T1.context_enum,
|
||||
T1.product_id,
|
||||
T1.status_enum,
|
||||
T2.id as activity_def_id,
|
||||
T2.name as activity_name,
|
||||
T2.delete_flag as activity_delete_flag,
|
||||
CASE
|
||||
WHEN T2.id IS NULL THEN '诊疗项目定义不存在或已删除'
|
||||
WHEN T2.delete_flag != '0' THEN '诊疗项目定义已删除'
|
||||
ELSE '正常'
|
||||
END as status
|
||||
FROM adm_charge_item AS T1
|
||||
LEFT JOIN wor_activity_definition AS T2
|
||||
ON T1.context_enum = 'ACTIVITY'
|
||||
AND T1.product_id = T2.id
|
||||
WHERE T1.encounter_id = :encounterId -- 替换为实际的encounterId
|
||||
AND T1.context_enum = 'ACTIVITY'
|
||||
AND T1.delete_flag = '0'
|
||||
AND T1.status_enum IN (1, 2, 3, 4, 5, 6);
|
||||
```
|
||||
|
||||
### 步骤4:检查后端传入的参数值
|
||||
检查 `OutpatientChargeAppServiceImpl.java` 中:
|
||||
```java
|
||||
ChargeItemContext.ACTIVITY.getValue()
|
||||
```
|
||||
确认这个值是什么(应该是 'ACTIVITY' 或对应的枚举值)
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 如果是数据问题:
|
||||
1. **缺少诊疗项目定义数据**:
|
||||
- 需要在 `wor_activity_definition` 表中添加诊疗项目数据
|
||||
- 或者在系统管理-目录管理-诊疗项目中添加
|
||||
|
||||
2. **product_id无法匹配**:
|
||||
- 检查 `adm_charge_item` 表中的 `product_id` 是否正确
|
||||
- 检查 `wor_activity_definition` 表中的 `id` 是否与 `product_id` 匹配
|
||||
|
||||
3. **delete_flag不正确**:
|
||||
- 将 `wor_activity_definition` 表中需要使用的记录的 `delete_flag` 设置为 '0'
|
||||
|
||||
### 如果是代码问题:
|
||||
1. **context_enum值不匹配**:
|
||||
- 检查后端代码中 `ChargeItemContext.ACTIVITY.getValue()` 返回的值
|
||||
- 确保与数据库中的 `context_enum` 值一致
|
||||
|
||||
2. **SQL查询条件错误**:
|
||||
- 检查 SQL 中的关联条件是否正确
|
||||
- 检查是否有其他过滤条件导致数据被过滤掉
|
||||
|
||||
## 快速诊断SQL
|
||||
运行以下SQL可以快速诊断问题:
|
||||
```sql
|
||||
-- 见 diagnose_treatment_items_issue.sql 文件
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
33
openhis-ui-vue3/src/api/system/lisConfig.js
Normal file
33
openhis-ui-vue3/src/api/system/lisConfig.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function getLisConfigPage(query) {
|
||||
return request({
|
||||
url: '/inspection/lisConfig/init-page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function getLisConfigDetail(id) {
|
||||
return request({
|
||||
url: '/inspection/lisConfig/info-detail',
|
||||
method: 'get',
|
||||
params: { id }
|
||||
})
|
||||
}
|
||||
|
||||
export function getLisConfigList(searchKey, type) {
|
||||
return request({
|
||||
url: '/inspection/lisConfig/init-list',
|
||||
method: 'get',
|
||||
params: { searchKey, type }
|
||||
})
|
||||
}
|
||||
|
||||
export function saveLisConfig(data) {
|
||||
return request({
|
||||
url: '/inspection/lisConfig/saveAll',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
48
openhis-ui-vue3/src/api/system/observation.js
Normal file
48
openhis-ui-vue3/src/api/system/observation.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function getObservationInit() {
|
||||
return request({
|
||||
url: '/inspection/observation/init',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getObservationPage(query) {
|
||||
return request({
|
||||
url: '/inspection/observation/information-page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function getObservationOne(id) {
|
||||
return request({
|
||||
url: '/inspection/observation/information-one',
|
||||
method: 'get',
|
||||
params: { id }
|
||||
})
|
||||
}
|
||||
|
||||
export function addObservation(data) {
|
||||
return request({
|
||||
url: '/inspection/observation/information',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateObservation(data) {
|
||||
return request({
|
||||
url: '/inspection/observation/information',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteObservation(ids) {
|
||||
return request({
|
||||
url: '/inspection/observation/information-status',
|
||||
method: 'post',
|
||||
data: { ids, type: '停用' }
|
||||
})
|
||||
}
|
||||
@@ -33,18 +33,32 @@
|
||||
:class="{ actived: activeCardId === item.encounterId }"
|
||||
>
|
||||
<div class="patient-card-header">
|
||||
<div class="header-top">
|
||||
<div class="bed-container">
|
||||
<div class="bed">
|
||||
<div class="bed-info">
|
||||
<div v-if="item.houseName" class="house-name">{{ item.houseName }}</div>
|
||||
<div class="bed-name">{{ item.bedName || '未分床' }}</div>
|
||||
<!-- 第1行:姓名 -->
|
||||
<div class="info-row name-row">
|
||||
<div class="name">
|
||||
<el-text :text="item.patientName" tclass="name" width="auto">
|
||||
{{ item.patientName || '-' }}
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-space>
|
||||
|
||||
<!-- 第2行:性别 年龄 + 入院状态 -->
|
||||
<div class="info-row gender-age-row">
|
||||
<div class="age">
|
||||
<el-tag
|
||||
size="small"
|
||||
class="age-tag"
|
||||
effect="plain"
|
||||
:class="{ 'age-tag-female': item.genderEnum_enumText === '女性' }"
|
||||
>
|
||||
{{ item.genderEnum_enumText || '-' }}
|
||||
<span v-if="item.age"> · {{ item.age }}</span>
|
||||
</el-tag>
|
||||
</div>
|
||||
|
||||
<!-- 入院状态放在性别年龄旁边 -->
|
||||
<div class="status-inline" v-if="item.statusEnum_enumText">
|
||||
<el-tag
|
||||
v-if="item.statusEnum_enumText"
|
||||
size="small"
|
||||
class="payer-tag-status"
|
||||
effect="light"
|
||||
@@ -52,22 +66,31 @@
|
||||
>
|
||||
{{ item.statusEnum_enumText }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第3行:房间号-分床状态 -->
|
||||
<div class="info-row room-bed-row">
|
||||
<div class="bed-info">
|
||||
<div v-if="item.houseName" class="house-name">{{ item.houseName }}</div>
|
||||
<div class="bed-name">{{ item.bedName || '未分床' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第4行:住院号 -->
|
||||
<div class="info-row busno-row">
|
||||
<span class="bus-no">住院号:{{ item.busNo || '-' }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 第5行:居民保险类型 -->
|
||||
<div class="info-row insurance-row" v-if="item.contractName">
|
||||
<el-tag
|
||||
v-if="item.contractName"
|
||||
size="small"
|
||||
class="payer-tag"
|
||||
effect="light"
|
||||
>
|
||||
{{ item.contractName }}
|
||||
</el-tag>
|
||||
</el-space>
|
||||
</div>
|
||||
|
||||
<div class="header-bottom">
|
||||
<span class="bus-no">住院号:{{ item.busNo || '-' }}</span>
|
||||
<span class="insurance-type" v-if="item.insutype_dictText">
|
||||
险种类型:{{ item.insutype_dictText }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -360,7 +383,7 @@ watch(
|
||||
<style lang="scss" scoped>
|
||||
.patient-card {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
overflow: visible; /* 改为visible以确保内容可见 */
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.04);
|
||||
@@ -383,50 +406,113 @@ watch(
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px 12px 4px;
|
||||
overflow: visible; /* 确保内容可见 */
|
||||
gap: 4px; /* 行与行之间的间距 */
|
||||
|
||||
.header-top {
|
||||
.info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
|
||||
.bed-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
&.room-bed-row {
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
|
||||
.bed {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
color: #1f2933;
|
||||
|
||||
.bed-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: 1.4;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
min-width: 0; /* 允许收缩 */
|
||||
|
||||
.house-name {
|
||||
color: #1f2933;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.bed-name {
|
||||
color: #1f2933;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.insurance-row {
|
||||
align-items: center;
|
||||
margin-left: 8px; /* 添加一点左边距,与第一行对齐 */
|
||||
}
|
||||
|
||||
&.busno-row {
|
||||
color: #6b7280;
|
||||
font-size: 12px;
|
||||
align-items: center;
|
||||
|
||||
.bus-no {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
&.name-row {
|
||||
align-items: center;
|
||||
color: #111827;
|
||||
|
||||
.name {
|
||||
color: #111827;
|
||||
font-weight: 700 !important; /* 更粗的字体,使用important确保优先级 */
|
||||
font-size: 18px !important; /* 更大的字体,使用important确保优先级 */
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
:deep(.el-text),
|
||||
:deep(.el-text__inner) {
|
||||
font-weight: 700 !important; /* 确保el-text组件也应用粗体 */
|
||||
font-size: 18px !important; /* 确保el-text组件也应用更大字体 */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.gender-age-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
|
||||
.age {
|
||||
flex-shrink: 0;
|
||||
|
||||
.age-tag {
|
||||
border-radius: 999px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.age-tag-female {
|
||||
border-color: rgb(255, 55, 158);
|
||||
color: rgb(255, 126, 184);
|
||||
}
|
||||
}
|
||||
|
||||
.status-inline {
|
||||
flex-shrink: 0;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.payer-tag {
|
||||
max-width: 120px;
|
||||
max-width: 100%;
|
||||
font-size: 12px;
|
||||
border-radius: 999px;
|
||||
font-weight: bolder;
|
||||
@@ -435,25 +521,13 @@ watch(
|
||||
font-weight: bolder;
|
||||
border-radius: 999px;
|
||||
}
|
||||
}
|
||||
|
||||
.header-bottom {
|
||||
margin-top: 4px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 2px;
|
||||
color: #6b7280;
|
||||
font-size: 12px;
|
||||
|
||||
.bus-no {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.insurance-type {
|
||||
white-space: nowrap;
|
||||
}
|
||||
/* 隐藏旧的布局结构,因为已被新的info-row结构替代 */
|
||||
.header-top,
|
||||
.header-bottom,
|
||||
.bed-container,
|
||||
.tags-container {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,6 +550,7 @@ watch(
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
display: none; /* 新的布局中,name-container已在header中显示,这里隐藏 */
|
||||
|
||||
.name {
|
||||
color: #111827;
|
||||
@@ -522,15 +597,16 @@ watch(
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
border-right: 1px solid #ebeef5;
|
||||
background-color: #ffffff;
|
||||
width: 240px;
|
||||
min-width: 240px;
|
||||
|
||||
&-unexpand {
|
||||
width: 84px;
|
||||
min-width: 84px;
|
||||
}
|
||||
|
||||
.patientList-operate {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -551,22 +627,14 @@ watch(
|
||||
flex-direction: column;
|
||||
height: 0;
|
||||
width: 240px;
|
||||
|
||||
&-unexpand {
|
||||
width: 84px;
|
||||
}
|
||||
|
||||
.search-operate {
|
||||
padding: 0 8px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.patient-cards {
|
||||
flex: 1;
|
||||
padding: 0 8px;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
:deep(.patient-cards-scrollbar) {
|
||||
@@ -610,6 +678,7 @@ watch(
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 4px 16px 8px;
|
||||
|
||||
&-unexpand {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@@ -187,6 +187,7 @@ const getStatusClass = (item: any) => {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
flex-wrap: nowrap; /* 防止换行 */
|
||||
|
||||
.bed-container {
|
||||
display: flex;
|
||||
@@ -216,9 +217,10 @@ const getStatusClass = (item: any) => {
|
||||
.header-tags {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
flex-wrap: nowrap; /* 防止换行 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,11 +78,181 @@ export const constantRoutes = [
|
||||
meta: { title: '个人中心', icon: 'user' }
|
||||
}
|
||||
]
|
||||
},
|
||||
// 添加套餐管理相关路由到公共路由,确保始终可用
|
||||
{
|
||||
path: '/maintainSystem/Inspection/PackageManagement',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/maintainSystem/Inspection/PackageManagement.vue'),
|
||||
name: 'DirectPackageManagement',
|
||||
meta: { title: '套餐管理' }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
// 动态路由,基于用户权限动态去加载
|
||||
export const dynamicRoutes = [
|
||||
{
|
||||
path: '/basicmanage',
|
||||
component: Layout,
|
||||
redirect: '/basicmanage/invoice-management',
|
||||
name: 'BasicManage',
|
||||
meta: { title: '基础管理', icon: 'component' },
|
||||
children: [
|
||||
{
|
||||
path: 'invoice-management',
|
||||
component: () => import('@/views/basicmanage/InvoiceManagement/index.vue'),
|
||||
name: 'invoice-management',
|
||||
meta: { title: '发票管理' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/system/tenant-user',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['*:*:*'],
|
||||
children: [
|
||||
{
|
||||
path: 'set/:tenantId(\\d+)',
|
||||
component: () => import('@/views/system/tenant/setUser'),
|
||||
name: 'SetUser',
|
||||
meta: { title: '所属用户', activeMenu: '/system/tenant' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/system/tenant-contract',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['*:*:*'],
|
||||
children: [
|
||||
{
|
||||
path: 'set/:tenantId(\\d+)',
|
||||
component: () => import('@/views/system/tenant/setContract'),
|
||||
name: 'SetContract',
|
||||
meta: { title: '合同管理', activeMenu: '/system/tenant' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/system/user-auth',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['system:user:edit'],
|
||||
children: [
|
||||
{
|
||||
path: 'role/:userId(\\d+)',
|
||||
component: () => import('@/views/system/user/authRole'),
|
||||
name: 'AuthRole',
|
||||
meta: { title: '分配角色', activeMenu: '/system/user' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/system/role-auth',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['system:role:edit'],
|
||||
children: [
|
||||
{
|
||||
path: 'user/:roleId(\\d+)',
|
||||
component: () => import('@/views/system/role/authUser'),
|
||||
name: 'AuthUser',
|
||||
meta: { title: '分配用户', activeMenu: '/system/role' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/system/dict-data',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['system:dict:list'],
|
||||
children: [
|
||||
{
|
||||
path: 'index/:dictId(\\d+)',
|
||||
component: () => import('@/views/system/dict/data'),
|
||||
name: 'Data',
|
||||
meta: { title: '字典数据', activeMenu: '/system/dict' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/monitor',
|
||||
component: Layout,
|
||||
redirect: '/monitor/operlog',
|
||||
name: 'Monitor',
|
||||
meta: { title: '系统监控', icon: 'monitor' },
|
||||
children: [
|
||||
{
|
||||
path: 'operlog',
|
||||
component: () => import('@/views/monitor/operlog/index.vue'),
|
||||
name: 'Operlog',
|
||||
meta: { title: '操作日志', icon: 'operlog', permissions: ['monitor:operlog:list'] }
|
||||
},
|
||||
{
|
||||
path: 'logininfor',
|
||||
component: () => import('@/views/monitor/logininfor/index.vue'),
|
||||
name: 'Logininfor',
|
||||
meta: { title: '登录日志', icon: 'logininfor', permissions: ['monitor:logininfor:list'] }
|
||||
},
|
||||
{
|
||||
path: 'job',
|
||||
component: () => import('@/views/monitor/job/index.vue'),
|
||||
name: 'Job',
|
||||
meta: { title: '定时任务', icon: 'job', permissions: ['monitor:job:list'] }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/tool',
|
||||
component: Layout,
|
||||
redirect: '/tool/gen',
|
||||
name: 'Tool',
|
||||
meta: { title: '系统工具', icon: 'tool' },
|
||||
children: [
|
||||
{
|
||||
path: 'gen',
|
||||
component: () => import('@/views/tool/gen/index.vue'),
|
||||
name: 'Gen',
|
||||
meta: { title: '代码生成', icon: 'gen', permissions: ['tool:gen:list'] }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/monitor/job-log',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['monitor:job:list'],
|
||||
children: [
|
||||
{
|
||||
path: 'index/:jobId(\\d+)',
|
||||
component: () => import('@/views/monitor/job/log'),
|
||||
name: 'JobLog',
|
||||
meta: { title: '调度日志', activeMenu: '/monitor/job' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/tool/gen-edit',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['tool:gen:edit'],
|
||||
children: [
|
||||
{
|
||||
path: 'index/:tableId(\\d+)',
|
||||
component: () => import('@/views/tool/gen/editTable'),
|
||||
name: 'GenEdit',
|
||||
meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/help-center',
|
||||
component: Layout,
|
||||
@@ -96,6 +266,32 @@ export const dynamicRoutes = [
|
||||
},
|
||||
],
|
||||
},
|
||||
// 字典类型路由(直接复制这段)
|
||||
{
|
||||
path: '/system/dict',
|
||||
component: Layout,
|
||||
alwaysShow: true,
|
||||
name: 'DictType',
|
||||
meta: {
|
||||
title: '字典类型管理',
|
||||
icon: 'list' // 图标随便选一个,比如list、dict,不影响跳转
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/system/dict/index.vue'),
|
||||
name: 'DictTypeList',
|
||||
meta: {title: '字典类型', noCache: false}
|
||||
},
|
||||
{
|
||||
path: 'data/:dictId?', // 带字典ID参数,?表示可选
|
||||
component: () => import('@/views/system/dict/data.vue'), // 你的data.vue路径
|
||||
name: 'DictData',
|
||||
hidden: true, // 不在侧边栏显示(子页面)
|
||||
meta: {title: '字典数据', activeMenu: '/system/dict'} // 保持侧边栏高亮
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
// 合并常量路由和动态路由,确保所有路由都能被访问
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
||||
<div id="topSearchArea" class="top-search-area">
|
||||
<!-- 搜索标签行 -->
|
||||
<div class="search-labels">
|
||||
<div class="search-label">导源日期</div>
|
||||
<div class="search-label">号源日期</div>
|
||||
<div class="search-label">状态</div>
|
||||
<div class="search-label">患者姓名</div>
|
||||
<div class="search-label">就诊卡号</div>
|
||||
@@ -34,8 +34,7 @@
|
||||
<select id="status-select" class="search-select" v-model="selectedStatus">
|
||||
<option value="all">全部</option>
|
||||
<option value="unbooked">未预约</option>
|
||||
<option value="locked">锁定</option>
|
||||
<option value="booked">待缴费</option>
|
||||
<option value="booked">已预约</option>
|
||||
<option value="checked">已取号</option>
|
||||
<option value="cancelled">已停诊</option>
|
||||
</select>
|
||||
@@ -105,38 +104,34 @@
|
||||
<!-- 使用网格布局的号源卡片列表 -->
|
||||
<div class="ticket-grid" v-if="filteredTickets.length > 0">
|
||||
<div v-for="(item, index) in filteredTickets" :key="item.slot_id" class="ticket-card" @dblclick="handleDoubleClick(item)" @contextmenu.prevent="handleRightClick($event, item)">
|
||||
<div class="ticket-header">
|
||||
<div class="ticket-number">{{ index + 1 }}</div>
|
||||
<div class="ticket-type-badge" :class="item.ticketType === 'general' ? 'type-general' : 'type-expert'">
|
||||
{{ item.ticketType === 'general' ? '普通' : '专家' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="ticket-time">{{ item.dateTime }}</div>
|
||||
<div class="ticket-doctor">{{ item.doctor }}</div>
|
||||
<!-- 序号放在最右侧 -->
|
||||
<div class="ticket-index">{{ index + 1 }}</div>
|
||||
<!-- 1.时间 -->
|
||||
<div class="ticket-id-time">{{ item.dateTime }}</div>
|
||||
<!-- 2. 状态标签 -->
|
||||
<div class="ticket-status" :class="`status-${item.status}`">
|
||||
<span class="status-dot"></span>
|
||||
{{ item.status }}
|
||||
</div>
|
||||
<div class="ticket-fee">挂号费: {{ item.fee }}</div>
|
||||
<!-- 显示患者信息(如果已预约或已取号) -->
|
||||
<!-- 3. 医生姓名(截断显示,悬停展示完整信息) -->
|
||||
<div class="ticket-doctor" :title="item.doctor">{{ item.doctor }}</div>
|
||||
<!-- 4. 挂号费 -->
|
||||
<div class="ticket-fee">挂号费:{{ item.fee }}元</div>
|
||||
<!-- 5. 号源类型 -->
|
||||
<div class="ticket-type">{{ item.ticketType === 'general' ? '普通' : '专家' }}</div>
|
||||
<!-- 6. 已预约患者信息 -->
|
||||
<div v-if="(item.status === '已预约' || item.status === '已取号') && item.patientName" class="ticket-patient">
|
||||
<div class="patient-name">{{ item.patientName }}</div>
|
||||
<div class="patient-card">{{ item.patientId }}</div>
|
||||
<div class="patient-gender">性别:{{ item.patientGender || '-' }}</div>
|
||||
{{ item.patientName }}({{ item.patientId }})
|
||||
</div>
|
||||
<!-- 7. 患者电话号码 -->
|
||||
<div v-if="(item.status === '已预约' || item.status === '已取号') && item.phone" class="ticket-phone">
|
||||
电话号码: {{ item.phone }}
|
||||
</div>
|
||||
<div class="ticket-actions">
|
||||
<button class="action-button book-button" @click="openPatientSelectModal(item.slot_id)" :disabled="item.status !== '未预约'" :class="{ 'disabled': item.status !== '未预约' }">
|
||||
<i class="el-icon-tickets"></i>
|
||||
预约
|
||||
</button>
|
||||
<button class="action-button cancel-button" @click="confirmCancelConsultation(item)" :disabled="item.status === '已取消'" :class="{ 'disabled': item.status === '已取消' }">
|
||||
<i class="el-icon-close"></i>
|
||||
停诊
|
||||
</button>
|
||||
<button class="action-button check-in-button" @click="confirmCheckIn(item)" :disabled="item.status !== '已预约'" :class="{ 'disabled': item.status !== '已预约' }">
|
||||
<i class="el-icon-circle-check"></i>
|
||||
取号
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="hasMore" class="loading-more">
|
||||
@@ -166,7 +161,7 @@
|
||||
<input id="phone" class="search-input" placeholder="手机号" v-model="patientSearchParams.phone" @input="onPatientSearchInput">
|
||||
<button id="patient-search-btn" class="search-btn" @click="searchPatients">查询</button>
|
||||
<div class="patient-table-container">
|
||||
<table id="patient-table" class="patient-table">
|
||||
<table id="patient-table" class="patient-table" v-if="patients.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
@@ -192,6 +187,11 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 无搜索结果提示 -->
|
||||
<div v-else-if="hasSearchCriteria && !isLoading" class="no-results">
|
||||
未找到符合条件的患者,请检查搜索条件
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@@ -231,13 +231,14 @@ import { listTicket, bookTicket, cancelTicket, checkInTicket, cancelConsultation
|
||||
import { getPatientList } from '@/api/cardRenewal/api';
|
||||
import { listDept } from '@/api/appoinmentmanage/dept';
|
||||
import { ref } from 'vue'
|
||||
import { ElDatePicker } from 'element-plus'
|
||||
import { ElDatePicker, ElPagination, ElMessageBox, ElMessage } from 'element-plus'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
|
||||
export default {
|
||||
name: 'OutpatientAppointment',
|
||||
components: {
|
||||
ElDatePicker
|
||||
ElDatePicker,
|
||||
ElPagination
|
||||
},
|
||||
|
||||
// 添加全局点击事件监听器,用于关闭右键菜单
|
||||
@@ -324,35 +325,34 @@ export default {
|
||||
filteredTickets() {
|
||||
let filtered = [...this.tickets];
|
||||
|
||||
// 按日期筛选(如果选择了日期) - 暂时注释掉以排查问题
|
||||
// if (this.selectedDate) {
|
||||
// filtered = filtered.filter(ticket => {
|
||||
// // 使用后端返回的dateTime字段进行筛选
|
||||
// if (ticket.dateTime) {
|
||||
// return ticket.dateTime.startsWith(this.selectedDate);
|
||||
// }
|
||||
// return true;
|
||||
// });
|
||||
// }
|
||||
//按日期筛选(如果选择了日期)
|
||||
if (this.selectedDate) {
|
||||
filtered = filtered.filter(ticket => {
|
||||
// 使用后端返回的dateTime字段进行筛选
|
||||
if (ticket.dateTime) {
|
||||
return ticket.dateTime.startsWith(this.selectedDate);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// 按状态筛选 - 暂时注释掉以排查问题
|
||||
// if (this.selectedStatus !== 'all') {
|
||||
// // 状态映射表(后端返回中文状态,前端使用英文状态)
|
||||
// const statusMap = {
|
||||
// 'unbooked': '未预约',
|
||||
// 'locked': '已锁定',
|
||||
// 'booked': '已预约',
|
||||
// 'checked': '已取号',
|
||||
// 'cancelled': '已取消'
|
||||
// };
|
||||
// const chineseStatus = statusMap[this.selectedStatus];
|
||||
// filtered = filtered.filter(ticket => ticket.status === chineseStatus);
|
||||
// }
|
||||
//按状态筛选
|
||||
if (this.selectedStatus !== 'all') {
|
||||
// 状态映射表(后端返回中文状态,前端使用英文状态)
|
||||
const statusMap = {
|
||||
'unbooked': '未预约',
|
||||
'booked': '已预约',
|
||||
'checked': '已取号',
|
||||
'cancelled': '已停诊'
|
||||
};
|
||||
const chineseStatus = statusMap[this.selectedStatus];
|
||||
filtered = filtered.filter(ticket => ticket.status === chineseStatus);
|
||||
}
|
||||
|
||||
// 按科室筛选 - 暂时注释掉以排查问题
|
||||
// if (this.selectedDepartment !== 'all') {
|
||||
// filtered = filtered.filter(ticket => ticket.department === this.selectedDepartment);
|
||||
// }
|
||||
// 按科室筛选
|
||||
if (this.selectedDepartment !== 'all') {
|
||||
filtered = filtered.filter(ticket => ticket.department === this.selectedDepartment);
|
||||
}
|
||||
|
||||
// 按号源类型筛选
|
||||
filtered = filtered.filter(ticket => {
|
||||
@@ -365,62 +365,67 @@ export default {
|
||||
});
|
||||
|
||||
// 按医生筛选 - 暂时注释掉以排查问题
|
||||
// if (this.selectedDoctorId) {
|
||||
// const selectedDoctor = this.doctors.find(d => d.id === this.selectedDoctorId);
|
||||
// if (selectedDoctor) {
|
||||
// filtered = filtered.filter(ticket => ticket.doctor === selectedDoctor.name);
|
||||
// }
|
||||
// }
|
||||
|
||||
// 按患者信息筛选(姓名、就诊卡号、手机号) - 暂时注释掉以排查问题
|
||||
// if (this.patientName || this.patientCard || this.patientPhone) {
|
||||
// filtered = filtered.filter(ticket => {
|
||||
// const matchesName = !this.patientName || ticket.patientName?.includes(this.patientName);
|
||||
// const matchesCard = !this.patientCard || ticket.patientId?.includes(this.patientCard);
|
||||
// const matchesPhone = !this.patientPhone || ticket.phone?.includes(this.patientPhone);
|
||||
//
|
||||
// return matchesName && matchesCard && matchesPhone;
|
||||
// });
|
||||
// }
|
||||
if (this.selectedDoctorId) {
|
||||
const selectedDoctor = this.doctors.find(d => d.id === this.selectedDoctorId);
|
||||
if (selectedDoctor) {
|
||||
filtered = filtered.filter(ticket => ticket.doctor === selectedDoctor.name);
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
},
|
||||
hasSearchCriteria() {
|
||||
return Object.values(this.patientSearchParams).some(value => value && value.trim() !== '');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
selectDoctor(doctorId) {
|
||||
this.selectedDoctorId = doctorId
|
||||
console.log('Selected doctor:', doctorId)
|
||||
// 医生选择后,筛选号源
|
||||
this.onSearch();
|
||||
},
|
||||
onTypeChange() {
|
||||
console.log('Type changed:', this.selectedType)
|
||||
// 移除onSearch()调用,让计算属性自动处理类型筛选
|
||||
},
|
||||
onDepartmentChange() {
|
||||
console.log('Department changed:', this.selectedDepartment)
|
||||
this.onSearch()
|
||||
},
|
||||
onDoctorSearch() {
|
||||
console.log('Searching doctor:', this.searchQuery)
|
||||
// 使用计算属性filteredDoctors实现实时搜索,无需调用API
|
||||
},
|
||||
openPatientSelectModal(ticketId) {
|
||||
console.log('openPatientSelectModal called with ticketId:', ticketId);
|
||||
this.currentTicket = this.tickets.find(ticket => ticket.slot_id === ticketId);
|
||||
console.log('Found ticket for modal:', this.currentTicket);
|
||||
// 打开患者选择弹窗时,查询患者列表
|
||||
this.searchPatients();
|
||||
// 重置搜索参数
|
||||
this.patientSearchParams = {
|
||||
patientName: '',
|
||||
medicalCard: '',
|
||||
idCard: '',
|
||||
phone: ''
|
||||
};
|
||||
// 清空患者列表
|
||||
this.patients = [];
|
||||
this.showPatientModal = true;
|
||||
console.log('Show patient modal:', this.showPatientModal);
|
||||
// 默认加载所有患者
|
||||
this.searchPatients();
|
||||
},
|
||||
|
||||
// 患者搜索
|
||||
searchPatients() {
|
||||
console.log('searchPatients called with params:', this.patientSearchParams);
|
||||
// 检查是否有搜索条件
|
||||
const hasSearchCriteria = Object.values(this.patientSearchParams).some(value => value && value.trim() !== '');
|
||||
|
||||
// 设置加载状态
|
||||
this.isLoading = true;
|
||||
|
||||
// 准备搜索参数,确保支持模糊匹配
|
||||
const searchParams = {
|
||||
patientName: this.patientSearchParams.patientName?.trim() || '',
|
||||
medicalCard: this.patientSearchParams.medicalCard?.trim() || '',
|
||||
idCard: this.patientSearchParams.idCard?.trim() || '',
|
||||
phone: this.patientSearchParams.phone?.trim() || ''
|
||||
};
|
||||
// 调用真实API获取患者列表
|
||||
getPatientList(this.patientSearchParams).then(response => {
|
||||
console.log('Patient API response:', response);
|
||||
// 尝试不同的数据结构解析
|
||||
let records = [];
|
||||
if (response.data && response.data.records) {
|
||||
@@ -432,30 +437,39 @@ export default {
|
||||
}
|
||||
|
||||
this.patients = records;
|
||||
console.log('Loaded patients:', this.patients);
|
||||
// 在前端进行额外的模糊匹配过滤(以防后端API不支持完整的模糊匹配)
|
||||
if (hasSearchCriteria) {
|
||||
this.patients = this.patients.filter(patient => {
|
||||
const nameMatch = !searchParams.patientName ||
|
||||
(patient.name && patient.name.toLowerCase().includes(searchParams.patientName.toLowerCase()));
|
||||
const cardMatch = !searchParams.medicalCard ||
|
||||
(patient.medicalCard && patient.medicalCard.toLowerCase().includes(searchParams.medicalCard.toLowerCase()));
|
||||
const idMatch = !searchParams.idCard ||
|
||||
(patient.idCard && patient.idCard.toLowerCase().includes(searchParams.idCard.toLowerCase()));
|
||||
const phoneMatch = !searchParams.phone ||
|
||||
(patient.phone && patient.phone.toLowerCase().includes(searchParams.phone.toLowerCase()));
|
||||
|
||||
return nameMatch && cardMatch && idMatch && phoneMatch;
|
||||
});
|
||||
}
|
||||
// 验证每个患者是否有idCard字段,如果没有,尝试使用其他唯一标识
|
||||
this.patients.forEach((patient, index) => {
|
||||
console.log(`Patient ${index}:`, patient);
|
||||
console.log(`Patient ${index} has idCard:`, patient.idCard ? 'Yes' : 'No');
|
||||
console.log(`Patient ${index} has id:`, patient.id ? 'Yes' : 'No');
|
||||
console.log(`Patient ${index} has medicalCard:`, patient.medicalCard ? 'Yes' : 'No');
|
||||
|
||||
// 如果没有idCard,尝试设置一个临时唯一标识
|
||||
if (!patient.idCard) {
|
||||
patient.idCard = patient.id || patient.medicalCard || `temp_${index}`;
|
||||
console.log(`Set temporary idCard for patient ${index}:`, patient.idCard);
|
||||
}
|
||||
});
|
||||
|
||||
if (this.patients.length === 0) {
|
||||
console.log('No patients found in response');
|
||||
alert('没有找到患者数据,请检查搜索条件或联系管理员');
|
||||
ElMessage.error('没有找到患者数据,请检查搜索条件或联系管理员');
|
||||
}
|
||||
// 清除加载状态
|
||||
this.isLoading = false;
|
||||
}).catch(error => {
|
||||
console.error('获取患者列表失败:', error);
|
||||
this.patients = [];
|
||||
alert('获取患者列表失败:' + (error.message || '未知错误'));
|
||||
ElMessage.error('获取患者列表失败:' + (error.message || '未知错误'));
|
||||
// 清除加载状态
|
||||
this.isLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
@@ -483,18 +497,12 @@ export default {
|
||||
|
||||
// 双击未预约卡片触发患者选择流程
|
||||
handleDoubleClick(ticket) {
|
||||
console.log('handleDoubleClick called with ticket:', ticket);
|
||||
if (ticket.status === '未预约') {
|
||||
this.currentTicket = ticket;
|
||||
console.log('Set currentTicket:', this.currentTicket);
|
||||
this.selectedPatientId = null;
|
||||
this.selectedPatient = null;
|
||||
console.log('Reset selected patient info:', { selectedPatientId: this.selectedPatientId, selectedPatient: this.selectedPatient });
|
||||
|
||||
// 先打开弹窗,再加载患者数据,避免等待
|
||||
this.showPatientModal = true;
|
||||
console.log('Show patient modal after double click:', this.showPatientModal);
|
||||
|
||||
// 调用患者搜索接口,加载患者列表
|
||||
this.searchPatients();
|
||||
}
|
||||
@@ -518,70 +526,39 @@ export default {
|
||||
// 确认取消预约
|
||||
confirmCancelAppointment() {
|
||||
if (this.selectedTicketForCancel) {
|
||||
// 二次确认,显示患者姓名
|
||||
if (confirm(`确认取消患者${this.selectedTicketForCancel.patientName || ''}的预约?`)) {
|
||||
// 调用API取消预约
|
||||
// 使用 ElMessageBox.confirm 进行二次确认,显示患者姓名
|
||||
ElMessageBox.confirm(
|
||||
`确认取消患者${this.selectedTicketForCancel.patientName || ''}的预约?`,
|
||||
'系统提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
// 用户点击确定,调用API取消预约
|
||||
this.cancelAppointment(this.selectedTicketForCancel);
|
||||
}
|
||||
}).catch(() => {
|
||||
// 用户取消操作
|
||||
this.closeContextMenu();
|
||||
}
|
||||
},
|
||||
|
||||
// 确认停诊
|
||||
confirmCancelConsultation(ticket) {
|
||||
// 二次确认
|
||||
if (confirm(`确认取消${ticket.doctor}在${ticket.dateTime}的看诊吗?`)) {
|
||||
// 调用API处理停诊
|
||||
this.cancelConsultation(ticket);
|
||||
}
|
||||
},
|
||||
|
||||
// 处理停诊
|
||||
cancelConsultation(ticket) {
|
||||
// 使用真实API调用处理停诊
|
||||
cancelConsultation(ticket.slot_id).then(response => {
|
||||
// API调用成功后,重新获取最新的号源数据
|
||||
this.onSearch();
|
||||
alert('停诊已处理,该号源已被取消');
|
||||
}).catch(error => {
|
||||
console.error('停诊处理失败:', error);
|
||||
alert('停诊处理失败,请稍后重试。');
|
||||
});
|
||||
},
|
||||
|
||||
// 确认取号
|
||||
confirmCheckIn(ticket) {
|
||||
// 二次确认
|
||||
if (confirm(`确认${ticket.patientName || ''}取号吗?`)) {
|
||||
// 调用API处理取号
|
||||
this.checkIn(ticket);
|
||||
}
|
||||
},
|
||||
|
||||
// 处理取号
|
||||
checkIn(ticket) {
|
||||
// 使用真实API调用处理取号
|
||||
checkInTicket(ticket.slot_id).then(response => {
|
||||
// API调用成功后,重新获取最新的号源数据
|
||||
this.onSearch();
|
||||
alert('取号成功!');
|
||||
}).catch(error => {
|
||||
console.error('取号失败:', error);
|
||||
alert('取号失败,请稍后重试。');
|
||||
});
|
||||
},
|
||||
|
||||
// 取消预约API调用
|
||||
cancelAppointment(ticket) {
|
||||
if (!ticket || !ticket.slot_id) {
|
||||
console.error('取消预约失败:缺少号源信息');
|
||||
alert('取消预约失败:缺少号源信息');
|
||||
ElMessage.error('取消预约失败:缺少号源信息');
|
||||
this.closeContextMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用真实API调用取消预约
|
||||
// 使用真实API调用取消预约,传递slot_id
|
||||
cancelTicket(ticket.slot_id).then(response => {
|
||||
// 根据后端返回判断是否成功
|
||||
if (response.code === 200 || response.msg === '取消成功' || response.message === '取消成功') {
|
||||
console.log('取消预约成功,更新前端状态');
|
||||
|
||||
// API调用成功后,更新当前卡片状态
|
||||
const ticketIndex = this.tickets.findIndex(t => t.slot_id === ticket.slot_id);
|
||||
if (ticketIndex !== -1) {
|
||||
@@ -592,14 +569,23 @@ export default {
|
||||
this.tickets[ticketIndex].patientGender = null;
|
||||
this.tickets[ticketIndex].medicalCard = null;
|
||||
this.tickets[ticketIndex].phone = null;
|
||||
|
||||
// 更新医生号源列表中的余号数量
|
||||
this.updateDoctorsList();
|
||||
}
|
||||
|
||||
// 关闭上下文菜单
|
||||
this.closeContextMenu();
|
||||
alert('预约已取消,号源已释放');
|
||||
ElMessage.success('预约已取消,号源已释放');
|
||||
} else {
|
||||
// 取消失败
|
||||
const errorMsg = response.msg || response.message || '取消预约失败';
|
||||
ElMessage.error(`取消预约失败:${errorMsg}`);
|
||||
this.closeContextMenu();
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('取消预约失败:', error);
|
||||
alert('取消预约失败,请稍后重试。');
|
||||
const errorMsg = error.message || '取消预约失败,请稍后重试';
|
||||
ElMessage.error(`取消预约失败:${errorMsg}`);
|
||||
this.closeContextMenu();
|
||||
});
|
||||
},
|
||||
@@ -608,20 +594,13 @@ export default {
|
||||
this.selectedPatientId = null
|
||||
},
|
||||
selectPatient(patientId) {
|
||||
console.log('selectPatient called with patientId:', patientId);
|
||||
console.log('Current patients data:', this.patients);
|
||||
console.log('Patients array length:', this.patients.length);
|
||||
|
||||
// 确保患者数据已加载
|
||||
if (this.patients.length === 0) {
|
||||
console.error('No patients data available');
|
||||
alert('患者数据未加载,请先搜索患者');
|
||||
ElMessage.error('患者数据未加载,请先搜索患者');
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedPatientId = patientId;
|
||||
console.log('Set selectedPatientId:', this.selectedPatientId);
|
||||
|
||||
// 保存选择的患者对象 - 使用idCard作为唯一标识,但增加容错性
|
||||
this.selectedPatient = this.patients.find(patient => {
|
||||
// 尝试多种匹配方式
|
||||
@@ -629,42 +608,44 @@ export default {
|
||||
const matchById = patient.id === patientId;
|
||||
const matchByMedicalCard = patient.medicalCard === patientId;
|
||||
const match = matchByIdCard || matchById || matchByMedicalCard;
|
||||
|
||||
console.log(`Checking patient ${patient.name || 'unknown'}:`);
|
||||
console.log(` idCard: ${patient.idCard}, matchByIdCard: ${matchByIdCard}`);
|
||||
console.log(` id: ${patient.id}, matchById: ${matchById}`);
|
||||
console.log(` medicalCard: ${patient.medicalCard}, matchByMedicalCard: ${matchByMedicalCard}`);
|
||||
console.log(` Overall match: ${match}`);
|
||||
|
||||
return match;
|
||||
});
|
||||
|
||||
console.log('Selected patient:', this.selectedPatient);
|
||||
console.log('Selected patientId:', this.selectedPatientId);
|
||||
|
||||
if (this.selectedPatient) {
|
||||
// 添加视觉反馈,验证患者是否被选中
|
||||
alert('已选择患者: ' + this.selectedPatient.name);
|
||||
|
||||
// 可以考虑自动滚动到确认按钮,提高用户体验
|
||||
// 使用 ElMessageBox.confirm 进行二次确认
|
||||
ElMessageBox.confirm(
|
||||
`确认选择患者 ${this.selectedPatient.name} 进行预约?`,
|
||||
'患者确认',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'info'
|
||||
}
|
||||
).then(() => {
|
||||
// 用户点击确定,自动滚动到确认按钮
|
||||
ElMessage.success('已选择患者: ' + this.selectedPatient.name);
|
||||
const confirmBtn = document.querySelector('.confirm-btn');
|
||||
if (confirmBtn) {
|
||||
confirmBtn.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
}
|
||||
}).catch(() => {
|
||||
// 用户取消选择,清空已选患者
|
||||
this.selectedPatientId = null;
|
||||
this.selectedPatient = null;
|
||||
ElMessage.info('已取消患者选择');
|
||||
});
|
||||
} else {
|
||||
console.error('Patient not found with id:', patientId);
|
||||
alert('未找到该患者,请重新选择');
|
||||
ElMessage.error('未找到该患者,请重新选择');
|
||||
this.selectedPatientId = null;
|
||||
}
|
||||
},
|
||||
confirmPatientSelection() {
|
||||
if (!this.selectedPatientId || !this.selectedPatient) {
|
||||
alert('请选择患者');
|
||||
ElMessage.error('请选择患者');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.currentTicket) {
|
||||
alert('预约信息错误,请重新选择号源');
|
||||
ElMessage.error('预约信息错误,请重新选择号源');
|
||||
this.closePatientSelectModal();
|
||||
return;
|
||||
}
|
||||
@@ -672,6 +653,7 @@ export default {
|
||||
try {
|
||||
const userStore = useUserStore();
|
||||
const appointmentData = {
|
||||
slotId: this.currentTicket.slot_id, // 添加号源ID
|
||||
ticketId: Number(this.currentTicket.slot_id),
|
||||
patientId: this.selectedPatientId, // 使用身份证号作为患者ID,不需要转换为数字
|
||||
patientName: this.selectedPatient.name,
|
||||
@@ -685,16 +667,15 @@ export default {
|
||||
|
||||
// 验证必填字段
|
||||
if (!appointmentData.ticketId || isNaN(appointmentData.ticketId)) {
|
||||
alert('号源ID无效');
|
||||
ElMessage.error('号源ID无效');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!appointmentData.patientId) {
|
||||
alert('患者ID无效');
|
||||
ElMessage.error('患者ID无效');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Sending appointment data to backend:', appointmentData);
|
||||
// 调用真实API进行预约
|
||||
bookTicket(appointmentData).then(response => {
|
||||
const ticketIndex = this.tickets.findIndex(t => t.slot_id === this.currentTicket.slot_id);
|
||||
if (ticketIndex !== -1) {
|
||||
@@ -702,22 +683,25 @@ export default {
|
||||
this.tickets[ticketIndex].patientName = this.selectedPatient.name;
|
||||
this.tickets[ticketIndex].patientId = this.selectedPatient.medicalCard;
|
||||
this.tickets[ticketIndex].patientGender = this.selectedPatient.gender;
|
||||
|
||||
// 更新医生号源列表中的余号数量
|
||||
this.updateDoctorsList();
|
||||
}
|
||||
|
||||
this.closePatientSelectModal();
|
||||
alert('预约成功,号源已锁定。患者到院签到时需缴费取号。');
|
||||
|
||||
// 重新加载号源数据,确保显示最新状态
|
||||
this.onSearch();
|
||||
|
||||
ElMessage.success('预约成功,号源已锁定。患者到院签到时需缴费取号。');
|
||||
}).catch(error => {
|
||||
console.error('预约失败:', error);
|
||||
alert('预约失败,请稍后重试。');
|
||||
ElMessage.error('预约失败,请稍后重试。');
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('预约数据处理错误:', error);
|
||||
alert('预约信息格式错误,请重新操作。');
|
||||
ElMessage.error('预约信息格式错误,请重新操作。');
|
||||
}
|
||||
},
|
||||
onDateChange() {
|
||||
// 日期变化时刷新号源列表
|
||||
console.log('Date changed:', this.selectedDate)
|
||||
// 重置页码并重新加载数据
|
||||
this.currentPage = 1;
|
||||
this.onSearch()
|
||||
@@ -732,15 +716,6 @@ export default {
|
||||
},
|
||||
onSearch() {
|
||||
this.isLoading = true
|
||||
console.log('Searching with:', {
|
||||
date: this.selectedDate,
|
||||
status: this.selectedStatus,
|
||||
type: this.selectedType,
|
||||
name: this.patientName,
|
||||
card: this.patientCard,
|
||||
phone: this.patientPhone
|
||||
});
|
||||
|
||||
// 调用真实API获取号源数据
|
||||
const queryParams = {
|
||||
date: this.selectedDate,
|
||||
@@ -749,7 +724,7 @@ export default {
|
||||
name: this.patientName,
|
||||
card: this.patientCard,
|
||||
phone: this.patientPhone,
|
||||
page: 1,
|
||||
page: this.currentPage,
|
||||
limit: this.pageSize
|
||||
};
|
||||
|
||||
@@ -758,9 +733,6 @@ export default {
|
||||
listAllTickets(), // 使用测试接口获取固定的5条专家号数据
|
||||
listDept()
|
||||
]).then(([ticketResponse, deptResponse]) => {
|
||||
// 打印完整的响应数据以便调试
|
||||
console.log('完整的号源响应数据:', ticketResponse);
|
||||
|
||||
// 处理号源数据
|
||||
if (ticketResponse) {
|
||||
// 检查是否有data.list字段(后端返回的数据结构是{code, data:{list, total}})
|
||||
@@ -775,6 +747,10 @@ export default {
|
||||
|
||||
// 保存所有原始数据
|
||||
this.allTickets = [...this.tickets];
|
||||
|
||||
// 根据患者信息进行前端筛选
|
||||
this.filterTicketsByPatientInfo();
|
||||
|
||||
// 更新医生列表
|
||||
this.updateDoctorsList();
|
||||
this.hasMore = this.tickets.length < this.totalTickets;
|
||||
@@ -787,7 +763,6 @@ export default {
|
||||
|
||||
// 处理科室数据
|
||||
this.updateDepartmentsListFromApi(deptResponse);
|
||||
|
||||
this.isLoading = false;
|
||||
}).catch(error => {
|
||||
console.error('获取数据失败:', error);
|
||||
@@ -799,8 +774,6 @@ export default {
|
||||
this.isLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
// 从号源数据中更新科室列表(备用方法)
|
||||
updateDepartmentsList() {
|
||||
const departmentSet = new Set();
|
||||
@@ -855,6 +828,25 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// 根据患者信息过滤号源
|
||||
filterTicketsByPatientInfo() {
|
||||
// 如果没有输入患者信息,就不进行过滤
|
||||
if (!this.patientName && !this.patientCard && !this.patientPhone) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据患者信息进行过滤(使用AND逻辑,全部匹配)
|
||||
this.tickets = this.tickets.filter(ticket => {
|
||||
const matchesName = !this.patientName || ticket.patientName?.includes(this.patientName);
|
||||
const matchesCard = !this.patientCard || ticket.patientId?.includes(this.patientCard);
|
||||
const matchesPhone = !this.patientPhone || ticket.phone?.includes(this.patientPhone);
|
||||
|
||||
return matchesName && matchesCard && matchesPhone;
|
||||
});
|
||||
|
||||
console.log('按患者信息过滤后的号源数量:', this.tickets.length);
|
||||
},
|
||||
|
||||
// 获取号源状态文本
|
||||
getStatusText(status) {
|
||||
switch (status) {
|
||||
@@ -902,9 +894,6 @@ export default {
|
||||
|
||||
// 转换为数组
|
||||
this.doctors = Array.from(doctorMap.values());
|
||||
|
||||
// 打印医生列表以便调试
|
||||
console.log('生成的医生列表:', this.doctors);
|
||||
},
|
||||
// 加载更多数据
|
||||
loadMore() {
|
||||
@@ -927,9 +916,6 @@ export default {
|
||||
};
|
||||
|
||||
listTicket(queryParams).then(response => {
|
||||
// 打印完整的响应数据以便调试
|
||||
console.log('加载更多的号源响应数据:', response);
|
||||
|
||||
// 处理号源数据
|
||||
if (response) {
|
||||
let newRecords = [];
|
||||
@@ -1461,6 +1447,15 @@ export default {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ticket-index {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
left: 12px;
|
||||
color: #1890ff;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.ticket-time {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
@@ -1514,6 +1509,13 @@ export default {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ticket-id-time {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ticket-fee {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
@@ -1548,50 +1550,6 @@ export default {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.cancel-button {
|
||||
background-color: #F56C6C;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 4px 12px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: background-color 0.3s;
|
||||
margin-top: 8px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.cancel-button:hover {
|
||||
background-color: #F78989;
|
||||
}
|
||||
|
||||
.cancel-button:disabled {
|
||||
background-color: #C0C4CC;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.check-in-button {
|
||||
background-color: #67C23A;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 4px 12px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: background-color 0.3s;
|
||||
margin-top: 8px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.check-in-button:hover {
|
||||
background-color: #85ce61;
|
||||
}
|
||||
|
||||
.check-in-button:disabled {
|
||||
background-color: #C0C4CC;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* 患者信息样式 */
|
||||
.ticket-patient {
|
||||
margin-top: 8px;
|
||||
@@ -1603,6 +1561,18 @@ export default {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 患者电话号码样式 */
|
||||
.ticket-phone {
|
||||
margin-top: 4px;
|
||||
padding: 4px;
|
||||
background-color: rgba(34, 177, 76, 0.05);
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
color: #22b14c;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.ticket-grid {
|
||||
@@ -1618,6 +1588,14 @@ export default {
|
||||
.ticket-doctor {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.ticket-id-time {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.ticket-index {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 弹窗样式 */
|
||||
@@ -1803,6 +1781,54 @@ export default {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* 搜索提示样式 */
|
||||
.search-hint {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
background-color: #fafafa;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
/* 加载指示器样式 */
|
||||
.loading-indicator {
|
||||
text-align: center;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid #f3f3f3;
|
||||
border-top: 2px solid #1890ff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.no-results {
|
||||
text-align: center;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
background-color: #fff2f0;
|
||||
border: 1px solid #ffccc7;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
/* 右键菜单样式 */
|
||||
.context-menu {
|
||||
position: fixed;
|
||||
@@ -1842,4 +1868,60 @@ export default {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* 分页组件样式 */
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
padding: 20px 0;
|
||||
background-color: #fafafa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination .el-pager li {
|
||||
min-width: 36px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
border-radius: 4px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination .el-pager li:hover {
|
||||
background-color: #e6f7ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination .el-pager li.active {
|
||||
background-color: #1890ff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination .btn-prev,
|
||||
.pagination-container .el-pagination .btn-next {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 4px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination .el-pagination__sizes {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination .el-pagination__sizes .el-input {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination .el-pagination__jump {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination .el-pagination__jump .el-input {
|
||||
width: 60px;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -81,15 +81,15 @@
|
||||
v-model="scope.row.activityDefinitionId"
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
placeholder="请选择"
|
||||
remote-show-suffix
|
||||
:remote-method="(query) => handleRemoteQuery(query, scope.row)"
|
||||
:loading="scope.row.loading"
|
||||
placeholder="请输入并搜索项目"
|
||||
style="width: 400px; max-width: 500px"
|
||||
:class="{ 'error-border': scope.row.error }"
|
||||
clearable
|
||||
>
|
||||
<el-option
|
||||
v-for="item in allImplementDepartmentList"
|
||||
v-for="item in scope.row.filteredOptions || []"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
@@ -236,15 +236,34 @@ const filterNode = (value, data) => {
|
||||
};
|
||||
// 所有诊疗项目列表
|
||||
const allImplementDepartmentList = ref([]);
|
||||
function getAllImplementDepartment() {
|
||||
async function getAllImplementDepartment() {
|
||||
loading.value = true;
|
||||
getAllTreatmentList().then((res) => {
|
||||
try {
|
||||
const res = await getAllTreatmentList();
|
||||
allImplementDepartmentList.value = res.data.map((item) => ({
|
||||
value: item.activityDefinitionId,
|
||||
label: item.activityDefinitionName,
|
||||
}));
|
||||
loading.value = false;
|
||||
|
||||
// 为所有现有行初始化过滤选项(使用防抖处理,避免频繁更新)
|
||||
if (catagoryList.value && catagoryList.value.length > 0) {
|
||||
// 使用 setTimeout 将 DOM 更新推迟到下一个事件循环,避免阻塞
|
||||
setTimeout(() => {
|
||||
catagoryList.value.forEach(row => {
|
||||
if (!row.hasOwnProperty('filteredOptions')) {
|
||||
row.filteredOptions = allImplementDepartmentList.value.slice(0, 100); // 限制为前100个
|
||||
row.loading = false;
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
|
||||
loading.value = false;
|
||||
} catch (error) {
|
||||
console.error('获取诊疗项目列表失败:', error);
|
||||
loading.value = false;
|
||||
proxy.$message.error('获取诊疗项目列表失败');
|
||||
}
|
||||
}
|
||||
|
||||
/** 选择条数 */
|
||||
@@ -253,18 +272,63 @@ function handleSelectionChange(selection) {
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
}
|
||||
// 远程搜索处理函数
|
||||
function handleRemoteQuery(query, row) {
|
||||
if (query !== '') {
|
||||
// 设置加载状态
|
||||
row.loading = true;
|
||||
// 模拟异步延迟
|
||||
setTimeout(() => {
|
||||
// 确保数据已加载
|
||||
if (!allImplementDepartmentList.value || allImplementDepartmentList.value.length === 0) {
|
||||
row.filteredOptions = [];
|
||||
row.loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 过滤选项,限制结果数量以提高性能
|
||||
const filtered = allImplementDepartmentList.value.filter(item => {
|
||||
return item.label.toLowerCase().includes(query.toLowerCase()) ||
|
||||
item.value.toLowerCase().includes(query.toLowerCase());
|
||||
});
|
||||
|
||||
// 限制返回结果数量,避免过多选项导致性能问题
|
||||
row.filteredOptions = filtered.slice(0, 100); // 限制为前100个匹配项
|
||||
row.loading = false;
|
||||
}, 300); // 300ms 延迟,模拟网络请求
|
||||
} else {
|
||||
// 如果查询为空,显示所有选项(但限制数量以提高性能)
|
||||
if (allImplementDepartmentList.value && allImplementDepartmentList.value.length > 0) {
|
||||
row.filteredOptions = allImplementDepartmentList.value.slice(0, 100); // 限制为前100个
|
||||
} else {
|
||||
row.filteredOptions = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 新增项目
|
||||
function handleAddItem() {
|
||||
if (data.isAdding) {
|
||||
proxy.$message.warning('请先保存当前行后再新增!');
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保 allImplementDepartmentList 已经初始化
|
||||
if (!allImplementDepartmentList.value || allImplementDepartmentList.value.length === 0) {
|
||||
proxy.$message.warning('正在加载数据,请稍后再试!');
|
||||
// 如果数据还未加载完成,尝试重新加载
|
||||
getAllImplementDepartment();
|
||||
return;
|
||||
}
|
||||
|
||||
const newRow = {
|
||||
startTime: '00:00:00',
|
||||
endTime: '23:59:59',
|
||||
loading: false, // 添加加载状态
|
||||
filteredOptions: allImplementDepartmentList.value.slice(0, 100), // 初始化过滤选项,限制数量
|
||||
};
|
||||
catagoryList.value.push(newRow);
|
||||
total.value = organization.value.length;
|
||||
total.value = catagoryList.value.length; // 修正:使用实际数据列表长度,而不是组织结构长度
|
||||
data.isAdding = true; // 设置标志位为 true,表示有未保存的
|
||||
}
|
||||
// 批量添加
|
||||
@@ -272,6 +336,7 @@ function handleBacthAddItem() {
|
||||
// 批量添加显示对话框
|
||||
bacthAddItemDialogVisible.value = true;
|
||||
}
|
||||
|
||||
// 检验 编辑或 保存数据
|
||||
function handleBlur(row, index) {
|
||||
let hasError = false;
|
||||
@@ -344,15 +409,25 @@ function deleteSelectedRows(row) {
|
||||
}
|
||||
/** 节点单击事件 */
|
||||
function handleNodeClick(res, node) {
|
||||
// 新增按钮是否 disable
|
||||
// 检查是否有未保存的数据
|
||||
if (data.isAdding) {
|
||||
proxy.$modal.confirm('当前有未保存的数据,切换节点将丢失未保存的数据,是否继续?').then(() => {
|
||||
// 确认切换,重置状态
|
||||
data.isAdding = false;
|
||||
|
||||
// 新增按钮是否 disable
|
||||
if (node.parent === null || node.level === 1) {
|
||||
isAddDisable.value = true;
|
||||
continueHandleNodeClick(node);
|
||||
}).catch(() => {
|
||||
// 取消切换,保持当前状态
|
||||
return;
|
||||
});
|
||||
} else {
|
||||
isAddDisable.value = false;
|
||||
continueHandleNodeClick(node);
|
||||
}
|
||||
}
|
||||
|
||||
// 实际的节点点击处理逻辑
|
||||
function continueHandleNodeClick(node) {
|
||||
// 新增按钮是否 disable
|
||||
isAddDisable.value = false;
|
||||
// 检查节点是否有子节点
|
||||
if (node.data.children && node.data.children.length > 0) {
|
||||
// proxy.$message.warning("不能选择父节点");
|
||||
@@ -365,26 +440,28 @@ function handleNodeClick(res, node) {
|
||||
}
|
||||
|
||||
/** 目录分类查询 */
|
||||
function getDiseaseTreatmentList() {
|
||||
async function getDiseaseTreatmentList() {
|
||||
loading.value = true;
|
||||
getDiseaseTreatmentInit().then(({ data }) => {
|
||||
loading.value = false;
|
||||
try {
|
||||
const { data } = await getDiseaseTreatmentInit();
|
||||
|
||||
// 分类目录初始化获取
|
||||
catagoryDicts.value = data.diagnosisCategoryOptions.sort((a, b) => {
|
||||
return parseInt(a.value) - parseInt(b.value);
|
||||
});
|
||||
});
|
||||
// 诊疗目录分类查询下拉树结d构
|
||||
loading.value = true;
|
||||
// 诊疗目录分类查询下拉树结d构
|
||||
getImplDepartList();
|
||||
} catch (error) {
|
||||
console.error('获取疾病治疗初始化数据失败:', error);
|
||||
proxy.$message.error('获取分类目录失败');
|
||||
}
|
||||
|
||||
// 诊疗目录分类查询下拉树结构
|
||||
await getImplDepartList();
|
||||
loading.value = false;
|
||||
}
|
||||
// 诊疗目录分类查询下拉树结d构
|
||||
function getImplDepartList() {
|
||||
loading.value = true;
|
||||
getImplementDepartmentList().then((res) => {
|
||||
loading.value = false;
|
||||
async function getImplDepartList() {
|
||||
try {
|
||||
const res = await getImplementDepartmentList();
|
||||
if (res.code === 200) {
|
||||
if (res.data.records.length > 0) {
|
||||
organization.value = res.data.records.map((res) => {
|
||||
@@ -398,13 +475,24 @@ function getImplDepartList() {
|
||||
organization.value = [];
|
||||
}
|
||||
} else {
|
||||
this.$modal.msgError(res.code);
|
||||
proxy.$modal.msgError(res.code);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取实施部门列表失败:', error);
|
||||
proxy.$message.error('获取科室信息失败');
|
||||
}
|
||||
}
|
||||
onMounted(async () => {
|
||||
try {
|
||||
// 并行加载数据,提高效率
|
||||
await Promise.all([
|
||||
getAllImplementDepartment(),
|
||||
getDiseaseTreatmentList()
|
||||
]);
|
||||
} catch (error) {
|
||||
console.error('初始化数据加载失败:', error);
|
||||
proxy.$message.error('数据加载失败,请稍后重试');
|
||||
}
|
||||
onMounted(() => {
|
||||
getAllImplementDepartment();
|
||||
getDiseaseTreatmentList();
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
@@ -386,6 +386,12 @@ function parseClassEnumValues(value) {
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.pageNo = 1;
|
||||
if (Array.isArray(queryParams.value.classEnum)) {
|
||||
queryParams.value.classEnum =
|
||||
queryParams.value.classEnum.length > 0
|
||||
? queryParams.value.classEnum.join(',')
|
||||
: undefined;
|
||||
}
|
||||
getPageList();
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
align="center"
|
||||
key="name"
|
||||
prop="name"
|
||||
width="300"
|
||||
:show-overflow-tooltip="true"
|
||||
>
|
||||
<template #default="scope">
|
||||
@@ -51,8 +52,10 @@
|
||||
<el-select
|
||||
v-model="scope.row.organizationId"
|
||||
placeholder="请选择"
|
||||
:class="{ 'error-border': scope.row.error }"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
:class="{ 'error-border': scope.row.error }"
|
||||
filterable
|
||||
>
|
||||
<el-option
|
||||
v-for="item in departmentOptions"
|
||||
|
||||
@@ -183,6 +183,23 @@
|
||||
prop="statusEnum_enumText"
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column
|
||||
label="划价标记"
|
||||
align="center"
|
||||
key="pricingFlag_enumText"
|
||||
prop="pricingFlag_enumText"
|
||||
:show-overflow-tooltip="true"
|
||||
width="100"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-tag
|
||||
:type="scope.row.pricingFlag === 1 ? 'success' : scope.row.pricingFlag === 0 ? 'danger' : 'info'"
|
||||
size="small"
|
||||
>
|
||||
{{ scope.row.pricingFlag_enumText || (scope.row.pricingFlag === 1 ? '允许' : scope.row.pricingFlag === 0 ? '不允许' : '未设置') }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
align="center"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
@current-change="handleCurrentChange"
|
||||
row-key="patientId"
|
||||
@cell-click="clickRow"
|
||||
@row-click="clickRow"
|
||||
>
|
||||
<el-table-column label="名称" align="center" prop="adviceName" />
|
||||
<el-table-column label="类型" align="center" prop="activityType_enumText" />
|
||||
@@ -38,6 +39,10 @@ const props = defineProps({
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
popoverVisible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['selectAdviceBase']);
|
||||
const total = ref(0);
|
||||
@@ -61,30 +66,80 @@ const throttledGetList = throttle(
|
||||
watch(
|
||||
() => props.adviceQueryParams,
|
||||
(newValue) => {
|
||||
queryParams.value.searchKey = newValue.searchKey;
|
||||
queryParams.value.adviceType = newValue.adviceType;
|
||||
// 只有在弹窗打开时才响应 adviceQueryParams 的变化,避免选择项目后弹窗关闭时触发不必要的请求
|
||||
if (!props.popoverVisible) {
|
||||
return;
|
||||
}
|
||||
queryParams.value.searchKey = newValue?.searchKey;
|
||||
queryParams.value.adviceType = newValue?.adviceType;
|
||||
throttledGetList();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 监听弹窗打开状态,当弹窗打开时主动加载数据
|
||||
watch(
|
||||
() => props.popoverVisible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
// 弹窗打开时,确保 adviceQueryParams 同步到 queryParams
|
||||
if (props.adviceQueryParams) {
|
||||
queryParams.value.searchKey = props.adviceQueryParams.searchKey;
|
||||
queryParams.value.adviceType = props.adviceQueryParams.adviceType;
|
||||
}
|
||||
// 主动触发数据加载
|
||||
getList();
|
||||
} else {
|
||||
// 弹窗关闭时,清空列表数据,避免显示错误的数据
|
||||
adviceBaseList.value = [];
|
||||
total.value = 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 移除组件初始化时的 getList() 调用,避免在没有 adviceType 时查询所有类型的数据
|
||||
// getList();
|
||||
function getList() {
|
||||
// 验证是否已选择患者
|
||||
if (!props.patientInfo || Object.keys(props.patientInfo).length === 0) {
|
||||
console.log('[adviceBaseList] getList() 跳过:未选择患者');
|
||||
return; // 不执行API调用
|
||||
}
|
||||
|
||||
// 只有在弹窗打开时才执行查询
|
||||
if (!props.popoverVisible) {
|
||||
console.log('[adviceBaseList] getList() 跳过:弹窗未打开');
|
||||
return;
|
||||
}
|
||||
|
||||
// 必须有 adviceType 才查询,避免查询所有类型的数据
|
||||
if (!queryParams.value.adviceType) {
|
||||
console.log('[adviceBaseList] getList() 跳过:adviceType 未设置,当前值:', queryParams.value.adviceType);
|
||||
return;
|
||||
}
|
||||
|
||||
queryParams.value.organizationId = props.patientInfo.orgId;
|
||||
console.log('[adviceBaseList] getList() 请求参数:', JSON.stringify(queryParams.value));
|
||||
|
||||
getAdviceBaseInfo(queryParams.value).then((res) => {
|
||||
adviceBaseList.value = res.data.records;
|
||||
total.value = res.data.total;
|
||||
console.log('[adviceBaseList] getList() 响应数据:', {
|
||||
total: res.data?.total,
|
||||
recordsCount: res.data?.records?.length || 0,
|
||||
firstRecord: res.data?.records?.[0]?.adviceName || '无数据',
|
||||
adviceType: queryParams.value.adviceType
|
||||
});
|
||||
adviceBaseList.value = res.data.records || [];
|
||||
total.value = res.data.total || 0;
|
||||
nextTick(() => {
|
||||
currentIndex.value = 0;
|
||||
if (adviceBaseList.value.length > 0) {
|
||||
adviceBaseRef.value.setCurrentRow(adviceBaseList.value[0]);
|
||||
adviceBaseRef.value?.setCurrentRow(adviceBaseList.value[0]);
|
||||
}
|
||||
});
|
||||
}).catch((err) => {
|
||||
console.error('[adviceBaseList] getList() 请求失败:', err);
|
||||
adviceBaseList.value = [];
|
||||
total.value = 0;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -136,9 +191,13 @@ const handleCurrentChange = (currentRow) => {
|
||||
currentSelectRow.value = currentRow;
|
||||
};
|
||||
|
||||
function clickRow(row) {
|
||||
function clickRow(row, column, cell, event) {
|
||||
// cell-click 事件会传递 row, column, cell, event 四个参数
|
||||
// 确保传递的是完整的行数据
|
||||
if (row) {
|
||||
emit('selectAdviceBase', row);
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
handleKeyDown,
|
||||
|
||||
@@ -301,6 +301,7 @@ const nextId = ref(1);
|
||||
const unitCodeList = ref([]);
|
||||
const adviceTableRef = ref([]);
|
||||
const organization = ref([]);
|
||||
const orgTreeLoaded = ref(false);
|
||||
const rowRules = ref({
|
||||
conditionDefinitionId: [{ required: true, message: '请选择诊断', trigger: 'change' }],
|
||||
dose: [{ required: true, message: '请输入单次剂量', trigger: 'change' }],
|
||||
@@ -449,6 +450,10 @@ function handleDiagnosisChange(item, row) {
|
||||
|
||||
function handleFocus(row, index) {
|
||||
rowIndex.value = index;
|
||||
// 打开当前行弹窗前,先关闭其它行,避免多个弹窗同时存在
|
||||
prescriptionList.value.forEach((r, i) => {
|
||||
if (i !== index) r.showPopover = false;
|
||||
});
|
||||
// 如果当前行已选择adviceType,同步到adviceQueryParams
|
||||
if (row.adviceType !== undefined) {
|
||||
adviceQueryParams.value.adviceType = row.adviceType;
|
||||
@@ -457,7 +462,9 @@ function handleFocus(row, index) {
|
||||
}
|
||||
|
||||
function handleBlur(row) {
|
||||
row.showPopover = false;
|
||||
// 不能在 input blur 时立刻关闭弹窗:
|
||||
// 点击弹窗里的表格会先触发 blur,导致弹窗瞬间关闭,从而“点了项目没反应”
|
||||
// 弹窗关闭交给 selectAdviceBase()(选中后关闭)以及 handleFocus()(切行时关闭其他行)
|
||||
}
|
||||
|
||||
function handleChange(value) {
|
||||
@@ -465,10 +472,37 @@ function handleChange(value) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择药品回调
|
||||
* 选择药品/诊疗项目回调
|
||||
* 这里恢复为之前“能正常工作”的简单逻辑,只做最小必要的修正
|
||||
*/
|
||||
function selectAdviceBase(key, row) {
|
||||
getOrgList();
|
||||
if (!row) {
|
||||
console.error('[selectAdviceBase] row 为空');
|
||||
return;
|
||||
}
|
||||
|
||||
// rowIndex 理论上由 handleFocus 设置;防御一下越界
|
||||
if (rowIndex.value < 0 || rowIndex.value >= prescriptionList.value.length) {
|
||||
const foundIndex = prescriptionList.value.findIndex((item) => item.uniqueKey === key);
|
||||
if (foundIndex === -1) {
|
||||
console.error('[selectAdviceBase] 找不到对应行,key =', key);
|
||||
return;
|
||||
}
|
||||
rowIndex.value = foundIndex;
|
||||
}
|
||||
|
||||
// 关闭当前行弹窗
|
||||
const currentRow = prescriptionList.value[rowIndex.value];
|
||||
if (currentRow) {
|
||||
currentRow.showPopover = false;
|
||||
}
|
||||
|
||||
// 诊疗(adviceType=3) 才需要加载执行科室树,且只加载一次
|
||||
if (row.adviceType === 3) {
|
||||
ensureOrgTreeLoaded();
|
||||
}
|
||||
|
||||
// 构建单位列表(保持原有逻辑)
|
||||
unitCodeList.value = [];
|
||||
unitCodeList.value.push({ value: row.unitCode, label: row.unitCode_dictText, type: 'unit' });
|
||||
if (row.doseUnitCode != row.minUnitCode) {
|
||||
@@ -488,10 +522,14 @@ function selectAdviceBase(key, row) {
|
||||
type: 'minUnit',
|
||||
});
|
||||
}
|
||||
|
||||
// 将选中的基础项“覆盖”到当前处方行(这是之前正常工作的核心逻辑)
|
||||
prescriptionList.value[rowIndex.value] = {
|
||||
...prescriptionList.value[rowIndex.value],
|
||||
...JSON.parse(JSON.stringify(row)),
|
||||
};
|
||||
|
||||
// 后续字段处理保持原样
|
||||
prescriptionList.value[rowIndex.value].orgId = undefined;
|
||||
prescriptionList.value[rowIndex.value].dose = undefined;
|
||||
prescriptionList.value[rowIndex.value].unitCodeList = unitCodeList.value;
|
||||
@@ -500,12 +538,11 @@ function selectAdviceBase(key, row) {
|
||||
prescriptionList.value[rowIndex.value].minUnitCode = JSON.parse(JSON.stringify(row.doseUnitCode));
|
||||
prescriptionList.value[rowIndex.value].unitCode =
|
||||
row.partAttributeEnum == 1 ? row.minUnitCode : row.unitCode;
|
||||
// prescriptionList.value[rowIndex.value].doseUnitCode_dictText = row.minUnitCode_dictText;
|
||||
prescriptionList.value[rowIndex.value].definitionId = JSON.parse(
|
||||
JSON.stringify(row)
|
||||
).chargeItemDefinitionId;
|
||||
|
||||
// 库存列表 + 价格列表拼成批次号的下拉框
|
||||
// 库存列表 + 价格列表拼成批次号的下拉框(非诊疗)
|
||||
if (row.adviceType != 3) {
|
||||
if (row.inventoryList && row.inventoryList.length == 0) {
|
||||
expandOrder.value = [];
|
||||
@@ -532,9 +569,15 @@ function selectAdviceBase(key, row) {
|
||||
prescriptionList.value[rowIndex.value].positionName = stock.locationName;
|
||||
}
|
||||
} else {
|
||||
// 诊疗:设置执行科室和价格
|
||||
prescriptionList.value[rowIndex.value].orgId = JSON.parse(JSON.stringify(row)).positionId;
|
||||
if (row.priceList && row.priceList.length > 0) {
|
||||
prescriptionList.value[rowIndex.value].unitPrice = row.priceList[0].price;
|
||||
} else {
|
||||
prescriptionList.value[rowIndex.value].unitPrice = 0;
|
||||
}
|
||||
}
|
||||
|
||||
expandOrder.value = [key];
|
||||
nextTick(() => {
|
||||
if (row.adviceType == 1) {
|
||||
@@ -549,10 +592,17 @@ function selectAdviceBase(key, row) {
|
||||
});
|
||||
}
|
||||
|
||||
function getOrgList() {
|
||||
getOrgTree().then((res) => {
|
||||
organization.value = res.data.records;
|
||||
console.log(organization.value,"organization.value")
|
||||
function ensureOrgTreeLoaded() {
|
||||
if (orgTreeLoaded.value) return;
|
||||
orgTreeLoaded.value = true;
|
||||
getOrgTree()
|
||||
.then((res) => {
|
||||
// 组织机构树接口通常返回分页 records,这里做兼容兜底
|
||||
organization.value = res?.data?.records ?? res?.data ?? [];
|
||||
})
|
||||
.catch(() => {
|
||||
// 加载失败时允许重试
|
||||
orgTreeLoaded.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -706,6 +756,8 @@ function handleSaveSign(row, index) {
|
||||
row.contentJson = JSON.stringify(row);
|
||||
row.dbOpType = row.requestId ? '2' : '1';
|
||||
row.minUnitQuantity = row.quantity * row.partPercent;
|
||||
row.categoryEnum = row.adviceType
|
||||
console.log('row', row)
|
||||
savePrescription({ adviceSaveList: [row] }).then((res) => {
|
||||
if (res.code === 200) {
|
||||
proxy.$modal.msgSuccess('保存成功');
|
||||
|
||||
@@ -54,6 +54,17 @@ export function leaveEncounter(encounterId) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新排序未到诊患者
|
||||
*/
|
||||
export const rearrangeMissedNumber = (encounterId) => {
|
||||
return request({
|
||||
url: '/doctor-station/main/rearrange-missed-encounter', // 对应Controller的路径
|
||||
method: 'get', // 注意:现有接口都是GET,这里和后端保持一致
|
||||
params: { encounterId } // GET请求用params传参
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 完诊
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,675 @@
|
||||
<template>
|
||||
<el-dialog :model-value="dialogVisible" @update:model-value="val => emit('update:dialogVisible', val)" title=""
|
||||
width="90%" :close-on-click-modal="false" custom-class="call-dialog-custom">
|
||||
<!-- 顶部标题栏 -->
|
||||
<div class="dialog-header">
|
||||
<div class="header-title">医生叫号界面 - {{ department }}</div>
|
||||
<div class="header-time">{{ formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss') }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 当前就诊患者区域 -->
|
||||
<div class="current-patient">
|
||||
<div class="current-label">当前就诊患者</div>
|
||||
<div class="current-info">
|
||||
<div>
|
||||
<span>患者姓名:</span>
|
||||
<span class="info-value">{{ getPatientNo(currentCallPatient) }} {{ currentCallPatient.patientName || '暂无'
|
||||
}}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>诊室:</span>
|
||||
<span class="info-value">{{ roomNo || '4号' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 叫号按钮区 -->
|
||||
<div class="call-buttons">
|
||||
<el-button class="btn-next" @click="callNextPatient">下一患者</el-button>
|
||||
<el-button class="btn-recall" @click="showRecallDialog">选呼</el-button>
|
||||
<el-button class="btn-finish" @click="finishCallPatient">完成</el-button>
|
||||
<el-button class="btn-skip" @click="skipPatient">跳过</el-button>
|
||||
<el-button class="btn-requeue" @click="requeuePatient">过号重排</el-button>
|
||||
<el-button class="btn-seen" @click="markSeenPatient">已就诊</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 选呼对话框 -->
|
||||
<el-dialog v-model="recallDialogVisible" title="选择患者呼叫" width="600px" :close-on-click-modal="false">
|
||||
<div style="padding: 20px 0;">
|
||||
<el-select v-model="selectedPatientEncounterId" filterable remote reserve-keyword placeholder="搜索患者姓名、身份证号或就诊ID"
|
||||
:remote-method="remoteSearchPatient" :loading="patientSearchLoading" clearable
|
||||
style="width: 100%; margin-bottom: 20px;">
|
||||
<el-option v-for="patient in patientSearchOptions" :key="patient.encounterId"
|
||||
:label="`${patient.patientName} (${patient.idCard || '无身份证号'})`" :value="patient.encounterId">
|
||||
<div style="display: flex; justify-content: space-between;">
|
||||
<span>{{ patient.patientName }}</span>
|
||||
<span style="color: #8492a6; font-size: 14px;">{{ patient.idCard || '无身份证号' }}</span>
|
||||
</div>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<div style="text-align: right;">
|
||||
<el-button @click="recallDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmRecallPatient"
|
||||
:disabled="!selectedPatientEncounterId">确定呼叫</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 候诊患者列表标题 -->
|
||||
<div class="wait-list-title">候诊患者列表</div>
|
||||
|
||||
<!-- 候诊患者表格 -->
|
||||
<el-table :data="sortedWaitPatientList" border style="width: 100%" header-cell-class-name="table-header"
|
||||
:row-class-name="() => 'table-row'">
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column prop="patientName" label="患者" width="180">
|
||||
<template #default="scope">{{ scope.row.patientName }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="typeCode_dictText" label="号别" width="120" />
|
||||
<el-table-column prop="organizationName" label="诊室" width="120" />
|
||||
<el-table-column label="医生" width="120">
|
||||
<template #default="scope">{{ scope.row.jz_practitioner_name || '心内科医生' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="120">
|
||||
<template #default>等待中</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-button size="small" class="btn-receive" @click="callThisPatient(scope.row)">
|
||||
接诊
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 底部信息栏 -->
|
||||
<div class="footer-info">
|
||||
<div>当前时间:{{ formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss') }}</div>
|
||||
<div>当前号:{{ getPatientNo(currentCallPatient) || '暂无' }}</div>
|
||||
<div>等待人数:{{ currentWaitPatientList.length }}</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// ✅ 核心修复:h 从 vue 导入,而非 element-plus
|
||||
import { ref, watch, onMounted, h, computed } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { receiveEncounter, completeEncounter, leaveEncounter, rearrangeMissedNumber } from '../api.js';
|
||||
|
||||
// ===== 1. Props/Emits 定义 =====
|
||||
const props = defineProps({
|
||||
dialogVisible: Boolean,
|
||||
currentPatient: { type: Object, default: () => ({}) },
|
||||
currentPatientList: { type: Array, default: () => [] }, // 候诊列表
|
||||
roomNo: { type: String, default: '4号' },
|
||||
department: { type: String, default: '心内科' } // 科室名称,默认为心内科
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:dialogVisible', 'callNext', 'reCall', 'finish', 'skip', 'requeue', 'markSeen', 'callThis']);
|
||||
|
||||
// ===== 2. 响应式变量 =====
|
||||
const currentWaitPatientList = ref([]); // 候诊患者列表
|
||||
const currentCallPatient = ref({}); // 当前就诊患者
|
||||
const selectedId = ref(''); // 选呼患者ID
|
||||
|
||||
// 选呼功能相关变量
|
||||
const selectedPatientEncounterId = ref('');
|
||||
const patientSearchOptions = ref([]);
|
||||
const patientSearchLoading = ref(false);
|
||||
const patientSearchQuery = ref('');
|
||||
const recallDialogVisible = ref(false); // 选呼对话框显示/隐藏
|
||||
|
||||
// ===== 3. 计算属性 =====
|
||||
// 按创建时间(create_time)和过号时间(missed_time)排序后的候诊列表
|
||||
const sortedWaitPatientList = computed(() => {
|
||||
// 空列表直接返回
|
||||
if (!currentWaitPatientList.value.length) return [];
|
||||
|
||||
// 调试:打印当前时间和排序前的患者列表
|
||||
const now = new Date();
|
||||
console.log('当前时间:', now);
|
||||
console.log('排序前的患者列表:');
|
||||
currentWaitPatientList.value.forEach(item => {
|
||||
console.log(`患者 ${item.patientName}:`, {
|
||||
encounterId: item.encounterId,
|
||||
missed_time: item.missed_time,
|
||||
missedTime: item.missedTime,
|
||||
create_time: item.create_time,
|
||||
createTime: item.createTime,
|
||||
statusEnum: item.statusEnum,
|
||||
registerTime: item.registerTime,
|
||||
parsed_missed_time: item.missed_time ? new Date(item.missed_time) : null,
|
||||
parsed_create_time: item.create_time ? new Date(item.create_time) : null,
|
||||
parsed_registerTime: item.registerTime ? new Date(item.registerTime) : null
|
||||
});
|
||||
});
|
||||
|
||||
// 判断患者是否为过号患者
|
||||
const isMissedPatient = (patient) => {
|
||||
// 检查missed_time或missedTime是否存在且不为null
|
||||
return patient.missed_time != null || patient.missedTime != null;
|
||||
};
|
||||
|
||||
// 获取患者有效时间的辅助函数
|
||||
const getPatientTime = (patient) => {
|
||||
// 调试:打印患者的完整时间信息
|
||||
console.log(`患者 ${patient.patientName} 的时间信息:`, {
|
||||
missed_time: patient.missed_time,
|
||||
missedTime: patient.missedTime,
|
||||
registerTime: patient.registerTime,
|
||||
create_time: patient.create_time,
|
||||
createTime: patient.createTime
|
||||
});
|
||||
|
||||
// 1. 优先使用missed_time(过号时间),同时处理null和undefined
|
||||
if (patient.missed_time != null) {
|
||||
console.log(`患者 ${patient.patientName} 使用missed_time: ${patient.missed_time}`);
|
||||
return patient.missed_time;
|
||||
}
|
||||
if (patient.missedTime != null) {
|
||||
console.log(`患者 ${patient.patientName} 使用missedTime: ${patient.missedTime}`);
|
||||
return patient.missedTime;
|
||||
}
|
||||
// 2. 其次使用registerTime(挂号时间)
|
||||
if (patient.registerTime != null) {
|
||||
console.log(`患者 ${patient.patientName} 使用registerTime: ${patient.registerTime}`);
|
||||
return patient.registerTime;
|
||||
}
|
||||
// 3. 最后使用create_time或createTime(创建时间)
|
||||
if (patient.create_time != null) {
|
||||
console.log(`患者 ${patient.patientName} 使用create_time: ${patient.create_time}`);
|
||||
return patient.create_time;
|
||||
}
|
||||
if (patient.createTime != null) {
|
||||
console.log(`患者 ${patient.patientName} 使用createTime: ${patient.createTime}`);
|
||||
return patient.createTime;
|
||||
}
|
||||
// 4. 兜底:使用当前时间减去1000年,确保排到最前面
|
||||
console.warn(`患者 ${patient.patientName} 没有有效时间字段,使用默认时间`);
|
||||
return new Date(0); // 1970-01-01
|
||||
};
|
||||
|
||||
// 核心改进:明确区分过号患者和未过号患者
|
||||
// 1. 未过号患者排在过号患者前面
|
||||
// 2. 未过号患者按照registerTime升序排列
|
||||
// 3. 过号患者按照missed_time升序排列
|
||||
const sortedList = [...currentWaitPatientList.value].sort((a, b) => {
|
||||
const aIsMissed = isMissedPatient(a);
|
||||
const bIsMissed = isMissedPatient(b);
|
||||
|
||||
// 调试:打印排序比较
|
||||
console.log(`排序比较: ${a.patientName}(过号:${aIsMissed}) vs ${b.patientName}(过号:${bIsMissed})`);
|
||||
|
||||
// 1. 未过号患者排在过号患者前面
|
||||
if (aIsMissed !== bIsMissed) {
|
||||
const result = aIsMissed ? 1 : -1;
|
||||
console.log(`比较结果: ${result} (${aIsMissed ? 'a是过号患者,排后面' : 'b是过号患者,排后面'})`);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 2. 获取a和b的有效时间
|
||||
const aTime = getPatientTime(a);
|
||||
const bTime = getPatientTime(b);
|
||||
|
||||
// 解析时间,确保正确处理UTC时间
|
||||
const timeA = new Date(aTime);
|
||||
const timeB = new Date(bTime);
|
||||
|
||||
// 确保时间有效
|
||||
if (isNaN(timeA.getTime())) {
|
||||
console.log(`患者 ${a.patientName} 时间无效,排后面`);
|
||||
return 1; // 无效时间排后面
|
||||
}
|
||||
if (isNaN(timeB.getTime())) {
|
||||
console.log(`患者 ${b.patientName} 时间无效,排后面`);
|
||||
return -1; // 有效时间排前面
|
||||
}
|
||||
|
||||
// 3. 同类型患者按照时间升序排列
|
||||
const result = timeA - timeB;
|
||||
console.log(`比较结果: ${result} (时间比较: ${timeA} vs ${timeB})`);
|
||||
return result;
|
||||
});
|
||||
|
||||
// 调试:打印排序后的患者列表
|
||||
console.log('排序后的患者列表:');
|
||||
sortedList.forEach((item, index) => {
|
||||
const effectiveTime = getPatientTime(item);
|
||||
const isMissed = isMissedPatient(item);
|
||||
console.log(`${index + 1}. ${item.patientName}:`, {
|
||||
is_missed: isMissed,
|
||||
missed_time: item.missed_time,
|
||||
registerTime: item.registerTime,
|
||||
create_time: item.create_time || item.createTime,
|
||||
effective_time: new Date(effectiveTime)
|
||||
});
|
||||
});
|
||||
|
||||
return sortedList;
|
||||
});
|
||||
|
||||
// ===== 4. 临时 formatDate 函数(避免外部依赖报错)=====
|
||||
const formatDate = (date, format = 'YYYY-MM-DD') => {
|
||||
const d = new Date(date);
|
||||
const year = d.getFullYear();
|
||||
const month = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(d.getDate()).padStart(2, '0');
|
||||
const hour = String(d.getHours()).padStart(2, '0');
|
||||
const minute = String(d.getMinutes()).padStart(2, '0');
|
||||
const second = String(d.getSeconds()).padStart(2, '0');
|
||||
if (format === 'YYYY-MM-DD HH:mm:ss') {
|
||||
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
|
||||
}
|
||||
return `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
// ===== 4. 监听 Props 同步数据 =====
|
||||
watch(() => props.currentPatientList, (newVal) => {
|
||||
currentWaitPatientList.value = newVal || [];
|
||||
}, { immediate: true });
|
||||
|
||||
watch(() => props.currentPatient, (newVal) => {
|
||||
currentCallPatient.value = newVal || {};
|
||||
}, { immediate: true });
|
||||
|
||||
// ===== 5. 排队号计算 =====
|
||||
const getPatientNo = (patient) => {
|
||||
if (!patient || !patient.encounterId || sortedWaitPatientList.value.length === 0) return '';
|
||||
const index = sortedWaitPatientList.value.findIndex(item => item.encounterId === patient.encounterId);
|
||||
return index > -1 ? `${index + 1}号` : '';
|
||||
};
|
||||
|
||||
// ===== 6. 按钮核心逻辑 =====
|
||||
// 6.1 下一患者
|
||||
const callNextPatient = async () => {
|
||||
if (sortedWaitPatientList.value.length === 0) {
|
||||
ElMessage.warning('暂无候诊患者');
|
||||
return;
|
||||
}
|
||||
// 获取第一个患者
|
||||
const nextPatient = sortedWaitPatientList.value[0];
|
||||
try {
|
||||
// 调用接诊API
|
||||
await receiveEncounter(nextPatient.encounterId);
|
||||
// 更新当前呼叫患者
|
||||
currentCallPatient.value = nextPatient;
|
||||
// 通知父组件
|
||||
emit('callNext');
|
||||
ElMessage.success(`已呼叫下一位患者:${nextPatient.patientName}`);
|
||||
} catch (error) {
|
||||
console.error('呼叫下一位患者失败:', error);
|
||||
ElMessage.error(`呼叫下一位患者失败:${error.message || '系统错误'}`);
|
||||
}
|
||||
};
|
||||
|
||||
// 6.2 显示选呼对话框
|
||||
const showRecallDialog = () => {
|
||||
// 清空之前的选择
|
||||
selectedPatientEncounterId.value = '';
|
||||
patientSearchOptions.value = [];
|
||||
// 显示对话框
|
||||
recallDialogVisible.value = true;
|
||||
};
|
||||
|
||||
// 6.3 远程搜索患者
|
||||
const remoteSearchPatient = (query) => {
|
||||
if (query === '') {
|
||||
patientSearchOptions.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
patientSearchLoading.value = true;
|
||||
|
||||
// 模拟远程搜索,实际从候诊列表中查找
|
||||
setTimeout(() => {
|
||||
// 搜索匹配的患者 - 支持模糊搜索
|
||||
patientSearchOptions.value = sortedWaitPatientList.value.filter(patient => {
|
||||
// 患者姓名模糊匹配
|
||||
const nameMatch = patient.patientName && patient.patientName.toLowerCase().includes(query.toLowerCase());
|
||||
// 身份证号模糊匹配
|
||||
const idCardMatch = patient.idCard && patient.idCard.includes(query);
|
||||
// 就诊ID模糊匹配
|
||||
const encounterIdMatch = patient.encounterId && patient.encounterId.toString().includes(query);
|
||||
// 至少满足一个条件
|
||||
return nameMatch || idCardMatch || encounterIdMatch;
|
||||
});
|
||||
|
||||
patientSearchLoading.value = false;
|
||||
}, 200);
|
||||
};
|
||||
|
||||
// 6.4 确认呼叫患者
|
||||
const confirmRecallPatient = async () => {
|
||||
if (!selectedPatientEncounterId.value) return;
|
||||
|
||||
try {
|
||||
// 查找选中的患者
|
||||
const selectedPatient = sortedWaitPatientList.value.find(patient => patient.encounterId === selectedPatientEncounterId.value);
|
||||
if (!selectedPatient) {
|
||||
ElMessage.warning('未找到选中的患者');
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用接诊API
|
||||
await receiveEncounter(selectedPatient.encounterId);
|
||||
// 更新当前呼叫患者
|
||||
currentCallPatient.value = selectedPatient;
|
||||
// 通知父组件
|
||||
emit('reCall');
|
||||
// 关闭对话框
|
||||
recallDialogVisible.value = false;
|
||||
// 清空选择
|
||||
selectedPatientEncounterId.value = '';
|
||||
ElMessage.success(`已呼叫患者:${selectedPatient.patientName}`);
|
||||
} catch (error) {
|
||||
console.error('选呼患者失败:', error);
|
||||
ElMessage.error(`选呼患者失败:${error.message || '系统错误'}`);
|
||||
}
|
||||
};
|
||||
|
||||
// 6.5 完成
|
||||
const finishCallPatient = async () => {
|
||||
if (!currentCallPatient.value.encounterId) {
|
||||
ElMessage.warning('当前没有就诊患者');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await completeEncounter(currentCallPatient.value.encounterId);
|
||||
emit('finish');
|
||||
emit('update:dialogVisible', false);
|
||||
ElMessage.success('患者已完诊');
|
||||
} catch (error) {
|
||||
console.error('完诊失败:', error);
|
||||
ElMessage.error(`完诊失败:${error.message || '系统错误'}`);
|
||||
}
|
||||
};
|
||||
|
||||
// 6.6 跳过
|
||||
const skipPatient = async () => {
|
||||
if (sortedWaitPatientList.value.length === 0) {
|
||||
ElMessage.warning('暂无候诊患者');
|
||||
return;
|
||||
}
|
||||
// 获取第一个患者
|
||||
const nextPatient = sortedWaitPatientList.value[0];
|
||||
try {
|
||||
// 调用接诊API
|
||||
await receiveEncounter(nextPatient.encounterId);
|
||||
// 更新当前呼叫患者
|
||||
currentCallPatient.value = nextPatient;
|
||||
// 通知父组件
|
||||
emit('skip');
|
||||
ElMessage.success(`已跳过当前患者,呼叫下一位患者:${nextPatient.patientName}`);
|
||||
} catch (error) {
|
||||
console.error('跳过患者失败:', error);
|
||||
ElMessage.error(`跳过患者失败:${error.message || '系统错误'}`);
|
||||
}
|
||||
};
|
||||
|
||||
// 6.7 过号重排
|
||||
const requeuePatient = async () => {
|
||||
// 1. 校验:当前必须有就诊患者才能重排
|
||||
if (!currentCallPatient.value || !currentCallPatient.value.encounterId) {
|
||||
ElMessage.warning('当前没有就诊患者,无法过号重排');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 2. 显示确认对话框
|
||||
await ElMessageBox.confirm(
|
||||
`确定将患者 ${currentCallPatient.value.patientName} 进行过号重排吗?`,
|
||||
'过号重排确认',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
draggable: true
|
||||
}
|
||||
);
|
||||
|
||||
// 3. 调用过号重排接口
|
||||
console.log('开始调用过号重排接口,患者ID:', currentCallPatient.value.encounterId);
|
||||
const res = await rearrangeMissedNumber(currentCallPatient.value.encounterId);
|
||||
|
||||
// 4. 处理后端返回结果
|
||||
console.log('过号重排接口返回结果:', res);
|
||||
if (res.code === 200) {
|
||||
// 5. 直接通知父组件刷新候诊列表,依赖父组件重新获取数据
|
||||
emit('requeue');
|
||||
|
||||
// 6. 提示用户+清空当前就诊患者
|
||||
ElMessage.success(res.msg || '过号重排成功,患者已排至队尾');
|
||||
currentCallPatient.value = {};
|
||||
} else {
|
||||
// 7. 处理后端返回的错误信息
|
||||
console.error('过号重排失败,后端返回错误:', res);
|
||||
ElMessage.error(res.msg || '过号重排失败');
|
||||
}
|
||||
} catch (error) {
|
||||
// 处理取消操作
|
||||
if (error !== 'cancel') {
|
||||
console.error('过号重排失败,捕获异常:', error);
|
||||
ElMessage.error(`过号重排失败:${error.message || '系统错误'}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 6.8 标记已就诊
|
||||
const markSeenPatient = async () => {
|
||||
if (!currentCallPatient.value.encounterId) {
|
||||
ElMessage.warning('当前没有就诊患者');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await leaveEncounter(currentCallPatient.value.encounterId);
|
||||
emit('markSeen');
|
||||
emit('update:dialogVisible', false);
|
||||
ElMessage.success('患者已暂离');
|
||||
} catch (error) {
|
||||
console.error('暂离失败:', error);
|
||||
ElMessage.error(`暂离失败:${error.message || '系统错误'}`);
|
||||
}
|
||||
};
|
||||
|
||||
// 6.9 接诊指定患者
|
||||
const callThisPatient = async (row) => {
|
||||
try {
|
||||
// 调用接诊API
|
||||
await receiveEncounter(row.encounterId);
|
||||
// 更新当前呼叫患者
|
||||
currentCallPatient.value = row;
|
||||
// 通知父组件
|
||||
emit('callThis', row);
|
||||
ElMessage.success(`已接诊患者:${row.patientName}`);
|
||||
} catch (error) {
|
||||
console.error('接诊患者失败:', error);
|
||||
ElMessage.error(`接诊患者失败:${error.message || '系统错误'}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.call-dialog-custom {
|
||||
--el-dialog-body-padding: 0;
|
||||
}
|
||||
|
||||
.dialog-header {
|
||||
background-color: #0b8074;
|
||||
color: #fff;
|
||||
padding: 12px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.header-time {
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.current-patient {
|
||||
background-color: #f8fafc;
|
||||
padding: 20px 20px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.current-label {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #1f7a7a;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.current-info {
|
||||
display: flex;
|
||||
gap: 60px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-weight: 800 !important;
|
||||
color: #1f7a7a !important;
|
||||
font-size: 26px !important;
|
||||
background-color: #e6f7ff;
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
margin-left: 8px;
|
||||
display: inline-block;
|
||||
min-width: 200px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.call-buttons {
|
||||
padding: 0 20px 20px;
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.btn-next {
|
||||
background-color: #10b981 !important;
|
||||
border-color: #10b981 !important;
|
||||
color: #fff !important;
|
||||
font-size: 16px !important;
|
||||
padding: 14px 24px !important;
|
||||
font-weight: 600;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.btn-recall {
|
||||
background-color: #f59e0b !important;
|
||||
border-color: #f59e0b !important;
|
||||
color: #fff !important;
|
||||
font-size: 16px !important;
|
||||
padding: 14px 24px !important;
|
||||
font-weight: 600;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.btn-finish {
|
||||
background-color: #3b82f6 !important;
|
||||
border-color: #3b82f6 !important;
|
||||
color: #fff !important;
|
||||
font-size: 16px !important;
|
||||
padding: 14px 24px !important;
|
||||
font-weight: 600;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.btn-skip {
|
||||
background-color: #f5c518 !important;
|
||||
border-color: #f5c518 !important;
|
||||
color: #fff !important;
|
||||
font-size: 16px !important;
|
||||
padding: 14px 24px !important;
|
||||
font-weight: 600;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.btn-requeue {
|
||||
background-color: #165dff !important;
|
||||
border-color: #165dff !important;
|
||||
color: #fff !important;
|
||||
font-size: 16px !important;
|
||||
padding: 14px 24px !important;
|
||||
font-weight: 600;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.btn-seen {
|
||||
background-color: #86909c !important;
|
||||
border-color: #86909c !important;
|
||||
color: #fff !important;
|
||||
font-size: 16px !important;
|
||||
padding: 14px 24px !important;
|
||||
font-weight: 600;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.btn-receive {
|
||||
background-color: #10b981 !important;
|
||||
border-color: #10b981 !important;
|
||||
color: #fff !important;
|
||||
font-size: 14px !important;
|
||||
padding: 8px 12px !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.wait-list-title {
|
||||
padding: 0 20px 12px;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #1f7a7a;
|
||||
}
|
||||
|
||||
:deep(.table-header) {
|
||||
background-color: #1f7a7a !important;
|
||||
color: #fff !important;
|
||||
font-weight: 700 !important;
|
||||
font-size: 18px !important;
|
||||
}
|
||||
|
||||
:deep(.table-row) {
|
||||
font-size: 18px !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
:deep(.el-table__cell) {
|
||||
padding: 16px 0 !important;
|
||||
}
|
||||
|
||||
:deep(.el-table--enable-row-hover .el-table__body tr:hover>td) {
|
||||
background-color: #f0f9ff !important;
|
||||
}
|
||||
|
||||
.footer-info {
|
||||
background-color: #1f7a7a;
|
||||
color: #fff;
|
||||
padding: 14px 20px;
|
||||
margin-top: 20px;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -17,7 +17,7 @@
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--检验信息-->
|
||||
<el-table
|
||||
ref="inspectionTableRef"
|
||||
:data="inspectionList"
|
||||
@@ -29,17 +29,17 @@
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="申请ID" prop="applicationId" width="90" align="center" />
|
||||
<el-table-column label="申请单号" prop="applicationNo" width="140" align="center" />
|
||||
<el-table-column label="申请单号" prop="applyNo" width="140" align="center" />
|
||||
<el-table-column label="检验项目" prop="inspectionItem" width="150" align="center" />
|
||||
<el-table-column label="申请医生" prop="doctorName" width="120" align="center" />
|
||||
<el-table-column label="申请医生" prop="applyDocName" width="120" align="center" />
|
||||
<el-table-column label="急" width="60" align="center">
|
||||
<template #default="scope">
|
||||
<el-checkbox :model-value="scope.row.isUrgent" disabled />
|
||||
<el-checkbox :model-value="scope.row.priorityCode" disabled />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="收费" width="80" align="center">
|
||||
<template #default="scope">
|
||||
<el-checkbox :model-value="scope.row.needCharge" disabled />
|
||||
<el-checkbox :model-value="scope.row.applyStatus" disabled />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="退费" width="80" align="center">
|
||||
@@ -88,22 +88,22 @@
|
||||
<div class="application-form" style="padding: 20px; height: 700px; overflow-y: auto; border: 1px solid #e4e7ed; border-radius: 4px; margin: 10px;">
|
||||
<div style="margin-bottom: 20px">
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">申请单号</label>
|
||||
<el-input v-model="formData.applicationNo" disabled size="small" />
|
||||
<el-input v-model="formData.applyNo" readonly size="small" />
|
||||
</div>
|
||||
|
||||
<!-- 患者信息行 -->
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 15px; margin-bottom: 20px">
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">姓名</label>
|
||||
<el-input v-model="formData.patientName" disabled size="small" />
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">姓名<span style="color: #f56c6c">*</span></label>
|
||||
<el-input v-model="formData.patientName" readonly size="small" />
|
||||
</div>
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">就诊卡号</label>
|
||||
<el-input v-model="formData.cardNo" disabled size="small" />
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">就诊卡号<span style="color: #f56c6c">*</span></label>
|
||||
<el-input v-model="formData.medicalrecordNumber" readonly size="small" />
|
||||
</div>
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">费用性质</label>
|
||||
<el-select v-model="formData.feeType" placeholder="请选择费用性质" size="small" style="width: 100%">
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">费用性质<span style="color: #f56c6c">*</span></label>
|
||||
<el-select v-model="formData.natureofCost" placeholder="请选择费用性质" size="small" style="width: 100%">
|
||||
<el-option label="自费医疗" value="self" />
|
||||
<el-option label="医保" value="medical" />
|
||||
<el-option label="公费医疗" value="public" />
|
||||
@@ -115,10 +115,11 @@
|
||||
|
||||
<!-- 申请信息行 -->
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 15px; margin-bottom: 20px">
|
||||
<!--申请日期-->
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">申请日期</label>
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">申请日期<span style="color: #f56c6c">*</span></label>
|
||||
<el-date-picker
|
||||
v-model="formData.applicationDate"
|
||||
v-model="formData.applyTime"
|
||||
type="datetime"
|
||||
placeholder="选择日期时间"
|
||||
size="small"
|
||||
@@ -127,21 +128,16 @@
|
||||
style="width: 100%"
|
||||
/>
|
||||
</div>
|
||||
<!--申请科室-->
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">申请科室</label>
|
||||
<el-select v-model="formData.departmentName" placeholder="请选择申请科室" size="small" style="width: 100%" disabled>
|
||||
<el-option label="内科" value="internal" />
|
||||
<el-option label="外科" value="surgery" />
|
||||
<el-option label="儿科" value="pediatrics" />
|
||||
</el-select>
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">申请科室<span style="color: #f56c6c">*</span></label>
|
||||
<el-input v-model="formData.applyDepartment" readonly size="small" />
|
||||
</div>
|
||||
|
||||
<!--申请医生-->
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">申请医生</label>
|
||||
<el-select v-model="formData.doctorName" placeholder="请选择申请医生" size="small" style="width: 100%" disabled>
|
||||
<el-option label="张医生" value="doctor_zhang" />
|
||||
<el-option label="李医生" value="doctor_li" />
|
||||
<el-option label="王医生" value="doctor_wang" />
|
||||
</el-select>
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">申请医生<span style="color: #f56c6c">*</span></label>
|
||||
<el-input v-model="formData.applyDocName" readonly size="small" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -164,29 +160,29 @@
|
||||
<div v-if="validationErrors.executeDepartment" class="error-message">请选择执行科室</div>
|
||||
</div>
|
||||
|
||||
<!-- 诊断相关字段 -->
|
||||
<!-- 诊断描述 -->
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 20px">
|
||||
<div :class="{ 'form-item-error': validationErrors.diagnosisDesc }">
|
||||
<div :class="{ 'form-item-error': validationErrors.clinicDesc }">
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">诊断描述 <span style="color: #f56c6c">*</span></label>
|
||||
<el-input
|
||||
v-model="formData.diagnosisDesc"
|
||||
v-model="formData.clinicDesc"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
size="small"
|
||||
:class="{ 'is-error': validationErrors.diagnosisDesc }"
|
||||
:class="{ 'is-error': validationErrors.clinicDesc }"
|
||||
/>
|
||||
<div v-if="validationErrors.diagnosisDesc" class="error-message">请输入诊断描述</div>
|
||||
<div v-if="validationErrors.clinicDesc" class="error-message">请输入诊断描述</div>
|
||||
</div>
|
||||
<div :class="{ 'form-item-error': validationErrors.clinicalDiagnosis }">
|
||||
<div :class="{ 'form-item-error': validationErrors.clinicDiag }">
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">临床诊断 <span style="color: #f56c6c">*</span></label>
|
||||
<el-input
|
||||
v-model="formData.clinicalDiagnosis"
|
||||
v-model="formData.clinicDiag"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
size="small"
|
||||
:class="{ 'is-error': validationErrors.clinicalDiagnosis }"
|
||||
:class="{ 'is-error': validationErrors.clinicDiag }"
|
||||
/>
|
||||
<div v-if="validationErrors.clinicalDiagnosis" class="error-message">请输入临床诊断</div>
|
||||
<div v-if="validationErrors.clinicDiag" class="error-message">请输入临床诊断</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -194,29 +190,29 @@
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 20px">
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">禁忌症</label>
|
||||
<el-input v-model="formData.contraindications" type="textarea" :rows="2" size="small" />
|
||||
<el-input v-model="formData.contraindication" type="textarea" :rows="2" size="small" />
|
||||
</div>
|
||||
<div :class="{ 'form-item-error': validationErrors.medicalHistory }">
|
||||
<div :class="{ 'form-item-error': validationErrors.medicalHistorySummary }">
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">病史摘要 <span style="color: #f56c6c">*</span></label>
|
||||
<el-input
|
||||
v-model="formData.medicalHistory"
|
||||
v-model="formData.medicalHistorySummary"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
size="small"
|
||||
:class="{ 'is-error': validationErrors.medicalHistory }"
|
||||
:class="{ 'is-error': validationErrors.medicalHistorySummary }"
|
||||
/>
|
||||
<div v-if="validationErrors.medicalHistory" class="error-message">请输入病史摘要</div>
|
||||
<div v-if="validationErrors.medicalHistorySummary" class="error-message">请输入病史摘要</div>
|
||||
</div>
|
||||
<div :class="{ 'form-item-error': validationErrors.inspectionPurpose }">
|
||||
<div :class="{ 'form-item-error': validationErrors.purposeofInspection }">
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">检验目的 <span style="color: #f56c6c">*</span></label>
|
||||
<el-input
|
||||
v-model="formData.inspectionPurpose"
|
||||
v-model="formData.purposeofInspection"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
size="small"
|
||||
:class="{ 'is-error': validationErrors.inspectionPurpose }"
|
||||
:class="{ 'is-error': validationErrors.purposeofInspection }"
|
||||
/>
|
||||
<div v-if="validationErrors.inspectionPurpose" class="error-message">请输入检验目的</div>
|
||||
<div v-if="validationErrors.purposeofInspection" class="error-message">请输入检验目的</div>
|
||||
</div>
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">体格检查</label>
|
||||
@@ -232,7 +228,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold">备注</label>
|
||||
<el-input v-model="formData.remark" type="textarea" :rows="2" size="small" />
|
||||
<el-input v-model="formData.applyRemark" type="textarea" :rows="2" size="small" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -240,15 +236,14 @@
|
||||
<div style="margin-bottom: 20px; padding: 15px; background: #f8f9fa; border-radius: 4px; border: 1px solid #e9ecef">
|
||||
<label style="display: block; margin-bottom: 10px; font-weight: bold; color: #1a2b6d">状态设置</label>
|
||||
<div style="display: flex; gap: 30px; flex-wrap: wrap">
|
||||
<el-checkbox v-model="formData.isUrgent">急</el-checkbox>
|
||||
<el-checkbox v-model="formData.needCharge" checked>收费</el-checkbox>
|
||||
<el-checkbox v-model="formData.priorityCode" :true-value="1" :false-value="0">急</el-checkbox>
|
||||
<el-checkbox v-model="formData.applyStatus" :true-value="1" :false-value="0">收费</el-checkbox>
|
||||
<el-checkbox v-model="formData.needRefund">退费</el-checkbox>
|
||||
<el-checkbox v-model="formData.needExecute">执行</el-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="检验信息" name="inspectionInfo">
|
||||
<div style="padding: 20px; height: 700px; overflow-y: auto; border: 1px solid #e4e7ed; border-radius: 4px; margin: 10px;">
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 20px">
|
||||
@@ -290,22 +285,22 @@
|
||||
<div style="margin-top: 20px">
|
||||
<h4 style="margin-bottom: 10px; font-weight: bold">检验信息详情</h4>
|
||||
<el-table :data="selectedInspectionItems" border size="small" style="width: 100%" max-height="300">
|
||||
<el-table-column label="检验信息" prop="name" width="200" />
|
||||
<el-table-column label="项目名称" prop="itemName" width="200" />
|
||||
<el-table-column label="样本类型" prop="sampleType" width="80" align="center" />
|
||||
<el-table-column label="单位" prop="unit" width="60" align="center" />
|
||||
<el-table-column label="总量" width="60" align="center">
|
||||
<el-table-column label="总量" prop="itemQty" width="60" align="center">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.quantity || 1 }}</span>
|
||||
<span>{{ scope.row.itemQty || 1 }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单价" width="70" align="right">
|
||||
<el-table-column label="单价" prop="itemPrice" width="70" align="right">
|
||||
<template #default="scope">
|
||||
¥{{ scope.row.price }}
|
||||
¥{{ scope.row.itemPrice }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="金额" width="70" align="right">
|
||||
<el-table-column label="金额" prop="itemAmount" width="70" align="right">
|
||||
<template #default="scope">
|
||||
¥{{ (scope.row.quantity || 1) * scope.row.price }}
|
||||
¥{{ scope.row.itemAmount }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="服务费" width="70" align="right">
|
||||
@@ -371,8 +366,8 @@
|
||||
@change="toggleInspectionItem(item)"
|
||||
@click.stop
|
||||
/>
|
||||
<span class="item-name">{{ item.name }}</span>
|
||||
<span class="item-price">¥{{ item.price }}</span>
|
||||
<span class="item-itemName">{{ item.itemName }}</span>
|
||||
<span class="item-price">¥{{ item.itemPrice }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -395,8 +390,8 @@
|
||||
class="selected-tree-item"
|
||||
>
|
||||
<div class="selected-item-content">
|
||||
<span class="item-name">{{ item.name }}</span>
|
||||
<span class="item-price">¥{{ item.price }}</span>
|
||||
<span class="item-itemName">{{ item.itemName }}</span>
|
||||
<span class="item-price">¥{{ item.itemPrice }}</span>
|
||||
<el-button
|
||||
type="text"
|
||||
size="small"
|
||||
@@ -422,6 +417,13 @@
|
||||
import {onMounted, reactive, ref, watch} from 'vue'
|
||||
import {ElMessage, ElMessageBox} from 'element-plus'
|
||||
import {deleteInspectionApplication, getInspectionApplicationList, saveInspectionApplication} from '../api'
|
||||
import useUserStore from '@/store/modules/user.js'
|
||||
import {storeToRefs} from 'pinia'
|
||||
|
||||
// 在 onMounted 中调用初始化函数
|
||||
onMounted(async () => {
|
||||
await initData();
|
||||
})
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
@@ -443,6 +445,32 @@ const loading = ref(false)
|
||||
const total = ref(0)
|
||||
const leftActiveTab = ref('application')
|
||||
|
||||
// 用户信息store
|
||||
const userStore = useUserStore()
|
||||
const { id: userId, name: userName, nickName: userNickName } = storeToRefs(userStore)
|
||||
|
||||
// 修改 initData 函数
|
||||
async function initData() {
|
||||
// 然后执行原有的初始化逻辑
|
||||
if (props.patientInfo) {
|
||||
queryParams.encounterId = props.patientInfo.encounterId
|
||||
formData.visitNo = props.patientInfo.busNo || ''
|
||||
formData.patientId = props.patientInfo.patientId || ''
|
||||
formData.patientName = props.patientInfo.patientName || ''
|
||||
formData.medicalrecordNumber = props.patientInfo.identifierNo || ''
|
||||
formData.applyDepartment = props.patientInfo.organizationName || ''
|
||||
formData.operatorId = userId.value || ''
|
||||
formData.applyDocName = userNickName.value || userName.value || ''
|
||||
formData.applyDocCode = userId.value || ''
|
||||
formData.specimenName = '血液'
|
||||
}
|
||||
|
||||
// 只有在存在 encounterId 时才调用接口
|
||||
if (queryParams.encounterId) {
|
||||
getInspectionList()
|
||||
}
|
||||
}
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
@@ -456,43 +484,49 @@ const inspectionList = ref([])
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
applicationId: null,
|
||||
applicationNo: '202511210001',
|
||||
applyNo: '202511210001',
|
||||
patientId:'',
|
||||
patientName: '',
|
||||
cardNo: '',
|
||||
feeType: 'self',
|
||||
applicationDate: new Date(),
|
||||
departmentName: '',
|
||||
doctorName: '',
|
||||
medicalrecordNumber: '',
|
||||
natureofCost: 'self',
|
||||
applyTime: new Date(),
|
||||
applyDepartment: '',
|
||||
applyDocName: '',
|
||||
executeDepartment: 'medical_lab',
|
||||
diagnosisDesc: '',
|
||||
contraindications: '',
|
||||
clinicalDiagnosis: '',
|
||||
medicalHistory: '',
|
||||
inspectionPurpose: '',
|
||||
clinicDesc: '',
|
||||
contraindication: '',
|
||||
clinicDiag: '',
|
||||
medicalHistorySummary: '',
|
||||
purposeofInspection: '',
|
||||
physicalExam: '',
|
||||
inspectionItems: [],
|
||||
labApplyItemList: [],
|
||||
inspectionItemsText: '',
|
||||
remark: '',
|
||||
isUrgent: false,
|
||||
needCharge: true,
|
||||
applyRemark: '',
|
||||
priorityCode: 0,
|
||||
applyStatus: 1,
|
||||
needRefund: false,
|
||||
needExecute: false,
|
||||
inspectionDoctor: '',
|
||||
inspectionTime: null,
|
||||
auditDoctor: '',
|
||||
auditTime: null
|
||||
auditTime: null,
|
||||
visitNo: '',
|
||||
applyDocCode:'',
|
||||
applyDeptCode: '',
|
||||
operatorId: '',
|
||||
specimenName: '血液',
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = {
|
||||
feeType: [{ required: true, message: '请选择费用性质', trigger: 'change' }],
|
||||
applicationDate: [{ required: true, message: '请选择申请日期', trigger: 'change' }],
|
||||
natureofCost: [{ required: true, message: '请选择费用性质', trigger: 'change' }],
|
||||
applyTime: [{ required: true, message: '请选择申请日期', trigger: 'change' }],
|
||||
executeDepartment: [{ required: true, message: '请选择执行科室', trigger: 'change' }],
|
||||
diagnosisDesc: [{ required: true, message: '请输入诊断描述', trigger: 'blur' }],
|
||||
clinicalDiagnosis: [{ required: true, message: '请输入临床诊断', trigger: 'blur' }],
|
||||
medicalHistory: [{ required: true, message: '请输入病史摘要', trigger: 'blur' }],
|
||||
inspectionPurpose: [{ required: true, message: '请输入检验目的', trigger: 'blur' }],
|
||||
inspectionItems: [{ required: true, message: '请至少选择一个检验项目', trigger: 'change' }]
|
||||
clinicDesc: [{ required: true, message: '请输入诊断描述', trigger: 'blur' }],
|
||||
clinicDiag: [{ required: true, message: '请输入临床诊断', trigger: 'blur' }],
|
||||
medicalHistorySummary: [{ required: true, message: '请输入病史摘要', trigger: 'blur' }],
|
||||
purposeofInspection: [{ required: true, message: '请输入检验目的', trigger: 'blur' }],
|
||||
labApplyItemList: [{ required: true, message: '请至少选择一个检验项目', trigger: 'change' }]
|
||||
}
|
||||
|
||||
// 表单引用
|
||||
@@ -504,11 +538,11 @@ const inspectionTableRef = ref()
|
||||
// 验证错误状态
|
||||
const validationErrors = reactive({
|
||||
executeDepartment: false,
|
||||
diagnosisDesc: false,
|
||||
clinicalDiagnosis: false,
|
||||
medicalHistory: false,
|
||||
inspectionPurpose: false,
|
||||
inspectionItems: false
|
||||
clinicDesc: false,
|
||||
clinicDiag: false,
|
||||
medicalHistorySummary: false,
|
||||
purposeofInspection: false,
|
||||
labApplyItemList: false
|
||||
})
|
||||
|
||||
// 已选择的表格行
|
||||
@@ -524,15 +558,15 @@ const searchKeyword = ref('')
|
||||
const activeCategory = ref('biochemical')
|
||||
|
||||
// 检验项目分类(树形结构)
|
||||
const inspectionCategories = ref([
|
||||
let inspectionCategories = ref([
|
||||
{
|
||||
key: 'biochemical',
|
||||
label: '生化',
|
||||
expanded: true,
|
||||
items: [
|
||||
{ id: 1, name: '肝功能', price: 31, sampleType: '血清', unit: 'U/L', quantity: 1, serviceFee: 0, type: '生化', isSelfPay: false },
|
||||
{ id: 2, name: '肾功能', price: 28, sampleType: '血清', unit: 'U/L', quantity: 1, serviceFee: 0, type: '生化', isSelfPay: false },
|
||||
{ id: 3, name: '血糖', price: 15, sampleType: '血清', unit: 'mmol/L', quantity: 1, serviceFee: 0, type: '生化', isSelfPay: false }
|
||||
{ id: 1, itemName: '肝功能', itemPrice: 31, itemAmount: 31, sampleType: '血清', unit: 'U/L', itemQty: 1, serviceFee: 0, type: '生化', isSelfPay: false },
|
||||
{ id: 2, itemName: '肾功能', itemPrice: 28, itemAmount: 28, sampleType: '血清', unit: 'U/L', itemQty: 1, serviceFee: 0, type: '生化', isSelfPay: false },
|
||||
{ id: 3, itemName: '血糖', itemPrice: 15, itemAmount: 15, sampleType: '血清', unit: 'mmol/L', itemQty: 1, serviceFee: 0, type: '生化', isSelfPay: false }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -540,8 +574,8 @@ const inspectionCategories = ref([
|
||||
label: '临检',
|
||||
expanded: false,
|
||||
items: [
|
||||
{ id: 4, name: '血常规+crp', price: 50, sampleType: '全血', unit: '×10^9/L', quantity: 1, serviceFee: 0, type: '血液', isSelfPay: false },
|
||||
{ id: 5, name: '血常规(五分类)', price: 15, sampleType: '全血', unit: '×10^9/L', quantity: 1, serviceFee: 0, type: '血液', isSelfPay: false }
|
||||
{ id: 4, itemName: '血常规+crp', itemPrice: 50, itemAmount: 50, sampleType: '全血', unit: '×10^9/L', itemQty: 1, serviceFee: 0, type: '血液', isSelfPay: false },
|
||||
{ id: 5, itemName: '血常规(五分类)', itemPrice: 15, itemAmount: 15, sampleType: '全血', unit: '×10^9/L', itemQty: 1, serviceFee: 0, type: '血液', isSelfPay: false }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -549,7 +583,7 @@ const inspectionCategories = ref([
|
||||
label: '尿液',
|
||||
expanded: false,
|
||||
items: [
|
||||
{ id: 6, name: '尿常规', price: 20, sampleType: '尿液', unit: '细胞/μl', quantity: 1, serviceFee: 0, type: '尿液', isSelfPay: false }
|
||||
{ id: 6, itemName: '尿常规', itemPrice: 20, itemAmount: 20, sampleType: '尿液', unit: '细胞/μl', itemQty: 1, serviceFee: 0, type: '尿液', isSelfPay: false }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -557,8 +591,8 @@ const inspectionCategories = ref([
|
||||
label: '免疫',
|
||||
expanded: false,
|
||||
items: [
|
||||
{ id: 7, name: '总IgE测定', price: 30, sampleType: '血清', unit: 'IU/ml', quantity: 1, serviceFee: 0, type: '免疫', isSelfPay: false },
|
||||
{ id: 8, name: '风湿', price: 119, sampleType: '血清', unit: 'U/ml', quantity: 1, serviceFee: 0, type: '免疫', isSelfPay: false }
|
||||
{ id: 7, itemName: '总IgE测定', itemPrice: 30, itemAmount: 30, sampleType: '血清', unit: 'IU/ml', itemQty: 1, serviceFee: 0, type: '免疫', isSelfPay: false },
|
||||
{ id: 8, itemName: '风湿', itemPrice: 119, itemAmount: 119, sampleType: '血清', unit: 'U/ml', itemQty: 1, serviceFee: 0, type: '免疫', isSelfPay: false }
|
||||
]
|
||||
}
|
||||
])
|
||||
@@ -573,70 +607,39 @@ const getFilteredItems = (categoryKey) => {
|
||||
}
|
||||
|
||||
return category.items.filter(item =>
|
||||
item.name.toLowerCase().includes(searchKeyword.value.toLowerCase())
|
||||
item.itemName.toLowerCase().includes(searchKeyword.value.toLowerCase())
|
||||
)
|
||||
}
|
||||
|
||||
// 初始化数据
|
||||
function initData() {
|
||||
console.log('检验组件初始化,patientInfo:', props.patientInfo)
|
||||
if (props.patientInfo) {
|
||||
// 确保 encounterId 存在且有效,优先使用 encounterId,其次尝试 id,最后尝试 patientId
|
||||
queryParams.encounterId = props.patientInfo.encounterId || props.patientInfo.id || props.patientInfo.patientId
|
||||
formData.patientName = props.patientInfo.patientName || ''
|
||||
formData.cardNo = props.patientInfo.cardNo || ''
|
||||
formData.departmentName = props.patientInfo.departmentName || ''
|
||||
formData.doctorName = props.patientInfo.doctorName || ''
|
||||
}
|
||||
|
||||
// 只有在存在有效的 encounterId 时才调用接口
|
||||
if (queryParams.encounterId && queryParams.encounterId !== 'undefined' && queryParams.encounterId !== 'null' && queryParams.encounterId !== '') {
|
||||
getInspectionList()
|
||||
} else {
|
||||
console.warn('缺少有效的就诊ID,无法获取检验申请单列表')
|
||||
inspectionList.value = []
|
||||
total.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
// 获取检验申请单列表
|
||||
function getInspectionList() {
|
||||
// 先检查是否有有效的encounterId
|
||||
if (!queryParams.encounterId || queryParams.encounterId === 'undefined' || queryParams.encounterId === 'null') {
|
||||
console.warn('缺少有效的就诊ID,无法获取检验申请单列表')
|
||||
inspectionList.value = []
|
||||
total.value = 0
|
||||
loading.value = false
|
||||
// 如果没有encounterId,不调用接口
|
||||
if (!queryParams.encounterId) {
|
||||
// console.warn('【检验】encounterId为空,不调用接口')
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
|
||||
// 调用真实的API,只传递 encounterId 参数
|
||||
// console.log('【检验】调用API,encounterId:', queryParams.encounterId)
|
||||
getInspectionApplicationList({ encounterId: queryParams.encounterId }).then((res) => {
|
||||
if (res.code === 200) {
|
||||
inspectionList.value = res.data || []
|
||||
total.value = res.data?.length || 0
|
||||
// console.log('【检验】获取数据成功,数量:', inspectionList.value.length)
|
||||
} else {
|
||||
inspectionList.value = []
|
||||
total.value = 0
|
||||
console.error('获取检验申请单列表失败:', res.message || res.msg)
|
||||
ElMessage.error(res.message || res.msg || '获取检验申请单列表失败')
|
||||
ElMessage.error('获取检验申请单列表失败')
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error('获取检验申请单列表异常:', error)
|
||||
// console.error('获取检验申请单列表异常:', error)
|
||||
inspectionList.value = []
|
||||
total.value = 0
|
||||
// 提供更友好的错误信息
|
||||
let errorMessage = '获取检验申请单列表异常'
|
||||
if (error.response) {
|
||||
errorMessage += ` (${error.response.status}): ${error.response.data.message || error.response.statusText}`
|
||||
} else if (error.request) {
|
||||
errorMessage += ': 网络请求失败,请检查网络连接'
|
||||
} else {
|
||||
errorMessage += `: ${error.message}`
|
||||
}
|
||||
ElMessage.error(errorMessage)
|
||||
ElMessage.error('获取检验申请单列表异常')
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
@@ -644,10 +647,12 @@ function getInspectionList() {
|
||||
|
||||
// 新增申请单
|
||||
function handleNewApplication() {
|
||||
console.log('点击新增按钮')
|
||||
// console.log('点击新增按钮')
|
||||
resetForm()
|
||||
// 生成申请单号
|
||||
formData.applicationNo = generateApplicationNo()
|
||||
formData.applyNo = generateApplicationNo()
|
||||
// 确保申请医生是当前登录医生
|
||||
formData.applyDocName = userNickName.value || userName.value || ''
|
||||
leftActiveTab.value = 'application'
|
||||
}
|
||||
|
||||
@@ -661,30 +666,37 @@ function generateApplicationNo() {
|
||||
return `${year}${month}${day}${String(timestamp).slice(-3)}`
|
||||
}
|
||||
|
||||
|
||||
// 重置表单
|
||||
function resetForm() {
|
||||
async function resetForm() {
|
||||
Object.assign(formData, {
|
||||
applicationId: null,
|
||||
applicationNo: '',
|
||||
apply: '',
|
||||
patientName: props.patientInfo.patientName || '',
|
||||
cardNo: props.patientInfo.cardNo || '',
|
||||
feeType: 'self',
|
||||
applicationDate: new Date(),
|
||||
departmentName: props.patientInfo.departmentName || '',
|
||||
doctorName: props.patientInfo.doctorName || '',
|
||||
medicalrecordNumber: props.patientInfo.identifierNo || '',
|
||||
natureofCost: 'self',
|
||||
applyTime: new Date(),
|
||||
applyDepartment: props.patientInfo.organizationName || '',
|
||||
applyDeptCode: '',
|
||||
applyDocCode: userId.value || '',
|
||||
applyDocName: userNickName.value || userName.value || '',
|
||||
operatorId: userId.value || '',
|
||||
executeDepartment: 'medical_lab',
|
||||
diagnosisDesc: '',
|
||||
contraindications: '',
|
||||
clinicalDiagnosis: '',
|
||||
medicalHistory: '',
|
||||
inspectionPurpose: '',
|
||||
clinicDesc: '',
|
||||
contraindication: '',
|
||||
clinicDiag: '',
|
||||
medicalHistorySummary: '',
|
||||
purposeofInspection: '',
|
||||
physicalExam: '',
|
||||
inspectionItems: [],
|
||||
remark: '',
|
||||
isUrgent: false,
|
||||
needCharge: true,
|
||||
labApplyItemList: [],
|
||||
applyRemark: '',
|
||||
priorityCode: 0,
|
||||
applyStatus: 1,
|
||||
needRefund: false,
|
||||
needExecute: false
|
||||
needExecute: false,
|
||||
patientId: props.patientInfo.patientId || '',
|
||||
visitNo: '',
|
||||
specimenName: '血液',
|
||||
})
|
||||
selectedInspectionItems.value = []
|
||||
|
||||
@@ -712,32 +724,43 @@ function handleSave() {
|
||||
let hasErrors = false
|
||||
|
||||
// 检查必填字段
|
||||
// 检查必填字段,执行科室
|
||||
if (!formData.executeDepartment) {
|
||||
validationErrors.executeDepartment = true
|
||||
hasErrors = true
|
||||
}
|
||||
if (!formData.diagnosisDesc.trim()) {
|
||||
validationErrors.diagnosisDesc = true
|
||||
// 检查必填字段,诊断描述
|
||||
if (!formData.clinicDesc.trim()) {
|
||||
validationErrors.clinicDesc = true
|
||||
hasErrors = true
|
||||
}
|
||||
if (!formData.clinicalDiagnosis.trim()) {
|
||||
validationErrors.clinicalDiagnosis = true
|
||||
// 检查必填字段,临床诊断
|
||||
if (!formData.clinicDiag.trim()) {
|
||||
validationErrors.clinicDiag = true
|
||||
hasErrors = true
|
||||
}
|
||||
if (!formData.medicalHistory.trim()) {
|
||||
validationErrors.medicalHistory = true
|
||||
// 检查必填字段,病史摘要
|
||||
if (!formData.medicalHistorySummary.trim()) {
|
||||
validationErrors.medicalHistorySummary = true
|
||||
hasErrors = true
|
||||
}
|
||||
if (!formData.inspectionPurpose.trim()) {
|
||||
validationErrors.inspectionPurpose = true
|
||||
// 检查必填字段,检验目的
|
||||
if (!formData.purposeofInspection.trim()) {
|
||||
validationErrors.purposeofInspection = true
|
||||
hasErrors = true
|
||||
}
|
||||
// 检查必填字段,检验项目
|
||||
if (selectedInspectionItems.value.length === 0) {
|
||||
validationErrors.inspectionItems = true
|
||||
validationErrors.labApplyItemList = true
|
||||
hasErrors = true
|
||||
ElMessage.error('请至少选择一项检验项目')
|
||||
return
|
||||
}
|
||||
// 检查必填字段,申请日期
|
||||
if(!formData.applyTime || (typeof formData.applyTime === 'string' && !formData.applyTime.trim())) {
|
||||
validationErrors.applyTime = true
|
||||
hasErrors = true
|
||||
}
|
||||
|
||||
if (hasErrors) {
|
||||
ElMessage.error('请填写所有必填字段')
|
||||
@@ -747,11 +770,11 @@ function handleSave() {
|
||||
// 准备保存数据
|
||||
const saveData = {
|
||||
...formData,
|
||||
inspectionItems: selectedInspectionItems.value,
|
||||
inspectionItemsText: selectedInspectionItems.value.map(item => item.name).join('、'),
|
||||
amount: selectedInspectionItems.value.reduce((sum, item) => sum + item.price, 0),
|
||||
labApplyItemList: selectedInspectionItems.value,
|
||||
inspectionItemsText: selectedInspectionItems.value.map(item => item.itemName).join('、'),
|
||||
amount: selectedInspectionItems.value.reduce((sum, item) => sum + item.itemAmount, 0),
|
||||
serviceFee: selectedInspectionItems.value.reduce((sum, item) => sum + (item.serviceFee || 0), 0),
|
||||
totalAmount: selectedInspectionItems.value.reduce((sum, item) => sum + item.price + (item.serviceFee || 0), 0)
|
||||
totalAmount: selectedInspectionItems.value.reduce((sum, item) => sum + item.itemAmount + (item.serviceFee || 0), 0)
|
||||
}
|
||||
|
||||
console.log('保存检验申请单数据:', saveData)
|
||||
@@ -768,7 +791,7 @@ function handleSave() {
|
||||
ElMessage.error(res.message || '保存失败')
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error('保存检验申请单异常:', error)
|
||||
// console.error('保存检验申请单异常:', error)
|
||||
ElMessage.error('保存异常')
|
||||
})
|
||||
}
|
||||
@@ -777,8 +800,8 @@ function handleSave() {
|
||||
function handleFormSave() {
|
||||
formRef.value?.validate((valid) => {
|
||||
if (valid) {
|
||||
formData.inspectionItems = selectedInspectionItems.value.map(item => item.id)
|
||||
console.log('保存检验申请单表单数据:', formData)
|
||||
formData.labApplyItemList = selectedInspectionItems.value.map(item => item.id)
|
||||
// console.log('保存检验申请单表单数据:', formData)
|
||||
ElMessage.success('保存成功')
|
||||
showForm.value = false
|
||||
getInspectionList()
|
||||
@@ -788,7 +811,7 @@ function handleFormSave() {
|
||||
|
||||
// 查看详情
|
||||
function handleView(row) {
|
||||
console.log('点击查看按钮,数据:', row)
|
||||
// console.log('点击查看按钮,数据:', row)
|
||||
// 加载表单数据
|
||||
Object.assign(formData, row)
|
||||
|
||||
@@ -828,7 +851,7 @@ function switchCategory(category) {
|
||||
// 处理搜索
|
||||
function handleSearch() {
|
||||
// 搜索逻辑已在getFilteredItems中实现,这里可以添加额外的搜索逻辑
|
||||
console.log('搜索关键词:', searchKeyword.value)
|
||||
// console.log('搜索关键词:', searchKeyword.value)
|
||||
}
|
||||
|
||||
// 处理项目项点击(排除勾选框点击)
|
||||
@@ -847,7 +870,12 @@ function toggleInspectionItem(item) {
|
||||
if (index > -1) {
|
||||
selectedInspectionItems.value.splice(index, 1)
|
||||
} else {
|
||||
selectedInspectionItems.value.push({ ...item })
|
||||
// 创建新对象,包含 itemName 属性(等于 name 属性)
|
||||
const newItem = {
|
||||
...item,
|
||||
itemName: item.itemName
|
||||
};
|
||||
selectedInspectionItems.value.push(newItem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -883,7 +911,7 @@ function handleSelectionChange(selection) {
|
||||
|
||||
// 打印申请单
|
||||
function handlePrint(row) {
|
||||
console.log('打印申请单:', row)
|
||||
// console.log('打印申请单:', row)
|
||||
|
||||
// 切换到申请单TAB
|
||||
leftActiveTab.value = 'application'
|
||||
@@ -957,7 +985,7 @@ function handlePrint(row) {
|
||||
// 删除申请单
|
||||
function handleDelete(row) {
|
||||
ElMessageBox.confirm(
|
||||
`确定要删除申请单 "${row.applicationNo}" 吗?此操作不可撤销。`,
|
||||
`确定要删除申请单 "${row.applyNo}" 吗?此操作不可撤销。`,
|
||||
'删除确认',
|
||||
{
|
||||
confirmButtonText: '确定删除',
|
||||
@@ -966,7 +994,7 @@ function handleDelete(row) {
|
||||
confirmButtonClass: 'el-button--danger'
|
||||
}
|
||||
).then(() => {
|
||||
console.log('删除申请单:', row)
|
||||
// console.log('删除申请单:', row)
|
||||
// 调用真实的API删除
|
||||
deleteInspectionApplication(row.applicationId).then((res) => {
|
||||
if (res.code === 200) {
|
||||
@@ -977,7 +1005,7 @@ function handleDelete(row) {
|
||||
ElMessage.error(res.message || '删除失败')
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error('删除检验申请单异常:', error)
|
||||
// console.error('删除检验申请单异常:', error)
|
||||
ElMessage.error('删除异常')
|
||||
})
|
||||
}).catch(() => {
|
||||
@@ -991,9 +1019,9 @@ function handleCellClick(row, column) {
|
||||
}
|
||||
|
||||
// 监听activeTab变化
|
||||
watch(() => props.activeTab, (newVal) => {
|
||||
watch(() => props.activeTab, async (newVal) => {
|
||||
if (newVal === 'inspection') {
|
||||
initData()
|
||||
await initData()
|
||||
// 默认展开生化分类
|
||||
activeCategory.value = 'biochemical'
|
||||
inspectionCategories.value.forEach(cat => {
|
||||
@@ -1002,33 +1030,32 @@ watch(() => props.activeTab, (newVal) => {
|
||||
}
|
||||
})
|
||||
|
||||
// 监听patientInfo变化,确保在患者信息更新时也更新检验申请单列表
|
||||
watch(() => props.patientInfo, (newPatientInfo) => {
|
||||
if (newPatientInfo && Object.keys(newPatientInfo).length > 0) {
|
||||
// 更新encounterId
|
||||
queryParams.encounterId = newPatientInfo.encounterId || newPatientInfo.id || newPatientInfo.patientId
|
||||
// 如果有有效的encounterId,则获取检验申请单列表
|
||||
if (queryParams.encounterId && queryParams.encounterId !== 'undefined' && queryParams.encounterId !== 'null' && queryParams.encounterId !== '') {
|
||||
getInspectionList()
|
||||
// 监听patientInfo变化,确保encounterId及时更新
|
||||
watch(() => props.patientInfo, async (newVal) => {
|
||||
// console.log('【检验】patientInfo变化:', newVal)
|
||||
console.log('【检验】接收到的完整patientInfo:', JSON.stringify(newVal, null, 2))
|
||||
if (newVal && newVal.encounterId) {
|
||||
queryParams.encounterId = newVal.encounterId
|
||||
// console.log('【检验】更新encounterId:', queryParams.encounterId)
|
||||
|
||||
// 更新科室编码
|
||||
// const currentDeptCode = await getCurrentDeptCode();
|
||||
// formData.applyDeptCode = currentDeptCode || '';
|
||||
}
|
||||
}
|
||||
}, { deep: true })
|
||||
}, { deep: true, immediate: true })
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
initData()
|
||||
onMounted(async () => {
|
||||
await initData()
|
||||
})
|
||||
|
||||
// 暴露方法
|
||||
defineExpose({
|
||||
getList() {
|
||||
// 在调用getList时,先检查是否有有效的patientInfo,如果有则更新encounterId
|
||||
if (props.patientInfo && Object.keys(props.patientInfo).length > 0) {
|
||||
queryParams.encounterId = props.patientInfo.encounterId || props.patientInfo.id || props.patientInfo.patientId;
|
||||
}
|
||||
getInspectionList();
|
||||
}
|
||||
getList: getInspectionList
|
||||
})
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -1207,7 +1234,7 @@ defineExpose({
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
.item-itemName {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@@ -1392,7 +1419,7 @@ defineExpose({
|
||||
border-left: 3px solid #409eff;
|
||||
}
|
||||
|
||||
.inspection-tree-item .item-name {
|
||||
.inspection-tree-item .item-itemName {
|
||||
flex: 1;
|
||||
margin-left: 10px;
|
||||
font-weight: 500;
|
||||
@@ -1436,7 +1463,7 @@ defineExpose({
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
.selected-item-content .item-name {
|
||||
.selected-item-content .item-itemName {
|
||||
flex: 1;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@@ -741,7 +741,7 @@
|
||||
check-strictly
|
||||
default-expand-all
|
||||
:ref="(el) => (inputRefs.orgId = el)"
|
||||
@change="(value) => handleOrgChange(value, scope.$index)"
|
||||
@change="(value) => handleOrgChange(value, scope.$index, scope.row)"
|
||||
placeholder="请选择执行科室"
|
||||
/>
|
||||
</el-form-item>
|
||||
@@ -1104,6 +1104,10 @@ const isAdding = ref(false);
|
||||
const isSaving = ref(false);
|
||||
const prescriptionRef = ref();
|
||||
const expandOrder = ref([]); //目前的展开行
|
||||
// 更新展开行的函数
|
||||
const updateExpandOrder = (keys) => {
|
||||
expandOrder.value = keys;
|
||||
};
|
||||
const stockList = ref([]);
|
||||
const contractList = ref([]);
|
||||
const conditionId = ref('');
|
||||
@@ -2261,7 +2265,7 @@ function handleSaveSign(row, index, prescriptionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formRefName = 'formRef_' + targetPrescriptionId + '_' + actualIndex;
|
||||
const formRefName = 'formRef' + actualIndex;
|
||||
let formRef = proxy.$refs[formRefName];
|
||||
|
||||
console.log('handleSaveSign - 查找表单引用:', formRefName, '是否找到:', !!formRef, '类型:', typeof formRef, 'formRef:', formRef);
|
||||
@@ -2663,7 +2667,7 @@ const handleEnter = (currentProp, row, rowIndex, prescriptionId) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const formRefName = 'formRef_' + targetPrescriptionId + '_' + actualIndex;
|
||||
const formRefName = 'formRef' + actualIndex;
|
||||
let formRef = proxy.$refs[formRefName];
|
||||
|
||||
// 如果 formRef 是数组,取第一个元素
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
<template>
|
||||
<div style="display: flex; justify-content: space-between; height: 90vh">
|
||||
<div style="width: 15%; height: 100%; border: 1px solid #eee; border-right: 0">
|
||||
<div style="padding: 10px; border: 1px solid #eee; height: 50px; border-right: 0">
|
||||
<span>现诊患者</span>
|
||||
<el-badge :value="waitCount > 0 ? waitCount : ''" :max="10"
|
||||
style="float: right; color: #409eff; cursor: pointer; margin-right: 10px">
|
||||
<span @click="openDrawer"> 患者队列 </span>
|
||||
<div
|
||||
style="padding: 10px; border: 1px solid #eee; height: 50px; border-right: 0; display: flex; align-items: center; justify-content: space-between">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<span style="margin-right: 20px; font-weight: 600;">现诊患者</span>
|
||||
<el-button type="primary" size="small" @click.stop="handleOpenCallDialog" title="点击打开叫号界面">
|
||||
<i class="el-icon-bell"></i> 呼叫
|
||||
</el-button>
|
||||
</div>
|
||||
<el-badge :value="waitCount > 0 ? waitCount : ''" :max="10" style="color: #409eff; cursor: pointer;">
|
||||
<span @click="openDrawer" style="font-weight: 600;"> 患者队列 </span>
|
||||
</el-badge>
|
||||
</div>
|
||||
<div style="width: 100%; padding: 10px">
|
||||
@@ -15,16 +20,9 @@
|
||||
<el-button icon="Search" @click="getPatientList" />
|
||||
</template>
|
||||
</el-input>
|
||||
<el-date-picker
|
||||
v-model="registerTime"
|
||||
@change="handleTimeChange"
|
||||
type="date"
|
||||
style="width: 100%; margin-bottom: 10px"
|
||||
:clearable="false"
|
||||
placeholder="挂号时间"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
<el-date-picker v-model="registerTime" @change="handleTimeChange" type="date"
|
||||
style="width: 100%; margin-bottom: 10px" :clearable="false" placeholder="挂号时间" format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD" />
|
||||
<el-scrollbar height="700px">
|
||||
<div v-for="(item, index) in patientList" :class="item.active ? 'patient-card actived' : 'patient-card'"
|
||||
:key="item.id" @click="handleCardClick(item, index)">
|
||||
@@ -110,27 +108,22 @@
|
||||
<el-button type="primary" plain @click.stop="handleRefund(patientInfo.encounterId)">
|
||||
退费
|
||||
</el-button>
|
||||
<el-button type="primary" plain class="top-layer-btn" @click.stop="getEnPrescription(patientInfo.encounterId)">
|
||||
<el-button type="primary" plain class="top-layer-btn"
|
||||
@click.stop="getEnPrescription(patientInfo.encounterId)">
|
||||
处方单
|
||||
</el-button>
|
||||
<el-button type="primary" plain class="top-layer-btn" :disabled="isHospitalizationButtonDisabled" @click.stop="handleHospitalizationClick()" @mouseenter="console.log('办理住院按钮状态:', { patientInfo: patientInfo?.value, hasEncounterId: patientInfo?.value?.encounterId, isDisabled: isHospitalizationButtonDisabled })"> 办理住院 </el-button>
|
||||
<el-button type="primary" plain class="top-layer-btn" :disabled="isHospitalizationButtonDisabled"
|
||||
@click.stop="handleHospitalizationClick()"
|
||||
@mouseenter="console.log('办理住院按钮状态:', { patientInfo: patientInfo?.value, hasEncounterId: patientInfo?.value?.encounterId, isDisabled: isHospitalizationButtonDisabled })">
|
||||
办理住院 </el-button>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
<div style="padding: 10px; position: relative">
|
||||
<el-tabs
|
||||
type="card"
|
||||
style="width: 100%; height: 100%"
|
||||
v-loading="loading"
|
||||
v-model="activeTab"
|
||||
@tab-change="handleClick(activeTab)"
|
||||
>
|
||||
<el-tabs type="card" style="width: 100%; height: 100%" v-loading="loading" v-model="activeTab"
|
||||
@tab-change="handleClick(activeTab)">
|
||||
<el-tab-pane label="门诊病历" name="hospitalizationEmr">
|
||||
<hospitalizationEmr
|
||||
:patientInfo="patientInfo"
|
||||
:activeTab="activeTab"
|
||||
@emrSaved="handleEmrSaved"
|
||||
/>
|
||||
<hospitalizationEmr :patientInfo="patientInfo" :activeTab="activeTab" @emrSaved="handleEmrSaved" />
|
||||
</el-tab-pane>
|
||||
<!-- <el-tab-pane label="病历" name="emr">
|
||||
<Emr
|
||||
@@ -151,12 +144,8 @@
|
||||
" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="医嘱" name="prescription">
|
||||
<prescriptionlist
|
||||
:patientInfo="patientInfo"
|
||||
ref="prescriptionRef"
|
||||
:activeTab="activeTab"
|
||||
:outpatientEmrSaved="outpatientEmrSaved"
|
||||
/>
|
||||
<prescriptionlist :patientInfo="patientInfo" ref="prescriptionRef" :activeTab="activeTab"
|
||||
:outpatientEmrSaved="outpatientEmrSaved" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="中医" name="tcm">
|
||||
<tcmAdvice :patientInfo="patientInfo" ref="tcmRef" />
|
||||
@@ -180,24 +169,17 @@
|
||||
<el-drawer v-model="drawer" title="患者队列" direction="ltr" @open="handleOpen">
|
||||
<PatientList ref="patientDrawerRef" @toCurrent="handleReceive" />
|
||||
</el-drawer>
|
||||
<RefundListDialog
|
||||
:open="openRefundListDialog"
|
||||
:encounterId="currentEncounterId"
|
||||
@close="openRefundListDialog = false"
|
||||
@refresh="() => prescriptionRef.getListInfo()"
|
||||
/>
|
||||
<HospitalizationDialog
|
||||
:open="openDialog"
|
||||
:patientInfo="patientInfo"
|
||||
:encounterId="currentEncounterId"
|
||||
:mainDiagnosis="mainDiagnosis"
|
||||
@close="openDialog = false"
|
||||
/>
|
||||
<PrescriptionInfo
|
||||
:open="openPrescriptionDialog"
|
||||
:precriptionInfo="prescriptionInfo"
|
||||
@close="openPrescriptionDialog = false"
|
||||
/>
|
||||
<RefundListDialog :open="openRefundListDialog" :encounterId="currentEncounterId"
|
||||
@close="openRefundListDialog = false" @refresh="() => prescriptionRef.getListInfo()" />
|
||||
<HospitalizationDialog :open="openDialog" :patientInfo="patientInfo" :encounterId="currentEncounterId"
|
||||
:mainDiagnosis="mainDiagnosis" @close="openDialog = false" />
|
||||
<PrescriptionInfo :open="openPrescriptionDialog" :precriptionInfo="prescriptionInfo"
|
||||
@close="openPrescriptionDialog = false" />
|
||||
<!-- 新增叫号弹窗组件 -->
|
||||
<DoctorCallDialog v-model:dialogVisible="dialogVisible" :current-patient="currentCallPatient"
|
||||
:current-patient-list="currentWaitPatientList" :room-no="roomNo"
|
||||
:department="userStore.orgName || patientInfo.organizationName || '心内科'" @callNext="callNext" @reCall="reCall"
|
||||
@finish="finishCall" @skip="skip" @requeue="requeue" @markSeen="markSeen" @callThis="callThis" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
@@ -221,6 +203,7 @@ import HospitalizationDialog from './components/hospitalizationDialog.vue';
|
||||
import tcmAdvice from './components/tcm/tcmAdvice.vue';
|
||||
import inspectionApplication from './components/inspection/inspectionApplication.vue';
|
||||
import surgeryApplication from './components/surgery/surgeryApplication.vue';
|
||||
import DoctorCallDialog from './components/callQueue/DoctorCallDialog.vue';
|
||||
import { formatDate, formatDateStr } from '@/utils/index';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import { nextTick } from 'vue';
|
||||
@@ -253,6 +236,10 @@ const drawer = ref(false);
|
||||
const openRefundListDialog = ref(false);
|
||||
const openDialog = ref(false);
|
||||
const openPrescriptionDialog = ref(false);
|
||||
const dialogVisible = ref(false); // 叫号弹窗显示/隐藏
|
||||
const currentCallPatient = ref({}); // 传给弹窗的【当前就诊患者】
|
||||
const currentWaitPatientList = ref([]); // 传给弹窗的【候诊患者列表】
|
||||
const roomNo = ref('4号'); // 诊室号
|
||||
const saveStatus = ref(false);
|
||||
const outpatientEmrSaved = ref(false); // 门诊病历保存状态
|
||||
const currentEncounterId = ref('');
|
||||
@@ -327,8 +314,9 @@ const shortcuts = [
|
||||
const eprescriptionRef = ref();
|
||||
onMounted(() => {
|
||||
getWaitPatient();
|
||||
});
|
||||
getWaitPatientList();
|
||||
getPatientList();
|
||||
});
|
||||
// 获取现诊患者列表
|
||||
function getPatientList() {
|
||||
queryParams.value.statusEnum = 2;
|
||||
@@ -405,6 +393,33 @@ function getWaitPatient() {
|
||||
waitCount.value = res.data.total;
|
||||
});
|
||||
}
|
||||
// 新增:获取候诊患者完整列表
|
||||
function getWaitPatientList() {
|
||||
queryParams.value.registerTimeSTime = formatDateStr(new Date(), 'YYYY-MM-DD') + ' 00:00:00';
|
||||
queryParams.value.registerTimeETime = formatDateStr(new Date(), 'YYYY-MM-DD') + ' 23:59:59';
|
||||
queryParams.value.statusEnum = 1; // 筛选【待诊】患者
|
||||
getList(queryParams.value).then((res) => {
|
||||
currentWaitPatientList.value = res.data.records;
|
||||
waitCount.value = res.data.total;
|
||||
});
|
||||
}
|
||||
|
||||
// 新增:打开叫号弹窗方法
|
||||
function handleOpenCallDialog() {
|
||||
// 刷新患者列表和候诊列表,确保数据是最新的
|
||||
getPatientList();
|
||||
getWaitPatientList();
|
||||
|
||||
// 检查当前选中的患者是否已经完诊,如果已经完诊,就不设置为当前呼叫患者
|
||||
if (patientInfo.value && patientInfo.value.statusEnum !== 3) { // 3代表已完诊
|
||||
currentCallPatient.value = patientInfo.value;
|
||||
} else {
|
||||
// 如果当前患者已经完诊,就清空当前呼叫患者
|
||||
currentCallPatient.value = {};
|
||||
}
|
||||
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
function handleClick(tab) {
|
||||
switch (tab) {
|
||||
@@ -716,6 +731,58 @@ const onHospitalization = async () => {
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// ========== 叫号弹窗所有回调事件 ==========
|
||||
const callNext = () => {
|
||||
// 直接刷新患者列表和候诊列表,API调用已经在子组件中完成
|
||||
getPatientList();
|
||||
getWaitPatientList();
|
||||
// 子组件已处理提示,这里无需重复提示
|
||||
};
|
||||
const reCall = () => {
|
||||
// 选呼功能已实现,无需提示
|
||||
};
|
||||
const finishCall = () => {
|
||||
dialogVisible.value = false;
|
||||
// 刷新患者列表和候诊列表
|
||||
getPatientList();
|
||||
getWaitPatientList();
|
||||
// 清空当前呼叫患者
|
||||
currentCallPatient.value = {};
|
||||
// 子组件已处理提示,这里无需重复提示
|
||||
};
|
||||
const skip = () => {
|
||||
// 直接刷新患者列表和候诊列表,API调用已经在子组件中完成
|
||||
getPatientList();
|
||||
getWaitPatientList();
|
||||
// 子组件已处理提示,这里无需重复提示
|
||||
};
|
||||
const requeue = () => {
|
||||
// 1. 重新获取候诊患者列表
|
||||
getWaitPatientList();
|
||||
// 2. 刷新现诊患者列表
|
||||
getPatientList();
|
||||
// 3. 刷新候诊人数统计
|
||||
getWaitPatient();
|
||||
// 4. 清空当前呼叫患者
|
||||
currentCallPatient.value = {};
|
||||
// 子组件已处理提示,这里无需重复提示
|
||||
};
|
||||
const markSeen = async () => {
|
||||
// 直接刷新患者列表和候诊列表,API调用已经在子组件中完成
|
||||
getPatientList();
|
||||
getWaitPatientList();
|
||||
// 清空当前呼叫患者
|
||||
currentCallPatient.value = {};
|
||||
};
|
||||
const callThis = (row) => {
|
||||
handleCardClick(row);
|
||||
currentCallPatient.value = row;
|
||||
dialogVisible.value = false;
|
||||
// 刷新患者列表和候诊列表
|
||||
getPatientList();
|
||||
getWaitPatientList();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -724,6 +791,7 @@ const onHospitalization = async () => {
|
||||
:deep(.el-descriptions__label) {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
:deep(.el-descriptions__content) {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
@@ -906,15 +974,18 @@ const onHospitalization = async () => {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(100% - 50px);
|
||||
z-index: 998; /* 降低z-index,避免覆盖按钮 */
|
||||
z-index: 998;
|
||||
/* 降低z-index,避免覆盖按钮 */
|
||||
/* 确保覆盖在内容上方,但不覆盖顶部按钮区域 */
|
||||
cursor: not-allowed;
|
||||
background-color: rgba(255, 255, 255, 0.01);
|
||||
pointer-events: none; /* 默认不捕获鼠标事件,只在真正需要禁用时才启用 */
|
||||
pointer-events: none;
|
||||
/* 默认不捕获鼠标事件,只在真正需要禁用时才启用 */
|
||||
}
|
||||
|
||||
.disabled-wrapper .overlay.overlay-disabled {
|
||||
pointer-events: auto; /* 当需要真正禁用时启用指针事件 */
|
||||
pointer-events: auto;
|
||||
/* 当需要真正禁用时启用指针事件 */
|
||||
}
|
||||
|
||||
/* 顶层按钮样式,确保按钮始终在最上层 */
|
||||
|
||||
@@ -371,8 +371,9 @@ const handleTransferInOk = async () => {
|
||||
// 单击患者卡片事件 - 直接触发入科选床界面
|
||||
function handleCardClick(item: any, index: number) {
|
||||
if (item.encounterStatus == 2) {
|
||||
// 显示提示信息,指导用户如何分配床位
|
||||
ElMessage({
|
||||
message: '请分配病床!',
|
||||
message: '该患者尚未分配病床,请通过拖拽操作将患者分配到右侧床位',
|
||||
type: 'warning',
|
||||
grouping: true,
|
||||
showClose: true,
|
||||
@@ -390,8 +391,9 @@ function handleCardClick(item: any, index: number) {
|
||||
// 双击患者卡片事件 - 保持原有逻辑
|
||||
function handleCardDblClick(item: any) {
|
||||
if (item.encounterStatus == 2) {
|
||||
// 显示提示信息,指导用户如何分配床位
|
||||
ElMessage({
|
||||
message: '请分配病床!',
|
||||
message: '该患者尚未分配病床,请通过拖拽操作将患者分配到右侧床位',
|
||||
type: 'warning',
|
||||
grouping: true,
|
||||
showClose: true,
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
v-for="(item, index) in tableData"
|
||||
:key="index"
|
||||
:class="{ 'editing-row': item.editing, 'child-row': item.row.includes('.') }"
|
||||
@click="handleRowClick(index)"
|
||||
>
|
||||
<td>{{ item.row }}</td>
|
||||
<td>
|
||||
@@ -967,6 +968,14 @@ async function loadMenuData(menu) {
|
||||
}
|
||||
}
|
||||
|
||||
// 处理行点击进入编辑状态
|
||||
function handleRowClick(index) {
|
||||
const item = tableData.value[index];
|
||||
if (!item.editing) {
|
||||
item.editing = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理编辑按钮点击
|
||||
function handleEdit(index) {
|
||||
const item = tableData.value[index];
|
||||
@@ -982,6 +991,25 @@ function handleCancelEdit(index) {
|
||||
// 处理确认按钮点击
|
||||
async function handleConfirm(index) {
|
||||
const item = tableData.value[index];
|
||||
|
||||
// 必填字段验证
|
||||
if (!item.code || item.code.trim() === '') {
|
||||
ElMessage.error('编码不能为空');
|
||||
return;
|
||||
}
|
||||
if (!item.name || item.name.trim() === '') {
|
||||
ElMessage.error('名称不能为空');
|
||||
return;
|
||||
}
|
||||
if (!item.type || item.type.trim() === '') {
|
||||
ElMessage.error('检查类型不能为空');
|
||||
return;
|
||||
}
|
||||
if (!item.department || item.department.trim() === '') {
|
||||
ElMessage.error('执行科室不能为空');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 根据当前激活的菜单调用不同的API
|
||||
if (activeMenu.value === '检查方法') {
|
||||
@@ -1212,9 +1240,24 @@ function handleAddNewRow() {
|
||||
function handleAdd(index) {
|
||||
const parentRow = tableData.value[index];
|
||||
|
||||
// 查找该父行的所有现有子行,确定下一个子行号
|
||||
const parentRowPrefix = parentRow.row + '.';
|
||||
let maxChildNum = 0;
|
||||
|
||||
tableData.value.forEach((item, idx) => {
|
||||
if (item.row.startsWith(parentRowPrefix) && idx > index) {
|
||||
const childNum = parseInt(item.row.split('.').pop());
|
||||
if (childNum > maxChildNum) {
|
||||
maxChildNum = childNum;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const nextChildNum = maxChildNum + 1;
|
||||
|
||||
// 创建子行数据,继承父行的编码
|
||||
const childRow = {
|
||||
row: parentRow.row + '.1', // 子行编号
|
||||
row: parentRow.row + '.' + nextChildNum, // 子行编号
|
||||
code: parentRow.code, // 继承父行编码
|
||||
name: '',
|
||||
type: '',
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
</el-row>
|
||||
<el-row :gutter="10" class="mb8" v-else>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="submitAudit"
|
||||
>提交审核</el-button
|
||||
>
|
||||
<el-button type="primary" plain icon="Plus" @click="submitAudit">
|
||||
提交审核
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="EditPen" @click="handleTotalAmount">
|
||||
@@ -35,7 +35,8 @@
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleSave"
|
||||
>批量保存</el-button
|
||||
>批量保存
|
||||
</el-button
|
||||
>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -271,11 +272,11 @@
|
||||
>
|
||||
<div class="select_wrapper_div">
|
||||
<el-select
|
||||
v-if="viewStatus !== 'view'"
|
||||
v-model="scope.row.purposeLocationId"
|
||||
placeholder="请选择仓库"
|
||||
:class="{ 'error-border': scope.row.error }"
|
||||
clearable
|
||||
:disabled="viewStatus == 'view'"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item, index) in locationList"
|
||||
@@ -285,8 +286,19 @@
|
||||
@click="handleLocationClick(item, scope.row, scope.$index)"
|
||||
/>
|
||||
</el-select>
|
||||
|
||||
<!-- 非编辑状态下显示为只读输入框 -->
|
||||
<el-input
|
||||
v-else
|
||||
:model-value="getLocationNameById(scope.row.purposeLocationId)"
|
||||
readonly
|
||||
disabled
|
||||
:placeholder="'-'"
|
||||
class="readonly-input" style="pointer-events: none;"
|
||||
/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -724,7 +736,27 @@ const {
|
||||
"specialty_code",
|
||||
"purchase_type"
|
||||
);
|
||||
//
|
||||
// // 监听locationList变化,重新处理已存在的数据
|
||||
// watch(locationList, (newList) => {
|
||||
// if (newList && newList.length > 0) {
|
||||
// // 可以触发表格重新渲染或更新相关数据
|
||||
// }
|
||||
// }, { immediate: true });
|
||||
const getLocationNameById = (id) => {
|
||||
|
||||
// 检查locationList是否已加载
|
||||
if (!locationList.value || !Array.isArray(locationList.value) || locationList.value.length === 0) {
|
||||
return '仓库列表未加载';
|
||||
}
|
||||
|
||||
// 使用严格相等比较,处理类型转换
|
||||
const location = locationList.value.find(item => {
|
||||
return item.id == id; // 使用 == 而不是 === 以处理字符串和数字之间的比较
|
||||
});
|
||||
|
||||
return location ? location.name : '仓库不存在';
|
||||
};
|
||||
const viewStatus = ref("");
|
||||
const startTimeOld = ref("");
|
||||
const endTimeOld = ref("");
|
||||
|
||||
@@ -205,9 +205,9 @@
|
||||
>
|
||||
<template #default="scope">
|
||||
<!-- v-hasPermi="['system:user:edit']" -->
|
||||
<el-button link type="primary" icon="View" @click.stop="handleUpdate(scope.row, 'view')"
|
||||
>详情</el-button
|
||||
>
|
||||
<el-button link type="primary" icon="View" @click.stop="handleUpdate(scope.row, 'view')">
|
||||
详情
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@@ -588,6 +588,7 @@ function handleUpdate(row, view) {
|
||||
// 采购入库
|
||||
editRow.value = row;
|
||||
getpurchaseInventoryDetail(row.supplyBusNo).then((response) => {
|
||||
console.log('返回的数据,response:', response.data)
|
||||
currentData.value = response.data;
|
||||
// nextTick(() => {
|
||||
// proxy.$refs['inventoryReceiptRef'].edit();
|
||||
@@ -604,7 +605,6 @@ function handleUpdate(row, view) {
|
||||
path: 'purchaseDocument',
|
||||
});
|
||||
}
|
||||
|
||||
getList();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,55 +1,23 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form
|
||||
:model="queryParams"
|
||||
ref="queryRef"
|
||||
:inline="true"
|
||||
v-show="showSearch"
|
||||
label-width="68px"
|
||||
class="query-form"
|
||||
>
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"
|
||||
class="query-form">
|
||||
<el-form-item label="字典名称" prop="dictName">
|
||||
<el-input
|
||||
v-model="queryParams.dictName"
|
||||
placeholder="请输入字典名称"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
<el-input v-model="queryParams.dictName" placeholder="请输入字典名称" clearable style="width: 240px"
|
||||
@keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="字典类型" prop="dictType">
|
||||
<el-input
|
||||
v-model="queryParams.dictType"
|
||||
placeholder="请输入字典类型"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
<el-input v-model="queryParams.dictType" placeholder="请输入字典类型" clearable style="width: 240px"
|
||||
@keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="字典状态"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in sys_normal_disable"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
<el-select v-model="queryParams.status" placeholder="字典状态" clearable style="width: 240px">
|
||||
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" style="width: 308px">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
<el-date-picker v-model="dateRange" value-format="YYYY-MM-DD" type="daterange" range-separator="-"
|
||||
start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item class="search-buttons">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
@@ -59,76 +27,34 @@
|
||||
|
||||
<el-row :gutter="10" class="button-group">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['system:dict:add']"
|
||||
>新增</el-button
|
||||
>
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:dict:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleUpdate"
|
||||
v-hasPermi="['system:dict:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate"
|
||||
v-hasPermi="['system:dict:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
v-hasPermi="['system:dict:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"
|
||||
v-hasPermi="['system:dict:remove']">删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['system:dict:export']"
|
||||
>导出</el-button
|
||||
>
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport"
|
||||
v-hasPermi="['system:dict:export']">导出</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Refresh"
|
||||
@click="handleRefreshCache"
|
||||
v-hasPermi="['system:dict:remove']"
|
||||
>刷新缓存</el-button
|
||||
>
|
||||
<el-button type="danger" plain icon="Refresh" @click="handleRefreshCache"
|
||||
v-hasPermi="['system:dict:remove']">刷新缓存</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<div class="table-wrapper">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="typeList"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="字典编号" align="center" prop="dictId" />
|
||||
<el-table-column
|
||||
label="字典名称"
|
||||
align="center"
|
||||
prop="dictName"
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column label="字典名称" align="center" prop="dictName" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="字典类型" align="center" :show-overflow-tooltip="true">
|
||||
<template #default="scope">
|
||||
<router-link :to="'/system/dict-data/index/' + scope.row.dictId" class="link-type">
|
||||
<router-link :to="'/system/dict/data/' + scope.row.dictId" class="link-type">
|
||||
<span>{{ scope.row.dictType }}</span>
|
||||
</router-link>
|
||||
</template>
|
||||
@@ -144,40 +70,18 @@
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
align="center"
|
||||
width="160"
|
||||
class-name="small-padding fixed-width"
|
||||
>
|
||||
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:dict:edit']"
|
||||
class="action-button">修改</el-button
|
||||
>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['system:dict:remove']"
|
||||
class="action-button">删除</el-button
|
||||
>
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:dict:edit']" class="action-button">修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['system:dict:remove']" class="action-button">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination-container">
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -291,7 +195,7 @@ function handleAdd() {
|
||||
/** 多选框选中数据 */
|
||||
function handleSelectionChange(selection) {
|
||||
ids.value = selection.map((item) => item.dictId);
|
||||
single.value = selection.length != 1;
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
}
|
||||
/** 修改按钮操作 */
|
||||
@@ -308,14 +212,14 @@ function handleUpdate(row) {
|
||||
function submitForm() {
|
||||
proxy.$refs['dictRef'].validate((valid) => {
|
||||
if (valid) {
|
||||
if (form.value.dictId != undefined) {
|
||||
updateType(form.value).then((response) => {
|
||||
if (form.value.dictId !== undefined) {
|
||||
updateType(form.value).then(() => {
|
||||
proxy.$modal.msgSuccess('修改成功');
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
} else {
|
||||
addType(form.value).then((response) => {
|
||||
addType(form.value).then(() => {
|
||||
proxy.$modal.msgSuccess('新增成功');
|
||||
open.value = false;
|
||||
getList();
|
||||
|
||||
112
openhis-ui-vue3/src/views/system/user copy/authRole.vue
Normal file
112
openhis-ui-vue3/src/views/system/user copy/authRole.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<h4 class="form-header h4">基本信息</h4>
|
||||
<el-form :model="form" label-width="80px">
|
||||
<el-row>
|
||||
<el-col :span="8" :offset="2">
|
||||
<el-form-item label="用户昵称" prop="nickName">
|
||||
<el-input v-model="form.nickName" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" :offset="2">
|
||||
<el-form-item label="登录账号" prop="userName">
|
||||
<el-input v-model="form.userName" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<h4 class="form-header h4">角色信息</h4>
|
||||
<el-table v-loading="loading" :row-key="getRowKey" @row-click="clickRow" ref="roleRef" @selection-change="handleSelectionChange" :data="roles.slice((pageNum - 1) * pageSize, pageNum * pageSize)">
|
||||
<el-table-column label="序号" width="55" type="index" align="center">
|
||||
<template #default="scope">
|
||||
<span>{{ (pageNum - 1) * pageSize + scope.$index + 1 }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column type="selection" :reserve-selection="true" width="55"></el-table-column>
|
||||
<el-table-column label="角色编号" align="center" prop="roleId" />
|
||||
<el-table-column label="角色名称" align="center" prop="roleName" />
|
||||
<el-table-column label="权限字符" align="center" prop="roleKey" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" />
|
||||
|
||||
<el-form label-width="100px">
|
||||
<div style="text-align: center;margin-left:-120px;margin-top:30px;">
|
||||
<el-button type="primary" @click="submitForm()">提交</el-button>
|
||||
<el-button @click="close()">返回</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="AuthRole">
|
||||
import {getAuthRole, updateAuthRole} from "@/api/system/user copy";
|
||||
|
||||
const route = useRoute();
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const loading = ref(true);
|
||||
const total = ref(0);
|
||||
const pageNum = ref(1);
|
||||
const pageSize = ref(10);
|
||||
const roleIds = ref([]);
|
||||
const roles = ref([]);
|
||||
const form = ref({
|
||||
nickName: undefined,
|
||||
userName: undefined,
|
||||
userId: undefined
|
||||
});
|
||||
|
||||
/** 单击选中行数据 */
|
||||
function clickRow(row) {
|
||||
proxy.$refs["roleRef"].toggleRowSelection(row);
|
||||
};
|
||||
/** 多选框选中数据 */
|
||||
function handleSelectionChange(selection) {
|
||||
roleIds.value = selection.map(item => item.roleId);
|
||||
};
|
||||
/** 保存选中的数据编号 */
|
||||
function getRowKey(row) {
|
||||
return row.roleId;
|
||||
};
|
||||
/** 关闭按钮 */
|
||||
function close() {
|
||||
const obj = { path: "/system/user" };
|
||||
proxy.$tab.closeOpenPage(obj);
|
||||
};
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
const userId = form.value.userId;
|
||||
const rIds = roleIds.value.join(",");
|
||||
updateAuthRole({ userId: userId, roleIds: rIds }).then(response => {
|
||||
proxy.$modal.msgSuccess("授权成功");
|
||||
close();
|
||||
});
|
||||
};
|
||||
|
||||
(() => {
|
||||
const userId = route.params && route.params.userId;
|
||||
if (userId) {
|
||||
loading.value = true;
|
||||
getAuthRole(userId).then(response => {
|
||||
form.value = response.user;
|
||||
roles.value = response.roles;
|
||||
total.value = roles.value.length;
|
||||
nextTick(() => {
|
||||
roles.value.forEach(row => {
|
||||
if (row.flag) {
|
||||
proxy.$refs["roleRef"].toggleRowSelection(row);
|
||||
}
|
||||
});
|
||||
});
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
621
openhis-ui-vue3/src/views/system/user copy/index.vue
Normal file
621
openhis-ui-vue3/src/views/system/user copy/index.vue
Normal file
@@ -0,0 +1,621 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<!--部门数据-->
|
||||
<el-col :span="4" :xs="24">
|
||||
<div class="head-container">
|
||||
<el-input
|
||||
v-model="deptName"
|
||||
placeholder="请输入部门名称"
|
||||
clearable
|
||||
prefix-icon="Search"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
</div>
|
||||
<div class="head-container">
|
||||
<el-tree
|
||||
:data="deptOptions"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterNode"
|
||||
ref="deptTreeRef"
|
||||
node-key="id"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
</div>
|
||||
</el-col>
|
||||
<!--用户数据-->
|
||||
<el-col :span="20" :xs="24">
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="用户名称" prop="userName">
|
||||
<el-input
|
||||
v-model="queryParams.userName"
|
||||
placeholder="请输入用户名称"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input
|
||||
v-model="queryParams.phonenumber"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="用户状态"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in sys_normal_disable"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" style="width: 308px;">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['system:user:add']"
|
||||
>新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleUpdate"
|
||||
v-hasPermi="['system:user:edit']"
|
||||
>修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
v-hasPermi="['system:user:remove']"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="info"
|
||||
plain
|
||||
icon="Upload"
|
||||
@click="handleImport"
|
||||
v-hasPermi="['system:user:import']"
|
||||
>导入</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['system:user:export']"
|
||||
>导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="用户编号" align="center" key="userId" prop="userId" v-if="columns[0].visible" />
|
||||
<el-table-column label="用户名称" align="center" key="userName" prop="userName" v-if="columns[1].visible" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="用户昵称" align="center" key="nickName" prop="nickName" v-if="columns[2].visible" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="部门" align="center" key="deptName" prop="dept.deptName" v-if="columns[3].visible" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="手机号码" align="center" key="phonenumber" prop="phonenumber" v-if="columns[4].visible" width="120" />
|
||||
<el-table-column label="状态" align="center" key="status" v-if="columns[5].visible">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
@change="handleStatusChange(scope.row)"
|
||||
></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" v-if="columns[6].visible" width="160">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="150" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top" v-if="scope.row.userId !== 1">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:user:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top" v-if="scope.row.userId !== 1">
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:user:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="重置密码" placement="top" v-if="scope.row.userId !== 1">
|
||||
<el-button link type="primary" icon="Key" @click="handleResetPwd(scope.row)" v-hasPermi="['system:user:resetPwd']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="分配角色" placement="top" v-if="scope.row.userId !== 1">
|
||||
<el-button link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)" v-hasPermi="['system:user:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 添加或修改用户配置对话框 -->
|
||||
<el-dialog :title="title" v-model="open" width="600px" append-to-body>
|
||||
<el-form :model="form" :rules="rules" ref="userRef" label-width="80px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="用户昵称" prop="nickName">
|
||||
<el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="归属部门" prop="deptId">
|
||||
<el-tree-select
|
||||
v-model="form.deptId"
|
||||
:data="deptOptions"
|
||||
:props="{ value: 'id', label: 'label', children: 'children' }"
|
||||
value-key="id"
|
||||
placeholder="请选择归属部门"
|
||||
check-strictly
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
|
||||
<el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
|
||||
<el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="用户性别">
|
||||
<el-select v-model="form.sex" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="dict in sys_user_sex"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio
|
||||
v-for="dict in sys_normal_disable"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
>{{ dict.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="岗位">
|
||||
<el-select v-model="form.postIds" multiple placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in postOptions"
|
||||
:key="item.postId"
|
||||
:label="item.postName"
|
||||
:value="item.postId"
|
||||
:disabled="item.status == 1"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="角色">
|
||||
<el-select v-model="form.roleIds" multiple placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in roleOptions"
|
||||
:key="item.roleId"
|
||||
:label="item.roleName"
|
||||
:value="item.roleId"
|
||||
:disabled="item.status == 1"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 用户导入对话框 -->
|
||||
<el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body>
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
:limit="1"
|
||||
accept=".xlsx, .xls"
|
||||
:headers="upload.headers"
|
||||
:action="upload.url + '?updateSupport=' + upload.updateSupport"
|
||||
:disabled="upload.isUploading"
|
||||
:on-progress="handleFileUploadProgress"
|
||||
:on-success="handleFileSuccess"
|
||||
:auto-upload="false"
|
||||
drag
|
||||
>
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip text-center">
|
||||
<div class="el-upload__tip">
|
||||
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据
|
||||
</div>
|
||||
<span>仅允许导入xls、xlsx格式文件。</span>
|
||||
<el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitFileForm">确 定</el-button>
|
||||
<el-button @click="upload.open = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="User">
|
||||
import {getToken} from "@/utils/auth";
|
||||
import {
|
||||
addUser,
|
||||
changeUserStatus,
|
||||
delUser,
|
||||
deptTreeSelect,
|
||||
getUser,
|
||||
listUser,
|
||||
resetUserPwd,
|
||||
updateUser
|
||||
} from "@/api/system/user copy";
|
||||
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { sys_normal_disable, sys_user_sex } = proxy.useDict("sys_normal_disable", "sys_user_sex");
|
||||
|
||||
const userList = ref([]);
|
||||
const open = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const title = ref("");
|
||||
const dateRange = ref([]);
|
||||
const deptName = ref("");
|
||||
const deptOptions = ref(undefined);
|
||||
const initPassword = ref(undefined);
|
||||
const postOptions = ref([]);
|
||||
const roleOptions = ref([]);
|
||||
/*** 用户导入参数 */
|
||||
const upload = reactive({
|
||||
// 是否显示弹出层(用户导入)
|
||||
open: false,
|
||||
// 弹出层标题(用户导入)
|
||||
title: "",
|
||||
// 是否禁用上传
|
||||
isUploading: false,
|
||||
// 是否更新已经存在的用户数据
|
||||
updateSupport: 0,
|
||||
// 设置上传的请求头部
|
||||
headers: { Authorization: "Bearer " + getToken() },
|
||||
// 上传的地址
|
||||
url: import.meta.env.VITE_APP_BASE_API + "/system/user/importData"
|
||||
});
|
||||
// 列显隐信息
|
||||
const columns = ref([
|
||||
{ key: 0, label: `用户编号`, visible: true },
|
||||
{ key: 1, label: `用户名称`, visible: true },
|
||||
{ key: 2, label: `用户昵称`, visible: true },
|
||||
{ key: 3, label: `部门`, visible: true },
|
||||
{ key: 4, label: `手机号码`, visible: true },
|
||||
{ key: 5, label: `状态`, visible: true },
|
||||
{ key: 6, label: `创建时间`, visible: true }
|
||||
]);
|
||||
|
||||
const data = reactive({
|
||||
form: {},
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
userName: undefined,
|
||||
phonenumber: undefined,
|
||||
status: undefined,
|
||||
deptId: undefined
|
||||
},
|
||||
rules: {
|
||||
userName: [{ required: true, message: "用户名称不能为空", trigger: "blur" }, { min: 2, max: 20, message: "用户名称长度必须介于 2 和 20 之间", trigger: "blur" }],
|
||||
nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
|
||||
password: [{ required: true, message: "用户密码不能为空", trigger: "blur" }, { min: 5, max: 20, message: "用户密码长度必须介于 5 和 20 之间", trigger: "blur" }, { pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }],
|
||||
email: [{ type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],
|
||||
phonenumber: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }]
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 通过条件过滤节点 */
|
||||
const filterNode = (value, data) => {
|
||||
if (!value) return true;
|
||||
return data.label.indexOf(value) !== -1;
|
||||
};
|
||||
/** 根据名称筛选部门树 */
|
||||
watch(deptName, val => {
|
||||
proxy.$refs["deptTreeRef"].filter(val);
|
||||
});
|
||||
/** 查询部门下拉树结构 */
|
||||
function getDeptTree() {
|
||||
deptTreeSelect().then(response => {
|
||||
deptOptions.value = response.data;
|
||||
});
|
||||
};
|
||||
/** 查询用户列表 */
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
listUser(proxy.addDateRange(queryParams.value, dateRange.value)).then(res => {
|
||||
loading.value = false;
|
||||
userList.value = res.rows;
|
||||
total.value = res.total;
|
||||
});
|
||||
};
|
||||
/** 节点单击事件 */
|
||||
function handleNodeClick(data) {
|
||||
queryParams.value.deptId = data.id;
|
||||
handleQuery();
|
||||
};
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
dateRange.value = [];
|
||||
proxy.resetForm("queryRef");
|
||||
queryParams.value.deptId = undefined;
|
||||
proxy.$refs.deptTreeRef.setCurrentKey(null);
|
||||
handleQuery();
|
||||
};
|
||||
/** 删除按钮操作 */
|
||||
function handleDelete(row) {
|
||||
const userIds = row.userId || ids.value;
|
||||
proxy.$modal.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?').then(function () {
|
||||
return delUser(userIds);
|
||||
}).then(() => {
|
||||
getList();
|
||||
proxy.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
};
|
||||
/** 导出按钮操作 */
|
||||
function handleExport() {
|
||||
proxy.download("system/user/export", {
|
||||
...queryParams.value,
|
||||
},`user_${new Date().getTime()}.xlsx`);
|
||||
};
|
||||
/** 用户状态修改 */
|
||||
function handleStatusChange(row) {
|
||||
let text = row.status === "0" ? "启用" : "停用";
|
||||
proxy.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?').then(function () {
|
||||
return changeUserStatus(row.userId, row.status);
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess(text + "成功");
|
||||
}).catch(function () {
|
||||
row.status = row.status === "0" ? "1" : "0";
|
||||
});
|
||||
};
|
||||
/** 更多操作 */
|
||||
function handleCommand(command, row) {
|
||||
switch (command) {
|
||||
case "handleResetPwd":
|
||||
handleResetPwd(row);
|
||||
break;
|
||||
case "handleAuthRole":
|
||||
handleAuthRole(row);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
/** 跳转角色分配 */
|
||||
function handleAuthRole(row) {
|
||||
const userId = row.userId;
|
||||
router.push("/system/user-auth/role/" + userId);
|
||||
};
|
||||
/** 重置密码按钮操作 */
|
||||
function handleResetPwd(row) {
|
||||
proxy.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
closeOnClickModal: false,
|
||||
inputPattern: /^.{5,20}$/,
|
||||
inputErrorMessage: "用户密码长度必须介于 5 和 20 之间",
|
||||
inputValidator: (value) => {
|
||||
if (/<|>|"|'|\||\\/.test(value)) {
|
||||
return "不能包含非法字符:< > \" ' \\\ |"
|
||||
}
|
||||
},
|
||||
}).then(({ value }) => {
|
||||
resetUserPwd(row.userId, value).then(response => {
|
||||
proxy.$modal.msgSuccess("修改成功,新密码是:" + value);
|
||||
});
|
||||
}).catch(() => {});
|
||||
};
|
||||
/** 选择条数 */
|
||||
function handleSelectionChange(selection) {
|
||||
ids.value = selection.map(item => item.userId);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
/** 导入按钮操作 */
|
||||
function handleImport() {
|
||||
upload.title = "用户导入";
|
||||
upload.open = true;
|
||||
};
|
||||
/** 下载模板操作 */
|
||||
function importTemplate() {
|
||||
proxy.download("system/user/importTemplate", {
|
||||
}, `user_template_${new Date().getTime()}.xlsx`);
|
||||
};
|
||||
/**文件上传中处理 */
|
||||
const handleFileUploadProgress = (event, file, fileList) => {
|
||||
upload.isUploading = true;
|
||||
};
|
||||
/** 文件上传成功处理 */
|
||||
const handleFileSuccess = (response, file, fileList) => {
|
||||
upload.open = false;
|
||||
upload.isUploading = false;
|
||||
proxy.$refs["uploadRef"].handleRemove(file);
|
||||
proxy.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
|
||||
getList();
|
||||
};
|
||||
/** 提交上传文件 */
|
||||
function submitFileForm() {
|
||||
proxy.$refs["uploadRef"].submit();
|
||||
};
|
||||
/** 重置操作表单 */
|
||||
function reset() {
|
||||
form.value = {
|
||||
userId: undefined,
|
||||
deptId: undefined,
|
||||
userName: undefined,
|
||||
nickName: undefined,
|
||||
password: undefined,
|
||||
phonenumber: undefined,
|
||||
email: undefined,
|
||||
sex: undefined,
|
||||
status: "0",
|
||||
remark: undefined,
|
||||
postIds: [],
|
||||
roleIds: []
|
||||
};
|
||||
proxy.resetForm("userRef");
|
||||
};
|
||||
/** 取消按钮 */
|
||||
function cancel() {
|
||||
open.value = false;
|
||||
reset();
|
||||
};
|
||||
/** 新增按钮操作 */
|
||||
function handleAdd() {
|
||||
reset();
|
||||
getUser().then(response => {
|
||||
postOptions.value = response.posts;
|
||||
roleOptions.value = response.roles;
|
||||
open.value = true;
|
||||
title.value = "添加用户";
|
||||
form.value.password = initPassword.value;
|
||||
});
|
||||
};
|
||||
/** 修改按钮操作 */
|
||||
function handleUpdate(row) {
|
||||
reset();
|
||||
const userId = row.userId || ids.value;
|
||||
getUser(userId).then(response => {
|
||||
form.value = response.data;
|
||||
postOptions.value = response.posts;
|
||||
roleOptions.value = response.roles;
|
||||
form.value.postIds = response.postIds;
|
||||
form.value.roleIds = response.roleIds;
|
||||
open.value = true;
|
||||
title.value = "修改用户";
|
||||
form.password = "";
|
||||
});
|
||||
};
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs["userRef"].validate(valid => {
|
||||
if (valid) {
|
||||
if (form.value.userId != undefined) {
|
||||
updateUser(form.value).then(response => {
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
} else {
|
||||
addUser(form.value).then(response => {
|
||||
proxy.$modal.msgSuccess("新增成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
getDeptTree();
|
||||
getList();
|
||||
</script>
|
||||
87
openhis-ui-vue3/src/views/system/user copy/profile/index.vue
Normal file
87
openhis-ui-vue3/src/views/system/user copy/profile/index.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6" :xs="24">
|
||||
<el-card class="box-card">
|
||||
<template v-slot:header>
|
||||
<div class="clearfix">
|
||||
<span>个人信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<div class="text-center">
|
||||
<userAvatar />
|
||||
</div>
|
||||
<ul class="list-group list-group-striped">
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="user" />用户名称
|
||||
<div class="pull-right">{{ state.user.userName }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="phone" />手机号码
|
||||
<div class="pull-right">{{ state.user.phonenumber }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="email" />用户邮箱
|
||||
<div class="pull-right">{{ state.user.email }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="tree" />所属部门
|
||||
<div class="pull-right" v-if="state.user.dept">{{ state.user.dept.deptName }} / {{ state.postGroup }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="peoples" />所属角色
|
||||
<div class="pull-right">{{ state.roleGroup }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="date" />创建日期
|
||||
<div class="pull-right">{{ state.user.createTime }}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="18" :xs="24">
|
||||
<el-card>
|
||||
<template v-slot:header>
|
||||
<div class="clearfix">
|
||||
<span>基本资料</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="基本资料" name="userinfo">
|
||||
<userInfo :user="state.user" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="修改密码" name="resetPwd">
|
||||
<resetPwd />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Profile">
|
||||
import userAvatar from "./userAvatar";
|
||||
import userInfo from "./userInfo";
|
||||
import resetPwd from "./resetPwd";
|
||||
import {getUserProfile} from "@/api/system/user copy";
|
||||
|
||||
const activeTab = ref("userinfo");
|
||||
const state = reactive({
|
||||
user: {},
|
||||
roleGroup: {},
|
||||
postGroup: {}
|
||||
});
|
||||
|
||||
function getUser() {
|
||||
getUserProfile().then(response => {
|
||||
state.user = response.data;
|
||||
state.roleGroup = response.roleGroup;
|
||||
state.postGroup = response.postGroup;
|
||||
});
|
||||
};
|
||||
|
||||
getUser();
|
||||
</script>
|
||||
@@ -0,0 +1,65 @@
|
||||
<!--
|
||||
* @Author: 徐靖博 xujb@ccjb.com.cn
|
||||
* @Date: 2025-03-26 10:49:30
|
||||
* @LastEditTime: 2025-03-26 13:57:02
|
||||
* @LastEditors: 徐靖博 xujb@ccjb.com.cn
|
||||
* @Description:
|
||||
* @FilePath: \openhis-ui-vue3\src\views\system\user copy\profile\resetPwd.vue
|
||||
-->
|
||||
<template>
|
||||
<el-form ref="pwdRef" :model="user" :rules="rules" label-width="80px">
|
||||
<el-form-item label="旧密码" prop="oldPassword">
|
||||
<el-input v-model="user.oldPassword" placeholder="请输入旧密码" type="password" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码" prop="newPassword">
|
||||
<el-input v-model="user.newPassword" placeholder="请输入新密码" type="password" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" prop="confirmPassword">
|
||||
<el-input v-model="user.confirmPassword" placeholder="请确认新密码" type="password" show-password/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submit">保存</el-button>
|
||||
<el-button type="danger" @click="close">关闭</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {updateUserPwd} from "@/api/system/user copy";
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const user = reactive({
|
||||
oldPassword: undefined,
|
||||
newPassword: undefined,
|
||||
confirmPassword: undefined
|
||||
});
|
||||
|
||||
const equalToPassword = (rule, value, callback) => {
|
||||
if (user.newPassword !== value) {
|
||||
callback(new Error("两次输入的密码不一致"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const rules = ref({
|
||||
oldPassword: [{ required: true, message: "旧密码不能为空", trigger: "blur" }],
|
||||
newPassword: [{ required: true, message: "新密码不能为空", trigger: "blur" }, { min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" }, { pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }],
|
||||
confirmPassword: [{ required: true, message: "确认密码不能为空", trigger: "blur" }, { required: true, validator: equalToPassword, trigger: "blur" }]
|
||||
});
|
||||
|
||||
/** 提交按钮 */
|
||||
function submit() {
|
||||
proxy.$refs.pwdRef.validate(valid => {
|
||||
if (valid) {
|
||||
updateUserPwd(user.oldPassword, user.newPassword).then(response => {
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 关闭按钮 */
|
||||
function close() {
|
||||
proxy.$tab.closePage();
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<div class="user-info-head" @click="editCropper()">
|
||||
<img :src="options.img" title="点击上传头像" class="img-circle img-lg" />
|
||||
<el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
|
||||
<el-row>
|
||||
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
|
||||
<vue-cropper
|
||||
ref="cropper"
|
||||
:img="options.img"
|
||||
:info="true"
|
||||
:autoCrop="options.autoCrop"
|
||||
:autoCropWidth="options.autoCropWidth"
|
||||
:autoCropHeight="options.autoCropHeight"
|
||||
:fixedBox="options.fixedBox"
|
||||
:outputType="options.outputType"
|
||||
@realTime="realTime"
|
||||
v-if="visible"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
|
||||
<div class="avatar-upload-preview">
|
||||
<img :src="options.previews.url" :style="options.previews.img" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<br />
|
||||
<el-row>
|
||||
<el-col :lg="2" :md="2">
|
||||
<el-upload
|
||||
action="#"
|
||||
:http-request="requestUpload"
|
||||
:show-file-list="false"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<el-button>
|
||||
选择
|
||||
<el-icon class="el-icon--right"><Upload /></el-icon>
|
||||
</el-button>
|
||||
</el-upload>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 2 }" :md="2">
|
||||
<el-button icon="Plus" @click="changeScale(1)"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
|
||||
<el-button icon="Minus" @click="changeScale(-1)"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
|
||||
<el-button icon="RefreshLeft" @click="rotateLeft()"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
|
||||
<el-button icon="RefreshRight" @click="rotateRight()"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 2, offset: 6 }" :md="2">
|
||||
<el-button type="primary" @click="uploadImg()">提 交</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import "vue-cropper/dist/index.css";
|
||||
import {VueCropper} from "vue-cropper";
|
||||
import {uploadAvatar} from "@/api/system/user copy";
|
||||
import useUserStore from "@/store/modules/user";
|
||||
|
||||
const userStore = useUserStore();
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const open = ref(false);
|
||||
const visible = ref(false);
|
||||
const title = ref("修改头像");
|
||||
|
||||
//图片裁剪数据
|
||||
const options = reactive({
|
||||
img: userStore.avatar, // 裁剪图片的地址
|
||||
autoCrop: true, // 是否默认生成截图框
|
||||
autoCropWidth: 200, // 默认生成截图框宽度
|
||||
autoCropHeight: 200, // 默认生成截图框高度
|
||||
fixedBox: true, // 固定截图框大小 不允许改变
|
||||
outputType: "png", // 默认生成截图为PNG格式
|
||||
filename: 'avatar', // 文件名称
|
||||
previews: {} //预览数据
|
||||
});
|
||||
|
||||
/** 编辑头像 */
|
||||
function editCropper() {
|
||||
open.value = true;
|
||||
}
|
||||
/** 打开弹出层结束时的回调 */
|
||||
function modalOpened() {
|
||||
visible.value = true;
|
||||
}
|
||||
/** 覆盖默认上传行为 */
|
||||
function requestUpload() {}
|
||||
/** 向左旋转 */
|
||||
function rotateLeft() {
|
||||
proxy.$refs.cropper.rotateLeft();
|
||||
}
|
||||
/** 向右旋转 */
|
||||
function rotateRight() {
|
||||
proxy.$refs.cropper.rotateRight();
|
||||
}
|
||||
/** 图片缩放 */
|
||||
function changeScale(num) {
|
||||
num = num || 1;
|
||||
proxy.$refs.cropper.changeScale(num);
|
||||
}
|
||||
/** 上传预处理 */
|
||||
function beforeUpload(file) {
|
||||
if (file.type.indexOf("image/") == -1) {
|
||||
proxy.$modal.msgError("文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。");
|
||||
} else {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
options.img = reader.result;
|
||||
options.filename = file.name;
|
||||
};
|
||||
}
|
||||
}
|
||||
/** 上传图片 */
|
||||
function uploadImg() {
|
||||
proxy.$refs.cropper.getCropBlob(data => {
|
||||
let formData = new FormData();
|
||||
formData.append("avatarfile", data, options.filename);
|
||||
uploadAvatar(formData).then(response => {
|
||||
open.value = false;
|
||||
options.img = import.meta.env.VITE_APP_BASE_API + response.imgUrl;
|
||||
userStore.avatar = options.img;
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
visible.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
/** 实时预览 */
|
||||
function realTime(data) {
|
||||
options.previews = data;
|
||||
}
|
||||
/** 关闭窗口 */
|
||||
function closeDialog() {
|
||||
options.img = userStore.avatar;
|
||||
options.visible = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.user-info-head {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.user-info-head:hover:after {
|
||||
content: "+";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
color: #eee;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
font-size: 24px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
cursor: pointer;
|
||||
line-height: 110px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<el-form ref="userRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="用户昵称" prop="nickName">
|
||||
<el-input v-model="form.nickName" maxlength="30" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input v-model="form.phonenumber" maxlength="11" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="form.email" maxlength="50" />
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-radio-group v-model="form.sex">
|
||||
<el-radio label="0">男</el-radio>
|
||||
<el-radio label="1">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submit">保存</el-button>
|
||||
<el-button type="danger" @click="close">关闭</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {updateUserProfile} from "@/api/system/user copy";
|
||||
|
||||
const props = defineProps({
|
||||
user: {
|
||||
type: Object
|
||||
}
|
||||
});
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const form = ref({});
|
||||
const rules = ref({
|
||||
nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
|
||||
email: [{ required: true, message: "邮箱地址不能为空", trigger: "blur" }, { type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],
|
||||
phonenumber: [{ required: true, message: "手机号码不能为空", trigger: "blur" }, { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }],
|
||||
});
|
||||
|
||||
/** 提交按钮 */
|
||||
function submit() {
|
||||
proxy.$refs.userRef.validate(valid => {
|
||||
if (valid) {
|
||||
updateUserProfile(form.value).then(response => {
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
props.user.phonenumber = form.value.phonenumber;
|
||||
props.user.email = form.value.email;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 关闭按钮 */
|
||||
function close() {
|
||||
proxy.$tab.closePage();
|
||||
};
|
||||
|
||||
// 回显当前登录用户信息
|
||||
watch(() => props.user, user => {
|
||||
if (user) {
|
||||
form.value = { nickName: user.nickName, phonenumber: user.phonenumber, email: user.email, sex: user.sex };
|
||||
}
|
||||
},{ immediate: true });
|
||||
</script>
|
||||
@@ -27,42 +27,49 @@ export function updateCallNumberVoiceConfig(data) {
|
||||
}
|
||||
|
||||
// 分诊排队管理相关API
|
||||
// 获取智能候选池(已签到未入队)
|
||||
// 说明:直接使用门诊挂号的"当日已挂号"接口
|
||||
// 获取智能候选池(使用门诊挂号当日已挂号接口)
|
||||
export function getCandidatePool(params) {
|
||||
return request({
|
||||
url: '/triage/queue/candidatePool',
|
||||
url: '/charge-manage/register/current-day-encounter',
|
||||
method: 'get',
|
||||
params: params,
|
||||
params: {
|
||||
pageNo: params?.pageNo || 1,
|
||||
pageSize: params?.pageSize || 10000,
|
||||
searchKey: params?.searchKey || '',
|
||||
statusEnum: params?.statusEnum || -1 // -1表示排除退号记录(正常挂号)
|
||||
},
|
||||
skipErrorMsg: true // 跳过错误提示,由组件处理
|
||||
}).catch(() => {
|
||||
// 返回一个 rejected promise,让组件可以捕获
|
||||
return Promise.reject(new Error('API未实现'))
|
||||
})
|
||||
}
|
||||
|
||||
// 获取智能队列(当前队列)
|
||||
// 获取智能队列(使用门诊挂号当日已挂号接口)
|
||||
export function getQueueList(params) {
|
||||
return request({
|
||||
url: '/triage/queue/list',
|
||||
url: '/charge-manage/register/current-day-encounter',
|
||||
method: 'get',
|
||||
params: params,
|
||||
params: {
|
||||
pageNo: params?.pageNo || 1,
|
||||
pageSize: params?.pageSize || 10000,
|
||||
searchKey: params?.searchKey || '',
|
||||
statusEnum: params?.statusEnum || -1 // -1表示排除退号记录(正常挂号)
|
||||
},
|
||||
skipErrorMsg: true // 跳过错误提示,由组件处理
|
||||
}).catch(() => {
|
||||
// 返回一个 rejected promise,让组件可以捕获
|
||||
return Promise.reject(new Error('API未实现'))
|
||||
})
|
||||
}
|
||||
|
||||
// 获取统计信息
|
||||
// 获取统计信息(使用门诊挂号当日已挂号接口统计)
|
||||
export function getQueueStatistics(params) {
|
||||
return request({
|
||||
url: '/triage/queue/statistics',
|
||||
url: '/charge-manage/register/current-day-encounter',
|
||||
method: 'get',
|
||||
params: params,
|
||||
params: {
|
||||
pageNo: 1,
|
||||
pageSize: 10000,
|
||||
searchKey: params?.searchKey || '',
|
||||
statusEnum: params?.statusEnum || -1
|
||||
},
|
||||
skipErrorMsg: true // 跳过错误提示,由组件处理
|
||||
}).catch(() => {
|
||||
// 返回一个 rejected promise,让组件可以捕获
|
||||
return Promise.reject(new Error('API未实现'))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -73,7 +80,26 @@ export function addToQueue(data) {
|
||||
method: 'post',
|
||||
data: data,
|
||||
skipErrorMsg: true
|
||||
}).catch(() => Promise.reject(new Error('API未实现')))
|
||||
})
|
||||
}
|
||||
|
||||
// 获取队列列表(从数据库读取)
|
||||
export function getTriageQueueList(params) {
|
||||
return request({
|
||||
url: '/triage/queue/list',
|
||||
method: 'get',
|
||||
params: params,
|
||||
skipErrorMsg: true
|
||||
})
|
||||
}
|
||||
|
||||
// 移出队列
|
||||
export function removeFromQueue(id) {
|
||||
return request({
|
||||
url: `/triage/queue/remove/${id}`,
|
||||
method: 'delete',
|
||||
skipErrorMsg: true
|
||||
})
|
||||
}
|
||||
|
||||
// 调整队列顺序
|
||||
@@ -83,7 +109,7 @@ export function adjustQueueOrder(data) {
|
||||
method: 'put',
|
||||
data: data,
|
||||
skipErrorMsg: true
|
||||
}).catch(() => Promise.reject(new Error('API未实现')))
|
||||
})
|
||||
}
|
||||
|
||||
// 叫号控制
|
||||
@@ -93,7 +119,7 @@ export function callPatient(data) {
|
||||
method: 'post',
|
||||
data: data,
|
||||
skipErrorMsg: true
|
||||
}).catch(() => Promise.reject(new Error('API未实现')))
|
||||
})
|
||||
}
|
||||
|
||||
// 跳过患者
|
||||
@@ -103,7 +129,7 @@ export function skipPatient(data) {
|
||||
method: 'post',
|
||||
data: data,
|
||||
skipErrorMsg: true
|
||||
}).catch(() => Promise.reject(new Error('API未实现')))
|
||||
})
|
||||
}
|
||||
|
||||
// 完成叫号
|
||||
@@ -113,7 +139,7 @@ export function completeCall(data) {
|
||||
method: 'post',
|
||||
data: data,
|
||||
skipErrorMsg: true
|
||||
}).catch(() => Promise.reject(new Error('API未实现')))
|
||||
})
|
||||
}
|
||||
|
||||
// 过号重排
|
||||
@@ -123,5 +149,24 @@ export function requeuePatient(data) {
|
||||
method: 'post',
|
||||
data: data,
|
||||
skipErrorMsg: true
|
||||
}).catch(() => Promise.reject(new Error('API未实现')))
|
||||
})
|
||||
}
|
||||
|
||||
// 下一患者
|
||||
export function nextPatient(data) {
|
||||
return request({
|
||||
url: '/triage/queue/next',
|
||||
method: 'post',
|
||||
data: data,
|
||||
skipErrorMsg: true
|
||||
})
|
||||
}
|
||||
|
||||
// 查询就诊科室列表(从门诊挂号模块复用)
|
||||
export function getLocationTree(query) {
|
||||
return request({
|
||||
url: '/charge-manage/register/org-list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
25
openhis-ui-vue3/test-util-extend.js
Normal file
25
openhis-ui-vue3/test-util-extend.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// 测试util._extend是否存在
|
||||
if (typeof process !== 'undefined' && process.versions && process.versions.node) {
|
||||
try {
|
||||
const util = require('util');
|
||||
console.log('util._extend存在吗?', typeof util._extend);
|
||||
if (typeof util._extend === 'function') {
|
||||
console.log('util._extend是一个函数');
|
||||
} else {
|
||||
console.log('util._extend不是一个函数,添加兼容实现');
|
||||
util._extend = function(destination, source) {
|
||||
for (var key in source) {
|
||||
if (source.hasOwnProperty(key)) {
|
||||
destination[key] = source[key];
|
||||
}
|
||||
}
|
||||
return destination;
|
||||
};
|
||||
console.log('兼容实现添加成功');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('util模块加载失败:', e);
|
||||
}
|
||||
} else {
|
||||
console.log('不在Node.js环境中');
|
||||
}
|
||||
121
query_serial_number.sql
Normal file
121
query_serial_number.sql
Normal file
@@ -0,0 +1,121 @@
|
||||
-- ============================================
|
||||
-- 查询流水号相关SQL语句
|
||||
-- ============================================
|
||||
|
||||
-- 1. 查询所有挂号记录的流水号(主键ID)及相关信息
|
||||
SELECT
|
||||
id AS 流水号,
|
||||
display_order AS 就诊序号,
|
||||
bus_no AS 就诊编码,
|
||||
patient_id AS 患者ID,
|
||||
organization_id AS 科室ID,
|
||||
create_time AS 挂号时间,
|
||||
status_enum AS 状态
|
||||
FROM adm_encounter
|
||||
WHERE delete_flag = '0'
|
||||
ORDER BY create_time DESC;
|
||||
|
||||
-- 2. 根据流水号(主键ID)查询某条挂号记录的详细信息
|
||||
SELECT
|
||||
e.id AS 流水号,
|
||||
e.display_order AS 就诊序号,
|
||||
e.bus_no AS 就诊编码,
|
||||
p.name AS 患者姓名,
|
||||
p.id_card AS 身份证号,
|
||||
p.phone AS 联系电话,
|
||||
o.name AS 科室名称,
|
||||
pr.name AS 医生姓名,
|
||||
e.create_time AS 挂号时间,
|
||||
e.status_enum AS 状态
|
||||
FROM adm_encounter e
|
||||
LEFT JOIN adm_patient p ON e.patient_id = p.id AND p.delete_flag = '0'
|
||||
LEFT JOIN adm_organization o ON e.organization_id = o.id AND o.delete_flag = '0'
|
||||
LEFT JOIN adm_encounter_participant ep ON e.id = ep.encounter_id AND ep.delete_flag = '0' AND ep.type_code = 'PRF'
|
||||
LEFT JOIN adm_practitioner pr ON ep.practitioner_id = pr.id AND pr.delete_flag = '0'
|
||||
WHERE e.id = 1234567890123456789 -- 替换为实际的流水号(主键ID)
|
||||
AND e.delete_flag = '0';
|
||||
|
||||
-- 3. 根据患者姓名或身份证号查询流水号
|
||||
SELECT
|
||||
e.id AS 流水号,
|
||||
e.display_order AS 就诊序号,
|
||||
p.name AS 患者姓名,
|
||||
p.id_card AS 身份证号,
|
||||
e.create_time AS 挂号时间,
|
||||
o.name AS 科室名称
|
||||
FROM adm_encounter e
|
||||
LEFT JOIN adm_patient p ON e.patient_id = p.id AND p.delete_flag = '0'
|
||||
LEFT JOIN adm_organization o ON e.organization_id = o.id AND o.delete_flag = '0'
|
||||
WHERE e.delete_flag = '0'
|
||||
AND (p.name LIKE '%张三%' OR p.id_card = '110101199001011234') -- 替换为实际的患者姓名或身份证号
|
||||
ORDER BY e.create_time DESC;
|
||||
|
||||
-- 4. 查询当日的挂号记录及流水号
|
||||
SELECT
|
||||
e.id AS 流水号,
|
||||
e.display_order AS 就诊序号,
|
||||
e.bus_no AS 就诊编码,
|
||||
p.name AS 患者姓名,
|
||||
p.id_card AS 身份证号,
|
||||
o.name AS 科室名称,
|
||||
pr.name AS 医生姓名,
|
||||
e.create_time AS 挂号时间,
|
||||
e.status_enum AS 状态
|
||||
FROM adm_encounter e
|
||||
LEFT JOIN adm_patient p ON e.patient_id = p.id AND p.delete_flag = '0'
|
||||
LEFT JOIN adm_organization o ON e.organization_id = o.id AND o.delete_flag = '0'
|
||||
LEFT JOIN adm_encounter_participant ep ON e.id = ep.encounter_id AND ep.delete_flag = '0' AND ep.type_code = 'PRF'
|
||||
LEFT JOIN adm_practitioner pr ON ep.practitioner_id = pr.id AND pr.delete_flag = '0'
|
||||
WHERE e.delete_flag = '0'
|
||||
AND DATE(e.create_time) = CURRENT_DATE -- 查询当日
|
||||
ORDER BY e.display_order ASC, e.create_time ASC;
|
||||
|
||||
-- 5. 查询指定日期范围的挂号记录及流水号
|
||||
SELECT
|
||||
e.id AS 流水号,
|
||||
e.display_order AS 就诊序号,
|
||||
e.bus_no AS 就诊编码,
|
||||
p.name AS 患者姓名,
|
||||
p.id_card AS 身份证号,
|
||||
o.name AS 科室名称,
|
||||
e.create_time AS 挂号时间
|
||||
FROM adm_encounter e
|
||||
LEFT JOIN adm_patient p ON e.patient_id = p.id AND p.delete_flag = '0'
|
||||
LEFT JOIN adm_organization o ON e.organization_id = o.id AND o.delete_flag = '0'
|
||||
WHERE e.delete_flag = '0'
|
||||
AND e.create_time >= '2024-01-01 00:00:00' -- 开始时间
|
||||
AND e.create_time <= '2024-01-31 23:59:59' -- 结束时间
|
||||
ORDER BY e.create_time DESC;
|
||||
|
||||
-- 6. 查询指定科室的挂号记录及流水号
|
||||
SELECT
|
||||
e.id AS 流水号,
|
||||
e.display_order AS 就诊序号,
|
||||
p.name AS 患者姓名,
|
||||
o.name AS 科室名称,
|
||||
e.create_time AS 挂号时间
|
||||
FROM adm_encounter e
|
||||
LEFT JOIN adm_patient p ON e.patient_id = p.id AND p.delete_flag = '0'
|
||||
LEFT JOIN adm_organization o ON e.organization_id = o.id AND o.delete_flag = '0'
|
||||
WHERE e.delete_flag = '0'
|
||||
AND o.name LIKE '%心内科%' -- 替换为实际的科室名称
|
||||
AND DATE(e.create_time) = CURRENT_DATE
|
||||
ORDER BY e.display_order ASC;
|
||||
|
||||
-- 7. 统计每日挂号数量及流水号范围
|
||||
SELECT
|
||||
DATE(create_time) AS 日期,
|
||||
COUNT(*) AS 挂号数量,
|
||||
MIN(id) AS 最小流水号,
|
||||
MAX(id) AS 最大流水号,
|
||||
MIN(display_order) AS 最小就诊序号,
|
||||
MAX(display_order) AS 最大就诊序号
|
||||
FROM adm_encounter
|
||||
WHERE delete_flag = '0'
|
||||
GROUP BY DATE(create_time)
|
||||
ORDER BY 日期 DESC
|
||||
LIMIT 30; -- 最近30天
|
||||
|
||||
|
||||
|
||||
|
||||
224
sql/query_today_outpatient_patients.sql
Normal file
224
sql/query_today_outpatient_patients.sql
Normal file
@@ -0,0 +1,224 @@
|
||||
-- ============================================
|
||||
-- 查询今日门诊患者数据(用于调试分诊排队页面)
|
||||
-- 简化版:直接查询,不考虑登录和租户
|
||||
-- ============================================
|
||||
|
||||
-- 1. 最简单查询:查看今天所有的挂号记录(不关联患者表)
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
enc.start_time AS registerTime,
|
||||
enc.status_enum AS statusEnum,
|
||||
enc.organization_id AS departmentId,
|
||||
enc.tenant_id AS tenantId,
|
||||
enc.delete_flag AS deleteFlag,
|
||||
enc.bus_no AS encounterBusNo
|
||||
FROM adm_encounter enc
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.start_time::DATE = CURRENT_DATE
|
||||
ORDER BY enc.start_time DESC
|
||||
LIMIT 100;
|
||||
|
||||
-- ============================================
|
||||
-- 2. 完整查询:直接查询患者信息(简化版,不考虑租户)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
pt.name AS patientName,
|
||||
pt.gender_enum AS genderEnum,
|
||||
pt.birth_date AS birthDate,
|
||||
pt.id_card AS idCard,
|
||||
pt.phone AS phone,
|
||||
enc.bus_no AS encounterBusNo,
|
||||
enc.start_time AS registerTime,
|
||||
enc.reception_time AS receptionTime,
|
||||
enc.status_enum AS statusEnum,
|
||||
enc.subject_status_enum AS subjectStatusEnum,
|
||||
CASE
|
||||
WHEN enc.reception_time IS NOT NULL
|
||||
THEN EXTRACT(EPOCH FROM (enc.reception_time - enc.start_time)) / 60
|
||||
ELSE EXTRACT(EPOCH FROM (NOW() - enc.start_time)) / 60
|
||||
END AS waitingDuration,
|
||||
CASE
|
||||
WHEN enc.end_time IS NOT NULL AND enc.reception_time IS NOT NULL
|
||||
THEN EXTRACT(EPOCH FROM (enc.end_time - enc.reception_time)) / 60
|
||||
ELSE NULL
|
||||
END AS visitDuration,
|
||||
pt.type_code AS typeCode,
|
||||
enc.important_flag AS importantFlag,
|
||||
-- 医生信息(左关联)
|
||||
ep.practitioner_id AS doctorId
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt
|
||||
ON enc.patient_id = pt.id
|
||||
AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_encounter_participant ep
|
||||
ON enc.id = ep.encounter_id
|
||||
AND ep.type_code = 'admitter'
|
||||
AND ep.delete_flag = '0'
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.start_time::DATE = CURRENT_DATE
|
||||
ORDER BY enc.start_time DESC
|
||||
LIMIT 10000;
|
||||
|
||||
-- ============================================
|
||||
-- 3. 检查数据条件:查看可能影响查询结果的数据
|
||||
-- ============================================
|
||||
|
||||
-- 3.1 检查今天的挂号记录总数
|
||||
SELECT
|
||||
COUNT(*) AS total_count,
|
||||
COUNT(CASE WHEN delete_flag = '0' THEN 1 END) AS not_deleted_count,
|
||||
COUNT(CASE WHEN delete_flag = '1' THEN 1 END) AS deleted_count
|
||||
FROM adm_encounter
|
||||
WHERE start_time::DATE = CURRENT_DATE;
|
||||
|
||||
-- 3.2 检查不同租户的数据分布
|
||||
SELECT
|
||||
tenant_id,
|
||||
COUNT(*) AS count,
|
||||
COUNT(CASE WHEN delete_flag = '0' THEN 1 END) AS not_deleted_count
|
||||
FROM adm_encounter
|
||||
WHERE start_time::DATE = CURRENT_DATE
|
||||
GROUP BY tenant_id
|
||||
ORDER BY tenant_id;
|
||||
|
||||
-- 3.3 检查不同状态的数据分布
|
||||
SELECT
|
||||
status_enum,
|
||||
COUNT(*) AS count,
|
||||
COUNT(CASE WHEN delete_flag = '0' THEN 1 END) AS not_deleted_count
|
||||
FROM adm_encounter
|
||||
WHERE start_time::DATE = CURRENT_DATE
|
||||
AND delete_flag = '0'
|
||||
GROUP BY status_enum
|
||||
ORDER BY status_enum;
|
||||
|
||||
-- 3.4 检查是否有患者数据关联
|
||||
SELECT
|
||||
COUNT(DISTINCT enc.id) AS encounter_count,
|
||||
COUNT(DISTINCT pt.id) AS patient_count,
|
||||
COUNT(DISTINCT CASE WHEN pt.delete_flag = '0' THEN pt.id END) AS patient_not_deleted_count
|
||||
FROM adm_encounter enc
|
||||
LEFT JOIN adm_patient pt ON enc.patient_id = pt.id
|
||||
WHERE enc.start_time::DATE = CURRENT_DATE
|
||||
AND enc.delete_flag = '0';
|
||||
|
||||
-- 3.5 检查医生参与信息
|
||||
SELECT
|
||||
COUNT(DISTINCT enc.id) AS encounter_count,
|
||||
COUNT(DISTINCT ep.encounter_id) AS has_doctor_count,
|
||||
COUNT(DISTINCT ep.practitioner_id) AS doctor_count
|
||||
FROM adm_encounter enc
|
||||
LEFT JOIN adm_encounter_participant ep
|
||||
ON enc.id = ep.encounter_id
|
||||
AND ep.type_code = 'admitter'
|
||||
AND ep.delete_flag = '0'
|
||||
WHERE enc.start_time::DATE = CURRENT_DATE
|
||||
AND enc.delete_flag = '0';
|
||||
|
||||
-- ============================================
|
||||
-- 4. 快速查询:直接查看患者姓名和挂号信息(最简单)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS 就诊记录ID,
|
||||
pt.name AS 患者姓名,
|
||||
pt.id_card AS 身份证号,
|
||||
pt.phone AS 联系电话,
|
||||
enc.start_time AS 挂号时间,
|
||||
enc.create_time AS 创建时间,
|
||||
enc.status_enum AS 状态,
|
||||
enc.bus_no AS 就诊流水号
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND pt.delete_flag = '0'
|
||||
AND enc.start_time::DATE = CURRENT_DATE
|
||||
ORDER BY enc.start_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 4.1 使用 create_time 查询(和门诊挂号页面一致)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS 就诊记录ID,
|
||||
pt.name AS 患者姓名,
|
||||
pt.id_card AS 身份证号,
|
||||
pt.phone AS 联系电话,
|
||||
enc.create_time AS 创建时间,
|
||||
enc.start_time AS 开始时间,
|
||||
enc.status_enum AS 状态,
|
||||
enc.bus_no AS 就诊流水号
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND pt.delete_flag = '0'
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 4.2 完全模拟门诊挂号页面的查询(包含支付状态)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS 就诊记录ID,
|
||||
pt.name AS 患者姓名,
|
||||
pt.id_card AS 身份证号,
|
||||
pt.phone AS 联系电话,
|
||||
enc.create_time AS 挂号时间,
|
||||
enc.status_enum AS 状态,
|
||||
enc.bus_no AS 就诊流水号,
|
||||
ci.total_price AS 挂号金额,
|
||||
pr.status_enum AS 支付状态
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
LEFT JOIN fin_payment_reconciliation pr ON ci.id::TEXT = ANY(string_to_array(pr.charge_item_ids,','))
|
||||
AND pr.delete_flag = '0'
|
||||
AND pr.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1 -- 门诊
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 5. 快速检查:查看最近7天的挂号记录数量(使用 start_time)
|
||||
-- ============================================
|
||||
SELECT
|
||||
start_time::DATE AS register_date,
|
||||
COUNT(*) AS total_count,
|
||||
COUNT(CASE WHEN delete_flag = '0' THEN 1 END) AS not_deleted_count
|
||||
FROM adm_encounter
|
||||
WHERE start_time::DATE >= CURRENT_DATE - INTERVAL '7 days'
|
||||
GROUP BY start_time::DATE
|
||||
ORDER BY start_time::DATE DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 5.1 快速检查:查看最近7天的挂号记录数量(使用 create_time)
|
||||
-- ============================================
|
||||
SELECT
|
||||
create_time::DATE AS register_date,
|
||||
COUNT(*) AS total_count,
|
||||
COUNT(CASE WHEN delete_flag = '0' THEN 1 END) AS not_deleted_count
|
||||
FROM adm_encounter
|
||||
WHERE create_time::DATE >= CURRENT_DATE - INTERVAL '7 days'
|
||||
AND class_enum = 1 -- 门诊
|
||||
GROUP BY create_time::DATE
|
||||
ORDER BY create_time::DATE DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 6. 对比 start_time 和 create_time 的差异
|
||||
-- ============================================
|
||||
SELECT
|
||||
id AS 就诊记录ID,
|
||||
start_time AS 开始时间,
|
||||
create_time AS 创建时间,
|
||||
(start_time::DATE) AS 开始日期,
|
||||
(create_time::DATE) AS 创建日期,
|
||||
status_enum AS 状态,
|
||||
delete_flag AS 是否删除
|
||||
FROM adm_encounter
|
||||
WHERE (start_time::DATE = CURRENT_DATE OR create_time::DATE = CURRENT_DATE)
|
||||
AND delete_flag = '0'
|
||||
ORDER BY create_time DESC
|
||||
LIMIT 20;
|
||||
|
||||
294
排查指南-字段查询问题.md
Normal file
294
排查指南-字段查询问题.md
Normal file
@@ -0,0 +1,294 @@
|
||||
# 排查指南:字段查询不到数据的问题
|
||||
|
||||
## 问题类型
|
||||
**症状**:SQL 查询条件使用了某个字段,但查询结果为空,而数据库中明明有数据。
|
||||
|
||||
**根本原因**:查询条件使用的字段在数据插入/更新时没有被设置(为 NULL 或默认值)。
|
||||
|
||||
---
|
||||
|
||||
## 排查步骤
|
||||
|
||||
### 第一步:确认字段是否存在
|
||||
```sql
|
||||
-- 检查表结构,确认字段是否存在
|
||||
SELECT column_name, data_type, is_nullable, column_default
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'adm_encounter'
|
||||
AND column_name = 'start_time';
|
||||
```
|
||||
|
||||
**检查点**:
|
||||
- ✅ 字段确实存在
|
||||
- ❌ 字段不存在 → 检查字段名拼写、大小写
|
||||
|
||||
---
|
||||
|
||||
### 第二步:检查字段是否有值
|
||||
```sql
|
||||
-- 检查字段的实际值
|
||||
SELECT
|
||||
id,
|
||||
start_time, -- 检查这个字段
|
||||
create_time, -- 对比其他时间字段
|
||||
status_enum
|
||||
FROM adm_encounter
|
||||
WHERE delete_flag = '0'
|
||||
AND create_time::DATE = CURRENT_DATE -- 用能查到数据的条件
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
**检查点**:
|
||||
- ✅ `start_time` 有值 → 继续排查查询条件
|
||||
- ❌ `start_time` 为 NULL → **问题确认**:字段没有被设置
|
||||
|
||||
---
|
||||
|
||||
### 第三步:检查插入/更新代码
|
||||
找到创建/更新数据的 Service 方法,检查是否设置了该字段。
|
||||
|
||||
**检查位置**:
|
||||
1. **Service 实现类**:查找 `save`、`insert`、`update` 相关方法
|
||||
2. **Mapper XML**:检查 INSERT/UPDATE 语句
|
||||
3. **Entity 类**:检查字段定义和默认值
|
||||
|
||||
**示例检查**:
|
||||
```java
|
||||
// ❌ 错误示例:没有设置 start_time
|
||||
Encounter encounter = new Encounter();
|
||||
encounter.setBusNo("xxx");
|
||||
encounter.setPatientId(123L);
|
||||
// 没有 encounter.setStartTime(...)
|
||||
iEncounterService.save(encounter);
|
||||
|
||||
// ✅ 正确示例:设置了 start_time
|
||||
Encounter encounter = new Encounter();
|
||||
encounter.setBusNo("xxx");
|
||||
encounter.setPatientId(123L);
|
||||
encounter.setStartTime(new Date()); // 设置了字段
|
||||
iEncounterService.save(encounter);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 第四步:对比能查到数据的方法
|
||||
找到系统中**能查到数据**的类似查询,对比差异。
|
||||
|
||||
**对比维度**:
|
||||
1. **使用的字段**:`start_time` vs `create_time`
|
||||
2. **查询条件**:WHERE 子句的差异
|
||||
3. **关联表**:是否 JOIN 了其他表
|
||||
4. **业务逻辑**:是否过滤了特定状态
|
||||
|
||||
**示例对比**:
|
||||
```sql
|
||||
-- ❌ 查不到数据的方法
|
||||
SELECT * FROM adm_encounter
|
||||
WHERE start_time::DATE = CURRENT_DATE; -- start_time 为 NULL
|
||||
|
||||
-- ✅ 能查到数据的方法
|
||||
SELECT * FROM adm_encounter
|
||||
WHERE create_time::DATE = CURRENT_DATE; -- create_time 有值
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 第五步:检查字段的业务含义
|
||||
确认字段的**业务含义**和**使用场景**。
|
||||
|
||||
**常见情况**:
|
||||
- `create_time`:记录创建时间(自动设置)
|
||||
- `update_time`:记录更新时间(自动设置)
|
||||
- `start_time`:业务开始时间(需要手动设置)
|
||||
- `end_time`:业务结束时间(需要手动设置)
|
||||
|
||||
**判断规则**:
|
||||
- 如果字段是**业务字段**(如 `start_time`),需要手动设置
|
||||
- 如果字段是**系统字段**(如 `create_time`),通常自动设置
|
||||
|
||||
---
|
||||
|
||||
## 调试技巧
|
||||
|
||||
### 技巧1:打印 SQL 和参数
|
||||
在 Service 方法中添加日志,查看实际执行的 SQL:
|
||||
|
||||
```java
|
||||
@Override
|
||||
public IPage<TodayOutpatientPatientDto> getTodayOutpatientPatients(...) {
|
||||
// 打印查询条件
|
||||
log.info("查询条件: queryDate={}, doctorId={}, departmentId={}",
|
||||
queryDate, doctorId, departmentId);
|
||||
|
||||
// 执行查询
|
||||
IPage<TodayOutpatientPatientDto> result = mapper.getTodayOutpatientPatients(...);
|
||||
|
||||
// 打印结果
|
||||
log.info("查询结果: 总数={}, 记录数={}",
|
||||
result.getTotal(), result.getRecords().size());
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
### 技巧2:直接执行 SQL 验证
|
||||
在数据库客户端直接执行 SQL,验证查询条件是否正确:
|
||||
|
||||
```sql
|
||||
-- 1. 先查看数据
|
||||
SELECT id, start_time, create_time, status_enum
|
||||
FROM adm_encounter
|
||||
WHERE delete_flag = '0'
|
||||
LIMIT 10;
|
||||
|
||||
-- 2. 测试查询条件
|
||||
SELECT COUNT(*)
|
||||
FROM adm_encounter
|
||||
WHERE delete_flag = '0'
|
||||
AND start_time::DATE = CURRENT_DATE; -- 测试这个条件
|
||||
|
||||
-- 3. 对比其他条件
|
||||
SELECT COUNT(*)
|
||||
FROM adm_encounter
|
||||
WHERE delete_flag = '0'
|
||||
AND create_time::DATE = CURRENT_DATE; -- 对比这个条件
|
||||
```
|
||||
|
||||
### 技巧3:使用数据库工具检查
|
||||
使用数据库管理工具(如 pgAdmin、DBeaver):
|
||||
1. 查看表结构
|
||||
2. 查看数据样本
|
||||
3. 执行测试查询
|
||||
4. 检查字段的 NULL 值比例
|
||||
|
||||
---
|
||||
|
||||
## 预防措施
|
||||
|
||||
### 1. 代码审查检查清单
|
||||
在代码审查时,检查以下内容:
|
||||
|
||||
- [ ] **插入数据时**:是否设置了所有必要的业务字段?
|
||||
- [ ] **查询条件时**:使用的字段是否在插入时被设置?
|
||||
- [ ] **字段命名**:是否遵循命名规范(`create_time` vs `start_time`)?
|
||||
- [ ] **文档注释**:字段的业务含义是否清晰?
|
||||
|
||||
### 2. 单元测试
|
||||
编写单元测试,验证字段设置:
|
||||
|
||||
```java
|
||||
@Test
|
||||
public void testSaveEncounter() {
|
||||
Encounter encounter = new Encounter();
|
||||
encounter.setPatientId(123L);
|
||||
encounter.setBusNo("TEST001");
|
||||
// 检查是否设置了 start_time
|
||||
assertNotNull(encounter.getStartTime(), "start_time 应该被设置");
|
||||
|
||||
Long id = encounterService.saveEncounterByRegister(encounter);
|
||||
|
||||
// 验证数据库中的值
|
||||
Encounter saved = encounterService.getById(id);
|
||||
assertNotNull(saved.getStartTime(), "数据库中的 start_time 不应该为 NULL");
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 数据库约束
|
||||
在数据库层面添加约束,防止 NULL 值:
|
||||
|
||||
```sql
|
||||
-- 如果 start_time 是必填字段,添加 NOT NULL 约束
|
||||
ALTER TABLE adm_encounter
|
||||
ALTER COLUMN start_time SET NOT NULL;
|
||||
|
||||
-- 或者添加默认值
|
||||
ALTER TABLE adm_encounter
|
||||
ALTER COLUMN start_time SET DEFAULT CURRENT_TIMESTAMP;
|
||||
```
|
||||
|
||||
### 4. 代码规范
|
||||
建立代码规范,明确字段使用规则:
|
||||
|
||||
```java
|
||||
/**
|
||||
* 保存就诊信息(门诊挂号)
|
||||
*
|
||||
* 注意:
|
||||
* - create_time: 自动设置(系统字段)
|
||||
* - start_time: 需要手动设置(业务字段,表示就诊开始时间)
|
||||
* - 如果 start_time 为 NULL,查询时使用 create_time 作为挂号时间
|
||||
*/
|
||||
public Long saveEncounterByRegister(Encounter encounter) {
|
||||
// 如果没有设置 start_time,使用当前时间
|
||||
if (encounter.getStartTime() == null) {
|
||||
encounter.setStartTime(new Date());
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常见问题模式
|
||||
|
||||
### 模式1:时间字段混淆
|
||||
- **问题**:使用 `start_time` 查询,但插入时没有设置
|
||||
- **解决**:使用 `create_time` 或确保插入时设置 `start_time`
|
||||
|
||||
### 模式2:状态字段未设置
|
||||
- **问题**:使用 `status_enum` 查询,但插入时使用默认值
|
||||
- **解决**:明确设置状态值
|
||||
|
||||
### 模式3:关联字段未设置
|
||||
- **问题**:使用 `organization_id` 查询,但插入时为 NULL
|
||||
- **解决**:确保插入时设置关联字段
|
||||
|
||||
---
|
||||
|
||||
## 快速排查清单
|
||||
|
||||
遇到"查询不到数据"问题时,按以下顺序检查:
|
||||
|
||||
1. ✅ **数据库中有数据吗?**
|
||||
```sql
|
||||
SELECT COUNT(*) FROM adm_encounter WHERE delete_flag = '0';
|
||||
```
|
||||
|
||||
2. ✅ **查询条件使用的字段有值吗?**
|
||||
```sql
|
||||
SELECT start_time FROM adm_encounter WHERE delete_flag = '0' LIMIT 10;
|
||||
```
|
||||
|
||||
3. ✅ **插入代码设置了该字段吗?**
|
||||
- 查看 Service 的 save/insert 方法
|
||||
- 检查是否调用了 setter 方法
|
||||
|
||||
4. ✅ **字段的业务含义是什么?**
|
||||
- 是系统字段(自动设置)还是业务字段(手动设置)?
|
||||
|
||||
5. ✅ **有类似功能能查到数据吗?**
|
||||
- 对比能查到数据的方法,找出差异
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
**核心原则**:
|
||||
1. **先看数据**:检查字段是否有值
|
||||
2. **再看代码**:检查插入时是否设置
|
||||
3. **对比差异**:找出能查到数据的方法
|
||||
4. **验证修复**:修改后验证数据
|
||||
|
||||
**记住**:查询条件使用的字段,必须在数据插入/更新时被设置!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user