吉安感知网项目-前端
shuishen
2026-01-26 dc89af73e55f7bda6018bace4813a4107b5248d4
feat:数据驾驶舱等页面调整
7 files modified
208 ■■■■ changed files
applications/drone-command/src/api/dataCockpit/index.js 5 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/components/map-container/device-map-container.vue 119 ●●●● patch | view | raw | blame | history
applications/drone-command/src/components/map-container/device-map-materials.js 40 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/LegendBar.vue 8 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/detectionRange/DetectionRangeDialog.vue 28 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/detectionRange/index.vue 6 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/api/dataCockpit/index.js
@@ -2,7 +2,7 @@
 * @Author       : yuan
 * @Date         : 2026-01-09 11:14:04
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-26 09:38:16
 * @LastEditTime : 2026-01-26 19:02:07
 * @FilePath     : \applications\drone-command\src\api\dataCockpit\index.js
 * @Description  : 
 * Copyright 2026 OBKoro1, All Rights Reserved. 
@@ -78,7 +78,8 @@
export const cockpitAggregationApi = (params) => {
    return request({
        url: '/drone-fw/device/fwDevice/statisticalDeviceOut',
        method: 'get'
        method: 'get',
        params
    })
}
applications/drone-command/src/components/map-container/device-map-container.vue
@@ -2,8 +2,9 @@
    <div class="map-shell">
        <CommonCesiumMap ref="mapRef" class="command-cesium map-container" :dom-id="props.containerId" :active="true"
            :flat-mode="false" :terrain="true" :layer-mode="4" :contour="false" :boundary="false"
            :show-admin-boundary="true" :zoom-to-boundary="true" :enable-stage-emit="true" :cluster-height="100000"
            @ready="handleMapReady" @stage-change="handleStageChange" />
            :show-admin-boundary="true" :zoom-to-boundary="true" :enable-stage-emit="true"
            :cluster-height="CLUSTER_HEIGHT" :detail-height="DETAIL_HEIGHT" @ready="handleMapReady"
            @stage-change="handleStageChange" />
        <div v-if="props.showLayerControl" class="layer-control-root" :class="{ collapsed: props.leftCollapsed }">
            <div class="layer-control-wrap" ref="layerWrapRef">
                <div class="layer-control" @click="toggleLayerPanel">
