forked from drone/command-center-dashboard

张含笑
2025-04-19 cf95f7829bc0026b931f233f32358f156cde4313
Merge remote-tracking branch 'origin/master'
4 files modified
2 files added
8178 ■■■■ changed files
pnpm-lock.yaml 7190 ●●●● patch | view | raw | blame | history
src/components/CurrentTaskDetails/CurrentTaskDetails.vue 40 ●●●● patch | view | raw | blame | history
src/components/CurrentTaskDetails/RealTimeMap.vue 67 ●●●●● patch | view | raw | blame | history
src/hooks/useTaskDetails/useTaskDetails.js 27 ●●●●● patch | view | raw | blame | history
src/hooks/useTaskWayline/useTaskWayline.js 103 ●●●●● patch | view | raw | blame | history
src/views/SignMachineNest/MachineRight/InspectionRaskList.vue 751 ●●●●● patch | view | raw | blame | history
pnpm-lock.yaml
Diff too large
src/components/CurrentTaskDetails/CurrentTaskDetails.vue
@@ -1,3 +1,13 @@
<!--
 * @Author: shuishen 1109946754@qq.com
 * @Date: 2025-04-19 13:13:15
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2025-04-19 15:07:04
 * @FilePath: \command-center-dashboard\src\components\CurrentTaskDetails\CurrentTaskDetails.vue
 * @Description:
 *
 * Copyright (c) 2025 by shuishen, All Rights Reserved.
-->
<!--当前任务详情-->
<template>
    <el-dialog
