chore: freeze guide and explain update
This commit is contained in:
@@ -2,9 +2,11 @@
|
||||
<GuidePageFrame
|
||||
:active-tab="activeTopTab"
|
||||
variant="static"
|
||||
show-back
|
||||
@tab-change="handleTopTabChange"
|
||||
@back="handleBack"
|
||||
>
|
||||
<view class="detail-page">
|
||||
<view class="detail-page" :class="{ 'with-audio-player': showAudioPlayer }">
|
||||
<scroll-view class="content" scroll-y>
|
||||
<view class="detail-hero">
|
||||
<image
|
||||
@@ -59,7 +61,7 @@
|
||||
</view>
|
||||
|
||||
<view v-if="exhibit.audio.status !== 'playable'" class="audio-note">
|
||||
<text class="audio-note-title">音频待同步</text>
|
||||
<text class="audio-note-title">{{ audioNoteTitle }}</text>
|
||||
<text class="audio-note-desc">
|
||||
{{ exhibit.audio.unavailableReason || '当前仅提供图文讲解,正式音频接入后将显示播放入口。' }}
|
||||
</text>
|
||||
@@ -107,7 +109,7 @@
|
||||
|
||||
<view v-if="exhibit.location" class="location-note">
|
||||
<text class="location-title">位置说明</text>
|
||||
<text class="location-desc">当前支持三维位置预览,馆内路线规划待开放。</text>
|
||||
<text class="location-desc">{{ locationDescription }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
@@ -143,14 +145,27 @@
|
||||
<text class="btn-text">分享</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<AudioPlayer
|
||||
ref="audioPlayerRef"
|
||||
: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 { computed, ref } from 'vue'
|
||||
import { computed, nextTick, ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
||||
import AudioPlayer, { type AudioItem } from '@/components/audio/AudioPlayer.vue'
|
||||
import {
|
||||
explainUseCase
|
||||
} from '@/usecases/explainUseCase'
|
||||
@@ -174,17 +189,25 @@ const defaultDetail: ExplainDetailPageViewModel = {
|
||||
subtitle: '位置待补充',
|
||||
contentType: 'exhibit',
|
||||
coverImages: ['/static/exhibit-placeholder.jpg'],
|
||||
summary: '该讲解内容待 SGS 场景设置或 CMS 内容补充。',
|
||||
body: '该讲解内容待 SGS 场景设置或 CMS 内容补充。',
|
||||
summary: '该讲解内容待补充。',
|
||||
body: '该讲解内容待补充。',
|
||||
audio: {
|
||||
status: 'unavailable',
|
||||
unavailableReason: 'SGS 场景设置音频资源尚未同步到当前 H5 项目'
|
||||
unavailableReason: '该展项暂无已发布音频,当前提供图文讲解。'
|
||||
},
|
||||
chapters: [],
|
||||
relatedItems: []
|
||||
}
|
||||
|
||||
interface AudioPlayerExpose {
|
||||
play: (audio: AudioItem) => void
|
||||
pause: () => void
|
||||
}
|
||||
|
||||
const exhibit = ref<ExplainDetailPageViewModel>(defaultDetail)
|
||||
const audioPlayerRef = ref<AudioPlayerExpose | null>(null)
|
||||
const showAudioPlayer = ref(false)
|
||||
const currentAudio = ref<AudioItem | null>(null)
|
||||
const isPlaying = ref(false)
|
||||
const isCollected = ref(false)
|
||||
const activeTopTab = ref<GuideTopTab>('guide')
|
||||
@@ -197,9 +220,15 @@ const audioStatusText = computed(() => {
|
||||
if (exhibit.value.audio.status === 'none') {
|
||||
return '图文讲解'
|
||||
}
|
||||
return exhibit.value.audio.unavailableReason?.includes('包含音频地址') ? '音频待同步' : '图文讲解'
|
||||
return '图文讲解'
|
||||
})
|
||||
|
||||
const audioNoteTitle = computed(() => (
|
||||
exhibit.value.audio.status === 'playable'
|
||||
? '音频讲解'
|
||||
: '图文讲解'
|
||||
))
|
||||
|
||||
const contentTypeText = computed(() => {
|
||||
const map: Record<ExplainContentType, string> = {
|
||||
hall: '空间讲解',
|
||||
@@ -210,6 +239,10 @@ const contentTypeText = computed(() => {
|
||||
return map[exhibit.value.contentType]
|
||||
})
|
||||
|
||||
const locationDescription = computed(() => (
|
||||
exhibit.value.location?.note || '当前支持三维位置预览。'
|
||||
))
|
||||
|
||||
onLoad(async (options: any) => {
|
||||
if (options.id) {
|
||||
const exhibitData = await explainUseCase.getExhibitById(options.id)
|
||||
@@ -231,6 +264,31 @@ const formatChapterTime = (seconds: number) => {
|
||||
}
|
||||
|
||||
const handlePlayAudio = async () => {
|
||||
if (exhibit.value.audio.url) {
|
||||
const audio: AudioItem = {
|
||||
id: `media-${exhibit.value.id}`,
|
||||
name: exhibit.value.title,
|
||||
audioUrl: exhibit.value.audio.url,
|
||||
image: heroImage.value,
|
||||
duration: undefined
|
||||
}
|
||||
|
||||
const isSameAudio = currentAudio.value?.id === audio.id
|
||||
if (showAudioPlayer.value && isSameAudio && isPlaying.value) {
|
||||
audioPlayerRef.value?.pause()
|
||||
return
|
||||
}
|
||||
|
||||
showAudioPlayer.value = true
|
||||
currentAudio.value = audio
|
||||
await nextTick()
|
||||
|
||||
if (!isPlaying.value) {
|
||||
audioPlayerRef.value?.play(audio)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const selection = exhibit.value.id
|
||||
? await explainUseCase.selectAudioForExhibit(exhibit.value.id)
|
||||
: null
|
||||
@@ -243,7 +301,56 @@ const handlePlayAudio = async () => {
|
||||
return
|
||||
}
|
||||
|
||||
isPlaying.value = !isPlaying.value
|
||||
const audio: AudioItem = {
|
||||
id: selection.media.id,
|
||||
name: selection.track?.title || selection.exhibit.name,
|
||||
audioUrl: selection.media.url,
|
||||
image: selection.exhibit.image || heroImage.value,
|
||||
duration: selection.media.duration
|
||||
}
|
||||
|
||||
const isSameAudio = currentAudio.value?.id === audio.id
|
||||
if (showAudioPlayer.value && isSameAudio && isPlaying.value) {
|
||||
audioPlayerRef.value?.pause()
|
||||
return
|
||||
}
|
||||
|
||||
showAudioPlayer.value = true
|
||||
currentAudio.value = audio
|
||||
await nextTick()
|
||||
|
||||
if (!isPlaying.value) {
|
||||
audioPlayerRef.value?.play(audio)
|
||||
}
|
||||
}
|
||||
|
||||
const handleAudioVisibleChange = (visible: boolean) => {
|
||||
showAudioPlayer.value = visible
|
||||
if (!visible) {
|
||||
isPlaying.value = false
|
||||
currentAudio.value = null
|
||||
}
|
||||
}
|
||||
|
||||
const handleAudioPlay = () => {
|
||||
isPlaying.value = true
|
||||
}
|
||||
|
||||
const handleAudioPause = () => {
|
||||
isPlaying.value = false
|
||||
}
|
||||
|
||||
const handleAudioEnded = () => {
|
||||
isPlaying.value = false
|
||||
showAudioPlayer.value = false
|
||||
currentAudio.value = null
|
||||
}
|
||||
|
||||
const handleAudioError = (_audio: AudioItem | null, message: string) => {
|
||||
console.warn('详情音频不可播放:', message)
|
||||
isPlaying.value = false
|
||||
showAudioPlayer.value = false
|
||||
currentAudio.value = null
|
||||
}
|
||||
|
||||
const handleNavigate = async () => {
|
||||
@@ -284,6 +391,17 @@ const handleShare = () => {
|
||||
const handleTopTabChange = (tab: GuideTopTab) => {
|
||||
navigateToGuideTopTab(tab)
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
fail: () => {
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index?tab=explain'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -530,6 +648,10 @@ const handleTopTabChange = (tab: GuideTopTab) => {
|
||||
border-top: 1px solid #e7e9e0;
|
||||
}
|
||||
|
||||
.detail-page.with-audio-player .action-bar {
|
||||
margin-bottom: 88px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
height: 46px;
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user