@@ -63,6 +64,9 @@
    createRadialGradientMaterial,
    getTexturedVertexFormat,
} from './device-map-materials'
const CLUSTER_HEIGHT = 100000
const DETAIL_HEIGHT = 10000
const props = defineProps({
    onlineDevices: {
@@ -157,6 +161,14 @@
    const latitude = Number(latitudeRaw)
    if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) return null
    return { longitude, latitude }
}
const getDeviceRange = item => {
    if (!item) return null
    const rawRange = item.effectiveRangeKm ?? item.range ?? item.coverRadiusM
    const range = Number(rawRange)
    if (!Number.isFinite(range) || range <= 0) return null
    return range
}
const ensureCockpitPrimitiveLayer = () => {
@@ -305,6 +317,20 @@
    if (aggregationSource) aggregationSource.show = visible
}
const setDroneVisibility = visible => {
    if (droneTrackBillboardCollection) droneTrackBillboardCollection.show = visible
    if (droneTrackPolylineCollection) droneTrackPolylineCollection.show = visible
    if (!visible && selectedTargetType.value === 'drone') {
        closePopup()
    }
}
const getStageByHeight = height => {
    if (height == null) return 'detail'
    if (height >= CLUSTER_HEIGHT) return 'cluster'
    if (height <= DETAIL_HEIGHT) return 'detail'
    return 'mid'
}
const ensureCountyCenterMap = () => {
    if (countyCenterMap.size) return
    const geojson = JSON.parse(jaGeojsonRaw)
@@ -316,24 +342,44 @@
    })
}
const buildSimulatedTrackPoints = base => {
    ensureCountyCenterMap()
    const anchor = base || { longitude: 116.397, latitude: 39.908 }
    const offsets = [
        [0, 0, 120],
        [0.02, 0.012, 150],
        [0.04, 0.006, 180],
        [0.06, -0.008, 200],
        [0.03, -0.02, 160],
        [0.0, -0.015, 140],
        [-0.02, -0.005, 120],
        [-0.01, 0.01, 130],
const buildSimulatedTrackPoints = () => {
    return [
        {
            longitude: 114.963191,
            latitude: 27.136716,
            height: 120
        },
        {
            longitude:114.957308,
            latitude: 27.138452,
            height: 120
        },
        {
            longitude:114.952,
            latitude: 27.136317,
            height: 120
        },
        {
            longitude:114.949293,
            latitude: 27.133864,
            height: 120
        },
        {
            longitude:114.944666,
            latitude: 27.130526,
            height: 120
        },
        {
            longitude:114.945909,
            latitude:27.127845,
            height: 120
        },
        {
            longitude:114.962974,
            latitude:27.136242,
            height: 120
        },
    ]
    return offsets.map(item => ({
        longitude: anchor.longitude + item[0],
        latitude: anchor.latitude + item[1],
        height: item[2],
    }))
}
@@ -422,13 +468,11 @@
const renderSimulatedDroneTrack = () => {
    if (!viewer) return
    clearDroneTrackEntities()
    ensureCountyCenterMap()
    ensureDroneTrackCollections()
    droneTrackBillboardCollection.show = detailVisible.value
    droneTrackPolylineCollection.show = detailVisible.value
    droneTrackRuntime = []
    const centers = Array.from(countyCenterMap.entries())
    const baseCenters = centers.length
        ? centers.slice(0, 4)
        : [['默认区域', { longitude: 116.397, latitude: 39.908 }]]
    const baseCenters = [['默认区域', { longitude:114.958541, latitude: 27.121917 }]]
    const segmentDuration = 6
    const baseTrackColor = Cesium.Color.fromCssColorString('red')
    baseCenters.forEach(([centerName, center], trackIndex) => {
@@ -454,7 +498,7 @@
        })
        const droneId = `drone-track-${trackIndex}`
        const billboard = droneTrackBillboardCollection.add({
            position: positions[0],
            position: positions[positions.length - 1],
            image: droneIcon,
            width: 36,
            height: 36,
@@ -511,7 +555,8 @@
        const position = getDevicePosition(item)
        if (!position) return
        const entityId = `online-device-${item.id ?? index}-${index}`
        addDeviceRings(position, ringFillInstancesByStyle, ringOutlineInstancesByStyle)
        const rangeMeters = getDeviceRange(item)
        addDeviceRings(position, ringFillInstancesByStyle, ringOutlineInstancesByStyle, rangeMeters)
        const billboard = deviceBillboardCollection.add({
            position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0),
            image: equipmentIcon,
@@ -528,9 +573,20 @@
    ringFillInstancesByStyle.forEach((instances, index) => {
        if (!instances.length) return
        const ring = RING_STYLES[index]
        const alpha = ring.alpha ?? 0.64
        const innerRatio =
            typeof ring.innerRatio === 'number'
                ? ring.innerRatio
                : ring.inner && ring.outer
                    ? Math.min(Math.max(ring.inner / ring.outer, 0), 0.9)
                    : 0
        const material = createRadialGradientMaterial(
            Cesium.Color.fromCssColorString(ring.gradient[0]).withAlpha(0.64),
            Cesium.Color.fromCssColorString(ring.gradient[1]).withAlpha(0.64)
            Cesium.Color.fromCssColorString(ring.gradient[0]).withAlpha(alpha),
            Cesium.Color.fromCssColorString(ring.gradient[1]).withAlpha(alpha),
            {
                gamma: 1.7,
                innerCutoff: innerRatio,
            }
        )
        const primitive = new Cesium.GroundPrimitive({
            geometryInstances: instances,
@@ -629,7 +685,7 @@
const renderDefenseZones = zones => {
    if (!viewer) return
    clearDefenseZoneEntities()
    const result = buildZonePrimitives(zones, '#19D266', '#2AEDBF', '#012B11')
    const result = buildZonePrimitives(zones, '#72F3FF', '#72F3FF', '#0A7C88')
    defenseZonePrimitive = result.primitive
    defenseZoneOutlinePrimitive = result.outlinePrimitive
    if (defenseZonePrimitive) defenseZonePrimitive.show = detailVisible.value
@@ -640,7 +696,7 @@
const renderPartitions = zones => {
    if (!viewer) return
    clearPartitionEntities()
    const result = buildZonePrimitives(zones, '#FFCD2A', '#FFC609', '#583300')
    const result = buildZonePrimitives(zones, '#FFD772', '#FFD772', '#A86B00')
    partitionPrimitive = result.primitive
    partitionOutlinePrimitive = result.outlinePrimitive
    if (partitionPrimitive) partitionPrimitive.show = detailVisible.value
@@ -855,13 +911,14 @@
    const showCluster = stage === 'cluster'
    setClusterVisibility(showCluster)
    setDetailVisibility(!showCluster)
    setDroneVisibility(!showCluster)
}
const handleMapReady = ({ viewer: mapViewer }) => {
    viewer = mapViewer
    ensureCockpitPrimitiveLayer()
    const height = viewer?.camera?.positionCartographic?.height
    const stage = height != null && height >= 100000 ? 'cluster' : 'detail'
    const stage = getStageByHeight(height)
    updateStageDisplay(stage)
    renderDeviceEntities(props.onlineDevices)
    loadDefenseZones()
applications/drone-command/src/components/map-container/device-map-materials.js
@@ -1,6 +1,15 @@
import * as Cesium from 'cesium'
export const RING_STYLES = [{ inner: 0, outer: 2000, gradient: ['#FF361C', '#360B00'] }]
export const RING_STYLES = [
    {
        inner: 0,
        outer: 2000,
        gradient: ['#FF6A57', '#A11806'],
        alpha: 0.42,
        outlineAlpha: 0.85,
        innerRatio: 0.12,
    },
]
export const MATERIAL_TYPE = 'RadialGradientMaterial'
export const DRONE_TRACK_MATERIAL_TYPE = 'DroneTrackFlowMaterial'
@@ -26,15 +35,25 @@
            uniforms: {
                color1: new Cesium.Color(1.0, 1.0, 1.0, 1.0),
                color2: new Cesium.Color(0.0, 0.0, 0.0, 1.0),
                gamma: 1.7,
                innerCutoff: 0.0,
            },
            source: `
                uniform vec4 color1;
                uniform vec4 color2;
                uniform float gamma;
                uniform float innerCutoff;
                czm_material czm_getMaterial(czm_materialInput materialInput) {
                    czm_material material = czm_getDefaultMaterial(materialInput);
                    vec2 st = materialInput.st - vec2(0.5);
                    float t = clamp(length(st) * 2.0, 0.0, 1.0);
                    vec4 color = mix(color1, color2, t);
                    float ramp = smoothstep(innerCutoff, 1.0, t);
                    float shaped = pow(t, gamma);
                    float alpha = color.a * ramp * shaped;
                    material.diffuse = color.rgb;
                    material.alpha = color.a;
                    material.alpha = alpha;
                    return material;
                }
            `,
@@ -82,9 +101,9 @@
    })
}
export const createRadialGradientMaterial = (color1, color2) => {
export const createRadialGradientMaterial = (color1, color2, options = {}) => {
    registerRadialGradientMaterial()
    return Cesium.Material.fromType(MATERIAL_TYPE, { color1, color2 })
    return Cesium.Material.fromType(MATERIAL_TYPE, { color1, color2, ...(options || {}) })
}
export const createDroneTrackMaterial = options => {
@@ -115,18 +134,19 @@
    return positions
}
export const addDeviceRings = (center, ringFillInstancesByStyle, ringOutlineInstancesByStyle) => {
export const addDeviceRings = (center, ringFillInstancesByStyle, ringOutlineInstancesByStyle, rangeMeters) => {
    if (!ringFillInstancesByStyle) return
    const centerPosition = Cesium.Cartesian3.fromDegrees(center.longitude, center.latitude, 0)
    const vertexFormat = getTexturedVertexFormat()
    RING_STYLES.forEach((ring, index) => {
        if (!ringFillInstancesByStyle[index]) return
        const outer = Number.isFinite(rangeMeters) && rangeMeters > 0 ? rangeMeters : ring.outer
        ringFillInstancesByStyle[index].push(
            new Cesium.GeometryInstance({
                geometry: new Cesium.EllipseGeometry({
                    center: centerPosition,
                    semiMajorAxis: ring.outer,
                    semiMinorAxis: ring.outer,
                    semiMajorAxis: outer,
                    semiMinorAxis: outer,
                    vertexFormat,
                }),
            })
@@ -135,7 +155,9 @@
    if (!ringOutlineInstancesByStyle) return
    RING_STYLES.forEach((ring, index) => {
        if (!ringOutlineInstancesByStyle[index]) return
        const positions = buildCirclePositions(center, ring.outer)
        const outer = Number.isFinite(rangeMeters) && rangeMeters > 0 ? rangeMeters : ring.outer
        const positions = buildCirclePositions(center, outer)
        const outlineAlpha = typeof ring.outlineAlpha === 'number' ? ring.outlineAlpha : 0.8
        ringOutlineInstancesByStyle[index].push(
            new Cesium.GeometryInstance({
                geometry: new Cesium.GroundPolylineGeometry({
@@ -144,7 +166,7 @@
                }),
                attributes: {
                    color: Cesium.ColorGeometryInstanceAttribute.fromColor(
                        Cesium.Color.fromCssColorString(ring.gradient[0]).withAlpha(0.8)
                        Cesium.Color.fromCssColorString(ring.gradient[0]).withAlpha(outlineAlpha)
                    ),
                },
            })
applications/drone-command/src/views/dataCockpit/components/LegendBar.vue
@@ -85,13 +85,13 @@
}
.legend-color.defense {
    background: radial-gradient(50% 50% at 50% 50%, #2aedbf 0%, #012b11 100%);
    border: 1px solid #19d266;
    background: radial-gradient(50% 50% at 50% 50%, rgba(114, 243, 255, 0.45) 0%, rgba(10, 124, 136, 0.55) 100%);
    border: 1px solid rgba(114, 243, 255, 0.85);
}
.legend-color.partition {
    background: radial-gradient(50% 50% at 50% 50%, #ffc609 0%, #583300 100%);
    border: 1px solid #ffcd2a;
    background: radial-gradient(50% 50% at 50% 50%, rgba(255, 215, 114, 0.5) 0%, rgba(168, 107, 0, 0.6) 100%);
    border: 1px solid rgba(255, 215, 114, 0.9);
}
.legend-icon {
applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue
@@ -67,7 +67,7 @@
                </div>
                <div class="col">
                    <span class="label">有效范围</span>
                    {{ data.effectiveRangeKm }}m
                    {{ Number(data.effectiveRangeKm).toFixed(0) }}m
                </div>
            </div>
        </div>
applications/drone-command/src/views/detectionCountermeasure/detectionRange/DetectionRangeDialog.vue
@@ -123,6 +123,7 @@
                            v-model="formData.effectiveRangeKm"
                            :min="0"
                            :precision="0"
                            :step="1"
                            :controls="false"
                            placeholder="请输入"
                        />
@@ -227,7 +228,14 @@
function formatRange(value) {
    if (value == null || value === '') return '-'
    return `${value}`
    const normalized = normalizeRange(value)
    return normalized == null ? '-' : `${normalized}`
}
function normalizeRange(value) {
    const numeric = Number(value)
    if (!Number.isFinite(numeric)) return null
    return Math.trunc(numeric)
}
// 关闭弹框
@@ -242,14 +250,16 @@
    submitting.value = true
    try {
        const {longitude, latitude} = parseDeployLocation(formData.value.deployLocation)
        const normalizedRange = normalizeRange(formData.value.effectiveRangeKm)
        const payload = {
            id: formData.value.id || formData.value.deviceId || undefined,
            effectiveRangeKmIsNotNull: formData.value.effectiveRangeKm ? 1 : 2,
            effectiveRangeKm: formData.value.effectiveRangeKm,
            effectiveRangeKmIsNotNull: normalizedRange != null ? 1 : 2,
            effectiveRangeKm: normalizedRange,
            longitude,
            latitude
        }
        formData.value.effectiveRangeKm = normalizedRange
        delete payload.deviceId
        await detectionRangeSubmitApi(payload)
@@ -280,6 +290,7 @@
        current: 1,
        size: 200,
        effectiveRangeKmIsNotNull: 2,
        isTrack: 1
    })
    const records = res?.data?.data?.records ?? []
    deviceOptions.value = records.map(item => ({
@@ -432,9 +443,18 @@
watch(
    () => formData.value.effectiveRangeKm,
    value => {
        const normalized = normalizeRange(value)
        if (value != null && normalized == null) {
            formData.value.effectiveRangeKm = null
            return
        }
        if (value != null && normalized !== value) {
            formData.value.effectiveRangeKm = normalized
            return
        }
        const parsed = parseDeployLocation(formData.value.deployLocation)
        if (!parsed) return
        updateRangeCircle(parsed.longitude, parsed.latitude, value)
        updateRangeCircle(parsed.longitude, parsed.latitude, normalized)
    }
)
</script>
applications/drone-command/src/views/detectionCountermeasure/detectionRange/index.vue
@@ -204,8 +204,10 @@
function formatRange(row) {
    if (!row) return '-'
    const value = row.effectiveRangeKm ?? row.range
    return value === null || value === undefined || value === '' ? '-' : value
    let value = row.effectiveRangeKm ?? row.range
    return value === null || value === undefined || value === '' ? '-' : Number(value).toFixed(0)
}
function formatDeviceCode(row) {