如上代码已经实现了一些基础功能了,现在要求如图所示,再整个地图左侧加一个如图所示的控件,题目为专题图层选择,现在主要包括人口信息,人才分布,边界,路网,景区这几个专题图层,点击勾选这几个就可以在图上展示出来,现在你就先搭个框架,后续的具体的实现功能后面在写,具体要求如下:在左侧添加一个半透明的专题图层控制面板使用卡片式设计,包含标题和分组选项实现可折叠的专题分类(人口信息、基础图层等)添加搜索过滤功能使用开关控件切换图层显示状态添加视觉反馈和悬停效果 - CSDN文库",
"datePublished": "2025-07-23",
"keywords": "
如上代码已经实现了一些基础功能了,现在要求如图所示,再整个地图左侧加一个如图所示的控件,题目为专题图层选择,现在主要包括人口信息,人才分布,边界,路网,景区这几个专题图层,点击勾选这几个就可以在图上展示出来,现在你就先搭个框架,后续的具体的实现功能后面在写,具体要求如下:在左侧添加一个半透明的专题图层控制面板
使用卡片式设计,包含标题和分组选项
实现可折叠的专题分类(人口信息、基础图层等)
添加搜索过滤功能
使用开关控件切换图层显示状态
添加视觉反馈和悬停效果",
"description": "文章浏览阅读15次。我们正在使用Vue3和Cesium构建一个左侧专题图层控制面板。这个面板需要有以下功能: 1. 可折叠的分类(例如,不同的图层类别如'基础图层'、'专题图层'等) 2. 搜索过滤功能(可以通过输入关键词快速过滤图层) 3. 开关控件(每个图层项前面有一个开关,可以控制该图层的显示和隐藏)"
}
首页<template>
<!-- 主地图容器 -->
<div id="cesium-container" ref="cesiumContainer"></div>
<!-- 鹰眼地图容器 -->
<div ref="miniMapContainer" class="mini-map" @click="handleMiniMapClick">
<div class="location-indicator" :style="indicatorStyle"></div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import * as Cesium from 'cesium';
import 'cesium/Build/Cesium/Widgets/widgets.css';
// 株洲市范围定义
const ZHUZHOU_EXTENT = {
west: 112.5,
east: 114.5,
south: 26.0,
north: 28.0
};
// 株洲市矩形区域
const ZHUZHOU_RECTANGLE = Cesium.Rectangle.fromDegrees(
ZHUZHOU_EXTENT.west,
ZHUZHOU_EXTENT.south,
ZHUZHOU_EXTENT.east,
ZHUZHOU_EXTENT.north
);
// 株洲市影像瓦片服务URL
const ZHUZHOU_TILES_URL = "http://124.232.190.30:9000/proxy/pk1725866655224/map/zzzsyx_18/{z}/{x}/{y}.png";
// 地图容器引用
const cesiumContainer = ref<HTMLDivElement | null>(null);
const miniMapContainer = ref<HTMLElement | null>(null);
// 地图实例
let viewer: Cesium.Viewer | null = null;
let overviewViewer: Cesium.Viewer | null = null;
// 位置指示器样式
const indicatorStyle = ref({
left: '50%',
top: '50%',
display: 'block'
});
// 创建株洲市影像提供者
const createZhuzhouImageryProvider = () => {
// 创建有效的版权对象
const credit = new Cesium.Credit("株洲市影像", true);
return new Cesium.UrlTemplateImageryProvider({
url: ZHUZHOU_TILES_URL,
minimumLevel: 1,
maximumLevel: 17,
tilingScheme: new Cesium.WebMercatorTilingScheme(),
credit: credit // 使用有效的Credit对象
});
};
// 创建主地图
const initMainMap = () => {
if (!cesiumContainer.value) return;
try {
// 创建株洲市影像提供者
const zhuzhouProvider = createZhuzhouImageryProvider();
// 添加世界底图作为背景
const worldProvider = new Cesium.UrlTemplateImageryProvider({
url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
credit: new Cesium.Credit("World Imagery", true)
});
// 初始化主地图
viewer = new Cesium.Viewer(cesiumContainer.value, {
baseLayerPicker: false,
animation: false,
timeline: false,
navigationHelpButton: false,
fullscreenButton: false,
geocoder: false,
homeButton: false,
sceneModePicker: false,
skyBox: false,
skyAtmosphere: false,
infoBox: false,
selectionIndicator: false,
terrainProvider: new Cesium.EllipsoidTerrainProvider(),
sceneMode: Cesium.SceneMode.SCENE3D,
});
// 隐藏版权信息
viewer.cesiumWidget.creditContainer.style.display = "none";
// 添加株洲市影像图层
viewer.imageryLayers.addImageryProvider(zhuzhouProvider);
// 添加世界底图作为背景(在株洲市影像下方)
viewer.imageryLayers.addImageryProvider(worldProvider, 0);
// 关键修改:设置初始视野为整个株洲市区域
viewer.camera.flyTo({
destination: ZHUZHOU_RECTANGLE,
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-90),
roll: 0.0
},
duration: 0 // 立即跳转,无动画
});
console.log("主地图初始化完成,加载株洲市影像");
} catch (error) {
console.error("主地图初始化失败:", error);
}
};
// 初始化鹰眼地图
const initMiniMap = () => {
if (!miniMapContainer.value) return;
try {
// 创建株洲市影像提供者
const zhuzhouProvider = createZhuzhouImageryProvider();
// 初始化鹰眼地图
overviewViewer = new Cesium.Viewer(miniMapContainer.value, {
sceneMode: Cesium.SceneMode.SCENE2D,
baseLayerPicker: false,
homeButton: false,
timeline: false,
navigationHelpButton: false,
animation: false,
scene3DOnly: true,
selectionIndicator: false,
infoBox: false,
mapProjection: new Cesium.WebMercatorProjection(),
skyBox: false,
skyAtmosphere: false,
// 禁用版权显示
creditContainer: document.createElement("div")
});
// 添加株洲市影像图层
overviewViewer.imageryLayers.addImageryProvider(zhuzhouProvider);
// 设置鹰眼地图视图为株洲范围
overviewViewer.camera.setView({
destination: ZHUZHOU_RECTANGLE
});
// 隐藏UI元素
const toolbar = overviewViewer.container.getElementsByClassName(
"cesium-viewer-toolbar"
)[0];
if (toolbar) toolbar.style.display = "none";
// 确保版权容器被禁用
overviewViewer.cesiumWidget.creditContainer.style.display = "none";
console.log("鹰眼地图初始化完成,加载株洲市影像");
} catch (error) {
console.error("鹰眼地图初始化失败:", error);
}
};
// 更新位置指示器
const updateIndicatorPosition = () => {
if (!viewer) return;
const camera = viewer.camera;
const rect = camera.computeViewRectangle();
if (!rect) return;
const center = Cesium.Rectangle.center(rect);
const lon = Cesium.Math.toDegrees(center.longitude);
const lat = Cesium.Math.toDegrees(center.latitude);
// 确保在株洲范围内
const constrainedLon = Math.max(
ZHUZHOU_EXTENT.west,
Math.min(ZHUZHOU_EXTENT.east, lon)
);
const constrainedLat = Math.max(
ZHUZHOU_EXTENT.south,
Math.min(ZHUZHOU_EXTENT.north, lat)
);
// 计算位置百分比
const lonPercent =
((constrainedLon - ZHUZHOU_EXTENT.west) /
(ZHUZHOU_EXTENT.east - ZHUZHOU_EXTENT.west)) *
100;
const latPercent =
100 -
((constrainedLat - ZHUZHOU_EXTENT.south) /
(ZHUZHOU_EXTENT.north - ZHUZHOU_EXTENT.south)) *
100;
indicatorStyle.value = {
left: `${lonPercent}%`,
top: `${latPercent}%`,
display: "block"
};
};
// 监听主地图相机变化
const setupCameraListener = () => {
if (!viewer) return;
viewer.camera.changed.addEventListener(updateIndicatorPosition);
};
// 鹰眼地图点击处理
const handleMiniMapClick = (event: MouseEvent) => {
if (!miniMapContainer.value || !overviewViewer || !viewer) return;
const rect = miniMapContainer.value.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
const xPercent = (x / rect.width) * 100;
const yPercent = (y / rect.height) * 100;
// 计算点击位置对应的经纬度
const lon = ZHUZHOU_EXTENT.west +
(xPercent / 100) * (ZHUZHOU_EXTENT.east - ZHUZHOU_EXTENT.west);
const lat = ZHUZHOU_EXTENT.north -
(yPercent / 100) * (ZHUZHOU_EXTENT.north - ZHUZHOU_EXTENT.south);
// 飞向点击位置
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(lon, lat, 5000),
duration: 1
});
};
// 组件挂载时初始化
onMounted(() => {
initMainMap();
setTimeout(() => {
initMiniMap();
setupCameraListener();
updateIndicatorPosition();
}, 1000);
});
// 组件卸载时清理
onUnmounted(() => {
if (viewer && !viewer.isDestroyed()) {
viewer.destroy();
}
if (overviewViewer && !overviewViewer.isDestroyed()) {
overviewViewer.destroy();
}
});
</script>
<style scoped>
/* 主地图容器样式 */
#cesium-container {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
background: #000;
}
/* 鹰眼地图样式 */
.mini-map {
position: absolute;
right: 20px;
bottom: 20px;
width: 200px;
height: 150px;
border: 2px solid #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
z-index: 999;
cursor: pointer;
overflow: hidden;
}
/* 位置指示器样式 */
.location-indicator {
position: absolute;
width: 14px;
height: 14px;
background: #ff3e3e;
border: 2px solid white;
border-radius: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 0 15px rgba(255, 62, 62, 1);
z-index: 100;
}
/* 隐藏版权信息 */
:deep(.cesium-widget) {
background-color: #000 !important;
}
:deep(.cesium-widget-credits),
:deep(.cesium-credit-textContainer),
:deep(.cesium-credit-expand-link) {
display: none !important;
visibility: hidden !important;
}
</style>
如上代码已经实现了一些基础功能了,现在要求如图所示,再整个地图左侧加一个如图所示的控件,题目为专题图层选择,现在主要包括人口信息,人才分布,边界,路网,景区这几个专题图层,点击勾选这几个就可以在图上展示出来,现在你就先搭个框架,后续的具体的实现功能后面在写,具体要求如下:在左侧添加一个半透明的专题图层控制面板
使用卡片式设计,包含标题和分组选项
实现可折叠的专题分类(人口信息、基础图层等)
添加搜索过滤功能
使用开关控件切换图层显示状态
添加视觉反馈和悬停效果 <think>我们正在使用Vue3和Cesium构建一个左侧专题图层控制面板。这个面板需要有以下功能:
1. 可折叠的分类(例如,不同的图层类别如“基础图层”、“专题图层”等)
2. 搜索过滤功能(可以通过输入关键词快速过滤图层)
3. 开关控件(每个图层项前面有一个开关,可以控制该图层的显示和隐藏)
设计思路:
1. 使用Vue3的Composition API进行状态管理。
2. 使用Cesium的图层控制功能(通过控制图层的show属性或者通过图层的显隐接口)。
3. 面板的UI框架可以使用Element Plus或者其他UI库,也可以自己构建。
步骤:
一、创建Vue组件
我们将创建一个名为`LayerControlPanel`的组件。
二、数据结构
定义图层组的数据结构,每个组包含组名、折叠状态、图层列表。每个图层包含名称、是否显示、图层对象(或图层在Cesium中的引用)等。
三、实现功能
1. 可折叠分类:每个组有一个标题,点击标题可以折叠/展开该组内的图层列表。
2. 搜索过滤:在面板顶部添加一个搜索框,输入关键词后,实时过滤图层名称(也可以考虑过滤组名)。
3. 开关控件:每个图层项前有一个开关,切换开关可以控制该图层在Cesium中的显示和隐藏。
四、与Cesium集成
在切换开关时,需要调用Cesium图层对象的相应方法(例如,对于`ImageryLayer`,可以设置`show`属性;对于`Entity`,可以设置`show`属性;对于`Cesium3DTileset`,可以设置`show`属性)。
五、样式设计
使用CSS进行样式设计,使面板固定在左侧,可折叠,并且有良好的用户体验。
代码示例:
1. 定义数据结构(在Vue组件中)
2. 模板结构
3. 方法实现
下面是一个简化的实现示例:</think>### 在Cesium地图应用中实现左侧专题图层控制面板
以下是基于Vue3和Cesium实现左侧可折叠专题图层控制面板的完整方案:
#### 1. 组件结构设计
```vue:文件路径 src/components/LayerControlPanel.vue
<template>
<div class="layer-control-panel">
<!-- 搜索过滤区域 -->
<div class="search-box">
<input
v-model="searchQuery"
placeholder="搜索图层..."
class="search-input"
/>
</div>
<!-- 可折叠分类区域 -->
<div
v-for="category in filteredCategories"
:key="category.id"
class="category"
>
<div class="category-header" @click="toggleCategory(category.id)">
<span>{{ category.name }}</span>
<span class="toggle-icon">
{{ category.expanded ? '▼' : '►' }}
</span>
</div>
<!-- 图层列表 -->
<div v-show="category.expanded" class="layer-list">
<div
v-for="layer in category.layers"
:key="layer.id"
class="layer-item"
>
<label class="switch">
<input
type="checkbox"
v-model="layer.visible"
@change="toggleLayer(layer)"
>
<span class="slider round"></span>
</label>
<span class="layer-name">{{ layer.name }}</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import * as Cesium from 'cesium'
// 图层数据结构
const categories = ref([
{
id: 'base',
name: '基础图层',
expanded: true,
layers: [
{ id: 'osm', name: 'OpenStreetMap', visible: true, type: 'imagery' },
{ id: 'terrain', name: '地形高程', visible: true, type: 'terrain' }
]
},
{
id: 'thematic',
name: '专题图层',
expanded: true,
layers: [
{ id: 'population', name: '人口密度', visible: false, type: 'geojson' },
{ id: 'traffic', name: '交通流量', visible: false, type: 'geojson' }
]
}
])
const searchQuery = ref('')
// Cesium viewer实例
const viewer = ref(null)
// 初始化Cesium
onMounted(() => {
viewer.value = new Cesium.Viewer('cesiumContainer', {
baseLayerPicker: false,
animation: false,
timeline: false
})
// 初始化图层
initLayers()
})
// 初始化图层到Cesium
function initLayers() {
categories.value.forEach(category => {
category.layers.forEach(layer => {
if (layer.type === 'imagery') {
viewer.value.imageryLayers.addImageryProvider(
new Cesium.OpenStreetMapImageryProvider()
).show = layer.visible
}
// 其他图层类型初始化逻辑...
})
})
}
// 切换图层显示状态
function toggleLayer(layer) {
if (layer.type === 'imagery') {
const imageryLayer = viewer.value.imageryLayers.get(layer.id)
if (imageryLayer) imageryLayer.show = layer.visible
}
// 其他图层类型切换逻辑...
}
// 折叠/展开分类
function toggleCategory(categoryId) {
const category = categories.value.find(c => c.id === categoryId)
if (category) category.expanded = !category.expanded
}
// 过滤图层
const filteredCategories = computed(() => {
if (!searchQuery.value) return categories.value
return categories.value.map(category => ({
...category,
layers: category.layers.filter(layer =>
layer.name.toLowerCase().includes(searchQuery.value.toLowerCase())
)
})).filter(category => category.layers.length > 0)
})
</script>
<style scoped>
.layer-control-panel {
position: absolute;
top: 10px;
left: 10px;
width: 300px;
background: rgba(42, 42, 42, 0.8);
border-radius: 8px;
padding: 15px;
z-index: 100;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
color: white;
}
.search-box {
margin-bottom: 15px;
}
.search-input {
width: 100%;
padding: 8px;
border-radius: 4px;
border: 1px solid #555;
background: #333;
color: white;
}
.category-header {
padding: 10px;
background: #333;
border-radius: 4px;
margin-bottom: 5px;
cursor: pointer;
display: flex;
justify-content: space-between;
}
.layer-list {
padding-left: 15px;
}
.layer-item {
display: flex;
align-items: center;
padding: 8px 0;
}
.layer-name {
margin-left: 10px;
}
/* 开关控件样式 */
.switch {
position: relative;
display: inline-block;
width: 40px;
height: 20px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 34px;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 2px;
bottom: 2px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .slider {
background-color: #4caf50;
}
input:checked + .slider:before {
transform: translateX(20px);
}
</style>
```
#### 2. 在主应用中使用组件
```vue:文件路径 src/App.vue
<template>
<div id="app">
<div id="cesiumContainer"></div>
<LayerControlPanel />
</div>
</template>
<script setup>
import LayerControlPanel from './components/LayerControlPanel.vue'
</script>
<style>
#app {
position: relative;
width: 100vw;
height: 100vh;
}
#cesiumContainer {
width: 100%;
height: 100%;
}
</style>
```
#### 3. 关键功能实现说明
1. **可折叠分类**:
- 使用`v-for`渲染分类数组
- 每个分类对象包含`expanded`属性控制展开状态
- 点击分类头部触发`toggleCategory`方法切换状态
2. **搜索过滤**:
- 使用`v-model`绑定搜索框输入值
- 通过计算属性`filteredCategories`实现实时过滤
- 过滤逻辑同时处理图层名称匹配和空结果处理
3. **图层开关**:
- 使用CSS自定义开关样式
- 通过`v-model`双向绑定图层的`visible`属性
- `toggleLayer`方法同步更新Cesium图层状态
4. **Cesium集成**:
- 在`onMounted`生命周期初始化Cesium viewer
- `initLayers`函数初始化图层到Cesium
- 图层状态变化时调用Cesium API更新显示
#### 4. 优化建议
1. **性能优化**:
- 对于大型数据集使用虚拟滚动
- 添加防抖处理搜索输入
- 使用Web Worker处理复杂过滤逻辑
2. **功能增强**:
```javascript
// 添加图层透明度控制
function setLayerOpacity(layer, opacity) {
if (layer.type === 'imagery') {
const imageryLayer = viewer.value.imageryLayers.get(layer.id)
imageryLayer.alpha = opacity
}
}
// 添加图层排序控制
function moveLayer(layer, direction) {
const imageryLayer = viewer.value.imageryLayers.get(layer.id)
const currentIndex = viewer.value.imageryLayers.indexOf(imageryLayer)
if (direction === 'up' && currentIndex > 0) {
viewer.value.imageryLayers.raise(imageryLayer)
} else if (direction === 'down') {
viewer.value.imageryLayers.lower(imageryLayer)
}
}
```
3. **状态持久化**:
```javascript
// 使用localStorage保存面板状态
function savePanelState() {
const state = {
categories: categories.value,
searchQuery: searchQuery.value
}
localStorage.setItem('layerPanelState', JSON.stringify(state))
}
function loadPanelState() {
const savedState = localStorage.getItem('layerPanelState')
if (savedState) {
const state = JSON.parse(savedState)
categories.value = state.categories
searchQuery.value = state.searchQuery
}
}
```
#### 5. 注意事项
1. **Cesium资源加载**:
- 确保在`index.html`中正确引入Cesium库和样式
- 配置CESIUM_BASE_URL指向正确的资源路径
2. **响应式设计**:
- 添加媒体查询适应移动设备
- 实现面板可拖动调整大小功能
3. **图层管理**:
- 为每个图层添加唯一标识符
- 实现图层加载状态指示器
- 添加错误处理机制
此实现提供了完整的专题图层控制功能,包含可折叠分类、实时搜索过滤和开关控件。通过响应式设计确保良好的用户体验,并预留了扩展接口用于添加更多高级功能[^1]。
相关推荐

