forked from drone/command-center-dashboard

罗广辉
2025-04-21 2800fa4f32f3900509cb4d6eefaf2bfaf54efdd7
src/views/SignMachineNest/MachineRight/InspectionRaskList.vue
@@ -1,276 +1,410 @@
<!-- 巡检任务列表 -->
<template>
  <CommonTitle title="巡检任务列表" />
  <div :style="{ marginLeft: pxToRem(14) }">
    <div class="inspection-rask-list">
      <div class="tab-search">
        <div class="tab-btn">
          <div :class="tabIndex===1?'active':''" @click="tabClick(1)">当前任务</div>
          <div :class="tabIndex===2?'active':''" @click="tabClick(2)">历史任务</div>
        </div>
        <div class="search-box">
          <el-input v-model="searchText" placeholder="请输入搜索内容" class="input-with-select">
            <template #append>
              <el-button :icon="Search" @click="searchNickName"/>
            </template>
          </el-input>
        </div>
      </div>
      <div class="table-list" v-if="tableList.length > 0"
        infinite-scroll-distance="6"
        v-infinite-scroll="loadMore"
        :infinite-scroll-disabled="busy"
        infinite-scroll-immediate="true">>
        <div class="item" v-for="(item,index) in tableList">
          <div class="left">
            <div class="left-t">
              <span>{{ index+1 }}.</span>{{ item.name }}
              <span class="status" :class="item.status===2?'active':''">
                {{ getStatusText(item.status) }}
              </span>
            </div>
            <div class="left-b">
              <img src="../../../assets/images/signMachineNest/machineRight/date.png" alt="" />{{ item.begin_time }}
              <img src="../../../assets/images/signMachineNest/machineRight/name.png" alt="" />{{ item.creator_name || '' }}
            </div>
          </div>
          <div class="right">
            <img src="../../../assets/images/signMachineNest/machineRight/return.png" alt="">
          </div>
        </div>
      </div>
      <el-empty class="custom-empty" v-else>
        <template #description>
          <span class="custom-text">暂无数据</span>
        </template>
      </el-empty>
    </div>
  </div>
   <CommonTitle title="巡检任务列表" />
   <div :style="{ marginLeft: pxToRem(14) }">
      <div class="inspection-rask-list">
         <div class="tab-search">
            <div class="tab-btn">
               <div :class="tabIndex === 1 ? 'active' : ''" @click="tabClick(1)">当前任务</div>
               <div :class="tabIndex === 2 ? 'active' : ''" @click="tabClick(2)">历史任务</div>
            </div>
            <div class="search-box">
               <el-input v-model="searchText" placeholder="请输入关键字" class="input-with-select">
                  <template #append>
                     <el-button :icon="Search" @click="searchNickName" />
                  </template>
               </el-input>
            </div>
         </div>
         <div
            class="table-list"
            v-if="tableList.length > 0"
            infinite-scroll-distance="6"
            v-infinite-scroll="loadMore"
            :infinite-scroll-disabled="busy"
            infinite-scroll-immediate="true"
         >
            <div class="item" v-for="(item, index) in tableList">
               <div class="left" @click="taskClick(item)">
                  <div class="left-t">
                     <div>{{ index + 1 }}.</div>
                     <div class="t-name">{{item.name}}</div>
                     <div class="status" :class="item.status === 2 ? 'active' : ''">
                        {{ getStatusText(item.status) }}
                     </div>
                  </div>
                  <div class="left-b">
                     <img src="../../../assets/images/signMachineNest/machineRight/date.png" alt="" />
                     {{ item.begin_time }}
                     <img src="../../../assets/images/signMachineNest/machineRight/name.png" alt="" />
                     {{ item.creator_name || '' }}
                  </div>
               </div>
               <div class="right" v-if="tabIndex === 1" @click="reExecute(item.dock_sn)">
                  <span>立即返航</span>
                  <img src="../../../assets/images/signMachineNest/machineRight/return-fly.png" alt="" />
               </div>
               <div class="right" v-else @click="returnImmediately(item.job_id)">
                  <span>再次执行</span>
                  <img src="../../../assets/images/signMachineNest/machineRight/return-fly.png" alt="" />
               </div>
            </div>
         </div>
         <el-empty class="custom-empty" v-else>
            <template #description>
               <span class="custom-text">暂无数据</span>
            </template>
         </el-empty>
      </div>
   </div>
   <!-- 当前任务详情 -->
   <CurrentTaskDetails v-if="isShowCurrentTaskDetails" v-model:show="isShowCurrentTaskDetails" :id="currentInfoId" />
   <!-- 历史任务详情 -->
   <DeviceJobDetails
      v-if="isShowDeviceJobDetails"
      v-model:show="isShowDeviceJobDetails"
      :wayLineJodInfoId="wayLineJodInfoId"
   />
