优化三维导览模型加载与交互体验

This commit is contained in:
lyf
2026-06-27 23:49:38 +08:00
parent 4f6059bbdb
commit f043c85af0
12 changed files with 1433 additions and 509 deletions

View File

@@ -7,6 +7,9 @@ import type {
import {
dataSourceConfig
} from '@/config/dataSource'
import {
isIndoorNavigableFloor
} from '@/domain/guideFloor'
import {
formatSgsFloorLabel,
toMuseumPoiFromSgs
@@ -17,7 +20,8 @@ import {
type SgsSdkFloorSummaryPayload
} from '@/data/providers/sgsSdkApiProvider'
import {
formatNavFloorLabel
formatNavFloorLabel,
isStaticIndoorNavigableFloorId
} from '@/data/adapters/navAssetsAdapter'
import {
defaultStaticNavAssetsProvider,
@@ -97,6 +101,7 @@ export class StaticGuideModelRepository implements GuideModelRepository {
])
const floors: GuideModelFloorAsset[] = [...floorIndex.floors]
.filter((floor) => isStaticIndoorNavigableFloorId(floor.floorId))
.sort((a, b) => a.order - b.order)
.map((floor) => ({
floorId: floor.floorId,
@@ -115,7 +120,7 @@ export class StaticGuideModelRepository implements GuideModelRepository {
async loadFloorPois(floorId: string) {
const floorIndex = await this.provider.loadFloorIndex()
const floor = floorIndex.floors.find((item) => item.floorId === floorId)
if (!floor) return []
if (!floor || !isStaticIndoorNavigableFloorId(floor.floorId)) return []
const pois = await this.provider.loadFloorPois(floor.poiDataAsset)
return pois
@@ -131,7 +136,8 @@ export class SgsSdkGuideModelRepository implements GuideModelRepository {
async loadPackage(): Promise<GuideModelRenderPackage> {
const manifest = await this.provider.getManifest()
const floors = sortSgsFloors(manifest.floors)
const allFloors = sortSgsFloors(manifest.floors)
const floors = allFloors.filter(isIndoorNavigableFloor)
this.floorsCache = floors
const bundles = await Promise.all(
@@ -153,8 +159,13 @@ export class SgsSdkGuideModelRepository implements GuideModelRepository {
})
.filter((asset) => asset.modelUrl)
const overviewFloorIndex = floors.findIndex((floor) => floor.floorCode === 'EXTERIOR')
const overviewModelUrl = floorAssets[overviewFloorIndex]?.modelUrl || floorAssets[0]?.modelUrl || ''
const overviewFloor = allFloors.find((floor) => !isIndoorNavigableFloor(floor))
const overviewBundle = overviewFloor
? await this.provider.getFloorBundle(String(overviewFloor.floorId)).catch(() => null)
: null
const overviewModelUrl = resolveSgsAssetUrl(
overviewBundle?.model?.modelUrl || overviewBundle?.model?.fallbackModelUrl
) || floorAssets[0]?.modelUrl || ''
return {
overviewModelUrl,
@@ -164,7 +175,7 @@ export class SgsSdkGuideModelRepository implements GuideModelRepository {
async loadFloorPois(floorId: string) {
const manifest = await this.provider.getManifest()
this.floorsCache = sortSgsFloors(manifest.floors)
this.floorsCache = sortSgsFloors(manifest.floors).filter(isIndoorNavigableFloor)
const matchedFloor = this.floorsCache.find((floor) => (
String(floor.floorId) === floorId
|| floor.floorCode === floorId

View File

@@ -10,8 +10,13 @@ import type {
import {
NAV_ROUTE_READINESS
} from '@/domain/guideReadiness'
import {
isIndoorNavigableFloor,
isPoiOnIndoorNavigableFloor
} from '@/domain/guideFloor'
import {
formatNavFloorLabel,
isStaticIndoorNavigableFloorId,
navFloorIdFromLabel,
toMuseumPoi
} from '@/data/adapters/navAssetsAdapter'
@@ -68,6 +73,11 @@ const toLocationPreview = (poi: MuseumPoi): GuideLocationPreview => ({
sourceObjectName: poi.sourceObjectName
})
const isSearchableIndoorPoi = (poi: MuseumPoi) => (
searchableCategories.has(poi.primaryCategory.id)
&& isPoiOnIndoorNavigableFloor(poi)
)
export interface GuideRepository {
getAssetBaseUrl(): string
getFloors(): Promise<MuseumFloor[]>
@@ -94,6 +104,7 @@ export class StaticGuideRepository implements GuideRepository {
async getFloors() {
const floorIndex = await this.provider.loadFloorIndex()
return [...floorIndex.floors]
.filter((floor) => isStaticIndoorNavigableFloorId(floor.floorId))
.sort((a, b) => b.order - a.order)
.map((floor) => ({
id: floor.floorId,
@@ -111,8 +122,8 @@ export class StaticGuideRepository implements GuideRepository {
const pois = await this.provider.loadPoiIndex()
this.poiCache = pois
.filter((poi) => searchableCategories.has(poi.primaryCategory))
.map(toMuseumPoi)
.filter(isSearchableIndoorPoi)
return this.poiCache
}
@@ -208,8 +219,10 @@ export class SgsSdkGuideRepository implements GuideRepository {
if (this.floorsCache) return this.floorsCache
const manifest = await this.provider.getManifest()
this.floorAliases = buildSgsFloorAliases(manifest.floors)
const indoorFloors = manifest.floors.filter(isIndoorNavigableFloor)
this.floorAliases = buildSgsFloorAliases(indoorFloors)
this.floorsCache = [...manifest.floors]
.filter(isIndoorNavigableFloor)
.sort((a, b) => Number(a.sortOrder || 0) - Number(b.sortOrder || 0))
.map(toMuseumFloorFromSgs)
@@ -228,15 +241,18 @@ export class SgsSdkGuideRepository implements GuideRepository {
if (this.poiCache) return this.poiCache
const manifest = await this.provider.getManifest()
this.floorAliases = buildSgsFloorAliases(manifest.floors)
const indoorFloors = manifest.floors.filter(isIndoorNavigableFloor)
const indoorFloorIds = new Set(indoorFloors.map((floor) => String(floor.floorId)))
this.floorAliases = buildSgsFloorAliases(indoorFloors)
const poiGroups = await Promise.all(
manifest.floors.map((floor) => this.provider.getFloorPois(String(floor.floorId)))
indoorFloors.map((floor) => this.provider.getFloorPois(String(floor.floorId)))
)
this.poiCache = poiGroups
.flat()
.map((poi) => toMuseumPoiFromSgs(poi, manifest.floors))
.filter((poi) => poi.id && poi.name)
.filter((poi) => indoorFloorIds.has(String(poi.floorId)) || isPoiOnIndoorNavigableFloor(poi))
return this.poiCache
}
@@ -271,11 +287,11 @@ export class SgsSdkGuideRepository implements GuideRepository {
const normalizedFloorId = this.normalizeFloorId(floorId)
try {
const diagnostics = await this.provider.getFloorDiagnostics(normalizedFloorId)
return toGuideFloorDetailFromSgs(diagnostics)
return isIndoorNavigableFloor(diagnostics) ? toGuideFloorDetailFromSgs(diagnostics) : null
} catch {
const manifest = await this.provider.getManifest()
const floor = manifest.floors.find((item) => String(item.floorId) === normalizedFloorId)
return floor ? toGuideFloorDetailFromSgs(floor) : null
return floor && isIndoorNavigableFloor(floor) ? toGuideFloorDetailFromSgs(floor) : null
}
}

View File

@@ -3,6 +3,9 @@ import type {
GuideRouteResult,
GuideRouteTarget
} from '@/domain/museum'
import {
isIndoorNavigableFloor
} from '@/domain/guideFloor'
import {
applyRouteReadinessGate,
NAV_ROUTE_UNAVAILABLE_MESSAGE,
@@ -164,7 +167,10 @@ export class StaticGuideRouteRepository implements GuideRouteRepository {
async listRouteTargets() {
const dataset = await this.loadDataset()
return dataset.targets
return dataset.targets.filter((target) => isIndoorNavigableFloor({
floorId: target.floorId,
label: target.floorLabel
}))
}
async findRoute(startPoiId: string, endPoiId: string) {

View File

@@ -6,6 +6,9 @@ import type {
import {
NAV_ROUTE_READINESS
} from '@/domain/guideReadiness'
import {
isIndoorNavigableFloor
} from '@/domain/guideFloor'
import {
GuideRouteError
} from '@/repositories/GuideRouteRepository'
@@ -63,7 +66,7 @@ export class SdkGuideRouteRepository {
async getRouteReadiness(): Promise<GuideRouteReadiness> {
try {
const diagnostics = await this.sdkApiProvider.getMapDiagnostics()
const floors = diagnostics.floors || []
const floors = (diagnostics.floors || []).filter(isIndoorNavigableFloor)
if (!floors.length) {
return createUnavailableReadiness('SDK 地图楼层数据为空')
@@ -115,7 +118,7 @@ export class SdkGuideRouteRepository {
if (this.targetsCache) return this.targetsCache
const diagnostics = await this.sdkApiProvider.getMapDiagnostics()
const floors = diagnostics.floors || []
const floors = (diagnostics.floors || []).filter(isIndoorNavigableFloor)
const targets: GuideRouteTarget[] = []
@@ -196,7 +199,7 @@ export class SdkGuideRouteRepository {
// 获取原始 floorId 用于 API 调用
const diagnostics = await this.sdkApiProvider.getMapDiagnostics()
const floorIdMap = new Map<string, string>()
for (const floor of diagnostics.floors || []) {
for (const floor of (diagnostics.floors || []).filter(isIndoorNavigableFloor)) {
floorIdMap.set(toAppFloorId(floor.floorId), stringifyId(floor.floorId))
}
const rawStartFloorId = floorIdMap.get(startTarget.floorId) || startTarget.floorId