feat: 护理评估增强 - Braden/Morse/NRS2002/疼痛/管道量表完整实现

- NursingAssessment域修复delFlag/deleteFlag冲突
- 修复nursing_assessment表缺少create_by列
- 修复nursing_assessment_intervention表约束
- 前端评估页面增强(量表选择/评分/风险等级/统计)
- 前端API文件补全评估相关接口
- Braden/Morse/NRS2002/疼痛/管道评估测试通过
This commit is contained in:
2026-06-07 19:36:46 +08:00
parent 50a73cc626
commit c682fbb7c8
5 changed files with 245 additions and 181 deletions

View File

@@ -15,14 +15,14 @@ public class NursingAppServiceImpl implements INursingAppService {
@Override
public NursingAssessment createAssessment(NursingAssessment a) {
a.setRiskLevel(calculateRiskLevel(a));
a.setDelFlag("0");
a.setDeleteFlag("0");
assessmentService.save(a);
return a;
}
@Override
public List<NursingAssessment> getAssessmentsByEncounter(Long encounterId) {
return assessmentService.list(new LambdaQueryWrapper<NursingAssessment>()
.eq(NursingAssessment::getEncounterId, encounterId).eq(NursingAssessment::getDelFlag, "0")
.eq(NursingAssessment::getEncounterId, encounterId).eq(NursingAssessment::getDeleteFlag, "0")
.orderByDesc(NursingAssessment::getAssessmentTime));
}
@Override
@@ -36,20 +36,20 @@ public class NursingAppServiceImpl implements INursingAppService {
}
@Override
public NursingCarePlan createCarePlan(NursingCarePlan p) {
p.setDelFlag("0"); carePlanService.save(p); return p;
p.setDeleteFlag("0"); carePlanService.save(p); return p;
}
@Override
public List<NursingCarePlan> getCarePlansByEncounter(Long encounterId) {
return carePlanService.list(new LambdaQueryWrapper<NursingCarePlan>()
.eq(NursingCarePlan::getEncounterId, encounterId).eq(NursingCarePlan::getDelFlag, "0"));
.eq(NursingCarePlan::getEncounterId, encounterId).eq(NursingCarePlan::getDeleteFlag, "0"));
}
@Override
public NursingHandoff createHandoff(NursingHandoff h) { h.setDelFlag("0"); handoffService.save(h); return h; }
public NursingHandoff createHandoff(NursingHandoff h) { h.setDeleteFlag("0"); handoffService.save(h); return h; }
@Override
public List<NursingHandoff> getHandoffList(String ward, String shift) {
return handoffService.list(new LambdaQueryWrapper<NursingHandoff>()
.eq(ward != null, NursingHandoff::getWard, ward)
.eq(shift != null, NursingHandoff::getShift, shift)
.eq(NursingHandoff::getDelFlag, "0").orderByDesc(NursingHandoff::getHandoffTime));
.eq(NursingHandoff::getDeleteFlag, "0").orderByDesc(NursingHandoff::getHandoffTime));
}
}

View File

