修改完毕
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from "vue";
|
||||
import monitorVideo from "../assets/循环背景动画.mp4";
|
||||
import { recentAlertPhotoSources } from "../shared/alertPhotos";
|
||||
|
||||
const monitorVideo = "https://wuhuimg.oss-cn-shanghai.aliyuncs.com/video/output.mp4";
|
||||
|
||||
type DriveMode = "阿克曼" | "旋转" | "横移";
|
||||
|
||||
interface DeviceItem {
|
||||
id: string;
|
||||
@@ -26,7 +30,11 @@ interface NavPoint {
|
||||
|
||||
interface NavScenario {
|
||||
routePath: string;
|
||||
cloudPoints: NavPoint[];
|
||||
lanePath: string;
|
||||
upperEdgePath: string;
|
||||
lowerEdgePath: string;
|
||||
upperWallPoints: NavPoint[];
|
||||
lowerWallPoints: NavPoint[];
|
||||
checkPoints: Array<{ x: number; y: number; label: string }>;
|
||||
}
|
||||
|
||||
@@ -34,8 +42,8 @@ interface AlertItem {
|
||||
id: string;
|
||||
title: string;
|
||||
level: "高" | "中" | "低";
|
||||
location: string;
|
||||
time: string;
|
||||
photo: string;
|
||||
}
|
||||
|
||||
const projectDevices: ProjectItem[] = [
|
||||
@@ -92,28 +100,35 @@ const recentAlerts: AlertItem[] = [
|
||||
id: "AL-001",
|
||||
title: "线缆破损",
|
||||
level: "高",
|
||||
location: "西江隧道 K1+240",
|
||||
time: "10:24:18",
|
||||
time: "2026-06-26 10:24:18",
|
||||
photo: recentAlertPhotoSources["线缆破损"],
|
||||
},
|
||||
{
|
||||
id: "AL-002",
|
||||
title: "局部高温",
|
||||
level: "中",
|
||||
location: "西江隧道 泵房区",
|
||||
time: "10:18:42",
|
||||
time: "2026-06-26 10:18:42",
|
||||
photo: recentAlertPhotoSources["局部高温"],
|
||||
},
|
||||
{
|
||||
id: "AL-003",
|
||||
title: "墙体裂缝",
|
||||
level: "低",
|
||||
location: "西江隧道 K1+860",
|
||||
time: "09:56:11",
|
||||
time: "2026-06-26 09:56:11",
|
||||
photo: recentAlertPhotoSources["墙体裂缝"],
|
||||
},
|
||||
];
|
||||
|
||||
const alertTabs = ["全部", "线缆破损", "局部高温", "墙体裂缝"] as const;
|
||||
const activeAlertTab = ref<(typeof alertTabs)[number]>("全部");
|
||||
const activeCameraMode = ref<"white" | "infrared">("white");
|
||||
const driveModeTabs: DriveMode[] = ["阿克曼", "旋转", "横移"];
|
||||
const activeDriveMode = ref<DriveMode>("阿克曼");
|
||||
const presetPointOptions = Array.from(
|
||||
{ length: 10 },
|
||||
(_, index) => `预置点${index + 1}`,
|
||||
);
|
||||
const selectedPresetPoint = ref("预置点1");
|
||||
const controlMode = ref<"人工接管" | "自主巡检">("人工接管");
|
||||
const targetLiftHeight = ref("1200");
|
||||
const liftRealtimeStatus = ref("待命");
|
||||
@@ -121,38 +136,73 @@ const liftRealtimeHeight = ref("1180");
|
||||
|
||||
const navScenario: NavScenario = {
|
||||
routePath:
|
||||
"M 72 310 C 118 282, 164 265, 212 248 S 312 205, 360 216 S 446 270, 504 248 S 612 188, 690 210",
|
||||
cloudPoints: [
|
||||
{ x: 52, y: 120 },
|
||||
{ x: 86, y: 132 },
|
||||
{ x: 128, y: 148 },
|
||||
{ x: 164, y: 138 },
|
||||
{ x: 204, y: 118 },
|
||||
{ x: 252, y: 104 },
|
||||
{ x: 304, y: 120 },
|
||||
{ x: 346, y: 142 },
|
||||
{ x: 392, y: 168 },
|
||||
{ x: 442, y: 162 },
|
||||
{ x: 494, y: 134 },
|
||||
{ x: 548, y: 116 },
|
||||
{ x: 602, y: 132 },
|
||||
{ x: 660, y: 152 },
|
||||
{ x: 94, y: 254 },
|
||||
{ x: 150, y: 236 },
|
||||
{ x: 214, y: 220 },
|
||||
{ x: 278, y: 208 },
|
||||
{ x: 338, y: 214 },
|
||||
{ x: 400, y: 236 },
|
||||
{ x: 468, y: 258 },
|
||||
{ x: 530, y: 238 },
|
||||
{ x: 592, y: 214 },
|
||||
{ x: 656, y: 226 },
|
||||
"M 106 318 L 154 306 L 208 292 L 256 278 L 306 264 L 358 258 L 414 258 L 468 248 L 522 241 L 578 234 L 638 226",
|
||||
lanePath:
|
||||
"M 82 270 L 136 258 L 190 244 L 244 230 L 296 216 L 350 208 L 408 208 L 464 198 L 518 190 L 576 182 L 642 174 L 656 188 L 650 278 L 586 286 L 528 294 L 472 302 L 414 312 L 358 312 L 304 320 L 252 334 L 198 348 L 142 360 L 88 370 L 74 356 Z",
|
||||
upperEdgePath:
|
||||
"M 82 270 L 136 258 L 190 244 L 244 230 L 296 216 L 350 208 L 408 208 L 464 198 L 518 190 L 576 182 L 642 174",
|
||||
lowerEdgePath:
|
||||
"M 88 370 L 142 360 L 198 348 L 252 334 L 304 320 L 358 312 L 414 312 L 472 302 L 528 294 L 586 286 L 650 278",
|
||||
upperWallPoints: [
|
||||
{ x: 64, y: 164 },
|
||||
{ x: 84, y: 150 },
|
||||
{ x: 104, y: 142 },
|
||||
{ x: 126, y: 136 },
|
||||
{ x: 148, y: 132 },
|
||||
{ x: 172, y: 126 },
|
||||
{ x: 196, y: 122 },
|
||||
{ x: 224, y: 118 },
|
||||
{ x: 252, y: 116 },
|
||||
{ x: 280, y: 112 },
|
||||
{ x: 312, y: 110 },
|
||||
{ x: 344, y: 108 },
|
||||
{ x: 378, y: 108 },
|
||||
{ x: 412, y: 110 },
|
||||
{ x: 448, y: 112 },
|
||||
{ x: 484, y: 114 },
|
||||
{ x: 520, y: 116 },
|
||||
{ x: 556, y: 120 },
|
||||
{ x: 590, y: 126 },
|
||||
{ x: 622, y: 134 },
|
||||
{ x: 650, y: 144 },
|
||||
{ x: 676, y: 158 },
|
||||
{ x: 116, y: 170 },
|
||||
{ x: 214, y: 156 },
|
||||
{ x: 326, y: 146 },
|
||||
{ x: 446, y: 148 },
|
||||
{ x: 566, y: 160 },
|
||||
{ x: 638, y: 172 },
|
||||
],
|
||||
lowerWallPoints: [
|
||||
{ x: 72, y: 380 },
|
||||
{ x: 96, y: 392 },
|
||||
{ x: 122, y: 404 },
|
||||
{ x: 150, y: 410 },
|
||||
{ x: 180, y: 416 },
|
||||
{ x: 214, y: 420 },
|
||||
{ x: 248, y: 422 },
|
||||
{ x: 284, y: 424 },
|
||||
{ x: 322, y: 426 },
|
||||
{ x: 360, y: 426 },
|
||||
{ x: 400, y: 424 },
|
||||
{ x: 440, y: 420 },
|
||||
{ x: 480, y: 414 },
|
||||
{ x: 520, y: 408 },
|
||||
{ x: 558, y: 400 },
|
||||
{ x: 594, y: 392 },
|
||||
{ x: 628, y: 384 },
|
||||
{ x: 660, y: 374 },
|
||||
{ x: 700, y: 366 },
|
||||
{ x: 126, y: 376 },
|
||||
{ x: 238, y: 392 },
|
||||
{ x: 354, y: 396 },
|
||||
{ x: 474, y: 388 },
|
||||
{ x: 590, y: 372 },
|
||||
],
|
||||
checkPoints: [
|
||||
{ x: 104, y: 280, label: "起点" },
|
||||
{ x: 268, y: 224, label: "高温检测点" },
|
||||
{ x: 480, y: 256, label: "泵房区" },
|
||||
{ x: 674, y: 214, label: "终点" },
|
||||
{ x: 106, y: 318, label: "起点" },
|
||||
{ x: 358, y: 258, label: "途经点" },
|
||||
{ x: 638, y: 226, label: "终点" },
|
||||
],
|
||||
};
|
||||
|
||||
@@ -177,11 +227,19 @@ function switchCameraMode(mode: "white" | "infrared") {
|
||||
activeCameraMode.value = mode;
|
||||
}
|
||||
|
||||
function switchDriveMode(mode: DriveMode) {
|
||||
activeDriveMode.value = mode;
|
||||
}
|
||||
|
||||
function toggleControlMode() {
|
||||
controlMode.value =
|
||||
controlMode.value === "人工接管" ? "自主巡检" : "人工接管";
|
||||
}
|
||||
|
||||
function jumpToPresetPoint() {
|
||||
selectedPresetPoint.value = selectedPresetPoint.value;
|
||||
}
|
||||
|
||||
function runToTargetHeight() {
|
||||
const normalizedHeight = targetLiftHeight.value.replace(/[^\d]/g, "");
|
||||
|
||||
@@ -218,7 +276,6 @@ function getStatusClass(status: DeviceItem["status"]) {
|
||||
</div>
|
||||
<div class="device-meta">
|
||||
<span>{{ device.id }}</span>
|
||||
<span>{{ device.location }}</span>
|
||||
<span>{{ device.battery }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -242,14 +299,24 @@ function getStatusClass(status: DeviceItem["status"]) {
|
||||
</div>
|
||||
<template v-if="filteredRecentAlerts.length">
|
||||
<div v-for="alert in filteredRecentAlerts" :key="alert.id" class="alert-item">
|
||||
<div class="alert-main">
|
||||
<div class="alert-title">{{ alert.title }}</div>
|
||||
<div class="alert-level">{{ alert.level }}级</div>
|
||||
</div>
|
||||
<div class="alert-meta">
|
||||
<span>{{ alert.id }}</span>
|
||||
<span>{{ alert.location }}</span>
|
||||
<span>{{ alert.time }}</span>
|
||||
<div class="alert-content">
|
||||
<img class="alert-thumbnail" :src="alert.photo" :alt="alert.title" />
|
||||
<div class="alert-info">
|
||||
<div class="alert-summary">
|
||||
<div class="alert-field">
|
||||
<div class="alert-label">告警类型</div>
|
||||
<div class="alert-title">{{ alert.title }}</div>
|
||||
</div>
|
||||
<div class="alert-field alert-field-right">
|
||||
<div class="alert-label">告警等级</div>
|
||||
<div :class="['alert-level', `level-${alert.level}`]">{{ alert.level }}级</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert-time-row">
|
||||
<span class="alert-label">告警时间</span>
|
||||
<span class="alert-time">{{ alert.time }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -266,13 +333,25 @@ function getStatusClass(status: DeviceItem["status"]) {
|
||||
<line v-for="line in 7" :key="`h-${line}`" :x1="40" :y1="line * 52" :x2="720" :y2="line * 52" />
|
||||
<line v-for="line in 12" :key="`v-${line}`" :x1="line * 56" :y1="40" :x2="line * 56" :y2="380" />
|
||||
</g>
|
||||
<g class="cloud-layer">
|
||||
<path class="corridor-lane" :d="currentScenario.lanePath" />
|
||||
<path class="corridor-edge" :d="currentScenario.upperEdgePath" />
|
||||
<path class="corridor-edge" :d="currentScenario.lowerEdgePath" />
|
||||
<g class="wall-layer wall-upper">
|
||||
<circle
|
||||
v-for="(point, index) in currentScenario.cloudPoints"
|
||||
:key="`point-${index}`"
|
||||
v-for="(point, index) in currentScenario.upperWallPoints"
|
||||
:key="`upper-wall-${index}`"
|
||||
:cx="point.x"
|
||||
:cy="point.y"
|
||||
:r="index % 3 === 0 ? 3 : 2"
|
||||
:r="index % 4 === 0 ? 3.4 : 2.4"
|
||||
/>
|
||||
</g>
|
||||
<g class="wall-layer wall-lower">
|
||||
<circle
|
||||
v-for="(point, index) in currentScenario.lowerWallPoints"
|
||||
:key="`lower-wall-${index}`"
|
||||
:cx="point.x"
|
||||
:cy="point.y"
|
||||
:r="index % 5 === 0 ? 3.2 : 2.3"
|
||||
/>
|
||||
</g>
|
||||
<path class="route-glow" :d="currentScenario.routePath" />
|
||||
@@ -300,35 +379,14 @@ function getStatusClass(status: DeviceItem["status"]) {
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
<div class="nav-map-overlay top-left">激光雷达点云</div>
|
||||
<div class="nav-map-overlay top-right">路径跟踪中</div>
|
||||
<div class="nav-map-overlay top-left">隧道点云走廊</div>
|
||||
<div class="nav-map-overlay top-right">窄通道路径跟踪</div>
|
||||
<div class="nav-map-overlay bottom-left">
|
||||
机器人编号:{{ currentRobot.id }} / {{ currentRobot.task }}
|
||||
</div>
|
||||
<div class="nav-map-overlay bottom-right">
|
||||
线速度 {{ currentRobot.linearSpeed }} / 角速度 {{ currentRobot.angularSpeed }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav-map-footer">
|
||||
<div class="footer-item">
|
||||
<span class="footer-label">当前位置</span>
|
||||
<span class="footer-value">{{ currentRobot.location }}</span>
|
||||
</div>
|
||||
<div class="footer-item">
|
||||
<span class="footer-label">电量</span>
|
||||
<span class="footer-value">{{ currentRobot.battery }}</span>
|
||||
</div>
|
||||
<div class="footer-item">
|
||||
<span class="footer-label">任务状态</span>
|
||||
<span class="footer-value">{{ currentRobot.task }}</span>
|
||||
</div>
|
||||
<div class="footer-item">
|
||||
<span class="footer-label">实时线速度</span>
|
||||
<span class="footer-value">{{ currentRobot.linearSpeed }}</span>
|
||||
</div>
|
||||
<div class="footer-item">
|
||||
<span class="footer-label">实时角速度</span>
|
||||
<span class="footer-value">{{ currentRobot.angularSpeed }}</span>
|
||||
电量 {{ currentRobot.battery }} / 线速度 {{ currentRobot.linearSpeed }} / 角速度
|
||||
{{ currentRobot.angularSpeed }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -371,7 +429,6 @@ function getStatusClass(status: DeviceItem["status"]) {
|
||||
{{ activeCameraMode === "white" ? "白光镜头" : "红外镜头" }}
|
||||
</div>
|
||||
<div class="video-overlay bottom-left">{{ currentRobot.id }}</div>
|
||||
<div class="video-overlay bottom-right">{{ currentRobot.location }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -403,6 +460,17 @@ function getStatusClass(status: DeviceItem["status"]) {
|
||||
<div class="control-top-row">
|
||||
<div class="vehicle-section">
|
||||
<div class="section-title control-block-title">底盘驱动</div>
|
||||
<div class="drive-mode-tabs">
|
||||
<button
|
||||
v-for="mode in driveModeTabs"
|
||||
:key="mode"
|
||||
:class="['drive-mode-tab', { active: activeDriveMode === mode }]"
|
||||
type="button"
|
||||
@click="switchDriveMode(mode)"
|
||||
>
|
||||
{{ mode }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="vehicle-pad">
|
||||
<button class="drive-btn drive-up" type="button">
|
||||
<span class="drive-arrow">↑</span>
|
||||
@@ -425,6 +493,22 @@ function getStatusClass(status: DeviceItem["status"]) {
|
||||
</div>
|
||||
<div class="control-section gimbal-section">
|
||||
<div class="section-title">云台控制</div>
|
||||
<div class="gimbal-preset-panel">
|
||||
<label class="gimbal-preset-group">
|
||||
<select v-model="selectedPresetPoint" class="gimbal-preset-select">
|
||||
<option
|
||||
v-for="preset in presetPointOptions"
|
||||
:key="preset"
|
||||
:value="preset"
|
||||
>
|
||||
{{ preset }}
|
||||
</option>
|
||||
</select>
|
||||
</label>
|
||||
<button class="gimbal-preset-btn" type="button" @click="jumpToPresetPoint">
|
||||
跳转
|
||||
</button>
|
||||
</div>
|
||||
<div class="vehicle-pad gimbal-wheel">
|
||||
<button class="drive-btn drive-up" type="button">
|
||||
<span class="drive-arrow">↑</span>
|
||||
@@ -482,3 +566,4 @@ function getStatusClass(status: DeviceItem["status"]) {
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user