| | |
| | | :terrain="true" |
| | | :layer-mode="4" |
| | | :boundary="false" |
| | | :zoom-to-boundary="dialogMode === 'add'" |
| | | /> |
| | | </div> |
| | | <div class="right-container"> |
| | |
| | | start-placeholder="开始时间" |
| | | end-placeholder="结束时间" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | :disabled-date="disablePastDate" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="指挥点位置" prop="longitude"> |
| | | <div class="val">{{ formatLocation(formData) }}</div> |
| | | <el-input class="command-input" :model-value="commandPointText" disabled placeholder="请选择指挥点位置"> |
| | | <template #suffix> |
| | | <span class="suffix-action">地图选点</span> |
| | | </template> |
| | | </el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div class="footer"> |
| | |
| | | import { saveOperationLog } from '@ztzf/apis' |
| | | import { useRoute } from 'vue-router' |
| | | import { fwAreaDivideDetailApi, fwAreaDivideListApi } from '../partition/partitionApi' |
| | | import { buildEllipsePositions } from '@/utils/cesium/shapeTools' |
| | | import { |
| | | addBufferRadiusLabelEntity, |
| | | buildEllipsePositions, |
| | | } from '@/utils/cesium/shapeTools' |
| | | import { cartesian3Convert } from '@/utils/cesium/mapUtil' |
| | | import { AREA_TYPE_STYLE_MAP, BUFFER_LEVEL_STYLES, DEFAULT_AREA_STYLE } from '@ztzf/constants' |
| | | import { MapTooltip } from '@ztzf/utils' |
| | | |
| | | // 初始化表单数据 |
| | | const initForm = () => ({ |
| | |
| | | defenseSceneId: '', // 关联场景配置ID |
| | | }) |
| | | |
| | | const commandPointText = computed(() => { |
| | | // 你也可以改成 `${lng}, ${lat}` |
| | | if (!formData.value.longitude) { |
| | | return '' |
| | | } |
| | | return `${formData.value.longitude}, ${formData.value.latitude}` |
| | | }) |
| | | |
| | | const emit = defineEmits(['success']) |
| | | const formRef = ref(null) // 表单实例 |
| | | const formData = ref(initForm()) // 表单数据 |
| | | const visible = defineModel() // 弹框显隐 |
| | | const dialogMode = ref('add') // 弹框模式 |
| | | const dialogMode = ref() // 弹框模式 |
| | | const submitting = ref(false) // 提交中 |
| | | const readonly = computed(() => dialogMode.value === 'view') |
| | | const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' }) |
| | |
| | | let redPointEntity |
| | | let areaDisplaySource |
| | | let leftClickBound = false |
| | | let mouseMoveBound = false |
| | | let mapTooltip |
| | | let publicCesium |
| | | const tooltipEventKey = 'scene-manage-point-tip' |
| | | const disablePastDate = date => { |
| | | if (!date) return false |
| | | const todayStart = new Date() |
| | | todayStart.setHours(0, 0, 0, 0) |
| | | return date.getTime() < todayStart.getTime() |
| | | } |
| | | |
| | | // 表单验证规则 |
| | | const rules = { |
| | |
| | | return normalized.map(point => Cesium.Cartesian3.fromDegrees(point.lng, point.lat, point.height)) |
| | | } |
| | | |
| | | function resolveLngLatPoint(point) { |
| | | if (!point) return null |
| | | const lng = point?.lng ?? point?.longitude |
| | | const lat = point?.lat ?? point?.latitude |
| | | if (Number.isFinite(Number(lng)) && Number.isFinite(Number(lat))) { |
| | | return { |
| | | lng: Number(lng), |
| | | lat: Number(lat), |
| | | height: Number.isFinite(Number(point?.height)) ? Number(point.height) : 0, |
| | | } |
| | | } |
| | | if (!viewer || !Number.isFinite(point?.x) || !Number.isFinite(point?.y) || !Number.isFinite(point?.z)) { |
| | | return null |
| | | } |
| | | const val = cartesian3Convert(point, viewer) |
| | | return { |
| | | lng: val.longitude, |
| | | lat: val.latitude, |
| | | height: Number.isFinite(val.height) ? val.height : 0, |
| | | } |
| | | } |
| | | |
| | | function getShapeDisplayPoints(shape) { |
| | | if (Array.isArray(shape?.displayPoints) && shape.displayPoints.length) { |
| | | return shape.displayPoints |
| | |
| | | .filter(Boolean) |
| | | } |
| | | |
| | | function pushRadiusBoundingPoints(positions, center, radius) { |
| | | if (!center || !Number.isFinite(Number(radius)) || radius <= 0) return |
| | | const resolved = resolveLngLatPoint(center) || {} |
| | | const lng = Number(resolved.lng ?? resolved.longitude) |
| | | const lat = Number(resolved.lat ?? resolved.latitude) |
| | | if (!Number.isFinite(lng) || !Number.isFinite(lat)) return |
| | | const baseLat = Cesium.Math.toRadians(lat) |
| | | const earthRadius = Cesium.Ellipsoid.WGS84.maximumRadius |
| | | const deltaLat = radius / earthRadius |
| | | const deltaLng = radius / (earthRadius * Math.max(Math.cos(baseLat), 0.000001)) |
| | | const points = [ |
| | | { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) + deltaLng), lat }, |
| | | { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) - deltaLng), lat }, |
| | | { lng, lat: Cesium.Math.toDegrees(baseLat + deltaLat) }, |
| | | { lng, lat: Cesium.Math.toDegrees(baseLat - deltaLat) }, |
| | | ] |
| | | points.forEach(point => { |
| | | positions.push(Cesium.Cartesian3.fromDegrees(point.lng, point.lat, resolved.height || 0)) |
| | | }) |
| | | } |
| | | |
| | | function collectSceneAreaPositions() { |
| | | const positions = [] |
| | | if (!viewer || !areaList.value.length) return positions |
| | | areaList.value.forEach(area => { |
| | | const shapes = resolveAreaShapes(area) |
| | | shapes.forEach(shape => { |
| | | const points = getShapeDisplayPoints(shape) |
| | | if (Array.isArray(points) && points.length) { |
| | | points.forEach(point => { |
| | | const resolved = resolveLngLatPoint(point) |
| | | const normalized = normalizeShapePoint(resolved) |
| | | if (!normalized) return |
| | | positions.push(Cesium.Cartesian3.fromDegrees(normalized.lng, normalized.lat, normalized.height)) |
| | | }) |
| | | } |
| | | if (shape?.drawType === 'buffer' && shape?.meta?.center && Array.isArray(shape?.meta?.bufferRadii)) { |
| | | const maxRadius = Math.max(...shape.meta.bufferRadii.filter(item => Number.isFinite(item))) |
| | | pushRadiusBoundingPoints(positions, shape.meta.center, maxRadius) |
| | | } |
| | | if (shape?.drawType === 'ellipse' && shape?.meta?.center) { |
| | | const major = Number(shape?.meta?.semiMajor) |
| | | const minor = Number(shape?.meta?.semiMinor) |
| | | const radius = Number.isFinite(major) && Number.isFinite(minor) ? Math.max(major, minor) : null |
| | | pushRadiusBoundingPoints(positions, shape.meta.center, radius) |
| | | } |
| | | }) |
| | | }) |
| | | return positions |
| | | } |
| | | |
| | | function fitViewToSceneAreas() { |
| | | if (!viewer || !areaList.value.length) return |
| | | const positions = collectSceneAreaPositions() |
| | | if (positions.length < 2) return |
| | | const boundingSphere = Cesium.BoundingSphere.fromPoints(positions) |
| | | const range = Math.max(boundingSphere.radius * 2.4, 200) |
| | | viewer.camera.flyToBoundingSphere(boundingSphere, { |
| | | duration: 0, |
| | | offset: new Cesium.HeadingPitchRange(viewer.camera.heading, Cesium.Math.toRadians(-90), range), |
| | | }) |
| | | } |
| | | |
| | | function renderSceneAreas() { |
| | | if (!visible.value) { |
| | | clearAreaDisplay() |
| | |
| | | width: 2, |
| | | material: style.outline, |
| | | }, |
| | | }) |
| | | addBufferRadiusLabelEntity(dataSource, centerCartesian, radius, { |
| | | name: 'scene-manage-area-display', |
| | | customData: { drawType: 'buffer', label: 'radius' }, |
| | | }) |
| | | }) |
| | | return |
| | |
| | | const list = res?.data?.data ?? [] |
| | | areaList.value = await ensureAreaExtList(list) |
| | | renderSceneAreas() |
| | | await nextTick() |
| | | fitViewToSceneAreas() |
| | | } |
| | | |
| | | async function getSceneConfigList() { |
| | | if (sceneConfigList.value.length) return |
| | | const res = await fwDefenseSceneListApi({unbound: 1}) |
| | | const params = { unbound: 1 } |
| | | if (dialogMode.value !== 'add' && formData.value?.id) { |
| | | params.sceneManageId = formData.value.id |
| | | } |
| | | const res = await fwDefenseSceneListApi(params) |
| | | sceneConfigList.value = res?.data?.data ?? [] |
| | | } |
| | | |
| | |
| | | |
| | | // 初始化地图实例 |
| | | function initMap() { |
| | | if (viewer) return |
| | | const map = mapRef.value?.getMap() |
| | | if (!map?.viewer) return |
| | | viewer = map.viewer |
| | | viewer ||= map.viewer |
| | | publicCesium ||= map.publicCesium |
| | | if (!readonly.value && !leftClickBound) { |
| | | map.publicCesium?.addLeftClickEvent?.(null, LeftClickEvent) |
| | | leftClickBound = true |
| | | } |
| | | if (!readonly.value) ensurePointTooltip() |
| | | } |
| | | |
| | | function ensurePointTooltip() { |
| | | if (!viewer || !publicCesium || mouseMoveBound) return |
| | | mapTooltip ||= new MapTooltip(viewer, { offset: { x: 28, y: 24 } }) |
| | | publicCesium.addMouseHandler?.( |
| | | null, |
| | | movement => { |
| | | if (!visible.value || readonly.value) return |
| | | mapTooltip?.show('点击地图选择指挥点位置', movement.endPosition) |
| | | }, |
| | | tooltipEventKey |
| | | ) |
| | | mouseMoveBound = true |
| | | } |
| | | |
| | | function clearPointTooltip() { |
| | | mapTooltip?.hide() |
| | | if (publicCesium && mouseMoveBound) { |
| | | publicCesium.removeMouseHandler?.(tooltipEventKey) |
| | | mouseMoveBound = false |
| | | } |
| | | } |
| | | |
| | |
| | | redPointEntity = null |
| | | await nextTick() |
| | | initMap() |
| | | if (dialogMode.value === 'view') { |
| | | clearPointTooltip() |
| | | } |
| | | await getSceneConfigList() |
| | | if (dialogMode.value !== 'add') { |
| | | if (dialogMode.value === 'add') { |
| | | mapRef.value?.zoomToAdminBoundary?.() |
| | | } else { |
| | | await loadDetail() |
| | | } |
| | | await nextTick() |
| | | setMapPoint(formData.value.longitude, formData.value.latitude) |
| | | await loadAreaList(formData.value.defenseSceneId) |
| | | fitViewToSceneAreas() |
| | | } |
| | | |
| | | onMounted(() => { |
| | | }) |
| | | onMounted(() => {}) |
| | | |
| | | watch( |
| | | () => formData.value.defenseSceneId, |
| | |
| | | watch( |
| | | () => visible.value, |
| | | val => { |
| | | if (!val) clearAreaDisplay() |
| | | if (!val) { |
| | | clearAreaDisplay() |
| | | clearPointTooltip() |
| | | } |
| | | } |
| | | ) |
| | | |
| | | |
| | | defineExpose({ |
| | | open, |