| | |
| | | <!-- 巡检任务列表 --> |
| | | <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> |