无人机管理后台前端(已迁走)
shuishen
2025-09-27 db3e35114ed8d1318be257324747cd9f486038ac
Merge branch 'feature/v6.0/6.0.4' of http://139.196.74.78:10010/r/drone/drone-web-manage into feature/v6.0/6.0.4
9 files modified
1 files renamed
1 files added
257 ■■■■■ changed files
src/App.vue 3 ●●●● patch | view | raw | blame | history
src/api/resource/wayline.js 8 ●●●● patch | view | raw | blame | history
src/hooks/useRouteLine/useRouteLine.js 5 ●●●●● patch | view | raw | blame | history
src/page/index/GlobalWS.vue 11 ●●●●● patch | view | raw | blame | history
src/page/index/index.vue 39 ●●●● patch | view | raw | blame | history
src/page/index/useGlobalWS.js 45 ●●●● patch | view | raw | blame | history
src/store/modules/common.js 14 ●●●●● patch | view | raw | blame | history
src/views/dataCenter/components/searchData.vue 2 ●●● patch | view | raw | blame | history
src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue 11 ●●●● patch | view | raw | blame | history
src/views/monitor/log/flightLog.vue 11 ●●●● patch | view | raw | blame | history
src/views/tickets/orderLog.vue 108 ●●●●● patch | view | raw | blame | history
src/App.vue
@@ -3,9 +3,8 @@
</template>
<script setup>
import { useDownloadWs } from '@/page/index/useDownloadWs';
useDownloadWs()
</script>
<style>
src/api/resource/wayline.js
@@ -17,7 +17,13 @@
  });
};
export const newGetWorkspacesPage = params => {
  return request({
    url: `/drone-device-core/wayline/api/v1/wayline-groups/page`,
    method: 'get',
    params,
  })
}
export const getWaylineFileListByArea = (areaCode) => {
  return request({
src/hooks/useRouteLine/useRouteLine.js
@@ -13,6 +13,8 @@
import { ArrowLineMaterialProperty } from '@/utils/cesium/Material'
import { flyVisual } from '@/utils/cesium/mapUtil'
import { useStore } from 'vuex'
let arrowLineMaterialProperty = new ArrowLineMaterialProperty({
    color: new Cesium.Color(128 / 255, 215 / 255, 255 / 255, 1),
    directionColor: new Cesium.Color(1, 1, 1, 1),
@@ -22,6 +24,7 @@
})
export function useRouteLine () {
    const store = useStore()
    let viewer = null
    let previewDataSource = null
@@ -191,6 +194,8 @@
                height: 50,
            },
        })
        // 获取最后一点高度
        store.commit('setLastHeight', Number(pointList[pointList.length - 1].height))
        flyVisual({
            positionsData:pointList.map(i => [Number(i.longitude), Number(i.latitude), Number(i.height)]),
            viewer,
src/page/index/GlobalWS.vue
New file
@@ -0,0 +1,11 @@
<template>
</template>
<script setup>
import { useGlobalWS } from '@/page/index/useGlobalWS';
useGlobalWS()
</script>
<style scoped lang="scss">
</style>
src/page/index/index.vue
@@ -24,23 +24,26 @@
        </div>
      </div>
    </div>
    <GlobalWS />
    <!-- <wechat></wechat> -->
  </div>
</template>
<script>
import index from '@/mixins/index'
import wechat from './wechat.vue'
import index from '@/mixins/index';
import wechat from './wechat.vue';
//import { validatenull } from 'utils/validate';
import { mapGetters } from 'vuex'
import tags from './tags.vue'
import search from './search.vue'
import logo from './logo.vue'
import top from './top/index.vue'
import sidebar from './sidebar/index.vue'
import { mapGetters } from 'vuex';
import tags from './tags.vue';
import search from './search.vue';
import logo from './logo.vue';
import top from './top/index.vue';
import sidebar from './sidebar/index.vue';
import GlobalWS from '@/page/index/GlobalWS.vue';
export default {
  mixins: [index],
  components: {
    GlobalWS,
    top,
    logo,
    tags,
@@ -52,7 +55,7 @@
  provide () {
    return {
      index: this,
    }
    };
  },
  computed: {
    ...mapGetters([
@@ -67,7 +70,7 @@
    validSidebar () {
      return !(
        (this.$route.meta || {}).menu === false || (this.$route.query || {}).menu === 'false'
      )
      );
    },
  },
  props: [],
@@ -76,15 +79,15 @@
    openMenu (item = {}) {
      this.$store.dispatch('GetMenu', item.id).then(data => {
        if (data.length !== 0) {
          this.$router.$avueRouter.formatRoutes(data, true)
          this.$router.$avueRouter.formatRoutes(data, true);
          // 获取url里面的redirect
          const redirect = decodeURIComponent(this.$route.query.redirect || '')
          const redirect = decodeURIComponent(this.$route.query.redirect || '');
          if (redirect) {
            console.log('redirect', redirect)
            const [path, queryString] = redirect.split('?')
            const query = queryString ? Object.fromEntries(new URLSearchParams(queryString)) : {}
            this.$router.push({ path, query })
            console.log('redirect', redirect);
            const [path, queryString] = redirect.split('?');
            const query = queryString ? Object.fromEntries(new URLSearchParams(queryString)) : {};
            this.$router.push({ path, query });
          }
        }
        //当点击顶部菜单后默认打开第一个菜单
@@ -108,10 +111,10 @@
            }, itemActive.meta)
          });
        }*/
      })
      });
    },
  },
}
};
</script>
<style lang="scss" scoped>
src/page/index/useGlobalWS.js
File was renamed from src/page/index/useDownloadWs.js
@@ -1,20 +1,21 @@
import { computed, onBeforeUnmount, onMounted } from 'vue';
import { getWebsocketUrl } from '@/utils/websocket/config'
import { useConnectWebSocket } from '@/utils/websocket/connect-websocket'
import { useStore } from 'vuex'
import dayjs from 'dayjs'
import { aLinkDownloadUtil } from '@/utils/util'
import EventBus from '@/utils/eventBus'
import { computed, onBeforeUnmount, onMounted } from 'vue';
import { setStore,getStore } from '@/utils/store';
import { getDownloadStatusApi } from '@/api/dataCenter/dataCenter';
import { getStore, setStore } from '@/utils/store'
export const useDownloadWs = () => {
export const useGlobalWS = () => {
    const store = useStore()
    const loginUserInfo = computed(() => store.state.user.userInfo)
    const downloadProgress = computed(() => store.state.common.downloadProgress)
    let currentWebSocket = null
    function messageHandler(payload) {
        const {type,status,progress,download_url} = payload
    // 下载进度处理
    function downloadProcessing(payload) {
        const {type,status,progress,download_url} = payload?.data || {}
        let setProgress = 0
        if (status === 'PENDING'){
            setProgress = 1
@@ -29,7 +30,7 @@
            ...downloadProgress.value,
            [type]: setProgress,
        })
        if (status === 'COMPLETED' && ['htsjzx','htlsrwxq'].includes(type)) {
        if (status === 'COMPLETED' && ['dpsjzx','dplsrwxq'].includes(type)) {
            const name = `数据中心-${dayjs().format('YYYYMMDDHHmmss')}.zip`
            const currentUrl = getStore({name: 'downloadUrl'})
            if (currentUrl === download_url && download_url!== undefined) return
@@ -38,18 +39,42 @@
        }
    }
    const logOut = () => {
        store.commit('SET_THEME_NAME', '')
        store.dispatch('LogOut').then(() => {
            router.push({ path: '/login' })
            setTimeout(() => location.reload())
        })
    }
    function messageHandler(payload) {
        switch (payload.biz_code) {
            case 'JOB_ISREFRESH':
                store.commit('jobUpdateKeyAdd')
                break
            case 'DEVICE_ISREFRESH':
                store.commit('deviceUpdateKeyAdd')
                break
            case 'DOWNLOAD_PROGRESS':
                downloadProcessing(payload)
                break
            case 'LOGOUT_USER':
                logOut()
                break
        }
    }
    onMounted(() => {
        let webSocketUrl = getWebsocketUrl() + `&model_type=3&workspace-id=${loginUserInfo.value.user_id}`
        currentWebSocket = useConnectWebSocket((result)=>{
            const payload = JSON.parse(result)
            messageHandler(payload.data)
            messageHandler(payload)
        }, webSocketUrl)
        EventBus.on('useDownloadWs-messageHandler',messageHandler)
        EventBus.on('useGlobalWS-messageHandler',messageHandler)
    })
    onBeforeUnmount(() => {
        currentWebSocket?.close()
        EventBus.off('useDownloadWs-messageHandler',messageHandler)
        EventBus.off('useGlobalWS-messageHandler',messageHandler)
    })
    return {}
}
src/store/modules/common.js
@@ -15,15 +15,27 @@
    lockPasswd: getStore({ name: 'lockPasswd' }) || '',
    website: website,
    setting: website.setting,
    deviceUpdateKey: 0, //设备刷新key
    jobUpdateKey: 0, //任务刷新key
    downloadProgress:{
      htsjzx: 100, //数据中心
      htlsrwxq: 100, //历史任务详情
    }
    },
    lastHeight: 0, // 获取最后一点高度
  },
  mutations: {
    setLastHeight(state, data) {
      state.lastHeight = data
    },
    setDownloadProgress(state, data) {
      state.downloadProgress = data
    },
    deviceUpdateKeyAdd(state, data) {
      state.deviceUpdateKey = state.deviceUpdateKey + 1
    },
    jobUpdateKeyAdd(state, data) {
      state.jobUpdateKey = state.jobUpdateKey + 1
    },
    SET_LANGUAGE: (state, language) => {
      state.language = language;
      setStore({
src/views/dataCenter/components/searchData.vue
@@ -361,7 +361,7 @@
  requestDockInfo();
  getDownloadStatusApi({type: 'htsjzx'}).then(res =>{
    if (!['CANCELLED','COMPLETED'].includes(res.data.data?.status || 'COMPLETED')){
      EventBus.emit('useDownloadWs-messageHandler',res.data.data)
      EventBus.emit('useGlobalWS-messageHandler',res.data.data)
    }
  })
});
src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue
@@ -264,14 +264,13 @@
const changeKey = inject('changeKey')
// 轮询查询是否刷新
const polling = setInterval(async () => {
  const res = await statusChangedApi()
  if (res.data.data.DEVICE_REFRESH || res.data.data.JOB_REFRESH) {
const jobUpdateKey = computed(() => store.state.common.jobUpdateKey)
const deviceUpdateKey = computed(() => store.state.common.deviceUpdateKey)
watch([jobUpdateKey,deviceUpdateKey],()=>{
    getJobList()
    changeKey.value++
  }
}, 4000)
})
onBeforeUnmount(() => {
  clearInterval(polling)
src/views/monitor/log/flightLog.vue
@@ -4,7 +4,7 @@
    <div class="ztzf-search-mange">
      <el-form :model="params" inline>
        <div class="search-contain">
          <div>
          <!--          <div>-->
            <el-form-item label="任务名称:">
              <el-input v-model="params.jobName" placeholder="请输入任务名称" clearable />
            </el-form-item>
@@ -22,11 +22,11 @@
                @change="changeselect"
              />
            </el-form-item>
          </div>
          <div class="btns">
          <!--          </div>-->
          <!--          <div class="btns">-->
            <el-button type="primary" @click="getList" :icon="Search">搜索</el-button>
            <el-button @click="cancelSearch" :icon="Delete">清空</el-button>
          </div>
          <!--          </div>-->
        </div>
      </el-form>
    </div>
@@ -307,7 +307,8 @@
        Number(i.height)
      ]),
      viewer,
      multiple: 10
      multiple: 4.6,
      pitch: -60
    });
  } else {
    console.warn('No valid positions data');
src/views/tickets/orderLog.vue
@@ -281,7 +281,7 @@
              ></el-input>
            </el-form-item>
          </el-col>
          <el-col :span="12">
          <el-col :span="6">
            <el-form-item label="关联航线" prop="file_id">
              <el-select
                v-model="form.file_id"
@@ -298,15 +298,15 @@
              </el-select>
            </el-form-item>
          </el-col>
<!--          <el-col :span="6">-->
<!--            <el-form-item label="安全返航真高" prop="rth_altitude">-->
<!--              <el-input-number-->
<!--                v-model="form.rth_altitude"-->
<!--                :min="30"-->
<!--                :max="300"-->
<!--              ></el-input-number>-->
<!--            </el-form-item>-->
<!--          </el-col>-->
          <el-col :span="6">
            <el-form-item label="安全返航真高" prop="rth_altitude">
              <el-input-number
                v-model="form.rth_altitude"
                :min="50"
                :max="300"
              ></el-input-number>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
@@ -848,7 +848,7 @@
} from '@/api/tickets/orderLog';
import { getTicketInfo } from '@/api/tickets/ticket';
import { getDictionaryByCode } from '@/api/system/dictbiz';
import { getWaylineFileListByArea } from '@/api/resource/wayline';
import { getWaylineFileListByArea, newGetWorkspacesPage } from '@/api/resource/wayline';
import { export_json_to_excel } from '@/utils/exportExcel';
import { getFlyingNestBy } from '@/api/device/device';
import { mapGetters } from 'vuex';
@@ -861,9 +861,13 @@
import weekday from 'dayjs/plugin/weekday';
import elTooltipCopy from '@/components/ElTooltipCopy.vue';
import { CircleClose, Promotion, Select } from '@element-plus/icons-vue';
import { useStore } from 'vuex';
import _ from 'lodash'
// const store = useStore()
dayjs.extend(weekday);
dayjs.locale('zh-cn');
// const lastHeight = computed(() => store.state.common.lastHeight)
export default {
  components: { elTooltipCopy },
@@ -871,7 +875,7 @@
  data() {
    return {
      activeTab: 'all',
      getlastHeight: 0,
      tabs: [
        { label: '全部工单', name: 'all', value: null, count: 0 },
        { label: '待审核', name: 'WAIT_AUDIT', value: 1, count: 0 },
@@ -913,7 +917,7 @@
        indexLabel: '序号',
        indexFixed: true,
        align: 'center',
        align: 'left',
        border: true,
        stripe: false,
        searchShow: true,
@@ -1095,6 +1099,10 @@
      return CircleClose
    },
    ...mapGetters(['userInfo', 'permission']),
    lastHeight() {
      const height = this.$store.state.common.lastHeight;
      return Number(height) || 0; // 确保是数字类型
    },
    filteredTabs() {
      // rejection_and_draft 权限控制“已驳回”和“草稿”tab
      // console.log(this.permission, '权限信息')
@@ -1269,33 +1277,23 @@
              return this.$message.warning('任务时间不能小于当前时间');
            }
          }
          const maxItem = this.device_sns.reduce((max, item) => {
            return item.drone_height > max.drone_height ? item : max;
          });
          console.log(maxItem, '高度')
          let result = _.round((maxItem.drone_height + this.form.rth_altitude), 2)
          if (this.lastHeight > result) {
            this.$confirm(`安全返航真高${result}米低于航线返航点高度${this.lastHeight}米,为确保返航路径安全,建议您进行调整后再发布`, '提示', {
              confirmButtonText: '确定',
              cancelButtonText: '取消',
              type: 'warning'
            }).then(async () => {
          const submitData = {
            ...this.form,
            status: status,
            ai_types: this.form.ai_types?.length ? this.form.ai_types : [],
          };
          // 判断该值this.form.date_range[0] 周几 和rep_fre_type 是不是相等
          // if (this.form.deal_time) {
          //    const weekday = dayjs(this.form.date_range[0]).format('dddd');
          //    console.log('444', weekday)
          //   //  form.rep_fre_type
          //   const now = new Date()
          //   const today = now.toDateString()
          //   const selectedDate = new Date(this.form.date_range[0]).toDateString()
          //   if (today === selectedDate) {
          //     const [hours, minutes] = this.form.deal_time.split(':')
          //     const selectedTime = new Date()
          //     selectedTime.setHours(parseInt(hours), parseInt(minutes))
          //     if (selectedTime < now) {
          //       return this.$message.warning('任务时间不能小于当前时间')
          //     }
          //   }
          // }
          await saveUpdateOrderLog(submitData);
          let id = this.form.id;
          if (id) {
@@ -1306,6 +1304,26 @@
          this.dialogVisible = false;
          this.detailVisible = false;
          (this.device_sns = []), (this.wayLineList = []), this.fetchTableData();
            }).catch(() => {
            });
          } else {
            const submitData = {
              ...this.form,
              status: status,
              ai_types: this.form.ai_types?.length ? this.form.ai_types : [],
            };
            await saveUpdateOrderLog(submitData);
            let id = this.form.id;
            if (id) {
              this.$message.success('工单发布成功');
            } else {
              this.$message.success('工单创建成功');
            }
            this.dialogVisible = false;
            this.detailVisible = false;
            (this.device_sns = []), (this.wayLineList = []), this.fetchTableData();
          }
        }
      });
    },
