Compare commits

..

No commits in common. "a1397c591fd3f4dfc264b05b0acca721bdbb5f6f" and "ed230e69ab06983090735f11147ecd5074497093" have entirely different histories.

7 changed files with 36 additions and 365 deletions

View File

@ -11,15 +11,7 @@
<div class="video-monitor-item__content">
<div class="video-placeholder">
<!-- 视频播放器 -->
<video
:src="monitor.videoSrc"
autoplay
loop
muted
playsinline
/>
<!-- 这里放置实际的视频流组件 -->
<div class="video-time">{{ currentTime }}</div>
<!-- 控制条叠加在视频底部 -->
@ -172,15 +164,17 @@ onUnmounted(() => {
overflow: hidden;
// margin-bottom: vh(12);
//
video {
//
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
z-index: 0;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: vw(48);
height: vh(48);
background: url(../../assets/images/SketchPngb3b734375de691a8ba794eee7807988d78f942877ab220ebea0aac3bbddccd8b.png) center/contain no-repeat;
opacity: 0.3;
}
.video-time {

View File

@ -7,8 +7,8 @@ import soldierIcon from '../assets/images/SketchPngfbec927027ff9e49207749ebaafd2
import deviceIcon from '../assets/images/SketchPng860d54f2a31f5f441fc6a88081224f1e98534bf6d5ca1246e420983bdf690380.png'
import emergencyBaseIcon from '../assets/images/应急基地.png'
// 默认高度偏移(米)- 与 WuRenJi 保持一致
const DEFAULT_HEIGHT_OFFSET = 100
// 默认高度偏移(米)
const DEFAULT_HEIGHT_OFFSET = 10
/**
* 地图标记管理 Composable
@ -136,18 +136,9 @@ export function useMapMarkers() {
// 计算中心点
const center = Cesium.BoundingSphere.fromPoints(positions).center
// 将中心点转换为经纬度然后重新创建位置确保高度为0
// 这样 CLAMP_TO_GROUND 才能正确工作
const centerCartographic = Cesium.Cartographic.fromCartesian(center)
const centerPosition = Cesium.Cartesian3.fromRadians(
centerCartographic.longitude,
centerCartographic.latitude,
0 // 高度设为0让 CLAMP_TO_GROUND 自动贴地
)
// 添加标签
const labelEntity = viewer.entities.add({
position: centerPosition,
position: center,
label: {
text: '模拟塌陷区域',
font: '18px "Microsoft YaHei", sans-serif',
@ -167,7 +158,7 @@ export function useMapMarkers() {
// 添加中心点标记
const pointEntity = viewer.entities.add({
position: centerPosition,
position: center,
point: {
color: Cesium.Color.ORANGE,
pixelSize: 12,
@ -180,10 +171,6 @@ export function useMapMarkers() {
collapseAreaEntities.value = entities
console.log('[useMapMarkers] 塌陷区域绘制完成')
// 强制渲染场景,确保 CLAMP_TO_GROUND 立即生效
viewer.scene.requestRender()
return center
}
@ -304,10 +291,6 @@ export function useMapMarkers() {
markerEntities.value.push(...entities)
console.log(`[useMapMarkers] 添加固定标记 ${entities.length}`)
// 强制渲染场景,确保 CLAMP_TO_GROUND 立即生效
viewer.scene.requestRender()
return entities
}
@ -404,10 +387,6 @@ export function useMapMarkers() {
markerEntities.value.push(...entities)
console.log(`[useMapMarkers] 添加随机标记 ${entities.length}`)
// 强制渲染场景,确保 CLAMP_TO_GROUND 立即生效
viewer.scene.requestRender()
return entities
}
@ -648,11 +627,6 @@ export function useMapMarkers() {
emergencyResourceEntities.value = entities
console.log(`[useMapMarkers] 添加养护站标记 ${entities.length}`)
// 强制渲染场景,确保 CLAMP_TO_GROUND 立即生效
if (entities.length > 0) {
viewer.scene.requestRender()
}
}
return {

View File

@ -1,63 +0,0 @@
import { ref } from 'vue'
/**
* 地图 Tooltip 状态管理
* 用于显示地图标记点的轻量级信息提示框
*/
export function useMapTooltip() {
// Tooltip 状态
const tooltipState = ref({
visible: false,
x: 0,
y: 0,
title: '',
icon: '',
zIndex: 20,
data: null // 业务数据,用于内容插槽渲染
})
/**
* 显示 Tooltip
* @param {Object} options - Tooltip 配置选项
* @param {number} options.x - 屏幕 X 坐标像素
* @param {number} options.y - 屏幕 Y 坐标像素
* @param {string} [options.title=''] - Tooltip 标题文本
* @param {string} [options.icon=''] - 标题左侧图标的图片路径
* @param {Object} [options.data=null] - 业务数据
*/
const showTooltip = ({ x, y, title = '', icon = '', data = null }) => {
tooltipState.value = {
visible: true,
x,
y,
title,
icon,
zIndex: 20,
data
}
}
/**
* 隐藏 Tooltip
*/
const hideTooltip = () => {
tooltipState.value.visible = false
}
/**
* 更新 Tooltip 位置
* @param {number} x - 屏幕 X 坐标
* @param {number} y - 屏幕 Y 坐标
*/
const updateTooltipPosition = (x, y) => {
tooltipState.value.x = x
tooltipState.value.y = y
}
return {
tooltipState,
showTooltip,
hideTooltip,
updateTooltipPosition
}
}

View File

@ -2,8 +2,6 @@
* 3D态势感知常量配置
*/
import { getVideoUrl } from '@shared/utils'
// 视频监控视角类型
export const VIDEO_TYPES = {
PERSONNEL: 'personnel', // 单兵视角
@ -18,8 +16,7 @@ export const VIDEO_MONITORS = [
id: 1,
type: VIDEO_TYPES.PERSONNEL,
title: '单兵(张三三)设备视角',
// videoSrc: getVideoUrl('demo/ylzg/单兵视角.mp4'), // 从 OSS 获取视频 URL
videoSrc: 'http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/demo/ylzg/单兵视角.mp4',
videoSrc: '/videos/personnel-001.mp4', // 视频源路径
dateRange: '2025/9/1-2025/12/1', // 日期范围
hasAudio: true,
hasMegaphone: true,
@ -30,8 +27,7 @@ export const VIDEO_MONITORS = [
id: 2,
type: VIDEO_TYPES.DRONE,
title: '无人机(001)视角',
// videoSrc: getVideoUrl('demo/ylzg/无人机视角.mp4'), // 从 OSS 获取视频 URL
videoSrc: 'http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/demo/ylzg/无人机视角.mp4',
videoSrc: '/videos/drone-001.mp4',
dateRange: '2025/9/1-2025/12/1',
hasAudio: false,
hasMegaphone: true,
@ -42,8 +38,7 @@ export const VIDEO_MONITORS = [
id: 3,
type: VIDEO_TYPES.VEHICLE_EXTERNAL,
title: '指挥车外部视角',
// videoSrc: getVideoUrl('demo/ylzg/单兵视角.mp4'), // 暂时使用单兵视角视频
videoSrc: 'http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/demo/ylzg/单兵视角.mp4',
videoSrc: '/videos/vehicle-external-001.mp4',
dateRange: '2025/9/1-2025/12/1',
hasAudio: true,
hasMegaphone: true,
@ -54,8 +49,7 @@ export const VIDEO_MONITORS = [
id: 4,
type: VIDEO_TYPES.VEHICLE_MEETING,
title: '指挥车会议视角',
// videoSrc: getVideoUrl('demo/ylzg/无人机视角.mp4'), // 暂时使用无人机视角视频
videoSrc: 'http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/demo/ylzg/无人机视角.mp4',
videoSrc: '/videos/vehicle-meeting-001.mp4',
dateRange: '2025/9/1-2025/12/1',
hasAudio: true,
hasMegaphone: true,

View File

@ -111,15 +111,11 @@ import { useDisasterData } from "./composables/useDisasterData";
import { useDualMapCompare } from "./composables/useDualMapCompare";
import { useMapMarkers } from "./composables/useMapMarkers";
import { use3DTiles } from "./composables/use3DTiles";
import { useMapTooltip } from "./composables/useMapTooltip";
import { useMapStore } from "@/map";
import { request } from "@shared/utils/request";
//
//
import emergencyCenterIcon from "./assets/images/应急中心.png";
import soldierIcon from "./assets/images/SketchPngfbec927027ff9e49207749ebaafd229429315341fda199251b6dfb1723ff17fb.png";
import deviceIcon from "./assets/images/SketchPng860d54f2a31f5f441fc6a88081224f1e98534bf6d5ca1246e420983bdf690380.png";
import emergencyBaseIcon from "./assets/images/应急基地.png";
// 使
const disasterData = useDisasterData();
@ -154,182 +150,9 @@ const {
clearEmergencyResourceMarkers,
} = useMapMarkers();
// Tooltip
const { tooltipState: mapTooltip, showTooltip, hideTooltip, updateTooltipPosition } = useMapTooltip();
// tooltip
const currentTooltipEntity = ref(null);
// 3D Tiles
const { load3DTileset, waitForTilesetReady } = use3DTiles();
/**
* 设置地图点击事件处理器
* 当用户点击地图标记点时显示 Tooltip
*/
const setupMapClickHandler = (viewer) => {
if (!viewer) return;
// ScreenSpaceEventHandler
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction((click) => {
//
const pickedObject = viewer.scene.pick(click.position);
if (Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id)) {
const entity = pickedObject.id;
// properties
if (entity.properties) {
const type = entity.properties.type?.getValue();
// Tooltip
if (type === 'soldier') {
showMarkerTooltip(viewer, entity, click.position, soldierIcon);
} else if (type === 'device') {
showMarkerTooltip(viewer, entity, click.position, deviceIcon);
} else if (type === 'emergencyBase' || type === 'station') {
showMarkerTooltip(viewer, entity, click.position, emergencyBaseIcon);
}
}
} else {
// Tooltip
hideTooltip();
currentTooltipEntity.value = null;
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
// tooltip
viewer.scene.postRender.addEventListener(() => {
if (currentTooltipEntity.value && mapTooltip.value.visible) {
updateTooltipPositionForEntity(viewer, currentTooltipEntity.value);
}
});
};
/**
* 显示标记点 Tooltip
* @param {Cesium.Viewer} viewer - Cesium viewer 实例
* @param {Cesium.Entity} entity - 被点击的实体
* @param {Cesium.Cartesian2} screenPosition - 点击的屏幕坐标备用
* @param {string} icon - 图标路径
*/
const showMarkerTooltip = (viewer, entity, screenPosition, icon) => {
const properties = entity.properties;
const type = properties.type?.getValue();
// 3D
const position = entity.position?.getValue(Cesium.JulianDate.now());
if (!position) {
console.warn('[Tooltip] 无法获取实体位置');
return;
}
// 使 CLAMP_TO_GROUND billboard
const cartographic = Cesium.Cartographic.fromCartesian(position);
let clampedPosition = position;
// 使 globe.getHeight
if (viewer.scene.globe) {
const height = viewer.scene.globe.getHeight(cartographic);
if (Cesium.defined(height)) {
cartographic.height = height;
clampedPosition = Cesium.Cartographic.toCartesian(cartographic);
}
}
// 3D
const canvasPosition = viewer.scene.cartesianToCanvasCoordinates(clampedPosition);
if (!Cesium.defined(canvasPosition)) {
console.warn('[Tooltip] 无法转换坐标到屏幕位置');
return;
}
// Tooltip
let title = '';
const fields = [];
if (type === 'soldier') {
title = '单兵信息';
fields.push(
{ label: '姓名', value: properties.name?.getValue() || '-' },
{ label: '部门', value: properties.department?.getValue() || '-' },
{ label: '位置', value: properties.location?.getValue() || '-' }
);
} else if (type === 'device') {
title = '设备信息';
fields.push(
{ label: '设备名称', value: properties.name?.getValue() || '-' },
{ label: '设备类型', value: properties.deviceType?.getValue() || '-' },
{ label: '位置', value: properties.location?.getValue() || '-' }
);
} else if (type === 'emergencyBase') {
title = '应急基地';
fields.push(
{ label: '名称', value: properties.name?.getValue() || '-' },
{ label: '地址', value: properties.address?.getValue() || '-' },
{ label: '距离', value: properties.distance?.getValue() || '-' }
);
} else if (type === 'station') {
title = '养护站';
fields.push(
{ label: '名称', value: properties.name?.getValue() || '-' },
{ label: '距离', value: `${properties.distance?.getValue() || 0}公里` }
);
}
// Tooltip使
showTooltip({
x: canvasPosition.x,
y: canvasPosition.y,
title,
icon,
data: { fields }
});
//
currentTooltipEntity.value = entity;
};
/**
* 更新 Tooltip 位置当相机移动时
* @param {Cesium.Viewer} viewer - Cesium viewer 实例
* @param {Cesium.Entity} entity - 实体对象
*/
const updateTooltipPositionForEntity = (viewer, entity) => {
const position = entity.position?.getValue(Cesium.JulianDate.now());
if (!position) return;
// 使 CLAMP_TO_GROUND billboard
const cartographic = Cesium.Cartographic.fromCartesian(position);
let clampedPosition = position;
// 使 globe.getHeight
if (viewer.scene.globe) {
const height = viewer.scene.globe.getHeight(cartographic);
if (Cesium.defined(height)) {
cartographic.height = height;
clampedPosition = Cesium.Cartographic.toCartesian(cartographic);
}
}
// 3D
const canvasPosition = viewer.scene.cartesianToCanvasCoordinates(clampedPosition);
// tooltip
if (!Cesium.defined(canvasPosition)) {
hideTooltip();
currentTooltipEntity.value = null;
return;
}
updateTooltipPosition(canvasPosition.x, canvasPosition.y);
};
//
onMounted(() => {
//
@ -374,10 +197,6 @@ onMounted(() => {
...DEFAULT_CAMERA_VIEW,
duration: 1,
});
// - Tooltip
setupMapClickHandler(viewer);
// 1000ms
// setTimeout(() => {
// camera.flyTo({
@ -385,7 +204,7 @@ onMounted(() => {
// duration: 1,
// });
// }, 5000);
// return;
return;
/**
* 设置相机到指定的笛卡尔坐标
@ -446,7 +265,7 @@ onMounted(() => {
console.log("[index.vue] 开始初始化地图标记...");
const sampledCollapseCenter = await initializeMarkers(viewer, {
useSampledHeights: true, // 使
heightOffset: 100, // 100 WuRenJi
heightOffset: 10, // 10
});
//
@ -590,6 +409,20 @@ const selectedCenter = ref({
image: null,
});
/**
* 地图 Tooltip 状态管理
* 用于显示地图标记点的轻量级信息提示框
*/
const mapTooltip = ref({
visible: false,
x: 0,
y: 0,
title: "",
icon: "",
zIndex: 20, //
data: null, //
});
//
const handleBack = () => {
console.log("返回驾驶舱");

View File

@ -16,31 +16,3 @@ export const APP_CONFIG = {
title: '数据大屏',
version: '1.0.0'
}
// OSS 配置
// 注意: 实际使用时需要从配置中心或环境变量读取,此处为示例配置
export const OSS_CONFIG = {
// http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/demo/ylzg/%E5%8D%95%E5%85%B5%E8%A7%86%E8%A7%92.mp4
// OSS 服务地址
url: import.meta.env.VITE_OSS_URL || 'http://222.212.85.86:9000',
// OSS bucket
bucket: import.meta.env.VITE_OSS_BUCKET || '300bdf2b-a150-406e-be63-d28bd29b409f'
}
/**
* 获取 OSS 配置
* @returns {{url: string, bucket: string}}
*/
export const getOssConfig = () => {
const { url, bucket } = OSS_CONFIG
// 确保 URL 包含协议
if (url.includes('http://') || url.includes('https://')) {
return { url, bucket }
} else {
return {
url: `http://${url}`,
bucket
}
}
}

View File

@ -1,5 +1,3 @@
import { getOssConfig } from '../config'
/**
* 格式化日期
* @param {Date|string|number} date
@ -75,34 +73,3 @@ export function deepClone(obj) {
}
return clonedObj
}
/**
* 获取 OSS 资源 URL
* @param {string} path - OSS 对象路径, 'demo/ylzg/单兵视角.mp4'
* @returns {string} 完整的 OSS 资源 URL
* @example
* getAssetUrl('demo/ylzg/单兵视角.mp4')
* // => 'http://183.221.225.106:9001/6251daf8-4127-40e0-980d-c86f8a765b20/demo/ylzg/单兵视角.mp4'
*/
export function getAssetUrl(path) {
const { url, bucket } = getOssConfig()
return `${url}/${bucket}/${path}`
}
/**
* 获取视频 URL (getAssetUrl 的别名,用于语义化)
* @param {string} path - 视频文件路径
* @returns {string} 完整的视频 URL
*/
export function getVideoUrl(path) {
return getAssetUrl(path)
}
/**
* 获取图片 URL (getAssetUrl 的别名,用于语义化)
* @param {string} path - 图片文件路径
* @returns {string} 完整的图片 URL
*/
export function getImageUrl(path) {
return getAssetUrl(path)
}