Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
package com.openhis.web.appointmentmanage.appservice;
|
||||||
|
|
||||||
|
import com.core.common.core.domain.R;
|
||||||
|
import com.openhis.appointmentmanage.domain.AppointmentConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预约配置AppService接口
|
||||||
|
*
|
||||||
|
* @author openhis
|
||||||
|
* @date 2026-03-23
|
||||||
|
*/
|
||||||
|
public interface IAppointmentConfigAppService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前机构的预约配置
|
||||||
|
*
|
||||||
|
* @return 预约配置
|
||||||
|
*/
|
||||||
|
R<?> getAppointmentConfig();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存预约配置
|
||||||
|
*
|
||||||
|
* @param appointmentConfig 预约配置
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
R<?> saveAppointmentConfig(AppointmentConfig appointmentConfig);
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.openhis.web.appointmentmanage.appservice.impl;
|
||||||
|
|
||||||
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.SecurityUtils;
|
||||||
|
import com.openhis.appointmentmanage.domain.AppointmentConfig;
|
||||||
|
import com.openhis.appointmentmanage.service.IAppointmentConfigService;
|
||||||
|
import com.openhis.web.appointmentmanage.appservice.IAppointmentConfigAppService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预约配置AppService实现类
|
||||||
|
*
|
||||||
|
* @author openhis
|
||||||
|
* @date 2026-03-23
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class AppointmentConfigAppServiceImpl implements IAppointmentConfigAppService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IAppointmentConfigService appointmentConfigService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R<?> getAppointmentConfig() {
|
||||||
|
// 获取当前登录用户的机构ID
|
||||||
|
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||||
|
if (tenantId == null) {
|
||||||
|
return R.fail("获取机构信息失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
AppointmentConfig config = appointmentConfigService.getConfigByTenantId(tenantId);
|
||||||
|
return R.ok(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R<?> saveAppointmentConfig(AppointmentConfig appointmentConfig) {
|
||||||
|
// 获取当前登录用户的机构ID
|
||||||
|
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||||
|
if (tenantId == null) {
|
||||||
|
return R.fail("获取机构信息失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询是否已存在配置
|
||||||
|
AppointmentConfig existingConfig = appointmentConfigService.getConfigByTenantId(tenantId);
|
||||||
|
|
||||||
|
if (existingConfig != null) {
|
||||||
|
// 更新现有配置
|
||||||
|
existingConfig.setCancelAppointmentType(appointmentConfig.getCancelAppointmentType());
|
||||||
|
existingConfig.setCancelAppointmentCount(appointmentConfig.getCancelAppointmentCount());
|
||||||
|
existingConfig.setValidFlag(appointmentConfig.getValidFlag());
|
||||||
|
appointmentConfigService.saveOrUpdate(existingConfig);
|
||||||
|
return R.ok(existingConfig);
|
||||||
|
} else {
|
||||||
|
// 新增配置
|
||||||
|
appointmentConfig.setTenantId(tenantId);
|
||||||
|
appointmentConfig.setValidFlag(1);
|
||||||
|
appointmentConfigService.saveOrUpdateConfig(appointmentConfig);
|
||||||
|
return R.ok(appointmentConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
package com.openhis.web.appointmentmanage.controller;
|
package com.openhis.web.appointmentmanage.controller;
|
||||||
|
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.openhis.appointmentmanage.domain.AppointmentConfig;
|
||||||
|
import com.openhis.web.appointmentmanage.appservice.IAppointmentConfigAppService;
|
||||||
import com.openhis.web.appointmentmanage.appservice.IDeptAppService;
|
import com.openhis.web.appointmentmanage.appservice.IDeptAppService;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
@@ -16,6 +15,9 @@ public class DeptController {
|
|||||||
@Resource
|
@Resource
|
||||||
private IDeptAppService deptAppService;
|
private IDeptAppService deptAppService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IAppointmentConfigAppService appointmentConfigAppService;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 获取科室列表
|
* 获取科室列表
|
||||||
*
|
*
|
||||||
@@ -38,4 +40,22 @@ public class DeptController {
|
|||||||
){
|
){
|
||||||
return R.ok(deptAppService.searchDept(pageNo,pageSize,orgName,deptName));
|
return R.ok(deptAppService.searchDept(pageNo,pageSize,orgName,deptName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 获取预约配置
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
@GetMapping("/config")
|
||||||
|
public R<?> getAppointmentConfig(){
|
||||||
|
return appointmentConfigAppService.getAppointmentConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 保存预约配置
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
@PostMapping("/config")
|
||||||
|
public R<?> saveAppointmentConfig(@RequestBody AppointmentConfig appointmentConfig){
|
||||||
|
return appointmentConfigAppService.saveAppointmentConfig(appointmentConfig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,7 +128,10 @@ public class CheckPackageAppServiceImpl implements ICheckPackageAppService {
|
|||||||
// 保存套餐明细
|
// 保存套餐明细
|
||||||
if (checkPackageDto.getItems() != null && !checkPackageDto.getItems().isEmpty()) {
|
if (checkPackageDto.getItems() != null && !checkPackageDto.getItems().isEmpty()) {
|
||||||
List<CheckPackageDetail> details = convertToDetails(checkPackageDto.getItems(), checkPackage.getId(), 1);
|
List<CheckPackageDetail> details = convertToDetails(checkPackageDto.getItems(), checkPackage.getId(), 1);
|
||||||
checkPackageDetailService.saveBatch(details);
|
boolean detailSaveResult = checkPackageDetailService.saveBatch(details);
|
||||||
|
if (!detailSaveResult) {
|
||||||
|
throw new RuntimeException("保存套餐明细失败");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return R.ok(checkPackage.getId(), "保存成功");
|
return R.ok(checkPackage.getId(), "保存成功");
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ public class CheckPackageDetailDto {
|
|||||||
@NotNull(message = "数量不能为空")
|
@NotNull(message = "数量不能为空")
|
||||||
private Integer quantity;
|
private Integer quantity;
|
||||||
|
|
||||||
|
/** 单位 */
|
||||||
|
private String unit;
|
||||||
|
|
||||||
/** 单价 */
|
/** 单价 */
|
||||||
@NotNull(message = "单价不能为空")
|
@NotNull(message = "单价不能为空")
|
||||||
private BigDecimal unitPrice;
|
private BigDecimal unitPrice;
|
||||||
|
|||||||
@@ -413,10 +413,10 @@ public class SurgicalScheduleAppServiceImpl implements ISurgicalScheduleAppServi
|
|||||||
/**
|
/**
|
||||||
* 格式化安排时间
|
* 格式化安排时间
|
||||||
*/
|
*/
|
||||||
private String formatScheduleDate(LocalDate scheduleDate) {
|
private String formatScheduleDate(LocalDateTime scheduleDate) {
|
||||||
if (scheduleDate == null) return "";
|
if (scheduleDate == null) return "";
|
||||||
|
|
||||||
// 格式化为 yyyy-MM-dd
|
// 格式化为 yyyy-MM-dd HH:mm:ss
|
||||||
return scheduleDate.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
return scheduleDate.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,9 +45,10 @@ public class OpCreateScheduleDto {
|
|||||||
private String postoperativeDiagnosis;
|
private String postoperativeDiagnosis;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 手术安排日期
|
* 手术安排日期时间
|
||||||
*/
|
*/
|
||||||
private LocalDate scheduleDate;
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime scheduleDate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 手术台次序号
|
* 手术台次序号
|
||||||
@@ -82,11 +83,13 @@ public class OpCreateScheduleDto {
|
|||||||
/**
|
/**
|
||||||
* 入院时间
|
* 入院时间
|
||||||
*/
|
*/
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private LocalDateTime admissionTime;
|
private LocalDateTime admissionTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 入手术室时间
|
* 入手术室时间
|
||||||
*/
|
*/
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private LocalDateTime entryTime;
|
private LocalDateTime entryTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -167,21 +170,25 @@ public class OpCreateScheduleDto {
|
|||||||
/**
|
/**
|
||||||
* 手术开始时间
|
* 手术开始时间
|
||||||
*/
|
*/
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private LocalDateTime startTime;
|
private LocalDateTime startTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 手术结束时间
|
* 手术结束时间
|
||||||
*/
|
*/
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private LocalDateTime endTime;
|
private LocalDateTime endTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 麻醉开始时间
|
* 麻醉开始时间
|
||||||
*/
|
*/
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private LocalDateTime anesStart;
|
private LocalDateTime anesStart;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 麻醉结束时间
|
* 麻醉结束时间
|
||||||
*/
|
*/
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private LocalDateTime anesEnd;
|
private LocalDateTime anesEnd;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -84,6 +84,7 @@
|
|||||||
T1.id,
|
T1.id,
|
||||||
T1.bus_no,
|
T1.bus_no,
|
||||||
T1.name,
|
T1.name,
|
||||||
|
T1.permitted_unit_code,
|
||||||
T2.price as retail_price
|
T2.price as retail_price
|
||||||
FROM wor_activity_definition T1
|
FROM wor_activity_definition T1
|
||||||
INNER JOIN adm_charge_item_definition T2
|
INNER JOIN adm_charge_item_definition T2
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package com.openhis.appointmentmanage.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预约配置Entity
|
||||||
|
*
|
||||||
|
* @date 2026-03-23
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName(value = "appointment_config")
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class AppointmentConfig {
|
||||||
|
|
||||||
|
/** 主键ID */
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/** 机构ID(关联 sys_tenant) */
|
||||||
|
private Integer tenantId;
|
||||||
|
|
||||||
|
/** 取消预约时间类型:YEAR/MONTH/DAY */
|
||||||
|
private String cancelAppointmentType;
|
||||||
|
|
||||||
|
/** 取消预约次数限制 */
|
||||||
|
private Integer cancelAppointmentCount;
|
||||||
|
|
||||||
|
/** 有效标志:1=有效,0=无效 */
|
||||||
|
private Integer validFlag;
|
||||||
|
|
||||||
|
/** 创建人 */
|
||||||
|
private String createBy;
|
||||||
|
|
||||||
|
/** 创建时间 */
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/** 更新时间 */
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.openhis.appointmentmanage.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.openhis.appointmentmanage.domain.AppointmentConfig;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预约配置Mapper接口
|
||||||
|
*
|
||||||
|
* @author openhis
|
||||||
|
* @date 2026-03-23
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface AppointmentConfigMapper extends BaseMapper<AppointmentConfig> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.openhis.appointmentmanage.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.openhis.appointmentmanage.domain.AppointmentConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预约配置Service接口
|
||||||
|
*
|
||||||
|
* @author openhis
|
||||||
|
* @date 2026-03-23
|
||||||
|
*/
|
||||||
|
public interface IAppointmentConfigService extends IService<AppointmentConfig> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据机构ID获取预约配置
|
||||||
|
*
|
||||||
|
* @param tenantId 机构ID
|
||||||
|
* @return 预约配置
|
||||||
|
*/
|
||||||
|
AppointmentConfig getConfigByTenantId(Integer tenantId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存或更新预约配置
|
||||||
|
*
|
||||||
|
* @param appointmentConfig 预约配置
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
int saveOrUpdateConfig(AppointmentConfig appointmentConfig);
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.openhis.appointmentmanage.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.openhis.appointmentmanage.domain.AppointmentConfig;
|
||||||
|
import com.openhis.appointmentmanage.mapper.AppointmentConfigMapper;
|
||||||
|
import com.openhis.appointmentmanage.service.IAppointmentConfigService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预约配置Service实现类
|
||||||
|
*
|
||||||
|
* @author openhis
|
||||||
|
* @date 2026-03-23
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class AppointmentConfigServiceImpl
|
||||||
|
extends ServiceImpl<AppointmentConfigMapper, AppointmentConfig>
|
||||||
|
implements IAppointmentConfigService {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AppointmentConfig getConfigByTenantId(Integer tenantId) {
|
||||||
|
LambdaQueryWrapper<AppointmentConfig> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(AppointmentConfig::getTenantId, tenantId)
|
||||||
|
.eq(AppointmentConfig::getValidFlag, 1);
|
||||||
|
return this.getOne(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int saveOrUpdateConfig(AppointmentConfig appointmentConfig) {
|
||||||
|
if (appointmentConfig.getId() == null) {
|
||||||
|
// 新增
|
||||||
|
appointmentConfig.setCreateTime(LocalDateTime.now());
|
||||||
|
appointmentConfig.setUpdateTime(LocalDateTime.now());
|
||||||
|
return this.baseMapper.insert(appointmentConfig) > 0 ? 1 : 0;
|
||||||
|
} else {
|
||||||
|
// 更新
|
||||||
|
appointmentConfig.setUpdateTime(LocalDateTime.now());
|
||||||
|
return this.baseMapper.updateById(appointmentConfig) > 0 ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,6 +42,7 @@ public class CheckPackageDetail {
|
|||||||
private String dose;
|
private String dose;
|
||||||
|
|
||||||
/** 途径 */
|
/** 途径 */
|
||||||
|
@TableField("\"method\"")
|
||||||
private String method;
|
private String method;
|
||||||
|
|
||||||
/** 频次 */
|
/** 频次 */
|
||||||
@@ -53,6 +54,10 @@ public class CheckPackageDetail {
|
|||||||
/** 数量 */
|
/** 数量 */
|
||||||
private Integer quantity;
|
private Integer quantity;
|
||||||
|
|
||||||
|
/** 单位 */
|
||||||
|
@TableField("\"unit\"")
|
||||||
|
private String unit;
|
||||||
|
|
||||||
/** 单价 */
|
/** 单价 */
|
||||||
private BigDecimal unitPrice;
|
private BigDecimal unitPrice;
|
||||||
|
|
||||||
|
|||||||
@@ -33,4 +33,14 @@ public interface IOrderService extends IService<Order> {
|
|||||||
Order createAppointmentOrder(Map<String, Object> params);
|
Order createAppointmentOrder(Map<String, Object> params);
|
||||||
|
|
||||||
int cancelAppointmentOrder(Long orderId, String cancelReason);
|
int cancelAppointmentOrder(Long orderId, String cancelReason);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计患者在指定机构、指定起始时间后的取消预约次数
|
||||||
|
*
|
||||||
|
* @param patientId 患者ID
|
||||||
|
* @param tenantId 机构ID
|
||||||
|
* @param startTime 起始时间
|
||||||
|
* @return 取消次数
|
||||||
|
*/
|
||||||
|
long countPatientCancellations(Long patientId, Integer tenantId, java.time.LocalDateTime startTime);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,4 +178,16 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
|||||||
Date cancelTime = new Date();
|
Date cancelTime = new Date();
|
||||||
return orderMapper.updateOrderCancelInfoById(orderId, cancelTime, cancelReason);
|
return orderMapper.updateOrderCancelInfoById(orderId, cancelTime, cancelReason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long countPatientCancellations(Long patientId, Integer tenantId, java.time.LocalDateTime startTime) {
|
||||||
|
if (patientId == null || tenantId == null || startTime == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return this.count(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<Order>()
|
||||||
|
.eq(Order::getPatientId, patientId)
|
||||||
|
.eq(Order::getTenantId, tenantId)
|
||||||
|
.ge(Order::getCancelTime, startTime)
|
||||||
|
.eq(Order::getStatus, AppointmentOrderStatus.CANCELLED));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ 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.openhis.appointmentmanage.domain.AppointmentConfig;
|
||||||
|
import com.openhis.appointmentmanage.service.IAppointmentConfigService;
|
||||||
import com.openhis.appointmentmanage.domain.TicketSlotDTO;
|
import com.openhis.appointmentmanage.domain.TicketSlotDTO;
|
||||||
import com.openhis.appointmentmanage.mapper.SchedulePoolMapper;
|
import com.openhis.appointmentmanage.mapper.SchedulePoolMapper;
|
||||||
import com.openhis.appointmentmanage.mapper.ScheduleSlotMapper;
|
import com.openhis.appointmentmanage.mapper.ScheduleSlotMapper;
|
||||||
@@ -23,6 +25,7 @@ import java.time.LocalDate;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
|
import java.time.temporal.TemporalAdjusters;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -49,6 +52,9 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
|||||||
@Resource
|
@Resource
|
||||||
private SchedulePoolMapper schedulePoolMapper;
|
private SchedulePoolMapper schedulePoolMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IAppointmentConfigService appointmentConfigService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询号源列表
|
* 查询号源列表
|
||||||
*
|
*
|
||||||
@@ -235,6 +241,27 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
|||||||
if (orders == null || orders.isEmpty()) {
|
if (orders == null || orders.isEmpty()) {
|
||||||
throw new RuntimeException("当前号源没有可取消的预约订单");
|
throw new RuntimeException("当前号源没有可取消的预约订单");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 核心逻辑:获取订单信息并检查机构取消限制
|
||||||
|
Order latestOrder = orders.get(0);
|
||||||
|
Integer tenantId = latestOrder.getTenantId();
|
||||||
|
Long patientId = latestOrder.getPatientId();
|
||||||
|
|
||||||
|
if (tenantId != null && patientId != null) {
|
||||||
|
AppointmentConfig config = appointmentConfigService.getConfigByTenantId(tenantId);
|
||||||
|
if (config != null && config.getCancelAppointmentCount() != null
|
||||||
|
&& config.getCancelAppointmentCount() > 0) {
|
||||||
|
// 计算当前周期的起始时间
|
||||||
|
LocalDateTime startTime = calculatePeriodStartTime(config.getCancelAppointmentType());
|
||||||
|
// 统计已取消次数
|
||||||
|
long cancelledCount = orderService.countPatientCancellations(patientId, tenantId, startTime);
|
||||||
|
if (cancelledCount >= config.getCancelAppointmentCount()) {
|
||||||
|
String periodName = getPeriodName(config.getCancelAppointmentType());
|
||||||
|
throw new RuntimeException("您在" + periodName + "内已达到该机构取消预约次数上限(" + config.getCancelAppointmentCount() + "次),禁止取消");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (Order order : orders) {
|
for (Order order : orders) {
|
||||||
orderService.cancelAppointmentOrder(order.getId(), "患者取消预约");
|
orderService.cancelAppointmentOrder(order.getId(), "患者取消预约");
|
||||||
}
|
}
|
||||||
@@ -309,4 +336,35 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
|||||||
throw new RuntimeException("挂号费格式错误: " + fee);
|
throw new RuntimeException("挂号费格式错误: " + fee);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据类型获取周期名称
|
||||||
|
*/
|
||||||
|
private String getPeriodName(String type) {
|
||||||
|
if ("YEAR".equalsIgnoreCase(type)) {
|
||||||
|
return "年度";
|
||||||
|
} else if ("MONTH".equalsIgnoreCase(type)) {
|
||||||
|
return "月度";
|
||||||
|
} else {
|
||||||
|
return "当日";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据取消预约时间类型计算本周期的起始时间
|
||||||
|
*
|
||||||
|
* @param type YEAR/MONTH/DAY
|
||||||
|
* @return 起始时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime calculatePeriodStartTime(String type) {
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
if ("YEAR".equalsIgnoreCase(type)) {
|
||||||
|
return now.with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN);
|
||||||
|
} else if ("MONTH".equalsIgnoreCase(type)) {
|
||||||
|
return now.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
|
||||||
|
} else {
|
||||||
|
// 默认为 DAY
|
||||||
|
return now.with(LocalTime.MIN);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.openhis.surgicalschedule.domain;
|
package com.openhis.surgicalschedule.domain;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.*;
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
@@ -52,9 +51,10 @@ public class OpSchedule extends HisBaseEntity {
|
|||||||
@TableField(value = "postoperative_diagnosis", insertStrategy = FieldStrategy.IGNORED)
|
@TableField(value = "postoperative_diagnosis", insertStrategy = FieldStrategy.IGNORED)
|
||||||
private String postoperativeDiagnosis;
|
private String postoperativeDiagnosis;
|
||||||
|
|
||||||
/** 手术安排日期 */
|
/** 手术安排日期时间 */
|
||||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private LocalDate scheduleDate;
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime scheduleDate;
|
||||||
|
|
||||||
/** 手术台次序号 */
|
/** 手术台次序号 */
|
||||||
private Integer sequenceNo;
|
private Integer sequenceNo;
|
||||||
|
|||||||
@@ -239,32 +239,32 @@
|
|||||||
<select id="countOrders" resultType="int">
|
<select id="countOrders" resultType="int">
|
||||||
select count(*) from order_main
|
select count(*) from order_main
|
||||||
<where>
|
<where>
|
||||||
<if test="orderNo != null and orderNo != ''">
|
<if test="params.orderNo != null and params.orderNo != ''">
|
||||||
and order_no = #{orderNo}
|
and order_no = #{params.orderNo}
|
||||||
</if>
|
</if>
|
||||||
<if test="patientId != null">
|
<if test="params.patientId != null">
|
||||||
and patient_id = #{patientId}
|
and patient_id = #{params.patientId}
|
||||||
</if>
|
</if>
|
||||||
<if test="scheduleId != null">
|
<if test="params.scheduleId != null">
|
||||||
and schedule_id = #{scheduleId}
|
and schedule_id = #{params.scheduleId}
|
||||||
</if>
|
</if>
|
||||||
<if test="slotId != null">
|
<if test="params.slotId != null">
|
||||||
and slot_id = #{slotId}
|
and slot_id = #{params.slotId}
|
||||||
</if>
|
</if>
|
||||||
<if test="departmentId != null">
|
<if test="params.departmentId != null">
|
||||||
and department_id = #{departmentId}
|
and department_id = #{params.departmentId}
|
||||||
</if>
|
</if>
|
||||||
<if test="doctorId != null">
|
<if test="params.doctorId != null">
|
||||||
and doctor_id = #{doctorId}
|
and doctor_id = #{params.doctorId}
|
||||||
</if>
|
</if>
|
||||||
<if test="status != null">
|
<if test="params.status != null">
|
||||||
and status = #{status}
|
and status = #{params.status}
|
||||||
</if>
|
</if>
|
||||||
<if test="payStatus != null">
|
<if test="params.payStatus != null">
|
||||||
and pay_status = #{payStatus}
|
and pay_status = #{params.payStatus}
|
||||||
</if>
|
</if>
|
||||||
<if test="appointmentDate != null">
|
<if test="params.appointmentDate != null">
|
||||||
and appointment_date = #{appointmentDate}
|
and appointment_date = #{params.appointmentDate}
|
||||||
</if>
|
</if>
|
||||||
</where>
|
</where>
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取预约配置
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getAppointmentConfig() {
|
||||||
|
return request({
|
||||||
|
url: '/dept/config',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存预约配置
|
||||||
|
* @param {Object} data - 预约配置数据
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function saveAppointmentConfig(data) {
|
||||||
|
return request({
|
||||||
|
url: '/dept/config',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -100,9 +100,9 @@
|
|||||||
<el-form label-position="top" :model="appointmentSettingForm">
|
<el-form label-position="top" :model="appointmentSettingForm">
|
||||||
<el-form-item label="取消预约时间类型">
|
<el-form-item label="取消预约时间类型">
|
||||||
<el-select v-model="appointmentSettingForm.cancelAppointmentType" placeholder="请选择" style="width: 200px">
|
<el-select v-model="appointmentSettingForm.cancelAppointmentType" placeholder="请选择" style="width: 200px">
|
||||||
<el-option label="年" value="年"></el-option>
|
<el-option label="年" value="YEAR"></el-option>
|
||||||
<el-option label="月" value="月"></el-option>
|
<el-option label="月" value="MONTH"></el-option>
|
||||||
<el-option label="日" value="日"></el-option>
|
<el-option label="日" value="DAY"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="取消预约次数">
|
<el-form-item label="取消预约次数">
|
||||||
@@ -390,6 +390,7 @@ import {useRouter} from 'vue-router'
|
|||||||
import {ElDialog, ElForm, ElFormItem, ElInput, ElMessage, ElMessageBox, ElOption, ElSelect} from 'element-plus'
|
import {ElDialog, ElForm, ElFormItem, ElInput, ElMessage, ElMessageBox, ElOption, ElSelect} from 'element-plus'
|
||||||
import {DocumentRemove, EditPen, View, Delete} from '@element-plus/icons-vue'
|
import {DocumentRemove, EditPen, View, Delete} from '@element-plus/icons-vue'
|
||||||
import {listDept, searchDept} from '@/api/appoinmentmanage/dept'
|
import {listDept, searchDept} from '@/api/appoinmentmanage/dept'
|
||||||
|
import {getAppointmentConfig, saveAppointmentConfig} from '@/api/appoinmentmanage/appointmentConfig'
|
||||||
import {getLocationTree, getPractitionerMetadata, getHealthcareMetadata} from '@/views/charge/outpatientregistration/components/outpatientregistration'
|
import {getLocationTree, getPractitionerMetadata, getHealthcareMetadata} from '@/views/charge/outpatientregistration/components/outpatientregistration'
|
||||||
import {addDoctorSchedule, addDoctorScheduleWithDate, updateDoctorSchedule, deleteDoctorSchedule, getRegisterOrganizations, getDoctorScheduleListByDeptId, getDoctorScheduleListByDeptIdAndDateRange} from './api'
|
import {addDoctorSchedule, addDoctorScheduleWithDate, updateDoctorSchedule, deleteDoctorSchedule, getRegisterOrganizations, getDoctorScheduleListByDeptId, getDoctorScheduleListByDeptIdAndDateRange} from './api'
|
||||||
import {getClinicRoomList} from '@/api/appoinmentmanage/clinicRoom'
|
import {getClinicRoomList} from '@/api/appoinmentmanage/clinicRoom'
|
||||||
@@ -922,16 +923,51 @@ const handleReset = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 预约设置弹窗显示
|
// 预约设置弹窗显示
|
||||||
const handleAppointmentSetting = () => {
|
const handleAppointmentSetting = async () => {
|
||||||
|
// 获取当前机构的预约配置
|
||||||
|
try {
|
||||||
|
const res = await getAppointmentConfig()
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
// 回显已有配置
|
||||||
|
appointmentSettingForm.value = {
|
||||||
|
cancelAppointmentType: res.data.cancelAppointmentType || 'YEAR',
|
||||||
|
cancelAppointmentCount: res.data.cancelAppointmentCount || 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 无配置时使用默认值
|
||||||
|
appointmentSettingForm.value = {
|
||||||
|
cancelAppointmentType: 'YEAR',
|
||||||
|
cancelAppointmentCount: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取预约配置失败:', error)
|
||||||
|
appointmentSettingForm.value = {
|
||||||
|
cancelAppointmentType: 'YEAR',
|
||||||
|
cancelAppointmentCount: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
appointmentSettingDialog.value = true
|
appointmentSettingDialog.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 预约设置确定
|
// 预约设置确定
|
||||||
const handleAppointmentSettingConfirm = () => {
|
const handleAppointmentSettingConfirm = async () => {
|
||||||
// 这里可以添加表单验证和提交逻辑
|
try {
|
||||||
console.log('预约设置提交:', appointmentSettingForm.value)
|
const res = await saveAppointmentConfig({
|
||||||
|
cancelAppointmentType: appointmentSettingForm.value.cancelAppointmentType,
|
||||||
|
cancelAppointmentCount: appointmentSettingForm.value.cancelAppointmentCount,
|
||||||
|
validFlag: 1
|
||||||
|
})
|
||||||
|
if (res.code === 200) {
|
||||||
ElMessage.success('预约设置保存成功')
|
ElMessage.success('预约设置保存成功')
|
||||||
appointmentSettingDialog.value = false
|
appointmentSettingDialog.value = false
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.msg || '保存失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('保存预约配置失败:', error)
|
||||||
|
ElMessage.error('保存失败')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 预约设置取消
|
// 预约设置取消
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<el-form-item label="所属机构" prop="institution" class="filter-item">
|
<el-form-item label="所属机构" prop="institution" class="filter-item">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="queryParams.institution"
|
v-model="queryParams.institution"
|
||||||
placeholder="演示医院"
|
placeholder="请选择"
|
||||||
clearable
|
clearable
|
||||||
style="width: 150px"
|
style="width: 150px"
|
||||||
:popper-append-to-body="false"
|
:popper-append-to-body="false"
|
||||||
|
|||||||
@@ -510,8 +510,7 @@ export default {
|
|||||||
this.closeContextMenu();
|
this.closeContextMenu();
|
||||||
}
|
}
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
const errorMsg = error.message || '取消预约失败,请稍后重试';
|
console.error('取消预约失败:', error);
|
||||||
ElMessage.error(`取消预约失败:${errorMsg}`);
|
|
||||||
this.closeContextMenu();
|
this.closeContextMenu();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -590,12 +589,10 @@ export default {
|
|||||||
|
|
||||||
ElMessage.success('预约成功,号源已锁定。患者到院签到时需缴费取号。');
|
ElMessage.success('预约成功,号源已锁定。患者到院签到时需缴费取号。');
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
// 显示具体的错误信息
|
console.error('预约失败:', error);
|
||||||
const errorMessage = error.response?.data?.msg || error.response?.data?.message || '预约失败,请稍后重试。';
|
|
||||||
ElMessage.error(errorMessage);
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error('预约信息格式错误,请重新操作。');
|
console.error('操作异常:', error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 切换侧边栏显示/隐藏
|
// 切换侧边栏显示/隐藏
|
||||||
|
|||||||
@@ -628,11 +628,11 @@ async function handleSaveDiagnosis() {
|
|||||||
// 开始加载状态,防止重复提交
|
// 开始加载状态,防止重复提交
|
||||||
saveLoading.value = true;
|
saveLoading.value = true;
|
||||||
|
|
||||||
// 保存前按排序号排序,并转换日期格式
|
// 保存前按排序号排序,并转换日期格式为ISO字符串
|
||||||
const diagnosisChildList = form.value.diagnosisList.map(item => ({
|
const diagnosisChildList = form.value.diagnosisList.map(item => ({
|
||||||
...item,
|
...item,
|
||||||
onsetDate: item.onsetDate ? new Date(item.onsetDate) : null,
|
onsetDate: item.onsetDate ? new Date(item.onsetDate).toISOString() : null,
|
||||||
diagnosisTime: item.diagnosisTime ? new Date(item.diagnosisTime) : null
|
diagnosisTime: item.diagnosisTime ? new Date(item.diagnosisTime).toISOString() : null
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 调用保存诊断接口
|
// 调用保存诊断接口
|
||||||
|
|||||||
@@ -194,10 +194,13 @@ import {ElMessage} from 'element-plus';
|
|||||||
import {getLocationTree} from '@/views/charge/outpatientregistration/components/outpatientregistration';
|
import {getLocationTree} from '@/views/charge/outpatientregistration/components/outpatientregistration';
|
||||||
import {listInspectionPackage, delInspectionPackage} from '@/api/system/inspectionPackage';
|
import {listInspectionPackage, delInspectionPackage} from '@/api/system/inspectionPackage';
|
||||||
import { getTenantPage } from '@/api/system/tenant';
|
import { getTenantPage } from '@/api/system/tenant';
|
||||||
|
import useTagsViewStore from '@/store/modules/tagsView';
|
||||||
|
|
||||||
|
|
||||||
// 创建路由实例
|
// 创建路由实例
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const tagsViewStore = useTagsViewStore();
|
||||||
|
|
||||||
|
|
||||||
// 侧边栏状态
|
// 侧边栏状态
|
||||||
const sidebarActive = ref(false);
|
const sidebarActive = ref(false);
|
||||||
@@ -207,7 +210,6 @@ const departments = ref([]);
|
|||||||
|
|
||||||
// 获取科室数据 - 与门诊挂号页面保持一致
|
// 获取科室数据 - 与门诊挂号页面保持一致
|
||||||
function getDepartmentList() {
|
function getDepartmentList() {
|
||||||
console.log('调用getLocationTree API...');
|
|
||||||
getLocationTree().then((response) => {
|
getLocationTree().then((response) => {
|
||||||
|
|
||||||
|
|
||||||
@@ -430,13 +432,17 @@ const fetchTenantList = async () => {
|
|||||||
// 初始化数据
|
// 初始化数据
|
||||||
// 整合后的 onMounted 钩子
|
// 整合后的 onMounted 钩子
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// 1. 加载科室数据
|
// 1. 加载科室数据(不需要等待)
|
||||||
getDepartmentList();
|
getDepartmentList();
|
||||||
|
|
||||||
// 2. 加载机构列表 (包含默认选中逻辑)
|
// 2. 并行发起:机构列表 + 表格数据,减少串行等待
|
||||||
await fetchTenantList();
|
const tenantPromise = fetchTenantList();
|
||||||
|
|
||||||
// 3. 等待 DOM 更新
|
// 先用默认参数(不带 tenantId)立刻加载表格,让用户尽快看到数据
|
||||||
|
loadData();
|
||||||
|
|
||||||
|
// 3. 等待机构列表加载完成
|
||||||
|
await tenantPromise;
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
// 4. 防御性检查
|
// 4. 防御性检查
|
||||||
@@ -444,9 +450,10 @@ onMounted(async () => {
|
|||||||
selectedTenantId.value = tenantOptions.value[0].value;
|
selectedTenantId.value = tenantOptions.value[0].value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 加载表格数据
|
// 5. 如果选中了机构,用 tenantId 重新过滤一次
|
||||||
|
if (selectedTenantId.value) {
|
||||||
loadData();
|
loadData();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 过滤后的数据 - 现在直接从API获取,这里保留前端过滤作为补充
|
// 过滤后的数据 - 现在直接从API获取,这里保留前端过滤作为补充
|
||||||
@@ -583,51 +590,27 @@ const pageButtons = computed(() => {
|
|||||||
|
|
||||||
// 处理新增
|
// 处理新增
|
||||||
function handleAdd() {
|
function handleAdd() {
|
||||||
router.push({
|
window.location.href = '/maintainSystem/Inspection?tab=2&mode=add';
|
||||||
path: '/maintainSystem/Inspection',
|
|
||||||
query: {
|
|
||||||
tab: '2',
|
|
||||||
mode: 'add'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理编辑
|
// 处理编辑
|
||||||
function handleEdit(item) {
|
function handleEdit(item) {
|
||||||
// 跳转到套餐设置主界面,并传递套餐ID用于加载数据
|
|
||||||
// 后端接口使用 basicInformationId 作为路径参数
|
|
||||||
const packageId = item.basicInformationId || item.id;
|
const packageId = item.basicInformationId || item.id;
|
||||||
if (!packageId) {
|
if (!packageId) {
|
||||||
ElMessage.error('无法获取套餐ID,请刷新页面后重试');
|
ElMessage.error('无法获取套餐ID,请刷新页面后重试');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
router.push({
|
window.location.href = `/maintainSystem/Inspection?tab=2&mode=edit&packageId=${packageId}`;
|
||||||
path: '/maintainSystem/Inspection',
|
|
||||||
query: {
|
|
||||||
tab: '2',
|
|
||||||
packageId: packageId,
|
|
||||||
mode: 'edit'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理查看
|
// 处理查看
|
||||||
function handleView(item) {
|
function handleView(item) {
|
||||||
// 跳转到套餐设置主界面,并传递套餐ID用于加载数据
|
|
||||||
// 后端接口使用 basicInformationId 作为路径参数
|
|
||||||
const packageId = item.basicInformationId || item.id;
|
const packageId = item.basicInformationId || item.id;
|
||||||
if (!packageId) {
|
if (!packageId) {
|
||||||
ElMessage.error('无法获取套餐ID,请刷新页面后重试');
|
ElMessage.error('无法获取套餐ID,请刷新页面后重试');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
router.push({
|
window.location.href = `/maintainSystem/Inspection?tab=2&mode=view&packageId=${packageId}`;
|
||||||
path: '/maintainSystem/Inspection',
|
|
||||||
query: {
|
|
||||||
tab: '2',
|
|
||||||
packageId: packageId,
|
|
||||||
mode: 'view'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理删除
|
// 处理删除
|
||||||
|
|||||||
@@ -813,7 +813,8 @@ import {
|
|||||||
updateInspectionPackage,
|
updateInspectionPackage,
|
||||||
getInspectionPackage,
|
getInspectionPackage,
|
||||||
listInspectionPackageDetails,
|
listInspectionPackageDetails,
|
||||||
saveInspectionPackageDetails
|
saveInspectionPackageDetails,
|
||||||
|
batchSaveInspectionPackageDetails
|
||||||
} from '@/api/system/inspectionPackage';
|
} from '@/api/system/inspectionPackage';
|
||||||
import { getTenantPage } from '@/api/system/tenant';
|
import { getTenantPage } from '@/api/system/tenant';
|
||||||
import {deptTreeSelect} from '@/api/system/user';
|
import {deptTreeSelect} from '@/api/system/user';
|
||||||
@@ -1470,6 +1471,8 @@ const packageItems = ref([]);
|
|||||||
const packageMode = ref('add');
|
const packageMode = ref('add');
|
||||||
// 当前编辑的套餐ID(用于编辑和查看模式)
|
// 当前编辑的套餐ID(用于编辑和查看模式)
|
||||||
const currentPackageId = ref(null);
|
const currentPackageId = ref(null);
|
||||||
|
// 正在从后端加载套餐数据的标志,加载期间跳过 calculateAmounts 防止覆盖后端金额
|
||||||
|
const isLoadingPackage = ref(false);
|
||||||
// 是否为查看模式(只读)
|
// 是否为查看模式(只读)
|
||||||
const isViewMode = computed(() => packageMode.value === 'view');
|
const isViewMode = computed(() => packageMode.value === 'view');
|
||||||
|
|
||||||
@@ -2364,6 +2367,10 @@ const savePackageData = async (basicInfo, detailData) => {
|
|||||||
if (isEditMode) {
|
if (isEditMode) {
|
||||||
// 编辑模式:调用更新API
|
// 编辑模式:调用更新API
|
||||||
basicResponse = await updateInspectionPackage(basicInfo);
|
basicResponse = await updateInspectionPackage(basicInfo);
|
||||||
|
if (basicResponse.code !== 200) {
|
||||||
|
loading.close();
|
||||||
|
throw new Error(basicResponse.msg || '更新基本信息失败');
|
||||||
|
}
|
||||||
packageId = currentPackageId.value;
|
packageId = currentPackageId.value;
|
||||||
} else {
|
} else {
|
||||||
// 新增模式:调用新增API
|
// 新增模式:调用新增API
|
||||||
@@ -2390,12 +2397,24 @@ const savePackageData = async (basicInfo, detailData) => {
|
|||||||
// 验证套餐ID是否存在
|
// 验证套餐ID是否存在
|
||||||
if (!packageId) {
|
if (!packageId) {
|
||||||
loading.close();
|
loading.close();
|
||||||
console.error('无法从响应中获取套餐ID,完整响应:', basicResponse);
|
|
||||||
throw new Error('保存成功但未返回套餐ID。请检查后端接口是否正确返回了packageId或id字段');
|
throw new Error('保存成功但未返回套餐ID。请检查后端接口是否正确返回了packageId或id字段');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 分别保存每个明细数据到明细表,并将后端返回的 id 回填到 packageItems
|
// 2. 保存明细数据:编辑模式用批量接口(先删旧后插新),新增模式逐条新增
|
||||||
|
if (isEditMode) {
|
||||||
|
// 编辑模式:调用批量保存接口,后端会先删除旧明细再插入新明细
|
||||||
|
const batchPayload = {
|
||||||
|
basicInformationId: packageId,
|
||||||
|
details: detailData.map(item => ({ ...item, packageId: packageId }))
|
||||||
|
};
|
||||||
|
const batchResponse = await batchSaveInspectionPackageDetails(batchPayload);
|
||||||
|
if (batchResponse.code !== 200) {
|
||||||
|
loading.close();
|
||||||
|
throw new Error(batchResponse.msg || '保存明细数据失败');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 新增模式:逐条保存并回填后端生成的明细 id
|
||||||
for (let i = 0; i < detailData.length; i++) {
|
for (let i = 0; i < detailData.length; i++) {
|
||||||
const detailItem = {
|
const detailItem = {
|
||||||
...detailData[i],
|
...detailData[i],
|
||||||
@@ -2410,19 +2429,25 @@ const savePackageData = async (basicInfo, detailData) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 回填后端生成的明细 id,防止取消编辑时被误判为新增行
|
// 回填后端生成的明细 id,防止取消编辑时被误判为新增行
|
||||||
// 后端返回字段名是 detailId(见 InspectionPackageDetail.java)
|
|
||||||
if (detailResponse.data && (detailResponse.data.detailId || detailResponse.data.id)) {
|
if (detailResponse.data && (detailResponse.data.detailId || detailResponse.data.id)) {
|
||||||
packageItems.value[i].id = detailResponse.data.detailId || detailResponse.data.id;
|
packageItems.value[i].id = detailResponse.data.detailId || detailResponse.data.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 关闭加载提示
|
// 关闭加载提示
|
||||||
loading.close();
|
loading.close();
|
||||||
|
|
||||||
ElMessage.success('保存成功');
|
ElMessage.success('保存成功');
|
||||||
|
|
||||||
// 保存成功后重置表单
|
if (isEditMode) {
|
||||||
|
// 编辑模式:重新加载最新数据,保持在编辑页面
|
||||||
|
loadInspectionPackage(String(packageId));
|
||||||
|
} else {
|
||||||
|
// 新增模式:保存成功后重置表单
|
||||||
doResetForm();
|
doResetForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 确保在错误时也关闭loading
|
// 确保在错误时也关闭loading
|
||||||
@@ -2495,7 +2520,7 @@ const doResetForm = () => {
|
|||||||
// 清空明细数据
|
// 清空明细数据
|
||||||
packageItems.value = [];
|
packageItems.value = [];
|
||||||
|
|
||||||
// 重置模式为新增
|
// 重置为新增模式(仅在外部未指定 mode 时,由调用方负责设置 packageMode)
|
||||||
packageMode.value = 'add';
|
packageMode.value = 'add';
|
||||||
currentPackageId.value = null;
|
currentPackageId.value = null;
|
||||||
|
|
||||||
@@ -2527,6 +2552,9 @@ const loadInspectionPackage = async (packageId) => {
|
|||||||
const basicData = basicResponse.data;
|
const basicData = basicResponse.data;
|
||||||
const detailData = detailResponse.data || [];
|
const detailData = detailResponse.data || [];
|
||||||
|
|
||||||
|
// 【关键】暂停 packageItems 的 watch,防止赋值时触发 calculateAmounts 覆盖后端返回的金额
|
||||||
|
isLoadingPackage.value = true;
|
||||||
|
|
||||||
// 填充基本信息
|
// 填充基本信息
|
||||||
packageLevel.value = basicData.packageLevel;
|
packageLevel.value = basicData.packageLevel;
|
||||||
packageName.value = basicData.packageName;
|
packageName.value = basicData.packageName;
|
||||||
@@ -2536,8 +2564,8 @@ const loadInspectionPackage = async (packageId) => {
|
|||||||
showPackageName.value = basicData.showPackageName !== false;
|
showPackageName.value = basicData.showPackageName !== false;
|
||||||
generateServiceFee.value = basicData.generateServiceFee !== false;
|
generateServiceFee.value = basicData.generateServiceFee !== false;
|
||||||
enablePackagePrice.value = basicData.enablePackagePrice !== false;
|
enablePackagePrice.value = basicData.enablePackagePrice !== false;
|
||||||
packageAmount.value = basicData.packageAmount || 0.00;
|
packageAmount.value = parseFloat(basicData.packageAmount || 0);
|
||||||
serviceFee.value = basicData.serviceFee || 0.00;
|
serviceFee.value = parseFloat(basicData.serviceFee || 0);
|
||||||
selectedLisGroup.value = basicData.lisGroup || '';
|
selectedLisGroup.value = basicData.lisGroup || '';
|
||||||
bloodVolume.value = basicData.bloodVolume || '';
|
bloodVolume.value = basicData.bloodVolume || '';
|
||||||
remarks.value = basicData.remarks || '';
|
remarks.value = basicData.remarks || '';
|
||||||
@@ -2559,11 +2587,15 @@ const loadInspectionPackage = async (packageId) => {
|
|||||||
origin: item.origin || ''
|
origin: item.origin || ''
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// 恢复监听
|
||||||
|
isLoadingPackage.value = false;
|
||||||
|
|
||||||
loading.close();
|
loading.close();
|
||||||
ElMessage.success('套餐数据加载成功');
|
ElMessage.success('套餐数据加载成功');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载套餐数据失败:', error);
|
isLoadingPackage.value = false;
|
||||||
|
|
||||||
ElMessage.error(error.message || '加载套餐数据失败');
|
ElMessage.error(error.message || '加载套餐数据失败');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -2597,47 +2629,43 @@ watch(activity_category_code, (newVal) => {
|
|||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关键修复:
|
* 统一监听 query 变化,处理 tab 切换和套餐数据加载。
|
||||||
* 从“套餐管理”跳转到这里时,通常只是改变 query(packageId/mode/tab),组件不会重新挂载,
|
* 使用 watch(route) 深度监听,确保在 keep-alive 场景下同路由 query 变化也能响应。
|
||||||
* 所以仅靠 onMounted 读取一次 query 会导致“查看/修改”出现空白,刷新才正常。
|
|
||||||
* 这里监听 query 变化,自动切换 tab 并加载套餐数据。
|
|
||||||
*/
|
*/
|
||||||
watch(
|
|
||||||
() => route.query.tab,
|
|
||||||
(tab) => {
|
|
||||||
if (tab === '0' || tab === '1' || tab === '2') {
|
|
||||||
activeNav.value = parseInt(String(tab));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
const applyRouteForPackage = async (query) => {
|
const applyRouteForPackage = async (query) => {
|
||||||
|
const tab = query?.tab;
|
||||||
const packageId = query?.packageId;
|
const packageId = query?.packageId;
|
||||||
const mode = query?.mode || 'add';
|
const mode = query?.mode || 'add';
|
||||||
|
|
||||||
// 设置当前模式和套餐ID
|
// 先切换 tab
|
||||||
packageMode.value = mode;
|
if (tab === '0' || tab === '1' || tab === '2') {
|
||||||
currentPackageId.value = packageId ? String(packageId) : null;
|
activeNav.value = parseInt(tab);
|
||||||
|
};
|
||||||
|
|
||||||
// 如果是新增模式,重置表单
|
// 只有 tab=2(套餐设置)才处理套餐数据
|
||||||
|
if (activeNav.value !== 2) return;
|
||||||
|
|
||||||
|
// 新增模式:重置表单,然后明确设置 mode
|
||||||
if (mode === 'add' || !packageId) {
|
if (mode === 'add' || !packageId) {
|
||||||
activeNav.value = 2;
|
doResetForm(); // 内部会设 packageMode='add',符合预期
|
||||||
doResetForm();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
activeNav.value = 2;
|
// 编辑/查看模式:先设置 mode 和 ID,再加载数据
|
||||||
|
packageMode.value = mode;
|
||||||
|
currentPackageId.value = String(packageId);
|
||||||
|
|
||||||
await nextTick();
|
await nextTick();
|
||||||
loadInspectionPackage(String(packageId));
|
loadInspectionPackage(String(packageId));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 监听整个 route.query,确保任何 query 变化(tab/packageId/mode)都能触发
|
||||||
watch(
|
watch(
|
||||||
() => route.query.packageId,
|
() => route.query,
|
||||||
async () => {
|
(newQuery) => {
|
||||||
await applyRouteForPackage(route.query);
|
applyRouteForPackage(newQuery);
|
||||||
},
|
},
|
||||||
{ immediate: true, flush: 'post' }
|
{ immediate: true, deep: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
// 兜底:如果该页面被 keep-alive 缓存,从别的页面返回时不会触发 onMounted
|
// 兜底:如果该页面被 keep-alive 缓存,从别的页面返回时不会触发 onMounted
|
||||||
@@ -2652,11 +2680,14 @@ onBeforeRouteUpdate((to) => {
|
|||||||
|
|
||||||
// 监听生成服务费选项变更
|
// 监听生成服务费选项变更
|
||||||
watch(generateServiceFee, (newVal) => {
|
watch(generateServiceFee, (newVal) => {
|
||||||
|
if (isLoadingPackage.value) return;
|
||||||
calculateAmounts();
|
calculateAmounts();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听套餐项目变化
|
// 监听套餐项目变化
|
||||||
watch(packageItems, (newVal) => {
|
watch(packageItems, (newVal) => {
|
||||||
|
// 加载期间跳过计算,防止覆盖后端返回的金额
|
||||||
|
if (isLoadingPackage.value) return;
|
||||||
calculateAmounts();
|
calculateAmounts();
|
||||||
}, { deep: true });
|
}, { deep: true });
|
||||||
// 样本类型数据
|
// 样本类型数据
|
||||||
|
|||||||
@@ -154,9 +154,9 @@
|
|||||||
v-model="formData.serviceFee"
|
v-model="formData.serviceFee"
|
||||||
:precision="2"
|
:precision="2"
|
||||||
:min="0"
|
:min="0"
|
||||||
placeholder="请输入服务费"
|
placeholder="自动合计"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
:disabled="isReadOnly"
|
disabled
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -281,6 +281,12 @@
|
|||||||
<span v-else>{{ row.quantity }}</span>
|
<span v-else>{{ row.quantity }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column prop="unit" label="单位" width="80" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row.editing">{{ row.unit || '-' }}</span>
|
||||||
|
<span v-else>{{ row.unit || '-' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="unitPrice" label="单价" width="150" align="center">
|
<el-table-column prop="unitPrice" label="单价" width="150" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-input
|
<el-input
|
||||||
@@ -309,7 +315,7 @@
|
|||||||
placeholder="服务费"
|
placeholder="服务费"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
:controls="false"
|
:controls="false"
|
||||||
@change="calculateTotal(row)"
|
@input="(val) => handleServiceChargeInput(val, row)"
|
||||||
/>
|
/>
|
||||||
<span v-else>{{ row.serviceCharge?.toFixed(2) || '0.00' }}</span>
|
<span v-else>{{ row.serviceCharge?.toFixed(2) || '0.00' }}</span>
|
||||||
</template>
|
</template>
|
||||||
@@ -539,6 +545,7 @@ function loadPackageData(data) {
|
|||||||
days: item.days || '',
|
days: item.days || '',
|
||||||
quantity: item.quantity || 1,
|
quantity: item.quantity || 1,
|
||||||
unitPrice: item.unitPrice || 0,
|
unitPrice: item.unitPrice || 0,
|
||||||
|
unit: item.unit || '',
|
||||||
amount: item.amount || 0,
|
amount: item.amount || 0,
|
||||||
serviceCharge: item.serviceCharge || 0,
|
serviceCharge: item.serviceCharge || 0,
|
||||||
total: item.total || 0,
|
total: item.total || 0,
|
||||||
@@ -553,6 +560,9 @@ function loadPackageData(data) {
|
|||||||
|
|
||||||
console.log('formData 加载后:', formData)
|
console.log('formData 加载后:', formData)
|
||||||
console.log('detailData 加载后:', detailData.value)
|
console.log('detailData 加载后:', detailData.value)
|
||||||
|
|
||||||
|
// 加载数据后自动计算总服务费
|
||||||
|
calculateTotalServiceFee()
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@@ -781,10 +791,12 @@ async function loadDiagnosisTreatmentItem(itemId, itemData) {
|
|||||||
async function loadDiagnosisTreatmentList(forceRefresh = false) {
|
async function loadDiagnosisTreatmentList(forceRefresh = false) {
|
||||||
// 如果不是强制刷新且已有数据且未过期,直接返回
|
// 如果不是强制刷新且已有数据且未过期,直接返回
|
||||||
if (!forceRefresh && diagnosisTreatmentList.value.length > 0) {
|
if (!forceRefresh && diagnosisTreatmentList.value.length > 0) {
|
||||||
return
|
// 由于缓存过期时间改为0,始终视为过期,需要重新加载
|
||||||
|
// 这里直接跳过,不返回,让它重新加载
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从session缓存读取
|
// 从session缓存读取
|
||||||
|
let useCache = false
|
||||||
try {
|
try {
|
||||||
const cachedData = cache.session.getJSON(DIAGNOSIS_TREATMENT_CACHE_KEY)
|
const cachedData = cache.session.getJSON(DIAGNOSIS_TREATMENT_CACHE_KEY)
|
||||||
if (cachedData && cachedData.timestamp) {
|
if (cachedData && cachedData.timestamp) {
|
||||||
@@ -877,6 +889,7 @@ function handleAddRow() {
|
|||||||
frequency: '',
|
frequency: '',
|
||||||
days: '',
|
days: '',
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
|
unit: '',
|
||||||
unitPrice: 0,
|
unitPrice: 0,
|
||||||
amount: 0,
|
amount: 0,
|
||||||
serviceCharge: 0,
|
serviceCharge: 0,
|
||||||
@@ -914,6 +927,7 @@ function handleDeleteRow(index) {
|
|||||||
}).then(() => {
|
}).then(() => {
|
||||||
detailData.value.splice(index, 1)
|
detailData.value.splice(index, 1)
|
||||||
calculatePackagePrice()
|
calculatePackagePrice()
|
||||||
|
calculateTotalServiceFee()
|
||||||
ElMessage.success('删除成功')
|
ElMessage.success('删除成功')
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
}
|
}
|
||||||
@@ -1027,7 +1041,9 @@ function handleItemSelect(row) {
|
|||||||
row.itemName = item.name || item.itemName || ''
|
row.itemName = item.name || item.itemName || ''
|
||||||
row.code = item.busNo || item.code || item.itemCode || ''
|
row.code = item.busNo || item.code || item.itemCode || ''
|
||||||
row.unitPrice = parseFloat(item.retailPrice || item.unitPrice || item.price || 0)
|
row.unitPrice = parseFloat(item.retailPrice || item.unitPrice || item.price || 0)
|
||||||
console.log('设置单价:', row.unitPrice)
|
// permittedUnitCode_dictText是字典翻译后的值,permittedUnitCode是后端返回的原始值
|
||||||
|
row.unit = item.permittedUnitCode_dictText || item.permittedUnitCode || ''
|
||||||
|
|
||||||
|
|
||||||
// 缓存选中的项目
|
// 缓存选中的项目
|
||||||
loadDiagnosisTreatmentItem(row.itemId, item)
|
loadDiagnosisTreatmentItem(row.itemId, item)
|
||||||
@@ -1044,10 +1060,23 @@ function calculateAmount(row) {
|
|||||||
calculateTotal(row)
|
calculateTotal(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理服务费输入
|
||||||
|
function handleServiceChargeInput(val, row) {
|
||||||
|
row.serviceCharge = val || 0
|
||||||
|
calculateTotal(row)
|
||||||
|
}
|
||||||
|
|
||||||
// 计算总金额
|
// 计算总金额
|
||||||
function calculateTotal(row) {
|
function calculateTotal(row) {
|
||||||
row.total = (row.amount || 0) + (row.serviceCharge || 0)
|
row.total = (row.amount || 0) + (row.serviceCharge || 0)
|
||||||
calculatePackagePrice()
|
calculatePackagePrice()
|
||||||
|
calculateTotalServiceFee()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算总服务费(合计所有明细行的服务费)
|
||||||
|
function calculateTotalServiceFee() {
|
||||||
|
const totalServiceFee = detailData.value.reduce((sum, item) => sum + (item.serviceCharge || 0), 0)
|
||||||
|
formData.serviceFee = totalServiceFee
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算套餐金额(应用折扣)
|
// 计算套餐金额(应用折扣)
|
||||||
@@ -1203,6 +1232,7 @@ async function handleSave() {
|
|||||||
frequency: item.frequency || '',
|
frequency: item.frequency || '',
|
||||||
days: item.days || '',
|
days: item.days || '',
|
||||||
quantity: parseInt(item.quantity) || 1,
|
quantity: parseInt(item.quantity) || 1,
|
||||||
|
unit: item.unit || '',
|
||||||
unitPrice: parseFloat(item.unitPrice) || 0,
|
unitPrice: parseFloat(item.unitPrice) || 0,
|
||||||
amount: parseFloat(item.amount) || 0,
|
amount: parseFloat(item.amount) || 0,
|
||||||
serviceCharge: parseFloat(item.serviceCharge) || 0,
|
serviceCharge: parseFloat(item.serviceCharge) || 0,
|
||||||
|
|||||||
Reference in New Issue
Block a user