Browse Source

任务列表模块开发完成

李富豪 1 year ago
parent
commit
2ce4553163

+ 18 - 0
Web/src/api/custom/index.ts

@@ -136,6 +136,8 @@ export type DeleteDirApiParams = {
 export type FetchBingCodeApi = () => Promise<any>;
 export type FetchJobListApi = (params: FetchJobListApiParams) => Promise<any>;
 export type DeleteJobApi = (params: { job_id: string }) => Promise<any>;
+export type RenewalJobApi = (job_id: string) => Promise<any>;
+export type FetchJobDetailApi = (job_id: string) => Promise<any>;
 export type FetchAllDockDeviceListApi = () => Promise<any>;
 export type FetchAllWaylineListApi = () => Promise<any>;
 export type FetchWaylineListApi = (params: FetchWaylineListApiParams) => Promise<any>;
@@ -191,6 +193,20 @@ const deleteJobApi: DeleteJobApi = async (params) => {
     return res.data;
 };
 
+// 恢复任务
+const renewalJobApi: RenewalJobApi = async (job_id) => {
+    const url = `/wayline/api/v1/workspaces/${getWorkspaceId()}/${job_id}/renewal`
+    const res = await request.post(url);
+    return res.data;
+};
+
+// 获取任务详情
+const fetchJobDetailApi: FetchJobDetailApi = async (id) => {
+    const url = `/wayline/api/v1/workspaces/${getWorkspaceId()}/jobDetail`
+    const res = await request.get(url, { params: { jobId: id } });
+    return res.data;
+};
+
 // 获取全部机场设备列表
 const fetchAllDockDeviceListApi: FetchAllDockDeviceListApi = async () => {
     const url = `/manage/api/v1/devices/${getWorkspaceId()}/getDockDevices`
@@ -385,6 +401,8 @@ export const apis = {
     fetchBingCode: fetchBingCodeApi,
     fetchJobList: fetchJobListApi,
     deleteJob: deleteJobApi,
+    renewalJob: renewalJobApi,
+    fetchJobDetail: fetchJobDetailApi,
     fetchAllDockDeviceList: fetchAllDockDeviceListApi,
     fetchAllWaylineList: fetchAllWaylineListApi,
     fetchWaylineList: fetchWaylineListApi,

+ 8 - 3
Web/src/pages/page-web/projects/task/taskList/components/CreateTaskModal.vue

@@ -3,7 +3,7 @@
         wrapClassName="createTask-modal" :visible="visible">
         <div class="content">
             <div class="content-panel">
-                <TaskPanel :onClickConfirm="onClickConfirm" :onClickCancel="onClickCancel" />
+                <TaskPanel ref="taskPanelRef" :onClickConfirm="onClickConfirm" :onClickCancel="onClickCancel" />
             </div>
             <div class="content-map">
                 <div id="taskMap" :style="{ width: '100%', height: '100%' }"></div>
@@ -13,12 +13,12 @@
 </template>
 
 <script lang="ts" setup>
-import { reactive, onMounted } from 'vue';
+import { ref, reactive, onMounted } from 'vue';
 import TaskPanel from './TaskPanel.vue';
 import { useGMapManage } from '/@/hooks/use-g-map';
 
 interface Props {
-    jobId?: string,
+    jobId: string,
     visible: boolean,
     onClickConfirm: (data: any) => Promise<any>,
     onClickCancel: () => void,
@@ -28,6 +28,8 @@ const props = withDefaults(defineProps<Props>(), {
 
 });
 
+const taskPanelRef = ref();
+
 const state = reactive({
     loading: false,
     map: null,// 高德地图实例
@@ -43,6 +45,9 @@ const init = async () => {
         zoom: 12
     })
     state.map = map;
+    if (props.jobId) {
+        taskPanelRef.value.init(props.jobId);
+    }
 }
 
 onMounted(() => {

+ 57 - 13
Web/src/pages/page-web/projects/task/taskList/components/TaskPanel.vue

@@ -96,7 +96,9 @@
               <!-- 执行时间 -->
               <a-form-item label="执行时间" name="task_time" :rules="[{ required: true, message: '执行时间不能为空' }]">
                 <a-date-picker style="width: 100%;" :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
-                  valueFormat="YYYY-MM-DD HH:mm" placeholder="请选择执行时间" v-model:value="state.formModel.task_time" />
+                  valueFormat="YYYY-MM-DD HH:mm"
+                  :disabledDate="(current: any) => current < moment().subtract(1, 'days')" placeholder="请选择执行时间"
+                  v-model:value="state.formModel.task_time" />
               </a-form-item>
             </template>
             <template v-if="state.formModel.task_type === 2">
@@ -224,7 +226,6 @@ import noDataSrc from '/@/assets/icons/no-data.png';
 import aircraftSrc from '/@/components/airport/icons/aircraft.svg';
 import dockSrc from '/@/components/airport/icons/dockInfo.svg';
 import moment from 'moment';
-import { createPlan } from '/@/api/wayline';
 import { apis } from '/@/api/custom';
 import { DEVICE_NAME } from '/@/types/device'
 
@@ -244,6 +245,7 @@ interface State {
   formModel: {
     name: string;
     file_id: string;
+    wayline_type?: number,
     dock_sn: string;
     wayline_precision_type: number;
     task_type: number;
@@ -261,6 +263,7 @@ const state: State = reactive({
   formModel: {
     name: '',// 任务名称
     file_id: '',// 执行航线
+    wayline_type: undefined,
     dock_sn: '',// 执行设备
     wayline_precision_type: 1,// 任务精度
     task_type: 0,// 任务策略
@@ -283,6 +286,7 @@ const waylineState = reactive({
     user_name: string,
     drone_model: string,
     payload_model: string,
+    wayline_type: number,
   }[],
 })
 
@@ -297,10 +301,10 @@ const deviceState = reactive({
   list: [] as {
     sn: string,
     name: string,
-    drone_model: string,
     children: {
       sn: string,
       name: string,
+      drone_model: string,
     },
   }[],
 })
@@ -323,6 +327,7 @@ const fetchWaylineList = async () => {
         user_name: item.user_name,
         drone_model: DEVICE_NAME[item.drone_model_key],
         payload_model: item.payload_model_keys.map((key: any) => DEVICE_NAME[key]).join('、'),
+        wayline_type: item.template_types ? item.template_types[0] : '',
       }
     })
     waylineState.list = list;
@@ -341,13 +346,14 @@ const fetchDeviceList = async () => {
     // 在线设备列表
     const olineList = res.data.filter((item: any) => item.status);
     const list = olineList.map((item: any) => {
+      const drone_model_key = item.children ? item.children.domain + '-' + item.children.type + '-' + item.children.sub_type : '';
       return {
         sn: item.device_sn,
         name: item.nickname,
-        drone_model: item.children?.device_name || '',
         children: {
           sn: item.children?.device_sn || '',
-          name: item.children?.nickname || '',
+          name: item.children?.device_name || '',
+          drone_model: DEVICE_NAME[drone_model_key],
         }
       }
     })
@@ -359,11 +365,43 @@ const fetchDeviceList = async () => {
   }
 }
 
-onMounted(async () => {
+const fetchDetail = async (id: string) => {
+  state.loading = true;
+  try {
+    const res = await apis.fetchJobDetail(id);
+    const info = res.data;
+    const data = {
+      ...state.formModel,
+      name: info.job_name,
+      file_id: info.file_id,
+      wayline_type: info.wayline_type,
+      dock_sn: info.dock_sn,
+      wayline_precision_type: info.wayline_precision_type,
+      task_type: info.task_type,
+      task_time: info.task_type === 1 ? info.begin_time : state.formModel.task_time,
+      rth_altitude: info.rth_altitude,
+      breakpoint_continuation: info.breakpoint_continuation,
+    }
+    state.formModel = data;
+  } catch (error) {
+    console.error(error);
+  } finally {
+    state.loading = false;
+  }
+}
+
+const init = async (id?: string) => {
   await Promise.all([
     fetchWaylineList(),
     fetchDeviceList(),
   ])
+  if (id) {
+    await fetchDetail(id);
+  }
+}
+
+onMounted(() => {
+  init();
 })
 
 watch(() => waylineState.visible, async (visible) => {
@@ -411,13 +449,18 @@ const handleClickSelectDevice = () => {
 
 // 点击确定
 const handleClickConfirm = async () => {
-  formRef.value?.validateFields().then((values: any) => {
-    const waylineDroneModel = waylineInfo.value?.drone_model;
-    const droneModel = deviceInfo.value?.drone_model;
+  formRef.value?.validateFields().then(async (values: any) => {
+    const wayline = waylineInfo.value;
+    const waylineDroneModel = wayline?.drone_model;
+    const droneModel = deviceInfo.value?.children.drone_model;
+    state.formModel.wayline_type = wayline?.wayline_type;
     if (waylineDroneModel !== droneModel) {
-      return message.warning('请选择相同型号的设备');
+      // return message.warning('请选择相同型号的设备');
     }
-    const data = { ...values };
+    const data = {
+      ...values,
+      wayline_type: state.formModel.wayline_type,
+    };
     if (values.task_time) {
       data.task_time = moment(values.task_time).valueOf();
     }
@@ -431,12 +474,13 @@ const handleClickConfirm = async () => {
       const endDate = moment(values.task_periods[1]).valueOf();
       data.task_periods = [startDate, endDate];
     }
-    console.log(data, 'data');
-    // await createPlan()
+    await props.onClickConfirm(data);
   }).catch((error: any) => {
     console.error(error);
   });
 }
+
+defineExpose({ init });
 </script>
 
 <style lang="scss" scoped>

+ 51 - 17
Web/src/pages/page-web/projects/task/taskList/index.vue

@@ -12,11 +12,11 @@
           </a-checkbox>
         </div>
         <div v-if="state.onlineDockList.length">
-          <div v-for="(dock, index) in state.onlineDockList" :key="dock.sn">
+          <div v-for="(dock) in state.onlineDockList" :key="dock.sn">
             <div :class="[
               'taskList-left-item',
-              checkState.checkSnList.includes(dock.sn) ? 'taskList-left-item-selected' : ''
-            ]" @click="onClickCheckItem(dock.sn)">
+              checkState.checkSnList.includes(dock.gateway.sn) ? 'taskList-left-item-selected' : ''
+            ]" @click="onClickCheckItem(dock.gateway.sn)">
               <Airport :dock="dock" :look-info="false" />
             </div>
           </div>
@@ -26,9 +26,8 @@
       </div>
     </a-spin>
     <div :style="{ width: state.collapsed ? 'calc(100% - 250px)' : '100%' }">
-      <Search :onClickCollapsed="() => { state.collapsed = !state.collapsed }"
-        :onClickCreateTask="() => { state.visible = true }" :onClickSearch="onClickSearch"
-        :onClickReset="onClickReset" />
+      <Search :onClickCollapsed="() => { state.collapsed = !state.collapsed }" :onClickCreateTask="onClickCreateTask"
+        :onClickSearch="onClickSearch" :onClickReset="onClickReset" />
       <a-table :scroll="{ x: '100%', y: 500 }" rowKey="job_id" :loading="state.listLoading" :columns="columns"
         :dataSource="state.list" @change="refreshData" :rowClassName="rowClassName" :pagination="paginationConfig">
         <!-- 计划|实际时间 -->
@@ -86,11 +85,12 @@
         <!-- 操作 -->
         <template #action="{ record }">
           <div class="flex-align-center flex-row" style="color: #2d8cf0">
-            <a-tooltip title="断点续飞" v-if="record.breakpoint_continuation && [4, 5, 6].includes(record.status)">
+            <a-tooltip title="断点续飞" v-if="record.breakpoint_continuation && [5].includes(record.status)"
+              @click="onClickContinueFlight(record.job_id)">
               <ApiOutlined style="margin-right: 10px;" />
             </a-tooltip>
-            <a-tooltip title="复制任务" v-if="[0, 1].includes(record.task_type)">
-              <CopyOutlined style="margin-right: 10px;" />
+            <a-tooltip title="复制任务" v-if="[0, 1].includes(record.task_type)" @click="onClickCopy(record.job_id)">
+              <CopyOutlined style=" margin-right: 10px;" />
             </a-tooltip>
             <a-tooltip title="查看轨迹" v-if="false">
               <GatewayOutlined style="margin-right: 10px;" />
@@ -102,8 +102,9 @@
         </template>
       </a-table>
     </div>
-    <CreateTaskModal :jobId="''" :visible="state.visible" :onClickConfirm="createTaskModalOnClickConfirm"
-      :onClickCancel="createTaskModalOnClickCancel" v-if="state.visible" />
+    <CreateTaskModal :jobId="state.currentJobId" :visible="state.visible"
+      :onClickConfirm="createTaskModalOnClickConfirm" :onClickCancel="createTaskModalOnClickCancel"
+      v-if="state.visible" />
   </div>
 </template>
 
@@ -119,11 +120,13 @@ import { useMyStore } from '/@/store';
 import { useFormatTask } from '/@/components/task/use-format-task';
 import { apis } from '/@/api/custom';
 import { getDeviceTopo, getUnreadDeviceHms } from '/@/api/manage';
+import { createPlan } from '/@/api/wayline';
 import { getWorkspaceId } from '/@/utils';
 import { OnlineDevice, EModeCode } from '/@/types/device';
 import { EDeviceTypeName } from '/@/types';
 
 interface State {
+  currentJobId: string,
   visible: boolean,
   collapsed: boolean,
   onlineDockListLoading: boolean,
@@ -134,6 +137,7 @@ interface State {
 };
 
 const state: State = reactive({
+  currentJobId: '',
   visible: false,
   collapsed: true,
   onlineDockListLoading: false,
@@ -205,7 +209,7 @@ const fetchOnlineDock = async () => {
     })
     state.onlineDockList = list;
     checkState.checkAll = true;
-    checkState.checkSnList = list.map(item => item.sn);
+    checkState.checkSnList = list.map(item => item.gateway.sn);
   } catch (error) {
     console.error(error);
   } finally {
@@ -277,7 +281,7 @@ onMounted(async () => {
 // 全选
 const onCheckAllChange = async (e: any) => {
   Object.assign(checkState, {
-    checkSnList: e.target.checked ? state.onlineDockList.map(item => item.sn) : [],
+    checkSnList: e.target.checked ? state.onlineDockList.map(item => item.gateway.sn) : [],
     indeterminate: false,
   });
   await fetchList();
@@ -296,8 +300,15 @@ const onClickCheckItem = async (sn: string) => {
 }
 
 // 新建机场任务弹出层-点击确定
-const createTaskModalOnClickConfirm = async () => {
-  state.visible = false;
+const createTaskModalOnClickConfirm = async (data: any) => {
+  try {
+    await createPlan(getWorkspaceId(), data);
+    state.visible = false;
+    message.success('新建成功');
+    await fetchList();
+  } catch (error) {
+    console.error(error);
+  }
 }
 
 // 新建机场任务弹出层-点击取消
@@ -318,7 +329,7 @@ const columns = [
   {
     title: '计划|实际时间',
     dataIndex: 'duration',
-    width: 200,
+    width: 300,
     slots: { customRender: 'duration' },
   },
   {
@@ -382,7 +393,7 @@ const columns = [
     title: '操作',
     dataIndex: 'actions',
     fixed: 'right',
-    width: 100,
+    width: 120,
     slots: { customRender: 'action' },
   },
 ];
@@ -401,6 +412,12 @@ const refreshData = async (page: any) => {
   await fetchList();
 }
 
+// 点击创建任务
+const onClickCreateTask = () => {
+  state.currentJobId = '';
+  state.visible = true
+}
+
 // 点击搜索
 const onClickSearch = async (query: any) => {
   state.query = query;
@@ -413,6 +430,23 @@ const onClickReset = async (query: any) => {
   await fetchList();
 }
 
+// 点击断点续飞
+const onClickContinueFlight = async (id: string) => {
+  try {
+    await apis.renewalJob(id);
+    await fetchList();
+    message.success('操作成功');
+  } catch (error) {
+    console.error(error);
+  }
+}
+
+// 点击复制
+const onClickCopy = (id: string) => {
+  state.currentJobId = id;
+  state.visible = true;
+}
+
 // 点击删除
 const onClickDelete = (id: string, name: string) => {
   Modal.confirm({