Appearance
文件上传组件使用示例
错误示例:
- 点击上传按钮,应避免使用弹窗,让用户二次点击上传按钮,进行文件上传
- 应该避免使用
el-upload
组件中的file-list
属性,show-file-list
应该设置成false
正确示例:
- 需自行实现文件列表回显文件
- 避免使用组件自带的文件列表,避免使用
el-upload
组件中的file-list
属性,show-file-list
应该设置成false
- 模块id,需要根据实际的模块信息设置
- 为避免element-ui组件问题,每次上传前,需要清空文件列表
- 页面增加loading,防止重复点击上传
- 如果上传的是图片,需要对图片进行压缩
- 上传成功的钩子函数也需要关闭loading
- 上传错误时,需要关闭loading
- 删除文件时,应该对删除操作进行提示
实现效果示例:
代码示例:
vue
<!-- 表单上传 -->
<template>
<div class="home-index">
<!-- 上传按钮 -->
<el-button @click="onUpload" size="small" type="primary">上传文件</el-button>
<!-- 文件列表TODO:需自行实现文件列表回显文件 -->
<div class="fix mt10">
<!-- 文件项目 -->
<div class="home-index-file-item l" v-for="(e, i) in fileList" :key="e.id">
<img class="home-index-file-item-img" :src="fileDisplay(e)" alt="">
<!-- 蒙层区域 -->
<div class="home-index-file-item-shade" @click="onPreview(e.id)">
<v-svg class="home-index-file-item-preview" type="bs-show" color="#fff" size="24" />
<el-button type="text" @click.stop="onPreview(e.id)">预览</el-button>
<el-divider direction="vertical"></el-divider>
<el-button type="text" @click.stop="onDownload(e)">下载</el-button>
<template v-if="canOperate">
<el-divider direction="vertical"></el-divider>
<el-button type="text" @click.stop="onRemove(e.id, i)">删除</el-button>
</template>
</div>
<div class="home-index-file-item-label ell" :title="e.name">{{e.name}}</div>
</div>
</div>
<!-- 上传组件,TODO:show-file-list属性,应设置为false,避免使用组件自带的文件列表 -->
<el-upload ref="upload" :class="`home-index-upload dn`" :accept="ACCEPT_TYPE" :action="`${CONTEXT_PATH}/pub/attachmentFileUpload`" :headers="uploadHeaders" :data="uploadData" multiple :show-file-list="false" :before-upload="onBeforeUpload" :on-success="onSuccess" :on-error="onError">
</el-upload>
</div>
</template>
<script>
import $ from 'jquery';
import { mapGetters } from 'vuex';
import { GET_TOKEN, GET_LOGIN_INFO } from '@/store/login';
import {
fileDisplay,
fileDownload,
filePreview,
fileRemove,
imageCompress,
} from '@/mixin/file';
import { getTimestamp } from '@/mixin/system';
import util from '@/assets/js/util';
import validate from '@/assets/js/validate';
import { CONTEXT_PATH, FILE_TYPE, ACCEPT_TYPE } from '@/assets/js/constant';
export default {
data() {
return {
uploadHeaders: {
Authorization: '', //token信息
Timestamp: '', //时间戳摘要
}, //上传头信息
uploadData: {
sysId: 'SYS', //TODO:模块id,需要根据实际的模块信息设置
attachId: '', //附件id
contentType: '', //附件类型
mofDivCode: '', //区划
creator: '', //用户代码
creatorName: '', //用户名称
}, //附加参数信息
fileList: [],
CONTEXT_PATH,
ACCEPT_TYPE,
};
},
methods: {
/**
* 处理上传文件
* 1、TODO:为避免element-ui组件问题,每次上传前,需要清空文件列表
* 2、手动触发click事件来触发上传
*/
onUpload() {
this.$refs.upload.clearFiles();
$(`.home-index-upload .el-upload`).click();
},
/**
* 处理上传前的业务逻辑
* 1、校验文件类型
* 2、设置上传时请求头信息、时间密钥
* 3、设置上传时的附加参数
* 4、TODO:页面增加loading,防止重复点击上传
*/
onBeforeUpload(file) {
if (!validate.validateFileType(file.name)) {
this.$message({
type: 'warning',
message: '文件非法!',
});
return false;
}
this.uploadHeaders.Authorization = this.GET_TOKEN;
this.uploadHeaders.Timestamp = getTimestamp();
this.uploadData.attachId = util.generateUUID();
this.uploadData.contentType = file.type;
this.uploadData.mofDivCode = this.GET_LOGIN_INFO.mofDivCode;
this.uploadData.creator = this.GET_LOGIN_INFO.userCode;
this.uploadData.creatorName = this.GET_LOGIN_INFO.userName;
this.$loading();
return true;
},
/**
* 上传成功时的钩子
* 1、如果上传的是图片,上传完图片之后,需要生成缩略图(解决因文件体积过大导致预览速度慢的问题)
*/
async onSuccess(response, { name }) {
try {
let { code, data } = response;
if (code === '200') {
// TODO:如果上传的是图片,需要对图片进行压缩
if (util.getFileType(name) === FILE_TYPE.IMAGE) {
await imageCompress(data);
}
this.fileList.push({
id: data,
name
});
} else {
//可以上传会返回200,如果code不是200,说明后端校验拦截了,需要将错误抛出
this.$message({
type: 'error',
message: response.msg,
});
}
} catch (err) {
console.error(err);
this.$message({
type: 'error',
message: err.msg,
});
} finally {
//TODO:上传成功的钩子函数也需要关闭loading
this.$loadingClose();
}
},
/**
* 处理上传失败
*/
onError(err) {
console.error(err);
// TODO:上传错误时,需要关闭loading
this.$loadingClose();
this.$message({
type: 'error',
message: '上传失败,请稍后重试!',
});
},
/**
* 处理预览
*/
onPreview(id) {
filePreview(id, this.fileList);
},
/**
* 处理下载附件
*/
onDownload({ id }) {
fileDownload(id);
this.$message({
type: 'success',
message: '正在下载,请稍等!',
});
},
/**
* 处理删除附件
*/
async onRemove(id, index) {
try {
this.$loading();
// TODO:删除文件时,应该对删除操作进行提示
await this.$confirm('确定要删除当前附件?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
});
await fileRemove(id);
this.fileList.splice(index, 1);
this.$message({
type: 'success',
message: '删除成功!',
});
} catch (err) {
console.error(err);
if (err !== 'cancel') {
this.$message({
type: 'error',
message: err.msg,
});
}
} finally {
this.$loadingClose();
}
},
/**
* 文件显示
*/
fileDisplay({id, name}) {
return fileDisplay(id, name);
}
},
computed: {
...mapGetters([GET_TOKEN, GET_LOGIN_INFO]),
},
};
</script>
<style lang="scss" scoped>
@import '~@/assets/style/variables.scss';
.home-index {
height: 100vh;
padding: 10px;
background: $-bs-color-white;
text-align: center;
.home-index-file-item {
position: relative;
width: 160px;
height: 90px;
margin-right: 10px;
margin-bottom: 20px;
display: flex;
align-items: center;
border: $-bs-border;
border-radius: $-bs-border-radius;
background: $-bs-color-white;
.home-index-file-item-label {
position: absolute;
bottom: -21px;
left: 0;
width: 100%;
font-size: 13px;
color: $-bs-text-color-light;
}
.home-index-file-item-img {
width: 72px;
margin-left: 15px;
}
.home-index-file-item-shade {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
padding: 10px;
line-height: 120px;
box-sizing: border-box;
text-align: right;
background: rgba(0, 0, 0, 0.3);
opacity: 0;
border-radius: $-bs-border-radius;
cursor: pointer;
&:hover {
opacity: 1;
}
.home-index-file-item-preview {
position: absolute;
top: 8px;
right: 8px;
}
.el-button--text {
padding: 0;
color: $-bs-color-white;
&:hover {
color: $-bs-color-blue;
}
}
}
::v-deep .el-divider--vertical {
margin: 0 5px;
}
}
}
</style>