web-map/src/pages/movement-supervision.vue

167 lines
4.8 KiB
Vue
Raw Normal View History

2025-05-22 18:06:30 +08:00
<script setup lang="ts">
2025-05-25 00:07:22 +08:00
import type { RobotRealtimeInfo } from '@api/robot';
import { getSceneByGroupId, getSceneById, monitorSceneById } from '@api/scene';
2025-05-22 18:06:30 +08:00
import { EditorService } from '@core/editor.service';
2025-05-25 00:07:22 +08:00
import { isNil } from 'lodash-es';
import { computed, onMounted, onUnmounted, provide, ref, shallowRef, watch } from 'vue';
2025-05-22 18:06:30 +08:00
const EDITOR_KEY = Symbol('editor-key');
type Props = {
sid: string;
id?: string;
};
const props = defineProps<Props>();
//#region 接口
const readScene = async () => {
const res = props.id ? await getSceneByGroupId(props.id, props.sid) : await getSceneById(props.sid);
title.value = res?.label ?? '';
editor.value?.load(res?.json);
};
2025-05-25 00:07:22 +08:00
const monitorScene = async () => {
client.value?.close();
const ws = await monitorSceneById(props.sid);
if (isNil(ws)) return;
ws.onmessage = (e) => {
const { id, x, y, active, angle, path, ...rest } = <RobotRealtimeInfo>JSON.parse(e.data || '{}');
if (!editor.value?.checkRobotById(id)) return;
editor.value?.updateRobot(id, rest);
if (isNil(x) || isNil(y)) {
editor.value.updatePen(id, { visible: false });
} else {
editor.value.refreshRobot(id, { x, y, active, angle, path });
}
};
client.value = ws;
};
2025-05-22 18:06:30 +08:00
//#endregion
const title = ref<string>('');
const container = shallowRef<HTMLDivElement>();
const editor = shallowRef<EditorService>();
provide(EDITOR_KEY, editor);
onMounted(() => {
editor.value = new EditorService(container.value!);
});
2025-05-25 00:07:22 +08:00
const client = shallowRef<WebSocket>();
onMounted(async () => {
await readScene();
await editor.value?.initRobots();
// await monitorScene();
const id = 'mock-robot-1';
let x = 800;
let y = 500;
const active = true;
const angle = 0;
const path = <[number, number][]>[
[600, 500],
// [100, 400],
];
editor.value?.refreshRobot(id, { x, y, active, angle, path });
const test = () =>
requestAnimationFrame(() => {
x -= 0.1;
editor.value?.refreshRobot(id, { x, y });
test();
});
// test();
});
onUnmounted(() => {
client.value?.close();
});
2025-05-22 18:06:30 +08:00
const show = ref<boolean>(true);
const current = ref<{ type: 'robot' | 'point' | 'line' | 'area'; id: string }>();
watch(
() => editor.value?.selected.value[0],
(v) => {
const pen = editor.value?.getPenById(v);
if (pen?.id) {
current.value = { type: <'point' | 'line' | 'area'>pen.name, id: pen.id };
return;
}
if (current.value?.type === 'robot') return;
current.value = undefined;
},
);
const isRobot = computed(() => current.value?.type === 'robot');
const isPoint = computed(() => current.value?.type === 'point');
const isRoute = computed(() => current.value?.type === 'line');
const isArea = computed(() => current.value?.type === 'area');
const selectRobot = (id: string) => {
current.value = { type: 'robot', id };
editor.value?.inactive();
};
</script>
<template>
<a-layout class="full">
<a-layout-header class="p-16" style="height: 64px">
<a-flex justify="space-between" align="center">
<a-typography-text class="title">{{ title }}</a-typography-text>
</a-flex>
</a-layout-header>
<a-layout class="p-16">
<a-layout-sider :width="320">
<a-tabs type="card">
<a-tab-pane key="1" :tab="$t('机器人')">
<RobotGroups v-if="editor" :token="EDITOR_KEY" :sid="sid" :current="current?.id" @change="selectRobot" />
</a-tab-pane>
<a-tab-pane key="2" :tab="$t('库区')">
<PenGroups v-if="editor" :token="EDITOR_KEY" :current="current?.id" only-area1 />
</a-tab-pane>
<a-tab-pane key="3" :tab="$t('高级组')">
<PenGroups v-if="editor" :token="EDITOR_KEY" :current="current?.id" />
</a-tab-pane>
</a-tabs>
</a-layout-sider>
<a-layout-content>
<div ref="container" class="editor-container full"></div>
</a-layout-content>
</a-layout>
</a-layout>
2025-05-25 00:07:22 +08:00
<template v-if="current?.id">
<a-float-button style="top: 80px; right: 16px" shape="square" @click="show = !show">
<template #icon><i class="icon detail" /></template>
</a-float-button>
<div v-if="show" class="card-container">
<RobotDetailCard v-if="isRobot" :token="EDITOR_KEY" :current="current.id" />
<PointDetailCard v-if="isPoint" :token="EDITOR_KEY" :current="current.id" />
<RouteDetailCard v-if="isRoute" :token="EDITOR_KEY" :current="current.id" />
<AreaDetailCard v-if="isArea" :token="EDITOR_KEY" :current="current.id" />
</div>
</template>
2025-05-22 18:06:30 +08:00
</template>
<style scoped lang="scss">
.editor-container {
background-color: transparent !important;
}
.toolbar-container {
position: fixed;
bottom: 40px;
left: 50%;
z-index: 100;
transform: translateX(-50%);
}
.card-container {
position: fixed;
top: 80px;
right: 64px;
z-index: 100;
width: 320px;
height: calc(100% - 96px);
overflow: visible;
}
</style>