@@ -45,11 +55,11 @@
import { getLiveAiLinkApi } from '@/api/payload'
import { CURRENT_CONFIG } from '@/utils/http/config'
import { useDroneWS } from '@/hooks/useDroneWS'
import { useTaskDetails } from '@/hooks/useTaskDetails/useTaskDetails'
const isAutoControl = ref(true) //是否自动控制
const lineQuality = ref(1) //1流畅,2标清
const taskDetailsViewer = ref(null) //地图实例
let taskDetails = ref({}) //任务详情
const deviceOsdInfo = computed(() => wsInfo.value?.device_osd)
const dockSn = computed(() => taskDetails?.value?.device_sns?.[0])
const droneSn = computed(() => deviceOsdInfo?.value?.data?.sn)
@@ -62,8 +72,16 @@
const isTakeOff = ref(false) // 是在飞行中
const isMaxMap = ref(false) //是大地图
const client_id = ref('') //是大地图
const workspace_id = ref('')
// 获取机巢直播
const getDeviceLiveUrl = async () => {
    const res = await liveStart(dockSn.value, 2)
    currentLiveUrl.value = res.data.data.rtcs_url
}
let { taskDetails, workspace_id, getTaskDetails } = useTaskDetails(getDeviceLiveUrl)
let { wsInfo, removeWS } = useDroneWS(workspace_id) //ws信息,是一个ref对象
provide('wsInfo', wsInfo)
provide('deviceOsdInfo', deviceOsdInfo)
provide('dockOsdInfo', wsInfo?.value?.dock_osd)
@@ -86,12 +104,6 @@
    },
    { deep: true }
)
// 获取机巢直播
const getDeviceLiveUrl = async () => {
    const res = await liveStart(dockSn.value, 2)
    currentLiveUrl.value = res.data.data.rtcs_url
}
const getAiLiveUrl = async () => {
    const res = await getLiveAiLinkApi({
@@ -130,19 +142,9 @@
    isTakeOff.value = currentIsTakeOff
    isTakeOff.value ? await getDroneLiveUrl() : await getDeviceLiveUrl()
}
// 获取任务详情获取航线文件
const getTaskDetails = () => {
    if (!props.id) ElMessage.warning('请检查是否传入id')
    getJobDetails({ wayLineJobInfoId: props.id }).then(async res => {
        taskDetails.value = res.data.data
        await getDeviceLiveUrl()
        taskDetails.value.workspace_id = taskDetails.value.way_lines[0]?.workspace_id
        workspace_id.value = taskDetails.value.workspace_id
    })
}
onMounted(() => {
    getTaskDetails()
    getTaskDetails(props?.id)
    EventBus.on('CurrentTaskDetails-timeStop', changeLineQuality)
    EventBus.on('CurrentTaskDetails-getAiLiveUrl', getAiLiveUrl)
    EventBus.on('CurrentTaskDetails-getDroneLiveUrl', getDroneLiveUrl)
src/components/CurrentTaskDetails/RealTimeMap.vue
@@ -5,15 +5,10 @@
import * as Cesium from 'cesium'
import AmapMercatorTilingScheme from '@/utils/cesium/AmapMercatorTilingScheme'
import { Cartesian3, Terrain, Viewer } from 'cesium'
import endPointImg from '@/assets/images/EndPointicon.png'
import { addBlueFilter } from '@/utils/cesium/common'
import { analyzeKmzFile, removeTextKey, XMLToJSON } from '@/utils/cesium/kmz'
import rwqfdImg from '@/assets/images/signMachineNest/rwqfd.png'
import ImageTrailMaterial from '@/utils/cesium/ImageTrailMaterial'
import lineImg from '@/assets/images/arrow-right-blue.png'
import { flyVisual } from '@/utils/cesium/mapUtil'
import CreateFrustum from '@/utils/cesium/frustum/CreateFrustum'
import aircraftGltf from '@/assets/gltf/aircraft.gltf'
import { useTaskWayline } from '@/hooks/useTaskWayline/useTaskWayline'
const imageryProvider_ammapSL = new Cesium.UrlTemplateImageryProvider({
    url: 'https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
@@ -61,59 +56,6 @@
        destination: Cartesian3.fromDegrees(115.763819, 28.787374, 5000),
    })
}
const drawWayline = lineObj => {
    const positions = lineObj.Placemark.map(item => {
        const [lon, lat] = item.Point.coordinates.split(',')
        return Cartesian3.fromDegrees(Number(lon), Number(lat))
    })
    // 起点
    taskDetailsViewer?.value.entities.add({
        position: positions[0],
        billboard: {
            image: new Cesium.ConstantProperty(rwqfdImg),
            width: 70,
            height: 70,
        },
    })
    // 终点
    taskDetailsViewer?.value.entities.add({
        position: positions[positions.length - 1],
        billboard: {
            image: new Cesium.ConstantProperty(endPointImg),
            width: 30,
            height: 30,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 底部对齐
        },
    })
    // 路径线
    taskDetailsViewer?.value.entities.add({
        polyline: {
            width: 4,
            positions: positions,
            material: new ImageTrailMaterial({
                color: { alpha: 1, blue: 1, green: 1, red: 1 },
                speed: 20,
                image: lineImg,
                repeat: { x: Math.floor(40), y: 1 },
            }),
            clampToGround: false,
        },
    })
}
// 解析kmz文件
const parsingFiles = async url => {
    const res = await analyzeKmzFile(`${url}?_t=${new Date().getTime()}`)
    const waylinesXML = await res.fileInfoObj['wpmz/waylines.wpml']
    const waylinesXMLJSON = XMLToJSON(waylinesXML)?.['Document']
    const waylinesXMLObj = removeTextKey(waylinesXMLJSON.Folder)
    if (!waylinesXMLObj.Placemark.length) return
    const allPoint = waylinesXMLObj.Placemark.map(item => item.Point.coordinates.split(','))
    flyVisual(allPoint, taskDetailsViewer?.value)
    drawWayline(waylinesXMLObj)
}
let viewInfoFrustum
// 设置视椎
@@ -165,11 +107,8 @@
    setCreateFrustum()
    setAircraftGltf()
})
watch(taskDetails, () => {
    if (taskDetails.value.way_lines.length) {
        parsingFiles(taskDetails.value.way_lines[0].url)
    }
})
useTaskWayline(taskDetailsViewer, taskDetails)
const removeMap = () => {
    taskDetailsViewer?.value.entities.removeAll()
src/hooks/useTaskDetails/useTaskDetails.js
New file
@@ -0,0 +1,27 @@
import { ElMessage } from 'element-plus'
import { getJobDetails } from '@/api/home/task'
export function useTaskDetails (cb) {
  const taskDetails = ref({})
  const workspace_id = ref('')
  const getTaskDetails = async (id) => {
    if (!id) ElMessage.warning('请检查是否传入id')
    try {
      const res = await getJobDetails({ wayLineJobInfoId: id })
      taskDetails.value = res.data.data
      cb && await cb()
      workspace_id.value = taskDetails.value.way_lines[0]?.workspace_id
      return taskDetails.value
    } catch (error) {
      ElMessage.warning('获取任务详情失败')
      throw error
    }
  }
  return {
    taskDetails,
    workspace_id,
    getTaskDetails
  }
}
src/hooks/useTaskWayline/useTaskWayline.js
New file
@@ -0,0 +1,103 @@
/*
 * @Author: shuishen 1109946754@qq.com
 * @Date: 2025-04-19 14:24:34
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2025-04-19 15:23:57
 * @FilePath: \command-center-dashboard\src\hooks\useTaskWayline\useTaskWayline.js
 * @Description:
 *
 * Copyright (c) 2025 by shuishen, All Rights Reserved.
 */
import lineImg from '@/assets/images/arrow-right-blue.png'
import rwqfdImg from '@/assets/images/signMachineNest/rwqfd.png'
import endPointImg from '@/assets/images/EndPointicon.png'
import { analyzeKmzFile, removeTextKey, XMLToJSON } from '@/utils/cesium/kmz'
import { flyVisual } from '@/utils/cesium/mapUtil'
import ImageTrailMaterial from '@/utils/cesium/ImageTrailMaterial'
import * as Cesium from 'cesium'
import { Cartesian3 } from 'cesium'
export function useTaskWayline (viewer, taskDetails) {
  const newViewer = unref(viewer)
  // 解析kmz文件
  const parsingFiles = async url => {
    const res = await analyzeKmzFile(`${url}?_t=${new Date().getTime()}`)
    const waylinesXML = await res.fileInfoObj['wpmz/waylines.wpml']
    const waylinesXMLJSON = XMLToJSON(waylinesXML)?.['Document']
    const waylinesXMLObj = removeTextKey(waylinesXMLJSON.Folder)
    if (!waylinesXMLObj.Placemark.length) return
    const allPoint = waylinesXMLObj.Placemark.map(item => item.Point.coordinates.split(','))
    flyVisual(allPoint, newViewer)
    drawWayline(waylinesXMLObj)
  }
  const drawWayline = lineObj => {
    const positions = lineObj.Placemark.map(item => {
      const [lon, lat] = item.Point.coordinates.split(',')
      return Cartesian3.fromDegrees(Number(lon), Number(lat))
    })
    // 起点
    newViewer.entities.add({
      id: 'drone-job-wayline-start',
      position: positions[0],
      billboard: {
        image: new Cesium.ConstantProperty(rwqfdImg),
        width: 70,
        height: 70,
      },
    })
    // 终点
    newViewer.entities.add({
      id: 'drone-job-wayline-end',
      position: positions[positions.length - 1],
      billboard: {
        image: new Cesium.ConstantProperty(endPointImg),
        width: 30,
        height: 30,
        verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 底部对齐
      },
    })
    // 路径线
    newViewer.entities.add({
      id: 'drone-job-wayline-polyline',
      polyline: {
        width: 4,
        positions: positions,
        material: new ImageTrailMaterial({
          color: { alpha: 1, blue: 1, green: 1, red: 1 },
          speed: 20,
          image: lineImg,
          repeat: { x: Math.floor(40), y: 1 },
        }),
        clampToGround: false,
      },
    })
  }
  watch(taskDetails, () => {
    if (taskDetails.value?.way_lines?.length) {
      parsingFiles(taskDetails.value.way_lines[0].url)
    }
  }, { immediate: true })
  const removeEntitys = () => {
    const entitiesIDs = newViewer?.entities.values.map(i => i.id)
    entitiesIDs.forEach(item => {
      item.includes('drone-job-wayline-') && newViewer?.entities.removeById(item)
    })
  }
  onBeforeUnmount(() => {
    removeEntitys()
  })
  return {
    removeEntitys
  }
}
src/views/SignMachineNest/MachineRight/InspectionRaskList.vue
@@ -1,355 +1,492 @@
<!-- 巡检任务列表 -->
<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" @click="taskClick(item)">
            <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" 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"/>
    <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">
                            <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" 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
    <DeviceJobDetails
        v-if="isShowDeviceJobDetails"
        v-model:show="isShowDeviceJobDetails"
        :wayLineJodInfoId="wayLineJodInfoId"/>
        :wayLineJodInfoId="wayLineJodInfoId"
    />
</template>
<script setup>
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 { 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 { getBeforeJob, getTodayJob, flyByJobId, returnHome } from '@/api/home'
import { useStore } from 'vuex'
import { useTaskWayline } from '@/hooks/useTaskWayline/useTaskWayline'
import { useTaskDetails } from '@/hooks/useTaskDetails/useTaskDetails'
let { taskDetails, workspace_id, getTaskDetails } = useTaskDetails()
const store = useStore();
const store = useStore()
// 设备任务详情
let currentInfoId = ref('');
let isShowCurrentTaskDetails = ref(false);
let wayLineJodInfoId = ref('');
let isShowDeviceJobDetails = ref(false);
let currentInfoId = ref('')
let isShowCurrentTaskDetails = ref(false)
let wayLineJodInfoId = ref('')
let isShowDeviceJobDetails = ref(false)
// 单个机巢信息
const singleUavHome = computed(() => store.state.home.singleUavHome);
const singleUavHome = computed(() => store.state.home.singleUavHome)
const isMore = ref(true);
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: 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 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()
}
// 立即返航
const returnImmediately = (id) => {
  flyByJobId(id).then(result => {
    if (result.data.code === 0) {
      ElMessage.success('执行成功');
    } else {
      ElMessage.error(result.data.message);
    }
  });
};
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 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) {
    // 展示当前任务详情
    currentInfoId.value = item.wayline_job_info_id;
    isShowCurrentTaskDetails.value = true;
  } else {
    // 展示历史任务详情
    wayLineJodInfoId.value = item.wayline_job_info_id;
    isShowDeviceJobDetails.value = true;
  }
};
const taskClick = item => {
    if (tabIndex.value === 1) {
        // 展示当前任务详情
        currentInfoId.value = item.wayline_job_info_id
        isShowCurrentTaskDetails.value = true
    } else {
        // 展示历史任务详情
        wayLineJodInfoId.value = item.wayline_job_info_id
        isShowDeviceJobDetails.value = true
    }
}
onMounted(() => {
  getJobList();
});
onMounted(async () => {
    await getJobList()
    // tableList.value = [
    //     {
    //         id: 11563,
    //         status: 5,
    //         begin_time: '2025/04/19 05:46:25',
    //         end_time: '2025/04/19 05:46:25',
    //         create_time: '2025-04-19T05:46:36.032+00:00',
    //         name: '智引即飞202504191252',
    //         event_number: 0,
    //         dock_sn: '7CTDLCR00BQBM2',
    //         wayline_job_info_id: 340,
    //         job_id: '484a7846-60e0-4ec3-b9fb-272a055c6938',
    //     },
    //     {
    //         id: 11559,
    //         status: 5,
    //         begin_time: '2025/04/19 05:39:42',
    //         end_time: '2025/04/19 05:39:42',
    //         create_time: '2025-04-19T05:39:52.022+00:00',
    //         name: '智引即飞202504191252',
    //         event_number: 0,
    //         dock_sn: '7CTDLCR00BQBM2',
    //         wayline_job_info_id: 340,
    //         job_id: 'b8c58bfb-4352-47c5-ba33-242eec8df188',
    //     },
    //     {
    //         id: 11558,
    //         status: 3,
    //         begin_time: '2025/04/19 05:39:17',
    //         end_time: '2025/04/19 05:39:17',
    //         create_time: '2025-04-19T05:39:27.407+00:00',
    //         name: '智引即飞202504191252',
    //         event_number: 0,
    //         dock_sn: '7CTDLCR00BQBM2',
    //         wayline_job_info_id: 340,
    //         job_id: '839773ce-bd46-4947-a2ef-d859eaea82f6',
    //     },
    //     {
    //         id: 11557,
    //         status: 5,
    //         begin_time: '2025/04/19 04:52:04',
    //         end_time: '2025/04/19 04:52:04',
    //         create_time: '2025-04-19T04:52:14.503+00:00',
    //         name: '智引即飞202504191252',
    //         event_number: 0,
    //         dock_sn: '7CTDLCR00BQBM2',
    //         wayline_job_info_id: 340,
    //         job_id: '58366fd4-8361-456f-8ad9-3b881f933620',
    //     },
    //     {
    //         id: 11556,
    //         status: 5,
    //         begin_time: '2025/04/19 03:45:06',
    //         end_time: '2025/04/19 03:45:06',
    //         create_time: '2025-04-19T03:45:16.522+00:00',
    //         name: '智引即飞202504191145',
    //         event_number: 0,
    //         dock_sn: '7CTDLCR00BQBM2',
    //         wayline_job_info_id: 338,
    //         job_id: '39c60fb8-e5c4-4474-912a-c3af3f1b9054',
    //     },
    //     {
    //         id: 11555,
    //         status: 5,
    //         begin_time: '2025/04/19 03:27:55',
    //         end_time: '2025/04/19 03:27:55',
    //         create_time: '2025-04-19T03:28:05.196+00:00',
    //         name: '智引即飞202504191127',
    //         event_number: 0,
    //         dock_sn: '7CTDLCR00BQBM2',
    //         wayline_job_info_id: 336,
    //         job_id: '78c42532-45e4-4a01-b698-69bca19286ff',
    //     },
    //     {
    //         id: 11554,
    //         status: 5,
    //         begin_time: '2025/04/19 02:58:53',
    //         end_time: '2025/04/19 02:58:53',
    //         create_time: '2025-04-19T02:59:03.602+00:00',
    //         name: '智引即飞202504191058',
    //         event_number: 0,
    //         dock_sn: '7CTDLCR00BQBM2',
    //         wayline_job_info_id: 335,
    //         job_id: 'd3b25edc-ec23-4ad6-800a-0f659a72bff3',
    //     },
    //     {
    //         id: 11553,
    //         status: 5,
    //         begin_time: '2025/04/19 02:52:21',
    //         end_time: '2025/04/19 02:52:21',
    //         create_time: '2025-04-19T02:52:31.415+00:00',
    //         name: '智引即飞202504191052',
    //         event_number: 0,
    //         dock_sn: '7CTDLCR00BQBM2',
    //         wayline_job_info_id: 334,
    //         job_id: 'e27090f1-e740-4ae4-8d4d-301354eb0bbc',
    //     },
    //     {
    //         id: 11548,
    //         status: 5,
    //         begin_time: '2025/04/19 02:23:06',
    //         end_time: '2025/04/19 02:23:06',
    //         create_time: '2025-04-19T02:23:16.151+00:00',
    //         name: '智引即飞202504191023',
    //         event_number: 0,
    //         dock_sn: '7CTDLCR00BQBM2',
    //         wayline_job_info_id: 332,
    //         job_id: '9e1f7283-fe34-4cfd-b468-cd2b34a96d34',
    //     },
    //     {
    //         id: 11547,
    //         status: 3,
    //         begin_time: '2025/04/19 02:18:24',
    //         end_time: '2025/04/19 02:18:24',
    //         create_time: '2025-04-19T02:18:34.590+00:00',
    //         name: '智引即飞202504191018',
    //         event_number: 0,
    //         dock_sn: '7CTDLCR00BQBM2',
    //         wayline_job_info_id: 331,
    //         job_id: '7aa5222c-2f8b-401e-b6ba-d6a550398829',
    //     },
    // ]
    if (tableList.value.length > 0) {
        await getTaskDetails(tableList.value[0].wayline_job_info_id)
    }
})
useTaskWayline(window.$viewer, 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;
        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 {
            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 {
          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;
      }
    }
 }
            :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 {
                    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 {
                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>