temp
This commit is contained in:
parent
f8b88edcad
commit
c2c1d8eb5f
@ -113,14 +113,17 @@
|
||||
.ant-checkbox-wrapper {
|
||||
align-items: center;
|
||||
font: 400 14px/22px Roboto;
|
||||
vertical-align: top;
|
||||
color: get-color(text2);
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.ant-collapse.ant-collapse-ghost {
|
||||
.ant-collapse {
|
||||
background-color: transparent;
|
||||
border-radius: 0;
|
||||
|
||||
& > .ant-collapse-item {
|
||||
border: none;
|
||||
|
||||
& > .ant-collapse-header {
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
@ -128,7 +131,6 @@
|
||||
padding: 0;
|
||||
font: 500 14px/22px Roboto;
|
||||
color: get-color(text1);
|
||||
background: get-color(fill3);
|
||||
border-radius: 2px;
|
||||
|
||||
& > .ant-collapse-header-text {
|
||||
@ -155,6 +157,22 @@
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.ant-collapse-borderless > .ant-collapse-item {
|
||||
& > .ant-collapse-header {
|
||||
background: get-color(neutral3);
|
||||
}
|
||||
|
||||
& + .ant-collapse-item {
|
||||
margin-block-start: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
&.ant-collapse-ghost > .ant-collapse-item {
|
||||
& > .ant-collapse-header {
|
||||
background: get-color(fill3);
|
||||
}
|
||||
|
||||
& + .ant-collapse-item {
|
||||
margin-block-start: 8px;
|
||||
@ -271,13 +289,13 @@
|
||||
box-shadow: none !important;
|
||||
|
||||
&.ant-input-status-error {
|
||||
outline-color: rgba($color: get-color(error), $alpha: 20%) !important;
|
||||
border-color: get-color(error) !important;
|
||||
outline-color: rgba($color: get-color(error), $alpha: 20%) !important;
|
||||
}
|
||||
|
||||
&:not(:disabled):focus {
|
||||
outline: 2px solid rgba($color: get-color(primary), $alpha: 20%);
|
||||
border-color: get-color(primary);
|
||||
outline: 2px solid rgba($color: get-color(primary), $alpha: 20%);
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@ -291,13 +309,13 @@
|
||||
box-shadow: none !important;
|
||||
|
||||
&.ant-input-affix-wrapper-status-error {
|
||||
outline-color: rgba($color: get-color(error), $alpha: 20%) !important;
|
||||
border-color: get-color(error) !important;
|
||||
outline-color: rgba($color: get-color(error), $alpha: 20%) !important;
|
||||
}
|
||||
|
||||
&:not(.ant-input-affix-wrapper-disabled).ant-input-affix-wrapper-focused {
|
||||
outline: 2px solid rgba($color: get-color(primary), $alpha: 20%);
|
||||
border-color: get-color(primary);
|
||||
outline: 2px solid rgba($color: get-color(primary), $alpha: 20%);
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@ -306,20 +324,24 @@
|
||||
}
|
||||
|
||||
& > .ant-input {
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
& > .ant-input-suffix {
|
||||
margin-inline-start: 12px;
|
||||
font-size: 16px;
|
||||
color: get-color(icon-disabled);
|
||||
color: get-color(icon);
|
||||
}
|
||||
|
||||
&.search {
|
||||
padding-inline-start: 7px;
|
||||
background-color: get-color(fill3);
|
||||
border-color: get-color(border2);
|
||||
|
||||
& > .ant-input-suffix {
|
||||
color: get-color(icon-disabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,13 +354,13 @@
|
||||
box-shadow: none !important;
|
||||
|
||||
&.ant-input-number-status-error {
|
||||
outline-color: rgba($color: get-color(error), $alpha: 20%) !important;
|
||||
border-color: get-color(error) !important;
|
||||
outline-color: rgba($color: get-color(error), $alpha: 20%) !important;
|
||||
}
|
||||
|
||||
&:not(:disabled).ant-input-number-focused {
|
||||
outline: 2px solid rgba($color: get-color(primary), $alpha: 20%);
|
||||
border-color: get-color(primary);
|
||||
outline: 2px solid rgba($color: get-color(primary), $alpha: 20%);
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
@ -489,8 +511,8 @@
|
||||
}
|
||||
|
||||
&.ant-select-status-error > .ant-select-selector {
|
||||
outline-color: rgba($color: get-color(error), $alpha: 20%) !important;
|
||||
border-color: get-color(error) !important;
|
||||
outline-color: rgba($color: get-color(error), $alpha: 20%) !important;
|
||||
}
|
||||
|
||||
&:not(.ant-select-disabled):hover > .ant-select-selector {
|
||||
@ -498,8 +520,8 @@
|
||||
}
|
||||
|
||||
&:not(.ant-select-disabled).ant-select-focused > .ant-select-selector {
|
||||
outline: 2px solid rgba($color: get-color(primary), $alpha: 20%);
|
||||
border-color: get-color(primary);
|
||||
outline: 2px solid rgba($color: get-color(primary), $alpha: 20%);
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@ -640,8 +662,8 @@
|
||||
padding: 3px 5px;
|
||||
margin: 0;
|
||||
font: 500 14px/22px Roboto;
|
||||
vertical-align: top;
|
||||
color: get-color(text1);
|
||||
vertical-align: top;
|
||||
background-color: transparent;
|
||||
border-color: get-color(border1);
|
||||
border-radius: 4px;
|
||||
|
@ -2,20 +2,22 @@
|
||||
@use 'asset/icons/icon' as *;
|
||||
|
||||
@include themed {
|
||||
.icon-btn {
|
||||
.ant-btn.icon-btn {
|
||||
color: get-color(text1) !important;
|
||||
|
||||
&.panel-btn {
|
||||
color: get-color(icon);
|
||||
color: get-color(icon) !important;
|
||||
|
||||
&:disabled {
|
||||
color: get-color(icon-disabled);
|
||||
color: get-color(icon-disabled) !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.tool-btn {
|
||||
color: get-color(icon);
|
||||
color: get-color(icon) !important;
|
||||
|
||||
&:disabled {
|
||||
color: get-color(icon-disabled);
|
||||
color: get-color(icon-disabled) !important;
|
||||
}
|
||||
|
||||
&.active {
|
||||
@ -23,7 +25,7 @@
|
||||
}
|
||||
|
||||
&:not(:disabled):hover {
|
||||
background-color: get-color(bg_layout);
|
||||
background-color: get-color(bg_layout) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -45,7 +47,7 @@
|
||||
background-color: currentcolor;
|
||||
|
||||
&.primary {
|
||||
color: get-color(icon-brand);
|
||||
color: get-color(icon-brand) !important;
|
||||
}
|
||||
|
||||
@each $icon in $icons {
|
||||
|
@ -40,3 +40,5 @@ export interface MapAreaInfo {
|
||||
routes?: Array<string>; // 绑定线路id集合
|
||||
}
|
||||
//#endregion
|
||||
|
||||
export type MapRect = Record<'x' | 'y' | 'width' | 'height', number>;
|
||||
|
@ -36,14 +36,11 @@ const bindAction = computed<string>(
|
||||
const mapAreas = (type: MapAreaType): string => {
|
||||
const id = pen.value?.id;
|
||||
if (!id) return '';
|
||||
return (
|
||||
editor.value
|
||||
.find(`area-${type}`)
|
||||
.filter(({ area }) => area?.points?.includes(id))
|
||||
?.map(({ label }) => label)
|
||||
.filter((v) => !!v)
|
||||
.join('、') ?? ''
|
||||
);
|
||||
return editor.value
|
||||
.getBoundAreas(id, 'point', type)
|
||||
.map(({ label }) => label)
|
||||
.filter((v) => !!v)
|
||||
.join('、');
|
||||
};
|
||||
const coArea1 = computed<string>(() => mapAreas(MapAreaType.库区));
|
||||
const coArea2 = computed<string>(() => mapAreas(MapAreaType.互斥区));
|
||||
|
@ -1,29 +1,57 @@
|
||||
<script setup lang="ts">
|
||||
import { MAP_POINT_TYPES, type MapPen, type MapPointInfo } from '@api/map';
|
||||
import { MAP_POINT_TYPES, MapAreaType, type MapPen, type MapPointInfo, MapPointType, type MapRect } from '@api/map';
|
||||
import type { RobotInfo } from '@api/robot';
|
||||
import type { RobotBindModalRef } from '@common/modal/robot-bind-modal.vue';
|
||||
import type { EditorService } from '@core/editor.service';
|
||||
import sTheme from '@core/theme.service';
|
||||
import { isNil } from 'lodash-es';
|
||||
import { ref, shallowRef } from 'vue';
|
||||
import { computed, inject, type InjectionKey, type ShallowRef } from 'vue';
|
||||
|
||||
type Props = {
|
||||
token: InjectionKey<ShallowRef<EditorService>>;
|
||||
id?: string;
|
||||
};
|
||||
const props = defineProps<Props>();
|
||||
const editor = inject(props.token)!;
|
||||
|
||||
const pen = computed<MapPen | undefined>(() => editor.value.current.value);
|
||||
const point = computed<MapPointInfo | null>(() => {
|
||||
const point = pen.value?.point;
|
||||
if (!point?.type) return null;
|
||||
return point;
|
||||
const pen = computed<MapPen | null>(() => {
|
||||
const v = editor.value.current.value;
|
||||
if (v?.id !== props.id) return null;
|
||||
return v!;
|
||||
});
|
||||
const point = computed<MapPointInfo | null>(() => {
|
||||
const v = pen.value?.point;
|
||||
if (!v?.type) return null;
|
||||
return v;
|
||||
});
|
||||
|
||||
const rect = computed<MapRect | null>(() => {
|
||||
if (isNil(pen.value)) return null;
|
||||
return editor.value.getPenRect(pen.value);
|
||||
});
|
||||
|
||||
const refBindRobot = shallowRef<RobotBindModalRef>();
|
||||
const robotKeyword = ref<string>('');
|
||||
const robots = computed<RobotInfo[]>(
|
||||
() =>
|
||||
<RobotInfo[]>(
|
||||
point.value?.robots?.map((v) => editor.value.getRobotById(v)).filter((v) => v?.label.includes(robotKeyword.value))
|
||||
) ?? [],
|
||||
);
|
||||
|
||||
const coArea1 = computed<MapPen[]>(() => editor.value.getBoundAreas(props.id, 'point', MapAreaType.库区));
|
||||
const coArea2 = computed<MapPen[]>(() => editor.value.getBoundAreas(props.id, 'point', MapAreaType.互斥区));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RobotBindModal ref="refBindRobot" :token="token" />
|
||||
|
||||
<a-card :title="$t('属性')" :bordered="false">
|
||||
<template v-if="pen && point">
|
||||
<a-row :gutter="8">
|
||||
<a-flex v-if="id && pen && point" :gap="24" vertical>
|
||||
<a-row :gutter="[8, 8]">
|
||||
<a-col :span="12">
|
||||
<a-select :value="point.type" @change="editor.changePointType(pen.id!, <number>$event)">
|
||||
<a-select :value="point.type" @change="editor.changePointType(id, <number>$event)">
|
||||
<a-select-option v-for="[l, v] in MAP_POINT_TYPES" :key="v">{{ $t(l) }}</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
@ -31,13 +59,139 @@ const point = computed<MapPointInfo | null>(() => {
|
||||
<a-input
|
||||
:maxlength="10"
|
||||
:value="pen.label"
|
||||
@change="editor.updatePen(pen.id!, { label: $event.target.value }, false)"
|
||||
@change="editor.updatePen(id, { label: $event.target.value }, false)"
|
||||
>
|
||||
<template #suffix><EditOutlined /></template>
|
||||
</a-input>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
<a-row :gutter="[8, 8]">
|
||||
<a-col :span="24">
|
||||
<a-typography-text>{{ $t('描述') }}:</a-typography-text>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-textarea
|
||||
class="prop"
|
||||
:placeholder="$t('请输入描述内容')"
|
||||
:maxlength="100"
|
||||
:autoSize="{ minRows: 3, maxRows: 3 }"
|
||||
:value="pen?.desc"
|
||||
@change="editor.updatePen(id, { desc: $event.target.value }, false)"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row align="middle" :gutter="16">
|
||||
<a-col flex="auto">
|
||||
<a-typography-text>{{ $t('坐标') }}:</a-typography-text>
|
||||
</a-col>
|
||||
<a-col flex="none">
|
||||
<a-space :size="8">
|
||||
<a-typography-text code>X:</a-typography-text>
|
||||
<a-input-number
|
||||
style="width: 80px"
|
||||
:placeholder="$t('请输入')"
|
||||
:precision="0"
|
||||
:controls="false"
|
||||
:value="rect?.x?.toFixed()"
|
||||
@change="editor.updatePen(id, { x: <number>$event })"
|
||||
/>
|
||||
</a-space>
|
||||
</a-col>
|
||||
<a-col flex="none">
|
||||
<a-space :size="8">
|
||||
<a-typography-text code>Y:</a-typography-text>
|
||||
<a-input-number
|
||||
style="width: 80px"
|
||||
:placeholder="$t('请输入')"
|
||||
:precision="0"
|
||||
:controls="false"
|
||||
:value="rect?.y?.toFixed()"
|
||||
@change="editor.updatePen(id, { y: <number>$event })"
|
||||
/>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-collapse expand-icon-position="end" :bordered="false">
|
||||
<template #expandIcon="v">
|
||||
<i class="icon dropdown" :class="{ active: v?.isActive }" />
|
||||
</template>
|
||||
|
||||
<a-collapse-panel
|
||||
v-if="[MapPointType.充电点, MapPointType.停靠点].includes(point.type)"
|
||||
:header="$t('绑定机器人')"
|
||||
>
|
||||
<template #extra>
|
||||
<a-button class="icon-btn" size="small" @click.stop="refBindRobot?.open(pen)">
|
||||
<i class="mask plus" />
|
||||
</a-button>
|
||||
</template>
|
||||
<a-input class="search mv-8" :placeholder="$t('请输入搜索关键字')" v-model:value="robotKeyword">
|
||||
<template #suffix>
|
||||
<i class="icon search size-16" />
|
||||
</template>
|
||||
</a-input>
|
||||
<a-list rowKey="id" :data-source="robots">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item class="ph-16" style="height: 36px">
|
||||
<a-typography-text>{{ item.label }}</a-typography-text>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel v-if="MapPointType.等待点 === point.type" :header="$t('绑定动作点')">
|
||||
<template #extra>
|
||||
<a-button class="icon-btn" size="small" @click.stop>
|
||||
<i class="mask plus" />
|
||||
</a-button>
|
||||
</template>
|
||||
<a-list rowKey="id" :data-source="point.actions">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item class="ph-16" style="height: 36px">
|
||||
<a-typography-text>{{ editor.getPenById(item)?.label }}</a-typography-text>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel v-if="MapPointType.动作点 === point.type" :header="$t('关联库区')">
|
||||
<a-list rowKey="id" :data-source="coArea1">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item class="ph-16" style="height: 36px">
|
||||
<a-typography-text>{{ item.label }}</a-typography-text>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel
|
||||
v-if="
|
||||
[
|
||||
MapPointType.普通点,
|
||||
MapPointType.电梯点,
|
||||
MapPointType.自动门点,
|
||||
MapPointType.等待点,
|
||||
MapPointType.充电点,
|
||||
MapPointType.停靠点,
|
||||
MapPointType.动作点,
|
||||
MapPointType.临时避让点,
|
||||
].includes(point.type)
|
||||
"
|
||||
:header="$t('关联互斥区')"
|
||||
>
|
||||
<a-list rowKey="id" :data-source="coArea2">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item class="ph-16" style="height: 36px">
|
||||
<a-typography-text>{{ item.label }}</a-typography-text>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
</a-flex>
|
||||
<a-empty v-else :image="sTheme.empty" />
|
||||
</a-card>
|
||||
</template>
|
||||
|
70
src/components/modal/robot-bind-modal.vue
Normal file
70
src/components/modal/robot-bind-modal.vue
Normal file
@ -0,0 +1,70 @@
|
||||
<script setup lang="ts">
|
||||
import type { MapPen } from '@api/map';
|
||||
import { type RobotInfo, RobotType } from '@api/robot';
|
||||
import type { EditorService } from '@core/editor.service';
|
||||
import { computed, inject, type InjectionKey, ref, type ShallowRef, toRaw } from 'vue';
|
||||
|
||||
type Props = {
|
||||
token: InjectionKey<ShallowRef<EditorService>>;
|
||||
};
|
||||
const props = defineProps<Props>();
|
||||
const editor = inject(props.token)!;
|
||||
|
||||
export type RobotBindModalRef = Ref;
|
||||
type Ref = {
|
||||
open: (pen: MapPen) => void;
|
||||
};
|
||||
const open: Ref['open'] = (pen) => {
|
||||
if (!pen?.id) return;
|
||||
keyword.value = '';
|
||||
id.value = pen.id;
|
||||
selected.value = pen.point?.robots ?? [];
|
||||
show.value = true;
|
||||
};
|
||||
defineExpose<Ref>({ open });
|
||||
|
||||
const show = ref<boolean>(false);
|
||||
const keyword = ref<string>('');
|
||||
|
||||
const robots = computed<RobotInfo[]>(() => editor.value.robots.filter(({ label }) => label.includes(keyword.value)));
|
||||
|
||||
const id = ref<string>('');
|
||||
const selected = ref<string[]>([]);
|
||||
|
||||
const submit = () => {
|
||||
editor.value.updatePoint(id.value, { robots: toRaw(selected.value) });
|
||||
show.value = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal :width="420" :title="$t('绑定机器人')" v-model:open="show" :mask-closable="false" centered @ok="submit">
|
||||
<a-input class="search" :placeholder="$t('请输入搜索关键字')" v-model:value="keyword">
|
||||
<template #suffix>
|
||||
<i class="icon search size-16" />
|
||||
</template>
|
||||
</a-input>
|
||||
|
||||
<a-table
|
||||
class="mt-10"
|
||||
rowKey="id"
|
||||
:dataSource="robots"
|
||||
:pagination="false"
|
||||
:rowSelection="{
|
||||
columnWidth: 32,
|
||||
selectedRowKeys: selected,
|
||||
onChange: (keys) => (selected = <string[]>keys),
|
||||
}"
|
||||
:scroll="{ y: 80 }"
|
||||
bordered
|
||||
>
|
||||
<a-table-column dataIndex="label" :title="$t('机器人')" />
|
||||
<a-table-column dataIndex="brand" :title="$t('品牌')" />
|
||||
<a-table-column dataIndex="type" :title="$t('机器人类型')">
|
||||
<template #default="{ text }">
|
||||
{{ $t(RobotType[text]) }}
|
||||
</template>
|
||||
</a-table-column>
|
||||
</a-table>
|
||||
</a-modal>
|
||||
</template>
|
@ -138,7 +138,7 @@ const selectRobot = (id: string) => {
|
||||
<RobotDetailCard v-if="isRobot" :token="EDITOR_KEY" :current="current.id" />
|
||||
|
||||
<template v-if="isPoint">
|
||||
<PointEditCard v-if="editable" :token="EDITOR_KEY" :current="current.id" />
|
||||
<PointEditCard v-if="editable" :token="EDITOR_KEY" :id="current.id" />
|
||||
<PointDetailCard v-else :token="EDITOR_KEY" :current="current.id" />
|
||||
</template>
|
||||
<template v-if="isRoute">
|
||||
@ -171,6 +171,7 @@ const selectRobot = (id: string) => {
|
||||
right: 64px;
|
||||
z-index: 100;
|
||||
width: 320px;
|
||||
max-height: calc(100% - 96px);
|
||||
}
|
||||
|
||||
.ant-typography.title {
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { EDITOR_CONFIG, MapAreaType, type MapPen, MapPointType } from '@api/map';
|
||||
import { EDITOR_CONFIG, MapAreaType, type MapPen, type MapPointInfo, MapPointType } from '@api/map';
|
||||
import type { RobotGroup, RobotInfo } from '@api/robot';
|
||||
import type { SceneData } from '@api/scene';
|
||||
import sTheme from '@core/theme.service';
|
||||
import { CanvasLayer, EditType, LockState, Meta2d, type Pen, s8 } from '@meta2d/core';
|
||||
import { useObservable } from '@vueuse/rxjs';
|
||||
import { clone, cloneDeep, get, isNil, isString, pick, remove, some } from 'lodash-es';
|
||||
import { clone, cloneDeep, get, isNil, isString, mapKeys, pick, remove, some } from 'lodash-es';
|
||||
import { BehaviorSubject, debounceTime, filter, map, Subject, switchMap } from 'rxjs';
|
||||
import { reactive, watch } from 'vue';
|
||||
|
||||
@ -195,6 +195,12 @@ export class EditorService extends Meta2d {
|
||||
// this.pushHistory({ type: EditType.Add, pens: [cloneDeep(pen)] });
|
||||
}
|
||||
|
||||
public updatePoint(id: string, info: Partial<MapPointInfo>): void {
|
||||
const { point } = this.getPenById(id) ?? {};
|
||||
if (!point?.type) return;
|
||||
const p = { ...point, ...info };
|
||||
this.setValue({ id, point: p }, { render: true, history: true, doEvent: true });
|
||||
}
|
||||
public changePointType(id: string, type: MapPointType): void {
|
||||
this.setValue(
|
||||
{ id, ...this.#mapPoint(type), ...this.#mapPointImage(type), point: { type } },
|
||||
@ -241,6 +247,15 @@ export class EditorService extends Meta2d {
|
||||
{ initialValue: new Array<MapPen>() },
|
||||
);
|
||||
|
||||
public getBoundAreas(id: string = '', name: 'point' | 'line', type: MapAreaType): MapPen[] {
|
||||
if (!id) return [];
|
||||
return this.find(`area-${type}`).filter(({ area }) => {
|
||||
if (name === 'point') return area?.points?.includes(id);
|
||||
if (name === 'line') return area?.routes?.includes(id);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public async addArea(p1: Point, p2: Point, type = MapAreaType.库区) {
|
||||
const scale = this.data().scale ?? 1;
|
||||
const w = Math.abs(p1.x - p2.x);
|
||||
|
Loading…
x
Reference in New Issue
Block a user