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

View File

@@ -0,0 +1,799 @@
# frontend-miniapp 数据层全面审计报告
审计日期2026-05-28
审计分支:`analysis/ux-ui-audit-2026-05-28`
审计对象:`museum-guide-v4.0/frontend-miniapp`
审计范围Mock 数据、类型定义、数据加载、搜索/地图/详情页硬编码数据、静态资源、3D/室内 POI 数据、数据治理与性能策略。
## 执行摘要
当前 `frontend-miniapp` 的数据层处于“多套 Mock 数据并存”的原型状态,不能直接作为深圳自然博物馆导览的数据基础。最高风险集中在三类:业务定位数据错配、数据源不统一、资源与关联关系不可验证。
| 优先级 | 关键风险 | 影响 | 建议 |
| --- | --- | --- | --- |
| P0 | 核心 JSON 仍是“深圳国际艺术馆/世界名画”内容,而产品标识是“深圳自然博物馆” | 搜索、详情、路线、讲解内容与真实馆方定位冲突 | 立即建立自然博物馆数据字典和迁移清单,冻结旧艺术馆 Mock 数据新增 |
| P0 | 列表、详情、地图、搜索各自硬编码不同实体与 ID | 用户从搜索/地图进入详情会看到不一致内容,甚至找不到对应数据 | 建立单一数据源,所有页面按 `entityId` 查询同一仓库 |
| P0 | 15 个图片/音频 URL 指向不存在资源,另有 `https://example.com/audio.mp3` 占位 | 展品图、展厅图、语音讲解失败,影响核心导览体验 | 建立资源清单校验,缺失资源上线前阻断 |
| P1 | 类型定义与真实 POI/地图数据不匹配 | TypeScript 无法保护 3D 坐标、入口/展厅/设施枚举,后续扩展易破 | 扩展 `Position``POI``FacilityType`、多语言与状态字段 |
| P1 | 无数据版本、校验、缓存、错误分级 | 数据异常会静默返回空数组,难以监控和降级 | 引入 schema 校验、数据版本元信息和错误态 UI |
建议采用两步治理:先用 1 周完成“数据源收敛和阻断性校验”,再用 2 到 4 周完成自然博物馆正式数据迁移、地图 POI 绑定、资源治理与多语言模型。
## 审计方法
本次审计使用静态扫描和结构化脚本校验,未修改业务代码。
| 方法 | 覆盖内容 | 证据来源 |
| --- | --- | --- |
| JSON 结构扫描 | 展品、展厅、设施、路线、楼层、室内 POI 数量与字段 | `src/assets/data/*.json``static/data/f1-indoor-pois.json` |
| 引用完整性校验 | 展品到展厅、楼层到展厅/设施、路线到站点 | Node 脚本读取 JSON 后交叉比对 |
| 静态资源存在性校验 | 图片、音频、展厅图是否存在于 `static/` | 文件系统校验 |
| 硬编码扫描 | 页面、组件、地图、搜索中的本地数组和占位 URL | `rg` 搜索 |
| 类型与数据流审查 | TypeScript 接口、数据加载、搜索工具、服务层 | `src/types/index.ts``src/utils/dataLoader.ts``src/utils/search.ts``src/services/map/Map3DManager.ts` |
## 数据资产盘点
| 数据源 | 数量 | 现状判断 | 主要问题 |
| --- | ---: | --- | --- |
| `src/assets/data/exhibits.json` | 5 | 艺术馆展品 Mock | 全部是世界名画,不是自然博物馆展品;图片/音频缺失 |
| `src/assets/data/halls.json` | 5 | 艺术馆展厅 Mock | 展厅主题为文艺复兴、印象派、现代艺术;展品数量与实际 JSON 不一致 |
| `src/assets/data/facilities.json` | 8 | 基础设施/模型坐标混合数据 | 点位坐标来自 `f1-floor.glb` 提取,应保留;但实体名称、类型、描述仍与真实 1F POI 和自然博物馆语义不一致 |
| `src/assets/data/routes.json` | 3 | 艺术馆路线 Mock | 路线名称、站点均围绕艺术作品;不适配自然博物馆参观动线 |
| `src/assets/data/floors.json` | 4 | 楼层索引 | `B1` 引用不存在设施,且无展厅 |
| `static/data/f1-indoor-pois.json` | 67 | 真实 1F 室内点位基准 | 其它设施、展厅、搜索、地图详情数据未以它为准,存在大面积冲突 |
| `static/models/*.glb` | 2 | 真实 1F 3D 模型资产 | `f1-indoor.glb``f1-floor.glb` 均为自然博物馆 1F 真实模型;需补模型清单、坐标系与部署目录策略 |
| `static/icons/*.svg` | 8 | 地图图标 | 可用,但 POI 类型枚举与图标映射未统一 |
当前数据流可以概括为:
```mermaid
flowchart TD
A["assets/data/*.json<br/>艺术馆核心 Mock"] --> B["dataLoader.ts"]
B --> C["部分列表/工具函数"]
D["pages/detail.vue<br/>硬编码详情"] --> E["详情页展示"]
F["SearchPanel / ExplainList<br/>自然博物馆硬编码 Mock"] --> G["搜索/讲解抽屉"]
H["TencentMap / ThreeMap<br/>硬编码地图点"] --> I["地图弹层"]
J["static/data/f1-indoor-pois.json<br/>67 个室内 POI"] -.未统一接入.-> I
```
## 1. 数据完整性审计
### 1.1 Mock 数据覆盖度
| 实体 | 当前覆盖 | 与真实自然博物馆导览的差距 | 严重性 |
| --- | --- | --- | --- |
| 展品 | 5 个艺术作品 | 缺少标本、化石、矿物、动植物、年代、分类、馆藏编号、展陈状态、讲解文本层级 | P0 |
| 展厅 | 5 个艺术主题展厅 | 缺少自然史展厅分区、楼层分布、入口/出口、展厅开放状态、人流/容量 | P0 |
| 设施 | 8 个基础设施 | 室内 POI 有 58 个设施,但未与设施表合并;缺少服务台、母婴室、寄存、无障碍、楼梯等正式类型 | P1 |
| 路线 | 3 条艺术馆路线 | 缺少亲子、研学、无障碍、快速参观、自然史主题路线 | P0 |
| 楼层 | 4 层索引 | 只有简单数组引用,无真实地图区域、楼层坐标系、模型版本、开放状态 | P1 |
| POI | F1 有 67 个模型点 | 只覆盖 `1F`,未绑定展品/展厅/路线/设施详情 | P1 |
### 1.2 必填字段缺失与占位符
结构字段层面,展品、展厅、设施、路线大多包含基础字段,但业务必填字段明显不足:没有馆藏编号、自然史分类、状态、更新时间、版权、资源元信息、语种、坐标系。
静态校验结果:
```text
COUNTS
exhibits: 5
halls: 5
facilities: 8
routes: 3
floors: 4
pois: 67
MISSING REQUIRED
exhibits: none
halls: none
facilities: none
routes: none
floors: floor_b1:halls
```
虽然基础字段未大量为空,但资源字段失效非常严重:
```text
exhibit_1.image -> /static/exhibits/mona-lisa.jpg missing
exhibit_1.audioUrl -> /static/audio/exhibit-1.mp3 missing
exhibit_2.image -> /static/exhibits/last-supper.jpg missing
exhibit_2.audioUrl -> /static/audio/exhibit-2.mp3 missing
exhibit_3.image -> /static/exhibits/starry-night.jpg missing
exhibit_3.audioUrl -> /static/audio/exhibit-3.mp3 missing
exhibit_4.image -> /static/exhibits/sunflowers.jpg missing
exhibit_4.audioUrl -> /static/audio/exhibit-4.mp3 missing
exhibit_5.image -> /static/exhibits/david.jpg missing
exhibit_5.audioUrl -> /static/audio/exhibit-5.mp3 missing
hall_1.image -> /static/halls/hall-1.jpg missing
hall_2.image -> /static/halls/hall-2.jpg missing
hall_3.image -> /static/halls/hall-3.jpg missing
hall_4.image -> /static/halls/hall-4.jpg missing
hall_5.image -> /static/halls/hall-5.jpg missing
```
页面中还存在音频占位 URL
```text
frontend-miniapp/src/pages/index/index.vue:306
audioUrl: exhibit.audioUrl || 'https://example.com/audio.mp3'
frontend-miniapp/src/pages/index/index.vue:355
audioUrl: exhibit.audioUrl || 'https://example.com/audio.mp3'
```
### 1.3 数据一致性
展厅统计与实际展品数量完全不一致:
| 展厅 | `halls.json` 标称展品数 | `exhibits.json` 实际归属数 | 差异 |
| --- | ---: | ---: | ---: |
| `hall_1` | 25 | 3 | -22 |
| `hall_2` | 18 | 2 | -16 |
| `hall_3` | 30 | 0 | -30 |
| `hall_4` | 22 | 0 | -22 |
| `hall_5` | 15 | 0 | -15 |
同一逻辑实体在不同模块中出现了多套 ID 和名称:
| 模块 | 示例 | 问题 |
| --- | --- | --- |
| 核心 JSON | `exhibit_1``蒙娜丽莎` | 艺术馆实体 |
| 搜索页 | `id: '1', name: '蒙娜丽莎'` | ID 不是 `exhibit_1`,进入详情时无法保证匹配 |
| 搜索面板 | `id: '1', name: '霸王龙化石骨架'` | 同一 ID `1` 在另一模块代表自然博物馆展品 |
| 讲解抽屉 | `id: '1', name: '银杏化石'` | 同一 ID `1` 又代表第三个实体 |
| 地图首页 | `exhibit-1``恐龙化石展区` | ID 命名与核心 JSON 不一致 |
| 3D 地图 | `exhibit-1``恐龙化石展区` | 与 `exhibit_1` 不一致,且未绑定详情数据 |
硬编码证据截图:
```text
frontend-miniapp/src/pages/search/index.vue:101
{ id: '1', name: '蒙娜丽莎', artist: '达芬奇', hall: '1号展厅', hasAudio: true }
frontend-miniapp/src/components/search/SearchPanel.vue:143
{ id: '1', name: '霸王龙化石骨架', desc: '恐龙厅 · 2F', type: 'exhibit' }
frontend-miniapp/src/components/explain/ExplainList.vue:293
{ id: '1', name: '银杏化石', hall: '演化厅 B1', hasAudio: true, audioUrl: '' }
frontend-miniapp/src/pages/index/index.vue:164
'exhibit-1': { id: 'exhibit-1', name: '恐龙化石展区', subtitle: '中生代恐龙化石展示', type: 'exhibit', floor: '2F' }
```
### 1.4 关联关系完整性
路线站点都能在艺术馆展品表中找到,但楼层到设施存在断链:
```text
BROKEN REFERENCES
floor floor_b1 facility parking_b1
floor floor_b1 facility storage_b1
```
更大的关联问题是 `static/data/f1-indoor-pois.json` 与核心实体完全脱钩。该文件已确认为真实 1F 设施点位基准,包含 67 个 POI类型分布为
```text
POI TYPES
{ hall: 6, facility: 58, entrance: 3 }
POI floors: 1F
```
这些 POI 的 `id` 形如 `poi_0`,没有 `entityId` 指向 `hall_1``facility_*` 或路线站点,无法支撑“点击地图点打开统一详情”“按路线高亮真实点位”等核心导览能力。
### 1.5 数据时效性
当前数据没有以下字段,无法判断是否过期:
| 缺失能力 | 影响 |
| --- | --- |
| `status` / `visibility` | 无法区分开放、临时关闭、下架、维护中 |
| `validFrom` / `validTo` | 无法表达临展、活动、设施维修周期 |
| `updatedAt` / `sourceVersion` | 无法追踪数据来源和更新批次 |
| `reviewedBy` / `reviewedAt` | 无法形成馆方内容审核闭环 |
因此“已下架展品、已关闭设施”目前无法自动识别,只能依赖人工回归。
## 2. 数据结构与类型规范性
### 2.1 TypeScript 类型定义
`src/types/index.ts` 更像早期 Mock 类型,无法覆盖现有地图和真实业务数据。
| 类型 | 当前定义 | 发现的问题 | 严重性 |
| --- | --- | --- | --- |
| `Position` | `{ x, y }` | 2D 坐标,不支持室内 POI 的 `{ x, y, z }`,也不支持腾讯地图的 `{ latitude, longitude }` | P1 |
| `Facility.type` | `restroom/cafe/shop/exit/elevator/info` | 不包含 `entrance``hall``stairs``mother_baby``accessible_restroom``service_desk` 等真实 POI 类型 | P1 |
| `POIMarker.type` | `exhibit/hall/facility/location` | 不包含 `entrance`,与 `ThreeMap``f1-indoor-pois.json` 不一致 | P1 |
| `Exhibit` | 艺术品字段 `artist/year/material/size` | 自然博物馆需要 `taxonomy``period``specimenType``collectionNo``scientificName``ageRange` 等字段 | P0 |
| 全部实体 | 无多语言、无状态、无版本 | 无法支撑中英文切换、数据审核、增量更新和下线 | P1 |
建议新增分层模型:
```ts
type LocaleText = { zhCN: string; enUS?: string }
type Coordinate =
| { system: 'indoor_3d'; floorId: string; x: number; y: number; z: number }
| { system: 'indoor_2d'; floorId: string; x: number; y: number }
| { system: 'wgs84' | 'gcj02'; latitude: number; longitude: number }
type EntityStatus = 'draft' | 'published' | 'hidden' | 'closed' | 'maintenance'
```
### 2.2 API 响应格式
`src/services/` 下只有 `map/Map3DManager.ts`,没有业务 API service。数据加载集中在 `src/utils/dataLoader.ts`,但返回值未声明类型,错误时统一返回空数组:
```ts
export const loadExhibits = async () => {
try {
const data = await import('@/assets/data/exhibits.json')
return data.default || data
} catch (error) {
console.error('加载展品数据失败:', error)
return []
}
}
```
风险:
| 问题 | 影响 |
| --- | --- |
| 无 `Promise<Exhibit[]>` 等返回类型 | 调用方无法获得类型保护 |
| 使用 `any` 查找 | ID 类型、字段缺失、空数据不会被编译期发现 |
| 异常统一变成 `[]` | 加载失败、数据为空、校验失败三种状态无法区分 |
| 无响应 envelope | 后续接 API 时缺少 `version``updatedAt``source``errors``pagination` 规范 |
建议定义统一响应:
```ts
interface DataResult<T> {
ok: boolean
data: T
version: string
updatedAt: string
source: 'mock' | 'cms' | 'cache'
errors?: DataError[]
}
```
### 2.3 枚举值规范
枚举值目前分散在类型、地图、组件、数据文件中:
| 位置 | 枚举/类别 | 问题 |
| --- | --- | --- |
| `types/index.ts` | `Facility.type` | 只覆盖 6 种旧设施 |
| `components/area/AreaSelector.vue` | `storage``accessible` 等 | UI 类别未进入类型系统 |
| `static/data/f1-indoor-pois.json` | `hall/facility/entrance` | 与 `POIMarker.type` 不一致 |
| `ThreeMap.vue` | `entrance/exhibit/facility/hall` | 局部定义,与全局类型重复 |
| `TencentMap.vue` | marker 图标路径 | 通过硬编码绑定,不受枚举约束 |
建议将枚举集中到 `src/types/enums.ts``src/domain/guideSchema.ts`,并由数据校验脚本复用。
### 2.4 数据转换逻辑
当前没有明确的 adapter/transformer。结果是 2D JSON、3D POI、腾讯地图经纬度、页面详情对象各自拥有不同字段形态。
建议新增三层转换:
| 层级 | 职责 |
| --- | --- |
| Raw Schema | 接收 CMS/JSON 原始字段,严格校验 |
| Domain Model | 统一实体、坐标、资源、状态、多语言 |
| View Model | 为卡片、地图弹层、路线列表、搜索结果生成轻量字段 |
## 3. 业务数据合理性
### 3.1 内容与项目定位不符
项目描述与页面标题已经使用“深圳自然博物馆”,但核心数据仍是艺术馆:
| 位置 | 当前内容 | 业务判断 |
| --- | --- | --- |
| `manifest.json` | `深圳自然博物馆智能导览应用` | 项目定位明确为自然博物馆 |
| `exhibits.json` | `蒙娜丽莎``最后的晚餐``星空``向日葵``大卫像` | 与自然博物馆定位冲突 |
| `halls.json` | 文艺复兴、印象派、现代艺术 | 与自然史展陈冲突 |
| `routes.json` | 经典艺术之旅、印象派精选 | 与自然博物馆导览路线冲突 |
| `SearchPanel.vue` / `ExplainList.vue` | 恐龙、化石、银杏、矿石 | 内容方向正确,但与核心 JSON 脱节 |
这是 P0 风险,因为它会直接导致“产品名是自然博物馆,但内容是艺术馆”的体验断裂。
### 3.2 数量级合理性
真实自然博物馆导览通常需要覆盖更多实体:
| 数据类型 | 当前数量 | 建议 MVP 数量 | 建议正式版数量 |
| --- | ---: | ---: | ---: |
| 展品/标本 | 5 | 30 到 60 | 200+ |
| 展厅/展区 | 5 | 8 到 12 | 按真实楼层和展陈分区完整覆盖 |
| 设施 | 8 | 30 到 60 | 与室内 POI 全量绑定 |
| 路线 | 3 | 5 到 8 | 支持人群、时长、无障碍、拥堵策略 |
| 室内 POI | 67仅 1F | 每层 50+ | 全楼层、全关键节点 |
当前数量只适合演示 UI无法支撑真实导览。
### 3.3 地理坐标准确性
坐标系统存在三套并行:
| 坐标来源 | 字段 | 问题 |
| --- | --- | --- |
| 核心 JSON | `position: { x, y }` | 未声明坐标系、楼层、比例尺和原点 |
| 3D POI | `{ x, y, z }` | 未进入 `types/index.ts`,未绑定实体 |
| 腾讯地图 | `{ latitude, longitude }` | 硬编码在组件内,未与室内 POI 建立转换关系 |
`TencentMap.vue` 里的建筑轮廓和 `ThreeMap.vue` 的中心点均硬编码为深圳自然博物馆附近坐标,但无法验证这些点与 GLB 模型、室内 POI、楼层图是否同源。
### 3.4 多语言数据
当前核心实体没有英文名、英文说明、拼音、别名、检索关键词,也没有语言切换后的字段 fallback。
建议每个可展示字段采用 `LocaleText`,搜索索引包括:
| 字段 | 用途 |
| --- | --- |
| `name.zhCN` / `name.enUS` | 中英文展示 |
| `aliases.zhCN[]` / `aliases.enUS[]` | 同义词、简称、俗名 |
| `scientificName` | 自然史学名 |
| `keywords[]` | 搜索、推荐、路线匹配 |
| `audio.zhCN` / `audio.enUS` | 多语言讲解 |
## 4. 数据管理与维护性
### 4.1 数据源管理
当前数据分散在至少 11 类位置:
| 数据位置 | 数据类型 | 风险 |
| --- | --- | --- |
| `src/assets/data/*.json` | 核心 Mock | 内容过旧,艺术馆化 |
| `static/data/f1-indoor-pois.json` | 3D 模型 POI | 未接入业务实体 |
| `pages/search/index.vue` | 搜索页本地数组 | 与核心 JSON ID 不一致 |
| `components/search/SearchPanel.vue` | 搜索弹层本地数组 | 自然博物馆 Mock与核心 JSON 不一致 |
| `components/explain/ExplainList.vue` | 讲解列表本地数组 | 音频字段为空ID 冲突 |
| `pages/index/index.vue` | 地图详情 `markerDataMap` | 自然博物馆硬编码,与 POI/JSON 不一致 |
| `components/map/ThreeMap.vue` | `defaultPOIs` | 默认值会被空数组 props 覆盖,且 ID 不统一 |
| `components/map/TencentMap.vue` | 经纬度 markers | 地图数据不可配置 |
| `pages/exhibit/detail.vue` | 展品详情默认对象 | 不按 ID 加载真实数据 |
| `pages/hall/detail.vue` | 展厅详情默认对象 | 不按 ID 加载真实数据 |
| `pages/facility/detail.vue` | 设施详情默认对象 | 不按 ID 加载真实数据 |
建议将所有页面改为读取同一份 domain store
```mermaid
flowchart LR
A["CMS / Mock JSON"] --> B["schema validation"]
B --> C["domain repository"]
C --> D["search index"]
C --> E["map POI view model"]
C --> F["detail pages"]
C --> G["route engine"]
C --> H["offline cache"]
```
### 4.2 数据更新机制
当前没有发现以下机制:
| 机制 | 当前状态 | 建议 |
| --- | --- | --- |
| 数据版本控制 | 无 `version` 文件 | 新增 `data-manifest.json` |
| 增量更新 | 无 | 按实体类型和 `updatedAt` 增量拉取 |
| 本地缓存 | 未见 `uni.setStorage`/`uni.getStorage` 数据缓存 | 小程序端缓存核心 JSON 和搜索索引 |
| 灰度发布 | 无 | 支持 `draft/published` 与版本回滚 |
| 数据来源追踪 | 无 | 每条记录保留 `sourceId``sourceUpdatedAt` |
### 4.3 数据校验
建议建立 `scripts/audit-data.ts`,在 CI 或提交前执行:
| 校验类型 | 规则 |
| --- | --- |
| Schema 校验 | 必填字段、枚举、坐标类型、资源字段 |
| 引用校验 | 展品到展厅、路线到站点、POI 到实体、楼层到设施 |
| 资源校验 | 图片/音频/模型路径存在,远程 URL 可访问或有降级 |
| 业务校验 | 开放时间格式、路线时长总和、展厅展品数可计算 |
| 多语言校验 | 中文必填,英文按上线策略设阈值 |
| 审核状态校验 | 非 `published` 数据不得进入正式包 |
### 4.4 错误处理
当前 `dataLoader.ts` 在异常时返回空数组,页面无法知道错误原因。建议改为:
| 异常类型 | 降级方式 |
| --- | --- |
| JSON 加载失败 | 展示“数据暂不可用”,上报错误码 |
| Schema 校验失败 | 阻止使用该批数据,回退到上一个缓存版本 |
| 资源缺失 | 展示占位图,隐藏播放按钮,上报资源 ID |
| 关联断链 | 从路线/地图中隐藏该站点,保留错误日志 |
| 多语言缺失 | 回退中文,并记录缺失翻译 |
## 5. 性能与优化
### 5.1 数据体积与加载策略
当前 JSON 体积不大;`static/models/` 下模型已确认为自然博物馆 1F 真实 3D 模型资产,不应按普通占位资源处理。但模型资产仍需要元数据和部署治理:
| 资源 | 路径 | 大小 |
| --- | --- | ---: |
| 室内模型 | `static/models/f1-indoor.glb` | 1,562,816 bytes |
| 楼层模型 | `static/models/f1-floor.glb` | 95,720 bytes`facilities.json` 点位坐标来源 |
| 部署副本 | `public/models/f1-indoor.glb``public/models/f1-floor.glb` | 与 `static/models` 同大小;如果两端都会打包,需要明确去重或按端分发 |
风险:
| 问题 | 影响 |
| --- | --- |
| `public/models``static/models` 同时存在 | 若二者同时进入同一端包体,会造成重复;若分别服务 web/小程序,需要 manifest 标注用途 |
| 图片/音频 URL 缺失 | 无法验证真实媒体体积和懒加载策略 |
| 3D 模型与 POI 分离加载 | 需要版本锁定,否则 POI 坐标与模型不匹配 |
建议:
| 优先级 | 建议 |
| --- | --- |
| P1 | 明确模型部署目录策略:同一端只保留一份,跨端复用则在 manifest 中声明 |
| P1 | 模型按楼层拆包并懒加载,非当前楼层不加载 |
| P1 | 图片提供缩略图、详情图、原图三级资源 |
| P2 | 音频使用按需加载,播放前预取下一站讲解 |
### 5.2 请求与预加载
当前没有真实请求层,数据通过动态 import 加载;页面和组件中重复本地数组导致“无请求但重复数据维护”。未来接 API 时建议:
| 场景 | 策略 |
| --- | --- |
| 首页启动 | 加载 `data-manifest`、楼层、轻量 POI、热门展品 |
| 搜索打开 | 延迟构建或读取本地搜索索引 |
| 进入详情 | 按 `entityId` 读取详情,缺资源时显示降级态 |
| 开始路线 | 预加载路线站点、当前位置楼层 POI、下一站音频元信息 |
### 5.3 本地存储与离线可用性
导览类小程序对弱网很敏感,应支持基础离线:
| 数据 | 离线等级 |
| --- | --- |
| 楼层、展厅、设施、核心 POI | 必须离线可用 |
| 展品详情、路线 | 建议离线可用 |
| 图片缩略图 | 建议缓存最近访问和路线相关资源 |
| 音频 | 可选缓存,需考虑版权与包体 |
| 3D 模型 | 按楼层缓存,版本变更后更新 |
## 数据问题清单
### P0 阻塞性问题
| ID | 问题 | 证据 | 影响 | 修复建议 |
| --- | --- | --- | --- | --- |
| D-P0-01 | 核心数据仍是艺术馆内容 | `exhibits.json``蒙娜丽莎` 等世界名画 | 产品定位与内容冲突 | 建立自然博物馆实体数据集并替换核心 JSON |
| D-P0-02 | 多套硬编码实体互相冲突 | 同一 `id: '1'` 分别代表蒙娜丽莎、霸王龙、银杏化石 | 搜索、讲解、详情跳转不可信 | 收敛到统一 `entityId`,禁止页面本地 Mock |
| D-P0-03 | 资源 URL 大量失效 | 5 张展品图、5 个音频、5 张展厅图缺失 | 图片/音频核心体验失败 | 上线前加入资源存在性校验 |
| D-P0-04 | 详情页未按 ID 加载 | `exhibit/detail.vue``hall/detail.vue` 等使用默认对象 | 用户点击任意实体都可能看到同一详情 | 详情页接入 repository `findById` |
| D-P0-05 | 路线仍为艺术馆路线 | `routes.json` 为经典艺术之旅等 | 参观规划完全不适配自然博物馆 | 重建路线数据模型和正式路线 |
### P1 重要问题
| ID | 问题 | 证据 | 影响 | 修复建议 |
| --- | --- | --- | --- | --- |
| D-P1-01 | 楼层引用断链 | `floor_b1` 引用 `parking_b1``storage_b1` | 楼层设施展示不完整 | 补齐设施或删除无效引用 |
| D-P1-02 | 展厅 `exhibitCount` 与实际数量不一致 | `hall_1` 标称 25实际 3 | 统计误导用户 | 改为派生计算或校验阻断 |
| D-P1-03 | 3D POI 未绑定实体 | 67 个 `poi_*``entityId` | 地图无法打开统一详情 | 增加 `entityType/entityId` |
| D-P1-04 | 类型定义不支持 3D/经纬度坐标 | `Position` 只有 `{x,y}` | 地图类型保护失效 | 定义统一 `Coordinate` union |
| D-P1-05 | 设施枚举不完整 | 真实 POI 含入口、母婴室、服务台等 | UI 图标和筛选无法统一 | 建立设施类型字典 |
| D-P1-06 | 无数据版本与缓存策略 | 未见 manifest 和 storage 机制 | 无法增量更新、离线弱 | 新增 `data-manifest` 和本地缓存 |
| D-P1-07 | 错误处理吞掉原因 | `dataLoader` 异常返回 `[]` | 无法区分空数据和加载失败 | 返回 `DataResult<T>` |
| D-P1-08 | 多语言字段缺失 | 核心 JSON 无英文/别名/学名 | 国际访客和英文搜索不可用 | 引入 `LocaleText` 与搜索索引 |
### P2 优化项
| ID | 问题 | 证据 | 影响 | 修复建议 |
| --- | --- | --- | --- | --- |
| D-P2-01 | 模型部署目录未说明 | `static/models``public/models` 同名同大小 | 可能包体重复,也可能是跨端分发但缺少说明 | 建立模型 manifest明确每个端使用哪个路径 |
| D-P2-02 | 搜索工具未统一使用 | 页面自建数组,`utils/search.ts` 被绕开 | 搜索规则重复 | 搜索统一走 index service |
| D-P2-03 | 缺少资源元数据 | 图片/音频只有 URL | 版权、尺寸、时长不可控 | 建立 `Asset` 模型 |
| D-P2-04 | 缺少数据生成文档 | 无数据字典和维护手册 | 后续运营难维护 | 建立数据治理规范 |
## 数据迁移方案:从艺术馆到深圳自然博物馆
### 阶段 0冻结与基线1 到 2 天
目标:防止旧 Mock 继续扩散。
| 任务 | 产出 |
| --- | --- |
| 标记艺术馆数据为 legacy | `legacy-art-gallery` 数据目录或删除计划 |
| 建立数据审计脚本 | 可输出数量、断链、资源缺失 |
| 定义自然博物馆实体清单 | 展厅、展品、设施、路线、POI、资源字段 |
| 统一 ID 规范 | `exhibit_dino_trex_001``hall_dinosaur_2f``facility_restroom_1f_east` |
### 阶段 1核心模型重建3 到 5 天
目标:让所有页面可以读同一套自然博物馆数据。
| 任务 | 产出 |
| --- | --- |
| 重写 TypeScript domain 类型 | `Exhibit` 从艺术品模型改为自然史模型 |
| 新增 `Coordinate``Asset``LocaleText``EntityStatus` | 支撑地图、资源、多语言、上下线 |
| 建立 mock data v1 | 至少 30 个展品、8 个展厅、30 个设施、5 条路线 |
| 改造 `dataLoader` | 返回 typed `DataResult<T>`,支持校验错误 |
| 改造详情页 | 按 URL `id` 从 repository 查询 |
### 阶段 2地图与路线绑定5 到 8 天
目标地图、POI、路线、详情形成闭环。
| 任务 | 产出 |
| --- | --- |
| 为 67 个 F1 POI 增加 `entityType/entityId` | 点击 POI 打开统一详情 |
| 建立楼层坐标系元信息 | `origin``scale``rotation``modelVersion` |
| 把 `TencentMap`/`ThreeMap` 的硬编码 marker 迁出组件 | 地图 view model |
| 路线站点绑定 POI | 路线可在 2D/3D 地图高亮 |
| 增加无障碍/亲子/研学路线 | 覆盖典型人群 |
### 阶段 3资源、多语言与离线5 到 10 天
目标:接近可运营数据质量。
| 任务 | 产出 |
| --- | --- |
| 建立图片、音频资源清单 | 路径、尺寸、时长、版权、语言 |
| 替换占位 URL 和缺失资源 | 所有核心展品可展示/播放 |
| 建立中英文内容字段 | 英文名、学名、英文简介、英文音频 |
| 生成搜索索引 | 支持中文、英文、别名、学名 |
| 增加本地缓存和版本更新 | 弱网可用,支持增量更新 |
### 阶段 4CMS/API 接入2 到 4 周
目标:从静态 Mock 过渡到可维护的馆方内容系统。
| 任务 | 产出 |
| --- | --- |
| 定义 API response envelope | `version/source/updatedAt/errors` |
| 接入 CMS 或后台服务 | 馆方可维护内容 |
| 增加审核流 | draft 到 published |
| 增加数据监控 | 资源失效、断链、翻译缺失告警 |
| 建立回滚机制 | 数据版本可回退 |
## 数据治理建议
### 数据目录建议
```text
src/domain/
guideTypes.ts
guideEnums.ts
guideRepository.ts
guideAdapters.ts
src/assets/data/
data-manifest.json
museum/
exhibits.json
halls.json
facilities.json
floors.json
routes.json
pois.json
assets.json
scripts/
audit-data.ts
build-search-index.ts
```
### 数据质量门禁
| 门禁 | 触发时机 | 阻断规则 |
| --- | --- | --- |
| 本地审计 | 提交前 | P0 不允许提交 |
| CI 审计 | PR/合并前 | P0/P1 必须有处理或豁免 |
| 资源审计 | 打包前 | 核心图片/音频缺失阻断 |
| 内容审计 | 发布前 | 未审核、未发布数据不能进正式包 |
| 多语言审计 | 英文版发布前 | 英文关键字段缺失阻断 |
### 数据字典建议
每个实体至少包含:
| 字段组 | 必填内容 |
| --- | --- |
| 标识 | `id``type``status``version` |
| 展示 | `name``summary``description``keywords` |
| 位置 | `floorId``hallId``coordinates[]``poiIds[]` |
| 资源 | `coverImage``gallery[]``audio[]``modelAssetId` |
| 运营 | `validFrom``validTo``updatedAt``reviewedAt` |
| 多语言 | `zhCN` 必填,`enUS` 按上线范围要求 |
## 预估修复工作量
| 工作包 | 角色 | 预估工时 | 优先级 |
| --- | --- | ---: | --- |
| 数据审计脚本与 CI 门禁 | 前端/工具链 | 1 到 2 人日 | P0 |
| 自然博物馆数据模型重建 | 前端 + 内容策划 | 2 到 3 人日 | P0 |
| 核心 Mock 数据迁移 v1 | 内容策划 + 前端 | 3 到 5 人日 | P0 |
| 详情页/搜索/地图统一接 repository | 前端 | 4 到 6 人日 | P0 |
| POI 与实体绑定 | 前端 + 3D/地图 | 3 到 5 人日 | P1 |
| 资源清单与缺失资源替换 | 内容/设计/前端 | 3 到 6 人日 | P1 |
| 多语言字段与搜索索引 | 前端 + 翻译/内容 | 4 到 8 人日 | P1 |
| 离线缓存与增量更新 | 前端 | 4 到 7 人日 | P1 |
| CMS/API 接入 | 后端 + 前端 | 10 到 20 人日 | P1 |
建议最小可交付排期:
| 周期 | 目标 | 验收标准 |
| --- | --- | --- |
| 短期1 周内 | 解决 P0 | 核心数据为自然博物馆;搜索/详情/地图 ID 统一;资源缺失校验可运行 |
| 中期2 到 4 周 | 解决主要 P1 | POI 绑定、路线可用、缓存和版本机制上线 |
| 长期1 到 2 个月 | 数据治理闭环 | CMS/API、审核流、多语言、监控和回滚机制可用 |
## 下一步行动清单
1. 建立 `scripts/audit-data.ts`,把本报告中的数量、断链、资源缺失、统计不一致变成可重复命令。
2. 先删除或隔离旧艺术馆 Mock 数据,防止新页面继续引用。
3. 制定深圳自然博物馆首批 MVP 数据清单8 个展厅、30 个展品、30 个设施、5 条路线、F1 全量 POI 绑定。
4. 改造 `dataLoader` 为 typed repository详情页、搜索、地图统一从 repository 读取。
5. 在发布流程中加入 P0 数据门禁:资源缺失、引用断链、实体冲突、非自然博物馆内容不得进入正式包。
## 补充审计:以真实 F1 点位为基准
补充前提:`static/data/f1-indoor-pois.json` 是真实 1 楼设施点位信息。因此它不应被视为普通 Mock而应作为校验 1F 设施、展厅、入口、地图点击、搜索和筛选数据的基准。
### 基准数据摘要
| 指标 | 结果 |
| --- | --- |
| POI 总数 | 67 |
| 类型分布 | `facility: 58``hall: 6``entrance: 3` |
| 楼层分布 | 全部为 `1F` |
| 坐标范围 | `x: -106.59 到 150.26``y: 0 到 0``z: -37.73 到 79.06` |
| 重复坐标 | 无 |
| 加载路径 | `Map3DManager` 会从 `/static/models/f1-indoor.glb` 推断并加载 `/static/data/f1-indoor-pois.json` |
真实 1F 点位包含的代表性设施和区域:
```text
展厅/场馆:展厅人类厅、巨幕影院、动感多维影院、自然剧场、球幕影院、展厅
入口/服务:售票处、售票机、服务台
设施:女卫、男卫、无障碍卫生间、楼梯、电梯、扶梯、存包处、轮椅及儿童车租车处、贵宾接待区、贵宾卫生间、茶水间、母婴间
```
### 真实 F1 点位反查出的新增问题
| 严重性 | 问题 | 证据 | 影响 | 建议 |
| --- | --- | --- | --- | --- |
| P0 | `facilities.json` 的 1F 设施与真实点位完全不匹配 | 1F 设施只有 `洗手间``艺术咖啡厅``艺术品商店``主出口`,真实 POI 中均无这些同名点位 | 设施列表、设施详情、楼层设施入口都会展示不存在或不准确的设施 | 以 `f1-indoor-pois.json` 生成 1F 设施实例,旧艺术馆设施迁入 legacy |
| P0 | `halls.json` 的 1F 展厅与真实点位完全不匹配 | 1F 展厅为 `1号展厅``2号展厅`;真实 POI 展厅为 `展厅人类厅``巨幕影院``动感多维影院``自然剧场``球幕影院``展厅` | 地图点位点击与展厅详情无法对应,展厅统计不可用 | 建立真实 1F 展厅/场馆实体,并用 `poiId` 绑定 |
| P0 | 3D 点击逻辑会忽略大部分真实设施点位 | `handlePOIClick` 只处理 `exhibit``hall`;真实 POI 中 58/67 为 `facility` | 用户点击厕所、电梯、楼梯、母婴间、售票处等核心服务点位不会弹出详情 | 点击逻辑必须支持 `facility``entrance`,并进入统一设施详情/导航 |
| P1 | 真实 POI 与 `markerDataMap` 无法映射 | 真实 ID 为 `poi_0``poi_66`;首页映射使用 `0``1``hall-1``exhibit-1` | POI 点击只能走临时 fallback无法获得图片、说明、导航策略、收藏状态 | 用 `poiId``entityId` 建立真实映射,删除手写 `markerDataMap` |
| P1 | `floors.json` 的 1F 设施引用严重不足 | `floor_1f.facilities` 只引用 3 个设施,真实 1F 有 58 个 facility POI 和 3 个 entrance POI | 楼层筛选和设施面板无法覆盖真实服务点 | `floor_1f` 应引用真实 POI 或引用从 POI 派生的 facility instances |
| P1 | 设施类型粒度不足 | 真实 POI 全部压成 `facility/entrance/hall`,具体类别藏在中文 label 中 | 搜索、筛选、图标、无障碍路线无法可靠判断类别 | 增加 `category/subtype`,如 `restroom_female``elevator``stairs``ticket_office``nursing_room` |
| P1 | UI 筛选分类与真实标签没有规范映射 | `AreaSelector``卫生间/电梯/楼梯/停车场/母婴室/服务中心/寄存处/饮水处/影院`,真实标签是 `女卫001``电梯020``楼梯.001``存包处``茶水间` 等 | 用户选择分类后很难精确筛出真实点位 | 建立 label 归一化和分类映射表,区分展示名与检索名 |
| P1 | 搜索数据未覆盖真实 1F 设施 | 搜索 Mock 只有 `洗手间``咖啡厅``服务中心``纪念品商店` 等 | 搜索不到真实存在的售票机、服务台、母婴间、轮椅及儿童车租车处、贵宾卫生间等 | 搜索索引从真实 POI 和设施实体生成 |
| P1 | 存在两套 POI 坐标模型 | 真实 POI 为 `{x,y,z}``ThreeMap.vue` props/default POI 为 `{latitude,longitude}` | 组件接口同名但数据形态不同,后续接入容易错用 | 将室内 3D POI 与室外经纬度 POI 拆成不同类型 |
| P2 | 真实 POI label 带模型导出痕迹 | `电梯001``楼梯.001``无障碍卫生间.001` | 直接展示会显得粗糙,也不利于搜索同义词 | 保留原始 label同时新增 `displayName``instanceNo``normalizedName` |
| P2 | `entrance` 类型语义混杂 | `售票处``售票机``服务台` 被标为 `entrance` | 类型名称无法表达真实服务属性 | 原始类型可保留,业务层补充 `category: ticket/service` |
### 与真实 F1 点位冲突的关键文件
| 文件 | 冲突点 | 处理建议 |
| --- | --- | --- |
| `src/assets/data/facilities.json` | 1F 设施与真实 POI 无同名匹配 | 用真实 POI 重建 1F facilities |
| `src/assets/data/halls.json` | 1F 展厅名称和数量与真实 POI 冲突 | 重建 halls并绑定 `poi_0/poi_51``poi_55` |
| `src/assets/data/floors.json` | 1F 只引用 3 个旧设施 | 从真实 POI 派生楼层设施索引 |
| `src/pages/index/index.vue` | `markerDataMap` 不认识 `poi_*`,点击设施被忽略 | 改为根据 `poiId/entityId` 查询 repository |
| `src/components/map/ThreeMap.vue` | 默认 POI 使用经纬度,和真实室内 `{x,y,z}` POI 混用 | 拆分 `IndoorPOI``OutdoorMarker` |
| `src/types/index.ts` | `Position` 只有 `{x,y}``POIMarker` 不含 `entrance` | 扩展坐标和 POI 类型 |
| `src/components/search/SearchPanel.vue` | 搜索 Mock 未来自真实 POI | 搜索索引从 `pois + entities` 生成 |
| `src/components/area/AreaSelector.vue` | 分类只停留在 UI 文案,没有连接真实 POI subtype | 建立设施分类字典和 label 归一化规则 |
### 建议的数据归一化模型
真实 POI 文件可以继续作为原始点位数据,但业务层需要派生规范实体:
```ts
interface RawIndoorPOI {
id: string
label: string
x: number
y: number
z: number
type: 'hall' | 'facility' | 'entrance'
floor: '1F'
}
interface FacilityInstance {
id: string
poiId: string
floorId: string
displayName: string
normalizedName: string
category: 'restroom' | 'elevator' | 'stairs' | 'escalator' | 'ticket' | 'service' | 'storage' | 'nursing' | 'theater' | 'vip' | 'water' | 'entrance'
accessibility?: string[]
coordinate: { system: 'indoor_3d'; modelId: 'f1-indoor'; x: number; y: number; z: number }
}
```
### 更新后的优先修复顺序
1. P0让 3D POI 点击支持 `facility``entrance`,否则真实 1F 点位 61 个服务类点位无法交互。
2. P0用真实 POI 重建 1F `facilities``halls`,移除艺术馆设施/展厅对 1F 的覆盖。
3. P1建立 `poiId -> entityId` 映射,让地图、搜索、详情、路线共享同一实体。
4. P1新增设施分类字典`女卫001``男卫002``电梯020``楼梯.001` 等归一到可筛选类别。
5. P1`floor_1f` 从真实 POI 派生设施和展厅索引,避免手工维护数量不一致。
6. P2保留原始 label 作为 `sourceLabel`,增加面向用户的 `displayName`,避免模型导出编号直接暴露。
## 补充审计:真实模型与 `f1-floor.glb` 坐标来源
补充前提:`frontend-miniapp/static/models/` 下的模型是自然博物馆 1F 真实 3D 模型;`facilities.json` 中的点位坐标来自 `frontend-miniapp/static/models/f1-floor.glb` 提取,目的是为后续 web 端页面与模型交互准备数据。
因此,对模型和坐标的审计结论需要修正为:模型资产和部分坐标来源可信,不能简单当作 Mock 删除;真正的问题是缺少模型元数据、坐标系说明、实体语义绑定和跨端资源治理。
### 修正后的判断
| 数据/资产 | 可信部分 | 仍存在的问题 |
| --- | --- | --- |
| `static/models/f1-indoor.glb` | 真实 1F 室内 3D 模型 | 缺少 `modelId`、hash、版本、来源、坐标系、比例尺、与 POI 文件的绑定说明 |
| `static/models/f1-floor.glb` | 真实 1F 楼层模型,且是 `facilities.json.position` 的提取来源 | `facilities.json` 未记录 `sourceModel`、提取工具、提取时间、坐标原点、单位、投影规则 |
| `static/data/f1-indoor-pois.json` | 真实 1F POI 点位,使用 `{x,y,z}` 模型空间坐标 | 未绑定业务实体label 仍是模型导出名称,需要业务归一化 |
| `src/assets/data/facilities.json` | `position` 字段有真实模型来源 | 设施名称、类型、描述仍是旧艺术馆语义;坐标字段只有 `{x,y}`,无法判断与 `f1-indoor-pois.json``{x,y,z}` 如何互转 |
| `public/models/*.glb` | 可能用于 web 端静态访问 | 与 `static/models/*.glb` 同名同大小,需说明是否为跨端副本,避免构建重复打包 |
### 新增数据风险
| 严重性 | 问题 | 影响 | 建议 |
| --- | --- | --- | --- |
| P0 | 坐标来源真实,但业务实体语义不真实 | 后续 web 与模型交互可能点位能落到模型上,但弹出的名称、类别、详情仍是错的 | 将 `facilities.json` 拆为“模型点位层”和“业务设施层”,用 `poiId/entityId` 连接 |
| P1 | 缺少模型 manifest | 无法保证 `f1-floor.glb``f1-indoor.glb`、POI JSON、设施坐标来自同一版本 | 新增 `static/models/model-manifest.json``src/assets/data/model-manifest.json` |
| P1 | 缺少坐标系/变换元数据 | `facilities.json.position {x,y}``f1-indoor-pois {x,y,z}` 可能来自不同模型或投影空间,未来 web 交互容易错位 | 为每组坐标声明 `sourceModel``coordinateSystem``unit``origin``scale``axisMapping` |
| P1 | `Map3DManager` 运行时加载 `f1-indoor-pois.json`,但 `facilities.json` 坐标来自 `f1-floor.glb` | 运行态点位和业务设施点位可能使用两套坐标基准 | 明确 `f1-floor``f1-indoor` 的关系,建立统一转换或只让业务层引用同一基准 |
| P2 | 模型部署目录策略不明确 | web 端和小程序端可能各自引用不同路径,造成缓存、版本和包体问题 | 约定 web 用 `public/models``static/models` 之一,并通过 manifest 管理 |
### 建议新增模型清单
```json
{
"models": [
{
"id": "f1-indoor",
"path": "/static/models/f1-indoor.glb",
"purpose": "runtime-3d-indoor-map",
"floor": "1F",
"version": "2026-05-28",
"hash": "<sha256>",
"poiDataset": "/static/data/f1-indoor-pois.json",
"coordinateSystem": {
"type": "model_world_3d",
"unit": "model_unit",
"scaleInMap3DManager": 0.3
}
},
{
"id": "f1-floor",
"path": "/static/models/f1-floor.glb",
"purpose": "floor-point-extraction",
"floor": "1F",
"version": "2026-05-28",
"derivedDatasets": [
"src/assets/data/facilities.json"
],
"coordinateSystem": {
"type": "model_floor_projection",
"unit": "model_unit",
"axisMapping": "to-be-documented"
}
}
]
}
```
### 修正后的下一步
1. 保留 `static/models/f1-indoor.glb``static/models/f1-floor.glb` 作为真实资产,不再按占位资源处理。
2.`facilities.json.position` 增加来源字段,例如 `sourceModel: "f1-floor"``sourceCoordinateSystem: "model_floor_projection"`
3.`facilities.json` 中的业务字段与模型坐标分离:坐标可来自模型,设施名称/类型/描述需按真实自然博物馆内容重建。
4. 建立 `f1-floor` 坐标与 `f1-indoor` POI 坐标的关系说明,否则 web 端点击、3D 高亮和导航路径会有错位风险。
5. 明确 `public/models``static/models` 的分工;如果 `public/models` 是 web 端专用副本,应写入 README/manifest而不是简单删除。

