forked from drone/command-center-dashboard

xiebin
2025-04-21 8fa0d6fdb819ddb15b22250d019ce0a564632959
Merge branch 'test' into prod
18 files modified
1 files added
358 ■■■■■ changed files
src/api/home/index.js 6 ●●●● patch | view | raw | blame | history
src/assets/images/login/bg.png patch | view | raw | blame | history
src/assets/images/login/bg1.png patch | view | raw | blame | history
src/components/CommonWeather.vue 22 ●●●● patch | view | raw | blame | history
src/components/DeviceJobDetails/DeviceJobDetails.vue 14 ●●●●● patch | view | raw | blame | history
src/const/drc.js 7 ●●●●● patch | view | raw | blame | history
src/hooks/useMapAggregation/useMapAggregation.js 21 ●●●● patch | view | raw | blame | history
src/page/login/index.vue 181 ●●●● patch | view | raw | blame | history
src/page/login/userlogin.vue 2 ●●● patch | view | raw | blame | history
src/store/modules/home.js 4 ●●●● patch | view | raw | blame | history
src/styles/login.scss 9 ●●●● patch | view | raw | blame | history
src/utils/cesium/mapUtil.js 14 ●●●●● patch | view | raw | blame | history
src/views/Home/EventOverviewDetail/EventOverviewDetailRight.vue 2 ●●● patch | view | raw | blame | history
src/views/Home/Home.vue 33 ●●●●● patch | view | raw | blame | history
src/views/Home/HomeLeft/InspectionRaskDetails/InspectionRaskDetails.vue 2 ●●● patch | view | raw | blame | history
src/views/Home/HomeLeft/InspectionRaskDetails/InspectionRaskDetailsDialog.vue 3 ●●●● patch | view | raw | blame | history
src/views/SignMachineNest/SignMachineNest.vue 21 ●●●●● patch | view | raw | blame | history
src/views/TaskManage/TaskIntermediateContent/AddTask.vue 16 ●●●● patch | view | raw | blame | history
src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue 1 ●●●● patch | view | raw | blame | history
src/api/home/index.js
@@ -124,9 +124,9 @@
// 根据经纬度获取区域code
export const getAreaCodeApi = (params) => {
    return axios({
        url: `https://restapi.amap.com/v3/geocode/regeo`,
        method: 'post',
    return request({
        url: `/drone-device-core/map/amap/searchByLatLng?`,
        method: 'get',
        params
    })
}
src/assets/images/login/bg.png

src/assets/images/login/bg1.png
src/components/CommonWeather.vue
@@ -6,8 +6,8 @@
            <img src="@/assets/images/home/homeLeft/tq.png" alt="" />
            <span class="tq">{{ weather }}</span>
            <span class="tq">风速:{{ windVelocity }} </span>
            <span v-if="flylevel >= 5" :class="[isFly === '适合飞行' ? 'qk' : isFly === '禁止飞行' ? 'redqk' : 'yellowqk']">{{
                isFly
            <span v-if="flightAdvice" :class="[flightAdvice === '适合飞行' ? 'qk' : flightAdvice === '禁止飞行' ? 'redqk' : 'yellowqk']">{{
                    flightAdvice
            }}</span>
        </div>
    </div>
@@ -16,6 +16,11 @@
<script setup>
import dayjs from 'dayjs'
import { getDroneSuggest } from '@/api/home/common'
import { useStore } from 'vuex'
import { ElMessage } from 'element-plus'
const store = useStore()
const flySuggest = computed(() => store.state.home.flySuggest)
const time = ref('')
const updateTime = () => {
@@ -24,26 +29,21 @@
// 天气
const weather = ref('')
// 是否适合飞行
const isFly = ref('')
// 风速
const windVelocity = ref('')
// 区县级
const flylevel = ref('')
const flightAdvice = computed(() => flySuggest.value?.flightAdvice)
// 获取天气建议
const getWeatherSuggest = () => {
    // ElMessage.success('wobeichonghzil')
    getDroneSuggest().then(res => {
        if (res.data.code !== 0) return
        weather.value = res.data.data.weather
        isFly.value = res.data.data.flightAdvice
        windVelocity.value = res.data.data.windPower
        flylevel.value = res.data.data.adcode.replace(/0+$/, "").length
    })
}
let intervalTime
onMounted(() => {
src/components/DeviceJobDetails/DeviceJobDetails.vue
@@ -18,7 +18,7 @@
                        <div v-for="(item, index) in infoList" :key="index">
                            <div class="itemBox">
                                <div class="itemTitle">{{ item.name }}:</div>
                                <div class="itemValue">{{ item.value ? item.value : '' }}</div>
                                <div class="itemValue">{{ item.value ? item.value : '--' }}</div>
                            </div>
                        </div>
                    </div>
@@ -74,7 +74,7 @@
    { name: '任务名称', value: '', field: 'name' },
    { name: '所属单位', value: '', field: 'dept_name' },
    { name: '任务类型', value: '', field: 'industry_type_str' },
    { name: '任务时间', value: '', field: 'begin_time' + ' - ' + 'end_time' },
    { name: '任务时间', value: '', field: 'cycle_time_value' },
    { name: '飞行事件', value: '', field: 'event_number' },
    { name: '任务频次', value: '', field: 'rep_rule_type rep_rule_val' },
    { name: '任务描述', value: '', field: 'remark' },
@@ -123,14 +123,16 @@
const getDetails = () => {
    getJobDetails({ wayLineJobInfoId: wayLineJodInfoId.value }).then(res => {
        detailsData.value = res.data.data
        infoList.value.forEach(item => {
            if (item.name === '任务时间') {
                item.value = detailsData.value.begin_time.slice(0, 10) + '-' + detailsData.value.end_time.slice(0, 10)
            } else if (item.name === '任务频次') {
            // if (item.name === '任务时间') {
            //     item.value = detailsData.value.begin_time.slice(0, 10) + '-' + detailsData.value.end_time.slice(0, 10)
            // } else
             if (item.name === '任务频次') {
                const { rep_rule_type = '', rep_rule_val = '' } = detailsData?.value || {}
                item.value = rep_rule_type + ' -- ' + rep_rule_val
            } else {
                item.value = detailsData.value?.[item.field] || ''
                item.value = detailsData.value?.[item.field] || '--'
            }
        })
        flystatus.value = res.data.data.ai_type_str
src/const/drc.js
@@ -38,3 +38,10 @@
    {name:'悬停',value:'hover',img:hoverImg},
    {name:'拍照',value:'takePhoto',img:takePhotoImg},
]
// 地图层级
export const MAP_LEVEL = [
    { name: '县',heightRange: [0, 48651], height: 31753},
    { name: '市',heightRange: [48651, 314863], height: 257731},
    { name: '省',heightRange: [314863, 3796280000], height: 1987280},
]
src/hooks/useMapAggregation/useMapAggregation.js
@@ -1,8 +1,7 @@
import * as Cesium from 'cesium'
import aggregationImg from '@/assets/images/home/useUavHome/aggregation.png'
import eventAggregationImg from '@/assets/images/home/useUavHome/eventAggregationImg.png'
import uavImg from '@/assets/images/home/useUavHome/uavImg.png'
import _ from 'lodash'
import { getEventImage } from '@/utils/stateToImageMap/event'
import { getDroneStatusImage } from '@/utils/stateToImageMap/drone'
@@ -15,6 +14,7 @@
// hook
import { useMapHandlerClick } from '@/hooks/components/useMapHandlerClick'
import { MAP_LEVEL } from '@/const/drc'
/**
 * 机巢聚合功能
@@ -56,19 +56,8 @@
    }
  })
  let scalingJudgment = [
    { name: '县', splashedList: [], gJson: null, show: false, outline: {}, value: [0, 48651], height: 31753 },
    { name: '市', splashedList: [], gJson: null, show: false, outline: {}, value: [48651, 314863], height: 257731 },
    {
      name: '省',
      splashedList: [],
      gJson: null,
      show: false,
      outline: {},
      value: [314863, 3796280000],
      height: 1987280,
    },
  ]
  let scalingJudgment = _.cloneDeep(MAP_LEVEL).map(i => ({...i,gJson: null,splashedList:[], show: false, outline: {}}))
  let viewer = null
  let active = null
@@ -100,7 +89,7 @@
    // 根据高度展示对应的 gJson
    for (let [index, item] of scalingJudgment.entries()) {
      if (!item.show) return
      if (height > item.value[0] && height <= item.value[1]) {
      if (height > item.heightRange[0] && height <= item.heightRange[1]) {
        if (active === item.name) return
        active = item.name
        removeEntities()
src/page/login/index.vue
@@ -1,11 +1,13 @@
<template>
  <div class="login-container" @keyup.enter="handleLogin">
    <div class="login-header">
      <div class="title">中图智飞低空智能感知网平台</div>
  <div :class="{loginContainer:true, 'narrowScreen':narrowScreen}" @keyup.enter="handleLogin">
    <div class="login-container">
      <div class="login-header">
        <div class="title">中图智飞低空智能感知网平台</div>
      </div>
      <!-- <div class="login-left-title">中国图强 智领飞跃</div> -->
      <!-- <div class="login-left"></div> -->
      <userLogin v-if="activeName === 'user'"></userLogin>
    </div>
    <!-- <div class="login-left-title">中国图强 智领飞跃</div> -->
    <div class="login-left"></div>
    <userLogin v-if="activeName === 'user'"></userLogin>
  </div>
</template>
<script>
@@ -30,6 +32,7 @@
  },
  data() {
    return {
      narrowScreen: false,
      login:{
        info: '中图智飞低空智能感知网平台'
      },
@@ -53,12 +56,19 @@
    this.handleLogin();
    this.getTime();
  },
  mounted() {},
  mounted() {
    this.narrowScreenFun()
    window.addEventListener('resize', this.narrowScreenFun);
  },
  computed: {
    ...mapGetters(['tagWel']),
  },
  props: [],
  methods: {
    narrowScreenFun() {
      // 监听窗口变化,计算是不是 窄屏幕
      this.narrowScreen = window.innerWidth / window.innerHeight < 16 / 9;
    },
    getTime() {
      setInterval(() => {
        this.time = this.$dayjs().format('YYYY年MM月DD日 HH:mm:ss');
@@ -131,127 +141,40 @@
</script>
<style lang="scss" scoped>
// .login-index {
//   position: relative;
//   width: 100%;
//   height: 100%;
//   background: url('@/assets/images/login/bg.png') no-repeat center / 100% 100%;
//   .login-header {
//     background: url('@/assets/images/login/big-title.png') no-repeat center / 100% 100%;
//     width: 100%;
//     height: 102px;
//     position: relative;
//     .title {
//       position: absolute;
//       left: 637px;
//       top: 22px;
//       width: 646px;
//       height: 58px;
//       font-family: YouSheBiaoTiHei, YouSheBiaoTiHei;
//       font-weight: 400;
//       font-size: 54px;
//       line-height: 58px;
//       // text-shadow: 0px 2px 6px #0C3D79;
//       text-align: center;
//       font-style: normal;
//       text-transform: none;
//       color: #fff;
//       // background: linear-gradient(90deg, #FFFFFF 0%, #B2D5FF 100%);
//     }
//   }
//   .login-left-title {
//     position: absolute;
//     left: 147px;
//     top: 263px;
//     width: 926px;
//     height: 42px;
//     font-family: Source Han Sans CN, Source Han Sans CN;
//     font-weight: bold;
//     font-size: 28px;
//     line-height: 33px;
//     letter-spacing: 30px;
//     // text-shadow: 0px 4px 6px rgba(0,0,0,0.72);
//     color: #fff;
//     text-align: center;
//     font-style: normal;
//     text-transform: none;
//     // background: linear-gradient(90.00000000000004deg, #FFFFFF 0%, #E3FEFF 63%, #88BAFF 100%);
//   }
//   .login-left {
//     position: absolute;
//     top: 334px;
//     left: 147px;
//     width: 858px;
//     height: 540px;
//     background: url('@/assets/images/login/left-logo.png') no-repeat center / 100% 100%;
//   }
.loginContainer {
  position: relative;
  width: 100%;
  height: 100%;
  overflow-x: auto;
  scrollbar-width: none; /* Firefox 隐藏滚动条 */
  &::-webkit-scrollbar {
    display: none; /* Chrome、Safari 隐藏滚动条 */
  }
//   .login-right {
//     position: absolute;
//     top: 305px;
//     right: 291px;
//     width: 488px;
//     height: 508px;
//     background: url('@/assets/images/login/right-login-info.png') no-repeat center / 100% 100%;
//     .user-login {
//       position: absolute;
//       top: 72px;
//       left: 180px;
//       width: 118px;
//       height: 26px;
//       font-family: YouSheBiaoTiHei, YouSheBiaoTiHei;
//       font-weight: 400;
//       font-size: 32px;
//       // color: #D1E0F9;
//       color: #fff;
//       line-height: 26px;
//       text-align: center;
//     }
//     .username {
//       position: absolute;
//       top: 127px;
//       left: 46px;
//       width: 394px;
//       height: 72px;
//       background: url('../assets/images/login/username.png') no-repeat center / 100% 100%;
//       // :deep(.el-form-item) {
//       //   width: 120px;
//       // }
//     }
//     .password {
//       position: absolute;
//       top: 208px;
//       left: 46px;
//       width: 394px;
//       height: 72px;
//       background: url('../assets/images/login/password.png') no-repeat center / 100% 100%;
//     }
//     .forgot-password {
//       position: absolute;
//       width: 64px;
//       height: 24px;
//       top: 289px;
//       right: 58px;
//       font-family: Source Han Sans CN, Source Han Sans CN;
//       font-weight: 500;
//       font-size: 16px;
//       color: #D1E0F9;
//       line-height: 19px;
//     }
//     .login-click {
//       position: absolute;
//       bottom: 106px;
//       left: 44px;
//       width: 391px;
//       height: 61px;
//       background: url('../assets/images/login/login-btn.png') no-repeat center / 100% 100%;
//       font-family: Source Han Sans CN, Source Han Sans CN;
//       font-weight: 500;
//       font-size: 24px;
//       line-height: 50px;
//       text-align: center;
//       color: #fff;
//     }
//   }
// }
  &.narrowScreen{
    >div{
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
    }
  }
  >div{
    left: 0;
    top: 0;
    position: absolute;
    height: 1080px;
    width: 1920px;
  }
  .login-container {
    background-image: url('@/assets/images/login/bg.png');
    background-size: 100% 100%;
    background-repeat: no-repeat;
    // background: url('@/assets/images/login/bg.png') no-repeat center / 100% 100%;
    pointer-events: none;
    > * {
      pointer-events: auto;
    }
  }
}
</style>
src/page/login/userlogin.vue
@@ -40,7 +40,7 @@
      </el-input>
    </el-form-item>
  </div>
  <div class="forgot-password">忘记密码</div>
  <!-- <div class="forgot-password">忘记密码</div> -->
  <div class="">
    <el-form-item>
      <el-button type="primary" @click.prevent="handleLogin" class="login-submit">
src/store/modules/home.js
@@ -15,9 +15,13 @@
    eventTimeType: 'day',
    eventTimeParams: 'CURRENT_WEEK',
    eventTimeRang: '',
        flySuggest:{},//飞行建议
  },
  actions: {},
  mutations: {
        setFlySuggest: (state, data) => {
            state.flySuggest = data;
        },
        setUserAreaPosition: (state, data) => {
            setStore({ name: 'userAreaPosition', content: data })
            state.userAreaPosition = data;
src/styles/login.scss
@@ -1,9 +1,4 @@
.login-container {
  background: url('../assets/images/login/bg.png') no-repeat center / 100% 100%;
  position: relative;
  width: 100%;
  height: 100%;
}
.login-header {
  background: url('../assets/images/login/big-title.png') no-repeat center / 100% 100%;
  width: 100%;
@@ -140,7 +135,7 @@
  display: flex;
}
.login-left {
  padding-top: 100px;
  // padding-top: 100px;
  justify-content: center;
  flex-direction: column;
  color: #fff;
src/utils/cesium/mapUtil.js
@@ -366,3 +366,17 @@
        offset: new Cesium.HeadingPitchRange(0, 0, boundingSphere.radius * rangeMultiple),
    })
}
// 获取视口地图中心点
export function getMapCenterPoint(viewer) {
    const centerResult = viewer.camera.pickEllipsoid(
        new Cesium.Cartesian2(
            viewer.canvas.clientWidth / 2,
            viewer.canvas.clientHeight / 2
        )
    );
    const curPosition = Cesium.Ellipsoid.WGS84.cartesianToCartographic(centerResult);
    const longitude = (curPosition.longitude * 180) / Math.PI;
    const latitude = (curPosition.latitude * 180) / Math.PI;
    return { longitude, latitude };
}
src/views/Home/EventOverviewDetail/EventOverviewDetailRight.vue
@@ -1,7 +1,7 @@
<template>
    <div class="event-overviewdetail-right" :class="{ isMore }">
        <UserOperate/>
        <CommonTitle title="事件概况" :style="{ width: isMore ? pxToRem(820) : pxToRem(404) }" />
        <CommonTitle title="事件概况详情" :style="{ width: isMore ? pxToRem(820) : pxToRem(404) }" />
        <div class="content">
            <img
src/views/Home/Home.vue
@@ -32,6 +32,11 @@
import cesiumOperation from '@/utils/cesium-tsa'
import EventOverviewDetail from '@/views/Home/EventOverviewDetail/EventOverviewDetail.vue'
import AINowFly from '@/views/Home/AINowFly.vue'
import { getMapCenterPoint } from '@/utils/cesium/mapUtil'
import { getAreaCodeApi } from '@/api/home'
import { getDroneSuggest } from '@/api/home/common'
import { MAP_LEVEL } from '@/const/drc'
import _ from 'lodash'
const store = useStore()
const { _init, viewerDestory } = cesiumOperation()
@@ -52,7 +57,35 @@
    viewerDestory()
})
// 获取飞行建议
function getFlySuggestion(){
    const viewer = window.$viewer
    const {longitude,latitude} = getMapCenterPoint(viewer)
    let height = viewer.camera.positionCartographic.height;
    if (height > MAP_LEVEL[0].heightRange[1]) {
        store.commit('setFlySuggest', {})
        return
    }
    const params = { location: longitude + ',' + latitude}
    getAreaCodeApi(params).then(res => {
        const { adcode:areaCode } = res.data.data.regeocode.addressComponent
        if (!areaCode) return
        return getDroneSuggest({areaCode})
    }).then(res => {
        const setFlySuggest = _.pick(res.data.data,['windPower','flightAdvice']) || {}
        store.commit('setFlySuggest', setFlySuggest)
    })
}
onBeforeUnmount(() =>{
    window.$viewer.camera.moveEnd.removeEventListener(getFlySuggestion);
})
onMounted(() => {
    _init('cesium')
    nextTick(() => {
        getFlySuggestion()
        window.$viewer.camera.moveEnd.addEventListener(getFlySuggestion);
    })
})
</script>
src/views/Home/HomeLeft/InspectionRaskDetails/InspectionRaskDetails.vue
@@ -1,6 +1,6 @@
<!-- 巡检任务详情 -->
<template>
    <common-title title="巡检任务情况" :style="{ marginLeft: pxToRem(14) }" @details="detailsFun"></common-title>
    <common-title title="巡检任务概况" :style="{ marginLeft: pxToRem(14) }" @details="detailsFun"></common-title>
    <div class="inspection-rask-details">
        <div class="inspection-num">
            <div class="total">
src/views/Home/HomeLeft/InspectionRaskDetails/InspectionRaskDetailsDialog.vue
@@ -107,7 +107,7 @@
                    </template>
                </el-table-column>
                <el-table-column show-overflow-tooltip prop="industry_type_str" label="任务类型" />
                <el-table-column prop="begin_time" label="任务时间" width="150" />
                <el-table-column prop="cycle_time_value" label="任务时间" width="150" />
                <el-table-column prop="event_number" label="关联事件">
                    <template #default="scope">
                        <span>{{ scope.row.event_number ? scope.row.event_number : '/' }}</span>
@@ -241,6 +241,7 @@
        if (res.data.code !== 0) return
        taskDetailData.value = res.data.data.records
        total.value = res.data.data.total
    })
}
// 机巢列表数据
src/views/SignMachineNest/SignMachineNest.vue
@@ -40,8 +40,8 @@
    getDeviceDetail(singleUavHome.value.device_sn).then(res => {
        const result = res.data.data
        dockDetails.value = result
    const storageObj  = _.pick(result, ['latitude', 'longitude'])
        setSingleUavAreaCode(storageObj)
    const storageObj  = _.pick(result, ['latitude', 'longitude']) || {}
        store.commit('setSingleUavHome',{...singleUavHome.value, ...storageObj})
        initDroneEntity({
            lng: result.longitude,
            lat: result.latitude,
@@ -50,23 +50,6 @@
        workspaceId.value = result.workspace_id
    })
}
// 设置单机巢得位置信息
const setSingleUavAreaCode = (position) =>{
    store.commit('setSingleUavHome',{...singleUavHome.value, ...position})
    const params = {
        output:'json',
        location: position.longitude+','+position.latitude,
        key:'6c3ea75b215f0c0efcbcfdf13273991b',
        radius:'0',
        extensions:'base',
    }
    // todo 2025年4月23号 后端提供接口然后对接
    // getAreaCodeApi(params).then(res => {
    //     console.log(res,66666666)
    // })
}
// 获取机巢统计数据 提供给左右侧组件使用
const getMachineData = () => {
src/views/TaskManage/TaskIntermediateContent/AddTask.vue
@@ -18,15 +18,15 @@
                        <el-input class="ztzf-input" v-model="searchForm.name" placeholder="请输入任务/机巢名称"></el-input>
                    </div>
                    <div class="item">
                        <div class="itemchild">日期选择:</div>
                        <el-date-picker
                            popper-class="custom-date-picker"
                            class="ztzf-date-picker"
                            class="ztzf-date-picker tasktimer"
                            v-model="taskData"
                            type="daterange"
                            range-separator="至"
                            start-placeholder="开始日期"
                            end-placeholder="结束日期"
                            type="date"
                            placeholder="请选择日期"
                            value-format="YYYY-MM-DD"
                            :disabled-date="disabledDate"
                        />
                    </div>
                    <div class="item">
@@ -61,7 +61,7 @@
                    </div>
                    <div class="item">
                        <div class="itemchild">关联算法:</div>
                        <TaskAlgorithmBusiness :setWidth="200" :showAlgorithm="true" @algorithmChange="algorithmChange" />
                        <TaskAlgorithmBusiness :setWidth="220" :showAlgorithm="true" @algorithmChange="algorithmChange" />
                    </div>
                    <div class="item">
                        <div class="itemchild">任务描述:</div>
@@ -251,8 +251,8 @@
        return
    }
    searchForm.begin_time = `${taskData.value[0]} 00:00:00`
    searchForm.end_time = `${taskData.value[1]} 23:59:59`
    searchForm.begin_time = `${taskData.value} 00:00:00`
    searchForm.end_time = `${taskData.value} 23:59:59`
    searchForm.execute_time_arr = timeSlot.value ? [timeSlot.value] : []
    createTask(searchForm).then(res => {
        if (res.data.code === 0) {
src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue
@@ -94,7 +94,6 @@
    if (res.data.code !== 0) return;
    jobListData.value = res.data.data.records;
    total.value = res.data.data.total;
    console.log('任务管理列表',jobListData.value);
    
  });
};