refactor(map): 将地图模块封装成独立的单元
* 将 `SvgIcon` 组件移至 `map/shared/SvgIcon` * 将地图图标迁移至 `map/assets/icons` * 更新组件导入,使用 `MapIcon` 替代 `svg-icon` * 为 Cesium 地图 SDK 添加详尽的 `README.md` * 更新 vite 配置,纳入新的图标目录 * 移除 `main.js` 中的 `SvgIcon` 导入
@ -7,7 +7,6 @@ import ElementPlus from 'element-plus'
|
|||||||
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||||
import 'cesium/Build/Cesium/Widgets/widgets.css'
|
import 'cesium/Build/Cesium/Widgets/widgets.css'
|
||||||
import 'virtual:svg-icons-register'
|
import 'virtual:svg-icons-register'
|
||||||
import SvgIcon from './components/SvgIcon/index.vue'
|
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
@ -16,6 +15,5 @@ app.use(ElementPlus, {
|
|||||||
locale: zhCn,
|
locale: zhCn,
|
||||||
})
|
})
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.component('svg-icon', SvgIcon)
|
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|||||||
389
packages/screen/src/map/README.md
Normal file
@ -0,0 +1,389 @@
|
|||||||
|
# 🗺️ Cesium 地图 SDK
|
||||||
|
|
||||||
|
基于 Cesium 的 Vue 3 地图组件库,提供完整的地图交互、图层管理、底图切换等功能。
|
||||||
|
|
||||||
|
## ✨ 特性
|
||||||
|
|
||||||
|
- 🎯 **完全自包含** - 所有依赖集成在 `/map` 目录内
|
||||||
|
- 🚀 **开箱即用** - 复制目录即可使用,无需额外配置
|
||||||
|
- 📦 **模块化设计** - 组件化架构,灵活组合
|
||||||
|
- 🎨 **UI 集成** - 内置精美的地图控件 UI
|
||||||
|
- 🔧 **TypeScript 友好** - 完整的类型支持(计划中)
|
||||||
|
- 🌍 **多底图支持** - 天地图、ArcGIS、Cesium Ion 等
|
||||||
|
|
||||||
|
## 📦 核心组件
|
||||||
|
|
||||||
|
### 地图容器
|
||||||
|
- **MapViewport** - 地图视口容器,Cesium Viewer 初始化
|
||||||
|
- **MapControls** - 地图控制面板容器
|
||||||
|
|
||||||
|
### 交互控件
|
||||||
|
- **BaseMapSwitcher** - 底图切换器
|
||||||
|
- **LayerDirectoryControl** - 图层目录管理
|
||||||
|
- **MapCompass** - 指南针导航
|
||||||
|
- **SceneModeToggle** - 2D/3D 场景切换
|
||||||
|
|
||||||
|
### 工具组件
|
||||||
|
- **MapIcon** - SVG 图标组件
|
||||||
|
|
||||||
|
### 状态管理
|
||||||
|
- **useMapStore** - 地图核心状态管理
|
||||||
|
- **useMapUiStore** - 地图 UI 状态管理
|
||||||
|
|
||||||
|
### Composables
|
||||||
|
- **useMapViewSnapshot** - 地图视图快照管理
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 快速开始
|
||||||
|
|
||||||
|
### 1. 复制模块
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 复制整个地图模块到你的项目
|
||||||
|
cp -r src/map /your-project/src/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 安装依赖
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 核心依赖
|
||||||
|
npm install cesium@^1.135.0
|
||||||
|
npm install vite-plugin-cesium@^1.2.22
|
||||||
|
npm install vite-plugin-svg-icons@^2.0.1
|
||||||
|
|
||||||
|
# Vue 生态(如果项目中没有)
|
||||||
|
npm install vue@^3.5.0 pinia@^3.0.0
|
||||||
|
npm install element-plus@^2.0.0
|
||||||
|
npm install vue-router@^4.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Vite 配置
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// vite.config.js
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import cesium from 'vite-plugin-cesium'
|
||||||
|
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
|
||||||
|
import { resolve } from 'path'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
cesium(),
|
||||||
|
createSvgIconsPlugin({
|
||||||
|
iconDirs: [resolve(__dirname, 'src/map/assets/icons')],
|
||||||
|
symbolId: 'icon-[name]'
|
||||||
|
})
|
||||||
|
],
|
||||||
|
define: {
|
||||||
|
CESIUM_BASE_URL: JSON.stringify('/cesium')
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': resolve(__dirname, 'src')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 主入口配置
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// main.js
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
import { createPinia } from 'pinia'
|
||||||
|
import ElementPlus from 'element-plus'
|
||||||
|
import 'element-plus/dist/index.css'
|
||||||
|
import 'cesium/Build/Cesium/Widgets/widgets.css'
|
||||||
|
import 'virtual:svg-icons-register'
|
||||||
|
import App from './App.vue'
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
app.use(createPinia())
|
||||||
|
app.use(ElementPlus)
|
||||||
|
app.mount('#app')
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 使用示例
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div class="map-container">
|
||||||
|
<MapViewport />
|
||||||
|
<MapControls />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { MapViewport, MapControls } from '@/map'
|
||||||
|
import { useMapStore } from '@/map'
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
|
||||||
|
const mapStore = useMapStore()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 等待地图加载完成
|
||||||
|
mapStore.onReady(() => {
|
||||||
|
console.log('地图已就绪')
|
||||||
|
|
||||||
|
// 获取地图服务
|
||||||
|
const { camera, layer } = mapStore.services()
|
||||||
|
|
||||||
|
// 飞行到指定位置
|
||||||
|
camera.setCenter(116.4074, 39.9042, 10000)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.map-container {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 API 文档
|
||||||
|
|
||||||
|
### MapViewport
|
||||||
|
|
||||||
|
地图视口容器,负责初始化 Cesium Viewer。
|
||||||
|
|
||||||
|
**Props:** 无
|
||||||
|
|
||||||
|
**Events:** 无
|
||||||
|
|
||||||
|
**说明:**
|
||||||
|
- 自动初始化 Cesium Viewer
|
||||||
|
- 配置地图基础参数(地形、场景模式等)
|
||||||
|
- 加载默认底图
|
||||||
|
- 注入 mapStore 实例
|
||||||
|
|
||||||
|
### MapControls
|
||||||
|
|
||||||
|
地图控制面板容器,包含所有交互控件。
|
||||||
|
|
||||||
|
**Props:** 无
|
||||||
|
|
||||||
|
**使用示例:**
|
||||||
|
```vue
|
||||||
|
<MapControls />
|
||||||
|
```
|
||||||
|
|
||||||
|
### useMapStore()
|
||||||
|
|
||||||
|
地图核心状态管理 Store。
|
||||||
|
|
||||||
|
**主要方法:**
|
||||||
|
- `init(viewer)` - 初始化地图实例
|
||||||
|
- `onReady(callback)` - 地图就绪回调
|
||||||
|
- `services()` - 获取地图服务(camera, layer, entity, query)
|
||||||
|
- `destroy()` - 销毁地图实例
|
||||||
|
|
||||||
|
**使用示例:**
|
||||||
|
```javascript
|
||||||
|
import { useMapStore } from '@/map'
|
||||||
|
|
||||||
|
const mapStore = useMapStore()
|
||||||
|
|
||||||
|
// 等待地图就绪
|
||||||
|
mapStore.onReady(() => {
|
||||||
|
const { camera, layer } = mapStore.services()
|
||||||
|
|
||||||
|
// 添加图层
|
||||||
|
layer.addLayer({
|
||||||
|
id: 'my-layer',
|
||||||
|
type: 'WmtsServiceLayer',
|
||||||
|
url: 'https://...',
|
||||||
|
options: { visible: true }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 高级配置
|
||||||
|
|
||||||
|
### 底图配置
|
||||||
|
|
||||||
|
编辑 `src/map/data/baseMap.json` 配置底图服务:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Groups": [
|
||||||
|
{
|
||||||
|
"Attribute": {
|
||||||
|
"rid": "tianditu-group",
|
||||||
|
"name": "天地图",
|
||||||
|
"sortValue": 1
|
||||||
|
},
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"Attribute": {
|
||||||
|
"rid": "tianditu-img",
|
||||||
|
"name": "天地图影像",
|
||||||
|
"serviceTypeName": "TiandituImgLayer",
|
||||||
|
"servicePath": "http://t{s}.tianditu.gov.cn/img_w/wmts?...",
|
||||||
|
"sortValue": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 图层目录配置
|
||||||
|
|
||||||
|
编辑 `src/map/data/layerMap.json` 配置图层目录:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"Name": "业务图层",
|
||||||
|
"Rid": "business-layers",
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"Name": "我的图层",
|
||||||
|
"Attribute": {
|
||||||
|
"rid": "my-layer-001",
|
||||||
|
"serviceTypeName": "WmtsServiceLayer",
|
||||||
|
"servicePath": "https://..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 环境变量
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# .env
|
||||||
|
VITE_CESIUM_ION_TOKEN=your_cesium_ion_token
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
src/map/
|
||||||
|
├── components/ # 地图组件
|
||||||
|
│ ├── MapViewport.vue # 地图视口
|
||||||
|
│ ├── MapControls.vue # 控制面板
|
||||||
|
│ ├── BaseMapSwitcher.vue # 底图切换器
|
||||||
|
│ ├── LayerDirectoryControl.vue # 图层目录
|
||||||
|
│ ├── MapCompass.vue # 指南针
|
||||||
|
│ └── SceneModeToggle.vue # 场景切换
|
||||||
|
├── services/ # 地图服务
|
||||||
|
│ ├── createCameraService.js # 相机服务
|
||||||
|
│ ├── createLayerService.js # 图层服务
|
||||||
|
│ ├── createEntityService.js # 实体服务
|
||||||
|
│ └── createQueryService.js # 查询服务
|
||||||
|
├── stores/ # 状态管理
|
||||||
|
│ ├── mapStore.js # 地图状态
|
||||||
|
│ └── mapUiStore.js # UI 状态
|
||||||
|
├── composables/ # 组合式函数
|
||||||
|
│ └── useMapViewSnapshot.js
|
||||||
|
├── shared/ # 共享组件
|
||||||
|
│ └── SvgIcon/ # 图标组件
|
||||||
|
├── assets/ # 资源文件
|
||||||
|
│ └── icons/ # SVG 图标
|
||||||
|
├── data/ # 配置数据
|
||||||
|
│ ├── baseMap.json # 底图配置
|
||||||
|
│ ├── mapBaseConfig.json # 地图配置
|
||||||
|
│ └── layerMap.json # 图层目录
|
||||||
|
├── utils/ # 工具函数
|
||||||
|
│ ├── pickPosition.js
|
||||||
|
│ └── utils.js
|
||||||
|
├── index.js # 导出入口
|
||||||
|
└── README.md # 本文档
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌍 浏览器支持
|
||||||
|
|
||||||
|
- Chrome >= 90
|
||||||
|
- Firefox >= 88
|
||||||
|
- Safari >= 14
|
||||||
|
- Edge >= 90
|
||||||
|
|
||||||
|
**注意:** Cesium 需要 WebGL 2.0 支持。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 依赖清单
|
||||||
|
|
||||||
|
### PeerDependencies
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cesium": "^1.135.0",
|
||||||
|
"vue": "^3.5.0",
|
||||||
|
"pinia": "^3.0.0",
|
||||||
|
"element-plus": "^2.0.0",
|
||||||
|
"vue-router": "^4.0.0"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### DevDependencies
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"vite-plugin-cesium": "^1.2.22",
|
||||||
|
"vite-plugin-svg-icons": "^2.0.1"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔨 开发指南
|
||||||
|
|
||||||
|
### 添加新组件
|
||||||
|
|
||||||
|
1. 在 `components/` 创建新组件
|
||||||
|
2. 在 `index.js` 导出组件
|
||||||
|
3. 更新本 README 文档
|
||||||
|
|
||||||
|
### 添加新服务
|
||||||
|
|
||||||
|
1. 在 `services/` 创建服务文件
|
||||||
|
2. 在 `mapStore.js` 中注册服务
|
||||||
|
3. 提供完整的 JSDoc 注释
|
||||||
|
|
||||||
|
### 添加新图标
|
||||||
|
|
||||||
|
1. 将 SVG 文件放到 `assets/icons/`
|
||||||
|
2. 使用 `<MapIcon icon-class="your-icon" />` 引用
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🤝 贡献
|
||||||
|
|
||||||
|
欢迎提交 Issue 和 Pull Request!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📮 联系方式
|
||||||
|
|
||||||
|
如有问题或建议,请通过以下方式联系:
|
||||||
|
|
||||||
|
- Issue: [GitHub Issues](#)
|
||||||
|
- Email: [your-email@example.com](#)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Generated with ❤️ by Cesium Map SDK Team**
|
||||||
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 686 B After Width: | Height: | Size: 686 B |
|
Before Width: | Height: | Size: 867 B After Width: | Height: | Size: 867 B |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
@ -27,7 +27,7 @@
|
|||||||
@click="selectBaseGroup(group)"
|
@click="selectBaseGroup(group)"
|
||||||
>
|
>
|
||||||
<div class="base-map-switcher__thumb">
|
<div class="base-map-switcher__thumb">
|
||||||
<svg-icon
|
<MapIcon
|
||||||
icon-class="GisLandcoverMap"
|
icon-class="GisLandcoverMap"
|
||||||
class="base-map-switcher__icon"
|
class="base-map-switcher__icon"
|
||||||
/>
|
/>
|
||||||
@ -55,7 +55,7 @@
|
|||||||
:aria-controls="panelId"
|
:aria-controls="panelId"
|
||||||
@click.stop="togglePanel"
|
@click.stop="togglePanel"
|
||||||
>
|
>
|
||||||
<svg-icon icon-class="GisLandcoverMap" class="base-map-switcher__trigger-icon" />
|
<MapIcon icon-class="GisLandcoverMap" class="base-map-switcher__trigger-icon" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -64,6 +64,7 @@
|
|||||||
import { computed, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'
|
import { computed, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
import MapIcon from '@/map/shared/SvgIcon/index.vue'
|
||||||
import useMapStore from '@/map/stores/mapStore'
|
import useMapStore from '@/map/stores/mapStore'
|
||||||
|
|
||||||
const panelId = 'base-map-switcher-panel'
|
const panelId = 'base-map-switcher-panel'
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
type="primary"
|
type="primary"
|
||||||
@click="togglePanel"
|
@click="togglePanel"
|
||||||
>
|
>
|
||||||
<svg-icon icon-class="GisLayers" />
|
<MapIcon icon-class="GisLayers" />
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<transition name="layer-directory-fade">
|
<transition name="layer-directory-fade">
|
||||||
@ -116,6 +116,7 @@ import { computed, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch
|
|||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { ArrowDown, ArrowUp, Close, DataAnalysis, Hide, Operation, Search, View, CollectionTag } from '@element-plus/icons-vue'
|
import { ArrowDown, ArrowUp, Close, DataAnalysis, Hide, Operation, Search, View, CollectionTag } from '@element-plus/icons-vue'
|
||||||
|
import MapIcon from '@/map/shared/SvgIcon/index.vue'
|
||||||
import useMapStore from '@/map/stores/mapStore'
|
import useMapStore from '@/map/stores/mapStore'
|
||||||
import layerCatalog from '@/map/data/layerMap.json'
|
import layerCatalog from '@/map/data/layerMap.json'
|
||||||
import { DEFAULT_VECTOR_LAYER_ID } from '@/map/utils/utils'
|
import { DEFAULT_VECTOR_LAYER_ID } from '@/map/utils/utils'
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
>
|
>
|
||||||
<!-- 指南针背景 - 根据相机朝向旋转 -->
|
<!-- 指南针背景 - 根据相机朝向旋转 -->
|
||||||
<img
|
<img
|
||||||
src="@/assets/icons/svg/compass_bg.svg"
|
src="@/map/assets/icons/compass_bg.svg"
|
||||||
alt="指南针背景"
|
alt="指南针背景"
|
||||||
class="compass-bg"
|
class="compass-bg"
|
||||||
:style="{ transform: `rotate(${-heading}deg)` }"
|
:style="{ transform: `rotate(${-heading}deg)` }"
|
||||||
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<!-- 指南针指针 - 固定指向北方 -->
|
<!-- 指南针指针 - 固定指向北方 -->
|
||||||
<img
|
<img
|
||||||
src="@/assets/icons/svg/compass.svg"
|
src="@/map/assets/icons/compass.svg"
|
||||||
alt="指南针指针"
|
alt="指南针指针"
|
||||||
class="compass-needle"
|
class="compass-needle"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -6,6 +6,8 @@ export { default as SceneModeToggle } from './components/SceneModeToggle.vue'
|
|||||||
export { default as MapCompass } from './components/MapCompass.vue'
|
export { default as MapCompass } from './components/MapCompass.vue'
|
||||||
export { default as LayerDirectoryControl } from './components/LayerDirectoryControl.vue'
|
export { default as LayerDirectoryControl } from './components/LayerDirectoryControl.vue'
|
||||||
|
|
||||||
|
export { default as MapIcon } from './shared/SvgIcon/index.vue'
|
||||||
|
|
||||||
export { default as useMapStore } from './stores/mapStore'
|
export { default as useMapStore } from './stores/mapStore'
|
||||||
export { default as useMapUiStore } from './stores/mapUiStore'
|
export { default as useMapUiStore } from './stores/mapUiStore'
|
||||||
|
|
||||||
|
|||||||
@ -69,7 +69,10 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
resolvers: [ElementPlusResolver()],
|
resolvers: [ElementPlusResolver()],
|
||||||
}),
|
}),
|
||||||
createSvgIconsPlugin({
|
createSvgIconsPlugin({
|
||||||
iconDirs: [resolve(__dirname, 'src/assets/icons/svg')],
|
iconDirs: [
|
||||||
|
resolve(__dirname, 'src/assets/icons/svg'),
|
||||||
|
resolve(__dirname, 'src/map/assets/icons')
|
||||||
|
],
|
||||||
symbolId: 'icon-[dir]-[name]',
|
symbolId: 'icon-[dir]-[name]',
|
||||||
}),
|
}),
|
||||||
cesium(),
|
cesium(),
|
||||||
|
|||||||