@@ -1411,6 +1429,7 @@
    handleAdd() {
      this.form = {};
      this.form.rth_altitude = 120;
      this.dialogVisible = true;
      this.$nextTick(() => {
@@ -1573,8 +1592,15 @@
    },
    //获取航线列表
    async asyncgetWaylineFileListByArea(name) {
      var wayLineListResponse = await getWaylineFileListByArea(this.userInfo.detail.areaCode);
      this.wayLineList = wayLineListResponse.data.data;
      const formData = {
        name: '',
        waylineType: undefined,
        current: 1,
        size: 99999999,
        descs: 'update_time',
      }
      var wayLineListResponse = await newGetWorkspacesPage(formData);
      this.wayLineList =  wayLineListResponse.data.data.records
      this.initMapLine();
    },
@@ -1608,10 +1634,9 @@
      this.initMapLine({}, async polygon => {
        const currentLine = that.wayLineList.find(item => item.wayline_id === waylineId);
        //按照航线来
        const params = {
          type: [2, 4, 5].includes(currentLine.wayline_type) ? 2 : 0,
          type: ['2', '4', '5'].includes(currentLine.wayline_type) ? 2 : 0,
          wayline_id: waylineId,
          polygon,
        };
@@ -1670,8 +1695,11 @@
      });
    },
  },
  watch: {
    // lastHeight(newVal, oldVal) {
    //   console.log('lastHeight', newVal, oldVal);
    //   this.getlastHeight = newVal;
    // },
    tableData: {
      handler() {
        // this.updateTabCounts()