This commit is contained in:
lxd 2025-07-15 10:58:45 +08:00
commit 38d7256439
4 changed files with 206 additions and 99 deletions

View File

@ -1,10 +1,14 @@
import request from '@/config/axios'
import { patientinfoVO } from '../patientinfo'
// ABPM动态血压监测 VO
export interface abpmVO {
id: number // 主键ID
examid: string // 检查ID
regid: string // 患者注册ID
name: string // 患者姓名
gender: string // 性别
age: number // 年龄
orgid: string // 机构ID
orgname: string // 机构名称
managerorg: string // 管理机构
@ -48,5 +52,10 @@ export const abpmApi = {
// 导出ABPM动态血压监测 Excel
exportabpm: async (params) => {
return await request.download({ url: `/system/abpm/export-excel`, params })
},
// 批量新增ABPM患者数据
insertAbpmPatientData: async (data: patientinfoVO[]) => {
return await request.post({ url: `/system/abpm/insertAbpmPatientData`, data })
}
}
}

View File

@ -107,11 +107,11 @@
<template #default="scope">
<el-tag
:type="
scope.row.gender === 1 ? 'primary' : scope.row.gender === 2 ? 'danger' : 'info'
scope.row.gender === '1' ? 'primary' : scope.row.gender === '2' ? 'danger' : 'info'
"
size="small"
>
{{ scope.row.gender === 1 ? '男' : scope.row.gender === 2 ? '女' : '未知' }}
{{ scope.row.gender === '1' ? '男' : scope.row.gender === '2' ? '女' : '未知' }}
</el-tag>
</template>
</el-table-column>
@ -201,6 +201,7 @@
</template>
<script setup lang="ts">
import { abpmApi } from '@/api/abpm'
import { ecgdataApi } from '@/api/ecgdata'
import { patientinfoApi, patientinfoVO } from '@/api/patientinfo'
import { getUserProfile, ProfileVO } from '@/api/system/user/profile'
@ -348,6 +349,10 @@ const handleConfirm = async () => {
//
await ecgdataApi.insertEcgPatientData(selectedPatients.value)
}
if (props.type === 'abpm') {
// ABPM
await abpmApi.insertAbpmPatientData(selectedPatients.value)
}
handleCancel()
} catch (error) {
message.error('操作失败,请重试')

View File

@ -2,26 +2,43 @@
<div class="abpm-analysis">
<!-- 分析弹窗组件 -->
<AnalysisDialog ref="analysisDialogRef" />
<!-- 患者选择组件 -->
<PatientSelect
ref="patientSelectRef"
title="选择患者"
type="abpm"
:multiple="true"
:max-select="50"
@confirm="handlePatientConfirm"
@cancel="handlePatientCancel"
/>
<!-- 顶部筛选区 -->
<el-card class="filter-card" shadow="never">
<el-form :inline="true" :model="filters" class="filter-form">
<el-form :inline="true" :model="queryParams" class="filter-form">
<el-form-item label="姓名:">
<el-input
v-model="filters.name"
v-model="queryParams.name"
placeholder="请输入姓名"
clearable
style="width: 120px"
/>
</el-form-item>
<el-form-item label="性别:">
<el-select v-model="filters.gender" placeholder="请选择" clearable style="width: 100px">
<el-select
v-model="queryParams.gender"
placeholder="请选择"
clearable
style="width: 100px"
>
<el-option label="男" value="男" />
<el-option label="女" value="女" />
</el-select>
</el-form-item>
<el-form-item label="日期:">
<el-date-picker
v-model="filters.dateRange"
v-model="queryParams.dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
@ -32,7 +49,7 @@
</el-form-item>
<el-form-item label="佩戴时间:">
<el-date-picker
v-model="filters.wearTime"
v-model="queryParams.wearTime"
type="datetime"
placeholder="选择时间"
style="width: 180px"
@ -40,12 +57,10 @@
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery" :loading="loading">
<Icon icon="ep:search" />搜索
</el-button>
<el-button @click="handleQuery"> <Icon icon="ep:search" />搜索 </el-button>
<el-button @click="resetQuery"> <Icon icon="ep:refresh" />重置 </el-button>
<el-button type="primary" @click="handleAddPatient">
<Icon icon="ep:select" />选择患者
<el-button type="primary" @click="handleSelectPatients">
<Icon icon="ep:user" />选择患者
</el-button>
</el-form-item>
</el-form>
@ -59,7 +74,11 @@
<el-table :data="tableData" border stripe style="width: 100%">
<el-table-column type="index" label="序号" align="center" min-width="60" />
<el-table-column prop="name" label="姓名" align="center" min-width="90" />
<el-table-column prop="gender" label="性别" align="center" min-width="60" />
<el-table-column prop="gender" label="性别" align="center" min-width="60">
<template #default="{ row }">
<span>{{ row.gender === '1' ? '男' : row.gender === '2' ? '女' : '未知' }}</span>
</template>
</el-table-column>
<el-table-column prop="age" label="年龄" align="center" min-width="60" />
<el-table-column prop="wearTime" label="佩戴时间" align="center" min-width="120" />
<el-table-column prop="device" label="设备" align="center" min-width="150">
@ -99,7 +118,7 @@
</div>
</template>
</el-table-column>
<el-table-column prop="org" label="机构" align="center" min-width="120" />
<el-table-column prop="orgname" label="机构" align="center" min-width="120" />
<el-table-column label="分析" align="center" min-width="70">
<template #default="{ row }">
<el-link type="primary" @click="onAnalyze(row)">分析</el-link>
@ -116,20 +135,35 @@
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</el-card>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { Icon } from '@/components/Icon'
import AnalysisDialog from './analysis.vue'
import PatientSelect from '@/patientcom/index.vue'
import { abpmApi, abpmVO } from '@/api/abpm'
const loading = ref(false)
const analysisDialogRef = ref()
const patientSelectRef = ref()
const total = ref(0)
const filters = ref({
//
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
name: '',
gender: '',
dateRange: [],
@ -137,6 +171,9 @@ const filters = ref({
device: ''
})
//
const tableData = ref<abpmVO[]>([])
//
const deviceList = ref<any[]>([])
@ -153,64 +190,61 @@ const fetchDeviceList = () => {
onMounted(() => {
fetchDeviceList()
getList()
})
// tableData value
const tableData = ref([
{
name: '张晓宁',
gender: '男',
age: 45,
createdAt: '2025/7/09',
wearTime: '2025/7/14 11:23',
device: '1001',
org: '廊坊华康',
editingDevice: false
},
{
name: '李小明',
gender: '女',
age: 32,
createdAt: '2025/7/10',
wearTime: '2025/7/15 09:30',
device: '1002',
org: '北京协和',
editingDevice: false
},
{
name: '王大力',
gender: '男',
age: 58,
createdAt: '2025/7/11',
wearTime: '2025/7/16 14:15',
device: '1001',
org: '上海瑞金',
editingDevice: false
}
])
// label
const getDeviceLabel = (value: string) => {
const found = deviceList.value.find((item) => item.value === value)
return found ? found.label : value
}
const handleQuery = () => {
//
const getList = async () => {
loading.value = true
// API
setTimeout(() => {
ElMessage.success('查询功能待接入')
try {
const data = await abpmApi.getabpmPage(queryParams)
tableData.value = data.list
total.value = data.total
} catch (error) {
console.error('获取ABPM数据失败:', error)
ElMessage.error('获取数据失败')
} finally {
loading.value = false
}, 300)
}
}
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
const resetQuery = () => {
filters.value = { name: '', gender: '', dateRange: [], wearTime: '', device: '' }
ElMessage.info('已清除筛选条件')
Object.assign(queryParams, {
pageNo: 1,
pageSize: 10,
name: '',
gender: '',
dateRange: [],
wearTime: '',
device: ''
})
getList()
}
const handleAddPatient = () => {
ElMessage.success('新增患者功能待开发')
const handleSelectPatients = () => {
patientSelectRef.value?.open()
}
const handlePatientConfirm = (selectedPatients: any[]) => {
ElMessage.success(`已成功添加 ${selectedPatients.length} 位患者到ABPM系统`)
// API
//
handleQuery()
}
const handlePatientCancel = () => {
handleQuery()
}
//

View File

@ -105,7 +105,7 @@
</div>
</div>
<div class="chart-container">
<Echart v-if="showChart" :options="chartOption" :height="350" />
<Echart v-if="showChart" :key="chartMode" :options="chartOption" :height="550" />
<div v-else class="chart-loading">
<el-icon style="margin-right: 8px"><Loading /></el-icon>
图表加载中...
@ -115,22 +115,24 @@
<!-- 分析结果 -->
<el-card class="result-section" shadow="never">
<div class="result-title">分析结果</div>
<div class="result-content">
最高收缩压245mmHg最低收缩压194mmHg发生于09日 20:02 最高舒张压119mmHg发生于09日
10:31最低舒张压76mmHg发生于09日 18:42
平均收缩压151mmHg理想&lt;130mmHg平均舒张压106mmHg理想&lt;80
平均脉压120mmHg平均心率106次/
血压高峰概率56.41%血压高峰概率66.67%夜间血压下降率4.63%
<div class="result-title"> 分析结果 </div>
<el-input
v-model="analysisResult"
type="textarea"
class="result-textarea"
:autosize="false"
resize="none"
placeholder="请输入分析结果..."
:rows="10"
/>
<!-- 操作按钮 -->
<div class="result-action-buttons">
<el-button type="primary">保存</el-button>
<el-button @click="handleClose">关闭</el-button>
<el-button type="success">报告浏览</el-button>
</div>
</el-card>
<!-- 操作按钮 -->
<div class="action-section">
<el-button type="primary">保存</el-button>
<el-button @click="handleClose">关闭</el-button>
<el-button type="success">报告浏览</el-button>
</div>
</el-card>
</el-dialog>
</template>
@ -149,6 +151,20 @@ const showChart = ref(false)
//
const chartMode = ref<'all' | 'bp' | 'hr'>('all')
//
const analysisResult =
ref(`最高收缩压245mmHg最低收缩压194mmHg发生于09日 20:02 最高舒张压119mmHg发生于09日 10:31最低舒张压76mmHg发生于09日 18:42。
平均收缩压151mmHg理想<130mmHg平均舒张压106mmHg理想<80
平均脉压120mmHg平均心率106次/
血压高峰概率56.41%血压高峰概率66.67%夜间血压下降率4.63%
分析建议
1. 患者血压明显偏高建议立即调整降压药物治疗方案
2. 夜间血压下降率偏低提示可能存在非杓型血压模式
3. 血压变异性较大需要密切监测并调整用药时间
4. 建议患者改善生活方式包括低盐饮食适量运动控制体重
5. 定期复查24小时动态血压监测评估治疗效果`)
//
const open = () => {
visible.value = true
@ -189,6 +205,7 @@ const allSeries = [
name: '收缩压统计',
type: 'line' as const,
smooth: false,
symbol: 'none',
data: [
120, 130, 125, 140, 135, 150, 160, 245, 200, 210, 220, 180, 170, 160, 150, 140, 130, 120, 125,
130, 140, 150, 200
@ -204,6 +221,7 @@ const allSeries = [
name: '舒张压统计',
type: 'line' as const,
smooth: false,
symbol: 'none',
data: [
80, 85, 90, 95, 100, 105, 110, 119, 100, 110, 115, 100, 95, 90, 85, 80, 76, 80, 85, 90, 95,
100, 110
@ -219,6 +237,7 @@ const allSeries = [
name: '心率统计',
type: 'line' as const,
smooth: false,
symbol: 'none',
data: [
70, 72, 75, 78, 80, 82, 85, 90, 88, 86, 84, 82, 80, 78, 76, 74, 72, 70, 71, 73, 75, 77, 79
],
@ -233,10 +252,10 @@ const allSeries = [
const chartOption = ref<any>({
grid: {
left: 60,
right: 40,
bottom: 60,
top: 80,
left: 30,
right: 20,
bottom: 50,
top: 50,
containLabel: true
},
tooltip: {
@ -321,21 +340,31 @@ const chartOption = ref<any>({
})
function updateChartSeries() {
//
if (!showChart.value) return
let newSeries: any[] = []
let newLegendData: string[] = []
let yAxisConfig: any = {}
if (chartMode.value === 'all') {
newSeries = [...allSeries]
newLegendData = ['收缩压统计', '舒张压统计', '心率统计']
yAxisConfig = {
min: 0,
max: 260
}
} else if (chartMode.value === 'bp') {
newSeries = allSeries.slice(0, 2)
newLegendData = ['收缩压统计', '舒张压统计']
yAxisConfig = {
min: 0,
max: 260
}
} else if (chartMode.value === 'hr') {
newSeries = [allSeries[2]]
newLegendData = ['心率统计']
yAxisConfig = {
min: 60,
max: 120
}
}
//
@ -345,6 +374,10 @@ function updateChartSeries() {
legend: {
...chartOption.value.legend,
data: newLegendData
},
yAxis: {
...chartOption.value.yAxis,
...yAxisConfig
}
}
}
@ -354,7 +387,7 @@ function updateChartSeries() {
.abpm-analysis-dialog {
:deep(.el-dialog__body) {
padding: 0;
height: calc(100vh - 60px);
height: 100vh;
overflow-y: auto;
}
:deep(.el-dialog) {
@ -364,8 +397,7 @@ function updateChartSeries() {
height: 100% !important;
}
:deep(.el-dialog__header) {
padding: 16px 24px;
border-bottom: 1px solid #e4e7ed;
display: none;
}
}
@ -456,18 +488,18 @@ function updateChartSeries() {
margin-bottom: 20px;
:deep(.echart) {
width: 100% !important;
height: 350px !important;
height: 550px !important;
}
:deep(.echart div) {
width: 100% !important;
height: 350px !important;
height: 550px !important;
}
}
.chart-container {
width: 100%;
height: 350px;
min-height: 350px;
height: 550px;
min-height: 550px;
position: relative;
}
@ -496,23 +528,50 @@ function updateChartSeries() {
font-weight: bold;
margin-bottom: 16px;
color: #333;
display: flex;
align-items: center;
gap: 8px;
}
.result-content {
font-size: 15px;
color: #333;
line-height: 1.8;
padding: 16px;
background: #f8f9fa;
border-radius: 8px;
.result-subtitle {
font-size: 12px;
color: #999;
font-weight: normal;
}
.action-section {
.result-textarea {
:deep(.el-textarea__inner) {
height: 350px !important;
min-height: 350px !important;
max-height: 350px !important;
font-size: 15px;
color: #333;
line-height: 1.8;
padding: 16px;
background: #f8f9fa;
border: 1px solid #e4e7ed;
border-radius: 8px;
resize: none;
overflow-y: auto;
font-family: inherit;
}
:deep(.el-textarea__inner:focus) {
border-color: #409eff;
background: #fff;
}
:deep(.el-textarea__inner:hover) {
border-color: #c0c4cc;
}
}
.result-action-buttons {
display: flex;
justify-content: flex-end;
gap: 16px;
margin-top: 20px;
padding: 16px 0;
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid #e4e7ed;
}