View File

@@ -0,0 +1,272 @@
# 获取建筑围栏坐标指南
## 方法概览
| 方法 | 准确度 | 难度 | 推荐度 |
|------|--------|------|--------|
| 腾讯地图坐标拾取器 | ⭐⭐⭐⭐⭐ | 简单 | ⭐⭐⭐⭐⭐ |
| 卫星图手动标注 | ⭐⭐⭐⭐ | 简单 | ⭐⭐⭐⭐ |
| 建筑平面图转换 | ⭐⭐⭐⭐⭐ | 中等 | ⭐⭐⭐⭐ |
| 估算矩形围栏 | ⭐⭐ | 简单 | ⭐⭐ |
---
## 方法 1腾讯地图坐标拾取器推荐
### 步骤
1. **打开坐标拾取器**
- 访问https://lbs.qq.com/getPoint/
- 或使用https://lbs.qq.com/tool/getpoint/
2. **定位到博物馆**
- 搜索框输入:深圳自然博物馆
- 或直接输入坐标22.692763, 114.363487
3. **切换卫星图**
- 点击右上角地图类型切换按钮
- 选择"卫星图"模式
- 放大到最大级别(可以看清建筑轮廓)
4. **标注建筑角点**
- 沿着建筑外围,从西北角开始
- 顺时针或逆时针依次点击每个角点
- 记录每个点的坐标(纬度, 经度)
5. **复制坐标**
- 将所有角点坐标记录下来
- 按顺序排列(形成闭合多边形)
### 示例
假设标注了6个角点
```javascript
const polygons = ref([
{
points: [
{ latitude: 22.693100, longitude: 114.363100 }, // 西北角
{ latitude: 22.693100, longitude: 114.363500 }, // 北侧转角
{ latitude: 22.693100, longitude: 114.363900 }, // 东北角
{ latitude: 22.692400, longitude: 114.363900 }, // 东南角
{ latitude: 22.692400, longitude: 114.363500 }, // 南侧转角
{ latitude: 22.692400, longitude: 114.363100 }, // 西南角
],
strokeWidth: 3,
strokeColor: '#E0E100',
fillColor: '#E0E10020',
zIndex: 1
}
])
```
---
## 方法 2使用 Google Earth / 百度地图
### Google Earth Pro桌面版
1. 下载安装 Google Earth Pro
2. 搜索"深圳自然博物馆"
3. 使用"添加多边形"工具
4. 沿建筑外围绘制多边形
5. 右键 → 属性 → 查看坐标
6. 导出为 KML 文件,提取坐标
### 百度地图拾取坐标
1. 访问https://api.map.baidu.com/lbsapi/getpoint/
2. 搜索定位到博物馆
3. 切换卫星图
4. 依次点击建筑角点
5. **注意**:百度地图使用 BD-09 坐标系,需要转换为 GCJ-02
---
## 方法 3使用 API 脚本获取
### 运行脚本
```bash
cd museum-guide-v4.0
node scripts/get-building-polygon.js
```
### 脚本功能
1. 调用腾讯地图 WebService API
2. 搜索博物馆 POI 信息
3. 尝试获取建筑轮廓数据
4. 如果没有轮廓数据,计算估算的矩形围栏
### 调整建筑尺寸
如果使用估算方法,需要知道建筑的实际尺寸:
```javascript
// 修改脚本中的参数
calculateRectangle(
22.692763, // 中心纬度
114.363487, // 中心经度
150, // 宽度(米)- 根据实际调整
100 // 高度(米)- 根据实际调整
)
```
---
## 方法 4从建筑平面图转换
如果有建筑平面图或 CAD 图纸:
### 步骤
1. **确定参考点**
- 在平面图上标注一个已知坐标的点(如主入口)
- 确定平面图的方向(正北方向)
2. **测量相对位置**
- 测量每个角点相对于参考点的距离和方向
- 单位:米
3. **计算坐标**
```javascript
// 纬度1度 ≈ 111km
// 经度1度 ≈ 111km × cos(纬度)
const latPerMeter = 1 / 111000
const lngPerMeter = 1 / (111000 * Math.cos(22.692763 * Math.PI / 180))
// 从参考点向北50米向东30米
const newLat = refLat + 50 * latPerMeter
const newLng = refLng + 30 * lngPerMeter
```
4. **验证坐标**
- 在地图上标注计算出的坐标
- 对比卫星图验证准确性
---
## 方法 5实地测量最准确
使用 GPS 设备或手机 APP
### 推荐 APP
- **奥维互动地图**(支持多种坐标系)
- **两步路户外助手**
- **GPS Test**Android
### 步骤
1. 到博物馆现场
2. 打开 GPS APP
3. 走到建筑每个角点
4. 记录 GPS 坐标
5. 确保使用 GCJ-02 坐标系(火星坐标)
---
## 坐标系转换
### 常见坐标系
- **WGS-84**GPS 原始坐标,国际标准
- **GCJ-02**:火星坐标,中国加密坐标(腾讯地图使用)
- **BD-09**:百度坐标,百度地图专用
### 转换工具
- 在线转换https://tool.lu/coordinate/
- npm 包:`coordtransform`
```bash
npm install coordtransform
```
```javascript
const coordtransform = require('coordtransform')
// WGS-84 转 GCJ-02
const [lng, lat] = coordtransform.wgs84togcj02(114.363487, 22.692763)
// BD-09 转 GCJ-02
const [lng2, lat2] = coordtransform.bd09togcj02(114.369487, 22.698763)
```
---
## 验证围栏准确性
### 在地图上检查
1. 将坐标添加到 `TencentMap.vue`
2. 运行项目查看地图
3. 对比卫星图底图
4. 调整不准确的点
### 计算围栏面积
```javascript
// 使用 Shoelace 公式计算多边形面积
function calculateArea(points) {
let area = 0
for (let i = 0; i < points.length; i++) {
const j = (i + 1) % points.length
area += points[i].longitude * points[j].latitude
area -= points[j].longitude * points[i].latitude
}
area = Math.abs(area) / 2
// 转换为平方米(近似)
const latPerMeter = 1 / 111000
const lngPerMeter = 1 / (111000 * Math.cos(points[0].latitude * Math.PI / 180))
const areaInSquareMeters = area / (latPerMeter * lngPerMeter)
return areaInSquareMeters
}
```
---
## 常见问题
### Q: 为什么我的围栏位置偏移?
A: 可能的原因:
1. 坐标系不匹配(检查是否为 GCJ-02
2. 纬度经度顺序错误(应为 latitude, longitude
3. 地图缩放级别太低,看不清细节
### Q: 如何绘制不规则建筑?
A: 增加更多角点,多边形支持任意数量的点:
```javascript
points: [
{ latitude: 22.693100, longitude: 114.363100 },
{ latitude: 22.693100, longitude: 114.363300 },
{ latitude: 22.693050, longitude: 114.363500 }, // 斜角
{ latitude: 22.692900, longitude: 114.363700 }, // 弧形可用多个点近似
// ... 更多点
]
```
### Q: 围栏太大或太小?
A: 检查坐标单位:
- 纬度/经度差 0.0001 ≈ 11米/9米
- 纬度/经度差 0.001 ≈ 110米/90米
---
## 推荐流程
1. ✅ **使用腾讯地图坐标拾取器**手动标注5-10分钟
2. ✅ 将坐标复制到代码中
3. ✅ 运行项目验证
4. ✅ 微调不准确的点
5. ✅ 完成!
这是最快速、最准确的方法。

View File

@@ -0,0 +1,143 @@
# 深圳自然博物馆地图配置说明
## 地图中心坐标
当前使用的是示例坐标,需要替换为实际的深圳自然博物馆坐标。
### 如何获取实际坐标
1. **使用腾讯地图拾取坐标工具**
- 访问https://lbs.qq.com/getPoint/
- 搜索"深圳自然博物馆"
- 点击地图上的建筑位置
- 复制经纬度坐标
2. **使用高德地图坐标拾取**
- 访问https://lbs.amap.com/tools/picker
- 搜索并定位博物馆
- 获取坐标(需转换为 GCJ-02 坐标系)
## 建筑外围标记配置
`TencentMap.vue` 中的 `polygons` 数组配置建筑轮廓:
```typescript
const polygons = ref<Polygon[]>([
{
points: [
{ latitude: 22.5433, longitude: 114.0577 }, // 西北角
{ latitude: 22.5433, longitude: 114.0581 }, // 东北角
{ latitude: 22.5429, longitude: 114.0581 }, // 东南角
{ latitude: 22.5429, longitude: 114.0577 }, // 西南角
],
strokeWidth: 3,
strokeColor: '#E0E100', // 黄色边框
fillColor: '#E0E10020', // 半透明黄色填充
zIndex: 1
}
])
```
### 配置说明
- **points**: 建筑外围的坐标点数组(按顺时针或逆时针顺序)
- **strokeWidth**: 边框宽度(像素)
- **strokeColor**: 边框颜色(十六进制)
- **fillColor**: 填充颜色(十六进制,最后两位是透明度)
- **zIndex**: 层级(数值越大越在上层)
### 获取建筑轮廓坐标的方法
1. **手动标记**
- 在腾讯地图拾取坐标工具中
- 依次点击建筑的各个角点
- 记录每个点的经纬度
2. **使用卫星图**
- 在地图上切换到卫星视图
- 更清晰地看到建筑轮廓
- 标记关键点位
3. **简化轮廓**
- 如果建筑形状复杂,可以简化为矩形或多边形
- 只需标记主要的转角点
## 标记点配置
### 博物馆主标记
```typescript
{
id: 0,
latitude: 22.5431,
longitude: 114.0579,
iconPath: '/static/icons/marker-museum.png',
width: 40,
height: 40,
title: '深圳自然博物馆',
callout: {
content: '深圳自然博物馆',
display: 'ALWAYS', // 始终显示
bgColor: '#E0E100',
color: '#000000'
}
}
```
### 入口标记
```typescript
{
id: 1,
latitude: 22.5429,
longitude: 114.0579,
iconPath: '/static/icons/marker-entrance.png',
title: '主入口'
}
```
## 需要准备的图标
- `/static/icons/marker-museum.png` - 博物馆主标记40x40px
- `/static/icons/marker-entrance.png` - 入口标记30x30px
- `/static/icons/marker-hall.png` - 展厅标记25x25px
- `/static/icons/marker-exhibit.png` - 展品标记25x25px
## 坐标系说明
- **GCJ-02**: 国测局坐标系(火星坐标系)
- 腾讯地图、高德地图使用此坐标系
- GPS 原始坐标需要转换
## 调试建议
1. 先设置地图中心点
2. 调整缩放级别scale: 18-20
3. 添加建筑外围多边形
4. 微调多边形坐标点
5. 添加内部标记点
## 示例:矩形建筑
```typescript
// 假设建筑是一个矩形,长 100 米,宽 80 米
const centerLat = 22.5431
const centerLng = 114.0579
// 经纬度偏移量(约 1 度 ≈ 111 公里)
const latOffset = 0.00045 // 约 50 米
const lngOffset = 0.00036 // 约 40 米
const polygons = [{
points: [
{ latitude: centerLat + latOffset, longitude: centerLng - lngOffset },
{ latitude: centerLat + latOffset, longitude: centerLng + lngOffset },
{ latitude: centerLat - latOffset, longitude: centerLng + lngOffset },
{ latitude: centerLat - latOffset, longitude: centerLng - lngOffset },
],
strokeWidth: 3,
strokeColor: '#E0E100',
fillColor: '#E0E10020',
zIndex: 1
}]
```

109
docs/MAP_MARKERS_GUIDE.md Normal file
View File

@@ -0,0 +1,109 @@
# 地图标记图标说明
## 图标列表
### 用户定位标记
- **文件**: `marker-user-location.svg`
- **样式**: 黑色圆圈 + 黄色定位针 + 半透明外圈
- **尺寸**: 40×40px
- **用途**: 显示用户当前位置
- **特点**:
- 外圈半透明黑色opacity: 0.2
- 内圈黑色,黄色边框
- 中心黄色指针图标
### 博物馆主标记
- **文件**: `marker-museum.svg`
- **样式**: 黄色圆形 + 博物馆建筑图标
- **尺寸**: 40×40px
- **用途**: 标记博物馆主建筑位置
- **特点**: 黄色背景,黑色建筑图标
### 入口标记
- **文件**: `marker-entrance.svg`
- **样式**: 黑色圆点 + 白色边框
- **尺寸**: 30×30px
- **用途**: 标记入口位置
### 展厅标记
- **文件**: `marker-hall.svg`
- **样式**: 红色圆点 + 白色边框
- **尺寸**: 30×30px
- **用途**: 标记展厅位置
- **颜色**: #FF6B6B
### 展品标记
- **文件**: `marker-exhibit.svg`
- **样式**: 青色圆点 + 白色边框
- **尺寸**: 30×30px
- **用途**: 标记展品位置
- **颜色**: #5ED0E4
### 设施标记
- **文件**: `marker-facility.svg`
- **样式**: 绿色圆点 + 白色边框
- **尺寸**: 30×30px
- **用途**: 标记设施位置(洗手间、咖啡厅等)
- **颜色**: #4ECDC4
### 通用 POI 标记
- **文件**: `marker-poi.svg`
- **样式**: 青色圆点 + 白色边框
- **尺寸**: 30×30px
- **用途**: 通用兴趣点标记
## 标记 ID 规范
- **0**: 博物馆主标记
- **1-99**: 入口标记
- **100-199**: 展厅标记
- **200-299**: 展品标记
- **300-399**: 设施标记
- **999**: 用户当前位置(动态添加)
## 使用说明
### 添加标记
```typescript
markers.value.push({
id: 101,
latitude: 22.692863,
longitude: 114.363687,
iconPath: '/static/icons/marker-hall.svg',
width: 30,
height: 30,
title: '2号展厅',
callout: {
content: '2号展厅',
display: 'BYCLICK',
padding: 8,
borderRadius: 4,
bgColor: '#FF6B6B',
color: '#FFFFFF',
fontSize: 12
}
})
```
### 用户定位
点击定位按钮后会自动添加用户位置标记ID: 999使用 `marker-user-location.svg` 图标。
## 设计规范
- **圆点标记**: 直径 16px白色边框 2px
- **大标记**: 40×40px用户位置、博物馆主标记
- **小标记**: 30×30px其他所有标记
- **边框**: 白色2px 宽度
- **阴影**: 可选,用于增强立体感
## 颜色方案
| 类型 | 颜色代码 | 颜色名称 |
|------|---------|---------|
| 强调/当前位置 | #E0E100 | 黄色 |
| 入口 | #333333 | 黑色 |
| 展厅 | #FF6B6B | 红色 |
| 展品 | #5ED0E4 | 青色 |
| 设施 | #4ECDC4 | 绿色 |

View File

@@ -0,0 +1,81 @@
# 深圳自然博物馆地图配置
## 博物馆信息
- **名称**: 深圳自然博物馆
- **坐标**: 22.692763, 114.363487
- **POI ID**: 7043042949572197975
- **地址**: 广东省深圳市坪山区
## 地图配置
### 中心点坐标
```typescript
const mapCenter = {
latitude: 22.692763,
longitude: 114.363487
}
```
### 建筑外围多边形
建筑轮廓采用矩形标记(约 100m × 80m
```typescript
const polygons = [{
points: [
{ latitude: 22.693163, longitude: 114.363087 }, // 西北角
{ latitude: 22.693163, longitude: 114.363887 }, // 东北角
{ latitude: 22.692363, longitude: 114.363887 }, // 东南角
{ latitude: 22.692363, longitude: 114.363087 }, // 西南角
],
strokeWidth: 3,
strokeColor: '#E0E100',
fillColor: '#E0E10020',
zIndex: 1
}]
```
### 标记点配置
| ID | 名称 | 纬度 | 经度 | 类型 |
|----|------|------|------|------|
| 0 | 深圳自然博物馆 | 22.692763 | 114.363487 | 主标记 |
| 1 | 主入口 | 22.692363 | 114.363487 | 入口 |
| 2 | 1号展厅 | 22.692863 | 114.363687 | 展厅 |
| 3 | 恐龙化石展区 | 22.692663 | 114.363287 | 展品 |
## 坐标偏移参考
在当前坐标系下:
- 纬度 +0.0001 ≈ 北移 11 米
- 经度 +0.0001 ≈ 东移 9 米
- 纬度 +0.0004 ≈ 北移 44 米
- 经度 +0.0004 ≈ 东移 36 米
## 调整建议
1. **建筑轮廓微调**
- 使用腾讯地图卫星视图查看实际建筑形状
- 根据实际轮廓调整多边形坐标点
- 如果建筑不是矩形,可添加更多坐标点
2. **标记点位置**
- 根据实际楼层平面图调整展厅位置
- 入口位置应对应实际入口
- 展品位置根据实际展陈布局设置
3. **缩放级别**
- 当前设置为 19较高缩放
- 可根据需要调整为 17-20 之间
## 腾讯地图 POI 信息
- **POI ID**: 7043042949572197975
- 可用于调用腾讯地图 API 获取更多信息
- 可用于路线规划和导航功能
## 更新日期
2026-05-21

View File

@@ -0,0 +1,195 @@
# H5/小程序业务流程测试报告
日期2026-06-09
项目:深圳自然博物馆智能导览应用 - frontend-miniapp
审计范围:导览、讲解、搜索、详情、设施导航、路线规划闭环
## 结论
Production audit48/100Blocked。H5 与微信小程序都能构建成功,但关键用户任务没有形成闭环:用户可以点击入口、跳转页面、看到局部状态变化,却无法稳定完成“找内容 -> 看详情/听讲解 -> 到达目的地/结束任务”的业务目标。
这不是单个 UI 问题,而是业务对象没有贯穿全链路:页面层大量使用硬编码 mock 数据,路由 ID 没有被详情页真实加载,地图 POI/搜索结果/讲解列表/详情页/路线页之间不是同一套数据。
## 使用的本项目 skills
本次只使用项目路径下的本地 skills没有使用全局安装
- `.agents/skills/ux-review/SKILL.md`:按可用性、状态反馈、错误/空态/成功态检查。
- `.agents/skills/production-audit/SKILL.md`:按上线风险、证据、阻塞项给出分数。
- `.agents/skills/user-flows-and-guided-paths/SKILL.md`:检查流程是否有明确起点、步骤、回退、完成态。
- `.agents/skills/playwright-e2e-tester/SKILL.md`:整理应补的端到端测试用例。
## 验证结果
| 项目 | 结果 | 说明 |
| --- | --- | --- |
| `pnpm build:h5` | 通过 | 产物可构建Sass legacy API 有弃用警告。 |
| `pnpm build:mp-weixin` | 通过 | 微信小程序产物可构建Sass legacy API 有弃用警告。 |
| `pnpm type-check` | 失败 | 失败于 `vue-tsc@1.8.27` 与当前 TypeScript/Node 组合的工具链补丁:`Search string not found: "/supportedTSExtensions = .*(?=;)/"`。 |
| 自动化 E2E | 未覆盖 | 未发现 `playwright.config.*``vitest.config.*` 或测试用例文件。 |
| Git 基线 | 无提交 | 当前仓库 `No commits yet on master`,本报告基于当前工作区文件。 |
## 关键业务问题
### P0自然馆产品壳与艺术馆数据混用
证据:
- `src/pages.json:52` 全局标题是“深圳自然博物馆”。
- `src/assets/data/exhibits.json:4` 仍是“蒙娜丽莎”。
- `src/assets/data/halls.json:6` 仍是“文艺复兴时期艺术作品展厅”。
- `src/assets/data/routes.json:4` 仍是“经典艺术之旅”。
- `src/assets/data/facilities.json:17``src/assets/data/facilities.json:30` 仍有“艺术咖啡厅”“艺术品商店”。
影响:用户在讲解、搜索、详情、路线里看到的对象不属于同一个博物馆业务域。即使页面跳转成功,也无法说明用户完成了自然馆导览任务。
建议:先统一一份自然馆业务数据契约,至少包含 `id/type/name/floor/hallId/audioUrl/position/routeTarget`,然后让讲解、搜索、详情、路线全部从同一数据源读取。
### P0详情页接收 ID 但不加载对应业务数据
证据:
- `src/pages/exhibit/detail.vue:82` 读取 `options.id` 后只 `console.log`
- `src/pages/exhibit/detail.vue:68` 默认展品固定为“蒙娜丽莎”。
- `src/pages/hall/detail.vue:72` 读取 `options.id` 后只 `console.log`
- `src/pages/hall/detail.vue:58``src/pages/hall/detail.vue:68` 展厅和展品列表固定写死。
- `src/utils/dataLoader.ts:61``src/utils/dataLoader.ts:73` 已经有 `findExhibitById``findFacilityById`,但页面没有使用。
影响:搜索结果、地图 marker、讲解列表跳到详情页后用户看到的不一定是刚才点的对象。流程在“详情确认”这一步断掉。
建议:详情页必须用路由参数查询统一数据源;查不到时给明确空态/返回路径,不能显示默认假数据。
### P0讲解链路没有真实音频闭环
证据:
- `src/components/explain/ExplainList.vue:293``src/components/explain/ExplainList.vue:300` 多个展品 `hasAudio: true`,但 `audioUrl: ''`
- `src/pages/index/index.vue:377``src/pages/index/index.vue:426` 对空音频使用 `https://example.com/audio.mp3` 兜底。
- `src/pages/exhibit/detail.vue:89` 的详情页播放按钮只切换 `isPlaying` 并打印日志。
影响:用户点击“讲解”或音频按钮后,可能进入不可控的外部示例音频,或只看到本地播放状态变化。用户无法确认“当前展品讲解已开始、可暂停、可结束、可回到展品”。
建议:去掉 `example.com` 兜底;无音频时显示“暂无讲解”;有音频时使用真实 `audioUrl`,并在列表、详情、底部播放器之间共享播放状态。
### P0室内 3D/路线导航仍是演示态
证据:
- `src/components/navigation/GuideMapShell.vue:5``src/components/navigation/GuideMapShell.vue:8` 室内地图分支是 `/static/images/guide-indoor-3d-bg.png` 静态图。
- `src/components/navigation/GuideMapShell.vue:11` 室外分支已使用原 `TencentMap`,但室内没有使用 `ThreeMap`
- `src/pages/route/detail.vue:156` 路线固定指向“1F 南侧卫生间”。
- `src/pages/route/detail.vue:358` 开始导航只是把 `navigationState` 改成 `navigating`
- `src/pages/route/detail.vue:377` 返回室内继续也只是改状态。
影响:用户从设施详情点击“开始导航”后,看起来进入了路线页,但没有真实路径、定位、楼层切换、到达判定,也没有和 3D 模型或地图 POI 绑定。
建议:将路线页目标、起点、路径段、楼层、地图渲染层统一绑定;室内模式接入 `ThreeMap` 或明确降级为 2D 平面图,不能用静态设计图冒充可导航地图。
### P1室外 TencentMap 恢复了,但 marker 交互不闭环
证据:
- `src/components/navigation/GuideMapShell.vue:11` 当前室外地图使用 `TencentMap`,这是正确方向。
- `src/components/map/TencentMap.vue:239` marker 点击先 `emit('markerClick')`
- `src/components/map/TencentMap.vue:244``src/components/map/TencentMap.vue:250` 同一个点击又立即 `navigateTo`
- `src/pages/index/index.vue:232``src/pages/index/index.vue:282` 首页准备了 marker 详情、导航、讲解、收藏等处理函数,但 `GuideMapShell` 没有把 `TencentMap` 的 marker 事件继续暴露给首页。
影响marker 点击到底应该打开底部卡片,还是直接跳详情页,当前没有统一。用户无法在地图上完成“看点位 -> 选择讲解/导航/收藏”的预期动作。
建议:`TencentMap` 只负责发出 marker 事件;由 `GuideMapShell`/页面决定打开 sheet、跳详情或开始导航。避免组件内部自行跳转。
### P1搜索页不是综合搜索关键词没有真正驱动结果
证据:
- `src/pages/search/index.vue:85` 搜索页使用本地设施数组。
- `src/pages/search/index.vue:112``src/pages/search/index.vue:119` 只按筛选项过滤设施。
- `src/pages/search/index.vue:123` `all` 模式固定显示 6 条,但实际设施数组只有 3 条。
- `src/pages/search/index.vue:133` 搜索框点击只打印“保持搜索结果页”。
- `src/utils/search.ts:70` 已有 `searchAll`,但搜索页没有接入。
影响:用户从首页或设施详情带关键词进入搜索页后,看到的是固定设施列表,不是围绕关键词的展品/展厅/设施结果。搜索任务没有“查找 -> 确认 -> 进入详情/导航”的闭环。
建议:搜索页接入 `loadExhibits/loadHalls/loadFacilities + searchAll`,按类型展示结果,并根据 `type` 跳到对应详情页。
### P1“讲解”不是独立页面而是首页 tab但缺少独立业务边界
证据:
- `src/pages.json:2``src/pages.json:42` 注册页面只有首页、搜索、展品/展厅/设施详情、路线详情。
- `src/pages.json:61` `tabBar.list` 为空。
- `src/pages/index/index.vue:127``src/pages/index/index.vue:128` 首页只有 `导览``讲解` 两个内容 tab。
- `src/pages/index/index.vue:81` 讲解分支渲染 `ExplainList`
结论:原有“讲解”能力没有完全消失,但它不是独立页面,而是被内嵌为首页 tab。当前点击“讲解”不会进入室内模型它进入 `ExplainList`。真正的问题是讲解 tab 内的数据、音频、详情页没有连成闭环。
建议:如果产品需要“讲解”作为一级业务,应明确它是首页 tab 还是独立页面;然后补齐分享/返回/深链/播放状态保存规则。
### P1导航与操作按钮多为 toast/log/返回上一页
证据:
- `src/pages/exhibit/detail.vue:94``src/pages/exhibit/detail.vue:96` 展品详情“导航”只是 `navigateBack()`
- `src/pages/hall/detail.vue:84``src/pages/hall/detail.vue:86` 展厅详情“导航到展厅”只是 `navigateBack()`
- `src/pages/facility/detail.vue:73` “选择起点”只是 toast。
- `src/pages/route/detail.vue:420` 工具按钮主要打印日志或本地切换状态。
影响:用户点击关键按钮后没有进入下一步任务,也没有明确失败原因。界面有按钮,但业务没有提交、没有完成、没有恢复路径。
建议:每个主按钮必须有确定结果:进入路线页、打开起点选择、保存收藏、启动音频、到达完成页或显示可恢复错误。
## 用户流程闭环检查
| 流程 | 当前状态 | 断点 | 闭环建议 |
| --- | --- | --- | --- |
| 导览首页 -> 室外地图 -> 点 marker -> 详情/导航/讲解 | 未闭环 | marker 组件内部直接跳转,首页 sheet 逻辑未接上ID 与详情数据不一致。 | marker 只发事件,页面统一打开 POI sheetsheet 操作分别进入详情、路线、音频。 |
| 导览首页 -> 室内 3D -> 选 POI -> 路线 | 未闭环 | 室内是静态图,未接 `ThreeMap`、POI、路径。 | 接入真实室内地图/3D 场景POI ID 与路线目标一致。 |
| 搜索关键词 -> 结果 -> 详情 -> 导航 | 未闭环 | 搜索页不使用关键词做综合搜索;详情页不加载 ID详情导航返回上一页。 | 搜索接统一数据源;详情按 ID 渲染;导航按钮带目标进入路线页。 |
| 讲解 -> 筛选/搜索 -> 展品 -> 播放音频 -> 结束/返回 | 未闭环 | `activeFilter` 对数据组织影响弱;音频 URL 为空却标记可播放;详情页播放是假状态。 | 按展厅/主题真实分组;无音频禁用播放;播放器跨列表/详情共享状态。 |
| 设施详情 -> 选择起点 -> 开始导航 -> 到达 | 未闭环 | 选择起点没有输入结果;路线页固定目标;开始导航只切状态。 | 起点选择写入 route query/store路线页按起终点生成路径提供到达/结束态。 |
| 路线中 -> 查看室外地图 -> 返回室内继续 | 部分演示 | 暂停/恢复只改 `navigationState`,未保留地图层、楼层、进度。 | 保存 route session室外/室内切换只换展示层,不丢路径和当前步骤。 |
## 建议补充的 E2E 用例
按照 `playwright-e2e-tester` skill本项目至少需要以下端到端用例作为业务闭环验收
1. 点击首页“讲解”后,应显示讲解列表,不应加载室内 3D/静态室内地图。
2. 首页“导览”室外模式应渲染 `TencentMap` 容器,并能点击 marker 打开 POI 操作面板。
3. 搜索“卫生间”应只展示匹配设施;点击结果进入对应设施详情;点击“开始导航”进入路线页并保留目标 ID。
4. 讲解列表中 `audioUrl` 为空的展品应显示“暂无讲解”或禁用播放,不应请求 `example.com`
5. 点击自然馆展品讲解,应进入同一个展品详情,并能播放同一个音频对象。
6. 路线页从 planning -> navigating -> paused -> navigating -> arrived 应保留目标、楼层、路径步骤。
7. 详情页传入不存在的 ID 时,应显示空态与返回路径,不应显示默认“蒙娜丽莎”。
## 修复优先级
1. 建立统一自然馆数据模型,替换艺术馆 mock 数据。
2. 让详情页、搜索页、讲解页全部接入 `dataLoader`/`searchAll`,消除硬编码默认对象。
3. 移除 `https://example.com/audio.mp3`,补真实音频状态和无音频状态。
4. 拆清 `TencentMap` 责任:地图只发事件,页面负责业务动作。
5. 路线页接入真实目标、起点和路径状态;室内地图不要再用静态设计图冒充导航。
6. 修复 `vue-tsc` 工具链版本,让类型检查成为有效质量门。
7. 增加 Playwright 冒烟用例覆盖“导览、讲解、搜索、详情、路线”五条主链路。
## Evidence checked
- `src/pages.json`
- `src/pages/index/index.vue`
- `src/components/navigation/GuideMapShell.vue`
- `src/components/map/TencentMap.vue`
- `src/components/map/ThreeMap.vue`
- `src/components/explain/ExplainList.vue`
- `src/components/search/SearchPanel.vue`
- `src/components/audio/AudioPlayer.vue`
- `src/pages/search/index.vue`
- `src/pages/exhibit/detail.vue`
- `src/pages/hall/detail.vue`
- `src/pages/facility/detail.vue`
- `src/pages/route/detail.vue`
- `src/assets/data/exhibits.json`
- `src/assets/data/halls.json`
- `src/assets/data/facilities.json`
- `src/assets/data/routes.json`
- `src/utils/dataLoader.ts`
- `src/utils/search.ts`
- `package.json`

View File

@@ -0,0 +1,146 @@
# 用户使用流程图
日期2026-06-09
范围:导览、讲解、搜索、详情、路线导航
## 当前用户流(含断点)
```mermaid
flowchart TD
A["用户打开小程序/H5"] --> B["首页"]
B --> C{"选择一级内容"}
C -->|"导览"| D["导览页地图"]
D --> E{"地图模式"}
E -->|"室外"| F["TencentMap 室外地图"]
E -->|"室内 3D"| G["室内静态设计图"]
F --> H["点击 marker"]
H --> H1["组件内部直接跳详情"]
H --> H2["首页预期打开 POI 操作卡片"]
H2 -. "事件未接通" .-> X1["断点:无法稳定选择详情/导航/讲解"]
G -. "未接 ThreeMap/POI/路径" .-> X2["断点:室内导航只是展示图"]
D --> I["点击搜索"]
I --> J["搜索页"]
J --> J1["固定设施列表"]
J1 -. "关键词未驱动综合搜索" .-> X3["断点:搜索结果与用户意图不一致"]
J1 --> K["设施详情"]
K --> K1["选择起点"]
K1 -. "只 toast" .-> X4["断点:起点没有写入路线"]
K --> L["开始导航"]
L --> M["路线详情"]
M --> M1["固定目标/本地 navigationState"]
M1 -. "无真实路径/定位/到达判定" .-> X5["断点:导航无法完成"]
C -->|"讲解"| N["首页讲解 Tab"]
N --> O["讲解列表"]
O --> P{"用户操作"}
P -->|"搜索/筛选"| P1["本地 mock 列表"]
P1 -. "筛选未形成真实分组" .-> X6["断点:讲解发现路径不稳定"]
P -->|"点击展品"| Q["展品详情"]
Q --> Q1["默认硬编码展品"]
Q1 -. "路由 ID 未加载真实数据" .-> X7["断点:详情不是刚点击的对象"]
P -->|"播放音频"| R["底部播放器"]
R --> R1["audioUrl 为空时使用 example.com"]
R1 -. "无真实音频/无不可用状态" .-> X8["断点:讲解播放无法验收"]
Q --> S["详情页导航"]
S -. "navigateBack" .-> X9["断点:详情无法发起路线"]
```
## 目标闭环用户流
```mermaid
flowchart TD
A["用户打开小程序/H5"] --> B["首页"]
B --> C{"用户目标"}
C -->|"我要找位置/设施"| D["导览"]
D --> D1{"选择地图层"}
D1 -->|"室外"| D2["TencentMap 室外地图"]
D1 -->|"室内"| D3["ThreeMap 或真实室内平面图"]
D2 --> E["点击 POI"]
D3 --> E
E --> F["POI 操作卡片"]
F -->|"看详情"| G["对应详情页"]
F -->|"到这里"| H["选择起点"]
F -->|"听讲解"| I["打开对应讲解音频"]
C -->|"我要搜索"| J["搜索页"]
J --> J1["输入关键词"]
J1 --> J2["综合搜索:展品/展厅/设施"]
J2 --> J3{"选择结果类型"}
J3 -->|"展品"| G1["展品详情"]
J3 -->|"展厅"| G2["展厅详情"]
J3 -->|"设施"| G3["设施详情"]
G1 --> G
G2 --> G
G3 --> G
C -->|"我要听讲解"| K["讲解"]
K --> K1["按展厅/主题分组"]
K1 --> K2["选择展品"]
K2 --> K3{"音频是否可用"}
K3 -->|"可用"| I
K3 -->|"不可用"| K4["显示暂无讲解"]
I --> I1["播放/暂停/进度/结束"]
I1 --> I2["回到展品详情或讲解列表"]
G --> G4{"详情页主操作"}
G4 -->|"开始导航"| H
G4 -->|"播放讲解"| I
G4 -->|"收藏/分享"| G5["保存状态并反馈"]
H --> H1["确认当前位置或手动选择起点"]
H1 --> H2["生成起点到目标路径"]
H2 --> H3["路线规划页"]
H3 --> H4["开始导航"]
H4 --> H5["导航中:楼层/步骤/距离/方向"]
H5 --> H6{"用户中途操作"}
H6 -->|"查看室外"| H7["切换室外地图层并保留路线"]
H7 --> H5
H6 -->|"暂停/继续"| H8["保留当前进度"]
H8 --> H5
H6 -->|"到达"| H9["到达完成页"]
H9 --> H10["下一步:继续游览/返回首页/听该点讲解"]
```
## 主流程闭环标准
| 流程 | 起点 | 必要中间态 | 完成态 |
| --- | --- | --- | --- |
| 导览找点 | 首页导览 | 地图层、POI 卡片、详情或路线选择 | 到达 POI或返回地图继续浏览 |
| 搜索找内容 | 搜索入口 | 关键词、综合结果、类型详情 | 进入详情,或发起导航/播放讲解 |
| 讲解收听 | 首页讲解 | 分组/搜索、展品、音频可用性判断 | 播放结束,返回展品或讲解列表 |
| 设施导航 | 设施详情 | 起点确认、路径生成、导航中状态 | 到达完成页,有下一步动作 |
| 详情转导航 | 展品/展厅/设施详情 | 目标 ID、起点、路线规划 | 到达目标,不是简单返回上一页 |
## 页面与数据关系
```mermaid
flowchart LR
DS["统一自然馆数据源"] --> S["搜索页"]
DS --> E["讲解列表"]
DS --> D["详情页"]
DS --> M["地图 POI"]
DS --> R["路线规划"]
S -->|"result.type + result.id"| D
E -->|"exhibit.id"| D
M -->|"poi.id + poi.type"| D
D -->|"target.id + target.type"| R
R -->|"routeSession"| M
E -->|"audioUrl"| P["播放器"]
D -->|"audioUrl"| P
```
## 落地顺序
1. 统一自然馆数据源和 ID 规则。
2. 搜索页、讲解页、详情页全部按统一数据源渲染。
3. 地图 marker 只发 POI 事件,由页面统一打开操作卡片。
4. 详情页主按钮明确进入路线页或播放器。
5. 路线页保存 `routeSession`,覆盖起点、目标、路径、楼层、当前步骤和完成态。

87
docs/TENCENT_MAP_GUIDE.md Normal file
View File

@@ -0,0 +1,87 @@
# 腾讯地图集成说明
## 配置步骤
### 1. 获取腾讯地图 Key
1. 访问 [腾讯位置服务](https://lbs.qq.com/)
2. 注册并登录账号
3. 进入控制台 -> 应用管理 -> 我的应用
4. 创建应用并添加 Key
5. 选择产品类型:
- **H5**: WebServiceAPI
- **微信小程序**: 微信小程序
- **App**: 移动应用
### 2. 配置 Key
`src/manifest.json` 中配置:
```json
{
"h5": {
"sdkConfigs": {
"maps": {
"qqmap": {
"key": "YOUR_TENCENT_MAP_KEY"
}
}
}
},
"mp-weixin": {
"permission": {
"scope.userLocation": {
"desc": "您的位置信息将用于地图导航"
}
}
}
}
```
### 3. 微信小程序额外配置
在微信小程序后台配置:
1. 设置 -> 开发设置 -> 服务器域名
2. 添加 request 合法域名:`https://apis.map.qq.com`
## 使用说明
uni-app 已内置地图组件,无需额外安装插件。直接使用 `<map>` 组件即可。
### 基本用法
```vue
<template>
<map
:latitude="latitude"
:longitude="longitude"
:markers="markers"
@markertap="handleMarkerTap"
/>
</template>
```
## 功能特性
- ✅ 地图显示(腾讯地图)
- ✅ 标记点(展厅、展品、设施、当前位置)
- ✅ 点击标记点跳转详情
- ✅ 楼层切换
- ✅ 定位功能
- ✅ 地图缩放
- ✅ 地图拖拽
## 图标资源清单
需要准备以下图标(建议 PNG 格式,透明背景):
- `/static/icons/marker-location.png` - 当前位置30x30px
- `/static/icons/marker-hall.png` - 展厅25x25px
- `/static/icons/marker-exhibit.png` - 展品25x25px
- `/static/icons/marker-facility.png` - 设施25x25px
- `/static/icons/location.png` - 定位按钮图标24x24px
## 参考文档
- [腾讯位置服务官网](https://lbs.qq.com/)
- [uni-app 地图组件文档](https://uniapp.dcloud.net.cn/component/map.html)

View File

@@ -0,0 +1,669 @@
# frontend-miniapp 用户体验与界面审查报告
日期2026-05-28
分支:`analysis/ux-ui-audit-2026-05-28`
项目范围:`museum-guide-v4.0/frontend-miniapp`
审查对象uni-app / Vue 3 小程序与 H5 导览前端,包括地图、讲解、搜索、详情页、路线、设施、音频和 3D 地图模块。
## 1. 执行摘要
### 1.1 总体判断
当前项目已经具备“导览原型”的主要界面部件2D 腾讯地图、Three.js 3D 室内地图、顶部导览/讲解切换、搜索框、设施筛选、讲解抽屉、音频播放器、展品/展厅/设施/路线详情页。
但从真实到馆任务看,它还没有形成可完整测试的用户体验闭环。最关键的问题不是视觉细节,而是入口、数据、状态和操作结果之间没有打通:
- 用户能看到搜索框,但搜索结果体系分散且首页搜索只 `console.log`
- 用户能点击地图点位,但 `TencentMap.vue` 同时触发底部详情浮层和页面跳转。
- 用户能看到路线详情页,但没有路线入口,“开始导览”只是 `uni.navigateBack()`
- 用户能看到音频按钮,但不同入口各自维护状态,且首页回退到 `https://example.com/audio.mp3`
- 用户能进入 3D 地图,但首页传入空 POI 数组,`ThreeMap.vue` 不会回退默认点位。
- 项目标题是深圳自然博物馆,但核心数据仍是“蒙娜丽莎”“星空”“经典艺术之旅”等艺术馆 mock 内容。
- 无障碍语义、键盘焦点、读屏标签和颜色对比度基础不足。
### 1.2 最高优先级建议
| 优先级 | 关键发现 | 用户影响 | 建议 |
| --- | --- | --- | --- |
| P0 | `pages.json``tabBar.list` 为空,`BottomTabBar` 未接入 | 用户找不到路线、服务、我的等一级任务 | 重建一级导航:地图、讲解、路线、服务、我的 |
| P0 | 地图 marker 点击既打开浮层又跳详情页 | 用户还没选择行动就被带离地图 | marker 点击只打开浮层,按钮再触发详情/导航/音频 |
| P0 | `AreaSelector` emit 与首页监听不匹配 | 设施筛选无法稳定驱动地图 | 统一为 `change(typeId)` 并过滤地图/列表 |
| P0 | 搜索、路线、音频、收藏大量占位 | 用户测试会卡在功能断点 | 先完成可测试闭环,再做视觉精修 |
| P0 | 自然馆数据缺失,艺术馆 mock 混入 | 用户画像和任务无法真实验证 | 替换自然博物馆 seed data |
| P0 | 无障碍和色彩基础不足 | 老年、低视力、视障用户不可用 | 建立语义按钮、图标标签、高对比 token |
| P1 | 视觉层级和底部浮层互相竞争 | 地图、抽屉、播放器会遮挡 | 定义 z-index 和底部避让规则 |
| P2 | 竞品已具备路线、音频、多语言、无障碍、AR/AI/客流 | 功能广度落后 | 分阶段补齐官方导览生态能力 |
### 1.3 推荐实施策略
不要先大面积重做视觉。建议用三条竖切片把产品打通:
1. 设施导航:搜索/筛选卫生间或电梯,地图定位,点击“到这里”,出现路线状态。
2. 展品讲解:搜索“恐龙/化石”,进入展品详情,播放真实音频,播放器跨页保留。
3. 推荐路线:从一级入口进入路线列表,选择亲子/镇馆/无障碍路线,点击开始后回到地图。
这三条链路打通后,再开展第一轮真实用户测试。
## 2. 审查方法与证据范围
### 2.1 本地源码审查
重点审查文件:
| 模块 | 文件 |
| --- | --- |
| 路由与导航 | `src/pages.json``src/pages/index/index.vue``src/components/navigation/TopTabBar.vue``src/components/navigation/BottomTabBar.vue` |
| 地图 | `src/components/map/TencentMap.vue``src/components/map/ThreeMap.vue``src/components/map/MarkerDetailSheet.vue``src/services/map/Map3DManager.ts` |
| 搜索 | `src/components/search/SearchBar.vue``src/components/search/SearchPanel.vue``src/pages/search/index.vue``src/utils/search.ts` |
| 讲解与音频 | `src/components/explain/ExplainList.vue``src/components/audio/AudioPlayer.vue``src/components/audio/FloatingAudioButton.vue` |
| 详情页 | `src/pages/exhibit/detail.vue``src/pages/hall/detail.vue``src/pages/facility/detail.vue``src/pages/route/detail.vue` |
| 数据 | `src/assets/data/exhibits.json``halls.json``facilities.json``routes.json` |
| 视觉 token | `src/App.vue``src/styles/variables.scss` |
### 2.2 竞品资料来源
官方资料与页面:
- 故宫博物院导览页https://www.dpm.org.cn/Visit.html
- “数字故宫”小程序 2.0 官方发布稿https://www.dpm.org.cn/classify_detail/257034.html
- 上海自然博物馆 App 下载页https://www.snhm.org.cn/cgfw/appDown.htm
- 上海自然博物馆楼层导览https://nao.snhm.org.cn/foreign/cszl/index.htm
- 上海自然博物馆虚拟漫游https://nao.snhm.org.cn/foreign/cszl/virtualRoaming.htm
- British Museum Audio apphttps://www.britishmuseum.org/visit/audio-app
- MoMA Audio / Bloomberg Connects guidehttps://www.moma.org/audio/
- The Met Plan Your Visit / Digital Guidehttps://www.metmuseum.org/plan-your-visit
- The Met Audio Guidehttps://www.metmuseum.org/audio-guide
## 3. 核心用户测试场景设计
### 3.1 用户画像 A首次访客
画像:
- 年龄 20-45 岁。
- 第一次到馆,对展馆楼层、展厅名称和服务设施不了解。
- 目标是“快速开始参观,不迷路”。
关键任务 A1找到目标展品或展区。
| 项目 | 内容 |
| --- | --- |
| 任务 | 打开小程序,搜索或浏览“恐龙/化石/生命演化”相关内容,进入一个展品详情 |
| 成功标准 | 3 分钟内找到目标;不超过 2 次误点;能说出展品所在楼层/展厅 |
| 预期行为 | 先找搜索框或地图点位,然后进入详情,最后返回地图继续参观 |
| 当前风险 | 首页搜索只日志输出;数据仍是艺术馆 mock详情页没有真实加载 id 数据 |
关键任务 A2从地图点位了解“我点到的是什么”。
| 项目 | 内容 |
| --- | --- |
| 任务 | 点击地图上的一个展厅/展品点位,查看信息后选择“查看详情” |
| 成功标准 | 点击 marker 后停留在地图,浮层信息清晰;用户自主选择下一步 |
| 预期行为 | 点位先展开底部 sheet再选择导航、详情、讲解或收藏 |
| 当前风险 | `TencentMap.vue` 点击 marker 后同时 emit 和 `navigateTo`,流程冲突 |
关键任务 A3找到出口或服务台。
| 项目 | 内容 |
| --- | --- |
| 任务 | 在首页找到“出口/服务中心”,点击开始导航 |
| 成功标准 | 45 秒内找到;导航状态明确;能返回地图 |
| 预期行为 | 用户会找“服务”“设施”“地图上的按钮” |
| 当前风险 | 设施入口隐藏在横向标签中;导航动作多为 toast 或 `navigateBack()` |
### 3.2 用户画像 B家庭游客
画像:
- 家长带 6-12 岁儿童。
- 注意力分散,需要短平快的路线和互动反馈。
- 目标是找到孩子感兴趣的内容,减少走冤枉路。
关键任务 B1选择亲子路线。
| 项目 | 内容 |
| --- | --- |
| 任务 | 找到适合 60 分钟的亲子路线,查看站点并开始导览 |
| 成功标准 | 60 秒内发现路线入口;路线时长和站点可理解;开始后地图显示第一站 |
| 预期行为 | 用户优先找“路线”“推荐”“亲子”而不是手动搜索每个展品 |
| 当前风险 | 路线详情存在但无一级入口;`route/detail.vue` 开始导览只是返回 |
关键任务 B2播放儿童友好讲解。
| 项目 | 内容 |
| --- | --- |
| 任务 | 从讲解列表或展品详情播放一条适合儿童的音频 |
| 成功标准 | 音频 10 秒内开始;标题和当前展品对应;切页后播放器仍存在 |
| 预期行为 | 用户会点击耳机/播放图标,期望自动开始 |
| 当前风险 | 音频状态分散;无儿童标签;无真实音频 URL 时请求 example.com |
关键任务 B3完成一个小任务或打卡。
| 项目 | 内容 |
| --- | --- |
| 任务 | 找到一个“寻宝/打卡/问答”互动入口并完成一项 |
| 成功标准 | 入口可发现;反馈明确;儿童能理解完成状态 |
| 预期行为 | 家庭用户会被“任务”“徽章”“找一找”吸引 |
| 当前风险 | 当前没有互动任务体系,上海自然博物馆 App 已提供寻宝游戏方向参考 |
### 3.3 用户画像 C资深爱好者
画像:
- 自然科学爱好者、摄影/标本/古生物爱好者。
- 愿意花 2-4 小时深度参观。
- 目标是按主题系统浏览并收藏内容。
关键任务 C1按主题浏览展品。
| 项目 | 内容 |
| --- | --- |
| 任务 | 在讲解页按主题筛选,如古生物、矿物、生态、植物 |
| 成功标准 | 用户能理解主题分类;列表内容匹配主题;可进入详情 |
| 预期行为 | 从“讲解”或“路线”进入主题内容 |
| 当前风险 | `ExplainList.vue` 的“按展厅/按主题”只切 activeFilter未真正重组数据 |
关键任务 C2收藏并形成个人清单。
| 项目 | 内容 |
| --- | --- |
| 任务 | 收藏 3 个展品,稍后从“我的”或收藏页找回 |
| 成功标准 | 收藏状态可见;刷新或切页后保留;能批量查看 |
| 预期行为 | 用户点击爱心后期待保存到个人清单 |
| 当前风险 | 收藏是组件局部状态,无持久化和“我的”入口 |
关键任务 C3在 3D 地图中理解空间关系。
| 项目 | 内容 |
| --- | --- |
| 任务 | 进入 3D 室内地图,查看某楼层展厅和点位关系 |
| 成功标准 | 5 秒内出现可用模型或骨架;有 POI可切换楼层 |
| 预期行为 | 用户会探索模型并点击点位 |
| 当前风险 | 首页传入空 `poiList`3D 默认点位不会出现3D 与路线/详情未连接 |
### 3.4 用户画像 D学生与研学团队
画像:
- 中小学生、大学生或老师带队。
- 有明确学习目标,需要时间管理、知识点和团队路线。
- 目标是完成主题学习和记录。
关键任务 D1按研学主题规划路线。
| 项目 | 内容 |
| --- | --- |
| 任务 | 选择“生命演化 90 分钟”或“地球矿物 60 分钟”路线 |
| 成功标准 | 路线有学习目标、站点、预计时长;可开始导航 |
| 预期行为 | 老师会先看路线目标,再看站点顺序 |
| 当前风险 | `routes.json` 是艺术路线,且路线没有地图状态 |
关键任务 D2获取展品详细信息。
| 项目 | 内容 |
| --- | --- |
| 任务 | 进入某展品详情,查看介绍、年代、位置、讲解 |
| 成功标准 | 关键信息首屏可见;说明适合学习记录;有引用或延伸知识 |
| 预期行为 | 学生会截图、收藏、返回列表继续看 |
| 当前风险 | 展品详情是固定 mock没有按 id 加载,内容和自然馆不匹配 |
关键任务 D3完成小组集合点导航。
| 项目 | 内容 |
| --- | --- |
| 任务 | 找到集合点、服务台或出口并导航 |
| 成功标准 | 30 秒内找到,地图反馈明确 |
| 预期行为 | 用户会搜索“出口/服务台/电梯” |
| 当前风险 | 设施搜索和导航未闭环 |
### 3.5 用户画像 E老年与无障碍需求用户
画像:
- 老年游客、低视力用户、视障用户、行动不便用户。
- 对复杂手势和小图标不敏感或不可用。
- 目标是独立完成找设施、听讲解、少走楼梯。
关键任务 E1找到无障碍电梯和卫生间。
| 项目 | 内容 |
| --- | --- |
| 任务 | 选择无障碍设施,找到电梯或无障碍卫生间 |
| 成功标准 | 读屏能读出入口;目标点击区域不小于 44px地图或列表明确反馈 |
| 预期行为 | 用户更依赖文字和清晰按钮,而不是图标猜测 |
| 当前风险 | 大量 emoji 和 `view @tap`;缺少 aria-label、focus、role |
关键任务 E2使用语音讲解。
| 项目 | 内容 |
| --- | --- |
| 任务 | 进入展品详情或讲解页,播放、暂停、关闭音频 |
| 成功标准 | 读屏能读出播放状态;按钮可键盘触发;错误可理解 |
| 预期行为 | 用户需要稳定的“播放/暂停/关闭”文字反馈 |
| 当前风险 | 播放按钮缺少语义状态,进度条不可访问 |
关键任务 E3选择少楼梯路线。
| 项目 | 内容 |
| --- | --- |
| 任务 | 选择“无障碍优先”路线并开始导览 |
| 成功标准 | 路线避开楼梯;明确提示电梯、坡道、休息区 |
| 预期行为 | 用户会寻找“无障碍”“电梯优先”设置 |
| 当前风险 | 路线数据没有无障碍属性 |
## 4. UX 交互逻辑系统分析
### 4.1 信息架构
当前 IA 示意:
```mermaid
flowchart TD
A["首页 pages/index"] --> B["顶部 TopTabBar"]
B --> C["导览 guide"]
B --> D["讲解 explain"]
C --> E["地图 + 搜索 + 设施筛选"]
C --> F["点位详情 sheet"]
D --> G["讲解抽屉 + 搜索"]
E --> H["展品/展厅/设施详情页"]
G --> H
H --> I["导航/收藏/音频占位动作"]
J["路线详情页"] -. "无稳定入口" .-> A
```
问题:
| 问题 | 证据 | 影响 | 建议 |
| --- | --- | --- | --- |
| 一级导航缺失 | `pages.json``tabBar.list` 为空;`BottomTabBar.vue` 未接入首页 | 用户只能看到“导览/讲解”,路线、服务、我的缺位 | 建立 5 个一级入口:地图、讲解、路线、服务、我的 |
| 路线入口过深或不存在 | 仅有 `pages/route/detail.vue`,无列表页和首页入口 | 时间敏感用户无法开始路线 | 新增 `route/index` 和首页/底部 tab 入口 |
| 内容分类不匹配 | `exhibits.json``halls.json``routes.json` 是艺术馆内容 | 自然馆测试失真 | 替换自然科学 taxonomy 和 seed data |
| 搜索 IA 重复 | 搜索页、SearchPanel、ExplainList 内各有 mock 数据 | 用户从不同入口得到不同结果 | 统一搜索服务和结果动作 |
| 服务信息缺位 | 没有开放时间、预约、票务、交通、临展、服务须知入口 | 到馆前/馆内服务链路断裂 | 新增服务页,整合官方服务能力 |
建议 IA
```mermaid
flowchart TD
A["底部主导航"] --> M["地图"]
A --> N["讲解"]
A --> R["路线"]
A --> S["服务"]
A --> U["我的"]
M --> M1["2D/3D 地图"]
M --> M2["楼层/设施筛选"]
M --> M3["点位详情"]
N --> N1["按展厅"]
N --> N2["按主题"]
N --> N3["当前播放"]
R --> R1["亲子路线"]
R --> R2["镇馆路线"]
R --> R3["无障碍路线"]
S --> S1["开放时间/预约"]
S --> S2["卫生间/电梯/出口"]
U --> U1["收藏"]
U --> U2["历史"]
U --> U3["无障碍偏好"]
```
### 4.2 交互流程
关键断点示意:
```mermaid
flowchart LR
A["点击地图点位"] --> B["父组件打开 MarkerDetailSheet"]
A --> C["TencentMap 内部 navigateTo"]
B --> D["用户本应选择行动"]
C --> E["页面已跳走"]
D -. "被中断" .-> E
```
主要问题:
| 流程 | 当前问题 | 建议 |
| --- | --- | --- |
| 地图点位 | `TencentMap.vue` marker 点击同时 emit 和跳页 | marker 点击只打开 sheetsheet 按钮触发详情/导航 |
| 设施筛选 | `AreaSelector` emits `change`,父组件监听 `area-click``floor-change` | 统一事件合约,筛选后更新地图点位和结果列表 |
| 3D 地图 | `poiList` 空数组传入后不回退默认 POI | 改为 `props.pois?.length ? props.pois : defaultPOIs`,再接真实数据 |
| 搜索 | 首页 `handleSearch` 只日志输出;搜索组件重复 mock | 一套搜索服务,多入口复用 |
| 音频 | 详情页只切换 `isPlaying`;首页 fallback 到 example.com | 统一音频 store禁用无音频项支持加载/错误/重试 |
| 路线 | `handleStartRoute` 只是返回 | 设置导航状态,回地图展示路线进度 |
| 收藏 | 组件局部 ref无“我的” | 持久化收藏,增加收藏入口 |
操作步骤数量评估:
| 任务 | 理想步骤 | 当前可能步骤 | 问题 |
| --- | ---: | ---: | --- |
| 找卫生间并导航 | 3服务/筛选 -> 选择卫生间 -> 到这里 | 5+,且可能无结果 | 入口、筛选、导航未闭环 |
| 播放展品讲解 | 2展品 -> 播放 | 3-5且可能是假 URL | 音频状态分散 |
| 开始推荐路线 | 3路线 -> 选路线 -> 开始 | 无稳定入口 | IA 缺口 |
| 查看点位详情 | 2点 marker -> 查看详情 | 1但被自动跳页 | 缺少确认和行动选择 |
### 4.3 认知负荷
| 负荷来源 | 表现 | 用户后果 | 改进 |
| --- | --- | --- | --- |
| 功能命名抽象 | “导览/讲解”重叠 | 用户不确定去哪找路线、设施、音频 | 改为任务型一级导航 |
| 控件过多且叠加 | 顶部 tab、搜索、设施、楼层、地图按钮、抽屉、播放器 | 首屏决策复杂 | 分组并减少默认可见控件 |
| 状态不可见 | 收藏、路线、音频、筛选状态本地化 | 用户不知道操作是否生效 | 全局状态条和明确反馈 |
| 手势学习成本 | 抽屉拖拽、地图拖拽、浮层关闭同时存在 | 用户误触或不知道如何关闭 | 增加显性按钮,手势作为增强 |
| 图标语义不稳定 | emoji 用于设施、导航、收藏 | 读屏和跨端展示不可控 | SVG 图标 + 文字 label |
| 内容可信度低 | 自然馆里出现艺术名作 | 用户信任下降 | 换真实自然馆内容 |
## 5. UI 展示逻辑审查
### 5.1 当前首页层级示意
```text
┌──────────────────────────┐
│ 顶部 TopTabBar: 导览/讲解 │ z=1001
├──────────────────────────┤
│ 搜索栏 │ z=1001
├──────────────────────────┤
│ 设施横向筛选 │ z=1001
│ │
│ 地图/3D模型 │ base
│ 楼层按钮/定位按钮 │ map-control
│ │
├──────────────────────────┤
│ Marker Sheet / 讲解抽屉 │ z=500 / 100
├──────────────────────────┤
│ 音频播放器 │ z=1000
└──────────────────────────┘
```
问题:顶部工具层和底部播放器层级接近,底部 sheet、讲解抽屉和音频条缺少互斥或避让规则。
### 5.2 视觉层次
问题:
- 主任务入口不突出,顶部只有两个文字 tab缺少地图/路线/服务等核心任务权重。
- 详情页 hero 固定 450px小屏首屏几乎只剩图片标题和位置下沉。
- `MarkerDetailSheet` 的标题、位置、导航、音频、收藏都在 280px 内挤压。
- 3D 地图加载态视觉强,但失败态和回退态弱。
- 地图控件右侧堆叠,缺少任务分组。
建议:
- 一级导航使用底部 tab 或平台原生 tab地图页内部再用 segmented control。
- 展品详情首屏优先级:标题、展厅/楼层、播放、导航,再是大图。
- 底部 sheet 两档:摘要态 180-240px全屏态承载详细信息。
- 地图控件按“定位/缩放”“楼层”“模式”分区。
### 5.3 色彩系统
当前主色:
- `--museum-accent: #E0E100`
- `--museum-poi-marker: #5ED0E4`
- 多处硬编码 `#E0E100`
对比度风险:
| 组合 | 对比度 | 结论 |
| --- | ---: | --- |
| `#E0E100` / 白色 | 约 1.41:1 | 不适合白底小图形、边框、细状态 |
| `#E0E100` / `#262421` | 约 11.01:1 | 适合作为深色文字背景 |
| `#5ED0E4` / 白色 | 约 1.81:1 | 白底可见性不足 |
| `#999999` / 白色 | 约 2.85:1 | 不适合正文 |
问题:
- 同一个黄色承担品牌、选中态、路径、地图建筑边界、音频、加载、按钮等过多语义。
- 缺少功能色:成功、警告、错误、信息、拥挤度、无障碍。
- 地图上浅色点位和浅色路径在复杂背景上不稳定。
建议:
- 保留黄色作为品牌背景和大面积 CTA不作为白底细边框。
- 增加语义色 token
- `--color-info: #1463D9`
- `--color-success: #1B7F3A`
- `--color-warning: #9A5B00`
- `--color-danger: #C62828`
- `--color-accessible: #005A9C`
- 地图点位采用深色底、白描边、类型图标和文字缩写。
### 5.4 空间布局
问题:
- 大量固定高度:`100vh``450px` hero、`50vh` 抽屉、`46px` 控件。
- `AreaSelector.vue` 的展开面板 `top: 100%`,在 fixed overlay 内可能落在视口外。
- 设施标签固定 68px长词如“无障碍设施”容易拥挤。
- 抽屉和播放器都在底部,没有全局占位变量。
建议:
- 详情图使用 `height: min(42vh, 320px)` 思路。
- 抽屉使用 `bottom: 0` 和明确 `max-height`,不要依赖 `top: 100%`
- 设施标签改为内容自适应,常用设施用快捷按钮。
- 定义 `--bottom-player-height`,所有底部浮层避让。
### 5.5 响应式设计
风险:
| 视口 | 风险 | 建议验证 |
| --- | --- | --- |
| 375x667 小屏 | 顶部 tab + 搜索 + 筛选占用地图过多 | 首屏是否还能看到当前位置和地图点位 |
| 390x844 常规手机 | 抽屉、播放器、marker sheet 互相遮挡 | 播放音频时打开 marker sheet |
| 430x932 大屏手机 | 控件分散但仍偏移动端 | 检查底部操作区拇指可达 |
| 768x1024 平板 | 列表过窄,地图空旷 | 讲解列表可改侧栏 |
| 横屏 | 100vh 和底部栏遮挡严重 | 改为侧边工具栏或紧凑布局 |
建议:
- 增加 H5 视觉回归,至少覆盖上述 5 个视口。
- 平板/H5 使用地图 + 侧栏双栏布局。
- 小屏默认折叠非必要筛选,仅保留搜索和“最近设施”。
### 5.6 无障碍性
问题:
- 核心控件是 `view @tap`H5 下缺少 button 语义。
- 未发现 `aria-label``aria-live``role="dialog"``aria-pressed`
- emoji 图标用于卫生间、收藏、导航、播放等核心功能。
- 播放器进度条不可键盘操作,读屏无法知道当前时间。
- 抽屉打开后没有焦点陷阱,背景地图仍可被误触。
建议:
- 抽象 `AppButton``IconButton`H5 输出语义 button小程序端映射为可点击 view。
- 所有图标按钮必须具备文字 label 或 `aria-label`
- 弹层用 `role="dialog"`,关闭按钮明确,打开时聚焦标题或第一个操作。
- 音频状态用 `aria-live="polite"`
- 增加“大字/高对比/减少动画/无障碍路线优先”设置。
## 6. 竞品对比分析
### 6.1 竞品选择
本次选取 5 个官方移动端/数字导览生态:
1. 故宫博物院“数字故宫”小程序与官方导览页。
2. 上海自然博物馆 App/H5 楼层导览/虚拟漫游。
3. British Museum Audio app。
4. MoMA Audio + Bloomberg Connects digital guide。
5. The Met Audio Guide / Digital Guide / interactive map。
### 6.2 核心功能差异
| 功能 | 当前项目 | 故宫 | 上海自然博物馆 | British Museum | MoMA | The Met | 我们缺失 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 一级导航 | 仅导览/讲解 | 导览、展览、探索、票务等 | 常设展览、参观服务等 | Visit 下含 audio app/map/accessibility | Visit 下含 map/audio/accessibility | Plan Visit 下含 audio/map/accessibility | 任务型主导航 |
| 官方路线 | mock 路线,无入口 | LBS 推荐路线、定制路线 | App 提供参观路线 | self-guided tours | self-guided tours | highlights / collection tours | 路线入口和开始导览 |
| 地图 | 2D/3D 原型 | 精确路线、AR 实景导航 | 楼层导览 | interactive map | interactive museum map | interactive map | 地图与路线/服务打通 |
| 音频 | 组件存在,未闭环 | 官方语音讲解 | 实时学习/导览生态 | 250 highlights、65 gallery intros、多语言 | 分主题音频、kids、verbal descriptions | 展览/藏品音频 playlists | 真实音频、分类、多语言、儿童/无障碍版本 |
| 收藏 | 局部状态 | 未作为本次重点 | 未作为本次重点 | favourites | digital guide 支持探索内容 | digital guide/audio guide | 持久化收藏和“我的” |
| 互动 | 无 | AI 助手、AR 瑞兽 | 寻宝游戏、AR 展品复原 | self-guided tours | highlights / exhibitions | featured tours | 亲子任务、AR、AI |
| 客流 | 无 | 舒适度指数 | 限流提示 | 无官方页面重点 | 无官方页面重点 | 访问计划信息 | 客流/拥挤度 |
| 无障碍 | 基础缺失 | 读屏、无障碍路线/设施 | 页面有基础服务App 未见完整无障碍描述 | BSL、audio descriptive guide | verbal descriptions | accessibility guide | 读屏、无障碍路线、描述音频 |
| 离线/预下载 | 无 | 未见明确离线 | 建议到馆前下载 App | 建议到馆前下载 audio tours | Bloomberg Connects app | Digital guide/app store | 到馆前下载/缓存策略 |
### 6.3 交互设计亮点
| 竞品 | 亮点 | 可借鉴点 |
| --- | --- | --- |
| 故宫 | 将路线、语音讲解、AR 导航、舒适度指数、AI 助手、无障碍路线整合为到馆闭环 | 把“地图”升级为路线和服务中枢,不只是展示平面图 |
| 上海自然博物馆 | 楼层导览和虚拟漫游围绕自然科学主题,如起源之谜、生命长河、演化之道 | 当前项目应采用自然馆主题 taxonomy而不是通用展品列表 |
| British Museum | 音频 app 有多语言、主题 tour、250 件重点藏品、收藏、互动地图、BSL 和描述音频 | 音频应按主题、语言、人群和无障碍类型组织 |
| MoMA | Audio 页面按 collection、kids、verbal descriptions 等分组,并接入 Bloomberg Connects | 讲解不只是列表,应有人群化入口,如儿童、描述音频、展览专题 |
| The Met | Plan Visit 聚合票务、地图、音频、无障碍、家庭访问Audio Guide 支持 stop number 和 playlists | 到馆前信息和到馆中导览应在一条链路里 |
### 6.4 技术实现优势对比
| 方向 | 竞品优势 | 当前差距 | 建议 |
| --- | --- | --- | --- |
| 内容分发 | British Museum / MoMA / The Met 使用成熟的音频和导览内容体系 | 当前音频 URL、内容分类、详情加载均未成熟 | 建立内容模型和 CMS/API 接口 |
| 定位与路线 | 故宫 LBS 路线和 AR 导航 | 当前路径是模拟,详情导航只返回 | 先做静态路线状态,再接真实室内定位 |
| 预下载 | British Museum 建议到馆前下载音频 | 当前没有缓存策略 | H5/小程序缓存地图、音频元数据、轻量图片 |
| 多语言 | British Museum、The Met、MoMA 支持多语言或 Bloomberg Connects 多语言能力 | 当前只有中文 | 中期增加中英双语,长期多语言 |
| 无障碍 | 故宫、British Museum、MoMA、The Met 都有可访问内容或服务入口 | 当前语义和内容均缺失 | 建立 a11y 组件规范和无障碍路线数据 |
| 性能 | 竞品多数使用轻量列表/音频/地图3D 不作为首屏基础依赖 | 当前 3D GLB 可能成为性能风险 | 默认 2D3D 按需加载,低端机降级 |
### 6.5 按优先级的竞品借鉴建议
P0
- 借鉴 The Met / MoMA 的“Visit/Map/Audio/Accessibility”任务入口重建主导航。
- 借鉴 British Museum 的音频内容模型:主题 tour、重点对象、收藏、地图信息。
- 借鉴上海自然博物馆的自然科学主题命名,替换艺术馆 mock。
P1
- 借鉴故宫路线闭环:路线推荐、语音讲解、地图导航联动。
- 借鉴 MoMA 的 kids / verbal descriptions 分组,建立儿童和无障碍讲解入口。
- 借鉴 The Met 的计划访问页面,把开放时间、票务、地图、音频、无障碍聚合。
P2
- 借鉴故宫舒适度指数,增加人工或实时客流状态。
- 借鉴上海自然博物馆寻宝和 AR 展品复原,做亲子互动任务。
- 借鉴故宫 AI 助手,但限制在官方知识库和地图数据内,避免编造。
## 7. 分阶段优化计划
### 7.1 短期1-2 周:可测试版本
目标:修复 P0 阻塞,让 5 类用户任务能被真实测试。
| 编号 | 改进项 | 涉及模块 | 工作量 |
| --- | --- | --- | --- |
| S1 | 建立主导航:地图、讲解、路线、服务、我的 | `pages.json`、导航组件、首页 | 3-5 人日 |
| S2 | 修复 marker 点击冲突 | `TencentMap.vue``MarkerDetailSheet.vue` | 1-2 人日 |
| S3 | 修复设施筛选事件和展开面板 | `AreaSelector.vue``index.vue` | 2-3 人日 |
| S4 | 统一搜索服务和结果动作 | `SearchPanel``ExplainList``search/index``utils/search.ts` | 4-6 人日 |
| S5 | 替换自然馆 seed data | data JSON、详情页加载 | 3-5 人日 |
| S6 | 统一音频播放器,去掉假 URL | `AudioPlayer`、详情页、讲解列表 | 4-6 人日 |
| S7 | 路线入口和开始导览闭环 | 路线列表、路线详情、地图状态 | 5-7 人日 |
| S8 | P0 无障碍和对比度修复 | 基础按钮、图标、色彩 token | 4-6 人日 |
短期验收:
- 5 个用户画像至少各 1 条任务可完整执行。
- 地图点位、搜索、设施、音频、路线不再只停留在 toast 或 console。
- 读屏能识别核心按钮。
### 7.2 中期3-6 周:体验一致性与内容体系
目标:降低认知负荷,提高导览效率,支撑第二轮用户测试。
| 编号 | 改进项 | 说明 | 工作量 |
| --- | --- | --- | --- |
| M1 | 全局状态模型 | 地图、路线、音频、收藏、搜索历史 | 5-8 人日 |
| M2 | 视觉层级和 z-index token | 顶栏、地图控件、sheet、播放器、modal | 2-3 人日 |
| M3 | 响应式专项 | 小屏、常规手机、平板、横屏 | 5-7 人日 |
| M4 | 内容分类体系 | 展厅、主题、展品、路线、设施 taxonomy | 5-8 人日 |
| M5 | 真实加载/空态/错误态 | 3D、音频、搜索、定位、路线 | 4-6 人日 |
| M6 | 收藏和我的 | 收藏、历史、设置、无障碍偏好 | 4-6 人日 |
| M7 | 多语言基础 | 中英字段、语言切换框架 | 5-8 人日 |
中期验收:
- SUS 目标 75+。
- 设施查找 30 秒内。
- 搜索结果一致性 100%。
- 关键视口无明显遮挡。
### 7.3 长期7-12 周:官方导览生态和差异化
目标:补齐竞品能力,形成 3D + 自然科学 + 亲子/研学差异化。
| 编号 | 改进项 | 说明 | 工作量 |
| --- | --- | --- | --- |
| L1 | 服务与票务入口 | 开放时间、预约、票务、交通、临展 | 5-8 人日 |
| L2 | 无障碍路线 | 电梯优先、少楼梯、休息区、无障碍卫生间 | 8-12 人日 |
| L3 | 客流舒适度 | 先人工运营,后接实时数据 | 8-15 人日 |
| L4 | 亲子寻宝/研学任务 | 打卡、问答、奖励、路线 | 10-15 人日 |
| L5 | 3D 地图产品化 | POI、路线、楼层剖切、性能降级 | 12-20 人日 |
| L6 | AR 展品复原 | 古生物/化石复原场景 | 15-30 人日 |
| L7 | AI 导览助手 | 官方知识库、路线推荐、服务问答 | 15-25 人日 |
长期验收:
- 亲子路线、无障碍路线、深度主题路线均可测试。
- 3D 不只是模型展示,而能完成找展品/找设施/走路线。
- AI 回答有来源边界,不编造展品和场馆信息。
## 8. 工作量与资源需求
### 8.1 角色配置
| 阶段 | 前端 | UX/UI | 内容/策展 | 后端/API | 3D/图形 | QA | 备注 |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | --- |
| 短期 | 1-2 人 | 0.5 人 | 0.5 人 | 0-0.5 人 | 0.2 人 | 0.5 人 | 以本地 seed data 为主 |
| 中期 | 1-2 人 | 1 人 | 1 人 | 0.5-1 人 | 0.5 人 | 0.5-1 人 | 建立状态、内容和响应式体系 |
| 长期 | 2 人 | 1 人 | 1-2 人 | 1-2 人 | 1 人 | 1 人 | 涉及客流、AR、AI、3D 性能 |
### 8.2 粗略工期
| 阶段 | 工期 | 人日估算 |
| --- | --- | ---: |
| 短期 P0 | 1-2 周 | 26-40 人日 |
| 中期 P1 | 3-6 周 | 30-46 人日 |
| 长期 P2/P3 | 7-12 周 | 73-125 人日 |
### 8.3 关键依赖
- 自然馆真实内容 taxonomy 和 seed data。
- 展厅、展品、设施、路线的统一 ID。
- 音频资源和版权状态。
- 室内点位坐标和无障碍设施数据。
- 是否接入实时客流、定位、AR 或 AI 后端服务。
## 9. 用户测试计划
### 9.1 测试轮次
| 轮次 | 时间点 | 样本 | 目标 |
| --- | --- | --- | --- |
| 第 1 轮 | 短期 P0 完成后 | 8-10 人 | 验证核心任务是否可执行 |
| 第 2 轮 | 中期 P1 完成后 | 15-20 人 | 验证 IA、响应式、无障碍基础 |
| 第 3 轮 | 长期能力上线后 | 25+ 人 | 验证亲子、研学、无障碍和 3D 差异化 |
### 9.2 指标
| 指标 | P0 目标 | P1 目标 | P2/P3 目标 |
| --- | ---: | ---: | ---: |
| 核心任务完成率 | 80% | 90% | 95% |
| 找设施用时 | 45 秒内 | 30 秒内 | 20 秒内 |
| 路线入口发现率 | 70% | 90% | 95% |
| 音频播放成功率 | 90% | 95% | 98% |
| SUS | 68+ | 75+ | 80+ |
| 读屏核心按钮可识别率 | 80% | 95% | 98% |
## 10. 结论
当前项目最需要的不是再增加一个视觉模块,而是把已经存在的部件连接成真实任务:
1. 信息架构从“导览/讲解”改为“地图/讲解/路线/服务/我的”。
2. 地图点位先确认,再由用户选择导航、详情、音频或收藏。
3. 搜索、音频、路线、收藏都要统一状态和统一数据源。
4. 先替换自然博物馆内容,再做用户测试。
5. 先满足老年、儿童、首次访客的基础可用性,再上 AR、AI、客流、3D 深化。
建议下一步直接以“设施导航”和“展品讲解”两条竖切片启动 P0实现后用 8-10 名用户做第一轮可用性测试。