吉安感知网项目-前端
shuishen
2026-01-30 521bcd986338f91ef38f7a7b56f4253ce8723afb
Merge branch 'master' of http://139.196.74.78:10010/r/jagzwxm/ja_web
8 files modified
1 files deleted
1 files added
735 ■■■■■ changed files
applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue 6 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/sceneConfig/index.vue 6 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/sceneManage/FormDiaLog.vue 349 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/sceneManage/index.vue 67 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/sceneManage/sceneConfigApi.js 46 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/sceneManage/sceneManageApi.js 38 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/appInnovation/FormDiaLog.vue 1 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/dataObjection/FormDiaLog.vue 130 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/dataObjection/index.vue 2 ●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/FormDiaLog.vue 90 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue
@@ -23,7 +23,7 @@
                    <div class="detail-title">场景详情</div>
                    <el-row>
                        <el-col :span="24">
                            <div class="label">场景名称</div>
                            <div class="label">场景配置名称</div>
                            <div class="val">{{ formData.sceneName }}</div>
                        </el-col>
                        <el-col :span="24">
@@ -60,7 +60,7 @@
                    :disabled="readonly"
                    label-width="96px"
                >
                    <el-form-item label="场景名称" prop="sceneName">
                    <el-form-item label="场景配置名称" prop="sceneName">
                        <el-input
                            class="command-input"
                            v-model="formData.sceneName"
