Merge branch 'master' of http://139.196.74.78:10010/r/drone/command-center-dashboard
7 files modified
2 files renamed
7 files added
| | |
| | | |
| | | // 机巢统计 |
| | | export const getDeviceInfoNum = params => { |
| | | return request({ |
| | | url: '/drone-device-core/manage/api/v1/devices/getDeviceInfoNum', |
| | | method: 'get', |
| | | params, |
| | | }) |
| | | return request({ |
| | | url: '/drone-device-core/manage/api/v1/devices/getDeviceInfoNum', |
| | | method: 'get', |
| | | params, |
| | | }) |
| | | } |
| | | // 机巢列表 |
| | | export const selectDevicePage = ({ nickname, ...params }) => { |
| | | return request({ |
| | | url: `/drone-device-core/manage/api/v1/devices/selectDevicePage?type=${params.type}¤t=${params.current}&size=${params.size}`, |
| | | method: 'post', |
| | | data: { |
| | | nickname, |
| | | }, |
| | | }) |
| | | return request({ |
| | | url: `/drone-device-core/manage/api/v1/devices/selectDevicePage?type=${params.type}¤t=${params.current}&size=${params.size}`, |
| | | method: 'post', |
| | | data: { |
| | | nickname, |
| | | }, |
| | | }) |
| | | } |
| | | // 机巢数据 |
| | | export const getFlightStatistics = dockSn => { |
| | | return request({ |
| | | url: `/drone-device-core/manage/api/v1/devices/getFlightStatistics?dockSn=${dockSn}`, |
| | | method: 'get', |
| | | params: {}, |
| | | }) |
| | | return request({ |
| | | url: `/drone-device-core/manage/api/v1/devices/getFlightStatistics?dockSn=${dockSn}`, |
| | | method: 'get', |
| | | params: {}, |
| | | }) |
| | | } |
| | | // 机巢直播/无人机直播 均可使用 |
| | | export const liveStart = (deviceSn, quality) => { |
| | | return request({ |
| | | url: `/drone-device-core/manage/api/v1/live/streams/liveStart?deviceSn=${deviceSn}&quality=${quality}`, |
| | | method: 'post', |
| | | data: {}, |
| | | headers: { |
| | | areaCode: '', |
| | | }, |
| | | }) |
| | | return request({ |
| | | url: `/drone-device-core/manage/api/v1/live/streams/liveStart?deviceSn=${deviceSn}&quality=${quality}`, |
| | | method: 'post', |
| | | data: {}, |
| | | headers: { |
| | | areaCode: '', |
| | | }, |
| | | }) |
| | | } |
| | | // 单个机巢获取机巢详情 |
| | | export const getDeviceDetail = deviceSn => { |
| | | return request({ |
| | | url: `/drone-device-core/manage/api/v1/devices/getDeviceDetail?deviceSn=${deviceSn}`, |
| | | method: 'get', |
| | | params: {}, |
| | | }) |
| | | return request({ |
| | | url: `/drone-device-core/manage/api/v1/devices/getDeviceDetail?deviceSn=${deviceSn}`, |
| | | method: 'get', |
| | | params: {}, |
| | | }) |
| | | } |
| | | |
| | | |
| | | |
| | | // 设备-事件列表 |
| | | export const getDeviceEventList = (data,params) => { |
| | | return request({ |
| | | url: `/drone-device-core/jobEvent/eventPage`, |
| | | method: 'post', |
| | | data, |
| | | params |
| | | }) |
| | | export const getDeviceEventList = (data, params) => { |
| | | return request({ |
| | | url: `/drone-device-core/jobEvent/eventPage`, |
| | | method: 'post', |
| | | data, |
| | | params |
| | | }) |
| | | } |
| | | |
| | | // 事件列表---点位上图 |
| | | export const getEventList = (data, params) => { |
| | | return request({ |
| | | url: '/drone-device-core/jobEvent/eventList', |
| | | method: 'post', |
| | | data, |
| | | params |
| | | }) |
| | | } |
| | | |
| | |
| | | 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' |
| New file |
| | |
| | | /* |
| | | * @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, |
| | | } |
| | | } |
| | |
| | | <!-- 任务详情 --> |
| | | <!-- 历史任务详情 --> |
| | | <template> |
| | | <el-dialog |
| | | class="ztzf-dialog" |
| | |
| | | </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" /> |
| | |
| | | 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] || '' |
| | | } |
| | |
| | | } |
| | | |
| | | .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; |
| | | // } |
| | | } |
| | | } |
| | | |
| | |
| | | <template> |
| | | <MachineLeft></MachineLeft> |
| | | <MachineRight></MachineRight> |
| | | <MachineLeft></MachineLeft> |
| | | <MachineRight></MachineRight> |
| | | </template> |
| | | |
| | | <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; |
| | | // 监听ws 消息 |
| | | connectWs.value = useConnectWebSocket(messageHandler, webSorketUrl); |
| | | }; |
| | | let webSorketUrl = getWebsocketUrl() + '&workspace-id=' + workspaceId.value |
| | | // 监听ws 消息 |
| | | connectWs.value = useConnectWebSocket(messageHandler, webSorketUrl) |
| | | } |
| | | |
| | | const messageHandler = (result) => { |
| | | let payload = JSON.parse(result) // 为了兼容聊天消息 |
| | | if (!payload) return |
| | | switch (payload.biz_code) { |
| | | case EBizCode.GatewayOsd: { |
| | | store.commit('setGatewayInfo', payload.data) |
| | | break |
| | | } |
| | | case EBizCode.DeviceOsd: { |
| | | // console.log(payload, 'DeviceOsd') |
| | | store.commit('setDeviceInfo', payload) |
| | | store.commit('setWsMessage', payload) |
| | | break |
| | | } |
| | | case EBizCode.DockOsd: { |
| | | store.commit('setDockOnfo', payload.data) |
| | | break |
| | | } |
| | | default: |
| | | break |
| | | } |
| | | }; |
| | | const messageHandler = result => { |
| | | let payload = JSON.parse(result) // 为了兼容聊天消息 |
| | | if (!payload) return |
| | | switch (payload.biz_code) { |
| | | case EBizCode.GatewayOsd: { |
| | | store.commit('setGatewayInfo', payload.data) |
| | | break |
| | | } |
| | | case EBizCode.DeviceOsd: { |
| | | // console.log(payload, 'DeviceOsd') |
| | | store.commit('setDeviceInfo', payload) |
| | | store.commit('setWsMessage', payload) |
| | | break |
| | | } |
| | | case EBizCode.DockOsd: { |
| | | store.commit('setDockOnfo', payload.data) |
| | | break |
| | | } |
| | | 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; |
| | | // 对应store 里面数据结构 |
| | | osdVisible.value.sn = child?.device_sn || '' |
| | | osdVisible.value.callsign = child?.nickname || '--' |
| | | osdVisible.value.model = EModeCode.Disconnected || '' |
| | | osdVisible.value.visible = true |
| | | osdVisible.value.gateway_sn = result?.device_sn || '' |
| | | osdVisible.value.is_dock = true |
| | | osdVisible.value.gateway_callsign = result?.callsign || '--' |
| | | osdVisible.value.payloads = child?.payloads_list || [] |
| | | osdVisible.value.device_domain = child.domain || 0 |
| | | 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(); |
| | | }); |
| | | }; |
| | | 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 || '--' |
| | | osdVisible.value.model = EModeCode.Disconnected || '' |
| | | osdVisible.value.visible = true |
| | | osdVisible.value.gateway_sn = result?.device_sn || '' |
| | | osdVisible.value.is_dock = true |
| | | osdVisible.value.gateway_callsign = result?.callsign || '--' |
| | | osdVisible.value.payloads = child?.payloads_list || [] |
| | | osdVisible.value.device_domain = child.domain || 0 |
| | | 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() |
| | | }) |
| | | } |
| | | |
| | | // 获取机巢统计数据 提供给左右侧组件使用 |
| | | const getMachineData = () => { |
| | | getFlightStatistics(singleUavHome.value.device_sn).then(res => { |
| | | if (res.data.code !== 0) return; |
| | | const result = res.data.data; |
| | | store.commit('setSingleTotal', result); |
| | | }) |
| | | }; |
| | | getFlightStatistics(singleUavHome.value.device_sn).then(res => { |
| | | 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> |
| | |
| | | <!-- 机巢列表详情 --> |
| | | <!-- 新建任务 --> |
| | | <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"/> |
| | | </el-select> |
| | | </div> |
| | | <div class="item">关联算法: |
| | | <TaskAlgorithmBusiness :showAlgorithm="true" @algorithmChange="algorithmChange"/> |
| | | </div> |
| | | <div class="item">任务描述:<el-input v-model="searchForm.remark" placeholder="请输入任务名称"></el-input></div> |
| | | :value="item.wayline_id" |
| | | /> |
| | | </el-select> |
| | | </div> |
| | | <div class="item"> |
| | | |
| | | <div class="itemchild">关联算法:</div> |
| | | <TaskAlgorithmBusiness :setWidth="200" :showAlgorithm="true" @algorithmChange="algorithmChange" /> |
| | | </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"> |
| | |
| | | <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" |
| | | :waylineTypeTest="waylineType" |
| | | @clickPosition="clickSignPosition" |
| | | @saveWayline="savePlanerWayline" |
| | | /> |
| | | </div> |
| | | </div> |
| | | <div class="right"> |
| | | <TaskTable ref="taskTableRef" |
| | | :waylineId="waylineId" |
| | | :waylineType="waylineType" |
| | | <div class="right "> |
| | | <TaskTable |
| | | ref="taskTableRef" |
| | | :waylineId="waylineId" |
| | | :waylineType="waylineType" |
| | | :singlePoint="singlePoint" |
| | | :planarPoints="planarPoints" |
| | | @update:selected="handleSelected" |
| | | @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> |
| | |
| | | </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: [], |
| | |
| | | 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(); |
| | | if (res.data.code === 0) { |
| | | routeOptions.value = res.data.data; |
| | | } |
| | | }; |
| | | const res = await getWaylineList() |
| | | if (res.data.code === 0) { |
| | | 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(); |
| | | } |
| | | }); |
| | | }; |
| | | if (taskTableRef.value) { |
| | | 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; |
| | | } |
| | | ElMessage({ |
| | | message: '请选择任务日期', |
| | | type: 'warning', |
| | | }) |
| | | return |
| | | } |
| | | if (!searchForm.name) { |
| | | ElMessage({ |
| | | message: '请输入任务名称', |
| | | type: 'warning' |
| | | }); |
| | | return; |
| | | } |
| | | ElMessage({ |
| | | message: '请输入任务名称', |
| | | type: 'warning', |
| | | }) |
| | | return |
| | | } |
| | | // 检查任务时间 |
| | | if (timeSlot.value) { |
| | | 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)); |
| | | |
| | | if (selectedTime < now) { |
| | | ElMessage({ |
| | | message: '任务时间不能小于当前时间', |
| | | type: 'warning' |
| | | }); |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | if (timeSlot.value) { |
| | | 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)) |
| | | |
| | | if (selectedTime < now) { |
| | | ElMessage({ |
| | | message: '任务时间不能小于当前时间', |
| | | 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> |
| | | |
| | | <style lang="scss"> |
| | | .add-task{ |
| | | .add-task { |
| | | .el-pagination { |
| | | text-align: left; |
| | | padding: 20px; |
| | |
| | | <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); |
| | | grid-template-rows: repeat(2, 1fr); |
| | | gap: 30px; // 增加间距使布局更合理 |
| | | margin-bottom: 8px; // 添加底部间距 |
| | | grid-template-columns: repeat(3, 1fr); |
| | | grid-template-rows: repeat(2, 1fr); |
| | | gap: 30px; // 增加间距使布局更合理 |
| | | margin-bottom: 8px; // 添加底部间距 |
| | | .item { |
| | | 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; |
| | | } |
| | | :deep(.el-date-editor.el-input__wrapper) { |
| | | width: 200px; // 调整日期选择器宽度 |
| | | } |
| | | .required { |
| | | color: #f56c6c; |
| | | margin-left: 4px; |
| | | position: absolute; |
| | | left: -10px; |
| | | top: 8px; |
| | | display: flex; |
| | | align-items: center; |
| | | font-size: 16px; |
| | | color: #ffffff; |
| | | .itemchild { |
| | | width: 68px; |
| | | white-space: nowrap; |
| | | margin-right: 5px; |
| | | } |
| | | } |
| | | :deep(.el-date-editor.el-input__wrapper) { |
| | | width: 200px; // 调整日期选择器宽度 |
| | | } |
| | | |
| | | } |
| | | } |
| | | } |
| | | .right { |
| | | width: 32%; |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: space-between; |
| | | |
| | | .btn { |
| | | margin-top: 20px; |
| | | text-align: center; |
| | | |
| | | .el-button { |
| | | margin: 0 10px; |
| | | } |
| | | } |
| | | flex-direction: column; |
| | | justify-content: space-between; |
| | | |
| | | .btn { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | img { |
| | | width: 137px; |
| | | height: 32px; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | } |
| | | } |
| | | :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> |
| | |
| | | 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: '' |
| | | }, |
| | | checkedTableData: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | type: String, |
| | | default: '', |
| | | }, |
| | | checkedTableData: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | waylineTypeTest: { |
| | | type: Number, |
| | | default: 3 |
| | | } |
| | | type: Number, |
| | | default: 3, |
| | | }, |
| | | }) |
| | | |
| | | const imageryProvider_ammapSL = new Cesium.UrlTemplateImageryProvider({ |
| | |
| | | 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', { |
| | |
| | | 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 |
| | | ); |
| | | if (cartesian) { |
| | | // 清除之前的实体 |
| | | viewer.entities.removeAll(); |
| | | |
| | | // 添加新点 |
| | | const point = viewer.entities.add({ |
| | | position: cartesian, |
| | | point: { |
| | | pixelSize: 10, |
| | | color: Cesium.Color.WHITE |
| | | } |
| | | }); |
| | | |
| | | // 转换坐标 |
| | | const cartographic = Cesium.Cartographic.fromCartesian(cartesian); |
| | | const longitude = Cesium.Math.toDegrees(cartographic.longitude); |
| | | const latitude = Cesium.Math.toDegrees(cartographic.latitude); |
| | | |
| | | // 更新当前实体引用 |
| | | currentEntity = point; |
| | | |
| | | // 发送坐标 |
| | | emit('clickPosition', { longitude, latitude }); |
| | | } |
| | | }, Cesium.ScreenSpaceEventType.LEFT_CLICK); |
| | | }; |
| | | const cartesian = viewer.camera.pickEllipsoid(click.position, viewer.scene.globe.ellipsoid) |
| | | if (cartesian) { |
| | | // 清除之前的实体 |
| | | viewer.entities.removeAll() |
| | | |
| | | // 添加新点 |
| | | const point = viewer.entities.add({ |
| | | position: cartesian, |
| | | point: { |
| | | pixelSize: 10, |
| | | 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) |
| | | |
| | | // 更新当前实体引用 |
| | | currentEntity = point |
| | | |
| | | // 发送坐标 |
| | | emit('clickPosition', { longitude, latitude }) |
| | | } |
| | | }, 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) { |
| | | // 添加新点 |
| | | const point = viewer.entities.add({ |
| | | 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) |
| | | } |
| | | |
| | | |
| | | // 创建新的多边形 |
| | | polygonEntity = viewer.entities.add({ |
| | | polygon: { |
| | |
| | | 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'); |
| | | if (existingEntity) { |
| | | viewer.entities.remove(existingEntity); |
| | | } |
| | | const cartesian3List = ref([]); |
| | | dataValue.forEach((lnglat) => { |
| | | existingEntity = viewer.entities.getById('result_wayline') |
| | | if (existingEntity) { |
| | | viewer.entities.remove(existingEntity) |
| | | } |
| | | 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: { |
| | |
| | | polyline: setting.polyline, |
| | | id: setting.id, |
| | | }) |
| | | }; |
| | | } |
| | | |
| | | // 选中航线时调用 渲染线和点 type = 0 |
| | | const renderingLine = lineObj => { |
| | |
| | | const [lon, lat] = item.Point.coordinates.split(',') |
| | | return Cartesian3.fromDegrees(Number(lon), Number(lat)) |
| | | }) |
| | | |
| | | |
| | | 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 = {}; |
| | | // if (index === 0) { |
| | | // //TODO |
| | | // } |
| | | |
| | | positions.forEach((point, index) => { |
| | | let setting = {} |
| | | // if (index === 0) { |
| | | // //TODO |
| | | // } |
| | | // else if (index === 1) { |
| | | // setting = { |
| | | // position: point, |
| | | // id: `point_${index}`, |
| | | // billboard: { |
| | | // image: Startingpointicon, |
| | | // outlineWidth: 0, |
| | | // width: 20, |
| | | // height: 20, |
| | | // scale: 1.0, |
| | | // }, |
| | | // } |
| | | // } else |
| | | // setting = { |
| | | // position: point, |
| | | // id: `point_${index}`, |
| | | // billboard: { |
| | | // image: Startingpointicon, |
| | | // outlineWidth: 0, |
| | | // width: 20, |
| | | // height: 20, |
| | | // scale: 1.0, |
| | | // }, |
| | | // } |
| | | // } else |
| | | if (index === positions.length - 1) { |
| | | setting = { |
| | | position: point, |
| | | id: `point_${index}`, |
| | | billboard: { |
| | | image: EndPointicon, |
| | | outlineWidth: 0, |
| | | width: 20, |
| | | height: 20, |
| | | scale: 1.0, |
| | | }, |
| | | } |
| | | } else { |
| | | setting = { |
| | | position: point, |
| | | id: `point_${index}`, |
| | | 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) // 使标签在点的上方 |
| | | }, |
| | | setting = { |
| | | position: point, |
| | | id: `point_${index}`, |
| | | billboard: { |
| | | image: newEndPointImg, |
| | | outlineWidth: 0, |
| | | width: 20, |
| | | height: 20, |
| | | scale: 1.0, |
| | | }, |
| | | } |
| | | } else { |
| | | setting = { |
| | | position: point, |
| | | id: `point_${index}`, |
| | | label: { |
| | | text: `${index + 1}`, |
| | | font: 'bold 14px serif', |
| | | 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), // 使标签在点的上方 |
| | | }, |
| | | billboard: { |
| | | image: new Cesium.ConstantProperty(newStartPoint), |
| | | width: 70, |
| | | height: 70, |
| | | }, |
| | | // point: { |
| | | // pixelSize: 24, |
| | | // color: new Cesium.Color.fromBytes(255, 186, 0, 255), |
| | | // }, |
| | | |
| | | point: { |
| | | pixelSize: 24, |
| | | color: new Cesium.Color.fromBytes(255, 186, 0, 255), |
| | | }, |
| | | |
| | | offset: new Cesium.Cartesian2(10, 30), |
| | | } |
| | | } |
| | | viewer.entities.add(setting) |
| | | }); |
| | | 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, { |
| | |
| | | }) |
| | | } |
| | | |
| | | |
| | | // 异步解析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) |
| | |
| | | |
| | | // 绘制线和飞行 |
| | | 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(); |
| | | // 重新绘制航线 |
| | |
| | | // } |
| | | // 添加选中点和连接线 |
| | | 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, |
| | |
| | | width: 24, |
| | | height: 24, |
| | | }, |
| | | }); |
| | | return position; |
| | | }); |
| | | }) |
| | | return position |
| | | }) |
| | | |
| | | // 添加连接线 |
| | | if (positions.length > 1) { |
| | |
| | | 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: { |
| | |
| | | width: 24, |
| | | height: 24, |
| | | }, |
| | | }); |
| | | }) |
| | | |
| | | // 创建连接线 |
| | | const line = viewer.entities.add({ |
| | |
| | | width: 2, |
| | | material: new Cesium.PolylineDashMaterialProperty({ |
| | | color: Cesium.Color.RED, |
| | | dashLength: 8.0 |
| | | }) |
| | | } |
| | | }); |
| | | connectLines.push(line); |
| | | }); |
| | | dashLength: 8.0, |
| | | }), |
| | | }, |
| | | }) |
| | | 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) => { |
| | | // 如果存在面状航线 |
| | | if (existingEntity) { |
| | | const waylinePositions = existingEntity.polyline.positions.getValue(); |
| | | |
| | | newVal.forEach(item => { |
| | | // 创建机巢点 |
| | | const nestPosition = Cartesian3.fromDegrees( |
| | | Number(item.longitude), |
| | | Number(item.latitude) |
| | | ); |
| | | |
| | | // 添加机巢图标 |
| | | viewer.entities.add({ |
| | | position: nestPosition, |
| | | billboard: { |
| | | image: new Cesium.ConstantProperty(uavImg), |
| | | width: 24, |
| | | height: 24, |
| | | }, |
| | | }); |
| | | const planarPointsLines = newVal => { |
| | | // 如果存在面状航线 |
| | | if (existingEntity) { |
| | | const waylinePositions = existingEntity.polyline.positions.getValue() |
| | | |
| | | // 找到最近的航线点并连线 |
| | | let minDistance = Number.MAX_VALUE; |
| | | let closestPosition = null; |
| | | |
| | | waylinePositions.forEach(waylinePos => { |
| | | const distance = Cartesian3.distance(nestPosition, waylinePos); |
| | | if (distance < minDistance) { |
| | | minDistance = distance; |
| | | closestPosition = waylinePos; |
| | | } |
| | | }); |
| | | newVal.forEach(item => { |
| | | // 创建机巢点 |
| | | const nestPosition = Cartesian3.fromDegrees(Number(item.longitude), Number(item.latitude)) |
| | | |
| | | // 创建连接线 |
| | | if (closestPosition) { |
| | | const line = viewer.entities.add({ |
| | | polyline: { |
| | | positions: [nestPosition, closestPosition], |
| | | width: 2, |
| | | material: new Cesium.PolylineDashMaterialProperty({ |
| | | color: Cesium.Color.CHARTREUSE, |
| | | dashLength: 8.0 |
| | | }) |
| | | } |
| | | }); |
| | | connectLines.push(line); |
| | | } |
| | | }); |
| | | // 添加机巢图标 |
| | | viewer.entities.add({ |
| | | position: nestPosition, |
| | | billboard: { |
| | | image: new Cesium.ConstantProperty(uavImg), |
| | | width: 24, |
| | | height: 24, |
| | | }, |
| | | }) |
| | | |
| | | // 飞到所有点的中心位置 |
| | | const lngLatArr = newVal.map(item => [item.longitude, item.latitude]); |
| | | flyToPoints(lngLatArr); |
| | | } |
| | | }; |
| | | // 找到最近的航线点并连线 |
| | | let minDistance = Number.MAX_VALUE |
| | | let closestPosition = null |
| | | |
| | | waylinePositions.forEach(waylinePos => { |
| | | const distance = Cartesian3.distance(nestPosition, waylinePos) |
| | | if (distance < minDistance) { |
| | | minDistance = distance |
| | | closestPosition = waylinePos |
| | | } |
| | | }) |
| | | |
| | | // 创建连接线 |
| | | if (closestPosition) { |
| | | const line = viewer.entities.add({ |
| | | polyline: { |
| | | positions: [nestPosition, closestPosition], |
| | | width: 2, |
| | | material: new Cesium.PolylineDashMaterialProperty({ |
| | | color: Cesium.Color.CHARTREUSE, |
| | | dashLength: 8.0, |
| | | }), |
| | | }, |
| | | }) |
| | | connectLines.push(line) |
| | | } |
| | | }) |
| | | |
| | | // 飞到所有点的中心位置 |
| | | const lngLatArr = newVal.map(item => [item.longitude, item.latitude]) |
| | | flyToPoints(lngLatArr) |
| | | } |
| | | } |
| | | |
| | | // 监听选择航线文件事件 |
| | | watch(() => props.wayLineFile, async (newVal, oldValue) => { |
| | | await removeMap(); |
| | | if(newVal){ |
| | | await drawLine(); |
| | | } |
| | | }, { deep: true }); |
| | | watch( |
| | | () => props.wayLineFile, |
| | | async (newVal, oldValue) => { |
| | | await removeMap() |
| | | if (newVal) { |
| | | await drawLine() |
| | | } |
| | | }, |
| | | { deep: true } |
| | | ) |
| | | |
| | | // 监听表格选中数据变化 |
| | | watch(() => props.checkedTableData, (newVal) => { |
| | | if (newVal.length > 0 && props.waylineTypeTest === 0) { |
| | | selectLineFile(newVal); |
| | | } else if (newVal.length > 0 && props.waylineTypeTest === 1) { |
| | | singlePointLines(newVal); |
| | | } else if (newVal.length > 0 && props.waylineTypeTest === 2) { |
| | | planarPointsLines(newVal); |
| | | } |
| | | }, { deep: true }); |
| | | watch( |
| | | () => props.checkedTableData, |
| | | newVal => { |
| | | if (newVal.length > 0 && props.waylineTypeTest === 0) { |
| | | selectLineFile(newVal) |
| | | } else if (newVal.length > 0 && props.waylineTypeTest === 1) { |
| | | singlePointLines(newVal) |
| | | } else if (newVal.length > 0 && props.waylineTypeTest === 2) { |
| | | planarPointsLines(newVal) |
| | | } |
| | | }, |
| | | { deep: true } |
| | | ) |
| | | |
| | | // 监听航线类型 |
| | | watch(() => props.waylineTypeTest, async (newVal) => { |
| | | await removeMap(); |
| | | if (newVal === 1) { |
| | | await singlePointLeftClick(); |
| | | } else if (newVal === 2) { |
| | | await intelligentPlanning(); |
| | | } |
| | | }, { deep: true }); |
| | | watch( |
| | | () => props.waylineTypeTest, |
| | | async newVal => { |
| | | await removeMap() |
| | | if (newVal === 1) { |
| | | await singlePointLeftClick() |
| | | } else if (newVal === 2) { |
| | | await intelligentPlanning() |
| | | } |
| | | }, |
| | | { deep: true } |
| | | ) |
| | | |
| | | const removeEvent = () => { |
| | | // 清除事件监听器 |
| | | 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 = []; |
| | | |
| | | // 清除多边形点和实体 |
| | | polygonPoints = []; |
| | | if (polygonEntity) { |
| | | viewer.entities.remove(polygonEntity); |
| | | } |
| | | |
| | | // 清除当前实体和面状航线 |
| | | if (currentEntity) { |
| | | viewer.entities.remove(currentEntity); |
| | | } |
| | | if (existingEntity) { |
| | | viewer.entities.remove(existingEntity); |
| | | } |
| | | |
| | | viewer.entities.removeAll(); |
| | | |
| | | // 重置所有变量 |
| | | currentEntity = null; |
| | | polygonEntity = null; |
| | | existingEntity = null; |
| | | } |
| | | 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 = [] |
| | | |
| | | // 清除多边形点和实体 |
| | | polygonPoints = [] |
| | | if (polygonEntity) { |
| | | viewer.entities.remove(polygonEntity) |
| | | } |
| | | |
| | | // 清除当前实体和面状航线 |
| | | if (currentEntity) { |
| | | viewer.entities.remove(currentEntity) |
| | | } |
| | | if (existingEntity) { |
| | | viewer.entities.remove(existingEntity) |
| | | } |
| | | |
| | | viewer.entities.removeAll() |
| | | |
| | | // 重置所有变量 |
| | | 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 { |
| | |
| | | display: none; |
| | | } |
| | | } |
| | | |
| | | :deep(.context-menu) { |
| | | position: absolute; |
| | | background: rgba(0, 21, 41, 0.9);; |
| | | border-radius: 4px; |
| | | padding: 8px 0; |
| | | min-width: 120px; |
| | | box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3); |
| | | |
| | | :deep(.context-menu) { |
| | | position: absolute; |
| | | background: rgba(0, 21, 41, 0.9); |
| | | border-radius: 4px; |
| | | padding: 8px 0; |
| | | min-width: 120px; |
| | | box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3); |
| | | |
| | | .menu-item { |
| | | padding: 8px 16px; |
| | |
| | | cursor: pointer; |
| | | transition: all 0.3s; |
| | | font-size: 14px; |
| | | |
| | | |
| | | &:hover { |
| | | background: rgba(255, 255, 255, 0.1); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | </style> |
| | |
| | | <template> |
| | | <div class="table-container"> |
| | | <el-table :data="tableData" style="width: 100%" height="400" |
| | | @selection-change="handleSelectionChange" |
| | | @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> |
| | | <div class="pagination"> |
| | | <el-pagination |
| | | v-model:current-page="pagingParams.page" |
| | | v-model:page-size="pagingParams.size" |
| | | :page-sizes="[10, 20, 30]" |
| | | layout="total, sizes, prev, pager, next" |
| | | :total="total" |
| | | @size-change="handleSizeChange" |
| | | @current-change="handleCurrentChange" |
| | | /> |
| | | </div> |
| | | </div> |
| | | <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" |
| | | > |
| | | <el-table-column type="selection" width="55" :selectable="checkSelectable" /> |
| | | <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=" prev, pager, next" |
| | | :total="total" |
| | | @size-change="handleSizeChange" |
| | | @current-change="handleCurrentChange" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { getFlyingNestBy } from '@/api/home/task'; |
| | | import { getFlyingNestBy } from '@/api/home/task' |
| | | |
| | | const props = defineProps({ |
| | | waylineId: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | waylineType: { |
| | | type: Number, |
| | | default: 3 |
| | | }, |
| | | singlePoint: { |
| | | type: Object, |
| | | default: () => ({}) |
| | | }, |
| | | planarPoints: { |
| | | type: Array, |
| | | default: () => ([]) |
| | | } |
| | | }); |
| | | waylineId: { |
| | | type: String, |
| | | default: '', |
| | | }, |
| | | waylineType: { |
| | | type: Number, |
| | | default: 3, |
| | | }, |
| | | singlePoint: { |
| | | type: Object, |
| | | default: () => ({}), |
| | | }, |
| | | planarPoints: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | }) |
| | | |
| | | // 选中的数据 |
| | | const selectedRows = ref([]); |
| | | const selectedRows = ref([]) |
| | | // 分页参数 |
| | | let pageParams = ref({ |
| | | wayline_id: props.waylineId, |
| | | type: props.waylineType, |
| | | longitude: props.singlePoint.longitude, |
| | | latitude: props.singlePoint.latitude, |
| | | polygon: [], |
| | | }); |
| | | wayline_id: props.waylineId, |
| | | type: props.waylineType, |
| | | longitude: props.singlePoint.longitude, |
| | | latitude: props.singlePoint.latitude, |
| | | polygon: [], |
| | | }) |
| | | let pagingParams = ref({ |
| | | current: 1, |
| | | size: 10 |
| | | }); |
| | | current: 1, |
| | | 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); |
| | | if (res.data.code === 0) { |
| | | tableData.value = res.data.data; |
| | | } |
| | | }; |
| | | tableData.value = [] |
| | | const res = await getFlyingNestBy(pageParams.value, pagingParams.value) |
| | | if (res.data.code === 0) { |
| | | 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) => { |
| | | // 如果不是智能规划选区模式,才更新所有选中数据 |
| | | if (props.waylineType !== 2) { |
| | | selectedRows.value = val; |
| | | emit('update:selected', val); |
| | | } |
| | | }; |
| | | const emit = defineEmits(['update:selected']) |
| | | const handleSelectionChange = val => { |
| | | // 如果不是智能规划选区模式,才更新所有选中数据 |
| | | if (props.waylineType !== 2) { |
| | | selectedRows.value = val |
| | | emit('update:selected', val) |
| | | } |
| | | } |
| | | |
| | | // 控制表格行是否可选 |
| | | const checkSelectable = (row) => { |
| | | if (props.waylineType === 2) { |
| | | // 如果已经有选中的行,且当前行未被选中,则不允许选择 |
| | | return selectedRows.value.length === 0 || selectedRows.value.some(selected => selected.device_sn === row.device_sn); |
| | | } |
| | | return true; |
| | | }; |
| | | const checkSelectable = row => { |
| | | if (props.waylineType === 2) { |
| | | // 如果已经有选中的行,且当前行未被选中,则不允许选择 |
| | | return selectedRows.value.length === 0 || selectedRows.value.some(selected => selected.device_sn === row.device_sn) |
| | | } |
| | | return true |
| | | } |
| | | |
| | | // 处理单行选择 |
| | | const handleSelect = (selection, row) => { |
| | | if (props.waylineType === 2) { |
| | | // 如果是智能规划选区模式,确保只能选中一行 |
| | | if (selection.length > 1) { |
| | | // 保留最后选中的那一行 |
| | | const lastSelected = selection[selection.length - 1]; |
| | | selectedRows.value = [lastSelected]; |
| | | // 触发更新事件 |
| | | emit('update:selected', selectedRows.value); |
| | | } |
| | | } |
| | | }; |
| | | if (props.waylineType === 2) { |
| | | // 如果是智能规划选区模式,确保只能选中一行 |
| | | if (selection.length > 1) { |
| | | // 保留最后选中的那一行 |
| | | const lastSelected = selection[selection.length - 1] |
| | | selectedRows.value = [lastSelected] |
| | | // 触发更新事件 |
| | | emit('update:selected', selectedRows.value) |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 监听航线ID变化 |
| | | watch(() => props.waylineId, (newVal) => { |
| | | pageParams.value.type = 0; |
| | | if (newVal) { |
| | | pageParams.value.wayline_id = newVal; |
| | | refreshData(); |
| | | } else { |
| | | // 数据为空时,清除列表数据 |
| | | tableData.value = []; |
| | | } |
| | | }, { deep: true }); |
| | | watch( |
| | | () => props.waylineId, |
| | | newVal => { |
| | | pageParams.value.type = 0 |
| | | if (newVal) { |
| | | pageParams.value.wayline_id = newVal |
| | | refreshData() |
| | | } else { |
| | | // 数据为空时,清除列表数据 |
| | | tableData.value = [] |
| | | } |
| | | }, |
| | | { deep: true } |
| | | ) |
| | | |
| | | // 监听单点返回的数据 |
| | | 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(); |
| | | } |
| | | }, { deep: true }); |
| | | 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() |
| | | } |
| | | }, |
| | | { deep: true } |
| | | ) |
| | | |
| | | // 监听面状航线返回的数据 |
| | | 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(); |
| | | } |
| | | }, { deep: true }); |
| | | 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() |
| | | } |
| | | }, |
| | | { 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; |
| | | // } |
| | | height: 500px; |
| | | .pagination { |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | // 分页 |
| | | :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> |