Update guide presentation and route preview gating
This commit is contained in:
@@ -98,7 +98,7 @@ interface QuickAction {
|
|||||||
route?: string
|
route?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<{
|
defineProps<{
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ const handleSettingClick = (setting: string) => {
|
|||||||
if (setting === 'about') {
|
if (setting === 'about') {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '关于',
|
title: '关于',
|
||||||
content: '深圳自然博物馆智能导览系统\n版本 1.0.0\n\n为您的博物馆之旅提供智能导航和语音讲解服务。',
|
content: '深圳自然博物馆馆内导览服务\n版本 1.0.0\n\n为您的博物馆之旅提供馆内导览、位置预览和讲解服务。',
|
||||||
showCancel: false
|
showCancel: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ const props = withDefaults(defineProps<{
|
|||||||
routePreview?: GuideRouteResult | null
|
routePreview?: GuideRouteResult | null
|
||||||
showRoute?: boolean
|
showRoute?: boolean
|
||||||
}>(), {
|
}>(), {
|
||||||
searchText: '搜索设施、展厅、入口',
|
searchText: '请输入地点进行搜索',
|
||||||
activeMode: '3d',
|
activeMode: '3d',
|
||||||
activeFloor: '1F',
|
activeFloor: '1F',
|
||||||
indoorView: 'floor',
|
indoorView: 'floor',
|
||||||
@@ -579,12 +579,12 @@ const handleAutoSwitch = (event: { from: 'overview' | 'floor'; to: 'overview' |
|
|||||||
.guide-search-field.variant-home {
|
.guide-search-field.variant-home {
|
||||||
left: 20px;
|
left: 20px;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
height: 48px;
|
height: 46px;
|
||||||
padding: 0 14px;
|
padding: 0 14px;
|
||||||
background: rgba(255, 255, 255, 0.96);
|
background: rgba(255, 255, 255, 0.96);
|
||||||
border: 1px solid #d7dad3;
|
border: 1px solid #d7dad3;
|
||||||
border-radius: 12px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 8px 22px rgba(36, 49, 42, 0.08);
|
box-shadow: 0 8px 18px rgba(36, 49, 42, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-field-text {
|
.search-field-text {
|
||||||
@@ -792,7 +792,7 @@ const handleAutoSwitch = (event: { from: 'overview' | 'floor'; to: 'overview' |
|
|||||||
overflow: visible;
|
overflow: visible;
|
||||||
background: rgba(255, 255, 255, 0.84);
|
background: rgba(255, 255, 255, 0.84);
|
||||||
border: 1px solid #dde5df;
|
border: 1px solid #dde5df;
|
||||||
border-radius: 10px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 8px 18px rgba(110, 127, 115, 0.12);
|
box-shadow: 0 8px 18px rgba(110, 127, 115, 0.12);
|
||||||
z-index: 35;
|
z-index: 35;
|
||||||
}
|
}
|
||||||
@@ -851,7 +851,7 @@ const handleAutoSwitch = (event: { from: 'overview' | 'floor'; to: 'overview' |
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border: 1px solid #dde5df;
|
border: 1px solid #dde5df;
|
||||||
border-radius: 10px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 8px 18px rgba(110, 127, 115, 0.12);
|
box-shadow: 0 8px 18px rgba(110, 127, 115, 0.12);
|
||||||
z-index: 36;
|
z-index: 36;
|
||||||
}
|
}
|
||||||
@@ -944,7 +944,7 @@ const handleAutoSwitch = (event: { from: 'overview' | 'floor'; to: 'overview' |
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: rgba(255, 255, 255, 0.94);
|
background: rgba(255, 255, 255, 0.94);
|
||||||
border: 1px solid #dde5df;
|
border: 1px solid #dde5df;
|
||||||
border-radius: 10px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 8px 18px rgba(110, 127, 115, 0.12);
|
box-shadow: 0 8px 18px rgba(110, 127, 115, 0.12);
|
||||||
z-index: 35;
|
z-index: 35;
|
||||||
}
|
}
|
||||||
@@ -978,7 +978,7 @@ const handleAutoSwitch = (event: { from: 'overview' | 'floor'; to: 'overview' |
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
border: 1px solid #dde5df;
|
border: 1px solid #dde5df;
|
||||||
border-radius: 10px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 8px 18px rgba(110, 127, 115, 0.12);
|
box-shadow: 0 8px 18px rgba(110, 127, 115, 0.12);
|
||||||
z-index: 35;
|
z-index: 35;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,12 +67,12 @@ const handleTabTap = (tab: GuideTopTab) => {
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
right: auto;
|
right: auto;
|
||||||
width: 174px;
|
width: 174px;
|
||||||
height: 38px;
|
height: 36px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
background: #ffffff;
|
background: rgba(255, 255, 255, 0.95);
|
||||||
border: 1px solid #151713;
|
border: 1px solid #d7dad3;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 8px 20px rgba(36, 49, 42, 0.08);
|
box-shadow: 0 8px 18px rgba(36, 49, 42, 0.08);
|
||||||
transform: translateX(-50%) translateZ(0);
|
transform: translateX(-50%) translateZ(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,12 +88,12 @@ const handleTabTap = (tab: GuideTopTab) => {
|
|||||||
.variant-segmented .guide-top-tab {
|
.variant-segmented .guide-top-tab {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
width: auto;
|
width: auto;
|
||||||
height: 32px;
|
height: 30px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.variant-segmented .guide-top-tab.active {
|
.variant-segmented .guide-top-tab.active {
|
||||||
background: #000000;
|
background: #151713;
|
||||||
}
|
}
|
||||||
|
|
||||||
.guide-top-tab-label {
|
.guide-top-tab-label {
|
||||||
|
|||||||
@@ -1,53 +1,89 @@
|
|||||||
<template>
|
<template>
|
||||||
<view v-if="visible" class="route-planner-panel">
|
<view v-if="visible" class="route-planner-panel" :class="{ collapsed: isCollapsed }">
|
||||||
<view class="panel-handle"></view>
|
<view class="panel-handle" @tap.stop="toggleCollapsed"></view>
|
||||||
<view class="panel-header">
|
|
||||||
<view class="panel-title-group">
|
<view v-if="isCollapsed" class="panel-collapsed" @tap="expandPanel">
|
||||||
|
<view class="panel-collapsed-copy">
|
||||||
<text class="panel-title">室内到室内</text>
|
<text class="panel-title">室内到室内</text>
|
||||||
<text v-if="summary" class="panel-summary">{{ summary }}</text>
|
<text class="panel-summary">{{ collapsedSummary }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="panel-clear" @tap="handleClear">
|
<view class="panel-expand">
|
||||||
<text class="panel-clear-text">清除</text>
|
<text class="panel-expand-text">展开</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="point-row">
|
<template v-else>
|
||||||
<view class="point-card" @tap="openPicker('start')">
|
<view class="panel-header">
|
||||||
<view class="point-dot start"></view>
|
<view class="panel-title-group">
|
||||||
<text class="point-label">起点</text>
|
<text class="panel-title">室内到室内</text>
|
||||||
<text class="point-name" :class="{ placeholder: !startPoint }">
|
<text v-if="summary" class="panel-summary">{{ summary }}</text>
|
||||||
{{ startPoint?.name || '选择起点' }}
|
</view>
|
||||||
</text>
|
<view class="panel-actions">
|
||||||
<text v-if="startPoint" class="point-meta">{{ pointMeta(startPoint) }}</text>
|
<view class="panel-light-action" @tap="collapsePanel">
|
||||||
|
<text class="panel-light-action-text">收起</text>
|
||||||
|
</view>
|
||||||
|
<view class="panel-light-action" @tap="handleClear">
|
||||||
|
<text class="panel-light-action-text">清除</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="swap-btn" @tap="handleSwap">
|
<view class="point-row">
|
||||||
<text class="swap-text">⇅</text>
|
<view class="point-card" @tap="openPicker('start')">
|
||||||
|
<view class="point-dot start"></view>
|
||||||
|
<text class="point-label">起点</text>
|
||||||
|
<text class="point-name" :class="{ placeholder: !startPoint }">
|
||||||
|
{{ startPoint?.name || '选择起点' }}
|
||||||
|
</text>
|
||||||
|
<text v-if="startPoint" class="point-meta">{{ pointMeta(startPoint) }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="swap-btn" @tap="handleSwap">
|
||||||
|
<text class="swap-text">⇅</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="point-card" @tap="openPicker('end')">
|
||||||
|
<view class="point-dot end"></view>
|
||||||
|
<text class="point-label">终点</text>
|
||||||
|
<text class="point-name" :class="{ placeholder: !endPoint }">
|
||||||
|
{{ endPoint?.name || '选择终点' }}
|
||||||
|
</text>
|
||||||
|
<text v-if="endPoint" class="point-meta">{{ pointMeta(endPoint) }}</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="point-card" @tap="openPicker('end')">
|
<view v-if="startPoint && endPoint" class="route-options">
|
||||||
<view class="point-dot end"></view>
|
<view class="route-options-header">
|
||||||
<text class="point-label">终点</text>
|
<text class="route-options-title">线路预览</text>
|
||||||
<text class="point-name" :class="{ placeholder: !endPoint }">
|
<text class="route-options-note">偏好选择</text>
|
||||||
{{ endPoint?.name || '选择终点' }}
|
</view>
|
||||||
</text>
|
<view class="route-option-list">
|
||||||
<text v-if="endPoint" class="point-meta">{{ pointMeta(endPoint) }}</text>
|
<view
|
||||||
|
v-for="option in routeOptions"
|
||||||
|
:key="option.id"
|
||||||
|
class="route-option"
|
||||||
|
:class="{ active: option.active, disabled: option.disabled }"
|
||||||
|
>
|
||||||
|
<text class="route-option-title">{{ option.title }}</text>
|
||||||
|
<text class="route-option-meta">{{ option.meta }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
|
|
||||||
<view v-if="loading || error || summary" class="panel-status">
|
<view v-if="loading || error || summary" class="panel-status">
|
||||||
<text v-if="loading" class="panel-status-text">路径加载中</text>
|
<text v-if="loading" class="panel-status-text">线路预览生成中</text>
|
||||||
<text v-else-if="error" class="panel-status-text error">{{ error }}</text>
|
<text v-else-if="error" class="panel-status-text error">{{ error }}</text>
|
||||||
<text v-else class="panel-status-text">{{ summary }}</text>
|
<text v-else class="panel-status-text">{{ summary }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view
|
<view
|
||||||
class="view-route-btn"
|
class="view-route-btn"
|
||||||
:class="{ disabled: !canViewRoute }"
|
:class="{ disabled: !canPrimaryAction }"
|
||||||
@tap="handlePrimaryAction"
|
@tap="handlePrimaryAction"
|
||||||
>
|
>
|
||||||
<text class="view-route-text">{{ primaryActionText }}</text>
|
<text class="view-route-text">{{ primaryActionText }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
<RoutePointPicker
|
<RoutePointPicker
|
||||||
:visible="pickerMode !== ''"
|
:visible="pickerMode !== ''"
|
||||||
@@ -67,7 +103,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
import RoutePointPicker, {
|
import RoutePointPicker, {
|
||||||
type RoutePointOption
|
type RoutePointOption
|
||||||
} from '@/components/navigation/RoutePointPicker.vue'
|
} from '@/components/navigation/RoutePointPicker.vue'
|
||||||
@@ -115,6 +151,7 @@ const emit = defineEmits<{
|
|||||||
}>()
|
}>()
|
||||||
|
|
||||||
const pickerMode = ref<PickerMode>('')
|
const pickerMode = ref<PickerMode>('')
|
||||||
|
const isCollapsed = ref(false)
|
||||||
|
|
||||||
const pickerTitle = computed(() => (
|
const pickerTitle = computed(() => (
|
||||||
pickerMode.value === 'start' ? '选择起点' : '选择终点'
|
pickerMode.value === 'start' ? '选择起点' : '选择终点'
|
||||||
@@ -137,10 +174,59 @@ const canViewRoute = computed(() => Boolean(
|
|||||||
&& props.startPoint.poiId !== props.endPoint.poiId
|
&& props.startPoint.poiId !== props.endPoint.poiId
|
||||||
))
|
))
|
||||||
|
|
||||||
|
const canPrimaryAction = computed(() => canViewRoute.value && !props.error)
|
||||||
|
|
||||||
const primaryActionText = computed(() => (
|
const primaryActionText = computed(() => (
|
||||||
props.hasRoutePreview ? '模拟导览' : '生成线路'
|
props.hasRoutePreview ? '模拟导览' : '线路预览'
|
||||||
))
|
))
|
||||||
|
|
||||||
|
const collapsedSummary = computed(() => {
|
||||||
|
if (props.startPoint && props.endPoint) {
|
||||||
|
return `${props.startPoint.name} → ${props.endPoint.name}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return '手动选择起点和终点'
|
||||||
|
})
|
||||||
|
|
||||||
|
const routeOptions = computed(() => {
|
||||||
|
const unavailable = Boolean(props.error)
|
||||||
|
const summaryText = props.summary || '选择后查看位置关系'
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: 'recommended',
|
||||||
|
title: '推荐路线',
|
||||||
|
meta: unavailable ? '当前为路线示意状态' : props.hasRoutePreview ? summaryText : '优先展示位置关系',
|
||||||
|
active: !unavailable,
|
||||||
|
disabled: unavailable
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'stairs',
|
||||||
|
title: '少走楼梯',
|
||||||
|
meta: '偏好预览,待路线数据验证',
|
||||||
|
active: false,
|
||||||
|
disabled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'elevator',
|
||||||
|
title: '电梯优先',
|
||||||
|
meta: '偏好预览,待路线数据验证',
|
||||||
|
active: false,
|
||||||
|
disabled: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
(visible) => {
|
||||||
|
if (visible) {
|
||||||
|
isCollapsed.value = false
|
||||||
|
} else {
|
||||||
|
pickerMode.value = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const pointMeta = (point: RoutePointOption) => {
|
const pointMeta = (point: RoutePointOption) => {
|
||||||
return point.categoryLabel
|
return point.categoryLabel
|
||||||
? `${point.floorLabel} · ${point.categoryLabel}`
|
? `${point.floorLabel} · ${point.categoryLabel}`
|
||||||
@@ -188,6 +274,18 @@ const handleClear = () => {
|
|||||||
emit('clear')
|
emit('clear')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const collapsePanel = () => {
|
||||||
|
isCollapsed.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const expandPanel = () => {
|
||||||
|
isCollapsed.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleCollapsed = () => {
|
||||||
|
isCollapsed.value = !isCollapsed.value
|
||||||
|
}
|
||||||
|
|
||||||
const handleViewRoute = () => {
|
const handleViewRoute = () => {
|
||||||
if (!canViewRoute.value || !props.startPoint || !props.endPoint) return
|
if (!canViewRoute.value || !props.startPoint || !props.endPoint) return
|
||||||
|
|
||||||
@@ -218,13 +316,17 @@ const handlePrimaryAction = () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: #ffffff;
|
background: rgba(255, 255, 255, 0.97);
|
||||||
border: 1px solid #ffffff;
|
border: 1px solid #e5e6de;
|
||||||
border-radius: 16px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 10px 28px rgba(36, 49, 42, 0.12);
|
box-shadow: 0 10px 24px rgba(36, 49, 42, 0.12);
|
||||||
z-index: 1003;
|
z-index: 1003;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.route-planner-panel.collapsed {
|
||||||
|
padding: 8px 10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.panel-handle {
|
.panel-handle {
|
||||||
width: 38px;
|
width: 38px;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
@@ -233,6 +335,39 @@ const handlePrimaryAction = () => {
|
|||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.panel-collapsed {
|
||||||
|
min-height: 44px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-collapsed-copy {
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-expand {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
height: 30px;
|
||||||
|
padding: 0 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #151713;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-expand-text {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--museum-accent);
|
||||||
|
}
|
||||||
|
|
||||||
.panel-header {
|
.panel-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
@@ -251,7 +386,7 @@ const handlePrimaryAction = () => {
|
|||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #000000;
|
color: #151713;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-summary {
|
.panel-summary {
|
||||||
@@ -263,7 +398,13 @@ const handlePrimaryAction = () => {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-clear {
|
.panel-actions {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-light-action {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
@@ -276,7 +417,7 @@ const handlePrimaryAction = () => {
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-clear-text {
|
.panel-light-action-text {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@@ -304,7 +445,7 @@ const handlePrimaryAction = () => {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: #f6f7f4;
|
background: #f6f7f4;
|
||||||
border: 1px solid #e5e6de;
|
border: 1px solid #e5e6de;
|
||||||
border-radius: 10px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.point-dot {
|
.point-dot {
|
||||||
@@ -369,7 +510,7 @@ const handlePrimaryAction = () => {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border: 1px solid #d7dad3;
|
border: 1px solid #d7dad3;
|
||||||
border-radius: 10px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swap-text {
|
.swap-text {
|
||||||
@@ -400,8 +541,8 @@ const handlePrimaryAction = () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background: #000000;
|
background: #151713;
|
||||||
border-radius: 10px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.view-route-btn.disabled {
|
.view-route-btn.disabled {
|
||||||
@@ -418,4 +559,84 @@ const handlePrimaryAction = () => {
|
|||||||
.view-route-btn.disabled .view-route-text {
|
.view-route-btn.disabled .view-route-text {
|
||||||
color: #8b8b84;
|
color: #8b8b84;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.route-options {
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 10px;
|
||||||
|
background: #f9fafb;
|
||||||
|
border: 1px solid #ecede7;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-options-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-options-title {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #151713;
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-options-note {
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 15px;
|
||||||
|
color: #8b8b84;
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-option-list {
|
||||||
|
margin-top: 8px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-option {
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 56px;
|
||||||
|
padding: 8px 6px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 3px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e5e6de;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-option.active {
|
||||||
|
border-color: #151713;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-option.disabled {
|
||||||
|
opacity: 0.68;
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-option-title {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #151713;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-option-meta {
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 14px;
|
||||||
|
color: #696962;
|
||||||
|
text-align: center;
|
||||||
|
display: -webkit-box;
|
||||||
|
overflow: hidden;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
applyRouteReadinessGate,
|
||||||
NAV_ASSET_BASE_URL,
|
NAV_ASSET_BASE_URL,
|
||||||
NAV_ROUTE_READINESS
|
NAV_ROUTE_READINESS
|
||||||
} from '@/domain/guideReadiness'
|
} from '@/domain/guideReadiness'
|
||||||
@@ -345,7 +346,7 @@ export const createStaticNavAssetsProvider = (
|
|||||||
},
|
},
|
||||||
async loadRouteReadiness() {
|
async loadRouteReadiness() {
|
||||||
const manifest = await provider.loadManifest()
|
const manifest = await provider.loadManifest()
|
||||||
return manifest.routeReadiness || NAV_ROUTE_READINESS
|
return applyRouteReadinessGate(manifest.routeReadiness || NAV_ROUTE_READINESS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type {
|
|||||||
|
|
||||||
export const NAV_ROUTE_GRAPH_READY = false
|
export const NAV_ROUTE_GRAPH_READY = false
|
||||||
|
|
||||||
export const NAV_ROUTE_UNAVAILABLE_MESSAGE = '当前支持位置预览、三维位置预览与室外入口参考'
|
export const NAV_ROUTE_UNAVAILABLE_MESSAGE = '当前暂不支持正式导航,可查看位置预览、路线示意和室外参考'
|
||||||
|
|
||||||
export const NAV_ROUTE_READINESS: GuideRouteReadiness = {
|
export const NAV_ROUTE_READINESS: GuideRouteReadiness = {
|
||||||
ready: NAV_ROUTE_GRAPH_READY,
|
ready: NAV_ROUTE_GRAPH_READY,
|
||||||
@@ -12,4 +12,34 @@ export const NAV_ROUTE_READINESS: GuideRouteReadiness = {
|
|||||||
requiredData: ['route_graph', 'nav_data']
|
requiredData: ['route_graph', 'nav_data']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const applyRouteReadinessGate = (
|
||||||
|
readiness: GuideRouteReadiness | null | undefined
|
||||||
|
): GuideRouteReadiness => {
|
||||||
|
const fallback = readiness || NAV_ROUTE_READINESS
|
||||||
|
const requiredData = fallback.requiredData?.length
|
||||||
|
? fallback.requiredData
|
||||||
|
: NAV_ROUTE_READINESS.requiredData
|
||||||
|
|
||||||
|
if (!NAV_ROUTE_GRAPH_READY) {
|
||||||
|
return {
|
||||||
|
...NAV_ROUTE_READINESS,
|
||||||
|
requiredData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fallback.ready) {
|
||||||
|
return {
|
||||||
|
...fallback,
|
||||||
|
ready: false,
|
||||||
|
message: fallback.message || NAV_ROUTE_UNAVAILABLE_MESSAGE,
|
||||||
|
requiredData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...fallback,
|
||||||
|
requiredData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const NAV_ASSET_BASE_URL = '/static/nav-assets/codex_nav_20260621_175310_museum_default_centerline_finalxy'
|
export const NAV_ASSET_BASE_URL = '/static/nav-assets/codex_nav_20260621_175310_museum_default_centerline_finalxy'
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
"usingComponents": true,
|
"usingComponents": true,
|
||||||
"permission": {
|
"permission": {
|
||||||
"scope.userLocation": {
|
"scope.userLocation": {
|
||||||
"desc": "您的位置信息将用于地图导航"
|
"desc": "您的位置信息将用于位置参考和地图展示"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
mode-status-tone="glass"
|
mode-status-tone="glass"
|
||||||
search-top="46px"
|
search-top="46px"
|
||||||
search-variant="home"
|
search-variant="home"
|
||||||
show-voice-icon
|
|
||||||
:show-mode-row="false"
|
:show-mode-row="false"
|
||||||
mode-top="104px"
|
mode-top="104px"
|
||||||
:map-type="guideMapType"
|
:map-type="guideMapType"
|
||||||
@@ -34,6 +33,7 @@
|
|||||||
show-floor-header
|
show-floor-header
|
||||||
:show-layer-mode-toggle="false"
|
:show-layer-mode-toggle="false"
|
||||||
:show-floor="is3DMode"
|
:show-floor="is3DMode"
|
||||||
|
tools-top="154px"
|
||||||
show-zoom-controls
|
show-zoom-controls
|
||||||
zoom-controls-top="560px"
|
zoom-controls-top="560px"
|
||||||
show-location-control
|
show-location-control
|
||||||
@@ -62,43 +62,68 @@
|
|||||||
<view
|
<view
|
||||||
v-if="selectedGuidePoi && !showRoutePlanner && !isSimulatingRoute"
|
v-if="selectedGuidePoi && !showRoutePlanner && !isSimulatingRoute"
|
||||||
class="guide-poi-card"
|
class="guide-poi-card"
|
||||||
|
:class="{ collapsed: isPoiCardCollapsed }"
|
||||||
>
|
>
|
||||||
<view class="poi-card-handle"></view>
|
<view class="poi-card-handle" @tap.stop="togglePoiCardCollapsed"></view>
|
||||||
<view class="poi-card-header">
|
<view v-if="isPoiCardCollapsed" class="poi-card-collapsed" @tap="expandPoiCard">
|
||||||
<view class="poi-card-copy">
|
<view class="poi-card-copy">
|
||||||
<text class="poi-card-title">{{ selectedGuidePoi.name }}</text>
|
<text class="poi-card-title">{{ selectedGuidePoi.name }}</text>
|
||||||
<text class="poi-card-subtitle">{{ selectedGuidePoiSubtitle }}</text>
|
<text class="poi-card-subtitle">位置预览 · 点按展开操作</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="poi-card-close" @tap="handleGuideSelectionClear">
|
<view class="poi-card-mini-action">
|
||||||
<text class="poi-card-close-text">×</text>
|
<text class="poi-card-mini-text">展开</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="poi-card-actions">
|
<template v-else>
|
||||||
<view class="poi-action primary" @tap="handlePreviewSelectedPoi">
|
<view class="poi-card-header">
|
||||||
<text class="poi-action-text">查看位置</text>
|
<view class="poi-card-copy">
|
||||||
|
<text class="poi-card-title">{{ selectedGuidePoi.name }}</text>
|
||||||
|
<text class="poi-card-subtitle">{{ selectedGuidePoiSubtitle }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="poi-card-header-actions">
|
||||||
|
<view class="poi-card-soft-action" @tap="collapsePoiCard">
|
||||||
|
<text class="poi-card-soft-text">收起</text>
|
||||||
|
</view>
|
||||||
|
<view class="poi-card-close" @tap="handleGuideSelectionClear">
|
||||||
|
<text class="poi-card-close-text">×</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="poi-action" @tap="handleSetSelectedPoiAsStart">
|
<view class="poi-card-actions">
|
||||||
<text class="poi-action-text">从这里出发</text>
|
<view class="poi-action primary" @tap="handlePreviewSelectedPoi">
|
||||||
|
<text class="poi-action-text">查看位置</text>
|
||||||
|
</view>
|
||||||
|
<view class="poi-action" @tap="handleSetSelectedPoiAsStart">
|
||||||
|
<text class="poi-action-text">从这里出发</text>
|
||||||
|
</view>
|
||||||
|
<view class="poi-action" @tap="handleSetSelectedPoiAsEnd">
|
||||||
|
<text class="poi-action-text">去这里</text>
|
||||||
|
</view>
|
||||||
|
<view class="poi-action" @tap="handleSelectedPoiRelated">
|
||||||
|
<text class="poi-action-text">相关讲解</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="poi-action" @tap="handleSetSelectedPoiAsEnd">
|
</template>
|
||||||
<text class="poi-action-text">去这里</text>
|
|
||||||
</view>
|
|
||||||
<view class="poi-action" @tap="handleSelectedPoiRelated">
|
|
||||||
<text class="poi-action-text">相关讲解</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="showGuideMoreMenu && !showRoutePlanner" class="guide-more-popover">
|
<view v-if="showGuideMoreMenu && !showRoutePlanner" class="guide-more-popover">
|
||||||
<view class="more-popover-handle"></view>
|
<view class="more-popover-handle"></view>
|
||||||
<view class="more-popover-grid">
|
<view class="more-popover-grid">
|
||||||
<view class="more-action" @tap="handleMoreRouteGuide">
|
<view class="more-action" @tap="handleMoreRouteGuide">
|
||||||
<text class="more-action-title">线路导览</text>
|
<text class="more-action-title">线路预览</text>
|
||||||
<text class="more-action-desc">室内到室内</text>
|
<text class="more-action-desc">室内到室内</text>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="more-action" @tap="handleMoreOutdoorReference">
|
||||||
|
<text class="more-action-title">室外参考</text>
|
||||||
|
<text class="more-action-desc">入口位置</text>
|
||||||
|
</view>
|
||||||
|
<view class="more-action" @tap="handleMoreRecommendedEntrance">
|
||||||
|
<text class="more-action-title">推荐入口</text>
|
||||||
|
<text class="more-action-desc">入口参考</text>
|
||||||
|
</view>
|
||||||
<view class="more-action" @tap="handleEnter3DMode">
|
<view class="more-action" @tap="handleEnter3DMode">
|
||||||
<text class="more-action-title">查看位置</text>
|
<text class="more-action-title">室内3D</text>
|
||||||
<text class="more-action-desc">三维预览</text>
|
<text class="more-action-desc">位置预览</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="more-action" @tap="handleMoreToggleLayer">
|
<view class="more-action" @tap="handleMoreToggleLayer">
|
||||||
<text class="more-action-title">{{ indoorLayerMode === 'multi' ? '单层' : '多层' }}</text>
|
<text class="more-action-title">{{ indoorLayerMode === 'multi' ? '单层' : '多层' }}</text>
|
||||||
@@ -115,7 +140,7 @@
|
|||||||
:point-options="routePointOptions"
|
:point-options="routePointOptions"
|
||||||
:has-route-preview="Boolean(activeRoutePreview)"
|
:has-route-preview="Boolean(activeRoutePreview)"
|
||||||
:loading="routePlanning"
|
:loading="routePlanning"
|
||||||
:error="routePlanError"
|
:error="routePanelError"
|
||||||
:summary="routePlanSummary"
|
:summary="routePlanSummary"
|
||||||
:picker-loading="routeTargetsLoading"
|
:picker-loading="routeTargetsLoading"
|
||||||
:picker-error="routeTargetsError"
|
:picker-error="routeTargetsError"
|
||||||
@@ -138,7 +163,7 @@
|
|||||||
<view v-if="isSimulatingRoute && activeRoutePreview" class="route-sim-step-bar">
|
<view v-if="isSimulatingRoute && activeRoutePreview" class="route-sim-step-bar">
|
||||||
<text class="route-sim-step-text">{{ routeSimulationStepText }}</text>
|
<text class="route-sim-step-text">{{ routeSimulationStepText }}</text>
|
||||||
<view class="route-sim-expand" @tap="showRoutePlanner = true">
|
<view class="route-sim-expand" @tap="showRoutePlanner = true">
|
||||||
<text class="route-sim-expand-text">展开步骤</text>
|
<text class="route-sim-expand-text">展开预览</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -275,9 +300,11 @@ const routeTargetsLoading = ref(false)
|
|||||||
const routeTargetsError = ref('')
|
const routeTargetsError = ref('')
|
||||||
const routePlanning = ref(false)
|
const routePlanning = ref(false)
|
||||||
const routePlanError = ref('')
|
const routePlanError = ref('')
|
||||||
|
const routePreviewUnavailableMessage = ref('')
|
||||||
const activeRoutePreview = ref<GuideRouteResult | null>(null)
|
const activeRoutePreview = ref<GuideRouteResult | null>(null)
|
||||||
const showGuideMoreMenu = ref(false)
|
const showGuideMoreMenu = ref(false)
|
||||||
const isSimulatingRoute = ref(false)
|
const isSimulatingRoute = ref(false)
|
||||||
|
const isPoiCardCollapsed = ref(false)
|
||||||
|
|
||||||
const indoorNavAssetBaseUrl = guideUseCase.getAssetBaseUrl()
|
const indoorNavAssetBaseUrl = guideUseCase.getAssetBaseUrl()
|
||||||
const indoorModelSource = guideUseCase.getModelSource()
|
const indoorModelSource = guideUseCase.getModelSource()
|
||||||
@@ -287,7 +314,7 @@ const indoorLayerMode = computed<LayerDisplayMode>(() => (
|
|||||||
indoorView.value === 'multi' ? 'multi' : 'single'
|
indoorView.value === 'multi' ? 'multi' : 'single'
|
||||||
))
|
))
|
||||||
const guide3DTools = computed(() => {
|
const guide3DTools = computed(() => {
|
||||||
return []
|
return is3DMode.value ? ['重置', '俯视', '斜视', '清除', '清路线'] : []
|
||||||
})
|
})
|
||||||
|
|
||||||
const routePlanSummary = computed(() => {
|
const routePlanSummary = computed(() => {
|
||||||
@@ -304,6 +331,8 @@ const routePlanSummary = computed(() => {
|
|||||||
return ''
|
return ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const routePanelError = computed(() => routePlanError.value || routePreviewUnavailableMessage.value)
|
||||||
|
|
||||||
const toRoutePointOption = (target: GuideRouteTarget): RoutePointOption => ({
|
const toRoutePointOption = (target: GuideRouteTarget): RoutePointOption => ({
|
||||||
poiId: target.poiId,
|
poiId: target.poiId,
|
||||||
name: target.name,
|
name: target.name,
|
||||||
@@ -321,9 +350,9 @@ const selectedGuidePoiSubtitle = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const routeSimulationStepText = computed(() => {
|
const routeSimulationStepText = computed(() => {
|
||||||
if (!activeRoutePreview.value) return '下一步 · 查看线路'
|
if (!activeRoutePreview.value) return '路线示意 · 查看预览'
|
||||||
const hasConnector = activeRoutePreview.value.connectorPoints.length > 0
|
const hasConnector = activeRoutePreview.value.connectorPoints.length > 0
|
||||||
return hasConnector ? '下一步 · 电梯上行 3/8' : '下一步 · 沿路线前行 1/4'
|
return hasConnector ? '路线示意 · 跨楼层连接点' : '路线示意 · 同层位置关系'
|
||||||
})
|
})
|
||||||
|
|
||||||
// 音频播放器状态
|
// 音频播放器状态
|
||||||
@@ -340,12 +369,20 @@ const syncTopTabToUrl = (tabId: GuideTopTab) => {
|
|||||||
if (typeof window === 'undefined') return
|
if (typeof window === 'undefined') return
|
||||||
if (!isIndexHashRoute()) return
|
if (!isIndexHashRoute()) return
|
||||||
|
|
||||||
const url = `#/?tab=${tabId}`
|
const url = `#/pages/index/index?tab=${tabId}`
|
||||||
if (window.location.hash !== url) {
|
if (window.location.hash !== url) {
|
||||||
window.history.replaceState(null, '', url)
|
window.history.replaceState(null, '', url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loadTopTabData = (tabId: GuideTopTab) => {
|
||||||
|
if (tabId === 'explain') {
|
||||||
|
void loadExplainExhibits()
|
||||||
|
} else {
|
||||||
|
void loadGuideFloors()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleTabChange = (tabId: GuideTopTab) => {
|
const handleTabChange = (tabId: GuideTopTab) => {
|
||||||
if (tabId === 'guide' && currentTab.value === 'explain') {
|
if (tabId === 'guide' && currentTab.value === 'explain') {
|
||||||
const lastLocationPreviewUrl = getLastGuideLocationPreviewUrl()
|
const lastLocationPreviewUrl = getLastGuideLocationPreviewUrl()
|
||||||
@@ -359,12 +396,7 @@ const handleTabChange = (tabId: GuideTopTab) => {
|
|||||||
|
|
||||||
currentTab.value = tabId
|
currentTab.value = tabId
|
||||||
syncTopTabToUrl(tabId)
|
syncTopTabToUrl(tabId)
|
||||||
|
loadTopTabData(tabId)
|
||||||
if (tabId === 'explain') {
|
|
||||||
void loadExplainExhibits()
|
|
||||||
} else {
|
|
||||||
void loadGuideFloors()
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('切换标签:', tabId)
|
console.log('切换标签:', tabId)
|
||||||
|
|
||||||
@@ -372,6 +404,7 @@ const handleTabChange = (tabId: GuideTopTab) => {
|
|||||||
showGuideMoreMenu.value = false
|
showGuideMoreMenu.value = false
|
||||||
showRoutePlanner.value = false
|
showRoutePlanner.value = false
|
||||||
isSimulatingRoute.value = false
|
isSimulatingRoute.value = false
|
||||||
|
isPoiCardCollapsed.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const syncTopTabFromHash = () => {
|
const syncTopTabFromHash = () => {
|
||||||
@@ -379,16 +412,27 @@ const syncTopTabFromHash = () => {
|
|||||||
|
|
||||||
const tab = topTabFromHash()
|
const tab = topTabFromHash()
|
||||||
if (tab) {
|
if (tab) {
|
||||||
|
const tabChanged = currentTab.value !== tab
|
||||||
currentTab.value = tab
|
currentTab.value = tab
|
||||||
|
loadTopTabData(tab)
|
||||||
|
if (tabChanged) {
|
||||||
|
showGuideMoreMenu.value = false
|
||||||
|
showRoutePlanner.value = false
|
||||||
|
isSimulatingRoute.value = false
|
||||||
|
isPoiCardCollapsed.value = false
|
||||||
|
}
|
||||||
|
syncTopTabToUrl(tab)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
syncTopTabToUrl(currentTab.value)
|
syncTopTabToUrl(currentTab.value)
|
||||||
|
loadTopTabData(currentTab.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (typeof window === 'undefined') return
|
if (typeof window === 'undefined') return
|
||||||
|
|
||||||
|
syncTopTabFromHash()
|
||||||
window.addEventListener('hashchange', syncTopTabFromHash)
|
window.addEventListener('hashchange', syncTopTabFromHash)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -407,11 +451,7 @@ onLoad((options: any = {}) => {
|
|||||||
syncTopTabToUrl(currentTab.value)
|
syncTopTabToUrl(currentTab.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentTab.value === 'explain') {
|
loadTopTabData(currentTab.value)
|
||||||
void loadExplainExhibits()
|
|
||||||
} else {
|
|
||||||
void loadGuideFloors()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const loadGuideFloors = async () => {
|
const loadGuideFloors = async () => {
|
||||||
@@ -462,6 +502,7 @@ const handleFloorChange = (floor: string) => {
|
|||||||
activeGuideFloor.value = floor
|
activeGuideFloor.value = floor
|
||||||
indoorView.value = 'floor'
|
indoorView.value = 'floor'
|
||||||
selectedGuidePoi.value = null
|
selectedGuidePoi.value = null
|
||||||
|
isPoiCardCollapsed.value = false
|
||||||
console.log('切换楼层:', floor)
|
console.log('切换楼层:', floor)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,6 +510,7 @@ const handleIndoorViewChange = (view: GuideIndoorView) => {
|
|||||||
indoorView.value = view
|
indoorView.value = view
|
||||||
if (view !== 'floor') {
|
if (view !== 'floor') {
|
||||||
selectedGuidePoi.value = null
|
selectedGuidePoi.value = null
|
||||||
|
isPoiCardCollapsed.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,11 +519,13 @@ const handleAutoSwitch = (event: { from: 'overview' | 'floor'; to: 'overview' |
|
|||||||
indoorView.value = event.to
|
indoorView.value = event.to
|
||||||
if (event.to !== 'floor') {
|
if (event.to !== 'floor') {
|
||||||
selectedGuidePoi.value = null
|
selectedGuidePoi.value = null
|
||||||
|
isPoiCardCollapsed.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleGuidePoiClick = (poi: GuideRenderPoi) => {
|
const handleGuidePoiClick = (poi: GuideRenderPoi) => {
|
||||||
selectedGuidePoi.value = poi
|
selectedGuidePoi.value = poi
|
||||||
|
isPoiCardCollapsed.value = false
|
||||||
indoorView.value = 'floor'
|
indoorView.value = 'floor'
|
||||||
showGuideMoreMenu.value = false
|
showGuideMoreMenu.value = false
|
||||||
isSimulatingRoute.value = false
|
isSimulatingRoute.value = false
|
||||||
@@ -491,6 +535,19 @@ const handleGuidePoiClick = (poi: GuideRenderPoi) => {
|
|||||||
|
|
||||||
const handleGuideSelectionClear = () => {
|
const handleGuideSelectionClear = () => {
|
||||||
selectedGuidePoi.value = null
|
selectedGuidePoi.value = null
|
||||||
|
isPoiCardCollapsed.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const collapsePoiCard = () => {
|
||||||
|
isPoiCardCollapsed.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const expandPoiCard = () => {
|
||||||
|
isPoiCardCollapsed.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const togglePoiCardCollapsed = () => {
|
||||||
|
isPoiCardCollapsed.value = !isPoiCardCollapsed.value
|
||||||
}
|
}
|
||||||
|
|
||||||
const findRouteOptionForPoi = async (poi: GuideRenderPoi) => {
|
const findRouteOptionForPoi = async (poi: GuideRenderPoi) => {
|
||||||
@@ -574,16 +631,30 @@ const loadRouteTargets = async (keyword = '') => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const refreshRouteReadinessState = async () => {
|
||||||
|
try {
|
||||||
|
const readiness = await guideRouteUseCase.getRouteReadiness()
|
||||||
|
routePreviewUnavailableMessage.value = readiness.ready
|
||||||
|
? ''
|
||||||
|
: readiness.message || '当前暂不支持正式导航,可查看位置预览和路线示意'
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('读取路线能力状态失败:', error)
|
||||||
|
routePreviewUnavailableMessage.value = '当前暂不支持正式导航,可查看位置预览和路线示意'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const openRoutePlanner = () => {
|
const openRoutePlanner = () => {
|
||||||
showRoutePlanner.value = true
|
showRoutePlanner.value = true
|
||||||
is3DMode.value = true
|
is3DMode.value = true
|
||||||
indoorView.value = indoorView.value === 'overview' ? 'floor' : indoorView.value
|
indoorView.value = indoorView.value === 'overview' ? 'floor' : indoorView.value
|
||||||
guideOutdoorState.value = 'home'
|
guideOutdoorState.value = 'home'
|
||||||
showGuideMoreMenu.value = false
|
showGuideMoreMenu.value = false
|
||||||
|
isPoiCardCollapsed.value = false
|
||||||
isSimulatingRoute.value = false
|
isSimulatingRoute.value = false
|
||||||
if (!routePointOptions.value.length) {
|
if (!routePointOptions.value.length) {
|
||||||
void loadRouteTargets()
|
void loadRouteTargets()
|
||||||
}
|
}
|
||||||
|
void refreshRouteReadinessState()
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearRoutePreviewState = () => {
|
const clearRoutePreviewState = () => {
|
||||||
@@ -639,6 +710,13 @@ const handleRouteView = async ({ startPoint, endPoint }: { startPoint: RoutePoin
|
|||||||
isSimulatingRoute.value = false
|
isSimulatingRoute.value = false
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
await refreshRouteReadinessState()
|
||||||
|
if (routePreviewUnavailableMessage.value) {
|
||||||
|
activeRoutePreview.value = null
|
||||||
|
routePlanError.value = routePreviewUnavailableMessage.value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const result = await guideRouteUseCase.planRoute({
|
const result = await guideRouteUseCase.planRoute({
|
||||||
startPoiId: startPoint.poiId,
|
startPoiId: startPoint.poiId,
|
||||||
endPoiId: endPoint.poiId
|
endPoiId: endPoint.poiId
|
||||||
@@ -666,6 +744,7 @@ const handleRouteSimulate = () => {
|
|||||||
if (!activeRoutePreview.value) return
|
if (!activeRoutePreview.value) return
|
||||||
showRoutePlanner.value = false
|
showRoutePlanner.value = false
|
||||||
selectedGuidePoi.value = null
|
selectedGuidePoi.value = null
|
||||||
|
isPoiCardCollapsed.value = false
|
||||||
showGuideMoreMenu.value = false
|
showGuideMoreMenu.value = false
|
||||||
isSimulatingRoute.value = true
|
isSimulatingRoute.value = true
|
||||||
}
|
}
|
||||||
@@ -689,6 +768,7 @@ const handleModeChange = (mode: '2d' | '3d') => {
|
|||||||
indoorView.value = 'overview'
|
indoorView.value = 'overview'
|
||||||
guideOutdoorState.value = 'home'
|
guideOutdoorState.value = 'home'
|
||||||
selectedGuidePoi.value = null
|
selectedGuidePoi.value = null
|
||||||
|
isPoiCardCollapsed.value = false
|
||||||
showRoutePlanner.value = false
|
showRoutePlanner.value = false
|
||||||
showGuideMoreMenu.value = false
|
showGuideMoreMenu.value = false
|
||||||
isSimulatingRoute.value = false
|
isSimulatingRoute.value = false
|
||||||
@@ -728,7 +808,28 @@ const handleMoreRouteGuide = () => {
|
|||||||
openRoutePlanner()
|
openRoutePlanner()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const openOutdoorReference = (state: 'home' | 'entrance') => {
|
||||||
|
is3DMode.value = false
|
||||||
|
guideOutdoorState.value = state
|
||||||
|
indoorView.value = 'overview'
|
||||||
|
selectedGuidePoi.value = null
|
||||||
|
isPoiCardCollapsed.value = false
|
||||||
|
showRoutePlanner.value = false
|
||||||
|
showGuideMoreMenu.value = false
|
||||||
|
clearRoutePreviewState()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMoreOutdoorReference = () => {
|
||||||
|
openOutdoorReference('home')
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMoreRecommendedEntrance = () => {
|
||||||
|
openOutdoorReference('entrance')
|
||||||
|
}
|
||||||
|
|
||||||
const handleMoreToggleLayer = () => {
|
const handleMoreToggleLayer = () => {
|
||||||
|
is3DMode.value = true
|
||||||
|
guideOutdoorState.value = 'home'
|
||||||
indoorView.value = indoorLayerMode.value === 'multi' ? 'floor' : 'multi'
|
indoorView.value = indoorLayerMode.value === 'multi' ? 'floor' : 'multi'
|
||||||
showGuideMoreMenu.value = false
|
showGuideMoreMenu.value = false
|
||||||
}
|
}
|
||||||
@@ -739,15 +840,7 @@ const guideOutdoorVariant = computed(() => (
|
|||||||
guideOutdoorState.value === 'entrance' ? 'entrance' : 'home'
|
guideOutdoorState.value === 'entrance' ? 'entrance' : 'home'
|
||||||
))
|
))
|
||||||
const guideSearchText = computed(() => {
|
const guideSearchText = computed(() => {
|
||||||
if (is3DMode.value) {
|
return '请输入地点进行搜索'
|
||||||
return '搜索设施、展厅、入口'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (guideOutdoorState.value === 'entrance') {
|
|
||||||
return '选择入口或停车位置'
|
|
||||||
}
|
|
||||||
|
|
||||||
return '搜索设施、展厅、入口'
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 音频相关处理
|
// 音频相关处理
|
||||||
@@ -897,9 +990,9 @@ const handleExplainLocationClick = async (exhibit: Exhibit) => {
|
|||||||
bottom: calc(env(safe-area-inset-bottom) + 30px);
|
bottom: calc(env(safe-area-inset-bottom) + 30px);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: rgba(255, 255, 255, 0.96);
|
background: rgba(255, 255, 255, 0.96);
|
||||||
border: 1px solid #ffffff;
|
border: 1px solid #e5e6de;
|
||||||
border-radius: 16px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 10px 28px rgba(36, 49, 42, 0.12);
|
box-shadow: 0 10px 24px rgba(36, 49, 42, 0.12);
|
||||||
z-index: 1003;
|
z-index: 1003;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -907,6 +1000,10 @@ const handleExplainLocationClick = async (exhibit: Exhibit) => {
|
|||||||
padding: 10px 14px 14px;
|
padding: 10px 14px 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.guide-poi-card.collapsed {
|
||||||
|
padding: 8px 10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.poi-card-handle,
|
.poi-card-handle,
|
||||||
.more-popover-handle {
|
.more-popover-handle {
|
||||||
width: 38px;
|
width: 38px;
|
||||||
@@ -916,6 +1013,32 @@ const handleExplainLocationClick = async (exhibit: Exhibit) => {
|
|||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.poi-card-collapsed {
|
||||||
|
min-height: 44px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poi-card-mini-action {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
height: 30px;
|
||||||
|
padding: 0 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #151713;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poi-card-mini-text {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--museum-accent);
|
||||||
|
}
|
||||||
|
|
||||||
.poi-card-header {
|
.poi-card-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
@@ -946,6 +1069,31 @@ const handleExplainLocationClick = async (exhibit: Exhibit) => {
|
|||||||
color: #696962;
|
color: #696962;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.poi-card-header-actions {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poi-card-soft-action {
|
||||||
|
height: 28px;
|
||||||
|
padding: 0 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid #e4e5df;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f5f7f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poi-card-soft-text {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #545861;
|
||||||
|
}
|
||||||
|
|
||||||
.poi-card-close {
|
.poi-card-close {
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
@@ -979,12 +1127,12 @@ const handleExplainLocationClick = async (exhibit: Exhibit) => {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: #f6f7f4;
|
background: #f6f7f4;
|
||||||
border: 1px solid #e5e6de;
|
border: 1px solid #e5e6de;
|
||||||
border-radius: 10px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poi-action.primary {
|
.poi-action.primary {
|
||||||
background: #000000;
|
background: #151713;
|
||||||
border-color: #000000;
|
border-color: #151713;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poi-action-text {
|
.poi-action-text {
|
||||||
@@ -1019,7 +1167,7 @@ const handleExplainLocationClick = async (exhibit: Exhibit) => {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: #f6f7f4;
|
background: #f6f7f4;
|
||||||
border: 1px solid #e5e6de;
|
border: 1px solid #e5e6de;
|
||||||
border-radius: 10px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.more-action-title {
|
.more-action-title {
|
||||||
@@ -1048,7 +1196,7 @@ const handleExplainLocationClick = async (exhibit: Exhibit) => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: rgba(21, 23, 19, 0.86);
|
background: rgba(21, 23, 19, 0.86);
|
||||||
border-radius: 17px;
|
border-radius: 8px;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
z-index: 1003;
|
z-index: 1003;
|
||||||
}
|
}
|
||||||
@@ -1077,9 +1225,9 @@ const handleExplainLocationClick = async (exhibit: Exhibit) => {
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: rgba(255, 255, 255, 0.96);
|
background: rgba(255, 255, 255, 0.96);
|
||||||
border: 1px solid #ffffff;
|
border: 1px solid #e5e6de;
|
||||||
border-radius: 16px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 10px 28px rgba(36, 49, 42, 0.12);
|
box-shadow: 0 10px 24px rgba(36, 49, 42, 0.12);
|
||||||
z-index: 1003;
|
z-index: 1003;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1103,7 +1251,7 @@ const handleExplainLocationClick = async (exhibit: Exhibit) => {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: #000000;
|
background: #000000;
|
||||||
border-radius: 10px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.route-sim-expand-text {
|
.route-sim-expand-text {
|
||||||
|
|||||||
@@ -28,16 +28,16 @@
|
|||||||
<view class="error-icon">
|
<view class="error-icon">
|
||||||
<text class="error-icon-mark">!</text>
|
<text class="error-icon-mark">!</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="error-title">无法确认当前位置</text>
|
<text class="error-title">无法自动获取起点</text>
|
||||||
<text class="error-desc">
|
<text class="error-desc">
|
||||||
可以返回位置预览,或手动选择当前楼层与区域,继续完成室内三维导览。
|
可以返回位置预览,或手动选择预览起点所在楼层与区域。
|
||||||
</text>
|
</text>
|
||||||
<view class="error-actions">
|
<view class="error-actions">
|
||||||
<view class="dialog-btn secondary" @tap="handleRelocate">
|
<view class="dialog-btn secondary" @tap="handleRelocate">
|
||||||
<text class="dialog-btn-text">返回预览</text>
|
<text class="dialog-btn-text">返回预览</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="dialog-btn primary" @tap="handleManualSelect">
|
<view class="dialog-btn primary" @tap="handleManualSelect">
|
||||||
<text class="dialog-btn-text">手动选择</text>
|
<text class="dialog-btn-text">手动选择起点</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="manual-location-btn primary" @tap="handleConfirmLocation">
|
<view class="manual-location-btn primary" @tap="handleConfirmLocation">
|
||||||
<text class="manual-location-btn-text">
|
<text class="manual-location-btn-text">
|
||||||
确认当前位置:{{ selectedManualFloor }} {{ selectedManualArea }}
|
确认预览起点:{{ selectedManualFloor }} {{ selectedManualArea }}
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import type {
|
|||||||
GuideRouteTarget
|
GuideRouteTarget
|
||||||
} from '@/domain/museum'
|
} from '@/domain/museum'
|
||||||
import {
|
import {
|
||||||
|
applyRouteReadinessGate,
|
||||||
|
NAV_ROUTE_UNAVAILABLE_MESSAGE,
|
||||||
NAV_ROUTE_READINESS
|
NAV_ROUTE_READINESS
|
||||||
} from '@/domain/guideReadiness'
|
} from '@/domain/guideReadiness'
|
||||||
import {
|
import {
|
||||||
@@ -139,20 +141,24 @@ export class StaticGuideRouteRepository implements GuideRouteRepository {
|
|||||||
constructor(private readonly provider: StaticNavAssetsProvider = defaultStaticNavAssetsProvider) {}
|
constructor(private readonly provider: StaticNavAssetsProvider = defaultStaticNavAssetsProvider) {}
|
||||||
|
|
||||||
async getRouteReadiness() {
|
async getRouteReadiness() {
|
||||||
const manifestReadiness = await this.provider.loadRouteReadiness()
|
const hardGateReadiness = applyRouteReadinessGate(NAV_ROUTE_READINESS)
|
||||||
|
if (!hardGateReadiness.ready) return hardGateReadiness
|
||||||
|
|
||||||
|
const manifestReadiness = applyRouteReadinessGate(await this.provider.loadRouteReadiness()
|
||||||
.catch(() => NAV_ROUTE_READINESS)
|
.catch(() => NAV_ROUTE_READINESS)
|
||||||
|
)
|
||||||
|
|
||||||
if (!manifestReadiness.ready) return manifestReadiness
|
if (!manifestReadiness.ready) return manifestReadiness
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const dataset = await this.loadDataset()
|
const dataset = await this.loadDataset()
|
||||||
if (!dataset.nodes.size || !dataset.edges.length || !dataset.anchorsByPoiId.size) {
|
if (!dataset.nodes.size || !dataset.edges.length || !dataset.anchorsByPoiId.size) {
|
||||||
return createUnavailableReadiness('路线数据不完整,暂不可查看路径')
|
return createUnavailableReadiness('路线示意数据不完整,当前暂不支持正式导航')
|
||||||
}
|
}
|
||||||
|
|
||||||
return manifestReadiness
|
return applyRouteReadinessGate(manifestReadiness)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return createUnavailableReadiness(error instanceof Error ? error.message : '路线数据加载失败')
|
return createUnavailableReadiness(error instanceof Error ? error.message : '路线示意数据加载失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,6 +168,14 @@ export class StaticGuideRouteRepository implements GuideRouteRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async findRoute(startPoiId: string, endPoiId: string) {
|
async findRoute(startPoiId: string, endPoiId: string) {
|
||||||
|
const readiness = await this.getRouteReadiness()
|
||||||
|
if (!readiness.ready) {
|
||||||
|
throw new GuideRouteError(
|
||||||
|
'ROUTE_DATA_UNAVAILABLE',
|
||||||
|
readiness.message || NAV_ROUTE_UNAVAILABLE_MESSAGE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (!startPoiId || !endPoiId) {
|
if (!startPoiId || !endPoiId) {
|
||||||
throw new GuideRouteError('ROUTE_TARGET_MISSING', '请选择起点和终点')
|
throw new GuideRouteError('ROUTE_TARGET_MISSING', '请选择起点和终点')
|
||||||
}
|
}
|
||||||
@@ -175,11 +189,11 @@ export class StaticGuideRouteRepository implements GuideRouteRepository {
|
|||||||
const endAnchor = dataset.anchorsByPoiId.get(endPoiId)
|
const endAnchor = dataset.anchorsByPoiId.get(endPoiId)
|
||||||
|
|
||||||
if (!startAnchor) {
|
if (!startAnchor) {
|
||||||
throw new GuideRouteError('ROUTE_TARGET_UNANCHORED', '起点暂无可用路径锚点')
|
throw new GuideRouteError('ROUTE_TARGET_UNANCHORED', '起点暂无可用路线示意锚点')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!endAnchor) {
|
if (!endAnchor) {
|
||||||
throw new GuideRouteError('ROUTE_TARGET_UNANCHORED', '终点暂无可用路径锚点')
|
throw new GuideRouteError('ROUTE_TARGET_UNANCHORED', '终点暂无可用路线示意锚点')
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchResult = this.findShortestPath(startAnchor.routeNodeId, endAnchor.routeNodeId)
|
const searchResult = this.findShortestPath(startAnchor.routeNodeId, endAnchor.routeNodeId)
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import {
|
|||||||
guideRouteRepository,
|
guideRouteRepository,
|
||||||
type GuideRouteRepository
|
type GuideRouteRepository
|
||||||
} from '@/repositories/GuideRouteRepository'
|
} from '@/repositories/GuideRouteRepository'
|
||||||
|
import {
|
||||||
|
applyRouteReadinessGate
|
||||||
|
} from '@/domain/guideReadiness'
|
||||||
|
|
||||||
const toSearchText = (target: GuideRouteTarget) => [
|
const toSearchText = (target: GuideRouteTarget) => [
|
||||||
target.name,
|
target.name,
|
||||||
@@ -38,7 +41,7 @@ export class GuideRouteUseCase {
|
|||||||
async getRouteReadiness() {
|
async getRouteReadiness() {
|
||||||
if (this.readinessCache) return this.readinessCache
|
if (this.readinessCache) return this.readinessCache
|
||||||
|
|
||||||
this.readinessCache = await this.repository.getRouteReadiness()
|
this.readinessCache = applyRouteReadinessGate(await this.repository.getRouteReadiness())
|
||||||
return this.readinessCache
|
return this.readinessCache
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +67,7 @@ export class GuideRouteUseCase {
|
|||||||
if (!readiness.ready) {
|
if (!readiness.ready) {
|
||||||
return {
|
return {
|
||||||
route: null,
|
route: null,
|
||||||
error: readiness.message || '路线数据暂不可用'
|
error: readiness.message || '当前暂不支持正式导航,可查看位置预览和路线示意'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
type GuideModelRepository
|
type GuideModelRepository
|
||||||
} from '@/repositories/GuideModelRepository'
|
} from '@/repositories/GuideModelRepository'
|
||||||
import {
|
import {
|
||||||
|
applyRouteReadinessGate,
|
||||||
NAV_ROUTE_READINESS
|
NAV_ROUTE_READINESS
|
||||||
} from '@/domain/guideReadiness'
|
} from '@/domain/guideReadiness'
|
||||||
|
|
||||||
@@ -81,12 +82,12 @@ export class GuideUseCase {
|
|||||||
|
|
||||||
const routeReadiness = await this.guide.getRouteReadiness()
|
const routeReadiness = await this.guide.getRouteReadiness()
|
||||||
.catch(() => NAV_ROUTE_READINESS)
|
.catch(() => NAV_ROUTE_READINESS)
|
||||||
this.routeReadinessCache = NAV_ROUTE_READINESS.ready ? routeReadiness : NAV_ROUTE_READINESS
|
this.routeReadinessCache = applyRouteReadinessGate(routeReadiness)
|
||||||
return this.routeReadinessCache
|
return this.routeReadinessCache
|
||||||
}
|
}
|
||||||
|
|
||||||
getRouteReadinessSnapshot(): GuideRouteReadiness {
|
getRouteReadinessSnapshot(): GuideRouteReadiness {
|
||||||
return this.routeReadinessCache || NAV_ROUTE_READINESS
|
return this.routeReadinessCache || applyRouteReadinessGate(NAV_ROUTE_READINESS)
|
||||||
}
|
}
|
||||||
|
|
||||||
getFloors() {
|
getFloors() {
|
||||||
|
|||||||
Reference in New Issue
Block a user