修复首页快捷入口与三维点位同步
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
cxk
2026-07-21 00:40:06 +08:00
parent 1f89e8b3e0
commit b90c58451c
7 changed files with 257 additions and 16 deletions

View File

@@ -583,17 +583,21 @@ const markFloorSwitchFailedIfUnrendered = (floorId: string, requestSeq: number)
}) })
} }
const handleFloorChange = (floor: { id: string; label: string }) => { const requestFloorSwitch = (
floor: { id: string; label: string },
options: { force?: boolean } = {}
) => {
const floorId = floor.id const floorId = floor.id
if (!floorId || loadingFloorId.value) return if (!floorId) return Promise.resolve()
if (loadingFloorId.value === floorId) return Promise.resolve()
if ( if (
floorId === renderedFloorId.value !options.force && floorId === renderedFloorId.value
&& activeFloorId.value === floorId && activeFloorId.value === floorId
&& props.indoorView === 'floor' && props.indoorView === 'floor'
&& props.layerMode !== 'multi' && props.layerMode !== 'multi'
) { ) {
emit('floorChange', floorId) emit('floorChange', floorId)
return return Promise.resolve()
} }
requestedFloorId.value = floorId requestedFloorId.value = floorId
@@ -607,7 +611,7 @@ const handleFloorChange = (floor: { id: string; label: string }) => {
floorId, floorId,
floorLabel: floor.label floorLabel: floor.label
}) })
Promise.resolve(indoorRendererRef.value?.switchFloor?.(floorId)) return Promise.resolve(indoorRendererRef.value?.switchFloor?.(floorId))
.then(() => { .then(() => {
markFloorSwitchFailedIfUnrendered(floorId, requestSeq) markFloorSwitchFailedIfUnrendered(floorId, requestSeq)
}) })
@@ -623,6 +627,10 @@ const handleFloorChange = (floor: { id: string; label: string }) => {
}) })
} }
const handleFloorChange = (floor: { id: string; label: string }) => {
void requestFloorSwitch(floor)
}
const handleLayerModeChange = (mode: LayerDisplayMode) => { const handleLayerModeChange = (mode: LayerDisplayMode) => {
// 手动切换展示层数时使用统一的短保护期。 // 手动切换展示层数时使用统一的短保护期。
indoorRendererRef.value?.disableAutoSwitchTemporarily?.(manualAutoSwitchPauseMs) indoorRendererRef.value?.disableAutoSwitchTemporarily?.(manualAutoSwitchPauseMs)
@@ -789,7 +797,11 @@ defineExpose({
indoorRendererRef.value?.clearRoute?.() indoorRendererRef.value?.clearRoute?.()
}, },
// 仅发起切换;父级必须以 floor-change 作为已提交的唯一依据。 // 仅发起切换;父级必须以 floor-change 作为已提交的唯一依据。
switchFloor: (floorId: string) => indoorRendererRef.value?.switchFloor?.(floorId), switchFloor: (floorId: string) => {
const floor = findFloorItemById(floorId)
if (!floor) return Promise.resolve()
return requestFloorSwitch(floor, { force: true })
},
showOverview: handleShowOverview, showOverview: handleShowOverview,
resetToViewBaseline: (options: { resetToViewBaseline: (options: {
view: 'overview' | 'floor' view: 'overview' | 'floor'

View File

@@ -248,6 +248,7 @@ import type {
PoiCategoryResultState, PoiCategoryResultState,
PoiSearchContext PoiSearchContext
} from '@/domain/poiSearch' } from '@/domain/poiSearch'
import { nextHomeSearchResultVersion } from './homeSearchResultVersion'
import { isEmbeddedInWechatMiniProgram } from '@/utils/hostEnvironment' import { isEmbeddedInWechatMiniProgram } from '@/utils/hostEnvironment'
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
@@ -397,7 +398,8 @@ const emitResultsState = () => {
emit('results-change', { emit('results-change', {
...context, ...context,
visiblePoiIds: active ? context.visiblePoiIds : [], visiblePoiIds: active ? context.visiblePoiIds : [],
active active,
requestId: props.variant === 'home' ? nextHomeSearchResultVersion() : undefined
}) })
} }
@@ -411,7 +413,8 @@ const emitPendingHomeSearchResults = (floor?: Pick<MuseumFloor, 'id' | 'label'>)
floorLabel: floor?.label || context.floorLabel, floorLabel: floor?.label || context.floorLabel,
visiblePoiIds: [], visiblePoiIds: [],
active: true, active: true,
pending: true pending: true,
requestId: nextHomeSearchResultVersion()
}) })
} }

