2025年04月22日18:31:44

This commit is contained in:
luojiayi 2025-04-22 18:31:46 +08:00
parent 55ff343e35
commit e472857a5f
10 changed files with 119 additions and 107 deletions

View File

@ -1,5 +1,5 @@
# VITE_APP_URL = 'http://192.168.3.116:8001/' VITE_APP_URL = 'http://192.168.3.116:8001/'
# VITE_APP_URL_WEBSOCKET = 'http://192.168.3.116:8000/api/websocket' # VITE_APP_URL_WEBSOCKET = 'http://192.168.3.116:8000/api/websocket'
VITE_APP_URL = 'http://api.handcuff.youaikang.cn' # VITE_APP_URL = 'http://api.handcuff.youaikang.cn'
VITE_APP_URL_WEBSOCKET = 'ws://device.handcuff.youaikang.cn:8000/api/websocket' VITE_APP_URL_WEBSOCKET = 'ws://device.handcuff.youaikang.cn:8000/api/websocket'

View File

@ -1,26 +1,31 @@
<template> <template>
<teleport to="body"> <teleport to="body">
<transition name="emergency-modal"> <transition name="emergency-modal">
<div class="modal-mask" @click.self="$emit('close')"> <div class="modal-mask" @click="visible = false" v-if="visible">
<div class="emergency-modal" :class="{ 'emergency-pulse': show }"> <div class="emergency-modal">
<div class="glow"></div> <div class="glow"></div>
<div class="modal-header"> <div class="modal-header">
<div class="warning-icon"></div> <div class="warning-icon"></div>
<h2>{{ title }}</h2> <h2>紧急警报</h2>
</div>
<div class="alarmTable">
<div class="alarmTable-item">
<div class="alarmTable-item-text">设备号</div>
<div class="alarmTable-item-text">报警类型</div>
<div class="alarmTable-item-text">佩戴者</div>
<div class="alarmTable-item-text">触发时间</div>
<div class="alarmTable-item-text">操作</div>
</div>
<div class="alarmTable-content scrollbar">
<div class="alarmTable-item" v-for="(item, index) in warningList" :key="index">
<div class="alarmTable-item-text">{{ item.deviceId }}</div>
<div class="alarmTable-item-text">{{ warnTypeEnum[item.type] }}</div>
<div class="alarmTable-item-text">{{ item.useName }}</div>
<div class="alarmTable-item-text">{{ item.createTime }}</div>
<div class="alarmTable-item-text" @click="toIncidentDispose(item.deviceId)">去处理</div>
</div>
</div> </div>
<div class="modal-content">
<p>{{ content }}</p>
</div> </div>
<button class="emergency-button" @click="handleAudioEnd">
<span class="button-text">立即处理</span>
<div class="button-glow"></div>
</button>
<audio ref="audioPlayer" hidden>
<source src="../assets/audio/alarm.mp3" type="audio/mpeg" />
</audio>
</div> </div>
</div> </div>
</transition> </transition>
@ -28,42 +33,41 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref } from "vue"; import { ref } from "vue";
const audioPlayer = ref(null); import { useRouter } from "vue-router";
defineProps({ interface TWarning {
show: Boolean, deviceId: string;
title: { useName: string;
type: String, createTime: string;
default: "紧急警报!", type: number;
}, }
content: {
type: String,
default: "检测到危险操作,请立即处理!",
},
});
const emit = defineEmits(["close", "confirm"]); enum warnTypeEnum {
"SOS告警",
"围栏告警",
"破坏告警",
"低电告警",
"心率告警",
"血氧告警",
"体温告警",
}
const router = useRouter();
const visible = ref(false);
const warningList = ref<TWarning[]>([]);
defineExpose({ visible, warningList });
const handleAudioEnd = () => { const toIncidentDispose = (id: string) => {
emit("close"); router.push({
// if (audioPlayer.value) { path: "/incidentDispose",
// audioPlayer.value.play().catch((error) => { query: { id },
// console.error(":", error);
// });
// }
};
onMounted(() => {
if (audioPlayer.value) {
audioPlayer.value.play().catch((error) => {
console.error("自动播放被阻止:", error);
}); });
} warningList.value = [];
}); visible.value = false;
};
</script> </script>
<style scoped> <style scoped lang="less">
.modal-mask { .modal-mask {
position: fixed; position: fixed;
z-index: 9998; z-index: 9998;
@ -77,10 +81,37 @@ onMounted(() => {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.alarmTable {
width: 100%;
max-height: 475px;
cursor: pointer;
z-index: 1;
display: flex;
flex-direction: column;
.alarmTable-item {
width: 100%;
padding: 10px 0;
border-bottom: 1px solid #cf8585;
display: flex;
.alarmTable-item-text {
flex: 1;
color: #ff4444;
flex-shrink: 0;
text-align: center;
&:nth-child(2) {
width: 50px;
}
}
}
.alarmTable-content {
flex: 1;
overflow: auto;
}
}
.emergency-modal { .emergency-modal {
position: relative; position: relative;
width: 300px; width: 800px;
padding: 2rem; padding: 2rem;
background: #2a0a0a; background: #2a0a0a;
border-radius: 12px; border-radius: 12px;
@ -158,19 +189,6 @@ h2 {
transform: scale(0.95); transform: scale(0.95);
} }
/* 动画效果 */
@keyframes emergency-pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.02);
}
100% {
transform: scale(1);
}
}
@keyframes shake { @keyframes shake {
0% { 0% {
transform: translateX(0); transform: translateX(0);

View File

@ -13,53 +13,44 @@
</router-view> </router-view>
</div> </div>
</div> </div>
<Alarm ref="alarmRef" v-if="visible" @close="visible = false" /> <Alarm ref="alarmRef" />
<audio ref="audioPlayer" hidden>
<source src="../assets/audio/alarm.mp3" type="audio/mpeg" />
</audio>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useSidebarStore } from "@/store/sidebar"; import { useSidebarStore } from "@/store/sidebar";
import vHeader from "@/components/header.vue"; import vHeader from "@/components/header.vue";
import vSidebar from "@/components/sidebar.vue"; import vSidebar from "@/components/sidebar.vue";
import Alarm from "@/components/alarm.vue";
import { inject, onBeforeUnmount, onMounted, ref } from "vue"; import { inject, onBeforeUnmount, onMounted, ref } from "vue";
import { ElMessageBox } from "element-plus"; import { format } from "@/utils";
import Alarm from "@/components/alarm.vue";
const ws: any = inject("ws"); const ws: any = inject("ws");
// import useWebSocket from "@/utils/webSocket";
// const { onMessage } = useWebSocket();
// onMessage((res) => {
// console.log(res, "WebSocket ");
// if (res.cmd == "warning") {
// console.log("");
// visible.value = true;
// }
// });
const sidebar = useSidebarStore(); const sidebar = useSidebarStore();
const alarmRef = ref(null);
const audioPlayer = ref(null); const audioPlayer = ref(null);
const visible = ref(false); const alarmRef = ref();
const getInclude = (routeList: any) => {
if (!routeList) return [];
let list = [];
routeList.forEach((item) => {
if (item.meta && item.meta.keepAlive) {
list.push(item.name);
}
if (item.children && item.children.length) {
list = [...list, ...getInclude(item.children)];
}
});
return list;
};
const handleBeforeUnload = (event: any) => { const handleBeforeUnload = (event: any) => {
localStorage.removeItem("isReload"); localStorage.removeItem("isReload");
}; };
const onMessage = (res) => {
console.log(res, "WebSocket接收服务器消息");
if (res.cmd == "warning") {
alarmRef.value.visible = true;
alarmRef.value.warningList.unshift({ ...res, createTime: format(new Date(), "HH:mm:ss") });
if (audioPlayer.value) {
audioPlayer.value.play().catch((error) => {
console.error("自动播放被阻止:", error);
});
}
}
};
onMounted(() => { onMounted(() => {
const isReload = localStorage.getItem("isReload"); // const isReload = localStorage.getItem("isReload");
// if (isReload !== "true") { // if (isReload !== "true") {
// ElMessageBox.alert("", "", { // ElMessageBox.alert("", "", {
// confirmButtonText: "OK", // confirmButtonText: "OK",
@ -72,17 +63,13 @@ onMounted(() => {
if (ws.socket == null) { if (ws.socket == null) {
ws.connect(); ws.connect();
} }
}); ws.onMessage(onMessage);
onMounted(() => {
window.addEventListener("beforeunload", handleBeforeUnload); window.addEventListener("beforeunload", handleBeforeUnload);
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
window.removeEventListener("beforeunload", handleBeforeUnload); window.removeEventListener("beforeunload", handleBeforeUnload);
}); });
// const include = getInclude(routes[0].children);
</script> </script>
<style scoped lang="less"> <style scoped lang="less">

View File

@ -4,7 +4,7 @@ export class MapCustom {
this.map = new AMap.Map(data.dom, { this.map = new AMap.Map(data.dom, {
version: "1.4.15", version: "1.4.15",
zoom: 13, zoom: 13,
center: data.center || [116.397428, 39.90923], //初始化地图中心点 center: [116.397428, 39.90923], //初始化地图中心点
...data, ...data,
}); });
} }

View File

@ -44,7 +44,7 @@ export default class WebSocketService {
this.sendMessage({ this.sendMessage({
username: "admin", username: "admin",
password: "111111", password: "111111",
type: 0, type: 'web',
cmd: "webLogin", cmd: "webLogin",
}) })
} }

View File

@ -96,7 +96,6 @@ const tableData = ref<TWarnRecord.IListRes[]>([]);
const getData = async () => { const getData = async () => {
try { try {
console.log(query, "queryqueryquery");
let p = { ...paging, ...query, startDate: query.time?.[0], endDate: query.time?.[1] }; let p = { ...paging, ...query, startDate: query.time?.[0], endDate: query.time?.[1] };
delete p.time; delete p.time;
const res = await warnRecord(p); const res = await warnRecord(p);

View File

@ -35,15 +35,15 @@
<div class="info-box-contetn"> <div class="info-box-contetn">
<div class="item"> <div class="item">
<div class="label">&nbsp;&nbsp;当前指标</div> <div class="label">&nbsp;&nbsp;当前指标</div>
<div class="value">{{ curData.maxValue }}{{ unitEnum[curData.warnType] }}</div> <div class="value">{{ curData.value }}{{ unitEnum[curData.warnType] }}</div>
</div> </div>
<div class="item" v-if="curData.warnType == 5"> <div class="item" v-if="curData.warnType != 5">
<div class="label">&nbsp;&nbsp;最大值</div> <div class="label">&nbsp;&nbsp;最大值</div>
<div class="value">{{ curData.maxValue }}{{ unitEnum[curData.warnType] }}</div> <div class="value">{{ curData.maxValue }}{{ unitEnum[curData.warnType] }}</div>
</div> </div>
<div class="item"> <div class="item">
<div class="label">&nbsp;&nbsp;最小值</div> <div class="label">&nbsp;&nbsp;最小值</div>
<div class="value">{{ curData.maxValue }}{{ unitEnum[curData.warnType] }}</div> <div class="value">{{ curData.minValue }}{{ unitEnum[curData.warnType] }}</div>
</div> </div>
</div> </div>
</div> </div>
@ -376,6 +376,9 @@ onUnmounted(() => {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
margin-top: 20px; margin-top: 20px;
&:nth-child(1) {
color: red;
}
} }
} }
} }

View File

@ -62,7 +62,7 @@ let Interval = null;
let funcList = ref([ let funcList = ref([
{ title: "当前心率", en: "DANGQIANXINLV", icon: heart, unit: "次/分", num: 0, color: "#FF0303" }, { title: "当前心率", en: "DANGQIANXINLV", icon: heart, unit: "次/分", num: 0, color: "#FF0303" },
{ title: "当前血氧", en: "DANGQIANXUEYANG", icon: blood, unit: "%", num: 0, color: "#8B51FD" }, { title: "当前血氧", en: "DANGQIANXUEYANG", icon: blood, unit: "%", num: 0, color: "#8B51FD" },
{ title: "当前体表温度", en: "DANGQIANTIBIAOWENDU", icon: temperature, unit: "次/分", num: 0, color: "#FF6905" }, { title: "当前体表温度", en: "DANGQIANTIBIAOWENDU", icon: temperature, unit: "", num: 0, color: "#FF6905" },
]); ]);
const options = { const options = {
@ -108,6 +108,7 @@ const devicePaging = reactive({
size: 10, size: 10,
mode: undefined, mode: undefined,
useStatus: 1, useStatus: 1,
status: 1,
}); });
const HealthData = ref<THealthLatestData.TRes>(); const HealthData = ref<THealthLatestData.TRes>();

View File

@ -8,7 +8,7 @@
<div class="en">{{ item.en }}</div> <div class="en">{{ item.en }}</div>
</div> </div>
<div class="item-left-bottom"> <div class="item-left-bottom">
<div class="num" :style="{ color: item.color }">{{ item.num || "--" }}</div> <div class="num" :style="{ color: item.color }">{{ item.num || item.num == 0 ? item.num : "--" }}</div>
<div class="unit" :style="{ color: item.color }">{{ item.unit }}</div> <div class="unit" :style="{ color: item.color }">{{ item.unit }}</div>
</div> </div>
</div> </div>

View File

@ -133,8 +133,12 @@ const rules = reactive<FormRules<typeof ruleForm>>({
onMounted(() => { onMounted(() => {
nextTick(() => { nextTick(() => {
let center = [116.397428, 39.90923];
if (rowData.rails && rowData.rails.length) {
center = [rowData.rails[0].lng, rowData.rails[0].lat];
}
try { try {
mapInfo = new MapCustom({ dom: "mapcontainer" }); mapInfo = new MapCustom({ dom: "mapcontainer", center, zoom: 16 });
} catch (error) {} } catch (error) {}
mapInfo.polyEditor(ruleForm.rails, (res) => { mapInfo.polyEditor(ruleForm.rails, (res) => {
ruleForm.rails = res; ruleForm.rails = res;
@ -144,7 +148,7 @@ onMounted(() => {
const clearMap = () => { const clearMap = () => {
mapInfo.clearMap(); mapInfo.clearMap();
ruleForm.rails = ""; ruleForm.rails = [];
}; };
const draw = () => { const draw = () => {
mapInfo.draw().then((res) => { mapInfo.draw().then((res) => {