Merge branch 'develop' of https://gitea.gentronhealth.com/wangyizhe/his into develop
This commit is contained in:
10
md/test.html
10
md/test.html
@@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>测试合并11111</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -29,6 +29,8 @@ import com.openhis.web.chargemanage.dto.PatientMetadata;
|
||||
import com.openhis.web.chargemanage.dto.PractitionerMetadata;
|
||||
import com.openhis.web.chargemanage.dto.ReprintRegistrationDto;
|
||||
import com.openhis.web.chargemanage.mapper.OutpatientRegistrationAppMapper;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageCandidateExclusion;
|
||||
import com.openhis.triageandqueuemanage.service.TriageCandidateExclusionService;
|
||||
import com.openhis.web.paymentmanage.appservice.IPaymentRecService;
|
||||
import com.openhis.web.paymentmanage.dto.CancelPaymentDto;
|
||||
import com.openhis.web.paymentmanage.dto.CancelRegPaymentDto;
|
||||
@@ -38,12 +40,15 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 门诊挂号 应用实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistrationAppService {
|
||||
|
||||
@@ -77,6 +82,9 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
||||
@Resource
|
||||
IPatientIdentifierService patientIdentifierService;
|
||||
|
||||
@Resource
|
||||
TriageCandidateExclusionService triageCandidateExclusionService;
|
||||
|
||||
/**
|
||||
* 门诊挂号 - 查询患者信息
|
||||
*
|
||||
@@ -308,6 +316,47 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
||||
new Page<>(pageNo, pageSize), EncounterClass.AMB.getValue(), EncounterStatus.IN_PROGRESS.getValue(),
|
||||
ParticipantType.ADMITTER.getCode(), ParticipantType.REGISTRATION_DOCTOR.getCode(), queryWrapper,
|
||||
ChargeItemContext.REGISTER.getValue(), PaymentStatus.SUCCESS.getValue());
|
||||
|
||||
// 过滤候选池排除列表(如果是从智能候选池查询,排除已加入队列的患者)
|
||||
// 检查请求参数 excludeFromCandidatePool,如果为 true 或未设置,则过滤排除列表
|
||||
String excludeParam = request.getParameter("excludeFromCandidatePool");
|
||||
boolean shouldExclude = excludeParam == null || "true".equalsIgnoreCase(excludeParam);
|
||||
if (shouldExclude && currentDayEncounter != null && !currentDayEncounter.getRecords().isEmpty()) {
|
||||
try {
|
||||
// 获取当前租户和日期
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
// 查询排除列表
|
||||
List<TriageCandidateExclusion> exclusions = triageCandidateExclusionService.list(
|
||||
new LambdaQueryWrapper<TriageCandidateExclusion>()
|
||||
.eq(TriageCandidateExclusion::getTenantId, tenantId)
|
||||
.eq(TriageCandidateExclusion::getExclusionDate, today)
|
||||
.eq(TriageCandidateExclusion::getDeleteFlag, "0")
|
||||
);
|
||||
|
||||
if (exclusions != null && !exclusions.isEmpty()) {
|
||||
// 构建排除的 encounterId 集合
|
||||
Set<Long> excludedEncounterIds = exclusions.stream()
|
||||
.map(TriageCandidateExclusion::getEncounterId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 过滤结果
|
||||
List<CurrentDayEncounterDto> filteredRecords = currentDayEncounter.getRecords().stream()
|
||||
.filter(e -> e.getEncounterId() == null || !excludedEncounterIds.contains(e.getEncounterId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 更新分页结果
|
||||
currentDayEncounter.setRecords(filteredRecords);
|
||||
currentDayEncounter.setTotal(filteredRecords.size());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 如果过滤失败,记录日志但不影响正常查询
|
||||
log.warn("过滤候选池排除列表失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
currentDayEncounter.getRecords().forEach(e -> {
|
||||
// 性别
|
||||
e.setGenderEnum_enumText(EnumUtils.getInfoByValue(AdministrativeGender.class, e.getGenderEnum()));
|
||||
|
||||
@@ -13,7 +13,7 @@ import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class DoctorPhraesAppServiceImpl implements IDoctorPhraseAppService {
|
||||
public class DoctorPhraseAppServiceImpl implements IDoctorPhraseAppService {
|
||||
|
||||
@Resource
|
||||
private IDoctorPhraseService doctorPhraseService;
|
||||
@@ -293,11 +293,8 @@ public class ATDManageAppServiceImpl implements IATDManageAppService {
|
||||
if (admissionPatientInfoDto.getPriorityEnum() != null) {
|
||||
// 更新患者病情
|
||||
encounterService.updatePriorityEnumById(encounterId, admissionPatientInfoDto.getPriorityEnum());
|
||||
// 将之前的住院参与者更新为已完成
|
||||
Integer result = encounterParticipantService.updateEncounterParticipantsStatus(encounterId);
|
||||
if (result == 0) {
|
||||
return R.fail("患者信息更新失败,请联系管理员");
|
||||
}
|
||||
// 将之前的住院参与者更新为已完成(如果存在的话)
|
||||
encounterParticipantService.updateEncounterParticipantsStatus(encounterId);
|
||||
// 更新住院参与者
|
||||
// 住院医生
|
||||
encounterParticipantService.creatEncounterParticipants(encounterId, startTime,
|
||||
|
||||
@@ -308,12 +308,20 @@ public class PatientInformationServiceImpl implements IPatientInformationService
|
||||
* @return 患者信息
|
||||
*/
|
||||
private Patient handlePatientInfo(PatientBaseInfoDto patientInfoDto) {
|
||||
Patient patient = new Patient();
|
||||
patient.setId(patientInfoDto.getId());
|
||||
if (patientInfoDto.getId() == null) {
|
||||
patient.setBusNo(assignSeqUtil.getSeq(AssignSeqEnum.PATIENT_NUM.getPrefix(), 10));
|
||||
patientInfoDto.setActiveFlag(PublicationStatus.ACTIVE.getValue()); // 默认启用
|
||||
Patient patient;
|
||||
if (patientInfoDto.getId() != null) {
|
||||
// 更新现有患者信息
|
||||
patient = patientService.getById(patientInfoDto.getId());
|
||||
if (patient == null) {
|
||||
throw new ServiceException("患者信息不存在,无法更新");
|
||||
}
|
||||
} else {
|
||||
// 新增患者信息
|
||||
patient = new Patient();
|
||||
patient.setBusNo(assignSeqUtil.getSeq(AssignSeqEnum.PATIENT_NUM.getPrefix(), 10));
|
||||
patient.setActiveFlag(PublicationStatus.ACTIVE.getValue()); // 默认启用
|
||||
}
|
||||
|
||||
patient.setName(patientInfoDto.getName()); // 患者姓名
|
||||
patient.setPyStr(ChineseConvertUtils.toPinyinFirstLetter(patientInfoDto.getName())); // 拼音首拼
|
||||
patient.setWbStr(ChineseConvertUtils.toWBFirstLetter(patientInfoDto.getName())); // 五笔首拼
|
||||
@@ -337,7 +345,14 @@ public class PatientInformationServiceImpl implements IPatientInformationService
|
||||
patient.setDeceasedDate(patientInfoDto.getDeceasedDate()); // 死亡时间
|
||||
patient.setNationalityCode(patientInfoDto.getNationalityCode());// 民族
|
||||
patient.setActiveFlag(patientInfoDto.getActiveFlag());// 活动标识
|
||||
patientService.saveOrUpdate(patient);
|
||||
|
||||
if (patientInfoDto.getId() != null) {
|
||||
// 更新操作
|
||||
patientService.updateById(patient);
|
||||
} else {
|
||||
// 新增操作
|
||||
patientService.save(patient);
|
||||
}
|
||||
|
||||
return patient;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.openhis.web.triageandqueuemanage.appservice;
|
||||
|
||||
import com.core.common.core.domain.R;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueActionReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAddReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAdjustReq;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public interface TriageQueueAppService {
|
||||
R<?> list(Long organizationId, LocalDate date);
|
||||
R<?> add(TriageQueueAddReq req);
|
||||
R<?> remove(Long id);
|
||||
R<?> adjust(TriageQueueAdjustReq req);
|
||||
/** 选呼:将之前叫号中置为完成,选中的置为叫号中 */
|
||||
R<?> call(TriageQueueActionReq req);
|
||||
/** 完成:叫号中 -> 完成(移出列表),并自动推进下一个等待为叫号中 */
|
||||
R<?> complete(TriageQueueActionReq req);
|
||||
/** 过号重排:叫号中 -> 跳过并移到末尾,并自动推进下一个等待为叫号中 */
|
||||
R<?> requeue(TriageQueueActionReq req);
|
||||
/** 跳过:兼容前端按钮(当前实现等同于过号重排) */
|
||||
R<?> skip(TriageQueueActionReq req);
|
||||
/** 下一患者:当前叫号中 -> 完成,下一位等待 -> 叫号中 */
|
||||
R<?> next(TriageQueueActionReq req);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,556 @@
|
||||
package com.openhis.web.triageandqueuemanage.appservice.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.core.common.core.domain.R;
|
||||
import com.core.common.utils.SecurityUtils;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageQueueItem;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageCandidateExclusion;
|
||||
import com.openhis.triageandqueuemanage.service.TriageQueueItemService;
|
||||
import com.openhis.triageandqueuemanage.service.TriageCandidateExclusionService;
|
||||
import com.openhis.web.triageandqueuemanage.appservice.TriageQueueAppService;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueActionReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAddReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAdjustReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueEncounterItem;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@Service
|
||||
public class TriageQueueAppServiceImpl implements TriageQueueAppService {
|
||||
|
||||
private static final String STATUS_WAITING = "WAITING";
|
||||
private static final String STATUS_CALLING = "CALLING";
|
||||
private static final String STATUS_SKIPPED = "SKIPPED";
|
||||
private static final String STATUS_COMPLETED = "COMPLETED";
|
||||
|
||||
@Resource
|
||||
private TriageQueueItemService triageQueueItemService;
|
||||
|
||||
@Resource
|
||||
private TriageCandidateExclusionService triageCandidateExclusionService;
|
||||
|
||||
@Override
|
||||
public R<?> list(Long organizationId, LocalDate date) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
// 只查询今天的患者
|
||||
LocalDate qd = date != null ? date : LocalDate.now();
|
||||
|
||||
LambdaQueryWrapper<TriageQueueItem> wrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getQueueDate, qd)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.ne(TriageQueueItem::getStatus, STATUS_COMPLETED)
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder);
|
||||
|
||||
// 如果指定了科室,按科室过滤;否则查询所有科室(全科模式)
|
||||
if (organizationId != null) {
|
||||
wrapper.eq(TriageQueueItem::getOrganizationId, organizationId);
|
||||
}
|
||||
|
||||
List<TriageQueueItem> list = triageQueueItemService.list(wrapper);
|
||||
|
||||
// 双重保险:再次过滤掉 COMPLETED 状态的患者(防止数据库中有异常数据)
|
||||
if (list != null && !list.isEmpty()) {
|
||||
int beforeSize = list.size();
|
||||
list = list.stream()
|
||||
.filter(item -> !STATUS_COMPLETED.equals(item.getStatus()))
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
if (beforeSize != list.size()) {
|
||||
System.out.println(">>> [TriageQueue] list() 警告:过滤掉了 " + (beforeSize - list.size()) + " 条 COMPLETED 状态的记录");
|
||||
}
|
||||
}
|
||||
|
||||
// 调试日志:检查状态值
|
||||
if (list != null && !list.isEmpty()) {
|
||||
System.out.println(">>> [TriageQueue] list() 返回 " + list.size() + " 条记录(已排除 COMPLETED)");
|
||||
for (int i = 0; i < Math.min(3, list.size()); i++) {
|
||||
TriageQueueItem item = list.get(i);
|
||||
System.out.println(" [" + i + "] patientName=" + item.getPatientName()
|
||||
+ ", status=" + item.getStatus()
|
||||
+ ", organizationId=" + item.getOrganizationId());
|
||||
}
|
||||
}
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> add(TriageQueueAddReq req) {
|
||||
if (req == null || ObjectUtil.isNull(req.getOrganizationId())) {
|
||||
return R.fail("organizationId 不能为空");
|
||||
}
|
||||
if (CollUtil.isEmpty(req.getItems())) {
|
||||
return R.fail("items 不能为空");
|
||||
}
|
||||
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
LocalDate qd = LocalDate.now();
|
||||
Long orgId = req.getOrganizationId();
|
||||
|
||||
List<TriageQueueItem> existing = triageQueueItemService.list(new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getOrganizationId, orgId)
|
||||
.eq(TriageQueueItem::getQueueDate, qd)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.ne(TriageQueueItem::getStatus, STATUS_COMPLETED));
|
||||
|
||||
int maxOrder = existing.stream().map(TriageQueueItem::getQueueOrder).filter(Objects::nonNull).max(Integer::compareTo).orElse(0);
|
||||
|
||||
int added = 0;
|
||||
for (TriageQueueEncounterItem it : req.getItems()) {
|
||||
if (it == null || it.getEncounterId() == null) continue;
|
||||
boolean exists = existing.stream().anyMatch(e -> Objects.equals(e.getEncounterId(), it.getEncounterId()));
|
||||
if (exists) continue;
|
||||
|
||||
TriageQueueItem qi = new TriageQueueItem()
|
||||
.setTenantId(tenantId)
|
||||
.setQueueDate(qd)
|
||||
.setOrganizationId(orgId)
|
||||
.setOrganizationName(req.getOrganizationName())
|
||||
.setEncounterId(it.getEncounterId())
|
||||
.setPatientId(it.getPatientId())
|
||||
.setPatientName(it.getPatientName())
|
||||
.setHealthcareName(it.getHealthcareName())
|
||||
.setPractitionerName(it.getPractitionerName())
|
||||
.setStatus(STATUS_WAITING)
|
||||
.setQueueOrder(++maxOrder)
|
||||
.setDeleteFlag("0")
|
||||
.setCreateTime(LocalDateTime.now())
|
||||
.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
triageQueueItemService.save(qi);
|
||||
|
||||
// 记录到候选池排除列表(避免刷新后重新出现在候选池)
|
||||
TriageCandidateExclusion exclusion = triageCandidateExclusionService.getOne(
|
||||
new LambdaQueryWrapper<TriageCandidateExclusion>()
|
||||
.eq(TriageCandidateExclusion::getTenantId, tenantId)
|
||||
.eq(TriageCandidateExclusion::getExclusionDate, qd)
|
||||
.eq(TriageCandidateExclusion::getEncounterId, it.getEncounterId())
|
||||
.eq(TriageCandidateExclusion::getDeleteFlag, "0")
|
||||
);
|
||||
if (exclusion == null) {
|
||||
exclusion = new TriageCandidateExclusion()
|
||||
.setTenantId(tenantId)
|
||||
.setExclusionDate(qd)
|
||||
.setEncounterId(it.getEncounterId())
|
||||
.setPatientId(it.getPatientId())
|
||||
.setPatientName(it.getPatientName())
|
||||
.setOrganizationId(orgId)
|
||||
.setOrganizationName(req.getOrganizationName())
|
||||
.setReason("ADDED_TO_QUEUE")
|
||||
.setDeleteFlag("0")
|
||||
.setCreateTime(LocalDateTime.now())
|
||||
.setUpdateTime(LocalDateTime.now());
|
||||
triageCandidateExclusionService.save(exclusion);
|
||||
}
|
||||
|
||||
added++;
|
||||
}
|
||||
|
||||
return R.ok(added);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> remove(Long id) {
|
||||
if (id == null) return R.fail("id 不能为空");
|
||||
TriageQueueItem item = triageQueueItemService.getById(id);
|
||||
if (item == null) return R.fail("队列项不存在");
|
||||
|
||||
// 逻辑删除队列项
|
||||
item.setDeleteFlag("1").setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(item);
|
||||
|
||||
// 从排除列表中删除记录,使患者重新出现在候选池中
|
||||
Integer tenantId = item.getTenantId();
|
||||
LocalDate exclusionDate = item.getQueueDate();
|
||||
Long encounterId = item.getEncounterId();
|
||||
|
||||
if (tenantId != null && exclusionDate != null && encounterId != null) {
|
||||
TriageCandidateExclusion exclusion = triageCandidateExclusionService.getOne(
|
||||
new LambdaQueryWrapper<TriageCandidateExclusion>()
|
||||
.eq(TriageCandidateExclusion::getTenantId, tenantId)
|
||||
.eq(TriageCandidateExclusion::getExclusionDate, exclusionDate)
|
||||
.eq(TriageCandidateExclusion::getEncounterId, encounterId)
|
||||
.eq(TriageCandidateExclusion::getDeleteFlag, "0")
|
||||
);
|
||||
|
||||
if (exclusion != null) {
|
||||
// 逻辑删除排除记录
|
||||
exclusion.setDeleteFlag("1").setUpdateTime(LocalDateTime.now());
|
||||
triageCandidateExclusionService.updateById(exclusion);
|
||||
}
|
||||
}
|
||||
|
||||
recalcOrders(item.getOrganizationId(), item.getQueueDate());
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> adjust(TriageQueueAdjustReq req) {
|
||||
if (req == null || req.getId() == null) return R.fail("id 不能为空");
|
||||
if (!"up".equalsIgnoreCase(req.getDirection()) && !"down".equalsIgnoreCase(req.getDirection())) {
|
||||
return R.fail("direction 只能是 up/down");
|
||||
}
|
||||
TriageQueueItem cur = triageQueueItemService.getById(req.getId());
|
||||
if (cur == null) return R.fail("队列项不存在");
|
||||
|
||||
List<TriageQueueItem> list = listInternal(cur.getOrganizationId(), cur.getQueueDate());
|
||||
list.sort(Comparator.comparing(TriageQueueItem::getQueueOrder).thenComparing(TriageQueueItem::getId));
|
||||
int idx = -1;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (Objects.equals(list.get(i).getId(), cur.getId())) { idx = i; break; }
|
||||
}
|
||||
if (idx == -1) return R.fail("队列项不在当前队列");
|
||||
|
||||
int targetIdx = "up".equalsIgnoreCase(req.getDirection()) ? idx - 1 : idx + 1;
|
||||
if (targetIdx < 0 || targetIdx >= list.size()) return R.ok(false);
|
||||
|
||||
TriageQueueItem other = list.get(targetIdx);
|
||||
Integer tmp = cur.getQueueOrder();
|
||||
cur.setQueueOrder(other.getQueueOrder()).setUpdateTime(LocalDateTime.now());
|
||||
other.setQueueOrder(tmp).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(cur);
|
||||
triageQueueItemService.updateById(other);
|
||||
|
||||
recalcOrders(cur.getOrganizationId(), cur.getQueueDate());
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> call(TriageQueueActionReq req) {
|
||||
if (req == null || req.getId() == null) return R.fail("id 不能为空");
|
||||
TriageQueueItem selected = triageQueueItemService.getById(req.getId());
|
||||
if (selected == null) return R.fail("队列项不存在");
|
||||
|
||||
// 只将"等待"状态的患者转为"叫号中",允许有多个"叫号中"的患者
|
||||
if (STATUS_WAITING.equals(selected.getStatus())) {
|
||||
selected.setStatus(STATUS_CALLING).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(selected);
|
||||
return R.ok(true);
|
||||
} else if (STATUS_CALLING.equals(selected.getStatus())) {
|
||||
// 如果已经是"叫号中"状态,直接返回成功(不做任何操作)
|
||||
return R.ok(true);
|
||||
} else {
|
||||
// 其他状态(如 SKIPPED、COMPLETED)不能选呼
|
||||
return R.fail("只能选呼\"等待\"状态的患者,当前患者状态为:" + selected.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> complete(TriageQueueActionReq req) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
TriageQueueItem calling = null;
|
||||
|
||||
// 关键改进:如果提供了 id,直接通过ID获取(像 call 方法一样),不依赖查询条件
|
||||
if (req != null && req.getId() != null) {
|
||||
calling = triageQueueItemService.getById(req.getId());
|
||||
if (calling == null) {
|
||||
return R.fail("队列项不存在");
|
||||
}
|
||||
// 验证状态
|
||||
if (!STATUS_CALLING.equals(calling.getStatus())) {
|
||||
return R.fail("只能完成\"叫号中\"状态的患者,当前患者状态为:" + calling.getStatus());
|
||||
}
|
||||
} else {
|
||||
// 如果没有提供 id,通过查询条件查找(兼容旧逻辑)
|
||||
Long orgId = req != null && req.getOrganizationId() != null ? req.getOrganizationId() : null;
|
||||
System.out.println(">>> [TriageQueue] complete() 开始执行(不限制日期,通过查询条件), tenantId=" + tenantId + ", orgId=" + orgId);
|
||||
|
||||
LambdaQueryWrapper<TriageQueueItem> callingWrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.eq(TriageQueueItem::getStatus, STATUS_CALLING)
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
.last("LIMIT 1");
|
||||
|
||||
if (orgId != null) {
|
||||
callingWrapper.eq(TriageQueueItem::getOrganizationId, orgId);
|
||||
}
|
||||
|
||||
calling = triageQueueItemService.getOne(callingWrapper, false);
|
||||
|
||||
System.out.println(">>> [TriageQueue] complete() 查询叫号中患者(不限制日期): orgId=" + orgId + ", 结果=" + (calling != null ? calling.getPatientName() + "(status=" + calling.getStatus() + ", queueDate=" + calling.getQueueDate() + ")" : "null"));
|
||||
|
||||
if (calling == null) {
|
||||
return R.fail("当前没有叫号中的患者");
|
||||
}
|
||||
}
|
||||
|
||||
// 使用实际找到的科室ID
|
||||
Long actualOrgId = calling.getOrganizationId();
|
||||
|
||||
// 1) 叫号中 -> 完成(移出列表)
|
||||
calling.setStatus(STATUS_COMPLETED).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(calling);
|
||||
|
||||
// 2) 自动推进下一个等待为叫号中(同一科室,包含跳过状态,不限制日期)
|
||||
LambdaQueryWrapper<TriageQueueItem> nextWrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.and(w -> w.eq(TriageQueueItem::getStatus, STATUS_WAITING)
|
||||
.or()
|
||||
.eq(TriageQueueItem::getStatus, STATUS_SKIPPED))
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
.last("LIMIT 1");
|
||||
|
||||
// 如果指定了科室ID,则按科室过滤;否则查询所有科室(全科模式)
|
||||
if (actualOrgId != null) {
|
||||
nextWrapper.eq(TriageQueueItem::getOrganizationId, actualOrgId);
|
||||
}
|
||||
|
||||
TriageQueueItem next = triageQueueItemService.getOne(nextWrapper, false);
|
||||
|
||||
System.out.println(">>> [TriageQueue] complete() 查询等待患者(不限制日期): actualOrgId=" + actualOrgId + ", 结果=" + (next != null ? next.getPatientName() + "(status=" + next.getStatus() + ")" : "null"));
|
||||
|
||||
if (next != null) {
|
||||
next.setStatus(STATUS_CALLING).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(next);
|
||||
}
|
||||
|
||||
recalcOrders(actualOrgId, null);
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> requeue(TriageQueueActionReq req) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
TriageQueueItem calling = null;
|
||||
|
||||
|
||||
if (req != null && req.getId() != null) {
|
||||
calling = triageQueueItemService.getById(req.getId());
|
||||
if (calling == null) {
|
||||
return R.fail("队列项不存在");
|
||||
}
|
||||
// 验证状态
|
||||
if (!STATUS_CALLING.equals(calling.getStatus())) {
|
||||
return R.fail("只能对\"叫号中\"状态的患者进行过号重排,当前患者状态为:" + calling.getStatus());
|
||||
}
|
||||
} else {
|
||||
// 如果没有提供 id,通过查询条件查找(兼容旧逻辑)
|
||||
Long orgId = req != null && req.getOrganizationId() != null ? req.getOrganizationId() : null;
|
||||
|
||||
LambdaQueryWrapper<TriageQueueItem> callingWrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.eq(TriageQueueItem::getStatus, STATUS_CALLING)
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
.last("LIMIT 1");
|
||||
|
||||
if (orgId != null) {
|
||||
callingWrapper.eq(TriageQueueItem::getOrganizationId, orgId);
|
||||
}
|
||||
|
||||
calling = triageQueueItemService.getOne(callingWrapper, false);
|
||||
|
||||
if (calling == null) return R.fail("当前没有叫号中的患者");
|
||||
}
|
||||
|
||||
// 使用实际找到的科室ID
|
||||
Long actualOrgId = calling.getOrganizationId();
|
||||
|
||||
// 关键改进:在执行"跳过"操作之前,先检查是否有等待中的患者(判断队列状态)
|
||||
// 如果没有等待中的患者,就不应该执行"过号重排"操作
|
||||
LambdaQueryWrapper<TriageQueueItem> nextWrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.and(w -> w.eq(TriageQueueItem::getStatus, STATUS_WAITING)
|
||||
.or()
|
||||
.eq(TriageQueueItem::getStatus, STATUS_SKIPPED))
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
.last("LIMIT 1");
|
||||
|
||||
// 如果指定了科室ID,则按科室过滤;否则查询所有科室(全科模式)
|
||||
if (actualOrgId != null) {
|
||||
nextWrapper.eq(TriageQueueItem::getOrganizationId, actualOrgId);
|
||||
}
|
||||
|
||||
TriageQueueItem next = triageQueueItemService.getOne(nextWrapper, false);
|
||||
|
||||
// 调试日志:检查查询结果
|
||||
System.out.println(">>> [TriageQueue] requeue() 查询等待中的患者:");
|
||||
System.out.println(">>> - 科室ID: " + actualOrgId);
|
||||
System.out.println(">>> - 找到的等待患者: " + (next != null ? next.getPatientName() + " (状态: " + next.getStatus() + ")" : "null"));
|
||||
|
||||
// 如果找不到等待中的患者,直接返回失败(不执行跳过操作)
|
||||
if (next == null) {
|
||||
System.out.println(">>> [TriageQueue] requeue() 失败:没有等待中的患者");
|
||||
return R.fail("当前没有等待中的患者");
|
||||
}
|
||||
|
||||
// 找末尾序号(同一科室,不限制日期)
|
||||
Integer maxOrder = triageQueueItemService.list(new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getOrganizationId, actualOrgId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.ne(TriageQueueItem::getStatus, STATUS_COMPLETED))
|
||||
.stream()
|
||||
.map(TriageQueueItem::getQueueOrder)
|
||||
.filter(Objects::nonNull)
|
||||
.max(Integer::compareTo)
|
||||
.orElse(0);
|
||||
|
||||
// 1) 叫号中 -> 跳过,并移到末尾
|
||||
calling.setStatus(STATUS_SKIPPED).setQueueOrder(maxOrder + 1).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(calling);
|
||||
|
||||
// 2) 自动推进下一个等待为叫号中
|
||||
next.setStatus(STATUS_CALLING).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(next);
|
||||
|
||||
recalcOrders(actualOrgId, null);
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> skip(TriageQueueActionReq req) {
|
||||
// 当前业务“跳过”按“过号重排”处理:叫号中 -> 跳过并移到末尾,自动推进下一等待
|
||||
return requeue(req);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> next(TriageQueueActionReq req) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
TriageQueueItem calling = null;
|
||||
|
||||
System.out.println(">>> [TriageQueue] next() 开始执行(不限制日期), tenantId=" + tenantId);
|
||||
|
||||
// 关键改进:如果提供了 id,优先使用 id 直接查找(像 call 方法一样)
|
||||
if (req != null && req.getId() != null) {
|
||||
calling = triageQueueItemService.getById(req.getId());
|
||||
if (calling == null) {
|
||||
return R.fail("队列项不存在");
|
||||
}
|
||||
// 验证状态:必须是"叫号中"状态
|
||||
if (!STATUS_CALLING.equals(calling.getStatus())) {
|
||||
return R.fail("只能对\"叫号中\"状态的患者执行\"下一患者\"操作,当前患者状态为:" + calling.getStatus());
|
||||
}
|
||||
} else {
|
||||
// 如果没有提供 id,通过查询条件查找(兼容旧逻辑)
|
||||
Long orgId = req != null && req.getOrganizationId() != null ? req.getOrganizationId() : null;
|
||||
|
||||
LambdaQueryWrapper<TriageQueueItem> callingWrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.eq(TriageQueueItem::getStatus, STATUS_CALLING)
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
.last("LIMIT 1");
|
||||
|
||||
if (orgId != null) {
|
||||
callingWrapper.eq(TriageQueueItem::getOrganizationId, orgId);
|
||||
}
|
||||
|
||||
calling = triageQueueItemService.getOne(callingWrapper, false);
|
||||
}
|
||||
|
||||
Long actualOrgId = null;
|
||||
|
||||
// 当前叫号中 -> 完成(如果不存在,就当作从头找第一位等待)
|
||||
if (calling != null) {
|
||||
calling.setStatus(STATUS_COMPLETED).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(calling);
|
||||
actualOrgId = calling.getOrganizationId(); // 使用叫号中患者所在的科室
|
||||
} else {
|
||||
// 如果没有叫号中的患者,使用请求中的 organizationId(如果有)
|
||||
if (req != null && req.getOrganizationId() != null) {
|
||||
actualOrgId = req.getOrganizationId();
|
||||
}
|
||||
}
|
||||
|
||||
// 下一位等待 -> 叫号中(如果之前有叫号中的,就在同一科室找;否则在全科找)
|
||||
// 注意:也包含"跳过"状态的患者,因为跳过后的患者也可以重新叫号(不限制日期)
|
||||
LambdaQueryWrapper<TriageQueueItem> nextWrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.and(w -> w.eq(TriageQueueItem::getStatus, STATUS_WAITING)
|
||||
.or()
|
||||
.eq(TriageQueueItem::getStatus, STATUS_SKIPPED))
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
.last("LIMIT 1");
|
||||
|
||||
if (actualOrgId != null) {
|
||||
nextWrapper.eq(TriageQueueItem::getOrganizationId, actualOrgId);
|
||||
}
|
||||
|
||||
TriageQueueItem next = triageQueueItemService.getOne(nextWrapper, false);
|
||||
|
||||
// 调试日志:打印查询条件和结果
|
||||
System.out.println(">>> [TriageQueue] next() 查询条件(不限制日期): tenantId=" + tenantId
|
||||
+ ", actualOrgId=" + actualOrgId
|
||||
+ ", deleteFlag=0"
|
||||
+ ", status IN (WAITING, SKIPPED)");
|
||||
System.out.println(">>> [TriageQueue] next() 查询结果: calling=" + (calling != null ? calling.getPatientName() + "(status=" + calling.getStatus() + ", queueDate=" + calling.getQueueDate() + ")" : "null")
|
||||
+ ", next=" + (next != null ? next.getPatientName() + "(status=" + next.getStatus() + ", queueDate=" + next.getQueueDate() + ")" : "null"));
|
||||
|
||||
if (next == null) {
|
||||
if (actualOrgId != null) {
|
||||
recalcOrders(actualOrgId, null);
|
||||
}
|
||||
return R.fail("当前没有等待的患者");
|
||||
}
|
||||
|
||||
next.setStatus(STATUS_CALLING).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(next);
|
||||
|
||||
if (next.getOrganizationId() != null) {
|
||||
recalcOrders(next.getOrganizationId(), null);
|
||||
}
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
private List<TriageQueueItem> listInternal(Long orgId, LocalDate qd) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
LambdaQueryWrapper<TriageQueueItem> wrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getOrganizationId, orgId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.ne(TriageQueueItem::getStatus, STATUS_COMPLETED);
|
||||
// 如果 qd 不为 null,才添加日期限制
|
||||
if (qd != null) {
|
||||
wrapper.eq(TriageQueueItem::getQueueDate, qd);
|
||||
}
|
||||
return triageQueueItemService.list(wrapper);
|
||||
}
|
||||
|
||||
private TriageQueueItem findCalling(Long orgId, LocalDate qd) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
return triageQueueItemService.getOne(new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getOrganizationId, orgId)
|
||||
.eq(TriageQueueItem::getQueueDate, qd)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.eq(TriageQueueItem::getStatus, STATUS_CALLING)
|
||||
.last("LIMIT 1"), false);
|
||||
}
|
||||
|
||||
private void recalcOrders(Long orgId, LocalDate qd) {
|
||||
List<TriageQueueItem> list = listInternal(orgId, qd);
|
||||
list.sort(Comparator.comparing(TriageQueueItem::getQueueOrder).thenComparing(TriageQueueItem::getId));
|
||||
int i = 1;
|
||||
for (TriageQueueItem it : list) {
|
||||
if (!Objects.equals(it.getQueueOrder(), i)) {
|
||||
it.setQueueOrder(i).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(it);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.openhis.web.triageandqueuemanage.controller;
|
||||
|
||||
import com.core.common.core.domain.R;
|
||||
import com.openhis.web.triageandqueuemanage.appservice.TriageQueueAppService;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueActionReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAddReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAdjustReq;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDate;
|
||||
|
||||
@RestController
|
||||
@Slf4j
|
||||
@RequestMapping("/triage/queue")
|
||||
public class TriageQueueController {
|
||||
|
||||
@Resource
|
||||
private TriageQueueAppService triageQueueAppService;
|
||||
|
||||
@GetMapping("/list")
|
||||
public R<?> list(@RequestParam(value = "organizationId", required = false) Long organizationId,
|
||||
@RequestParam(value = "date", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) {
|
||||
return triageQueueAppService.list(organizationId, date);
|
||||
}
|
||||
|
||||
@PostMapping("/add")
|
||||
public R<?> add(@RequestBody TriageQueueAddReq req) {
|
||||
return triageQueueAppService.add(req);
|
||||
}
|
||||
|
||||
@DeleteMapping("/remove/{id}")
|
||||
public R<?> remove(@PathVariable("id") Long id) {
|
||||
return triageQueueAppService.remove(id);
|
||||
}
|
||||
|
||||
@PutMapping("/adjust")
|
||||
public R<?> adjust(@RequestBody TriageQueueAdjustReq req) {
|
||||
return triageQueueAppService.adjust(req);
|
||||
}
|
||||
|
||||
@PostMapping("/call")
|
||||
public R<?> call(@RequestBody TriageQueueActionReq req) {
|
||||
return triageQueueAppService.call(req);
|
||||
}
|
||||
|
||||
@PostMapping("/complete")
|
||||
public R<?> complete(@RequestBody(required = false) TriageQueueActionReq req) {
|
||||
return triageQueueAppService.complete(req);
|
||||
}
|
||||
|
||||
@PostMapping("/requeue")
|
||||
public R<?> requeue(@RequestBody(required = false) TriageQueueActionReq req) {
|
||||
return triageQueueAppService.requeue(req);
|
||||
}
|
||||
|
||||
@PostMapping("/skip")
|
||||
public R<?> skip(@RequestBody(required = false) TriageQueueActionReq req) {
|
||||
return triageQueueAppService.skip(req);
|
||||
}
|
||||
|
||||
@PostMapping("/next")
|
||||
public R<?> next(@RequestBody(required = false) TriageQueueActionReq req) {
|
||||
return triageQueueAppService.next(req);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.openhis.web.triageandqueuemanage.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TriageQueueActionReq {
|
||||
/** 目标队列项ID(例如:选呼时选中的患者) */
|
||||
private Long id;
|
||||
/** 科室ID(可选:不传则用当前登录人orgId) */
|
||||
private Long organizationId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.openhis.web.triageandqueuemanage.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class TriageQueueAddReq {
|
||||
/** 科室ID(就诊科室) */
|
||||
private Long organizationId;
|
||||
/** 科室名称(冗余存储,便于展示) */
|
||||
private String organizationName;
|
||||
/** 要加入队列的就诊记录 */
|
||||
private List<TriageQueueEncounterItem> items;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.openhis.web.triageandqueuemanage.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TriageQueueAdjustReq {
|
||||
private Long id;
|
||||
/** up / down */
|
||||
private String direction;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.openhis.web.triageandqueuemanage.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TriageQueueEncounterItem {
|
||||
private Long encounterId;
|
||||
private Long patientId;
|
||||
private String patientName;
|
||||
private String healthcareName;
|
||||
private String practitionerName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -224,28 +224,7 @@
|
||||
</select>
|
||||
<select id="selectAdmissionPatientInfo"
|
||||
resultType="com.openhis.web.inhospitalnursestation.dto.AdmissionPatientInfoDto">
|
||||
WITH locations AS (SELECT ael.encounter_id,
|
||||
ael.start_time,
|
||||
al.form_enum,
|
||||
al.id AS location_id,
|
||||
al."name" AS location_name
|
||||
FROM adm_encounter_location ael
|
||||
LEFT JOIN adm_location al
|
||||
ON ael.location_id = al.id
|
||||
AND al.delete_flag = '0'
|
||||
WHERE ael.status_enum = #{active}
|
||||
AND ael.delete_flag = '0'),
|
||||
practitioners AS (SELECT aep.encounter_id,
|
||||
aep.type_code,
|
||||
pra.id AS practitioner_id,
|
||||
pra."name" AS practitioner_name
|
||||
FROM adm_encounter_participant aep
|
||||
LEFT JOIN adm_practitioner pra
|
||||
ON aep.practitioner_id = pra.id
|
||||
AND pra.delete_flag = '0'
|
||||
WHERE aep.status_enum = #{active}
|
||||
AND aep.delete_flag = '0')
|
||||
SELECT ae.id AS encounter_id,
|
||||
SELECT DISTINCT ae.id AS encounter_id,
|
||||
ae.bus_no,
|
||||
ae.priority_enum,
|
||||
ae.organization_id,
|
||||
@@ -273,27 +252,84 @@
|
||||
fc.contract_name,
|
||||
diagnosis.condition_names
|
||||
FROM adm_encounter ae
|
||||
LEFT JOIN locations AS bed
|
||||
ON bed.encounter_id = ae.id
|
||||
AND bed.form_enum = #{bed}
|
||||
LEFT JOIN locations AS house
|
||||
ON house.encounter_id = ae.id
|
||||
AND house.form_enum = #{house}
|
||||
LEFT JOIN locations AS ward
|
||||
ON ward.encounter_id = ae.id
|
||||
AND ward.form_enum = #{ward}
|
||||
LEFT JOIN practitioners AS primaryNurse
|
||||
ON primaryNurse.encounter_id = ae.id
|
||||
AND primaryNurse.type_code = #{primaryNurse}
|
||||
LEFT JOIN practitioners AS attendingDoctor
|
||||
ON attendingDoctor.encounter_id = ae.id
|
||||
AND attendingDoctor.type_code = #{attendingDoctor}
|
||||
LEFT JOIN practitioners AS admittingDoctor
|
||||
ON admittingDoctor.encounter_id = ae.id
|
||||
AND admittingDoctor.type_code = #{admittingDoctor}
|
||||
LEFT JOIN practitioners AS chiefDoctor
|
||||
ON chiefDoctor.encounter_id = ae.id
|
||||
AND chiefDoctor.type_code = #{chiefDoctor}
|
||||
LEFT JOIN (
|
||||
SELECT ael.encounter_id,
|
||||
ael.start_time,
|
||||
al.id AS location_id,
|
||||
al."name" AS location_name
|
||||
FROM adm_encounter_location ael
|
||||
LEFT JOIN adm_location al ON ael.location_id = al.id AND al.delete_flag = '0'
|
||||
WHERE ael.status_enum = #{active}
|
||||
AND ael.delete_flag = '0'
|
||||
AND ael.form_enum = #{bed}
|
||||
LIMIT 1
|
||||
) AS bed ON bed.encounter_id = ae.id
|
||||
LEFT JOIN (
|
||||
SELECT ael.encounter_id,
|
||||
al.id AS location_id,
|
||||
al."name" AS location_name
|
||||
FROM adm_encounter_location ael
|
||||
LEFT JOIN adm_location al ON ael.location_id = al.id AND al.delete_flag = '0'
|
||||
WHERE ael.status_enum = #{active}
|
||||
AND ael.delete_flag = '0'
|
||||
AND ael.form_enum = #{house}
|
||||
LIMIT 1
|
||||
) AS house ON house.encounter_id = ae.id
|
||||
LEFT JOIN (
|
||||
SELECT ael.encounter_id,
|
||||
al.id AS location_id,
|
||||
al."name" AS location_name
|
||||
FROM adm_encounter_location ael
|
||||
LEFT JOIN adm_location al ON ael.location_id = al.id AND al.delete_flag = '0'
|
||||
WHERE ael.status_enum = #{active}
|
||||
AND ael.delete_flag = '0'
|
||||
AND ael.form_enum = #{ward}
|
||||
LIMIT 1
|
||||
) AS ward ON ward.encounter_id = ae.id
|
||||
LEFT JOIN (
|
||||
SELECT aep.encounter_id,
|
||||
pra.id AS practitioner_id,
|
||||
pra."name" AS practitioner_name
|
||||
FROM adm_encounter_participant aep
|
||||
LEFT JOIN adm_practitioner pra ON aep.practitioner_id = pra.id AND pra.delete_flag = '0'
|
||||
WHERE aep.status_enum = #{active}
|
||||
AND aep.delete_flag = '0'
|
||||
AND aep.type_code = #{primaryNurse}
|
||||
LIMIT 1
|
||||
) AS primaryNurse ON primaryNurse.encounter_id = ae.id
|
||||
LEFT JOIN (
|
||||
SELECT aep.encounter_id,
|
||||
pra.id AS practitioner_id,
|
||||
pra."name" AS practitioner_name
|
||||
FROM adm_encounter_participant aep
|
||||
LEFT JOIN adm_practitioner pra ON aep.practitioner_id = pra.id AND pra.delete_flag = '0'
|
||||
WHERE aep.status_enum = #{active}
|
||||
AND aep.delete_flag = '0'
|
||||
AND aep.type_code = #{attendingDoctor}
|
||||
LIMIT 1
|
||||
) AS attendingDoctor ON attendingDoctor.encounter_id = ae.id
|
||||
LEFT JOIN (
|
||||
SELECT aep.encounter_id,
|
||||
pra.id AS practitioner_id,
|
||||
pra."name" AS practitioner_name
|
||||
FROM adm_encounter_participant aep
|
||||
LEFT JOIN adm_practitioner pra ON aep.practitioner_id = pra.id AND pra.delete_flag = '0'
|
||||
WHERE aep.status_enum = #{active}
|
||||
AND aep.delete_flag = '0'
|
||||
AND aep.type_code = #{admittingDoctor}
|
||||
LIMIT 1
|
||||
) AS admittingDoctor ON admittingDoctor.encounter_id = ae.id
|
||||
LEFT JOIN (
|
||||
SELECT aep.encounter_id,
|
||||
pra.id AS practitioner_id,
|
||||
pra."name" AS practitioner_name
|
||||
FROM adm_encounter_participant aep
|
||||
LEFT JOIN adm_practitioner pra ON aep.practitioner_id = pra.id AND pra.delete_flag = '0'
|
||||
WHERE aep.status_enum = #{active}
|
||||
AND aep.delete_flag = '0'
|
||||
AND aep.type_code = #{chiefDoctor}
|
||||
LIMIT 1
|
||||
) AS chiefDoctor ON chiefDoctor.encounter_id = ae.id
|
||||
LEFT JOIN adm_organization ao
|
||||
ON ao.id = ae.organization_id
|
||||
AND ao.delete_flag = '0'
|
||||
@@ -307,47 +343,17 @@
|
||||
LEFT JOIN fin_contract fc
|
||||
ON aa.contract_no = fc.bus_no
|
||||
AND fc.delete_flag = '0'
|
||||
LEFT JOIN (SELECT aed.encounter_id,
|
||||
LEFT JOIN (
|
||||
SELECT aed.encounter_id,
|
||||
STRING_AGG(ccd.name, ', ') AS condition_names
|
||||
FROM adm_encounter_diagnosis aed
|
||||
INNER JOIN cli_condition cc
|
||||
ON cc.id = aed.condition_id
|
||||
AND cc.delete_flag = '0'
|
||||
INNER JOIN cli_condition_definition ccd
|
||||
ON ccd.id = cc.definition_id
|
||||
AND ccd.delete_flag = '0'
|
||||
INNER JOIN cli_condition cc ON cc.id = aed.condition_id AND cc.delete_flag = '0'
|
||||
INNER JOIN cli_condition_definition ccd ON ccd.id = cc.definition_id AND ccd.delete_flag = '0'
|
||||
WHERE aed.delete_flag = '0'
|
||||
GROUP BY aed.encounter_id) AS diagnosis
|
||||
ON ae.id = diagnosis.encounter_id
|
||||
GROUP BY aed.encounter_id
|
||||
) AS diagnosis ON ae.id = diagnosis.encounter_id
|
||||
WHERE ae.id = #{encounterId}
|
||||
AND ae.delete_flag = '0'
|
||||
GROUP BY ae.id,
|
||||
ae.bus_no,
|
||||
ae.priority_enum,
|
||||
ae.organization_id,
|
||||
ae.start_time,
|
||||
bed.start_time,
|
||||
bed.location_id,
|
||||
bed.location_name,
|
||||
house.location_id,
|
||||
house.location_name,
|
||||
ward.location_id,
|
||||
ward.location_name,
|
||||
primaryNurse.practitioner_id,
|
||||
primaryNurse.practitioner_name,
|
||||
attendingDoctor.practitioner_id,
|
||||
attendingDoctor.practitioner_name,
|
||||
admittingDoctor.practitioner_id,
|
||||
admittingDoctor.practitioner_name,
|
||||
chiefDoctor.practitioner_id,
|
||||
chiefDoctor.practitioner_name,
|
||||
ao."name",
|
||||
ap."name",
|
||||
ap.gender_enum,
|
||||
ap.birth_date,
|
||||
ap.phone,
|
||||
fc.contract_name,
|
||||
diagnosis.condition_names
|
||||
</select>
|
||||
<select id="getAmount" resultType="com.openhis.web.inhospitalnursestation.dto.EncounterAccountDto">
|
||||
SELECT aa.id,
|
||||
|
||||
@@ -2,10 +2,66 @@
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.openhis.web.inpatientmanage.mapper.PatientHomeAppMapper">
|
||||
|
||||
<!-- 患者首页信息分页查询-->
|
||||
<!-- 患者首页信息分页查询 -->
|
||||
<select id="getPage" parameterType="java.util.Map"
|
||||
resultType="com.openhis.web.inpatientmanage.dto.PatientHomeDto">
|
||||
SELECT
|
||||
id,
|
||||
active_flag,
|
||||
temp_flag,
|
||||
patientName,
|
||||
name_json,
|
||||
patientNo,
|
||||
gender_enum,
|
||||
birth_date,
|
||||
deceased_date,
|
||||
marital_status_enum,
|
||||
prfs_enum,
|
||||
phone,
|
||||
address,
|
||||
address_province,
|
||||
address_city,
|
||||
address_district,
|
||||
address_street,
|
||||
address_json,
|
||||
nationality_code,
|
||||
id_card,
|
||||
py_str,
|
||||
wb_str,
|
||||
blood_abo,
|
||||
blood_rh,
|
||||
work_company,
|
||||
native_place,
|
||||
country_code,
|
||||
link_name,
|
||||
link_relation_code,
|
||||
link_telcom,
|
||||
link_jsons,
|
||||
tenant_id,
|
||||
hospitalNo,
|
||||
encounterId,
|
||||
priority_enum,
|
||||
status_enum,
|
||||
organization_id,
|
||||
admissionDate,
|
||||
dischargeDate,
|
||||
class_enum,
|
||||
responsibleDoctor,
|
||||
responsibleNurse,
|
||||
type_code,
|
||||
surgeryStatusEnum,
|
||||
surgeryStartTime,
|
||||
surgeryEndTime,
|
||||
encounterLocationId,
|
||||
location_id,
|
||||
caty,
|
||||
mainDiagnosis
|
||||
FROM (
|
||||
SELECT
|
||||
patient_base.*,
|
||||
ROW_NUMBER() OVER (PARTITION BY patient_base.id ORDER BY patient_base.encounterId) as rn
|
||||
FROM (
|
||||
SELECT DISTINCT
|
||||
T1.id,
|
||||
T1.active_flag,
|
||||
T1.temp_flag,
|
||||
@@ -46,8 +102,24 @@
|
||||
T2.start_time AS admissionDate, -- 入院日期
|
||||
T2.end_time AS dischargeDate, -- 出院日期
|
||||
T2.class_enum, -- 就诊类别
|
||||
Doctor.name AS responsibleDoctor, -- 责任医生
|
||||
Nurse.name AS responsibleNurse, -- 责任护士
|
||||
-- 获取责任医生(使用子查询确保只返回一个值)
|
||||
(SELECT p."name"
|
||||
FROM adm_encounter_participant ep
|
||||
LEFT JOIN adm_practitioner p ON ep.practitioner_id = p.id AND p.delete_flag = '0'
|
||||
WHERE ep.encounter_id = T2.id
|
||||
AND ep.type_code = '9'
|
||||
AND ep.delete_flag = '0'
|
||||
ORDER BY ep.practitioner_id
|
||||
LIMIT 1) AS responsibleDoctor,
|
||||
-- 获取责任护士(使用子查询确保只返回一个值)
|
||||
(SELECT p."name"
|
||||
FROM adm_encounter_participant ep
|
||||
LEFT JOIN adm_practitioner p ON ep.practitioner_id = p.id AND p.delete_flag = '0'
|
||||
WHERE ep.encounter_id = T2.id
|
||||
AND ep.type_code = '2'
|
||||
AND ep.delete_flag = '0'
|
||||
ORDER BY ep.practitioner_id
|
||||
LIMIT 1) AS responsibleNurse,
|
||||
T6.type_code, -- 费别
|
||||
T7.status_enum AS surgeryStatusEnum, -- 手术状态
|
||||
T8.start_time AS surgeryStartTime, -- 手术开始时间
|
||||
@@ -57,50 +129,10 @@
|
||||
T11."name" AS caty, -- 入院科室
|
||||
T12."name" AS mainDiagnosis -- 主要诊断
|
||||
FROM adm_patient AS T1
|
||||
LEFT JOIN adm_encounter T2
|
||||
INNER JOIN adm_encounter T2 -- 改为INNER JOIN确保患者有就诊记录
|
||||
ON T1.id = T2.patient_id -- 患者ID
|
||||
AND T2.delete_flag = '0'
|
||||
AND T2.class_enum = '1' -- 仅选择住院患者
|
||||
-- 获取责任医生
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
encounter_id,
|
||||
practitioner_id,
|
||||
"name",
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY practitioner_id) as rn
|
||||
FROM (
|
||||
SELECT
|
||||
T3.encounter_id,
|
||||
T4."name",
|
||||
T3.practitioner_id
|
||||
FROM adm_encounter_participant T3
|
||||
LEFT JOIN adm_practitioner T4
|
||||
ON T3.practitioner_id = T4.id
|
||||
AND T4.delete_flag = '0'
|
||||
WHERE T3.type_code = '9' -- 责任医生类型
|
||||
AND T3.delete_flag = '0'
|
||||
) sub
|
||||
) Doctor ON T2.id = Doctor.encounter_id AND Doctor.rn = 1
|
||||
-- 获取责任护士
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
encounter_id,
|
||||
practitioner_id,
|
||||
"name",
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY practitioner_id) as rn
|
||||
FROM (
|
||||
SELECT
|
||||
T3.encounter_id,
|
||||
T4."name",
|
||||
T3.practitioner_id
|
||||
FROM adm_encounter_participant T3
|
||||
LEFT JOIN adm_practitioner T4
|
||||
ON T3.practitioner_id = T4.id
|
||||
AND T4.delete_flag = '0'
|
||||
WHERE T3.type_code = '2' -- 责任护士类型
|
||||
AND T3.delete_flag = '0'
|
||||
) sub
|
||||
) Nurse ON T2.id = Nurse.encounter_id AND Nurse.rn = 1
|
||||
LEFT JOIN adm_encounter_diagnosis T5
|
||||
ON T2.id = T5.encounter_id -- 就诊ID
|
||||
AND T5.maindise_flag = 1
|
||||
@@ -108,12 +140,25 @@
|
||||
LEFT JOIN adm_account T6
|
||||
ON T2.id = T6.encounter_id -- 就诊ID
|
||||
AND T6.delete_flag = '0'
|
||||
LEFT JOIN cli_procedure T7
|
||||
ON T1.id = T7.patient_id -- 患者ID
|
||||
AND T7.delete_flag = '0'
|
||||
LEFT JOIN cli_procedure_performer T8
|
||||
ON T7.id = T8.procedure_id -- 手术ID
|
||||
AND T8.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
patient_id,
|
||||
status_enum,
|
||||
id,
|
||||
request_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY id DESC) as rn
|
||||
FROM cli_procedure
|
||||
WHERE delete_flag = '0'
|
||||
) T7 ON T1.id = T7.patient_id AND T7.rn = 1
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
procedure_id,
|
||||
start_time,
|
||||
end_time,
|
||||
ROW_NUMBER() OVER (PARTITION BY procedure_id ORDER BY id DESC) as rn
|
||||
FROM cli_procedure_performer
|
||||
WHERE delete_flag = '0'
|
||||
) T8 ON T7.id = T8.procedure_id AND T8.rn = 1
|
||||
LEFT JOIN adm_encounter_location T9
|
||||
ON T2.id = T9.encounter_id -- 就诊ID
|
||||
AND T9.form_enum = 8
|
||||
@@ -125,16 +170,7 @@
|
||||
LEFT JOIN cli_condition_definition T12
|
||||
ON T5.condition_id = T12.id
|
||||
AND T12.delete_flag = '0'
|
||||
LEFT JOIN wor_service_request T13
|
||||
ON T2.id = T13.encounter_id
|
||||
AND T7.base_service_req_id = T13.id
|
||||
AND T13.delete_flag = '0'
|
||||
LEFT JOIN wor_service_request_detail T14
|
||||
ON T13.id = T14.service_req_id
|
||||
AND T14.type_code = '3'
|
||||
AND T14.delete_flag = '0'
|
||||
<where>
|
||||
T1.delete_flag = '0'
|
||||
WHERE T1.delete_flag = '0'
|
||||
-- 患者ID
|
||||
<if test="patientId != null">
|
||||
AND T1.id = #{patientId}
|
||||
@@ -157,7 +193,15 @@
|
||||
|
||||
-- 手术
|
||||
<if test="statusEnum == 5">
|
||||
AND T14.type_code = '3'
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM wor_service_request_detail wsrd
|
||||
INNER JOIN wor_service_request wsr ON wsrd.service_req_id = wsr.id
|
||||
WHERE wsr.encounter_id = T2.id
|
||||
AND wsrd.type_code = '3'
|
||||
AND wsr.delete_flag = '0'
|
||||
AND wsrd.delete_flag = '0'
|
||||
)
|
||||
</if>
|
||||
|
||||
-- 欠费
|
||||
@@ -176,15 +220,32 @@
|
||||
T2.bus_no = #{searchKey}
|
||||
-- 患者姓名
|
||||
OR T1.name like concat('%', #{searchKey}, '%')
|
||||
-- 责任医生
|
||||
OR Doctor.name like concat('%', #{searchKey}, '%')
|
||||
-- 责任护士
|
||||
OR Nurse.name like concat('%', #{searchKey}, '%')
|
||||
-- 责任医生(在子查询中处理)
|
||||
OR EXISTS (
|
||||
SELECT 1
|
||||
FROM adm_encounter_participant ep
|
||||
LEFT JOIN adm_practitioner p ON ep.practitioner_id = p.id AND p.delete_flag = '0'
|
||||
WHERE ep.encounter_id = T2.id
|
||||
AND ep.type_code = '9'
|
||||
AND ep.delete_flag = '0'
|
||||
AND p."name" like concat('%', #{searchKey}, '%')
|
||||
)
|
||||
-- 责任护士(在子查询中处理)
|
||||
OR EXISTS (
|
||||
SELECT 1
|
||||
FROM adm_encounter_participant ep
|
||||
LEFT JOIN adm_practitioner p ON ep.practitioner_id = p.id AND p.delete_flag = '0'
|
||||
WHERE ep.encounter_id = T2.id
|
||||
AND ep.type_code = '2'
|
||||
AND ep.delete_flag = '0'
|
||||
AND p."name" like concat('%', #{searchKey}, '%')
|
||||
)
|
||||
)
|
||||
</if>
|
||||
</where>
|
||||
|
||||
ORDER BY T9.location_id ASC
|
||||
) patient_base
|
||||
) ranked_result
|
||||
WHERE rn = 1
|
||||
ORDER BY location_id ASC
|
||||
</select>
|
||||
|
||||
<!-- 科室空床查询-->
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
AND T6.delete_flag = '0'
|
||||
LEFT JOIN wor_service_request T7
|
||||
ON T1.encounter_id = T7.encounter_id
|
||||
AND T5.base_service_req_id = T7.id
|
||||
AND T5.request_id = T7.id
|
||||
AND T7.delete_flag = '0'
|
||||
LEFT JOIN wor_service_request_detail T8
|
||||
ON T7.id = T8.service_req_id
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.openhis.common.aspectj;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.core.common.core.domain.R;
|
||||
import com.core.common.utils.DictUtils;
|
||||
import com.openhis.common.annotation.Dict;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
@@ -115,19 +116,12 @@ public class DictAspect {
|
||||
}
|
||||
|
||||
private String queryDictLabel(String dictTable, String dictCode, String dictText, String dictValue) {
|
||||
String sql;
|
||||
if (StringUtils.isEmpty(dictTable)) {
|
||||
// 场景 1:默认查询 sys_dict_data 表
|
||||
sql = "SELECT dict_label FROM sys_dict_data WHERE dict_type = ? AND dict_value::varchar = ? LIMIT 1";
|
||||
try {
|
||||
return jdbcTemplate.queryForObject(sql, String.class, dictCode, dictValue);
|
||||
} catch (DataAccessException e) {
|
||||
// 如果查询结果为空,返回 空字符串
|
||||
return "";
|
||||
}
|
||||
if (StringUtils.hasText(dictTable)) {
|
||||
// 场景 1:默认字典走DictUtils缓存
|
||||
return DictUtils.getDictLabel(dictCode, dictValue);
|
||||
} else {
|
||||
// 场景 2:查询指定表
|
||||
sql = String.format("SELECT %s FROM %s WHERE %s::varchar = ? LIMIT 1", dictText, dictTable, dictCode);
|
||||
String sql = String.format("SELECT %s FROM %s WHERE %s::varchar = ? LIMIT 1", dictText, dictTable, dictCode);
|
||||
try {
|
||||
return jdbcTemplate.queryForObject(sql, String.class, dictValue);
|
||||
} catch (DataAccessException e) {
|
||||
|
||||
@@ -32,18 +32,11 @@ public class PatientServiceImpl extends ServiceImpl<PatientMapper, Patient> impl
|
||||
*/
|
||||
@Override
|
||||
public boolean saveOrUpdatePatient(Patient patient) {
|
||||
|
||||
// 身份证ID,患者ID,确定唯一患者
|
||||
LambdaQueryWrapper<Patient> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(Patient::getId, patient.getId()).eq(Patient::getIdCard, patient.getIdCard());
|
||||
|
||||
Patient existingPatient = baseMapper.selectOne(queryWrapper);
|
||||
if (existingPatient != null) {
|
||||
// 如果记录存在,更新记录
|
||||
patient.setId(existingPatient.getId());
|
||||
if (patient.getId() != null) {
|
||||
// 如果提供了ID,则直接更新该ID的记录
|
||||
return baseMapper.updateById(patient) > 0;
|
||||
} else {
|
||||
// 如果记录不存在,插入新记录
|
||||
// 如果没有提供ID,则插入新记录
|
||||
return baseMapper.insert(patient) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package com.openhis.template.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@@ -28,8 +30,13 @@ public class DoctorPhrase {
|
||||
private Integer phraseType;
|
||||
|
||||
/** 业务分类(主诉/现病史等) */
|
||||
@NotBlank(message = "业务分类不能为空")
|
||||
private String phraseCategory;
|
||||
|
||||
// 非数据库字段,用于前端展示名称
|
||||
@TableField(exist = false)
|
||||
private String businessTypeName;
|
||||
|
||||
/** 模板内容 */
|
||||
private String phraseContent;
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.openhis.template.enums;
|
||||
|
||||
// 枚举类不需要任何Lombok注解(@Data/@AllArgsConstructor/@NoArgsConstructor都删掉)
|
||||
public enum DoctorPhraseBizTypeEnum {
|
||||
// 1. 枚举项:直接传值(不用code:xxx,直接写字符串)
|
||||
MAIN_COMPLAINT("MAIN_COMPLAINT", "主诉"),
|
||||
PRESENT_HISTORY("PRESENT_HISTORY", "现病史"),
|
||||
PRE_OPERATION("PRE_OPERATION", "术前"),
|
||||
POST_OPERATION("POST_OPERATION", "术后"),
|
||||
PAST_HISTORY("PAST_HISTORY", "既往史");
|
||||
|
||||
// 2. 定义枚举的成员变量(private final 保证不可变)
|
||||
private final String code; // 数据库存储的编码
|
||||
private final String name; // 前端展示的名称
|
||||
|
||||
// 3. 手动写私有构造器(枚举构造器必须私有,且要给变量赋值)
|
||||
DoctorPhraseBizTypeEnum(String code, String name) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
// 4. 提供getter方法(枚举没有setter,因为枚举项是常量,不能改)
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
// 【可选】添加工具方法:根据code找对应的枚举(前端传code时,后端快速匹配)
|
||||
public static DoctorPhraseBizTypeEnum getByCode(String code) {
|
||||
for (DoctorPhraseBizTypeEnum enumObj : values()) {
|
||||
if (enumObj.getCode().equals(code)) {
|
||||
return enumObj;
|
||||
}
|
||||
}
|
||||
return null; // 或抛异常:throw new IllegalArgumentException("无效的业务分类编码:" + code);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.openhis.triageandqueuemanage.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName(value = "hisdev.triage_candidate_exclusion")
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class TriageCandidateExclusion {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private Integer tenantId;
|
||||
private LocalDate exclusionDate;
|
||||
private Long encounterId;
|
||||
private Long patientId;
|
||||
private String patientName;
|
||||
private Long organizationId;
|
||||
private String organizationName;
|
||||
|
||||
/** 排除原因:ADDED_TO_QUEUE(加入队列)、MANUAL_REMOVE(手动移除)等 */
|
||||
private String reason;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
private String deleteFlag;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.openhis.triageandqueuemanage.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName(value = "hisdev.triage_queue_item")
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class TriageQueueItem {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private Integer tenantId;
|
||||
private LocalDate queueDate;
|
||||
private Long organizationId;
|
||||
private String organizationName;
|
||||
|
||||
private Long encounterId;
|
||||
private Long patientId;
|
||||
private String patientName;
|
||||
|
||||
private String healthcareName;
|
||||
private String practitionerName;
|
||||
|
||||
/** WAITING / CALLING / SKIPPED / COMPLETED */
|
||||
private String status;
|
||||
private Integer queueOrder;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
private String deleteFlag;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.openhis.triageandqueuemanage.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageCandidateExclusion;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface TriageCandidateExclusionMapper extends BaseMapper<TriageCandidateExclusion> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.openhis.triageandqueuemanage.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageQueueItem;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface TriageQueueItemMapper extends BaseMapper<TriageQueueItem> {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.openhis.triageandqueuemanage.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageCandidateExclusion;
|
||||
|
||||
public interface TriageCandidateExclusionService extends IService<TriageCandidateExclusion> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.openhis.triageandqueuemanage.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageQueueItem;
|
||||
|
||||
public interface TriageQueueItemService extends IService<TriageQueueItem> {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.openhis.triageandqueuemanage.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageCandidateExclusion;
|
||||
import com.openhis.triageandqueuemanage.mapper.TriageCandidateExclusionMapper;
|
||||
import com.openhis.triageandqueuemanage.service.TriageCandidateExclusionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class TriageCandidateExclusionServiceImpl extends ServiceImpl<TriageCandidateExclusionMapper, TriageCandidateExclusion> implements TriageCandidateExclusionService {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.openhis.triageandqueuemanage.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageQueueItem;
|
||||
import com.openhis.triageandqueuemanage.mapper.TriageQueueItemMapper;
|
||||
import com.openhis.triageandqueuemanage.service.TriageQueueItemService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class TriageQueueItemServiceImpl extends ServiceImpl<TriageQueueItemMapper, TriageQueueItem> implements TriageQueueItemService {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
-- 智能分诊排队:候选池排除记录表
|
||||
-- 用途:记录已从智能候选池中移除的患者(加入队列后不再出现在候选池)
|
||||
-- 注意:必须在后端实际连接的 schema 中执行(dev环境是 hisdev,test环境是 histest,prd环境是 hisprd)
|
||||
-- 执行前请先确认:SET search_path TO hisdev; (或对应的 schema)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS hisdev.triage_candidate_exclusion (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id INTEGER NOT NULL,
|
||||
exclusion_date DATE NOT NULL,
|
||||
encounter_id BIGINT NOT NULL,
|
||||
patient_id BIGINT,
|
||||
patient_name VARCHAR(255),
|
||||
organization_id BIGINT,
|
||||
organization_name VARCHAR(255),
|
||||
reason VARCHAR(100) DEFAULT 'ADDED_TO_QUEUE', -- 排除原因:ADDED_TO_QUEUE(加入队列)、MANUAL_REMOVE(手动移除)等
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
delete_flag CHAR(1) NOT NULL DEFAULT '0'
|
||||
);
|
||||
|
||||
-- 常用查询索引:按租户/日期/就诊记录查询
|
||||
CREATE INDEX IF NOT EXISTS idx_triage_candidate_exclusion_list
|
||||
ON hisdev.triage_candidate_exclusion (tenant_id, exclusion_date, delete_flag, encounter_id);
|
||||
|
||||
-- 防重复:同一天同租户同就诊记录只能有一条排除记录(未删除)
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_triage_candidate_exclusion_encounter
|
||||
ON hisdev.triage_candidate_exclusion (tenant_id, exclusion_date, encounter_id, delete_flag)
|
||||
WHERE delete_flag = '0';
|
||||
|
||||
-- 就诊记录ID索引(用于关联查询)
|
||||
CREATE INDEX IF NOT EXISTS idx_triage_candidate_exclusion_encounter_id
|
||||
ON hisdev.triage_candidate_exclusion (encounter_id);
|
||||
|
||||
-- 注释
|
||||
COMMENT ON TABLE hisdev.triage_candidate_exclusion IS '智能分诊排队候选池排除记录表';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.tenant_id IS '租户ID';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.exclusion_date IS '排除日期(通常为当天)';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.encounter_id IS '就诊记录ID(唯一标识)';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.patient_id IS '患者ID';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.patient_name IS '患者姓名';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.organization_id IS '科室ID';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.organization_name IS '科室名称';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.reason IS '排除原因:ADDED_TO_QUEUE(加入队列)、MANUAL_REMOVE(手动移除)等';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.delete_flag IS '删除标志:0-未删除,1-已删除';
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
-- 智能分诊排队:队列持久化表(与 TriageQueueItem.java 字段一致)
|
||||
-- 注意:必须在后端实际连接的 schema 中执行(dev环境是 hisdev,test环境是 histest,prd环境是 hisprd)
|
||||
-- 执行前请先确认:SET search_path TO hisdev; (或对应的 schema)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS triage_queue_item (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id INTEGER NOT NULL,
|
||||
queue_date DATE NOT NULL,
|
||||
organization_id BIGINT NOT NULL,
|
||||
organization_name VARCHAR(255),
|
||||
encounter_id BIGINT NOT NULL,
|
||||
patient_id BIGINT,
|
||||
patient_name VARCHAR(255),
|
||||
healthcare_name VARCHAR(255),
|
||||
practitioner_name VARCHAR(255),
|
||||
status VARCHAR(50) NOT NULL, -- WAITING/CALLING/SKIPPED/COMPLETED
|
||||
queue_order INTEGER NOT NULL,
|
||||
create_time TIMESTAMP,
|
||||
update_time TIMESTAMP,
|
||||
delete_flag CHAR(1) NOT NULL DEFAULT '0'
|
||||
);
|
||||
|
||||
-- 常用查询索引:按租户/科室/日期/状态/顺序取队列
|
||||
CREATE INDEX IF NOT EXISTS idx_triage_queue_item_list
|
||||
ON triage_queue_item (tenant_id, queue_date, organization_id, delete_flag, status, queue_order);
|
||||
|
||||
-- 防重复:同一天同租户同科室同就诊记录只能在队列里一条(未删除)
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_triage_queue_item_encounter
|
||||
ON triage_queue_item (tenant_id, queue_date, organization_id, encounter_id, delete_flag)
|
||||
WHERE delete_flag = '0';
|
||||
|
||||
-- 就诊记录ID索引(用于关联查询)
|
||||
CREATE INDEX IF NOT EXISTS idx_triage_queue_item_encounter_id
|
||||
ON triage_queue_item (encounter_id);
|
||||
|
||||
108
openhis-server-new/sql/debug_queue_date_issue.sql
Normal file
108
openhis-server-new/sql/debug_queue_date_issue.sql
Normal file
@@ -0,0 +1,108 @@
|
||||
-- ============================================
|
||||
-- 调试队列日期问题
|
||||
-- 用途:检查数据库中的 queue_date 字段值,找出为什么查询不到数据
|
||||
-- ============================================
|
||||
|
||||
-- 1. 查看所有队列记录的日期分布
|
||||
SELECT
|
||||
queue_date,
|
||||
status,
|
||||
COUNT(*) AS count,
|
||||
STRING_AGG(patient_name, ', ') AS patients
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
GROUP BY queue_date, status
|
||||
ORDER BY queue_date DESC, status;
|
||||
|
||||
-- 2. 查看指定科室的所有记录(不限制日期)
|
||||
SELECT
|
||||
id,
|
||||
queue_date,
|
||||
status,
|
||||
patient_name,
|
||||
organization_id,
|
||||
organization_name,
|
||||
queue_order,
|
||||
create_time
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
AND organization_id = 1995302136614969300
|
||||
ORDER BY queue_date DESC, queue_order ASC;
|
||||
|
||||
-- 3. 查看今天的记录(使用 CURRENT_DATE)
|
||||
SELECT
|
||||
id,
|
||||
queue_date,
|
||||
status,
|
||||
patient_name,
|
||||
organization_id,
|
||||
queue_order
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND organization_id = 1995302136614969300
|
||||
ORDER BY queue_order ASC;
|
||||
|
||||
-- 4. 查看 2026-01-16 的记录
|
||||
SELECT
|
||||
id,
|
||||
queue_date,
|
||||
status,
|
||||
patient_name,
|
||||
organization_id,
|
||||
queue_order
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
AND queue_date = '2026-01-16'::DATE
|
||||
AND organization_id = 1995302136614969300
|
||||
ORDER BY queue_order ASC;
|
||||
|
||||
-- 5. 查看 2025-01-16 的记录(可能是昨天的数据)
|
||||
SELECT
|
||||
id,
|
||||
queue_date,
|
||||
status,
|
||||
patient_name,
|
||||
organization_id,
|
||||
queue_order
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
AND queue_date = '2025-01-16'::DATE
|
||||
AND organization_id = 1995302136614969300
|
||||
ORDER BY queue_order ASC;
|
||||
|
||||
-- 6. 查看最近的记录(最近7天)
|
||||
SELECT
|
||||
queue_date,
|
||||
status,
|
||||
COUNT(*) AS count
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
AND queue_date >= CURRENT_DATE - INTERVAL '7 days'
|
||||
GROUP BY queue_date, status
|
||||
ORDER BY queue_date DESC, status;
|
||||
|
||||
-- 7. 检查系统当前日期(PostgreSQL)
|
||||
SELECT CURRENT_DATE AS current_date_in_db;
|
||||
|
||||
-- 8. 查看所有状态为 WAITING 或 SKIPPED 的记录(不限制日期)
|
||||
SELECT
|
||||
id,
|
||||
queue_date,
|
||||
status,
|
||||
patient_name,
|
||||
organization_id,
|
||||
queue_order
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
AND status IN ('WAITING', 'SKIPPED')
|
||||
AND organization_id = 1995302136614969300
|
||||
ORDER BY queue_date DESC, queue_order ASC;
|
||||
|
||||
313
openhis-server-new/sql/query_smart_candidate_pool.sql
Normal file
313
openhis-server-new/sql/query_smart_candidate_pool.sql
Normal file
@@ -0,0 +1,313 @@
|
||||
-- ============================================
|
||||
-- 智能候选池查询SQL(当日已挂号患者)
|
||||
-- 对应接口:GET /charge-manage/register/current-day-encounter
|
||||
-- 用途:查询当日已挂号但未入队的患者,用于智能分诊排队管理
|
||||
-- ============================================
|
||||
|
||||
-- 完整SQL查询(包含所有关联表)
|
||||
SELECT T9.tenant_id AS tenantId,
|
||||
T9.encounter_id AS encounterId,
|
||||
T9.display_order AS displayOrder,
|
||||
T9.organization_id AS organizationId,
|
||||
T9.organization_name AS organizationName,
|
||||
T9.healthcare_name AS healthcareName,
|
||||
T9.practitioner_user_id AS practitionerUserId,
|
||||
T9.practitioner_name AS practitionerName,
|
||||
T9.contract_name AS contractName,
|
||||
T9.patient_id AS patientId,
|
||||
T9.patient_name AS patientName,
|
||||
T9.phone,
|
||||
T9.gender_enum AS genderEnum,
|
||||
T9.id_card AS idCard,
|
||||
T9.status_enum AS statusEnum,
|
||||
T9.register_time AS registerTime,
|
||||
T9.total_price AS totalPrice,
|
||||
T9.account_name AS accountName,
|
||||
T9.enterer_name AS entererName,
|
||||
T9.charge_item_ids AS chargeItemIds,
|
||||
T9.payment_id AS paymentId,
|
||||
T9.picture_url AS pictureUrl,
|
||||
T9.birth_date AS birthDate,
|
||||
COALESCE(T9.identifier_no, T9.patient_bus_no, '') AS identifierNo
|
||||
FROM (
|
||||
SELECT T1.tenant_id AS tenant_id,
|
||||
T1.id AS encounter_id,
|
||||
T1.display_order AS display_order,
|
||||
T1.organization_id AS organization_id,
|
||||
T2.NAME AS organization_name,
|
||||
T3.NAME AS healthcare_name,
|
||||
T5.user_id AS practitioner_user_id,
|
||||
T5.NAME AS practitioner_name,
|
||||
T7.contract_name AS contract_name,
|
||||
T8.id AS patient_id,
|
||||
T8.NAME AS patient_name,
|
||||
T8.phone AS phone,
|
||||
T8.gender_enum AS gender_enum,
|
||||
T8.id_card AS id_card,
|
||||
T1.status_enum AS status_enum,
|
||||
T1.create_time AS register_time,
|
||||
T10.total_price,
|
||||
T11."name" AS account_name,
|
||||
T12."name" AS enterer_name,
|
||||
T13.charge_item_ids,
|
||||
T13.id AS payment_id,
|
||||
ai.picture_url AS picture_url,
|
||||
T8.birth_date AS birth_date,
|
||||
T8.bus_no AS patient_bus_no,
|
||||
T18.identifier_no AS identifier_no
|
||||
FROM adm_encounter AS T1
|
||||
-- 关联科室表
|
||||
LEFT JOIN adm_organization AS T2
|
||||
ON T1.organization_id = T2.ID
|
||||
AND T2.delete_flag = '0'
|
||||
-- 关联医疗服务类型表(号别)
|
||||
LEFT JOIN adm_healthcare_service AS T3
|
||||
ON T1.service_type_id = T3.ID
|
||||
AND T3.delete_flag = '0'
|
||||
-- 关联就诊参与者表(医生信息)
|
||||
LEFT JOIN (
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT *,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0'
|
||||
AND (
|
||||
-- 如果就诊状态为"进行中",查找"接诊医生"(admitter)
|
||||
(type_code = 'admitter' AND EXISTS(SELECT 1
|
||||
FROM adm_encounter AS T1
|
||||
WHERE T1.status_enum = 2 -- IN_PROGRESS
|
||||
AND T1.ID = encounter_id))
|
||||
OR
|
||||
-- 如果就诊状态不是"进行中",查找"挂号医生"(registration_doctor)
|
||||
(type_code = 'registration_doctor' AND NOT EXISTS(SELECT 1
|
||||
FROM adm_encounter AS T1
|
||||
WHERE T1.status_enum = 2 -- IN_PROGRESS
|
||||
AND T1.ID = encounter_id))
|
||||
)
|
||||
) t
|
||||
WHERE rn = 1
|
||||
) AS T4 ON T1.ID = T4.encounter_id
|
||||
-- 关联医生表
|
||||
LEFT JOIN adm_practitioner AS T5
|
||||
ON T5.ID = T4.practitioner_id
|
||||
AND T5.delete_flag = '0'
|
||||
-- 关联账户表
|
||||
LEFT JOIN adm_account AS T6
|
||||
ON T1.ID = T6.encounter_id
|
||||
AND T6.delete_flag = '0'
|
||||
AND T6.encounter_flag = '1'
|
||||
-- 关联合同表
|
||||
LEFT JOIN fin_contract AS T7
|
||||
ON T6.contract_no = T7.bus_no
|
||||
AND T7.delete_flag = '0'
|
||||
-- 关联患者表
|
||||
LEFT JOIN adm_patient AS T8
|
||||
ON T1.patient_id = T8.ID
|
||||
AND T8.delete_flag = '0'
|
||||
-- 关联患者标识表(身份证号等)
|
||||
LEFT JOIN (
|
||||
SELECT patient_id,
|
||||
identifier_no
|
||||
FROM (
|
||||
SELECT patient_id,
|
||||
identifier_no,
|
||||
ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_patient_identifier
|
||||
WHERE delete_flag = '0'
|
||||
AND identifier_no IS NOT NULL
|
||||
AND identifier_no != ''
|
||||
) t
|
||||
WHERE rn = 1
|
||||
) AS T18 ON T8.id = T18.patient_id
|
||||
-- 关联收费项目表(挂号费)
|
||||
LEFT JOIN adm_charge_item AS T10
|
||||
ON T1.id = T10.encounter_id
|
||||
AND T10.delete_flag = '0'
|
||||
AND T10.context_enum = 1 -- REGISTER(挂号)
|
||||
-- 关联账户表(收费账户)
|
||||
LEFT JOIN adm_account AS T11
|
||||
ON T10.account_id = T11.id
|
||||
AND T11.delete_flag = '0'
|
||||
-- 关联医生表(收费医生)
|
||||
LEFT JOIN adm_practitioner AS T12
|
||||
ON T12.ID = T10.enterer_id
|
||||
AND T12.delete_flag = '0'
|
||||
-- 关联支付对账表(已支付)
|
||||
LEFT JOIN fin_payment_reconciliation T13
|
||||
ON T10.id::TEXT = ANY(string_to_array(T13.charge_item_ids,','))
|
||||
AND T13.delete_flag = '0'
|
||||
AND T13.status_enum = 1 -- SUCCESS(支付成功)
|
||||
-- 关联退号记录(当状态为退号时,通过relation_id关联原支付记录)
|
||||
LEFT JOIN fin_payment_reconciliation T14
|
||||
ON T13.id = T14.relation_id
|
||||
AND T14.delete_flag = '0'
|
||||
AND T14.status_enum = 3 -- REFUND(退款)
|
||||
AND T14.payment_enum = 1
|
||||
-- 关联退号医生
|
||||
LEFT JOIN adm_practitioner AS T15
|
||||
ON T15.ID = T14.enterer_id
|
||||
AND T15.delete_flag = '0'
|
||||
-- 关联系统用户表
|
||||
LEFT JOIN sys_user AS T17
|
||||
ON T17.user_id = T15.user_id
|
||||
AND T17.delete_flag = '0'
|
||||
-- 关联退号支付详情,获取退款方式(聚合多个支付方式)
|
||||
LEFT JOIN (
|
||||
SELECT reconciliation_id,
|
||||
STRING_AGG(
|
||||
CASE pay_enum
|
||||
WHEN 220400 THEN '现金'
|
||||
WHEN 220100 THEN '微信'
|
||||
WHEN 220200 THEN '支付宝'
|
||||
WHEN 220300 THEN '银联'
|
||||
END,
|
||||
','
|
||||
ORDER BY pay_enum
|
||||
) AS refund_method
|
||||
FROM fin_payment_rec_detail
|
||||
WHERE delete_flag = '0'
|
||||
AND amount < 0
|
||||
AND pay_enum IN (220400, 220100, 220200, 220300)
|
||||
GROUP BY reconciliation_id
|
||||
) AS T16 ON T14.id = T16.reconciliation_id
|
||||
-- 关联发票表
|
||||
LEFT JOIN adm_invoice AS ai
|
||||
ON ai.reconciliation_id = T13.id
|
||||
AND ai.delete_flag = '0'
|
||||
WHERE T1.delete_flag = '0'
|
||||
AND T1.class_enum = 1 -- 门诊(AMB)
|
||||
AND T10.context_enum = 1 -- 挂号(REGISTER)
|
||||
-- 动态条件(由前端传入,通过 ${ew.customSqlSegment} 注入)
|
||||
-- 例如:AND T1.create_time::DATE = CURRENT_DATE -- 当日挂号
|
||||
-- 例如:AND T1.status_enum != 6 -- 排除退号(statusEnum = -1时)
|
||||
-- 例如:AND (T8.name LIKE '%关键词%' OR T2.name LIKE '%关键词%' ...) -- 模糊搜索
|
||||
) AS T9
|
||||
-- 动态查询条件(由 MyBatis-Plus QueryWrapper 生成)
|
||||
-- ${ew.customSqlSegment}
|
||||
ORDER BY T9.register_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 简化版查询(仅查询核心字段,用于快速测试)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
pt.name AS patientName,
|
||||
pt.birth_date AS birthDate,
|
||||
org.id AS organizationId,
|
||||
org.name AS organizationName,
|
||||
hcs.name AS healthcareName,
|
||||
pr.name AS practitionerName,
|
||||
enc.create_time AS registerTime,
|
||||
enc.status_enum AS statusEnum
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt
|
||||
ON enc.patient_id = pt.id
|
||||
AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org
|
||||
ON enc.organization_id = org.id
|
||||
AND org.delete_flag = '0'
|
||||
LEFT JOIN adm_healthcare_service hcs
|
||||
ON enc.service_type_id = hcs.id
|
||||
AND hcs.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT encounter_id, practitioner_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0'
|
||||
AND type_code IN ('admitter', 'registration_doctor')
|
||||
) ep ON enc.id = ep.encounter_id AND ep.rn = 1
|
||||
LEFT JOIN adm_practitioner pr
|
||||
ON ep.practitioner_id = pr.id
|
||||
AND pr.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci
|
||||
ON enc.id = ci.encounter_id
|
||||
AND ci.delete_flag = '0'
|
||||
AND ci.context_enum = 1 -- 挂号
|
||||
INNER JOIN fin_payment_reconciliation prc
|
||||
ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0'
|
||||
AND prc.status_enum = 1 -- 支付成功
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1 -- 门诊
|
||||
AND enc.create_time::DATE = CURRENT_DATE -- 当日挂号
|
||||
AND enc.status_enum != 6 -- 排除退号
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 查询条件说明
|
||||
-- ============================================
|
||||
-- 1. 基础条件:
|
||||
-- - enc.delete_flag = '0' -- 未删除
|
||||
-- - enc.class_enum = 1 -- 门诊(AMB)
|
||||
-- - ci.context_enum = 1 -- 挂号(REGISTER)
|
||||
-- - prc.status_enum = 1 -- 支付成功(SUCCESS)
|
||||
--
|
||||
-- 2. 日期过滤:
|
||||
-- - enc.create_time::DATE = CURRENT_DATE -- 当日挂号(由前端传入 date 参数)
|
||||
--
|
||||
-- 3. 状态过滤:
|
||||
-- - enc.status_enum != 6 -- 排除退号(当 statusEnum = -1 时)
|
||||
-- - enc.status_enum = 2 -- 进行中(IN_PROGRESS,用于判断医生类型)
|
||||
--
|
||||
-- 4. 模糊搜索(由前端传入 searchKey 参数):
|
||||
-- - patient_name(患者姓名)
|
||||
-- - organization_name(科室名称)
|
||||
-- - practitioner_name(医生姓名)
|
||||
-- - healthcare_name(号别名称)
|
||||
-- - identifier_no(身份证号)
|
||||
--
|
||||
-- 5. 租户过滤(由后端自动添加):
|
||||
-- - tenant_id = ? -- 当前登录用户的租户ID
|
||||
--
|
||||
-- 6. 分页(由 MyBatis-Plus Page 对象处理):
|
||||
-- - LIMIT ? OFFSET ?
|
||||
|
||||
-- ============================================
|
||||
-- 常用查询示例
|
||||
-- ============================================
|
||||
|
||||
-- 示例1:查询今天所有已挂号患者(排除退号)
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
pt.name AS patientName,
|
||||
org.name AS organizationName,
|
||||
hcs.name AS healthcareName,
|
||||
pr.name AS practitionerName,
|
||||
enc.create_time AS registerTime
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org ON enc.organization_id = org.id AND org.delete_flag = '0'
|
||||
LEFT JOIN adm_healthcare_service hcs ON enc.service_type_id = hcs.id AND hcs.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT encounter_id, practitioner_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0' AND type_code IN ('admitter', 'registration_doctor')
|
||||
) ep ON enc.id = ep.encounter_id AND ep.rn = 1
|
||||
LEFT JOIN adm_practitioner pr ON ep.practitioner_id = pr.id AND pr.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- 示例2:查询指定科室的已挂号患者
|
||||
-- 在示例1的基础上添加:
|
||||
-- AND enc.organization_id = ? -- 科室ID
|
||||
|
||||
-- 示例3:按患者姓名模糊搜索
|
||||
-- 在示例1的基础上添加:
|
||||
-- AND pt.name LIKE '%关键词%'
|
||||
|
||||
-- 示例4:查询已加入队列的患者(用于去重)
|
||||
SELECT DISTINCT encounter_id
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND status != 'COMPLETED';
|
||||
|
||||
268
openhis-server-new/sql/query_smart_candidate_pool_page.sql
Normal file
268
openhis-server-new/sql/query_smart_candidate_pool_page.sql
Normal file
@@ -0,0 +1,268 @@
|
||||
-- ============================================
|
||||
-- 智能候选池查询SQL(智能分诊排队管理页面专用)
|
||||
-- 页面:智能分诊排队管理 - 心内科
|
||||
-- 用途:查询"已签到未入队"的患者(当日已挂号,但未加入队列)
|
||||
-- ============================================
|
||||
|
||||
-- ============================================
|
||||
-- 方案一:完整查询(包含所有字段,排除已在队列中的患者)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
pt.name AS patientName,
|
||||
pt.birth_date AS birthDate,
|
||||
-- 计算年龄(前端会调用 parseAge 函数)
|
||||
CASE
|
||||
WHEN pt.birth_date IS NOT NULL THEN
|
||||
EXTRACT(YEAR FROM AGE(pt.birth_date))::TEXT || '岁'
|
||||
ELSE ''
|
||||
END AS age,
|
||||
org.id AS organizationId,
|
||||
org.name AS organizationName,
|
||||
hcs.name AS healthcareName,
|
||||
pr.name AS practitionerName,
|
||||
enc.create_time AS registerTime,
|
||||
enc.status_enum AS statusEnum,
|
||||
-- 其他字段(如果需要)
|
||||
pt.phone,
|
||||
pt.gender_enum AS genderEnum,
|
||||
pt.id_card AS idCard
|
||||
FROM adm_encounter enc
|
||||
-- 关联患者表
|
||||
INNER JOIN adm_patient pt
|
||||
ON enc.patient_id = pt.id
|
||||
AND pt.delete_flag = '0'
|
||||
-- 关联科室表
|
||||
LEFT JOIN adm_organization org
|
||||
ON enc.organization_id = org.id
|
||||
AND org.delete_flag = '0'
|
||||
-- 关联医疗服务类型表(号别)
|
||||
LEFT JOIN adm_healthcare_service hcs
|
||||
ON enc.service_type_id = hcs.id
|
||||
AND hcs.delete_flag = '0'
|
||||
-- 关联就诊参与者表(医生信息)
|
||||
LEFT JOIN (
|
||||
SELECT encounter_id, practitioner_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0'
|
||||
AND (
|
||||
-- 如果就诊状态为"进行中",查找"接诊医生"(admitter)
|
||||
(type_code = 'admitter' AND EXISTS(SELECT 1
|
||||
FROM adm_encounter AS e
|
||||
WHERE e.status_enum = 2 -- IN_PROGRESS
|
||||
AND e.id = encounter_id))
|
||||
OR
|
||||
-- 如果就诊状态不是"进行中",查找"挂号医生"(registration_doctor)
|
||||
(type_code = 'registration_doctor' AND NOT EXISTS(SELECT 1
|
||||
FROM adm_encounter AS e
|
||||
WHERE e.status_enum = 2 -- IN_PROGRESS
|
||||
AND e.id = encounter_id))
|
||||
)
|
||||
) ep ON enc.id = ep.encounter_id AND ep.rn = 1
|
||||
-- 关联医生表
|
||||
LEFT JOIN adm_practitioner pr
|
||||
ON ep.practitioner_id = pr.id
|
||||
AND pr.delete_flag = '0'
|
||||
-- 关联收费项目表(挂号费)
|
||||
INNER JOIN adm_charge_item ci
|
||||
ON enc.id = ci.encounter_id
|
||||
AND ci.delete_flag = '0'
|
||||
AND ci.context_enum = 1 -- REGISTER(挂号)
|
||||
-- 关联支付对账表(已支付)
|
||||
INNER JOIN fin_payment_reconciliation prc
|
||||
ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0'
|
||||
AND prc.status_enum = 1 -- SUCCESS(支付成功)
|
||||
-- 排除已在队列中的患者(关键:LEFT JOIN + WHERE IS NULL)
|
||||
LEFT JOIN hisdev.triage_queue_item tqi
|
||||
ON enc.id = tqi.encounter_id
|
||||
AND tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED' -- 排除已完成的状态
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1 -- 门诊(AMB)
|
||||
AND enc.create_time::DATE = CURRENT_DATE -- 当日挂号(前端会过滤 registerTime)
|
||||
AND enc.status_enum != 6 -- 排除退号(statusEnum = -1 时)
|
||||
AND tqi.id IS NULL -- 关键:只查询不在队列中的患者
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 方案二:简化查询(仅核心字段,便于快速测试)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
pt.name AS patientName,
|
||||
org.id AS organizationId,
|
||||
org.name AS organizationName,
|
||||
hcs.name AS healthcareName,
|
||||
pr.name AS practitionerName,
|
||||
enc.create_time AS registerTime
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org ON enc.organization_id = org.id AND org.delete_flag = '0'
|
||||
LEFT JOIN adm_healthcare_service hcs ON enc.service_type_id = hcs.id AND hcs.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT encounter_id, practitioner_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0' AND type_code IN ('admitter', 'registration_doctor')
|
||||
) ep ON enc.id = ep.encounter_id AND ep.rn = 1
|
||||
LEFT JOIN adm_practitioner pr ON ep.practitioner_id = pr.id AND pr.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
-- 排除已在队列中的患者
|
||||
LEFT JOIN hisdev.triage_queue_item tqi
|
||||
ON enc.id = tqi.encounter_id
|
||||
AND tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED'
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6
|
||||
AND tqi.id IS NULL -- 不在队列中
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 方案三:分步查询(便于调试和理解)
|
||||
-- ============================================
|
||||
|
||||
-- 步骤1:查询当日已挂号的患者(不排除队列)
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
pt.name AS patientName,
|
||||
org.name AS organizationName,
|
||||
enc.create_time AS registerTime
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org ON enc.organization_id = org.id AND org.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6;
|
||||
|
||||
-- 步骤2:查询已在队列中的患者
|
||||
SELECT DISTINCT encounter_id
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND status != 'COMPLETED';
|
||||
|
||||
-- 步骤3:合并查询(使用 NOT EXISTS 或 NOT IN)
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
pt.name AS patientName,
|
||||
org.name AS organizationName,
|
||||
enc.create_time AS registerTime
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org ON enc.organization_id = org.id AND org.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6
|
||||
-- 排除已在队列中的患者
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM hisdev.triage_queue_item tqi
|
||||
WHERE tqi.encounter_id = enc.id
|
||||
AND tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED'
|
||||
)
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 查询条件说明
|
||||
-- ============================================
|
||||
-- 1. 基础条件:
|
||||
-- - enc.delete_flag = '0' -- 未删除
|
||||
-- - enc.class_enum = 1 -- 门诊(AMB)
|
||||
-- - ci.context_enum = 1 -- 挂号(REGISTER)
|
||||
-- - prc.status_enum = 1 -- 支付成功(SUCCESS)
|
||||
--
|
||||
-- 2. 日期过滤:
|
||||
-- - enc.create_time::DATE = CURRENT_DATE -- 当日挂号
|
||||
-- 注意:前端还会基于 registerTime 字段再次过滤今天的数据
|
||||
--
|
||||
-- 3. 状态过滤:
|
||||
-- - enc.status_enum != 6 -- 排除退号(对应前端 statusEnum = -1)
|
||||
--
|
||||
-- 4. 队列去重:
|
||||
-- - LEFT JOIN hisdev.triage_queue_item ... WHERE tqi.id IS NULL
|
||||
-- 或者
|
||||
-- - NOT EXISTS (SELECT 1 FROM hisdev.triage_queue_item ...)
|
||||
-- 排除已在队列中且状态不是 COMPLETED 的患者
|
||||
--
|
||||
-- 5. 租户过滤(如果需要):
|
||||
-- - enc.tenant_id = ? -- 当前登录用户的租户ID
|
||||
--
|
||||
-- 6. 模糊搜索(如果需要):
|
||||
-- - pt.name LIKE '%关键词%' -- 患者姓名
|
||||
-- - org.name LIKE '%关键词%' -- 科室名称
|
||||
-- - pr.name LIKE '%关键词%' -- 医生姓名
|
||||
-- - hcs.name LIKE '%关键词%' -- 号别名称
|
||||
|
||||
-- ============================================
|
||||
-- 调试查询(检查数据是否正确)
|
||||
-- ============================================
|
||||
|
||||
-- 1. 检查当日已挂号患者总数
|
||||
SELECT COUNT(*) AS total_registered
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6;
|
||||
|
||||
-- 2. 检查已在队列中的患者数量
|
||||
SELECT COUNT(DISTINCT encounter_id) AS in_queue_count
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND status != 'COMPLETED';
|
||||
|
||||
-- 3. 检查候选池应该显示的患者数量(已挂号 - 已在队列)
|
||||
SELECT
|
||||
(SELECT COUNT(*)
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6)
|
||||
-
|
||||
(SELECT COUNT(DISTINCT encounter_id)
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND status != 'COMPLETED')
|
||||
AS candidate_pool_count;
|
||||
|
||||
-- 4. 查看队列中的患者详情(用于对比)
|
||||
SELECT
|
||||
tqi.encounter_id,
|
||||
tqi.patient_name,
|
||||
tqi.status,
|
||||
tqi.queue_order
|
||||
FROM hisdev.triage_queue_item tqi
|
||||
WHERE tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED'
|
||||
ORDER BY tqi.queue_order;
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
-- ============================================
|
||||
-- 智能候选池查询SQL(完整版 - 包含排除列表)
|
||||
-- 页面:智能分诊排队管理 - 心内科
|
||||
-- 用途:查询"已签到未入队"的患者(当日已挂号,但未加入队列,且未在排除列表中)
|
||||
-- 更新日期:2025-01-15
|
||||
-- ============================================
|
||||
|
||||
-- ============================================
|
||||
-- 方案一:完整查询(包含所有字段,排除已在队列和排除列表中的患者)
|
||||
-- ============================================
|
||||
SELECT
|
||||
T9.tenant_id AS tenantId,
|
||||
T9.encounter_id AS encounterId,
|
||||
T9.display_order AS displayOrder,
|
||||
T9.organization_id AS organizationId,
|
||||
T9.organization_name AS organizationName,
|
||||
T9.healthcare_name AS healthcareName,
|
||||
T9.practitioner_user_id AS practitionerUserId,
|
||||
T9.practitioner_name AS practitionerName,
|
||||
T9.contract_name AS contractName,
|
||||
T9.patient_id AS patientId,
|
||||
T9.patient_name AS patientName,
|
||||
T9.phone,
|
||||
T9.gender_enum AS genderEnum,
|
||||
T9.id_card AS idCard,
|
||||
T9.status_enum AS statusEnum,
|
||||
T9.register_time AS registerTime,
|
||||
T9.total_price AS totalPrice,
|
||||
T9.account_name AS accountName,
|
||||
T9.enterer_name AS entererName,
|
||||
T9.charge_item_ids AS chargeItemIds,
|
||||
T9.payment_id AS paymentId,
|
||||
T9.picture_url AS pictureUrl,
|
||||
T9.birth_date AS birthDate,
|
||||
COALESCE(T9.identifier_no, T9.patient_bus_no, '') AS identifierNo,
|
||||
-- 计算年龄(前端会调用 parseAge 函数)
|
||||
CASE
|
||||
WHEN T9.birth_date IS NOT NULL THEN
|
||||
EXTRACT(YEAR FROM AGE(T9.birth_date))::TEXT || '岁'
|
||||
ELSE ''
|
||||
END AS age
|
||||
FROM (
|
||||
SELECT T1.tenant_id AS tenant_id,
|
||||
T1.id AS encounter_id,
|
||||
T1.display_order AS display_order,
|
||||
T1.organization_id AS organization_id,
|
||||
T2.NAME AS organization_name,
|
||||
T3.NAME AS healthcare_name,
|
||||
T5.user_id AS practitioner_user_id,
|
||||
T5.NAME AS practitioner_name,
|
||||
T7.contract_name AS contract_name,
|
||||
T8.id AS patient_id,
|
||||
T8.NAME AS patient_name,
|
||||
T8.phone AS phone,
|
||||
T8.gender_enum AS gender_enum,
|
||||
T8.id_card AS id_card,
|
||||
T1.status_enum AS status_enum,
|
||||
T1.create_time AS register_time,
|
||||
T10.total_price,
|
||||
T11."name" AS account_name,
|
||||
T12."name" AS enterer_name,
|
||||
T13.charge_item_ids,
|
||||
T13.id AS payment_id,
|
||||
ai.picture_url AS picture_url,
|
||||
T8.birth_date AS birth_date,
|
||||
T8.bus_no AS patient_bus_no,
|
||||
T18.identifier_no AS identifier_no
|
||||
FROM adm_encounter AS T1
|
||||
LEFT JOIN adm_organization AS T2 ON T1.organization_id = T2.ID AND T2.delete_flag = '0'
|
||||
LEFT JOIN adm_healthcare_service AS T3 ON T1.service_type_id = T3.ID AND T3.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT *,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0'
|
||||
AND (
|
||||
(type_code = 'admitter' AND EXISTS(SELECT 1
|
||||
FROM adm_encounter AS T1
|
||||
WHERE T1.status_enum = 2 -- IN_PROGRESS
|
||||
AND T1.ID = encounter_id))
|
||||
OR
|
||||
(type_code = 'registration_doctor' AND NOT EXISTS(SELECT 1
|
||||
FROM adm_encounter AS T1
|
||||
WHERE T1.status_enum = 2 -- IN_PROGRESS
|
||||
AND T1.ID = encounter_id))
|
||||
)
|
||||
) t
|
||||
WHERE rn = 1
|
||||
) AS T4 ON T1.ID = T4.encounter_id
|
||||
LEFT JOIN adm_practitioner AS T5 ON T5.ID = T4.practitioner_id AND T5.delete_flag = '0'
|
||||
LEFT JOIN adm_account AS T6
|
||||
ON T1.ID = T6.encounter_id AND T6.delete_flag = '0' AND T6.encounter_flag = '1'
|
||||
LEFT JOIN fin_contract AS T7 ON T6.contract_no = T7.bus_no AND T7.delete_flag = '0'
|
||||
LEFT JOIN adm_patient AS T8 ON T1.patient_id = T8.ID AND T8.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT patient_id,
|
||||
identifier_no
|
||||
FROM (
|
||||
SELECT patient_id,
|
||||
identifier_no,
|
||||
ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_patient_identifier
|
||||
WHERE delete_flag = '0'
|
||||
AND identifier_no IS NOT NULL
|
||||
AND identifier_no != ''
|
||||
) t
|
||||
WHERE rn = 1
|
||||
) AS T18 ON T8.id = T18.patient_id
|
||||
LEFT JOIN adm_charge_item AS T10 ON T1.id = T10.encounter_id AND T10.delete_flag = '0'
|
||||
LEFT JOIN adm_account AS T11 ON T10.account_id = T11.id AND T11.delete_flag = '0'
|
||||
LEFT JOIN adm_practitioner AS T12 ON T12.ID = T10.enterer_id AND T12.delete_flag = '0'
|
||||
LEFT JOIN fin_payment_reconciliation T13
|
||||
ON T10.id::TEXT = ANY(string_to_array(T13.charge_item_ids,','))
|
||||
AND T13.delete_flag = '0'
|
||||
AND T13.status_enum = 1 -- SUCCESS(支付成功)
|
||||
LEFT JOIN adm_invoice AS ai
|
||||
ON ai.reconciliation_id = T13.id AND ai.delete_flag = '0'
|
||||
WHERE T1.delete_flag = '0'
|
||||
AND T1.class_enum = 1 -- 门诊(AMB)
|
||||
AND T1.create_time::DATE = CURRENT_DATE -- 当日挂号
|
||||
AND T1.status_enum != 6 -- 排除退号
|
||||
AND T10.context_enum = 1 -- REGISTER(挂号)
|
||||
) AS T9
|
||||
-- 排除已在队列中的患者(关键:LEFT JOIN + WHERE IS NULL)
|
||||
LEFT JOIN hisdev.triage_queue_item tqi
|
||||
ON T9.encounter_id = tqi.encounter_id
|
||||
AND tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED' -- 排除已完成的状态
|
||||
-- 排除已在排除列表中的患者(新增:关键:LEFT JOIN + WHERE IS NULL)
|
||||
LEFT JOIN hisdev.triage_candidate_exclusion tce
|
||||
ON T9.encounter_id = tce.encounter_id
|
||||
AND tce.delete_flag = '0'
|
||||
AND tce.exclusion_date = CURRENT_DATE
|
||||
AND tce.tenant_id = T9.tenant_id -- 按租户过滤
|
||||
WHERE tqi.id IS NULL -- 关键:只查询不在队列中的患者
|
||||
AND tce.id IS NULL -- 关键:只查询不在排除列表中的患者
|
||||
ORDER BY T9.register_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 方案二:简化查询(仅核心字段,便于快速测试)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
pt.name AS patientName,
|
||||
org.id AS organizationId,
|
||||
org.name AS organizationName,
|
||||
hcs.name AS healthcareName,
|
||||
pr.name AS practitionerName,
|
||||
enc.create_time AS registerTime
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org ON enc.organization_id = org.id AND org.delete_flag = '0'
|
||||
LEFT JOIN adm_healthcare_service hcs ON enc.service_type_id = hcs.id AND hcs.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT encounter_id, practitioner_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0' AND type_code IN ('admitter', 'registration_doctor')
|
||||
) ep ON enc.id = ep.encounter_id AND ep.rn = 1
|
||||
LEFT JOIN adm_practitioner pr ON ep.practitioner_id = pr.id AND pr.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
-- 排除已在队列中的患者
|
||||
LEFT JOIN hisdev.triage_queue_item tqi
|
||||
ON enc.id = tqi.encounter_id
|
||||
AND tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED'
|
||||
-- 排除已在排除列表中的患者(新增)
|
||||
LEFT JOIN hisdev.triage_candidate_exclusion tce
|
||||
ON enc.id = tce.encounter_id
|
||||
AND tce.delete_flag = '0'
|
||||
AND tce.exclusion_date = CURRENT_DATE
|
||||
AND tce.tenant_id = enc.tenant_id
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6
|
||||
AND tqi.id IS NULL -- 不在队列中
|
||||
AND tce.id IS NULL -- 不在排除列表中
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 方案三:使用 NOT EXISTS 子查询(性能可能更好)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
pt.name AS patientName,
|
||||
org.id AS organizationId,
|
||||
org.name AS organizationName,
|
||||
hcs.name AS healthcareName,
|
||||
pr.name AS practitionerName,
|
||||
enc.create_time AS registerTime
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org ON enc.organization_id = org.id AND org.delete_flag = '0'
|
||||
LEFT JOIN adm_healthcare_service hcs ON enc.service_type_id = hcs.id AND hcs.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT encounter_id, practitioner_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0' AND type_code IN ('admitter', 'registration_doctor')
|
||||
) ep ON enc.id = ep.encounter_id AND ep.rn = 1
|
||||
LEFT JOIN adm_practitioner pr ON ep.practitioner_id = pr.id AND pr.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6
|
||||
-- 排除已在队列中的患者
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM hisdev.triage_queue_item tqi
|
||||
WHERE tqi.encounter_id = enc.id
|
||||
AND tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED'
|
||||
)
|
||||
-- 排除已在排除列表中的患者(新增)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM hisdev.triage_candidate_exclusion tce
|
||||
WHERE tce.encounter_id = enc.id
|
||||
AND tce.delete_flag = '0'
|
||||
AND tce.exclusion_date = CURRENT_DATE
|
||||
AND tce.tenant_id = enc.tenant_id
|
||||
)
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 查询条件说明
|
||||
-- ============================================
|
||||
-- 1. 基础条件:
|
||||
-- - enc.delete_flag = '0' -- 未删除
|
||||
-- - enc.class_enum = 1 -- 门诊(AMB)
|
||||
-- - ci.context_enum = 1 -- 挂号(REGISTER)
|
||||
-- - prc.status_enum = 1 -- 支付成功(SUCCESS)
|
||||
--
|
||||
-- 2. 日期过滤:
|
||||
-- - enc.create_time::DATE = CURRENT_DATE -- 当日挂号
|
||||
-- 注意:前端还会基于 registerTime 字段再次过滤今天的数据
|
||||
--
|
||||
-- 3. 状态过滤:
|
||||
-- - enc.status_enum != 6 -- 排除退号(对应前端 statusEnum = -1)
|
||||
--
|
||||
-- 4. 队列去重:
|
||||
-- - LEFT JOIN hisdev.triage_queue_item ... WHERE tqi.id IS NULL
|
||||
-- 或者
|
||||
-- - NOT EXISTS (SELECT 1 FROM hisdev.triage_queue_item ...)
|
||||
-- 排除已在队列中且状态不是 COMPLETED 的患者
|
||||
--
|
||||
-- 5. 排除列表去重(新增):
|
||||
-- - LEFT JOIN hisdev.triage_candidate_exclusion ... WHERE tce.id IS NULL
|
||||
-- 或者
|
||||
-- - NOT EXISTS (SELECT 1 FROM hisdev.triage_candidate_exclusion ...)
|
||||
-- 排除已在排除列表中的患者(已加入队列后被记录)
|
||||
--
|
||||
-- 6. 租户过滤(如果需要):
|
||||
-- - enc.tenant_id = ? -- 当前登录用户的租户ID
|
||||
--
|
||||
-- 7. 模糊搜索(如果需要):
|
||||
-- - pt.name LIKE '%关键词%' -- 患者姓名
|
||||
-- - org.name LIKE '%关键词%' -- 科室名称
|
||||
-- - pr.name LIKE '%关键词%' -- 医生姓名
|
||||
-- - hcs.name LIKE '%关键词%' -- 号别名称
|
||||
|
||||
-- ============================================
|
||||
-- 调试查询(检查数据是否正确)
|
||||
-- ============================================
|
||||
|
||||
-- 1. 检查当日已挂号患者总数
|
||||
SELECT COUNT(*) AS total_registered
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6;
|
||||
|
||||
-- 2. 检查已在队列中的患者数量
|
||||
SELECT COUNT(DISTINCT encounter_id) AS in_queue_count
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND status != 'COMPLETED';
|
||||
|
||||
-- 3. 检查已在排除列表中的患者数量(新增)
|
||||
SELECT COUNT(DISTINCT encounter_id) AS in_exclusion_count
|
||||
FROM hisdev.triage_candidate_exclusion
|
||||
WHERE delete_flag = '0'
|
||||
AND exclusion_date = CURRENT_DATE;
|
||||
|
||||
-- 4. 检查候选池应该显示的患者数量(已挂号 - 已在队列 - 已在排除列表)
|
||||
SELECT
|
||||
(SELECT COUNT(*)
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6)
|
||||
-
|
||||
(SELECT COUNT(DISTINCT encounter_id)
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND status != 'COMPLETED')
|
||||
-
|
||||
(SELECT COUNT(DISTINCT encounter_id)
|
||||
FROM hisdev.triage_candidate_exclusion
|
||||
WHERE delete_flag = '0'
|
||||
AND exclusion_date = CURRENT_DATE)
|
||||
AS candidate_pool_count;
|
||||
|
||||
-- 5. 查看队列中的患者详情(用于对比)
|
||||
SELECT
|
||||
tqi.encounter_id,
|
||||
tqi.patient_name,
|
||||
tqi.status,
|
||||
tqi.queue_order
|
||||
FROM hisdev.triage_queue_item tqi
|
||||
WHERE tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED'
|
||||
ORDER BY tqi.queue_order;
|
||||
|
||||
-- 6. 查看排除列表中的患者详情(新增)
|
||||
SELECT
|
||||
tce.encounter_id,
|
||||
tce.patient_name,
|
||||
tce.reason,
|
||||
tce.create_time
|
||||
FROM hisdev.triage_candidate_exclusion tce
|
||||
WHERE tce.delete_flag = '0'
|
||||
AND tce.exclusion_date = CURRENT_DATE
|
||||
ORDER BY tce.create_time DESC;
|
||||
|
||||
-- 7. 查看重叠的患者(既在队列中,又在排除列表中)
|
||||
SELECT
|
||||
tqi.encounter_id,
|
||||
tqi.patient_name AS queue_patient_name,
|
||||
tce.patient_name AS exclusion_patient_name,
|
||||
tqi.status,
|
||||
tce.reason
|
||||
FROM hisdev.triage_queue_item tqi
|
||||
INNER JOIN hisdev.triage_candidate_exclusion tce
|
||||
ON tqi.encounter_id = tce.encounter_id
|
||||
AND tqi.queue_date = tce.exclusion_date
|
||||
AND tqi.tenant_id = tce.tenant_id
|
||||
WHERE tqi.delete_flag = '0'
|
||||
AND tce.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
ORDER BY tqi.encounter_id;
|
||||
|
||||
18
openhis-ui-vue3/package-lock.json
generated
18
openhis-ui-vue3/package-lock.json
generated
@@ -1637,7 +1637,6 @@
|
||||
"resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz",
|
||||
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
@@ -1648,7 +1647,6 @@
|
||||
"integrity": "sha512-gWEkeiyYE4vqjON/+Obqcoeffmk0NF15WSBwSs7zwVA2bAbTaE0SJ7P0WNGoJn8uE7fiaV5a7dKYIJriEqOrmA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
@@ -3335,7 +3333,6 @@
|
||||
"resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -5578,15 +5575,13 @@
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash-unified": {
|
||||
"version": "1.0.3",
|
||||
@@ -6539,7 +6534,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
@@ -6780,7 +6774,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
@@ -6793,7 +6786,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.0"
|
||||
@@ -7045,7 +7037,6 @@
|
||||
"integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.8"
|
||||
},
|
||||
@@ -7224,7 +7215,6 @@
|
||||
"integrity": "sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
"immutable": "^4.0.0",
|
||||
@@ -7257,7 +7247,6 @@
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/segmentit/-/segmentit-2.0.3.tgz",
|
||||
"integrity": "sha512-7mn2XL3OdTUQ+AhHz7SbgyxLTaQRzTWQNVwiK+UlTO8aePGbSwvKUzTwE4238+OUY9MoR6ksAg35zl8sfTunQQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"preval.macro": "^4.0.0"
|
||||
}
|
||||
@@ -8457,7 +8446,6 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -8819,7 +8807,6 @@
|
||||
"integrity": "sha512-RzAr8LSvM8lmhB4tQ5OPcBhpjOZRZjuxv9zO5UcxeoY2bd3kP3Ticd40Qma9/BqZ8JS96Ll/jeBX9u+LJZrhVg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.19.3",
|
||||
"postcss": "^8.4.31",
|
||||
@@ -8924,7 +8911,6 @@
|
||||
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.25.tgz",
|
||||
"integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.25",
|
||||
"@vue/compiler-sfc": "3.5.25",
|
||||
|
||||
33
openhis-ui-vue3/src/api/system/lisConfig.js
Normal file
33
openhis-ui-vue3/src/api/system/lisConfig.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function getLisConfigPage(query) {
|
||||
return request({
|
||||
url: '/inspection/lisConfig/init-page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function getLisConfigDetail(id) {
|
||||
return request({
|
||||
url: '/inspection/lisConfig/info-detail',
|
||||
method: 'get',
|
||||
params: { id }
|
||||
})
|
||||
}
|
||||
|
||||
export function getLisConfigList(searchKey, type) {
|
||||
return request({
|
||||
url: '/inspection/lisConfig/init-list',
|
||||
method: 'get',
|
||||
params: { searchKey, type }
|
||||
})
|
||||
}
|
||||
|
||||
export function saveLisConfig(data) {
|
||||
return request({
|
||||
url: '/inspection/lisConfig/saveAll',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
48
openhis-ui-vue3/src/api/system/observation.js
Normal file
48
openhis-ui-vue3/src/api/system/observation.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function getObservationInit() {
|
||||
return request({
|
||||
url: '/inspection/observation/init',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getObservationPage(query) {
|
||||
return request({
|
||||
url: '/inspection/observation/information-page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function getObservationOne(id) {
|
||||
return request({
|
||||
url: '/inspection/observation/information-one',
|
||||
method: 'get',
|
||||
params: { id }
|
||||
})
|
||||
}
|
||||
|
||||
export function addObservation(data) {
|
||||
return request({
|
||||
url: '/inspection/observation/information',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateObservation(data) {
|
||||
return request({
|
||||
url: '/inspection/observation/information',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteObservation(ids) {
|
||||
return request({
|
||||
url: '/inspection/observation/information-status',
|
||||
method: 'post',
|
||||
data: { ids, type: '停用' }
|
||||
})
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import {createWebHistory, createRouter} from 'vue-router'
|
||||
import { createWebHistory, createRouter } from 'vue-router'
|
||||
/* Layout */
|
||||
import Layout from '@/layout'
|
||||
|
||||
@@ -61,7 +61,7 @@ export const constantRoutes = [
|
||||
path: '/index',
|
||||
component: () => import('@/views/index'),
|
||||
name: 'Index',
|
||||
meta: {title: '首页', icon: 'dashboard', affix: true}
|
||||
meta: { title: '首页', icon: 'dashboard', affix: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -75,7 +75,21 @@ export const constantRoutes = [
|
||||
path: 'profile',
|
||||
component: () => import('@/views/system/user/profile/index'),
|
||||
name: 'Profile',
|
||||
meta: {title: '个人中心', icon: 'user'}
|
||||
meta: { title: '个人中心', icon: 'user' }
|
||||
}
|
||||
]
|
||||
},
|
||||
// 添加套餐管理相关路由到公共路由,确保始终可用
|
||||
{
|
||||
path: '/maintainSystem/Inspection/PackageManagement',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/maintainSystem/Inspection/PackageManagement.vue'),
|
||||
name: 'DirectPackageManagement',
|
||||
meta: { title: '套餐管理' }
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -83,6 +97,162 @@ export const constantRoutes = [
|
||||
|
||||
// 动态路由,基于用户权限动态去加载
|
||||
export const dynamicRoutes = [
|
||||
{
|
||||
path: '/basicmanage',
|
||||
component: Layout,
|
||||
redirect: '/basicmanage/invoice-management',
|
||||
name: 'BasicManage',
|
||||
meta: { title: '基础管理', icon: 'component' },
|
||||
children: [
|
||||
{
|
||||
path: 'invoice-management',
|
||||
component: () => import('@/views/basicmanage/InvoiceManagement/index.vue'),
|
||||
name: 'invoice-management',
|
||||
meta: { title: '发票管理' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/system/tenant-user',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['*:*:*'],
|
||||
children: [
|
||||
{
|
||||
path: 'set/:tenantId(\\d+)',
|
||||
component: () => import('@/views/system/tenant/setUser'),
|
||||
name: 'SetUser',
|
||||
meta: { title: '所属用户', activeMenu: '/system/tenant' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/system/tenant-contract',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['*:*:*'],
|
||||
children: [
|
||||
{
|
||||
path: 'set/:tenantId(\\d+)',
|
||||
component: () => import('@/views/system/tenant/setContract'),
|
||||
name: 'SetContract',
|
||||
meta: { title: '合同管理', activeMenu: '/system/tenant' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/system/user-auth',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['system:user:edit'],
|
||||
children: [
|
||||
{
|
||||
path: 'role/:userId(\\d+)',
|
||||
component: () => import('@/views/system/user/authRole'),
|
||||
name: 'AuthRole',
|
||||
meta: { title: '分配角色', activeMenu: '/system/user' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/system/role-auth',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['system:role:edit'],
|
||||
children: [
|
||||
{
|
||||
path: 'user/:roleId(\\d+)',
|
||||
component: () => import('@/views/system/role/authUser'),
|
||||
name: 'AuthUser',
|
||||
meta: { title: '分配用户', activeMenu: '/system/role' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/system/dict-data',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['system:dict:list'],
|
||||
children: [
|
||||
{
|
||||
path: 'index/:dictId(\\d+)',
|
||||
component: () => import('@/views/system/dict/data'),
|
||||
name: 'Data',
|
||||
meta: { title: '字典数据', activeMenu: '/system/dict' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/monitor',
|
||||
component: Layout,
|
||||
redirect: '/monitor/operlog',
|
||||
name: 'Monitor',
|
||||
meta: { title: '系统监控', icon: 'monitor' },
|
||||
children: [
|
||||
{
|
||||
path: 'operlog',
|
||||
component: () => import('@/views/monitor/operlog/index.vue'),
|
||||
name: 'Operlog',
|
||||
meta: { title: '操作日志', icon: 'operlog', permissions: ['monitor:operlog:list'] }
|
||||
},
|
||||
{
|
||||
path: 'logininfor',
|
||||
component: () => import('@/views/monitor/logininfor/index.vue'),
|
||||
name: 'Logininfor',
|
||||
meta: { title: '登录日志', icon: 'logininfor', permissions: ['monitor:logininfor:list'] }
|
||||
},
|
||||
{
|
||||
path: 'job',
|
||||
component: () => import('@/views/monitor/job/index.vue'),
|
||||
name: 'Job',
|
||||
meta: { title: '定时任务', icon: 'job', permissions: ['monitor:job:list'] }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/tool',
|
||||
component: Layout,
|
||||
redirect: '/tool/gen',
|
||||
name: 'Tool',
|
||||
meta: { title: '系统工具', icon: 'tool' },
|
||||
children: [
|
||||
{
|
||||
path: 'gen',
|
||||
component: () => import('@/views/tool/gen/index.vue'),
|
||||
name: 'Gen',
|
||||
meta: { title: '代码生成', icon: 'gen', permissions: ['tool:gen:list'] }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/monitor/job-log',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['monitor:job:list'],
|
||||
children: [
|
||||
{
|
||||
path: 'index/:jobId(\\d+)',
|
||||
component: () => import('@/views/monitor/job/log'),
|
||||
name: 'JobLog',
|
||||
meta: { title: '调度日志', activeMenu: '/monitor/job' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/tool/gen-edit',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['tool:gen:edit'],
|
||||
children: [
|
||||
{
|
||||
path: 'index/:tableId(\\d+)',
|
||||
component: () => import('@/views/tool/gen/editTable'),
|
||||
name: 'GenEdit',
|
||||
meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/help-center',
|
||||
component: Layout,
|
||||
@@ -92,10 +262,36 @@ export const dynamicRoutes = [
|
||||
path: '',
|
||||
component: () => import('@/views/helpcenter/index.vue'),
|
||||
name: 'HelpCenter',
|
||||
meta: {title: '帮助中心'},
|
||||
meta: { title: '帮助中心'},
|
||||
},
|
||||
],
|
||||
},
|
||||
// 字典类型路由(直接复制这段)
|
||||
{
|
||||
path: '/system/dict',
|
||||
component: Layout,
|
||||
alwaysShow: true,
|
||||
name: 'DictType',
|
||||
meta: {
|
||||
title: '字典类型管理',
|
||||
icon: 'list' // 图标随便选一个,比如list、dict,不影响跳转
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/system/dict/index.vue'),
|
||||
name: 'DictTypeList',
|
||||
meta: {title: '字典类型', noCache: false}
|
||||
},
|
||||
{
|
||||
path: 'data/:dictId?', // 带字典ID参数,?表示可选
|
||||
component: () => import('@/views/system/dict/data.vue'), // 你的data.vue路径
|
||||
name: 'DictData',
|
||||
hidden: true, // 不在侧边栏显示(子页面)
|
||||
meta: {title: '字典数据', activeMenu: '/system/dict'} // 保持侧边栏高亮
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
// 合并常量路由和动态路由,确保所有路由都能被访问
|
||||
@@ -115,7 +311,7 @@ const router = createRouter({
|
||||
if (savedPosition) {
|
||||
return savedPosition
|
||||
} else {
|
||||
return {top: 0}
|
||||
return { top: 0 }
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -347,7 +347,7 @@ const printForm = () => {
|
||||
};
|
||||
|
||||
function handleClick() {
|
||||
console.log('112313413');
|
||||
console.log('住院记录表单点击事件触发');
|
||||
}
|
||||
|
||||
const resetFun = (data) => {
|
||||
|
||||
@@ -81,15 +81,15 @@
|
||||
v-model="scope.row.activityDefinitionId"
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
placeholder="请选择"
|
||||
remote-show-suffix
|
||||
:remote-method="(query) => handleRemoteQuery(query, scope.row)"
|
||||
:loading="scope.row.loading"
|
||||
placeholder="请输入并搜索项目"
|
||||
style="width: 400px; max-width: 500px"
|
||||
:class="{ 'error-border': scope.row.error }"
|
||||
clearable
|
||||
>
|
||||
<el-option
|
||||
v-for="item in allImplementDepartmentList"
|
||||
v-for="item in scope.row.filteredOptions || []"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
@@ -236,15 +236,34 @@ const filterNode = (value, data) => {
|
||||
};
|
||||
// 所有诊疗项目列表
|
||||
const allImplementDepartmentList = ref([]);
|
||||
function getAllImplementDepartment() {
|
||||
async function getAllImplementDepartment() {
|
||||
loading.value = true;
|
||||
getAllTreatmentList().then((res) => {
|
||||
try {
|
||||
const res = await getAllTreatmentList();
|
||||
allImplementDepartmentList.value = res.data.map((item) => ({
|
||||
value: item.activityDefinitionId,
|
||||
label: item.activityDefinitionName,
|
||||
}));
|
||||
loading.value = false;
|
||||
|
||||
// 为所有现有行初始化过滤选项(使用防抖处理,避免频繁更新)
|
||||
if (catagoryList.value && catagoryList.value.length > 0) {
|
||||
// 使用 setTimeout 将 DOM 更新推迟到下一个事件循环,避免阻塞
|
||||
setTimeout(() => {
|
||||
catagoryList.value.forEach(row => {
|
||||
if (!row.hasOwnProperty('filteredOptions')) {
|
||||
row.filteredOptions = allImplementDepartmentList.value.slice(0, 100); // 限制为前100个
|
||||
row.loading = false;
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
|
||||
loading.value = false;
|
||||
} catch (error) {
|
||||
console.error('获取诊疗项目列表失败:', error);
|
||||
loading.value = false;
|
||||
proxy.$message.error('获取诊疗项目列表失败');
|
||||
}
|
||||
}
|
||||
|
||||
/** 选择条数 */
|
||||
@@ -253,18 +272,63 @@ function handleSelectionChange(selection) {
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
}
|
||||
// 远程搜索处理函数
|
||||
function handleRemoteQuery(query, row) {
|
||||
if (query !== '') {
|
||||
// 设置加载状态
|
||||
row.loading = true;
|
||||
// 模拟异步延迟
|
||||
setTimeout(() => {
|
||||
// 确保数据已加载
|
||||
if (!allImplementDepartmentList.value || allImplementDepartmentList.value.length === 0) {
|
||||
row.filteredOptions = [];
|
||||
row.loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 过滤选项,限制结果数量以提高性能
|
||||
const filtered = allImplementDepartmentList.value.filter(item => {
|
||||
return item.label.toLowerCase().includes(query.toLowerCase()) ||
|
||||
item.value.toLowerCase().includes(query.toLowerCase());
|
||||
});
|
||||
|
||||
// 限制返回结果数量,避免过多选项导致性能问题
|
||||
row.filteredOptions = filtered.slice(0, 100); // 限制为前100个匹配项
|
||||
row.loading = false;
|
||||
}, 300); // 300ms 延迟,模拟网络请求
|
||||
} else {
|
||||
// 如果查询为空,显示所有选项(但限制数量以提高性能)
|
||||
if (allImplementDepartmentList.value && allImplementDepartmentList.value.length > 0) {
|
||||
row.filteredOptions = allImplementDepartmentList.value.slice(0, 100); // 限制为前100个
|
||||
} else {
|
||||
row.filteredOptions = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 新增项目
|
||||
function handleAddItem() {
|
||||
if (data.isAdding) {
|
||||
proxy.$message.warning('请先保存当前行后再新增!');
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保 allImplementDepartmentList 已经初始化
|
||||
if (!allImplementDepartmentList.value || allImplementDepartmentList.value.length === 0) {
|
||||
proxy.$message.warning('正在加载数据,请稍后再试!');
|
||||
// 如果数据还未加载完成,尝试重新加载
|
||||
getAllImplementDepartment();
|
||||
return;
|
||||
}
|
||||
|
||||
const newRow = {
|
||||
startTime: '00:00:00',
|
||||
endTime: '23:59:59',
|
||||
loading: false, // 添加加载状态
|
||||
filteredOptions: allImplementDepartmentList.value.slice(0, 100), // 初始化过滤选项,限制数量
|
||||
};
|
||||
catagoryList.value.push(newRow);
|
||||
total.value = organization.value.length;
|
||||
total.value = catagoryList.value.length; // 修正:使用实际数据列表长度,而不是组织结构长度
|
||||
data.isAdding = true; // 设置标志位为 true,表示有未保存的
|
||||
}
|
||||
// 批量添加
|
||||
@@ -272,6 +336,7 @@ function handleBacthAddItem() {
|
||||
// 批量添加显示对话框
|
||||
bacthAddItemDialogVisible.value = true;
|
||||
}
|
||||
|
||||
// 检验 编辑或 保存数据
|
||||
function handleBlur(row, index) {
|
||||
let hasError = false;
|
||||
@@ -344,15 +409,25 @@ function deleteSelectedRows(row) {
|
||||
}
|
||||
/** 节点单击事件 */
|
||||
function handleNodeClick(res, node) {
|
||||
// 新增按钮是否 disable
|
||||
// 检查是否有未保存的数据
|
||||
if (data.isAdding) {
|
||||
proxy.$modal.confirm('当前有未保存的数据,切换节点将丢失未保存的数据,是否继续?').then(() => {
|
||||
// 确认切换,重置状态
|
||||
data.isAdding = false;
|
||||
|
||||
// 新增按钮是否 disable
|
||||
if (node.parent === null || node.level === 1) {
|
||||
isAddDisable.value = true;
|
||||
continueHandleNodeClick(node);
|
||||
}).catch(() => {
|
||||
// 取消切换,保持当前状态
|
||||
return;
|
||||
});
|
||||
} else {
|
||||
isAddDisable.value = false;
|
||||
continueHandleNodeClick(node);
|
||||
}
|
||||
}
|
||||
|
||||
// 实际的节点点击处理逻辑
|
||||
function continueHandleNodeClick(node) {
|
||||
// 新增按钮是否 disable
|
||||
isAddDisable.value = false;
|
||||
// 检查节点是否有子节点
|
||||
if (node.data.children && node.data.children.length > 0) {
|
||||
// proxy.$message.warning("不能选择父节点");
|
||||
@@ -365,26 +440,28 @@ function handleNodeClick(res, node) {
|
||||
}
|
||||
|
||||
/** 目录分类查询 */
|
||||
function getDiseaseTreatmentList() {
|
||||
async function getDiseaseTreatmentList() {
|
||||
loading.value = true;
|
||||
getDiseaseTreatmentInit().then(({ data }) => {
|
||||
loading.value = false;
|
||||
try {
|
||||
const { data } = await getDiseaseTreatmentInit();
|
||||
|
||||
//分类目录初始化获取
|
||||
// 分类目录初始化获取
|
||||
catagoryDicts.value = data.diagnosisCategoryOptions.sort((a, b) => {
|
||||
return parseInt(a.value) - parseInt(b.value);
|
||||
});
|
||||
});
|
||||
// 诊疗目录分类查询下拉树结d构
|
||||
loading.value = true;
|
||||
// 诊疗目录分类查询下拉树结d构
|
||||
getImplDepartList();
|
||||
} catch (error) {
|
||||
console.error('获取疾病治疗初始化数据失败:', error);
|
||||
proxy.$message.error('获取分类目录失败');
|
||||
}
|
||||
|
||||
// 诊疗目录分类查询下拉树结构
|
||||
await getImplDepartList();
|
||||
loading.value = false;
|
||||
}
|
||||
// 诊疗目录分类查询下拉树结d构
|
||||
function getImplDepartList() {
|
||||
loading.value = true;
|
||||
getImplementDepartmentList().then((res) => {
|
||||
loading.value = false;
|
||||
async function getImplDepartList() {
|
||||
try {
|
||||
const res = await getImplementDepartmentList();
|
||||
if (res.code === 200) {
|
||||
if (res.data.records.length > 0) {
|
||||
organization.value = res.data.records.map((res) => {
|
||||
@@ -398,13 +475,24 @@ function getImplDepartList() {
|
||||
organization.value = [];
|
||||
}
|
||||
} else {
|
||||
this.$modal.msgError(res.code);
|
||||
proxy.$modal.msgError(res.code);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取实施部门列表失败:', error);
|
||||
proxy.$message.error('获取科室信息失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
onMounted(() => {
|
||||
getAllImplementDepartment();
|
||||
getDiseaseTreatmentList();
|
||||
onMounted(async () => {
|
||||
try {
|
||||
// 并行加载数据,提高效率
|
||||
await Promise.all([
|
||||
getAllImplementDepartment(),
|
||||
getDiseaseTreatmentList()
|
||||
]);
|
||||
} catch (error) {
|
||||
console.error('初始化数据加载失败:', error);
|
||||
proxy.$message.error('数据加载失败,请稍后重试');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
@@ -582,7 +582,7 @@ function submitForm() {
|
||||
console.log('params11========>', JSON.stringify(params));
|
||||
orgRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
console.log('99999999');
|
||||
console.log('表单验证通过,准备提交数据');
|
||||
|
||||
if (form.busNoParent) {
|
||||
if (form.formEnum == 4) {
|
||||
|
||||
@@ -424,7 +424,7 @@ function clickRow(row) {
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
preloadData(); // 预加载数据
|
||||
// preloadData(); // 预加载数据
|
||||
getList(); // 获取初始数据
|
||||
});
|
||||
|
||||
|
||||
@@ -7,183 +7,78 @@
|
||||
<el-option label="科室" :value="2"></el-option>
|
||||
<el-option label="全院" :value="3"></el-option>
|
||||
</el-select>
|
||||
<el-input
|
||||
v-model="searchKeyword"
|
||||
placeholder="请输入名称"
|
||||
style="width: 240px; margin-left: 10px;"
|
||||
@keyup.enter="handleSearch"
|
||||
></el-input>
|
||||
<el-button style="margin-left: 10px;" @click="handleSearch"><el-icon><Search /></el-icon> 查询</el-button>
|
||||
<el-button style="margin-left: 10px;" @click="handleReset"><el-icon><Refresh /></el-icon> 重置</el-button>
|
||||
<el-button type="primary" style="margin-left: 10px;" @click="showAddDialog"><el-icon><Plus /></el-icon> 增加</el-button>
|
||||
<el-input v-model="searchKeyword" placeholder="请输入名称" style="width: 240px; margin-left: 10px;"
|
||||
@keyup.enter="handleSearch"></el-input>
|
||||
<el-button style="margin-left: 10px;" @click="handleSearch"><el-icon>
|
||||
<Search />
|
||||
</el-icon> 查询</el-button>
|
||||
<el-button style="margin-left: 10px;" @click="handleReset"><el-icon>
|
||||
<Refresh />
|
||||
</el-icon> 重置</el-button>
|
||||
<el-button type="primary" style="margin-left: 10px;" @click="showAddDialog"><el-icon>
|
||||
<Plus />
|
||||
</el-icon> 增加</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 表格 -->
|
||||
<el-table :data="tableData" style="width: 100%; margin-top: 20px;">
|
||||
<el-table-column prop="sortNo" label="排序号" width="200">
|
||||
<template #default="scope">
|
||||
<el-input-number
|
||||
v-if="editingId === scope.row.id"
|
||||
v-model="editForm.sortNo"
|
||||
:min="1"
|
||||
:step="1"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<span v-else>{{ scope.row.sortNo }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="phraseName" label="名称" width="250">
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-if="editingId === scope.row.id"
|
||||
v-model="editForm.phraseName"
|
||||
placeholder="请输入名称(不超过50字)"
|
||||
maxlength="50"
|
||||
show-word-limit
|
||||
style="width: 100%"
|
||||
/>
|
||||
<span v-else>{{ scope.row.phraseName }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="phraseContent" label="内容">
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-if="editingId === scope.row.id"
|
||||
v-model="editForm.phraseContent"
|
||||
placeholder="请输入内容"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<span v-else>{{ scope.row.phraseContent }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="范围" width="250">
|
||||
<template #default="scope">
|
||||
<el-select
|
||||
v-if="editingId === scope.row.id"
|
||||
v-model="editForm.phraseType"
|
||||
placeholder="请选择范围"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option label="个人" :value="1"></el-option>
|
||||
<el-option label="科室" :value="2"></el-option>
|
||||
<el-option label="全院" :value="3"></el-option>
|
||||
</el-select>
|
||||
<span v-else>{{ getScopeName(scope.row.phraseType) }}</span>
|
||||
{{ getScopeName(scope.row.phraseType) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 业务分类列:使用枚举转换中文名称 -->
|
||||
<el-table-column label="业务分类" width="200">
|
||||
<template #default="scope">
|
||||
<el-select
|
||||
v-if="editingId === scope.row.id"
|
||||
v-model="editForm.phraseCategory"
|
||||
placeholder="请选择业务分类"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option label="病愈" value="12"></el-option>
|
||||
<el-option
|
||||
v-for="item in businessclassification"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
<span v-else>{{ getBusinessTypeName(scope.row.phraseCategory) }}</span>
|
||||
{{ getBusinessTypeName(scope.row.phraseCategory) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200" fixed="right">
|
||||
<template #default="scope">
|
||||
<!-- 编辑状态 -->
|
||||
<template v-if="editingId === scope.row.id">
|
||||
<el-button
|
||||
type="success"
|
||||
size="small"
|
||||
@click="handleSave"
|
||||
>
|
||||
<el-icon><Check /></el-icon> 保存
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
size="small"
|
||||
style="margin-left: 5px;"
|
||||
@click="handleCancel"
|
||||
>
|
||||
<el-icon><Close /></el-icon> 取消
|
||||
</el-button>
|
||||
</template>
|
||||
<!-- 非编辑状态 -->
|
||||
<template v-else>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleEdit(scope.row)"
|
||||
>
|
||||
<el-button type="primary" size="small" @click="showEditDialog(scope.row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
style="margin-left: 5px;"
|
||||
@click="handleDelete(scope.row)"
|
||||
>
|
||||
<el-button type="danger" size="small" style="margin-left: 5px;" @click="handleDelete(scope.row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="currentPage"
|
||||
:page-sizes="[15, 30, 50]"
|
||||
:page-size="pageSize"
|
||||
layout="total, prev, pager, next, jumper, sizes"
|
||||
:total="total"
|
||||
></el-pagination>
|
||||
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="currentPage"
|
||||
:page-sizes="[15, 30, 50]" :page-size="pageSize" layout="total, prev, pager, next, jumper, sizes"
|
||||
:total="total"></el-pagination>
|
||||
</div>
|
||||
|
||||
<!-- 新增模态框 -->
|
||||
<el-dialog
|
||||
v-model="addDialogVisible"
|
||||
title="新增医生常用语"
|
||||
width="600px"
|
||||
center
|
||||
>
|
||||
<el-form ref="addFormRef" :model="addForm" :rules="rules" label-width="100px" class="add-form">
|
||||
<el-form-item label="名称" prop="phraseName" required>
|
||||
<el-input v-model="addForm.phraseName" placeholder="请输入常用语名称(不超过50字)" maxlength="50" show-word-limit style="width: 100%;"></el-input>
|
||||
<el-dialog v-model="addDialogVisible" title="新增医生常用语" width="600px" center>
|
||||
<el-form ref="addFormRef" :model="addForm" :rules="addRules" label-width="100px" class="add-form">
|
||||
<el-form-item label="名称" prop="phraseName">
|
||||
<el-input v-model="addForm.phraseName" placeholder="请输入常用语名称(不超过50字)" maxlength="50" show-word-limit
|
||||
style="width: 100%;"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="内容" prop="phraseContent" required>
|
||||
<el-input
|
||||
v-model="addForm.phraseContent"
|
||||
placeholder="请输入常用语内容"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
style="width: 100%;"
|
||||
></el-input>
|
||||
<el-form-item label="内容" prop="phraseContent">
|
||||
<el-input v-model="addForm.phraseContent" placeholder="请输入常用语内容" type="textarea" :rows="4"
|
||||
style="width: 100%;"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序号">
|
||||
<el-input-number v-model="addForm.sortNo" :min="1" :step="1" style="width: 100%;"></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item label="业务分类">
|
||||
<el-form-item label="业务分类" prop="phraseCategory">
|
||||
<el-select v-model="addForm.phraseCategory" placeholder="请选择业务分类" style="width: 100%;">
|
||||
<!-- 默认添加"病愈"选项 -->
|
||||
<el-option label="病愈" value="12"></el-option>
|
||||
<!-- 从数据字典获取其他选项 -->
|
||||
<el-option
|
||||
v-for="item in businessclassification"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
></el-option>
|
||||
<el-option v-for="item in businessTypeOptions" :key="item.value" :label="item.label"
|
||||
:value="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="范围">
|
||||
<el-form-item label="范围" prop="phraseType">
|
||||
<el-select v-model="addForm.phraseType" placeholder="请选择范围" style="width: 100%;">
|
||||
<el-option label="个人" :value="1"></el-option>
|
||||
<el-option label="科室" :value="2"></el-option>
|
||||
@@ -198,12 +93,48 @@
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 编辑模态框 -->
|
||||
<el-dialog v-model="editDialogVisible" title="编辑医生常用语" width="600px" center>
|
||||
<el-form ref="editFormRef" :model="editForm" :rules="editRules" label-width="100px" class="add-form">
|
||||
<el-form-item label="名称" prop="phraseName">
|
||||
<el-input v-model="editForm.phraseName" placeholder="请输入常用语名称(不超过50字)" maxlength="50" show-word-limit
|
||||
style="width: 100%;"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="内容" prop="phraseContent">
|
||||
<el-input v-model="editForm.phraseContent" placeholder="请输入常用语内容" type="textarea" :rows="4"
|
||||
style="width: 100%;"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序号">
|
||||
<el-input-number v-model="editForm.sortNo" :min="1" :step="1" style="width: 100%;"></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item label="业务分类" prop="phraseCategory">
|
||||
<el-select v-model="editForm.phraseCategory" placeholder="请选择业务分类" style="width: 100%;">
|
||||
<el-option v-for="item in businessTypeOptions" :key="item.value" :label="item.label"
|
||||
:value="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="范围" prop="phraseType">
|
||||
<el-select v-model="editForm.phraseType" placeholder="请选择范围" style="width: 100%;">
|
||||
<el-option label="个人" :value="1"></el-option>
|
||||
<el-option label="科室" :value="2"></el-option>
|
||||
<el-option label="全院" :value="3"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="editDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleEditSave">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onMounted, ref} from 'vue'
|
||||
import {Check, Close, Plus, Refresh, Search} from '@element-plus/icons-vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { Check, Close, Plus, Refresh, Search } from '@element-plus/icons-vue'
|
||||
import {
|
||||
addDoctorPhrase,
|
||||
deleteDoctorPhrase,
|
||||
@@ -211,71 +142,98 @@ import {
|
||||
searchDoctorPhraseList,
|
||||
updateDoctorPhrase
|
||||
} from './api'
|
||||
import {ElMessage, ElMessageBox} from 'element-plus'
|
||||
import {useDict} from '@/utils/dict'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
// 如果数据字典未对齐后端枚举,暂时注释useDict,改用固定枚举(后续可替换为dict)
|
||||
// import { useDict } from '@/utils/dict'
|
||||
|
||||
// 核心配置:定义和后端枚举对齐的业务分类选项(替换原businessclassification)
|
||||
const businessTypeOptions = ref([
|
||||
{ label: '主诉', value: 'MAIN_COMPLAINT' },
|
||||
{ label: '现病史', value: 'PRESENT_HISTORY' },
|
||||
{ label: '术前', value: 'PRE_OPERATION' },
|
||||
{ label: '术后', value: 'POST_OPERATION' },
|
||||
{ label: '既往史', value: 'PAST_HISTORY' }
|
||||
])
|
||||
|
||||
// 搜索条件
|
||||
const searchScope = ref(null) // null表示未选择,数字类型:1=个人,2=科室,3=全院
|
||||
const searchKeyword = ref('')
|
||||
const searchScope = ref(null) // null表示未选择,数字类型:1=个人,2=科室,3=全院
|
||||
const searchKeyword = ref('')
|
||||
|
||||
// 表格数据
|
||||
const tableData = ref([])
|
||||
|
||||
// 分页
|
||||
// 分页参数配置
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(15)
|
||||
const total = ref(0)
|
||||
|
||||
// 新增模态框相关
|
||||
// 新增模态框相关配置
|
||||
const addDialogVisible = ref(false)
|
||||
const addFormRef = ref()
|
||||
// 新增表单默认值:业务分类默认空,强制用户选择
|
||||
const addForm = ref({
|
||||
phraseName: '',
|
||||
phraseContent: '',
|
||||
sortNo: 1,
|
||||
phraseType: 1,
|
||||
phraseCategory: '病愈'
|
||||
phraseCategory: ''
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const rules = {
|
||||
// 完善新增表单验证规则(补充业务分类必填、名称50字限制)
|
||||
const addRules = {
|
||||
phraseName: [
|
||||
{ required: true, message: '请输入常用语名称', trigger: 'blur' }
|
||||
{ required: true, message: '请输入常用语名称', trigger: 'blur' },
|
||||
{ max: 50, message: '常用语名称不能超过50字', trigger: 'blur' }
|
||||
],
|
||||
phraseContent: [
|
||||
{ required: true, message: '请输入常用语内容', trigger: 'blur' }
|
||||
],
|
||||
phraseType: [
|
||||
{ required: true, message: '请选择范围', trigger: 'change' }
|
||||
],
|
||||
phraseCategory: [
|
||||
{ required: true, message: '请选择业务分类', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
// 编辑状态管理
|
||||
const editingId = ref(null)
|
||||
const editForm = ref({})
|
||||
// 编辑模态框相关配置
|
||||
const editDialogVisible = ref(false)
|
||||
const editFormRef = ref()
|
||||
// 编辑表单默认值配置
|
||||
const editForm = ref({
|
||||
id: '',
|
||||
phraseName: '',
|
||||
phraseContent: '',
|
||||
sortNo: 1,
|
||||
phraseType: 1,
|
||||
phraseCategory: ''
|
||||
})
|
||||
|
||||
// 完善编辑表单验证规则(和新增一致,保证校验统一)
|
||||
const editRules = {
|
||||
phraseName: [
|
||||
{ required: true, message: '请输入常用语名称', trigger: 'blur' }
|
||||
{ required: true, message: '请输入常用语名称', trigger: 'blur' },
|
||||
{ max: 50, message: '常用语名称不能超过50字', trigger: 'blur' }
|
||||
],
|
||||
phraseContent: [
|
||||
{ required: true, message: '请输入常用语内容', trigger: 'blur' }
|
||||
],
|
||||
phraseType: [
|
||||
{ required: true, message: '请选择范围', trigger: 'change' }
|
||||
],
|
||||
phraseCategory: [
|
||||
{ required: true, message: '请选择业务分类', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
// 获取业务分类数据字典
|
||||
const { businessclassification } = useDict('businessclassification')
|
||||
|
||||
// 获取业务分类名称
|
||||
const getBusinessTypeName = (category) => {
|
||||
// 直接返回后端返回的phrase_category字段值
|
||||
// 从数据库截图看,phrase_category已经是中文名称:"主诉"、"现病史"、"术后"等
|
||||
if (category) {
|
||||
return category
|
||||
}
|
||||
return '未知类型'
|
||||
// 重构业务分类名称转换方法(适配后端编码→中文展示)
|
||||
const getBusinessTypeName = (code) => {
|
||||
if (!code) return '未知类型'
|
||||
const item = businessTypeOptions.value.find(item => item.value === code)
|
||||
return item ? item.label : '未知类型'
|
||||
}
|
||||
|
||||
// 获取范围名称
|
||||
// 获取范围名称转换方法
|
||||
const getScopeName = (scope) => {
|
||||
const scopeMap = {
|
||||
1: '个人',
|
||||
@@ -285,46 +243,41 @@ const getScopeName = (scope) => {
|
||||
return scopeMap[scope] || '未知范围'
|
||||
}
|
||||
|
||||
// 名称唯一性校验函数
|
||||
// 名称唯一性校验函数(新增/编辑通用,编辑时排除自身ID)
|
||||
const validatePhraseName = (phraseName, excludeId = null) => {
|
||||
if (!phraseName || !phraseName.trim()) {
|
||||
return { valid: false, message: '请输入常用语名称' }
|
||||
}
|
||||
|
||||
// 检查字数限制
|
||||
// 检查字数限制,严格控制50字
|
||||
if (phraseName.trim().length > 50) {
|
||||
return { valid: false, message: '常用语名称不能超过50字' }
|
||||
}
|
||||
|
||||
// 检查名称是否已存在
|
||||
// 检查名称是否已存在,编辑时排除当前记录
|
||||
const existingPhrase = allData.value.find(item => {
|
||||
// 排除自身(更新时)
|
||||
if (excludeId && item.id === excludeId) {
|
||||
return false
|
||||
}
|
||||
return item.phraseName === phraseName.trim()
|
||||
return item.phraseName.trim() === phraseName.trim()
|
||||
})
|
||||
|
||||
if (existingPhrase) {
|
||||
return { valid: false, message: '常用语名称已存在,请输入不同的名称' }
|
||||
}
|
||||
|
||||
return { valid: true, message: '' }
|
||||
}
|
||||
|
||||
// 所有数据(用于客户端分页)
|
||||
// 所有数据(用于客户端分页处理)
|
||||
const allData = ref([])
|
||||
|
||||
// 获取医生常用语列表
|
||||
// 获取医生常用语列表数据
|
||||
const fetchDoctorPhraseList = async () => {
|
||||
try {
|
||||
const response = await getDoctorPhraseList()
|
||||
// 处理后端返回的数据结构:data.data
|
||||
if (response.code === 200 && response.data && response.data.data) {
|
||||
// 按照sortNo由小到大排序
|
||||
// 按照sortNo由小到大排序,保证列表顺序正确
|
||||
allData.value = response.data.data.sort((a, b) => a.sortNo - b.sortNo)
|
||||
total.value = response.data.data.length
|
||||
// 执行客户端分页
|
||||
total.value = allData.value.length
|
||||
// 执行客户端分页逻辑
|
||||
applyPagination()
|
||||
} else {
|
||||
ElMessage.error('获取数据失败: ' + (response.msg || '未知错误'))
|
||||
@@ -332,56 +285,54 @@ const fetchDoctorPhraseList = async () => {
|
||||
total.value = 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取列表失败:', error) // 增加控制台日志便于调试
|
||||
ElMessage.error('获取数据失败: 网络请求错误')
|
||||
allData.value = []
|
||||
total.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
// 重置功能
|
||||
// 重置功能方法
|
||||
const handleReset = () => {
|
||||
// 重置搜索条件
|
||||
searchScope.value = null
|
||||
searchKeyword.value = ''
|
||||
|
||||
// 重置分页到第一页
|
||||
currentPage.value = 1
|
||||
// 重新加载所有数据
|
||||
fetchDoctorPhraseList()
|
||||
}
|
||||
|
||||
// 客户端分页处理
|
||||
// 客户端分页处理核心方法
|
||||
const applyPagination = () => {
|
||||
const start = (currentPage.value - 1) * pageSize.value
|
||||
const end = start + pageSize.value
|
||||
tableData.value = allData.value.slice(start, end)
|
||||
}
|
||||
|
||||
// 分页处理
|
||||
// 分页条数改变事件
|
||||
const handleSizeChange = (val) => {
|
||||
pageSize.value = val
|
||||
applyPagination()
|
||||
}
|
||||
|
||||
// 分页页码改变事件
|
||||
const handleCurrentChange = (val) => {
|
||||
currentPage.value = val
|
||||
applyPagination()
|
||||
}
|
||||
|
||||
// 搜索功能
|
||||
// 搜索功能核心方法
|
||||
const handleSearch = async () => {
|
||||
try {
|
||||
// searchScope可能是null(未选择)、1=个人,2=科室,3=全院
|
||||
const searchScopeValue = searchScope.value
|
||||
const phraseType = searchScopeValue === null ? undefined : searchScopeValue
|
||||
|
||||
const phraseType = searchScope.value === null ? undefined : searchScope.value
|
||||
// 调用搜索接口:phraseName, phraseType
|
||||
// 如果phraseType为undefined,则后端不会按范围过滤,查询所有数据
|
||||
const response = await searchDoctorPhraseList(searchKeyword.value, phraseType)
|
||||
|
||||
// 处理后端返回的数据结构:data.data
|
||||
if (response.code === 200 && response.data && response.data.data) {
|
||||
// 按照sortNo由小到大排序
|
||||
allData.value = response.data.data.sort((a, b) => a.sortNo - b.sortNo)
|
||||
total.value = response.data.data.length
|
||||
total.value = allData.value.length
|
||||
currentPage.value = 1 // 搜索后重置到第一页
|
||||
applyPagination() // 应用分页
|
||||
} else {
|
||||
@@ -390,31 +341,37 @@ const handleSearch = async () => {
|
||||
total.value = 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('搜索失败:', error)
|
||||
ElMessage.error('搜索失败: 网络请求错误')
|
||||
allData.value = []
|
||||
total.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
// 打开新增模态框
|
||||
// 打开新增模态框方法
|
||||
const showAddDialog = () => {
|
||||
// 重置表单
|
||||
// 重置表单数据
|
||||
addForm.value = {
|
||||
phraseName: '',
|
||||
phraseContent: '',
|
||||
sortNo: 1,
|
||||
phraseType: 1,
|
||||
phraseCategory: '病愈'
|
||||
phraseCategory: ''
|
||||
}
|
||||
// 重置表单验证状态
|
||||
if (addFormRef.value) {
|
||||
addFormRef.value.clearValidate()
|
||||
}
|
||||
// 打开模态框
|
||||
addDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 提交新增表单
|
||||
// 提交新增表单方法
|
||||
const handleAdd = async () => {
|
||||
try {
|
||||
// 验证表单
|
||||
await addFormRef.value.validate()
|
||||
// 先执行表单验证
|
||||
const validateResult = await addFormRef.value.validate()
|
||||
if (!validateResult) return
|
||||
|
||||
// 名称唯一性校验
|
||||
const nameValidation = validatePhraseName(addForm.value.phraseName)
|
||||
@@ -426,143 +383,115 @@ const handleAdd = async () => {
|
||||
// 添加数据库要求的必填默认字段
|
||||
const formData = {
|
||||
...addForm.value,
|
||||
phraseCode: 'PHR000',
|
||||
phraseCode: 'PHR000', // 建议后端自动生成,前端可传空
|
||||
enableFlag: 1,
|
||||
staffId: 1001,
|
||||
deptCode: 'DEPT001',
|
||||
creatorId: 1001
|
||||
staffId: 1001, // 建议从登录态获取,不要硬编码
|
||||
deptCode: 'DEPT001', // 建议从登录态获取
|
||||
creatorId: 1001 // 建议从登录态获取
|
||||
}
|
||||
|
||||
// 调用新增接口
|
||||
const response = await addDoctorPhrase(formData)
|
||||
|
||||
// 处理新增结果
|
||||
if (response.code === 200) {
|
||||
ElMessage.success('新增成功')
|
||||
// 关闭模态框
|
||||
addDialogVisible.value = false
|
||||
// 将新数据添加到数组开头,使新增的数据在表格最上方显示
|
||||
const newData = {
|
||||
...formData,
|
||||
id: response.data.id || Date.now() // 使用后端返回的id或当前时间戳作为临时id
|
||||
}
|
||||
allData.value.unshift(newData)
|
||||
total.value = allData.value.length
|
||||
// 重新应用分页
|
||||
applyPagination()
|
||||
// 重新拉取数据(比手动unshift更可靠,避免数据不一致)
|
||||
fetchDoctorPhraseList()
|
||||
} else {
|
||||
ElMessage.error('新增失败: ' + (response.msg || '未知错误'))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('新增失败:', error)
|
||||
// 表单验证失败或其他错误
|
||||
if (error instanceof Error) {
|
||||
ElMessage.error('请填写完整信息或网络请求错误')
|
||||
} else {
|
||||
// 表单验证失败的情况
|
||||
ElMessage.error('请填写完整信息')
|
||||
}
|
||||
ElMessage.error('新增失败: 请填写完整信息或网络异常')
|
||||
}
|
||||
}
|
||||
|
||||
// 删除功能
|
||||
// 删除功能方法
|
||||
const handleDelete = async (row) => {
|
||||
try {
|
||||
// 弹出确认对话框
|
||||
await ElMessageBox.confirm('确定要删除该常用语吗?', '删除确认', {
|
||||
await ElMessageBox.confirm(
|
||||
'确定要删除该常用语吗?删除后无法恢复!',
|
||||
'删除确认',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
|
||||
}
|
||||
)
|
||||
// 调用删除接口
|
||||
const response = await deleteDoctorPhrase(row.id)
|
||||
|
||||
// 处理删除结果
|
||||
if (response.code === 200) {
|
||||
ElMessage.success('删除成功')
|
||||
// 重新获取数据以更新界面
|
||||
// 重新拉取数据
|
||||
fetchDoctorPhraseList()
|
||||
} else {
|
||||
ElMessage.error('删除失败: ' + (response.msg || '未知错误'))
|
||||
}
|
||||
} catch (error) {
|
||||
// 如果用户取消删除,不显示错误信息
|
||||
if (error !== 'cancel' && error !== undefined) {
|
||||
if (error.msg) {
|
||||
ElMessage.error('删除操作失败: ' + error.msg)
|
||||
} else if (error instanceof Error) {
|
||||
ElMessage.error('删除操作失败: 网络请求错误')
|
||||
} else {
|
||||
ElMessage.error('删除操作失败: 未知错误')
|
||||
}
|
||||
// 用户取消删除时不提示错误
|
||||
if (error !== 'cancel') {
|
||||
console.error('删除失败:', error)
|
||||
ElMessage.error('删除操作失败: 网络异常或权限不足')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑按钮点击事件
|
||||
const handleEdit = (row) => {
|
||||
// 进入编辑状态
|
||||
editingId.value = row.id
|
||||
// 复制当前行数据到编辑表单
|
||||
editForm.value = {
|
||||
...row,
|
||||
// 确保phraseCategory和phraseType类型正确
|
||||
phraseCategory: row.phraseCategory,
|
||||
phraseType: row.phraseType
|
||||
// 打开编辑弹窗核心方法 - 点击表格编辑按钮触发
|
||||
const showEditDialog = (row) => {
|
||||
// 深拷贝当前行数据到编辑表单,避免原数据被修改
|
||||
editForm.value = JSON.parse(JSON.stringify(row))
|
||||
// 确保数字类型正确,防止表单赋值异常
|
||||
editForm.value.sortNo = Number(editForm.value.sortNo) || 1
|
||||
editForm.value.phraseType = Number(editForm.value.phraseType) || 1
|
||||
// 重置表单验证状态
|
||||
if (editFormRef.value) {
|
||||
editFormRef.value.clearValidate()
|
||||
}
|
||||
// 打开编辑弹窗
|
||||
editDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 取消编辑
|
||||
const handleCancel = () => {
|
||||
// 退出编辑状态
|
||||
editingId.value = null
|
||||
// 清空编辑表单
|
||||
editForm.value = {}
|
||||
}
|
||||
|
||||
// 保存编辑
|
||||
const handleSave = async () => {
|
||||
// 编辑表单提交保存方法
|
||||
const handleEditSave = async () => {
|
||||
try {
|
||||
// 验证表单数据
|
||||
if (!editForm.value.phraseName || !editForm.value.phraseContent) {
|
||||
ElMessage.error('请填写完整信息')
|
||||
return
|
||||
}
|
||||
// 先执行表单验证
|
||||
const validateResult = await editFormRef.value.validate()
|
||||
if (!validateResult) return
|
||||
|
||||
// 名称唯一性校验(排除当前记录)
|
||||
const nameValidation = validatePhraseName(editForm.value.phraseName, editingId.value)
|
||||
// 名称唯一性校验(排除当前编辑的这条记录ID)
|
||||
const nameValidation = validatePhraseName(editForm.value.phraseName, editForm.value.id)
|
||||
if (!nameValidation.valid) {
|
||||
ElMessage.error(nameValidation.message)
|
||||
return
|
||||
}
|
||||
|
||||
// 准备更新数据
|
||||
// 准备更新数据,修复时间格式为ISO字符串,适配后端LocalDateTime
|
||||
const updateData = {
|
||||
...editForm.value,
|
||||
enableFlag: 1 // 确保启用状态
|
||||
enableFlag: 1,
|
||||
updateTime: new Date().toISOString() // 前端临时赋值,后端最终以自己的为准
|
||||
}
|
||||
|
||||
// 调用更新接口
|
||||
const response = await updateDoctorPhrase(updateData)
|
||||
|
||||
// 处理更新结果
|
||||
if (response.code === 200) {
|
||||
ElMessage.success('更新成功')
|
||||
// 退出编辑状态
|
||||
editingId.value = null
|
||||
// 清空编辑表单
|
||||
editForm.value = {}
|
||||
// 重新获取数据以更新界面
|
||||
editDialogVisible.value = false
|
||||
// 重新拉取数据,保证列表数据最新
|
||||
fetchDoctorPhraseList()
|
||||
} else {
|
||||
ElMessage.error('更新失败: ' + (response.msg || '未知错误'))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('更新失败:', error)
|
||||
ElMessage.error('更新操作失败: 网络请求错误')
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时获取数据
|
||||
// 组件挂载时初始化加载数据
|
||||
onMounted(() => {
|
||||
fetchDoctorPhraseList()
|
||||
})
|
||||
@@ -577,6 +506,10 @@ onMounted(() => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
/* 适配小屏幕,防止按钮换行溢出 */
|
||||
gap: 10px;
|
||||
/* 替换margin-left,更优雅的间距 */
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
@@ -584,4 +517,9 @@ onMounted(() => {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* 新增表单样式适配 */
|
||||
.add-form {
|
||||
padding: 10px 0;
|
||||
}
|
||||
</style>
|
||||
@@ -59,12 +59,12 @@ const doRegistering = (row: any) => {
|
||||
// })
|
||||
/* 取消 */
|
||||
const cancelAct = () => {
|
||||
console.log('121231');
|
||||
console.log('取消患者列表对话框操作');
|
||||
|
||||
}
|
||||
/* 保存,登记 */
|
||||
const handleSubmit = () => {
|
||||
console.log('121231');
|
||||
console.log('提交患者列表对话框操作');
|
||||
}
|
||||
defineExpose({})
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@ function handleDatePickerChange() {
|
||||
|
||||
// 清空
|
||||
const onClear = () => {
|
||||
console.log('1111111111');
|
||||
console.log('费用明细查询条件已清空');
|
||||
|
||||
const today = new Date();
|
||||
dateRangeValue.value = [formatDateStr(today, 'YYYY-MM-DD'), formatDateStr(today, 'YYYY-MM-DD')];
|
||||
|
||||
@@ -44,8 +44,8 @@
|
||||
>
|
||||
<div class="bed-card__body">
|
||||
<div>
|
||||
<div class="bed-card__title" :title="item.houseName + '-' + item.bedName">
|
||||
{{ item.houseName + '-' + item.bedName }}
|
||||
<div class="bed-card__title" :title="(item.houseName || '-') + '-' + (item.bedName || '-')">
|
||||
{{ (item.houseName || '-') + '-' + (item.bedName || '-') }}
|
||||
</div>
|
||||
<div class="bed-tag" :class="getBedTagClass(item)">
|
||||
{{ item.bedStatus_enumText }}
|
||||
@@ -130,6 +130,8 @@ const initInfoOptions = ref<InitInfoOptions>({
|
||||
priorityOptions: [],
|
||||
wardListOptions: [],
|
||||
});
|
||||
// 用于处理单击/双击冲突
|
||||
let clickTimer = null;
|
||||
let loadingInstance: any = undefined;
|
||||
// 入院病区loading
|
||||
const selectHosLoding = ref(true);
|
||||
@@ -223,29 +225,38 @@ const queryParams = ref<{
|
||||
bedStatus: '', // 这个字段现在只用于床位查询,不再用于患者列表查询
|
||||
});
|
||||
|
||||
const ininData = () => {
|
||||
Promise.all([
|
||||
getInit().then((res) => {
|
||||
initInfoOptions.value = res.data;
|
||||
priorityOptions.value = res.data.priorityOptions || [];
|
||||
return res;
|
||||
}),
|
||||
getPractitionerWard().then((res) => {
|
||||
const ininData = async () => {
|
||||
try {
|
||||
// 先获取初始化数据
|
||||
const initRes = await getInit();
|
||||
initInfoOptions.value = initRes.data;
|
||||
priorityOptions.value = initRes.data.priorityOptions || [];
|
||||
|
||||
// 然后获取科室数据
|
||||
const wardRes = await getPractitionerWard();
|
||||
selectHosLoding.value = false;
|
||||
queryParams.value.wardId = res[0].id;
|
||||
initInfoOptions.value.wardListOptions = res;
|
||||
return changeWardLocationId(res[0].id, true); // 传入 true 表示初始化阶段,不调用 getList
|
||||
}),
|
||||
]).then(() => {
|
||||
queryParams.value.wardId = wardRes[0]?.id || '';
|
||||
initInfoOptions.value.wardListOptions = wardRes;
|
||||
|
||||
// 获取病房数据
|
||||
if (wardRes[0]?.id) {
|
||||
await changeWardLocationId(wardRes[0].id, true); // 传入 true 表示初始化阶段,不调用 getList
|
||||
}
|
||||
|
||||
// 最后获取列表数据
|
||||
getList();
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('初始化数据失败:', error);
|
||||
// 即使某个请求失败,也要尝试加载列表
|
||||
getList();
|
||||
}
|
||||
};
|
||||
onMounted(() => {
|
||||
ininData();
|
||||
});
|
||||
|
||||
const refreshTap = () => {
|
||||
ininData();
|
||||
const refreshTap = async () => {
|
||||
await ininData();
|
||||
};
|
||||
defineExpose({ state, refreshTap });
|
||||
|
||||
@@ -257,23 +268,32 @@ const filteredBadList = computed(() => {
|
||||
return badList.value.filter((item) => item.bedStatus == bedStatusFilter.value);
|
||||
});
|
||||
|
||||
const getList = () => {
|
||||
const getList = async () => {
|
||||
loadingInstance = ElLoading.service({ fullscreen: true });
|
||||
getPatientList();
|
||||
// 床位查询不使用encounterStatus参数,只使用基本的查询参数
|
||||
const bedQueryParams = {
|
||||
|
||||
try {
|
||||
// 并行获取患者列表和床位列表
|
||||
const [patientData, bedRes] = await Promise.all([
|
||||
getPatientList(),
|
||||
getBedInfo({
|
||||
...queryParams.value,
|
||||
encounterStatus: undefined, // 移除encounterStatus,确保不影响床位列表查询
|
||||
};
|
||||
})
|
||||
]);
|
||||
|
||||
getBedInfo(bedQueryParams).then((res) => {
|
||||
// 更新床位列表
|
||||
badList.value = bedRes.data.records;
|
||||
} catch (error) {
|
||||
console.error('获取列表数据失败:', error);
|
||||
} finally {
|
||||
if (loadingInstance) {
|
||||
loadingInstance.close();
|
||||
badList.value = res.data.records;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 重置查询条件
|
||||
function resetQuery() {
|
||||
async function resetQuery() {
|
||||
// 不重置入院病区
|
||||
const resetParams = {
|
||||
...queryParams.value,
|
||||
@@ -288,7 +308,7 @@ function resetQuery() {
|
||||
bedStatus: '',
|
||||
};
|
||||
bedStatusFilter.value = '';
|
||||
getList();
|
||||
await getList();
|
||||
}
|
||||
// 入院病区下拉选
|
||||
function changeWardLocationId(id: string | number, isInit = false) {
|
||||
@@ -302,45 +322,78 @@ function changeWardLocationId(id: string | number, isInit = false) {
|
||||
selectHoouseLoding.value = false;
|
||||
wardLocationList.value = res;
|
||||
if (!isInit) {
|
||||
getList();
|
||||
// 在非初始化情况下,需要同时更新患者列表和床位列表
|
||||
getPatientList();
|
||||
// 床位查询不使用encounterStatus参数,只使用基本的查询参数
|
||||
const bedQueryParams = {
|
||||
...queryParams.value,
|
||||
encounterStatus: undefined, // 移除encounterStatus,确保不影响床位列表查询
|
||||
};
|
||||
getBedInfo(bedQueryParams).then((bedRes) => {
|
||||
badList.value = bedRes.data.records;
|
||||
});
|
||||
}
|
||||
return res;
|
||||
});
|
||||
}
|
||||
// 入院病房下拉选
|
||||
const onHosHouse = () => {
|
||||
getList();
|
||||
const onHosHouse = async () => {
|
||||
await getList();
|
||||
};
|
||||
// 住院状态下拉选
|
||||
const onHosStatus = () => {
|
||||
getList();
|
||||
const onHosStatus = async () => {
|
||||
await getList();
|
||||
};
|
||||
// 获新入院患者列表
|
||||
function getPatientList() {
|
||||
async function getPatientList() {
|
||||
// 为患者列表查询创建一个新的参数对象,不包含bedStatus
|
||||
const patientQueryParams = {
|
||||
...queryParams.value,
|
||||
bedStatus: undefined, // 移除bedStatus,确保不影响患者列表查询
|
||||
};
|
||||
getPendingInfo(patientQueryParams).then((res) => {
|
||||
try {
|
||||
const res = await getPendingInfo(patientQueryParams);
|
||||
loading.value = false;
|
||||
patientList.value = res.data.records;
|
||||
total.value = res.data.total;
|
||||
});
|
||||
return res;
|
||||
} catch (error) {
|
||||
console.error('获取患者列表失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const handleTransferInOk = () => {
|
||||
const handleTransferInOk = async () => {
|
||||
transferInDialogVisible.value = false;
|
||||
getList();
|
||||
await getList();
|
||||
};
|
||||
|
||||
function handleCardClick(item: any, index: number) {}
|
||||
// 单击患者卡片事件 - 直接触发入科选床界面
|
||||
function handleCardClick(item: any, index: number) {
|
||||
if (item.encounterStatus == 2) {
|
||||
// 显示提示信息,指导用户如何分配床位
|
||||
ElMessage({
|
||||
message: '该患者尚未分配病床,请通过拖拽操作将患者分配到右侧床位',
|
||||
type: 'warning',
|
||||
grouping: true,
|
||||
showClose: true,
|
||||
});
|
||||
} else {
|
||||
pendingInfo.value = {
|
||||
...item,
|
||||
entranceType: 1,
|
||||
};
|
||||
|
||||
// 双击患者卡片事件
|
||||
transferInDialogVisible.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 双击患者卡片事件 - 保持原有逻辑
|
||||
function handleCardDblClick(item: any) {
|
||||
if (item.encounterStatus == 2) {
|
||||
// 显示提示信息,指导用户如何分配床位
|
||||
ElMessage({
|
||||
message: '请分配病床!',
|
||||
message: '该患者尚未分配病床,请通过拖拽操作将患者分配到右侧床位',
|
||||
type: 'warning',
|
||||
grouping: true,
|
||||
showClose: true,
|
||||
@@ -367,8 +420,8 @@ function handleDragStart(event: DragEvent, item: any) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleQuery() {
|
||||
getList();
|
||||
async function handleQuery() {
|
||||
await getList();
|
||||
}
|
||||
|
||||
// 拖拽结束事件
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<div class="patient-info">
|
||||
<div style="display: flex; align-items: center; margin-bottom: 16px">
|
||||
<div style="margin-right: 36px; font-size: 18px; font-weight: 700">
|
||||
{{ props.pendingInfo.houseName + '-' + props.pendingInfo.bedName }}
|
||||
{{ (props.pendingInfo.houseName || '-') + '-' + (props.pendingInfo.bedName || '-') }}
|
||||
</div>
|
||||
<div style="border-radius: 50px; border: 2px solid slategray; padding: 4px 12px">
|
||||
{{ props.pendingInfo.contractName }}
|
||||
|
||||
@@ -746,25 +746,42 @@ function handleGetDRMedication() {
|
||||
});
|
||||
}
|
||||
|
||||
const initData = () => {
|
||||
getInit().then((res) => {
|
||||
initInfoOptions.value = res.data;
|
||||
const initData = async () => {
|
||||
try {
|
||||
// 并行获取初始化数据和科室数据
|
||||
const [initRes, wardRes] = await Promise.all([
|
||||
getInit(),
|
||||
getPractitionerWard()
|
||||
]);
|
||||
|
||||
// 处理初始化数据
|
||||
initInfoOptions.value = initRes.data;
|
||||
|
||||
// 处理科室数据
|
||||
queryParams.value.wardId = wardRes[0]?.id || '';
|
||||
initInfoOptions.value.wardListOptions = wardRes;
|
||||
|
||||
// 获取病房数据
|
||||
if (wardRes[0]?.id) {
|
||||
changeWardLocationId(wardRes[0].id);
|
||||
}
|
||||
|
||||
// 最后获取患者列表
|
||||
getPatientList();
|
||||
});
|
||||
getPractitionerWard().then((res) => {
|
||||
queryParams.value.wardId = res[0].id;
|
||||
initInfoOptions.value.wardListOptions = res;
|
||||
changeWardLocationId(res[0].id);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('初始化数据失败:', error);
|
||||
// 即使初始化失败,也要尝试加载患者列表
|
||||
getPatientList();
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化加载患者列表
|
||||
onMounted(() => {
|
||||
initData();
|
||||
onMounted(async () => {
|
||||
await initData();
|
||||
});
|
||||
const refreshTap = () => {
|
||||
console.log('22222223');
|
||||
initData();
|
||||
const refreshTap = async () => {
|
||||
console.log('刷新转科/出院页面数据');
|
||||
await initData();
|
||||
};
|
||||
defineExpose({ refreshTap });
|
||||
</script>
|
||||
|
||||
@@ -44,16 +44,15 @@ const thirdRef = ref();
|
||||
onBeforeMount(() => {});
|
||||
onMounted(() => {});
|
||||
defineExpose({ state });
|
||||
const test = () => {
|
||||
nextTick(() => {
|
||||
const test = async () => {
|
||||
await nextTick();
|
||||
if (activeTabName.value == 'first') {
|
||||
firstRef?.value?.refreshTap();
|
||||
await firstRef?.value?.refreshTap();
|
||||
} else if (activeTabName.value == 'second') {
|
||||
secondRef?.value?.refreshTap();
|
||||
await secondRef?.value?.refreshTap();
|
||||
} else if (activeTabName.value == 'third') {
|
||||
thirdRef?.value?.refreshTap();
|
||||
await thirdRef?.value?.refreshTap();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const activeTabName = ref('first');
|
||||
|
||||
@@ -496,7 +496,7 @@ const dateChange = () => {
|
||||
};
|
||||
// 计算时间
|
||||
rulesFrom.value.recordTime = computed(() => {
|
||||
console.log('11111111111111');
|
||||
console.log('操作记录时间计算', rulesFrom.value.date + ' ' + rulesFrom.value.time);
|
||||
|
||||
return rulesFrom.value.date + ' ' + rulesFrom.value.time;
|
||||
});
|
||||
|
||||
@@ -274,7 +274,7 @@ function init1(data) {
|
||||
* 点击患者列表行 获取患者体温单数据
|
||||
*/
|
||||
function viewPatient(row) {
|
||||
console.log('1232312123221231');
|
||||
console.log('查看患者体温单数据', row.patientName);
|
||||
|
||||
patientInfo.value = row;
|
||||
console.log('点击患者列表行 获取患者体温单数据', row);
|
||||
|
||||
@@ -557,7 +557,7 @@ watch(
|
||||
queryParams.value.dispenseTimeETime = newQuery.occurrenceTimeETime + ' 23:59:59';
|
||||
}
|
||||
queryParams.value.flag = 1;
|
||||
console.log('11111111111111');
|
||||
console.log('药品明细查询参数已设置完成');
|
||||
|
||||
// 执行查询
|
||||
getList();
|
||||
|
||||
@@ -218,7 +218,7 @@ watch(
|
||||
if (currentMonth < birthMonth || (currentMonth === birthMonth && currentDay < birthDay)) {
|
||||
age--;
|
||||
}
|
||||
console.log('22222222');
|
||||
console.log('计算患者年龄完成:', age);
|
||||
|
||||
form.value.age = age;
|
||||
}
|
||||
|
||||
@@ -1,55 +1,23 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form
|
||||
:model="queryParams"
|
||||
ref="queryRef"
|
||||
:inline="true"
|
||||
v-show="showSearch"
|
||||
label-width="68px"
|
||||
class="query-form"
|
||||
>
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"
|
||||
class="query-form">
|
||||
<el-form-item label="字典名称" prop="dictName">
|
||||
<el-input
|
||||
v-model="queryParams.dictName"
|
||||
placeholder="请输入字典名称"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
<el-input v-model="queryParams.dictName" placeholder="请输入字典名称" clearable style="width: 240px"
|
||||
@keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="字典类型" prop="dictType">
|
||||
<el-input
|
||||
v-model="queryParams.dictType"
|
||||
placeholder="请输入字典类型"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
<el-input v-model="queryParams.dictType" placeholder="请输入字典类型" clearable style="width: 240px"
|
||||
@keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="字典状态"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in sys_normal_disable"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
<el-select v-model="queryParams.status" placeholder="字典状态" clearable style="width: 240px">
|
||||
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" style="width: 308px">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
<el-date-picker v-model="dateRange" value-format="YYYY-MM-DD" type="daterange" range-separator="-"
|
||||
start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item class="search-buttons">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
@@ -59,76 +27,34 @@
|
||||
|
||||
<el-row :gutter="10" class="button-group">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['system:dict:add']"
|
||||
>新增</el-button
|
||||
>
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:dict:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleUpdate"
|
||||
v-hasPermi="['system:dict:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate"
|
||||
v-hasPermi="['system:dict:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
v-hasPermi="['system:dict:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"
|
||||
v-hasPermi="['system:dict:remove']">删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['system:dict:export']"
|
||||
>导出</el-button
|
||||
>
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport"
|
||||
v-hasPermi="['system:dict:export']">导出</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Refresh"
|
||||
@click="handleRefreshCache"
|
||||
v-hasPermi="['system:dict:remove']"
|
||||
>刷新缓存</el-button
|
||||
>
|
||||
<el-button type="danger" plain icon="Refresh" @click="handleRefreshCache"
|
||||
v-hasPermi="['system:dict:remove']">刷新缓存</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<div class="table-wrapper">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="typeList"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="字典编号" align="center" prop="dictId" />
|
||||
<el-table-column
|
||||
label="字典名称"
|
||||
align="center"
|
||||
prop="dictName"
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column label="字典名称" align="center" prop="dictName" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="字典类型" align="center" :show-overflow-tooltip="true">
|
||||
<template #default="scope">
|
||||
<router-link :to="'/system/dict-data/index/' + scope.row.dictId" class="link-type">
|
||||
<router-link :to="'/system/dict/data/' + scope.row.dictId" class="link-type">
|
||||
<span>{{ scope.row.dictType }}</span>
|
||||
</router-link>
|
||||
</template>
|
||||
@@ -144,40 +70,18 @@
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
align="center"
|
||||
width="160"
|
||||
class-name="small-padding fixed-width"
|
||||
>
|
||||
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:dict:edit']"
|
||||
class="action-button">修改</el-button
|
||||
>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['system:dict:remove']"
|
||||
class="action-button">删除</el-button
|
||||
>
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:dict:edit']" class="action-button">修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['system:dict:remove']" class="action-button">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination-container">
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -213,7 +117,7 @@
|
||||
|
||||
<script setup name="Dict">
|
||||
import useDictStore from '@/store/modules/dict';
|
||||
import {addType, delType, getType, listType, refreshCache, updateType,} from '@/api/system/dict/type';
|
||||
import { addType, delType, getType, listType, refreshCache, updateType, } from '@/api/system/dict/type';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { sys_normal_disable } = proxy.useDict('sys_normal_disable');
|
||||
@@ -291,7 +195,7 @@ function handleAdd() {
|
||||
/** 多选框选中数据 */
|
||||
function handleSelectionChange(selection) {
|
||||
ids.value = selection.map((item) => item.dictId);
|
||||
single.value = selection.length != 1;
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
}
|
||||
/** 修改按钮操作 */
|
||||
@@ -308,14 +212,14 @@ function handleUpdate(row) {
|
||||
function submitForm() {
|
||||
proxy.$refs['dictRef'].validate((valid) => {
|
||||
if (valid) {
|
||||
if (form.value.dictId != undefined) {
|
||||
updateType(form.value).then((response) => {
|
||||
if (form.value.dictId !== undefined) {
|
||||
updateType(form.value).then(() => {
|
||||
proxy.$modal.msgSuccess('修改成功');
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
} else {
|
||||
addType(form.value).then((response) => {
|
||||
addType(form.value).then(() => {
|
||||
proxy.$modal.msgSuccess('新增成功');
|
||||
open.value = false;
|
||||
getList();
|
||||
@@ -336,7 +240,7 @@ function handleDelete(row) {
|
||||
getList();
|
||||
proxy.$modal.msgSuccess('删除成功');
|
||||
})
|
||||
.catch(() => {});
|
||||
.catch(() => { });
|
||||
}
|
||||
/** 导出按钮操作 */
|
||||
function handleExport() {
|
||||
|
||||
112
openhis-ui-vue3/src/views/system/user copy/authRole.vue
Normal file
112
openhis-ui-vue3/src/views/system/user copy/authRole.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<h4 class="form-header h4">基本信息</h4>
|
||||
<el-form :model="form" label-width="80px">
|
||||
<el-row>
|
||||
<el-col :span="8" :offset="2">
|
||||
<el-form-item label="用户昵称" prop="nickName">
|
||||
<el-input v-model="form.nickName" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" :offset="2">
|
||||
<el-form-item label="登录账号" prop="userName">
|
||||
<el-input v-model="form.userName" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<h4 class="form-header h4">角色信息</h4>
|
||||
<el-table v-loading="loading" :row-key="getRowKey" @row-click="clickRow" ref="roleRef" @selection-change="handleSelectionChange" :data="roles.slice((pageNum - 1) * pageSize, pageNum * pageSize)">
|
||||
<el-table-column label="序号" width="55" type="index" align="center">
|
||||
<template #default="scope">
|
||||
<span>{{ (pageNum - 1) * pageSize + scope.$index + 1 }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column type="selection" :reserve-selection="true" width="55"></el-table-column>
|
||||
<el-table-column label="角色编号" align="center" prop="roleId" />
|
||||
<el-table-column label="角色名称" align="center" prop="roleName" />
|
||||
<el-table-column label="权限字符" align="center" prop="roleKey" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" />
|
||||
|
||||
<el-form label-width="100px">
|
||||
<div style="text-align: center;margin-left:-120px;margin-top:30px;">
|
||||
<el-button type="primary" @click="submitForm()">提交</el-button>
|
||||
<el-button @click="close()">返回</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="AuthRole">
|
||||
import {getAuthRole, updateAuthRole} from "@/api/system/user copy";
|
||||
|
||||
const route = useRoute();
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const loading = ref(true);
|
||||
const total = ref(0);
|
||||
const pageNum = ref(1);
|
||||
const pageSize = ref(10);
|
||||
const roleIds = ref([]);
|
||||
const roles = ref([]);
|
||||
const form = ref({
|
||||
nickName: undefined,
|
||||
userName: undefined,
|
||||
userId: undefined
|
||||
});
|
||||
|
||||
/** 单击选中行数据 */
|
||||
function clickRow(row) {
|
||||
proxy.$refs["roleRef"].toggleRowSelection(row);
|
||||
};
|
||||
/** 多选框选中数据 */
|
||||
function handleSelectionChange(selection) {
|
||||
roleIds.value = selection.map(item => item.roleId);
|
||||
};
|
||||
/** 保存选中的数据编号 */
|
||||
function getRowKey(row) {
|
||||
return row.roleId;
|
||||
};
|
||||
/** 关闭按钮 */
|
||||
function close() {
|
||||
const obj = { path: "/system/user" };
|
||||
proxy.$tab.closeOpenPage(obj);
|
||||
};
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
const userId = form.value.userId;
|
||||
const rIds = roleIds.value.join(",");
|
||||
updateAuthRole({ userId: userId, roleIds: rIds }).then(response => {
|
||||
proxy.$modal.msgSuccess("授权成功");
|
||||
close();
|
||||
});
|
||||
};
|
||||
|
||||
(() => {
|
||||
const userId = route.params && route.params.userId;
|
||||
if (userId) {
|
||||
loading.value = true;
|
||||
getAuthRole(userId).then(response => {
|
||||
form.value = response.user;
|
||||
roles.value = response.roles;
|
||||
total.value = roles.value.length;
|
||||
nextTick(() => {
|
||||
roles.value.forEach(row => {
|
||||
if (row.flag) {
|
||||
proxy.$refs["roleRef"].toggleRowSelection(row);
|
||||
}
|
||||
});
|
||||
});
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
621
openhis-ui-vue3/src/views/system/user copy/index.vue
Normal file
621
openhis-ui-vue3/src/views/system/user copy/index.vue
Normal file
@@ -0,0 +1,621 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<!--部门数据-->
|
||||
<el-col :span="4" :xs="24">
|
||||
<div class="head-container">
|
||||
<el-input
|
||||
v-model="deptName"
|
||||
placeholder="请输入部门名称"
|
||||
clearable
|
||||
prefix-icon="Search"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
</div>
|
||||
<div class="head-container">
|
||||
<el-tree
|
||||
:data="deptOptions"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterNode"
|
||||
ref="deptTreeRef"
|
||||
node-key="id"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
</div>
|
||||
</el-col>
|
||||
<!--用户数据-->
|
||||
<el-col :span="20" :xs="24">
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="用户名称" prop="userName">
|
||||
<el-input
|
||||
v-model="queryParams.userName"
|
||||
placeholder="请输入用户名称"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input
|
||||
v-model="queryParams.phonenumber"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="用户状态"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in sys_normal_disable"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" style="width: 308px;">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['system:user:add']"
|
||||
>新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleUpdate"
|
||||
v-hasPermi="['system:user:edit']"
|
||||
>修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
v-hasPermi="['system:user:remove']"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="info"
|
||||
plain
|
||||
icon="Upload"
|
||||
@click="handleImport"
|
||||
v-hasPermi="['system:user:import']"
|
||||
>导入</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['system:user:export']"
|
||||
>导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="用户编号" align="center" key="userId" prop="userId" v-if="columns[0].visible" />
|
||||
<el-table-column label="用户名称" align="center" key="userName" prop="userName" v-if="columns[1].visible" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="用户昵称" align="center" key="nickName" prop="nickName" v-if="columns[2].visible" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="部门" align="center" key="deptName" prop="dept.deptName" v-if="columns[3].visible" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="手机号码" align="center" key="phonenumber" prop="phonenumber" v-if="columns[4].visible" width="120" />
|
||||
<el-table-column label="状态" align="center" key="status" v-if="columns[5].visible">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
@change="handleStatusChange(scope.row)"
|
||||
></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" v-if="columns[6].visible" width="160">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="150" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top" v-if="scope.row.userId !== 1">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:user:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top" v-if="scope.row.userId !== 1">
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:user:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="重置密码" placement="top" v-if="scope.row.userId !== 1">
|
||||
<el-button link type="primary" icon="Key" @click="handleResetPwd(scope.row)" v-hasPermi="['system:user:resetPwd']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="分配角色" placement="top" v-if="scope.row.userId !== 1">
|
||||
<el-button link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)" v-hasPermi="['system:user:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 添加或修改用户配置对话框 -->
|
||||
<el-dialog :title="title" v-model="open" width="600px" append-to-body>
|
||||
<el-form :model="form" :rules="rules" ref="userRef" label-width="80px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="用户昵称" prop="nickName">
|
||||
<el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="归属部门" prop="deptId">
|
||||
<el-tree-select
|
||||
v-model="form.deptId"
|
||||
:data="deptOptions"
|
||||
:props="{ value: 'id', label: 'label', children: 'children' }"
|
||||
value-key="id"
|
||||
placeholder="请选择归属部门"
|
||||
check-strictly
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
|
||||
<el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
|
||||
<el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="用户性别">
|
||||
<el-select v-model="form.sex" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="dict in sys_user_sex"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio
|
||||
v-for="dict in sys_normal_disable"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
>{{ dict.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="岗位">
|
||||
<el-select v-model="form.postIds" multiple placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in postOptions"
|
||||
:key="item.postId"
|
||||
:label="item.postName"
|
||||
:value="item.postId"
|
||||
:disabled="item.status == 1"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="角色">
|
||||
<el-select v-model="form.roleIds" multiple placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in roleOptions"
|
||||
:key="item.roleId"
|
||||
:label="item.roleName"
|
||||
:value="item.roleId"
|
||||
:disabled="item.status == 1"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 用户导入对话框 -->
|
||||
<el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body>
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
:limit="1"
|
||||
accept=".xlsx, .xls"
|
||||
:headers="upload.headers"
|
||||
:action="upload.url + '?updateSupport=' + upload.updateSupport"
|
||||
:disabled="upload.isUploading"
|
||||
:on-progress="handleFileUploadProgress"
|
||||
:on-success="handleFileSuccess"
|
||||
:auto-upload="false"
|
||||
drag
|
||||
>
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip text-center">
|
||||
<div class="el-upload__tip">
|
||||
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据
|
||||
</div>
|
||||
<span>仅允许导入xls、xlsx格式文件。</span>
|
||||
<el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitFileForm">确 定</el-button>
|
||||
<el-button @click="upload.open = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="User">
|
||||
import {getToken} from "@/utils/auth";
|
||||
import {
|
||||
addUser,
|
||||
changeUserStatus,
|
||||
delUser,
|
||||
deptTreeSelect,
|
||||
getUser,
|
||||
listUser,
|
||||
resetUserPwd,
|
||||
updateUser
|
||||
} from "@/api/system/user copy";
|
||||
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { sys_normal_disable, sys_user_sex } = proxy.useDict("sys_normal_disable", "sys_user_sex");
|
||||
|
||||
const userList = ref([]);
|
||||
const open = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const title = ref("");
|
||||
const dateRange = ref([]);
|
||||
const deptName = ref("");
|
||||
const deptOptions = ref(undefined);
|
||||
const initPassword = ref(undefined);
|
||||
const postOptions = ref([]);
|
||||
const roleOptions = ref([]);
|
||||
/*** 用户导入参数 */
|
||||
const upload = reactive({
|
||||
// 是否显示弹出层(用户导入)
|
||||
open: false,
|
||||
// 弹出层标题(用户导入)
|
||||
title: "",
|
||||
// 是否禁用上传
|
||||
isUploading: false,
|
||||
// 是否更新已经存在的用户数据
|
||||
updateSupport: 0,
|
||||
// 设置上传的请求头部
|
||||
headers: { Authorization: "Bearer " + getToken() },
|
||||
// 上传的地址
|
||||
url: import.meta.env.VITE_APP_BASE_API + "/system/user/importData"
|
||||
});
|
||||
// 列显隐信息
|
||||
const columns = ref([
|
||||
{ key: 0, label: `用户编号`, visible: true },
|
||||
{ key: 1, label: `用户名称`, visible: true },
|
||||
{ key: 2, label: `用户昵称`, visible: true },
|
||||
{ key: 3, label: `部门`, visible: true },
|
||||
{ key: 4, label: `手机号码`, visible: true },
|
||||
{ key: 5, label: `状态`, visible: true },
|
||||
{ key: 6, label: `创建时间`, visible: true }
|
||||
]);
|
||||
|
||||
const data = reactive({
|
||||
form: {},
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
userName: undefined,
|
||||
phonenumber: undefined,
|
||||
status: undefined,
|
||||
deptId: undefined
|
||||
},
|
||||
rules: {
|
||||
userName: [{ required: true, message: "用户名称不能为空", trigger: "blur" }, { min: 2, max: 20, message: "用户名称长度必须介于 2 和 20 之间", trigger: "blur" }],
|
||||
nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
|
||||
password: [{ required: true, message: "用户密码不能为空", trigger: "blur" }, { min: 5, max: 20, message: "用户密码长度必须介于 5 和 20 之间", trigger: "blur" }, { pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }],
|
||||
email: [{ type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],
|
||||
phonenumber: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }]
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 通过条件过滤节点 */
|
||||
const filterNode = (value, data) => {
|
||||
if (!value) return true;
|
||||
return data.label.indexOf(value) !== -1;
|
||||
};
|
||||
/** 根据名称筛选部门树 */
|
||||
watch(deptName, val => {
|
||||
proxy.$refs["deptTreeRef"].filter(val);
|
||||
});
|
||||
/** 查询部门下拉树结构 */
|
||||
function getDeptTree() {
|
||||
deptTreeSelect().then(response => {
|
||||
deptOptions.value = response.data;
|
||||
});
|
||||
};
|
||||
/** 查询用户列表 */
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
listUser(proxy.addDateRange(queryParams.value, dateRange.value)).then(res => {
|
||||
loading.value = false;
|
||||
userList.value = res.rows;
|
||||
total.value = res.total;
|
||||
});
|
||||
};
|
||||
/** 节点单击事件 */
|
||||
function handleNodeClick(data) {
|
||||
queryParams.value.deptId = data.id;
|
||||
handleQuery();
|
||||
};
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
dateRange.value = [];
|
||||
proxy.resetForm("queryRef");
|
||||
queryParams.value.deptId = undefined;
|
||||
proxy.$refs.deptTreeRef.setCurrentKey(null);
|
||||
handleQuery();
|
||||
};
|
||||
/** 删除按钮操作 */
|
||||
function handleDelete(row) {
|
||||
const userIds = row.userId || ids.value;
|
||||
proxy.$modal.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?').then(function () {
|
||||
return delUser(userIds);
|
||||
}).then(() => {
|
||||
getList();
|
||||
proxy.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
};
|
||||
/** 导出按钮操作 */
|
||||
function handleExport() {
|
||||
proxy.download("system/user/export", {
|
||||
...queryParams.value,
|
||||
},`user_${new Date().getTime()}.xlsx`);
|
||||
};
|
||||
/** 用户状态修改 */
|
||||
function handleStatusChange(row) {
|
||||
let text = row.status === "0" ? "启用" : "停用";
|
||||
proxy.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?').then(function () {
|
||||
return changeUserStatus(row.userId, row.status);
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess(text + "成功");
|
||||
}).catch(function () {
|
||||
row.status = row.status === "0" ? "1" : "0";
|
||||
});
|
||||
};
|
||||
/** 更多操作 */
|
||||
function handleCommand(command, row) {
|
||||
switch (command) {
|
||||
case "handleResetPwd":
|
||||
handleResetPwd(row);
|
||||
break;
|
||||
case "handleAuthRole":
|
||||
handleAuthRole(row);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
/** 跳转角色分配 */
|
||||
function handleAuthRole(row) {
|
||||
const userId = row.userId;
|
||||
router.push("/system/user-auth/role/" + userId);
|
||||
};
|
||||
/** 重置密码按钮操作 */
|
||||
function handleResetPwd(row) {
|
||||
proxy.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
closeOnClickModal: false,
|
||||
inputPattern: /^.{5,20}$/,
|
||||
inputErrorMessage: "用户密码长度必须介于 5 和 20 之间",
|
||||
inputValidator: (value) => {
|
||||
if (/<|>|"|'|\||\\/.test(value)) {
|
||||
return "不能包含非法字符:< > \" ' \\\ |"
|
||||
}
|
||||
},
|
||||
}).then(({ value }) => {
|
||||
resetUserPwd(row.userId, value).then(response => {
|
||||
proxy.$modal.msgSuccess("修改成功,新密码是:" + value);
|
||||
});
|
||||
}).catch(() => {});
|
||||
};
|
||||
/** 选择条数 */
|
||||
function handleSelectionChange(selection) {
|
||||
ids.value = selection.map(item => item.userId);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
/** 导入按钮操作 */
|
||||
function handleImport() {
|
||||
upload.title = "用户导入";
|
||||
upload.open = true;
|
||||
};
|
||||
/** 下载模板操作 */
|
||||
function importTemplate() {
|
||||
proxy.download("system/user/importTemplate", {
|
||||
}, `user_template_${new Date().getTime()}.xlsx`);
|
||||
};
|
||||
/**文件上传中处理 */
|
||||
const handleFileUploadProgress = (event, file, fileList) => {
|
||||
upload.isUploading = true;
|
||||
};
|
||||
/** 文件上传成功处理 */
|
||||
const handleFileSuccess = (response, file, fileList) => {
|
||||
upload.open = false;
|
||||
upload.isUploading = false;
|
||||
proxy.$refs["uploadRef"].handleRemove(file);
|
||||
proxy.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
|
||||
getList();
|
||||
};
|
||||
/** 提交上传文件 */
|
||||
function submitFileForm() {
|
||||
proxy.$refs["uploadRef"].submit();
|
||||
};
|
||||
/** 重置操作表单 */
|
||||
function reset() {
|
||||
form.value = {
|
||||
userId: undefined,
|
||||
deptId: undefined,
|
||||
userName: undefined,
|
||||
nickName: undefined,
|
||||
password: undefined,
|
||||
phonenumber: undefined,
|
||||
email: undefined,
|
||||
sex: undefined,
|
||||
status: "0",
|
||||
remark: undefined,
|
||||
postIds: [],
|
||||
roleIds: []
|
||||
};
|
||||
proxy.resetForm("userRef");
|
||||
};
|
||||
/** 取消按钮 */
|
||||
function cancel() {
|
||||
open.value = false;
|
||||
reset();
|
||||
};
|
||||
/** 新增按钮操作 */
|
||||
function handleAdd() {
|
||||
reset();
|
||||
getUser().then(response => {
|
||||
postOptions.value = response.posts;
|
||||
roleOptions.value = response.roles;
|
||||
open.value = true;
|
||||
title.value = "添加用户";
|
||||
form.value.password = initPassword.value;
|
||||
});
|
||||
};
|
||||
/** 修改按钮操作 */
|
||||
function handleUpdate(row) {
|
||||
reset();
|
||||
const userId = row.userId || ids.value;
|
||||
getUser(userId).then(response => {
|
||||
form.value = response.data;
|
||||
postOptions.value = response.posts;
|
||||
roleOptions.value = response.roles;
|
||||
form.value.postIds = response.postIds;
|
||||
form.value.roleIds = response.roleIds;
|
||||
open.value = true;
|
||||
title.value = "修改用户";
|
||||
form.password = "";
|
||||
});
|
||||
};
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs["userRef"].validate(valid => {
|
||||
if (valid) {
|
||||
if (form.value.userId != undefined) {
|
||||
updateUser(form.value).then(response => {
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
} else {
|
||||
addUser(form.value).then(response => {
|
||||
proxy.$modal.msgSuccess("新增成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
getDeptTree();
|
||||
getList();
|
||||
</script>
|
||||
87
openhis-ui-vue3/src/views/system/user copy/profile/index.vue
Normal file
87
openhis-ui-vue3/src/views/system/user copy/profile/index.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6" :xs="24">
|
||||
<el-card class="box-card">
|
||||
<template v-slot:header>
|
||||
<div class="clearfix">
|
||||
<span>个人信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<div class="text-center">
|
||||
<userAvatar />
|
||||
</div>
|
||||
<ul class="list-group list-group-striped">
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="user" />用户名称
|
||||
<div class="pull-right">{{ state.user.userName }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="phone" />手机号码
|
||||
<div class="pull-right">{{ state.user.phonenumber }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="email" />用户邮箱
|
||||
<div class="pull-right">{{ state.user.email }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="tree" />所属部门
|
||||
<div class="pull-right" v-if="state.user.dept">{{ state.user.dept.deptName }} / {{ state.postGroup }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="peoples" />所属角色
|
||||
<div class="pull-right">{{ state.roleGroup }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<svg-icon icon-class="date" />创建日期
|
||||
<div class="pull-right">{{ state.user.createTime }}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="18" :xs="24">
|
||||
<el-card>
|
||||
<template v-slot:header>
|
||||
<div class="clearfix">
|
||||
<span>基本资料</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="基本资料" name="userinfo">
|
||||
<userInfo :user="state.user" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="修改密码" name="resetPwd">
|
||||
<resetPwd />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Profile">
|
||||
import userAvatar from "./userAvatar";
|
||||
import userInfo from "./userInfo";
|
||||
import resetPwd from "./resetPwd";
|
||||
import {getUserProfile} from "@/api/system/user copy";
|
||||
|
||||
const activeTab = ref("userinfo");
|
||||
const state = reactive({
|
||||
user: {},
|
||||
roleGroup: {},
|
||||
postGroup: {}
|
||||
});
|
||||
|
||||
function getUser() {
|
||||
getUserProfile().then(response => {
|
||||
state.user = response.data;
|
||||
state.roleGroup = response.roleGroup;
|
||||
state.postGroup = response.postGroup;
|
||||
});
|
||||
};
|
||||
|
||||
getUser();
|
||||
</script>
|
||||
@@ -0,0 +1,65 @@
|
||||
<!--
|
||||
* @Author: 徐靖博 xujb@ccjb.com.cn
|
||||
* @Date: 2025-03-26 10:49:30
|
||||
* @LastEditTime: 2025-03-26 13:57:02
|
||||
* @LastEditors: 徐靖博 xujb@ccjb.com.cn
|
||||
* @Description:
|
||||
* @FilePath: \openhis-ui-vue3\src\views\system\user copy\profile\resetPwd.vue
|
||||
-->
|
||||
<template>
|
||||
<el-form ref="pwdRef" :model="user" :rules="rules" label-width="80px">
|
||||
<el-form-item label="旧密码" prop="oldPassword">
|
||||
<el-input v-model="user.oldPassword" placeholder="请输入旧密码" type="password" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码" prop="newPassword">
|
||||
<el-input v-model="user.newPassword" placeholder="请输入新密码" type="password" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" prop="confirmPassword">
|
||||
<el-input v-model="user.confirmPassword" placeholder="请确认新密码" type="password" show-password/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submit">保存</el-button>
|
||||
<el-button type="danger" @click="close">关闭</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {updateUserPwd} from "@/api/system/user copy";
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const user = reactive({
|
||||
oldPassword: undefined,
|
||||
newPassword: undefined,
|
||||
confirmPassword: undefined
|
||||
});
|
||||
|
||||
const equalToPassword = (rule, value, callback) => {
|
||||
if (user.newPassword !== value) {
|
||||
callback(new Error("两次输入的密码不一致"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const rules = ref({
|
||||
oldPassword: [{ required: true, message: "旧密码不能为空", trigger: "blur" }],
|
||||
newPassword: [{ required: true, message: "新密码不能为空", trigger: "blur" }, { min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" }, { pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }],
|
||||
confirmPassword: [{ required: true, message: "确认密码不能为空", trigger: "blur" }, { required: true, validator: equalToPassword, trigger: "blur" }]
|
||||
});
|
||||
|
||||
/** 提交按钮 */
|
||||
function submit() {
|
||||
proxy.$refs.pwdRef.validate(valid => {
|
||||
if (valid) {
|
||||
updateUserPwd(user.oldPassword, user.newPassword).then(response => {
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 关闭按钮 */
|
||||
function close() {
|
||||
proxy.$tab.closePage();
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<div class="user-info-head" @click="editCropper()">
|
||||
<img :src="options.img" title="点击上传头像" class="img-circle img-lg" />
|
||||
<el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
|
||||
<el-row>
|
||||
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
|
||||
<vue-cropper
|
||||
ref="cropper"
|
||||
:img="options.img"
|
||||
:info="true"
|
||||
:autoCrop="options.autoCrop"
|
||||
:autoCropWidth="options.autoCropWidth"
|
||||
:autoCropHeight="options.autoCropHeight"
|
||||
:fixedBox="options.fixedBox"
|
||||
:outputType="options.outputType"
|
||||
@realTime="realTime"
|
||||
v-if="visible"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
|
||||
<div class="avatar-upload-preview">
|
||||
<img :src="options.previews.url" :style="options.previews.img" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<br />
|
||||
<el-row>
|
||||
<el-col :lg="2" :md="2">
|
||||
<el-upload
|
||||
action="#"
|
||||
:http-request="requestUpload"
|
||||
:show-file-list="false"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<el-button>
|
||||
选择
|
||||
<el-icon class="el-icon--right"><Upload /></el-icon>
|
||||
</el-button>
|
||||
</el-upload>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 2 }" :md="2">
|
||||
<el-button icon="Plus" @click="changeScale(1)"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
|
||||
<el-button icon="Minus" @click="changeScale(-1)"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
|
||||
<el-button icon="RefreshLeft" @click="rotateLeft()"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
|
||||
<el-button icon="RefreshRight" @click="rotateRight()"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 2, offset: 6 }" :md="2">
|
||||
<el-button type="primary" @click="uploadImg()">提 交</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import "vue-cropper/dist/index.css";
|
||||
import {VueCropper} from "vue-cropper";
|
||||
import {uploadAvatar} from "@/api/system/user copy";
|
||||
import useUserStore from "@/store/modules/user";
|
||||
|
||||
const userStore = useUserStore();
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const open = ref(false);
|
||||
const visible = ref(false);
|
||||
const title = ref("修改头像");
|
||||
|
||||
//图片裁剪数据
|
||||
const options = reactive({
|
||||
img: userStore.avatar, // 裁剪图片的地址
|
||||
autoCrop: true, // 是否默认生成截图框
|
||||
autoCropWidth: 200, // 默认生成截图框宽度
|
||||
autoCropHeight: 200, // 默认生成截图框高度
|
||||
fixedBox: true, // 固定截图框大小 不允许改变
|
||||
outputType: "png", // 默认生成截图为PNG格式
|
||||
filename: 'avatar', // 文件名称
|
||||
previews: {} //预览数据
|
||||
});
|
||||
|
||||
/** 编辑头像 */
|
||||
function editCropper() {
|
||||
open.value = true;
|
||||
}
|
||||
/** 打开弹出层结束时的回调 */
|
||||
function modalOpened() {
|
||||
visible.value = true;
|
||||
}
|
||||
/** 覆盖默认上传行为 */
|
||||
function requestUpload() {}
|
||||
/** 向左旋转 */
|
||||
function rotateLeft() {
|
||||
proxy.$refs.cropper.rotateLeft();
|
||||
}
|
||||
/** 向右旋转 */
|
||||
function rotateRight() {
|
||||
proxy.$refs.cropper.rotateRight();
|
||||
}
|
||||
/** 图片缩放 */
|
||||
function changeScale(num) {
|
||||
num = num || 1;
|
||||
proxy.$refs.cropper.changeScale(num);
|
||||
}
|
||||
/** 上传预处理 */
|
||||
function beforeUpload(file) {
|
||||
if (file.type.indexOf("image/") == -1) {
|
||||
proxy.$modal.msgError("文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。");
|
||||
} else {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
options.img = reader.result;
|
||||
options.filename = file.name;
|
||||
};
|
||||
}
|
||||
}
|
||||
/** 上传图片 */
|
||||
function uploadImg() {
|
||||
proxy.$refs.cropper.getCropBlob(data => {
|
||||
let formData = new FormData();
|
||||
formData.append("avatarfile", data, options.filename);
|
||||
uploadAvatar(formData).then(response => {
|
||||
open.value = false;
|
||||
options.img = import.meta.env.VITE_APP_BASE_API + response.imgUrl;
|
||||
userStore.avatar = options.img;
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
visible.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
/** 实时预览 */
|
||||
function realTime(data) {
|
||||
options.previews = data;
|
||||
}
|
||||
/** 关闭窗口 */
|
||||
function closeDialog() {
|
||||
options.img = userStore.avatar;
|
||||
options.visible = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.user-info-head {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.user-info-head:hover:after {
|
||||
content: "+";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
color: #eee;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
font-size: 24px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
cursor: pointer;
|
||||
line-height: 110px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<el-form ref="userRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="用户昵称" prop="nickName">
|
||||
<el-input v-model="form.nickName" maxlength="30" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input v-model="form.phonenumber" maxlength="11" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="form.email" maxlength="50" />
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-radio-group v-model="form.sex">
|
||||
<el-radio label="0">男</el-radio>
|
||||
<el-radio label="1">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submit">保存</el-button>
|
||||
<el-button type="danger" @click="close">关闭</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {updateUserProfile} from "@/api/system/user copy";
|
||||
|
||||
const props = defineProps({
|
||||
user: {
|
||||
type: Object
|
||||
}
|
||||
});
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const form = ref({});
|
||||
const rules = ref({
|
||||
nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
|
||||
email: [{ required: true, message: "邮箱地址不能为空", trigger: "blur" }, { type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],
|
||||
phonenumber: [{ required: true, message: "手机号码不能为空", trigger: "blur" }, { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }],
|
||||
});
|
||||
|
||||
/** 提交按钮 */
|
||||
function submit() {
|
||||
proxy.$refs.userRef.validate(valid => {
|
||||
if (valid) {
|
||||
updateUserProfile(form.value).then(response => {
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
props.user.phonenumber = form.value.phonenumber;
|
||||
props.user.email = form.value.email;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 关闭按钮 */
|
||||
function close() {
|
||||
proxy.$tab.closePage();
|
||||
};
|
||||
|
||||
// 回显当前登录用户信息
|
||||
watch(() => props.user, user => {
|
||||
if (user) {
|
||||
form.value = { nickName: user.nickName, phonenumber: user.phonenumber, email: user.email, sex: user.sex };
|
||||
}
|
||||
},{ immediate: true });
|
||||
</script>
|
||||
@@ -27,42 +27,49 @@ export function updateCallNumberVoiceConfig(data) {
|
||||
}
|
||||
|
||||
// 分诊排队管理相关API
|
||||
// 获取智能候选池(已签到未入队)
|
||||
// 说明:直接使用门诊挂号的"当日已挂号"接口
|
||||
// 获取智能候选池(使用门诊挂号当日已挂号接口)
|
||||
export function getCandidatePool(params) {
|
||||
return request({
|
||||
url: '/triage/queue/candidatePool',
|
||||
url: '/charge-manage/register/current-day-encounter',
|
||||
method: 'get',
|
||||
params: params,
|
||||
params: {
|
||||
pageNo: params?.pageNo || 1,
|
||||
pageSize: params?.pageSize || 10000,
|
||||
searchKey: params?.searchKey || '',
|
||||
statusEnum: params?.statusEnum || -1 // -1表示排除退号记录(正常挂号)
|
||||
},
|
||||
skipErrorMsg: true // 跳过错误提示,由组件处理
|
||||
}).catch(() => {
|
||||
// 返回一个 rejected promise,让组件可以捕获
|
||||
return Promise.reject(new Error('API未实现'))
|
||||
})
|
||||
}
|
||||
|
||||
// 获取智能队列(当前队列)
|
||||
// 获取智能队列(使用门诊挂号当日已挂号接口)
|
||||
export function getQueueList(params) {
|
||||
return request({
|
||||
url: '/triage/queue/list',
|
||||
url: '/charge-manage/register/current-day-encounter',
|
||||
method: 'get',
|
||||
params: params,
|
||||
params: {
|
||||
pageNo: params?.pageNo || 1,
|
||||
pageSize: params?.pageSize || 10000,
|
||||
searchKey: params?.searchKey || '',
|
||||
statusEnum: params?.statusEnum || -1 // -1表示排除退号记录(正常挂号)
|
||||
},
|
||||
skipErrorMsg: true // 跳过错误提示,由组件处理
|
||||
}).catch(() => {
|
||||
// 返回一个 rejected promise,让组件可以捕获
|
||||
return Promise.reject(new Error('API未实现'))
|
||||
})
|
||||
}
|
||||
|
||||
// 获取统计信息
|
||||
// 获取统计信息(使用门诊挂号当日已挂号接口统计)
|
||||
export function getQueueStatistics(params) {
|
||||
return request({
|
||||
url: '/triage/queue/statistics',
|
||||
url: '/charge-manage/register/current-day-encounter',
|
||||
method: 'get',
|
||||
params: params,
|
||||
params: {
|
||||
pageNo: 1,
|
||||
pageSize: 10000,
|
||||
searchKey: params?.searchKey || '',
|
||||
statusEnum: params?.statusEnum || -1
|
||||
},
|
||||
skipErrorMsg: true // 跳过错误提示,由组件处理
|
||||
}).catch(() => {
|
||||
// 返回一个 rejected promise,让组件可以捕获
|
||||
return Promise.reject(new Error('API未实现'))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -73,7 +80,26 @@ export function addToQueue(data) {
|
||||
method: 'post',
|
||||
data: data,
|
||||
skipErrorMsg: true
|
||||
}).catch(() => Promise.reject(new Error('API未实现')))
|
||||
})
|
||||
}
|
||||
|
||||
// 获取队列列表(从数据库读取)
|
||||
export function getTriageQueueList(params) {
|
||||
return request({
|
||||
url: '/triage/queue/list',
|
||||
method: 'get',
|
||||
params: params,
|
||||
skipErrorMsg: true
|
||||
})
|
||||
}
|
||||
|
||||
// 移出队列
|
||||
export function removeFromQueue(id) {
|
||||
return request({
|
||||
url: `/triage/queue/remove/${id}`,
|
||||
method: 'delete',
|
||||
skipErrorMsg: true
|
||||
})
|
||||
}
|
||||
|
||||
// 调整队列顺序
|
||||
@@ -83,7 +109,7 @@ export function adjustQueueOrder(data) {
|
||||
method: 'put',
|
||||
data: data,
|
||||
skipErrorMsg: true
|
||||
}).catch(() => Promise.reject(new Error('API未实现')))
|
||||
})
|
||||
}
|
||||
|
||||
// 叫号控制
|
||||
@@ -93,7 +119,7 @@ export function callPatient(data) {
|
||||
method: 'post',
|
||||
data: data,
|
||||
skipErrorMsg: true
|
||||
}).catch(() => Promise.reject(new Error('API未实现')))
|
||||
})
|
||||
}
|
||||
|
||||
// 跳过患者
|
||||
@@ -103,7 +129,7 @@ export function skipPatient(data) {
|
||||
method: 'post',
|
||||
data: data,
|
||||
skipErrorMsg: true
|
||||
}).catch(() => Promise.reject(new Error('API未实现')))
|
||||
})
|
||||
}
|
||||
|
||||
// 完成叫号
|
||||
@@ -113,7 +139,7 @@ export function completeCall(data) {
|
||||
method: 'post',
|
||||
data: data,
|
||||
skipErrorMsg: true
|
||||
}).catch(() => Promise.reject(new Error('API未实现')))
|
||||
})
|
||||
}
|
||||
|
||||
// 过号重排
|
||||
@@ -123,5 +149,24 @@ export function requeuePatient(data) {
|
||||
method: 'post',
|
||||
data: data,
|
||||
skipErrorMsg: true
|
||||
}).catch(() => Promise.reject(new Error('API未实现')))
|
||||
})
|
||||
}
|
||||
|
||||
// 下一患者
|
||||
export function nextPatient(data) {
|
||||
return request({
|
||||
url: '/triage/queue/next',
|
||||
method: 'post',
|
||||
data: data,
|
||||
skipErrorMsg: true
|
||||
})
|
||||
}
|
||||
|
||||
// 查询就诊科室列表(从门诊挂号模块复用)
|
||||
export function getLocationTree(query) {
|
||||
return request({
|
||||
url: '/charge-manage/register/org-list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
25
openhis-ui-vue3/test-util-extend.js
Normal file
25
openhis-ui-vue3/test-util-extend.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// 测试util._extend是否存在
|
||||
if (typeof process !== 'undefined' && process.versions && process.versions.node) {
|
||||
try {
|
||||
const util = require('util');
|
||||
console.log('util._extend存在吗?', typeof util._extend);
|
||||
if (typeof util._extend === 'function') {
|
||||
console.log('util._extend是一个函数');
|
||||
} else {
|
||||
console.log('util._extend不是一个函数,添加兼容实现');
|
||||
util._extend = function(destination, source) {
|
||||
for (var key in source) {
|
||||
if (source.hasOwnProperty(key)) {
|
||||
destination[key] = source[key];
|
||||
}
|
||||
}
|
||||
return destination;
|
||||
};
|
||||
console.log('兼容实现添加成功');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('util模块加载失败:', e);
|
||||
}
|
||||
} else {
|
||||
console.log('不在Node.js环境中');
|
||||
}
|
||||
121
query_serial_number.sql
Normal file
121
query_serial_number.sql
Normal file
@@ -0,0 +1,121 @@
|
||||
-- ============================================
|
||||
-- 查询流水号相关SQL语句
|
||||
-- ============================================
|
||||
|
||||
-- 1. 查询所有挂号记录的流水号(主键ID)及相关信息
|
||||
SELECT
|
||||
id AS 流水号,
|
||||
display_order AS 就诊序号,
|
||||
bus_no AS 就诊编码,
|
||||
patient_id AS 患者ID,
|
||||
organization_id AS 科室ID,
|
||||
create_time AS 挂号时间,
|
||||
status_enum AS 状态
|
||||
FROM adm_encounter
|
||||
WHERE delete_flag = '0'
|
||||
ORDER BY create_time DESC;
|
||||
|
||||
-- 2. 根据流水号(主键ID)查询某条挂号记录的详细信息
|
||||
SELECT
|
||||
e.id AS 流水号,
|
||||
e.display_order AS 就诊序号,
|
||||
e.bus_no AS 就诊编码,
|
||||
p.name AS 患者姓名,
|
||||
p.id_card AS 身份证号,
|
||||
p.phone AS 联系电话,
|
||||
o.name AS 科室名称,
|
||||
pr.name AS 医生姓名,
|
||||
e.create_time AS 挂号时间,
|
||||
e.status_enum AS 状态
|
||||
FROM adm_encounter e
|
||||
LEFT JOIN adm_patient p ON e.patient_id = p.id AND p.delete_flag = '0'
|
||||
LEFT JOIN adm_organization o ON e.organization_id = o.id AND o.delete_flag = '0'
|
||||
LEFT JOIN adm_encounter_participant ep ON e.id = ep.encounter_id AND ep.delete_flag = '0' AND ep.type_code = 'PRF'
|
||||
LEFT JOIN adm_practitioner pr ON ep.practitioner_id = pr.id AND pr.delete_flag = '0'
|
||||
WHERE e.id = 1234567890123456789 -- 替换为实际的流水号(主键ID)
|
||||
AND e.delete_flag = '0';
|
||||
|
||||
-- 3. 根据患者姓名或身份证号查询流水号
|
||||
SELECT
|
||||
e.id AS 流水号,
|
||||
e.display_order AS 就诊序号,
|
||||
p.name AS 患者姓名,
|
||||
p.id_card AS 身份证号,
|
||||
e.create_time AS 挂号时间,
|
||||
o.name AS 科室名称
|
||||
FROM adm_encounter e
|
||||
LEFT JOIN adm_patient p ON e.patient_id = p.id AND p.delete_flag = '0'
|
||||
LEFT JOIN adm_organization o ON e.organization_id = o.id AND o.delete_flag = '0'
|
||||
WHERE e.delete_flag = '0'
|
||||
AND (p.name LIKE '%张三%' OR p.id_card = '110101199001011234') -- 替换为实际的患者姓名或身份证号
|
||||
ORDER BY e.create_time DESC;
|
||||
|
||||
-- 4. 查询当日的挂号记录及流水号
|
||||
SELECT
|
||||
e.id AS 流水号,
|
||||
e.display_order AS 就诊序号,
|
||||
e.bus_no AS 就诊编码,
|
||||
p.name AS 患者姓名,
|
||||
p.id_card AS 身份证号,
|
||||
o.name AS 科室名称,
|
||||
pr.name AS 医生姓名,
|
||||
e.create_time AS 挂号时间,
|
||||
e.status_enum AS 状态
|
||||
FROM adm_encounter e
|
||||
LEFT JOIN adm_patient p ON e.patient_id = p.id AND p.delete_flag = '0'
|
||||
LEFT JOIN adm_organization o ON e.organization_id = o.id AND o.delete_flag = '0'
|
||||
LEFT JOIN adm_encounter_participant ep ON e.id = ep.encounter_id AND ep.delete_flag = '0' AND ep.type_code = 'PRF'
|
||||
LEFT JOIN adm_practitioner pr ON ep.practitioner_id = pr.id AND pr.delete_flag = '0'
|
||||
WHERE e.delete_flag = '0'
|
||||
AND DATE(e.create_time) = CURRENT_DATE -- 查询当日
|
||||
ORDER BY e.display_order ASC, e.create_time ASC;
|
||||
|
||||
-- 5. 查询指定日期范围的挂号记录及流水号
|
||||
SELECT
|
||||
e.id AS 流水号,
|
||||
e.display_order AS 就诊序号,
|
||||
e.bus_no AS 就诊编码,
|
||||
p.name AS 患者姓名,
|
||||
p.id_card AS 身份证号,
|
||||
o.name AS 科室名称,
|
||||
e.create_time AS 挂号时间
|
||||
FROM adm_encounter e
|
||||
LEFT JOIN adm_patient p ON e.patient_id = p.id AND p.delete_flag = '0'
|
||||
LEFT JOIN adm_organization o ON e.organization_id = o.id AND o.delete_flag = '0'
|
||||
WHERE e.delete_flag = '0'
|
||||
AND e.create_time >= '2024-01-01 00:00:00' -- 开始时间
|
||||
AND e.create_time <= '2024-01-31 23:59:59' -- 结束时间
|
||||
ORDER BY e.create_time DESC;
|
||||
|
||||
-- 6. 查询指定科室的挂号记录及流水号
|
||||
SELECT
|
||||
e.id AS 流水号,
|
||||
e.display_order AS 就诊序号,
|
||||
p.name AS 患者姓名,
|
||||
o.name AS 科室名称,
|
||||
e.create_time AS 挂号时间
|
||||
FROM adm_encounter e
|
||||
LEFT JOIN adm_patient p ON e.patient_id = p.id AND p.delete_flag = '0'
|
||||
LEFT JOIN adm_organization o ON e.organization_id = o.id AND o.delete_flag = '0'
|
||||
WHERE e.delete_flag = '0'
|
||||
AND o.name LIKE '%心内科%' -- 替换为实际的科室名称
|
||||
AND DATE(e.create_time) = CURRENT_DATE
|
||||
ORDER BY e.display_order ASC;
|
||||
|
||||
-- 7. 统计每日挂号数量及流水号范围
|
||||
SELECT
|
||||
DATE(create_time) AS 日期,
|
||||
COUNT(*) AS 挂号数量,
|
||||
MIN(id) AS 最小流水号,
|
||||
MAX(id) AS 最大流水号,
|
||||
MIN(display_order) AS 最小就诊序号,
|
||||
MAX(display_order) AS 最大就诊序号
|
||||
FROM adm_encounter
|
||||
WHERE delete_flag = '0'
|
||||
GROUP BY DATE(create_time)
|
||||
ORDER BY 日期 DESC
|
||||
LIMIT 30; -- 最近30天
|
||||
|
||||
|
||||
|
||||
|
||||
224
sql/query_today_outpatient_patients.sql
Normal file
224
sql/query_today_outpatient_patients.sql
Normal file
@@ -0,0 +1,224 @@
|
||||
-- ============================================
|
||||
-- 查询今日门诊患者数据(用于调试分诊排队页面)
|
||||
-- 简化版:直接查询,不考虑登录和租户
|
||||
-- ============================================
|
||||
|
||||
-- 1. 最简单查询:查看今天所有的挂号记录(不关联患者表)
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
enc.start_time AS registerTime,
|
||||
enc.status_enum AS statusEnum,
|
||||
enc.organization_id AS departmentId,
|
||||
enc.tenant_id AS tenantId,
|
||||
enc.delete_flag AS deleteFlag,
|
||||
enc.bus_no AS encounterBusNo
|
||||
FROM adm_encounter enc
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.start_time::DATE = CURRENT_DATE
|
||||
ORDER BY enc.start_time DESC
|
||||
LIMIT 100;
|
||||
|
||||
-- ============================================
|
||||
-- 2. 完整查询:直接查询患者信息(简化版,不考虑租户)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
pt.name AS patientName,
|
||||
pt.gender_enum AS genderEnum,
|
||||
pt.birth_date AS birthDate,
|
||||
pt.id_card AS idCard,
|
||||
pt.phone AS phone,
|
||||
enc.bus_no AS encounterBusNo,
|
||||
enc.start_time AS registerTime,
|
||||
enc.reception_time AS receptionTime,
|
||||
enc.status_enum AS statusEnum,
|
||||
enc.subject_status_enum AS subjectStatusEnum,
|
||||
CASE
|
||||
WHEN enc.reception_time IS NOT NULL
|
||||
THEN EXTRACT(EPOCH FROM (enc.reception_time - enc.start_time)) / 60
|
||||
ELSE EXTRACT(EPOCH FROM (NOW() - enc.start_time)) / 60
|
||||
END AS waitingDuration,
|
||||
CASE
|
||||
WHEN enc.end_time IS NOT NULL AND enc.reception_time IS NOT NULL
|
||||
THEN EXTRACT(EPOCH FROM (enc.end_time - enc.reception_time)) / 60
|
||||
ELSE NULL
|
||||
END AS visitDuration,
|
||||
pt.type_code AS typeCode,
|
||||
enc.important_flag AS importantFlag,
|
||||
-- 医生信息(左关联)
|
||||
ep.practitioner_id AS doctorId
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt
|
||||
ON enc.patient_id = pt.id
|
||||
AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_encounter_participant ep
|
||||
ON enc.id = ep.encounter_id
|
||||
AND ep.type_code = 'admitter'
|
||||
AND ep.delete_flag = '0'
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.start_time::DATE = CURRENT_DATE
|
||||
ORDER BY enc.start_time DESC
|
||||
LIMIT 10000;
|
||||
|
||||
-- ============================================
|
||||
-- 3. 检查数据条件:查看可能影响查询结果的数据
|
||||
-- ============================================
|
||||
|
||||
-- 3.1 检查今天的挂号记录总数
|
||||
SELECT
|
||||
COUNT(*) AS total_count,
|
||||
COUNT(CASE WHEN delete_flag = '0' THEN 1 END) AS not_deleted_count,
|
||||
COUNT(CASE WHEN delete_flag = '1' THEN 1 END) AS deleted_count
|
||||
FROM adm_encounter
|
||||
WHERE start_time::DATE = CURRENT_DATE;
|
||||
|
||||
-- 3.2 检查不同租户的数据分布
|
||||
SELECT
|
||||
tenant_id,
|
||||
COUNT(*) AS count,
|
||||
COUNT(CASE WHEN delete_flag = '0' THEN 1 END) AS not_deleted_count
|
||||
FROM adm_encounter
|
||||
WHERE start_time::DATE = CURRENT_DATE
|
||||
GROUP BY tenant_id
|
||||
ORDER BY tenant_id;
|
||||
|
||||
-- 3.3 检查不同状态的数据分布
|
||||
SELECT
|
||||
status_enum,
|
||||
COUNT(*) AS count,
|
||||
COUNT(CASE WHEN delete_flag = '0' THEN 1 END) AS not_deleted_count
|
||||
FROM adm_encounter
|
||||
WHERE start_time::DATE = CURRENT_DATE
|
||||
AND delete_flag = '0'
|
||||
GROUP BY status_enum
|
||||
ORDER BY status_enum;
|
||||
|
||||
-- 3.4 检查是否有患者数据关联
|
||||
SELECT
|
||||
COUNT(DISTINCT enc.id) AS encounter_count,
|
||||
COUNT(DISTINCT pt.id) AS patient_count,
|
||||
COUNT(DISTINCT CASE WHEN pt.delete_flag = '0' THEN pt.id END) AS patient_not_deleted_count
|
||||
FROM adm_encounter enc
|
||||
LEFT JOIN adm_patient pt ON enc.patient_id = pt.id
|
||||
WHERE enc.start_time::DATE = CURRENT_DATE
|
||||
AND enc.delete_flag = '0';
|
||||
|
||||
-- 3.5 检查医生参与信息
|
||||
SELECT
|
||||
COUNT(DISTINCT enc.id) AS encounter_count,
|
||||
COUNT(DISTINCT ep.encounter_id) AS has_doctor_count,
|
||||
COUNT(DISTINCT ep.practitioner_id) AS doctor_count
|
||||
FROM adm_encounter enc
|
||||
LEFT JOIN adm_encounter_participant ep
|
||||
ON enc.id = ep.encounter_id
|
||||
AND ep.type_code = 'admitter'
|
||||
AND ep.delete_flag = '0'
|
||||
WHERE enc.start_time::DATE = CURRENT_DATE
|
||||
AND enc.delete_flag = '0';
|
||||
|
||||
-- ============================================
|
||||
-- 4. 快速查询:直接查看患者姓名和挂号信息(最简单)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS 就诊记录ID,
|
||||
pt.name AS 患者姓名,
|
||||
pt.id_card AS 身份证号,
|
||||
pt.phone AS 联系电话,
|
||||
enc.start_time AS 挂号时间,
|
||||
enc.create_time AS 创建时间,
|
||||
enc.status_enum AS 状态,
|
||||
enc.bus_no AS 就诊流水号
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND pt.delete_flag = '0'
|
||||
AND enc.start_time::DATE = CURRENT_DATE
|
||||
ORDER BY enc.start_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 4.1 使用 create_time 查询(和门诊挂号页面一致)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS 就诊记录ID,
|
||||
pt.name AS 患者姓名,
|
||||
pt.id_card AS 身份证号,
|
||||
pt.phone AS 联系电话,
|
||||
enc.create_time AS 创建时间,
|
||||
enc.start_time AS 开始时间,
|
||||
enc.status_enum AS 状态,
|
||||
enc.bus_no AS 就诊流水号
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND pt.delete_flag = '0'
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 4.2 完全模拟门诊挂号页面的查询(包含支付状态)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS 就诊记录ID,
|
||||
pt.name AS 患者姓名,
|
||||
pt.id_card AS 身份证号,
|
||||
pt.phone AS 联系电话,
|
||||
enc.create_time AS 挂号时间,
|
||||
enc.status_enum AS 状态,
|
||||
enc.bus_no AS 就诊流水号,
|
||||
ci.total_price AS 挂号金额,
|
||||
pr.status_enum AS 支付状态
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
LEFT JOIN fin_payment_reconciliation pr ON ci.id::TEXT = ANY(string_to_array(pr.charge_item_ids,','))
|
||||
AND pr.delete_flag = '0'
|
||||
AND pr.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1 -- 门诊
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 5. 快速检查:查看最近7天的挂号记录数量(使用 start_time)
|
||||
-- ============================================
|
||||
SELECT
|
||||
start_time::DATE AS register_date,
|
||||
COUNT(*) AS total_count,
|
||||
COUNT(CASE WHEN delete_flag = '0' THEN 1 END) AS not_deleted_count
|
||||
FROM adm_encounter
|
||||
WHERE start_time::DATE >= CURRENT_DATE - INTERVAL '7 days'
|
||||
GROUP BY start_time::DATE
|
||||
ORDER BY start_time::DATE DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 5.1 快速检查:查看最近7天的挂号记录数量(使用 create_time)
|
||||
-- ============================================
|
||||
SELECT
|
||||
create_time::DATE AS register_date,
|
||||
COUNT(*) AS total_count,
|
||||
COUNT(CASE WHEN delete_flag = '0' THEN 1 END) AS not_deleted_count
|
||||
FROM adm_encounter
|
||||
WHERE create_time::DATE >= CURRENT_DATE - INTERVAL '7 days'
|
||||
AND class_enum = 1 -- 门诊
|
||||
GROUP BY create_time::DATE
|
||||
ORDER BY create_time::DATE DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 6. 对比 start_time 和 create_time 的差异
|
||||
-- ============================================
|
||||
SELECT
|
||||
id AS 就诊记录ID,
|
||||
start_time AS 开始时间,
|
||||
create_time AS 创建时间,
|
||||
(start_time::DATE) AS 开始日期,
|
||||
(create_time::DATE) AS 创建日期,
|
||||
status_enum AS 状态,
|
||||
delete_flag AS 是否删除
|
||||
FROM adm_encounter
|
||||
WHERE (start_time::DATE = CURRENT_DATE OR create_time::DATE = CURRENT_DATE)
|
||||
AND delete_flag = '0'
|
||||
ORDER BY create_time DESC
|
||||
LIMIT 20;
|
||||
|
||||
284
排查指南-字段查询问题.md
Normal file
284
排查指南-字段查询问题.md
Normal file
@@ -0,0 +1,284 @@
|
||||
# 排查指南:字段查询不到数据的问题
|
||||
|
||||
## 问题类型
|
||||
**症状**:SQL 查询条件使用了某个字段,但查询结果为空,而数据库中明明有数据。
|
||||
|
||||
**根本原因**:查询条件使用的字段在数据插入/更新时没有被设置(为 NULL 或默认值)。
|
||||
|
||||
---
|
||||
|
||||
## 排查步骤
|
||||
|
||||
### 第一步:确认字段是否存在
|
||||
```sql
|
||||
-- 检查表结构,确认字段是否存在
|
||||
SELECT column_name, data_type, is_nullable, column_default
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'adm_encounter'
|
||||
AND column_name = 'start_time';
|
||||
```
|
||||
|
||||
**检查点**:
|
||||
- ✅ 字段确实存在
|
||||
- ❌ 字段不存在 → 检查字段名拼写、大小写
|
||||
|
||||
---
|
||||
|
||||
### 第二步:检查字段是否有值
|
||||
```sql
|
||||
-- 检查字段的实际值
|
||||
SELECT
|
||||
id,
|
||||
start_time, -- 检查这个字段
|
||||
create_time, -- 对比其他时间字段
|
||||
status_enum
|
||||
FROM adm_encounter
|
||||
WHERE delete_flag = '0'
|
||||
AND create_time::DATE = CURRENT_DATE -- 用能查到数据的条件
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
**检查点**:
|
||||
- ✅ `start_time` 有值 → 继续排查查询条件
|
||||
- ❌ `start_time` 为 NULL → **问题确认**:字段没有被设置
|
||||
|
||||
---
|
||||
|
||||
### 第三步:检查插入/更新代码
|
||||
找到创建/更新数据的 Service 方法,检查是否设置了该字段。
|
||||
|
||||
**检查位置**:
|
||||
1. **Service 实现类**:查找 `save`、`insert`、`update` 相关方法
|
||||
2. **Mapper XML**:检查 INSERT/UPDATE 语句
|
||||
3. **Entity 类**:检查字段定义和默认值
|
||||
|
||||
**示例检查**:
|
||||
```java
|
||||
// ❌ 错误示例:没有设置 start_time
|
||||
Encounter encounter = new Encounter();
|
||||
encounter.setBusNo("xxx");
|
||||
encounter.setPatientId(123L);
|
||||
// 没有 encounter.setStartTime(...)
|
||||
iEncounterService.save(encounter);
|
||||
|
||||
// ✅ 正确示例:设置了 start_time
|
||||
Encounter encounter = new Encounter();
|
||||
encounter.setBusNo("xxx");
|
||||
encounter.setPatientId(123L);
|
||||
encounter.setStartTime(new Date()); // 设置了字段
|
||||
iEncounterService.save(encounter);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 第四步:对比能查到数据的方法
|
||||
找到系统中**能查到数据**的类似查询,对比差异。
|
||||
|
||||
**对比维度**:
|
||||
1. **使用的字段**:`start_time` vs `create_time`
|
||||
2. **查询条件**:WHERE 子句的差异
|
||||
3. **关联表**:是否 JOIN 了其他表
|
||||
4. **业务逻辑**:是否过滤了特定状态
|
||||
|
||||
**示例对比**:
|
||||
```sql
|
||||
-- ❌ 查不到数据的方法
|
||||
SELECT * FROM adm_encounter
|
||||
WHERE start_time::DATE = CURRENT_DATE; -- start_time 为 NULL
|
||||
|
||||
-- ✅ 能查到数据的方法
|
||||
SELECT * FROM adm_encounter
|
||||
WHERE create_time::DATE = CURRENT_DATE; -- create_time 有值
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 第五步:检查字段的业务含义
|
||||
确认字段的**业务含义**和**使用场景**。
|
||||
|
||||
**常见情况**:
|
||||
- `create_time`:记录创建时间(自动设置)
|
||||
- `update_time`:记录更新时间(自动设置)
|
||||
- `start_time`:业务开始时间(需要手动设置)
|
||||
- `end_time`:业务结束时间(需要手动设置)
|
||||
|
||||
**判断规则**:
|
||||
- 如果字段是**业务字段**(如 `start_time`),需要手动设置
|
||||
- 如果字段是**系统字段**(如 `create_time`),通常自动设置
|
||||
|
||||
---
|
||||
|
||||
## 调试技巧
|
||||
|
||||
### 技巧1:打印 SQL 和参数
|
||||
在 Service 方法中添加日志,查看实际执行的 SQL:
|
||||
|
||||
```java
|
||||
@Override
|
||||
public IPage<TodayOutpatientPatientDto> getTodayOutpatientPatients(...) {
|
||||
// 打印查询条件
|
||||
log.info("查询条件: queryDate={}, doctorId={}, departmentId={}",
|
||||
queryDate, doctorId, departmentId);
|
||||
|
||||
// 执行查询
|
||||
IPage<TodayOutpatientPatientDto> result = mapper.getTodayOutpatientPatients(...);
|
||||
|
||||
// 打印结果
|
||||
log.info("查询结果: 总数={}, 记录数={}",
|
||||
result.getTotal(), result.getRecords().size());
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
### 技巧2:直接执行 SQL 验证
|
||||
在数据库客户端直接执行 SQL,验证查询条件是否正确:
|
||||
|
||||
```sql
|
||||
-- 1. 先查看数据
|
||||
SELECT id, start_time, create_time, status_enum
|
||||
FROM adm_encounter
|
||||
WHERE delete_flag = '0'
|
||||
LIMIT 10;
|
||||
|
||||
-- 2. 测试查询条件
|
||||
SELECT COUNT(*)
|
||||
FROM adm_encounter
|
||||
WHERE delete_flag = '0'
|
||||
AND start_time::DATE = CURRENT_DATE; -- 测试这个条件
|
||||
|
||||
-- 3. 对比其他条件
|
||||
SELECT COUNT(*)
|
||||
FROM adm_encounter
|
||||
WHERE delete_flag = '0'
|
||||
AND create_time::DATE = CURRENT_DATE; -- 对比这个条件
|
||||
```
|
||||
|
||||
### 技巧3:使用数据库工具检查
|
||||
使用数据库管理工具(如 pgAdmin、DBeaver):
|
||||
1. 查看表结构
|
||||
2. 查看数据样本
|
||||
3. 执行测试查询
|
||||
4. 检查字段的 NULL 值比例
|
||||
|
||||
---
|
||||
|
||||
## 预防措施
|
||||
|
||||
### 1. 代码审查检查清单
|
||||
在代码审查时,检查以下内容:
|
||||
|
||||
- [ ] **插入数据时**:是否设置了所有必要的业务字段?
|
||||
- [ ] **查询条件时**:使用的字段是否在插入时被设置?
|
||||
- [ ] **字段命名**:是否遵循命名规范(`create_time` vs `start_time`)?
|
||||
- [ ] **文档注释**:字段的业务含义是否清晰?
|
||||
|
||||
### 2. 单元测试
|
||||
编写单元测试,验证字段设置:
|
||||
|
||||
```java
|
||||
@Test
|
||||
public void testSaveEncounter() {
|
||||
Encounter encounter = new Encounter();
|
||||
encounter.setPatientId(123L);
|
||||
encounter.setBusNo("TEST001");
|
||||
// 检查是否设置了 start_time
|
||||
assertNotNull(encounter.getStartTime(), "start_time 应该被设置");
|
||||
|
||||
Long id = encounterService.saveEncounterByRegister(encounter);
|
||||
|
||||
// 验证数据库中的值
|
||||
Encounter saved = encounterService.getById(id);
|
||||
assertNotNull(saved.getStartTime(), "数据库中的 start_time 不应该为 NULL");
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 数据库约束
|
||||
在数据库层面添加约束,防止 NULL 值:
|
||||
|
||||
```sql
|
||||
-- 如果 start_time 是必填字段,添加 NOT NULL 约束
|
||||
ALTER TABLE adm_encounter
|
||||
ALTER COLUMN start_time SET NOT NULL;
|
||||
|
||||
-- 或者添加默认值
|
||||
ALTER TABLE adm_encounter
|
||||
ALTER COLUMN start_time SET DEFAULT CURRENT_TIMESTAMP;
|
||||
```
|
||||
|
||||
### 4. 代码规范
|
||||
建立代码规范,明确字段使用规则:
|
||||
|
||||
```java
|
||||
/**
|
||||
* 保存就诊信息(门诊挂号)
|
||||
*
|
||||
* 注意:
|
||||
* - create_time: 自动设置(系统字段)
|
||||
* - start_time: 需要手动设置(业务字段,表示就诊开始时间)
|
||||
* - 如果 start_time 为 NULL,查询时使用 create_time 作为挂号时间
|
||||
*/
|
||||
public Long saveEncounterByRegister(Encounter encounter) {
|
||||
// 如果没有设置 start_time,使用当前时间
|
||||
if (encounter.getStartTime() == null) {
|
||||
encounter.setStartTime(new Date());
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常见问题模式
|
||||
|
||||
### 模式1:时间字段混淆
|
||||
- **问题**:使用 `start_time` 查询,但插入时没有设置
|
||||
- **解决**:使用 `create_time` 或确保插入时设置 `start_time`
|
||||
|
||||
### 模式2:状态字段未设置
|
||||
- **问题**:使用 `status_enum` 查询,但插入时使用默认值
|
||||
- **解决**:明确设置状态值
|
||||
|
||||
### 模式3:关联字段未设置
|
||||
- **问题**:使用 `organization_id` 查询,但插入时为 NULL
|
||||
- **解决**:确保插入时设置关联字段
|
||||
|
||||
---
|
||||
|
||||
## 快速排查清单
|
||||
|
||||
遇到"查询不到数据"问题时,按以下顺序检查:
|
||||
|
||||
1. ✅ **数据库中有数据吗?**
|
||||
```sql
|
||||
SELECT COUNT(*) FROM adm_encounter WHERE delete_flag = '0';
|
||||
```
|
||||
|
||||
2. ✅ **查询条件使用的字段有值吗?**
|
||||
```sql
|
||||
SELECT start_time FROM adm_encounter WHERE delete_flag = '0' LIMIT 10;
|
||||
```
|
||||
|
||||
3. ✅ **插入代码设置了该字段吗?**
|
||||
- 查看 Service 的 save/insert 方法
|
||||
- 检查是否调用了 setter 方法
|
||||
|
||||
4. ✅ **字段的业务含义是什么?**
|
||||
- 是系统字段(自动设置)还是业务字段(手动设置)?
|
||||
|
||||
5. ✅ **有类似功能能查到数据吗?**
|
||||
- 对比能查到数据的方法,找出差异
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
**核心原则**:
|
||||
1. **先看数据**:检查字段是否有值
|
||||
2. **再看代码**:检查插入时是否设置
|
||||
3. **对比差异**:找出能查到数据的方法
|
||||
4. **验证修复**:修改后验证数据
|
||||
|
||||
**记住**:查询条件使用的字段,必须在数据插入/更新时被设置!
|
||||
|
||||
|
||||
Reference in New Issue
Block a user