Files
tunnel-cloud-web/src/views/tunnel/polygon-demo.vue
dj f2b590ee0b refactor(tunnel): 重构隧道页面
- 移除了图像热点区域和相关逻辑
-调整了页面布局和样式- 优化了部分组件的使用方式
2025-09-13 11:35:14 +08:00

1312 lines
35 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div id="main">
<div class="img-box" id="imghot">
<!-- @/assets/images/tunnel/img.png-->
<!-- <img :src="'data:image/png;base64,'+siteImage" style="width:3500px;height:1789px" id="imgModel" usemap="#image"-->
<!-- alt="" @click="clickHandler">-->
<div style="display: flex;justify-content: center;align-items: center;position: relative;" >
<img src="/images/img.png" alt="" class="imgModel" id="imgModel" usemap="#image" @click="handleImageClick"/>
<!-- 测量模式控制按钮 -->
<!-- <div v-if="isMeasuring" style="position: absolute; top: 20px; right: 20px; z-index: 1000;">-->
<!-- <button @click.stop="toggleMeasurementMode" -->
<!-- style="padding: 10px 20px; background-color: #409eff; color: white; border: none; border-radius: 4px; cursor: pointer; margin-right: 10px;">-->
<!-- 结束测量-->
<!-- </button>-->
<!-- <button @click.stop="clearMeasurementPoints" -->
<!-- style="padding: 10px 20px; background-color: #f56c6c; color: white; border: none; border-radius: 4px; cursor: pointer;">-->
<!-- 清除点位-->
<!-- </button>-->
<!-- </div>-->
<!-- <div v-else style="position: absolute; top: 20px; right: 20px; z-index: 1000;">-->
<!-- <button @click.stop="toggleMeasurementMode" -->
<!-- style="padding: 10px 20px; background-color: #67c23a; color: white; border: none; border-radius: 4px; cursor: pointer;">-->
<!-- 开始测量-->
<!-- </button>-->
<!-- </div>-->
</div>
<map name="image" id="image">
<area shape="poly" v-for="(item,index) in coordsList" :coords="item.coords" :key="index" alt=""
:title="item.title" style="cursor: pointer;" @click="clickHot(item.tunnelId,item.clickIndex)">
<!-- :href="'/' + item.tunnelId + '/' + siteId"-->
</map>
</div>
<div class="box-top">
<manage-btn
v-model="selectIndex"
@select="manageSelect"
:list="routeList"
v-if="showMenu"
/>
<tunnel-title v-if="showTunnelTitle" />
<div class="top-length">
<span>隧道总长度: {{ tunnelLength }}</span>
<!-- <span>当前施工长度: {{ constructionLength }}</span>-->
</div>
<div class="top-right">
<div class="current-site">
当前站点<span>{{ currentSite }}</span>
<el-dropdown
trigger="click"
@command="handleChangeSite"
popper-class="dropdown-style"
>
<div class="toggle"></div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="item in siteList"
:key="item.value"
:command="item"
>{{ item.label }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-icon
size="50"
color="#0BE9FA"
style="margin-left: 50px; cursor: pointer"
@click="
isVisited = true;
getAlarmList();
"
>
<Bell />
</el-icon>
</div>
<div class="current-user">
你好<span>{{ currentUser }}</span>
<span>今天是{{ currentDate }}</span>
<div class="logout" @click="handleLogout"></div>
</div>
</div>
</div>
<!-- <tunnel-scene id="tunnel-box" :isedit="false" /> -->
<!-- 一进去的话应该是预览模式所以引入这个组件1 -->
<!-- <preview-scene-->
<!-- id="tunnel-box"-->
<!-- :isedit="false"-->
<!-- :tunnelId="tunnelId"-->
<!-- :key="tunnelId"-->
<!-- :tunnelLen="tunnelLen"-->
<!-- :largeScreen="largeScreen"-->
<!-- :fanList="socketData.leftData"-->
<!-- :devRealtimeData="socketData"-->
<!-- ></preview-scene>-->
<div class="left">
<el-drawer
v-model="drawerLeft"
direction="ltr"
modal-class="modal-box"
:modal="false"
:show-close="false"
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<fan-info
v-if="showFan"
:list="socketData.leftData"
:fan-data="largeScreenData"
:transducer-data="largeScreenData"
:loading="showFanLoading"
:tunnel-id="tunnelId"
/>
<used-ele
v-if="showFan"
:list="socketData.leftData"
:loading="showUsedLoading"
:ele-data="largeScreenData"
/>
</el-drawer>
<div v-if="drawerLeft" class="left-arrow" @click="closeLeft"></div>
<div v-else class="shrink-left" @click="closeLeft"></div>
</div>
<div class="right">
<el-drawer
v-model="drawerRight"
direction="rtl"
modal-class="modal-box"
:modal="false"
:show-close="false"
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<wind-pressure-list
v-if="showFan"
:list="socketData.windPressure"
:win-data="largeScreenData"
:loading="showWindLoading"
/>
<air-info
v-if="showFan"
:list="socketData.sensor"
:windSpeed="socketData.windSpeed"
:air-data="largeScreenData"
/>
<bad-gas-info
v-if="showFan"
:list="socketData.sensor"
:bad-gas-data="largeScreenData"
:tunnelId="tunnelId"
:loading="showBadLoading"
/>
</el-drawer>
<div v-if="drawerRight" class="right-arrow" @click="closeRight"></div>
<div v-else class="shrink-right" @click="closeRight"></div>
</div>
<div class="switch-btn">
<div class="arrow" @click="previousBtn"></div>
<el-carousel height="150px" type="card" ref="tunnelBtn" :autoplay="false" :initial-index="initialIndex"
@change="changeTunnel">
<div class="btn">
<el-carousel-item v-for="item in tunnelList" :key="item.value">
{{ item.label }}
</el-carousel-item>
</div>
</el-carousel>
<div class="arrow right" @click="nextBtn"></div>
</div>
</div>
<div class="alarm-dialog alarm-tunnel">
<el-dialog
:close-on-click-modal="false"
v-model="isDetailVisited"
title="报警信息详情"
width="1500px"
>
<div class="detail">
<div>报警时间</div>
{{ alarmDetail.alarmTime }}
</div>
<div class="detail">
<div>报警内容</div>
{{ alarmDetail.alarmContent }}
</div>
</el-dialog>
</div>
<div class="alarm-tunnel">
<el-dialog
:close-on-click-modal="false"
v-model="isVisited"
title="报警信息"
width="2175px"
:modal="false"
>
<div class="left-top-icon"></div>
<div class="right-top-icon"></div>
<el-form
:model="queryParams"
inline
class="query-form"
ref="queryForm"
@submit.prevent="getAlarmList"
v-if="roleKey !== 'administrator'"
>
<el-form-item label="查阅状态" prop="lookupStatus">
<el-select
v-model="queryParams.lookupStatus"
placeholder="请选择查阅状态"
:fit-input-width="true"
:teleported="false"
clearable
filterable
>
<el-option label="已读" :value="true" />
<el-option label="未读" :value="false" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="getAlarmList">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<div class="device-table" :style="{ marginTop: roleKey === 'administrator' ? '10px' : '0' }">
<el-table stripe v-loading="loading" :empty-text="tableEmptyText"
style="background-color: #011c29;--el-table-border-color: none;"
:header-cell-style="{ backgroundColor: '#064B66', color: '#fff', fontSize: '40px', borderBottom: 'none' }"
:data="alarmList">
<el-table-column prop="tunnelName" label="隧道名称" align="center" width="400px" />
<el-table-column prop="alarmContent" label="告警信息" align="center" />
<el-table-column prop="alarmTime" label="告警时间" align="center" width="480px" />
<el-table-column prop="lookupStatus" label="查阅状态" align="center" width="200px"
v-if="roleKey !== 'administrator'">
<template #default="scope">
<el-tag :type="scope.row.lookupStatus ? 'success' : 'warning'"
>{{ scope.row.lookupStatus ? "已读" : "未读" }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="300px">
<template #default="scope">
<el-button
type="primary"
size="mini"
style="font-weight: bold"
@click="handleView(scope.row)"
link
>详情
</el-button>
<el-button
type="danger"
size="mini"
v-if="roleKey !== 'administrator'"
@click="handleDelete(scope.row)"
link
>删除
</el-button>
</template>
</el-table-column>
</el-table>
<div
class="pagination"
:style="{ bottom: roleKey === 'administrator' ? '-7px' : '0' }"
>
<el-pagination
background
v-model:current-page="pageInfo.pageNum"
v-model:page-size="pageInfo.pageSize"
:total="total" :pager-count="8"
prev-text="上一页"
next-text="下一页"
layout="prev, pager, next"
@current-change="handleCurrentChange"
:hide-on-single-page="true"
/>
</div>
</div>
<div class="left-bottom-icon"></div>
<div class="right-bottom-icon"></div>
</el-dialog>
</div>
</template>
<script setup>
import PreviewScene from "@/components/content/tunnelScene/PreviewScene.vue";
import FanInfo from "@/components/content/fanInfo/FanInfo.vue";
import UsedEle from "@/components/content/usedEle/UsedEle.vue";
import WindPressureList from "@/components/content/windPressure/WindPressureList.vue";
import AirInfo from "@/components/content/airInfo/AirInfo.vue";
import BadGasInfo from "@/components/content/badGasInfo/BadGasInfo.vue";
import ManageBtn from "@/components/manageBtn/index.vue";
import TunnelTitle from "@/components/tunnelTitle/index.vue";
import { dateFormat } from "@/utils/date.js";
import { getToken } from "@/utils/auth";
import { useAuthStore } from "@/store/userstore.js";
import {
getLargeScreen,
getLargeScreenInfo,
getTunnelBySiteId,
getAlarmInfo,
deleteAlarmSate,
getAlarmDetail,
updateAlarmState,
} from "@/api/largeScreen";
import { ElMessage, ElMessageBox } from "element-plus";
import { getUserInfo } from "@/api/login";
import { initSceneData } from "@/api/tunnelScene";
import { getTunnelList } from "@/api/tunnelManage";
import { debounce } from "lodash";
const authStore = useAuthStore();
const router = useRouter();
const previewId = reactive(router.currentRoute.value.params.tunnelId);
const siteId = reactive(router.currentRoute.value.params.siteId);
const tunnelIdFlag = reactive(router.currentRoute.value.params.tunnelId);
const selectIndex = ref(-1);
const isVisited = ref(false);
const isDetailVisited = ref(false);
const showFan = ref(false);
const drawerLeft = ref(true);
const showTunnelTitle = ref(true);
const loading = ref(false);
const tableEmptyText = ref("加载中~");
const initialIndex = ref(0);
const showFanLoading = ref(0);
const showUsedLoading = ref(0);
const showWindLoading = ref(0);
const showBadLoading = ref(0);
const drawerRight = ref(true);
const showMenu = ref(false);
const currentSiteId = ref(0);
const currentSite = ref("");
const siteList = ref([]);
const alarmList = ref([]);
const alarmDetail = ref([]);
const totalTunnelList = ref([]);
const currentUser = ref("");
const currentUserId = ref(0);
const currentDate = ref(dateFormat());
const tunnelBtn = ref();
const tunnelList = ref([]);
const tunnelId = ref(0);
const tunnelLength = ref(0);
const constructionLength = ref(0);
const routeList = ref([]);
let socket = reactive("");
let pattern = reactive(new RegExp("[A-Za-z]+"));
let isTunnel = reactive(false);
const coordsList = ref([
{
//厂房
clickIndex:0,
tunnelId: 98,
title: '厂房',
coords: '127,418,165,415,175,440,136,556,98,558,106,478',
}, {
//2#尾水
clickIndex:2,
tunnelId: 1,
title: '2#尾水隧洞',
coords: '283,489,840,170,886,67,869,64,831,155,272,471'
}, {
//5#支洞
clickIndex:1,
tunnelId: 109,
title: '5#支洞',
coords: '847,74,833,64,819,28,820,79,842,87'
}
])
const btnList = ref([
{
route: "/site",
icon: "sp_icon_zdgl.png",
name: "站点管理",
},
{
route: "/tunnel",
icon: "sp_icon_sdgl.png",
name: "隧道管理",
},
{
route: "/user",
icon: "sp_icon_yhgl.png",
name: "用户管理",
},
// {
// route: '/system',
// icon: 'sp_icon_xtgl.png',
// name: '系统管理'
// },
{
route: '/simulate',
icon: 'sp_icon_mngl.png',
name: '模拟仿真'
},
]);
const serialNumber = ref("");
const roleKey = ref("");
// 测量模式相关变量
const isMeasuring = ref(false);
const measurementPoints = ref([]);
let token = getToken();
let send = {
type: "ping",
};
const pageInfo = reactive({
pageNum: 1,
pageSize: 40,
});
const queryForm = ref();
const total = ref(10);
const largeScreenData = ref(null);
const socketData = reactive({
leftData: [],
windPressure: [],
sensor: [],
windSpeed: [],
});
let tunnelLen = computed(() => tunnelLength);
const queryParams = reactive({
equipmentId: "",
lookupStatus: "",
});
onMounted(() => {
getList();
getUser();
getOtherInfo();
nextTick(() => {
showFan.value = true;
});
});
function convertCoordsToArray(coordsArray) {
return coordsArray.map(point => `${point.x},${point.y}`).join(',');
}
// 示例使用:
const coordsData = [
{ x: 163, y: 487 },
{ x: 153, y: 483 },
{ x: 217, y: 480 },
{ x: 170, y: 644 },
{ x: 111, y: 640 },
{ x: 145, y: 483 }
];
const result = convertCoordsToArray(coordsData);
console.log(result);
// 输出: "163,487,153,483,217,480,170,644,111,640,145,483"
const clickHot = (id,index) => {
console.log('点击热区===============')
initialIndex.value=index
changeTunnel(index)
}
const changeName = (id) => {
for (let item of equipmentOption.value) {
if (item.value === id) {
return item.label;
}
}
return "";
};
const handleReset = () => {
queryForm.value.resetFields();
getAlarmList();
};
//点击页码进行分页功能
const handleCurrentChange = (val) => {
pageInfo.pageNum = val;
getAlarmList();
};
const handleDelete = (row) => {
ElMessageBox.confirm(`确认删除该报警信息吗?`, "系统提示", {
type: "warning",
closeOnClickModal: false,
}).then(() => {
deleteAlarmSate(row.alarmId).then((res) => {
if (res.code === 1000) {
ElMessage.success("删除成功");
getAlarmList();
}
});
});
};
//查看报警信息详情
const handleView = (row) => {
getAlarmDetail(row.alarmId).then((res) => {
if (res.code === 1000) {
isDetailVisited.value = true;
alarmDetail.value = res.data;
if (roleKey.value !== "administrator") {
if (!row.lookupStatus) {
handleChangeState(row);
}
}
}
});
};
const handleChangeState = (row) => {
updateAlarmState([
{
alarmId: row.alarmId,
lookupStatus: true,
userId: currentUserId.value,
},
]).then((res) => {
if (res.code === 1000) {
// ElMessage.success(res.msg);
getAlarmList();
} else {
// ElMessage.error(res.msg);
}
});
};
const getAlarmList = () => {
loading.value = true;
getAlarmInfo({
tunnelId: tunnelId.value,
lookupStatus: queryParams.lookupStatus,
...pageInfo,
}).then((res) => {
if (res.code === 1000) {
loading.value = false;
if (res.data.rows.length === 0) {
tableEmptyText.value = "暂无数据~";
} else {
tableEmptyText.value = "";
}
alarmList.value = res.data.rows;
total.value = res.data.total;
}
});
};
const getUser = () => {
getUserInfo().then((res) => {
currentUser.value = res.data.user.userName;
currentUserId.value = res.data.user.userId;
roleKey.value = res.data.roles[0];
localStorage.setItem("roleKey", res.data.roles[0]);
localStorage.setItem("userId", currentUserId.value);
});
};
const getOtherInfo = () => {
getLargeScreenInfo().then((res) => {
if (res?.code === 1000) {
let routeArr = [];
res.data.routeList.push('/simulate')
res.data.routeList.forEach((item) => {
for (let btn of btnList.value) {
if (item === btn.route) {
routeArr.push(btn);
}
}
});
routeList.value = routeArr;
showMenu.value = true;
siteList.value = res.data.siteOption;
tunnelList.value = res.data.tunnelOption;
if (siteId) {
currentSiteId.value = siteId;
currentSite.value = localStorage.getItem("site");
showTunnelTitle.value = false;
nextTick(() => {
showTunnelTitle.value = true;
});
getTunnel(siteId);
} else {
getTunnel(res.data.siteOption[0].value);
currentSiteId.value = res.data.siteOption[0].value;
currentSite.value = res.data.siteOption[0].label;
localStorage.setItem("site", currentSite.value);
localStorage.setItem("currentSiteId", currentSiteId.value);
}
}
});
};
const getScreenInfo = (id) => {
if (id) {
tunnelId.value = id;
// equipmentOption.value = []
getLargeScreen(id).then((res) => {
if (res?.code === 1000) {
if (res.data.frequencyChangerList.length !== 0) {
showFanLoading.value = 0;
} else {
showFanLoading.value = 1;
}
if (res.data.windPressureSensorList.length !== 0) {
showWindLoading.value = 0;
} else {
showWindLoading.value = 1;
}
if (res.data.sensorList.length !== 0) {
// res.data.sensorList.forEach((item) => {
// option = {
// value: item.equipmentId,
// label: item.equipmentName,
// }
// equipmentOption.value.push(option)
// })
res.data.sensorList.forEach((item) => {
if (
item.equipmentType === "dust" ||
item.equipmentType === "carbonDioxide" ||
item.equipmentType === "carbonMonoxide" ||
item.equipmentType === "hydrogenSulfide" ||
item.equipmentType === "sulfurDioxide" ||
item.equipmentType === "sulfurMonoxide" ||
item.equipmentType === "nitrogenDioxide"
) {
showBadLoading.value = 0;
} else {
showBadLoading.value = 1;
}
});
} else {
showBadLoading.value = 1;
}
largeScreenData.value = res.data;
} else {
ElMessage.warning(res.msg);
}
});
initSceneData(id).then((res) => {
tunnelLength.value = res.data.tunnelLength;
constructionLength.value = res.data.constructionLength;
// serialNumber.value = res.data.serialNumber.slice(0, -2)
serialNumber.value = res.data.serialNumberPrefix;
initWebSocket();
});
}
};
//这里可以获取隧道简称
const getList = () => {
getTunnelList({
siteId: siteId,
}).then((res) => {
if (res.code === 1000) {
if (res.data.rows.length !== 0) {
isTunnel = true;
totalTunnelList.value = res.data.rows;
} else {
isTunnel = false;
}
}
});
};
let largeScreen = computed(() => largeScreenData);
const getTunnel = (id) => {
getTunnelBySiteId(id).then((res) => {
if (res?.code === 1000) {
if (res.data.length === 0) {
if (isTunnel) {
ElMessage.warning("该站点下没有隧道可展示, 请添加设备后再试!");
} else {
ElMessage.warning("该站点下没有隧道, 请新增隧道后再试!");
}
} else {
if (!pattern.test(previewId)) {
getScreenInfo(previewId);
tunnelList.value.forEach((item, index) => {
if (item.value == previewId) {
initialIndex.value = index;
tunnelBtn.value.setActiveItem(index);
}
});
} else {
// console.info("🚀 ~method: tunnelList.value -----", tunnelList.value)
// tunnelList.value = res.data;
getScreenInfo(res.data[0]?.value);
}
}
}
});
};
const changeTunnel = (e) => {
// console.info("🚀 ~method:'tunnelList.value' -----", tunnelList.value)
console.info("🚀 ~method:'changeTunnel' -----", e)
if(socket){
socket.close()
}
let newObj = {}
tunnelList.value.forEach((item, index) => {
if (index == e) {
newObj = item;
}
});
// console.info("🚀 ~method:changeTunnel -----", newObj)
showFan.value = false;
showBadLoading.value = 0;
showWindLoading.value = 0;
showFanLoading.value = 0;
// showUsedLoading.value = 0
pageInfo.pageNum = 1;
getScreenInfo(newObj.value);
nextTick(() => {
showFan.value = true;
});
};
const manageSelect = (name) => {
if (name === "站点管理") {
if (currentUserId.value) {
router.push("/site/" + currentUserId.value + "/" + currentSiteId.value);
}
} else if (name === "隧道管理") {
if (currentSiteId.value && currentUserId.value) {
router.push(
"/tunnel/" +
localStorage.getItem("currentSiteId") +
"/byHome/" +
currentUserId.value
);
}
} else if (name === "用户管理") {
if (currentSiteId.value) {
router.push("/user/" + localStorage.getItem("currentSiteId"));
}
} else if (name === "模拟仿真") {
if (tunnelId.value) {
router.push("/simulate/tunnel/list");
}
}
if(socket){
socket.close()
}
};
const handleChangeSite = debounce((item) => {
if(socket){
socket.close()
}
currentSite.value = item.label
currentSiteId.value = item.value
getTunnel(item.value)
showFan.value = false
nextTick(() => {
showFan.value = true;
});
localStorage.setItem("site", currentSite.value);
localStorage.setItem("currentSiteId", currentSiteId.value);
if (tunnelIdFlag) {
router.push("/" + tunnelIdFlag + "/" + currentSiteId.value);
}
showTunnelTitle.value = false;
nextTick(() => {
showTunnelTitle.value = true;
});
}, 100);
const closeLeft = () => {
drawerLeft.value = !drawerLeft.value;
};
const closeRight = () => {
drawerRight.value = !drawerRight.value;
};
const handleLogout = () => {
ElMessageBox.confirm(`确认退出登录吗`, "系统提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
authStore.userLogout();
router.push("/login");
});
};
const previousBtn = () => {
tunnelBtn.value.prev();
};
const nextBtn = () => {
tunnelBtn.value.next();
};
const initWebSocket = () => {
// let wsUrl = `ws://192.168.31.176:9000/websocket/equipment/${token}/${serialNumber.value}`
let wsUrl = `ws://tunnel.feashow.com/api/wstunnel/websocket/equipment/${token}/${serialNumber.value}`;
// let wsUrl = import.meta.env.VITE_BASE_WSURL+`/${token}/${serialNumber.value}`;
// let wsUrl = `ws://clay.frp.feashow.cn/wstunnel/websocket/equipment/${token}/${serialNumber.value}`;
socket = new WebSocket(wsUrl);
//连接发生错误的回调方法
socket.onerror = function () {
console.log("ws连接发生错误");
};
//连接成功建立的回调方法
socket.onopen = function () {
// console.log("ws连接成功");
};
//接收到消息的回调方法
socket.onmessage = function (event) {
// console.log("服务器返回的信息: ", JSON.parse(event.data));
const type = JSON.parse(event.data).type;
const data = JSON.parse(event.data).data;
if (type === "equipment") {
data.forEach((item) => {
if (item.typeKey === "frequency") {
socketData.leftData = data;
// console.log(socketData.leftData);
} else if (item.typeKey === "windPressure") {
socketData.windPressure = data;
} else if (item.typeKey === "sensor") {
socketData.sensor = data;
} else if (item.typeKey === "windSpeed") {
socketData.windSpeed = data;
}
});
}
};
//连接关闭的回调方法
socket.onclose = function () {
console.log("ws连接关闭");
// initWebSocket()
};
setInterval(() => {
socket.send(JSON.stringify(send));
}, 30000);
};
// 测量模式相关函数
const toggleMeasurementMode = () => {
isMeasuring.value = !isMeasuring.value;
if (!isMeasuring.value) {
// 退出测量模式时输出坐标数组
console.log('测量点坐标数组:', measurementPoints.value);
// 格式化输出坐标点(符合项目规范)
const formattedCoords = measurementPoints.value.map(point => `${point.x},${point.y}`).join(',');
console.log('格式化坐标点:', formattedCoords);
// 显示提示信息
ElMessage.success(`测量完成,共记录${measurementPoints.value.length}个点`);
} else {
// 进入测量模式时清空之前的坐标
measurementPoints.value = [];
// 清除之前的视觉反馈点
const points = document.querySelectorAll('.measurement-point');
points.forEach(point => point.remove());
// 显示提示信息
ElMessage.info('已进入测量模式,请在图片上点击记录坐标点');
}
};
const handleImageClick = (event) => {
// 如果不是测量模式执行原来的getInfo逻辑
if (!isMeasuring.value) {
getInfo();
return;
}
// 获取图片元素和点击位置
const imgElement = document.getElementById('imgModel');
const rect = imgElement.getBoundingClientRect();
// 计算相对于图片的坐标
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
// 添加坐标到数组
measurementPoints.value.push({ x: Math.round(x), y: Math.round(y) });
// 在点击位置创建一个视觉反馈点
const pointElement = document.createElement('div');
pointElement.style.position = 'absolute';
pointElement.style.left = (event.clientX - 2) + 'px';
pointElement.style.top = (event.clientY - 2) + 'px';
pointElement.style.width = '4px';
pointElement.style.height = '4px';
pointElement.style.backgroundColor = 'red';
pointElement.style.borderRadius = '50%';
pointElement.style.zIndex = '9999';
pointElement.className = 'measurement-point';
document.body.appendChild(pointElement);
// 显示当前点数
ElMessage.info(`已记录${measurementPoints.value.length}个点`);
};
// 清除测量点
const clearMeasurementPoints = () => {
measurementPoints.value = [];
// 清除视觉反馈点
const points = document.querySelectorAll('.measurement-point');
points.forEach(point => point.remove());
ElMessage.info('已清除所有测量点');
};
</script>
<style lang="scss">
.alarm-tunnel .device-table{
height: 1158px!important;
overflow-y: scroll;
.el-dialog__body{
height: 1158px!important;
overflow-y: scroll;
}
&::-webkit-scrollbar {
width: 16px;
}
// 滚动条轨道
&::-webkit-scrollbar-track {
background: transparent;
border-radius: 2px;
}
// 小滑块
&::-webkit-scrollbar-thumb {
background: rgb(8, 183, 184);
border-radius: 10px;
}
}
.el-drawer__header {
display: none;
}
.el-drawer__body {
padding: 160px 68px 0 68px;
overflow: hidden;
}
.el-dropdown__popper.el-popper {
background: transparent;
//border: none;
border: 1px solid #0e7daa;
border-radius: 10px;
}
.dropdown-style {
padding: 20px;
margin-left: 50px;
width: 150px;
background: rgba(7, 35, 72, 0.9);
.el-scrollbar__wrap {
.el-dropdown__list {
.el-dropdown-menu {
background-color: rgba(7, 35, 72, 0.9);
border-radius: 10px;
padding: 5px;
.el-dropdown-menu__item {
color: #ffffff;
//border:none;
padding: 5px;
border-bottom: 1px solid #05feff;
&:last-child {
border-bottom: none;
}
}
.el-dropdown-menu__item.hover,
.el-dropdown-menu__item:hover {
background-color: transparent !important;
color: #f7b500;
}
}
}
}
.is-light {
background: rgba(7, 35, 72, 0.9);
}
}
</style>
<style lang="scss" scoped>
#main {
height: 100%;
width: 100%;
//background-color: #072348;
background-image: url('/images/background/background.png');
#tunnel-box {
height: 100%;
}
.img-box {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.imgModel{
width: 1040px;
height: 646px;
}
}
.alarm-dialog {
:deep(.el-dialog) {
height: auto !important;
margin: 820px auto 0 auto;
}
}
:deep(.el-table__body-wrapper) {
height: 90px;
}
:deep(.el-table--fit) {
width: auto;
//height: 600px;
background-color: transparent !important;
.el-loading-mask {
background: rgba(6, 34, 71, 0.78);
.el-loading-spinner {
.circular {
width: 150px;
height: 150px;
}
}
}
}
:deep(.el-table__empty-block) {
height: 200px !important;
//display: none;
.el-table__empty-text {
font-size: 60px;
color: #08b7b8;
}
}
.query-form {
:deep(.el-form-item__label) {
font-size: 45px;
height: 70px;
line-height: 70px;
color: #ffffff;
}
:deep(.el-select__wrapper.is-hovering) {
.el-select__suffix {
.el-icon {
font-size: 55px;
}
}
}
:deep(.el-select-dropdown__item.is-hovering) {
background-color: #064b66 !important;
}
:deep(.el-select__wrapper) {
height: 70px;
line-height: 70px;
background-color: transparent;
border: 1px solid #08b7b8;
font-size: 35px;
width: 356px;
.el-select__suffix {
.el-icon {
font-size: 55px;
}
}
.el-select__placeholder {
color: #ffffff;
height: 70px;
line-height: 70px;
}
.el-select__placeholder.is-transparent {
height: 70px;
line-height: 70px;
}
}
:deep(.el-form-item__content) {
.el-select__popper {
margin-top: -12px !important;
background: #072247 !important;
border: 1px solid #0f82af !important;
.el-select-dropdown__item {
font-size: 38px !important;
height: 70px !important;
line-height: 70px !important;
> span {
color: #ffffff;
}
}
.el-select-dropdown__item.hover,
.el-select-dropdown__item:hover {
background-color: #064b66 !important;
}
}
.el-button {
height: 70px;
font-size: 40px;
border-radius: 10px;
}
.el-button--primary {
background: #064b66;
}
.el-input {
width: 380px;
height: 70px;
.el-input__wrapper {
font-size: 40px;
background-color: transparent;
border: 1px solid #08b7b8;
box-shadow: none;
.el-input__inner {
height: 70px;
color: #ffffff;
}
.el-input__suffix-inner {
.el-icon {
font-size: 55px;
}
}
}
}
}
}
.device-table {
margin-top: 7px;
:deep(.el-button) {
font-size: 40px;
}
.active {
color: #ffffff;
background: #0f7da9;
}
.wind-switch {
margin-left: 65px;
}
.switch {
margin-top: 22px;
display: flex;
width: 200px;
height: 50px;
border-radius: 6px;
border: 1px solid #05feff;
overflow: hidden;
color: #51a2b3;
line-height: 40px;
font-size: 36px;
& > div {
flex: 1;
text-align: center;
cursor: pointer;
}
}
:deep(.el-table--fit) {
//width: 1780px !important;
width: auto;
}
:deep(.cell) {
height: 80px;
line-height: 80px;
color: #fff;
font-size: 38px;
}
:deep(.el-table tr) {
background-color: #1c5971;
}
:deep(
.el-table--enable-row-hover .el-table__body tr:hover > td.el-table__cell
) {
background-color: #1c5971;
}
:deep(.el-table__row--striped) {
background-color: #13849c !important;
}
:deep(
.el-table--striped
.el-table__body
tr.el-table__row--striped
td.el-table__cell
) {
background-color: #13849c !important;
}
:deep(.el-table__cell) {
.el-input {
width: 180px;
height: 53px;
}
.el-input__wrapper {
border-radius: 6px;
border: 1px solid #05feff;
background-color: transparent;
.el-input__inner {
color: #ffffff;
font-size: 40px;
height: auto;
line-height: normal;
}
}
}
:deep(.el-table__inner-wrapper::before) {
display: none;
}
}
.pagination {
display: flex;
align-items: center;
position: absolute;
left: 50%;
transform: translate(-50%, -50%);
bottom: 0;
color: #60ddde;
font-size: 38px;
font-weight: bold;
:deep(.el-pagination.is-background) {
.btn-next,
.btn-prev {
background-color: transparent;
}
.el-pager {
li {
margin: 0 0 0 40px;
}
li.is-active {
background-color: #60ddde;
}
}
}
> span:first-child {
margin-right: 60px;
}
> span:last-child {
margin-left: 71px;
}
:deep(.btn-prev) {
background-color: transparent;
font-size: 38px;
font-weight: bold;
color: #60ddde;
margin-right: 20px;
}
:deep(.btn-next) {
background-color: transparent;
font-size: 38px;
font-weight: bold;
color: #60ddde;
margin-left: 30px;
}
:deep(.el-pager li.is-active) {
min-width: 70px;
height: 70px;
background: #60ddde;
border-radius: 50%;
color: #071f40;
font-size: 38px;
font-weight: bold;
}
:deep(.el-pager li) {
margin-left: 40px !important;
}
:deep(.el-pager li:not(.is-active)) {
min-width: 70px;
height: 70px;
border: 1px solid #60ddde;
border-radius: 50%;
background-color: transparent;
font-size: 38px;
font-weight: bold;
color: #60ddde;
}
}
</style>