无人机系统

This commit is contained in:
Teo
2025-05-14 18:31:25 +08:00
parent c92f2091d9
commit 3e67c83ced
232 changed files with 16610 additions and 7 deletions

View File

@ -0,0 +1,609 @@
<template>
<div class="medium-home-container">
<div class="medium-home-main">
<div class="medium-home-main-top">
<div class="medium-breadcrumb">
<div v-for="item in paths" :key="item" class="breadcrumb-item" @click="changePath(item)">
{{ item || '全部文件' }}
</div>
</div>
</div>
<div class="medium-home-top-btn">
<div>
<el-button size="small" @click="handleDelete(false)" type="danger"> 删除 </el-button>
<el-button size="small" @click="handleDownload(false)" type="success"> 下载 </el-button>
</div>
<div class="medium-check">
<div class="check-item">已选/全部</div>
<div class="check-item">{{ selectNum }}/{{ totle }}</div>
</div>
</div>
<div class="medium-home-main-table">
<el-table
ref="multipleTable"
:data="fileList"
tooltip-effect="dark"
stripe
size="small"
height="600"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55"> </el-table-column>
<el-table-column prop="fileName" label="文件名称">
<template slot-scope="scope">
<div
class="file-name-wrapper"
style="display: flex; align-items: center; cursor: pointer"
@click="handleSelectFile(scope.row.path, scope.row)"
>
<img :src="scope.row.img" alt="" style="width: 16px; height: 16px; transform: translateY(-1px); margin-right: 2px" />
<div class="file-name">{{ scope.row.name ? scope.row.name : scope.row.fileName }}</div>
</div>
</template>
</el-table-column>
<el-table-column prop="size" label="大小" width="80" show-overflow-tooltip> </el-table-column>
<el-table-column prop="creatTime" label="创建时间">
<template slot-scope="scope">{{ scope.row.creatTime }}</template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="120">
<template slot-scope="scope">
<div class="opration-btns">
<el-tooltip v-if="scope.row.size !== 'N/A'" class="item" content="下载" effect="dark">
<!-- v-if="scope.row.size !== 'N/A'" -->
<span class="opration-btn" style="display: inline-block; cursor: pointer" @click="handleDownload(scope.row)">
<img :src="require('../../images/medium/down-load.png')" srcset="" />
</span>
</el-tooltip>
<el-tooltip v-if="scope.row.size !== 'N/A'" class="item" content="预览" effect="dark">
<span class="opration-btn" style="display: inline-block; cursor: pointer" @click="handlePreview(scope.row)">
<img :src="require('../../images/medium/prview-icon.png')" srcset="" />
</span>
</el-tooltip>
<el-tooltip class="item" content="删除" effect="dark">
<span class="opration-btn" style="display: inline-block; cursor: pointer" @click="handleDelete(scope.row)">
<img :src="require('../../images/medium/delete.png')" srcset="" />
</span>
</el-tooltip>
</div>
</template>
</el-table-column>
</el-table>
</div>
</div>
<div class="shade" v-if="loading">
<el-progress type="circle" :percentage="percentage"></el-progress>
<!-- <div
style="white-space: pre-line;text-align: center;color: aqua;color: aqua; display: flex; flex-direction: column;">
<img src="../../images/loadingdow.gif" alt="">
<span style="display: inline-block;position: absolute;left: 50%;top:50%;transform: translate(-50%, -50%);">{{
this.text }}</span>
</div> -->
</div>
</div>
</template>
<script>
import axios from 'axios';
import JSZip from 'jszip';
import { getAiFiles, deleteAiFile, getAiUrlList } from '@/api/fileMangement';
import Preview from './preview.vue';
import { useAirStore } from '@/store/modules/drone';
export default {
components: { Preview },
data() {
return {
percentage: 0,
info: {},
paths: [''],
fileList: [
// {
// fileName: item,
// size: list[item].size,
// creatTime: list[item].creation_time,
// path:''
// img: ''
// },
],
totle: 0,
selectNum: 0,
length: 0,
sn: '',
currentPath: '',
searchName: '', // 搜索关键字
dateTime: [new Date(), new Date()], // 初始时间范围为今日至今日
typeOptions: [
{
value: '选项1',
label: '选项1'
},
{
value: '选项2',
label: '选项2'
}
],
typeValue: '',
loadOptions: [
{
value: '选项1',
label: '选项1'
},
{
value: '选项2',
label: '选项2'
}
],
loadValue: '',
selectList: [],
loading: false,
gateWay: useAirStore().gateWay,
text: ''
};
},
watch: {
// 监听 Vuex 中的状态变化
'useAirStore().gateWay': {
handler(newValue, oldVal) {
this.gateWay = newValue;
this.getFileList(this.gateWay.gateway);
},
deep: true
}
},
methods: {
handleSelectionChange(datas) {
// console.log("datas: ", datas);
this.selectList = datas;
this.selectNum = datas.length;
},
isEmptyObject(obj) {
return Object.keys(obj).length === 0;
},
// 获取文件列表
async getFileList(gateway, path = '') {
this.fileList = [];
const list = await getAiFiles({ gateway, itemPath: path });
// console.log("文件列表1-------", list);
if (Object.keys(list).length === 0) {
return;
}
this.paths = [
'',
...Object.keys(list)[0]
.split('/')
.filter((path) => path !== '')
.slice(0, -1)
];
this.fileList = Object.keys(list).map((item) => {
const pathList = item.split('/').filter((path) => path !== '');
let img = require('../../images/medium/files-icon.png');
let type = 'file';
// console.log("item----------", item);
// console.log("list----------", list[item]);
// 文件缩略图
if (list[item].size !== 'N/A') {
type = item.split('.')[1];
if (item.split('.')[1] !== 'mp4') {
img = list[item].download_url;
} else {
img = require('../../images/medium/video-icon.png');
}
}
return {
name: list[item].missionName,
fileName: pathList[pathList.length - 1],
size: list[item].size,
creatTime: list[item].creation_time,
path: item,
img,
type,
url: list[item].download_url,
fileLength: list[item].length
};
});
this.totle = this.fileList.length;
this.length = this.fileList.fileLength;
// console.log("文件列表2-------", this.fileList);
},
// 文件夹点击进入文件夹
handleSelectFile(path, data) {
console.log('path11111111111', path);
if (data && data.size !== 'N/A') {
// console.log("文件------------");
} else {
this.currentPath = path;
const { gateway } = this.gateWay;
this.getFileList(gateway, path);
}
},
// 路径变化
changePath(path) {
if (path) {
// console.log("currentPath", this.currentPath);
this.currentPath = this.currentPath.split(path)[0] + path;
// console.log("path", path);
// 跳转到该路径
this.handleSelectFile(this.currentPath);
} else {
this.handleSelectFile('');
}
},
// 删除
handleDelete(row) {
// console.log("删除---------", row);
let params = { itemPaths: [row.path], gateway: this.gateWay.gateway };
if (!row) {
if (this.selectList.length === 0) {
this.$message.warning('请先选择要删除的文件');
this.loading = false;
return;
}
const pathList = this.selectList.map((item) => item.path);
params = { itemPaths: pathList, gateway: this.gateWay.gateway };
}
this.$confirm('此操作将删除该文件, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
customClass: 'customMessageBox',
confirmButtonClass: 'customConfirm',
cancelButtonClass: 'customCancel'
}).then(() => {
deleteAiFile(params).then((res) => {
this.$message.success(res.msg);
this.$sendChanel('mediumDel');
// 过滤掉删除的文件
this.fileList = this.fileList.filter((file) => {
return !params.itemPaths.includes(file.path);
});
if (this.fileList.length === 0) {
// 如果列表为空跳转到根路径
this.handleSelectFile('');
}
});
});
},
// 下载
handleDownload(row) {
this.loading = true;
// console.log("下载---------", row);
let params = { itemPath: row.path, gateway: this.gateWay.gateway };
if (!row) {
if (this.selectList.length === 0) {
this.$message.warning('请先选择要下载的文件');
this.loading = false;
return;
}
let pathList = [];
// 定义是否是文件夹
let isFile = false;
// console.log("this.selectList", this.selectList);
this.selectList.forEach((item) => {
if (item.type === 'file') {
isFile = true;
}
});
if (isFile) {
const { gateway } = this.gateWay;
// this.selectList.forEach((item) => {
// const params = { gateway, itemPath: item.path };
// getUrlList(params).then((res) => {
// pathList.push(...res);
// });
// });
// console.log("pathList1", pathList);
// // 并行下载文件并压缩为 zip
// this.downloadAndZipFiles(pathList);
const promises = this.selectList.map((item) => {
const params = { gateway, itemPath: item.path };
return getAiUrlList(params).then((res) => {
pathList.push(...res); // 将结果合并到 pathList
});
});
// 等待所有异步操作完成后,再执行下载操作
Promise.all(promises)
.then(() => {
// console.log("pathList1", pathList);
// 确保 pathList 有值后再调用下载方法
this.downloadAndZipFiles(pathList);
})
.catch((error) => {
console.error('获取文件路径列表时出错:', error);
});
} else {
pathList = this.selectList.map((item) => item.url);
// console.log("pathList2", pathList);
// 并行下载文件并压缩为 zip
this.downloadAndZipFiles(pathList);
}
} else {
this.downloadAndZipFiles([row.url], row);
}
},
async downloadAndZipFiles(fileUrls, file) {
try {
const zip = new JSZip();
const fileSizeMap = new Map();
let fileTotal = 0;
if (file) {
fileTotal = file.fileLength;
} else {
this.selectList.forEach((item) => {
fileTotal += item.fileLength;
});
}
// console.log("file-----", fileTotal);l
// 并行下载所有文件
const downloadPromises = fileUrls.map((url) =>
axios({
url: url,
method: 'get',
responseType: 'blob',
onDownloadProgress: (progressEvent) => {
const { loaded, total } = progressEvent;
fileSizeMap.set(url, loaded);
const totalFileSize = [...fileSizeMap.values()].reduce((acc, value) => acc + value, 0);
const progress = Math.round((totalFileSize / fileTotal) * 100); // 计算下载进度百分比
// console.log("progressEvent", progressEvent);
// console.log("overallProgress", progress);
// this.percentage = progress; // 更新进度百分比
if (progress == 100) {
this.text = `${99}%\n 正在打包中`;
} else {
this.text = `${progress}%\n 下载中`;
}
}
}).then((response) => {
console.log('Response:', response); // 在这里打印 response 对象
console.log('url:', url); // 在这里打印 response 对象
console.log('flie:', file); // 在这里打印 response 对象
const regex = /\/(DJI_[^\/]+\/DJI_[^\/]+\.[^\/?]+)(?=\?)/;
// 提取匹配的部分
const match = url.match(regex);
let extractedString = response.data.size + '.jpg';
if (match) {
extractedString = match[1]; // 捕获的字符串
}
// 打印文件名和文件数据
const fileName = file ? file.fileName : extractedString; // 获取文件名
const fileData = response.data; // 保存文件数据
// 获取文件大小:使用 Blob 的 size 属性
const fileSize = fileData.size; // 单位为字节bytes
// console.log("File size:", fileSize);
// 返回一个对象,继续后续操作
return { fileName, fileData };
})
);
// 等待所有文件下载完成
const downloadedFiles = await Promise.all(downloadPromises);
if (file) {
// console.log("Downloading", downloadedFiles);
const url = window.URL.createObjectURL(downloadedFiles[0].fileData);
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', downloadedFiles[0].fileName); // 设置下载的文件名
document.body.appendChild(link);
link.click();
link.remove();
this.loading = false;
this.percentage = 0; // 下载完进度百分比清零
return;
}
// 将每个文件添加到 zip 中
downloadedFiles.forEach(({ fileName, fileData }) => {
// console.log("File Name:", fileName);
zip.file(fileName, fileData); // 添加文件到 zip
});
// 生成 zip 文件并触发下载
const zipBlob = await zip.generateAsync({ type: 'blob' });
const url = window.URL.createObjectURL(zipBlob);
const link = document.createElement('a');
link.href = url;
const temp = new Date().getTime();
link.setAttribute('download', `文件${temp}.zip`); // 设置下载的文件名
document.body.appendChild(link);
link.click();
link.remove();
this.loading = false;
this.percentage = 0; // 下载完进度百分比清零
} catch (error) {
// console.error("Error downloading or zipping files:", error);
this.loading = false;
this.percentage = 0; // 下载完进度百分比清零
}
},
// 预览
handlePreview(row) {
const list = this.fileList;
// console.log("预览---------", row);
// console.log("list---------", list);
const index = list.findIndex((item) => item === row);
const data = { list, detail: row, index };
this.$sendChanel('previewBus', data);
}
},
created() {},
mounted() {
this.getFileList(this.gateWay.gateway);
}
};
</script>
<style lang="scss">
.medium-home-container {
height: 100%;
width: 100%;
position: relative;
background-color: antiquewhite;
.medium-home-search-wrapper {
width: 100%;
height: 60px;
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
> .el-date-editor {
width: 38%;
}
> .el-select {
width: 18%;
}
> .el-input {
width: 18%;
}
> .el-button {
width: 32px;
height: 32px;
display: flex;
justify-content: center;
align-items: center;
img {
width: 20px;
height: 20px;
}
}
}
.medium-home-main {
height: calc(100%);
background: #fff;
.medium-home-main-top {
display: flex;
justify-content: space-between;
line-height: 40px;
padding: 0 16px;
font-size: 12px;
width: 100%;
.medium-breadcrumb {
display: flex;
.breadcrumb-item {
// margin-right: 20px;
color: #909399;
cursor: pointer;
&::after {
content: '/';
display: inline-block;
margin: 0 3px;
}
}
.breadcrumb-item:last-child {
color: #000;
&::after {
content: '';
display: inline-block;
}
}
}
.file-name-wrapper {
display: flex;
.file-img-wrapper {
height: 20px !important;
width: 20px !important;
img {
width: 100%;
height: 100%;
}
}
}
.el-table {
.el-checkbox {
margin-left: 8px !important;
}
}
}
.medium-home-top-btn {
width: 100%;
height: 40px;
padding: 0 16px;
display: flex;
align-items: center;
font-size: 12px;
justify-content: space-between;
.medium-check {
display: flex;
align-items: center;
color: #000;
}
}
.medium-home-main-table {
overflow-y: auto;
height: calc(100% - 80px);
width: 100%;
}
.el-checkbox__input {
margin-left: 8px !important;
}
}
.shade {
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 10;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
.el-progress__text {
color: aqua !important;
white-space: pre-line;
line-height: 20px;
}
}
.el-picker-panel {
background: rgba(255, 255, 255, 1) !important;
color: #606266 !important;
filter: drop-shadow(2px 4px 6px black);
}
.opration-btns {
display: flex;
justify-content: start;
align-items: center;
// background-color: #606266;
.opration-btn {
width: 16px;
height: 16px;
display: flex;
justify-content: center;
align-items: center;
margin-left: 10px;
cursor: pointer;
img {
width: 12px;
height: 12px;
}
&:hover {
// background: #e6e6e6;
}
}
}
}
</style>