Vue3实战Easy云盘(三):文件删除+文件移动+目录导航+上传优化/文件过滤/搜索

本文主要是介绍Vue3实战Easy云盘(三):文件删除+文件移动+目录导航+上传优化/文件过滤/搜索,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、文件删除

(1)选中了之后才可以删除,没有选中时就显示暗调删除按钮
(2)实现选中高亮功能
(3)单个删除
(4)批量删除

Main.vue中 

                <!-- 按钮3 --><!-- 如果selectFileIdList数组的长度为0(即数组为空),则HTML元素的disabled属性将被设置为true,从而使该元素变为禁用状态 --><el-button type="danger" @click="delFileBatch" :disabled="selectFileIdList.length == 0"><span class="iconfont icon-del"></span>批量删除</el-button>.....<span class="iconfont icon-del" @click="delFile(row)">删除
</span>......
// 定义多选文件夹列表
const selectFileIdList = ref([]);
// 多选
const rowSelected = (rows) => {// 重置为一个空数组,清空数据selectFileIdList.value = [];// 遍历 rows 数组中的每一个元素rows.forEach((item) => {// 经遍历到的fileId添加到selectFileIdList中selectFileIdList.value.push(item.fileId);});
};
// 删除单个文件
const delFile = (row) => {// 调用Confirmproxy.Confirm(`你确定要删除【$row.fileName】吗?删除的文件可在 10 天内通过回收站还原`,async () => {let result = await proxy.Request({url: api.delFile,params: {fileIds: row.fileId,},});if (!result) {return;}// 重新获取数据loadDataList();});
};
// 批量删除文件
const delFileBatch = () => {// 如果要删除的长度为0就不执行if (selectFileIdList.value.length == 0) {return;}// 调用Confirmproxy.Confirm(// 第一个参数是一个字符串,用于显示给用户的确认消息`你确定要删除这些文件吗?删除的文件可在 10 天内通过回收站还原`,// 第二个参数,当用户点击确认按钮后,这个异步函数会被执行async () => {// 使用await关键字调用proxy.Request方法,发送一个HTTP请求到服务器// 接收proxy.Request方法的响应并将其存储在result变量中let result = await proxy.Request({// 请求的URL来自api.delFile,这可能是一个常量或配置对象中的属性,指向删除文件的API端点。url: api.delFile,// join将这个数组转换为一个由逗号分隔的字符串(因为批量删除多个ids所以要分开),作为查询参数发送的文件ID列表params: {fileIds: selectFileIdList.value.join(","),},});// 处理响应,if (!result) {return;}// 重新获取数据loadDataList();});
};

 二、文件移动

(1)定义全局组件,FolderSelect.vue

(因为文件移动到哪个文件夹,文件保存到哪个文件夹都要用到,所以封装一个全局组件)

别忘记在Main.js中引入

src/components/FolderSelect.vue(不全面,未添加导航)

