更新 SGS 地图 SDK 并补充数据层接入手册
This commit is contained in:
568
docs/Data/SGS_SDK_DATA_LAYER_INTEGRATION_GUIDE.md
Normal file
568
docs/Data/SGS_SDK_DATA_LAYER_INTEGRATION_GUIDE.md
Normal file
@@ -0,0 +1,568 @@
|
||||
# SGS Map SDK 数据层接入操作手册
|
||||
|
||||
创建日期:2026-06-25
|
||||
适用项目:`frontend-miniapp` H5 导览业务
|
||||
目标:通过后端 SGS Map SDK 数据接口接入真实楼层、POI、空间面和导航目的地数据,同时保持展示层只消费领域模型。
|
||||
|
||||
## 1. 接入原则
|
||||
|
||||
SGS Map SDK 接入分为两条边界:
|
||||
|
||||
| 边界 | 职责 | 允许接触 SDK/后端字段的位置 | 禁止事项 |
|
||||
| --- | --- | --- | --- |
|
||||
| 数据层 | 拉取楼层、POI、空间面、导航目的地、诊断信息,并转换为 `MuseumFloor`、`MuseumPoi`、`GuideLocationPreview` 等领域模型 | `src/data/providers`、`src/data/adapters`、`src/repositories` | 页面和组件直接请求 `/app-api/gis/sdk/*` |
|
||||
| 渲染层 | 加载 SGS H5 地图基座、管理 iframe/SDK 生命周期、执行切楼层和聚焦命令 | `src/services/sgs`、`src/components/map/SgsMapRenderer.vue` | 把后端数据解析逻辑写进 `SgsMapRenderer.vue` |
|
||||
|
||||
展示层必须只通过以下入口拿数据:
|
||||
|
||||
- `src/usecases/guideUseCase.ts`
|
||||
- `src/repositories/GuideRepository.ts` 暴露的 `GuideRepository` 合同
|
||||
- `src/domain/museum.ts` 中的领域模型
|
||||
|
||||
展示层禁止直接依赖:
|
||||
|
||||
- `SGSMapSDK`
|
||||
- `/app-api/gis/sdk/*` 响应结构
|
||||
- `floorCode`、`typeName`、`position.x/y/z` 等后端原始字段
|
||||
- `manifest.poiCount`、`manifest.spaceCount` 等不可靠摘要字段
|
||||
|
||||
## 2. 当前服务地址
|
||||
|
||||
部署信息:
|
||||
|
||||
| 服务 | 地址 | 当前验证结果 |
|
||||
| --- | --- | --- |
|
||||
| 后端直连 | `http://1.92.206.90:48080/yudao-server/app-api` | 当前开发机访问超时,需服务端排查防火墙/安全组/监听地址 |
|
||||
| 前端代理 | `http://1.92.206.90:3001/app-api` | 可访问,返回后端 `CommonResult code=0` |
|
||||
| H5 SDK 基座 | `http://1.92.206.90:3001/h5-sdk` | 可访问 |
|
||||
| SDK 脚本 | `http://1.92.206.90:3001/sdk/sgs-map-sdk.min.js` | 可访问 |
|
||||
|
||||
本项目推荐环境变量:
|
||||
|
||||
```env
|
||||
VITE_DATA_SOURCE_MODE=sdk
|
||||
VITE_API_BASE_URL=/app-api
|
||||
VITE_SGS_SDK_SCRIPT_URL=/sdk/sgs-map-sdk.min.js
|
||||
VITE_SGS_H5_ENGINE_URL=/h5-sdk
|
||||
VITE_SGS_SDK_ORIGIN=http://1.92.206.90:3001
|
||||
VITE_SGS_SDK_TIMEOUT_MS=10000
|
||||
```
|
||||
|
||||
本地直连联调时可临时使用:
|
||||
|
||||
```env
|
||||
VITE_API_BASE_URL=http://1.92.206.90:3001/app-api
|
||||
VITE_SGS_H5_ENGINE_URL=http://1.92.206.90:3001/h5-sdk
|
||||
VITE_SGS_SDK_SCRIPT_URL=http://1.92.206.90:3001/sdk/sgs-map-sdk.min.js
|
||||
VITE_SGS_SDK_ORIGIN=http://1.92.206.90:3001
|
||||
```
|
||||
|
||||
不要在页面或组件里读取这些环境变量。统一通过 `src/config/dataSource.ts` 暴露配置。
|
||||
|
||||
## 3. 后端接口清单
|
||||
|
||||
SDK 数据接口走 App API 通道,控制器为:
|
||||
|
||||
- `smart-navigation-system/yudao-module-gis/src/main/java/cn/iocoder/yudao/module/gis/controller/app/spatial/SdkMapController.java`
|
||||
- 请求前缀:`/app-api/gis/sdk`
|
||||
|
||||
必接接口:
|
||||
|
||||
| 用途 | 方法 | 路径 | 数据层使用方式 |
|
||||
| --- | --- | --- | --- |
|
||||
| 地图 Manifest | GET | `/gis/sdk/maps/{mapId}/manifest` | 只用于楼层列表、版本、能力声明;不要信任 `poiCount/spaceCount` |
|
||||
| 楼层完整包 | GET | `/gis/sdk/floors/{floorId}/bundle` | 单层详情、模型信息、POI、空间面、路网摘要 |
|
||||
| 楼层 POI | GET | `/gis/sdk/floors/{floorId}/pois` | POI 主数据 |
|
||||
| 楼层空间面 | GET | `/gis/sdk/floors/{floorId}/spaces` | 展厅/空间/服务空间 |
|
||||
| 可导航目的地 | GET | `/gis/sdk/floors/{floorId}/navigable-places` | 起终点候选和空间门点 |
|
||||
| 地图诊断 | GET | `/gis/sdk/maps/{mapId}/diagnostics` | QA、健康检查、路网就绪判断 |
|
||||
| 楼层诊断 | GET | `/gis/sdk/floors/{floorId}/diagnostics` | 单层数据完整性与路网就绪 |
|
||||
|
||||
后端原始 App 口径,可用于交叉核验:
|
||||
|
||||
| 用途 | 方法 | 路径 |
|
||||
| --- | --- | --- |
|
||||
| 楼层主表 | GET | `/gis/floor/list` |
|
||||
| 原始 POI | GET | `/gis/poi/list-by-floor?floorId={floorId}` |
|
||||
| C 端精简 POI | GET | `/gis/sgs-poi/list-by-floor?floorId={floorId}` |
|
||||
|
||||
当前已知数据风险:
|
||||
|
||||
- `manifest.floors[].poiCount` 当前全部为 `0`,实际后端 POI 为 `162`。
|
||||
- `manifest.floors[].spaceCount` 当前与实际空间面数量不一致。
|
||||
- `L-1` 有 POI 和路网,但 `navigable-places=0`。
|
||||
- `EXTERIOR` 路网节点和边为 `0`,不能用于路径规划。
|
||||
- 逐层 `guide-stops` 合计为 `0`,地图 diagnostics 中 `guideStopCount=1`,聚合口径不一致。
|
||||
- SDK POI 当前全部 `status=INACTIVE`,`anchorNodeName`、`description`、`iconUrl` 为空。数据层不要因为 `INACTIVE` 自动丢弃全部 POI,除非后端明确状态语义。
|
||||
|
||||
## 4. 推荐落地文件
|
||||
|
||||
只新增或修改数据边界文件,展示层不动。
|
||||
|
||||
```text
|
||||
src/config/dataSource.ts
|
||||
继续作为 mode/api/sdk/url 配置唯一入口
|
||||
|
||||
src/data/providers/sgsSdkApiProvider.ts
|
||||
新增:负责请求 /app-api/gis/sdk/*
|
||||
|
||||
src/data/adapters/sgsSdkGuideAdapter.ts
|
||||
新增:负责 SDK 响应 -> MuseumFloor/MuseumPoi/GuideLocationPreview/GuideRouteReadiness
|
||||
|
||||
src/repositories/GuideRepository.ts
|
||||
增加 ApiGuideRepository 或 SgsSdkGuideRepository
|
||||
保持 GuideRepository interface 不变
|
||||
|
||||
src/repositories/createGuideRepository.ts
|
||||
可选新增:根据 dataSourceConfig.mode 选择 StaticGuideRepository 或 SgsSdkGuideRepository
|
||||
|
||||
src/usecases/guideUseCase.ts
|
||||
原则上不改业务方法,只替换 repository 注入来源
|
||||
```
|
||||
|
||||
不要把以下逻辑写进组件:
|
||||
|
||||
```text
|
||||
src/pages/**
|
||||
src/components/navigation/**
|
||||
src/components/search/**
|
||||
src/components/map/SgsMapRenderer.vue
|
||||
```
|
||||
|
||||
`SgsMapRenderer.vue` 只负责 SDK iframe/渲染生命周期。它可以接收领域模型里来的 `floorId`、`poiId`、`positionGltf`,但不能自己请求 POI 列表或解析后端字段。
|
||||
|
||||
## 5. Provider 实现要求
|
||||
|
||||
`sgsSdkApiProvider.ts` 只做网络请求和 CommonResult 解包,不做领域转换。
|
||||
|
||||
建议接口:
|
||||
|
||||
```ts
|
||||
export interface SgsSdkApiProvider {
|
||||
getManifest(mapId?: string): Promise<SgsSdkManifestPayload>
|
||||
getMapDiagnostics(mapId?: string): Promise<SgsMapDiagnosticsPayload>
|
||||
getFloorDiagnostics(floorId: string): Promise<SgsFloorDiagnosticsPayload>
|
||||
getFloorBundle(floorId: string): Promise<SgsFloorBundlePayload>
|
||||
getFloorPois(floorId: string): Promise<SgsPoiPayload[]>
|
||||
getFloorSpaces(floorId: string): Promise<SgsSpacePayload[]>
|
||||
getNavigablePlaces(floorId: string): Promise<SgsNavigablePlacePayload[]>
|
||||
}
|
||||
```
|
||||
|
||||
请求实现规则:
|
||||
|
||||
- 使用 `dataSourceConfig.apiBaseUrl` 作为基地址。
|
||||
- H5 下可继续用 `uni.request`,不要在组件中使用 `fetch`。
|
||||
- 统一处理 `{ code, data, msg }`,`code !== 0` 必须抛出带路径的错误。
|
||||
- 请求超时和网络错误要带 endpoint 信息,方便 QA 判断是后端还是代理问题。
|
||||
- 可以在 Provider 内做短生命周期缓存,避免每次搜索重复拉全量 POI。
|
||||
- Provider 返回类型命名使用 `Payload` 后缀,提醒调用者这是后端形状,不是领域模型。
|
||||
|
||||
示例骨架:
|
||||
|
||||
```ts
|
||||
import { dataSourceConfig } from '@/config/dataSource'
|
||||
|
||||
const normalizeBaseUrl = (baseUrl: string) => baseUrl.replace(/\/+$/, '')
|
||||
|
||||
const requestJson = <T>(path: string): Promise<T> => new Promise((resolve, reject) => {
|
||||
const baseUrl = normalizeBaseUrl(dataSourceConfig.apiBaseUrl || '/app-api')
|
||||
|
||||
uni.request({
|
||||
url: `${baseUrl}${path}`,
|
||||
method: 'GET',
|
||||
success: (response) => {
|
||||
const statusCode = Number(response.statusCode || 0)
|
||||
if (statusCode < 200 || statusCode >= 300) {
|
||||
reject(new Error(`SGS 后端接口请求失败: ${statusCode} ${path}`))
|
||||
return
|
||||
}
|
||||
|
||||
const body = typeof response.data === 'string'
|
||||
? JSON.parse(response.data)
|
||||
: response.data
|
||||
|
||||
if (!body || body.code !== 0) {
|
||||
reject(new Error(`SGS 后端接口业务失败: ${path} code=${body?.code} msg=${body?.msg || ''}`))
|
||||
return
|
||||
}
|
||||
|
||||
resolve(body.data as T)
|
||||
},
|
||||
fail: (error) => reject(new Error(`SGS 后端接口网络失败: ${path} ${JSON.stringify(error)}`))
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## 6. Adapter 映射规则
|
||||
|
||||
`sgsSdkGuideAdapter.ts` 是阻断展示层污染的关键。所有后端字段都在这里转换。
|
||||
|
||||
### 6.1 楼层映射
|
||||
|
||||
后端字段:
|
||||
|
||||
```ts
|
||||
{
|
||||
floorId: '2065808921272119298',
|
||||
floorCode: 'L1',
|
||||
floorName: '1.0层',
|
||||
sortOrder: 10
|
||||
}
|
||||
```
|
||||
|
||||
领域模型:
|
||||
|
||||
```ts
|
||||
MuseumFloor {
|
||||
id: floorId,
|
||||
label: readableFloorLabel(floorCode, floorName),
|
||||
order: sortOrder
|
||||
}
|
||||
```
|
||||
|
||||
建议显示标签:
|
||||
|
||||
| 后端 floorCode | 领域 label |
|
||||
| --- | --- |
|
||||
| `EXTERIOR` | `室外` |
|
||||
| `L-2` | `B2` |
|
||||
| `L-1` | `B1` |
|
||||
| `L1` | `1F` |
|
||||
| `L1.5` | `1.5F` |
|
||||
| `L2` | `2F` |
|
||||
|
||||
`normalizeFloorId(labelOrId)` 必须同时支持:
|
||||
|
||||
- 后端长 ID,例如 `2065808921272119298`
|
||||
- 后端 `floorCode`,例如 `L1`
|
||||
- 前端显示标签,例如 `1F`
|
||||
|
||||
### 6.2 POI 映射
|
||||
|
||||
后端字段:
|
||||
|
||||
```ts
|
||||
{
|
||||
id: '5576',
|
||||
name: '服务台',
|
||||
type: 'service_desk',
|
||||
typeName: '服务台',
|
||||
floorId: '2065808921272119298',
|
||||
floorCode: 'L1',
|
||||
position: { x: -59.617386, y: 0.658652, z: 16.816816 },
|
||||
status: 'INACTIVE',
|
||||
anchorNodeName: null,
|
||||
description: null,
|
||||
iconUrl: null
|
||||
}
|
||||
```
|
||||
|
||||
领域模型:
|
||||
|
||||
```ts
|
||||
MuseumPoi {
|
||||
id: String(id),
|
||||
name,
|
||||
floorId: String(floorId),
|
||||
floorLabel,
|
||||
primaryCategory: mapSgsPoiCategory(type, typeName),
|
||||
categories: [primaryCategory],
|
||||
positionGltf: [position.x, position.y, position.z],
|
||||
sourceConfidence: 'backend-sgs-sdk',
|
||||
navigationReadiness,
|
||||
accessible
|
||||
}
|
||||
```
|
||||
|
||||
坐标规则:
|
||||
|
||||
- 后端 SDK 文档声明坐标系为 `GLB_METER`。
|
||||
- `position.x/y/z` 进入领域层统一保存为 `positionGltf: [x, y, z]`。
|
||||
- 不要在展示层重排坐标轴。
|
||||
- 如果某个接口只返回 `x/y/z` 平铺字段,也在 Adapter 内统一转为 `positionGltf`。
|
||||
|
||||
类型映射建议:
|
||||
|
||||
| SGS type | 领域分类 id | label | accessible |
|
||||
| --- | --- | --- | --- |
|
||||
| `toilet` | `basic_service_facility` | `卫生间` | false |
|
||||
| `accessible_toilet` | `accessibility_special_service` | `无障碍卫生间` | true |
|
||||
| `elevator` | `transport_circulation` | `电梯` | true |
|
||||
| `stairs` | `transport_circulation` | `楼梯` | false |
|
||||
| `escalator` | `transport_circulation` | `扶梯` | false |
|
||||
| `entrance_exit` | `transport_circulation` | `出入口` | false |
|
||||
| `service_desk` | `basic_service_facility` | `服务台` | false |
|
||||
| `mother_baby_room` | `basic_service_facility` | `母婴室` | true |
|
||||
| `locker` | `basic_service_facility` | `存包处` | false |
|
||||
| `rental_service` | `basic_service_facility` | `租赁服务` | true |
|
||||
| `ticket_office` | `basic_service_facility` | `售票处` | false |
|
||||
|
||||
不要让组件判断 `type === 'toilet'`。组件只看领域层的 `primaryCategory`、`accessible`、`floorLabel`。
|
||||
|
||||
### 6.3 导航目的地映射
|
||||
|
||||
`navigable-places` 不等同于 POI,它可能是空间门点。
|
||||
|
||||
建议在数据层使用独立内部类型,例如:
|
||||
|
||||
```ts
|
||||
interface SgsNavigablePlaceDomain {
|
||||
id: string
|
||||
name: string
|
||||
floorId: string
|
||||
floorLabel: string
|
||||
category: 'poi' | 'door' | 'space' | 'guide'
|
||||
positionGltf: [number, number, number]
|
||||
ownerName?: string
|
||||
}
|
||||
```
|
||||
|
||||
当前 `GuideRepository` 合同没有暴露导航目的地列表。如果近期只做位置预览,可暂时只把 POI 映射到 `MuseumPoi`。如果要启用真实路线选择,再扩展 `GuideRouteRepository` 或新增 route use case,避免把导航目的地塞进 POI 列表污染搜索。
|
||||
|
||||
### 6.4 路线就绪映射
|
||||
|
||||
`GuideRepository.getRouteReadiness()` 应基于 diagnostics,而不是静态常量。
|
||||
|
||||
建议规则:
|
||||
|
||||
```text
|
||||
mapDiagnostics.status === 'OK'
|
||||
且所有室内可导航楼层 routePlanningReady=true
|
||||
且 routeNodeCount > 0
|
||||
且 routeEdgeCount > 0
|
||||
且关键楼层 navigablePlaceCount > 0
|
||||
=> ready=true
|
||||
|
||||
否则 ready=false,并把 warnings / requiredData 写入 GuideRouteReadiness
|
||||
```
|
||||
|
||||
当前线上数据必须返回 `ready=false` 或“实验性路线预览”,因为:
|
||||
|
||||
- `EXTERIOR` 路径规划未就绪。
|
||||
- `L-1` 无可导航目的地。
|
||||
- guideStop 聚合口径不一致。
|
||||
|
||||
## 7. Repository 接入方式
|
||||
|
||||
保持现有 `GuideRepository` interface 不变:
|
||||
|
||||
```ts
|
||||
export interface GuideRepository {
|
||||
getAssetBaseUrl(): string
|
||||
getFloors(): Promise<MuseumFloor[]>
|
||||
normalizeFloorId(labelOrId: string): string
|
||||
listPois(): Promise<MuseumPoi[]>
|
||||
getPoiById(id: string): Promise<MuseumPoi | null>
|
||||
searchPois(keyword?: string): Promise<MuseumPoi[]>
|
||||
getLocationPreview(poiId: string): Promise<GuideLocationPreview | null>
|
||||
getRouteReadiness(): Promise<GuideRouteReadiness>
|
||||
}
|
||||
```
|
||||
|
||||
新增 `SgsSdkGuideRepository`:
|
||||
|
||||
```ts
|
||||
export class SgsSdkGuideRepository implements GuideRepository {
|
||||
private floorsCache: MuseumFloor[] | null = null
|
||||
private poiCache: MuseumPoi[] | null = null
|
||||
|
||||
constructor(private readonly provider: SgsSdkApiProvider = defaultSgsSdkApiProvider) {}
|
||||
|
||||
getAssetBaseUrl() {
|
||||
return ''
|
||||
}
|
||||
|
||||
async getFloors() {
|
||||
if (this.floorsCache) return this.floorsCache
|
||||
const manifest = await this.provider.getManifest('1')
|
||||
this.floorsCache = manifest.floors
|
||||
.map(toMuseumFloor)
|
||||
.sort((a, b) => a.order - b.order)
|
||||
return this.floorsCache
|
||||
}
|
||||
|
||||
async listPois() {
|
||||
if (this.poiCache) return this.poiCache
|
||||
const manifest = await this.provider.getManifest('1')
|
||||
const poisByFloor = await Promise.all(
|
||||
manifest.floors.map((floor) => this.provider.getFloorPois(String(floor.floorId)))
|
||||
)
|
||||
this.poiCache = poisByFloor.flat().map((poi) => toMuseumPoiFromSgs(poi, manifest.floors))
|
||||
return this.poiCache
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Repository 规则:
|
||||
|
||||
- `listPois()` 可以跨楼层聚合,但只返回领域模型。
|
||||
- `getPoiById()` 从 `listPois()` 缓存查找,避免重复请求。
|
||||
- `searchPois()` 使用 `MuseumPoi` 字段构建搜索文本,不能搜索后端 raw JSON。
|
||||
- `getLocationPreview()` 只返回 `GuideLocationPreview`。
|
||||
- `getRouteReadiness()` 只返回 `GuideRouteReadiness`,不要把 diagnostics 原样传给 UI。
|
||||
|
||||
## 8. 模式切换
|
||||
|
||||
当前 `src/config/dataSource.ts` 已有:
|
||||
|
||||
```ts
|
||||
export type DataSourceMode = 'static' | 'api' | 'sdk'
|
||||
export const isSgsSdkMode = () => dataSourceConfig.mode === 'sdk'
|
||||
```
|
||||
|
||||
推荐新增仓库工厂:
|
||||
|
||||
```ts
|
||||
import { dataSourceConfig } from '@/config/dataSource'
|
||||
import { StaticGuideRepository } from '@/repositories/GuideRepository'
|
||||
import { SgsSdkGuideRepository } from '@/repositories/SgsSdkGuideRepository'
|
||||
|
||||
export const createGuideRepository = () => {
|
||||
if (dataSourceConfig.mode === 'sdk' || dataSourceConfig.mode === 'api') {
|
||||
return new SgsSdkGuideRepository()
|
||||
}
|
||||
|
||||
return new StaticGuideRepository()
|
||||
}
|
||||
```
|
||||
|
||||
然后 `guideUseCase.ts` 只依赖工厂返回的 `GuideRepository`,页面不用知道当前是 static/api/sdk。
|
||||
|
||||
推荐模式语义:
|
||||
|
||||
| mode | 数据来源 | 渲染器 |
|
||||
| --- | --- | --- |
|
||||
| `static` | 本地 clean nav-assets | `ThreeMap` |
|
||||
| `api` | 后端 SGS App API | 仍可用 `ThreeMap` 或本地渲染 |
|
||||
| `sdk` | 后端 SGS App API | `SgsMapRenderer` |
|
||||
|
||||
不要把 `sdk` 理解成“页面直接调用 `SGSMapSDK.getFloorPois()`”。数据仍从 Repository 进来,SDK 只做地图渲染和交互命令。
|
||||
|
||||
## 9. 展示层改造边界
|
||||
|
||||
允许展示层做的事:
|
||||
|
||||
- 调用 `guideUseCase.getFloors()`
|
||||
- 调用 `guideUseCase.searchPois(keyword)`
|
||||
- 调用 `guideUseCase.getPoiById(id)`
|
||||
- 传 `GuideLocationPreview.positionGltf` 给地图聚焦
|
||||
- 通过 `GuideMapShell` 选择 `ThreeMap` 或 `SgsMapRenderer`
|
||||
|
||||
禁止展示层做的事:
|
||||
|
||||
```ts
|
||||
// 禁止:页面直接请求后端
|
||||
uni.request({ url: '/app-api/gis/sdk/floors/xxx/pois' })
|
||||
|
||||
// 禁止:组件直接判断后端字段
|
||||
if (poi.type === 'accessible_toilet') { ... }
|
||||
|
||||
// 禁止:组件直接拼后端楼层 ID 显示
|
||||
text = `${raw.floorCode}-${raw.floorName}`
|
||||
|
||||
// 禁止:SgsMapRenderer 拉取 POI 列表
|
||||
await service.getFloorPois(floorId)
|
||||
```
|
||||
|
||||
如果组件需要新增展示信息,先判断是否属于领域模型:
|
||||
|
||||
- 属于稳定导览语义:加到 `src/domain/museum.ts`。
|
||||
- 属于后端传输字段:留在 Provider/Adapter,禁止穿透。
|
||||
- 属于 SDK 渲染命令结果:放到 `src/services/sgs/SgsMapEventAdapter.ts` 转成 UI 事件。
|
||||
|
||||
## 10. 推荐实施步骤
|
||||
|
||||
1. 新增 `src/data/providers/sgsSdkApiProvider.ts`。
|
||||
2. 新增 `src/data/adapters/sgsSdkGuideAdapter.ts`。
|
||||
3. 新增 `SgsSdkGuideRepository`,实现现有 `GuideRepository` interface。
|
||||
4. 新增仓库工厂,根据 `dataSourceConfig.mode` 选择 static 或 SGS 后端数据。
|
||||
5. 让 `guideUseCase` 使用仓库工厂,不改页面调用方式。
|
||||
6. 在 `sdk` 模式下让 `GuideMapShell` 继续选择 `SgsMapRenderer`,但 POI/楼层数据仍来自 `GuideUseCase`。
|
||||
7. 增加数据健康检查脚本或开发命令,校验楼层、POI、空间面、导航目的地计数。
|
||||
8. 通过 H5 浏览器检查搜索、楼层切换、POI 聚焦、位置预览。
|
||||
|
||||
## 11. 验收检查清单
|
||||
|
||||
数据接口检查:
|
||||
|
||||
```powershell
|
||||
$base = "http://1.92.206.90:3001/app-api"
|
||||
Invoke-RestMethod "$base/gis/floor/list"
|
||||
Invoke-RestMethod "$base/gis/sdk/maps/1/manifest"
|
||||
Invoke-RestMethod "$base/gis/sdk/maps/1/diagnostics"
|
||||
Invoke-RestMethod "$base/gis/sdk/floors/2065808921272119298/pois"
|
||||
Invoke-RestMethod "$base/gis/sdk/floors/2065808921272119298/navigable-places"
|
||||
```
|
||||
|
||||
必须满足:
|
||||
|
||||
- `/gis/floor/list` 与 SDK manifest 楼层 ID 一致。
|
||||
- `/gis/poi/list-by-floor` 与 `/gis/sdk/floors/{id}/pois` POI ID 一致。
|
||||
- POI 无重复 ID。
|
||||
- POI 无缺失 `id/name/type/floorId/position`。
|
||||
- POI `floorId` 与请求楼层一致。
|
||||
- 空间面无缺失 `id/name/type/floorId/boundaryWkt`。
|
||||
- `GuideRepository.listPois()` 只返回 `MuseumPoi[]`。
|
||||
- 页面源码中不能出现 `/app-api/gis/sdk`。
|
||||
- 页面源码中不能出现 `SGSMapSDK`。
|
||||
|
||||
代码扫描:
|
||||
|
||||
```powershell
|
||||
rg -n "/app-api/gis/sdk|SGSMapSDK|getFloorPois|getManifest|getNavigablePlaces" src/pages src/components
|
||||
```
|
||||
|
||||
预期:
|
||||
|
||||
- `src/components/map/SgsMapRenderer.vue` 可以出现 SDK 渲染相关服务调用。
|
||||
- 其它页面和组件不应直接出现后端 SDK 数据接口或 SDK 全局对象。
|
||||
|
||||
构建验证:
|
||||
|
||||
```powershell
|
||||
pnpm type-check
|
||||
pnpm lint
|
||||
pnpm build:h5
|
||||
```
|
||||
|
||||
浏览器验证:
|
||||
|
||||
- `static` 模式仍可加载本地 3D/POI。
|
||||
- `api` 模式能展示后端楼层和 POI,渲染器不变。
|
||||
- `sdk` 模式能加载 SGS 地图基座,楼层切换与 POI 聚焦可用。
|
||||
- SDK 失败时有错误态,不出现空白地图。
|
||||
- 顶部 tabs、搜索、楼层控件、详情卡片不被 iframe/canvas 遮挡。
|
||||
|
||||
## 12. 上线前阻断项
|
||||
|
||||
以下问题未解决前,不建议对用户宣称“正式室内导航”:
|
||||
|
||||
- `L-1` 无可导航目的地。
|
||||
- `EXTERIOR` 路网未就绪。
|
||||
- `manifest` 计数与实际明细不一致。
|
||||
- `guideStopCount` 聚合口径不一致。
|
||||
- POI 全部 `status=INACTIVE` 的业务语义未确认。
|
||||
|
||||
用户文案应保持为:
|
||||
|
||||
- `位置预览`
|
||||
- `查看三维位置`
|
||||
- `路线预览`
|
||||
- `导航数据准备中`
|
||||
|
||||
不要使用:
|
||||
|
||||
- `开始馆内导航`
|
||||
- `实时导航`
|
||||
- `到达引导`
|
||||
- `精准路径规划`
|
||||
|
||||
## 13. 维护约定
|
||||
|
||||
- 后端接口字段变化先改 Provider Payload 类型。
|
||||
- 领域含义变化再改 Adapter。
|
||||
- UI 需要新信息时先扩展领域模型,再通过 Repository 暴露。
|
||||
- 不要为了一个页面临时把 raw SDK 字段传透到组件。
|
||||
- 每次更新 SDK 后,重新跑楼层/POI/空间面/导航目的地完整性检查。
|
||||
- 每次启用真实路线能力前,重新审核 `GuideRouteReadiness`,保持不夸大导航能力。
|
||||
@@ -1,5 +1,44 @@
|
||||
# SGS Map SDK Changelog
|
||||
|
||||
## [2.3.0] - 2026-06-23
|
||||
|
||||
### 🚀 新特性 (New Features)
|
||||
- **Manifest Capabilities 扩展**:新增 9 个 v2.3 标准能力字段(`mapLoading`, `floorSwitching`, `poiQuery`, `spaceQuery`, `navigablePlaces`, `crossFloorRoute`, `accessibleRoute`, `highlight`, `diagnostics`),前端可按需判断后端支持的功能。
|
||||
- **诊断接口 (Diagnostics API)**:新增地图级和楼层级诊断接口,实时检查数据完整性和就绪状态:
|
||||
- `sdk.getDiagnostics()` - 获取地图整体诊断信息(包含所有楼层摘要和汇总统计)
|
||||
- `sdk.getFloorDiagnostics(floorId)` - 获取单个楼层的详细诊断信息
|
||||
- 诊断信息包含:模型就绪状态、POI数量、空间面数量、讲解点数量、可导航目的地数量、路网节点/边数量、路径规划就绪状态、警告列表
|
||||
- **统一状态枚举**:诊断接口状态统一为 `OK` / `WARN` / `ERROR`,提供清晰的健康状态指示
|
||||
|
||||
### 🐞 修复与强化 (Fixes & Improvements)
|
||||
- **修复阻断问题**:Manifest 正常流程补齐 v2.3 capabilities 字段设置,修复所有新增字段返回 null 的问题
|
||||
- **楼层级统计准确性修复**:修复楼层诊断中 `guideStopCount` 缺少 `floorId` 过滤,现在正确按楼层统计而非全馆统计
|
||||
- **地图级 summary 完善**:补齐地图级诊断 summary 的 4 个统计项(`modelReadyFloorCount`, `poiCount`, `spaceCount`, `guideStopCount`),现在包含完整的 8 个统计维度
|
||||
- **H5 Bridge 消息处理**:新增 `GET_DIAGNOSTICS` 和 `GET_FLOOR_DIAGNOSTICS` 消息处理,完整支持诊断功能
|
||||
|
||||
### 📝 类型定义更新
|
||||
- 新增 `SgsMapDiagnostics` 接口(地图级诊断响应)
|
||||
- 新增 `SgsFloorDiagnostics` 接口(楼层级诊断响应)
|
||||
- 新增 `SgsFloorDiagnosticsSummary` 接口(楼层诊断摘要)
|
||||
- 扩展 `SgsSdkManifest.capabilities` 类型定义,包含所有 v2.3 标准字段
|
||||
|
||||
### 🔧 后端接口新增
|
||||
- `GET /app-api/gis/sdk/maps/{mapId}/diagnostics` - 地图级诊断
|
||||
- `GET /app-api/gis/sdk/floors/{floorId}/diagnostics` - 楼层级诊断
|
||||
|
||||
---
|
||||
|
||||
## [2.2.0] - 2026-06-19
|
||||
|
||||
### 🚀 新特性 (New Features)
|
||||
- **性能监控与埋点治理**:新增底层加载耗时记录(如 `sdkCreatedAt`, `iframeDomReadyMs`, `engineReadyMs`, `manifestLoadMs`, `modelDownloadAndParseMs`),提供完整的控制台性能概览面板。
|
||||
- **Demo 全面重构**:Kiosk Demo 与 Mobile Demo 引入非阻塞型 Loading 遮罩层,提升了白屏阶段的 UI 可交互体验。
|
||||
|
||||
### 🐞 修复与强化 (Fixes & Improvements)
|
||||
- **SDK 发布管线加固**:构建体系重构,每次 Build 将自动清空并同步覆盖 `public/sdk/` 与发版包中的产物,修正了 Source Map 的内部指向错误,彻底杜绝了旧类型定义的残留。
|
||||
- 深度清理所有旧版硬编码版本号注释,统一向 v2.2.0 API 规范拉齐。
|
||||
- 对齐了前后端 SDK Payload 返回值 VO 定义。
|
||||
|
||||
## [2.0.0] - 2026-06-12
|
||||
|
||||
### 🚀 新特性 (New Features)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SGS Map SDK 交付包
|
||||
|
||||
> **版本**:V2.0.0
|
||||
> **版本**:V2.3.0
|
||||
> **定位**:深圳自然博物馆统一三维高精地图导航服务平台 H5 SDK
|
||||
|
||||
欢迎接入 SGS Map SDK!本 SDK 将复杂的 WebGL 三维渲染、GLB 模型管线与 NavMesh 物理寻路引擎封装在服务端基座中,业务端(H5 / 大屏 / 小程序)只需通过几行代码即可极速唤起 3D 地图。
|
||||
@@ -16,10 +16,9 @@ sgs-map-sdk-release/
|
||||
│ ├── index.mjs.map # ESM source map
|
||||
│ ├── index.d.ts # TypeScript 类型声明文件
|
||||
│ └── index.d.mts # TypeScript 模块声明文件
|
||||
├── example/
|
||||
│ └── index.html # 完整接入示例(带控制台、日志面板、与后端 API 联调)
|
||||
├── package.json # NPM 元信息
|
||||
├── sdk-quickstart.md # 【必读】10 分钟快速接入指南(含完整示例代码)
|
||||
├── sdk-api-reference.md # 【必读】第三方开发 API 参考手册
|
||||
├── sdk-protocol.md # 【选读】底层通信协议与安全沙箱规范说明
|
||||
└── README.md # 当前说明文档
|
||||
```
|
||||
@@ -41,14 +40,84 @@ sgs-map-sdk-release/
|
||||
- **部署要求**:本 SDK 是“双域架构”。除了在您的业务前端引入 `sgs-map-sdk` 库以外,**SGS 统一地图管理平台必须在您的服务器或内网环境中完成独立部署**(基座项目为 `sgs-frontend-map`)。
|
||||
- **版本对应**:SDK 与基座严格遵守大版本一致原则。例如,SDK `V2.x.x` 必须对应基座引擎的 `V2.x.x`。如果强行跨版本混用,`HELLO` 协议握手将被拒绝并抛出 `ERR_NOT_READY`。
|
||||
|
||||
## 📤 发布目录说明
|
||||
|
||||
| 用途 | 目录/入口 | 说明 |
|
||||
|------|-----------|------|
|
||||
| SDK 独立交付包 | `E:\sgs-dm\sgs-map-sdk-release` | 对外交付 SDK、类型声明、示例和接入文档 |
|
||||
| SDK 包产物 | `E:\sgs-dm\sgs-map-sdk-release\dist` | 保留 `index.global.js`、`index.mjs`、`index.d.ts` 等包内标准命名 |
|
||||
| 前端公开 SDK | `E:\sgs-dm\sgs-frontend-map\public\sdk` | Next 静态目录,对外路径为 `/sdk/sgs-map-sdk.min.js` |
|
||||
|
||||
从 `sgs-frontend-map` 执行 `npm run build:sdk` 后,会自动构建 `dist/sdk`,并同步到 `public/sdk` 与 `sgs-map-sdk-release/dist`。正式发布前以这三个目录的产物一致性作为验收口径。
|
||||
|
||||
## 📚 如何开始?
|
||||
|
||||
- **如果您是普通前端业务开发 / 微信小程序开发**:
|
||||
直接打开 `sdk-quickstart.md`,复制里面的示例代码,10分钟即可完成接入。
|
||||
*(注:微信小程序团队完全无需引入 `dist` 下的代码,请仔细阅读文档中小程序 `<web-view>` 的原生接入方式)*
|
||||
|
||||
- **如果您想看一份能直接跑的完整示例**:
|
||||
打开 `example/index.html`,用本地静态服务器(如 `npx serve`)打开即可,里面包含楼层切换、聚焦、寻路、图钉、超时、销毁等全场景演示。需要先把基座 `sgs-frontend-map` 跑起来并把 `sdkUrl` 改成对应的本地地址。
|
||||
- **如果您需要逐个查询 API 参数、返回值和错误码**:
|
||||
打开 `sdk-api-reference.md`,按方法名查阅第三方调用说明。
|
||||
|
||||
- **如果您是对架构感兴趣的高级开发**:
|
||||
可以阅读 `sdk-protocol.md`,了解我们如何通过双域确权协议解决 `postMessage` 多实例安全问题,以及 WebGL 的安全释放机制。
|
||||
|
||||
---
|
||||
|
||||
## 🆕 V2.3.0 当前能力概览
|
||||
|
||||
### V2.3.0 新增方法
|
||||
|
||||
| 方法 | 签名 | 说明 |
|
||||
|------|------|------|
|
||||
| `getNavigablePlaces` | `getNavigablePlaces(floorId: string \| number)` | 获取指定楼层可用于导航的目的地列表,优先返回业务门点并过滤纯交通设施本体 |
|
||||
| `getDiagnostics` | `getDiagnostics()` | 获取地图级健康诊断,包含楼层、模型、POI、空间、讲解点、路网和可导航目的地统计 |
|
||||
| `getFloorDiagnostics` | `getFloorDiagnostics(floorId: string \| number)` | 获取单楼层详细诊断,用于发布前检查数据完整性 |
|
||||
|
||||
### V2.3.0 Manifest 能力字段
|
||||
|
||||
`manifest.capabilities` 新增标准能力声明:
|
||||
|
||||
```text
|
||||
mapLoading, floorSwitching, poiQuery, spaceQuery,
|
||||
navigablePlaces, crossFloorRoute, accessibleRoute,
|
||||
highlight, diagnostics
|
||||
```
|
||||
|
||||
下游业务端应优先通过 capabilities 判断当前地图基座是否支持对应能力。
|
||||
|
||||
## V2.2.0 基础数据能力
|
||||
|
||||
### 新增方法 (8 个)
|
||||
|
||||
| 方法 | 签名 | 说明 |
|
||||
|------|------|------|
|
||||
| `getManifest` | `getManifest()` | 获取地图 Manifest(地图元数据、楼层列表等) |
|
||||
| `loadFloorBundle` | `loadFloorBundle(floorId: string \| number)` | 加载楼层 Bundle(模型 + POI + 空间面 + 讲解点一次性拉取) |
|
||||
| `getFloorPois` | `getFloorPois(floorId: string \| number)` | 获取指定楼层的 POI 列表 |
|
||||
| `getSpaces` | `getSpaces(floorId: string \| number)` | 获取指定楼层的空间面(展厅/分区)列表 |
|
||||
| `getGuideStops` | `getGuideStops(floorId: string \| number)` | 获取指定楼层的讲解点列表 |
|
||||
| `preloadFloor` | `preloadFloor(floorId: string \| number)` | 预加载指定楼层(后台静默下载模型资源,不切换视图) |
|
||||
| `clearModelCache` | `clearModelCache()` | 清空本地模型缓存(IndexedDB / Memory) |
|
||||
| `getPerformanceStats` | `getPerformanceStats()` | 获取性能统计(FPS、DrawCall、纹理内存等) |
|
||||
|
||||
> **类型放宽**:v2.2.0 继续保持所有 ID 字段(`floorId`、`mapId`、`poiId` 等)支持 `string | number`,兼容字符串形式的业务 ID。
|
||||
|
||||
### 新增事件 (5 个)
|
||||
|
||||
| 事件名 | Payload | 说明 |
|
||||
|--------|---------|------|
|
||||
| `modelLoading` | `{ floorId, modelUrl }` | 模型文件开始加载(可用于显示 Loading UI) |
|
||||
| `modelReady` | `{ floorId, parseTimeMs }` | 模型解析完成,Three.js Scene 已挂载 |
|
||||
| `modelError` | `{ floorId, error, fallbackUsed }` | 模型加载失败,含是否使用了 fallback 回退信息 |
|
||||
| `floorBundleReady` | `{ floorId, dataVersion }` | 楼层 Bundle 数据全部就绪 |
|
||||
| `routeReady` | `{ routeId, distance, duration }` | 路径规划完成,返回距离/时长 |
|
||||
|
||||
### Draco 压缩策略
|
||||
|
||||
v2.2.0 的模型管线保持自动 Draco 压缩策略:
|
||||
|
||||
- **导入阶段**:GLB 模型导入后,服务端自动执行保守级 Draco 压缩(quantization position=14, normal=10, texcoord=12)
|
||||
- **容错机制**:若 Draco 压缩失败(如非标网格),系统自动回退使用原始 GLB 文件,不影响渲染
|
||||
- **客户端解码**:SDK 内置 Draco WASM 解码器,客户端无需任何额外配置
|
||||
- **体积收益**:典型博物馆楼层模型压缩率约 60%~75%,首屏加载时间显著缩短
|
||||
|
||||
731
static/sgs-map-sdk/index.d.ts
vendored
731
static/sgs-map-sdk/index.d.ts
vendored
@@ -1,24 +1,40 @@
|
||||
/**
|
||||
* 轻量级类型安全事件发射器
|
||||
*
|
||||
* <p>SDK 内部事件系统基类,提供 on/off/once/emit/removeAllListeners/listenerCount。
|
||||
*/
|
||||
declare class EventEmitter<Events extends Record<string, any>> {
|
||||
private events;
|
||||
constructor();
|
||||
/** 注册事件监听器 */
|
||||
on<K extends keyof Events>(event: K, listener: (payload: Events[K]) => void): void;
|
||||
/** 移除指定事件的指定监听器 */
|
||||
off<K extends keyof Events>(event: K, listener: (payload: Events[K]) => void): void;
|
||||
/** 注册一次性监听器,触发后自动移除 */
|
||||
once<K extends keyof Events>(event: K, listener: (payload: Events[K]) => void): void;
|
||||
/** 触发事件,通知所有已注册的监听器 */
|
||||
emit<K extends keyof Events>(event: K, data: Events[K]): void;
|
||||
/** 移除指定事件的全部监听器;不传参则移除所有事件的所有监听器 */
|
||||
removeAllListeners<K extends keyof Events>(event?: K): void;
|
||||
/** 返回指定事件的监听器数量 */
|
||||
listenerCount<K extends keyof Events>(event: K): number;
|
||||
}
|
||||
|
||||
/** 统一位置描述符 */
|
||||
type SgsMapNode = {
|
||||
type: 'coord';
|
||||
floorId: number;
|
||||
floorId: string | number;
|
||||
x: number;
|
||||
z: number;
|
||||
y?: number;
|
||||
} | {
|
||||
type: 'poiId';
|
||||
value: number;
|
||||
value: string | number;
|
||||
floorId?: string | number;
|
||||
} | {
|
||||
type: 'nodeName';
|
||||
value: string;
|
||||
floorId?: string | number;
|
||||
};
|
||||
/** 单点吸附详情 */
|
||||
interface SnapDetail {
|
||||
@@ -33,6 +49,8 @@ interface SnapDetail {
|
||||
distance: number;
|
||||
}
|
||||
/** 寻路结果 */
|
||||
type SgsRouteTransferType = 'ELEVATOR' | 'STAIRS' | 'ESCALATOR';
|
||||
type SgsRouteSegmentType = 'WALK' | 'TRANSFER';
|
||||
interface RouteResult {
|
||||
success: boolean;
|
||||
distance: number;
|
||||
@@ -42,25 +60,33 @@ interface RouteResult {
|
||||
y: number;
|
||||
z: number;
|
||||
}>;
|
||||
startSnap: SnapDetail;
|
||||
endSnap: SnapDetail;
|
||||
computeTimeMs: number;
|
||||
polyCount: number;
|
||||
startSnap?: SnapDetail;
|
||||
endSnap?: SnapDetail;
|
||||
computeTimeMs?: number;
|
||||
polyCount?: number;
|
||||
warnings: string[];
|
||||
errorCode?: string;
|
||||
message?: string;
|
||||
segments?: SgsRouteSegment[];
|
||||
}
|
||||
/** 寻路选项 */
|
||||
interface RouteOptions {
|
||||
mode?: 'walk' | 'accessible';
|
||||
wheelchair?: boolean;
|
||||
}
|
||||
/** SDK 初始化配置 */
|
||||
interface SGSMapSDKOptions {
|
||||
container: string | HTMLElement;
|
||||
sdkUrl?: string;
|
||||
targetOrigin?: string;
|
||||
floorId?: number;
|
||||
/** 初始楼层 ID(支持雪花 ID 字符串或数字) */
|
||||
floorId?: string | number;
|
||||
/** mapId,默认 "1" */
|
||||
mapId?: string | number;
|
||||
/** 通用命令超时(毫秒),默认 5000 */
|
||||
timeout?: number;
|
||||
/** ready 超时(毫秒),默认 15000。超过此时间 ENGINE_READY 仍未到达则 reject */
|
||||
readyTimeout?: number;
|
||||
}
|
||||
/** SDK 事件映射 */
|
||||
interface SGSMapEvents {
|
||||
@@ -69,14 +95,14 @@ interface SGSMapEvents {
|
||||
protocolVersion: number;
|
||||
};
|
||||
poiClick: {
|
||||
id: number;
|
||||
id: string | number;
|
||||
name: string;
|
||||
type: string;
|
||||
floorId?: number;
|
||||
floorId?: string | number;
|
||||
description?: string;
|
||||
};
|
||||
floorChanged: {
|
||||
floorId: number;
|
||||
floorId: string | number;
|
||||
};
|
||||
error: {
|
||||
code: string;
|
||||
@@ -86,9 +112,50 @@ interface SGSMapEvents {
|
||||
type: string;
|
||||
detail: string;
|
||||
};
|
||||
routeTransfer: {
|
||||
currentFloorId: string | number;
|
||||
targetFloorId: string | number;
|
||||
fromFloorId: string | number;
|
||||
fromNodeName: string;
|
||||
toNodeName: string;
|
||||
transferType: SgsRouteTransferType;
|
||||
connectorName?: string;
|
||||
segmentIndex: number;
|
||||
totalSegments: number;
|
||||
routeId?: string;
|
||||
};
|
||||
/** 模型开始加载 */
|
||||
modelLoading: {
|
||||
floorId: string | number;
|
||||
modelUrl: string;
|
||||
};
|
||||
/** 模型解析完成 */
|
||||
modelReady: {
|
||||
floorId: string | number;
|
||||
parseTimeMs: number;
|
||||
};
|
||||
/** 模型加载失败(含是否使用了 fallback) */
|
||||
modelError: {
|
||||
floorId: string | number;
|
||||
error: string;
|
||||
fallbackUsed: boolean;
|
||||
};
|
||||
/** 楼层 Bundle 数据加载完成 */
|
||||
floorBundleReady: {
|
||||
floorId: string | number;
|
||||
dataVersion: string;
|
||||
};
|
||||
/** 路径规划完成 */
|
||||
routeReady: {
|
||||
routeId: string;
|
||||
distance: number;
|
||||
duration: number;
|
||||
segmentCount?: number;
|
||||
hasTransfer?: boolean;
|
||||
};
|
||||
}
|
||||
/** SDK 错误码 */
|
||||
type SGSErrorCode = 'ERR_TIMEOUT' | 'ERR_NO_PATH' | 'ERR_FLOOR_NOT_FOUND' | 'ERR_NOT_READY' | 'ERR_POI_NOT_FOUND' | 'ERR_NETWORK' | 'ERR_IFRAME_DEAD' | 'ERR_UNAUTHORIZED' | 'ERR_UNSUPPORTED' | 'ERR_FAILED';
|
||||
type SGSErrorCode = 'ERR_TIMEOUT' | 'ERR_NO_PATH' | 'ERR_FLOOR_NOT_FOUND' | 'ERR_NOT_READY' | 'ERR_POI_NOT_FOUND' | 'ERR_NETWORK' | 'ERR_IFRAME_DEAD' | 'ERR_UNAUTHORIZED' | 'ERR_UNSUPPORTED' | 'ERR_FAILED' | 'ERR_DESTROYED' | 'ERR_IFRAME_LOAD' | 'ERR_READY_TIMEOUT';
|
||||
/** 图钉配置参数 */
|
||||
interface MarkerConfig {
|
||||
id: string;
|
||||
@@ -101,43 +168,673 @@ interface MarkerConfig {
|
||||
z: number;
|
||||
};
|
||||
}
|
||||
/** 地图发布清单 — 对应 GET /app-api/gis/sdk/maps/{mapId}/manifest */
|
||||
interface SgsMapManifest {
|
||||
/** 地图 ID(数据库主键,number) */
|
||||
mapId: number;
|
||||
/** 地图业务标识码 */
|
||||
mapCode: string;
|
||||
/** 发布 ID,全局唯一 */
|
||||
releaseId: string;
|
||||
/** 语义化版本号 */
|
||||
version: string;
|
||||
/** 发布时间 ISO8601 */
|
||||
releasedAt: string;
|
||||
/** 建筑名称 */
|
||||
buildingName: string;
|
||||
/** 坐标系标识,固定 "GLB_METER" */
|
||||
coordinateSystem: string;
|
||||
/** 建筑外观模型资源 */
|
||||
exterior: SgsModelResource;
|
||||
/** 楼层资源列表(按 sortOrder 升序) */
|
||||
floors: SgsFloorResource[];
|
||||
/** 楼层总数 */
|
||||
totalFloors: number;
|
||||
/** 所有资源总大小(字节) */
|
||||
totalSizeBytes: number;
|
||||
}
|
||||
/** 模型资源描述 — 对应 GET /app-api/gis/sdk/maps/{mapId}/resources 中的单个资源 */
|
||||
interface SgsModelResource {
|
||||
/** 楼层代码(如 "L1"/"EXTERIOR") */
|
||||
floorCode: string;
|
||||
/** 楼层名称 */
|
||||
floorName?: string;
|
||||
/** 模型分组(通常与 floorCode 相同) */
|
||||
modelGroup: string;
|
||||
/** 模型文件 URL(相对路径) */
|
||||
modelUrl: string;
|
||||
/** 文件 SHA256 哈希(MVP 阶段为 mock-sha256-{filename}) */
|
||||
hash: string;
|
||||
/** 文件大小(字节) */
|
||||
sizeBytes: number;
|
||||
/** 3D 节点数量 */
|
||||
nodeCount?: number;
|
||||
/** 材质数量 */
|
||||
materialCount?: number;
|
||||
/** 贴图数量 */
|
||||
imageCount?: number;
|
||||
/** 排序序号 */
|
||||
sortOrder?: number;
|
||||
}
|
||||
/** 楼层资源(继承 SgsModelResource,必填 floorName + sortOrder) */
|
||||
interface SgsFloorResource extends SgsModelResource {
|
||||
floorName: string;
|
||||
sortOrder: number;
|
||||
}
|
||||
/** 图层树节点 — 对应 GET /app-api/gis/sdk/maps/{mapId}/layer-tree */
|
||||
interface SgsLayerTreeNode {
|
||||
/** 节点 ID */
|
||||
id: number | string;
|
||||
/** 节点名称 */
|
||||
name: string;
|
||||
/** 节点类型 */
|
||||
nodeType: 'BUILDING' | 'FLOOR' | 'SPACE' | 'ZONE' | 'POI_GROUP';
|
||||
/** 楼层代码(FLOOR 节点必填) */
|
||||
floorCode?: string;
|
||||
/** 空间功能分类 */
|
||||
spaceCategory?: string;
|
||||
/** 子节点 */
|
||||
children?: SgsLayerTreeNode[];
|
||||
/** 是否可见 */
|
||||
visible?: boolean;
|
||||
/** 包含的 3D 节点数量 */
|
||||
nodeCount?: number;
|
||||
}
|
||||
/** SDK 消费的 POI — 对应 GET /app-api/gis/sdk/floors/{floorId}/pois */
|
||||
interface SgsPoi {
|
||||
/** POI ID(雪花 ID,字符串或数字均可) */
|
||||
id: string | number;
|
||||
/** POI 名称 */
|
||||
name: string;
|
||||
/** POI 类型代码(如 TOILET/ELEVATOR/STAIRS/ENTRANCE) */
|
||||
type: string;
|
||||
/** POI 类型中文名 */
|
||||
typeName: string;
|
||||
/** 所在楼层代码 */
|
||||
floorCode: string;
|
||||
/** GLB 坐标(米),水平面 x/z,高度 y */
|
||||
position: {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
};
|
||||
/** 状态 */
|
||||
status: 'ACTIVE' | 'INACTIVE';
|
||||
/** 锚定的 GLB 3D 节点名(可选,用于精准定位) */
|
||||
anchorNodeName?: string | null;
|
||||
/** 描述 */
|
||||
description?: string;
|
||||
/** 图标 URL */
|
||||
iconUrl?: string;
|
||||
}
|
||||
/** 路径规划结果 — 对应 POST /app-api/gis/sdk/routes/plan */
|
||||
interface SgsRoutePlanV2 {
|
||||
/** 是否成功 */
|
||||
success: boolean;
|
||||
/** 总距离(米) */
|
||||
totalDistance: number;
|
||||
/** 总时长(秒) */
|
||||
totalDuration: number;
|
||||
/** 路线分段列表 */
|
||||
segments: SgsRouteSegment[];
|
||||
/** 警告信息 */
|
||||
warnings: string[];
|
||||
}
|
||||
/** 路线分段 — WALK 段在单楼层内,TRANSFER 段通过 ELEVATOR/STAIRS/ESCALATOR 换层 */
|
||||
interface SgsRouteSegment {
|
||||
/** 分段索引(从 0 开始) */
|
||||
segmentIndex: number;
|
||||
/** 所在楼层 ID(TRANSFER 段可能为空或记录起始楼层) */
|
||||
floorId?: string | number | null;
|
||||
/** 所在楼层代码(换乘段为 "TRANSFER") */
|
||||
floorCode: string;
|
||||
/** 楼层名称 */
|
||||
floorName: string;
|
||||
/** 标准分段类型 */
|
||||
segmentType?: SgsRouteSegmentType;
|
||||
/** 分段类型 */
|
||||
type: 'WALK' | SgsRouteTransferType;
|
||||
/** 换乘类型,WALK 段为 WALK,TRANSFER 段为 ELEVATOR/STAIRS/ESCALATOR */
|
||||
transferType?: 'WALK' | SgsRouteTransferType;
|
||||
/** 距离(米) */
|
||||
distance: number;
|
||||
/** 时长(秒) */
|
||||
duration: number;
|
||||
/** 路径点序列 */
|
||||
pathPoints: Array<{
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
}>;
|
||||
/** 路径点序列兼容字段 */
|
||||
points?: Array<{
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
}>;
|
||||
/** 起始节点名 */
|
||||
fromNodeName?: string | null;
|
||||
/** 终止节点名 */
|
||||
toNodeName?: string | null;
|
||||
/** 起始节点名兼容字段 */
|
||||
startNodeName?: string | null;
|
||||
/** 终止节点名兼容字段 */
|
||||
endNodeName?: string | null;
|
||||
/** 跨楼层通道名称 */
|
||||
connectorName?: string | null;
|
||||
}
|
||||
/** v2.2.0 Manifest — GET /app-api/gis/sdk/maps/{mapId}/manifest */
|
||||
interface SgsSdkManifest {
|
||||
/** 地图 ID */
|
||||
mapId: string | number;
|
||||
/** 地图名称 */
|
||||
mapName: string;
|
||||
/** SDK 协议版本 */
|
||||
sdkVersion: string;
|
||||
/** 协议版本号 */
|
||||
protocolVersion: number;
|
||||
/** 数据版本(ISO8601 或递增序号) */
|
||||
dataVersion: string;
|
||||
/** 最后更新时间 ISO8601 */
|
||||
updatedAt: string;
|
||||
/** 坐标系标识,固定 "GLB_METER" */
|
||||
coordinateSystem: string;
|
||||
/** 楼层摘要列表(按 sortOrder 升序) */
|
||||
floors: SgsSdkFloorSummary[];
|
||||
/** 能力声明 */
|
||||
capabilities: {
|
||||
/** 是否支持 Draco 压缩模型 */
|
||||
dracoCompression: boolean;
|
||||
/** 是否支持路径规划 */
|
||||
routePlanning: boolean;
|
||||
/** 是否支持空间面查询 */
|
||||
spatialQuery: boolean;
|
||||
/** 是否支持讲解点 */
|
||||
guideStops: boolean;
|
||||
/** 是否支持地图加载 */
|
||||
mapLoading?: boolean;
|
||||
/** 是否支持楼层切换 */
|
||||
floorSwitching?: boolean;
|
||||
/** 是否支持 POI 查询 */
|
||||
poiQuery?: boolean;
|
||||
/** 是否支持空间查询 */
|
||||
spaceQuery?: boolean;
|
||||
/** 是否支持可导航目的地查询 */
|
||||
navigablePlaces?: boolean;
|
||||
/** 是否支持跨楼层路径规划 */
|
||||
crossFloorRoute?: boolean;
|
||||
/** 是否支持无障碍路径 */
|
||||
accessibleRoute?: boolean;
|
||||
/** 是否支持高亮功能 */
|
||||
highlight?: boolean;
|
||||
/** 是否支持诊断接口 */
|
||||
diagnostics?: boolean;
|
||||
};
|
||||
}
|
||||
/** v2.2.0 楼层摘要(manifest 内嵌) */
|
||||
interface SgsSdkFloorSummary {
|
||||
/** 楼层 ID */
|
||||
floorId: string | number;
|
||||
/** 楼层代码(如 L1、L-2) */
|
||||
floorCode: string;
|
||||
/** 楼层名称 */
|
||||
floorName: string;
|
||||
/** 排序序号 */
|
||||
sortOrder: number;
|
||||
/** 模型文件大小(字节) */
|
||||
modelSizeBytes: number;
|
||||
/** 该楼层 POI 数量 */
|
||||
poiCount: number;
|
||||
/** 该楼层空间面数量 */
|
||||
spaceCount: number;
|
||||
}
|
||||
/** v2.2.0 楼层 Bundle — GET /app-api/gis/sdk/floors/{floorId}/bundle */
|
||||
interface SgsFloorBundle {
|
||||
/** 楼层基础信息 */
|
||||
floor: {
|
||||
floorId: string | number;
|
||||
floorCode: string;
|
||||
floorName: string;
|
||||
sortOrder: number;
|
||||
};
|
||||
/** 模型资源信息 */
|
||||
model: {
|
||||
/** Draco 压缩模型 URL(浏览器可直接访问) */
|
||||
modelUrl: string;
|
||||
/**
|
||||
* 原始 GLB URL(未压缩版),压缩失败时自动回退到此 URL。
|
||||
* 保障发布和 H5 SDK 可用性不受 Draco 压缩状态影响。
|
||||
*/
|
||||
fallbackModelUrl: string;
|
||||
/** 压缩类型:'draco' | 'none' */
|
||||
compressionType: 'draco' | 'none';
|
||||
/** 模型版本号 */
|
||||
modelVersion: string;
|
||||
/** 3D 节点数量 */
|
||||
nodeCount: number;
|
||||
/** 压缩后文件大小(字节) */
|
||||
sizeBytes: number;
|
||||
/** 原始文件大小(字节) */
|
||||
originSizeBytes: number;
|
||||
};
|
||||
/** 该楼层的 POI 列表 */
|
||||
pois: SgsPoi[];
|
||||
/** 该楼层的空间面列表 */
|
||||
spaces: SgsSpaceArea[];
|
||||
/** 该楼层的讲解点列表 */
|
||||
guideStops: SgsGuideStop[];
|
||||
/** 路网概览 */
|
||||
routeSummary: {
|
||||
/** 是否有路网数据 */
|
||||
hasRouteNetwork: boolean;
|
||||
/** 路网节点数 */
|
||||
nodeCount: number;
|
||||
/** 路网边数 */
|
||||
edgeCount: number;
|
||||
};
|
||||
/** 需要在前端隐藏的 3D 场景节点名列表 */
|
||||
hiddenSceneNodeNames: string[];
|
||||
/** 数据版本 */
|
||||
dataVersion: string;
|
||||
/** 最后更新时间 ISO8601 */
|
||||
updatedAt: string;
|
||||
}
|
||||
/** v2.2.0 空间面 — GET /app-api/gis/sdk/floors/{floorId}/spaces */
|
||||
interface SgsSpaceArea {
|
||||
/** 空间面 ID */
|
||||
id: string | number;
|
||||
/** 显示名称 */
|
||||
name: string;
|
||||
/** 空间类型(如 exhibition_hall / corridor / office) */
|
||||
type: string;
|
||||
/** 所属楼层 ID */
|
||||
floorId: string | number;
|
||||
/** WKT 边界多边形 */
|
||||
boundaryWkt: string;
|
||||
/** 中心点 GLB 坐标(米) */
|
||||
center: {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
};
|
||||
/** 关联的 3D 场景节点名(可选) */
|
||||
sourceNodeName?: string;
|
||||
/** 状态 */
|
||||
status: 'ACTIVE' | 'INACTIVE';
|
||||
/** 颜色(十六进制,如 #0097A9) */
|
||||
colorHex?: string;
|
||||
}
|
||||
/** v2.2.0 讲解点 — 已标定、可在地图定位的导览讲解点 */
|
||||
interface SgsGuideStop {
|
||||
/** 讲解点 ID */
|
||||
id: string | number;
|
||||
/** 讲解点名称 */
|
||||
name: string;
|
||||
/** 讲解点类型 */
|
||||
type: string;
|
||||
/** 讲解点类型名称 */
|
||||
typeName?: string;
|
||||
/** 所属楼层 ID */
|
||||
floorId: string | number;
|
||||
/** GLB 坐标(米) */
|
||||
position: {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
};
|
||||
/** 目标类型(如 EXHIBIT / SCENE / INTERACTIVE) */
|
||||
targetType: string;
|
||||
/** 关联的目标 ID(如展品 ID) */
|
||||
targetId?: string | number;
|
||||
/** 语音导览音频 URL */
|
||||
audioUrl?: string;
|
||||
/** 封面图片 URL */
|
||||
coverImageUrl?: string;
|
||||
/** 描述 */
|
||||
description?: string;
|
||||
/** 状态 */
|
||||
status: 'ACTIVE' | 'INACTIVE';
|
||||
/** 是否已完成地图标定 */
|
||||
located?: boolean;
|
||||
/** 是否有可播放音频 */
|
||||
hasAudio?: boolean;
|
||||
/** 关联 POI ID */
|
||||
poiId?: string | number;
|
||||
/** 关联大纲 ID */
|
||||
outlineId?: string | number;
|
||||
/** 关联大纲名称 */
|
||||
outlineName?: string;
|
||||
/** 所属展厅 ID */
|
||||
hallId?: string | number;
|
||||
/** 所属展厅名称 */
|
||||
hallName?: string;
|
||||
/** 讲解点排序 */
|
||||
sort?: number;
|
||||
/** 所属路线 ID */
|
||||
routeId?: string | number;
|
||||
/** 所属路线名称 */
|
||||
routeName?: string;
|
||||
/** 路线中的顺序 */
|
||||
seqOrder?: number;
|
||||
/** 建议停留时长(分钟) */
|
||||
stayMinutes?: number;
|
||||
}
|
||||
/** v2.2.0 SDK 错误响应 */
|
||||
interface SgsSdkError {
|
||||
/** 错误码(如 ERR_TIMEOUT / ERR_NETWORK) */
|
||||
code: string;
|
||||
/** 人类可读的错误消息 */
|
||||
message: string;
|
||||
/** 额外错误详情(可选) */
|
||||
details?: Record<string, any>;
|
||||
}
|
||||
/** v2.2.0 性能统计 */
|
||||
interface SgsPerformanceStats {
|
||||
/** SDK 实例创建时间戳(绝对时间 ms) */
|
||||
sdkCreatedAt?: number;
|
||||
/** iframe DOM 加载完成耗时(从 SDK 创建起算 ms) */
|
||||
iframeDomReadyMs?: number;
|
||||
/** 地图引擎就绪耗时(从 SDK 创建起算 ms) */
|
||||
engineReadyMs?: number;
|
||||
/** Manifest 加载耗时(ms) */
|
||||
manifestLoadMs?: number;
|
||||
/** Bundle 加载耗时(ms) */
|
||||
bundleLoadMs?: number;
|
||||
/** 模型文件下载耗时(ms) */
|
||||
modelDownloadMs?: number;
|
||||
/** 模型文件下载与解析总耗时(ms) */
|
||||
modelDownloadAndParseMs?: number;
|
||||
/** 模型解析耗时(ms) */
|
||||
modelParseMs?: number;
|
||||
/** 首帧渲染耗时(ms) */
|
||||
firstRenderMs?: number;
|
||||
/** 路径规划耗时(ms) */
|
||||
routePlanMs?: number;
|
||||
/** 使用了 Draco 压缩 */
|
||||
dracoUsed?: boolean;
|
||||
/** 是否使用了 fallback 模型 */
|
||||
fallbackUsed?: boolean;
|
||||
/** 当前楼层 ID */
|
||||
currentFloorId?: string | number;
|
||||
}
|
||||
interface SgsNavigablePlace {
|
||||
id: string | number;
|
||||
name: string;
|
||||
category: "door" | "poi" | "space" | "guide_stop";
|
||||
floorId: string | number;
|
||||
floorCode: string;
|
||||
floorName?: string;
|
||||
x: number;
|
||||
z: number;
|
||||
nodeId?: string | number;
|
||||
ownerName?: string;
|
||||
typeCode?: string;
|
||||
typeName?: string;
|
||||
}
|
||||
/** v2.3.0 地图级诊断响应 — GET /app-api/gis/sdk/maps/{mapId}/diagnostics */
|
||||
interface SgsMapDiagnostics {
|
||||
/** 地图 ID */
|
||||
mapId: string | number;
|
||||
/** 地图名称 */
|
||||
mapName: string;
|
||||
/** SDK 版本号 */
|
||||
sdkVersion: string;
|
||||
/** 整体状态 */
|
||||
status: 'OK' | 'WARN' | 'ERROR';
|
||||
/** 楼层诊断摘要列表 */
|
||||
floors: SgsFloorDiagnosticsSummary[];
|
||||
/** 汇总统计 */
|
||||
summary: {
|
||||
/** 楼层总数 */
|
||||
floorCount: number;
|
||||
/** 模型就绪楼层数 */
|
||||
modelReadyFloorCount: number;
|
||||
/** POI 总数 */
|
||||
poiCount: number;
|
||||
/** 空间面总数 */
|
||||
spaceCount: number;
|
||||
/** 讲解点总数 */
|
||||
guideStopCount: number;
|
||||
/** 可导航目的地总数 */
|
||||
navigablePlaceCount: number;
|
||||
/** 路网节点总数 */
|
||||
routeNodeCount: number;
|
||||
/** 路网边总数 */
|
||||
routeEdgeCount: number;
|
||||
};
|
||||
/** 警告信息列表 */
|
||||
warnings: string[];
|
||||
}
|
||||
/** v2.3.0 楼层诊断摘要(地图级诊断内嵌) */
|
||||
interface SgsFloorDiagnosticsSummary {
|
||||
/** 楼层 ID */
|
||||
floorId: string | number;
|
||||
/** 楼层代码 */
|
||||
floorCode: string;
|
||||
/** 楼层名称 */
|
||||
floorName: string;
|
||||
/** 楼层状态 */
|
||||
status: 'OK' | 'WARN' | 'ERROR';
|
||||
/** 模型是否就绪 */
|
||||
modelReady: boolean;
|
||||
/** 路径规划是否就绪 */
|
||||
routePlanningReady: boolean;
|
||||
/** 可导航目的地数量 */
|
||||
navigablePlaceCount: number;
|
||||
}
|
||||
/** v2.3.0 楼层级诊断响应 — GET /app-api/gis/sdk/floors/{floorId}/diagnostics */
|
||||
interface SgsFloorDiagnostics {
|
||||
/** 楼层 ID */
|
||||
floorId: string | number;
|
||||
/** 楼层代码 */
|
||||
floorCode: string;
|
||||
/** 楼层名称 */
|
||||
floorName: string;
|
||||
/** 楼层状态 */
|
||||
status: 'OK' | 'WARN' | 'ERROR';
|
||||
/** 模型是否就绪 */
|
||||
modelReady: boolean;
|
||||
/** POI 数量 */
|
||||
poiCount: number;
|
||||
/** 空间面数量 */
|
||||
spaceCount: number;
|
||||
/** 讲解点数量 */
|
||||
guideStopCount: number;
|
||||
/** 可导航目的地数量 */
|
||||
navigablePlaceCount: number;
|
||||
/** 路网节点数量 */
|
||||
routeNodeCount: number;
|
||||
/** 路网边数量 */
|
||||
routeEdgeCount: number;
|
||||
/** 路径规划是否就绪 */
|
||||
routePlanningReady: boolean;
|
||||
/** 警告信息列表 */
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* SGS 3D 地图 H5 SDK 入口类
|
||||
*
|
||||
* <p>通过 iframe 嵌入 /h5-sdk 底座页面,以 postMessage 进行双向通信。
|
||||
* <p>所有需要后端数据的操作均通过 postMessage 指令由底座代理,
|
||||
* Demo / 宿主不允许直连后端 API。
|
||||
*
|
||||
* @version 2.3.0
|
||||
*/
|
||||
declare class SGSMapSDK extends EventEmitter<SGSMapEvents> {
|
||||
private container;
|
||||
private sdkUrl;
|
||||
private floorId;
|
||||
private mapId;
|
||||
private targetOrigin;
|
||||
private iframe;
|
||||
private isReady;
|
||||
private _isReady;
|
||||
private _isDestroyed;
|
||||
/** ready 失败终态:iframe 加载失败或 ready 超时后置 true,后续命令直接 reject */
|
||||
private _readyFailed;
|
||||
/** ready 失败时的错误对象,用于后续命令的立即 reject */
|
||||
private _readyFailedError;
|
||||
private defaultTimeout;
|
||||
private readyTimeoutMs;
|
||||
/** 已 ready 后正在等待响应的命令队列 */
|
||||
private promiseQueue;
|
||||
/** ready 前暂存的异步命令队列 */
|
||||
private _pendingCommands;
|
||||
/** ready 前暂存的 fire-and-forget 命令队列 */
|
||||
private _pendingFireAndForget;
|
||||
private _messageListener;
|
||||
/** ready 超时计时器 */
|
||||
private _readyTimer;
|
||||
/** ENGINE_READY 返回的 payload(供外部读取) */
|
||||
readyPayload: any;
|
||||
/** ready Promise 的 resolve/reject,保证 whenReady() 返回同一个 Promise */
|
||||
private _readyResolve;
|
||||
private _readyReject;
|
||||
private _readyPromise;
|
||||
/** 性能追踪字段 */
|
||||
private _sdkCreatedAt;
|
||||
private _iframeDomReadyMs;
|
||||
private _engineReadyMs;
|
||||
constructor(options: SGSMapSDKOptions);
|
||||
private _initIframeBridge;
|
||||
/**
|
||||
* ENGINE_READY 到达时的处理
|
||||
*/
|
||||
private _onEngineReady;
|
||||
/**
|
||||
* reject ready Promise 并清空所有挂起命令,设置失败终态
|
||||
*/
|
||||
private _rejectReady;
|
||||
/**
|
||||
* 排空 ready 前暂存的命令队列 — 逐个以 _postCommandAsync 发送
|
||||
*/
|
||||
private _flushPendingCommands;
|
||||
/**
|
||||
* reject 所有挂起的 Promise 和命令队列
|
||||
*/
|
||||
private _rejectAllPending;
|
||||
/**
|
||||
* 返回 ready Promise(全局唯一,多次调用返回同一个)。
|
||||
* 别名:ready()
|
||||
*
|
||||
* @example
|
||||
* await sdk.whenReady();
|
||||
* sdk.changeFloor(floorId);
|
||||
*/
|
||||
whenReady(): Promise<any>;
|
||||
/**
|
||||
* whenReady 的别名
|
||||
*/
|
||||
ready(): Promise<any>;
|
||||
/** 获取 ready 状态 */
|
||||
getIsReady(): boolean;
|
||||
/** 获取当前楼层 ID */
|
||||
getCurrentFloor(): string | number;
|
||||
/** 获取当前 mapId */
|
||||
getMapId(): string | number;
|
||||
/** 获取 SDK 版本号 */
|
||||
getVersion(): string;
|
||||
private _generateRequestId;
|
||||
private _postCommand;
|
||||
/**
|
||||
* 异步命令发送(外部入口)— 支持命令队列
|
||||
*
|
||||
* <p>状态机优先级:destroyed > readyFailed > !ready(入队) > ready(发送)
|
||||
*/
|
||||
private _postCommandAsync;
|
||||
getIsReady(): boolean;
|
||||
getCurrentFloor(): number;
|
||||
/**
|
||||
* fire-and-forget 命令发送(void 方法统一入口)
|
||||
*
|
||||
* <p>受 destroyed/readyFailed/!ready 状态保护:
|
||||
* - destroyed/readyFailed:静默丢弃 + console.warn
|
||||
* - !ready:暂存到 _pendingFireAndForget,ready 后自动发送
|
||||
* - ready:立即发送
|
||||
*/
|
||||
private _postCommandSafe;
|
||||
/**
|
||||
* 异步命令发送(内部实现)— 已确认 ready
|
||||
*/
|
||||
private _postCommandAsyncInner;
|
||||
/** 聚焦到指定要素节点 */
|
||||
focusTo(node: SgsMapNode | string): void;
|
||||
changeFloor(floorId: number, timeout?: number): Promise<{
|
||||
/** 切换楼层 */
|
||||
changeFloor(floorId: string | number, timeout?: number): Promise<{
|
||||
success: boolean;
|
||||
floorId: number;
|
||||
floorId: string | number;
|
||||
}>;
|
||||
/** 添加业务图钉 */
|
||||
addMarker(config: MarkerConfig): void;
|
||||
/** 移除指定图钉 */
|
||||
removeMarker(markerId: string): void;
|
||||
/** 清空所有业务图钉 */
|
||||
clearMarkers(): void;
|
||||
/** 高亮多边形轮廓 */
|
||||
highlightPolygon(nodeName: string): void;
|
||||
/** 寻路规划(v1 兼容) */
|
||||
planRoute(from: SgsMapNode | string, to: SgsMapNode | string, options?: any, timeout?: number): Promise<RouteResult>;
|
||||
/** 寻路规划 V2(结构化参数) */
|
||||
planRouteV2(from: SgsMapNode, to: SgsMapNode, options?: any, timeout?: number): Promise<RouteResult>;
|
||||
/** 清除当前路线 */
|
||||
clearRoute(): void;
|
||||
/** 获取引擎状态 */
|
||||
getState(): Promise<any>;
|
||||
/** 重置视角 */
|
||||
resetView(): void;
|
||||
/** 设置 2D/3D 视图模式 */
|
||||
setViewMode(mode: '2d' | '3d'): void;
|
||||
/** 切换图层可见性 */
|
||||
toggleLayer(types: string[], visible: boolean, timeout?: number): Promise<{
|
||||
success: boolean;
|
||||
visibleTypes: string[];
|
||||
}>;
|
||||
/** 设置可见图层类型列表 */
|
||||
setVisibleLayers(types: string[], timeout?: number): Promise<{
|
||||
success: boolean;
|
||||
visibleTypes: string[];
|
||||
}>;
|
||||
/** 获取地图 Manifest 清单 */
|
||||
getManifest(timeout?: number): Promise<SgsSdkManifest>;
|
||||
/** 加载指定楼层的完整 Bundle 数据(含 POI/空间面/讲解点/路网) */
|
||||
loadFloorBundle(floorId: string | number, timeout?: number): Promise<SgsFloorBundle>;
|
||||
/** 获取指定楼层的 POI 列表 */
|
||||
getFloorPois(floorId: string | number, timeout?: number): Promise<SgsPoi[]>;
|
||||
/** 获取指定楼层的起终点候选目的地列表(门点/POI/空间等吸附后目的地) */
|
||||
getNavigablePlaces(floorId: string | number, timeout?: number): Promise<SgsNavigablePlace[]>;
|
||||
/** 获取当前楼层的 POI 列表(根据 floorCode 参数) */
|
||||
getPois(floorCode: string, timeout?: number): Promise<SgsPoi[]>;
|
||||
/** 获取指定楼层的空间面列表 */
|
||||
getSpaces(floorId: string | number, timeout?: number): Promise<SgsSpaceArea[]>;
|
||||
/** 获取指定楼层的已标定讲解点列表(地图渲染入口) */
|
||||
getGuideStops(floorId: string | number, timeout?: number): Promise<SgsGuideStop[]>;
|
||||
/** 获取指定展厅的已标定讲解点列表(业务导览入口) */
|
||||
getGuideStopsByHall(hallId: string | number, timeout?: number): Promise<SgsGuideStop[]>;
|
||||
/** 预加载指定楼层的模型资源(不渲染) */
|
||||
preloadFloor(floorId: string | number, timeout?: number): Promise<{
|
||||
cached: boolean;
|
||||
}>;
|
||||
/** 获取性能统计信息 */
|
||||
getPerformanceStats(timeout?: number): Promise<SgsPerformanceStats>;
|
||||
/** 获取地图级诊断信息 */
|
||||
getDiagnostics(timeout?: number): Promise<SgsMapDiagnostics>;
|
||||
/** 获取楼层级诊断信息 */
|
||||
getFloorDiagnostics(floorId: string | number, timeout?: number): Promise<SgsFloorDiagnostics>;
|
||||
/** 清空客户端模型缓存 */
|
||||
clearModelCache(): void;
|
||||
/** @deprecated 使用 getManifest() 替代 */
|
||||
getReleaseManifest(timeout?: number): Promise<any>;
|
||||
/** @deprecated 已废弃,无替代方法 */
|
||||
getModelResources(timeout?: number): Promise<any>;
|
||||
/** @deprecated 已废弃,无替代方法 */
|
||||
getLayerTree(timeout?: number): Promise<any>;
|
||||
/** @deprecated 已废弃,无替代方法 */
|
||||
loadMockReleaseRoute(timeout?: number): Promise<any>;
|
||||
/**
|
||||
* 销毁 SDK 实例
|
||||
*
|
||||
* <p>清理 iframe、移除事件监听器、reject 所有挂起的 Promise。
|
||||
* <p>销毁后调用任何命令方法都会 reject ERR_DESTROYED。
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
export { type MarkerConfig, type RouteOptions, type RouteResult, type SGSErrorCode, type SGSMapEvents, type SGSMapSDKOptions, type SgsMapNode, type SnapDetail, SGSMapSDK as default };
|
||||
export { type MarkerConfig, type RouteOptions, type RouteResult, type SGSErrorCode, type SGSMapEvents, type SGSMapSDKOptions, type SgsFloorBundle, type SgsFloorDiagnostics, type SgsFloorDiagnosticsSummary, type SgsFloorResource, type SgsGuideStop, type SgsLayerTreeNode, type SgsMapDiagnostics, type SgsMapManifest, type SgsMapNode, type SgsModelResource, type SgsNavigablePlace, type SgsPerformanceStats, type SgsPoi, type SgsRoutePlanV2, type SgsRouteSegment, type SgsRouteSegmentType, type SgsRouteTransferType, type SgsSdkError, type SgsSdkFloorSummary, type SgsSdkManifest, type SgsSpaceArea, type SnapDetail, SGSMapSDK as default };
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,22 +1,23 @@
|
||||
{
|
||||
"name": "@sgs/map-sdk",
|
||||
"version": "2.0.0",
|
||||
"version": "2.3.0",
|
||||
"description": "SGS Map SDK",
|
||||
"main": "./dist/index.mjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"main": "./index.global.js",
|
||||
"types": "./index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts"
|
||||
"types": "./index.d.ts",
|
||||
"default": "./index.global.js"
|
||||
}
|
||||
},
|
||||
"sideEffects": true,
|
||||
"files": [
|
||||
"dist",
|
||||
"example",
|
||||
"index.global.js",
|
||||
"index.global.js.map",
|
||||
"index.d.ts",
|
||||
"README.md",
|
||||
"sdk-quickstart.md",
|
||||
"sdk-api-reference.md",
|
||||
"sdk-protocol.md"
|
||||
],
|
||||
"devDependencies": {},
|
||||
|
||||
662
static/sgs-map-sdk/sdk-api-reference.md
Normal file
662
static/sgs-map-sdk/sdk-api-reference.md
Normal file
@@ -0,0 +1,662 @@
|
||||
# SGS Map SDK API Reference
|
||||
|
||||
> 版本: V2.3.0
|
||||
> 适用对象: 第三方 H5、大屏、Kiosk、业务前端开发人员
|
||||
|
||||
本文档按公开 SDK 方法组织,说明初始化参数、事件、常用 API、返回值和错误处理。快速接入请先阅读 `sdk-quickstart.md`,底层通信协议请阅读 `sdk-protocol.md`。
|
||||
|
||||
## 1. 接入入口
|
||||
|
||||
### Script 标签
|
||||
|
||||
```html
|
||||
<script src="./dist/index.global.js"></script>
|
||||
<script>
|
||||
const map = new SGSMapSDK({
|
||||
container: 'map-container',
|
||||
sdkUrl: 'https://map.example.com/h5-sdk',
|
||||
targetOrigin: 'https://map.example.com',
|
||||
floorId: 1
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
如果 SDK 已随 `sgs-frontend-map` 发布到静态目录:
|
||||
|
||||
```html
|
||||
<script src="/sdk/sgs-map-sdk.min.js?v=2.3.0"></script>
|
||||
```
|
||||
|
||||
### ESM
|
||||
|
||||
```ts
|
||||
import SGSMapSDK from './dist/index.mjs';
|
||||
|
||||
const map = new SGSMapSDK({
|
||||
container: document.getElementById('map-container')!,
|
||||
sdkUrl: 'https://map.example.com/h5-sdk',
|
||||
targetOrigin: 'https://map.example.com',
|
||||
floorId: 1
|
||||
});
|
||||
```
|
||||
|
||||
## 2. 初始化配置
|
||||
|
||||
```ts
|
||||
interface SGSMapSDKOptions {
|
||||
container: string | HTMLElement;
|
||||
sdkUrl?: string;
|
||||
targetOrigin?: string;
|
||||
floorId?: string | number;
|
||||
mapId?: string | number;
|
||||
timeout?: number;
|
||||
readyTimeout?: number;
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 必填 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `container` | 是 | 无 | 地图 iframe 挂载容器,可传 DOM id 或 HTMLElement |
|
||||
| `sdkUrl` | 否 | `/h5-sdk` | H5 SDK 渲染基座地址 |
|
||||
| `targetOrigin` | 否 | 从 `sdkUrl` 推导 | `postMessage` 安全通信域,生产环境建议显式填写 |
|
||||
| `floorId` | 否 | `1` | 初始楼层 ID,支持字符串或数字 |
|
||||
| `mapId` | 否 | `1` | 地图 ID,当前由 SDK 实例保存 |
|
||||
| `timeout` | 否 | `5000` | 普通命令超时时间,单位 ms |
|
||||
| `readyTimeout` | 否 | `15000` | 等待 `ENGINE_READY` 的最大时间,单位 ms |
|
||||
|
||||
生产环境必须保证 `sdkUrl` 可访问,并且 `targetOrigin` 与 `sdkUrl` 的 Origin 一致。
|
||||
|
||||
## 3. 生命周期
|
||||
|
||||
### `whenReady(): Promise<any>`
|
||||
|
||||
等待地图引擎 ready。所有需要基座响应的 API 建议在 ready 后调用。
|
||||
|
||||
```ts
|
||||
await map.whenReady();
|
||||
await map.changeFloor(2);
|
||||
```
|
||||
|
||||
### `ready(): Promise<any>`
|
||||
|
||||
`whenReady()` 的别名。
|
||||
|
||||
### `getIsReady(): boolean`
|
||||
|
||||
返回 SDK 是否已收到基座 `ENGINE_READY`。
|
||||
|
||||
### `destroy(): void`
|
||||
|
||||
销毁 SDK 实例,清理 iframe、事件监听和所有挂起 Promise。销毁后再调用异步命令会返回 `ERR_DESTROYED`。
|
||||
|
||||
```ts
|
||||
window.addEventListener('beforeunload', () => {
|
||||
map.destroy();
|
||||
});
|
||||
```
|
||||
|
||||
## 4. 状态查询
|
||||
|
||||
### `getCurrentFloor(): string | number`
|
||||
|
||||
返回 SDK 当前记录的楼层 ID。
|
||||
|
||||
### `getMapId(): string | number`
|
||||
|
||||
返回初始化时设置的 `mapId`。
|
||||
|
||||
### `getVersion(): string`
|
||||
|
||||
返回 SDK 版本号,v2.3.0 返回 `"2.3.0"`。
|
||||
|
||||
### `getState(): Promise<any>`
|
||||
|
||||
向渲染基座查询内部状态。主要用于调试或 Demo 面板。
|
||||
|
||||
```ts
|
||||
const state = await map.getState();
|
||||
console.log(state);
|
||||
```
|
||||
|
||||
## 5. 地图控制
|
||||
|
||||
### `changeFloor(floorId, timeout?): Promise<{ success: boolean; floorId: string | number }>`
|
||||
|
||||
切换楼层。成功后 SDK 内部当前楼层会同步更新。
|
||||
|
||||
```ts
|
||||
await map.changeFloor(2);
|
||||
```
|
||||
|
||||
### `focusTo(node): void`
|
||||
|
||||
镜头聚焦到 POI、GLB 节点或坐标。
|
||||
|
||||
```ts
|
||||
map.focusTo({ type: 'poiId', value: 10001 });
|
||||
map.focusTo({ type: 'nodeName', value: 'Hall_Dinosaur' });
|
||||
map.focusTo({ type: 'coord', floorId: 1, x: 12.5, z: -8.2 });
|
||||
map.focusTo('Hall_Dinosaur');
|
||||
```
|
||||
|
||||
`SgsMapNode` 类型:
|
||||
|
||||
```ts
|
||||
type SgsMapNode =
|
||||
| { type: 'coord'; floorId: string | number; x: number; z: number; y?: number }
|
||||
| { type: 'poiId'; value: string | number }
|
||||
| { type: 'nodeName'; value: string };
|
||||
```
|
||||
|
||||
坐标统一使用 GLB/Three.js 米制坐标,水平面为 `x/z`,高度为 `y`。
|
||||
|
||||
### `resetView(): void`
|
||||
|
||||
重置相机视角。
|
||||
|
||||
### `setViewMode(mode): void`
|
||||
|
||||
切换 2D/3D 模式。
|
||||
|
||||
```ts
|
||||
map.setViewMode('2d');
|
||||
map.setViewMode('3d');
|
||||
```
|
||||
|
||||
### `highlightPolygon(nodeName): void`
|
||||
|
||||
高亮指定 3D 场景节点对应的空间轮廓。
|
||||
|
||||
```ts
|
||||
map.highlightPolygon('Hall_Area_01');
|
||||
```
|
||||
|
||||
## 6. 图层控制
|
||||
|
||||
### `toggleLayer(types, visible, timeout?): Promise<{ success: boolean; visibleTypes: string[] }>`
|
||||
|
||||
批量控制指定类型图层的显示隐藏。
|
||||
|
||||
```ts
|
||||
await map.toggleLayer(['TOILET', 'ELEVATOR'], true);
|
||||
await map.toggleLayer(['STAIRS'], false);
|
||||
```
|
||||
|
||||
### `setVisibleLayers(types, timeout?): Promise<{ success: boolean; visibleTypes: string[] }>`
|
||||
|
||||
设置当前可见的图层类型列表。
|
||||
|
||||
```ts
|
||||
await map.setVisibleLayers(['TOILET', 'ELEVATOR', 'SERVICE_DESK']);
|
||||
```
|
||||
|
||||
## 7. 图钉 Marker
|
||||
|
||||
### `addMarker(config): void`
|
||||
|
||||
添加业务图钉。
|
||||
|
||||
```ts
|
||||
map.addMarker({
|
||||
id: 'user-location',
|
||||
name: '我的位置',
|
||||
iconUrl: 'https://example.com/avatar.png',
|
||||
position: { x: 10, y: 1.6, z: -5 }
|
||||
});
|
||||
```
|
||||
|
||||
`MarkerConfig`:
|
||||
|
||||
```ts
|
||||
interface MarkerConfig {
|
||||
id: string;
|
||||
name?: string;
|
||||
iconUrl?: string;
|
||||
anchorNodeName?: string;
|
||||
position?: [number, number, number] | { x: number; y: number; z: number };
|
||||
}
|
||||
```
|
||||
|
||||
### `removeMarker(markerId): void`
|
||||
|
||||
移除指定图钉。
|
||||
|
||||
```ts
|
||||
map.removeMarker('user-location');
|
||||
```
|
||||
|
||||
### `clearMarkers(): void`
|
||||
|
||||
清空所有业务图钉。
|
||||
|
||||
## 8. 路径规划
|
||||
|
||||
### `planRoute(from, to, options?, timeout?): Promise<RouteResult>`
|
||||
|
||||
规划路线,兼容字符串节点名和结构化节点。
|
||||
|
||||
```ts
|
||||
const route = await map.planRoute(
|
||||
{ type: 'poiId', value: 8001 },
|
||||
{ type: 'poiId', value: 9005 },
|
||||
{ mode: 'walk' }
|
||||
);
|
||||
|
||||
if (route.success) {
|
||||
console.log(route.distance, route.duration, route.pathPoints);
|
||||
}
|
||||
```
|
||||
|
||||
也可传节点名:
|
||||
|
||||
```ts
|
||||
await map.planRoute('一楼南卫生间', '生态大展区');
|
||||
```
|
||||
|
||||
### `planRouteV2(from, to, options?, timeout?): Promise<RouteResult>`
|
||||
|
||||
结构化参数版本。当前底层同样发送 `PLAN_ROUTE` 指令。
|
||||
|
||||
```ts
|
||||
await map.planRouteV2(
|
||||
{ type: 'coord', floorId: 1, x: 0, z: 0 },
|
||||
{ type: 'coord', floorId: 1, x: 12, z: -6 },
|
||||
{ mode: 'accessible' }
|
||||
);
|
||||
```
|
||||
|
||||
### `clearRoute(): void`
|
||||
|
||||
清除当前路线。
|
||||
|
||||
```ts
|
||||
map.clearRoute();
|
||||
```
|
||||
|
||||
`RouteResult` 主要字段:
|
||||
|
||||
```ts
|
||||
interface RouteResult {
|
||||
success: boolean;
|
||||
distance: number;
|
||||
duration: number;
|
||||
pathPoints: Array<{ x: number; y: number; z: number }>;
|
||||
computeTimeMs: number;
|
||||
warnings: string[];
|
||||
errorCode?: string;
|
||||
message?: string;
|
||||
}
|
||||
```
|
||||
|
||||
## 9. 地图数据 API
|
||||
|
||||
### `getManifest(timeout?): Promise<SgsSdkManifest>`
|
||||
|
||||
获取地图 Manifest,包含地图名称、楼层摘要、能力声明和版本信息。
|
||||
|
||||
```ts
|
||||
const manifest = await map.getManifest();
|
||||
console.log(manifest.mapName, manifest.sdkVersion, manifest.floors);
|
||||
```
|
||||
|
||||
### `loadFloorBundle(floorId, timeout?): Promise<SgsFloorBundle>`
|
||||
|
||||
一次性加载指定楼层的模型、POI、空间面、讲解点和路网摘要。
|
||||
|
||||
```ts
|
||||
const bundle = await map.loadFloorBundle(1);
|
||||
console.log(bundle.model.modelUrl);
|
||||
console.log(bundle.pois.length, bundle.spaces.length, bundle.guideStops.length);
|
||||
```
|
||||
|
||||
### `getFloorPois(floorId, timeout?): Promise<SgsPoi[]>`
|
||||
|
||||
获取指定楼层 POI 列表。
|
||||
|
||||
```ts
|
||||
const pois = await map.getFloorPois(1);
|
||||
pois.forEach(poi => console.log(poi.name, poi.type, poi.position));
|
||||
```
|
||||
|
||||
### `getPois(floorCode, timeout?): Promise<SgsPoi[]>`
|
||||
|
||||
按楼层代码获取 POI 列表。该方法用于兼容既有基座能力,推荐新接入优先使用 `getFloorPois(floorId)`。
|
||||
|
||||
```ts
|
||||
const pois = await map.getPois('L1');
|
||||
```
|
||||
|
||||
### `getSpaces(floorId, timeout?): Promise<SgsSpaceArea[]>`
|
||||
|
||||
获取指定楼层空间面列表。
|
||||
|
||||
```ts
|
||||
const spaces = await map.getSpaces(1);
|
||||
spaces.forEach(space => console.log(space.name, space.boundaryWkt));
|
||||
```
|
||||
|
||||
### `getGuideStops(floorId, timeout?): Promise<SgsGuideStop[]>`
|
||||
|
||||
获取指定楼层已标定讲解点列表。
|
||||
|
||||
```ts
|
||||
const stops = await map.getGuideStops(1);
|
||||
stops.forEach(stop => console.log(stop.name, stop.audioUrl));
|
||||
```
|
||||
|
||||
### `getGuideStopsByHall(hallId, timeout?): Promise<SgsGuideStop[]>`
|
||||
|
||||
获取指定展厅下的讲解点列表。
|
||||
|
||||
```ts
|
||||
const stops = await map.getGuideStopsByHall('hall-001');
|
||||
```
|
||||
|
||||
### `preloadFloor(floorId, timeout?): Promise<{ cached: boolean }>`
|
||||
|
||||
后台预加载指定楼层模型资源,不切换当前视图。
|
||||
|
||||
```ts
|
||||
await map.preloadFloor(2);
|
||||
```
|
||||
|
||||
### `clearModelCache(): void`
|
||||
|
||||
清空客户端模型缓存。
|
||||
|
||||
```ts
|
||||
map.clearModelCache();
|
||||
```
|
||||
|
||||
### `getNavigablePlaces(floorId, timeout?): Promise<SgsNavigablePlace[]>`
|
||||
|
||||
获取指定楼层的可导航目的地列表(v2.3.0+)。
|
||||
|
||||
```ts
|
||||
const places = await map.getNavigablePlaces(1);
|
||||
places.forEach(place => console.log(place.name, place.category));
|
||||
```
|
||||
|
||||
## 10. 诊断接口 (v2.3.0+)
|
||||
|
||||
### `getDiagnostics(timeout?): Promise<SgsMapDiagnostics>`
|
||||
|
||||
获取地图整体诊断信息,包含所有楼层摘要和汇总统计。用于检查数据完整性和就绪状态。
|
||||
|
||||
```ts
|
||||
const diagnostics = await map.getDiagnostics();
|
||||
console.log('地图状态:', diagnostics.status);
|
||||
console.log('楼层总数:', diagnostics.summary.floorCount);
|
||||
console.log('POI总数:', diagnostics.summary.poiCount);
|
||||
console.log('可导航目的地总数:', diagnostics.summary.navigablePlaceCount);
|
||||
|
||||
// 检查警告信息
|
||||
if (diagnostics.warnings.length > 0) {
|
||||
console.warn('诊断警告:', diagnostics.warnings);
|
||||
}
|
||||
|
||||
// 遍历楼层摘要
|
||||
diagnostics.floors.forEach(floor => {
|
||||
console.log(`${floor.floorName}: ${floor.status}, 可导航点=${floor.navigablePlaceCount}`);
|
||||
});
|
||||
```
|
||||
|
||||
**返回结构**:
|
||||
|
||||
```ts
|
||||
interface SgsMapDiagnostics {
|
||||
mapId: string | number;
|
||||
mapName: string;
|
||||
sdkVersion: string;
|
||||
status: 'OK' | 'WARN' | 'ERROR';
|
||||
floors: SgsFloorDiagnosticsSummary[];
|
||||
summary: {
|
||||
floorCount: number;
|
||||
modelReadyFloorCount: number;
|
||||
poiCount: number;
|
||||
spaceCount: number;
|
||||
guideStopCount: number;
|
||||
navigablePlaceCount: number;
|
||||
routeNodeCount: number;
|
||||
routeEdgeCount: number;
|
||||
};
|
||||
warnings: string[];
|
||||
}
|
||||
```
|
||||
|
||||
### `getFloorDiagnostics(floorId, timeout?): Promise<SgsFloorDiagnostics>`
|
||||
|
||||
获取单个楼层的详细诊断信息。
|
||||
|
||||
```ts
|
||||
const floorDiag = await map.getFloorDiagnostics(1);
|
||||
console.log('楼层状态:', floorDiag.status);
|
||||
console.log('模型就绪:', floorDiag.modelReady);
|
||||
console.log('路径规划就绪:', floorDiag.routePlanningReady);
|
||||
console.log('POI数量:', floorDiag.poiCount);
|
||||
console.log('可导航目的地:', floorDiag.navigablePlaceCount);
|
||||
```
|
||||
|
||||
**返回结构**:
|
||||
|
||||
```ts
|
||||
interface SgsFloorDiagnostics {
|
||||
floorId: string | number;
|
||||
floorCode: string;
|
||||
floorName: string;
|
||||
status: 'OK' | 'WARN' | 'ERROR';
|
||||
modelReady: boolean;
|
||||
poiCount: number;
|
||||
spaceCount: number;
|
||||
guideStopCount: number;
|
||||
navigablePlaceCount: number;
|
||||
routeNodeCount: number;
|
||||
routeEdgeCount: number;
|
||||
routePlanningReady: boolean;
|
||||
warnings: string[];
|
||||
}
|
||||
```
|
||||
|
||||
**使用场景**:
|
||||
- 应用启动时检查地图数据是否完整
|
||||
- 排查路径规划失败原因(检查 routePlanningReady)
|
||||
- 监控数据发布后的就绪状态
|
||||
- 生成数据健康报告
|
||||
|
||||
## 11. 性能监控
|
||||
|
||||
### `getPerformanceStats(timeout?): Promise<SgsPerformanceStats>`
|
||||
|
||||
获取 SDK 与渲染基座的性能统计。v2.3.0 保持合并 SDK 自身耗时与基座返回的模型加载耗时。
|
||||
|
||||
```ts
|
||||
const stats = await map.getPerformanceStats();
|
||||
console.table({
|
||||
sdkReady: stats.engineReadyMs,
|
||||
iframeDom: stats.iframeDomReadyMs,
|
||||
manifest: stats.manifestLoadMs,
|
||||
bundle: stats.bundleLoadMs ?? 'N/A',
|
||||
model: stats.modelDownloadAndParseMs,
|
||||
firstRender: stats.firstRenderMs
|
||||
});
|
||||
```
|
||||
|
||||
主要字段:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `sdkCreatedAt` | SDK 实例创建时间戳 |
|
||||
| `iframeDomReadyMs` | iframe DOM 加载完成耗时 |
|
||||
| `engineReadyMs` | 地图引擎 ready 耗时 |
|
||||
| `manifestLoadMs` | Manifest 加载耗时 |
|
||||
| `bundleLoadMs` | Bundle 加载耗时,未触发时可能为空 |
|
||||
| `modelDownloadMs` | 模型下载耗时 |
|
||||
| `modelDownloadAndParseMs` | 模型下载与解析总耗时 |
|
||||
| `modelParseMs` | 模型解析耗时 |
|
||||
| `firstRenderMs` | 首帧渲染耗时 |
|
||||
| `routePlanMs` | 路径规划耗时 |
|
||||
| `dracoUsed` | 是否使用 Draco 压缩模型 |
|
||||
| `fallbackUsed` | 是否使用 fallback 原始模型 |
|
||||
| `currentFloorId` | 当前楼层 ID |
|
||||
|
||||
## 11. 事件
|
||||
|
||||
SDK 继承事件能力,可通过 `on(eventName, handler)` 监听事件。
|
||||
|
||||
```ts
|
||||
map.on('ready', (info) => {
|
||||
console.log('ready', info.engineVersion, info.protocolVersion);
|
||||
});
|
||||
|
||||
map.on('poiClick', (poi) => {
|
||||
console.log('poi clicked', poi.id, poi.name);
|
||||
});
|
||||
```
|
||||
|
||||
| 事件名 | Payload | 说明 |
|
||||
|--------|---------|------|
|
||||
| `ready` | `{ engineVersion, protocolVersion }` | 基座完全挂载,可安全调用 API |
|
||||
| `poiClick` | `{ id, name, type, floorId?, description? }` | 用户点击 POI |
|
||||
| `floorChanged` | `{ floorId }` | 楼层切换完成 |
|
||||
| `error` | `{ code, message }` | 基座或 SDK 异常 |
|
||||
| `loadError` | `{ type, detail }` | 资源加载异常 |
|
||||
| `routeTransfer` | `{ currentFloorId, targetFloorId, transferType, ... }` | 跨楼层换乘提示 |
|
||||
| `modelLoading` | `{ floorId, modelUrl }` | 模型开始加载 |
|
||||
| `modelReady` | `{ floorId, parseTimeMs }` | 模型解析完成 |
|
||||
| `modelError` | `{ floorId, error, fallbackUsed }` | 模型加载失败 |
|
||||
| `floorBundleReady` | `{ floorId, dataVersion }` | 楼层 Bundle 数据加载完成 |
|
||||
| `routeReady` | `{ routeId, distance, duration }` | 路径规划完成 |
|
||||
|
||||
## 12. 常用类型摘要
|
||||
|
||||
### `SgsSdkManifest`
|
||||
|
||||
```ts
|
||||
interface SgsSdkManifest {
|
||||
mapId: string | number;
|
||||
mapName: string;
|
||||
sdkVersion: string;
|
||||
protocolVersion: number;
|
||||
dataVersion: string;
|
||||
updatedAt: string;
|
||||
coordinateSystem: string;
|
||||
floors: SgsSdkFloorSummary[];
|
||||
capabilities: {
|
||||
dracoCompression: boolean;
|
||||
routePlanning: boolean;
|
||||
spatialQuery: boolean;
|
||||
guideStops: boolean;
|
||||
// v2.3.0 新增标准能力字段
|
||||
mapLoading?: boolean;
|
||||
floorSwitching?: boolean;
|
||||
poiQuery?: boolean;
|
||||
spaceQuery?: boolean;
|
||||
navigablePlaces?: boolean;
|
||||
crossFloorRoute?: boolean;
|
||||
accessibleRoute?: boolean;
|
||||
highlight?: boolean;
|
||||
diagnostics?: boolean;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### `SgsPoi`
|
||||
|
||||
```ts
|
||||
interface SgsPoi {
|
||||
id: string | number;
|
||||
name: string;
|
||||
type: string;
|
||||
typeName: string;
|
||||
floorCode: string;
|
||||
position: { x: number; y: number; z: number };
|
||||
status: 'ACTIVE' | 'INACTIVE';
|
||||
anchorNodeName?: string | null;
|
||||
description?: string;
|
||||
iconUrl?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### `SgsSpaceArea`
|
||||
|
||||
```ts
|
||||
interface SgsSpaceArea {
|
||||
id: string | number;
|
||||
name: string;
|
||||
type: string;
|
||||
floorId: string | number;
|
||||
boundaryWkt: string;
|
||||
center: { x: number; y: number; z: number };
|
||||
sourceNodeName?: string;
|
||||
status: 'ACTIVE' | 'INACTIVE';
|
||||
colorHex?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### `SgsGuideStop`
|
||||
|
||||
```ts
|
||||
interface SgsGuideStop {
|
||||
id: string | number;
|
||||
name: string;
|
||||
type: string;
|
||||
floorId: string | number;
|
||||
position: { x: number; y: number; z: number };
|
||||
targetType: string;
|
||||
targetId?: string | number;
|
||||
audioUrl?: string;
|
||||
coverImageUrl?: string;
|
||||
description?: string;
|
||||
status: 'ACTIVE' | 'INACTIVE';
|
||||
hallId?: string | number;
|
||||
hallName?: string;
|
||||
}
|
||||
```
|
||||
|
||||
完整类型以 `dist/index.d.ts` 为准。
|
||||
|
||||
## 13. 错误处理
|
||||
|
||||
所有 Promise API 失败时会 reject 一个 `{ code, message }` 结构。
|
||||
|
||||
```ts
|
||||
try {
|
||||
await map.changeFloor(999);
|
||||
} catch (err) {
|
||||
console.warn(err.code, err.message);
|
||||
}
|
||||
```
|
||||
|
||||
| 错误码 | 说明 |
|
||||
|--------|------|
|
||||
| `ERR_TIMEOUT` | 命令响应超时 |
|
||||
| `ERR_READY_TIMEOUT` | ready 超时,基座未发出 `ENGINE_READY` |
|
||||
| `ERR_NOT_READY` | SDK ready 失败后继续调用命令 |
|
||||
| `ERR_DESTROYED` | SDK 实例已销毁 |
|
||||
| `ERR_IFRAME_LOAD` | iframe 加载失败 |
|
||||
| `ERR_UNAUTHORIZED` | Origin 校验失败 |
|
||||
| `ERR_UNSUPPORTED` | 当前基座不支持该命令 |
|
||||
| `ERR_NO_PATH` | 路径规划无可达路线 |
|
||||
| `ERR_FLOOR_NOT_FOUND` | 楼层不存在或未发布 |
|
||||
| `ERR_NETWORK` | 网络或接口异常 |
|
||||
|
||||
## 14. 废弃方法
|
||||
|
||||
以下方法保留签名用于兼容旧接入方,新项目不要使用:
|
||||
|
||||
| 方法 | 状态 | 替代方案 |
|
||||
|------|------|----------|
|
||||
| `getReleaseManifest()` | 已废弃,返回 `ERR_UNSUPPORTED` | `getManifest()` |
|
||||
| `getModelResources()` | 已废弃,返回 `ERR_UNSUPPORTED` | `loadFloorBundle()` |
|
||||
| `getLayerTree()` | 已废弃,返回 `ERR_UNSUPPORTED` | 暂无 |
|
||||
| `loadMockReleaseRoute()` | 已废弃,返回 `ERR_UNSUPPORTED` | `planRoute()` |
|
||||
|
||||
## 15. 第三方接入检查清单
|
||||
|
||||
- 页面中存在 SDK 挂载容器,且容器有稳定宽高。
|
||||
- `sdkUrl` 可在浏览器直接访问,并能加载 `/h5-sdk`。
|
||||
- `targetOrigin` 与 `sdkUrl` 的 Origin 一致。
|
||||
- 所有地图控制 API 在 `await map.whenReady()` 后调用。
|
||||
- 坐标统一使用 GLB/Three.js 米制坐标,水平面为 `x/z`。
|
||||
- 页面卸载时调用 `map.destroy()`。
|
||||
- 生产发布时 SDK 版本、基座版本和后端 Manifest 的 `sdkVersion` 保持 `2.3.0`。
|
||||
@@ -1,6 +1,6 @@
|
||||
# SGS Map SDK 通信协议规格 (Protocol Specification)
|
||||
|
||||
> 版本: V2.0.0
|
||||
> 版本: V2.3.0
|
||||
> 适用对象: SGS Map SDK 核心开发者、渲染基座 (h5-sdk) 开发者
|
||||
|
||||
本文档定义了外壳 SDK (`sgs-map-sdk.js`) 与 独立渲染基座 iframe (`h5-sdk/page.tsx`) 之间的 `postMessage` 通信契约。所有消息传递必须严格遵循此结构,以保障多域环境下的确权、安全过滤与异步生命周期追踪。
|
||||
@@ -61,7 +61,7 @@ sequenceDiagram
|
||||
|
||||
Note over SDK,Base: 此时 HTML 刚加载,WebGL/GLB 模型并未解析完成
|
||||
|
||||
SDK->>Base: 发送 HELLO { protocolVersion: 2, sdkVersion: "2.0.0" }
|
||||
SDK->>Base: 发送 HELLO { protocolVersion: 2, sdkVersion: "2.3.0" }
|
||||
Base->>Base: 挂载 WebGL,拉取 GLB 及 POI 接口
|
||||
Base-->>SDK: 内部加载完毕,推送 ENGINE_READY 事件
|
||||
SDK->>SDK: 解锁 isReady = true,清空缓存命令队列
|
||||
@@ -88,9 +88,24 @@ sequenceDiagram
|
||||
| `RESET_VIEW` | (空) | `{ success: true }` | 重置相机视角至初始状态 |
|
||||
| `SET_VIEW_MODE` | `{ mode: '2d' | '3d' }` | `{ success: true }` | 切换 2D/3D 视角模式 |
|
||||
| `TOGGLE_LAYER` | `{ types: string[], visible: boolean }` | `{ success: true, visibleTypes: string[] }` | 批量控制某类底座设施的显示隐藏 |
|
||||
| `GET_MANIFEST` | `{}` | `ManifestData` | 获取地图 Manifest(楼层列表、元数据) |
|
||||
| `LOAD_FLOOR_BUNDLE` | `{ floorId: string \| number }` | `FloorBundleData` | 一次性加载楼层完整 Bundle(模型+POI+空间面+讲解点) |
|
||||
| `GET_FLOOR_POIS` | `{ floorId: string \| number }` | `{ pois: PoiItem[] }` | 获取指定楼层 POI 列表 |
|
||||
| `GET_SPACES` | `{ floorId: string \| number }` | `{ spaces: SpaceItem[] }` | 获取指定楼层空间面列表 |
|
||||
| `GET_GUIDE_STOPS` | `{ floorId: string \| number }` | `{ guideStops: GuideStopItem[] }` | 获取指定楼层讲解点列表 |
|
||||
| `GET_NAVIGABLE_PLACES` | `{ floorId: string \| number }` | `{ navigablePlaces: SgsNavigablePlace[] }` | 获取指定楼层可导航目的地列表 |
|
||||
| `PRELOAD_FLOOR` | `{ floorId: string \| number }` | `{ success: true }` | 后台静默预加载指定楼层模型资源 |
|
||||
| `CLEAR_MODEL_CACHE` | (空) | `{ success: true, freedBytes: number }` | 清空客户端模型缓存 |
|
||||
| `GET_PERFORMANCE_STATS` | (空) | `PerformanceStats` | 获取渲染性能统计(FPS/DrawCall/纹理内存) |
|
||||
| `GET_DIAGNOSTICS` | `{}` | `SgsMapDiagnostics` | 获取地图级 SDK 数据健康诊断 |
|
||||
| `GET_FLOOR_DIAGNOSTICS` | `{ floorId: string \| number }` | `SgsFloorDiagnostics` | 获取指定楼层 SDK 数据健康诊断 |
|
||||
|
||||
> **类型定义备注:**
|
||||
> `SgsMapNode` 及 `RouteResult` 的精确结构请参考 TypeScript 源码定义:`sdk-src/types.ts`。
|
||||
>
|
||||
> **v2.3.0 类型放宽**:所有 ID 字段(`floorId`、`mapId`、`poiId` 等)支持 `string | number`,向下兼容纯数字调用。
|
||||
>
|
||||
> **v2.3.0 主要类型**:`ManifestData`、`FloorBundleData`、`PoiItem`、`SpaceItem`、`GuideStopItem`、`SgsNavigablePlace`、`SgsMapDiagnostics`、`SgsFloorDiagnostics`、`PerformanceStats` 的精确结构请参考 `sdk-src/types.ts`。
|
||||
|
||||
---
|
||||
|
||||
@@ -101,6 +116,11 @@ sequenceDiagram
|
||||
| `ENGINE_READY` | `{ engineVersion, protocolVersion, sdkVersion }` | `ready` | 基座完全挂载,WebGL 画布可渲染 |
|
||||
| `ON_POI_CLICK` | `{ id, name, type }` | `poiClick` | 用户在触摸屏或鼠标点击了高亮要素 |
|
||||
| `ON_FLOOR_CHANGED`| `{ floorId }` | `floorChanged` | 楼层完全切换完毕后自动抛出 |
|
||||
| `ON_MODEL_LOADING` | `{ floorId, modelUrl }` | `modelLoading` | 模型文件开始网络加载 |
|
||||
| `ON_MODEL_READY` | `{ floorId, parseTimeMs }` | `modelReady` | 模型解析完成,Scene 已挂载 |
|
||||
| `ON_MODEL_ERROR` | `{ floorId, error, fallbackUsed }` | `modelError` | 模型加载失败(含 fallback 信息) |
|
||||
| `ON_FLOOR_BUNDLE_READY` | `{ floorId, dataVersion }` | `floorBundleReady` | 楼层 Bundle 数据全部就绪 |
|
||||
| `ON_ROUTE_READY` | `{ routeId, distance, duration }` | `routeReady` | 路径规划完成 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SGS Map SDK 快速接入指南
|
||||
|
||||
> 版本: V2.0.0 | 支持终端: H5 浏览器、大屏终端、微信小程序
|
||||
> 版本: V2.3.0 | 支持终端: H5 浏览器、大屏终端、微信小程序
|
||||
|
||||
## 1. 引入方式
|
||||
|
||||
@@ -15,6 +15,12 @@ SDK 提供多种模块规范的产出文件,适配不同的应用场景:
|
||||
</script>
|
||||
```
|
||||
|
||||
如果 SDK 已由 `sgs-frontend-map` 发布到前端静态目录,也可以直接使用浏览器发布文件:
|
||||
|
||||
```html
|
||||
<script src="/sdk/sgs-map-sdk.min.js?v=2.3.0"></script>
|
||||
```
|
||||
|
||||
### 1.2 ESM Import (Webpack / Vite / Next.js)
|
||||
在现代前端工程中,直接使用 ESM 格式:
|
||||
```javascript
|
||||
@@ -30,6 +36,16 @@ const sdk = new SGSMapSDK({ ... });
|
||||
<web-view src="https://map.museum.com/h5-sdk?floorId=1&mode=miniapp"></web-view>
|
||||
```
|
||||
|
||||
### 1.4 渲染基座地址
|
||||
|
||||
SDK 通过 `sdkUrl` 连接独立渲染基座。正式接入时请使用地图服务平台管理员提供的基座地址,例如:
|
||||
|
||||
```text
|
||||
https://map.museum.com/h5-sdk
|
||||
```
|
||||
|
||||
本 SDK 发布包不附带产品化 Demo。Demo 将作为独立交付物在后续版本中重构,不作为当前 SDK 接入前置条件。
|
||||
|
||||
---
|
||||
|
||||
## 2. 初始化与生命周期
|
||||
@@ -135,6 +151,156 @@ map.clearMarkers();
|
||||
|
||||
---
|
||||
|
||||
## 核心数据 API (v2.2.0+ / v2.3.0 兼容)
|
||||
|
||||
### 获取地图 Manifest
|
||||
|
||||
获取地图元数据(楼层列表、地图名称、版本信息等),是初始化后的第一步。
|
||||
|
||||
```javascript
|
||||
map.getManifest().then((manifest) => {
|
||||
console.log('地图名称:', manifest.mapName);
|
||||
console.log('楼层列表:', manifest.floors);
|
||||
// manifest.floors => [{ id: 1, name: 'F1', ... }, { id: 2, name: 'F2', ... }]
|
||||
});
|
||||
```
|
||||
|
||||
### 加载楼层 Bundle
|
||||
|
||||
一次性拉取楼层的模型、POI、空间面和讲解点数据,适合需要预加载全部数据的场景。
|
||||
|
||||
```javascript
|
||||
map.loadFloorBundle(1).then((bundle) => {
|
||||
console.log('模型:', bundle.model);
|
||||
console.log('POI 数量:', bundle.pois.length);
|
||||
console.log('空间面数量:', bundle.spaces.length);
|
||||
console.log('讲解点数量:', bundle.guideStops.length);
|
||||
});
|
||||
|
||||
// 监听 Bundle 就绪事件
|
||||
map.on('floorBundleReady', (data) => {
|
||||
console.log(`楼层 ${data.floorId} Bundle 就绪,数据版本: ${data.dataVersion}`);
|
||||
});
|
||||
```
|
||||
|
||||
### 查询楼层数据
|
||||
|
||||
分别获取 POI 列表、空间面列表、讲解点列表。
|
||||
|
||||
```javascript
|
||||
// 获取楼层 POI 列表
|
||||
const pois = await map.getFloorPois(1);
|
||||
pois.forEach(poi => console.log(poi.name, poi.type));
|
||||
|
||||
// 获取空间面列表(展厅/分区)
|
||||
const spaces = await map.getSpaces(1);
|
||||
spaces.forEach(s => console.log(s.name, s.area));
|
||||
|
||||
// 获取讲解点列表
|
||||
const guideStops = await map.getGuideStops(1);
|
||||
guideStops.forEach(stop => console.log(stop.title, stop.audioUrl));
|
||||
```
|
||||
|
||||
### 获取可导航目的地 (v2.3.0)
|
||||
|
||||
获取指定楼层可用于路线规划的候选目的地。该列表会优先返回业务门点(例如“快餐店 出入口 1”),并过滤纯电梯、楼梯、扶梯等交通设施本体。
|
||||
|
||||
```javascript
|
||||
const places = await map.getNavigablePlaces(2065808921012072449);
|
||||
places.forEach(place => {
|
||||
console.log(place.name, place.category, place.x, place.z, place.ownerName);
|
||||
});
|
||||
```
|
||||
|
||||
### SDK 诊断接口 (v2.3.0)
|
||||
|
||||
发布前或接入异常时,可先调用诊断接口确认地图数据是否完整。
|
||||
|
||||
```javascript
|
||||
const mapDiag = await map.getDiagnostics();
|
||||
console.log('地图状态:', mapDiag.status);
|
||||
console.log('楼层数量:', mapDiag.summary.floorCount);
|
||||
console.log('POI 数量:', mapDiag.summary.poiCount);
|
||||
console.log('可导航目的地:', mapDiag.summary.navigablePlaceCount);
|
||||
|
||||
const floorDiag = await map.getFloorDiagnostics(2065808921578303490);
|
||||
console.log('楼层状态:', floorDiag.status);
|
||||
console.log('路网节点:', floorDiag.routeNodeCount);
|
||||
console.log('路网边:', floorDiag.routeEdgeCount);
|
||||
```
|
||||
|
||||
### 预加载楼层
|
||||
|
||||
在用户当前浏览 F1 时,后台静默预加载 F2 的模型资源,切换时无需等待。
|
||||
|
||||
```javascript
|
||||
// 预加载 F2(不切换视图)
|
||||
map.preloadFloor(2).then(() => {
|
||||
console.log('F2 模型预加载完成,切换时将秒开');
|
||||
});
|
||||
```
|
||||
|
||||
### 模型加载生命周期事件
|
||||
|
||||
监控模型加载全过程,实现自定义 Loading UI。
|
||||
|
||||
```javascript
|
||||
map.on('modelLoading', ({ floorId, modelUrl }) => {
|
||||
showLoadingSpinner(`正在加载楼层 ${floorId} 模型...`);
|
||||
console.log('模型地址:', modelUrl);
|
||||
});
|
||||
|
||||
map.on('modelReady', ({ floorId, parseTimeMs }) => {
|
||||
hideLoadingSpinner();
|
||||
console.log(`楼层 ${floorId} 模型就绪,解析耗时 ${parseTimeMs}ms`);
|
||||
});
|
||||
|
||||
map.on('modelError', ({ floorId, error, fallbackUsed }) => {
|
||||
hideLoadingSpinner();
|
||||
if (fallbackUsed) {
|
||||
console.warn(`楼层 ${floorId} Draco 解码失败,已回退原始 GLB`);
|
||||
} else {
|
||||
console.error(`楼层 ${floorId} 模型加载失败:`, error);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 路径规划完成事件
|
||||
|
||||
除了 `planRoute` 的 Promise 返回,还可以通过事件监听获取路径结果。
|
||||
|
||||
```javascript
|
||||
map.on('routeReady', ({ routeId, distance, duration }) => {
|
||||
console.log(`路线 ${routeId} 规划完成`);
|
||||
console.log(`路径总长 ${distance}m, 预计 ${duration}s`);
|
||||
});
|
||||
```
|
||||
|
||||
### 性能监控
|
||||
|
||||
获取当前渲染性能统计,便于排查性能问题。
|
||||
|
||||
```javascript
|
||||
map.getPerformanceStats().then((stats) => {
|
||||
console.log('FPS:', stats.fps);
|
||||
console.log('DrawCalls:', stats.drawCalls);
|
||||
console.log('纹理内存:', stats.textureMemoryMB, 'MB');
|
||||
console.log('几何体内存:', stats.geometryMemoryMB, 'MB');
|
||||
});
|
||||
```
|
||||
|
||||
### 清空模型缓存
|
||||
|
||||
当需要强制刷新模型(如模型更新后),可清空本地缓存。
|
||||
|
||||
```javascript
|
||||
map.clearModelCache().then(() => {
|
||||
console.log('模型缓存已清空,下次加载将从服务端重新拉取');
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 错误处理
|
||||
|
||||
SDK 采用 Promise 风格的异步响应,所有 `catch` 捕获的错误都遵循 `{ code, message }` 格式。
|
||||
@@ -145,3 +311,7 @@ SDK 采用 Promise 风格的异步响应,所有 `catch` 捕获的错误都遵
|
||||
| `ERR_NOT_READY` | 在 `ready` 事件触发前,强行调用了 API |
|
||||
| `ERR_NO_PATH` | 起点与终点之间没有连通的 NavMesh 物理网格 |
|
||||
| `ERR_UNAUTHORIZED` | 通信双方跨域安全策略 (`targetOrigin`) 校验失败 |
|
||||
| `ERR_FLOOR_NOT_FOUND` | 指定的楼层 ID 不存在或未发布 |
|
||||
| `ERR_MODEL_LOAD` | 模型文件加载失败(网络异常或文件损坏) |
|
||||
| `ERR_DRACO_DECODE` | Draco 压缩模型解码失败(已自动回退原始 GLB) |
|
||||
| `ERR_BUNDLE_INCOMPLETE` | 楼层 Bundle 数据不完整(部分资源缺失) |
|
||||
|
||||
Reference in New Issue
Block a user