//cesium.vue
<template>
<canvas id="threeContainer"></canvas>
<Overlayui />
</template>
<script setup>
import { onMounted } from "vue";
import { EffectComposer,initCesium, initThreejs, init3DObject, startRenderLoop } from "./three-cesium/three_cesium.js";
onMounted(() => {
initCesium()
initThreejs()
init3DObject()
startRenderLoop()
EffectComposer()
})
</script>
<style scoped>
.container {
position: relative;
width: 100%;
height: 100vh;
overflow: hidden;
}
#cesiumContainer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
#threeContainer {
position: absolute;
height: 100%;
width: 100%;
margin: 0;
z-index: 10;
pointer-events: none;
}
.cesium-popup {
background-color: rgba(0, 0, 0, 0.75);
color: white;
padding: 8px 12px;
border-radius: 5px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5);
font-size: 14px;
z-index: 999;
pointer-events: auto;
transition: opacity 0.2s ease;
opacity: 1;
}
.popup-header {
font-weight: bold;
margin-bottom: 4px;
}
.popup-body {
font-size: 12px;
}
</style>

<template>
晴 31℃/西北风
株洲市"天空地水"动态监测平台
{{ currentTime }} {{ currentWeek }}
<select v-model="projectionType" @change="switchProjectionType">
<option value="c">经纬度投影</option>
<option value="w">球面墨卡托投影</option>
</select>
<select v-model="selectedMapType" @change="switchMapType">
<option v-for="option in mapOptions" :value="option.value" :key="option.value">
{{ option.label }}
</option>
</select>
<select v-model="networkType" @change="switchNetworkType">
<option value="internal">内网地图</option>
<option value="external">外网地图</option>
</select>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import * as Cesium from 'cesium';
import "cesium/Build/Cesium/Widgets/widgets.css";
// 天地图服务密钥
const TIANDITU_TOKEN = '72d487f15710ca558987b9baaba13736';
// 当前时间和星期
const currentTime = ref("");
const currentWeek = ref("");
const weekMap = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
const updateTime = () => {
const now = new Date();
currentTime.value = now.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}).replace(/\//g, '-').replace(/(\d{4})-(\d{2})-(\d{2})/, '$ 1年$ 2月$ 3日');
currentWeek.value = weekMap[now.getDay()];
};
setInterval(updateTime, 1000);
updateTime();
// 网络类型选择
const networkType = ref('external'); // 默认外网
// 地图类型选择
const selectedMapType = ref('img_c'); // 默认显示影像+注记
// 投影类型选择
const projectionType = ref('c'); // 默认经纬度投影
// 地图选项配置
const mapOptions = ref([
{ value: 'img_c', label: '影像+注记' },
{ value: 'vec_c', label: '矢量+注记' }
]);
// Cesium相关变量
let viewer = null;
let tiandituLayers = { baseLayer: null, annotationLayer: null };
// 网络类型切换
const switchNetworkType = () => {
// 根据网络类型更新地图选项
if (networkType.value === 'internal') {
mapOptions.value = [
{ value: 'img', label: '内网影像地图' },
{ value: 'vec', label: '内网矢量地图' }
];
// 设置内网默认地图类型
selectedMapType.value = 'img';
} else {
mapOptions.value = [
{ value: 'img', label: '天地图影像' },
{ value: 'img_c', label: '影像+注记' },
{ value: 'vec', label: '矢量地图' },
{ value: 'vec_c', label: '矢量+注记' },
{ value: 'ter', label: '地形图' }
];
// 设置外网默认地图类型
selectedMapType.value = 'img_c';
}
// 重新加载地图
loadMapService(selectedMapType.value);
// 重新加载石河子数据并定位
loadShiheziVector(viewer);
setTimeout(flyToShihezi, 500);
};
// 地图类型切换
const switchMapType = () => {
if (viewer) {
loadMapService(selectedMapType.value);
}
};
// 投影类型切换
const switchProjectionType = () => {
if (viewer) {
// 重新加载地图
loadMapService(selectedMapType.value);
// 重新定位到石河子
setTimeout(flyToShihezi, 500);
}
};
// 加载地图服务
const loadMapService = (type) => {
if (!viewer || !viewer.imageryLayers) return;
// 清除现有图层
if (tiandituLayers.baseLayer) viewer.imageryLayers.remove(tiandituLayers.baseLayer);
if (tiandituLayers.annotationLayer) viewer.imageryLayers.remove(tiandituLayers.annotationLayer);
if (networkType.value === 'internal') {
// 内网地图服务 - 支持c和w投影
const baseUrl = "http://59.255.48.160:81";
// 根据投影类型选择后缀
const projectionSuffix = projectionType.value === 'c' ? '_c' : '_w';
if (type === 'img') {
// 内网影像地图
tiandituLayers.baseLayer = viewer.imageryLayers.addImageryProvider(
new Cesium.WebMapTileServiceImageryProvider({
url: ${baseUrl}/img${projectionSuffix}/wmts,
layer: "img",
style: "default",
tileMatrixSetID: projectionType.value, // 使用选择的投影类型
format: "tiles",
credit: new Cesium.Credit("内网影像地图"),
maximumLevel: 18
})
);
} else if (type === 'vec') {
// 内网矢量地图
tiandituLayers.baseLayer = viewer.imageryLayers.addImageryProvider(
new Cesium.WebMapTileServiceImageryProvider({
url: ${baseUrl}/vec${projectionSuffix}/wmts,
layer: "vec",
style: "default",
tileMatrixSetID: projectionType.value, // 使用选择的投影类型
format: "tiles",
credit: new Cesium.Credit("内网矢量地图"),
maximumLevel: 18
})
);
}
} else {
// 外网天地图服务 - 支持c和w投影
// 根据投影类型动态构建URL
const baseUrlTemplate = https://t{s}.tianditu.gov.cn/{layer}_${projectionType.value}/wmts?tk=${TIANDITU_TOKEN};
// 图层配置映射表 - 根据投影类型调整图层名称
const layerConfigs = {
img: {
layer: "img",
credit: "天地图影像"
},
img_c: {
baseLayer: {
layer: "img",
credit: "天地图影像"
},
annotationLayer: {
layer: projectionType.value === 'c' ? "cia" : "cia_w",
credit: "天地图注记"
}
},
vec: {
layer: "vec",
credit: "天地图矢量"
},
vec_c: {
baseLayer: {
layer: "vec",
credit: "天地图矢量"
},
annotationLayer: {
layer: projectionType.value === 'c' ? "cva" : "cva_w",
credit: "天地图注记"
}
},
ter: {
layer: "ter",
credit: "天地图地形"
}
};
const config = layerConfigs[type];
if (!config) return;
// 添加基础图层
tiandituLayers.baseLayer = viewer.imageryLayers.addImageryProvider(
new Cesium.WebMapTileServiceImageryProvider({
url: baseUrlTemplate.replace('{layer}', config.layer || config.baseLayer.layer),
layer: config.layer || config.baseLayer.layer,
style: "default",
tileMatrixSetID: projectionType.value, // 使用选择的投影类型
format: "tiles",
credit: new Cesium.Credit(config.credit || config.baseLayer.credit),
subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
maximumLevel: 18
})
);
// 添加注记图层(如果需要)
if (config.annotationLayer) {
tiandituLayers.annotationLayer = viewer.imageryLayers.addImageryProvider(
new Cesium.WebMapTileServiceImageryProvider({
url: baseUrlTemplate.replace('{layer}', config.annotationLayer.layer),
layer: config.annotationLayer.layer,
style: "default",
tileMatrixSetID: projectionType.value, // 使用选择的投影类型
format: "tiles",
credit: new Cesium.Credit(config.annotationLayer.credit),
subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
maximumLevel: 18
})
);
}
}
};
// 保存石河子范围矩形
const shiheziRectangle = ref(null);
// 加载石河子矢量数据
const loadShiheziVector = async (viewer) => {
try {
// 清除现有石河子数据
const dataSources = viewer.dataSources;
for (let i = dataSources.length - 1; i >= 0; i--) {
if (dataSources.get(i).name === 'shihezi') {
dataSources.remove(dataSources.get(i));
}
}
// 加载石河子市整体边界
const cityDataSource = await Cesium.GeoJsonDataSource.load(
"https://geo.datav.aliyun.com/areas_v3/bound/geojson?code=659001",
{
clampToGround: true,
stroke: Cesium.Color.GREEN.withAlpha(0.8),
strokeWidth: 2,
fill: Cesium.Color.WHITE.withAlpha(0.3),
name: 'shihezi' // 添加标识
}
);
viewer.dataSources.add(cityDataSource);
// 添加石河子市标签
cityDataSource.entities.values.forEach(entity => {
if (entity.polygon && entity.properties && entity.properties.name) {
const hierarchy = entity.polygon.hierarchy.getValue(Cesium.JulianDate.now());
const positions = hierarchy.positions;
// 计算地理中心
const center = Cesium.BoundingSphere.fromPoints(positions).center;
entity.label = {
text: "石河子市",
font: '18px 黑体',
fillColor: Cesium.Color.WHITE,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 3,
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
verticalOrigin: Cesium.VerticalOrigin.CENTER,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
pixelOffset: new Cesium.Cartesian2(0, 0),
disableDepthTestDistance: Number.POSITIVE_INFINITY,
scaleByDistance: new Cesium.NearFarScalar(1e3, 1.0, 1e6, 0.5)
};
entity.position = center;
}
});
// 保存石河子范围(地理坐标)
const positions = cityDataSource.entities.values[0].polygon.hierarchy.getValue().positions;
const cartographicPositions = positions.map(pos =>
Cesium.Cartographic.fromCartesian(pos)
);
const minLon = Math.min(...cartographicPositions.map(p => p.longitude));
const maxLon = Math.max(...cartographicPositions.map(p => p.longitude));
const minLat = Math.min(...cartographicPositions.map(p => p.latitude));
const maxLat = Math.max(...cartographicPositions.map(p => p.latitude));
shiheziRectangle.value = new Cesium.Rectangle(
minLon, minLat, maxLon, maxLat
);
} catch (error) {
console.error("加载石河子数据失败:", error);
}
};
// 定位到石河子
const flyToShihezi = () => {
if (viewer && shiheziRectangle.value) {
// 根据当前投影类型转换坐标
let destination;
if (projectionType.value === 'c') {
// 经纬度投影直接使用地理坐标
destination = shiheziRectangle.value;
} else {
// 墨卡托投影需要转换坐标
const sw = Cesium.WebMercatorProjection.geodeticToWebMercator(
new Cesium.Cartographic(shiheziRectangle.value.west, shiheziRectangle.value.south)
);
const ne = Cesium.WebMercatorProjection.geodeticToWebMercator(
new Cesium.Cartographic(shiheziRectangle.value.east, shiheziRectangle.value.north)
);
destination = new Cesium.Rectangle(
sw.x, sw.y, ne.x, ne.y
);
}
viewer.camera.flyTo({
destination: destination,
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-70),
roll: 0
},
duration: 1
});
}
};
onMounted(async () => {
try {
// 设置Cesium Ion访问令牌
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI0ZTdiZDBhZS0xNzBhLTRjZGUtOTY4NC1kYzA5ZDEyNGEyNjUiLCJpZCI6MzE2OTI5LCJpYXQiOjE3NTEzMzg2Mzh9.9QHGIwaWkUOX0NOSre5369rrf1k6bGhZu7xUQia4JmE';
// 初始化Cesium Viewer
viewer = new Cesium.Viewer('cesiumContainer', {
baseLayerPicker: false,
timeline: false,
animation: false,
geocoder: false,
sceneModePicker: false,
navigationHelpButton: false,
homeButton: false,
selectionIndicator: false,
infoBox: false,
navigationInstructionsInitiallyVisible: false,
scene3DOnly: true,
terrainProvider: new Cesium.EllipsoidTerrainProvider(),
// 使用默认投影类型初始化底图
imageryProvider: new Cesium.WebMapTileServiceImageryProvider({
url: https://t{s}.tianditu.gov.cn/img_${projectionType.value}/wmts?tk=${TIANDITU_TOKEN},
layer: "img",
style: "default",
tileMatrixSetID: projectionType.value,
subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
maximumLevel: 18
})
});
// 强制设置中国视角
viewer.camera.setView({
destination: Cesium.Rectangle.fromDegrees(73.0, 3.0, 136.0, 59.0)
});
// 加载石河子数据
await loadShiheziVector(viewer);
flyToShihezi();
// 加载地图服务
loadMapService(selectedMapType.value);
} catch (error) {
console.error("初始化失败:", error);
}
});
</script>
<style scoped>
/* 基础样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Microsoft YaHei", sans-serif;
}
.monitor-container {
width: 100vw;
height: 100vh;
background: radial-gradient(circle at center, #0c2a50 0%, #0a1a35 100%);
overflow: hidden;
position: fixed;
top: 0;
left: 0;
display: flex;
flex-direction: column;
}
/* 顶部导航栏样式 - 重新布局 */
.top-bar {
height: 70px;
background: linear-gradient(90deg, #0a1a35 0%, #1a3a6e 50%, #0a1a35 100%);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 15px;
position: relative;
z-index: 100;
border-bottom: 1px solid rgba(0, 150, 255, 0.3);
}
/* 左侧区域 */
.left-section {
display: flex;
align-items: center;
flex: 0 0 auto;
}
/* 中间区域 */
.center-section {
flex: 1 1 auto;
display: flex;
justify-content: center;
}
/* 右侧区域 */
.right-section {
display: flex;
align-items: center;
flex: 0 0 auto;
gap: 10px;
}
/* 控件组 */
.controls-group {
display: flex;
gap: 10px;
}
/* 天气模块 */
.weather-module {
display: flex;
align-items: center;
color: white;
font-size: 14px;
min-width: 120px;
justify-content: center;
}
.weather-icon {
color: #ffcc00;
margin-right: 8px;
font-size: 16px;
}
/* 梯形标题样式 */
.platform-title {
position: relative;
z-index: 10;
max-width: 500px; /* 限制最大宽度 */
}
.trapezoid-bg {
position: relative;
padding: 0 40px;
height: 70px;
display: flex;
align-items: center;
}
.trapezoid-bg::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 60, 113, 0.6);
clip-path: polygon(0% 0%, 100% 0%, 90% 100%, 10% 100%);
z-index: -1;
}
.platform-title h1 {
font-size: 24px;
font-weight: bold;
background: linear-gradient(to bottom, #a2e7eb, #00f2fe);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-shadow: 0 0 8px rgba(0, 242, 254, 0.3);
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
max-width: 100%;
}
/* 时间模块 */
.time-module {
color: white;
font-size: 14px;
min-width: 220px;
text-align: center;
white-space: nowrap;
}
/* 所有控件统一样式 */
.network-switcher select,
.map-switcher select,
.projection-switcher select {
background: rgba(10, 26, 53, 0.8);
color: #66ffff;
border: 1px solid rgba(0, 150, 255, 0.5);
border-radius: 4px;
padding: 6px 12px;
font-size: 14px;
outline: none;
cursor: pointer;
box-shadow: 0 0 8px rgba(0, 150, 255, 0.3);
transition: all 0.3s ease;
min-width: 120px;
}
.network-switcher select:hover,
.map-switcher select:hover,
.projection-switcher select:hover {
background: rgba(26, 58, 110, 0.8);
border-color: #00f2fe;
box-shadow: 0 0 12px rgba(0, 242, 254, 0.5);
}
.network-switcher select:focus,
.map-switcher select:focus,
.projection-switcher select:focus {
border-color: #00f2fe;
}
/* Cesium容器 */
#cesiumContainer {
width: 100%;
height: calc(100vh - 70px);
background-color: #000;
position: relative;
}
/* 响应式调整 */
@media (max-width: 1600px) {
.platform-title h1 {
font-size: 20px;
}
.weather-module, .time-module {
font-size: 13px;
}
.network-switcher select,
.map-switcher select,
.projection-switcher select {
padding: 5px 10px;
font-size: 13px;
min-width: 110px;
}
}
@media (max-width: 1400px) {
.platform-title h1 {
font-size: 18px;
}
.trapezoid-bg {
padding: 0 30px;
}
.time-module {
min-width: 180px;
}
.controls-group {
gap: 8px;
}
}
@media (max-width: 1200px) {
.platform-title h1 {
font-size: 16px;
}
.trapezoid-bg {
padding: 0 20px;
}
.time-module {
min-width: 160px;
font-size: 12px;
}
.network-switcher select,
.map-switcher select,
.projection-switcher select {
padding: 4px 8px;
font-size: 12px;
min-width: 100px;
}
}
@media (max-width: 992px) {
.time-module {
display: none;
}
.platform-title h1 {
font-size: 14px;
}
.trapezoid-bg {
padding: 0 15px;
}
.weather-module {
min-width: auto;
}
.network-switcher select,
.map-switcher select,
.projection-switcher select {
padding: 4px 6px;
font-size: 11px;
min-width: 90px;
}
}
@media (max-width: 768px) {
.weather-module {
display: none;
}
.platform-title h1 {
font-size: 12px;
}
.trapezoid-bg {
padding: 0 10px;
}
.controls-group {
gap: 5px;
}
.network-switcher select,
.map-switcher select,
.projection-switcher select {
min-width: 80px;
}
}
</style>
如上所示代码,还有一个问题,当投影切换到经纬度投影时石河子的位置不对,离实际位置很远,没有切换,这是为什么,你修改一下发我代码

