提交室内导览交互与讲解优化
This commit is contained in:
@@ -3,9 +3,20 @@
|
||||
<view class="map-layer" :class="`map-${mapType}`">
|
||||
<template v-if="mapType === 'indoor'">
|
||||
<!-- #ifdef H5 -->
|
||||
<SgsMapRenderer
|
||||
v-if="useSgsMapRenderer"
|
||||
ref="indoorRendererRef"
|
||||
class="indoor-three-map"
|
||||
:initial-floor-id="activeFloorId"
|
||||
:initial-view="indoorInitialView"
|
||||
:floors="props.floors"
|
||||
:target-focus="targetFocusRequest"
|
||||
@target-focus="handleTargetFocus"
|
||||
@floor-change="handleSdkFloorChange"
|
||||
/>
|
||||
<ThreeMap
|
||||
v-if="indoorModelSource"
|
||||
ref="threeMapRef"
|
||||
v-else-if="indoorModelSource"
|
||||
ref="indoorRendererRef"
|
||||
class="indoor-three-map"
|
||||
:asset-base-url="indoorAssetBaseUrl"
|
||||
:model-source="indoorModelSource"
|
||||
@@ -14,7 +25,11 @@
|
||||
:show-controls="false"
|
||||
:show-poi="shouldShowIndoorPois"
|
||||
:target-focus="targetFocusRequest"
|
||||
@floor-change="handleThreeFloorChange"
|
||||
@poi-click="handlePoiClick"
|
||||
@selection-clear="handleSelectionClear"
|
||||
@target-focus="handleTargetFocus"
|
||||
@auto-switch="handleAutoSwitch"
|
||||
/>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef H5 -->
|
||||
@@ -89,27 +104,15 @@
|
||||
<slot name="overlay"></slot>
|
||||
|
||||
<view
|
||||
v-if="mapType === 'indoor' && showIndoorViewToggle"
|
||||
class="indoor-view-toggle"
|
||||
:style="indoorViewToggleStyle"
|
||||
v-if="showIndoorRightControls && showLayerModeToggle"
|
||||
class="layer-mode-toggle"
|
||||
:style="layerModeToggleStyle"
|
||||
@tap="handleLayerModeToggle"
|
||||
>
|
||||
<view
|
||||
class="indoor-view-item"
|
||||
:class="{ active: indoorView === 'overview' }"
|
||||
@tap="handleIndoorViewChange('overview')"
|
||||
>
|
||||
<text class="indoor-view-label">全馆</text>
|
||||
</view>
|
||||
<view
|
||||
class="indoor-view-item"
|
||||
:class="{ active: indoorView === 'floor' }"
|
||||
@tap="handleIndoorViewChange('floor')"
|
||||
>
|
||||
<text class="indoor-view-label">楼层</text>
|
||||
</view>
|
||||
<text class="layer-mode-label">{{ layerModeActionLabel }}</text>
|
||||
</view>
|
||||
|
||||
<view v-if="showFloor" class="floor-switcher" :style="floorSwitcherStyle">
|
||||
<view v-if="showIndoorRightControls && showFloor" class="floor-switcher" :style="floorSwitcherStyle">
|
||||
<view
|
||||
v-for="floor in floorLabels"
|
||||
:key="floor"
|
||||
@@ -141,9 +144,14 @@ import { computed, ref } from 'vue'
|
||||
import TencentMap from '@/components/map/TencentMap.vue'
|
||||
// #ifdef H5
|
||||
import ThreeMap from '@/components/map/ThreeMap.vue'
|
||||
import SgsMapRenderer from '@/components/map/SgsMapRenderer.vue'
|
||||
import {
|
||||
isSgsSdkMode
|
||||
} from '@/config/dataSource'
|
||||
// #endif
|
||||
import type {
|
||||
GuideModelSource
|
||||
GuideModelSource,
|
||||
GuideRenderPoi
|
||||
} from '@/domain/guideModel'
|
||||
|
||||
interface GuideFloorOption {
|
||||
@@ -169,7 +177,8 @@ interface TargetPoiFocusResult {
|
||||
message?: string
|
||||
}
|
||||
|
||||
type IndoorViewMode = 'overview' | 'floor'
|
||||
type IndoorViewMode = 'overview' | 'floor' | 'multi'
|
||||
type LayerDisplayMode = 'single' | 'multi'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
searchText?: string
|
||||
@@ -177,14 +186,15 @@ const props = withDefaults(defineProps<{
|
||||
activeFloor?: string
|
||||
indoorView?: IndoorViewMode
|
||||
indoorInitialView?: IndoorViewMode
|
||||
layerMode?: LayerDisplayMode
|
||||
searchTop?: string
|
||||
floorTop?: string
|
||||
indoorViewToggleTop?: string
|
||||
layerModeToggleTop?: string
|
||||
toolsTop?: string
|
||||
tools?: string[]
|
||||
showSearch?: boolean
|
||||
showFloor?: boolean
|
||||
showIndoorViewToggle?: boolean
|
||||
showLayerModeToggle?: boolean
|
||||
modeTop?: string
|
||||
modeLayout?: 'full' | 'status'
|
||||
modeStatus?: string
|
||||
@@ -203,14 +213,15 @@ const props = withDefaults(defineProps<{
|
||||
activeFloor: '1F',
|
||||
indoorView: 'floor',
|
||||
indoorInitialView: 'floor',
|
||||
layerMode: 'single',
|
||||
searchTop: '16px',
|
||||
floorTop: '164px',
|
||||
indoorViewToggleTop: '154px',
|
||||
layerModeToggleTop: '154px',
|
||||
toolsTop: '406px',
|
||||
tools: () => [] as string[],
|
||||
showSearch: true,
|
||||
showFloor: true,
|
||||
showIndoorViewToggle: false,
|
||||
showLayerModeToggle: false,
|
||||
modeTop: '60px',
|
||||
modeLayout: 'full',
|
||||
modeStatus: '',
|
||||
@@ -231,16 +242,31 @@ const emit = defineEmits<{
|
||||
floorChange: [floor: string]
|
||||
toolClick: [tool: string]
|
||||
indoorViewChange: [view: IndoorViewMode]
|
||||
layerModeChange: [mode: LayerDisplayMode]
|
||||
poiClick: [poi: GuideRenderPoi]
|
||||
selectionClear: []
|
||||
targetFocus: [result: TargetPoiFocusResult]
|
||||
autoSwitch: [event: { from: 'overview' | 'floor'; to: 'overview' | 'floor'; trigger: string; distance: number }]
|
||||
}>()
|
||||
|
||||
const threeMapRef = ref<{
|
||||
const indoorRendererRef = ref<{
|
||||
switchFloor?: (floorId: string) => Promise<void> | void
|
||||
showOverview?: () => Promise<void> | void
|
||||
showMultiFloor?: () => Promise<void> | void
|
||||
resetCamera?: () => void
|
||||
setCameraPreset?: (preset: 'top' | 'oblique') => void
|
||||
clearSelection?: (shouldEmit?: boolean) => void
|
||||
disableAutoSwitchTemporarily?: (durationMs: number) => void
|
||||
} | null>(null)
|
||||
|
||||
const floorLabels = computed(() => props.floors.map((floor) => floor.label))
|
||||
const indoorModelSource = computed(() => props.indoorModelSource)
|
||||
// #ifdef H5
|
||||
const useSgsMapRenderer = computed(() => isSgsSdkMode())
|
||||
// #endif
|
||||
const showIndoorRightControls = computed(() => (
|
||||
props.mapType === 'indoor' && props.indoorView !== 'overview'
|
||||
))
|
||||
|
||||
const activeFloorId = computed(() => props.normalizeFloorId(props.activeFloor))
|
||||
|
||||
@@ -252,8 +278,8 @@ const floorSwitcherStyle = computed(() => ({
|
||||
top: props.floorTop
|
||||
}))
|
||||
|
||||
const indoorViewToggleStyle = computed(() => ({
|
||||
top: props.indoorViewToggleTop
|
||||
const layerModeToggleStyle = computed(() => ({
|
||||
top: props.layerModeToggleTop
|
||||
}))
|
||||
|
||||
const modeRowStyle = computed(() => ({
|
||||
@@ -264,7 +290,15 @@ const toolStackStyle = computed(() => ({
|
||||
top: props.toolsTop
|
||||
}))
|
||||
|
||||
const shouldShowIndoorPois = computed(() => Boolean(props.targetFocusRequest))
|
||||
const shouldShowIndoorPois = computed(() => (
|
||||
props.mapType === 'indoor'
|
||||
) || Boolean(props.targetFocusRequest))
|
||||
const nextLayerMode = computed<LayerDisplayMode>(() => (
|
||||
props.layerMode === 'multi' ? 'single' : 'multi'
|
||||
))
|
||||
const layerModeActionLabel = computed(() => (
|
||||
nextLayerMode.value === 'multi' ? '多层' : '单层'
|
||||
))
|
||||
|
||||
const handleSearchTap = () => {
|
||||
emit('searchTap')
|
||||
@@ -276,28 +310,79 @@ const handleModeChange = (mode: '2d' | '3d') => {
|
||||
|
||||
const handleFloorChange = (floor: string) => {
|
||||
const floorId = props.normalizeFloorId(floor)
|
||||
void threeMapRef.value?.switchFloor?.(floorId)
|
||||
indoorRendererRef.value?.disableAutoSwitchTemporarily?.(10000)
|
||||
void indoorRendererRef.value?.switchFloor?.(floorId)
|
||||
emit('indoorViewChange', 'floor')
|
||||
emit('layerModeChange', 'single')
|
||||
emit('floorChange', floor)
|
||||
}
|
||||
|
||||
const handleIndoorViewChange = (view: IndoorViewMode) => {
|
||||
if (view === 'overview') {
|
||||
void threeMapRef.value?.showOverview?.()
|
||||
const handleLayerModeChange = (mode: LayerDisplayMode) => {
|
||||
// 手动切换展示层数时临时禁用缩放自动切换 10 秒
|
||||
indoorRendererRef.value?.disableAutoSwitchTemporarily?.(10000)
|
||||
|
||||
if (mode === 'multi') {
|
||||
void indoorRendererRef.value?.showMultiFloor?.()
|
||||
emit('indoorViewChange', 'multi')
|
||||
} else {
|
||||
void threeMapRef.value?.switchFloor?.(activeFloorId.value)
|
||||
void indoorRendererRef.value?.switchFloor?.(activeFloorId.value)
|
||||
emit('indoorViewChange', 'floor')
|
||||
}
|
||||
|
||||
emit('indoorViewChange', view)
|
||||
emit('layerModeChange', mode)
|
||||
}
|
||||
|
||||
const handleLayerModeToggle = () => {
|
||||
handleLayerModeChange(nextLayerMode.value)
|
||||
}
|
||||
|
||||
const handleToolClick = (tool: string) => {
|
||||
if (props.mapType === 'indoor') {
|
||||
indoorRendererRef.value?.disableAutoSwitchTemporarily?.(10000)
|
||||
|
||||
if (tool === '重置') {
|
||||
indoorRendererRef.value?.resetCamera?.()
|
||||
} else if (tool === '俯视') {
|
||||
indoorRendererRef.value?.setCameraPreset?.('top')
|
||||
} else if (tool === '斜视') {
|
||||
indoorRendererRef.value?.setCameraPreset?.('oblique')
|
||||
} else if (tool === '清除') {
|
||||
indoorRendererRef.value?.clearSelection?.()
|
||||
}
|
||||
}
|
||||
|
||||
emit('toolClick', tool)
|
||||
}
|
||||
|
||||
const handleThreeFloorChange = (floorId: string) => {
|
||||
const floor = props.floors.find((item) => item.id === floorId)
|
||||
emit('floorChange', floor?.label || floorId)
|
||||
emit('indoorViewChange', 'floor')
|
||||
emit('layerModeChange', 'single')
|
||||
}
|
||||
|
||||
const handlePoiClick = (poi: GuideRenderPoi) => {
|
||||
emit('poiClick', poi)
|
||||
}
|
||||
|
||||
const handleSelectionClear = () => {
|
||||
emit('selectionClear')
|
||||
}
|
||||
|
||||
const handleTargetFocus = (result: TargetPoiFocusResult) => {
|
||||
emit('targetFocus', result)
|
||||
}
|
||||
|
||||
const handleSdkFloorChange = (floorId: string) => {
|
||||
const floor = props.floors.find((item) => item.id === floorId)
|
||||
if (floor) {
|
||||
emit('floorChange', floor.label)
|
||||
}
|
||||
}
|
||||
|
||||
const handleAutoSwitch = (event: { from: 'overview' | 'floor'; to: 'overview' | 'floor'; trigger: string; distance: number }) => {
|
||||
emit('autoSwitch', event)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -536,40 +621,31 @@ const handleTargetFocus = (result: TargetPoiFocusResult) => {
|
||||
z-index: 35;
|
||||
}
|
||||
|
||||
.indoor-view-toggle {
|
||||
.layer-mode-toggle {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
width: 58px;
|
||||
padding: 5px;
|
||||
width: 46px;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
background: rgba(255, 255, 255, 0.94);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
background: #ffffff;
|
||||
border: 1px solid #dde5df;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 8px 18px rgba(110, 127, 115, 0.12);
|
||||
z-index: 36;
|
||||
}
|
||||
|
||||
.indoor-view-item {
|
||||
min-height: 34px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 7px;
|
||||
color: #59645d;
|
||||
.layer-mode-toggle:active {
|
||||
background: #f5f7f2;
|
||||
}
|
||||
|
||||
.indoor-view-item.active {
|
||||
background: var(--museum-accent);
|
||||
color: #ffffff;
|
||||
box-shadow: 0 6px 12px rgba(82, 107, 91, 0.18);
|
||||
}
|
||||
|
||||
.indoor-view-label {
|
||||
.layer-mode-label {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
line-height: 16px;
|
||||
font-weight: 500;
|
||||
color: #545861;
|
||||
}
|
||||
|
||||
.floor-item {
|
||||
@@ -616,7 +692,7 @@ const handleTargetFocus = (result: TargetPoiFocusResult) => {
|
||||
|
||||
.tool-stack {
|
||||
position: absolute;
|
||||
right: 18px;
|
||||
left: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
Reference in New Issue
Block a user