修复室内导览楼层切换事务一致性
This commit is contained in:
@@ -84,7 +84,6 @@ interface CameraFitOptions {
|
||||
interface LoadFloorOptions {
|
||||
preserveCurrentSceneUntilReady?: boolean
|
||||
suppressProgress?: boolean
|
||||
allowOverviewFallback?: boolean
|
||||
detachPoiBeforeLoad?: boolean
|
||||
}
|
||||
|
||||
@@ -179,6 +178,15 @@ interface PoiMarkerCacheEntry {
|
||||
markerSize: number
|
||||
}
|
||||
|
||||
interface PreparedFloorScene {
|
||||
floor: FloorIndexItem
|
||||
model: THREE.Object3D
|
||||
poiEntry: PoiMarkerCacheEntry | null
|
||||
ownsModel: boolean
|
||||
cacheAsSharedModel: boolean
|
||||
protectedResources?: ReusableModelResources
|
||||
}
|
||||
|
||||
type ContainerRef = HTMLElement | { $el?: HTMLElement } | null
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
@@ -270,6 +278,7 @@ let isDisposed = false
|
||||
let pendingTargetFocus: TargetPoiFocusRequest | null = null
|
||||
let targetFocusQueue: Promise<unknown> = Promise.resolve()
|
||||
let modelLoadVersion = 0
|
||||
let pendingRequestedFloorId = ''
|
||||
let hasLoadedFloorViewOnce = false
|
||||
let pointerDownState: { x: number; y: number } | null = null
|
||||
let cachedOverviewModel: THREE.Object3D | null = null
|
||||
@@ -1098,8 +1107,6 @@ const setProgress = (progress: number, message: string) => {
|
||||
}
|
||||
|
||||
const staleModelLoadMessage = 'STALE_MODEL_LOAD'
|
||||
let isFloorSwitchLocked = false
|
||||
let queuedFloorSwitchId = ''
|
||||
|
||||
const startModelLoad = () => {
|
||||
modelLoadVersion += 1
|
||||
@@ -1108,6 +1115,7 @@ const startModelLoad = () => {
|
||||
|
||||
const invalidateModelLoads = () => {
|
||||
modelLoadVersion += 1
|
||||
pendingRequestedFloorId = ''
|
||||
}
|
||||
|
||||
const isCurrentModelLoad = (loadToken: number) => (
|
||||
@@ -1146,6 +1154,60 @@ const assertActiveFloorModelState = (loadToken: number, requestedFloorId: string
|
||||
}
|
||||
}
|
||||
|
||||
const assertPreparedFloorScene = (
|
||||
prepared: PreparedFloorScene,
|
||||
loadToken: number,
|
||||
expectedFloorId: string
|
||||
) => {
|
||||
assertCurrentModelLoad(loadToken, prepared.ownsModel ? prepared.model : undefined)
|
||||
|
||||
if (pendingRequestedFloorId !== expectedFloorId) {
|
||||
if (prepared.ownsModel) {
|
||||
disposeObject(prepared.model, prepared.protectedResources)
|
||||
}
|
||||
throw createStaleModelLoadError()
|
||||
}
|
||||
|
||||
const modelFloorId = typeof prepared.model.userData.floorId === 'string'
|
||||
? prepared.model.userData.floorId
|
||||
: ''
|
||||
if (modelFloorId !== expectedFloorId) {
|
||||
if (prepared.ownsModel) {
|
||||
disposeObject(prepared.model, prepared.protectedResources)
|
||||
}
|
||||
throw new Error(`楼层模型校验失败:请求 ${expectedFloorId},模型 ${modelFloorId || '未知'}`)
|
||||
}
|
||||
|
||||
const poiFloorId = typeof prepared.poiEntry?.group.userData.floorId === 'string'
|
||||
? prepared.poiEntry.group.userData.floorId
|
||||
: ''
|
||||
if (prepared.poiEntry && (prepared.poiEntry.floorId !== expectedFloorId || poiFloorId !== expectedFloorId)) {
|
||||
if (prepared.ownsModel) {
|
||||
disposeObject(prepared.model, prepared.protectedResources)
|
||||
}
|
||||
throw new Error(`楼层点位校验失败:请求 ${expectedFloorId},点位 ${poiFloorId || prepared.poiEntry.floorId || '未知'}`)
|
||||
}
|
||||
}
|
||||
|
||||
const assertCommittedFloorScene = (loadToken: number, expectedFloorId: string) => {
|
||||
assertActiveFloorModelState(loadToken, expectedFloorId)
|
||||
|
||||
const poiFloorId = typeof poiGroup?.userData.floorId === 'string'
|
||||
? poiGroup.userData.floorId
|
||||
: ''
|
||||
const poiCurrentFloor = typeof poiGroup?.userData.currentFloor === 'string'
|
||||
? poiGroup.userData.currentFloor
|
||||
: ''
|
||||
|
||||
if (
|
||||
shouldRenderPoiMarkers.value
|
||||
&& poiGroup
|
||||
&& (poiFloorId !== expectedFloorId || poiCurrentFloor !== expectedFloorId)
|
||||
) {
|
||||
throw new Error(`楼层点位状态不一致:请求 ${expectedFloorId},点位 ${poiFloorId || '未知'},当前 ${poiCurrentFloor || '未知'}`)
|
||||
}
|
||||
}
|
||||
|
||||
const waitForContainer = async () => {
|
||||
await nextTick()
|
||||
|
||||
@@ -1483,6 +1545,11 @@ const detachPoiMarkerGroups = () => {
|
||||
poiMarkerGroupCache.forEach((entry) => {
|
||||
entry.group.parent?.remove(entry.group)
|
||||
})
|
||||
|
||||
if (poiGroup) {
|
||||
poiGroup.userData.floorId = ''
|
||||
poiGroup.userData.currentFloor = ''
|
||||
}
|
||||
}
|
||||
|
||||
const detachActivePoiLayer = () => {
|
||||
@@ -1997,8 +2064,6 @@ const canAttachCachedSharedModel = (modelUrl: string) => (
|
||||
Boolean(cachedOverviewModel && cachedSharedModelUrl === modelUrl && scene)
|
||||
)
|
||||
|
||||
const canAttachCachedOverviewModel = () => Boolean(cachedOverviewModel && scene)
|
||||
|
||||
const canLoadFloorSilently = (floorId: string) => {
|
||||
const floor = floorIndex.value.find((item) => item.floorId === floorId)
|
||||
return Boolean(floor && canAttachCachedSharedModel(floor.modelUrl))
|
||||
@@ -2301,7 +2366,6 @@ const checkAutoSwitch = (options: { forceOverviewEntry?: boolean } = {}) => {
|
||||
() => loadFloor(targetFloorId, {
|
||||
preserveCurrentSceneUntilReady: hasSceneToKeep,
|
||||
suppressProgress: hasSceneToKeep,
|
||||
allowOverviewFallback: false,
|
||||
detachPoiBeforeLoad: true
|
||||
}),
|
||||
'单楼层模型加载失败',
|
||||
@@ -2617,24 +2681,123 @@ const loadOverview = async () => {
|
||||
renderRoutePreview()
|
||||
}
|
||||
|
||||
const attachCachedSharedModel = (
|
||||
modelUrl: string,
|
||||
name: string,
|
||||
const prepareFloorScene = async (
|
||||
floor: FloorIndexItem,
|
||||
loadToken: number,
|
||||
floorId?: string,
|
||||
options: { allowAnyCachedOverview?: boolean } = {}
|
||||
options: LoadFloorOptions = {}
|
||||
): Promise<PreparedFloorScene> => {
|
||||
const requestedFloorId = floor.floorId
|
||||
let model: THREE.Object3D | null = null
|
||||
let ownsModel = true
|
||||
let cacheAsSharedModel = false
|
||||
let protectedResources: ReusableModelResources | undefined
|
||||
|
||||
const cachedExactSharedModel = canAttachCachedSharedModel(floor.modelUrl)
|
||||
? cachedOverviewModel
|
||||
: null
|
||||
|
||||
if (cachedExactSharedModel) {
|
||||
model = cachedExactSharedModel
|
||||
ownsModel = false
|
||||
protectedResources = collectReusableModelResources(cachedExactSharedModel)
|
||||
model.name = `GuideFloorModel_${requestedFloorId}`
|
||||
model.userData.floorId = requestedFloorId
|
||||
model.visible = true
|
||||
applyModelVisibilityForView(model, 'floor', requestedFloorId)
|
||||
} else {
|
||||
if (!options.suppressProgress) {
|
||||
setProgress(18, `正在加载 ${formatFloorLabel(requestedFloorId)} 模型...`)
|
||||
}
|
||||
|
||||
const gltf = await loadModel(
|
||||
floor.modelUrl,
|
||||
`正在加载 ${formatFloorLabel(requestedFloorId)} 模型`,
|
||||
loadToken,
|
||||
{ suppressProgress: options.suppressProgress }
|
||||
)
|
||||
|
||||
try {
|
||||
assertCurrentModelLoad(loadToken, gltf.scene)
|
||||
} catch (error) {
|
||||
disposeObject(gltf.scene)
|
||||
throw error
|
||||
}
|
||||
|
||||
model = gltf.scene
|
||||
model.name = `GuideFloorModel_${requestedFloorId}`
|
||||
model.userData.floorId = requestedFloorId
|
||||
prepareModel(model)
|
||||
applyModelVisibilityForView(model, 'floor', requestedFloorId)
|
||||
cacheAsSharedModel = Boolean(floor.sharedModelAsset)
|
||||
|
||||
if (cacheAsSharedModel) {
|
||||
cacheAutoSwitchOverviewMaxDim(model)
|
||||
}
|
||||
}
|
||||
|
||||
if (!model) {
|
||||
throw new Error(`楼层模型加载失败:${requestedFloorId}`)
|
||||
}
|
||||
|
||||
let poiEntry: PoiMarkerCacheEntry | undefined | null = null
|
||||
try {
|
||||
poiEntry = shouldRenderPoiMarkers.value
|
||||
? await prepareFloorPOIs(floor, loadToken)
|
||||
: null
|
||||
} catch (error) {
|
||||
if (ownsModel) {
|
||||
disposeObject(model, protectedResources)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
const prepared: PreparedFloorScene = {
|
||||
floor,
|
||||
model,
|
||||
poiEntry: poiEntry || null,
|
||||
ownsModel,
|
||||
cacheAsSharedModel,
|
||||
protectedResources
|
||||
}
|
||||
assertPreparedFloorScene(prepared, loadToken, requestedFloorId)
|
||||
|
||||
return prepared
|
||||
}
|
||||
|
||||
const commitPreparedFloorScene = (
|
||||
prepared: PreparedFloorScene,
|
||||
loadToken: number,
|
||||
expectedFloorId: string
|
||||
) => {
|
||||
if (!cachedOverviewModel || (!options.allowAnyCachedOverview && cachedSharedModelUrl !== modelUrl) || !scene) return false
|
||||
assertPreparedFloorScene(prepared, loadToken, expectedFloorId)
|
||||
|
||||
const targetScene = scene
|
||||
activeModel = cachedOverviewModel
|
||||
activeModel.name = name
|
||||
activeModel.userData.floorId = floorId || ''
|
||||
activeModel.visible = true
|
||||
applyModelVisibilityForView(activeModel, floorId ? 'floor' : 'multi', floorId)
|
||||
if (!targetScene) {
|
||||
if (prepared.ownsModel) {
|
||||
disposeObject(prepared.model, prepared.protectedResources)
|
||||
}
|
||||
throw createStaleModelLoadError()
|
||||
}
|
||||
|
||||
activeView.value = 'floor'
|
||||
currentFloor.value = expectedFloorId
|
||||
clearSceneData()
|
||||
activeModel = prepared.model
|
||||
|
||||
if (prepared.cacheAsSharedModel) {
|
||||
cachedOverviewModel = activeModel
|
||||
cachedSharedModelUrl = prepared.floor.modelUrl
|
||||
}
|
||||
|
||||
targetScene.add(activeModel)
|
||||
assertCurrentModelLoad(loadToken)
|
||||
return true
|
||||
if (prepared.poiEntry) {
|
||||
attachPoiMarkerGroup(prepared.poiEntry)
|
||||
} else if (poiGroup) {
|
||||
poiGroup.userData.floorId = expectedFloorId
|
||||
poiGroup.userData.currentFloor = expectedFloorId
|
||||
}
|
||||
|
||||
assertCommittedFloorScene(loadToken, expectedFloorId)
|
||||
}
|
||||
|
||||
const loadFloor = async (floorId: string, options: LoadFloorOptions = {}) => {
|
||||
@@ -2648,120 +2811,20 @@ const loadFloor = async (floorId: string, options: LoadFloorOptions = {}) => {
|
||||
|
||||
const loadToken = startModelLoad()
|
||||
const requestedFloorId = floor.floorId
|
||||
pendingRequestedFloorId = requestedFloorId
|
||||
if (options.detachPoiBeforeLoad || activeView.value === 'floor' || currentFloor.value !== requestedFloorId) {
|
||||
detachActivePoiLayer()
|
||||
}
|
||||
|
||||
const canUseExactCachedModel = canAttachCachedSharedModel(floor.modelUrl)
|
||||
const canUseOverviewFallback = !canUseExactCachedModel
|
||||
&& Boolean(options.allowOverviewFallback)
|
||||
&& Boolean(options.preserveCurrentSceneUntilReady)
|
||||
&& canAttachCachedOverviewModel()
|
||||
const preserveCurrentSceneUntilReady = options.preserveCurrentSceneUntilReady && !canUseExactCachedModel
|
||||
let nextModel: THREE.Object3D | null = null
|
||||
|
||||
if (!preserveCurrentSceneUntilReady) {
|
||||
activeView.value = 'floor'
|
||||
currentFloor.value = floor.floorId
|
||||
clearSceneData()
|
||||
}
|
||||
|
||||
if (canUseExactCachedModel) {
|
||||
activeView.value = 'floor'
|
||||
currentFloor.value = floor.floorId
|
||||
attachCachedSharedModel(
|
||||
floor.modelUrl,
|
||||
`GuideFloorModel_${floor.floorId}`,
|
||||
loadToken,
|
||||
floor.floorId
|
||||
)
|
||||
assertActiveFloorModelState(loadToken, requestedFloorId)
|
||||
} else {
|
||||
let gltf: GLTF | null = null
|
||||
|
||||
try {
|
||||
if (!options.suppressProgress) {
|
||||
setProgress(18, `正在加载 ${formatFloorLabel(floor.floorId)} 模型...`)
|
||||
}
|
||||
gltf = await loadModel(
|
||||
floor.modelUrl,
|
||||
`正在加载 ${formatFloorLabel(floor.floorId)} 模型`,
|
||||
loadToken,
|
||||
{ suppressProgress: options.suppressProgress }
|
||||
)
|
||||
try {
|
||||
assertCurrentModelLoad(loadToken, gltf.scene)
|
||||
} catch (error) {
|
||||
disposeObject(gltf.scene)
|
||||
throw error
|
||||
}
|
||||
} catch (error) {
|
||||
if (isStaleModelLoadError(error) || !canUseOverviewFallback) {
|
||||
throw error
|
||||
}
|
||||
|
||||
activeView.value = 'floor'
|
||||
currentFloor.value = floor.floorId
|
||||
clearSceneData()
|
||||
attachCachedSharedModel(
|
||||
floor.modelUrl,
|
||||
`GuideFloorModel_${floor.floorId}`,
|
||||
loadToken,
|
||||
floor.floorId,
|
||||
{ allowAnyCachedOverview: true }
|
||||
)
|
||||
assertActiveFloorModelState(loadToken, requestedFloorId)
|
||||
}
|
||||
|
||||
if (gltf) {
|
||||
const targetScene = scene
|
||||
if (!targetScene) {
|
||||
disposeObject(gltf.scene)
|
||||
throw createStaleModelLoadError()
|
||||
}
|
||||
|
||||
nextModel = gltf.scene
|
||||
nextModel.name = `GuideFloorModel_${floor.floorId}`
|
||||
nextModel.userData.floorId = floor.floorId
|
||||
prepareModel(nextModel)
|
||||
try {
|
||||
assertCurrentModelLoad(loadToken, nextModel)
|
||||
} catch (error) {
|
||||
nextModel = null
|
||||
throw error
|
||||
}
|
||||
if (floor.sharedModelAsset) {
|
||||
cacheAutoSwitchOverviewMaxDim(nextModel)
|
||||
}
|
||||
applyModelVisibilityForView(nextModel, 'floor', floor.floorId)
|
||||
|
||||
if (preserveCurrentSceneUntilReady) {
|
||||
activeView.value = 'floor'
|
||||
currentFloor.value = floor.floorId
|
||||
clearSceneData()
|
||||
}
|
||||
|
||||
activeModel = nextModel
|
||||
if (floor.sharedModelAsset) {
|
||||
cachedOverviewModel = activeModel
|
||||
cachedSharedModelUrl = floor.modelUrl
|
||||
}
|
||||
targetScene.add(activeModel)
|
||||
assertActiveFloorModelState(loadToken, requestedFloorId)
|
||||
}
|
||||
}
|
||||
const prepared = await prepareFloorScene(floor, loadToken, options)
|
||||
commitPreparedFloorScene(prepared, loadToken, requestedFloorId)
|
||||
|
||||
if (!activeModel) throw createStaleModelLoadError()
|
||||
assertActiveFloorModelState(loadToken, requestedFloorId)
|
||||
assertCommittedFloorScene(loadToken, requestedFloorId)
|
||||
hasLoadedFloorViewOnce = true
|
||||
fitCameraToObject(activeModel)
|
||||
markFloorAutoSwitchEntry()
|
||||
|
||||
if (shouldRenderPoiMarkers.value) {
|
||||
await loadFloorPOIs(floor, loadToken)
|
||||
assertActiveFloorModelState(loadToken, requestedFloorId)
|
||||
}
|
||||
|
||||
refreshPoiVisibilityByDistance()
|
||||
renderRoutePreview()
|
||||
}
|
||||
@@ -3421,6 +3484,7 @@ const createPoiMarkerGroup = (
|
||||
const group = new THREE.Group()
|
||||
group.name = `GuideModelPOI_${floorId}_${displayMode}`
|
||||
group.userData.floorId = floorId
|
||||
group.userData.currentFloor = floorId
|
||||
group.userData.displayMode = displayMode
|
||||
|
||||
pois.forEach((poi) => {
|
||||
@@ -3451,34 +3515,24 @@ const attachPoiMarkerGroup = (entry: PoiMarkerCacheEntry) => {
|
||||
if (entry.group.parent !== poiGroup) {
|
||||
poiGroup.add(entry.group)
|
||||
}
|
||||
poiGroup.userData.floorId = entry.floorId
|
||||
poiGroup.userData.currentFloor = entry.floorId
|
||||
updatePoiMarkerFocus()
|
||||
}
|
||||
|
||||
const loadFloorPOIs = async (floor: FloorIndexItem, loadToken?: number) => {
|
||||
if (!poiGroup) return
|
||||
|
||||
const prepareFloorPOIs = async (floor: FloorIndexItem, loadToken?: number) => {
|
||||
if (loadToken !== undefined && !isCurrentModelLoad(loadToken)) return
|
||||
if (loadToken !== undefined) {
|
||||
assertActiveFloorModelState(loadToken, floor.floorId)
|
||||
}
|
||||
|
||||
const displayMode = getPoiDisplayMode()
|
||||
const markerCacheKey = getPoiMarkerCacheKey(floor.floorId, displayMode)
|
||||
const cachedEntry = poiMarkerGroupCache.get(markerCacheKey)
|
||||
if (cachedEntry) {
|
||||
if (loadToken !== undefined) {
|
||||
assertActiveFloorModelState(loadToken, floor.floorId)
|
||||
}
|
||||
attachPoiMarkerGroup(cachedEntry)
|
||||
return
|
||||
return cachedEntry
|
||||
}
|
||||
|
||||
const cachedPois = poiDataCache.get(floor.floorId)
|
||||
const floorPois = cachedPois || await props.modelSource.loadFloorPois(floor.floorId)
|
||||
if (loadToken !== undefined && !isCurrentModelLoad(loadToken)) return
|
||||
if (loadToken !== undefined) {
|
||||
assertActiveFloorModelState(loadToken, floor.floorId)
|
||||
}
|
||||
|
||||
poiDataCache.set(floor.floorId, floorPois)
|
||||
const validPois = floorPois.filter((poi) => shouldShowPoiInCurrentMode(poi))
|
||||
@@ -3496,11 +3550,25 @@ const loadFloorPOIs = async (floor: FloorIndexItem, loadToken?: number) => {
|
||||
disposeObject(markerGroup)
|
||||
return
|
||||
}
|
||||
|
||||
poiMarkerGroupCache.set(markerCacheKey, entry)
|
||||
return entry
|
||||
}
|
||||
|
||||
const loadFloorPOIs = async (floor: FloorIndexItem, loadToken?: number) => {
|
||||
if (!poiGroup) return
|
||||
|
||||
if (loadToken !== undefined) {
|
||||
assertActiveFloorModelState(loadToken, floor.floorId)
|
||||
}
|
||||
|
||||
const entry = await prepareFloorPOIs(floor, loadToken)
|
||||
if (!entry) return
|
||||
|
||||
if (loadToken !== undefined) {
|
||||
assertActiveFloorModelState(loadToken, floor.floorId)
|
||||
}
|
||||
|
||||
poiMarkerGroupCache.set(markerCacheKey, entry)
|
||||
attachPoiMarkerGroup(entry)
|
||||
}
|
||||
|
||||
@@ -3576,7 +3644,6 @@ const handleSceneTap = async (event: PointerEvent) => {
|
||||
await loadFloor(selectedPoi.floorId, {
|
||||
preserveCurrentSceneUntilReady: hasRenderableSceneForFloorTransition(),
|
||||
suppressProgress: true,
|
||||
allowOverviewFallback: false,
|
||||
detachPoiBeforeLoad: true
|
||||
})
|
||||
emit('floorChange', selectedPoi.floorId)
|
||||
@@ -3706,7 +3773,6 @@ const focusTargetPoi = async (request: TargetPoiFocusRequest) => {
|
||||
await loadFloor(request.floorId, {
|
||||
preserveCurrentSceneUntilReady: hasRenderableSceneForFloorTransition(),
|
||||
suppressProgress: true,
|
||||
allowOverviewFallback: false,
|
||||
detachPoiBeforeLoad: true
|
||||
})
|
||||
isLoading.value = false
|
||||
@@ -3823,22 +3889,14 @@ const init3DScene = async () => {
|
||||
|
||||
const handleFloorChange = async (floorId: string) => {
|
||||
const requestedFloorId = resolveFloorIdFromRequest(floorId) || floorId
|
||||
if (isFloorSwitchLocked) {
|
||||
queuedFloorSwitchId = requestedFloorId
|
||||
return
|
||||
}
|
||||
|
||||
isFloorSwitchLocked = true
|
||||
queuedFloorSwitchId = ''
|
||||
|
||||
try {
|
||||
isLoading.value = true
|
||||
loadError.value = false
|
||||
setProgress(12, `正在切换到 ${formatFloorLabel(requestedFloorId)}...`)
|
||||
await loadFloor(requestedFloorId, {
|
||||
preserveCurrentSceneUntilReady: false,
|
||||
preserveCurrentSceneUntilReady: true,
|
||||
suppressProgress: false,
|
||||
allowOverviewFallback: false,
|
||||
detachPoiBeforeLoad: true
|
||||
})
|
||||
updatePoiVisibilityByDistance()
|
||||
@@ -3851,15 +3909,6 @@ const handleFloorChange = async (floorId: string) => {
|
||||
loadError.value = true
|
||||
isLoading.value = false
|
||||
setProgress(0, error instanceof Error ? error.message : '楼层模型加载失败')
|
||||
} finally {
|
||||
isFloorSwitchLocked = false
|
||||
if (queuedFloorSwitchId && queuedFloorSwitchId !== currentFloor.value) {
|
||||
const nextFloorId = queuedFloorSwitchId
|
||||
queuedFloorSwitchId = ''
|
||||
void handleFloorChange(nextFloorId)
|
||||
} else {
|
||||
queuedFloorSwitchId = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -445,6 +445,7 @@ const activeFloorId = computed(() => {
|
||||
return normalizedFloor?.id || normalizedFloorId
|
||||
})
|
||||
const activeFloorScrollIntoView = ref('')
|
||||
const pendingFloorId = ref('')
|
||||
let suppressFloorAutoScrollUntil = 0
|
||||
|
||||
const syncActiveFloorScroll = () => {
|
||||
@@ -523,15 +524,31 @@ const handleModeChange = (mode: '2d' | '3d') => {
|
||||
emit('modeChange', mode)
|
||||
}
|
||||
|
||||
const isStaleFloorSwitchError = (error: unknown) => (
|
||||
error instanceof Error && error.message === 'STALE_MODEL_LOAD'
|
||||
)
|
||||
|
||||
const handleFloorChange = (floor: { id: string; label: string }) => {
|
||||
const floorId = floor.id || props.normalizeFloorId(floor.label)
|
||||
if (!floorId || pendingFloorId.value === floorId) return
|
||||
|
||||
pendingFloorId.value = floorId
|
||||
suppressFloorAutoScrollUntil = Date.now() + 650
|
||||
activeFloorScrollIntoView.value = ''
|
||||
indoorRendererRef.value?.disableAutoSwitchTemporarily?.(10000)
|
||||
void indoorRendererRef.value?.switchFloor?.(floorId)
|
||||
emit('indoorViewChange', 'floor')
|
||||
emit('layerModeChange', 'single')
|
||||
emit('floorChange', floor.label)
|
||||
|
||||
Promise.resolve(indoorRendererRef.value?.switchFloor?.(floorId))
|
||||
.catch((error) => {
|
||||
if (isStaleFloorSwitchError(error)) return
|
||||
console.error('楼层切换失败:', error)
|
||||
})
|
||||
.finally(() => {
|
||||
if (pendingFloorId.value === floorId) {
|
||||
pendingFloorId.value = ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleLayerModeChange = (mode: LayerDisplayMode) => {
|
||||
@@ -539,10 +556,22 @@ const handleLayerModeChange = (mode: LayerDisplayMode) => {
|
||||
indoorRendererRef.value?.disableAutoSwitchTemporarily?.(10000)
|
||||
|
||||
if (mode === 'multi') {
|
||||
pendingFloorId.value = ''
|
||||
void indoorRendererRef.value?.showMultiFloor?.()
|
||||
emit('indoorViewChange', 'multi')
|
||||
} else {
|
||||
void indoorRendererRef.value?.switchFloor?.(activeFloorId.value)
|
||||
const floorId = activeFloorId.value
|
||||
pendingFloorId.value = floorId
|
||||
Promise.resolve(indoorRendererRef.value?.switchFloor?.(floorId))
|
||||
.catch((error) => {
|
||||
if (isStaleFloorSwitchError(error)) return
|
||||
console.error('楼层切换失败:', error)
|
||||
})
|
||||
.finally(() => {
|
||||
if (pendingFloorId.value === floorId) {
|
||||
pendingFloorId.value = ''
|
||||
}
|
||||
})
|
||||
emit('indoorViewChange', 'floor')
|
||||
}
|
||||
|
||||
@@ -593,8 +622,7 @@ const handleMoreTap = () => {
|
||||
}
|
||||
|
||||
const handleThreeFloorChange = (floorId: string) => {
|
||||
const floor = indoorFloors.value.find((item) => item.id === floorId)
|
||||
emit('floorChange', floor?.label || floorId)
|
||||
emit('floorChange', floorId)
|
||||
emit('indoorViewChange', 'floor')
|
||||
emit('layerModeChange', 'single')
|
||||
}
|
||||
|
||||
@@ -164,8 +164,15 @@ export const buildSgsFloorAliases = (floors: SgsSdkFloorSummaryPayload[]) => {
|
||||
|
||||
const label = formatSgsFloorLabel(floor.floorCode, floor.floorName)
|
||||
aliases.set(floorId, floorId)
|
||||
if (floor.floorCode) aliases.set(String(floor.floorCode), floorId)
|
||||
if (floor.floorName) aliases.set(String(floor.floorName), floorId)
|
||||
aliases.set(floorId.toLowerCase(), floorId)
|
||||
if (floor.floorCode) {
|
||||
aliases.set(String(floor.floorCode), floorId)
|
||||
aliases.set(String(floor.floorCode).toLowerCase(), floorId)
|
||||
}
|
||||
if (floor.floorName) {
|
||||
aliases.set(String(floor.floorName), floorId)
|
||||
aliases.set(String(floor.floorName).toLowerCase(), floorId)
|
||||
}
|
||||
aliases.set(label, floorId)
|
||||
aliases.set(label.toLowerCase(), floorId)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user