<template> 晴 31℃/西北风 株洲市"天空地水"动态监测平台 {{ currentTime }} {{ currentWeek }} <select v-model="projectionType" @change="switchProjectionType"> <option value="c">经纬度投影</option> <option value="w">球面墨卡托投影</option> </select> <select v-model="selectedMapType" @change="switchMapType"> <option v-for="option in mapOptions" :value="option.value" :key="option.value"> {{ option.label }} </option> </select> <select v-model="networkType" @change="switchNetworkType"> <option value="internal">内网地图</option> <option value="external">外网地图</option> </select> </template> <script setup> import { ref, onMounted } from 'vue'; import * as Cesium from 'cesium'; import "cesium/Build/Cesium/Widgets/widgets.css"; // 天地图服务密钥 const TIANDITU_TOKEN = '72d487f15710ca558987b9baaba13736'; // 当前时间和星期 const currentTime = ref(""); const currentWeek = ref(""); const weekMap = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']; const updateTime = () => { const now = new Date(); currentTime.value = now.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }).replace(/\//g, '-').replace(/(\d{4})-(\d{2})-(\d{2})/, '$ 1年$ 2月$ 3日'); currentWeek.value = weekMap[now.getDay()]; }; setInterval(updateTime, 1000); updateTime(); // 网络类型选择 const networkType = ref('external'); // 默认外网 // 地图类型选择 const selectedMapType = ref('img_c'); // 默认显示影像+注记 // 投影类型选择 const projectionType = ref('c'); // 默认经纬度投影 // 地图选项配置 const mapOptions = ref([ { value: 'img_c', label: '影像+注记' }, { value: 'vec_c', label: '矢量+注记' } ]); // Cesium相关变量 let viewer = null; let tiandituLayers = { baseLayer: null, annotationLayer: null }; // 网络类型切换 const switchNetworkType = () => { // 重置投影类型为默认值 projectionType.value = 'c'; // 根据网络类型更新地图选项 if (networkType.value === 'internal') { mapOptions.value = [ { value: 'img_c', label: '内网影像地图' }, { value: 'vec_c', label: '内网矢量地图' } ]; // 设置内网默认地图类型 selectedMapType.value = 'img_c'; } else { mapOptions.value = [ { value: 'img', label: '天地图影像' }, { value: 'img_c', label: '影像+注记' }, { value: 'vec', label: '矢量地图' }, { value: 'vec_c', label: '矢量+注记' }, { value: 'ter', label: '地形图' } ]; // 设置外网默认地图类型 selectedMapType.value = 'img_c'; } // 重新加载地图 loadMapService(selectedMapType.value); // 重新加载石河子数据并定位 loadShiheziVector(viewer, projectionType.value); setTimeout(flyToShihezi, 500); }; // 地图类型切换 const switchMapType = () => { if (viewer) { loadMapService(selectedMapType.value); } }; // // 投影类型切换 // const switchProjectionType = () => { // if (viewer && networkType.value === 'external') { // loadMapService(selectedMapType.value); // } // }; // 加载地图服务 const loadMapService = (type) => { if (!viewer || !viewer.imageryLayers) return; // 清除现有图层 if (tiandituLayers.baseLayer) viewer.imageryLayers.remove(tiandituLayers.baseLayer); if (tiandituLayers.annotationLayer) viewer.imageryLayers.remove(tiandituLayers.annotationLayer); if (networkType.value === 'internal') { // 内网地图服务 - 只支持c投影 const baseUrl = "http://59.255.48.160:81"; if (type === 'img_c') { // 内网影像地图 tiandituLayers.baseLayer = viewer.imageryLayers.addImageryProvider( new Cesium.WebMapTileServiceImageryProvider({ url: ${baseUrl}/img_c/wmts, layer: "img", style: "default", tileMatrixSetID: "c", format: "tiles", credit: new Cesium.Credit("内网影像地图"), maximumLevel: 18 }) ); } else if (type === 'vec_c') { // 内网矢量地图 tiandituLayers.baseLayer = viewer.imageryLayers.addImageryProvider( new Cesium.WebMapTileServiceImageryProvider({ url: ${baseUrl}/vec_c/wmts, layer: "vec", style: "default", tileMatrixSetID: "c", format: "tiles", credit: new Cesium.Credit("内网矢量地图"), maximumLevel: 18 }) ); } } else { // 外网天地图服务 - 支持c和w投影 // 根据投影类型动态构建URL const baseUrlTemplate = https://t{s}.tianditu.gov.cn/{layer}_${projectionType.value}/wmts?tk=${TIANDITU_TOKEN}; // 图层配置映射表 - 根据投影类型调整图层名称 const layerConfigs = { img: { layer: "img", credit: "天地图影像" }, img_c: { baseLayer: { layer: "img", credit: "天地图影像" }, annotationLayer: { layer: projectionType.value === 'c' ? "cia" : "cia_w", credit: "天地图注记" } }, vec: { layer: "vec", credit: "天地图矢量" }, vec_c: { baseLayer: { layer: "vec", credit: "天地图矢量" }, annotationLayer: { layer: projectionType.value === 'c' ? "cva" : "cva_w", credit: "天地图注记" } }, ter: { layer: "ter", credit: "天地图地形" } }; const config = layerConfigs[type]; if (!config) return; // 添加基础图层 tiandituLayers.baseLayer = viewer.imageryLayers.addImageryProvider( new Cesium.WebMapTileServiceImageryProvider({ url: baseUrlTemplate.replace('{layer}', config.layer || config.baseLayer.layer), layer: config.layer || config.baseLayer.layer, style: "default", tileMatrixSetID: projectionType.value, // 使用选择的投影类型 format: "tiles", credit: new Cesium.Credit(config.credit || config.baseLayer.credit), subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'], maximumLevel: 18 }) ); // 添加注记图层(如果需要) if (config.annotationLayer) { tiandituLayers.annotationLayer = viewer.imageryLayers.addImageryProvider( new Cesium.WebMapTileServiceImageryProvider({ url: baseUrlTemplate.replace('{layer}', config.annotationLayer.layer), layer: config.annotationLayer.layer, style: "default", tileMatrixSetID: projectionType.value, // 使用选择的投影类型 format: "tiles", credit: new Cesium.Credit(config.annotationLayer.credit), subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'], maximumLevel: 18 }) ); } } }; // // 加载株洲矢量数据 // const loadZhuzhouVector = async (viewer) => { // try { // // 加载株洲市整体边界 // const cityDataSource = await Cesium.GeoJsonDataSource.load( // "https://geo.datav.aliyun.com/areas_v3/bound/geojson?code=430200", // { // clampToGround: true, // stroke: Cesium.Color.GREEN.withAlpha(0.8), // strokeWidth: 2, // fill: Cesium.Color.WHITE.withAlpha(0.3) // } // ); // viewer.dataSources.add(cityDataSource); // // 加载株洲各区县边界 // const countyDataSource = await Cesium.GeoJsonDataSource.load( // "https://geo.datav.aliyun.com/areas_v3/bound/geojson?code=430200_full", // { // clampToGround: true, // stroke: Cesium.Color.WHITE, // strokeWidth: 1, // fill: Cesium.Color.TRANSPARENT // } // ); // viewer.dataSources.add(countyDataSource); // // 添加区县标签 // countyDataSource.entities.values.forEach(entity => { // if (entity.polygon && entity.properties && entity.properties.name) { // const hierarchy = entity.polygon.hierarchy.getValue(Cesium.JulianDate.now()); // const center = Cesium.BoundingSphere.fromPoints(hierarchy.positions).center; // entity.label = { // text: entity.properties.name, // font: '18px 黑体', // fillColor: Cesium.Color.WHITE, // outlineColor: Cesium.Color.BLACK, // outlineWidth: 3, // style: Cesium.LabelStyle.FILL_AND_OUTLINE, // verticalOrigin: Cesium.VerticalOrigin.CENTER, // horizontalOrigin: Cesium.HorizontalOrigin.CENTER, // pixelOffset: new Cesium.Cartesian2(0, 0), // disableDepthTestDistance: Number.POSITIVE_INFINITY, // scaleByDistance: new Cesium.NearFarScalar(1e3, 1.0, 1e6, 0.5) // }; // entity.position = center; // } // }); // // 定位到株洲范围 // const rectangle = Cesium.Rectangle.fromDegrees(113.50, 24.94, 113.60, 26.84); // viewer.camera.flyTo({ // destination: rectangle, // orientation: { // heading: Cesium.Math.toRadians(0), // pitch: Cesium.Math.toRadians(-70), // roll: 0 // }, // duration: 2 // }); // } catch (error) { // console.error("加载株洲数据失败:", error); // const chinaRectangle = Cesium.Rectangle.fromDegrees(73.0, 3.0, 136.0, 59.0); // viewer.camera.flyTo({ // destination: chinaRectangle // }); // } // }; // 修改加载石河子矢量数据 // 保存石河子范围矩形 const shiheziRectangle = ref(null); // 加载石河子矢量数据(新增函数) const loadShiheziVector = async (viewer, projectionType = 'c') => { try { // 清除现有石河子数据 const dataSources = viewer.dataSources; for (let i = dataSources.length - 1; i >= 0; i--) { if (dataSources.get(i).name === 'shihezi') { dataSources.remove(dataSources.get(i)); } } // 加载石河子市整体边界 const cityDataSource = await Cesium.GeoJsonDataSource.load( "https://geo.datav.aliyun.com/areas_v3/bound/geojson?code=659001", { clampToGround: true, stroke: Cesium.Color.GREEN.withAlpha(0.8), strokeWidth: 2, fill: Cesium.Color.WHITE.withAlpha(0.3), name: 'shihezi' // 添加标识 } ); viewer.dataSources.add(cityDataSource); // 添加石河子市标签 cityDataSource.entities.values.forEach(entity => { if (entity.polygon && entity.properties && entity.properties.name) { const hierarchy = entity.polygon.hierarchy.getValue(Cesium.JulianDate.now()); const positions = hierarchy.positions; // 根据投影类型转换坐标 let center; if (projectionType === 'c') { // 经纬度投影直接使用地理中心 center = Cesium.BoundingSphere.fromPoints(positions).center; } else { // 墨卡托投影需要转换坐标 const cartographicPositions = positions.map(pos => Cesium.Cartographic.fromCartesian(pos) ); // 计算平均经纬度 const avgLon = cartographicPositions.reduce((sum, pos) => sum + pos.longitude, 0) / cartographicPositions.length; const avgLat = cartographicPositions.reduce((sum, pos) => sum + pos.latitude, 0) / cartographicPositions.length; // 转换为墨卡托坐标 center = Cesium.Cartesian3.fromDegrees( Cesium.Math.toDegrees(avgLon), Cesium.Math.toDegrees(avgLat) ); } entity.label = { text: "石河子市", font: '18px 黑体', fillColor: Cesium.Color.WHITE, outlineColor: Cesium.Color.BLACK, outlineWidth: 3, style: Cesium.LabelStyle.FILL_AND_OUTLINE, verticalOrigin: Cesium.VerticalOrigin.CENTER, horizontalOrigin: Cesium.HorizontalOrigin.CENTER, pixelOffset: new Cesium.Cartesian2(0, 0), disableDepthTestDistance: Number.POSITIVE_INFINITY, scaleByDistance: new Cesium.NearFarScalar(1e3, 1.0, 1e6, 0.5) }; entity.position = center; } }); // 保存石河子范围 const positions = cityDataSource.entities.values[0].polygon.hierarchy.getValue().positions; const cartographicPositions = positions.map(pos => Cesium.Cartographic.fromCartesian(pos) ); const minLon = Math.min(...cartographicPositions.map(p => p.longitude)); const maxLon = Math.max(...cartographicPositions.map(p => p.longitude)); const minLat = Math.min(...cartographicPositions.map(p => p.latitude)); const maxLat = Math.max(...cartographicPositions.map(p => p.latitude)); shiheziRectangle.value = new Cesium.Rectangle( minLon, minLat, maxLon, maxLat ); } catch (error) { console.error("加载石河子数据失败:", error); } }; // 定位到石河子(新增函数) const flyToShihezi = () => { if (viewer && shiheziRectangle.value) { viewer.camera.flyTo({ destination: shiheziRectangle.value, orientation: { heading: Cesium.Math.toRadians(0), pitch: Cesium.Math.toRadians(-70), roll: 0 }, duration: 1 }); } }; // 投影类型切换 const switchProjectionType = () => { if (viewer && networkType.value === 'external') { loadMapService(selectedMapType.value); // 重新加载石河子数据并定位 loadShiheziVector(viewer, projectionType.value); setTimeout(flyToShihezi, 500); } }; onMounted(async () => { try { // 设置Cesium Ion访问令牌 Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI0ZTdiZDBhZS0xNzBhLTRjZGUtOTY4NC1kYzA5ZDEyNGEyNjUiLCJpZCI6MzE2OTI5LCJpYXQiOjE3NTEzMzg2Mzh9.9QHGIwaWkUOX0NOSre5369rrf1k6bGhZu7xUQia4JmE'; // 初始化Cesium Viewer viewer = new Cesium.Viewer('cesiumContainer', { baseLayerPicker: false, timeline: false, animation: false, geocoder: false, sceneModePicker: false, navigationHelpButton: false, homeButton: false, selectionIndicator: false, infoBox: false, navigationInstructionsInitiallyVisible: false, scene3DOnly: true, terrainProvider: new Cesium.EllipsoidTerrainProvider(), imageryProvider: new Cesium.WebMapTileServiceImageryProvider({ url: https://t{s}.tianditu.gov.cn/img_w/wmts?tk=${TIANDITU_TOKEN}, layer: "img", style: "default", tileMatrixSetID: "w", subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'], maximumLevel: 18 }) }); // 强制设置中国视角 viewer.camera.setView({ destination: Cesium.Rectangle.fromDegrees(73.0, 3.0, 136.0, 59.0) }); // // 加载株洲数据 // loadZhuzhouVector(viewer); // 加载石河子数据(修改为调用新函数) await loadShiheziVector(viewer); flyToShihezi(); // 加载地图服务 loadMapService(selectedMapType.value); } catch (error) { console.error("初始化失败:", error); } }); </script> <style scoped> /* 基础样式 */ * { margin: 0; padding: 0; box-sizing: border-box; font-family: "Microsoft YaHei", sans-serif; } .monitor-container { width: 100vw; height: 100vh; background: radial-gradient(circle at center, #0c2a50 0%, #0a1a35 100%); overflow: hidden; position: fixed; top: 0; left: 0; display: flex; flex-direction: column; } /* 顶部导航栏样式 - 重新布局 */ .top-bar { height: 70px; background: linear-gradient(90deg, #0a1a35 0%, #1a3a6e 50%, #0a1a35 100%); display: flex; align-items: center; justify-content: space-between; padding: 0 15px; position: relative; z-index: 100; border-bottom: 1px solid rgba(0, 150, 255, 0.3); } /* 左侧区域 */ .left-section { display: flex; align-items: center; flex: 0 0 auto; } /* 中间区域 */ .center-section { flex: 1 1 auto; display: flex; justify-content: center; } /* 右侧区域 */ .right-section { display: flex; align-items: center; flex: 0 0 auto; gap: 10px; } /* 控件组 */ .controls-group { display: flex; gap: 10px; } /* 天气模块 */ .weather-module { display: flex; align-items: center; color: white; font-size: 14px; min-width: 120px; justify-content: center; } .weather-icon { color: #ffcc00; margin-right: 8px; font-size: 16px; } /* 梯形标题样式 */ .platform-title { position: relative; z-index: 10; max-width: 500px; /* 限制最大宽度 */ } .trapezoid-bg { position: relative; padding: 0 40px; height: 70px; display: flex; align-items: center; } .trapezoid-bg::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 60, 113, 0.6); clip-path: polygon(0% 0%, 100% 0%, 90% 100%, 10% 100%); z-index: -1; } .platform-title h1 { font-size: 24px; font-weight: bold; background: linear-gradient(to bottom, #a2e7eb, #00f2fe); -webkit-background-clip: text; background-clip: text; color: transparent; text-shadow: 0 0 8px rgba(0, 242, 254, 0.3); white-space: nowrap; text-overflow: ellipsis; overflow: hidden; max-width: 100%; } /* 时间模块 */ .time-module { color: white; font-size: 14px; min-width: 220px; text-align: center; white-space: nowrap; } /* 所有控件统一样式 */ .network-switcher select, .map-switcher select, .projection-switcher select { background: rgba(10, 26, 53, 0.8); color: #66ffff; border: 1px solid rgba(0, 150, 255, 0.5); border-radius: 4px; padding: 6px 12px; font-size: 14px; outline: none; cursor: pointer; box-shadow: 0 0 8px rgba(0, 150, 255, 0.3); transition: all 0.3s ease; min-width: 120px; } .network-switcher select:hover, .map-switcher select:hover, .projection-switcher select:hover { background: rgba(26, 58, 110, 0.8); border-color: #00f2fe; box-shadow: 0 0 12px rgba(0, 242, 254, 0.5); } .network-switcher select:focus, .map-switcher select:focus, .projection-switcher select:focus { border-color: #00f2fe; } /* Cesium容器 */ #cesiumContainer { width: 100%; height: calc(100vh - 70px); background-color: #000; position: relative; } /* 响应式调整 */ @media (max-width: 1600px) { .platform-title h1 { font-size: 20px; } .weather-module, .time-module { font-size: 13px; } .network-switcher select, .map-switcher select, .projection-switcher select { padding: 5px 10px; font-size: 13px; min-width: 110px; } } @media (max-width: 1400px) { .platform-title h1 { font-size: 18px; } .trapezoid-bg { padding: 0 30px; } .time-module { min-width: 180px; } .controls-group { gap: 8px; } } @media (max-width: 1200px) { .platform-title h1 { font-size: 16px; } .trapezoid-bg { padding: 0 20px; } .time-module { min-width: 160px; font-size: 12px; } .network-switcher select, .map-switcher select, .projection-switcher select { padding: 4px 8px; font-size: 12px; min-width: 100px; } } @media (max-width: 992px) { .time-module { display: none; } .platform-title h1 { font-size: 14px; } .trapezoid-bg { padding: 0 15px; } .weather-module { min-width: auto; } .network-switcher select, .map-switcher select, .projection-switcher select { padding: 4px 6px; font-size: 11px; min-width: 90px; } } @media (max-width: 768px) { .weather-module { display: none; } .platform-title h1 { font-size: 12px; } .trapezoid-bg { padding: 0 10px; } .controls-group { gap: 5px; } .network-switcher select, .map-switcher select, .projection-switcher select { min-width: 80px; } } </style>
检查代码,还是有问题,切换到经纬度投影时石河子位置还是不对,没有切换

