77 门诊挂号-》预约签到

This commit is contained in:
HuangXinQuan
2026-04-03 14:42:13 +08:00
parent cb46461ede
commit 1b3d4e3dc0
17 changed files with 821 additions and 77 deletions

View File

@@ -16,6 +16,7 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -123,6 +124,7 @@ public class TicketAppServiceImpl implements ITicketAppService {
if (query == null) {
query = new com.openhis.appointmentmanage.dto.TicketQueryDTO();
}
normalizeQueryStatus(query);
// 2. 构造 MyBatis 的分页对象 (传入前端给的当前页和每页条数)
com.baomidou.mybatisplus.extension.plugins.pagination.Page<com.openhis.appointmentmanage.domain.TicketSlotDTO> pageParam = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(
@@ -145,37 +147,61 @@ public class TicketAppServiceImpl implements ITicketAppService {
dto.setDepartment(raw.getDepartmentName()); // 注意以前这里传成了ID导致前端出Bug现在修复成了真正的科室名
dto.setFee(raw.getFee());
dto.setPatientName(raw.getPatientName());
dto.setPatientId(raw.getPatientId() != null ? String.valueOf(raw.getPatientId()) : null);
dto.setPatientId(raw.getMedicalCard());
dto.setPhone(raw.getPhone());
dto.setIdCard(raw.getIdCard());
dto.setDoctorId(raw.getDoctorId());
dto.setDepartmentId(raw.getDepartmentId());
dto.setRealPatientId(raw.getPatientId());
// 性别处理:直接读取优先级最高的订单性别字段 (SQL 已处理优先级)
if (raw.getPatientGender() != null) {
String pg = raw.getPatientGender().trim();
dto.setGender("1".equals(pg) ? "" : ("2".equals(pg) ? "" : "未知"));
} else {
dto.setGender("未知");
}
// 号源类型处理 (底层是1前端要的是expert)
if (raw.getRegType() != null && raw.getRegType() == 1) {
dto.setTicketType("expert");
} else {
dto.setTicketType("general");
}
// 拼接就诊时间
if (raw.getScheduleDate() != null && raw.getExpectTime() != null) {
dto.setDateTime(raw.getScheduleDate().toString() + " " + raw.getExpectTime().toString());
try {
dto.setAppointmentDate(
new java.text.SimpleDateFormat("yyyy-MM-dd").parse(raw.getScheduleDate().toString()));
String timeStr = raw.getAppointmentTime() != null ? raw.getAppointmentTime() : (raw.getScheduleDate().toString() + " " + raw.getExpectTime().toString());
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(timeStr.length() > 10 ? "yyyy-MM-dd HH:mm" : "yyyy-MM-dd");
java.util.Date date = sdf.parse(timeStr);
dto.setAppointmentDate(date);
dto.setAppointmentTime(date);
} catch (Exception e) {
dto.setAppointmentDate(new java.util.Date());
}
}
// 精准状态翻译把底层的1和2翻译回前端能懂的中文
if (Boolean.TRUE.equals(raw.getIsStopped())) {
dto.setStatus("已停诊");
} else {
Integer slotStatus = raw.getSlotStatus();
if (slotStatus != null) {
if (SlotStatus.BOOKED.equals(slotStatus)) {
dto.setStatus(AppointmentOrderStatus.CHECKED_IN.equals(raw.getOrderStatus()) ? "已取号" : "已预约");
} else if (SlotStatus.STOPPED.equals(slotStatus)) {
if (SlotStatus.CHECKED_IN.equals(slotStatus)) {
dto.setStatus("已取号");
} else if (SlotStatus.BOOKED.equals(slotStatus)) {
if (AppointmentOrderStatus.CHECKED_IN.equals(raw.getOrderStatus())) {
dto.setStatus("已取号");
} else if (AppointmentOrderStatus.RETURNED.equals(raw.getOrderStatus())) {
dto.setStatus("已退号");
} else {
dto.setStatus("已预约");
}
} else if (SlotStatus.RETURNED.equals(slotStatus)) {
dto.setStatus("已退号");
} else if (SlotStatus.CANCELLED.equals(slotStatus)) {
dto.setStatus("已停诊");
} else if (SlotStatus.LOCKED.equals(slotStatus)) {
dto.setStatus("已锁定");
} else {
dto.setStatus("未预约");
}
@@ -198,6 +224,62 @@ public class TicketAppServiceImpl implements ITicketAppService {
return R.ok(result);
}
/**
* 统一状态入参,避免前端状态值大小写/中文/数字差异导致 SQL 条件失效后回全量数据
*/
private void normalizeQueryStatus(com.openhis.appointmentmanage.dto.TicketQueryDTO query) {
String rawStatus = query.getStatus();
if (rawStatus == null) {
return;
}
String normalized = rawStatus.trim();
if (normalized.isEmpty()) {
query.setStatus(null);
return;
}
String lower = normalized.toLowerCase(Locale.ROOT);
switch (lower) {
case "all":
case "全部":
query.setStatus("all");
break;
case "unbooked":
case "0":
case "未预约":
query.setStatus("unbooked");
break;
case "booked":
case "1":
case "已预约":
query.setStatus("booked");
break;
case "checked":
case "checkin":
case "checkedin":
case "2":
case "已取号":
query.setStatus("checked");
break;
case "cancelled":
case "canceled":
case "3":
case "已停诊":
case "已取消":
query.setStatus("cancelled");
break;
case "returned":
case "4":
case "5":
case "已退号":
query.setStatus("returned");
break;
default:
// 设置为 impossible 值,配合 mapper 的 otherwise 分支直接返回空
query.setStatus("__invalid__");
break;
}
}
@Override
public R<?> listDoctorAvailability(com.openhis.appointmentmanage.dto.TicketQueryDTO query) {
if (query == null) {
@@ -242,7 +324,7 @@ public class TicketAppServiceImpl implements ITicketAppService {
dto.setDepartment(raw.getDepartmentName());
dto.setFee(raw.getFee());
dto.setPatientName(raw.getPatientName());
dto.setPatientId(raw.getPatientId() != null ? String.valueOf(raw.getPatientId()) : null);
dto.setPatientId(raw.getMedicalCard());
dto.setPhone(raw.getPhone());
// --- 号源类型处理 (普通/专家) ---
@@ -258,9 +340,13 @@ public class TicketAppServiceImpl implements ITicketAppService {
if (raw.getScheduleDate() != null && raw.getExpectTime() != null) {
dto.setDateTime(raw.getScheduleDate().toString() + " " + raw.getExpectTime().toString());
try {
dto.setAppointmentDate(
new java.text.SimpleDateFormat("yyyy-MM-dd").parse(raw.getScheduleDate().toString()));
String timeStr = raw.getAppointmentTime() != null ? raw.getAppointmentTime() : (raw.getScheduleDate().toString() + " " + raw.getExpectTime().toString());
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(timeStr.length() > 10 ? "yyyy-MM-dd HH:mm" : "yyyy-MM-dd");
java.util.Date date = sdf.parse(timeStr);
dto.setAppointmentDate(date);
dto.setAppointmentTime(date);
} catch (Exception e) {
log.error("时间解析失败", e);
dto.setAppointmentDate(new java.util.Date());
}
}
@@ -273,10 +359,22 @@ public class TicketAppServiceImpl implements ITicketAppService {
// 第二关:看独立的细分槽位状态 (0: 可用, 1: 已预约, 2: 已取消...)
Integer slotStatus = raw.getSlotStatus();
if (slotStatus != null) {
if (SlotStatus.BOOKED.equals(slotStatus)) {
dto.setStatus(AppointmentOrderStatus.CHECKED_IN.equals(raw.getOrderStatus()) ? "已取号" : "已预约");
} else if (SlotStatus.STOPPED.equals(slotStatus)) {
dto.setStatus("已停诊"); // 视业务可改回已取消
if (SlotStatus.CHECKED_IN.equals(slotStatus)) {
dto.setStatus("已取号");
} else if (SlotStatus.BOOKED.equals(slotStatus)) {
if (AppointmentOrderStatus.CHECKED_IN.equals(raw.getOrderStatus())) {
dto.setStatus("已取号");
} else if (AppointmentOrderStatus.RETURNED.equals(raw.getOrderStatus())) {
dto.setStatus("已退号");
} else {
dto.setStatus("已预约");
}
} else if (SlotStatus.RETURNED.equals(slotStatus)) {
dto.setStatus("已退号");
} else if (SlotStatus.CANCELLED.equals(slotStatus)) {
dto.setStatus("已停诊");
} else if (SlotStatus.LOCKED.equals(slotStatus)) {
dto.setStatus("已锁定");
} else {
dto.setStatus("未预约");
}
@@ -355,15 +453,12 @@ public class TicketAppServiceImpl implements ITicketAppService {
if (patient != null) {
Integer genderEnum = patient.getGenderEnum();
if (genderEnum != null) {
switch (genderEnum) {
case 1:
dto.setGender("");
break;
case 2:
dto.setGender("");
break;
default:
dto.setGender("未知");
if (Integer.valueOf(1).equals(genderEnum)) {
dto.setGender("");
} else if (Integer.valueOf(2).equals(genderEnum)) {
dto.setGender("");
} else {
dto.setGender("未知");
}
}
}

View File

@@ -99,4 +99,15 @@ public class TicketDto {
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long doctorId;
/**
* 真实患者ID数据库主键区别于 patientId 存的就诊卡号)
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long realPatientId;
/**
* 身份证号
*/
private String idCard;
}

View File

@@ -22,6 +22,10 @@ import com.openhis.common.enums.ybenums.YbPayment;
import com.openhis.common.utils.EnumUtils;
import com.openhis.common.utils.HisPageUtils;
import com.openhis.common.utils.HisQueryUtils;
import com.openhis.appointmentmanage.mapper.SchedulePoolMapper;
import com.openhis.appointmentmanage.mapper.ScheduleSlotMapper;
import com.openhis.clinical.domain.Order;
import com.openhis.clinical.service.IOrderService;
import com.openhis.financial.domain.PaymentReconciliation;
import com.openhis.financial.domain.RefundLog;
import com.openhis.financial.service.IRefundLogService;
@@ -48,6 +52,7 @@ import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
@@ -97,6 +102,15 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
@Resource
IRefundLogService iRefundLogService;
@Resource
IOrderService orderService;
@Resource
ScheduleSlotMapper scheduleSlotMapper;
@Resource
SchedulePoolMapper schedulePoolMapper;
/**
* 门诊挂号 - 查询患者信息
*
@@ -291,6 +305,11 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
}
}
// 如果本次门诊挂号来自预约签到,同步把预约订单与号源槽位状态改为已退号
if (result != null && result.getCode() == 200) {
syncAppointmentReturnStatus(byId, cancelRegPaymentDto.getReason());
}
// 记录退号日志
recordRefundLog(cancelRegPaymentDto, byId, result, paymentRecon);
@@ -399,6 +418,74 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
return R.ok("已取消挂号");
}
/**
* 同步预约号源状态为已退号。
* 说明:
* 1) 门诊退号主流程不依赖该步骤成功与否,因此此方法内部异常仅记录日志,不向上抛出。
* 2) 通过患者、科室、日期以及状态筛选最近一条预约订单,尽量避免误匹配。
*/
private void syncAppointmentReturnStatus(Encounter encounter, String reason) {
if (encounter == null || encounter.getPatientId() == null) {
return;
}
try {
LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<Order>()
.eq(Order::getPatientId, encounter.getPatientId())
.in(Order::getStatus, CommonConstants.AppointmentOrderStatus.BOOKED,
CommonConstants.AppointmentOrderStatus.CHECKED_IN)
.orderByDesc(Order::getUpdateTime)
.orderByDesc(Order::getCreateTime)
.last("LIMIT 1");
if (encounter.getOrganizationId() != null) {
queryWrapper.eq(Order::getDepartmentId, encounter.getOrganizationId());
}
if (encounter.getTenantId() != null) {
queryWrapper.eq(Order::getTenantId, encounter.getTenantId());
}
if (encounter.getCreateTime() != null) {
LocalDate encounterDate = encounter.getCreateTime().toInstant()
.atZone(ZoneId.systemDefault()).toLocalDate();
Date startOfDay = Date.from(encounterDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
Date nextDayStart = Date.from(encounterDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
queryWrapper.ge(Order::getAppointmentDate, startOfDay)
.lt(Order::getAppointmentDate, nextDayStart);
}
Order appointmentOrder = orderService.getOne(queryWrapper, false);
if (appointmentOrder == null) {
return;
}
Date now = new Date();
if (!CommonConstants.AppointmentOrderStatus.RETURNED.equals(appointmentOrder.getStatus())) {
Order updateOrder = new Order();
updateOrder.setId(appointmentOrder.getId());
updateOrder.setStatus(CommonConstants.AppointmentOrderStatus.RETURNED);
updateOrder.setCancelTime(now);
updateOrder.setCancelReason(
StringUtils.isNotEmpty(reason) ? reason : "门诊退号");
updateOrder.setUpdateTime(now);
orderService.updateById(updateOrder);
}
Long slotId = appointmentOrder.getSlotId();
if (slotId == null) {
return;
}
int slotRows = scheduleSlotMapper.updateSlotStatus(slotId, CommonConstants.SlotStatus.RETURNED);
if (slotRows > 0) {
Long poolId = scheduleSlotMapper.selectPoolIdBySlotId(slotId);
if (poolId != null) {
schedulePoolMapper.refreshPoolStats(poolId);
}
}
} catch (Exception e) {
log.warn("同步预约号源已退号状态失败, encounterId={}", encounter.getId(), e);
}
}
/**
* 补打挂号
* 补打挂号不需要修改数据库,只需要返回成功即可,前端已有所有需要的数据用于打印

View File

@@ -769,15 +769,21 @@ public class CommonConstants {
}
/**
* 号源槽位状态 (adm_schedule_slot.slot_status)
* 号源槽位状态 (adm_schedule_slot.status)
*/
public interface SlotStatus {
/** 可用 / 待预约 */
Integer AVAILABLE = 0;
/** 已预约 */
Integer BOOKED = 1;
/** 已停诊 / 已失效 */
Integer STOPPED = 2;
/** 已取消 / 已停诊 */
Integer CANCELLED = 2;
/** 已锁定 */
Integer LOCKED = 3;
/** 已签到 / 已取号 */
Integer CHECKED_IN = 4;
/** 已退号 */
Integer RETURNED = 5;
}
/**
@@ -790,6 +796,8 @@ public class CommonConstants {
Integer CHECKED_IN = 2;
/** 已取消 */
Integer CANCELLED = 3;
/** 已退号 */
Integer RETURNED = 4;
}
}

View File

@@ -9,6 +9,8 @@ import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalTime;
import java.util.Date;
/**
* 号源池明细Entity
*
@@ -29,7 +31,7 @@ public class ScheduleSlot extends HisBaseEntity {
/** 序号 */
private Integer seqNo;
/** 序号状态: 0-可用,1-已预约,2-已取消,3-已过期等 */
/** 序号状态: 0-可用,1-已预约,2-已取消/已停诊,3-已锁定,4-已签到,5-已退号 */
private Integer status;
/** 预约订单ID */
@@ -37,4 +39,7 @@ public class ScheduleSlot extends HisBaseEntity {
/** 预计叫号时间 */
private LocalTime expectTime;
/** 签到时间 */
private Date checkInTime;
}

View File

@@ -22,6 +22,13 @@ public class TicketSlotDTO {
private Long patientId;
private String phone;
private Integer orderStatus;
private Long orderId;
private String orderNo;
private String patientGender;
private Integer genderEnum;
private String idCard;
private String encounterId;
private String appointmentTime;
// 底层逻辑判断专属字段
private Integer slotStatus;

View File

@@ -37,4 +37,21 @@ public interface SchedulePoolMapper extends BaseMapper<SchedulePool> {
AND p.delete_flag = '0'
""")
int refreshPoolStats(@Param("poolId") Long poolId);
/**
* 签到时更新号源池统计:锁定数-1已预约数+1
*
* @param poolId 号源池ID
* @return 结果
*/
@Update("""
UPDATE adm_schedule_pool
SET locked_num = locked_num - 1,
booked_num = booked_num + 1,
update_time = NOW()
WHERE id = #{poolId}
AND locked_num > 0
AND delete_flag = '0'
""")
int updatePoolStatsOnCheckIn(@Param("poolId") Integer poolId);
}

View File

@@ -6,6 +6,7 @@ import com.openhis.appointmentmanage.domain.ScheduleSlot;
import com.openhis.appointmentmanage.domain.TicketSlotDTO;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.openhis.appointmentmanage.dto.TicketQueryDTO;
import java.util.Date;
import java.util.List;
import org.springframework.stereotype.Repository;
import org.apache.ibatis.annotations.Param;
@@ -30,6 +31,16 @@ public interface ScheduleSlotMapper extends BaseMapper<ScheduleSlot> {
*/
int updateSlotStatus(@Param("slotId") Long slotId, @Param("status") Integer status);
/**
* 更新槽位状态并记录签到时间
*
* @param slotId 槽位ID
* @param status 状态
* @param checkInTime 签到时间
* @return 结果
*/
int updateSlotStatusAndCheckInTime(@Param("slotId") Long slotId, @Param("status") Integer status, @Param("checkInTime") Date checkInTime);
/**
* 根据槽位ID查询所属号源池ID。
*/

View File

@@ -20,7 +20,7 @@ import lombok.experimental.Accessors;
@EqualsAndHashCode(callSuper = false)
public class Order extends HisBaseEntity {
@TableId(type = IdType.ASSIGN_ID)
@TableId(type = IdType.AUTO)
@JsonSerialize(using = ToStringSerializer.class)
private Long id;

View File

@@ -32,4 +32,14 @@ public interface OrderMapper extends BaseMapper<Order> {
int updateOrderStatusById(Long id, Integer status);
int updateOrderCancelInfoById(Long id, Date cancelTime, String cancelReason);
/**
* 更新订单支付状态
*
* @param orderId 订单ID
* @param payStatus 支付状态0-未支付1-已支付
* @param payTime 支付时间
* @return 结果
*/
int updatePayStatus(@Param("orderId") Long orderId, @Param("payStatus") Integer payStatus, @Param("payTime") Date payTime);
}

View File

@@ -88,6 +88,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
}
Order order = new Order();
order.setId(null); // 显式置空,确保触发数据库自增,避免 MP 预分配雪花 ID 的干扰
String orderNo = assignSeqUtil.getSeq(AssignSeqEnum.ORDER_NUM.getPrefix(), 18);
order.setOrderNo(orderNo);

View File

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.openhis.appointmentmanage.domain.AppointmentConfig;
import com.openhis.appointmentmanage.service.IAppointmentConfigService;
import com.openhis.appointmentmanage.domain.TicketSlotDTO;
import com.openhis.appointmentmanage.domain.ScheduleSlot;
import com.openhis.appointmentmanage.mapper.SchedulePoolMapper;
import com.openhis.appointmentmanage.mapper.ScheduleSlotMapper;
import com.openhis.clinical.domain.Order;
@@ -52,6 +53,9 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
@Resource
private SchedulePoolMapper schedulePoolMapper;
@Resource
private com.openhis.clinical.mapper.OrderMapper orderMapper;
@Resource
private IAppointmentConfigService appointmentConfigService;
@@ -277,7 +281,7 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
}
/**
* 取号
* 取号(签到)
*
* @param slotId 槽位ID
* @return 结果
@@ -290,7 +294,24 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
throw new RuntimeException("当前号源没有可取号的预约订单");
}
Order latestOrder = orders.get(0);
return orderService.updateOrderStatusById(latestOrder.getId(), AppointmentOrderStatus.CHECKED_IN);
// 1. 更新订单状态为已取号,并更新支付状态和支付时间
orderService.updateOrderStatusById(latestOrder.getId(), AppointmentOrderStatus.CHECKED_IN);
// 更新支付状态为已支付,记录支付时间
orderMapper.updatePayStatus(latestOrder.getId(), 1, new Date());
// 2. 查询号源槽位信息
ScheduleSlot slot = scheduleSlotMapper.selectById(slotId);
// 3. 更新号源槽位状态为已签到,记录签到时间
scheduleSlotMapper.updateSlotStatusAndCheckInTime(slotId, SlotStatus.CHECKED_IN, new Date());
// 4. 更新号源池统计:锁定数-1已预约数+1
if (slot != null && slot.getPoolId() != null) {
schedulePoolMapper.updatePoolStatsOnCheckIn(slot.getPoolId());
}
return 1;
}
/**
@@ -312,7 +333,7 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
orderService.cancelAppointmentOrder(order.getId(), "医生停诊");
}
int updated = scheduleSlotMapper.updateSlotStatus(slotId, SlotStatus.STOPPED);
int updated = scheduleSlotMapper.updateSlotStatus(slotId, SlotStatus.CANCELLED);
if (updated > 0) {
refreshPoolStatsBySlotId(slotId);
}

View File

@@ -4,6 +4,41 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.openhis.appointmentmanage.mapper.ScheduleSlotMapper">
<!-- 统一状态值(兼容数字/英文字符串存储),输出 Integer避免 resultType 映射 NumberFormatException -->
<sql id="slotStatusNormExpr">
CASE
WHEN LOWER(CONCAT('', s.status)) IN ('0', 'unbooked', 'available') THEN 0
WHEN LOWER(CONCAT('', s.status)) IN ('1', 'booked') THEN 1
WHEN LOWER(CONCAT('', s.status)) IN ('2', 'cancelled', 'canceled', 'stopped') THEN 2
WHEN LOWER(CONCAT('', s.status)) IN ('3', 'locked') THEN 3
WHEN LOWER(CONCAT('', s.status)) IN ('4', 'checked', 'checked_in', 'checkin') THEN 4
WHEN LOWER(CONCAT('', s.status)) IN ('5', 'returned') THEN 5
ELSE NULL
END
</sql>
<sql id="orderStatusNormExpr">
CASE
WHEN LOWER(CONCAT('', o.status)) IN ('1', 'booked') THEN 1
WHEN LOWER(CONCAT('', o.status)) IN ('2', 'checked', 'checked_in', 'checkin') THEN 2
WHEN LOWER(CONCAT('', o.status)) IN ('3', 'cancelled', 'canceled') THEN 3
WHEN LOWER(CONCAT('', o.status)) IN ('4', 'returned') THEN 4
ELSE NULL
END
</sql>
<sql id="poolStatusNormExpr">
CASE
WHEN LOWER(CONCAT('', p.status)) IN ('0', 'unbooked', 'available') THEN 0
WHEN LOWER(CONCAT('', p.status)) IN ('1', 'booked') THEN 1
WHEN LOWER(CONCAT('', p.status)) IN ('2', 'cancelled', 'canceled', 'stopped') THEN 2
WHEN LOWER(CONCAT('', p.status)) IN ('3', 'locked') THEN 3
WHEN LOWER(CONCAT('', p.status)) IN ('4', 'checked', 'checked_in', 'checkin') THEN 4
WHEN LOWER(CONCAT('', p.status)) IN ('5', 'returned') THEN 5
ELSE NULL
END
</sql>
<!-- 注意这里的 resultType 指向了您刚建好的 DTO 实体类 -->
<select id="selectAllTicketSlots" resultType="com.openhis.appointmentmanage.domain.TicketSlotDTO">
SELECT
@@ -16,12 +51,12 @@
o.patient_name AS patientName,
o.medical_card AS medicalCard,
o.phone AS phone,
o.status AS orderStatus,
s.status AS slotStatus,
<include refid="orderStatusNormExpr" /> AS orderStatus,
<include refid="slotStatusNormExpr" /> AS slotStatus,
s.expect_time AS expectTime,
p.schedule_date AS scheduleDate,
d.reg_type AS regType,
p.status AS poolStatus,
<include refid="poolStatusNormExpr" /> AS poolStatus,
p.stop_reason AS stopReason,
d.is_stopped AS isStopped
FROM
@@ -39,7 +74,7 @@
FROM
order_main
WHERE
status IN (1, 2)
LOWER(CONCAT('', status)) IN ('1', '2', '4', 'booked', 'checked', 'checked_in', 'checkin', 'returned')
ORDER BY
slot_id,
create_time DESC
@@ -66,12 +101,18 @@
o.patient_name AS patientName,
o.medical_card AS medicalCard,
o.phone AS phone,
o.status AS orderStatus,
s.status AS slotStatus,
o.id AS orderId,
o.order_no AS orderNo,
COALESCE(CAST(o.gender AS VARCHAR), CAST(pinfo.gender_enum AS VARCHAR)) AS patientGender,
pinfo.gender_enum AS genderEnum,
pinfo.id_card AS idCard,
o.appointment_time AS appointmentTime,
<include refid="orderStatusNormExpr" /> AS orderStatus,
<include refid="slotStatusNormExpr" /> AS slotStatus,
s.expect_time AS expectTime,
p.schedule_date AS scheduleDate,
d.reg_type AS regType,
p.status AS poolStatus,
<include refid="poolStatusNormExpr" /> AS poolStatus,
p.stop_reason AS stopReason,
d.is_stopped AS isStopped
FROM
@@ -87,15 +128,20 @@
patient_name,
medical_card,
phone,
id,
order_no,
gender,
appointment_time,
status
FROM
order_main
WHERE
status IN (1, 2)
LOWER(CONCAT('', status)) IN ('1', '2', '4', 'booked', 'checked', 'checked_in', 'checkin', 'returned')
ORDER BY
slot_id,
create_time DESC
) o ON o.slot_id = s.id
LEFT JOIN adm_patient pinfo ON o.patient_id = pinfo.id
WHERE
s.id = #{id}
</select>
@@ -115,7 +161,7 @@
UPDATE adm_schedule_slot
SET
status = #{status},
<if test="status == 0">
<if test="status != null and '0'.equals(status.toString())">
order_id = NULL,
</if>
update_time = now()
@@ -124,11 +170,25 @@
AND delete_flag = '0'
</update>
<update id="updateSlotStatusAndCheckInTime">
UPDATE adm_schedule_slot
SET
status = #{status},
check_in_time = #{checkInTime},
update_time = NOW()
WHERE
id = #{slotId}
AND delete_flag = '0'
</update>
<select id="selectPoolIdBySlotId" resultType="java.lang.Long">
SELECT pool_id
FROM adm_schedule_slot
WHERE id = #{slotId}
AND delete_flag = '0'
SELECT
pool_id
FROM
adm_schedule_slot
WHERE
id = #{slotId}
AND delete_flag = '0'
</select>
<update id="bindOrderToSlot">
@@ -155,12 +215,18 @@
o.patient_name AS patientName,
o.medical_card AS medicalCard,
o.phone AS phone,
o.status AS orderStatus,
s.status AS slotStatus,
o.id AS orderId,
o.order_no AS orderNo,
COALESCE(CAST(o.gender AS VARCHAR), CAST(pinfo.gender_enum AS VARCHAR)) AS patientGender,
pinfo.gender_enum AS genderEnum,
pinfo.id_card AS idCard,
o.appointment_time AS appointmentTime,
<include refid="orderStatusNormExpr" /> AS orderStatus,
<include refid="slotStatusNormExpr" /> AS slotStatus,
s.expect_time AS expectTime,
p.schedule_date AS scheduleDate,
d.reg_type AS regType,
p.status AS poolStatus,
<include refid="poolStatusNormExpr" /> AS poolStatus,
p.stop_reason AS stopReason,
d.is_stopped AS isStopped
FROM
@@ -176,15 +242,20 @@
patient_name,
medical_card,
phone,
id,
order_no,
gender,
appointment_time,
status
FROM
order_main
WHERE
status IN (1, 2)
LOWER(CONCAT('', status)) IN ('1', '2', '4', 'booked', 'checked', 'checked_in', 'checkin', 'returned')
ORDER BY
slot_id,
create_time DESC
) o ON o.slot_id = s.id
LEFT JOIN adm_patient pinfo ON o.patient_id = pinfo.id
<where>
p.delete_flag = '0'
AND s.delete_flag = '0' <!-- 1. 按日期查 -->
@@ -225,35 +296,49 @@
<!-- 5. 核心:解答您疑问的 4 种业务状态的复合查询! -->
<if test="query.status != null and query.status != '' and query.status != 'all'">
<choose>
<when test="query.status == 'unbooked'">
AND s.status = 0
<when test="'unbooked'.equals(query.status) or '未预约'.equals(query.status)">
AND <include refid="slotStatusNormExpr" /> = 0
AND (
d.is_stopped IS NULL
OR d.is_stopped = FALSE
)
</when>
<when test="query.status == 'booked'">
AND s.status = 1
AND o.status = 1
<when test="'booked'.equals(query.status) or '已预约'.equals(query.status)">
AND <include refid="slotStatusNormExpr" /> = 1
AND <include refid="orderStatusNormExpr" /> = 1
AND (
d.is_stopped IS NULL
OR d.is_stopped = FALSE
)
</when>
<when test="query.status == 'checked'">
AND s.status = 1
AND o.status = 2
<when test="'checked'.equals(query.status) or '已取号'.equals(query.status)">
AND (
<include refid="slotStatusNormExpr" /> = 4
OR (
<include refid="slotStatusNormExpr" /> = 1
AND <include refid="orderStatusNormExpr" /> = 2
)
)
AND (
d.is_stopped IS NULL
OR d.is_stopped = FALSE
)
</when>
<when test="query.status == 'cancelled'">
<when test="'cancelled'.equals(query.status) or '已停诊'.equals(query.status) or '已取消'.equals(query.status)">
AND (
s.status = 2
<include refid="slotStatusNormExpr" /> = 2
OR d.is_stopped = TRUE
)
</when>
<when test="'returned'.equals(query.status) or '已退号'.equals(query.status)">
AND (
<include refid="slotStatusNormExpr" /> = 5
OR <include refid="orderStatusNormExpr" /> = 4
)
</when>
<otherwise>
AND 1 = 2
</otherwise>
</choose>
</if>
</where>
@@ -266,9 +351,22 @@
SELECT
p.doctor_id AS doctorId,
p.doctor_name AS doctorName,
COALESCE(SUM(GREATEST(COALESCE(p.total_quota, 0) - COALESCE(p.booked_num, 0) - COALESCE(p.locked_num, 0), 0)), 0) AS available,
COALESCE(
SUM(
GREATEST(
COALESCE(p.total_quota, 0) - COALESCE(p.booked_num, 0) - COALESCE(p.locked_num, 0),
0
)
),
0
) AS available,
CASE
WHEN MAX(CASE WHEN d.reg_type = 1 THEN 1 ELSE 0 END) = 1 THEN 'expert'
WHEN MAX(
CASE
WHEN d.reg_type = 1 THEN 1
ELSE 0
END
) = 1 THEN 'expert'
ELSE 'general'
END AS ticketType
FROM
@@ -290,7 +388,10 @@
AND d.reg_type = 1
</when>
<when test="query.type == 'general'">
AND (d.reg_type != 1 OR d.reg_type IS NULL)
AND (
d.reg_type != 1
OR d.reg_type IS NULL
)
</when>
</choose>
</if>

View File

@@ -127,7 +127,7 @@
select * from order_main where slot_id = #{slotId} and status = 1
</select>
<insert id="insertOrder" parameterType="com.openhis.clinical.domain.Order">
<insert id="insertOrder" parameterType="com.openhis.clinical.domain.Order" useGeneratedKeys="true" keyProperty="id">
insert into order_main
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="orderNo != null and orderNo != ''">order_no,</if>
@@ -225,6 +225,12 @@
update order_main set status = 3, cancel_time = #{cancelTime}, cancel_reason = #{cancelReason} where id = #{id}
</update>
<update id="updatePayStatus">
update order_main
set pay_status = #{payStatus}, pay_time = #{payTime}, update_time = NOW()
where id = #{orderId}
</update>
<delete id="deleteOrderById">
delete from order_main where id = #{id}
</delete>

View File

@@ -162,3 +162,61 @@ export const STATUS = {
NORMAL: '0', // 正常/启用
DISABLE: '1' // 停用
};
/**
* 号源槽位状态(与后端 CommonConstants.SlotStatus 保持一致)
* adm_schedule_slot.status 字段
*/
export const SlotStatus = {
/** 可用 / 待预约 */
AVAILABLE: 0,
/** 已预约 */
BOOKED: 1,
/** 已取消 / 已停诊 */
CANCELLED: 2,
/** 已锁定 */
LOCKED: 3,
/** 已签到 / 已取号 */
CHECKED_IN: 4,
};
/**
* 号源槽位状态说明信息
*/
export const SlotStatusDescriptions = {
0: '未预约',
1: '已预约',
2: '已停诊',
3: '已锁定',
4: '已取号',
};
/**
* 号源槽位状态对应的CSS类名
*/
export const SlotStatusClassMap = {
'未预约': 'status-unbooked',
'已预约': 'status-booked',
'已取号': 'status-checked',
'已停诊': 'status-cancelled',
'已取消': 'status-cancelled',
'已锁定': 'status-locked',
};
/**
* 获取号源槽位状态的说明
* @param {number} value - 状态值
* @returns {string} - 说明信息
*/
export function getSlotStatusDescription(value) {
return SlotStatusDescriptions[value] || '未知状态';
}
/**
* 获取号源槽位状态对应的CSS类名
* @param {string} status - 状态说明
* @returns {string} - CSS类名
*/
export function getSlotStatusClass(status) {
return SlotStatusClassMap[status] || 'status-unbooked';
}

View File

@@ -37,6 +37,7 @@
<option value="booked">已预约</option>
<option value="checked">已取号</option>
<option value="cancelled">已停诊</option>
<option value="returned">已退号</option>
</select>
</div>
<div id="patientSearch" class="patient-search">
@@ -254,6 +255,7 @@ const STATUS_CLASS_MAP = {
'未预约': 'status-unbooked',
'已预约': 'status-booked',
'已取号': 'status-checked',
'已退号': 'status-returned',
'已停诊': 'status-cancelled',
'已取消': 'status-cancelled'
};
@@ -703,10 +705,36 @@ export default {
return;
}
const records = payload.list || payload.records || [];
const filteredRecords = this.applyStatusFilter(records);
const total = Number(payload.total);
this.tickets = [...records];
this.allTickets = [...records];
this.totalTickets = Number.isFinite(total) ? total : this.tickets.length;
this.tickets = [...filteredRecords];
this.allTickets = [...filteredRecords];
// 当按状态筛选时,优先使用前端过滤后的数量,避免后端状态未生效导致“显示全部”
if (this.selectedStatus && this.selectedStatus !== 'all') {
this.totalTickets = this.tickets.length;
} else {
this.totalTickets = Number.isFinite(total) ? total : this.tickets.length;
}
},
applyStatusFilter(records = []) {
if (!Array.isArray(records) || records.length === 0) {
return [];
}
if (!this.selectedStatus || this.selectedStatus === 'all') {
return records;
}
const statusMap = {
unbooked: ['未预约'],
booked: ['已预约'],
checked: ['已取号'],
cancelled: ['已停诊', '已取消'],
returned: ['已退号']
};
const matchedStatusList = statusMap[this.selectedStatus] || [];
if (matchedStatusList.length === 0) {
return records;
}
return records.filter(item => matchedStatusList.includes(item?.status));
},
updateDoctorsListFromApi(doctorResponse) {
let doctorList = [];
@@ -1376,6 +1404,11 @@ export default {
color: #52c41a;
}
.status-returned {
background-color: #fff7e6;
color: #d46b08;
}
.status-cancelled {
background-color: #fff1f0;
color: #ff4d4f;

View File

@@ -7,6 +7,7 @@
<div style="display: flex; align-items: center; width: 100%">
<span style="font-size: 16px; font-weight: bold; margin-right: 20px;">门诊挂号</span>
<div style="flex: 1; display: flex; justify-content: center; align-items: center;">
<el-button type="success" icon="Check" @click="handleCheckIn" size="small">预约签到</el-button>
<el-button type="primary" icon="Document" @click="goToPatientRecord" size="small">档案</el-button>
<el-button type="primary" icon="Plus" @click="handleAddPatient" size="small">新建</el-button>
<el-button type="primary" plain icon="Search" @click="handleSearch" size="small">查询</el-button>
@@ -15,7 +16,7 @@
<el-button type="primary" plain @click="handleReadCard('03')" size="small">医保卡</el-button>
<el-button type="warning" plain icon="CircleClose" @click="handleClear" size="small">清空</el-button>
<el-button type="primary" icon="Plus" @click="handleAdd" size="small">保存挂号</el-button>
<el-button type="success" icon="Printer" @click="handleReprint" size="small">补打挂号</el-button>
<el-button type="info" icon="Printer" @click="handleReprint" size="small">补打挂号</el-button>
</div>
</div>
</template>
@@ -615,6 +616,74 @@
}
"
/>
<!-- 预约签到患者选择弹窗 -->
<el-dialog
v-model="showCheckInPatientModal"
title="请选择预约的患者"
width="1200px"
:close-on-click-modal="false"
>
<div style="margin-bottom: 20px; display: flex; gap: 10px;">
<el-input
v-model="checkInSearchKey"
placeholder="输入患者姓名回车查询"
style="width: 400px"
@keyup.enter="loadCheckInPatientList"
/>
<el-button type="primary" @click="loadCheckInPatientList">查询</el-button>
</div>
<el-table
v-loading="checkInLoading"
:data="checkInPatientList"
border
style="width: 100%"
@row-click="selectRow"
highlight-current-row
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="patientId" label="就诊卡号" width="120" align="center" />
<el-table-column prop="patientName" label="姓名" width="120" align="center">
<template #default="scope">
<span style="color: #ff4d4f">{{ scope.row.patientName }}</span>
</template>
</el-table-column>
<el-table-column prop="gender" label="性别" width="80" align="center" />
<el-table-column label="证件类型" width="150" align="center">
<template #default>居民身份证</template>
</el-table-column>
<el-table-column prop="idCard" label="证件号码" width="200" align="center" />
<el-table-column prop="phone" label="手机号码" width="150" align="center" />
<el-table-column label="号源类型" width="100" align="center">
<template #default="scope">
<el-tag :type="scope.row.ticketType === 'expert' ? 'danger' : 'success'">
{{ scope.row.ticketType === 'expert' ? '专家号' : '普通号' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="fee" label="预约金额" width="100" align="center">
<template #default="scope">
<span style="font-weight: bold; color: #f5222d">{{ scope.row.fee }}</span>
</template>
</el-table-column>
<el-table-column prop="dateTime" label="就诊时间" width="180" align="center" />
</el-table>
<div style="margin-top: 20px; display: flex; justify-content: space-between; align-items: center;">
<el-pagination
v-model:current-page="checkInPage"
v-model:page-size="checkInLimit"
:total="checkInTotal"
layout="prev, pager, next"
@current-change="loadCheckInPatientList"
/>
<div class="dialog-footer">
<el-button @click="showCheckInPatientModal = false">取消</el-button>
<el-button type="primary" @click="confirmCheckIn" :disabled="!selectedCheckInPatient">确定</el-button>
</div>
</div>
</el-dialog>
</div>
</template>
@@ -632,7 +701,8 @@ import {
returnRegister,
updatePatientPhone,
} from './components/outpatientregistration';
import {invokeYbPlugin5000, invokeYbPlugin5001} from '@/api/public';
import { listTicket, checkInTicket } from '@/api/appoinmentmanage/ticket';
import { invokeYbPlugin5000, invokeYbPlugin5001 } from '@/api/public';
import patientInfoDialog from './components/patientInfoDialog';
import PatientAddDialog from './components/patientAddDialog';
import patientList from './components/patientList';
@@ -644,7 +714,7 @@ import {handleColor} from '@/utils/his';
import useUserStore from '@/store/modules/user';
import {formatDateStr} from '@/utils/index';
import {isValidCNPhoneNumber} from '../../../utils/validate';
import {ElMessage} from 'element-plus';
import {ElMessage, ElMessageBox} from 'element-plus';
import {hiprint} from 'vue-plugin-hiprint';
import outpatientRegistrationTemplate from '@/components/Print/OutpatientRegistration.json';
@@ -687,14 +757,25 @@ const ybTypeRef = ref(null);
const openDialog = ref(false);
const openRefundDialog = ref(false);
const openReprintDialog = ref(false);
// 预约签到相关变量
const showCheckInPatientModal = ref(false);
const checkInPatientList = ref([]);
const selectedCheckInPatient = ref(null);
const totalAmount = ref(0);
const chargeItemIdList = ref([]);
const chrgBchnoList = ref([]);
const paymentId = ref('');
const loadingText = ref('');
const checkInSearchKey = ref('');
const checkInPage = ref(1);
const checkInLimit = ref(10);
const checkInTotal = ref(0);
const checkInLoading = ref(false);
const registerInfo = ref({}); // 原挂号记录信息
const queryType = ref('all'); // 查询类型all-全部, normal-正常挂号, returned-退号记录
const guardianAgeConfig = ref(''); // 监护人规定年龄配置
const currentSlotId = ref(null); // 当前预约签到的号源ID
// 使用 ref 定义查询所得用户信息数据
const patientInfoList = ref(undefined);
@@ -1584,6 +1665,189 @@ function handleReprint() {
openReprintDialog.value = true;
}
/** 预约签到 - 打开患者选择弹窗 */
function handleCheckIn() {
// 打开患者选择弹窗,显示已预约但未签到的患者列表
showCheckInPatientModal.value = true;
// 加载已预约未签到的患者列表
loadCheckInPatientList();
}
/** 加载预约签到患者列表 */
function loadCheckInPatientList() {
checkInLoading.value = true;
const today = formatDateStr(new Date(), 'YYYY-MM-DD');
listTicket({
date: today,
status: 'booked',
name: checkInSearchKey.value, // 支持姓名等模糊查询,后端需适配
page: checkInPage.value,
limit: checkInLimit.value
}).then(res => {
const data = res.data?.list || res.list || res.data || [];
const total = res.data?.total || res.total || data.length;
checkInPatientList.value = data.map(item => ({
...item,
appointmentDate: item.scheduleDate + ' ' + (item.expectTime || '')
}));
checkInTotal.value = total;
}).catch(err => {
console.error('加载预约导出失败:', err);
ElMessage.error('获取预约列表失败');
}).finally(() => {
checkInLoading.value = false;
});
}
/** 弹窗行点击处理 */
function selectRow(row) {
selectedCheckInPatient.value = row;
}
/** 确认签到(一键签到:直接构建挂号参数 → 预结算 → 弹收费窗口) */
async function confirmCheckIn() {
if (!selectedCheckInPatient.value) {
ElMessage.warning('请先选择患者');
return;
}
const patient = selectedCheckInPatient.value;
// 每次开始新的签到流程先清理残留 slotId避免历史脏值串单
currentSlotId.value = null;
// 弹出确认提示
try {
await ElMessageBox.confirm(
`确认为患者【${patient.patientName}】办理签到挂号?\n` +
`科室:${patient.department || '-'}\n` +
`医生:${patient.doctor || '-'}\n` +
`费用:¥${patient.fee || '0.00'}`,
'签到确认',
{
confirmButtonText: '确认签到',
cancelButtonText: '取消',
type: 'info',
}
);
} catch {
// 用户点了取消
return;
}
showCheckInPatientModal.value = false;
readCardLoading.value = true;
loadingText.value = '正在处理签到挂号...';
try {
// 1. 用科室ID加载该科室的挂号类型列表获取 serviceTypeId 和 definitionId
const healthcareRes = await getHealthcareMetadata({ organizationId: patient.departmentId });
const healthcareRecords = healthcareRes.data?.records || [];
if (healthcareRecords.length === 0) {
ElMessage.error('该科室未配置挂号类型,无法自动签到');
readCardLoading.value = false;
return;
}
// 2. 按号源类型(专家/普通)模糊匹配挂号类型
const matchTypeName = (patient.ticketType === 'expert') ? '专家' : '普通';
const matchedService = healthcareRecords.find(h => h.name && h.name.includes(matchTypeName));
if (!matchedService) {
// 匹配不到就取第一个作为兜底
ElMessage.warning('未精确匹配到挂号类型,已使用默认类型');
}
const service = matchedService || healthcareRecords[0];
const realPatientId = patient.realPatientId; // 后端新增的真实患者数据库ID
if (!realPatientId) {
ElMessage.error('患者ID缺失请联系管理员检查预约数据');
readCardLoading.value = false;
return;
}
// 3. 构建挂号参数(与 transformFormData 结构一致)
const registrationParam = {
encounterFormData: {
patientId: realPatientId,
priorityEnum: 3, // 默认优先级
serviceTypeId: service.id,
organizationId: patient.departmentId,
},
encounterLocationFormData: {
locationId: null,
},
encounterParticipantFormData: {
practitionerId: patient.doctorId,
},
accountFormData: {
patientId: realPatientId,
typeCode: 1, // 个人现金账户
contractNo: '0000', // 默认自费
},
chargeItemFormData: {
patientId: realPatientId,
definitionId: service.definitionId,
serviceId: service.id,
totalPrice: parseFloat(patient.fee) || ((service.price || 0) + (service.activityPrice || 0)),
},
};
// 4. 设置 patientInfoChargeDialog 需要展示)
patientInfo.value = {
patientId: realPatientId,
patientName: patient.patientName,
genderEnum_enumText: patient.gender || '-',
age: '',
contractName: '自费',
idCard: patient.idCard,
phone: patient.phone,
categoryEnum: '门诊',
organizationName: patient.department || '',
practitionerName: patient.doctor || '',
healthcareName: service.name || '',
};
// 同步设置 form 的 contractNoChargeDialog 的 feeType 会读取它
form.value.contractNo = '0000';
// 5. 调用预结算接口reg-pre-pay
const res = await addOutpatientRegistration(registrationParam);
if (res.code == 200) {
// 仅在预结算成功后记录待签到的号源,避免失败路径残留脏数据
currentSlotId.value = patient.slot_id;
// 6. 设置收费弹窗所需的数据
chrgBchno.value = res.data.chrgBchno;
registerBusNo.value = res.data.busNo;
totalAmount.value = res.data.psnCashPay;
patientInfo.value.encounterId = res.data.encounterId || '';
patientInfo.value.busNo = res.data.busNo || '';
transformedData.value = registrationParam;
chargeItemIdList.value = [];
// 7. 打开收费弹窗
openDialog.value = true;
// 打印挂号单
printRegistrationByHiprint(res.data);
} else {
currentSlotId.value = null;
ElMessage.error(res.msg || '预结算失败');
}
} catch (err) {
currentSlotId.value = null;
console.error('预约签到失败:', err);
ElMessage.error('签到处理失败: ' + (err.message || '未知错误'));
} finally {
readCardLoading.value = false;
}
}
/**
* 点击患者列表给表单赋值
*/
@@ -1656,20 +1920,29 @@ function handleClose(value) {
proxy.$modal.msgSuccess('操作成功');
// 更新患者手机号
updatePhone();
// getList();
// reset();
// addOutpatientRegistration(transformedData.value).then((response) => {
// reset();
// proxy.$modal.msgSuccess('新增成功');
// getList();
// });
// 先取出并清空,避免接口失败/取消等路径导致 slotId 残留污染下一单
const pendingSlotId = currentSlotId.value;
currentSlotId.value = null;
// 如果是预约签到的挂号,执行签到状态更新
if (pendingSlotId) {
checkInTicket(pendingSlotId).then(() => {
console.log('预约状态已更新为已取号');
}).catch(err => {
console.error('更新预约状态失败:', err);
ElMessage.error('预约状态更新失败,请手动签到');
});
}
} else if (value == 'cancel') {
currentSlotId.value = null;
// cancelRegister(patientInfo.value.encounterId).then((res) => {
// if (res.code == 200) {
// getList();
// }
// });
} else {
currentSlotId.value = null;
openRefundDialog.value = false;
}
}