记录一下之前写过的一个文件管理系统demo。
功能包括文件夹的新增、删除、重命名及移动,文件的上传、删除、移动及下载功能。
相关功能的操作直接和 后端 进行请求交互。
因为该demo集成在大的系统中,懒得提取建库开源,所以算是只记录思路。
运行截图
- 右键文件夹时显示操作目录
- 右键文件时显示操作目录
- 新建文件夹
- 上传文件
- 重命名文件夹
- 移动
实现代码
shareSpace.vue 为页面组件
addFolder.vue 为文件上传弹窗组件
moveFolder.vue 为移动文件/文件夹弹窗组件
// shareSpace.vue<template><div class="app-container"><el-page-header class="pageHeader":content="'当前所处:' + currentLocationName" @back="goBack"></el-page-header><el-divider></el-divider><el-row :gutter="10"class="mb8"><el-col :span="1.5" style="float: right;"><el-button plain icon="el-icon-refresh" size="mini" @click="refreshGetList">刷新</el-button></el-col><el-col :span="1.5" style="float: right;"><el-button type="primary" plain icon="el-icon-plus" size="mini" @click="addFolder">新建文件夹</el-button></el-col><el-col :span="1.5" style="float: right;"><el-button type="success" plain icon="el-icon-upload" size="mini" @click="addFile">上传文件</el-button></el-col></el-row><!-- 文件浏览区 --><div style="overflow: hidden;"><el-card class="drawing_card" v-loading="cardLoading" style="height: 60vh"><template v-if="folderList.length === 0 && filesList.length === 0"><el-empty description="暂无文件,请创建一个文件夹吧" style="height:60vh"></el-empty></template><!-- 文件夹 --><div v-for="( item, index ) in folderList "><div class="folderContainer"><div class="folderWrapper" @dblclick="doubleClickFolder(index, item)"
@contextmenu.prevent.stop="rightClickFolder(index, item, $event)"><img src="@/assets/images/folder/folder.png" style="width: 100px;height: 90px;margin-top: -13px"
@contextmenu.prevent.stop="rightClickFolder(index, item, $event)"/><div class="folderName"><span>{{
item.folderName.length >10? item.folderName.substring(0,6)+'...': item.folderName
}}</span></div></div></div></div><!-- 文件 --><div v-for="( item, index ) in filesList "><div class="folderContainer"><div class="folderWrapper" @dblclick="down(item.fileUrl)"><img src="@/assets/images/folder/fileImg.png" style="width: 100px;height: 90px;margin-top: -13px"
@contextmenu.prevent.stop="rightClickfile(index, item, $event)"/><div class="folderName"><span>{{
item.fileName.length >10? item.fileName.substring(0,6)+'...': item.fileName
}}</span></div></div></div></div></el-card></div><!-- 文件夹【右键菜单】 --><div class="add-folder-9":style="folderStyle" v-show="folderShow"><div class="add-folder-1"><div class="add-folder-2" @click="openFolder">
打开文件夹
</div><div style="border: 2px solid rgba(18,17,42,.07)"></div><div class="add-folder-2" @click="moveFolder">
移动
</div><div style="border: 2px solid rgba(18,17,42,.07)"></div><div class="add-folder-2" @click="updateFloder">
重命名
</div><div style="border: 2px solid rgba(18,17,42,.07)"></div><div class="add-folder-6" @click="deleteFolder">
删 除
</div></div></div><!-- 文件【右键菜单】 --><div class="add-folder-9":style="fileStyle" v-show="fileShow"><div class="add-folder-1"><div class="add-folder-2"><a :href="clickFilePath" download="1">下载文件</a></div><div style="border: 2px solid rgba(18,17,42,.07)"></div><!--<div class="add-folder-2" @click="updateFloder">
重命名
</div><div style="border: 2px solid rgba(18,17,42,.07)"></div>--><div class="add-folder-2" @click="moveFolder">
移动
</div><div style="border: 2px solid rgba(18,17,42,.07)"></div><div class="add-folder-6" @click="deleteFileFun">
删 除
</div></div></div><!-- 上传文件 弹窗 --><addFolder ref="addFolder1":currentLocationId="currentLocationId"/><moveFolder ref="moveFolder1":moveData="moveData"></moveFolder></div></template><script>import addFolder from'../components/addFolder.vue'import moveFolder from'../components/moveFolder.vue'import{ qeryFolderList, createPublicFolder, renameFolder, deleteFolder, deleteFile }from'@/api/folder/folder'exportdefault{name:'shareSpace',components:{ addFolder, moveFolder },data(){return{historyFolderId:0,//历史文件夹id,用于【返回上一级】historyFolderName:'',//历史文件夹name,用于【返回上一级】currentLocationId:0,//当前所处位置(文件夹)id,0为根目录currentLocationName:'共享空间',//当前所处位置(文件夹)名//移动文件(夹)时需要的参数moveData:{typeofFolder:0,//所选对象的类型(1:文件夹;2:文件)clickFolderId:-1,//被右键的文件夹id},cardLoading:false,folderList:[],//文件夹列表filesList:[],//文件列表// 文件夹 右键菜单栏folderStyle:{left:'0px',top:'0px'},folderShow:false,clickFolderId:-1,//被右键的文件夹idclickFolderName:'',//被右键的文件夹名// 文件 右键菜单栏fileStyle:{left:'0px',top:'0px'},fileShow:false,clickFileId:-1,//被右键的文件idclickFileName:'',//被右键的文件名clickFilePath:'',//被右键的文件路径-已加上下载的路径网站前端queryParams:{//查询参数folderId:0//目标文件(夹)id,值为0则查询根目录文件(夹)}}},methods:{a(){
window.open(`这里填服务器储存文件的地址啦~`+this.clickFileName);},//返回上一级goBack(){if(this.currentLocationId ==0){this.$message({message:'已经不能再往后退啦!',type:'warning'});}else{this.queryParams.folderId =this.historyFolderId;this.currentLocationId =this.historyFolderId;this.currentLocationName =this.historyFolderName ==null?'文件管理空间':this.historyFolderName;this.historyFolderId =this.currentLocationId;this.historyFolderName =this.currentLocationName;this.getList();}},// 获取列表数据getList(){this.loading =trueqeryFolderList(this.queryParams).then(response=>{
console.log(response)this.folderList = response.data.folders
this.filesList = response.data.sysFiles
this.historyFolderId = response.data.sysFolder ==null?0: response.data.sysFolder.parentId;this.historyFolderName = response.data.sysFolder ==null?'文件管理空间': response.data.sysFolder.parentName;})},// 刷新当前列表refreshGetList(){this.queryParams.folderId =this.currentLocationId;this.getList()this.$message({message:'已经成功获取最新数据啦!',type:'success'});this.initClickId()},//上传文件addFile(){this.$refs.addFolder1.open();},//创建文件夹addFolder(){this.$prompt('请输入新文件夹名称','创建文件夹',{confirmButtonText:'确定',cancelButtonText:'取消',}).then(({ value })=>{let sysFolder ={folderName: value,parentId:this.currentLocationId
}createPublicFolder(sysFolder).then(res=>{if(res.code ==200){this.$message({type:'success',message:'创建成功 '});const that =this;setTimeout(function(){
that.refreshGetList();// 刷新当前页面},500);}else{this.$message({type:'error',message:'创建失败 '});}})}).catch(()=>{});},// 重命名文件夹updateFloder(){this.folderShow =false;this.$prompt('请输入文件夹的新名称','重命名',{confirmButtonText:'确定',cancelButtonText:'取消',inputValue:this.clickFolderName,inputErrorMessage:'输入不能为空',inputValidator:(value)=>{// 点击按钮时,对文本框里面的值进行验证if(!value){return'输入不能为空';}},}).then(({ value })=>{let sysFolder ={folderName: value,folderId:this.clickFolderId //默认为0}renameFolder(sysFolder).then(res=>{if(res.code ==200){this.$message({type:'success',message:'修改成功 '});let that =this;setTimeout(function(){
that.refreshGetList();// 刷新当前页面},500);}else{this.$message({type:'error',message:'修改失败 '});}})})},//删除文件夹deleteFolder(){this.folderShow =false;this.$confirm('此操作将永久删除该文件夹,包括文件夹内的所有内容,是否继续?','提示',{confirmButtonText:'确定',cancelButtonText:'取消',type:'warning'}).then(()=>{deleteFolder(this.clickFolderId).then(res=>{if(res.code ==200){this.$message({type:'success',message:'删除成功 '});let that =this;setTimeout(function(){
that.refreshGetList();// 刷新当前页面},1000);}else{this.$message({type:'error',message:'删除失败! '});}})})},//打开文件夹openFolder(){this.folderShow =false;this.queryParams.folderId =this.clickFolderId;this.currentLocationId =this.clickFolderId;this.currentLocationName =this.clickFolderName;this.getList();},//鼠标双击文件夹doubleClickFolder(index, item){this.clickFolderId = item.folderId;this.clickFolderName = item.folderName;this.openFolder();},//文件夹右键rightClickFolder(index, item, e){this.initClickId()this.clickFolderId = item.folderId
this.clickFolderName = item.folderName
this.folderStyle.left = e.pageX -140+'px'this.folderStyle.top = e.pageY -70+'px'this.folderShow =truethis.moveData.typeofFolder =1},//文件 右键rightClickfile(index, item, e){this.initClickId()this.clickFileId = item.fileId
this.clickFileName = item.fileName
this.clickFilePath ="https://huang-pu.oss-cn-guangzhou.aliyuncs.com/"+ item.filePath
this.fileStyle.left = e.pageX -140+'px'this.fileStyle.top = e.pageY -70+'px'this.fileShow =truethis.moveData.typeofFolder =2},//删除文件deleteFileFun(){this.fileShow =false;this.$confirm('此操作将永久删除该文件, 是否继续?','提示',{confirmButtonText:'确定',cancelButtonText:'取消',type:'warning'}).then(()=>{deleteFile(this.clickFileId).then(res=>{if(res.code ==200){this.$message({type:'success',message:'删除成功 '});let that =this;setTimeout(function(){
that.refreshGetList();// 刷新当前页面},1000);}else{this.$message({type:'error',message:'删除失败! '});}})})},//移动文件(夹)moveFolder(){this.fileShow =false//通过判断文件/文件夹被右键选择而进行参数存储if(this.clickFolderId !=-1){this.moveData.clickFolderId =this.clickFolderId;}else{this.moveData.clickFolderId =this.clickFileId;}this.$refs.moveFolder1.open();},//初始化右键选择相关参数initClickId(){this.clickFileId =-1;this.clickFolderId =-1;this.fileShow =false;this.folderShow =false;}},mounted(){//监听鼠标点击事件
document.addEventListener("click",(e)=>{if(!this.folderShow &&!this.fileShow)return;// 如果右键菜单不显示,则不处理点击事件let target = e.target;while(target && target.parentNode){if(target.parentNode.class ==="folderContainer"){return;}
target = target.parentNode;}this.folderShow =false;this.fileShow =false;// 如果点击的是其他区域,则隐藏this.clickFolderId =-1;this.clickFileId =-1;});},created(){this.getList()}}</script><style lang="scss">.pageHeader {.el-page-header__content {
font-size: 16px !important;}}</style><style scoped lang="scss">.drawing_card {width:100%;height:100%;float: left;
margin-top: 15px;overflow: auto;
box-shadow:0 5px 5px rgb(000/10%);transition: all 0.9s;
border-radius: 10px;}.folderContainer {width: 150px;float: left;display: block;
flex-direction: column;
align-items: center;
justify-content: center;
margin-bottom: 20px;
margin-left: 30px;}.folder {width: 110px;height: 80px;perspective: 600px;
transform-style: preserve-3d;cursor: pointer;}.folderWrapper {width: 140px;height: 130px;padding: 20px 20px 10px 20px;position: relative;transition: all .2s ease;
border-radius: 6px;cursor: pointer;}.folderWrapper:hover {
background-color: aliceblue;}.folderName {
margin-top: 5px;
font-size: 14px;
line-height: 20px;
text-align: center;width: 100px;}.add-folder-9{position: absolute;display: flex;
justify-content: center;padding: 2px;
align-items: center;width: 130px;
background-color:rgba(6,13,20,.18);
border-radius: 12px;
box-shadow: 0px 8px 24px rgba(25,25,26,.06), 0px 4px 16px rgba(25,25,26,.04), 0px 0px 4px rgba(25,25,26,.04);}.add-folder-1{overflow: hidden;width:97%;height:96%;
background-color: #fff;
border-radius: 10px;}.add-folder-2{color: #19191a;display: flex;
justify-content: center;
align-items: center;width:100%;height: 36px;// margin-top: 5px;// margin-bottom: 5px}.add-folder-2:hover {
background-color:rgba(6,13,20,.18);// border-radius: 10px;cursor: pointer;}.add-folder-6{color: #19191a;display: flex;
justify-content: center;
align-items: center;width:100%;height: 36px;// margin-top: 5px;}.add-folder-6:hover {
background-color: red;// border-radius: 10px;cursor: pointer;}</style>
移动操作时,当点击移动后,需要先获取整个系统的树型目录,将该文件(夹)所属的父文件夹id更改为 树型目录中所选的文件夹id 。
//moveFolder.vue<template><el-dialog v-if="dialogVisible":modal-append-to-body="false":close-on-click-modal="false" title="移动":visible.sync="dialogVisible":show-close="false" width="400px"class="moveFolderDialog"><el-alert title="请选择要将当前文件(夹)移动到:" type="info" show-icon></el-alert><el-tree accordion :data="data" node-key="id" ref="tree" highlight-current @node-click="handleNodeClick"></el-tree><div style="margin-top: 20px"><el-button type="success" @click="submit">确定</el-button><el-button @click="close">取消</el-button></div></el-dialog></template><script>import{ getTreeDirectory, moveFolder, moveFile }from"@/api/folder/folder";exportdefault{name:'moveFolder',props:['moveData'],// moveDate对象中有两个参数: typeofFolder: 0,//所选对象的类型(1:文件夹;2:文件)// clickFolderId: -1,//所选对象的iddata(){return{dialogVisible:false,data:[],//树型目录defaultProps:{children:'children',label:'label'},clickDirectoryId:-1,//所选择的移动目标文件夹id}},methods:{open(){this.dialogVisible =true,getTreeDirectory().then(res=>{
console.log(res)this.data = res.data
})},close(){this.dialogVisible =false},submit(){if(this.moveData.typeofFolder ==1&&this.moveData.clickFolderId ==this.clickDirectoryId){this.$message({type:'error',message:'移动失败!请勿把文件夹移动到它本身中! '});}else{if(this.moveData.typeofFolder ==1){this.moveFolderFun()}elseif(this.moveData.typeofFolder ==2){this.moveFileFun()}}},//移动 文件夹moveFolderFun(){let dataObj ={folderId:this.moveData.clickFolderId,parentId:this.clickDirectoryId
}moveFolder(dataObj).then(res=>{if(res.code ==200){this.$message({type:'success',message:'修改成功 '});let that =this;this.dialogVisible =falsesetTimeout(function(){
that.$parent.refreshGetList();// 刷新当前页面},500);}else{this.$message({type:'error',message:'修改失败 '});}})},//移动 文件moveFileFun(){let dataObj ={fileId:this.moveData.clickFolderId,folderId:this.clickDirectoryId
}moveFile(dataObj).then(res=>{if(res.code ==200){this.$message({type:'success',message:'修改成功 '});let that =this;this.dialogVisible =falsesetTimeout(function(){
that.$parent.refreshGetList();// 刷新当前页面},500);}else{this.$message({type:'error',message:'修改失败 '});}})},//树型目录被选择时handleNodeClick(DirectoryId){this.clickDirectoryId = DirectoryId.id
}}}</script><style lang="scss">.moveFolderDialog {.el-dialog__body {
padding-top:0!important;}.el-alert {
margin-bottom: 20px;}}</style>
文件上传时,后端同学需要对文件信息做进一步处理,即先执行自定义policy()方法获取服务器存储的key加入文件信息再存入数据库。
不必要,可根据个人需求直接修改返回文件信息即可。
//addFolder.vue<template><el-dialog v-if="dialogVisible":modal-append-to-body="false":close-on-click-modal="false" title="上传文件":visible.sync="dialogVisible":show-close="false" width="400px"><el-upload ref="upload":data="dataObj" action="这里填写文件上传到的服务器地址"class="upload-demo"drag:limit="1":on-success="uploadSuccess":on-error="uploadError":on-exceed="handleExceed":before-upload="beforeUpload":auto-upload="false"><i class="el-icon-upload"></i><div class="el-upload__text">将文件拖到此处,或<em>点击上传,当前目录只允许上传1个文件</em></div></el-upload><div style="margin-top: 20px"><el-button icon="el-icon-upload2" type="success" @click="submit">提交</el-button><el-button @click="close">取消</el-button></div></el-dialog></template><script>import{ policy, addFile }from"@/api/folder/folder";import{ getUUID }from"../../../utils/index"exportdefault{name:'addFolder',props:['currentLocationId'],//当前所处文件夹iddata(){return{// oss资源dataObj:{},dialogVisible:false,// 文件信息 - 存于后端数据库fileInfo:{fileName:'',filePath:'',fileSize:0,//单位为kbfolderId:0}}},methods:{open(){this.dialogVisible =true},close(){this.dialogVisible =falsethis.$parent.refreshGetList();},//文件改变调用handleExceed(){this.$message.error('当前目录只能上传一个文件!');},//上传成功uploadSuccess(res){this.dialogVisible =falsethis.$parent.refreshGetList();},//上传失败uploadError(){this.$message.error('服务器异常请重试!');},//上传文件submit(){this.$refs.upload.submit();},// 资源上传前beforeUpload(files){returnnewPromise((resolve, reject)=>{policy().then(response=>{//数据处理因为业务需求写入的,不必要。//存储服务器数据处理this.dataObj.policy = response.data.policy
this.dataObj.signature = response.data.signature
this.dataObj.ossaccessKeyId = response.data.accessid
this.dataObj.dir = response.data.dir
this.dataObj.host = response.data.host
this.dataObj.key = response.data.dir +getUUID()+ files.name
console.log(this.dataObj)//后端数据库数据处理this.fileInfo.fileName = files.name;this.fileInfo.filePath =this.dataObj.key;this.fileInfo.fileSize =parseInt(files.size /2024);//file.size的单位为字节,转换成kb
console.log(this.fileInfo)resolve(true)this.fileInfo.folderId =this.currentLocationId;//确定该文件所处的文件夹id //上传到后端数据库addFile(this.fileInfo).then(res=>{if(res.code ==200){this.$message({type:'success',message:'上传成功 '});}else{this.$message({type:'error',message:'上传失败 '});}})})})},}}</script><style scoped lang="scss"></style>
api不知道需要不需要,一并丢上来好了。
该系统是集成在基于ruoyi框架的系统中。
//folder.jsimport request from'@/utils/request'//查询文件夹及文件列表exportfunctionqeryFolderList(query){returnrequest({url:'/system/folder/listFolderAndFile/'+ query.folderId,method:'get',params: query
})}//移动 前置请求-获取所有目录结构exportfunctiongetTreeDirectory(){returnrequest({url:'/system/folder/listFolderids',method:'get'})}//===================文件夹=====================//新建公共文件夹exportfunctioncreatePublicFolder(data){returnrequest({url:'/system/folder',method:'post',data: data
})}//重命名文件夹exportfunctionrenameFolder(data){returnrequest({url:'/system/folder',method:'put',data: data
})}//删除文件夹exportfunctiondeleteFolder(folderId){returnrequest({url:'/system/folder/'+ folderId,method:'delete'})}//移动文件夹exportfunctionmoveFolder(data){returnrequest({url:'/system/folder',method:'put',data: data
})}//===================文件=====================// oss资源上传 - 后端服务器exportfunctionpolicy(){returnrequest({url:'/system/ziyuan/oss/policy',method:'get'})}//上传文件-后端数据库exportfunctionaddFile(data){returnrequest({url:'/system/file',method:'post',data: data
})}//删除文件exportfunctiondeleteFile(fileId){returnrequest({url:'/system/file/'+ fileId,method:'delete'})}//移动文件夹exportfunctionmoveFile(data){returnrequest({url:'/system/file',method:'put',data: data
})}
TODO:
没有制作分页查询操作。
有点乱(磕头,有空再整理。
版权归原作者 用户已经懒死 所有, 如有侵权,请联系我们删除。