提交 40c90ae4 作者: 翁进城

feat:

1. 创建航线
2. 任务库改航线库
上级 c538d864
流水线 #10706 已失败 于阶段
......@@ -9,6 +9,7 @@
:scene="scene"
:useSTLAirway="useSTLAirway"
:useTimedTask="useTimedTask"
:useAirway="useAirway"
@tokenInvalid="dispatchEvent('tokenInvalid')"
@uavChange="dispatchEvent('uavChange', $event)"
@uavTaskStart="dispatchEvent('uavTaskStart', $event)"
......@@ -84,6 +85,7 @@ export default {
uavTaskAddCB: null, //机库创建任务回调
airwayPageChangeCB: null, //航线翻页时回调
airwayGetCB: null, //获取航线数据回调
useAirway: true, //使用航线而不使用任务起飞
};
},
computed: {},
......
<template>
<div class="rm-actions dialog1027" v-interact>
<div class="dialog-header">
<img class="dialog-header__icon" src="../../../../../../assets/images/mount_head.png" />
<div class="dialog-header__title">航点动作</div>
<div class="dialog-header__close">
<el-dropdown trigger="click">
<span class="el-dropdown-link">
<div class="icon el-icon-plus" title="添加"></div>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-for="(item, index) in actions"
@click.native="selectActions1.push(item)"
>{{ item.label }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<div class @click="$emit('close')">关闭</div>
</div>
</div>
<div class="rm-as-list">
<div
class="rm-as-item"
v-for="(item, i) in selectActions1"
:key="i"
:class="{double: ['云台控制'].includes(item.label)}"
>
<div class="rm-as-item__title">{{item.label}}</div>
<div class="rm-as-item__input" v-if="['间隔拍照', '悬停', '变焦'].includes(item.label)">
<div>
<el-input-number
:min="item.min"
:max="item.max"
v-if="['间隔拍照', '悬停'].includes(item.label)"
v-model="item.param1"
></el-input-number>
<el-input-number
:min="item.min"
:max="item.max"
v-if="['变焦'].includes(item.label)"
v-model="item.param2"
></el-input-number>
{{item.unit}}
</div>
</div>
<div class="rm-as-item__input" v-if="['云台控制'].includes(item.label)">
<div>
(俯仰)
<el-input-number :min="-90" :max="30" v-model="item.param1"></el-input-number>(度)
</div>
<div>
(偏航)
<el-input-number :min="-145" :max="145" v-model="item.param3"></el-input-number>(度)
</div>
</div>
<i class="el-icon-delete rm-as-item__del" v-hover @click="selectActions.splice()"></i>
</div>
</div>
<div class="rm-rp-btns">
<div class="rm-rp-btn" v-hover @click="$emit('close')">取消</div>
<div class="rm-rp-btn save" v-hover @click="$emit('save', selectActions1)">保存</div>
</div>
</div>
</template>
<script>
export default {
name: "Actions",
props: {
selectActions: {
type: Array,
default() {
return [];
},
},
},
data() {
return {
actions: [
{
label: "无动作",
actionType: "",
param1: 1,
},
{
label: "间隔拍照",
actionType: "TRIGGER",
param1: 1,
unit: "米",
},
{
label: "悬停",
min: 0,
// max: 32000,
actionType: "STAY",
param1: 10,
unit: "秒",
},
{
label: "云台控制",
actionType: "GIMBAL_PITCH",
param1: 0,
param2: 0,
param3: 0,
param4: 2,
},
{
label: "开始录像",
actionType: "START_RECORD",
param1: 1,
},
{
label: "停止录像",
actionType: "STOP_RECORD",
param1: 1,
},
{
label: "变焦",
min: 0,
// max: 32000,
actionType: "CAMERA_ZOOM",
param1: 2,
param2: 0,
unit: "倍",
},
{
label: "拍照",
actionType: "START_TAKE_PHOTO",
param3: 1,
},
], //全部动作
selectActions1: [], //选中的动作
};
},
created(){
this.selectActions1 = JSON.parse(JSON.stringify(this.selectActions));
}
};
</script>
<style lang="scss" scoped>
.rm-actions {
position: absolute;
left: 480px;
top: 0;
height: 100%;
width: 420px;
background: #222222;
display: flex;
flex-direction: column;
.dialog-header__close::v-deep {
display: flex;
gap: 20px;
.icon {
color: #fff;
}
}
.rm-as-list {
padding: 16px;
flex: auto;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 16px;
.rm-as-item::v-deep {
background: #3c3c3c;
border-radius: 1px;
display: flex;
align-items: center;
font-weight: 400;
font-size: 14px;
color: #dedede;
line-height: 20px;
padding: 16px 16px;
justify-content: space-between;
&.double {
.rm-as-item__title {
width: 60px;
}
}
.el-input__inner {
height: 32px;
background: #191919;
border-radius: 2px;
border: 1px solid #4b4b4b;
}
.rm-as-item__title {
width: 120px;
}
.rm-as-item__input {
width: 140px;
flex: auto;
display: flex;
flex-direction: column;
gap: 16px;
.el-input-number {
width: 140px;
line-height: 32px;
}
.el-input-number__decrease {
width: 32px;
height: 32px;
background: transparent;
border-right: 1px solid #4b4b4b;
}
.el-input-number__increase {
width: 32px;
height: 32px;
background: transparent;
border-left: 1px solid #4b4b4b;
}
}
.rm-as-item__del {
font-size: 24px;
}
}
}
}
.rm-rp-btns {
flex: 0;
padding: 32px 0;
display: flex;
justify-content: center;
gap: 32px;
}
.rm-rp-btn {
width: 100px;
height: 32px;
border-radius: 2px;
border: 1px solid #3388ff;
font-weight: 400;
font-size: 14px;
color: #ffffff;
line-height: 16px;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
&.save {
background: #3388ff;
border: none;
}
&.del {
background: #f56c6c;
border: none;
}
}
</style>
\ No newline at end of file
<template>
<div class="airway-edit dialog1027" v-interact>
<div class="dialog-header">
<img class="dialog-header__icon" src="../../../../assets/images/mount_head.png" />
<div class="dialog-header__title">手动规划</div>
<div class="dialog-header__close" @click="$emit('close')">关闭</div>
</div>
<div class="dialog-content">
<div class="ae-page">
<div v-hover @click="onPrev">上一航点</div>
<div>{{ pageIndex }} / {{ pageCount }}</div>
<div v-hover @click="onNext">下一航点</div>
</div>
<el-form class="ae-form" :form="curForm" label-width="100px">
<el-form-item label="航线名称" required>
<el-input clearable v-model="name"></el-input>
</el-form-item>
<el-form-item label="航线速度" prop="speed">
<el-input clearable v-model="curForm.speed"></el-input>
</el-form-item>
<el-form-item label="目标位置" prop="address">
<el-autocomplete
:popper-append-to-body="false"
v-model="curForm.address"
:fetch-suggestions="onAddressInput"
placeholder="请输入目标位置"
:trigger-on-focus="false"
@select="
(item) => {
onAddressChange(item, 'end');
}
"
clearable
>
<template slot-scope="{ item }">
<div>
<span style="font-size: 14px; color: #9e9e9e">
{{
item.name
}}
</span>
<span style="font-size: 12px; color: #999; margin-left: 12px">{{ item.address }}</span>
</div>
</template>
<el-button slot="append" icon="el-icon-location" @click="onPickAddress"></el-button>
</el-autocomplete>
</el-form-item>
<el-form-item label="纬度" prop="latitude">
<el-input clearable v-model="curForm.latitude"></el-input>
</el-form-item>
<el-form-item label="经度" prop="longitude">
<el-input clearable v-model="curForm.longitude"></el-input>
</el-form-item>
<el-form-item label="高度" prop="altitude">
<el-input clearable v-model="curForm.altitude"></el-input>
</el-form-item>
<el-form-item label="航线标签" prop="label">
<el-input clearable v-model="curForm.label"></el-input>
</el-form-item>
<el-form-item label="航点动作">
<el-button type="text" @click="showActions = true">{{curForm.actions.length}}个动作</el-button>
</el-form-item>
<el-form-item label="航线总里程">{{distance}}m</el-form-item>
<el-form-item label="预计飞行时间">{{time}}s</el-form-item>
</el-form>
<div class="ae-btns">
<el-button type="primary" @click="onSave">保存</el-button>
<el-button type="danger" @click="onDel">删除航点</el-button>
</div>
</div>
<Actions
v-if="showActions"
@close="showActions = false"
@save="onActionsSave"
:selectActions="curForm.actions"
></Actions>
</div>
</template>
<script>
import { Map } from "../../../../../../../../../../api";
import { mapState } from "vuex";
import Utils from "../../../../../../../../../../components/cesiumLayer/lib/cesium/utils";
import { nanoid } from "nanoid";
import Actions from "./components/actions";
class Airway {
uuid = nanoid();
speed = "6";
address = "";
latitude = "";
longitude = "";
altitude = "100";
label = "";
actions = [];
position = null; // cesium卡迪尔积三维数据
distance = 0; // 与上一个航点的距离
}
export default {
name: "AirwayEdit",
components: {
Actions,
},
inject: ["rootNode"],
data() {
return {
name: "", //航线名
form: [], //表单集合
pageIndex: 0, //当前页码
locationIcon: null, //定位图标
handler: null, //地图内容抓取事件
dataSource: null, //航点数据集
positions: [], //航点坐标集合
isPickAddress: false, //是否通过目标位置对地理信息
showActions: false, // 打开航点动作
};
},
computed: {
...mapState("MMCFlightControlCenter", ["cesiumViewer"]),
// 当前的表单
curForm() {
return this.form[this.pageIndex - 1] || new Airway();
},
//总页数,
pageCount() {
return this.form.length;
},
//总里程
distance() {
let distance = 0;
this.form.forEach((item) => {
distance += item.distance;
});
return distance;
},
//预计总时间
time() {
let time = 0;
this.form.forEach((item) => {
let speed = item.speed || 1;
time += parseInt(item.distance / speed);
});
return time;
},
},
mounted() {
let viewer = this.cesiumViewer;
this.dataSource = new Cesium.CustomDataSource("airway_edit");
viewer.dataSources.add(this.dataSource);
this.handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas); //获取事件对象
// 绘制航线
let polyline = this.dataSource.entities.add({
polyline: {
material: Cesium.Color.RED,
width: 3,
positions: new Cesium.CallbackProperty(() => {
return this.positions.map((item) => {
let wgs84 = Utils.transformCartesian2WGS84(item);
return Utils.transformWGS842Cartesian({
lng: wgs84.lng,
lat: wgs84.lat,
alt: wgs84.alt + 10,
});
});
}, false),
},
});
// 监听取点事件
this.handler.setInputAction(
this.onPickPoint,
Cesium.ScreenSpaceEventType.LEFT_CLICK
);
this.pickerPointInit();
},
beforeDestroy() {
this.handler.removeInputAction(
this.onPickPoint,
Cesium.ScreenSpaceEventType.LEFT_DOWN
);
this.cesiumViewer.dataSources.remove(this.dataSource);
},
methods: {
// 保存航线
onSave() {
if (!this.name) {
this.$message.warning("请输入航线名称");
return;
}
if (this.form.length === 0) {
this.$message.warning("请点击地图选择航点");
return;
}
let waypoints = this.form.map((val) => {
let actions = val.actions.map((item) => {
let ret = {
actionType: item.actionType,
};
if (item.param1) {
ret.param1 = item.param1;
}
if (item.param2) {
ret.param2 = item.param2;
}
if (item.param3) {
ret.param3 = item.param3;
}
if (item.param4) {
ret.param4 = item.param4;
}
return ret;
});
return {
altitude: val.altitude,
coordinate: {
latitude: val.latitude,
longitude: val.longitude,
},
frame: 3,
speed: val.speed,
stay: 0,
waypointActions: actions,
};
});
this.rootNode.$emit("uavAirwayAdd", {
airway: {
content: waypoints,
distance: this.distance,
dutyOrganizationId: "",
name: this.name,
speed: this.form[0].speed,
},
});
this.$nextTick(() => {
this.$emit('close');
})
},
/**
* 动作保存
*/
onActionsSave(actions) {
console.log("动作", actions);
this.curForm.actions = actions;
this.showActions = false;
},
/**
* 刷新所有点,线,标签
*/
refreshPointLineLabel() {
this.distance = 0;
this.dataSource.entities.removeAll();
this.form.forEach((item, i) => {
let position = item.position;
let uuid = item.uuid;
if (this.form.length > 1 && i > 0) {
let before = this.form[i - 1].position;
let after = position;
this.curForm.distance = this.createLabel(uuid, before, after);
}
this.createPoint(uuid, position);
});
// 航线
this.dataSource.entities.add({
polyline: {
material: Cesium.Color.RED,
width: 3,
positions: new Cesium.CallbackProperty(() => {
return this.positions.map((item) => {
let wgs84 = Utils.transformCartesian2WGS84(item);
return Utils.transformWGS842Cartesian({
lng: wgs84.lng,
lat: wgs84.lat,
alt: wgs84.alt + 10,
});
});
}, false),
},
});
},
/**
* 删除航点
*/
onDel() {
if (this.pageIndex > 0) {
this.positions.splice(this.pageIndex - 1, 1);
let delForm = this.form.splice(this.pageIndex - 1, 1)[0];
this.pageIndex = Math.max(
Math.min(this.pageCount, 1),
this.pageIndex - 1
);
let viewer = this.cesiumViewer;
this.refreshPointLineLabel();
}
},
/**
* 上一航点
*/
onPrev() {
if (this.pageIndex > 0) {
this.pageIndex = Math.max(1, this.pageIndex - 1);
let position = Utils.transformWGS842Cartesian({
lng: this.curForm.longitude,
lat: this.curForm.latitude,
alt: 10000,
});
this.cesiumViewer.camera.flyTo({
destination: position,
});
}
},
/**
* 下一航点
*/
onNext() {
if (this.pageIndex < this.pageCount) {
this.pageIndex = Math.min(this.pageCount, this.pageIndex + 1);
let position = Utils.transformWGS842Cartesian({
lng: this.curForm.longitude,
lat: this.curForm.latitude,
alt: 10000,
});
this.cesiumViewer.camera.flyTo({
destination: position,
});
}
},
/**
* 地图取点事件
*/
async onPickPoint(movement) {
let viewer = this.cesiumViewer;
let entities = this.dataSource.entities;
let windowPosition = movement.position;
let pickedObject = viewer.scene.pick(windowPosition);
if (pickedObject) {
console.log("pickedObject", JSON.stringify(pickedObject.id.id));
}
if (
Cesium.defined(pickedObject) &&
pickedObject.id?.name === "airline_point"
) {
} else {
// 航点添加
let position = Utils.getCartesian3FromPX(viewer, movement.position);
if (Cesium.defined(position)) {
let wgs84 = Utils.transformCartesian2WGS84(position);
let address = await Map.Regeo({
lon: wgs84.lng,
lat: wgs84.lat,
ver: 1,
});
// 是否取地址中
if (this.isPickAddress) {
if (address) {
this.isPickAddress = false;
this.curForm.address = address;
this.curForm.longitude = wgs84.lng;
this.curForm.latitude = wgs84.lat;
this.curForm.position = position;
this.positions[this.pageIndex - 1] = position;
this.refreshPointLineLabel();
}
} else {
let form = this.formAdd();
let uuid = form.uuid;
this.pageIndex++;
this.positions.push(position);
if (address) {
form.address = address;
}
form.longitude = wgs84.lng;
form.latitude = wgs84.lat;
form.position = position;
if (this.positions.length > 1) {
let before = this.positions[this.positions.length - 2];
let after = this.positions[this.positions.length - 1];
form.distance = this.createLabel(uuid, before, after);
}
this.createPoint(uuid, position);
}
}
}
},
/**
* 选取航线初始化
*/
async pickerPointInit() {
if (this.handler) {
this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOWN);
}
},
/**
* 创建航点
*/
createPoint(uuid, position) {
let wgs84 = Utils.transformCartesian2WGS84(position);
if (this.dataSource) {
let entities = this.dataSource.entities;
entities.add({
name: "airline_point",
id: uuid,
position: Utils.transformWGS842Cartesian({
lng: wgs84.lng,
lat: wgs84.lat,
alt: wgs84.alt + 10,
}),
point: {
pixelSize: 20,
color: Cesium.Color.RED,
fillColor: Cesium.Color.RED,
// heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND, // supermap版本会导致拖动显示错误
},
label: {
text: new Cesium.CallbackProperty(() => {
let index = this.form.findIndex((item) => item.uuid === uuid);
return String(index + 1);
}, false),
scale: 0.5,
font: "bold 24px Microsoft YaHei",
fillColor: Cesium.Color.WHITE,
horizontalOrigin: Cesium.VerticalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.CENTER,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
showBackground: false,
outlineWidth: 0,
},
});
}
},
/**
* 创建航点标签
*/
createLabel(uuid, before, after, alt) {
if (this.dataSource) {
let entities = this.dataSource.entities;
let before_wgs84 = Utils.transformCartesian2WGS84(before);
let after_wgs84 = Utils.transformCartesian2WGS84(after);
let center_lng = (before_wgs84.lng + after_wgs84.lng) / 2;
let cetner_lat = (before_wgs84.lat + after_wgs84.lat) / 2;
let alt = (after_wgs84.alt + before_wgs84.alt) / 2;
let position = Utils.transformWGS842Cartesian({
lng: center_lng,
lat: cetner_lat,
alt: alt + 10,
});
let distance = Number(
Cesium.Cartesian3.distance(before, after).toFixed(2)
);
let text = `${distance} m`;
entities.add({
id: `label_${uuid}`,
position: position,
label: {
text: text,
scale: 0.5,
font: "bold 30px Microsoft YaHei",
fillColor: Cesium.Color.fromCssColorString("#fff"),
horizontalOrigin: Cesium.VerticalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
showBackground: true,
backgroundPadding: new Cesium.Cartesian2(10, 10),
},
});
return distance;
}
},
/**
* 地理查询
*/
onAddressInput(address, cb) {
Map.Geo({
keyWord: address,
level: 12,
// specify: "156320900",
mapBound: "-180,-90,180,90",
queryType: 1,
start: 0,
count: 10,
})
.then((res) => {
cb(res.data.data.rows);
})
.catch((e) => cb([]));
},
// 表单位置信息索引
onAddressChange(item, type) {
if (item) {
let location = item.lonlat.split(",");
this.curForm.address = item.name;
this.curForm.longitude = Number(location[0]);
this.curForm.latitude = Number(location[1]);
let viewer = this.cesiumViewer;
let position = Utils.transformWGS842Cartesian({
lng: this.curForm.longitude,
lat: this.curForm.latitude,
alt: Number(this.curForm.altitude),
});
this.curForm.position = position;
this.positions[this.pageIndex - 1] = position;
this.refreshPointLineLabel();
let position1 = Utils.transformWGS842Cartesian({
lng: this.curForm.longitude,
lat: this.curForm.latitude,
alt: 10000,
});
viewer.camera.flyTo({
destination: position1,
});
}
},
// 点击表单旁图标定位
async onPickAddress(type) {
this.isPickAddress = true;
},
formAdd() {
let altitude = this.curForm.altitude;
let form = new Airway();
//继承上一个点的高度
form.altitude = altitude;
this.form.push(form);
return form;
},
},
};
</script>
<style lang="scss" scoped>
.airway-edit {
position: absolute;
top: -50px;
width: 450px;
.dialog-content {
display: flex;
flex-direction: column;
.ae-page {
flex-shrink: 0;
display: flex;
justify-content: space-around;
margin-bottom: 16px;
margin-top: 16px;
}
.ae-form {
flex: auto;
}
.ae-btns {
text-align: right;
}
}
}
</style>
\ No newline at end of file
<template>
<div class="cpt-command-airway-list" v-interact>
<div class="hd">
<div class="left ml8">
<!-- <img src="../../../../assets/images/mount_head.png" /> -->
<div class="title">航空航线</div>
</div>
<div @click="close" class="close">关闭</div>
</div>
<div class="list-box">
<div class="tb-box">
<div class="tb-hd-box">
<div class="tb-hd">航线ID</div>
<div class="tb-hd">航线名称</div>
<div class="tb-hd">所属单位</div>
<div class="tb-hd">空域状态</div>
<div class="tb-hd">安全状态</div>
<div class="tb-hd">航线标签</div>
<div class="tb-hd last-tb-hd">操作</div>
</div>
<div class="tb-bd-box">
<div class="tb-tr" v-for="item in airwayData.records" :key="item.id">
<div class="td">{{ item.id || "暂无" }}</div>
<div class="td">
<div>{{ item.name || "暂无" }}</div>
</div>
<div class="td">{{ item.organizationName || "暂无" }}</div>
<!-- 空域状态 -->
<div class="td">
<div class="status">{{item.status || "暂无"}}</div>
<!-- <div v-if="item.status == 1" class="status">可用</div>
<div v-else-if="item.status == 2" class="status" style="color: #2ca1e2">待申请</div>
<div v-else-if="item.status == 3" class="status" style="color: #ffbd36">待审批</div>
<div v-else-if="item.status == 4" class="status" style="color: #2bfdf1">通过</div>
<div v-else-if="item.status == 5" class="status" style="color: #fb4a2d">驳回</div>
<div v-else>暂无</div>-->
</div>
<!-- 安全状态 -->
<div
class="td"
:style="{ color: item.safe == 1 ? '#19D864' : '' }"
>{{ item.safe == 1 ? "安全" : "待确定" }}</div>
<!-- 航线标签 -->
<div class="td">
<!-- <span v-for="item2 in item.labelList"
:key="item2.labelId">{{ item2.labelName }}</span>-->
{{ item.labelName ||"暂无" }}
</div>
<div class="td last-td" style="width: 15%">
<div @click="changeLine(item)">选择航线</div>
</div>
</div>
</div>
</div>
</div>
<el-pagination
layout="prev, pager, next"
:total="airwayData.total"
:pageSize="airwayData.size"
:currentPage.sync="airwayData.current"
@current-change="getAirway"
></el-pagination>
</div>
</template>
<script>
import { mapState } from "vuex";
import { Control_API } from "../../../../../../../../../../api";
export default {
inject: ["rootNode"],
data() {
return {
keyword: null,
airwayData: {
current: 1,
records: [],
size: 10,
total: 0,
},
};
},
computed: {
...mapState("MMCFlightControlCenter", ["useSTLAirway"]),
},
mounted() {
this.getAirway();
},
methods: {
async changeLine(item) {
try {
if (item.safe != 1) {
await this.$confirm(
"此航线为非安全航线,开始任务前请确认航线安全!",
"安全确认",
{ customClass: "uav_controlPane", showClose: false }
);
}
this.$emit("change", item);
this.close();
} catch (e) {}
},
close() {
this.$emit("close");
},
async getAirway() {
if (this.useSTLAirway) {
let res = await Control_API.getUavRouteList({
pageNo: this.airwayData.current,
pageSize: this.airwayData.size,
});
if (res?.code === 0) {
let statusMap = {
1: "可用",
2: "待申请",
3: "待审批",
4: "通过",
5: "驳回",
};
this.airwayData = (res.data.list && {
current: this.airwayData.current,
records: res.data.list,
size: this.airwayData.size,
total: res.data.total,
}) || {
current: this.airwayData.current,
records: [],
size: this.airwayData.size,
total: this.airwayData.total,
};
this.airwayData.records = this.airwayData.records?.map((item) => {
let content = [];
try {
content = JSON.parse(item.content).content; // TODO 待确认字段
} catch (e) {
console.log(e);
}
return {
...item,
status: statusMap[item.status],
content,
};
});
}
} else {
this.rootNode.$emit("airwayPageChange", {
page: this.airwayData.current,
pageSize: this.airwayData.size,
callback: (data) => {
this.airwayData = data || {
current: 1,
records: [],
size: 10,
total: 0,
};
},
});
}
},
},
};
</script>
<style lang="scss" scoped>
.cpt-command-airway-list {
width: 600px;
position: absolute;
right: -213px;
top: 300px;
box-sizing: border-box;
background: #222222;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.1);
border-radius: 2px;
.hd {
flex-shrink: 0;
display: flex;
justify-content: space-between;
align-items: center;
height: 32px;
background: #3c3c3c;
border-radius: 2px 2px 0px 0px;
.left {
display: flex;
align-items: center;
.title {
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 14px;
color: #ffffff;
}
}
.close {
font-size: 14px;
font-family: MicrosoftYaHei;
color: #d2dfff;
margin-right: 8px;
cursor: pointer;
}
}
.list-box {
// width: 1132px;
height: calc(100% - 56px);
box-sizing: border-box;
.search-box {
margin-left: 0 !important;
// height: 80px;
height: auto;
margin: 24px 0 24px 0;
box-sizing: border-box;
display: flex;
align-items: center;
.item-plan {
width: 79px;
height: 32px;
background: rgba(28, 67, 191, 0.6) !important;
box-shadow: 0px 2px 4px 0px rgba(23, 33, 60, 0.5),
inset 0px 0px 16px 0px rgba(33, 137, 255, 0.4),
inset 0px 0px 4px 0px #00a7ff;
border: 1px solid;
border-image: linear-gradient(
180deg,
rgba(138, 218, 255, 1),
rgba(82, 179, 255, 0)
)
1 1;
border-radius: 0px;
font-size: 12px;
font-family: MicrosoftYaHei;
color: #43deff;
line-height: 16px;
}
.item-plan:hover {
opacity: 0.5;
}
.item-input {
width: 168px;
height: 40px;
margin-left: 10px;
// margin-right: 10px;
color: #08c2d1;
::v-deep .el-input__inner {
background: rgba(2, 31, 51, 0);
border: 0px solid #06b6e0;
border-radius: 4px;
font-family: MicrosoftYaHeiUI;
font-size: 16px;
color: #08c2d1;
font-weight: 400;
padding-left: 0;
&::placeholder {
font-size: 14px;
font-family: MicrosoftYaHei;
color: #397c8b;
line-height: 19px;
}
}
}
.andLinlineBtn {
position: absolute;
top: 65px;
right: 160px;
width: 130px;
cursor: pointer;
height: 32px;
opacity: 0.8;
font-family: PangMenZhengDao;
font-size: 22px;
color: #00ffff;
text-align: center;
font-weight: 400;
line-height: 40px;
}
.routeLabelBtn {
position: absolute;
top: 65px;
right: 20px;
width: 130px;
cursor: pointer;
height: 40px;
opacity: 0.8;
font-family: PangMenZhengDao;
font-size: 22px;
color: #00ffff;
text-align: center;
font-weight: 400;
line-height: 40px;
}
.routeLabelBtnDefault {
background: rgba(0, 3, 36, 0.8);
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.5),
inset 0 0 15px 0 rgba(0, 182, 255, 0.9);
border-radius: 6px;
}
.routeLabelBtnActive {
background: rgba(0, 34, 140, 0.2);
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.5), inset 0 0 10px 0 #00b6ff;
border-radius: 6px;
}
}
.tb-box {
// overflow-x: scroll;
// overflow-y: hidden;
padding: 0 !important;
.tb-hd-box {
display: flex;
background: #303133;
// width: 1415.62px;
border-top: 1px solid rgba(207, 234, 255, 0.33);
border-bottom: 1px solid rgba(207, 234, 255, 0.33);
height: 37.6px;
.tb-hd {
flex: 1;
line-height: 37.6px;
width: calc(100% / 8);
white-space: nowrap;
height: 100%;
text-align: center;
font-family: MicrosoftYaHei-Bold;
font-size: 16px;
color: #b5e5ff;
letter-spacing: 0;
font-weight: 700;
}
}
.tb-bd-box {
width: 100%;
max-height: 280px;
overflow: hidden;
overflow-y: auto;
.tb-tr {
display: flex;
color: #fff;
align-items: center;
font-size: 14px;
width: 100%;
margin: 5px 0 0 0;
// border: 1px solid;
background: #081a3a;
cursor: pointer;
// background-image: url("~@/assets/newImage/tiaokaung.png") !important;
background-size: 100% 100%;
// &:hover {
// // background: rgba(2, 19, 96, 0.2);
// box-shadow: inset 0px 0px 10px 2px #3fcbff;
// // border: 1px solid #70f6f9;
// }
&:nth-of-type(2n - 1) {
background: #3c3c3c;
}
.td {
// width: calc(100% / 7);
flex: 1;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
}
.last-td {
width: 80px;
height: 32px;
text-align: center;
line-height: 32px;
margin: 0 auto;
color: #fff;
background: #3388ff;
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 400;
font-size: 14px;
}
}
}
}
}
}
.toubu {
display: flex;
.tiao {
width: 3px !important;
height: 15px;
background: #ffbd36;
margin: 6px 5px 0 16px;
}
.hd-box {
font-size: 18px;
font-family: MicrosoftYaHei-Bold, MicrosoftYaHei;
font-weight: bold;
color: #ffffff;
line-height: 24px;
text-shadow: 0px 2px 4px #136791;
}
}
.cpt-command-airway-list .tb-box {
height: 100%;
}
.flex {
display: flex;
}
.uavImg {
width: 20px;
height: 20px;
margin: 0 6px 0 6px;
img {
width: 100%;
height: 100%;
}
}
.fangkuai {
border: 1px solid #43deff;
height: 32px;
background: rgba(13, 50, 92, 0.7);
}
.duanxian {
width: 1px;
height: 22px;
border-left: 1px solid;
border-image: linear-gradient(
180deg,
rgba(67, 222, 255, 0),
rgba(67, 222, 255, 1),
rgba(67, 222, 255, 0)
)
1 1;
}
.el-input__suffix-inner::v-deep {
i {
color: #43deff;
}
}
// 滚动动画
.animate {
padding-left: 20px;
// font-size: 12px;
// color: #000;
display: inline-block;
white-space: nowrap;
animation: 5s wordsLoop linear infinite normal;
}
@keyframes wordsLoop {
0% {
transform: translateX(100%);
-webkit-transform: translateX(100%);
}
100% {
transform: translateX(-100%);
// -webkit-transform: translateX(-100%);
}
}
// @-webkit-keyframes wordsLoop {
// 0% {
// transform: translateX(100%);
// -webkit-transform: translateX(100%);
// }
// 100% {
// transform: translateX(-100%);
// -webkit-transform: translateX(-100%);
// }
// }
// 提示框样式
.td::v-deep .el-tooltip {
background: rgba(2, 19, 96, 0);
border: 0px solid rgba(207, 234, 255, 0.33);
font-family: MicrosoftYaHei;
color: #ffffff;
line-height: 19px;
}
// 空域状态
.status {
font-size: 14px;
font-family: MicrosoftYaHei;
color: #00d45c;
line-height: 19px;
}
//操作状态
.el-tooltip {
opacity: 0.7;
}
.el-tooltip:hover {
opacity: 1;
}
// 页签样式
.zongji {
font-size: 12px;
font-family: MicrosoftYaHei;
color: #b5e5ff;
line-height: 16px;
.tiaoshu {
color: #43deff;
}
}
.douhao {
margin: 0 5px 0 5px;
}
.dangqianye {
margin: 0 20px 0 0;
}
.el-pager::v-deep .number:hover {
background: #00b6ff !important;
border-radius: 2px;
color: #000 !important;
width: 14px;
height: 22px;
line-height: 22px;
}
.el-pagination--small::v-deep .el-pager .number {
font-size: 12px;
font-family: MicrosoftYaHei;
color: #889fb2;
}
.active::v-deep {
color: #000 !important;
}
.btnqueding {
margin: 0 0 0 8px;
width: 79px;
height: 32px;
background: rgba(28, 67, 191, 0.6) !important;
box-shadow: 0px 2px 4px 0px rgba(23, 33, 60, 0.5),
inset 0px 0px 16px 0px rgba(33, 137, 255, 0.4),
inset 0px 0px 4px 0px #00a7ff;
border-radius: 0px;
border: 1px solid;
border-image: linear-gradient(
180deg,
rgba(138, 218, 255, 1),
rgba(82, 179, 255, 0)
)
1 1;
line-height: 1px;
color: #43deff;
}
.btnqueding:hover {
opacity: 0.5 !important;
}
.tb-pagination {
margin: 0 0 19px 0;
}
.zhuan {
font-size: 12px;
font-family: MicrosoftYaHei;
color: #889fb2;
line-height: 16px;
input {
width: 48px;
min-width: 48px;
max-width: auto;
text-align: center;
height: 28px;
background: rgba(12, 13, 20, 0.5);
border-radius: 2px;
border: 1px solid rgba(36, 146, 252, 0.3);
margin: 0 5px 0 5px;
color: #fff;
outline: 0px solid;
}
}
// 说明
.shuo {
margin: 0 0 29px 0;
}
.shuoming {
font-size: 12px;
font-family: MicrosoftYaHei;
color: #43deff;
line-height: 16px;
}
.maohao {
font-size: 12px;
font-family: MicrosoftYaHei;
color: #43deff;
line-height: 16px;
margin: 0 14px 0 5px;
}
.icons {
font-size: 12px;
font-family: MicrosoftYaHei;
color: #b3bbc5;
line-height: 16px;
margin: 0 12px 0 0;
}
.cpt-command-flight-task-explain {
display: flex;
align-items: center;
margin-left: 4px;
// margin-top: 8px;
padding: 0;
margin: 28px 0 29px 0;
.explain_title {
font-family: MicrosoftYaHei;
font-size: 14px;
color: #08c2d1;
}
.explain_box {
display: flex;
}
.explain_box_detail {
margin-right: 15px;
font-size: 12px;
font-family: MicrosoftYaHei;
color: #b3bbc5;
}
}
.waixian {
// border-radius: 6px;
// border: 1px solid #8adaff;
}
.el-tooltip__popper::v-deep .is-dark {
z-index: -100;
}
.el-pagination::v-deep {
text-align: center;
.btn-prev,
.btn-next {
color: #fff;
background-color: transparent;
}
.el-pager {
color: #fff;
li {
background-color: transparent;
}
}
}
</style>
\ No newline at end of file
<template>
<div class="taskListBox">
<div class="header">
<div class="title">
<img src="../../assets/images/mount_head.png" />
<div class="font">航线库</div>
</div>
</div>
<el-form class="task-main" label-width="70px">
<el-form-item label="航线选择">
<el-select v-model="selectedAirwayId">
<el-option v-for="(item , i) in airwayList" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<div class="btn" @click="onStartTask" v-hover>一键任务</div>
<div class>
<span class="btn__add-line" @click="showAirwayEdit = true">
<span class="f8"></span> 创建航线
</span>
</div>
</el-form>
<AirwayEdit v-if="showAirwayEdit" @close="showAirwayEdit = false"></AirwayEdit>
</div>
</template>
<script>
import { Utils } from "../../../../../../../../lib/cesium";
import { mapState, mapActions } from "vuex";
import { Control_API } from "../../../../../../../../api";
import AirwayEdit from "./components/airwayEdit";
const airwayEntities = []; // 航线实体
let point_entity = null;
export default {
name: "taskList",
components: { AirwayEdit },
props: {},
inject: ["rootNode", "bus"],
data() {
return {
//航线列表
airwayList: [],
// 选择的航线
selectedAirwayId: "",
// 创建航线窗口
showAirwayEdit: false,
};
},
computed: {
...mapState("MMCFlightControlCenter", ["cesiumViewer", "useSTLAirway"]),
...mapState("MMCFlightControlCenter/uav", ["uav"]),
// 选择的航线
selectedAirway() {
let find = this.airwayList.find((item) => {
return item.id == this.selectedAirwayId;
});
if (find) {
return find;
} else {
return {
name: "",
id: -1,
};
}
},
},
watch: {
selectedAirway() {
this.clearAirwayEntities();
if (this.selectedAirway.id !== -1) {
try {
let airway = this.selectedAirway.content;
this.createAirwayEntities({
polyline: airway,
id: airway.id,
});
} catch (e) {
console.log("绘制航线失败", e);
}
}
},
},
async created() {
this.bus.$on("startTask", this.onStartTask);
let res = await Control_API.getUavRouteList({
pageNo: 1,
pageSize: 100,
});
if (res?.code === 0) {
let airwayList = [];
for (let i = 0; i < res.data.list.length; i++) {
let item = res.data.list[i];
let flightCourseJson;
try {
flightCourseJson = JSON.parse(item.flightCourseJson);
} catch (e) {
console.log(e);
}
// 转换成飞控中心能接受的数据协议
let content = flightCourseJson
? await this.$store.dispatch(
"MMCFlightControlCenter/apiPointsToFKZXPoints",
{
list: flightCourseJson?.linePointSaveReqVOS || [],
actionListKey: "pointActionSaveReqVOS",
}
)
: null;
airwayList.push({
name: item.flightName,
id: item.id,
content: content,
});
}
this.airwayList = airwayList;
}
},
beforeDestroy() {
this.clearAirwayEntities();
},
methods: {
...mapActions("MMCFlightControlCenter", [
"createAirwayEntities",
"clearAirwayEntities",
"apiPointsToFKZXPoints",
]),
...mapActions("MMCFlightControlCenter/uav", ["isTakeOver"]),
/**
* 一键任务事件
*/
async onStartTask() {
// 判断是否已接管
if (!(await this.isTakeOver())) {
this.$message.warning("请先接管无人机");
return;
}
// 判断是否选择了航线
if (this.selectedAirway.id === -1) {
this.$message.warning("请选择航线");
return;
}
try {
await this.$confirm("请确认是否进行一键任务操作?", "安全确认", {
cancelButtonText: "取消",
confirmButtonText: "确定",
customClass: "uav_controlPane",
showClose: false,
});
this.$store.commit("MMCFlightControlCenter/uav/setState", {
key: "airlineData",
value: this.selectedAirway,
});
this.$store.dispatch("MMCFlightControlCenter/uav/takeOff", {
callback: (status) => {
if (status) {
this.$message.success("一键任务指令发送成功");
} else {
this.$message.error("一键任务指令发送失败");
}
},
});
this.rootNode.$emit("uavTaskStart", {
uav: this.uav,
selectedTask: this.selectedTask,
selectedAirway: this.selectedAirway,
});
} catch (e) {}
},
},
};
</script>
<style lang="scss" scoped>
.taskListBox {
height: 200px;
width: 416px;
background: #222222;
border-radius: 12px;
transition: 0.3s;
display: flex;
flex-direction: column;
.header {
flex-shrink: 0;
display: flex;
justify-content: space-between;
height: 32px;
background: #3c3c3c;
border-radius: 12px 12px 0px 0px;
.title {
display: flex;
align-items: center;
margin-left: 10px;
.font {
font-size: 20px;
font-family: YouSheBiaoTiHei;
color: #14faff;
line-height: 26px;
text-shadow: 0px 1px 1px rgba(2, 32, 56, 0.2);
background-image: -webkit-linear-gradient(
right,
#e3aa77,
#f5cda9,
#f9ecd3,
#fcdbb1,
#edb07a
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
}
.active {
background-image: linear-gradient(
180deg,
#9198ff 0%,
rgba(45, 81, 153, 0.22) 40%,
#05091a 100%
);
border: 1px solid #70daf9;
box-shadow: inset 0 0 10px 2px #3f9dff;
font-family: MicrosoftYaHei-Bold;
font-size: 14px;
color: #70daf9;
letter-spacing: 0;
font-weight: 700;
}
.default {
background-image: linear-gradient(
180deg,
#9198ff 0%,
rgba(45, 81, 153, 0.22) 40%,
#05091a 100%
);
border: 1px solid #70daf9;
font-family: MicrosoftYaHei-Bold;
font-size: 14px;
color: rgba(112, 218, 249, 0.5);
letter-spacing: 0;
font-weight: 700;
}
}
.task-main::v-deep {
flex: auto;
padding: 38px 16px 16px;
position: relative;
display: flex;
flex-direction: column;
.el-form-item__label {
color: #fff;
}
.btn__add-line {
position: absolute;
bottom: 20px;
right: 20px;
font-size: 10px;
color: #43deff;
cursor: pointer;
}
}
.btn {
width: 122px;
height: 32px;
text-align: center;
line-height: 32px;
margin: 0 auto;
color: #fff;
background: #3388ff;
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 400;
font-size: 14px;
cursor: pointer;
}
}
.select-airway__btn {
width: 100%;
height: 30px;
padding: 0;
}
</style>
......@@ -4,7 +4,8 @@
<img class="left-bar-item__icon" src="./assets/images/task.svg" />
<div class="left-bar-item__text">任务</div>
</div>
<TaskList class="task-list" v-if="openTask"></TaskList>
<TaskList class="task-list" v-if="!useAirway && openTask"></TaskList>
<AirwayList class="task-list" v-if="useAirway && openTask"></AirwayList>
<div class="left-bar-item item" @click="onClickAI">
<img class="left-bar-item__icon" src="./assets/images/ai.svg" />
<div class="left-bar-item__text">智能识别</div>
......@@ -46,6 +47,7 @@ import Car from "./components/car";
import Face from "./components/face";
import Jm from "./components/Jm";
import TaskList from "./components/taskList";
import AirwayList from "./components/airwayList";
import Traffic from "./components/traffic";
export default {
......@@ -54,6 +56,7 @@ export default {
Car,
Jm,
TaskList,
AirwayList,
Traffic,
Face,
},
......@@ -68,6 +71,7 @@ export default {
};
},
computed: {
...mapState("MMCFlightControlCenter", ["useAirway"]),
...mapState("MMCFlightControlCenter/uav", ["uav"]),
// 收起列表按钮
listCollapse: {
......
......@@ -125,6 +125,11 @@ export default {
type: Boolean,
default: false,
},
// 使用航线而不使用任务起飞
useAirway: {
type: Boolean,
default: true
}
},
data() {
return {
......@@ -175,6 +180,15 @@ export default {
});
},
},
useAirway: {
immediate: true,
handler(newVal) {
this.$store.commit("MMCFlightControlCenter/setState", {
key: "useAirway",
value: newVal,
});
},
},
},
beforeCreate() {
Vue.component("SymbolIcon", SymbolIcon);
......
......@@ -34,6 +34,7 @@ export default {
airwayEntities: [], //航线实体集合, 元素为new Cesium.EntityCollection()创建
useSTLAirway: true, //是否使用标准航线库
useTimedTask: false, //是否使用定时任务
useAirway: true, //使用航线而不使用任务起飞
},
mutations: {
/**
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论