吉安感知网项目-前端
张含笑
2026-01-27 9e0f0ee575c7b490e768af0188a494eec7944492
Merge remote-tracking branch 'origin/master'
10 files modified
1 files added
716 ■■■■■ changed files
applications/drone-command/src/components/map-container/device-map-container.vue 250 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/FormDiaLog.vue 57 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/LeftContainer.vue 35 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/RealWarning.vue 130 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/index.vue 113 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/assets/images/orderView/orderManage/drone.png patch | view | raw | blame | history
applications/task-work-order/src/assets/images/orderView/orderManage/droneActive.png patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue 12 ●●●● patch | view | raw | blame | history
uniapps/work-wx/src/subPackages/deviceRegistration/details.vue 54 ●●●● patch | view | raw | blame | history
uniapps/work-wx/src/subPackages/flightApplication/add.vue 13 ●●●●● patch | view | raw | blame | history
uniapps/work-wx/src/subPackages/flightApplication/details.vue 52 ●●●● patch | view | raw | blame | history
applications/drone-command/src/components/map-container/device-map-container.vue
@@ -39,7 +39,7 @@
            @close="closePopup" />
        <DronePopup :visible="popupVisible && isDronePopup" :position="popupPosition" :drone="selectedDevice"
            :favorite="Boolean(selectedDevice?.isFavorite)" @close="closePopup" @toggle-favorite="toggleDroneFavorite"
            :favorite="Boolean(selectedDevice?.isFavorite)" @close="closePopup" @toggle-favorite="handleDroneFavorite"
            @signal="handleDroneSignal" @counter="handleDroneCounter" />
    </div>
</template>
@@ -78,6 +78,10 @@
        type: Array,
        default: () => [],
    },
    alarmDrones: {
        type: Array,
        default: () => [],
    },
    leftCollapsed: {
        type: Boolean,
        default: false,
@@ -91,7 +95,7 @@
        default: true,
    },
})
const emit = defineEmits(['droneSignal', 'droneCounter'])
const emit = defineEmits(['droneSignal', 'droneCounter', 'droneFavorite'])
const DEFAULT_ZONE_PAGE_SIZE = 999
@@ -363,44 +367,86 @@
    })
}
const buildSimulatedTrackPoints = () => {
    return [
        {
            longitude: 114.963191,
            latitude: 27.136716,
            height: 120
        },
        {
            longitude: 114.957308,
            latitude: 27.138452,
            height: 120
        },
        {
            longitude: 114.952,
            latitude: 27.136317,
            height: 120
        },
        {
            longitude: 114.949293,
            latitude: 27.133864,
            height: 120
        },
        {
            longitude: 114.944666,
            latitude: 27.130526,
            height: 120
        },
        {
            longitude: 114.945909,
            latitude: 27.127845,
            height: 120
        },
        {
            longitude: 114.962974,
            latitude: 27.136242,
            height: 120
        },
const buildSimulatedTrackPoints = center => {
    const basePoints = [
        [
            {
                longitude: 114.963191,
                latitude: 27.136716,
                height: 120
            },
            {
                longitude: 114.957308,
                latitude: 27.138452,
                height: 120
            },
            {
                longitude: 114.952,
                latitude: 27.136317,
                height: 120
            },
            {
                longitude: 114.949293,
                latitude: 27.133864,
                height: 120
            },
            {
                longitude: 114.944666,
                latitude: 27.130526,
                height: 120
            },
            {
                longitude: 114.945909,
                latitude: 27.127845,
                height: 120
            },
            {
                longitude: 114.962974,
                latitude: 27.136242,
                height: 120
            },
        ],
        [
            {
                longitude: 114.931063,
                latitude: 27.095052,
                height: 120
            },
            {
                longitude: 114.928653,
                latitude: 27.096307,
                height: 120
            },
            {
                longitude: 114.925958,
                latitude: 27.096963,
                height: 120
            },
            {
                longitude: 114.925836,
                latitude: 27.096102,
                height: 120
            },
            {
                longitude: 114.928384,
                latitude: 27.095866,
                height: 120
            },
            {
                longitude: 114.929874,
                latitude: 27.096095,
                height: 120
            },
            {
                longitude: 114.931048,
                latitude: 27.097666,
                height: 120
            },
        ]
    ]
    return basePoints[center.trackIndex]
}
@@ -486,70 +532,67 @@
    droneTrackTickHandler = null
}
const renderSimulatedDroneTrack = () => {
const renderSimulatedDroneTrack = (list) => {
    if (!viewer) return
    clearDroneTrackEntities()
    if (!list?.length) return
    ensureDroneTrackCollections()
    droneTrackBillboardCollection.show = detailVisible.value
    droneTrackPolylineCollection.show = detailVisible.value
    droneTrackRuntime = []
    const baseCenters = [['默认区域', { longitude: 114.958541, latitude: 27.121917 }]]
    const segmentDuration = 6
    const baseTrackColor = Cesium.Color.fromCssColorString('red')
    baseCenters.forEach(([centerName, 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)
        )
        let trackMaterial = createDroneTrackMaterial({
            color: baseTrackColor,
            speed: 4.5,
            headWidth: 0.2,
            glowPower: 1.8,
            backgroundAlpha: 0.34,
        ; (list || []).forEach((item, trackIndex) => {
            const position = getDevicePosition(item)
            if (!position) return
            const points = buildSimulatedTrackPoints({ ...position, height: item.flightHeightM, trackIndex })
            if (points.length < 2) return
            const positions = points.map(point =>
                Cesium.Cartesian3.fromDegrees(point.longitude, point.latitude, point.height)
            )
            let trackMaterial = createDroneTrackMaterial({
                color: baseTrackColor,
                speed: 4.5,
                headWidth: 0.2,
                glowPower: 1.8,
                backgroundAlpha: 0.34,
            })
            if (!trackMaterial) {
                trackMaterial = Cesium.Material.fromType('Color', { color: baseTrackColor })
            }
            droneTrackPolylineCollection.add({
                positions,
                width: 3,
                material: trackMaterial,
            })
            const droneId = `drone-alarm-${item?.alarmRecordId ?? item?.id ?? trackIndex}`
            const billboard = droneTrackBillboardCollection.add({
                position: positions[positions.length - 1],
                image: droneIcon,
                width: 36,
                height: 36,
                verticalOrigin: Cesium.VerticalOrigin.CENTER,
                disableDepthTestDistance: Number.POSITIVE_INFINITY,
            })
            billboard.id = droneId
            const speedMs = Math.round(Cesium.Cartesian3.distance(positions[0], positions[1]) / segmentDuration)
            dronePickMap.set(droneId, {
                data: {
                    ...item,
                    flightHeightM: item.flightHeightM ?? points[0].height,
                    flightSpeedMs: item.flightSpeedMs ?? speedMs,
                    longitude: item.longitude ?? points[0].longitude,
                    latitude: item.latitude ?? points[0].latitude,
                },
                billboard,
            })
            droneTrackRuntime.push({
                positions,
                billboard,
                segmentDuration,
                duration: (points.length - 1) * segmentDuration,
            })
        })
        if (!trackMaterial) {
            trackMaterial = Cesium.Material.fromType('Color', { color: baseTrackColor })
        }
        droneTrackPolylineCollection.add({
            positions,
            width: 3,
            material: trackMaterial,
        })
        const droneId = `drone-track-${trackIndex}`
        const billboard = droneTrackBillboardCollection.add({
            position: positions[positions.length - 1],
            image: droneIcon,
            width: 36,
            height: 36,
            verticalOrigin: Cesium.VerticalOrigin.CENTER,
            disableDepthTestDistance: Number.POSITIVE_INFINITY,
        })
        billboard.id = droneId
        const speedMs = Math.round(Cesium.Cartesian3.distance(positions[0], positions[1]) / segmentDuration)
        dronePickMap.set(droneId, {
            data: {
                droneName: `无人机-${trackIndex + 1}`,
                droneSerialNo: `UAV-${trackIndex + 1}`,
                flightHeightM: points[0].height,
                flightSpeedMs: speedMs,
                longitude: points[0].longitude,
                latitude: points[0].latitude,
                flightStatus: 1,
                deviceName: centerName,
                dataSource: centerName,
                counterDeviceName: centerName,
            },
            billboard,
        })
        droneTrackRuntime.push({
            positions,
            billboard,
            segmentDuration,
            duration: (points.length - 1) * segmentDuration,
        })
    })
    viewer.clock.multiplier = 1
    startDroneTrackAnimation()
    reorderCockpitPrimitives()
@@ -860,9 +903,9 @@
const popupPosition = ref({ x: 0, y: 0 })
const popupVisible = computed(() => Boolean(selectedDevice.value))
const isDronePopup = computed(() => selectedTargetType.value === 'drone')
const toggleDroneFavorite = () => {
const handleDroneFavorite = () => {
    if (!selectedDevice.value) return
    selectedDevice.value.isFavorite = !selectedDevice.value.isFavorite
    emit('droneFavorite', selectedDevice.value)
}
const handleDroneSignal = () => emit('droneSignal', selectedDevice.value)
const handleDroneCounter = () => emit('droneCounter', selectedDevice.value)
@@ -911,6 +954,19 @@
    () => props.onlineDevices,
    devices => {
        renderDeviceEntities(devices || [])
    },
    { deep: true }
)
watch(
    () => props.alarmDrones,
    list => {
        renderSimulatedDroneTrack(list || [])
        if (selectedTargetType.value === 'drone' && selectedDevice.value) {
            const selectedId = selectedDevice.value.alarmRecordId ?? selectedDevice.value.id
            const match = (list || []).find(item => (item?.alarmRecordId ?? item?.id) === selectedId)
            if (match) selectedDevice.value = { ...selectedDevice.value, ...match }
        }
    },
    { deep: true }
)
@@ -970,7 +1026,7 @@
    loadPartitions()
    loadAggregation()
    loadCommandPosts()
    renderSimulatedDroneTrack()
    renderSimulatedDroneTrack(props.alarmDrones)
    initDeviceClickHandler()
}
applications/drone-command/src/views/basicManage/deviceStock/FormDiaLog.vue
@@ -62,15 +62,20 @@
                        <el-table-column prop="charger" label="负责人" />
                        <el-table-column prop="contactPhone" label="联系电话" />
                        <el-table-column prop="outTarget" label="出库去向" />
                        <el-table-column prop="outTime" label="出库时间" />
                        <el-table-column prop="outTime" show-overflow-tooltip label="出库时间">
                            <template v-slot="{ row }">
                                {{ dayjs(row.outTime).format('YYYY-MM-DD') }}
                            </template>
                        </el-table-column>
                        <el-table-column prop="purpose" label="用途" />
                    </el-table>
                </div>
                <div class="command-table-pagination">
                    <el-pagination popper-class="command-select-popper"
                        v-model:current-page="searchParams.current" v-model:page-size="searchParams.size"
                        layout="total, prev, pager, next, sizes" :total="total" @change="getList" />
                    <el-pagination popper-class="command-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>
        </div>
@@ -79,8 +84,8 @@
            <el-row>
                <el-col :span="12">
                    <el-form-item label="设备名称" prop="deviceName">
                        <el-input class="command-input" v-model="formData.deviceName" maxlength="50"
                            placeholder="请输入" clearable />
                        <el-input class="command-input" v-model="formData.deviceName" maxlength="50" placeholder="请输入"
                            clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
@@ -94,8 +99,8 @@
                </el-col>
                <el-col :span="12">
                    <el-form-item label="设备编码" prop="deviceSn">
                        <el-input class="command-input" v-model="formData.deviceSn" maxlength="50"
                            placeholder="请输入" clearable />
                        <el-input class="command-input" v-model="formData.deviceSn" maxlength="50" placeholder="请输入"
                            clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
@@ -109,40 +114,39 @@
                </el-col>
                <el-col :span="12">
                    <el-form-item label="型号" prop="deviceModel">
                        <el-input class="command-input" v-model="formData.deviceModel" maxlength="50"
                            placeholder="请输入" clearable />
                        <el-input class="command-input" v-model="formData.deviceModel" maxlength="50" placeholder="请输入"
                            clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="规格" prop="deviceSpecification">
                        <el-input class="command-input" v-model="formData.deviceSpecification"
                            maxlength="50" placeholder="请输入" clearable />
                        <el-input class="command-input" v-model="formData.deviceSpecification" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="生产厂商" prop="manufacturer">
                        <el-input class="command-input" v-model="formData.manufacturer" maxlength="50"
                            placeholder="请输入" clearable />
                        <el-input class="command-input" v-model="formData.manufacturer" maxlength="50" placeholder="请输入"
                            clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="来源" prop="source">
                        <el-input class="command-input" v-model="formData.source" maxlength="50"
                            placeholder="请输入" clearable />
                        <el-input class="command-input" v-model="formData.source" maxlength="50" placeholder="请输入"
                            clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="用途" prop="purpose">
                        <el-input class="command-input" v-model="formData.purpose" maxlength="50"
                            placeholder="请输入" clearable />
                        <el-input class="command-input" v-model="formData.purpose" maxlength="50" placeholder="请输入"
                            clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="所属部门" prop="belongDept">
                        <el-tree-select class="command-select"
                            popper-class="command-tree-select-popper" v-model="formData.belongDept"
                            node-key="id" :data="deptTree" :props="treeProps" check-strictly @change="belongDeptChange"
                            clearable />
                        <el-tree-select class="command-select" popper-class="command-tree-select-popper"
                            v-model="formData.belongDept" node-key="id" :data="deptTree" :props="treeProps"
                            check-strictly @change="belongDeptChange" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
@@ -155,14 +159,15 @@
                </el-col>
                <el-col :span="12">
                    <el-form-item label="联系电话" prop="contactPhone">
                        <el-input class="command-input" v-model="formData.contactPhone" maxlength="50"
                            placeholder="请输入" clearable />
                        <el-input class="command-input" v-model="formData.contactPhone" maxlength="50" placeholder="请输入"
                            clearable />
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <template #footer>
            <el-button v-if="dialogMode != 'view'" color="#2B2B4C" @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
            <el-button v-if="dialogMode != 'view'" color="#2B2B4C" @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消'
                }}</el-button>
            <el-button color="#284FE3" v-if="!dialogReadonly" type="primary" :loading="submitting"
                :disabled="submitting" @click="handleSubmit">
                保存
@@ -172,6 +177,8 @@
</template>
<script setup>
import dayjs from 'dayjs'
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { fwDeviceDetailApi, fwDeviceSubmitApi } from '@/views/basicManage/deviceStock/fwDevice'
applications/drone-command/src/views/dataCockpit/components/LeftContainer.vue
@@ -12,7 +12,16 @@
            </div>
            <div class="content">
                <component :is="currentComponent" />
                <RealWarning
                    v-if="activeIdModel === 1"
                    :data="realWarningData"
                    :loading="realWarningLoading"
                    @signal="emit('real-warning-signal', $event)"
                    @counter="emit('real-warning-counter', $event)"
                    @favorite="emit('real-warning-favorite', $event)"
                />
                <EquipmentWarning v-else-if="activeIdModel === 2" />
                <HistoryWarning v-else />
            </div>
        </div>
@@ -24,11 +33,11 @@
<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'
import { computed, onMounted, ref } from 'vue'
import RealWarning from './RealWarning.vue'
const props = defineProps({
    activeId: {
@@ -38,10 +47,24 @@
    collapsed: {
        type: Boolean,
        default: false
    },
    realWarningData: {
        type: Array,
        default: () => []
    },
    realWarningLoading: {
        type: Boolean,
        default: false
    }
})
const emit = defineEmits(['update:activeId', 'update:collapsed'])
const emit = defineEmits([
    'update:activeId',
    'update:collapsed',
    'real-warning-signal',
    'real-warning-counter',
    'real-warning-favorite'
])
const activeIdModel = computed({
    get: () => props.activeId,
@@ -73,10 +96,6 @@
        component: HistoryWarning
    }
])
const currentComponent = computed(() => {
    return categoryList.value.find(i => i.id === activeIdModel.value)?.component
})
const toggleCollapse = () => {
    collapsedModel.value = !collapsedModel.value
applications/drone-command/src/views/dataCockpit/components/RealWarning.vue
@@ -2,137 +2,39 @@
 * @Author       : yuan
 * @Date         : 2026-01-08 09:29:07
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-26 09:59:52
 * @LastEditTime : 2026-01-27 12:01:37
 * @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="加载中..."
    >
    <div class="real-warning" v-loading="props.loading" element-loading-background="rgba(5, 5, 15, 0.6)"
        element-loading-text="???...">
        <div class="list-content">
            <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="() => handleFavorite(item)"
            />
            <EmptyState v-if="!props.data.length && !props.loading" />
            <RealTemplate v-else v-for="(item, ind) in props.data" :key="ind" :data="item"
                @signal="emit('signal', item)" @counter="emit('counter', item)" @favorite="emit('favorite', item)" />
        </div>
    </div>
</template>
<script setup>
import RealTemplate from './templateComponents/RealTemplate.vue'
import {
    alarmLogApi,
    alarmFavoriteRemoveApi,
    alarmFavoriteSaveApi,
    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 getAlarmRecordId = (item) => item?.alarmRecordId ?? item?.id
const getFavoriteId = (item) =>
    item?.favoriteId ??
    item?.alarmFavoriteId ??
    item?.fwAlarmFavoriteId ??
    item?.favoriteRecordId ??
    getAlarmRecordId(item)
const isFavorited = (item) => {
    const value = item?.favorited ?? item?.isFavorite
    return value === 1 || value === '1' || value === true
}
const buildPayload = (type, item) => ({
    counterWay: type === 'signal' ? 1 : type === 'counter' ? 2 : '',
    alarmRecordId: getAlarmRecordId(item)
const props = defineProps({
    data: {
        type: Array,
        default: () => []
    },
    loading: {
        type: Boolean,
        default: false
    }
})
const handleAction = async (type, item) => {
    const actionText = actionTextMap[type] || '操作'
    try {
        const res = await interferenceAndExpulsionApi(buildPayload(type, item))
        if (res?.data?.success) {
            ElMessage({ type: 'success', message: actionText + '成功' })
            await fetchRealWarning()
        } else {
            ElMessage({ type: 'error', message: res?.data?.msg || actionText + '失败' })
        }
    } catch (error) {
        ElMessage({ type: 'error', message: actionText + '失败' })
    }
}
const handleFavorite = async (item) => {
    const alarmRecordId = getAlarmRecordId(item)
    if (!alarmRecordId) {
        ElMessage({ type: 'warning', message: '缺少告警记录ID' })
        return
    }
    const isFavorite = isFavorited(item)
    const actionText = isFavorite ? '取消关注' : '关注'
    try {
        const res = isFavorite
            ? await alarmFavoriteRemoveApi(getFavoriteId(item))
            : await alarmFavoriteSaveApi({ alarmRecordId })
        if (res?.data?.success) {
            ElMessage({ type: 'success', message: actionText + '成功' })
            await fetchRealWarning()
        } 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.map((record) => ({
            ...record,
            isFavorite:
                record?.isFavorite ??
                (record?.favorited === 1 || record?.favorited === '1')
        }))
    } finally {
        const elapsed = Date.now() - startAt
        if (elapsed < minLoadingMs) {
            await new Promise((resolve) => setTimeout(resolve, minLoadingMs - elapsed))
        }
        loading.value = false
    }
}
onMounted(() => {
    fetchRealWarning()
})
const emit = defineEmits(['signal', 'counter', 'favorite'])
</script>
<style lang="scss" scoped>
applications/drone-command/src/views/dataCockpit/index.vue
@@ -12,10 +12,23 @@
  <div class="page-container">
    <DeviceMapContainer
      :online-devices="onlineDevices"
      :alarm-drones="realWarningData"
      :left-collapsed="leftCollapsed"
      container-id="data-cockpit-map"
      @drone-signal="handleAlarmAction('signal', $event)"
      @drone-counter="handleAlarmAction('counter', $event)"
      @drone-favorite="handleAlarmFavorite"
    />  
    <LeftContainer class="left-container" v-model:activeId="leftActiveId" v-model:collapsed="leftCollapsed" />
    <LeftContainer
      class="left-container"
      v-model:activeId="leftActiveId"
      v-model:collapsed="leftCollapsed"
      :real-warning-data="realWarningData"
      :real-warning-loading="realWarningLoading"
      @real-warning-signal="handleAlarmAction('signal', $event)"
      @real-warning-counter="handleAlarmAction('counter', $event)"
      @real-warning-favorite="handleAlarmFavorite"
    />
    <RightContainer class="right-container" v-model:collapsed="rightCollapsed" v-model:onlineDevices="onlineDevices" />  
    <CenterContainer @select-warning="onSelectWarning" @select-equipment="onSelectEquipment" />  
    <LegendBar class="legend-container" />
@@ -28,12 +41,23 @@
  import RightContainer from './components/RightContainer.vue';
  import CenterContainer from './components/CenterContainer.vue';
  import LegendBar from './components/LegendBar.vue';
  import {
    alarmLogApi,
    alarmFavoriteRemoveApi,
    alarmFavoriteSaveApi,
    interferenceAndExpulsionApi
  } from '@/api/dataCockpit';
  import { ElMessage } from 'element-plus';
  import { onMounted, ref } from 'vue';
  const leftActiveId = ref(1);
  const leftCollapsed = ref(false);
  const rightCollapsed = ref(false);
  const onlineDevices = ref([]);
  const realWarningData = ref([]);
  const realWarningLoading = ref(true);
  const minLoadingMs = 400;
  const onSelectWarning = (type) => {
    leftActiveId.value = type;
@@ -43,6 +67,93 @@
  const onSelectEquipment = () => {
    rightCollapsed.value = false;
  };
  const actionTextMap = {
    signal: '信号干扰',
    counter: '诱导驱离'
  };
  const getAlarmRecordId = (item) => item?.alarmRecordId ?? item?.id;
  const getFavoriteId = (item) =>
    item?.favoriteId ??
    item?.alarmFavoriteId ??
    item?.fwAlarmFavoriteId ??
    item?.favoriteRecordId ??
    getAlarmRecordId(item);
  const isFavorited = (item) => {
    const value = item?.favorited ?? item?.isFavorite;
    return value === 1 || value === '1' || value === true;
  };
  const buildPayload = (type, item) => ({
    counterWay: type === 'signal' ? 1 : type === 'counter' ? 2 : '',
    alarmRecordId: getAlarmRecordId(item)
  });
  const handleAlarmAction = async (type, item) => {
    const actionText = actionTextMap[type] || '操作';
    try {
      const res = await interferenceAndExpulsionApi(buildPayload(type, item));
      if (res?.data?.success) {
        ElMessage({ type: 'success', message: actionText + '成功' });
        await fetchRealWarning();
      } else {
        ElMessage({ type: 'error', message: res?.data?.msg || actionText + '失败' });
      }
    } catch (error) {
      ElMessage({ type: 'error', message: actionText + '失败' });
    }
  };
  const handleAlarmFavorite = async (item) => {
    const alarmRecordId = getAlarmRecordId(item);
    if (!alarmRecordId) {
      ElMessage({ type: 'warning', message: '缺少告警记录ID' });
      return;
    }
    const favorite = isFavorited(item);
    const actionText = favorite ? '取消关注' : '关注';
    try {
      const res = favorite
        ? await alarmFavoriteRemoveApi(getFavoriteId(item))
        : await alarmFavoriteSaveApi({ alarmRecordId });
      if (res?.data?.success) {
        ElMessage({ type: 'success', message: actionText + '成功' });
        await fetchRealWarning();
      } else {
        ElMessage({ type: 'error', message: res?.data?.msg || actionText + '失败' });
      }
    } catch (error) {
      ElMessage({ type: 'error', message: actionText + '失败' });
    }
  };
  const fetchRealWarning = async () => {
    const startAt = Date.now();
    realWarningLoading.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.map((record) => ({
        ...record,
        isFavorite:
          record?.isFavorite ??
          (record?.favorited === 1 || record?.favorited === '1')
      }));
    } finally {
      const elapsed = Date.now() - startAt;
      if (elapsed < minLoadingMs) {
        await new Promise((resolve) => setTimeout(resolve, minLoadingMs - elapsed));
      }
      realWarningLoading.value = false;
    }
  };
  onMounted(() => {
    fetchRealWarning();
  });
</script>
<style scoped lang="scss">
applications/task-work-order/src/assets/images/orderView/orderManage/drone.png

applications/task-work-order/src/assets/images/orderView/orderManage/droneActive.png
applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue
@@ -302,6 +302,7 @@
let viewPlane
const hasPatrolTaskList = computed(() => ['30', '40', '50', '60'].includes(String(formData.value.workOrderStatus)))
import droneIcon from '@/assets/images/orderView/orderManage/drone.png'
import droneActiveIcon from '@/assets/images/orderView/orderManage/droneActive.png'
const gdStatusObj = {
    '10': { reason: '拒绝原因', operationType: '2' },
@@ -590,13 +591,12 @@
    const selectedIds = (formData.value.recommendDeviceIds || '').split(',').filter(Boolean)
    if (!viewer?.entities) return
    viewer.entities.values.forEach(entity => {
        if (entity.billboard?.image?._value === droneIcon && entity.label) {
        const image = entity.billboard?.image?._value
        if ((image === droneIcon || image === droneActiveIcon) && entity.label) {
            const device = deviceList.value.find(d => d.nickname === entity.label.text._value)
            if (device && selectedIds.includes(String(device.id))) {
                entity.label.fillColor = Cesium.Color.YELLOW
            } else {
                entity.label.fillColor = Cesium.Color.WHITE
            }
            entity.billboard.image = device && selectedIds.includes(String(device.id))
                ? droneActiveIcon
                : droneIcon
        }
    })
}
uniapps/work-wx/src/subPackages/deviceRegistration/details.vue
@@ -9,7 +9,7 @@
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.name" border="none"></u-input>
            <u-input v-model="formParams.name" readonly border="none"></u-input>
        </u-form-item>
        <u-form-item
                label="SN(出厂序列号)"
@@ -18,7 +18,7 @@
                :borderBottom="true"
                ref="item2"
        >
            <u-input v-model="formParams.sn" border="none"></u-input>
            <u-input v-model="formParams.sn" readonly border="none"></u-input>
        </u-form-item>
        <u-form-item
                label="飞行器厂商"
@@ -28,9 +28,8 @@
                ref="item2"
        >
            <u-input
                v-model="formParams.manufacturer"
                disabled
                disabledColor="#ffffff"
                v-model="formParams.manufacturerText"
                readonly
                border="none"
            ></u-input>
        </u-form-item>
@@ -41,7 +40,7 @@
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.model" border="none"></u-input>
            <u-input v-model="formParams.model" readonly border="none"></u-input>
        </u-form-item>
        <u-form-item
                label="是否改装"
@@ -52,8 +51,7 @@
        >
            <u-input
                v-model="formParams.isModified"
                disabled
                disabledColor="#ffffff"
                readonly
                border="none"
            ></u-input>
        </u-form-item>
@@ -66,8 +64,7 @@
        >
            <u-input
                v-model="formParams.type"
                disabled
                disabledColor="#ffffff"
                readonly
                border="none"
            ></u-input>
        </u-form-item>
@@ -79,7 +76,8 @@
                ref="item1"
        >
            <u-input
                    v-model="formParams.holder"
                    v-model="formParams.holder"
                    readonly
                    border="none"
            ></u-input>
        </u-form-item>
@@ -90,7 +88,7 @@
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.purchaseDate" border="none" placeholder="请选择日期" suffixIcon="calendar"></u-input>
            <u-input v-model="formParams.purchaseDate" readonly border="none" placeholder="请选择日期" suffixIcon="calendar"></u-input>
        </u-form-item>
        <u-form-item
                label="有无保险"
@@ -101,8 +99,7 @@
        >
            <u-input
                v-model="formParams.hasInsurance"
                disabled
                disabledColor="#ffffff"
                readonly
                border="none"
            ></u-input>
        </u-form-item>
@@ -115,8 +112,7 @@
        >
            <u-input
                v-model="formParams.region"
                disabled
                disabledColor="#ffffff"
                readonly
                border="none"
            ></u-input>
        </u-form-item>
@@ -128,7 +124,8 @@
                ref="item1"
        >
            <u-input
                    v-model="formParams.caacRegistrationCode"
                    v-model="formParams.caacRegistrationCode"
                    readonly
                    border="none"
            ></u-input>
        </u-form-item>
@@ -140,7 +137,8 @@
                ref="item1"
        >
            <u-input
                    v-model="formParams.deviceImage"
                    v-model="formParams.deviceImage"
                    readonly
                    border="none"
            ></u-input>
        </u-form-item>
@@ -152,7 +150,8 @@
                ref="item1"
        >
            <u-input
                    v-model="formParams.remarks"
                    v-model="formParams.remarks"
                    readonly
                    border="none"
            ></u-input>
        </u-form-item>
@@ -314,11 +313,13 @@
        current: 1,
        size: 1000,
    }).then(res => {
        manufacturerList.value = res.data.data.records.map(item => ({
            ...item,
            label: item.unitName,
            value: item.id,
        }))
        // manufacturerList.value = res.data.data.records.map(item => ({
        //     ...item,
        //     label: item.unitName,
        //     value: item.id,
        // }))
        console.log(formParams.value.manufacturer, res.data.data.records.find(item => item.id === formParams.value.manufacturer))
        formParams.value.manufacturerText = res.data.data.records.find(item => item.id === formParams.value.manufacturer)?.unitName || ''
    })
}
onLoad((options) => {
@@ -326,16 +327,17 @@
        formParams.value = res.data.data
        formParams.value.isModified = res.data.data.isModified ? '是' : '否'
        formParams.value.hasInsurance = res.data.data.hasInsurance === '1' ? '有' : '无'
        formParams.value.manufacturer = manufacturerList.value.find(item => item.value === res.data.data.manufacturer)?.label || ''
        // formParams.value.manufacturer = manufacturerList.value.find(item => item.value === res.data.data.manufacturer)?.label || ''
        formParams.value.type = typeList.value.find(item => item.value === res.data.data.type)?.label || ''
        formParams.value.region = res.data.data.region
        getManufacturerInfoApi()
    })
    
    
})
onMounted(async () => {
    // getManufacturerInfoApi()
    getProTypeApi()
    getManufacturerInfoApi()
    // 动态导入xzqhData,确保在使用前已初始化
    const { xzqhData } = await import('@/static/xzqh/index')
    areaData.value = xzqhData
uniapps/work-wx/src/subPackages/flightApplication/add.vue
@@ -329,20 +329,22 @@
        @confirm="onPickerPlanType"
        @cancel="isShowPlanType = false"
    />
    <up-datetime-picker
    <u-datetime-picker
            :show="isShowStartTime"
            v-model="startTime"
            mode="datetime"
            :min-date="minDate"
            @confirm="onConfirmStartTime"
             @cancel="isShowStartTime = false"
        ></up-datetime-picker>
        <up-datetime-picker
        ></u-datetime-picker>
        <u-datetime-picker
            :show="isShowEndTime"
            v-model="endTime"
            mode="datetime"
            :min-date="minDate"
            @confirm="onConfirmEndTime"
            @cancel="isShowEndTime = false"
        ></up-datetime-picker>
        ></u-datetime-picker>
    <!-- <u-calendar :show="isShowTimeRange" mode="date" @confirm="onConfirmTimeRange" @close="isShowTimeRange = false" :close-on-click-overlay="true"></u-calendar> -->
    <!-- <u-calendar :show="isShowTimeRange" mode="range" @confirm="onConfirmTimeRange" @close="isShowTimeRange = false" :close-on-click-overlay="true" :allow-same-day="true" :show-time="true" time-format="HH:mm:ss"></u-calendar> -->
    <!-- 飞行器 -->
@@ -541,6 +543,7 @@
const isShowStartTime = ref(false)
// 是否显示结束时间选择器
const isShowEndTime = ref(false)
const minDate = ref(Date.now()); // 设置最小日期为当前时间戳
const startTime = ref(Date.now()); // 设置默认值为当前时间戳
const endTime = ref(Date.now()); // 设置默认值为当前时间戳
@@ -714,6 +717,8 @@
        duration: 2000
      })
    }
  }).finally(() => {
    isSubmitDisabled.value = false
  })
}
onMounted(() => {
uniapps/work-wx/src/subPackages/flightApplication/details.vue
@@ -10,7 +10,7 @@
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.flightPlanName" border="none"></u-input>
            <u-input v-model="formParams.flightPlanName" readonly border="none"></u-input>
        </u-form-item>
        <u-form-item
                label="任务类型"
@@ -21,8 +21,7 @@
        >
            <u-input
                v-model="formParams.flightTaskType"
                disabled
                disabledColor="#ffffff"
                readonly
                border="none"
            ></u-input>
        </u-form-item>
@@ -35,8 +34,7 @@
        >
            <u-input
                v-model="formParams.flightRule"
                disabled
                disabledColor="#ffffff"
                readonly
                border="none"
            ></u-input>
        </u-form-item>
@@ -49,8 +47,7 @@
        >
            <u-input
                v-model="formParams.flightMode"
                disabled
                disabledColor="#ffffff"
                readonly
                border="none"
            ></u-input>
        </u-form-item>
@@ -61,7 +58,7 @@
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.flightStartTime" border="none" suffixIcon="calendar"></u-input>
            <u-input v-model="formParams.flightStartTime" border="none" readonly suffixIcon="calendar"></u-input>
        </u-form-item>
         <u-form-item
                label="飞行活动结束日期"
@@ -70,7 +67,7 @@
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.flightEndTime" border="none" suffixIcon="calendar"></u-input>
            <u-input v-model="formParams.flightEndTime" readonly border="none" suffixIcon="calendar"></u-input>
        </u-form-item>
        <u-form-item
@@ -82,8 +79,7 @@
        >
            <u-input
                v-model="formParams.planType"
                disabled
                disabledColor="#ffffff"
                readonly
                border="none"
            ></u-input>
        </u-form-item>
@@ -94,7 +90,7 @@
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.contactPerson" border="none"></u-input>
            <u-input v-model="formParams.contactPerson" readonly border="none"></u-input>
        </u-form-item>
        <u-form-item
                label="联系电话"
@@ -103,7 +99,7 @@
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.contactPhone" border="none"></u-input>
            <u-input v-model="formParams.contactPhone" readonly border="none"></u-input>
        </u-form-item>
        <u-form-item
                label="承办人"
@@ -112,7 +108,7 @@
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.undertaker" border="none"></u-input>
            <u-input v-model="formParams.undertaker" readonly border="none"></u-input>
        </u-form-item>
        <u-form-item
                label="承办人联系电话"
@@ -121,7 +117,7 @@
                :borderBottom="true"
                ref="item1"
        >
            <u-input v-model="formParams.undertakerPhone" border="none"></u-input>
            <u-input v-model="formParams.undertakerPhone" readonly border="none"></u-input>
        </u-form-item>
        <u-form-item
                label="临时空域批件"
@@ -140,7 +136,8 @@
                ref="item1"
        >
            <u-input
                    v-model="formParams.weatherCondition"
                    v-model="formParams.weatherCondition"
                    readonly
                    border="none"
            ></u-input>
        </u-form-item>
@@ -154,8 +151,7 @@
        >
            <u-input
                v-model="formParams.aircraftId"
                disabled
                disabledColor="#ffffff"
                readonly
                border="none"
            ></u-input>
        </u-form-item>
@@ -168,8 +164,7 @@
        >
            <u-input
                v-model="formParams.pilotId"
                disabled
                disabledColor="#ffffff"
                readonly
                border="none"
            ></u-input>
        </u-form-item>
@@ -183,8 +178,7 @@
        >
            <u-input
                v-model="formParams.airspaceId"
                disabled
                disabledColor="#ffffff"
                readonly
                border="none"
            ></u-input>
        </u-form-item>
@@ -197,8 +191,7 @@
        >
            <u-input
                v-model="formParams.routeId"
                disabled
                disabledColor="#ffffff"
                readonly
                border="none"
            ></u-input>
        </u-form-item>
@@ -317,16 +310,19 @@
     flightPlanDetailsApi(options.id).then(res => {
        // return
        const result = res.data.data
        console.log(result, '7777')
        formParams.value = result
        formParams.value.routeId = result.routeInfo.name
        formParams.value.airspaceId = result.airspaceInfo.taskName || '无'
        formParams.value.aircraftId = result.airspaceInfo.aircraftInfoList.map(item => item.name).join(',') || '无'
        formParams.value.pilotId = result.operatorInfoList.map(item => item.name).join(',') || '无'
        // 字典
        formParams.value.flightRule = actionsFlightRule.value.find(item => item.dictValue === result.flightRule)?.dictLabel || '无'
        formParams.value.flightMode = actionsFlightMode.value.find(item => item.dictValue === result.flightMode)?.dictLabel || '无'
        formParams.value.planType = actionsPlanType.value.find(item => item.dictValue === result.planType)?.dictLabel || '无'
        formParams.value.flightTaskType = actionsFlightTaskType.value.find(item => item.dictValue === result.flightTaskType)?.dictLabel || '无'
        // 数据
        formParams.value.routeId = result?.routeInfo?.name || '无'
        formParams.value.airspaceId = result?.airspaceInfo?.taskName || '无'
        formParams.value.aircraftId = result?.airspaceInfo?.aircraftInfoList?.map(item => item.name).join(',') || '无'
        formParams.value.pilotId = result?.operatorInfoList?.map(item => item.name).join(',') || '无'
    })
})
onMounted(() => {