<template>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import * as Cesium from 'cesium';
import 'cesium/Build/Cesium/Widgets/widgets.css';
// 株洲市范围定义
const ZHUZHOU_EXTENT = {
west: 112.5,
east: 114.5,
south: 26.0,
north: 28.0
};
// 地图容器引用
const cesiumContainer = ref<HTMLDivElement | null>(null);
const miniMapContainer = ref<HTMLElement | null>(null);
// 地图实例
let viewer: Cesium.Viewer | null = null;
let overviewViewer: Cesium.Viewer | null = null;
// 位置指示器样式
const indicatorStyle = ref({
left: '50%',
top: '50%',
display: 'block'
});
// 创建主地图
const initMainMap = () => {
if (!cesiumContainer.value) return;
try {
// 使用ArcGIS世界影像服务
const imageryProvider = new Cesium.ArcGisMapServerImageryProvider({
url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
});
// 初始化主地图
viewer = new Cesium.Viewer(cesiumContainer.value, {
baseLayerPicker: false,
animation: false,
timeline: false,
navigationHelpButton: false,
fullscreenButton: false,
geocoder: false,
homeButton: false,
sceneModePicker: false,
skyBox: false,
skyAtmosphere: false,
infoBox: false,
selectionIndicator: false,
terrainProvider: new Cesium.EllipsoidTerrainProvider(),
sceneMode: Cesium.SceneMode.SCENE3D,
});
// 隐藏版权信息
viewer.cesiumWidget.creditContainer.style.display = "none";
// 设置初始视角到株洲市
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(113.13, 27.83, 5000),
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-90),
roll: 0.0
}
});
console.log("主地图初始化完成");
} catch (error) {
console.error("主地图初始化失败:", error);
}
};
// 初始化鹰眼地图
const initMiniMap = () => {
if (!miniMapContainer.value) return;
try {
// 鹰眼地图使用相同的影像源
const imageryProvider = new Cesium.ArcGisMapServerImageryProvider({
url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
});
// 初始化鹰眼地图
overviewViewer = new Cesium.Viewer(miniMapContainer.value, {
sceneMode: Cesium.SceneMode.SCENE2D,
baseLayerPicker: false,
homeButton: false,
timeline: false,
navigationHelpButton: false,
animation: false,
scene3DOnly: true,
selectionIndicator: false,
infoBox: false,
mapProjection: new Cesium.WebMercatorProjection(),
skyBox: false,
skyAtmosphere: false
});
// 设置鹰眼地图视图为株洲范围
overviewViewer.camera.setView({
destination: Cesium.Rectangle.fromDegrees(
ZHUZHOU_EXTENT.west,
ZHUZHOU_EXTENT.south,
ZHUZHOU_EXTENT.east,
ZHUZHOU_EXTENT.north
)
});
// 隐藏UI元素
const toolbar = overviewViewer.container.getElementsByClassName(
"cesium-viewer-toolbar"
)[0];
if (toolbar) toolbar.style.display = "none";
overviewViewer.cesiumWidget.creditContainer.style.display = "none";
console.log("鹰眼地图初始化完成");
} catch (error) {
console.error("鹰眼地图初始化失败:", error);
}
};
// 更新位置指示器
const updateIndicatorPosition = () => {
if (!viewer) return;
const camera = viewer.camera;
const rect = camera.computeViewRectangle();
if (!rect) return;
const center = Cesium.Rectangle.center(rect);
const lon = Cesium.Math.toDegrees(center.longitude);
const lat = Cesium.Math.toDegrees(center.latitude);
// 确保在株洲范围内
const constrainedLon = Math.max(
ZHUZHOU_EXTENT.west,
Math.min(ZHUZHOU_EXTENT.east, lon)
);
const constrainedLat = Math.max(
ZHUZHOU_EXTENT.south,
Math.min(ZHUZHOU_EXTENT.north, lat)
);
// 计算位置百分比
const lonPercent =
((constrainedLon - ZHUZHOU_EXTENT.west) /
(ZHUZHOU_EXTENT.east - ZHUZHOU_EXTENT.west)) *
100;
const latPercent =
100 -
((constrainedLat - ZHUZHOU_EXTENT.south) /
(ZHUZHOU_EXTENT.north - ZHUZHOU_EXTENT.south)) *
100;
indicatorStyle.value = {
left: ${lonPercent}%,
top: ${latPercent}%,
display: "block"
};
};
// 监听主地图相机变化
const setupCameraListener = () => {
if (!viewer) return;
viewer.camera.changed.addEventListener(updateIndicatorPosition);
};
// 鹰眼地图点击处理
const handleMiniMapClick = (event: MouseEvent) => {
if (!miniMapContainer.value || !overviewViewer || !viewer) return;
const rect = miniMapContainer.value.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
const xPercent = (x / rect.width) * 100;
const yPercent = (y / rect.height) * 100;
// 计算点击位置对应的经纬度
const lon = ZHUZHOU_EXTENT.west +
(xPercent / 100) * (ZHUZHOU_EXTENT.east - ZHUZHOU_EXTENT.west);
const lat = ZHUZHOU_EXTENT.north -
(yPercent / 100) * (ZHUZHOU_EXTENT.north - ZHUZHOU_EXTENT.south);
// 飞向点击位置
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(lon, lat, 5000),
duration: 1
});
};
// 组件挂载时初始化
onMounted(() => {
initMainMap();
setTimeout(() => {
initMiniMap();
setupCameraListener();
updateIndicatorPosition();
}, 1000);
});
// 组件卸载时清理
onUnmounted(() => {
if (viewer && !viewer.isDestroyed()) {
viewer.destroy();
}
if (overviewViewer && !overviewViewer.isDestroyed()) {
overviewViewer.destroy();
}
});
</script>
<style scoped>
/* 主地图容器样式 */
#cesium-container {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
background: #000;
}
/* 鹰眼地图样式 */
.mini-map {
position: absolute;
right: 20px;
bottom: 20px;
width: 200px;
height: 150px;
border: 2px solid #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
z-index: 999;
cursor: pointer;
overflow: hidden;
}
/* 位置指示器样式 */
.location-indicator {
position: absolute;
width: 14px;
height: 14px;
background: #ff3e3e;
border: 2px solid white;
border-radius: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 0 15px rgba(255, 62, 62, 1);
z-index: 100;
}
/* 隐藏版权信息 */
:deep(.cesium-widget) {
background-color: #000 !important;
}
:deep(.cesium-widget-credits),
:deep(.cesium-credit-textContainer),
:deep(.cesium-credit-expand-link) {
display: none !important;
visibility: hidden !important;
}
</style>
好的现在初始功能有了,现在借鉴他的株洲市影像瓦片,再主地图与鹰眼地图都加载,基于此修改上述代码发我

runtime-core.esm-bundler.js:51 [Vue warn]: Unhandled error during execution of mounted hook
at <Map>
at <App>
warn$1 @ runtime-core.esm-bundler.js:51
Map.vue:33 Uncaught TypeError: Cesium.createWorldTerrain is not a function
at Map.vue:33:29
at runtime-core.esm-bundler.js:2836:40
at callWithErrorHandling (runtime-core.esm-bundler.js:199:19)
at callWithAsyncErrorHandling (runtime-core.esm-bundler.js:206:17)
at hook.__weh.hook.__weh (runtime-core.esm-bundler.js:2816:19)
at flushPostFlushCbs (runtime-core.esm-bundler.js:385:28)
at render2 (runtime-core.esm-bundler.js:6048:7)
at mount (runtime-core.esm-bundler.js:3962:13)
at app.mount (runtime-dom.esm-bundler.js:1775:19)
at main.ts:9:5 如上所示报错修改下面代码: <template>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import * as Cesium from 'cesium';
import 'cesium/Build/Cesium/Widgets/widgets.css';
// Cesium访问令牌 - 请替换成自己的有效令牌
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI0ZTdiZDBhZS0xNzBhLTRjZGUtOTY4NC1kYzA5ZDEyNGEyNjUiLCJpZCI6MzE2OTI5LCJpYXQiOjE3NTEzMzg2Mzh9.9QHGIwaWkUOX0NOSre5369rrf1k6bGhZu7xUQia4JmE';
const cesiumContainer = ref<HTMLDivElement | null>(null);
let viewer: Cesium.Viewer | null = null;
onMounted(() => {
if (!cesiumContainer.value) return;
// 初始化Cesium地球
viewer = new Cesium.Viewer(cesiumContainer.value, {
animation: false,
baseLayerPicker: false,
fullscreenButton: false,
vrButton: false,
geocoder: false,
homeButton: false,
infoBox: false,
sceneModePicker: false,
selectionIndicator: false,
timeline: false,
navigationHelpButton: false,
navigationInstructionsInitiallyVisible: false,
terrainProvider: Cesium.createWorldTerrain(),
imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
})
});
// 设置初始视图位置(株洲市)
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(113.13, 27.83, 10000000),
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-90),
roll: 0.0
},
duration: 2
});
});
onUnmounted(() => {
if (viewer) {
viewer.destroy();
viewer = null;
}
});
</script>
<style scoped>
#cesium-container {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
/* 覆盖Cesium默认的logo */
:deep(.cesium-viewer-bottom) {
display: none !important;
}
</style> 注意我的cesium是最新版的

