From 8f32b56b30e07f1614e0c5f5511ae4e0432b780e Mon Sep 17 00:00:00 2001
From: shuishen <1109946754@qq.com>
Date: Tue, 27 Jan 2026 09:19:07 +0800
Subject: [PATCH] Merge branch 'master' of http://139.196.74.78:10010/r/jagzwxm/ja_web
---
/dev/null | 66 ---------------------------------
1 files changed, 0 insertions(+), 66 deletions(-)
diff --git a/applications/task-work-order/src/components/map-container/device-map-container.vue b/applications/task-work-order/src/components/map-container/device-map-container.vue
deleted file mode 100644
index 46199a1..0000000
--- a/applications/task-work-order/src/components/map-container/device-map-container.vue
+++ /dev/null
@@ -1,1094 +0,0 @@
-<template>
- <div class="map-shell">
- <CommonCesiumMap ref="mapRef" class="command-cesium map-container" :dom-id="props.containerId" :active="true"
- :flat-mode="false" :terrain="false" :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" />
- <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">
- <img :src="layerControlIcon" alt="图层控制" />
- </div>
- <div v-if="showLayerPanel" class="layer-panel">
- <div class="panel-title">图层管理</div>
-
- <div class="panel-content">
- <el-tree :data="layerTree" show-checkbox default-expand-all node-key="key"
- :props="layerTreeProps" :default-checked-keys="defaultCheckedKeys" />
- </div>
- </div>
- </div>
- </div>
-
- <div v-if="popupVisible" class="device-popup" :style="popupStyle" @click.stop>
- <div class="device-popup__header">
- <el-tooltip
- class="device-popup__title-tooltip"
- :content="popupTitle"
- placement="top"
- effect="dark"
- :show-after="200"
- >
- <span class="device-popup__title">{{ popupTitleDisplay }}</span>
- </el-tooltip>
-
- <img class="device-popup__close" :src="popupClose" alt="" @click.stop="closePopup">
- </div>
- <div class="device-popup__type">
- <span class="value">{{ popupTypeText }}</span>
- </div>
-
- <div class="device-popup__subtitle">
- <span class="device-popup__dot" :class="popupActionClass"></span>
- <span class="device-popup__action">{{ popupActionText }}</span>
- </div>
-
- <div class="device-popup__rows">
- <div class="device-popup__row">
- <div class="device-popup__col">
- <span class="label">状态</span>
- <span class="value" :class="popupStatusClass">{{ popupStatusText }}</span>
- </div>
-
- <span class="divider">|</span>
-
- <div class="device-popup__col">
- <span class="label">电量</span>
- <span class="value">{{ popupBatteryText }}</span>
- </div>
- </div>
- <div class="device-popup__row">
- <div class="device-popup__col">
- <span class="label">方位角</span>
- <span class="value">{{ popupAzimuthText }}</span>
- </div>
-
- <span class="divider">|</span>
-
- <div class="device-popup__col">
- <span class="label">俯仰角</span>
- <span class="value">{{ popupElevationText }}</span>
- </div>
- </div>
- <div class="device-popup__row">
- <span class="label">侦测目标</span>
- <span class="value">{{ popupDetectCountText }}</span>
- </div>
- <div class="device-popup__row">
- <span class="label">有效范围</span>
- <span class="value">{{ popupRangeText }}</span>
- </div>
- </div>
- </div>
- </div>
-</template>
-
-<script setup>
-import * as Cesium from 'cesium'
-import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue'
-import { geomAnalysis } from '@ztzf/utils'
-import { fwDefenseZonePageApi } from '@/views/areaManage/defenseZone/defenseZoneApi'
-import { fwAreaDividePageApi } from '@/views/areaManage/partition/partitionApi'
-import { fwDefenseSceneListApi } from '@/views/areaManage/sceneConfig/sceneConfigApi'
-import { cockpitAggregationApi } from '@/api/dataCockpit'
-import layerControlIcon from '@/assets/images/dataCockpit/layerControl.png'
-import equipmentIcon from '@/assets/images/dataCockpit/map/equipment.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'
-import jaGeojsonRaw from '@/assets/geojson/ja.geojson?raw'
-
-const props = defineProps({
- onlineDevices: {
- type: Array,
- default: () => [],
- },
- leftCollapsed: {
- type: Boolean,
- default: false,
- },
- containerId: {
- type: String,
- default: 'device-map-container',
- },
- showLayerControl: {
- type: Boolean,
- default: true,
- },
-})
-
-const DEFAULT_ZONE_PAGE_SIZE = 999
-
-const mapRef = ref(null)
-let viewer = null
-const deviceEntityIds = new Set()
-const deviceEntityMap = new Map()
-let defenseZoneSource = null
-let partitionSource = null
-let aggregationSource = null
-let commandPostSource = null
-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 deviceClickHandler = null
-let popupRenderHandler = null
-const layerTreeProps = {
- label: 'label',
- children: 'children',
-}
-const defaultCheckedKeys = ['global', 'city-base']
-const layerTree = ref([
- {
- key: 'base',
- label: '地理信息图层',
- children: [
- { key: 'global', label: '全球地形' },
- { key: 'admin', label: '行政区划' },
- ],
- },
- {
- key: 'city',
- label: '城市CIM图层',
- children: [
- { key: 'city-base', label: '皖山白模' },
- { key: 'city-grid', label: '皖山白模光栅网格' },
- { key: 'city-tilt', label: '皖山倾斜摄影' },
- { key: 'city-tilt-grid', label: '皖山倾斜摄影网格' },
- ],
- },
- {
- key: 'sky',
- label: '空域要素图层',
- children: [
- { key: 'airspace', label: '空域边界' },
- { key: 'route', label: '飞行航路' },
- ],
- },
-])
-
-const getDevicePosition = item => {
- const longitudeRaw = item.longitude ?? item.lng ?? item.lon
- const latitudeRaw = item.latitude ?? item.lat
- if (longitudeRaw == null || latitudeRaw == null) return null
- const longitude = Number(longitudeRaw)
- const latitude = Number(latitudeRaw)
- if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) return null
- return { longitude, latitude }
-}
-
-const clearDeviceEntities = () => {
- if (!viewer) return
- deviceEntityIds.forEach(id => {
- viewer.entities.removeById(id)
- })
- deviceEntityIds.clear()
- deviceEntityMap.clear()
-}
-
-const clearDefenseZoneEntities = () => {
- if (!defenseZoneSource) return
- defenseZoneSource.entities.removeAll()
-}
-
-const clearPartitionEntities = () => {
- if (!partitionSource) return
- partitionSource.entities.removeAll()
-}
-
-const clearAggregationEntities = () => {
- if (!aggregationSource) return
- aggregationSource.entities.removeAll()
-}
-
-const clearCommandPostEntities = () => {
- if (!commandPostSource) return
- commandPostSource.entities.removeAll()
-}
-
-const setDetailVisibility = visible => {
- detailVisible.value = visible
- if (defenseZoneSource) defenseZoneSource.show = visible
- if (partitionSource) partitionSource.show = visible
- if (commandPostSource) commandPostSource.show = visible
- if (!visible) closePopup()
- deviceEntityIds.forEach(id => {
- const entity = viewer?.entities?.getById(id)
- if (entity) entity.show = visible
- })
-}
-
-const setClusterVisibility = visible => {
- clusterVisible.value = visible
- if (aggregationSource) aggregationSource.show = visible
-}
-
-const ensureCountyCenterMap = () => {
- if (countyCenterMap.size) return
- const geojson = JSON.parse(jaGeojsonRaw)
- geojson.features?.forEach(feature => {
- const name = feature?.properties?.name
- const center = feature?.properties?.centroid || feature?.properties?.center
- if (!name || !Array.isArray(center) || center.length < 2) return
- countyCenterMap.set(name, { longitude: center[0], latitude: center[1] })
- })
-}
-
-
-const RING_STYLES = [
- { inner: 0, outer: 2000, gradient: ['#FF361C', '#360B00'] }
-]
-
-const MATERIAL_TYPE = 'RadialGradientMaterial'
-let materialRegistered = false
-
-const registerRadialGradientMaterial = () => {
- if (materialRegistered || !Cesium?.Material) return
- materialRegistered = true
- Cesium.Material._materialCache.addMaterial(MATERIAL_TYPE, {
- fabric: {
- type: MATERIAL_TYPE,
- 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),
- },
- source: `
- 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);
- material.diffuse = color.rgb;
- material.alpha = color.a;
- return material;
- }
- `,
- },
- translucent: () => true,
- })
-}
-
-const buildCirclePositions = (center, radiusMeters, steps = 64) => {
- const positions = []
- const latRad = Cesium.Math.toRadians(center.latitude)
- const metersToLat = 1 / 111320
- const metersToLon = 1 / (111320 * Math.cos(latRad))
- for (let i = 0; i <= steps; i += 1) {
- const angle = Cesium.Math.toRadians((i / steps) * 360)
- const dx = radiusMeters * Math.cos(angle)
- const dy = radiusMeters * Math.sin(angle)
- const lon = center.longitude + dx * metersToLon
- const lat = center.latitude + dy * metersToLat
- positions.push(Cesium.Cartesian3.fromDegrees(lon, lat))
- }
- return positions
-}
-
-const addDeviceRings = (center, baseId, visible) => {
- 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,
- },
- })
- })
-}
-
-class RadialGradientMaterialProperty {
- constructor(color1, color2) {
- this._definitionChanged = new Cesium.Event()
- this.color1 = color1
- this.color2 = color2
- }
-
- get isConstant () {
- return true
- }
-
- get definitionChanged () {
- return this._definitionChanged
- }
-
- getType () {
- return MATERIAL_TYPE
- }
-
- getValue (time, result) {
- const target = result || {}
- target.color1 = this.color1
- target.color2 = this.color2
- return target
- }
-
- equals (other) {
- return (
- other instanceof RadialGradientMaterialProperty &&
- Cesium.Color.equals(this.color1, other.color1) &&
- Cesium.Color.equals(this.color2, other.color2)
- )
- }
-}
-
-const renderDeviceEntities = devices => {
- if (!viewer) return
- clearDeviceEntities()
- devices.forEach((item, index) => {
- const position = getDevicePosition(item)
- if (!position) return
- const entityId = `online-device-${item.id ?? index}`
- deviceEntityIds.add(entityId)
- addDeviceRings(position, entityId, detailVisible.value)
- const entity = viewer.entities.add({
- id: entityId,
- show: detailVisible.value,
- position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0),
- billboard: {
- image: equipmentIcon,
- width: 40,
- height: 56,
- verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
- },
- })
- deviceEntityMap.set(entityId, { data: item, entity })
- })
- if (selectedDeviceEntity && !deviceEntityMap.has(selectedDeviceEntity.id)) {
- closePopup()
- }
-}
-
-const getDefenseZonePositions = geom => {
- const points = geomAnalysis(geom)
- if (points.length < 3) return []
- const first = points[0]
- const last = points[points.length - 1]
- const list =
- first && last && first.longitude === last.longitude && first.latitude === last.latitude
- ? points.slice(0, -1)
- : points
- return list.map(item => Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude))
-}
-
-const renderZonePolygons = ({ zones, source, idPrefix, lineColor, fillGradient }) => {
- if (!viewer || !source) return
- 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) => {
- 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 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 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 loadDefenseZones = async () => {
- if (!viewer) return
- try {
- const res = await fwDefenseZonePageApi({ current: 1, size: DEFAULT_ZONE_PAGE_SIZE })
- renderDefenseZones(res?.data?.data?.records ?? [])
- } catch (error) {
- renderDefenseZones([])
- }
-}
-
-const loadPartitions = async () => {
- if (!viewer) return
- try {
- const res = await fwAreaDividePageApi({ current: 1, size: DEFAULT_ZONE_PAGE_SIZE })
- renderPartitions(res?.data?.data?.records ?? [])
- } catch (error) {
- renderPartitions([])
- }
-}
-
-const renderAggregation = list => {
- if (!viewer) return
- ensureCountyCenterMap()
- if (!aggregationSource) {
- aggregationSource = new Cesium.CustomDataSource('aggregationSource')
- viewer.dataSources.add(aggregationSource)
- }
- clearAggregationEntities()
- aggregationSource.show = clusterVisible.value
- const countMap = new Map()
- ; (list || []).forEach(item => {
- if (!item?.type) return
- countMap.set(item.type, Number(item.count ?? 0))
- })
- Array.from(countyCenterMap.entries()).forEach(([name, center], index) => {
- const position = Cesium.Cartesian3.fromDegrees(center.longitude, center.latitude, 0)
- const count = countMap.get(name) ?? 0
- aggregationSource.entities.add({
- id: `aggregation-${name}-${index}`,
- position,
- billboard: {
- image: aggregationIcon,
- width: 66.73,
- height: 43,
- verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
- heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
- },
- label: {
- text: `${count}`,
- fillColor: Cesium.Color.WHITE,
- style: Cesium.LabelStyle.FILL_AND_OUTLINE,
- verticalOrigin: Cesium.VerticalOrigin.CENTER,
- horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
- font: '11pt Source Han Sans CN',
- eyeOffset: new Cesium.Cartesian3(0, 0, -20), // 让label "浮" 在广告牌前面
-
- pixelOffset: new Cesium.Cartesian2(0, -35),
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
- heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
- },
- })
- })
-}
-
-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)
- }
- }
- return null
-}
-
-const updatePopupPosition = () => {
- if (!viewer || !selectedDeviceEntity) return
- const cartesian =
- selectedDeviceEntity.position?.getValue?.(Cesium.JulianDate.now()) ||
- selectedDeviceEntity.position?._value
- if (!cartesian) return
- const screenPosition = viewer.scene.cartesianToCanvasCoordinates(cartesian)
- if (!screenPosition) return
- popupPosition.value = { x: screenPosition.x, y: screenPosition.y }
-}
-
-const startPopupRender = () => {
- if (!viewer || popupRenderHandler) return
- popupRenderHandler = () => updatePopupPosition()
- viewer.scene.postRender.addEventListener(popupRenderHandler)
- updatePopupPosition()
-}
-
-const stopPopupRender = () => {
- if (!viewer || !popupRenderHandler) return
- viewer.scene.postRender.removeEventListener(popupRenderHandler)
- popupRenderHandler = null
-}
-
-const handleDeviceClick = movement => {
- if (!viewer) return
- const picks = viewer.scene.drillPick(movement.position) || []
- const pickedDevice = getPickedDevice(picks)
- if (!pickedDevice) {
- closePopup()
- return
- }
- selectedDevice.value = pickedDevice.data
- selectedDeviceEntity = pickedDevice.entity
- startPopupRender()
-}
-
-const initDeviceClickHandler = () => {
- if (deviceClickHandler || !viewer) return
- deviceClickHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
- deviceClickHandler.setInputAction(handleDeviceClick, Cesium.ScreenSpaceEventType.LEFT_CLICK)
-}
-
-const destroyDeviceClickHandler = () => {
- deviceClickHandler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
- deviceClickHandler?.destroy()
- deviceClickHandler = null
-}
-
-function closePopup () {
- selectedDevice.value = null
- selectedDeviceEntity = null
- stopPopupRender()
-}
-
-const popupPosition = ref({ x: 0, y: 0 })
-const popupVisible = computed(() => Boolean(selectedDevice.value))
-const popupStyle = computed(() => ({
- left: `${popupPosition.value.x}px`,
- top: `${popupPosition.value.y}px`,
-}))
-
-const POPUP_TITLE_MAX = 12
-const popupTitle = computed(() => selectedDevice.value?.deviceName || selectedDevice.value?.deviceModel || '-')
-const popupTitleOverflow = computed(() => popupTitle.value.length > POPUP_TITLE_MAX)
-const popupTitleDisplay = computed(() => {
- if (!popupTitleOverflow.value) return popupTitle.value
- return `${popupTitle.value.slice(0, POPUP_TITLE_MAX)}……`
-})
-const popupStatusText = computed(() => {
- const status = selectedDevice.value?.status
- if (status === 0) return '在线'
- if (status === 1) return '离线'
- if (status === 2) return '故障'
- if (status === 3) return '报废'
- return '-'
-})
-const popupStatusClass = computed(() => (selectedDevice.value?.status === 0 ? 'online' : ''))
-const popupActionText = computed(() => {
- const workMode = selectedDevice.value?.workMode
- if (workMode === 1 || workMode === '1') return '侦测中'
- if (workMode === 2 || workMode === '2') return '信号干扰中'
- if (workMode === 3 || workMode === '3') return '诱导驱离中'
- if (workMode === 4 || workMode === '4') return '待机'
- return workMode || '-'
-})
-const popupActionClass = computed(() => {
- const workMode = selectedDevice.value?.workMode
- if (workMode === 1 || workMode === '1') return 'is-detecting'
- if (workMode === 2 || workMode === '2') return 'is-jamming'
- if (workMode === 3 || workMode === '3') return 'is-driving'
- if (workMode === 4 || workMode === '4') return 'is-standby'
- return 'is-standby'
-})
-const popupTypeText = computed(() => {
- const type = selectedDevice.value?.deviceType
- if (type === 1 || type === '1') return '便捷侦测箱'
- if (type === 2 || type === '2') return '反制枪'
- if (type === 3 || type === '3') return '察打一体'
- return type || '-'
-})
-const popupBatteryText = computed(() => {
- const val = selectedDevice.value?.batteryPct
- if (val == null) return '-'
- return `${val}%`
-})
-const popupAzimuthText = computed(() => {
- const val = selectedDevice.value?.azimuth
- if (val == null) return '-'
- return `${val}°`
-})
-const popupElevationText = computed(() => {
- const val = selectedDevice.value?.elevation
- if (val == null) return '-'
- return `${val}°`
-})
-const popupDetectCountText = computed(() => {
- const val = selectedDevice.value?.detectTargetCnt
- if (val == null) return '-'
- return `${val}台`
-})
-const popupRangeText = computed(() => {
- const val = selectedDevice.value?.effectiveRangeKm
- if (val == null) return '-'
- return `${val}KM`
-})
-
-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,
- },
- })
- })
-}
-
-const loadAggregation = async () => {
- try {
- const res = await cockpitAggregationApi()
- renderAggregation(res?.data?.data ?? [])
- } catch (error) {
- renderAggregation([])
- }
-}
-
-const loadCommandPosts = async () => {
- try {
- const res = await fwDefenseSceneListApi()
- renderCommandPosts(res?.data?.data ?? [])
- } catch (error) {
- renderCommandPosts([])
- }
-}
-
-
-watch(
- () => props.onlineDevices,
- devices => {
- renderDeviceEntities(devices || [])
- },
- { deep: true }
-)
-
-watch(
- () => props.leftCollapsed,
- isCollapsed => {
- if (isCollapsed) showLayerPanel.value = false
- }
-)
-
-const toggleLayerPanel = () => {
- if (!props.showLayerControl) return
- showLayerPanel.value = !showLayerPanel.value
-}
-
-const updateStageDisplay = stage => {
- const showCluster = stage === 'cluster'
- setClusterVisibility(showCluster)
- setDetailVisibility(!showCluster)
-}
-
-const handleMapReady = ({ viewer: mapViewer }) => {
- viewer = mapViewer
- const height = viewer?.camera?.positionCartographic?.height
- const stage = height != null && height >= 100000 ? 'cluster' : 'detail'
- updateStageDisplay(stage)
- renderDeviceEntities(props.onlineDevices)
- loadDefenseZones()
- loadPartitions()
- loadAggregation()
- loadCommandPosts()
- initDeviceClickHandler()
-}
-
-const handleStageChange = stage => {
- updateStageDisplay(stage)
-}
-
-const handleClickOutside = event => {
- if (!showLayerPanel.value) return
- const target = event.target
- if (layerWrapRef.value?.contains(target)) return
- showLayerPanel.value = false
-}
-
-onMounted(() => {
- document.addEventListener('click', handleClickOutside)
- const map = mapRef.value?.getMap()
- if (map?.viewer) handleMapReady(map)
-})
-
-onBeforeUnmount(() => {
- document.removeEventListener('click', handleClickOutside)
- clearDeviceEntities()
- clearDefenseZoneEntities()
- clearPartitionEntities()
- clearAggregationEntities()
- clearCommandPostEntities()
- closePopup()
- destroyDeviceClickHandler()
- viewer = null
-})
-</script>
-
-<style lang="scss" scoped>
-.map-shell {
- position: relative;
- width: 100%;
- height: 100%;
-}
-
-.map-container {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
-}
-
-.device-popup {
- position: absolute;
- transform: translate(-50%, calc(-100% - 72px));
- width: 202px;
- padding: 0 16px 20px;
- border-radius: 16px;
- background: rgba(5, 5, 15, 0.7);
- backdrop-filter: blur(16px);
- color: #C3C3DD;
- font-family: Source Han Sans CN, Source Han Sans CN;
- font-size: 12px;
- pointer-events: auto;
- z-index: 11;
- box-sizing: border-box;
-
- &::after {
- content: "";
- position: absolute;
- left: 50%;
- bottom: -8px;
- transform: translateX(-50%);
- width: 0;
- height: 0;
- border-left: 8px solid transparent;
- border-right: 8px solid transparent;
- border-top: 8px solid rgba(5, 5, 15, 0.7);
- }
-
- .device-popup__header {
- height: 40px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- position: relative;
-
- font-weight: bold;
- font-size: 14px;
- color: #FFFFFF;
-
- &::after {
- content: "";
- position: absolute;
- left: 0;
- bottom: 0;
- width: 50px;
- height: 2px;
- background: #284FE3;
- box-shadow: 0px 3px 2px 0px rgba(15, 89, 255, 0.1), 0px 7px 5px 0px rgba(15, 89, 255, 0.15), 0px 13px 10px 0px rgba(15, 89, 255, 0.18), 0px 22px 18px 0px rgba(15, 89, 255, 0.21), 0px 42px 33px 0px rgba(15, 89, 255, 0.26), 0px 100px 80px 0px rgba(15, 89, 255, 0.36);
- border-radius: 5px 5px 0px 0px;
- }
-
- &::before {
- content: "";
- position: absolute;
- left: 55px;
- right: 0;
- bottom: 0;
- height: 2px;
- background: #323245;
- border-radius: 0px 0px 5px 5px;
- }
-
- .device-popup__title-tooltip {
- flex: 1;
- min-width: 0;
- display: block;
- }
-
- .device-popup__title {
- display: inline-block;
- max-width: 100%;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
-
- .device-popup__close {
- cursor: pointer;
- }
- }
-
- .device-popup__type {
- display: flex;
- align-items: center;
- gap: 6px;
- font-size: 12px;
- margin-top: 8px;
- margin-bottom: 10px;
- color: #d2d7df;
-
- .label {
- color: #9aa4b2;
- }
- }
-
- .device-popup__subtitle {
- display: inline-flex;
- align-items: center;
- gap: 6px;
- padding: 2px 10px;
- border-radius: 999px;
- background: rgba(255, 255, 255, 0.06);
- font-size: 12px;
- margin-bottom: 10px;
-
- .device-popup__dot {
- width: 6px;
- height: 6px;
- border-radius: 50%;
- background: #6b7685;
-
- &.is-detecting {
- background: #35f08c;
- box-shadow: 0 0 6px rgba(53, 240, 140, 0.8);
- }
-
- &.is-jamming {
- background: #ff4444;
- box-shadow: 0 0 6px rgba(255, 68, 68, 0.7);
- }
-
- &.is-driving {
- background: #ffb020;
- box-shadow: 0 0 6px rgba(255, 176, 32, 0.7);
- }
-
- &.is-standby {
- background: #9aa4b2;
- }
- }
- }
-
- .device-popup__rows {
- display: flex;
- flex-direction: column;
- gap: 6px;
- font-size: 12px;
- color: #d2d7df;
-
- .device-popup__row {
- display: flex;
- align-items: center;
- margin-bottom: 10px;
-
- .device-popup__col {
- flex: 1;
- display: flex;
- align-items: center;
-
- &:last-child {
- justify-content: flex-end;
- }
- }
- }
-
- .value {
- margin-left: 10px;
- }
-
- .value.online {
- color: #35f08c;
- }
- }
-
-}
-
-.layer-control-root {
- position: absolute;
- left: 337px;
- bottom: 22px;
- z-index: 9;
- transition: transform 0.3s ease-in-out;
- pointer-events: none;
-
- &.collapsed {
- transform: translateX(-317px);
- }
-}
-
-.layer-control-wrap {
- position: relative;
- display: flex;
- align-items: flex-end;
- pointer-events: auto;
-}
-
-.layer-control {
- width: 46px;
- height: 46px;
- cursor: pointer;
-
- img {
- width: 100%;
- height: 100%;
- display: block;
- }
-}
-
-.layer-panel {
- display: flex;
- flex-direction: column;
- position: absolute;
- left: 66px;
- bottom: 0;
- width: 160px;
- max-height: 442px;
- background: #191932;
- border-radius: 8px 8px 8px 8px;
-
- .panel-title {
- padding: 0 16px;
- line-height: 42px;
- font-family: 'Open Sans', Open Sans;
- font-weight: 400;
- font-size: 12px;
- color: #ffffff;
- text-align: left;
- font-style: normal;
- text-transform: none;
- border-bottom: 1px solid rgba(70, 70, 100, 0.5);
- box-sizing: border-box;
- }
-
- .panel-content {
- padding: 0 16px;
- height: 0;
- flex: 1;
- overflow: auto;
- }
-
- ::v-deep(.el-tree) {
- background: transparent;
- color: #c3c3dd;
- font-size: 12px;
-
- .el-tree-node {
- line-height: 30px !important;
-
- .el-tree-node__content {
- padding-left: 0 !important;
- display: flex;
- align-items: center;
- height: 40px !important;
- line-height: 40px !important;
- border-bottom: 1px solid rgba(70, 70, 100, 0.5);
- box-sizing: border-box;
- }
-
- .el-tree-node__children {
- .el-tree-node__content {
- border: none;
- }
- }
-
- &:focus {
- .el-tree-node__content {
- background: transparent !important;
- }
- }
- }
- }
-}
-
-.layer-panel :deep(.el-tree-node__label) {
- color: #c3c3dd;
-}
-
-.layer-panel :deep(.el-tree-node__expand-icon) {
- order: 3;
- margin-left: auto;
-}
-
-.layer-panel :deep(.el-tree-node__expand-icon.is-leaf) {
- visibility: hidden;
-}
-
-.layer-panel :deep(.el-checkbox) {
- order: 1;
-}
-
-.layer-panel :deep(.el-tree-node__label) {
- order: 2;
-}
-
-.layer-panel :deep(.el-tree-node__expand-icon:not(.is-leaf) ~ .el-checkbox) {
- display: none;
-}
-
-.layer-panel :deep(.el-tree-node__content:hover),
-.layer-panel :deep(.el-tree-node__content:focus) {
- background: transparent !important;
-}
-
-.layer-panel :deep(.el-tree-node.is-current > .el-tree-node__content) {
- background: transparent !important;
- color: #ffffff;
-}
-
-.layer-panel :deep(.el-tree-node.is-current > .el-tree-node__content .el-tree-node__label) {
- color: #ffffff;
-}
-
-.layer-panel :deep(.el-checkbox__input.is-checked .el-checkbox__inner) {
- background-color: #023aff;
- border-color: #023aff;
-}
-
-.layer-panel :deep(.el-checkbox__inner) {
- background-color: transparent;
- border-color: #a1a3d4;
-}
-</style>
diff --git a/applications/task-work-order/src/components/map-container/mapContainer-copy.vue b/applications/task-work-order/src/components/map-container/mapContainer-copy.vue
deleted file mode 100644
index 66d1b89..0000000
--- a/applications/task-work-order/src/components/map-container/mapContainer-copy.vue
+++ /dev/null
@@ -1,264 +0,0 @@
-<!--
- * @Author: shuishen 1109946754@qq.com
- * @Date: 2024-10-25 15:07:51
- * @LastEditors: shuishen 1109946754@qq.com
- * @LastEditTime: 2025-04-28 11:34:40
- * @FilePath: \drone-command\src\components\map-container\mapContainer.vue
- * @Description:
- *
- * Copyright (c) 2024 by shuishen, All Rights Reserved.
--->
-<template>
- <div class="map-container">
- <div class="viewer-container" id="viewer-container">
- <div class="content">
- <slot name="content"></slot>
- </div>
- </div>
- </div>
-</template>
-
-<script setup>
-import * as turf from '@turf/turf'
-
-import { nextTick, onMounted, onUnmounted } from 'vue'
-import { read } from 'xlsx'
-
-import startPng from '@/assets/map_images/Startingpointicon.png'
-import endPng from '@/assets/map_images/EndPointicon.png'
-
-window.$viewer = null
-window.$Cesium = null
-let pointLayer = null
-let polylineLayer = null
-let pointHtmlLayer = null
-
-const { VITE_APP_BASE } = import.meta.env
-// import * as Cesium from 'cesium'
-// import 'cesium/Build/Cesium/Widgets/widgets.css'
-const isViewerReady = ref(false)
-const { rowDetails } = defineProps({
- rowDetails: {
- type: Object,
- default: () => ({}),
- }
-})
-
-
-async function initMap () {
- if (window.$viewer) return
-
- await DC.ready({
- // Cesium: Cesium,
- baseUrl: `${VITE_APP_BASE}/libs/dc-sdk/resources/`,
- })
-
- window.$Cesium = DC.getLib('Cesium')
-
- // 天地图地图
- const imageryProvider_standZh = new window.$Cesium.UrlTemplateImageryProvider({
- url: 'https://t{s}.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=e45274b0235bb913eceb393aabbf9c9c',
- subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
- maximumLevel: 18,
- credit: 'stand_zj',
- })
- const imageryProvider_stand = new window.$Cesium.UrlTemplateImageryProvider({
- url: 'https://t{s}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=e45274b0235bb913eceb393aabbf9c9c',
- subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
- // format: 'image/jpeg',
- // show: true,
- maximumLevel: 18,
- credit: 'stand_tc',
- })
-
- window.$viewer = new DC.Viewer('viewer-container', {
- "sceneMode": 2 //1: 2.5D,2: 2D,3: 3D
- })
-
- window.$viewer.locationBar.enable = false
-
- window.$viewer?.zoomToPosition(new DC.Position(115.892151, 28.676493, 1000000, 0, -90, 0))
-
- window.$viewer?.imageryLayers.addImageryProvider(imageryProvider_stand)
- window.$viewer?.imageryLayers.addImageryProvider(imageryProvider_standZh)
-
- pointLayer = new DC.VectorLayer('pointLayer')
- window.$viewer?.addLayer(pointLayer)
- polylineLayer = new DC.VectorLayer('polylineLayer')
- window.$viewer?.addLayer(polylineLayer)
- pointHtmlLayer = new DC.HtmlLayer('pointHtmlLayer')
- window.$viewer?.addLayer(pointHtmlLayer)
-
- isViewerReady.value = true
-}
-
-/**
-* 初始化标注添加
-* @param type 类型
-* @param data 数据
-*/
-const initAddEntity = (type, data) => {
- watch(() => isViewerReady.value,
- (ready) => {
- if (ready) {
- type === 'point' ? addPoint(data) : addPolyline(data)
- }
- },
- { deep: true, immediate: true } // 初始化时立即执行
- )
-}
-
-/**
-* 添加点标注
-* @param data 数据 数据格式 [lng, lat]
-*/
-function addPoint (data) {
- if (pointLayer) pointLayer.clear()
-
- const [lng, lat] = data
-
- if (!lng || !lat) return
-
- let point = new DC.Point(new DC.Position(lng, lat))
- pointLayer.addOverlay(point)
-
- window.$viewer?.zoomTo(pointLayer)
-}
-
-/**
-* 添加点标注
-* @param data 数据 数据格式 [[lng, lat], [lng, lat], [lng, lat]]
-*/
-function addPolyline (data) {
- if (polylineLayer) polylineLayer.clear()
- if (pointHtmlLayer) pointHtmlLayer.clear()
-
- if (data.length === 0) return
-
- const positionStr = data.map(item => {
- const [lng, lat] = item
-
- return `${lng}, ${lat}`
- }).join(';')
-
- let polyline = new DC.Polyline(positionStr)
- polyline.setStyle({
- width: 4,
- material: new DC.PolylineTrailMaterialProperty({
- color: DC.Color.DEEPSKYBLUE,
- speed: 10
- }),
- clampToGround: true
- })
- polylineLayer.addOverlay(polyline)
-
-
- data.forEach((item, index) => {
- const [lng, lat] = item
- let position = new DC.Position(lng, lat)
-
- console.log(lng, lat)
- let billboard = null
-
- if (index === 0) {
- billboard = new DC.Billboard(position, startPng)
- }
-
- if (index === data.length - 1) {
- billboard = new DC.Billboard(position, endPng)
- }
-
-
- billboard && (billboard.size = [20, 20])
- billboard && (billboard.setStyle({
- "pixelOffset": { "x": 0, "y": -8 }
- }))
- billboard && polylineLayer.addOverlay(billboard)
-
- if (index !== 0 && index !== data.length - 1) {
- let divIcon = new DC.DivIcon(
- position,
- `<div class="point-icon-box">${index}</div>`
- )
- pointHtmlLayer.addOverlay(divIcon)
- }
- })
-
- const line = turf.lineString(positionStr.split(';').map(i => i.split(',')))
- const bbox = turf.bbox(line)
- const bboxPolygon = turf.bboxPolygon(bbox)
- const scaledPolygon = turf.transformScale(bboxPolygon, 3)
- const newBbox = turf.bbox(scaledPolygon)
-
- window.$viewer?.flyToBounds(
- newBbox,
- { heading: 0, pitch: -90, roll: 0 },
- () => { },
- 0
- )
-}
-
-onMounted(() => {
- nextTick(() => {
- initMap()
- })
-})
-
-onUnmounted(() => {
- if (pointLayer) {
- window.$viewer?.removeLayer(pointLayer)
- pointLayer = null
- }
-
- if (polylineLayer) {
- window.$viewer?.removeLayer(polylineLayer)
- polylineLayer = null
- }
-
- window.$viewer?.entities.removeAll()
- window.$viewer?.imageryLayers.removeAll()
- window.$viewer?.dataSources.removeAll()
- let gl = window.$viewer.scene.context._originalGLContext
- gl.canvas.width = 1
- gl.canvas.height = 1
-
- window.$viewer && window.$viewer.setTerrain()
- window.$viewer && window.$viewer.destroy()
- window.$viewer = null
- delete window.$viewer
- window.$Cesium = null
- delete window.$Cesium
- var cesiumContainer = document.getElementById('viewer-container')
- if (cesiumContainer) {
- cesiumContainer.remove() // 移除与地图相关的DOM元素
- }
-})
-
-defineExpose({
- initAddEntity
-})
-</script>
-
-<script>
-export default {
- name: 'MapContainer'
-}
-</script>
-
-<style lang="scss" scoped>
-.map-container {
- position: relative;
- width: 100% !important;
- height: 100% !important;
- overflow: hidden;
-}
-
-.viewer-container {
- position: absolute;
- top: 50%;
- left: 50%;
- width: 120%;
- height: 120%;
- transform: translate(-50%, -50%);
-}
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/CenterContainer.vue b/applications/task-work-order/src/views/dataCockpit/components/CenterContainer.vue
deleted file mode 100644
index 170826c..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/CenterContainer.vue
+++ /dev/null
@@ -1,237 +0,0 @@
-<template>
- <div class="center-container">
- <div class="left-box">
- <div class="logo">
- <img :src="warningLogo" alt="">
- </div>
-
- <div class="list">
- <div class="item" v-for="(item, ind) in warningList" :key="ind">
- <div class="val" @click="warningTypeClick(item.type)">
- {{ item.value }}
- </div>
-
- <div class="name">
- {{ item.title }}
- </div>
- </div>
- </div>
- </div>
-
- <div class="right-box">
- <div class="logo">
- <img :src="equipmentLogo" alt="">
- </div>
-
- <div class="list">
- <div class="item" v-for="(item, ind) in equipmentList" :key="ind">
- <div class="val" :class="{highlight: item.isHighlight}" @click="equipmentClick(item.isHighlight)">
- {{ item.value }}
- </div>
-
- <div class="name">
- {{ item.title }}
- </div>
- </div>
- </div>
- </div>
- </div>
-</template>
-
-<script setup>
-import { onMounted, ref } from 'vue'
-import warningLogo from '@/assets/images/dataCockpit/warning.png'
-import equipmentLogo from '@/assets/images/dataCockpit/equipment.png'
-import { alarmStatisticsApi } from '@/api/dataCockpit'
-
-const warningList = ref([
- {
- title: '实时告警',
- value: 0,
- type: 1
- },
- {
- title: '设备告警',
- value: 0,
- type: 2
- },
- {
- title: '历史告警',
- value: 0,
- type: 3
- },
-])
-
-const equipmentList = ref([
- {
- title: '设备统计',
- value: 0,
- isHighlight: true,
- },
- {
- title: '无线电',
- value: 0,
- },
- {
- title: '光电',
- value: 0,
- },
- {
- title: '雷达',
- value: 0,
- }
-])
-
-const emit = defineEmits(['select-warning', 'select-equipment'])
-
-const warningTypeClick = (type) => {
- emit('select-warning', type)
-}
-
-const equipmentClick = (isHighlight) => {
- if (isHighlight) {
- emit('select-equipment')
- }
-}
-
-const updateStats = async () => {
- const res = await alarmStatisticsApi()
- const data = res?.data?.data || {}
- warningList.value = [
- { title: '实时告警', value: data.realTimeAlarmCount ?? 0, type: 1 },
- { title: '设备告警', value: data.deviceAlarmCount ?? 0, type: 2 },
- { title: '历史告警', value: data.historyAlarmCount ?? 0, type: 3 }
- ]
- equipmentList.value = [
- { title: '设备统计', value: data.deviceCount ?? 0, isHighlight: true },
- { title: '无线电', value: data.radioCount ?? 0 },
- { title: '光电', value: data.opticalCount ?? 0 },
- { title: '雷达', value: data.radarCount ?? 0 }
- ]
-}
-
-onMounted(() => {
- updateStats()
-})
-</script>
-
-<style lang="scss" scoped>
-.center-container {
- display: flex;
- position: absolute;
- top: 75px;
- left: 465px;
- z-index: 9;
- pointer-events: none;
-
- .left-box {
- width: 356px;
-
- .list {
- .item {
- .val {
- color: #FF4444;
- cursor: pointer;
- }
- }
- }
- }
-
- .right-box {
- margin-left: 20px;
- width: 455px;
-
- .list {
- .item {
- .val.highlight {
- color: #6382FF;
- cursor: pointer;
- }
- }
- }
- }
-
- .left-box,
- .right-box {
- position: relative;
- padding: 17px 20px;
- display: flex;
- align-items: center;
- height: 100px;
- background: rgba(0,0,0,0.3);
- box-sizing: border-box;
- border-radius: 16px 16px 16px 16px;
- pointer-events: auto;
-
- &::after {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: -1;
- background: rgba(17, 23, 34, 0.56);
- backdrop-filter: blur(2px);
- border-radius: 16px 16px 16px 16px;
- }
-
-
- .logo {
- img {
- width: 40px;
- height: 40px;
- }
- }
-
- .list {
- width: 0;
- flex: 1;
- height: 100%;
- display: flex;
-
- .item {
- width: 0;
- flex: 1;
- position: relative;
- padding-left: 20px;
- color: #fff;
-
- .name {
- font-family: Open Sans, Open Sans;
- font-weight: 400;
- font-size: 14px;
- color: #FFFFFF;
- text-align: left;
- font-style: normal;
- text-transform: none;
- }
-
- .val {
- font-family: Source Han Sans CN, Source Han Sans CN;
- font-weight: bold;
- font-size: 30px;
- text-align: left;
- font-style: normal;
- text-transform: none;
- }
-
- &::after {
- content: '';
- position: absolute;
- top: 0;
- right: 0;
- width: 1px;
- height: 100%;
- width: 1px;
- background: #333355;
- }
-
- &:last-child::after {
- display: none;
- }
- }
- }
- }
-}
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/DeviceHistoryDialog.vue b/applications/task-work-order/src/views/dataCockpit/components/DeviceHistoryDialog.vue
deleted file mode 100644
index 85e5351..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/DeviceHistoryDialog.vue
+++ /dev/null
@@ -1,208 +0,0 @@
-<template>
- <el-dialog class="gd-dialog1" append-to-body v-model="visibleModel" :title="dialogTitle"
- :close-on-click-modal="false" :destroy-on-close="true">
- <el-form ref="queryParamsRef" :model="searchParams" class="gd-dialog-form">
- <el-form-item label="关键字" prop="keyword">
- <el-input class="gd-input" v-model="searchParams.keyword" placeholder="名称/序列号"
- clearable @clear="handleSearch" />
- </el-form-item>
-
- <el-form-item prop="dateRange">
- <el-date-picker class="gd-date-picker"
- popper-class="gd-tree-select-popper" v-model="searchParams.dateRange"
- type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"
- value-format="YYYY/MM/DD" @change="handleSearch" />
- </el-form-item>
-
- <el-form-item label="区域" prop="area">
- <el-tree-select class="gd-select" popper-class="gd-tree-select-popper"
- v-model="searchParams.area" :data="areaTree" :props="areaTreeProps" node-key="value" check-strictly
- clearable placeholder="请选择" @change="handleSearch" />
- </el-form-item>
-
- <el-form-item label="无人机类型" prop="droneType">
- <el-select class="gd-select" popper-class="gd-select-popper"
- v-model="searchParams.droneType" placeholder="请选择" clearable @change="handleSearch">
- <el-option v-for="item in droneTypeOptions" :key="item.value" :label="item.label"
- :value="item.value" />
- </el-select>
- </el-form-item>
-
- <el-form-item class="gd-search-actions">
- <el-button :icon="RefreshRight" @click="resetForm"></el-button>
- <el-button :icon="Search" @click="handleSearch"></el-button>
- </el-form-item>
- </el-form>
-
- <div class="gd-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
- <div class="gd-table-content">
- <el-table class="gd-table" :data="list">
- <el-table-column type="index" width="60" label="序号" />
- <el-table-column prop="droneName" label="无人机名称" min-width="120" />
- <el-table-column prop="droneSerialNo" label="无人机序列号" min-width="140" />
- <el-table-column label="无人机类型" min-width="110">
- <template #default="{ row }">
- {{ getDroneTypeText(row.droneType) }}
- </template>
- </el-table-column>
- <el-table-column prop="areaName" label="区域" min-width="90" />
- <el-table-column prop="deviceName" label="数据来源" min-width="110" />
- <el-table-column label="信号频段" min-width="100">
- <template #default="{ row }">
- {{ formatFrequency(row.signalFreqMhz) }}
- </template>
- </el-table-column>
- <el-table-column prop="alarmTime" label="发现时间" min-width="150" />
- <el-table-column prop="stayDuration" label="停留时间" min-width="110" />
- <el-table-column label="反制方式" min-width="110">
- <template #default="{ row }">
- {{ getCounterWayText(row.counterWay) }}
- </template>
- </el-table-column>
- </el-table>
- </div>
-
- <div class="gd-pagination-parent">
- <el-pagination popper-class="gd-select-popper"
- v-model:current-page="searchParams.current" v-model:page-size="searchParams.size"
- layout="total, prev, pager, next, sizes" :total="total" @change="getList" />
- </div>
- </div>
- </el-dialog>
-</template>
-
-<script setup>
-import { Search, RefreshRight } from '@element-plus/icons-vue'
-import { computed, ref, watch } from 'vue'
-import { alarmLogApi } from '@/api/dataCockpit'
-
-const props = defineProps({
- modelValue: {
- type: Boolean,
- default: false
- },
- device: {
- type: Object,
- default: () => null
- }
-})
-
-const emit = defineEmits(['update:modelValue'])
-
-const visibleModel = computed({
- get: () => props.modelValue,
- set: (val) => emit('update:modelValue', val)
-})
-
-const dialogTitle = computed(() => {
- return `反制设备名称 ${props.device?.name || ''}`.trim()
-})
-
-const initSearchParams = () => ({
- keyword: '',
- dateRange: [],
- area: '',
- droneType: '',
- current: 1,
- size: 10
-})
-const searchParams = ref(initSearchParams())
-const queryParamsRef = ref(null)
-const loading = ref(true)
-const list = ref([])
-const total = ref(0)
-const areaTree = ref([
- {
- label: '古村区',
- value: '古村区'
- },
- {
- label: '新区',
- value: '新区'
- },
- {
- label: '产业园',
- value: '产业园'
- }
-])
-const areaTreeProps = {
- label: 'label',
- children: 'children'
-}
-
-const droneTypeOptions = [
- { label: '微型机', value: '1' },
- { label: '植保机', value: '2' }
-]
-
-const getList = async () => {
- if (!props.device?.id) {
- list.value = []
- total.value = 0
- return
- }
- const [startTime, endTime] = searchParams.value.dateRange || []
- const keyword = (searchParams.value.keyword || '').trim()
- const params = {
- alarmType: '2',
- deviceId: props.device.id,
- current: searchParams.value.current,
- size: searchParams.value.size,
- startTime,
- endTime,
- areaName: searchParams.value.area || undefined,
- droneType: searchParams.value.droneType || undefined,
- droneName: keyword || undefined,
- }
- loading.value = true
- try {
- const res = await alarmLogApi(params)
- list.value = res?.data?.data?.records ?? []
- total.value = res?.data?.data?.total ?? 0
- } finally {
- loading.value = false
- }
-}
-
-function handleSearch () {
- searchParams.value.current = 1
- getList()
-}
-
-function resetForm () {
- searchParams.value = initSearchParams()
-
- getList()
-}
-
-const formatFrequency = (value) => {
- if (value == null || value === '') return '-'
- return `${value}MHZ`
-}
-
-const getCounterWayText = (value) => {
- if (value === 1 || value === '1') return '信号干扰'
- if (value === 2 || value === '2') return '诱导驱离'
- if (value === 3 || value === '3') return '无'
- return value || '-'
-}
-
-const getDroneTypeText = (value) => {
- if (value === 1 || value === '1') return '微型机'
- if (value === 2 || value === '2') return '植保机'
- return value || '-'
-}
-
-watch(
- () => visibleModel.value,
- (visible) => {
- if (visible) {
- searchParams.value.current = 1
- getList()
- }
- }
-)
-
-</script>
-
-<style lang="scss" scoped></style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/EmptyState.vue b/applications/task-work-order/src/views/dataCockpit/components/EmptyState.vue
deleted file mode 100644
index 929fb57..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/EmptyState.vue
+++ /dev/null
@@ -1,59 +0,0 @@
-<!--
- * @Author : yuan
- * @Date : 2026-01-09 15:06:02
- * @LastEditors : yuan
- * @LastEditTime : 2026-01-09 15:10:44
- * @FilePath : \applications\drone-command\src\views\dataCockpit\components\EmptyState.vue
- * @Description :
- * Copyright 2026 OBKoro1, All Rights Reserved.
- * 2026-01-09 15:06:02
--->
-<template>
- <div class="empty-state">
- <div class="empty-content">
- <img :src="noDataBg" alt="暂无数据">
- <span class="empty-text">暂无数据</span>
- </div>
- </div>
-</template>
-
-<script setup>
-import noDataBg from '@/assets/images/no-data-bg.png'
-</script>
-
-<style scoped lang="scss">
-.empty-state {
- display: flex;
- align-items: center;
- justify-content: center;
- flex: 1;
- width: 100%;
- height: 100%;
- min-height: 220px;
-
- .empty-content {
- display: flex;
- align-items: center;
- justify-content: center;
- flex-direction: column;
- text-align: center;
- }
-
- img {
- max-width: 78%;
- max-height: 78%;
- object-fit: contain;
- }
-
- .empty-text {
- font-family: Source Han Sans CN, Source Han Sans CN;
- font-weight: 400;
- font-size: 14px;
- color: #9E9EBA;
- line-height: 18px;
- text-align: center;
- font-style: normal;
- text-transform: none;
- }
-}
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/EquipmentWarning.vue b/applications/task-work-order/src/views/dataCockpit/components/EquipmentWarning.vue
deleted file mode 100644
index ddbd81d..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/EquipmentWarning.vue
+++ /dev/null
@@ -1,101 +0,0 @@
-<!--
- * @Author : yuan
- * @Date : 2026-01-08 09:29:07
- * @LastEditors : yuan
- * @LastEditTime : 2026-01-08 14:49:25
- * @FilePath : \applications\drone-command\src\views\dataCockpit\components\EquipmentWarning.vue
- * @Description :
- * Copyright 2026 OBKoro1, All Rights Reserved.
- * 2026-01-08 09:29:07
--->
-<template>
- <div
- class="equipment-warning"
- v-loading="loading"
- element-loading-background="rgba(5, 5, 15, 0.6)"
- element-loading-text="加载中..."
- >
- <EmptyState v-if="!equipmentWarningData.length" />
- <EquipmentTemplate
- v-else
- v-for="(item, ind) in equipmentWarningData"
- :key="ind"
- :data="item"
- @click="onCardClick"
- />
- </div>
-</template>
-
-<script setup>
-import { onMounted, ref } from 'vue'
-import EquipmentTemplate from './templateComponents/EquipmentTemplate.vue'
-import { deviceSearchApi } from '@/api/dataCockpit'
-import EmptyState from './EmptyState.vue'
-
-const equipmentWarningData = ref([])
-const loading = ref(true)
-const minLoadingMs = 400
-
-const statusMap = {
- 0: { text: '在线', statusType: 'online' },
- 1: { text: '离线', statusType: 'offline' },
- 2: { text: '故障', statusType: 'fault' },
- 3: { text: '报废', statusType: 'offline' }
-}
-const deviceTypeMap = {
- 1: '便捷侦测箱',
- 2: '反制枪',
- 3: '察打一体'
-}
-
-const formatDeviceItem = (item) => {
- const statusInfo = statusMap[item?.status] || { text: '-', statusType: '' }
- return {
- id: item.id,
- name: item.deviceName || item.deviceModel || '-',
- status: statusInfo.text,
- statusType: statusInfo.statusType,
- type: deviceTypeMap[item.deviceType] || item.deviceType || '-',
- manufacturer: item.manufacturer || '-',
- azimuth: item.azimuth ?? 0,
- pitch: item.elevation ?? 0,
- range: item.effectiveRangeKm ?? 0,
- model: item.deviceModel || '-',
- longitude: item.longitude ?? '-',
- latitude: item.latitude ?? '-'
- }
-}
-
-const fetchDeviceWarning = async () => {
- const startAt = Date.now()
- loading.value = true
- try {
- const params = { current: 1, size: 20, deviceStatusList: '1,2,3' }
- const res = await deviceSearchApi(params)
- const records = res?.data?.data?.records ?? []
- equipmentWarningData.value = records.map(formatDeviceItem)
- } finally {
- const elapsed = Date.now() - startAt
- if (elapsed < minLoadingMs) {
- await new Promise((resolve) => setTimeout(resolve, minLoadingMs - elapsed))
- }
- loading.value = false
- }
-}
-
-onMounted(() => {
- fetchDeviceWarning()
-})
-
-const onCardClick = (item) => {
- console.log('点击设备卡片:', item.id)
-}
-</script>
-
-<style lang="scss" scoped>
-.equipment-warning {
- width: 100%;
- height: 100%;
-}
-
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/HistoryWarning.vue b/applications/task-work-order/src/views/dataCockpit/components/HistoryWarning.vue
deleted file mode 100644
index 79160f1..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/HistoryWarning.vue
+++ /dev/null
@@ -1,123 +0,0 @@
-<template>
- <div
- class="history-warning"
- v-loading="loading"
- element-loading-background="rgba(5, 5, 15, 0.6)"
- element-loading-text="加载中..."
- >
- <!-- 搜索区(设计稿红框) -->
- <div class="search-box">
- <!-- 关键字 -->
- <el-input v-model="query.keyword" class="gd-input" placeholder="请输入数据名称" clearable
- @keyup.enter="onSearch">
- </el-input>
-
- <!-- 日期范围 -->
- <el-date-picker v-model="query.dateRange"
- class="gd-date-picker"
- popper-class="gd-date-picker-popper"
- type="daterange"
- range-separator="~"
- start-placeholder="创建开始日期" end-placeholder="结束日期" format="YYYY年MM月DD日" value-format="YYYY-MM-DD"
- :clearable="true" @change="onSearch">
- </el-date-picker>
- </div>
-
- <!-- 列表 -->
- <div class="list-content">
- <EmptyState v-if="!filteredList.length" />
- <HistoryTemplate
- v-else
- v-for="(item, ind) in filteredList"
- :key="item.id || ind"
- :data="item"
- @click="onCardClick"
- />
- </div>
- </div>
-</template>
-
-<script setup>
-import { ref, computed, onMounted } from 'vue'
-import HistoryTemplate from './templateComponents/HistoryTemplate.vue'
-import { alarmLogApi } from '@/api/dataCockpit'
-import EmptyState from './EmptyState.vue'
-
-const historyWarningData = ref([])
-const loading = ref(true)
-const minLoadingMs = 400
-
-// 查询条件:keyword + dateRange(YYYY-MM-DD数组)
-const query = ref({
- keyword: '',
- dateRange: [] // [start, end]
-})
-
-const onSearch = () => {
- fetchHistoryWarning()
-}
-
-const onCardClick = (item) => {
- console.log('点击无人机卡片:', item.id)
-}
-
-const fetchHistoryWarning = async () => {
- const startAt = Date.now()
- loading.value = true
- try {
- const [startTime, endTime] = query.value.dateRange || []
- const keyword = (query.value.keyword || '').trim()
- const params = {
- current: 1,
- size: 20,
- alarmType: '2',
- startTime,
- endTime,
- droneName: keyword || undefined,
- droneSerialNo: keyword || undefined
- }
- const res = await alarmLogApi(params)
- const records = res?.data?.data?.records ?? []
- historyWarningData.value = records
- } finally {
- const elapsed = Date.now() - startAt
- if (elapsed < minLoadingMs) {
- await new Promise((resolve) => setTimeout(resolve, minLoadingMs - elapsed))
- }
- loading.value = false
- }
-}
-
-const filteredList = computed(() => {
- return historyWarningData.value
-})
-
-onMounted(() => {
- fetchHistoryWarning()
-})
-</script>
-
-<style scoped lang="scss">
-.history-warning {
- display: flex;
- flex-direction: column;
- width: 100%;
- height: 100%;
-
- .search-box {
- display: flex;
- flex-direction: column;
- gap: 10px;
-
- ::v-deep(.gd-date-picker) {
- width: 100% !important;
- }
- }
-
- .list-content {
- height: 0;
- flex: 1;
- }
-}
-
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/LeftContainer.vue b/applications/task-work-order/src/views/dataCockpit/components/LeftContainer.vue
deleted file mode 100644
index 7b54029..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/LeftContainer.vue
+++ /dev/null
@@ -1,219 +0,0 @@
-<template>
- <div class="left-container" :class="{ collapsed: collapsedModel }">
- <div class="wrapper">
- <TitleTemplate>异常情况告警</TitleTemplate>
-
- <div class="category-container">
- <div class="category-item" v-for="(item, ind) in categoryList" :key="ind"
- @click="activeIdModel = item.id" :class="{ active: activeIdModel === item.id }">
- {{ item.name }}
- <span class="badge" v-if="item.badge">{{ item.badge }}</span>
- </div>
- </div>
-
- <div class="content">
- <component :is="currentComponent" />
- </div>
- </div>
-
- <div class="collapse-btn" @click="toggleCollapse">
- <span class="arrow" :class="collapsedModel ? 'right' : 'left'"></span>
- </div>
- </div>
-</template>
-
-<script setup>
-import TitleTemplate from './templateComponents/TitleTemplate.vue'
-import RealWarning from './RealWarning.vue'
-import EquipmentWarning from './EquipmentWarning.vue'
-import HistoryWarning from './HistoryWarning.vue'
-import { alarmStatisticsApi } from '@/api/dataCockpit'
-import { onMounted } from 'vue'
-
-const props = defineProps({
- activeId: {
- type: Number,
- default: 1
- },
- collapsed: {
- type: Boolean,
- default: false
- }
-})
-
-const emit = defineEmits(['update:activeId', 'update:collapsed'])
-
-const activeIdModel = computed({
- get: () => props.activeId,
- set: (val) => emit('update:activeId', val)
-})
-
-const collapsedModel = computed({
- get: () => props.collapsed,
- set: (val) => emit('update:collapsed', val)
-})
-
-const categoryList = ref([
- {
- id: 1,
- name: '实时告警',
- badge: 0,
- component: RealWarning
- },
- {
- id: 2,
- name: '设备告警',
- badge: 0,
- component: EquipmentWarning
- },
- {
- id: 3,
- name: '历史告警',
- badge: 0,
- component: HistoryWarning
- }
-])
-
-const currentComponent = computed(() => {
- return categoryList.value.find(i => i.id === activeIdModel.value)?.component
-})
-
-const toggleCollapse = () => {
- collapsedModel.value = !collapsedModel.value
-}
-
-const updateBadges = async () => {
- const res = await alarmStatisticsApi()
- const data = res?.data?.data || {}
- const realTotal = data.realTimeAlarmCount ?? 0
- const historyTotal = data.historyAlarmCount ?? 0
- const deviceTotal = data.deviceAlarmCount ?? 0
- categoryList.value = categoryList.value.map((item) => {
- if (item.id === 1) return { ...item, badge: realTotal }
- if (item.id === 2) return { ...item, badge: deviceTotal }
- if (item.id === 3) return { ...item, badge: historyTotal }
- return item
- })
-}
-
-onMounted(() => {
- updateBadges()
-})
-</script>
-
-<style lang="scss" scoped>
-.left-container {
- position: relative;
- transition: transform 0.3s ease-in-out;
-
- &.collapsed {
- transform: translateX(-100%);
- }
-}
-
-.collapse-btn {
- position: absolute;
- top: 50%;
- left: 357px;
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- color: #fff;
- z-index: 10;
- transform: translateY(-50%);
-
- .arrow {
- width: 24px;
- height: 24px;
- background: url('@/assets/images/dataCockpit/left-btn.png') center / 100% 100% no-repeat;
- }
-
- &:hover {
- background: rgba(40, 79, 227, 0.6);
- }
-}
-
-.category-container {
- position: relative;
- display: flex;
- justify-content: space-between;
-
- &::after {
- content: '';
- position: absolute;
- left: 0;
- bottom: 0;
- width: 100%;
- height: 1px;
- background: #333355;
- }
-
- .category-item {
- position: relative;
- width: 0;
- flex: 1;
- margin-left: 26px;
- height: 68px;
- line-height: 68px;
-
- font-family: Source Code Pro, Source Code Pro;
- font-weight: 400;
- font-size: 14px;
- color: #86909C;
- text-align: center;
- font-style: normal;
- text-transform: none;
- cursor: pointer;
-
- &:first-child {
- margin-left: 0;
- }
-
- &.active {
- font-weight: normal;
- color: #FFFFFF;
- }
-
- &::after {
- content: '';
- position: absolute;
- left: 0;
- bottom: 0;
- width: 100%;
- height: 0;
- }
-
- &.active::after {
- height: 3px;
- background: #284FE3;
- box-shadow: 0px 3px 2px 0px rgba(15, 89, 255, 0.1), 0px 7px 5px 0px rgba(15, 89, 255, 0.15), 0px 13px 10px 0px rgba(15, 89, 255, 0.18), 0px 22px 18px 0px rgba(15, 89, 255, 0.21), 0px 42px 33px 0px rgba(15, 89, 255, 0.26), 0px 100px 80px 0px rgba(15, 89, 255, 0.36);
- border-radius: 5px 5px 0px 0px;
- z-index: 9;
- }
-
- .badge {
- position: absolute;
- top: 16px;
- right: 4px;
- min-width: 18px;
- line-height: 18px;
- font-size: 12px;
- color: #FFFFFF;
- background: #FF3B30;
- border-radius: 50%;
- text-align: center;
- box-shadow: 0px 2px 6px rgba(255, 59, 48, 0.6);
- pointer-events: none;
- }
- }
-}
-
-.content {
- height: 0;
- flex: 1;
- margin-top: 20px;
- overflow-x: hidden;
- overflow-y: auto;
-}
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/RealWarning.vue b/applications/task-work-order/src/views/dataCockpit/components/RealWarning.vue
deleted file mode 100644
index 607b94d..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/RealWarning.vue
+++ /dev/null
@@ -1,109 +0,0 @@
-<!--
- * @Author : yuan
- * @Date : 2026-01-08 09:29:07
- * @LastEditors : yuan
- * @LastEditTime : 2026-01-08 14:49:09
- * @FilePath : \applications\drone-command\src\views\dataCockpit\components\RealWarning.vue
- * @Description :
- * Copyright 2026 OBKoro1, All Rights Reserved.
- * 2026-01-08 09:29:07
--->
-<template>
- <div
- class="real-warning"
- v-loading="loading"
- element-loading-background="rgba(5, 5, 15, 0.6)"
- element-loading-text="加载中..."
- >
- <EmptyState v-if="!realWarningData.length" />
- <RealTemplate
- v-else
- v-for="(item, ind) in realWarningData"
- :key="ind"
- :data="item"
- @signal="() => handleAction('signal', item)"
- @counter="() => handleAction('counter', item)"
- @favorite="() => console.log('收藏', item.id)"
- />
- </div>
-</template>
-
-<script setup>
-import RealTemplate from './templateComponents/RealTemplate.vue'
-import { alarmLogApi, interferenceAndExpulsionApi } from '@/api/dataCockpit'
-import { onMounted, ref } from 'vue'
-import EmptyState from './EmptyState.vue'
-import { ElMessage } from 'element-plus'
-
-const realWarningData = ref([])
-const loading = ref(true)
-const minLoadingMs = 400
-
-const actionTextMap = {
- signal: '信号干扰',
- counter: '诱导驱离'
-}
-
-const buildPayload = (item) => ({
- alarmRecordId: item.id,
- areaCode: item.areaCode,
- counterEffect: '',
- coverRadiusM: item.coverRadiusM,
- deployLatitude: item.latitude,
- deployLongitude: item.longitude,
- deviceCode: item.deviceCode,
- deviceId: item.deviceId,
- deviceModel: item.deviceModel,
- deviceName: item.deviceName,
- deviceType: item.deviceType,
- droneDeviceCode: item.droneSerialNo,
- droneName: item.droneName,
- droneType: item.droneType,
- findTime: item.alarmTime,
- id: item.id,
- workMode: ''
-})
-
-const handleAction = async (type, item) => {
- const actionText = actionTextMap[type] || '操作'
- try {
- const res = await interferenceAndExpulsionApi(buildPayload(item))
- if (res?.data?.success) {
- ElMessage({ type: 'success', message: `${actionText}成功` })
- } else {
- ElMessage({ type: 'error', message: res?.data?.msg || `${actionText}失败` })
- }
- } catch (error) {
- ElMessage({ type: 'error', message: `${actionText}失败` })
- }
-}
-
-const fetchRealWarning = async () => {
- const startAt = Date.now()
- loading.value = true
- try {
- const params = { current: 1, size: 20, alarmType: '1' }
- const res = await alarmLogApi(params)
- const records = res?.data?.data?.records ?? []
- realWarningData.value = records
- } finally {
- const elapsed = Date.now() - startAt
- if (elapsed < minLoadingMs) {
- await new Promise((resolve) => setTimeout(resolve, minLoadingMs - elapsed))
- }
- loading.value = false
- }
-}
-
-onMounted(() => {
- fetchRealWarning()
-})
-</script>
-
-<style lang="scss" scoped>
-.real-warning {
- width: 100%;
- height: 100%;
-}
-
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/RightContainer.vue b/applications/task-work-order/src/views/dataCockpit/components/RightContainer.vue
deleted file mode 100644
index b014989..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/RightContainer.vue
+++ /dev/null
@@ -1,288 +0,0 @@
-<!--
- * @Author : yuan
- * @Date : 2026-01-07 15:17:54
- * @LastEditors : yuan
- * @LastEditTime : 2026-01-09 16:54:31
- * @FilePath : \applications\drone-command\src\views\dataCockpit\components\RightContainer.vue
- * @Description :
- * Copyright 2026 OBKoro1, All Rights Reserved.
- * 2026-01-07 15:17:54
--->
-<template>
- <div class="right-container" :class="{ collapsed: collapsedModel }">
- <div class="wrapper">
- <TitleTemplate>侦测反制设备</TitleTemplate>
-
- <div class="category-container">
- <div class="category-item" v-for="(item, ind) in categoryList" :key="ind">
- <div class="category-item-logo">
- <img :src="item.logo" alt="">
- </div>
-
- <div class="category-item-content">
- <span class="name">{{ item.name }}</span>
- <span class="val">{{ item.val }}</span>
- </div>
- </div>
- </div>
-
- <div
- class="content"
- v-loading="loading"
- element-loading-background="rgba(5, 5, 15, 0.6)"
- element-loading-text="加载中..."
- >
- <EmptyState v-if="!counterDeviceStatusList.length" />
- <RealEquipmentTemplate
- v-else
- v-for="item in counterDeviceStatusList"
- :key="item.id"
- :data="item"
- @history="onHistory"
- @click="onCardClick"
- />
- </div>
- </div>
-
- <div class="collapse-btn" @click="toggleCollapse">
- <span class="arrow" :class="collapsedModel ? 'left' : 'right'"></span>
- </div>
-
- <DeviceHistoryDialog v-model="historyVisible" :device="historyDevice" />
- </div>
-</template>
-
-<script setup>
-import TitleTemplate from './templateComponents/TitleTemplate.vue'
-import RealEquipmentTemplate from './templateComponents/RealEquipmentTemplate.vue'
-import DeviceHistoryDialog from './DeviceHistoryDialog.vue'
-import EmptyState from './EmptyState.vue'
-import zcsbLogo from '@/assets/images/dataCockpit/zcsb.png'
-import fzsbLogo from '@/assets/images/dataCockpit/fzsb.png'
-import { deviceSearchApi, deviceStatisticsApi } from '@/api/dataCockpit'
-
-const props = defineProps({
- collapsed: {
- type: Boolean,
- default: false
- }
-})
-
-const emit = defineEmits(['update:collapsed', 'update:onlineDevices'])
-
-const collapsedModel = computed({
- get: () => props.collapsed,
- set: (val) => emit('update:collapsed', val)
-})
-
-const categoryList = ref([
- {
- id: 1,
- name: '侦测设备',
- val: 0,
- logo: zcsbLogo
- },
- {
- id: 2,
- name: '反制设备',
- val: 0,
- logo: fzsbLogo
- }
-])
-
-const counterDeviceStatusList = ref([])
-
-const historyVisible = ref(false)
-const historyDevice = ref(null)
-const loading = ref(true)
-const minLoadingMs = 400
-
-const onHistory = (item) => {
- historyDevice.value = item
- historyVisible.value = true
- console.log('历史数据:', item.id)
-}
-
-const onCardClick = (item) => {
- console.log('点击卡片:', item.id)
-}
-const toggleCollapse = () => {
- collapsedModel.value = !collapsedModel.value
-}
-
-const statusMap = {
- 0: { text: '在线', actionType: 'detecting' },
- 1: { text: '离线', actionType: '' },
- 2: { text: '故障', actionType: 'jamming' },
- 3: { text: '报废', actionType: '' }
-}
-const deviceTypeMap = {
- 1: '便捷侦测箱',
- 2: '反制枪',
- 3: '察打一体'
-}
-
-const formatDeviceItem = (item) => {
- const statusInfo = statusMap[item?.status] || { text: '-', actionType: '' }
- return {
- id: item.id,
- name: item.deviceName || item.deviceModel || '-',
- actionText: statusInfo.text,
- actionType: statusInfo.actionType,
- type: deviceTypeMap[item.deviceType] || item.deviceType || '-',
- batteryPct: item.batteryPct ?? 0,
- azimuth: item.azimuth ?? 0,
- elevation: item.elevation ?? 0,
- status: item.status ?? '-',
- manufacturer: item.manufacturer || '-',
- detectTargetCnt: item.detectTargetCnt ?? 0,
- effectiveRangeKm: item.effectiveRangeKm ?? 0
- }
-}
-
-const fetchDeviceList = async () => {
- const startAt = Date.now()
- loading.value = true
- try {
- const params = { current: 1, size: 20, deviceStatusList: '0' }
- const res = await deviceSearchApi(params)
- const records = res?.data?.data?.records ?? []
- counterDeviceStatusList.value = records.map(formatDeviceItem)
- emit('update:onlineDevices', records)
- } finally {
- const elapsed = Date.now() - startAt
- if (elapsed < minLoadingMs) {
- await new Promise((resolve) => setTimeout(resolve, minLoadingMs - elapsed))
- }
- loading.value = false
- }
-}
-
-onMounted(() => {
- fetchDeviceList()
- fetchDeviceStatistics()
-})
-
-const normalizeType = (value) => {
- if (value === 1 || value === '1') return '侦测设备'
- if (value === 2 || value === '2') return '反制设备'
- if (value === 'detect') return '侦测设备'
- if (value === 'counter') return '反制设备'
- return value
-}
-
-const fetchDeviceStatistics = async () => {
- const res = await deviceStatisticsApi()
- const list = res?.data?.data ?? []
- const map = new Map(list.map((item) => [normalizeType(item.type), item.count]))
- categoryList.value = categoryList.value.map((item) => ({
- ...item,
- val: map.has(item.name) ? map.get(item.name) : item.val
- }))
-}
-</script>
-
-<style lang="scss" scoped>
-.category-container {
- margin-top: 20px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 28px;
- box-sizing: border-box;
-
- .category-item {
- position: relative;
- width: 0;
- flex: 1;
- display: flex;
- height: 64px;
-
- &-logo {
- display: flex;
- align-items: center;
-
- img {
- width: 40px;
- height: 40px;
- }
- }
-
- &-content {
- width: 0;
- flex: 1;
- display: flex;
- flex-direction: column;
- border-bottom: 1px solid #6C6C89;
-
- span {
- margin-left: 8px;
- }
-
- .name {
- font-family: Open Sans, Open Sans;
- font-weight: 400;
- font-size: 16px;
- color: #C3C3DD;
- line-height: 20px;
- text-align: left;
- font-style: normal;
- text-transform: none;
- }
-
- .val {
- margin-top: 10px;
- font-family: DIN, DIN;
- font-weight: bold;
- font-size: 22px;
- color: #FFFFFF;
- line-height: 20px;
- text-align: left;
- font-style: normal;
- text-transform: none;
- }
- }
- }
-}
-
-.content {
- height: 0;
- flex: 1;
- margin-top: 20px;
- overflow-x: hidden;
- overflow-y: auto;
-}
-
-
-.right-container {
- transition: transform 0.3s ease-in-out;
- position: relative;
-
- &.collapsed {
- transform: translateX(100%);
- }
-}
-
-.collapse-btn {
- position: absolute;
- top: 50%;
- right: 357px;
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- color: #fff;
- z-index: 10;
- transform: translateY(-50%);
-
- .arrow {
- width: 24px;
- height: 24px;
- background: url('@/assets/images/dataCockpit/right-btn.png') center / 100% 100% no-repeat;
- }
-
- &:hover {
- background: rgba(40, 79, 227, 0.6);
- }
-}
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/EquipmentTemplate.vue b/applications/task-work-order/src/views/dataCockpit/components/templateComponents/EquipmentTemplate.vue
deleted file mode 100644
index 7b09cfb..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/EquipmentTemplate.vue
+++ /dev/null
@@ -1,192 +0,0 @@
-<template>
- <div class="device-card" @click="emit('click', data)">
- <div class="header">
- <div class="title">
- <el-tooltip
- class="title-tooltip"
- :content="data.name || '-'"
- placement="top"
- effect="dark"
- :show-after="200"
- >
- <span class="name">{{ data.name }}</span>
- </el-tooltip>
-
- <span class="status">
- <span :class="data.statusType">
-
- </span>
-
- <span>
- {{ data.status }}
- </span>
- </span>
- </div>
- </div>
-
- <div class="content">
- <div class="row">
- <div class="col">
- <span class="label">类型</span>
- {{ data.type }}
- </div>
- <div class="col">
- <span class="label">厂商</span>
- {{ data.manufacturer }}
- </div>
- </div>
- <div class="row">
- <div class="col">
- <span class="label">方位角</span>
- {{ data.azimuth }}°
- </div>
- <div class="col">
- <span class="label">俯仰角</span>
- {{ data.pitch }}°
- </div>
- </div>
- <div class="row">
- <div class="col">
- <span class="label">有效范围</span>
- {{ data.range }}
- </div>
- <div class="col">
- <span class="label">设备型号</span>
- {{ data.model }}
- </div>
- </div>
-
- <div>
- <span class="label">部署位置</span>
- <span class="value">{{ data.longitude }},{{ data.latitude }}</span>
- </div>
- </div>
- </div>
-</template>
-
-<script setup>
-const props = defineProps({
- data: {
- type: Object,
- required: true,
- default: () => ({})
- }
-})
-
-const emit = defineEmits(['click'])
-</script>
-
-<style scoped lang="scss">
-.device-card {
- margin-top: 10px;
- padding: 10px;
- color: #fff;
- background: #191933;
- border-radius: 6px 6px 6px 6px;
-
- &:first-child {
- margin-top: 0;
- }
-
- .header {
- display: flex;
- justify-content: space-between;
- align-items: center;
-
- .title {
- display: flex;
- align-items: center;
- gap: 6px;
- min-width: 0;
-
- .title-tooltip {
- flex: 1;
- min-width: 0;
- display: block;
- }
-
- .name {
- display: inline-block;
- max-width: 100%;
- font-family: Source Han Sans CN, Source Han Sans CN;
- font-weight: bold;
- font-size: 14px;
- color: #FFFFFF;
- text-align: left;
- font-style: normal;
- text-transform: none;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
-
- .status {
- flex-shrink: 0;
- padding: 0 10px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- height: 21px;
-
- background: #303041;
- border-radius: 10px;
- gap: 6px;
-
- span {
- font-family: Source Han Sans CN, Source Han Sans CN;
- font-weight: 400;
- font-size: 10px;
- color: #FFFFFF;
- text-align: center;
- font-style: normal;
- text-transform: none;
- }
-
-
- span.offline {
- width: 4px;
- height: 4px;
- background: #939393;
- border-radius: 50%;
- }
-
- span.fault {
- width: 4px;
- height: 4px;
- background: #FF4444;
- border-radius: 50%;
- }
- }
- }
- }
-
- .content {
- font-family: Source Han Sans CN, Source Han Sans CN;
- font-weight: 400;
- font-size: 12px;
- color: #9E9EBA;
- text-align: left;
- font-style: normal;
- text-transform: none;
-
- &>div {
- margin-top: 10px;
- line-height: 22px;
-
- span.label {
- margin-right: 10px;
- color: #D4D5D7;
- }
- }
-
- .row {
- display: flex;
-
- &>div {
- width: 0;
- flex: 1;
- }
- }
- }
-}
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/HistoryTemplate.vue b/applications/task-work-order/src/views/dataCockpit/components/templateComponents/HistoryTemplate.vue
deleted file mode 100644
index 66a633d..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/HistoryTemplate.vue
+++ /dev/null
@@ -1,169 +0,0 @@
-<template>
- <div class="drone-info-card" @click="emit('click', data)">
- <div class="title">
- <el-tooltip
- class="title-tooltip"
- :content="data.droneName || '-'"
- placement="top"
- effect="dark"
- :show-after="200"
- >
- <span class="name">
- {{ data.droneName }}
- </span>
- </el-tooltip>
- </div>
-
- <div class="rows">
- <div class="row">
- <span class="label">序列号</span>
- <span class="value">{{ data.droneSerialNo }}</span>
- </div>
-
- <div class="row">
- <span class="label">数据源</span>
- <span class="value">{{ getDataSource(data) }}</span>
- </div>
-
- <div class="row">
- <span class="label">信号频段</span>
- <span class="value">{{ formatFrequency(data.signalFreqMhz) }}</span>
- </div>
-
- <div class="row">
- <span class="label">发现时间</span>
- <span class="value">
- <span class="date">{{ getAlarmDate(data.alarmTime) }}</span>
- <span class="time">{{ getAlarmTime(data.alarmTime) }}</span>
- </span>
- </div>
-
- <div class="row">
- <span class="label">停留时间</span>
- <span class="value">{{ data.stayDuration }}</span>
- </div>
-
- <div class="row">
- <span class="label">反制方式</span>
- <span class="value">{{ getCounterWayText(data.counterWay) }}</span>
- </div>
-
- <div class="row">
- <span class="label">飞行状态</span>
- <span class="value">{{ getFlightStatusText(data.flightStatus) }}</span>
- </div>
- </div>
- </div>
-</template>
-
-<script setup>
-defineProps({
- data: {
- type: Object,
- required: true,
- default: () => ({})
- }
-})
-
-const emit = defineEmits(['click'])
-
-const getDataSource = (item) => {
- return item?.deviceName || item?.areaName || '-'
-}
-
-const formatFrequency = (value) => {
- if (value == null || value === '') return '-'
- return `${value}MHZ`
-}
-
-const getAlarmDate = (value) => {
- if (!value) return '-'
- const parts = value.replace('T', ' ').split(' ')
- return parts[0] || value
-}
-
-const getAlarmTime = (value) => {
- if (!value) return '-'
- const parts = value.replace('T', ' ').split(' ')
- return parts[1] || value
-}
-
-const getFlightStatusText = (value) => {
- if (value === 1 || value === '1') return '侦测中'
- if (value === 2 || value === '2') return '反制中'
- return value || '-'
-}
-
-const getCounterWayText = (value) => {
- if (value === 1 || value === '1') return '信号干扰'
- if (value === 2 || value === '2') return '诱导驱离'
- if (value === 3 || value === '3') return '无'
- return value || '-'
-}
-</script>
-
-<style scoped lang="scss">
-.drone-info-card {
- margin-top: 10px;
- padding: 10px;
- color: #fff;
- background: #191933;
- border-radius: 6px 6px 6px 6px;
-
- &:hover {
- filter: brightness(1.05);
- }
-
- .title {
- display: flex;
- align-items: center;
- gap: 6px;
- min-width: 0;
-
- .title-tooltip {
- flex: 1;
- min-width: 0;
- display: block;
- }
-
- .name {
- display: inline-block;
- max-width: 100%;
- font-family: Source Han Sans CN, Source Han Sans CN;
- font-weight: bold;
- font-size: 14px;
- color: #FFFFFF;
- text-align: left;
- font-style: normal;
- text-transform: none;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- }
-
- .rows {
- margin-top: 10px;
- display: flex;
- flex-direction: column;
- gap: 10px;
-
- font-family: Source Han Sans CN, Source Han Sans CN;
- font-weight: 400;
- font-size: 12px;
- color: #9E9EBA;
- text-align: left;
- font-style: normal;
- text-transform: none;
-
- .row {
- line-height: 22px;
-
- .label {
- margin-right: 10px;
- color: #D4D5D7;
- }
- }
- }
-}
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue b/applications/task-work-order/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue
deleted file mode 100644
index 559b6e5..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue
+++ /dev/null
@@ -1,234 +0,0 @@
-<template>
- <div class="counter-card" @click="emit('click', data)">
- <!-- 头部 -->
- <div class="header">
- <div class="title">
- <el-tooltip
- class="title-tooltip"
- :content="data.name || '-'"
- placement="top"
- effect="dark"
- :show-after="200"
- >
- <span class="name">{{ data.name }}</span>
- </el-tooltip>
-
- <span class="status">
- <span :class="getStatusType(data.status)">
-
- </span>
-
- <span>
- {{ getStatusText(data.status) }}
- </span>
- </span>
- </div>
- </div>
-
- <!-- 字段区:两列 -->
- <div class="content">
- <div class="row">
- <div class="col">
- <span class="label">类型</span>
- {{ data.type }}
- </div>
- <div class="col">
- <span class="label">电量</span>
- {{ data.batteryPct }}%
- </div>
- </div>
-
- <div class="row">
- <div class="col">
- <span class="label">方位角</span>
- {{ data.azimuth }}°
- </div>
- <div class="col">
- <span class="label">俯仰角</span>
- {{ data.elevation }}°
- </div>
- </div>
-
- <div class="row">
- <div class="col">
- <span class="label">状态</span>
- {{ getStatusText(data.status) }}
- </div>
- <div class="col">
- <span class="label">厂商</span>
- {{ data.manufacturer }}
- </div>
- </div>
-
- <div class="row">
- <div class="col">
- <span class="label">侦测目标</span>
- {{ data.detectTargetCnt }}台
- </div>
- <div class="col">
- <span class="label">有效范围</span>
- {{ data.effectiveRangeKm }}KM
- </div>
- </div>
- </div>
-
- <!-- 底部按钮 -->
- <div class="footer">
- <el-button color="#284FE3" type="primary" @click.stop="emit('history', data)">历史数据</el-button>
- </div>
- </div>
-</template>
-
-<script setup>
-defineProps({
- data: {
- type: Object,
- required: true,
- default: () => ({})
- }
-})
-
-const emit = defineEmits(['history', 'click'])
-
-const getStatusText = (value) => {
- if (value === 0 || value === '0') return '在线'
- if (value === 1 || value === '1') return '离线'
- if (value === 2 || value === '2') return '故障'
- if (value === 3 || value === '3') return '报废'
- return value || '-'
-}
-
-const getStatusType = (value) => {
- if (value === 0 || value === '0') return 'detecting'
- if (value === 2 || value === '2') return 'jamming'
- return ''
-}
-</script>
-
-<style scoped lang="scss">
-.counter-card {
- margin-top: 10px;
- padding: 10px;
- color: #fff;
- background: #191933;
- border-radius: 6px 6px 6px 6px;
-
- &:first-child {
- margin-top: 0;
- }
-
- .header {
- display: flex;
- justify-content: space-between;
- align-items: center;
-
- .title {
- display: flex;
- align-items: center;
- gap: 6px;
- min-width: 0;
-
- .title-tooltip {
- flex: 1;
- min-width: 0;
- display: block;
- }
-
- .name {
- display: inline-block;
- max-width: 100%;
- font-family: Source Han Sans CN, Source Han Sans CN;
- font-weight: bold;
- font-size: 14px;
- color: #FFFFFF;
- text-align: left;
- font-style: normal;
- text-transform: none;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
-
- .status {
- flex-shrink: 0;
- padding: 0 10px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- height: 21px;
-
- background: #303041;
- border-radius: 10px;
- gap: 6px;
-
- span {
- font-family: Source Han Sans CN, Source Han Sans CN;
- font-weight: 400;
- font-size: 10px;
- color: #FFFFFF;
- text-align: center;
- font-style: normal;
- text-transform: none;
- }
-
-
- span.detecting {
- width: 4px;
- height: 4px;
- background: #18FF4A;
- border-radius: 50%;
- }
-
- span.jamming {
- width: 4px;
- height: 4px;
- background: #FF4444;
- border-radius: 50%;
- }
- }
- }
- }
-
- .content {
- font-family: Source Han Sans CN, Source Han Sans CN;
- font-weight: 400;
- font-size: 12px;
- color: #9E9EBA;
- text-align: left;
- font-style: normal;
- text-transform: none;
-
- &>div {
- margin-top: 10px;
- line-height: 22px;
-
- span.label {
- margin-right: 10px;
- color: #D4D5D7;
- }
- }
-
- .row {
- display: flex;
-
- &>div {
- width: 0;
- flex: 1;
- }
- }
- }
-
- .footer {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 10px;
- margin-top: 10px;
-
- ::v-deep(.el-button) {
- width: 96px;
- height: 36px;
- }
- }
-}
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/RealTemplate.vue b/applications/task-work-order/src/views/dataCockpit/components/templateComponents/RealTemplate.vue
deleted file mode 100644
index 2e7c9a8..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/RealTemplate.vue
+++ /dev/null
@@ -1,266 +0,0 @@
-<template>
- <div class="drone-card">
- <div class="header">
- <div class="title">
- <el-tooltip
- class="title-tooltip"
- :content="data.droneName || '-'"
- placement="top"
- effect="dark"
- :show-after="200"
- >
- <span class="name">{{ data.droneName }}</span>
- </el-tooltip>
- <span class="status">
- <span :class="getStatusType(data)">
-
- </span>
-
- <span>
- {{ getStatusText(data) }}
- </span>
- </span>
- </div>
-
- <div class="actions">
- <span class="star" @click="emit('favorite')">
- {{ data.isFavorite ? '★' : '☆' }}
- </span>
- </div>
- </div>
-
- <div class="content">
- <div>
- <span class="label">序列号</span>
- {{ data.droneSerialNo }}
- </div>
- <div>
- <span class="label">数据源</span>
- {{ getDataSource(data) }}
- </div>
- <div>
- <span class="label">经纬度</span>
- {{ data.longitude }}, {{ data.latitude }}
- </div>
- <div class="row">
- <div class="col">
- <span class="label">信号频段</span>
- {{ formatFrequency(data.signalFreqMhz) }}
- </div>
- <div class="col">
- <span class="label">发现时间</span>
- {{ getAlarmTime(data.alarmTime) }}
- </div>
- </div>
- <div class="row">
- <div class="col">
- <span class="label">飞行高度</span>
- {{ formatHeight(data.flightHeightM) }}
- </div>
- <div class="col">
- <span class="label">飞行速度</span>
- {{ formatSpeed(data.flightSpeedMs) }}
- </div>
- </div>
- </div>
-
- <div class="footer">
- <el-button class="general" color="#2B2B4C" @click="emit('signal')">信号干扰</el-button>
- <el-button color="#284FE3" type="primary" @click="emit('counter')">诱导驱离</el-button>
- </div>
- </div>
-</template>
-
-<script setup>
-const props = defineProps({
- data: {
- type: Object,
- required: true,
- default: () => ({})
- }
-})
-
-const emit = defineEmits(['signal', 'counter', 'favorite'])
-
-const getStatusText = (item) => {
- const status = item?.flightStatus
- if (status === 1 || status === '1') return '侦测中'
- if (status === 2 || status === '2') return '反制中'
- return status || '侦测中'
-}
-
-const getStatusType = (item) => {
- const statusText = getStatusText(item)
- const counterWay = item?.counterWay
- if (statusText.includes('反制') || counterWay === 2 || counterWay === '2' || counterWay === '诱导驱离') {
- return 'countering'
- }
- return 'detecting'
-}
-
-const getDataSource = (item) => {
- return item?.deviceName || item?.areaName || '-'
-}
-
-const formatFrequency = (value) => {
- if (value == null || value === '') return '-'
- return `${value}MHz`
-}
-
-const getAlarmTime = (value) => {
- if (!value) return '-'
- const parts = value.replace('T', ' ').split(' ')
- return parts[1] || value
-}
-
-const formatHeight = (value) => {
- if (value == null || value === '') return '-'
- return `${value}m`
-}
-
-const formatSpeed = (value) => {
- if (value == null || value === '') return '-'
- return `${value}m/s`
-}
-</script>
-
-<style scoped lang="scss">
-.drone-card {
- margin-top: 10px;
- padding: 10px;
- color: #fff;
- background: #191933;
- border-radius: 6px 6px 6px 6px;
-
- &:hover {
- background: #29294D;
- ;
- }
-
- &:first-child {
- margin-top: 0;
- }
-
- .header {
- display: flex;
- justify-content: space-between;
- align-items: center;
-
- .title {
- display: flex;
- align-items: center;
- gap: 6px;
- min-width: 0;
-
- .title-tooltip {
- flex: 1;
- min-width: 0;
- display: block;
- }
-
- .name {
- display: inline-block;
- max-width: 100%;
- font-family: Source Han Sans CN, Source Han Sans CN;
- font-weight: bold;
- font-size: 14px;
- color: #FFFFFF;
- text-align: left;
- font-style: normal;
- text-transform: none;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
-
- .status {
- flex-shrink: 0;
- padding: 0 10px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- height: 21px;
-
- background: #303041;
- border-radius: 10px;
- gap: 6px;
-
- span {
- font-family: Source Han Sans CN, Source Han Sans CN;
- font-weight: 400;
- font-size: 10px;
- color: #FFFFFF;
- text-align: center;
- font-style: normal;
- text-transform: none;
- }
-
-
- span.detecting {
- width: 4px;
- height: 4px;
- background: #18FF4A;
- border-radius: 50%;
- }
-
- span.countering {
- width: 4px;
- height: 4px;
- background: #FF4444;
- border-radius: 50%;
- }
- }
- }
-
- .actions {
- display: flex;
- gap: 8px;
- font-size: 16px;
- cursor: pointer;
- }
- }
-
- .content {
- font-family: Source Han Sans CN, Source Han Sans CN;
- font-weight: 400;
- font-size: 12px;
- color: #9E9EBA;
- text-align: left;
- font-style: normal;
- text-transform: none;
-
- &>div {
- margin-top: 10px;
- line-height: 22px;
-
- span.label {
- margin-right: 10px;
- color: #D4D5D7;
- }
- }
-
- .row {
- display: flex;
- justify-content: space-between;
- }
- }
-
- .footer {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 10px;
- margin-top: 10px;
-
- ::v-deep(.el-button) {
- width: 96px;
- height: 36px;
-
- &.general.el-button {
- border-radius: 4px 4px 4px 4px;
- border: 1px solid rgba(255, 255, 255, 0.5);
- }
- }
- }
-}
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/TitleTemplate.vue b/applications/task-work-order/src/views/dataCockpit/components/templateComponents/TitleTemplate.vue
deleted file mode 100644
index 9d3c9b0..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/TitleTemplate.vue
+++ /dev/null
@@ -1,43 +0,0 @@
-<!--
- * @Author : yuan
- * @Date : 2026-01-08 09:32:52
- * @LastEditors : yuan
- * @LastEditTime : 2026-01-08 09:43:00
- * @FilePath : \applications\drone-command\src\views\dataCockpit\components\templateComponents\TitleTemplate.vue
- * @Description :
- * Copyright 2026 OBKoro1, All Rights Reserved.
- * 2026-01-08 09:32:52
--->
-<template>
- <div class="title-box">
- <div class="title">
- <slot></slot>
- </div>
- </div>
-</template>
-
-<script setup>
-
-</script>
-
-<style lang="scss" scoped>
-.title-box {
- padding-left: 26px;
- background: url('@/assets/images/dataCockpit/title-bg.png') center / 100% 100% no-repeat;
- height: 34px;
-
- .title {
- height: 25px;
- line-height: 25px;
-
- font-family: PangMen;
- font-weight: 400;
- font-size: 18px;
- color: #FFFFFF;
- letter-spacing: 1px;
- text-align: left;
- font-style: normal;
- text-transform: none;
- }
-}
-</style>
\ No newline at end of file
diff --git a/applications/task-work-order/src/views/dataCockpit/index.vue b/applications/task-work-order/src/views/dataCockpit/index.vue
deleted file mode 100644
index 137328a..0000000
--- a/applications/task-work-order/src/views/dataCockpit/index.vue
+++ /dev/null
@@ -1,96 +0,0 @@
-<!--
- * @Author : yuan
- * @Date : 2026-01-06 16:35:50
- * @LastEditors : yuan
- * @LastEditTime : 2026-01-09 16:34:39
- * @FilePath : \applications\drone-command\src\views\dataCockpit\index.vue
- * @Description :
- * Copyright 2026 OBKoro1, All Rights Reserved.
- * 2026-01-06 16:35:50
--->
-<template>
- <div class="page-container">
- <DeviceMapContainer
- :online-devices="onlineDevices"
- :left-collapsed="leftCollapsed"
- container-id="data-cockpit-map"
- />
- <LeftContainer class="left-container" v-model:activeId="leftActiveId" v-model:collapsed="leftCollapsed" />
- <RightContainer class="right-container" v-model:collapsed="rightCollapsed" v-model:onlineDevices="onlineDevices" />
- <CenterContainer @select-warning="onSelectWarning" @select-equipment="onSelectEquipment" />
- </div>
-</template>
-
-<script setup>
- import DeviceMapContainer from '@/components/map-container/device-map-container.vue';
- import LeftContainer from './components/LeftContainer.vue';
- import RightContainer from './components/RightContainer.vue';
- import CenterContainer from './components/CenterContainer.vue';
-
- const leftActiveId = ref(1);
- const leftCollapsed = ref(false);
-
- const rightCollapsed = ref(false);
- const onlineDevices = ref([]);
-
- const onSelectWarning = (type) => {
- leftActiveId.value = type;
- leftCollapsed.value = false;
- };
-
- const onSelectEquipment = () => {
- rightCollapsed.value = false;
- };
-</script>
-
-<style scoped lang="scss">
- .page-container {
- position: relative;
- width: 100%;
- height: 100%;
- }
-
- .left-container,
- .right-container {
- position: absolute;
- top: 96px;
- bottom: 0;
- width: 401px;
- z-index: 9;
- backdrop-filter: blur(2px);
-
- ::v-deep(.wrapper) {
- display: flex;
- flex-direction: column;
- position: absolute;
- top: 17px;
- bottom: 20px;
- padding: 20px;
- width: 300px;
- background: rgba(0,0,0,0.3);
- backdrop-filter: blur(16px);
- border-radius: 10px 10px 10px 10px;
- }
- }
-
- .left-container {
- top: 96px;
- left: 0;
- background: linear-gradient( 270deg, rgba(17,23,34,0) 0%, rgba(17,23,34,0.56) 50%, rgba(17,23,34,0.96) 100%);
-
- ::v-deep(.wrapper) {
- left: 17px;
- }
- }
-
- .right-container {
- top: 54px;
- right: 0;
- background: linear-gradient( 270deg, rgba(17,23,34,0.96) 0.16%, rgba(17,23,34,0.56) 57.57%, rgba(4,30,37,0) 100%);
-
- ::v-deep(.wrapper) {
- top: 21px;
- right: 17px;
- }
- }
-</style>
diff --git a/applications/task-work-order/src/views/orderView/organizationalMessage/agenciesManagement/agenciesApi.js b/applications/task-work-order/src/views/orderView/organizationalMessage/agenciesManagement/agenciesApi.js
deleted file mode 100644
index 2f4dd8c..0000000
--- a/applications/task-work-order/src/views/orderView/organizationalMessage/agenciesManagement/agenciesApi.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import request from '@/axios'
-
-// 列表
-export const agenciesPageApi = params => {
- return request({
- url: `/blade-system/dept/page`,
- method: 'get',
- params: { ...params },
- })
-}
-
-// 新增或编辑
-export const agenciesSubmitApi = data => {
- return request({
- url: `/blade-system/dept/submit`,
- method: 'post',
- data,
- })
-}
-
-// 删除
-export const agenciesRemoveApi = params => {
- return request({
- url: `/blade-system/dept/remove`,
- method: 'post',
- params,
- })
-}
-
-// 详情
-export const agenciesDetailApi = params => {
- return request({
- url: `/blade-system/dept/detail`,
- method: 'get',
- params,
- })
-}
-
-// 导入机构
-export const agenciesImportApi = (data,params) => {
- return request({
- url: `/blade-system/dept/import-region`,
- method: 'post',
- data,
- params
- })
-}
-
-// 导出机构
-export const agenciesExportApi = params => {
- return request({
- url: `/blade-system/dept/export-region`,
- method: 'get',
- params,
- responseType: 'blob',
- })
-}
-
-// 懒加载列表
-export const regionLazyTreeApi = params => {
- return request({
- url: `/blade-system/region/lazy-tree`,
- method: 'get',
- params,
- })
-}
--
Gitblit v1.9.3