Merge remote-tracking branch 'origin/master'
16 files modified
2 files added
| | |
| | | }) |
| | | } |
| | | // 机巢列表 |
| | | export const selectDevicePage = ({ nickname, is_execute, ...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, |
| | | is_execute, |
| | | }, |
| | | }) |
| | | } |
| | |
| | | // 任务列表 |
| | | export const jobList = data => { |
| | | return request({ |
| | | url: '/drone-device-core/wayline/waylineJobInfo/jobList?current=' + data.page + '&size=' + data.limit, |
| | | url: '/drone-device-core/wayline/waylineJobInfo/jobList?current=' + data.page + '&size=' + data.size, |
| | | method: 'post', |
| | | data: data.searchParams, |
| | | }) |
| | |
| | | } |
| | | |
| | | // 获取可飞行机巢列表 |
| | | export const getFlyingNestBy = (data) => { |
| | | export const getFlyingNestBy = (data,page) => { |
| | | return request({ |
| | | url: `/drone-device-core/manage/api/v1/devices/getFlyingNestBy?current=${data.page}&size=${data.limit}`, |
| | | url: `/drone-device-core/manage/api/v1/devices/getFlyingNestBy?current=${page.page}&size=${page.size}`, |
| | | method: 'post', |
| | | data: { |
| | | wayline_id: data.wayline_id, |
| | | type: data.type |
| | | }, |
| | | data: data, |
| | | }) |
| | | } |
| | | |
| | |
| | | }) |
| | | } |
| | | |
| | | // 面状航线 |
| | | export const getWaylineByArea = (data) => { |
| | | return request({ |
| | | url: `/drone-device-core/wayline/api/v1/workspaces/getWaylineByArea`, |
| | | method: 'post', |
| | | data, |
| | | }) |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | |
| | | import { set } from 'lodash' |
| | | import { EDeviceTypeName } from '@/utils/staticData/enums' |
| | | import { getStore, setStore } from '@/utils/store' |
| | | |
| | | const home = { |
| | | state: { |
| | | machineNestDetail: false, // 机巢详情 |
| | | machineNestDetail: false, // 机巢长列表 |
| | | isEventOverviewDetail: false,//是事件概述详情 |
| | | // 用户行政区划中心点 |
| | | userAreaPosition: getStore({ name: 'userAreaPosition' }) || {}, |
| | | // 用户切换后行政区划中心点 |
| | | currentAreaPosition:getStore({ name: 'currentAreaPosition' }) || {}, |
| | | singleUavHome: {}, |
| | | footActiveIndex: 0, |
| | | isEventOverviewDetail: false,//是事件概述详情 |
| | | singleTotal: {}, // 统计单个机巢数据 |
| | | // 项目id |
| | | selectedWorkSpaceId: window.localStorage.getItem('bs_workspace_id'), |
| New file |
| | |
| | | <template> |
| | | <div class="AINowFly"> |
| | | <div>智引即飞</div> |
| | | <el-form ref="ruleFormRef" :model="params" :rules="rules" label-width="auto" size="small" status-icon> |
| | | <el-form-item label="任务名称" prop="name"> |
| | | <el-input v-model="params.name" /> |
| | | </el-form-item> |
| | | <el-form-item label="关联算法" prop="ai_types"> |
| | | <el-select v-model="params.ai_types" placeholder="请选择" multiple> |
| | | <el-option v-for="item in taskAlgorithm" :key="item.id" :label="item.dictValue" :value="item.dictKey" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="地址"> |
| | | <el-input disabled type="text" v-model="di" /> |
| | | </el-form-item> |
| | | <!-- todo--> |
| | | <el-form-item label="事件"> |
| | | <div>默认拍照</div> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <div>可用机巢列表</div> |
| | | <el-table :data="list" @selection-change="handleSelectionChange"> |
| | | <el-table-column type="selection" width="55" /> |
| | | <el-table-column type="index" label="序号" /> |
| | | <el-table-column prop="nickname" label="机巢名称" /> |
| | | <el-table-column prop="minute" label="预计飞行时长(分钟)" /> |
| | | <el-table-column prop="exe_distance" label="预计飞行距离(m)" /> |
| | | </el-table> |
| | | <el-button type="primary" @click="nowFly">迅速起飞</el-button> |
| | | </div> |
| | | </template> |
| | | <script setup> |
| | | import { getMultipleDictionary } from '@/api/system/dictbiz' |
| | | import * as Cesium from 'cesium' |
| | | import { createTask, getFlyingNestBy } from '@/api/home/task' |
| | | import { cartesian3Convert } from '@/utils/cesium/mapUtil' |
| | | import { Cartesian3 } from 'cesium' |
| | | import uavImg from '@/assets/images/home/useUavHome/uavImg.png' |
| | | import ImageTrailMaterial from '@/utils/cesium/ImageTrailMaterial' |
| | | import lineImg from '@/assets/images/arrow-right-blue.png' |
| | | import _ from 'lodash' |
| | | import { ElMessage } from 'element-plus' |
| | | import dayjs from 'dayjs' |
| | | import { useStore } from 'vuex' |
| | | |
| | | const now = dayjs().format('YYYYMMDDHHmm') |
| | | const nowTime = dayjs().format('YYYY-MM-DD HH:mm:ss') |
| | | |
| | | const paramsInit = { |
| | | name: `智引即飞${now}`, |
| | | ai_types: '', |
| | | longitude: '', |
| | | latitude: '', |
| | | dock_sns: [], |
| | | type: 1, |
| | | begin_time: nowTime, |
| | | end_time: nowTime, |
| | | } |
| | | const params = ref(_.cloneDeep(paramsInit)) |
| | | const di = computed(() => params.value.longitude + ',' + params.value.latitude) |
| | | |
| | | const rules = ref({ |
| | | name: [ |
| | | { required: true, message: '请输入任务名称', trigger: 'blur' }, |
| | | { min: 3, max: 50, message: '长度在 3 到 20 个字符', trigger: 'blur' }, |
| | | ], |
| | | ai_types: [{ required: true, message: '请选择算法', trigger: 'change' }], |
| | | }) |
| | | |
| | | const ruleFormRef = ref(null) |
| | | let handler = null |
| | | const taskAlgorithm = ref([]) |
| | | |
| | | let viewer |
| | | const list = ref([]) |
| | | let eventPoint = {} |
| | | const store = useStore() |
| | | const handleSelectionChange = val => { |
| | | params.value.dock_sns = val.map(item => item.device_sn) |
| | | } |
| | | |
| | | const nowFly = async () => { |
| | | await ruleFormRef.value.validate() |
| | | if (!params.value.dock_sns.length) return ElMessage.warning('请选择将要飞行的机巢') |
| | | if (!params.value.longitude) return ElMessage.warning('请选择飞行的点') |
| | | createTask(params.value).then(res => { |
| | | ElMessage.success('起飞成功') |
| | | if (params.value.dock_sns.length === 1) { |
| | | store.commit('setSingleUavHome', { device_sn: params.value.dock_sns[0] }) |
| | | } else { |
| | | store.commit('setFootActiveIndex', 0) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | const renderingLine = () => { |
| | | const { longitude, latitude } = eventPoint |
| | | list.value.forEach((item, index) => { |
| | | const start = Cartesian3.fromDegrees(Number(item.longitude), Number(item.latitude)) |
| | | const end = Cartesian3.fromDegrees(Number(longitude), Number(latitude)) |
| | | const positionsList = [start, end] |
| | | // 线 |
| | | viewer.entities.add({ |
| | | id: `AINow-line-${index}`, |
| | | polyline: { |
| | | width: 4, |
| | | positions: positionsList, |
| | | material: new ImageTrailMaterial({ |
| | | color: { alpha: 1, blue: 1, green: 1, red: 1 }, |
| | | speed: 20, |
| | | image: lineImg, |
| | | repeat: { x: Math.floor(40), y: 1 }, |
| | | }), |
| | | clampToGround: false, |
| | | }, |
| | | }) |
| | | // 点 |
| | | viewer.entities.add({ |
| | | id: `AINow-point-${index}`, |
| | | position: start, |
| | | label: { |
| | | text: item.nickname, |
| | | font: '12pt Source Han Sans CN', |
| | | fillColor: Cesium.Color.WHITE, |
| | | outlineColor: Cesium.Color.BLACK, |
| | | outlineWidth: 2, |
| | | style: Cesium.LabelStyle.FILL_AND_OUTLINE, |
| | | verticalOrigin: Cesium.VerticalOrigin.BOTTOM, |
| | | pixelOffset: new Cesium.Cartesian2(0, -9), |
| | | }, |
| | | billboard: { |
| | | image: new Cesium.ConstantProperty(uavImg), |
| | | width: 24, |
| | | height: 24, |
| | | }, |
| | | properties: { |
| | | customData: { |
| | | data: item, |
| | | }, |
| | | }, |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | const removeEntities = () => { |
| | | const entitiesIDs = viewer.entities.values.map(i => i.id) |
| | | entitiesIDs.forEach(item => { |
| | | item.includes('AINow-') && viewer.entities.removeById(item) |
| | | }) |
| | | } |
| | | |
| | | // 单击事件 |
| | | const singleClickEvent = click => { |
| | | removeEntities() |
| | | const earthPosition = viewer.scene.pickPosition(click.position) |
| | | viewer.entities.add({ |
| | | position: earthPosition, |
| | | id: `AINow-event-0`, |
| | | point: { |
| | | pixelSize: 10, |
| | | color: Cesium.Color.RED, |
| | | outlineColor: Cesium.Color.WHITE, |
| | | outlineWidth: 2, |
| | | }, |
| | | }) |
| | | const { longitude, latitude } = cartesian3Convert(earthPosition, viewer) |
| | | eventPoint = { longitude, latitude } |
| | | params.value = { ...params.value, longitude, latitude } |
| | | getFJList() |
| | | } |
| | | |
| | | // 请求字典字段 |
| | | const requestDictionary = () => { |
| | | getMultipleDictionary('SF').then(res => { |
| | | if (res.code !== 0) { |
| | | taskAlgorithm.value = res.data.data['SF'] |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // 获取飞机列表 |
| | | const getFJList = async () => { |
| | | params.value.dock_sns = [] |
| | | const getListParams = { ...eventPoint, page: 1, limit: 9999, type: 1 } |
| | | const res = await getFlyingNestBy(getListParams) |
| | | list.value = (res.data?.data || []).map(item => ({ |
| | | ...item, |
| | | minute: _.round(item.estimated_arrival_time / 60, 0), |
| | | })) |
| | | if (!list.value.length) ElMessage.warning('附近暂无可用无人机') |
| | | renderingLine() |
| | | } |
| | | |
| | | const initMap = () => { |
| | | handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas) |
| | | handler.setInputAction(singleClickEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK) |
| | | } |
| | | |
| | | const removeAll = () => { |
| | | removeEntities() |
| | | handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK) |
| | | handler?.destroy() |
| | | handler = null |
| | | } |
| | | |
| | | onBeforeUnmount(() => { |
| | | removeAll() |
| | | }) |
| | | |
| | | onMounted(() => { |
| | | requestDictionary() |
| | | viewer = window.$viewer |
| | | initMap() |
| | | }) |
| | | </script> |
| | | <style scoped lang="scss"> |
| | | .AINowFly { |
| | | position: absolute; |
| | | top: 200px; |
| | | right: 0; |
| | | width: 400px; |
| | | height: 700px; |
| | | background: white; |
| | | color: black; |
| | | font-size: 20px; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="footer"> |
| | | <img |
| | | v-for="item in list" |
| | | v-for="(item,index) in list.filter(i => !i.disable)" |
| | | :class="item.className" |
| | | :src="item.active ? item.activeImg : item.img" |
| | | alt="" |
| | | @click="imgClick(item)" |
| | | @click="footAction(item,index)" |
| | | /> |
| | | </div> |
| | | <div class="intelligent-introduction-flying"> |
| | | <img class="orthophoto" src="@/assets/images/intelligent-introduction-flying.png" alt="" /> |
| | | <img |
| | | class="orthophoto" |
| | | src="@/assets/images/intelligent-introduction-flying.png" |
| | | alt="" |
| | | @click="footAction(list[5],5)" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | |
| | | import { useMapAggregation } from '@/views/Home/useMapAggregation/useMapAggregation' |
| | | import { useStore } from 'vuex' |
| | | import func from '@/utils/func' |
| | | |
| | | const store = useStore() |
| | | const footActiveIndex = computed(() => store.state.home.footActiveIndex) |
| | | |
| | | // 机巢聚合 |
| | | const { init, removeAll } = useMapAggregation('device') |
| | |
| | | |
| | | const list = ref([ |
| | | { |
| | | name: 'event1', |
| | | name: 'jc', |
| | | img: img1, |
| | | activeImg: activeImg1, |
| | | active: true, |
| | |
| | | removeAll: removeAll, |
| | | }, |
| | | { |
| | | name: 'event2', |
| | | name: 'sj', |
| | | img: img2, |
| | | activeImg: activeImg2, |
| | | active: false, |
| | |
| | | init: eventInit, |
| | | removeAll: eventRemove, |
| | | }, |
| | | { name: 'event3', img: img3, activeImg: activeImg3, active: false, className: 'panorama' }, |
| | | { name: 'event4', img: img4, activeImg: activeImg4, active: false, className: 'threeD' }, |
| | | { name: 'event5', img: img5, activeImg: activeImg5, active: false, className: 'orthophoto' }, |
| | | { name: 'qj', img: img3, activeImg: activeImg3, active: false, className: 'panorama' }, |
| | | { name: 'sw', img: img4, activeImg: activeImg4, active: false, className: 'threeD' }, |
| | | { name: 'zs', img: img5, activeImg: activeImg5, active: false, className: 'orthophoto' }, |
| | | { name: 'zyjf', active: false, disable: true }, |
| | | ]) |
| | | const store = useStore() |
| | | const imgClick = (toItem,index) => { |
| | | index !== undefined && store.commit('setFootActiveIndex',index) |
| | | const fromItem = list.value.find(item => item.active) |
| | | if (fromItem.name === toItem.name) return |
| | | |
| | | const footAction = (toItem, index) => { |
| | | const fromItem = list.value.find(item => item.active) //上一个激活item |
| | | if (fromItem.name === toItem?.name) return //是重复点击 |
| | | index !== undefined && store.commit('setFootActiveIndex', index) //是按钮点击得动作 |
| | | fromItem?.removeAll?.() |
| | | nextTick(() => { |
| | | toItem?.init?.() |
| | | }) |
| | | list.value = list.value.map(item => ({ ...item, active: item.name === toItem.name })) |
| | | nextTick(() => toItem?.init?.()) |
| | | list.value = list.value.map(item => ({ ...item, active: item.name === toItem?.name })) |
| | | } |
| | | |
| | | const footActiveIndex = computed(() => store.state.home.footActiveIndex) |
| | | watch(footActiveIndex, val => { |
| | | console.log(6666666) |
| | | imgClick(list.value[val]) |
| | | },{deep:true}) |
| | | watch( |
| | | footActiveIndex, |
| | | val => { |
| | | footAction(list.value[val]) |
| | | }, |
| | | { deep: true } |
| | | ) |
| | | |
| | | // 销毁前钩子 |
| | | onBeforeUnmount(() => { |
| | |
| | | bottom: 23px; |
| | | |
| | | img { |
| | | cursor: pointer; |
| | | width: 146px; |
| | | height: 54px; |
| | | } |
| | |
| | | <template> |
| | | <template v-if="singleUavHome?.device_sn"> |
| | | <SignMachineNest /> |
| | | </template> |
| | | <!-- 单个机巢页面--> |
| | | <SignMachineNest v-if="singleUavHome?.device_sn" /> |
| | | <!-- 事件概述详细信息页面--> |
| | | <EventOverviewDetail v-else-if="isEventOverviewDetail" /> |
| | | |
| | | <AINowFly v-else-if="isAINowFly" /> |
| | | <!-- 首页默认的页面--> |
| | | <template v-else> |
| | | <EventOverviewDetail v-if="isEventOverviewDetail" /> |
| | | <template v-else> |
| | | <SearchBox /> |
| | | <HomeLeft /> |
| | | <HomeRight /> |
| | | </template> |
| | | <HomeLeft /> |
| | | <HomeRight /> |
| | | </template> |
| | | |
| | | <SearchBox /> |
| | | <RSide /> |
| | | <Footer /> |
| | | </template> |
| | |
| | | import { onMounted } from 'vue' |
| | | import cesiumOperation from '@/utils/cesium-tsa' |
| | | import EventOverviewDetail from '@/views/Home/EventOverviewDetail/EventOverviewDetail.vue' |
| | | import AINowFly from '@/views/Home/AINowFly.vue' |
| | | |
| | | const store = useStore() |
| | | const { _init, viewerDestory } = cesiumOperation() |
| | | |
| | | const singleUavHome = computed(() => store.state.home.singleUavHome) |
| | | const isEventOverviewDetail = computed(() => store.state.home.isEventOverviewDetail) |
| | | const isAINowFly = computed(() => store.state.home.footActiveIndex === 5) |
| | | |
| | | onUnmounted(() => { |
| | | store.commit('setSingleUavHome', null) |
| | | console.log('home销毁') |
| | | store.commit('setIsEventOverviewDetail', false) |
| | | store.commit('setFootActiveIndex', 0) |
| | | viewerDestory() |
| | | }) |
| | | |
| | |
| | | |
| | | // 搜索数据 |
| | | const handleSearch = (name) => { |
| | | console.log(name, '哒哒哒'); |
| | | searchText.value = name; |
| | | pageParams.value.current = 1; |
| | | tableList.value = []; |
| | |
| | | <!-- 机巢概况 --> |
| | | <template> |
| | | <common-title :style="{ marginLeft: pxToRem(14) }" @details="detailsFun"></common-title> |
| | | <common-title title="机巢概况" :style="{ marginLeft: pxToRem(14) }" @details="detailsFun"></common-title> |
| | | <div class="overview-next"> |
| | | <MachineNestTotal @searchNickName="handleSearch" /> |
| | | <div class="table-list"> |
| | |
| | | const getTableList = () => { |
| | | const params = { |
| | | nickname: searchText.value, |
| | | is_execute: true, |
| | | current: pageParams.value.current, |
| | | size: pageParams.value.size |
| | | }; |
| | |
| | | border-radius: 0px 0px 0px 0px; |
| | | opacity: 0.85; |
| | | margin-bottom: 10px; |
| | | |
| | | |
| | | .table-list { |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | margin: 16px; |
| | |
| | | getDeviceInfoNum().then((res) => { |
| | | if (res.data.code !== 0) return; |
| | | let result = res.data.data; |
| | | listNum.value[0].value = result.ex_num; |
| | | listNum.value[1].value = result.leisure_num; |
| | | listNum.value[0].value = result.leisure_num; |
| | | listNum.value[1].value = result.ex_num; |
| | | listNum.value[2].value = result.offline_num; |
| | | total.value = result.device_num; |
| | | }); |
| | |
| | | const resJob = await getTotalJobNum({ areaCode }); |
| | | dataObj.value.jobNum = resJob.data.data; |
| | | dataObj.value.device_num = res.data.data.device_num; |
| | | list.value[0].value = res.data.data.ex_num; |
| | | list.value[1].value = res.data.data.leisure_num; |
| | | list.value[0].value = res.data.data.leisure_num; |
| | | list.value[1].value = res.data.data.ex_num; |
| | | list.value[2].value = res.data.data.offline_num; |
| | | loading.value = false; |
| | | }); |
| | |
| | | scalingJudgment.forEach(item => item.show && (item.outline = outlineGJson)) |
| | | const [longitude, latitude] = outlineGJson.features[0].properties.centroid |
| | | const height = scalingJudgment[(hierarchy.length - 3) * (-1)].height |
| | | setCenterPosition({longitude, latitude,height}) |
| | | setCenterPosition({ longitude, latitude, height }) |
| | | flyTo({ longitude, latitude }, 0, height) |
| | | } |
| | | |
| | | const userAreaPosition = computed(() => store.state.home.userAreaPosition); |
| | | const userAreaPosition = computed(() => store.state.home.userAreaPosition) |
| | | |
| | | const setCenterPosition = (position) => { |
| | | store.commit('setCurrentAreaPosition', position) |
| | | if (!userAreaPosition.value.longitude){ |
| | | store.commit('setUserAreaPosition', position) |
| | | } |
| | | if (!userAreaPosition.value.longitude) { |
| | | store.commit('setUserAreaPosition', position) |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | id: `aggregation-splashed-${index}`, |
| | | position: Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude), |
| | | label: { |
| | | // 随机整数 |
| | | text: item.nickname, |
| | | font: '12pt Source Han Sans CN', |
| | | fillColor: Cesium.Color.WHITE, |
| | |
| | | // 渲染轮廓 |
| | | const renderOutline = item => { |
| | | item.outline && |
| | | Cesium.GeoJsonDataSource.load(item.outline).then(dataSource => { |
| | | viewer.dataSources.add(dataSource) |
| | | const entities = dataSource.entities.values |
| | | entities.forEach(entity => { |
| | | // 隐藏多边形填充 |
| | | entity.polygon.material = Cesium.Color.TRANSPARENT |
| | | entity.polygon.outline = false // 关闭原生轮廓 |
| | | // 创建独立折线作为轮廓 |
| | | const positions = entity.polygon.hierarchy.getValue().positions |
| | | viewer.entities.add({ |
| | | polyline: { |
| | | positions: positions, |
| | | width: 5, // 直接设置宽度 |
| | | material: new Cesium.PolylineGlowMaterialProperty({ |
| | | glowPower: 0.5, |
| | | color: Cesium.Color.AQUA, |
| | | }), |
| | | clampToGround: true, // 贴地显示 |
| | | }, |
| | | Cesium.GeoJsonDataSource.load(item.outline).then(dataSource => { |
| | | const entities = dataSource.entities.values |
| | | entities.forEach((entity, index) => { |
| | | // 创建独立折线作为轮廓 |
| | | const positions = entity.polygon.hierarchy.getValue().positions |
| | | viewer.entities.add({ |
| | | id: `aggregation-outline-${index}`, |
| | | polyline: { |
| | | positions: positions, |
| | | width: 5, // 直接设置宽度 |
| | | material: new Cesium.PolylineGlowMaterialProperty({ |
| | | glowPower: 0.5, |
| | | color: Cesium.Color.AQUA, |
| | | }), |
| | | }, |
| | | }) |
| | | }) |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | // 聚合机巢 |
| | |
| | | // 遍历特征并添加实体 |
| | | featuresList.forEach((feature, index) => { |
| | | if (!feature.position) return |
| | | const position = Cesium.Cartesian3.fromDegrees(feature.position[0], feature.position[1],12000) |
| | | const position = Cesium.Cartesian3.fromDegrees(feature.position[0], feature.position[1], 12000) |
| | | viewer.entities.add({ |
| | | position: position, |
| | | id: `aggregation-name-${index}`, |
| | |
| | | return arrColor[++index % arrColor.length] |
| | | } |
| | | |
| | | // 加载新的 GeoJSON 数据 |
| | | // 加载边界 |
| | | Cesium.GeoJsonDataSource.load(item.gJson).then(dataSource => { |
| | | console.log(dataSource, item.gJson) |
| | | |
| | | viewer.dataSources.add(dataSource) |
| | | item.dataSource = dataSource // 保存数据源以便后续删除 |
| | | item.BJDataSource = dataSource // 保存数据源以便后续删除 |
| | | // 获取数据源中的实体 |
| | | const entities = dataSource.entities.values |
| | | |
| | |
| | | alphaPower: 1.3 |
| | | }) |
| | | |
| | | entity.polygon.extrudedHeight = (entity.propertyNames.length || 1) * 500 |
| | | entity.polygon.extrudedHeight = (entity.properties.childrenNum._value || 1) * 500 |
| | | |
| | | entity.polygon.material = material |
| | | entity.polygon.outline = false // 显示边框 |
| | | }) |
| | | |
| | | needFly && viewer.flyTo(dataSource, { |
| | | offset: new Cesium.HeadingPitchRange( |
| | | 0, // heading: 0 (朝向不变) |
| | | Cesium.Math.toRadians(-60), // pitch: -90° (垂直向下) |
| | | 0 // range: 0 (默认距离) |
| | | ), |
| | | offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-60), 0), |
| | | duration: 0.5, |
| | | }) |
| | | needFly = false |
| | |
| | | currentEntity = entity |
| | | const entityPosition = entity?.position?._value |
| | | if (!entityPosition) return |
| | | if (entityPosition){ |
| | | if (entityPosition) { |
| | | positionC3 = entityPosition |
| | | } |
| | | removeLabel() |
| | |
| | | |
| | | // 移除 点 和 gjson 实体 |
| | | const removeEntities = () => { |
| | | viewer.dataSources?.removeAll(true) |
| | | // viewer.entities?.removeAll(); |
| | | // dataSources移除 |
| | | scalingJudgment.forEach(item => { |
| | | item.BJDataSource && viewer.dataSources.remove(item.BJDataSource) |
| | | item.BJDataSource = null |
| | | }) |
| | | // entities移除 |
| | | const entitiesIDs = viewer.entities.values.map(i => i.id) |
| | | entitiesIDs.forEach(item => { |
| | | item.includes('aggregation-') && viewer.entities.removeById(item) |
| | |
| | | |
| | | const goBack = () => { |
| | | store.commit('setSingleUavHome', {}); |
| | | store.commit('setFootActiveIndex', 0) |
| | | }; |
| | | </script> |
| | | |
| | |
| | | import ImageTrailMaterial from '@/utils/cesium/ImageTrailMaterial' |
| | | import lineImg from '@/assets/images/arrow-right-blue.png' |
| | | import uavImg from '@/assets/images/home/useUavHome/uavImg.png' |
| | | import { getCenterPoint } from '@/utils/cesium/mapUtil' |
| | | |
| | | const props = defineProps(['detailsData']) |
| | | |
| | |
| | | <div class="task-contain"> |
| | | <div class="left"> |
| | | <div class="search"> |
| | | <div class="item">任务名称:<el-input v-model="searchForm.name" placeholder="请输入任务名称"></el-input></div> |
| | | <div class="item"><span style="color: red;">*</span>任务日期: |
| | | <div class="item"><span class="required">*</span>任务名称:<el-input v-model="searchForm.name" placeholder="请输入任务名称"></el-input></div> |
| | | <div class="item"><span class="required">*</span>任务日期: |
| | | <el-date-picker |
| | | v-model="searchForm.begin_time" |
| | | v-model="taskData" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | :disabled-date="disabledDate" |
| | | /> |
| | | </div> |
| | | <div class="item">任务时间: |
| | | <!-- <el-time-picker |
| | | <el-time-picker |
| | | v-model="searchForm.execute_time_arr" |
| | | placeholder="选择时间" |
| | | format="HH:mm" |
| | | value-format="HH:mm"/> --> |
| | | value-format="HH:mm"/> |
| | | </div> |
| | | <div class="item">选择航线: |
| | | <el-select |
| | |
| | | </div> |
| | | <div class="lines"> |
| | | <div class="wayline-type"> |
| | | <el-radio-group v-model="searchForm.wayline_type"> |
| | | <el-radio label="1">单点航线</el-radio> |
| | | <el-radio label="2">智能规划选区</el-radio> |
| | | <el-radio-group v-model="waylineType" @change="radioChange"> |
| | | <el-radio :value="1" label="单点航线"></el-radio> |
| | | <el-radio :value="2" label="智能规划选区"></el-radio> |
| | | </el-radio-group> |
| | | </div> |
| | | <TaskMap class="wayline-map" :wayLineFile="wayLineFile"/> |
| | | <TaskMap class="wayline-map" |
| | | :wayLineFile="wayLineFile" |
| | | :checkedTableData="checkedTableData" |
| | | :waylineTypeTest="waylineType" |
| | | @clickPosition="clickSignPosition" |
| | | @saveWayline="savePlanerWayline" |
| | | /> |
| | | </div> |
| | | </div> |
| | | <div class="right"> |
| | | <TaskTable :waylineId="waylineId" :waylineType="waylineType" @update:selected="handleSelected" /> |
| | | <TaskTable ref="taskTableRef" |
| | | :waylineId="waylineId" |
| | | :waylineType="waylineType" |
| | | :singlePoint="singlePoint" |
| | | :planarPoints="planarPoints" |
| | | @update:selected="handleSelected" |
| | | /> |
| | | <div class="btn"> |
| | | <el-button type="primary" @click="cancel">取消</el-button> |
| | | <el-button type="primary" @click="submitClick">发布</el-button> |
| | |
| | | import TaskMap from './TaskMap.vue'; |
| | | import TaskTable from './TaskTable.vue'; |
| | | |
| | | const emit = defineEmits(['refresh']); |
| | | |
| | | const rangDate = ref([]); |
| | | // 航线ID |
| | | let waylineId = ref(''); |
| | | const waylineId = ref(''); |
| | | // 航线文件 |
| | | let wayLineFile = ref(''); |
| | | const wayLineFile = ref(''); |
| | | // 航线类型 |
| | | let waylineType = ref(0); |
| | | |
| | | const waylineType = ref(3); |
| | | // 添加子组件引用 |
| | | const taskTableRef = ref(null); |
| | | const taskData = ref(''); |
| | | const searchForm = reactive({ |
| | | name: '', |
| | | ai_types: [], |
| | |
| | | remark: '', |
| | | type: waylineType.value, |
| | | dock_sns: [], |
| | | longitude: '', |
| | | latitude: '', |
| | | polygon: [], |
| | | }); |
| | | const isShowAddTask = defineModel('show'); |
| | | |
| | | // 禁用当天之前的日期 |
| | | const disabledDate = (time) => { |
| | | return time.getTime() < Date.now() - 8.64e7; // 86400000 = 24 * 60 * 60 * 1000 |
| | | }; |
| | | |
| | | // 获取航线列表数据 |
| | | const routeOptions = ref([]); |
| | |
| | | routeOptions.value = res.data.data; |
| | | } |
| | | }; |
| | | // 关联算法 |
| | | const algorithmChange = (val) => { |
| | | searchForm.ai_types = val; |
| | | }; |
| | | |
| | | // 切换航线类型 |
| | | const radioChange = (val) => { |
| | | // 清空选中航线值 |
| | | searchForm.file_id = ''; |
| | | // 航线ID也清空 |
| | | waylineId.value = ''; |
| | | wayLineFile.value = ''; |
| | | waylineType.value = val; |
| | | searchForm.type = val; |
| | | // 清空列表数据 |
| | | nextTick(() => { |
| | | 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; |
| | | }; |
| | | |
| | | // 获取选中机场列表数据,并且发布 |
| | | let checkedTableData = ref([]); |
| | | const handleSelected = (val) => { |
| | | searchForm.dock_sns = val.map(item => item.device_sn); |
| | | checkedTableData.value = val; |
| | | }; |
| | | const emit = defineEmits(['refresh']); |
| | | |
| | | // 地图点击事件 表格重新刷新数据 |
| | | 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; |
| | | }; |
| | | |
| | | // 提交 |
| | | const submitClick = () => { |
| | | if (!searchForm.begin_time) { |
| | | if (!taskData.value) { |
| | | ElMessage({ |
| | | message: '请选择任务日期', |
| | | type: 'warning' |
| | | }); |
| | | return; |
| | | } |
| | | searchForm.end_time = `${searchForm.begin_time} 23:59:59`; |
| | | searchForm.begin_time = `${searchForm.begin_time} 00:00:00`; |
| | | if (!searchForm.name) { |
| | | ElMessage({ |
| | | message: '请输入任务名称', |
| | | type: 'warning' |
| | | }); |
| | | return; |
| | | } |
| | | // 检查任务时间 |
| | | if (searchForm.execute_time_arr) { |
| | | const now = new Date(); |
| | | const today = now.toDateString(); |
| | | const selectedDate = new Date(taskData.value).toDateString(); |
| | | |
| | | if (today === selectedDate) { |
| | | const [hours, minutes] = searchForm.execute_time_arr.split(':'); |
| | | const selectedTime = new Date(); |
| | | selectedTime.setHours(parseInt(hours), parseInt(minutes)); |
| | | |
| | | if (selectedTime < now) { |
| | | ElMessage({ |
| | | message: '任务时间不能小于当前时间', |
| | | type: 'warning' |
| | | }); |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | searchForm.begin_time = `${taskData.value} 00:00:00`; |
| | | searchForm.end_time = `${taskData.value} 23:59:59`; |
| | | |
| | | createTask(searchForm).then((res) => { |
| | | if (res.data.code === 0) { |
| | |
| | | rangDate.value = []; |
| | | waylineId.value = ''; |
| | | wayLineFile.value = ''; |
| | | taskData.value = ''; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getRouteList(); |
| | | }) |
| | |
| | | .task-contain { |
| | | display: flex; |
| | | .left { |
| | | width: 70%; |
| | | width: 68%; |
| | | .search { |
| | | display: grid; |
| | | grid-template-columns: repeat(3, 1fr); |
| | |
| | | gap: 30px; // 增加间距使布局更合理 |
| | | margin-bottom: 8px; // 添加底部间距 |
| | | .item { |
| | | position: relative; |
| | | display: flex; |
| | | align-items: center; |
| | | :deep(.el-input), |
| | |
| | | :deep(.el-date-editor.el-input__wrapper) { |
| | | width: 200px; // 调整日期选择器宽度 |
| | | } |
| | | .required { |
| | | color: #f56c6c; |
| | | margin-left: 4px; |
| | | position: absolute; |
| | | left: -10px; |
| | | top: 8px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .right { |
| | | width: 30%; |
| | | width: 32%; |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: space-between; |
| New file |
| | |
| | | <!--当前任务详情--> |
| | | <template> |
| | | <el-dialog |
| | | modal-class="current-task-details" |
| | | v-model="isShowCurrentTaskDetails" |
| | | title="当前任务详情" |
| | | :width="pxToRem(1500)" |
| | | :close-on-click-modal="false" |
| | | :destroy-on-close="true"> |
| | | <div class=""></div> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | const isShowCurrentTaskDetails = defineModel('show'); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .current-task-details {} |
| | | </style> |
| | |
| | | <el-table :data="jobListData" style="width: 100%" height="calc(100vh - 180px)"> |
| | | <el-table-column label="序号" width="60"> |
| | | <template #default="scope"> |
| | | {{ (jobListParams.page - 1) * jobListParams.limit + scope.$index + 1 }} |
| | | {{ (jobListParams.current - 1) * jobListParams.size + scope.$index + 1 }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="job_info_num" label="任务编号" /> |
| | |
| | | <el-table-column prop="dept_name" label="所属部门" /> |
| | | <el-table-column prop="device_names" label="所属机巢" /> |
| | | <el-table-column prop="ai_type_str" label="关联算法" /> |
| | | <el-table-column prop="status" label="任务状态" > |
| | | <el-table-column label="任务状态" > |
| | | <template #default="scope"> |
| | | {{ scope.row.status ? getStatusText(scope.row.status) : '' }} |
| | | </template> |
| | |
| | | </el-table> |
| | | <div class="pagination"> |
| | | <el-pagination |
| | | v-model:current-page="jobListParams.page" |
| | | v-model:page-size="jobListParams.limit" |
| | | v-model:current-page="jobListParams.current" |
| | | v-model:page-size="jobListParams.size" |
| | | :page-sizes="[10, 20, 30, 50]" |
| | | layout="total, sizes, prev, pager, next" |
| | | :total="total" |
| | |
| | | /> |
| | | </div> |
| | | </div> |
| | | <!-- 添加任务 --> |
| | | <AddTask v-model:show="isShowAddTask" @refresh="searchClick"/> |
| | | <!-- 当前任务详情 --> |
| | | <CurrentTaskDetails v-model:show="isShowCurrentTaskDetails"/> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import SearchBox from '../SearchBox.vue'; |
| | | import AddTask from './AddTask.vue'; |
| | | import CurrentTaskDetails from './CurrentTaskDetails.vue'; |
| | | import { jobList } from '@/api/home/task'; |
| | | |
| | | const jobListParams = reactive({ |
| | | page: 1, |
| | | limit: 10, |
| | | current: 1, |
| | | size: 10, |
| | | searchParams:{} |
| | | }); |
| | | const jobListData = ref([]); |
| | |
| | | 4: '已取消', |
| | | 5: '执行失败' |
| | | }; |
| | | return statusMap[status] || '未知'; |
| | | return statusMap[status] || '-'; |
| | | }; |
| | | |
| | | // 状态标签类型 |
| | | const getStatusType = (status) => { |
| | | const typeMap = { |
| | | 1: 'info', |
| | | 2: 'warning', |
| | | 3: 'success', |
| | | 4: '', |
| | | 5: 'danger' |
| | | }; |
| | | return typeMap[status] || ''; |
| | | }; |
| | | |
| | | // 查看详情 |
| | | // 查看当前任务详情 |
| | | let isShowCurrentTaskDetails = ref(false); |
| | | const handleDetail = (row) => { |
| | | console.log('查看详情', row); |
| | | isShowCurrentTaskDetails.value = true; |
| | | }; |
| | | |
| | | // 分页大小改变 |
| | | const handleSizeChange = (val) => { |
| | | jobListParams.limit = val; |
| | | jobListParams.size = val; |
| | | getJobList(); |
| | | }; |
| | | |
| | | // 页码改变 |
| | | const handleCurrentChange = (val) => { |
| | | jobListParams.page = val; |
| | | jobListParams.current = val; |
| | | getJobList(); |
| | | }; |
| | | |
| | | // 传参查询条件 |
| | | const searchClick = (params) => { |
| | | jobListParams.page = 1; |
| | | jobListParams.limit = 10; |
| | | jobListParams.current = 1; |
| | | jobListParams.size = 10; |
| | | jobListParams.searchParams = params; |
| | | getJobList(); |
| | | }; |
| | |
| | | <template> |
| | | <div id="taskMap" /> |
| | | <div id="taskMap"></div> |
| | | </template> |
| | | <script setup> |
| | | import * as Cesium from 'cesium' |
| | |
| | | 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 props = defineProps(['wayLineFile']) |
| | | const store = useStore(); |
| | | const userAreaPosition = computed(() => store.state.home.userAreaPosition); |
| | | |
| | | // 声明事件 |
| | | const emit = defineEmits(['clickPosition', 'saveWayline']); |
| | | |
| | | const props = defineProps({ |
| | | wayLineFile: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | checkedTableData: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | waylineTypeTest: { |
| | | type: Number, |
| | | default: 3 |
| | | } |
| | | }) |
| | | |
| | | const imageryProvider_ammapSL = new Cesium.UrlTemplateImageryProvider({ |
| | | url: 'https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', |
| | |
| | | maximumLevel: 18, |
| | | tilingScheme: new AmapMercatorTilingScheme(), |
| | | credit: 'amap_SL', |
| | | }) |
| | | let viewer = null |
| | | }); |
| | | |
| | | let viewer = null; |
| | | let currentEntity = null; |
| | | let connectLines = []; // 存储连接线实体 |
| | | let polygonPoints = []; // 存储多边形的点 |
| | | let polygonEntity = null; // 存储多边形实体 |
| | | let existingEntity = null; // 后端返回数据生成得面状线 |
| | | // 添加变量跟踪当前菜单 |
| | | let currentMenu = null; |
| | | |
| | | |
| | | const init = () => { |
| | | viewer = new Viewer('taskMap', { |
| | | terrain: Terrain.fromWorldTerrain(), |
| | |
| | | }) |
| | | viewer.imageryLayers.addImageryProvider(imageryProvider_ammapSL) |
| | | viewer.scene.morphTo2D(0); |
| | | //设置默认点 |
| | | 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) => { |
| | | 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 intelligentPlanning = () => { |
| | | // 添加点击事件监听 |
| | | viewer.screenSpaceEventHandler.setInputAction((click) => { |
| | | if (props.waylineTypeTest !== 2) return |
| | | 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 |
| | | } |
| | | }); |
| | | |
| | | // 存储点位 |
| | | polygonPoints.push(cartesian); |
| | | |
| | | // 当点击超过2个点时绘制多边形 |
| | | if (polygonPoints.length > 2) { |
| | | // 移除旧的多边形 |
| | | if (polygonEntity) { |
| | | viewer.entities.remove(polygonEntity); |
| | | } |
| | | |
| | | // 创建新的多边形 |
| | | polygonEntity = viewer.entities.add({ |
| | | polygon: { |
| | | hierarchy: new Cesium.PolygonHierarchy(polygonPoints), |
| | | // material: new Cesium.Color.fromBytes(212, 46, 32, 100), |
| | | material: new Cesium.Color(0, 0.5, 1, 0.3), // 蓝色 |
| | | outline: true, |
| | | // outlineColor: new Cesium.Color.fromBytes(212, 46, 32, 255), |
| | | outlineColor: new Cesium.Color(0, 0.5, 1, 1), // 蓝 |
| | | outlineWidth: 2, |
| | | height: 0, // Set explicit height |
| | | 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); |
| | | // emit('clickPosition', cartographic); |
| | | |
| | | } |
| | | }, Cesium.ScreenSpaceEventType.LEFT_CLICK); |
| | | // 修改右键点击事件,添加菜单 |
| | | viewer.screenSpaceEventHandler.setInputAction((movement) => { |
| | | if (props.waylineTypeTest !== 2) return |
| | | if (polygonPoints.length > 2) { |
| | | // 清除之前的菜单 |
| | | if (currentMenu) { |
| | | document.body.querySelectorAll('.context-menu').forEach(menu => menu.remove()); |
| | | } |
| | | |
| | | const menuContainer = document.createElement('div'); |
| | | menuContainer.className = 'context-menu'; |
| | | |
| | | // 获取地图容器 |
| | | 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.innerHTML = ` |
| | | <div class="menu-item" id="saveWayline">保存航线</div> |
| | | <div class="menu-item" id="cancelDraw">取消绘制</div> |
| | | `; |
| | | |
| | | mapContainer.appendChild(menuContainer); |
| | | currentMenu = menuContainer; |
| | | |
| | | // 添加全局点击事件监听 |
| | | const handleClickOutside = (e) => { |
| | | if (!menuContainer) return; |
| | | const isClickInside = menuContainer.contains(e.target); |
| | | if (!isClickInside) { |
| | | menuContainer.remove(); |
| | | document.removeEventListener('mousedown', handleClickOutside); |
| | | } |
| | | }; |
| | | |
| | | // 延迟添加事件监听,避免右键点击立即触发 |
| | | setTimeout(() => { |
| | | document.addEventListener('mousedown', handleClickOutside); |
| | | }, 100); |
| | | |
| | | // 菜单按钮点击事件 |
| | | document.getElementById('saveWayline').onclick = () => { |
| | | const coordinates = polygonPoints.map(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); |
| | | }; |
| | | |
| | | document.getElementById('cancelDraw').onclick = () => { |
| | | polygonPoints = []; |
| | | viewer.entities.removeAll(); |
| | | mapContainer.removeChild(menuContainer); |
| | | }; |
| | | } |
| | | }, Cesium.ScreenSpaceEventType.RIGHT_CLICK); |
| | | }; |
| | | |
| | | // 保存航线并且获取线 |
| | | 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); |
| | | }); |
| | | } |
| | | |
| | | // 绘制后端生成得面状线 |
| | | const drawResultWayline = (dataValue) => { |
| | | // 先检查并删除已存在的航线 |
| | | 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 |
| | | ) |
| | | cartesian3List.value.push(cartesian3) |
| | | }); |
| | | const setting = { |
| | | id: 'result_wayline', |
| | | polyline: { |
| | | width: 2, |
| | | positions: cartesian3List.value, |
| | | material: Cesium.Color.CHARTREUSE, |
| | | }, |
| | | } |
| | | existingEntity = viewer?.entities.add({ |
| | | polyline: setting.polyline, |
| | | id: setting.id, |
| | | }) |
| | | }; |
| | | |
| | | // 选中航线时调用 渲染线和点 type = 0 |
| | | const renderingLine = lineObj => { |
| | | const positions = lineObj.Placemark.map(item => { |
| | | const [lon, lat] = item.Point.coordinates.split(',') |
| | | return Cartesian3.fromDegrees(Number(lon), Number(lat)) |
| | | }) |
| | | // viewer.entities.add({ |
| | | // position: positions[0], |
| | | // billboard: { |
| | | // image: new Cesium.ConstantProperty(uavImg), |
| | | // width: 24, |
| | | // height: 24, |
| | | // }, |
| | | // }); |
| | | |
| | | viewer.entities.add({ |
| | | polyline: { |
| | | width: 4, |
| | |
| | | clampToGround: false, |
| | | }, |
| | | }) |
| | | console.log('positions',positions) |
| | | |
| | | 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 if (index === positions.length - 1) { |
| | | // 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 |
| | | if (index === positions.length - 1) { |
| | | setting = { |
| | | position: point, |
| | | id: `point_${index}`, |
| | |
| | | position: point, |
| | | id: `point_${index}`, |
| | | label: { |
| | | text: `${index}`, |
| | | text: `${index+1}`, |
| | | font: 'bold 14px serif', |
| | | style: Cesium.LabelStyle.FILL, |
| | | verticalOrigin: Cesium.VerticalOrigin.CENTER, // 垂直居中 |
| | |
| | | }) |
| | | } |
| | | |
| | | // const drawLine = () => { |
| | | // console.log('尽量么'); |
| | | // // props.wayLineFile.way_lines.forEach(item => { |
| | | // // let prexUrl = ref(import.meta.env.VITE_APP_AIRLINE_URL+ props.wayLineFile); |
| | | // let prexUrl2 = ref('https://wrj.shuixiongit.com/minio/cloud-bucket/wayline/20241224/wayline_1735032173222.kmz'); |
| | | |
| | | // analyzeKmzFile(`${prexUrl2.value}?_t=${new Date().getTime()}`).then(async res => { |
| | | // const templateXML = await res.fileInfoObj['wpmz/template.kml'] |
| | | // const templateXMLJSON = XMLToJSON(templateXML)?.['Document'] |
| | | // const templateXMLObj = removeTextKey(templateXMLJSON.Folder) |
| | | // renderingLine(templateXMLObj) |
| | | // }) |
| | | // // }) |
| | | // } |
| | | |
| | | // 异步解析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); |
| | | if (!res.Placemark.length) return |
| | | renderingLine(res); |
| | | const points = res.Placemark.map(item => item.Point.coordinates.split(',')) |
| | | flyToPoints(points) |
| | | } |
| | | |
| | | watch(() => props.wayLineFile, async (newValue, oldValue) => { |
| | | if(newValue){ |
| | | await removeMap(); |
| | | await init(); |
| | | // 选择航线时,根据选择机巢连线 |
| | | const selectLineFile = (newVal) => { |
| | | // 清除之前的实体 |
| | | // viewer.entities.removeAll(); |
| | | // 重新绘制航线 |
| | | // if (props.wayLineFile) { |
| | | // drawLine(); |
| | | // } |
| | | // 添加选中点和连接线 |
| | | const positions = newVal.map(item => { |
| | | const position = Cartesian3.fromDegrees(Number(item.longitude), Number(item.latitude)); |
| | | // 添加机巢点 |
| | | viewer.entities.add({ |
| | | position: position, |
| | | billboard: { |
| | | image: new Cesium.ConstantProperty(uavImg), |
| | | width: 24, |
| | | height: 24, |
| | | }, |
| | | }); |
| | | return position; |
| | | }); |
| | | |
| | | // 添加连接线 |
| | | if (positions.length > 1) { |
| | | viewer.entities.add({ |
| | | polyline: { |
| | | positions: positions, |
| | | width: 2, |
| | | material: new Cesium.PolylineDashMaterialProperty({ |
| | | color: Cesium.Color.RED, |
| | | dashLength: 8.0 |
| | | }) |
| | | } |
| | | }); |
| | | } |
| | | // 飞到中心点 |
| | | const lngLatArr = newVal.map(item => [item.longitude, item.latitude]); |
| | | flyToPoints(lngLatArr); |
| | | }; |
| | | |
| | | // 单个点生成选择多个机巢生成航线 |
| | | const singlePointLines = (newVal) => { |
| | | // 清除之前的连接线 |
| | | connectLines.forEach(line => viewer.entities.remove(line)); |
| | | connectLines = []; |
| | | |
| | | if (currentEntity) { |
| | | // 获取当前点的位置 |
| | | const currentPosition = currentEntity.position.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 line = viewer.entities.add({ |
| | | polyline: { |
| | | positions: [currentPosition, nestPosition], |
| | | width: 2, |
| | | material: new Cesium.PolylineDashMaterialProperty({ |
| | | color: Cesium.Color.RED, |
| | | dashLength: 8.0 |
| | | }) |
| | | } |
| | | }); |
| | | connectLines.push(line); |
| | | }); |
| | | |
| | | // 飞到所有点的中心位置 |
| | | 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, |
| | | }, |
| | | }); |
| | | |
| | | // 找到最近的航线点并连线 |
| | | 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.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 }); |
| | | |
| | | const removeEvent = () => { |
| | | // 清除事件监听器 |
| | | viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK); |
| | | viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK); |
| | | }; |
| | | |
| | | const removeMap = () => { |
| | | viewer.entities.removeAll() |
| | | viewer.destroy() |
| | | // 清除所有实体 |
| | | 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() |
| | | removeMap(); |
| | | removeEvent(); |
| | | // 移除所有实体并销毁viewer |
| | | viewer.destroy(); |
| | | viewer = null; |
| | | }) |
| | | |
| | | onMounted(() => { |
| | | nextTick(() => { |
| | | init() |
| | | }) |
| | | }) |
| | | }); |
| | | </script> |
| | | <style scoped lang="scss"> |
| | | #taskMap { |
| | | position: relative; |
| | | height: 100%; |
| | | |
| | | :deep() { |
| | |
| | | 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); |
| | | |
| | | .menu-item { |
| | | padding: 8px 16px; |
| | | color: #fff; |
| | | 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"> |
| | | <el-table-column type="selection" width="55" /> |
| | | <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> |
| | | <div class="pagination"> |
| | | <el-pagination |
| | | v-model:current-page="pageParams.page" |
| | | v-model:page-size="pageParams.limit" |
| | | 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" |
| | |
| | | }, |
| | | waylineType: { |
| | | type: Number, |
| | | default: 0 |
| | | default: 3 |
| | | }, |
| | | singlePoint: { |
| | | type: Object, |
| | | default: () => ({}) |
| | | }, |
| | | planarPoints: { |
| | | type: Array, |
| | | default: () => ([]) |
| | | } |
| | | }); |
| | | |
| | | // 选中的数据 |
| | | const selectedRows = ref([]); |
| | | // 分页参数 |
| | | let pageParams = reactive({ |
| | | let pageParams = ref({ |
| | | wayline_id: props.waylineId, |
| | | type: props.waylineType, |
| | | longitude: props.singlePoint.longitude, |
| | | latitude: props.singlePoint.latitude, |
| | | polygon: [], |
| | | }); |
| | | let pagingParams = ref({ |
| | | page: 1, |
| | | limit: 10 |
| | | size: 10 |
| | | }); |
| | | // 获取可用机巢列表数据 |
| | | const total = ref(0); |
| | | const tableData = ref([]); |
| | | const getNestList = async () => { |
| | | tableData.value = []; |
| | | const res = await getFlyingNestBy(pageParams); |
| | | |
| | | const res = await getFlyingNestBy(pageParams.value, pagingParams.value); |
| | | if (res.data.code === 0) { |
| | | console.log(res.data.data, '哒哒哒'); |
| | | tableData.value = res.data.data; |
| | | } |
| | | }; |
| | | // 分页大小改变 |
| | | const handleSizeChange = (val) => { |
| | | pageParams.limit = val; |
| | | pagingParams.value.size = val; |
| | | getNestList(); |
| | | }; |
| | | |
| | | // 页码改变 |
| | | const handleCurrentChange = (val) => { |
| | | pageParams.page = val; |
| | | pagingParams.value.page = val; |
| | | getNestList(); |
| | | }; |
| | | |
| | | // 刷新数据 |
| | | const refreshData = () => { |
| | | tableData.value = []; |
| | | pagingParams.value.page = 1; |
| | | getNestList(); |
| | | }; |
| | | |
| | | const emit = defineEmits(['update:selected']); |
| | | const handleSelectionChange = (val) => { |
| | | selectedRows.value = val; |
| | | // 可以通过emit将选中数据传给父组件 |
| | | emit('update:selected', 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 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); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 监听航线ID变化 |
| | | watch(() => props.waylineId, (newVal) => { |
| | | pageParams.wayline_id = newVal; |
| | | pageParams.value.type = 0; |
| | | if (newVal) { |
| | | pageParams.page = 1; |
| | | getNestList(); |
| | | pageParams.value.wayline_id = newVal; |
| | | refreshData(); |
| | | } else { |
| | | // 数据为空时,清除列表数据 |
| | | tableData.value = []; |
| | | } |
| | | }, { immediate: true }); |
| | | onMounted(() => { |
| | | // getNestList(); |
| | | }, { 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 }); |
| | | |
| | | // 暴露给父组件的方法 |
| | | const clearTableData = () => { |
| | | tableData.value = []; |
| | | }; |
| | | defineExpose({ |
| | | clearTableData |
| | | }); |
| | | |
| | | |
| | | onMounted(() => {}); |
| | | |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |