优化导览页面面板与楼层交互
This commit is contained in:
@@ -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"
|
||||
>
|
||||
<view v-if="showFloorHeader" class="floor-header">
|
||||
<view
|
||||
v-if="showFloorHeader"
|
||||
class="floor-header"
|
||||
:class="{ active: layerMode === 'multi' }"
|
||||
@tap.stop="handleFloorHeaderTap"
|
||||
>
|
||||
<text class="floor-header-icon">▱</text>
|
||||
<text class="floor-header-label">楼层</text>
|
||||
</view>
|
||||
@@ -146,7 +153,7 @@
|
||||
v-for="floor in floorLabels"
|
||||
:key="floor"
|
||||
class="floor-item"
|
||||
:class="{ active: activeFloor === floor }"
|
||||
:class="{ active: activeFloor === floor && layerMode !== 'multi' }"
|
||||
@tap="handleFloorChange(floor)"
|
||||
>
|
||||
<text class="floor-label">{{ floor }}</text>
|
||||
@@ -242,6 +249,7 @@ const props = withDefaults(defineProps<{
|
||||
layerMode?: LayerDisplayMode
|
||||
searchTop?: string
|
||||
floorTop?: string
|
||||
floorMaxHeight?: string
|
||||
layerModeToggleTop?: string
|
||||
toolsTop?: string
|
||||
tools?: string[]
|
||||
@@ -275,6 +283,8 @@ const props = withDefaults(defineProps<{
|
||||
targetFocusDistanceFactor?: number
|
||||
routePreview?: GuideRouteResult | null
|
||||
showRoute?: boolean
|
||||
autoSwitchThresholdLow?: number
|
||||
autoSwitchThresholdHigh?: number
|
||||
}>(), {
|
||||
searchText: '请输入地点进行搜索',
|
||||
activeMode: '3d',
|
||||
@@ -284,6 +294,7 @@ const props = withDefaults(defineProps<{
|
||||
layerMode: 'single',
|
||||
searchTop: '16px',
|
||||
floorTop: '164px',
|
||||
floorMaxHeight: '',
|
||||
layerModeToggleTop: '154px',
|
||||
toolsTop: '406px',
|
||||
tools: () => [] as string[],
|
||||
@@ -316,7 +327,9 @@ const props = withDefaults(defineProps<{
|
||||
touchGestureMode: 'orbit',
|
||||
targetFocusDistanceFactor: 0.22,
|
||||
routePreview: null,
|
||||
showRoute: false
|
||||
showRoute: false,
|
||||
autoSwitchThresholdLow: 1.0,
|
||||
autoSwitchThresholdHigh: 1.3
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -339,6 +352,7 @@ const indoorRendererRef = ref<{
|
||||
showMultiFloor?: () => Promise<void> | void
|
||||
resetCamera?: () => void
|
||||
setCameraPreset?: (preset: 'top' | 'oblique') => void
|
||||
zoomCamera?: (direction: 'in' | 'out') => void
|
||||
clearSelection?: (shouldEmit?: boolean) => void
|
||||
disableAutoSwitchTemporarily?: (durationMs: number) => void
|
||||
} | null>(null)
|
||||
@@ -359,7 +373,8 @@ const searchFieldStyle = computed(() => ({
|
||||
}))
|
||||
|
||||
const floorSwitcherStyle = computed(() => ({
|
||||
top: props.floorTop
|
||||
top: props.floorTop,
|
||||
...(props.floorMaxHeight ? { maxHeight: props.floorMaxHeight } : {})
|
||||
}))
|
||||
|
||||
const layerModeToggleStyle = computed(() => ({
|
||||
@@ -432,6 +447,10 @@ const handleLayerModeToggle = () => {
|
||||
handleLayerModeChange(nextLayerMode.value)
|
||||
}
|
||||
|
||||
const handleFloorHeaderTap = () => {
|
||||
handleLayerModeChange('multi')
|
||||
}
|
||||
|
||||
const handleToolClick = (tool: string) => {
|
||||
if (props.mapType === 'indoor') {
|
||||
indoorRendererRef.value?.disableAutoSwitchTemporarily?.(10000)
|
||||
@@ -456,6 +475,7 @@ const handleResetView = () => {
|
||||
}
|
||||
|
||||
const handleZoomClick = (direction: 'in' | 'out') => {
|
||||
indoorRendererRef.value?.zoomCamera?.(direction)
|
||||
emit('toolClick', direction === 'in' ? '放大' : '缩小')
|
||||
}
|
||||
|
||||
@@ -797,7 +817,9 @@ const handleAutoSwitch = (event: { from: 'overview' | 'floor'; to: 'overview' |
|
||||
max-height: calc(100vh - 260px);
|
||||
padding: 1px;
|
||||
box-sizing: border-box;
|
||||
overflow: visible;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
scrollbar-width: none;
|
||||
background: rgba(255, 255, 255, 0.84);
|
||||
border: 1px solid #dde5df;
|
||||
border-radius: 8px;
|
||||
@@ -805,6 +827,10 @@ const handleAutoSwitch = (event: { from: 'overview' | 'floor'; to: 'overview' |
|
||||
z-index: 35;
|
||||
}
|
||||
|
||||
.floor-switcher::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.floor-switcher.side-right {
|
||||
right: 16px;
|
||||
}
|
||||
@@ -814,6 +840,7 @@ const handleAutoSwitch = (event: { from: 'overview' | 'floor'; to: 'overview' |
|
||||
}
|
||||
|
||||
.floor-header {
|
||||
position: relative;
|
||||
min-height: 50px;
|
||||
padding: 7px 4px 6px;
|
||||
display: flex;
|
||||
@@ -826,6 +853,10 @@ const handleAutoSwitch = (event: { from: 'overview' | 'floor'; to: 'overview' |
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
.floor-header.active {
|
||||
background: #000000;
|
||||
}
|
||||
|
||||
.floor-header::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
@@ -842,12 +873,20 @@ const handleAutoSwitch = (event: { from: 'overview' | 'floor'; to: 'overview' |
|
||||
color: #151713;
|
||||
}
|
||||
|
||||
.floor-header.active .floor-header-icon {
|
||||
color: var(--museum-accent);
|
||||
}
|
||||
|
||||
.floor-header-label {
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
color: #545861;
|
||||
}
|
||||
|
||||
.floor-header.active .floor-header-label {
|
||||
color: var(--museum-accent);
|
||||
}
|
||||
|
||||
.layer-mode-toggle {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
<template>
|
||||
<view v-if="visible" class="route-planner-panel" :class="{ collapsed: isCollapsed }">
|
||||
<view
|
||||
v-if="visible"
|
||||
class="route-planner-panel"
|
||||
:class="{ collapsed: isCollapsed }"
|
||||
@touchstart.stop="handlePanelTouchStart"
|
||||
@touchmove.stop="handlePanelTouchMove"
|
||||
@touchend.stop="handlePanelTouchEnd"
|
||||
@mousedown.stop="handlePanelMouseStart"
|
||||
@mousemove.stop="handlePanelMouseMove"
|
||||
@mouseup.stop="handlePanelMouseEnd"
|
||||
>
|
||||
<view class="panel-handle" @tap.stop="toggleCollapsed"></view>
|
||||
|
||||
<view v-if="isCollapsed" class="panel-collapsed" @tap="expandPanel">
|
||||
@@ -19,6 +29,9 @@
|
||||
<text v-if="summary" class="panel-summary">{{ summary }}</text>
|
||||
</view>
|
||||
<view class="panel-actions">
|
||||
<view class="panel-light-action" @tap="handleBack">
|
||||
<text class="panel-light-action-text">返回</text>
|
||||
</view>
|
||||
<view class="panel-light-action" @tap="collapsePanel">
|
||||
<text class="panel-light-action-text">收起</text>
|
||||
</view>
|
||||
@@ -94,6 +107,7 @@
|
||||
:loading="pickerLoading"
|
||||
:error="pickerError"
|
||||
:empty-text="pickerEmptyText"
|
||||
close-text="返回"
|
||||
@close="closePicker"
|
||||
@select="handlePointSelect"
|
||||
@search="handlePickerSearch"
|
||||
@@ -148,10 +162,13 @@ const emit = defineEmits<{
|
||||
clear: []
|
||||
viewRoute: [payload: { startPoint: RoutePointOption; endPoint: RoutePointOption }]
|
||||
simulateGuide: []
|
||||
back: []
|
||||
}>()
|
||||
|
||||
const pickerMode = ref<PickerMode>('')
|
||||
const isCollapsed = ref(false)
|
||||
const panelTouchStartY = ref(0)
|
||||
const panelTouchCurrentY = ref(0)
|
||||
|
||||
const pickerTitle = computed(() => (
|
||||
pickerMode.value === 'start' ? '选择起点' : '选择终点'
|
||||
@@ -274,6 +291,15 @@ const handleClear = () => {
|
||||
emit('clear')
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
if (pickerMode.value) {
|
||||
closePicker()
|
||||
return
|
||||
}
|
||||
|
||||
emit('back')
|
||||
}
|
||||
|
||||
const collapsePanel = () => {
|
||||
isCollapsed.value = true
|
||||
}
|
||||
@@ -286,6 +312,56 @@ const toggleCollapsed = () => {
|
||||
isCollapsed.value = !isCollapsed.value
|
||||
}
|
||||
|
||||
const getGestureClientY = (event: TouchEvent | MouseEvent) => {
|
||||
if ('changedTouches' in event) {
|
||||
return event.changedTouches?.[0]?.clientY
|
||||
?? event.touches?.[0]?.clientY
|
||||
?? 0
|
||||
}
|
||||
|
||||
return event.clientY
|
||||
}
|
||||
|
||||
const handlePanelTouchStart = (event: TouchEvent) => {
|
||||
if (pickerMode.value) return
|
||||
const clientY = getGestureClientY(event)
|
||||
panelTouchStartY.value = clientY
|
||||
panelTouchCurrentY.value = clientY
|
||||
}
|
||||
|
||||
const handlePanelTouchMove = (event: TouchEvent) => {
|
||||
if (pickerMode.value) return
|
||||
panelTouchCurrentY.value = getGestureClientY(event)
|
||||
}
|
||||
|
||||
const handlePanelTouchEnd = (event: TouchEvent) => {
|
||||
if (pickerMode.value) return
|
||||
panelTouchCurrentY.value = getGestureClientY(event)
|
||||
if (panelTouchCurrentY.value - panelTouchStartY.value > 48) {
|
||||
collapsePanel()
|
||||
}
|
||||
}
|
||||
|
||||
const handlePanelMouseStart = (event: MouseEvent) => {
|
||||
if (pickerMode.value) return
|
||||
const clientY = getGestureClientY(event)
|
||||
panelTouchStartY.value = clientY
|
||||
panelTouchCurrentY.value = clientY
|
||||
}
|
||||
|
||||
const handlePanelMouseMove = (event: MouseEvent) => {
|
||||
if (pickerMode.value) return
|
||||
panelTouchCurrentY.value = getGestureClientY(event)
|
||||
}
|
||||
|
||||
const handlePanelMouseEnd = (event: MouseEvent) => {
|
||||
if (pickerMode.value) return
|
||||
panelTouchCurrentY.value = getGestureClientY(event)
|
||||
if (panelTouchCurrentY.value - panelTouchStartY.value > 48) {
|
||||
collapsePanel()
|
||||
}
|
||||
}
|
||||
|
||||
const handleViewRoute = () => {
|
||||
if (!canViewRoute.value || !props.startPoint || !props.endPoint) return
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<view class="picker-header">
|
||||
<text class="picker-title">{{ title }}</text>
|
||||
<view class="picker-close" @tap="handleClose">
|
||||
<text class="picker-close-text">关闭</text>
|
||||
<text class="picker-close-text">{{ closeText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -72,6 +72,7 @@ const props = withDefaults(defineProps<{
|
||||
loading?: boolean
|
||||
error?: string
|
||||
emptyText?: string
|
||||
closeText?: string
|
||||
}>(), {
|
||||
visible: false,
|
||||
title: '选择目标',
|
||||
@@ -81,7 +82,8 @@ const props = withDefaults(defineProps<{
|
||||
initialKeyword: '',
|
||||
loading: false,
|
||||
error: '',
|
||||
emptyText: '暂无匹配地点'
|
||||
emptyText: '暂无匹配地点',
|
||||
closeText: '关闭'
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
Reference in New Issue
Block a user