548 lines
12 KiB
Vue
548 lines
12 KiB
Vue
<template>
|
|
<div class="alarm-modal" v-if="visible">
|
|
<div class="modal-content">
|
|
<!-- 标题 -->
|
|
<div class="modal-header">
|
|
<span class="title">{{ alarmData.title || "告警详情" }}</span>
|
|
<img
|
|
src="../../assets/img/close.png"
|
|
class="close-icon"
|
|
@click="handleClose"
|
|
/>
|
|
</div>
|
|
|
|
<!-- 左侧图片区域 -->
|
|
<div class="modal-body">
|
|
<div class="image-section">
|
|
<TitleBlock title="监控视图">监控视图</TitleBlock>
|
|
<div class="images-container">
|
|
<div class="main-camera">
|
|
<div class="camera-wrapper">
|
|
<div class="camera-title">主监控</div>
|
|
<div class="camera-view">
|
|
<template v-if="alarmData.mainImage">
|
|
<img
|
|
:src="alarmData.mainImage"
|
|
alt=""
|
|
@error="imgLoadError.main = true"
|
|
v-show="!imgLoadError.main"
|
|
/>
|
|
<EmptyState
|
|
v-if="imgLoadError.main"
|
|
subtitle="暂无信息"
|
|
title=""
|
|
iconSrc="empty"
|
|
class="video-empty-state"
|
|
/>
|
|
</template>
|
|
<EmptyState
|
|
v-else
|
|
subtitle="暂无信息"
|
|
title=""
|
|
:iconSrc="empty"
|
|
class="video-empty-state"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sub-cameras">
|
|
<div class="camera-item">
|
|
<div class="camera-wrapper">
|
|
<div class="camera-title">云台监控</div>
|
|
<div class="camera-view">
|
|
<template
|
|
v-if="
|
|
alarmData.subImages && alarmData.subImages.length > 0
|
|
"
|
|
>
|
|
<img
|
|
:src="alarmData.subImages[0]"
|
|
alt=""
|
|
@error="imgLoadError.sub1 = true"
|
|
v-show="!imgLoadError.sub1"
|
|
/>
|
|
<EmptyState
|
|
v-if="imgLoadError.sub1"
|
|
subtitle="暂无信息"
|
|
title=""
|
|
:iconSrc="empty"
|
|
class="video-empty-state"
|
|
/>
|
|
</template>
|
|
<EmptyState
|
|
v-else
|
|
subtitle="暂无信息"
|
|
title=""
|
|
:iconSrc="empty"
|
|
class="video-empty-state"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="camera-item">
|
|
<div class="camera-wrapper">
|
|
<div class="camera-title">热成像</div>
|
|
<div class="camera-view">
|
|
<template
|
|
v-if="
|
|
alarmData.subImages && alarmData.subImages.length > 1
|
|
"
|
|
>
|
|
<img
|
|
:src="alarmData.subImages[1]"
|
|
alt=""
|
|
@error="imgLoadError.sub2 = true"
|
|
v-show="!imgLoadError.sub2"
|
|
/>
|
|
<EmptyState
|
|
v-if="imgLoadError.sub2"
|
|
title=""
|
|
subtitle="暂无信息"
|
|
:iconSrc="empty"
|
|
class="video-empty-state"
|
|
/>
|
|
</template>
|
|
<EmptyState
|
|
v-else
|
|
title=""
|
|
subtitle="暂无信息"
|
|
:iconSrc="empty"
|
|
class="video-empty-state"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 右侧信息区域 -->
|
|
<div class="info-section">
|
|
<div class="info-group">
|
|
<TitleBlock title="事件信息">事件信息</TitleBlock>
|
|
<div class="info-content">
|
|
<div class="info-item">
|
|
<p class="label">事件类型:</p>
|
|
<p class="value">{{ alarmData.type }}</p>
|
|
</div>
|
|
<div class="info-item">
|
|
<p class="label">上报时间:</p>
|
|
<p class="value">{{ alarmData.time }}</p>
|
|
</div>
|
|
<div class="info-item">
|
|
<p class="label">机器人名称:</p>
|
|
<p class="value">{{ alarmData.robotName }}</p>
|
|
</div>
|
|
<div class="info-item">
|
|
<p class="label">状态:</p>
|
|
<p class="value" :class="alarmData.status">
|
|
{{ alarmData.status }}
|
|
</p>
|
|
</div>
|
|
<div class="info-item" v-if="alarmData.temperature">
|
|
<p class="label">温度:</p>
|
|
<p class="value">{{ alarmData.temperature }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="info-group">
|
|
<TitleBlock title="备注">备注</TitleBlock>
|
|
<div class="remark-content">
|
|
<div
|
|
class="remark-box"
|
|
contenteditable="true"
|
|
data-placeholder="请输入备注信息......"
|
|
@input="updateRemark"
|
|
v-html="alarmData.remark || ''"
|
|
></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 底部按钮 -->
|
|
<div class="modal-footer">
|
|
<button class="btn cancel" @click="handleClose">取消</button>
|
|
<button class="btn confirm" @click="handleConfirm" v-if="alarmData.status !== '已处理'">确认处理</button>
|
|
<button class="btn report" @click="handleReport" v-if="alarmData.status !== '已处理'">处理并上报</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted, watch } from "vue";
|
|
import TitleBlock from "../common/TitleBlock.vue";
|
|
import EmptyState from "../common/EmptyState.vue";
|
|
import empty from "../../assets/img/empty.png";
|
|
|
|
const props = defineProps({
|
|
visible: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
alarmData: {
|
|
type: Object,
|
|
default: () => ({
|
|
title: "",
|
|
mainImage: "",
|
|
subImages: [],
|
|
type: "",
|
|
time: "",
|
|
robotName: "",
|
|
status: "",
|
|
messageId: "",
|
|
remark: "",
|
|
}),
|
|
},
|
|
});
|
|
|
|
// 追踪图片加载错误状态
|
|
const imgLoadError = ref({
|
|
main: false,
|
|
sub1: false,
|
|
sub2: false,
|
|
});
|
|
|
|
// 重置图片加载错误状态
|
|
const resetImgLoadError = () => {
|
|
imgLoadError.value = {
|
|
main: false,
|
|
sub1: false,
|
|
sub2: false,
|
|
};
|
|
};
|
|
|
|
// 监听模态框可见性变化
|
|
watch(
|
|
() => props.visible,
|
|
(newValue) => {
|
|
if (newValue) {
|
|
// 当模态框显示时重置图片加载错误状态
|
|
resetImgLoadError();
|
|
}
|
|
}
|
|
);
|
|
|
|
// 当模态框显示时重置图片加载错误状态
|
|
onMounted(() => {
|
|
if (props.visible) {
|
|
resetImgLoadError();
|
|
}
|
|
});
|
|
|
|
const emit = defineEmits(["update:visible", "confirm", "report"]);
|
|
|
|
const remarkText = ref("");
|
|
|
|
// 监听alarmData中的remark变化
|
|
watch(
|
|
() => props.alarmData.remark,
|
|
(newValue) => {
|
|
remarkText.value = newValue || "";
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
|
|
const handleClose = () => {
|
|
emit("update:visible", false);
|
|
};
|
|
|
|
const handleConfirm = () => {
|
|
emit("confirm", {
|
|
...props.alarmData,
|
|
remark: remarkText.value,
|
|
});
|
|
};
|
|
|
|
const handleReport = () => {
|
|
emit("report", {
|
|
...props.alarmData,
|
|
remark: remarkText.value,
|
|
});
|
|
};
|
|
|
|
const updateRemark = (e) => {
|
|
remarkText.value = e.target.innerText;
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.alarm-modal {
|
|
position: fixed;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 100vw;
|
|
height: 100vh;
|
|
overflow: auto;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 1000;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
}
|
|
|
|
.modal-content {
|
|
width: 900px;
|
|
height: 600px;
|
|
background: url("../../assets/img/alert.png") no-repeat;
|
|
background-size: 100% 100%;
|
|
border-radius: 4px;
|
|
padding: 20px;
|
|
}
|
|
|
|
.modal-header {
|
|
display: flex;
|
|
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.title {
|
|
color: #b9e8ff;
|
|
font-size: 28px;
|
|
letter-spacing: 4px;
|
|
padding-left: 20px;
|
|
}
|
|
|
|
.close-icon {
|
|
width: 20px;
|
|
height: 20px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.modal-body {
|
|
height: 80%;
|
|
display: flex;
|
|
gap: 20px;
|
|
/* padding-top: 20px; */
|
|
}
|
|
|
|
.image-section {
|
|
height: 100%;
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
/* margin-top: 20px; */
|
|
gap: 10px;
|
|
border-radius: 4px;
|
|
border: 1px solid rgba(0, 206, 234, 0.7);
|
|
}
|
|
|
|
.images-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
padding: 10px;
|
|
}
|
|
|
|
.main-camera {
|
|
width: 100%;
|
|
}
|
|
|
|
.camera-wrapper {
|
|
position: relative;
|
|
min-height: 240px;
|
|
/* border: 1px solid rgba(0,206,234,0.7); */
|
|
border-radius: 4px;
|
|
background: #033347; /* 设置底部矩形的背景色 */
|
|
}
|
|
|
|
.sub-cameras {
|
|
display: flex;
|
|
gap: 10px;
|
|
}
|
|
|
|
.camera-item {
|
|
flex: 1;
|
|
}
|
|
|
|
.camera-item .camera-wrapper {
|
|
min-height: 120px;
|
|
}
|
|
|
|
.camera-title {
|
|
width: 100%;
|
|
position: absolute;
|
|
top: 0px;
|
|
left: 0px;
|
|
z-index: 1;
|
|
color: #b9e8ff;
|
|
font-size: 12px;
|
|
padding: 4px 8px;
|
|
background: rgba(0, 21, 31, 0.5);
|
|
border-radius: 2px;
|
|
text-align: left;
|
|
}
|
|
|
|
.camera-view {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.camera-view img {
|
|
width: 100%;
|
|
height: 100%;
|
|
/* object-fit: cover; */
|
|
}
|
|
|
|
.info-section {
|
|
width: 400px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
gap: 20px;
|
|
}
|
|
|
|
.info-group {
|
|
background: rgba(0, 21, 31, 0.5);
|
|
border-radius: 4px;
|
|
|
|
border: 1px solid rgba(0, 206, 234, 0.7);
|
|
}
|
|
|
|
.info-group h3 {
|
|
color: #b9e8ff;
|
|
font-size: 14px;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.info-content {
|
|
padding: 15px;
|
|
background: rgba(0, 21, 31, 0.2);
|
|
/* border: 1px solid rgba(0,206,234,0.7); */
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.info-item {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
/* background: rgba(0, 21, 31, 0.2); */
|
|
border-bottom: 1px solid #244c60;
|
|
padding: 10px;
|
|
/* padding: 15px 20px; */
|
|
}
|
|
|
|
.info-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.info-item .label {
|
|
color: #b9e8ff;
|
|
font-size: 14px;
|
|
margin: 0;
|
|
}
|
|
|
|
.info-item .label:before {
|
|
content: "";
|
|
display: inline-block;
|
|
width: 10px;
|
|
height: 10px;
|
|
background: url("../../assets/img/bt.png") no-repeat;
|
|
background-size: 100% 100%;
|
|
margin-right: 5px;
|
|
}
|
|
|
|
.info-item .value {
|
|
color: #b9e8ff;
|
|
font-size: 14px;
|
|
margin: 0;
|
|
text-align: right;
|
|
}
|
|
|
|
.info-item .value.未处理 {
|
|
color: #f33f3f;
|
|
}
|
|
|
|
.info-item .value.已处理 {
|
|
color: #00ff84;
|
|
}
|
|
|
|
.remark-content {
|
|
/* background: rgba(0, 21, 31, 0.2); */
|
|
/* border: 1px solid rgba(0,206,234,0.7); */
|
|
border-radius: 4px;
|
|
padding: 0;
|
|
}
|
|
|
|
.remark-box {
|
|
width: 100%;
|
|
min-height: 130px;
|
|
background: none;
|
|
border-radius: 4px;
|
|
color: #b9e8ff;
|
|
padding: 10px;
|
|
outline: none;
|
|
}
|
|
|
|
.remark-box:empty:before {
|
|
content: attr(data-placeholder);
|
|
color: rgba(185, 232, 255, 0.4);
|
|
}
|
|
|
|
.modal-footer {
|
|
margin-top: 20px;
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 10px;
|
|
}
|
|
|
|
.btn {
|
|
padding: 0 20px;
|
|
height: 32px;
|
|
border-radius: 4px;
|
|
border: none;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.btn.cancel {
|
|
background: url("../../assets/img/cancel.png") no-repeat;
|
|
background-size: 100% 100%;
|
|
color: #c6f4ff;
|
|
}
|
|
|
|
.btn.confirm {
|
|
background: url("../../assets/img/confirm.png") no-repeat;
|
|
background-size: 100% 100%;
|
|
color: #c6f4ff;
|
|
}
|
|
|
|
.btn.report {
|
|
background: url("../../assets/img/report.png") no-repeat;
|
|
background-size: 100% 100%;
|
|
color: #c6f4ff;
|
|
}
|
|
.video-empty-state {
|
|
padding-top: 30px;
|
|
background: #033347;
|
|
}
|
|
|
|
:deep(.empty-icon) {
|
|
/* width: 100px; */
|
|
height: 46px !important;
|
|
}
|
|
|
|
:deep(.empty-title) {
|
|
display: none;
|
|
}
|
|
|
|
:deep(.empty-subtitle) {
|
|
color: #fff;
|
|
font-size: 10px;
|
|
letter-spacing: 1px;
|
|
margin-top: 5px;
|
|
}
|
|
</style>
|