| | |
| | | import commandPost from '@/assets/images/dataCockpit/legend/command-post.png' |
| | | import { saveOperationLog } from '@ztzf/apis' |
| | | import { useRoute } from 'vue-router' |
| | | import { fwAreaDivideDetailApi, fwAreaDivideListApi } from '../partition/partitionApi' |
| | | import { buildEllipsePositions } from '@/utils/cesium/shapeTools' |
| | | import { AREA_TYPE_STYLE_MAP, BUFFER_LEVEL_STYLES, DEFAULT_AREA_STYLE } from '@ztzf/constants' |
| | | |
| | | // 初始化表单数据 |
| | | const initForm = () => ({ |
| | |
| | | const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' }) |
| | | const effectiveDateRange = ref([]) // 有效时间范围 |
| | | const sceneConfigList = ref([]) // 场景配置列表 |
| | | const areaList = ref([]) // ???? |
| | | const mapRef = ref(null) |
| | | const route = useRoute() |
| | | let viewer |
| | | let redPointEntity |
| | | let areaDisplaySource |
| | | let leftClickBound = false |
| | | |
| | | // 表单验证规则 |
| | |
| | | } |
| | | |
| | | // 获取场景配置列表 |
| | | // ????? |
| | | function ensureAreaDisplaySource() { |
| | | if (!viewer) return null |
| | | if (!areaDisplaySource) { |
| | | areaDisplaySource = new Cesium.CustomDataSource('scene-manage-area-display') |
| | | viewer.dataSources.add(areaDisplaySource) |
| | | } |
| | | return areaDisplaySource |
| | | } |
| | | |
| | | function clearAreaDisplay() { |
| | | if (!areaDisplaySource) return |
| | | areaDisplaySource.entities.removeAll() |
| | | } |
| | | |
| | | function normalizeShapePoint(point) { |
| | | if (!point) return null |
| | | const lng = point?.lng ?? point?.longitude |
| | | const lat = point?.lat ?? point?.latitude |
| | | const height = Number.isFinite(Number(point?.height)) ? Number(point.height) : 0 |
| | | if (!Number.isFinite(Number(lng)) || !Number.isFinite(Number(lat))) return null |
| | | return { lng: Number(lng), lat: Number(lat), height } |
| | | } |
| | | |
| | | function buildShapePositions(points = []) { |
| | | const normalized = points.map(normalizeShapePoint).filter(Boolean) |
| | | return normalized.map(point => Cesium.Cartesian3.fromDegrees(point.lng, point.lat, point.height)) |
| | | } |
| | | |
| | | function getShapeDisplayPoints(shape) { |
| | | if (Array.isArray(shape?.displayPoints) && shape.displayPoints.length) { |
| | | return shape.displayPoints |
| | | } |
| | | return shape?.points || [] |
| | | } |
| | | |
| | | function getAreaTypeStyle(areaType) { |
| | | return AREA_TYPE_STYLE_MAP?.[String(areaType)] || DEFAULT_AREA_STYLE |
| | | } |
| | | |
| | | function parseGeomJson(geomJson) { |
| | | if (!geomJson) return null |
| | | if (typeof geomJson === 'object') return geomJson |
| | | if (typeof geomJson !== 'string') return null |
| | | const trimmed = geomJson.trim() |
| | | if (!trimmed) return null |
| | | try { |
| | | return JSON.parse(trimmed) |
| | | } catch (error) { |
| | | return null |
| | | } |
| | | } |
| | | |
| | | function resolveAreaShapes(area) { |
| | | const extList = Array.isArray(area?.fwAreaDivideExtList) ? area.fwAreaDivideExtList : [] |
| | | if (!extList.length) return [] |
| | | return extList |
| | | .map((item, index) => { |
| | | const isShapePayload = item?.drawType || item?.points |
| | | if (isShapePayload) { |
| | | return { |
| | | id: item?.id || `shape_${Date.now()}_${index}_${Math.random().toString(16).slice(2, 6)}`, |
| | | drawType: item?.drawType ?? 'polygon', |
| | | areaType: item?.areaType ?? item?.areaTypeKey ?? '', |
| | | points: Array.isArray(item?.points) ? item.points : [], |
| | | displayPoints: Array.isArray(item?.displayPoints) ? item.displayPoints : null, |
| | | meta: item?.meta ?? null, |
| | | } |
| | | } |
| | | const parsed = parseGeomJson(item?.geomJson) |
| | | if (!parsed) return null |
| | | return { |
| | | id: parsed?.id || `shape_${Date.now()}_${index}_${Math.random().toString(16).slice(2, 6)}`, |
| | | drawType: parsed?.drawType ?? 'polygon', |
| | | areaType: item?.areaTypeKey ?? parsed?.areaType ?? '', |
| | | points: Array.isArray(parsed?.points) ? parsed.points : [], |
| | | displayPoints: Array.isArray(parsed?.displayPoints) ? parsed.displayPoints : null, |
| | | meta: parsed?.meta ?? null, |
| | | } |
| | | }) |
| | | .filter(Boolean) |
| | | } |
| | | |
| | | function renderSceneAreas() { |
| | | if (!visible.value) { |
| | | clearAreaDisplay() |
| | | return |
| | | } |
| | | if (!viewer) initMap() |
| | | const dataSource = ensureAreaDisplaySource() |
| | | if (!dataSource) return |
| | | dataSource.entities.removeAll() |
| | | areaList.value.forEach(area => { |
| | | const shapes = resolveAreaShapes(area) |
| | | shapes.forEach(shape => { |
| | | if (shape.drawType === 'buffer' && shape.meta?.bufferRadii?.length && shape.meta?.center) { |
| | | const center = shape.meta.center |
| | | const centerCartesian = Cesium.Cartesian3.fromDegrees(center.lng, center.lat, center.height || 0) |
| | | const radii = shape.meta.bufferRadii |
| | | .map(radius => Number(radius)) |
| | | .filter(radius => Number.isFinite(radius) && radius > 0) |
| | | radii.forEach((radius, index) => { |
| | | const style = BUFFER_LEVEL_STYLES[index] || BUFFER_LEVEL_STYLES[BUFFER_LEVEL_STYLES.length - 1] |
| | | dataSource.entities.add({ |
| | | name: 'scene-manage-area-display', |
| | | customData: { drawType: shape.drawType, level: index + 1 }, |
| | | position: centerCartesian, |
| | | ellipse: { |
| | | semiMajorAxis: radius, |
| | | semiMinorAxis: radius, |
| | | material: style.fill, |
| | | outline: false, |
| | | heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, |
| | | }, |
| | | }) |
| | | const positions = buildEllipsePositions(centerCartesian, radius, radius) |
| | | dataSource.entities.add({ |
| | | name: 'scene-manage-area-display', |
| | | customData: { drawType: shape.drawType, level: index + 1, outline: true }, |
| | | polyline: { |
| | | positions: positions.length ? [...positions, positions[0]] : positions, |
| | | clampToGround: true, |
| | | width: 2, |
| | | material: style.outline, |
| | | }, |
| | | }) |
| | | }) |
| | | return |
| | | } |
| | | const positions = buildShapePositions(getShapeDisplayPoints(shape)) |
| | | if (positions.length < 3) return |
| | | const style = getAreaTypeStyle(shape.areaType) |
| | | dataSource.entities.add({ |
| | | name: 'scene-manage-area-display', |
| | | customData: { drawType: shape.drawType }, |
| | | polygon: { |
| | | hierarchy: new Cesium.PolygonHierarchy(positions), |
| | | material: style.fill, |
| | | outline: false, |
| | | heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, |
| | | }, |
| | | polyline: { |
| | | positions: positions.length ? [...positions, positions[0]] : positions, |
| | | clampToGround: true, |
| | | width: 2, |
| | | material: style.outline, |
| | | }, |
| | | }) |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | async function ensureAreaExtList(rows) { |
| | | const missingRows = rows.filter(row => row?.id && !Array.isArray(row?.fwAreaDivideExtList)) |
| | | if (!missingRows.length) return rows |
| | | const detailList = await Promise.all( |
| | | missingRows.map(row => |
| | | fwAreaDivideDetailApi({ id: row.id }) |
| | | .then(res => res?.data?.data) |
| | | .catch(() => null) |
| | | ) |
| | | ) |
| | | const detailMap = new Map(detailList.filter(Boolean).map(item => [String(item.id), item])) |
| | | if (!detailMap.size) return rows |
| | | return rows.map(item => { |
| | | const detail = detailMap.get(String(item.id)) |
| | | return detail ? { ...item, ...detail } : item |
| | | }) |
| | | } |
| | | |
| | | // ?????????? |
| | | async function loadAreaList(sceneId) { |
| | | if (!sceneId) { |
| | | areaList.value = [] |
| | | clearAreaDisplay() |
| | | return |
| | | } |
| | | const res = await fwAreaDivideListApi({ sceneId }) |
| | | const list = res?.data?.data ?? [] |
| | | areaList.value = await ensureAreaExtList(list) |
| | | renderSceneAreas() |
| | | } |
| | | |
| | | async function getSceneConfigList() { |
| | | if (sceneConfigList.value.length) return |
| | | const res = await fwDefenseSceneListApi({unbound: 1}) |
| | |
| | | } |
| | | |
| | | // 地图左键点击事件 |
| | | function LeftClickEvent(click) { |
| | | async function LeftClickEvent(click) { |
| | | const pos = click.position |
| | | // 优先使用 pickPosition 获取贴地坐标(包含地形高度) |
| | | let cartesian3 = viewer.scene.pickPosition(pos) |
| | |
| | | formData.value.longitude = _.round(longitude, 6) |
| | | formData.value.latitude = _.round(latitude, 6) |
| | | setMapPoint(formData.value.longitude, formData.value.latitude) |
| | | await loadAreaList(formData.value.defenseSceneId) |
| | | } |
| | | |
| | | // 设置地图点位 |
| | |
| | | } |
| | | await nextTick() |
| | | setMapPoint(formData.value.longitude, formData.value.latitude) |
| | | await loadAreaList(formData.value.defenseSceneId) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getSceneConfigList() |
| | | }) |
| | | |
| | | watch( |
| | | () => formData.value.defenseSceneId, |
| | | sceneId => { |
| | | if (!visible.value) return |
| | | loadAreaList(sceneId) |
| | | } |
| | | ) |
| | | |
| | | watch( |
| | | () => visible.value, |
| | | val => { |
| | | if (!val) clearAreaDisplay() |
| | | } |
| | | ) |
| | | |
| | | |
| | | defineExpose({ |
| | | open, |
| | | }) |