@@ -165,7 +165,7 @@
import { AREA_TYPE_STYLE_MAP, BUFFER_LEVEL_STYLES, DEFAULT_AREA_STYLE } from '@ztzf/constants'
const initForm = () => ({
    sceneName: '', // 场景名称
    sceneName: '', // 场景配置名称
    sceneType: '', // 场景类型
    deviceMode: '', // 设备模式
    areaDivideIds: '', // 关联区域ID
applications/drone-command/src/views/areaManage/sceneConfig/index.vue
@@ -1,7 +1,7 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="场景名称" prop="sceneName">
            <el-form-item label="场景配置名称" prop="sceneName">
                <el-input
                    class="command-input"
                    v-model="searchParams.sceneName"
@@ -46,7 +46,7 @@
                    <el-table-column type="selection" width="46" />
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="sceneName" show-overflow-tooltip label="场景名称" />
                    <el-table-column prop="sceneName" show-overflow-tooltip label="场景配置名称" />
                    <el-table-column prop="sceneType" show-overflow-tooltip label="场景类型">
                        <template v-slot="{ row }">
                            {{ getDictLabel(row.sceneType, dictObj.CommandSceneType) }}
@@ -93,7 +93,7 @@
import { useRoute } from 'vue-router'
const initSearchParams = () => ({
    sceneName: '', // 场景名称
    sceneName: '', // 场景配置名称
    sceneType: '', // 场景类型
    current: 1, // 当前页
    size: 10, // 每页大小
applications/drone-command/src/views/areaManage/sceneManage/FormDiaLog.vue
@@ -20,35 +20,33 @@
                </div>
                <div class="content" v-if="readonly">
                    <div class="detail-title">场景详情</div>
                    <div class="detail-title">场景管理详情</div>
                    <el-row>
                        <el-col :span="24">
                            <div class="label">场景名称</div>
                            <div class="val">{{ formData.sceneName }}</div>
                        </el-col>
                        <el-col :span="24">
                            <div class="label">场景类型</div>
                            <div class="val">{{ getDictLabel(formData.sceneType, dictObj.CommandSceneType) }}</div>
                            <div class="label">负责人</div>
                            <div class="val">{{ formData.defenseLeader }}</div>
                        </el-col>
                        <el-col :span="24">
                            <div class="label">设备模式</div>
                            <div class="val">{{ getDictLabel(formData.deviceMode, dictObj.deviceMode) }}</div>
                            <div class="label">负责人电话</div>
                            <div class="val">{{ formData.leaderPhone }}</div>
                        </el-col>
                        <el-col :span="24">
                            <div class="label">有效时间</div>
                            <div class="val">{{ formatEffectiveDate(formData) }}</div>
                        </el-col>
                        <el-col :span="24">
                            <div class="label">指挥点位置</div>
                            <div class="val">{{ formatLocation(formData) }}</div>
                        </el-col>
                        <el-col :span="24">
                            <div class="label">关联场景配置</div>
                            <div class="val">{{ getSceneConfigName(formData.defenseSceneId) }}</div>
                        </el-col>
                    </el-row>
                    <div class="detail-title">关联区域</div>
                    <div class="command-table-container">
                        <div class="command-table-content">
                            <el-table class="command-table" ref="areaTableRef" :data="areaList" row-key="id">
                                <el-table-column prop="areaName" label="区域名称" />
                                <el-table-column prop="areaType" label="区域类型">
                                    <template v-slot="{ row }">
                                        {{ getDictLabel(row.areaType, dictObj.areaType) }}
                                    </template>
                                </el-table-column>
                            </el-table>
                        </div>
                    </div>
                </div>
                <el-form
@@ -58,7 +56,7 @@
                    :model="formData"
                    :rules="rules"
                    :disabled="readonly"
                    label-width="96px"
                    label-width="110px"
                >
                    <el-form-item label="场景名称" prop="sceneName">
                        <el-input
@@ -69,66 +67,52 @@
                            clearable
                        />
                    </el-form-item>
                    <el-form-item label="场景类型" prop="sceneType">
                    <el-form-item label="关联场景配置" prop="defenseSceneId">
                        <el-select
                            class="command-select"
                            popper-class="command-select-popper"
                            v-model="formData.sceneType"
                            v-model="formData.defenseSceneId"
                            placeholder="请选择"
                            clearable
                        >
                            <el-option
                                v-for="item in dictObj.CommandSceneType"
                                :key="item.dictKey"
                                :label="item.dictValue"
                                :value="item.dictKey"
                            />
                            <el-option v-for="item in sceneConfigList" :key="item.id" :label="item.sceneName" :value="item.id" />
                        </el-select>
                    </el-form-item>
                    <el-form-item label="设备模式" prop="deviceMode">
                        <el-select
                            class="command-select"
                            popper-class="command-select-popper"
                            v-model="formData.deviceMode"
                            placeholder="请选择"
                    <el-form-item label="负责人" prop="defenseLeader">
                        <el-input
                            class="command-input"
                            v-model="formData.defenseLeader"
                            maxlength="50"
                            placeholder="请输入"
                            clearable
                        >
                            <el-option
                                v-for="item in dictObj.deviceMode"
                                :key="item.dictKey"
                                :label="item.dictValue"
                                :value="item.dictKey"
                            />
                        </el-select>
                        />
                    </el-form-item>
                    <div class="search-table-container">
                        <div class="search-box">
                            <div class="label">关联区域</div>
                            <el-input class="command-input" v-model="searchName" placeholder="请输入" clearable></el-input>
                        </div>
                        <el-form-item prop="areaDivideIds" label-width="0">
                            <el-table
                                class="command-table"
                                ref="areaTableRef"
                                :data="areaList.filter(item => item.areaName.includes(searchName))"
                                row-key="id"
                                @selection-change="handleAreaSelectionChange"
                            >
                                <el-table-column type="selection" width="55" :reserve-selection="true" />
                                <el-table-column prop="areaName" show-overflow-tooltip label="区域名称" />
                                <el-table-column prop="areaType" show-overflow-tooltip label="区域类型">
                                    <template v-slot="{ row }">
                                        {{ getDictLabel(row.areaType, dictObj.areaType) }}
                                    </template>
                                </el-table-column>
                            </el-table>
                        </el-form-item>
                    </div>
                    <el-form-item label="负责人电话" prop="leaderPhone">
                        <el-input
                            class="command-input"
                            v-model="formData.leaderPhone"
                            maxlength="50"
                            placeholder="请输入"
                            clearable
                        />
                    </el-form-item>
                    <el-form-item label="有效时间范围" prop="effectiveDateStart">
                        <el-date-picker
                            class="command-date-picker"
                            popper-class="command-date-picker-popper"
                            v-model="effectiveDateRange"
                            type="datetimerange"
                            range-separator="至"
                            start-placeholder="开始时间"
                            end-placeholder="结束时间"
                            value-format="YYYY-MM-DD HH:mm:ss"
                            clearable
                        />
                    </el-form-item>
                    <el-form-item label="指挥点位置" prop="longitude">
                        <div class="val">{{ formatLocation(formData) }}</div>
                    </el-form-item>
                </el-form>
                <div class="footer">
                    <el-button v-if="dialogMode != 'view'" color="#2B2B4C" @click="handleCancel">
                        {{ readonly ? '关闭' : '取消' }}
@@ -152,24 +136,27 @@
<script setup>
import { Close } from '@element-plus/icons-vue'
import { computed, inject, nextTick, onMounted, ref, watch } from 'vue'
import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { fwDefenseSceneDetailApi, fwDefenseSceneSubmitApi } from './sceneConfigApi'
import { fieldRules, geomAnalysis, getDictLabel } from '@ztzf/utils'
import { fwDefenseSceneManageDetailApi, fwDefenseSceneManageSubmitApi } from './sceneManageApi'
import { fwDefenseSceneListApi } from '../sceneConfig/sceneConfigApi'
import { fieldRules } from '@ztzf/utils'
import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue'
import * as Cesium from 'cesium'
import { fwAreaDivideDetailApi, fwAreaDivideListApi } from '../partition/partitionApi'
import { DrawPolygon } from '@/utils/cesium/DrawPolygon'
import commandPost from '@/assets/images/dataCockpit/legend/command-post.png'
import { saveOperationLog } from '@ztzf/apis'
import { useRoute } from 'vue-router'
// 初始化表单数据
const initForm = () => ({
    sceneName: '', // 场景名称
    sceneType: '', // 场景类型
    deviceMode: '', // 设备模式
    areaDivideIds: '', // 关联区域ID
    areaCount: 0, // 区域数量
    defenseLeader: '', // 防控负责人
    leaderPhone: '', // 防控负责人电话
    effectiveDateStart: '', // 有效时间-开始
    effectiveDateEnd: '', // 有效时间-结束
    longitude: null, // 指挥点经度
    latitude: null, // 指挥点纬度
    defenseSceneId: '', // 关联场景配置ID
})
const emit = defineEmits(['success'])
@@ -180,22 +167,22 @@
const submitting = ref(false) // 提交中
const readonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' })
const areaTableRef = ref(null)
const searchName = ref('')
const areaList = ref([]) // 关联区域列表
const selectedAreaRows = ref([]) //选中列表
const dictObj = inject('dictObj')
const effectiveDateRange = ref([]) // 有效时间范围
const sceneConfigList = ref([]) // 场景配置列表
const mapRef = ref(null)
const route = useRoute()
let viewer
let redPointEntity
let leftClickBound = false
// 表单验证规则
const rules = {
    sceneName: fieldRules(true, 50),
    sceneType: fieldRules(true),
    deviceMode: fieldRules(true),
    areaDivideIds: fieldRules(false),
    defenseLeader: fieldRules(true, 50),
    leaderPhone: fieldRules(true, 50),
    effectiveDateStart: fieldRules(true),
    longitude: fieldRules(true),
    defenseSceneId: fieldRules(true),
}
// 关闭弹框
@@ -209,13 +196,10 @@
    if (!isValid) return
    submitting.value = true
    try {
        const ids = selectedAreaRows.value.map(item => item.id)
        formData.value.areaDivideIds = ids.join(',')
        formData.value.areaCount = ids.length
        await fwDefenseSceneSubmitApi(formData.value)
        await fwDefenseSceneManageSubmitApi(formData.value)
        saveOperationLog({
            requestUri: route.path,
            title: `${route.name || '场景配置管理'}-${dialogMode.value === 'add' ? '新增' : '编辑'}`,
            title: `${route.name || '场景管理'}-${dialogMode.value === 'add' ? '新增' : '编辑'}`,
            type: 1,
        })
        ElMessage.success(dialogMode.value === 'add' ? '新增成功' : '更新成功')
@@ -226,128 +210,135 @@
    }
}
// 监听有效时间范围变化,同步到表单数据
watch(
    () => effectiveDateRange.value,
    () => {
        formData.value.effectiveDateStart = effectiveDateRange.value?.[0] || ''
        formData.value.effectiveDateEnd = effectiveDateRange.value?.[1] || ''
    }
)
// 加载详情
async function loadDetail() {
    if (!formData.value.id) return
    const res = await fwDefenseSceneDetailApi({ id: formData.value.id })
    const res = await fwDefenseSceneManageDetailApi({ id: formData.value.id })
    formData.value = res?.data?.data ?? initForm()
    // 同步有效时间范围
    effectiveDateRange.value = [formData.value.effectiveDateStart, formData.value.effectiveDateEnd].filter(Boolean)
}
// 获取区域列表
async function getAreaList() {
    if (areaList.value.length) return
    const res = await fwAreaDivideListApi({ filterSelected: 1, sceneId: formData.value.id })
    areaList.value = res?.data?.data ?? []
// 获取场景配置列表
async function getSceneConfigList() {
    if (sceneConfigList.value.length) return
    const res = await fwDefenseSceneListApi({unbound: 1})
    sceneConfigList.value = res?.data?.data ?? []
}
// 关联区域变更
async function handleAreaSelectionChange(rows) {
    const selectedRows = await ensureAreaGeom(rows)
    selectedAreaRows.value = selectedRows
    const ids = selectedRows.map(item => item.id)
    formData.value.areaDivideIds = ids.join(',')
    formData.value.areaCount = ids.length
// 获取场景配置名称
function getSceneConfigName(id) {
    if (!id) return ''
    const item = sceneConfigList.value.find(item => item.id === id)
    return item?.sceneName || id
}
// 渲染面
function renderingSurface() {
    if (!geometricSource) {
        initMap()
    }
    if (!geometricSource) return
    geometricSource && geometricSource.entities.removeAll()
    selectedAreaRows.value.forEach(item => {
        if (item.geom) {
            const pointList = geomAnalysis(item.geom)
            const result = pointList.map(item => [item.longitude, item.latitude]).flat()
            geometricSource.entities?.add({
                customType: 'control_group',
                position: Cesium.Cartesian3.fromDegrees(result[0], result[1]),
                polyline: {
                    positions: Cesium.Cartesian3.fromDegreesArray(result),
                    clampToGround: true,
                    width: 3,
                    material: Cesium.Color.RED,
                },
            })
        }
    })
// 格式化指挥点位置
function formatLocation(row) {
    if (row?.longitude == null || row?.latitude == null) return ''
    return `${_.round(row.longitude, 6)}, ${_.round(row.latitude, 6)}`
}
watch(() => selectedAreaRows.value, renderingSurface)
async function ensureAreaGeom(rows) {
    const missingRows = rows.filter(row => row?.id && !row.geom)
    if (!missingRows.length) return rows
    const detailList = await Promise.all(
        missingRows.map(row =>
            fwAreaDivideDetailApi({ id: row.id })
                .then(res => res?.data?.data)
                .catch(() => null)
        )
    )
    const detailMap = new Map(detailList.filter(Boolean).map(item => [String(item.id), item]))
    if (!detailMap.size) return rows
    areaList.value = areaList.value.map(item => {
        const detail = detailMap.get(String(item.id))
        return detail ? { ...item, ...detail } : item
    })
    return rows.map(item => {
        const detail = detailMap.get(String(item.id))
        return detail ? { ...item, ...detail } : item
    })
// 格式化有效时间
function formatEffectiveDate(row) {
    if (!row?.effectiveDateStart && !row?.effectiveDateEnd) return ''
    return `${row.effectiveDateStart || '-'} ~ ${row.effectiveDateEnd || '-'}`
}
// 同步关联区域
function syncAreaSelection() {
    if (!areaTableRef.value) return
    areaTableRef.value.clearSelection()
    const rows = []
    const areaDivideIds = formData.value.areaDivideIds
    const arr = Array.isArray(areaDivideIds)
        ? areaDivideIds.map(item => String(item))
        : String(areaDivideIds || '')
                .split(',')
                .filter(Boolean)
    areaList.value.forEach(row => {
        if (arr.includes(String(row.id))) {
            areaTableRef.value.toggleRowSelection(row, true)
            rows.push(row)
        }
    })
    selectedAreaRows.value = rows
    if (!rows.length) return
    formData.value.areaCount = rows.length
}
let geometricSource
// 初始化地图实例
function initMap() {
    if (viewer) return
    const map = mapRef.value?.getMap()
    if (!map?.viewer) return
    viewer = map.viewer
    if (!geometricSource) {
        geometricSource = new Cesium.CustomDataSource('geometricSource')
        viewer.dataSources.add(geometricSource)
    if (!readonly.value && !leftClickBound) {
        map.publicCesium?.addLeftClickEvent?.(null, LeftClickEvent)
        leftClickBound = true
    }
}
// 地图左键点击事件
function LeftClickEvent(click) {
    const pos = click.position
    // 优先使用 pickPosition 获取贴地坐标(包含地形高度)
    let cartesian3 = viewer.scene.pickPosition(pos)
    // 如果 pickPosition 失败,回退到 pickEllipsoid
    if (!cartesian3 || !Cesium.defined(cartesian3)) {
        cartesian3 = viewer.camera.pickEllipsoid(pos, viewer.scene.globe.ellipsoid)
    }
    if (!cartesian3) return
    const carto = Cesium.Cartographic.fromCartesian(cartesian3)
    const longitude = Cesium.Math.toDegrees(carto.longitude)
    const latitude = Cesium.Math.toDegrees(carto.latitude)
    formData.value.longitude = _.round(longitude, 6)
    formData.value.latitude = _.round(latitude, 6)
    setMapPoint(formData.value.longitude, formData.value.latitude)
}
// 设置地图点位
function setMapPoint(longitude, latitude) {
    if (!viewer || longitude == null || latitude == null) return
    if (!redPointEntity) {
        redPointEntity = viewer.entities.add({
            position: Cesium.Cartesian3.fromDegrees(longitude, latitude),
            billboard: {
                image: commandPost,
                width: 40,
                height: 56,
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
            },
            label: {
                text: `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`,
                font: '14px',
                fillColor: Cesium.Color.WHITE,
                backgroundColor: Cesium.Color.BLACK.withAlpha(0.6),
                backgroundPadding: new Cesium.Cartesian2(5, 5),
                showBackground: true,
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
                verticalOrigin: Cesium.VerticalOrigin.CENTER,
                outlineWidth: 1,
                outlineColor: Cesium.Color.WHITE,
                pixelOffset: new Cesium.Cartesian2(0, -72),
                heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
            },
        })
    } else {
        redPointEntity.position = Cesium.Cartesian3.fromDegrees(longitude, latitude)
        redPointEntity.label.text = `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`
    }
}
// 打开弹框
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'add'
    formData.value = dialogMode.value === 'add' ? initForm() : row
    selectedAreaRows.value = []
    formData.value = dialogMode.value === 'add' ? initForm() : { ...row }
    effectiveDateRange.value = []
    redPointEntity = null
    await nextTick()
    initMap()
    await getAreaList()
    await getSceneConfigList()
    if (dialogMode.value !== 'add') {
        await loadDetail()
    }
    await nextTick()
    syncAreaSelection()
    setMapPoint(formData.value.longitude, formData.value.latitude)
}
onMounted(() => {
    getSceneConfigList()
})
defineExpose({
    open,
})
applications/drone-command/src/views/areaManage/sceneManage/index.vue
@@ -11,24 +11,6 @@
                />
            </el-form-item>
            <el-form-item label="场景类型" prop="sceneType">
                <el-select
                    class="command-select"
                    popper-class="command-select-popper"
                    v-model="searchParams.sceneType"
                    placeholder="请选择"
                    clearable
                    @change="handleSearch"
                >
                    <el-option
                        v-for="item in dictObj.CommandSceneType"
                        :key="item.dictKey"
                        :label="item.dictValue"
                        :value="item.dictKey"
                    />
                </el-select>
            </el-form-item>
            <el-form-item class="history-search-actions">
                <el-button :icon="RefreshRight" @click="resetForm"></el-button>
                <el-button class="search-btn" :icon="Search" @click="handleSearch"></el-button>
@@ -47,14 +29,19 @@
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="sceneName" show-overflow-tooltip label="场景名称" />
                    <el-table-column prop="sceneType" show-overflow-tooltip label="场景类型">
                    <el-table-column show-overflow-tooltip label="指挥点经度纬度">
                        <template v-slot="{ row }">
                            {{ getDictLabel(row.sceneType, dictObj.CommandSceneType) }}
                            {{ formatLocation(row) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="areaCount" show-overflow-tooltip label="区域数量" />
                    <el-table-column prop="counterDeviceCount" show-overflow-tooltip label="反制设备数量" />
                    <el-table-column prop="detectDeviceCount" show-overflow-tooltip label="探测设备数量" />
                    <el-table-column prop="defenseLeader" show-overflow-tooltip label="防控负责人" />
                    <el-table-column prop="leaderPhone" show-overflow-tooltip label="防控负责人电话" />
                    <el-table-column show-overflow-tooltip label="有效时间">
                        <template v-slot="{ row }">
                            {{ formatEffectiveDate(row) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="defenseSceneId" show-overflow-tooltip label="关联场景配置ID" />
                    <el-table-column label="操作" class-name="operation-btns">
                        <template v-slot="{ row }">
                            <el-link @click="openForm('view', row)">查看</el-link>
@@ -83,18 +70,16 @@
<script setup>
import { Search, RefreshRight, Plus, Delete } from '@element-plus/icons-vue'
import { nextTick, onMounted, provide, ref } from 'vue'
import { nextTick, onMounted, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { fwDefenseScenePageApi, fwDefenseSceneRemoveApi } from './sceneConfigApi'
import { fwDefenseSceneManagePageApi, fwDefenseSceneManageRemoveApi } from './sceneManageApi'
import FormDiaLog from './FormDiaLog.vue'
import { getDictionaryByCode } from '@/api/system/dictbiz'
import { getDictLabel } from '@ztzf/utils'
import { saveOperationLog } from '@ztzf/apis'
import { useRoute } from 'vue-router'
// 初始化查询参数
const initSearchParams = () => ({
    sceneName: '', // 场景名称
    sceneType: '', // 场景类型
    current: 1, // 当前页
    size: 10, // 每页大小
})
@@ -109,11 +94,11 @@
const dialogVisible = ref(false)
const route = useRoute()
// 获取列表
// 获取列表数据
async function getList() {
    loading.value = true
    try {
        const res = await fwDefenseScenePageApi(searchParams.value)
        const res = await fwDefenseSceneManagePageApi(searchParams.value)
        list.value = res?.data?.data?.records ?? []
        total.value = res?.data?.data?.total ?? 0
    } finally {
@@ -144,10 +129,10 @@
        cancelButtonClass: 'command-message-box-cancel',
    })
    const ids = row ? row.id : selectedIds.value.join(',')
    await fwDefenseSceneRemoveApi({ ids })
    await fwDefenseSceneManageRemoveApi({ ids })
    saveOperationLog({
        requestUri: route.path,
        title: `${route.name || '场景配置管理'}-删除`,
        title: `${route.name || '场景管理'}-删除`,
        type: 1,
    })
    ElMessage.success('删除成功')
@@ -160,23 +145,16 @@
    selectedIds.value = rows.map(item => item.id)
}
// 格式化指挥点位置
function formatLocation(row) {
    if (row?.longitude == null || row?.latitude == null) return ''
    return `${_.round(row.longitude, 6)}, ${_.round(row.latitude, 6)}`
}
const dictObj = ref({
    sceneType: [], // 场景类型
    deviceMode: [], // 设备模式
    areaType: [], // 区域类型
})
provide('dictObj', dictObj)
// 获取字典
function getDictList() {
    getDictionaryByCode('CommandSceneType,deviceMode,areaType').then(res => {
        dictObj.value = res.data.data
    })
// 格式化有效时间
function formatEffectiveDate(row) {
    if (!row?.effectiveDateStart && !row?.effectiveDateEnd) return ''
    return `${row.effectiveDateStart || '-'} ~ ${row.effectiveDateEnd || '-'}`
}
// 新增/编辑/查看 弹框
@@ -189,7 +167,6 @@
onMounted(() => {
    getList()
    getDictList()
})
</script>
<style scoped lang="scss"></style>
applications/drone-command/src/views/areaManage/sceneManage/sceneConfigApi.js
File was deleted
applications/drone-command/src/views/areaManage/sceneManage/sceneManageApi.js
New file
@@ -0,0 +1,38 @@
import request from '@/axios'
// 分页查询场景管理列表
export const fwDefenseSceneManagePageApi = params => {
    return request({
        url: `/drone-fw/area/fwDefenseSceneManage/page`,
        method: 'get',
        params: { descs: 'update_time', ...params },
    })
}
// 获取场景管理详情
export const fwDefenseSceneManageDetailApi = params => {
    return request({
        url: `/drone-fw/area/fwDefenseSceneManage/detail`,
        method: 'get',
        params,
    })
}
// 新增或修改场景管理
export const fwDefenseSceneManageSubmitApi = data => {
    return request({
        url: `/drone-fw/area/fwDefenseSceneManage/submit`,
        method: 'post',
        data,
    })
}
// 删除场景管理
export const fwDefenseSceneManageRemoveApi = params => {
    return request({
        url: `/drone-fw/area/fwDefenseSceneManage/remove`,
        method: 'post',
        params,
    })
}
applications/task-work-order/src/views/orderView/orderDataManage/appInnovation/FormDiaLog.vue
@@ -6,6 +6,7 @@
        @closed="visible = false"
        destroy-on-close
        :close-on-click-modal="false"
        width="800px"
    >
        <div class="detail-container" v-if="dialogReadonly">
            <el-row class="detail-row-view">
applications/task-work-order/src/views/orderView/orderDataManage/dataObjection/FormDiaLog.vue
@@ -218,6 +218,42 @@
<!--                <el-button color="#F2F3F5" :loading="submitting" :disabled="submitting" @click="visible = false">关闭</el-button>-->
            </template>
        </template>
        <!-- 申请弹框 -->
        <el-dialog
            class="gd-dialog"
            v-model="showApplyDialog"
            title="申请"
            @closed="handleApplyDialogClosed"
            width="500px"
            destroy-on-close
            :close-on-click-modal="false"
        >
            <el-form class="gd-dialog-form" ref="applyFormRef" :model="applyFormData" :rules="applyRules">
                <el-row>
                    <el-col :span="24">
                        <el-form-item label="申请对象部门" prop="applyTargetDeptId">
                            <el-tree-select
                                class="gd-select"
                                popper-class="gd-tree-select-popper"
                                v-model="applyFormData.applyTargetDeptId"
                                node-key="id"
                                :data="deptTree"
                                :props="treeProps"
                                check-strictly
                                clearable
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
            <template #footer>
                <el-button color="#F2F3F5" @click="showApplyDialog = false">取消</el-button>
                <el-button class="" color="#4C34FF" :loading="applying" :disabled="applying" @click="handleApplySubmit">
                    确认
                </el-button>
            </template>
        </el-dialog>
    </el-dialog>
</template>
@@ -283,6 +319,14 @@
const attachmentDetailsList = ref([])
const uploading = ref(false) // 文件上传中状态
// 申请弹框相关
const showApplyDialog = ref(false)
const applyFormRef = ref(null)
const applying = ref(false)
const applyFormData = ref({
    applyTargetDeptId: ''
})
// 表单校验规则
const rules = {
    title: fieldRules(true),
@@ -300,6 +344,11 @@
const feedbackRules = {
    otherObjectionDetail: fieldRules(true),
    reviewOpinion: fieldRules(true),
}
// 申请弹框校验规则
const applyRules = {
    applyTargetDeptId: fieldRules(true),
}
// 自定义上传请求
@@ -323,9 +372,6 @@
}
// 文件移除
// const handleFileRemove = (file, fileListVal) => {
// uploadedFiles.value = fileListVal
// }
const handleFileRemove = (file, fileListVal) => {
  // 从展示列表中移除
  fileList.value = fileListVal
@@ -409,13 +455,6 @@
            }),
      objectionStatus: objectionStatus
    }
    // 如果是编辑且没有修改附件,可以保留原有附件ID
    if (dialogMode.value === 'edit' && formData.value.attachIds) {
      // 这里可以根据业务需求决定是否保留原附件ID
      // 如果接口支持更新时重新上传附件ID列表,可以这样做
    }
    await gdDataObjectionSubmitApi(submitData)
    ElMessage.success(dialogMode.value === 'add' ? '新增成功' : '更新成功')
    visible.value = false
@@ -430,9 +469,66 @@
    await submitForm('0')
}
// 申请提交
// 申请弹框关闭处理
function handleApplyDialogClosed() {
    applyFormData.value.applyTargetDeptId = ''
}
// 申请
async function handleApply() {
    await submitForm('1')
     formData.value.attachId = uploadedFiles.value.length > 0 ? uploadedFiles.value.length : ''
     // 先验证主表单
     const mainFormValid = await formRef.value?.validate().catch(() => false)
     if (!mainFormValid) return
     showApplyDialog.value = true
}
// 申请提交
async function handleApplySubmit() {
    const isValid = await applyFormRef.value?.validate().catch(() => false)
    if (!isValid) return
    applying.value = true
    try {
        // 构建申请数据
        const applyData = {
            ...formData.value,
            objectionStatus: '1',
            applyTargetDeptId: applyFormData.value.applyTargetDeptId
        }
        // 构建符合接口要求的提交数据
        const submitData = {
            // 编辑模式下传递id
            ...(applyData.id && { id: applyData.id }),
            title: applyData.title,
            objectionType: applyData.objectionType,
            submitter: applyData.submitter,
            submitterContact: applyData.submitterContact,
            catalogResourceName: applyData.catalogResourceName,
            objectionDesc: applyData.objectionDesc,
            objectionBasis: applyData.objectionBasis,
            areaCode: applyData.areaCode || '',
            handleUnit: applyData.handleUnit || '',
            // 构建附件列表 - 使用当前已上传的文件(包括回显的)
            ...(uploadedFiles.value.length > 0 && {
                attachmentList: uploadedFiles.value.map(file => ({
                    attachId: file.attachId,
                    attachName: file.originalName,
                })),
            }),
            objectionStatus: applyData.objectionStatus,
            applyTargetDeptId: applyData.applyTargetDeptId
        }
        await gdDataObjectionSubmitApi(submitData)
        ElMessage.success('申请成功')
        showApplyDialog.value = false
        visible.value = false
        emit('success')
    } finally {
        applying.value = false
    }
}
// 处理异议反馈提交
@@ -510,8 +606,6 @@
    ElMessage.warning('没有可下载的附件')
    return
  }
  // 顺序下载每个附件
  for (let i = 0; i < attachmentDetailsList.value.length; i++) {
    const attach = attachmentDetailsList.value[i]
@@ -522,10 +616,6 @@
    }
    try {
      // 显示当前下载的文件信息
      const currentFileName = attach.originalName || attach.name || `附件_${i + 1}`
      const response = await fetch(attach.link)
      const blob = await response.blob()
      const url = window.URL.createObjectURL(blob)
@@ -569,8 +659,6 @@
  }
  
}
// 打开弹框
async function open({ mode = 'add', row } = {}) {
  dialogMode.value = mode
@@ -581,6 +669,8 @@
  formData.value = dialogMode.value === 'add' ? initForm() : row
  // 重置反馈表单数据
  feedbackFormData.value = initFeedbackForm()
  // 重置申请弹框数据
  applyFormData.value.applyTargetDeptId = ''
  
  if (dialogMode.value !== 'add') {
    // 如果是编辑,设置ID并加载详情
applications/task-work-order/src/views/orderView/orderDataManage/dataObjection/index.vue
@@ -59,7 +59,7 @@
                            {{ getDictLabel(row.objectionStatus, dictObj.objectionStatus) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="catalogResourceName" width="130" show-overflow-tooltip  label="目录/申请资源名称" />
                    <el-table-column prop="catalogResourceName" width="140" show-overflow-tooltip  label="目录/申请资源名称" />
                    <el-table-column prop="otherObjectionDetail" show-overflow-tooltip label="其他异议详情" />
                    <el-table-column prop="submitter" show-overflow-tooltip label="问题提交人" />
                    <el-table-column prop="submitterContact" show-overflow-tooltip width="120" label="提交人联系方式" />
applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/FormDiaLog.vue
@@ -8,7 +8,7 @@
        :close-on-click-modal="false"
    >
        <div class="detail-container" v-if="dialogReadonly">
            <el-row class="detail-row-view">
            <el-row class="detail-row-view supplyAddLabel">
                <el-col :span="12">
                    <div class="label">需求名称</div>
                    <div class="val">{{ formData.demandName }}</div>
@@ -194,9 +194,45 @@
            >
                保存
            </el-button>
            <el-button color="#4C34FF" :loading="submitting" :disabled="submitting" @click="handleApply">申请</el-button>
            <el-button color="#4C34FF" :loading="submitting" :disabled="submitting" @click="handleApply" >申请</el-button>
        </template>
        <!-- 申请弹框 -->
        <el-dialog
            class="gd-dialog"
            v-model="showApplyDialog"
            title="申请"
            @closed="handleApplyDialogClosed"
            width="500px"
            destroy-on-close
            :close-on-click-modal="false"
        >
            <el-form class="gd-dialog-form" ref="applyFormRef" :model="applyFormData" :rules="applyRules">
                <el-row>
                    <el-col :span="24">
                        <el-form-item label="申请对象部门" prop="applyTargetDeptId">
                            <el-tree-select
                                class="gd-select"
                                popper-class="gd-tree-select-popper"
                                v-model="applyFormData.applyTargetDeptId"
                                node-key="id"
                                :data="deptTree"
                                :props="treeProps"
                                check-strictly
                                clearable
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
            <template #footer>
                <el-button color="#F2F3F5" @click="showApplyDialog = false">取消</el-button>
                <el-button class="" color="#4C34FF" :loading="applying" :disabled="applying" @click="handleApplySubmit">
                    确认
                </el-button>
            </template>
        </el-dialog>
    </el-dialog>
</template>
@@ -240,6 +276,14 @@
const dialogReadonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' })
// 申请弹框相关
const showApplyDialog = ref(false)
const applyFormRef = ref(null)
const applying = ref(false)
const applyFormData = ref({
    applyTargetDeptId: ''
})
const rules = {
    demandName: fieldRules(true),
    demandType: fieldRules(true),
@@ -253,6 +297,11 @@
    responsibleDeptId: fieldRules(true),
    dataSource: fieldRules(true),
    demandInfo: fieldRules(true),
}
// 申请弹框校验规则
const applyRules = {
    applyTargetDeptId: fieldRules(true),
}
// 根据部门ID获取部门名称
function getDeptNameById(deptId, deptList) {
@@ -295,19 +344,37 @@
    }
}
// 申请提交
// 申请弹框关闭处理
function handleApplyDialogClosed() {
    applyFormData.value.applyTargetDeptId = ''
}
// 申请
async function handleApply() {
    const isValid = await formRef.value?.validate().catch(() => false)
     // 先验证主表单
     const mainFormValid = await formRef.value?.validate().catch(() => false)
     if (!mainFormValid) return
     showApplyDialog.value = true
 }
// 申请提交
async function handleApplySubmit() {
    const isValid = await applyFormRef.value?.validate().catch(() => false)
    if (!isValid) return
    submitting.value = true
    applying.value = true
    try {
        formData.value.demandStatus = '1'
        await gdSupplyDemandSubmitApi(formData.value)
        // 构建申请数据
        const applyData = {
            ...formData.value,
            demandStatus: '1',
            applyTargetDeptId: applyFormData.value.applyTargetDeptId
        }
        await gdSupplyDemandSubmitApi(applyData)
        ElMessage.success('申请成功')
        showApplyDialog.value = false
        visible.value = false
        emit('success')
    } finally {
        submitting.value = false
        applying.value = false
    }
}
@@ -334,3 +401,10 @@
defineExpose({ open })
</script>
<style scoped lang="scss">
.supplyAddLabel {
    .label {
        width: 120px !important;
    }
}
</style>