第03章 项目结构解析
3.1 整体目录结构
map-360-demo 采用标准的 Vite + Vue 3 + TypeScript 项目结构:
map-360-demo/
├── public/ # 静态资源(构建时原样复制到 dist/)
│ └── icons/
│ └── link-pin.svg # 场景跳转标记图标(本地资源,不依赖 CDN)
├── src/ # 源代码
│ ├── App.vue # 根组件:编排状态、跳转逻辑、导入导出
│ ├── main.ts # 入口文件
│ ├── env.d.ts # 类型声明(vite/client + *.vue 模块)
│ ├── components/ # 组件
│ │ ├── PsvContainer.vue # 核心:封装 PSV viewer 生命周期
│ │ ├── SceneSelector.vue # 场景下拉选择
│ │ ├── MarkerModal.vue # 添加/编辑标记弹窗
│ │ ├── MarkerList.vue # 底部标记列表
│ │ └── ToastHost.vue # 全局 Toast 反馈
│ ├── composables/ # 组合式函数
│ │ ├── useAppState.ts # 全局响应式状态 + localStorage 持久化
│ │ ├── useAddMarkerFlow.ts # 添加/编辑标记状态机
│ │ └── useToast.ts # Toast 单例状态
│ ├── data/
│ │ └── scenes.ts # 预设场景、默认标记、PSV 标记配置转换
│ ├── styles/
│ │ └── psv.css # PSV 全局样式覆盖
│ ├── types/
│ │ └── index.ts # 类型定义
│ └── utils/
│ └── geo.ts # 方位角换算(点击位置 ↔ 经纬度)
├── index.html # HTML 模板
├── vite.config.ts # Vite 配置
├── tsconfig.json # TypeScript 根配置
├── tsconfig.app.json # 应用代码配置
├── tsconfig.node.json # Node 环境配置(vite.config.ts)
└── package.json # 项目依赖与脚本
3.2 各目录职责详解
3.2.1 public/
存放静态资源,构建时原样复制到 dist/ 目录。icons/link-pin.svg 是一个 32×32 的绿色箭头 pin 图标,用于场景跳转标记,同时被用作 index.html 的 favicon。
3.2.2 src/components/
存放 Vue 组件,每个组件职责单一:
| 组件 | 职责 |
|---|---|
| PsvContainer.vue | 唯一与 PSV 直接交互的组件,封装 Viewer + 两个插件的完整生命周期 |
| SceneSelector.vue | 场景下拉选择,纯展示组件 |
| MarkerModal.vue | 添加/编辑标记弹窗,含类型选择、焦点管理、过渡动效 |
| MarkerList.vue | 底部标记列表,含搜索、折叠、编辑/删除、导入导出 |
| ToastHost.vue | 全局 Toast 反馈 |
3.2.3 src/composables/
存放组合式函数,是项目状态管理的核心:
| 文件 | 职责 |
|---|---|
| useAppState.ts | 全局响应式状态 + localStorage 持久化 + 导入导出 |
| useAddMarkerFlow.ts | 添加/编辑标记状态机(选点、预览 pin、弹窗协调) |
| useToast.ts | Toast 单例状态(定时器自动清理) |
3.2.4 src/data/
存放静态数据与配置转换逻辑:
scenes.ts:预设场景、默认标记、PSV 标记配置转换函数
3.2.5 src/styles/
存放全局样式:
psv.css:PSV 全局样式覆盖(tooltip、预览脉冲、跳转标记)
3.2.6 src/types/
存放 TypeScript 类型定义:
index.ts:Scene / MarkerData / MarkerPayload / PsvMarkerConfig 类型
3.2.7 src/utils/
存放工具函数:
geo.ts:方位角换算(点击位置 ↔ 经纬度)
3.3 数据流架构
项目的核心数据流如下:
App.vue (编排层)
├── useAppState() → scenes / currentSceneId / currentMarkers / CRUD
├── useAddMarkerFlow() → 添加/编辑状态机
├── useToast() → Toast 反馈
└── PsvContainer → PSV viewer 生命周期封装
├── Viewer → 全景查看器
├── MarkersPlugin → 全景标记
└── PlanPlugin → Leaflet 地图
关键设计:
- App.vue 不持有业务数据:全部状态来自 composables,只做编排
- PsvContainer 是唯一与 PSV 交互的组件:通过
defineExpose暴露语义化接口 - 模块级单例:composables 中的状态是模块顶层
ref,多处调用共享同一份数据
3.4 类型定义
src/types/index.ts 定义了核心类型:
// 场景
interface Scene {
id: string
name: string
description: string
panorama: string
coordinates: [number, number] // [lng, lat]
bearing: string // 方位角,如 '120deg'
defaultZoom: number
}
// 标记类型
type MarkerType = 'info' | 'link'
// 标记数据
interface MarkerData {
id: string
sceneId: string
name: string
description: string
position: { yaw: number, pitch: number } // 全景球形坐标
coordinates: [number, number] // GPS 经纬度
createdAt: number
type?: MarkerType // 缺省视为 info
targetSceneId?: string // link 类型的目标场景
}
// 弹窗确认后的表单数据
interface MarkerPayload {
name: string
description: string
coordinates: [number, number]
type: MarkerType
targetSceneId?: string
}
// PSV markers-plugin 配置格式
interface PsvMarkerConfig {
id: string
tooltip: string
content: string
position: { yaw: number, pitch: number }
image: string
size: { width: number, height: number }
anchor: string
className?: string
data: {
plan: { // PlanPlugin 联动字段
coordinates: [number, number]
size: number
image: string
}
}
}
3.5 下一步
了解了项目结构后,进入 第04章 核心组件 PsvContainer 详解 深入理解核心组件的实现。