From c4d819021f875e3757bd067cbb7835bca3d151f0 Mon Sep 17 00:00:00 2001 From: lyf <2514544224@qq.com> Date: Sun, 28 Jun 2026 19:23:27 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=A5=BC=E5=B1=82=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E9=A1=BA=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/map/ThreeMap.vue | 11 +++-- src/domain/guideFloor.ts | 59 ++++++++++++++++++++++++ src/pages/search/index.vue | 11 +---- src/repositories/GuideModelRepository.ts | 5 +- src/repositories/GuideRepository.ts | 5 +- 5 files changed, 74 insertions(+), 17 deletions(-) diff --git a/src/components/map/ThreeMap.vue b/src/components/map/ThreeMap.vue index 583f3fa..f34e7c9 100644 --- a/src/components/map/ThreeMap.vue +++ b/src/components/map/ThreeMap.vue @@ -62,6 +62,9 @@ import type { GuideRouteFloorSegment, GuideRouteResult } from '@/domain/museum' +import { + compareFloorsTopToBottom +} from '@/domain/guideFloor' type ViewMode = 'overview' | 'floor' | 'multi' type CameraPreset = 'top' | 'oblique' @@ -202,7 +205,7 @@ const renderPackage = ref(null) const floors = computed(() => ( [...floorIndex.value] - .sort((a, b) => b.order - a.order) + .sort(compareFloorsTopToBottom) .map((floor) => ({ id: floor.floorId, label: formatFloorLabel(floor.floorId) @@ -1424,7 +1427,7 @@ const applyMultiFloorLayout = (items: MultiFloorModelItem[]) => { const centerIndex = (items.length - 1) / 2 items.forEach((item, index) => { - const offsetY = (index - centerIndex) * verticalGap + const offsetY = (centerIndex - index) * verticalGap item.model.position.y += offsetY item.model.userData.multiFloorOffsetY = offsetY item.model.userData.multiFloorVerticalGap = verticalGap @@ -1892,7 +1895,7 @@ const loadMultiFloor = async () => { const group = new THREE.Group() group.name = 'GuideMultiFloorModel' - const orderedFloors = [...floorIndex.value].sort((a, b) => a.order - b.order) + const orderedFloors = [...floorIndex.value].sort(compareFloorsTopToBottom) const loadedFloors: MultiFloorModelItem[] = [] const sharedModelUrl = getSharedMultiFloorModelUrl(orderedFloors) @@ -2695,7 +2698,7 @@ const loadModelPackage = async () => { renderPackage.value = packageData setProgress(14, '正在读取楼层索引...') - floorIndex.value = [...packageData.floors].sort((a, b) => a.order - b.order) + floorIndex.value = [...packageData.floors].sort(compareFloorsTopToBottom) if (!floorIndex.value.some((floor) => floor.floorId === currentFloor.value)) { currentFloor.value = floorIndex.value.find((floor) => floor.floorId === 'L1')?.floorId || floorIndex.value[0]?.floorId || 'L1' diff --git a/src/domain/guideFloor.ts b/src/domain/guideFloor.ts index 9fb08a1..28cd26a 100644 --- a/src/domain/guideFloor.ts +++ b/src/domain/guideFloor.ts @@ -11,6 +11,8 @@ interface FloorLike { floorName?: string | null name?: string | null modelGroup?: string | null + order?: number | null + sortOrder?: number | null } const indoorFloorCodePattern = /^L-?\d+(?:\.\d+)?$/i @@ -22,6 +24,63 @@ const normalizeValue = (value: unknown) => ( value === null || typeof value === 'undefined' ? '' : String(value).trim() ) +const parseFloorLevelFromToken = (value: unknown) => { + const normalized = normalizeValue(value).toUpperCase() + if (!normalized || isNonNavigableFloorToken(normalized)) return Number.NaN + + const basementMatch = normalized.match(/^B(\d+(?:\.\d+)?)$/) + if (basementMatch) return -Number(basementMatch[1]) + + const prefixedFloorMatch = normalized.match(/^F(\d+(?:\.\d+)?)$/) + if (prefixedFloorMatch) return Number(prefixedFloorMatch[1]) + + const labelMatch = normalized.match(/^(\d+(?:\.\d+)?)F$/) + if (labelMatch) return Number(labelMatch[1]) + + const codeMatch = normalized.match(/^L(-?\d+(?:\.\d+)?)$/) + if (codeMatch) return Number(codeMatch[1]) + + return Number.NaN +} + +export const getFloorSortLevel = (floor: FloorLike | null | undefined) => { + if (!floor) return Number.NaN + + const semanticTokens = [ + floor.label, + floor.floorCode, + floor.floorName, + floor.name, + floor.modelGroup, + floor.id, + floor.floorId + ] + + for (const token of semanticTokens) { + const level = parseFloorLevelFromToken(token) + if (Number.isFinite(level)) return level + } + + const order = Number(floor.order ?? floor.sortOrder) + return Number.isFinite(order) ? order : Number.NaN +} + +export const compareFloorsTopToBottom = (a: FloorLike, b: FloorLike) => { + const aLevel = getFloorSortLevel(a) + const bLevel = getFloorSortLevel(b) + + if (Number.isFinite(aLevel) && Number.isFinite(bLevel) && aLevel !== bLevel) { + return bLevel - aLevel + } + + if (Number.isFinite(aLevel) && !Number.isFinite(bLevel)) return -1 + if (!Number.isFinite(aLevel) && Number.isFinite(bLevel)) return 1 + + const aLabel = normalizeValue(a.label || a.floorCode || a.floorName || a.id || a.floorId) + const bLabel = normalizeValue(b.label || b.floorCode || b.floorName || b.id || b.floorId) + return aLabel.localeCompare(bLabel, 'zh-Hans-CN') +} + export const isNonNavigableFloorToken = (value: unknown) => { const normalized = normalizeValue(value) return Boolean(normalized && nonNavigableFloorPattern.test(normalized)) diff --git a/src/pages/search/index.vue b/src/pages/search/index.vue index 5590f99..c01cf79 100644 --- a/src/pages/search/index.vue +++ b/src/pages/search/index.vue @@ -107,6 +107,7 @@ import type { MuseumPoi } from '@/domain/museum' import { + compareFloorsTopToBottom, isIndoorNavigableFloor, isPoiOnIndoorNavigableFloor } from '@/domain/guideFloor' @@ -146,7 +147,7 @@ const isLoading = ref(false) const displayFloors = computed(() => [...floors.value] .filter(isIndoorNavigableFloor) - .sort((a, b) => floorOrder(a) - floorOrder(b))) + .sort(compareFloorsTopToBottom)) const floorResults = computed(() => { const matched = pois.value.filter((poi) => poi.floorLabel === activeFloor.value) @@ -157,14 +158,6 @@ const emptyTitle = computed(() => ( isLoading.value ? '正在读取点位' : '暂无匹配点位' )) -const floorOrder = (floor: MuseumFloor) => { - if (Number.isFinite(floor.order)) return floor.order - - const normalized = floor.label.toUpperCase() - if (normalized.startsWith('B')) return -Number(normalized.replace('B', '')) || -1 - return Number(normalized.replace('F', '')) || 0 -} - const isVisiblePoi = (poi: MuseumPoi) => poi.primaryCategory.id !== 'touring_poi' && isPoiOnIndoorNavigableFloor(poi) diff --git a/src/repositories/GuideModelRepository.ts b/src/repositories/GuideModelRepository.ts index bb1d680..82c848c 100644 --- a/src/repositories/GuideModelRepository.ts +++ b/src/repositories/GuideModelRepository.ts @@ -8,6 +8,7 @@ import { dataSourceConfig } from '@/config/dataSource' import { + compareFloorsTopToBottom, isIndoorNavigableFloor } from '@/domain/guideFloor' import { @@ -87,7 +88,7 @@ const resolveSgsAssetUrl = (url?: string | null) => { } const sortSgsFloors = (floors: SgsSdkFloorSummaryPayload[]) => [...floors] - .sort((a, b) => Number(a.sortOrder || 0) - Number(b.sortOrder || 0)) + .sort(compareFloorsTopToBottom) const isSharedSgsModelAsset = ( modelUrl: string, @@ -122,7 +123,7 @@ export class StaticGuideModelRepository implements GuideModelRepository { const floors: GuideModelFloorAsset[] = [...floorIndex.floors] .filter((floor) => isStaticIndoorNavigableFloorId(floor.floorId)) - .sort((a, b) => a.order - b.order) + .sort(compareFloorsTopToBottom) .map((floor) => ({ floorId: floor.floorId, label: formatNavFloorLabel(floor.floorId), diff --git a/src/repositories/GuideRepository.ts b/src/repositories/GuideRepository.ts index 6686478..681c3aa 100644 --- a/src/repositories/GuideRepository.ts +++ b/src/repositories/GuideRepository.ts @@ -11,6 +11,7 @@ import { NAV_ROUTE_READINESS } from '@/domain/guideReadiness' import { + compareFloorsTopToBottom, isIndoorNavigableFloor, isPoiOnIndoorNavigableFloor } from '@/domain/guideFloor' @@ -105,7 +106,7 @@ export class StaticGuideRepository implements GuideRepository { const floorIndex = await this.provider.loadFloorIndex() return [...floorIndex.floors] .filter((floor) => isStaticIndoorNavigableFloorId(floor.floorId)) - .sort((a, b) => b.order - a.order) + .sort(compareFloorsTopToBottom) .map((floor) => ({ id: floor.floorId, label: formatNavFloorLabel(floor.floorId), @@ -223,7 +224,7 @@ export class SgsSdkGuideRepository implements GuideRepository { this.floorAliases = buildSgsFloorAliases(indoorFloors) this.floorsCache = [...manifest.floors] .filter(isIndoorNavigableFloor) - .sort((a, b) => Number(a.sortOrder || 0) - Number(b.sortOrder || 0)) + .sort(compareFloorsTopToBottom) .map(toMuseumFloorFromSgs) return this.floorsCache