</template>
<script setup>
import { Search } from '@element-plus/icons-vue';
import CommonTitle from '@/components/CommonTitle.vue';
import { getBeforeJob, getTodayJob } from '@/api/home'
import { Search } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import CommonTitle from '@/components/CommonTitle.vue'
import CurrentTaskDetails from '@/components/CurrentTaskDetails/CurrentTaskDetails.vue'
import DeviceJobDetails from '@/components/DeviceJobDetails/DeviceJobDetails.vue'
import { getBeforeJob, getTodayJob, flyByJobId, returnHome } from '@/api/home'
import { useStore } from 'vuex'
import { useTaskWayline } from '@/hooks/useTaskWayline/useTaskWayline'
import { useTaskDetails } from '@/hooks/useTaskDetails/useTaskDetails'
import { useDroneWS } from '@/hooks/useDroneWS'
let viewer = null
const isMore = ref(true);
let { taskDetails, workspace_id, getTaskDetails } = useTaskDetails()
const wsInfo = inject('wsInfo')
const store = useStore()
// 设备任务详情
let currentInfoId = ref('')
let isShowCurrentTaskDetails = ref(false)
let wayLineJodInfoId = ref('')
let isShowDeviceJobDetails = ref(false)
// 单个机巢信息
const singleUavHome = computed(() => store.state.home.singleUavHome)
const osdVisible = computed(() => store.state.home.osdVisible);
const isMore = ref(true)
// 控制加载状态
const busy = ref(false);
const busy = ref(false)
let searchText = ref('')
const tableList = ref([]);
const tableList = ref([])
// 分页
const pageParams = ref({
  current: 1,
  size: 5,
  total: 0
});
   current: 1,
   size: 5,
   total: 0,
})
// 当前任务和历史任务切换
let tabIndex = ref(1);
const tabClick = (value) => {
  tabIndex.value = value;
  clearData();
};
let tabIndex = ref(1)
const tabClick = value => {
   tabIndex.value = value
   clearData()
}
// 状态文字判断
const getStatusText = (status) => {
  switch (status) {
    case 1:
      return '待执行';
    case 2:
      return '执行中';
    case 3:
      return '完成';
    case 4:
      return '取消';
    case 5:
      return '失败';
    default:
      return '未知';
  }
};
const getStatusText = status => {
   switch (status) {
      case 1:
         return '待执行'
      case 2:
         return '执行中'
      case 3:
         return '完成'
      case 4:
         return '取消'
      case 5:
         return '失败'
      default:
         return '未知'
   }
}
// 获取历史巡检任务列表
const getJobList = async () => {
  const params = {
    area_code: '',
    device_sn: '',
    job_name: searchText.value,
    current: pageParams.value.current,
    size: pageParams.value.size,
  }
  let result = null;
  if (tabIndex.value === 1) {
    result = await getTodayJob(params)
  } else {
    result = await getBeforeJob(params)
  }
  if (result.data.code !== 0) return;
  if (result.data.data.records.length === 0) return (isMore.value = false);
  pageParams.value.current += 1;
  tableList.value = [...tableList.value, ...result.data.data.records];
  busy.value = false;
   const params = {
      area_code: '',
      device_sn: singleUavHome.value.device_sn,
      job_name: searchText.value,
      current: pageParams.value.current,
      size: pageParams.value.size,
   }
   let result = null
   if (tabIndex.value === 1) {
      result = await getTodayJob(params)
   } else {
      result = await getBeforeJob(params)
   }
   if (result.data.code !== 0) return
   if (result.data.data.records.length === 0) return (isMore.value = false)
   pageParams.value.current += 1
   tableList.value = [...tableList.value, ...result.data.data.records]
   busy.value = false
}
// 清除数据
const clearData = () => {
  tableList.value = [];
  pageParams.value.current = 1;
  isMore.value = true;
  getJobList();
};
   tableList.value = []
   pageParams.value.current = 1
   isMore.value = true
   getJobList()
}
// 加载更多数据
const loadMore = async () => {
    busy.value = true;
    if (!isMore.value) return;
    getJobList();
};
   busy.value = true
   if (!isMore.value) return
   getJobList()
}
// 搜索数据
const searchNickName = () => {
  clearData();
};
   clearData()
}
onMounted(() => {
  getJobList();
// 立即返航
const returnImmediately = id => {
   flyByJobId(id).then(result => {
      if (result.data.code === 0) {
         ElMessage.success('执行成功')
      } else {
         ElMessage.error(result.data.message)
      }
   })
}
// 重新执行
const reExecute = dock_sn => {
   returnHome(dock_sn).then(result => {
      if (result.data.code === 0) {
         ElMessage.success('返航成功')
      } else {
         ElMessage.error(result.data.message)
      }
   })
}
// 点击当前任务显示当前任务详情
const taskClick = item => {
   if (tabIndex.value === 1) {
      // 展示当前任务详情
      if (!item?.device_sns?.length) return ElMessage.warning('没有device_sns');
      if (item?.device_sns?.length !== 1) {
         // todo 后续开发
         ElMessage.success('即将跳转到集群调度');
         return
      }
      currentInfoId.value = item.wayline_job_info_id
      isShowCurrentTaskDetails.value = true
   } else {
      // 展示历史任务详情
      wayLineJodInfoId.value = item.wayline_job_info_id
      isShowDeviceJobDetails.value = true
   }
}
watch([() => tabIndex.value, () => tableList.value], async ([newTabIndex, newTableList]) => {
   if (newTabIndex == 1 && newTableList.length > 0) {
      const params = {
         // 航线信息id
         wayLineJobInfoId:newTableList[0].wayline_job_info_id,
         // 当前航线id
         waylineJobId:newTableList[0].id,
      }
      await getTaskDetails(params)
   }
})
// 监听如果当前任务飞行完毕,当前任务即消失
watch(() => store.state.home.deviceState, async (newValue) => {
   const deviceInfo = newValue?.deviceInfo[osdVisible.value.sn];
   if (!deviceInfo) return
   // 重新刷新数据
   if (deviceInfo?.mode_code === 14) {
      getJobList()
   }
});
const { init: initTaskWayline, removeEntitys } = useTaskWayline()
onMounted(async () => {
   viewer = window.$viewer
   await getJobList()
   initTaskWayline(viewer, wsInfo, taskDetails)
})
</script>
<style lang="scss" scoped>
  .inspection-rask-list {
    width: 390px;
    height: 325px;
    background: linear-gradient(
      270deg,
      #1f3e7a 0%,
      rgba(31, 62, 122, 0.35) 79%,
      rgba(31, 62, 122, 0) 100%
    );
    opacity: 0.85;
    margin: 2px 0 13 0;
    padding: 11px 27px 0;
    .tab-search {
      width: 358px;
      height: 76px;
      margin-bottom: 12px;
      .tab-btn {
        display: flex;
        justify-content: center;
        margin-bottom: 16px;
        div {
          width: 104px;
          height: 32px;
          background: #0E2042;
          border: 1px solid #8EA3D1;
          font-family: Source Han Sans CN, Source Han Sans CN;
          font-weight: 400;
          font-size: 16px;
          color: #8EA3D1;
          line-height: 32px;
          text-align: center;
          cursor: pointer;
        }
        .active {
          width: 104px;
          height: 32px;
          background: linear-gradient( 180deg, rgba(0,82,248,0.58) 0%, rgba(103,209,251,0.8) 100%);
          color: #fff;
          border: 1px solid rgba(103,209,251,0.8);
        }
      }
      .search-box {
        :deep(.el-input__wrapper) {
          background-color: rgba(0, 112, 255, 0.1);
          background: rgba(0, 15, 34, 0.5);
          box-shadow: 0 0 0 1px #0070ff inset;
        }
.inspection-rask-list {
   width: 390px;
   height: 325px;
   background: linear-gradient(270deg, #1f3e7a 0%, rgba(31, 62, 122, 0.35) 79%, rgba(31, 62, 122, 0) 100%);
   opacity: 0.85;
   margin: 2px 0 13 0;
   padding: 11px 27px 0;
   .tab-search {
      width: 358px;
      height: 76px;
      margin-bottom: 12px;
      .tab-btn {
         display: flex;
         justify-content: center;
         margin-bottom: 16px;
         div {
            width: 104px;
            height: 32px;
            background: #0e2042;
            border: 1px solid #8ea3d1;
            font-family: Source Han Sans CN, Source Han Sans CN;
            font-weight: 400;
            font-size: 16px;
            color: #8ea3d1;
            line-height: 32px;
            text-align: center;
            cursor: pointer;
         }
         .active {
            width: 104px;
            height: 32px;
            background: linear-gradient(180deg, rgba(0, 82, 248, 0.58) 0%, rgba(103, 209, 251, 0.8) 100%);
            color: #fff;
            border: 1px solid rgba(103, 209, 251, 0.8);
         }
      }
      .search-box {
         :deep(.el-input__wrapper) {
            background-color: rgba(0, 112, 255, 0.1);
            background: rgba(0, 15, 34, 0.5);
            box-shadow: 0 0 0 1px #0070ff inset;
         }
        :deep(.el-input-group__append) {
          background: rgba(0, 112, 255, 0.38);
          .el-button {
            background-color: transparent;
            border: 1px solid #0070ff;
            border-left: none;
            color: #fff;
          }
        }
      }
    }
    .table-list {
      width: 358px;
      height: 202px;
      overflow: auto;
      &::-webkit-scrollbar {
        width: 0;
        display: none;
      }
      -ms-overflow-style: none;  /* IE and Edge */
      scrollbar-width: none;  /* Firefox */
      .item {
        width: 100%;
        height: 72px;
        padding: 12px 16px;
        margin-bottom: 8px;
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 500;
        font-size: 14px;
        color: #fff;
        display: flex;
        justify-content: space-between;
        .left {
          .left-t {
            height: 24px;
            font-size: 16px;
            margin-bottom: 4px;
            .status {
              text-align: center;
              font-size: 12px;
              display: inline-block;
              width: 48px;
              height: 20px;
              background: rgba(76,166,255,0.08);
              border-radius: 4px 4px 4px 4px;
              border: 1px solid #4CA6FF;
              color: #4CA6FF;
              margin-left: 10px;
            }
            .active {
              border: 1px solid #04F0D1;
              color: #04F0D1;
            }
          }
          .left-b {
            height: 21px;
            line-height: 21px;
            img {
              width: 16px;
              height: 16px;
              margin-right: 2px;
              &:last-child {
                margin-left: 42px;
              }
            }
          }
        }
        .right {
          img {
            width: 46px;
            height: 46px
          }
        }
      }
    }
    .custom-empty {
      color: #fff;
      margin: 16px 0;
      padding: 0;
      :deep(.el-empty__image) {
        width: 100px;
        height: 100px;
      }
    }
 }
         :deep(.el-input-group__append) {
            background: rgba(0, 112, 255, 0.38);
            .el-button {
               background-color: transparent;
               border: 1px solid #0070ff;
               border-left: none;
               color: #fff;
            }
         }
      }
   }
   .table-list {
      width: 358px;
      height: 202px;
      overflow: auto;
      &::-webkit-scrollbar {
         width: 0;
         display: none;
      }
      -ms-overflow-style: none; /* IE and Edge */
      scrollbar-width: none; /* Firefox */
      .item {
         width: 100%;
         height: 72px;
         padding: 12px 16px;
         margin-bottom: 8px;
         font-family: Source Han Sans CN, Source Han Sans CN;
         font-weight: 500;
         font-size: 14px;
         color: #fff;
         display: flex;
         justify-content: space-between;
         background: linear-gradient(
            90deg,
            rgba(71, 157, 255, 0) 0%,
            rgba(71, 157, 255, 0.12) 50%,
            rgba(71, 157, 255, 0) 100%
         );
         .left {
            cursor: pointer;
            .left-t {
               display: flex;
               height: 24px;
               font-size: 16px;
               margin-bottom: 4px;
               .t-name {
                  width: 180px;
                  white-space: nowrap; // 不换行
                  overflow: hidden; // 超出隐藏
                  text-overflow: ellipsis; // 显示省略号
               }
               .status {
                  text-align: center;
                  font-size: 12px;
                  width: 48px;
                  height: 20px;
                  background: rgba(76, 166, 255, 0.08);
                  border-radius: 4px 4px 4px 4px;
                  border: 1px solid #4ca6ff;
                  color: #4ca6ff;
                  margin-left: 10px;
               }
               .active {
                  border: 1px solid #04f0d1;
                  color: #04f0d1;
               }
            }
            .left-b {
               height: 21px;
               line-height: 21px;
               width: 260px; // 添加固定宽度
               white-space: nowrap; // 不换行
               overflow: hidden; // 超出隐藏
               text-overflow: ellipsis; // 显示省略号
               img {
                  width: 16px;
                  height: 16px;
                  margin-right: 2px;
                  &:last-child {
                     margin-left: 42px;
                  }
               }
            }
         }
         .right {
            cursor: pointer;
            position: relative;
            span {
               position: absolute;
               display: inline-block;
               top: 12px;
               left: 12px;
               width: 26px;
               line-height: 12px;
               font-size: 12px;
               font-family: YouSheBiaoTiHei, YouSheBiaoTiHei, serif;
               color: rgb(4, 18, 44);
            }
            img {
               width: 46px;
               height: 46px;
            }
         }
      }
   }
   .custom-empty {
      color: #fff;
      margin: 16px 0;
      padding: 0;
      :deep(.el-empty__image) {
         width: 100px;
         height: 100px;
      }
   }
}
</style>