无人机管理后台前端(已迁走)
罗广辉
2025-10-09 57c1aad776baa26cd534afcddcdc7cee25910880
Merge branch 'feature/v6.0/6.0.4' into feature/v7.0/7.0.1

# Conflicts:
# src/styles/element-ui.scss
# src/views/device/airport.vue
# src/views/tickets/orderLog.vue
# src/views/tickets/ticket.vue
18 files modified
1 files added
253 ■■■■ changed files
src/assets/images/warn.svg 10 ●●●●● patch | view | raw | blame | history
src/option/system/dict.js 2 ●●● patch | view | raw | blame | history
src/styles/element-ui.scss 12 ●●●● patch | view | raw | blame | history
src/views/dataCenter/components/searchData.vue 2 ●●●●● patch | view | raw | blame | history
src/views/device/addDevice.vue 4 ●●●● patch | view | raw | blame | history
src/views/device/airport.vue 58 ●●●● patch | view | raw | blame | history
src/views/device/components/devicePerShare.vue 5 ●●●●● patch | view | raw | blame | history
src/views/device/components/firmwareManage.vue 6 ●●●● patch | view | raw | blame | history
src/views/device/devicePerMenu.vue 2 ●●●●● patch | view | raw | blame | history
src/views/job/components/DeviceJobDetails.vue 5 ●●●● patch | view | raw | blame | history
src/views/job/components/SearchBox.vue 6 ●●●● patch | view | raw | blame | history
src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue 2 ●●● patch | view | raw | blame | history
src/views/resource/wayline.vue 2 ●●● patch | view | raw | blame | history
src/views/system/dept.vue 50 ●●●● patch | view | raw | blame | history
src/views/system/helpCenter.vue 68 ●●●●● patch | view | raw | blame | history
src/views/system/user.vue 4 ●●●● patch | view | raw | blame | history
src/views/tickets/orderLog.vue 4 ●●● patch | view | raw | blame | history
src/views/tickets/ticket.vue 2 ●●● patch | view | raw | blame | history
src/views/wel/components/backlog.vue 9 ●●●●● patch | view | raw | blame | history
src/assets/images/warn.svg
New file
@@ -0,0 +1,10 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame" clip-path="url(#clip0_2023_1729)">
<path id="Vector" d="M10.9609 1.95508C5.96561 1.95508 1.91602 6.00424 1.91602 11C1.91602 15.9958 5.96561 20.0449 10.9609 20.0449C15.9563 20.0449 20.0059 15.9949 20.0059 11C20.0059 6.0051 15.9563 1.95508 10.9609 1.95508ZM10.9609 15.843C10.7528 15.8426 10.5495 15.7805 10.3767 15.6645C10.2038 15.5486 10.0692 15.3841 9.98987 15.1917C9.91052 14.9993 9.88998 14.7877 9.93084 14.5836C9.9717 14.3795 10.0721 14.1921 10.2194 14.0451C10.3668 13.8981 10.5543 13.7981 10.7585 13.7576C10.9626 13.7172 11.1742 13.7381 11.3664 13.8179C11.5587 13.8976 11.723 14.0326 11.8386 14.2056C11.9541 14.3787 12.0158 14.5822 12.0158 14.7903C12.0158 14.9287 11.9885 15.0658 11.9355 15.1936C11.8824 15.3215 11.8047 15.4376 11.7067 15.5354C11.6087 15.6332 11.4924 15.7107 11.3645 15.7635C11.2365 15.8163 11.0994 15.8433 10.9609 15.843ZM12.0158 11.2836C12.0119 11.5607 11.899 11.8252 11.7016 12.0198C11.5042 12.2143 11.2382 12.3234 10.961 12.3234C10.6839 12.3234 10.4179 12.2143 10.2205 12.0198C10.0231 11.8252 9.91022 11.5607 9.90627 11.2836V6.37313C9.91022 6.09599 10.0231 5.83154 10.2205 5.63696C10.4179 5.44238 10.6839 5.3333 10.961 5.3333C11.2382 5.3333 11.5042 5.44238 11.7016 5.63696C11.899 5.83154 12.0119 6.09599 12.0158 6.37313V11.2836Z" fill="#FF0000"/>
</g>
<defs>
<clipPath id="clip0_2023_1729">
<rect width="22" height="22" fill="white"/>
</clipPath>
</defs>
</svg>
src/option/system/dict.js
@@ -63,7 +63,7 @@
      label: '封存',
      prop: 'isSealed',
      type: 'switch',
      align: 'center',
      // align: 'center',
      width: 100,
      dicData: [
        {
src/styles/element-ui.scss
@@ -98,7 +98,7 @@
      .el-dialog__headerbtn {
        position: absolute;
        left: 0;
        top: 14px;
        //top: 14px;
        .el-dialog__close {
          color: #fff;
@@ -111,10 +111,13 @@
}
.ztzf-drawer-body-basic-container {
  .el-drawer__header {
    margin-bottom: 0;
  }
  .el-drawer__body {
    display: flex;
    flex-direction: column;
  }
}
@@ -177,6 +180,9 @@
.ztzf-form-mange {
  .el-form-item {
    width: 300px;
    .el-form-item__content {
      width: 100%;
    }
    .el-form-item__label {
      width: 120px;
    }
@@ -228,4 +234,4 @@
    box-sizing: border-box;
    background: #fafafa;
  }
}
}
src/views/dataCenter/components/searchData.vue
@@ -359,6 +359,8 @@
function cancelDownload() {
  cancelDownloadApi({ type:'htsjzx' }).then(res =>{
    ElMessage.success('取消成功')
  }).catch(e =>{
    EventBus.emit('useGlobalWS-messageHandler', {biz_code: 'DOWNLOAD_PROGRESS',data:{status: 'CANCELLED',type:'htsjzx'}})
  })
}
// 切换文件夹
src/views/device/addDevice.vue
@@ -11,7 +11,7 @@
                    @click.stop="copyInfo(scope.row, scope.index)" v-if="userInfo.role_name.includes('admin')">复制
                </el-button>
            </template>
        </avue-crud>F
        </avue-crud>
    </basic-container>
</template>
@@ -217,7 +217,7 @@
                      prop: 'area_code',
                      // type: 'cascader',
                      type: 'tree',
                      labelWidth: 145,
                      labelWidth: 130,
                      searchSpan: 4,
                      hide: true,
                      search: true,
src/views/device/airport.vue
@@ -132,15 +132,16 @@
      <el-input v-model="operate_password" disabled></el-input>
    </el-dialog>
    <el-dialog title="注销" class="zx-cancel" append-to-body v-model="cancenOperate" width="460px">
      <div style="display: flex;justify-content: center;margin-bottom: 10px;">注销
        <span style="color:cornflowerblue;font-weight: bolder;margin: 0px 4px;">
    <el-dialog title="注销" class="ztzf-dialog-mange zx-cancel" append-to-body v-model="cancenOperate" width="560px">
      <div class="txt-contain">
        <img :src="warnSvg" />注销
        <span class="name">
          {{ cancelSNName }}
        </span> 设备可能会影响相关数据,确认注销吗?
      </div>
      <div style="display: flex;justify-content: center;color:red;font-size: 12px;">*删除相关图片、视频、事件</div>
      <div class="delete-txt">*删除相关图片、视频、事件</div>
      <template #footer>
        <span class="dialog-footer" style="display: flex;justify-content: center;">
        <span class="dialog-footer">
          <el-button @click="cancenOperate = false">取 消</el-button>
          <el-button type="primary" @click="cancenOperateDo">确 定</el-button>
        </span>
@@ -231,8 +232,10 @@
import FirmwareManage from './components/firmwareManage.vue'
import DevicePerShare from './components/devicePerShare.vue'
import DockControlPanel from './components/DockControlPanel.vue'
import { getWebsocketUrl } from '@/utils/websocket/config'
import ConnectWebSocket from '@/utils/websocket'
import { getWebsocketUrl } from '@/utils/websocket/config';
import ConnectWebSocket from '@/utils/websocket';
import warnSvg from '@/assets/images/warn.svg'
export default {
  components: {
    FirmwareManage,
@@ -264,6 +267,7 @@
        password: [{ required: true, validator: validatePass, trigger: 'blur' }],
        password2: [{ required: true, validator: validatePass2, trigger: 'blur' }],
      },
      warnSvg: warnSvg,
      cancelSNName: '',
      cancenOperate: false,
      cancenOperateRow: {},
@@ -1564,4 +1568,44 @@
  height: 190px;
  right: 0;
}
:deep(.avue-crud__pagination) {
  position: fixed;
  bottom: 10px;
  right: 30px;
}
.force-center-dialog .el-dialog {
}
.zx-cancel {
  .txt-contain {
    display: flex;
    justify-content: center;
    margin-bottom: 20px;
    font-family: Source Han Sans CN, Source Han Sans CN;
    font-weight: 400;
    font-size: 14px;
    img {
      width: 20px;
      height: 20px;
    }
    .name {
      color: #0E8BFF;
      margin-left: 6px;
      margin-right: 6px;
    }
  }
  .delete-txt {
    font-family: Source Han Sans CN, Source Han Sans CN;
    font-weight: 400;
    display: flex;
    justify-content: center;
    color: #FF0000;
    font-size: 12px;
    margin-bottom: 20px;
  }
}
</style>
src/views/device/components/devicePerShare.vue
@@ -39,6 +39,7 @@
        index: true,
        viewBtn: true,
        addBtn: true,
        editBtn: false,
        selection: true,
        excelBtn: false,
        dialogClickModal: false,
@@ -119,7 +120,7 @@
            editDisplay: false,
            span: 24,
            row: true,
            width: 160,
            width: 200,
            format: 'YYYY-MM-DD HH:mm:ss',
          },
        ],
@@ -134,7 +135,7 @@
        addBtn: this.validData(this.permission.airport_add, true),
        viewBtn: this.validData(this.permission.airport_view, true),
        delBtn: this.validData(this.permission.airport_delete, true),
        editBtn: this.validData(this.permission.airport_edit, true),
        editBtn: this.validData(this.permission.airport_edit, false),
      }
    },
    ids () {
src/views/device/components/firmwareManage.vue
@@ -435,4 +435,8 @@
  /* color: aqua; */
}
</style>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.upload-demo {
  height: 30px;
}
</style>
src/views/device/devicePerMenu.vue
@@ -79,6 +79,8 @@
          },
          {
            label: '绑定菜单',
            searchSpan: 5,
            span: 24,
            prop: 'menu_id',
            type: 'tree',
            dicUrl: '/blade-system/menu/deviceButtons',
src/views/job/components/DeviceJobDetails.vue
@@ -162,6 +162,7 @@
import { droneEventList } from '../const/drc'
import { ElMessage,ElLoading } from 'element-plus'
import { useStore } from 'vuex';
import EventBus from '@/utils/eventBus';
const store = useStore();
const htlsrwxq = computed(() => store.state.common.downloadProgress?.htlsrwxq || 100)
const isShow = defineModel('show')
@@ -350,7 +351,9 @@
function cancelDownload() {
    cancelDownloadApi({ type:'htlsrwxq' }).then(res =>{
        ElMessage.success('取消成功')
    })
    }).catch(e =>{
    EventBus.emit('useGlobalWS-messageHandler', {biz_code: 'DOWNLOAD_PROGRESS',data:{status: 'CANCELLED',type:'htlsrwxq'}})
  })
}
onMounted(() => {
    getDetails()
src/views/job/components/SearchBox.vue
@@ -14,7 +14,8 @@
            check-strictly
            node-key="id"
            :props="treeProps"
            @node-click="handleNodeClick"
            clearable
            @change="handleNodeClick"
          />
        </el-form-item>
        <el-form-item label="所属机巢:">
@@ -61,7 +62,7 @@
        <div class="more" v-if="isExpand" @click="toggleExpand">收起</div>
        <div class="more" v-else @click="toggleExpand">更多</div>
        <div class="search-btn" :style="{ bottom: isExpand ? '5px' : '-3px' }">
          <el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
          <el-button icon="el-icon-refresh" @click="handleReset">清空</el-button>
        </div>
@@ -89,7 +90,6 @@
            collapse-tags
            collapse-tags-tooltip
            clearable
            @node-click="handleNodeClick"
            @check="handleCheck"
            @clear="handleClear"
          />
src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue
@@ -17,7 +17,7 @@
            </el-tooltip-copy>
          </template>
        </el-table-column>
        <el-table-column prop="name" label="任务编号" width="160">
        <el-table-column prop="name" label="任务名称" width="160">
          <template #default="scope">
            <el-tooltip-copy :content="scope.row.name" :showCopyText="true">
              {{scope.row.name}}
src/views/resource/wayline.vue
@@ -47,7 +47,7 @@
        searchMenuSpan: 4,
        border: true,
        index: true,
        selection: true,
        // selection: true,
        dialogClickModal: false,
        menu: false, // 禁用默认操作列
src/views/system/dept.vue
@@ -40,10 +40,12 @@
import { getLazyTree } from '@/api/base/region'
import { mapGetters } from 'vuex'
import website from '@/config/website'
import { getList } from '@/api/device/device';
export default {
  data () {
    return {
      treeResolveMap: new Map(),
      form: {},
      selectionList: [],
      query: {},
@@ -383,6 +385,14 @@
            type: 'success',
            message: '操作成功!',
          })
          this.parentId = ''
          const resolve = this.treeResolveMap.get(row.parentId)
          if (resolve) {
            // 重新加载子节点数据
            getChildLazyTree({ parentId: row.parentId }).then(res => {
              resolve(res.data.data)
            })
          }
          this.onLoad(this.page)
          done(row)
        },
@@ -398,7 +408,7 @@
        loading()
        return
      }
      console.log('--- Start rowUpdate ---')
      // console.log('--- Start rowUpdate ---')
      // 提交时只使用县级代码
      const submitData = {
@@ -406,27 +416,33 @@
        areaCode: row.areaCode.split(',').pop(), // 取最后一个代码(县级)
      }
      console.log('Row data before submit:', row)
      console.log('编辑', row)
      update(submitData)
        .then(
          res => {
            console.log('Update response:', res.data)
            const resolve = this.treeResolveMap.get(row.parentId)
            if (resolve) {
              // 重新加载子节点数据
              getChildLazyTree({ parentId: row.parentId }).then(res => {
                resolve(res.data.data)
              })
            }
            this.onLoad(this.page)
            this.$message({
              type: 'success',
              message: '操作成功!',
            })
            this.onLoad(this.page)
            done(row)
          },
          error => {
            console.log('Update error:', error)
            // console.log('Update error:', error)
            this.loading = false
            loading()
          }
        )
        .catch(error => {
          console.log('Update promise rejected:', error)
          // console.log('Update promise rejected:', error)
          this.loading = false
        })
    },
