feat: 加载地图
This commit is contained in:
parent
780665fa9a
commit
6af1118a3c
@ -10,3 +10,5 @@ VITE_APP_BASE_API = '/dev-api'
|
|||||||
VITE_API_URL = http://192.168.28.10:13080
|
VITE_API_URL = http://192.168.28.10:13080
|
||||||
VITE_WS_URL = ws://192.168.0.201:13080/ws
|
VITE_WS_URL = ws://192.168.0.201:13080/ws
|
||||||
|
|
||||||
|
# explanation
|
||||||
|
VITE_INSPECTION_TYPE = "inspection"
|
||||||
@ -15,3 +15,6 @@ VITE_NODE_RED_URL = 'http://10.148.108.59:13080'
|
|||||||
|
|
||||||
VITE_API_URL = http://192.168.28.10:13080
|
VITE_API_URL = http://192.168.28.10:13080
|
||||||
VITE_WS_URL = ws://192.168.0.201:13080/ws
|
VITE_WS_URL = ws://192.168.0.201:13080/ws
|
||||||
|
|
||||||
|
# explanation
|
||||||
|
VITE_INSPECTION_TYPE = "inspection"
|
||||||
@ -30,3 +30,28 @@ export function deleteRobot(id) {
|
|||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取机器人地图
|
||||||
|
* @param {*} robotId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getMapByRobot(robotId) {
|
||||||
|
return request({
|
||||||
|
url: `/inspection/robot/${robotId}/maps`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取地图JSON数据
|
||||||
|
* @param {*} params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getMapJson(params) {
|
||||||
|
return request({
|
||||||
|
url: `/inspection/robot/${params.robotId}/download-map`,
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -153,7 +153,7 @@ async function loadMapIntoApplication(parsedMap) {
|
|||||||
const worldHeightMeters = parsedMap.maxY - parsedMap.originY; // 地图世界高度
|
const worldHeightMeters = parsedMap.maxY - parsedMap.originY; // 地图世界高度
|
||||||
const fitScaleX = inspectionStore.canvasWidthPx / worldWidthMeters; // 水平方向恰好填满的缩放
|
const fitScaleX = inspectionStore.canvasWidthPx / worldWidthMeters; // 水平方向恰好填满的缩放
|
||||||
const fitScaleY = inspectionStore.canvasHeightPx / worldHeightMeters; // 垂直方向恰好填满的缩放
|
const fitScaleY = inspectionStore.canvasHeightPx / worldHeightMeters; // 垂直方向恰好填满的缩放
|
||||||
inspectionStore.camera.pixelsPerMeter = Math.max(1, Math.min(fitScaleX, fitScaleY) * 1); // 取较小值
|
inspectionStore.camera.pixelsPerMeter = Math.max(1, Math.max(fitScaleX, fitScaleY) * 1); // 取较小值
|
||||||
|
|
||||||
// 标记加载完成并更新UI
|
// 标记加载完成并更新UI
|
||||||
applicationState.isMapLoaded = true;
|
applicationState.isMapLoaded = true;
|
||||||
|
|||||||
@ -65,10 +65,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="right-container">
|
<div class="right-container">
|
||||||
<div class="sensor-container">
|
<!-- <div class="sensor-container">
|
||||||
<div class="sensor-title">传感器数据</div>
|
<div class="sensor-title">传感器数据</div>
|
||||||
</div>
|
</div> -->
|
||||||
<div class="split-line"></div>
|
<!-- <div class="split-line"></div> -->
|
||||||
<div class="alarm-container">
|
<div class="alarm-container">
|
||||||
<div class="container-title">告警记录</div>
|
<div class="container-title">告警记录</div>
|
||||||
<div class="alarm-box" v-for="item, index in 2">
|
<div class="alarm-box" v-for="item, index in 2">
|
||||||
@ -85,6 +85,8 @@ import SvgIcon from "@/components/SvgIcon";
|
|||||||
import MapCanvas from "./MapCanvas.vue";
|
import MapCanvas from "./MapCanvas.vue";
|
||||||
import { onMounted, onUnmounted } from "vue";
|
import { onMounted, onUnmounted } from "vue";
|
||||||
|
|
||||||
|
console.log('sss', import.meta.env.VITE_INSPECTION_TYPE)
|
||||||
|
|
||||||
const robotState = ref('online')
|
const robotState = ref('online')
|
||||||
|
|
||||||
const mapCanvasRef = ref(null)
|
const mapCanvasRef = ref(null)
|
||||||
|
|||||||
@ -88,7 +88,7 @@
|
|||||||
|
|
||||||
import { useContainerHeight } from "@/hooks/tableHeight";
|
import { useContainerHeight } from "@/hooks/tableHeight";
|
||||||
import { addMap, updateMap, deleteMap, getMapList } from "../../../api/inspection/map";
|
import { addMap, updateMap, deleteMap, getMapList } from "../../../api/inspection/map";
|
||||||
import { getRobotList } from "../../../api/inspection/robot";
|
import { getRobotList, getMapByRobot, getMapJson } from "../../../api/inspection/robot";
|
||||||
import { onMounted } from "vue";
|
import { onMounted } from "vue";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -171,6 +171,7 @@ function cancel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.removeEventListener('resize', resizeCanvasToContainer)
|
window.removeEventListener('resize', resizeCanvasToContainer)
|
||||||
|
canvasCtx.clearRect(0, 0, applicationState.canvasWidthPx, applicationState.canvasHeightPx);
|
||||||
canvasCtx = null
|
canvasCtx = null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,6 +216,7 @@ function handleUpdate(row) {
|
|||||||
isNew.value = false;
|
isNew.value = false;
|
||||||
open.value = true;
|
open.value = true;
|
||||||
title.value = "修改地图";
|
title.value = "修改地图";
|
||||||
|
getMapArr(row.mapSourceRobotId)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
init();
|
init();
|
||||||
if (form.value.mapSourceName) {
|
if (form.value.mapSourceName) {
|
||||||
@ -265,12 +267,6 @@ function handleDelete(row) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadSourceMap = async (url) => {
|
|
||||||
const response = await fetch(url);
|
|
||||||
if (!response.ok) throw new Error(`加载失败:${response.status}`);
|
|
||||||
return response.text();
|
|
||||||
};
|
|
||||||
|
|
||||||
function parseSmapJson(rawJson) {
|
function parseSmapJson(rawJson) {
|
||||||
// ── 解析 header ──
|
// ── 解析 header ──
|
||||||
const header = rawJson.header || rawJson; // 兼容 header 嵌套或平铺
|
const header = rawJson.header || rawJson; // 兼容 header 嵌套或平铺
|
||||||
@ -360,7 +356,6 @@ const init = () => {
|
|||||||
*/
|
*/
|
||||||
function resizeCanvasToContainer() {
|
function resizeCanvasToContainer() {
|
||||||
const containerRect = mapCanvas.parentElement.getBoundingClientRect(); // 获取容器实际尺寸
|
const containerRect = mapCanvas.parentElement.getBoundingClientRect(); // 获取容器实际尺寸
|
||||||
console.log('containerRect', containerRect)
|
|
||||||
devicePixelRatio = window.devicePixelRatio || 1; // 获取设备像素比
|
devicePixelRatio = window.devicePixelRatio || 1; // 获取设备像素比
|
||||||
applicationState.canvasWidthPx = containerRect.width; // 更新逻辑宽度
|
applicationState.canvasWidthPx = containerRect.width; // 更新逻辑宽度
|
||||||
applicationState.canvasHeightPx = containerRect.height; // 逻辑高度 = 容器高度 - 状态栏高度
|
applicationState.canvasHeightPx = containerRect.height; // 逻辑高度 = 容器高度 - 状态栏高度
|
||||||
@ -386,7 +381,7 @@ async function loadMapIntoApplication(parsedMap) {
|
|||||||
const worldHeightMeters = parsedMap.maxY - parsedMap.originY; // 地图世界高度
|
const worldHeightMeters = parsedMap.maxY - parsedMap.originY; // 地图世界高度
|
||||||
const fitScaleX = applicationState.canvasWidthPx / worldWidthMeters; // 水平方向恰好填满的缩放
|
const fitScaleX = applicationState.canvasWidthPx / worldWidthMeters; // 水平方向恰好填满的缩放
|
||||||
const fitScaleY = applicationState.canvasHeightPx / worldHeightMeters; // 垂直方向恰好填满的缩放
|
const fitScaleY = applicationState.canvasHeightPx / worldHeightMeters; // 垂直方向恰好填满的缩放
|
||||||
applicationState.camera.pixelsPerMeter = Math.max(1, Math.min(fitScaleX, fitScaleY)); // 取较小值并留12%边距
|
applicationState.camera.pixelsPerMeter = Math.max(1, Math.max(fitScaleX, fitScaleY)); // 取较小值并留12%边距
|
||||||
|
|
||||||
// 标记加载完成并更新UI
|
// 标记加载完成并更新UI
|
||||||
applicationState.isMapLoaded = true;
|
applicationState.isMapLoaded = true;
|
||||||
@ -443,10 +438,16 @@ const bindEvent = () => {
|
|||||||
*/
|
*/
|
||||||
const loadUserFile = async () => {
|
const loadUserFile = async () => {
|
||||||
try {
|
try {
|
||||||
const filemapDesc = await loadSourceMap('/3.smap');
|
const res = await getMapJson({
|
||||||
const rawJsonObject = JSON.parse(filemapDesc); // 解析 JSON
|
robotId: form.value.mapSourceRobotId,
|
||||||
const parsedMap = parseSmapJson(rawJsonObject); // 转换为内部结构
|
mapName: form.value.mapSourceName
|
||||||
await loadMapIntoApplication(parsedMap); // 加载到应用
|
});
|
||||||
|
if (res.code === 200) {
|
||||||
|
const rawJsonObject = JSON.parse(res.data); // 解析 JSON
|
||||||
|
const parsedMap = parseSmapJson(rawJsonObject); // 转换为内部结构
|
||||||
|
await loadMapIntoApplication(parsedMap); // 加载到应用
|
||||||
|
}
|
||||||
|
|
||||||
} catch (parseError) {
|
} catch (parseError) {
|
||||||
alert('Parse error: ' + parseError.message); // 显示错误信息
|
alert('Parse error: ' + parseError.message); // 显示错误信息
|
||||||
}
|
}
|
||||||
@ -530,13 +531,13 @@ const getRobotArr = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const getMapArr = (robotId) => {
|
const getMapArr = async (robotId) => {
|
||||||
// TODO: 这里可以根据 robotId 来过滤地图列表,目前接口没有提供这个功能,所以暂时获取全部地图
|
const res = await getMapByRobot(robotId)
|
||||||
mapList.value = [
|
if (res.code === 200) {
|
||||||
{ label: "地图1", value: "map1" },
|
mapList.value = res.data.map(item => {
|
||||||
{ label: "地图2", value: "map2" },
|
return { label: item, value: item }
|
||||||
{ label: "地图3", value: "map3" },
|
})
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const robotChange = (value) => {
|
const robotChange = (value) => {
|
||||||
|
|||||||
@ -26,7 +26,12 @@
|
|||||||
<div :style="containerHeight">
|
<div :style="containerHeight">
|
||||||
<el-table v-loading="loading" height="100%" style="width: 100%" :data="tableDataList">
|
<el-table v-loading="loading" height="100%" style="width: 100%" :data="tableDataList">
|
||||||
<el-table-column label="机器人名称" align="center" prop="robotName" />
|
<el-table-column label="机器人名称" align="center" prop="robotName" />
|
||||||
<el-table-column label="机器人类型" align="center" prop="robotType" />
|
<el-table-column label="机器人编码" align="center" prop="robotCode" />
|
||||||
|
<el-table-column label="机器人类型" align="center" prop="robotType">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.robotType == 1 ? '讲解机器人' : '巡检机器人' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="IP地址" align="center" prop="ipAddress" min-width="160" show-overflow-tooltip />
|
<el-table-column label="IP地址" align="center" prop="ipAddress" min-width="160" show-overflow-tooltip />
|
||||||
<el-table-column label="电量" align="center" prop="batteryLevel" />
|
<el-table-column label="电量" align="center" prop="batteryLevel" />
|
||||||
<el-table-column label="状态" align="center" prop="status" />
|
<el-table-column label="状态" align="center" prop="status" />
|
||||||
@ -49,6 +54,9 @@
|
|||||||
<el-form-item label="机器人名称" prop="robotName">
|
<el-form-item label="机器人名称" prop="robotName">
|
||||||
<el-input v-model="form.robotName" placeholder="请输入机器人名称" />
|
<el-input v-model="form.robotName" placeholder="请输入机器人名称" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="机器人编码" prop="robotCode">
|
||||||
|
<el-input v-model="form.robotCode" placeholder="请输入机器人编码" />
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="机器人类型" prop="robotType">
|
<el-form-item label="机器人类型" prop="robotType">
|
||||||
<el-select v-model="form.robotType" placeholder="请输入机器人类型" clearable style="width: 100%">
|
<el-select v-model="form.robotType" placeholder="请输入机器人类型" clearable style="width: 100%">
|
||||||
<el-option v-for="type in robot_type" :key="type.value" :label="type.label"
|
<el-option v-for="type in robot_type" :key="type.value" :label="type.label"
|
||||||
@ -126,6 +134,9 @@ const data = reactive({
|
|||||||
robotName: [
|
robotName: [
|
||||||
{ required: true, message: "请输入机器人名称", trigger: "change" },
|
{ required: true, message: "请输入机器人名称", trigger: "change" },
|
||||||
],
|
],
|
||||||
|
robotCode: [
|
||||||
|
{ required: true, message: "请输入机器人编码", trigger: "change" },
|
||||||
|
],
|
||||||
robotType: [
|
robotType: [
|
||||||
{ required: true, message: "请输入机器人类型", trigger: "change" },
|
{ required: true, message: "请输入机器人类型", trigger: "change" },
|
||||||
],
|
],
|
||||||
|
|||||||
@ -616,11 +616,6 @@ onUnmounted(() => {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.run-log-container {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user