feat: light
5
mocks/robot/syncByGroupId
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"code": 200,
|
||||
"success": true,
|
||||
"message": "模拟提示"
|
||||
}
|
@ -3,7 +3,37 @@
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "mock-group-scene-1",
|
||||
"label": "模拟组场景A"
|
||||
"label": "模拟组场景A",
|
||||
"group": {
|
||||
"sid": "mock-scene-1",
|
||||
"id": "mock-robot-group",
|
||||
"label": "模拟机器人组",
|
||||
"robots": [
|
||||
"mock-robot-1",
|
||||
"mock-robot-2"
|
||||
]
|
||||
},
|
||||
"robots": [
|
||||
{
|
||||
"id": "mock-robot-1",
|
||||
"label": "模拟机器人A",
|
||||
"brand": "模拟品牌A",
|
||||
"type": 1,
|
||||
"ip": "127.0.1.1",
|
||||
"isConnected": true,
|
||||
"state": 4,
|
||||
"canOrder": true,
|
||||
"canStop": true,
|
||||
"canControl": true
|
||||
},
|
||||
{
|
||||
"id": "mock-robot-2",
|
||||
"label": "模拟机器人B",
|
||||
"brand": "模拟品牌A",
|
||||
"type": 2,
|
||||
"ip": "127.0.1.2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"message": "模拟提示"
|
||||
}
|
||||
|
5
mocks/scene/saveByGroupId
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"code": 200,
|
||||
"success": false,
|
||||
"message": "模拟提示"
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import http from '@core/http';
|
||||
|
||||
import type { RobotDetail, RobotInfo } from './type';
|
||||
import type { RobotDetail, RobotGroup, RobotInfo } from './type';
|
||||
|
||||
const enum API {
|
||||
获取所有机器人 = '/robot/getAll',
|
||||
@ -8,6 +8,7 @@ const enum API {
|
||||
注册机器人 = '/robot/register',
|
||||
|
||||
批量抢占控制权 = '/robot/seizeByIds',
|
||||
同步组文件 = 'robot/syncByGroupId',
|
||||
}
|
||||
|
||||
export async function getAllRobots(): Promise<Array<RobotInfo>> {
|
||||
@ -47,3 +48,16 @@ export async function seizeRobotByIds(ids: Array<RobotInfo['id']>): Promise<Arra
|
||||
return [];
|
||||
}
|
||||
}
|
||||
export async function syncGroupRobotsById(id: RobotGroup['id'], sid: RobotGroup['sid']): Promise<boolean> {
|
||||
if (!id || !sid) return false;
|
||||
type B = { id: string; sid: string };
|
||||
type D = void;
|
||||
try {
|
||||
const body = { id, sid };
|
||||
await http.post<D, B>(API.同步组文件, body);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.debug(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
import type { RobotGroup } from '@api/robot';
|
||||
import http from '@core/http';
|
||||
|
||||
import type { SceneDetail, SceneInfo } from './type';
|
||||
import type { GroupSceneDetail, SceneDetail, SceneInfo } from './type';
|
||||
|
||||
const enum API {
|
||||
获取场景 = '/scene/getById',
|
||||
保存场景 = '/scene/saveById',
|
||||
|
||||
获取组场景 = '/scene/getByGroupId',
|
||||
保存组场景 = '/scene/saveByGroupId',
|
||||
}
|
||||
|
||||
export async function getSceneById(id: SceneInfo['id']): Promise<SceneDetail | null> {
|
||||
@ -38,16 +39,33 @@ export async function saveSceneById(id: SceneInfo['id'], json: string): Promise<
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSceneByGroupId(id: RobotGroup['id'], sid: RobotGroup['sid']): Promise<SceneDetail | null> {
|
||||
if (!id) return null;
|
||||
type B = { id: string; sid?: string };
|
||||
type D = SceneDetail;
|
||||
export async function getSceneByGroupId(
|
||||
id: RobotGroup['id'],
|
||||
sid: RobotGroup['sid'],
|
||||
): Promise<GroupSceneDetail | null> {
|
||||
if (!id || !sid) return null;
|
||||
type B = { id: string; sid: string };
|
||||
type D = GroupSceneDetail;
|
||||
try {
|
||||
const body = { id, sid };
|
||||
const data = await http.post<D, B>(API.获取场景, body);
|
||||
const data = await http.post<D, B>(API.获取组场景, body);
|
||||
return data ?? null;
|
||||
} catch (error) {
|
||||
console.debug(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveSceneByGroupId(id: RobotGroup['id'], sid: RobotGroup['sid'], json: string): Promise<boolean> {
|
||||
if (!id || !sid) return false;
|
||||
type B = { id: string; sid: string; json: string };
|
||||
type D = void;
|
||||
try {
|
||||
const body = { id, sid, json };
|
||||
await http.post<D, B>(API.保存组场景, body);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.debug(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,10 @@ export interface SceneInfo {
|
||||
export interface SceneDetail extends SceneInfo {
|
||||
json?: string; // 场景JSON
|
||||
}
|
||||
export interface GroupSceneDetail extends SceneDetail {
|
||||
group: RobotGroup;
|
||||
robots?: Array<RobotInfo>;
|
||||
}
|
||||
|
||||
export interface SceneData extends Meta2dData {
|
||||
robots?: Array<RobotInfo>;
|
||||
|
BIN
src/assets/icons/light/area1-active.png
Normal file
After Width: | Height: | Size: 578 B |
BIN
src/assets/icons/light/area1-detail.png
Normal file
After Width: | Height: | Size: 568 B |
BIN
src/assets/icons/light/area1.png
Normal file
After Width: | Height: | Size: 536 B |
BIN
src/assets/icons/light/area11-active.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/icons/light/area11-detail.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
src/assets/icons/light/area11.png
Normal file
After Width: | Height: | Size: 1003 B |
BIN
src/assets/icons/light/area12-active.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
src/assets/icons/light/area12-detail.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
src/assets/icons/light/area12.png
Normal file
After Width: | Height: | Size: 970 B |
BIN
src/assets/icons/light/battery.png
Normal file
After Width: | Height: | Size: 473 B |
BIN
src/assets/icons/light/battery_charge.png
Normal file
After Width: | Height: | Size: 699 B |
BIN
src/assets/icons/light/connect_off.png
Normal file
After Width: | Height: | Size: 980 B |
BIN
src/assets/icons/light/connect_on.png
Normal file
After Width: | Height: | Size: 782 B |
BIN
src/assets/icons/light/control.png
Normal file
After Width: | Height: | Size: 903 B |
BIN
src/assets/icons/light/detail.png
Normal file
After Width: | Height: | Size: 580 B |
BIN
src/assets/icons/light/dot.png
Normal file
After Width: | Height: | Size: 328 B |
BIN
src/assets/icons/light/dropdown.png
Normal file
After Width: | Height: | Size: 305 B |
BIN
src/assets/icons/light/edit.png
Normal file
After Width: | Height: | Size: 643 B |
BIN
src/assets/icons/light/edit_group.png
Normal file
After Width: | Height: | Size: 688 B |
BIN
src/assets/icons/light/exit.png
Normal file
After Width: | Height: | Size: 484 B |
BIN
src/assets/icons/light/pen.png
Normal file
After Width: | Height: | Size: 601 B |
BIN
src/assets/icons/light/plus.png
Normal file
After Width: | Height: | Size: 314 B |
BIN
src/assets/icons/light/point.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/icons/light/redo.png
Normal file
After Width: | Height: | Size: 567 B |
BIN
src/assets/icons/light/register.png
Normal file
After Width: | Height: | Size: 637 B |
BIN
src/assets/icons/light/robot.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
src/assets/icons/light/route.png
Normal file
After Width: | Height: | Size: 795 B |
BIN
src/assets/icons/light/save.png
Normal file
After Width: | Height: | Size: 644 B |
BIN
src/assets/icons/light/search.png
Normal file
After Width: | Height: | Size: 607 B |
BIN
src/assets/icons/light/trash.png
Normal file
After Width: | Height: | Size: 759 B |
BIN
src/assets/icons/light/trash_fill.png
Normal file
After Width: | Height: | Size: 719 B |
BIN
src/assets/icons/light/undo.png
Normal file
After Width: | Height: | Size: 559 B |
BIN
src/assets/images/empty-light.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
@ -1 +1,31 @@
|
||||
{}
|
||||
{
|
||||
"color": "#595959",
|
||||
"background": "#F0F2F5",
|
||||
"point-s": {
|
||||
"stroke": "#8C8C8C",
|
||||
"strokeActive": "#EBB214",
|
||||
"fill-1": "#14D1A5",
|
||||
"fill-2": "#69C6F5",
|
||||
"fill-3": "#E48B1D",
|
||||
"fill-4": "#E48B1D"
|
||||
},
|
||||
"point-l": {
|
||||
"stroke": "#595959",
|
||||
"strokeActive": "#EBB214"
|
||||
},
|
||||
"route": {
|
||||
"stroke-0": "#8C8C8C",
|
||||
"stroke-1": "#52C41A",
|
||||
"stroke-2": "#FAAD14",
|
||||
"stroke-10": "#E63A3A"
|
||||
},
|
||||
"area": {
|
||||
"stroke-1": "#9ACDFF99",
|
||||
"fill-1": "#9ACDFF33",
|
||||
"stroke-11": "#FF535399",
|
||||
"fill-11": "#FF9A9A33",
|
||||
"stroke-12": "#0DBB8A99",
|
||||
"fill-12": "#0DBB8A33",
|
||||
"strokeActive": "#EBB214"
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { MapAreaType } from '@api/map';
|
||||
import { saveSceneById } from '@api/scene';
|
||||
import { saveSceneByGroupId, saveSceneById } from '@api/scene';
|
||||
import type { EditorService } from '@core/editor.service';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
import { inject, type InjectionKey, ref, type ShallowRef, watch } from 'vue';
|
||||
@ -8,6 +8,7 @@ import { inject, type InjectionKey, ref, type ShallowRef, watch } from 'vue';
|
||||
type Props = {
|
||||
token: InjectionKey<ShallowRef<EditorService>>;
|
||||
editable?: boolean;
|
||||
sid?: string;
|
||||
id: string;
|
||||
};
|
||||
const props = defineProps<Props>();
|
||||
@ -17,7 +18,11 @@ const editor = inject(props.token)!;
|
||||
const updateScene = async () => {
|
||||
const json = editor.value.save();
|
||||
if (!json) return;
|
||||
await saveSceneById(props.id, json);
|
||||
if (props.sid) {
|
||||
await saveSceneByGroupId(props.id, props.sid, json);
|
||||
} else {
|
||||
await saveSceneById(props.id, json);
|
||||
}
|
||||
};
|
||||
//#endregion
|
||||
|
||||
|
@ -1,14 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { type RobotGroup, type RobotInfo, seizeRobotByIds } from '@api/robot';
|
||||
import type { RobotAddModalRef } from '@common/modal/robot-add-modal.vue';
|
||||
import type { RobotGroupRenameModalRef } from '@common/modal/robot-group-rename-modal.vue';
|
||||
import type { RobotRegisterModalRef } from '@common/modal/robot-register-modal.vue';
|
||||
import type { EditorService } from '@core/editor.service';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { watch } from 'vue';
|
||||
import { reactive } from 'vue';
|
||||
import { computed, inject, type InjectionKey, ref, type ShallowRef, shallowRef } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { computed, inject, type InjectionKey, ref, type ShallowRef } from 'vue';
|
||||
|
||||
type Props = {
|
||||
token: InjectionKey<ShallowRef<EditorService>>;
|
||||
@ -23,8 +18,6 @@ type Events = {
|
||||
};
|
||||
const emit = defineEmits<Events>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
//#region 接口
|
||||
const seizeRobots = async () => {
|
||||
const res = await seizeRobotByIds([...selected.keys()]);
|
||||
@ -35,10 +28,8 @@ const seizeRobots = async () => {
|
||||
const keyword = ref<string>('');
|
||||
|
||||
//#region 机器人列表
|
||||
const groups = computed<RobotGroup[]>(() => editor.value.robotGroups.value ?? []);
|
||||
const group = computed<RobotGroup | undefined>(() => editor.value.robotGroups.value?.[0]);
|
||||
const robots = computed<RobotInfo[]>(() => editor.value.robots.filter(({ label }) => label.includes(keyword.value)));
|
||||
const getGroupRobots = (ids: RobotGroup['robots']) =>
|
||||
ids?.map((id) => editor.value.getRobotById(id)).filter((robot) => robot?.label.includes(keyword.value));
|
||||
|
||||
const selected = reactive<Set<RobotInfo['id']>>(new Set());
|
||||
watch(
|
||||
@ -46,12 +37,12 @@ watch(
|
||||
() => selected.clear(),
|
||||
);
|
||||
|
||||
const checkGroupSelected = (ids: RobotGroup['robots']) => !!ids?.length && ids.every((id) => selected.has(id));
|
||||
const selectGroup = (ids: RobotGroup['robots'], checked: boolean) => {
|
||||
const isAllSelected = computed<boolean>(() => selected.size > 0 && robots.value.every(({ id }) => selected.has(id)));
|
||||
const selectAll = (checked: boolean) => {
|
||||
if (checked) {
|
||||
ids?.forEach((id) => selected.add(id));
|
||||
robots.value.forEach(({ id }) => selected.add(id));
|
||||
} else {
|
||||
ids?.forEach((id) => selected.delete(id));
|
||||
selected.clear();
|
||||
}
|
||||
};
|
||||
|
||||
@ -63,57 +54,9 @@ const selectRobot = (id: RobotInfo['id'], checked: boolean) => {
|
||||
}
|
||||
};
|
||||
//#endregion
|
||||
|
||||
//#region 机器人组操作
|
||||
const refAddRobot = shallowRef<RobotAddModalRef>();
|
||||
const refRegisterRobot = shallowRef<RobotRegisterModalRef>();
|
||||
const refRenameGroup = shallowRef<RobotGroupRenameModalRef>();
|
||||
|
||||
const toDeleteGroup = (id: RobotGroup['id']) =>
|
||||
Modal.confirm({
|
||||
class: 'confirm',
|
||||
title: t('您确定要删除该机器人组吗?'),
|
||||
centered: true,
|
||||
cancelText: t('返回'),
|
||||
okText: t('删除'),
|
||||
onOk: () => editor.value.deleteRobotGroup(id),
|
||||
});
|
||||
//#endregion
|
||||
|
||||
//#region 机器人操作
|
||||
const toRemoveRobot = (id: RobotInfo['id']) =>
|
||||
Modal.confirm({
|
||||
class: 'confirm',
|
||||
title: t('您确定要从场景中移除该机器人吗?'),
|
||||
centered: true,
|
||||
cancelText: t('返回'),
|
||||
okText: t('移除'),
|
||||
onOk: () => {
|
||||
editor.value.removeRobots([id]);
|
||||
selected.delete(id);
|
||||
},
|
||||
});
|
||||
|
||||
const toRemoveRobots = () =>
|
||||
Modal.confirm({
|
||||
class: 'confirm',
|
||||
title: t('您确定要从场景中移除已选机器人吗?'),
|
||||
centered: true,
|
||||
cancelText: t('返回'),
|
||||
okText: t('移除'),
|
||||
onOk: () => {
|
||||
editor.value.removeRobots([...selected.keys()]);
|
||||
selected.clear();
|
||||
},
|
||||
});
|
||||
//#endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RobotAddModal ref="refAddRobot" :token="token" />
|
||||
<RobotRegisterModal ref="refRegisterRobot" :token="token" />
|
||||
<RobotGroupRenameModal ref="refRenameGroup" :token="token" />
|
||||
|
||||
<a-flex class="full" vertical>
|
||||
<a-input class="search mb-16" :placeholder="$t('请输入搜索关键字')" v-model:value="keyword">
|
||||
<template #suffix>
|
||||
@ -121,27 +64,16 @@ const toRemoveRobots = () =>
|
||||
</template>
|
||||
</a-input>
|
||||
|
||||
<a-flex v-if="editable" class="mb-8" style="height: 32px" justify="space-between" align="center">
|
||||
<a-space align="center">
|
||||
<a-button
|
||||
class="icon-btn panel-btn"
|
||||
:title="$t('抢占控制权')"
|
||||
size="small"
|
||||
@click="seizeRobots"
|
||||
:disabled="!selected.size"
|
||||
>
|
||||
<i class="mask control" />
|
||||
</a-button>
|
||||
<a-button
|
||||
class="icon-btn panel-btn"
|
||||
:title="$t('移除机器人')"
|
||||
size="small"
|
||||
@click="toRemoveRobots"
|
||||
:disabled="!selected.size"
|
||||
>
|
||||
<i class="mask trash_fill" />
|
||||
</a-button>
|
||||
</a-space>
|
||||
<a-flex v-if="editable" class="mb-8" style="height: 32px" justify="end" align="center">
|
||||
<a-button
|
||||
class="icon-btn panel-btn"
|
||||
:title="$t('抢占控制权')"
|
||||
size="small"
|
||||
@click="seizeRobots"
|
||||
:disabled="!selected.size"
|
||||
>
|
||||
<i class="mask control" />
|
||||
</a-button>
|
||||
</a-flex>
|
||||
|
||||
<a-collapse style="flex: auto; overflow-y: auto" expand-icon-position="end" ghost>
|
||||
@ -149,72 +81,26 @@ const toRemoveRobots = () =>
|
||||
<i class="icon dropdown" :class="{ active: v?.isActive }" />
|
||||
</template>
|
||||
|
||||
<a-collapse-panel v-for="{ id, label, robots } in groups" :key="id">
|
||||
<template v-if="editable" #extra>
|
||||
<a-dropdown placement="bottomRight">
|
||||
<a-button class="icon-btn" size="small" @click.stop>
|
||||
<i class="icon dot" />
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item>
|
||||
<a-space align="center" :size="4" @click="refAddRobot?.open(id)">
|
||||
<i class="icon plus size-20" />
|
||||
<span>{{ $t('添加机器人') }}</span>
|
||||
</a-space>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a-space align="center" :size="4" @click="refRegisterRobot?.open(id)">
|
||||
<i class="icon register size-20" />
|
||||
<span>{{ $t('注册机器人') }}</span>
|
||||
</a-space>
|
||||
</a-menu-item>
|
||||
<a-menu-item @click="refRenameGroup?.open(id, label)">
|
||||
<a-space align="center" :size="4">
|
||||
<i class="icon pen size-20" />
|
||||
<span>{{ $t('修改组名称') }}</span>
|
||||
</a-space>
|
||||
</a-menu-item>
|
||||
<a-menu-item @click="toDeleteGroup(id)">
|
||||
<a-space align="center" :size="4">
|
||||
<i class="icon trash size-20" />
|
||||
<span>{{ $t('删除组') }}</span>
|
||||
</a-space>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<a-collapse-panel>
|
||||
<template #header>
|
||||
<a-space align="center" :size="8">
|
||||
<a-checkbox
|
||||
v-if="editable"
|
||||
:checked="checkGroupSelected(robots)"
|
||||
@change="selectGroup(robots, $event.target.checked)"
|
||||
:checked="isAllSelected"
|
||||
@change="selectAll($event.target.checked)"
|
||||
@click.stop
|
||||
/>
|
||||
<span>{{ label }}</span>
|
||||
<span>{{ group?.label }}</span>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<a-list rowKey="id" :data-source="getGroupRobots(robots)">
|
||||
<a-list rowKey="id" :data-source="robots">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item
|
||||
:class="{ 'ph-16': !editable, 'pl-12': editable, 'pr-8': editable, selected: item.id === current }"
|
||||
style="height: 36px"
|
||||
@click="emit('change', item.id)"
|
||||
>
|
||||
<template v-if="editable" #actions>
|
||||
<a-button
|
||||
class="icon-btn panel-btn"
|
||||
:title="$t('移除机器人')"
|
||||
size="small"
|
||||
@click.stop="toRemoveRobot(item.id)"
|
||||
>
|
||||
<i class="icon trash_fill" />
|
||||
</a-button>
|
||||
</template>
|
||||
<a-space align="center" :size="8">
|
||||
<a-checkbox
|
||||
v-if="editable"
|
||||
|
@ -6,5 +6,3 @@ defineProps<Props>();
|
||||
</script>
|
||||
|
||||
<template>{{ code }}</template>
|
||||
|
||||
<!-- <style scoped lang="scss"></style> -->
|
||||
|
@ -1,8 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { getSceneByGroupId } from '@api/scene';
|
||||
import { syncGroupRobotsById } from '@api/robot';
|
||||
import { getSceneByGroupId, type GroupSceneDetail } from '@api/scene';
|
||||
import { EditorService } from '@core/editor.service';
|
||||
import { decodeTextFile, downloadFile, selectFile, textToBlob } from '@core/utils';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { computed, onMounted, provide, ref, shallowRef, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const EDITOR_KEY = Symbol('editor-key');
|
||||
|
||||
@ -12,14 +15,22 @@ type Props = {
|
||||
};
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
//#region 接口
|
||||
const readScene = async () => {
|
||||
const res = await getSceneByGroupId(props.id, props.sid);
|
||||
detail.value = res ?? undefined;
|
||||
title.value = res?.label ?? '';
|
||||
editor.value?.load(res?.json, editable.value);
|
||||
editor.value?.load(res?.json, editable.value, detail.value ?? {});
|
||||
};
|
||||
|
||||
const syncGroup = async () => {
|
||||
await syncGroupRobotsById(props.id, props.sid);
|
||||
};
|
||||
//#endregion
|
||||
|
||||
const detail = ref<GroupSceneDetail>();
|
||||
const title = ref<string>('');
|
||||
watch(
|
||||
() => props.id,
|
||||
@ -37,11 +48,22 @@ onMounted(() => {
|
||||
const editable = ref<boolean>(false);
|
||||
watch(editable, (v) => editor.value?.setState(v));
|
||||
|
||||
const toSync = () =>
|
||||
Modal.confirm({
|
||||
class: 'confirm',
|
||||
title: t('您确定要同步组文件至机器人吗?'),
|
||||
content: t('请确保当前场景已经保存,否则可能导致同步的文件版本不正确。'),
|
||||
centered: true,
|
||||
cancelText: t('返回'),
|
||||
okText: t('同步'),
|
||||
onOk: () => syncGroup(),
|
||||
});
|
||||
|
||||
const importScene = async () => {
|
||||
const file = await selectFile('.scene');
|
||||
if (!file?.size) return;
|
||||
const json = await decodeTextFile(file);
|
||||
editor.value?.load(json, editable.value);
|
||||
editor.value?.load(json, editable.value, detail.value ?? {});
|
||||
};
|
||||
const exportScene = () => {
|
||||
const json = editor.value?.save();
|
||||
@ -92,7 +114,7 @@ const selectRobot = (id: string) => {
|
||||
<i class="icon edit size-18 mr-8" />
|
||||
<span>{{ $t('启用编辑器') }}</span>
|
||||
</a-button>
|
||||
<a-button>{{ $t('同步') }}</a-button>
|
||||
<a-button @click="toSync">{{ $t('同步') }}</a-button>
|
||||
<a-button @click="importScene">{{ $t('导入') }}</a-button>
|
||||
<a-button @click="exportScene">{{ $t('导出') }}</a-button>
|
||||
</a-space>
|
||||
|
@ -93,7 +93,6 @@ const selectRobot = (id: string) => {
|
||||
<i class="icon edit size-18 mr-8" />
|
||||
<span>{{ $t('启用编辑器') }}</span>
|
||||
</a-button>
|
||||
<a-button>{{ $t('推送') }}</a-button>
|
||||
<a-button @click="importScene">{{ $t('导入') }}</a-button>
|
||||
<a-button @click="exportScene">{{ $t('导出') }}</a-button>
|
||||
</a-space>
|
||||
|
@ -12,26 +12,24 @@ import {
|
||||
type Point,
|
||||
} from '@api/map';
|
||||
import type { RobotGroup, RobotInfo } from '@api/robot';
|
||||
import type { SceneData } from '@api/scene';
|
||||
import type { GroupSceneDetail, SceneData } from '@api/scene';
|
||||
import sTheme from '@core/theme.service';
|
||||
import { CanvasLayer, EditType, LockState, Meta2d, type Meta2dStore, type Pen, s8 } from '@meta2d/core';
|
||||
import { CanvasLayer, LockState, Meta2d, type Meta2dStore, type Pen, s8 } from '@meta2d/core';
|
||||
import { useObservable } from '@vueuse/rxjs';
|
||||
import { clone, cloneDeep, get, isNil, isString, mapKeys, pick, remove, some } from 'lodash-es';
|
||||
import { clone, get, isNil, isString, pick, remove, some } from 'lodash-es';
|
||||
import { BehaviorSubject, debounceTime, filter, map, Subject, switchMap } from 'rxjs';
|
||||
import { reactive, watch } from 'vue';
|
||||
|
||||
export class EditorService extends Meta2d {
|
||||
public async load(map?: string, editable = false): Promise<void> {
|
||||
const data = map ? JSON.parse(map) : undefined;
|
||||
public async load(map?: string, editable = false, detail?: Partial<GroupSceneDetail>): Promise<void> {
|
||||
let data = map ? JSON.parse(map) : undefined;
|
||||
if (!isNil(detail)) {
|
||||
data ??= this.data();
|
||||
data.robotGroups = [detail.group];
|
||||
data.robots = detail.robots;
|
||||
}
|
||||
this.open(data);
|
||||
this.setState(editable);
|
||||
|
||||
setTimeout(() => {
|
||||
const pens = this.data().pens;
|
||||
this.addRoute([pens[4].id, pens[9].id]);
|
||||
|
||||
// this.addRoute(['21b74c90', '7f201a25'], 'bezier3');
|
||||
}, 1000);
|
||||
}
|
||||
public save(): string {
|
||||
const data = this.data();
|
||||
@ -401,10 +399,7 @@ export class EditorService extends Meta2d {
|
||||
pens?.forEach((pen) => {
|
||||
switch (pen.name) {
|
||||
case 'point':
|
||||
{
|
||||
const lines = this.getLines(pen);
|
||||
this.delete(lines, true, false);
|
||||
}
|
||||
this.delete(this.getLines(pen), true, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|