提交 3d868b90 作者: 温凯

Merge branch 'v4_master' into v4

......@@ -66,7 +66,12 @@ Vue.use(MMCSTL);
* 修正机库指令
* 增加是否定支持定时任务的判断
* 修正多个缺陷
## v1.6.0
* MQTT新增账号密码
## v1.6.1
* fix: 文件名大小写错误
## v2.0.0
* 合并V4分支更新到v2.0.0版本
## 项目结构
```
......
{
"name": "mmc-stl-vue2",
"version": "1.5.0",
"version": "2.0.0",
"description": "科比特前端标准化组件",
"main": "index.js",
"scripts": {
......
......@@ -39,4 +39,17 @@ export default class TaskInfo {
data
});
}
}
/**
* 全部单位
* @param {*} params
* @returns
*/
static listAll(params) {
return request({
url: '/admin-api/system/dept/simple-list',
method: 'get',
params,
});
}
}
\ No newline at end of file
......@@ -49,7 +49,7 @@ class Control_API {
// 申请接管无人机
static applyControlUav(params) {
return request({
url: `/admin-api/uas/device-take/controlUav`,
url: `/admin-api/uas/device-take/updateCurrentOperator`,
method: 'post',
params
});
......
......@@ -15,28 +15,30 @@
<el-form-item label="航线名称" required>
<el-input clearable v-model="name"></el-input>
</el-form-item>
<el-form-item label="所属单位:" required>
<el-cascader ref="CascaderRef" v-model="departmentId" :options="orgList" clearable :show-all-levels="false"
placeholder="请选择所属单位" :props="{
children: 'children',
label: 'name',
value: 'id',
checkStrictly: true,
emitPath: false,
}" @change="changeNode"></el-cascader>
</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
>
<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
item.name
}}
</span>
<span style="font-size: 12px; color: #999; margin-left: 12px">{{ item.address }}</span>
......@@ -58,27 +60,23 @@
<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-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}}</el-form-item>
<el-form-item label="航线总里程">{{ distance }}m</el-form-item>
<el-form-item label="预计飞行时间">{{ time }}</el-form-item>
</el-form>
<div class="ae-btns">
<el-button type="primary" @click="onSave" :loading="saveLoading">保存</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>
<Actions v-if="showActions" @close="showActions = false" @save="onActionsSave" :selectActions="curForm.actions">
</Actions>
</div>
</template>
<script>
import { Map, AirLine } from "../../../../../../../../../../../../api";
import { Map, AirLine, TaskInfo } from "../../../../../../../../../../../../api";
import { mapState } from "vuex";
import Utils from "../../../../../../../../../../../../components/cesiumLayer/lib/cesium/utils";
import { nanoid } from "nanoid";
......@@ -115,7 +113,9 @@ export default {
inject: ["rootNode"],
data() {
return {
orgList: [],
name: "", //航线名
departmentId: "", //部门id
form: [], //表单集合
pageIndex: 0, //当前页码
locationIcon: null, //定位图标
......@@ -158,6 +158,7 @@ export default {
},
mounted() {
let viewer = this.cesiumViewer;
this.getOrgList()
this.dataSource = new Cesium.CustomDataSource("airway_edit");
viewer.dataSources.add(this.dataSource);
this.handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas); //获取事件对象
......@@ -184,6 +185,7 @@ export default {
Cesium.ScreenSpaceEventType.LEFT_CLICK
);
this.pickerPointInit();
},
beforeDestroy() {
this.handler.removeInputAction(
......@@ -201,12 +203,87 @@ export default {
this.dataSource.show = false;
},
methods: {
async getOrgList() {
let data = await TaskInfo.listAll();
let res = data.data;
const config = {
id: "id",
parentId: "parentId",
childrenList: "children",
};
const childrenListMap = {};
const nodeIds = {};
const tree = [];
for (const d of res) {
const parentId = d[config.parentId];
if (childrenListMap[parentId] == null) {
childrenListMap[parentId] = [];
}
nodeIds[d[config.id]] = d;
childrenListMap[parentId].push(d);
}
for (const d of res) {
const parentId = d[config.parentId];
if (nodeIds[parentId] == null) {
tree.push(d);
}
}
for (const t of tree) {
adaptToChildrenList(t);
}
function adaptToChildrenList(o) {
if (childrenListMap[o[config.id]] !== null) {
o[config.childrenList] = childrenListMap[o[config.id]];
}
if (o[config.childrenList]) {
for (const c of o[config.childrenList]) {
adaptToChildrenList(c);
}
}
}
this.orgList = tree;
},
convertToSeconds(timeString) {
const regex = /(\d+)\s*(小时|分钟|秒)/g;
let totalSeconds = 0;
let match;
while ((match = regex.exec(timeString)) !== null) {
const value = parseInt(match[1], 10);
const unit = match[2];
switch (unit) {
case '小时':
totalSeconds += value * 3600; // 1小时 = 3600秒
break;
case '分钟':
totalSeconds += value * 60; // 1分钟 = 60秒
break;
case '秒':
totalSeconds += value; // 直接加秒
break;
}
}
return totalSeconds;
},
changeNode() {
this.$refs.CascaderRef.dropDownVisible = false;
},
// 保存航线
async onSave() {
if (!this.name) {
this.$message.warning("请输入航线名称");
return;
}
if (!this.departmentId) {
this.$message.warning("请选择所属机构");
return;
}
if (this.form.length === 0) {
this.$message.warning("请点击地图选择航点");
return;
......@@ -248,8 +325,10 @@ export default {
content: waypoints,
distance: this.distance,
dutyOrganizationId: "",
departmentId: this.departmentId,
name: this.name,
speed: this.form[0].speed,
flyTime: this.convertToSeconds(this.time)
};
try {
let res = await AirLine.add({
......@@ -257,6 +336,8 @@ export default {
pointCount: airway.content.length,
distance: airway.distance,
sourceType: 2,
departmentId: airway.departmentId,
flyTime:airway.flyTime,
linePointSaveReqVOS: airway.content.map((point) => ({
latitude: point.coordinate.latitude,
longitude: point.coordinate.longitude,
......@@ -279,7 +360,7 @@ export default {
this.$message.success("创建航线成功");
this.$emit("addDone", res.data);
this.$emit("close");
} catch (e) {}
} catch (e) { }
this.saveLoading = false;
// 通过事件创建会出现重复创建的情况, 改为标准化里创建
......@@ -302,7 +383,6 @@ export default {
* 动作保存
*/
onActionsSave(actions) {
console.log("动作", actions);
this.curForm.actions = actions;
this.showActions = false;
},
......@@ -606,8 +686,8 @@ export default {
<style lang="scss" scoped>
.airway-edit {
position: absolute;
top: -50px;
width: 450px;
top: 0px;
width: 351px;
z-index: 2;
.dialog-content {
......
......@@ -140,6 +140,7 @@ export default {
}
},
beforeDestroy() {
this.showAirwayEdit = false;
this.bus.$off("startTask", this.onStartTask);
this.clearAirwayEntities();
},
......
<template>
<div class="task-add dialog1027" v-interact>
<div class="dialog-header">
<div class="dialog-header__title">周期任务</div>
<div class="dialog-header__close" @click="$emit('close')">关闭</div>
</div>
<div class="dialog-content">
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-form-item label="执行日期" prop="date">
<el-date-picker
v-model="form.date"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
size="mini"
popper-class="mmc"
value-format="yyyy-MM-dd"
:picker-options="pickerOptions"
></el-date-picker>
</el-form-item>
<el-form-item label="执行时间" prop="time">
<el-time-picker
is-range
v-model="form.time"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
placeholder="选择时间范围"
size="mini"
popper-class="mmc"
value-format="HH:mm:ss"
></el-time-picker>
</el-form-item>
<el-form-item label="航线名称" prop="airwayId">
<el-select v-model="form.airwayId" size="mini" popper-class="mmc" style="width:100%">
<el-option :label="item.name" :value="item.id" v-for="(item, index) in airwayList"></el-option>
</el-select>
</el-form-item>
</el-form>
</div>
<div class="task-add-btn">
<el-button type="primary" size="medium" :loading="confirmLoading" @click="onConfirm">确认</el-button>
</div>
</div>
</template>
<script>
import { mapState, mapActions } from "vuex";
import { AirLine, Control_API } from "../../../../../../../../../../../../api";
export default {
name: "TaskAdd",
inject: ["rootNode"],
data() {
return {
form: {
date: [],
time: ["00:00:00", "23:59:59"],
airwayId: "",
},
rules: {
date: [{ required: true, message: "请选择日期", trigger: "blur" }],
time: [{ required: true, message: "请选择时间", trigger: "blur" }],
airwayId: [{ required: true, message: "请选择航线", trigger: "blur" }],
},
pickerOptions: {
disabledDate: (time) => {
// 日期选择限制
return time.getTime() < Date.now() - 8.64e7;
},
},
airwayList: [], //航线列表
confirmLoading: false,
};
},
computed: {
...mapState("MMCFlightControlCenter/hangar", ["hangar"]),
...mapState("MMCFlightControlCenter", ["airwayEntities", "useSTLAirway"]),
/**
* 选择的日常任务数据
*/
selectedAirway() {
let find = this.airwayList.find((item) => {
return item.id === this.form.airwayId;
});
return find;
},
},
watch: {
selectedAirway: {
async handler(newVal, oldVal) {
//清除旧的航线并渲染新航线
let find = this.airwayEntities.find(
(item1) => oldVal?.id === item1.airwayId
);
if (find) {
this.clearAirwayEntities({ id: this.selectedAirway.id });
}
//渲染新航线
if (newVal) {
this.createAirwayEntities({
polyline: this.selectedAirway.content,
id: this.selectedAirway.id,
});
}
},
},
},
async mounted() {
if (this.useSTLAirway) {
let res = await Control_API.getUavRouteList({
pageNo: 1,
pageSize: 100,
nestId: this.hangar.id,
});
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;
}
} else {
this.rootNode.$emit("airwayListGet", {
pageNo: 1,
pageSize: 100,
hangar: this.hangar,
callback: (data) => {
this.airwayList = data?.records || [];
},
});
}
},
methods: {
...mapActions("MMCFlightControlCenter", [
"createAirwayEntities",
"clearAirwayEntities",
]),
/**
* 确认事件
*/
onConfirm() {
this.$refs["form"].validate((valid) => {
if (valid) {
this.confirmLoading = true;
this.rootNode.$emit("taskBatchAdd", {
type: 3, //1: 日常任务 2.定时任务 3.周期任务
taskList: [{ ...this.form, airway: this.selectedAirway }], //任务数据
hangar: this.hangar,
callback: ({ code }) => {
this.confirmLoading = false;
if (code === 0) {
this.$emit("addDone");
this.$emit("close");
}
},
}); // 根节点发送机库任务新增事件
} else {
return false;
}
});
},
},
};
</script>
<style lang="scss" scoped>
.task-add {
height: 286px;
background: rgba(9, 32, 87, 0.7);
// border: 1px solid #70daf9;
position: absolute;
top: 50px;
left: 550px;
width: 512x;
z-index: 1;
display: flex;
flex-direction: column;
gap: 5px;
padding-bottom: 20px;
&.more {
height: 376px;
}
.dialog-header {
padding-left: 16px !important;
}
.task-add-main {
padding: 20px 20px 0 10px;
}
.task-add-btn {
flex-shrink: 0;
text-align: center;
.task-add__btn {
margin: auto;
width: 92px;
height: 36px;
background: #3388ff;
border-radius: 2px;
cursor: pointer;
text-align: center;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
}
}
.task-add-more {
background-color: rgba(13, 82, 143, 0.6);
height: 24px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
}
}
</style>
\ No newline at end of file
<template>
<div class="timed-task">
<div class="timed-task-header">
<div class="header__column flex2">名称</div>
<div class="header__column flex2">时间</div>
<div class="header__column status">状态</div>
<div class="header__column flex2">操作</div>
</div>
<div class="timed-task-main">
<div class="row" v-for="item in taskListAll" :key="item.id">
<div class="row__column flex2">
<el-tooltip class="item" effect="dark" :content="item.name" placement="top-start">
<span>{{item.name}}</span>
</el-tooltip>
</div>
<div class="row__column flex2">
<el-tooltip
class="item"
effect="dark"
:content="`${item.taskStartTime}至${item.taskEndTime}`"
placement="top-start"
>
<span>{{`${item.taskStartTime}至${item.taskEndTime}`}}</span>
</el-tooltip>
</div>
<div class="row__column status" style="color: rgb(255, 189, 54);">{{item.status}}</div>
<div class="row__column flex2 ctrl">
<el-tooltip content="查看" placement="top">
<span
class="icon-chakan1 iconfont icon"
style="color: #ffffff; font-size: 10px;"
@click="onSwitchAirway(item)"
></span>
</el-tooltip>
<!-- <el-tooltip content="历史" placement="top">
<img class="icon" style="width: 15px;" src="../../assets/images/history.png" />
</el-tooltip>-->
<!-- <el-tooltip content="禁用" v-if="!item.enable" placement="top">
<img class="icon" style="width: 18px;" src="../../assets/images/enable.png" />
</el-tooltip>
<el-tooltip content="启用" v-if="item.enable" placement="top">
<img class="icon" style="width: 15px;" src="../../assets/images/able.png" />
</el-tooltip>-->
<el-tooltip content="删除" placement="top">
<span class="icon-shanchu iconfont icon" @click="onDelAirway(item)"></span>
</el-tooltip>
</div>
</div>
</div>
<div class="task-add-btn">
<div class="task-add__btn" @click="showTaskAdd = true">创建周期任务</div>
</div>
<TaskAdd v-if="showTaskAdd" @close="onTaskAddClose" @addDone="onAddDone"></TaskAdd>
</div>
</template>
<script>
import { mapState, mapActions } from "vuex";
import { AirLine } from "../../../../../../../../../../api";
import TaskAdd from "./components/taskAdd";
export default {
name: "hangarPeriodTask",
components: {
TaskAdd,
},
inject: ["rootNode", "bus"],
data() {
return {
showTaskAdd: false,
taskListAll: [],
timeHandle: null,
};
},
computed: {
...mapState("MMCFlightControlCenter", ["airwayEntities"]),
...mapState("MMCFlightControlCenter/hangar", [
"hangarRealTimeData",
"hangar",
]),
},
watch: {
},
created() {
this.rootNode.$emit("hangarTaskTabChange", {
type: 3,
});
this.getTaskList();
this.timeHandle = setInterval(() => {
this.getTaskList();
}, 5000);
},
beforeDestroy() {
this.clearAirwayEntities();
clearInterval(this.timeHandle);
},
methods: {
...mapActions("MMCFlightControlCenter", [
"createAirwayEntities",
"clearAirwayEntities",
]),
/**
* 更新任务列表
*/
getTaskList() {
console.log("getTaskList", this.taskListAll);
this.rootNode.$emit("taskListGet", {
pageNo: 1,
taskStatus: 11,//筛选掉审核未通过的
pageSize: 100,
type: 3, // 1: 日常任务 2: 定时任务 3:周期任务
hangar: this.hangar,
callback: (res) => {
this.taskListAll = res.records || [];
},
});
},
/**
* 显示或隐藏航线
*/
async onSwitchAirway(item) {
this.rootNode.$emit("airwayGet", {
airwayId: item.airwayId,
callback: (airway) => {
let find = this.airwayEntities.find(
(item1) => item1.airwayId === airway.id
);
if (find) {
this.clearAirwayEntities({ id: airway.id });
} else {
this.createAirwayEntities({
polyline: airway.content,
id: airway.id,
});
}
},
});
},
async onDelAirway(item) {
try {
if (item.loading) {
return;
}
await this.$confirm("请确认是否删除该任务?", "安全确认", {
cancelButtonText: "取消",
confirmButtonText: "确定",
customClass: "uav_controlPane",
showClose: false,
});
item.loading = true;
this.rootNode.$emit("taskDel", {
task: item,
type: 3, // 1: 日常任务 2: 定时任务 3:周期任务
callback: () => {
this.getTaskList();
},
});
} catch (e) {
console.log(e);
}
},
onTaskAddClose() {
this.showTaskAdd = false;
},
onAddDone() {
this.getTaskList();
},
},
};
</script>
<style lang="scss" scoped>
.timed-task {
height: 100%;
display: flex;
flex-direction: column;
padding: 5px 10px 5px;
gap: 8px;
box-sizing: border-box;
.timed-task-header {
display: flex;
flex-shrink: 0;
font-family: MicrosoftYaHei-Bold;
font-size: 14px;
color: #b5e5ff;
padding: 5px 0;
letter-spacing: 0;
font-weight: 700;
background: rgba(87, 96, 138, 0.2);
border: 1px solid rgba(207, 234, 255, 0.33);
gap: 3px;
.header__column {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
&.flex2 {
flex: 2;
}
&.status {
flex: initial;
width: 70px;
}
}
}
.timed-task-main {
color: #fff;
overflow-y: auto;
flex: 1;
.row {
display: flex;
color: #fff;
background: url("../../assets/images/listBg.png");
background-repeat: no-repeat;
background-size: 100% 100%;
height: 33px;
line-height: 33px;
padding-left: 4px;
margin-bottom: 16px;
gap: 3px;
.row__column {
flex: 1;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&.flex2 {
flex: 2;
}
&.ctrl {
display: flex;
justify-content: center;
align-items: center;
gap: 5px;
.icon {
cursor: pointer;
&.loading {
opacity: 0.5;
}
}
}
&.status {
flex: initial;
width: 70px;
}
}
}
}
.task-add-btn {
text-align: center;
.task-add__btn {
width: 124px;
height: 36px;
background: #3388ff;
border-radius: 2px;
text-align: center;
line-height: 32px;
margin: 0 auto;
margin-bottom: 10px;
cursor: pointer;
color: #fff;
}
}
}
</style>
\ No newline at end of file
<template>
<div class="task-add dialog1027" :class="{more: showMore}" v-interact>
<div class="task-add-header dialog-header">
<img class="dialog-header__icon" src="../../../../../../assets/images/mount_head.png" />
<div class="header__title dialog-header__title">定时任务</div>
<div class="header-right dialog-header__close">
<div class="header-right__add" @click="onTaskAdd">
<span class="iconfont icon-zengjia"></span>添加任务
</div>
<div class="header-right__close" @click="$emit('close')">关闭</div>
</div>
</div>
<div class="task-add-main dialog-content">
<div class="main-item" v-for="(item, index) in list" :key="index">
<el-date-picker
v-model="item.time"
size="mini"
popper-class="mmc"
type="datetime"
placeholder="选择时间"
value-format="yyyy-MM-dd HH:mm:ss"
:picker-options="pickerOptions"
></el-date-picker>
<el-select v-model="item.airwayId" size="mini" popper-class="mmc" placeholder="请选择航线">
<el-option :label="item1.name" :value="item1.id" v-for="(item1, index) in airwayList"></el-option>
</el-select>
<el-tooltip content="删除" placement="top">
<span class="icon-shanchu iconfont" @click="onDelTask(index)"></span>
</el-tooltip>
<el-tooltip content="查看" placement="top">
<span
class="icon-chakan1 iconfont"
style="font-size: 10px;"
@click="onSwitchAirway(item)"
></span>
</el-tooltip>
</div>
</div>
<div class="task-add-btn">
<el-button type="primary" size="medium" :loading="confirmLoading" @click="onConfirm">确认</el-button>
</div>
<div class="task-add-more" @click="showMore = !showMore">
<img src="../../../../assets/images/xb.png" width="15" />
</div>
</div>
</template>
<script>
import { mapState, mapActions } from "vuex";
import { AirLine } from "../../../../../../../../../../../../api";
export default {
name: "TaskAdd",
inject: ["rootNode"],
data() {
return {
list: [
/* {
time: '',
airwayId: ''
} */
],
showMore: false,
pickerOptions: {
disabledDate: (time) => {
// 日期选择限制
return time.getTime() < Date.now() - 8.64e7;
},
},
airwayList: [], //航线列表
confirmLoading: false,
};
},
computed: {
...mapState("MMCFlightControlCenter/hangar", ["hangar"]),
...mapState("MMCFlightControlCenter", ["airwayEntities"]),
},
mounted() {
this.rootNode.$emit("airwayListGet", {
pageNo: 1,
pageSize: 100,
hangar: this.hangar,
callback: (data) => {
this.airwayList = data?.records || [];
},
});
},
methods: {
...mapActions("MMCFlightControlCenter", [
"createAirwayEntities",
"clearAirwayEntities",
]),
/**
* 显示或隐藏航线
*/
async onSwitchAirway(item) {
this.rootNode.$emit("airwayGet", {
airwayId: item.airwayId,
callback: (airway) => {
let find = this.airwayEntities.find(
(item1) => item1.airwayId === airway.id
);
if (find) {
this.clearAirwayEntities({ id: airway.id });
} else {
this.createAirwayEntities({
polyline: airway.content,
id: airway.id,
});
}
},
});
},
/**
* 添加新任务
*/
onTaskAdd(item) {
this.list.push({
time: "",
airwayId: "",
});
},
/**
* 删除的任务
*/
onDelTask(index) {
this.list = this.list.filter((item, i) => i !== index);
},
/**
* 确认事件
*/
onConfirm() {
let isOk = true;
this.list.some((item) => {
if (!item.time) {
this.$message.warning("请选择时间");
isOk = false;
return true;
}
if (!item.airwayId) {
this.$message.warning("请选择航线");
isOk = false;
return true;
}
});
if (isOk) {
let list = this.list.map((item) => {
let find = this.airwayList.find(
(airway) => airway.id === item.airwayId
);
return {
...item,
airway: find,
};
});
this.confirmLoading = true;
this.rootNode.$emit("taskBatchAdd", {
type: 2, //1: 日常任务 2.定时任务 3.周期任务
taskList: list, //任务数据
hangar: this.hangar,
callback: ({ code }) => {
this.confirmLoading = false;
if (code === 0) {
this.$emit("addDone");
this.$emit("close");
}
},
}); // 根节点发送机库任务新增事件
}
},
},
};
</script>
<style lang="scss" scoped>
.task-add {
height: 250px;
position: absolute;
top: 50px;
left: 550px;
width: 520px;
z-index: 1;
display: flex;
flex-direction: column;
gap: 5px;
&.more {
height: 376px;
}
.task-add-header {
flex-shrink: 0;
display: flex;
justify-content: space-between;
align-items: center;
height: 32px;
padding: 0 20px 0 0;
.header__title {
font-family: MicrosoftYaHei-Bold;
font-size: 16px;
color: #70daf9;
letter-spacing: 0;
font-weight: 700;
}
.header-right {
display: flex;
gap: 20px;
font-family: MicrosoftYaHei;
font-size: 16px;
color: #70daf9;
letter-spacing: 0;
font-weight: 400;
.header-right__add {
font-size: 12px;
display: flex;
align-items: center;
cursor: pointer;
}
.header-right__close {
font-size: 16px;
cursor: pointer;
}
}
}
.task-add-main {
padding: 20px 20px 0 10px;
flex: 1;
overflow-y: auto;
.main-item::v-deep {
margin-bottom: 20px;
display: flex;
align-items: center;
font-size: 15px;
color: #fff;
gap: 5px;
height: fit-content;
.el-date-editor {
width: 188px;
}
.el-select {
width: 200px;
}
.input {
width: 150px;
}
.iconfont {
color: rgb(67, 222, 255);
cursor: pointer;
}
}
}
.task-add-btn {
flex-shrink: 0;
text-align: center;
.task-add__btn {
width: 92px;
height: 36px;
background: #3388ff;
border-radius: 2px;
text-align: center;
line-height: 32px;
margin: 0 auto;
margin-bottom: 10px;
cursor: pointer;
color: #fff;
}
}
.task-add-more {
background-color: #191919;
height: 24px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
}
}
</style>
\ No newline at end of file
......@@ -141,6 +141,7 @@ export default {
pageNo: 1,
pageSize: 100,
type: this.type, // 0: 日常任务 1.定时任务 2.周期任务
taskStatus: 11,//筛选掉审核未通过的
hangar: this.hangar,
putDevice: 2,
callback: (res) => {
......
......@@ -59,8 +59,8 @@ export default {
<style lang="scss" scoped>
.task-list {
width: 526px;
height: 262px;
width: 350px;
height: 254px;
background: #222222;
border-radius: 10px 10px 10px 10px;
display: flex;
......
<template>
<div class="task-list">
<div class="task-list-header">
<div
class="task-list-header__item"
:class="{active: tabIndex === 0 && useTimedTask}"
@click="tabIndex = 0"
>
<label>常态飞行</label>
</div>
<template v-if="useTimedTask">
<div class="task-list-header__item" :class="{active: tabIndex === 1}" @click="tabIndex = 1">
<label>定时飞行</label>
</div>
<div class="task-list-header__item" :class="{active: tabIndex === 2}" @click="tabIndex = 2">
<label>周期飞行</label>
</div>
</template>
</div>
<div class="task-list-main">
<!-- 常态任务 -->
<NormalTask v-if="tabIndex === 0"></NormalTask>
<!-- 定时任务 -->
<TimedTask v-else-if="tabIndex === 1"></TimedTask>
<!-- 周期任务 -->
<PeriodTask v-else-if="tabIndex === 2"></PeriodTask>
</div>
</div>
</template>
<script>
import NormalTask from "./components/normalTask";
import TimedTask from "./components/timedTask";
import PeriodTask from "./components/periodTask";
import { mapState } from "vuex";
export default {
name: "hangarTaskList",
components: {
NormalTask,
TimedTask,
PeriodTask,
},
data() {
return {
tabIndex: 0,
};
},
computed: {
...mapState("MMCFlightControlCenter", ["useTimedTask"]),
},
watch: {
useTimedTask() {
if (!this.useTimedTask) {
this.tabIndex = 0;
}
},
},
mounted() {},
methods: {},
};
</script>
<style lang="scss" scoped>
.task-list {
width: 350px;
height: 254px;
background: #222222;
border-radius: 10px 10px 0 0;
display: flex;
flex-direction: column;
.task-list-header {
background: #3c3c3c;
border-radius: 10px 10px 0px 0px;
height: 32px;
flex-shrink: 0;
display: flex;
.task-list-header__item {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
font-family: YouSheBiaoTiHei;
font-size: 16px;
color: #fff;
cursor: pointer;
* {
cursor: pointer;
}
&.active {
background: #3388ff;
label {
font-family: YouSheBiaoTiHei;
font-size: 20px;
color: #14faff;
line-height: 26px;
text-shadow: 0px 1px 1px rgba(2, 32, 56, 0.2);
text-align: left;
font-style: normal;
background-image: -webkit-linear-gradient(
right,
#e3aa77,
#f5cda9,
#f9ecd3,
#fcdbb1,
#edb07a
);
line-height: 32px;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
}
}
}
.task-list-main {
flex: 1;
overflow: hidden;
}
}
</style>
\ No newline at end of file
......@@ -200,6 +200,7 @@ export default {
width: 48px;
height: 48px;
cursor: pointer;
z-index: 1;
&::before {
font-size: 24px;
......
......@@ -15,6 +15,16 @@
<el-form-item label="航线名称" required>
<el-input clearable v-model="name"></el-input>
</el-form-item>
<el-form-item label="所属单位:" required>
<el-cascader ref="CascaderRef" v-model="departmentId" :options="orgList" clearable :show-all-levels="false"
placeholder="请选择所属单位" :props="{
children: 'children',
label: 'name',
value: 'id',
checkStrictly: true,
emitPath: false,
}" @change="changeNode"></el-cascader>
</el-form-item>
<el-form-item label="航线速度" prop="speed">
<el-input clearable v-model="curForm.speed"></el-input>
</el-form-item>
......@@ -86,7 +96,7 @@
</template>
<script>
import { Map, AirLine } from "../../../../../../../../../../api";
import { Map, AirLine,TaskInfo } from "../../../../../../../../../../api";
import { mapState } from "vuex";
import Utils from "../../../../../../../../../../components/cesiumLayer/lib/cesium/utils";
import { nanoid } from "nanoid";
......@@ -123,7 +133,9 @@ export default {
inject: ["rootNode"],
data() {
return {
orgList: [],
name: "", //航线名
departmentId:'',//部门id
flightLabel:"",//航线标签
form: [], //表单集合
pageIndex: 0, //当前页码
......@@ -168,6 +180,7 @@ export default {
},
mounted() {
let viewer = this.cesiumViewer;
this.getOrgList();
this.dataSource = new Cesium.CustomDataSource(this.dataSourceName);
viewer.dataSources.add(this.dataSource);
this.handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas); //获取事件对象
......@@ -212,6 +225,77 @@ export default {
this.dataSource.show = false;
},
methods: {
changeNode() {
this.$refs.CascaderRef.dropDownVisible = false;
},
async getOrgList() {
let data = await TaskInfo.listAll();
let res = data.data;
const config = {
id: "id",
parentId: "parentId",
childrenList: "children",
};
const childrenListMap = {};
const nodeIds = {};
const tree = [];
for (const d of res) {
const parentId = d[config.parentId];
if (childrenListMap[parentId] == null) {
childrenListMap[parentId] = [];
}
nodeIds[d[config.id]] = d;
childrenListMap[parentId].push(d);
}
for (const d of res) {
const parentId = d[config.parentId];
if (nodeIds[parentId] == null) {
tree.push(d);
}
}
for (const t of tree) {
adaptToChildrenList(t);
}
function adaptToChildrenList(o) {
if (childrenListMap[o[config.id]] !== null) {
o[config.childrenList] = childrenListMap[o[config.id]];
}
if (o[config.childrenList]) {
for (const c of o[config.childrenList]) {
adaptToChildrenList(c);
}
}
}
this.orgList = tree;
},
convertToSeconds(timeString) {
const regex = /(\d+)\s*(小时|分钟|秒)/g;
let totalSeconds = 0;
let match;
while ((match = regex.exec(timeString)) !== null) {
const value = parseInt(match[1], 10);
const unit = match[2];
switch (unit) {
case '小时':
totalSeconds += value * 3600; // 1小时 = 3600秒
break;
case '分钟':
totalSeconds += value * 60; // 1分钟 = 60秒
break;
case '秒':
totalSeconds += value; // 直接加秒
break;
}
}
return totalSeconds;
},
async getLabel() {
this.rootNode.$emit("getLabelList", {
callback: (data) => {
......@@ -225,6 +309,10 @@ export default {
this.$message.warning("请输入航线名称");
return;
}
if (!this.departmentId) {
this.$message.warning("请选择所属机构");
return;
}
if (this.form.length === 0) {
this.$message.warning("请点击地图选择航点");
return;
......@@ -269,15 +357,19 @@ export default {
content: waypoints,
distance: this.distance,
dutyOrganizationId: "",
departmentId: this.departmentId, //部门id
name: this.name,
speed: this.form[0].speed,
flyTime: this.convertToSeconds(this.time)
};
try {
let res = await AirLine.add({
flightName: airway.name || `${this.userInfo.username}-巡查}`,
pointCount: airway.content.length,
distance: airway.distance,
departmentId: airway.departmentId,
sourceType: 1,
flyTime:airway.flyTime,
linePointSaveReqVOS: airway.content.map((point) => ({
latitude: point.coordinate.latitude,
longitude: point.coordinate.longitude,
......@@ -629,9 +721,8 @@ export default {
<style lang="scss" scoped>
.airway-edit {
position: absolute;
top: -50px;
width: 450px;
top: 0px;
width: 352px;
.dialog-content {
display: flex;
flex-direction: column;
......
......@@ -270,8 +270,8 @@ export default {
</script>
<style lang="scss" scoped>
.taskListBox {
min-height: 200px;
width: 416px;
height: 200px;
width: 350px;
background: #222222;
border-radius: 12px;
transition: 0.3s;
......
......@@ -543,9 +543,8 @@ export default {
<style lang="scss" scoped>
.airway-edit {
position: absolute;
top: -50px;
width: 450px;
top: 0px;
width: 352px;
.dialog-content {
display: flex;
flex-direction: column;
......
......@@ -234,7 +234,7 @@ export default {
<style lang="scss" scoped>
.taskListBox {
min-height: 254px;
width: 416px;
width:350px;
background: #222222;
border-radius: 12px;
transition: 0.3s;
......
......@@ -135,6 +135,7 @@ export default {
height: 48px;
background: #ffffff;
border-radius: 2px;
top: -29px;
display: flex;
justify-content: center;
align-items: center;
......
......@@ -2,15 +2,10 @@
<div class="mountBox">
<div class="main" v-if="show">
<div v-if="mountList.length > 0" class="list">
<div
class="mount-item pr mt6"
:class="{
active:
(selectMount && selectMount.gimbalName) === item.gimbalName,
}"
v-for="(item, index) in mountList"
:key="index"
>
<div class="mount-item pr mt6" :class="{
active:
(selectMount && selectMount.gimbalName) === item.gimbalName,
}" v-for="(item, index) in mountList" :key="index">
<div class="icon-box" @click="onSelectMount(item)">
<img class :src="item.icon" />
</div>
......@@ -18,17 +13,9 @@
</div>
</div>
<template v-if="selectMount">
<component
:is="selectMount.gimbalName"
v-if="selectMount"
class="mount-panel"
:keyFlag="nxNormal"
:payload_data="selectMountPayload"
@directive="mountDirective"
@take_photo="mountTakePhoto"
@record="mountRecord"
@close="mountClose"
/>
<component :is="selectMount.gimbalName" v-if="selectMount" class="mount-panel" :keyFlag="nxNormal"
:payload_data="selectMountPayload" @directive="mountDirective" @take_photo="mountTakePhoto"
@record="mountRecord" @close="mountClose" />
</template>
</div>
</template>
......@@ -48,6 +35,11 @@ export default {
...mountComponents,
},
props: {
// 是否机库页面使用
isHangar: {
type: Boolean,
default: false,
},
show: {
type: Boolean,
default: false,
......@@ -82,9 +74,9 @@ export default {
return this.uavRealTimeData?.healthData?.NX?.code === "0x2110000";
},
},
created() {},
mounted() {},
beforeDestroy() {},
created() { },
mounted() { },
beforeDestroy() { },
methods: {
...mapActions("MMCFlightControlCenter/uav", ["isTakeOver"]),
isTakeOverHangar() {
......
......@@ -2,72 +2,38 @@
<div class="control-right">
<!-- 挂载-警灯-无人机 -->
<div class="control-list" style="top: 13%">
<div
class="control-item"
:class="showHealth ? 'active' : ''"
@click="onSwitchShow('showHealth')"
>
<img :class="{active:showHealth}" src="./assets/images/health.svg" />
<div class="control-item" :class="showHealth ? 'active' : ''" @click="onSwitchShow('showHealth')">
<img :class="{ active: showHealth }" src="./assets/images/health.svg" />
<span class="">健康管理</span>
</div>
<!-- 机库信息 -->
<slot name="hangar"></slot>
<div
class="control-item"
:class="showMount ? 'active' : ''"
@click="onSwitchShow('showMount')"
>
<img :class="{active:showMount}" src="./assets/images/mount.svg" />
<div class="control-item" :class="showMount ? 'active' : ''" @click="onSwitchShow('showMount')">
<img :class="{ active: showMount }" src="./assets/images/mount.svg" />
<span class="">挂载</span>
</div>
<div
class="control-item"
:class="showControlList ? 'active' : ''"
@click="onSwitchShow('showControlList')"
>
<img :class="{active:showControlList}" src="./assets/images/uav.svg" />
<div class="control-item" :class="showControlList ? 'active' : ''" @click="onSwitchShow('showControlList')">
<img :class="{ active: showControlList }" src="./assets/images/uav.svg" />
<span class="">无人机</span>
</div>
<div
v-if="!isHangar && uav.network === 2"
class="control-item"
:class="showAlarmLamp ? 'active' : ''"
@click="onSwitchShow('showAlarmLamp')"
>
<img :class="{active:showAlarmLamp}" src="./assets/images/lamp.svg" />
<div v-if="!isHangar && uav.network === 2" class="control-item" :class="showAlarmLamp ? 'active' : ''"
@click="onSwitchShow('showAlarmLamp')">
<img :class="{ active: showAlarmLamp }" src="./assets/images/lamp.svg" />
<span class="">警灯</span>
</div>
<div
class="control-item"
:class="showViewLib ? 'active' : ''"
@click="onSwitchShow('showViewLib')"
>
<img :class="{active:showViewLib}" src="./assets/images/files.svg" />
<div class="control-item" :class="showViewLib ? 'active' : ''" @click="onSwitchShow('showViewLib')">
<img :class="{ active: showViewLib }" src="./assets/images/files.svg" />
<span class="">视图库</span>
</div>
<ViewLib v-if="showViewLib"></ViewLib>
</div>
<ControlList
@clearId="$emit('clearId')"
@closeIconShow="iconShow = false"
@exit="showControlList = false"
:show="showControlList"
:isHangar="isHangar"
></ControlList>
<ControlList @clearId="$emit('clearId')" @closeIconShow="iconShow = false" @exit="showControlList = false"
:show="showControlList" :isHangar="isHangar"></ControlList>
<Health v-if="showHealth" @exit="showHealth = false"></Health>
<Mount v-if="showMount" :show="showMount"></Mount>
<Mount v-if="showMount" :isHangar="isHangar" :show="showMount"></Mount>
<!-- 喊话器 -->
<MMCGimbalP1
class="PagerP1"
v-show="showMMCGimbalP1"
ref="MMCGimbalP1"
@close="showMMCGimbalP1 = false"
/>
<MountController
@webscoketFn="(data) => fun(data)"
v-if="uav && uav.deviceId"
ref="MountControllerRef"
/>
<MMCGimbalP1 class="PagerP1" v-show="showMMCGimbalP1" ref="MMCGimbalP1" @close="showMMCGimbalP1 = false" />
<MountController @webscoketFn="(data) => fun(data)" v-if="uav && uav.deviceId" ref="MountControllerRef" />
<AlarmLamp v-if="showAlarmLamp" :uav="uav" @close="showAlarmLamp = false" />
<slot name="dialog"></slot>
</div>
......@@ -114,12 +80,12 @@ export default {
},
computed: {
...mapState("MMCFlightControlCenter", ["showAirwayEdit"]),
...mapState("MMCFlightControlCenter/uav", ["uav", "airlineData"]),
...mapState("MMCFlightControlCenter/uav", ["uav", "airlineData",'uavRealTimeData']),
},
watch: {
// 打开航线编辑时关闭所有窗口
showAirwayEdit(newVal){
if(newVal){
showAirwayEdit(newVal) {
if (newVal) {
this.showAlarmLamp = false;
this.showHealth = false;
this.showMMCGimbalP1 = false;
......@@ -130,6 +96,37 @@ export default {
}
},
methods: {
network() {
let { uavRealTimeData } = this;
let name = null;
if (uavRealTimeData && uavRealTimeData.link) {
for (let i = 0; i < uavRealTimeData.link.length; i++) {
const k = uavRealTimeData.link[i];
if (k.priority == 2 && k.using) {
return "专网";
} else if (k.priority == 3 && k.using) {
return "公网";
} else if (k.priority == 1 && k.using) {
return "图传";
}
}
}
return name || "离线";
},
/**
* 接管判断, 机库模块中不需要判断接管
*/
async isTakeOver() {
if (this.isHangar) {
return await this.$store.dispatch(
"MMCFlightControlCenter/hangar/isTakeOver"
);
} else {
return await this.$store.dispatch(
"MMCFlightControlCenter/uav/isTakeOver"
);
}
},
hideAll(key) {
let arr = [
"showAlarmLamp",
......@@ -148,7 +145,16 @@ export default {
/**
* 切换展示
*/
onSwitchShow(key) {
async onSwitchShow(key) {
let networkType = this.network()
if (key == 'showHealth' && networkType == '离线') {
return this.$message.info('无人机不在线!');
}
// 判断是否已接管
if (!(await this.isTakeOver())) {
this.$message.warning("请先接管设备");
return;
}
this.$emit("switchCallback");
this.hideAll(key);
this[key] = !this[key];
......@@ -185,7 +191,7 @@ export default {
box-sizing: border-box;
overflow: hidden;
> img {
>img {
width: 24px;
}
......@@ -197,6 +203,7 @@ export default {
border: 1px solid #3C3C3C;
color: #3388ff;
}
.active {
filter: drop-shadow(#3388ff 100px 0);
transform: translateX(-100px);
......
......@@ -11,17 +11,19 @@
<el-table-column :label="`${hangar ? '机库' : '无人机'}所属单位`" align="center" prop="deviceDeptName"></el-table-column>
<el-table-column label="接管状态" align="center" prop="takeStats">
<template slot-scope="scope">
{{scope.row.takeStats ? '接管中' : '未接管'}}
<div :class="scope.row.takeStats ?'color-aqua':'color-tomato'">
{{scope.row.takeStats ? '接管中' : '未接管'}}
</div>
</template>
</el-table-column>
<el-table-column label="当前控制单位" align="center" prop="takeUserName"></el-table-column>
<el-table-column label="操作" align="center" prop="name">
<template slot-scope="scope">
<el-button @click="onExit(scope.row)" type="text" size="small" v-if="scope.row.takeStats">退出接管</el-button>
<el-buttonclas @click="onExit(scope.row)" type="text" class="color-aqua" size="small" v-if="scope.row.takeStats">退出接管</el-buttonclas>
</template>
</el-table-column>
</el-table>
</div>
</el-table>
</div>
<el-pagination
layout="prev, pager, next"
:total="page.total"
......@@ -105,4 +107,12 @@ export default {
}
}
}
.dialog-content{
.color-aqua{
color: aqua;
}
.color-tomato{
color: #ff993f;
}
}
</style>
\ No newline at end of file
......@@ -53,7 +53,7 @@
<img :src="gpsImg" />
</el-tooltip>
<!-- 搜星数 -->
<el-tooltip class="item" effect="dark" content="搜星数" placement="bottom">
<el-tooltip class="item " effect="dark" content="搜星数" placement="bottom">
<div>
<img src="./assets/images/satellite.png" />
<div class="s-count">{{ satelliteCount }}</div>
......@@ -481,6 +481,7 @@ export default {
align-items: center;
font-family: MicrosoftYaHei;
font-size: 16px;
z-index: 2;
color: #ffffff;
line-height: 21px;
padding: 0 4px;
......
# vue2
# node版本 >= 18.12.0
## Project setup
```
yarn install
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论