610 lines
19 KiB
Vue
610 lines
19 KiB
Vue
<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>
|