<template>
坐标系:
<el-radio-group v-model="coordinateSystem" size="small">
<el-radio-button label="gcj02">高德/火星坐标</el-radio-button>
<el-radio-button label="bd09">百度坐标</el-radio-button>
</el-radio-group>
<Search @location-selected="handleLocationSelected" />
<LocationBar v-if="loaded" :update-interval="100" :use-dms-format="useDmsFormat" />
</template>
<style>
/* 原有样式完全不变 */
</style>
<script setup lang="ts">
// 原有导入 + 新增坐标转换工具类导入
import { computed, onUnmounted, onMounted, reactive, ref } from "vue";
import LocationBar from "./location-bar.vue";
import Search from "./search.vue";
import initMap from "./init";
import { throttle } from "lodash";
import { loadRipplePoints, createMultipleRippleCircles } from "./circle.js";
import { $prototype } from "../../main.ts";
import markerImage from "@/assets/images/building.png";
import { imageDark } from "naive-ui/es/image/styles";
// 新增:导入坐标转换工具类
import { CoordinateTransformer } from "./coordinate-transformer";
// 修复类型定义
type Rectangle = any;
// 原有状态 + 新增坐标系选择状态
const miniMapContainer = ref<HTMLElement>();
let viewIndicator: Rectangle;
const currentPosition = reactive({
longitude: 113.361538,
latitude: 27.339318,
});
const ZHUZHOU_EXTENT = {
west: 112.5,
east: 114.5,
south: 26.0,
north: 28.0,
};
const rippleEntities = ref<any[]>([]);
const heightThreshold = 80000;
const indicatorStyle = ref({
left: "50%",
top: "50%",
display: "block",
});
const loaded = ref(false);
const useDmsFormat = ref(false);
const overviewViewer = ref(null);
let currentMarker: any = null;
// 新增:坐标系选择状态(默认高德GCJ-02)
const coordinateSystem = ref("gcj02"); // 'gcj02' | 'bd09'
// 原有方法完全不变(波纹可见性更新)
const updateRippleVisibility = throttle(() => {
if (!$prototype.$map || rippleEntities.value.length === 0) return;
let shouldShow = false;
const cartographic = $prototype.$map.camera.positionCartographic;
if (cartographic) {
const cameraHeight = cartographic.height;
shouldShow = cameraHeight > heightThreshold;
}
rippleEntities.value.forEach((entity) => {
//entity.show = shouldShow;
entity.show = false;
});
}, 200);
// 原有方法完全不变(指示器位置更新)
const updateIndicatorPosition = () => {
if (!$prototype.$map) return;
const camera = $prototype.$map.camera;
const rect = camera.computeViewRectangle();
if (!rect) return;
const center = Cesium.Rectangle.center(rect);
const lon = Cesium.Math.toDegrees(center.longitude);
const lat = Cesium.Math.toDegrees(center.latitude);
const constrainedLon = Math.max(
ZHUZHOU_EXTENT.west,
Math.min(ZHUZHOU_EXTENT.east, lon)
);
const constrainedLat = Math.max(
ZHUZHOU_EXTENT.south,
Math.min(ZHUZHOU_EXTENT.north, lat)
);
const lonPercent =
((constrainedLon - ZHUZHOU_EXTENT.west) /
(ZHUZHOU_EXTENT.east - ZHUZHOU_EXTENT.west)) *
100;
const latPercent =
100 -
((constrainedLat - ZHUZHOU_EXTENT.south) /
(ZHUZHOU_EXTENT.north - ZHUZHOU_EXTENT.south)) *
100;
indicatorStyle.value = {
left: ${lonPercent}%,
top: ${latPercent}%,
display: "block",
};
};
// 原有方法完全不变(鹰眼地图更新)
const updateOverview = () => {
if (!$prototype.$map || !overviewViewer.value) return;
const rectangle = $prototype.$map.camera.computeViewRectangle();
if (!rectangle) return;
updateIndicatorPosition();
// 添加视口矩形更新逻辑
if (viewIndicator) {
viewIndicator.rectangle.coordinates = rectangle;
}
};
// 原有方法完全不变(初始化鹰眼地图)
const initMiniMap = () => {
Cesium.Ion.defaultAccessToken =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxMDhlNDdmYy03NzFhLTQ1ZTQtOWQ3NS1lZDAzNDc3YjE4NDYiLCJpZCI6MzAxNzQyLCJpYXQiOjE3NDcwNTMyMDN9.eaez8rQxVbPv2LKEU0sMDclPWyHKhh1tR27Vg-_rQSM";
if (!miniMapContainer.value) return;
const worldProvider = new Cesium.UrlTemplateImageryProvider({
url:
"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
minimumLevel: 0,
maximumLevel: 19,
tilingScheme: new Cesium.WebMercatorTilingScheme(),
});
const zhuzhouProvider = new Cesium.UrlTemplateImageryProvider({
url: "http://124.232.190.30:9000/proxy/pk1725866655224/map/zzzsyx_18/{z}/{x}/{y}.png",
minimumLevel: 1,
maximumLevel: 17,
tilingScheme: new Cesium.WebMercatorTilingScheme(),
});
overviewViewer.value = new Cesium.Viewer(miniMapContainer.value, {
sceneMode: Cesium.SceneMode.SCENE2D,
baseLayerPicker: false,
homeButton: false,
timeline: false,
navigationHelpButton: false,
animation: false,
scene3DOnly: true,
selectionIndicator: false,
infoBox: false,
imageryProvider: worldProvider,
terrainProvider: undefined,
mapProjection: new Cesium.WebMercatorProjection(),
skyBox: false,
skyAtmosphere: false,
});
// 禁用所有相机交互控制(新增功能代码)
const cameraController = overviewViewer.value.scene.screenSpaceCameraController;
cameraController.enableTranslate = false;
cameraController.enableZoom = false;
cameraController.enableRotate = false;
cameraController.enableTilt = false;
cameraController.enableLook = false;
const zhuzhouLayer = overviewViewer.value.imageryLayers.addImageryProvider(
zhuzhouProvider
);
overviewViewer.value.camera.setView({
destination: Cesium.Rectangle.fromDegrees(
ZHUZHOU_EXTENT.west,
ZHUZHOU_EXTENT.south,
ZHUZHOU_EXTENT.east,
ZHUZHOU_EXTENT.north
),
});
const toolbar = overviewViewer.value.container.getElementsByClassName(
"cesium-viewer-toolbar"
)[0];
if (toolbar) toolbar.style.display = "none";
overviewViewer.value.cesiumWidget.creditContainer.style.display = "none";
initRectangle();
};
// 原有方法完全不变(初始化视图指示器)
function initRectangle() {
viewIndicator = overviewViewer.value.entities.add({
rectangle: {
coordinates: Cesium.Rectangle.fromDegrees(
ZHUZHOU_EXTENT.west,
ZHUZHOU_EXTENT.south,
ZHUZHOU_EXTENT.east,
ZHUZHOU_EXTENT.north
),
material: Cesium.Color.RED.withAlpha(0.3),
outline: true,
outlineColor: Cesium.Color.RED,
outlineWidth: 2,
},
});
}
// 原有方法完全不变(添加演示图形)
function addDemoGraphics() {
const chinaBoundary = $prototype.$map.dataSources.add(
Cesium.GeoJsonDataSource.load("/shp_zz.geojson", {
stroke: Cesium.Color.WHITE,
fill: false,
clampToGround: true,
describe: null,
})
);
chinaBoundary.then((dataSource) => {
const entities = dataSource.entities.values;
for (let entity of entities) {
if (entity.polyline) {
entity.polyline.fill = false;
}
}
});
}
// 原有方法完全不变(飞行到默认位置)
function flyToDes() {
const center = Cesium.Cartesian3.fromDegrees(-98.0, 40.0);
$prototype.$map.camera.flyTo({
destination: new Cesium.Cartesian3(
-2432812.6687511606,
5559483.804371395,
2832009.419525571
),
orientation: {
heading: 6.283185307179421,
pitch: -1.0472145569408116,
roll: 6.2831853071795205,
},
complete: function () {},
});
}
// 原有方法完全不变(监听相机变化)
const setupCameraListener = () => {
$prototype.$map.camera.changed.addEventListener(updateOverview);
};
// 原有方法完全不变(鹰眼地图点击处理)
const handleMiniMapClick = (event: MouseEvent) => {
if (!miniMapContainer.value || !overviewViewer.value) return;
const rect = miniMapContainer.value.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
const xPercent = (x / rect.width) * 100;
const yPercent = (y / rect.height) * 100;
const lon =
ZHUZHOU_EXTENT.west + (xPercent / 100) * (ZHUZHOU_EXTENT.east - ZHUZHOU_EXTENT.west);
const lat =
ZHUZHOU_EXTENT.north -
(yPercent / 100) * (ZHUZHOU_EXTENT.north - ZHUZHOU_EXTENT.south);
$prototype.$map.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(lon, lat, 1000000),
});
};
// 原有生命周期方法完全不变
onMounted(() => {
initMap();
loaded.value = true;
addDemoGraphics();
flyToDes();
setTimeout(() => {
initMiniMap();
setupCameraListener();
updateOverview();
}, 1000);
(async () => {
try {
const ripplePoints = await loadRipplePoints();
rippleEntities.value = createMultipleRippleCircles($prototype.$map, ripplePoints);
$prototype.$map.camera.changed.addEventListener(updateRippleVisibility);
updateRippleVisibility();
} catch (error) {
console.error("加载波纹圆失败:", error);
}
})();
});
// 原有方法完全不变(创建标记)
const createMarker = (wgsLocation) => {
if (currentMarker) {
$prototype.$map.entities.remove(currentMarker);
}
console.log("标记图片加载状态:", markerImage);
console.log("创建标记位置:", wgsLocation);
currentMarker = $prototype.$map.entities.add({
position: Cesium.Cartesian3.fromDegrees(wgsLocation.lng, wgsLocation.lat, 10),
label: {
text: wgsLocation.name + "(标记位置)",
font: "18px sans-serif",
fillColor: Cesium.Color.YELLOW,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2,
verticalOrigin: Cesium.VerticalOrigin.TOP,
pixelOffset: new Cesium.Cartesian2(0, 20),
heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND,
show: true,
},
// 关键:用 billboard 替换 point
billboard: {
image: new URL("@/assets/images/building.png", import.meta.url).href, // 本地图标
width: 32, // 图标宽度(像素)
height: 32, // 图标高度(像素)
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 让图标尖点对地
heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND,
scale: 1.0,
},
});
console.log("标记实体已添加:", currentMarker);
$prototype.$map.scene.requestRender();
return currentMarker;
};
// 核心修改:使用工具类实现动态坐标系转换
const handleLocationSelected = (location: { lng: number; lat: number; name: string }) => {
if (!$prototype.$map) return;
// 根据选择的坐标系进行转换
let wgsLocation;
if (coordinateSystem.value === "gcj02") {
// 高德/火星坐标转WGS84
wgsLocation = CoordinateTransformer.gcj02ToWgs84(location.lng, location.lat);
} else {
// 百度坐标转WGS84
wgsLocation = CoordinateTransformer.bd09ToWgs84(location.lng, location.lat);
}
console.log(转换前(${coordinateSystem.value})坐标:, location.lng, location.lat);
console.log("转换后(WGS84)坐标:", wgsLocation.lng, wgsLocation.lat);
// 使用转换后的坐标执行原有逻辑
const destination = Cesium.Cartesian3.fromDegrees(
wgsLocation.lng,
wgsLocation.lat,
3000
);
$prototype.$map.camera.flyTo({
destination,
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-90),
roll: 0,
},
duration: 2,
complete: () => {
createMarker({ ...location, ...wgsLocation });
},
});
};
// 原有方法完全不变(组件销毁清理)
onUnmounted(() => {
if ($prototype.$map) {
$prototype.$map.destroy();
$prototype.$map = null;
}
if ($prototype.$map) {
$prototype.$map.camera.changed.removeEventListener(updateRippleVisibility);
}
console.log("组件销毁");
});
const emit = defineEmits(["onload", "onclick"]);
const initMars3d = async (option: any) => {
emit("onclick", true);
emit("onload", $prototype.$map);
};
</script>
<style lang="less">
/**cesium 工具按钮栏*/
.cesium-viewer-toolbar {
top: auto !important;
bottom: 35px !important;
left: 12px !important;
right: auto !important;
}
.cesium-toolbar-button img {
height: 100%;
}
.cesium-viewer-toolbar > .cesium-toolbar-button,
.cesium-navigationHelpButton-wrapper,
.cesium-viewer-geocoderContainer {
margin-bottom: 5px;
float: left;
clear: both;
text-align: center;
}
.cesium-button {
background-color: rgba(23, 49, 71, 0.8);
color: #e6e6e6;
fill: #e6e6e6;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
line-height: 32px;
}
.cesium-button:hover {
background: #3ea6ff;
}
/**cesium 底图切换面板*/
.cesium-baseLayerPicker-dropDown {
bottom: 0;
left: 40px;
max-height: 700px;
margin-bottom: 5px;
background-color: rgba(23, 49, 71, 0.8);
}
/**cesium 帮助面板*/
.cesium-navigation-help {
top: auto;
bottom: 0;
left: 40px;
transform-origin: left bottom;
background: none;
background-color: rgba(23, 49, 71, 0.8);
.cesium-navigation-help-instructions {
background: none;
}
.cesium-navigation-button {
background: none;
}
.cesium-navigation-button-selected,
.cesium-navigation-button-unselected:hover {
background: rgba(0, 138, 255, 0.2);
}
}
/**cesium 二维三维切换*/
.cesium-sceneModePicker-wrapper {
width: auto;
}
.cesium-sceneModePicker-wrapper .cesium-sceneModePicker-dropDown-icon {
float: right;
margin: 0 3px;
}
/**cesium POI查询输入框*/
.cesium-viewer-geocoderContainer .search-results {
left: 0;
right: 40px;
width: auto;
z-index: 9999;
}
.cesium-geocoder-searchButton {
background-color: rgba(23, 49, 71, 0.8);
}
.cesium-viewer-geocoderContainer .cesium-geocoder-input {
background-color: rgba(63, 72, 84, 0.7);
}
.cesium-viewer-geocoderContainer .cesium-geocoder-input:focus {
background-color: rgba(63, 72, 84, 0.9);
}
.cesium-viewer-geocoderContainer .search-results {
background-color: rgba(23, 49, 71, 0.8);
}
/**cesium info信息框*/
.cesium-infoBox {
top: 50px;
background-color: rgba(23, 49, 71, 0.8);
}
.cesium-infoBox-title {
background-color: rgba(23, 49, 71, 0.8);
}
/**cesium 任务栏的FPS信息*/
.cesium-performanceDisplay-defaultContainer {
top: auto;
bottom: 35px;
right: 50px;
}
.cesium-performanceDisplay-ms,
.cesium-performanceDisplay-fps {
color: #fff;
}
/**cesium tileset调试信息面板*/
.cesium-viewer-cesiumInspectorContainer {
top: 10px;
left: 10px;
right: auto;
}
.cesium-cesiumInspector {
background-color: rgba(23, 49, 71, 0.8);
}
/**覆盖mars3d内部控件的颜色等样式*/
.mars3d-compass .mars3d-compass-outer {
fill: rgba(23, 49, 71, 0.8);
}
.mars3d-contextmenu-ul,
.mars3d-sub-menu {
background-color: rgba(23, 49, 71, 0.8);
> li > a:hover,
> li > a:focus,
> li > .active {
background-color: #3ea6ff;
}
> .active > a,
> .active > a:hover,
> .active > a:focus {
background-color: #3ea6ff;
}
}
/* Popup样式*/
.mars3d-popup-color {
color: #ffffff;
}
.mars3d-popup-background {
background: rgba(23, 49, 71, 0.8);
}
.mars3d-popup-content {
margin: 15px;
}
.mars3d-template-content label {
padding-right: 6px;
}
.mars3d-template-titile {
border-bottom: 1px solid #3ea6ff;
}
.mars3d-template-titile a {
font-size: 16px;
}
.mars3d-tooltip {
background: rgba(23, 49, 71, 0.8);
border: 1px solid rgba(23, 49, 71, 0.8);
}
.mars3d-popup-btn-custom {
padding: 3px 10px;
border: 1px solid #209ffd;
background: #209ffd1c;
}
.mars-dialog .mars-dialog__content {
height: 100%;
width: 100%;
overflow: auto;
padding: 5px;
}
.image {
border: solid 2px #fff;
}
.content {
height: 90%;
padding-top: 10px;
overflow-x: auto;
overflow-y: auto;
}
.content-text {
padding: 0 10px;
text-indent: 30px;
font-size: 17px;
}
.details-video {
width: 100%;
height: 760px;
background-color: #000;
}
:where(.css-lt97qq9).ant-space {
display: inline-flex;
}
:where(.css-lt97qq9).ant-space-align-center {
align-items: center;
}
:where(.css-lt97qq9).ant-image .ant-image-mask {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
background: rgba(0, 0, 0, 0.5);
cursor: pointer;
opacity: 0;
transition: opacity 0.3s;
}
:where(.css-lt97qq9).ant-image .ant-image-mask .ant-image-mask-info {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding: 0 4px;
}
:where(.css-1t97qq9)[class^="ant-image"] [class^="ant-image"],
:where(.css-1t97qq9)[class*=" ant-image"] [class^="ant-image"],
:where(.css-1t97qq9)[class^="ant-image"] [class*=" ant-image"],
:where(.css-1t97qq9)[class*=" ant-image"] [class*=" ant-image"] {
box-sizing: border-box;
}
:where(.css-lt97qq9).ant-image .ant-image-img {
width: 100%;
height: auto;
vertical-align: middle;
}
</style>
<style scoped>
.mini-map-container {
position: relative;
width: 100%;
height: 100%;
}
.main-viewer {
width: 100%;
height: 100%;
}
.mini-map {
position: absolute;
right: 3vw;
bottom: 6vh;
width: 12vw;
height: 17vh;
border: 2px solid #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
z-index: 999;
cursor: pointer;
overflow: hidden;
}
.location-indicator {
position: absolute;
width: 19px;
height: 19px;
transform: translate(-50%, -50%);
z-index: 100;
/* border: 2px solid red;
border-radius: 2px; */
}
/* 原十字准星伪元素保持不变 */
.location-indicator::before,
.location-indicator::after {
content: "";
position: absolute;
background-color: red;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.location-indicator::before {
width: 14px;
height: 2px;
}
.location-indicator::after {
width: 2px;
height: 14px;
}
.view-rectangle {
position: absolute;
border: 2px solid #00f2fe;
z-index: 99;
pointer-events: none;
box-shadow: 0 0 20px rgba(0, 242, 254, 0.7);
}
/* 相机聚焦样式 - 四个角折角 */
.corner {
position: absolute;
width: 25px;
height: 25px;
z-index: 100;
pointer-events: none;
}
.corner::before,
.corner::after {
content: "";
position: absolute;
background: #00f2fe;
box-shadow: 0 0 10px rgba(0, 242, 254, 0.8);
}
.corner-top-left {
top: -2px;
left: -2px;
}
.corner-top-left::before {
top: 0;
left: 0;
width: 15px;
height: 3px;
}
.corner-top-left::after {
top: 0;
left: 0;
width: 3px;
height: 15px;
}
.corner-top-right {
top: -2px;
right: -2px;
}
.corner-top-right::before {
top: 0;
right: 0;
width: 15px;
height: 3px;
}
.corner-top-right::after {
top: 0;
right: 0;
width: 3px;
height: 15px;
}
.corner-bottom-left {
bottom: -2px;
left: -2px;
}
.corner-bottom-left::before {
bottom: 0;
left: 0;
width: 15px;
height: 3px;
}
.corner-bottom-left::after {
bottom: 0;
left: 0;
width: 3px;
height: 15px;
}
.corner-bottom-right {
bottom: -2px;
right: -2px;
}
.corner-bottom-right::before {
bottom: 0;
right: 0;
width: 15px;
height: 3px;
}
.corner-bottom-right::after {
bottom: 0;
right: 0;
width: 3px;
height: 15px;
}
.camera-icon {
position: absolute;
top: 10px;
right: 10px;
color: #00f2fe;
font-size: 24px;
z-index: 100;
text-shadow: 0 0 10px rgba(0, 242, 254, 0.8);
}
.focus-effect {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 98;
border: 2px solid rgba(0, 242, 254, 0.2);
border-radius: 5px;
box-shadow: inset 0 0 30px rgba(0, 242, 254, 0.1);
}
.pulse {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 10px;
height: 10px;
border-radius: 50%;
background: rgba(0, 242, 254, 0.7);
box-shadow: 0 0 0 0 rgba(0, 242, 254, 0.7);
animation: pulse 2s infinite;
}
::v-deep.cesium-viewer-toolbar {
display: none;
}
</style>
检查live-map代码,其中为什么这个红色十字定位位置会不对,会呆在左上方,虽然移动后会变正确,但是刷新页面显示时就是有时候会不对,会影响观感,你阅读live-map.vue代码中鹰眼地图部分代码,尝试寻找原因修改,然后尽可能不更改其他任何无关代码,减少对代码的修改

<template>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue';
import * as Cesium from 'cesium';
import 'cesium/Build/Cesium/Widgets/widgets.css';
// 株洲市范围常量
const ZHUZHOU_EXTENT = Cesium.Rectangle.fromDegrees(
112.5, // west
26.0, // south
114.5, // east
28.0 // north
);
// 地图实例引用
const mainViewer = ref<Cesium.Viewer | null>(null);
const overviewViewer = ref<Cesium.Viewer | null>(null);
const rectangleEntity = ref<Cesium.Entity | null>(null); // 改用 Entity 替代 RectanglePrimitive
// 初始化主地图
const initMainMap = async () => {
try {
// 设置Cesium访问令牌 - 替换为您的实际令牌
Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI0ZTdiZDBhZS0xNzBhLTRjZGUtOTY4NC1kYzA5ZDEyNGEyNjUiLCJpZCI6MzE2OTI5LCJpYXQiOjE3NTEzMzg2Mzh9.9QHGIwaWkUOX0NOSre5369rrf1k6bGhZu7xUQia4JmE";
// 创建主地图实例
mainViewer.value = new Cesium.Viewer('cesiumContainer', {
sceneMode: Cesium.SceneMode.SCENE3D,
animation: false,
baseLayerPicker: false,
fullscreenButton: true,
geocoder: false,
homeButton: true,
infoBox: false,
sceneModePicker: true,
selectionIndicator: true,
timeline: false,
navigationHelpButton: false,
shouldAnimate: true,
// 修复地形创建方式 - 使用正确的API
terrainProvider: Cesium.createWorldTerrain(),
imageryProvider: new Cesium.UrlTemplateImageryProvider({
url: "http://124.232190.30:9000/proxy/pk1725866655224/map/zzzsyx_18/{z}/{x}/{y}.png",
minimumLevel: 1,
maximumLevel: 17
})
});
// 设置初始视图到株洲市范围
mainViewer.value.camera.flyTo({
destination: ZHUZHOU_EXTENT,
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-30),
roll: 0.0
},
duration: 2.0
});
// 添加错误处理
mainViewer.value.scene.globe.tileLoadProgressEvent.addEventListener((pending) => {
if (pending === 0) {
console.log('所有瓦片加载完成');
}
});
mainViewer.value.scene.error.addEventListener((error) => {
console.error('Cesium错误:', error);
});
} catch (error) {
console.error('初始化主地图失败:', error);
}
};
// 初始化鹰眼地图
const initOverviewMap = () => {
try {
// 创建鹰眼地图实例
overviewViewer.value = new Cesium.Viewer('overviewMap', {
sceneMode: Cesium.SceneMode.SCENE2D,
baseLayerPicker: false,
fullscreenButton: false,
geocoder: false,
homeButton: false,
infoBox: false,
sceneModePicker: false,
selectionIndicator: false,
timeline: false,
navigationHelpButton: false,
animation: false,
imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer'
})
});
// 隐藏不需要的控件
overviewViewer.value.cesiumWidget.creditContainer.style.display = "none";
// 设置鹰眼地图范围 - 使用 fromDegrees 简化
overviewViewer.value.camera.setView({
destination: Cesium.Rectangle.fromDegrees(110.0, 24.0, 116.0, 30.0)
});
// 添加株洲市范围指示器 - 使用 Entity 替代 RectanglePrimitive
rectangleEntity.value = overviewViewer.value.entities.add({
name: '株洲市范围',
rectangle: {
coordinates: ZHUZHOU_EXTENT,
material: Cesium.Color.RED.withAlpha(0.3),
outline: true,
outlineColor: Cesium.Color.RED,
outlineWidth: 2
}
});
} catch (error) {
console.error('初始化鹰眼地图失败:', error);
}
};
// 同步主地图和鹰眼地图
const syncMaps = () => {
try {
if (!mainViewer.value || !overviewViewer.value || !rectangleEntity.value) return;
// 监听主地图相机变化
mainViewer.value.camera.changed.addEventListener(() => {
const camera = mainViewer.value!.camera;
// 计算当前视图范围
const rectangle = camera.computeViewRectangle();
if (rectangle) {
// 更新鹰眼地图中的矩形
rectangleEntity.value!.rectangle!.coordinates = rectangle;
// 更新鹰眼地图视角(如果矩形超出当前视图)
const overviewCamera = overviewViewer.value!.camera;
const overviewRectangle = overviewCamera.computeViewRectangle();
if (overviewRectangle && !Cesium.Rectangle.contains(overviewRectangle, rectangle)) {
// 扩展矩形以包含当前视图
const expandedRect = new Cesium.Rectangle(
Math.min(overviewRectangle.west, rectangle.west),
Math.min(overviewRectangle.south, rectangle.south),
Math.max(overviewRectangle.east, rectangle.east),
Math.max(overviewRectangle.north, rectangle.north)
);
overviewCamera.flyTo({
destination: expandedRect,
duration: 0.5
});
}
}
});
// 鹰眼地图点击事件
overviewViewer.value.screenSpaceEventHandler.setInputAction((click: any) => {
const position = click.position;
const ray = overviewViewer.value!.camera.getPickRay(position);
const cartesian = overviewViewer.value!.scene.globe.pick(ray, overviewViewer.value!.scene);
if (cartesian) {
// 主地图跳转到点击位置
mainViewer.value!.camera.flyTo({
destination: cartesian,
orientation: {
heading: mainViewer.value!.camera.heading,
pitch: mainViewer.value!.camera.pitch,
roll: 0.0
},
duration: 1.0
});
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
} catch (error) {
console.error('地图同步失败:', error);
}
};
// 组件挂载时初始化地图
onMounted(() => {
initMainMap();
initOverviewMap();
// 等待地图初始化完成后再同步
setTimeout(syncMaps, 1000);
});
// 组件卸载时销毁地图实例
onUnmounted(() => {
try {
if (mainViewer.value) {
mainViewer.value.destroy();
mainViewer.value = null;
}
if (overviewViewer.value) {
overviewViewer.value.destroy();
overviewViewer.value = null;
}
} catch (error) {
console.error('销毁地图实例失败:', error);
}
});
</script>
<style>
/* 全局重置样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body, #app {
width: 100%;
height: 100%;
overflow: hidden; /* 防止滚动条 */
font-family: sans-serif;
}
</style>
<style scoped>
.map-container {
/* 移除 position: relative 改为绝对定位 */
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #1e1e1e; /* 深色背景防止白色间隙 */
}
.main-map {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 2;
}
.overview-map {
position: absolute;
bottom: 20px;
right: 20px;
width: 300px;
height: 200px;
border: 2px solid #fff;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.7);
z-index: 100;
background-color: #000;
border-radius: 4px;
overflow: hidden; /* 防止内部元素溢出 */
}
/* 响应式调整 */
@media (max-width: 768px) {
.overview-map {
width: 150px;
height: 100px;
bottom: 10px;
right: 10px;
}
}
</style>那基于我刚刚发现的错误和解释,你修改一下代码发我

An error occurred while rendering. Rendering has stopped.
ReferenceError: shouldShow is not defined
ReferenceError: shouldShow is not defined
at http://localhost:8001/src/components/common-map/live-map.vue?t=1751774207331:38:23
at Proxy.forEach (<anonymous>)
at http://localhost:8001/src/components/common-map/live-map.vue?t=1751774207331:37:28
at invokeFunc (http://localhost:8001/node_modules/.vite/deps/lodash.js?v=6636ce6c:3839:28)
at leadingEdge (http://localhost:8001/node_modules/.vite/deps/lodash.js?v=6636ce6c:3845:30)
at debounced (http://localhost:8001/node_modules/.vite/deps/lodash.js?v=6636ce6c:3887:24)
at tD.raiseEvent (http://localhost:8001/libs/Cesium/Cesium.js:96:1282)
at Jt._updateCameraChanged (http://localhost:8001/libs/Cesium/Cesium.js:11295:124016)
at Xi.initializeFrame (http://localhost:8001/libs/Cesium/Cesium.js:12477:35438)
at uC.render (http://localhost:8001/libs/Cesium/Cesium.js:12479:1576)
如图所示报错为什么 修改以下代码:<template>
<Search @location-selected="handleLocationSelected" />
<LocationBar
v-if="loaded"
:update-interval="100"
:use-dms-format="useDmsFormat"
/>
</template>
<style>
/* @import "/temp/css/divGraphic.css"; */
</style>
<script setup lang="ts">
import { computed, onUnmounted, onMounted, reactive } from "vue";
import LocationBar from "./location-bar.vue";
import Search from "./search.vue";
import initMap from "./init";
import { ref } from "vue";
import { throttle } from 'lodash';
import { loadRipplePoints, createMultipleRippleCircles } from './circle.js';
import { $prototype } from "../../main.ts";
const miniMapContainer = ref<HTMLElement>();
let viewIndicator: Rectangle;
// 视图指示器样式
const currentPosition = reactive({
longitude: 113.361538,
latitude: 27.339318,
});
// 株洲市范围常量
const ZHUZHOU_EXTENT = {
west: 112.5, // 株洲市西边界
east: 114.5, // 株洲市东边界
south: 26.0, // 株洲市南边界
north: 28.0 // 株洲市北边界
};
const rippleEntities = ref<any[]>([]); // 存储波纹圆实体
const heightThreshold = 80000; // 高度阈值(米),高于此值隐藏波纹圆
// 高度节流函数
const updateRippleVisibility = throttle(() => {
if (!$prototype.$map || rippleEntities.value.length === 0) return;
rippleEntities.value.forEach(entity => {
entity.show = shouldShow;
});
}, 200); // 每200毫秒最多执行一次
// 更新指示器位置
const updateIndicatorPosition = () => {
if (!$prototype.$map) return;
const camera = $prototype.$map.camera;
const rect = camera.computeViewRectangle();
if (!rect) return;
// 计算在株洲市范围内的相对位置
const center = Cesium.Rectangle.center(rect);
const lon = Cesium.Math.toDegrees(center.longitude);
const lat = Cesium.Math.toDegrees(center.latitude);
const lonPercent = ((lon - ZHUZHOU_EXTENT.west) /
(ZHUZHOU_EXTENT.east - ZHUZHOU_EXTENT.west)) * 100;
const latPercent = 100 - ((lat - ZHUZHOU_EXTENT.south) /
(ZHUZHOU_EXTENT.north - ZHUZHOU_EXTENT.south)) * 100;
// 更新CSS指示器
indicatorStyle.value = {
left: ${lonPercent}%,
top: ${latPercent}%,
display: "block"
};
};
// 更新鹰眼地图 - 只更新指示器位置
const updateOverview = () => {
if (!$prototype.$map || !overviewViewer.value) return;
// 获取主地图的当前视图中心
const rectangle = $prototype.$map.camera.computeViewRectangle();
if (!rectangle) return;
const center = Cesium.Rectangle.center(rectangle);
currentPosition.longitude = Cesium.Math.toDegrees(center.longitude);
currentPosition.latitude = Cesium.Math.toDegrees(center.latitude);
// 更新指示器位置
updateIndicatorPosition();
};
const overviewViewer = ref(null);
// 初始化鹰眼地图 - 使用株洲市影像
const initMiniMap = () => {
Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxMDhlNDdmYy03NzFhLTQ1ZTQtOWQ3NS1lZDAzNDc3YjE4NDYiLCJpZCI6MzAxNzQyLCJpYXQiOjE3NDcwNTMyMDN9.eaez8rQxVbPv2LKEU0sMDclPWyHKhh1tR27Vg-_rQSM";
if (!miniMapContainer.value) return;
// 创建株洲市专用影像提供器
const zhuzhouProvider = new Cesium.UrlTemplateImageryProvider({
url: "http://124.232.190.30:9000/proxy/pk1725866655224/map/zzzsyx_18/{z}/{x}/{y}.png",
minimumLevel: 1,
maximumLevel: 17,
crs: "EPSG:3857",
});
// 鹰眼地图初始化 - 使用株洲市影像
overviewViewer.value = new Cesium.Viewer(miniMapContainer.value, {
sceneMode: Cesium.SceneMode.SCENE2D,
baseLayerPicker: false,
homeButton: false,
timeline: false,
navigationHelpButton: false,
animation: false,
scene3DOnly: true,
selectionIndicator: false,
infoBox: false,
imageryProvider: zhuzhouProvider, // 使用株洲市影像
terrainProvider: window.Cesium.createWorldTerrain(),
});
// 设置固定视野为株洲市范围
overviewViewer.value.camera.setView({
destination: Cesium.Rectangle.fromDegrees(
ZHUZHOU_EXTENT.west,
ZHUZHOU_EXTENT.south,
ZHUZHOU_EXTENT.east,
ZHUZHOU_EXTENT.north
)
});
// 隐藏控件
var toolbar = overviewViewer.value.container.getElementsByClassName("cesium-viewer-toolbar")[0];
if (toolbar) toolbar.style.display = "none";
overviewViewer.value.cesiumWidget.creditContainer.style.display = "none";
};
// 初始化视图指示器
function initRectangle() {
// 创建视图指示器(株洲市范围框)
viewIndicator = overviewViewer.value.entities.add({
rectangle: {
coordinates: new Cesium.CallbackProperty(() => {
return Cesium.Rectangle.fromDegrees(
ZHUZHOU_EXTENT.west,
ZHUZHOU_EXTENT.south,
ZHUZHOU_EXTENT.east,
ZHUZHOU_EXTENT.north
);
}, false),
material: Cesium.Color.RED.withAlpha(0.3),
outline: true,
outlineColor: Cesium.Color.RED,
outlineWidth: 2,
},
});
}
// 指示器样式计算
const indicatorStyle = computed(() => {
return {}; // 由updateIndicatorPosition直接设置
});
const loaded = ref(false);
const useDmsFormat = ref(false);
function addDemoGraphics() {
const chinaBoundary = $prototype.$map.dataSources.add(
Cesium.GeoJsonDataSource.load("/shp_zz.geojson", {
stroke: Cesium.Color.WHITE,
fill: false,
clampToGround: true,
describe: null,
})
);
chinaBoundary.then((dataSource) => {
const entities = dataSource.entities.values;
for (let entity of entities) {
if (entity.polyline) {
entity.polyline.fill = false;
}
}
});
}
function flyToDes() {
const center = Cesium.Cartesian3.fromDegrees(-98.0, 40.0);
$prototype.$map.camera.flyTo({
destination: new Cesium.Cartesian3(
-2432812.6687511606,
5559483.804371395,
2832009.419525571
),
orientation: {
heading: 6.283185307179421,
pitch: -1.0472145569408116,
roll: 6.2831853071795205,
},
complete: function () {},
});
}
// 监听主地图相机变化
const setupCameraListener = () => {
$prototype.$map.camera.changed.addEventListener(updateOverview);
};
// 鹰眼地图点击处理
const handleMiniMapClick = (event: MouseEvent) => {
if (!miniMapContainer.value || !overviewViewer.value) return;
const rect = miniMapContainer.value.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
// 计算在固定范围内的相对位置
const xPercent = (x / rect.width) * 100;
const yPercent = (y / rect.height) * 100;
// 转换为株洲市范围内的经纬度
const lon = ZHUZHOU_EXTENT.west +
(xPercent / 100) * (ZHUZHOU_EXTENT.east - ZHUZHOU_EXTENT.west);
const lat = ZHUZHOU_EXTENT.north -
(yPercent / 100) * (ZHUZHOU_EXTENT.north - ZHUZHOU_EXTENT.south);
// 主地图飞向点击位置
$prototype.$map.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(lon, lat, 1000000),
});
};
function addImage() {
var rightImageProvider = new Cesium.UrlTemplateImageryProvider({
name: "影像图",
type: "xyz",
layer: "vec_d",
url: "http://124.232.190.30:9000/proxy/pk1725866655224/map/zzzsyx_18/{z}/{x}/{y}.png",
minimumLevel: 1,
maximumLevel: 17,
crs: "EPSG:3857",
});
$prototype.$map.imageryLayers.addImageryProvider(rightImageProvider);
rightImageProvider.splitDirection = Cesium.SplitDirection.right;
}
onMounted(() => {
initMap();
addImage();
loaded.value = true;
addDemoGraphics();
flyToDes();
initMiniMap();
setupCameraListener();
setTimeout(function () {
initRectangle();
}, 2000);
(async () => {
try {
const ripplePoints = await loadRipplePoints();
rippleEntities.value = createMultipleRippleCircles(
$prototype.$map,
ripplePoints
);
$prototype.$map.camera.changed.addEventListener(updateRippleVisibility);
updateRippleVisibility();
} catch (error) {
console.error('加载波纹圆失败:', error);
}
})();
});
let currentMarker: any = null;
const createMarker = (location: { lng: number; lat: number; name: string }) => {
if (currentMarker) {
$prototype.$map.entities.remove(currentMarker);
}
currentMarker = $prototype.$map.entities.add({
position: Cesium.Cartesian3.fromDegrees(
location.lng,
location.lat,
50
),
point: {
pixelSize: 200,
color: Cesium.Color.RED,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2,
heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND,
},
label: {
text: location.name,
font: "40px sans-serif",
fillColor: Cesium.Color.WHITE,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 1,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0, -20),
heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND,
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 10000),
},
description:
${location.name}
经度:${location.lng.toFixed(6)}
纬度:${location.lat.toFixed(6)}
,
});
return currentMarker;
};
const handleLocationSelected = (location: { lng: number; lat: number; name: string; }) => {
if (!$prototype.$map) return;
const destination = Cesium.Cartesian3.fromDegrees(
location.lng,
location.lat,
200
);
$prototype.$map.camera.flyTo({
destination,
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-45),
roll: 0,
},
duration: 2,
complete: () => {
createMarker(location);
},
});
};
onUnmounted(() => {
if ($prototype.$map) {
$prototype.$map.destroy();
$prototype.$map = null;
}
if ($prototype.$map) {
$prototype.$map.camera.changed.removeEventListener(updateRippleVisibility);
}
console.log("组件销毁");
});
const emit = defineEmits(["onload", "onclick"]);
const initMars3d = async (option: any) => {
emit("onclick", true);
emit("onload", $prototype.$map);
};
</script>
<style lang="less">
/**cesium 工具按钮栏*/
.cesium-viewer-toolbar {
top: auto !important;
bottom: 35px !important;
left: 12px !important;
right: auto !important;
}
.cesium-toolbar-button img {
height: 100%;
}
.cesium-viewer-toolbar > .cesium-toolbar-button,
.cesium-navigationHelpButton-wrapper,
.cesium-viewer-geocoderContainer {
margin-bottom: 5px;
float: left;
clear: both;
text-align: center;
}
.cesium-button {
background-color: rgba(23, 49, 71, 0.8);
color: #e6e6e6;
fill: #e6e6e6;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
line-height: 32px;
}
.cesium-button:hover {
background: #3ea6ff;
}
/**cesium 底图切换面板*/
.cesium-baseLayerPicker-dropDown {
bottom: 0;
left: 40px;
max-height: 700px;
margin-bottom: 5px;
background-color: rgba(23, 49, 71, 0.8);
}
/**cesium 帮助面板*/
.cesium-navigation-help {
top: auto;
bottom: 0;
left: 40px;
transform-origin: left bottom;
background: none;
background-color: rgba(23, 49, 71, 0.8);
.cesium-navigation-help-instructions {
background: none;
}
.cesium-navigation-button {
background: none;
}
.cesium-navigation-button-selected,
.cesium-navigation-button-unselected:hover {
background: rgba(0, 138, 255, 0.2);
}
}
/**cesium 二维三维切换*/
.cesium-sceneModePicker-wrapper {
width: auto;
}
.cesium-sceneModePicker-wrapper .cesium-sceneModePicker-dropDown-icon {
float: right;
margin: 0 3px;
}
/**cesium POI查询输入框*/
.cesium-viewer-geocoderContainer .search-results {
left: 0;
right: 40px;
width: auto;
z-index: 9999;
}
.cesium-geocoder-searchButton {
background-color: rgba(23, 49, 71, 0.8);
}
.cesium-viewer-geocoderContainer .cesium-geocoder-input {
background-color: rgba(63, 72, 84, 0.7);
}
.cesium-viewer-geocoderContainer .cesium-geocoder-input:focus {
background-color: rgba(63, 72, 84, 0.9);
}
.cesium-viewer-geocoderContainer .search-results {
background-color: rgba(23, 49, 71, 0.8);
}
/**cesium info信息框*/
.cesium-infoBox {
top: 50px;
background-color: rgba(23, 49, 71, 0.8);
}
.cesium-infoBox-title {
background-color: rgba(23, 49, 71, 0.8);
}
/**cesium 任务栏的FPS信息*/
.cesium-performanceDisplay-defaultContainer {
top: auto;
bottom: 35px;
right: 50px;
}
.cesium-performanceDisplay-ms,
.cesium-performanceDisplay-fps {
color: #fff;
}
/**cesium tileset调试信息面板*/
.cesium-viewer-cesiumInspectorContainer {
top: 10px;
left: 10px;
right: auto;
}
.cesium-cesiumInspector {
background-color: rgba(23, 49, 71, 0.8);
}
/**覆盖mars3d内部控件的颜色等样式*/
.mars3d-compass .mars3d-compass-outer {
fill: rgba(23, 49, 71, 0.8);
}
.mars3d-contextmenu-ul,
.mars3d-sub-menu {
background-color: rgba(23, 49, 71, 0.8);
> li > a:hover,
> li > a:focus,
> li > .active {
background-color: #3ea6ff;
}
> .active > a,
> .active > a:hover,
> .active > a:focus {
background-color: #3ea6ff;
}
}
/* Popup样式*/
.mars3d-popup-color {
color: #ffffff;
}
.mars3d-popup-background {
background: rgba(23, 49, 71, 0.8);
}
.mars3d-popup-content {
margin: 15px;
}
.mars3d-template-content label {
padding-right: 6px;
}
.mars3d-template-titile {
border-bottom: 1px solid #3ea6ff;
}
.mars3d-template-titile a {
font-size: 16px;
}
.mars3d-tooltip {
background: rgba(23, 49, 71, 0.8);
border: 1px solid rgba(23, 49, 71, 0.8);
}
.mars3d-popup-btn-custom {
padding: 3px 10px;
border: 1px solid #209ffd;
background: #209ffd1c;
}
.mars-dialog .mars-dialog__content {
height: 100%;
width: 100%;
overflow: auto;
padding: 5px;
}
.image {
border: solid 2px #fff;
}
.content {
height: 90%;
padding-top: 10px;
overflow-x: auto;
overflow-y: auto;
}
.content-text {
padding: 0 10px;
text-indent: 30px;
font-size: 17px;
}
.details-video {
width: 100%;
height: 760px;
background-color: #000;
}
:where(.css-lt97qq9).ant-space {
display: inline-flex;
}
:where(.css-lt97qq9).ant-space-align-center {
align-items: center;
}
:where(.css-lt97qq9).ant-image .ant-image-mask {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
background: rgba(0, 0, 0, 0.5);
cursor: pointer;
opacity: 0;
transition: opacity 0.3s;
}
:where(.css-lt97qq9).ant-image .ant-image-mask .ant-image-mask-info {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding: 0 4px;
}
:where(.css-1t97qq9)[class^="ant-image"] [class^="ant-image"],
:where(.css-1t97qq9)[class*=" ant-image"] [class^="ant-image"],
:where(.css-1t97qq9)[class^="ant-image"] [class*=" ant-image"],
:where(.css-1t97qq9)[class*=" ant-image"] [class*=" ant-image"] {
box-sizing: border-box;
}
:where(.css-lt97qq9).ant-image .ant-image-img {
width: 100%;
height: auto;
vertical-align: middle;
}
</style>
<style scoped>
.mini-map-container {
position: relative;
width: 100%;
height: 100%;
}
.main-viewer {
width: 100%;
height: 100%;
}
.mini-map {
position: absolute;
right: 3vw;
bottom: 6vh;
width: 12vw;
height: 17vh;
border: 2px solid #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
z-index: 999;
cursor: pointer;
overflow: hidden;
}
.location-indicator {
position: absolute;
width: 14px;
height: 14px;
background: #ff3e3e;
border: 2px solid white;
border-radius: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 0 15px rgba(255, 62, 62, 1);
z-index: 100;
}
.view-rectangle {
position: absolute;
border: 2px solid #00f2fe;
z-index: 99;
pointer-events: none;
box-shadow: 0 0 20px rgba(0, 242, 254, 0.7);
}
/* 相机聚焦样式 - 四个角折角 */
.corner {
position: absolute;
width: 25px;
height: 25px;
z-index: 100;
pointer-events: none;
}
.corner::before,
.corner::after {
content: "";
position: absolute;
background: #00f2fe;
box-shadow: 0 0 10px rgba(0, 242, 254, 0.8);
}
.corner-top-left {
top: -2px;
left: -2px;
}
.corner-top-left::before {
top: 0;
left: 0;
width: 15px;
height: 3px;
}
.corner-top-left::after {
top: 0;
left: 0;
width: 3px;
height: 15px;
}
.corner-top-right {
top: -2px;
right: -2px;
}
.corner-top-right::before {
top: 0;
right: 0;
width: 15px;
height: 3px;
}
.corner-top-right::after {
top: 0;
right: 0;
width: 3px;
height: 15px;
}
.corner-bottom-left {
bottom: -2px;
left: -2px;
}
.corner-bottom-left::before {
bottom: 0;
left: 0;
width: 15px;
height: 3px;
}
.corner-bottom-left::after {
bottom: 0;
left: 0;
width: 3px;
height: 15px;
}
.corner-bottom-right {
bottom: -2px;
right: -2px;
}
.corner-bottom-right::before {
bottom: 0;
right: 0;
width: 15px;
height: 3px;
}
.corner-bottom-right::after {
bottom: 0;
right: 0;
width: 3px;
height: 15px;
}
.camera-icon {
position: absolute;
top: 10px;
right: 10px;
color: #00f2fe;
font-size: 24px;
z-index: 100;
text-shadow: 0 0 10px rgba(0, 242, 254, 0.8);
}
.focus-effect {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 98;
border: 2px solid rgba(0, 242, 254, 0.2);
border-radius: 5px;
box-shadow: inset 0 0 30px rgba(0, 242, 254, 0.1);
}
.pulse {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 10px;
height: 10px;
border-radius: 50%;
background: rgba(0, 242, 254, 0.7);
box-shadow: 0 0 0 0 rgba(0, 242, 254, 0.7);
animation: pulse 2s infinite;
}
::v-deep.cesium-viewer-toolbar {
display: none;
}
</style>

<template> 坐标系: <el-radio-group v-model="coordinateSystem" size="small"> <el-radio-button label="gcj02">高德/火星坐标</el-radio-button> <el-radio-button label="bd09">百度坐标</el-radio-button> </el-radio-group> <Search @location-selected="handleLocationSelected" /> <LocationBar v-if="loaded" :update-interval="100" :use-dms-format="useDmsFormat" /> </template> <style> /* 原有样式完全不变 */ </style> <script setup lang="ts"> // 原有导入 + 新增坐标转换工具类导入 import { computed, onUnmounted, onMounted, reactive, ref } from "vue"; import LocationBar from "./location-bar.vue"; import Search from "./search.vue"; import initMap from "./init"; import { throttle } from "lodash"; import { loadRipplePoints, createMultipleRippleCircles } from "./circle.js"; import { $ prototype } from "../../main.ts"; import markerImage from "@/assets/images/building.png"; import { imageDark } from "naive-ui/es/image/styles"; // 新增:导入坐标转换工具类 import { CoordinateTransformer } from "./coordinate-transformer"; // 修复类型定义 type Rectangle = any; // 原有状态 + 新增坐标系选择状态 const miniMapContainer = ref<HTMLElement>(); let viewIndicator: Rectangle; const currentPosition = reactive({ longitude: 113.361538, latitude: 27.339318, }); const ZHUZHOU_EXTENT = { west: 112.5, east: 114.5, south: 26.0, north: 28.0, }; const rippleEntities = ref<any[]>([]); const heightThreshold = 80000; const indicatorStyle = ref({ left: "50%", top: "50%", display: "block", }); const loaded = ref(false); const useDmsFormat = ref(false); const overviewViewer = ref(null); let currentMarker: any = null; // 新增:坐标系选择状态(默认高德GCJ-02) const coordinateSystem = ref("gcj02"); // 'gcj02' | 'bd09' // 原有方法完全不变(波纹可见性更新) const updateRippleVisibility = throttle(() => { if (!$ prototype.$ map || rippleEntities.value.length === 0) return; let shouldShow = false; const cartographic = $ prototype.$ map.camera.positionCartographic; if (cartographic) { const cameraHeight = cartographic.height; shouldShow = cameraHeight > heightThreshold; } rippleEntities.value.forEach((entity) => { entity.show = shouldShow; }); }, 200); // 原有方法完全不变(指示器位置更新) const updateIndicatorPosition = () => { if (!$ prototype.$ map) return; const camera = $ prototype.$ map.camera; const rect = camera.computeViewRectangle(); if (!rect) return; const center = Cesium.Rectangle.center(rect); const lon = Cesium.Math.toDegrees(center.longitude); const lat = Cesium.Math.toDegrees(center.latitude); const constrainedLon = Math.max( ZHUZHOU_EXTENT.west, Math.min(ZHUZHOU_EXTENT.east, lon) ); const constrainedLat = Math.max( ZHUZHOU_EXTENT.south, Math.min(ZHUZHOU_EXTENT.north, lat) ); const lonPercent = ((constrainedLon - ZHUZHOU_EXTENT.west) / (ZHUZHOU_EXTENT.east - ZHUZHOU_EXTENT.west)) * 100; const latPercent = 100 - ((constrainedLat - ZHUZHOU_EXTENT.south) / (ZHUZHOU_EXTENT.north - ZHUZHOU_EXTENT.south)) * 100; indicatorStyle.value = { left: ${lonPercent}%, top: ${latPercent}%, display: "block", }; }; // 原有方法完全不变(鹰眼地图更新) const updateOverview = () => { if (!$ prototype.$ map || !overviewViewer.value) return; const rectangle = $ prototype.$ map.camera.computeViewRectangle(); if (!rectangle) return; updateIndicatorPosition(); }; // 原有方法完全不变(初始化鹰眼地图) const initMiniMap = () => { Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxMDhlNDdmYy03NzFhLTQ1ZTQtOWQ3NS1lZDAzNDc3YjE4NDYiLCJpZCI6MzAxNzQyLCJpYXQiOjE3NDcwNTMyMDN9.eaez8rQxVbPv2LKEU0sMDclPWyHKhh1tR27Vg-_rQSM"; if (!miniMapContainer.value) return; const worldProvider = new Cesium.UrlTemplateImageryProvider({ url: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", minimumLevel: 0, maximumLevel: 19, tilingScheme: new Cesium.WebMercatorTilingScheme(), }); const zhuzhouProvider = new Cesium.UrlTemplateImageryProvider({ url: "http://124.232.190.30:9000/proxy/pk1725866655224/map/zzzsyx_18/{z}/{x}/{y}.png", minimumLevel: 1, maximumLevel: 17, tilingScheme: new Cesium.WebMercatorTilingScheme(), }); overviewViewer.value = new Cesium.Viewer(miniMapContainer.value, { sceneMode: Cesium.SceneMode.SCENE2D, baseLayerPicker: false, homeButton: false, timeline: false, navigationHelpButton: false, animation: false, scene3DOnly: true, selectionIndicator: false, infoBox: false, imageryProvider: worldProvider, terrainProvider: undefined, mapProjection: new Cesium.WebMercatorProjection(), skyBox: false, skyAtmosphere: false, }); const zhuzhouLayer = overviewViewer.value.imageryLayers.addImageryProvider( zhuzhouProvider ); overviewViewer.value.camera.setView({ destination: Cesium.Rectangle.fromDegrees( ZHUZHOU_EXTENT.west, ZHUZHOU_EXTENT.south, ZHUZHOU_EXTENT.east, ZHUZHOU_EXTENT.north ), }); const toolbar = overviewViewer.value.container.getElementsByClassName( "cesium-viewer-toolbar" )[0]; if (toolbar) toolbar.style.display = "none"; overviewViewer.value.cesiumWidget.creditContainer.style.display = "none"; initRectangle(); }; // 原有方法完全不变(初始化视图指示器) function initRectangle() { viewIndicator = overviewViewer.value.entities.add({ rectangle: { coordinates: Cesium.Rectangle.fromDegrees( ZHUZHOU_EXTENT.west, ZHUZHOU_EXTENT.south, ZHUZHOU_EXTENT.east, ZHUZHOU_EXTENT.north ), material: Cesium.Color.RED.withAlpha(0.3), outline: true, outlineColor: Cesium.Color.RED, outlineWidth: 2, }, }); } // 原有方法完全不变(添加演示图形) function addDemoGraphics() { const chinaBoundary = $ prototype.$ map.dataSources.add( Cesium.GeoJsonDataSource.load("/shp_zz.geojson", { stroke: Cesium.Color.WHITE, fill: false, clampToGround: true, describe: null, }) ); chinaBoundary.then((dataSource) => { const entities = dataSource.entities.values; for (let entity of entities) { if (entity.polyline) { entity.polyline.fill = false; } } }); } // 原有方法完全不变(飞行到默认位置) function flyToDes() { const center = Cesium.Cartesian3.fromDegrees(-98.0, 40.0); $ prototype.$ map.camera.flyTo({ destination: new Cesium.Cartesian3( -2432812.6687511606, 5559483.804371395, 2832009.419525571 ), orientation: { heading: 6.283185307179421, pitch: -1.0472145569408116, roll: 6.2831853071795205, }, complete: function () {}, }); } // 原有方法完全不变(监听相机变化) const setupCameraListener = () => { $ prototype.$ map.camera.changed.addEventListener(updateOverview); }; // 原有方法完全不变(鹰眼地图点击处理) const handleMiniMapClick = (event: MouseEvent) => { if (!miniMapContainer.value || !overviewViewer.value) return; const rect = miniMapContainer.value.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; const xPercent = (x / rect.width) * 100; const yPercent = (y / rect.height) * 100; const lon = ZHUZHOU_EXTENT.west + (xPercent / 100) * (ZHUZHOU_EXTENT.east - ZHUZHOU_EXTENT.west); const lat = ZHUZHOU_EXTENT.north - (yPercent / 100) * (ZHUZHOU_EXTENT.north - ZHUZHOU_EXTENT.south); $ prototype.$ map.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(lon, lat, 1000000), }); }; // 原有生命周期方法完全不变 onMounted(() => { initMap(); loaded.value = true; addDemoGraphics(); flyToDes(); setTimeout(() => { initMiniMap(); setupCameraListener(); updateOverview(); }, 1000); (async () => { try { const ripplePoints = await loadRipplePoints(); rippleEntities.value = createMultipleRippleCircles($ prototype.$ map, ripplePoints); $ prototype.$ map.camera.changed.addEventListener(updateRippleVisibility); updateRippleVisibility(); } catch (error) { console.error("加载波纹圆失败:", error); } })(); }); // 原有方法完全不变(创建标记) const createMarker = (location: { lng: number; lat: number; name: string }) => { if (currentMarker) { $ prototype.$ map.entities.remove(currentMarker); } console.log("标记图片加载状态:", markerImage); console.log("创建标记位置:", location); currentMarker = $ prototype.$ map.entities.add({ position: Cesium.Cartesian3.fromDegrees(location.lng, location.lat, 10), label: { text: location.name + "(标记位置)", font: "18px sans-serif", fillColor: Cesium.Color.YELLOW, outlineColor: Cesium.Color.BLACK, outlineWidth: 2, verticalOrigin: Cesium.VerticalOrigin.TOP, pixelOffset: new Cesium.Cartesian2(0, 40), heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND, show: true, }, point: { pixelSize: 15, color: Cesium.Color.RED, outlineColor: Cesium.Color.WHITE, outlineWidth: 3, heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND, show: true, }, }); console.log("标记实体已添加:", currentMarker); $ prototype.$ map.scene.requestRender(); return currentMarker; }; // 核心修改:使用工具类实现动态坐标系转换 const handleLocationSelected = (location: { lng: number; lat: number; name: string }) => { if (!$ prototype.$ map) return; // 根据选择的坐标系进行转换 let wgsLocation; if (coordinateSystem.value === "gcj02") { // 高德/火星坐标转WGS84 wgsLocation = CoordinateTransformer.gcj02ToWgs84(location.lng, location.lat); } else { // 百度坐标转WGS84 wgsLocation = CoordinateTransformer.bd09ToWgs84(location.lng, location.lat); } console.log(转换前(${coordinateSystem.value})坐标:, location.lng, location.lat); console.log("转换后(WGS84)坐标:", wgsLocation.lng, wgsLocation.lat); // 使用转换后的坐标执行原有逻辑 const destination = Cesium.Cartesian3.fromDegrees( wgsLocation.lng, wgsLocation.lat, 2000 ); $ prototype.$ map.camera.flyTo({ destination, orientation: { heading: Cesium.Math.toRadians(0), pitch: Cesium.Math.toRadians(-45), roll: 0, }, duration: 2, complete: () => { createMarker({ ...location, ...wgsLocation }); }, }); }; // 原有方法完全不变(组件销毁清理) onUnmounted(() => { if ($ prototype.$ map) { $ prototype.$ map.destroy(); $ prototype.$ map = null; } if ($ prototype.$ map) { $ prototype.$ map.camera.changed.removeEventListener(updateRippleVisibility); } console.log("组件销毁"); }); const emit = defineEmits(["onload", "onclick"]); const initMars3d = async (option: any) => { emit("onclick", true); emit("onload", $ prototype.$ map); }; </script> <style lang="less"> /**cesium 工具按钮栏*/ .cesium-viewer-toolbar { top: auto !important; bottom: 35px !important; left: 12px !important; right: auto !important; } .cesium-toolbar-button img { height: 100%; } .cesium-viewer-toolbar > .cesium-toolbar-button, .cesium-navigationHelpButton-wrapper, .cesium-viewer-geocoderContainer { margin-bottom: 5px; float: left; clear: both; text-align: center; } .cesium-button { background-color: rgba(23, 49, 71, 0.8); color: #e6e6e6; fill: #e6e6e6; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); line-height: 32px; } .cesium-button:hover { background: #3ea6ff; } /**cesium 底图切换面板*/ .cesium-baseLayerPicker-dropDown { bottom: 0; left: 40px; max-height: 700px; margin-bottom: 5px; background-color: rgba(23, 49, 71, 0.8); } /**cesium 帮助面板*/ .cesium-navigation-help { top: auto; bottom: 0; left: 40px; transform-origin: left bottom; background: none; background-color: rgba(23, 49, 71, 0.8); .cesium-navigation-help-instructions { background: none; } .cesium-navigation-button { background: none; } .cesium-navigation-button-selected, .cesium-navigation-button-unselected:hover { background: rgba(0, 138, 255, 0.2); } } /**cesium 二维三维切换*/ .cesium-sceneModePicker-wrapper { width: auto; } .cesium-sceneModePicker-wrapper .cesium-sceneModePicker-dropDown-icon { float: right; margin: 0 3px; } /**cesium POI查询输入框*/ .cesium-viewer-geocoderContainer .search-results { left: 0; right: 40px; width: auto; z-index: 9999; } .cesium-geocoder-searchButton { background-color: rgba(23, 49, 71, 0.8); } .cesium-viewer-geocoderContainer .cesium-geocoder-input { background-color: rgba(63, 72, 84, 0.7); } .cesium-viewer-geocoderContainer .cesium-geocoder-input:focus { background-color: rgba(63, 72, 84, 0.9); } .cesium-viewer-geocoderContainer .search-results { background-color: rgba(23, 49, 71, 0.8); } /**cesium info信息框*/ .cesium-infoBox { top: 50px; background-color: rgba(23, 49, 71, 0.8); } .cesium-infoBox-title { background-color: rgba(23, 49, 71, 0.8); } /**cesium 任务栏的FPS信息*/ .cesium-performanceDisplay-defaultContainer { top: auto; bottom: 35px; right: 50px; } .cesium-performanceDisplay-ms, .cesium-performanceDisplay-fps { color: #fff; } /**cesium tileset调试信息面板*/ .cesium-viewer-cesiumInspectorContainer { top: 10px; left: 10px; right: auto; } .cesium-cesiumInspector { background-color: rgba(23, 49, 71, 0.8); } /**覆盖mars3d内部控件的颜色等样式*/ .mars3d-compass .mars3d-compass-outer { fill: rgba(23, 49, 71, 0.8); } .mars3d-contextmenu-ul, .mars3d-sub-menu { background-color: rgba(23, 49, 71, 0.8); > li > a:hover, > li > a:focus, > li > .active { background-color: #3ea6ff; } > .active > a, > .active > a:hover, > .active > a:focus { background-color: #3ea6ff; } } /* Popup样式*/ .mars3d-popup-color { color: #ffffff; } .mars3d-popup-background { background: rgba(23, 49, 71, 0.8); } .mars3d-popup-content { margin: 15px; } .mars3d-template-content label { padding-right: 6px; } .mars3d-template-titile { border-bottom: 1px solid #3ea6ff; } .mars3d-template-titile a { font-size: 16px; } .mars3d-tooltip { background: rgba(23, 49, 71, 0.8); border: 1px solid rgba(23, 49, 71, 0.8); } .mars3d-popup-btn-custom { padding: 3px 10px; border: 1px solid #209ffd; background: #209ffd1c; } .mars-dialog .mars-dialog__content { height: 100%; width: 100%; overflow: auto; padding: 5px; } .image { border: solid 2px #fff; } .content { height: 90%; padding-top: 10px; overflow-x: auto; overflow-y: auto; } .content-text { padding: 0 10px; text-indent: 30px; font-size: 17px; } .details-video { width: 100%; height: 760px; background-color: #000; } :where(.css-lt97qq9).ant-space { display: inline-flex; } :where(.css-lt97qq9).ant-space-align-center { align-items: center; } :where(.css-lt97qq9).ant-image .ant-image-mask { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: #fff; background: rgba(0, 0, 0, 0.5); cursor: pointer; opacity: 0; transition: opacity 0.3s; } :where(.css-lt97qq9).ant-image .ant-image-mask .ant-image-mask-info { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; padding: 0 4px; } :where(.css-1t97qq9)[class^="ant-image"] [class^="ant-image"], :where(.css-1t97qq9)[class*=" ant-image"] [class^="ant-image"], :where(.css-1t97qq9)[class^="ant-image"] [class*=" ant-image"], :where(.css-1t97qq9)[class*=" ant-image"] [class*=" ant-image"] { box-sizing: border-box; } :where(.css-lt97qq9).ant-image .ant-image-img { width: 100%; height: auto; vertical-align: middle; } </style> <style scoped> .mini-map-container { position: relative; width: 100%; height: 100%; } .main-viewer { width: 100%; height: 100%; } .mini-map { position: absolute; right: 3vw; bottom: 6vh; width: 12vw; height: 17vh; border: 2px solid #fff; box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); z-index: 999; cursor: pointer; overflow: hidden; } .location-indicator { position: absolute; width: 19px; height: 19px; transform: translate(-50%, -50%); z-index: 100; border: 2px solid red; border-radius: 2px; } /* 原十字准星伪元素保持不变 */ .location-indicator::before, .location-indicator::after { content: ''; position: absolute; background-color: red; top: 50%; left: 50%; transform: translate(-50%, -50%); } .location-indicator::before { width: 12px; height: 2px; } .location-indicator::after { width: 2px; height: 12px; } /* 新增四条准星延伸线 */ .ext-line { position: absolute; background-color: red; } /* 顶部延伸线 - 从矩形边框向上延伸 */ .ext-line-top { top: -5px; left: 50%; transform: translateX(-50%); width: 2px; height: 5px; } /* 右侧延伸线 - 从矩形边框向右延伸 */ .ext-line-right { top: 50%; right: -5px; transform: translateY(-50%); width: 5px; height: 2px; } /* 底部延伸线 - 从矩形边框向下延伸 */ .ext-line-bottom { bottom: -5px; left: 50%; transform: translateX(-50%); width: 2px; height: 5px; } /* 左侧延伸线 - 从矩形边框向左延伸 */ .ext-line-left { top: 50%; left: -5px; transform: translateY(-50%); width: 5px; height: 2px; } .view-rectangle { position: absolute; border: 2px solid #00f2fe; z-index: 99; pointer-events: none; box-shadow: 0 0 20px rgba(0, 242, 254, 0.7); } /* 相机聚焦样式 - 四个角折角 */ .corner { position: absolute; width: 25px; height: 25px; z-index: 100; pointer-events: none; } .corner::before, .corner::after { content: ""; position: absolute; background: #00f2fe; box-shadow: 0 0 10px rgba(0, 242, 254, 0.8); } .corner-top-left { top: -2px; left: -2px; } .corner-top-left::before { top: 0; left: 0; width: 15px; height: 3px; } .corner-top-left::after { top: 0; left: 0; width: 3px; height: 15px; } .corner-top-right { top: -2px; right: -2px; } .corner-top-right::before { top: 0; right: 0; width: 15px; height: 3px; } .corner-top-right::after { top: 0; right: 0; width: 3px; height: 15px; } .corner-bottom-left { bottom: -2px; left: -2px; } .corner-bottom-left::before { bottom: 0; left: 0; width: 15px; height: 3px; } .corner-bottom-left::after { bottom: 0; left: 0; width: 3px; height: 15px; } .corner-bottom-right { bottom: -2px; right: -2px; } .corner-bottom-right::before { bottom: 0; right: 0; width: 15px; height: 3px; } .corner-bottom-right::after { bottom: 0; right: 0; width: 3px; height: 15px; } .camera-icon { position: absolute; top: 10px; right: 10px; color: #00f2fe; font-size: 24px; z-index: 100; text-shadow: 0 0 10px rgba(0, 242, 254, 0.8); } .focus-effect { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 98; border: 2px solid rgba(0, 242, 254, 0.2); border-radius: 5px; box-shadow: inset 0 0 30px rgba(0, 242, 254, 0.1); } .pulse { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 10px; height: 10px; border-radius: 50%; background: rgba(0, 242, 254, 0.7); box-shadow: 0 0 0 0 rgba(0, 242, 254, 0.7); animation: pulse 2s infinite; } ::v-deep.cesium-viewer-toolbar { display: none; } </style>
检查live-map.vue代码,其中的鹰眼地图部分代码好像存在问题如下:鹰眼联动有问题,鹰眼图不用操作,基于此你帮我检查修改一下,尽可能不更改其他任何无关代码,减少对vue文件的修改