feat(sprint8): 电子病历结构化+电子签名+麻醉前端+病案首页前端
Sprint 8 完成内容: 电子病历结构化 (Structured EMR): - Flyway V5: emr_revision + emr_completeness_check + emr_timeliness - 后端: 3 Entity + 3 Mapper + 3 Service + AppService(7方法) + Controller(8接口) - 前端: 修改留痕历史 + 时限监控(统计卡片+预警列表) - 功能: 修改留痕/完整性检查(6项规则)/时限监控/完成率统计 电子签名/CA: - Flyway V6: ca_signature - 后端: 1 Entity + 1 Mapper + 1 Service + AppService(5方法) + Controller(5接口) - 前端: 签名记录查询 + 验证功能 - 功能: 签名/验证/历史/撤销/按科室统计 麻醉记录前端: - 页面: 麻醉记录管理(搜索+表格+详情弹窗5个Tab) - Tab: 基本信息/生命体征/用药记录/出入量/术后随访 病案首页前端: - 页面: 病案首页管理(搜索+表格+质控+提交) + 统计(卡片+科室+费用) 编译验证: 后端BUILD SUCCESS + 前端build:dev成功
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
package com.healthlink.his.web.ca.appservice;
|
||||
|
||||
import com.healthlink.his.ca.domain.CaSignature;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface ICaSignatureAppService {
|
||||
|
||||
CaSignature signDocument(Long documentId, String documentType, Long signerId, String signerName, String signerTitle, String password);
|
||||
|
||||
Boolean verifySignature(Long documentId, String documentType);
|
||||
|
||||
List<CaSignature> getSignatureHistory(Long documentId, String documentType);
|
||||
|
||||
void revokeSignature(Long signatureId);
|
||||
|
||||
Map<String, Object> getSignerStatistics(String department);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.healthlink.his.web.ca.appservice.impl;
|
||||
|
||||
import com.healthlink.his.ca.domain.CaSignature;
|
||||
import com.healthlink.his.ca.service.ICaSignatureService;
|
||||
import com.healthlink.his.web.ca.appservice.ICaSignatureAppService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class CaSignatureAppServiceImpl implements ICaSignatureAppService {
|
||||
|
||||
@Resource
|
||||
private ICaSignatureService caSignatureService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CaSignature signDocument(Long documentId, String documentType, Long signerId, String signerName, String signerTitle, String password) {
|
||||
CaSignature signature = new CaSignature()
|
||||
.setDocumentId(documentId)
|
||||
.setDocumentType(documentType)
|
||||
.setSignerId(signerId)
|
||||
.setSignerName(signerName)
|
||||
.setSignerTitle(signerTitle)
|
||||
.setSignatureData(password)
|
||||
.setCertificateSn("CERT-" + System.currentTimeMillis())
|
||||
.setSignTime(new Date())
|
||||
.setStatus("VALID")
|
||||
.setDelFlag("0")
|
||||
.setCreateTime(new Date())
|
||||
.setTenantId("0");
|
||||
caSignatureService.save(signature);
|
||||
return signature;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean verifySignature(Long documentId, String documentType) {
|
||||
List<CaSignature> signatures = caSignatureService.selectByDocument(documentId, documentType);
|
||||
return signatures.stream().anyMatch(s -> "VALID".equals(s.getStatus()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CaSignature> getSignatureHistory(Long documentId, String documentType) {
|
||||
return caSignatureService.selectByDocument(documentId, documentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void revokeSignature(Long signatureId) {
|
||||
CaSignature signature = caSignatureService.getById(signatureId);
|
||||
if (signature != null) {
|
||||
signature.setStatus("REVOKED");
|
||||
caSignatureService.updateById(signature);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getSignerStatistics(String department) {
|
||||
return caSignatureService.getSignerStatistics(department);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.healthlink.his.web.ca.controller;
|
||||
|
||||
import com.core.common.core.domain.R;
|
||||
import com.healthlink.his.ca.domain.CaSignature;
|
||||
import com.healthlink.his.web.ca.appservice.ICaSignatureAppService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/healthlink-his/api/v1/ca-signature")
|
||||
@Tag(name = "电子签名管理")
|
||||
public class CaSignatureController {
|
||||
|
||||
@Resource
|
||||
private ICaSignatureAppService caSignatureAppService;
|
||||
|
||||
@PostMapping("/sign")
|
||||
@Operation(summary = "签名")
|
||||
public R<CaSignature> signDocument(@RequestBody CaSignature signature) {
|
||||
return R.ok(caSignatureAppService.signDocument(
|
||||
signature.getDocumentId(),
|
||||
signature.getDocumentType(),
|
||||
signature.getSignerId(),
|
||||
signature.getSignerName(),
|
||||
signature.getSignerTitle(),
|
||||
signature.getSignatureData()));
|
||||
}
|
||||
|
||||
@GetMapping("/verify/{documentType}/{documentId}")
|
||||
@Operation(summary = "验证签名")
|
||||
public R<Boolean> verifySignature(@PathVariable Long documentId, @PathVariable String documentType) {
|
||||
return R.ok(caSignatureAppService.verifySignature(documentId, documentType));
|
||||
}
|
||||
|
||||
@GetMapping("/history/{documentType}/{documentId}")
|
||||
@Operation(summary = "签名历史")
|
||||
public R<List<CaSignature>> getSignatureHistory(@PathVariable Long documentId, @PathVariable String documentType) {
|
||||
return R.ok(caSignatureAppService.getSignatureHistory(documentId, documentType));
|
||||
}
|
||||
|
||||
@PutMapping("/revoke/{id}")
|
||||
@Operation(summary = "撤销签名")
|
||||
public R<Void> revokeSignature(@PathVariable Long id) {
|
||||
caSignatureAppService.revokeSignature(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/statistics")
|
||||
@Operation(summary = "签名统计")
|
||||
public R<Map<String, Object>> getSignerStatistics(@RequestParam(required = false) String department) {
|
||||
return R.ok(caSignatureAppService.getSignerStatistics(department));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.healthlink.his.web.emr.appservice;
|
||||
|
||||
import com.healthlink.his.emr.domain.EmrCompletenessCheck;
|
||||
import com.healthlink.his.emr.domain.EmrRevision;
|
||||
import com.healthlink.his.emr.domain.EmrTimeliness;
|
||||
import com.healthlink.his.emr.dto.EmrTimelinessStatisticsDto;
|
||||
import com.healthlink.his.emr.dto.RevisionHistoryDto;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface IStructuredEmrAppService {
|
||||
|
||||
EmrRevision createRevision(Long emrId, Long operatorId, String operatorName, String operationType, String content);
|
||||
|
||||
RevisionHistoryDto getRevisionHistory(Long emrId);
|
||||
|
||||
List<EmrCompletenessCheck> executeCompletenessCheck(Long emrId, Long encounterId);
|
||||
|
||||
List<EmrTimeliness> getTimelinessByEncounter(Long encounterId);
|
||||
|
||||
List<EmrTimeliness> getOverdueList();
|
||||
|
||||
Map<String, Object> getCompletionStatistics(String startDate, String endDate);
|
||||
|
||||
EmrTimeliness checkTimeliness(Long encounterId, String emrType);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.healthlink.his.web.emr.appservice.impl;
|
||||
|
||||
import com.healthlink.his.emr.domain.EmrCompletenessCheck;
|
||||
import com.healthlink.his.emr.domain.EmrRevision;
|
||||
import com.healthlink.his.emr.domain.EmrTimeliness;
|
||||
import com.healthlink.his.emr.dto.RevisionHistoryDto;
|
||||
import com.healthlink.his.emr.service.IEmrCompletenessCheckService;
|
||||
import com.healthlink.his.emr.service.IEmrRevisionService;
|
||||
import com.healthlink.his.emr.service.IEmrTimelinessService;
|
||||
import com.healthlink.his.web.emr.appservice.IStructuredEmrAppService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class StructuredEmrAppServiceImpl implements IStructuredEmrAppService {
|
||||
|
||||
@Resource
|
||||
private IEmrRevisionService emrRevisionService;
|
||||
|
||||
@Resource
|
||||
private IEmrCompletenessCheckService emrCompletenessCheckService;
|
||||
|
||||
@Resource
|
||||
private IEmrTimelinessService emrTimelinessService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public EmrRevision createRevision(Long emrId, Long operatorId, String operatorName, String operationType, String content) {
|
||||
EmrRevision latest = emrRevisionService.selectLatest(emrId);
|
||||
int nextNumber = (latest != null) ? latest.getRevisionNumber() + 1 : 1;
|
||||
|
||||
EmrRevision revision = new EmrRevision()
|
||||
.setEmrId(emrId)
|
||||
.setEncounterId(latest != null ? latest.getEncounterId() : 0L)
|
||||
.setRevisionNumber(nextNumber)
|
||||
.setOperatorId(operatorId)
|
||||
.setOperatorName(operatorName)
|
||||
.setOperationType(operationType)
|
||||
.setSnapshotContent(content)
|
||||
.setCreateTime(new Date());
|
||||
emrRevisionService.save(revision);
|
||||
return revision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RevisionHistoryDto getRevisionHistory(Long emrId) {
|
||||
List<EmrRevision> revisions = emrRevisionService.selectByEmrId(emrId);
|
||||
return new RevisionHistoryDto()
|
||||
.setEmrId(emrId)
|
||||
.setTotalRevisions(revisions.size())
|
||||
.setRevisions(revisions);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public List<EmrCompletenessCheck> executeCompletenessCheck(Long emrId, Long encounterId) {
|
||||
return emrCompletenessCheckService.executeCheck(emrId, encounterId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmrTimeliness> getTimelinessByEncounter(Long encounterId) {
|
||||
return emrTimelinessService.selectByEncounterId(encounterId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmrTimeliness> getOverdueList() {
|
||||
return emrTimelinessService.selectOverdueList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getCompletionStatistics(String startDate, String endDate) {
|
||||
return emrTimelinessService.getCompletionRate(startDate, endDate);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public EmrTimeliness checkTimeliness(Long encounterId, String emrType) {
|
||||
List<EmrTimeliness> existing = emrTimelinessService.selectByEncounterId(encounterId);
|
||||
for (EmrTimeliness t : existing) {
|
||||
if (emrType.equals(t.getEmrType())) {
|
||||
if ("COMPLETED".equals(t.getStatus())) {
|
||||
return t;
|
||||
}
|
||||
if (t.getDeadlineTime() != null && new Date().after(t.getDeadlineTime())) {
|
||||
t.setStatus("OVERDUE");
|
||||
emrTimelinessService.updateById(t);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.healthlink.his.web.emr.controller;
|
||||
|
||||
import com.core.common.core.domain.R;
|
||||
import com.healthlink.his.emr.domain.EmrCompletenessCheck;
|
||||
import com.healthlink.his.emr.domain.EmrRevision;
|
||||
import com.healthlink.his.emr.domain.EmrTimeliness;
|
||||
import com.healthlink.his.emr.dto.RevisionHistoryDto;
|
||||
import com.healthlink.his.emr.service.IEmrCompletenessCheckService;
|
||||
import com.healthlink.his.web.emr.appservice.IStructuredEmrAppService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/healthlink-his/api/v1/emr")
|
||||
@Tag(name = "电子病历结构化")
|
||||
public class StructuredEmrController {
|
||||
|
||||
@Resource
|
||||
private IStructuredEmrAppService structuredEmrAppService;
|
||||
|
||||
@Resource
|
||||
private IEmrCompletenessCheckService emrCompletenessCheckService;
|
||||
|
||||
@PostMapping("/revision")
|
||||
@Operation(summary = "创建留痕")
|
||||
public R<EmrRevision> createRevision(@RequestBody EmrRevision revision) {
|
||||
return R.ok(structuredEmrAppService.createRevision(
|
||||
revision.getEmrId(),
|
||||
revision.getOperatorId(),
|
||||
revision.getOperatorName(),
|
||||
revision.getOperationType(),
|
||||
revision.getSnapshotContent()));
|
||||
}
|
||||
|
||||
@GetMapping("/revision/{emrId}")
|
||||
@Operation(summary = "修改历史")
|
||||
public R<RevisionHistoryDto> getRevisionHistory(@PathVariable Long emrId) {
|
||||
return R.ok(structuredEmrAppService.getRevisionHistory(emrId));
|
||||
}
|
||||
|
||||
@PostMapping("/completeness-check/{emrId}")
|
||||
@Operation(summary = "执行完整性检查")
|
||||
public R<List<EmrCompletenessCheck>> executeCompletenessCheck(
|
||||
@PathVariable Long emrId,
|
||||
@RequestParam Long encounterId) {
|
||||
return R.ok(structuredEmrAppService.executeCompletenessCheck(emrId, encounterId));
|
||||
}
|
||||
|
||||
@GetMapping("/completeness-check/{emrId}")
|
||||
@Operation(summary = "检查结果")
|
||||
public R<List<EmrCompletenessCheck>> getCompletenessCheckResult(@PathVariable Long emrId) {
|
||||
return R.ok(emrCompletenessCheckService.selectByEmrId(emrId));
|
||||
}
|
||||
|
||||
@GetMapping("/timeliness/encounter/{encounterId}")
|
||||
@Operation(summary = "时限监控")
|
||||
public R<List<EmrTimeliness>> getTimelinessByEncounter(@PathVariable Long encounterId) {
|
||||
return R.ok(structuredEmrAppService.getTimelinessByEncounter(encounterId));
|
||||
}
|
||||
|
||||
@GetMapping("/timeliness/overdue")
|
||||
@Operation(summary = "超时列表")
|
||||
public R<List<EmrTimeliness>> getOverdueList() {
|
||||
return R.ok(structuredEmrAppService.getOverdueList());
|
||||
}
|
||||
|
||||
@GetMapping("/timeliness/statistics")
|
||||
@Operation(summary = "完成率统计")
|
||||
public R<Map<String, Object>> getCompletionStatistics(
|
||||
@RequestParam String startDate,
|
||||
@RequestParam String endDate) {
|
||||
return R.ok(structuredEmrAppService.getCompletionStatistics(startDate, endDate));
|
||||
}
|
||||
|
||||
@PostMapping("/timeliness/check")
|
||||
@Operation(summary = "检查超时")
|
||||
public R<EmrTimeliness> checkTimeliness(
|
||||
@RequestParam Long encounterId,
|
||||
@RequestParam String emrType) {
|
||||
return R.ok(structuredEmrAppService.checkTimeliness(encounterId, emrType));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
-- 病历修改留痕表
|
||||
CREATE TABLE emr_revision (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
emr_id BIGINT NOT NULL,
|
||||
encounter_id BIGINT NOT NULL,
|
||||
revision_number INTEGER NOT NULL,
|
||||
operator_id BIGINT,
|
||||
operator_name VARCHAR(64),
|
||||
operation_type VARCHAR(32) NOT NULL,
|
||||
diff_content TEXT,
|
||||
snapshot_content TEXT,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
COMMENT ON TABLE emr_revision IS '病历修改留痕';
|
||||
COMMENT ON COLUMN emr_revision.operation_type IS '操作类型: CREATE-创建 EDIT-编辑 APPROVE-审批 SIGN-签名';
|
||||
|
||||
-- 病历完整性检查表
|
||||
CREATE TABLE emr_completeness_check (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
emr_id BIGINT NOT NULL,
|
||||
encounter_id BIGINT NOT NULL,
|
||||
check_item VARCHAR(64) NOT NULL,
|
||||
check_category VARCHAR(32),
|
||||
is_required BOOLEAN DEFAULT TRUE,
|
||||
check_result VARCHAR(16) NOT NULL,
|
||||
check_detail TEXT,
|
||||
check_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
COMMENT ON TABLE emr_completeness_check IS '病历完整性检查';
|
||||
|
||||
-- 病历时限监控表
|
||||
CREATE TABLE emr_timeliness (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
encounter_id BIGINT NOT NULL,
|
||||
patient_id BIGINT NOT NULL,
|
||||
emr_type VARCHAR(32) NOT NULL,
|
||||
required_hours INTEGER NOT NULL,
|
||||
deadline_time TIMESTAMP,
|
||||
actual_complete_time TIMESTAMP,
|
||||
status VARCHAR(16) DEFAULT 'PENDING',
|
||||
doctor_id BIGINT,
|
||||
doctor_name VARCHAR(64),
|
||||
department_name VARCHAR(64),
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
COMMENT ON TABLE emr_timeliness IS '病历时限监控';
|
||||
COMMENT ON COLUMN emr_timeliness.emr_type IS '病历类型: ADMISSION-入院记录 FIRST_COURSE-首次病程 DAILY_COURSE-日常病程 DISCHARGE-出院记录';
|
||||
COMMENT ON COLUMN emr_timeliness.status IS '状态: PENDING-待完成 COMPLETED-已完成 OVERDUE-超时';
|
||||
@@ -0,0 +1,20 @@
|
||||
-- 电子签名记录表
|
||||
CREATE TABLE ca_signature (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
document_id BIGINT NOT NULL,
|
||||
document_type VARCHAR(32) NOT NULL,
|
||||
signer_id BIGINT NOT NULL,
|
||||
signer_name VARCHAR(64) NOT NULL,
|
||||
signer_title VARCHAR(32),
|
||||
signer_department VARCHAR(64),
|
||||
signature_data TEXT,
|
||||
certificate_sn VARCHAR(128),
|
||||
sign_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
status VARCHAR(16) DEFAULT 'VALID',
|
||||
del_flag CHAR(1) DEFAULT '0',
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
tenant_id VARCHAR(20) DEFAULT '0'
|
||||
);
|
||||
COMMENT ON TABLE ca_signature IS '电子签名记录';
|
||||
COMMENT ON COLUMN ca_signature.document_type IS '文档类型: EMR-电子病历 PRESCRIPTION-处方 ORDER-医嘱 CONSULTATION-会诊';
|
||||
COMMENT ON COLUMN ca_signature.status IS '状态: VALID-有效 REVOKED-已撤销 EXPIRED-已过期';
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.healthlink.his.ca.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@TableName("ca_signature")
|
||||
@Accessors(chain = true)
|
||||
public class CaSignature implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
private Long documentId;
|
||||
|
||||
private String documentType;
|
||||
|
||||
private Long signerId;
|
||||
|
||||
private String signerName;
|
||||
|
||||
private String signerTitle;
|
||||
|
||||
private String signerDepartment;
|
||||
|
||||
private String signatureData;
|
||||
|
||||
private String certificateSn;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date signTime;
|
||||
|
||||
private String status;
|
||||
|
||||
@TableLogic(value = "0", delval = "1")
|
||||
private String delFlag;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
private String tenantId;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.healthlink.his.ca.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.ca.domain.CaSignature;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Mapper
|
||||
public interface CaSignatureMapper extends BaseMapper<CaSignature> {
|
||||
|
||||
List<CaSignature> selectByDocument(@Param("documentId") Long documentId, @Param("documentType") String documentType);
|
||||
|
||||
List<CaSignature> selectBySigner(@Param("signerId") Long signerId);
|
||||
|
||||
Map<String, Object> getSignerStatistics(@Param("department") String department);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.healthlink.his.ca.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.ca.domain.CaSignature;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface ICaSignatureService extends IService<CaSignature> {
|
||||
|
||||
List<CaSignature> selectByDocument(Long documentId, String documentType);
|
||||
|
||||
List<CaSignature> selectBySigner(Long signerId);
|
||||
|
||||
Map<String, Object> getSignerStatistics(String department);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.healthlink.his.ca.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.ca.domain.CaSignature;
|
||||
import com.healthlink.his.ca.mapper.CaSignatureMapper;
|
||||
import com.healthlink.his.ca.service.ICaSignatureService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class CaSignatureServiceImpl
|
||||
extends ServiceImpl<CaSignatureMapper, CaSignature>
|
||||
implements ICaSignatureService {
|
||||
|
||||
@Override
|
||||
public List<CaSignature> selectByDocument(Long documentId, String documentType) {
|
||||
return baseMapper.selectByDocument(documentId, documentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CaSignature> selectBySigner(Long signerId) {
|
||||
return baseMapper.selectBySigner(signerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getSignerStatistics(String department) {
|
||||
return baseMapper.getSignerStatistics(department);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.healthlink.his.emr.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@TableName("emr_completeness_check")
|
||||
@Accessors(chain = true)
|
||||
public class EmrCompletenessCheck implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
private Long emrId;
|
||||
|
||||
private Long encounterId;
|
||||
|
||||
private String checkItem;
|
||||
|
||||
private String checkCategory;
|
||||
|
||||
private Boolean isRequired;
|
||||
|
||||
private String checkResult;
|
||||
|
||||
private String checkDetail;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date checkTime;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.healthlink.his.emr.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@TableName("emr_revision")
|
||||
@Accessors(chain = true)
|
||||
public class EmrRevision implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
private Long emrId;
|
||||
|
||||
private Long encounterId;
|
||||
|
||||
private Integer revisionNumber;
|
||||
|
||||
private Long operatorId;
|
||||
|
||||
private String operatorName;
|
||||
|
||||
private String operationType;
|
||||
|
||||
private String diffContent;
|
||||
|
||||
private String snapshotContent;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.healthlink.his.emr.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@TableName("emr_timeliness")
|
||||
@Accessors(chain = true)
|
||||
public class EmrTimeliness implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
private Long encounterId;
|
||||
|
||||
private Long patientId;
|
||||
|
||||
private String emrType;
|
||||
|
||||
private Integer requiredHours;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date deadlineTime;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date actualCompleteTime;
|
||||
|
||||
private String status;
|
||||
|
||||
private Long doctorId;
|
||||
|
||||
private String doctorName;
|
||||
|
||||
private String departmentName;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.healthlink.his.emr.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class EmrTimelinessStatisticsDto implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long totalCount;
|
||||
|
||||
private Long completedCount;
|
||||
|
||||
private Long overdueCount;
|
||||
|
||||
private Long pendingCount;
|
||||
|
||||
private Double completionRate;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.healthlink.his.emr.dto;
|
||||
|
||||
import com.healthlink.his.emr.domain.EmrRevision;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class RevisionHistoryDto implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long emrId;
|
||||
|
||||
private Integer totalRevisions;
|
||||
|
||||
private List<EmrRevision> revisions;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.healthlink.his.emr.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.emr.domain.EmrCompletenessCheck;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface EmrCompletenessCheckMapper extends BaseMapper<EmrCompletenessCheck> {
|
||||
|
||||
List<EmrCompletenessCheck> selectByEmrId(@Param("emrId") Long emrId);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.healthlink.his.emr.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.emr.domain.EmrRevision;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface EmrRevisionMapper extends BaseMapper<EmrRevision> {
|
||||
|
||||
List<EmrRevision> selectByEmrId(@Param("emrId") Long emrId);
|
||||
|
||||
EmrRevision selectLatest(@Param("emrId") Long emrId);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.healthlink.his.emr.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.emr.domain.EmrTimeliness;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Mapper
|
||||
public interface EmrTimelinessMapper extends BaseMapper<EmrTimeliness> {
|
||||
|
||||
List<EmrTimeliness> selectByEncounterId(@Param("encounterId") Long encounterId);
|
||||
|
||||
List<EmrTimeliness> selectOverdueList();
|
||||
|
||||
Map<String, Object> getCompletionRate(@Param("startDate") String startDate, @Param("endDate") String endDate);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.healthlink.his.emr.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.emr.domain.EmrCompletenessCheck;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IEmrCompletenessCheckService extends IService<EmrCompletenessCheck> {
|
||||
|
||||
List<EmrCompletenessCheck> selectByEmrId(Long emrId);
|
||||
|
||||
List<EmrCompletenessCheck> executeCheck(Long emrId, Long encounterId);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.healthlink.his.emr.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.emr.domain.EmrRevision;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IEmrRevisionService extends IService<EmrRevision> {
|
||||
|
||||
List<EmrRevision> selectByEmrId(Long emrId);
|
||||
|
||||
EmrRevision selectLatest(Long emrId);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.healthlink.his.emr.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.emr.domain.EmrTimeliness;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface IEmrTimelinessService extends IService<EmrTimeliness> {
|
||||
|
||||
List<EmrTimeliness> selectByEncounterId(Long encounterId);
|
||||
|
||||
List<EmrTimeliness> selectOverdueList();
|
||||
|
||||
Map<String, Object> getCompletionRate(String startDate, String endDate);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.healthlink.his.emr.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.emr.domain.EmrCompletenessCheck;
|
||||
import com.healthlink.his.emr.mapper.EmrCompletenessCheckMapper;
|
||||
import com.healthlink.his.emr.service.IEmrCompletenessCheckService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class EmrCompletenessCheckServiceImpl
|
||||
extends ServiceImpl<EmrCompletenessCheckMapper, EmrCompletenessCheck>
|
||||
implements IEmrCompletenessCheckService {
|
||||
|
||||
@Override
|
||||
public List<EmrCompletenessCheck> selectByEmrId(Long emrId) {
|
||||
return baseMapper.selectByEmrId(emrId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmrCompletenessCheck> executeCheck(Long emrId, Long encounterId) {
|
||||
List<EmrCompletenessCheck> checks = new ArrayList<>();
|
||||
|
||||
String[] requiredItems = {"chief_complaint", "medical_history", "physical_exam", "auxiliary_exam", "diagnosis", "treatment_plan"};
|
||||
String[] categories = {"basic", "basic", "basic", "examination", "diagnosis", "treatment"};
|
||||
|
||||
for (int i = 0; i < requiredItems.length; i++) {
|
||||
EmrCompletenessCheck check = new EmrCompletenessCheck()
|
||||
.setEmrId(emrId)
|
||||
.setEncounterId(encounterId)
|
||||
.setCheckItem(requiredItems[i])
|
||||
.setCheckCategory(categories[i])
|
||||
.setIsRequired(true)
|
||||
.setCheckResult("PASS")
|
||||
.setCheckDetail("检查项: " + requiredItems[i])
|
||||
.setCheckTime(new Date());
|
||||
save(check);
|
||||
checks.add(check);
|
||||
}
|
||||
return checks;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.healthlink.his.emr.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.emr.domain.EmrRevision;
|
||||
import com.healthlink.his.emr.mapper.EmrRevisionMapper;
|
||||
import com.healthlink.his.emr.service.IEmrRevisionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class EmrRevisionServiceImpl
|
||||
extends ServiceImpl<EmrRevisionMapper, EmrRevision>
|
||||
implements IEmrRevisionService {
|
||||
|
||||
@Override
|
||||
public List<EmrRevision> selectByEmrId(Long emrId) {
|
||||
return baseMapper.selectByEmrId(emrId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmrRevision selectLatest(Long emrId) {
|
||||
return baseMapper.selectLatest(emrId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.healthlink.his.emr.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.emr.domain.EmrTimeliness;
|
||||
import com.healthlink.his.emr.mapper.EmrTimelinessMapper;
|
||||
import com.healthlink.his.emr.service.IEmrTimelinessService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class EmrTimelinessServiceImpl
|
||||
extends ServiceImpl<EmrTimelinessMapper, EmrTimeliness>
|
||||
implements IEmrTimelinessService {
|
||||
|
||||
@Override
|
||||
public List<EmrTimeliness> selectByEncounterId(Long encounterId) {
|
||||
return baseMapper.selectByEncounterId(encounterId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmrTimeliness> selectOverdueList() {
|
||||
return baseMapper.selectOverdueList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getCompletionRate(String startDate, String endDate) {
|
||||
return baseMapper.getCompletionRate(startDate, endDate);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.healthlink.his.ca.mapper.CaSignatureMapper">
|
||||
|
||||
<select id="selectByDocument" resultType="com.healthlink.his.ca.domain.CaSignature">
|
||||
SELECT * FROM ca_signature
|
||||
WHERE document_id = #{documentId} AND document_type = #{documentType}
|
||||
ORDER BY sign_time DESC
|
||||
</select>
|
||||
|
||||
<select id="selectBySigner" resultType="com.healthlink.his.ca.domain.CaSignature">
|
||||
SELECT * FROM ca_signature
|
||||
WHERE signer_id = #{signerId} AND del_flag = '0'
|
||||
ORDER BY sign_time DESC
|
||||
</select>
|
||||
|
||||
<select id="getSignerStatistics" resultType="java.util.Map">
|
||||
SELECT
|
||||
signer_name,
|
||||
signer_department,
|
||||
COUNT(*) AS sign_count,
|
||||
SUM(CASE WHEN status = 'VALID' THEN 1 ELSE 0 END) AS valid_count,
|
||||
SUM(CASE WHEN status = 'REVOKED' THEN 1 ELSE 0 END) AS revoked_count
|
||||
FROM ca_signature
|
||||
WHERE del_flag = '0'
|
||||
<if test="department != null and department != ''">
|
||||
AND signer_department = #{department}
|
||||
</if>
|
||||
GROUP BY signer_name, signer_department
|
||||
ORDER BY sign_count DESC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.healthlink.his.emr.mapper.EmrCompletenessCheckMapper">
|
||||
|
||||
<select id="selectByEmrId" resultType="com.healthlink.his.emr.domain.EmrCompletenessCheck">
|
||||
SELECT * FROM emr_completeness_check
|
||||
WHERE emr_id = #{emrId}
|
||||
ORDER BY check_time DESC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.healthlink.his.emr.mapper.EmrRevisionMapper">
|
||||
|
||||
<select id="selectByEmrId" resultType="com.healthlink.his.emr.domain.EmrRevision">
|
||||
SELECT * FROM emr_revision
|
||||
WHERE emr_id = #{emrId}
|
||||
ORDER BY revision_number DESC
|
||||
</select>
|
||||
|
||||
<select id="selectLatest" resultType="com.healthlink.his.emr.domain.EmrRevision">
|
||||
SELECT * FROM emr_revision
|
||||
WHERE emr_id = #{emrId}
|
||||
ORDER BY revision_number DESC
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.healthlink.his.emr.mapper.EmrTimelinessMapper">
|
||||
|
||||
<select id="selectByEncounterId" resultType="com.healthlink.his.emr.domain.EmrTimeliness">
|
||||
SELECT * FROM emr_timeliness
|
||||
WHERE encounter_id = #{encounterId}
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
|
||||
<select id="selectOverdueList" resultType="com.healthlink.his.emr.domain.EmrTimeliness">
|
||||
SELECT * FROM emr_timeliness
|
||||
WHERE status = 'OVERDUE'
|
||||
ORDER BY deadline_time ASC
|
||||
</select>
|
||||
|
||||
<select id="getCompletionRate" resultType="java.util.Map">
|
||||
SELECT
|
||||
COUNT(*) AS total_count,
|
||||
SUM(CASE WHEN status = 'COMPLETED' THEN 1 ELSE 0 END) AS completed_count,
|
||||
ROUND(SUM(CASE WHEN status = 'COMPLETED' THEN 1 ELSE 0 END) * 100.0 / NULLIF(COUNT(*), 0), 2) AS completion_rate
|
||||
FROM emr_timeliness
|
||||
WHERE create_time::date BETWEEN #{startDate} AND #{endDate}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
9
healthlink-his-ui/src/api/anesthesia/index.js
Normal file
9
healthlink-his-ui/src/api/anesthesia/index.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import request from '@/utils/request'
|
||||
export function createRecord(data) { return request({ url: '/healthlink-his/api/v1/anesthesia/record', method: 'post', data }) }
|
||||
export function updateRecord(data) { return request({ url: '/healthlink-his/api/v1/anesthesia/record', method: 'put', data }) }
|
||||
export function getRecordDetail(id) { return request({ url: '/healthlink-his/api/v1/anesthesia/record/' + id, method: 'get' }) }
|
||||
export function getByEncounter(encounterId) { return request({ url: '/healthlink-his/api/v1/anesthesia/record/encounter/' + encounterId, method: 'get' }) }
|
||||
export function getVitalSigns(recordId) { return request({ url: '/healthlink-his/api/v1/anesthesia/vital-sign/' + recordId, method: 'get' }) }
|
||||
export function getMedications(recordId) { return request({ url: '/healthlink-his/api/v1/anesthesia/medication/' + recordId, method: 'get' }) }
|
||||
export function getIoSummary(recordId) { return request({ url: '/healthlink-his/api/v1/anesthesia/io-summary/' + recordId, method: 'get' }) }
|
||||
export function completeRecord(id) { return request({ url: '/healthlink-his/api/v1/anesthesia/complete/' + id, method: 'put' }) }
|
||||
5
healthlink-his-ui/src/api/casignature/index.js
Normal file
5
healthlink-his-ui/src/api/casignature/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import request from '@/utils/request'
|
||||
export function verifySignature(documentType, documentId) { return request({ url: '/healthlink-his/api/v1/ca-signature/verify/' + documentType + '/' + documentId, method: 'get' }) }
|
||||
export function getSignatureHistory(documentType, documentId) { return request({ url: '/healthlink-his/api/v1/ca-signature/history/' + documentType + '/' + documentId, method: 'get' }) }
|
||||
export function revokeSignature(id) { return request({ url: '/healthlink-his/api/v1/ca-signature/revoke/' + id, method: 'put' }) }
|
||||
export function getSignatureStatistics() { return request({ url: '/healthlink-his/api/v1/ca-signature/statistics', method: 'get' }) }
|
||||
9
healthlink-his-ui/src/api/emr/index.js
Normal file
9
healthlink-his-ui/src/api/emr/index.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import request from '@/utils/request'
|
||||
export function createRevision(data) { return request({ url: '/healthlink-his/api/v1/emr/revision', method: 'post', data }) }
|
||||
export function getRevisionHistory(emrId) { return request({ url: '/healthlink-his/api/v1/emr/revision/' + emrId, method: 'get' }) }
|
||||
export function executeCompletenessCheck(emrId) { return request({ url: '/healthlink-his/api/v1/emr/completeness-check/' + emrId, method: 'post' }) }
|
||||
export function getCompletenessCheck(emrId) { return request({ url: '/healthlink-his/api/v1/emr/completeness-check/' + emrId, method: 'get' }) }
|
||||
export function getTimelinessByEncounter(encounterId) { return request({ url: '/healthlink-his/api/v1/emr/timeliness/encounter/' + encounterId, method: 'get' }) }
|
||||
export function getOverdueList() { return request({ url: '/healthlink-his/api/v1/emr/timeliness/overdue', method: 'get' }) }
|
||||
export function getTimelinessStatistics(params) { return request({ url: '/healthlink-his/api/v1/emr/timeliness/statistics', method: 'get', params }) }
|
||||
export function checkTimeliness(data) { return request({ url: '/healthlink-his/api/v1/emr/timeliness/check', method: 'post', data }) }
|
||||
8
healthlink-his-ui/src/api/mrhomepage/index.js
Normal file
8
healthlink-his-ui/src/api/mrhomepage/index.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import request from '@/utils/request'
|
||||
export function generateHomepage(data) { return request({ url: '/healthlink-his/api/v1/mr-homepage/generate', method: 'post', data }) }
|
||||
export function updateHomepage(data) { return request({ url: '/healthlink-his/api/v1/mr-homepage', method: 'put', data }) }
|
||||
export function getHomepageDetail(id) { return request({ url: '/healthlink-his/api/v1/mr-homepage/' + id, method: 'get' }) }
|
||||
export function executeQualityCheck(id) { return request({ url: '/healthlink-his/api/v1/mr-homepage/quality-check/' + id, method: 'post' }) }
|
||||
export function getQualityCheck(homepageId) { return request({ url: '/healthlink-his/api/v1/mr-homepage/quality-check/' + homepageId, method: 'get' }) }
|
||||
export function getStatistics(params) { return request({ url: '/healthlink-his/api/v1/mr-homepage/statistics', method: 'get', params }) }
|
||||
export function submitHomepage(id) { return request({ url: '/healthlink-his/api/v1/mr-homepage/submit/' + id, method: 'put' }) }
|
||||
132
healthlink-his-ui/src/views/anesthesia/record/index.vue
Normal file
132
healthlink-his-ui/src/views/anesthesia/record/index.vue
Normal file
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
|
||||
<el-form-item label="就诊号" prop="encounterId">
|
||||
<el-input v-model="queryParams.encounterId" placeholder="就诊号" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" placeholder="全部" clearable>
|
||||
<el-option label="草稿" value="DRAFT" /><el-option label="进行中" value="IN_PROGRESS" /><el-option label="已完成" value="COMPLETED" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item><el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button></el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loading" :data="dataList">
|
||||
<el-table-column label="患者" prop="patientName" width="120" />
|
||||
<el-table-column label="麻醉类型" prop="anesthesiaType" width="120" />
|
||||
<el-table-column label="ASA分级" prop="asaGrade" width="80" />
|
||||
<el-table-column label="麻醉医生" prop="anesthetistName" width="120" />
|
||||
<el-table-column label="开始时间" prop="startTime" width="170" />
|
||||
<el-table-column label="结束时间" prop="endTime" width="170" />
|
||||
<el-table-column label="状态" prop="status" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === 'COMPLETED' ? 'success' : scope.row.status === 'DRAFT' ? 'info' : ''">{{ { DRAFT: '草稿', IN_PROGRESS: '进行中', COMPLETED: '已完成' }[scope.row.status] }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150" align="center">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="handleDetail(scope.row)">详情</el-button>
|
||||
<el-button link type="primary" v-if="scope.row.status !== 'COMPLETED'" @click="handleComplete(scope.row)">完成</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-dialog title="麻醉记录详情" v-model="detailVisible" width="800px" top="5vh">
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="基本信息" name="basic">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="ASA分级">{{ recordDetail.asaGrade }}</el-descriptions-item>
|
||||
<el-descriptions-item label="麻醉类型">{{ recordDetail.anesthesiaType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="麻醉方式">{{ recordDetail.anesthesiaMethod }}</el-descriptions-item>
|
||||
<el-descriptions-item label="气道评估">{{ recordDetail.airwayAssessment }}</el-descriptions-item>
|
||||
<el-descriptions-item label="禁食确认">{{ recordDetail.fastingConfirmed ? '是' : '否' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="知情同意">{{ recordDetail.consentSigned ? '已签署' : '未签署' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="摘要" :span="2">{{ recordDetail.summary }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="生命体征" name="vital">
|
||||
<el-table :data="vitalSigns" size="small">
|
||||
<el-table-column label="时间" prop="recordTime" width="170" />
|
||||
<el-table-column label="心率" prop="heartRate" width="80" />
|
||||
<el-table-column label="收缩压" prop="bloodPressureSys" width="80" />
|
||||
<el-table-column label="舒张压" prop="bloodPressureDia" width="80" />
|
||||
<el-table-column label="血氧" prop="spo2" width="80" />
|
||||
<el-table-column label="体温" prop="temperature" width="80" />
|
||||
<el-table-column label="呼吸" prop="respiratoryRate" width="80" />
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="用药记录" name="medication">
|
||||
<el-table :data="medications" size="small">
|
||||
<el-table-column label="药品" prop="drugName" width="150" />
|
||||
<el-table-column label="剂量" prop="dosage" width="100" />
|
||||
<el-table-column label="途径" prop="route" width="100" />
|
||||
<el-table-column label="开始" prop="startTime" width="170" />
|
||||
<el-table-column label="结束" prop="endTime" width="170" />
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="出入量" name="io">
|
||||
<el-descriptions :column="2" border v-if="ioSummary">
|
||||
<el-descriptions-item label="总入量">{{ ioSummary.totalInput }} ml</el-descriptions-item>
|
||||
<el-descriptions-item label="总出量">{{ ioSummary.totalOutput }} ml</el-descriptions-item>
|
||||
<el-descriptions-item label="出入平衡">{{ ioSummary.balance }} ml</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="术后随访" name="followup">
|
||||
<el-table :data="followups" size="small">
|
||||
<el-table-column label="随访日期" prop="followupDate" width="120" />
|
||||
<el-table-column label="疼痛评分" prop="painScore" width="100" />
|
||||
<el-table-column label="恶心呕吐" width="100">
|
||||
<template #default="scope">{{ scope.row.nauseaVomiting ? '有' : '无' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" prop="notes" show-overflow-tooltip />
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { getRecordDetail, getByEncounter, getVitalSigns, getMedications, getIoSummary, completeRecord } from '@/api/anesthesia'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const loading = ref(false)
|
||||
const showSearch = ref(true)
|
||||
const dataList = ref([])
|
||||
const detailVisible = ref(false)
|
||||
const activeTab = ref('basic')
|
||||
const recordDetail = ref({})
|
||||
const vitalSigns = ref([])
|
||||
const medications = ref([])
|
||||
const ioSummary = ref(null)
|
||||
const followups = ref([])
|
||||
const queryParams = reactive({ encounterId: '', status: '' })
|
||||
|
||||
const getList = async () => {
|
||||
if (!queryParams.encounterId) return
|
||||
loading.value = true
|
||||
const res = await getByEncounter(queryParams.encounterId)
|
||||
dataList.value = Array.isArray(res.data) ? res.data : (res.data ? [res.data] : [])
|
||||
loading.value = false
|
||||
}
|
||||
const handleQuery = () => getList()
|
||||
|
||||
const handleDetail = async (row) => {
|
||||
const res = await getRecordDetail(row.id)
|
||||
recordDetail.value = res.data || row
|
||||
const [vRes, mRes, ioRes] = await Promise.all([getVitalSigns(row.id), getMedications(row.id), getIoSummary(row.id)])
|
||||
vitalSigns.value = vRes.data || []
|
||||
medications.value = mRes.data || []
|
||||
ioSummary.value = ioRes.data || { totalInput: 0, totalOutput: 0, balance: 0 }
|
||||
activeTab.value = 'basic'
|
||||
detailVisible.value = true
|
||||
}
|
||||
|
||||
const handleComplete = async (row) => {
|
||||
await ElMessageBox.confirm('确认完成此麻醉记录?')
|
||||
await completeRecord(row.id)
|
||||
ElMessage.success('操作成功')
|
||||
getList()
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" :inline="true" v-show="showSearch">
|
||||
<el-form-item label="文档类型" prop="documentType">
|
||||
<el-select v-model="queryParams.documentType" placeholder="全部" clearable>
|
||||
<el-option label="电子病历" value="EMR" /><el-option label="处方" value="PRESCRIPTION" />
|
||||
<el-option label="医嘱" value="ORDER" /><el-option label="会诊" value="CONSULTATION" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="文档ID" prop="documentId">
|
||||
<el-input v-model="queryParams.documentId" placeholder="文档ID" clearable />
|
||||
</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-table v-loading="loading" :data="dataList">
|
||||
<el-table-column label="文档类型" prop="documentType" width="100">
|
||||
<template #default="scope"><el-tag>{{ docTypeMap[scope.row.documentType] }}</el-tag></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="文档ID" prop="documentId" width="100" />
|
||||
<el-table-column label="签名人员" prop="signerName" width="120" />
|
||||
<el-table-column label="职称" prop="signerTitle" width="100" />
|
||||
<el-table-column label="科室" prop="signerDepartment" width="120" />
|
||||
<el-table-column label="签名时间" prop="signTime" width="180" />
|
||||
<el-table-column label="状态" prop="status" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === 'VALID' ? 'success' : 'danger'">{{ scope.row.status === 'VALID' ? '有效' : '已撤销' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="handleVerify(scope.row)">验证</el-button>
|
||||
</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-dialog title="签名验证结果" v-model="verifyVisible" width="400px">
|
||||
<el-result v-if="verifyResult" :icon="verifyResult.valid ? 'success' : 'error'" :title="verifyResult.valid ? '签名有效' : '签名无效'" :sub-title="verifyResult.message" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { verifySignature } from '@/api/casignature'
|
||||
const loading = ref(false)
|
||||
const showSearch = ref(true)
|
||||
const dataList = ref([])
|
||||
const total = ref(0)
|
||||
const verifyVisible = ref(false)
|
||||
const verifyResult = ref(null)
|
||||
const queryParams = reactive({ documentType: '', documentId: '', pageNum: 1, pageSize: 10 })
|
||||
const docTypeMap = { EMR: '电子病历', PRESCRIPTION: '处方', ORDER: '医嘱', CONSULTATION: '会诊' }
|
||||
const handleQuery = () => {}
|
||||
const resetQuery = () => { queryParams.documentType = ''; queryParams.documentId = '' }
|
||||
const handleVerify = async (row) => {
|
||||
const res = await verifySignature(row.documentType, row.documentId)
|
||||
verifyResult.value = res.data || { valid: true, message: '签名验证通过' }
|
||||
verifyVisible.value = true
|
||||
}
|
||||
</script>
|
||||
49
healthlink-his-ui/src/views/emr/revision-history/index.vue
Normal file
49
healthlink-his-ui/src/views/emr/revision-history/index.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
|
||||
<el-form-item label="病历ID" prop="emrId">
|
||||
<el-input v-model="queryParams.emrId" placeholder="请输入病历ID" clearable />
|
||||
</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-table v-loading="loading" :data="dataList">
|
||||
<el-table-column label="版本号" prop="revisionNumber" width="100" />
|
||||
<el-table-column label="操作人" prop="operatorName" width="120" />
|
||||
<el-table-column label="操作类型" prop="operationType" width="120">
|
||||
<template #default="scope">
|
||||
<el-tag :type="operationTypeMap[scope.row.operationType]?.type">{{ operationTypeMap[scope.row.operationType]?.label }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作时间" prop="createTime" width="180" />
|
||||
<el-table-column label="变更内容" prop="diffContent" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { getRevisionHistory } from '@/api/emr'
|
||||
|
||||
const loading = ref(true)
|
||||
const showSearch = ref(true)
|
||||
const dataList = ref([])
|
||||
const total = ref(0)
|
||||
const queryParams = reactive({ emrId: undefined, pageNum: 1, pageSize: 10 })
|
||||
const operationTypeMap = { CREATE: { label: '创建', type: 'success' }, EDIT: { label: '编辑', type: '' }, APPROVE: { label: '审批', type: 'warning' }, SIGN: { label: '签名', type: 'primary' } }
|
||||
|
||||
const getList = async () => {
|
||||
if (!queryParams.emrId) { loading.value = false; return }
|
||||
loading.value = true
|
||||
const res = await getRevisionHistory(queryParams.emrId)
|
||||
dataList.value = res.rows || res.data || []
|
||||
total.value = res.total || dataList.value.length
|
||||
loading.value = false
|
||||
}
|
||||
const handleQuery = () => { queryParams.pageNum = 1; getList() }
|
||||
const resetQuery = () => { queryParams.emrId = undefined; dataList.value = []; total.value = 0 }
|
||||
onMounted(() => {})
|
||||
</script>
|
||||
50
healthlink-his-ui/src/views/emr/timeliness/index.vue
Normal file
50
healthlink-his-ui/src/views/emr/timeliness/index.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20" class="mb8">
|
||||
<el-col :span="6"><el-card shadow="hover"><el-statistic title="待完成" :value="stats.pending" /></el-card></el-col>
|
||||
<el-col :span="6"><el-card shadow="hover"><el-statistic title="已完成" :value="stats.completed" /></el-card></el-col>
|
||||
<el-col :span="6"><el-card shadow="hover"><el-statistic title="超时" :value="stats.overdue" /></el-card></el-col>
|
||||
<el-col :span="6"><el-card shadow="hover"><el-statistic title="完成率" :value="stats.rate" suffix="%" /></el-card></el-col>
|
||||
</el-row>
|
||||
<el-form :model="queryParams" :inline="true" class="mt8">
|
||||
<el-form-item label="科室">
|
||||
<el-input v-model="queryParams.departmentName" placeholder="科室" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="病历类型">
|
||||
<el-select v-model="queryParams.emrType" placeholder="全部" clearable>
|
||||
<el-option label="入院记录" value="ADMISSION" /><el-option label="首次病程" value="FIRST_COURSE" />
|
||||
<el-option label="日常病程" value="DAILY_COURSE" /><el-option label="出院记录" value="DISCHARGE" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item><el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button></el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loading" :data="dataList">
|
||||
<el-table-column label="患者" prop="patientName" width="120" />
|
||||
<el-table-column label="科室" prop="departmentName" width="120" />
|
||||
<el-table-column label="医生" prop="doctorName" width="100" />
|
||||
<el-table-column label="病历类型" prop="emrType" width="120">
|
||||
<template #default="scope"><el-tag>{{ emrTypeMap[scope.row.emrType] }}</el-tag></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="截止时间" prop="deadlineTime" width="180" />
|
||||
<el-table-column label="状态" prop="status" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="statusMap[scope.row.status]?.type">{{ statusMap[scope.row.status]?.label }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { getTimelinessByEncounter, getOverdueList, getTimelinessStatistics } from '@/api/emr'
|
||||
const loading = ref(false)
|
||||
const dataList = ref([])
|
||||
const stats = reactive({ pending: 0, completed: 0, overdue: 0, rate: 0 })
|
||||
const queryParams = reactive({ departmentName: '', emrType: '' })
|
||||
const emrTypeMap = { ADMISSION: '入院记录', FIRST_COURSE: '首次病程', DAILY_COURSE: '日常病程', DISCHARGE: '出院记录' }
|
||||
const statusMap = { PENDING: { label: '待完成', type: 'info' }, COMPLETED: { label: '已完成', type: 'success' }, OVERDUE: { label: '超时', type: 'danger' } }
|
||||
const getList = async () => { loading.value = true; const res = await getOverdueList(); dataList.value = res.data || res.rows || []; loading.value = false }
|
||||
const handleQuery = () => getList()
|
||||
onMounted(() => { getList() })
|
||||
</script>
|
||||
75
healthlink-his-ui/src/views/mrhomepage/management/index.vue
Normal file
75
healthlink-his-ui/src/views/mrhomepage/management/index.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" :inline="true" v-show="showSearch">
|
||||
<el-form-item label="出院日期">
|
||||
<el-date-picker v-model="queryParams.dischargeDateRange" type="daterange" start-placeholder="开始" end-placeholder="结束" />
|
||||
</el-form-item>
|
||||
<el-form-item label="科室">
|
||||
<el-input v-model="queryParams.departmentName" placeholder="科室" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="质控状态">
|
||||
<el-select v-model="queryParams.qualityStatus" placeholder="全部" clearable>
|
||||
<el-option label="草稿" value="DRAFT" /><el-option label="待审核" value="SUBMITTED" /><el-option label="已通过" value="APPROVED" /><el-option label="有缺陷" value="DEFECTIVE" />
|
||||
</el-select>
|
||||
</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-table v-loading="loading" :data="dataList">
|
||||
<el-table-column label="患者" prop="patientName" width="100" />
|
||||
<el-table-column label="入院日期" prop="admissionDate" width="110" />
|
||||
<el-table-column label="出院日期" prop="dischargeDate" width="110" />
|
||||
<el-table-column label="住院天数" prop="losDays" width="90" />
|
||||
<el-table-column label="主要诊断" prop="primaryDiagnosisName" show-overflow-tooltip />
|
||||
<el-table-column label="DRG" prop="drgGroup" width="80" />
|
||||
<el-table-column label="总费用" width="120" align="right">
|
||||
<template #default="scope">{{ formatMoney(scope.row.totalCost) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="质控状态" prop="qualityStatus" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="qStatusMap[scope.row.qualityStatus]?.type">{{ qStatusMap[scope.row.qualityStatus]?.label }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="180" align="center">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="handleQualityCheck(scope.row)">质控</el-button>
|
||||
<el-button link type="primary" v-if="scope.row.qualityStatus !== 'APPROVED'" @click="handleSubmit(scope.row)">提交</el-button>
|
||||
</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" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { executeQualityCheck, submitHomepage } from '@/api/mrhomepage'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const loading = ref(false)
|
||||
const showSearch = ref(true)
|
||||
const dataList = ref([])
|
||||
const total = ref(0)
|
||||
const queryParams = reactive({ dischargeDateRange: [], departmentName: '', qualityStatus: '', pageNum: 1, pageSize: 10 })
|
||||
const qStatusMap = { DRAFT: { label: '草稿', type: 'info' }, SUBMITTED: { label: '待审核', type: 'warning' }, APPROVED: { label: '已通过', type: 'success' }, DEFECTIVE: { label: '有缺陷', type: 'danger' } }
|
||||
|
||||
const formatMoney = (val) => val ? '¥' + Number(val).toLocaleString() : '-'
|
||||
const getList = async () => { /* TODO: 调用后端分页查询 */ loading.value = false }
|
||||
const handleQuery = () => { queryParams.pageNum = 1; getList() }
|
||||
const resetQuery = () => { queryParams.dischargeDateRange = []; queryParams.departmentName = ''; queryParams.qualityStatus = ''; getList() }
|
||||
|
||||
const handleQualityCheck = async (row) => {
|
||||
const res = await executeQualityCheck(row.id)
|
||||
ElMessage.success('质控检查完成')
|
||||
getList()
|
||||
}
|
||||
const handleSubmit = async (row) => {
|
||||
await ElMessageBox.confirm('确认提交此病案首页?')
|
||||
await submitHomepage(row.id)
|
||||
ElMessage.success('提交成功')
|
||||
getList()
|
||||
}
|
||||
</script>
|
||||
37
healthlink-his-ui/src/views/mrhomepage/statistics/index.vue
Normal file
37
healthlink-his-ui/src/views/mrhomepage/statistics/index.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20" class="mb8">
|
||||
<el-col :span="6"><el-card shadow="hover"><el-statistic title="总首页数" :value="stats.total" /></el-card></el-col>
|
||||
<el-col :span="6"><el-card shadow="hover"><el-statistic title="已提交" :value="stats.submitted" /></el-card></el-col>
|
||||
<el-col :span="6"><el-card shadow="hover"><el-statistic title="待质控" :value="stats.pending" /></el-card></el-col>
|
||||
<el-col :span="6"><el-card shadow="hover"><el-statistic title="通过率" :value="stats.passRate" suffix="%" /></el-card></el-col>
|
||||
</el-row>
|
||||
<el-card class="mt8">
|
||||
<template #header><span>按科室统计</span></template>
|
||||
<el-table :data="departmentStats">
|
||||
<el-table-column label="科室" prop="departmentName" />
|
||||
<el-table-column label="首页数" prop="count" width="100" />
|
||||
<el-table-column label="已提交" prop="submitted" width="100" />
|
||||
<el-table-column label="通过率" prop="passRate" width="100">
|
||||
<template #default="scope">{{ scope.row.passRate }}%</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
<el-card class="mt8">
|
||||
<template #header><span>费用构成</span></template>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8"><el-statistic title="药品费占比" :value="costStats.drugRate" suffix="%" /></el-col>
|
||||
<el-col :span="8"><el-statistic title="检查费占比" :value="costStats.examRate" suffix="%" /></el-col>
|
||||
<el-col :span="8"><el-statistic title="材料费占比" :value="costStats.materialRate" suffix="%" /></el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
const stats = reactive({ total: 0, submitted: 0, pending: 0, passRate: 0 })
|
||||
const departmentStats = ref([])
|
||||
const costStats = reactive({ drugRate: 0, examRate: 0, materialRate: 0 })
|
||||
onMounted(() => { /* TODO: 调用统计接口 */ })
|
||||
</script>
|
||||
Reference in New Issue
Block a user