chore: freeze guide and explain update
This commit is contained in:
221
src/pages/explain/list.vue
Normal file
221
src/pages/explain/list.vue
Normal file
@@ -0,0 +1,221 @@
|
||||
<template>
|
||||
<GuidePageFrame
|
||||
active-tab="explain"
|
||||
variant="static"
|
||||
show-back
|
||||
back-label="讲解首页"
|
||||
@tab-change="handleTopTabChange"
|
||||
@back="handleBack"
|
||||
>
|
||||
<view class="explain-all-page">
|
||||
<ExplainList
|
||||
:cards="explainExhibits"
|
||||
:loading="explainLoading"
|
||||
:error="explainError"
|
||||
list-title="全部讲解"
|
||||
page-mode="all"
|
||||
:initial-render-limit="48"
|
||||
:page-size="48"
|
||||
@exhibit-click="handleExplainExhibitClick"
|
||||
@featured-click="handleExplainFeaturedClick"
|
||||
@audio-click="handleAudioClick"
|
||||
@location-click="handleExplainLocationClick"
|
||||
@hall-click="handleExplainHallClick"
|
||||
/>
|
||||
|
||||
<AudioPlayer
|
||||
:visible="showAudioPlayer"
|
||||
:audio="currentAudio"
|
||||
:auto-play="true"
|
||||
@update:visible="handleAudioVisibleChange"
|
||||
@play="handleAudioPlay"
|
||||
@pause="handleAudioPause"
|
||||
@ended="handleAudioEnded"
|
||||
@error="handleAudioError"
|
||||
/>
|
||||
</view>
|
||||
</GuidePageFrame>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
||||
import ExplainList, { type Exhibit } from '@/components/explain/ExplainList.vue'
|
||||
import AudioPlayer, { type AudioItem } from '@/components/audio/AudioPlayer.vue'
|
||||
import {
|
||||
explainUseCase
|
||||
} from '@/usecases/explainUseCase'
|
||||
import {
|
||||
guideUseCase
|
||||
} from '@/usecases/guideUseCase'
|
||||
import {
|
||||
toExplainCardViewModel
|
||||
} from '@/view-models/explainViewModels'
|
||||
import {
|
||||
navigateToGuideTopTab,
|
||||
type GuideTopTab
|
||||
} from '@/utils/guideTopTabs'
|
||||
|
||||
const explainExhibits = ref<Exhibit[]>([])
|
||||
const explainLoading = ref(false)
|
||||
const explainError = ref('')
|
||||
const showAudioPlayer = ref(false)
|
||||
const isAudioPlaying = ref(false)
|
||||
const currentAudio = ref<AudioItem | null>(null)
|
||||
let explainLoadPromise: Promise<void> | null = null
|
||||
|
||||
const loadExplainExhibits = async () => {
|
||||
if (explainExhibits.value.length) return
|
||||
if (explainLoadPromise) return explainLoadPromise
|
||||
|
||||
explainLoading.value = true
|
||||
explainError.value = ''
|
||||
|
||||
explainLoadPromise = (async () => {
|
||||
try {
|
||||
const exhibits = await explainUseCase.listExplainExhibits()
|
||||
explainExhibits.value = exhibits.map(toExplainCardViewModel)
|
||||
} catch (error) {
|
||||
console.error('加载全部讲解内容失败:', error)
|
||||
explainError.value = '讲解内容加载失败,请稍后重试'
|
||||
explainExhibits.value = []
|
||||
} finally {
|
||||
explainLoading.value = false
|
||||
explainLoadPromise = null
|
||||
}
|
||||
})()
|
||||
|
||||
return explainLoadPromise
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
void loadExplainExhibits()
|
||||
})
|
||||
|
||||
const handleBack = () => {
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
fail: () => {
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index?tab=explain'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleTopTabChange = (tab: GuideTopTab) => {
|
||||
navigateToGuideTopTab(tab)
|
||||
}
|
||||
|
||||
const handleExplainExhibitClick = (exhibit: Exhibit) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/exhibit/detail?id=${exhibit.id}&tab=explain`
|
||||
})
|
||||
}
|
||||
|
||||
const handleExplainFeaturedClick = async (exhibit: Exhibit) => {
|
||||
await handleAudioClick(exhibit)
|
||||
}
|
||||
|
||||
const handleExplainHallClick = (hallId: string) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/hall/detail?id=${encodeURIComponent(hallId)}&tab=explain`
|
||||
})
|
||||
}
|
||||
|
||||
const handleAudioClick = async (exhibit: Exhibit) => {
|
||||
if (exhibit.audioUrl) {
|
||||
currentAudio.value = {
|
||||
id: `media-${exhibit.id}`,
|
||||
name: exhibit.title,
|
||||
audioUrl: exhibit.audioUrl,
|
||||
image: exhibit.coverImage,
|
||||
duration: exhibit.audioDuration
|
||||
}
|
||||
showAudioPlayer.value = true
|
||||
return
|
||||
}
|
||||
|
||||
const selection = await explainUseCase.selectAudioForExhibit(exhibit.id)
|
||||
|
||||
if (!selection?.playable || !selection.media?.url) {
|
||||
uni.showToast({
|
||||
title: selection?.unavailableMessage || exhibit.audioStatusText || '该讲解暂无可播放音频',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
currentAudio.value = {
|
||||
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
|
||||
}
|
||||
|
||||
const clearAudioState = () => {
|
||||
showAudioPlayer.value = false
|
||||
isAudioPlaying.value = false
|
||||
currentAudio.value = null
|
||||
}
|
||||
|
||||
const handleAudioVisibleChange = (visible: boolean) => {
|
||||
showAudioPlayer.value = visible
|
||||
if (!visible) {
|
||||
isAudioPlaying.value = false
|
||||
currentAudio.value = null
|
||||
}
|
||||
}
|
||||
|
||||
const handleAudioPlay = () => {
|
||||
isAudioPlaying.value = true
|
||||
}
|
||||
|
||||
const handleAudioPause = () => {
|
||||
isAudioPlaying.value = false
|
||||
}
|
||||
|
||||
const handleAudioEnded = () => {
|
||||
clearAudioState()
|
||||
}
|
||||
|
||||
const handleAudioError = (_audio: AudioItem | null, message: string) => {
|
||||
console.warn('全部讲解音频不可播放:', message)
|
||||
clearAudioState()
|
||||
}
|
||||
|
||||
const handleExplainLocationClick = async (exhibit: Exhibit) => {
|
||||
if (!exhibit.poiId) {
|
||||
uni.showToast({
|
||||
title: '该讲解暂无三维位置数据',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const matchedPoi = await guideUseCase.getPoiById(exhibit.poiId)
|
||||
if (!matchedPoi) {
|
||||
uni.showToast({
|
||||
title: '该讲解位置尚未映射到当前三维导览资源',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/route/detail?facilityId=${encodeURIComponent(exhibit.poiId)}&target=${encodeURIComponent(exhibit.title)}&state=preview`
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.explain-all-page {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: #edf0ea;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user