记录一下之前写过的一个文件管理系统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'
export default {
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,//被右键的文件夹id
clickFolderName: '',//被右键的文件夹名
// 文件 右键菜单栏
fileStyle: {
left: '0px',
top: '0px'
},
fileShow: false,
clickFileId: -1,//被右键的文件id
clickFileName: '',//被右键的文件名
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 = true
qeryFolderList(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 = true
this.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 = true
this.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(0 0 0 /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";
export default {
name: 'moveFolder',
props: ['moveData'],
// moveDate对象中有两个参数: typeofFolder: 0,//所选对象的类型(1:文件夹;2:文件)
// clickFolderId: -1,//所选对象的id
data() {
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()
} else if (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 = false
setTimeout(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 = false
setTimeout(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"
export default {
name: 'addFolder',
props: ['currentLocationId'], //当前所处文件夹id
data() {
return {
// oss资源
dataObj: {},
dialogVisible: false,
// 文件信息 - 存于后端数据库
fileInfo: {
fileName: '',
filePath: '',
fileSize: 0,//单位为kb
folderId: 0
}
}
},
methods: {
open() {
this.dialogVisible = true
},
close() {
this.dialogVisible = false
this.$parent.refreshGetList();
},
//文件改变调用
handleExceed() {
this.$message.error('当前目录只能上传一个文件!');
},
//上传成功
uploadSuccess(res) {
this.dialogVisible = false
this.$parent.refreshGetList();
},
//上传失败
uploadError() {
this.$message.error('服务器异常请重试!');
},
//上传文件
submit() {
this.$refs.upload.submit();
},
// 资源上传前
beforeUpload(files) {
return new Promise((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框架的系统中。文章来源:https://www.toymoban.com/news/detail-837300.html
//folder.js
import request from '@/utils/request'
//查询文件夹及文件列表
export function qeryFolderList(query) {
return request({
url: '/system/folder/listFolderAndFile/' + query.folderId,
method: 'get',
params: query
})
}
//移动 前置请求-获取所有目录结构
export function getTreeDirectory() {
return request({
url: '/system/folder/listFolderids',
method: 'get'
})
}
//===================文件夹=====================
//新建公共文件夹
export function createPublicFolder(data) {
return request({
url: '/system/folder',
method: 'post',
data: data
})
}
//重命名文件夹
export function renameFolder(data) {
return request({
url: '/system/folder',
method: 'put',
data: data
})
}
//删除文件夹
export function deleteFolder(folderId) {
return request({
url: '/system/folder/' + folderId,
method: 'delete'
})
}
//移动文件夹
export function moveFolder(data) {
return request({
url: '/system/folder',
method: 'put',
data: data
})
}
//===================文件=====================
// oss资源上传 - 后端服务器
export function policy() {
return request({
url: '/system/ziyuan/oss/policy',
method: 'get'
})
}
//上传文件-后端数据库
export function addFile(data) {
return request({
url: '/system/file',
method: 'post',
data: data
})
}
//删除文件
export function deleteFile(fileId) {
return request({
url: '/system/file/' + fileId,
method: 'delete'
})
}
//移动文件夹
export function moveFile(data) {
return request({
url: '/system/file',
method: 'put',
data: data
})
}
TODO:
没有制作分页查询操作。
有点乱(磕头,有空再整理。文章来源地址https://www.toymoban.com/news/detail-837300.html
到了这里,关于基于Vue+Element UI的文件管理系统-Demo的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!