rax-medical/src/views/remote-manage/remote-control.vue

1365 lines
52 KiB
Vue
Raw Normal View History

2023-12-20 14:54:01 +08:00
<template>
2024-11-13 13:17:04 +08:00
<div class="remote-box" id="screenBox">
<div class="main-box">
<div class="left-box">
<h3>{{ currentRemote?.taskName }}</h3>
<chart-line ref="chartDom1" class="chart-line" :names="['BIS', 'HR']"
@exception-event="vitalExcepEvent"></chart-line>
<chart-line ref="chartDom2" class="chart-line" :names="['SBP', 'DBP']"
@exception-event="vitalExcepEvent"></chart-line>
<chart-line ref="chartDom3" class="chart-line" :names="['SPO2', 'TEMP']"
@exception-event="vitalExcepEvent"></chart-line>
2024-12-30 15:47:56 +08:00
<chart-ecg ref="chartDom4" class="chart-ecg" :name="['CH1', 'CH2']"
@exception-event="vitalExcepEvent"></chart-ecg>
</div>
2024-11-13 13:17:04 +08:00
<div class="center-box">
<div class="body-box">
<div class="body-img">
<img src="@/assets/imgs/main_body.png" style="width: 100%;height: 100%;"/>
<img class="lung-img" :class="{ 'shake_1': lungAlarm }"
:src="lungAlarm ? imgLungAlarm : imgLung">
<img class="heart-img" :class="{ 'shake_1': heartAlarm }"
:src="heartAlarm ? imgHeartAlarm : imgHeart">
</div>
</div>
2024-12-30 15:47:56 +08:00
<div class="btn-box">
<el-button class="start-btn" color="#F80000" @click="startAI">
开始
</el-button>
<div class="right-btn-box">
<el-button :class="{ 'active': isAIDose }" size="small">AI给药</el-button>
<el-button :class="{ 'active': !isAIDose }" size="small">人工给药</el-button>
</div>
</div>
2024-11-13 13:17:04 +08:00
</div>
<div class="right-box">
<div class="top-btn-box">
2024-12-30 15:47:56 +08:00
<div class="top-left-btn-box" v-loading="connectionUnityLoading">
<el-space direction="vertical">
<el-text style="margin-right:20px;font-size: 18px;font-weight:700;color:#006080 ">
病人尿量(ml):500
</el-text>
</el-space>
<el-button color="#C77000" @click="viewPatientInfo">患者信息</el-button>
<el-button type="text" color="#C77000">当前延迟{{ timeDiffPing }}ms</el-button>
<el-button color="#C77000" @click="connectionUnity('1')" v-if="!whetherControl">连接远程控制
</el-button>
<template v-else>
<el-button color="#C77000">已控制</el-button>
<el-button color="#C77000" @click="connectionUnity('0')">断开远程控制</el-button>
</template>
2024-11-13 13:17:04 +08:00
</div>
<el-button color="#e0e0e0" @click="backRemote">
<el-icon>
<Back/>
</el-icon>
返回
</el-button>
</div>
<div class="monitoring-message">
<div class="left-box">
<span class="unusual-title">异常信息</span>
<ul ref="unusualMsgRef" class="unusual-box">
<li v-for="item in unusual" :key="item">{{ item }}</li>
</ul>
</div>
<div class="right-box">
2024-12-30 15:47:56 +08:00
<!--带视频组件的聊天框-->
<!--<div class="video-box" @click="playPause">-->
<!-- <div class="icon-box">-->
<!-- <el-icon v-if="isVideoPlay">-->
<!-- <VideoPause/>-->
<!-- </el-icon>-->
<!-- <el-icon v-else>-->
<!-- <VideoPlay/>-->
<!-- </el-icon>-->
<!-- </div>-->
<!-- &lt;!&ndash; poster="@/assets/imgs/video_bck.png" &ndash;&gt;-->
<!-- <video ref="liveVideo">-->
<!-- <source src="@/assets/medical.mp4" type="video/mp4"/>-->
<!-- </video>-->
<!--</div>-->
2024-11-13 13:17:04 +08:00
<!--<div class="message-box">-->
<!-- <ul ref="msgLog" class="message-log">-->
<!-- <li v-for="(item, index) in mssageList" :key="'msg-log-' + index"-->
<!-- :class="{ 'align-right': item.createUser == userInfo.account }">-->
2024-12-30 15:47:56 +08:00
<!-- <span>{{ item.content }}</span>-->
2024-11-13 13:17:04 +08:00
<!-- </li>-->
<!-- </ul>-->
<!-- <div class="send-box">-->
2024-12-30 15:47:56 +08:00
<!-- <el-input v-model="msgVal" placeholder="请输入消息"/>-->
<!-- <el-button color="#006080" @click="sendMsg">发送消息</el-button>-->
2024-11-13 13:17:04 +08:00
<!-- </div>-->
<!--</div>-->
2024-12-30 15:47:56 +08:00
<!-- 聊天框, 添加音频组件 -->
<div class="message-box">
<ul ref="msgLog" class="message-log">
<li v-for="(item, index) in mssageList" :key="'msg-log-' + index"
:class="{ 'align-right': item.createUser == userInfo.account }">
<span v-if="item.msgType === 'msg'">{{ item.content }}</span>
<audio v-if="item.msgType === 'audio'" controls>
21312
<source :src="item.content" type="audio/mpeg"/>
您的浏览器不支持音频元素
</audio>
</li>
</ul>
<div class="send-box">
<el-input style="width: 60%" v-model="msgVal" placeholder="请输入消息"/>
<el-button style="color: #006080; width: 20%; margin-left: 10px;" @click="sendMsg">发送消息</el-button>
<el-button style="color: #006080; width: 20%" class="mic-btn" @mousedown="startRecording" @mouseup="stopRecording"
@mouseleave="stopRecording">录音</el-button>
</div>
<div v-if="isRecording" class="mic-icon">
<i class="fa-solid fa-microphone"></i> <!-- 麦克风图标 -->
正在录音... {{ remainingTime }} 秒剩余
</div>
</div>
2024-11-13 13:17:04 +08:00
</div>
</div>
<!-- table1 -->
<div class="table-box">
<el-table :data="varTableData" height="100%" style="width: 100%">
2024-12-30 15:47:56 +08:00
<el-table-column prop="linkNum" label="泵号" width="40" align="center"/>
<el-table-column prop="medicineName" label="药物名称" align="center"/>
<el-table-column prop="medicineRate" label="速度(ml/h)" width="120">
<template #header>
<span>速度(ml/h)</span>
</template>
<template #default="scope">
<span style="margin-right:25px">{{ scope.row.medicineRate }}</span>
<el-link type="success" v-if="scope.row.change>=0">+{{ scope.row.change }}</el-link>
<el-link type="danger" v-else>{{ scope.row.change }}</el-link>
</template>
</el-table-column>
<el-table-column prop="countMedicine" label="累计药量(ml)" width="100">
</el-table-column>
<el-table-column prop="state" label="工作状态" width="70" align="center"/>
<el-table-column label="特殊情况人为干预" align="center">
<template #default="scope">
<div class="table-btn-box">
<el-button size="small" color="#006080" @click="tableItemPlus(scope)"
:disabled="!whetherControl">
<el-icon>
<Plus/>
</el-icon>
</el-button>
<el-button size="small" color="#006080" :disabled="!whetherControl"
@click="tableItemMinus(scope)">
<el-icon>
<Minus/>
</el-icon>
</el-button>
<el-button size="small" color="#006080"
@click="tableItemConfirm(scope, varTableData)"
:disabled="!whetherControl">确定
</el-button>
<el-button size="small" color="#006080" @click="tableItemCancel(scope)"
:disabled="!whetherControl">取消
</el-button>
</div>
</template>
2024-11-13 13:17:04 +08:00
</el-table-column>
</el-table>
</div>
<!-- table2 -->
<div class="table-box">
<el-table :data="fixedTableData" height="100%" style="width: 100%">
2024-12-30 15:47:56 +08:00
<el-table-column prop="linkNum" label="泵号" width="40" align="center"/>
<el-table-column prop="medicineName" label="药物名称" align="center"/>
<el-table-column prop="medicineRate" label="剂量(ml)" width="120">
<template #default="scope">
<span style="margin-right:25px">{{ scope.row.medicineRate }}</span>
<el-link type="success" v-if="scope.row.change>=0">+{{ scope.row.change }}</el-link>
<el-link type="danger" v-else>{{ scope.row.change }}</el-link>
</template>
</el-table-column>
<el-table-column prop="countMedicine" label="累计药量(ml)" width="100">
</el-table-column>
<el-table-column prop="state" label="工作状态" width="70" align="center"/>
<el-table-column label="特殊情况人为干预" align="center">
<template #default="scope">
<div class="table-btn-box">
<el-button size="small" color="#006080" @click="tableItemPlus(scope)"
:disabled="!whetherControl">
<el-icon>
<Plus/>
</el-icon>
</el-button>
<el-button size="small" color="#006080" :disabled="!whetherControl"
@click="tableItemMinus(scope)">
<el-icon>
<Minus/>
</el-icon>
</el-button>
<el-button size="small" color="#006080"
@click="tableItemConfirm(scope, fixedTableData)"
:disabled="!whetherControl">确定
</el-button>
<el-button size="small" color="#006080" @click="tableItemCancel(scope)"
:disabled="!whetherControl">取消
</el-button>
</div>
</template>
</el-table-column>
2024-11-13 13:17:04 +08:00
</el-table>
</div>
2024-11-13 13:17:04 +08:00
</div>
</div>
2024-05-30 09:24:07 +08:00
2024-11-13 13:17:04 +08:00
<el-dialog v-model="setDatabaseDialog" title="请选择需要查询的病人" width="300px" align-center>
<el-select v-model="database" filterable placeholder="Select" style="width: 100%;">
<el-option v-for="item in databaseOptions" :key="item.value" :label="item.label" :value="item.value"/>
</el-select>
<template #footer>
2023-12-20 14:54:01 +08:00
<span class="dialog-footer">
<el-button @click="setDatabaseDialog = false">取消</el-button>
<el-button type="primary" @click="setDatabase">确定</el-button>
</span>
2024-11-13 13:17:04 +08:00
</template>
</el-dialog>
2024-12-30 15:47:56 +08:00
<el-dialog v-model="isPatientDialog" title="患者信息" width="40%">
2024-11-13 13:17:04 +08:00
<PatientsForm ref="patientsFormRef" type="view" @close="isPatientDialog = false"/>
</el-dialog>
</div>
2023-12-20 14:54:01 +08:00
</template>
<script lang="ts" setup>
2024-12-30 15:47:56 +08:00
import {h} from 'vue';
import _ from 'lodash';
import {computed, onMounted, onUnmounted, reactive, ref, watch} from 'vue';
2024-05-30 09:24:07 +08:00
import {useRouter} from 'vue-router'
2024-12-30 15:47:56 +08:00
import {ElMessage, ElMessageBox} from 'element-plus';
2024-05-30 09:24:07 +08:00
import {dateFormater} from '@/utils/date-util';
2023-12-20 14:54:01 +08:00
import chartLine from './chart/chart-line.vue';
import chartEcg from './chart/chart-ecg.vue';
2023-12-27 16:49:37 +08:00
import PatientsForm from '@/views/patients-manage/form/patients-form.vue'
2023-12-20 14:54:01 +08:00
import imgLung from '@/assets/imgs/lung.png';
import imgHeart from '@/assets/imgs/heart.png';
import imgLungAlarm from '@/assets/imgs/lung_alarm.png';
import imgHeartAlarm from '@/assets/imgs/heart_alarm.png';
2024-05-30 09:24:07 +08:00
import {useRemoteWsStore} from "@/stores/remote-ws-store";
import {useUserStore} from "@/stores/user-info-store";
2024-12-30 15:47:56 +08:00
import {getPatientInfo} from "@/api/patient";
2023-12-20 14:54:01 +08:00
const router = useRouter()
const medicineCustom: any[] = [
2024-11-13 13:17:04 +08:00
{name: '丙泊酚', plus: 0.5, total: 50},
{name: '舒芬太尼', plus: 1, total: 100},
{name: '瑞芬太尼', plus: 0.05, total: 5},
{name: '顺阿曲库胺', plus: 0.02, total: 2},
{name: '尼卡地平', plus: 1, total: 100},
{name: '艾司洛尔', plus: 1, total: 100},
{name: '麻黄素', plus: 1, total: 100},
{name: '阿托品', plus: 1, total: 100},
{name: '罗库溴铵', plus: 0.1, total: 10}
2024-04-23 09:42:25 +08:00
]
const remoteWsStore = useRemoteWsStore()
2024-12-30 15:47:56 +08:00
const currentRemote = ref(remoteWsStore.getRemoteTask()[remoteWsStore.getCurrentTaskIndex()])
2024-05-30 09:58:03 +08:00
const userInfoStore = useUserStore()
2024-12-30 15:47:56 +08:00
const userInfo = ref(userInfoStore.getlogin())
2023-12-20 14:54:01 +08:00
const chartDom1 = ref(),
chartDom2 = ref(),
chartDom3 = ref(),
chartDom4 = ref(),
liveVideo = ref(),
msgLog = ref(),
unusualMsgRef = ref(),
patientsFormRef = ref()
2024-03-16 12:00:13 +08:00
const isPatientDialog = ref(false)
2024-04-23 09:42:25 +08:00
const database = ref('')
const databaseOptions = ref([] as { value: string, label: string }[])
2024-12-30 15:47:56 +08:00
2023-12-20 14:54:01 +08:00
const setDatabaseDialog = ref(false);
const featureTable = ref([] as any[]);
const lungAlarm = ref(false); // 肺部警告
const heartAlarm = ref(false); // 心脏警告
2024-12-30 15:47:56 +08:00
const isAIDose = ref(0); // 是否AI给药
2023-12-20 14:54:01 +08:00
const isVideoPlay = ref(false); // 视频是否播放
2024-12-30 15:47:56 +08:00
// const videoSrc = ref('https://www.runoob.com/try/demo_source/mov_bbb.mp4');
2023-12-20 14:54:01 +08:00
const unusual = ref([] as any);
2024-04-23 09:42:25 +08:00
const fixedTableData = ref([] as any[]);
const varTableData = ref([] as any[]);
const table1SpeedVal = ref('');
2024-12-30 15:47:56 +08:00
const whetherControl = ref(false);
const subscribeMedicineData = ref(null);
const intervalFun = ref(null);
const timeDiffPing = ref(0);
2024-04-23 09:42:25 +08:00
let currentAIMedicine: any;
let currentDocMedicine: any;
const medicineSpeedTemp: any = {};
2024-12-30 15:47:56 +08:00
const connectionUnityLoading = ref(false);
const lastAddMedicineTime = ref(null);
2023-12-20 14:54:01 +08:00
onMounted(() => {
2024-12-30 15:47:56 +08:00
if (!(currentRemote.value && currentRemote.value.isRemote)) {
router.push('/remote-manage/remote-manage');
2024-11-13 13:17:04 +08:00
return;
}
2024-12-30 15:47:56 +08:00
msgLogScrollBottom()
initScale()
createConnect()
subscribeWS()
intervalFun.value = setInterval(loopSendMedicinePing, 1000 * 10) as any;
2023-12-20 14:54:01 +08:00
});
2024-03-16 12:00:13 +08:00
2024-04-23 09:42:25 +08:00
onUnmounted(() => {
2024-12-30 15:47:56 +08:00
if (currentRemote.value) {
remoteWsStore.unsubscribeChat(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentRemote.value.index);
remoteWsStore.unsubscribeMedicine(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentRemote.value.index);
remoteWsStore.unsubscribeVital(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentRemote.value.index);
disconnect()
}
intervalFun.value = null
})
watch(subscribeMedicineData, (newValue: any, oldValue: any) => {
if (newValue) {
const {msgType, unityConnectionFlag, msg} = newValue;
switch (msgType) {
case 'unityRequestConnection':
const params: any = {
name: currentRemote.value.patient,
id: currentRemote.value.patientId,
date: currentRemote.value.date,
index: currentRemote.value.index,
msgType: "webResponseConnection",
flag: "1"
}
ElMessageBox.confirm('确定接受远程控制', '确认提示', {
confirmButtonText: '确定',
cancelButtonText: '拒绝',
})
.then(() => {
whetherControl.value = unityConnectionFlag === '1';
remoteWsStore.reqMedicineConnect({...params, flag: unityConnectionFlag}, (res: any) => {
console.log('res >>>>>', res);
});
})
.catch(() => {
// 点击取消按钮后的逻辑
whetherControl.value = false;
remoteWsStore.reqMedicineConnect({...params, flag: '0'}, (res: any) => {
console.log('res >>>>>', res);
});
});
break;
case 'unityResponseConnection':
connectionUnityLoading.value = false
whetherControl.value = unityConnectionFlag === '1';
if (unityConnectionFlag === '1') {
ElMessage.success('连接成功')
} else {
ElMessage.info('连接断开')
}
break;
case 'getMedicine':
ElMessage.success('给药成功')
break;
case 'pong':
timeDiffPing.value = newValue.msg;
break;
case 'connectionMsg':
ElMessage.info(msg)
default:
break;
}
}
})
function createConnect() {
remoteWsStore.createChatConnect(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentRemote.value.index)
remoteWsStore.createMedicineConnect(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentRemote.value.index)
}
function loopSendMedicinePing() {
remoteWsStore.loopSendMedicinePing(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentRemote.value.index, (res: any) => {
})
}
// 连接远程控制
function connectionUnity(flag: string) {
if (flag === "1") {
console.log("连接远程控制");
connectionUnityLoading.value = true
} else {
console.log("断开远程控制");
connectionUnityLoading.value = false
}
const params: any = {
name: currentRemote.value.patient,
id: currentRemote.value.patientId,
date: currentRemote.value.date,
index: currentRemote.value.index,
msgType: "webRequestConnection",
flag
}
remoteWsStore.reqMedicineConnect(params, () => {
connectionUnityLoading.value = false
});
setTimeout(() => {
if (connectionUnityLoading.value) {
connectionUnityLoading.value = false
}
}, 10000)
//
}
function disconnect() {
remoteWsStore.disconnectMedicine(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentRemote.value.index)
remoteWsStore.disconnectChat(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentRemote.value.index)
if (!router.currentRoute.value.path.startsWith('/remote-manage/')) {
2024-11-13 13:17:04 +08:00
const tasks: any = remoteWsStore.getRemoteTask()
tasks.forEach((task: any) => {
remoteWsStore.disconnect(task.patient, task.patientId, task.date, task.index)
})
}
2024-12-30 15:47:56 +08:00
}
2024-03-16 12:00:13 +08:00
2024-04-23 09:42:25 +08:00
function subscribeWS() {
2024-11-13 13:17:04 +08:00
subscribeVital()
subscribeChat()
2024-12-30 15:47:56 +08:00
subscribeMedicine()
}
const subscribeVital = () => {
onVitalClose()
remoteWsStore.subscribeVital(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentRemote.value.index,
(res: any) => {
// console.log(res);
if (res.status != 2) {
2024-11-13 13:17:04 +08:00
const data = JSON.parse(res.data);
2024-12-30 15:47:56 +08:00
chartDom1.value.updateChartData(data.vitalSignsList[0]);
chartDom2.value.updateChartData(data.vitalSignsList[0]);
chartDom3.value.updateChartData(data.vitalSignsList[0]);
chartDom4.value.updateChartData(data.vitalSignsList[0]);
isAIDose.value = data.flags.aiFlag === '1' ? 1 : 0;
// console.log('data >>>>>', data);
if (!data.rateModTime) {
updateMedicineTable(data.medicineList);
return
}
if (lastAddMedicineTime.value !== data.rateModTime) {
lastAddMedicineTime.value = data.rateModTime
updateMedicineTable(data.medicineList);
2024-11-13 13:17:04 +08:00
}
2024-12-30 15:47:56 +08:00
}
})
}
2024-06-05 14:09:09 +08:00
2024-12-30 15:47:56 +08:00
const subscribeChat = () => {
onChatClose()
remoteWsStore.subscribeChat(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentRemote.value.index,
(res: any) => {
mssageList.value.push(JSON.parse(res.data));
})
2024-06-05 14:09:09 +08:00
}
2024-12-30 15:47:56 +08:00
const subscribeMedicine = () => {
onMedicineClose()
remoteWsStore.subscribeMedicine(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentRemote.value.index,
(res: any) => {
2024-11-13 13:17:04 +08:00
const data = JSON.parse(res.data);
2024-12-30 15:47:56 +08:00
subscribeMedicineData.value = data;
if (data.status != 1) {
if (data.medicine) {
if (varTableData.value[data.medicine]) varTableData.value[data.medicine] = medicineSpeedTemp[data.medicine];
if (fixedTableData.value[data.medicine]) fixedTableData.value[data.medicine] = medicineSpeedTemp[data.medicine];
}
} else {
ElMessage.error(data.msg)
2024-11-13 13:17:04 +08:00
}
})
2024-12-30 15:47:56 +08:00
}
const onVitalClose = () => {
remoteWsStore.vitalOnclose(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentRemote.value.index, () => {
ElMessage.info('远程控制' + (currentRemote.value.index + 1) + ' 生命体征数据连接断开,正在尝试重连……')
2024-11-13 13:17:04 +08:00
setTimeout(() => {
2024-12-30 15:47:56 +08:00
remoteWsStore.createVitalConnect(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentRemote.value.index)
2024-11-13 13:17:04 +08:00
subscribeVital()
}, 3000)
})
2024-06-05 14:09:09 +08:00
}
2024-12-30 15:47:56 +08:00
const onChatClose = () => {
remoteWsStore.chatOnclose(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentRemote.value.index, () => {
ElMessage.info('远程控制' + (currentRemote.value.index + 1) + ' 通讯连接断开,正在尝试重连……')
2024-11-13 13:17:04 +08:00
setTimeout(() => {
2024-12-30 15:47:56 +08:00
remoteWsStore.createChatConnect(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentRemote.value.index)
2024-11-13 13:17:04 +08:00
subscribeChat()
}, 3000)
})
2023-12-20 14:54:01 +08:00
}
2024-03-16 12:00:13 +08:00
2024-12-30 15:47:56 +08:00
const onMedicineClose = () => {
remoteWsStore.medicineOnclose(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentRemote.value.index, () => {
ElMessage.info('远程控制' + (currentRemote.value.index + 1) + ' 给药连接断开,正在尝试重连……')
setTimeout(() => {
remoteWsStore.createMedicineConnect(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentRemote.value.index)
subscribeMedicine()
}, 3000)
})
}
2024-04-23 09:42:25 +08:00
function initData() {
2024-11-13 13:17:04 +08:00
lungAlarm.value = false;
heartAlarm.value = false;
2024-12-30 15:47:56 +08:00
isAIDose.value = 0;
2024-11-13 13:17:04 +08:00
unusual.value = [];
mssageList.value = [];
fixedTableData.value = [];
varTableData.value = [];
featureTable.value = [];
try {
msgLogScrollBottom();
} catch (error) {
}
2023-12-20 14:54:01 +08:00
}
2024-03-16 12:00:13 +08:00
2024-12-30 15:47:56 +08:00
function updateMedicineTable(doctorMedicine: any) {
const varTableData1 = doctorMedicine.slice(0, 4)
const fixedTableData1 = doctorMedicine.slice(4, 8)
varTableData.value = varTableData1.map((i: any) => {
return {
...i,
change: i?.change || 0
2024-05-30 09:24:07 +08:00
}
2024-12-30 15:47:56 +08:00
})
fixedTableData.value = fixedTableData1.map((i: any) => {
return {
...i,
change: i?.change || 0
2024-05-30 09:24:07 +08:00
}
2024-12-30 15:47:56 +08:00
})
2023-12-20 14:54:01 +08:00
}
2024-03-16 12:00:13 +08:00
2024-12-30 15:47:56 +08:00
function vitalExcepEvent(data: any) {
logUpdate(data)
2023-12-20 14:54:01 +08:00
}
2024-03-16 12:00:13 +08:00
2024-12-30 15:47:56 +08:00
function logUpdate(msg: string) {
unusual.value.push(dateFormater('HH:mm:ss') + msg);
2024-11-13 13:17:04 +08:00
setTimeout(() => {
unusualMsgRef.value.scrollTo({
top: unusualMsgRef.value.scrollHeight,
behavior: 'smooth'
});
2024-05-30 09:24:07 +08:00
});
2023-12-20 14:54:01 +08:00
}
2024-03-16 12:00:13 +08:00
2024-04-11 11:53:29 +08:00
// table 速度选择
function table1SpeedCommand(e: any) {
2024-11-13 13:17:04 +08:00
table1SpeedVal.value = e
2024-04-11 11:53:29 +08:00
}
2024-04-23 09:42:25 +08:00
2023-12-20 14:54:01 +08:00
function keepFit(designWidth: number, designHeight: number, renderDomId: string) {
2024-11-13 13:17:04 +08:00
let width = designWidth || 1920,
height = designHeight || 1010;
let clientHeight = document.documentElement.clientHeight - 50;
let clientWidth = document.documentElement.clientWidth;
let scale = 1;
scale = (clientHeight / clientWidth) < (height / width) ? clientHeight / height : clientWidth / width;
// scale = clientHeight / height;
// scale = clientWidth / width;
const renderDom = document.getElementById(renderDomId);
if (renderDom) {
renderDom.style.height = clientHeight / scale + 'px';
renderDom.style.width = clientWidth / scale + 'px';
renderDom.style.transform = 'scale(' + scale + ')';
}
2023-12-20 14:54:01 +08:00
}
2024-03-16 12:00:13 +08:00
2023-12-20 14:54:01 +08:00
function initScale() {
2024-11-13 13:17:04 +08:00
let w = 1920,
h = 1010;
2024-05-30 09:24:07 +08:00
keepFit(w, h, 'screenBox');
2024-11-13 13:17:04 +08:00
window.addEventListener('resize', () => {
keepFit(w, h, 'screenBox');
});
2023-12-20 14:54:01 +08:00
}
const selectDatabase = (value: any) => {
2024-11-13 13:17:04 +08:00
initData();
2023-12-20 14:54:01 +08:00
};
const setDatabase = () => {
2024-11-13 13:17:04 +08:00
initData();
setDatabaseDialog.value = false;
2023-12-20 14:54:01 +08:00
};
2024-04-23 09:42:25 +08:00
2023-12-27 16:49:37 +08:00
const viewPatientInfo = () => {
2024-12-30 15:47:56 +08:00
getPatientInfo(currentRemote.value.patient, currentRemote.value.patientId,
2024-11-13 13:17:04 +08:00
currentRemote.value.date).then(res => {
})
2024-12-30 15:47:56 +08:00
isPatientDialog.value = true;
setTimeout(() => {
patientsFormRef.value.resetData()
patientsFormRef.value.formData = JSON.parse(JSON.stringify({
id: '',
name: '测试',
code: 'afasf',
sex: '男',
dept: '测试',
bodyHeight: '170',
isMedicalHistory: '无',
bodyWeight: '80',
medicalHistoryName: '无',
age: '22',
allergyHistoryName: '无',
phone: '123456',
hospitalTime: new Date()
}))
}, 0)
2023-12-27 16:49:37 +08:00
}
2023-12-20 14:54:01 +08:00
const backRemote = () => {
2024-11-13 13:17:04 +08:00
router.back();
2023-12-20 14:54:01 +08:00
}
const playPause = () => {
2024-11-13 13:17:04 +08:00
if (liveVideo.value.paused) {
liveVideo.value.play(); // 播放视频
isVideoPlay.value = true;
} else {
liveVideo.value.pause(); // 暂停视频
isVideoPlay.value = false;
}
2023-12-20 14:54:01 +08:00
}
const sendMsg = () => {
2024-11-13 13:17:04 +08:00
if (msgVal.value.trim() == '') return;
remoteWsStore.sendMsg(currentRemote.value.patient, currentRemote.value.patientId,
2024-12-30 15:47:56 +08:00
currentRemote.value.date, msgVal.value, currentRemote.value.index, function (res: any) {
2024-11-13 13:17:04 +08:00
if (res.code == 1) {
ElMessage.error(res.msg)
}
})
msgVal.value = '';
setTimeout(() => {
msgLogScrollBottom()
}, 0);
2024-04-23 09:42:25 +08:00
}
2023-12-20 14:54:01 +08:00
2024-04-23 09:42:25 +08:00
function msgLogScrollBottom() {
2024-11-13 13:17:04 +08:00
msgLog.value.scrollTo({
top: msgLog.value.scrollHeight,
behavior: 'smooth'
});
2024-04-23 09:42:25 +08:00
}
2024-03-16 12:00:13 +08:00
2024-04-23 09:42:25 +08:00
const tableItemPlus = (e: any) => {
2024-12-30 15:47:56 +08:00
const obj = medicineCustom.find(item => item.name === e.row.medicineName) || {plus: 1};
const medicineRate = e.row.medicineRate
const currTotal = _.add(Number(medicineRate), Number(e.row.change))
if (currTotal >= obj.total) {
ElMessage({
type: 'error',
message: `当前药物速率已超过设定的最大值${obj.total},不能增加只能减少`
})
return
}
;
const currRate = Number(e.row.change);
if (isNaN(currRate)) {
return
}
const rate = _.add(Number(currRate), Number(obj.plus));
e.row.change = _.floor(rate, 2)
2024-04-23 09:42:25 +08:00
}
2024-03-16 12:00:13 +08:00
2024-04-23 09:42:25 +08:00
const tableItemMinus = (e: any) => {
2024-12-30 15:47:56 +08:00
const obj = medicineCustom.find(item => item.name === e.row.medicineName) || {plus: 1};
const medicineRate = e.row.medicineRate;
const change = e.row.change;
if (Number(change) < 0) {
const reds = Number(change) + Number(medicineRate)
if (reds <= 0) {
ElMessage({
type: 'error',
message: '当前药物速率不能小于0'
})
return
}
}
const nowTotal = _.subtract(Number(medicineRate), Number(change))
e.row.change = _.subtract(change, obj.plus)
e.row.change = _.floor(e.row.change, 2)
2024-04-23 09:42:25 +08:00
}
2024-03-16 12:00:13 +08:00
2024-04-23 09:42:25 +08:00
const tableItemConfirm = (e: any, tableData: any) => {
2024-12-30 15:47:56 +08:00
const finalRate = _.add(Number(e.row.medicineRate), Number(e.row.change))
const final = _.floor(finalRate, 2)
ElMessageBox.confirm('确定给药吗?', '确认提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
message: h('p', null, [
h('span', null, '确定给药吗?'),
h('div', {style: 'font-size:14px;color:#085a75'}, `药物名称:${e.row.medicineName}`),
h('div', {style: 'font-size:14px;color:#085a75'}, `速率:${final}`),
]),
})
.then(() => {
// 点击确定按钮后的逻辑
if (tableData.length < 1) return;
const params = {
msgType: "addMedicine",
name: currentRemote.value.patient,
id: currentRemote.value.patientId,
date: currentRemote.value.date,
flag: "1",
medicine: e.row.medicineName,
value: _.add(Number(e.row.medicineRate), Number(e.row.change)),
index: currentRemote.value.index
}
remoteWsStore.sendMedicine(params, function () {
});
})
.catch(() => {
// 点击取消按钮后的逻辑
// e.row.speed = medicineSpeedTemp[e.row.name];
});
2024-04-23 09:42:25 +08:00
}
const tableItemCancel = (e: any) => {
2024-12-30 15:47:56 +08:00
// e.row.speed = medicineSpeedTemp[e.row.name];
2024-04-23 09:42:25 +08:00
}
2024-03-16 12:00:13 +08:00
2024-04-23 09:42:25 +08:00
function startAI() {
2024-11-13 13:17:04 +08:00
const params: any = {
name: currentRemote.value.patient,
id: currentRemote.value.patientId,
date: currentRemote.value.date,
flag: "0",
}
2024-12-30 15:47:56 +08:00
remoteWsStore.sendMedicine(params, function () {
2024-11-13 13:17:04 +08:00
});
2024-04-23 09:42:25 +08:00
}
2024-03-16 12:00:13 +08:00
2024-11-13 13:17:04 +08:00
/*
* 聊天室
*/
const msgVal = ref('');
const mssageList = ref([] as any);
2024-12-30 15:47:56 +08:00
const isRecording = ref(false); // 用于跟踪录音状态
const mediaRecorder = ref<MediaRecorder | null>(null);
const audioChunks = ref<Blob[]>([]);
const remainingTime = ref(10); // 初始化剩余时间为 10 秒
// 将 Blob 对象转换为 Base64 字符串
const convertBlobToBase64 = (blob: Blob): Promise<string> => {
return new Promise((resolve, reject) => {
const reader = new FileReader(); // 创建 FileReader 实例
// 定义 onloadend 事件
reader.onloadend = () => {
// 检查 result 是否不为 null
if (reader.result) {
// const base64String = (reader.result as string).split(',')[1]; // 获取 Base64 部分
const base64String = (reader.result as string); // 获取 Base64 部分
resolve(base64String); // 返回 Base64 字符串
} else {
reject(new Error("读取 Base64 失败")); // 处理读取失败情况
}
};
// 定义错误处理
reader.onerror = (error) => {
reject(error); // 处理读取过程中可能出现的错误
};
// 将 Blob 读取为数据 URL
reader.readAsDataURL(blob);
});
};
// 开始录音
const startRecording = async () => {
if (isRecording.value) return; // 预防多次点击
isRecording.value = true; // 设置录音状态为开启
remainingTime.value = 10; // 重置剩余时间为 10 秒
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
mediaRecorder.value = new MediaRecorder(stream);
// 监听数据可用事件
mediaRecorder.value.ondataavailable = (event) => {
// console.log("录音中...");
audioChunks.value.push(event.data);
// console.log("当前音频数据块:", event.data); // 这里打印每个数据块
};
// 监听停止事件
mediaRecorder.value.onstop = async () => {
isRecording.value = false; // 停止录音状态
if (audioChunks.value.length === 0) {
console.error("没有音频数据可用于创建 Blob");
return;
}
// 获取 Blob 数据
const audioBlob = new Blob(audioChunks.value, {type: 'audio/webm; codecs=opus'});
try {
// 转换为 Base64
const base64Audio = await convertBlobToBase64(audioBlob);
// console.log("转换后的 Base64 字符串:", base64Audio);
// 准备 WebSocket 消息
const index = remoteWsStore.getCurrentTaskIndex();
// 发送消息
remoteWsStore.sendAudio(
currentRemote.value.patient,
currentRemote.value.patientId,
currentRemote.value.date,
base64Audio.replace(/\s+/g, ''), // 移除空白字符
index,
function (res: any) {
if (res.code == 1) {
ElMessage.error(res.msg);
}
}
);
} catch (error) {
console.error("转换为 Base64 失败:", error);
}
audioChunks.value = []; // 可选: 重置音频块
};
// 启动录音
mediaRecorder.value.start();
// console.log("录音已开始"); // 别忘了在此处添加提示
// 设置最大录音时间为 10 秒,创建倒计时
const timer = setInterval(() => {
if (remainingTime.value > 0) {
remainingTime.value--; // 每秒减少1
} else {
stopRecording(); // 达到0秒时自动停止
// console.log("录音时间到,已自动停止!");
clearInterval(timer); // 清除倒计时
}
}, 1000); // 每秒执行一次
};
// 停止录音
const stopRecording = () => {
if (mediaRecorder.value) {
mediaRecorder.value.stop();
isRecording.value = false; // 设置录音状态为关闭
}
};
2024-05-30 10:18:39 +08:00
2024-11-13 13:17:04 +08:00
</script>
<style lang="scss" scoped>
.remote-box {
2024-12-30 15:47:56 +08:00
width: 100%;
height: 100%;
2024-11-13 13:17:04 +08:00
background: #f1f3f5;
padding: 10px 0 20px 0;
transform-origin: top left;
overflow-y: auto;
.el-button {
color: white;
}
.main-box {
width: 100%;
2024-11-13 13:17:04 +08:00
height: 100%;
background: white;
padding: 20px;
box-sizing: border-box;
display: flex;
justify-content: space-between;
2024-05-30 09:24:07 +08:00
2024-11-13 13:17:04 +08:00
.left-box {
2024-12-30 15:47:56 +08:00
width: 35%;
2024-11-13 13:17:04 +08:00
height: 100%;
2024-05-30 10:18:39 +08:00
2024-11-13 13:17:04 +08:00
& > h3 {
font-size: 26px;
color: $main-color;
line-height: 30px;
margin-bottom: 20px;
font-weight: 600;
}
2024-11-13 13:17:04 +08:00
.chart-line {
width: 100%;
height: calc(25% - 30px);
margin-bottom: 20px;
background: #e8f5f8;
border-top-left-radius: 20px;
border-top-right-radius: 20px;
// overflow: hidden;
}
2024-05-30 10:18:39 +08:00
2024-11-13 13:17:04 +08:00
.chart-ecg {
width: 100%;
height: calc(25% - 50px);
border-left: 1px solid #92b3c1;
border-bottom: 1px solid #92b3c1;
&::before {
content: '';
position: absolute;
width: 0;
height: 0;
top: 0;
left: -5px;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 15px solid #285e7d;
border-top: 0;
}
2024-05-30 09:24:07 +08:00
2024-11-13 13:17:04 +08:00
&::after {
content: '';
position: absolute;
width: 0;
height: 0;
bottom: -5px;
right: 0;
border-left: 15px solid #285e7d;
border-right: 0;
border-bottom: 5px solid transparent;
border-top: 5px solid transparent;
}
2024-05-30 09:24:07 +08:00
}
2024-05-30 10:18:39 +08:00
}
2024-11-13 13:17:04 +08:00
.center-box {
width: calc(100% - 1500px);
min-width: 500px;
2024-05-30 10:18:39 +08:00
height: 100%;
2024-05-30 09:24:07 +08:00
2024-11-13 13:17:04 +08:00
.body-box {
2024-05-30 10:18:39 +08:00
width: 100%;
2024-11-13 13:17:04 +08:00
height: calc(100% - 110px);
display: flex;
justify-content: center;
align-items: center;
.body-img {
position: relative;
// width: 400px;
// height: 650px;
margin-top: 80px;
width: 500px;
height: 812.5px;
min-height: 650px;
2024-12-30 15:47:56 +08:00
//width: 1000px;
//height: 850px;
//min-height: 650px;
2024-11-13 13:17:04 +08:00
.lung-img {
position: absolute;
width: 500px;
2024-12-30 15:47:56 +08:00
//width: 100%;
2024-11-13 13:17:04 +08:00
height: auto;
top: 240px;
left: 0;
transition: all 2s;
}
.heart-img {
position: absolute;
width: 500px;
height: auto;
top: 420px;
left: 0;
transition: all 2s;
}
}
}
2024-11-13 13:17:04 +08:00
.btn-box {
position: relative;
width: 100%;
height: 110px;
display: flex;
justify-content: center;
align-items: center;
2024-12-30 15:47:56 +08:00
.start-btn {
width: 230px;
height: 40px;
font-size: 20px;
border-radius: 16px;
font-weight: 600;
}
.right-btn-box {
position: absolute;
width: 100px;
height: 62px;
top: calc(50% - 31px);
right: 0;
display: flex;
flex-direction: column;
justify-content: space-between;
.el-button {
width: 100%;
padding: 0;
height: 25px;
line-height: 22px;
border: 1px solid #e0e0e0;
color: #616161;
&.active {
background: $main-color;
color: white;
}
&:last-of-type {
margin: 0;
}
}
}
2024-11-13 13:17:04 +08:00
}
}
2024-11-13 13:17:04 +08:00
.right-box {
2024-12-30 15:47:56 +08:00
width: 35%;
2024-11-13 13:17:04 +08:00
height: 100%;
display: flex;
flex-direction: column;
2024-11-13 13:17:04 +08:00
.top-btn-box {
width: 100%;
height: 34px;
margin-bottom: 5px;
display: flex;
justify-content: space-between;
align-items: center;
.top-left-btn-box {
display: flex;
align-items: center;
.el-select {
margin-right: 10px;
}
}
2024-11-13 13:17:04 +08:00
.el-button {
border-radius: 16px;
height: 31px;
background-color: transparent;
color: #c77000;
2024-05-30 10:18:39 +08:00
}
2024-11-13 13:17:04 +08:00
& > .el-button {
border-radius: 4px;
background-color: #f2f3f5;
color: $main-color;
}
2024-05-30 10:18:39 +08:00
}
2024-11-13 13:17:04 +08:00
.monitoring-message {
2024-12-30 15:47:56 +08:00
width: 670px;
height: 450px;
2024-11-13 13:17:04 +08:00
padding-bottom: 10px;
display: flex;
justify-content: space-between;
& > .left-box {
position: relative;
width: 200px;
height: 100%;
.unusual-title {
display: block;
width: 100%;
line-height: 38px;
color: #C77000;
font-size: 20px;
text-align: center;
border: 1px dashed #C1C1C1;
border-radius: 10px;
}
.unusual-box {
width: 100%;
height: calc(100% - 45px);
// background: #F8F8F8;
border-left: 1px dashed #C1C1C1;
// border-radius: 4px;
margin-top: 5px;
padding: 8px 16px;
overflow-y: auto;
li {
color: #F80000;
font-size: 14px;
line-height: 22px;
}
}
}
2024-11-13 13:17:04 +08:00
& > .right-box {
width: calc(100% - 205px);
height: 100%;
2024-12-30 15:47:56 +08:00
.video-box {
position: relative;
width: 100%;
height: 270px;
background: $main-color;
/* background: url(@/assets/imgs/video_bck.png);
background-size: 100% 100%; */
&:hover {
.icon-box {
display: flex;
}
}
.icon-box {
display: none;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
font-size: 60px;
color: white;
background: rgba(black, .3);
justify-content: center;
align-items: center;
}
video {
width: 100%;
height: 100%;
object-fit: cover;
}
}
2024-11-13 13:17:04 +08:00
2024-12-30 15:47:56 +08:00
// v2带视频的msg-box
//.message-box {
// width: 100%;
// // height: 270px;
// height: 100%;
// // margin-bottom: 5px;
//
// .message-log {
// width: 100%;
// height: calc(100% - 40px);
// max-height: 109px;
// padding: 16px 20px;
// box-sizing: border-box;
// border: 1px solid #c8c8c8;
// background: #f8f8f8;
// overflow-y: auto;
//
// li {
// width: 100%;
// font-size: 14px;
// line-height: 1.6;
// margin: 5px 0;
//
// &.align-right {
// text-align: right;
//
// span {
// background: $main-color;
// }
// }
//
// span {
// display: inline-block;
// max-width: 80%;
// padding: 6px 8px;
// box-sizing: border-box;
// border-radius: 8px;
// color: white;
// letter-spacing: 1px;
// background: #92b3c1;
// text-align: left;
// }
// }
// }
2024-11-13 13:17:04 +08:00
//
2024-12-30 15:47:56 +08:00
// .send-box {
// width: 100%;
// height: 40px;
// display: flex;
// justify-content: space-between;
// align-items: flex-end;
2024-11-13 13:17:04 +08:00
//
2024-12-30 15:47:56 +08:00
// .el-input {
// width: calc(100% - 110px);
// height: 32px;
2024-11-13 13:17:04 +08:00
//
2024-12-30 15:47:56 +08:00
// :deep(.el-input__wrapper) {
// background-color: #F2F3F5;
// border-color: #C1C1C1;
// }
// }
//
// .el-button {
// padding: 0;
// width: 100px;
// line-height: 30px;
// }
// }
//}
2024-11-13 13:17:04 +08:00
.message-box {
width: 100%;
// height: 270px;
height: 100%;
// margin-bottom: 5px;
.message-log {
width: 100%;
height: 100%;
max-height: calc(100% - 35px);
padding: 16px 20px;
box-sizing: border-box;
border: 1px solid #c8c8c8;
background: #f8f8f8;
overflow-y: auto;
li {
width: 100%;
font-size: 14px;
line-height: 1.6;
margin: 5px 0;
&.align-right {
text-align: right;
span {
background: $main-color;
}
}
span {
display: inline-block;
max-width: 80%;
padding: 6px 8px;
box-sizing: border-box;
border-radius: 8px;
color: white;
letter-spacing: 1px;
background: $main-color;
background: #92b3c1;
text-align: left;
}
}
}
.send-box {
width: 100%;
height: 40px;
display: flex;
justify-content: space-between;
align-items: flex-end;
.el-input {
width: calc(100% - 110px);
height: 32px;
:deep(.el-input__wrapper) {
background-color: #F2F3F5;
border-color: #C1C1C1;
}
}
.el-button {
padding: 0;
width: 100px;
line-height: 30px;
}
}
}
}
}
2024-05-30 10:18:39 +08:00
2024-11-13 13:17:04 +08:00
.table-box {
width: 100%;
2024-12-30 15:47:56 +08:00
height: calc(50% - 264.5px);
2024-11-13 13:17:04 +08:00
margin-bottom: 20px;
flex-grow: 1;
:deep(.el-table__inner-wrapper) {
.el-table__cell {
padding: 6px 0;
}
.cell {
padding: 0 2px;
}
.el-table__header-wrapper {
tr {
background-color: $main-color;
}
th.el-table__cell {
color: white;
background-color: $main-color;
padding: 8px 0;
font-weight: 400;
}
}
}
2024-12-30 15:47:56 +08:00
.table-btn-box {
.el-button {
padding: 0 7px;
height: 24px;
line-height: 22px;
&:not(:first-of-type) {
margin-left: 4px;
}
}
}
2024-05-30 10:18:39 +08:00
}
}
2024-11-13 13:17:04 +08:00
}
}
2024-11-13 13:17:04 +08:00
// 麦克风图标样式
.send-box {
position: relative; /* 设置相对定位用于绝对定位子元素的参考 */
}
2024-05-30 10:18:39 +08:00
2024-11-13 13:17:04 +08:00
.mic-icon {
position: fixed; /* 使用固定定位 */
bottom: 20px; /* 距离底部20px */
left: 50%; /* 水平居中 */
transform: translateX(-50%); /* 使图标真正居中 */
background-color: rgba(255, 255, 255, 0.9); /* 背景颜色 */
border-radius: 5px; /* 圆角 */
padding: 10px; /* 内边距 */
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); /* 阴影效果 */
z-index: 1000; /* 确保图标在其他元素之上 */
display: flex; /* 使用 flexbox 布局 */
align-items: center; /* 垂直居中 */
}
2024-11-13 13:17:04 +08:00
.mic-icon .fa-microphone {
color: red; /* 麦克风图标颜色 */
font-size: 80px; /* 调整图标大小 */
margin-right: 5px; /* 图标与文本间的间距 */
2024-05-30 10:18:39 +08:00
}
2024-12-30 15:47:56 +08:00
2024-05-28 22:09:05 +08:00
</style>