feat: wire guide data source and POI focus UI

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
lyf
2026-06-12 09:25:22 +08:00
parent a6bfda30e1
commit 2055b13b90
58 changed files with 5768 additions and 1898 deletions

View File

@@ -16,6 +16,9 @@
:map-type="guideMapType"
:outdoor-variant="guideOutdoorVariant"
:indoor-asset-base-url="indoorNavAssetBaseUrl"
:indoor-model-source="indoorModelSource"
:floors="guideFloors"
:normalize-floor-id="normalizeGuideFloorId"
indoor-initial-view="overview"
:indoor-view="indoorView"
:active-floor="activeGuideFloor"
@@ -80,6 +83,7 @@
<view v-else-if="currentTab === 'explain'" class="explain-page">
<ExplainList
:exhibits="explainExhibits"
@exhibit-click="handleExplainExhibitClick"
@featured-click="handleExplainFeaturedClick"
@audio-click="handleAudioClick"
@@ -109,7 +113,7 @@ import { computed, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
import GuideMapShell from '@/components/navigation/GuideMapShell.vue'
import ExplainList from '@/components/explain/ExplainList.vue'
import ExplainList, { type Exhibit } from '@/components/explain/ExplainList.vue'
import FloatingAudioButton from '@/components/audio/FloatingAudioButton.vue'
import AudioPlayer, { type AudioItem } from '@/components/audio/AudioPlayer.vue'
import {
@@ -117,9 +121,20 @@ import {
type GuideTopTab
} from '@/utils/guideTopTabs'
import {
NAV_ASSET_BASE_URL,
NAV_ROUTE_UNAVAILABLE_MESSAGE
} from '@/services/navAssets'
} from '@/domain/guideReadiness'
import {
guideUseCase
} from '@/usecases/guideUseCase'
import {
explainUseCase
} from '@/usecases/explainUseCase'
import {
toExplainExhibitViewModel
} from '@/view-models/explainViewModels'
import type {
MuseumFloor
} from '@/domain/museum'
// 3D 模式状态
const is3DMode = ref(false)
@@ -135,7 +150,10 @@ const showMarkerDetail = ref(false)
const showAreaSelector = ref(false)
const selectedAreaId = ref('service')
const indoorNavAssetBaseUrl = NAV_ASSET_BASE_URL
const indoorNavAssetBaseUrl = guideUseCase.getAssetBaseUrl()
const indoorModelSource = guideUseCase.getModelSource()
const guideFloors = ref<MuseumFloor[]>([])
const normalizeGuideFloorId = (labelOrId: string) => guideUseCase.normalizeFloorId(labelOrId)
const shortcutItems = [
{ id: 'toilet', name: '卫生间', abbr: '卫' },
@@ -149,6 +167,7 @@ const shortcutItems = [
const showAudioPlayer = ref(false)
const isAudioPlaying = ref(false)
const currentAudio = ref<AudioItem | null>(null)
const explainExhibits = ref<Exhibit[]>([])
// 选中的标记详情
interface MarkerDetail {
@@ -188,8 +207,35 @@ onLoad((options: any = {}) => {
if (isGuideTopTab(tab)) {
currentTab.value = tab
}
void loadGuideFloors()
void loadExplainExhibits()
})
const loadGuideFloors = async () => {
try {
const floors = await guideUseCase.getFloors()
guideFloors.value = floors
activeGuideFloor.value = floors.find((floor) => floor.label === activeGuideFloor.value)?.label
|| floors.find((floor) => floor.id === 'L1')?.label
|| floors[0]?.label
|| activeGuideFloor.value
} catch (error) {
console.error('加载导览楼层失败:', error)
guideFloors.value = []
}
}
const loadExplainExhibits = async () => {
try {
const exhibits = await explainUseCase.listExhibits()
explainExhibits.value = exhibits.map(toExplainExhibitViewModel)
} catch (error) {
console.error('加载讲解内容失败:', error)
explainExhibits.value = []
}
}
// 搜索处理
const handleSearchFocus = () => {
searchBarFocused.value = true
@@ -391,13 +437,23 @@ const guideSearchText = computed(() => {
})
// 音频相关处理
const handleAudioClick = (exhibit: any) => {
console.log('点击音频播放:', exhibit)
const handleAudioClick = async (exhibit: Exhibit) => {
const selection = await explainUseCase.selectAudioForExhibit(exhibit.id)
if (!selection?.playable || !selection.media?.url) {
uni.showToast({
title: selection?.unavailableMessage || exhibit.audioUnavailableReason || '该展项暂无讲解音频',
icon: 'none'
})
return
}
currentAudio.value = {
id: exhibit.id,
name: exhibit.name,
audioUrl: exhibit.audioUrl || 'https://example.com/audio.mp3',
image: exhibit.image
id: selection.media.id,
name: selection.track?.title || selection.exhibit.name,
audioUrl: selection.media.url,
image: selection.exhibit.image,
duration: selection.media.duration
}
showAudioPlayer.value = true
}
@@ -431,30 +487,14 @@ const handleAudioEnded = (audio: AudioItem) => {
}
// 讲解页面处理
const handleExplainExhibitClick = (exhibit: any) => {
console.log('点击讲解展品:', exhibit)
const handleExplainExhibitClick = (exhibit: Exhibit) => {
uni.navigateTo({
url: `/pages/exhibit/detail?id=${exhibit.id}&tab=explain`
})
}
const handleExplainFeaturedClick = (exhibit: any) => {
console.log('点击推荐讲解:', exhibit)
// 播放音频讲解
if (exhibit.hasAudio) {
currentAudio.value = {
id: exhibit.id,
name: exhibit.name,
audioUrl: exhibit.audioUrl || 'https://example.com/audio.mp3',
image: exhibit.image
}
showAudioPlayer.value = true
} else {
uni.showToast({
title: '该展品暂无讲解音频',
icon: 'none'
})
}
const handleExplainFeaturedClick = async (exhibit: Exhibit) => {
await handleAudioClick(exhibit)
}
</script>