View File

@@ -0,0 +1,7 @@
// This version survives a PoiSearchPanel remount, unlike its local request sequence.
let latestHomeSearchResultVersion = 0
export const nextHomeSearchResultVersion = () => {
latestHomeSearchResultVersion += 1
return latestHomeSearchResultVersion
}

View File

@@ -28,6 +28,8 @@ export interface PoiSearchSelection {
export interface PoiCategoryResultState extends PoiSearchContext { export interface PoiCategoryResultState extends PoiSearchContext {
active: boolean active: boolean
pending?: boolean pending?: boolean
/** Monotonic home-result event version used to reject stale cross-component state. */
requestId?: number
} }
export type GuidePoiSearchMode = 'default' | 'category' | 'keyword' export type GuidePoiSearchMode = 'default' | 'category' | 'keyword'

View File

@@ -373,6 +373,8 @@ const requestedFloorId = ref('')
const requestedFloorLabel = ref('') const requestedFloorLabel = ref('')
const loadingFloorId = ref('') const loadingFloorId = ref('')
const renderedFloorId = ref('') const renderedFloorId = ref('')
// Only GuideMapShell's floor-change confirms that the Three.js floor is usable.
const committedFloorId = ref('')
const failedFloorId = ref('') const failedFloorId = ref('')
const selectedGuidePoi = ref<GuideRenderPoi | null>(null) const selectedGuidePoi = ref<GuideRenderPoi | null>(null)
const selectedGuidePoiDetailTarget = ref<PoiDetailTarget | null>(null) const selectedGuidePoiDetailTarget = ref<PoiDetailTarget | null>(null)
@@ -432,6 +434,8 @@ const homeSearchExpanded = ref(false)
const homeCategoryModeActive = ref(false) const homeCategoryModeActive = ref(false)
const searchVisiblePoiIds = ref<string[] | null>(null) const searchVisiblePoiIds = ref<string[] | null>(null)
const searchTargetFocusRequest = ref<TargetPoiFocusRequestViewModel | null>(null) const searchTargetFocusRequest = ref<TargetPoiFocusRequestViewModel | null>(null)
const homeSearchMapState = ref<PoiCategoryResultState | null>(null)
let latestHomeSearchMapRequestId = 0
type PoiSearchOverlayHistoryState = { type PoiSearchOverlayHistoryState = {
museumGuideOverlay?: 'poi-search' museumGuideOverlay?: 'poi-search'
[key: string]: unknown [key: string]: unknown
@@ -853,6 +857,9 @@ const handleTabChange = (tabId: GuideTopTab) => {
} }
} }
if (tabId !== currentTab.value) {
invalidateHomeSearchFloorCommit()
}
currentTab.value = tabId currentTab.value = tabId
syncTopTabToUrl(tabId) syncTopTabToUrl(tabId)
loadTopTabData(tabId) loadTopTabData(tabId)
@@ -875,6 +882,7 @@ const syncTopTabFromHash = () => {
currentTab.value = tab currentTab.value = tab
loadTopTabData(tab) loadTopTabData(tab)
if (tabChanged) { if (tabChanged) {
invalidateHomeSearchFloorCommit()
showRoutePlanner.value = false showRoutePlanner.value = false
isSimulatingRoute.value = false isSimulatingRoute.value = false
isPoiCardCollapsed.value = false isPoiCardCollapsed.value = false
@@ -1000,11 +1008,19 @@ const handleFloorRequest = ({ floorId, floorLabel }: { floorId: string; floorLab
requestedFloorLabel.value = floorLabel requestedFloorLabel.value = floorLabel
loadingFloorId.value = floorId loadingFloorId.value = floorId
failedFloorId.value = '' failedFloorId.value = ''
if (homeSearchMapState.value?.active) {
searchVisiblePoiIds.value = []
}
} }
const handleFloorChange = (floorId: string) => { const handleFloorChange = (floorId: string) => {
activeGuideFloor.value = floorId activeGuideFloor.value = floorId
renderedFloorId.value = floorId renderedFloorId.value = floorId
committedFloorId.value = floorId
if (requestedFloorId.value === floorId) {
requestedFloorId.value = ''
requestedFloorLabel.value = ''
}
if (loadingFloorId.value === floorId) { if (loadingFloorId.value === floorId) {
loadingFloorId.value = '' loadingFloorId.value = ''
} }
@@ -1014,6 +1030,7 @@ const handleFloorChange = (floorId: string) => {
indoorView.value = 'floor' indoorView.value = 'floor'
selectedGuidePoi.value = null selectedGuidePoi.value = null
isPoiCardCollapsed.value = false isPoiCardCollapsed.value = false
syncHomeSearchMarkersForCommittedFloor(floorId)
showIndoorHint(`已切换到 ${getGuideFloorLabel(floorId)},单指平移、双指可缩放`, 3200) showIndoorHint(`已切换到 ${getGuideFloorLabel(floorId)},单指平移、双指可缩放`, 3200)
console.log('楼层渲染完成:', floorId) console.log('楼层渲染完成:', floorId)
} }
@@ -1023,6 +1040,13 @@ const handleFloorSwitchFailed = ({ floorId, floorLabel }: { floorId: string; flo
loadingFloorId.value = '' loadingFloorId.value = ''
} }
failedFloorId.value = floorId failedFloorId.value = floorId
if (requestedFloorId.value === floorId) {
requestedFloorId.value = ''
requestedFloorLabel.value = ''
}
if (homeSearchMapState.value?.floorId === floorId) {
searchVisiblePoiIds.value = []
}
showIndoorHint(`${floorLabel || getGuideFloorLabel(floorId)} 加载失败,请稍后重试`, 3600) showIndoorHint(`${floorLabel || getGuideFloorLabel(floorId)} 加载失败,请稍后重试`, 3600)
console.warn('楼层渲染失败:', floorId) console.warn('楼层渲染失败:', floorId)
} }
@@ -1030,6 +1054,8 @@ const handleFloorSwitchFailed = ({ floorId, floorLabel }: { floorId: string; flo
const handleIndoorViewChange = (view: GuideIndoorView) => { const handleIndoorViewChange = (view: GuideIndoorView) => {
indoorView.value = view indoorView.value = view
if (view !== 'floor') { if (view !== 'floor') {
committedFloorId.value = ''
syncHomeSearchMarkersForCommittedFloor()
selectedGuidePoi.value = null selectedGuidePoi.value = null
isPoiCardCollapsed.value = false isPoiCardCollapsed.value = false
} }
@@ -1045,6 +1071,8 @@ const handleAutoSwitch = (event: { from: 'overview' | 'floor'; to: 'overview' |
console.log('自动切换视图:', event) console.log('自动切换视图:', event)
indoorView.value = event.to indoorView.value = event.to
if (event.to !== 'floor') { if (event.to !== 'floor') {
committedFloorId.value = ''
syncHomeSearchMarkersForCommittedFloor()
selectedGuidePoi.value = null selectedGuidePoi.value = null
isPoiCardCollapsed.value = false isPoiCardCollapsed.value = false
} }
@@ -1053,6 +1081,11 @@ const handleAutoSwitch = (event: { from: 'overview' | 'floor'; to: 'overview' |
const handleInitialModelReady = (event: { view: GuideIndoorView; floorId?: string; elapsedMs?: number }) => { const handleInitialModelReady = (event: { view: GuideIndoorView; floorId?: string; elapsedMs?: number }) => {
const readyFloorId = event.floorId || activeGuideFloor.value const readyFloorId = event.floorId || activeGuideFloor.value
indoorView.value = event.view
if (event.view !== 'floor') {
committedFloorId.value = ''
syncHomeSearchMarkersForCommittedFloor()
}
if (event.view === 'overview' && readyFloorId) { if (event.view === 'overview' && readyFloorId) {
activeGuideFloor.value = readyFloorId activeGuideFloor.value = readyFloorId
renderedFloorId.value = readyFloorId renderedFloorId.value = readyFloorId
@@ -1092,6 +1125,7 @@ const handleGuidePoiClick = (poi: GuideRenderPoi) => {
isSimulatingRoute.value = false isSimulatingRoute.value = false
activeGuideFloor.value = poi.floorId activeGuideFloor.value = poi.floorId
renderedFloorId.value = poi.floorId renderedFloorId.value = poi.floorId
committedFloorId.value = poi.floorId
showIndoorHint(`${poi.name} · ${getGuideFloorLabel(poi.floorId)}`, 3000) showIndoorHint(`${poi.name} · ${getGuideFloorLabel(poi.floorId)}`, 3000)
} }
@@ -1519,6 +1553,7 @@ const resetGuideModelToFloorBaseline = async ({
if (!guideModelState.isCurrentRequest(resetRequest.requestId)) return if (!guideModelState.isCurrentRequest(resetRequest.requestId)) return
if (resetResult === 'applied') { if (resetResult === 'applied') {
renderedFloorId.value = activeGuideFloor.value renderedFloorId.value = activeGuideFloor.value
committedFloorId.value = activeGuideFloor.value
guideModelState.completeViewBaselineReset(resetRequest) guideModelState.completeViewBaselineReset(resetRequest)
console.info('馆内三维模型已恢复楼层视觉基线:', { rendererReason }) console.info('馆内三维模型已恢复楼层视觉基线:', { rendererReason })
} else if (resetResult === 'not-ready' || resetResult === undefined) { } else if (resetResult === 'not-ready' || resetResult === undefined) {
@@ -1589,12 +1624,20 @@ const clearHomeSearchMapState = () => {
homeCategoryModeActive.value = false homeCategoryModeActive.value = false
searchVisiblePoiIds.value = null searchVisiblePoiIds.value = null
searchTargetFocusRequest.value = null searchTargetFocusRequest.value = null
homeSearchMapState.value = null
}
const invalidateHomeSearchFloorCommit = () => {
clearHomeSearchMapState()
committedFloorId.value = ''
indoorView.value = 'overview'
} }
const handleHomeCategoryModeChange = (active: boolean) => { const handleHomeCategoryModeChange = (active: boolean) => {
homeCategoryModeActive.value = active homeCategoryModeActive.value = active
if (!active) { if (!active) {
searchVisiblePoiIds.value = null searchVisiblePoiIds.value = null
homeSearchMapState.value = null
return return
} }
@@ -1604,19 +1647,72 @@ const handleHomeCategoryModeChange = (active: boolean) => {
selectedGuidePoi.value = null selectedGuidePoi.value = null
isPoiCardCollapsed.value = false isPoiCardCollapsed.value = false
is3DMode.value = true is3DMode.value = true
indoorView.value = 'floor' searchVisiblePoiIds.value = []
} }
const handleHomeSearchResultsChange = (state: PoiCategoryResultState) => { const isHomeSearchFloorCommitted = (floorId: string) => (
if (!state.active) { Boolean(floorId)
searchVisiblePoiIds.value = null && committedFloorId.value === floorId
&& indoorView.value === 'floor'
)
const requestHomeSearchFloor = (floorId: string, floorLabel = '') => {
if (!floorId || requestedFloorId.value === floorId || loadingFloorId.value === floorId) return
requestedFloorId.value = floorId
requestedFloorLabel.value = floorLabel || getGuideFloorLabel(floorId)
loadingFloorId.value = floorId
failedFloorId.value = ''
void Promise.resolve(guideMapShellRef.value?.switchFloor?.(floorId))
.catch((error) => {
console.error('快捷入口楼层切换失败:', error)
})
}
const syncHomeSearchMarkersForCommittedFloor = (floorId = committedFloorId.value) => {
const state = homeSearchMapState.value
if (
!state?.active
|| state.pending
|| state.floorId !== floorId
|| indoorView.value !== 'floor'
) {
searchVisiblePoiIds.value = state?.active ? [] : null
return return
} }
searchVisiblePoiIds.value = [...state.visiblePoiIds] searchVisiblePoiIds.value = [...state.visiblePoiIds]
if (state.floorId && !state.pending) { }
activeGuideFloor.value = state.floorId
const handleHomeSearchResultsChange = (state: PoiCategoryResultState) => {
if (
typeof state.requestId === 'number'
&& state.requestId < latestHomeSearchMapRequestId
) return
if (typeof state.requestId === 'number') {
latestHomeSearchMapRequestId = state.requestId
} }
if (!state.active) {
clearHomeSearchMapState()
return
}
homeSearchMapState.value = {
...state,
visiblePoiIds: [...state.visiblePoiIds]
}
if (state.pending) {
searchVisiblePoiIds.value = []
}
if (!isHomeSearchFloorCommitted(state.floorId)) {
searchVisiblePoiIds.value = []
requestHomeSearchFloor(state.floorId, state.floorLabel)
return
}
syncHomeSearchMarkersForCommittedFloor(state.floorId)
} }
const handleHomeSearchResultTap = async (poi: MuseumPoi, context: PoiSearchContext) => { const handleHomeSearchResultTap = async (poi: MuseumPoi, context: PoiSearchContext) => {
@@ -1672,6 +1768,7 @@ const handleExplainHallClick = async (hallId: string) => {
} }
const handleExplainBack = () => { const handleExplainBack = () => {
invalidateHomeSearchFloorCommit()
currentTab.value = 'guide' currentTab.value = 'guide'
syncTopTabToUrl('guide') syncTopTabToUrl('guide')
loadTopTabData('guide') loadTopTabData('guide')

View File

@@ -18,6 +18,7 @@ const testState = vi.hoisted(() => ({
searchRestoreCount: 0, searchRestoreCount: 0,
searchCollapseAfterDetailCount: 0, searchCollapseAfterDetailCount: 0,
searchResetCount: 0, searchResetCount: 0,
floorSwitchRequests: [] as string[],
onShowHandler: null as null | (() => Promise<void> | void), onShowHandler: null as null | (() => Promise<void> | void),
halls: [] as Array<{ id: string; poiId?: string; location?: { poiId?: string }; name: string }>, halls: [] as Array<{ id: string; poiId?: string; location?: { poiId?: string }; name: string }>,
hallResolutionPromises: [] as Array<Promise<Array<{ hallResolutionPromises: [] as Array<Promise<Array<{
@@ -102,7 +103,7 @@ const GuideMapShellStub = defineComponent({
visiblePoiIds: { type: Array, default: null }, visiblePoiIds: { type: Array, default: null },
targetFocusRequest: { type: Object, default: null } targetFocusRequest: { type: Object, default: null }
}, },
emits: ['floor-change', 'poi-click', 'selection-clear', 'indoor-view-change'], emits: ['floor-change', 'floor-request', 'floor-switch-failed', 'initial-model-ready', 'poi-click', 'selection-clear', 'indoor-view-change'],
setup() { setup() {
onMounted(() => { onMounted(() => {
testState.mapMountCount += 1 testState.mapMountCount += 1
@@ -113,6 +114,10 @@ const GuideMapShellStub = defineComponent({
return {} return {}
}, },
methods: { methods: {
switchFloor(floorId: string) {
testState.floorSwitchRequests.push(floorId)
return Promise.resolve()
},
resetToViewBaseline(options: { resetToViewBaseline(options: {
view: 'overview' | 'floor' view: 'overview' | 'floor'
floorId?: string floorId?: string
@@ -217,6 +222,7 @@ beforeEach(() => {
testState.searchRestoreCount = 0 testState.searchRestoreCount = 0
testState.searchCollapseAfterDetailCount = 0 testState.searchCollapseAfterDetailCount = 0
testState.searchResetCount = 0 testState.searchResetCount = 0
testState.floorSwitchRequests = []
testState.onShowHandler = null testState.onShowHandler = null
testState.halls = [] testState.halls = []
testState.hallResolutionPromises = [] testState.hallResolutionPromises = []
@@ -355,7 +361,7 @@ describe('首页搜索与地图闭环', () => {
expect(wrapper.findAll('.guide-quick-action')[0]?.text()).toBe('导览') expect(wrapper.findAll('.guide-quick-action')[0]?.text()).toBe('导览')
}) })
it('向搜索面板传入当前楼层,并将分类结果 ID 精确传给地图', async () => { it('等待渲染器提交目标楼层后,才将快捷分类结果 ID 传给地图', async () => {
const wrapper = await mountIndex() const wrapper = await mountIndex()
const search = wrapper.getComponent(PoiSearchPanelStub) const search = wrapper.getComponent(PoiSearchPanelStub)
const map = wrapper.getComponent(GuideMapShellStub) const map = wrapper.getComponent(GuideMapShellStub)
@@ -370,6 +376,13 @@ describe('首页搜索与地图闭环', () => {
}) })
await wrapper.vm.$nextTick() await wrapper.vm.$nextTick()
expect(testState.floorSwitchRequests).toEqual(['L2'])
expect(map.props('activeFloor')).toBe('L1')
expect(map.props('visiblePoiIds')).toEqual([])
map.vm.$emit('floor-change', 'L2')
await wrapper.vm.$nextTick()
expect(map.props('activeFloor')).toBe('L2') expect(map.props('activeFloor')).toBe('L2')
expect(map.props('indoorView')).toBe('floor') expect(map.props('indoorView')).toBe('floor')
expect(map.props('visiblePoiIds')).toEqual(categoryContext.visiblePoiIds) expect(map.props('visiblePoiIds')).toEqual(categoryContext.visiblePoiIds)
@@ -388,6 +401,78 @@ describe('首页搜索与地图闭环', () => {
expect(map.props('visiblePoiIds')).toEqual([]) expect(map.props('visiblePoiIds')).toEqual([])
}) })
it('三维模型重新以总览就绪后,不将旧单层状态误作楼层提交', async () => {
const wrapper = await mountIndex()
const search = wrapper.getComponent(PoiSearchPanelStub)
const map = wrapper.getComponent(GuideMapShellStub)
map.vm.$emit('floor-change', 'L1')
await wrapper.vm.$nextTick()
expect(map.props('indoorView')).toBe('floor')
map.vm.$emit('initial-model-ready', { view: 'overview', floorId: 'L1' })
await wrapper.vm.$nextTick()
expect(map.props('indoorView')).toBe('overview')
search.vm.$emit('category-mode-change', true)
search.vm.$emit('results-change', {
...categoryContext,
floorId: 'L1',
floorLabel: '1F',
active: true,
requestId: 20
})
await wrapper.vm.$nextTick()
expect(testState.floorSwitchRequests).toEqual(['L1'])
expect(map.props('visiblePoiIds')).toEqual([])
map.vm.$emit('floor-change', 'L1')
await wrapper.vm.$nextTick()
expect(map.props('visiblePoiIds')).toEqual(categoryContext.visiblePoiIds)
})
it('忽略过期搜索结果,不允许旧楼层覆盖当前待提交地图状态', async () => {
const wrapper = await mountIndex()
const search = wrapper.getComponent(PoiSearchPanelStub)
const map = wrapper.getComponent(GuideMapShellStub)
search.vm.$emit('category-mode-change', true)
search.vm.$emit('results-change', { ...categoryContext, active: true, requestId: 8 })
search.vm.$emit('results-change', {
...categoryContext,
floorId: 'L1',
floorLabel: '1F',
visiblePoiIds: ['stale-l1'],
active: true,
requestId: 7
})
await wrapper.vm.$nextTick()
expect(testState.floorSwitchRequests).toEqual(['L2'])
map.vm.$emit('floor-change', 'L2')
await wrapper.vm.$nextTick()
expect(map.props('visiblePoiIds')).toEqual(categoryContext.visiblePoiIds)
})
it('楼层切换失败时清空快捷分类标记,重试提交后恢复与列表一致的 ID', async () => {
const wrapper = await mountIndex()
const search = wrapper.getComponent(PoiSearchPanelStub)
const map = wrapper.getComponent(GuideMapShellStub)
search.vm.$emit('category-mode-change', true)
search.vm.$emit('results-change', { ...categoryContext, active: true, requestId: 12 })
await wrapper.vm.$nextTick()
map.vm.$emit('floor-switch-failed', { floorId: 'L2', floorLabel: '2F' })
await wrapper.vm.$nextTick()
expect(map.props('activeFloor')).toBe('L1')
expect(map.props('visiblePoiIds')).toEqual([])
map.vm.$emit('floor-change', 'L2')
await wrapper.vm.$nextTick()
expect(map.props('visiblePoiIds')).toEqual(categoryContext.visiblePoiIds)
})
it('楼层数据按高到低返回时,首次进入仍将 1F 作为自动缩放的默认目标', async () => { it('楼层数据按高到低返回时,首次进入仍将 1F 作为自动缩放的默认目标', async () => {
const originalFloors = [...floors] const originalFloors = [...floors]
floors.splice(0, floors.length, floors.splice(0, floors.length,
@@ -417,6 +502,8 @@ describe('首页搜索与地图闭环', () => {
...categoryContext, ...categoryContext,
active: true active: true
}) })
map.vm.$emit('floor-change', 'L2')
await wrapper.vm.$nextTick()
search.vm.$emit('result-tap', poi, categoryContext) search.vm.$emit('result-tap', poi, categoryContext)
await flushPromises() await flushPromises()
@@ -486,6 +573,8 @@ describe('首页搜索与地图闭环', () => {
search.vm.$emit('category-mode-change', true) search.vm.$emit('category-mode-change', true)
search.vm.$emit('results-change', { ...categoryContext, active: true }) search.vm.$emit('results-change', { ...categoryContext, active: true })
map.vm.$emit('floor-change', 'L2')
await wrapper.vm.$nextTick()
search.vm.$emit('result-tap', poi, categoryContext) search.vm.$emit('result-tap', poi, categoryContext)
await flushPromises() await flushPromises()

View File

@@ -149,6 +149,7 @@ const lastResultState = (wrapper: ReturnType<typeof mount>) => (
categoryId: string categoryId: string
keyword: string keyword: string
pending?: boolean pending?: boolean
requestId?: number
} }
) )
@@ -323,6 +324,36 @@ describe('PoiSearchPanel 搜索状态契约', () => {
wrapper.unmount() wrapper.unmount()
}) })
it('首页分类结果在组件重挂载后仍发送递增的跨组件版本号', async () => {
const firstWrapper = mount(PoiSearchPanel, {
props: {
variant: 'home',
currentFloorId: 'floor-1',
currentFloorLabel: '1F'
}
})
await flushMountedSearch()
await firstWrapper.get('[data-testid="poi-category-restroom"]').trigger('tap')
await flushMountedSearch()
const firstRequestId = lastResultState(firstWrapper).requestId
firstWrapper.unmount()
const secondWrapper = mount(PoiSearchPanel, {
props: {
variant: 'home',
currentFloorId: 'floor-1',
currentFloorLabel: '1F'
}
})
await flushMountedSearch()
await secondWrapper.get('[data-testid="poi-category-restroom"]').trigger('tap')
await flushMountedSearch()
expect(firstRequestId).toEqual(expect.any(Number))
expect(lastResultState(secondWrapper).requestId).toBeGreaterThan(firstRequestId as number)
secondWrapper.unmount()
})
it('首页快捷分类请求期间清空列表并下发 pending 空地图 ID', async () => { it('首页快捷分类请求期间清空列表并下发 pending 空地图 ID', async () => {
const initialState = createSearchState() const initialState = createSearchState()
const deferredCategory = createDeferred<GuidePoiSearchViewState>() const deferredCategory = createDeferred<GuidePoiSearchViewState>()