修复小程序讲解内部返回控件
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
lyf
2026-07-28 14:42:02 +08:00
parent 2fb70fa1e3
commit 06ad609e1f
5 changed files with 19 additions and 9 deletions

View File

@@ -1,11 +1,11 @@
<template> <template>
<view class="guide-stop-catalog" :class="{ 'host-navigation': shouldUseHostNavigation }"> <view class="guide-stop-catalog" :class="{ 'host-navigation': shouldUseHostNavigation, 'has-embedded-back': showEmbeddedBack }">
<view v-if="showInternalHeader" class="catalog-header"> <view v-if="showInternalHeader || showEmbeddedBack" class="catalog-header">
<view class="header-back" @tap="emit('back')"> <view v-if="showInternalBack" class="header-back" @tap="emit('back')">
<text class="header-back-icon" /> <text class="header-back-icon" />
<text class="header-back-text">返回</text> <text class="header-back-text">返回</text>
</view> </view>
<text class="header-title">{{ selectedHallName || '讲解对象' }}</text> <text v-if="showInternalHeader" class="header-title">{{ selectedHallName || '讲解对象' }}</text>
</view> </view>
<scroll-view class="explain-scroll" scroll-y :show-scrollbar="false" @scroll="handleScroll" @scrolltolower="requestMore"> <scroll-view class="explain-scroll" scroll-y :show-scrollbar="false" @scroll="handleScroll" @scrolltolower="requestMore">
@@ -71,16 +71,18 @@ export interface ExplainGuideStopCatalogItem {
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
guideStops?: ExplainGuideStopCatalogItem[] guideStops?: ExplainGuideStopCatalogItem[]
selectedHallName?: string selectedHallName?: string
showEmbeddedBack?: boolean
loading?: boolean loading?: boolean
loadingMore?: boolean loadingMore?: boolean
hasMore?: boolean hasMore?: boolean
error?: string error?: string
loadMoreError?: string loadMoreError?: string
}>(), { guideStops: () => [], selectedHallName: '', loading: false, loadingMore: false, hasMore: false, error: '', loadMoreError: '' }) }>(), { guideStops: () => [], selectedHallName: '', showEmbeddedBack: false, loading: false, loadingMore: false, hasMore: false, error: '', loadMoreError: '' })
const emit = defineEmits<{ guideStopClick: [stop: ExplainGuideStopCatalogItem], back: [], retry: [], retryMore: [] }>() const emit = defineEmits<{ guideStopClick: [stop: ExplainGuideStopCatalogItem], back: [], retry: [], retryMore: [] }>()
const shouldUseHostNavigation = computed(() => isEmbeddedInWechatMiniProgram()) const shouldUseHostNavigation = computed(() => isEmbeddedInWechatMiniProgram())
const showInternalHeader = computed(() => !shouldUseHostNavigation.value) const showInternalHeader = computed(() => !shouldUseHostNavigation.value)
const showInternalBack = computed(() => !shouldUseHostNavigation.value || props.showEmbeddedBack)
const failedImageIds = ref(new Set<string>()) const failedImageIds = ref(new Set<string>())
const visibleCoverIds = ref(new Set<string>()) const visibleCoverIds = ref(new Set<string>())
const coverSlots = new Map<string, Element>() const coverSlots = new Map<string, Element>()
@@ -155,7 +157,7 @@ const handleScroll = (event: Event) => { const target = event.target as HTMLElem
.catalog-header { position: absolute; z-index: 2; top: 0; left: 0; right: 0; height: 64px; display: flex; align-items: center; justify-content: center; background: #f7f9f2; } .catalog-header { position: absolute; z-index: 2; top: 0; left: 0; right: 0; height: 64px; display: flex; align-items: center; justify-content: center; background: #f7f9f2; }
.header-back { position: absolute; left: 12px; top: 10px; width: 44px; height: 44px; display: flex; align-items: center; }.header-back-icon { width: 20px; height: 20px; position: relative; } .header-back-icon::before { content: ''; position: absolute; left: 3px; top: 4px; width: 10px; height: 10px; border-left: 1.5px solid #262421; border-bottom: 1.5px solid #262421; transform: rotate(45deg); } .header-back { position: absolute; left: 12px; top: 10px; width: 44px; height: 44px; display: flex; align-items: center; }.header-back-icon { width: 20px; height: 20px; position: relative; } .header-back-icon::before { content: ''; position: absolute; left: 3px; top: 4px; width: 10px; height: 10px; border-left: 1.5px solid #262421; border-bottom: 1.5px solid #262421; transform: rotate(45deg); }
.header-back-text { display: none; }.header-title { max-width: calc(100% - 152px); padding: 0 8px; font-size: 18px; line-height: 26px; font-weight: 700; color: #262421; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .header-back-text { display: none; }.header-title { max-width: calc(100% - 152px); padding: 0 8px; font-size: 18px; line-height: 26px; font-weight: 700; color: #262421; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.explain-scroll { height: 100%; padding: 80px 16px calc(16px + env(safe-area-inset-bottom)); box-sizing: border-box; background: #f7f9f2; }.host-navigation .explain-scroll { padding-top: 16px; } .explain-scroll { height: 100%; padding: 80px 16px calc(16px + env(safe-area-inset-bottom)); box-sizing: border-box; background: #f7f9f2; }.host-navigation .explain-scroll { padding-top: 16px; }.host-navigation.has-embedded-back .explain-scroll { padding-top: 80px; }
.stop-list { display: flex; width: 100%; min-width: 0; flex-direction: column; gap: 4px; }.stop-card { width: 100%; min-width: 0; height: 80px; padding: 8px 0; display: flex; align-items: center; gap: 12px; box-sizing: border-box; overflow: hidden; border: 0; border-bottom: 1px solid #dbded4; border-radius: 0; background: transparent; }.stop-card:active { background: rgba(227, 235, 201, 0.4); }.stop-cover { flex: 0 0 64px; width: 64px; height: 64px; overflow: hidden; border-radius: 6px; background: #f5f5ed; }.stop-cover-fallback { font-size: 22px; line-height: 28px; color: #a9b29b; }.stop-copy { min-width: 0; display: flex; flex: 1; overflow: hidden; }.stop-name { display: -webkit-box; max-height: 40px; font-size: 14px; line-height: 20px; font-weight: 700; color: #262421; overflow: hidden; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }.stop-status { flex: 0 0 44px; width: 44px; height: 24px; display: flex; align-items: center; justify-content: center; border-radius: 12px; background: #e3ebc9; }.stop-status-text { font-size: 11px; line-height: 16px; color: #262421; white-space: nowrap; } .stop-list { display: flex; width: 100%; min-width: 0; flex-direction: column; gap: 4px; }.stop-card { width: 100%; min-width: 0; height: 80px; padding: 8px 0; display: flex; align-items: center; gap: 12px; box-sizing: border-box; overflow: hidden; border: 0; border-bottom: 1px solid #dbded4; border-radius: 0; background: transparent; }.stop-card:active { background: rgba(227, 235, 201, 0.4); }.stop-cover { flex: 0 0 64px; width: 64px; height: 64px; overflow: hidden; border-radius: 6px; background: #f5f5ed; }.stop-cover-fallback { font-size: 22px; line-height: 28px; color: #a9b29b; }.stop-copy { min-width: 0; display: flex; flex: 1; overflow: hidden; }.stop-name { display: -webkit-box; max-height: 40px; font-size: 14px; line-height: 20px; font-weight: 700; color: #262421; overflow: hidden; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }.stop-status { flex: 0 0 44px; width: 44px; height: 24px; display: flex; align-items: center; justify-content: center; border-radius: 12px; background: #e3ebc9; }.stop-status-text { font-size: 11px; line-height: 16px; color: #262421; white-space: nowrap; }
.state-block { margin: 34px 0; padding: 22px 16px; box-sizing: border-box; text-align: center; border: 1px solid #e4e6df; border-radius: 8px; background: #fff; }.state-block.error { border-color: #e5c2c2; background: #fff7f7; }.state-title,.state-desc { display: block; }.state-title { font-size: 15px; line-height: 21px; font-weight: 700; color: #141412; }.state-desc { margin-top: 6px; font-size: 12px; line-height: 18px; color: #68725d; }.state-retry { margin-top: 12px; padding: 0 14px; font-size: 13px; line-height: 32px; color: #141412; background: #e0df00; border: 0; border-radius: 6px; }.load-more-state { min-height: 52px; padding: 12px 16px calc(12px + env(safe-area-inset-bottom)); text-align: center; } .state-block { margin: 34px 0; padding: 22px 16px; box-sizing: border-box; text-align: center; border: 1px solid #e4e6df; border-radius: 8px; background: #fff; }.state-block.error { border-color: #e5c2c2; background: #fff7f7; }.state-title,.state-desc { display: block; }.state-title { font-size: 15px; line-height: 21px; font-weight: 700; color: #141412; }.state-desc { margin-top: 6px; font-size: 12px; line-height: 18px; color: #68725d; }.state-retry { margin-top: 12px; padding: 0 14px; font-size: 13px; line-height: 32px; color: #141412; background: #e0df00; border: 0; border-radius: 6px; }.load-more-state { min-height: 52px; padding: 12px 16px calc(12px + env(safe-area-inset-bottom)); text-align: center; }
</style> </style>

View File

@@ -35,7 +35,7 @@
/> />
<view class="hero-image-shade"></view> <view class="hero-image-shade"></view>
<button <button
v-if="!shouldUseHostNavigation" v-if="shouldShowDetailBack"
class="hero-back" class="hero-back"
aria-label="返回上级" aria-label="返回上级"
@tap.stop="handleBack" @tap.stop="handleBack"
@@ -253,6 +253,9 @@ const detailTextLoadedByLanguage = ref<Record<AudioLanguage, boolean>>({
'en-US': false 'en-US': false
}) })
const shouldUseHostNavigation = computed(() => isEmbeddedInWechatMiniProgram()) const shouldUseHostNavigation = computed(() => isEmbeddedInWechatMiniProgram())
const shouldShowDetailBack = computed(() => (
!shouldUseHostNavigation.value || detailEntryRequest.value?.returnToGuideStopList === true
))
const isAudioLanguage = (value: unknown): value is AudioLanguage => ( const isAudioLanguage = (value: unknown): value is AudioLanguage => (
value === 'zh-CN' || value === 'yue-HK' || value === 'en-US' value === 'zh-CN' || value === 'yue-HK' || value === 'en-US'

View File

@@ -4,6 +4,7 @@
<ExplainGuideStopCatalog <ExplainGuideStopCatalog
:guide-stops="explainGuideStopItems" :guide-stops="explainGuideStopItems"
:selected-hall-name="selectedExplainHallName" :selected-hall-name="selectedExplainHallName"
:show-embedded-back="shouldReturnToHallList"
:loading="explainLoading" :loading="explainLoading"
:loading-more="loadingMore" :loading-more="loadingMore"
:has-more="hasMore" :has-more="hasMore"

View File

@@ -403,6 +403,7 @@ describe('讲解详情音频优先布局', () => {
hallId: 'hall-1', hallId: 'hall-1',
hallName: '恐龙厅' hallName: '恐龙厅'
}) })
expect(wrapper.find('.hero-back').exists()).toBe(false)
wrapper.getComponent(GuidePageFrameStub).vm.$emit('back') wrapper.getComponent(GuidePageFrameStub).vm.$emit('back')
await flushPromises() await flushPromises()
@@ -427,7 +428,8 @@ describe('讲解详情音频优先布局', () => {
returnToGuideStopList: '1', returnToGuideStopList: '1',
returnToHallList: '1' returnToHallList: '1'
}) })
wrapper.getComponent(GuidePageFrameStub).vm.$emit('back') expect(wrapper.find('.hero-back').exists()).toBe(true)
await wrapper.get('.hero-back').trigger('tap')
await flushPromises() await flushPromises()
const url = vi.mocked(uni.redirectTo).mock.calls[0]?.[0]?.url || '' const url = vi.mocked(uni.redirectTo).mock.calls[0]?.[0]?.url || ''

View File

@@ -52,7 +52,7 @@ const GuidePageFrameStub = defineComponent({
const ExplainGuideStopCatalogStub = defineComponent({ const ExplainGuideStopCatalogStub = defineComponent({
name: 'ExplainGuideStopCatalog', name: 'ExplainGuideStopCatalog',
props: ['guideStops', 'selectedHallName', 'loading', 'loadingMore', 'hasMore', 'error', 'loadMoreError'], props: ['guideStops', 'selectedHallName', 'showEmbeddedBack', 'loading', 'loadingMore', 'hasMore', 'error', 'loadMoreError'],
emits: ['back', 'retry', 'retryMore', 'guideStopClick'], emits: ['back', 'retry', 'retryMore', 'guideStopClick'],
setup(_, { emit }) { setup(_, { emit }) {
return { return {
@@ -200,6 +200,8 @@ describe('讲解对象列表返回', () => {
testState.onLoadHandler?.({ hallId: 'hall-1', hallName: '恐龙厅', returnToHallList: '1' }) testState.onLoadHandler?.({ hallId: 'hall-1', hallName: '恐龙厅', returnToHallList: '1' })
await flushPromises() await flushPromises()
expect(wrapper.getComponent(ExplainGuideStopCatalogStub).props('showEmbeddedBack')).toBe(true)
await wrapper.get('[data-testid="detail-click"]').trigger('click') await wrapper.get('[data-testid="detail-click"]').trigger('click')
const detailUrl = vi.mocked(uni.navigateTo).mock.calls[0]?.[0]?.url || '' const detailUrl = vi.mocked(uni.navigateTo).mock.calls[0]?.[0]?.url || ''
expect(new URLSearchParams(detailUrl.split('?')[1]).get('returnToHallList')).toBe('1') expect(new URLSearchParams(detailUrl.split('?')[1]).get('returnToHallList')).toBe('1')