fix: 统一馆内三维模型返回初始状态

This commit is contained in:
lyf
2026-07-14 21:57:31 +08:00
parent d813f0b375
commit 84fbbc0694
8 changed files with 319 additions and 168 deletions

View File

@@ -29,6 +29,8 @@ interface VisualReport {
anchors: AnchorReport[]
activeView: string
floorId: string
activeFocusPoiId: string
hasPendingTargetFocus: boolean
isCameraTweening: boolean
poiFocusCameraAnimationCount: number
cameraSnapshotRestoreCount: number
@@ -58,6 +60,7 @@ interface VisualStabilityApi {
getFloors: () => FloorReport[]
switchFloor: (floorId: string) => Promise<void>
showOverview: () => Promise<void>
resetToInitialState: () => Promise<boolean>
getFloorPois: (floorId: string) => Promise<PoiFocusCandidate[]>
focusTargetPoi: (request: {
requestId: number | string
@@ -399,3 +402,33 @@ test('queued cross-floor search POI focus keeps raw GLB_METER coordinates and do
await switchFloor(page, testInfo, l1, 'search-focus-clear-lminus1-l1')
await showOverview(page, testInfo, 'search-focus-clear-l1-exterior')
})
test('reset invalidates an in-flight cross-floor focus before it can overwrite the initial state', async ({ page }) => {
await page.goto('/')
await Promise.all([
page.waitForURL(/tab=guide/, { timeout: 30_000 }),
page.getByText('馆内', { exact: true }).click()
])
await expect.poll(async () => (await getApiState(page)) !== null, { timeout: 180_000 }).toBe(true)
await expect.poll(async () => (await getApiState(page))?.floors.length ?? 0, { timeout: 180_000 }).toBeGreaterThan(0)
const state = await getApiState(page)
expect(state).not.toBeNull()
const targetFloor = state!.floors.find((floor) => floor.floorId !== state!.report.floorId)?.floorId
|| state!.floors[0]!.floorId
const targetPoi = (await getFloorPois(page, targetFloor))[0]!
await page.evaluate(async (request) => {
const api = (window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: VisualStabilityApi })
.__GUIDE_3D_VISUAL_STABILITY__
if (!api) throw new Error('ThreeMap visual stability diagnostics unavailable')
const focus = api.focusTargetPoi({ requestId: 'reset-race', ...request })
await api.resetToInitialState()
await focus
}, targetPoi)
const report = await getReport(page)
expect(report.activeView).toBe('overview')
expect(report.activeFocusPoiId).toBe('')
expect(report.hasPendingTargetFocus).toBe(false)
expect(report.focus).toBeNull()
})

View File

@@ -143,10 +143,9 @@ afterEach(() => {
})
describe('点位详情实际渲染与返回闭环', () => {
it('解析目标楼层点位后才挂载地图,避免默认楼层中间帧', async () => {
it('目标楼层点位就绪后挂载位置预览,避免默认楼层中间帧', async () => {
const wrapper = mountDetail()
expect(wrapper.find('[data-testid="guide-map-shell"]').exists()).toBe(false)
expect(wrapper.get('[data-testid="facility-map-loading"]').text()).toContain('正在准备位置预览')
await loadDetail({
@@ -163,7 +162,7 @@ describe('点位详情实际渲染与返回闭环', () => {
})
})
it('只展示真实详情字段,并向地图发送正确楼层与点位定位请求', async () => {
it('只展示真实详情字段,并发送正确的位置预览请求', async () => {
const wrapper = mountDetail()
await loadDetail({
@@ -174,7 +173,6 @@ describe('点位详情实际渲染与返回闭环', () => {
})
const sheet = wrapper.get('[data-testid="facility-detail-sheet"]')
const mapShell = wrapper.getComponent(GuideMapShellStub)
expect(sheet.text()).toContain('二层卫生间')
expect(sheet.text()).toContain('类别洗手间')
expect(sheet.text()).toContain('楼层2F')
@@ -182,10 +180,11 @@ describe('点位详情实际渲染与返回闭环', () => {
expect(sheet.text()).not.toContain('展示点位')
expect(sheet.text()).not.toContain('已在对应楼层模型中标出点位')
expect(wrapper.find('.detail-restore').exists()).toBe(false)
const mapShell = wrapper.getComponent(GuideMapShellStub)
expect(mapShell.props('activeFloor')).toBe('floor-l2')
expect(mapShell.props('targetFocusRequest')).toMatchObject({
poiId: 'poi-restroom-l2',
floorId: 'floor-l2',
poiId: facilityPoi.id,
floorId: facilityPoi.floorId,
positionGltf: [12, 3, -8]
})
})
@@ -223,7 +222,7 @@ describe('点位详情实际渲染与返回闭环', () => {
expect(uni.reLaunch).not.toHaveBeenCalled()
})
it('搜索单结果关闭详情后回到首页', async () => {
it('搜索单结果关闭详情同样仅使用页面栈返回', async () => {
const wrapper = mountDetail()
await loadDetail({
source: 'search',
@@ -234,8 +233,8 @@ describe('点位详情实际渲染与返回闭环', () => {
await wrapper.get('[data-testid="facility-detail-close"]').trigger('tap')
expect(uni.reLaunch).toHaveBeenCalledWith({ url: '/pages/index/index' })
expect(uni.navigateBack).not.toHaveBeenCalled()
expect(uni.navigateBack).toHaveBeenCalledWith(expect.objectContaining({ delta: 1 }))
expect(uni.reLaunch).not.toHaveBeenCalled()
})
it('直接进入详情时使用常规返回', async () => {
@@ -248,11 +247,10 @@ describe('点位详情实际渲染与返回闭环', () => {
expect(uni.reLaunch).not.toHaveBeenCalled()
})
it('地图报告点位缺失或定位失败时显示面向用户的错误反馈', async () => {
it('位置预览失败时显示面向用户的错误反馈', async () => {
const wrapper = mountDetail()
await loadDetail({ id: facilityPoi.id, target: facilityPoi.name })
const mapShell = wrapper.getComponent(GuideMapShellStub)
mapShell.vm.$emit('target-focus', {
requestId: 1,
poiId: facilityPoi.id,
@@ -260,18 +258,7 @@ describe('点位详情实际渲染与返回闭环', () => {
status: 'missing'
})
await flushPromises()
expect(wrapper.get('[data-testid="facility-location-error"]').text())
.toContain('缺少可用的位置数据')
mapShell.vm.$emit('target-focus', {
requestId: 1,
poiId: facilityPoi.id,
floorId: facilityPoi.floorId,
status: 'error'
})
await flushPromises()
expect(wrapper.get('[data-testid="facility-location-error"]').text())
.toContain('地图定位暂不可用')
expect(wrapper.get('[data-testid="facility-location-error"]').text()).toContain('缺少可用的位置数据')
})
it('位置数据源加载失败时提供明确重试反馈', async () => {

View File

@@ -321,7 +321,7 @@ describe('首页搜索与地图闭环', () => {
expect(map.props('visiblePoiIds')).toEqual(categoryContext.visiblePoiIds)
})
it('分类结果进入详情时不触发首页地图,返回后才恢复目标焦点与楼层', async () => {
it('分类结果进入详情后统一恢复首页模型初始状态', async () => {
const wrapper = await mountIndex()
const search = wrapper.getComponent(PoiSearchPanelStub)
const map = wrapper.getComponent(GuideMapShellStub)
@@ -354,17 +354,14 @@ describe('首页搜索与地图闭环', () => {
await testState.onShowHandler?.()
await flushPromises()
expect(testState.searchCollapseAfterDetailCount).toBe(1)
expect(testState.searchResetCount).toBeGreaterThan(0)
expect(testState.searchRestoreCount).toBe(0)
expect(map.props('activeFloor')).toBe('L2')
expect(map.props('activeFloor')).toBe('L1')
expect(map.props('visiblePoiIds')).toBeNull()
expect(map.props('targetFocusRequest')).toMatchObject({
poiId: poi.id,
floorId: 'L2'
})
expect(map.props('targetFocusRequest')).toBeNull()
})
it('全屏搜索结果进入详情并返回后同样恢复为收缩态', async () => {
it('全屏搜索结果进入详情并返回后同样恢复为默认收缩态', async () => {
const wrapper = await mountIndex()
const search = wrapper.getComponent(PoiSearchPanelStub)
const map = wrapper.getComponent(GuideMapShellStub)
@@ -379,10 +376,10 @@ describe('首页搜索与地图闭环', () => {
await testState.onShowHandler?.()
await flushPromises()
expect(testState.searchCollapseAfterDetailCount).toBe(1)
expect(testState.searchResetCount).toBeGreaterThan(0)
expect(testState.searchRestoreCount).toBe(0)
expect(map.props('visiblePoiIds')).toBeNull()
expect(map.props('targetFocusRequest')).toMatchObject({ poiId: poi.id })
expect(map.props('targetFocusRequest')).toBeNull()
})
it('页面重新显示时要求搜索面板恢复原列表位置', async () => {