chore: initialize frontend miniapp repository

This commit is contained in:
lyf
2026-06-09 21:08:45 +08:00
commit a90f63cef0
107 changed files with 60454 additions and 0 deletions

152
src/services/navAssets.ts Normal file
View 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
}