chore: initialize frontend miniapp repository
This commit is contained in:
152
src/services/navAssets.ts
Normal file
152
src/services/navAssets.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
export const NAV_ASSET_BASE_URL = '/static/nav-assets/app_nav_assets_v2_clean_20260609_075339'
|
||||
|
||||
export const NAV_ROUTE_GRAPH_READY = false
|
||||
|
||||
export const NAV_ROUTE_UNAVAILABLE_MESSAGE = '正式路线数据尚未接入,可先查看馆内三维位置'
|
||||
|
||||
export interface NavFloorOption {
|
||||
id: string
|
||||
label: string
|
||||
order: number
|
||||
}
|
||||
|
||||
export interface CleanNavPoiCategory {
|
||||
topCategory: string
|
||||
topCategoryZh: string
|
||||
subcategory: string
|
||||
iconType: string
|
||||
reason?: string
|
||||
}
|
||||
|
||||
export interface CleanNavPoi {
|
||||
id: string
|
||||
name: string
|
||||
floorId: string
|
||||
primaryCategory: string
|
||||
primaryCategoryZh: string
|
||||
categories?: CleanNavPoiCategory[]
|
||||
iconType?: string
|
||||
positionGltf?: [number, number, number]
|
||||
navigationReadiness?: string
|
||||
sourceConfidence?: string
|
||||
}
|
||||
|
||||
interface CleanPoiIndex {
|
||||
status: string
|
||||
pois: CleanNavPoi[]
|
||||
}
|
||||
|
||||
export const NAV_FLOOR_OPTIONS: NavFloorOption[] = [
|
||||
{ id: 'L5', label: '5F', order: 5 },
|
||||
{ id: 'L4', label: '4F', order: 4 },
|
||||
{ id: 'L3', label: '3F', order: 3 },
|
||||
{ id: 'L2', label: '2F', order: 2 },
|
||||
{ id: 'L1.5', label: '1.5F', order: 1.5 },
|
||||
{ id: 'L1', label: '1F', order: 1 },
|
||||
{ id: 'L-1', label: 'B1', order: -1 },
|
||||
{ id: 'L-2', label: 'B2', order: -2 }
|
||||
]
|
||||
|
||||
const searchableCategories = new Set([
|
||||
'touring_poi',
|
||||
'basic_service_facility',
|
||||
'transport_circulation',
|
||||
'accessibility_special_service',
|
||||
'operation_experience'
|
||||
])
|
||||
|
||||
let poiCache: CleanNavPoi[] | null = null
|
||||
|
||||
const normalizeBaseUrl = (baseUrl: string) => baseUrl.replace(/\/+$/, '')
|
||||
|
||||
export const navAssetUrl = (relativePath: string, baseUrl = NAV_ASSET_BASE_URL) => (
|
||||
`${normalizeBaseUrl(baseUrl)}/${relativePath.replace(/^\/+/, '')}`
|
||||
)
|
||||
|
||||
export const formatNavFloorLabel = (floorId: string) => (
|
||||
NAV_FLOOR_OPTIONS.find((floor) => floor.id === floorId)?.label || floorId
|
||||
)
|
||||
|
||||
export const navFloorIdFromLabel = (labelOrId: string) => (
|
||||
NAV_FLOOR_OPTIONS.find((floor) => floor.label === labelOrId || floor.id === labelOrId)?.id || labelOrId
|
||||
)
|
||||
|
||||
export const isPoiAccessible = (poi: CleanNavPoi) => (
|
||||
poi.primaryCategory === 'accessibility_special_service'
|
||||
|| poi.categories?.some((category) => category.topCategory === 'accessibility_special_service') === true
|
||||
)
|
||||
|
||||
const parseJsonPayload = <T>(payload: unknown): T => {
|
||||
if (typeof payload === 'string') {
|
||||
return JSON.parse(payload) as T
|
||||
}
|
||||
|
||||
return payload as T
|
||||
}
|
||||
|
||||
const requestJson = <T>(url: string): Promise<T> => new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url,
|
||||
method: 'GET',
|
||||
success: (response) => {
|
||||
const statusCode = Number(response.statusCode || 0)
|
||||
if (statusCode < 200 || statusCode >= 300) {
|
||||
reject(new Error(`导览资源读取失败: ${statusCode} ${url}`))
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
resolve(parseJsonPayload<T>(response.data))
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
},
|
||||
fail: reject
|
||||
})
|
||||
})
|
||||
|
||||
export const loadCleanNavPois = async (baseUrl = NAV_ASSET_BASE_URL) => {
|
||||
if (poiCache) return poiCache
|
||||
|
||||
const data = await requestJson<CleanPoiIndex>(navAssetUrl('data/poi_all.json', baseUrl))
|
||||
if (data.status !== 'pass') {
|
||||
throw new Error('导览 POI 数据状态不是 pass')
|
||||
}
|
||||
|
||||
poiCache = data.pois.filter((poi) => searchableCategories.has(poi.primaryCategory))
|
||||
return poiCache
|
||||
}
|
||||
|
||||
export const loadCleanNavPoiById = async (id: string, baseUrl = NAV_ASSET_BASE_URL) => {
|
||||
const pois = await loadCleanNavPois(baseUrl)
|
||||
return pois.find((poi) => poi.id === id) || null
|
||||
}
|
||||
|
||||
export const searchCleanNavPois = async (keyword = '', baseUrl = NAV_ASSET_BASE_URL) => {
|
||||
const pois = await loadCleanNavPois(baseUrl)
|
||||
const normalizedKeyword = keyword.trim().toLowerCase()
|
||||
|
||||
const matchedPois = normalizedKeyword
|
||||
? pois.filter((poi) => {
|
||||
const searchableText = [
|
||||
poi.name,
|
||||
poi.floorId,
|
||||
formatNavFloorLabel(poi.floorId),
|
||||
poi.primaryCategoryZh,
|
||||
poi.iconType,
|
||||
...(poi.categories || []).flatMap((category) => [
|
||||
category.topCategoryZh,
|
||||
category.subcategory,
|
||||
category.iconType
|
||||
])
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
.toLowerCase()
|
||||
|
||||
return searchableText.includes(normalizedKeyword)
|
||||
})
|
||||
: pois
|
||||
|
||||
return matchedPois
|
||||
}
|
||||
Reference in New Issue
Block a user