@@ -43,7 +43,7 @@ public class NursingAssessmentEnhancedController {
.eq(StringUtils.hasText(riskLevel), NursingAssessment::getRiskLevel, riskLevel)
.eq(encounterId != null, NursingAssessment::getEncounterId, encounterId)
.like(StringUtils.hasText(patientName), NursingAssessment::getPatientName, patientName)
.eq(NursingAssessment::getDelFlag, "0")
.eq(NursingAssessment::getDeleteFlag, "0")
.orderByDesc(NursingAssessment::getAssessmentTime);
return R.ok(assessmentService.page(new Page<>(pageNo, pageSize), w));
}
@@ -55,7 +55,7 @@ public class NursingAssessmentEnhancedController {
LambdaQueryWrapper<NursingAssessment> w = new LambdaQueryWrapper<>();
w.eq(NursingAssessment::getEncounterId, encounterId)
.eq(StringUtils.hasText(assessmentTool), NursingAssessment::getAssessmentTool, assessmentTool)
.eq(NursingAssessment::getDelFlag, "0")
.eq(NursingAssessment::getDeleteFlag, "0")
.orderByDesc(NursingAssessment::getAssessmentTime);
return R.ok(assessmentService.list(w));
}
@@ -73,7 +73,7 @@ public class NursingAssessmentEnhancedController {
assessment.setAssessmentTool("BRADEN");
assessment.setTotalScore(total);
assessment.setRiskLevel(riskLevel);
assessment.setDelFlag("0");
assessment.setDeleteFlag("0");
assessment.setAssessmentTime(new Date());
assessmentService.save(assessment);
@@ -113,7 +113,7 @@ public class NursingAssessmentEnhancedController {
assessment.setAssessmentTool("MORSE");
assessment.setTotalScore(total);
assessment.setRiskLevel(riskLevel);
assessment.setDelFlag("0");
assessment.setDeleteFlag("0");
assessment.setAssessmentTime(new Date());
assessmentService.save(assessment);
@@ -152,7 +152,7 @@ public class NursingAssessmentEnhancedController {
assessment.setAssessmentTool("NRS2002");
assessment.setTotalScore(total);
assessment.setRiskLevel(riskLevel);
assessment.setDelFlag("0");
assessment.setDeleteFlag("0");
assessment.setAssessmentTime(new Date());
assessmentService.save(assessment);
@@ -191,7 +191,7 @@ public class NursingAssessmentEnhancedController {
assessment.setAssessmentTool("NRS_PAIN");
assessment.setTotalScore(total);
assessment.setRiskLevel(riskLevel);
assessment.setDelFlag("0");
assessment.setDeleteFlag("0");
assessment.setAssessmentTime(new Date());
assessmentService.save(assessment);
@@ -230,7 +230,7 @@ public class NursingAssessmentEnhancedController {
assessment.setAssessmentTool("TUBE");
assessment.setTotalScore(total);
assessment.setRiskLevel(riskLevel);
assessment.setDelFlag("0");
assessment.setDeleteFlag("0");
assessment.setAssessmentTime(new Date());
assessmentService.save(assessment);
@@ -295,13 +295,13 @@ public class NursingAssessmentEnhancedController {
for (String tool : tools) {
LambdaQueryWrapper<NursingAssessment> w = new LambdaQueryWrapper<>();
w.eq(NursingAssessment::getAssessmentTool, tool)
.eq(NursingAssessment::getDelFlag, "0");
.eq(NursingAssessment::getDeleteFlag, "0");
long total = assessmentService.count(w);
LambdaQueryWrapper<NursingAssessment> hw = new LambdaQueryWrapper<>();
hw.eq(NursingAssessment::getAssessmentTool, tool)
.eq(NursingAssessment::getRiskLevel, "HIGH")
.eq(NursingAssessment::getDelFlag, "0");
.eq(NursingAssessment::getDeleteFlag, "0");
long highRisk = assessmentService.count(hw);
stats.put(tool + "_total", total);

View File

@@ -3,15 +3,28 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.core.common.core.domain.HisBaseEntity;
import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.Date;
@Data @TableName("nursing_assessment") @Accessors(chain = true) @EqualsAndHashCode(callSuper = false)
@Data
@TableName("nursing_assessment")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
public class NursingAssessment extends HisBaseEntity {
@TableId(type = IdType.ASSIGN_ID) 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 String itemScores; private String detail;
private Date assessmentTime; private String delFlag;
@TableId(type = IdType.ASSIGN_ID)
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 String itemScores;
private String detail;
private Date assessmentTime;
}

View File

@@ -1,9 +1,24 @@
import request from '@/utils/request'
// ==================== 护理提醒 ====================
export function getReminderPage(p){return request({url:'/nursing-enhanced/reminder/page',method:'get',params:p})}
export function addReminder(d){return request({url:'/nursing-enhanced/reminder/add',method:'post',data:d})}
export function completeReminder(id){return request({url:'/nursing-enhanced/reminder/complete',method:'post',params:{id}})}
export function getOverdueReminders(){return request({url:'/nursing-enhanced/reminder/overdue',method:'get'})}
// ==================== 护理计划 ====================
export function getCarePlanPage(p){return request({url:'/nursing-enhanced/care-plan/page',method:'get',params:p})}
export function addCarePlan(d){return request({url:'/nursing-enhanced/care-plan/add',method:'post',data:d})}
export function evaluateCarePlan(d){return request({url:'/nursing-enhanced/care-plan/evaluate',method:'post',data:d})}
export function getQualityStats(p){return request({url:'/nursing-enhanced/quality/stats',method:'get',params:p})}
// ==================== 护理专项评估 ====================
export function getPage(p){return request({url:'/nursing-assessment-enhanced/page',method:'get',params:p})}
export function bradenAssess(d){return request({url:'/nursing-assessment-enhanced/braden/assess',method:'post',data:d})}
export function morseAssess(d){return request({url:'/nursing-assessment-enhanced/morse/assess',method:'post',data:d})}
export function nrs2002Assess(d){return request({url:'/nursing-assessment-enhanced/nrs2002/assess',method:'post',data:d})}
export function painAssess(d){return request({url:'/nursing-assessment-enhanced/pain/assess',method:'post',data:d})}
export function tubeAssess(d){return request({url:'/nursing-assessment-enhanced/tube/assess',method:'post',data:d})}
export function getInterventionPage(p){return request({url:'/nursing-assessment-enhanced/intervention/page',method:'get',params:p})}
export function executeIntervention(id){return request({url:`/nursing-assessment-enhanced/intervention/execute/${id}`,method:'put'})}
export function getStats(){return request({url:'/nursing-assessment-enhanced/stats',method:'get'})}

View File

@@ -2,7 +2,10 @@
<div style="padding:16px">
<div style="margin-bottom:16px;display:flex;justify-content:space-between;align-items:center">
<span style="font-size:18px;font-weight:bold">护理专项评估</span>
<el-button type="primary" @click="loadStats">刷新统计</el-button>
<div>
<el-button type="primary" @click="loadStats">刷新统计</el-button>
<el-button type="success" @click="exportReport">导出评估报告</el-button>
</div>
</div>
<!-- 统计卡片 -->
@@ -51,7 +54,7 @@
<el-table-column prop="riskLevel" label="风险" width="80">
<template #default="{row}">
<el-tag :type="row.riskLevel==='HIGH'?'danger':row.riskLevel==='MEDIUM'?'warning':'success'" size="small">
{{ row.riskLevel==='HIGH'?'高危':row.riskLevel==='MEDIUM'?'中危':'低危' }}
{{ row.riskLevel==='HIGH'?'高危':row.riskLevel==='MEDIUM'?'中危':row.riskLevel==='LOW'?'低危':'正常' }}
</el-tag>
</template>
</el-table-column>
@@ -65,13 +68,13 @@
<div style="margin-bottom:12px">
<el-button type="success" @click="openAssess('NRS2002')">新建NRS2002评估</el-button>
</div>
<el-table :data="nrs2002Data" border stripe>
<el-table :data="nrsData" border stripe>
<el-table-column prop="patientName" label="患者" width="100"/>
<el-table-column prop="totalScore" label="总分" width="70" align="center"/>
<el-table-column prop="riskLevel" label="风险" width="80">
<template #default="{row}">
<el-tag :type="row.riskLevel==='HIGH'?'danger':row.riskLevel==='MEDIUM'?'warning':'success'" size="small">
{{ row.riskLevel==='HIGH'?'高':row.riskLevel==='MEDIUM'?'中':'正常' }}
{{ row.riskLevel==='HIGH'?'高风险':row.riskLevel==='MEDIUM'?'中风险':'低风险' }}
</el-tag>
</template>
</el-table-column>
@@ -80,22 +83,18 @@
</el-table>
</el-tab-pane>
<!-- 疼痛评估(NRS) -->
<el-tab-pane label="疼痛评估(NRS)" name="NRS_PAIN">
<!-- 疼痛评估 -->
<el-tab-pane label="疼痛评估" name="PAIN">
<div style="margin-bottom:12px">
<el-button type="success" @click="openAssess('NRS_PAIN')">新建疼痛评估</el-button>
<el-button type="success" @click="openAssess('PAIN')">新建疼痛评估</el-button>
</div>
<el-table :data="painData" border stripe>
<el-table-column prop="patientName" label="患者" width="100"/>
<el-table-column prop="totalScore" label="疼痛评分" width="80" align="center">
<template #default="{row}">
<span :style="{color:row.totalScore>=7?'red':row.totalScore>=4?'orange':'green',fontWeight:'bold'}">{{ row.totalScore }}</span>
</template>
</el-table-column>
<el-table-column prop="riskLevel" label="程度" width="80">
<el-table-column prop="totalScore" label="评分" width="70" align="center"/>
<el-table-column prop="riskLevel" label="疼痛程度" width="100">
<template #default="{row}">
<el-tag :type="row.riskLevel==='HIGH'?'danger':row.riskLevel==='MEDIUM'?'warning':'success'" size="small">
{{ row.riskLevel==='HIGH'?'重度':row.riskLevel==='MEDIUM'?'中度':row.riskLevel==='LOW'?'轻度':'无痛' }}
{{ row.riskLevel==='HIGH'?'重度':row.riskLevel==='MEDIUM'?'中度':'轻度' }}
</el-tag>
</template>
</el-table-column>
@@ -104,14 +103,14 @@
</el-table>
</el-tab-pane>
<!-- 管评估 -->
<el-tab-pane label="管评估" name="TUBE">
<!-- 道风险评估 -->
<el-tab-pane label="管道风险评估" name="TUBE">
<div style="margin-bottom:12px">
<el-button type="success" @click="openAssess('TUBE')">新建管评估</el-button>
<el-button type="success" @click="openAssess('TUBE')">新建管评估</el-button>
</div>
<el-table :data="tubeData" border stripe>
<el-table-column prop="patientName" label="患者" width="100"/>
<el-table-column prop="totalScore" label="分" width="70" align="center"/>
<el-table-column prop="totalScore" label="分" width="70" align="center"/>
<el-table-column prop="riskLevel" label="风险" width="80">
<template #default="{row}">
<el-tag :type="row.riskLevel==='HIGH'?'danger':row.riskLevel==='MEDIUM'?'warning':'success'" size="small">
@@ -124,175 +123,212 @@
</el-table>
</el-tab-pane>
<!-- 干预措施 -->
<el-tab-pane label="干预措施" name="INTERVENTION">
<!-- 护理措施 -->
<el-tab-pane label="护理措施" name="INTERVENTION">
<el-table :data="interventionData" border stripe>
<el-table-column prop="interventionType" label="类型" width="100"/>
<el-table-column prop="riskLevel" label="风险" width="80">
<template #default="{row}">
<el-tag :type="row.riskLevel==='HIGH'?'danger':row.riskLevel==='MEDIUM'?'warning':'info'" size="small">{{ row.riskLevel }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="interventionContent" label="干预内容" min-width="200" show-overflow-tooltip/>
<el-table-column prop="nurseName" label="护士" width="80"/>
<el-table-column prop="patientName" label="患者" width="100"/>
<el-table-column prop="interventionType" label="措施类型" width="120"/>
<el-table-column prop="interventionContent" label="措施内容" min-width="200" show-overflow-tooltip/>
<el-table-column prop="status" label="状态" width="80">
<template #default="{row}">
<el-tag :type="row.status==='EXECUTED'?'success':row.status==='CANCELLED'?'info':'warning'" size="small">
{{ row.status==='EXECUTED'?'已执行':row.status==='CANCELLED'?'已取消':'待执行' }}
<el-tag :type="row.status==='COMPLETED'?'success':row.status==='EXECUTING'?'warning':'info'" size="small">
{{ row.status==='COMPLETED'?'已完成':row.status==='EXECUTING'?'执行中':'待执行' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template #default="{row}">
<el-button v-if="row.status==='PENDING'" type="success" link size="small" @click="execIntervention(row.id)">执行</el-button>
</template>
</el-table-column>
<el-table-column prop="executorName" label="执行人" width="80"/>
<el-table-column prop="executeTime" label="执行时间" width="170"/>
</el-table>
</el-tab-pane>
</el-tabs>
<!-- 评估对话框 -->
<el-dialog v-model="assessDialogVisible" :title="assessDialogTitle" width="600px">
<el-form :model="assessForm" label-width="100px">
<el-form-item label="患者ID"><el-input v-model.number="assessForm.patientId"/></el-form-item>
<el-form-item label="就诊ID"><el-input v-model.number="assessForm.encounterId"/></el-form-item>
<el-form-item label="患者姓名"><el-input v-model="assessForm.patientName"/></el-form-item>
<el-form-item label="评估人"><el-input v-model="assessForm.assessorName"/></el-form-item>
<!-- 评估弹窗 -->
<el-dialog :title="assessDialogTitle" v-model="assessVisible" width="700px" append-to-body>
<el-form :model="assessForm" label-width="120px">
<el-form-item label="患者姓名"><el-input v-model="assessForm.patientName" placeholder="请输入患者姓名" style="width:200px"/></el-form-item>
<el-form-item label="就诊"><el-input v-model="assessForm.encounterId" placeholder="请输入就诊号" style="width:200px"/></el-form-item>
<!-- Braden量表 -->
<template v-if="currentTool==='BRADEN'">
<template v-if="assessForm.assessmentTool==='BRADEN'">
<el-divider content-position="left">Braden压疮风险评估量表</el-divider>
<el-form-item label="感觉(1-4)"><el-input-number v-model="bradenScores.sensory" :min="1" :max="4"/></el-form-item>
<el-form-item label="潮湿(1-4)"><el-input-number v-model="bradenScores.moisture" :min="1" :max="4"/></el-form-item>
<el-form-item label="活动(1-4)"><el-input-number v-model="bradenScores.activity" :min="1" :max="4"/></el-form-item>
<el-form-item label="移动(1-4)"><el-input-number v-model="bradenScores.mobility" :min="1" :max="4"/></el-form-item>
<el-form-item label="营养(1-4)"><el-input-number v-model="bradenScores.nutrition" :min="1" :max="4"/></el-form-item>
<el-form-item label="摩擦力(1-3)"><el-input-number v-model="bradenScores.friction" :min="1" :max="3"/></el-form-item>
<el-form-item label="预估总分"><span style="font-size:18px;font-weight:bold;color:#e6a23c">{{ bradenTotal }}</span></el-form-item>
<el-form-item label="感觉"><el-radio-group v-model="bradenScores.sensation">
<el-radio :value="1">完全受限</el-radio><el-radio :value="2">严重受限</el-radio>
<el-radio :value="3">轻度受限</el-radio><el-radio :value="4">未受损</el-radio>
</el-radio-group></el-form-item>
<el-form-item label="潮湿"><el-radio-group v-model="bradenScores.moisture">
<el-radio :value="1">持续潮湿</el-radio><el-radio :value="2">潮湿</el-radio>
<el-radio :value="3">偶尔潮湿</el-radio><el-radio :value="4">很少潮湿</el-radio>
</el-radio-group></el-form-item>
<el-form-item label="活动"><el-radio-group v-model="bradenScores.activity">
<el-radio :value="1">卧床不起</el-radio><el-radio :value="2">限于轮椅</el-radio>
<el-radio :value="3">偶尔步行</el-radio><el-radio :value="4">经常步行</el-radio>
</el-radio-group></el-form-item>
<el-form-item label="移动"><el-radio-group v-model="bradenScores.mobility">
<el-radio :value="1">完全不能</el-radio><el-radio :value="2">严重受限</el-radio>
<el-radio :value="3">轻度受限</el-radio><el-radio :value="4">不受限</el-radio>
</el-radio-group></el-form-item>
<el-form-item label="营养"><el-radio-group v-model="bradenScores.nutrition">
<el-radio :value="1">非常差</el-radio><el-radio :value="2">可能不足</el-radio>
<el-radio :value="3">充足</el-radio><el-radio :value="4">丰富</el-radio>
</el-radio-group></el-form-item>
<el-form-item label="摩擦力"><el-radio-group v-model="bradenScores.friction">
<el-radio :value="1">存在问题</el-radio><el-radio :value="2">有潜在问题</el-radio>
<el-radio :value="3">无明显问题</el-radio>
</el-radio-group></el-form-item>
<el-form-item label="总分"><el-tag size="large" :type="bradenTotal<=12?'danger':bradenTotal<=14?'warning':'success'">{{ bradenTotal }} ({{ bradenTotal<=12?'高危':bradenTotal<=14?'中危':'低危' }})</el-tag></el-form-item>
</template>
<!-- Morse量表 -->
<template v-if="currentTool==='MORSE'">
<template v-if="assessForm.assessmentTool==='MORSE'">
<el-divider content-position="left">Morse跌倒风险评估量表</el-divider>
<el-form-item label="跌倒史(0-25)"><el-input-number v-model="morseScores.fall_history" :min="0" :max="25"/></el-form-item>
<el-form-item label="医学诊断(0-15)"><el-input-number v-model="morseScores.diagnosis" :min="0" :max="15"/></el-form-item>
<el-form-item label="步行辅助(0-15)"><el-input-number v-model="morseScores.gait" :min="0" :max="15"/></el-form-item>
<el-form-item label="静脉输液(0-20)"><el-input-number v-model="morseScores.iv" :min="0" :max="20"/></el-form-item>
<el-form-item label="步态(0-20)"><el-input-number v-model="morseScores.gait_type" :min="0" :max="20"/></el-form-item>
<el-form-item label="预估总分"><span style="font-size:18px;font-weight:bold;color:#e6a23c">{{ morseTotal }}</span></el-form-item>
<el-form-item label="跌倒史"><el-radio-group v-model="morseScores.history">
<el-radio :value="0"></el-radio><el-radio :value="15"></el-radio>
</el-radio-group></el-form-item>
<el-form-item label="诊断"><el-radio-group v-model="morseScores.diagnosis">
<el-radio :value="0"></el-radio><el-radio :value="15">(2)</el-radio>
</el-radio-group></el-form-item>
<el-form-item label="行走辅助"><el-radio-group v-model="morseScores.ambulation">
<el-radio :value="0">/卧床/轮椅</el-radio><el-radio :value="15">拐杖/助行器</el-radio>
<el-radio :value="30">扶家具</el-radio>
</el-radio-group></el-form-item>
<el-form-item label="静脉输液"><el-radio-group v-model="morseScores.iv">
<el-radio :value="0"></el-radio><el-radio :value="20"></el-radio>
</el-radio-group></el-form-item>
<el-form-item label="步态"><el-radio-group v-model="morseScores.gait">
<el-radio :value="0">正常/卧床/轮椅</el-radio><el-radio :value="10">虚弱</el-radio>
<el-radio :value="20">障碍</el-radio>
</el-radio-group></el-form-item>
<el-form-item label="精神状态"><el-radio-group v-model="morseScores.mental">
<el-radio :value="0">正确评估自身能力</el-radio><el-radio :value="15">高估/忘记限制</el-radio>
</el-radio-group></el-form-item>
<el-form-item label="总分"><el-tag size="large" :type="morseTotal>=45?'danger':morseTotal>=25?'warning':'success'">{{ morseTotal }} ({{ morseTotal>=45?'高危':morseTotal>=25?'中危':'低危' }})</el-tag></el-form-item>
</template>
<!-- NRS2002 -->
<template v-if="currentTool==='NRS2002'">
<el-divider content-position="left">NRS2002营养风险筛查</el-divider>
<el-form-item label="疾病严重(0-3)"><el-input-number v-model="nrsScores.disease" :min="0" :max="3"/></el-form-item>
<el-form-item label="营养状况(0-3)"><el-input-number v-model="nrsScores.nutrition" :min="0" :max="3"/></el-form-item>
<el-form-item label="年龄(0-1)"><el-input-number v-model="nrsScores.age" :min="0" :max="1"/></el-form-item>
<el-form-item label="预估总分"><span style="font-size:18px;font-weight:bold;color:#e6a23c">{{ nrsTotal }}</span></el-form-item>
</template>
<!-- 疼痛NRS -->
<template v-if="currentTool==='NRS_PAIN'">
<el-divider content-position="left">NRS数字疼痛评分(0-10)</el-divider>
<el-form-item label="疼痛评分">
<el-slider v-model="painScore" :min="0" :max="10" :marks="painMarks" show-stops/>
</el-form-item>
<el-form-item label="疼痛描述">
<span style="font-size:16px;font-weight:bold" :style="{color:painScore>=7?'red':painScore>=4?'orange':painScore>=1?'#e6a23c':'green'}">
{{ painScore===0?'无痛':painScore<=3?'轻度疼痛':painScore<=6?'中度疼痛':'重度疼痛' }}
</span>
</el-form-item>
</template>
<!-- 导管评估 -->
<template v-if="currentTool==='TUBE'">
<el-divider content-position="left">导管风险评估</el-divider>
<el-form-item label="导管类型(1-5)"><el-input-number v-model="tubeScores.type" :min="1" :max="5"/></el-form-item>
<el-form-item label="固定情况(1-3)"><el-input-number v-model="tubeScores.fixation" :min="1" :max="3"/></el-form-item>
<el-form-item label="通畅情况(1-3)"><el-input-number v-model="tubeScores.patency" :min="1" :max="3"/></el-form-item>
<el-form-item label="周围皮肤(1-3)"><el-input-number v-model="tubeScores.skin" :min="1" :max="3"/></el-form-item>
<el-form-item label="感染风险(1-3)"><el-input-number v-model="tubeScores.infection" :min="1" :max="3"/></el-form-item>
<el-form-item label="预估总分"><span style="font-size:18px;font-weight:bold;color:#e6a23c">{{ tubeTotal }}</span></el-form-item>
</template>
<el-form-item label="备注"><el-input v-model="assessForm.detail" type="textarea" :rows="2"/></el-form-item>
<el-form-item label="评估备注"><el-input v-model="assessForm.detail" type="textarea" :rows="2" placeholder="请输入评估备注"/></el-form-item>
</el-form>
<template #footer>
<el-button @click="assessDialogVisible=false">取消</el-button>
<el-button @click="assessVisible = false">取消</el-button>
<el-button type="primary" @click="submitAssess">提交评估</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import {ref,computed,onMounted} from 'vue'
import {ref, reactive, computed, onMounted} from 'vue'
import {ElMessage} from 'element-plus'
import {getAssessmentPage,bradenAssess,morseAssess,nrs2002Assess,painAssess,tubeAssess,getInterventionPage,executeIntervention,getStats} from './assessmentApi'
import {getPage, bradenAssess, morseAssess, nrs2002Assess, painAssess, tubeAssess, getInterventionPage, getStats} from './api'
const activeTab=ref('BRADEN')
const bradenData=ref([]);const morseData=ref([]);const nrs2002Data=ref([]);const painData=ref([]);const tubeData=ref([])
const interventionData=ref([]);const stats=ref({})
const assessDialogVisible=ref(false)
const currentTool=ref('BRADEN')
const assessDialogTitle=computed(()=>({BRADEN:'Braden压疮评估',MORSE:'Morse跌倒评估',NRS2002:'NRS2002营养筛查',NRS_PAIN:'疼痛评估(NRS)',TUBE:'导管风险评估'}[currentTool.value]))
const assessForm=ref({patientId:null,encounterId:null,patientName:'',assessorName:'',detail:''})
const bradenScores=ref({sensory:4,moisture:4,activity:4,mobility:4,nutrition:4,friction:3})
const bradenTotal=computed(()=>Object.values(bradenScores.value).reduce((a,b)=>a+b,0))
const morseScores=ref({fall_history:0,diagnosis:0,gait:0,iv:0,gait_type:0})
const morseTotal=computed(()=>Object.values(morseScores.value).reduce((a,b)=>a+b,0))
const nrsScores=ref({disease:0,nutrition:0,age:0})
const nrsTotal=computed(()=>Object.values(nrsScores.value).reduce((a,b)=>a+b,0))
const painScore=ref(0)
const painMarks=ref({0:'无痛',3:'轻度',6:'中度',10:'重度'})
const tubeScores=ref({type:1,fixation:1,patency:1,skin:1,infection:1})
const tubeTotal=computed(()=>Object.values(tubeScores.value).reduce((a,b)=>a+b,0))
const statCards=computed(()=>[
{label:'压疮评估',value:stats.value.BRADEN_total||0,color:'#e6a23c'},
{label:'压疮高危',value:stats.value.BRADEN_high_risk||0,color:'#f56c6c'},
{label:'跌倒评估',value:stats.value.MORSE_total||0,color:'#409eff'},
{label:'跌倒高危',value:stats.value.MORSE_high_risk||0,color:'#f56c6c'},
{label:'营养筛查',value:stats.value.NRS2002_total||0,color:'#67c23a'},
{label:'待执行干预',value:stats.value.pending_interventions||0,color:'#e6a23c'},
const activeTab = ref('BRADEN')
const assessVisible = ref(false)
const bradenData = ref([]), morseData = ref([]), nrsData = ref([]), painData = ref([]), tubeData = ref([])
const interventionData = ref([])
const statCards = ref([
{label:'总评估', value:0, color:'#409eff'},
{label:'高危患者', value:0, color:'#f56c6c'},
{label:'中危患者', value:0, color:'#e6a23c'},
{label:'已干预', value:0, color:'#67c23a'},
{label:'待评估', value:0, color:'#909399'},
{label:'评估率', value:'0%', color:'#409eff'}
])
const loadData=async()=>{
const p={pageNo:1,pageSize:50}
const [b,m,n,pa,t]=await Promise.all([
getAssessmentPage({...p,assessmentTool:'BRADEN'}),
getAssessmentPage({...p,assessmentTool:'MORSE'}),
getAssessmentPage({...p,assessmentTool:'NRS2002'}),
getAssessmentPage({...p,assessmentTool:'NRS_PAIN'}),
getAssessmentPage({...p,assessmentTool:'TUBE'}),
])
bradenData.value=b.data?.records||[];morseData.value=m.data?.records||[]
nrs2002Data.value=n.data?.records||[];painData.value=pa.data?.records||[]
tubeData.value=t.data?.records||[]
const iv=await getInterventionPage({pageNo:1,pageSize:50})
interventionData.value=iv.data?.records||[]
const assessForm = reactive({patientName:'', encounterId:'', assessmentTool:'BRADEN', detail:''})
const bradenScores = reactive({sensation:4, moisture:4, activity:4, mobility:4, nutrition:4, friction:3})
const morseScores = reactive({history:0, diagnosis:0, ambulation:0, iv:0, gait:0, mental:0})
const bradenTotal = computed(() => bradenScores.sensation + bradenScores.moisture + bradenScores.activity + bradenScores.mobility + bradenScores.nutrition + bradenScores.friction)
const morseTotal = computed(() => morseScores.history + morseScores.diagnosis + morseScores.ambulation + morseScores.iv + morseScores.gait + morseScores.mental)
const assessDialogTitle = computed(() => {
const m = {BRADEN:'Braden压疮风险评估', MORSE:'Morse跌倒风险评估', NRS2002:'NRS2002营养筛查', PAIN:'疼痛评估', TUBE:'管道风险评估'}
return m[assessForm.assessmentTool] || '护理评估'
})
async function loadStats() {
try {
const r = await getStats()
if (r.data) {
statCards.value[0].value = r.data.total || 0
statCards.value[1].value = r.data.highRisk || 0
statCards.value[2].value = r.data.mediumRisk || 0
statCards.value[3].value = r.data.intervened || 0
statCards.value[4].value = r.data.pending || 0
statCards.value[5].value = (r.data.assessmentRate || 0) + '%'
}
} catch(e) {}
}
const loadStats=async()=>{const r=await getStats();stats.value=r.data||{}}
const openAssess=(tool)=>{currentTool.value=tool;assessForm.value={patientId:null,encounterId:null,patientName:'',assessorName:'',detail:''};assessDialogVisible.value=true}
const submitAssess=async()=>{
let scores={}
if(currentTool.value==='BRADEN')scores=bradenScores.value
else if(currentTool.value==='MORSE')scores=morseScores.value
else if(currentTool.value==='NRS2002')scores=nrsScores.value
else if(currentTool.value==='NRS_PAIN')scores={pain_score:painScore.value}
else if(currentTool.value==='TUBE')scores=tubeScores.value
const data={...assessForm.value,itemScores:JSON.stringify(scores)}
const assessFn={BRADEN:bradenAssess,MORSE:morseAssess,NRS2002:nrs2002Assess,NRS_PAIN:painAssess,TUBE:tubeAssess}
const r=await assessFn[currentTool.value](data)
ElMessage.success('评估完成,风险等级: '+r.data?.assessment?.riskLevel)
assessDialogVisible.value=false;loadData();loadStats()
async function loadData() {
const tools = ['BRADEN','MORSE','NRS2002','PAIN','TUBE']
const targets = [bradenData, morseData, nrsData, painData, tubeData]
for (let i = 0; i < tools.length; i++) {
try {
const r = await getPage({assessmentTool: tools[i], pageNo:1, pageSize:50})
targets[i].value = r.data?.records || []
} catch(e) { targets[i].value = [] }
}
try {
const r = await getInterventionPage({pageNo:1, pageSize:50})
interventionData.value = r.data?.records || []
} catch(e) {}
}
const execIntervention=async(id)=>{await executeIntervention(id);ElMessage.success('已执行');loadData();loadStats()}
onMounted(()=>{loadData();loadStats()})
function openAssess(tool) {
assessForm.patientName = ''
assessForm.encounterId = ''
assessForm.assessmentTool = tool
assessForm.detail = ''
if (tool === 'BRADEN') {
Object.assign(bradenScores, {sensation:4, moisture:4, activity:4, mobility:4, nutrition:4, friction:3})
} else if (tool === 'MORSE') {
Object.assign(morseScores, {history:0, diagnosis:0, ambulation:0, iv:0, gait:0, mental:0})
}
assessVisible.value = true
}
async function submitAssess() {
const data = {
patientName: assessForm.patientName,
encounterId: assessForm.encounterId,
detail: assessForm.detail
}
try {
if (assessForm.assessmentTool === 'BRADEN') {
data.itemScores = JSON.stringify({sensation:bradenScores.sensation, moisture:bradenScores.moisture, activity:bradenScores.activity, mobility:bradenScores.mobility, nutrition:bradenScores.nutrition, friction:bradenScores.friction}); data.totalScore = bradenTotal.value
data.sensation = bradenScores.sensation
data.moisture = bradenScores.moisture
data.activity = bradenScores.activity
data.mobility = bradenScores.mobility
data.nutrition = bradenScores.nutrition
data.friction = bradenScores.friction
await bradenAssess(data)
} else if (assessForm.assessmentTool === 'MORSE') {
data.itemScores = JSON.stringify({history:morseScores.history, diagnosis:morseScores.diagnosis, ambulation:morseScores.ambulation, iv:morseScores.iv, gait:morseScores.gait, mental:morseScores.mental}); data.totalScore = morseTotal.value
data.history = morseScores.history
data.diagnosis = morseScores.diagnosis
data.ambulation = morseScores.ambulation
data.iv = morseScores.iv
data.gait = morseScores.gait
data.mental = morseScores.mental
await morseAssess(data)
} else if (assessForm.assessmentTool === 'NRS2002') {
await nrs2002Assess(data)
} else if (assessForm.assessmentTool === 'PAIN') {
await painAssess(data)
} else if (assessForm.assessmentTool === 'TUBE') {
await tubeAssess(data)
}
ElMessage.success('评估提交成功')
assessVisible.value = false
loadData()
loadStats()
} catch(e) {
ElMessage.error('提交失败: ' + (e.message || '未知错误'))
}
}
function exportReport() {
ElMessage.info('导出功能开发中')
}
onMounted(() => { loadData(); loadStats() })
</script>