前言
平常项目中经常会用到文件上传功能,由于每次都写比较麻烦,就简单进行了封装,方便重复使用。写了两个版本,根据需要选择,因为没有测试(缺失上传地址),可能存在一些bug。
文件列表版
<template>
<div v-loading="loading" element-loading-text="拼命上传中"
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.8)">
<el-row>
<el-col :span="4">
<el-form-item label="附件文档">
<el-upload ref="file" :data="params" :action="$uploadUrl" name="file" multiple :show-file-list="false" :limit="maxNumber" :file-list="fileList"
:before-upload="checkFiles" :on-exceed="handleExceed" :on-success="uploadSuccess" :on-error="uploadError">
<el-button size="small">选择文件</el-button>
</el-upload>
</el-form-item>
</el-col>
<el-col :span="20">
<!-- 使用插槽进行自定义 -->
<slot name="uploadTip"></slot>
<slot name="default">
<el-form-item label="附件">
<el-input :value="'支持PDF、JPEG、PNG、JPG、word、Excel、PPT等格式'" disabled></el-input>
</el-form-item>
</slot>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="附件列表"></el-form-item>
<div class="file-list">
<el-tag
v-for="(tag,index) in fileList"
:key="tag.fileCode"
class="file-tag"
:closable="closable"
:type="tag.type"
@click="handleClick(tag)"
@close="handleRemove(tag,index)">
{{ tag.sourceName }}
</el-tag>
</div>
</el-col>
</el-row>
<!-- 图片预览 -->
<el-dialog title="预览" :visible="openView" width="460px" :close-on-click-modal="false" append-to-body @close="openView=false">
<el-image class="img-view" :src="fileSrc" fit="fill"></el-image>
</el-dialog>
</div>
</template>
<script>
export default {
props: {
// 允许上传的文件裂隙
fileType: {
type: Array,
default: () => ['pdf', 'jpg', 'jpeg', 'png', 'xls', 'xlsx', 'doc', 'docx','ppt']
},
// 最大文件上传数
maxNumber: {
type: Number,
default: 4
},
// 上传时额外参数
params: {
type: Object,
default: () => {}
},
// 用于回显
fileList: {
type: Array,
default: () => []
},
closable: {
type: Boolean,
default: true
}
},
data() {
return {
fileSrc: '',
openView: false,
// 上传加载
loading: false
};
},
methods: {
// 获取文件后缀
getFileSuffix(file,name) {
let fileName = file[name].toLocaleLowerCase();
return fileName.substr(fileName.lastIndexOf('.') + 1);
},
// 确定文件格式
checkFiles(file) {
// 文件后缀
let suffix = this.getFileSuffix(file,'name');
if(!this.fileType.includes(suffix)) {
this.$message('格式错误,请按提示选择正确的文件格式');
// before-upload 必须返回false,否则还会继续上传
return false;
}
this.loading = true;
},
// 文件超限提醒
handleExceed(files, fileList) {
this.$message.warning(`当前限制选择 ${this.maxNumber} 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`);
},
// 文件移除
handleRemove(file, index) {
this.$confirm('确定删除?', '提示', {
confirmButtonText: '确定',
showCancelButton: '取消',
type: 'warning'
}).then(() => {
this.fileList.splice(index, 1);
// 返回文件信息
this.$emit('getFileInfo',this.fileList);
}).catch(() => {
this.$message({ type: 'info', message: '已取消删除' });
});
},
// 上传成功
uploadSuccess(response, file, fileList) {
this.loading = false;
// 这里存在一个问题,当第二次上传时,之前上传的文件也会存在fileList中,但是之前的文件信息不存在response属性
// 解决方式:跳过之前的文件信息判断
let size = this.fileList.length; // 获取之前的文件个数
if(fileList.every(e => e.status == 'success')) {
fileList.forEach((e,index) => {
if(index >= size) {
// 处理文件信息
this.fileList.push({
fileCode: e.response.data.code,
code: e.response.data.code,
fileName: e.response.data.fileName,
filePath: e.response.data.filePath,
fileType: 1,
sourceName: e.response.data.sourceName,
size: (e.size / 1000).toFixed(1)
});
}
});
// 清除已上传的文件列表
this.$refs.file.clearFiles();
// 返回文件信息
this.$emit('getFileInfo',this.fileList);
}
},
// 上传失败
uploadError(err) {
// 暂时无法模拟
this.loading = false;
this.$message.warning('抱歉,上传失败,请稍后重试!');
},
// 处理点击事件
handleClick(file) {
let suffix = this.getFileSuffix(file,'filePath');
if(['png','jpg','jpeg'].includes(suffix)) {
// 图片直接查看
this.fileSrc = file.filePath;
this.openView = true;
}else if(suffix == 'pdf') {
// pdf预览
}else{
// 其他下载
}
},
// 删除所有文件,一般用于弹窗关闭使用
removeAllFile() {
this.fileList = [];
// 返回文件信息
this.$emit('getFileInfo',[]);
}
}
};
</script>
<style scoped lang="scss">
.file-list{
width: 700px;
display: flex;
flex-flow: wrap;
.file-tag{
cursor:pointer;
margin-right: 10px;
margin-bottom: 10px;
}
}
.input-w{
width: 500px !important;
}
.img-view{
width: 380px;
height: 380px;
margin: 10px 20px;
}
</style>
照片墙版
效果图
<template>
<div v-loading="loading" element-loading-text="拼命上传中"
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.8)">
<el-row>
<el-col :span="4">
<el-form-item label="附件文档">
<el-upload ref="file" :data="params" :action="$uploadUrl" name="file" multiple :show-file-list="false" :limit="maxNumber" :file-list="fileList"
:before-upload="checkFiles" :on-exceed="handleExceed" :on-success="uploadSuccess" :on-error="uploadError">
<el-button size="small">选择文件</el-button>
</el-upload>
</el-form-item>
</el-col>
<el-col :span="20">
<!-- 使用插槽进行自定义 -->
<slot name="uploadTip"></slot>
<slot name="default">
<el-form-item label="附件">
<el-input :value="'支持PDF、JPEG、PNG、JPG、word、Excel、PPT等格式'" disabled></el-input>
</el-form-item>
</slot>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="附件列表"></el-form-item>
<div class="file-list">
<div v-for="(file,index) in fileList" :key="file.fileCode" class="file-item"
@mouseover="moveIndex=index" @mouseout="moveIndex=-1">
<!-- 缩略图 -->
<img :src="isImgOrPdf(file).url" />
<!-- 遮罩 -->
<div v-show="moveIndex==index" class="file-mask">
<!--
1、新增、修改时
图片显示查看和删除按钮
pdf显示查看和删除按钮
其他格式显示下载和删除按钮
2、查看时
图片、pdf显示查看
其他格式显示下载
-->
<i v-if="isImgOrPdf(file).res" class="el-icon-zoom-in" @click="handleClick(file)"></i>
<i v-else v-download="({name:file.sourceName,url:file.filePath})" class="el-icon-download"></i>
<i v-if="flog!='view'" class="el-icon-delete" @click="handleRemove(file,index)"></i>
</div>
</div>
</div>
</el-col>
</el-row>
<!-- 图片预览 -->
<el-dialog title="预览" :visible="openView" width="460px" :close-on-click-modal="false" append-to-body @close="openView=false">
<el-image class="img-view" :src="fileSrc" fit="fill"></el-image>
</el-dialog>
</div>
</template>
<script>
export default {
directives: {
// 下载指令
download: {
inserted: (el,binding,vnode) => {
el.onclick = () => {
let name = binding.value.name;
let url = binding.value.url;
let link = document.createElement('a');
link.setAttribute('download', name);
link.href = url;
document.body.appendChild(link); // 添加到页面中,为兼容Firefox浏览器
link.click();
document.body.removeChild(link); // 从页面移除
};
}
}
},
props: {
// 允许上传的文件裂隙
fileType: {
type: Array,
default: () => ['pdf', 'jpg', 'jpeg', 'png', 'xls', 'xlsx', 'doc', 'docx','ppt']
},
// 最大文件上传数
maxNumber: {
type: Number,
default: 4
},
// 上传时额外参数
params: {
type: Object,
default: () => {}
},
// 用于回显
fileList: {
type: Array,
default: () => []
},
// 标识
flog: {
type: String,
default: 'view'
}
},
data() {
return {
fileSrc: '',
openView: false,
// 上传加载
loading: false,
// 记录鼠标移入的下标
moveIndex: -1
};
},
methods: {
// 获取文件后缀
getFileSuffix(file,name) {
let fileName = file[name].toLocaleLowerCase();
return fileName.substr(fileName.lastIndexOf('.') + 1);
},
// 确定文件格式
checkFiles(file) {
// 文件后缀
let suffix = this.getFileSuffix(file,'name');
if(!this.fileType.includes(suffix)) {
this.$message('格式错误,请按提示选择正确的文件格式');
// before-upload 必须返回false,否则还会继续上传
return false;
}
this.loading = true;
},
// 文件超限提醒
handleExceed(files, fileList) {
this.$message.warning(`当前限制选择 ${this.maxNumber} 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`);
},
// 文件移除
handleRemove(file, index) {
this.$confirm('确定删除?', '提示', {
confirmButtonText: '确定',
showCancelButton: '取消',
type: 'warning'
}).then(() => {
this.fileList.splice(index, 1);
// 返回文件信息
this.$emit('getFileInfo',this.fileList);
}).catch(() => {
this.$message({ type: 'info', message: '已取消删除' });
});
},
// 上传成功
uploadSuccess(response, file, fileList) {
this.loading = false;
// 这里存在一个问题,当第二次上传时,之前上传的文件也会存在fileList中,但是之前的文件信息不存在response属性
// 解决方式:跳过之前的文件信息判断
let size = this.fileList.length; // 获取之前的文件个数
if(fileList.every(e => e.status == 'success')) {
fileList.forEach((e,index) => {
if(index >= size) {
// 处理文件信息
this.fileList.push({
fileCode: e.response.data.code,
code: e.response.data.code,
fileName: e.response.data.fileName,
filePath: e.response.data.filePath,
fileType: 1,
sourceName: e.response.data.sourceName,
size: (e.size / 1000).toFixed(1)
});
}
});
// 清除已上传的文件列表
this.$refs.file.clearFiles();
// 返回文件信息
this.$emit('getFileInfo',this.fileList);
}
},
// 上传失败
uploadError(err) {
// 暂时无法模拟
this.loading = false;
this.$message.warning('抱歉,上传失败,请稍后重试!');
},
// 查看
handleClick(file) {
let suffix = this.getFileSuffix(file,'filePath');
if(['png','jpg','jpeg'].includes(suffix)) {
// 图片直接查看
this.fileSrc = file.filePath;
this.openView = true;
}else if(suffix == 'pdf') {
// pdf预览
window.open(file.filePath);
}
},
// 删除所有文件,一般用于弹窗关闭使用
removeAllFile() {
this.fileList = [];
// 返回文件信息
this.$emit('getFileInfo',[]);
},
// 判断是否是图片或pdf
isImgOrPdf(file) {
let imgUrl = {
'pdf': require('../images/pdf.png'),
'ppt': require('../images/ppt.png'),
'pptx': require('../images/pdf.png'),
'docx': require('../images/word.png'),
'doc': require('../images/word.png'),
'xls': require('../images/excel.png'),
'xlsx': require('../images/excel.png'),
'dwg': require('../images/dwg.png'),
'zip': require('../images/zip.png'),
'rar': require('../images/zip.png'),
'7z': require('../images/zip.png')
};
let suffix = this.getFileSuffix(file,'filePath');
if(['png','jpg','jpeg'].includes(suffix)) {
return{
url: file.filePath,
res: true
};
}else{
return{
url: imgUrl[suffix],
res: false
};
}
}
}
};
</script>
<style scoped lang="scss">
.file-list{
width: 700px;
display: flex;
flex-flow: wrap;
.file-tag{
cursor:pointer;
margin-right: 10px;
margin-bottom: 10px;
}
.file-item{
width: 80px;
height: 80px;
border-radius: 10px;
margin-right: 10px;
cursor:pointer;
position: relative;
.file-mask{
width: 80px;
height: 80px;
border-radius: 10px;
background: black;
opacity: 0.6;
position: absolute;
z-index: 200;
display: flex;
align-items: center;
justify-content: space-evenly;
color: #ffffff;
font-size: 24px;
}
img{
width: 80px;
height: 80px;
border-radius: 10px;
position: absolute;
}
}
}
.input-w{
width: 500px !important;
}
.img-view{
width: 380px;
height: 380px;
margin: 10px 20px;
}
</style>