@@ -440,11 +456,18 @@
          return remove(row.id)
        })
        .then(() => {
          const resolve = this.treeResolveMap.get(row.parentId)
          if (resolve) {
            // 重新加载子节点数据
            getChildLazyTree({ parentId: row.parentId }).then(res => {
              resolve(res.data.data)
            })
          }
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          })
          this.onLoad(this.page)
          done(row)
        })
    },
@@ -546,11 +569,11 @@
    },
    onLoad (page, params = {}) {
      this.loading = true
      console.log('onLoad triggered with page:', page, 'params:', params)
      console.log('Current parentId:', this.parentId, 'query:', this.query)
      // console.log('onLoad triggered with page:', page, 'params:', params)
      // console.log('Current parentId:', this.parentId, 'query:', this.query)
      getLazyList(this.parentId, Object.assign(params, this.query))
        .then(res => {
          console.log('onLoad response:', res.data)
          // console.log('onLoad response:', res.data)
          // 处理 areaCode,确保是单一字符串
          const data = (res.data.data || []).map(item => {
            const fullAreaCode = this.getFullAreaCode(item.areaCode)
@@ -565,16 +588,17 @@
          this.data = data
          this.loading = false
          this.selectionClear()
          console.log('Table data after onLoad:', this.data)
          // console.log('Table data after onLoad:', this.data)
        })
        .catch(error => {
          console.log('onLoad error:', error)
          // console.log('onLoad error:', error)
          this.loading = false
          this.data = this.data // 保留当前数据
          console.log('Table data after onLoad error:', this.data)
          // console.log('Table data after onLoad error:', this.data)
        })
    },
    treeLoad (tree, treeNode, resolve) {
      this.treeResolveMap.set(tree.id, resolve)
      getChildLazyTree({ parentId: tree.id }).then(res => {
        resolve(res.data.data)
      })
src/views/system/helpCenter.vue
@@ -17,22 +17,37 @@
        <el-table-column prop="levelTwoType" label="二级类别" />
        <el-table-column prop="uploadUser" label="上传者" />
        <el-table-column prop="createTime" label="上传时间" />
        <el-table-column label="操作" width="150" align="center">
          <template #default="scope">
            <span class="look" @click="clickDetail(scope.row)" v-if="permission.helpCenterLook"
              >查看</span
            >
            <el-upload
              style="display: inline-block"
              :show-file-list="false"
              :before-upload="file => onUploadFileBefore(file, scope.row)"
              :limit="1"
              accept=".pdf"
            >
              <span class="uploads" v-if="permission.helpCenterUpload">上传</span>
            </el-upload>
          </template>
        </el-table-column>
     <el-table-column label="操作" width="200" align="center">
  <template #default="scope">
    <div class="actionBar" >
      <el-button
        class="look"
        icon="el-icon-view"
        type="text"
        @click="clickDetail(scope.row)"
        v-if="permission.helpCenterLook"
      >
        查看
      </el-button>
      <el-upload
        style="display: inline-block"
        :show-file-list="false"
        :before-upload="file => onUploadFileBefore(file, scope.row)"
        :limit="1"
        accept=".pdf"
      >
        <el-button
          class="uploads"
          icon="el-icon-upload"
          type="text"
          v-if="permission.helpCenterUpload"
        >
          上传
        </el-button>
      </el-upload>
    </div>
  </template>
</el-table-column>
      </el-table>
    </div>
  </basic-container>
@@ -146,18 +161,25 @@
.helpContainer {
  height: 100%;
  .look {
    font-family: Source Han Sans CN, Source Han Sans CN;
    color: #1c5cff;
    // font-family: Source Han Sans CN, Source Han Sans CN;
    // color: #1c5cff;
    cursor: pointer;
    margin-right: 15px;
    font-size: 14px;
    // font-size: 14px;
  }
  .uploads {
    font-family: Source Han Sans CN, Source Han Sans CN;
    font-weight: 400;
    font-size: 14px;
    color: #19876d;
    // font-family: Source Han Sans CN, Source Han Sans CN;
    // font-weight: 400;
    // font-size: 14px;
    // color: #19876d;
    cursor: pointer;
  }
  .actionBar {
  display: flex;
  justify-content: center;
   align-items: center;
    gap: 8px;
  }
  :deep(.custom-header th.el-table__cell) {
  color: rgba(0, 0, 0, 0.85);
src/views/system/user.vue
@@ -519,6 +519,7 @@
            column: [
              {
                label: '到期时间',
                labelWidth: 110,
                prop: 'expireTime',
                type: 'datetime',
                format: 'YYYY-MM-DD HH:mm:ss',
@@ -526,12 +527,14 @@
              },
              {
                label: '次数限制',
                labelWidth: 110,
                prop: 'maxLoginNum',
                type: 'input',
              },
              {
                label: '飞行开始时间',
                labelWidth: 110,
                prop: 'flightStartTime',
                type: 'time',
                format: 'HH:mm',
@@ -546,6 +549,7 @@
              },
              {
                label: '飞行结束时间',
                labelWidth: 110,
                prop: 'flightEndTime',
                type: 'time',
                format: 'HH:mm',
src/views/tickets/orderLog.vue
@@ -579,7 +579,8 @@
          <el-col :span="3">
            <el-time-picker
              style="width: 100px"
            class="timeStyle"
        :style="{width:pxToRem(140)}"
              v-model="form.deal_time"
              prop="deal_time"
              value-format="HH:mm"
@@ -861,6 +862,7 @@
</template>
<script>
import { pxToRem, pxToRemNum } from '@/utils/rem'
import { calculateDefaultRange } from '@/utils/util';
import {
  getList,
src/views/tickets/ticket.vue
@@ -685,7 +685,7 @@
                      :src="getThumbUrl(currentDetail.mediaUrl)"
                      :preview-src-list="[getPreviewUrl(currentDetail.mediaUrl)]"
                      fit="contain"
                      style="pxToRemNum(width: 700px); pxToRemNum(height: 520px); cursor: pointer"
                      style=" cursor: pointer"
                    >
                      <template #placeholder>
                        <div class="image-placeholder">
src/views/wel/components/backlog.vue
@@ -60,7 +60,9 @@
import { onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
const userInfo = computed(() => store.getters.userInfo)
const todos = ref([])
const loading = ref(false)
const router = useRouter()
const store = useStore()
const permission = computed(() => store.getters.permission)
@@ -83,12 +85,11 @@
])
const titleClick = async (item, index) => {
todos.value =[]
  checked.value = item.name
  await getListMatter()
}
const userInfo = computed(() => store.getters.userInfo)
const todos = ref([])
const loading = ref(false)
// * 事件工单中2=待审核,0=待处理,3=处理中
// * 智飞工单中1: 待审核,2=已驳回,3=已通过
const statusMap = {