Merge remote-tracking branch 'origin/develop' into guanyu

This commit is contained in:
2026-06-17 12:44:00 +08:00
26 changed files with 1181 additions and 16 deletions

View File

@@ -5,8 +5,8 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import java.util.Map;
public interface IBloodTransfusionAppService {
void apply(BloodTransfusionRecord record);
IPage<BloodTransfusionRecord> page(String patientName, String approvalStatus, String bloodComponent, Integer pageNum, Integer pageSize);
void approve(Long id, String approvalStatus, String approverName, String remark);
IPage<BloodTransfusionRecord> page(String approvalStatus, String bloodComponent, Integer pageNum, Integer pageSize);
void approve(Long id, String approvalStatus, String approverName);
void observe(BloodTransfusionObservation observation);
Map<String, Object> getRecordDetail(Long id);
}

View File

@@ -23,11 +23,8 @@ public class BloodTransfusionAppServiceImpl implements IBloodTransfusionAppServi
recordService.save(record);
}
@Override
public IPage<BloodTransfusionRecord> page(String patientName, String approvalStatus, String bloodComponent, Integer pageNum, Integer pageSize) {
public IPage<BloodTransfusionRecord> page(String approvalStatus, String bloodComponent, Integer pageNum, Integer pageSize) {
LambdaQueryWrapper<BloodTransfusionRecord> w = new LambdaQueryWrapper<>();
if (patientName != null && !patientName.isEmpty()) {
w.like(BloodTransfusionRecord::getDoctorName, patientName);
}
if (approvalStatus != null && !approvalStatus.isEmpty()) {
w.eq(BloodTransfusionRecord::getApprovalStatus, approvalStatus);
}
@@ -38,7 +35,7 @@ public class BloodTransfusionAppServiceImpl implements IBloodTransfusionAppServi
return recordService.page(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageNum, pageSize), w);
}
@Override
public void approve(Long id, String approvalStatus, String approverName, String remark) {
public void approve(Long id, String approvalStatus, String approverName) {
BloodTransfusionRecord record = recordService.getById(id);
if (record == null) {
throw new RuntimeException("输血记录不存在");
@@ -63,8 +60,7 @@ public class BloodTransfusionAppServiceImpl implements IBloodTransfusionAppServi
Map<String, Object> result = new LinkedHashMap<>();
BloodTransfusionRecord record = recordService.getById(id);
if (record == null) {
result.put("error", "记录不存在");
return result;
throw new RuntimeException("输血记录不存在");
}
result.put("record", record);
List<BloodTransfusionObservation> observations = observationService.list(

View File

@@ -22,21 +22,19 @@ public class BloodTransfusionController {
@Operation(summary = "输血申请分页")
@PreAuthorize("@ss.hasPermi('inpatient:bloodtransfusion:list')")
@GetMapping("/page")
public AjaxResult page(@RequestParam(required = false) String patientName,
@RequestParam(required = false) String approvalStatus,
public AjaxResult page(@RequestParam(required = false) String approvalStatus,
@RequestParam(required = false) String bloodComponent,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
return AjaxResult.success(appService.page(patientName, approvalStatus, bloodComponent, pageNum, pageSize));
return AjaxResult.success(appService.page(approvalStatus, bloodComponent, pageNum, pageSize));
}
@Operation(summary = "审批输血")
@PreAuthorize("@ss.hasPermi('inpatient:bloodtransfusion:edit')")
@PutMapping("/approve/{id}")
public AjaxResult approve(@PathVariable Long id,
@RequestParam String approvalStatus,
@RequestParam(required = false) String approverName,
@RequestParam(required = false) String remark) {
appService.approve(id, approvalStatus, approverName, remark);
@RequestParam(required = false) String approverName) {
appService.approve(id, approvalStatus, approverName);
return AjaxResult.success();
}
@Operation(summary = "输血观察记录")

View File

@@ -0,0 +1,17 @@
package com.healthlink.his.web.clinical.appservice;
import com.healthlink.his.clinical.domain.ClinicalPathway;
import com.healthlink.his.clinical.domain.ClinicalPathwayExecution;
import com.healthlink.his.clinical.domain.ClinicalPathwayVariance;
import com.baomidou.mybatisplus.core.metadata.IPage;
import java.util.Map;
public interface IClinicalPathwayAppService {
Map<String, Object> evaluate(Long encounterId);
void admit(ClinicalPathwayExecution execution);
void recordExecution(ClinicalPathwayExecution execution);
void recordVariance(ClinicalPathwayVariance variance);
void discharge(Long executionId);
Map<String, Object> getStatistics(String startDate, String endDate);
IPage<ClinicalPathway> page(String diseaseCode, Integer pageNo, Integer pageSize);
void add(ClinicalPathway pathway);
IPage<Map<String, Object>> getExecutionPage(Long pathwayId, Integer pageNo, Integer pageSize);
}

View File

@@ -0,0 +1,178 @@
package com.healthlink.his.web.clinical.appservice.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.healthlink.his.clinical.domain.ClinicalPathway;
import com.healthlink.his.clinical.domain.ClinicalPathwayExecution;
import com.healthlink.his.clinical.domain.ClinicalPathwayVariance;
import com.healthlink.his.clinical.service.IClinicalPathwayService;
import com.healthlink.his.clinical.service.IClinicalPathwayExecutionService;
import com.healthlink.his.clinical.service.IClinicalPathwayVarianceService;
import com.healthlink.his.web.clinical.appservice.IClinicalPathwayAppService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.*;
@Service
public class ClinicalPathwayAppServiceImpl implements IClinicalPathwayAppService {
@Autowired
private IClinicalPathwayService pathwayService;
@Autowired
private IClinicalPathwayExecutionService executionService;
@Autowired
private IClinicalPathwayVarianceService varianceService;
@Override
public Map<String, Object> evaluate(Long encounterId) {
Map<String, Object> result = new LinkedHashMap<>();
LambdaQueryWrapper<ClinicalPathwayExecution> qw = new LambdaQueryWrapper<>();
qw.eq(ClinicalPathwayExecution::getEncounterId, encounterId)
.eq(ClinicalPathwayExecution::getStatus, "IN_PATH");
ClinicalPathwayExecution active = executionService.getOne(qw);
result.put("hasActivePathway", active != null);
if (active != null) {
result.put("activeExecution", active);
LambdaQueryWrapper<ClinicalPathway> pw = new LambdaQueryWrapper<>();
pw.eq(ClinicalPathway::getId, active.getPathwayId());
result.put("pathway", pathwayService.getOne(pw));
}
LambdaQueryWrapper<ClinicalPathwayExecution> allQw = new LambdaQueryWrapper<>();
allQw.eq(ClinicalPathwayExecution::getEncounterId, encounterId);
result.put("totalExecutions", executionService.count(allQw));
return result;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void admit(ClinicalPathwayExecution execution) {
execution.setStatus("IN_PATH");
execution.setEnterDate(LocalDate.now());
execution.setCreateTime(new Date());
executionService.save(execution);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void recordExecution(ClinicalPathwayExecution execution) {
LambdaQueryWrapper<ClinicalPathwayExecution> qw = new LambdaQueryWrapper<>();
qw.eq(ClinicalPathwayExecution::getEncounterId, execution.getEncounterId())
.eq(ClinicalPathwayExecution::getStatus, "IN_PATH");
ClinicalPathwayExecution existing = executionService.getOne(qw);
if (existing == null) {
throw new RuntimeException("当前无在径执行记录");
}
if (execution.getActualDays() != null) {
existing.setActualDays(execution.getActualDays());
}
if (execution.getActualCost() != null) {
existing.setActualCost(execution.getActualCost());
}
existing.setUpdateTime(new Date());
executionService.updateById(existing);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void recordVariance(ClinicalPathwayVariance variance) {
variance.setCreateTime(new Date());
varianceService.save(variance);
LambdaQueryWrapper<ClinicalPathwayExecution> qw = new LambdaQueryWrapper<>();
qw.eq(ClinicalPathwayExecution::getId, variance.getExecutionId());
ClinicalPathwayExecution exec = executionService.getOne(qw);
if (exec != null) {
exec.setStatus("VARIATION");
exec.setVariationReason(variance.getVarianceReason());
exec.setUpdateTime(new Date());
executionService.updateById(exec);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void discharge(Long executionId) {
ClinicalPathwayExecution exec = executionService.getById(executionId);
if (exec == null) {
throw new RuntimeException("执行记录不存在");
}
exec.setStatus("COMPLETED");
exec.setCompleteDate(LocalDate.now());
if (exec.getEnterDate() != null) {
long days = ChronoUnit.DAYS.between(exec.getEnterDate(), LocalDate.now());
exec.setActualDays((int) days);
}
exec.setUpdateTime(new Date());
executionService.updateById(exec);
}
@Override
public Map<String, Object> getStatistics(String startDate, String endDate) {
Map<String, Object> stats = new LinkedHashMap<>();
stats.put("totalPathways", pathwayService.count());
stats.put("totalExecutions", executionService.count());
LambdaQueryWrapper<ClinicalPathwayExecution> inPathQw = new LambdaQueryWrapper<>();
inPathQw.eq(ClinicalPathwayExecution::getStatus, "IN_PATH");
stats.put("inPathCount", executionService.count(inPathQw));
LambdaQueryWrapper<ClinicalPathwayExecution> completedQw = new LambdaQueryWrapper<>();
completedQw.eq(ClinicalPathwayExecution::getStatus, "COMPLETED");
stats.put("completedCount", executionService.count(completedQw));
LambdaQueryWrapper<ClinicalPathwayExecution> variationQw = new LambdaQueryWrapper<>();
variationQw.eq(ClinicalPathwayExecution::getStatus, "VARIATION");
stats.put("variationCount", executionService.count(variationQw));
LambdaQueryWrapper<ClinicalPathwayExecution> exitQw = new LambdaQueryWrapper<>();
exitQw.eq(ClinicalPathwayExecution::getStatus, "EXIT");
stats.put("exitCount", executionService.count(exitQw));
long total = executionService.count();
long completed = (long) stats.get("completedCount");
stats.put("completionRate", total > 0 ? Math.round(completed * 100.0 / total) : 0);
stats.put("totalVariances", varianceService.count());
return stats;
}
@Override
public IPage<ClinicalPathway> page(String diseaseCode, Integer pageNo, Integer pageSize) {
LambdaQueryWrapper<ClinicalPathway> w = new LambdaQueryWrapper<>();
w.eq(StringUtils.hasText(diseaseCode), ClinicalPathway::getDiseaseCode, diseaseCode)
.eq(ClinicalPathway::getStatus, "ACTIVE");
return pathwayService.page(new Page<>(pageNo, pageSize), w);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void add(ClinicalPathway pathway) {
pathway.setVersion(1);
pathway.setStatus("ACTIVE");
pathway.setCreateTime(new Date());
pathwayService.save(pathway);
}
@Override
public IPage<Map<String, Object>> getExecutionPage(Long pathwayId, Integer pageNo, Integer pageSize) {
LambdaQueryWrapper<ClinicalPathwayExecution> qw = new LambdaQueryWrapper<>();
qw.eq(pathwayId != null, ClinicalPathwayExecution::getPathwayId, pathwayId)
.orderByDesc(ClinicalPathwayExecution::getCreateTime);
Page<ClinicalPathwayExecution> execPage = executionService.page(new Page<>(pageNo, pageSize), qw);
Page<Map<String, Object>> result = new Page<>(execPage.getCurrent(), execPage.getSize(), execPage.getTotal());
List<Map<String, Object>> records = new ArrayList<>();
for (ClinicalPathwayExecution exec : execPage.getRecords()) {
Map<String, Object> row = new LinkedHashMap<>();
row.put("id", exec.getId());
row.put("pathwayId", exec.getPathwayId());
row.put("encounterId", exec.getEncounterId());
row.put("patientId", exec.getPatientId());
row.put("patientName", exec.getPatientName());
row.put("enterDate", exec.getEnterDate());
row.put("expectedDays", exec.getExpectedDays());
row.put("actualDays", exec.getActualDays());
row.put("expectedCost", exec.getExpectedCost());
row.put("actualCost", exec.getActualCost());
row.put("status", exec.getStatus());
row.put("completeDate", exec.getCompleteDate());
row.put("createTime", exec.getCreateTime());
LambdaQueryWrapper<ClinicalPathway> pw = new LambdaQueryWrapper<>();
pw.eq(ClinicalPathway::getId, exec.getPathwayId());
ClinicalPathway pwObj = pathwayService.getOne(pw);
if (pwObj != null) {
row.put("pathwayName", pwObj.getPathwayName());
row.put("diseaseCode", pwObj.getDiseaseCode());
row.put("diseaseName", pwObj.getDiseaseName());
}
records.add(row);
}
result.setRecords(records);
return result;
}
}

View File

@@ -1,17 +1,24 @@
package com.healthlink.his.web.clinical.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.AjaxResult;
import com.core.common.core.domain.R;
import com.healthlink.his.clinical.domain.*;
import com.healthlink.his.clinical.service.*;
import com.healthlink.his.web.clinical.appservice.IClinicalPathwayAppService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;import org.springframework.web.bind.annotation.*;
import java.util.*;
@RestController @RequestMapping("/clinical-pathway") @Slf4j @AllArgsConstructor
@Tag(name = "临床路径管理") @RestController @RequestMapping("/clinical-pathway") @Slf4j @AllArgsConstructor
public class ClinicalPathwayController {
private final IClinicalPathwayService pathwayService;
private final IClinicalPathwayExecutionService executionService;
private final IClinicalPathwayAppService clinicalPathwayAppService;
@PreAuthorize("@ss.hasPermi('inpatient:clinical:list')")
@GetMapping("/page")
public R<?> getPage(@RequestParam(value="diseaseCode",required=false) String diseaseCode,
@RequestParam(value="pageNo",defaultValue="1") Integer pageNo,
@@ -21,13 +28,16 @@ public class ClinicalPathwayController {
.eq(ClinicalPathway::getStatus, "ACTIVE");
return R.ok(pathwayService.page(new Page<>(pageNo, pageSize), w));
}
@PreAuthorize("@ss.hasPermi('inpatient:clinical:edit')")
@PostMapping("/add") @Transactional(rollbackFor=Exception.class)
public R<?> add(@RequestBody ClinicalPathway p) { p.setVersion(1); p.setStatus("ACTIVE"); p.setCreateTime(new Date()); pathwayService.save(p); return R.ok(p); }
@PreAuthorize("@ss.hasPermi('inpatient:clinical:edit')")
@PostMapping("/enter") @Transactional(rollbackFor=Exception.class)
public R<?> enterPathway(@RequestBody ClinicalPathwayExecution e) {
e.setStatus("IN_PATH"); e.setEnterDate(java.time.LocalDate.now()); e.setCreateTime(new Date());
executionService.save(e); return R.ok(e);
}
@PreAuthorize("@ss.hasPermi('inpatient:clinical:edit')")
@PutMapping("/complete/{id}") @Transactional(rollbackFor=Exception.class)
public R<?> completePathway(@PathVariable Long id) {
LambdaQueryWrapper<ClinicalPathwayExecution> qw = new LambdaQueryWrapper<>();
@@ -36,6 +46,7 @@ public class ClinicalPathwayController {
e.setStatus("COMPLETED"); e.setCompleteDate(java.time.LocalDate.now());
executionService.updateById(e); return R.ok();
}
@PreAuthorize("@ss.hasPermi('inpatient:clinical:edit')")
@PutMapping("/vary/{id}") @Transactional(rollbackFor=Exception.class)
public R<?> varyPathway(@PathVariable Long id, @RequestParam("reason") String reason) {
LambdaQueryWrapper<ClinicalPathwayExecution> qw = new LambdaQueryWrapper<>();
@@ -43,6 +54,7 @@ public class ClinicalPathwayController {
ClinicalPathwayExecution e = executionService.getOne(qw); if (e == null) return R.fail("执行记录不存在");
e.setStatus("VARIATION"); e.setVariationReason(reason); executionService.updateById(e); return R.ok();
}
@PreAuthorize("@ss.hasPermi('inpatient:clinical:list')")
@GetMapping("/stats")
public R<?> getStats() {
Map<String, Object> stats = new HashMap<>();
@@ -59,4 +71,53 @@ public class ClinicalPathwayController {
stats.put("completionRate", total > 0 ? Math.round(completed * 100.0 / total) : 0);
return R.ok(stats);
}
@Operation(summary = "入径评估")
@PreAuthorize("@ss.hasPermi('inpatient:clinical:list')")
@GetMapping("/evaluate/{encounterId}")
public AjaxResult evaluate(@PathVariable Long encounterId) {
return AjaxResult.success(clinicalPathwayAppService.evaluate(encounterId));
}
@Operation(summary = "入径")
@PreAuthorize("@ss.hasPermi('inpatient:clinical:edit')")
@PostMapping("/admission")
public AjaxResult admission(@RequestBody ClinicalPathwayExecution execution) {
clinicalPathwayAppService.admit(execution);
return AjaxResult.success();
}
@Operation(summary = "执行记录")
@PreAuthorize("@ss.hasPermi('inpatient:clinical:edit')")
@PostMapping("/execution/record")
public AjaxResult executionRecord(@RequestBody ClinicalPathwayExecution execution) {
clinicalPathwayAppService.recordExecution(execution);
return AjaxResult.success();
}
@Operation(summary = "变异记录")
@PreAuthorize("@ss.hasPermi('inpatient:clinical:edit')")
@PostMapping("/variance/record")
public AjaxResult varianceRecord(@RequestBody ClinicalPathwayVariance variance) {
clinicalPathwayAppService.recordVariance(variance);
return AjaxResult.success();
}
@Operation(summary = "出径")
@PreAuthorize("@ss.hasPermi('inpatient:clinical:edit')")
@PostMapping("/discharge")
public AjaxResult discharge(@RequestParam Long executionId) {
clinicalPathwayAppService.discharge(executionId);
return AjaxResult.success();
}
@Operation(summary = "路径统计")
@PreAuthorize("@ss.hasPermi('inpatient:clinical:list')")
@GetMapping("/statistics")
public AjaxResult statistics(@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate) {
return AjaxResult.success(clinicalPathwayAppService.getStatistics(startDate, endDate));
}
@Operation(summary = "执行记录分页")
@PreAuthorize("@ss.hasPermi('inpatient:clinical:list')")
@GetMapping("/execution/page")
public AjaxResult getExecutionPage(@RequestParam(required = false) Long pathwayId,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
return AjaxResult.success(clinicalPathwayAppService.getExecutionPage(pathwayId, pageNo, pageSize));
}
}

View File

@@ -1,5 +1,6 @@
package com.healthlink.his.web.criticalvalue.appservice;
import com.healthlink.his.criticalvalue.domain.CriticalValue;
import com.healthlink.his.criticalvalue.domain.CriticalValueHandleRecord;
import java.util.List;
import java.util.Map;
public interface ICriticalValueAppService {
@@ -10,4 +11,7 @@ public interface ICriticalValueAppService {
List<CriticalValue> getPendingList();
List<CriticalValue> getOverdueList();
Map<String, Object> getStatistics(String startDate, String endDate);
List<CriticalValue> getPendingHandleList();
void handleCriticalValue(CriticalValueHandleRecord record);
List<CriticalValueHandleRecord> getHandleHistory(Long criticalValueId);
}

View File

@@ -1,14 +1,19 @@
package com.healthlink.his.web.criticalvalue.appservice.impl;
import com.healthlink.his.criticalvalue.domain.CriticalValue;
import com.healthlink.his.criticalvalue.domain.CriticalValueHandleRecord;
import com.healthlink.his.criticalvalue.service.ICriticalValueHandleRecordService;
import com.healthlink.his.criticalvalue.service.ICriticalValueService;
import com.healthlink.his.web.criticalvalue.appservice.ICriticalValueAppService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.core.common.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
@Service
public class CriticalValueAppServiceImpl implements ICriticalValueAppService {
@Autowired private ICriticalValueService criticalValueService;
@Autowired private ICriticalValueHandleRecordService handleRecordService;
@Override
public CriticalValue reportCriticalValue(CriticalValue cv) {
@@ -72,4 +77,42 @@ public class CriticalValueAppServiceImpl implements ICriticalValueAppService {
result.put("confirmRate", total > 0 ? Math.round(closed * 100.0 / total) : 0);
return result;
}
@Override
public List<CriticalValue> getPendingHandleList() {
return criticalValueService.list(new LambdaQueryWrapper<CriticalValue>()
.in(CriticalValue::getStatus, "PENDING", "RECEIVED")
.eq(CriticalValue::getDeleteFlag, "0")
.orderByDesc(CriticalValue::getReportTime));
}
@Override
@Transactional(rollbackFor = Exception.class)
public void handleCriticalValue(CriticalValueHandleRecord record) {
Long userId = SecurityUtils.getUserId();
String username = SecurityUtils.getLoginUser().getUsername();
record.setHandlerId(userId);
record.setHandlerName(username);
record.setHandleTime(new Date());
handleRecordService.save(record);
if ("HANDLE".equals(record.getHandleType())) {
CriticalValue cv = criticalValueService.getById(record.getCriticalValueId());
cv.setHandlerId(record.getHandlerId());
cv.setHandlerName(record.getHandlerName());
cv.setHandleTime(new Date());
cv.setHandleResult(record.getHandleResult());
cv.setStatus("PROCESSING");
criticalValueService.updateById(cv);
} else if ("CONFIRM".equals(record.getHandleType())) {
CriticalValue cv = criticalValueService.getById(record.getCriticalValueId());
cv.setCloseTime(new Date());
cv.setStatus("CLOSED");
criticalValueService.updateById(cv);
}
}
@Override
public List<CriticalValueHandleRecord> getHandleHistory(Long criticalValueId) {
return handleRecordService.list(new LambdaQueryWrapper<CriticalValueHandleRecord>()
.eq(CriticalValueHandleRecord::getCriticalValueId, criticalValueId)
.eq(CriticalValueHandleRecord::getDeleteFlag, "0")
.orderByDesc(CriticalValueHandleRecord::getHandleTime));
}
}

View File

@@ -1,10 +1,12 @@
package com.healthlink.his.web.criticalvalue.controller;
import com.core.common.core.domain.AjaxResult;
import com.healthlink.his.criticalvalue.domain.CriticalValue;
import com.healthlink.his.criticalvalue.domain.CriticalValueHandleRecord;
import com.healthlink.his.web.criticalvalue.appservice.ICriticalValueAppService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@Tag(name = "危急值管理")
@RestController
@@ -42,4 +44,21 @@ public class CriticalValueController {
@GetMapping("/statistics")
public AjaxResult statistics(@RequestParam(required = false) String startDate, @RequestParam(required = false) String endDate) {
return AjaxResult.success(criticalValueAppService.getStatistics(startDate, endDate)); }
@Operation(summary = "待处理危急值列表")
@PreAuthorize("@ss.hasPermi('inpatient:criticalvalue:list')")
@GetMapping("/pending-handle")
public AjaxResult pendingHandle() { return AjaxResult.success(criticalValueAppService.getPendingHandleList()); }
@Operation(summary = "处理危急值")
@PreAuthorize("@ss.hasPermi('inpatient:criticalvalue:edit')")
@PostMapping("/handle")
public AjaxResult handle(@RequestBody CriticalValueHandleRecord record) {
criticalValueAppService.handleCriticalValue(record); return AjaxResult.success(); }
@Operation(summary = "处理历史")
@PreAuthorize("@ss.hasPermi('inpatient:criticalvalue:list')")
@GetMapping("/history")
public AjaxResult history(@RequestParam Long criticalValueId) {
return AjaxResult.success(criticalValueAppService.getHandleHistory(criticalValueId)); }
}

View File

@@ -25,6 +25,7 @@ CREATE TABLE blood_transfusion_record (
adverse_reaction TEXT,
adverse_reaction_type VARCHAR(50),
status VARCHAR(20) DEFAULT 'DRAFT',
tenant_id INTEGER DEFAULT 0,
del_flag CHAR(1) DEFAULT '0',
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
create_by VARCHAR(64),
@@ -45,6 +46,7 @@ CREATE TABLE blood_transfusion_observation (
symptoms TEXT,
nurse_id BIGINT,
nurse_name VARCHAR(50),
tenant_id INTEGER DEFAULT 0,
del_flag CHAR(1) DEFAULT '0',
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
create_by VARCHAR(64)

View File

@@ -0,0 +1,21 @@
CREATE TABLE IF NOT EXISTS clinical_pathway_variance (
id BIGSERIAL PRIMARY KEY,
execution_id BIGINT NOT NULL,
pathway_id BIGINT NOT NULL,
variance_date DATE NOT NULL,
variance_type VARCHAR(20) NOT NULL,
expected_item VARCHAR(200),
actual_item VARCHAR(200),
variance_reason TEXT,
adjustment_action TEXT,
record_by BIGINT,
record_by_name VARCHAR(50),
tenant_id BIGINT DEFAULT 0,
delete_flag CHAR(1) DEFAULT '0',
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
create_by VARCHAR(64),
update_time TIMESTAMP,
update_by VARCHAR(64)
);
CREATE INDEX IF NOT EXISTS idx_cpv_execution ON clinical_pathway_variance(execution_id);
CREATE INDEX IF NOT EXISTS idx_cpv_pathway ON clinical_pathway_variance(pathway_id);

View File

@@ -0,0 +1,2 @@
ALTER TABLE clinical_pathway_variance RENAME COLUMN del_flag TO delete_flag;
ALTER TABLE clinical_pathway_variance ALTER COLUMN tenant_id SET DATA TYPE BIGINT;

View File

@@ -0,0 +1,25 @@
CREATE TABLE critical_value_handle_record (
id BIGSERIAL PRIMARY KEY,
critical_value_id BIGINT NOT NULL,
encounter_id BIGINT NOT NULL,
patient_id BIGINT NOT NULL,
handle_type VARCHAR(20) NOT NULL,
handler_id BIGINT,
handler_name VARCHAR(50),
handle_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
handle_result TEXT,
review_result TEXT,
doctor_id BIGINT,
doctor_name VARCHAR(50),
confirm_time TIMESTAMP,
tenant_id BIGINT DEFAULT 0,
delete_flag CHAR(1) DEFAULT '0',
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
create_by VARCHAR(64),
update_time TIMESTAMP,
update_by VARCHAR(64)
);
COMMENT ON TABLE critical_value_handle_record IS '危急值处理记录';
COMMENT ON COLUMN critical_value_handle_record.handle_type IS '处理类型: HANDLE-处理 REVIEW-复查 CONFIRM-确认';
CREATE INDEX idx_cv_handle_cv_id ON critical_value_handle_record(critical_value_id);
CREATE INDEX idx_cv_handle_patient ON critical_value_handle_record(patient_id);

View File

@@ -0,0 +1,3 @@
ALTER TABLE critical_value_handle_record ALTER COLUMN handler_id DROP NOT NULL;
ALTER TABLE critical_value_handle_record ADD COLUMN update_time TIMESTAMP;
ALTER TABLE critical_value_handle_record ADD COLUMN update_by VARCHAR(64);

View File

@@ -0,0 +1,12 @@
package com.healthlink.his.clinical.domain;
import com.baomidou.mybatisplus.annotation.*;import com.core.common.core.domain.HisBaseEntity;
import lombok.Data;import lombok.EqualsAndHashCode;
import java.time.LocalDate;
@Data @EqualsAndHashCode(callSuper=true) @TableName("clinical_pathway_variance")
public class ClinicalPathwayVariance extends HisBaseEntity {
@TableId(value="id",type=IdType.ASSIGN_ID) private Long id;
private Long executionId; private Long pathwayId; private LocalDate varianceDate;
private String varianceType; private String expectedItem; private String actualItem;
private String varianceReason; private String adjustmentAction;
private Long recordBy; private String recordByName;
}

View File

@@ -0,0 +1,6 @@
package com.healthlink.his.clinical.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.healthlink.his.clinical.domain.ClinicalPathwayVariance;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ClinicalPathwayVarianceMapper extends BaseMapper<ClinicalPathwayVariance> {}

View File

@@ -0,0 +1,4 @@
package com.healthlink.his.clinical.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.healthlink.his.clinical.domain.ClinicalPathwayVariance;
public interface IClinicalPathwayVarianceService extends IService<ClinicalPathwayVariance> {}

View File

@@ -0,0 +1,8 @@
package com.healthlink.his.clinical.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.healthlink.his.clinical.domain.ClinicalPathwayVariance;
import com.healthlink.his.clinical.mapper.ClinicalPathwayVarianceMapper;
import com.healthlink.his.clinical.service.IClinicalPathwayVarianceService;
import org.springframework.stereotype.Service;
@Service
public class ClinicalPathwayVarianceServiceImpl extends ServiceImpl<ClinicalPathwayVarianceMapper, ClinicalPathwayVariance> implements IClinicalPathwayVarianceService {}

View File

@@ -0,0 +1,29 @@
package com.healthlink.his.criticalvalue.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.core.common.core.domain.HisBaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.Date;
@Data
@TableName("critical_value_handle_record")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class CriticalValueHandleRecord extends HisBaseEntity {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private Long criticalValueId;
private Long encounterId;
private Long patientId;
private String handleType;
private Long handlerId;
private String handlerName;
private Date handleTime;
private String handleResult;
private String reviewResult;
private Long doctorId;
private String doctorName;
private Date confirmTime;
}

View File

@@ -0,0 +1,6 @@
package com.healthlink.his.criticalvalue.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.healthlink.his.criticalvalue.domain.CriticalValueHandleRecord;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CriticalValueHandleRecordMapper extends BaseMapper<CriticalValueHandleRecord> {}

View File

@@ -0,0 +1,4 @@
package com.healthlink.his.criticalvalue.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.healthlink.his.criticalvalue.domain.CriticalValueHandleRecord;
public interface ICriticalValueHandleRecordService extends IService<CriticalValueHandleRecord> {}

View File

@@ -0,0 +1,8 @@
package com.healthlink.his.criticalvalue.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.healthlink.his.criticalvalue.domain.CriticalValueHandleRecord;
import com.healthlink.his.criticalvalue.mapper.CriticalValueHandleRecordMapper;
import com.healthlink.his.criticalvalue.service.ICriticalValueHandleRecordService;
import org.springframework.stereotype.Service;
@Service
public class CriticalValueHandleRecordServiceImpl extends ServiceImpl<CriticalValueHandleRecordMapper, CriticalValueHandleRecord> implements ICriticalValueHandleRecordService {}

View File

@@ -0,0 +1,37 @@
import request from '@/utils/request'
export function evaluatePathway(encounterId) {
return request({ url: '/clinical-pathway/evaluate/' + encounterId, method: 'get' })
}
export function admitPathway(data) {
return request({ url: '/clinical-pathway/admission', method: 'post', data: data })
}
export function recordExecution(data) {
return request({ url: '/clinical-pathway/execution/record', method: 'post', data: data })
}
export function recordVariance(data) {
return request({ url: '/clinical-pathway/variance/record', method: 'post', data: data })
}
export function dischargePathway(executionId) {
return request({ url: '/clinical-pathway/discharge', method: 'post', params: { executionId: executionId } })
}
export function getPathwayStatistics(params) {
return request({ url: '/clinical-pathway/statistics', method: 'get', params: params })
}
export function getPathwayPage(params) {
return request({ url: '/clinical-pathway/page', method: 'get', params: params })
}
export function addPathway(data) {
return request({ url: '/clinical-pathway/add', method: 'post', data: data })
}
export function getExecutionPage(params) {
return request({ url: '/clinical-pathway/execution/page', method: 'get', params: params })
}

View File

@@ -4,3 +4,6 @@ export function confirmValue(id, params) { return request({ url: '/api/v1/critic
export function closeValue(id) { return request({ url: '/api/v1/critical-value/close/' + id, method: 'put' }) }
export function getStatistics() { return request({ url: '/api/v1/critical-value/statistics', method: 'get' }) }
export function getOverdueList() { return request({ url: '/api/v1/critical-value/overdue', method: 'get' }) }
export function getPendingHandleList() { return request({ url: '/api/v1/critical-value/pending-handle', method: 'get' }) }
export function handleCriticalValue(data) { return request({ url: '/api/v1/critical-value/handle', method: 'post', data: data }) }
export function getHandleHistory(criticalValueId) { return request({ url: '/api/v1/critical-value/history', method: 'get', params: { criticalValueId } }) }

View File

@@ -0,0 +1,472 @@
<template>
<div class="clinical-pathway-container">
<el-row :gutter="16" class="stats-row">
<el-col :span="4"><el-card shadow="hover"><el-statistic title="路径总数" :value="stats.totalPathways || 0" /></el-card></el-col>
<el-col :span="4"><el-card shadow="hover"><el-statistic title="在径数" :value="stats.inPathCount || 0" /></el-card></el-col>
<el-col :span="4"><el-card shadow="hover"><el-statistic title="完成数" :value="stats.completedCount || 0" /></el-card></el-col>
<el-col :span="4"><el-card shadow="hover"><el-statistic title="变异数" :value="stats.variationCount || 0" /></el-card></el-col>
<el-col :span="4"><el-card shadow="hover"><el-statistic title="退出数" :value="stats.exitCount || 0" /></el-card></el-col>
<el-col :span="4"><el-card shadow="hover"><el-statistic title="完成率(%)" :value="stats.completionRate || 0" /></el-card></el-col>
</el-row>
<el-card>
<template #header>
<div class="card-header">
<span>临床路径管理</span>
<el-button type="primary" size="small" @click="handleAddPathway">新增路径</el-button>
</div>
</template>
<el-form :inline="true" :model="pathwayQuery" class="query-form">
<el-form-item label="疾病编码">
<el-input v-model="pathwayQuery.diseaseCode" placeholder="疾病编码" clearable style="width: 160px" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadPathways">查询</el-button>
<el-button @click="pathwayQuery.diseaseCode = ''; loadPathways()">重置</el-button>
</el-form-item>
</el-form>
<el-table :data="pathwayList" border style="width: 100%">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="diseaseCode" label="疾病编码" width="120" />
<el-table-column prop="diseaseName" label="疾病名称" width="140" />
<el-table-column prop="pathwayName" label="路径名称" min-width="200" show-overflow-tooltip />
<el-table-column prop="departmentName" label="科室" width="120" />
<el-table-column prop="avgDays" label="平均住院日" width="100" align="center" />
<el-table-column prop="version" label="版本" width="70" align="center" />
<el-table-column prop="status" label="状态" width="80" align="center">
<template #default="scope">
<el-tag :type="scope.row.status === 'ACTIVE' ? 'success' : 'info'">{{ scope.row.status === 'ACTIVE' ? '启用' : '停用' }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="240" align="center" fixed="right">
<template #default="scope">
<el-button link type="primary" @click="handleEnterPathway(scope.row)">入径</el-button>
<el-button link type="success" @click="handleViewExecution(scope.row)">执行</el-button>
<el-button link type="warning" @click="handleVariance(scope.row)">变异</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
v-model:current-page="pathwayQuery.pageNo"
v-model:page-size="pathwayQuery.pageSize"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next, jumper"
:total="pathwayTotal"
style="margin-top: 12px; justify-content: flex-end"
@size-change="loadPathways"
@current-change="loadPathways"
/>
</el-card>
<el-dialog v-model="addDialogVisible" title="新增临床路径" width="650px" destroy-on-close :close-on-click-modal="false">
<el-form ref="addFormRef" :model="addForm" :rules="addRules" label-width="110px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="疾病编码" prop="diseaseCode">
<el-input v-model="addForm.diseaseCode" placeholder="请输入疾病编码" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="疾病名称" prop="diseaseName">
<el-input v-model="addForm.diseaseName" placeholder="请输入疾病名称" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="路径名称" prop="pathwayName">
<el-input v-model="addForm.pathwayName" placeholder="请输入路径名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="科室">
<el-input v-model="addForm.departmentName" placeholder="请输入科室" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="平均住院日">
<el-input-number v-model="addForm.avgDays" :min="1" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="路径内容">
<el-input v-model="addForm.pathwayJson" type="textarea" :rows="4" placeholder="请输入路径内容JSON格式" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="addDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="submitAddPathway">确定</el-button>
</template>
</el-dialog>
<el-dialog v-model="enterDialogVisible" title="入径" width="600px" destroy-on-close :close-on-click-modal="false">
<el-form ref="enterFormRef" :model="enterForm" :rules="enterRules" label-width="110px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="就诊ID" prop="encounterId">
<el-input v-model="enterForm.encounterId" placeholder="请输入就诊ID" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="患者ID" prop="patientId">
<el-input v-model="enterForm.patientId" placeholder="请输入患者ID" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="患者姓名">
<el-input v-model="enterForm.patientName" placeholder="请输入患者姓名" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="预计住院日">
<el-input-number v-model="enterForm.expectedDays" :min="1" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="预计费用">
<el-input-number v-model="enterForm.expectedCost" :min="0" :precision="2" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="enterDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="submitEnterPathway">确认入径</el-button>
</template>
</el-dialog>
<el-dialog v-model="executionDialogVisible" title="路径执行记录" width="900px" destroy-on-close :close-on-click-modal="false">
<el-table :data="executionList" border style="width: 100%">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="patientName" label="患者" width="100" />
<el-table-column prop="enterDate" label="入径日期" width="120" />
<el-table-column prop="expectedDays" label="预计天数" width="90" align="center" />
<el-table-column prop="actualDays" label="实际天数" width="90" align="center" />
<el-table-column prop="status" label="状态" width="90" align="center">
<template #default="scope">
<el-tag v-if="scope.row.status === 'IN_PATH'" type="primary">在径</el-tag>
<el-tag v-else-if="scope.row.status === 'COMPLETED'" type="success">完成</el-tag>
<el-tag v-else-if="scope.row.status === 'VARIATION'" type="warning">变异</el-tag>
<el-tag v-else-if="scope.row.status === 'EXIT'" type="danger">退出</el-tag>
<el-tag v-else type="info">{{ scope.row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="completeDate" label="完成日期" width="120" />
<el-table-column prop="createTime" label="创建时间" width="170" />
<el-table-column label="操作" width="160" align="center" fixed="right">
<template #default="scope">
<el-button v-if="scope.row.status === 'IN_PATH'" link type="success" @click="handleDischarge(scope.row)">出径</el-button>
<el-button v-if="scope.row.status === 'IN_PATH'" link type="warning" @click="handleVarianceFromExec(scope.row)">变异</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
v-model:current-page="executionQuery.pageNo"
v-model:page-size="executionQuery.pageSize"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next, jumper"
:total="executionTotal"
style="margin-top: 12px; justify-content: flex-end"
@size-change="loadExecutions"
@current-change="loadExecutions"
/>
<template #footer>
<el-button @click="executionDialogVisible = false">关闭</el-button>
</template>
</el-dialog>
<el-dialog v-model="varianceDialogVisible" title="变异记录" width="650px" destroy-on-close :close-on-click-modal="false">
<el-form ref="varianceFormRef" :model="varianceForm" :rules="varianceRules" label-width="110px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="变异日期" prop="varianceDate">
<el-date-picker v-model="varianceForm.varianceDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="变异类型" prop="varianceType">
<el-select v-model="varianceForm.varianceType" placeholder="请选择" style="width: 100%">
<el-option label="系统变异" value="SYSTEM" />
<el-option label="个人变异" value="PERSONAL" />
<el-option label="疾病变异" value="DISEASE" />
<el-option label="患者变异" value="PATIENT" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="预期项目">
<el-input v-model="varianceForm.expectedItem" placeholder="请输入预期项目" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="实际项目">
<el-input v-model="varianceForm.actualItem" placeholder="请输入实际项目" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="变异原因" prop="varianceReason">
<el-input v-model="varianceForm.varianceReason" type="textarea" :rows="3" placeholder="请输入变异原因" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="调整措施">
<el-input v-model="varianceForm.adjustmentAction" type="textarea" :rows="2" placeholder="请输入调整措施" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="记录人">
<el-input v-model="varianceForm.recordByName" placeholder="请输入记录人姓名" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="varianceDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="submitVariance">保存</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
getPathwayPage, addPathway, admitPathway, recordExecution,
recordVariance, dischargePathway, getPathwayStatistics, getExecutionPage
} from '@/api/clinicalPathway'
const loading = ref(false)
const submitLoading = ref(false)
const stats = ref({})
const pathwayList = ref([])
const pathwayTotal = ref(0)
const pathwayQuery = reactive({ diseaseCode: '', pageNo: 1, pageSize: 10 })
const addDialogVisible = ref(false)
const enterDialogVisible = ref(false)
const executionDialogVisible = ref(false)
const varianceDialogVisible = ref(false)
const addFormRef = ref(null)
const enterFormRef = ref(null)
const varianceFormRef = ref(null)
const addForm = reactive({ diseaseCode: '', diseaseName: '', pathwayName: '', departmentName: '', avgDays: 7, pathwayJson: '' })
const addRules = {
diseaseCode: [{ required: true, message: '请输入疾病编码', trigger: 'blur' }],
diseaseName: [{ required: true, message: '请输入疾病名称', trigger: 'blur' }],
pathwayName: [{ required: true, message: '请输入路径名称', trigger: 'blur' }]
}
const enterForm = reactive({ pathwayId: null, encounterId: '', patientId: '', patientName: '', expectedDays: 7, expectedCost: null })
const enterRules = {
encounterId: [{ required: true, message: '请输入就诊ID', trigger: 'blur' }],
patientId: [{ required: true, message: '请输入患者ID', trigger: 'blur' }]
}
const executionList = ref([])
const executionTotal = ref(0)
const executionQuery = reactive({ pathwayId: null, pageNo: 1, pageSize: 10 })
const varianceForm = reactive({
executionId: null, pathwayId: null, varianceDate: '', varianceType: '',
expectedItem: '', actualItem: '', varianceReason: '', adjustmentAction: '',
recordBy: null, recordByName: ''
})
const varianceRules = {
varianceDate: [{ required: true, message: '请选择变异日期', trigger: 'change' }],
varianceType: [{ required: true, message: '请选择变异类型', trigger: 'change' }],
varianceReason: [{ required: true, message: '请输入变异原因', trigger: 'blur' }]
}
let currentPathway = null
function loadStats() {
getPathwayStatistics().then(res => {
stats.value = res.data || {}
})
}
function loadPathways() {
loading.value = true
getPathwayPage(pathwayQuery).then(res => {
pathwayList.value = res.data?.records || []
pathwayTotal.value = res.data?.total || 0
}).catch(() => {
ElMessage.error('查询失败')
}).finally(() => {
loading.value = false
})
}
function handleAddPathway() {
addForm.diseaseCode = ''
addForm.diseaseName = ''
addForm.pathwayName = ''
addForm.departmentName = ''
addForm.avgDays = 7
addForm.pathwayJson = ''
addDialogVisible.value = true
}
function submitAddPathway() {
addFormRef.value?.validate(valid => {
if (!valid) return
submitLoading.value = true
addPathway({ ...addForm }).then(res => {
if (res.code === 200) {
ElMessage.success('创建成功')
addDialogVisible.value = false
loadPathways()
loadStats()
} else {
ElMessage.error(res.msg || '创建失败')
}
}).catch(() => {
ElMessage.error('创建失败')
}).finally(() => {
submitLoading.value = false
})
})
}
function handleEnterPathway(row) {
currentPathway = row
enterForm.pathwayId = row.id
enterForm.encounterId = ''
enterForm.patientId = ''
enterForm.patientName = ''
enterForm.expectedDays = row.avgDays || 7
enterForm.expectedCost = null
enterDialogVisible.value = true
}
function submitEnterPathway() {
enterFormRef.value?.validate(valid => {
if (!valid) return
submitLoading.value = true
admitPathway({ ...enterForm }).then(res => {
if (res.code === 200) {
ElMessage.success('入径成功')
enterDialogVisible.value = false
loadStats()
} else {
ElMessage.error(res.msg || '入径失败')
}
}).catch(() => {
ElMessage.error('入径失败')
}).finally(() => {
submitLoading.value = false
})
})
}
function handleViewExecution(row) {
currentPathway = row
executionQuery.pathwayId = row.id
executionQuery.pageNo = 1
loadExecutions()
}
function loadExecutions() {
loading.value = true
getExecutionPage(executionQuery).then(res => {
executionList.value = res.data?.records || []
executionTotal.value = res.data?.total || 0
executionDialogVisible.value = true
}).catch(() => {
ElMessage.error('查询执行记录失败')
}).finally(() => {
loading.value = false
})
}
function handleDischarge(row) {
ElMessageBox.confirm('确认出径?出径后将自动计算实际住院天数。', '出径确认', { type: 'success' })
.then(() => {
dischargePathway(row.id).then(res => {
if (res.code === 200) {
ElMessage.success('出径成功')
loadExecutions()
loadStats()
} else {
ElMessage.error(res.msg || '出径失败')
}
}).catch(() => {
ElMessage.error('出径失败')
})
}).catch(() => {})
}
function handleVariance(row) {
currentPathway = row
varianceForm.executionId = null
varianceForm.pathwayId = row.id
varianceForm.varianceDate = ''
varianceForm.varianceType = ''
varianceForm.expectedItem = ''
varianceForm.actualItem = ''
varianceForm.varianceReason = ''
varianceForm.adjustmentAction = ''
varianceForm.recordByName = ''
varianceDialogVisible.value = true
}
function handleVarianceFromExec(row) {
currentPathway = { id: row.pathwayId }
varianceForm.executionId = row.id
varianceForm.pathwayId = row.pathwayId
varianceForm.varianceDate = ''
varianceForm.varianceType = ''
varianceForm.expectedItem = ''
varianceForm.actualItem = ''
varianceForm.varianceReason = ''
varianceForm.adjustmentAction = ''
varianceForm.recordByName = ''
varianceDialogVisible.value = true
}
function submitVariance() {
varianceFormRef.value?.validate(valid => {
if (!valid) return
submitLoading.value = true
recordVariance({ ...varianceForm }).then(res => {
if (res.code === 200) {
ElMessage.success('变异记录已保存')
varianceDialogVisible.value = false
loadStats()
} else {
ElMessage.error(res.msg || '保存失败')
}
}).catch(() => {
ElMessage.error('保存失败')
}).finally(() => {
submitLoading.value = false
})
})
}
onMounted(() => {
loadPathways()
loadStats()
})
</script>
<style scoped>
.clinical-pathway-container {
padding: 12px;
}
.stats-row {
margin-bottom: 16px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.query-form {
margin-bottom: 0;
}
</style>

View File

@@ -0,0 +1,207 @@
<template>
<div class="critical-value-handle-container">
<el-card v-loading="loading">
<template #header>
<div class="card-header">
<span>危急值处理</span>
<el-button type="primary" size="small" @click="handleQuery">刷新</el-button>
</div>
</template>
<el-table :data="tableData" border style="width: 100%">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="patientName" label="患者姓名" width="120" />
<el-table-column prop="itemName" label="危急项目" min-width="160" show-overflow-tooltip />
<el-table-column prop="resultValue" label="结果" width="100" align="center" />
<el-table-column prop="referenceRange" label="参考范围" width="120" />
<el-table-column prop="labDepartment" label="检验科室" width="120" />
<el-table-column prop="reportTime" label="报告时间" width="170" />
<el-table-column prop="status" label="状态" width="100" align="center">
<template #default="scope">
<el-tag v-if="scope.row.status === 'PENDING'" type="danger">待处理</el-tag>
<el-tag v-else-if="scope.row.status === 'RECEIVED'" type="warning">已接收</el-tag>
<el-tag v-else-if="scope.row.status === 'PROCESSING'" type="primary">处理中</el-tag>
<el-tag v-else-if="scope.row.status === 'CLOSED'" type="success">已关闭</el-tag>
<el-tag v-else type="info">{{ scope.row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right">
<template #default="scope">
<el-button v-if="scope.row.status !== 'CLOSED'" link type="primary" @click="handleProcess(scope.row)">处理</el-button>
<el-button link type="info" @click="handleHistory(scope.row)">历史</el-button>
</template>
</el-table-column>
</el-table>
<el-empty v-if="!loading && tableData.length === 0" description="暂无待处理危急值" />
</el-card>
<el-dialog v-model="processDialogVisible" title="危急值处理" width="600px" destroy-on-close :close-on-click-modal="false">
<el-form ref="processFormRef" :model="processForm" :rules="processRules" label-width="100px">
<el-form-item label="患者">
<el-input :model-value="currentRow?.patientName" disabled />
</el-form-item>
<el-form-item label="危急项目">
<el-input :model-value="currentRow?.itemName" disabled />
</el-form-item>
<el-form-item label="结果">
<el-input :model-value="currentRow?.resultValue" disabled />
</el-form-item>
<el-form-item label="处理类型" prop="handleType">
<el-select v-model="processForm.handleType" placeholder="请选择" style="width: 100%">
<el-option label="处理" value="HANDLE" />
<el-option label="复查" value="REVIEW" />
<el-option label="确认关闭" value="CONFIRM" />
</el-select>
</el-form-item>
<el-form-item label="处理人" prop="handlerName">
<el-input v-model="processForm.handlerName" placeholder="请输入处理人姓名" />
</el-form-item>
<el-form-item v-if="processForm.handleType === 'HANDLE'" label="处理结果" prop="handleResult">
<el-input v-model="processForm.handleResult" type="textarea" :rows="3" placeholder="请输入处理结果" />
</el-form-item>
<el-form-item v-if="processForm.handleType === 'REVIEW'" label="复查结果" prop="reviewResult">
<el-input v-model="processForm.reviewResult" type="textarea" :rows="3" placeholder="请输入复查结果" />
</el-form-item>
<el-form-item v-if="processForm.handleType === 'CONFIRM'" label="审核医生" prop="doctorName">
<el-input v-model="processForm.doctorName" placeholder="请输入审核医生姓名" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="processDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="submitProcess">确认</el-button>
</template>
</el-dialog>
<el-dialog v-model="historyDialogVisible" title="处理历史" width="700px" destroy-on-close>
<el-table :data="historyData" border style="width: 100%" size="small">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="handleType" label="类型" width="100" align="center">
<template #default="scope">
<el-tag v-if="scope.row.handleType === 'HANDLE'" type="primary">处理</el-tag>
<el-tag v-else-if="scope.row.handleType === 'REVIEW'" type="warning">复查</el-tag>
<el-tag v-else-if="scope.row.handleType === 'CONFIRM'" type="success">确认</el-tag>
<el-tag v-else type="info">{{ scope.row.handleType }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="handlerName" label="处理人" width="100" />
<el-table-column prop="handleResult" label="处理结果" min-width="150" show-overflow-tooltip />
<el-table-column prop="reviewResult" label="复查结果" min-width="150" show-overflow-tooltip />
<el-table-column prop="doctorName" label="审核医生" width="100" />
<el-table-column prop="handleTime" label="处理时间" width="170" />
</el-table>
<el-empty v-if="historyData.length === 0" description="暂无处理记录" />
<template #footer>
<el-button @click="historyDialogVisible = false">关闭</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { getPendingHandleList, handleCriticalValue, getHandleHistory } from '@/api/criticalvalue'
const loading = ref(false)
const submitLoading = ref(false)
const tableData = ref([])
const processDialogVisible = ref(false)
const historyDialogVisible = ref(false)
const processFormRef = ref(null)
const currentRow = ref(null)
const processForm = reactive({
handleType: 'HANDLE',
handlerName: '',
handleResult: '',
reviewResult: '',
doctorName: ''
})
const processRules = {
handleType: [{ required: true, message: '请选择处理类型', trigger: 'change' }],
handlerName: [{ required: true, message: '请输入处理人', trigger: 'blur' }],
handleResult: [{ required: true, message: '请输入处理结果', trigger: 'blur' }],
reviewResult: [{ required: true, message: '请输入复查结果', trigger: 'blur' }],
doctorName: [{ required: true, message: '请输入审核医生', trigger: 'blur' }]
}
const historyData = ref([])
function handleQuery() {
loading.value = true
getPendingHandleList().then(res => {
tableData.value = res.data || []
}).catch(() => {
ElMessage.error('查询失败')
}).finally(() => {
loading.value = false
})
}
function handleProcess(row) {
currentRow.value = row
processForm.handleType = 'HANDLE'
processForm.handlerName = ''
processForm.handleResult = ''
processForm.reviewResult = ''
processForm.doctorName = ''
processDialogVisible.value = true
}
function submitProcess() {
processFormRef.value?.validate(valid => {
if (!valid) return
submitLoading.value = true
const payload = {
criticalValueId: currentRow.value.id,
encounterId: currentRow.value.encounterId,
patientId: currentRow.value.patientId,
handleType: processForm.handleType,
handlerId: null,
handlerName: processForm.handlerName,
handleResult: processForm.handleResult,
reviewResult: processForm.reviewResult,
doctorId: null,
doctorName: processForm.doctorName
}
handleCriticalValue(payload).then(res => {
if (res.code === 200) {
ElMessage.success('处理成功')
processDialogVisible.value = false
handleQuery()
} else {
ElMessage.error(res.msg || '处理失败')
}
}).catch(() => {
ElMessage.error('处理失败')
}).finally(() => {
submitLoading.value = false
})
})
}
function handleHistory(row) {
getHandleHistory(row.id).then(res => {
historyData.value = res.data || []
historyDialogVisible.value = true
}).catch(() => {
ElMessage.error('查询历史失败')
})
}
onMounted(() => {
handleQuery()
})
</script>
<style scoped>
.critical-value-handle-container {
padding: 12px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>