@@ -134,8 +134,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
||||||
import { onLoad, onUnload } from '@dcloudio/uni-app'
|
import { onHide, onLoad, onUnload } from '@dcloudio/uni-app'
|
||||||
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
||||||
import type { AudioItem } from '@/components/audio/AudioPlayer.vue'
|
import type { AudioItem } from '@/components/audio/AudioPlayer.vue'
|
||||||
import { useGlobalAudioPlayer } from '@/composables/useGlobalAudioPlayer'
|
import { useGlobalAudioPlayer } from '@/composables/useGlobalAudioPlayer'
|
||||||
@@ -305,6 +305,14 @@ const isCurrentDetailAudioTarget = computed(() => {
|
|||||||
&& source.targetId === targetId
|
&& source.targetId === targetId
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
let detailAudioClosedOnExit = false
|
||||||
|
|
||||||
|
const closeDetailAudioOnExit = () => {
|
||||||
|
if (detailAudioClosedOnExit || !isCurrentDetailAudioTarget.value) return
|
||||||
|
|
||||||
|
detailAudioClosedOnExit = true
|
||||||
|
globalAudioPlayer.close()
|
||||||
|
}
|
||||||
const audioDockTitle = computed(() => displayTitle.value || '讲解内容')
|
const audioDockTitle = computed(() => displayTitle.value || '讲解内容')
|
||||||
const formatDetailAudioTime = (seconds?: number) => {
|
const formatDetailAudioTime = (seconds?: number) => {
|
||||||
const normalizedSeconds = Number.isFinite(seconds || 0)
|
const normalizedSeconds = Number.isFinite(seconds || 0)
|
||||||
@@ -482,6 +490,7 @@ const loadExplainDetail = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
onLoad(async (options: any = {}) => {
|
onLoad(async (options: any = {}) => {
|
||||||
|
detailAudioClosedOnExit = false
|
||||||
const tab = Array.isArray(options.tab) ? options.tab[0] : options.tab
|
const tab = Array.isArray(options.tab) ? options.tab[0] : options.tab
|
||||||
if (isGuideTopTab(tab)) {
|
if (isGuideTopTab(tab)) {
|
||||||
activeTopTab.value = tab
|
activeTopTab.value = tab
|
||||||
@@ -525,9 +534,10 @@ onLoad(async (options: any = {}) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnload(() => {
|
// iOS H5 may hide a uni page before its route is fully unloaded.
|
||||||
globalAudioPlayer.close()
|
onHide(closeDetailAudioOnExit)
|
||||||
})
|
onUnload(closeDetailAudioOnExit)
|
||||||
|
onBeforeUnmount(closeDetailAudioOnExit)
|
||||||
|
|
||||||
const detailTextFor = (lang: AudioLanguage) => {
|
const detailTextFor = (lang: AudioLanguage) => {
|
||||||
return detailTextByLanguage.value[lang] || fallbackTextForLanguage(lang)
|
return detailTextByLanguage.value[lang] || fallbackTextForLanguage(lang)
|
||||||
@@ -908,6 +918,7 @@ const fallbackToExplainObjectList = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const returnToExplainObjectList = () => {
|
const returnToExplainObjectList = () => {
|
||||||
|
closeDetailAudioOnExit()
|
||||||
const pages = getCurrentPages()
|
const pages = getCurrentPages()
|
||||||
const previousPage = pages[pages.length - 2] as ExplainDetailPageStackEntry | undefined
|
const previousPage = pages[pages.length - 2] as ExplainDetailPageStackEntry | undefined
|
||||||
if (previousPage?.route === 'pages/explain/guide-stop-list') {
|
if (previousPage?.route === 'pages/explain/guide-stop-list') {
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { flushPromises, mount } from '@vue/test-utils'
|
|||||||
|
|
||||||
const mocks = vi.hoisted(() => ({
|
const mocks = vi.hoisted(() => ({
|
||||||
onLoadHandler: null as null | ((options?: Record<string, unknown>) => Promise<void>),
|
onLoadHandler: null as null | ((options?: Record<string, unknown>) => Promise<void>),
|
||||||
|
onHideHandler: null as null | (() => void),
|
||||||
|
onUnloadHandler: null as null | (() => void),
|
||||||
enterExplainDetail: vi.fn(),
|
enterExplainDetail: vi.fn(),
|
||||||
loadExplainDetailText: vi.fn(),
|
loadExplainDetailText: vi.fn(),
|
||||||
selectAudioForExplainDetail: vi.fn(),
|
selectAudioForExplainDetail: vi.fn(),
|
||||||
@@ -30,7 +32,12 @@ vi.mock('@dcloudio/uni-app', () => ({
|
|||||||
onLoad: (handler: (options?: Record<string, unknown>) => Promise<void>) => {
|
onLoad: (handler: (options?: Record<string, unknown>) => Promise<void>) => {
|
||||||
mocks.onLoadHandler = handler
|
mocks.onLoadHandler = handler
|
||||||
},
|
},
|
||||||
onUnload: () => undefined
|
onHide: (handler: () => void) => {
|
||||||
|
mocks.onHideHandler = handler
|
||||||
|
},
|
||||||
|
onUnload: (handler: () => void) => {
|
||||||
|
mocks.onUnloadHandler = handler
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('@/usecases/explainUseCase', () => ({
|
vi.mock('@/usecases/explainUseCase', () => ({
|
||||||
@@ -93,6 +100,8 @@ const exhibit = {
|
|||||||
describe('讲解详情音频优先布局', () => {
|
describe('讲解详情音频优先布局', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mocks.onLoadHandler = null
|
mocks.onLoadHandler = null
|
||||||
|
mocks.onHideHandler = null
|
||||||
|
mocks.onUnloadHandler = null
|
||||||
audioState.currentAudio.value = null
|
audioState.currentAudio.value = null
|
||||||
audioState.currentSource.value = null
|
audioState.currentSource.value = null
|
||||||
audioState.playing.value = false
|
audioState.playing.value = false
|
||||||
@@ -301,6 +310,24 @@ describe('讲解详情音频优先布局', () => {
|
|||||||
expect(uni.redirectTo).not.toHaveBeenCalled()
|
expect(uni.redirectTo).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('离开详情页时立即关闭当前讲解播放器', async () => {
|
||||||
|
audioState.currentAudio.value = { id: 'audio-1', audioUrl: exhibit.audioUrl }
|
||||||
|
audioState.currentSource.value = { targetType: 'STOP', targetId: 'stop-1', lang: 'en-US' }
|
||||||
|
const wrapper = mount(ExhibitDetail, {
|
||||||
|
global: { stubs: { GuidePageFrame: GuidePageFrameStub } }
|
||||||
|
})
|
||||||
|
|
||||||
|
await mocks.onLoadHandler?.({ id: exhibit.id, lang: 'en-US', targetType: 'STOP', targetId: 'stop-1' })
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
mocks.onHideHandler?.()
|
||||||
|
mocks.onUnloadHandler?.()
|
||||||
|
wrapper.getComponent(GuidePageFrameStub).vm.$emit('back')
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(mocks.close).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
it('深链详情按展厅上下文回退至讲解对象列表', async () => {
|
it('深链详情按展厅上下文回退至讲解对象列表', async () => {
|
||||||
const wrapper = mount(ExhibitDetail, {
|
const wrapper = mount(ExhibitDetail, {
|
||||||
global: { stubs: { GuidePageFrame: GuidePageFrameStub } }
|
global: { stubs: { GuidePageFrame: GuidePageFrameStub } }
|
||||||
|
|||||||
Reference in New Issue
Block a user