temp
This commit is contained in:
parent
597967497d
commit
0cb69e4b43
9
mocks/scene/getByGroupId
Normal file
9
mocks/scene/getByGroupId
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"id": "mock-group-scene-1",
|
||||||
|
"label": "模拟组场景A"
|
||||||
|
},
|
||||||
|
"message": "模拟提示"
|
||||||
|
}
|
@ -466,7 +466,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
& > .ant-modal-confirm-content {
|
& > .ant-modal-confirm-content {
|
||||||
margin: 0;
|
margin: 0 0 0 38px;
|
||||||
|
font: 400 14px/22px Roboto;
|
||||||
|
color: get-color(text1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -733,6 +735,10 @@
|
|||||||
color: get-color(text4);
|
color: get-color(text4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.title {
|
||||||
|
font: 500 16px/22px SourceHanSansSC;
|
||||||
|
}
|
||||||
|
|
||||||
&.card-title {
|
&.card-title {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import type { RobotBrand, RobotState, RobotType } from './constant';
|
import type { RobotBrand, RobotState, RobotType } from './constant';
|
||||||
|
|
||||||
export interface RobotGroup {
|
export interface RobotGroup {
|
||||||
|
sid?: string; // 场景id
|
||||||
id: string; // 机器人组id
|
id: string; // 机器人组id
|
||||||
label: string; // 机器人组名称
|
label: string; // 机器人组名称
|
||||||
robots?: Array<string>; // 机器人列表
|
robots?: Array<string>; // 机器人列表
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import type { RobotGroup } from '@api/robot';
|
||||||
import http from '@core/http';
|
import http from '@core/http';
|
||||||
|
|
||||||
import type { SceneDetail, SceneInfo } from './type';
|
import type { SceneDetail, SceneInfo } from './type';
|
||||||
@ -5,6 +6,8 @@ import type { SceneDetail, SceneInfo } from './type';
|
|||||||
const enum API {
|
const enum API {
|
||||||
获取场景 = '/scene/getById',
|
获取场景 = '/scene/getById',
|
||||||
保存场景 = '/scene/saveById',
|
保存场景 = '/scene/saveById',
|
||||||
|
|
||||||
|
获取组场景 = '/scene/getByGroupId',
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getSceneById(id: SceneInfo['id']): Promise<SceneDetail | null> {
|
export async function getSceneById(id: SceneInfo['id']): Promise<SceneDetail | null> {
|
||||||
@ -34,3 +37,17 @@ export async function saveSceneById(id: SceneInfo['id'], json: string): Promise<
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
try {
|
||||||
|
const body = { id, sid };
|
||||||
|
const data = await http.post<D, B>(API.获取场景, body);
|
||||||
|
return data ?? null;
|
||||||
|
} catch (error) {
|
||||||
|
console.debug(error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,6 +16,7 @@ $icons: (
|
|||||||
detail,
|
detail,
|
||||||
dot,
|
dot,
|
||||||
dropdown,
|
dropdown,
|
||||||
|
edit_group,
|
||||||
edit,
|
edit,
|
||||||
exit,
|
exit,
|
||||||
pen,
|
pen,
|
||||||
|
BIN
src/assets/icons/dark/edit_group.png
Normal file
BIN
src/assets/icons/dark/edit_group.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 663 B |
244
src/components/robot-group.vue
Normal file
244
src/components/robot-group.vue
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
<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';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
token: InjectionKey<ShallowRef<EditorService>>;
|
||||||
|
editable?: boolean;
|
||||||
|
current?: string;
|
||||||
|
};
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
const editor = inject(props.token)!;
|
||||||
|
|
||||||
|
type Events = {
|
||||||
|
(e: 'change', id: string): void;
|
||||||
|
};
|
||||||
|
const emit = defineEmits<Events>();
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
//#region 接口
|
||||||
|
const seizeRobots = async () => {
|
||||||
|
const res = await seizeRobotByIds([...selected.keys()]);
|
||||||
|
editor.value.updateRobots(res, { canControl: true });
|
||||||
|
};
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
const keyword = ref<string>('');
|
||||||
|
|
||||||
|
//#region 机器人列表
|
||||||
|
const groups = computed<RobotGroup[]>(() => editor.value.robotGroups.value ?? []);
|
||||||
|
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(
|
||||||
|
() => props.editable,
|
||||||
|
() => selected.clear(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const checkGroupSelected = (ids: RobotGroup['robots']) => !!ids?.length && ids.every((id) => selected.has(id));
|
||||||
|
const selectGroup = (ids: RobotGroup['robots'], checked: boolean) => {
|
||||||
|
if (checked) {
|
||||||
|
ids?.forEach((id) => selected.add(id));
|
||||||
|
} else {
|
||||||
|
ids?.forEach((id) => selected.delete(id));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectRobot = (id: RobotInfo['id'], checked: boolean) => {
|
||||||
|
if (checked) {
|
||||||
|
selected.add(id);
|
||||||
|
} else {
|
||||||
|
selected.delete(id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//#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>
|
||||||
|
<i class="icon search size-16" />
|
||||||
|
</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>
|
||||||
|
|
||||||
|
<a-collapse style="flex: auto; overflow-y: auto" expand-icon-position="end" ghost>
|
||||||
|
<template #expandIcon="v">
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<template #header>
|
||||||
|
<a-space align="center" :size="8">
|
||||||
|
<a-checkbox
|
||||||
|
v-if="editable"
|
||||||
|
:checked="checkGroupSelected(robots)"
|
||||||
|
@change="selectGroup(robots, $event.target.checked)"
|
||||||
|
@click.stop
|
||||||
|
/>
|
||||||
|
<span>{{ label }}</span>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<a-list rowKey="id" :data-source="getGroupRobots(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"
|
||||||
|
:checked="selected.has(item.id)"
|
||||||
|
@change="selectRobot(item.id, $event.target.checked)"
|
||||||
|
@click.stop
|
||||||
|
/>
|
||||||
|
<a-typography-text type="secondary">{{ item.label }}</a-typography-text>
|
||||||
|
</a-space>
|
||||||
|
</a-list-item>
|
||||||
|
</template>
|
||||||
|
</a-list>
|
||||||
|
</a-collapse-panel>
|
||||||
|
</a-collapse>
|
||||||
|
</a-flex>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.open-group-btn {
|
||||||
|
margin-right: -8px;
|
||||||
|
visibility: hidden;
|
||||||
|
|
||||||
|
.ant-collapse-header:hover & {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -9,9 +9,11 @@ import { watch } from 'vue';
|
|||||||
import { reactive } from 'vue';
|
import { reactive } from 'vue';
|
||||||
import { computed, inject, type InjectionKey, ref, type ShallowRef, shallowRef } from 'vue';
|
import { computed, inject, type InjectionKey, ref, type ShallowRef, shallowRef } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
token: InjectionKey<ShallowRef<EditorService>>;
|
token: InjectionKey<ShallowRef<EditorService>>;
|
||||||
|
sid: string;
|
||||||
editable?: boolean;
|
editable?: boolean;
|
||||||
current?: string;
|
current?: string;
|
||||||
};
|
};
|
||||||
@ -24,6 +26,7 @@ type Events = {
|
|||||||
const emit = defineEmits<Events>();
|
const emit = defineEmits<Events>();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
//#region 接口
|
//#region 接口
|
||||||
const seizeRobots = async () => {
|
const seizeRobots = async () => {
|
||||||
@ -87,6 +90,17 @@ const toDeleteGroup = (id: RobotGroup['id']) =>
|
|||||||
okText: t('删除'),
|
okText: t('删除'),
|
||||||
onOk: () => editor.value.deleteRobotGroup(id),
|
onOk: () => editor.value.deleteRobotGroup(id),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const toEditGroup = (id: RobotGroup['id']) =>
|
||||||
|
Modal.confirm({
|
||||||
|
class: 'confirm',
|
||||||
|
title: t('您确定要编辑该机器人组吗?'),
|
||||||
|
content: t('请确保当前场景已经保存,否则将导致当前场景数据丢失。'),
|
||||||
|
centered: true,
|
||||||
|
cancelText: t('取消'),
|
||||||
|
okText: t('编辑'),
|
||||||
|
onOk: () => router.push({ name: '组编辑', params: { sid: props.sid, id } }),
|
||||||
|
});
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region 机器人操作
|
//#region 机器人操作
|
||||||
@ -197,6 +211,7 @@ const toRemoveRobots = () =>
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #header>
|
<template #header>
|
||||||
|
<a-flex justify="space-between" align="center">
|
||||||
<a-space align="center" :size="8">
|
<a-space align="center" :size="8">
|
||||||
<a-checkbox
|
<a-checkbox
|
||||||
v-if="editable"
|
v-if="editable"
|
||||||
@ -206,6 +221,10 @@ const toRemoveRobots = () =>
|
|||||||
/>
|
/>
|
||||||
<span>{{ label }}</span>
|
<span>{{ label }}</span>
|
||||||
</a-space>
|
</a-space>
|
||||||
|
<a-button v-if="!editable" class="open-group-btn icon-btn" size="small" @click.stop="toEditGroup(id)">
|
||||||
|
<i class="icon edit_group" />
|
||||||
|
</a-button>
|
||||||
|
</a-flex>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<a-list rowKey="id" :data-source="getGroupRobots(robots)">
|
<a-list rowKey="id" :data-source="getGroupRobots(robots)">
|
||||||
@ -246,3 +265,14 @@ const toRemoveRobots = () =>
|
|||||||
</a-button>
|
</a-button>
|
||||||
</a-flex>
|
</a-flex>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.open-group-btn {
|
||||||
|
margin-right: -8px;
|
||||||
|
visibility: hidden;
|
||||||
|
|
||||||
|
.ant-collapse-header:hover & {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -1,10 +1,177 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { getSceneByGroupId } from '@api/scene';
|
||||||
|
import { EditorService } from '@core/editor.service';
|
||||||
|
import { decodeTextFile, downloadFile, selectFile, textToBlob } from '@core/utils';
|
||||||
|
import { computed, onMounted, provide, ref, shallowRef, watch } from 'vue';
|
||||||
|
|
||||||
|
const EDITOR_KEY = Symbol('editor-key');
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
sid: string;
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
//#region 接口
|
||||||
|
const readScene = async () => {
|
||||||
|
const res = await getSceneByGroupId(props.id, props.sid);
|
||||||
|
title.value = res?.label ?? '';
|
||||||
|
editor.value?.load(res?.json, editable.value);
|
||||||
|
};
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
const title = ref<string>('');
|
||||||
|
watch(
|
||||||
|
() => props.id,
|
||||||
|
() => readScene(),
|
||||||
|
{ immediate: true, flush: 'post' },
|
||||||
|
);
|
||||||
|
|
||||||
|
const container = shallowRef<HTMLDivElement>();
|
||||||
|
const editor = shallowRef<EditorService>();
|
||||||
|
provide(EDITOR_KEY, editor);
|
||||||
|
onMounted(() => {
|
||||||
|
editor.value = new EditorService(container.value!);
|
||||||
|
});
|
||||||
|
|
||||||
|
const editable = ref<boolean>(false);
|
||||||
|
watch(editable, (v) => editor.value?.setState(v));
|
||||||
|
|
||||||
|
const importScene = async () => {
|
||||||
|
const file = await selectFile('.scene');
|
||||||
|
if (!file?.size) return;
|
||||||
|
const json = await decodeTextFile(file);
|
||||||
|
editor.value?.load(json, editable.value);
|
||||||
|
};
|
||||||
|
const exportScene = () => {
|
||||||
|
const json = editor.value?.save();
|
||||||
|
if (!json) return;
|
||||||
|
const blob = textToBlob(json);
|
||||||
|
if (!blob?.size) return;
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
downloadFile(url, `${title.value || 'unknown'}.scene`);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
};
|
||||||
|
|
||||||
|
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>
|
</script>
|
||||||
|
|
||||||
<template>组编辑器</template>
|
<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-space align="center">
|
||||||
|
<a-button v-if="editable" class="warning" @click="editable = false">
|
||||||
|
<i class="icon exit size-18 mr-8" />
|
||||||
|
<span>{{ $t('退出编辑器') }}</span>
|
||||||
|
</a-button>
|
||||||
|
<a-button v-else type="primary" @click="editable = true">
|
||||||
|
<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>
|
||||||
|
</a-flex>
|
||||||
|
</a-layout-header>
|
||||||
|
|
||||||
<!-- <style scoped lang="scss"></style> -->
|
<a-layout class="p-16">
|
||||||
|
<a-layout-sider :width="320">
|
||||||
|
<a-tabs type="card">
|
||||||
|
<a-tab-pane key="1" :tab="$t('机器人')">
|
||||||
|
<RobotGroup
|
||||||
|
v-if="editor"
|
||||||
|
:token="EDITOR_KEY"
|
||||||
|
:editable="editable"
|
||||||
|
: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>
|
||||||
|
|
||||||
|
<div v-if="editable" class="toolbar-container">
|
||||||
|
<EditorToolbar v-if="editor" :token="EDITOR_KEY" :id="id" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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" />
|
||||||
|
|
||||||
|
<template v-if="isPoint">
|
||||||
|
<PointEditCard v-if="editable" :token="EDITOR_KEY" :id="current.id" />
|
||||||
|
<PointDetailCard v-else :token="EDITOR_KEY" :current="current.id" />
|
||||||
|
</template>
|
||||||
|
<template v-if="isRoute">
|
||||||
|
<RouteEditCard v-if="editable" :token="EDITOR_KEY" :id="current.id" />
|
||||||
|
<RouteDetailCard v-else :token="EDITOR_KEY" :current="current.id" />
|
||||||
|
</template>
|
||||||
|
<template v-if="isArea">
|
||||||
|
<AreaEditCard v-if="editable" :token="EDITOR_KEY" :id="current.id" />
|
||||||
|
<AreaDetailCard v-else :token="EDITOR_KEY" :current="current.id" />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</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>
|
||||||
|
@ -107,6 +107,7 @@ const selectRobot = (id: string) => {
|
|||||||
<RobotGroups
|
<RobotGroups
|
||||||
v-if="editor"
|
v-if="editor"
|
||||||
:token="EDITOR_KEY"
|
:token="EDITOR_KEY"
|
||||||
|
:sid="id"
|
||||||
:editable="editable"
|
:editable="editable"
|
||||||
:current="current?.id"
|
:current="current?.id"
|
||||||
@change="selectRobot"
|
@change="selectRobot"
|
||||||
@ -175,8 +176,4 @@ const selectRobot = (id: string) => {
|
|||||||
height: calc(100% - 96px);
|
height: calc(100% - 96px);
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-typography.title {
|
|
||||||
font: 500 16px/22px SourceHanSansSC;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -9,12 +9,14 @@ export const ROUTES = Object.freeze<RouteRecordRaw[]>([
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
|
name: '场景编辑',
|
||||||
path: '/scene-editor/:id',
|
path: '/scene-editor/:id',
|
||||||
props: true,
|
props: true,
|
||||||
component: () => import('@/scene-editor.vue'),
|
component: () => import('@/scene-editor.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/group-editor/:id',
|
name: '组编辑',
|
||||||
|
path: '/group-editor/:sid/:id',
|
||||||
props: true,
|
props: true,
|
||||||
component: () => import('@/group-editor.vue'),
|
component: () => import('@/group-editor.vue'),
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user