创建半径线圆弧 并且在半径上带上距离文本
This commit is contained in:
parent
055b83404b
commit
33665263fb
@ -62,7 +62,7 @@
|
||||
<span class="force-dispatch__stat-label">应急物资</span>
|
||||
<span class="force-dispatch__stat-value">
|
||||
<!-- {{ dispatchSuggestion.supplies }} -->
|
||||
100
|
||||
190
|
||||
<span class="force-dispatch__stat-unit">件</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -6,46 +6,43 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
text: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: ''
|
||||
default: "",
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'primary',
|
||||
validator: (value) => ['primary', 'secondary', 'link'].includes(value)
|
||||
default: "primary",
|
||||
validator: (value) => ["primary", "secondary", "link"].includes(value),
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'medium',
|
||||
validator: (value) => ['small', 'medium', 'large'].includes(value)
|
||||
}
|
||||
})
|
||||
default: "medium",
|
||||
validator: (value) => ["small", "medium", "large"].includes(value),
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['click'])
|
||||
const emit = defineEmits(["click"]);
|
||||
|
||||
const buttonClass = computed(() => {
|
||||
return [
|
||||
`action-button--${props.type}`,
|
||||
`action-button--${props.size}`
|
||||
]
|
||||
})
|
||||
return [`action-button--${props.type}`, `action-button--${props.size}`];
|
||||
});
|
||||
|
||||
const handleClick = () => {
|
||||
emit('click')
|
||||
}
|
||||
emit("click");
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use '@/styles/mixins.scss' as *;
|
||||
@use '../../assets/styles/common.scss' as *;
|
||||
@use "@/styles/mixins.scss" as *;
|
||||
@use "../../assets/styles/common.scss" as *;
|
||||
|
||||
.action-button {
|
||||
@extend .btn-custom-bg; // 继承自定义背景按钮基类
|
||||
@ -58,13 +55,6 @@ const handleClick = () => {
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
background-image: none;
|
||||
background-color: #4FECFF;
|
||||
color: #062B5B;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
width: vw(16);
|
||||
height: vh(16);
|
||||
@ -76,13 +66,19 @@ const handleClick = () => {
|
||||
|
||||
// 类型样式
|
||||
&--primary {
|
||||
background-image: url('../../assets/images/文本按钮bg.png');
|
||||
background-image: url("../../assets/images/文本按钮bg.png");
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
color: var(--text-white);
|
||||
}
|
||||
|
||||
&--inModal{
|
||||
background: #4FECFF;
|
||||
color: #062B5B;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
&--secondary {
|
||||
background: var(--bg-panel);
|
||||
color: var(--text-white);
|
||||
|
||||
@ -371,8 +371,25 @@ export function useDualMapCompare() {
|
||||
const rightCamera = rightViewerInstance.camera
|
||||
|
||||
try {
|
||||
// 获取右侧相机位置
|
||||
const rightPosition = rightCamera.position.clone()
|
||||
|
||||
// 计算右侧相机的右方向向量
|
||||
const rightDirection = Cesium.Cartesian3.normalize(
|
||||
rightCamera.right,
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
|
||||
// 向左偏移90米
|
||||
const offsetPosition = Cesium.Cartesian3.add(
|
||||
rightPosition,
|
||||
Cesium.Cartesian3.multiplyByScalar(rightDirection, -90, new Cesium.Cartesian3()),
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
|
||||
// 设置左侧相机位置(向左偏移90米)
|
||||
leftViewer.value.camera.setView({
|
||||
destination: rightCamera.position.clone(),
|
||||
destination: offsetPosition,
|
||||
orientation: {
|
||||
heading: rightCamera.heading,
|
||||
pitch: rightCamera.pitch,
|
||||
@ -467,9 +484,25 @@ export function useDualMapCompare() {
|
||||
|
||||
// 强制同步相机位置(使用多种方式确保成功)
|
||||
try {
|
||||
// 获取右侧相机位置
|
||||
const rightPosition = rightCamera.position.clone()
|
||||
|
||||
// 计算右侧相机的右方向向量
|
||||
const rightDirection = Cesium.Cartesian3.normalize(
|
||||
rightCamera.right,
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
|
||||
// 向左偏移90米
|
||||
const offsetPosition = Cesium.Cartesian3.add(
|
||||
rightPosition,
|
||||
Cesium.Cartesian3.multiplyByScalar(rightDirection, -90, new Cesium.Cartesian3()),
|
||||
new Cesium.Cartesian3()
|
||||
)
|
||||
|
||||
// 方式1: 使用 setView (推荐)
|
||||
leftViewerInstance.camera.setView({
|
||||
destination: rightCamera.position.clone(),
|
||||
destination: offsetPosition,
|
||||
orientation: {
|
||||
heading: rightCamera.heading,
|
||||
pitch: rightCamera.pitch,
|
||||
@ -478,12 +511,11 @@ export function useDualMapCompare() {
|
||||
})
|
||||
|
||||
// 方式2: 直接设置相机属性 (备份方案)
|
||||
leftViewerInstance.camera.position = rightCamera.position.clone()
|
||||
leftViewerInstance.camera.position = offsetPosition.clone()
|
||||
leftViewerInstance.camera.direction = rightCamera.direction.clone()
|
||||
leftViewerInstance.camera.up = rightCamera.up.clone()
|
||||
leftViewerInstance.camera.right = rightCamera.right.clone()
|
||||
|
||||
console.log('[useDualMapCompare] 初始相机同步成功')
|
||||
console.log('[useDualMapCompare] 初始相机同步成功(向左偏移90米)')
|
||||
|
||||
// 验证同步结果
|
||||
const leftCamera = leftViewerInstance.camera
|
||||
|
||||
@ -2,29 +2,11 @@ import * as Cesium from 'cesium'
|
||||
import { ref } from 'vue'
|
||||
import { DISASTER_CENTER, RANGE_CIRCLE_STYLE } from '../constants'
|
||||
|
||||
/**
|
||||
* 范围圈管理
|
||||
*
|
||||
* 职责:
|
||||
* 1. 创建和更新范围圈实体
|
||||
* 2. 管理范围圈的样式和可见性
|
||||
* 3. 清理范围圈资源
|
||||
*
|
||||
* @returns {Object} 范围圈管理 API
|
||||
*/
|
||||
export function useRangeCircle() {
|
||||
// 范围圈实体引用
|
||||
const rangeCircleEntity = ref(null)
|
||||
const radiusLineEntity = ref(null) // 单独存储半径线实体
|
||||
const labelEntity = ref(null) // 单独存储标签实体
|
||||
|
||||
/**
|
||||
* 创建或更新范围圈
|
||||
* @param {Cesium.Viewer} viewer - Cesium viewer 实例
|
||||
* @param {number} radiusKm - 半径(公里)
|
||||
* @param {Object} options - 可选配置
|
||||
* @param {number} options.centerLon - 中心点经度(默认使用 DISASTER_CENTER)
|
||||
* @param {number} options.centerLat - 中心点纬度(默认使用 DISASTER_CENTER)
|
||||
* @param {Object} options.style - 样式配置(默认使用 RANGE_CIRCLE_STYLE)
|
||||
*/
|
||||
const createOrUpdateRangeCircle = (
|
||||
viewer,
|
||||
radiusKm,
|
||||
@ -43,30 +25,72 @@ export function useRangeCircle() {
|
||||
|
||||
const radiusMeters = radiusKm * 1000
|
||||
|
||||
// 如果已存在范围圈,先移除
|
||||
// 如果已存在范围圈,先检查半径和中心点是否相同
|
||||
if (rangeCircleEntity.value) {
|
||||
viewer.entities.remove(rangeCircleEntity.value)
|
||||
rangeCircleEntity.value = null
|
||||
// 获取现有范围圈的半径(从 semiMajorAxis 获取,单位是米,转换为公里)
|
||||
const existingRadiusKm = rangeCircleEntity.value.ellipse.semiMajorAxis / 1000
|
||||
|
||||
// 检查中心点是否相同
|
||||
const existingPosition = rangeCircleEntity.value.position.getValue()
|
||||
const newPosition = Cesium.Cartesian3.fromDegrees(centerLon, centerLat, 0)
|
||||
const centerChanged = !Cesium.Cartesian3.equals(existingPosition, newPosition)
|
||||
|
||||
// 如果半径和中心点都相同,则不需要更新
|
||||
if (existingRadiusKm === radiusKm && !centerChanged) {
|
||||
console.log(`[useRangeCircle] 范围圈半径相同 (${radiusKm}km),无需更新`)
|
||||
return
|
||||
}
|
||||
|
||||
// 创建新的范围圈
|
||||
// 移除现有实体
|
||||
// clearRangeCircle(viewer)
|
||||
}
|
||||
|
||||
// 创建中心点坐标
|
||||
const centerCartesian = Cesium.Cartesian3.fromDegrees(centerLon, centerLat)
|
||||
|
||||
// 计算半径线终点(正东方向)- 使用您之前正确的计算方法
|
||||
const ellipsoid = Cesium.Ellipsoid.WGS84
|
||||
const surfaceNormal = ellipsoid.geodeticSurfaceNormal(centerCartesian, new Cesium.Cartesian3())
|
||||
const tangent = Cesium.Cartesian3.cross(surfaceNormal, Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3())
|
||||
Cesium.Cartesian3.normalize(tangent, tangent)
|
||||
Cesium.Cartesian3.multiplyByScalar(tangent, radiusMeters, tangent)
|
||||
const radiusEndCartesian = Cesium.Cartesian3.add(centerCartesian, tangent, new Cesium.Cartesian3())
|
||||
|
||||
// 计算半径线中点
|
||||
const midpointCartesian = new Cesium.Cartesian3()
|
||||
Cesium.Cartesian3.lerp(centerCartesian, radiusEndCartesian, 0.5, midpointCartesian)
|
||||
|
||||
// 创建范围圈实体
|
||||
rangeCircleEntity.value = viewer.entities.add({
|
||||
position: Cesium.Cartesian3.fromDegrees(centerLon, centerLat, 0),
|
||||
position: centerCartesian,
|
||||
ellipse: {
|
||||
semiMinorAxis: radiusMeters,
|
||||
semiMajorAxis: radiusMeters,
|
||||
height: 0,
|
||||
material: Cesium.Color.fromCssColorString(style.fillColor).withAlpha(style.fillAlpha),
|
||||
outline: true,
|
||||
outlineColor: Cesium.Color.fromCssColorString(style.outlineColor).withAlpha(
|
||||
style.outlineAlpha
|
||||
),
|
||||
outlineColor: Cesium.Color.fromCssColorString(style.outlineColor).withAlpha(style.outlineAlpha),
|
||||
outlineWidth: style.outlineWidth,
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
},
|
||||
allowPicking: false
|
||||
})
|
||||
|
||||
// 创建半径线实体
|
||||
radiusLineEntity.value = viewer.entities.add({
|
||||
polyline: {
|
||||
positions: [centerCartesian, radiusEndCartesian],
|
||||
width: 2,
|
||||
material: Cesium.Color.fromCssColorString(style.outlineColor).withAlpha(0.8),
|
||||
},
|
||||
allowPicking: false
|
||||
})
|
||||
|
||||
// 创建标签实体(单独创建,位置设为半径线中点)
|
||||
labelEntity.value = viewer.entities.add({
|
||||
position: midpointCartesian,
|
||||
label: {
|
||||
text: `${radiusKm.toFixed(1) - 20} km`, // 实际是30 / 50 km 临时修改减少20km 后续调整
|
||||
font: 'bold 24px sans-serif',
|
||||
text: `${radiusKm - 20} km`,
|
||||
font: 'bold 16px sans-serif',
|
||||
fillColor: Cesium.Color.WHITE,
|
||||
outlineColor: Cesium.Color.BLACK,
|
||||
outlineWidth: 2,
|
||||
@ -75,47 +99,44 @@ export function useRangeCircle() {
|
||||
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
||||
pixelOffset: new Cesium.Cartesian2(0, 0),
|
||||
backgroundColor: Cesium.Color.fromCssColorString('#333').withAlpha(0.7),
|
||||
backgroundPadding: new Cesium.Cartesian2(18, 12),
|
||||
showBackground: true
|
||||
}
|
||||
backgroundPadding: new Cesium.Cartesian2(8, 6),
|
||||
showBackground: true,
|
||||
},
|
||||
allowPicking: false
|
||||
})
|
||||
|
||||
// 禁用范围圈的鼠标交互,让点击可以穿透到下面的标记点
|
||||
if (rangeCircleEntity.value) {
|
||||
rangeCircleEntity.value.allowPicking = false
|
||||
}
|
||||
|
||||
console.log(`[useRangeCircle] 已创建/更新范围圈: ${radiusKm}km`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示范围圈
|
||||
*/
|
||||
const showRangeCircle = () => {
|
||||
if (rangeCircleEntity.value) {
|
||||
rangeCircleEntity.value.show = true
|
||||
}
|
||||
if (rangeCircleEntity.value) rangeCircleEntity.value.show = true
|
||||
if (radiusLineEntity.value) radiusLineEntity.value.show = true
|
||||
if (labelEntity.value) labelEntity.value.show = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏范围圈
|
||||
*/
|
||||
const hideRangeCircle = () => {
|
||||
if (rangeCircleEntity.value) {
|
||||
rangeCircleEntity.value.show = false
|
||||
}
|
||||
if (rangeCircleEntity.value) rangeCircleEntity.value.show = false
|
||||
if (radiusLineEntity.value) radiusLineEntity.value.show = false
|
||||
if (labelEntity.value) labelEntity.value.show = false
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除范围圈
|
||||
* @param {Cesium.Viewer} viewer - Cesium viewer 实例
|
||||
*/
|
||||
const clearRangeCircle = (viewer) => {
|
||||
if (rangeCircleEntity.value && viewer) {
|
||||
if (!viewer) return
|
||||
|
||||
if (rangeCircleEntity.value) {
|
||||
viewer.entities.remove(rangeCircleEntity.value)
|
||||
rangeCircleEntity.value = null
|
||||
console.log('[useRangeCircle] 范围圈已清除')
|
||||
}
|
||||
if (radiusLineEntity.value) {
|
||||
viewer.entities.remove(radiusLineEntity.value)
|
||||
radiusLineEntity.value = null
|
||||
}
|
||||
if (labelEntity.value) {
|
||||
viewer.entities.remove(labelEntity.value)
|
||||
labelEntity.value = null
|
||||
}
|
||||
|
||||
console.log('[useRangeCircle] 范围圈已清除')
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@ -27,7 +27,7 @@ export const DEFAULT_CAMERA_CARTESIAN = {
|
||||
* 相机初始对准的地理位置
|
||||
*/
|
||||
export const DEFAULT_CAMERA_POSITION = {
|
||||
lon: 108.011134,
|
||||
lon: 108.011800,
|
||||
lat: 30.175697,
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ export const DEFAULT_CAMERA_POSITION = {
|
||||
*/
|
||||
export const DEFAULT_CAMERA_VIEW = {
|
||||
height: 274,
|
||||
heading: 0,
|
||||
heading: -20,
|
||||
pitch: -45,
|
||||
roll: 0,
|
||||
}
|
||||
|
||||
@ -48,7 +48,7 @@ export const VIDEO_MONITORS = [
|
||||
type: VIDEO_TYPES.VEHICLE_EXTERNAL,
|
||||
title: '设备操作视角',
|
||||
videoSrc:
|
||||
'http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/demo/ylzg/指挥车外部视角.mp4',
|
||||
'http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/demo/ylzg/设备视角.mp4',
|
||||
dateRange: '2025/9/1-2025/12/1',
|
||||
hasAudio: true,
|
||||
hasMegaphone: true,
|
||||
@ -60,7 +60,7 @@ export const VIDEO_MONITORS = [
|
||||
type: VIDEO_TYPES.VEHICLE_MEETING,
|
||||
title: '指挥会议室视角',
|
||||
videoSrc:
|
||||
'http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/demo/ylzg/指挥车会议视角.mp4',
|
||||
'http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/demo/ylzg/指挥会议室视角.mp4',
|
||||
dateRange: '2025/9/1-2025/12/1',
|
||||
hasAudio: true,
|
||||
hasMegaphone: true,
|
||||
|
||||
@ -207,7 +207,7 @@
|
||||
<template #footer>
|
||||
<ActionButton
|
||||
text="响应调度"
|
||||
type="primary"
|
||||
type="inModal"
|
||||
size="medium"
|
||||
@click="handleModalStartDispatch"
|
||||
/>
|
||||
@ -295,7 +295,6 @@ import otherEmergencyIcon from "./assets/images/其他应急点.png";
|
||||
import mediaIcon from "./assets/images/media.png";
|
||||
import collapseLeftArrow from "./assets/images/折叠面板左箭头.png";
|
||||
import collapseRightArrow from "./assets/images/折叠面板右箭头.png";
|
||||
|
||||
// ====================
|
||||
// 数据层
|
||||
// ====================
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user