Compare commits
3 Commits
6123522fc9
...
06c462a577
| Author | SHA1 | Date | |
|---|---|---|---|
| 06c462a577 | |||
| 0d2b9b27d0 | |||
| dadfa1b1cc |
|
Before Width: | Height: | Size: 169 B After Width: | Height: | Size: 166 B |
|
Before Width: | Height: | Size: 140 B After Width: | Height: | Size: 143 B |
|
Before Width: | Height: | Size: 137 B After Width: | Height: | Size: 141 B |
|
Before Width: | Height: | Size: 133 B After Width: | Height: | Size: 136 B |
@ -94,6 +94,11 @@
|
||||
<div class="map-tooltip__content">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮区域 -->
|
||||
<div v-if="$slots.actions" class="map-tooltip__actions">
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -280,8 +285,8 @@ const handleClose = () => {
|
||||
*/
|
||||
.map-tooltip__corner {
|
||||
position: absolute;
|
||||
width: vw(20);
|
||||
height: vw(20); // 使用 vw 保持正方形
|
||||
// width: vw(20);
|
||||
// height: vw(20); // 使用 vw 保持正方形
|
||||
pointer-events: none; // 装饰元素不响应鼠标事件
|
||||
z-index: 1; // 确保显示在背景之上
|
||||
|
||||
@ -431,6 +436,19 @@ const handleClose = () => {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 淡入淡出动画
|
||||
* 结合缩放效果,提升视觉体验
|
||||
|
||||
@ -0,0 +1,261 @@
|
||||
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,6 +126,18 @@
|
||||
<span class="tooltip-field-value">{{ field.value }}</span>
|
||||
</div>
|
||||
</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>
|
||||
</div>
|
||||
|
||||
@ -172,6 +184,7 @@ import { useDualMapCompare } from "./composables/useDualMapCompare";
|
||||
import { useMapMarkers } from "./composables/useMapMarkers";
|
||||
import { use3DTiles } from "./composables/use3DTiles";
|
||||
import { useMapTooltip } from "./composables/useMapTooltip";
|
||||
import { useEntityAnimation } from "./composables/useEntityAnimation";
|
||||
import { useMapStore } from "@/map";
|
||||
import { request } from "@shared/utils/request";
|
||||
|
||||
@ -233,6 +246,9 @@ const {
|
||||
// 地图 Tooltip 功能
|
||||
const { tooltipState: mapTooltip, showTooltip, hideTooltip, updateTooltipPosition } = useMapTooltip();
|
||||
|
||||
// 实体动画功能
|
||||
const { startPersonnelMovement, stopPersonnelMovement, isAnimating } = useEntityAnimation();
|
||||
|
||||
// 当前显示 tooltip 的实体(用于相机移动时更新位置)
|
||||
const currentTooltipEntity = ref(null);
|
||||
|
||||
@ -403,28 +419,45 @@ const showMarkerTooltip = (viewer, entity, screenPosition, icon) => {
|
||||
// 构建 Tooltip 数据
|
||||
let title = '';
|
||||
const fields = [];
|
||||
const actions = [];
|
||||
|
||||
if (type === 'soldier') {
|
||||
title = '单兵信息';
|
||||
// 应急人员
|
||||
title = '应急人员';
|
||||
fields.push(
|
||||
{ label: '姓名', value: properties.name?.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') {
|
||||
title = '设备信息';
|
||||
// 应急装备
|
||||
title = '应急装备';
|
||||
fields.push(
|
||||
{ label: '设备名称', value: properties.name?.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') {
|
||||
// 应急基地
|
||||
title = '应急基地';
|
||||
fields.push(
|
||||
{ label: '名称', value: properties.name?.getValue() || '-' },
|
||||
{ label: '地址', value: properties.address?.getValue() || '-' },
|
||||
{ label: '距离', value: properties.distance?.getValue() || '-' }
|
||||
{ label: '基地名称', value: properties.name?.getValue() || '-' },
|
||||
{ label: '路线编号', value: properties.routeNumber?.getValue() || '-' },
|
||||
{ label: '隶属单位', value: properties.department?.getValue() || '-' },
|
||||
{ label: '位置信息', value: properties.location?.getValue() || '-' }
|
||||
);
|
||||
actions.push({
|
||||
label: '连线',
|
||||
type: 'connect',
|
||||
data: entity
|
||||
});
|
||||
} else if (type === 'station') {
|
||||
const stationName = properties.name?.getValue() || '';
|
||||
const distance = properties.distance?.getValue() || 0;
|
||||
@ -451,17 +484,14 @@ const showMarkerTooltip = (viewer, entity, screenPosition, icon) => {
|
||||
title = '储备中心';
|
||||
fields.push(
|
||||
{ label: '名称', value: properties.name?.getValue() || '-' },
|
||||
{ label: '区县', value: properties.district?.getValue() || '-' },
|
||||
{ label: '人员数量', value: properties.personnelCount?.getValue() || '0' },
|
||||
{ label: '占地面积', value: properties.area?.getValue() ? `${properties.area?.getValue()}㎡` : '-' }
|
||||
{ label: '位置信息', value: properties.location?.getValue() || '-' }
|
||||
);
|
||||
} else if (type === 'presetPoint') {
|
||||
// 预置点
|
||||
title = '预置点';
|
||||
fields.push(
|
||||
{ label: '名称', value: properties.name?.getValue() || '-' },
|
||||
{ label: '区县', value: properties.district?.getValue() || '-' },
|
||||
{ label: '人员数量', value: properties.personnelCount?.getValue() || '0' }
|
||||
{ label: '位置信息', value: properties.location?.getValue() || '-' }
|
||||
);
|
||||
}
|
||||
|
||||
@ -471,7 +501,7 @@ const showMarkerTooltip = (viewer, entity, screenPosition, icon) => {
|
||||
y: canvasPosition.y,
|
||||
title,
|
||||
icon,
|
||||
data: { fields }
|
||||
data: { fields, actions: actions.length > 0 ? actions : undefined }
|
||||
});
|
||||
|
||||
// 保存当前实体,用于相机移动时更新位置
|
||||
@ -554,17 +584,17 @@ onMounted(() => {
|
||||
// 在默认点附近添加10个模拟点位(应急人员和应急装备)
|
||||
const simulatedPoints = [
|
||||
// 应急人员 (6个)
|
||||
{ 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, 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, 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, icon: soldierIcon },
|
||||
{ type: 'soldier', name: '张三', department: '应急救援队', lon: 107.97, lat: 30.25, distance: 2.5, estimatedArrival: '10分钟', 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.974901, lat: 30.241428, distance: 3.1, estimatedArrival: '12分钟', 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.046344, lat: 30.168313, distance: 3.8, estimatedArrival: '14分钟', icon: soldierIcon },
|
||||
{ type: 'soldier', name: '陈八', department: '医疗队', lon: 108.050344, lat: 30.170313, distance: 4.5, estimatedArrival: '16分钟', icon: soldierIcon },
|
||||
// 应急装备 (4个)
|
||||
{ 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, 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, icon: deviceIcon }
|
||||
{ type: 'device', name: '救援车辆A', deviceType: '消防车', lon: 107.98088, lat: 30.2487, distance: 2.8, estimatedArrival: '11分钟', icon: deviceIcon },
|
||||
{ type: 'device', name: '救援车辆B', deviceType: '救护车', lon: 107.97898, lat: 30.2502, distance: 2.6, estimatedArrival: '9分钟', icon: deviceIcon },
|
||||
{ type: 'device', name: '无人机A', deviceType: 'DJI', lon: 108.049344, lat: 30.160313, distance: 4.8, estimatedArrival: '17分钟', icon: deviceIcon },
|
||||
{ type: 'device', name: '无人机B', deviceType: 'DJI', lon: 108.043344, lat: 30.169313, distance: 3.6, estimatedArrival: '13分钟', icon: deviceIcon }
|
||||
];
|
||||
|
||||
simulatedPoints.forEach(point => {
|
||||
@ -583,13 +613,15 @@ onMounted(() => {
|
||||
type: 'soldier',
|
||||
name: point.name,
|
||||
department: point.department,
|
||||
location: `目前为止距离现场${point.distance}公里`
|
||||
location: `目前为止距离现场${point.distance}公里`,
|
||||
estimatedArrival: point.estimatedArrival
|
||||
}
|
||||
: {
|
||||
type: 'device',
|
||||
name: point.name,
|
||||
deviceType: point.deviceType,
|
||||
location: `目前为止距离现场${point.distance}公里`
|
||||
location: `目前为止距离现场${point.distance}公里`,
|
||||
estimatedArrival: point.estimatedArrival
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -888,6 +920,55 @@ const handlePersonnelLink = (personnel) => {
|
||||
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
|
||||
* 统一的关闭入口,便于后续扩展埋点或联动逻辑
|
||||
@ -898,7 +979,7 @@ const handleMapTooltipClose = () => {
|
||||
|
||||
/**
|
||||
* 处理力量调度启动事件
|
||||
* 显示加载动画,3秒后自动隐藏
|
||||
* 显示加载动画,3秒后自动隐藏,然后启动人员移动动画
|
||||
*/
|
||||
const handleStartDispatch = (payload) => {
|
||||
console.log('[index.vue] 启动力量调度:', payload);
|
||||
@ -906,9 +987,22 @@ const handleStartDispatch = (payload) => {
|
||||
// 显示加载动画
|
||||
showLoading.value = true;
|
||||
|
||||
// 3秒后自动隐藏加载动画
|
||||
// 3秒后隐藏加载动画并启动人员移动
|
||||
setTimeout(() => {
|
||||
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);
|
||||
};
|
||||
|
||||
@ -1401,6 +1495,32 @@ 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 宽度)
|
||||
.situational-awareness.is-compact {
|
||||
--sa-left-width: calc(380 / 1920 * var(--cq-inline-100, 100vw));
|
||||
|
||||