From c9f32714aa5f31d53b59679238f54534220461ae Mon Sep 17 00:00:00 2001
From: lyf <2514544224@qq.com>
Date: Mon, 29 Jun 2026 02:04:08 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AF=BC=E8=A7=88=E9=A1=B5?=
=?UTF-8?q?=E5=9B=BE=E6=A0=87=E4=B8=8E=E5=BF=AB=E6=8D=B7=E5=85=A5=E5=8F=A3?=
=?UTF-8?q?=E5=B1=95=E7=A4=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/navigation/GuideMapShell.vue | 101 +++++++++++++-
src/pages/index/index.vue | 146 +++++++++++++++++++-
2 files changed, 236 insertions(+), 11 deletions(-)
diff --git a/src/components/navigation/GuideMapShell.vue b/src/components/navigation/GuideMapShell.vue
index 7d56577..c9fa7ce 100644
--- a/src/components/navigation/GuideMapShell.vue
+++ b/src/components/navigation/GuideMapShell.vue
@@ -170,9 +170,48 @@
v-for="tool in tools"
:key="tool"
class="tool-btn"
+ :aria-label="tool"
+ :title="tool"
@tap="handleToolClick(tool)"
>
- {{ tool }}
+
+
+
+
+ {{ tool }}
@@ -364,7 +403,7 @@ const indoorRendererRef = ref<{
showMultiFloor?: () => Promise | 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
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;
diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue
index 1556aa3..356c284 100644
--- a/src/pages/index/index.vue
+++ b/src/pages/index/index.vue
@@ -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 @@
主入口 约 4 分钟
+
+ {{ indoorGestureHintText }}
+
+
+
+
+
室内导览
+
+
+
来馆
@@ -325,6 +337,24 @@ const normalizeGuideFloorId = (labelOrId: string) => guideUseCase.normalizeFloor
const indoorLayerMode = computed(() => (
indoorView.value === 'multi' ? 'multi' : 'single'
))
+const indoorCameraTools = computed(() => (is3DMode.value ? ['复位', '俯视', '斜视'] : []))
+const showIndoorGestureHint = ref(true)
+const indoorGestureHintText = ref('拖动旋转 · 双指缩放 · 点选标记查看位置')
+let indoorGestureHintTimer: ReturnType | 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;