Compare commits
8 Commits
c92ff38133
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0c6f57f6b | ||
|
|
0d57e984a6 | ||
| 4450e3cc50 | |||
|
|
902ee0587e | ||
| 49550fcc2e | |||
|
|
1dd7ee3428 | ||
|
|
8dff5d466a | ||
|
|
19ada4ace9 |
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;
|
package com.core.framework.config;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.DbType;
|
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.MybatisPlusInterceptor;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||||
|
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
|
||||||
import com.core.common.utils.SecurityUtils;
|
import com.core.common.utils.SecurityUtils;
|
||||||
import net.sf.jsqlparser.expression.Expression;
|
import net.sf.jsqlparser.expression.Expression;
|
||||||
import net.sf.jsqlparser.expression.LongValue;
|
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.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
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.transaction.annotation.EnableTransactionManagement;
|
||||||
import org.springframework.web.context.request.RequestContextHolder;
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.sql.DataSource;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -149,4 +156,41 @@ public class MybatisPlusConfig {
|
|||||||
|
|
||||||
return result != null ? result : 1; // 默认租户ID
|
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 {
|
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.core.common.core.domain.R;
|
||||||
import com.openhis.administration.domain.Patient;
|
import com.openhis.administration.domain.Patient;
|
||||||
import com.openhis.administration.service.IPatientService;
|
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.domain.Ticket;
|
||||||
|
import com.openhis.clinical.mapper.OrderMapper;
|
||||||
import com.openhis.clinical.service.ITicketService;
|
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.appservice.ITicketAppService;
|
||||||
import com.openhis.web.appointmentmanage.dto.TicketDto;
|
import com.openhis.web.appointmentmanage.dto.TicketDto;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.time.*;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
/**
|
/**
|
||||||
* 号源管理应用服务实现类
|
* 号源管理应用服务实现类
|
||||||
*
|
*
|
||||||
@@ -31,145 +39,14 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private IPatientService patientService;
|
private IPatientService patientService;
|
||||||
|
@Resource
|
||||||
|
private IDoctorScheduleAppService doctorScheduleAppService;
|
||||||
|
@Resource
|
||||||
|
private DoctorScheduleMapper doctorScheduleMapper;
|
||||||
|
@Resource
|
||||||
|
private OrderMapper orderMapper;
|
||||||
|
|
||||||
/**
|
private static final Logger log = LoggerFactory.getLogger(TicketAppServiceImpl.class);
|
||||||
* 查询号源列表
|
|
||||||
*
|
|
||||||
* @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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 预约号源
|
* 预约号源
|
||||||
@@ -179,35 +56,73 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public R<?> bookTicket(Map<String, Object> params) {
|
public R<?> bookTicket(Map<String, Object> params) {
|
||||||
|
// 1. 获取 ticketId 和 slotId
|
||||||
Long ticketId = null;
|
Long ticketId = null;
|
||||||
|
Long slotId = null;
|
||||||
if (params.get("ticketId") != null) {
|
if (params.get("ticketId") != null) {
|
||||||
ticketId = Long.valueOf(params.get("ticketId").toString());
|
ticketId = Long.valueOf(params.get("ticketId").toString());
|
||||||
}
|
}
|
||||||
if (ticketId == null) {
|
if (params.get("slotId") != null) {
|
||||||
return R.fail("参数错误");
|
slotId = Long.valueOf(params.get("slotId").toString());
|
||||||
}
|
}
|
||||||
|
// 2. 参数校验
|
||||||
|
if (ticketId == null || slotId == null) {
|
||||||
|
return R.fail("参数错误:ticketId 或 slotId 不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 3. 执行原有的预约逻辑
|
||||||
int result = ticketService.bookTicket(params);
|
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) {
|
} 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 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public R<?> cancelTicket(Long ticketId) {
|
public R<?> cancelTicket(Long slotId) {
|
||||||
if (ticketId == null) {
|
if (slotId == null) {
|
||||||
return R.fail("参数错误");
|
return R.fail("参数错误");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
int result = ticketService.cancelTicket(ticketId);
|
ticketService.cancelTicket(slotId);
|
||||||
return R.ok(result > 0 ? "取消成功" : "取消失败");
|
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) {
|
} catch (Exception e) {
|
||||||
return R.fail(e.getMessage());
|
return R.fail(e.getMessage());
|
||||||
}
|
}
|
||||||
@@ -253,31 +168,87 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public R<?> listAllTickets() {
|
public R<?> listAllTickets() {
|
||||||
// 创建固定的测试数据,用于验证前端是否能展示数据
|
// 1. 从 AppService 获取排班数据
|
||||||
List<TicketDto> testTickets = new ArrayList<>();
|
R<?> response = doctorScheduleAppService.getDoctorScheduleList();
|
||||||
|
// 获取返回的 List 数据 (假设 R.ok 里的数据是 List<DoctorSchedule>)
|
||||||
// 创建5条测试数据
|
List<DoctorSchedule> scheduleList = (List<DoctorSchedule>) response.getData();
|
||||||
for (int i = 1; i <= 5; i++) {
|
|
||||||
TicketDto dto = new TicketDto();
|
// 2. 转换数据为 TicketDto
|
||||||
dto.setSlot_id((long) i);
|
List<TicketDto> tickets = new ArrayList<>();
|
||||||
dto.setBusNo("TEST0000" + i);
|
|
||||||
dto.setDepartment("内科");
|
if (scheduleList != null) {
|
||||||
dto.setDoctor("张三");
|
for (DoctorSchedule schedule : scheduleList) {
|
||||||
dto.setTicketType("expert");
|
TicketDto dto = new TicketDto();
|
||||||
dto.setDateTime("08:00-08:50");
|
|
||||||
dto.setStatus("未预约");
|
// 基础信息映射
|
||||||
dto.setFee("150");
|
dto.setSlot_id(Long.valueOf(schedule.getId())); // Integer 转 Long
|
||||||
dto.setAppointmentDate(new Date());
|
dto.setBusNo(String.valueOf(schedule.getId())); // 生成一个业务编号
|
||||||
testTickets.add(dto);
|
dto.setDepartment(String.valueOf(schedule.getDeptId())); // 如果有科室名建议关联查询,这里暂填ID
|
||||||
|
dto.setDoctor(schedule.getDoctor());
|
||||||
|
|
||||||
|
// 号源类型处理:根据挂号项目判断是普通号还是专家号
|
||||||
|
String registerItem = schedule.getRegisterItem();
|
||||||
|
if (registerItem != null && registerItem.contains("专家")) {
|
||||||
|
dto.setTicketType("expert");
|
||||||
|
} 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("未预约");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 费用处理 (挂号费 + 诊疗费)
|
||||||
|
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<>();
|
Map<String, Object> result = new HashMap<>();
|
||||||
result.put("list", testTickets);
|
result.put("list", tickets);
|
||||||
result.put("total", testTickets.size());
|
result.put("total", tickets.size());
|
||||||
result.put("page", 1);
|
result.put("page", 1);
|
||||||
result.put("limit", 20);
|
result.put("limit", 20);
|
||||||
|
|
||||||
return R.ok(result);
|
return R.ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,9 +293,6 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
case "cancelled":
|
case "cancelled":
|
||||||
dto.setStatus("已取消");
|
dto.setStatus("已取消");
|
||||||
break;
|
break;
|
||||||
case "locked":
|
|
||||||
dto.setStatus("已锁定");
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
dto.setStatus(status);
|
dto.setStatus(status);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,28 +21,6 @@ public class TicketController {
|
|||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ITicketAppService ticketAppService;
|
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 {
|
} else {
|
||||||
adviceTypes = List.of(1, 2, 3);
|
adviceTypes = List.of(1, 2, 3);
|
||||||
}
|
}
|
||||||
|
// 门诊划价:不要强制 pricingFlag=1 参与过滤(wor_activity_definition.pricing_flag 可能为 0),
|
||||||
|
// 否则会导致诊疗项目(adviceType=3)查询结果为空 records=[]
|
||||||
return iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, searchKey, locationId, null,
|
return iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, searchKey, locationId, null,
|
||||||
organizationId, pageNo, pageSize, Whether.YES.getValue(), adviceTypes, null);
|
organizationId, pageNo, pageSize, null, adviceTypes, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,4 +90,10 @@ public interface IDoctorStationMainAppService {
|
|||||||
*/
|
*/
|
||||||
List<ReceptionStatisticsDto> getReceptionStatistics(String startTime,String endTime,Long practitionerId);
|
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("从数据库查询医嘱基础信息");
|
log.info("从数据库查询医嘱基础信息");
|
||||||
|
|
||||||
// 设置默认科室 (不取前端传的了)
|
// 设置默认科室:仅当前端/调用方未传 organizationId 时才回退到登录人科室
|
||||||
organizationId = SecurityUtils.getLoginUser().getOrgId();
|
// 否则会导致门诊划价等场景(按患者挂号科室查询)返回空
|
||||||
|
if (organizationId == null) {
|
||||||
|
organizationId = SecurityUtils.getLoginUser().getOrgId();
|
||||||
|
}
|
||||||
|
|
||||||
// 医嘱定价来源
|
// 医嘱定价来源
|
||||||
String orderPricingSource = TenantOptionUtil.getOptionContent(TenantOptionDict.ORDER_PRICING_SOURCE);
|
String orderPricingSource = TenantOptionUtil.getOptionContent(TenantOptionDict.ORDER_PRICING_SOURCE);
|
||||||
|
|||||||
@@ -26,9 +26,11 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -121,6 +123,8 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public R<?> receiveEncounter(Long encounterId) {
|
public R<?> receiveEncounter(Long encounterId) {
|
||||||
|
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||||
|
String currentUsername = SecurityUtils.getUsername();
|
||||||
int update = encounterMapper.update(null,
|
int update = encounterMapper.update(null,
|
||||||
new LambdaUpdateWrapper<Encounter>().eq(Encounter::getId, encounterId)
|
new LambdaUpdateWrapper<Encounter>().eq(Encounter::getId, encounterId)
|
||||||
.set(Encounter::getReceptionTime, new Date())
|
.set(Encounter::getReceptionTime, new Date())
|
||||||
@@ -138,6 +142,9 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
|
|||||||
encounterParticipant.setTypeCode(ParticipantType.ADMITTER.getCode());// 接诊医生
|
encounterParticipant.setTypeCode(ParticipantType.ADMITTER.getCode());// 接诊医生
|
||||||
encounterParticipant.setPractitionerId(SecurityUtils.getLoginUser().getPractitionerId());
|
encounterParticipant.setPractitionerId(SecurityUtils.getLoginUser().getPractitionerId());
|
||||||
encounterParticipant.setStatusEnum(EncounterActivityStatus.ACTIVE.getValue()); // 状态
|
encounterParticipant.setStatusEnum(EncounterActivityStatus.ACTIVE.getValue()); // 状态
|
||||||
|
encounterParticipant.setTenantId(tenantId);
|
||||||
|
encounterParticipant.setCreateBy(currentUsername);
|
||||||
|
encounterParticipant.setCreateTime(new Date());
|
||||||
iEncounterParticipantService.save(encounterParticipant);
|
iEncounterParticipantService.save(encounterParticipant);
|
||||||
return update > 0 ? R.ok() : R.fail();
|
return update > 0 ? R.ok() : R.fail();
|
||||||
}
|
}
|
||||||
@@ -323,4 +330,45 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
|
|||||||
practitionerId);
|
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;
|
package com.openhis.web.doctorstation.dto;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
import com.openhis.common.annotation.Dict;
|
import com.openhis.common.annotation.Dict;
|
||||||
@@ -127,4 +128,9 @@ public class PatientInfoDto {
|
|||||||
* 就诊卡号
|
* 就诊卡号
|
||||||
*/
|
*/
|
||||||
private String identifierNo;
|
private String identifierNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过号时间
|
||||||
|
*/
|
||||||
|
private Date missedTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,29 @@
|
|||||||
<if test="updateTime != null">, #{updateTime}</if>
|
<if test="updateTime != null">, #{updateTime}</if>
|
||||||
)
|
)
|
||||||
</insert>
|
</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>
|
</mapper>
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,7 @@
|
|||||||
ON T1.context_enum = #{activity}
|
ON T1.context_enum = #{activity}
|
||||||
AND T1.product_id = T2.id
|
AND T1.product_id = T2.id
|
||||||
AND T2.delete_flag = '0'
|
AND T2.delete_flag = '0'
|
||||||
|
AND T2.status_enum = 1
|
||||||
LEFT JOIN med_medication_definition AS T3
|
LEFT JOIN med_medication_definition AS T3
|
||||||
ON T1.context_enum = #{medication}
|
ON T1.context_enum = #{medication}
|
||||||
AND T1.product_id = T3.id
|
AND T1.product_id = T3.id
|
||||||
@@ -205,6 +206,7 @@
|
|||||||
ON T1.context_enum = #{activity}
|
ON T1.context_enum = #{activity}
|
||||||
AND T1.product_id = T2.id
|
AND T1.product_id = T2.id
|
||||||
AND T2.delete_flag = '0'
|
AND T2.delete_flag = '0'
|
||||||
|
AND T2.status_enum = 1
|
||||||
LEFT JOIN med_medication_definition AS T3
|
LEFT JOIN med_medication_definition AS T3
|
||||||
ON T1.context_enum = #{medication}
|
ON T1.context_enum = #{medication}
|
||||||
AND T1.product_id = T3.id
|
AND T1.product_id = T3.id
|
||||||
|
|||||||
@@ -227,8 +227,11 @@
|
|||||||
AND T2.instance_table = #{activityTableName}
|
AND T2.instance_table = #{activityTableName}
|
||||||
LEFT JOIN adm_organization_location AS T3 ON T3.activity_definition_id = T1.ID
|
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)
|
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'
|
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)
|
AND (T1.pricing_flag = #{pricingFlag} OR T1.pricing_flag IS NULL)
|
||||||
</if>
|
</if>
|
||||||
<if test="adviceDefinitionIdParamList != null and !adviceDefinitionIdParamList.isEmpty()">
|
<if test="adviceDefinitionIdParamList != null and !adviceDefinitionIdParamList.isEmpty()">
|
||||||
|
|||||||
@@ -24,7 +24,8 @@
|
|||||||
T10.practitioner_user_id,
|
T10.practitioner_user_id,
|
||||||
T10.jz_practitioner_user_id,
|
T10.jz_practitioner_user_id,
|
||||||
T10.bus_no,
|
T10.bus_no,
|
||||||
T10.identifier_no
|
T10.identifier_no,
|
||||||
|
T10.missed_time
|
||||||
from
|
from
|
||||||
(
|
(
|
||||||
SELECT T1.tenant_id AS tenant_id,
|
SELECT T1.tenant_id AS tenant_id,
|
||||||
@@ -50,7 +51,8 @@
|
|||||||
T1.reception_time AS reception_time,
|
T1.reception_time AS reception_time,
|
||||||
T1.organization_id AS org_id,
|
T1.organization_id AS org_id,
|
||||||
T8.bus_no AS bus_no,
|
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
|
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_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 adm_healthcare_service AS T3 ON T1.service_type_id = T3.ID AND T3.delete_flag = '0'
|
||||||
@@ -70,18 +72,18 @@
|
|||||||
LEFT JOIN fin_contract AS T7 ON T6.contract_no = T7.bus_no AND T7.delete_flag = '0'
|
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 adm_patient AS T8 ON T1.patient_id = T8.ID AND T8.delete_flag = '0'
|
||||||
LEFT JOIN (
|
LEFT JOIN (
|
||||||
SELECT patient_id,
|
SELECT patient_id,
|
||||||
identifier_no
|
identifier_no
|
||||||
FROM (
|
FROM (
|
||||||
SELECT patient_id,
|
SELECT patient_id,
|
||||||
identifier_no,
|
identifier_no,
|
||||||
ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY create_time ASC) AS rn
|
ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY create_time ASC) AS rn
|
||||||
FROM adm_patient_identifier
|
FROM adm_patient_identifier
|
||||||
WHERE delete_flag = '0'
|
WHERE delete_flag = '0'
|
||||||
AND identifier_no IS NOT NULL
|
AND identifier_no IS NOT NULL
|
||||||
AND identifier_no != ''
|
AND identifier_no != ''
|
||||||
) t
|
) t
|
||||||
WHERE rn = 1
|
WHERE rn = 1
|
||||||
) AS T9 ON T8.id = T9.patient_id
|
) AS T9 ON T8.id = T9.patient_id
|
||||||
WHERE
|
WHERE
|
||||||
T1.delete_flag = '0'
|
T1.delete_flag = '0'
|
||||||
|
|||||||
@@ -117,10 +117,15 @@ public class DictAspect {
|
|||||||
|
|
||||||
private String queryDictLabel(String dictTable, String dictCode, String dictText, String dictValue) {
|
private String queryDictLabel(String dictTable, String dictCode, String dictText, String dictValue) {
|
||||||
if (!StringUtils.hasText(dictTable)) {
|
if (!StringUtils.hasText(dictTable)) {
|
||||||
// 场景 1:默认字典走DictUtils缓存
|
// 场景 1:默认字典走DictUtils缓存(dictTable 为空时)
|
||||||
return DictUtils.getDictLabel(dictCode, dictValue);
|
return DictUtils.getDictLabel(dictCode, dictValue);
|
||||||
} else {
|
} 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);
|
String sql = String.format("SELECT %s FROM %s WHERE %s::varchar = ? LIMIT 1", dictText, dictTable, dictCode);
|
||||||
try {
|
try {
|
||||||
return jdbcTemplate.queryForObject(sql, String.class, dictValue);
|
return jdbcTemplate.queryForObject(sql, String.class, dictValue);
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package com.openhis.administration.domain;
|
package com.openhis.administration.domain;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
|
||||||
import com.core.common.core.domain.HisBaseEntity;
|
import com.core.common.core.domain.HisBaseEntity;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
@@ -145,4 +143,9 @@ public class Encounter extends HisBaseEntity {
|
|||||||
*/
|
*/
|
||||||
private Long registrarId;
|
private Long registrarId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过号时间
|
||||||
|
*/
|
||||||
|
@TableField("missed_time")
|
||||||
|
private Date missedTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.openhis.administration.domain;
|
package com.openhis.administration.domain;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import com.core.common.core.domain.HisBaseEntity;
|
import com.core.common.core.domain.HisBaseEntity;
|
||||||
@@ -43,4 +44,9 @@ public class EncounterParticipant extends HisBaseEntity {
|
|||||||
|
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
private Integer statusEnum;
|
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)
|
* 自定义插入方法,排除id字段(数据库GENERATED ALWAYS)
|
||||||
*/
|
*/
|
||||||
int insertWithoutId(DoctorSchedule doctorSchedule);
|
int insertWithoutId(DoctorSchedule doctorSchedule);
|
||||||
|
/**
|
||||||
|
* 自定义更新方法
|
||||||
|
*/
|
||||||
|
int updateDoctorSchedule(DoctorSchedule doctorSchedule);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public interface OrderMapper extends BaseMapper<Order> {
|
|||||||
|
|
||||||
int countOrders(Map<String, Object> params);
|
int countOrders(Map<String, Object> params);
|
||||||
|
|
||||||
Order selectOrderBySlotId(Long slotId);
|
List<Order> selectOrderBySlotId(Long slotId);
|
||||||
|
|
||||||
int updateOrderStatusById(Long id, Integer status);
|
int updateOrderStatusById(Long id, Integer status);
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ public interface IOrderService extends IService<Order> {
|
|||||||
|
|
||||||
int countOrders(Map<String, Object> params);
|
int countOrders(Map<String, Object> params);
|
||||||
|
|
||||||
Order selectOrderBySlotId(Long slotId);
|
List<Order> selectOrderBySlotId(Long slotId);
|
||||||
|
|
||||||
int updateOrderStatusById(Long id, Integer status);
|
int updateOrderStatusById(Long id, Integer status);
|
||||||
|
|
||||||
int updateOrderCancelInfoById(Long id, java.util.Date cancelTime, String cancelReason);
|
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.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.core.common.utils.AssignSeqUtil;
|
import com.core.common.utils.AssignSeqUtil;
|
||||||
import com.core.common.utils.SecurityUtils;
|
|
||||||
import com.openhis.clinical.domain.Order;
|
import com.openhis.clinical.domain.Order;
|
||||||
import com.openhis.clinical.domain.Ticket;
|
import com.openhis.clinical.domain.Ticket;
|
||||||
import com.openhis.clinical.mapper.OrderMapper;
|
import com.openhis.clinical.mapper.OrderMapper;
|
||||||
@@ -71,7 +70,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Order selectOrderBySlotId(Long slotId) {
|
public List<Order> selectOrderBySlotId(Long slotId) {
|
||||||
return orderMapper.selectOrderBySlotId(slotId);
|
return orderMapper.selectOrderBySlotId(slotId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -197,8 +197,8 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
|||||||
throw new RuntimeException("号源不可取消预约");
|
throw new RuntimeException("号源不可取消预约");
|
||||||
}
|
}
|
||||||
|
|
||||||
Order order = orderService.selectOrderBySlotId(ticketId);
|
List<Order> orders = orderService.selectOrderBySlotId(ticketId);
|
||||||
if (order != null) {
|
for(Order order:orders){
|
||||||
orderService.cancelAppointmentOrder(order.getId(), "患者取消预约");
|
orderService.cancelAppointmentOrder(order.getId(), "患者取消预约");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,8 +249,8 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否存在相关订单,如果存在则取消
|
// 检查是否存在相关订单,如果存在则取消
|
||||||
Order order = orderService.selectOrderBySlotId(ticketId);
|
List<Order> orders = orderService.selectOrderBySlotId(ticketId);
|
||||||
if (order != null) {
|
for(Order order:orders){
|
||||||
orderService.cancelAppointmentOrder(order.getId(), "医生停诊");
|
orderService.cancelAppointmentOrder(order.getId(), "医生停诊");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,6 +119,8 @@
|
|||||||
|
|
||||||
<select id="selectOrderById" resultMap="OrderResult">
|
<select id="selectOrderById" resultMap="OrderResult">
|
||||||
select * from order_main where id = #{id}
|
select * from order_main where id = #{id}
|
||||||
|
and status = 1
|
||||||
|
order by create_time desc
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectOrderBySlotId" resultMap="OrderResult">
|
<select id="selectOrderBySlotId" resultMap="OrderResult">
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
|
||||||
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 文件
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
|||||||
<div id="topSearchArea" class="top-search-area">
|
<div id="topSearchArea" class="top-search-area">
|
||||||
<!-- 搜索标签行 -->
|
<!-- 搜索标签行 -->
|
||||||
<div class="search-labels">
|
<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>
|
<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">
|
<select id="status-select" class="search-select" v-model="selectedStatus">
|
||||||
<option value="all">全部</option>
|
<option value="all">全部</option>
|
||||||
<option value="unbooked">未预约</option>
|
<option value="unbooked">未预约</option>
|
||||||
<option value="locked">锁定</option>
|
<option value="booked">已预约</option>
|
||||||
<option value="booked">待缴费</option>
|
|
||||||
<option value="checked">已取号</option>
|
<option value="checked">已取号</option>
|
||||||
<option value="cancelled">已停诊</option>
|
<option value="cancelled">已停诊</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -105,38 +104,34 @@
|
|||||||
<!-- 使用网格布局的号源卡片列表 -->
|
<!-- 使用网格布局的号源卡片列表 -->
|
||||||
<div class="ticket-grid" v-if="filteredTickets.length > 0">
|
<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 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-index">{{ index + 1 }}</div>
|
||||||
<div class="ticket-type-badge" :class="item.ticketType === 'general' ? 'type-general' : 'type-expert'">
|
<!-- 1.时间 -->
|
||||||
{{ item.ticketType === 'general' ? '普通' : '专家' }}
|
<div class="ticket-id-time">{{ item.dateTime }}</div>
|
||||||
</div>
|
<!-- 2. 状态标签 -->
|
||||||
</div>
|
|
||||||
<div class="ticket-time">{{ item.dateTime }}</div>
|
|
||||||
<div class="ticket-doctor">{{ item.doctor }}</div>
|
|
||||||
<div class="ticket-status" :class="`status-${item.status}`">
|
<div class="ticket-status" :class="`status-${item.status}`">
|
||||||
<span class="status-dot"></span>
|
<span class="status-dot"></span>
|
||||||
{{ item.status }}
|
{{ item.status }}
|
||||||
</div>
|
</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 v-if="(item.status === '已预约' || item.status === '已取号') && item.patientName" class="ticket-patient">
|
||||||
<div class="patient-name">{{ item.patientName }}</div>
|
{{ item.patientName }}({{ item.patientId }})
|
||||||
<div class="patient-card">{{ item.patientId }}</div>
|
</div>
|
||||||
<div class="patient-gender">性别:{{ item.patientGender || '-' }}</div>
|
<!-- 7. 患者电话号码 -->
|
||||||
|
<div v-if="(item.status === '已预约' || item.status === '已取号') && item.phone" class="ticket-phone">
|
||||||
|
电话号码: {{ item.phone }}
|
||||||
</div>
|
</div>
|
||||||
<div class="ticket-actions">
|
<div class="ticket-actions">
|
||||||
<button class="action-button book-button" @click="openPatientSelectModal(item.slot_id)" :disabled="item.status !== '未预约'" :class="{ 'disabled': item.status !== '未预约' }">
|
<button class="action-button book-button" @click="openPatientSelectModal(item.slot_id)" :disabled="item.status !== '未预约'" :class="{ 'disabled': item.status !== '未预约' }">
|
||||||
<i class="el-icon-tickets"></i>
|
<i class="el-icon-tickets"></i>
|
||||||
预约
|
预约
|
||||||
</button>
|
</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>
|
</div>
|
||||||
<div v-if="hasMore" class="loading-more">
|
<div v-if="hasMore" class="loading-more">
|
||||||
@@ -166,7 +161,7 @@
|
|||||||
<input id="phone" class="search-input" placeholder="手机号" v-model="patientSearchParams.phone" @input="onPatientSearchInput">
|
<input id="phone" class="search-input" placeholder="手机号" v-model="patientSearchParams.phone" @input="onPatientSearchInput">
|
||||||
<button id="patient-search-btn" class="search-btn" @click="searchPatients">查询</button>
|
<button id="patient-search-btn" class="search-btn" @click="searchPatients">查询</button>
|
||||||
<div class="patient-table-container">
|
<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>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>序号</th>
|
<th>序号</th>
|
||||||
@@ -192,6 +187,11 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<!-- 无搜索结果提示 -->
|
||||||
|
<div v-else-if="hasSearchCriteria && !isLoading" class="no-results">
|
||||||
|
未找到符合条件的患者,请检查搜索条件
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
@@ -231,13 +231,14 @@ import { listTicket, bookTicket, cancelTicket, checkInTicket, cancelConsultation
|
|||||||
import { getPatientList } from '@/api/cardRenewal/api';
|
import { getPatientList } from '@/api/cardRenewal/api';
|
||||||
import { listDept } from '@/api/appoinmentmanage/dept';
|
import { listDept } from '@/api/appoinmentmanage/dept';
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { ElDatePicker } from 'element-plus'
|
import { ElDatePicker, ElPagination, ElMessageBox, ElMessage } from 'element-plus'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'OutpatientAppointment',
|
name: 'OutpatientAppointment',
|
||||||
components: {
|
components: {
|
||||||
ElDatePicker
|
ElDatePicker,
|
||||||
|
ElPagination
|
||||||
},
|
},
|
||||||
|
|
||||||
// 添加全局点击事件监听器,用于关闭右键菜单
|
// 添加全局点击事件监听器,用于关闭右键菜单
|
||||||
@@ -324,35 +325,34 @@ export default {
|
|||||||
filteredTickets() {
|
filteredTickets() {
|
||||||
let filtered = [...this.tickets];
|
let filtered = [...this.tickets];
|
||||||
|
|
||||||
// 按日期筛选(如果选择了日期) - 暂时注释掉以排查问题
|
//按日期筛选(如果选择了日期)
|
||||||
// if (this.selectedDate) {
|
if (this.selectedDate) {
|
||||||
// filtered = filtered.filter(ticket => {
|
filtered = filtered.filter(ticket => {
|
||||||
// // 使用后端返回的dateTime字段进行筛选
|
// 使用后端返回的dateTime字段进行筛选
|
||||||
// if (ticket.dateTime) {
|
if (ticket.dateTime) {
|
||||||
// return ticket.dateTime.startsWith(this.selectedDate);
|
return ticket.dateTime.startsWith(this.selectedDate);
|
||||||
// }
|
}
|
||||||
// return true;
|
return true;
|
||||||
// });
|
});
|
||||||
// }
|
}
|
||||||
|
|
||||||
// 按状态筛选 - 暂时注释掉以排查问题
|
//按状态筛选
|
||||||
// if (this.selectedStatus !== 'all') {
|
if (this.selectedStatus !== 'all') {
|
||||||
// // 状态映射表(后端返回中文状态,前端使用英文状态)
|
// 状态映射表(后端返回中文状态,前端使用英文状态)
|
||||||
// const statusMap = {
|
const statusMap = {
|
||||||
// 'unbooked': '未预约',
|
'unbooked': '未预约',
|
||||||
// 'locked': '已锁定',
|
'booked': '已预约',
|
||||||
// 'booked': '已预约',
|
'checked': '已取号',
|
||||||
// 'checked': '已取号',
|
'cancelled': '已停诊'
|
||||||
// 'cancelled': '已取消'
|
};
|
||||||
// };
|
const chineseStatus = statusMap[this.selectedStatus];
|
||||||
// const chineseStatus = statusMap[this.selectedStatus];
|
filtered = filtered.filter(ticket => ticket.status === chineseStatus);
|
||||||
// filtered = filtered.filter(ticket => ticket.status === chineseStatus);
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// 按科室筛选 - 暂时注释掉以排查问题
|
// 按科室筛选
|
||||||
// if (this.selectedDepartment !== 'all') {
|
if (this.selectedDepartment !== 'all') {
|
||||||
// filtered = filtered.filter(ticket => ticket.department === this.selectedDepartment);
|
filtered = filtered.filter(ticket => ticket.department === this.selectedDepartment);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// 按号源类型筛选
|
// 按号源类型筛选
|
||||||
filtered = filtered.filter(ticket => {
|
filtered = filtered.filter(ticket => {
|
||||||
@@ -365,62 +365,67 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 按医生筛选 - 暂时注释掉以排查问题
|
// 按医生筛选 - 暂时注释掉以排查问题
|
||||||
// if (this.selectedDoctorId) {
|
if (this.selectedDoctorId) {
|
||||||
// const selectedDoctor = this.doctors.find(d => d.id === this.selectedDoctorId);
|
const selectedDoctor = this.doctors.find(d => d.id === this.selectedDoctorId);
|
||||||
// if (selectedDoctor) {
|
if (selectedDoctor) {
|
||||||
// filtered = filtered.filter(ticket => ticket.doctor === selectedDoctor.name);
|
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;
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
return filtered;
|
return filtered;
|
||||||
},
|
},
|
||||||
|
hasSearchCriteria() {
|
||||||
|
return Object.values(this.patientSearchParams).some(value => value && value.trim() !== '');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
selectDoctor(doctorId) {
|
selectDoctor(doctorId) {
|
||||||
this.selectedDoctorId = doctorId
|
this.selectedDoctorId = doctorId
|
||||||
console.log('Selected doctor:', doctorId)
|
|
||||||
// 医生选择后,筛选号源
|
// 医生选择后,筛选号源
|
||||||
this.onSearch();
|
this.onSearch();
|
||||||
},
|
},
|
||||||
onTypeChange() {
|
onTypeChange() {
|
||||||
console.log('Type changed:', this.selectedType)
|
|
||||||
// 移除onSearch()调用,让计算属性自动处理类型筛选
|
// 移除onSearch()调用,让计算属性自动处理类型筛选
|
||||||
},
|
},
|
||||||
onDepartmentChange() {
|
onDepartmentChange() {
|
||||||
console.log('Department changed:', this.selectedDepartment)
|
|
||||||
this.onSearch()
|
this.onSearch()
|
||||||
},
|
},
|
||||||
onDoctorSearch() {
|
onDoctorSearch() {
|
||||||
console.log('Searching doctor:', this.searchQuery)
|
|
||||||
// 使用计算属性filteredDoctors实现实时搜索,无需调用API
|
// 使用计算属性filteredDoctors实现实时搜索,无需调用API
|
||||||
},
|
},
|
||||||
openPatientSelectModal(ticketId) {
|
openPatientSelectModal(ticketId) {
|
||||||
console.log('openPatientSelectModal called with ticketId:', ticketId);
|
|
||||||
this.currentTicket = this.tickets.find(ticket => ticket.slot_id === ticketId);
|
this.currentTicket = this.tickets.find(ticket => ticket.slot_id === ticketId);
|
||||||
console.log('Found ticket for modal:', this.currentTicket);
|
// 重置搜索参数
|
||||||
// 打开患者选择弹窗时,查询患者列表
|
this.patientSearchParams = {
|
||||||
this.searchPatients();
|
patientName: '',
|
||||||
|
medicalCard: '',
|
||||||
|
idCard: '',
|
||||||
|
phone: ''
|
||||||
|
};
|
||||||
|
// 清空患者列表
|
||||||
|
this.patients = [];
|
||||||
this.showPatientModal = true;
|
this.showPatientModal = true;
|
||||||
console.log('Show patient modal:', this.showPatientModal);
|
// 默认加载所有患者
|
||||||
|
this.searchPatients();
|
||||||
},
|
},
|
||||||
|
|
||||||
// 患者搜索
|
// 患者搜索
|
||||||
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获取患者列表
|
// 调用真实API获取患者列表
|
||||||
getPatientList(this.patientSearchParams).then(response => {
|
getPatientList(this.patientSearchParams).then(response => {
|
||||||
console.log('Patient API response:', response);
|
|
||||||
// 尝试不同的数据结构解析
|
// 尝试不同的数据结构解析
|
||||||
let records = [];
|
let records = [];
|
||||||
if (response.data && response.data.records) {
|
if (response.data && response.data.records) {
|
||||||
@@ -431,31 +436,40 @@ export default {
|
|||||||
records = response;
|
records = response;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.patients = records;
|
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字段,如果没有,尝试使用其他唯一标识
|
// 验证每个患者是否有idCard字段,如果没有,尝试使用其他唯一标识
|
||||||
this.patients.forEach((patient, index) => {
|
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,尝试设置一个临时唯一标识
|
// 如果没有idCard,尝试设置一个临时唯一标识
|
||||||
if (!patient.idCard) {
|
if (!patient.idCard) {
|
||||||
patient.idCard = patient.id || patient.medicalCard || `temp_${index}`;
|
patient.idCard = patient.id || patient.medicalCard || `temp_${index}`;
|
||||||
console.log(`Set temporary idCard for patient ${index}:`, patient.idCard);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.patients.length === 0) {
|
if (this.patients.length === 0) {
|
||||||
console.log('No patients found in response');
|
ElMessage.error('没有找到患者数据,请检查搜索条件或联系管理员');
|
||||||
alert('没有找到患者数据,请检查搜索条件或联系管理员');
|
}
|
||||||
}
|
// 清除加载状态
|
||||||
|
this.isLoading = false;
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error('获取患者列表失败:', error);
|
|
||||||
this.patients = [];
|
this.patients = [];
|
||||||
alert('获取患者列表失败:' + (error.message || '未知错误'));
|
ElMessage.error('获取患者列表失败:' + (error.message || '未知错误'));
|
||||||
|
// 清除加载状态
|
||||||
|
this.isLoading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -483,18 +497,12 @@ export default {
|
|||||||
|
|
||||||
// 双击未预约卡片触发患者选择流程
|
// 双击未预约卡片触发患者选择流程
|
||||||
handleDoubleClick(ticket) {
|
handleDoubleClick(ticket) {
|
||||||
console.log('handleDoubleClick called with ticket:', ticket);
|
|
||||||
if (ticket.status === '未预约') {
|
if (ticket.status === '未预约') {
|
||||||
this.currentTicket = ticket;
|
this.currentTicket = ticket;
|
||||||
console.log('Set currentTicket:', this.currentTicket);
|
|
||||||
this.selectedPatientId = null;
|
this.selectedPatientId = null;
|
||||||
this.selectedPatient = null;
|
this.selectedPatient = null;
|
||||||
console.log('Reset selected patient info:', { selectedPatientId: this.selectedPatientId, selectedPatient: this.selectedPatient });
|
|
||||||
|
|
||||||
// 先打开弹窗,再加载患者数据,避免等待
|
// 先打开弹窗,再加载患者数据,避免等待
|
||||||
this.showPatientModal = true;
|
this.showPatientModal = true;
|
||||||
console.log('Show patient modal after double click:', this.showPatientModal);
|
|
||||||
|
|
||||||
// 调用患者搜索接口,加载患者列表
|
// 调用患者搜索接口,加载患者列表
|
||||||
this.searchPatients();
|
this.searchPatients();
|
||||||
}
|
}
|
||||||
@@ -518,88 +526,66 @@ export default {
|
|||||||
// 确认取消预约
|
// 确认取消预约
|
||||||
confirmCancelAppointment() {
|
confirmCancelAppointment() {
|
||||||
if (this.selectedTicketForCancel) {
|
if (this.selectedTicketForCancel) {
|
||||||
// 二次确认,显示患者姓名
|
// 使用 ElMessageBox.confirm 进行二次确认,显示患者姓名
|
||||||
if (confirm(`确认取消患者${this.selectedTicketForCancel.patientName || ''}的预约?`)) {
|
ElMessageBox.confirm(
|
||||||
// 调用API取消预约
|
`确认取消患者${this.selectedTicketForCancel.patientName || ''}的预约?`,
|
||||||
|
'系统提示',
|
||||||
|
{
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
// 用户点击确定,调用API取消预约
|
||||||
this.cancelAppointment(this.selectedTicketForCancel);
|
this.cancelAppointment(this.selectedTicketForCancel);
|
||||||
}
|
}).catch(() => {
|
||||||
this.closeContextMenu();
|
// 用户取消操作
|
||||||
|
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调用
|
// 取消预约API调用
|
||||||
cancelAppointment(ticket) {
|
cancelAppointment(ticket) {
|
||||||
if (!ticket || !ticket.slot_id) {
|
if (!ticket || !ticket.slot_id) {
|
||||||
console.error('取消预约失败:缺少号源信息');
|
ElMessage.error('取消预约失败:缺少号源信息');
|
||||||
alert('取消预约失败:缺少号源信息');
|
|
||||||
this.closeContextMenu();
|
this.closeContextMenu();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用真实API调用取消预约
|
// 使用真实API调用取消预约,传递slot_id
|
||||||
cancelTicket(ticket.slot_id).then(response => {
|
cancelTicket(ticket.slot_id).then(response => {
|
||||||
// API调用成功后,更新当前卡片状态
|
// 根据后端返回判断是否成功
|
||||||
const ticketIndex = this.tickets.findIndex(t => t.slot_id === ticket.slot_id);
|
if (response.code === 200 || response.msg === '取消成功' || response.message === '取消成功') {
|
||||||
if (ticketIndex !== -1) {
|
console.log('取消预约成功,更新前端状态');
|
||||||
// 清除该号源关联的所有患者信息
|
|
||||||
this.tickets[ticketIndex].status = '未预约';
|
// API调用成功后,更新当前卡片状态
|
||||||
this.tickets[ticketIndex].patientName = null;
|
const ticketIndex = this.tickets.findIndex(t => t.slot_id === ticket.slot_id);
|
||||||
this.tickets[ticketIndex].patientId = null;
|
if (ticketIndex !== -1) {
|
||||||
this.tickets[ticketIndex].patientGender = null;
|
// 清除该号源关联的所有患者信息
|
||||||
this.tickets[ticketIndex].medicalCard = null;
|
this.tickets[ticketIndex].status = '未预约';
|
||||||
this.tickets[ticketIndex].phone = null;
|
this.tickets[ticketIndex].patientName = null;
|
||||||
|
this.tickets[ticketIndex].patientId = null;
|
||||||
|
this.tickets[ticketIndex].patientGender = null;
|
||||||
|
this.tickets[ticketIndex].medicalCard = null;
|
||||||
|
this.tickets[ticketIndex].phone = null;
|
||||||
|
|
||||||
|
// 更新医生号源列表中的余号数量
|
||||||
|
this.updateDoctorsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭上下文菜单
|
||||||
|
this.closeContextMenu();
|
||||||
|
ElMessage.success('预约已取消,号源已释放');
|
||||||
|
} else {
|
||||||
|
// 取消失败
|
||||||
|
const errorMsg = response.msg || response.message || '取消预约失败';
|
||||||
|
ElMessage.error(`取消预约失败:${errorMsg}`);
|
||||||
|
this.closeContextMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭上下文菜单
|
|
||||||
this.closeContextMenu();
|
|
||||||
alert('预约已取消,号源已释放');
|
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error('取消预约失败:', error);
|
const errorMsg = error.message || '取消预约失败,请稍后重试';
|
||||||
alert('取消预约失败,请稍后重试。');
|
ElMessage.error(`取消预约失败:${errorMsg}`);
|
||||||
this.closeContextMenu();
|
this.closeContextMenu();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -608,63 +594,58 @@ export default {
|
|||||||
this.selectedPatientId = null
|
this.selectedPatientId = null
|
||||||
},
|
},
|
||||||
selectPatient(patientId) {
|
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) {
|
if (this.patients.length === 0) {
|
||||||
console.error('No patients data available');
|
ElMessage.error('患者数据未加载,请先搜索患者');
|
||||||
alert('患者数据未加载,请先搜索患者');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selectedPatientId = patientId;
|
this.selectedPatientId = patientId;
|
||||||
console.log('Set selectedPatientId:', this.selectedPatientId);
|
|
||||||
|
|
||||||
// 保存选择的患者对象 - 使用idCard作为唯一标识,但增加容错性
|
// 保存选择的患者对象 - 使用idCard作为唯一标识,但增加容错性
|
||||||
this.selectedPatient = this.patients.find(patient => {
|
this.selectedPatient = this.patients.find(patient => {
|
||||||
// 尝试多种匹配方式
|
// 尝试多种匹配方式
|
||||||
const matchByIdCard = patient.idCard === patientId;
|
const matchByIdCard = patient.idCard === patientId;
|
||||||
const matchById = patient.id === patientId;
|
const matchById = patient.id === patientId;
|
||||||
const matchByMedicalCard = patient.medicalCard === patientId;
|
const matchByMedicalCard = patient.medicalCard === patientId;
|
||||||
const match = matchByIdCard || matchById || matchByMedicalCard;
|
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;
|
return match;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Selected patient:', this.selectedPatient);
|
|
||||||
console.log('Selected patientId:', this.selectedPatientId);
|
|
||||||
|
|
||||||
if (this.selectedPatient) {
|
if (this.selectedPatient) {
|
||||||
// 添加视觉反馈,验证患者是否被选中
|
// 使用 ElMessageBox.confirm 进行二次确认
|
||||||
alert('已选择患者: ' + this.selectedPatient.name);
|
ElMessageBox.confirm(
|
||||||
|
`确认选择患者 ${this.selectedPatient.name} 进行预约?`,
|
||||||
// 可以考虑自动滚动到确认按钮,提高用户体验
|
'患者确认',
|
||||||
const confirmBtn = document.querySelector('.confirm-btn');
|
{
|
||||||
if (confirmBtn) {
|
confirmButtonText: '确定',
|
||||||
confirmBtn.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
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 {
|
} else {
|
||||||
console.error('Patient not found with id:', patientId);
|
ElMessage.error('未找到该患者,请重新选择');
|
||||||
alert('未找到该患者,请重新选择');
|
|
||||||
this.selectedPatientId = null;
|
this.selectedPatientId = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
confirmPatientSelection() {
|
confirmPatientSelection() {
|
||||||
if (!this.selectedPatientId || !this.selectedPatient) {
|
if (!this.selectedPatientId || !this.selectedPatient) {
|
||||||
alert('请选择患者');
|
ElMessage.error('请选择患者');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.currentTicket) {
|
if (!this.currentTicket) {
|
||||||
alert('预约信息错误,请重新选择号源');
|
ElMessage.error('预约信息错误,请重新选择号源');
|
||||||
this.closePatientSelectModal();
|
this.closePatientSelectModal();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -672,6 +653,7 @@ export default {
|
|||||||
try {
|
try {
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const appointmentData = {
|
const appointmentData = {
|
||||||
|
slotId: this.currentTicket.slot_id, // 添加号源ID
|
||||||
ticketId: Number(this.currentTicket.slot_id),
|
ticketId: Number(this.currentTicket.slot_id),
|
||||||
patientId: this.selectedPatientId, // 使用身份证号作为患者ID,不需要转换为数字
|
patientId: this.selectedPatientId, // 使用身份证号作为患者ID,不需要转换为数字
|
||||||
patientName: this.selectedPatient.name,
|
patientName: this.selectedPatient.name,
|
||||||
@@ -685,39 +667,41 @@ export default {
|
|||||||
|
|
||||||
// 验证必填字段
|
// 验证必填字段
|
||||||
if (!appointmentData.ticketId || isNaN(appointmentData.ticketId)) {
|
if (!appointmentData.ticketId || isNaN(appointmentData.ticketId)) {
|
||||||
alert('号源ID无效');
|
ElMessage.error('号源ID无效');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!appointmentData.patientId) {
|
if (!appointmentData.patientId) {
|
||||||
alert('患者ID无效');
|
ElMessage.error('患者ID无效');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 调用真实API进行预约
|
||||||
console.log('Sending appointment data to backend:', appointmentData);
|
bookTicket(appointmentData).then(response => {
|
||||||
bookTicket(appointmentData).then(response => {
|
|
||||||
const ticketIndex = this.tickets.findIndex(t => t.slot_id === this.currentTicket.slot_id);
|
const ticketIndex = this.tickets.findIndex(t => t.slot_id === this.currentTicket.slot_id);
|
||||||
if (ticketIndex !== -1) {
|
if (ticketIndex !== -1) {
|
||||||
this.tickets[ticketIndex].status = '已预约';
|
this.tickets[ticketIndex].status = '已预约';
|
||||||
this.tickets[ticketIndex].patientName = this.selectedPatient.name;
|
this.tickets[ticketIndex].patientName = this.selectedPatient.name;
|
||||||
this.tickets[ticketIndex].patientId = this.selectedPatient.medicalCard;
|
this.tickets[ticketIndex].patientId = this.selectedPatient.medicalCard;
|
||||||
this.tickets[ticketIndex].patientGender = this.selectedPatient.gender;
|
this.tickets[ticketIndex].patientGender = this.selectedPatient.gender;
|
||||||
|
|
||||||
|
// 更新医生号源列表中的余号数量
|
||||||
|
this.updateDoctorsList();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.closePatientSelectModal();
|
this.closePatientSelectModal();
|
||||||
alert('预约成功,号源已锁定。患者到院签到时需缴费取号。');
|
|
||||||
|
// 重新加载号源数据,确保显示最新状态
|
||||||
|
this.onSearch();
|
||||||
|
|
||||||
|
ElMessage.success('预约成功,号源已锁定。患者到院签到时需缴费取号。');
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error('预约失败:', error);
|
ElMessage.error('预约失败,请稍后重试。');
|
||||||
alert('预约失败,请稍后重试。');
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('预约数据处理错误:', error);
|
ElMessage.error('预约信息格式错误,请重新操作。');
|
||||||
alert('预约信息格式错误,请重新操作。');
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDateChange() {
|
onDateChange() {
|
||||||
// 日期变化时刷新号源列表
|
|
||||||
console.log('Date changed:', this.selectedDate)
|
|
||||||
// 重置页码并重新加载数据
|
// 重置页码并重新加载数据
|
||||||
this.currentPage = 1;
|
this.currentPage = 1;
|
||||||
this.onSearch()
|
this.onSearch()
|
||||||
@@ -731,16 +715,7 @@ export default {
|
|||||||
this.isMobile = window.innerWidth <= 768;
|
this.isMobile = window.innerWidth <= 768;
|
||||||
},
|
},
|
||||||
onSearch() {
|
onSearch() {
|
||||||
this.isLoading = true
|
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获取号源数据
|
// 调用真实API获取号源数据
|
||||||
const queryParams = {
|
const queryParams = {
|
||||||
date: this.selectedDate,
|
date: this.selectedDate,
|
||||||
@@ -749,7 +724,7 @@ export default {
|
|||||||
name: this.patientName,
|
name: this.patientName,
|
||||||
card: this.patientCard,
|
card: this.patientCard,
|
||||||
phone: this.patientPhone,
|
phone: this.patientPhone,
|
||||||
page: 1,
|
page: this.currentPage,
|
||||||
limit: this.pageSize
|
limit: this.pageSize
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -757,10 +732,7 @@ export default {
|
|||||||
Promise.all([
|
Promise.all([
|
||||||
listAllTickets(), // 使用测试接口获取固定的5条专家号数据
|
listAllTickets(), // 使用测试接口获取固定的5条专家号数据
|
||||||
listDept()
|
listDept()
|
||||||
]).then(([ticketResponse, deptResponse]) => {
|
]).then(([ticketResponse, deptResponse]) => {
|
||||||
// 打印完整的响应数据以便调试
|
|
||||||
console.log('完整的号源响应数据:', ticketResponse);
|
|
||||||
|
|
||||||
// 处理号源数据
|
// 处理号源数据
|
||||||
if (ticketResponse) {
|
if (ticketResponse) {
|
||||||
// 检查是否有data.list字段(后端返回的数据结构是{code, data:{list, total}})
|
// 检查是否有data.list字段(后端返回的数据结构是{code, data:{list, total}})
|
||||||
@@ -775,6 +747,10 @@ export default {
|
|||||||
|
|
||||||
// 保存所有原始数据
|
// 保存所有原始数据
|
||||||
this.allTickets = [...this.tickets];
|
this.allTickets = [...this.tickets];
|
||||||
|
|
||||||
|
// 根据患者信息进行前端筛选
|
||||||
|
this.filterTicketsByPatientInfo();
|
||||||
|
|
||||||
// 更新医生列表
|
// 更新医生列表
|
||||||
this.updateDoctorsList();
|
this.updateDoctorsList();
|
||||||
this.hasMore = this.tickets.length < this.totalTickets;
|
this.hasMore = this.tickets.length < this.totalTickets;
|
||||||
@@ -786,8 +762,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 处理科室数据
|
// 处理科室数据
|
||||||
this.updateDepartmentsListFromApi(deptResponse);
|
this.updateDepartmentsListFromApi(deptResponse);
|
||||||
|
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error('获取数据失败:', error);
|
console.error('获取数据失败:', error);
|
||||||
@@ -798,9 +773,7 @@ export default {
|
|||||||
this.departments = [{ value: 'all', label: '全部科室' }];
|
this.departments = [{ value: 'all', label: '全部科室' }];
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
// 从号源数据中更新科室列表(备用方法)
|
// 从号源数据中更新科室列表(备用方法)
|
||||||
updateDepartmentsList() {
|
updateDepartmentsList() {
|
||||||
const departmentSet = new Set();
|
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) {
|
getStatusText(status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
@@ -901,10 +893,7 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 转换为数组
|
// 转换为数组
|
||||||
this.doctors = Array.from(doctorMap.values());
|
this.doctors = Array.from(doctorMap.values());
|
||||||
|
|
||||||
// 打印医生列表以便调试
|
|
||||||
console.log('生成的医生列表:', this.doctors);
|
|
||||||
},
|
},
|
||||||
// 加载更多数据
|
// 加载更多数据
|
||||||
loadMore() {
|
loadMore() {
|
||||||
@@ -926,10 +915,7 @@ export default {
|
|||||||
limit: this.pageSize
|
limit: this.pageSize
|
||||||
};
|
};
|
||||||
|
|
||||||
listTicket(queryParams).then(response => {
|
listTicket(queryParams).then(response => {
|
||||||
// 打印完整的响应数据以便调试
|
|
||||||
console.log('加载更多的号源响应数据:', response);
|
|
||||||
|
|
||||||
// 处理号源数据
|
// 处理号源数据
|
||||||
if (response) {
|
if (response) {
|
||||||
let newRecords = [];
|
let newRecords = [];
|
||||||
@@ -1461,6 +1447,15 @@ export default {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ticket-index {
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
left: 12px;
|
||||||
|
color: #1890ff;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.ticket-time {
|
.ticket-time {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #333;
|
color: #333;
|
||||||
@@ -1514,6 +1509,13 @@ export default {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ticket-id-time {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.ticket-fee {
|
.ticket-fee {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #333;
|
color: #333;
|
||||||
@@ -1547,50 +1549,6 @@ export default {
|
|||||||
color: #91d5ff;
|
color: #91d5ff;
|
||||||
cursor: not-allowed;
|
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 {
|
.ticket-patient {
|
||||||
@@ -1603,6 +1561,18 @@ export default {
|
|||||||
color: #333;
|
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) {
|
@media (max-width: 768px) {
|
||||||
.ticket-grid {
|
.ticket-grid {
|
||||||
@@ -1618,6 +1588,14 @@ export default {
|
|||||||
.ticket-doctor {
|
.ticket-doctor {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ticket-id-time {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket-index {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 弹窗样式 */
|
/* 弹窗样式 */
|
||||||
@@ -1803,6 +1781,54 @@ export default {
|
|||||||
box-shadow: none;
|
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 {
|
.context-menu {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -1842,4 +1868,60 @@ export default {
|
|||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
cursor: not-allowed;
|
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>
|
</style>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -183,6 +183,23 @@
|
|||||||
prop="statusEnum_enumText"
|
prop="statusEnum_enumText"
|
||||||
:show-overflow-tooltip="true"
|
: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
|
<el-table-column
|
||||||
label="操作"
|
label="操作"
|
||||||
align="center"
|
align="center"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
row-key="patientId"
|
row-key="patientId"
|
||||||
@cell-click="clickRow"
|
@cell-click="clickRow"
|
||||||
|
@row-click="clickRow"
|
||||||
>
|
>
|
||||||
<el-table-column label="名称" align="center" prop="adviceName" />
|
<el-table-column label="名称" align="center" prop="adviceName" />
|
||||||
<el-table-column label="类型" align="center" prop="activityType_enumText" />
|
<el-table-column label="类型" align="center" prop="activityType_enumText" />
|
||||||
@@ -38,6 +39,10 @@ const props = defineProps({
|
|||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
popoverVisible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const emit = defineEmits(['selectAdviceBase']);
|
const emit = defineEmits(['selectAdviceBase']);
|
||||||
const total = ref(0);
|
const total = ref(0);
|
||||||
@@ -61,30 +66,80 @@ const throttledGetList = throttle(
|
|||||||
watch(
|
watch(
|
||||||
() => props.adviceQueryParams,
|
() => props.adviceQueryParams,
|
||||||
(newValue) => {
|
(newValue) => {
|
||||||
queryParams.value.searchKey = newValue.searchKey;
|
// 只有在弹窗打开时才响应 adviceQueryParams 的变化,避免选择项目后弹窗关闭时触发不必要的请求
|
||||||
queryParams.value.adviceType = newValue.adviceType;
|
if (!props.popoverVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
queryParams.value.searchKey = newValue?.searchKey;
|
||||||
|
queryParams.value.adviceType = newValue?.adviceType;
|
||||||
throttledGetList();
|
throttledGetList();
|
||||||
},
|
},
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
getList();
|
// 监听弹窗打开状态,当弹窗打开时主动加载数据
|
||||||
|
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() {
|
function getList() {
|
||||||
// 验证是否已选择患者
|
// 验证是否已选择患者
|
||||||
if (!props.patientInfo || Object.keys(props.patientInfo).length === 0) {
|
if (!props.patientInfo || Object.keys(props.patientInfo).length === 0) {
|
||||||
|
console.log('[adviceBaseList] getList() 跳过:未选择患者');
|
||||||
return; // 不执行API调用
|
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;
|
queryParams.value.organizationId = props.patientInfo.orgId;
|
||||||
|
console.log('[adviceBaseList] getList() 请求参数:', JSON.stringify(queryParams.value));
|
||||||
|
|
||||||
getAdviceBaseInfo(queryParams.value).then((res) => {
|
getAdviceBaseInfo(queryParams.value).then((res) => {
|
||||||
adviceBaseList.value = res.data.records;
|
console.log('[adviceBaseList] getList() 响应数据:', {
|
||||||
total.value = res.data.total;
|
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(() => {
|
nextTick(() => {
|
||||||
currentIndex.value = 0;
|
currentIndex.value = 0;
|
||||||
if (adviceBaseList.value.length > 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,8 +191,12 @@ const handleCurrentChange = (currentRow) => {
|
|||||||
currentSelectRow.value = currentRow;
|
currentSelectRow.value = currentRow;
|
||||||
};
|
};
|
||||||
|
|
||||||
function clickRow(row) {
|
function clickRow(row, column, cell, event) {
|
||||||
emit('selectAdviceBase', row);
|
// cell-click 事件会传递 row, column, cell, event 四个参数
|
||||||
|
// 确保传递的是完整的行数据
|
||||||
|
if (row) {
|
||||||
|
emit('selectAdviceBase', row);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
|||||||
@@ -301,6 +301,7 @@ const nextId = ref(1);
|
|||||||
const unitCodeList = ref([]);
|
const unitCodeList = ref([]);
|
||||||
const adviceTableRef = ref([]);
|
const adviceTableRef = ref([]);
|
||||||
const organization = ref([]);
|
const organization = ref([]);
|
||||||
|
const orgTreeLoaded = ref(false);
|
||||||
const rowRules = ref({
|
const rowRules = ref({
|
||||||
conditionDefinitionId: [{ required: true, message: '请选择诊断', trigger: 'change' }],
|
conditionDefinitionId: [{ required: true, message: '请选择诊断', trigger: 'change' }],
|
||||||
dose: [{ required: true, message: '请输入单次剂量', trigger: 'change' }],
|
dose: [{ required: true, message: '请输入单次剂量', trigger: 'change' }],
|
||||||
@@ -449,6 +450,10 @@ function handleDiagnosisChange(item, row) {
|
|||||||
|
|
||||||
function handleFocus(row, index) {
|
function handleFocus(row, index) {
|
||||||
rowIndex.value = index;
|
rowIndex.value = index;
|
||||||
|
// 打开当前行弹窗前,先关闭其它行,避免多个弹窗同时存在
|
||||||
|
prescriptionList.value.forEach((r, i) => {
|
||||||
|
if (i !== index) r.showPopover = false;
|
||||||
|
});
|
||||||
// 如果当前行已选择adviceType,同步到adviceQueryParams
|
// 如果当前行已选择adviceType,同步到adviceQueryParams
|
||||||
if (row.adviceType !== undefined) {
|
if (row.adviceType !== undefined) {
|
||||||
adviceQueryParams.value.adviceType = row.adviceType;
|
adviceQueryParams.value.adviceType = row.adviceType;
|
||||||
@@ -457,7 +462,9 @@ function handleFocus(row, index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleBlur(row) {
|
function handleBlur(row) {
|
||||||
row.showPopover = false;
|
// 不能在 input blur 时立刻关闭弹窗:
|
||||||
|
// 点击弹窗里的表格会先触发 blur,导致弹窗瞬间关闭,从而“点了项目没反应”
|
||||||
|
// 弹窗关闭交给 selectAdviceBase()(选中后关闭)以及 handleFocus()(切行时关闭其他行)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleChange(value) {
|
function handleChange(value) {
|
||||||
@@ -465,10 +472,37 @@ function handleChange(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 选择药品回调
|
* 选择药品/诊疗项目回调
|
||||||
|
* 这里恢复为之前“能正常工作”的简单逻辑,只做最小必要的修正
|
||||||
*/
|
*/
|
||||||
function selectAdviceBase(key, row) {
|
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 = [];
|
||||||
unitCodeList.value.push({ value: row.unitCode, label: row.unitCode_dictText, type: 'unit' });
|
unitCodeList.value.push({ value: row.unitCode, label: row.unitCode_dictText, type: 'unit' });
|
||||||
if (row.doseUnitCode != row.minUnitCode) {
|
if (row.doseUnitCode != row.minUnitCode) {
|
||||||
@@ -488,10 +522,14 @@ function selectAdviceBase(key, row) {
|
|||||||
type: 'minUnit',
|
type: 'minUnit',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将选中的基础项“覆盖”到当前处方行(这是之前正常工作的核心逻辑)
|
||||||
prescriptionList.value[rowIndex.value] = {
|
prescriptionList.value[rowIndex.value] = {
|
||||||
...prescriptionList.value[rowIndex.value],
|
...prescriptionList.value[rowIndex.value],
|
||||||
...JSON.parse(JSON.stringify(row)),
|
...JSON.parse(JSON.stringify(row)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 后续字段处理保持原样
|
||||||
prescriptionList.value[rowIndex.value].orgId = undefined;
|
prescriptionList.value[rowIndex.value].orgId = undefined;
|
||||||
prescriptionList.value[rowIndex.value].dose = undefined;
|
prescriptionList.value[rowIndex.value].dose = undefined;
|
||||||
prescriptionList.value[rowIndex.value].unitCodeList = unitCodeList.value;
|
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].minUnitCode = JSON.parse(JSON.stringify(row.doseUnitCode));
|
||||||
prescriptionList.value[rowIndex.value].unitCode =
|
prescriptionList.value[rowIndex.value].unitCode =
|
||||||
row.partAttributeEnum == 1 ? row.minUnitCode : row.unitCode;
|
row.partAttributeEnum == 1 ? row.minUnitCode : row.unitCode;
|
||||||
// prescriptionList.value[rowIndex.value].doseUnitCode_dictText = row.minUnitCode_dictText;
|
|
||||||
prescriptionList.value[rowIndex.value].definitionId = JSON.parse(
|
prescriptionList.value[rowIndex.value].definitionId = JSON.parse(
|
||||||
JSON.stringify(row)
|
JSON.stringify(row)
|
||||||
).chargeItemDefinitionId;
|
).chargeItemDefinitionId;
|
||||||
|
|
||||||
// 库存列表 + 价格列表拼成批次号的下拉框
|
// 库存列表 + 价格列表拼成批次号的下拉框(非诊疗)
|
||||||
if (row.adviceType != 3) {
|
if (row.adviceType != 3) {
|
||||||
if (row.inventoryList && row.inventoryList.length == 0) {
|
if (row.inventoryList && row.inventoryList.length == 0) {
|
||||||
expandOrder.value = [];
|
expandOrder.value = [];
|
||||||
@@ -532,9 +569,15 @@ function selectAdviceBase(key, row) {
|
|||||||
prescriptionList.value[rowIndex.value].positionName = stock.locationName;
|
prescriptionList.value[rowIndex.value].positionName = stock.locationName;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// 诊疗:设置执行科室和价格
|
||||||
prescriptionList.value[rowIndex.value].orgId = JSON.parse(JSON.stringify(row)).positionId;
|
prescriptionList.value[rowIndex.value].orgId = JSON.parse(JSON.stringify(row)).positionId;
|
||||||
prescriptionList.value[rowIndex.value].unitPrice = row.priceList[0].price;
|
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];
|
expandOrder.value = [key];
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (row.adviceType == 1) {
|
if (row.adviceType == 1) {
|
||||||
@@ -549,11 +592,18 @@ function selectAdviceBase(key, row) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOrgList() {
|
function ensureOrgTreeLoaded() {
|
||||||
getOrgTree().then((res) => {
|
if (orgTreeLoaded.value) return;
|
||||||
organization.value = res.data.records;
|
orgTreeLoaded.value = true;
|
||||||
console.log(organization.value,"organization.value")
|
getOrgTree()
|
||||||
});
|
.then((res) => {
|
||||||
|
// 组织机构树接口通常返回分页 records,这里做兼容兜底
|
||||||
|
organization.value = res?.data?.records ?? res?.data ?? [];
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// 加载失败时允许重试
|
||||||
|
orgTreeLoaded.value = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDelete() {
|
function handleDelete() {
|
||||||
@@ -706,6 +756,8 @@ function handleSaveSign(row, index) {
|
|||||||
row.contentJson = JSON.stringify(row);
|
row.contentJson = JSON.stringify(row);
|
||||||
row.dbOpType = row.requestId ? '2' : '1';
|
row.dbOpType = row.requestId ? '2' : '1';
|
||||||
row.minUnitQuantity = row.quantity * row.partPercent;
|
row.minUnitQuantity = row.quantity * row.partPercent;
|
||||||
|
row.categoryEnum = row.adviceType
|
||||||
|
console.log('row', row)
|
||||||
savePrescription({ adviceSaveList: [row] }).then((res) => {
|
savePrescription({ adviceSaveList: [row] }).then((res) => {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
proxy.$modal.msgSuccess('保存成功');
|
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>
|
||||||
@@ -419,40 +419,10 @@ import {ElMessage, ElMessageBox} from 'element-plus'
|
|||||||
import {deleteInspectionApplication, getInspectionApplicationList, saveInspectionApplication} from '../api'
|
import {deleteInspectionApplication, getInspectionApplicationList, saveInspectionApplication} from '../api'
|
||||||
import useUserStore from '@/store/modules/user.js'
|
import useUserStore from '@/store/modules/user.js'
|
||||||
import {storeToRefs} from 'pinia'
|
import {storeToRefs} from 'pinia'
|
||||||
import {listType} from "@/api/system/dict/type.js";
|
|
||||||
|
|
||||||
|
|
||||||
// const getCurrentDeptCode = async () => {
|
|
||||||
// // try {
|
|
||||||
// const res = await listType({ dictType: 'dept' }); // 按字典类型查询科室
|
|
||||||
// console.log('【检验】获取科室字典数据:', res.data)
|
|
||||||
// deptOptions.value = res.data;
|
|
||||||
// console.log('【检验】科室字典数据:',deptOptions.value)
|
|
||||||
|
|
||||||
// // 获取当前科室名称(来自患者信息)
|
|
||||||
// const currentDeptName = props.patientInfo?.organizationName;
|
|
||||||
// console.log('【检验】当前科室名称:', currentDeptName)
|
|
||||||
//
|
|
||||||
// // 根据当前科室名称查找对应的编码
|
|
||||||
// const deptItem = res.data.find(item => item.dictLabel === currentDeptName);
|
|
||||||
// console.log('【检验】找到的科室名称:', deptItem)
|
|
||||||
// if (deptItem) {
|
|
||||||
// return deptItem.dictValue; // 返回科室编码
|
|
||||||
// } else {
|
|
||||||
// console.warn(`未找到科室名称为 ${currentDeptName} 的对应编码`);
|
|
||||||
// return currentDeptName || ''; // 使用原始名称作为默认值,符合防御性编程原则
|
|
||||||
// }
|
|
||||||
// } catch (error) {
|
|
||||||
// console.error('获取科室字典数据失败:', error);
|
|
||||||
// return '';
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// 在 onMounted 中调用初始化函数
|
// 在 onMounted 中调用初始化函数
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await initData();
|
await initData();
|
||||||
// 设置当前科室编码
|
|
||||||
// const currentDeptCode = await getCurrentDeptCode();
|
|
||||||
// formData.applyDeptCode = currentDeptCode || '';
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
@@ -474,7 +444,6 @@ const showForm = ref(false)
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
const leftActiveTab = ref('application')
|
const leftActiveTab = ref('application')
|
||||||
const deptOptions = ref([])
|
|
||||||
|
|
||||||
// 用户信息store
|
// 用户信息store
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div style="display: flex; justify-content: space-between; height: 90vh">
|
<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="width: 15%; height: 100%; border: 1px solid #eee; border-right: 0">
|
||||||
<div style="padding: 10px; border: 1px solid #eee; height: 50px; border-right: 0">
|
<div
|
||||||
<span>现诊患者</span>
|
style="padding: 10px; border: 1px solid #eee; height: 50px; border-right: 0; display: flex; align-items: center; justify-content: space-between">
|
||||||
<el-badge :value="waitCount > 0 ? waitCount : ''" :max="10"
|
<div style="display: flex; align-items: center;">
|
||||||
style="float: right; color: #409eff; cursor: pointer; margin-right: 10px">
|
<span style="margin-right: 20px; font-weight: 600;">现诊患者</span>
|
||||||
<span @click="openDrawer"> 患者队列 </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>
|
</el-badge>
|
||||||
</div>
|
</div>
|
||||||
<div style="width: 100%; padding: 10px">
|
<div style="width: 100%; padding: 10px">
|
||||||
@@ -15,16 +20,9 @@
|
|||||||
<el-button icon="Search" @click="getPatientList" />
|
<el-button icon="Search" @click="getPatientList" />
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
<el-date-picker
|
<el-date-picker v-model="registerTime" @change="handleTimeChange" type="date"
|
||||||
v-model="registerTime"
|
style="width: 100%; margin-bottom: 10px" :clearable="false" placeholder="挂号时间" format="YYYY-MM-DD"
|
||||||
@change="handleTimeChange"
|
value-format="YYYY-MM-DD" />
|
||||||
type="date"
|
|
||||||
style="width: 100%; margin-bottom: 10px"
|
|
||||||
:clearable="false"
|
|
||||||
placeholder="挂号时间"
|
|
||||||
format="YYYY-MM-DD"
|
|
||||||
value-format="YYYY-MM-DD"
|
|
||||||
/>
|
|
||||||
<el-scrollbar height="700px">
|
<el-scrollbar height="700px">
|
||||||
<div v-for="(item, index) in patientList" :class="item.active ? 'patient-card actived' : 'patient-card'"
|
<div v-for="(item, index) in patientList" :class="item.active ? 'patient-card actived' : 'patient-card'"
|
||||||
:key="item.id" @click="handleCardClick(item, index)">
|
:key="item.id" @click="handleCardClick(item, index)">
|
||||||
@@ -86,11 +84,11 @@
|
|||||||
' / ' +
|
' / ' +
|
||||||
patientInfo.genderEnum_enumText +
|
patientInfo.genderEnum_enumText +
|
||||||
' / ' +
|
' / ' +
|
||||||
(patientInfo?.contractName ? patientInfo.contractName : '') +
|
(patientInfo?.contractName ? patientInfo.contractName : '') +
|
||||||
'/' +
|
'/' +
|
||||||
patientInfo.phone +
|
patientInfo.phone +
|
||||||
'/' +
|
'/' +
|
||||||
patientInfo.busNo
|
patientInfo.busNo
|
||||||
: '-'
|
: '-'
|
||||||
}}
|
}}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
@@ -110,27 +108,22 @@
|
|||||||
<el-button type="primary" plain @click.stop="handleRefund(patientInfo.encounterId)">
|
<el-button type="primary" plain @click.stop="handleRefund(patientInfo.encounterId)">
|
||||||
退费
|
退费
|
||||||
</el-button>
|
</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>
|
||||||
<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-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</div>
|
</div>
|
||||||
<div style="padding: 10px; position: relative">
|
<div style="padding: 10px; position: relative">
|
||||||
<el-tabs
|
<el-tabs type="card" style="width: 100%; height: 100%" v-loading="loading" v-model="activeTab"
|
||||||
type="card"
|
@tab-change="handleClick(activeTab)">
|
||||||
style="width: 100%; height: 100%"
|
|
||||||
v-loading="loading"
|
|
||||||
v-model="activeTab"
|
|
||||||
@tab-change="handleClick(activeTab)"
|
|
||||||
>
|
|
||||||
<el-tab-pane label="门诊病历" name="hospitalizationEmr">
|
<el-tab-pane label="门诊病历" name="hospitalizationEmr">
|
||||||
<hospitalizationEmr
|
<hospitalizationEmr :patientInfo="patientInfo" :activeTab="activeTab" @emrSaved="handleEmrSaved" />
|
||||||
:patientInfo="patientInfo"
|
|
||||||
:activeTab="activeTab"
|
|
||||||
@emrSaved="handleEmrSaved"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<!-- <el-tab-pane label="病历" name="emr">
|
<!-- <el-tab-pane label="病历" name="emr">
|
||||||
<Emr
|
<Emr
|
||||||
@@ -151,12 +144,8 @@
|
|||||||
" />
|
" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="医嘱" name="prescription">
|
<el-tab-pane label="医嘱" name="prescription">
|
||||||
<prescriptionlist
|
<prescriptionlist :patientInfo="patientInfo" ref="prescriptionRef" :activeTab="activeTab"
|
||||||
:patientInfo="patientInfo"
|
:outpatientEmrSaved="outpatientEmrSaved" />
|
||||||
ref="prescriptionRef"
|
|
||||||
:activeTab="activeTab"
|
|
||||||
:outpatientEmrSaved="outpatientEmrSaved"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="中医" name="tcm">
|
<el-tab-pane label="中医" name="tcm">
|
||||||
<tcmAdvice :patientInfo="patientInfo" ref="tcmRef" />
|
<tcmAdvice :patientInfo="patientInfo" ref="tcmRef" />
|
||||||
@@ -180,24 +169,17 @@
|
|||||||
<el-drawer v-model="drawer" title="患者队列" direction="ltr" @open="handleOpen">
|
<el-drawer v-model="drawer" title="患者队列" direction="ltr" @open="handleOpen">
|
||||||
<PatientList ref="patientDrawerRef" @toCurrent="handleReceive" />
|
<PatientList ref="patientDrawerRef" @toCurrent="handleReceive" />
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
<RefundListDialog
|
<RefundListDialog :open="openRefundListDialog" :encounterId="currentEncounterId"
|
||||||
:open="openRefundListDialog"
|
@close="openRefundListDialog = false" @refresh="() => prescriptionRef.getListInfo()" />
|
||||||
:encounterId="currentEncounterId"
|
<HospitalizationDialog :open="openDialog" :patientInfo="patientInfo" :encounterId="currentEncounterId"
|
||||||
@close="openRefundListDialog = false"
|
:mainDiagnosis="mainDiagnosis" @close="openDialog = false" />
|
||||||
@refresh="() => prescriptionRef.getListInfo()"
|
<PrescriptionInfo :open="openPrescriptionDialog" :precriptionInfo="prescriptionInfo"
|
||||||
/>
|
@close="openPrescriptionDialog = false" />
|
||||||
<HospitalizationDialog
|
<!-- 新增叫号弹窗组件 -->
|
||||||
:open="openDialog"
|
<DoctorCallDialog v-model:dialogVisible="dialogVisible" :current-patient="currentCallPatient"
|
||||||
:patientInfo="patientInfo"
|
:current-patient-list="currentWaitPatientList" :room-no="roomNo"
|
||||||
:encounterId="currentEncounterId"
|
:department="userStore.orgName || patientInfo.organizationName || '心内科'" @callNext="callNext" @reCall="reCall"
|
||||||
:mainDiagnosis="mainDiagnosis"
|
@finish="finishCall" @skip="skip" @requeue="requeue" @markSeen="markSeen" @callThis="callThis" />
|
||||||
@close="openDialog = false"
|
|
||||||
/>
|
|
||||||
<PrescriptionInfo
|
|
||||||
:open="openPrescriptionDialog"
|
|
||||||
:precriptionInfo="prescriptionInfo"
|
|
||||||
@close="openPrescriptionDialog = false"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
@@ -221,11 +203,12 @@ import HospitalizationDialog from './components/hospitalizationDialog.vue';
|
|||||||
import tcmAdvice from './components/tcm/tcmAdvice.vue';
|
import tcmAdvice from './components/tcm/tcmAdvice.vue';
|
||||||
import inspectionApplication from './components/inspection/inspectionApplication.vue';
|
import inspectionApplication from './components/inspection/inspectionApplication.vue';
|
||||||
import surgeryApplication from './components/surgery/surgeryApplication.vue';
|
import surgeryApplication from './components/surgery/surgeryApplication.vue';
|
||||||
import {formatDate, formatDateStr} from '@/utils/index';
|
import DoctorCallDialog from './components/callQueue/DoctorCallDialog.vue';
|
||||||
|
import { formatDate, formatDateStr } from '@/utils/index';
|
||||||
import useUserStore from '@/store/modules/user';
|
import useUserStore from '@/store/modules/user';
|
||||||
import {nextTick} from 'vue';
|
import { nextTick } from 'vue';
|
||||||
import {updatePatientInfo} from './components/store/patient.js';
|
import { updatePatientInfo } from './components/store/patient.js';
|
||||||
import {ElMessage, ElMessageBox} from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
|
|
||||||
// // 监听路由离开事件
|
// // 监听路由离开事件
|
||||||
// onBeforeRouteLeave((to, from, next) => {
|
// onBeforeRouteLeave((to, from, next) => {
|
||||||
@@ -253,6 +236,10 @@ const drawer = ref(false);
|
|||||||
const openRefundListDialog = ref(false);
|
const openRefundListDialog = ref(false);
|
||||||
const openDialog = ref(false);
|
const openDialog = ref(false);
|
||||||
const openPrescriptionDialog = 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 saveStatus = ref(false);
|
||||||
const outpatientEmrSaved = ref(false); // 门诊病历保存状态
|
const outpatientEmrSaved = ref(false); // 门诊病历保存状态
|
||||||
const currentEncounterId = ref('');
|
const currentEncounterId = ref('');
|
||||||
@@ -267,11 +254,11 @@ const visitTypeDisabled = ref(false);
|
|||||||
// 计算属性:确定办理住院按钮是否应被禁用
|
// 计算属性:确定办理住院按钮是否应被禁用
|
||||||
const isHospitalizationButtonDisabled = computed(() => {
|
const isHospitalizationButtonDisabled = computed(() => {
|
||||||
return !patientInfo.value ||
|
return !patientInfo.value ||
|
||||||
typeof patientInfo.value !== 'object' ||
|
typeof patientInfo.value !== 'object' ||
|
||||||
!patientInfo.value.encounterId ||
|
!patientInfo.value.encounterId ||
|
||||||
patientInfo.value.encounterId === '' ||
|
patientInfo.value.encounterId === '' ||
|
||||||
patientInfo.value.encounterId === null ||
|
patientInfo.value.encounterId === null ||
|
||||||
patientInfo.value.encounterId === undefined;
|
patientInfo.value.encounterId === undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
const prescriptionInfo = ref([]);
|
const prescriptionInfo = ref([]);
|
||||||
@@ -327,8 +314,9 @@ const shortcuts = [
|
|||||||
const eprescriptionRef = ref();
|
const eprescriptionRef = ref();
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getWaitPatient();
|
getWaitPatient();
|
||||||
|
getWaitPatientList();
|
||||||
|
getPatientList();
|
||||||
});
|
});
|
||||||
getPatientList();
|
|
||||||
// 获取现诊患者列表
|
// 获取现诊患者列表
|
||||||
function getPatientList() {
|
function getPatientList() {
|
||||||
queryParams.value.statusEnum = 2;
|
queryParams.value.statusEnum = 2;
|
||||||
@@ -405,6 +393,33 @@ function getWaitPatient() {
|
|||||||
waitCount.value = res.data.total;
|
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) {
|
function handleClick(tab) {
|
||||||
switch (tab) {
|
switch (tab) {
|
||||||
@@ -456,10 +471,10 @@ function getEnPrescription(encounterId) {
|
|||||||
type: 'error',
|
type: 'error',
|
||||||
message: '暂无处方单',
|
message: '暂无处方单',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
prescriptionInfo.value = res.data.records;
|
prescriptionInfo.value = res.data.records;
|
||||||
openPrescriptionDialog.value = true;
|
openPrescriptionDialog.value = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -724,6 +791,7 @@ const onHospitalization = async () => {
|
|||||||
:deep(.el-descriptions__label) {
|
:deep(.el-descriptions__label) {
|
||||||
font-size: 16px !important;
|
font-size: 16px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-descriptions__content) {
|
:deep(.el-descriptions__content) {
|
||||||
font-size: 16px !important;
|
font-size: 16px !important;
|
||||||
}
|
}
|
||||||
@@ -906,15 +974,18 @@ const onHospitalization = async () => {
|
|||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100% - 50px);
|
height: calc(100% - 50px);
|
||||||
z-index: 998; /* 降低z-index,避免覆盖按钮 */
|
z-index: 998;
|
||||||
|
/* 降低z-index,避免覆盖按钮 */
|
||||||
/* 确保覆盖在内容上方,但不覆盖顶部按钮区域 */
|
/* 确保覆盖在内容上方,但不覆盖顶部按钮区域 */
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
background-color: rgba(255, 255, 255, 0.01);
|
background-color: rgba(255, 255, 255, 0.01);
|
||||||
pointer-events: none; /* 默认不捕获鼠标事件,只在真正需要禁用时才启用 */
|
pointer-events: none;
|
||||||
|
/* 默认不捕获鼠标事件,只在真正需要禁用时才启用 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.disabled-wrapper .overlay.overlay-disabled {
|
.disabled-wrapper .overlay.overlay-disabled {
|
||||||
pointer-events: auto; /* 当需要真正禁用时启用指针事件 */
|
pointer-events: auto;
|
||||||
|
/* 当需要真正禁用时启用指针事件 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 顶层按钮样式,确保按钮始终在最上层 */
|
/* 顶层按钮样式,确保按钮始终在最上层 */
|
||||||
|
|||||||
@@ -282,3 +282,13 @@ public Long saveEncounterByRegister(Encounter encounter) {
|
|||||||
**记住**:查询条件使用的字段,必须在数据插入/更新时被设置!
|
**记住**:查询条件使用的字段,必须在数据插入/更新时被设置!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user