src/api/home/machineNest.js
@@ -58,4 +58,13 @@ }) } // 事件列表---点位上图 export const getEventList = (data, params) => { return request({ url: '/drone-device-core/jobEvent/eventList', method: 'post', data, params }) } src/assets/images/home/useEventOperate/offline.png
src/assets/images/newEndPointicon.png
src/assets/images/newStartPoint.png
src/assets/images/newarrow-right.png
src/assets/images/task/cancel.png
src/assets/images/task/publish.png
src/hooks/components/DevicePopUpBox.vue
src/hooks/components/EventPopUpBox.vue
src/hooks/useMapAggregation/useMapAggregation.js
@@ -4,8 +4,8 @@ import uavImg from '@/assets/images/home/useUavHome/uavImg.png' import eventSingle from '@/assets/images/home/useEventOperate/eventSingle.png' import DevicePopUpBox from '@/hooks/useMapAggregation/DevicePopUpBox.vue' import EventPopUpBox from '@/hooks/useMapAggregation/EventPopUpBox.vue' import DevicePopUpBox from '@/hooks/components/DevicePopUpBox.vue' import EventPopUpBox from '@/hooks/components/EventPopUpBox.vue' import { render } from 'vue' import { useStore } from 'vuex' src/hooks/useSingleDroneMap/useSingleDroneMap.js
New file @@ -0,0 +1,236 @@ /* * @Author: shuishen 1109946754@qq.com * @Date: 2025-04-15 22:41:40 * @LastEditors: shuishen 1109946754@qq.com * @LastEditTime: 2025-04-16 18:38:00 * @FilePath: \command-center-dashboard\src\hooks\useSingleDroneMap\useSingleDroneMap.js * @Description: * * Copyright (c) 2025 by shuishen, All Rights Reserved. */ import * as Cesium from 'cesium' import endingImg from '@/assets/images/aiNowFly/ending.png' import endingHighImg from '@/assets/images/aiNowFly/ending-high.png' /** * * @param {Object} options - 配置选项 */ export function useSingleDroneMap (options = { eventPositions: [], eventApi: null, eventApiParams: {} }) { const { eventPositions, eventApi, eventApiParams } = options let viewer = null let handler = null let currentEntity = null // 初始化机场位置 const initDroneEntity = (dronePosition) => { const { lng, lat, status } = dronePosition if (!lng || !lat) return const markerImg = status ? endingHighImg : endingImg const position = Cesium.Cartesian3.fromDegrees(+lng, +lat, 0) const droneEntity = viewer?.entities.add({ id: `single-drone-position`, position: position, billboard: { image: new Cesium.ConstantProperty(markerImg), width: 35, height: 35, }, ellipse: { semiMinorAxis: 5000, semiMajorAxis: 5000, outline: true, material: Cesium.Color.CORNFLOWERBLUE.withAlpha(0.3), }, properties: { customData: { data: { type: 'single-drone-event' } } } }) viewer?.flyTo(droneEntity, { offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-60), 0), duration: 0.5, }) } // 机巢事件地图撒点 const initDroneEventEntity = (eventPositions = eventPositions) => { // 遍历特征并添加实体 eventPositions.length && eventPositions.forEach((item, index) => { const { longitude, latitude, status, id } = item const position = Cesium.Cartesian3.fromDegrees(+longitude, +latitude, 0) viewer?.entities.add({ id: `single-drone-event-${index}`, position: position, billboard: { image: new Cesium.ConstantProperty(endingHighImg), width: 35, height: 35, }, properties: { customData: { data: { type: 'single-drone-sign' } } }, }) }) } // 调用传入api const initEventApiEntity = () => { removeEventLayer() eventApi( { ...eventApiParams, } ).then(res => { const result = res.data.data.records initDroneEventEntity(result) }) } // 事件地图撒点加载方式 const initEventLayer = () => { eventApi ? initEventApiEntity() : initDroneEventEntity() } const removeEventLayer = () => { // entities移除 const entitiesIDs = viewer?.entities.values.map(i => i.id) entitiesIDs && entitiesIDs.forEach(item => { item.includes('single-drone-event') && viewer?.entities.removeById(item) }) } // 移除当前地图所有entity const removeLayer = () => { // entities移除 const entitiesIDs = viewer?.entities.values.map(i => i.id) entitiesIDs.forEach(item => { item.includes('single-drone-') && viewer?.entities.removeById(item) }) } // 查找特定类型的实体 const findEntityByType = (entities, type) => { return entities.find(entity => entity?.properties?.customData?._value?.data?.type === type ) } // 左键单机事件 const singleMachineEvent = async click => { let clickedEntities = viewer?.scene.drillPick(click.position).map(item => item.id) if (!clickedEntities.length) return const currentEntity = findEntityByType(clickedEntities, 'single-drone-event') removeLabel() if (currentEntity) { viewer?.scene.postRender.addEventListener(labelBoxRender) } } // 事件初始化 const handlerInit = () => { if (handler) return handler = new Cesium.ScreenSpaceEventHandler(viewer?.scene.canvas) handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK) } // 事件移除 const removeHandler = () => { handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK) handler?.destroy() handler = null } // 获取弹框box const getLabelDom = data => { const vNode = h(EventPopUpBox, { data, removeLabel }) const tooltipContainer = document.createElement('div') tooltipContainer.id = 'mapPopUpBox' tooltipContainer.style.position = 'absolute' tooltipContainer.style.transform = styleTransform tooltipContainer.style.pointerEvents = 'none' document.querySelector('.page-index').append(tooltipContainer) render(vNode, tooltipContainer) return tooltipContainer } // 弹框位置刷新 const labelBoxRender = () => { if (!currentEntity) return let dom = document.querySelector('#mapPopUpBox') if (!dom) { dom = getLabelDom(currentEntity.properties.customData._value.data) } const screenPosition = viewer?.scene.cartesianToCanvasCoordinates(currentEntity?.position?._value) if (screenPosition) { dom.style.left = `${screenPosition.x}px` dom.style.top = `${screenPosition.y}px` dom.style.display = 'block' } } const removeDom = () => { const dom = document.querySelector('#mapPopUpBox') if (dom && dom.parentNode) { dom.parentNode.removeChild(dom) } } // 移除弹框标签 const removeLabel = () => { viewer?.scene.postRender.removeEventListener(labelBoxRender) removeDom() } const init = () => { viewer = window.$viewer handlerInit() } const removeAll = () => { removeLayer() removeHandler() removeLabel() } // 自动清理 onUnmounted(() => { removeAll() }) return { init, removeAll, initEventLayer, initDroneEntity, } } src/views/SignMachineNest/MachineRight/MachineStatus/MachineTableDetails/DeviceJob/DeviceJobDetails/DeviceJobDetails.vue
@@ -1,4 +1,4 @@ <!-- 任务详情 --> <!-- 历史任务详情 --> <template> <el-dialog class="ztzf-dialog" @@ -24,7 +24,7 @@ </div> <div class="itemBoxRight"> <div class="itemTitle">关联算法:</div> <div class="itemValue">{{ flystatus }}</div> <div class="itemValue">{{ flystatus ? flystatus :'' }}</div> </div> </div> <JobRelatedEvents v-if="isShow" /> @@ -123,7 +123,7 @@ if (item.name === '任务时间') { item.value = detailsData.value.begin_time.slice(0, 10) + '-' + detailsData.value.end_time.slice(0, 10) } else if (item.name === '任务频次') { item.value = detailsData.value.rep_rule_type + ' -- ' + detailsData.value.rep_rule_val item.value ===undefined ? '' : detailsData.value.rep_rule_type + ' -- ' + detailsData.value.rep_rule_val } else { item.value = detailsData.value?.[item.field] || '' } @@ -240,14 +240,16 @@ } .imgListBox { display: flex; flex-wrap: wrap; // display: flex; // flex-wrap: wrap; display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px; .imgItem { width: 200px; height: 200px; } margin-bottom: 49px; // .imgItem { // width: 200px; // height: 200px; // } } } src/views/SignMachineNest/SignMachineNest.vue
@@ -5,31 +5,33 @@ <script setup> import MachineLeft from '@/views/SignMachineNest/MachineLeft/MachineLeft.vue' import MachineRight from '@/views/SignMachineNest/MachineRight/MachineRight.vue'; import { useConnectWebSocket } from '@/utils/websocket/connect-websocket'; import { getWebsocketUrl } from '@/websocket/util/config'; import { EBizCode } from '@/utils/staticData/enums.js'; import { EModeCode } from '@/utils/staticData/device.js'; import { useStore } from 'vuex'; import { getDeviceDetail, getFlightStatistics } from '@/api/home/machineNest'; import MachineRight from '@/views/SignMachineNest/MachineRight/MachineRight.vue' import { useConnectWebSocket } from '@/utils/websocket/connect-websocket' import { getWebsocketUrl } from '@/websocket/util/config' import { EBizCode } from '@/utils/staticData/enums.js' import { EModeCode } from '@/utils/staticData/device.js' import { useStore } from 'vuex' import { getDeviceDetail, getFlightStatistics, getEventList } from '@/api/home/machineNest' import { useSingleDroneMap } from '@/hooks/useSingleDroneMap/useSingleDroneMap' const store = useStore(); let connectWs = ref(null); const store = useStore() let connectWs = ref(null) // 单个机巢信息 const singleUavHome = computed(() => store.state.home.singleUavHome); const singleUavHome = computed(() => store.state.home.singleUavHome) const selectedAreaCode = computed(() => store.state.user.selectedAreaCode) let osdVisible = ref({}); let osdVisible = ref({}) let workspaceId = ref(''); let workspaceId = ref('') // 进入单个机巢开始连接ws const createWsConntect = () => { let webSorketUrl = getWebsocketUrl() + '&workspace-id=' + workspaceId.value; let webSorketUrl = getWebsocketUrl() + '&workspace-id=' + workspaceId.value // 监听ws 消息 connectWs.value = useConnectWebSocket(messageHandler, webSorketUrl); }; connectWs.value = useConnectWebSocket(messageHandler, webSorketUrl) } const messageHandler = (result) => { const messageHandler = result => { let payload = JSON.parse(result) // 为了兼容聊天消息 if (!payload) return switch (payload.biz_code) { @@ -50,14 +52,29 @@ default: break } }; } // 单机巢初始化及事件撒点 const { init, initEventLayer, initDroneEntity } = useSingleDroneMap({ eventApi: getEventList, eventApiParams: { device_sn: singleUavHome.value.device_sn, }, }) // 获取单个机巢信息 const getSingleDetails = () => { getDeviceDetail(singleUavHome.value.device_sn).then((res) => { if (res.data.code !== 0) return; const result = res.data.data; const child = result.children; getDeviceDetail(singleUavHome.value.device_sn).then(res => { if (res.data.code !== 0) return const result = res.data.data initDroneEntity({ lng: result.longitude, lat: result.latitude, status: result.status, }) const child = result.children // 对应store 里面数据结构 osdVisible.value.sn = child?.device_sn || '' osdVisible.value.callsign = child?.nickname || '--' @@ -71,28 +88,32 @@ osdVisible.value.device_sub_type = child.sub_type || 1 osdVisible.value.device_type = child.type || 0 // osdVisible.value.latest_wayline_job = result?.latest_wayline_job || {} store.commit('setOsdVisibleInfo', osdVisible.value); store.commit('setSelectedWorkSpaceId', result.workspace_id); workspaceId.value = result.workspace_id; createWsConntect(); }); }; store.commit('setOsdVisibleInfo', osdVisible.value) store.commit('setSelectedWorkSpaceId', result.workspace_id) workspaceId.value = result.workspace_id createWsConntect() }) } // 获取机巢统计数据 提供给左右侧组件使用 const getMachineData = () => { getFlightStatistics(singleUavHome.value.device_sn).then(res => { if (res.data.code !== 0) return; const result = res.data.data; store.commit('setSingleTotal', result); if (res.data.code !== 0) return const result = res.data.data store.commit('setSingleTotal', result) }) }; } onMounted(() => { getSingleDetails(); getMachineData(); }); nextTick(() => { init() }) initEventLayer() getSingleDetails() getMachineData() }) onUnmounted(() => { connectWs?.value?.close(); }); connectWs?.value?.close() }) </script> src/views/TaskManage/TaskIntermediateContent/AddTask.vue
@@ -1,49 +1,71 @@ <!-- 机巢列表详情 --> <!-- 新建任务 --> <template> <el-dialog class="ztzf-dialog" modal-class="add-task" v-model="isShowAddTask" title="新建任务" :width="pxToRem(1500)" :close-on-click-modal="false" :destroy-on-close="true"> :destroy-on-close="true" > <!-- <el-divider content-position="left">新建任务</el-divider> --> <div class="task-contain"> <div class="left"> <div class="search"> <div class="item"><span class="required">*</span>任务名称:<el-input v-model="searchForm.name" placeholder="请输入任务名称"></el-input></div> <div class="item"><span class="required">*</span>任务日期: <div class="item"> <el-input class="ztzf-input" v-model="searchForm.name" placeholder="请输入任务/机巢名称"></el-input> </div> <div class="item"> <el-date-picker class="ztzf-date-picker" v-model="taskData" format="YYYY-MM-DD" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD" :disabled-date="disabledDate" @change="changeselect" /> </div> <div class="item">任务时间: <div class="item"> <div class="itemchild">任务时间:</div> <el-time-picker class="ztzf-date-picker tasktimer" v-model="timeSlot" placeholder="选择时间" placeholder="请选择" format="HH:mm" value-format="HH:mm"/> value-format="HH:mm" /> </div> <div class="item">选择航线: <div class="item"> <div class="itemchild">选择航线:</div> <el-select class="ztzf-select" v-model="searchForm.file_id" @change="getWayLineFile" placeholder="请选择航线" clearable> clearable > <el-option v-for="item in routeOptions" :key="item.wayline_id" :label="item.name" :value="item.wayline_id"/> :value="item.wayline_id" /> </el-select> </div> <div class="item">关联算法: <TaskAlgorithmBusiness :showAlgorithm="true" @algorithmChange="algorithmChange"/> <div class="item"> <div class="itemchild">关联算法:</div> <TaskAlgorithmBusiness :setWidth="200" :showAlgorithm="true" @algorithmChange="algorithmChange" /> </div> <div class="item">任务描述:<el-input v-model="searchForm.remark" placeholder="请输入任务名称"></el-input></div> <div class="item"> <div class="itemchild">任务描述:</div> <el-input class="ztzf-input" v-model="searchForm.remark" placeholder="请输入任务名称"></el-input> </div> </div> <div class="lines"> <div class="wayline-type"> @@ -52,7 +74,8 @@ <el-radio :value="2" label="智能规划选区"></el-radio> </el-radio-group> </div> <TaskMap class="wayline-map" <TaskMap class="wayline-map" :wayLineFile="wayLineFile" :checkedTableData="checkedTableData" :waylineTypeTest="waylineType" @@ -62,7 +85,8 @@ </div> </div> <div class="right"> <TaskTable ref="taskTableRef" <TaskTable ref="taskTableRef" :waylineId="waylineId" :waylineType="waylineType" :singlePoint="singlePoint" @@ -70,8 +94,9 @@ @update:selected="handleSelected" /> <div class="btn"> <el-button type="primary" @click="cancel">取消</el-button> <el-button type="primary" @click="submitClick">发布</el-button> <img @click="cancel" style="margin-right:23px" src="@/assets/images/task/cancel.png" alt=""> <img @click="submitClick" src="@/assets/images/task/publish.png" alt=""> </div> </div> </div> @@ -79,26 +104,26 @@ </template> <script setup> import { ElMessage } from 'element-plus'; import { pxToRem } from '@/utils/rem'; import { getWaylineList, createTask } from '@/api/home/task'; import TaskAlgorithmBusiness from '../components/TaskAlgorithmBusiness.vue'; import TaskMap from './TaskMap.vue'; import TaskTable from './TaskTable.vue'; import { ElMessage } from 'element-plus' import { pxToRem } from '@/utils/rem' import { getWaylineList, createTask } from '@/api/home/task' import TaskAlgorithmBusiness from '../components/TaskAlgorithmBusiness.vue' import TaskMap from './TaskMap.vue' import TaskTable from './TaskTable.vue' const emit = defineEmits(['refresh']); const emit = defineEmits(['refresh']) const rangDate = ref([]); const rangDate = ref([]) // 航线ID const waylineId = ref(''); const waylineId = ref('') // 航线文件 const wayLineFile = ref(''); const wayLineFile = ref('') // 航线类型 const waylineType = ref(3); const waylineType = ref(3) // 添加子组件引用 const taskTableRef = ref(null); const taskData = ref(''); const timeSlot = ref(''); const taskTableRef = ref(null) const taskData = ref('') const timeSlot = ref('') const searchForm = reactive({ name: '', ai_types: [], @@ -112,152 +137,152 @@ longitude: '', latitude: '', polygon: [], }); const isShowAddTask = defineModel('show'); }) const isShowAddTask = defineModel('show') // 禁用当天之前的日期 const disabledDate = (time) => { return time.getTime() < Date.now() - 8.64e7; // 86400000 = 24 * 60 * 60 * 1000 }; const disabledDate = time => { return time.getTime() < Date.now() - 8.64e7 // 86400000 = 24 * 60 * 60 * 1000 } // 获取航线列表数据 const routeOptions = ref([]); const routeOptions = ref([]) const getRouteList = async () => { const res = await getWaylineList(); const res = await getWaylineList() if (res.data.code === 0) { routeOptions.value = res.data.data; routeOptions.value = res.data.data } }; } // 关联算法 const algorithmChange = (val) => { searchForm.ai_types = val; }; const algorithmChange = val => { searchForm.ai_types = val } // 切换航线类型 const radioChange = (val) => { const radioChange = val => { // 清空选中航线值 searchForm.file_id = ''; searchForm.file_id = '' // 航线ID也清空 waylineId.value = ''; wayLineFile.value = ''; waylineType.value = val; searchForm.type = val; waylineId.value = '' wayLineFile.value = '' waylineType.value = val searchForm.type = val // 清空列表数据 nextTick(() => { if (taskTableRef.value) { taskTableRef.value.clearTableData(); taskTableRef.value.clearTableData() } }); }; }) } // 获取航线文件 const getWayLineFile = async (val) => { searchForm.type = 0; waylineType.value = 0; waylineId.value = val; const currentRoute = routeOptions.value.find(item => item.wayline_id === val); wayLineFile.value = currentRoute.object_key; }; const getWayLineFile = async val => { searchForm.type = 0 waylineType.value = 0 waylineId.value = val const currentRoute = routeOptions.value.find(item => item.wayline_id === val) wayLineFile.value = currentRoute.object_key } // 获取选中机场列表数据,并且发布 let checkedTableData = ref([]); const handleSelected = (val) => { searchForm.dock_sns = val.map(item => item.device_sn); checkedTableData.value = val; }; let checkedTableData = ref([]) const handleSelected = val => { searchForm.dock_sns = val.map(item => item.device_sn) checkedTableData.value = val } // 地图点击事件 表格重新刷新数据 let singlePoint = ref({}); const clickSignPosition = (val) => { singlePoint.value = val; searchForm.longitude = val.longitude; searchForm.latitude = val.latitude; }; let singlePoint = ref({}) const clickSignPosition = val => { singlePoint.value = val searchForm.longitude = val.longitude searchForm.latitude = val.latitude } // 保存面状航线 let planarPoints = ref([]); const savePlanerWayline = (val) => { planarPoints.value = val; const polygonArray = val.map(point => [point.longitude, point.latitude]); searchForm.polygon = polygonArray; }; let planarPoints = ref([]) const savePlanerWayline = val => { planarPoints.value = val const polygonArray = val.map(point => [point.longitude, point.latitude]) searchForm.polygon = polygonArray } // 提交 const submitClick = () => { if (!taskData.value) { ElMessage({ message: '请选择任务日期', type: 'warning' }); return; type: 'warning', }) return } if (!searchForm.name) { ElMessage({ message: '请输入任务名称', type: 'warning' }); return; type: 'warning', }) return } // 检查任务时间 if (timeSlot.value) { const now = new Date(); const today = now.toDateString(); const selectedDate = new Date(taskData.value).toDateString(); const now = new Date() const today = now.toDateString() const selectedDate = new Date(taskData.value).toDateString() if (today === selectedDate) { const [hours, minutes] = timeSlot.value.split(':'); const selectedTime = new Date(); selectedTime.setHours(parseInt(hours), parseInt(minutes)); const [hours, minutes] = timeSlot.value.split(':') const selectedTime = new Date() selectedTime.setHours(parseInt(hours), parseInt(minutes)) if (selectedTime < now) { ElMessage({ message: '任务时间不能小于当前时间', type: 'warning' }); return; type: 'warning', }) return } } } if (searchForm.dock_sns.length === 0) { ElMessage({ message: '请选择机场', type: 'warning' }); return; type: 'warning', }) return } searchForm.begin_time = `${taskData.value} 00:00:00`; searchForm.end_time = `${taskData.value} 23:59:59`; searchForm.execute_time_arr = timeSlot.value ? [timeSlot.value] : []; createTask(searchForm).then((res) => { searchForm.begin_time = `${taskData.value} 00:00:00` searchForm.end_time = `${taskData.value} 23:59:59` searchForm.execute_time_arr = timeSlot.value ? [timeSlot.value] : [] createTask(searchForm).then(res => { if (res.data.code === 0) { ElMessage.success('任务创建成功'); ElMessage.success('任务创建成功') // 关闭当前窗口,刷新任务管理列表 isShowAddTask.value = false; isShowAddTask.value = false // 清除数据 cancel(); emit('refresh'); cancel() emit('refresh') } }); }; }) } const cancel = () => { isShowAddTask.value = false; isShowAddTask.value = false // 清除搜索数据 searchForm.name = ''; searchForm.ai_types = []; searchForm.file_id = ''; searchForm.begin_time = ''; searchForm.end_time = ''; timeSlot.value = ''; searchForm.remark = ''; searchForm.dock_sns = []; rangDate.value = []; waylineId.value = ''; wayLineFile.value = ''; taskData.value = ''; }; searchForm.name = '' searchForm.ai_types = [] searchForm.file_id = '' searchForm.begin_time = '' searchForm.end_time = '' timeSlot.value = '' searchForm.remark = '' searchForm.dock_sns = [] rangDate.value = [] waylineId.value = '' wayLineFile.value = '' taskData.value = '' } onMounted(() => { getRouteList(); getRouteList() }) </script> @@ -273,8 +298,10 @@ <style lang="scss" scoped> .task-contain { display: flex; padding: 20px 32px 32px 27px; .left { width: 68%; margin-right: 35px; .search { display: grid; grid-template-columns: repeat(3, 1fr); @@ -285,23 +312,17 @@ position: relative; display: flex; align-items: center; :deep(.el-input), :deep(.el-select), :deep(.el-date-picker), :deep(.el-time-picker) { width: 240px; // margin-left: 10px; font-size: 16px; color: #ffffff; .itemchild { width: 68px; white-space: nowrap; margin-right: 5px; } :deep(.el-date-editor.el-input__wrapper) { width: 200px; // 调整日期选择器宽度 } .required { color: #f56c6c; margin-left: 4px; position: absolute; left: -10px; top: 8px; } } } } @@ -312,13 +333,37 @@ justify-content: space-between; .btn { margin-top: 20px; text-align: center; display: flex; justify-content: center; align-items: center; img { width: 137px; height: 32px; cursor: pointer; } .el-button { margin: 0 10px; } } :deep(.tasktimer .el-input__wrapper) { background: none !important; color: #8ac3fd !important; } :deep(.el-input__inner) { color: #8ac3fd !important; &::placeholder { color: #8ac3fd !important; } } :deep(.el-radio__inner){ background: none !important; border: 1px solid #1B5D9A !important; } :deep(.el-radio__label){ color: #fff !important; } :deep(.el-radio__inner:after ){ background: #65B5FF !important; } } </style> src/views/TaskManage/TaskIntermediateContent/TaskMap.vue
@@ -7,33 +7,37 @@ import AmapMercatorTilingScheme from '@/utils/cesium/AmapMercatorTilingScheme' import { analyzeKmzFile, removeTextKey, XMLToJSON } from '@/utils/cesium/kmz' import ImageTrailMaterial from '@/utils/cesium/ImageTrailMaterial' import lineImg from '@/assets/images/arrow-right-blue.png'; import Startingpointicon from '@/assets/images/Startingpointicon.png'; import EndPointicon from '@/assets/images/EndPointicon.png'; import lineImg from '@/assets/images/arrow-right-blue.png' import Startingpointicon from '@/assets/images/Startingpointicon.png' import EndPointicon from '@/assets/images/EndPointicon.png' import uavImg from '@/assets/images/home/useUavHome/uavImg.png' import { getCenterPoint } from '@/utils/cesium/mapUtil' import { getWaylineByArea } from '@/api/home/task'; import { useStore } from 'vuex'; const store = useStore(); const userAreaPosition = computed(() => store.state.home.userAreaPosition); import { getWaylineByArea } from '@/api/home/task' import { useStore } from 'vuex' import { addBlueFilter } from '@/utils/cesium/common' // 新图片 import newStartPoint from '@/assets/images/newStartPoint.png' import newEndPointImg from '@/assets/images/newEndPointicon.png' import newlineImg from '@/assets/images/newarrow-right.png' const store = useStore() const userAreaPosition = computed(() => store.state.home.userAreaPosition) // 声明事件 const emit = defineEmits(['clickPosition', 'saveWayline']); const emit = defineEmits(['clickPosition', 'saveWayline']) const props = defineProps({ wayLineFile: { type: String, default: '' default: '', }, checkedTableData: { type: Array, default: () => [] default: () => [], }, waylineTypeTest: { type: Number, default: 3 } default: 3, }, }) const imageryProvider_ammapSL = new Cesium.UrlTemplateImageryProvider({ @@ -46,17 +50,16 @@ maximumLevel: 18, tilingScheme: new AmapMercatorTilingScheme(), credit: 'amap_SL', }); }) let viewer = null; let currentEntity = null; let connectLines = []; // 存储连接线实体 let polygonPoints = []; // 存储多边形的点 let polygonEntity = null; // 存储多边形实体 let existingEntity = null; // 后端返回数据生成得面状线 let viewer = null let currentEntity = null let connectLines = [] // 存储连接线实体 let polygonPoints = [] // 存储多边形的点 let polygonEntity = null // 存储多边形实体 let existingEntity = null // 后端返回数据生成得面状线 // 添加变量跟踪当前菜单 let currentMenu = null; let currentMenu = null const init = () => { viewer = new Viewer('taskMap', { @@ -74,59 +77,60 @@ fullscreenButton: false, // sceneMode: Cesium.SceneMode.COLUMBUS_VIEW, }) viewer.imageryLayers.addImageryProvider(imageryProvider_ammapSL) viewer.scene.morphTo2D(0); const gdLayer = viewer.imageryLayers.addImageryProvider(imageryProvider_ammapSL) const options = { bInvertColor: true, bFilterColor: true, filterColor: '#4e70a6', } // 添加蓝色滤镜 addBlueFilter(options, viewer, gdLayer) viewer.scene.morphTo2D(0) //设置默认点 const { longitude = 115.763819, latitude = 28.787374, height = 10 } = userAreaPosition.value || {}; const { longitude = 115.763819, latitude = 28.787374, height = 10 } = userAreaPosition.value || {} viewer.camera.setView({ destination: Cartesian3.fromDegrees(longitude, latitude, height), }); }) } // 单点左键点击事件 const singlePointLeftClick = () => { viewer.screenSpaceEventHandler.setInputAction((click) => { viewer.screenSpaceEventHandler.setInputAction(click => { if (props.waylineTypeTest !== 1) return const cartesian = viewer.camera.pickEllipsoid( click.position, viewer.scene.globe.ellipsoid ); const cartesian = viewer.camera.pickEllipsoid(click.position, viewer.scene.globe.ellipsoid) if (cartesian) { // 清除之前的实体 viewer.entities.removeAll(); viewer.entities.removeAll() // 添加新点 const point = viewer.entities.add({ position: cartesian, point: { pixelSize: 10, color: Cesium.Color.WHITE } }); color: Cesium.Color.fromCssColorString('#1FFF69'), }, }) // 转换坐标 const cartographic = Cesium.Cartographic.fromCartesian(cartesian); const longitude = Cesium.Math.toDegrees(cartographic.longitude); const latitude = Cesium.Math.toDegrees(cartographic.latitude); const cartographic = Cesium.Cartographic.fromCartesian(cartesian) const longitude = Cesium.Math.toDegrees(cartographic.longitude) const latitude = Cesium.Math.toDegrees(cartographic.latitude) // 更新当前实体引用 currentEntity = point; currentEntity = point // 发送坐标 emit('clickPosition', { longitude, latitude }); emit('clickPosition', { longitude, latitude }) } }, Cesium.ScreenSpaceEventType.LEFT_CLICK); }; }, Cesium.ScreenSpaceEventType.LEFT_CLICK) } // 智能规划航线 const intelligentPlanning = () => { // 添加点击事件监听 viewer.screenSpaceEventHandler.setInputAction((click) => { viewer.screenSpaceEventHandler.setInputAction(click => { if (props.waylineTypeTest !== 2) return const cartesian = viewer.camera.pickEllipsoid( click.position, viewer.scene.globe.ellipsoid ); const cartesian = viewer.camera.pickEllipsoid(click.position, viewer.scene.globe.ellipsoid) if (cartesian) { // 添加新点 @@ -134,18 +138,18 @@ position: cartesian, point: { pixelSize: 10, color: Cesium.Color.WHITE } }); color: Cesium.Color.fromCssColorString('#1FFF69'), }, }) // 存储点位 polygonPoints.push(cartesian); polygonPoints.push(cartesian) // 当点击超过2个点时绘制多边形 if (polygonPoints.length > 2) { // 移除旧的多边形 if (polygonEntity) { viewer.entities.remove(polygonEntity); viewer.entities.remove(polygonEntity) } // 创建新的多边形 @@ -159,110 +163,109 @@ outlineColor: new Cesium.Color(0, 0.5, 1, 1), // 蓝 outlineWidth: 2, height: 0, // Set explicit height heightReference: Cesium.HeightReference.NONE // Disable terrain clamping } }); heightReference: Cesium.HeightReference.NONE, // Disable terrain clamping }, }) } // 转换坐标并发送 const cartographic = Cesium.Cartographic.fromCartesian(cartesian); const longitude = Cesium.Math.toDegrees(cartographic.longitude); const latitude = Cesium.Math.toDegrees(cartographic.latitude); const cartographic = Cesium.Cartographic.fromCartesian(cartesian) const longitude = Cesium.Math.toDegrees(cartographic.longitude) const latitude = Cesium.Math.toDegrees(cartographic.latitude) // emit('clickPosition', cartographic); } }, Cesium.ScreenSpaceEventType.LEFT_CLICK); }, Cesium.ScreenSpaceEventType.LEFT_CLICK) // 修改右键点击事件,添加菜单 viewer.screenSpaceEventHandler.setInputAction((movement) => { viewer.screenSpaceEventHandler.setInputAction(movement => { if (props.waylineTypeTest !== 2) return if (polygonPoints.length > 2) { // 清除之前的菜单 if (currentMenu) { document.body.querySelectorAll('.context-menu').forEach(menu => menu.remove()); document.body.querySelectorAll('.context-menu').forEach(menu => menu.remove()) } const menuContainer = document.createElement('div'); menuContainer.className = 'context-menu'; const menuContainer = document.createElement('div') menuContainer.className = 'context-menu' // 获取地图容器 const mapContainer = document.getElementById('taskMap'); const mapContainer = document.getElementById('taskMap') // 使用鼠标右键点击的实际位置 menuContainer.style.position = 'absolute'; menuContainer.style.left = `${movement.position.x}px`; menuContainer.style.top = `${movement.position.y}px`; menuContainer.style.zIndex = '1000'; menuContainer.style.position = 'absolute' menuContainer.style.left = `${movement.position.x}px` menuContainer.style.top = `${movement.position.y}px` menuContainer.style.zIndex = '1000' menuContainer.innerHTML = ` <div class="menu-item" id="saveWayline">保存航线</div> <div class="menu-item" id="cancelDraw">取消绘制</div> `; ` mapContainer.appendChild(menuContainer); currentMenu = menuContainer; mapContainer.appendChild(menuContainer) currentMenu = menuContainer // 添加全局点击事件监听 const handleClickOutside = (e) => { if (!menuContainer) return; const isClickInside = menuContainer.contains(e.target); const handleClickOutside = e => { if (!menuContainer) return const isClickInside = menuContainer.contains(e.target) if (!isClickInside) { menuContainer.remove(); document.removeEventListener('mousedown', handleClickOutside); menuContainer.remove() document.removeEventListener('mousedown', handleClickOutside) } }; } // 延迟添加事件监听,避免右键点击立即触发 setTimeout(() => { document.addEventListener('mousedown', handleClickOutside); }, 100); document.addEventListener('mousedown', handleClickOutside) }, 100) // 菜单按钮点击事件 document.getElementById('saveWayline').onclick = () => { const coordinates = polygonPoints.map(point => { const cartographic = Cesium.Cartographic.fromCartesian(point); const cartographic = Cesium.Cartographic.fromCartesian(point) return { longitude: Cesium.Math.toDegrees(cartographic.longitude), latitude: Cesium.Math.toDegrees(cartographic.latitude) }; }); emit('saveWayline', coordinates); saveWaylineByArea(coordinates); mapContainer.removeChild(menuContainer); }; latitude: Cesium.Math.toDegrees(cartographic.latitude), } }) emit('saveWayline', coordinates) saveWaylineByArea(coordinates) mapContainer.removeChild(menuContainer) } document.getElementById('cancelDraw').onclick = () => { polygonPoints = []; viewer.entities.removeAll(); mapContainer.removeChild(menuContainer); }; polygonPoints = [] viewer.entities.removeAll() mapContainer.removeChild(menuContainer) } }, Cesium.ScreenSpaceEventType.RIGHT_CLICK); }; } }, Cesium.ScreenSpaceEventType.RIGHT_CLICK) } // 保存航线并且获取线 const saveWaylineByArea = (dataValue) => { const polygonArray = dataValue.map(point => [point.longitude, point.latitude]); const saveWaylineByArea = dataValue => { const polygonArray = dataValue.map(point => [point.longitude, point.latitude]) getWaylineByArea({ type: 2, polygon: polygonArray }).then(res => { if (res.data.code !== 0) retrun; drawResultWayline(res.data.data); }); if (res.data.code !== 0) retrun drawResultWayline(res.data.data) }) } // 绘制后端生成得面状线 const drawResultWayline = (dataValue) => { const drawResultWayline = dataValue => { // 先检查并删除已存在的航线 existingEntity = viewer.entities.getById('result_wayline'); existingEntity = viewer.entities.getById('result_wayline') if (existingEntity) { viewer.entities.remove(existingEntity); viewer.entities.remove(existingEntity) } const cartesian3List = ref([]); dataValue.forEach((lnglat) => { const cartesian3List = ref([]) dataValue.forEach(lnglat => { const cartesian3 = Cesium.Cartesian3.fromDegrees( Number(lnglat.x), Number(lnglat.y), Number(100), // 默认100 Number(100) // 默认100 ) cartesian3List.value.push(cartesian3) }); }) const setting = { id: 'result_wayline', polyline: { @@ -275,7 +278,7 @@ polyline: setting.polyline, id: setting.id, }) }; } // 选中航线时调用 渲染线和点 type = 0 const renderingLine = lineObj => { @@ -286,20 +289,24 @@ viewer.entities.add({ polyline: { width: 4, width: 5, 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 }, // material: new ImageTrailMaterial({ // color: { alpha: 1, blue: 1, green: 1, red: 1 }, // speed: 20, // image: newlineImg, // repeat: { x: Math.floor(40), y: 1 }, // }), material: new Cesium.PolylineGlowMaterialProperty({ // color: Cesium.Color.GREEN, image: newlineImg, }), clampToGround: false, }, }) positions.forEach((point, index) => { let setting = {}; let setting = {} // if (index === 0) { // //TODO // } @@ -321,7 +328,7 @@ position: point, id: `point_${index}`, billboard: { image: EndPointicon, image: newEndPointImg, outlineWidth: 0, width: 20, height: 20, @@ -335,31 +342,34 @@ label: { text: `${index+1}`, font: 'bold 14px serif', style: Cesium.LabelStyle.FILL, verticalOrigin: Cesium.VerticalOrigin.CENTER, // 垂直居中 horizontalOrigin: Cesium.HorizontalOrigin.CENTER, // 水平居中 pixelOffset: new Cesium.Cartesian2(0, 0), // 根据需要调整偏移量 eyeOffset: new Cesium.Cartesian3(0, 0, -10) // 使标签在点的上方 fillColor: Cesium.Color.WHITE, // style: Cesium.LabelStyle.FILL, // verticalOrigin: Cesium.VerticalOrigin.CENTER, // 垂直居中 // horizontalOrigin: Cesium.HorizontalOrigin.CENTER, // 水平居中 pixelOffset: new Cesium.Cartesian2(2, 0), // 根据需要调整偏移量 eyeOffset: new Cesium.Cartesian3(0, 0, -10), // 使标签在点的上方 }, point: { pixelSize: 24, color: new Cesium.Color.fromBytes(255, 186, 0, 255), billboard: { image: new Cesium.ConstantProperty(newStartPoint), width: 70, height: 70, }, // point: { // pixelSize: 24, // color: new Cesium.Color.fromBytes(255, 186, 0, 255), // }, offset: new Cesium.Cartesian2(10, 30), } } viewer.entities.add(setting) }); }) } // 飞到中心点 function flyToPoints(lngLatArr) { if (!Array.isArray(lngLatArr) || lngLatArr.length === 0) return const positions = lngLatArr.map(([lon, lat]) => Cesium.Cartesian3.fromDegrees(Number(lon), Number(lat)) ) const positions = lngLatArr.map(([lon, lat]) => Cesium.Cartesian3.fromDegrees(Number(lon), Number(lat))) // 计算包围盒 BoundingSphere(所有点的外接球) const boundingSphere = Cesium.BoundingSphere.fromPoints(positions) viewer.camera.flyToBoundingSphere(boundingSphere, { @@ -368,11 +378,10 @@ }) } // 异步解析kmz文件 const analysis = async url => { return new Promise(async resolve => { const res = await analyzeKmzFile(`${url}?_t=${new Date().getTime()}`); const res = await analyzeKmzFile(`${url}?_t=${new Date().getTime()}`) const templateXML = await res.fileInfoObj['wpmz/template.kml'] const templateXMLJSON = XMLToJSON(templateXML)?.['Document'] const templateXMLObj = removeTextKey(templateXMLJSON.Folder) @@ -382,16 +391,16 @@ // 绘制线和飞行 const drawLine = async () => { let prexUrl = ref(import.meta.env.VITE_APP_AIRLINE_URL+ props.wayLineFile); const res = await analysis(prexUrl.value); let prexUrl = ref(import.meta.env.VITE_APP_AIRLINE_URL + props.wayLineFile) const res = await analysis(prexUrl.value) if (!res.Placemark.length) return renderingLine(res); renderingLine(res) const points = res.Placemark.map(item => item.Point.coordinates.split(',')) flyToPoints(points) } // 选择航线时,根据选择机巢连线 const selectLineFile = (newVal) => { const selectLineFile = newVal => { // 清除之前的实体 // viewer.entities.removeAll(); // 重新绘制航线 @@ -400,7 +409,7 @@ // } // 添加选中点和连接线 const positions = newVal.map(item => { const position = Cartesian3.fromDegrees(Number(item.longitude), Number(item.latitude)); const position = Cartesian3.fromDegrees(Number(item.longitude), Number(item.latitude)) // 添加机巢点 viewer.entities.add({ position: position, @@ -409,9 +418,9 @@ width: 24, height: 24, }, }); return position; }); }) return position }) // 添加连接线 if (positions.length > 1) { @@ -421,33 +430,30 @@ width: 2, material: new Cesium.PolylineDashMaterialProperty({ color: Cesium.Color.RED, dashLength: 8.0 dashLength: 8.0, }), }, }) } }); } // 飞到中心点 const lngLatArr = newVal.map(item => [item.longitude, item.latitude]); flyToPoints(lngLatArr); }; const lngLatArr = newVal.map(item => [item.longitude, item.latitude]) flyToPoints(lngLatArr) } // 单个点生成选择多个机巢生成航线 const singlePointLines = (newVal) => { const singlePointLines = newVal => { // 清除之前的连接线 connectLines.forEach(line => viewer.entities.remove(line)); connectLines = []; connectLines.forEach(line => viewer.entities.remove(line)) connectLines = [] if (currentEntity) { // 获取当前点的位置 const currentPosition = currentEntity.position.getValue(); const currentPosition = currentEntity.position.getValue() // 为每个选中的机巢创建点和连接线 newVal.forEach(item => { // 创建机巢点 const nestPosition = Cartesian3.fromDegrees( Number(item.longitude), Number(item.latitude) ); const nestPosition = Cartesian3.fromDegrees(Number(item.longitude), Number(item.latitude)) viewer.entities.add({ position: nestPosition, billboard: { @@ -455,7 +461,7 @@ width: 24, height: 24, }, }); }) // 创建连接线 const line = viewer.entities.add({ @@ -464,31 +470,28 @@ width: 2, material: new Cesium.PolylineDashMaterialProperty({ color: Cesium.Color.RED, dashLength: 8.0 dashLength: 8.0, }), }, }) } }); connectLines.push(line); }); connectLines.push(line) }) // 飞到所有点的中心位置 const lngLatArr = newVal.map(item => [item.longitude, item.latitude]); flyToPoints(lngLatArr); const lngLatArr = newVal.map(item => [item.longitude, item.latitude]) flyToPoints(lngLatArr) } }; } // 智慧规划航线-面状航线 const planarPointsLines = (newVal) => { const planarPointsLines = newVal => { // 如果存在面状航线 if (existingEntity) { const waylinePositions = existingEntity.polyline.positions.getValue(); const waylinePositions = existingEntity.polyline.positions.getValue() newVal.forEach(item => { // 创建机巢点 const nestPosition = Cartesian3.fromDegrees( Number(item.longitude), Number(item.latitude) ); const nestPosition = Cartesian3.fromDegrees(Number(item.longitude), Number(item.latitude)) // 添加机巢图标 viewer.entities.add({ @@ -498,19 +501,19 @@ width: 24, height: 24, }, }); }) // 找到最近的航线点并连线 let minDistance = Number.MAX_VALUE; let closestPosition = null; let minDistance = Number.MAX_VALUE let closestPosition = null waylinePositions.forEach(waylinePos => { const distance = Cartesian3.distance(nestPosition, waylinePos); const distance = Cartesian3.distance(nestPosition, waylinePos) if (distance < minDistance) { minDistance = distance; closestPosition = waylinePos; minDistance = distance closestPosition = waylinePos } }); }) // 创建连接线 if (closestPosition) { @@ -520,99 +523,110 @@ width: 2, material: new Cesium.PolylineDashMaterialProperty({ color: Cesium.Color.CHARTREUSE, dashLength: 8.0 dashLength: 8.0, }), }, }) connectLines.push(line) } }); connectLines.push(line); } }); }) // 飞到所有点的中心位置 const lngLatArr = newVal.map(item => [item.longitude, item.latitude]); flyToPoints(lngLatArr); const lngLatArr = newVal.map(item => [item.longitude, item.latitude]) flyToPoints(lngLatArr) } }; } // 监听选择航线文件事件 watch(() => props.wayLineFile, async (newVal, oldValue) => { await removeMap(); watch( () => props.wayLineFile, async (newVal, oldValue) => { await removeMap() if(newVal){ await drawLine(); await drawLine() } }, { deep: true }); }, { deep: true } ) // 监听表格选中数据变化 watch(() => props.checkedTableData, (newVal) => { watch( () => props.checkedTableData, newVal => { if (newVal.length > 0 && props.waylineTypeTest === 0) { selectLineFile(newVal); selectLineFile(newVal) } else if (newVal.length > 0 && props.waylineTypeTest === 1) { singlePointLines(newVal); singlePointLines(newVal) } else if (newVal.length > 0 && props.waylineTypeTest === 2) { planarPointsLines(newVal); planarPointsLines(newVal) } }, { deep: true }); }, { deep: true } ) // 监听航线类型 watch(() => props.waylineTypeTest, async (newVal) => { await removeMap(); watch( () => props.waylineTypeTest, async newVal => { await removeMap() if (newVal === 1) { await singlePointLeftClick(); await singlePointLeftClick() } else if (newVal === 2) { await intelligentPlanning(); await intelligentPlanning() } }, { deep: true }); }, { deep: true } ) const removeEvent = () => { // 清除事件监听器 viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK); viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK); }; viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK) viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK) } const removeMap = () => { // 清除所有实体 if (viewer) { // 清除连接线 connectLines.forEach(line => viewer.entities.remove(line)); connectLines = []; connectLines.forEach(line => viewer.entities.remove(line)) connectLines = [] // 清除多边形点和实体 polygonPoints = []; polygonPoints = [] if (polygonEntity) { viewer.entities.remove(polygonEntity); viewer.entities.remove(polygonEntity) } // 清除当前实体和面状航线 if (currentEntity) { viewer.entities.remove(currentEntity); viewer.entities.remove(currentEntity) } if (existingEntity) { viewer.entities.remove(existingEntity); viewer.entities.remove(existingEntity) } viewer.entities.removeAll(); viewer.entities.removeAll() // 重置所有变量 currentEntity = null; polygonEntity = null; existingEntity = null; currentEntity = null polygonEntity = null existingEntity = null } } onBeforeUnmount(() => { removeMap(); removeEvent(); removeMap() removeEvent() // 移除所有实体并销毁viewer viewer.destroy(); viewer = null; viewer.destroy() viewer = null }) onMounted(() => { nextTick(() => { init() }) }); }) </script> <style scoped lang="scss"> #taskMap { @@ -647,7 +661,7 @@ :deep(.context-menu) { position: absolute; background: rgba(0, 21, 41, 0.9);; background: rgba(0, 21, 41, 0.9); border-radius: 4px; padding: 8px 0; min-width: 120px; @@ -666,5 +680,4 @@ } } } </style> src/views/TaskManage/TaskIntermediateContent/TaskTable.vue
@@ -1,20 +1,32 @@ <template> <div class="table-container"> <el-table :data="tableData" style="width: 100%" height="400" <div class="table-container ztzf-table"> <div class="taskTableTitle"><span>可飞行机巢列表</span></div> <el-table :data="tableData" style="width: 100%" height="400" :row-style="{ height: '38px', fontSize: '14px', 'text-align': 'center' }" :header-cell-style="{ 'text-align': 'center', height: '36px', fontSize: '14px' }" @selection-change="handleSelectionChange" @select="handleSelect"> @select="handleSelect" > <el-table-column type="selection" width="55" :selectable="checkSelectable" /> <el-table-column type="index" label="序号" width="60" /> <el-table-column prop="nickname" label="机巢名称" /> <el-table-column prop="estimated_arrival_time" label="预计到达时间" /> <el-table-column prop="exe_distance" label="执行里程" /> <el-table-column type="index" label="序号" width="60" > <template #default="{ $index }"> {{ ($index + 1).toString().padStart(2, '0') }} </template> </el-table-column> <el-table-column show-overflow-tooltip prop="nickname" label="机巢名称" /> <el-table-column show-overflow-tooltip width="120" prop="estimated_arrival_time" label="预计到达时间" /> <el-table-column show-overflow-tooltip prop="exe_distance" label="执行里程" /> </el-table> <div class="pagination"> <el-pagination class="ztzf-pagination" v-model:current-page="pagingParams.page" v-model:page-size="pagingParams.size" :page-sizes="[10, 20, 30]" layout="total, sizes, prev, pager, next" layout=" prev, pager, next" :total="total" @size-change="handleSizeChange" @current-change="handleCurrentChange" @@ -24,29 +36,29 @@ </template> <script setup> import { getFlyingNestBy } from '@/api/home/task'; import { getFlyingNestBy } from '@/api/home/task' const props = defineProps({ waylineId: { type: String, default: '' default: '', }, waylineType: { type: Number, default: 3 default: 3, }, singlePoint: { type: Object, default: () => ({}) default: () => ({}), }, planarPoints: { type: Array, default: () => ([]) } }); default: () => [], }, }) // 选中的数据 const selectedRows = ref([]); const selectedRows = ref([]) // 分页参数 let pageParams = ref({ wayline_id: props.waylineId, @@ -54,57 +66,57 @@ longitude: props.singlePoint.longitude, latitude: props.singlePoint.latitude, polygon: [], }); }) let pagingParams = ref({ current: 1, size: 10 }); size: 10, }) // 获取可用机巢列表数据 const total = ref(0); const tableData = ref([]); const total = ref(0) const tableData = ref([]) const getNestList = async () => { tableData.value = []; const res = await getFlyingNestBy(pageParams.value, pagingParams.value); tableData.value = [] const res = await getFlyingNestBy(pageParams.value, pagingParams.value) if (res.data.code === 0) { tableData.value = res.data.data; tableData.value = res.data.data } }; } // 分页大小改变 const handleSizeChange = (val) => { pagingParams.value.size = val; getNestList(); }; const handleSizeChange = val => { pagingParams.value.size = val getNestList() } // 页码改变 const handleCurrentChange = (val) => { pagingParams.value.current = val; getNestList(); }; const handleCurrentChange = val => { pagingParams.value.current = val getNestList() } // 刷新数据 const refreshData = () => { tableData.value = []; pagingParams.value.current = 1; getNestList(); }; tableData.value = [] pagingParams.value.current = 1 getNestList() } const emit = defineEmits(['update:selected']); const handleSelectionChange = (val) => { const emit = defineEmits(['update:selected']) const handleSelectionChange = val => { // 如果不是智能规划选区模式,才更新所有选中数据 if (props.waylineType !== 2) { selectedRows.value = val; emit('update:selected', val); selectedRows.value = val emit('update:selected', val) } }; } // 控制表格行是否可选 const checkSelectable = (row) => { const checkSelectable = row => { if (props.waylineType === 2) { // 如果已经有选中的行,且当前行未被选中,则不允许选择 return selectedRows.value.length === 0 || selectedRows.value.some(selected => selected.device_sn === row.device_sn); return selectedRows.value.length === 0 || selectedRows.value.some(selected => selected.device_sn === row.device_sn) } return true; }; return true } // 处理单行选择 const handleSelect = (selection, row) => { @@ -112,78 +124,107 @@ // 如果是智能规划选区模式,确保只能选中一行 if (selection.length > 1) { // 保留最后选中的那一行 const lastSelected = selection[selection.length - 1]; selectedRows.value = [lastSelected]; const lastSelected = selection[selection.length - 1] selectedRows.value = [lastSelected] // 触发更新事件 emit('update:selected', selectedRows.value); emit('update:selected', selectedRows.value) } } }; } // 监听航线ID变化 watch(() => props.waylineId, (newVal) => { pageParams.value.type = 0; watch( () => props.waylineId, newVal => { pageParams.value.type = 0 if (newVal) { pageParams.value.wayline_id = newVal; refreshData(); pageParams.value.wayline_id = newVal refreshData() } else { // 数据为空时,清除列表数据 tableData.value = []; tableData.value = [] } }, { deep: true }); }, { deep: true } ) // 监听单点返回的数据 watch(() => props.singlePoint, (newVal) => { pageParams.value.wayline_id = ''; pageParams.value.type = 1; watch( () => props.singlePoint, newVal => { pageParams.value.wayline_id = '' pageParams.value.type = 1 if (newVal && newVal.latitude && newVal.longitude) { pageParams.value.latitude = newVal.latitude; pageParams.value.longitude = newVal.longitude; refreshData(); pageParams.value.latitude = newVal.latitude pageParams.value.longitude = newVal.longitude refreshData() } }, { deep: true }); }, { deep: true } ) // 监听面状航线返回的数据 watch(() => props.planarPoints, (newVal) => { pageParams.value.wayline_id = ''; pageParams.value.type = 2; watch( () => props.planarPoints, newVal => { pageParams.value.wayline_id = '' pageParams.value.type = 2 if (newVal && newVal.length > 0) { const polygonArray = newVal.map(point => [point.longitude, point.latitude]); pageParams.value.polygon = polygonArray; refreshData(); const polygonArray = newVal.map(point => [point.longitude, point.latitude]) pageParams.value.polygon = polygonArray refreshData() } }, { deep: true }); }, { deep: true } ) // 暴露给父组件的方法 const clearTableData = () => { tableData.value = []; }; tableData.value = [] } defineExpose({ clearTableData }); onMounted(() => {}); clearTableData, }) // 表格隔行变色 const tableRowClassName = ({ row, rowIndex }) => { if (rowIndex % 2 === 1) { return 'warning-row' } else { return 'success-row' } } onMounted(() => {}) </script> <style lang="scss" scoped> .table-container { height: 500px; // display: flex; // flex-direction: column; .pagination { margin-top: 10px; } // :deep(.el-table) { // flex: 1; // background-color: transparent; // --el-table-border-color: rgba(255, 255, 255, 0.1); // --el-table-header-bg-color: rgba(31, 62, 122, 0.5); // --el-table-header-text-color: #fff; // --el-table-text-color: #fff; // } // 分页 :deep(.el-pagination) { display: flex; justify-content: center; } .taskTableTitle { margin-bottom: 16px; background: url('/src/assets/images/signMachineNest/machineRight/detailtitle.png') no-repeat center; background-size: 100% 100%; span { display: inline-block; margin-left: 10px; font-size: 16px; color: #ddf0ff; line-height: 20px; text-align: left; margin-bottom: 8px; } } :deep(.el-checkbox__inner){ background: none !important; border: 1px solid #3194EF !important; } } </style>