add: 一期代码

This commit is contained in:
republicline 2024-11-13 13:17:04 +08:00
parent d90d5e4eef
commit 55320a8dd3
14 changed files with 9394 additions and 1316 deletions

7876
src/assets/font/all.css Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -7,7 +7,7 @@ import ElementPlus, {ElDialog} from 'element-plus';
import * as ElementPlusIconsVue from '@element-plus/icons-vue' import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import TableAbility from '@/components/table-ability.vue' import TableAbility from '@/components/table-ability.vue'
import './assets/font/all.css'
import 'element-plus/dist/index.css'; import 'element-plus/dist/index.css';
import './assets/css/global.scss'; import './assets/css/global.scss';
import './assets/font/iconfont.css'; import './assets/font/iconfont.css';

View File

@ -4,7 +4,7 @@ import {ElMessage} from "element-plus";
// 本地调试版本: // 本地调试版本:
const vitalUrl = "ws://" + window.location.host + "/socket.io/admin/rax/vitalSignsMedicine?token=" + Session.getToken() const vitalUrl = "ws://" + window.location.host + "/socket.io/admin/rax/vitalSignsMedicine?token=" + Session.getToken()
const medicineUrl = "ws://" + window.location.host + "/socket.io/admin/rax/addMedicine?token=" + Session.getToken() // const medicineUrl = "ws://" + window.location.host + "/socket.io/admin/rax/addMedicine?token=" + Session.getToken()
const chatUrl = "ws://" + window.location.host + "/socket.io/admin/rax/chatRoom?token=" + Session.getToken() const chatUrl = "ws://" + window.location.host + "/socket.io/admin/rax/chatRoom?token=" + Session.getToken()
// 服务器部署版本 // 服务器部署版本
@ -242,7 +242,7 @@ export const useRemoteWsStore = defineStore("remoteWs", {
patientName: name, patientName: name,
idNum: id, idNum: id,
date: date, date: date,
msg, msg: msg,
msgType: "msg" msgType: "msg"
} }
patient.chatWS.send(JSON.stringify(params)) patient.chatWS.send(JSON.stringify(params))
@ -256,6 +256,30 @@ export const useRemoteWsStore = defineStore("remoteWs", {
}) })
} }
}, },
sendAudio(name: string, id: string, date: string, audio: any, index: number, cb: any) {
const patient: any = this.patient[name + id + date + index]
if (patient) {
const params = {
patientName: name,
idNum: id,
date: date,
content: audio,
msgType: "audio"
}
patient.chatWS.send(JSON.stringify(params))
cb({
status: 0
})
} else {
cb({
status: 1,
msg: "已断开连接"
})
}
},
disconnectVital(name: string, id: string, date: string, index: number) { disconnectVital(name: string, id: string, date: string, index: number) {
const patient: any = this.patient[name + id + date + index] const patient: any = this.patient[name + id + date + index]
if (patient && patient.vitalWS) { if (patient && patient.vitalWS) {
@ -289,7 +313,7 @@ export const useRemoteWsStore = defineStore("remoteWs", {
patient.chatWS.onmessage = (e: any) => { patient.chatWS.onmessage = (e: any) => {
if (e && e.data) { if (e && e.data) {
const data = JSON.parse(e.data); const data = JSON.parse(e.data);
if (data.msgType == "msg") { if (data.msgType == "msg" || data.msgType == "audio") {
cb(e) cb(e)
} else { } else {
patient.chatWS.send(JSON.stringify({msgType: "heartbeat"})) patient.chatWS.send(JSON.stringify({msgType: "heartbeat"}))

View File

@ -100,7 +100,10 @@
<el-form-item label="手机号" prop="phone"> <el-form-item label="手机号" prop="phone">
<el-input v-model="registerParams.phone" placeholder="请输入手机号"></el-input> <el-input v-model="registerParams.phone" placeholder="请输入手机号"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="医院" prop="hospital"> <el-form-item label="医院" prop="hospital" class="before-hospital">
<template #label>
<span style="color: #f56c6c;">*&nbsp;</span>医院
</template>
<el-select v-model="registerParams.hospitalId" style="width: 100%;"> <el-select v-model="registerParams.hospitalId" style="width: 100%;">
<el-option v-for="item in hospitals" :key="item.id" :label="item.name" <el-option v-for="item in hospitals" :key="item.id" :label="item.name"
:value="item.id"/> :value="item.id"/>
@ -352,7 +355,7 @@ function getCaptchaCode() {
} }
function refreshImg() { function refreshImg() {
randomStr = v4() randomStr = v4();
captchaImgUrl.value = '/api/admin/code/textImage?randomStr=' + randomStr captchaImgUrl.value = '/api/admin/code/textImage?randomStr=' + randomStr
} }
@ -564,6 +567,7 @@ const loginPost = (data: any) => {
} }
} }
} }
.model { .model {
display: flex; display: flex;
text-align: center; text-align: center;
@ -573,5 +577,7 @@ const loginPost = (data: any) => {
color: #8c9094; color: #8c9094;
font-size: 70%; font-size: 70%;
} }
} }
</style> </style>

View File

@ -186,8 +186,9 @@ function getData() {
remoteWsStore.subscribeVital(remoteItem.value.patient, remoteItem.value.patientId, remoteItem.value.date, currentIndex, (res: any) => { remoteWsStore.subscribeVital(remoteItem.value.patient, remoteItem.value.patientId, remoteItem.value.date, currentIndex, (res: any) => {
if (res && res.data) { if (res && res.data) {
const data = JSON.parse(res.data); const data = JSON.parse(res.data);
if (data.vitalSignsList && data.vitalSignsList.length > 0) { // if (data.vitalSignsList && data.vitalSignsList.length > 0) {
Object.assign(patientInfo.value, data.vitalSignsList[0]); if (data.vitalSignsList) {
Object.assign(patientInfo.value, data.vitalSignsList);
patientInfo.value.state = (patientInfo.value.BIS_except || patientInfo.value.SBP_except || patientInfo.value.state = (patientInfo.value.BIS_except || patientInfo.value.SBP_except ||
patientInfo.value.DBP_except || patientInfo.value.HR_except); patientInfo.value.DBP_except || patientInfo.value.HR_except);
setLog(patientInfo.value) setLog(patientInfo.value)

View File

@ -75,11 +75,13 @@
<!-- <source src="@/assets/medical.mp4" type="video/mp4"/>--> <!-- <source src="@/assets/medical.mp4" type="video/mp4"/>-->
<!-- </video>--> <!-- </video>-->
<!-- </div>--> <!-- </div>-->
<!--聊天框 -->
<div class="message-box"> <div class="message-box">
<ul ref="msgLog" class="message-log"> <ul ref="msgLog" class="message-log">
<li v-for="(item, index) in mssageList" :key="'msg-log-' + index" <li v-for="(item, index) in mssageList" :key="'msg-log-' + index"
:class="{ 'align-right': item.createUser == userInfo.account }"> :class="{ 'align-right': item.createUser == userInfo.account }">
<span>{{ item.content }}</span> <span>{{ item.msg }}</span>
</li> </li>
</ul> </ul>
<div class="send-box"> <div class="send-box">
@ -87,6 +89,30 @@
<el-button color="#006080" @click="sendMsg">发送消息</el-button> <el-button color="#006080" @click="sendMsg">发送消息</el-button>
</div> </div>
</div> </div>
<!-- 聊天框, 添加音频组件 -->
<!--<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>-->
<!-- <source :src="'data:audio/mpeg;base64,' + 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> &lt;!&ndash; 麦克风图标 &ndash;&gt;-->
<!-- 正在录音... {{ remainingTime }} 秒剩余-->
<!-- </div>-->
<!--</div>-->
</div> </div>
</div> </div>
<!-- table1 --> <!-- table1 -->
@ -202,6 +228,7 @@ import imgHeartAlarm from '@/assets/imgs/heart_alarm.png';
import {useRemoteWsStore} from "@/stores/remote-ws-store"; import {useRemoteWsStore} from "@/stores/remote-ws-store";
import {useUserStore} from "@/stores/user-info-store"; import {useUserStore} from "@/stores/user-info-store";
import {getPatientInfo, getPatientInfoM} from "@/api/patient"; import {getPatientInfo, getPatientInfoM} from "@/api/patient";
import axios from "axios";
const router = useRouter() const router = useRouter()
@ -244,8 +271,6 @@ const heartAlarm = ref(false); // 心脏警告
const isAIDose = ref(false); // AI const isAIDose = ref(false); // AI
const isVideoPlay = ref(false); // const isVideoPlay = ref(false); //
const videoSrc = ref('https://www.runoob.com/try/demo_source/mov_bbb.mp4'); const videoSrc = ref('https://www.runoob.com/try/demo_source/mov_bbb.mp4');
const mssageList = ref([] as any);
const msgVal = ref('');
const unusual = ref([] as any); const unusual = ref([] as any);
const fixedTableData = ref([] as any[]); const fixedTableData = ref([] as any[]);
const varTableData = ref([] as any[]); const varTableData = ref([] as any[]);
@ -260,6 +285,7 @@ onMounted(() => {
router.replace('/remote-manage/remote-manage'); router.replace('/remote-manage/remote-manage');
return; return;
} }
// msgLogScrollBottom();
msgLogScrollBottom(); msgLogScrollBottom();
initScale(); initScale();
subscribeWS(); subscribeWS();
@ -270,7 +296,7 @@ onUnmounted(() => {
remoteWsStore.unsubscribeChat(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentIndex); remoteWsStore.unsubscribeChat(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentIndex);
// remoteWsStore.unsubscribeMedicine(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, index); // remoteWsStore.unsubscribeMedicine(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, index);
remoteWsStore.unsubscribeVital(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentIndex); remoteWsStore.unsubscribeVital(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentIndex);
remoteWsStore.disconnectChat(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentIndex) // remoteWsStore.disconnectChat(currentRemote.value.patient, currentRemote.value.patientId, currentRemote.value.date, currentIndex)
if (!router.currentRoute.value.path.startsWith("/remote-manage/")) { if (!router.currentRoute.value.path.startsWith("/remote-manage/")) {
const tasks: any = remoteWsStore.getRemoteTask() const tasks: any = remoteWsStore.getRemoteTask()
tasks.forEach((task: any) => { tasks.forEach((task: any) => {
@ -326,7 +352,8 @@ const subscribeChat = () => {
function (res: any) { function (res: any) {
const chatObj = JSON.parse(res.data); const chatObj = JSON.parse(res.data);
if (chatObj.history) { if (chatObj.history) {
mssageList.value = chatObj.history; // mssageList.value = chatObj.history;
mssageList.value = chatObj;
} else { } else {
mssageList.value.push(JSON.parse(res.data)); mssageList.value.push(JSON.parse(res.data));
} }
@ -528,6 +555,8 @@ const playPause = () => {
isVideoPlay.value = false; isVideoPlay.value = false;
} }
} }
const sendMsg = () => { const sendMsg = () => {
if (msgVal.value.trim() == '') return; if (msgVal.value.trim() == '') return;
const index = remoteWsStore.getCurrentTaskIndex() const index = remoteWsStore.getCurrentTaskIndex()
@ -595,6 +624,123 @@ function startAI() {
}); });
} }
/*
* 聊天室
*/
const msgVal = ref('');
const mssageList = ref([] as any);
const isRecording = ref(false); //
const mediaRecorder = ref(null);
const audioChunks = ref([]);
const remainingTime = ref(10); // 10
//
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; //
clearInterval(timer); //
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,
index,
function (res) {
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("录音时间到,已自动停止!");
}
}, 1000); //
};
/**
* Blob 对象转换为 Base64 字符串
* @param {Blob} blob - 需要转换的 Blob 对象
* @returns {Promise<string>} - 返回一个 Promise解析为 Base64 字符串
*/
const convertBlobToBase64 = (blob) => {
return new Promise((resolve, reject) => {
const reader = new FileReader(); // FileReader
// onloadend
reader.onloadend = () => {
// result URL
const base64String = reader.result.split(',')[1]; // Base64
resolve(base64String); // Base64
};
//
reader.onerror = (error) => {
reject(error); //
};
// Blob URL
reader.readAsDataURL(blob);
});
};
//
const stopRecording = () => {
if (mediaRecorder.value) {
mediaRecorder.value.stop();
isRecording.value = false; //
}
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -1038,4 +1184,29 @@ function startAI() {
} }
} }
} }
//
.send-box {
position: relative; /* 设置相对定位用于绝对定位子元素的参考 */
}
.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; /* 垂直居中 */
}
.mic-icon .fa-microphone {
color: red; /* 麦克风图标颜色 */
font-size: 80px; /* 调整图标大小 */
margin-right: 5px; /* 图标与文本间的间距 */
}
</style> </style>