diff --git a/src/components/map/ThreeMap.vue b/src/components/map/ThreeMap.vue
index a633500..a072534 100644
--- a/src/components/map/ThreeMap.vue
+++ b/src/components/map/ThreeMap.vue
@@ -1158,7 +1158,7 @@ const getMultiFloorVerticalGap = (items: MultiFloorModelItem[]) => {
const maxFloorHeight = Math.max(...items.map((item) => item.size.y), 1)
const maxFootprint = Math.max(...items.map((item) => Math.max(item.size.x, item.size.z)), 1)
- return Math.max(maxFloorHeight * 3.2, maxFootprint * 0.2, 42)
+ return Math.max(maxFloorHeight * 5.2, maxFootprint * 0.32, 68)
}
const applyMultiFloorLayout = (items: MultiFloorModelItem[]) => {
@@ -1332,6 +1332,42 @@ const setCameraPreset = (preset: CameraPreset) => {
}
}
+const zoomCamera = (direction: 'in' | 'out') => {
+ if (!camera || !controls) return
+
+ const offset = camera.position.clone().sub(controls.target)
+ const currentDistance = offset.length()
+ const zoomFactor = direction === 'in' ? 0.72 : 1.28
+ const minDistance = controls.minDistance || 2
+ const maxDistance = controls.maxDistance || 1200
+ const nextDistance = Math.min(maxDistance, Math.max(minDistance, currentDistance * zoomFactor))
+
+ if (!Number.isFinite(nextDistance) || nextDistance <= 0) return
+
+ offset.setLength(nextDistance)
+ camera.position.copy(controls.target).add(offset)
+ controls.update()
+
+ if (direction === 'in' && activeView.value === 'overview' && !isLoading.value && !isAutoSwitchLocked) {
+ isAutoSwitchLocked = true
+ lastAutoSwitchTime = Date.now()
+
+ void runAutoSwitchLoad(
+ {
+ from: 'overview',
+ to: 'floor',
+ trigger: 'zoom-in',
+ distance: nextDistance
+ },
+ () => loadFloor(currentFloor.value || props.initialFloorId || 'L1'),
+ '单楼层模型加载失败'
+ )
+ return
+ }
+
+ handleControlChange()
+}
+
const loadOverview = async () => {
const packageData = renderPackage.value
if (!packageData || !scene) return
@@ -1447,36 +1483,6 @@ const loadMultiFloor = async () => {
activeView.value = 'multi'
clearSceneData()
setProgress(18, '正在加载多层展示模型...')
- const packageData = renderPackage.value
- const allFloorsShareOverview = Boolean(packageData)
- && floorIndex.value.every((floor) => floor.sharedModelAsset && floor.modelUrl === packageData!.overviewModelUrl)
-
- if (allFloorsShareOverview && packageData) {
- if (!attachCachedSharedModel(packageData.overviewModelUrl, 'GuideMultiFloorModel', loadToken)) {
- const gltf = await loadModel(packageData.overviewModelUrl, '正在加载多层展示模型', loadToken)
- assertCurrentModelLoad(loadToken, gltf.scene)
-
- const targetScene = scene
- if (!targetScene) {
- disposeObject(gltf.scene)
- throw createStaleModelLoadError()
- }
-
- activeModel = gltf.scene
- activeModel.name = 'GuideMultiFloorModel'
- prepareModel(activeModel)
- applyModelVisibilityForView(activeModel, 'multi')
- cachedOverviewModel = activeModel
- cachedSharedModelUrl = packageData.overviewModelUrl
- targetScene.add(activeModel)
- }
-
- if (!activeModel) throw createStaleModelLoadError()
- fitCameraToObject(activeModel)
- refreshPoiVisibilityByDistance()
- renderRoutePreview()
- return
- }
const group = new THREE.Group()
group.name = 'GuideMultiFloorModel'
@@ -2310,6 +2316,7 @@ defineExpose({
showMultiFloor,
resetCamera,
setCameraPreset,
+ zoomCamera,
focusTargetPoi: (request: TargetPoiFocusRequest) => {
queueTargetFocus(request)
},
diff --git a/src/components/navigation/GuideMapShell.vue b/src/components/navigation/GuideMapShell.vue
index fecdf80..2d3b975 100644
--- a/src/components/navigation/GuideMapShell.vue
+++ b/src/components/navigation/GuideMapShell.vue
@@ -29,6 +29,8 @@
:target-focus-distance-factor="targetFocusDistanceFactor"
:route-preview="routePreview"
:show-route="showRoute"
+ :auto-switch-threshold-low="autoSwitchThresholdLow"
+ :auto-switch-threshold-high="autoSwitchThresholdHigh"
@floor-change="handleThreeFloorChange"
@poi-click="handlePoiClick"
@selection-clear="handleSelectionClear"
@@ -138,7 +140,12 @@
:class="`side-${floorSide}`"
:style="floorSwitcherStyle"
>
-
-
-
+
+
线路预览
@@ -122,14 +134,6 @@
推荐入口
入口参考
-
- 室内3D
- 位置预览
-
-
- {{ indoorLayerMode === 'multi' ? '单层' : '多层' }}
- 切换楼层
-
@@ -153,6 +157,7 @@
@clear="handleRouteClear"
@view-route="handleRouteView"
@simulate-guide="handleRouteSimulate"
+ @back="handleRouteBack"
/>
@@ -262,7 +267,7 @@ const initialTopTab = (): GuideTopTab => topTabFromHash() || 'guide'
// 3D 模式状态
const is3DMode = ref(true)
const guideOutdoorState = ref<'home' | 'entrance'>('home')
-const indoorView = ref('floor')
+const indoorView = ref('overview')
const activeGuideFloor = ref('1F')
const selectedGuidePoi = ref(null)
@@ -281,6 +286,8 @@ const activeRoutePreview = ref(null)
const showGuideMoreMenu = ref(false)
const isSimulatingRoute = ref(false)
const isPoiCardCollapsed = ref(false)
+const guideMoreTouchStartY = ref(0)
+const guideMoreTouchCurrentY = ref(0)
const indoorNavAssetBaseUrl = guideUseCase.getAssetBaseUrl()
const indoorModelSource = guideUseCase.getModelSource()
@@ -530,8 +537,9 @@ const buildExplainHallItems = (
}
const handleGuideSearchTap = () => {
- uni.navigateTo({
- url: '/pages/search/index'
+ uni.showToast({
+ title: '功能待开放',
+ icon: 'none'
})
}
@@ -707,6 +715,19 @@ const handleRouteClear = () => {
clearRoutePreviewState()
}
+const handleRouteBack = () => {
+ if (activeRoutePreview.value) {
+ activeRoutePreview.value = null
+ routePlanError.value = ''
+ isSimulatingRoute.value = false
+ showRoutePlanner.value = true
+ return
+ }
+
+ showRoutePlanner.value = false
+ isSimulatingRoute.value = false
+}
+
const handleRouteStartPointChange = (point: RoutePointOption | null) => {
routeStartPoint.value = point
clearRoutePreviewState()
@@ -789,7 +810,7 @@ const handleRouteSimulate = () => {
// 进入 3D 室内模式
const handleEnter3DMode = () => {
console.log('进入 3D 室内模式')
- indoorView.value = 'floor'
+ indoorView.value = 'overview'
is3DMode.value = true
guideOutdoorState.value = 'home'
showGuideMoreMenu.value = false
@@ -811,7 +832,7 @@ const handleModeChange = (mode: '2d' | '3d') => {
isSimulatingRoute.value = false
clearRoutePreviewState()
} else {
- indoorView.value = 'floor'
+ indoorView.value = 'overview'
if (!routePointOptions.value.length) {
void loadRouteTargets()
}
@@ -841,6 +862,54 @@ const handleGuideMoreClick = () => {
selectedGuidePoi.value = null
}
+const closeGuideMoreMenu = () => {
+ showGuideMoreMenu.value = false
+}
+
+const getGestureClientY = (event: TouchEvent | MouseEvent) => {
+ if ('changedTouches' in event) {
+ return event.changedTouches?.[0]?.clientY
+ ?? event.touches?.[0]?.clientY
+ ?? 0
+ }
+
+ return event.clientY
+}
+
+const handleGuideMoreTouchStart = (event: TouchEvent) => {
+ const clientY = getGestureClientY(event)
+ guideMoreTouchStartY.value = clientY
+ guideMoreTouchCurrentY.value = clientY
+}
+
+const handleGuideMoreTouchMove = (event: TouchEvent) => {
+ guideMoreTouchCurrentY.value = getGestureClientY(event)
+}
+
+const handleGuideMoreTouchEnd = (event: TouchEvent) => {
+ guideMoreTouchCurrentY.value = getGestureClientY(event)
+ if (guideMoreTouchCurrentY.value - guideMoreTouchStartY.value > 48) {
+ closeGuideMoreMenu()
+ }
+}
+
+const handleGuideMoreMouseStart = (event: MouseEvent) => {
+ const clientY = getGestureClientY(event)
+ guideMoreTouchStartY.value = clientY
+ guideMoreTouchCurrentY.value = clientY
+}
+
+const handleGuideMoreMouseMove = (event: MouseEvent) => {
+ guideMoreTouchCurrentY.value = getGestureClientY(event)
+}
+
+const handleGuideMoreMouseEnd = (event: MouseEvent) => {
+ guideMoreTouchCurrentY.value = getGestureClientY(event)
+ if (guideMoreTouchCurrentY.value - guideMoreTouchStartY.value > 48) {
+ closeGuideMoreMenu()
+ }
+}
+
const handleMoreRouteGuide = () => {
openRoutePlanner()
}
@@ -864,13 +933,6 @@ const handleMoreRecommendedEntrance = () => {
openOutdoorReference('entrance')
}
-const handleMoreToggleLayer = () => {
- is3DMode.value = true
- guideOutdoorState.value = 'home'
- indoorView.value = indoorLayerMode.value === 'multi' ? 'floor' : 'multi'
- showGuideMoreMenu.value = false
-}
-
const guideShellMode = computed(() => (is3DMode.value ? '3d' : '2d'))
const guideMapType = computed(() => (is3DMode.value ? 'indoor' : 'outdoor'))
const guideOutdoorVariant = computed(() => (