无人机管理后台前端(已迁走)
张含笑
2025-12-09 0bf99978663e2d682ba611c13c8777c00632b476
Merge branch 'refs/heads/feature/v9.0/9.0.1' into test

# Conflicts:
# src/views/job/components/DeviceJobDetails.vue
6 files modified
209 ■■■■■ changed files
src/views/job/components/DeviceJobDetails.vue 176 ●●●●● patch | view | raw | blame | history
src/views/layerManagement/components/leftList.vue 2 ●●● patch | view | raw | blame | history
src/views/wel/components/flightStatistics.vue 6 ●●●● patch | view | raw | blame | history
src/views/wel/components/flyratio.vue 6 ●●●● patch | view | raw | blame | history
src/views/wel/components/proportionStatic.vue 13 ●●●●● patch | view | raw | blame | history
src/views/wel/components/taskOutcome.vue 6 ●●●● patch | view | raw | blame | history
src/views/job/components/DeviceJobDetails.vue
@@ -17,8 +17,8 @@
                                <!-- 飞行事件 -->
                                <div class="flightEvents" v-if="item.name === '飞行事件'">
                                    <template v-if="flightEvents.length">
                                        <img v-for="(item, index) in flightEvents" alt="" :src="item.img"
                                            :title="item.name" :key="index"></img>
                    <img v-for="(item, index) in flightEvents" alt="" :src="item.img" :title="item.name"
                      :key="index"></img>
                                    </template>
                                    <div class="itemValue" v-else>--</div>
                                </div>
@@ -39,11 +39,14 @@
                </div>
                    <div class="rightBox" v-if="total">
                        <div class="downloadBtn" @click="htlsrwxq === 100 && downloadFun()">
                            <el-progress v-if="htlsrwxq !== 100" :percentage="htlsrwxq" :show-text="false" striped striped-flow :duration="1" />
              <el-progress v-if="htlsrwxq !== 100" :percentage="htlsrwxq" :show-text="false" striped striped-flow
                :duration="1" />
                            <div class="downloadBtnText">
                                <span v-if="htlsrwxq === 100">下载</span>
                                <template v-else>
                                    处理中<el-icon @click="cancelDownload"><CircleClose /></el-icon>
                  处理中<el-icon @click="cancelDownload">
                    <CircleClose />
                  </el-icon>
                                </template>
                            </div>
                        </div>
@@ -59,45 +62,21 @@
                        <el-checkbox v-model="item.checked" />
                        <div class="itemName">{{ item.createTime }}</div>
                        <!-- 正射 -->
                            <el-image
                                v-if="item.resultType === 4"
                                :src="item.smallUrl"
                                :preview-src-list="[item.showUrl]"
                                fit="cover"
                                preview-teleported
                            />
              <el-image v-if="item.resultType === 4" :src="item.smallUrl" :preview-src-list="[item.showUrl]" fit="cover"
                preview-teleported />
                            <!-- 全景 -->
                            <img
                                v-else-if="item.resultType === 5"
                                class="quanjing"
                                @click="clickpanorama(item)"
                                :src="item?.smallUrl"
                                alt=""
                            />
              <img v-else-if="item.resultType === 5" class="quanjing" @click="clickpanorama(item)" :src="item?.smallUrl"
                alt="" />
                        <!-- 视频 -->
                            <div v-else-if="item.resultType === 1" class="videotime">
                                <img
                                    class="videoDisplay"
                                    :src="convertVideoUrlToThumbnail(item.link)"
                                    alt=""
                                    @click="enterFullScreen(index)"
                                />
                                <img
                                    @click="enterFullScreen(index)"
                                    class="videobutton"
                                    src="@/assets/images/task/videoshow.png"
                                    alt=""
                                />
                <img class="videoDisplay" :src="convertVideoUrlToThumbnail(item.link)" alt=""
                  @click="enterFullScreen(index)" />
                <img @click="enterFullScreen(index)" class="videobutton" src="@/assets/images/task/videoshow.png"
                  alt="" />
                            </div>
                                <!-- 图片 -->
                            <el-image
                                v-else
                                :src="item.smallUrl"
                                :preview-src-list="[item.showUrl]"
                                preview-teleported
                                :initial-index="index"
                                fit="cover"
                            />
              <el-image v-else :src="item.smallUrl" :preview-src-list="[item.showUrl]" preview-teleported
                :initial-index="index" fit="cover" />
            </div>
