@@ -296,7 +296,7 @@ const floorLoadError = ref('')
|
||||
const excludedPoiCount = ref(0)
|
||||
const duplicatePoiCount = ref(0)
|
||||
const resultListScrollTop = ref(0)
|
||||
const resultListRef = ref<HTMLElement | null>(null)
|
||||
const resultListRef = ref<HTMLElement | { $el?: HTMLElement } | null>(null)
|
||||
let cachedPoiPool: PreparedPoiResults | null = null
|
||||
let searchRequestSeq = 0
|
||||
|
||||
@@ -807,8 +807,15 @@ const handleResultListScroll = (event: any) => {
|
||||
resultListScrollTop.value = Number.isFinite(scrollTop) ? Math.max(0, scrollTop) : 0
|
||||
}
|
||||
|
||||
const getResultListElement = (): HTMLElement | null => {
|
||||
const target = resultListRef.value
|
||||
if (!target) return null
|
||||
if ('$el' in target) return target.$el || null
|
||||
return target as HTMLElement
|
||||
}
|
||||
|
||||
const captureResultListScroll = () => {
|
||||
const scrollTop = Number(resultListRef.value?.scrollTop)
|
||||
const scrollTop = Number(getResultListElement()?.scrollTop)
|
||||
if (Number.isFinite(scrollTop)) resultListScrollTop.value = Math.max(0, scrollTop)
|
||||
}
|
||||
|
||||
@@ -817,7 +824,8 @@ const restoreResultListScroll = async () => {
|
||||
if (scrollTop <= 0) return
|
||||
|
||||
await nextTick()
|
||||
if (resultListRef.value) resultListRef.value.scrollTop = scrollTop
|
||||
const resultListElement = getResultListElement()
|
||||
if (resultListElement) resultListElement.scrollTop = scrollTop
|
||||
}
|
||||
|
||||
const poiDisplayName = (poi: MuseumPoi) => poi.name?.trim() || '未命名点位'
|
||||
|
||||
@@ -672,6 +672,13 @@ const categoryForSgsSpace = (space: SgsSpacePayload): MuseumCategory => {
|
||||
return spaceCategory
|
||||
}
|
||||
|
||||
const createCanonicalSpacePoiId = (space: SgsSpacePayload) => {
|
||||
const spaceId = stringifyId(space.id)
|
||||
if (!spaceId) return ''
|
||||
|
||||
return `${isExhibitionHallSpace(space) ? 'hall' : 'space'}-${spaceId}`
|
||||
}
|
||||
|
||||
export const toMuseumSpacePointFromSgs = (
|
||||
space: SgsSpacePayload,
|
||||
floors: SgsSdkFloorSummaryPayload[],
|
||||
@@ -686,9 +693,11 @@ export const toMuseumSpacePointFromSgs = (
|
||||
if (!spaceId || !name || !spacePosition) return null
|
||||
|
||||
const category = categoryForSgsSpace(space)
|
||||
const poiId = createCanonicalSpacePoiId(space)
|
||||
|
||||
return {
|
||||
id: `space-${spaceId}`,
|
||||
// 可生成展厅类 marker 的空间统一使用 hall ID,确保弱网回退结果仍可直接定位。
|
||||
id: poiId,
|
||||
name,
|
||||
floorId,
|
||||
floorLabel: floorLabelForSgs(floorId, floors),
|
||||
@@ -734,8 +743,8 @@ const createHallEntrance = (
|
||||
const createHallPoiId = (
|
||||
space: SgsSpacePayload
|
||||
) => {
|
||||
const spaceId = stringifyId(space.id)
|
||||
if (spaceId) return `hall-${spaceId}`
|
||||
const canonicalId = createCanonicalSpacePoiId(space)
|
||||
if (canonicalId) return canonicalId
|
||||
|
||||
const ownerName = normalizedHallName(space.name)
|
||||
const floorIdentity = normalizedHallName(stringifyId(space.floorId) || 'unknown-floor')
|
||||
@@ -755,16 +764,17 @@ const createSpaceFallbackHallPoi = (
|
||||
const position = spacePosition?.position
|
||||
const hallId = stringifyId(space.id)
|
||||
|
||||
if (!hallId || !normalizedText(space.name) || !position) return null
|
||||
|
||||
return {
|
||||
if (!hallId || !normalizedText(space.name) || !position) return null
|
||||
const category = categoryForSgsSpace(space)
|
||||
|
||||
return {
|
||||
id: `hall-${hallId}`,
|
||||
name: normalizedText(space.name),
|
||||
floorId,
|
||||
floorLabel: floorLabelForSgs(floorId, floors),
|
||||
primaryCategory: hallCategory,
|
||||
categories: [
|
||||
hallCategory
|
||||
primaryCategory: category,
|
||||
categories: [
|
||||
category
|
||||
],
|
||||
positionGltf: position,
|
||||
sourceObjectName: space.sourceNodeName || undefined,
|
||||
@@ -807,7 +817,8 @@ export const toMuseumHallPoisFromSgs = (
|
||||
const hallId = stringifyId(matchedSpace?.id) || undefined
|
||||
const poiId = createHallPoiId(matchedSpace)
|
||||
const hallName = normalizedText(matchedSpace?.name) || placeHallName(place) || entrance.name
|
||||
const existing = halls.get(poiId)
|
||||
const category = categoryForSgsSpace(matchedSpace)
|
||||
const existing = halls.get(poiId)
|
||||
|
||||
if (existing) {
|
||||
existing.entrances = [
|
||||
@@ -843,10 +854,10 @@ export const toMuseumHallPoisFromSgs = (
|
||||
name: hallName,
|
||||
floorId: usesSpaceCenter ? fallbackFloorIdForPoi : entrance.floorId,
|
||||
floorLabel: usesSpaceCenter ? fallbackFloorLabel : entrance.floorLabel,
|
||||
primaryCategory: hallCategory,
|
||||
primaryCategory: category,
|
||||
categories: [
|
||||
hallCategory,
|
||||
hallEntranceCategory
|
||||
category,
|
||||
...(category.id === hallCategory.id ? [hallEntranceCategory] : [])
|
||||
],
|
||||
positionGltf,
|
||||
sourceObjectName: usesSpaceCenter
|
||||
|
||||
@@ -86,13 +86,17 @@ const toGuideRenderPoi = (poi: MuseumPoi): GuideRenderPoi => ({
|
||||
|
||||
const toSgsRenderPoi = (poi: ReturnType<typeof toMuseumPoiFromSgs>) => toGuideRenderPoi(poi)
|
||||
|
||||
const getRenderPoiDedupeKeys = (poi: GuideRenderPoi) => [
|
||||
poi.id ? `id:${poi.id}` : '',
|
||||
poi.sourceSpaceId ? `space:${poi.floorId}:${poi.sourceSpaceId}` : '',
|
||||
poi.spaceId ? `space:${poi.floorId}:${poi.spaceId}` : '',
|
||||
poi.sourcePlaceId ? `place:${poi.floorId}:${poi.sourcePlaceId}` : '',
|
||||
poi.sourceObjectName ? `object:${poi.floorId}:${poi.sourceObjectName}` : ''
|
||||
].filter(Boolean)
|
||||
const getRenderPoiDedupeKeys = (poi: GuideRenderPoi) => {
|
||||
const stableKeys = [
|
||||
poi.id ? `id:${poi.id}` : '',
|
||||
poi.sourceSpaceId ? `space:${poi.floorId}:${poi.sourceSpaceId}` : '',
|
||||
poi.spaceId ? `space:${poi.floorId}:${poi.spaceId}` : '',
|
||||
poi.sourcePlaceId ? `place:${poi.floorId}:${poi.sourcePlaceId}` : ''
|
||||
].filter(Boolean)
|
||||
|
||||
if (stableKeys.length) return stableKeys
|
||||
return poi.sourceObjectName ? [`object:${poi.floorId}:${poi.sourceObjectName}`] : []
|
||||
}
|
||||
|
||||
const dedupeRenderPoisById = (pois: GuideRenderPoi[]) => {
|
||||
const seen = new Set<string>()
|
||||
@@ -523,7 +527,7 @@ export class SgsSdkGuideModelRepository implements GuideModelRepository {
|
||||
fallbackHallY: floorPoiMedianY ?? null
|
||||
})
|
||||
|
||||
if (hallDiagnostics.hallPoiWithPositionCount > 0 && !renderPois.some((poi) => poi.primaryCategory === 'exhibition_hall')) {
|
||||
if (hallDiagnostics.hallPoiWithPositionCount > 0 && !renderHallPois.length) {
|
||||
warnSgsGuideModelDiagnostics('render POI filter removed all hall POIs', {
|
||||
floorId: resolvedFloorId,
|
||||
...hallDiagnostics,
|
||||
|
||||
@@ -4,6 +4,7 @@ import type {
|
||||
GuideLocationPreview,
|
||||
GuideMapDiagnostics,
|
||||
GuideRouteReadiness,
|
||||
MuseumCategory,
|
||||
MuseumFloor,
|
||||
MuseumPoi
|
||||
} from '@/domain/museum'
|
||||
@@ -132,9 +133,28 @@ const dedupePoisById = (pois: MuseumPoi[]) => {
|
||||
|
||||
const getPoiSpaceIdentity = (poi: MuseumPoi) => poi.sourceSpaceId || poi.spaceId || ''
|
||||
|
||||
const getPoiLookupIdentities = (id: string) => {
|
||||
const normalizedId = id.trim()
|
||||
return new Set([
|
||||
normalizedId,
|
||||
normalizedId.replace(/^(?:hall|space)-/, '')
|
||||
].filter(Boolean))
|
||||
}
|
||||
|
||||
const mergePoiCategories = (...categoryGroups: MuseumCategory[][]) => {
|
||||
const seenIds = new Set<string>()
|
||||
return categoryGroups.flat().filter((category) => {
|
||||
const categoryId = category.id.trim()
|
||||
if (!categoryId || seenIds.has(categoryId)) return false
|
||||
seenIds.add(categoryId)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
const mergeSearchablePois = (pois: MuseumPoi[], spacePoints: MuseumPoi[]) => {
|
||||
const hallPoisBySpaceId = new Map(
|
||||
pois
|
||||
.filter((poi) => poi.kind === 'hall' || Boolean(poi.hallId))
|
||||
.map((poi) => [getPoiSpaceIdentity(poi), poi] as const)
|
||||
.filter(([spaceId]) => Boolean(spaceId))
|
||||
)
|
||||
@@ -144,24 +164,30 @@ const mergeSearchablePois = (pois: MuseumPoi[], spacePoints: MuseumPoi[]) => {
|
||||
.filter(([spaceId]) => Boolean(spaceId))
|
||||
)
|
||||
|
||||
const filteredPois = pois.filter((poi) => {
|
||||
const mergedPois = pois.map((poi) => {
|
||||
const spaceId = getPoiSpaceIdentity(poi)
|
||||
const matchingSpacePoint = spaceId ? spacePointsBySpaceId.get(spaceId) : null
|
||||
if (!matchingSpacePoint) return true
|
||||
const matchingHallPoi = spaceId ? hallPoisBySpaceId.get(spaceId) : null
|
||||
if (!matchingSpacePoint || matchingHallPoi?.id !== poi.id) return poi
|
||||
|
||||
return matchingSpacePoint.primaryCategory.id === 'exhibition_hall'
|
||||
return {
|
||||
...poi,
|
||||
primaryCategory: matchingSpacePoint.primaryCategory,
|
||||
categories: mergePoiCategories(
|
||||
[matchingSpacePoint.primaryCategory],
|
||||
matchingSpacePoint.categories
|
||||
)
|
||||
}
|
||||
})
|
||||
const filteredSpacePoints = spacePoints.filter((poi) => {
|
||||
const unmatchedSpacePoints = spacePoints.filter((poi) => {
|
||||
const spaceId = getPoiSpaceIdentity(poi)
|
||||
const matchingHallPoi = spaceId ? hallPoisBySpaceId.get(spaceId) : null
|
||||
if (!matchingHallPoi) return true
|
||||
|
||||
return poi.primaryCategory.id !== 'exhibition_hall'
|
||||
return !matchingHallPoi
|
||||
})
|
||||
|
||||
return dedupePoisById([
|
||||
...filteredPois,
|
||||
...filteredSpacePoints
|
||||
...mergedPois,
|
||||
...unmatchedSpacePoints
|
||||
])
|
||||
}
|
||||
|
||||
@@ -527,7 +553,12 @@ export class SgsSdkGuideRepository implements GuideRepository {
|
||||
|
||||
async getPoiById(id: string) {
|
||||
const pois = await this.searchPois()
|
||||
return pois.find((poi) => poi.id === id || poi.spaceId === id || poi.sourceSpaceId === id)
|
||||
const lookupIdentities = getPoiLookupIdentities(id)
|
||||
return pois.find((poi) => (
|
||||
lookupIdentities.has(poi.id)
|
||||
|| lookupIdentities.has(poi.spaceId || '')
|
||||
|| lookupIdentities.has(poi.sourceSpaceId || '')
|
||||
))
|
||||
|| findPoiByNavIdNameFallback(pois, id)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user