优化导览页图标与快捷入口展示

This commit is contained in:
lyf
2026-06-29 02:04:08 +08:00
parent f4de24fad8
commit c9f32714aa
2 changed files with 236 additions and 11 deletions

View File

@@ -170,9 +170,48 @@
v-for="tool in tools"
:key="tool"
class="tool-btn"
:aria-label="tool"
:title="tool"
@tap="handleToolClick(tool)"
>
<text class="tool-label">{{ tool }}</text>
<svg
v-if="toolIconType(tool) === 'reset'"
class="tool-icon-svg"
viewBox="0 0 24 24"
fill="none"
>
<path d="M5 7v5h5" />
<path d="M6.2 13.4A6.2 6.2 0 1 0 7.4 6.7L5 9.1" />
</svg>
<svg
v-else-if="toolIconType(tool) === 'top'"
class="tool-icon-svg"
viewBox="0 0 24 24"
fill="none"
>
<rect x="6" y="6" width="12" height="12" rx="2" />
<path d="M9 9h6v6H9z" />
</svg>
<svg
v-else-if="toolIconType(tool) === 'oblique'"
class="tool-icon-svg"
viewBox="0 0 24 24"
fill="none"
>
<path d="M5 9.5 12 5l7 4.5-7 4.5-7-4.5Z" />
<path d="M5 9.5v5L12 19l7-4.5v-5" />
<path d="M12 14v5" />
</svg>
<svg
v-else-if="toolIconType(tool) === 'clear'"
class="tool-icon-svg"
viewBox="0 0 24 24"
fill="none"
>
<path d="M7 7l10 10" />
<path d="M17 7 7 17" />
</svg>
<text v-else class="tool-label">{{ tool }}</text>
</view>
</view>
@@ -364,7 +403,7 @@ const indoorRendererRef = ref<{
showMultiFloor?: () => Promise<void> | void
resetCamera?: () => void
setCameraPreset?: (preset: 'top' | 'oblique') => void
zoomCamera?: (direction: 'in' | 'out') => void
zoomCamera?: (direction: 'in' | 'out', options?: { source?: 'button' | 'gesture' }) => void
clearSelection?: (shouldEmit?: boolean) => void
clearRoute?: () => Promise<void> | void
disableAutoSwitchTemporarily?: (durationMs: number) => void
@@ -477,7 +516,7 @@ const handleToolClick = (tool: string) => {
if (props.mapType === 'indoor') {
indoorRendererRef.value?.disableAutoSwitchTemporarily?.(10000)
if (tool === '重置') {
if (tool === '复位') {
indoorRendererRef.value?.resetCamera?.()
} else if (tool === '俯视') {
indoorRendererRef.value?.setCameraPreset?.('top')
@@ -491,8 +530,16 @@ const handleToolClick = (tool: string) => {
emit('toolClick', tool)
}
const toolIconType = (tool: string) => {
if (tool === '复位') return 'reset'
if (tool === '俯视') return 'top'
if (tool === '斜视') return 'oblique'
if (tool === '清除') return 'clear'
return 'text'
}
const handleZoomClick = (direction: 'in' | 'out') => {
indoorRendererRef.value?.zoomCamera?.(direction)
indoorRendererRef.value?.zoomCamera?.(direction, { source: 'button' })
emit('toolClick', direction === 'in' ? '放大' : '缩小')
}
@@ -892,9 +939,37 @@ defineExpose({
}
.floor-header-icon {
font-size: 16px;
line-height: 16px;
width: 18px;
height: 18px;
position: relative;
display: block;
color: #151713;
font-size: 0;
line-height: 0;
}
.floor-header-icon::before,
.floor-header-icon::after {
content: '';
position: absolute;
box-sizing: border-box;
border: 1.8px solid currentColor;
border-radius: 2px;
}
.floor-header-icon::before {
left: 2px;
top: 4px;
width: 12px;
height: 10px;
}
.floor-header-icon::after {
left: 5px;
top: 1px;
width: 12px;
height: 10px;
background: #ffffff;
}
.floor-header.active .floor-header-icon {
@@ -1001,6 +1076,20 @@ defineExpose({
box-sizing: border-box;
}
.tool-btn:active {
background: #f6f7f4;
transform: translateY(1px);
}
.tool-icon-svg {
width: 22px;
height: 22px;
stroke: #151713;
stroke-width: 1.8;
stroke-linecap: round;
stroke-linejoin: round;
}
.tool-label {
font-size: 12px;
line-height: 16px;

View File

@@ -36,6 +36,8 @@
show-floor-header
:show-layer-mode-toggle="false"
:show-floor="is3DMode"
:tools="indoorCameraTools"
tools-top=""
tools-bottom="54px"
show-zoom-controls
zoom-controls-top="calc(100vh - 284px)"
@@ -57,6 +59,9 @@
<view v-if="guideOutdoorState === 'entrance' && !is3DMode" class="entrance-tip">
<text class="entrance-tip-text">主入口 4 分钟</text>
</view>
<view v-if="is3DMode && showIndoorGestureHint" class="indoor-gesture-hint">
<text class="indoor-gesture-text">{{ indoorGestureHintText }}</text>
</view>
</template>
<view
@@ -111,9 +116,16 @@
class="guide-quick-actions"
>
<view class="guide-quick-action primary" @tap="handleMoreRouteGuide">
<view class="guide-quick-icon indoor">
<view class="guide-quick-icon-floor top"></view>
<view class="guide-quick-icon-floor bottom"></view>
</view>
<text class="guide-quick-action-text primary">室内导览</text>
</view>
<view class="guide-quick-action" @tap="handleMoreOutdoorNav">
<view class="guide-quick-icon arrival">
<view class="guide-quick-pin"></view>
</view>
<text class="guide-quick-action-text">来馆</text>
</view>
</view>
@@ -325,6 +337,24 @@ const normalizeGuideFloorId = (labelOrId: string) => guideUseCase.normalizeFloor
const indoorLayerMode = computed<LayerDisplayMode>(() => (
indoorView.value === 'multi' ? 'multi' : 'single'
))
const indoorCameraTools = computed(() => (is3DMode.value ? ['复位', '俯视', '斜视'] : []))
const showIndoorGestureHint = ref(true)
const indoorGestureHintText = ref('拖动旋转 · 双指缩放 · 点选标记查看位置')
let indoorGestureHintTimer: ReturnType<typeof setTimeout> | null = null
const showIndoorHint = (text = '拖动旋转 · 双指缩放 · 点选标记查看位置', durationMs = 5200) => {
indoorGestureHintText.value = text
showIndoorGestureHint.value = is3DMode.value
if (indoorGestureHintTimer) {
clearTimeout(indoorGestureHintTimer)
}
indoorGestureHintTimer = setTimeout(() => {
showIndoorGestureHint.value = false
indoorGestureHintTimer = null
}, durationMs)
}
const routePlanSummary = computed(() => {
if (activeRoutePreview.value) {
@@ -446,6 +476,8 @@ const syncTopTabFromHash = () => {
}
onMounted(() => {
showIndoorHint()
if (typeof window === 'undefined') return
syncTopTabFromHash()
@@ -453,6 +485,11 @@ onMounted(() => {
})
onUnmounted(() => {
if (indoorGestureHintTimer) {
clearTimeout(indoorGestureHintTimer)
indoorGestureHintTimer = null
}
if (typeof window === 'undefined') return
window.removeEventListener('hashchange', syncTopTabFromHash)
@@ -584,6 +621,7 @@ const handleFloorChange = (floor: string) => {
indoorView.value = 'floor'
selectedGuidePoi.value = null
isPoiCardCollapsed.value = false
showIndoorHint(`已切换到 ${floor},可拖动旋转查看`, 3200)
console.log('切换楼层:', floor)
}
@@ -593,6 +631,12 @@ const handleIndoorViewChange = (view: GuideIndoorView) => {
selectedGuidePoi.value = null
isPoiCardCollapsed.value = false
}
if (view === 'multi') {
showIndoorHint('已切换多层视图,可点选楼层查看', 3200)
} else if (view === 'floor') {
showIndoorHint('已切换单层视图,可点选标记查看位置', 3200)
}
}
const handleAutoSwitch = (event: { from: 'overview' | 'floor'; to: 'overview' | 'floor'; trigger: string; distance: number }) => {
@@ -602,6 +646,7 @@ const handleAutoSwitch = (event: { from: 'overview' | 'floor'; to: 'overview' |
selectedGuidePoi.value = null
isPoiCardCollapsed.value = false
}
showIndoorHint(event.to === 'floor' ? '已进入单层视图,可点选标记查看位置' : '已返回建筑外观', 3600)
}
const handleGuidePoiClick = (poi: GuideRenderPoi) => {
@@ -611,6 +656,7 @@ const handleGuidePoiClick = (poi: GuideRenderPoi) => {
isSimulatingRoute.value = false
const matchedFloor = guideFloors.value.find((floor) => floor.id === poi.floorId)
activeGuideFloor.value = matchedFloor?.label || poi.floorId
showIndoorHint('已选中标记,可查看位置预览', 3200)
}
const handleGuideSelectionClear = () => {
@@ -839,6 +885,7 @@ const handleEnter3DMode = () => {
indoorView.value = 'overview'
is3DMode.value = true
guideOutdoorState.value = 'home'
showIndoorHint()
if (!routePointOptions.value.length) {
void loadRouteTargets()
}
@@ -857,6 +904,7 @@ const handleModeChange = (mode: '2d' | '3d') => {
clearRoutePreviewState()
} else {
indoorView.value = 'overview'
showIndoorHint()
if (!routePointOptions.value.length) {
void loadRouteTargets()
}
@@ -1273,12 +1321,14 @@ const handleExplainBack = () => {
}
.guide-quick-action {
width: 84px;
min-height: 38px;
padding: 0 12px;
width: 88px;
min-height: 48px;
padding: 7px 8px 6px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
box-sizing: border-box;
background: rgba(255, 255, 255, 0.95);
border: 1px solid #dde5df;
@@ -1291,9 +1341,66 @@ const handleExplainBack = () => {
border-color: #151713;
}
.guide-quick-action:active {
transform: translateY(1px);
}
.guide-quick-icon {
width: 22px;
height: 22px;
position: relative;
color: #151713;
}
.guide-quick-action.primary .guide-quick-icon {
color: var(--museum-accent);
}
.guide-quick-icon-floor {
position: absolute;
left: 3px;
width: 16px;
height: 9px;
box-sizing: border-box;
border: 1.7px solid currentColor;
border-radius: 2px;
transform: skewY(-18deg);
}
.guide-quick-icon-floor.top {
top: 2px;
}
.guide-quick-icon-floor.bottom {
top: 10px;
}
.guide-quick-pin {
position: absolute;
left: 5px;
top: 2px;
width: 12px;
height: 16px;
box-sizing: border-box;
border: 1.8px solid currentColor;
border-radius: 7px 7px 7px 0;
transform: rotate(-45deg);
}
.guide-quick-pin::after {
content: '';
position: absolute;
left: 3px;
top: 3px;
width: 4px;
height: 4px;
background: currentColor;
border-radius: 50%;
}
.guide-quick-action-text {
font-size: 13px;
line-height: 18px;
font-size: 12px;
line-height: 16px;
font-weight: 600;
color: #151713;
text-align: center;
@@ -1402,6 +1509,35 @@ const handleExplainBack = () => {
color: #1f2329;
}
.indoor-gesture-hint {
position: absolute;
top: clamp(174px, 23vh, 228px);
left: 50%;
max-width: calc(100% - 136px);
min-height: 34px;
padding: 7px 12px;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
background: rgba(21, 23, 19, 0.76);
border: 1px solid rgba(255, 255, 255, 0.16);
border-radius: 8px;
transform: translateX(-50%);
pointer-events: none;
z-index: 34;
}
.indoor-gesture-text {
min-width: 0;
font-size: 12px;
line-height: 17px;
font-weight: 500;
color: #ffffff;
text-align: center;
white-space: normal;
}
.home-shortcuts-card {
position: absolute;
left: 12px;