吉安感知网项目-前端
chenyao
2026-01-08 8f354d0f7974ba99c6f53240e7393c235d042c78
Merge remote-tracking branch 'origin/master'
9 files modified
8 files added
1 files deleted
753 ■■■■■ changed files
applications/drone-command/src/assets/images/dataCockpit/equipment.png patch | view | raw | blame | history
applications/drone-command/src/assets/images/dataCockpit/warning.png patch | view | raw | blame | history
applications/drone-command/src/assets/images/topContainer/top-bg.png patch | view | raw | blame | history
applications/drone-command/src/components/basic-container/main.vue 4 ●●● patch | view | raw | blame | history
applications/drone-command/src/page/index/top/index.vue 4 ●●● patch | view | raw | blame | history
applications/drone-command/src/store/modules/common.js 13 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/styles/common.scss 5 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/styles/sidebar.scss 26 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/styles/top.scss 1 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock.vue 19 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/FormDiaLog.vue 181 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/deviceStock.vue 125 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js 43 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/CenterContainer.vue 199 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/LeftContainer.vue 15 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/MapContainer.vue 32 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/RightContainer.vue 15 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/index.vue 71 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/assets/images/dataCockpit/equipment.png
applications/drone-command/src/assets/images/dataCockpit/warning.png
applications/drone-command/src/assets/images/topContainer/top-bg.png

applications/drone-command/src/components/basic-container/main.vue
@@ -2,7 +2,7 @@
 * @Author       : yuan
 * @Date         : 2025-06-14 15:19:16
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-07 09:28:08
 * @LastEditTime : 2026-01-07 16:26:20
 * @FilePath     : \applications\drone-command\src\components\basic-container\main.vue
 * @Description  : 
 * Copyright 2025 OBKoro1, All Rights Reserved. 
