Merge remote-tracking branch 'origin/develop' into guanyu
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
package com.healthlink.his.web.anesthesia.appservice;
|
||||
|
||||
import com.healthlink.his.anesthesia.domain.AnesAsaAssessment;
|
||||
import com.healthlink.his.anesthesia.domain.AnesSummary;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaFollowup;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaIoRecord;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaMedication;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaPostopFollowup;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaRecord;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaVitalSign;
|
||||
import com.healthlink.his.anesthesia.dto.AnesthesiaIoSummaryDto;
|
||||
@@ -31,4 +34,20 @@ public interface IAnesthesiaAppService {
|
||||
AnesthesiaIoSummaryDto getIoSummary(Long recordId);
|
||||
|
||||
void completeRecord(Long recordId);
|
||||
|
||||
AnesAsaAssessment saveAsaAssessment(AnesAsaAssessment assessment);
|
||||
|
||||
List<AnesAsaAssessment> getAsaAssessments(Long recordId);
|
||||
|
||||
AnesthesiaVitalSign recordVitalSign(AnesthesiaVitalSign vitalSign);
|
||||
|
||||
List<AnesthesiaVitalSign> getVitalSignTimeline(Long recordId);
|
||||
|
||||
AnesSummary saveSummary(AnesSummary summary);
|
||||
|
||||
AnesSummary getSummary(Long recordId);
|
||||
|
||||
AnesthesiaPostopFollowup recordFollowup(AnesthesiaPostopFollowup followup);
|
||||
|
||||
List<AnesthesiaPostopFollowup> getFollowups(Long encounterId);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
package com.healthlink.his.web.anesthesia.appservice.impl;
|
||||
|
||||
import com.healthlink.his.anesthesia.domain.AnesAsaAssessment;
|
||||
import com.healthlink.his.anesthesia.domain.AnesSummary;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaFollowup;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaIoRecord;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaMedication;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaPostopFollowup;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaRecord;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaVitalSign;
|
||||
import com.healthlink.his.anesthesia.dto.AnesthesiaIoSummaryDto;
|
||||
import com.healthlink.his.anesthesia.dto.AnesthesiaRecordDetailDto;
|
||||
import com.healthlink.his.anesthesia.service.IAnesAsaAssessmentService;
|
||||
import com.healthlink.his.anesthesia.service.IAnesSummaryService;
|
||||
import com.healthlink.his.anesthesia.service.IAnesthesiaFollowupService;
|
||||
import com.healthlink.his.anesthesia.service.IAnesthesiaIoRecordService;
|
||||
import com.healthlink.his.anesthesia.service.IAnesthesiaPostopFollowupService;
|
||||
import com.healthlink.his.anesthesia.service.IAnesthesiaMedicationService;
|
||||
import com.healthlink.his.anesthesia.service.IAnesthesiaRecordService;
|
||||
import com.healthlink.his.anesthesia.service.IAnesthesiaVitalSignService;
|
||||
import com.healthlink.his.web.anesthesia.appservice.IAnesthesiaAppService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -39,6 +46,15 @@ public class AnesthesiaAppServiceImpl implements IAnesthesiaAppService {
|
||||
@Resource
|
||||
private IAnesthesiaFollowupService anesthesiaFollowupService;
|
||||
|
||||
@Resource
|
||||
private IAnesAsaAssessmentService anesAsaAssessmentService;
|
||||
|
||||
@Resource
|
||||
private IAnesSummaryService anesSummaryService;
|
||||
|
||||
@Resource
|
||||
private IAnesthesiaPostopFollowupService anesthesiaPostopFollowupService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public AnesthesiaRecord createRecord(AnesthesiaRecord record) {
|
||||
@@ -125,4 +141,74 @@ public class AnesthesiaAppServiceImpl implements IAnesthesiaAppService {
|
||||
record.setEndTime(new Date());
|
||||
anesthesiaRecordService.updateById(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public AnesAsaAssessment saveAsaAssessment(AnesAsaAssessment assessment) {
|
||||
if (assessment.getId() != null) {
|
||||
anesAsaAssessmentService.updateById(assessment);
|
||||
} else {
|
||||
anesAsaAssessmentService.save(assessment);
|
||||
}
|
||||
return assessment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AnesAsaAssessment> getAsaAssessments(Long recordId) {
|
||||
return anesAsaAssessmentService.selectByRecordId(recordId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public AnesthesiaVitalSign recordVitalSign(AnesthesiaVitalSign vitalSign) {
|
||||
anesthesiaVitalSignService.save(vitalSign);
|
||||
return vitalSign;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AnesthesiaVitalSign> getVitalSignTimeline(Long recordId) {
|
||||
return anesthesiaVitalSignService.selectByRecordId(recordId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public AnesSummary saveSummary(AnesSummary summary) {
|
||||
if (summary.getId() != null) {
|
||||
anesSummaryService.updateById(summary);
|
||||
} else {
|
||||
AnesSummary existing = anesSummaryService.selectByRecordId(summary.getRecordId());
|
||||
if (existing != null) {
|
||||
summary.setId(existing.getId());
|
||||
anesSummaryService.updateById(summary);
|
||||
} else {
|
||||
anesSummaryService.save(summary);
|
||||
}
|
||||
}
|
||||
return summary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnesSummary getSummary(Long recordId) {
|
||||
return anesSummaryService.selectByRecordId(recordId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AnesthesiaPostopFollowup recordFollowup(AnesthesiaPostopFollowup followup) {
|
||||
if (followup.getId() != null) {
|
||||
anesthesiaPostopFollowupService.updateById(followup);
|
||||
} else {
|
||||
followup.setStatus(0);
|
||||
anesthesiaPostopFollowupService.save(followup);
|
||||
}
|
||||
return followup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AnesthesiaPostopFollowup> getFollowups(Long encounterId) {
|
||||
LambdaQueryWrapper<AnesthesiaPostopFollowup> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(AnesthesiaPostopFollowup::getEncounterId, encounterId)
|
||||
.orderByDesc(AnesthesiaPostopFollowup::getFollowupTime);
|
||||
return anesthesiaPostopFollowupService.list(wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package com.healthlink.his.web.anesthesia.controller;
|
||||
|
||||
import com.core.common.core.domain.R;
|
||||
import com.healthlink.his.anesthesia.domain.AnesAsaAssessment;
|
||||
import com.healthlink.his.anesthesia.domain.AnesSummary;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaFollowup;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaIoRecord;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaMedication;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaPostopFollowup;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaRecord;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaVitalSign;
|
||||
import com.healthlink.his.anesthesia.dto.AnesthesiaIoSummaryDto;
|
||||
@@ -17,6 +20,7 @@ import com.healthlink.his.web.anesthesia.appservice.IAnesthesiaAppService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
@@ -134,4 +138,60 @@ public class AnesthesiaController {
|
||||
anesthesiaAppService.completeRecord(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/asa-assessment")
|
||||
@Operation(summary = "保存ASA评估")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:anesthesia:edit')")
|
||||
public R<AnesAsaAssessment> saveAsaAssessment(@RequestBody AnesAsaAssessment assessment) {
|
||||
return R.ok(anesthesiaAppService.saveAsaAssessment(assessment));
|
||||
}
|
||||
|
||||
@GetMapping("/asa-assessment/{recordId}")
|
||||
@Operation(summary = "查询ASA评估列表")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:anesthesia:list')")
|
||||
public R<List<AnesAsaAssessment>> getAsaAssessments(@PathVariable Long recordId) {
|
||||
return R.ok(anesthesiaAppService.getAsaAssessments(recordId));
|
||||
}
|
||||
|
||||
@PostMapping("/vital-sign/record")
|
||||
@Operation(summary = "记录术中生命体征")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:anesthesia:edit')")
|
||||
public R<AnesthesiaVitalSign> recordVitalSign(@RequestBody AnesthesiaVitalSign vitalSign) {
|
||||
return R.ok(anesthesiaAppService.recordVitalSign(vitalSign));
|
||||
}
|
||||
|
||||
@GetMapping("/vital-sign/timeline/{recordId}")
|
||||
@Operation(summary = "查询生命体征时间线")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:anesthesia:list')")
|
||||
public R<List<AnesthesiaVitalSign>> getVitalSignTimeline(@PathVariable Long recordId) {
|
||||
return R.ok(anesthesiaAppService.getVitalSignTimeline(recordId));
|
||||
}
|
||||
|
||||
@PostMapping("/summary")
|
||||
@Operation(summary = "保存麻醉小结")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:anesthesia:edit')")
|
||||
public R<AnesSummary> saveSummary(@RequestBody AnesSummary summary) {
|
||||
return R.ok(anesthesiaAppService.saveSummary(summary));
|
||||
}
|
||||
|
||||
@GetMapping("/summary/{recordId}")
|
||||
@Operation(summary = "获取麻醉小结")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:anesthesia:list')")
|
||||
public R<AnesSummary> getSummary(@PathVariable Long recordId) {
|
||||
return R.ok(anesthesiaAppService.getSummary(recordId));
|
||||
}
|
||||
|
||||
@PostMapping("/postop-followup")
|
||||
@Operation(summary = "记录术后随访")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:anesthesia:edit')")
|
||||
public R<AnesthesiaPostopFollowup> recordFollowup(@RequestBody AnesthesiaPostopFollowup followup) {
|
||||
return R.ok(anesthesiaAppService.recordFollowup(followup));
|
||||
}
|
||||
|
||||
@GetMapping("/postop-followup/{encounterId}")
|
||||
@Operation(summary = "查询术后随访列表")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:anesthesia:list')")
|
||||
public R<List<AnesthesiaPostopFollowup>> getPostopFollowups(@PathVariable Long encounterId) {
|
||||
return R.ok(anesthesiaAppService.getFollowups(encounterId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
CREATE TABLE anes_asa_assessment (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
record_id BIGINT NOT NULL,
|
||||
encounter_id BIGINT NOT NULL,
|
||||
patient_id BIGINT NOT NULL,
|
||||
asa_grade VARCHAR(5) NOT NULL,
|
||||
assessment_time TIMESTAMP NOT NULL,
|
||||
assessor_id BIGINT NOT NULL,
|
||||
assessor_name VARCHAR(50),
|
||||
airway_assessment TEXT,
|
||||
mallampati_grade VARCHAR(5),
|
||||
neck_mobility VARCHAR(50),
|
||||
mouth_opening_cm DECIMAL(4,1),
|
||||
weight_kg DECIMAL(6,2),
|
||||
height_cm DECIMAL(5,1),
|
||||
bmi DECIMAL(5,1),
|
||||
npo_hours INTEGER,
|
||||
asa_factors TEXT,
|
||||
risk_level VARCHAR(20),
|
||||
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)
|
||||
);
|
||||
@@ -0,0 +1,29 @@
|
||||
CREATE TABLE anes_summary (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
record_id BIGINT NOT NULL,
|
||||
encounter_id BIGINT NOT NULL,
|
||||
patient_id BIGINT NOT NULL,
|
||||
anesthesia_type VARCHAR(32),
|
||||
anesthesia_start_time TIMESTAMP,
|
||||
anesthesia_end_time TIMESTAMP,
|
||||
surgery_start_time TIMESTAMP,
|
||||
surgery_end_time TIMESTAMP,
|
||||
intraop_blood_loss_ml INTEGER,
|
||||
intraop_urine_ml INTEGER,
|
||||
intraop_fluid_ml INTEGER,
|
||||
blood_transfusion_ml INTEGER,
|
||||
complications TEXT,
|
||||
has_complications BOOLEAN DEFAULT FALSE,
|
||||
airway_management VARCHAR(50),
|
||||
extubation_time TIMESTAMP,
|
||||
patient_condition VARCHAR(50),
|
||||
summary TEXT,
|
||||
anesthetist_id BIGINT,
|
||||
anesthetist_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)
|
||||
);
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.healthlink.his.anesthesia.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 com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@TableName("anes_asa_assessment")
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class AnesAsaAssessment extends HisBaseEntity {
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
private Long recordId;
|
||||
|
||||
private Long encounterId;
|
||||
|
||||
private Long patientId;
|
||||
|
||||
private String asaGrade;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date assessmentTime;
|
||||
|
||||
private Long assessorId;
|
||||
|
||||
private String assessorName;
|
||||
|
||||
private String airwayAssessment;
|
||||
|
||||
private String mallampatiGrade;
|
||||
|
||||
private String neckMobility;
|
||||
|
||||
private BigDecimal mouthOpeningCm;
|
||||
|
||||
private BigDecimal weightKg;
|
||||
|
||||
private BigDecimal heightCm;
|
||||
|
||||
private BigDecimal bmi;
|
||||
|
||||
private Integer npoHours;
|
||||
|
||||
private String asaFactors;
|
||||
|
||||
private String riskLevel;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.healthlink.his.anesthesia.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 com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@TableName("anes_summary")
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class AnesSummary extends HisBaseEntity {
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
private Long recordId;
|
||||
|
||||
private Long encounterId;
|
||||
|
||||
private Long patientId;
|
||||
|
||||
private String anesthesiaType;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date anesthesiaStartTime;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date anesthesiaEndTime;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date surgeryStartTime;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date surgeryEndTime;
|
||||
|
||||
private Integer intraopBloodLossMl;
|
||||
|
||||
private Integer intraopUrineMl;
|
||||
|
||||
private Integer intraopFluidMl;
|
||||
|
||||
private Integer bloodTransfusionMl;
|
||||
|
||||
private String complications;
|
||||
|
||||
private Boolean hasComplications;
|
||||
|
||||
private String airwayManagement;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date extubationTime;
|
||||
|
||||
private String patientCondition;
|
||||
|
||||
private String summary;
|
||||
|
||||
private Long anesthetistId;
|
||||
|
||||
private String anesthetistName;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.healthlink.his.anesthesia.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.anesthesia.domain.AnesAsaAssessment;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface AnesAsaAssessmentMapper extends BaseMapper<AnesAsaAssessment> {
|
||||
|
||||
List<AnesAsaAssessment> selectByRecordId(@Param("recordId") Long recordId);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.healthlink.his.anesthesia.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.anesthesia.domain.AnesSummary;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
@Mapper
|
||||
public interface AnesSummaryMapper extends BaseMapper<AnesSummary> {
|
||||
|
||||
AnesSummary selectByRecordId(@Param("recordId") Long recordId);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.healthlink.his.anesthesia.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.anesthesia.domain.AnesAsaAssessment;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IAnesAsaAssessmentService extends IService<AnesAsaAssessment> {
|
||||
|
||||
List<AnesAsaAssessment> selectByRecordId(Long recordId);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.healthlink.his.anesthesia.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.anesthesia.domain.AnesSummary;
|
||||
|
||||
public interface IAnesSummaryService extends IService<AnesSummary> {
|
||||
|
||||
AnesSummary selectByRecordId(Long recordId);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.healthlink.his.anesthesia.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.anesthesia.domain.AnesAsaAssessment;
|
||||
import com.healthlink.his.anesthesia.mapper.AnesAsaAssessmentMapper;
|
||||
import com.healthlink.his.anesthesia.service.IAnesAsaAssessmentService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class AnesAsaAssessmentServiceImpl
|
||||
extends ServiceImpl<AnesAsaAssessmentMapper, AnesAsaAssessment>
|
||||
implements IAnesAsaAssessmentService {
|
||||
|
||||
@Override
|
||||
public List<AnesAsaAssessment> selectByRecordId(Long recordId) {
|
||||
return baseMapper.selectByRecordId(recordId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.healthlink.his.anesthesia.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.anesthesia.domain.AnesSummary;
|
||||
import com.healthlink.his.anesthesia.mapper.AnesSummaryMapper;
|
||||
import com.healthlink.his.anesthesia.service.IAnesSummaryService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class AnesSummaryServiceImpl
|
||||
extends ServiceImpl<AnesSummaryMapper, AnesSummary>
|
||||
implements IAnesSummaryService {
|
||||
|
||||
@Override
|
||||
public AnesSummary selectByRecordId(Long recordId) {
|
||||
return baseMapper.selectByRecordId(recordId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?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.anesthesia.mapper.AnesAsaAssessmentMapper">
|
||||
<select id="selectByRecordId" resultType="com.healthlink.his.anesthesia.domain.AnesAsaAssessment">
|
||||
SELECT * FROM anes_asa_assessment WHERE record_id = #{recordId} AND delete_flag = '0' ORDER BY assessment_time DESC
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?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.anesthesia.mapper.AnesSummaryMapper">
|
||||
<select id="selectByRecordId" resultType="com.healthlink.his.anesthesia.domain.AnesSummary">
|
||||
SELECT * FROM anes_summary WHERE record_id = #{recordId} AND delete_flag = '0' LIMIT 1
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -69,3 +69,27 @@ export function getFollowup(recordId) {
|
||||
export function addFollowup(data) {
|
||||
return request({ url: '/api/v1/anesthesia/followup', method: 'post', data })
|
||||
}
|
||||
|
||||
export function saveAsaAssessment(data) {
|
||||
return request({ url: '/api/v1/anesthesia/asa-assessment', method: 'post', data })
|
||||
}
|
||||
|
||||
export function getAsaAssessments(recordId) {
|
||||
return request({ url: '/api/v1/anesthesia/asa-assessment/' + recordId, method: 'get' })
|
||||
}
|
||||
|
||||
export function recordVitalSign(data) {
|
||||
return request({ url: '/api/v1/anesthesia/vital-sign/record', method: 'post', data })
|
||||
}
|
||||
|
||||
export function getVitalSignTimeline(recordId) {
|
||||
return request({ url: '/api/v1/anesthesia/vital-sign/timeline/' + recordId, method: 'get' })
|
||||
}
|
||||
|
||||
export function saveAnesSummary(data) {
|
||||
return request({ url: '/api/v1/anesthesia/summary', method: 'post', data })
|
||||
}
|
||||
|
||||
export function getAnesSummary(recordId) {
|
||||
return request({ url: '/api/v1/anesthesia/summary/' + recordId, method: 'get' })
|
||||
}
|
||||
|
||||
@@ -7,3 +7,5 @@ export function getVitalSigns(recordId) { return request({ url: '/api/v1/anesthe
|
||||
export function getMedications(recordId) { return request({ url: '/api/v1/anesthesia/medication/' + recordId, method: 'get' }) }
|
||||
export function getIoSummary(recordId) { return request({ url: '/api/v1/anesthesia/io-summary/' + recordId, method: 'get' }) }
|
||||
export function completeRecord(id) { return request({ url: '/api/v1/anesthesia/complete/' + id, method: 'put' }) }
|
||||
export function recordPostopFollowup(data) { return request({ url: '/api/v1/anesthesia/postop-followup', method: 'post', data }) }
|
||||
export function getPostopFollowups(encounterId) { return request({ url: '/api/v1/anesthesia/postop-followup/' + encounterId, method: 'get' }) }
|
||||
|
||||
@@ -0,0 +1,349 @@
|
||||
<template>
|
||||
<div class="asa-assessment-container">
|
||||
<el-card v-loading="loading">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>ASA麻醉评估</span>
|
||||
<el-button type="primary" size="small" @click="handleNew">新建评估</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="asaGrade" label="ASA分级" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getGradeType(scope.row.asaGrade)">{{ scope.row.asaGrade }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="assessmentTime" label="评估时间" width="170" />
|
||||
<el-table-column prop="assessorName" label="评估医师" width="120" />
|
||||
<el-table-column prop="mallampatiGrade" label="Mallampati" width="100" align="center" />
|
||||
<el-table-column prop="weightKg" label="体重(kg)" width="90" align="center" />
|
||||
<el-table-column prop="heightCm" label="身高(cm)" width="90" align="center" />
|
||||
<el-table-column prop="bmi" label="BMI" width="80" align="center" />
|
||||
<el-table-column prop="npoHours" label="禁食(h)" width="90" align="center" />
|
||||
<el-table-column prop="riskLevel" label="风险等级" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.riskLevel === 'LOW'" type="success">低风险</el-tag>
|
||||
<el-tag v-else-if="scope.row.riskLevel === 'MEDIUM'" type="warning">中风险</el-tag>
|
||||
<el-tag v-else-if="scope.row.riskLevel === 'HIGH'" type="danger">高风险</el-tag>
|
||||
<el-tag v-else-if="scope.row.riskLevel === 'CRITICAL'" type="danger">极高风险</el-tag>
|
||||
<el-tag v-else type="info">{{ scope.row.riskLevel || '-' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="handleEdit(scope.row)">编辑</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="dialogVisible" :title="isEdit ? '编辑ASA评估' : '新建ASA评估'" width="800px" destroy-on-close :close-on-click-modal="false">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="110px">
|
||||
<el-divider content-position="left">基本信息</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="ASA分级" prop="asaGrade">
|
||||
<el-select v-model="form.asaGrade" placeholder="请选择ASA分级" style="width: 100%">
|
||||
<el-option label="Ⅰ级 - 健康患者" value="Ⅰ" />
|
||||
<el-option label="Ⅱ级 - 轻度系统性疾病" value="Ⅱ" />
|
||||
<el-option label="Ⅲ级 - 严重系统性疾病" value="Ⅲ" />
|
||||
<el-option label="Ⅳ级 - 持续威胁生命的疾病" value="Ⅳ" />
|
||||
<el-option label="Ⅴ级 - 不手术则无法存活" value="Ⅴ" />
|
||||
<el-option label="Ⅵ级 - 脑死亡,器官捐献" value="Ⅵ" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="评估时间" prop="assessmentTime">
|
||||
<el-date-picker v-model="form.assessmentTime" type="datetime" placeholder="选择评估时间" value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="评估医师ID" prop="assessorId">
|
||||
<el-input-number v-model="form.assessorId" :min="1" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="评估医师姓名">
|
||||
<el-input v-model="form.assessorName" placeholder="请输入评估医师姓名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="就诊ID" prop="encounterId">
|
||||
<el-input-number v-model="form.encounterId" :min="1" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="患者ID" prop="patientId">
|
||||
<el-input-number v-model="form.patientId" :min="1" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="麻醉记录ID" prop="recordId">
|
||||
<el-input-number v-model="form.recordId" :min="1" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">气道评估</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="Mallampati分级">
|
||||
<el-select v-model="form.mallampatiGrade" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="Ⅰ级 - 可见咽腭弓、软腭、悬雍垂" value="Ⅰ" />
|
||||
<el-option label="Ⅱ级 - 可见咽腭弓、软腭" value="Ⅱ" />
|
||||
<el-option label="Ⅲ级 - 只能见软腭" value="Ⅲ" />
|
||||
<el-option label="Ⅳ级 - 只能见硬腭" value="Ⅳ" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="颈部活动度">
|
||||
<el-select v-model="form.neckMobility" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="正常" value="正常" />
|
||||
<el-option label="轻度受限" value="轻度受限" />
|
||||
<el-option label="明显受限" value="明显受限" />
|
||||
<el-option label="完全受限" value="完全受限" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="张口度(cm)">
|
||||
<el-input-number v-model="form.mouthOpeningCm" :min="0" :max="10" :step="0.1" :precision="1" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="气道评估描述">
|
||||
<el-input v-model="form.airwayAssessment" type="textarea" :rows="2" placeholder="请输入气道评估描述" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">体格信息</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="体重(kg)">
|
||||
<el-input-number v-model="form.weightKg" :min="0" :max="300" :step="0.1" :precision="2" style="width: 100%" @change="calcBmi" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="身高(cm)">
|
||||
<el-input-number v-model="form.heightCm" :min="0" :max="300" :step="0.1" :precision="1" style="width: 100%" @change="calcBmi" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="BMI">
|
||||
<el-input v-model="form.bmi" disabled placeholder="自动计算" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">禁食与风险</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="禁食时间(h)">
|
||||
<el-input-number v-model="form.npoHours" :min="0" :max="72" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="风险等级">
|
||||
<el-select v-model="form.riskLevel" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="低风险" value="LOW" />
|
||||
<el-option label="中风险" value="MEDIUM" />
|
||||
<el-option label="高风险" value="HIGH" />
|
||||
<el-option label="极高风险" value="CRITICAL" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="风险因素">
|
||||
<el-checkbox-group v-model="selectedFactors">
|
||||
<el-checkbox label="高血压" value="高血压" />
|
||||
<el-checkbox label="糖尿病" value="糖尿病" />
|
||||
<el-checkbox label="冠心病" value="冠心病" />
|
||||
<el-checkbox label="COPD" value="COPD" />
|
||||
<el-checkbox label="哮喘" value="哮喘" />
|
||||
<el-checkbox label="肝硬化" value="肝硬化" />
|
||||
<el-checkbox label="肾功能不全" value="肾功能不全" />
|
||||
<el-checkbox label="凝血障碍" value="凝血障碍" />
|
||||
<el-checkbox label="肥胖" value="肥胖" />
|
||||
<el-checkbox label="困难气道" value="困难气道" />
|
||||
<el-checkbox label="过敏史" value="过敏史" />
|
||||
<el-checkbox label="吸烟" value="吸烟" />
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="其他评估">
|
||||
<el-input v-model="form.asaFactors" type="textarea" :rows="2" placeholder="请输入其他风险因素或补充说明" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitLoading" @click="handleSave">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { saveAsaAssessment, getAsaAssessments } from '@/api/anesthesia'
|
||||
|
||||
const props = defineProps({
|
||||
recordId: { type: [Number, String], default: null }
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
const tableData = ref([])
|
||||
const dialogVisible = ref(false)
|
||||
const isEdit = ref(false)
|
||||
const formRef = ref(null)
|
||||
const selectedFactors = ref([])
|
||||
|
||||
const form = reactive({
|
||||
id: null,
|
||||
recordId: null,
|
||||
encounterId: null,
|
||||
patientId: null,
|
||||
asaGrade: '',
|
||||
assessmentTime: '',
|
||||
assessorId: null,
|
||||
assessorName: '',
|
||||
airwayAssessment: '',
|
||||
mallampatiGrade: '',
|
||||
neckMobility: '',
|
||||
mouthOpeningCm: null,
|
||||
weightKg: null,
|
||||
heightCm: null,
|
||||
bmi: null,
|
||||
npoHours: null,
|
||||
asaFactors: '',
|
||||
riskLevel: ''
|
||||
})
|
||||
|
||||
const rules = {
|
||||
asaGrade: [{ required: true, message: '请选择ASA分级', trigger: 'change' }],
|
||||
assessmentTime: [{ required: true, message: '请选择评估时间', trigger: 'change' }],
|
||||
assessorId: [{ required: true, message: '请输入评估医师ID', trigger: 'blur' }],
|
||||
encounterId: [{ required: true, message: '请输入就诊ID', trigger: 'blur' }],
|
||||
patientId: [{ required: true, message: '请输入患者ID', trigger: 'blur' }],
|
||||
recordId: [{ required: true, message: '请输入麻醉记录ID', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
function getGradeType(grade) {
|
||||
const map = { 'Ⅰ': 'success', 'Ⅱ': 'success', 'Ⅲ': 'warning', 'Ⅳ': 'danger', 'Ⅴ': 'danger', 'Ⅵ': 'danger' }
|
||||
return map[grade] || 'info'
|
||||
}
|
||||
|
||||
function calcBmi() {
|
||||
if (form.weightKg && form.heightCm && form.heightCm > 0) {
|
||||
const heightM = form.heightCm / 100
|
||||
form.bmi = Math.round((form.weightKg / (heightM * heightM)) * 10) / 10
|
||||
} else {
|
||||
form.bmi = null
|
||||
}
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
form.id = null
|
||||
form.recordId = null
|
||||
form.encounterId = null
|
||||
form.patientId = null
|
||||
form.asaGrade = ''
|
||||
form.assessmentTime = ''
|
||||
form.assessorId = null
|
||||
form.assessorName = ''
|
||||
form.airwayAssessment = ''
|
||||
form.mallampatiGrade = ''
|
||||
form.neckMobility = ''
|
||||
form.mouthOpeningCm = null
|
||||
form.weightKg = null
|
||||
form.heightCm = null
|
||||
form.bmi = null
|
||||
form.npoHours = null
|
||||
form.asaFactors = ''
|
||||
form.riskLevel = ''
|
||||
selectedFactors.value = []
|
||||
}
|
||||
|
||||
function handleNew() {
|
||||
resetForm()
|
||||
isEdit.value = false
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function handleEdit(row) {
|
||||
Object.assign(form, row)
|
||||
if (row.asaFactors) {
|
||||
try {
|
||||
selectedFactors.value = row.asaFactors.split(',').filter(Boolean)
|
||||
} catch {
|
||||
selectedFactors.value = []
|
||||
}
|
||||
} else {
|
||||
selectedFactors.value = []
|
||||
}
|
||||
isEdit.value = true
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
formRef.value?.validate(valid => {
|
||||
if (!valid) return
|
||||
submitLoading.value = true
|
||||
const data = { ...form }
|
||||
data.asaFactors = selectedFactors.value.join(',')
|
||||
saveAsaAssessment(data).then(res => {
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('保存成功')
|
||||
dialogVisible.value = false
|
||||
loadData()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '保存失败')
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.error('保存失败')
|
||||
}).finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function loadData() {
|
||||
if (!props.recordId) return
|
||||
loading.value = true
|
||||
getAsaAssessments(props.recordId).then(res => {
|
||||
tableData.value = res.data || []
|
||||
}).catch(() => {
|
||||
ElMessage.error('查询失败')
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
watch(() => props.recordId, () => {
|
||||
loadData()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.asa-assessment-container {
|
||||
padding: 12px;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,336 @@
|
||||
<template>
|
||||
<div class="anes-summary-container">
|
||||
<el-card v-loading="loading">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>麻醉小结</span>
|
||||
<el-button type="primary" size="small" @click="handleNew">新建小结</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="!summaryData" class="empty-tip">
|
||||
<el-empty description="暂无麻醉小结" />
|
||||
</div>
|
||||
|
||||
<div v-else class="summary-content">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="麻醉类型">{{ summaryData.anesthesiaType || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="麻醉医师">{{ summaryData.anesthetistName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="麻醉开始时间">{{ summaryData.anesthesiaStartTime || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="麻醉结束时间">{{ summaryData.anesthesiaEndTime || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="手术开始时间">{{ summaryData.surgeryStartTime || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="手术结束时间">{{ summaryData.surgeryEndTime || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="气道管理">{{ summaryData.airwayManagement || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="拔管时间">{{ summaryData.extubationTime || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="术中出血量(ml)">{{ summaryData.intraopBloodLossMl ?? '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="术中尿量(ml)">{{ summaryData.intraopUrineMl ?? '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="术中输液(ml)">{{ summaryData.intraopFluidMl ?? '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="输血量(ml)">{{ summaryData.bloodTransfusionMl ?? '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="有无并发症">
|
||||
<el-tag v-if="summaryData.hasComplications" type="danger">有</el-tag>
|
||||
<el-tag v-else type="success">无</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="患者情况">{{ summaryData.patientCondition || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="并发症详情" :span="2">{{ summaryData.complications || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="小结" :span="2">{{ summaryData.summary || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div class="summary-actions">
|
||||
<el-button type="primary" size="small" @click="handleEdit(summaryData)">编辑</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="dialogVisible" title="麻醉小结" width="800px" destroy-on-close :close-on-click-modal="false">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-divider content-position="left">基本信息</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="麻醉类型" prop="anesthesiaType">
|
||||
<el-select v-model="form.anesthesiaType" placeholder="请选择麻醉类型" style="width: 100%">
|
||||
<el-option label="全身麻醉" value="全身麻醉" />
|
||||
<el-option label="椎管内麻醉" value="椎管内麻醉" />
|
||||
<el-option label="神经阻滞" value="神经阻滞" />
|
||||
<el-option label="局部麻醉" value="局部麻醉" />
|
||||
<el-option label="复合麻醉" value="复合麻醉" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="麻醉医师ID" prop="anesthetistId">
|
||||
<el-input-number v-model="form.anesthetistId" :min="1" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="麻醉医师姓名">
|
||||
<el-input v-model="form.anesthetistName" placeholder="请输入麻醉医师姓名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="就诊ID" prop="encounterId">
|
||||
<el-input-number v-model="form.encounterId" :min="1" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="患者ID" prop="patientId">
|
||||
<el-input-number v-model="form.patientId" :min="1" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="麻醉记录ID" prop="recordId">
|
||||
<el-input-number v-model="form.recordId" :min="1" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">时间记录</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="麻醉开始时间">
|
||||
<el-date-picker v-model="form.anesthesiaStartTime" type="datetime" placeholder="选择时间" value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="麻醉结束时间">
|
||||
<el-date-picker v-model="form.anesthesiaEndTime" type="datetime" placeholder="选择时间" value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手术开始时间">
|
||||
<el-date-picker v-model="form.surgeryStartTime" type="datetime" placeholder="选择时间" value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手术结束时间">
|
||||
<el-date-picker v-model="form.surgeryEndTime" type="datetime" placeholder="选择时间" value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">术中数据</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="术中出血量(ml)">
|
||||
<el-input-number v-model="form.intraopBloodLossMl" :min="0" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="术中尿量(ml)">
|
||||
<el-input-number v-model="form.intraopUrineMl" :min="0" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="术中输液(ml)">
|
||||
<el-input-number v-model="form.intraopFluidMl" :min="0" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="输血量(ml)">
|
||||
<el-input-number v-model="form.bloodTransfusionMl" :min="0" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">气道与并发症</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="气道管理">
|
||||
<el-select v-model="form.airwayManagement" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="气管插管" value="气管插管" />
|
||||
<el-option label="喉罩" value="喉罩" />
|
||||
<el-option label="面罩" value="面罩" />
|
||||
<el-option label="其他" value="其他" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="拔管时间">
|
||||
<el-date-picker v-model="form.extubationTime" type="datetime" placeholder="选择时间" value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="患者情况">
|
||||
<el-select v-model="form.patientCondition" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="良好" value="良好" />
|
||||
<el-option label="一般" value="一般" />
|
||||
<el-option label="较差" value="较差" />
|
||||
<el-option label="危重" value="危重" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="有无并发症">
|
||||
<el-select v-model="form.hasComplications" style="width: 100%">
|
||||
<el-option label="无" :value="false" />
|
||||
<el-option label="有" :value="true" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item v-if="form.hasComplications" label="并发症详情">
|
||||
<el-input v-model="form.complications" type="textarea" :rows="3" placeholder="请描述并发症情况" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">小结</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="麻醉小结">
|
||||
<el-input v-model="form.summary" type="textarea" :rows="4" placeholder="请输入麻醉小结" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitLoading" @click="handleSave">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { saveAnesSummary, getAnesSummary } from '@/api/anesthesia'
|
||||
|
||||
const props = defineProps({
|
||||
recordId: { type: [Number, String], default: null }
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
const summaryData = ref(null)
|
||||
const dialogVisible = ref(false)
|
||||
const formRef = ref(null)
|
||||
|
||||
const form = reactive({
|
||||
id: null,
|
||||
recordId: null,
|
||||
encounterId: null,
|
||||
patientId: null,
|
||||
anesthesiaType: '',
|
||||
anesthesiaStartTime: '',
|
||||
anesthesiaEndTime: '',
|
||||
surgeryStartTime: '',
|
||||
surgeryEndTime: '',
|
||||
intraopBloodLossMl: null,
|
||||
intraopUrineMl: null,
|
||||
intraopFluidMl: null,
|
||||
bloodTransfusionMl: null,
|
||||
complications: '',
|
||||
hasComplications: false,
|
||||
airwayManagement: '',
|
||||
extubationTime: '',
|
||||
patientCondition: '',
|
||||
summary: '',
|
||||
anesthetistId: null,
|
||||
anesthetistName: ''
|
||||
})
|
||||
|
||||
const rules = {
|
||||
anesthesiaType: [{ required: true, message: '请选择麻醉类型', trigger: 'change' }],
|
||||
recordId: [{ required: true, message: '请输入麻醉记录ID', trigger: 'blur' }],
|
||||
encounterId: [{ required: true, message: '请输入就诊ID', trigger: 'blur' }],
|
||||
patientId: [{ required: true, message: '请输入患者ID', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
form.id = null
|
||||
form.recordId = null
|
||||
form.encounterId = null
|
||||
form.patientId = null
|
||||
form.anesthesiaType = ''
|
||||
form.anesthesiaStartTime = ''
|
||||
form.anesthesiaEndTime = ''
|
||||
form.surgeryStartTime = ''
|
||||
form.surgeryEndTime = ''
|
||||
form.intraopBloodLossMl = null
|
||||
form.intraopUrineMl = null
|
||||
form.intraopFluidMl = null
|
||||
form.bloodTransfusionMl = null
|
||||
form.complications = ''
|
||||
form.hasComplications = false
|
||||
form.airwayManagement = ''
|
||||
form.extubationTime = ''
|
||||
form.patientCondition = ''
|
||||
form.summary = ''
|
||||
form.anesthetistId = null
|
||||
form.anesthetistName = ''
|
||||
}
|
||||
|
||||
function handleNew() {
|
||||
resetForm()
|
||||
if (props.recordId) {
|
||||
form.recordId = Number(props.recordId)
|
||||
}
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function handleEdit(row) {
|
||||
Object.assign(form, row)
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
formRef.value?.validate(valid => {
|
||||
if (!valid) return
|
||||
submitLoading.value = true
|
||||
saveAnesSummary({ ...form }).then(res => {
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('保存成功')
|
||||
dialogVisible.value = false
|
||||
loadData()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '保存失败')
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.error('保存失败')
|
||||
}).finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function loadData() {
|
||||
if (!props.recordId) return
|
||||
loading.value = true
|
||||
getAnesSummary(props.recordId).then(res => {
|
||||
summaryData.value = res.data || null
|
||||
}).catch(() => {
|
||||
ElMessage.error('查询失败')
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
watch(() => props.recordId, () => {
|
||||
loadData()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.anes-summary-container {
|
||||
padding: 12px;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.empty-tip {
|
||||
padding: 40px 0;
|
||||
}
|
||||
.summary-content {
|
||||
padding: 12px 0;
|
||||
}
|
||||
.summary-actions {
|
||||
margin-top: 16px;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
315
healthlink-his-ui/src/views/inpatientDoctor/IntraopVitalSign.vue
Normal file
315
healthlink-his-ui/src/views/inpatientDoctor/IntraopVitalSign.vue
Normal file
@@ -0,0 +1,315 @@
|
||||
<template>
|
||||
<div class="intraop-vital-sign-container">
|
||||
<el-card v-loading="loading">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>术中生命体征监测</span>
|
||||
<div class="header-actions">
|
||||
<el-select v-model="currentRecordId" placeholder="选择麻醉记录" style="width: 220px" @change="loadTimeline">
|
||||
<el-option v-for="item in recordOptions" :key="item.id" :label="item.id + ' - ' + (item.patientName || '')" :value="item.id" />
|
||||
</el-select>
|
||||
<el-button type="primary" size="small" :disabled="!currentRecordId" @click="handleRecord">录入体征</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="!currentRecordId" class="empty-tip">
|
||||
<el-empty description="请先选择一条麻醉记录" />
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<el-table :data="timelineData" border style="width: 100%" size="small">
|
||||
<el-table-column type="index" label="序号" width="55" align="center" />
|
||||
<el-table-column prop="recordTime" label="记录时间" width="170" />
|
||||
<el-table-column prop="heartRate" label="心率(bpm)" width="95" align="center" />
|
||||
<el-table-column label="血压(mmHg)" width="120" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.bloodPressureSys }}/{{ scope.row.bloodPressureDia }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="spo2" label="SpO2(%)" width="90" align="center" />
|
||||
<el-table-column prop="etco2" label="EtCO2(mmHg)" width="105" align="center" />
|
||||
<el-table-column prop="temperature" label="体温(℃)" width="90" align="center" />
|
||||
<el-table-column prop="respiratoryRate" label="呼吸(次/分)" width="100" align="center" />
|
||||
<el-table-column prop="remark" label="备注" min-width="120" show-overflow-tooltip />
|
||||
</el-table>
|
||||
|
||||
<div v-if="timelineData.length > 0" class="trend-section">
|
||||
<div class="trend-title">趋势概览</div>
|
||||
<div class="trend-cards">
|
||||
<div class="trend-card">
|
||||
<div class="trend-label">心率</div>
|
||||
<div class="trend-range">
|
||||
<span class="trend-min">{{ minVal('heartRate') }}</span>
|
||||
<span class="trend-sep">~</span>
|
||||
<span class="trend-max">{{ maxVal('heartRate') }}</span>
|
||||
<span class="trend-unit">bpm</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="trend-card">
|
||||
<div class="trend-label">血压</div>
|
||||
<div class="trend-range">
|
||||
<span class="trend-min">{{ minVal('bloodPressureSys') }}</span>
|
||||
<span class="trend-sep">/</span>
|
||||
<span class="trend-max">{{ maxVal('bloodPressureDia') }}</span>
|
||||
<span class="trend-unit">mmHg</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="trend-card">
|
||||
<div class="trend-label">SpO2</div>
|
||||
<div class="trend-range">
|
||||
<span class="trend-min">{{ minVal('spo2') }}</span>
|
||||
<span class="trend-sep">~</span>
|
||||
<span class="trend-max">{{ maxVal('spo2') }}</span>
|
||||
<span class="trend-unit">%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="trend-card">
|
||||
<div class="trend-label">体温</div>
|
||||
<div class="trend-range">
|
||||
<span class="trend-min">{{ minVal('temperature') }}</span>
|
||||
<span class="trend-sep">~</span>
|
||||
<span class="trend-max">{{ maxVal('temperature') }}</span>
|
||||
<span class="trend-unit">℃</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="trend-card">
|
||||
<div class="trend-label">呼吸</div>
|
||||
<div class="trend-range">
|
||||
<span class="trend-min">{{ minVal('respiratoryRate') }}</span>
|
||||
<span class="trend-sep">~</span>
|
||||
<span class="trend-max">{{ maxVal('respiratoryRate') }}</span>
|
||||
<span class="trend-unit">次/分</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="recordDialogVisible" title="录入术中生命体征" width="520px" destroy-on-close :close-on-click-modal="false">
|
||||
<el-form ref="recordFormRef" :model="recordForm" :rules="recordRules" label-width="110px">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="心率(bpm)" prop="heartRate">
|
||||
<el-input-number v-model="recordForm.heartRate" :min="30" :max="250" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="收缩压(mmHg)" prop="bloodPressureSys">
|
||||
<el-input-number v-model="recordForm.bloodPressureSys" :min="50" :max="300" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="舒张压(mmHg)" prop="bloodPressureDia">
|
||||
<el-input-number v-model="recordForm.bloodPressureDia" :min="20" :max="200" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="SpO2(%)" prop="spo2">
|
||||
<el-input-number v-model="recordForm.spo2" :min="50" :max="100" :step="0.1" :precision="1" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="EtCO2(mmHg)">
|
||||
<el-input-number v-model="recordForm.etco2" :min="0" :max="100" :step="0.1" :precision="1" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="体温(℃)" prop="temperature">
|
||||
<el-input-number v-model="recordForm.temperature" :min="34" :max="42" :step="0.1" :precision="1" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="呼吸(次/分)" prop="respiratoryRate">
|
||||
<el-input-number v-model="recordForm.respiratoryRate" :min="5" :max="60" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="记录时间" prop="recordTime">
|
||||
<el-date-picker v-model="recordForm.recordTime" type="datetime" placeholder="选择时间" value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="recordForm.remark" type="textarea" :rows="2" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="recordDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitLoading" @click="submitRecord">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { getByEncounter, getVitalSignTimeline, recordVitalSign } from '@/api/anesthesia'
|
||||
|
||||
const loading = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
const currentRecordId = ref(null)
|
||||
const recordOptions = ref([])
|
||||
const timelineData = ref([])
|
||||
const recordDialogVisible = ref(false)
|
||||
const recordFormRef = ref(null)
|
||||
|
||||
const recordForm = reactive({
|
||||
heartRate: null,
|
||||
bloodPressureSys: null,
|
||||
bloodPressureDia: null,
|
||||
spo2: null,
|
||||
etco2: null,
|
||||
temperature: null,
|
||||
respiratoryRate: null,
|
||||
recordTime: '',
|
||||
remark: ''
|
||||
})
|
||||
|
||||
const recordRules = {
|
||||
heartRate: [{ required: true, message: '请输入心率', trigger: 'blur' }],
|
||||
bloodPressureSys: [{ required: true, message: '请输入收缩压', trigger: 'blur' }],
|
||||
bloodPressureDia: [{ required: true, message: '请输入舒张压', trigger: 'blur' }],
|
||||
spo2: [{ required: true, message: '请输入血氧饱和度', trigger: 'blur' }],
|
||||
temperature: [{ required: true, message: '请输入体温', trigger: 'blur' }],
|
||||
respiratoryRate: [{ required: true, message: '请输入呼吸频率', trigger: 'blur' }],
|
||||
recordTime: [{ required: true, message: '请选择记录时间', trigger: 'change' }]
|
||||
}
|
||||
|
||||
function minVal(field) {
|
||||
if (timelineData.value.length === 0) return '-'
|
||||
const vals = timelineData.value.map(item => item[field]).filter(v => v != null)
|
||||
return vals.length > 0 ? Math.min(...vals) : '-'
|
||||
}
|
||||
|
||||
function maxVal(field) {
|
||||
if (timelineData.value.length === 0) return '-'
|
||||
const vals = timelineData.value.map(item => item[field]).filter(v => v != null)
|
||||
return vals.length > 0 ? Math.max(...vals) : '-'
|
||||
}
|
||||
|
||||
function loadRecordOptions() {
|
||||
getByEncounter(1).then(res => {
|
||||
recordOptions.value = res.data || []
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
function loadTimeline() {
|
||||
if (!currentRecordId.value) return
|
||||
loading.value = true
|
||||
getVitalSignTimeline(currentRecordId.value).then(res => {
|
||||
timelineData.value = res.data || []
|
||||
}).catch(() => {
|
||||
ElMessage.error('查询时间线失败')
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function handleRecord() {
|
||||
recordForm.heartRate = null
|
||||
recordForm.bloodPressureSys = null
|
||||
recordForm.bloodPressureDia = null
|
||||
recordForm.spo2 = null
|
||||
recordForm.etco2 = null
|
||||
recordForm.temperature = null
|
||||
recordForm.respiratoryRate = null
|
||||
recordForm.recordTime = ''
|
||||
recordForm.remark = ''
|
||||
recordDialogVisible.value = true
|
||||
}
|
||||
|
||||
function submitRecord() {
|
||||
recordFormRef.value?.validate(valid => {
|
||||
if (!valid) return
|
||||
submitLoading.value = true
|
||||
const payload = {
|
||||
...recordForm,
|
||||
recordId: currentRecordId.value
|
||||
}
|
||||
recordVitalSign(payload).then(res => {
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('生命体征已记录')
|
||||
recordDialogVisible.value = false
|
||||
loadTimeline()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '记录失败')
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.error('记录失败')
|
||||
}).finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadRecordOptions()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.intraop-vital-sign-container {
|
||||
padding: 12px;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
.empty-tip {
|
||||
padding: 40px 0;
|
||||
}
|
||||
.trend-section {
|
||||
margin-top: 20px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
padding-top: 16px;
|
||||
}
|
||||
.trend-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 12px;
|
||||
color: #303133;
|
||||
}
|
||||
.trend-cards {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.trend-card {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
background: #f5f7fa;
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
.trend-label {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.trend-range {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
.trend-sep {
|
||||
color: #c0c4cc;
|
||||
margin: 0 2px;
|
||||
}
|
||||
.trend-unit {
|
||||
font-size: 11px;
|
||||
color: #909399;
|
||||
margin-left: 2px;
|
||||
}
|
||||
</style>
|
||||
281
healthlink-his-ui/src/views/inpatientDoctor/PostopFollowup.vue
Normal file
281
healthlink-his-ui/src/views/inpatientDoctor/PostopFollowup.vue
Normal file
@@ -0,0 +1,281 @@
|
||||
<template>
|
||||
<div class="postop-followup-container">
|
||||
<el-card v-loading="loading">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>术后随访记录</span>
|
||||
<el-button type="primary" size="small" @click="handleNew">新建随访</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="followupType" label="随访类型" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getTypeTag(scope.row.followupType)">{{ scope.row.followupType }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="followupTime" label="随访时间" width="170" />
|
||||
<el-table-column prop="painScore" label="疼痛评分" width="90" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getPainType(scope.row.painScore)">{{ scope.row.painScore ?? '-' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="nauseaVomiting" label="恶心呕吐" width="90" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.nauseaVomiting" type="danger">有</el-tag>
|
||||
<el-tag v-else type="success">无</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="consciousnessStatus" label="意识状态" width="100" />
|
||||
<el-table-column prop="vitalSigns" label="生命体征" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="complications" label="并发症" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="treatment" label="处理措施" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="followupDoctorName" label="随访医师" width="100" />
|
||||
<el-table-column label="操作" width="80" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="handleEdit(scope.row)">编辑</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="dialogVisible" :title="isEdit ? '编辑术后随访' : '新建术后随访'" width="750px" destroy-on-close :close-on-click-modal="false">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="110px">
|
||||
<el-divider content-position="left">基本信息</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="随访类型" prop="followupType">
|
||||
<el-select v-model="form.followupType" placeholder="请选择随访类型" style="width: 100%">
|
||||
<el-option label="24h随访" value="24h" />
|
||||
<el-option label="48h随访" value="48h" />
|
||||
<el-option label="72h随访" value="72h" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="随访时间" prop="followupTime">
|
||||
<el-date-picker v-model="form.followupTime" type="datetime" placeholder="选择随访时间" value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="就诊ID" prop="encounterId">
|
||||
<el-input-number v-model="form.encounterId" :min="1" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="患者ID" prop="patientId">
|
||||
<el-input-number v-model="form.patientId" :min="1" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="患者姓名">
|
||||
<el-input v-model="form.patientName" placeholder="请输入患者姓名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手术ID">
|
||||
<el-input-number v-model="form.surgeryId" :min="0" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">疼痛评估</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="疼痛评分" prop="painScore">
|
||||
<el-input-number v-model="form.painScore" :min="0" :max="10" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="恶心呕吐">
|
||||
<el-select v-model="form.nauseaVomiting" style="width: 100%">
|
||||
<el-option label="无" :value="false" />
|
||||
<el-option label="有" :value="true" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">术后评估</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="意识状态">
|
||||
<el-select v-model="form.consciousnessStatus" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="清醒" value="清醒" />
|
||||
<el-option label="嗜睡" value="嗜睡" />
|
||||
<el-option label="模糊" value="模糊" />
|
||||
<el-option label="昏迷" value="昏迷" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="随访医师ID">
|
||||
<el-input-number v-model="form.followupDoctorId" :min="0" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="随访医师姓名">
|
||||
<el-input v-model="form.followupDoctorName" placeholder="请输入医师姓名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="生命体征">
|
||||
<el-input v-model="form.vitalSigns" type="textarea" :rows="2" placeholder="请描述生命体征(如:T 36.5℃, P 78次/min, R 18次/min, BP 120/80mmHg)" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="并发症">
|
||||
<el-input v-model="form.complications" type="textarea" :rows="2" placeholder="请描述并发症情况" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="处理措施">
|
||||
<el-input v-model="form.treatment" type="textarea" :rows="2" placeholder="请输入处理措施" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitLoading" @click="handleSave">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { recordPostopFollowup, getPostopFollowups } from '@/api/anesthesia'
|
||||
|
||||
const props = defineProps({
|
||||
encounterId: { type: [Number, String], default: null }
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
const tableData = ref([])
|
||||
const dialogVisible = ref(false)
|
||||
const isEdit = ref(false)
|
||||
const formRef = ref(null)
|
||||
|
||||
const form = reactive({
|
||||
id: null,
|
||||
encounterId: null,
|
||||
patientId: null,
|
||||
patientName: '',
|
||||
surgeryId: null,
|
||||
followupType: '',
|
||||
followupTime: '',
|
||||
painScore: null,
|
||||
nauseaVomiting: false,
|
||||
consciousnessStatus: '',
|
||||
vitalSigns: '',
|
||||
complications: '',
|
||||
treatment: '',
|
||||
followupDoctorId: null,
|
||||
followupDoctorName: ''
|
||||
})
|
||||
|
||||
const rules = {
|
||||
followupType: [{ required: true, message: '请选择随访类型', trigger: 'change' }],
|
||||
followupTime: [{ required: true, message: '请选择随访时间', trigger: 'change' }],
|
||||
encounterId: [{ required: true, message: '请输入就诊ID', trigger: 'blur' }],
|
||||
patientId: [{ required: true, message: '请输入患者ID', trigger: 'blur' }],
|
||||
painScore: [{ required: true, message: '请输入疼痛评分', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
function getTypeTag(type) {
|
||||
const map = { '24h': 'success', '48h': 'warning', '72h': 'danger' }
|
||||
return map[type] || 'info'
|
||||
}
|
||||
|
||||
function getPainType(score) {
|
||||
if (score == null) return 'info'
|
||||
if (score <= 3) return 'success'
|
||||
if (score <= 6) return 'warning'
|
||||
return 'danger'
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
form.id = null
|
||||
form.encounterId = null
|
||||
form.patientId = null
|
||||
form.patientName = ''
|
||||
form.surgeryId = null
|
||||
form.followupType = ''
|
||||
form.followupTime = ''
|
||||
form.painScore = null
|
||||
form.nauseaVomiting = false
|
||||
form.consciousnessStatus = ''
|
||||
form.vitalSigns = ''
|
||||
form.complications = ''
|
||||
form.treatment = ''
|
||||
form.followupDoctorId = null
|
||||
form.followupDoctorName = ''
|
||||
}
|
||||
|
||||
function handleNew() {
|
||||
resetForm()
|
||||
isEdit.value = false
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function handleEdit(row) {
|
||||
Object.assign(form, row)
|
||||
isEdit.value = true
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
formRef.value?.validate(valid => {
|
||||
if (!valid) return
|
||||
submitLoading.value = true
|
||||
recordPostopFollowup({ ...form }).then(res => {
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('保存成功')
|
||||
dialogVisible.value = false
|
||||
loadData()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '保存失败')
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.error('保存失败')
|
||||
}).finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function loadData() {
|
||||
if (!props.encounterId) return
|
||||
loading.value = true
|
||||
getPostopFollowups(props.encounterId).then(res => {
|
||||
tableData.value = res.data || []
|
||||
}).catch(() => {
|
||||
ElMessage.error('查询失败')
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
watch(() => props.encounterId, () => {
|
||||
loadData()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.postop-followup-container {
|
||||
padding: 12px;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user