吉安感知网项目-前端
chenyao
2026-01-14 b07d485502803e3c573ee9c3e3c86cab9bf8966d
Merge branch 'master' of http://139.196.74.78:10010/r/jagzwxm/ja_web
29 files modified
1 files renamed
6 files deleted
14 files added
4765 ■■■■ changed files
applications/drone-command/src/styles/common/cockpit.scss 245 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/areaStatistics/FormDiaLog.vue 80 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/areaStatistics/index.vue 143 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/defenseZone/index.vue 101 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue 114 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue 34 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/precinctInfo/index.vue 7 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/sceneConfig/index.vue 88 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceScrap/FormDiaLog.vue 40 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/DeviceScrapDiaLog.vue 15 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/DeviceTrackDiaLog.vue 19 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/FormDiaLog.vue 167 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/index.vue 98 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/maintainRecord/FormDiaLog.vue 121 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/maintainRecord/MaintenanceDiaLog.vue 52 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/maintainRecord/index.vue 7 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/DeviceHistoryDialog.vue 65 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/countermeasureEvaluation.vue 9 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/countermeasureEvaluation/FormDiaLog.vue 137 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/countermeasureEvaluation/countermeasureEvaluationApi.js 19 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/countermeasureEvaluation/index.vue 180 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/detectionRange.vue 191 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/detectionRange/DetectionRangeDialog.vue 39 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/detectionRange/index.vue 209 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig.vue 9 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/FormDiaLog.vue 201 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/deviceAppConfigApi.js 19 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/index.vue 136 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/taskSchedule.vue 9 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/taskSchedule/FormDiaLog.vue 383 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/taskSchedule/index.vue 232 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/taskSchedule/taskScheduleApi.js 37 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/recordManage/alarmRecords/index.vue 182 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/recordManage/historyTracks/index.vue 137 ●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/appComponents/workMap/index.vue 297 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/appDataSource/appwork/defaultAvatar.svg 17 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/appDataSource/appwork/positioning1.svg 10 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue 20 ●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/appPages/work/workDetail/mapWork/index.vue 46 ●●●● patch | view | raw | blame | history
packages/constants/fwDevice.js 8 ●●●●● patch | view | raw | blame | history
packages/utils/common/index.js 2 ●●● patch | view | raw | blame | history
uniapps/work-app/src/pages.json 7 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/pages/login/index.vue 8 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/pages/work/index.vue 526 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/static/images/tabbar/icon_speech.png patch | view | raw | blame | history
uniapps/work-app/src/static/images/tabbar/icon_speech_selected.png patch | view | raw | blame | history
uniapps/work-app/src/subPackages/userDetail/password/index.vue 2 ●●● patch | view | raw | blame | history
uniapps/work-app/src/subPackages/workDetail/addWork/index.vue 52 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/subPackages/workDetail/index.vue 219 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/subPackages/workDetail/photoMagnify/index.vue 26 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/styles/common/cockpit.scss
@@ -457,7 +457,11 @@
    }
}
.ztzf-dialog-history-search {
.el-form.ztzf-dialog-history-search {
    .el-form-item {
        margin-left: 20px;
    }
    .el-form-item__label {
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 500;
@@ -528,6 +532,20 @@
                font-size: 16px;
                color: #fff;
            }
        }
    }
    .el-input {
        width: 160px;
    }
    .el-date-editor {
        width: 226px;
    }
    .el-select {
        .el-select__wrapper {
            width: 160px;
        }
    }
}
@@ -625,6 +643,9 @@
    .el-form-item {
        margin-left: 30px;
        margin-bottom: 0;
        padding: 0;
        display: flex;
        align-items: center;
        &:first-child {
            margin-left: 0;
@@ -633,6 +654,7 @@
    .el-form-item__label {
        margin-right: 12px;
        padding: 0 !important;
        height: 40px;
        line-height: 40px;
    }
@@ -657,6 +679,16 @@
    .el-button {
        width: 96px;
        height: 40px;
    }
    .history-search-actions {
        .el-button {
            margin-left: 20px;
            &:first-child {
                margin-left: 0;
            }
        }
    }
}
@@ -827,4 +859,213 @@
            box-shadow: none;
        }
    }
}
}
.ztzf-page-view-dialog {
    display: flex;
    flex-direction: column;
    width: 770px;
    background: #1A1A2A;
    border-radius: 6px 6px 6px 6px;
    border: 1px solid #2E2E46;
    .el-dialog__header {
        span {
            font-family: Source Han Sans CN, Source Han Sans CN;
            font-weight: bold;
            font-size: 18px;
            color: #FFFFFF;
            line-height: 22px;
            text-align: left;
            font-style: normal;
            text-transform: none;
        }
        .el-icon {
            color: #FFFFFF;
        }
    }
    .el-dialog__body {
        height: 0;
        flex: 1;
        .detail-container {
            padding: 0 10px;
            .detail-title {
                margin-bottom: 30px;
                font-family: Source Han Sans CN, Source Han Sans CN;
                font-weight: 500;
                font-size: 18px;
                color: #FFFFFF;
                text-align: left;
                font-style: normal;
                text-transform: none;
            }
            .el-row {
                .el-col {
                    margin-bottom: 20px;
                    display: flex;
                    align-items: center;
                    .label {
                        width: 88px;
                        font-family: Source Han Sans CN, Source Han Sans CN;
                        font-weight: 500;
                        font-size: 14px;
                        color: #D4D5D7;
                        line-height: 22px;
                        text-align: right;
                        font-style: normal;
                        text-transform: none;
                    }
                    .val {
                        margin-left: 12px;
                        font-family: Source Han Sans CN, Source Han Sans CN;
                        font-weight: 400;
                        font-size: 14px;
                        color: #9E9EBA;
                        line-height: 22px;
                        text-align: left;
                        font-style: normal;
                        text-transform: none;
                    }
                }
            }
            .ztzf-table-container {
                height: 360px;
                .ztzf-table-pagination {
                    justify-content: center;
                }
            }
        }
        .dialog-form {
            .el-form-item {
                display: flex;
                align-items: center;
                .el-form-item__label {
                    display: flex;
                    align-items: center;
                    font-family: Source Han Sans CN, Source Han Sans CN;
                    font-weight: 500;
                    font-size: 14px;
                    color: #D4D5D7;
                    line-height: 22px;
                    text-align: right;
                    font-style: normal;
                    text-transform: none;
                }
                .el-form-item__content {
                    display: flex;
                    align-items: center;
                    .el-input {
                        width: 0;
                        flex: 1;
                    }
                    .el-textarea {
                        .el-textarea__inner {
                            color: #fff;
                            background: #2E2E48 !important;
                            border: none;
                            box-shadow: none;
                        }
                    }
                    .el-button {
                        color: #fff;
                        background: #2E2E48 !important;
                        border: none;
                        box-shadow: none;
                    }
                }
                .el-input {
                    .el-input__wrapper {
                        background: #2E2E48 !important;
                        .el-input__inner {
                            color: #ffffff;
                        }
                    }
                }
            }
        }
    }
    .el-dialog__footer {
        display: flex;
        justify-content: center;
        .el-button {
            margin-left: 16px;
            width: 96px;
            height: 36px;
            &:first-child {
                margin-left: 0;
            }
        }
    }
}
.ztzf-page-view-message-box {
    background: #1A1A2A;
    border-radius: 6px;
    border: 1px solid #2E2E46;
    .el-message-box__header {
        .el-message-box__title {
            font-family: Source Han Sans CN, Source Han Sans CN;
            font-weight: bold;
            font-size: 18px;
            color: #FFFFFF;
            line-height: 22px;
            text-align: left;
            font-style: normal;
            text-transform: none;
        }
    }
    .el-message-box__content {
        color: #D4D5D7;
    }
    .el-message-box__btns {
        display: flex;
        justify-content: center;
        .el-button {
            margin-left: 16px;
            width: 96px;
            height: 36px;
            &:first-child {
                margin-left: 0;
            }
        }
    }
}
.ztzf-message-box-confirm {
    background: #284FE3 !important;
    border-color: #284FE3 !important;
}
.ztzf-message-box-cancel {
    background: #2B2B4C !important;
    border-color: #2B2B4C !important;
    color: #FFFFFF !important;
}
applications/drone-command/src/views/areaManage/areaStatistics/FormDiaLog.vue
@@ -1,36 +1,48 @@
<template>
    <el-dialog v-model="visible" :title="titleEnum[dialogMode]" :close-on-click-modal="false" width="520px">
        <el-row>
            <el-col :span="12">
                <div>区域名称: {{ formData.areaName || '-' }}</div>
            </el-col>
            <el-col :span="12">
                <div>区域类型: {{ getDictLabel(formData.areaType, dictObj.areaType) }}</div>
            </el-col>
            <el-col :span="12">
                <div>关联场景: {{ formData.sceneName || '-' }}</div>
            </el-col>
            <el-col :span="12">
                <div>场景类型: {{ getDictLabel(formData.sceneType, dictObj.sceneType) }}</div>
            </el-col>
            <el-col :span="12">
                <div>设备数量: {{ formData.deviceCount ?? '-' }}</div>
            </el-col>
            <el-col :span="12">
                <div>告警次数: {{ formData.alarmCount ?? '-' }}</div>
            </el-col>
            <el-col :span="12">
                <div>侦测反制次数: {{ formData.counterCount ?? '-' }}</div>
            </el-col>
            <el-col :span="12">
                <div>反制成功次数: {{ formData.counterSuccessCount ?? '-' }}</div>
            </el-col>
            <el-col :span="24">
                <div>管控效果: {{ formData.controlEffect || '-' }}</div>
            </el-col>
        </el-row>
    <el-dialog class="ztzf-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]"
        :close-on-click-modal="false" width="520px">
        <div class="detail-container">
            <el-row>
                <el-col :span="12">
                    <div class="label">区域名称</div>
                    <div class="val">{{ formData.areaName || '-' }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">区域类型</div>
                    <div class="val">{{ getDictLabel(formData.areaType, dictObj.areaType) }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">关联场景</div>
                    <div class="val">{{ formData.sceneName || '-' }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">场景类型</div>
                    <div class="val">{{ getDictLabel(formData.sceneType, dictObj.sceneType) }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">设备数量</div>
                    <div class="val">{{ formData.deviceCount ?? '-' }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">告警次数</div>
                    <div class="val">{{ formData.alarmCount ?? '-' }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">侦测反制次数</div>
                    <div class="val">{{ formData.counterCount ?? '-' }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">反制成功次数</div>
                    <div class="val">{{ formData.counterSuccessCount ?? '-' }}</div>
                </el-col>
                <el-col :span="24">
                    <div class="label">管控效果</div>
                    <div class="val">{{ formData.controlEffect || '-' }}</div>
                </el-col>
            </el-row>
        </div>
        <template #footer>
            <el-button @click="handleCancel">关闭</el-button>
            <el-button color="#2B2B4C" @click="handleCancel">关闭</el-button>
        </template>
    </el-dialog>
</template>
@@ -54,9 +66,9 @@
    sceneType: '', // 场景类型
})
const formData = ref(initForm()) // 表单数据
const visible = defineModel() // 弹框显隐
const dialogMode = ref('view') // 弹框模式
const formData = ref(initForm()) // 弹框显隐
const visible = defineModel() // 弹框模式
const dialogMode = ref('view') // ????
const titleEnum = ref({ view: '查看' })
const dictObj = inject('dictObj')
applications/drone-command/src/views/areaManage/areaStatistics/index.vue
@@ -1,82 +1,75 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams">
            <el-row :gutter="16">
                <el-col :span="4">
                    <el-form-item label="区域名称" prop="areaName">
                        <el-input v-model="searchParams.areaName" placeholder="请输入" clearable @clear="handleSearch" />
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item label="区域类型" prop="areaType">
                        <el-select v-model="searchParams.areaType" placeholder="请选择" clearable @change="handleSearch">
                            <el-option
                                v-for="item in dictObj.areaType"
                                :key="item.dictKey"
                                :label="item.dictValue"
                                :value="item.dictKey"
                            />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item label="场景类型" prop="sceneType">
                        <el-select v-model="searchParams.sceneType" placeholder="请选择" clearable @change="handleSearch">
                            <el-option
                                v-for="item in dictObj.sceneType"
                                :key="item.dictKey"
                                :label="item.dictValue"
                                :value="item.dictKey"
                            />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item>
                        <el-button @click="resetForm">重置</el-button>
                        <el-button type="primary" @click="handleSearch">查询</el-button>
                    </el-form-item>
                </el-col>
            </el-row>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
            <el-form-item label="区域名称" prop="areaName">
                <el-input class="ztzf-data-cockpit-search-input" v-model="searchParams.areaName" placeholder="请输入"
                    clearable @clear="handleSearch" />
            </el-form-item>
            <el-form-item label="区域类型" prop="areaType">
                <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                    v-model="searchParams.areaType" placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in dictObj.areaType" :key="item.dictKey" :label="item.dictValue"
                        :value="item.dictKey" />
                </el-select>
            </el-form-item>
            <el-form-item label="场景类型" prop="sceneType">
                <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                    v-model="searchParams.sceneType" placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in dictObj.sceneType" :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>
            </el-form-item>
        </el-form>
        <el-table v-loading="loading" :data="list">
            <el-table-column type="index" width="60" label="序号" />
            <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-column prop="sceneName" label="关联场景" />
            <el-table-column prop="sceneType" label="场景类型">
                <template v-slot="{ row }">
                    {{ getDictLabel(row.sceneType, dictObj.sceneType) }}
                </template>
            </el-table-column>
            <el-table-column prop="deviceCount" label="设备数量" />
            <el-table-column prop="alarmCount" label="告警次数" />
            <el-table-column prop="counterCount" label="侦测反制次数" />
            <el-table-column prop="counterSuccessCount" label="反制成功次数" />
            <el-table-column prop="controlEffect" label="管控效果" />
            <el-table-column label="操作">
                <template v-slot="{ row }">
                    <el-link @click="openForm(row)" type="primary">查看</el-link>
                </template>
            </el-table-column>
        </el-table>
        <div>
            <el-pagination
                v-model:current-page="searchParams.current"
                v-model:page-size="searchParams.size"
                :total="total"
                @change="getList"
            />
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list">
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="areaName" show-overflow-tooltip width="150" label="区域名称" />
                    <el-table-column prop="areaType" show-overflow-tooltip width="130" label="区域类型">
                        <template v-slot="{ row }">
                            {{ getDictLabel(row.areaType, dictObj.areaType) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="sceneName" show-overflow-tooltip width="150" label="关联场景" />
                    <el-table-column prop="sceneType" show-overflow-tooltip width="130" label="场景类型">
                        <template v-slot="{ row }">
                            {{ getDictLabel(row.sceneType, dictObj.sceneType) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="deviceCount" show-overflow-tooltip width="110" label="设备数量" />
                    <el-table-column prop="alarmCount" show-overflow-tooltip width="110" label="告警次数" />
                    <el-table-column prop="counterCount" show-overflow-tooltip width="120" label="侦测反制次数" />
                    <el-table-column prop="counterSuccessCount" show-overflow-tooltip width="130" label="反制成功次数" />
                    <el-table-column prop="controlEffect" show-overflow-tooltip width="110" label="管控效果" />
                    <el-table-column label="操作" class-name="operation-btns">
                        <template v-slot="{ row }">
                            <el-link @click="openForm(row)">查看</el-link>
                        </template>
                    </el-table-column>
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
                <el-pagination popper-class="ztzf-data-cockpit-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>
        <FormDiaLog v-if="dialogVisible" v-model="dialogVisible" ref="dialogRef" />
    </basic-container>
</template>
<script setup>
import { Search, RefreshRight } from '@element-plus/icons-vue'
import { nextTick, onMounted, ref } from 'vue'
import { fwAreaDivideStatisticsPageApi } from './areaStatisticsApi'
import FormDiaLog from './FormDiaLog.vue'
@@ -107,7 +100,7 @@
provide('dictObj', dictObj)
// 获取列表
async function getList() {
async function getList () {
    loading.value = true
    try {
        const res = await fwAreaDivideStatisticsPageApi(searchParams.value)
@@ -119,27 +112,27 @@
}
// 查询
function handleSearch() {
function handleSearch () {
    searchParams.value.current = 1
    getList()
}
// 重置查询
function resetForm() {
function resetForm () {
    queryParamsRef.value?.resetFields()
    searchParams.value.current = 1
    getList()
}
// 获取字典
function getDictList() {
function getDictList () {
    getDictionaryByCode('areaType,sceneType').then(res => {
        dictObj.value = res.data.data
    })
}
// 打开查看弹框
function openForm(row) {
function openForm (row) {
    dialogVisible.value = true
    nextTick(() => {
        dialogRef.value?.open({ mode: 'view', row })
applications/drone-command/src/views/areaManage/defenseZone/index.vue
@@ -1,47 +1,47 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams">
            <el-row :gutter="16">
                <el-col :span="4">
                    <el-form-item label="防区名称" prop="zoneName">
                        <el-input v-model="searchParams.zoneName" placeholder="请输入" clearable @clear="handleSearch" />
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item>
                        <el-button @click="resetForm">重置</el-button>
                        <el-button type="primary" @click="handleSearch">查询</el-button>
                    </el-form-item>
                </el-col>
            </el-row>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
            <el-form-item label="防区名称" prop="zoneName">
                <el-input class="ztzf-data-cockpit-search-input" v-model="searchParams.zoneName" placeholder="请输入"
                    clearable @clear="handleSearch" />
            </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>
            </el-form-item>
        </el-form>
        <div>
            <el-button type="primary" @click="openForm('add')">新增</el-button>
        <div class="ztzf-table-toolbar">
            <el-button :icon="Plus" color="#284FE3" type="primary" @click="openForm('add')">新增</el-button>
            <el-button type="danger" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
        </div>
        <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
            <el-table-column type="selection" width="55" />
            <el-table-column type="index" width="60" label="序号" />
            <el-table-column prop="zoneName" label="防区名称" />
            <el-table-column prop="zoneArea" label="防区面积(k㎡)" />
            <el-table-column prop="sceneCount" label="场景数量" />
            <el-table-column prop="areaCount" label="区域数量" />
            <el-table-column prop="deviceCount" label="设备数量" />
            <el-table-column label="操作">
                <template v-slot="{ row }">
                    <el-link @click="openForm('view', row)" type="primary">查看</el-link>
                    <el-link @click="openForm('edit', row)" type="warning">编辑</el-link>
                    <el-link @click="handleDelete(row)" type="danger">删除</el-link>
                </template>
            </el-table-column>
        </el-table>
        <div>
            <el-pagination
                v-model:current-page="searchParams.current"
                v-model:page-size="searchParams.size"
                :total="total"
                @change="getList"
            />
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
                    <el-table-column type="selection" width="46" />
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="zoneName" show-overflow-tooltip width="150" label="防区名称" />
                    <el-table-column prop="zoneArea" show-overflow-tooltip width="130" label="防区面积(k㎡)" />
                    <el-table-column prop="sceneCount" show-overflow-tooltip width="120" label="场景数量" />
                    <el-table-column prop="areaCount" show-overflow-tooltip width="120" label="区域数量" />
                    <el-table-column prop="deviceCount" show-overflow-tooltip width="120" label="设备数量" />
                    <el-table-column label="操作" class-name="operation-btns">
                        <template v-slot="{ row }">
                            <el-link @click="openForm('view', row)">查看</el-link>
                            <el-link @click="openForm('edit', row)">编辑</el-link>
                            <el-link @click="handleDelete(row)">删除</el-link>
                        </template>
                    </el-table-column>
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
                <el-pagination popper-class="ztzf-data-cockpit-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>
        <FormDiaLog v-if="dialogVisible" v-model="dialogVisible" ref="dialogRef" @success="getList" />
@@ -49,6 +49,7 @@
</template>
<script setup>
import { Search, RefreshRight, Plus } from '@element-plus/icons-vue'
import { nextTick, onMounted, provide, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { fwDefenseZonePageApi, fwDefenseZoneRemoveApi } from './defenseZoneApi'
@@ -73,7 +74,7 @@
const dialogVisible = ref(false)
// 获取列表
async function getList() {
async function getList () {
    loading.value = true
    try {
        const res = await fwDefenseZonePageApi(searchParams.value)
@@ -85,22 +86,27 @@
}
// 查询
function handleSearch() {
function handleSearch () {
    searchParams.value.current = 1
    getList()
}
// 重置查询
function resetForm() {
function resetForm () {
    queryParamsRef.value?.resetFields()
    searchParams.value.current = 1
    getList()
}
// 删除
async function handleDelete(row) {
async function handleDelete (row) {
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { type: 'warning' })
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', {
        type: 'warning',
        customClass: 'ztzf-page-view-message-box',
        confirmButtonClass: 'ztzf-message-box-confirm',
        cancelButtonClass: 'ztzf-message-box-cancel',
    })
    const ids = row ? row.id : selectedIds.value.join(',')
    await fwDefenseZoneRemoveApi({ ids })
    ElMessage.success('删除成功')
@@ -109,7 +115,7 @@
}
// 勾选值设置
function handleSelectionChange(rows) {
function handleSelectionChange (rows) {
    selectedIds.value = rows.map(item => item.id)
}
@@ -120,21 +126,20 @@
provide('dictObj', dictObj)
// 获取字典
function getDictList() {
function getDictList () {
    getDictionaryByCode('areaType,sceneType').then(res => {
        dictObj.value = res.data.data
    })
}
// 新增/编辑/查看 弹框
function openForm(mode, row) {
function openForm (mode, row) {
    dialogVisible.value = true
    nextTick(() => {
        dialogRef.value?.open({ mode, row })
    })
}
// 初始化加载
onMounted(() => {
    getList()
    getDictList()
applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
@@ -1,32 +1,72 @@
<template>
    <el-dialog v-model="visible" :title="titleEnum[dialogMode]" :close-on-click-modal="false" width="80%">
    <el-dialog class="ztzf-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]"
        :close-on-click-modal="false" width="80%">
        <div class="bodyBox">
            <div class="leftMap ztzf-cesium" id="mapContainer"></div>
            <div class="rightInfo">
                <div v-if="readonly">
                    <div>区域名称: {{ formData.areaName }}</div>
                    <div>区域位置: {{ formatLocation(formData) }}</div>
                    <div>区域面积: {{ formData.areaSize || '-' }}k㎡</div>
                    <div>区域类型: {{ getDictLabel(formData.areaType, dictObj.areaType) }}</div>
                    <div>触发条件: {{ formData.triggerCondition }}</div>
                    <div>响应机制: {{ formData.responseMechanism }}</div>
                    <div>管控级别: {{ getDictLabel(formData.controlLevel, dictObj.controlLevel) }}</div>
                    <div>关联派出所: {{ formData.policeStationName }}</div>
                    <div>可飞行时间段: {{ formatFlyDate(formData) }}</div>
                    <el-table ref="deviceTableRef" :data="deviceOptions" row-key="id">
                        <el-table-column prop="deviceName" label="设备名称" />
                        <el-table-column prop="deviceType" label="类型">
                            <template v-slot="{ row }">
                                {{ getDictLabel(row.deviceType, dictObj.deviceType) }}
                            </template>
                        </el-table-column>
                    </el-table>
                <div class="detail-container" v-if="readonly">
                    <div class="detail-title">区域详情</div>
                    <el-row>
                        <el-col :span="24">
                            <div class="label">区域名称</div>
                            <div class="val">{{ formData.areaName }}</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">{{ formData.areaSize || '-' }}k㎡</div>
                        </el-col>
                        <el-col :span="24">
                            <div class="label">区域类型</div>
                            <div class="val">{{ getDictLabel(formData.areaType, dictObj.areaType) }}</div>
                        </el-col>
                        <el-col :span="24">
                            <div class="label">触发条件</div>
                            <div class="val">{{ formData.triggerCondition }}</div>
                        </el-col>
                        <el-col :span="24">
                            <div class="label">响应机制</div>
                            <div class="val">{{ formData.responseMechanism }}</div>
                        </el-col>
                        <el-col :span="24">
                            <div class="label">管控级别</div>
                            <div class="val">{{ getDictLabel(formData.controlLevel, dictObj.controlLevel) }}</div>
                        </el-col>
                        <el-col :span="24">
                            <div class="label">关联派出所</div>
                            <div class="val">{{ formData.policeStationName }}</div>
                        </el-col>
                        <el-col :span="24">
                            <div class="label">可飞行时间段</div>
                            <div class="val">{{ formatFlyDate(formData) }}</div>
                        </el-col>
                    </el-row>
                    <div class="detail-title">关联设备</div>
                    <div class="ztzf-table-container">
                        <div class="ztzf-table-content">
                            <el-table class="ztzf-data-cockpit-table" ref="deviceTableRef" :data="deviceOptions"
                                row-key="id">
                                <el-table-column prop="deviceName" label="设备名称" />
                                <el-table-column prop="deviceType" label="类型">
                                    <template v-slot="{ row }">
                                        {{ getDictLabel(row.deviceType, dictObj.deviceType) }}
                                    </template>
                                </el-table-column>
                            </el-table>
                        </div>
                    </div>
                </div>
                <el-form v-else ref="formRef" :model="formData" :rules="rules" :disabled="readonly" label-width="100px">
                <el-form class="dialog-form" v-else ref="formRef" :model="formData" :rules="rules" :disabled="readonly"
                    label-width="100px">
                    <el-row>
                        <el-col :span="24">
                            <el-form-item label="区域名称" prop="areaName">
                                <el-input v-model="formData.areaName" maxlength="50" placeholder="请输入" clearable />
                                <el-input class="ztzf-data-cockpit-search-input" v-model="formData.areaName"
                                    maxlength="50" placeholder="请输入" clearable />
                            </el-form-item>
                        </el-col>
                        <el-col :span="24">
@@ -39,7 +79,8 @@
                        </el-col>
                        <el-col :span="24">
                            <el-form-item label="区域类型" prop="areaType">
                                <el-select v-model="formData.areaType" placeholder="请选择" clearable>
                                <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                                    v-model="formData.areaType" placeholder="请选择" clearable>
                                    <el-option
                                        v-for="item in dictObj.areaType"
                                        :key="item.dictKey"
@@ -51,17 +92,20 @@
                        </el-col>
                        <el-col :span="24">
                            <el-form-item label="响应机制" prop="responseMechanism">
                                <el-input v-model="formData.responseMechanism" maxlength="200" placeholder="请输入" clearable />
                                <el-input class="ztzf-data-cockpit-search-input" v-model="formData.responseMechanism"
                                    maxlength="200" placeholder="请输入" clearable />
                            </el-form-item>
                        </el-col>
                        <el-col :span="24">
                            <el-form-item label="触发条件" prop="triggerCondition">
                                <el-input v-model="formData.triggerCondition" maxlength="200" placeholder="请输入" clearable />
                                <el-input class="ztzf-data-cockpit-search-input" v-model="formData.triggerCondition"
                                    maxlength="200" placeholder="请输入" clearable />
                            </el-form-item>
                        </el-col>
                        <el-col :span="24">
                            <el-form-item label="管控级别" prop="controlLevel">
                                <el-select v-model="formData.controlLevel" placeholder="请选择" clearable>
                                <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                                    v-model="formData.controlLevel" placeholder="请选择" clearable>
                                    <el-option
                                        v-for="item in dictObj.controlLevel"
                                        :key="item.dictKey"
@@ -74,6 +118,8 @@
                        <el-col :span="24">
                            <el-form-item label="可飞行时段" prop="flyDateStart">
                                <el-date-picker
                                    class="ztzf-data-cockpit-date-picker"
                                    popper-class="ztzf-data-cockpit-date-picker-popper"
                                    v-model="flyDateRange"
                                    type="datetimerange"
                                    range-separator="至"
@@ -86,7 +132,8 @@
                        </el-col>
                        <el-col :span="24">
                            <el-form-item label="关联派出所" prop="policeStationId">
                                <el-select v-model="formData.policeStationId" placeholder="请选择" clearable>
                                <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                                    v-model="formData.policeStationId" placeholder="请选择" clearable>
                                    <el-option
                                        v-for="item in policeStationOptions"
                                        :key="item.id"
@@ -100,10 +147,12 @@
                            <div>
                                <div>
                                    关联设备
                                    <el-input v-model="searchName" placeholder="请输入" clearable></el-input>
                                    <el-input class="ztzf-data-cockpit-search-input" v-model="searchName" placeholder="请输入"
                                        clearable></el-input>
                                </div>
                                <el-form-item prop="deviceIds" label-width="0">
                                    <el-table
                                        class="ztzf-data-cockpit-table"
                                        ref="deviceTableRef"
                                        :data="deviceOptions.filter(item => item.deviceName.includes(searchName))"
                                        row-key="id"
@@ -125,8 +174,9 @@
            </div>
        </div>
        <template #footer>
            <el-button @click="handleCancel">{{ readonly ? '关闭' : '取消' }}</el-button>
            <el-button v-if="!readonly" type="primary" :loading="submitting" :disabled="submitting" @click="handleSubmit">
            <el-button color="#2B2B4C" @click="handleCancel">{{ readonly ? '关闭' : '取消' }}</el-button>
            <el-button color="#284FE3" v-if="!readonly" type="primary" :loading="submitting" :disabled="submitting"
                @click="handleSubmit">
                确定
            </el-button>
        </template>
@@ -391,6 +441,12 @@
        width: 30%;
        height: 100%;
        overflow: auto;
        .ztzf-table-container,
        .ztzf-table-content {
            height: auto;
            flex: none;
        }
    }
}
</style>
applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue
@@ -1,36 +1,45 @@
<template>
    <el-dialog v-model="visible" :title="titleEnum[dialogMode]">
        <div v-if="readonly">
    <el-dialog class="ztzf-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]">
        <div class="detail-container" v-if="readonly">
            <div class="detail-title">派出所详情</div>
            <el-row>
                <el-col :span="12">
                    <div>派出所名称: {{ formData.stationName }}</div>
                    <div class="label">派出所名称</div>
                    <div class="val">{{ formData.stationName }}</div>
                </el-col>
                <el-col :span="12">
                    <div>联系人: {{ formData.contactPerson }}</div>
                    <div class="label">联系人</div>
                    <div class="val">{{ formData.contactPerson }}</div>
                </el-col>
                <el-col :span="12">
                    <div>位置: {{ formData.address }}</div>
                    <div class="label">位置</div>
                    <div class="val">{{ formData.address }}</div>
                </el-col>
                <el-col :span="12">
                    <div>联系方式: {{ formData.contactPhone }}</div>
                    <div class="label">联系方式</div>
                    <div class="val">{{ formData.contactPhone }}</div>
                </el-col>
            </el-row>
        </div>
        <el-form v-else ref="formRef" :model="formData" :rules="rules" :disabled="readonly" label-width="100px">
        <el-form class="dialog-form" v-else ref="formRef" :model="formData" :rules="rules" :disabled="readonly"
            label-width="100px">
            <el-row>
                <el-col :span="12">
                    <el-form-item label="派出所名称" prop="stationName">
                        <el-input v-model="formData.stationName" maxlength="50" placeholder="请输入" clearable />
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.stationName" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="联系人" prop="contactPerson">
                        <el-input v-model="formData.contactPerson" maxlength="50" placeholder="请输入" clearable />
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.contactPerson" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="联系方式" prop="contactPhone">
                        <el-input v-model="formData.contactPhone" maxlength="50" placeholder="请输入" clearable />
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.contactPhone" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="24">
@@ -43,8 +52,9 @@
            </el-row>
        </el-form>
        <template #footer>
            <el-button @click="handleCancel">{{ readonly ? '关闭' : '取消' }}</el-button>
            <el-button v-if="!readonly" type="primary" :loading="submitting" :disabled="submitting" @click="handleSubmit">
            <el-button color="#2B2B4C" @click="handleCancel">{{ readonly ? '关闭' : '取消' }}</el-button>
            <el-button color="#284FE3" v-if="!readonly" type="primary" :loading="submitting" :disabled="submitting"
                @click="handleSubmit">
                确定
            </el-button>
        </template>
applications/drone-command/src/views/areaManage/precinctInfo/index.vue
@@ -97,7 +97,12 @@
// 删除
async function handleDelete (row) {
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { type: 'warning' })
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', {
        type: 'warning',
        customClass: 'ztzf-page-view-message-box',
        confirmButtonClass: 'ztzf-message-box-confirm',
        cancelButtonClass: 'ztzf-message-box-cancel',
    })
    const ids = row ? row.id : selectedIds.value.join(',')
    await fwPoliceStationRemoveApi({ ids })
    ElMessage.success('删除成功')
applications/drone-command/src/views/areaManage/sceneConfig/index.vue
@@ -2,22 +2,48 @@
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
            <el-form-item label="场景名称" prop="sceneName">
                <el-input class="ztzf-data-cockpit-search-input" v-model="searchParams.sceneName" placeholder="请输入"
                    clearable @clear="handleSearch" />
                <el-input
                    class="ztzf-data-cockpit-search-input"
                    v-model="searchParams.sceneName"
                    placeholder="请输入"
                    clearable
                    @clear="handleSearch"
                />
            </el-form-item>
            <el-form-item label="场景类型" prop="sceneType">
                <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                    v-model="searchParams.sceneType" placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in dictObj.sceneType" :key="item.dictKey" :label="item.dictValue"
                        :value="item.dictKey" />
                <el-select
                    class="ztzf-data-cockpit-select"
                    popper-class="ztzf-data-cockpit-select-popper"
                    v-model="searchParams.sceneType"
                    placeholder="请选择"
                    clearable
                    @change="handleSearch"
                >
                    <el-option
                        v-for="item in dictObj.sceneType"
                        :key="item.dictKey"
                        :label="item.dictValue"
                        :value="item.dictKey"
                    />
                </el-select>
            </el-form-item>
            <el-form-item label="场景状态" prop="status">
                <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                    v-model="searchParams.status" placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
                <el-select
                    class="ztzf-data-cockpit-select"
                    popper-class="ztzf-data-cockpit-select-popper"
                    v-model="searchParams.status"
                    placeholder="请选择"
                    clearable
                    @change="handleSearch"
                >
                    <el-option
                        v-for="item in dictObj.sceneStatus"
                        :key="item.dictKey"
                        :label="item.dictValue"
                        :value="item.dictKey"
                    />
                </el-select>
            </el-form-item>
@@ -37,7 +63,7 @@
                <el-table class="ztzf-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
                    <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 width="130" label="场景名称" />
                    <el-table-column prop="sceneType" show-overflow-tooltip width="120" label="场景类型">
                        <template v-slot="{ row }">
@@ -65,9 +91,14 @@
            </div>
            <div class="ztzf-table-pagination">
                <el-pagination popper-class="ztzf-data-cockpit-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="ztzf-data-cockpit-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>
@@ -101,13 +132,8 @@
const dialogRef = ref(null) // 弹框实例
const dialogVisible = ref(false)
const statusOptions = ref([
    { label: '正常', value: 0 },
    { label: '停用', value: 1 },
])
// 获取列表
async function getList () {
async function getList() {
    loading.value = true
    try {
        const res = await fwDefenseScenePageApi(searchParams.value)
@@ -119,22 +145,27 @@
}
// 查询
function handleSearch () {
function handleSearch() {
    searchParams.value.current = 1
    getList()
}
// 重置查询
function resetForm () {
function resetForm() {
    queryParamsRef.value?.resetFields()
    searchParams.value.current = 1
    getList()
}
// 删除
async function handleDelete (row) {
async function handleDelete(row) {
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { type: 'warning' })
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', {
        type: 'warning',
        customClass: 'ztzf-page-view-message-box',
        confirmButtonClass: 'ztzf-message-box-confirm',
        cancelButtonClass: 'ztzf-message-box-cancel',
    })
    const ids = row ? row.id : selectedIds.value.join(',')
    await fwDefenseSceneRemoveApi({ ids })
    ElMessage.success('删除成功')
@@ -143,11 +174,11 @@
}
// 勾选值设置
function handleSelectionChange (rows) {
function handleSelectionChange(rows) {
    selectedIds.value = rows.map(item => item.id)
}
function formatLocation (row) {
function formatLocation(row) {
    if (row?.longitude == null || row?.latitude == null) return ''
    return `${_.round(row.longitude, 6)}, ${_.round(row.latitude, 6)}`
}
@@ -156,18 +187,19 @@
    sceneType: [], // 场景类型
    deviceMode: [], // 设备模式
    areaType: [], // 区域类型
    sceneStatus: [], // 场景状态
})
provide('dictObj', dictObj)
// 获取字典
function getDictList () {
    getDictionaryByCode('sceneType,deviceMode,areaType').then(res => {
function getDictList() {
    getDictionaryByCode('sceneType,deviceMode,areaType,sceneStatus').then(res => {
        dictObj.value = res.data.data
    })
}
// 新增/编辑/查看 弹框
function openForm (mode, row) {
function openForm(mode, row) {
    dialogVisible.value = true
    nextTick(() => {
        dialogRef.value?.open({ mode, row })
applications/drone-command/src/views/basicManage/deviceScrap/FormDiaLog.vue
@@ -1,44 +1,56 @@
<template>
    <el-dialog v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed" destroy-on-close>
        <div v-if="dialogReadonly">
    <el-dialog class="ztzf-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed"
        destroy-on-close>
        <div class="detail-container" v-if="dialogReadonly">
            <el-row>
                <el-col :span="12">
                    <div>设备名称: {{ formData.deviceName }}</div>
                    <div class="label">设备名称</div>
                    <div class="val">{{ formData.deviceName }}</div>
                </el-col>
                <el-col :span="12">
                    <div>设备类型: {{ getDictLabel(formData.deviceType, dictObj.deviceType) }}</div>
                    <div class="label">设备类型</div>
                    <div class="val">{{ getDictLabel(formData.deviceType, dictObj.deviceType) }}</div>
                </el-col>
                <el-col :span="12">
                    <div>型号 : {{ formData.deviceModel }}</div>
                    <div class="label">型号</div>
                    <div class="val">{{ formData.deviceModel }}</div>
                </el-col>
                <el-col :span="12">
                    <div>生产厂商 : {{ formData.manufacturer }}</div>
                    <div class="label">生产厂商</div>
                    <div class="val">{{ formData.manufacturer }}</div>
                </el-col>
                <el-col :span="12">
                    <div>入库时间 : {{ formData.createTime }}</div>
                    <div class="label">入库时间</div>
                    <div class="val">{{ formData.createTime }}</div>
                </el-col>
                <el-col :span="12">
                    <div>来源 : {{ formData.source }}</div>
                    <div class="label">来源</div>
                    <div class="val">{{ formData.source }}</div>
                </el-col>
                <el-col :span="12">
                    <div>所属部门 : {{ formData.belongDeptName }}</div>
                    <div class="label">所属部门</div>
                    <div class="val">{{ formData.belongDeptName }}</div>
                </el-col>
                <el-col :span="12">
                    <div>负责人 : {{ formData.charger }}</div>
                    <div class="label">负责人</div>
                    <div class="val">{{ formData.charger }}</div>
                </el-col>
                <el-col :span="12">
                    <div>报废时间 : {{ formData.scrapTime }}</div>
                    <div class="label">报废时间</div>
                    <div class="val">{{ formData.scrapTime }}</div>
                </el-col>
                <el-col :span="24">
                    <div>报废原因 : {{ formData.scrapReason }}</div>
                    <div class="label">报废原因</div>
                    <div class="val">{{ formData.scrapReason }}</div>
                </el-col>
                <el-col :span="24">
                    <div>处置方式 : {{ formData.disposeWay }}</div>
                    <div class="label">处置方式</div>
                    <div class="val">{{ formData.disposeWay }}</div>
                </el-col>
            </el-row>
        </div>
        <template #footer>
            <el-button @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
            <el-button color="#2B2B4C" @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
        </template>
    </el-dialog>
</template>
applications/drone-command/src/views/basicManage/deviceStock/DeviceScrapDiaLog.vue
@@ -1,20 +1,20 @@
<template>
    <el-dialog v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close>
        <el-form ref="formRef" :model="formData" :rules="rules" label-width="100px">
    <el-dialog class="ztzf-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close>
        <el-form class="dialog-form" ref="formRef" :model="formData" :rules="rules" label-width="100px">
            <el-row>
                <el-col :span="24">
                    <el-form-item label="报废原因" prop="scrapReason">
                        <el-input v-model="formData.scrapReason" maxlength="200" type="textarea" placeholder="请输入" clearable />
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.scrapReason" maxlength="200" type="textarea" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="24">
                    <el-form-item label="处置方式" prop="disposeWay">
                        <el-input v-model="formData.disposeWay" maxlength="200" type="textarea" placeholder="请输入" clearable />
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.disposeWay" maxlength="200" type="textarea" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="报废时间" prop="scrapTime">
                        <el-date-picker
                        <el-date-picker class="ztzf-data-cockpit-date-picker" popper-class="ztzf-data-cockpit-date-picker-popper"
                            v-model="formData.scrapTime"
                            type="date"
                            placeholder="选择日期"
@@ -26,10 +26,11 @@
            </el-row>
        </el-form>
        <template #footer>
            <el-button @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
            <el-button color="#2B2B4C" @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
            <el-button
                v-if="!dialogReadonly"
                type="primary"
                color="#284FE3"
                :loading="submitting"
                :disabled="submitting"
                @click="handleSubmit"
@@ -87,7 +88,7 @@
    submitting.value = true
    try {
        await fwDeviceScrapSaveApi(formData.value)
        ElMessage.success(dialogMode.value === 'add' ? '出库成功' : '更新成功')
        ElMessage.success('报废成功')
        visible.value = false
        emit('success')
    } finally {
applications/drone-command/src/views/basicManage/deviceStock/DeviceTrackDiaLog.vue
@@ -1,27 +1,27 @@
<template>
    <el-dialog v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close>
        <el-form ref="formRef" :model="formData" :rules="rules" label-width="100px">
    <el-dialog class="ztzf-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close>
        <el-form class="dialog-form" ref="formRef" :model="formData" :rules="rules" label-width="100px">
            <el-row>
                <el-col :span="12">
                    <el-form-item label="负责人" prop="charger">
                        <el-select v-model="formData.charger" placeholder="请选择" clearable>
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper" v-model="formData.charger" placeholder="请选择" clearable>
                            <el-option v-for="item in userList" :key="item.id" :label="item.name" :value="item.name" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="联系电话" prop="contactPhone">
                        <el-input v-model="formData.contactPhone" maxlength="50" placeholder="请输入" clearable />
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.contactPhone" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="出库去向" prop="outTarget">
                        <el-input v-model="formData.outTarget" maxlength="50" placeholder="请输入" clearable />
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.outTarget" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="出库时间" prop="outTime">
                        <el-date-picker
                        <el-date-picker class="ztzf-data-cockpit-date-picker" popper-class="ztzf-data-cockpit-date-picker-popper"
                            v-model="formData.outTime"
                            type="date"
                            placeholder="选择日期"
@@ -32,16 +32,17 @@
                </el-col>
                <el-col :span="12">
                    <el-form-item label="用途" prop="purpose">
                        <el-input v-model="formData.purpose" maxlength="50" placeholder="请输入" clearable />
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.purpose" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <template #footer>
            <el-button @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
            <el-button color="#2B2B4C" @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
            <el-button
                v-if="!dialogReadonly"
                type="primary"
                color="#284FE3"
                :loading="submitting"
                :disabled="submitting"
                @click="handleSubmit"
@@ -111,7 +112,7 @@
    submitting.value = true
    try {
        await fwDeviceTrackSubmitApi(formData.value)
        ElMessage.success(dialogMode.value === 'add' ? '出库成功' : '更新成功')
        ElMessage.success('出库成功')
        visible.value = false
        emit('success')
    } finally {
applications/drone-command/src/views/basicManage/deviceStock/FormDiaLog.vue
@@ -1,152 +1,161 @@
<template>
    <el-dialog v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close>
        <div v-if="dialogReadonly">
            <h3>设备详情</h3>
    <el-dialog class="ztzf-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed"
        destroy-on-close>
        <div class="detail-container" v-if="dialogReadonly">
            <div class="detail-title">设备详情</div>
            <el-row>
                <el-col :span="12">
                    <div>设备信息: {{ formData.deviceName }}</div>
                    <div class="label">设备信息</div>
                    <div class="val">{{ formData.deviceName }}</div>
                </el-col>
                <el-col :span="12">
                    <div>设备类型: {{ formData.deviceType }}</div>
                    <div class="label">设备类型</div>
                    <div class="val">{{ formData.deviceType }}</div>
                </el-col>
                <el-col :span="12">
                    <div>设备属性: {{ formData.deviceAtt }}</div>
                    <div class="label">设备属性</div>
                    <div class="val">{{ formData.deviceAtt }}</div>
                </el-col>
                <el-col :span="12">
                    <div>型号: {{ formData.deviceModel }}</div>
                    <div class="label">型号</div>
                    <div class="val">{{ formData.deviceModel }}</div>
                </el-col>
                <el-col :span="12">
                    <div>规格: {{ formData.deviceSpecification }}</div>
                    <div class="label">规格</div>
                    <div class="val">{{ formData.deviceSpecification }}</div>
                </el-col>
                <el-col :span="12">
                    <div>生产厂商: {{ formData.manufacturer }}</div>
                    <div class="label">生产厂商</div>
                    <div class="val">{{ formData.manufacturer }}</div>
                </el-col>
                <el-col :span="12">
                    <div>来源: {{ formData.source }}</div>
                    <div class="label">来源</div>
                    <div class="val">{{ formData.source }}</div>
                </el-col>
                <el-col :span="12">
                    <div>用途: {{ formData.purpose }}</div>
                    <div class="label">用途</div>
                    <div class="val">{{ formData.purpose }}</div>
                </el-col>
                <el-col :span="12">
                    <div>所属部门: {{ formData.belongDeptName }}</div>
                    <div class="label">所属部门</div>
                    <div class="val">{{ formData.belongDeptName }}</div>
                </el-col>
                <el-col :span="12">
                    <div>负责人: {{ formData.charger }}</div>
                    <div class="label">负责人</div>
                    <div class="val">{{ formData.charger }}</div>
                </el-col>
                <el-col :span="12">
                    <div>联系电话: {{ formData.contactPhone }}</div>
                    <div class="label">联系电话</div>
                    <div class="val">{{ formData.contactPhone }}</div>
                </el-col>
            </el-row>
            <h3>出库去向详情</h3>
            <el-table v-loading="loading" :data="list">
                <el-table-column type="index" width="60" label="序号" />
                <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="purpose" label="用途" />
            </el-table>
            <el-pagination
                v-model:current-page="searchParams.current"
                v-model:page-size="searchParams.size"
                :total="total"
                @change="getList"
            />
            <div class="detail-title">出库去向详情</div>
            <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
                <div class="ztzf-table-content">
                    <el-table class="ztzf-data-cockpit-table" :data="list">
                        <el-table-column type="index" width="60" label="序号" />
                        <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="purpose" label="用途" />
                    </el-table>
                </div>
                <div class="ztzf-table-pagination">
                    <el-pagination popper-class="ztzf-data-cockpit-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>
        <el-form v-else ref="formRef" :model="formData" :rules="rules" label-width="100px">
        <el-form class="dialog-form" v-else ref="formRef" :model="formData" :rules="rules" label-width="100px">
            <el-row>
                <el-col :span="12">
                    <el-form-item label="设备名称" prop="deviceName">
                        <el-input v-model="formData.deviceName" maxlength="50" placeholder="请输入" clearable />
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.deviceName" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="设备类型" prop="deviceType">
                        <el-select v-model="formData.deviceType" placeholder="请选择" clearable>
                            <el-option
                                v-for="item in dictObj.deviceType"
                                :key="item.dictKey"
                                :label="item.dictValue"
                                :value="item.dictKey"
                            />
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                            v-model="formData.deviceType" placeholder="请选择" clearable>
                            <el-option v-for="item in dictObj.deviceType" :key="item.dictKey" :label="item.dictValue"
                                :value="item.dictKey" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="设备属性" prop="deviceAtt">
                        <el-select v-model="formData.deviceAtt" placeholder="请选择" clearable>
                            <el-option
                                v-for="item in dictObj.deviceAtt"
                                :key="item.dictKey"
                                :label="item.dictValue"
                                :value="item.dictKey"
                            />
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                            v-model="formData.deviceAtt" placeholder="请选择" clearable>
                            <el-option v-for="item in dictObj.deviceAtt" :key="item.dictKey" :label="item.dictValue"
                                :value="item.dictKey" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="型号" prop="deviceModel">
                        <el-input v-model="formData.deviceModel" maxlength="50" placeholder="请输入" clearable />
                        <el-input class="ztzf-data-cockpit-search-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 v-model="formData.deviceSpecification" maxlength="50" placeholder="请输入" clearable />
                        <el-input class="ztzf-data-cockpit-search-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 v-model="formData.manufacturer" maxlength="50" placeholder="请输入" clearable />
                        <el-input class="ztzf-data-cockpit-search-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 v-model="formData.source" maxlength="50" placeholder="请输入" clearable />
                        <el-input class="ztzf-data-cockpit-search-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 v-model="formData.purpose" maxlength="50" placeholder="请输入" clearable />
                        <el-input class="ztzf-data-cockpit-search-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
                            v-model="formData.belongDept"
                            node-key="id"
                            :data="deptTree"
                            :props="treeProps"
                            check-strictly
                            @change="belongDeptChange"
                            clearable
                        />
                        <el-tree-select class="ztzf-data-cockpit-select"
                            popper-class="ztzf-data-cockpit-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">
                    <el-form-item label="负责人" prop="charger">
                        <el-select v-model="formData.charger" placeholder="请选择" :disabled="!formData.belongDept" clearable>
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                            v-model="formData.charger" placeholder="请选择" :disabled="!formData.belongDept" clearable>
                            <el-option v-for="item in userList" :key="item.id" :label="item.name" :value="item.name" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="联系电话" prop="contactPhone">
                        <el-input v-model="formData.contactPhone" maxlength="50" placeholder="请输入" clearable />
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.contactPhone" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <template #footer>
            <el-button @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
            <el-button
                v-if="!dialogReadonly"
                type="primary"
                :loading="submitting"
                :disabled="submitting"
                @click="handleSubmit"
            >
                确定
            <el-button color="#2B2B4C" @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
            <el-button color="#284FE3" v-if="!dialogReadonly" type="primary" :loading="submitting"
                :disabled="submitting" @click="handleSubmit">
                保存
            </el-button>
        </template>
    </el-dialog>
@@ -155,7 +164,7 @@
<script setup>
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { fwDeviceDetailApi,  fwDeviceSubmitApi } from '@/views/basicManage/deviceStock/fwDevice'
import { fwDeviceDetailApi, fwDeviceSubmitApi } from '@/views/basicManage/deviceStock/fwDevice'
import { getUserListApi } from '@/api/system/user'
import { fieldRules, getDictLabel } from '@ztzf/utils'
import { fwDeviceTrackPageApi } from '@/views/basicManage/deviceStock/fwDeviceTrackApi'
@@ -219,11 +228,11 @@
const userList = ref([])
function belongDeptChange() {
function belongDeptChange () {
    formData.value.charger = ''
}
function getUserList() {
function getUserList () {
    getUserListApi(formData.value.belongDept).then(res => {
        userList.value = res.data.data.records
    })
@@ -232,12 +241,12 @@
watch(() => formData.value.belongDept, getUserList)
// 关闭弹框
function handleCancel() {
function handleCancel () {
    visible.value = false
}
// 提交新增/编辑
async function handleSubmit() {
async function handleSubmit () {
    const isValid = await formRef.value?.validate().catch(() => false)
    if (!isValid) return
    submitting.value = true
@@ -252,20 +261,20 @@
}
// 加载详情
async function loadDetail() {
async function loadDetail () {
    if (!formData.value.id) return
    const res = await fwDeviceDetailApi({ id: formData.value.id })
    formData.value = res?.data?.data ?? {}
}
// 关闭后重置
function handleClosed() {
function handleClosed () {
    formData.value = initForm()
}
const total = ref(0) // 总条数
const loading = ref(false) // 列表加载中
const list = ref([]) // 列表数据
async function getList() {
async function getList () {
    loading.value = true
    try {
        const params = { ...searchParams.value, deviceId: formData.value.id }
@@ -278,7 +287,7 @@
}
// 打开弹框
async function open({ mode, row } = {}) {
async function open ({ mode, row } = {}) {
    dialogMode.value = mode || 'add'
    visible.value = true
    if (dialogMode.value === 'add') {
applications/drone-command/src/views/basicManage/deviceStock/index.vue
@@ -2,29 +2,59 @@
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
            <el-form-item label="名称" prop="deviceName">
                <el-input class="ztzf-data-cockpit-search-input" v-model="searchParams.deviceName" placeholder="请输入"
                    clearable @clear="handleSearch" />
                <el-input
                    class="ztzf-data-cockpit-search-input"
                    v-model="searchParams.deviceName"
                    placeholder="请输入"
                    clearable
                    @clear="handleSearch"
                />
            </el-form-item>
            <el-form-item label="类型" prop="deviceType">
                <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                    v-model="searchParams.deviceType" placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in dictObj.deviceType" :key="item.dictKey" :label="item.dictValue"
                        :value="item.dictKey" />
                <el-select
                    class="ztzf-data-cockpit-select"
                    popper-class="ztzf-data-cockpit-select-popper"
                    v-model="searchParams.deviceType"
                    placeholder="请选择"
                    clearable
                    @change="handleSearch"
                >
                    <el-option
                        v-for="item in dictObj.deviceType"
                        :key="item.dictKey"
                        :label="item.dictValue"
                        :value="item.dictKey"
                    />
                </el-select>
            </el-form-item>
            <el-form-item label="部门" prop="belongDept">
                <el-tree-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-tree-select-popper"
                    v-model="searchParams.belongDept" :data="deptTree" :props="treeProps" node-key="id" check-strictly
                    clearable @change="handleSearch" />
                <el-tree-select
                    class="ztzf-data-cockpit-select"
                    popper-class="ztzf-data-cockpit-tree-select-popper"
                    v-model="searchParams.belongDept"
                    :data="deptTree"
                    :props="treeProps"
                    node-key="id"
                    check-strictly
                    clearable
                    @change="handleSearch"
                />
            </el-form-item>
            <el-form-item label="时间">
                <el-date-picker popper-class="ztzf-data-cockpit-date-picker-popper"
                    class="ztzf-data-cockpit-date-picker" v-model="dateRange" type="daterange" range-separator="至"
                    start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD HH:mm:ss"
                    @change="handleSearch" />
                <el-date-picker
                    popper-class="ztzf-data-cockpit-date-picker-popper"
                    class="ztzf-data-cockpit-date-picker"
                    v-model="dateRange"
                    type="daterange"
                    range-separator="至"
                    start-placeholder="开始日期"
                    end-placeholder="结束日期"
                    value-format="YYYY-MM-DD HH:mm:ss"
                    @change="handleSearch"
                />
            </el-form-item>
            <el-form-item class="history-search-actions">
@@ -64,7 +94,7 @@
                    <el-table-column prop="charger" show-overflow-tooltip width="100" label="负责人" />
                    <el-table-column prop="yxzt" show-overflow-tooltip width="116" label="运行状态">
                        <template v-slot="{ row }">
                            {{ DEVICE_STATUS[row.status] || '-' }}
                            {{ getDictLabel(row.status, dictObj.deviceStatus) }}
                        </template>
                    </el-table-column>
                    <el-table-column label="操作" class-name="operation-btns">
@@ -80,9 +110,14 @@
            </div>
            <div class="ztzf-table-pagination">
                <el-pagination popper-class="ztzf-data-cockpit-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="ztzf-data-cockpit-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>
@@ -101,7 +136,6 @@
import { getDictionaryByCode } from '@/api/system/dictbiz'
import { getDeptTree } from '@/api/system/dept'
import { blobDownload, dateRangeFormat, getDictLabel, formatDateToSlash } from '@ztzf/utils'
import { DEVICE_STATUS } from '@ztzf/constants'
import DeviceTrackDiaLog from '@/views/basicManage/deviceStock/DeviceTrackDiaLog.vue'
import DeviceScrapDiaLog from '@/views/basicManage/deviceStock/DeviceScrapDiaLog.vue'
@@ -136,7 +170,7 @@
provide('deptTree', deptTree)
// 获取列表
async function getList () {
async function getList() {
    const range = dateRangeFormat(dateRange.value)
    loading.value = true
    try {
@@ -150,13 +184,13 @@
}
// 查询
function handleSearch () {
function handleSearch() {
    searchParams.value.current = 1
    getList()
}
// 重置查询
function resetForm () {
function resetForm() {
    dateRange.value = []
    queryParamsRef.value?.resetFields()
    searchParams.value.current = 1
@@ -164,25 +198,25 @@
}
// 查看
function handleView (row) {
function handleView(row) {
    dialogRef.value?.open({ mode: 'view', row: { ...row } })
}
// 编辑
function handleEdit (row) {
function handleEdit(row) {
    dialogRef.value?.open({ mode: 'edit', row: { ...row } })
}
function outbound (row) {
function outbound(row) {
    outboundDialogRef.value?.open({ mode: 'edit', row: { ...row } })
}
function scrap (row) {
function scrap(row) {
    scrapDialogRef.value?.open({ mode: 'edit', row: { ...row } })
}
// 删除
async function handleDelete (row) {
async function handleDelete(row) {
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { type: 'warning' })
    const ids = row ? row.id : selectedIds.value.join(',')
@@ -193,11 +227,11 @@
}
// 勾选值设置
function handleSelectionChange (rows) {
function handleSelectionChange(rows) {
    selectedIds.value = rows.map(item => item.id)
}
function exportFile () {
function exportFile() {
    exportLoading.value = true
    exportFwDeviceApi()
        .then(res => {
@@ -209,21 +243,21 @@
}
// 获取字典
function getDictList () {
    getDictionaryByCode('deviceType,deviceAtt').then(res => {
function getDictList() {
    getDictionaryByCode('deviceType,deviceAtt,deviceStatus').then(res => {
        dictObj.value = res.data.data
    })
}
// 获取部门树
function getDeptTreeFun () {
function getDeptTreeFun() {
    getDeptTree().then(res => {
        deptTree.value = res.data.data
    })
}
// 新增
function handleAdd () {
function handleAdd() {
    dialogRef.value?.open({ mode: 'add' })
}
applications/drone-command/src/views/basicManage/maintainRecord/FormDiaLog.vue
@@ -1,104 +1,109 @@
<template>
    <el-dialog v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed" destroy-on-close>
        <div v-if="dialogReadonly">
            <h3>设备详情</h3>
    <el-dialog class="ztzf-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed"
        destroy-on-close>
        <div class="detail-container" v-if="dialogReadonly">
            <div class="detail-title">设备详情</div>
            <el-row>
                <el-col :span="12">
                    <div>设备名称: {{ formData.deviceName }}</div>
                    <div class="label">设备名称</div>
                    <div class="val">{{ formData.deviceName }}</div>
                </el-col>
                <el-col :span="12">
                    <div>设备类型: {{ getDictLabel(formData.deviceType, dictObj.deviceType) }}</div>
                    <div class="label">设备类型</div>
                    <div class="val">{{ getDictLabel(formData.deviceType, dictObj.deviceType) }}</div>
                </el-col>
                <el-col :span="12">
                    <div>型号 : {{ formData.deviceModel }}</div>
                    <div class="label">型号</div>
                    <div class="val">{{ formData.deviceModel }}</div>
                </el-col>
                <el-col :span="12">
                    <div>生产厂商 : {{ formData.manufacturer }}</div>
                    <div class="label">生产厂商</div>
                    <div class="val">{{ formData.manufacturer }}</div>
                </el-col>
                <el-col :span="12">
                    <div>所属部门 : {{ formData.belongDeptName }}</div>
                    <div class="label">所属部门</div>
                    <div class="val">{{ formData.belongDeptName }}</div>
                </el-col>
                <el-col :span="12">
                    <div>负责人 : {{ formData.charger }}</div>
                    <div class="label">负责人</div>
                    <div class="val">{{ formData.charger }}</div>
                </el-col>
                <el-col :span="12">
                    <div>维护计划 : {{ getPlanCycleLabel(formData) }}</div>
                    <div class="label">维护计划</div>
                    <div class="val">{{ getPlanCycleLabel(formData) }}</div>
                </el-col>
                <el-col :span="12">
                    <div>维护时间 : {{ list?.[0]?.maintainTime }}</div>
                    <div class="label">维护时间</div>
                    <div class="val">{{ list?.[0]?.maintainTime }}</div>
                </el-col>
                <el-col :span="12">
                    <div>维护内容 : {{ formData.manufacturer }}</div>
                    <div class="label">维护内容</div>
                    <div class="val">{{ formData.manufacturer }}</div>
                </el-col>
                <el-col :span="12">
                    <div>跟换部件 : {{ formData.manufacturer }}</div>
                    <div class="label">跟换部件</div>
                    <div class="val">{{ formData.manufacturer }}</div>
                </el-col>
            </el-row>
            <h3>维护详情</h3>
            <el-table v-loading="loading" :data="list">
                <el-table-column type="index" width="60" label="序号" />
                <el-table-column prop="maintainTime" label="维护时间" />
                <el-table-column prop="maintainContent" label="维护内容" />
                <el-table-column prop="replacePart" label="跟换部件" />
            </el-table>
            <el-pagination
                v-model:current-page="searchParams.current"
                v-model:page-size="searchParams.size"
                :total="total"
                @change="getList"
            />
            <div class="detail-title">维护详情</div>
            <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
                <div class="ztzf-table-content">
                    <el-table class="ztzf-data-cockpit-table" :data="list">
                        <el-table-column type="index" width="60" label="序号" />
                        <el-table-column prop="maintainTime" label="维护时间" />
                        <el-table-column prop="maintainContent" label="维护内容" />
                        <el-table-column prop="replacePart" label="跟换部件" />
                    </el-table>
                </div>
                <div class="ztzf-table-pagination">
                    <el-pagination popper-class="ztzf-data-cockpit-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>
        <el-form v-else ref="formRef" :model="formData" :rules="rules" :disabled="dialogReadonly" label-width="100px">
        <el-form class="dialog-form" v-else ref="formRef" :model="formData" :rules="rules" :disabled="dialogReadonly"
            label-width="120px">
            <el-row>
                <el-col :span="12">
                    <el-form-item label="选择设备" prop="deviceId">
                        <el-select v-model="formData.deviceId" placeholder="请选择" filterable clearable>
                            <el-option v-for="item in deviceList" :key="item.id" :label="item.deviceName" :value="item.id" />
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                            v-model="formData.deviceId" placeholder="请选择" filterable clearable>
                            <el-option v-for="item in deviceList" :key="item.id" :label="item.deviceName"
                                :value="item.id" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="维护计划周期" prop="planCycleType">
                        <el-select
                            v-model="formData.planCycleType"
                            placeholder="请选择"
                            clearable
                            @change="formData.planCycleValue = []"
                        >
                            <el-option v-for="item in planCycleOptions" :key="item.value" :label="item.label" :value="item.value" />
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                            v-model="formData.planCycleType" placeholder="请选择" clearable
                            @change="formData.planCycleValue = []">
                            <el-option v-for="item in planCycleOptions" :key="item.value" :label="item.label"
                                :value="item.value" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="维护计划时间" prop="planCycleValue">
                        <el-select
                            v-model="formData.planCycleValue"
                            placeholder="请选择"
                            clearable
                            multiple
                            :disabled="!formData.planCycleType"
                        >
                            <el-option
                                v-for="item in planCycleValueOptions"
                                :key="item.value"
                                :label="item.value"
                                :value="item.value"
                            />
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                            v-model="formData.planCycleValue" placeholder="请选择" clearable multiple
                            :disabled="!formData.planCycleType">
                            <el-option v-for="item in planCycleValueOptions" :key="item.value" :label="item.value"
                                :value="item.value" />
                        </el-select>
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <template #footer>
            <el-button @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
            <el-button
                v-if="!dialogReadonly"
                type="primary"
                :loading="submitting"
                :disabled="submitting"
                @click="handleSubmit"
            >
                确定
            <el-button color="#2B2B4C" @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
            <el-button color="#284FE3" v-if="!dialogReadonly" type="primary" :loading="submitting"
                :disabled="submitting" @click="handleSubmit">
                保存
            </el-button>
        </template>
    </el-dialog>
applications/drone-command/src/views/basicManage/maintainRecord/MaintenanceDiaLog.vue
@@ -1,20 +1,26 @@
<template>
    <el-dialog v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed" destroy-on-close>
        <div v-if="dialogReadonly">
    <el-dialog class="ztzf-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed"
        destroy-on-close>
        <div class="detail-container" v-if="dialogReadonly">
            <div class="detail-title">设备详情</div>
            <el-row>
                <el-col :span="12">
                    <div>设备名称: {{ deviceName }}</div>
                    <div class="label">设备名称</div>
                    <div class="val">{{ deviceName }}</div>
                </el-col>
                <el-col :span="12">
                    <div>维护计划: {{ planCycleLabel }}</div>
                    <div class="label">维护计划</div>
                    <div class="val">{{ planCycleLabel }}</div>
                </el-col>
            </el-row>
        </div>
        <el-form v-else ref="formRef" :model="formData" :rules="rules" :disabled="dialogReadonly" label-width="100px">
        <el-form class="dialog-form" v-else ref="formRef" :model="formData" :rules="rules"
            :disabled="dialogReadonly" label-width="110px">
            <el-row>
                <el-col :span="24">
                    <el-form-item label="维护内容" prop="maintainContent">
                        <el-input
                            class="ztzf-data-cockpit-search-input"
                            v-model="formData.maintainContent"
                            maxlength="200"
                            type="textarea"
@@ -25,27 +31,37 @@
                </el-col>
                <el-col :span="24">
                    <el-form-item label="更换部件" prop="replacePart">
                        <el-input v-model="formData.replacePart" maxlength="200" type="textarea" placeholder="请输入" clearable />
                        <el-input
                            class="ztzf-data-cockpit-search-input"
                            v-model="formData.replacePart"
                            maxlength="200"
                            type="textarea"
                            placeholder="请输入"
                            clearable
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="实际维护时间" prop="maintainTime">
                        <el-date-picker
                            class="ztzf-data-cockpit-date-picker"
                            popper-class="ztzf-data-cockpit-date-picker-popper"
                            v-model="formData.maintainTime"
                            type="date"
                            placeholder="选择日期"
                            value-format="YYYY-MM-DD HH:mm:ss"
                            clearable
                        />
                    </el-form-item>
                </el-col>
            </el-row>
            <el-col :span="12">
                <el-form-item label="实际维护时间" prop="maintainTime">
                    <el-date-picker
                        v-model="formData.maintainTime"
                        type="date"
                        placeholder="选择日期"
                        value-format="YYYY-MM-DD HH:mm:ss"
                        clearable
                    />
                </el-form-item>
            </el-col>
        </el-form>
        <template #footer>
            <el-button @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
            <el-button color="#2B2B4C" @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
            <el-button
                v-if="!dialogReadonly"
                type="primary"
                color="#284FE3"
                :loading="submitting"
                :disabled="submitting"
                @click="handleSubmit"
applications/drone-command/src/views/basicManage/maintainRecord/index.vue
@@ -156,7 +156,12 @@
// 删除
async function handleDelete (row) {
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { type: 'warning' })
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', {
        type: 'warning',
        customClass: 'ztzf-page-view-message-box',
        confirmButtonClass: 'ztzf-message-box-confirm',
        cancelButtonClass: 'ztzf-message-box-cancel',
    })
    const ids = row ? row.id : selectedIds.value.join(',')
    await fwDeviceMaintainPlanRemoveApi({ ids })
    ElMessage.success('删除成功')
applications/drone-command/src/views/dataCockpit/components/DeviceHistoryDialog.vue
@@ -2,43 +2,36 @@
    <el-dialog class="ztzf-data-cockpit-dialog" append-to-body v-model="visibleModel" :title="dialogTitle"
        :close-on-click-modal="false" :destroy-on-close="true">
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-dialog-history-search">
            <el-row :gutter="12">
                <el-col :span="4">
                    <el-form-item label="关键字" prop="keyword">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="searchParams.keyword"
                            placeholder="名称/序列号" clearable @clear="handleSearch" />
                    </el-form-item>
                </el-col>
                <el-col :span="5">
                    <el-form-item prop="dateRange">
                        <el-date-picker class="ztzf-data-cockpit-date-picker"
                            popper-class="ztzf-data-cockpit-date-picker-popper" v-model="searchParams.dateRange"
                            type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"
                            value-format="YYYY/MM/DD" @change="handleSearch" />
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item label="区域" prop="area">
                        <el-tree-select class="ztzf-data-cockpit-select" popper-class="ztzf-tree-cockpit-select-popper"
                            v-model="searchParams.area" :data="areaTree" :props="areaTreeProps" node-key="value"
                            check-strictly clearable placeholder="请选择" @change="handleSearch" />
                    </el-form-item>
                </el-col>
                <el-col :span="5">
                    <el-form-item label="无人机类型" prop="droneType">
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-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-col>
            <el-form-item label="关键字" prop="keyword">
                <el-input class="ztzf-data-cockpit-search-input" v-model="searchParams.keyword" placeholder="名称/序列号"
                    clearable @clear="handleSearch" />
            </el-form-item>
                <el-form-item class="history-search-actions">
                    <el-button :icon="RefreshRight" @click="resetForm"></el-button>
                    <el-button :icon="Search" @click="handleSearch"></el-button>
                </el-form-item>
            </el-row>
            <el-form-item prop="dateRange">
                <el-date-picker class="ztzf-data-cockpit-date-picker"
                    popper-class="ztzf-data-cockpit-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="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-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="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-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="history-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="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
applications/drone-command/src/views/detectionCountermeasure/countermeasureEvaluation.vue
File was deleted
applications/drone-command/src/views/detectionCountermeasure/countermeasureEvaluation/FormDiaLog.vue
New file
@@ -0,0 +1,137 @@
<template>
    <el-dialog v-model="visible" :title="titleEnum[dialogMode]" :close-on-click-modal="false">
        <div v-if="readonly">
            <el-row>
                <el-col :span="12">
                    <div>无人机名称: {{ formData.droneName }}</div>
                </el-col>
                <el-col :span="12">
                    <div>无人机类型: {{ formData.droneType }}</div>
                </el-col>
                <el-col :span="12">
                    <div>无人机设备编码: {{ formData.droneDeviceCode }}</div>
                </el-col>
                <el-col :span="12">
                    <div>发现时间: {{ formData.findTime }}</div>
                </el-col>
                <el-col :span="12">
                    <div>反制效果: {{ getDictLabel(formData.counterEffect, dictObj.counterEffect) }}</div>
                </el-col>
                <el-col :span="12">
                    <div>反制方式: {{ getCounterWayLabel(formData.counterWay) }}</div>
                </el-col>
                <el-col :span="12">
                    <div>反制设备名称: {{ formData.deviceName }}</div>
                </el-col>
                <el-col :span="12">
                    <div>反制设备型号: {{ formData.deviceModel }}</div>
                </el-col>
                <el-col :span="12">
                    <div>反制设备类型: {{ getDeviceTypeLabel(formData.deviceType) }}</div>
                </el-col>
                <el-col :span="12">
                    <div>反制设备编码: {{ formData.deviceSn }}</div>
                </el-col>
                <el-col :span="12">
                    <div>区域编码: {{ formData.areaCode }}</div>
                </el-col>
                <el-col :span="12">
                    <div>部署位置: {{ getDeployPosition() }}</div>
                </el-col>
                <el-col :span="12">
                    <div>覆盖范围: {{ formData.coverRadiusM ? `${formData.coverRadiusM}米` : '' }}</div>
                </el-col>
                <el-col :span="12">
                    <div>工作模式: {{ getWorkModeLabel(formData.workMode) }}</div>
                </el-col>
            </el-row>
        </div>
        <template #footer>
            <el-button @click="handleCancel">关闭</el-button>
        </template>
    </el-dialog>
</template>
<script setup>
import { computed, ref, inject } from 'vue'
import { fwEffectEvalDetailApi } from './countermeasureEvaluationApi'
import { getDictLabel } from '../../../../../../packages/utils'
// 初始化表单数据
const initForm = () => ({
    droneName: '', // 无人机名称
    droneType: '', // 无人机类型
    droneDeviceCode: '', // 无人机设备编码
    findTime: '', // 发现时间
    counterEffect: '', // 反制效果
    counterWay: '', // 反制方式
    deviceName: '', // 反制设备名称
    deviceModel: '', // 反制设备型号
    deviceType: '', // 反制设备类型
    deviceSn: '', // 反制设备编码
    areaCode: '', // 区域编码
    deployLongitude: '', // 部署经度
    deployLatitude: '', // 部署纬度
    coverRadiusM: '', // 覆盖范围
    workMode: '', // 工作模式
})
// 注入字典数据
const dictObj = inject('dictObj')
// 注入反制效果选项
const formData = ref(initForm()) // 表单数据
const visible = defineModel() // 弹框显隐
const dialogMode = ref('view') // 弹框模式
const readonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ view: '查看' })
// 关闭弹框
function handleCancel() {
    visible.value = false
}
// 获取反制方式标签
function getCounterWayLabel(value) {
    const map = { '1': '信号干扰', '2': '诱导驱离' }
    return map[value] || value || '无'
}
// 获取设备类型标签
function getDeviceTypeLabel(value) {
    const item = dictObj.value?.deviceType?.find(item => item.dictKey === value)
    return item ? item.dictValue : value
}
// 获取工作模式标签
function getWorkModeLabel(value) {
    const map = { '1': '机动', '2': '固定' }
    return map[value] || value
}
// 获取部署位置
function getDeployPosition() {
    if (formData.value.deployLongitude && formData.value.deployLatitude) {
        return `${formData.value.deployLongitude}, ${formData.value.deployLatitude}`
    }
    return ''
}
// 加载详情
async function loadDetail() {
    if (!formData.value.id) return
    const res = await fwEffectEvalDetailApi({ id: formData.value.id })
    formData.value = res?.data?.data ?? {}
}
// 打开弹框
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'view'
    formData.value = row ? { ...row } : initForm()
    dialogMode.value === 'view' && (await loadDetail())
}
defineExpose({ open })
</script>
<style scoped lang="scss"></style>
applications/drone-command/src/views/detectionCountermeasure/countermeasureEvaluation/countermeasureEvaluationApi.js
New file
@@ -0,0 +1,19 @@
import request from '@/axios'
// 分页查询反制效果评估列表
export const fwEffectEvalPageApi = params => {
    return request({
        url: `/drone-fw/detection/fwEffectEval/page`,
        method: 'get',
        params,
    })
}
// 获取反制效果评估详情
export const fwEffectEvalDetailApi = params => {
    return request({
        url: `/drone-fw/detection/fwEffectEval/detail`,
        method: 'get',
        params,
    })
}
applications/drone-command/src/views/detectionCountermeasure/countermeasureEvaluation/index.vue
New file
@@ -0,0 +1,180 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams">
            <el-row :gutter="16">
                <el-col :span="4">
                    <el-form-item label="设备名称" prop="deviceName">
                        <el-input v-model="searchParams.deviceName" placeholder="请输入" clearable @clear="handleSearch" />
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item label="设备类型" prop="deviceType">
                        <el-select v-model="searchParams.deviceType" placeholder="请选择" clearable @change="handleSearch">
                            <el-option
                                v-for="item in dictObj.deviceType"
                                :key="item.dictKey"
                                :label="item.dictValue"
                                :value="item.dictKey"
                            />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item label="反制效果" prop="counterEffect">
                        <el-select v-model="searchParams.counterEffect" placeholder="请选择" clearable @change="handleSearch">
                            <el-option
                                v-for="item in dictObj.counterEffect"
                                :key="item.dictKey"
                                :label="item.dictValue"
                                :value="item.dictKey"
                            />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item>
                        <el-button @click="resetForm">重置</el-button>
                        <el-button type="primary" @click="handleSearch">查询</el-button>
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <el-table v-loading="loading" :data="list">
            <el-table-column type="index" width="60" label="序号" />
            <el-table-column prop="droneName" label="无人机名称" />
            <el-table-column prop="droneType" label="无人机类型" />
            <el-table-column prop="droneDeviceCode" label="无人机设备编码" />
            <el-table-column prop="findTime" label="发现时间" />
            <el-table-column prop="counterEffect" label="反制效果">
                <template v-slot="{ row }">
                    {{ getDictLabel(row.counterEffect, dictObj.counterEffect) }}
                </template>
            </el-table-column>
            <el-table-column prop="deviceName" label="反制设备名称" />
            <el-table-column prop="deviceModel" label="反制设备型号" />
            <el-table-column prop="deviceType" label="反制设备类型">
                <template v-slot="{ row }">
                    {{ getDictLabel(row.deviceType, dictObj.deviceType) }}
                </template>
            </el-table-column>
            <el-table-column prop="deviceSn" label="反制设备编码" />
            <el-table-column prop="areaCode" label="场景" />
            <el-table-column prop="areaCode" label="区域" />
            <el-table-column label="部署位置">
                <template v-slot="{ row }">
                    {{ row.deployLongitude && row.deployLatitude ? `${row.deployLongitude}, ${row.deployLatitude}` : '' }}
                </template>
            </el-table-column>
            <el-table-column prop="coverRadiusM" label="覆盖范围">
                <template v-slot="{ row }">
                    {{ row.coverRadiusM ? `${row.coverRadiusM}米` : '' }}
                </template>
            </el-table-column>
            <el-table-column prop="workMode" label="工作模式">
                <template v-slot="{ row }">
                    {{ getWorkModeLabel(row.workMode) }}
                </template>
            </el-table-column>
            <el-table-column label="操作" width="100">
                <template v-slot="{ row }">
                    <el-link @click="openForm('view', row)" type="primary">查看</el-link>
                </template>
            </el-table-column>
        </el-table>
        <div>
            <el-pagination
                v-model:current-page="searchParams.current"
                v-model:page-size="searchParams.size"
                :total="total"
                @change="getList"
            />
        </div>
        <FormDiaLog v-if="dialogVisible" v-model="dialogVisible" ref="dialogRef" />
    </basic-container>
</template>
<script setup>
import { onMounted, ref, nextTick, provide } from 'vue'
import { fwEffectEvalPageApi } from './countermeasureEvaluationApi'
import FormDiaLog from './FormDiaLog.vue'
import { getDictionaryByCode } from '@/api/system/dictbiz'
import { getDictLabel } from '@ztzf/utils'
// 初始化查询参数
const initSearchParams = () => ({
    deviceName: '', // 设备名称
    deviceType: '', // 设备类型
    counterEffect: '', // 反制效果
    current: 1, // 当前页
    size: 10, // 每页大小
})
const searchParams = ref(initSearchParams()) // 查询参数
const total = ref(0) // 总条数
const loading = ref(false) // 列表加载中
const list = ref([]) // 列表数据
const queryParamsRef = ref(null) // 查询表单实例
const dialogRef = ref(null) // 弹框实例
const dialogVisible = ref(null) // 弹框显隐
const dictObj = ref({
    deviceType: [], // 设备类型
    counterEffect: [], // 反制效果
})
// 注入字典和反制效果选项供FormDiaLog使用
provide('dictObj', dictObj)
// 获取列表
async function getList() {
    loading.value = true
    try {
        const params = { ...searchParams.value }
        const res = await fwEffectEvalPageApi(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() {
    queryParamsRef.value?.resetFields()
    searchParams.value = initSearchParams()
    getList()
}
// 获取工作模式标签
function getWorkModeLabel(value) {
    const map = { '1': '机动', '2': '固定' }
    return map[value] || value
}
// 获取字典
function getDictList() {
    getDictionaryByCode('deviceType,counterEffect').then(res => {
        dictObj.value = res.data.data
    })
}
// 查看弹框
function openForm(mode, row) {
    dialogVisible.value = true
    nextTick(() => {
        dialogRef.value?.open({ mode, row })
    })
}
onMounted(() => {
    getList()
    getDictList()
})
</script>
<style scoped lang="scss"></style>
applications/drone-command/src/views/detectionCountermeasure/detectionRange.vue
File was deleted
applications/drone-command/src/views/detectionCountermeasure/detectionRange/DetectionRangeDialog.vue
File was renamed from applications/drone-command/src/views/detectionCountermeasure/components/DetectionRangeDialog.vue
@@ -24,21 +24,10 @@
            </div>
            <div class="dialog-form">
                <div class="form-title">{{ formTitle }}</div>
                <el-form
                    ref="formRef"
                    :model="formData"
                    :rules="rules"
                    label-width="84px"
                    :disabled="dialogReadonly"
                >
                <el-form ref="formRef" :model="formData" :rules="rules" label-width="84px" :disabled="dialogReadonly">
                    <el-form-item label="侦测设备" prop="deviceId">
                        <el-select v-model="formData.deviceId" placeholder="请选择" clearable @change="handleDeviceChange">
                            <el-option
                                v-for="item in deviceOptions"
                                :key="item.value"
                                :label="item.label"
                                :value="item.value"
                            />
                            <el-option v-for="item in deviceOptions" :key="item.value" :label="item.label" :value="item.value" />
                        </el-select>
                    </el-form-item>
                    <el-form-item label="设备类型" prop="deviceType">
@@ -58,8 +47,8 @@
                            </template>
                        </el-input>
                    </el-form-item>
                    <el-form-item label="设备编号" prop="deviceCode">
                        <el-input v-model="formData.deviceCode" placeholder="请输入" clearable />
                    <el-form-item label="设备编号" prop="deviceSn">
                        <el-input v-model="formData.deviceSn" placeholder="请输入" clearable />
                    </el-form-item>
                    <el-form-item label="设备型号" prop="deviceModel">
                        <el-input v-model="formData.deviceModel" placeholder="请输入" clearable />
@@ -78,14 +67,7 @@
        </div>
        <template v-if="!dialogReadonly" #footer>
            <el-button @click="handleCancel">取消</el-button>
            <el-button
                type="primary"
                :loading="submitting"
                :disabled="submitting"
                @click="handleSubmit"
            >
                保存
            </el-button>
            <el-button type="primary" :loading="submitting" :disabled="submitting" @click="handleSubmit">保存</el-button>
        </template>
    </el-dialog>
</template>
@@ -105,7 +87,7 @@
    deviceName: '', // 侦测设备
    deviceType: '', // 设备类型
    deployLocation: '', // 设备位置
    deviceCode: '', // 设备编号
    deviceSn: '', // 设备编号
    deviceModel: '', // 设备型号
    effectiveRangeKm: null, // 覆盖范围
})
@@ -152,14 +134,9 @@
    formData.value.deviceName = raw.deviceName ?? formData.value.deviceName
    formData.value.deviceType = raw.deviceType ?? formData.value.deviceType
    formData.value.deviceModel = raw.deviceModel ?? formData.value.deviceModel
    formData.value.deviceCode =
        raw.deviceCode ?? raw.deviceSn ?? raw.deviceNo ?? formData.value.deviceCode
    formData.value.deviceSn = raw.deviceSn ?? raw.deviceSn ?? raw.deviceNo ?? formData.value.deviceCode
    formData.value.deployLocation =
        raw.deployLocation ??
        raw.address ??
        raw.location ??
        raw.deployAddress ??
        formData.value.deployLocation
        raw.deployLocation ?? raw.address ?? raw.location ?? raw.deployAddress ?? formData.value.deployLocation
}
// 关闭弹框
applications/drone-command/src/views/detectionCountermeasure/detectionRange/index.vue
New file
@@ -0,0 +1,209 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
            <el-form-item label="名称" prop="deviceName">
                <el-input
                    class="ztzf-data-cockpit-search-input"
                    v-model="searchParams.deviceName"
                    placeholder="请输入"
                    clearable
                    @clear="handleSearch"
                />
            </el-form-item>
            <el-form-item label="类型" prop="deviceType">
                <el-select
                    class="ztzf-data-cockpit-select"
                    popper-class="ztzf-data-cockpit-select-popper"
                    v-model="searchParams.deviceType"
                    placeholder="请选择"
                    clearable
                    @change="handleSearch"
                >
                    <el-option
                        v-for="item in dictObj.deviceType"
                        :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>
            </el-form-item>
        </el-form>
        <div class="ztzf-table-toolbar">
            <el-button :icon="Plus" color="#284FE3" type="primary" @click="handleAdd">新增</el-button>
        </div>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list">
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="deviceName" show-overflow-tooltip width="140" label="设备名称" />
                    <el-table-column prop="deviceType" show-overflow-tooltip width="120" label="设备类型">
                        <template v-slot="{ row }">
                            {{ getDictLabel(row.deviceType, dictObj.deviceType) }}
                        </template>
                    </el-table-column>
                    <el-table-column show-overflow-tooltip width="160" label="部署位置">
                        <template v-slot="{ row }">
                            {{ formatDeployLocation(row) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="deviceModel" show-overflow-tooltip width="110" label="型号" />
                    <el-table-column show-overflow-tooltip width="120" label="覆盖范围">
                        <template v-slot="{ row }">
                            {{ formatRange(row) }}
                        </template>
                    </el-table-column>
                    <el-table-column show-overflow-tooltip width="150" label="设备编码">
                        <template v-slot="{ row }">
                            {{ formatDeviceCode(row) }}
                        </template>
                    </el-table-column>
                    <el-table-column label="操作" class-name="operation-btns">
                        <template v-slot="{ row }">
                            <el-link @click="handleView(row)">查看</el-link>
                            <el-link @click="handleEdit(row)">编辑</el-link>
                            <el-link @click="handleDelete(row)">删除</el-link>
                        </template>
                    </el-table-column>
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
                <el-pagination
                    popper-class="ztzf-data-cockpit-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>
        <DetectionRangeDialog ref="dialogRef" @success="getList" />
    </basic-container>
</template>
<script setup>
import { Search, RefreshRight, Plus } from '@element-plus/icons-vue'
import { onMounted, ref, provide } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getDictionaryByCode } from '@/api/system/dictbiz'
import { getDictLabel } from '@ztzf/utils'
import { detectionRangePageApi, detectionRangeSubmitApi } from '@/api/detectionCountermeasure/detectionRange'
import DetectionRangeDialog from './DetectionRangeDialog.vue'
const initSearchParams = () => ({
    deviceName: '', // 设备名称
    deviceType: '', // 设备类型
    effectiveRangeKmIsNotNull: 1, // 1-不为空
    current: 1, // 当前页
    size: 10, // 每页大小
})
const searchParams = ref(initSearchParams()) // 查询参数
const loading = ref(false) // 列表加载中
const list = ref([]) // 列表数据
const total = ref(0) // 总数
const queryParamsRef = ref(null) // 查询表单实例
const dialogRef = ref(null) // 弹框实例
const dictObj = ref({
    deviceType: [], // 设备类型
})
provide('dictObj', dictObj)
// 获取列表
async function getList() {
    loading.value = true
    try {
        const res = await detectionRangePageApi(searchParams.value)
        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() {
    queryParamsRef.value?.resetFields()
    searchParams.value.current = 1
    getList()
}
// 新增
function handleAdd() {
    dialogRef.value?.open({ mode: 'add' })
}
// 查看
function handleView(row) {
    dialogRef.value?.open({ mode: 'view', row: { ...row } })
}
// 编辑
function handleEdit(row) {
    dialogRef.value?.open({ mode: 'edit', row: { ...row } })
}
// 删除
async function handleDelete(row) {
    await ElMessageBox.confirm('确认删除该条数据吗?', '提示', { type: 'warning' })
    await detectionRangeSubmitApi({ id: row.id, effectiveRangeKm: 0 })
    ElMessage.success('删除成功')
    getList()
}
// 获取字典
function getDictList() {
    getDictionaryByCode('deviceType').then(res => {
        dictObj.value = res.data.data
    })
}
function formatDeployLocation(row) {
    if (!row) return '-'
    const location = row.deployLocation ?? row.address ?? row.location ?? row.deployAddress
    if (Array.isArray(location) && location.length) {
        return location.filter(value => value !== null && value !== undefined).join(',')
    }
    if (typeof location === 'string' && location.trim()) {
        return location
    }
    const longitude = row.deployLongitude ?? row.longitude
    const latitude = row.deployLatitude ?? row.latitude
    if (longitude != null || latitude != null) {
        return `${longitude ?? '-'},${latitude ?? '-'}`
    }
    return '-'
}
function formatRange(row) {
    if (!row) return '-'
    const value = row.effectiveRangeKm ?? row.range
    return value === null || value === undefined || value === '' ? '-' : value
}
function formatDeviceCode(row) {
    if (!row) return ''
    return row.deviceSn
}
onMounted(() => {
    getList()
    getDictList()
})
</script>
<style scoped lang="scss"></style>
applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig.vue
File was deleted
applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/FormDiaLog.vue
New file
@@ -0,0 +1,201 @@
<template>
    <el-dialog v-model="visible" :title="titleEnum[dialogMode]" :close-on-click-modal="false">
        <div v-if="readonly">
            <el-row>
                <el-col :span="12">
                    <div>起始扇区: {{ formData.startSector }}</div>
                </el-col>
                <el-col :span="12">
                    <div>终止扇区: {{ formData.endSector }}</div>
                </el-col>
                <el-col :span="12">
                    <div>搜索模式: {{ formData.searchMode }}</div>
                </el-col>
                <el-col :span="12">
                    <div>雷达频点: {{ formData.radarFreq }}</div>
                </el-col>
                <el-col :span="12">
                    <div>伺服模式: {{ formData.servoMode }}</div>
                </el-col>
                <el-col :span="12">
                    <div>伺服数据模式: {{ formData.servoDataMode }}</div>
                </el-col>
                <el-col :span="12">
                    <div>轴选择: {{ formData.axisSelect }}</div>
                </el-col>
                <el-col :span="12">
                    <div>伺服转速度: {{ formData.servoTurnSpeed }}</div>
                </el-col>
            </el-row>
        </div>
        <el-form v-else ref="formRef" :model="formData" :rules="rules" :disabled="readonly" label-width="120px">
            <el-row>
                <el-col :span="12">
                    <el-form-item label="起始扇区" prop="startSector">
                        <el-input v-model="formData.startSector" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="终止扇区" prop="endSector">
                        <el-input v-model="formData.endSector" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="搜索模式" prop="searchMode">
                        <el-select v-model="formData.searchMode" placeholder="请选择" clearable>
                            <el-option v-for="item in searchModeOptions" :key="item.value" :label="item.label" :value="item.value" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="雷达频点" prop="radarFreq">
                        <el-input v-model="formData.radarFreq" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="伺服模式" prop="servoMode">
                        <el-select v-model="formData.servoMode" placeholder="请选择" clearable>
                            <el-option v-for="item in servoModeOptions" :key="item.value" :label="item.label" :value="item.value" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="伺服数据模式" prop="servoDataMode">
                        <el-select v-model="formData.servoDataMode" placeholder="请选择" clearable>
                            <el-option
                                v-for="item in servoDataModeOptions"
                                :key="item.value"
                                :label="item.label"
                                :value="item.value"
                            />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="轴选择" prop="axisSelect">
                        <el-select v-model="formData.axisSelect" placeholder="请选择" clearable>
                            <el-option v-for="item in axisSelectOptions" :key="item.value" :label="item.label" :value="item.value" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="伺服转速度" prop="servoTurnSpeed">
                        <el-input v-model="formData.servoTurnSpeed" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <template #footer>
            <el-button @click="handleCancel">{{ readonly ? '关闭' : '取消' }}</el-button>
            <el-button v-if="!readonly" type="primary" :loading="submitting" :disabled="submitting" @click="handleSubmit">
                确定
            </el-button>
        </template>
    </el-dialog>
</template>
<script setup>
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { fwDeviceConfigDetailApi, fwDeviceConfigSubmitApi } from './deviceAppConfigApi'
import { fieldRules } from '@ztzf/utils'
const configKeys = [
    'startSector',
    'endSector',
    'searchMode',
    'radarFreq',
    'servoMode',
    'servoDataMode',
    'axisSelect',
    'servoTurnSpeed',
]
// 初始化表单
const initForm = () => ({
    id: '',
    deviceId: '',
    startSector: '',
    endSector: '',
    searchMode: '',
    radarFreq: '',
    servoMode: '',
    servoDataMode: '',
    axisSelect: '',
    servoTurnSpeed: '',
})
const emit = defineEmits(['success'])
const formRef = ref(null) // 表单实例
const formData = ref(initForm()) // 表单数据
const visible = defineModel() // 弹框显隐
const dialogMode = ref('view') // 弹框模式
const submitting = ref(false) // 提交中
const readonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ edit: '编辑', view: '查看' })
const searchModeOptions = [
    { label: '0', value: '0' },
    { label: '90', value: '90' },
    { label: '180', value: '180' },
]
const servoModeOptions = [
    { label: '周扫', value: '周扫' },
    { label: '扇区', value: '扇区' },
    { label: '定位', value: '定位' },
]
const servoDataModeOptions = [{ label: '编码器', value: '编码器' }]
const axisSelectOptions = [{ label: '方位', value: '方位' }]
const rules = {
    startSector: fieldRules(true, 50),
    endSector: fieldRules(true, 50),
    searchMode: fieldRules(true),
    radarFreq: fieldRules(true, 50),
    servoMode: fieldRules(true),
    servoDataMode: fieldRules(true),
    axisSelect: fieldRules(true),
    servoTurnSpeed: fieldRules(true, 50),
}
// 提取配置字段
function pickConfigFields(target) {
    return configKeys.reduce((acc, key) => {
        acc[key] = target?.[key] ?? ''
        return acc
    }, {})
}
// 关闭弹框
function handleCancel() {
    visible.value = false
}
// 提交更新
async function handleSubmit() {
    const isValid = await formRef.value?.validate().catch(() => false)
    if (!isValid) return
    submitting.value = true
    try {
        const configJson = JSON.stringify(pickConfigFields(formData.value))
        const payload = { ...formData.value, configJson }
        configKeys.forEach(key => {
            delete payload[key]
        })
        await fwDeviceConfigSubmitApi(payload)
        ElMessage.success('更新成功')
        visible.value = false
        emit('success')
    } finally {
        submitting.value = false
    }
}
// 打开弹框
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'view'
    formData.value = { ...initForm(), deviceId: row.id }
}
defineExpose({ open })
</script>
applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/deviceAppConfigApi.js
New file
@@ -0,0 +1,19 @@
import request from '@/axios'
// 查询详情
export const fwDeviceConfigDetailApi = params => {
    return request({
        url: `/drone-fw/detection/fwDeviceConfig/detail`,
        method: 'get',
        params,
    })
}
// 提交更新
export const fwDeviceConfigSubmitApi = data => {
    return request({
        url: `/drone-fw/detection/fwDeviceConfig/submit`,
        method: 'post',
        data,
    })
}
applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/index.vue
New file
@@ -0,0 +1,136 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams">
            <el-row :gutter="16">
                <el-col :span="4">
                    <el-form-item label="设备名称" prop="deviceName">
                        <el-input v-model="searchParams.deviceName" placeholder="请输入" clearable @clear="handleSearch" />
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item label="设备类型" prop="deviceType">
                        <el-select v-model="searchParams.deviceType" placeholder="请选择" clearable @change="handleSearch">
                            <el-option
                                v-for="item in dictObj.deviceType"
                                :key="item.dictKey"
                                :label="item.dictValue"
                                :value="item.dictKey"
                            />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item>
                        <el-button @click="resetForm">重置</el-button>
                        <el-button type="primary" @click="handleSearch">查询</el-button>
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <el-table v-loading="loading" :data="list">
            <el-table-column type="index" width="60" label="序号" />
            <el-table-column prop="deviceType" label="设备类型">
                <template v-slot="{ row }">
                    {{ getDictLabel(row.deviceType, dictObj.deviceType) }}
                </template>
            </el-table-column>
            <el-table-column prop="deviceModel" label="设备型号" />
            <el-table-column prop="deviceName" label="设备名称" />
            <el-table-column prop="deviceSn" label="设备编码" />
            <el-table-column prop="deployLocation" label="部署位置" />
            <el-table-column prop="manufacturer" label="厂商" />
            <el-table-column prop="contactPhone" label="联系方式" />
            <el-table-column prop="sceneName" label="所属场景" />
            <el-table-column label="操作">
                <template v-slot="{ row }">
                    <el-link @click="openForm('edit', row)" type="warning">配置接口</el-link>
                </template>
            </el-table-column>
        </el-table>
        <div>
            <el-pagination
                v-model:current-page="searchParams.current"
                v-model:page-size="searchParams.size"
                :total="total"
                @change="getList"
            />
        </div>
        <FormDiaLog v-if="dialogVisible" v-model="dialogVisible" ref="dialogRef" @success="getList" />
    </basic-container>
</template>
<script setup>
import { nextTick, onMounted, provide, ref } from 'vue'
import FormDiaLog from './FormDiaLog.vue'
import { getDictionaryByCode } from '@/api/system/dictbiz'
import { getDictLabel } from '@ztzf/utils'
import { fwDevicePageApi } from '@/views/basicManage/deviceStock/fwDevice'
// 初始化查询参数
const initSearchParams = () => ({
    deviceName: '', // 设备名称
    deviceType: '', // 设备类型
    current: 1, // 当前页
    size: 10, // 每页大小
})
const searchParams = ref(initSearchParams()) // 查询参数
const total = ref(0) // 总条数
const loading = ref(false) // 列表加载中
const list = ref([]) // 列表数据
const queryParamsRef = ref(null) // 查询表单实例
const dialogRef = ref(null) // 弹框实例
const dialogVisible = ref(false)
const dictObj = ref({
    deviceType: [], //设备类型
})
provide('dictObj', dictObj)
// 获取列表
async function getList() {
    loading.value = true
    try {
        const res = await fwDevicePageApi(searchParams.value)
        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() {
    queryParamsRef.value?.resetFields()
    searchParams.value.current = 1
    getList()
}
// 获取字典
function getDictList() {
    getDictionaryByCode('deviceType').then(res => {
        dictObj.value = res.data.data
    })
}
// 编辑/查看 弹框
function openForm(mode, row) {
    dialogVisible.value = true
    nextTick(() => {
        dialogRef.value?.open({ mode, row })
    })
}
onMounted(() => {
    getList()
    getDictList()
})
</script>
<style scoped lang="scss"></style>
applications/drone-command/src/views/detectionCountermeasure/taskSchedule.vue
File was deleted
applications/drone-command/src/views/detectionCountermeasure/taskSchedule/FormDiaLog.vue
New file
@@ -0,0 +1,383 @@
<template>
    <el-dialog v-model="visible" :title="titleEnum[dialogMode]">
        <div v-if="readonly">
            <el-row>
                <el-col :span="12">
                    <div>设备名称: {{ formData.deviceName }}</div>
                </el-col>
                <el-col :span="12">
                    <div>设备编码: {{ formData.deviceSn }}</div>
                </el-col>
                <el-col :span="12">
                    <div>场景: {{ formData.defenseSceneName }}</div>
                </el-col>
                <el-col :span="12">
                    <div>区域: {{ formData.areaDivideName }}</div>
                </el-col>
                <el-col :span="12">
                    <div>调度人员: {{ formData.dispatchUser }}</div>
                </el-col>
                <el-col :span="12">
                    <div>调度位置: {{ formData.longitude }}, {{ formData.latitude }}</div>
                </el-col>
                <el-col :span="12">
                    <div>调度日期: {{ formData.dispatchStartTime }} ~ {{ formData.dispatchEndTime }}</div>
                </el-col>
                <el-col :span="24">
                    <div>调度内容: {{ formData.dispatchContent }}</div>
                </el-col>
                <el-col :span="24">
                    <div>调度结果: {{ formData.dispatchResult }}</div>
                </el-col>
                <el-col :span="24">
                    <div>处理措施: {{ formData.disposeMeasure }}</div>
                </el-col>
            </el-row>
        </div>
        <el-form v-else ref="formRef" :model="formData" :rules="rules" :disabled="readonly" label-width="100px">
            <el-row>
                <el-col :span="12">
                    <el-form-item label="设备" prop="deviceId">
                        <el-select v-model="formData.deviceId" placeholder="请选择" clearable @change="handleDeviceChange">
                            <el-option v-for="item in deviceList" :key="item.id" :label="item.deviceName" :value="item.id" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="设备编码" prop="deviceSn">
                        <el-input v-model="formData.deviceSn" disabled placeholder="选择设备后自动带出" />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="场景" prop="defenseSceneId">
                        <el-select v-model="formData.defenseSceneId" placeholder="请选择" clearable @change="handleSceneChange">
                            <el-option v-for="item in sceneList" :key="item.id" :label="item.sceneName" :value="item.id" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="区域" prop="areaDivideId">
                        <el-select
                            v-model="formData.areaDivideId"
                            placeholder="请先选择场景"
                            clearable
                            :disabled="!formData.defenseSceneId"
                            @change="handleAreaChange"
                        >
                            <el-option v-for="item in areaList" :key="item.id" :label="item.areaName" :value="item.id" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="调度人员" prop="dispatchUser">
                        <el-input v-model="formData.dispatchUser" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="调度日期" prop="dispatchDateRange">
                        <el-date-picker
                            v-model="formData.dispatchDateRange"
                            type="datetimerange"
                            range-separator="至"
                            start-placeholder="开始日期"
                            end-placeholder="结束日期"
                            value-format="YYYY-MM-DD HH:mm:ss"
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="24">
                    <el-form-item label="调度位置" prop="longitude">
<!--                        <el-button @click="selectLocation" style="width: 100%">-->
                        <el-button style="width: 100%">
                            {{
                                formData.longitude && formData.latitude ? `${formData.longitude}, ${formData.latitude}` : '选择区域后自动带出'
                            }}
                        </el-button>
                    </el-form-item>
                </el-col>
                <el-col :span="24">
                    <el-form-item label="调度内容" prop="dispatchContent">
                        <el-input
                            v-model="formData.dispatchContent"
                            type="textarea"
                            :rows="3"
                            maxlength="500"
                            placeholder="请输入"
                            clearable
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="24">
                    <el-form-item label="调度结果" prop="dispatchResult">
                        <el-input
                            v-model="formData.dispatchResult"
                            type="textarea"
                            :rows="3"
                            maxlength="500"
                            placeholder="请输入"
                            clearable
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="24">
                    <el-form-item label="处理措施" prop="disposeMeasure">
                        <el-input
                            v-model="formData.disposeMeasure"
                            type="textarea"
                            :rows="3"
                            maxlength="500"
                            placeholder="请输入"
                            clearable
                        />
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <template #footer>
            <el-button @click="handleCancel">{{ readonly ? '关闭' : '取消' }}</el-button>
            <el-button v-if="!readonly" type="primary" :loading="submitting" :disabled="submitting" @click="handleSubmit">
                确定
            </el-button>
        </template>
        <el-dialog v-model="visible1" title="选择位置" destroy-on-close @closed="positionClosed">
            <div class="ztzf-cesium" id="mapContainer"></div>
            <template #footer>
                <el-button @click="handleMapCancel">取消</el-button>
                <el-button type="primary" @click="handleMapConfirm">确定</el-button>
            </template>
        </el-dialog>
    </el-dialog>
</template>
<script setup>
import { computed, nextTick, ref, inject } from 'vue'
import { ElMessage } from 'element-plus'
import { fwTaskScheduleDetailApi, fwTaskScheduleSubmitApi } from './taskScheduleApi'
import { fwAreaDivideListApi } from '@/views/areaManage/partition/partitionApi'
import { fieldRules } from '@ztzf/utils'
import { PublicCesium } from '@/utils/cesium/publicCesium'
import * as Cesium from 'cesium'
// 初始化表单
const initForm = () => ({
    deviceId: '', // 设备ID
    deviceSn: '', // 设备编码
    deviceName: '', // 设备名称
    deviceStatus: '', // 设备状态
    deviceStatusLabel: '', // 设备状态标签
    defenseSceneId: '', // 场景ID
    defenseSceneName: '', // 场景名称
    areaDivideId: '', // 区域ID
    areaDivideName: '', // 区域名称
    areaCode: '', // 区域编码
    dispatchUser: '', // 调度人员
    dispatchStartTime: '', // 调度开始时间
    dispatchEndTime: '', // 调度结束时间
    dispatchDateRange: [], // 调度日期范围
    dispatchContent: '', // 调度内容
    dispatchResult: '', // 调度结果
    disposeMeasure: '', // 处理措施
    longitude: null, // 经度
    latitude: null, // 纬度
})
const props = defineProps({
    sceneList: { type: Array, default: () => [] },
    deviceList: { type: Array, default: () => [] },
})
const emit = defineEmits(['success'])
const formRef = ref(null) // 表单实例
const formData = ref(initForm()) // 表单数据
const visible = defineModel() // 弹框显隐
const visible1 = ref(false) // 地图弹框显隐
const dialogMode = ref('add') // 弹框模式
const submitting = ref(false) // 提交中
const readonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' })
const tempLocation = ref({ longitude: null, latitude: null })
const areaList = ref([]) // 区域列表
const rules = {
    deviceId: fieldRules(true),
    areaDivideId: fieldRules(true),
    defenseSceneId: fieldRules(true),
    dispatchUser: fieldRules(true, 50),
    dispatchDateRange: fieldRules(true),
}
// 设备选择变化处理
function handleDeviceChange(deviceId) {
    const device = props.deviceList.find(item => item.id === deviceId)
    if (device) {
        formData.value.deviceName = device.deviceName
        formData.value.deviceSn = device.deviceSn
        formData.value.deviceStatus = device.deviceStatus
    } else {
        formData.value.deviceName = ''
        formData.value.deviceSn = ''
        formData.value.deviceStatus = ''
    }
}
// 场景选择变化处理
async function handleSceneChange(sceneId) {
    formData.value.areaDivideId = ''
    if (sceneId) {
        await loadAreaList(sceneId)
    } else {
        areaList.value = []
    }
}
function handleAreaChange(areaId) {
    const find = areaList.value.find(item => item.id === areaId)
    formData.value.longitude = find?.longitude
    formData.value.latitude = find?.latitude
}
// 加载区域列表
async function loadAreaList(sceneId) {
    const res = await fwAreaDivideListApi({ sceneId })
    areaList.value = res?.data?.data ?? []
}
// 关闭弹框
function handleCancel() {
    visible.value = false
}
// 提交新增/编辑
async function handleSubmit() {
    const isValid = await formRef.value?.validate().catch(() => false)
    if (!isValid) return
    submitting.value = true
    try {
        // 处理调度日期
        if (formData.value.dispatchDateRange?.length === 2) {
            formData.value.dispatchStartTime = formData.value.dispatchDateRange[0]
            formData.value.dispatchEndTime = formData.value.dispatchDateRange[1]
        }
        await fwTaskScheduleSubmitApi(formData.value)
        ElMessage.success(dialogMode.value === 'add' ? '新增成功' : '更新成功')
        visible.value = false
        emit('success')
    } finally {
        submitting.value = false
    }
}
// 加载详情
async function loadDetail() {
    if (!formData.value.id) return
    const res = await fwTaskScheduleDetailApi({ id: formData.value.id })
    const data = res?.data?.data ?? {}
    formData.value = {
        ...data,
        dispatchDateRange:
            data.dispatchStartTime && data.dispatchEndTime ? [data.dispatchStartTime, data.dispatchEndTime] : [],
    }
    // 如果有场景ID,加载区域列表
    if (data.defenseSceneId) {
        await loadAreaList(data.defenseSceneId)
    }
}
// 左键点击事件处理
function LeftClickEvent(click) {
    const pos = click.position
    const cartesian3 = viewer.camera.pickEllipsoid(pos, viewer.scene.globe.ellipsoid)
    if (!cartesian3) {
        return { longitude: 112, latitude: 23 }
    }
    const carto = Cesium.Cartographic.fromCartesian(cartesian3)
    const longitude = Cesium.Math.toDegrees(carto.longitude)
    const latitude = Cesium.Math.toDegrees(carto.latitude)
    tempLocation.value = { longitude, latitude }
    // 添加红点并渲染经纬度标签
    if (!redPointEntity) {
        redPointEntity = viewer.entities.add({
            position: Cesium.Cartesian3.fromDegrees(longitude, latitude),
            point: {
                color: Cesium.Color.RED,
                pixelSize: 10,
            },
            label: {
                text: `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`,
                font: '14px',
                fillColor: Cesium.Color.WHITE,
                backgroundColor: Cesium.Color.BLACK.withAlpha(0.45),
                backgroundPadding: new Cesium.Cartesian2(5, 5),
                pixelOffset: new Cesium.Cartesian2(0, -20),
                showBackground: true,
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
                verticalOrigin: Cesium.VerticalOrigin.CENTER,
                outlineWidth: 1,
                outlineColor: Cesium.Color.WHITE,
                eyeOffset: new Cesium.Cartesian3(0, 0, -20),
            },
        })
    } else {
        redPointEntity.position = Cesium.Cartesian3.fromDegrees(longitude, latitude)
        redPointEntity.label.text = `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`
    }
}
// 地图弹框关闭处理
function positionClosed() {
    redPointEntity = null
}
let viewer
let redPointEntity
// 选择位置
async function selectLocation() {
    visible1.value = true
    await nextTick()
    const publicCesiumInstance = new PublicCesium({
        dom: 'mapContainer',
        flatMode: false,
        terrain: true,
        layerMode: 4,
        boundary: false,
    })
    publicCesiumInstance.addLeftClickEvent(null, LeftClickEvent)
    viewer = publicCesiumInstance.getViewer()
}
// 地图取消
function handleMapCancel() {
    tempLocation.value = { longitude: null, latitude: null }
    visible1.value = false
}
// 地图确认
function handleMapConfirm() {
    if (tempLocation.value.longitude != null && tempLocation.value.latitude != null) {
        formData.value.longitude = _.round(tempLocation.value.longitude, 6)
        formData.value.latitude = _.round(tempLocation.value.latitude, 6)
    }
    visible1.value = false
}
// 打开弹框
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'add'
    formData.value = dialogMode.value === 'add' ? initForm() : { ...row }
    if (dialogMode.value !== 'add') {
        await loadDetail()
    }
}
defineExpose({ open })
</script>
<style scoped lang="scss">
#mapContainer {
    width: 100%;
    height: 500px;
}
</style>
applications/drone-command/src/views/detectionCountermeasure/taskSchedule/index.vue
New file
@@ -0,0 +1,232 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
            <el-form-item label="设备名称" prop="deviceId">
                <el-select
                    class="ztzf-data-cockpit-search-input"
                    v-model="searchParams.deviceId"
                    placeholder="请选择"
                    clearable
                    @clear="handleSearch"
                    @change="handleSearch"
                >
                    <el-option v-for="item in deviceList" :key="item.id" :label="item.deviceName" :value="item.id" />
                </el-select>
            </el-form-item>
            <el-form-item label="设备类型" prop="deviceType">
                <el-select
                    class="ztzf-data-cockpit-search-input"
                    v-model="searchParams.deviceType"
                    placeholder="请选择"
                    clearable
                    @clear="handleSearch"
                    @change="handleSearch"
                >
                    <el-option
                        v-for="item in dictObj.deviceType"
                        :key="item.dictKey"
                        :label="item.dictValue"
                        :value="item.dictKey"
                    />
                </el-select>
            </el-form-item>
            <el-form-item label="设备编号" prop="deviceSn">
                <el-input
                    class="ztzf-data-cockpit-search-input"
                    v-model="searchParams.deviceSn"
                    placeholder="请输入"
                    clearable
                    @clear="handleSearch"
                />
            </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>
            </el-form-item>
        </el-form>
        <div class="ztzf-table-toolbar">
            <el-button :icon="Plus" color="#284FE3" type="primary" @click="openForm('add')">新增</el-button>
            <el-button type="danger" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
        </div>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
                    <el-table-column type="selection" width="46" />
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="deviceName" show-overflow-tooltip width="140" label="设备名称" />
                    <el-table-column prop="deviceSn" show-overflow-tooltip width="140" label="设备编码" />
                    <!--                    <el-table-column prop="deviceStatus" show-overflow-tooltip width="100" label="设备状态">
                        <template v-slot="{ row }">
                            {{ getDictLabel(row.deviceStatus, dictObj.deviceStatus) }}
                        </template>
                    </el-table-column>-->
                    <el-table-column prop="areaName" show-overflow-tooltip width="120" label="区域" />
                    <el-table-column prop="defenseZoneName" show-overflow-tooltip width="120" label="场景" />
                    <el-table-column show-overflow-tooltip width="200" label="调度位置">
                        <template v-slot="{ row }">{{ row.longitude }}, {{ row.latitude }}</template>
                    </el-table-column>
                    <el-table-column prop="dispatchUser" show-overflow-tooltip width="100" label="调度人员" />
                    <el-table-column show-overflow-tooltip width="180" label="调度日期">
                        <template v-slot="{ row }">
                            {{ formatDispatchDate(row) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="dispatchContent" show-overflow-tooltip width="160" label="调度内容" />
                    <el-table-column prop="dispatchResult" show-overflow-tooltip width="160" label="调度结果" />
                    <el-table-column prop="disposeMeasure" show-overflow-tooltip width="160" label="处理措施" />
                    <!--                    <el-table-column label="操作" class-name="operation-btns" fixed="right" width="120">
                        <template v-slot="{ row }">
                            <el-link @click="openForm('edit', row)">编辑</el-link>
                            <el-link @click="handleDelete(row)">删除</el-link>
                        </template>
                    </el-table-column>-->
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
                <el-pagination
                    popper-class="ztzf-data-cockpit-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>
        <FormDiaLog
            ref="dialogRef"
            @success="getList"
            v-if="dialogVisible"
            v-model="dialogVisible"
            :sceneList="sceneList"
            :deviceList="deviceList"
        />
    </basic-container>
</template>
<script setup>
import { Search, RefreshRight, Plus } from '@element-plus/icons-vue'
import { nextTick, onMounted, ref, provide } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { fwTaskSchedulePageApi, fwTaskScheduleRemoveApi } from './taskScheduleApi'
import { fwDefenseSceneListApi } from '@/views/areaManage/sceneConfig/sceneConfigApi'
import { fwDeviceListApi } from '@/views/basicManage/deviceStock/fwDevice'
import { getDictionaryByCode } from '@/api/system/dictbiz'
import FormDiaLog from './FormDiaLog.vue'
import { getDictLabel } from '@ztzf/utils'
// 初始化查询参数
const initSearchParams = () => ({
    deviceId: '', // 设备名称
    deviceType: '', // 设备类型
    deviceSn: '', // 设备编号
    current: 1, // 当前页
    size: 10, // 每页大小
})
const searchParams = ref(initSearchParams()) // 查询参数
const total = ref(0) // 总条数
const loading = ref(false) // 列表加载中
const list = ref([]) // 列表数据
const selectedIds = ref([]) // 勾选的ID列表
const queryParamsRef = ref(null) // 查询表单实例
const dialogRef = ref(null) // 弹框实例
const dialogVisible = ref(false)
const dictObj = ref({
    deviceType: [], // 设备类型
    deviceStatus: [], // 设备状态
})
const sceneList = ref([]) // 场景列表
const deviceList = ref([]) // 设备列表
// 注入字典到子组件
provide('dictObj', dictObj)
// 格式化调度日期
function formatDispatchDate(row) {
    if (row.dispatchStartTime && row.dispatchEndTime) {
        return `${row.dispatchStartTime} ~ ${row.dispatchEndTime}`
    }
    return row.dispatchStartTime || row.dispatchEndTime || ''
}
// 获取列表
async function getList() {
    loading.value = true
    try {
        const res = await fwTaskSchedulePageApi(searchParams.value)
        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() {
    queryParamsRef.value?.resetFields()
    searchParams.value = initSearchParams()
    getList()
}
// 删除
async function handleDelete(row) {
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { type: 'warning' })
    const ids = row ? row.id : selectedIds.value.join(',')
    await fwTaskScheduleRemoveApi({ ids })
    ElMessage.success('删除成功')
    selectedIds.value = []
    getList()
}
// 勾选值设置
function handleSelectionChange(rows) {
    selectedIds.value = rows.map(item => item.id)
}
// 新增/编辑
function openForm(mode, row) {
    dialogVisible.value = true
    nextTick(() => {
        dialogRef.value?.open({ mode, row })
    })
}
// 加载字典数据
function getDictList() {
    getDictionaryByCode('deviceType,deviceStatus').then(res => {
        dictObj.value = res.data.data
    })
}
// 加载场景列表
async function loadSceneList() {
    const res = await fwDefenseSceneListApi()
    sceneList.value = res?.data?.data ?? []
}
// 加载设备列表
async function loadDeviceList() {
    const res = await fwDeviceListApi()
    deviceList.value = res?.data?.data ?? []
}
onMounted(() => {
    getDictList()
    loadSceneList()
    loadDeviceList()
    getList()
})
</script>
<style scoped lang="scss"></style>
applications/drone-command/src/views/detectionCountermeasure/taskSchedule/taskScheduleApi.js
New file
@@ -0,0 +1,37 @@
import request from '@/axios'
// 分页查询任务调度列表
export const fwTaskSchedulePageApi = params => {
    return request({
        url: `/drone-fw/detection/fwTaskSchedule/page`,
        method: 'get',
        params,
    })
}
// 查询任务调度详情
export const fwTaskScheduleDetailApi = params => {
    return request({
        url: `/drone-fw/detection/fwTaskSchedule/detail`,
        method: 'get',
        params,
    })
}
// 新增或修改任务调度
export const fwTaskScheduleSubmitApi = data => {
    return request({
        url: `/drone-fw/detection/fwTaskSchedule/submit`,
        method: 'post',
        data,
    })
}
// 逻辑删除任务调度
export const fwTaskScheduleRemoveApi = params => {
    return request({
        url: `/drone-fw/detection/fwTaskSchedule/remove`,
        method: 'post',
        params,
    })
}
applications/drone-command/src/views/recordManage/alarmRecords/index.vue
@@ -1,84 +1,98 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams">
            <el-row :gutter="16">
                <el-col :span="4">
                    <el-form-item label="无人机名称" prop="droneName">
                        <el-input v-model="searchParams.droneName" placeholder="请输入" clearable @clear="getList" />
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item label="区域" prop="areaName">
                        <el-input v-model="searchParams.areaName" placeholder="请输入" clearable @clear="getList" />
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item label="设备类型" prop="droneType">
                        <el-select v-model="searchParams.droneType" placeholder="请选择" clearable @change="getList">
                            <el-option
                                v-for="item in dictObj.deviceType"
                                :key="item.dictKey"
                                :label="item.dictValue"
                                :value="item.dictKey"
                            />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item label="时间">
                        <el-date-picker
                            popper-class="ztzf-date-picker-popper"
                            class="ztzf-date-picker"
                            v-model="dateRange"
                            type="daterange"
                            range-separator="至"
                            start-placeholder="开始日期"
                            end-placeholder="结束日期"
                            value-format="YYYY-MM-DD HH:mm:ss"
                            @change="handleSearch"
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item>
                        <el-button @click="resetForm">重置</el-button>
                        <el-button type="primary" @click="handleSearch">查询</el-button>
                    </el-form-item>
                </el-col>
            </el-row>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
            <el-form-item label="无人机名称" prop="droneName">
                <el-input
                    class="ztzf-data-cockpit-search-input"
                    v-model="searchParams.droneName"
                    placeholder="请输入"
                    clearable
                    @clear="handleSearch"
                />
            </el-form-item>
            <el-form-item label="区域" prop="areaName">
                <el-input
                    class="ztzf-data-cockpit-search-input"
                    v-model="searchParams.areaName"
                    placeholder="请输入"
                    clearable
                    @clear="handleSearch"
                />
            </el-form-item>
            <el-form-item label="设备类型" prop="droneType">
                <el-select
                    class="ztzf-data-cockpit-select"
                    popper-class="ztzf-data-cockpit-select-popper"
                    v-model="searchParams.droneType"
                    placeholder="请选择"
                    clearable
                    @change="handleSearch"
                >
                    <el-option
                        v-for="item in dictObj.deviceType"
                        :key="item.dictKey"
                        :label="item.dictValue"
                        :value="item.dictKey"
                    />
                </el-select>
            </el-form-item>
            <el-form-item label="时间">
                <el-date-picker
                    popper-class="ztzf-data-cockpit-date-picker-popper"
                    class="ztzf-data-cockpit-date-picker"
                    v-model="dateRange"
                    type="daterange"
                    range-separator="至"
                    start-placeholder="开始日期"
                    end-placeholder="结束日期"
                    value-format="YYYY-MM-DD HH:mm:ss"
                    @change="handleSearch"
                />
            </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>
            </el-form-item>
        </el-form>
        <div>
            <el-button type="primary" @click="exportFile" :loading="exportLoading">导出</el-button>
        <div class="ztzf-table-toolbar">
            <el-button :icon="Download" type="primary" @click="exportFile" :loading="exportLoading">导出</el-button>
        </div>
        <el-table v-loading="loading" :data="list">
            <el-table-column type="index" width="60" label="序号" />
            <el-table-column prop="droneName" label="无人机名称" />
            <el-table-column prop="droneSerialNo" label="无人机序列号" />
            <el-table-column prop="alarmTime" label="告警时间" />
            <el-table-column prop="areaName" label="区域" />
            <el-table-column prop="triggerReason" label="触发原因" />
            <el-table-column prop="deviceName" label="侦测反制设备" />
            <el-table-column prop="droneType" label="设备类型">
                <template v-slot="{ row }">
                    {{ getDeviceTypeName(row.droneType) }}
                </template>
            </el-table-column>
            <el-table-column prop="deviceCode" label="设备编码" />
            <el-table-column label="设备状态">
                <template v-slot="{ row }">
                    {{ getDeviceStatus(row) }}
                </template>
            </el-table-column>
        </el-table>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list">
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="droneName" show-overflow-tooltip width="140" label="无人机名称" />
                    <el-table-column prop="droneSerialNo" show-overflow-tooltip width="160" label="无人机序列号" />
                    <el-table-column prop="alarmTime" show-overflow-tooltip width="160" label="告警时间" />
                    <el-table-column prop="areaName" show-overflow-tooltip width="120" label="区域" />
                    <el-table-column prop="triggerReason" show-overflow-tooltip width="160" label="触发原因" />
                    <el-table-column prop="deviceName" show-overflow-tooltip width="160" label="侦测反制设备" />
                    <el-table-column prop="droneType" show-overflow-tooltip width="120" label="设备类型">
                        <template v-slot="{ row }">
                            {{ getDeviceTypeName(row.droneType) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="deviceSn" show-overflow-tooltip width="140" label="设备编码" />
                    <el-table-column show-overflow-tooltip width="120" label="设备状态">
                        <template v-slot="{ row }">
                            {{ getDictLabel(row.deviceStatus, dictObj.deviceStatus) }}
                        </template>
                    </el-table-column>
                </el-table>
            </div>
        <div>
            <el-pagination
                v-model:current-page="searchParams.current"
                v-model:page-size="searchParams.size"
                :total="total"
                @change="getList"
            />
            <div class="ztzf-table-pagination">
                <el-pagination
                    popper-class="ztzf-data-cockpit-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>
        <FormDiaLog ref="dialogRef" />
@@ -86,11 +100,12 @@
</template>
<script setup>
import { Search, RefreshRight, Download } from '@element-plus/icons-vue'
import { computed, onMounted, ref } from 'vue'
import { getDictionaryByCode } from '@/api/system/dictbiz'
import { exportFwDroneAlarmRecordApi, fwDroneAlarmRecordPageApi } from './fwDroneAlarmRecord'
import FormDiaLog from './FormDiaLog.vue'
import { blobDownload, dateRangeFormat } from '@ztzf/utils'
import { blobDownload, dateRangeFormat, getDictLabel } from '@ztzf/utils'
const initSearchParams = () => ({
    droneName: '', // 无人机名称
@@ -106,7 +121,8 @@
const queryParamsRef = ref(null) // 查询表单实例
const dialogRef = ref(null) // 弹框实例
const dictObj = ref({
    deviceType: [], //设备属性
    deviceType: [], // 设备类型
    deviceStatus: [], // 设备状态
})
const dateRange = ref([])
const exportLoading = ref(false)
@@ -140,14 +156,10 @@
// 重置查询
function resetForm() {
    dateRange.value = []
    queryParamsRef.value?.resetFields()
    searchParams.value.current = 1
    getList()
}
// 查看
function handleView(row) {
    dialogRef.value?.open({ mode: 'view', row: { ...row } })
}
function exportFile() {
@@ -161,17 +173,13 @@
        })
}
function getDeviceStatus(row) {
    return row.deviceStatus ?? row.status ?? '-'
}
function getDeviceTypeName(value) {
    return deviceTypeMap.value.get(value) || value || '-'
}
// 获取字典
function getDictList() {
    getDictionaryByCode('deviceType').then(res => {
    getDictionaryByCode('deviceType,deviceStatus').then(res => {
        dictObj.value = res.data.data
    })
}
applications/drone-command/src/views/recordManage/historyTracks/index.vue
@@ -1,70 +1,79 @@
<template>
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams">
            <el-row :gutter="16">
                <el-col :span="4">
                    <el-form-item label="无人机名称" prop="droneName">
                        <el-input v-model="searchParams.droneName" placeholder="请输入" clearable @clear="getList" />
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item label="区域" prop="areaName">
                        <el-input v-model="searchParams.areaName" placeholder="请输入" clearable @clear="getList" />
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item label="告警时间">
                        <el-date-picker
                            popper-class="ztzf-date-picker-popper"
                            class="ztzf-date-picker"
                            v-model="dateRange"
                            type="daterange"
                            range-separator="至"
                            start-placeholder="开始日期"
                            end-placeholder="结束日期"
                            value-format="YYYY-MM-DD HH:mm:ss"
                            @change="handleSearch"
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item>
                        <el-button @click="resetForm">重置</el-button>
                        <el-button type="primary" @click="handleSearch">查询</el-button>
                    </el-form-item>
                </el-col>
            </el-row>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
            <el-form-item label="无人机名称" prop="droneName">
                <el-input
                    class="ztzf-data-cockpit-search-input"
                    v-model="searchParams.droneName"
                    placeholder="请输入"
                    clearable
                    @clear="handleSearch"
                />
            </el-form-item>
            <el-form-item label="区域" prop="areaName">
                <el-input
                    class="ztzf-data-cockpit-search-input"
                    v-model="searchParams.areaName"
                    placeholder="请输入"
                    clearable
                    @clear="handleSearch"
                />
            </el-form-item>
            <el-form-item label="告警时间">
                <el-date-picker
                    popper-class="ztzf-data-cockpit-date-picker-popper"
                    class="ztzf-data-cockpit-date-picker"
                    v-model="dateRange"
                    type="daterange"
                    range-separator="至"
                    start-placeholder="开始日期"
                    end-placeholder="结束日期"
                    value-format="YYYY-MM-DD HH:mm:ss"
                    @change="handleSearch"
                />
            </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>
            </el-form-item>
        </el-form>
        <div>
            <el-button type="primary" @click="exportFile" :loading="exportLoading">导出</el-button>
        <div class="ztzf-table-toolbar">
            <el-button :icon="Download" type="primary" @click="exportFile" :loading="exportLoading">导出</el-button>
        </div>
        <el-table v-loading="loading" :data="list">
            <el-table-column type="index" width="60" label="序号" />
            <el-table-column prop="droneName" label="无人机名称" />
            <el-table-column prop="serialNo" label="序列号" />
            <el-table-column prop="alarmTime" label="告警时间" />
            <el-table-column prop="areaName" label="区域" />
            <el-table-column prop="triggerType" label="触发原因" />
            <el-table-column label="停留时长">
                <template v-slot="{ row }">
                    {{ formatStayDuration(row.stayDuration) }}
                </template>
            </el-table-column>
            <el-table-column label="操作">
                <template v-slot="{ row }">
                    <el-link @click="handleView(row)" type="primary">历史轨迹</el-link>
                </template>
            </el-table-column>
        </el-table>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list">
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="droneName" show-overflow-tooltip width="140" label="无人机名称" />
                    <el-table-column prop="serialNo" show-overflow-tooltip width="160" label="序列号" />
                    <el-table-column prop="alarmTime" show-overflow-tooltip width="160" label="告警时间" />
                    <el-table-column prop="areaName" show-overflow-tooltip width="120" label="区域" />
                    <el-table-column prop="triggerType" show-overflow-tooltip width="160" label="触发原因" />
                    <el-table-column show-overflow-tooltip width="140" label="停留时长">
                        <template v-slot="{ row }">
                            {{ formatStayDuration(row.stayDuration) }}
                        </template>
                    </el-table-column>
                    <el-table-column label="操作" class-name="operation-btns">
                        <template v-slot="{ row }">
                            <el-link @click="handleView(row)">历史轨迹</el-link>
                        </template>
                    </el-table-column>
                </el-table>
            </div>
        <div>
            <el-pagination
                v-model:current-page="searchParams.current"
                v-model:page-size="searchParams.size"
                :total="total"
                @change="getList"
            />
            <div class="ztzf-table-pagination">
                <el-pagination
                    popper-class="ztzf-data-cockpit-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>
        <TrajectoryDiaLog ref="dialogRef" />
@@ -72,10 +81,10 @@
</template>
<script setup>
import { Search, RefreshRight, Download } from '@element-plus/icons-vue'
import { onMounted, ref } from 'vue'
import { exportFwDroneFlightRecordApi, fwDroneFlightRecordPageApi } from './fwDroneFlightRecord'
import { blobDownload, dateRangeFormat } from '@ztzf/utils'
import FormDiaLog from '@/views/basicManage/deviceStock/FormDiaLog.vue'
import TrajectoryDiaLog from '@/views/recordManage/historyTracks/TrajectoryDiaLog.vue'
const initSearchParams = () => ({
@@ -123,7 +132,7 @@
function formatStayDuration(value) {
    if (!value) return '-'
    if (typeof value === 'string') return value
    if (typeof value == 'string') return value
    const hour = String(value.hour ?? 0).padStart(2, '0')
    const minute = String(value.minute ?? 0).padStart(2, '0')
    const second = String(value.second ?? 0).padStart(2, '0')
applications/mobile-web-view/src/appComponents/workMap/index.vue
@@ -1,77 +1,21 @@
<template>
    <div class="page-container">
        <div class="map" id="map"></div>
        <div class="weather-box" v-show="weatherShow">
            <div class="icon">
                <van-image :src="WEATHER?.[weather] || ''" width="100%" height="100%"></van-image>
            </div>
            <div class="content">
                <span class="tq">{{ weather }} {{ temperature }}</span>
                <span class="tq">风速:{{ windVelocity }}</span>
            </div>
        </div>
        <div class="back-btn" @click="goBackWork" v-show="workNavigationShow">
            <van-image width="20" height="20" :src="backIcon" />
            <div class="label">返回</div>
        </div>
        <div class="layer-btn" @click="layerChangePopupShow = true" v-show="layerShow">
            <van-image width="20" height="20" :src="layerIcon" />
            <div class="label">图层</div>
        </div>
        <div class="location-btn" @click="setMapLocation" v-show="locationShow">
            <van-image width="20" height="20" :src="locationIcon" />
            <div class="label">定位</div>
        </div>
        <!-- 事件导航按钮 -->
        <div class="work-btn" v-show="workNavigationShow">
            <van-button round block type="primary" color="#1D6FE9" @click="openAmap">事件导航</van-button>
        </div>
        <slot name="searchBar"></slot>
        <slot name="eventNav"></slot>
        <van-popup v-model:show="layerChangePopupShow" position="bottom" round>
            <div class="popup-container">
                <div class="category-content">
                    <div class="header">
                        <div class="title">底图切换</div>
                    </div>
                    <div class="content">
                        <div
                            class="layer-box"
                            :class="{ on: setSelectMapLayerKey === item.key }"
                            v-for="(item, ind) in layers"
                            :key="ind"
                            @click="setMapLayer(item)"
                        >
                            <div class="image">
                                <van-image :src="item.src" width="100%" height="100%"></van-image>
                            </div>
                            <div class="label">{{ item.name }}</div>
                        </div>
                    </div>
                <div class="location-btn" @click="setMapLocation" v-show="locationShow">
                    <van-image width="20" height="20" :src="locationIcon" />
                    <div class="label">定位</div>
                </div>
            </div>
        </van-popup>
    </div>
</template>
<script setup>
import { searchGeocoder } from '@/utils/util'
import mapnavIcon from '@/appDataSource/appwork/mapnav.svg'
import backIcon from '@/appDataSource/leafletMapIcon/back-icon.svg'
import incidentPoint from '@/appDataSource/appwork/positioning.svg'
import droneIcon from '@/appDataSource/leafletMapIcon/drone-icon.svg'
import incidentPoint from '@/appDataSource/appwork/positioning1.svg'
import userLocationIcon from '@/appDataSource/leafletMapIcon/user-location.svg'
import layerIcon from '@/appDataSource/leafletMapIcon/layer-icon.svg'
import locationIcon from '@/appDataSource/leafletMapIcon/location-icon.svg'
import { useStore } from 'vuex'
import { getDroneSuggest } from '@/api/home/common'
import DroneMarkerPopup from '@/appComponents/DroneMarkerPopup/index.vue'
import EventMarkerPopup from '@/appComponents/EventMarkerPopup/index.vue'
import { basemapLayer0, basemapLayer1, basemapLayer2, basemapLayer3 } from '@/const/leafletConst'
import L from 'leaflet'
@@ -79,24 +23,12 @@
import sl from '@/appDataSource/leafletMapIcon/sl.svg'
import yx from '@/appDataSource/leafletMapIcon/yx.svg'
import EventBus from '@/utils/eventBus'
import { polygonTransformScale } from '@/utils/turfFunc'
import { getEventImage } from '@/utils/stateToImageMap/event'
import { getDroneStatusImage } from '@/utils/stateToImageMap/drone'
import { WEATHER } from '@/const/weather'
import { wgs84ToGcj02 } from '@/utils/coordinateTransformation'
const emit = defineEmits(['location-selected'])
import _ from 'lodash'
import { useAreaBoundary } from '@/hooks/useAreaBoundary'
const { weatherShow, layerShow, locationShow, createWorkShow, workNavigationShow, mapCurrentDetail, clearMarker } =
const {  locationShow, createWorkShow, workNavigationShow, mapCurrentDetail, clearMarker } =
    defineProps({
        weatherShow: {
            type: Boolean,
            default: false,
        },
        layerShow: {
            type: Boolean,
            default: false,
        },
        locationShow: {
            type: Boolean,
            default: false,
@@ -140,9 +72,7 @@
    },
]
const layerChangePopupShow = ref(false)
const setSelectMapLayerKey = ref(1) // 选择的地图图层
let boundaryInstance = useAreaBoundary({ initFly: true })
let markersLayer = null
const initMap = () => {
    if (map) return
@@ -155,7 +85,6 @@
        doubleClickZoom: false,
        editable: true, //绘制控件
    }).setView([25.992338, 114.823254], 13)
    boundaryInstance.initBoundary(map)
    markersLayer = L.layerGroup().addTo(map) // 创建一个标注层,便于管理和移除
    if (createWorkShow) {
@@ -311,29 +240,6 @@
const store = useStore()
const selectedAreaCode = computed(() => store.state.user.selectedAreaCode)
const weather = ref('')
// 风速
const windVelocity = ref('')
const temperature = ref('')
// 获取天气建议
const getWeatherSuggest = () => {
    getDroneSuggest({ areaCode: selectedAreaCode.value }).then(res => {
        if (res.data.code !== 0) return
        weather.value = res.data.data.weather
        windVelocity.value = res.data.data.windPower
        temperature.value = res.data.data.temperature
    })
}
const goBackWork = () => {
    const transmitData = { data: { type: 'workDetailback' } }
    wx.miniProgram.navigateBack({
      delta: 1,
    })
    wx.miniProgram.postMessage(transmitData)
    uni.postMessage(transmitData)
    markersLayer.removeLayer(incidentMarker)
}
// 存储事件点标记实例,用于后续更新或移除
let incidentMarker = null
@@ -398,28 +304,8 @@
    { immediate: true, deep: true }
)
const openAmap = () => {
    if (!mapCurrentDetailData.value.longitude || !mapCurrentDetailData.value.latitude) {
        showToast('未获取到事件坐标')
        return
    }
    // wgs84ToGcj02(Number(item.longitude), Number(item.latitude))
    // wgs84ToGcj02(mapCurrentDetailData.value.longitude, mapCurrentDetailData.value.latitude)
    const [gcjLng, gcjLat] = wgs84ToGcj02(
        Number(mapCurrentDetailData.value.longitude),
        Number(mapCurrentDetailData.value.latitude)
    )
    // 高德地图URL Scheme
    const url = `https://uri.amap.com/marker?position=${gcjLng},${gcjLat}&name=%E4%BA%8B%E4%BB%B6%E7%82%B9&from='ztzf'`
    const transmitData = { data: { type: 'browser', data: { url: url } } }
    wx.miniProgram.navigateTo({ url: `/subPackages/browser/index?url=${encodeURIComponent(url)}` })
    wx.miniProgram.postMessage(transmitData)
    uni.postMessage(transmitData)
}
onMounted(async () => {
    getWeatherSuggest()
    await nextTick()
    initMap()
@@ -453,83 +339,7 @@
        width: 100%;
        height: 100%;
    }
    // 事件导航按钮
    .work-btn {
        position: absolute;
        bottom: 80px;
        left: 0;
        right: 0;
        z-index: 996;
        padding: 0 16px;
        display: flex;
        justify-content: center;
    }
    .work-btn :deep(.van-button) {
        width: 100%;
        max-width: 194px;
        height: 38px;
    }
    .weather-box {
        padding: 4px 0;
        padding-right: 6px;
        display: flex;
        justify-content: center;
        align-items: center;
        position: absolute;
        top: 12px;
        left: 12px;
        height: 48px;
        background: #fff;
        z-index: 996;
        box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.18);
        border-radius: 6px 6px 6px 6px;
        .icon {
            margin-left: 10px;
            width: 24px;
            height: 24px;
        }
        .content {
            height: 100%;
            margin-left: 4px;
            display: flex;
            flex-direction: column;
            justify-content: space-around;
            font-family: Source Han Sans CN, Source Han Sans CN;
            font-weight: 400;
            font-size: 12px;
            color: #757575;
            text-align: left;
            font-style: normal;
            text-transform: none;
        }
        .tq {
            margin: 0 5px;
        }
        .qk {
            margin-left: 5px;
            color: #04f043;
        }
        .yellowqk {
            margin-left: 5px;
            color: yellow;
        }
        .redqk {
            margin-left: 5px;
            color: red;
        }
    }
    .location-btn,
    .layer-btn,
    .back-btn {
    .location-btn{
        display: flex;
        flex-direction: column;
        justify-content: center;
@@ -547,99 +357,8 @@
            font-size: 10px;
        }
    }
    .back-btn {
        bottom: 185px;
    }
    .layer-btn {
        bottom: 130px;
    }
    .location-btn {
        bottom: 76px;
    }
}
.popup-container {
    padding: 10px 22px;
    background: #fff;
    border-radius: 12px 12px 0 0;
    .category-content {
        .header {
            height: 20px;
            font-family: Source Han Sans CN, Source Han Sans CN;
            font-weight: bold;
            font-size: 16px;
            color: #222324;
            line-height: 20px;
            text-align: left;
            font-style: normal;
            text-transform: none;
        }
        .content {
            padding: 12px;
            display: flex;
            background: #fff;
            border-radius: 16px;
            .layer-box {
                margin-left: calc((100% / 3 - 84px) * 3 / 2);
                &:first-child {
                    margin-left: 0;
                }
                &.on {
                    .image {
                        position: relative;
                        &::after {
                            content: '';
                            position: absolute;
                            top: 0;
                            left: 0;
                            width: 100%;
                            height: 100%;
                            border-radius: 4px 4px 4px 4px;
                            border: 4px solid #1d6fe9;
                            z-index: 1;
                            box-sizing: border-box;
                        }
                    }
                    .label {
                        color: #1d6fe9;
                    }
                }
                .image {
                    width: 84px;
                    height: 82px;
                    border-radius: 4px 4px 4px 4px;
                    overflow: hidden;
                    box-sizing: border-box;
                    border: none;
                }
                .label {
                    margin-top: 6px;
                    height: 20px;
                    font-family: Source Han Sans CN, Source Han Sans CN;
                    font-weight: 400;
                    font-size: 14px;
                    color: #222324;
                    line-height: 20px;
                    text-align: center;
                    font-style: normal;
                    text-transform: none;
                }
            }
            .layer-box:nth-child(3n + 1) {
                margin-left: 0;
            }
        }
    }
}
</style>
applications/mobile-web-view/src/appDataSource/appwork/defaultAvatar.svg
New file
@@ -0,0 +1,17 @@
<svg width="66" height="66" viewBox="0 0 66 66" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame" clip-path="url(#clip0_134_811)">
<path id="Vector" d="M30.6833 42.3407H35.8941V48.5364H30.6833V42.3407ZM47.647 28.7265C48.7195 28.1028 49.3564 28.1028 49.5544 28.7265C49.8531 29.6588 49.1221 32.3895 48.3714 33.099C47.8714 33.5693 47.1867 33.7673 46.3155 33.6897L47.647 28.7249V28.7265Z" fill="#FAF5F1"/>
<path id="Vector_2" d="M0 33C0 41.7521 3.47678 50.1458 9.66548 56.3345C15.8542 62.5232 24.2479 66 33 66C41.7521 66 50.1458 62.5232 56.3345 56.3345C62.5232 50.1458 66 41.7521 66 33C66 24.2479 62.5232 15.8542 56.3345 9.66548C50.1458 3.47678 41.7521 0 33 0C24.2479 0 15.8542 3.47678 9.66548 9.66548C3.47678 15.8542 0 24.2479 0 33Z" fill="#DCE0EC"/>
<path id="Vector_3" d="M33 66C28.3651 66 23.3558 64.9226 19.3479 63.0432C20.1746 58.3671 23.0703 54.4038 27.4494 52.6449C29.1423 51.9651 31.3335 52.1499 33.2789 52.1499C35.3414 52.1499 37.9847 52.0674 39.7584 52.8264C43.8488 54.5787 46.6504 58.2434 47.5794 62.606C46.8633 62.9871 45.3535 63.6966 43.1904 64.388C42.3027 64.6718 41.5536 64.8995 40.1956 65.2064C37.8317 65.7232 35.4198 65.9892 33 66Z" fill="#1D6FE9" fill-opacity="0.5"/>
<path id="Vector_4" d="M33.0298 50.6385C41.5025 50.6385 45.9724 43.8273 47.7923 35.4288C46.2199 29.4608 41.9876 20.2785 33.5149 20.2785C25.0421 20.2785 17.0149 27.0881 18.7507 35.4882C20.4865 43.8867 24.557 50.6369 33.0298 50.6369V50.6385Z" fill="#FAF5F1"/>
<path id="Vector_5" d="M20.2307 35.8264C19.0427 35.4882 18.3216 35.404 18.0626 35.5707C17.8695 35.6961 17.9207 36.5013 18.2061 37.2372C18.4916 37.9681 19.0113 38.6298 19.2786 38.6298C19.8099 38.6298 20.8032 37.8378 20.8032 37.8378L20.2307 35.8248V35.8264ZM46.3089 35.8264C47.4969 35.4882 48.2196 35.404 48.477 35.5707C48.6717 35.6961 48.6189 36.5013 48.3335 37.2372C48.048 37.9681 47.5299 38.6298 47.2626 38.6298C46.7297 38.6298 45.7364 37.8378 45.7364 37.8378L46.3089 35.8248V35.8264Z" fill="#FAF5F1"/>
<path id="Vector_6" d="M41.3555 17.9404C40.2929 17.721 34.937 12.6159 29.6157 13.6603C24.2978 14.7048 19.2966 17.886 17.4849 22.0308C15.6749 26.1723 17.7159 35.6878 18.6944 35.6878C19.6745 35.6878 20.9631 30.9144 24.4067 29.8551C27.8502 28.7958 28.5581 29.8551 32.6699 29.2314C35.4105 28.8156 37.8245 26.9412 39.9118 23.6098C44.4328 26.8356 47.0068 30.8616 47.6371 35.6878C48.8284 35.7835 49.7623 25.1212 47.6371 22.2502C45.5119 19.3792 42.4165 18.1615 41.3555 17.9404Z" fill="#1D6FE9" fill-opacity="0.5"/>
<path id="Vector_7" d="M31.2626 47.8797H35.3166V52.1268C37.092 52.1994 38.2801 52.338 38.8807 52.5393C39.1166 52.6878 38.0177 52.8644 37.9434 53.0887C37.8906 53.2438 37.6135 53.5161 37.1119 53.9022C35.4982 53.9962 34.297 54.054 33.5099 54.0771C32.7229 54.1002 31.9754 54.1002 31.2626 54.0771C29.3486 53.3544 28.5071 52.8429 28.7414 52.5409C28.9774 52.2373 29.8172 52.0839 31.2626 52.079V47.8797Z" fill="#FAF5F1"/>
<path id="Vector_8" d="M29.8485 52.1499L33.5775 54.0771L31.2626 56.3558L28.3289 52.3661C28.5797 52.2968 28.8008 52.2473 28.9905 52.2209C29.1819 52.1928 29.4674 52.1697 29.8485 52.1499ZM37.5078 52.2704L33.5792 54.0771L35.8809 56.3558L38.9631 52.5476C38.6496 52.475 38.4087 52.4238 38.2437 52.3925C38.0787 52.3595 37.8345 52.3199 37.5078 52.2704Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_134_811">
<rect width="66" height="66" fill="white"/>
</clipPath>
</defs>
</svg>
applications/mobile-web-view/src/appDataSource/appwork/positioning1.svg
New file
@@ -0,0 +1,10 @@
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame" clip-path="url(#clip0_109_1462)">
<path id="Vector" d="M11.9166 19.4328C9.73119 19.1548 7.73368 18.055 6.33004 16.357C4.9264 14.659 4.22197 12.4903 4.35992 10.2916C4.49788 8.09294 5.46786 6.02927 7.07273 4.52007C8.67759 3.01087 10.7969 2.16939 12.9999 2.16666C15.2056 2.16502 17.329 3.00444 18.9373 4.51389C20.5456 6.02334 21.5179 8.08923 21.656 10.2906C21.7942 12.492 21.0877 14.6632 19.6807 16.3619C18.2736 18.0605 16.2719 19.1588 14.0833 19.4328V21.6797C18.3581 21.7793 21.6666 22.4629 21.6666 23.2917C21.6666 24.1887 17.7861 24.9167 12.9999 24.9167C8.21375 24.9167 4.33325 24.1887 4.33325 23.2917C4.33325 22.4629 7.64175 21.7793 11.9166 21.6797V19.4328ZM12.9999 13C13.5746 13 14.1257 12.7717 14.532 12.3654C14.9383 11.9591 15.1666 11.408 15.1666 10.8333C15.1666 10.2587 14.9383 9.70759 14.532 9.30126C14.1257 8.89493 13.5746 8.66666 12.9999 8.66666C12.4253 8.66666 11.8742 8.89493 11.4679 9.30126C11.0615 9.70759 10.8333 10.2587 10.8333 10.8333C10.8333 11.408 11.0615 11.9591 11.4679 12.3654C11.8742 12.7717 12.4253 13 12.9999 13Z" fill="#FF2121"/>
</g>
<defs>
<clipPath id="clip0_109_1462">
<rect width="26" height="26" fill="white"/>
</clipPath>
</defs>
</svg>
applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue
@@ -16,7 +16,10 @@
        <div class="button-group">
            <button @click="connectWS" :disabled="connected" class="btn btn-connect">连接WS</button>
        </div>
      <!--  头像-->
            <div class="contactAvatar">
                <img :src="defaultAvatar" alt=""></img>
            </div>
        <div class="button-group">
            <button @click="requestCall" :disabled="!connected || state!=='idle'" class="btn btn-call">呼叫</button>
          
@@ -49,7 +52,7 @@
<script setup>
import { useRoute } from 'vue-router'
import defaultAvatar from '@/appDataSource/appwork/defaultAvatar.svg'
const route = useRoute()
/** ✅ 你的 WS 地址前缀(后面拼 userId) */
const WS_BASE = 'wss://wrj.shuixiongit.com/ws/chat?userId='
@@ -493,7 +496,6 @@
  <style scoped lang="scss">
    .call-container {
        padding: 20px;
        background-color: #f5f5f5;
        min-height: 100vh;
        font-family: Arial, sans-serif;
    }
@@ -535,6 +537,7 @@
        display: flex;
        gap: 10px;
        flex-wrap: wrap;
        justify-content: center;
    }
    .btn {
@@ -622,4 +625,15 @@
    .hidden-audio {
        display: none;
    }
    .contactAvatar {
        width: 172rpx;
        height: 172rpx;
        display: flex;
        justify-content: center;
        align-items: center;
        img{
            width: 172rpx;
            height: 172rpx;
        }
    }
  </style>
applications/mobile-web-view/src/appPages/work/workDetail/mapWork/index.vue
@@ -1,5 +1,5 @@
<template>
    <div class="wrapper" :style="{ paddingTop: route.query.topMargin + 'px' }">
    <div class="wrapper" >
        <WorkMap
            :weatherShow="true"
            :layerShow="true"
@@ -7,9 +7,9 @@
            :workNavigationShow="true"
            :mapCurrentDetail="mapCurrentDetail"
        >
            <template #eventNav>
                <EventNavigation :mapCurrentDetail="mapCurrentDetail"></EventNavigation>
            </template>
<!--            <template #eventNav>-->
<!--                <EventNavigation :mapCurrentDetail="mapCurrentDetail"></EventNavigation>-->
<!--            </template>-->
        </WorkMap>
    </div>
</template>
@@ -25,7 +25,39 @@
const store = useStore()
const userInfo = computed(() => store?.state?.user?.userInfo)
const route = useRoute()
const mapCurrentDetail = ref({})
const mapCurrentDetail = ref({
    "id": 8474,
    "wayline_job_id": "51835088-cd94-42f8-b2ec-a9069ced4e7b",
    "event_name": "测试",
    "device_sn": "7CTDM5E00B3H61",
    "status": 3,
    "photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260113/f090f03fdc52499473ab9cbfcdc0876d.jpeg",
    "file_path": "upload/20260113/f090f03fdc52499473ab9cbfcdc0876d.jpeg",
    "longitude": "115.85649472222222",
    "latitude": "28.624301527777778",
    "create_time": "2026-01-13 16:51:57",
    "media_type": "PHOTO_TYPE",
    "event_num": "BHJC20260113002",
    "ai_types": "道路裂缝",
    "ai_type_key_list": [
        "DLLFSB"
    ],
    "work_order_type_dict_key": "ZHZLL",
    "work_order_type": "交通运输与交管​",
    "create_user": "曾超杰",
    "update_user": "管理员",
    "dept_name": "中图智飞(江西省)",
    "create_user_id": "2009140684346388482",
    "user_id": "1991745495648227330",
    "handle_user_id": "1123598821738675201",
    "address": "江西省南昌市西湖区朝农街道抚生南路1806号南昌台湾健康产业城",
    "update_photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260113/f090f03fdc52499473ab9cbfcdc0876d.jpeg",
    "content": "测试",
    "work_type": "3",
    "job_name": "定向拍照2",
    "is_review": 0,
    "job_create_time": "2026-01-13 16:51:57"
})
const eventNum = ref('')
// 机巢数据
let machineData = ref([]);
@@ -54,9 +86,9 @@
    }
}
onMounted(async () => {
  handleNodeClick()
  // handleNodeClick()
    eventNum.value = route.query.currentItem
    await getDataList(eventNum.value)
    // await getDataList(eventNum.value)
})
</script>
packages/constants/fwDevice.js
@@ -1,9 +1 @@
// 设备状态
export const DEVICE_STATUS = {
    0: '在线',
    1: '离线',
    2: '故障',
    3: '报废',
}
export const DEVICE_STATUS_LIST = Object.keys(DEVICE_STATUS).map(key => ({ value: key, label: DEVICE_STATUS[key] }))
packages/utils/common/index.js
@@ -14,7 +14,7 @@
export function getDictLabel(value, dictList) {
    return dictList.find(item => item.dictKey === value)?.dictValue || value || '-'
    return dictList.find(item => item.dictKey === String(value))?.dictValue || value || ''
}
export function blobDownload(res) {
uniapps/work-app/src/pages.json
@@ -59,7 +59,8 @@
        {
          "path": "workDetail/index",
          "style": {
            "navigationBarTitleText": "工单详情"
            "navigationBarTitleText": "工单详情",
            "navigationStyle": "custom"
          }
        },
        {
@@ -109,8 +110,8 @@
        "text": "工单管理"
      },
      {
        "iconPath": "static/images/tabbar/icon_order.png",
        "selectedIconPath": "static/images/tabbar/icon_order_selected.png",
        "iconPath": "static/images/tabbar/icon_speech.png",
        "selectedIconPath": "static/images/tabbar/icon_speech_selected.png",
        "pagePath": "pages/voiceCall/index",
        "text": "语音通话"
      },
uniapps/work-app/src/pages/login/index.vue
@@ -27,7 +27,7 @@
        </checkbox-group>
        记住密码
      </label>
      <div class="forgotPassword">忘记密码?</div>
      <div class="forgotPassword" @click="goToForgotPassword">忘记密码?</div>
    </div>
    <button class="login-btn" :style="[inputStyle]" @tap="submit">登录</button>
@@ -78,6 +78,12 @@
  showPassword.value = !showPassword.value;
}
function goToForgotPassword() {
  uni.navigateTo({
    url: '/subPackages/userDetail/password/index'
  });
}
async function submit() {
  if (!loginForm.value.username.trim()) {
    uni.showToast({
uniapps/work-app/src/pages/work/index.vue
@@ -9,15 +9,23 @@
        <up-tabs :list="tabList" @click="handleClick"></up-tabs>
      </div>
      <div class="eventBox">
        <div class="eventItem"  v-for="(item,index) in dataList" :key="index">
          <img :src="item.photo_url" alt="" @click="detailHandle(item)" />
          <div class="informationDisplay">
            <div class="itemTitle">{{item.event_name}}</div>
            <div class="itemContent">{{formatDate(item.create_time) }}</div>
      <scroll-view class="eventBox" scroll-y :lower-threshold="80" @scrolltolower="loadMore">
        <div class="eventList">
          <div class="eventItem"  v-for="(item,index) in dataList" :key="index">
            <img :src="item.photo_url" alt="" @click="detailHandle(item)" />
            <div class="informationDisplay">
              <!--              <div class="itemTitle">{{item.event_name}}</div>-->
              <div class="itemContent">{{formatDate(item.create_time) }}</div>
            </div>
          </div>
        </div>
      </div>
        <!-- 加载提示 -->
        <div class="loadingMore">
          <text v-if="loading">加载中...</text>
        </div>
      </scroll-view>
    </div>
  </div>
@@ -32,8 +40,408 @@
import { getStatusBarHeight } from '@/utils/common';
const userStore = useUserStore()
const userInfo = userStore.userInfo
const dataList = ref([])
const dataList = ref([
  {
    "id": 8474,
    "wayline_job_id": "51835088-cd94-42f8-b2ec-a9069ced4e7b",
    "event_name": "测试",
    "device_sn": "7CTDM5E00B3H61",
    "status": 3,
    "photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260113/f090f03fdc52499473ab9cbfcdc0876d.jpeg",
    "file_path": "upload/20260113/f090f03fdc52499473ab9cbfcdc0876d.jpeg",
    "longitude": "115.85649472222222",
    "latitude": "28.624301527777778",
    "create_time": "2026-01-13 16:51:57",
    "media_type": "PHOTO_TYPE",
    "event_num": "BHJC20260113002",
    "ai_types": "道路裂缝",
    "ai_type_key_list": [
      "DLLFSB"
    ],
    "work_order_type_dict_key": "ZHZLL",
    "work_order_type": "交通运输与交管​",
    "create_user": "曾超杰",
    "update_user": "管理员",
    "dept_name": "中图智飞(江西省)",
    "create_user_id": "2009140684346388482",
    "user_id": "1991745495648227330",
    "handle_user_id": "1123598821738675201",
    "address": "江西省南昌市西湖区朝农街道抚生南路1806号南昌台湾健康产业城",
    "update_photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260113/f090f03fdc52499473ab9cbfcdc0876d.jpeg",
    "content": "测试",
    "work_type": "3",
    "job_name": "定向拍照2",
    "is_review": 0,
    "job_create_time": "2026-01-13 16:51:57"
  },
  {
    "id": 8472,
    "wayline_job_id": "6beae73e-2d5d-47e5-859b-e2a1955d6e3b",
    "event_name": "车事件",
    "event_dict_key": "Traffic_flow",
    "device_sn": "7CTDM5E00B3H61",
    "status": 3,
    "photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket//6beae73e-2d5d-47e5-859b-e2a1955d6e3b/DJI_202601091336_002_6beae73e-2d5d-47e5-859b-e2a1955d6e3b/车AI20260112144421001.jpeg",
    "file_path": "/6beae73e-2d5d-47e5-859b-e2a1955d6e3b/DJI_202601091336_002_6beae73e-2d5d-47e5-859b-e2a1955d6e3b/车AI20260112144421001.jpeg",
    "longitude": "115.85502433333333",
    "latitude": "28.623021472222224",
    "create_time": "2026-01-12 14:44:25",
    "media_type": "PHOTO_TYPE",
    "event_num": "AI20260112144421001",
    "remark": "江西省南昌市西湖区朝农街道翠菊路银燕物流基地发生车事件",
    "ai_types": "车",
    "ai_type_key_list": [
      "CGL_CLLTJ"
    ],
    "work_order_type_dict_key": "ZHZLL",
    "work_order_type": "交通运输与交管​",
    "create_user": "曾超杰",
    "update_user": "空间大厦管理员",
    "dept_name": "中图智飞(江西省)",
    "create_user_id": "2009140684346388482",
    "user_id": "1991745495648227330",
    "handle_user_id": "1979075301412524033",
    "address": "江西省南昌市西湖区朝农街道翠菊路银燕物流基地",
    "content": "测试",
    "work_type": "2",
    "job_name": "马上智飞202601091336",
    "is_review": 0,
    "job_create_time": "2026-01-09 13:36:42"
  },
  {
    "id": 8464,
    "wayline_job_id": "8b4b7529-13ee-4885-a7b8-4a52667444fb",
    "event_name": "1212",
    "device_sn": "7CTDM5E00B3H61",
    "status": 3,
    "photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260107/5e6bc6ecedd830f2415a959667b3841a.jpeg",
    "file_path": "upload/20260107/5e6bc6ecedd830f2415a959667b3841a.jpeg",
    "longitude": "115.85803208048642",
    "latitude": "28.625135341676483",
    "create_time": "2026-01-07 11:18:35",
    "media_type": "PHOTO_TYPE",
    "event_num": "BHJC20260107003",
    "ai_types": "水体污染",
    "ai_type_key_list": [
      "STWLSB"
    ],
    "work_order_type_dict_key": "SLL",
    "work_order_type": "生态环境与水务",
    "create_user": "罗广辉",
    "update_user": "空间大厦管理员",
    "dept_name": "中图智飞(江西省)",
    "create_user_id": "1896495271624384514",
    "user_id": "1991745495648227330",
    "handle_user_id": "1979075301412524033",
    "address": "江西省南昌市西湖区朝农街道抚生南路世纪吉的堡幼儿园",
    "update_photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260107/5e6bc6ecedd830f2415a959667b3841a.jpeg",
    "content": "2121",
    "work_type": "3",
    "job_name": "马上智飞202601051720",
    "is_review": 0,
    "job_create_time": "2026-01-07 11:18:35"
  },
  {
    "id": 8463,
    "wayline_job_id": "8b4b7529-13ee-4885-a7b8-4a52667444fb",
    "event_name": "122112",
    "device_sn": "7CTDM5E00B3H61",
    "status": 3,
    "photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260107/a52b1adda405b94a4f9436e43e1b3185.jpeg",
    "file_path": "upload/20260107/a52b1adda405b94a4f9436e43e1b3185.jpeg",
    "longitude": "115.85780692063119",
    "latitude": "28.624775436656122",
    "create_time": "2026-01-07 11:17:47",
    "media_type": "PHOTO_TYPE",
    "event_num": "BHJC20260107002",
    "ai_types": "水体污染",
    "ai_type_key_list": [
      "STWLSB"
    ],
    "work_order_type_dict_key": "SLL",
    "work_order_type": "生态环境与水务",
    "create_user": "罗广辉",
    "update_user": "空间大厦管理员",
    "dept_name": "中图智飞(江西省)",
    "create_user_id": "1896495271624384514",
    "user_id": "1991745495648227330",
    "handle_user_id": "1979075301412524033",
    "address": "江西省南昌市西湖区朝农街道抚生南路世纪吉的堡幼儿园",
    "update_photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260107/a52b1adda405b94a4f9436e43e1b3185.jpeg",
    "content": "1",
    "work_type": "3",
    "job_name": "马上智飞202601051720",
    "is_review": 0,
    "job_create_time": "2026-01-07 11:17:48"
  },
  {
    "id": 8462,
    "wayline_job_id": "8b4b7529-13ee-4885-a7b8-4a52667444fb",
    "event_name": "1",
    "device_sn": "7CTDM5E00B3H61",
    "status": 3,
    "photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260107/a138491d214cc0ce7a5a3eedf6b79680.jpeg",
    "file_path": "upload/20260107/a138491d214cc0ce7a5a3eedf6b79680.jpeg",
    "longitude": "115.85763536212585",
    "latitude": "28.626095211224335",
    "create_time": "2026-01-07 11:14:41",
    "media_type": "PHOTO_TYPE",
    "event_num": "BHJC20260107001",
    "ai_types": "水体污染",
    "ai_type_key_list": [
      "STWLSB"
    ],
    "work_order_type_dict_key": "SLL",
    "work_order_type": "生态环境与水务",
    "create_user": "罗广辉",
    "update_user": "江西融媒",
    "dept_name": "中图智飞(江西省)",
    "create_user_id": "1896495271624384514",
    "user_id": "1991745495648227330",
    "handle_user_id": "1993119267150655490",
    "address": "江西省南昌市西湖区朝农街道国贸天琴湾东区",
    "update_photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260107/a138491d214cc0ce7a5a3eedf6b79680.jpeg",
    "content": "1",
    "work_type": "3",
    "job_name": "马上智飞202601051720",
    "is_review": 0,
    "job_create_time": "2026-01-07 11:14:42"
  },
  {
    "id": 8461,
    "wayline_job_id": "038b8f48-1704-4adc-abb7-7deb85eba101",
    "event_name": "测试",
    "device_sn": "7CTDM5E00B3H61",
    "status": 3,
    "photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260106/c94dca50d34e13f53fd5a5c05abfe03f.jpeg",
    "file_path": "upload/20260106/c94dca50d34e13f53fd5a5c05abfe03f.jpeg",
    "longitude": "115.85623975",
    "latitude": "28.624919472222224",
    "create_time": "2026-01-06 13:38:03",
    "media_type": "PHOTO_TYPE",
    "event_num": "BHJC20260106018",
    "ai_types": "水体污染",
    "ai_type_key_list": [
      "STWLSB"
    ],
    "work_order_type_dict_key": "SLL",
    "work_order_type": "生态环境与水务",
    "create_user": "张含笑",
    "update_user": "张含笑",
    "dept_name": "中图智飞(江西省)",
    "create_user_id": "1909067460995813377",
    "user_id": "1991745495648227330",
    "handle_user_id": "1909067460995813377",
    "address": "江西省南昌市西湖区朝农街道抚生南路1818号国贸天琴湾",
    "update_photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260106/c94dca50d34e13f53fd5a5c05abfe03f.jpeg",
    "content": "123",
    "work_type": "3",
    "job_name": "定向拍照-1-5",
    "is_review": 0,
    "job_create_time": "2026-01-06 13:38:03"
  },
  {
    "id": 8460,
    "wayline_job_id": "8b4b7529-13ee-4885-a7b8-4a52667444fb",
    "event_name": "11",
    "device_sn": "7CTDM5E00B3H61",
    "status": 3,
    "photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260106/6ef9355b143d4e89d83c77d18f4e312f.jpeg",
    "file_path": "upload/20260106/6ef9355b143d4e89d83c77d18f4e312f.jpeg",
    "longitude": "115.85763536212585",
    "latitude": "28.626095211224335",
    "create_time": "2026-01-06 11:08:51",
    "media_type": "PHOTO_TYPE",
    "event_num": "BHJC20260106017",
    "ai_types": "建筑垃圾",
    "ai_type_key_list": [
      "GCL_JZLJSB"
    ],
    "work_order_type_dict_key": "CGL",
    "work_order_type": "城市管理与综合执法",
    "create_user": "张含笑",
    "update_user": "罗广辉",
    "dept_name": "中图智飞(江西省)",
    "create_user_id": "1909067460995813377",
    "user_id": "1991745495648227330",
    "handle_user_id": "1896495271624384514",
    "address": "江西省南昌市西湖区朝农街道国贸天琴湾东区",
    "update_photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260106/6ef9355b143d4e89d83c77d18f4e312f.jpeg",
    "content": "12",
    "work_type": "3",
    "job_name": "马上智飞202601051720",
    "is_review": 0,
    "job_create_time": "2026-01-06 11:08:52"
  },
  {
    "id": 8459,
    "wayline_job_id": "038b8f48-1704-4adc-abb7-7deb85eba101",
    "event_name": "122",
    "device_sn": "7CTDM5E00B3H61",
    "status": 3,
    "photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260106/96c7769b12f2e55b7e788e8f35b0364b.jpeg",
    "file_path": "upload/20260106/96c7769b12f2e55b7e788e8f35b0364b.jpeg",
    "longitude": "115.85623975",
    "latitude": "28.624919472222224",
    "create_time": "2026-01-06 11:08:00",
    "media_type": "PHOTO_TYPE",
    "event_num": "BHJC20260106016",
    "ai_types": "水生植物漂浮物",
    "ai_type_key_list": [
      "SMPFWSB"
    ],
    "work_order_type_dict_key": "SLL",
    "work_order_type": "生态环境与水务",
    "create_user": "张含笑",
    "update_user": "张含笑",
    "dept_name": "中图智飞(江西省)",
    "create_user_id": "1909067460995813377",
    "user_id": "1991745495648227330",
    "handle_user_id": "1909067460995813377",
    "address": "江西省南昌市西湖区朝农街道抚生南路1818号国贸天琴湾",
    "update_photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260106/96c7769b12f2e55b7e788e8f35b0364b.jpeg",
    "content": "1",
    "work_type": "3",
    "job_name": "定向拍照-1-5",
    "is_review": 0,
    "job_create_time": "2026-01-06 11:08:00"
  },
  {
    "id": 8458,
    "wayline_job_id": "8b4b7529-13ee-4885-a7b8-4a52667444fb",
    "event_name": "12",
    "device_sn": "7CTDM5E00B3H61",
    "status": 3,
    "photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260106/2e065c4f0e78d4fa1cfdd15f65397b85.jpeg",
    "file_path": "upload/20260106/2e065c4f0e78d4fa1cfdd15f65397b85.jpeg",
    "longitude": "115.85677911492691",
    "latitude": "28.62569394530331",
    "create_time": "2026-01-06 11:00:53",
    "media_type": "PHOTO_TYPE",
    "event_num": "BHJC20260106015",
    "ai_types": "水体污染",
    "ai_type_key_list": [
      "STWLSB"
    ],
    "work_order_type_dict_key": "SLL",
    "work_order_type": "生态环境与水务",
    "create_user": "张含笑",
    "update_user": "罗广辉",
    "dept_name": "中图智飞(江西省)",
    "create_user_id": "1909067460995813377",
    "user_id": "1991745495648227330",
    "handle_user_id": "1896495271624384514",
    "address": "江西省南昌市西湖区朝农街道抚生南路1836号国贸天琴湾东区",
    "update_photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260106/2e065c4f0e78d4fa1cfdd15f65397b85.jpeg",
    "content": "22",
    "work_type": "3",
    "job_name": "马上智飞202601051720",
    "is_review": 0,
    "job_create_time": "2026-01-06 11:00:54"
  },
  {
    "id": 8457,
    "wayline_job_id": "038b8f48-1704-4adc-abb7-7deb85eba101",
    "event_name": "发布事件",
    "device_sn": "7CTDM5E00B3H61",
    "status": 3,
    "photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260106/ecdfe6c661dba2fa03efcc19a5863680.jpeg",
    "file_path": "upload/20260106/ecdfe6c661dba2fa03efcc19a5863680.jpeg",
    "longitude": "115.85623975",
    "latitude": "28.624919472222224",
    "create_time": "2026-01-06 10:43:29",
    "media_type": "PHOTO_TYPE",
    "event_num": "BHJC20260106014",
    "ai_types": "水体污染",
    "ai_type_key_list": [
      "STWLSB"
    ],
    "work_order_type_dict_key": "SLL",
    "work_order_type": "生态环境与水务",
    "create_user": "张含笑",
    "update_user": "张含笑",
    "dept_name": "中图智飞(江西省)",
    "create_user_id": "1909067460995813377",
    "user_id": "1991745495648227330",
    "handle_user_id": "1909067460995813377",
    "address": "江西省南昌市西湖区朝农街道抚生南路1818号国贸天琴湾",
    "update_photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260106/ecdfe6c661dba2fa03efcc19a5863680.jpeg",
    "content": "11",
    "work_type": "3",
    "job_name": "定向拍照-1-5",
    "is_review": 0,
    "job_create_time": "2026-01-06 10:43:30"
  },
  {
    "id": 8456,
    "wayline_job_id": "038b8f48-1704-4adc-abb7-7deb85eba101",
    "event_name": "122",
    "device_sn": "7CTDM5E00B3H61",
    "status": 3,
    "photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260106/fc9820c440da478fa5312e8647e1e311.jpeg",
    "file_path": "upload/20260106/fc9820c440da478fa5312e8647e1e311.jpeg",
    "longitude": "115.85623975",
    "latitude": "28.624919472222224",
    "create_time": "2026-01-06 10:38:20",
    "media_type": "PHOTO_TYPE",
    "event_num": "BHJC20260106013",
    "ai_types": "水体污染",
    "ai_type_key_list": [
      "STWLSB"
    ],
    "work_order_type_dict_key": "SLL",
    "work_order_type": "生态环境与水务",
    "create_user": "张含笑",
    "update_user": "张含笑",
    "dept_name": "中图智飞(江西省)",
    "create_user_id": "1909067460995813377",
    "user_id": "1991745495648227330",
    "handle_user_id": "1909067460995813377",
    "address": "江西省南昌市西湖区朝农街道抚生南路1818号国贸天琴湾",
    "update_photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260106/fc9820c440da478fa5312e8647e1e311.jpeg",
    "content": "3",
    "work_type": "3",
    "job_name": "定向拍照-1-5",
    "is_review": 0,
    "job_create_time": "2026-01-06 10:38:20"
  },
  {
    "id": 8455,
    "wayline_job_id": "038b8f48-1704-4adc-abb7-7deb85eba101",
    "event_name": "122",
    "device_sn": "7CTDM5E00B3H61",
    "status": 3,
    "photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260106/d9a152ab1484348be5e126f854eca86a.jpeg",
    "file_path": "upload/20260106/d9a152ab1484348be5e126f854eca86a.jpeg",
    "longitude": "115.85623975",
    "latitude": "28.624919472222224",
    "create_time": "2026-01-06 10:38:18",
    "media_type": "PHOTO_TYPE",
    "event_num": "BHJC20260106012",
    "ai_types": "水体污染",
    "ai_type_key_list": [
      "STWLSB"
    ],
    "work_order_type_dict_key": "SLL",
    "work_order_type": "生态环境与水务",
    "create_user": "张含笑",
    "update_user": "张含笑",
    "dept_name": "中图智飞(江西省)",
    "create_user_id": "1909067460995813377",
    "user_id": "1991745495648227330",
    "handle_user_id": "1909067460995813377",
    "address": "江西省南昌市西湖区朝农街道抚生南路1818号国贸天琴湾",
    "update_photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260106/d9a152ab1484348be5e126f854eca86a.jpeg",
    "content": "3",
    "work_type": "3",
    "job_name": "定向拍照-1-5",
    "is_review": 0,
    "job_create_time": "2026-01-06 10:38:18"
  }
])
const currentTab=ref('all')
const loading = ref(false)
const hasMore = ref(true)
const tabList = ref([
  {
    name: '全部工单',
@@ -49,7 +457,7 @@
    badge: {
      value: 1
    }
  },
  }
])
@@ -59,16 +467,19 @@
const listParams = ref({
  status: null,
  current: 1,
  size: 9999,
  size: 12,
  source: 1,
  department:'',
  keyword:''
})
const getDataList = () => {
  if (loading.value || !hasMore.value) return
  loading.value = true
  const params = {
    current: 1,
    size: 9999,
    current: listParams.value.current,
    size: listParams.value.size,
    source: 1,
    status:listParams.value.status,
    event_name:listParams.value.keyword,
@@ -76,10 +487,29 @@
  }
  getList(params).then(res => {
    const response = res.data.data.records
    dataList.value = response
    const response = res.data.data
    const records = response.records || []
    // 根据当前页码决定是替换还是追加数据
    if (listParams.value.current === 1) {
      dataList.value = records
    } else {
      dataList.value = [...dataList.value, ...records]
    }
    // 判断是否还有更多数据
    if (records.length < listParams.value.size || response.current >= response.pages) {
      hasMore.value = false
    } else {
      hasMore.value = true
    }
  }).finally(() => {
    loading.value = false
  })
}
// const getstatusCountData=()=>{
//     getstatusCount().then(res=>{
//         const response = res.data.data
@@ -102,8 +532,17 @@
const handleClick = (item) => {
  currentTab.value = item.key
  listParams.value.status = item.status
  listParams.value.current = 1
  hasMore.value = true
  dataList.value = []
  getDataList()
}
const loadMore = () => {
  if (loading.value || !hasMore.value) return
  listParams.value.current++
  getDataList()
}
const detailHandle = (val) => {
  uni.navigateTo({
    url: `/subPackages/workDetail/index?eventNum=${val.event_num}`,
@@ -111,8 +550,10 @@
}
const topMargin = getStatusBarHeight()
onShow(() => {
  getDataList()
  // listParams.value.current = 1
  // hasMore.value = true
  // dataList.value = []
  // getDataList()
})
</script>
@@ -123,6 +564,8 @@
  flex-direction: column;
  height: 100%;
  box-sizing: border-box;
  width: 100%;
  overflow-x: hidden;
  .searchTop {
    display: flex;
    align-items: center;
@@ -134,11 +577,13 @@
  :deep(){
    .u-tabs__wrapper__nav__line{
      width: 80rpx !important;
      // width: 80rpx !important;
      background: #1D6FE9 !important;
    }
    .u-badge {
      background-color: #1d6fe9 !important;
      margin-top: -17px !important;
      margin-left: -5px !important;
    }
  }
@@ -153,34 +598,53 @@
    display: flex;
    flex-direction: column;
    flex: 1;
    width: 100%;
    box-sizing: border-box;
  }
  .eventBox {
    display: flex;
    flex-wrap: wrap;
    gap: 20rpx;
    padding: 20rpx 0;
    overflow-y: auto;
    overflow-x: hidden;
    height: 0;
    flex-grow: 1;
    align-content: flex-start;
    width: 100%;
    box-sizing: border-box;
  }
  .eventList {
    display: flex;
    flex-wrap: wrap;
    gap: 20rpx;
    width: 100%;
    box-sizing: border-box;
    justify-content: space-between;
    .eventItem {
      width: calc(50% - 10rpx);
      background-color: #fff;
      border-radius: 12rpx;
      overflow: hidden;
      position: relative;
      box-sizing: border-box;
      max-width: 100%;
      img {
        width: 341rpx;
        height: 196rpx;
        width: 100%;
        height: 208rpx;
        border-radius: 12rpx;
        overflow: hidden;
      }
      .informationDisplay{
        width: 100%;
        position: absolute;
        bottom: 3px;
        background: rgba(11,11,11,0.35);
        border-radius: 0rpx 0rpx 12rpx 12rpx;
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 10rpx 20rpx 10rpx 12rpx;
        justify-content: flex-end;
        padding: 10rpx 0rpx 10rpx 0rpx;
        .itemTitle {
          width: 144rpx;
          font-family: Source Han Sans CN, Source Han Sans CN;
@@ -196,12 +660,22 @@
          font-family: Source Han Sans CN, Source Han Sans CN;
          font-weight: 400;
          font-size: 24rpx;
          color: rgba(0,0,0,0.5);
          color: #FFFFFF;
          padding-right: 14rpx;
        }
      }
    }
  }
  .loadingMore {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 80rpx;
    font-size: 28rpx;
    color: #999;
    width: 100%;
  }
}
</style>
uniapps/work-app/src/static/images/tabbar/icon_speech.png
uniapps/work-app/src/static/images/tabbar/icon_speech_selected.png
uniapps/work-app/src/subPackages/userDetail/password/index.vue
@@ -213,7 +213,7 @@
      password2: password2,
            code
        };
    console.log('canshu',resetData)
        resetPassword(resetData).then(res => {
            if (res.data.code === 200) {
                uni.showToast({
uniapps/work-app/src/subPackages/workDetail/addWork/index.vue
File was deleted
uniapps/work-app/src/subPackages/workDetail/index.vue
@@ -1,6 +1,18 @@
<!-- 工单详情 -->
<template>
  <div class="workDetailContainer">
    <!-- 自定义导航栏 -->
    <u-navbar
      title="工单详情"
      :is-back="true"
      back-text=""
      :back-icon-size="40"
      @left-click="onBackClick"
    >
      <template #right>
        <div class="share-btn" @click="onShareClick">分享</div>
      </template>
    </u-navbar>
    <div class="detailTop">
      <div class="image-container">
        <u-swiper
@@ -31,12 +43,11 @@
        <div class="workOrderContainer">
          <div class="orderRow">
            <div class="rowTitle">工单编号</div>
            <div>{{workDetailData.event_num}}</div>
            <div>{{ workDetailData.event_num }}</div>
          </div>
          <div class="orderRow">
            <div class="rowTitle">工单处置人</div>
            <div>{{workDetailData.event_num?.slice(0, 2) === 'AI' ? 'AI 小飞':workDetailData.creator}}
            </div>
            <div>{{ workDetailData.event_num?.slice(0, 2) === 'AI' ? 'AI 小飞' : workDetailData.creator }}</div>
          </div>
          <div class="orderRow">
            <div class="rowTitle">处置部门</div>
@@ -65,59 +76,89 @@
          </div>
        </div>
      </div>
    </div>
    <!-- 操作按钮 -->
    <div class="actionButton">
      <div class="btngroups" >
      <div class="btngroups">
        <up-button type="primary" color="#AEAEAE" text="退回"></up-button>
        <up-button type="primary" color="#1D6FE9" text="确认"></up-button>
      </div>
    </div>
  </div>
</template>
<script setup>
// import { getShowImg, getSmallImg } from '@/utils/util'
import {getList,flowEvent} from '/src/api/work/index.js'
import { ref, computed } from 'vue'
import { getList, flowEvent } from '/src/api/work/index.js'
import dayjs from 'dayjs'
const formatDate = dateString => {
  return dayjs(dateString).format('MM/DD HH:mm')
}
const eventNum = ref('');
const eventNum = ref('')
// 工单内容
const workDetailData = ref({})
onLoad(async (options) => {
  eventNum.value = options.eventNum;
  await getDataList(eventNum.value);
});
const getDataList = async (val) => {
const workDetailData = ref({
  "id": 8474,
  "wayline_job_id": "51835088-cd94-42f8-b2ec-a9069ced4e7b",
  "event_name": "测试",
  "device_sn": "7CTDM5E00B3H61",
  "status": 3,
  "photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260113/f090f03fdc52499473ab9cbfcdc0876d.jpeg",
  "file_path": "upload/20260113/f090f03fdc52499473ab9cbfcdc0876d.jpeg",
  "longitude": "115.85649472222222",
  "latitude": "28.624301527777778",
  "create_time": "2026-01-13 16:51:57",
  "media_type": "PHOTO_TYPE",
  "event_num": "BHJC20260113002",
  "ai_types": "道路裂缝",
  "ai_type_key_list": [
    "DLLFSB"
  ],
  "work_order_type_dict_key": "ZHZLL",
  "work_order_type": "交通运输与交管​",
  "create_user": "曾超杰",
  "update_user": "管理员",
  "dept_name": "中图智飞(江西省)",
  "create_user_id": "2009140684346388482",
  "user_id": "1991745495648227330",
  "handle_user_id": "1123598821738675201",
  "address": "江西省南昌市西湖区朝农街道抚生南路1806号南昌台湾健康产业城",
  "update_photo_url": "https://wrj.shuixiongit.com/minio/cloud-bucket/upload/20260113/f090f03fdc52499473ab9cbfcdc0876d.jpeg",
  "content": "测试",
  "work_type": "3",
  "job_name": "定向拍照2",
  "is_review": 0,
  "job_create_time": "2026-01-13 16:51:57"
},)
onLoad(async options => {
  eventNum.value = options.eventNum
  // await getDataList(eventNum.value)
})
const getDataList = async val => {
  const params = {
    current: 1,
    size: 9999,
    source: 1,
    event_name: val
    event_name: val,
  }
  const res = await getList(params);
  const response = res.data.data.records;
  workDetailData.value = response[0];
  const res = await getList(params)
  const response = res.data.data.records
  workDetailData.value = response[0]
  console.log('详情',response);
  console.log('详情', response)
}
// 图片预览
const previewImage = (index) => {
  if (getImageList.value.length === 0) return;
  const currentIndex = typeof index === 'number' ? index : 0;
const previewImage = index => {
  if (getImageList.value.length === 0) return
  const currentIndex = typeof index === 'number' ? index : 0
  uni.previewImage({
    urls: getImageList.value,
    current: currentIndex
  });
};
const getImageList =computed(()=>{
    current: currentIndex,
  })
}
const getImageList = computed(() => {
  const imageArr = []
  const detail = workDetailData.value
  if (detail.photo_url) {
@@ -125,7 +166,7 @@
    imageArr.push(smallUrl)
  }
  if (detail.update_photo_url) {
    const smallUrl =detail.update_photo_url
    const smallUrl = detail.update_photo_url
    imageArr.push(smallUrl)
  }
  return imageArr
@@ -136,9 +177,114 @@
    url: `/subPackages/workDetail/mapWork/index?eventNum=${item.event_num}`,
  })
}
// 分享按钮点击事件
const onShareClick = () => {
  // 检查当前平台是否支持分享功能
  const platform = uni.getSystemInfoSync().platform
  // H5平台不支持uni.share API
  if (platform === 'web') {
    uni.showToast({
      title: 'H5平台暂不支持分享功能',
      icon: 'none'
    })
    return
  }
  uni.showActionSheet({
    itemList: ['分享给微信好友', '分享到朋友圈'],
    success: res => {
      // 检查uni.share是否存在
      if (typeof uni.share !== 'function') {
        uni.showToast({
          title: '当前环境不支持分享功能',
          icon: 'none'
        })
        return
      }
      try {
        switch (res.tapIndex) {
          case 0:
            // 分享给微信好友
            uni.share({
              provider: 'weixin',
              scene: 'WXSceneSession',
              type: 0,
              title: '工单详情',
              summary: workDetailData.value.event_name,
              href: 'https://example.com/workDetail?eventNum=' + workDetailData.value.event_num,
              imageUrl: workDetailData.value.photo_url,
              success: () => {
                uni.showToast({ title: '分享成功', icon: 'success' })
              },
              fail: (err) => {
                console.log('分享失败', err.errMsg)
                uni.showToast({
                  title: err.errMsg,
                  icon: 'none'
                })
              }
            })
            break
          case 1:
            // 分享到朋友圈
            uni.share({
              provider: 'weixin',
              scene: 'WXSceneTimeline',
              type: 0,
              title: workDetailData.value.event_name,
              summary: '查看工单详情',
              href: 'https://example.com/workDetail?eventNum=' + workDetailData.value.event_num,
              imageUrl: workDetailData.value.photo_url,
              success: () => {
                uni.showToast({ title: '分享成功', icon: 'success' })
              },
              fail: (err) => {
                console.log('分享失败', err)
                uni.showToast({
                  title: '分享失败,请检查配置',
                  icon: 'none'
                })
              }
            })
            break
        }
      } catch (error) {
        console.error('分享出错:', error)
        uni.showToast({
          title: '分享功能异常',
          icon: 'none'
        })
      }
    }
  })
}
// 返回按钮点击事件
const onBackClick = () => {
  uni.navigateBack()
}
// 监听导航栏右侧按钮点击事件(兼容原来的配置)
const onNavigationBarButtonTap = e => {
  if (e.index === 0) {
    onShareClick()
  }
}
</script>
<style lang="scss" scoped>
.workDetailContainer {
  // 添加导航栏高度的padding-top,避免内容被遮挡
  padding-top: 44px;
  // 分享按钮样式
  .share-btn {
    color: #1D6FE9;
    font-size: 14px;
    padding: 0 10px;
    line-height: 44px;
  }
  .detailTop {
    .image-container {
      position: relative;
@@ -163,9 +309,8 @@
          justify-content: center;
          align-items: center;
          z-index: 10;
        }
        .u-swiper-indicator__wrapper__dot--active{
        .u-swiper-indicator__wrapper__dot--active {
          width: 5px !important;
        }
      }
@@ -177,7 +322,6 @@
        height: 60rpx;
        line-height: 60rpx;
        background: rgba(7, 7, 7, 0.4);
      }
      .titleText {
@@ -187,16 +331,17 @@
        align-items: center;
        color: #fff;
        .itemName, .itemTime {
        .itemName,
        .itemTime {
          font-family: Source Han Sans CN, Source Han Sans CN;
          font-weight: 400;
          font-size: 28rpx;
          color: #FFFFFF;
          color: #ffffff;
        }
        .itemName {
          padding-left: 20rpx;
        }
        .itemTime{
        .itemTime {
          padding-right: 20rpx;
        }
      }
@@ -235,13 +380,12 @@
          font-size: 30rpx;
          color: #222324;
          white-space: nowrap;
        }
        .rowAddress {
          font-family: Source Han Sans CN, Source Han Sans CN;
          font-weight: 400;
          font-size: 28rpx;
          color: #1D6FE9;
          color: #1d6fe9;
          //white-space: nowrap;
          ///* 禁止换行 */
          //overflow: hidden;
@@ -268,14 +412,13 @@
      display: flex;
      justify-content: space-between;
      align-items: center;
      :deep(.u-button){
      :deep(.u-button) {
        width: 276rpx !important;
        height: 100rpx !important;
        &:last-child {
          margin-left: 30rpx;
        }
      }
    }
  }
}
uniapps/work-app/src/subPackages/workDetail/photoMagnify/index.vue
File was deleted