<!-- 移动/保存到哪个文件夹组件 -->
<template><div><!-- 弹出的对话框组件Dialog --><Dialog:show="dialogConfig.show":title="dialogConfig.title":buttons="dialogConfig.buttons"width="600px":showCancel="true"@close="dialogConfig.show = false"><!-- 目录导航 --><div class="navigation-panel"></div><!-- 文件夹列表 --><div class="folder-list" v-if="folderList.length > 0"><!-- 每一项文件夹 --><divclass="folder-item"v-for="item in folderList"@click="selectFolder(item)"><!-- 文件类型为0时即文件夹就显示文件夹的图标 --><Icon :fileType="0"></Icon><!-- 每个文件夹的名字 --><span class="file-name">{{ item.fileName }}</span></div></div><!-- 判断 --><div v-else class="tips">移动到 <span>{{ currentFolder.fileName }}</span> 文件夹</div></Dialog></div>
</template><script setup>
import { ref, reactive, getCurrentInstance } from "vue";
import { useRouter, useRoute } from "vue-router";const { proxy } = getCurrentInstance();
const router = useRouter();
const route = useRoute();const api = {loadAllFolder: "/file/loadAllFolder",
};// 定义弹出框的属性
const dialogConfig = ref({show: false,// 对话框的标题title: "移动到",buttons: [{type: "primary",text: "移动到此",// 按钮上的文字// 当按钮被点击时触发的回调函数。在这个例子中,点击按钮会调用 folderSelect() 函数。click: (e) => {folderSelect();},},],
});// 目录列表
const folderList = ref([]);
// 父级ID
const filePid = ref("0");
// 当前目录ID
const currentFileIds = ref([]);
// 当前文件夹
const currentFolder = ref({});// 获取所有的目录文件夹列表
const loadAllFolder = async () => {// API请求// 使用 await 等待 proxy.Request 方法的执行结果存储到result里let result = await proxy.Request({// 指定请求的urlurl: api.loadAllFolder,// 传递父id和当前文件夹idparams: {filePid: filePid.value,currentFileIds: currentFileIds.value,},});// 判断结果if (!result) {return;}folderList.value = result.data;
};// 展示弹出框对外的方法
const showFolderDialog = (currentFolder) => {dialogConfig.value.show = true;// 更新当前文件id数组currentFileIds.value = currentFolder;// 在加载一次获取到的目录文件夹列表loadAllFolder();
};// 关闭弹出框
const close = () => {dialogConfig.value.show = false;
};
// 向外暴露这两个函数,使得父组件Main可以调用这两个函数
defineExpose({ showFolderDialog, close });// 选择目录(目录导航)
const selectFolder = (data) => {navigationRef.value.openFolder(data);
};// 确定选择要移动到的目录
// 将选定的文件目录参数传递给父组件 Main 中的 folderSelect 函数
const emit = defineEmits(["folderSelect"]);
// 此方法回调在父组件中
const folderSelect = () => {emit("folderSelect", filePid.value);
};</script><style lang="scss" scoped>
.navigation-panel {padding-left: 10px;background: #f1f1f1;
}.folder-list {.folder-item {cursor: pointer;display: flex;align-items: center;padding: 10px;.file-name {display: inline-block;margin-left: 10px;}&:hover {background: #f8f8f8;}}max-height: calc(100vh - 200px);min-height: 200px;
}.tips {text-align: center;line-height: 200px;span {color: #06a7ff;}
}</style>

(2)Main.vue中调用

(不全面,未添加导航栏)

<!-- 引入组件 --><FolderSelectref="folderSelectRef"@folderSelect="moveFolderDone"></FolderSelect>
// 移动目录
const folderSelectRef = ref();
// 当前要移动的文件(单个文件)
const currentMoveFile = ref({});
// 移动单个文件
const moveFolder = (data) => {// 存储当前要移动的单个文件的信息currentMoveFile.value = data;// 把当前文件id给showFolderDialog方法folderSelectRef.value.showFolderDialog(currentFolder.value.fileId);
};
// 移动批量文件
const moveFolderBatch = () => {// 清空当前要移动的文件数据currentMoveFile.value = {};// 把当前的文件夹id给showFolderDialogfolderSelectRef.value.showFolderDialog(currentFolder.value.fileId);
};// 点击按钮之后,移动文件操作
const moveFolderDone = async (folderId) => {// 如果要移动到当前目录,提醒无需移动if (// 如果当前移动的文件父级id等于此时要移动到的文件夹id或者当前文件夹的id等于要移动到的文件夹idcurrentMoveFile.value.filePid == folderId ||currentFolder.value.fileId == folderId) {// 就提示无需移动proxy.Message.warning("文件正在当前目录,无需移动");return;}// 定义一个数组存放要移动的文件或者文件夹信息let fileIdsArray = [];// 如果是单个文件移动if (currentMoveFile.value.fileId) {// 就把当前移动的文件id传给这个数组fileIdsArray.push(currentMoveFile.value.fileId);} else {// 如果是多个文件移动// concat 连接多个数组// selectFileIdList 是指批量选择时选择的文件IDfiledIdsArray = filedIdsArray.concat(selectFileIdList.value);}// 发请求并将结果存储let result = await proxy.Request({// 请求的urlurl: api.changeFileFolder,// 携带的参数params: {// 将 fileIdsArray 数组中的所有元素转换为一个由逗号分隔的字符串,赋值给fileIdsfileIds: fileIdsArray.join(","),// 把folderId传到父文件id里面filePid: folderId,},});if (!result) {return;}// 调用子组件暴露的close方法,实现当前弹出框页面的关闭folderSelectRef.value.close();// 更新当前文件列表loadDataList();
};

三、目录导航(难点)

(1)导航栏组件(全局)

src/components/Navigation.vue
template结构

js
1.设置点击目录事件 openFolder:
2.暴露此事件供父组件使用:defineExpose({ openFolder });
3.设置当前路径 setpath:当点击后目录改变,路径也随之改变
4.获取当前路径的目录 getNavigationFolder
5.doCallback
6.监听路由
7.初始化设置init
8.setCurrentFolder 设置当前目录,点击导航跳转到所点击的目录
9.返回上一级 backParent

src/components/Navigation.vue

<template><!-- 导航 --><div class="top-navigation"><!-- 返回上一级 --><template v-if="folderList.length > 0"><span class="back link" @click="backParent">返回上一级</span><!-- 竖线 --><el-divider direction="vertical" /></template><!-- 全部文件:外面粗体的不能点 --><span v-if="folderList.length == 0" class="all-file">全部文件</span><!-- 全部文件:能点的 --><spanclass="link"v-if="folderList.length > 0"@click="setCurrentFolder(-1)">全部文件</span><!-- 遍历文件列表 --><template v-for="(item, index) in folderList"><!-- 图标 --><span class="iconfont icon-right"></span><!-- 文件名字可以点击 --><spanclass="link"v-if="index < folderList.length - 1"@click="setCurrentFolder(index)">{{ item.fileName }}</span><!-- 文件名字不可以点击 --><span class="text" v-if="index == folderList.length - 1">{{item.fileName}}</span></template></div>
</template><script setup>
import { ref, reactive, getCurrentInstance, watch } from "vue";
import { useRouter, useRoute } from "vue-router";const { proxy } = getCurrentInstance();
const router = useRouter();
const route = useRoute();// 定义父组件Main.vue传递过来的值
const props = defineProps({// 默认开启路由监听watchPath: {// 是否监听路由变化type: Boolean,default: true,},shareId: {type: String,},adminShow: {type: Boolean,default: false,},
});const api = {// 首页 获取当前目录 获取列表 参数(path:完整路径)getFolderInfo: "/file/getFolderInfo",// 外部分享 获取目录信息 参数(shareId:分享id / path:完整路径)getFolderInfo4Share: "/showShare/getFolderInfo",// 管理员 获取当前目录 参数(path:完整路径)getFolderInfo4Admin: "/admin/getFolderInfo",
};// 分类
const category = ref();
// 目录的集合
const folderList = ref([]);
// 当前目录
const currentFolder = ref({ fileId: "0" });// 初始化
const init = () => {// 初始目录集合 设置为一个空数组folderList.value = [];// 初始当前目录设置currentFolder.value = { fileId: "0" };// 调用doCallback();
};// 点击目录openFolder
// 父组件 Main/FolderSelect 中调用该方法,实现目录(文件及)的预览
const openFolder = (data) => {// 把data赋值给文件id和文件nameconst { fileId, fileName } = data;// 定义folderconst folder = {fileName: fileName,fileId: fileId,};// 把folder push进目录集合folderList.value.push(folder);// 更新当前目录currentFolder.value = folder;// 设置当前路径,当点击后目录改变,路径也随之改变,调用setPathsetPath();
};
defineExpose({ openFolder });// 返回上一级
const backParent = () => {// 查找当前文件夹的索引let currentIndex = null;for (let i = 0; i < folderList.value.length; i++) {if (folderList.value[i].fileId == currentFolder.value.fileId) {currentIndex = i;break;}}// 设置当前文件夹为上一级目录setCurrentFolder(currentIndex - 1);
};// 点击导航 设置当前目录(点击目录,跳转到所点击的目录)
const setCurrentFolder = (index) => {// 如果点的是全部文件if (index == -1) {// 初始化数组currentFolder.value = { fileId: "0" };folderList.value = [];} else {// 当前目录的值更新为目录集合数组为index的值currentFolder.value = folderList.value[index];// 删除从index+1开始的长度为目录集合数组的长度 的数组// 对于 splice(start, deleteCount, ...items) 方法://  start(必需):开始更改数组的位置的索引。// deleteCount(必需):要删除的元素数量。如果设置为 0,则不会删除任何元素。// ...items(可选):要添加到数组中的新元素folderList.value.splice(index + 1, folderList.value.length);}setPath();
};// 设置当前路径,当点击后目录改变,路径也随之改变
const setPath = () => {if (!props.watchPath) {// 设置不监听路由回调方法doCallback();return;}// 定义路径数组let pathArray = [];// 遍历目录集合的每一项folderList.value.forEach((item) => {// 把每一项的fileId push 到 路径数组里pathArray.push(item.fileId);});// 设置路由router.push({// 当前路径pathpath: route.path,// 参数query:// 如果pathArray长度为0,参数就为空,否则就把路径用/隔开加入到pathArray里面,更新path,添加到参数里面pathArray.length == 0? "": {path: pathArray.join("/"),},});
};// 获取当前路径的目录
const getNavigationFolder = async (path) => {// 根据给定的 path 和一些其他属性(如 props.shareId 和 props.adminShow)来确定请求哪个 API,然后发送一个请求来获取该路径下的目录信息,并将获取到的目录信息存储在 folderList.value 中。let url = api.getFolderInfo;if (props.shareId) {url = api.getFolderInfo4Share;}if (props.adminShow) {url = api.getFolderInfo4Admin;}let result = await proxy.Request({url: url,showLoading: false,params: {path: path,shareId: props.shareId,},});if (!result) {return;}folderList.value = result.data;
};// 回调 将当前的参数传递给父组件 Main
// 定义了一个名为 navChange 的事件。这允许子组件在需要时触发这个事件,并传递一些数据给父组件。
const emit = defineEmits(["navChange"]);
const doCallback = () => {emit("navChange", {categoryId: category.value,curFolder: currentFolder.value,});
};// 监听路由
watch(() => route,// 它会在route的值变化时被调用。// newVal是route的新值,oldVal是route的旧值(newVal, oldVal) => {if (!props.watchPath) {return;}if (// 如果不在main路径里面就不用管newVal.path.indexOf("/main") === -1 &&newVal.path.indexOf("/settings/fileList") === -1 &&newVal.path.indexOf("/share") === -1) {return;}// 把新携带的路径赋值给path,可以拿到?后面的一坨,query参数是?后面的一截const path = newVal.query.path;// params是route路由,category是在router里面定义的const categoryId = newVal.params.category;category.value = categoryId;if (path == undefined) {// 调用init();} else {// 调用getNavigationFolder(path);// 刷新的时候要把当前目录设置进来// 使用split("/")方法将path字符串分割为一个数组pathArraylet pathArray = path.split("/");// fileId被赋值给currentFolder.valuecurrentFolder.value = {// 它使用数组的最后一个元素(即path中的最后一个部分)作为fileIdfileId: pathArray[pathArray.length - 1],};doCallback();}},{ immediate: true, deep: true }
);
</script><style lang="scss" scoped>
.top-navigation {font-size: 13px;display: flex;align-items: center;line-height: 40px;.all-file {font-weight: bold;}.link {color: #06a7ff;cursor: pointer;}.icon-right {color: #06a7ff;padding: 0px 5px;font-size: 13px;}
}
</style>

(2)main.js中引入

import Navigation from '@/components/Navigation.vue';
app.component('Navigation', Navigation);

(3)Main.vue中使用组件

<!-- 导航 -->
<Navigation ref="navigationRef" @navChange="navChange"></Navigation>

Main.vue中绑定点击事件
 

<span @click="preview(row)">{{ row.fileName }}</span>
<span @click="preview(row)">{{ row.fileName }}</span>

preview回调,预览

// 预览
const previewRef = ref();
const preview = (data) => {// 如果是文件夹if (data.folderType == 1) {// 就调用Navigation组件中的openFolder方法,实现预览navigationRef.value.openFolder(data);return;}if (data.status != 2) {proxy.Message.warning("文件未完成转码,无法预览");return;}previewRef.value.showPreview(data, 0);
};

navChange回调

// 目录,展示目录
const navChange = (data) => {// 从传入的 data 对象中解构出 curFolder 和 categoryId 两个属性,并将它们的值分别赋给新定义的常量 curFolder 和 categoryIdconst { curFolder, categoryId } = data;// 将当前文件夹的值更新为传过来的文件夹currentFolder.value = curFolder;// 展示showLoading.value = true;// 更新categorycategory.value = categoryId;loadDataList();
};

无文件上传时,Main.vue展示

<!-- 判断没有文件时 --><div class="no-data" v-else><div class="no-data-inner"><!-- 图片 --><Icon iconName="no_data" :width="120" fit="fill"></Icon><!-- 文字提示 --><div class="tips">当前目录为空,上传你的第一个文件吧</div><div class="op-list"><!-- 上传 --><el-upload:show-file-list="false":with-credentials="true":multiple="true":http-request="addFile":accept="fileAccept"><div class="op-item"><Icon iconName="file" :width="60"></Icon><div>上传文件</div></div></el-upload><!-- 新建目录 --><div class="op-item" v-if="category == 'all'" @click="newFolder"><Icon iconName="folder" :width="60"></Icon><div>新建目录</div></div></div></div></div>

四、 上传优化(列表自动刷新)

(1)上传回调 

Main.vue中

// 添加文件回调
const reload = () => {showLoading.value = false;// 刷新列表loadDataList();
};
defineExpose({ reload });

Framework.vue中

<component @addFile="addFile" ref="routerViewRef" :is="Component"></component>
// 上传文件回调
// 上传文件后的刷新列表(调用Uploader子组件中的函数)
const routerViewRef = ref();
const uploadCallbackHandler = () => {nextTick(() => {// 它首先等待DOM更新完成(通过nextTick)// 然后重新加载一个组件(可能是router-view)routerViewRef.value.reload();// 并最后调用一个函数来获取空间使用情况。getUseSpace();});
};

五、文件选择(过滤)

组件:分类文件类型
src/js/CategoryInfo.js

export default {"all": {accept: "*"},"video": {accept: ".mp4,.avi,.rmvb,.mkv,.mov"},"music": {accept: ".mp3,.wav,.wma,.mp2,.flac,.midi,.ra,.ape,.aac,.cda"},"image": {accept: ".jpeg,.jpg,.png,.gif,.bmp,.dds,.psd,.pdt,.webp,.xmp,.svg,.tiff"},"doc": {accept: ".pdf,.doc,.docx,.xls,.xlsx,.txt"},"others": {accept: "*"},
}

Main.vue中上传按钮

:accept="fileAccept"// 实现文件选择
const fileAccept = computed(() => {const categoryItem = CategoryInfo[category.value];return categoryItem ? categoryItem.accept : "*";
});

六、搜索功能实现

Main.vue中搜索输入框

 <!-- 搜索输入框 --><div class="search-panel"><el-inputclearableplaceholder="请输入文件名搜索"v-model="fileNameFuzzy"@keyup.enter="search"><template #suffix><i class="iconfont icon-search" @click="search"></i></template></el-input></div><!-- 搜索图标 --><div class="iconfont icon-refresh" @click="loadDataList"></div>

回调 

// 搜索功能
const search = () => {showLoading.value = true;loadDataList();
};

七、文件移动的目录导航

FolderSelect.vue中

<!-- 目录导航 --><div class="navigation-panel"><Navigationref="navigationRef"@navChange="navChange":watchPath="false"></Navigation></div>
// 绑定导航栏
const navigationRef = ref();
// 调用Navigation子组件中的navChange,使得参数传递给该组件
const navChange = (data) => {const { curFolder } = data;currentFolder.value = curFolder;filePid.value = curFolder.fileId;loadAllFolder();
};
// 选择目录(目录导航)
const selectFolder = (data) => {navigationRef.value.openFolder(data);
};

这篇关于Vue3实战Easy云盘(三):文件删除+文件移动+目录导航+上传优化/文件过滤/搜索的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/988419

相关文章

uniapp接入微信小程序原生代码配置方案(优化版)

uniapp项目需要把微信小程序原生语法的功能代码嵌套过来,无需把原生代码转换为uniapp,可以配置拷贝的方式集成过来 1、拷贝代码包到src目录 2、vue.config.js中配置原生代码包直接拷贝到编译目录中 3、pages.json中配置分包目录,原生入口组件的路径 4、manifest.json中配置分包,使用原生组件 5、需要把原生代码包里的页面修改成组件的方

电脑不小心删除的文件怎么恢复?4个必备恢复方法!

“刚刚在对电脑里的某些垃圾文件进行清理时,我一不小心误删了比较重要的数据。这些误删的数据还有机会恢复吗?希望大家帮帮我,非常感谢!” 在这个数字化飞速发展的时代,电脑早已成为我们日常生活和工作中不可或缺的一部分。然而,就像生活中的小插曲一样,有时我们可能会在不经意间犯下一些小错误,比如不小心删除了重要的文件。 当那份文件消失在眼前,仿佛被时间吞噬,我们不禁会心生焦虑。但别担心,就像每个问题

vue, 左右布局宽,可拖动改变

1:建立一个draggableMixin.js  混入的方式使用 2:代码如下draggableMixin.js  export default {data() {return {leftWidth: 330,isDragging: false,startX: 0,startWidth: 0,};},methods: {startDragging(e) {this.isDragging = tr

vue项目集成CanvasEditor实现Word在线编辑器

CanvasEditor实现Word在线编辑器 官网文档:https://hufe.club/canvas-editor-docs/guide/schema.html 源码地址:https://github.com/Hufe921/canvas-editor 前提声明: 由于CanvasEditor目前不支持vue、react 等框架开箱即用版,所以需要我们去Git下载源码,拿到其中两个主

React+TS前台项目实战(十七)-- 全局常用组件Dropdown封装

文章目录 前言Dropdown组件1. 功能分析2. 代码+详细注释3. 使用方式4. 效果展示 总结 前言 今天这篇主要讲全局Dropdown组件封装,可根据UI设计师要求自定义修改。 Dropdown组件 1. 功能分析 (1)通过position属性,可以控制下拉选项的位置 (2)通过传入width属性, 可以自定义下拉选项的宽度 (3)通过传入classN

js+css二级导航

效果 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Con

基于Springboot + vue 的抗疫物质管理系统的设计与实现

目录 📚 前言 📑摘要 📑系统流程 📚 系统架构设计 📚 数据库设计 📚 系统功能的具体实现    💬 系统登录注册 系统登录 登录界面   用户添加  💬 抗疫列表展示模块     区域信息管理 添加物资详情 抗疫物资列表展示 抗疫物资申请 抗疫物资审核 ✒️ 源码实现 💖 源码获取 😁 联系方式 📚 前言 📑博客主页:

移动硬盘盒:便携与交互的完美结合 PD 充电IC

在数字化时代的浪潮中,数据已成为我们生活中不可或缺的一部分。随着数据的不断增长,人们对于数据存储的需求也在不断增加。传统的存储设备如U盘、光盘等,虽然具有一定的便携性,但在容量和稳定性方面往往难以满足现代人的需求。而移动硬盘,以其大容量、高稳定性和可移动性,成为了数据存储的优选方案。然而,单纯的移动硬盘在携带和使用上仍存在诸多不便,于是,移动硬盘盒应运而生,以其独特的便携性和交互性,成为了数据存储

vue+el国际化-东抄西鉴组合拳

vue-i18n 国际化参考 https://blog.csdn.net/zuorishu/article/details/81708585 说得比较详细。 另外做点补充,比如这里cn下的可以以项目模块加公共模块来细分。 import zhLocale from 'element-ui/lib/locale/lang/zh-CN' //引入element语言包const cn = {mess

vue同页面多路由懒加载-及可能存在问题的解决方式

先上图,再解释 图一是多路由页面,图二是路由文件。从图一可以看出每个router-view对应的name都不一样。从图二可以看出层路由对应的组件加载方式要跟图一中的name相对应,并且图二的路由层在跟图一对应的页面中要加上components层,多一个s结尾,里面的的方法名就是图一路由的name值,里面还可以照样用懒加载的方式。 页面上其他的路由在路由文件中也跟图二是一样的写法。 附送可能存在