刘妍 2025-08-11 09:51:36 +08:00
commit f16c74f139
31 changed files with 1911 additions and 752 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 968 B

View File

@ -0,0 +1,486 @@
<template>
<div class="title-div">
<div class="title-span-div">
<div class="title-icon"></div>
<div class="title-span">创建算法</div>
</div>
<div class="close-button" @click="emits('changeAddModal',false)"></div>
</div>
<div class="interval"></div>
<div class="modal-content">
<div class="left-content">
<div class="item-title">模型名称</div>
<a-input class="name-input" v-model:value="modelName" placeholder="请输入相关名称" />
<div class="item-title">模型描述</div>
<a-textarea class="item-desc" v-model:value="modelDescription" placeholder="请输入相关描述" :rows="4" />
<div class="item-title">模型封面</div>
<div class="item-image">
<div class="empty-image" @click="changeSelectImageModal(true)">
<div class="empty-image-content">
<div class="empty-image-icon"></div>
<div class="empty-image-span">请选择文件上传</div>
</div>
</div>
</div>
</div>
<div class="right-content">
<div class="item-title">模型类型</div>
<a-segmented class="item-type" v-model:value="modelType" :options="modelTypeOptions"></a-segmented>
<template v-if="modelType == '0'">
<div class="item-title">模型规模</div>
<a-segmented class="item-type" v-model:value="modelScale" :options="modelScaleOptions"></a-segmented>
<div class="item-title">模型文件</div>
<a-upload
v-model:file-list="modelFile"
accept=".zip"
:maxCount="1"
:custom-request="customRequest"
>
<a-button class="select-model-file-button" type="primary" :icon="h(PlusOutlined)">选择模型文件</a-button>
<!-- <template #itemRender="{ file, actions }">
<a-space>
<span>{{ file.name }}</span>
</a-space>
</template> -->
</a-upload>
<div class="upload-hint">*请上传模型压缩文件支持pt格式</div>
<div class="item-title">模型标签</div>
<div class="item-label-div">
<div class="label-title-div">
<div class="label-title-span">请根据模型实际情况填写信息</div>
<div class="add-button" @click="addLabelRow">
<div class="button-icon"></div>
<div class="button-span">添加行</div>
</div>
</div>
<div class="label-interval"></div>
<div class="label-table-title">
<div class="table-title-item" style="padding-left: 6px;">枚举值</div>
<div class="table-title-item">名称</div>
<div class="table-title-item">推荐置信度</div>
<div class="table-title-item" style="width: 55px;justify-content: center;">操作</div>
</div>
<div class="label-table-content">
<div class="table-row" v-for="(item,index) in modelLabelList" :key="index">
<div class="table-item">
<a-input class="table-item-input" v-model:value="item.test"/>
</div>
<div class="table-item">
<a-input class="table-item-input" />
</div>
<div class="table-item">
<a-input class="table-item-input" />
</div>
<div class="table-item" style="width: 55px; justify-content: center">
<div class="del-table-row" @click="delLabelRow(index)"></div>
</div>
</div>
</div>
</div>
</template>
<template v-else>
<div class="item-title">推流地址</div>
<a-input class="name-input" v-model:value="modelPushAddress" placeholder="请输入地址" />
<div class="item-title">拉流地址</div>
<a-input class="name-input" v-model:value="modelPullAddress" placeholder="请输入地址" />
<div class="item-title">服务地址</div>
<a-input class="name-input" v-model:value="modelServeAddress" placeholder="请输入地址" />
<div class="item-title">密钥</div>
<a-textarea class="item-key" v-model:value="modelKey" placeholder="请输入密钥" :rows="4" />
</template>
</div>
</div>
<div class="interval"></div>
<div class="footer">
<a-button class="cancel-button" @click="emits('changeAddModal',false)"></a-button>
<a-button class="save-button" type="primary">确定</a-button>
</div>
<a-modal v-model:open="selectImageModal" title="选择封面" width="1000px" :footer="null" :destroyOnClose="true">
<SelectImageModal @changeSelectImageModal="changeSelectImageModal"/>
</a-modal>
</template>
<script setup lang="ts">
import { ref, h, defineEmits, defineProps } from "vue"
import { uploadFile } from '@/api/formrender/index';
import { PlusOutlined, } from '@ant-design/icons-vue';
import SelectImageModal from "./SelectImageModal.vue";
const props = defineProps([''])
const emits = defineEmits(['changeAddModal'])
const modelName = ref('')
const modelDescription = ref('')
const modelType = ref('0')
const modelScale = ref('N')
const modelPullAddress = ref('')
const modelPushAddress = ref('')
const modelServeAddress = ref('')
const modelKey = ref('')
const modelFile = ref([])
const modelFilePath = ref()
const modelLabelList = ref([])
const selectImageModal = ref(false)
const modelTypeOptions = ref([
{ label: '本地模型', value: '0' },
{ label: '远程模型', value: '1' }
])
const modelScaleOptions = ref([
{ label: 'N', value: 'N' },
{ label: 'S', value: 'S' },
{ label: 'M', value: 'M' },
{ label: 'L', value: 'L' },
{ label: 'XI', value: 'XI' },
])
const customRequest = (file) => {
const formData = new FormData()
formData.append('files', file.file)
uploadFile(formData).then(res => {
modelFilePath.value = res[0].filePath
// emits('update:shppath',res[0].filePath)
})
}
const addLabelRow = () => {
// TODO
modelLabelList.value.push({
test:''
})
}
const delLabelRow = (index) => {
modelLabelList.value.splice(index,1)
}
const changeSelectImageModal = (type: boolean) => {
selectImageModal.value = type
}
</script>
<style lang="scss" scoped>
.title-div{
width: 100%;
height: 64px;
padding-top: 15px;
padding-left: 26px;
padding-right: 35px;
padding-bottom: 14px;
display: flex;
justify-content: space-between;
align-items: center;
.title-span-div{
display: flex;
align-items: center;
.title-icon{
width: 3px;
height: 18px;
background: #0B60BD;
box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
margin-right: 9px;
}
.title-span{
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 20px;
color: #11131B;
line-height: 33px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
}
}
.close-button{
width: 18px;
height: 18px;
background-image: url('/public/ailist/add_algorithm_close_button.png');
cursor: pointer;
}
}
.interval{
width: 631px;
height: 1px;
box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border: 1px solid #DBDBDB;
opacity: 0.66;
}
.modal-content{
display: flex;
padding-top: 14px;
padding-left: 27px;
padding-right: 35px;
padding-bottom: 28px;
.item-title{
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 16px;
color: #222738;
line-height: 33px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
margin-bottom: 9px;
}
.name-input{
height: 40px;
// box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border-radius: 2px;
border: 1px solid #CCCCCC;
margin-bottom: 6px;
}
.left-content{
width: 253px;
margin-right: 46px;
.item-desc{
height: 151px;
// box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border-radius: 2px;
border: 1px solid #CCCCCC;
margin-bottom: 6px;
}
.item-image{
position: relative;
width: 100%;
height: 155px;
background: #F5F5F5;
// box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border: 2px dashed rgba(28,29,34,0.08);
.empty-image{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 209px;
height: 119px;
background: #FFFFFF;
box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15), 0px 8px 6px 0px rgba(28,29,34,0.06);
border-radius: 2px;
border: 1px solid #CCCCCC;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
.empty-image-content{
width: 98px;
height: 63px;
display: flex;
flex-direction: column;
align-items: center;
.empty-image-icon{
width: 26px;
height: 28px;
background-image: url('/public/ailist/algorithm_upload_icon.png');
margin-bottom: 1px;
}
.empty-image-span{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 14px;
color: #222738;
line-height: 33px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
}
}
}
}
}
.right-content{
width: 269px;
.item-type{
width: 262px;
height: 40px;
background: rgba(28,29,34,0.04);
border-radius: 20px;
margin-bottom: 6px;
// box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
:deep(.ant-segmented-group){
height: 36px;
}
:deep(.ant-segmented-item){
width: 50%;
display: flex;
align-items: center;
justify-content: center;
border-radius: 18px;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 14px;
color: rgba(28,29,34,0.5);
line-height: 20px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
}
:deep(.ant-segmented-item-selected){
font-weight: 500;
color: #1C1D22;
}
}
.select-model-file-button{
width: 151px;
height: 40px;
background: #0D68CB;
box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border-radius: 2px;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 16px;
color: #FFFFFF;
line-height: 33px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
}
.upload-hint{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #333333;
line-height: 28px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
}
.item-label-div{
width: 100%;
height: 155px;
background: #F5F5F5;
// box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border-radius: 2px;
padding-top: 3px;
padding-left: 8px;
padding-right: 10px;
.label-title-div{
width: 100%;
height: 34px;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 4px;
.label-title-span{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #222738;
line-height: 33px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
}
.add-button{
width: 66px;
height: 26px;
background: #FFFFFF;
// box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15), 0px 8px 6px 0px rgba(28,29,34,0.06);
border: 1px solid rgba(28,29,34,0.06);
border-radius: 13px;
display: flex;
align-items: center;
padding-left: 8px;
cursor: pointer;
user-select: none;
.button-icon{
width: 8px;
height: 8px;
background-image: url('/public/ailist/algorithm_add_row_icon.png');
margin-right: 4px;
}
.button-span{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #0D68CB;
line-height: 33px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
}
}
}
.label-interval{
height: 1px;
box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
background:#DBDBDB;
opacity: 0.66;
margin-left: -8px;
margin-right: -10px;
}
.label-table-title{
height: 30px;
display: flex;
margin-bottom: 4px;
.table-title-item{
display: flex;
width: 66px;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #222738;
line-height: 33px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
}
}
.label-table-content{
height: 75px;
overflow: auto;
scrollbar-width: none;
::-webkit-scrollbar {
width: 0;
height: 0;
}
.table-row{
width: 100%;
height: 26px;
display: flex;
margin-bottom: 1px;
.table-item{
width: 65px;
height: 26px;
background: #FFFFFF;
// box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15), 0px 8px 6px 0px rgba(28,29,34,0.06);
border-radius: 2px 0px 0px 2px;
margin-right: 1px;
display: flex;
align-items: center;
// padding-left: 9px;
.table-item-input{
height: 26px;
border: 0px;
}
.table-item-input:focus{
box-shadow: none
}
.del-table-row{
width: 14px;
height: 14px;
background-image: url('/public/ailist/del_table_row.png');
cursor: pointer;
}
}
}
}
}
.item-key{
height: 88px;
// box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border-radius: 2px;
border: 1px solid #CCCCCC;
}
}
}
.footer{
height: 85px;
padding-right: 34px;
display: flex;
align-items: center;
justify-content: end;
.cancel-button{
width: 101px;
height: 40px;
box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border-radius: 4px;
border: 1px solid #979797;
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 16px;
color: #222738;
line-height: 33px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
margin-right: 14px;
}
.save-button{
width: 146px;
height: 40px;
background: #0D68CB;
box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15);
border-radius: 4px;
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 16px;
color: #FFFFFF;
line-height: 33px;
text-shadow: 0px 10px 30px rgba(0,0,6,0.15);
}
}
</style>

