修复讲解列表嵌入小程序标题回退
Some checks failed
CI / verify (push) Has been cancelled

- 同步讲解列表层级标题到宿主导航栏
- 嵌入小程序时为展厅/业务单元层级写入 H5 历史
- 保留普通 H5 自绘标题和返回行为

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
lyf
2026-07-10 11:54:15 +08:00
parent 5a218862e6
commit 2ca978b9b4

View File

@@ -28,8 +28,8 @@
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { computed, ref, watch } from 'vue'
import { onLoad, onUnload } from '@dcloudio/uni-app'
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
import ExplainHallSelect, {
type ExplainBusinessUnitSelectItem,
@@ -51,6 +51,7 @@ import {
navigateToGuideTopTab,
type GuideTopTab
} from '@/utils/guideTopTabs'
import { isEmbeddedInWechatMiniProgram } from '@/utils/hostEnvironment'
const explainHallItems = ref<ExplainHallSelectItem[]>([])
const explainBusinessUnits = ref<ExplainBusinessUnit[]>([])
@@ -89,6 +90,24 @@ const buildExplainHallItems = (
const selectedExplainBusinessUnit = computed(() => (
explainBusinessUnits.value.find((unit) => unit.id === selectedExplainBusinessUnitId.value) || null
))
const explainPageTitle = computed(() => {
if (explainStage.value === 'unit') return selectedExplainHallName.value || '选择业务单元'
if (explainStage.value === 'stop') return selectedExplainBusinessUnitName.value || '选择讲解点'
return '讲解'
})
const shouldUseHostNavigation = computed(() => isEmbeddedInWechatMiniProgram())
const syncHostNavigationTitle = () => {
const title = explainPageTitle.value
if (typeof uni !== 'undefined') {
uni.setNavigationBarTitle({ title })
}
if (typeof document !== 'undefined') {
document.title = title
}
}
const explainBusinessUnitItems = computed<ExplainBusinessUnitSelectItem[]>(() => (
explainBusinessUnits.value.map((unit) => ({
@@ -120,6 +139,125 @@ const explainGuideStopItems = computed<ExplainGuideStopSelectItem[]>(() => (
})) || []
))
type ExplainHistoryState = {
museumExplainList?: boolean
stage?: ExplainSelectStage
hallId?: string
hallName?: string
unitId?: string
unitName?: string
}
let syncingExplainHistory = false
let applyingExplainHistory = false
const stageRank: Record<ExplainSelectStage, number> = {
hall: 0,
unit: 1,
stop: 2
}
const currentExplainHistoryState = (): ExplainHistoryState => ({
museumExplainList: true,
stage: explainStage.value,
hallId: selectedExplainHallId.value,
hallName: selectedExplainHallName.value,
unitId: selectedExplainBusinessUnitId.value,
unitName: selectedExplainBusinessUnitName.value
})
const writeExplainHistoryState = (replace = false) => {
if (!shouldUseHostNavigation.value || typeof window === 'undefined') return
syncingExplainHistory = true
const state = currentExplainHistoryState()
const url = window.location.href
if (replace) {
window.history.replaceState(state, '', url)
} else {
window.history.pushState(state, '', url)
}
window.setTimeout(() => {
syncingExplainHistory = false
}, 0)
}
const applyExplainHistoryState = async (state: ExplainHistoryState | null) => {
if (!state?.museumExplainList || applyingExplainHistory) return
applyingExplainHistory = true
const targetStage = state.stage || 'hall'
if (targetStage === 'hall') {
explainStage.value = 'hall'
selectedExplainHallId.value = ''
selectedExplainHallName.value = ''
selectedExplainBusinessUnitId.value = ''
selectedExplainBusinessUnitName.value = ''
explainBusinessUnits.value = []
explainError.value = ''
applyingExplainHistory = false
return
}
if (state.hallId) {
selectedExplainHallId.value = state.hallId
selectedExplainHallName.value = state.hallName || '业务单元'
selectedExplainBusinessUnitId.value = ''
selectedExplainBusinessUnitName.value = ''
explainStage.value = 'unit'
if (!explainBusinessUnits.value.length || explainBusinessUnits.value[0]?.hallId !== state.hallId) {
explainLoading.value = true
explainError.value = ''
try {
explainBusinessUnits.value = await explainUseCase.loadTemporaryBusinessUnitsByHall(state.hallId)
} catch (error) {
console.error('恢复展厅讲解点失败:', error)
explainError.value = '展厅讲解点加载失败,请稍后重试'
explainBusinessUnits.value = []
} finally {
explainLoading.value = false
}
}
}
if (targetStage === 'stop' && state.unitId) {
selectedExplainBusinessUnitId.value = state.unitId
selectedExplainBusinessUnitName.value = state.unitName || '讲解点'
explainStage.value = 'stop'
}
applyingExplainHistory = false
}
const handleExplainHistoryPopState = (event: PopStateEvent) => {
if (syncingExplainHistory) return
void applyExplainHistoryState((event.state || null) as ExplainHistoryState | null)
}
const pushExplainStageHistory = (previousStage: ExplainSelectStage) => {
if (!shouldUseHostNavigation.value || applyingExplainHistory) return
if (stageRank[explainStage.value] <= stageRank[previousStage]) return
writeExplainHistoryState()
}
const replaceExplainStageHistory = () => {
writeExplainHistoryState(true)
}
watch(explainPageTitle, syncHostNavigationTitle)
watch(shouldUseHostNavigation, (value) => {
if (value) {
syncHostNavigationTitle()
replaceExplainStageHistory()
}
})
const loadExplainHalls = async () => {
if (explainHallItems.value.length) return
if (explainLoadPromise) return explainLoadPromise
@@ -146,7 +284,19 @@ const loadExplainHalls = async () => {
}
onLoad(() => {
syncHostNavigationTitle()
replaceExplainStageHistory()
void loadExplainHalls()
if (typeof window !== 'undefined') {
window.addEventListener('popstate', handleExplainHistoryPopState)
}
})
onUnload(() => {
if (typeof window !== 'undefined') {
window.removeEventListener('popstate', handleExplainHistoryPopState)
}
})
const handleBack = () => {
@@ -170,6 +320,7 @@ const handleTopTabChange = (tab: GuideTopTab) => {
}
const handleExplainHallClick = async (hallId: string) => {
const previousStage = explainStage.value
const hall = explainHallItems.value.find((item) => item.id === hallId)
selectedExplainHallId.value = hallId
selectedExplainHallName.value = hall?.name || '业务单元'
@@ -182,20 +333,24 @@ const handleExplainHallClick = async (hallId: string) => {
try {
explainBusinessUnits.value = await explainUseCase.loadTemporaryBusinessUnitsByHall(hallId)
explainStage.value = 'unit'
pushExplainStageHistory(previousStage)
} catch (error) {
console.error('加载展厅讲解点失败:', error)
explainError.value = '展厅讲解点加载失败,请稍后重试'
explainStage.value = 'unit'
pushExplainStageHistory(previousStage)
} finally {
explainLoading.value = false
}
}
const handleExplainBusinessUnitClick = (unitId: string) => {
const previousStage = explainStage.value
const unit = explainBusinessUnits.value.find((item) => item.id === unitId)
selectedExplainBusinessUnitId.value = unitId
selectedExplainBusinessUnitName.value = unit?.name || '讲解点'
explainStage.value = 'stop'
pushExplainStageHistory(previousStage)
}
const handleExplainGuideStopClick = (stop: ExplainGuideStopSelectItem) => {
@@ -222,6 +377,7 @@ const handlePanelBack = () => {
selectedExplainBusinessUnitId.value = ''
selectedExplainBusinessUnitName.value = ''
explainError.value = ''
replaceExplainStageHistory()
return
}
@@ -233,6 +389,7 @@ const handlePanelBack = () => {
selectedExplainBusinessUnitName.value = ''
explainBusinessUnits.value = []
explainError.value = ''
replaceExplainStageHistory()
return
}