From 215428db90f9ea81a8ba62eae37445d3b795270a Mon Sep 17 00:00:00 2001 From: lyf <2514544224@qq.com> Date: Sun, 19 Jul 2026 14:05:54 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=A6=86=E5=86=85=E9=A2=84?= =?UTF-8?q?=E8=A7=88=E7=8A=B6=E6=80=81=E4=B8=8E=E7=82=B9=E4=BD=8D=E5=B1=95?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/get-building-polygon.js | 10 +- src/components/navigation/GuideMapShell.vue | 20 ++-- src/components/navigation/GuideTopTabs.vue | 8 ++ src/components/search/PoiSearchPanel.vue | 25 +++-- src/pages/exhibit/detail.vue | 2 +- src/pages/index/index.vue | 17 ++- src/pages/route/detail.vue | 114 +++++++++++++++++--- src/view-models/visitorPoiPresentation.ts | 52 +++++++++ tests/unit/GuideMapShell.spec.ts | 29 ++++- tests/unit/IndexPoiSearchFlow.spec.ts | 3 + tests/unit/visitorPoiPresentation.spec.ts | 35 ++++++ 11 files changed, 280 insertions(+), 35 deletions(-) create mode 100644 src/view-models/visitorPoiPresentation.ts create mode 100644 tests/unit/visitorPoiPresentation.spec.ts diff --git a/scripts/get-building-polygon.js b/scripts/get-building-polygon.js index 0dd4f8a..b8211ac 100644 --- a/scripts/get-building-polygon.js +++ b/scripts/get-building-polygon.js @@ -5,8 +5,8 @@ const https = require('https') -// 腾讯地图 WebService API Key(需要在腾讯位置服务申请) -const API_KEY = 'EJPBZ-DQEEQ-PDN5U-4ZDVX-F4I3F-6MBJC' +// 由部署环境注入,避免把地图服务凭据写入仓库。 +const API_KEY = process.env.TENCENT_MAP_WEBSERVICE_KEY // 深圳自然博物馆坐标 const MUSEUM_LAT = 22.692763 @@ -109,6 +109,12 @@ function calculateRectangle(centerLat, centerLng, widthMeters, heightMeters) { return polygon } +if (!API_KEY) { + console.error('缺少 TENCENT_MAP_WEBSERVICE_KEY;请通过环境变量注入受域名/IP 限制的腾讯地图 WebService Key。') + process.exitCode = 1 + return +} + console.log('正在获取深圳自然博物馆建筑围栏信息...\n') // 执行查询 diff --git a/src/components/navigation/GuideMapShell.vue b/src/components/navigation/GuideMapShell.vue index db86624..a6517c7 100644 --- a/src/components/navigation/GuideMapShell.vue +++ b/src/components/navigation/GuideMapShell.vue @@ -586,10 +586,13 @@ const markFloorSwitchFailedIfUnrendered = (floorId: string, requestSeq: number) const handleFloorChange = (floor: { id: string; label: string }) => { const floorId = floor.id if (!floorId || loadingFloorId.value) return - if (floorId === renderedFloorId.value && activeFloorId.value === floorId && props.layerMode !== 'multi') { + if ( + floorId === renderedFloorId.value + && activeFloorId.value === floorId + && props.indoorView === 'floor' + && props.layerMode !== 'multi' + ) { emit('floorChange', floorId) - emit('indoorViewChange', 'floor') - emit('layerModeChange', 'single') return } @@ -604,9 +607,6 @@ const handleFloorChange = (floor: { id: string; label: string }) => { floorId, floorLabel: floor.label }) - emit('indoorViewChange', 'floor') - emit('layerModeChange', 'single') - Promise.resolve(indoorRendererRef.value?.switchFloor?.(floorId)) .then(() => { markFloorSwitchFailedIfUnrendered(floorId, requestSeq) @@ -667,10 +667,12 @@ const handleLayerModeChange = (mode: LayerDisplayMode) => { }) console.error('楼层切换失败:', error) }) - emit('indoorViewChange', 'floor') + // floor-change 才代表 ThreeMap 已提交单层模型。 } - emit('layerModeChange', mode) + if (mode === 'multi') { + emit('layerModeChange', mode) + } } const handleLayerModeToggle = () => { @@ -786,6 +788,8 @@ defineExpose({ clearRoute: () => { indoorRendererRef.value?.clearRoute?.() }, + // 仅发起切换;父级必须以 floor-change 作为已提交的唯一依据。 + switchFloor: (floorId: string) => indoorRendererRef.value?.switchFloor?.(floorId), showOverview: handleShowOverview, resetToViewBaseline: (options: { view: 'overview' | 'floor' diff --git a/src/components/navigation/GuideTopTabs.vue b/src/components/navigation/GuideTopTabs.vue index a1902d3..98abdfd 100644 --- a/src/components/navigation/GuideTopTabs.vue +++ b/src/components/navigation/GuideTopTabs.vue @@ -3,13 +3,21 @@ class="guide-top-tabs" :class="`variant-${variant}`" :style="{ top }" + role="tablist" + aria-label="主功能" > !getPoiDataIssues(poi).some((issue) = const unavailablePoiCount = computed(() => pois.value.filter((poi) => !isPoiLocatable(poi)).length) +// 数据质量统计保留给诊断日志,普通游客只看到可恢复的加载状态。 const dataWarning = computed(() => { - const warnings: string[] = [] - if (excludedPoiCount.value) warnings.push(`${excludedPoiCount.value} 个点位分类或楼层信息不完整`) - if (duplicatePoiCount.value) warnings.push(`${duplicatePoiCount.value} 个重复点位已合并`) - if (unavailablePoiCount.value) warnings.push(`${unavailablePoiCount.value} 个点位暂无地图坐标`) - if (floorLoadError.value) warnings.push('楼层信息读取失败') - return warnings.join(',') + const diagnostic = { + excluded: excludedPoiCount.value, + duplicates: duplicatePoiCount.value, + unavailable: unavailablePoiCount.value, + floorLoadError: floorLoadError.value + } + if (import.meta.env.DEV && Object.values(diagnostic).some(Boolean)) { + console.debug('[guide-poi-diagnostics]', diagnostic) + } + return '' }) const dedupePois = (items: MuseumPoi[]) => { @@ -860,7 +868,10 @@ const restoreResultListScroll = async () => { if (resultListElement) resultListElement.scrollTop = scrollTop } -const poiDisplayName = (poi: MuseumPoi) => poi.name?.trim() || '未命名点位' +const poiPresentations = computed(() => createVisitorPoiPresentations(pois.value)) +const poiDisplayName = (poi: MuseumPoi) => ( + poiPresentations.value.get(poi.id)?.displayName || '未命名点位' +) const poiResultMeta = (poi: MuseumPoi) => { const categoryLabel = resolvePoiCategory(poi)?.label || poi.primaryCategory?.label || '其他' diff --git a/src/pages/exhibit/detail.vue b/src/pages/exhibit/detail.vue index d00102f..b77eba3 100644 --- a/src/pages/exhibit/detail.vue +++ b/src/pages/exhibit/detail.vue @@ -17,7 +17,7 @@