feat(mobile+telehealth): 移动护理评估+输液管理+互联网医院
This commit is contained in:
@@ -11,4 +11,9 @@ public interface INursingMobileAppService {
|
||||
Map<String, Object> executeOrder(Long requestId, String adviceTable, Long encounterId, Long patientId);
|
||||
NursingMobileVitalSignDto saveVitalSign(NursingMobileVitalSignDto vitalSign);
|
||||
NursingMobileVitalSignTrendDto getVitalSignTrend(Long patientId, Integer days);
|
||||
NursingMobileAssessmentDto submitAssessment(NursingMobileAssessmentDto dto);
|
||||
List<NursingMobileAssessmentDto> getAssessmentList(Long patientId);
|
||||
NursingMobileInfusionDto startInfusion(NursingMobileInfusionDto dto);
|
||||
NursingMobileInfusionDto addPatrol(NursingMobileInfusionDto dto);
|
||||
List<NursingMobileInfusionDto> getInfusionStatus(Long patientId);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
package com.healthlink.his.web.nursing.appservice.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.healthlink.his.nursing.domain.NursingAssessment;
|
||||
import com.healthlink.his.nursing.domain.NursingInfusionPatrol;
|
||||
import com.healthlink.his.nursing.domain.NursingVitalSignsChart;
|
||||
import com.healthlink.his.nursing.service.INursingAssessmentService;
|
||||
import com.healthlink.his.nursing.service.INursingInfusionPatrolService;
|
||||
import com.healthlink.his.nursing.service.INursingVitalSignsChartService;
|
||||
import com.healthlink.his.web.nursing.appservice.INursingMobileAppService;
|
||||
import com.healthlink.his.web.nursing.dto.*;
|
||||
import com.healthlink.his.web.nursing.mapper.NursingMobileAppMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -24,6 +29,14 @@ public class NursingMobileAppServiceImpl implements INursingMobileAppService {
|
||||
@Resource
|
||||
private INursingVitalSignsChartService vitalSignsChartService;
|
||||
|
||||
@Resource
|
||||
private INursingAssessmentService assessmentService;
|
||||
|
||||
@Resource
|
||||
private INursingInfusionPatrolService infusionPatrolService;
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@Override
|
||||
public List<NursingMobilePatientDto> getMobilePatientList(String wardName, String searchKey) {
|
||||
return mobileMapper.selectMobilePatientList(wardName, searchKey);
|
||||
@@ -156,4 +169,158 @@ public class NursingMobileAppServiceImpl implements INursingMobileAppService {
|
||||
|
||||
return trend;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public NursingMobileAssessmentDto submitAssessment(NursingMobileAssessmentDto dto) {
|
||||
NursingAssessment assessment = new NursingAssessment();
|
||||
assessment.setEncounterId(dto.getEncounterId());
|
||||
assessment.setPatientId(dto.getPatientId());
|
||||
assessment.setPatientName(dto.getPatientName());
|
||||
assessment.setAssessorId(dto.getAssessorId());
|
||||
assessment.setAssessorName(dto.getAssessorName());
|
||||
assessment.setAssessmentType(dto.getAssessmentType());
|
||||
assessment.setAssessmentTool(dto.getAssessmentTool());
|
||||
assessment.setTotalScore(dto.getTotalScore());
|
||||
assessment.setRiskLevel(calculateRiskLevel(dto.getAssessmentTool(), dto.getTotalScore()));
|
||||
assessment.setDetail(dto.getDetail());
|
||||
assessment.setAssessmentTime(dto.getAssessmentTime() != null ? dto.getAssessmentTime() : new Date());
|
||||
assessment.setDeleteFlag("0");
|
||||
try {
|
||||
assessment.setItemScores(dto.getItemScores() != null ? objectMapper.writeValueAsString(dto.getItemScores()) : null);
|
||||
} catch (Exception e) {
|
||||
assessment.setItemScores(null);
|
||||
}
|
||||
assessmentService.save(assessment);
|
||||
dto.setId(assessment.getId());
|
||||
dto.setRiskLevel(assessment.getRiskLevel());
|
||||
return dto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NursingMobileAssessmentDto> getAssessmentList(Long patientId) {
|
||||
LambdaQueryWrapper<NursingAssessment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(NursingAssessment::getPatientId, patientId)
|
||||
.orderByDesc(NursingAssessment::getAssessmentTime);
|
||||
List<NursingAssessment> records = assessmentService.list(wrapper);
|
||||
List<NursingMobileAssessmentDto> result = new ArrayList<>();
|
||||
for (NursingAssessment r : records) {
|
||||
NursingMobileAssessmentDto dto = new NursingMobileAssessmentDto();
|
||||
dto.setId(r.getId());
|
||||
dto.setEncounterId(r.getEncounterId());
|
||||
dto.setPatientId(r.getPatientId());
|
||||
dto.setPatientName(r.getPatientName());
|
||||
dto.setAssessorName(r.getAssessorName());
|
||||
dto.setAssessmentType(r.getAssessmentType());
|
||||
dto.setAssessmentTool(r.getAssessmentTool());
|
||||
dto.setTotalScore(r.getTotalScore());
|
||||
dto.setRiskLevel(r.getRiskLevel());
|
||||
dto.setDetail(r.getDetail());
|
||||
dto.setAssessmentTime(r.getAssessmentTime());
|
||||
try {
|
||||
if (r.getItemScores() != null) {
|
||||
dto.setItemScores(objectMapper.readValue(r.getItemScores(), Map.class));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
dto.setItemScores(null);
|
||||
}
|
||||
result.add(dto);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public NursingMobileInfusionDto startInfusion(NursingMobileInfusionDto dto) {
|
||||
NursingInfusionPatrol patrol = new NursingInfusionPatrol();
|
||||
patrol.setEncounterId(dto.getEncounterId());
|
||||
patrol.setPatientId(dto.getPatientId());
|
||||
patrol.setPatientName(dto.getPatientName());
|
||||
patrol.setOrderId(dto.getOrderId());
|
||||
patrol.setDrugName(dto.getDrugName());
|
||||
patrol.setInfusionRate(dto.getInfusionRate());
|
||||
patrol.setTotalVolume(dto.getTotalVolume());
|
||||
patrol.setStartTime(dto.getStartTime() != null ? dto.getStartTime() : new Date());
|
||||
patrol.setPatencyStatus("NORMAL");
|
||||
patrol.setPatrolNurseId(dto.getPatrolNurseId());
|
||||
patrol.setPatrolNurseName(dto.getPatrolNurseName());
|
||||
patrol.setCreateTime(new Date());
|
||||
infusionPatrolService.save(patrol);
|
||||
dto.setId(patrol.getId());
|
||||
dto.setPatencyStatus("NORMAL");
|
||||
dto.setStatus("RUNNING");
|
||||
return dto;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public NursingMobileInfusionDto addPatrol(NursingMobileInfusionDto dto) {
|
||||
NursingInfusionPatrol patrol = new NursingInfusionPatrol();
|
||||
patrol.setEncounterId(dto.getEncounterId());
|
||||
patrol.setPatientId(dto.getPatientId());
|
||||
patrol.setPatientName(dto.getPatientName());
|
||||
patrol.setOrderId(dto.getOrderId());
|
||||
patrol.setDrugName(dto.getDrugName());
|
||||
patrol.setPatrolTime(new Date());
|
||||
patrol.setDripRate(dto.getDripRate());
|
||||
patrol.setPatencyStatus(dto.getPatencyStatus());
|
||||
patrol.setAdverseReaction(dto.getAdverseReaction());
|
||||
patrol.setPatrolNurseId(dto.getPatrolNurseId());
|
||||
patrol.setPatrolNurseName(dto.getPatrolNurseName());
|
||||
patrol.setCreateTime(new Date());
|
||||
infusionPatrolService.save(patrol);
|
||||
dto.setId(patrol.getId());
|
||||
dto.setPatrolTime(patrol.getPatrolTime());
|
||||
return dto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NursingMobileInfusionDto> getInfusionStatus(Long patientId) {
|
||||
LambdaQueryWrapper<NursingInfusionPatrol> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(NursingInfusionPatrol::getPatientId, patientId)
|
||||
.orderByDesc(NursingInfusionPatrol::getStartTime);
|
||||
List<NursingInfusionPatrol> records = infusionPatrolService.list(wrapper);
|
||||
Map<Long, NursingMobileInfusionDto> latestMap = new LinkedHashMap<>();
|
||||
for (NursingInfusionPatrol r : records) {
|
||||
Long orderId = r.getOrderId();
|
||||
if (orderId == null) orderId = r.getId();
|
||||
if (!latestMap.containsKey(orderId)) {
|
||||
NursingMobileInfusionDto dto = new NursingMobileInfusionDto();
|
||||
dto.setId(r.getId());
|
||||
dto.setEncounterId(r.getEncounterId());
|
||||
dto.setPatientId(r.getPatientId());
|
||||
dto.setPatientName(r.getPatientName());
|
||||
dto.setOrderId(r.getOrderId());
|
||||
dto.setDrugName(r.getDrugName());
|
||||
dto.setInfusionRate(r.getInfusionRate());
|
||||
dto.setTotalVolume(r.getTotalVolume());
|
||||
dto.setStartTime(r.getStartTime());
|
||||
dto.setPatrolTime(r.getPatrolTime());
|
||||
dto.setDripRate(r.getDripRate());
|
||||
dto.setPatencyStatus(r.getPatencyStatus());
|
||||
dto.setAdverseReaction(r.getAdverseReaction());
|
||||
dto.setPatrolNurseName(r.getPatrolNurseName());
|
||||
dto.setStatus("RUNNING");
|
||||
latestMap.put(orderId, dto);
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(latestMap.values());
|
||||
}
|
||||
|
||||
private String calculateRiskLevel(String tool, Integer score) {
|
||||
if (score == null) return "NORMAL";
|
||||
if ("BRADEN".equals(tool)) {
|
||||
if (score <= 12) return "HIGH";
|
||||
if (score <= 14) return "MEDIUM";
|
||||
return "LOW";
|
||||
} else if ("MORSE".equals(tool)) {
|
||||
if (score >= 45) return "HIGH";
|
||||
if (score >= 25) return "MEDIUM";
|
||||
return "LOW";
|
||||
} else if ("NRS2002".equals(tool)) {
|
||||
if (score >= 3) return "HIGH";
|
||||
return "LOW";
|
||||
}
|
||||
return "NORMAL";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,4 +69,44 @@ public class NursingMobileController {
|
||||
NursingMobileVitalSignTrendDto trend = mobileAppService.getVitalSignTrend(patientId, days);
|
||||
return R.ok(trend);
|
||||
}
|
||||
|
||||
@Operation(summary = "提交护理评估")
|
||||
@PostMapping("/assessment/submit")
|
||||
@PreAuthorize("hasAuthority('nursing:nursing:edit')")
|
||||
public R<?> submitAssessment(@RequestBody NursingMobileAssessmentDto assessment) {
|
||||
NursingMobileAssessmentDto saved = mobileAppService.submitAssessment(assessment);
|
||||
return R.ok(saved);
|
||||
}
|
||||
|
||||
@Operation(summary = "查询评估记录")
|
||||
@GetMapping("/assessment/list/{patientId}")
|
||||
@PreAuthorize("hasAuthority('nursing:nursing:list')")
|
||||
public R<?> getAssessmentList(@PathVariable Long patientId) {
|
||||
List<NursingMobileAssessmentDto> list = mobileAppService.getAssessmentList(patientId);
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
@Operation(summary = "开始输液")
|
||||
@PostMapping("/infusion/start")
|
||||
@PreAuthorize("hasAuthority('nursing:nursing:edit')")
|
||||
public R<?> startInfusion(@RequestBody NursingMobileInfusionDto infusion) {
|
||||
NursingMobileInfusionDto saved = mobileAppService.startInfusion(infusion);
|
||||
return R.ok(saved);
|
||||
}
|
||||
|
||||
@Operation(summary = "输液巡视记录")
|
||||
@PostMapping("/infusion/patrol")
|
||||
@PreAuthorize("hasAuthority('nursing:nursing:edit')")
|
||||
public R<?> addPatrol(@RequestBody NursingMobileInfusionDto patrol) {
|
||||
NursingMobileInfusionDto saved = mobileAppService.addPatrol(patrol);
|
||||
return R.ok(saved);
|
||||
}
|
||||
|
||||
@Operation(summary = "输液状态查询")
|
||||
@GetMapping("/infusion/status/{patientId}")
|
||||
@PreAuthorize("hasAuthority('nursing:nursing:list')")
|
||||
public R<?> getInfusionStatus(@PathVariable Long patientId) {
|
||||
List<NursingMobileInfusionDto> list = mobileAppService.getInfusionStatus(patientId);
|
||||
return R.ok(list);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.healthlink.his.web.nursing.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import lombok.Data;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class NursingMobileAssessmentDto {
|
||||
private Long id;
|
||||
private Long encounterId;
|
||||
private Long patientId;
|
||||
private String patientName;
|
||||
private Long assessorId;
|
||||
private String assessorName;
|
||||
private String assessmentType;
|
||||
private String assessmentTool;
|
||||
private Integer totalScore;
|
||||
private String riskLevel;
|
||||
private Map<String, Integer> itemScores;
|
||||
private String detail;
|
||||
private Date assessmentTime;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.healthlink.his.web.nursing.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import lombok.Data;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class NursingMobileInfusionDto {
|
||||
private Long id;
|
||||
private Long encounterId;
|
||||
private Long patientId;
|
||||
private String patientName;
|
||||
private Long orderId;
|
||||
private String drugName;
|
||||
private String infusionRate;
|
||||
private Integer totalVolume;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date startTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date patrolTime;
|
||||
private Integer dripRate;
|
||||
private String patencyStatus;
|
||||
private String adverseReaction;
|
||||
private Long patrolNurseId;
|
||||
private String patrolNurseName;
|
||||
private String status;
|
||||
private Integer remainingVolume;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.healthlink.his.web.telehealth.appservice;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.healthlink.his.web.telehealth.dto.TelehealthConsultationDto;
|
||||
|
||||
public interface ITelehealthAppService {
|
||||
|
||||
Long createConsultation(TelehealthConsultationDto dto);
|
||||
|
||||
Page<TelehealthConsultationDto> pageConsultation(TelehealthConsultationDto dto);
|
||||
|
||||
Boolean replyConsultation(TelehealthConsultationDto dto);
|
||||
|
||||
Boolean prescribeConsultation(TelehealthConsultationDto dto);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.healthlink.his.web.telehealth.appservice.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.core.common.utils.SecurityUtils;
|
||||
import com.healthlink.his.web.telehealth.appservice.ITelehealthAppService;
|
||||
import com.healthlink.his.web.telehealth.domain.TelehealthConsultation;
|
||||
import com.healthlink.his.web.telehealth.dto.TelehealthConsultationDto;
|
||||
import com.healthlink.his.web.telehealth.mapper.TelehealthConsultationMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class TelehealthAppServiceImpl implements ITelehealthAppService {
|
||||
|
||||
@Resource
|
||||
private TelehealthConsultationMapper telehealthConsultationMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createConsultation(TelehealthConsultationDto dto) {
|
||||
TelehealthConsultation entity = new TelehealthConsultation();
|
||||
entity.setPatientId(dto.getPatientId());
|
||||
entity.setDoctorId(dto.getDoctorId());
|
||||
entity.setConsultationType(dto.getConsultationType());
|
||||
entity.setStatus("PENDING");
|
||||
entity.setChiefComplaint(dto.getChiefComplaint());
|
||||
entity.setConsultationTime(new Date());
|
||||
entity.setTenantId(SecurityUtils.getLoginUser().getTenantId());
|
||||
telehealthConsultationMapper.insert(entity);
|
||||
return entity.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<TelehealthConsultationDto> pageConsultation(TelehealthConsultationDto dto) {
|
||||
Page<TelehealthConsultation> page = new Page<>(dto.getPageNum(), dto.getPageSize());
|
||||
LambdaQueryWrapper<TelehealthConsultation> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
if (StringUtils.hasText(dto.getStatus())) {
|
||||
wrapper.eq(TelehealthConsultation::getStatus, dto.getStatus());
|
||||
}
|
||||
if (dto.getDoctorId() != null) {
|
||||
wrapper.eq(TelehealthConsultation::getDoctorId, dto.getDoctorId());
|
||||
}
|
||||
if (dto.getPatientId() != null) {
|
||||
wrapper.eq(TelehealthConsultation::getPatientId, dto.getPatientId());
|
||||
}
|
||||
wrapper.orderByDesc(TelehealthConsultation::getCreateTime);
|
||||
|
||||
Page<TelehealthConsultation> result = telehealthConsultationMapper.selectPage(page, wrapper);
|
||||
|
||||
Page<TelehealthConsultationDto> dtoPage = new Page<>(result.getCurrent(), result.getSize(), result.getTotal());
|
||||
List<TelehealthConsultationDto> dtoList = result.getRecords().stream()
|
||||
.map(this::toDto)
|
||||
.collect(Collectors.toList());
|
||||
dtoPage.setRecords(dtoList);
|
||||
return dtoPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean replyConsultation(TelehealthConsultationDto dto) {
|
||||
TelehealthConsultation entity = telehealthConsultationMapper.selectById(dto.getId());
|
||||
if (entity == null) {
|
||||
throw new RuntimeException("问诊记录不存在");
|
||||
}
|
||||
entity.setDiagnosis(dto.getDiagnosis());
|
||||
entity.setStatus("IN_PROGRESS");
|
||||
telehealthConsultationMapper.updateById(entity);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean prescribeConsultation(TelehealthConsultationDto dto) {
|
||||
TelehealthConsultation entity = telehealthConsultationMapper.selectById(dto.getId());
|
||||
if (entity == null) {
|
||||
throw new RuntimeException("问诊记录不存在");
|
||||
}
|
||||
entity.setPrescription(dto.getPrescription());
|
||||
entity.setDiagnosis(dto.getDiagnosis());
|
||||
entity.setStatus("COMPLETED");
|
||||
entity.setEndTime(new Date());
|
||||
telehealthConsultationMapper.updateById(entity);
|
||||
return true;
|
||||
}
|
||||
|
||||
private TelehealthConsultationDto toDto(TelehealthConsultation entity) {
|
||||
TelehealthConsultationDto dto = new TelehealthConsultationDto();
|
||||
BeanUtils.copyProperties(entity, dto);
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.healthlink.his.web.telehealth.controller;
|
||||
|
||||
import com.core.common.core.domain.R;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.healthlink.his.web.telehealth.appservice.ITelehealthAppService;
|
||||
import com.healthlink.his.web.telehealth.dto.TelehealthConsultationDto;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@Slf4j
|
||||
@Tag(name = "互联网医院-在线问诊")
|
||||
@RestController
|
||||
@RequestMapping("/telehealth/consultation")
|
||||
public class TelehealthController {
|
||||
|
||||
@Resource
|
||||
private ITelehealthAppService telehealthAppService;
|
||||
|
||||
@Operation(summary = "创建问诊")
|
||||
@PostMapping("/create")
|
||||
@PreAuthorize("@ss.hasPermi('outpatient:telehealth:edit')")
|
||||
public R<Long> create(@RequestBody TelehealthConsultationDto dto) {
|
||||
try {
|
||||
Long id = telehealthAppService.createConsultation(dto);
|
||||
return R.ok(id);
|
||||
} catch (Exception e) {
|
||||
log.error("创建问诊失败", e);
|
||||
return R.fail("创建问诊失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "问诊列表")
|
||||
@GetMapping("/page")
|
||||
@PreAuthorize("@ss.hasPermi('outpatient:telehealth:list')")
|
||||
public R<Page<TelehealthConsultationDto>> page(TelehealthConsultationDto dto) {
|
||||
try {
|
||||
Page<TelehealthConsultationDto> result = telehealthAppService.pageConsultation(dto);
|
||||
return R.ok(result);
|
||||
} catch (Exception e) {
|
||||
log.error("查询问诊列表失败", e);
|
||||
return R.fail("查询问诊列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "医生回复")
|
||||
@PostMapping("/reply")
|
||||
@PreAuthorize("@ss.hasPermi('outpatient:telehealth:edit')")
|
||||
public R<String> reply(@RequestBody TelehealthConsultationDto dto) {
|
||||
try {
|
||||
telehealthAppService.replyConsultation(dto);
|
||||
return R.ok("回复成功");
|
||||
} catch (Exception e) {
|
||||
log.error("医生回复失败", e);
|
||||
return R.fail("回复失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "复诊开方")
|
||||
@PostMapping("/prescribe")
|
||||
@PreAuthorize("@ss.hasPermi('outpatient:telehealth:edit')")
|
||||
public R<String> prescribe(@RequestBody TelehealthConsultationDto dto) {
|
||||
try {
|
||||
telehealthAppService.prescribeConsultation(dto);
|
||||
return R.ok("开方成功");
|
||||
} catch (Exception e) {
|
||||
log.error("复诊开方失败", e);
|
||||
return R.fail("开方失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.healthlink.his.web.telehealth.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.core.common.core.domain.HisBaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("telehealth_consultation")
|
||||
public class TelehealthConsultation extends HisBaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
@TableField("patient_id")
|
||||
private Long patientId;
|
||||
|
||||
@TableField("doctor_id")
|
||||
private Long doctorId;
|
||||
|
||||
@TableField("consultation_type")
|
||||
private String consultationType;
|
||||
|
||||
@TableField("status")
|
||||
private String status;
|
||||
|
||||
@TableField("chief_complaint")
|
||||
private String chiefComplaint;
|
||||
|
||||
@TableField("diagnosis")
|
||||
private String diagnosis;
|
||||
|
||||
@TableField("prescription")
|
||||
private String prescription;
|
||||
|
||||
@TableField("consultation_time")
|
||||
private Date consultationTime;
|
||||
|
||||
@TableField("end_time")
|
||||
private Date endTime;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.healthlink.his.web.telehealth.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
public class TelehealthConsultationDto {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long patientId;
|
||||
|
||||
private String patientName;
|
||||
|
||||
private Long doctorId;
|
||||
|
||||
private String doctorName;
|
||||
|
||||
private String consultationType;
|
||||
|
||||
private String status;
|
||||
|
||||
private String chiefComplaint;
|
||||
|
||||
private String diagnosis;
|
||||
|
||||
private String prescription;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date consultationTime;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date endTime;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
private Integer pageNum = 1;
|
||||
|
||||
private Integer pageSize = 10;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.healthlink.his.web.telehealth.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.web.telehealth.domain.TelehealthConsultation;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface TelehealthConsultationMapper extends BaseMapper<TelehealthConsultation> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
-- V85: 互联网医院 - 在线问诊+复诊开方
|
||||
CREATE TABLE IF NOT EXISTS telehealth_consultation (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
patient_id BIGINT NOT NULL,
|
||||
doctor_id BIGINT NOT NULL,
|
||||
consultation_type VARCHAR(20) NOT NULL,
|
||||
status VARCHAR(20) DEFAULT 'PENDING',
|
||||
chief_complaint TEXT,
|
||||
diagnosis TEXT,
|
||||
prescription TEXT,
|
||||
consultation_time TIMESTAMP,
|
||||
end_time TIMESTAMP,
|
||||
tenant_id BIGINT DEFAULT 0,
|
||||
delete_flag CHAR(1) DEFAULT '0',
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
create_by VARCHAR(64),
|
||||
update_time TIMESTAMP,
|
||||
update_by VARCHAR(64)
|
||||
);
|
||||
33
healthlink-his-ui/src/api/telehealth/index.js
Normal file
33
healthlink-his-ui/src/api/telehealth/index.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function createConsultation(data) {
|
||||
return request({
|
||||
url: '/telehealth/consultation/create',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function pageConsultation(query) {
|
||||
return request({
|
||||
url: '/telehealth/consultation/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function replyConsultation(data) {
|
||||
return request({
|
||||
url: '/telehealth/consultation/reply',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function prescribeConsultation(data) {
|
||||
return request({
|
||||
url: '/telehealth/consultation/prescribe',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
320
healthlink-his-ui/src/views/nursingmobile/InfusionManagement.vue
Normal file
320
healthlink-his-ui/src/views/nursingmobile/InfusionManagement.vue
Normal file
@@ -0,0 +1,320 @@
|
||||
<template>
|
||||
<div class="mobile-infusion">
|
||||
<div class="page-header">
|
||||
<el-page-header @back="goBack">
|
||||
<template #content>
|
||||
<span class="page-title">{{ patientName }} - 输液管理</span>
|
||||
</template>
|
||||
</el-page-header>
|
||||
<el-button type="primary" size="small" @click="showStartDialog">开始输液</el-button>
|
||||
</div>
|
||||
|
||||
<div v-loading="loading" class="infusion-list">
|
||||
<div
|
||||
v-for="item in infusionList"
|
||||
:key="item.id"
|
||||
class="infusion-card"
|
||||
>
|
||||
<div class="card-header">
|
||||
<span class="drug-name">{{ item.drugName || '输液' }}</span>
|
||||
<el-tag :type="getStatusType(item.patencyStatus)" size="small">
|
||||
{{ getStatusText(item.patencyStatus) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="info-row">
|
||||
<span class="label">输液速度:</span>
|
||||
<span class="value">{{ item.infusionRate || '-' }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="label">总量:</span>
|
||||
<span class="value">{{ item.totalVolume ? item.totalVolume + 'ml' : '-' }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="label">开始时间:</span>
|
||||
<span class="value">{{ formatTime(item.startTime) }}</span>
|
||||
</div>
|
||||
<div class="info-row" v-if="item.patrolTime">
|
||||
<span class="label">最近巡视:</span>
|
||||
<span class="value">{{ formatTime(item.patrolTime) }}</span>
|
||||
</div>
|
||||
<div class="info-row" v-if="item.dripRate">
|
||||
<span class="label">滴速:</span>
|
||||
<span class="value">{{ item.dripRate }} 滴/分</span>
|
||||
</div>
|
||||
<div class="info-row" v-if="item.adverseReaction">
|
||||
<span class="label">不良反应:</span>
|
||||
<span class="value danger-text">{{ item.adverseReaction }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<el-button size="small" @click="showPatrolDialog(item)">巡视记录</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-empty v-if="!loading && infusionList.length === 0" description="暂无输液记录" />
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="startDialogVisible" title="开始输液" width="400px">
|
||||
<el-form :model="startForm" label-width="80px">
|
||||
<el-form-item label="药品名称">
|
||||
<el-input v-model="startForm.drugName" placeholder="请输入药品名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="输液速度">
|
||||
<el-input v-model="startForm.infusionRate" placeholder="如: 40滴/分" />
|
||||
</el-form-item>
|
||||
<el-form-item label="总量(ml)">
|
||||
<el-input-number v-model="startForm.totalVolume" :min="0" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="护士姓名">
|
||||
<el-input v-model="startForm.patrolNurseName" placeholder="执行护士" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="startDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="handleStartInfusion">确认开始</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="patrolDialogVisible" title="输液巡视" width="400px">
|
||||
<el-form :model="patrolForm" label-width="80px">
|
||||
<el-form-item label="滴速(滴/分)">
|
||||
<el-input-number v-model="patrolForm.dripRate" :min="0" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="通畅状态">
|
||||
<el-select v-model="patrolForm.patencyStatus" style="width: 100%">
|
||||
<el-option label="正常" value="NORMAL" />
|
||||
<el-option label="堵塞" value="OCCLUDED" />
|
||||
<el-option label="静脉炎" value="PHLEBITIS" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="不良反应">
|
||||
<el-input v-model="patrolForm.adverseReaction" placeholder="无则留空" />
|
||||
</el-form-item>
|
||||
<el-form-item label="巡视护士">
|
||||
<el-input v-model="patrolForm.patrolNurseName" placeholder="巡视护士" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="patrolDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="handlePatrol">确认记录</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { getInfusionStatus, startInfusion, addInfusionPatrol } from './api'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const loading = ref(false)
|
||||
const submitting = ref(false)
|
||||
|
||||
const patientName = ref(route.query.patientName || '')
|
||||
const patientId = ref(route.query.patientId)
|
||||
const encounterId = ref(route.query.encounterId)
|
||||
|
||||
const infusionList = ref([])
|
||||
const startDialogVisible = ref(false)
|
||||
const patrolDialogVisible = ref(false)
|
||||
const currentInfusion = ref(null)
|
||||
|
||||
const startForm = reactive({
|
||||
drugName: '',
|
||||
infusionRate: '',
|
||||
totalVolume: 250,
|
||||
patrolNurseName: ''
|
||||
})
|
||||
|
||||
const patrolForm = reactive({
|
||||
dripRate: null,
|
||||
patencyStatus: 'NORMAL',
|
||||
adverseReaction: '',
|
||||
patrolNurseName: ''
|
||||
})
|
||||
|
||||
const goBack = () => {
|
||||
router.push('/nursingmobile/patient-list')
|
||||
}
|
||||
|
||||
const formatTime = (time) => {
|
||||
if (!time) return '-'
|
||||
const d = new Date(time)
|
||||
return d.toLocaleString('zh-CN')
|
||||
}
|
||||
|
||||
const getStatusType = (status) => {
|
||||
const map = { NORMAL: 'success', OCCLUDED: 'danger', PHLEBITIS: 'warning' }
|
||||
return map[status] || 'info'
|
||||
}
|
||||
|
||||
const getStatusText = (status) => {
|
||||
const map = { NORMAL: '正常', OCCLUDED: '堵塞', PHLEBITIS: '静脉炎' }
|
||||
return map[status] || '未知'
|
||||
}
|
||||
|
||||
const fetchInfusionList = async () => {
|
||||
if (!patientId.value) return
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getInfusionStatus(patientId.value)
|
||||
infusionList.value = res.data || []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const showStartDialog = () => {
|
||||
startForm.drugName = ''
|
||||
startForm.infusionRate = ''
|
||||
startForm.totalVolume = 250
|
||||
startForm.patrolNurseName = ''
|
||||
startDialogVisible.value = true
|
||||
}
|
||||
|
||||
const showPatrolDialog = (item) => {
|
||||
currentInfusion.value = item
|
||||
patrolForm.dripRate = item.dripRate || null
|
||||
patrolForm.patencyStatus = item.patencyStatus || 'NORMAL'
|
||||
patrolForm.adverseReaction = ''
|
||||
patrolForm.patrolNurseName = ''
|
||||
patrolDialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleStartInfusion = async () => {
|
||||
if (!startForm.drugName) {
|
||||
ElMessage.warning('请输入药品名称')
|
||||
return
|
||||
}
|
||||
submitting.value = true
|
||||
try {
|
||||
await startInfusion({
|
||||
patientId: patientId.value ? Number(patientId.value) : null,
|
||||
encounterId: encounterId.value ? Number(encounterId.value) : null,
|
||||
patientName: patientName.value,
|
||||
drugName: startForm.drugName,
|
||||
infusionRate: startForm.infusionRate,
|
||||
totalVolume: startForm.totalVolume,
|
||||
patrolNurseName: startForm.patrolNurseName,
|
||||
startTime: new Date()
|
||||
})
|
||||
ElMessage.success('输液已开始')
|
||||
startDialogVisible.value = false
|
||||
fetchInfusionList()
|
||||
} catch (e) {
|
||||
ElMessage.error('操作失败')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handlePatrol = async () => {
|
||||
submitting.value = true
|
||||
try {
|
||||
await addInfusionPatrol({
|
||||
patientId: patientId.value ? Number(patientId.value) : null,
|
||||
encounterId: encounterId.value ? Number(encounterId.value) : null,
|
||||
patientName: patientName.value,
|
||||
orderId: currentInfusion.value.orderId || currentInfusion.value.id,
|
||||
drugName: currentInfusion.value.drugName,
|
||||
dripRate: patrolForm.dripRate,
|
||||
patencyStatus: patrolForm.patencyStatus,
|
||||
adverseReaction: patrolForm.adverseReaction,
|
||||
patrolNurseName: patrolForm.patrolNurseName,
|
||||
patrolTime: new Date()
|
||||
})
|
||||
ElMessage.success('巡视记录已保存')
|
||||
patrolDialogVisible.value = false
|
||||
fetchInfusionList()
|
||||
} catch (e) {
|
||||
ElMessage.error('记录失败')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchInfusionList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mobile-infusion {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.infusion-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.infusion-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.drug-name {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.info-row .label {
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
min-width: 70px;
|
||||
}
|
||||
|
||||
.info-row .value {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.danger-text {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
margin-top: 12px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid #eee;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
395
healthlink-his-ui/src/views/nursingmobile/NursingAssessment.vue
Normal file
395
healthlink-his-ui/src/views/nursingmobile/NursingAssessment.vue
Normal file
@@ -0,0 +1,395 @@
|
||||
<template>
|
||||
<div class="mobile-assessment">
|
||||
<div class="page-header">
|
||||
<el-page-header @back="goBack">
|
||||
<template #content>
|
||||
<span class="page-title">{{ patientName }} - 护理评估</span>
|
||||
</template>
|
||||
</el-page-header>
|
||||
</div>
|
||||
|
||||
<div class="tool-selector">
|
||||
<el-radio-group v-model="selectedTool" size="small" @change="onToolChange">
|
||||
<el-radio-button value="BRADEN">Braden压疮</el-radio-button>
|
||||
<el-radio-button value="MORSE">Morse跌倒</el-radio-button>
|
||||
<el-radio-button value="NRS2002">NRS2002营养</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
|
||||
<el-card class="assessment-form" v-loading="submitting">
|
||||
<template #header>
|
||||
<span>{{ toolConfig.title }}</span>
|
||||
</template>
|
||||
|
||||
<div class="items-list">
|
||||
<div v-for="(item, index) in toolConfig.items" :key="index" class="assessment-item">
|
||||
<div class="item-label">{{ item.label }}</div>
|
||||
<div class="item-options">
|
||||
<el-radio-group v-model="itemScores[item.key]" size="small">
|
||||
<el-radio-button
|
||||
v-for="opt in item.options"
|
||||
:key="opt.value"
|
||||
:value="opt.value"
|
||||
>
|
||||
{{ opt.label }} ({{ opt.value }}分)
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="score-summary">
|
||||
<div class="total-score">
|
||||
总分: <span class="score-value">{{ totalScore }}</span>
|
||||
</div>
|
||||
<div class="risk-level">
|
||||
风险等级:
|
||||
<el-tag :type="getRiskType(currentRiskLevel)" size="small">
|
||||
{{ getRiskText(currentRiskLevel) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-form-item label="评估备注" style="margin-top: 12px;">
|
||||
<el-input v-model="detail" type="textarea" :rows="2" placeholder="可选备注" />
|
||||
</el-form-item>
|
||||
|
||||
<el-button type="primary" style="width: 100%" @click="handleSubmit">
|
||||
提交评估
|
||||
</el-button>
|
||||
</el-card>
|
||||
|
||||
<el-card class="history-section" v-loading="historyLoading">
|
||||
<template #header>
|
||||
<span>评估记录</span>
|
||||
</template>
|
||||
<div v-for="record in historyList" :key="record.id" class="history-item">
|
||||
<div class="history-header">
|
||||
<span class="history-tool">{{ getToolName(record.assessmentTool) }}</span>
|
||||
<el-tag :type="getRiskType(record.riskLevel)" size="small">
|
||||
{{ getRiskText(record.riskLevel) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="history-body">
|
||||
<span class="history-score">评分: {{ record.totalScore }}</span>
|
||||
<span class="history-time">{{ formatTime(record.assessmentTime) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-if="!historyLoading && historyList.length === 0" description="暂无评估记录" />
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { submitAssessment, getAssessmentList } from './api'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const submitting = ref(false)
|
||||
const historyLoading = ref(false)
|
||||
|
||||
const patientName = ref(route.query.patientName || '')
|
||||
const patientId = ref(route.query.patientId)
|
||||
const encounterId = ref(route.query.encounterId)
|
||||
|
||||
const selectedTool = ref('BRADEN')
|
||||
const detail = ref('')
|
||||
const historyList = ref([])
|
||||
|
||||
const bradenItems = [
|
||||
{ key: 'sensory', label: '感觉感知', options: [
|
||||
{ label: '完全受限', value: 1 }, { label: '严重受限', value: 2 },
|
||||
{ label: '轻度受限', value: 3 }, { label: '未受损', value: 4 }
|
||||
]},
|
||||
{ key: 'moisture', label: '潮湿程度', options: [
|
||||
{ label: '持续潮湿', value: 1 }, { label: '经常潮湿', value: 2 },
|
||||
{ label: '有时潮湿', value: 3 }, { label: '很少潮湿', value: 4 }
|
||||
]},
|
||||
{ key: 'activity', label: '活动能力', options: [
|
||||
{ label: '卧床不起', value: 1 }, { label: '仅限于椅', value: 2 },
|
||||
{ label: '偶尔步行', value: 3 }, { label: '经常步行', value: 4 }
|
||||
]},
|
||||
{ key: 'mobility', label: '移动能力', options: [
|
||||
{ label: '完全不动', value: 1 }, { label: '严重受限', value: 2 },
|
||||
{ label: '轻度受限', value: 3 }, { label: '不受限', value: 4 }
|
||||
]},
|
||||
{ key: 'nutrition', label: '营养摄取', options: [
|
||||
{ label: '非常差', value: 1 }, { label: '可能不足', value: 2 },
|
||||
{ label: '充足', value: 3 }, { label: '丰富', value: 4 }
|
||||
]},
|
||||
{ key: 'friction', label: '摩擦力和剪切力', options: [
|
||||
{ label: '存在问题', value: 1 }, { label: '有潜在问题', value: 2 },
|
||||
{ label: '无明显问题', value: 3 }
|
||||
]}
|
||||
]
|
||||
|
||||
const morseItems = [
|
||||
{ key: 'fallHistory', label: '跌倒史', options: [
|
||||
{ label: '无', value: 0 }, { label: '有', value: 25 }
|
||||
]},
|
||||
{ key: 'diagnosis', label: '超过一个医学诊断', options: [
|
||||
{ label: '无', value: 0 }, { label: '有', value: 15 }
|
||||
]},
|
||||
{ key: 'ambulation', label: '行走辅助', options: [
|
||||
{ label: '卧床/轮椅', value: 0 }, { label: '拐杖/助行器', value: 15 },
|
||||
{ label: '扶家具', value: 30 }
|
||||
]},
|
||||
{ key: 'ivTherapy', label: '静脉输液', options: [
|
||||
{ label: '无', value: 0 }, { label: '有', value: 20 }
|
||||
]},
|
||||
{ key: 'gait', label: '步态', options: [
|
||||
{ label: '正常/卧床/轮椅', value: 0 }, { label: '虚弱', value: 10 },
|
||||
{ label: '受损', value: 20 }
|
||||
]},
|
||||
{ key: 'mental', label: '精神状态', options: [
|
||||
{ label: '正确评估自身能力', value: 0 }, { label: '高估或忘记限制', value: 15 }
|
||||
]}
|
||||
]
|
||||
|
||||
const nrs2002Items = [
|
||||
{ key: 'bmi', label: 'BMI(kg/m²)', options: [
|
||||
{ label: '<18.5', value: 3 }, { label: '18.5-20.5', value: 1 },
|
||||
{ label: '>20.5', value: 0 }
|
||||
]},
|
||||
{ key: 'weightLoss', label: '体重下降', options: [
|
||||
{ label: '>10%/月', value: 3 }, { label: '5-10%/月', value: 2 },
|
||||
{ label: '2-5%/月', value: 1 }, { label: '无/1月', value: 0 }
|
||||
]},
|
||||
{ key: 'intake', label: '饮食摄入', options: [
|
||||
{ label: '无', value: 3 }, { label: '差', value: 2 },
|
||||
{ label: '中等', value: 1 }, { label: '好', value: 0 }
|
||||
]},
|
||||
{ key: 'illness', label: '疾病严重程度', options: [
|
||||
{ label: '大手术/创伤', value: 3 }, { label: '骨盆骨折', value: 2 },
|
||||
{ label: '慢性病急性发作', value: 1 }, { label: '无/轻度', value: 0 }
|
||||
]}
|
||||
]
|
||||
|
||||
const toolConfigs = {
|
||||
BRADEN: { title: 'Braden压疮风险评估', items: bradenItems },
|
||||
MORSE: { title: 'Morse跌倒风险评估', items: morseItems },
|
||||
NRS2002: { title: 'NRS2002营养风险筛查', items: nrs2002Items }
|
||||
}
|
||||
|
||||
const itemScores = reactive({})
|
||||
|
||||
const toolConfig = computed(() => toolConfigs[selectedTool.value])
|
||||
|
||||
const totalScore = computed(() => {
|
||||
let sum = 0
|
||||
for (const item of toolConfig.value.items) {
|
||||
sum += itemScores[item.key] || 0
|
||||
}
|
||||
return sum
|
||||
})
|
||||
|
||||
const currentRiskLevel = computed(() => {
|
||||
const tool = selectedTool.value
|
||||
const score = totalScore.value
|
||||
if (tool === 'BRADEN') {
|
||||
if (score <= 12) return 'HIGH'
|
||||
if (score <= 14) return 'MEDIUM'
|
||||
return 'LOW'
|
||||
} else if (tool === 'MORSE') {
|
||||
if (score >= 45) return 'HIGH'
|
||||
if (score >= 25) return 'MEDIUM'
|
||||
return 'LOW'
|
||||
} else if (tool === 'NRS2002') {
|
||||
if (score >= 3) return 'HIGH'
|
||||
return 'LOW'
|
||||
}
|
||||
return 'NORMAL'
|
||||
})
|
||||
|
||||
const onToolChange = () => {
|
||||
for (const key of Object.keys(itemScores)) {
|
||||
delete itemScores[key]
|
||||
}
|
||||
}
|
||||
|
||||
const getRiskType = (level) => {
|
||||
const map = { HIGH: 'danger', MEDIUM: 'warning', LOW: 'success', NORMAL: 'info' }
|
||||
return map[level] || 'info'
|
||||
}
|
||||
|
||||
const getRiskText = (level) => {
|
||||
const map = { HIGH: '高风险', MEDIUM: '中风险', LOW: '低风险', NORMAL: '正常' }
|
||||
return map[level] || '未知'
|
||||
}
|
||||
|
||||
const getToolName = (tool) => {
|
||||
const map = { BRADEN: 'Braden压疮', MORSE: 'Morse跌倒', NRS2002: 'NRS2002营养' }
|
||||
return map[tool] || tool
|
||||
}
|
||||
|
||||
const formatTime = (time) => {
|
||||
if (!time) return '-'
|
||||
const d = new Date(time)
|
||||
return d.toLocaleString('zh-CN')
|
||||
}
|
||||
|
||||
const goBack = () => {
|
||||
router.push('/nursingmobile/patient-list')
|
||||
}
|
||||
|
||||
const fetchHistory = async () => {
|
||||
if (!patientId.value) return
|
||||
historyLoading.value = true
|
||||
try {
|
||||
const res = await getAssessmentList(patientId.value)
|
||||
historyList.value = (res.data || []).slice(0, 20)
|
||||
} finally {
|
||||
historyLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const filled = toolConfig.value.items.filter(i => itemScores[i.key] != null)
|
||||
if (filled.length < toolConfig.value.items.length) {
|
||||
ElMessage.warning('请完成所有评估项目')
|
||||
return
|
||||
}
|
||||
submitting.value = true
|
||||
try {
|
||||
await submitAssessment({
|
||||
patientId: patientId.value ? Number(patientId.value) : null,
|
||||
encounterId: encounterId.value ? Number(encounterId.value) : null,
|
||||
patientName: patientName.value,
|
||||
assessmentType: 'MOBILE',
|
||||
assessmentTool: selectedTool.value,
|
||||
totalScore: totalScore.value,
|
||||
itemScores: { ...itemScores },
|
||||
detail: detail.value,
|
||||
assessmentTime: new Date()
|
||||
})
|
||||
ElMessage.success('评估提交成功')
|
||||
detail.value = ''
|
||||
fetchHistory()
|
||||
} catch (e) {
|
||||
ElMessage.error('提交失败')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchHistory()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mobile-assessment {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tool-selector {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.assessment-form {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.items-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.assessment-item {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.item-options {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.score-summary {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
background: #f5f7fa;
|
||||
border-radius: 6px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.total-score {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.score-value {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #409eff;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.risk-level {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.history-section {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.history-item {
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.history-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.history-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.history-tool {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.history-body {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.history-score {
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.history-time {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
@@ -19,3 +19,23 @@ export function saveVitalSign(data) {
|
||||
export function getVitalSignTrend(patientId, params) {
|
||||
return request({ url: '/nursing/mobile/vital-sign-trend/' + patientId, method: 'get', params })
|
||||
}
|
||||
|
||||
export function submitAssessment(data) {
|
||||
return request({ url: '/nursing/mobile/assessment/submit', method: 'post', data })
|
||||
}
|
||||
|
||||
export function getAssessmentList(patientId) {
|
||||
return request({ url: '/nursing/mobile/assessment/list/' + patientId, method: 'get' })
|
||||
}
|
||||
|
||||
export function startInfusion(data) {
|
||||
return request({ url: '/nursing/mobile/infusion/start', method: 'post', data })
|
||||
}
|
||||
|
||||
export function addInfusionPatrol(data) {
|
||||
return request({ url: '/nursing/mobile/infusion/patrol', method: 'post', data })
|
||||
}
|
||||
|
||||
export function getInfusionStatus(patientId) {
|
||||
return request({ url: '/nursing/mobile/infusion/status/' + patientId, method: 'get' })
|
||||
}
|
||||
|
||||
159
healthlink-his-ui/src/views/telehealth/doctor/index.vue
Normal file
159
healthlink-his-ui/src/views/telehealth/doctor/index.vue
Normal file
@@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<div class="telehealth-doctor">
|
||||
<div class="page-header">
|
||||
<span class="tab-title">问诊管理 - 医生端</span>
|
||||
</div>
|
||||
|
||||
<div class="search-section">
|
||||
<el-form :model="queryParams" inline>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" placeholder="全部" clearable style="width: 120px">
|
||||
<el-option label="待诊" value="PENDING" />
|
||||
<el-option label="问诊中" value="IN_PROGRESS" />
|
||||
<el-option label="已完成" value="COMPLETED" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleQuery">查询</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<el-table :data="tableData" v-loading="loading" border stripe>
|
||||
<el-table-column label="问诊ID" prop="id" width="120" />
|
||||
<el-table-column label="患者ID" prop="patientId" width="120" />
|
||||
<el-table-column label="问诊类型" prop="consultationType" width="120" />
|
||||
<el-table-column label="主诉" prop="chiefComplaint" show-overflow-tooltip />
|
||||
<el-table-column label="诊断" prop="diagnosis" show-overflow-tooltip />
|
||||
<el-table-column label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="statusType(row.status)">{{ statusText(row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" prop="createTime" width="170" />
|
||||
<el-table-column label="操作" width="200" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button v-if="row.status === 'PENDING' || row.status === 'IN_PROGRESS'" type="primary" link @click="handleReply(row)">回复</el-button>
|
||||
<el-button v-if="row.status === 'IN_PROGRESS'" type="success" link @click="handlePrescribe(row)">开方</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
v-model:current-page="queryParams.pageNum"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
:page-sizes="[10, 20, 50]"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
@size-change="handleQuery"
|
||||
@current-change="handleQuery"
|
||||
style="margin-top: 16px; justify-content: flex-end"
|
||||
/>
|
||||
|
||||
<el-dialog v-model="replyVisible" title="医生回复" width="500px">
|
||||
<el-form label-width="80px">
|
||||
<el-form-item label="诊断">
|
||||
<el-input v-model="replyForm.diagnosis" type="textarea" :rows="4" placeholder="请输入诊断" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="replyVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitReply">提交</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="prescribeVisible" title="复诊开方" width="600px">
|
||||
<el-form label-width="80px">
|
||||
<el-form-item label="诊断">
|
||||
<el-input v-model="prescribeForm.diagnosis" type="textarea" :rows="3" placeholder="请输入诊断" />
|
||||
</el-form-item>
|
||||
<el-form-item label="处方">
|
||||
<el-input v-model="prescribeForm.prescription" type="textarea" :rows="6" placeholder="请输入处方内容" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="prescribeVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitPrescribe">确认开方</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { pageConsultation, replyConsultation, prescribeConsultation } from '@/api/telehealth'
|
||||
|
||||
const loading = ref(false)
|
||||
const tableData = ref([])
|
||||
const total = ref(0)
|
||||
const replyVisible = ref(false)
|
||||
const prescribeVisible = ref(false)
|
||||
|
||||
const queryParams = ref({
|
||||
status: '',
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
|
||||
const replyForm = ref({ id: null, diagnosis: '' })
|
||||
const prescribeForm = ref({ id: null, diagnosis: '', prescription: '' })
|
||||
|
||||
const statusText = (s) => ({ PENDING: '待诊', IN_PROGRESS: '问诊中', COMPLETED: '已完成' }[s] || s)
|
||||
const statusType = (s) => ({ PENDING: 'warning', IN_PROGRESS: 'primary', COMPLETED: 'success' }[s] || 'info')
|
||||
|
||||
const handleQuery = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await pageConsultation(queryParams.value)
|
||||
tableData.value = res.data?.records || []
|
||||
total.value = res.data?.total || 0
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleReply = (row) => {
|
||||
replyForm.value = { id: row.id, diagnosis: row.diagnosis || '' }
|
||||
replyVisible.value = true
|
||||
}
|
||||
|
||||
const handlePrescribe = (row) => {
|
||||
prescribeForm.value = { id: row.id, diagnosis: row.diagnosis || '', prescription: '' }
|
||||
prescribeVisible.value = true
|
||||
}
|
||||
|
||||
const submitReply = async () => {
|
||||
await replyConsultation(replyForm.value)
|
||||
ElMessage.success('回复成功')
|
||||
replyVisible.value = false
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
const submitPrescribe = async () => {
|
||||
await prescribeConsultation(prescribeForm.value)
|
||||
ElMessage.success('开方成功')
|
||||
prescribeVisible.value = false
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
handleQuery()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.telehealth-doctor {
|
||||
padding: 16px;
|
||||
}
|
||||
.page-header {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.tab-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.search-section {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
151
healthlink-his-ui/src/views/telehealth/patient/index.vue
Normal file
151
healthlink-his-ui/src/views/telehealth/patient/index.vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<div class="telehealth-patient">
|
||||
<div class="page-header">
|
||||
<span class="tab-title">在线问诊</span>
|
||||
</div>
|
||||
|
||||
<div class="search-section">
|
||||
<el-form :model="queryParams" inline>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" placeholder="全部" clearable style="width: 120px">
|
||||
<el-option label="待诊" value="PENDING" />
|
||||
<el-option label="问诊中" value="IN_PROGRESS" />
|
||||
<el-option label="已完成" value="COMPLETED" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleQuery">查询</el-button>
|
||||
<el-button type="success" @click="handleCreate">新建问诊</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<el-table :data="tableData" v-loading="loading" border stripe>
|
||||
<el-table-column label="问诊ID" prop="id" width="120" />
|
||||
<el-table-column label="问诊类型" prop="consultationType" width="120" />
|
||||
<el-table-column label="主诉" prop="chiefComplaint" show-overflow-tooltip />
|
||||
<el-table-column label="诊断" prop="diagnosis" show-overflow-tooltip />
|
||||
<el-table-column label="处方" prop="prescription" show-overflow-tooltip />
|
||||
<el-table-column label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="statusType(row.status)">{{ statusText(row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" prop="createTime" width="170" />
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
v-model:current-page="queryParams.pageNum"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
:page-sizes="[10, 20, 50]"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
@size-change="handleQuery"
|
||||
@current-change="handleQuery"
|
||||
style="margin-top: 16px; justify-content: flex-end"
|
||||
/>
|
||||
|
||||
<el-dialog v-model="dialogVisible" title="新建问诊" width="500px" @close="resetForm">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="问诊类型" prop="consultationType">
|
||||
<el-select v-model="form.consultationType" placeholder="请选择">
|
||||
<el-option label="图文问诊" value="TEXT" />
|
||||
<el-option label="视频问诊" value="VIDEO" />
|
||||
<el-option label="电话问诊" value="PHONE" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="医生ID" prop="doctorId">
|
||||
<el-input v-model.number="form.doctorId" placeholder="请输入医生ID" />
|
||||
</el-form-item>
|
||||
<el-form-item label="主诉" prop="chiefComplaint">
|
||||
<el-input v-model="form.chiefComplaint" type="textarea" :rows="4" placeholder="请描述您的症状" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitCreate">提交</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { createConsultation, pageConsultation } from '@/api/telehealth'
|
||||
|
||||
const loading = ref(false)
|
||||
const tableData = ref([])
|
||||
const total = ref(0)
|
||||
const dialogVisible = ref(false)
|
||||
const formRef = ref(null)
|
||||
|
||||
const queryParams = ref({
|
||||
status: '',
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
|
||||
const form = ref({
|
||||
patientId: null,
|
||||
doctorId: null,
|
||||
consultationType: '',
|
||||
chiefComplaint: ''
|
||||
})
|
||||
|
||||
const rules = {
|
||||
consultationType: [{ required: true, message: '请选择问诊类型', trigger: 'change' }],
|
||||
doctorId: [{ required: true, message: '请输入医生ID', trigger: 'blur' }],
|
||||
chiefComplaint: [{ required: true, message: '请输入主诉', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
const statusText = (s) => ({ PENDING: '待诊', IN_PROGRESS: '问诊中', COMPLETED: '已完成' }[s] || s)
|
||||
const statusType = (s) => ({ PENDING: 'warning', IN_PROGRESS: 'primary', COMPLETED: 'success' }[s] || 'info')
|
||||
|
||||
const handleQuery = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await pageConsultation(queryParams.value)
|
||||
tableData.value = res.data?.records || []
|
||||
total.value = res.data?.total || 0
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleCreate = () => {
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
form.value = { patientId: null, doctorId: null, consultationType: '', chiefComplaint: '' }
|
||||
}
|
||||
|
||||
const submitCreate = async () => {
|
||||
await formRef.value.validate()
|
||||
await createConsultation(form.value)
|
||||
ElMessage.success('问诊创建成功')
|
||||
dialogVisible.value = false
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
handleQuery()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.telehealth-patient {
|
||||
padding: 16px;
|
||||
}
|
||||
.page-header {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.tab-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.search-section {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user