Compare commits
No commits in common. "06c462a57722d408b7cba93c6612ff97e2f25ffd" and "6123522fc9d3e07a742d9499eef4e625f2bf9ae7" have entirely different histories.
06c462a577
...
6123522fc9
|
Before Width: | Height: | Size: 166 B After Width: | Height: | Size: 169 B |
|
Before Width: | Height: | Size: 143 B After Width: | Height: | Size: 140 B |
|
Before Width: | Height: | Size: 141 B After Width: | Height: | Size: 137 B |
|
Before Width: | Height: | Size: 136 B After Width: | Height: | Size: 133 B |
@ -94,11 +94,6 @@
|
|||||||
<div class="map-tooltip__content">
|
<div class="map-tooltip__content">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 操作按钮区域 -->
|
|
||||||
<div v-if="$slots.actions" class="map-tooltip__actions">
|
|
||||||
<slot name="actions" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -285,8 +280,8 @@ const handleClose = () => {
|
|||||||
*/
|
*/
|
||||||
.map-tooltip__corner {
|
.map-tooltip__corner {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
// width: vw(20);
|
width: vw(20);
|
||||||
// height: vw(20); // 使用 vw 保持正方形
|
height: vw(20); // 使用 vw 保持正方形
|
||||||
pointer-events: none; // 装饰元素不响应鼠标事件
|
pointer-events: none; // 装饰元素不响应鼠标事件
|
||||||
z-index: 1; // 确保显示在背景之上
|
z-index: 1; // 确保显示在背景之上
|
||||||
|
|
||||||
@ -436,19 +431,6 @@ const handleClose = () => {
|
|||||||
gap: vh(6);
|
gap: vh(6);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 操作按钮区域
|
|
||||||
* 用于显示"连线"、"联动"等交互按钮
|
|
||||||
*/
|
|
||||||
.map-tooltip__actions {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
margin-top: vh(16);
|
|
||||||
padding-top: vh(12);
|
|
||||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 淡入淡出动画
|
* 淡入淡出动画
|
||||||
* 结合缩放效果,提升视觉体验
|
* 结合缩放效果,提升视觉体验
|
||||||
|
|||||||
@ -1,261 +0,0 @@
|
|||||||
import { ref } from 'vue'
|
|
||||||
import * as Cesium from 'cesium'
|
|
||||||
import soldierIcon from '../assets/images/SketchPngfbec927027ff9e49207749ebaafd229429315341fda199251b6dfb1723ff17fb.png'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 实体动画管理 Composable
|
|
||||||
* 负责管理地图实体的路径动画,特别是应急人员沿路径移动
|
|
||||||
*/
|
|
||||||
export function useEntityAnimation() {
|
|
||||||
// 动画实体引用
|
|
||||||
const animatedEntity = ref(null)
|
|
||||||
|
|
||||||
// 动画是否正在运行
|
|
||||||
const isAnimating = ref(false)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 应急人员路径坐标 (Cartesian3 格式)
|
|
||||||
* 这些坐标定义了人员从起点到终点的移动路径
|
|
||||||
*/
|
|
||||||
const PERSONNEL_PATH_COORDINATES = [
|
|
||||||
{ x: -1705480.2641142386, y: 5247435.146513505, z: 3189090.667137187 },
|
|
||||||
{ x: -1705494.4820981335, y: 5247449.285793587, z: 3189060.452114544 },
|
|
||||||
{ x: -1705510.0809808762, y: 5247465.767274618, z: 3189023.050648535 },
|
|
||||||
{ x: -1705511.0338125327, y: 5247476.4118378535, z: 3188998.3643177846 },
|
|
||||||
{ x: -1705515.8155320068, y: 5247491.504151518, z: 3188970.3225750974 },
|
|
||||||
{ x: -1705512.9523099929, y: 5247504.710873116, z: 3188943.7936982675 },
|
|
||||||
{ x: -1705519.7649184526, y: 5247519.060354441, z: 3188915.1883725724 },
|
|
||||||
{ x: -1705528.241912857, y: 5247539.302819527, z: 3188872.220619207 },
|
|
||||||
{ x: -1705530.7649293465, y: 5247548.26353356, z: 3188852.4565014304 },
|
|
||||||
{ x: -1705536.847870567, y: 5247562.107401437, z: 3188816.1164027476 },
|
|
||||||
{ x: -1705554.2817406887, y: 5247571.234068825, z: 3188789.6105980803 },
|
|
||||||
{ x: -1705573.026007999, y: 5247580.50225183, z: 3188770.244426234 },
|
|
||||||
{ x: -1705602.018256302, y: 5247597.236114229, z: 3188743.7470805836 }
|
|
||||||
]
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 启动应急人员沿路径移动动画
|
|
||||||
* @param {Cesium.Viewer} viewer - Cesium viewer 实例
|
|
||||||
* @param {Object} options - 配置选项
|
|
||||||
* @param {number} [options.duration=36] - 动画总时长(秒)
|
|
||||||
* @param {boolean} [options.trackEntity=false] - 是否相机跟随实体
|
|
||||||
* @param {string} [options.personnelName='应急人员'] - 人员名称
|
|
||||||
* @param {string} [options.department='应急救援队'] - 所属部门
|
|
||||||
* @returns {Cesium.Entity} 返回创建的动画实体
|
|
||||||
*/
|
|
||||||
const startPersonnelMovement = (viewer, options = {}) => {
|
|
||||||
if (!viewer) {
|
|
||||||
console.warn('[useEntityAnimation] startPersonnelMovement: viewer 为空')
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAnimating.value) {
|
|
||||||
console.warn('[useEntityAnimation] 动画已在运行中,请先停止当前动画')
|
|
||||||
return animatedEntity.value
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
duration: options.duration ?? 60, // 默认 60 秒,让移动更清晰可见
|
|
||||||
trackEntity: options.trackEntity ?? false,
|
|
||||||
personnelName: options.personnelName ?? '应急人员',
|
|
||||||
department: options.department ?? '应急救援队'
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[useEntityAnimation] 开始启动人员移动动画...', config)
|
|
||||||
|
|
||||||
// 设置动画时间范围
|
|
||||||
const startTime = Cesium.JulianDate.now()
|
|
||||||
const stopTime = Cesium.JulianDate.addSeconds(
|
|
||||||
startTime,
|
|
||||||
config.duration,
|
|
||||||
new Cesium.JulianDate()
|
|
||||||
)
|
|
||||||
|
|
||||||
// 配置 viewer 时钟
|
|
||||||
viewer.clock.startTime = startTime.clone()
|
|
||||||
viewer.clock.stopTime = stopTime.clone()
|
|
||||||
viewer.clock.currentTime = startTime.clone()
|
|
||||||
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP // 动画结束后停止
|
|
||||||
viewer.clock.multiplier = 1 // 实时速度
|
|
||||||
viewer.clock.shouldAnimate = true
|
|
||||||
|
|
||||||
// 创建 SampledPositionProperty 定义路径
|
|
||||||
const positionProperty = new Cesium.SampledPositionProperty()
|
|
||||||
|
|
||||||
// 计算每个路径点的时间间隔
|
|
||||||
const numberOfPoints = PERSONNEL_PATH_COORDINATES.length
|
|
||||||
const timeInterval = config.duration / (numberOfPoints - 1)
|
|
||||||
|
|
||||||
// 添加路径采样点
|
|
||||||
PERSONNEL_PATH_COORDINATES.forEach((coord, index) => {
|
|
||||||
const time = Cesium.JulianDate.addSeconds(
|
|
||||||
startTime,
|
|
||||||
index * timeInterval,
|
|
||||||
new Cesium.JulianDate()
|
|
||||||
)
|
|
||||||
const position = new Cesium.Cartesian3(coord.x, coord.y, coord.z)
|
|
||||||
positionProperty.addSample(time, position)
|
|
||||||
})
|
|
||||||
|
|
||||||
// 创建脉冲缩放效果 - 让图标闪烁更醒目
|
|
||||||
const pulseScale = new Cesium.CallbackProperty((time) => {
|
|
||||||
const elapsed = Cesium.JulianDate.secondsDifference(time, startTime)
|
|
||||||
// 使用正弦波产生脉冲效果,频率 3Hz,幅度 ±30%
|
|
||||||
return 1.0 + Math.sin(elapsed * 3) * 0.3
|
|
||||||
}, false)
|
|
||||||
|
|
||||||
// 创建动画实体
|
|
||||||
const entity = viewer.entities.add({
|
|
||||||
availability: new Cesium.TimeIntervalCollection([
|
|
||||||
new Cesium.TimeInterval({
|
|
||||||
start: startTime,
|
|
||||||
stop: stopTime
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
position: positionProperty,
|
|
||||||
orientation: new Cesium.VelocityOrientationProperty(positionProperty), // 自动朝向移动方向
|
|
||||||
billboard: {
|
|
||||||
image: soldierIcon,
|
|
||||||
width: 48, // 增大尺寸,从 36 增加到 48
|
|
||||||
height: 56, // 从 40 增加到 56
|
|
||||||
scale: pulseScale, // 使用脉冲缩放效果
|
|
||||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
||||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
|
||||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
|
||||||
// 添加距离缩放,让图标在不同距离下都清晰可见
|
|
||||||
scaleByDistance: new Cesium.NearFarScalar(
|
|
||||||
1000, 1.5, // 近距离(1000米)放大到 1.5 倍
|
|
||||||
50000, 0.8 // 远距离(50000米)缩小到 0.8 倍
|
|
||||||
)
|
|
||||||
},
|
|
||||||
// 添加发光轨迹线
|
|
||||||
path: {
|
|
||||||
resolution: 1,
|
|
||||||
material: new Cesium.PolylineGlowMaterialProperty({
|
|
||||||
glowPower: 0.4, // 发光强度
|
|
||||||
taperPower: 0.5, // 渐变效果
|
|
||||||
color: Cesium.Color.CYAN // 青色轨迹
|
|
||||||
}),
|
|
||||||
width: 8,
|
|
||||||
leadTime: 0, // 前方不显示轨迹
|
|
||||||
trailTime: config.duration // 后方显示完整轨迹
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
type: 'animatedSoldier',
|
|
||||||
name: config.personnelName,
|
|
||||||
department: config.department,
|
|
||||||
isAnimating: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
animatedEntity.value = entity
|
|
||||||
isAnimating.value = true
|
|
||||||
|
|
||||||
// 可选: 相机跟随实体
|
|
||||||
if (config.trackEntity) {
|
|
||||||
viewer.trackedEntity = entity
|
|
||||||
// 设置相机视角偏移
|
|
||||||
entity.viewFrom = new Cesium.Cartesian3(-100.0, -100.0, 50.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[useEntityAnimation] 人员移动动画已启动')
|
|
||||||
|
|
||||||
// 监听动画结束事件
|
|
||||||
const removeListener = viewer.clock.onStop.addEventListener(() => {
|
|
||||||
console.log('[useEntityAnimation] 动画已结束')
|
|
||||||
isAnimating.value = false
|
|
||||||
removeListener() // 移除监听器
|
|
||||||
})
|
|
||||||
|
|
||||||
return entity
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 停止人员移动动画
|
|
||||||
* @param {Cesium.Viewer} viewer - Cesium viewer 实例
|
|
||||||
* @param {boolean} [removeEntity=true] - 是否移除动画实体
|
|
||||||
*/
|
|
||||||
const stopPersonnelMovement = (viewer, removeEntity = true) => {
|
|
||||||
if (!viewer) {
|
|
||||||
console.warn('[useEntityAnimation] stopPersonnelMovement: viewer 为空')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[useEntityAnimation] 停止人员移动动画')
|
|
||||||
|
|
||||||
// 停止时钟动画
|
|
||||||
viewer.clock.shouldAnimate = false
|
|
||||||
|
|
||||||
// 取消相机跟随
|
|
||||||
if (viewer.trackedEntity === animatedEntity.value) {
|
|
||||||
viewer.trackedEntity = undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移除实体
|
|
||||||
if (removeEntity && animatedEntity.value) {
|
|
||||||
viewer.entities.remove(animatedEntity.value)
|
|
||||||
animatedEntity.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
isAnimating.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 暂停动画
|
|
||||||
* @param {Cesium.Viewer} viewer - Cesium viewer 实例
|
|
||||||
*/
|
|
||||||
const pauseAnimation = (viewer) => {
|
|
||||||
if (!viewer) return
|
|
||||||
viewer.clock.shouldAnimate = false
|
|
||||||
console.log('[useEntityAnimation] 动画已暂停')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 恢复动画
|
|
||||||
* @param {Cesium.Viewer} viewer - Cesium viewer 实例
|
|
||||||
*/
|
|
||||||
const resumeAnimation = (viewer) => {
|
|
||||||
if (!viewer) return
|
|
||||||
if (isAnimating.value) {
|
|
||||||
viewer.clock.shouldAnimate = true
|
|
||||||
console.log('[useEntityAnimation] 动画已恢复')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取路径的起点坐标 (用于初始标记点)
|
|
||||||
* @returns {Cesium.Cartesian3} 起点坐标
|
|
||||||
*/
|
|
||||||
const getStartPosition = () => {
|
|
||||||
const firstCoord = PERSONNEL_PATH_COORDINATES[0]
|
|
||||||
return new Cesium.Cartesian3(firstCoord.x, firstCoord.y, firstCoord.z)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将 Cartesian3 坐标转换为经纬度
|
|
||||||
* @param {Cesium.Cartesian3} cartesian - Cartesian3 坐标
|
|
||||||
* @returns {Object} { longitude, latitude, height }
|
|
||||||
*/
|
|
||||||
const cartesianToLonLat = (cartesian) => {
|
|
||||||
const cartographic = Cesium.Cartographic.fromCartesian(cartesian)
|
|
||||||
return {
|
|
||||||
longitude: Cesium.Math.toDegrees(cartographic.longitude),
|
|
||||||
latitude: Cesium.Math.toDegrees(cartographic.latitude),
|
|
||||||
height: cartographic.height
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
animatedEntity,
|
|
||||||
isAnimating,
|
|
||||||
startPersonnelMovement,
|
|
||||||
stopPersonnelMovement,
|
|
||||||
pauseAnimation,
|
|
||||||
resumeAnimation,
|
|
||||||
getStartPosition,
|
|
||||||
cartesianToLonLat,
|
|
||||||
PERSONNEL_PATH_COORDINATES
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useEntityAnimation
|
|
||||||
@ -126,18 +126,6 @@
|
|||||||
<span class="tooltip-field-value">{{ field.value }}</span>
|
<span class="tooltip-field-value">{{ field.value }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 操作按钮插槽 -->
|
|
||||||
<template v-if="mapTooltip.data && mapTooltip.data.actions" #actions>
|
|
||||||
<button
|
|
||||||
v-for="(action, index) in mapTooltip.data.actions"
|
|
||||||
:key="index"
|
|
||||||
class="tooltip-action-btn"
|
|
||||||
@click="handleTooltipAction(action)"
|
|
||||||
>
|
|
||||||
{{ action.label }}
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
</MapTooltip>
|
</MapTooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -184,7 +172,6 @@ import { useDualMapCompare } from "./composables/useDualMapCompare";
|
|||||||
import { useMapMarkers } from "./composables/useMapMarkers";
|
import { useMapMarkers } from "./composables/useMapMarkers";
|
||||||
import { use3DTiles } from "./composables/use3DTiles";
|
import { use3DTiles } from "./composables/use3DTiles";
|
||||||
import { useMapTooltip } from "./composables/useMapTooltip";
|
import { useMapTooltip } from "./composables/useMapTooltip";
|
||||||
import { useEntityAnimation } from "./composables/useEntityAnimation";
|
|
||||||
import { useMapStore } from "@/map";
|
import { useMapStore } from "@/map";
|
||||||
import { request } from "@shared/utils/request";
|
import { request } from "@shared/utils/request";
|
||||||
|
|
||||||
@ -246,9 +233,6 @@ const {
|
|||||||
// 地图 Tooltip 功能
|
// 地图 Tooltip 功能
|
||||||
const { tooltipState: mapTooltip, showTooltip, hideTooltip, updateTooltipPosition } = useMapTooltip();
|
const { tooltipState: mapTooltip, showTooltip, hideTooltip, updateTooltipPosition } = useMapTooltip();
|
||||||
|
|
||||||
// 实体动画功能
|
|
||||||
const { startPersonnelMovement, stopPersonnelMovement, isAnimating } = useEntityAnimation();
|
|
||||||
|
|
||||||
// 当前显示 tooltip 的实体(用于相机移动时更新位置)
|
// 当前显示 tooltip 的实体(用于相机移动时更新位置)
|
||||||
const currentTooltipEntity = ref(null);
|
const currentTooltipEntity = ref(null);
|
||||||
|
|
||||||
@ -419,45 +403,28 @@ const showMarkerTooltip = (viewer, entity, screenPosition, icon) => {
|
|||||||
// 构建 Tooltip 数据
|
// 构建 Tooltip 数据
|
||||||
let title = '';
|
let title = '';
|
||||||
const fields = [];
|
const fields = [];
|
||||||
const actions = [];
|
|
||||||
|
|
||||||
if (type === 'soldier') {
|
if (type === 'soldier') {
|
||||||
// 应急人员
|
title = '单兵信息';
|
||||||
title = '应急人员';
|
|
||||||
fields.push(
|
fields.push(
|
||||||
{ label: '姓名', value: properties.name?.getValue() || '-' },
|
{ label: '姓名', value: properties.name?.getValue() || '-' },
|
||||||
{ label: '部门', value: properties.department?.getValue() || '-' },
|
{ label: '部门', value: properties.department?.getValue() || '-' },
|
||||||
{ label: '位置信息', value: properties.location?.getValue() || '-' },
|
{ label: '位置', value: properties.location?.getValue() || '-' }
|
||||||
{ label: '预计到达时间', value: properties.estimatedArrival?.getValue() || '-' }
|
|
||||||
);
|
);
|
||||||
actions.push({
|
|
||||||
label: '联动',
|
|
||||||
type: 'link',
|
|
||||||
data: entity
|
|
||||||
});
|
|
||||||
} else if (type === 'device') {
|
} else if (type === 'device') {
|
||||||
// 应急装备
|
title = '设备信息';
|
||||||
title = '应急装备';
|
|
||||||
fields.push(
|
fields.push(
|
||||||
{ label: '设备名称', value: properties.name?.getValue() || '-' },
|
{ label: '设备名称', value: properties.name?.getValue() || '-' },
|
||||||
{ label: '设备类型', value: properties.deviceType?.getValue() || '-' },
|
{ label: '设备类型', value: properties.deviceType?.getValue() || '-' },
|
||||||
{ label: '位置信息', value: properties.location?.getValue() || '-' },
|
{ label: '位置', value: properties.location?.getValue() || '-' }
|
||||||
{ label: '预计到达时间', value: properties.estimatedArrival?.getValue() || '-' }
|
|
||||||
);
|
);
|
||||||
} else if (type === 'emergencyBase') {
|
} else if (type === 'emergencyBase') {
|
||||||
// 应急基地
|
|
||||||
title = '应急基地';
|
title = '应急基地';
|
||||||
fields.push(
|
fields.push(
|
||||||
{ label: '基地名称', value: properties.name?.getValue() || '-' },
|
{ label: '名称', value: properties.name?.getValue() || '-' },
|
||||||
{ label: '路线编号', value: properties.routeNumber?.getValue() || '-' },
|
{ label: '地址', value: properties.address?.getValue() || '-' },
|
||||||
{ label: '隶属单位', value: properties.department?.getValue() || '-' },
|
{ label: '距离', value: properties.distance?.getValue() || '-' }
|
||||||
{ label: '位置信息', value: properties.location?.getValue() || '-' }
|
|
||||||
);
|
);
|
||||||
actions.push({
|
|
||||||
label: '连线',
|
|
||||||
type: 'connect',
|
|
||||||
data: entity
|
|
||||||
});
|
|
||||||
} else if (type === 'station') {
|
} else if (type === 'station') {
|
||||||
const stationName = properties.name?.getValue() || '';
|
const stationName = properties.name?.getValue() || '';
|
||||||
const distance = properties.distance?.getValue() || 0;
|
const distance = properties.distance?.getValue() || 0;
|
||||||
@ -484,14 +451,17 @@ const showMarkerTooltip = (viewer, entity, screenPosition, icon) => {
|
|||||||
title = '储备中心';
|
title = '储备中心';
|
||||||
fields.push(
|
fields.push(
|
||||||
{ label: '名称', value: properties.name?.getValue() || '-' },
|
{ label: '名称', value: properties.name?.getValue() || '-' },
|
||||||
{ label: '位置信息', value: properties.location?.getValue() || '-' }
|
{ label: '区县', value: properties.district?.getValue() || '-' },
|
||||||
|
{ label: '人员数量', value: properties.personnelCount?.getValue() || '0' },
|
||||||
|
{ label: '占地面积', value: properties.area?.getValue() ? `${properties.area?.getValue()}㎡` : '-' }
|
||||||
);
|
);
|
||||||
} else if (type === 'presetPoint') {
|
} else if (type === 'presetPoint') {
|
||||||
// 预置点
|
// 预置点
|
||||||
title = '预置点';
|
title = '预置点';
|
||||||
fields.push(
|
fields.push(
|
||||||
{ label: '名称', value: properties.name?.getValue() || '-' },
|
{ label: '名称', value: properties.name?.getValue() || '-' },
|
||||||
{ label: '位置信息', value: properties.location?.getValue() || '-' }
|
{ label: '区县', value: properties.district?.getValue() || '-' },
|
||||||
|
{ label: '人员数量', value: properties.personnelCount?.getValue() || '0' }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,7 +471,7 @@ const showMarkerTooltip = (viewer, entity, screenPosition, icon) => {
|
|||||||
y: canvasPosition.y,
|
y: canvasPosition.y,
|
||||||
title,
|
title,
|
||||||
icon,
|
icon,
|
||||||
data: { fields, actions: actions.length > 0 ? actions : undefined }
|
data: { fields }
|
||||||
});
|
});
|
||||||
|
|
||||||
// 保存当前实体,用于相机移动时更新位置
|
// 保存当前实体,用于相机移动时更新位置
|
||||||
@ -584,17 +554,17 @@ onMounted(() => {
|
|||||||
// 在默认点附近添加10个模拟点位(应急人员和应急装备)
|
// 在默认点附近添加10个模拟点位(应急人员和应急装备)
|
||||||
const simulatedPoints = [
|
const simulatedPoints = [
|
||||||
// 应急人员 (6个)
|
// 应急人员 (6个)
|
||||||
{ type: 'soldier', name: '张三', department: '应急救援队', lon: 107.97, lat: 30.25, distance: 2.5, estimatedArrival: '10分钟', icon: soldierIcon },
|
{ type: 'soldier', name: '张三', department: '应急救援队', lon: 107.97, lat: 30.25, distance: 2.5, icon: soldierIcon },
|
||||||
{ type: 'soldier', name: '李四', department: '消防队', lon: 107.971901, lat: 30.251428, distance: 2.3, estimatedArrival: '8分钟', icon: soldierIcon },
|
{ type: 'soldier', name: '李四', department: '消防队', lon: 107.971901, lat: 30.251428, distance: 2.3, icon: soldierIcon },
|
||||||
{ type: 'soldier', name: '王五', department: '医疗队', lon: 107.974901, lat: 30.241428, distance: 3.1, estimatedArrival: '12分钟', icon: soldierIcon },
|
{ type: 'soldier', name: '王五', department: '医疗队', lon: 107.974901, lat: 30.241428, distance: 3.1, icon: soldierIcon },
|
||||||
{ type: 'soldier', name: '赵六', department: '应急救援队', lon: 108.047344, lat: 30.164313, distance: 4.2, estimatedArrival: '15分钟', icon: soldierIcon },
|
{ type: 'soldier', name: '赵六', department: '应急救援队', lon: 108.047344, lat: 30.164313, distance: 4.2, icon: soldierIcon },
|
||||||
{ type: 'soldier', name: '刘七', department: '消防队', lon: 108.046344, lat: 30.168313, distance: 3.8, estimatedArrival: '14分钟', icon: soldierIcon },
|
{ type: 'soldier', name: '刘七', department: '消防队', lon: 108.046344, lat: 30.168313, distance: 3.8, icon: soldierIcon },
|
||||||
{ type: 'soldier', name: '陈八', department: '医疗队', lon: 108.050344, lat: 30.170313, distance: 4.5, estimatedArrival: '16分钟', icon: soldierIcon },
|
{ type: 'soldier', name: '陈八', department: '医疗队', lon: 108.050344, lat: 30.170313, distance: 4.5, icon: soldierIcon },
|
||||||
// 应急装备 (4个)
|
// 应急装备 (4个)
|
||||||
{ type: 'device', name: '救援车辆A', deviceType: '消防车', lon: 107.98088, lat: 30.2487, distance: 2.8, estimatedArrival: '11分钟', icon: deviceIcon },
|
{ type: 'device', name: '救援车辆A', deviceType: '消防车', lon: 107.98088, lat: 30.2487, distance: 2.8, icon: deviceIcon },
|
||||||
{ type: 'device', name: '救援车辆B', deviceType: '救护车', lon: 107.97898, lat: 30.2502, distance: 2.6, estimatedArrival: '9分钟', icon: deviceIcon },
|
{ type: 'device', name: '救援车辆B', deviceType: '救护车', lon: 107.97898, lat: 30.2502, distance: 2.6, icon: deviceIcon },
|
||||||
{ type: 'device', name: '无人机A', deviceType: 'DJI', lon: 108.049344, lat: 30.160313, distance: 4.8, estimatedArrival: '17分钟', icon: deviceIcon },
|
{ type: 'device', name: '无人机A', deviceType: 'DJI', lon: 108.049344, lat: 30.160313, distance: 4.8, icon: deviceIcon },
|
||||||
{ type: 'device', name: '无人机B', deviceType: 'DJI', lon: 108.043344, lat: 30.169313, distance: 3.6, estimatedArrival: '13分钟', icon: deviceIcon }
|
{ type: 'device', name: '无人机B', deviceType: 'DJI', lon: 108.043344, lat: 30.169313, distance: 3.6, icon: deviceIcon }
|
||||||
];
|
];
|
||||||
|
|
||||||
simulatedPoints.forEach(point => {
|
simulatedPoints.forEach(point => {
|
||||||
@ -613,15 +583,13 @@ onMounted(() => {
|
|||||||
type: 'soldier',
|
type: 'soldier',
|
||||||
name: point.name,
|
name: point.name,
|
||||||
department: point.department,
|
department: point.department,
|
||||||
location: `目前为止距离现场${point.distance}公里`,
|
location: `目前为止距离现场${point.distance}公里`
|
||||||
estimatedArrival: point.estimatedArrival
|
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
type: 'device',
|
type: 'device',
|
||||||
name: point.name,
|
name: point.name,
|
||||||
deviceType: point.deviceType,
|
deviceType: point.deviceType,
|
||||||
location: `目前为止距离现场${point.distance}公里`,
|
location: `目前为止距离现场${point.distance}公里`
|
||||||
estimatedArrival: point.estimatedArrival
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -920,55 +888,6 @@ const handlePersonnelLink = (personnel) => {
|
|||||||
showPersonnelDetail.value = false;
|
showPersonnelDetail.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理 Tooltip 操作按钮点击事件
|
|
||||||
* @param {Object} action - 操作对象,包含 type 和 data
|
|
||||||
*/
|
|
||||||
const handleTooltipAction = (action) => {
|
|
||||||
console.log('[index.vue] Tooltip 操作按钮点击:', action);
|
|
||||||
|
|
||||||
if (action.type === 'link') {
|
|
||||||
// 应急人员的"联动"操作
|
|
||||||
const entity = action.data;
|
|
||||||
const properties = entity.properties;
|
|
||||||
|
|
||||||
console.log('[index.vue] 应急人员联动:', {
|
|
||||||
name: properties.name?.getValue(),
|
|
||||||
department: properties.department?.getValue()
|
|
||||||
});
|
|
||||||
|
|
||||||
// 可以在这里实现联动功能,比如:
|
|
||||||
// 1. 飞行到人员位置
|
|
||||||
// 2. 打开详情弹窗
|
|
||||||
// 3. 高亮显示相关信息
|
|
||||||
ElMessage.success(`已联动应急人员: ${properties.name?.getValue() || '未知'}`);
|
|
||||||
|
|
||||||
// 关闭 tooltip
|
|
||||||
hideTooltip();
|
|
||||||
currentTooltipEntity.value = null;
|
|
||||||
|
|
||||||
} else if (action.type === 'connect') {
|
|
||||||
// 应急基地的"连线"操作
|
|
||||||
const entity = action.data;
|
|
||||||
const properties = entity.properties;
|
|
||||||
|
|
||||||
console.log('[index.vue] 应急基地连线:', {
|
|
||||||
name: properties.name?.getValue(),
|
|
||||||
routeNumber: properties.routeNumber?.getValue()
|
|
||||||
});
|
|
||||||
|
|
||||||
// 可以在这里实现连线功能,比如:
|
|
||||||
// 1. 在地图上绘制线路
|
|
||||||
// 2. 显示路径规划
|
|
||||||
// 3. 计算距离和时间
|
|
||||||
ElMessage.success(`已连线应急基地: ${properties.name?.getValue() || '未知'}`);
|
|
||||||
|
|
||||||
// 关闭 tooltip
|
|
||||||
hideTooltip();
|
|
||||||
currentTooltipEntity.value = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关闭地图 Tooltip
|
* 关闭地图 Tooltip
|
||||||
* 统一的关闭入口,便于后续扩展埋点或联动逻辑
|
* 统一的关闭入口,便于后续扩展埋点或联动逻辑
|
||||||
@ -979,7 +898,7 @@ const handleMapTooltipClose = () => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理力量调度启动事件
|
* 处理力量调度启动事件
|
||||||
* 显示加载动画,3秒后自动隐藏,然后启动人员移动动画
|
* 显示加载动画,3秒后自动隐藏
|
||||||
*/
|
*/
|
||||||
const handleStartDispatch = (payload) => {
|
const handleStartDispatch = (payload) => {
|
||||||
console.log('[index.vue] 启动力量调度:', payload);
|
console.log('[index.vue] 启动力量调度:', payload);
|
||||||
@ -987,22 +906,9 @@ const handleStartDispatch = (payload) => {
|
|||||||
// 显示加载动画
|
// 显示加载动画
|
||||||
showLoading.value = true;
|
showLoading.value = true;
|
||||||
|
|
||||||
// 3秒后隐藏加载动画并启动人员移动
|
// 3秒后自动隐藏加载动画
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
showLoading.value = false;
|
showLoading.value = false;
|
||||||
|
|
||||||
// 启动应急人员沿路径移动动画
|
|
||||||
if (mapStore.viewer) {
|
|
||||||
console.log('[index.vue] 启动应急人员移动动画...');
|
|
||||||
startPersonnelMovement(mapStore.viewer, {
|
|
||||||
duration: 60, // 60秒完成整个路径,移动更清晰可见
|
|
||||||
trackEntity: false, // 不自动跟随相机(可设为 true 启用跟随)
|
|
||||||
personnelName: '应急救援人员',
|
|
||||||
department: '应急救援队'
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.warn('[index.vue] 地图viewer未就绪,无法启动动画');
|
|
||||||
}
|
|
||||||
}, 3000);
|
}, 3000);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1495,32 +1401,6 @@ const showMapTooltip = ({ x, y, title = "", icon = "", data = null }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tooltip 操作按钮样式
|
|
||||||
// 用于"连线"、"联动"等交互按钮
|
|
||||||
.tooltip-action-btn {
|
|
||||||
min-width: vw(100);
|
|
||||||
height: vh(36);
|
|
||||||
padding: 0 vw(24);
|
|
||||||
background: url('./assets/images/地图tooltip-button.png') no-repeat center/100% 100%;
|
|
||||||
border: none;
|
|
||||||
color: var(--text-white);
|
|
||||||
font-size: fs(14);
|
|
||||||
font-family: SourceHanSansCN-Medium, sans-serif;
|
|
||||||
font-weight: 500;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
filter: brightness(1.2);
|
|
||||||
transform: translateY(vh(-2));
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
filter: brightness(0.9);
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 窄容器嵌入的紧凑布局(<1100px 宽度)
|
// 窄容器嵌入的紧凑布局(<1100px 宽度)
|
||||||
.situational-awareness.is-compact {
|
.situational-awareness.is-compact {
|
||||||
--sa-left-width: calc(380 / 1920 * var(--cq-inline-100, 100vw));
|
--sa-left-width: calc(380 / 1920 * var(--cq-inline-100, 100vw));
|
||||||
|
|||||||