修复:首页点位讲解入口展示逻辑
This commit is contained in:
@@ -99,15 +99,12 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view
|
<view
|
||||||
v-if="selectedGuidePoiIsHall"
|
v-if="selectedGuidePoiExplainHall"
|
||||||
class="poi-card-actions"
|
class="poi-card-actions"
|
||||||
>
|
>
|
||||||
<view class="poi-action primary" @tap="handleViewSelectedHall">
|
<view class="poi-action primary" @tap="handleViewSelectedHall">
|
||||||
<text class="poi-action-text">查看展厅</text>
|
<text class="poi-action-text">查看展厅</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="poi-action" @tap="handleSelectedPoiRelated">
|
|
||||||
<text class="poi-action-text">相关讲解</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
</view>
|
</view>
|
||||||
@@ -290,7 +287,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue'
|
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||||
import { onLoad, onShow } from '@dcloudio/uni-app'
|
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||||
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
||||||
import GuideMapShell from '@/components/navigation/GuideMapShell.vue'
|
import GuideMapShell from '@/components/navigation/GuideMapShell.vue'
|
||||||
@@ -400,6 +397,8 @@ const loadingFloorId = ref('')
|
|||||||
const renderedFloorId = ref('')
|
const renderedFloorId = ref('')
|
||||||
const failedFloorId = ref('')
|
const failedFloorId = ref('')
|
||||||
const selectedGuidePoi = ref<GuideRenderPoi | null>(null)
|
const selectedGuidePoi = ref<GuideRenderPoi | null>(null)
|
||||||
|
const selectedGuidePoiExplainHall = ref<MuseumHall | null>(null)
|
||||||
|
let selectedGuidePoiExplainHallRequestId = 0
|
||||||
|
|
||||||
// 状态
|
// 状态
|
||||||
const currentTab = ref<GuideTopTab>(initialTopTab())
|
const currentTab = ref<GuideTopTab>(initialTopTab())
|
||||||
@@ -714,21 +713,20 @@ const selectedGuidePoiSubtitle = computed(() => {
|
|||||||
return `${floorLabel} · ${poi.primaryCategoryZh || '位置预览'}`
|
return `${floorLabel} · ${poi.primaryCategoryZh || '位置预览'}`
|
||||||
})
|
})
|
||||||
|
|
||||||
const selectedGuidePoiIsHall = computed(() => {
|
const isGuidePoiHallLike = (poi: GuideRenderPoi | null) => Boolean(
|
||||||
const poi = selectedGuidePoi.value
|
poi
|
||||||
return Boolean(
|
&& (
|
||||||
poi
|
poi.kind === 'hall'
|
||||||
&& (
|
|| poi.primaryCategory === 'touring_poi'
|
||||||
poi.kind === 'hall'
|
|| poi.primaryCategory === 'exhibition_hall'
|
||||||
|| poi.primaryCategory === 'touring_poi'
|
|| poi.primaryCategory === 'exhibition_hall_entrance'
|
||||||
|| poi.primaryCategory === 'exhibition_hall'
|
|
||||||
|| poi.primaryCategory === 'exhibition_hall_entrance'
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
})
|
)
|
||||||
|
|
||||||
|
const selectedGuidePoiIsHall = computed(() => isGuidePoiHallLike(selectedGuidePoi.value))
|
||||||
|
|
||||||
const selectedGuidePoiCollapsedSubtitle = computed(() => (
|
const selectedGuidePoiCollapsedSubtitle = computed(() => (
|
||||||
selectedGuidePoiIsHall.value ? '点按展开查看展厅' : selectedGuidePoiSubtitle.value
|
selectedGuidePoiExplainHall.value ? '点按展开查看展厅' : selectedGuidePoiSubtitle.value
|
||||||
))
|
))
|
||||||
|
|
||||||
const routeSimulationStepText = computed(() => {
|
const routeSimulationStepText = computed(() => {
|
||||||
@@ -1132,12 +1130,10 @@ const togglePoiCardCollapsed = () => {
|
|||||||
const normalizeHallMatchText = (value?: string) => (value || '')
|
const normalizeHallMatchText = (value?: string) => (value || '')
|
||||||
.trim()
|
.trim()
|
||||||
.replace(/[\s()()【】[\]_-]/g, '')
|
.replace(/[\s()()【】[\]_-]/g, '')
|
||||||
|
.replace(/^展厅\d+/, '')
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
|
|
||||||
const resolveSelectedPoiHall = async (): Promise<MuseumHall | null> => {
|
const resolvePoiExplainHall = async (poi: GuideRenderPoi): Promise<MuseumHall | null> => {
|
||||||
const poi = selectedGuidePoi.value
|
|
||||||
if (!poi) return null
|
|
||||||
|
|
||||||
const halls = await explainUseCase.listHalls()
|
const halls = await explainUseCase.listHalls()
|
||||||
const matchedByPoiId = halls.find((hall) => (
|
const matchedByPoiId = halls.find((hall) => (
|
||||||
hall.poiId === poi.id
|
hall.poiId === poi.id
|
||||||
@@ -1162,10 +1158,30 @@ const resolveSelectedPoiHall = async (): Promise<MuseumHall | null> => {
|
|||||||
const poiHallName = normalizeHallMatchText(poi.hallName || poi.name)
|
const poiHallName = normalizeHallMatchText(poi.hallName || poi.name)
|
||||||
return halls.find((hall) => {
|
return halls.find((hall) => {
|
||||||
const hallName = normalizeHallMatchText(hall.name)
|
const hallName = normalizeHallMatchText(hall.name)
|
||||||
return hallName && poiHallName && (hallName.includes(poiHallName) || poiHallName.includes(hallName))
|
return hallName && poiHallName && hallName === poiHallName
|
||||||
}) || null
|
}) || null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(selectedGuidePoi, (poi) => {
|
||||||
|
const requestId = ++selectedGuidePoiExplainHallRequestId
|
||||||
|
selectedGuidePoiExplainHall.value = null
|
||||||
|
if (!poi || !isGuidePoiHallLike(poi)) return
|
||||||
|
|
||||||
|
void resolvePoiExplainHall(poi)
|
||||||
|
.then((hall) => {
|
||||||
|
if (
|
||||||
|
requestId !== selectedGuidePoiExplainHallRequestId
|
||||||
|
|| selectedGuidePoi.value?.id !== poi.id
|
||||||
|
) return
|
||||||
|
|
||||||
|
selectedGuidePoiExplainHall.value = hall
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
if (requestId !== selectedGuidePoiExplainHallRequestId) return
|
||||||
|
console.warn('点位讲解展厅关联解析失败:', error)
|
||||||
|
})
|
||||||
|
}, { flush: 'sync' })
|
||||||
|
|
||||||
const navigateToPoiDetail = ({
|
const navigateToPoiDetail = ({
|
||||||
url,
|
url,
|
||||||
floorId,
|
floorId,
|
||||||
@@ -1209,43 +1225,12 @@ const navigateToHallExplainObjects = (hallId: string, hallName = '讲解') => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleViewSelectedHall = async () => {
|
const handleViewSelectedHall = () => {
|
||||||
const hall = await resolveSelectedPoiHall()
|
const hall = selectedGuidePoiExplainHall.value
|
||||||
if (!hall) {
|
if (!hall) return
|
||||||
uni.showToast({
|
|
||||||
title: '该展厅详情暂未接入',
|
|
||||||
icon: 'none'
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
navigateToHallExplainObjects(hall.id, hall.name)
|
navigateToHallExplainObjects(hall.id, hall.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSelectedPoiRelated = async () => {
|
|
||||||
if (!selectedGuidePoi.value) return
|
|
||||||
|
|
||||||
const hall = await resolveSelectedPoiHall()
|
|
||||||
if (hall) {
|
|
||||||
navigateToHallExplainObjects(hall.id, hall.name)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const keyword = selectedGuidePoi.value.hallName || selectedGuidePoi.value.name
|
|
||||||
const results = await explainUseCase.searchExplain(keyword)
|
|
||||||
const firstHall = results.find((item) => item.type === 'hall' && item.hallId)
|
|
||||||
|
|
||||||
if (firstHall?.hallId) {
|
|
||||||
navigateToHallExplainObjects(firstHall.hallId, selectedGuidePoi.value.hallName || '讲解')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
uni.showToast({
|
|
||||||
title: '该展厅暂无对应讲解',
|
|
||||||
icon: 'none'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadRouteTargets = async (keyword = '') => {
|
const loadRouteTargets = async (keyword = '') => {
|
||||||
routeTargetsLoading.value = true
|
routeTargetsLoading.value = true
|
||||||
routeTargetsError.value = ''
|
routeTargetsError.value = ''
|
||||||
|
|||||||
@@ -528,7 +528,7 @@ describe('首页搜索与地图闭环', () => {
|
|||||||
expect(testState.resetToViewBaselineCalls).toHaveLength(1)
|
expect(testState.resetToViewBaselineCalls).toHaveLength(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('模型点选展厅的相关讲解进入该展厅的讲解对象列表,并在返回时复位原楼层', async () => {
|
it('模型点选展厅只显示一个讲解入口,并在返回时复位原楼层', async () => {
|
||||||
testState.halls = [{ id: 'hall-l2', poiId: poi.id, name: '二层展厅' }]
|
testState.halls = [{ id: 'hall-l2', poiId: poi.id, name: '二层展厅' }]
|
||||||
const wrapper = await mountIndex()
|
const wrapper = await mountIndex()
|
||||||
const map = wrapper.getComponent(GuideMapShellStub)
|
const map = wrapper.getComponent(GuideMapShellStub)
|
||||||
@@ -541,9 +541,10 @@ describe('首页搜索与地图闭环', () => {
|
|||||||
})
|
})
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
|
|
||||||
const relatedActions = wrapper.findAll('.poi-action')
|
const actions = wrapper.findAll('.poi-action')
|
||||||
expect(relatedActions).toHaveLength(2)
|
expect(actions).toHaveLength(1)
|
||||||
await relatedActions[1]!.trigger('tap')
|
expect(wrapper.text()).not.toContain('相关讲解')
|
||||||
|
await wrapper.find('.poi-action.primary').trigger('tap')
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
|
|
||||||
expect(vi.mocked(uni.navigateTo).mock.calls[0]?.[0]?.url).toContain('/pages/explain/guide-stop-list?hallId=hall-l2')
|
expect(vi.mocked(uni.navigateTo).mock.calls[0]?.[0]?.url).toContain('/pages/explain/guide-stop-list?hallId=hall-l2')
|
||||||
@@ -559,7 +560,54 @@ describe('首页搜索与地图闭环', () => {
|
|||||||
expect(wrapper.find('.guide-poi-card').exists()).toBe(false)
|
expect(wrapper.find('.guide-poi-card').exists()).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('模型点选展厅的相关讲解优先进入讲解对象列表并保留一次性返回上下文', async () => {
|
it('展厅类型空间没有讲解展厅关联时不显示展厅与讲解按钮', async () => {
|
||||||
|
testState.halls = [{ id: 'hall-biology', poiId: 'another-poi', name: '生物厅' }]
|
||||||
|
const wrapper = await mountIndex()
|
||||||
|
const map = wrapper.getComponent(GuideMapShellStub)
|
||||||
|
|
||||||
|
map.vm.$emit('poi-click', {
|
||||||
|
...poi,
|
||||||
|
id: 'space-l2-temporary',
|
||||||
|
name: '生物厅临展空间',
|
||||||
|
kind: 'space',
|
||||||
|
hallId: 'space-l2-temporary',
|
||||||
|
primaryCategory: 'exhibition_hall',
|
||||||
|
primaryCategoryZh: '展厅'
|
||||||
|
})
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(wrapper.find('.guide-poi-card').exists()).toBe(true)
|
||||||
|
expect(wrapper.find('.poi-card-actions').exists()).toBe(false)
|
||||||
|
expect(wrapper.text()).not.toContain('查看展厅')
|
||||||
|
expect(wrapper.text()).not.toContain('相关讲解')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('展厅编号名称规范化后仍能关联真实讲解展厅', async () => {
|
||||||
|
testState.halls = [{ id: 'hall-biology', name: '生物厅' }]
|
||||||
|
const wrapper = await mountIndex()
|
||||||
|
const map = wrapper.getComponent(GuideMapShellStub)
|
||||||
|
|
||||||
|
map.vm.$emit('poi-click', {
|
||||||
|
...poi,
|
||||||
|
id: 'hall-space-biology',
|
||||||
|
name: '展厅_6生物厅',
|
||||||
|
kind: 'space',
|
||||||
|
hallId: 'space-biology',
|
||||||
|
primaryCategory: 'exhibition_hall',
|
||||||
|
primaryCategoryZh: '展厅'
|
||||||
|
})
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
const actions = wrapper.findAll('.poi-action')
|
||||||
|
expect(actions).toHaveLength(1)
|
||||||
|
await actions[0]!.trigger('tap')
|
||||||
|
await flushPromises()
|
||||||
|
expect(vi.mocked(uni.navigateTo).mock.calls[0]?.[0]?.url).toContain(
|
||||||
|
'/pages/explain/guide-stop-list?hallId=hall-biology'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('模型点选展厅的唯一讲解入口进入讲解对象列表并保留一次性返回上下文', async () => {
|
||||||
testState.halls = [{ id: 'hall-l2', poiId: poi.id, name: '二层展厅' }]
|
testState.halls = [{ id: 'hall-l2', poiId: poi.id, name: '二层展厅' }]
|
||||||
const wrapper = await mountIndex()
|
const wrapper = await mountIndex()
|
||||||
const map = wrapper.getComponent(GuideMapShellStub)
|
const map = wrapper.getComponent(GuideMapShellStub)
|
||||||
@@ -571,7 +619,7 @@ describe('首页搜索与地图闭环', () => {
|
|||||||
primaryCategoryZh: '展厅'
|
primaryCategoryZh: '展厅'
|
||||||
})
|
})
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
await wrapper.findAll('.poi-action')[1]!.trigger('tap')
|
await wrapper.find('.poi-action.primary').trigger('tap')
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
|
|
||||||
expect(vi.mocked(uni.navigateTo).mock.calls[0]?.[0]?.url).toContain('/pages/explain/guide-stop-list?hallId=hall-l2')
|
expect(vi.mocked(uni.navigateTo).mock.calls[0]?.[0]?.url).toContain('/pages/explain/guide-stop-list?hallId=hall-l2')
|
||||||
|
|||||||
Reference in New Issue
Block a user