<template>
|
<div class="AINowFly">
|
<div class="title">智引即飞<img @click="closeClick" src="@/assets/images/aiNowFly/close.png"/></div>
|
<el-form ref="ruleFormRef" :model="params" :rules="rules" label-width="auto" size="small" status-icon>
|
<el-form-item label="任务名称" prop="name">
|
<el-input class="ztzf-input" v-model="params.name" />
|
</el-form-item>
|
<el-form-item label="关联算法" prop="ai_types">
|
<el-select :teleported="false" class="ztzf-select" v-model="params.ai_types" placeholder="请选择" multiple collapse-tags collapse-tags-tooltip>
|
<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 class="ztzf-input" disabled type="text" v-model="di" placeholder="地名&经纬度"/>
|
</el-form-item>
|
<!-- todo-->
|
<el-form-item label="飞行事件">
|
<div class="event">
|
<img src="../../assets/images/aiNowFly/event/startRecord.png" alt="">
|
<div class="img-close" v-show="isPhoto">
|
<img @click="isPhoto=false" class="close" src="@/assets/images/aiNowFly/close.png" alt="">
|
<img src="../../assets/images/aiNowFly/event/takePhoto.png" alt="">
|
</div>
|
<div class="img-close" v-show="isStop">
|
<img @click="isStop=false" class="close" src="@/assets/images/aiNowFly/close.png" alt="">
|
<img src="../../assets/images/aiNowFly/event/hover.png" alt="">
|
</div>
|
<div class="add-event" @click="isShowEvent = !isShowEvent">+
|
<div class="event-select" v-show="isShowEvent">
|
<div class="photo" @click="isPhoto=true">拍照</div>
|
<div class="stop" @click="isStop=true">悬停</div>
|
</div>
|
</div>
|
</div>
|
</el-form-item>
|
</el-form>
|
|
<div class="title-list">可飞行机巢列表:</div>
|
<div class="tabledata">
|
<div class="table-content">
|
<div class="tabler-item">
|
<el-checkbox v-model="isCheckAll" @change="checkedAll"/>
|
</div>
|
<div class="table-item">机巢名称</div>
|
<div class="table-item">可飞行时间</div>
|
<div class="table-item">可飞行距离</div>
|
</div>
|
<div class="table-content" v-for="item in list">
|
<div class="tabler-item">
|
<el-checkbox v-model="item.checked" @change="handleItemCheck"/>
|
</div>
|
<div class="table-item">{{ item.nickname }}</div>
|
<div class="table-item">{{ item.minute }}(min)</div>
|
<div class="table-item">{{ item.exe_distance }}(km)</div>
|
</div>
|
</div>
|
<div class="btn-submit" @click="nowFly"><img src="@/assets/images/aiNowFly/fly.png" alt=""></div>
|
</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 endingImg from '@/assets/images/aiNowFly/ending.png'
|
import endingHighImg from '@/assets/images/aiNowFly/ending-high.png'
|
import startingImg from '@/assets/images/aiNowFly/starting.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,
|
action_modes: [{action_actuator_func: 'startRecord'}],
|
}
|
const params = ref(_.cloneDeep(paramsInit))
|
const di = computed(() => params.value.longitude + ',' + params.value.latitude)
|
|
// 事件默认隐藏
|
const isShowEvent = ref(false);
|
|
// 添加事件
|
const isPhoto = ref(false)
|
const isStop = ref(false)
|
|
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('请选择飞行的点')
|
// 先清除,再push
|
params.value.action_modes = [{action_actuator_func:'startRecord'}]
|
if (isPhoto.value) {
|
params.value.action_modes.push({action_actuator_func:'takePhoto'})
|
}
|
if (isStop.value) {
|
params.value.action_modes.push({action_actuator_func:'hover'})
|
}
|
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]
|
// 计算中点位置
|
const midpoint = Cartesian3.midpoint(start, end, new Cartesian3())
|
// 计算线段方向
|
const direction = Cartesian3.subtract(end, start, new Cartesian3())
|
Cartesian3.normalize(direction, direction)
|
// 线
|
// 添加中点文字标签
|
viewer.entities.add({
|
id: `AINow-time-${index}`,
|
position: midpoint,
|
label: {
|
text: `预计${item.minute}分钟`,
|
font: 'bold 16px Source Han Sans CN', // 加粗并增大字号
|
// fillColor: new Cesium.Color(27/255, 179/255, 255/255, 1.0),
|
// fillColor: Cesium.Color.WHITE,
|
// style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
// verticalOrigin: Cesium.VerticalOrigin.CENTER,
|
// horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
// eyeOffset: new Cesium.Cartesian3(0, 0, -10),
|
// pixelOffset: new Cesium.Cartesian2(0, -10),
|
rotation: 12.785398, // 修改旋转计算方式
|
}
|
})
|
viewer.entities.add({
|
id: `AINow-line-${index}`,
|
polyline: {
|
width: 2,
|
positions: positionsList,
|
material: index === 0 ? new Cesium.Color(2/255, 192/255, 255/255, 1) : new Cesium.Color(27/255, 179/255, 255/255, 0.4),
|
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(index === 0 ? endingHighImg : endingImg),
|
width: 40,
|
height: 40,
|
},
|
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`,
|
billboard: {
|
image: new Cesium.ConstantProperty(startingImg),
|
width: 40,
|
height: 40,
|
}
|
})
|
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']
|
}
|
})
|
}
|
|
let pagingParams = ref({
|
current: 1,
|
size: 10
|
});
|
|
const isCheckAll = ref(false);
|
// 获取飞机列表
|
const getFJList = async () => {
|
let pageParams = ref({
|
...eventPoint,
|
type: 1,
|
});
|
params.value.dock_sns = [];
|
const res = await getFlyingNestBy(pageParams.value, pagingParams.value)
|
list.value = (res.data?.data || []).map(item => ({
|
...item,
|
checked: false,
|
minute: _.round(item.estimated_arrival_time / 60, 0),
|
}))
|
if (!list.value.length) ElMessage.warning('附近暂无可用无人机')
|
renderingLine()
|
}
|
|
// 单个选择框变化
|
const handleItemCheck = () => {
|
const allChecked = list.value.every(item => item.checked)
|
const someChecked = list.value.some(item => item.checked)
|
isCheckAll.value = allChecked
|
params.value.dock_sns = list.value.filter(item => item.checked).map(item => item.device_sn)
|
}
|
|
// 全选
|
const checkedAll = () => {
|
list.value.forEach(item => {
|
item.checked = isCheckAll.value
|
})
|
params.value.dock_sns = isCheckAll.value ? list.value.map(item => item.device_sn) : []
|
}
|
|
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
|
}
|
|
// 关闭
|
const closeClick = () => {
|
removeAll()
|
params.value = _.cloneDeep(paramsInit)
|
list.value = []
|
eventPoint = {}
|
store.commit('setFootActiveIndex', 0)
|
// 显示相关的按钮
|
store.commit('setHideBottomIcon', true)
|
}
|
|
onBeforeUnmount(() => {
|
removeAll()
|
})
|
|
onMounted(() => {
|
requestDictionary()
|
viewer = window.$viewer
|
initMap()
|
})
|
</script>
|
<style scoped lang="scss">
|
.AINowFly {
|
position: absolute;
|
top: 263px;
|
right: 74px;
|
width: 320px;
|
height: 518.6px;
|
font-size: 14px;
|
color: #FFFFFF;
|
background: url('@/assets/images/aiNowFly/ai-fly-bg.png') no-repeat center / 100% 100%;
|
.title {
|
height: 50px;
|
line-height: 50px;
|
margin-left: 20px;
|
font-family: Segoe UI, Segoe UI;
|
font-weight: bold;
|
font-size: 20px;
|
letter-spacing: 2px;
|
background: linear-gradient(180deg, #FFFFFF 50%, #35D0FF 100%);
|
-webkit-background-clip: text;
|
-webkit-text-fill-color: transparent;
|
background-clip: text;
|
img {
|
position: absolute;
|
cursor: pointer;
|
width: 16px;
|
height: 16px;
|
right: 15px;
|
top: 15px;
|
}
|
}
|
.el-form {
|
padding: 20px 16px 13px 11px;
|
:deep(.el-form-item__label) {
|
color: #FFFFFF;
|
}
|
:deep(.el-input__wrapper),
|
:deep(.el-select),
|
:deep(.el-select__wrapper) {
|
background-color: #021740;
|
&.is-focus {
|
box-shadow: 0 0 0 1px #026AD6;
|
}
|
}
|
:deep(.el-tag, .el-tag.el-tag--primary),
|
:deep(.el-tag.el-tag--info) {
|
--el-tag-bg-color: none !important;
|
--el-tag-border-color: none !important;
|
--el-tag-hover-color: none !important;
|
}
|
:deep(.el-input__wrapper.is-disabled) {
|
background-color: #021740;
|
box-shadow: 0 0 0 1px #026AD6;
|
}
|
.event {
|
display: flex;
|
img {
|
width: 40px;
|
height: 34px;
|
margin-right: 10px;
|
}
|
.img-close {
|
position: relative;
|
.close {
|
cursor: pointer;
|
width: 8px;
|
height: 8px;
|
position: absolute;
|
top: -8px;
|
right: 1px;
|
}
|
}
|
.add-event {
|
width: 38px;
|
height: 38px;
|
background: rgba(83,179,255,0.16);
|
border-radius: 8px 8px 8px 8px;
|
border: 1px dashed;
|
text-align: center;
|
line-height: 38px;
|
color: #11C4FF;
|
position: relative;
|
cursor: pointer;
|
// border-image: linear-gradient(180deg, rgba(17, 196, 255, 1), rgba(255, 255, 255, 1)) 1 1;
|
.event-select {
|
z-index: 10;
|
position: absolute;
|
top: 40px;
|
width: 72px;
|
height: 70px;
|
background: #0F1929;
|
box-shadow: inset 0px -50px 50px 0px rgba(27,148,255,0.13);
|
border-radius: 8px 8px 8px 8px;
|
border: 1px solid #51A8FF;
|
text-align: center;
|
color: #D3E9FF;
|
.photo {
|
height: 36px;
|
border-bottom: 1px solid #51A8FF;
|
}
|
}
|
}
|
}
|
}
|
.title-list {
|
padding: 0 0 14px 11px;
|
}
|
.tabledata {
|
min-height: 120px;
|
border: 1px dashed #fff;
|
margin: 0px 10px;
|
.table-content {
|
padding: 0 8px;
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
&:not(:last-child) {
|
border-bottom: 1px dashed #fff;
|
}
|
}
|
.table-item {
|
width: 100px;
|
text-align: center;
|
height: 32px;
|
line-height: 32px;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
white-space: nowrap;
|
}
|
}
|
|
.btn-submit {
|
cursor: pointer;
|
margin-left: 88px;
|
margin-top: 12px;
|
img { width: 138px; height: 38px; }
|
}
|
}
|
</style>
|