吉安感知网项目-前端
chenyao
2026-01-23 0be723f121c68eefafb93aba726edb7866edd0d1
Merge remote-tracking branch 'origin/master'
14 files modified
2 files added
1347 ■■■■■ changed files
applications/drone-command/src/components/map-container/device-map-container.vue 580 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/HistoryWarning.vue 6 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/permissionManage/operationLog/index.vue 5 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/recordManage/historyTracks/index.vue 2 ●●● patch | view | raw | blame | history
applications/task-work-order/env/.env 3 ●●●●● patch | view | raw | blame | history
applications/task-work-order/env/.env.development 8 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/App.vue 14 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/api/zkxt/index.js 9 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/axios.js 446 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/axiosXT.js 73 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/FormDiaLog.vue 13 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/index.vue 23 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue 137 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/orderManage/RefuseOrderDialog.vue 12 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/orderManage/index.vue 6 ●●●●● patch | view | raw | blame | history
applications/task-work-order/vite.config.mjs 10 ●●●● patch | view | raw | blame | history
applications/drone-command/src/components/map-container/device-map-container.vue
@@ -1,4 +1,4 @@
<template>
<template>
    <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"
@@ -93,6 +93,7 @@
import { cockpitAggregationApi } from '@/api/dataCockpit'
import layerControlIcon from '@/assets/images/dataCockpit/layerControl.png'
import equipmentIcon from '@/assets/images/dataCockpit/map/equipment.png'
import droneIcon from '@/assets/images/dataCockpit/map/drone.png'
import aggregationIcon from '@/assets/images/dataCockpit/map/aggregation.png'
import commandPostIcon from '@/assets/images/dataCockpit/map/command-post.png'
import popupClose from '@/assets/images/dataCockpit/map/popup-close.png'
@@ -121,19 +122,29 @@
const mapRef = ref(null)
let viewer = null
const deviceEntityIds = new Set()
const deviceEntityMap = new Map()
let defenseZoneSource = null
let partitionSource = null
let cockpitPrimitiveLayer = null
const devicePickMap = new Map()
let deviceBillboardCollection = null
let deviceRingOutlinePrimitives = []
let deviceRingFillPrimitives = []
let defenseZonePrimitive = null
let defenseZoneOutlinePrimitive = null
let partitionPrimitive = null
let partitionOutlinePrimitive = null
let aggregationSource = null
let commandPostSource = null
let commandPostBillboardCollection = null
let droneTrackBillboardCollection = null
let droneTrackPolylineCollection = null
let droneTrackTickHandler = null
let droneTrackStartTime = null
let droneTrackRuntime = []
const detailVisible = ref(true)
const clusterVisible = ref(false)
const countyCenterMap = new Map()
const showLayerPanel = ref(false)
const layerWrapRef = ref(null)
const selectedDevice = ref(null)
let selectedDeviceEntity = null
let selectedDeviceBillboard = null
let deviceClickHandler = null
let popupRenderHandler = null
const layerTreeProps = {
@@ -180,23 +191,101 @@
    return { longitude, latitude }
}
const ensureCockpitPrimitiveLayer = () => {
    if (!viewer) return
    if (!cockpitPrimitiveLayer) {
        cockpitPrimitiveLayer = new Cesium.PrimitiveCollection({ destroyPrimitives: false })
        viewer.scene.primitives.add(cockpitPrimitiveLayer)
    }
}
const addCockpitPrimitive = primitive => {
    if (!primitive) return
    if (cockpitPrimitiveLayer) {
        cockpitPrimitiveLayer.add(primitive)
        return
    }
    viewer?.scene?.primitives?.add(primitive)
}
const removeCockpitPrimitive = (primitive, destroy = true) => {
    if (!primitive) return
    if (cockpitPrimitiveLayer) {
        cockpitPrimitiveLayer.remove(primitive, destroy)
        return
    }
    viewer?.scene?.primitives?.remove(primitive)
}
const reorderCockpitPrimitives = () => {
    if (!viewer) return
    ensureCockpitPrimitiveLayer()
    cockpitPrimitiveLayer.removeAll(false)
    if (defenseZonePrimitive) cockpitPrimitiveLayer.add(defenseZonePrimitive)
    if (defenseZoneOutlinePrimitive) cockpitPrimitiveLayer.add(defenseZoneOutlinePrimitive)
    if (partitionPrimitive) cockpitPrimitiveLayer.add(partitionPrimitive)
    if (partitionOutlinePrimitive) cockpitPrimitiveLayer.add(partitionOutlinePrimitive)
    if (deviceRingFillPrimitives.length) {
        deviceRingFillPrimitives.forEach(primitive => cockpitPrimitiveLayer.add(primitive))
    }
    if (deviceRingOutlinePrimitives.length) {
        deviceRingOutlinePrimitives.forEach(primitive => cockpitPrimitiveLayer.add(primitive))
    }
    if (droneTrackPolylineCollection) cockpitPrimitiveLayer.add(droneTrackPolylineCollection)
    if (droneTrackBillboardCollection) cockpitPrimitiveLayer.add(droneTrackBillboardCollection)
    if (deviceBillboardCollection) cockpitPrimitiveLayer.add(deviceBillboardCollection)
    if (commandPostBillboardCollection) cockpitPrimitiveLayer.add(commandPostBillboardCollection)
}
const clearDeviceEntities = () => {
    if (!viewer) return
    deviceEntityIds.forEach(id => {
        viewer.entities.removeById(id)
    })
    deviceEntityIds.clear()
    deviceEntityMap.clear()
    if (deviceBillboardCollection) {
        deviceBillboardCollection.removeAll()
        removeCockpitPrimitive(deviceBillboardCollection)
        deviceBillboardCollection = null
    }
    if (deviceRingFillPrimitives.length) {
        deviceRingFillPrimitives.forEach(primitive => removeCockpitPrimitive(primitive))
        deviceRingFillPrimitives = []
    }
    if (deviceRingOutlinePrimitives.length) {
        deviceRingOutlinePrimitives.forEach(primitive => removeCockpitPrimitive(primitive))
        deviceRingOutlinePrimitives = []
    }
    devicePickMap.clear()
}
const ensureDeviceCollections = () => {
    if (!viewer) return
    ensureCockpitPrimitiveLayer()
    if (!deviceBillboardCollection) {
        deviceBillboardCollection = new Cesium.BillboardCollection()
        addCockpitPrimitive(deviceBillboardCollection)
    }
}
const clearDefenseZoneEntities = () => {
    if (!defenseZoneSource) return
    defenseZoneSource.entities.removeAll()
    if (!viewer) return
    if (defenseZonePrimitive) {
        removeCockpitPrimitive(defenseZonePrimitive)
        defenseZonePrimitive = null
    }
    if (defenseZoneOutlinePrimitive) {
        removeCockpitPrimitive(defenseZoneOutlinePrimitive)
        defenseZoneOutlinePrimitive = null
    }
}
const clearPartitionEntities = () => {
    if (!partitionSource) return
    partitionSource.entities.removeAll()
    if (!viewer) return
    if (partitionPrimitive) {
        removeCockpitPrimitive(partitionPrimitive)
        partitionPrimitive = null
    }
    if (partitionOutlinePrimitive) {
        removeCockpitPrimitive(partitionOutlinePrimitive)
        partitionOutlinePrimitive = null
    }
}
const clearAggregationEntities = () => {
@@ -205,20 +294,42 @@
}
const clearCommandPostEntities = () => {
    if (!commandPostSource) return
    commandPostSource.entities.removeAll()
    if (!viewer) return
    if (commandPostBillboardCollection) {
        commandPostBillboardCollection.removeAll()
        removeCockpitPrimitive(commandPostBillboardCollection)
        commandPostBillboardCollection = null
    }
}
const ensureCommandPostCollection = () => {
    if (!viewer) return
    ensureCockpitPrimitiveLayer()
    if (!commandPostBillboardCollection) {
        commandPostBillboardCollection = new Cesium.BillboardCollection()
        addCockpitPrimitive(commandPostBillboardCollection)
    }
}
const setDetailVisibility = visible => {
    detailVisible.value = visible
    if (defenseZoneSource) defenseZoneSource.show = visible
    if (partitionSource) partitionSource.show = visible
    if (commandPostSource) commandPostSource.show = visible
    if (defenseZonePrimitive) defenseZonePrimitive.show = visible
    if (defenseZoneOutlinePrimitive) defenseZoneOutlinePrimitive.show = visible
    if (partitionPrimitive) partitionPrimitive.show = visible
    if (partitionOutlinePrimitive) partitionOutlinePrimitive.show = visible
    if (commandPostBillboardCollection) commandPostBillboardCollection.show = visible
    if (deviceBillboardCollection) deviceBillboardCollection.show = visible
    if (deviceRingFillPrimitives.length) {
        deviceRingFillPrimitives.forEach(primitive => {
            primitive.show = visible
        })
    }
    if (deviceRingOutlinePrimitives.length) {
        deviceRingOutlinePrimitives.forEach(primitive => {
            primitive.show = visible
        })
    }
    if (!visible) closePopup()
    deviceEntityIds.forEach(id => {
        const entity = viewer?.entities?.getById(id)
        if (entity) entity.show = visible
    })
}
const setClusterVisibility = visible => {
@@ -237,12 +348,139 @@
    })
}
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],
    ]
    return offsets.map(item => ({
        longitude: anchor.longitude + item[0],
        latitude: anchor.latitude + item[1],
        height: item[2],
    }))
}
const clearDroneTrackEntities = () => {
    if (!viewer) return
    if (droneTrackBillboardCollection) {
        droneTrackBillboardCollection.removeAll()
        removeCockpitPrimitive(droneTrackBillboardCollection)
        droneTrackBillboardCollection = null
    }
    if (droneTrackPolylineCollection) {
        droneTrackPolylineCollection.removeAll()
        removeCockpitPrimitive(droneTrackPolylineCollection)
        droneTrackPolylineCollection = null
    }
    droneTrackRuntime = []
    stopDroneTrackAnimation()
}
const ensureDroneTrackCollections = () => {
    if (!viewer) return
    ensureCockpitPrimitiveLayer()
    if (!droneTrackBillboardCollection) {
        droneTrackBillboardCollection = new Cesium.BillboardCollection()
        addCockpitPrimitive(droneTrackBillboardCollection)
    }
    if (!droneTrackPolylineCollection) {
        droneTrackPolylineCollection = new Cesium.PolylineCollection()
        addCockpitPrimitive(droneTrackPolylineCollection)
    }
}
const startDroneTrackAnimation = () => {
    if (!viewer) return
    stopDroneTrackAnimation()
    droneTrackStartTime = Cesium.JulianDate.now()
    droneTrackTickHandler = clock => {
        if (!droneTrackRuntime.length) return
        const current = clock.currentTime
        const elapsed = Cesium.JulianDate.secondsDifference(current, droneTrackStartTime)
        droneTrackRuntime.forEach(track => {
            const duration = track.duration
            if (duration <= 0) return
            const t = ((elapsed % duration) + duration) % duration
            const seg = Math.min(track.positions.length - 2, Math.floor(t / track.segmentDuration))
            const ratio = (t - seg * track.segmentDuration) / track.segmentDuration
            const pos = Cesium.Cartesian3.lerp(
                track.positions[seg],
                track.positions[seg + 1],
                ratio,
                new Cesium.Cartesian3()
            )
            track.billboard.position = pos
        })
    }
    viewer.clock.onTick.addEventListener(droneTrackTickHandler)
    viewer.clock.shouldAnimate = true
}
const stopDroneTrackAnimation = () => {
    if (!viewer || !droneTrackTickHandler) return
    viewer.clock.onTick.removeEventListener(droneTrackTickHandler)
    droneTrackTickHandler = null
}
const renderSimulatedDroneTrack = () => {
    if (!viewer) return
    ensureCountyCenterMap()
    ensureDroneTrackCollections()
    droneTrackBillboardCollection.removeAll()
    droneTrackPolylineCollection.removeAll()
    droneTrackRuntime = []
    const centers = Array.from(countyCenterMap.values())
    const baseCenters = centers.length ? centers.slice(0, 4) : [{ longitude: 116.397, latitude: 39.908 }]
    const segmentDuration = 6
    baseCenters.forEach((center, trackIndex) => {
        const points = buildSimulatedTrackPoints(center)
        if (points.length < 2) return
        const positions = points.map(point =>
            Cesium.Cartesian3.fromDegrees(point.longitude, point.latitude, point.height)
        )
        droneTrackPolylineCollection.add({
            positions,
            width: 2,
            material: Cesium.Material.fromType('Color', {
                color: Cesium.Color.fromCssColorString('#2AEDBF'),
            }),
        })
        const billboard = droneTrackBillboardCollection.add({
            position: positions[0],
            image: droneIcon,
            width: 36,
            height: 36,
            verticalOrigin: Cesium.VerticalOrigin.CENTER,
        })
        droneTrackRuntime.push({
            positions,
            billboard,
            segmentDuration,
            duration: (points.length - 1) * segmentDuration,
        })
    })
    viewer.clock.multiplier = 1
    startDroneTrackAnimation()
    reorderCockpitPrimitives()
}
const RING_STYLES = [
    { inner: 0, outer: 2000, gradient: ['#FF361C', '#360B00'] }
]
const MATERIAL_TYPE = 'RadialGradientMaterial'
const TEXTURED_VERTEX_FORMAT =
    Cesium.MaterialAppearance?.MaterialSupport?.TEXTURED?.vertexFormat || Cesium.VertexFormat.POSITION_AND_ST
let materialRegistered = false
const registerRadialGradientMaterial = () => {
@@ -287,27 +525,39 @@
    return positions
}
const addDeviceRings = (center, baseId, visible) => {
const addDeviceRings = (center, ringFillInstancesByStyle, ringOutlineInstancesByStyle) => {
    if (!ringFillInstancesByStyle) return
    const centerPosition = Cesium.Cartesian3.fromDegrees(center.longitude, center.latitude, 0)
    RING_STYLES.forEach((ring, index) => {
        const outerPositions = buildCirclePositions(center, ring.outer)
        const holes = ring.inner
            ? [new Cesium.PolygonHierarchy(buildCirclePositions(center, ring.inner))]
            : []
        const entityId = `${baseId}-ring-${index}`
        deviceEntityIds.add(entityId)
        registerRadialGradientMaterial()
        const [startColor, endColor] = ring.gradient
        const color1 = Cesium.Color.fromCssColorString(startColor).withAlpha(0.34)
        const color2 = Cesium.Color.fromCssColorString(endColor).withAlpha(0.34)
        const material = new RadialGradientMaterialProperty(color1, color2)
        viewer.entities.add({
            id: entityId,
            show: visible,
            polygon: {
                hierarchy: new Cesium.PolygonHierarchy(outerPositions, holes),
                material,
            },
        })
        if (!ringFillInstancesByStyle[index]) return
        ringFillInstancesByStyle[index].push(
            new Cesium.GeometryInstance({
                geometry: new Cesium.EllipseGeometry({
                    center: centerPosition,
                    semiMajorAxis: ring.outer,
                    semiMinorAxis: ring.outer,
                    vertexFormat: TEXTURED_VERTEX_FORMAT,
                }),
            })
        )
    })
    if (!ringOutlineInstancesByStyle) return
    RING_STYLES.forEach((ring, index) => {
        if (!ringOutlineInstancesByStyle[index]) return
        const positions = buildCirclePositions(center, ring.outer)
        ringOutlineInstancesByStyle[index].push(
            new Cesium.GeometryInstance({
                geometry: new Cesium.GroundPolylineGeometry({
                    positions,
                    width: 2,
                }),
                attributes: {
                    color: Cesium.ColorGeometryInstanceAttribute.fromColor(
                        Cesium.Color.fromCssColorString(ring.gradient[0]).withAlpha(0.8)
                    ),
                },
            })
        )
    })
}
@@ -348,29 +598,69 @@
const renderDeviceEntities = devices => {
    if (!viewer) return
    clearDeviceEntities()
    ensureCockpitPrimitiveLayer()
    ensureDeviceCollections()
    deviceBillboardCollection.removeAll()
    if (deviceRingFillPrimitives.length) {
        deviceRingFillPrimitives.forEach(primitive => removeCockpitPrimitive(primitive))
        deviceRingFillPrimitives = []
    }
    if (deviceRingOutlinePrimitives.length) {
        deviceRingOutlinePrimitives.forEach(primitive => removeCockpitPrimitive(primitive))
        deviceRingOutlinePrimitives = []
    }
    devicePickMap.clear()
    deviceBillboardCollection.show = detailVisible.value
    const ringFillInstancesByStyle = RING_STYLES.map(() => [])
    const ringOutlineInstancesByStyle = RING_STYLES.map(() => [])
    devices.forEach((item, index) => {
        const position = getDevicePosition(item)
        if (!position) return
        const entityId = `online-device-${item.id ?? index}-${index}`
        deviceEntityIds.add(entityId)
        addDeviceRings(position, entityId, detailVisible.value)
        const entity = viewer.entities.add({
            id: entityId,
            show: detailVisible.value,
        addDeviceRings(position, ringFillInstancesByStyle, ringOutlineInstancesByStyle)
        const billboard = deviceBillboardCollection.add({
            position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0),
            billboard: {
                image: equipmentIcon,
                width: 40,
                height: 56,
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
            },
            image: equipmentIcon,
            width: 40,
            height: 56,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
        })
        deviceEntityMap.set(entityId, { data: item, entity })
        billboard.id = entityId
        devicePickMap.set(entityId, { data: item, billboard })
    })
    if (selectedDeviceEntity && !deviceEntityMap.has(selectedDeviceEntity.id)) {
    if (selectedDeviceBillboard && !devicePickMap.has(selectedDeviceBillboard.id)) {
        closePopup()
    }
    registerRadialGradientMaterial()
    ringFillInstancesByStyle.forEach((instances, index) => {
        if (!instances.length) return
        const ring = RING_STYLES[index]
        const material = Cesium.Material.fromType(MATERIAL_TYPE, {
            color1: Cesium.Color.fromCssColorString(ring.gradient[0]).withAlpha(0.64),
            color2: Cesium.Color.fromCssColorString(ring.gradient[1]).withAlpha(0.64),
        })
        const primitive = new Cesium.GroundPrimitive({
            geometryInstances: instances,
            appearance: new Cesium.MaterialAppearance({
                material,
                translucent: true,
            }),
        })
        primitive.show = detailVisible.value
        addCockpitPrimitive(primitive)
        deviceRingFillPrimitives.push(primitive)
    })
    ringOutlineInstancesByStyle.forEach(instances => {
        if (!instances.length) return
        const primitive = new Cesium.GroundPolylinePrimitive({
            geometryInstances: instances,
            appearance: new Cesium.PolylineColorAppearance(),
        })
        primitive.show = detailVisible.value
        addCockpitPrimitive(primitive)
        deviceRingOutlinePrimitives.push(primitive)
    })
    reorderCockpitPrimitives()
}
const getDefenseZonePositions = geom => {
@@ -385,70 +675,87 @@
    return list.map(item => Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude))
}
const renderZonePolygons = ({ zones, source, idPrefix, lineColor, fillGradient }) => {
    if (!viewer || !source) return
const buildZonePrimitives = (zones, lineColor, fillColor1, fillColor2) => {
    if (!viewer) return { primitive: null, outlinePrimitive: null }
    const polygonInstances = []
    const lineInstances = []
    registerRadialGradientMaterial()
    const borderColor = Cesium.Color.fromCssColorString(lineColor)
    const fillColorStart = Cesium.Color.fromCssColorString(fillGradient[0]).withAlpha(0.34)
    const fillColorEnd = Cesium.Color.fromCssColorString(fillGradient[1]).withAlpha(0.34)
    const fillMaterial = new RadialGradientMaterialProperty(fillColorStart, fillColorEnd)
    zones.forEach((zone, index) => {
    ; (zones || []).forEach((zone, index) => {
        if (!zone?.geom) return
        const positions = getDefenseZonePositions(zone.geom)
        if (!positions.length) return
        const entityId = `${idPrefix}-${zone.id ?? index}`
        const linePositions = positions.length > 1 ? [...positions, positions[0]] : positions
        source.entities.add({
            id: entityId,
            polygon: {
                hierarchy: new Cesium.PolygonHierarchy(positions),
                material: fillMaterial,
                outline: true,
                outlineColor: borderColor,
                heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
            },
            polyline: {
                positions: linePositions,
                clampToGround: true,
                width: 2,
                material: borderColor,
            },
        const polygon = new Cesium.PolygonGeometry({
            polygonHierarchy: new Cesium.PolygonHierarchy(positions),
            vertexFormat: TEXTURED_VERTEX_FORMAT,
        })
        polygonInstances.push(
            new Cesium.GeometryInstance({
                geometry: polygon,
            })
        )
        const linePositions = positions.length > 1 ? [...positions, positions[0]] : positions
        lineInstances.push(
            new Cesium.GeometryInstance({
                geometry: new Cesium.GroundPolylineGeometry({
                    positions: linePositions,
                    width: 2,
                }),
                attributes: {
                    color: Cesium.ColorGeometryInstanceAttribute.fromColor(
                        Cesium.Color.fromCssColorString(lineColor)
                    ),
                },
            })
        )
    })
    let primitive = null
    if (polygonInstances.length) {
        const baseColor1 = Cesium.Color.fromCssColorString(fillColor1)
        const baseColor2 = Cesium.Color.fromCssColorString(fillColor2)
        const material = Cesium.Material.fromType(MATERIAL_TYPE, {
            color1: baseColor1.withAlpha(0.64),
            color2: baseColor2.withAlpha(0.64),
        })
        primitive = new Cesium.GroundPrimitive({
            geometryInstances: polygonInstances,
            appearance: new Cesium.MaterialAppearance({
                material,
                translucent: true,
            }),
        })
        addCockpitPrimitive(primitive)
    }
    let outlinePrimitive = null
    if (lineInstances.length) {
        outlinePrimitive = new Cesium.GroundPolylinePrimitive({
            geometryInstances: lineInstances,
            appearance: new Cesium.PolylineColorAppearance(),
        })
        addCockpitPrimitive(outlinePrimitive)
    }
    return { primitive, outlinePrimitive }
}
const renderDefenseZones = zones => {
    if (!viewer) return
    if (!defenseZoneSource) {
        defenseZoneSource = new Cesium.CustomDataSource('defenseZoneSource')
        viewer.dataSources.add(defenseZoneSource)
    }
    clearDefenseZoneEntities()
    defenseZoneSource.show = detailVisible.value
    renderZonePolygons({
        zones,
        source: defenseZoneSource,
        idPrefix: 'defense-zone',
        lineColor: '#19D266',
        fillGradient: ['#2AEDBF', '#012B11'],
    })
    const result = buildZonePrimitives(zones, '#19D266', '#2AEDBF', '#012B11')
    defenseZonePrimitive = result.primitive
    defenseZoneOutlinePrimitive = result.outlinePrimitive
    if (defenseZonePrimitive) defenseZonePrimitive.show = detailVisible.value
    if (defenseZoneOutlinePrimitive) defenseZoneOutlinePrimitive.show = detailVisible.value
    reorderCockpitPrimitives()
}
const renderPartitions = zones => {
    if (!viewer) return
    if (!partitionSource) {
        partitionSource = new Cesium.CustomDataSource('partitionSource')
        viewer.dataSources.add(partitionSource)
    }
    clearPartitionEntities()
    partitionSource.show = detailVisible.value
    renderZonePolygons({
        zones,
        source: partitionSource,
        idPrefix: 'partition-zone',
        lineColor: '#FFCD2A',
        fillGradient: ['#FFC609', '#583300'],
    })
    const result = buildZonePrimitives(zones, '#FFCD2A', '#FFC609', '#583300')
    partitionPrimitive = result.primitive
    partitionOutlinePrimitive = result.outlinePrimitive
    if (partitionPrimitive) partitionPrimitive.show = detailVisible.value
    if (partitionOutlinePrimitive) partitionOutlinePrimitive.show = detailVisible.value
    reorderCockpitPrimitives()
}
const loadDefenseZones = async () => {
@@ -517,19 +824,18 @@
const getPickedDevice = picks => {
    for (const pick of picks) {
        const entityId = typeof pick?.id === 'string' ? pick.id : pick?.id?.id
        if (entityId && deviceEntityMap.has(entityId)) {
            return deviceEntityMap.get(entityId)
        const pickId = pick?.id
        const resolvedId = typeof pickId === 'string' ? pickId : pickId?.id
        if (resolvedId && devicePickMap.has(resolvedId)) {
            return devicePickMap.get(resolvedId)
        }
    }
    return null
}
const updatePopupPosition = () => {
    if (!viewer || !selectedDeviceEntity) return
    const cartesian =
        selectedDeviceEntity.position?.getValue?.(Cesium.JulianDate.now()) ||
        selectedDeviceEntity.position?._value
    if (!viewer || !selectedDeviceBillboard) return
    const cartesian = selectedDeviceBillboard.position
    if (!cartesian) return
    const screenPosition = viewer.scene.cartesianToCanvasCoordinates(cartesian)
    if (!screenPosition) return
@@ -558,7 +864,7 @@
        return
    }
    selectedDevice.value = pickedDevice.data
    selectedDeviceEntity = pickedDevice.entity
    selectedDeviceBillboard = pickedDevice.billboard
    startPopupRender()
}
@@ -576,7 +882,7 @@
function closePopup () {
    selectedDevice.value = null
    selectedDeviceEntity = null
    selectedDeviceBillboard = null
    stopPopupRender()
}
@@ -654,27 +960,21 @@
const renderCommandPosts = list => {
    if (!viewer) return
    if (!commandPostSource) {
        commandPostSource = new Cesium.CustomDataSource('commandPostSource')
        viewer.dataSources.add(commandPostSource)
    }
    clearCommandPostEntities()
    commandPostSource.show = detailVisible.value
        ; (list || []).forEach((item, index) => {
            const position = getDevicePosition(item)
            if (!position) return
            commandPostSource.entities.add({
                id: `command-post-${item.id ?? index}`,
                position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0),
                billboard: {
                    image: commandPostIcon,
                    width: 40,
                    height: 56,
                    verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
                },
            })
    ensureCommandPostCollection()
    commandPostBillboardCollection.removeAll()
    commandPostBillboardCollection.show = detailVisible.value
    ; (list || []).forEach((item, index) => {
        const position = getDevicePosition(item)
        if (!position) return
        commandPostBillboardCollection.add({
            position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0),
            image: commandPostIcon,
            width: 40,
            height: 56,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
        })
    })
    reorderCockpitPrimitives()
}
const loadAggregation = async () => {
@@ -724,6 +1024,7 @@
const handleMapReady = ({ viewer: mapViewer }) => {
    viewer = mapViewer
    ensureCockpitPrimitiveLayer()
    const height = viewer?.camera?.positionCartographic?.height
    const stage = height != null && height >= 100000 ? 'cluster' : 'detail'
    updateStageDisplay(stage)
@@ -732,6 +1033,7 @@
    loadPartitions()
    loadAggregation()
    loadCommandPosts()
    renderSimulatedDroneTrack()
    initDeviceClickHandler()
}
@@ -759,8 +1061,13 @@
    clearPartitionEntities()
    clearAggregationEntities()
    clearCommandPostEntities()
    clearDroneTrackEntities()
    closePopup()
    destroyDeviceClickHandler()
    if (cockpitPrimitiveLayer && viewer?.scene?.primitives) {
        viewer.scene.primitives.remove(cockpitPrimitiveLayer)
    }
    cockpitPrimitiveLayer = null
    viewer = null
})
</script>
@@ -1092,3 +1399,4 @@
    border-color: #a1a3d4;
}
</style>
applications/drone-command/src/views/dataCockpit/components/HistoryWarning.vue
@@ -9,7 +9,7 @@
        <div class="search-box">
            <!-- 关键字 -->
            <el-input v-model="query.keyword" class="command-input" placeholder="请输入数据名称" clearable
                @keyup.enter="onSearch">
                @keyup.enter="onSearch" @clear="onSearch">
            </el-input>
            <!-- 日期范围 -->
@@ -38,6 +38,7 @@
</template>
<script setup>
    import { dateRangeFormat } from '@ztzf/utils'
import { ref, computed, onMounted } from 'vue'
import HistoryTemplate from './templateComponents/HistoryTemplate.vue'
import { alarmLogApi } from '@/api/dataCockpit'
@@ -62,10 +63,11 @@
}
const fetchHistoryWarning = async () => {
    const startAt = Date.now()
    loading.value = true
    try {
        const [startTime, endTime] = query.value.dateRange || []
        const [startTime, endTime] = dateRangeFormat(query.value.dateRange) || []
        const keyword = (query.value.keyword || '').trim()
        const params = {
            current: 1,
applications/drone-command/src/views/permissionManage/operationLog/index.vue
@@ -40,7 +40,7 @@
                    range-separator="至"
                    start-placeholder="开始日期"
                    end-placeholder="结束日期"
                    value-format="YYYY-MM-DD HH:mm:ss"
                    value-format="YYYY-MM-DD"
                    @change="handleSearch"
                />
            </el-form-item>
@@ -107,10 +107,9 @@
]
async function getList() {
    const range = dateRangeFormat(dateRange.value)
    loading.value = true
    try {
        const params = { ...searchParams.value, startTime: range[0], endTime: range[1] }
        const params = { ...searchParams.value, startTime: dateRange.value[0], endTime: dateRange.value[1] }
        const res = await getOperationLogPage(params)
        list.value = res?.data?.data?.records ?? []
        total.value = res?.data?.data?.total ?? 0
applications/drone-command/src/views/recordManage/historyTracks/index.vue
@@ -162,7 +162,7 @@
        ...searchParams.value,
        start_time: range?.[0],
        end_time: range?.[1],
        id: ids.join(','),
        ids: ids.join(','),
    })
        .then(res => {
            blobDownload(res)
applications/task-work-order/env/.env
@@ -1,6 +1,9 @@
#接口地址
VITE_APP_API=/api
#接口地址-xt
VITE_APP_API_XT=/apiXT
#页面基础路径
VITE_APP_BASE=/task-work-order
applications/task-work-order/env/.env.development
@@ -4,10 +4,10 @@
 # @LastEditors  : yuan
 # @LastEditTime : 2026-01-16 11:02:24
 # @FilePath     : \applications\task-work-order\env\.env.development
 # @Description  :
 # Copyright 2026 OBKoro1, All Rights Reserved.
 # @Description  :
 # Copyright 2026 OBKoro1, All Rights Reserved.
 # 2026-01-07 14:58:30
###
###
NODE_ENV = 'development'
#开发环境配置
@@ -17,6 +17,8 @@
# VITE_APP_URL = https://wrj.shuixiongit.com/api
VITE_APP_URL= http://192.168.1.204
VITE_APP_URL_XT=http://218.202.104.82:8200
#新大屏地址
VITE_APP_DASHBOARD_URL = 'https://wrj.shuixiongit.com/command-center-dashboard/'
applications/task-work-order/src/App.vue
@@ -2,9 +2,19 @@
  <router-view />
</template>
<script setup>
import { loginWithMiniProgramApi } from '@/api/zkxt'
function getToken() {
    loginWithMiniProgramApi({username: 'admin'}).then(res => {
        console.log(res)
    })
}
getToken()
setInterval(() => {
    getToken()
}, 600000)
</script>
<style>
applications/task-work-order/src/api/zkxt/index.js
New file
@@ -0,0 +1,9 @@
import request from '@/axiosXT'
export const loginWithMiniProgramApi = (data) => {
    return request({
        url: `/auth/loginWithMiniProgram`,
        method: 'post',
        data,
    })
}
applications/task-work-order/src/axios.js
@@ -6,240 +6,224 @@
 * isToken是否需要token
 */
import axios from 'axios';
import store from '@/store/';
import router from '@/router/';
import { serialize } from '@/utils/util';
import { getToken, removeToken, removeRefreshToken } from '@/utils/auth';
import { isURL, validatenull } from '@/utils/validate';
import { ElMessage } from 'element-plus';
import website from '@/config/website';
import { Base64 } from 'js-base64';
import { baseUrl } from '@/config/env';
import crypto from '@/utils/crypto';
const adminUrl = import.meta.env.VITE_APP_DASHBOARD_URL;
import axios from 'axios'
import store from '@/store/'
import router from '@/router/'
import { serialize } from '@/utils/util'
import { getToken, removeToken, removeRefreshToken } from '@/utils/auth'
import { isURL, validatenull } from '@/utils/validate'
import { ElMessage } from 'element-plus'
import website from '@/config/website'
import { Base64 } from 'js-base64'
import { baseUrl } from '@/config/env'
import crypto from '@/utils/crypto'
const adminUrl = import.meta.env.VITE_APP_DASHBOARD_URL
// 全局未授权错误提示状态,只提示一次
let isErrorShown = false;
let isErrorShown = false
let _retry = false;
// 超时时间设置为10分钟,部分接口上传比较慢,如固件上传
axios.defaults.timeout = 600000;
//返回其他状态码
axios.defaults.validateStatus = function (status) {
  return status >= 200 && status <= 500; // 默认的
};
//跨域请求,允许保存cookie
axios.defaults.withCredentials = true;
let _retry = false
// 创建作用域的 axios 实例
const service = axios.create({
    // 超时时间设置为10分钟,部分接口上传比较慢,如固件上传
    timeout: 600000,
    //返回其他状态码
    validateStatus: function (status) {
        return status >= 200 && status <= 500 // 默认的
    },
    //跨域请求,允许保存cookie
    withCredentials: true,
})
//http request拦截
axios.interceptors.request.use(
  config => {
    // 初始化错误提示状态
    isErrorShown = false;
    //地址为已经配置状态则不添加前缀
    if (!isURL(config.url) && !config.url.startsWith(baseUrl)) {
      config.url = baseUrl + config.url;
    }
    //安全请求header
    config.headers['areaCode'] = config?.params?.areaCode || store?.state?.user?.userInfo?.detail?.areaCode;
    config.headers['Blade-Requested-With'] = 'BladeHttpRequest';
    //headers判断是否需要
    const authorization = config.authorization === false;
    if (!authorization) {
      config.headers['Authorization'] = `Basic ${Base64.encode(
        `${website.clientId}:${website.clientSecret}`
      )}`;
    }
    // 后端的要求
    if (router.currentRoute.value.path === '/job/jobstatistics'){
      const userAreaCode = store?.state?.user?.userInfo?.detail?.areaCode
      const paramsKey = config.method === 'get' ? 'params' : 'data';
      const codeKey = config.method === 'get' ? 'areaCode' : 'area_code';
      const paramCode = config?.params?.areaCode || config?.data?.area_code
      try {
        if (paramCode){
          if (paramCode === userAreaCode) {
            config[paramsKey][codeKey] = config.method === 'get' ? '' : undefined;
          } else {
            if (!Array.isArray(config[paramsKey])){
              config[paramsKey] = {...config[paramsKey],[codeKey]: paramCode}
            }
          }
        }
      }catch (e) {}
    }
service.interceptors.request.use(
    config => {
        // 初始化错误提示状态
        isErrorShown = false
        //地址为已经配置状态则不添加前缀
        if (!isURL(config.url) && !config.url.startsWith(baseUrl)) {
            config.url = baseUrl + config.url
        }
        //安全请求header
        config.headers['areaCode'] = config?.params?.areaCode || store?.state?.user?.userInfo?.detail?.areaCode
        config.headers['Blade-Requested-With'] = 'BladeHttpRequest'
        //headers判断是否需要
        const authorization = config.authorization === false
        if (!authorization) {
            config.headers['Authorization'] = `Basic ${Base64.encode(`${website.clientId}:${website.clientSecret}`)}`
        }
    //headers判断请求是否携带token
    const meta = config.meta || {};
    const isToken = meta.isToken === false;
    //headers传递token是否加密
    const cryptoToken = config.cryptoToken === true;
    //判断传递数据是否加密
    const cryptoData = config.cryptoData === true;
    const token = getToken();
    if (token && !isToken) {
      config.headers[website.tokenHeader] = cryptoToken
        ? 'crypto ' + crypto.encryptAES(token, crypto.cryptoKey)
        : 'bearer ' + token;
    }
    // 开启报文加密
    // if (cryptoData) {
    //   if (config.params) {
    //     const data = crypto.encryptAES(JSON.stringify(config.params), crypto.aesKey);
    //     config.params = { data };
    //   }
    //   if (config.data) {
    //     config.text = true;
    //     config.data = crypto.encryptAES(JSON.stringify(config.data), crypto.aesKey);
    //   }
    // }
    //headers中配置text请求
    if (config.text === true) {
      config.headers['Content-Type'] = 'text/plain';
    }
    //headers中配置serialize为true开启序列化
    if (config.method === 'post' && meta.isSerialize === true) {
      config.data = serialize(config.data);
    }
    return config;
  },
  error => {
    return Promise.reject(error);
  }
);
        //headers判断请求是否携带token
        const meta = config.meta || {}
        const isToken = meta.isToken === false
        //headers传递token是否加密
        const cryptoToken = config.cryptoToken === true
        //判断传递数据是否加密
        const cryptoData = config.cryptoData === true
        const token = getToken()
        if (token && !isToken) {
            config.headers[website.tokenHeader] = cryptoToken
                ? 'crypto ' + crypto.encryptAES(token, crypto.cryptoKey)
                : 'bearer ' + token
        }
        // 开启报文加密
        // if (cryptoData) {
        //   if (config.params) {
        //     const data = crypto.encryptAES(JSON.stringify(config.params), crypto.aesKey);
        //     config.params = { data };
        //   }
        //   if (config.data) {
        //     config.text = true;
        //     config.data = crypto.encryptAES(JSON.stringify(config.data), crypto.aesKey);
        //   }
        // }
        //headers中配置text请求
        if (config.text === true) {
            config.headers['Content-Type'] = 'text/plain'
        }
        //headers中配置serialize为true开启序列化
        if (config.method === 'post' && meta.isSerialize === true) {
            config.data = serialize(config.data)
        }
        return config
    },
    error => {
        return Promise.reject(error)
    }
)
//http response拦截
axios.interceptors.response.use(
  res => {
    const status = res.data.error_code || res.data.code || res.status;
    const statusWhiteList = website.statusWhiteList || [];
    const message = res.data.msg || res.data.error_description || res.data.message || '系统错误';
    const config = res.config;
    const cryptoData = config.cryptoData === true;
    if (status === 511) {
      ElMessage({
        message: '请联系运维人员上传密钥证书!',
        type: 'error',
      });
      // 获取当前路由名称
      const currentRouteName = router.currentRoute.value.name;
      // 如果当前路由不是登录页,则跳转到登录页
      if (currentRouteName !== '登录页') {
        store.dispatch('FedLogOut').then(() =>
          router.push({
            path: '/login',
          })
        );
      }
      return Promise.reject(new Error(message));
    }
    //如果在白名单里则自行catch逻辑处理
    if (statusWhiteList.includes(status)) return Promise.reject(res);
    // 如果是401并且没有重试过,尝试刷新token
    if (status === 401 && !_retry) {
      // 标记此请求已尝试刷新token
      _retry = true;
      // 调用RefreshToken action来刷新token
      return store
        .dispatch('RefreshToken')
        .then(() => {
          const meta = config.meta || {};
          const isToken = meta.isToken === false;
          const cryptoToken = config.cryptoToken === true;
          // 获取刷新后的token
          const token = getToken();
          if (token && !isToken) {
            config.headers[website.tokenHeader] = cryptoToken
              ? 'crypto ' + crypto.encryptAES(token, crypto.cryptoKey)
              : 'bearer ' + token;
          }
          // 重新发送原来的请求
          return axios(config);
        })
        .catch(() => {
          // 首次报错时提示
          if (!isErrorShown) {
            isErrorShown = true;
            ElMessage({
              message: message,
              type: 'error',
            });
          }
          // 清除token信息
          removeToken();
          removeRefreshToken();
          const env = import.meta.env.VITE_APP_ENV;
          // 重定向到登录页
          // store.dispatch('FedLogOut').then(() => router.push({
          //     path: '/login'
          // }));
          env === 'development'
            ? store.dispatch('FedLogOut').then(() =>
                router.push({
                  path: '/login',
                })
              )
            : store.dispatch('FedLogOut').then(() => window.location.replace(`${adminUrl}#/login`));
          return Promise.reject(new Error(message));
        });
    }
    // 如果是401并且已经重试过,直接跳转到登录页面
    if (status === 401 && _retry) {
      // 首次报错时提示
      if (!isErrorShown) {
        isErrorShown = true;
        ElMessage({
          message: '用户令牌过期,请重新登录',
          type: 'error',
        });
      }
      // 清除token信息
      removeToken();
      removeRefreshToken();
      // 重定向到登录页
      //   store.dispatch('FedLogOut').then(() => router.push({
      //       path: '/login'
      //   }));
      const env = import.meta.env.VITE_APP_ENV;
      env === 'development'
        ? store.dispatch('FedLogOut').then(() =>
            router.push({
              path: '/login',
            })
          )
        : store.dispatch('FedLogOut').then(() => window.location.replace(`${adminUrl}#/login`));
      return Promise.reject(new Error(message));
    }
    // 如果请求为oauth2错误码则首次报错时提示
    if (status > 2000 && !validatenull(res.data.error_description)) {
      // 首次报错时提示
      if (!isErrorShown) {
        isErrorShown = true;
        ElMessage({
          message: message,
          type: 'error',
        });
      }
      return Promise.reject(new Error(message));
    }
    // 如果请求为非200默认统一处理
    if (status !== 200 && status !== 0) {
      if (!isErrorShown) {
        ElMessage({
          message: message,
          type: 'error',
        });
      }
      return Promise.reject(new Error(message));
    }
    // 解析加密报文
    if (cryptoData) {
      res.data = JSON.parse(crypto.decryptAES(res.data, crypto.aesKey));
    }
    return res;
  },
  error => {
    return Promise.reject(new Error(error));
  }
);
window._request = axios
export default axios;
service.interceptors.response.use(
    res => {
        const status = res.data.error_code || res.data.code || res.status
        const statusWhiteList = website.statusWhiteList || []
        const message = res.data.msg || res.data.error_description || res.data.message || '系统错误'
        const config = res.config
        const cryptoData = config.cryptoData === true
        if (status === 511) {
            ElMessage({
                message: '请联系运维人员上传密钥证书!',
                type: 'error',
            })
            // 获取当前路由名称
            const currentRouteName = router.currentRoute.value.name
            // 如果当前路由不是登录页,则跳转到登录页
            if (currentRouteName !== '登录页') {
                store.dispatch('FedLogOut').then(() =>
                    router.push({
                        path: '/login',
                    })
                )
            }
            return Promise.reject(new Error(message))
        }
        //如果在白名单里则自行catch逻辑处理
        if (statusWhiteList.includes(status)) return Promise.reject(res)
        // 如果是401并且没有重试过,尝试刷新token
        if (status === 401 && !_retry) {
            // 标记此请求已尝试刷新token
            _retry = true
            // 调用RefreshToken action来刷新token
            return store
                .dispatch('RefreshToken')
                .then(() => {
                    const meta = config.meta || {}
                    const isToken = meta.isToken === false
                    const cryptoToken = config.cryptoToken === true
                    // 获取刷新后的token
                    const token = getToken()
                    if (token && !isToken) {
                        config.headers[website.tokenHeader] = cryptoToken
                            ? 'crypto ' + crypto.encryptAES(token, crypto.cryptoKey)
                            : 'bearer ' + token
                    }
                    // 重新发送原来的请求
                    return service(config)
                })
                .catch(() => {
                    // 首次报错时提示
                    if (!isErrorShown) {
                        isErrorShown = true
                        ElMessage({
                            message: message,
                            type: 'error',
                        })
                    }
                    // 清除token信息
                    removeToken()
                    removeRefreshToken()
                    const env = import.meta.env.VITE_APP_ENV
                    // 重定向到登录页
                    // store.dispatch('FedLogOut').then(() => router.push({
                    //     path: '/login'
                    // }));
                    env === 'development'
                        ? store.dispatch('FedLogOut').then(() =>
                                router.push({
                                    path: '/login',
                                })
                          )
                        : store.dispatch('FedLogOut').then(() => window.location.replace(`${adminUrl}#/login`))
                    return Promise.reject(new Error(message))
                })
        }
        // 如果是401并且已经重试过,直接跳转到登录页面
        if (status === 401 && _retry) {
            // 首次报错时提示
            if (!isErrorShown) {
                isErrorShown = true
                ElMessage({
                    message: '用户令牌过期,请重新登录',
                    type: 'error',
                })
            }
            // 清除token信息
            removeToken()
            removeRefreshToken()
            // 重定向到登录页
            //   store.dispatch('FedLogOut').then(() => router.push({
            //       path: '/login'
            //   }));
            const env = import.meta.env.VITE_APP_ENV
            env === 'development'
                ? store.dispatch('FedLogOut').then(() =>
                        router.push({
                            path: '/login',
                        })
                  )
                : store.dispatch('FedLogOut').then(() => window.location.replace(`${adminUrl}#/login`))
            return Promise.reject(new Error(message))
        }
        // 如果请求为oauth2错误码则首次报错时提示
        if (status > 2000 && !validatenull(res.data.error_description)) {
            // 首次报错时提示
            if (!isErrorShown) {
                isErrorShown = true
                ElMessage({
                    message: message,
                    type: 'error',
                })
            }
            return Promise.reject(new Error(message))
        }
        // 如果请求为非200默认统一处理
        if (status !== 200 && status !== 0) {
            if (!isErrorShown) {
                ElMessage({
                    message: message,
                    type: 'error',
                })
            }
            return Promise.reject(new Error(message))
        }
        // 解析加密报文
        if (cryptoData) {
            res.data = JSON.parse(crypto.decryptAES(res.data, crypto.aesKey))
        }
        return res
    },
    error => {
        return Promise.reject(new Error(error))
    }
)
window._request = service
export default service
applications/task-work-order/src/axiosXT.js
New file
@@ -0,0 +1,73 @@
import axios from 'axios'
import store from '@/store/'
import { serialize } from '@/utils/util'
import { getToken, removeToken, removeRefreshToken } from '@/utils/auth'
import { isURL, validatenull } from '@/utils/validate'
import { ElMessage } from 'element-plus'
import website from '@/config/website'
import { Base64 } from 'js-base64'
import { baseUrl } from '@/config/env'
import crypto from '@/utils/crypto'
// 全局未授权错误提示状态,只提示一次
const { VITE_APP_API_XT } = import.meta.env
// 创建作用域的 axios 实例
const serviceXT = axios.create({
    // 超时时间设置为10分钟,部分接口上传比较慢,如固件上传
    timeout: 600000,
    baseURL: VITE_APP_API_XT,
    //返回其他状态码
    validateStatus: function (status) {
        return status >= 200 && status <= 500 // 默认的
    },
    //跨域请求,允许保存cookie
    withCredentials: true,
})
//http request拦截
serviceXT.interceptors.request.use(
    config => {
        //headers判断是否需要
        const authorization = config.authorization === false
        if (!authorization) {
            config.headers['Authorization'] = `Basic ${Base64.encode(`${website.clientId}:${website.clientSecret}`)}`
        }
        const meta = config.meta || {}
        //headers中配置text请求
        if (config.text === true) {
            config.headers['Content-Type'] = 'text/plain'
        }
        //headers中配置serialize为true开启序列化
        if (config.method === 'post' && meta.isSerialize === true) {
            config.data = serialize(config.data)
        }
        return config
    },
    error => {
        return Promise.reject(error)
    }
)
//http response拦截
serviceXT.interceptors.response.use(
    res => {
        const status = res.data.error_code || res.data.code || res.status
        const statusWhiteList = website.statusWhiteList || []
        const message = res?.data?.msg || res?.data?.message || '系统错误'
        //如果在白名单里则自行catch逻辑处理
        if (statusWhiteList.includes(status)) return Promise.reject(res)
        // 如果请求为非200默认统一处理
        if (status !== 200 && status !== 0) {
            ElMessage({
                message: message,
                type: 'error',
            })
            return Promise.reject(new Error(message))
        }
        return res
    },
    error => {
        return Promise.reject(new Error(error))
    }
)
export default serviceXT
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/FormDiaLog.vue
@@ -256,19 +256,26 @@
// 获取工单列表
async function getWorkOrderList() {
    const res = await gdWorkOrderPageApi({ size: 999 })
    const res = await gdWorkOrderPageApi({ size: 999, })
    workOrderList.value = res?.data?.data?.records ?? []
}
// 获取飞手列表
async function getFlyerList() {
    const res = await gdFlyerPageApi({ size: 999 })
    const res = await gdFlyerPageApi({
        size: 999,
        // todo 需要传入前面的巡查类型
        // patrolTaskType:  '1',
    })
    flyerList.value = res?.data?.data?.records ?? []
}
// 获取设备列表
async function getDeviceList() {
    const res = await gdManageDeviceListApi()
    const res = await gdManageDeviceListApi({
        // TODO 需要传入ids设备
        // ids:
    })
    deviceList.value = res?.data?.data ?? []
}
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/index.vue
@@ -1,5 +1,9 @@
<template>
    <basic-container>
        <el-tabs class="gd-tabs" v-model="activeName" @tab-click="tabsClick">
            <el-tab-pane label="全部 " name="all"></el-tab-pane>
            <el-tab-pane label="我的任务工单" name="my"></el-tab-pane>
        </el-tabs>
        <el-form ref="queryParamsRef" :model="searchParams" class="gd-search-form">
            <el-form-item label="任务名称" prop="patrolTaskName">
                <el-input
@@ -68,9 +72,7 @@
        </el-form>
        <div class="gd-table-toolbar">
<!--            <el-button color="#F2F3F5" @click="requester = !requester">我是{{ requester ? '需求方' : '服务方' }}</el-button>-->
            <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增</el-button>
            <!--            <el-button :icon="Delete" color="#4C34FF" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>-->
            <el-button v-if="requester" :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增</el-button>
        </div>
        <div class="gd-table-container" v-loading="loading">
@@ -159,7 +161,7 @@
    deviceLoadDemand: [], // 设备负载需求
    taskStatus: [], // 巡查任务状态
})
const activeName = ref('all')
provide('dictObj', dictObj)
provide('requester', requester)
@@ -168,7 +170,12 @@
    const range = dateRangeFormat(dateRange.value)
    loading.value = true
    try {
        const res = await gdPatrolTaskPageApi({ ...searchParams.value, startTime: range[0], endTime: range[1] })
        const res = await gdPatrolTaskPageApi({
            ...searchParams.value,
            startTime: range[0],
            endTime: range[1],
            createUser: activeName.value === 'my' ? store.state.user.userInfo.user_id : '',
        })
        list.value = res?.data?.data?.records ?? []
        total.value = res?.data?.data?.total ?? 0
    } finally {
@@ -176,6 +183,12 @@
    }
}
function tabsClick(tab, event) {
    activeName.value = tab.props.name
    resetForm()
    handleSearch()
}
// 查询
function handleSearch() {
    searchParams.value.current = 1
applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue
@@ -179,48 +179,35 @@
        </div>
        <template #footer>
            <!--            <el-button color="#F2F3F5" @click="visible = false">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>-->
            <el-button
                class="save-btn"
                color="#4C34FF"
                v-if="(!dialogReadonly && !formData.id) || suddenlyEdit"
                :loading="submitting"
                :disabled="submitting"
                @click="handleSubmit"
            >
                发布
            </el-button>
            <template v-if="requester">
                <el-button
                    v-if="['11', '31'].includes(gdStatus) || suddenlyEdit || (!dialogReadonly && !formData.id)"
                    :loading="submitting"
                    :disabled="submitting"
                    @click="handleSubmit"
                >
                    {{ gdStatus === '11' ? '发布' : '提交修改' }}
                    发布
                </el-button>
                <el-button v-if="['11', '21', '23', '24', '25', '31', '60'].includes(gdStatus)" @click="viewDescription">
                    {{ gdStatusObj[gdStatus].reason }}
                </el-button>
                <el-button @click="addDescription" v-if="['20', '24', '25', '50'].includes(gdStatus)">
                    {{ gdStatus === '50' ? '结算' : '申请取消' }}
                </el-button>
                <el-button @click="requestModification" v-if="['20', '25'].includes(gdStatus)">申请修改</el-button>
            </template>
            <template v-else>
                <template v-if="requester">
                    <el-button
                        class="save-btn"
                        color="#4C34FF"
                        v-if="editStatus.includes(gdStatus)"
                        :loading="submitting"
                        :disabled="submitting"
                        @click="handleSubmit"
                    >
                        发布
                    </el-button>
                    <el-button color="#F2F3F5" v-if="gdStatus === '11'" @click="addDescription(2, 'view')">拒单原因</el-button>
                    <el-button color="#F2F3F5" @click="statusChange(3)" v-if="gdStatus === '20'">申请取消</el-button>
                    <el-button color="#4C34FF" @click="requestModification" v-if="gdStatus === '20'" class="save-btn">
                        申请修改
                    </el-button>
                </template>
                <template v-else>
                    <el-button color="#F2F3F5" v-if="gdStatus === '10'" @click="addDescription(2)">拒绝接单</el-button>
                    <el-button color="#4C34FF" v-if="gdStatus === '10'" @click="statusChange(1)" class="save-btn">接单</el-button>
                    <el-button color="#F2F3F5" v-if="gdStatus === '22'" @click="statusChange(7)">同意修改</el-button>
                    <el-button color="#4C34FF" v-if="gdStatus === '22'" @click="statusChange(8)" class="save-btn">
                        不同意修改
                    </el-button>
                    <el-button color="#F2F3F5" v-if="gdStatus === '21'" @click="statusChange(5)">同意取消</el-button>
                    <el-button color="#4C34FF" v-if="gdStatus === '21'" @click="statusChange(6)" class="save-btn">
                        不同意取消
                    </el-button>
                    <el-button color="#F2F3F5" v-if="gdStatus === '30'" @click="addDescription(9)">协商修改</el-button>
                </template>
                <el-button v-if="['21', '23'].includes(gdStatus)" @click="viewDescription">
                    {{ gdStatusObj[gdStatus].reason }}
                </el-button>
                <el-button v-if="gdStatus === '10'" @click="addDescription">拒绝接单</el-button>
                <el-button v-if="gdStatus === '10'" @click="statusChange(1)">接单</el-button>
                <el-button v-if="gdStatus === '22'" @click="statusChange(7)">同意修改</el-button>
                <el-button v-if="gdStatus === '22'" @click="statusChange(8)">不同意修改</el-button>
                <el-button v-if="gdStatus === '21'" @click="statusChange(5)">同意取消</el-button>
                <el-button v-if="gdStatus === '21'" @click="statusChange(6)">不同意取消</el-button>
                <el-button v-if="gdStatus === '30'" @click="addDescription">协商修改</el-button>
            </template>
        </template>
@@ -286,11 +273,27 @@
let drawPolygonExample
let pointList = ref([])
const requester = inject('requester')
const editStatus = inject('editStatus')
const suddenlyEdit = ref(false)
const processList = ref([])
let viewPlane
const hasPatrolTaskList = computed(() => ['30', '40', '50', '60'].includes(String(formData.value.workOrderStatus)))
const gdStatusObj = {
    '0': { reason: '' },
    '10': { reason: '拒绝原因', operationType: '2' },
    '11': { reason: '拒绝原因' },
    '20': { reason: '取消原因', operationType: '3' },
    '21': { reason: '取消原因' },
    '22': { reason: '驳回原因' },
    '23': { reason: '取消原因' },
    '24': { reason: '驳回原因', operationType: '3' },
    '25': { reason: '驳回原因', operationType: '3' },
    '30': { reason: '修改原因', operationType: '9' },
    '31': { reason: '修改原因' },
    '40': { reason: '' },
    '50': { reason: '情况说明', operationType: '10' },
    '60': { reason: '情况说明' },
}
// 工单状态:0草稿、10发布中_接单中、11发布中_拒绝接单、20响应中_待拆分、21响应中_申请取消、22响应中_申请修改、23响应中_已取消、
// 24响应中_拒绝取消、25响应中_拒绝修改 30执行中_待全部完成、31执行中_协商修改、40完成待验_待全部验收、50验收通过_待结算、60结算完成_已结算
@@ -302,9 +305,13 @@
    executeStartTime: fieldRules(true),
    deviceLoadDemand: fieldRules(true),
}
// 操作类型:1接单,2拒接接单,3申请取消,4申请修改, 5同意取消 6不同意取消 7.同意修改 8.不同意修改 9.协商修改
function statusChange(operationType) {
    gdWorkOrderHandleStatusApi({ operationType: operationType, workOrderId: formData.value.id }).then(res => {
    gdWorkOrderHandleStatusApi({
        operationType: operationType,
        workOrderId: formData.value.id,
    }).then(res => {
        visible.value = false
        emit('success')
    })
@@ -326,24 +333,24 @@
}
// 填写说明
function addDescription(type, mode = 'add') {
    let formLabel = ''
    switch (type) {
        case 2:
            formLabel = '拒绝原因'
            break
        case 9:
            formLabel = '修改原因'
            break
    }
function addDescription() {
    rejectVisible.value = true
    nextTick(() => {
        refuseOrderDialogRef.value.open({
            mode,
            mode: 'add',
            row: formData.value,
            type,
            formLabel,
            type: gdStatusObj[gdStatus.value].operationType,
            formLabel: gdStatusObj[gdStatus.value].reason,
        })
    })
}
function viewDescription() {
    rejectVisible.value = true
    refuseOrderDialogRef.value.open({
        mode: 'view',
        row: formData.value,
        formLabel: gdStatusObj[gdStatus.value].reason,
    })
}
@@ -365,6 +372,7 @@
function loadDemandChange() {
    formData.value.recommendDeviceIds = ''
    selectedDevices.value = []
    getDeviceList()
    syncSelection()
}
@@ -380,8 +388,10 @@
    const isValid = await formRef.value?.validate().catch(() => false)
    if (!isValid) return
    if (!pointList.value.length) {
        ElMessage.warning('请绘制范围')
        return
        return ElMessage.warning('请绘制范围')
    }
    if (!formData.value.recommendDeviceIds) {
        return ElMessage.warning('请选择设备')
    }
    submitting.value = true
    try {
@@ -436,6 +446,8 @@
        const val = cartesian3Convert(item, viewer)
        return { ...val, lng: val.longitude, lat: val.latitude }
    })
    formData.value.recommendDeviceIds = ''
    selectedDevices.value = []
    await getDeviceList()
    syncSelection()
}
@@ -502,10 +514,11 @@
}
import droneIcon from '@/assets/images/orderView/orderManage/drone.png'
function renderingDevice(list) {
   // 渲染红点
    // 渲染红点
    list.forEach(item => {
        const position = Cesium.Cartesian3.fromDegrees(item.longitude || 115.00, item.latitude || 27.11)
        const position = Cesium.Cartesian3.fromDegrees(item.longitude || 115.0, item.latitude || 27.11)
        viewer.entities.add({
            position: position,
            billboard: {
@@ -554,16 +567,19 @@
.content {
    display: flex;
    gap: 0 20px;
    .leftBox {
        width: 0;
        flex: 1;
        display: flex;
        flex-direction: column;
        .gd-cesium {
            width: 100%;
            height: 423px;
        }
    }
    .rightBox {
        .title {
            font-weight: 500;
@@ -571,6 +587,7 @@
            color: #272e37;
        }
    }
    .processBox {
        width: 312px;
    }
applications/task-work-order/src/views/orderView/orderManage/orderManage/RefuseOrderDialog.vue
@@ -100,11 +100,19 @@
}
let formLabelStr = ref('原因')
// 打开弹框
// 操作类型:1接单,2拒接接单,3申请取消,4申请修改, 5同意取消 6不同意取消 7.同意修改 8.不同意修改 9.协商修改
async function open({ mode = 'add', row, formLabel, type } = {}) {
    dialogMode.value = mode
    formLabelStr.value = formLabel
    formData.value = { workOrderId: row.id, operationType: type, rejectReason: row.rejectReason }
    formData.value = {
        workOrderId: row.id,
        operationType: type,
    }
    if (dialogMode.value !== 'add'){
        formData.value.rejectReason = row.rejectReason
    }
}
defineExpose({ open })
applications/task-work-order/src/views/orderView/orderManage/orderManage/index.vue
@@ -107,8 +107,8 @@
                            {{ dayjs(row.executeEndTime).format('YYYY-MM-DD') }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="serviceParty" show-overflow-tooltip label="服务方名称" />
                    <el-table-column prop="createUserName" show-overflow-tooltip label="需求方名称" />
                    <el-table-column prop="serviceParty" show-overflow-tooltip label="服务方名称" />
                    <el-table-column prop="createTime" show-overflow-tooltip label="工单创建时间" />
                    <el-table-column label="操作" class-name="operation-btns">
                        <template v-slot="{ row }">
@@ -229,8 +229,6 @@
        dialogRef.value?.open({ mode, row })
    })
}
const editStatus = ['0','11','24','25']
provide('editStatus', editStatus)
// 工单状态:0草稿、10发布中_接单中、11发布中_拒绝接单、20响应中_待拆分、
// 21响应中_申请取消、22响应中_申请修改、23响应中_已取消、24响应中_拒绝取消、
// 25响应中_拒绝修改 30执行中_待全部完成、31执行中_协商修改、40完成待验_待全部验收、
@@ -238,7 +236,7 @@
function openFormChange(row) {
    dialogVisible.value = true
    nextTick(() => {
        const mode = editStatus.includes(row.workOrderStatus) && requester.value ? 'edit' : 'view'
        const mode = ['11','25'].includes(row.workOrderStatus) && requester.value ? 'edit' : 'view'
        dialogRef.value?.open({ mode, row })
    })
}
applications/task-work-order/vite.config.mjs
@@ -6,7 +6,7 @@
// https://vitejs.dev/config/
export default ({ mode, command }) => {
  const env = loadEnv(mode, fileURLToPath(new URL("./env", import.meta.url)))
  const { VITE_APP_ENV, VITE_APP_BASE, VITE_APP_URL } = env
  const { VITE_APP_ENV, VITE_APP_BASE, VITE_APP_URL,VITE_APP_API_XT,VITE_APP_URL_XT } = env
  // 判断是打生产环境包
  const isProd = VITE_APP_ENV === 'production'
@@ -41,11 +41,17 @@
      port: 5178,
      // host: '192.168.1.178',
      proxy: {
                '/apiXT': {
                    target: VITE_APP_URL_XT,
                    changeOrigin: true,
                    rewrite: path => path.replace(/^\/apiXT/, ''),
                },
        '/api': {
          target: VITE_APP_URL,
          changeOrigin: true,
          rewrite: path => path.replace(/^\/api/, ''),
        }
        },
      },
    },
    assetsInclude: ['**/*.gltf','**/*.docx','**/*.pdf'],