web-map/src/components/card/route-edit-card.vue

237 lines
7.7 KiB
Vue

<script setup lang="ts">
import {
MAP_ROUTE_PASS_TYPES,
MAP_ROUTE_TYPES,
type MapPen,
type MapRouteInfo,
MapRoutePassType,
MapRouteType,
} from '@api/map';
import type { EditorService } from '@core/editor.service';
import sTheme from '@core/theme.service';
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 | null>(() => {
const v = editor.value.current.value;
if (v?.id !== props.id) return null;
return v!;
});
const route = computed<MapRouteInfo | null>(() => {
const v = pen.value?.route;
if (!v?.type) return null;
return v;
});
// 双向路线管理
const connectedRoutes = computed<MapPen[]>(() => {
if (!pen.value) return [];
const [a1, a2] = pen.value.anchors ?? [];
if (!a1?.connectTo || !a2?.connectTo) return [];
return editor.value.getRoutesBetweenPoints(a1.connectTo, a2.connectTo);
});
const currentRoute = computed<MapPen | null>(() => {
return pen.value;
});
const oppositeRoute = computed<MapPen | null>(() => {
if (!currentRoute.value) return null;
const [a1, a2] = currentRoute.value.anchors ?? [];
if (!a1?.connectTo || !a2?.connectTo) return null;
return connectedRoutes.value.find(r => r.id !== currentRoute.value?.id) || null;
});
const isBidirectional = computed<boolean>(() => {
return connectedRoutes.value.length >= 2;
});
// 处理路线方向变化
function handleDirectionChange(direction: any) {
if (!props.id) return;
editor.value.updateRoute(props.id, { direction: Number(direction) as -1 | 1 });
}
// 处理路线类型变化
function handleRouteTypeChange(type: any) {
if (!props.id) return;
editor.value.changeRouteType(props.id, Number(type) as unknown as MapRouteType);
}
// 处理反向路线方向变化
function handleOppositeDirectionChange(direction: any) {
if (!oppositeRoute.value?.id) return;
editor.value.updateRoute(oppositeRoute.value.id, { direction: Number(direction) as -1 | 1 });
}
// 处理通行类型变化
function handlePassChange(pass: any) {
if (!props.id) return;
editor.value.updateRoute(props.id, { pass: Number(pass) as MapRoutePassType });
}
</script>
<template>
<a-card class="full" :title="$t('属性')" :bordered="false">
<a-flex v-if="id && pen && route" :gap="24" vertical>
<a-row :gutter="[8, 8]">
<a-col :span="24">
<a-select
:value="route.pass ?? MapRoutePassType.无"
@change="handlePassChange($event)"
>
<a-select-option v-for="[l, v] in MAP_ROUTE_PASS_TYPES" :key="v">{{ $t(l) }}</a-select-option>
</a-select>
</a-col>
</a-row>
<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="10" :wrap="false">
<a-col flex="none">
<a-typography-text>{{ $t('路段长度') }}:</a-typography-text>
</a-col>
<a-col flex="auto">
<a-input :value="pen.length?.toFixed()" disabled />
</a-col>
</a-row>
<a-row align="middle" :gutter="10" :wrap="false">
<a-col flex="none">
<a-typography-text>{{ $t('路段方向') }}:</a-typography-text>
</a-col>
<a-col flex="auto">
<a-select
:value="route.direction || 1"
@change="handleDirectionChange($event)"
>
<a-select-option :value="1">{{ editor.getRouteLabel(id, 1) }}</a-select-option>
<a-select-option :value="-1">{{ editor.getRouteLabel(id, -1) }}</a-select-option>
</a-select>
</a-col>
</a-row>
<!-- 反向路线方向(如果存在双向路线) -->
<a-row v-if="isBidirectional && oppositeRoute" align="middle" :gutter="10" :wrap="false">
<a-col flex="none">
<a-typography-text>{{ $t('反向路线方向') }}:</a-typography-text>
</a-col>
<a-col flex="auto">
<a-select
:value="oppositeRoute.route?.direction || -1"
@change="handleOppositeDirectionChange($event)"
>
<a-select-option :value="1">{{ editor.getRouteLabel(oppositeRoute.id, 1) }}</a-select-option>
<a-select-option :value="-1">{{ editor.getRouteLabel(oppositeRoute.id, -1) }}</a-select-option>
</a-select>
</a-col>
</a-row>
<a-row align="middle" :gutter="10" :wrap="false">
<a-col flex="none">
<a-typography-text>{{ $t('路段类型') }}:</a-typography-text>
</a-col>
<a-col flex="auto">
<a-select :value="route.type" @change="handleRouteTypeChange($event)">
<a-select-option v-for="[l, v] in MAP_ROUTE_TYPES" :key="v">{{ $t(l) }}</a-select-option>
</a-select>
</a-col>
</a-row>
<a-row
v-if="[MapRouteType.二阶贝塞尔曲线, MapRouteType.三阶贝塞尔曲线].includes(route.type)"
align="middle"
:gutter="8"
>
<a-col flex="auto">
<a-typography-text>{{ $t('控制点1') }}:</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="route?.c1?.x.toFixed()"
@change="editor.updateRoute(id, { c1: { x: +$event, y: route?.c1?.y ?? 0 } })"
/>
</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="route?.c1?.y.toFixed()"
@change="editor.updateRoute(id, { c1: { x: route?.c1?.x ?? 0, y: +$event } })"
/>
</a-space>
</a-col>
</a-row>
<a-row v-if="MapRouteType.三阶贝塞尔曲线 === route.type" align="middle" :gutter="8">
<a-col flex="auto">
<a-typography-text>{{ $t('控制点2') }}:</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="route?.c2?.x.toFixed()"
@change="editor.updateRoute(id, { c2: { x: +$event, y: route?.c2?.y ?? 0 } })"
/>
</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="route?.c2?.y.toFixed()"
@change="editor.updateRoute(id, { c2: { x: route?.c2?.x ?? 0, y: +$event } })"
/>
</a-space>
</a-col>
</a-row>
</a-flex>
<a-empty v-else :image="sTheme.empty" />
</a-card>
</template>