@@ -105,68 +84,43 @@
          </template>
        </div>
        <el-image-viewer
          v-if="showViewer"
          :url-list="previewUrls"
          :initial-index="activeIndex"
          @close="showViewer = false"
        />
        <el-image-viewer v-if="showViewer" :url-list="previewUrls" :initial-index="activeIndex"
          @close="showViewer = false" />
      </div>
      <div class="content-right" v-if="isShow">
        <DeviceJobDetailsMap
          :detailsData="detailsData"
          :yuanImages="yuanImages"
          @showImageeclick="showImageeclick"
          :jobId="props.jobId"
        />
        <DeviceJobDetailsMap :detailsData="detailsData" :yuanImages="yuanImages" @showImageeclick="showImageeclick"
          :jobId="props.jobId" />
        <div class="content-map-popups"></div>
      </div>
    </div>
  </el-dialog>
    <el-dialog
        class="ztzf-dialog"
        append-to-body
        modal-class="detailsOfHistoricalTasks"
        v-model="VideoShow"
        :width="pxToRem(1600)"
        :close-on-click-modal="false"
        :destroy-on-close="true"
      >
  <el-dialog class="ztzf-dialog" append-to-body modal-class="detailsOfHistoricalTasks" v-model="VideoShow"
    :width="pxToRem(1600)" :close-on-click-modal="false" :destroy-on-close="true">
        <div class="fullscreen">
          <video
            ref="fullscreenVideo"
            class="fullscreen-video"
            :src="currentVideoUrl"
            :style="{ width: pxToRem(1567), height: '80vh' }"
            controls
            preload="auto"
            @play="handleVideoPlay"
            @ended="handleVideoEnded"
          ></video>
      <video ref="fullscreenVideo" class="fullscreen-video" :src="currentVideoUrl"
        :style="{ width: pxToRem(1567), height: '80vh' }" controls preload="auto" @play="handleVideoPlay"
        @ended="handleVideoEnded"></video>
        </div>
      </el-dialog>
  <!-- 全景360 -->
  <PanoramaPopup
    v-if="'全景'"
    v-model:panoramaParamsShow="panoramaParamsShow"
    v-model:panoramaParamsUrl="panoramaParamsUrl"
  />
  <PanoramaPopup v-if="'全景'" v-model:panoramaParamsShow="panoramaParamsShow"
    v-model:panoramaParamsUrl="panoramaParamsUrl" />