@@ -45,6 +45,8 @@
<style lang="scss" scoped>
.basic-container {
  margin-top: 109px;
  height: 0;
  flex: 1;
applications/drone-command/src/page/index/top/index.vue
@@ -116,7 +116,8 @@
  display: flex;
  justify-content: space-between;
  height: 100%;
  background: url('@/assets/images/topContainer/top-bg.png') center no-repeat !important;
  background: url('@/assets/images/topContainer/top-bg.png') center / 100% 100% no-repeat !important;
  pointer-events: none;
}
.top-bar__left {
@@ -157,6 +158,7 @@
  display: flex !important;
  align-items: center;
  height: 100%;
  pointer-events: auto;
  .icon-box {
    display: flex;
applications/drone-command/src/store/modules/common.js
@@ -23,6 +23,15 @@
    },
    safeHeight: 0, // 获取选择航线高度
    positionsArr: [], // 获取选择航线点数据
    // 地图设置
        mapSetting: {
            mode: 17, // 0为标准地图, 1为卫星地图
            roadLine: true,
            visual: '3D',
            isDark: false,
        },
  },
  mutations: {
    setSafeHeight (state, data) {
@@ -104,6 +113,10 @@
        type: 'session',
      });
    },
        SET_MAP_MODE: (state, mode) => {
            state.mapSetting.mode = mode
        }
  },
};
export default common;
applications/drone-command/src/styles/common.scss
@@ -63,12 +63,12 @@
    top: 0;
    left: 0;
    width: 100%;
    height: 97px;
    height: 109px;
    z-index: 9;
  }
  > .leaf-container {
    padding-top: 97px;
    padding-top: 109px;
    width: 160px;
    height: 100%;
@@ -125,7 +125,6 @@
}
.main-avue-view-container {
  margin-top: 97px;
  height: 0;
  flex: 1;
  display: flex;
applications/drone-command/src/styles/sidebar.scss
@@ -37,6 +37,29 @@
    background-color: rgba(0, 0, 0, .4);
  }
  .el-menu-item {
    padding-left: 41.4px !important;
  }
  .el-sub-menu {
    .el-sub-menu__title {
      padding-left: 41.4px !important;
    }
    .el-menu-item {
      padding-left: 54px !important;
    }
    &.is-active {
      .el-sub-menu__title {
        i, span {
          color: #fff;
        }
      }
    }
  }
  .el-menu-item, .el-sub-menu__title {
    i {
      margin-right: 5px;
@@ -60,9 +83,10 @@
        top: 0;
        left: 0;
        bottom: 0;
        width: 4px;
        width: 2px;
        background: #0041FF;
        position: absolute;
        border-right: 1px solid #1D56FF;
      }
      background-color: rgba(0, 0, 0, .8);
applications/drone-command/src/styles/top.scss
@@ -1,7 +1,6 @@
.avue-top {
  position: relative;
  background-color: #fff;
  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.15);
  color: rgba(0, 0, 0, .65);
  font-size: 28px;
  height: $top_height;
applications/drone-command/src/views/basicManage/deviceStock.vue
File was deleted
applications/drone-command/src/views/basicManage/deviceStock/FormDiaLog.vue
New file
@@ -0,0 +1,181 @@
<template>
    <el-dialog v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close>
        <el-form ref="formRef" :model="formData" :rules="rules" :disabled="dialogReadonly" 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-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="设备类型" prop="deviceType">
                        <el-input v-model="formData.deviceType" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
            </el-row>
            <el-row>
                <el-col :span="12">
                    <el-form-item label="型号" prop="deviceModel">
                        <el-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-form-item>
                </el-col>
            </el-row>
            <el-row>
                <el-col :span="12">
                    <el-form-item label="生产厂商" prop="manufacturer">
                        <el-input v-model="formData.manufacturer" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="出库去向" prop="stockOutDestination">
                        <el-input v-model="formData.stockOutDestination" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
            </el-row>
            <el-row>
                <el-col :span="12">
                    <el-form-item label="来源" prop="source">
                        <el-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-form-item>
                </el-col>
            </el-row>
            <el-row>
                <el-col :span="12">
                    <el-form-item label="所属部门" prop="ownerDept">
                        <el-input v-model="formData.ownerDept" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="负责人" prop="ownerUser">
                        <el-input v-model="formData.ownerUser" 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>
        </template>
    </el-dialog>
</template>
<script setup>
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { fwDeviceDetailApi, fwDeviceSaveApi, fwDeviceUpdateApi } from '@/views/basicManage/deviceStock/fwDevice'
const initForm = () => ({
    // 表单默认值
    deviceModel: '', // 型号
    deviceName: '', // 设备名称
    deviceSpecification: '', // 规格
    deviceType: '', // 设备类型
    manufacturer: '', // 生产厂商
    ownerDept: '', // 所属部门
    ownerUser: '', // 负责人
    purpose: '', // 用途
    source: '', // 来源
    stockOutDestination: '', // 出库去向
})
const emit = defineEmits(['success'])
const formRef = ref(null) // 表单实例
const formData = ref(initForm()) // 表单数据
const visible = ref(false) // 弹框显隐
const dialogMode = ref('add') // 弹框模式
const submitting = ref(false)
const dialogReadonly = computed(() => dialogMode.value === 'view')
const dialogTitle = computed(() => {
    if (dialogMode.value === 'edit') {
        return '编辑'
    } else if (dialogMode.value === 'view') {
        return '查看'
    } else {
        return '新增'
    }
})
function verify(required, max, messageType = 1) {
    return [
        required ? { required: true, message: messageType ? '请输入' : '请选择', trigger: ['blur', 'change'] } : {},
        { max, message: `长度不超过${max}`, trigger: ['blur', 'change'] },
    ]
}
const rules = {
    deviceName: verify(true, 50),
    deviceType: verify(true, 50),
    deviceModel: verify(true, 50),
    deviceSpecification: verify(true, 50),
    manufacturer: verify(true, 50),
    stockOutDestination: verify(true, 50),
    source: verify(true, 50),
    purpose: verify(true, 50),
    ownerDept: verify(true, 50),
    ownerUser: verify(true, 50),
}
// 关闭弹框
function handleCancel() {
    visible.value = false
}
// 提交新增/编辑
async function handleSubmit() {
    const isValid = await formRef.value?.validate().catch(() => false)
    if (!isValid) return
    submitting.value = true
    try {
        dialogMode.value === 'add' ? await fwDeviceSaveApi(formData.value) : await fwDeviceUpdateApi(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 fwDeviceDetailApi({ id: formData.value.id })
    formData.value = res?.data?.data ?? {}
}
// 关闭后重置
function handleClosed() {
    formData.value = initForm()
}
// 打开弹框
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'add'
    formData.value = row
    visible.value = true
    if (dialogMode.value === 'add') {
        formData.value = initForm()
    } else {
        await loadDetail()
    }
}
defineExpose({ open })
</script>
applications/drone-command/src/views/basicManage/deviceStock/deviceStock.vue
New file
@@ -0,0 +1,125 @@
<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="getList" />
                    </el-form-item>
                </el-col>
                <el-col :span="4">
                    <el-form-item label="类型" prop="deviceType">
                        <el-input v-model="searchParams.deviceType" placeholder="请输入" clearable @clear="getList" />
                    </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>
        <div>
            <el-button type="primary" @click="handleAdd">新增</el-button>
        </div>
        <el-table v-loading="loading" :data="list">
            <el-table-column type="index" width="60" label="序号" />
            <el-table-column prop="deviceName" label="名称" />
            <el-table-column prop="deviceType" label="类型" />
            <el-table-column prop="deviceModel" label="设备型号" />
            <el-table-column prop="deviceSpecification" label="规格" />
            <el-table-column label="操作">
                <template v-slot="{ row }">
                    <el-link @click="handleView(row)" type="primary">查看</el-link>
                    <el-link @click="handleEdit(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>
        <FormDiaLog ref="dialogRef" @success="getList" />
    </basic-container>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { ElMessageBox } from 'element-plus'
import { fwDeviceListApi, fwDeviceRemoveApi } from '@/views/basicManage/deviceStock/fwDevice'
import FormDiaLog from './FormDiaLog.vue'
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) // 弹框实例
// 获取列表
async function getList() {
    loading.value = true
    try {
        const res = await fwDeviceListApi(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 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 fwDeviceRemoveApi({ ids: [row.id] })
    getList()
}
// 新增
function handleAdd() {
    dialogRef.value?.open({ mode: 'add' })
}
onMounted(() => {
    getList()
})
</script>
<style scoped lang="scss"></style>
applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js
New file
@@ -0,0 +1,43 @@
import request from '@/axios'
// 查
export const fwDeviceListApi = params => {
    return request({
        url: `/drone-fw/fwDevice/fwDevice/list`,
        method: 'get',
        params,
    })
}
// 增
export const fwDeviceSaveApi = data => {
    return request({
        url: `/drone-fw/fwDevice/fwDevice/save`,
        method: 'post',
        data,
    })
}
// 改
export const fwDeviceUpdateApi = data => {
    return request({
        url: `/drone-fw/fwDevice/fwDevice/update`,
        method: 'post',
        data,
    })
}
//删除
export const fwDeviceRemoveApi = data => {
    return request({
        url: `/drone-fw/fwDevice/fwDevice/remove`,
        method: 'post',
        data,
    })
}
//详情
export const fwDeviceDetailApi = params => {
    return request({
        url: `/drone-fw/fwDevice/fwDevice/detail`,
        method: 'get',
        params,
    })
}
applications/drone-command/src/views/dataCockpit/components/CenterContainer.vue
New file
@@ -0,0 +1,199 @@
<template>
    <div class="center-container">
        <div class="left-box">
            <div class="logo">
                <img :src="warningLogo" alt="">
            </div>
            <div class="list">
                <div class="item" v-for="item, ind in warningList" :key="ind">
                    <div class="val" @click="warningTypeClick(item.type)">
                        {{ item.value }}
                    </div>
                    <div class="name">
                        {{ item.title }}
                    </div>
                </div>
            </div>
        </div>
        <div class="right-box">
            <div class="logo">
                <img :src="equipmentLogo" alt="">
            </div>
            <div class="list">
                <div class="item" v-for="item, ind in equipmentList" :key="ind">
                    <div class="val" :class="{highlight: item.isHighlight}" @click="equipmentClick(item.isHighlight)">
                        {{ item.value }}
                    </div>
                    <div class="name">
                        {{ item.title }}
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>
<script setup>
import warningLogo from '@/assets/images/dataCockpit/warning.png'
import equipmentLogo from '@/assets/images/dataCockpit/equipment.png'
let warningList = ref([
    {
        title: '实时告警',
        value: 56,
        type: 1
    },
    {
        title: '设备告警',
        value: 127,
        type: 2
    },
    {
        title: '历史告警',
        value: 619,
        type: 3
    },
])
let equipmentList = ref([
    {
        title: '设备统计',
        value: 18,
        isHighlight: true,
    },
    {
        title: '无线电',
        value: 9,
    },
    {
        title: '光电',
        value: 6,
    },
    {
        title: '雷达',
        value: 3,
    }
])
const warningTypeClick = (type) => {
    console.log('点击了告警类型:', type);
}
const equipmentClick = (isHighlight) => {
    if (isHighlight) {
        console.log('点击了设备统计');
    }
}
</script>
<style lang="scss" scoped>
.center-container {
    display: flex;
    position: absolute;
    top: 75px;
    left: 465px;
    z-index: 9;
    pointer-events: none;
    .left-box {
        width: 356px;
        .list {
            .item {
                .val {
                    color: #FF4444;
                    cursor: pointer;
                }
            }
        }
    }
    .right-box {
        margin-left: 20px;
        width: 455px;
        .list {
            .item {
                .val.highlight {
                    color: #6382FF;
                    cursor: pointer;
                }
            }
        }
    }
    .left-box,
    .right-box {
        padding: 17px 20px;
        display: flex;
        align-items: center;
        height: 100px;
        background: #05050F;
        box-sizing: border-box;
        border-radius: 16px 16px 16px 16px;
        pointer-events: auto;
        .logo {
            img {
                width: 40px;
                height: 40px;
            }
        }
        .list {
            width: 0;
            flex: 1;
            height: 100%;
            display: flex;
            .item {
                width: 0;
                flex: 1;
                position: relative;
                padding-left: 20px;
                color: #fff;
                .name {
                    font-family: Open Sans, Open Sans;
                    font-weight: 400;
                    font-size: 14px;
                    color: #FFFFFF;
                    text-align: left;
                    font-style: normal;
                    text-transform: none;
                }
                .val {
                    font-family: Source Han Sans CN, Source Han Sans CN;
                    font-weight: bold;
                    font-size: 30px;
                    text-align: left;
                    font-style: normal;
                    text-transform: none;
                }
                &::after {
                    content: '';
                    position: absolute;
                    top: 0;
                    right: 0;
                    width: 1px;
                    height: 100%;
                    width: 1px;
                    background: #333355;
                }
                &:last-child::after {
                    display: none;
                }
            }
        }
    }
}
</style>
applications/drone-command/src/views/dataCockpit/components/LeftContainer.vue
New file
@@ -0,0 +1,15 @@
<template>
    <div class="left-container">
        <div class="wrapper">
        </div>
    </div>
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>
applications/drone-command/src/views/dataCockpit/components/MapContainer.vue
@@ -1,13 +1,41 @@
<template>
    <div>
    <div class="ztzf-cesium map-container" id="cesium">
    </div>
</template>
<script setup>
import { PublicCesium } from '@/utils/cesium/publicCesium'
let viewInstance = null
let viewer = null
onMounted(() => {
    viewInstance = new PublicCesium({
        dom: 'cesium',
        flatMode: false,
        terrain: false,
        layerMode: 4,
        contour: false,
    })
    viewer = viewInstance.getViewer()
})
onBeforeUnmount(() => {
    if (viewInstance) {
        viewInstance.destroy()
        viewInstance = null
    }
    viewer = null
})
</script>
<style lang="scss" scoped>
.map-container {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}
</style>
applications/drone-command/src/views/dataCockpit/components/RightContainer.vue
New file
@@ -0,0 +1,15 @@
<template>
    <div class="right-container">
        <div class="wrapper">
        </div>
    </div>
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>
applications/drone-command/src/views/dataCockpit/index.vue
@@ -1,9 +1,74 @@
<!--
 * @Author       : yuan
 * @Date         : 2026-01-06 16:35:50
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-07 17:33:02
 * @FilePath     : \applications\drone-command\src\views\dataCockpit\index.vue
 * @Description  :
 * Copyright 2026 OBKoro1, All Rights Reserved.
 * 2026-01-06 16:35:50
-->
<template>
  <basic-container>
    维护记录管理
  </basic-container>
  <div class="page-container">
    <MapContainer />
    <LeftContainer class="left-container" />
    <RightContainer class="right-container" />
    <CenterContainer />
  </div>
</template>
<script setup>
  import MapContainer from './components/MapContainer.vue';
  import LeftContainer from './components/LeftContainer.vue';
  import RightContainer from './components/RightContainer.vue';
  import CenterContainer from './components/CenterContainer.vue';
</script>
<style scoped lang="scss">
  .page-container {
    position: relative;
    width: 100%;
    height: 100%;
  }
  .left-container,
  .right-container {
    position: absolute;
    top: 96px;
    bottom: 0;
    width: 401px;
    z-index: 9;
    backdrop-filter: blur(5px);
    ::v-deep(.wrapper) {
      position: absolute;
      top: 17px;
      bottom: 20px;
      padding: 20px;
      width: 300px;
      background: rgba(0,0,0,0.3);
      border-radius: 10px 10px 10px 10px;
    }
  }
  .left-container {
    top: 96px;
    left: 0;
    background: linear-gradient( 270deg, rgba(17,23,34,0) 0%, rgba(17,23,34,0.56) 50%, rgba(17,23,34,0.96) 100%);
    ::v-deep(.wrapper) {
      left: 17px;
    }
  }
  .right-container {
    top: 54px;
    right: 0;
    background: linear-gradient( 270deg, rgba(17,23,34,0.96) 0.16%, rgba(17,23,34,0.56) 57.57%, rgba(4,30,37,0) 100%);
    ::v-deep(.wrapper) {
      top: 21px;
      right: 17px;
    }
  }
</style>