View File

@ -0,0 +1,166 @@
<template>
<div class="cropper-tool">
<div class="container">
<div class="left">
<div style="padding-left: 20px;">
<a-upload :fileList="[]" accept="image/*" :beforeUpload="onFileChange">
<a-button size="small" type="primary">选择图片</a-button>
</a-upload>
</div>
<div style="margin-top:8px">
<img style="width: 640px;height: 266px;" ref="sourceImg" crossorigin="anonymous" />
</div>
<div class="controls">
<button @click="zoomIn"></button>
<button @click="zoomOut"></button>
<button @click="rotateLeft"></button>
<button @click="rotateRight"></button>
<button @click="flipHorizontal"></button>
<button @click="flipVertical"></button>
<button @click="reset"></button>
</div>
</div>
<div class="right">
<div style="margin-bottom:10px">裁切预览固定 300×200</div>
<div class="preview" ref="previewBox" style="width:300px;height:200px;">
<span v-if="!croppedUrl"></span>
<img v-if="croppedUrl" :src="croppedUrl" alt="预览" />
</div>
<div style="margin-top:8px; display:flex; gap:8px;">
<a v-if="croppedUrl" :href="croppedUrl" download="cropped.png"></a>
<button @click="clear"></button>
</div>
</div>
</div>
<div class="footer">
<a-button style="margin-right: 15px;" @click="emits('changeSelectImageModal',false)"></a-button>
<a-button type="primary" @click="submitImage"></a-button>
</div>
</div>
</template>
<script setup>
import { ref, defineEmits } from 'vue';
import Cropper from 'cropperjs';
import 'cropperjs/dist/cropper.css';
import { dataURLtoBlob } from '@/utils/file/base64Conver';
import { uploadFile } from '@/api/formrender/index';
const emits = defineEmits(['changeSelectImageModal'])
const sourceImg = ref(null);
const cropper = ref(null);
const flipH = ref(1);
const flipV = ref(1);
const zoomValue = ref(1);
const croppedUrl = ref('');
const onFileChange = (e) => {
console.log(e)
const file = e
if (!file) return;
const url = URL.createObjectURL(file);
sourceImg.value.src = url;
sourceImg.value.onload = () => {
if (cropper.value) {
cropper.value.destroy();
}
cropper.value = new Cropper(sourceImg.value, {
viewMode: 1,
aspectRatio: 3 / 2, // 300:200
autoCropArea: 1,
movable: true,
zoomable: true,
rotatable: true,
scalable: true,
responsive: true,
background: true,
cropBoxResizable: false,
cropBoxMovable: false,
dragMode: 'move',
ready() {
zoomValue.value = 1;
flipH.value = 1;
flipV.value = 1;
croppedUrl.value = '';
getCropped()
},
crop() {
getCropped()
}
});
};
};
const zoomIn = () => cropper.value?.zoom(0.1);
const zoomOut = () => cropper.value?.zoom(-0.1);
const rotateLeft = () => cropper.value?.rotate(-90);
const rotateRight = () => cropper.value?.rotate(90);
const flipHorizontal = () => {
flipH.value = -flipH.value;
cropper.value?.scaleX(flipH.value);
};
const flipVertical = () => {
flipV.value = -flipV.value;
cropper.value?.scaleY(flipV.value);
};
const reset = () => {
cropper.value?.reset();
zoomValue.value = 1;
flipH.value = 1;
flipV.value = 1;
};
const getCropped = () => {
const canvas = cropper.value.getCroppedCanvas({ width: 300, height: 200 });
if (!canvas) return;
croppedUrl.value = canvas.toDataURL('image/png');
console.log('croppedUrl.value',croppedUrl.value)
};
const clear = () => {
if (cropper.value) {
cropper.value.destroy();
cropper.value = null;
}
sourceImg.value.src = '';
croppedUrl.value = '';
};
const submitImage = (format='image/jpeg', quality = 0.9) => {
// const blob = dataURLtoBlob(croppedUrl.value);
// const formData = new FormData();
// formData.append('files', blob);
// uploadFile(formData).then(res => {
// console.log(res)
// })
if (!cropper.value) return;
cropper.value.getCroppedCanvas({ width: 300, height: 200 })
.toBlob(async (blob) => {
if (!blob) return;
// FormData
const formData = new FormData();
formData.append('files', blob, `cropped.jpeg`);
uploadFile(formData).then(res => {
console.log(res)
})
}, format, quality);
}
</script>
<style scoped>
.container { display: flex; gap: 20px; }
.left { width: 640px; }
.right { width: 320px; }
img { max-width: 100%; display: block; max-height: 480px; background: #f3f3f3; }
.controls { margin-top: 10px; display:flex; flex-wrap:wrap; gap:8px; }
.preview { border:1px solid #ddd; display:flex; align-items:center; justify-content:center; overflow:hidden; background:#fafafa }
.footer{
margin-top: 12px;
height: 53px;
border-top: 1px solid #eee;
display: flex;
align-items: center;
justify-content: end;
padding-right: 40px;
}
</style>

View File

@ -1,136 +1,403 @@
<template>
<PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
<div class="w-1/4 xl:w-1/5 leftbox flex column">
<div class="flex ai-c jc-sb">
<div class="textbox">
<p>算法库</p>
<span>行业生态算法厂商</span>
<div class="w-1/4 xl:w-1/5 leftbox">
<div class="left-title-div">
<div class="span-div">
<div class="span-title">算法库</div>
<div class="span-subtitle">行业生态算法厂商</div>
</div>
<a-button type="primary" size="small" @click="createAddClick">
<template #icon><PlusOutlined /></template>
创建
</a-button>
<a-button class="add-algorithm" type="primary" :icon="h(PlusOutlined)" @click="changeAddModal(true)"></a-button>
</div>
<div class="flex ai-c jc-c column flex-1">
<div class="descbox">
<p>请先创建自定义算法基于算法新增实例<br />了解更多<BookOutlined :style="{fontSize: '14px', color: '#0A99EB'}" /> 说明书</p>
<div class="left-interval"></div>
<div class="show-algorithm-list-div">
<div class="empty-list">
<div class="empty-image"></div>
<div class="empty-span-title">暂无数据</div>
<div class="empty-span-subtitle">请先创建自定义算法基于算法新增实例</div>
<div style="display: flex;align-items: center;margin-bottom: 28px;">
<div class="empty-span-subtitle" style="display: inline;">了解更多</div>
<div class="empty-icon-image"></div>
<div class="empty-span-subtitle" style="display: inline;">说明书 </div>
</div>
<a-button class="empty-add-algorithm" type="primary" :icon="h(PlusOutlined)" @click="changeAddModal(true)"></a-button>
</div>
<a-button type="primary" @click="createAddClick">
<template #icon><PlusOutlined /></template>
创建自定义算法
</a-button>
</div>
</div>
<BasicTable
class="w-3/4 xl:w-4/5"
@register="registerTable"
@fetch-success="onFetchSuccess"
:searchInfo="searchInfo"
>
</BasicTable>
<Modal
@submitsuccess="submitsuccess"
@register="registerModal"
@submit-reload="submitReload"
/>
<div class="w-3/4 xl:w-4/5 right-out-div">
<div class="search-div">
<div class="search-span-icon"></div>
<div class="search-span">算法库</div>
<div class="search-input-div">
<a-input-search
class="search-input"
v-model:value="searchValue"
placeholder="按照实例搜索"
enter-button="搜索"
@search="onSearch"
>
<template #prefix>
<div class="search-input-icon"></div>
</template>
</a-input-search>
</div>
</div>
<div class="search-interval">
<div class="interval-icon"></div>
</div>
<div class="show-list-div">
<div class="list-div">
<div class="item" v-for="value in 8">
<div class="image-div">
<img class="image-item" src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png">
<div class="image-span">
<div class="image-span-icon"></div>
<div class="image-span-info">#排水沟盖板缺</div>
</div>
</div>
<div class="info-div">
<div class="info-icon"></div>
<div class="info-span-div">
<div class="info-span-title">高速排水沟盖板缺失检测算法</div>
<div class="info-span-subtitle">识别高速公路两侧的排水沟盖板缺</div>
</div>
<a-button class="show-info-button" type="primary">查看</a-button>
</div>
</div>
</div>
<div class="pagination-div">
<a-pagination
size="small"
:total="50"
show-size-changer
show-quick-jumper
:show-total="total => `共 ${total} 条数据`"
/>
</div>
</div>
</div>
<a-modal width="631px" v-model:open="addAlgorithmModal" :footer="null" :closable="false" :destroyOnClose="true" :maskClosable="false" :keyboard="false">
<AddAlgorithmModal @changeAddModal="changeAddModal"/>
</a-modal>
</PageWrapper>
</template>
<script lang="ts" setup>
import { reactive, nextTick, ref } from 'vue';
import { PlusOutlined, BookOutlined } from '@ant-design/icons-vue';
import { BasicTable, useTable } from '@/components/Table';
import { getButtonList, getMenuDetail, deleteButton } from '@/api/demo/system';
import { ref,h } from 'vue';
import { PlusOutlined, } from '@ant-design/icons-vue';
import { PageWrapper } from '@/components/Page';
import { columns, searchFormSchema } from './index.data';
import { useMessage } from '@/hooks/web/useMessage';
import Modal from './modal.vue';
import { useModal } from '@/components/Modal';
import AddAlgorithmModal from './AddAlgorithmModal.vue';
const { createConfirm, createMessage } = useMessage();
defineOptions({ name: 'MenuManagement' });
const searchInfo = reactive<Recordable>({});
const [registerTable, { reload, expandAll, getSelectRows, clearSelectedRowKeys }] = useTable({
title: '算法列表',
// api: getButtonList,
columns,
rowKey: 'id',
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
},
isTreeTable: true,
striped: false,
useSearchForm: true,
showTableSetting: true,
bordered: true,
showIndexColumn: false,
rowSelection: {
//
// type: 'checkbox',
type: 'radio',
},
handleSearchInfoFn(info) {
return info;
},
});
const [registerModal, { openModal }] = useModal();
function createAddClick(){
openModal(true, {
});
const searchValue = ref('');
const addAlgorithmModal = ref(false)
const onSearch = () => {
}
const changeAddModal = (type: boolean) => {
addAlgorithmModal.value = type
}
function onFetchSuccess() {
//
nextTick(expandAll);
}
</script>
<style scoped>
.flex{
display: flex;
}
.flex-1{
flex: 1;
}
.column{
flex-direction: column;
}
.ai-c{
align-items: center;
}
.jc-sb{
justify-content: space-between;
}
.jc-c{
justify-content: center;
}
<style lang="scss" scoped>
.leftbox{
background: #fff;
margin: 16px 0 16px 16px;
padding: 16px;
.textbox{
p{
.left-title-div{
width: 100%;
height: 82px;
display: flex;
align-items: center;
padding-left: 16px;
padding-right: 33px;
justify-content: space-between;
.span-div{
width: 104px;
height: 42px;
.span-title{
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 16px;
color: #060606;
line-height: 22px;
}
.span-subtitle{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 13px;
color: #393939;
line-height: 18px;
}
}
.add-algorithm{
width: 80px;
height: 30px;
background: #0B60BD;
border-radius: 0px;
}
}
.left-interval{
width: 100%;
height: 1px;
border: 1px solid rgba(28,29,34,0.08);
opacity: 0.46;
}
.show-algorithm-list-div{
position: relative;
width: 100%;
height: calc(100% - 84px );
.empty-list{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 280px;
height: 311px;
background: #FFFFFF;
box-shadow: 5px 18px 32px 0px rgba(0,0,0,0.1);
border: 2px dashed rgba(28,29,34,0.08);
border-radius: 10px;
padding-top: 28px;
display: flex;
flex-direction: column;
align-items: center;
.empty-image{
width: 114px;
height: 120px;
background-image: url('/public/ailist/empty_image.png');
margin-bottom: 8px;
}
.empty-span-title{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 13px;
color: #000000;
line-height: 18px;
margin-bottom: 12px;
}
.empty-span-subtitle{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 13px;
color: #393939;
line-height: 18px;
}
.empty-icon-image{
width: 16px;
height: 14px;
background-image: url('/public/ailist/empty_icon_image.png');
line-height: 18px;
display: inline;
cursor: pointer;
}
.empty-add-algorithm{
width: 167px;
height: 30px;
background: #0D68CB;
border-radius: 0px;
}
}
}
}
.right-out-div{
padding: 16px 16px 16px 33px;
.search-div{
position: relative;
height: 57px;
display: flex;
align-items: center;
.search-span-icon{
width: 20px;
height: 18px;
background-image: url('/public/ailist/search_span_icon.png');
margin-right: 10px;
}
.search-span{
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 16px;
color: #060606;
line-height: 22px;
margin-bottom: 0;
}
span{
font-weight: 400;
font-size: 13px;
color: #393939;
line-height: 18px;
.search-input-div{
position: absolute;
top: 0px;
right: 0px;
width: 304px;
height: 38px;
.search-input{
:deep(.ant-input-affix-wrapper){
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 14px;
line-height: 28px;
height: 38px;
border-top-left-radius: 19px;
border-bottom-left-radius: 19px;
}
:deep(.ant-input-search-button,){
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 14px;
color: #FFFFFF;
line-height: 28px;
height: 38px;
border-top-right-radius: 19px;
border-bottom-right-radius: 19px;
}
:deep(.ant-input){
padding-left: 20px;
}
.search-input-icon{
width: 18px;
height: 18px;
background-image: url('/public/ailist/search_input_icon.png');
}
}
}
}
.descbox{
font-weight: 400;
font-size: 13px;
color: #393939;
line-height: 18px;
text-align: center;
.search-interval{
position: relative;
width: 100%;
height: 2px;
background:rgba(28,29,34,0.08);
margin-bottom: 22px;
.interval-icon{
position: absolute;
top: 0px;
left: 0px;
width: 137px;
height: 2px;
background: #0B60BD;
}
}
.show-list-div{
width: 100%;
height: calc( 100% - 81px );
background: #FFFFFF;
border: 2px dashed rgba(28,29,34,0.08);
border-radius: 10px;
padding: 32px;
.list-div{
width: 100%;
display: flex;
flex-wrap: wrap;
margin-bottom: 90px;
.item{
width: 300px;
height: 268px;
background: #FFFFFF;
box-shadow: 5px 18px 32px 0px rgba(28,29,34,0.1);
border-radius: 10px;
margin-right: 10px;
margin-bottom: 20px;
.image-div{
position: relative;
width: 300px;
height: 200px;
margin-bottom: 12px;
.image-item{
width: 100%;
height: 100%;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.image-span{
position: absolute;
left: 0px;
bottom: 0px;
width: 205px;
height: 20px;
background: linear-gradient( 270deg, rgba(7,7,7,0) 0%, #111111 100%);
display: flex;
align-items: center;
padding-left: 9px;
.image-span-icon{
width: 4px;
height: 4px;
background: #FFA048;
border-radius: 50%;
margin-right: 5px;
}
.image-span-info{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 11px;
color: #FFFFFF;
line-height: 15px;
}
}
}
.info-div{
width: 100%;
height: 40px;
display: flex;
align-items: center;
padding-left: 7px;
.info-icon{
width: 3px;
height: 34px;
background: #0B60BD;
margin-right: 5px;
}
.info-span-div{
width: 187px;
margin-right: 22px;
.info-span-title{
font-family: PingFangSC-Medium;
font-weight: 500;
font-size: 14px;
color: #1C1D22;
line-height: 20px;
margin-bottom: 5px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.info-span-subtitle{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 11px;
color: rgba(28,29,34,0.5);
line-height: 15px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.show-info-button{
width: 54px;
height: 23px;
border-radius: 12px;
background: #0B60BD;
:deep(span){
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 11px;
color: #FFFFFF;
line-height: 15px;
}
}
}
}
.item:nth-child(4n){
margin-right: 0px;
}
}
.pagination-div{
padding-right: 32px;
width: 100%;
height: 66px;
display: flex;
align-items: center;
justify-content: end;
}
}
.right-box{
width: 100%;
height: 100%;
background-color: #fff;
}
}
</style>

View File

@ -1,184 +0,0 @@
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
:canFullscreen="false"
:defaultFullscreen="false"
:showCancelBtn="true"
:showOkBtn="true"
:draggable="false"
title="创建算法"
@ok="submitClick"
>
<div class="flex titlesbox">
<div class="li" v-for="(item,index) in tabList">
<span :class="index==tabIndex?'active':''" @click="tabsClick(index)">{{item}}</span>
</div>
</div>
<!-- 视频算法 -->
<div class="formsbox" v-if="tabIndex == 0">
<a-form
:model="formState"
ref="formRef"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
autocomplete="off"
>
<a-form-item
label="算法名称"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="概要描述"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-textarea v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="算力名称"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="推流地址"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="拉流地址"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="服务地址"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="秘钥"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
</a-form>
</div>
<!-- 图片算法 -->
<div class="formsbox" v-if="tabIndex == 1">
<a-form
:model="formState"
ref="formRef"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
autocomplete="off"
>
<a-form-item
label="算法名称"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="概要描述"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-textarea v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="算力名称"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="服务地址"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="秘钥"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
</a-form>
</div>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, defineEmits, reactive, toRaw } from 'vue';
import { BasicModal, useModalInner } from '@/components/Modal';
const tabIndex = ref(0);
const tabList = ["视频算法","图片算法"];
const tabsClick = (index) =>{
tabIndex.value = index
}
const formRef = ref();
interface FormState {
username: string;
password: string;
remember: boolean;
}
const formState = reactive<FormState>({
username: '',
password: '',
remember: true,
});
const submitClick = ()=>{
console.log('submit')
formRef.value.validate().then(() => {
console.log('values', formState, toRaw(formState));
})
.catch(error => {
console.log('error', error);
});
}
const [registerModal, { closeModal }] = useModalInner((data: any) => {
});
</script>
<style scoped>
.titlesbox{
width: 92%;
margin-left: 4%;
line-height: 40px;
border-bottom: 1px solid #F5F6FA;
.li{
width: 50%;
text-align: center;
span{
color: #222738;
cursor: pointer;
line-height: 40px;
display: inline-block;
}
.active{
border-bottom: 3px solid #3A57E8;
}
}
}
.formsbox{
width: 92%;
margin-left: 4%;
padding-top: 20px;
}
</style>

View File

@ -495,7 +495,15 @@
const searchParams = ref();
const [
registerTable,
{ reload, getDataSource, getSelectRows, setSelectedRows, clearSelectedRowKeys },
{
reload,
getDataSource,
getSelectRows,
setSelectedRows,
clearSelectedRowKeys,
getPaginationRef,
setPagination,
},
] = useTable({
api: GetMediaFile,
rowKey: 'id',
@ -585,6 +593,11 @@
nowParentKey.value = f.id;
floders.value = floders.value.splice(0, index + 1);
clearSelectedRowKeys();
if (current) {
setPagination({
current: current,
});
}
reload().then((res) => {
showTableData.value = res;
});
@ -719,6 +732,7 @@
//
const previewRecordList: any = ref([]);
// -
let current = 1;
async function lookRecord(record) {
//
if (record.objectKey) {
@ -751,6 +765,8 @@
id: nowParentKey.value,
name: record.name,
});
current = getPaginationRef().current;
setPagination({ current: 1 });
reload().then((res) => {
showTableData.value = res;
});

View File

@ -36,6 +36,8 @@ import {ref,watch,onMounted,defineEmits} from 'vue';
import airLineList from '../workplan/components/airLineList.vue';
import createAirLine from '../workplan/components/createAirLine.vue'
import importAirLine from '../workplan/components/importAirLine.vue'
import Map from '../workplan/components/map.vue'
import AirPolygon from './components/airPolygon.vue';
import {getAirLine} from '@/api/sys/workplan';
@ -47,10 +49,19 @@ import { XMLParser, XMLBuilder } from 'fast-xml-parser';
const emits = defineEmits(["updateAirLineLise"])
// add edit detail
//
const currentFolder = ref(null);
//
const setCurrentFolder = (folder)=>{
currentFolder.value = folder;
importAirLineShow.value = true;
}
// 线 add edit detail
const editMode = ref("add");
// airLine form
// 线
const airLineForm = ref({
"id": null,
"airLineName": null,
@ -74,197 +85,11 @@ const airLineForm = ref({
// "lineData":null,
});
// template.kml
const templateKmlConfig = ref({
"author": 17861857725,
"createTime": 1749689844431,
"updateTime": 1753241338101,
"missionConfig": {
"flyToWaylineMode": "pointToPoint",
"finishAction": "goHome",
"exitOnRCLost": "goContinue",
"executeRCLostAction": "goBack",
"takeOffSecurityHeight": 20,
"takeOffRefPoint": "",
"takeOffRefPointAGLHeight": 4.169064385,
"globalTransitionalSpeed": 15,
"globalRTHHeight": 100,
"droneInfo": {
"droneEnumValue": 100,
"droneSubEnumValue": 1
},
"autoRerouteInfo": {
"transitionalAutoRerouteMode": 1,
"missionAutoRerouteMode": 1
},
"waylineAvoidLimitAreaMode": 0,
"payloadInfo": {
"payloadEnumValue": 99,
"payloadSubEnumValue": 0,
"payloadPositionIndex": 0
}
},
"Folder": {
"templateType": "mapping2d",
"templateId": 0,
"waylineCoordinateSysParam": {
"coordinateMode": "WGS84",
"heightMode": "EGM96",
"globalShootHeight": 90,
"surfaceFollowModeEnable": 1,
"isRealtimeSurfaceFollow": 0,
"surfaceRelativeHeight": 90,
"dsmFile": "wpmz/res/dsm/wgs84_ASTGTMV003_N35E118_dem_7.tif"
},
"autoFlightSpeed": 12.7,
"Placemark": {
"caliFlightEnable": 0,
"elevationOptimizeEnable": 1,
"smartObliqueEnable": 0,
"quickOrthoMappingEnable": 0,
"facadeWaylineEnable": 0,
"isLookAtSceneSet": 0,
"shootType": "time",
"direction": 83,
"margin": 0,
"efficiencyFlightModeEnable": 0,
"overlap": {
"orthoCameraOverlapH": 80,
"orthoCameraOverlapW": 70,
"inclinedCameraOverlapH": 80,
"inclinedCameraOverlapW": 70
},
"Polygon": {
"outerBoundaryIs": {
"LinearRing": {
"coordinates": "118.293794766158,35.1353688096117,0\n 118.295429169407,35.1353304409052,0\n 118.295487507293,35.1332925168381,0\n 118.293672196844,35.1331918267775,0"
}
}
},
"ellipsoidHeight": 90,
"height": 90
},
"payloadParam": {
"payloadPositionIndex": 0,
"focusMode": "firstPoint",
"meteringMode": "average",
"returnMode": "singleReturnStrongest",
"samplingRate": 240000,
"scanningMode": "repetitive",
"imageFormat": "visable",
"photoSize": ""
}
}
})
// 线template.kml
const templateKmlConfig = ref({})
// wayline.wpml
const waylineWpmlConfig = ref({
"missionConfig": {
"flyToWaylineMode": "pointToPoint",
"finishAction": "goHome",
"exitOnRCLost": "goContinue",
"executeRCLostAction": "goBack",
"takeOffSecurityHeight": 20,
"globalTransitionalSpeed": 15,
"globalRTHHeight": 100,
"droneInfo": {
"droneEnumValue": 100,
"droneSubEnumValue": 1
},
"autoRerouteInfo": {
"transitionalAutoRerouteMode": 1,
"missionAutoRerouteMode": 1
},
"waylineAvoidLimitAreaMode": 0,
"payloadInfo": {
"payloadEnumValue": 99,
"payloadSubEnumValue": 0,
"payloadPositionIndex": 0
}
},
"Folder": {
"templateId": 0,
"executeHeightMode": "WGS84",
"waylineId": 0,
"distance": 1259.17163085938,
"duration": 145.652896165848,
"autoFlightSpeed": 12.7,
"startActionGroup": {
"action": [
{
"actionId": 0,
"actionActuatorFunc": "gimbalRotate",
"actionActuatorFuncParam": {
"gimbalHeadingYawBase": "aircraft",
"gimbalRotateMode": "absoluteAngle",
"gimbalPitchRotateEnable": 1,
"gimbalPitchRotateAngle": -90,
"gimbalRollRotateEnable": 0,
"gimbalRollRotateAngle": 0,
"gimbalYawRotateEnable": 1,
"gimbalYawRotateAngle": 0,
"gimbalRotateTimeEnable": 0,
"gimbalRotateTime": 10,
"payloadPositionIndex": 0
}
},
{
"actionId": 1,
"actionActuatorFunc": "hover",
"actionActuatorFuncParam": {
"hoverTime": 0.5
}
},
{
"actionId": 2,
"actionActuatorFunc": "setFocusType",
"actionActuatorFuncParam": {
"cameraFocusType": "manual",
"payloadPositionIndex": 0
}
},
{
"actionId": 3,
"actionActuatorFunc": "focus",
"actionActuatorFuncParam": {
"focusX": 0,
"focusY": 0,
"focusRegionWidth": 0,
"focusRegionHeight": 0,
"isPointFocus": 0,
"isInfiniteFocus": 1,
"payloadPositionIndex": 0,
"isCalibrationFocus": 0
}
},
{
"actionId": 4,
"actionActuatorFunc": "hover",
"actionActuatorFuncParam": {
"hoverTime": 1
}
}
]
},
"Placemark": []
}
})
//
const currentFolder = ref(null);
//
const setCurrentFolder = (folder)=>{
currentFolder.value = folder;
importAirLineShow.value = true;
}
const flyToTherePosition = ref({
lng:null,
lat:null,
alt:null,
})
// 线wayline.wpml
const waylineWpmlConfig = ref({})
const flyToThere = (e)=>{
@ -278,22 +103,6 @@ const aircraftShow = ref(false);
const createAirLineShow = ref(false);
const importAirLineShow = ref(false);
const selectAriLine = ()=> {
ariLineShow.value = true;
aircraftShow.value = false;
}
const selectAircraft = ()=>{
aircraftShow.value = true;
ariLineShow.value = false;
}
const cancleCraete = ()=>{
workPlanFormShow.value = false;
ariLineShow.value = false;
aircraftShow.value = false;
planListShow.value = true;
}
const checkedAriLine = ref({});
@ -306,23 +115,11 @@ const checkAriLine = (item)=>{
const checkedDronePort = ref({});
const checkDronePort = (item)=>{
if(item){
aircraftShow.value = false;
checkedDronePort.value = item;
}
}
const formData = ref(null);
const toCreateWorkPlan = (data)=> {
formData.value = data;
planListShow.value = false;
workPlanFormShow.value = true;
}
// 线
const globalFolder = ref(null);
const handlerCreateAirLine = (folder)=>{
globalFolder.value = folder;
createAirLineShow.value = true;
@ -337,21 +134,33 @@ const cancleImportAirLine = ()=>{
importAirLineShow.value = false;
}
const handlerCreateAirLineForm = (form)=>{
// 线
const handlerCreateAirLineForm =async (form)=>{
console.log("form",form);
// 线template.kmlwayline.wpml
if(form.airLineType == 'waypoint'){
const {templateKml,waylineWpml} = await import("../workplan/waylineConfig/waypointConfig");
templateKmlConfig.value = templateKml;
waylineWpmlConfig.value = waylineWpml;
}else if(form.airLineType == 'mapping2d'){
const {templateKml,waylineWpml} = await import("../workplan/waylineConfig/mapping2dConfig");
templateKmlConfig.value = templateKml;
waylineWpmlConfig.value = waylineWpml;
}
console.log("templateKmlConfig999",templateKmlConfig.value);
//
workPlanFormShow.value = false;
ariLineShow.value = false;
aircraftShow.value = false;
planListShow.value = false;
createAirLineShow.value = false;
// 线
airLineForm.value = form
airLineForm.value.folder = globalFolder.value;
console.log("airLineFor121m",airLineForm.value);
// 线
airLineForm.value = form
//
airLineForm.value.folder = globalFolder.value ? globalFolder.value : "";
}
const successCreatePlan = ()=>{
@ -370,8 +179,6 @@ const exitDraw = ()=>{
}
// 线
const wayline = ref({});
const waylineInfo = ref(null)
@ -400,7 +207,6 @@ const planDetail =async (item) => {
// wpml
const currentPreviewWayLine = ref(null);
const airLineDetail =async (item) => {
@ -441,10 +247,8 @@ const extractKmz = async (kmzBlob)=>{
}
onMounted(()=>{
let element = window.document.getElementsByClassName("mars3d-locationbar")[0] as HTMLElement;
element.style.bottom = '40px';
let element = window.document.getElementsByClassName("mars3d-locationbar")[0] as HTMLElement;
element.style.bottom = '40px';
})
@ -483,6 +287,7 @@ const importSuccess = () => {
airLineListRef.value.getAirList();
importAirLineShow.value = false;
}
</script>
<style scoped>
.map-out-container{

View File

@ -2,7 +2,11 @@
<div class="params-item">
<div class="params-label">俯仰角度</div>
<div class="params-value">
<a-slider v-model:value="props.params.gimbalPitchRotateAngle" :min="-180" :max="180" :step="0.01" />
<a-slider
v-model:value="props.params.gimbalPitchRotateAngle"
:min="-90" :max="30" :step="0.01"
@change="paramChagne"
/>
</div>
<div class="params-value-text">
{{ props.params.gimbalPitchRotateAngle }} °
@ -14,13 +18,16 @@
import {defineProps,defineComponent,defineOptions} from 'vue';
const props = defineProps(["params"])
const props = defineProps(["params","info"])
const emits = defineEmits(["paramChagne"])
defineComponent({
name: 'gimbalRotate'
})
const paramChagne = ()=> {
emits("paramChagne",props.info);
}
</script>

View File

@ -4,6 +4,7 @@
<div class="params-value">
<a-input-number v-model:value="props.params.hoverTime"></a-input-number>
</div>
<div class="params-unit"></div>
</div>
</template>
@ -35,6 +36,10 @@ defineComponent({
flex:1;
}
.params-item .params-unit{
line-height:30px;
padding:0px 5px 0px 15px;
}
::v-deep .ant-input-number{
color:#fff!important;

View File

@ -0,0 +1,55 @@
<template>
<div class="params-item">
<div class="params-label">间隔时间</div>
<div class="params-value">
<a-input-number v-model:value="props.params.hoverTime"></a-input-number>
</div>
<div class="params-unit"></div>
</div>
</template>
<script lang="ts" setup>
import {defineProps,defineComponent,defineOptions} from 'vue';
const props = defineProps(["params"])
defineComponent({
name: 'multipleTiming'
})
</script>
<style scoped>
.params-item{
width:100%;
display: flex;
gap:20px;
}
.params-item .params-label{
line-height:30px;
}
.params-item .params-value{
flex:1;
}
.params-item .params-unit{
line-height:30px;
padding:0px 5px 0px 15px;
}
::v-deep .ant-input-number{
color:#fff!important;
background:#3F4150!important;
border:0px !important;
}
::v-deep .ant-input-number-input{
color:#fff!important;
border:0px !important;
}
</style>

View File

@ -1,8 +1,11 @@
<template>
<div class="params-item">
<div class="params-label">偏航角度</div>
<div class="params-label">偏航角度</div>
<div class="params-value">
<a-slider v-model:value="props.params.aircraftHeading" :min="-180" :max="180" :step="0.01" />
<a-slider
v-model:value="props.params.aircraftHeading"
:min="-180" :max="180" :step="0.01"
@change="paramChagne" />
</div>
<div class="params-value-text">
{{ props.params.aircraftHeading }} °
@ -12,16 +15,20 @@
<script lang="ts" setup>
import {defineProps,defineComponent,defineOptions} from 'vue';
const props = defineProps(["params"])
import {defineProps,defineComponent,defineEmits} from 'vue';
const props = defineProps(["params","info"])
const emits = defineEmits(["paramChagne"])
defineComponent({
name: 'rotateYaw'
})
const paramChagne = ()=> {
emits("paramChagne",props.info);
}
</script>
<style scoped>

View File

@ -153,7 +153,9 @@
</div>
</div>
<div class="pagenation">
<a-pagination v-model:current="pageQuery.page" :total="ariLineCount" show-less-items @change="onPageChange" />
<a-pagination size="small" v-model:current="pageQuery.page"
v-model:page-size="pageQuery.limit" :total="ariLineCount"
:defaultPageSize="10" show-less-items show-size-changer @change="onPageChange" />
</div>
</div>
</div>
@ -245,7 +247,7 @@
const props = defineProps(["title"])
import {templateTypeOptions} from '../waylineConfig/index';
import {templateTypeOptions} from '../waylineConfig/waylineParamsOptions';
import JSZip from 'jszip';
import axios from 'axios';

View File

@ -2,18 +2,16 @@
<div class="container">
<div class="title">
<LeftOutlined @click="backPage" />
<div>航点航线</div>
<SaveOutlined @click="saveAirLine"/>
<div style="flex:1;">
<a-input v-model:value="props.airLineForm.airLineName" size="middle" placeholder="航线名称" />
</div>
<a-button type="primary" size="middle" @click="areaOptionsShow = !areaOptionsShow">
航线设置
<ControlOutlined />
</a-button>
<div style="flex:1;">
<a-input v-model:value="props.airLineForm.airLineName" size="middle" placeholder="航线名称" />
</div>
<SaveOutlined @click="saveAirLine" style="font-size:20px;" />
</div>
<!-- 航线信息 -->
@ -165,7 +163,7 @@
<div class="item">
<div class="label">全局航线飞行速度</div>
<div class="content">
<a-input style="width:100px;" placeholder="" v-model:value="missionConfig.globalTransitionalSpeed"></a-input>
<a-input style="width:100px;" placeholder="" v-model:value="props.templateKmlConfig.Folder.autoFlightSpeed"></a-input>
</div>
<div class="unit"> m/s</div>
</div>
@ -343,12 +341,14 @@ import {ref,defineEmits,defineProps} from 'vue'
import { SaveOutlined,LeftOutlined,AppstoreOutlined,DownOutlined,ControlOutlined,InfoCircleOutlined} from '@ant-design/icons-vue';
//
import { airPointActions } from '../waylineConfig/action'
import { airPointActions } from '../waylineConfig/actionConfig'
import { XMLParser, XMLBuilder } from 'fast-xml-parser';
import {uploadXmlFile,addAirLine} from '@/api/sys/workplan';
import {uploadXmlFile,addAirLine,editAirLine} from '@/api/sys/workplan';
import { Modal, message } from 'ant-design-vue';
import {missionConfigOptions,folderConfigOptions} from '../waylineConfig/index.ts';
import {missionConfigOptions,folderConfigOptions} from '../waylineConfig/waylineParamsOptions.ts';
import JSZip from "jszip";
import { saveAs } from "file-saver";
const emits = defineEmits(["setTakeOffPoint","checkPoint","exitDraw","addAction"])
@ -520,93 +520,11 @@ const pointInfo = ref({
//
const handlerPointInfo = ()=>{
if(props.airPoints?.length<=0){
message.warning("请添加航点!");
return null;
}
props.airPoints?.forEach((item,index)=>{
console.log("item123",item);
let point = {
"Point": {
"coordinates": item.lng+","+item.lat
},
"index": index,
"executeHeight": item.alt,
"waypointSpeed": 10,
"waypointHeadingParam": {
"waypointHeadingMode": "followWayline", //
"waypointHeadingAngle": item.aircraftHorizontalAngle, // [-180, 180] wpml:waypointHeadingModesmoothTransition
"waypointPoiPoint": "0.000000,0.000000,0.000000", // wpml:waypointHeadingModetowardPOI
"waypointHeadingAngleEnable": 0,
"waypointHeadingPathMode": "followBadArc", //
"waypointHeadingPoiIndex": 0
},
"waypointTurnParam": {
"waypointTurnMode": "toPointAndStopWithDiscontinuityCurvature",
"waypointTurnDampingDist": 0
},
"useStraightLine": 1,
"actionGroup": {
"actionGroupId": 0,
"actionGroupStartIndex": 0,
"actionGroupEndIndex": 0,
"actionGroupMode": "sequence",
"actionTrigger": {
"actionTriggerType": "reachPoint"
},
"action": [
{
"actionId": 0,
"actionActuatorFunc": "rotateYaw",
"actionActuatorFuncParam": {
"aircraftHeading": 0,
"aircraftPathMode": "counterClockwise"
}
},
{
"actionId": 1,
"actionActuatorFunc": "gimbalRotate",
"actionActuatorFuncParam": {
"gimbalHeadingYawBase": "north",
"gimbalRotateMode": "absoluteAngle",
"gimbalPitchRotateEnable": 1,
"gimbalPitchRotateAngle": 0,
"gimbalRollRotateEnable": 0,
"gimbalRollRotateAngle": 0,
"gimbalYawRotateEnable": 0,
"gimbalYawRotateAngle": 0,
"gimbalRotateTimeEnable": 0,
"gimbalRotateTime": 0,
"payloadPositionIndex": 0
}
},
{
"actionId": 2,
"actionActuatorFunc": "zoom",
"actionActuatorFuncParam": {
"focalLength": 24,
"isUseFocalFactor": 0,
"payloadPositionIndex": 0
}
}
]
},
"waypointGimbalHeadingParam": {
"waypointGimbalPitchAngle": item.cameraVerticalAngle, // [-90, -30]
"waypointGimbalYawAngle": 0 //
},
"isRisky": 0,
"waypointWorkType": 0
}
folder.value.Placemark.push(point);
})
return true;
}
// waylines.json
@ -626,52 +544,194 @@ const waylinesJson = ref(
// 线
const saveAirLine = ()=>{
// template.json
handlerTemplateKml();
//
handlerWaylineWpml();
let handlerResult = handlerPointInfo();
if(handlerResult){
const builder = new XMLBuilder();
let lineData = {...waylinesJson.value}
// template missionConfig wayline
props.waylineWpmlConfig.missionConfig = JSON.parse(JSON.stringify(props.templateKmlConfig.missionConfig))
let obj = handlerPrefixWpml(lineData);
const builder = new XMLBuilder({
format: true, //
indentBy: " ", // 2 \t
suppressEmptyNode: true, //
});
let xmlString = builder.build(obj);
// wpml template json
let templateJson = {kml:{Document:props.templateKmlConfig}}
let templateWpmlJson = handlerPrefixWpml(templateJson);
let templateXmlStr = builder.build(templateWpmlJson);
let templateXmlStrTemp = templateXmlStr.replace(/<\/?\d+>/g, "")
let templateXml = templateXmlStrTemp.replace("<kml>",`<?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://www.opengis.net/kml/2.2" xmlns:wpml="http://www.dji.com/wpmz/1.0.6">`)
let xmlString2 = xmlString.replace(/<\/?\d+>/g, "")
// wpml wayline json
// delete props.waylineWpmlConfig.missionConfig.takeOffRefPoint
// delete props.waylineWpmlConfig.missionConfig.takeOffRefPointAGLHeight
// delete props.waylineWpmlConfig.missionConfig.autoRerouteInfo
let xmlString3 = xmlString2.replace("<kml>",`<?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://www.opengis.net/kml/2.2" xmlns:wpml="http://www.dji.com/wpmz/1.0.6">`)
console.log("xmlString3",xmlString3);
return null;
handlerCreateFile(xmlString3);
let waylineJosn = {kml:{Document:props.waylineWpmlConfig}}
let waylienWpmlJson = handlerPrefixWpml(waylineJosn);
let waylineXmlStr = builder.build(waylienWpmlJson);
let waylineXmlStrTemp = waylineXmlStr.replace(/<\/?\d+>/g, "")
let waylineXml = waylineXmlStrTemp.replace("<kml>",`<?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://www.opengis.net/kml/2.2" xmlns:wpml="http://www.dji.com/wpmz/1.0.6">`)
//
handlerCreateFile(templateXml,waylineXml);
}
}
// wmpl:
const handlerPrefixWpml = (obj) => {
for (const key in obj) {
// template.json
const handlerTemplateKml = () => {
//
if (typeof obj[key] === 'object' && obj[key] !== null) {
//
handlerCreateOrUpdateTime();
handlerPrefixWpml(obj[key]); //
//
// handlerAirLineHeight();
const newAttrs = {};
for (const attrName in obj[key]) {
//
if (/^[a-z]/.test(attrName)) {
newAttrs[`wmpl:${attrName}`] = obj[key][attrName];
} else {
newAttrs[attrName] = obj[key][attrName];
}
}
//
const handlerCreateOrUpdateTime = () => {
let date = new Date();
if(props.editModel == 'add'){
props.templateKmlConfig.createTime = date.getTime();
props.templateKmlConfig.updateTime = date.getTime();
}else if(props.editModel == 'edit'){
props.templateKmlConfig.updateTime = date.getTime();
}
}
// wayline.json
const handlerWaylineWpml = () => {
//
props.waylineWpmlConfig.missionConfig = JSON.parse(JSON.stringify(props.templateKmlConfig.missionConfig));
//
handlerStatistics();
//
handelrAirPoint();
}
const handlerStatistics = () => {
}
//
const handelrAirPoint = () => {
props.airPoints?.forEach((item,index)=>{
let point = {
"Point": {
"coordinates": item.lng+","+item.lat
},
"index": index,
"executeHeight": 172.948304450636,
"waypointSpeed": props.templateKmlConfig.Folder.autoFlightSpeed,
"waypointHeadingParam": {
"waypointHeadingMode": "followWayline",
"waypointHeadingAngle": 0,
"waypointPoiPoint": "0.000000,0.000000,0.000000",
"waypointHeadingAngleEnable": 0,
"waypointHeadingPathMode": "followBadArc",
"waypointHeadingPoiIndex": 0
},
"waypointTurnParam": {
"waypointTurnMode": "toPointAndStopWithDiscontinuityCurvature",
"waypointTurnDampingDist": 0
},
"useStraightLine": 1,
"waypointGimbalHeadingParam": {
"waypointGimbalPitchAngle": 0,
"waypointGimbalYawAngle": 0
},
"isRisky": 0,
"waypointWorkType": 0
}
if(item.actions.length>0){
point.actionGroup = {
"actionGroupId": 0,
"actionGroupStartIndex": index,
"actionGroupEndIndex": index,
"actionGroupMode": "sequence",
"actionTrigger": {
"actionTriggerType": "reachPoint"
},
"action": []
};
item.actions.forEach((action,idx)=>{
let actor = action.config;
actor.actionId = idx
point.actionGroup.action.push(actor);
})
}
props.waylineWpmlConfig.Folder.Placemark.push(point);
props.templateKmlConfig.Folder.Placemark.push(point);
})
}
// wpml:
const handlerPrefixWpml = (obj) => {
for (const key in obj) {
if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) { //
handlerPrefixWpml(obj[key]); //
const newAttrs = {};
for (const attrName in obj[key] ) {
//
if (/^[a-z]/.test(attrName) && attrName != 'outerBoundaryIs' && attrName != 'coordinates') {
newAttrs[`wpml:${attrName}`] = obj[key][attrName];
} else {
newAttrs[attrName] = obj[key][attrName];
}
}
obj[key] = newAttrs;
}else if(typeof obj[key] === 'object' && obj[key] !== null && Array.isArray(obj[key])){ //
handlerPrefixWpml(obj[key]); //
const newAttrs = [];
for (const attrName in obj[key]) {
//
if (/^[a-z]/.test(attrName)) {
newAttrs[`wpml:${attrName}`] = obj[key][attrName];
} else {
newAttrs[attrName] = obj[key][attrName];
}
}
obj[key] = newAttrs;
}
obj[key] = newAttrs;
}
}
return obj;
return obj;
}
@ -695,15 +755,35 @@ const submitForm = ref({
})
// xml线
const handlerCreateFile =async (content)=>{
const blob = new Blob([content], { type: 'wmpl/plain' });
const handlerCreateFile =async (templateXml,waylineXml)=>{
console.log("waylineXml",templateXml,waylineXml);
const blob =await convertXmlToKmz(templateXml,waylineXml);
// FormData
const formData = new FormData();
formData.append('xmlFile', blob);
let res = await uploadXmlFile(formData);
formData.append('xmlFile', blob,"航点航线.kmz");
let res = await uploadXmlFile(props.airLineForm.folder,formData);
if(res){
submitForm.value.wpml = res.path;
let addAirLineRes =await addAirLine(submitForm.value);
props.airLineForm.wpml = res.path;
// props.airLineForm.lineData = {type:2}
let addAirLineRes = null;
if(props.editModel == 'add'){
addAirLineRes =await addAirLine(props.airLineForm);
}else if(props.editModel == 'edit'){
addAirLineRes =await editAirLine(props.airLineForm);
}
if(addAirLineRes){
message.success("操作成功!");
backPage();
@ -715,6 +795,28 @@ const handlerCreateFile =async (content)=>{
}
}
// xmlwpmlkml
const convertXmlToKmz =async (templateXml,waylineXml)=>{
const zip = new JSZip();
// 1. "wmpz"
const wmpzFolder = zip.folder("wpmz");
// 2.
wmpzFolder.file("waylines.wpml", waylineXml);
wmpzFolder.file("template.kml", templateXml);
// 3. KMZ (ZIP)
const kmzBlob = await zip.generateAsync({ type: "blob" });
// 线
// saveAs(kmzBlob, "output.kmz");
return kmzBlob;
}
// 退
const backPage = ()=>{
@ -727,7 +829,6 @@ const addAirPointAction = (action) => {
//
emits("addAction",action);
if(action.value == ""){

View File

@ -127,19 +127,23 @@
</div>
</div>
<div class="item-content">
<component :is="componentMap[item.value]" :params="item.config.actionActuatorFuncParam" :key="index"></component>
<component
:is="componentMap[item.value]"
:params="item.config.actionActuatorFuncParam"
:info="item"
:key="index"
@paramChagne="paramChagne"
></component>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import {ref,defineProps,watch,shallowRef,defineAsyncComponent,defineOptions } from 'vue';
import {ref,defineProps,watch,shallowRef,defineAsyncComponent,defineOptions,defineEmits} from 'vue';
import { DeleteOutlined,LeftOutlined,RightOutlined } from '@ant-design/icons-vue'
@ -163,8 +167,11 @@ const componentMap = {
};
const props = defineProps(['currentAirPoint','airPoints'])
const emits = defineEmits(["paramChagne"])
const paramChagne = (info) => {
emits("paramChagne",info);
}
watch(
()=>props.currentAirPoint,

View File

@ -250,7 +250,7 @@
<script lang="ts" setup>
import {ref,defineEmits,defineProps,watch} from 'vue'
import { SaveOutlined,LeftOutlined,InfoCircleOutlined } from '@ant-design/icons-vue';
import {missionConfigOptions,folderConfigOptions} from '../waylineConfig/index.ts';
import {missionConfigOptions,folderConfigOptions} from '../waylineConfig/waylineParamsOptions.ts';
import {uploadXmlFile,addAirLine,editAirLine} from '@/api/sys/workplan';
import { message } from 'ant-design-vue';
import { XMLParser, XMLBuilder } from 'fast-xml-parser';
@ -771,7 +771,6 @@ const handlerPrefixWpml = (obj) => {
//
if (/^[a-z]/.test(attrName) && attrName != 'outerBoundaryIs' && attrName != 'coordinates') {
console.log("attrName",attrName);
newAttrs[`wpml:${attrName}`] = obj[key][attrName];
} else {
newAttrs[attrName] = obj[key][attrName];
@ -814,7 +813,7 @@ const handlerCreateFile =async (templateXml,waylineXml)=>{
formData.append('xmlFile', blob,"面状航线.kmz");
let res = await uploadXmlFile("默认文件夹/面状航线",formData);
let res = await uploadXmlFile(props.airLineForm.folder,formData);
if(res){
props.airLineForm.wpml = res.path;

View File

@ -33,7 +33,7 @@
</div>
</a-form-item> -->
<a-form-item ref="workspaceId" label="选择项目" name="workspaceId">
<!-- <a-form-item ref="workspaceId" label="选择项目" name="workspaceId">
<div class="">
<a-select
v-model:value="submitForm.workspaceId"
@ -46,7 +46,7 @@
</a-select>
</div>
</a-form-item>
</a-form-item> -->
<a-form-item ref="name" label="选择航线" name="name">

View File

@ -44,6 +44,7 @@
<airPointConfig
:currentAirPoint="currentAirPoint"
:airPoints="airPoints"
@paramChagne="paramChagne"
></airPointConfig>
</div>
@ -66,7 +67,6 @@
import airPolygon from './airPolygon.vue';
import airPointConfig from './airPointConfig.vue';
// const props = defineProps([
// 'airLineForm',
// 'flyToTherePosition',
@ -296,12 +296,11 @@
watch(
() => props.waylineInfo,
(newVal,oldVal) => {
}
)
// 线
const generatePreviewPoint = (placemark)=>{
@ -358,24 +357,30 @@ const generatePreviewPoint = (placemark)=>{
let moveTool: mars3d.thing.MatrixMove2;
//
watch(
currentAirPoint,
(newVal, oldVal) => {
//
updateAirPoint(newVal);
//
handlerDrawCamera(newVal);
},
{ deep: true },
);
// watch(
// currentAirPoint,
// (newVal, oldVal) => {
// // console.log("newVal123",newVal);
// //
// // updateAirPoint(newVal);
// //
// // handlerDrawCamera(newVal);
// },
// { deep: true },
// );
const clickPoint = (id) => {
airPoints.value?.forEach((item, index) => {
if (item.id == id) {
currentAirPoint.value = item;
//
handlerDrawCamera(currentAirPoint.value);
airPointConfigShow.value = true;
}
});
};
const checkPoint = (e) => {
@ -686,7 +691,6 @@ const initMap = () => {
// 线
graphicLayer.on(mars3d.EventType.click, (e) => {
//
clickPoint(e.graphic.options.id);
});
@ -1575,7 +1579,8 @@ const handlerDrawLine = () => {
// let lineString = turf.lineString(positions);
// lineInfo.value.length = turf.length(lineString).toFixed(2);
};
//
const uavPoints = ref([]);
//
@ -1584,40 +1589,32 @@ const handlerDrawPoint = (e) => {
moveTool.destroy();
moveTool = null;
}
// 线
let position = mars3d.LngLatPoint.fromCartesian(e.position);
let uuid = buildUUID();
// 线
let globalHeight = props.templateKmlConfig.Folder.globalHeight;
//
let uavAngleGraphic = new mars3d.graphic.ModelEntity({
id:uuid,
name: "航向",
position: [position._lng, position._lat, position._alt],
position: [position._lng, position._lat, globalHeight],
style: {
url: "/map/uav-angle.gltf",
scale: 0.1,
heading: 90,
heading:90,
distanceDisplayCondition: true,
distanceDisplayCondition_near: 0,
distanceDisplayPoint: {
color: "#00ff00",
pixelSize: 8
},
label: {
text: "",
font_size: 18,
color: "#ffffff",
pixelOffsetY: -50,
distanceDisplayCondition: true,
distanceDisplayCondition_far: 10000,
distanceDisplayCondition_near: 0
}
},
attr: { remark: "示例1" }
}
})
graphicLayer.addGraphic(uavAngleGraphic)
//
let stickGraphic = new mars3d.graphic.PointEntity({
id: 'stick' + uuid,
@ -1632,13 +1629,13 @@ const handlerDrawPoint = (e) => {
},
});
stickGroundPointLayer.addGraphic(stickGraphic);
// 线
let lineGraphic = new mars3d.graphic.PolylineEntity({
id: 'line' + uuid,
positions: [
[position._lng, position._lat, position._alt],
[position._lng, position._lat, 0],
[position._lng, position._lat, globalHeight],
[position._lng, position._lat, 0],
],
style: {
color: '#f5f5f5',
@ -1646,34 +1643,83 @@ const handlerDrawPoint = (e) => {
},
});
lineGroundPointLayer.addGraphic(lineGraphic);
//
let airPointInfo = {
id: uuid,
name: '航点',
lng: position._lng,
lat: position._lat,
alt: position._alt,
alt: globalHeight,
aircraftHorizontalAngle: 0,
cameraHorizontalAngle: 0,
cameraVerticalAngle: 0,
focalLength: 2,
actions:[],
actions:[
{ //
"name":"飞行器偏航角",
"value":"rotateYaw",
"type":"action",
"config":{
"actionId": null,
"actionActuatorFunc": "rotateYaw",
"actionActuatorFuncParam": {
"aircraftHeading": 0,
"aircraftPathMode": "counterClockwise"
}
},
},{ //
"name":"云台俯仰角",
"value":"gimbalRotate",
"type":"action",
"config":{
"actionId": 3,
"actionActuatorFunc": "gimbalRotate",
"actionActuatorFuncParam": {
"gimbalHeadingYawBase": "north",
"gimbalRotateMode": "absoluteAngle",
"gimbalPitchRotateEnable": 1,
"gimbalPitchRotateAngle": 0,
"gimbalRollRotateEnable": 0,
"gimbalRollRotateAngle": 0,
"gimbalYawRotateEnable": 0,
"gimbalYawRotateAngle": 0,
"gimbalRotateTimeEnable": 0,
"gimbalRotateTime": 0,
"payloadPositionIndex": 0
}
}
},{ //
"name":"相机焦距",
"value":"zoom",
"type":"action",
"config":{
"actionId": null,
"actionActuatorFunc": "zoom",
"actionActuatorFuncParam": {
"focalLength": 24,
"isUseFocalFactor": 0,
"payloadPositionIndex": 0
}
}
}
],
};
airPoints.value?.push(airPointInfo);
uavPoints.value.push(graphic);
//
currentAirPoint.value = airPoints.value[airPoints.value?.length - 1];
//
handlerDrawCamera(airPointInfo);
// 线
handlerDrawLine();
};
//
const handlerDrawCamera = (e) => {
//
const graphic = graphicLayer.getGraphicById("cameraGraphic");
if(graphic){ //
@ -1692,8 +1738,8 @@ const handlerDrawCamera = (e) => {
topSteps: 2,
flat: true,
cameraHpr: true,
heading:e.aircraftHorizontalAngle,
pitch: e.cameraVerticalAngle+90, // 0 - 360
heading:e.aircraftHorizontalAngle - 90, //
pitch: e.cameraVerticalAngle - 90, // 0 - 360
roll: 0,
}
})
@ -1712,8 +1758,8 @@ const handlerDrawCamera = (e) => {
topShow: true,
topSteps: 2,
cameraHpr: true,
heading:e.aircraftHorizontalAngle,
pitch: e.cameraVerticalAngle+90, // 0 - 360
heading:e.aircraftHorizontalAngle - 90,
pitch: e.cameraVerticalAngle - 90, // 0 - 360
roll: 0,
}
})
@ -1733,24 +1779,14 @@ const updateAirPoint = (e) => {
style: {
url: "/map/uav-angle.gltf",
scale: 0.1,
heading: e.aircraftHorizontalAngle,
heading: e.aircraftHorizontalAngle + 90,
distanceDisplayCondition: true,
distanceDisplayCondition_near: 0,
distanceDisplayPoint: {
color: "#00ff00",
pixelSize: 8
},
label: {
text: "",
font_size: 18,
color: "#ffffff",
pixelOffsetY: -50,
distanceDisplayCondition: true,
distanceDisplayCondition_far: 10000,
distanceDisplayCondition_near: 0
}
},
attr: { remark: "示例1" }
}
}
);
}
@ -1816,27 +1852,73 @@ const updateAirPoint = (e) => {
handlerDrawLine();
};
//
const paramChagne = (info) => {
console.log("info1232",info);
//
if(info.value == 'rotateYaw'){ //
//
currentAirPoint.value.aircraftHorizontalAngle = info.config.actionActuatorFuncParam.aircraftHeading;
//
handlerDrawCamera(currentAirPoint.value);
//
updateAirPoint(currentAirPoint.value);
}
if(info.value == "gimbalRotate"){ //
//
currentAirPoint.value.cameraVerticalAngle = info.config.actionActuatorFuncParam.gimbalPitchRotateAngle
//
handlerDrawCamera(currentAirPoint.value);
}
}
//
const updateCamera = () => {
}
//
const deleteAirPoint = (e) => {
let id = e.graphic.id;
//
let point = graphicLayer.getGraphicById(id);
if (point) {
graphicLayer.removeGraphic(point);
let uavModel = graphicLayer.getGraphicById(id);
if (uavModel) {
graphicLayer.removeGraphic(uavModel);
}
//
let camera = graphicLayer.getGraphicById('camera' + id);
let camera = graphicLayer.getGraphicById("cameraGraphic");
if (camera) {
graphicLayer.removeGraphic(camera);
graphicLayer.removeGraphic(camera);
}
//
let point = lineGroundPointLayer.getGraphicById("line"+id);
if (point) {
lineGroundPointLayer.removeGraphic(point);
}
// 线
let line = stickGroundPointLayer.getGraphicById("stick"+id);
if (line) {
stickGroundPointLayer.removeGraphic(line);
}
//
airPoints.value?.forEach((item, index) => {
if (item.id == id) {
airPoints.value?.splice(index, 1);
}
if (item.id == id) {
airPoints.value?.splice(index, 1);
}
});
// 线
@ -2511,11 +2593,9 @@ function addRectSensor(route) {
///////////////////////////////////////////////////////////////////////
const addAction = (action) => {
//
console.log("action",action);
currentAirPoint.value.actions.push(action);
console.log("currentAirPoint",currentAirPoint.value)
currentAirPoint.value.actions.push(JSON.parse(JSON.stringify(action)));
}

View File

@ -0,0 +1,55 @@
<template>
<div class="params-item">
<div class="params-label">间隔距离</div>
<div class="params-value">
<a-input-number v-model:value="props.params.hoverTime"></a-input-number>
</div>
<div class="params-unit"></div>
</div>
</template>
<script lang="ts" setup>
import {defineProps,defineComponent,defineOptions} from 'vue';
const props = defineProps(["params"])
defineComponent({
name: 'multipleDistance'
})
</script>
<style scoped>
.params-item{
width:100%;
display: flex;
gap:20px;
}
.params-item .params-label{
line-height:30px;
}
.params-item .params-value{
flex:1;
}
.params-item .params-unit{
line-height:30px;
padding:0px 5px 0px 15px;
}
::v-deep .ant-input-number{
color:#fff!important;
background:#3F4150!important;
border:0px !important;
}
::v-deep .ant-input-number-input{
color:#fff!important;
border:0px !important;
}
</style>

View File

@ -225,14 +225,12 @@
}
]
)
const onPageChange = (e)=>{
pageQuery.value.page = e;
getTaskList();
}
//
const planStatusOptions = ref([
{

View File

@ -16,11 +16,10 @@ export const calculateHeight = (gsd,uavModel) => {
// 飞行高度= GSD(cm)×实际焦距(mm)/像元尺寸(um)
let abc = (gsd * uavModel['focalLength'] / uavModel['pixelLength']);
height = (gsd * uavModel['focalLength'] / uavModel['pixelLength']);
console.log("abc",abc);
height = ( (gsd / 100) * (uavModel['focalLength'] / 1000) * uavModel['imageWidth'] ) / ( uavModel['sensorLength'] / 1000 );
// height = ( (gsd / 100) * (uavModel['focalLength'] / 1000) * uavModel['imageWidth'] ) / ( uavModel['sensorLength'] / 1000 );
return height.toFixed(2);
}
@ -30,8 +29,16 @@ export const calculateHeight = (gsd,uavModel) => {
// 输入高度计算gsd
export const calculateGsd = (height,uavModel) => {
let gsd = 0;
gsd = ( (height*1000) * uavModel['sensorLength'] ) / (uavModel['focalLength'] * uavModel['imageWidth']) / 10;
gsd = (uavModel['pixelLength'] * height) / uavModel['focalLength'] ;
// gsd = ( (height*1000) * uavModel['sensorLength'] ) / (uavModel['focalLength'] * uavModel['imageWidth']) / 10;
return gsd.toFixed(2);
}

View File

@ -25,7 +25,7 @@ export const airPointActions = {
"gimbalHeadingYawBase": "north",
"gimbalRotateMode": "absoluteAngle",
"gimbalPitchRotateEnable": 1,
"gimbalPitchRotateAngle": -65.7,
"gimbalPitchRotateAngle": 0,
"gimbalRollRotateEnable": 0,
"gimbalRollRotateAngle": 0,
"gimbalYawRotateEnable": 0,

View File

@ -0,0 +1,176 @@
// 面状航线默认template.kml数据
export const templateKml = {
"author": 17861857725,
"createTime": 1749689844431,
"updateTime": 1753241338101,
"missionConfig": {
"flyToWaylineMode": "pointToPoint",
"finishAction": "goHome",
"exitOnRCLost": "goContinue",
"executeRCLostAction": "goBack",
"takeOffSecurityHeight": 20,
"takeOffRefPoint": "",
"takeOffRefPointAGLHeight": 4.169064385,
"globalTransitionalSpeed": 15,
"globalRTHHeight": 100,
"droneInfo": {
"droneEnumValue": 100,
"droneSubEnumValue": 1
},
"autoRerouteInfo": {
"transitionalAutoRerouteMode": 1,
"missionAutoRerouteMode": 1
},
"waylineAvoidLimitAreaMode": 0,
"payloadInfo": {
"payloadEnumValue": 99,
"payloadSubEnumValue": 0,
"payloadPositionIndex": 0
}
},
"Folder": {
"templateType": "mapping2d",
"templateId": 0,
"waylineCoordinateSysParam": {
"coordinateMode": "WGS84",
"heightMode": "EGM96",
"globalShootHeight": 90,
"surfaceFollowModeEnable": 1,
"isRealtimeSurfaceFollow": 0,
"surfaceRelativeHeight": 90,
"dsmFile": "wpmz/res/dsm/wgs84_ASTGTMV003_N35E118_dem_7.tif"
},
"autoFlightSpeed": 12.7,
"Placemark": {
"caliFlightEnable": 0,
"elevationOptimizeEnable": 1,
"smartObliqueEnable": 0,
"quickOrthoMappingEnable": 0,
"facadeWaylineEnable": 0,
"isLookAtSceneSet": 0,
"shootType": "time",
"direction": 83,
"margin": 0,
"efficiencyFlightModeEnable": 0,
"overlap": {
"orthoCameraOverlapH": 80,
"orthoCameraOverlapW": 70,
"inclinedCameraOverlapH": 80,
"inclinedCameraOverlapW": 70
},
"Polygon": {
"outerBoundaryIs": {
"LinearRing": {
"coordinates": "118.293794766158,35.1353688096117,0\n 118.295429169407,35.1353304409052,0\n 118.295487507293,35.1332925168381,0\n 118.293672196844,35.1331918267775,0"
}
}
},
"ellipsoidHeight": 90,
"height": 90
},
"payloadParam": {
"payloadPositionIndex": 0,
"focusMode": "firstPoint",
"meteringMode": "average",
"returnMode": "singleReturnStrongest",
"samplingRate": 240000,
"scanningMode": "repetitive",
"imageFormat": "visable",
"photoSize": ""
}
}
}
// 面状航线默认wayline.wpml数据
export const waylineWpml = {
"missionConfig": {
"flyToWaylineMode": "pointToPoint",
"finishAction": "goHome",
"exitOnRCLost": "goContinue",
"executeRCLostAction": "goBack",
"takeOffSecurityHeight": 20,
"globalTransitionalSpeed": 15,
"globalRTHHeight": 100,
"droneInfo": {
"droneEnumValue": 100,
"droneSubEnumValue": 1
},
"autoRerouteInfo": {
"transitionalAutoRerouteMode": 1,
"missionAutoRerouteMode": 1
},
"waylineAvoidLimitAreaMode": 0,
"payloadInfo": {
"payloadEnumValue": 99,
"payloadSubEnumValue": 0,
"payloadPositionIndex": 0
}
},
"Folder": {
"templateId": 0,
"executeHeightMode": "WGS84",
"waylineId": 0,
"distance": 1259.17163085938,
"duration": 145.652896165848,
"autoFlightSpeed": 12.7,
"startActionGroup": {
"action": [
{
"actionId": 0,
"actionActuatorFunc": "gimbalRotate",
"actionActuatorFuncParam": {
"gimbalHeadingYawBase": "aircraft",
"gimbalRotateMode": "absoluteAngle",
"gimbalPitchRotateEnable": 1,
"gimbalPitchRotateAngle": -90,
"gimbalRollRotateEnable": 0,
"gimbalRollRotateAngle": 0,
"gimbalYawRotateEnable": 1,
"gimbalYawRotateAngle": 0,
"gimbalRotateTimeEnable": 0,
"gimbalRotateTime": 10,
"payloadPositionIndex": 0
}
},
{
"actionId": 1,
"actionActuatorFunc": "hover",
"actionActuatorFuncParam": {
"hoverTime": 0.5
}
},
{
"actionId": 2,
"actionActuatorFunc": "setFocusType",
"actionActuatorFuncParam": {
"cameraFocusType": "manual",
"payloadPositionIndex": 0
}
},
{
"actionId": 3,
"actionActuatorFunc": "focus",
"actionActuatorFuncParam": {
"focusX": 0,
"focusY": 0,
"focusRegionWidth": 0,
"focusRegionHeight": 0,
"isPointFocus": 0,
"isInfiniteFocus": 1,
"payloadPositionIndex": 0,
"isCalibrationFocus": 0
}
},
{
"actionId": 4,
"actionActuatorFunc": "hover",
"actionActuatorFuncParam": {
"hoverTime": 1
}
}
]
},
"Placemark": []
}
}

View File

@ -97,6 +97,18 @@ export const missionConfigOptions = {
label:"悬停",
value:"hover"
}
],
globalWaypointTurnMode:[
{
label:"不过点,提前转弯",
value:"coordinateTurn"
},{
label:"直线飞行,到点停",
value:"toPointAndStopWithDiscontinuityCurvature"
},{
label:"平滑过点,提前转弯",
value:"toPointAndStopWithContinuityCurvature"
}
]
}

View File

@ -0,0 +1,92 @@
// 航点航线默认template.kml数据
export const templateKml = {
"author": 17861857725,
"createTime": 1753927747220,
"updateTime": 1753928575789,
"missionConfig": {
"flyToWaylineMode": "safely",
"finishAction": "goHome",
"exitOnRCLost": "goContinue",
"executeRCLostAction": "goBack",
"takeOffSecurityHeight": 20,
"takeOffRefPoint": "35.134614,118.296656,77.867669",
"takeOffRefPointAGLHeight": 0,
"globalTransitionalSpeed": 15,
"globalRTHHeight": 100,
"droneInfo": {
"droneEnumValue": 100,
"droneSubEnumValue": 1
},
"waylineAvoidLimitAreaMode": 0,
"payloadInfo": {
"payloadEnumValue": 99,
"payloadSubEnumValue": 2,
"payloadPositionIndex": 0
}
},
"Folder": {
"templateType": "waypoint",
"templateId": 0,
"waylineCoordinateSysParam": {
"coordinateMode": "WGS84",
"heightMode": "EGM96"
},
"autoFlightSpeed": 10,
"globalHeight": 124,
"caliFlightEnable": 0,
"gimbalPitchMode": "manual",
"globalWaypointHeadingParam": {
"waypointHeadingMode": "followWayline",
"waypointHeadingAngle": 0,
"waypointPoiPoint": "0.000000,0.000000,0.000000",
"waypointHeadingPathMode": "followBadArc",
"waypointHeadingPoiIndex": 0
},
"globalWaypointTurnMode": "toPointAndStopWithDiscontinuityCurvature",
"globalUseStraightLine": 1,
"Placemark": [],
"payloadParam": {
"payloadPositionIndex": 0,
"focusMode": "firstPoint",
"meteringMode": "average",
"returnMode": "singleReturnStrongest",
"samplingRate": 240000,
"scanningMode": "repetitive",
"imageFormat": "visable,ir",
"photoSize": ""
}
}
}
// 航点航线默认wayline.wpml数据
export const waylineWpml = {
"missionConfig": {
"flyToWaylineMode": "safely",
"finishAction": "goHome",
"exitOnRCLost": "goContinue",
"executeRCLostAction": "goBack",
"takeOffSecurityHeight": 20,
"globalTransitionalSpeed": 15,
"globalRTHHeight": 100,
"droneInfo": {
"droneEnumValue": 100,
"droneSubEnumValue": 1
},
"waylineAvoidLimitAreaMode": 0,
"payloadInfo": {
"payloadEnumValue": 99,
"payloadSubEnumValue": 2,
"payloadPositionIndex": 0
}
},
"Folder": {
"templateId": 0,
"executeHeightMode": "WGS84",
"waylineId": 0,
"distance": 4453.73876953125,
"duration": 636.023902893066,
"autoFlightSpeed": 10,
"Placemark": []
}
}