</template>
<script setup>
import PanoramaPopup from '@/components/PanoramaPopup/PanoramaPopup.vue';
import { getShowImg, getSmallImg, getzsSmallImg, getzsShowImg, aLinkDownloadUtil } from '@/utils/util';
import { getaiImagesPageAPI,cancelDownloadApi,getDownloadStatusApi,attachDownload,aiImagesPage} from '@/api/dataCenter/dataCenter';
import PanoramaPopup from '@/components/PanoramaPopup/PanoramaPopup.vue'
import { getShowImg, getSmallImg, getzsSmallImg, getzsShowImg, aLinkDownloadUtil } from '@/utils/util'
import { getaiImagesPageAPI, cancelDownloadApi, getDownloadStatusApi, attachDownload, aiImagesPage } from '@/api/dataCenter/dataCenter'
import { pxToRem } from '@/utils/rem'
import JobRelatedEvents from './JobRelatedEvents.vue'
import { getEventMediaListApi, getJobDetails, getJobInfoFiles,getJobsAllFiles } from '@/api/job/task'
import DeviceJobDetailsMap from './DeviceJobDetailsMap.vue'
import { droneEventList } from '../const/drc'
import { ElMessage,ElLoading } from 'element-plus'
import { useStore } from 'vuex';
import EventBus from '@/utils/eventBus';
const store = useStore();
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')
const VideoShow = ref(false)
@@ -243,31 +197,7 @@
const visibleCount = computed(() =>
    showAll.value ? achievementList.value.length : Math.min(5, achievementList.value.length)
)
const getAchievement = () => {
    if (!props.jobId) return
  const attachmentsParams = {
    'wayLineJobId': props.jobId,
    'resultTypes': [0, 1, 2, 4, 5],
    'orderByCreateTime': true,
    current: 1,
    size: 2000,
  }
    const pageParams = {
        current: 1,
        size: 2000,
    }
  aiImagesPage(attachmentsParams ).then(res => {
        achievementList.value = res.data.data.records.map(i => ({
            ...i,
            checked: false,
            smallUrl: i.resultType === 4 ? getzsSmallImg(i.link) : getSmallImg(i.link),
            showUrl: i.resultType === 4 ? getzsShowImg(i.link) : getShowImg(i.link),
        }))
        total.value = res.data.data.total
        // console.log('成果数据', res.data.data)
    })
}
const jumpMore = () => {
    ElMessage.warning('正在加急开发中...')
}
@@ -301,19 +231,26 @@
      }
                getJobsAllFiles({
            wayLineJobId: detailsData.value.way_lines.map(item => item.job_id).join(','),
            resultTypes: [0, 2, 4, 5],
        resultTypes: [0, 1, 2, 4, 5],
        orderByCreateTime: true,
        }).then(result => {
            if (result.data.code !== 200) return
            yuanImages.value = result.data.data.records
        achievementList.value = res.data.data.records.map(i => ({
          ...i,
          checked: false,
          smallUrl: i.resultType === 4 ? getzsSmallImg(i.link) : getSmallImg(i.link),
          showUrl: i.resultType === 4 ? getzsShowImg(i.link) : getShowImg(i.link),
        }))
        total.value = res.data.data.total
        yuanImages.value = res.data.data.records.filter(item => item.resultType !== 1)
        })
        })
        // flystatus.value = res.data.data.ai_type_str
    })
}
// 播放
const videoRef = ref(null);
const videoRef = ref(null)
const currentVideoIndex = ref(-1)
const enterFullScreen = (index) => {
@@ -334,7 +271,7 @@
// 下载
const downloadFun = async () => {
    const list = achievementList.value.filter(i => i.checked)
    console.log('list',list);
  console.log('list', list)
    if (!list?.length) return ElMessage.warning('请选择文件')
    if (list.length === 1) {
        list.forEach((item, index) => {
@@ -384,7 +321,6 @@
}
onMounted(() => {
    getDetails()
    getAchievement()
})
</script>
@@ -409,6 +345,7 @@
.content {
    display: flex;
    height: 100%;
.contentLeft {
        margin-left: 35px;
        margin-right: 24px;
@@ -420,9 +357,12 @@
            // background: url('/src/assets/images/task/detailtitle.png') no-repeat center;
            border-bottom: 2px solid #e4e7ed;
            background-size: 100% 100%;
            img{
            width: 15px;
            height: 15px;}
        height: 15px;
      }
            span {
                display: inline-block;
                margin-left: 10px;
@@ -443,6 +383,7 @@
            display: flex;
            justify-content: space-between;
            align-content: center;
            .rightBox{
                display: flex;
                align-items: center;
@@ -468,6 +409,7 @@
                        gap: 5px;
                        z-index: 5;
                    }
                    .el-progress{
                        position: absolute;
                        left: 0;
@@ -489,9 +431,12 @@
                }
            }
            img{
            width: 15px;
            height: 15px;}
        height: 15px;
      }
            p {
                display: inline-block;
                margin-left: 10px;
@@ -597,6 +542,7 @@
                border-radius: 0px 0px 4px 4px;
                overflow: hidden;
            }
            :deep(.el-checkbox__inner) {
                width: 17px !important;
                height: 17px !important;
@@ -604,21 +550,25 @@
                border-radius: 4px 4px 4px 4px;
                border: 1px solid #ffffff !important;
            }
            .el-checkbox {
                position: absolute;
                top: 0;
                left: 6px;
            }
            :deep(.el-checkbox__inner:after) {
                position: absolute;
                left: 50% !important;
                top: 40% !important;
                transform: translate(-50%, -50%) rotate(45deg) !important;
            }
            :deep(.el-checkbox.is-checked .el-checkbox__inner) {
                background-color: #09297b !important;
                border-color: #1c5cff !important;
            }
            .el-image {
                width: 100%;
                height: 100%;
@@ -645,6 +595,7 @@
                    object-fit: cover;
                }
            }
            .itemName {
                position: absolute;
                bottom: 0;
@@ -675,6 +626,7 @@
            background: #999;
            border-radius: 50%;
        }
        .videoDisplay {
            display: flex;
            flex-wrap: wrap;
src/views/layerManagement/components/leftList.vue
@@ -301,7 +301,7 @@
      }
    })
    .catch(() => {
      ElMessage.warning('已取消删除');
    });
};
// 新增围栏
src/views/wel/components/flightStatistics.vue
@@ -496,12 +496,16 @@
  .card-item {
    width: 94px;
    height: 100%;
    line-height: 28px;
    // line-height: 28px;
    cursor: pointer;
    font-family: 'Source Han Sans CN';
    font-weight: 400;
    font-size: 14px;
    color: #7c8091;
      border: 1px solid transparent;
      display: flex;
      align-items: center;
      justify-content: center;
  }
  .card-item:first-child {
src/views/wel/components/flyratio.vue
@@ -329,12 +329,16 @@
  .card-item {
    width: 94px;
    height: 100%;
    line-height: 28px;
    // line-height: 28px;
    cursor: pointer;
    font-family: 'Source Han Sans CN';
    font-weight: 400;
    font-size: 14px;
    color: #7c8091;
      border: 1px solid transparent;
      display: flex;
      align-items: center;
      justify-content: center;
  }
  .card-item:first-child {
src/views/wel/components/proportionStatic.vue
@@ -28,7 +28,7 @@
                <div class="ratio">
                  占比
                  <span :style="{ color: item.color }"
                    >{{ ((item.rate * 100) / 100).toFixed(2) }}%</span
                    >{{ item.rate || 0 }}%</span
                  >
                </div>
              </div>
@@ -133,12 +133,13 @@
let { chart } = useEchartsResize(echartsRef);
const initChart = val => {
  let totalNum = val[0].num //val.reduce((sum, item) => sum + item.num, 0);
  let filteredData = val.filter(item => item.name !== "全部状态");
  const data = {
    total: {
      title: '总计',
      figure: totalNum.toString(), // 动态计算总数
    },
    data: val.map(item => ({
    data: filteredData.map(item => ({
      value: item.num,
      name: item.name,
      rate: _.round((item.num/totalNum)*100, 1),
@@ -147,7 +148,7 @@
  const containerWidth = chart.value.clientWidth;
  const isSmallScreen = containerWidth < 768; // 移动端判断
  const echartsOption = {
    color: ['#FF472F', '#FF7411', '#FFC300', '#0291A1'],
    color: ['#0291A1', '#FF7411', '#FFC300', '#FF472F'],
    tooltip: {
      trigger: 'item',
      padding: 0,
@@ -366,12 +367,16 @@
  .card-item {
    width: 94px;
    height: 100%;
    line-height: 28px;
    // line-height: 28px;
    cursor: pointer;
    font-family: 'Source Han Sans CN';
    font-weight: 400;
    font-size: 14px;
    color: #7c8091;
      border: 1px solid transparent;
      display: flex;
      align-items: center;
      justify-content: center;
  }
  .card-item:first-child {
src/views/wel/components/taskOutcome.vue
@@ -281,12 +281,16 @@
  .card-item {
    width: 94px;
    height: 100%;
    line-height: 28px;
    // line-height: 28px;
    cursor: pointer;
    font-family: 'Source Han Sans CN';
    font-weight: 400;
    font-size: 14px;
    color: #7c8091;
      border: 1px solid transparent;
      display: flex;
      align-items: center;
      justify-content: center;
  }
  .card-item:first-child {