|
|
|
|
@@ -132,6 +132,12 @@
|
|
|
|
|
<view v-if="routePlanError" class="indoor-guide-error">
|
|
|
|
|
<text class="indoor-guide-error-text">{{ routePlanError }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="start-point-select" @tap="openStartPointPicker">
|
|
|
|
|
<text class="start-point-select-label">起点</text>
|
|
|
|
|
<text class="start-point-select-value" :class="{ placeholder: !selectedStartPoint }">
|
|
|
|
|
{{ selectedStartPoint ? `${selectedStartPoint.name} · ${selectedStartPoint.floorLabel}` : '请选择起点' }}
|
|
|
|
|
</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="indoor-guide-actions">
|
|
|
|
|
<view
|
|
|
|
|
class="indoor-guide-btn secondary"
|
|
|
|
|
@@ -159,6 +165,19 @@
|
|
|
|
|
<view class="indoor-guide-peek-handle"></view>
|
|
|
|
|
<text class="indoor-guide-peek-text">点位预览</text>
|
|
|
|
|
</view>
|
|
|
|
|
<RoutePointPicker
|
|
|
|
|
:visible="startPointPickerVisible"
|
|
|
|
|
title="选择起点"
|
|
|
|
|
placeholder="搜索起点"
|
|
|
|
|
:options="routePointOptions"
|
|
|
|
|
:selected-poi-id="selectedStartPoint?.poiId || ''"
|
|
|
|
|
:loading="startPointPickerLoading"
|
|
|
|
|
:error="startPointPickerError"
|
|
|
|
|
empty-text="暂无可作为起点的位置"
|
|
|
|
|
close-text="返回"
|
|
|
|
|
@close="startPointPickerVisible = false"
|
|
|
|
|
@select="handleStartPointSelect"
|
|
|
|
|
/>
|
|
|
|
|
</GuideMapShell>
|
|
|
|
|
</GuidePageFrame>
|
|
|
|
|
</template>
|
|
|
|
|
@@ -168,6 +187,7 @@ import { computed, onMounted, onUnmounted, ref } from 'vue'
|
|
|
|
|
import { onLoad } from '@dcloudio/uni-app'
|
|
|
|
|
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
|
|
|
|
import GuideMapShell from '@/components/navigation/GuideMapShell.vue'
|
|
|
|
|
import RoutePointPicker, { type RoutePointOption } from '@/components/navigation/RoutePointPicker.vue'
|
|
|
|
|
import {
|
|
|
|
|
guideUseCase
|
|
|
|
|
} from '@/usecases/guideUseCase'
|
|
|
|
|
@@ -226,6 +246,11 @@ const routeReadinessMessage = ref('')
|
|
|
|
|
const routePlanning = ref(false)
|
|
|
|
|
const routePlanError = ref('')
|
|
|
|
|
const activeRoutePreview = ref<GuideRouteResult | null>(null)
|
|
|
|
|
const selectedStartPoint = ref<RoutePointOption | null>(null)
|
|
|
|
|
const routePointOptions = ref<RoutePointOption[]>([])
|
|
|
|
|
const startPointPickerVisible = ref(false)
|
|
|
|
|
const startPointPickerLoading = ref(false)
|
|
|
|
|
const startPointPickerError = ref('')
|
|
|
|
|
const isIndoorGuidePanelHidden = ref(false)
|
|
|
|
|
const indoorGuideTouchStartY = ref(0)
|
|
|
|
|
const indoorGuideTouchCurrentY = ref(0)
|
|
|
|
|
@@ -724,9 +749,41 @@ const handleConfirmLocation = async () => {
|
|
|
|
|
activeFloor.value = startLocation.floor || activeFloor.value
|
|
|
|
|
isManualLocationPanelOpen.value = false
|
|
|
|
|
activeRoutePreview.value = null
|
|
|
|
|
selectedStartPoint.value = null
|
|
|
|
|
routeViewState.value = 'preview'
|
|
|
|
|
requestTargetFocus(route.value.targetLocation)
|
|
|
|
|
saveCurrentLocationPreviewUrl()
|
|
|
|
|
routePlanError.value = '请选择起点'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const openStartPointPicker = async () => {
|
|
|
|
|
startPointPickerVisible.value = true
|
|
|
|
|
startPointPickerError.value = ''
|
|
|
|
|
if (routePointOptions.value.length || startPointPickerLoading.value) return
|
|
|
|
|
|
|
|
|
|
startPointPickerLoading.value = true
|
|
|
|
|
try {
|
|
|
|
|
routePointOptions.value = (await guideRouteUseCase.listTargets())
|
|
|
|
|
.filter((target) => Boolean(target.poiId && target.routeNodeId))
|
|
|
|
|
.map((target) => ({
|
|
|
|
|
poiId: target.poiId,
|
|
|
|
|
name: target.name,
|
|
|
|
|
floorId: target.floorId,
|
|
|
|
|
floorLabel: target.floorLabel,
|
|
|
|
|
categoryLabel: target.categoryLabel
|
|
|
|
|
}))
|
|
|
|
|
} catch (error) {
|
|
|
|
|
startPointPickerError.value = error instanceof Error ? error.message : '起点列表加载失败,请重试'
|
|
|
|
|
} finally {
|
|
|
|
|
startPointPickerLoading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleStartPointSelect = (point: RoutePointOption) => {
|
|
|
|
|
selectedStartPoint.value = point
|
|
|
|
|
startPointPickerVisible.value = false
|
|
|
|
|
routePlanError.value = ''
|
|
|
|
|
activeRoutePreview.value = null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleStartIndoorGuide = async () => {
|
|
|
|
|
@@ -734,37 +791,31 @@ const handleStartIndoorGuide = async () => {
|
|
|
|
|
|
|
|
|
|
await refreshRouteReadinessState()
|
|
|
|
|
if (!routeReady.value) return
|
|
|
|
|
if (!selectedStartPoint.value?.poiId) {
|
|
|
|
|
routePlanError.value = '请选择起点'
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
routePlanning.value = true
|
|
|
|
|
routePlanError.value = ''
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const targets = await guideRouteUseCase.listTargets()
|
|
|
|
|
const startName = route.value.startLocation?.label || ''
|
|
|
|
|
const startFloor = route.value.startLocation?.floor || ''
|
|
|
|
|
const targetLocation = route.value.targetLocation
|
|
|
|
|
const fallbackStart = targets.find((target) => (
|
|
|
|
|
startName
|
|
|
|
|
? target.name.includes(startName) || startName.includes(target.name)
|
|
|
|
|
: false
|
|
|
|
|
)) || targets.find((target) => (
|
|
|
|
|
startFloor
|
|
|
|
|
? target.floorLabel === startFloor || target.floorId === normalizeGuideFloorId(startFloor)
|
|
|
|
|
: false
|
|
|
|
|
)) || targets.find((target) => target.poiId !== route.value.facilityId)
|
|
|
|
|
const selectedStart = targets.find((target) => target.poiId === selectedStartPoint.value?.poiId)
|
|
|
|
|
|
|
|
|
|
const endTarget = targets.find((target) => target.poiId === route.value.facilityId)
|
|
|
|
|
|| (targetLocation
|
|
|
|
|
? targets.find((target) => target.floorId === targetLocation.floorId && target.name === targetLocation.name)
|
|
|
|
|
: null)
|
|
|
|
|
|
|
|
|
|
if (!fallbackStart || !endTarget) {
|
|
|
|
|
routePlanError.value = '未找到可用于位置关系预览的起点或终点'
|
|
|
|
|
if (!selectedStart || !selectedStart.routeNodeId || !endTarget) {
|
|
|
|
|
routePlanError.value = selectedStart ? '未找到可用于位置关系预览的终点' : '请选择起点'
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const result = await guideRouteUseCase.planRoute({
|
|
|
|
|
startPoiId: fallbackStart.poiId,
|
|
|
|
|
startPoiId: selectedStart.poiId,
|
|
|
|
|
endPoiId: endTarget.poiId
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
@@ -952,6 +1003,41 @@ const handlePageBack = () => {
|
|
|
|
|
color: #b44b42;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.start-point-select {
|
|
|
|
|
min-height: 44px;
|
|
|
|
|
padding: 0 12px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
background: #f5f7f2;
|
|
|
|
|
border: 1px solid #e4e5df;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.start-point-select-label {
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
line-height: 18px;
|
|
|
|
|
color: #545861;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.start-point-select-value {
|
|
|
|
|
min-width: 0;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
line-height: 19px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
color: #151713;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.start-point-select-value.placeholder {
|
|
|
|
|
color: #7a7e76;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.indoor-guide-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|