上传组件
关注他
组件的使用实例
<!--
1、默认的文件格式是图片
2、如果是文件类型的,需要加 listType="text" 属性
3、如果是上传的是,用户头像的,:isAvatar="true" 属性
4、多文件上传,添加 :multiple="true" 属性
-->
<p class="bottom-space">上传文件类型图片:默认</p>
<fy-upload-drag></fy-upload-drag>
<!-- 上传的文件格式,文件类型 -->
<p class="bottom-space">上传文件格式类型:</p>
<fy-upload-drag listType="text"></fy-upload-drag>
<p class="bottom-space">用户头像使用:</p>
<fy-upload-drag :isAvatar="true" :multiple="true"></fy-upload-drag>
组件代码
<template>
<div class="upload-drag-wrap">
<div v-if="isAvatar&&uploadFiles[uploadFiles.length-1]" class="avatar">
<img
v-if="uploadFiles[uploadFiles.length-1]"
class="upload-display-img"
:src="uploadFiles[uploadFiles.length-1].url"
alt
/>
<span class="upload-list__item-actions">
<span
class="upload-list__item-delete"
@click="handlePreview(uploadFiles[uploadFiles.length-1])"
>
<i class="el-icon-plus"></i>
</span>
<span
class="upload-list__item-delete"
@click="handleRemove(uploadFiles[uploadFiles.length-1])"
>
<i class="el-icon-delete"></i>
</span>
</span>
</div>
<el-upload
v-show="!isAvatar||uploadFiles.length===0"
:before-upload="handleBeforeUpload"
:auto-upload="autoUpload"
:drag="drag"
:http-request="pushElParams"
:limit="isAvatar?0:limit"
:list-type="isAvatar?'picture-card':listType"
:multiple="multiple && !isAvatar"
:on-exceed="handleExceed"
:on-preview="handlePreview"
:on-remove="handleRemove"
:on-success="handleSuccess"
:on-change="handleChange"
:file-list="fileList"
:show-file-list="showFileList && !isAvatar"
action
class="upload-demo"
element-loading-background="rgba(0, 0, 0, 0.6)"
ref="upload"
v-loading="loading"
>
<i class="el-icon-plus">
<div v-if="listType==='picture-card'">拖拽/点击上传</div>
</i>
<div
v-if="listType==='picture'||listType==='text'"
class="text-center"
>{{ $_text['text.drag.file.here'] + $_text['text.click.select.file'] }}</div>
<div v-if="listType=='picture'" slot="tip" class="upload-tip">
{{
$_messages.msgVarReplace($_text['text.upload.file.restriction'], [
maxFileSize,
])
}}
{{ $_messages.msgVarReplace($_text['text.file.number'], [limit]) }}
</div>
<div v-if="listType=='picture-card'" slot="file" slot-scope="{file}">
<img class="upload-img" :src="file.url" alt />
<span class="el-upload-list__item-actions">
<span class="el-upload-list__item-delete" @click="handlePreview(file)">
<i class="el-icon-plus"></i>
</span>
<span class="el-upload-list__item-delete" @click="handleRemove(file)">
<i class="el-icon-delete"></i>
</span>
</span>
</div>
<div
v-else-if="listType=='picture'"
class="upload-list-picture"
slot="file"
slot-scope="{file}"
@click="handlePreview(file)"
>
<img class="item-img" :src="file.url" alt />
<a class="item-name">{{file.name}}</a>
<label v-if="file.status==='success'" class="item-status-label">
<i
:class="{
'icon-upload-success': true,
'icon-check': true,
'icon-circle-check': true
}"
></i>
</label>
<i class="icon-close" @click.stop="handleRemove(file)"></i>
</div>
<div
v-else-if="listType=='text'"
class="upload-list-text"
slot="file"
slot-scope="{file}"
@click="handlePreview(file)"
>
<i class="item-img" />
<a class="item-name">{{file.name}}</a>
<label v-if="file.status==='success'" class="item-status-label">
<i
:class="{
'icon-upload-success': true,
'icon-check': true,
'icon-circle-check': true
}"
></i>
</label>
<i class="icon-close" @click.stop="handleRemove(file)"></i>
</div>
</el-upload>
<!-- <fy-dialog :show.sync="dialogVisible">
<img :src="dialogImageUrl" alt width="100%" />
</fy-dialog>-->
<el-dialog :append-to-body="true" :visible.sync="dialogVisible" size="tiny">
<img :src="dialogImageUrl" alt width="100%" />
</el-dialog>
</div>
</template>
<script>
export default {
name: "fyUploadDrag",
componentName: "fyUploadDrag",
props: {
modelName: {
type: String,
default: "xfile"
},
delUrl: {
type: String,
default: "api/v1/attachment_info/"
},
uploadUrl: {
type: String,
default: "api/v1/upload/upload_attachment/"
},
//附件信息中,用户可以自定义的参数
csParam: {
type: Object,
default: function() {
return {
tableRecId: "",
tableName: "attachment",
applicationType: ""
};
}
},
// 文件大小限制,单位:MB
maxFileSize: {
type: Number,
default: 10
},
/**
* 文件类型限制,参考下面的:
* txt:text/plain rar:"" sql:"" word:application/vnd.openxmlformats-officedocument.wordprocessingml.document
* vsd:application/vnd.visio vsdx:application/vnd.ms-visio.drawing png:image/png jpg:image/jpeg md:""
* pdf:application/pdf xlsx:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
* json:application/json
*
* 支持两个快捷类型:
* image:['image/jpeg', 'image/gif', 'image/bmp', 'image/png']
* document:[
'text/plain',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'xlsx:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/json',
'application/pdf',
'application/vnd.visio',
'application/vnd.ms-visio.drawing',
]
*/
fileTypes: {
type: Array,
default: function() {
return [];
}
},
//el-upload 的参数
//TODO xsw 自动上传有问题,先注释 是否在选取文件后立即进行上传
autoUpload: {
type: Boolean,
default: true
},
//是否支持多选文件
multiple: {
type: Boolean,
default: true
},
//上传个数限制
limit: {
type: Number,
default: 5
},
//文件列表的类型
listType: {
type: String,
default: "picture"
},
//是否启用拖拽上传
drag: {
type: Boolean,
default: true
},
showFileList: {
type: Boolean,
default: true
},
/**
* 编辑文件时做回显的数组,所需参数:
* fileList: [
{
attachmentInfoId:'200104170935632szgl011229',
name: 'food.jpeg',
url:
'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100',
},
]
*/
fileList: {
type: Array,
default: function() {
return [];
}
},
// 清空按钮
isEmpty: {
type: Boolean,
default: true
},
// 是否在全部上传成功后清空显示列表
isClear: {
type: Boolean,
default: false
},
isAvatar: {
type: Boolean,
default: false
}
},
data() {
return {
delByTableRecUrl: "api/v1/attachment_info/by_table_rec/",
delByIdsUrl: "api/v1/attachment_info/by_ids/",
urlModelName: this.$_modelConfig.xfile, //后端模块名
csAtt: this.csParam,
allData: new FormData(),
csElParamsList: [],
index: 0,
loading: false,
dialogImageUrl: "",
dialogVisible: false,
uploadSuccessFiles: {},
uploadFailedFiles: {},
uploadErrorMsg: [],
/**
* 已经上传成功文件的附件id,使用该组件时,通过v-model可以同步获取该元素
* 示例:
* <fy-upload-drag v-model="attIds"></fy-upload-drag>
*/
attIds: [],
nowErrMsgs: [],
emitFileLists: [],
uploadFiles: []
};
},
mounted() {
// this.uploadCard = document.getElementsByClassName(
// "el-upload el-upload--picture-card"
// )[0];
if (this.fileList) {
for (let index in this.fileList) {
let file = this.fileList[index];
this.uploadSuccessFiles[file.uid] = {};
this.uploadSuccessFiles[file.uid].attachmentUploadResult = {};
this.uploadSuccessFiles[
file.uid
].attachmentUploadResult.attachmentInfoId = file.attachmentInfoId;
this.uploadSuccessFiles[file.uid].fileInfo = file;
this.attIds.push(file.attachmentInfoId);
this.emitFileLists.push(file);
}
if (this.isClear) {
this.$refs.upload.clearFiles();
}
this.$emit("emitFileLists", this.emitFileLists);
}
},
methods: {
/**
* el提供的点击删除的回掉
*/
handleRemove(file, fileList) {
if (this.uploadSuccessFiles[file.uid]) {
this.del(
this.uploadSuccessFiles[file.uid].attachmentUploadResult
.attachmentInfoId,
file.uid,
file
);
} else if (this.uploadFailedFiles[file.uid]) {
this.delUploadFile(file);
this.delfailedCsParam(file.uid);
} else {
this.delUploadFile(file);
}
},
handleChange(file, fileList) {
this.uploadFiles = fileList;
},
/**
* el提供的上传成功的回掉
*/
handleSuccess(response, file, fileList) {
//记录下上传成功文件的信息,用系统生成的uid为key
this.uploadSuccessFiles[file.uid] = {};
this.uploadSuccessFiles[file.uid].fileInfo = file.raw;
this.uploadSuccessFiles[file.uid].attachmentUploadResult =
response.data.attachmentUploadResult;
this.attIds.push(response.data.attachmentUploadResult.attachmentInfoId);
let obj = {
url: file.url,
name: file.name,
attachmentInfoId: response.data.attachmentUploadResult.attachmentInfoId,
uid: file.uid
};
this.emitFileLists.push(obj);
},
/**
* el提供的点击文件列表回掉
*/
handlePreview(file) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
},
/**
* 定义超出限制时的行为
*/
handleExceed(files, fileList) {
this.$alert(
this.$_messages.msgVarReplace(this.$_text["text.file.exceedLimit"], [
this.limit,
files.length,
files.length + fileList.length
]),
{ type: "warning" }
);
},
/**
* 根据:tableRecId、tableName、application_type 批量删除已上传的附件
* @param {*} csAtt 包括tableRecId(必填)、tableName(必填)、application_type
* @param {*} url
* 使用场景:
* 当前上传的tableRecId都是同一个,所有根据tableRecId删除即使删除本次上传的所有文件
*/
delUploadFileByCsAtt(csAtt = this.csAtt, url = this.delByTableRecUrl) {
this.$_del(this.urlModelName, url, csAtt)
.then(res => {
this.clear();
this.$message.success(res.msg);
})
.catch(err => {
this.$message.error(err.msg);
});
},
/**
* 根据attIds批量删除已经上传的文件
*/
delUploadFileByIds(ids = this.attIds, url = this.delByIdsUrl) {
let params = { attachmentInfoIds: ids + "" };
this.$_del(this.urlModelName, url, params)
.then(res => {
this.clear();
this.$message.success(res.msg);
})
.catch(err => {
this.$message.error(err.msg);
});
},
/**
* 这个清空不删除数据库数据,只是清空显示列表
* 不会清除attIds
*/
clear() {
this.$refs.upload.clearFiles();
this.uploadSuccessFiles = {};
this.uploadFailedFiles = {};
},
del(id, uid, file) {
this.$_del(this.urlModelName, this.delUrl + id)
.then(res => {
let fileName = this.uploadSuccessFiles[uid].fileInfo.name;
for (let i = 0; i < this.emitFileLists.length; i++) {
if (
this.uploadSuccessFiles[uid].fileInfo.name ==
this.emitFileLists[i].name
) {
this.emitFileLists.splice(i, 1);
}
}
this.$emit("emitFileLists", this.emitFileLists);
this.delSuccessCsParam(uid);
this.delUploadFile(file);
this.$message.success(res.msg);
})
.catch(err => {
this.$message.error(err.msg);
});
},
delSuccessCsParam(uid) {
this.$_utils.delAssignEle(
this.attIds,
this.uploadSuccessFiles[uid].attachmentUploadResult.attachmentInfoId
);
delete this.uploadSuccessFiles[uid];
this.$emit("input", this.attIds);
},
delfailedCsParam(uid) {
delete this.uploadFailedFiles[uid];
},
delUploadFile(file) {
let index = this.uploadFiles.indexOf(file);
if (index >= 0) {
this.uploadFiles.splice(index, 1);
}
},
finishBack() {
if (this.index < this.csElParamsList.length) {
// this.uploadAjax(this.csElParamsList[this.index]);
} else {
//数据初始化
this.index = 0;
this.loading = false;
this.csElParamsList = [];
this.$emit("input", this.attIds);
this.$emit("emitFileLists", this.emitFileLists);
// 上传成功是否情况列表
if (this.isClear) {
this.$refs.upload.clearFiles();
}
if (this.nowErrMsgs && this.nowErrMsgs.length > 0) {
this.$message.error(this.nowErrMsgs.join(", "));
this.nowErrMsgs = [];
}
}
},
/**
* (手动上传时)点击上传后执行的方法
*/
submitUploadOneByOne() {
let upload = this.$refs.upload;
if (upload.uploadFiles == null || upload.uploadFiles.length <= 0) {
this.$alert(
this.$_text("text.file.selectFile"),
this.$_text("text.hint")
);
return;
}
this.loading = true;
upload.submit();
if (!this.$_utils.isArrayEmpty(this.uploadErrorMsg)) {
let msg = "";
for (let index in this.uploadErrorMsg) {
msg += "<p>" + this.uploadErrorMsg[index] + "</p>";
}
this.$message.error({
dangerouslyUseHTMLString: true,
message: msg
});
this.uploadErrorMsg = [];
}
if (this.$_utils.isArrayEmpty(this.csElParamsList)) {
this.loading = false;
return;
}
this.uploadAjax(this.csElParamsList[this.index]);
},
/**
* 验证文件类型
*/
validateFileType(type) {
if (this.$_utils.isArrayEmpty(this.fileTypes)) {
return true;
}
if (this.fileTypes.indexOf(type) != -1) {
return true;
}
//走到这里有可能是设置了快捷类型,如image:['image/jpeg', 'image/gif', 'image/bmp', 'image/png']
for (let index in this.fileTypes) {
let fileType = this.fileTypes[index];
let akType = [];
switch (fileType) {
case "image":
akType = ["image/jpeg", "image/gif", "image/bmp", "image/png"];
break;
case "document":
akType = [
"text/plain",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"xlsx:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/json",
"application/pdf",
"application/vnd.visio",
"application/vnd.ms-visio.drawing"
];
break;
default:
break;
}
if (!this.$_utils.isArrayEmpty(akType) && akType.indexOf(type) != -1) {
return true;
}
}
return false;
},
/**
* 执行上传图片方法之前的回调方法,这里返回false的话,当前文件会被移出文件列表,并且不会往下走调用http-request
*/
handleBeforeUpload(file) {
let type = file.type;
let size = file.size;
let name = file.name;
let isConformFileType = this.validateFileType(type);
if (!isConformFileType) {
this.uploadErrorMsg.push(
this.$_messages.msgVarReplace(this.$_text["text.file.type.error"], [
name,
this.fileTypes
])
);
return false;
}
let fileSize = size / 1024 / 1024;
let isConformSize = fileSize <= this.maxFileSize;
if (!isConformSize) {
this.uploadErrorMsg.push(
this.$_messages.msgVarReplace(
this.$_text["text.file.maxFileSize.exceed"],
[name, this.maxFileSize]
)
);
return false;
}
return true;
},
/**
* 这是el触发upload.submit()后执行的方法(:http-request="pushElParams")
* 这里只是将所有需要上传的文件保存下来
*/
pushElParams(elParams) {
//设置element ui的upload_params到数组中
this.csElParamsList.push(elParams);
if (this.autoUpload) {
this.loading = true
this.uploadAjax(elParams);
}
},
/**
* 发送上传请求
*/
uploadAjax(elParams) {
if (!elParams) {
this.$alert(this.$_text("text.file.selectNewFile"));
this.loading = false;
return;
}
this.allData = new FormData();
this.allData.append(
"csAttachmentInfo",
new Blob([JSON.stringify(this.csAtt)], {
type: "application/json"
})
);
this.allData.append("file", elParams.file);
let url = this.uploadUrl;
url = this.$_stringUtil.getUrlByModel(this.urlModelName, url);
let uploadProgressBack = progressEvent => {
let percent = ((progressEvent.loaded / progressEvent.total) * 100) | 0;
//调用onProgress方法来显示进度条,需要传递个对象 percent为进度值
elParams.onProgress({ percent: percent });
};
this.$_upload(
this.urlModelName,
url,
this.allData,
{},
uploadProgressBack
)
.then(response => {
//element ui的上传成功标记
//上传成功 调用onSuccess方法,否则没有完成图标
elParams.onSuccess(response);
if (
response.data.attachmentUploadResult &&
response.data.attachmentUploadResult.tableRecId
) {
this.csAtt.tableRecId =
response.data.attachmentUploadResult.tableRecId;
}
//处理自己的逻辑
this.index++; //文件列表处理进度
this.finishBack(); //文件上传完成的回掉
})
.catch(err => {
//element ui的上传失败标记(不执行,执行后会将失败的记录删除)
// elParams.onError(err)
//上传失败的文件信息记录下来,用系统生成的uid为key
this.uploadFailedFiles[elParams.file.uid] = {};
this.uploadFailedFiles[elParams.file.uid].file = elParams.file;
this.uploadFailedFiles[elParams.file.uid].msg = err.msg;
this.uploadFailedFiles[elParams.file.uid].elParams = elParams;
this.nowErrMsgs.push(err.msg);
//处理自己的逻辑
this.index++; //文件列表处理进度
this.finishBack(); //文件上传完成的回掉
});
}
}
};
</script>
<style lang="scss" scoped>
.upload-drag-wrap {
::v-deep .el-upload-dragger {
width: 100%;
height: 100%;
color: #999999;
font-size: 14px;
padding: 12px;
&:hover {
@include themify() {
border-color: themed("color");
}
}
}
::v-deep .el-upload--picture,
::v-deep .el-upload--text {
width: 340px;
height: 70px;
}
::v-deep .el-upload--picture-card,
.upload-img {
width: 146px;
height: 146px;
.el-upload-list__item {
padding: 5px 10px 5px 52px;
}
&:hover {
@include themify() {
background-color: themed("table-header-background-color");
border-color: themed("color");
}
}
}
::v-deep .is-dragover {
@include themify() {
background-color: themed("table-header-background-color");
border-color: themed("color");
}
}
::v-deep .el-upload--picture-card i {
font-size: 20px;
color: #999999;
div {
margin-top: 8px;
font-size: 14px;
}
}
::v-deep .el-upload {
&:focus {
@include themify() {
color: themed("color");
border-color: themed("color");
}
}
}
::v-deep .el-upload-list--picture {
width: 340px;
.el-upload-list__item {
padding: 5px 10px 5px 52px;
margin-top: 1px;
height: auto;
&:first-of-type {
margin-top: 7px;
}
}
}
::v-deep .el-upload-list--text {
width: 340px;
.el-upload-list__item {
padding: 0px 10px 0px 33px;
margin-top: 1px;
height: auto;
&:first-of-type {
margin-top: 7px;
}
}
}
::v-deep .el-upload-list__item {
background: transparent;
border: none;
&:hover {
background: #ffedc5;
.item-status-label {
display: none;
}
.icon-close {
display: block;
}
}
&:focus {
outline: none;
}
}
.upload-tip {
color: #999999;
text-align: left;
margin-top: 5px;
}
.upload-display-img {
@extend .upload-img;
}
// ::v-deep .el-icon-upload {
// margin-top: 29px;
// }
.upload-btn {
margin: 20px 0;
text-align: center;
}
.upload-list-picture {
height: 100%;
.item-img {
vertical-align: middle;
width: 40px;
height: 40px;
float: left;
position: relative;
z-index: 1;
margin-left: -52px;
background-color: #fff;
}
.item-name {
width: 250px;
line-height: 40px;
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
transition: color 0.3s;
color: #333333;
font-size: 16px;
}
.item-status-label {
position: absolute;
top: 13px;
right: 5px;
.icon-upload-success {
color: #67c23a;
font-family: element-icons !important;
font-style: normal;
}
.icon-check .icon-circle-check {
color: #fff;
}
.icon-circle-check:before {
content: "\e720";
}
}
.icon-close {
display: none;
position: absolute;
top: 13px;
right: 5px;
cursor: pointer;
opacity: 0.75;
color: #999999;
font-weight: 600;
font-size: 14px;
font-style: normal;
font-family: element-icons !important;
//transform: scale(.7);
&:hover {
opacity: 1;
}
&:before {
content: "\e6db";
}
}
}
.upload-list-text {
height: 100%;
.item-img {
vertical-align: middle;
width: 24px;
height: 24px;
float: left;
position: relative;
z-index: 1;
margin-left: -33px;
background-size: 24px;
background-image: url("~@img/common/link.svg");
margin-top: 3px;
}
.item-name {
width: 250px;
line-height: 30px;
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
transition: color 0.3s;
color: #333333;
font-size: 16px;
}
.item-status-label {
position: absolute;
top: 3px;
right: 5px;
.icon-upload-success {
color: #67c23a;
font-family: element-icons !important;
font-style: normal;
}
.icon-check .icon-circle-check {
color: #fff;
}
.icon-circle-check:before {
content: "\e720";
}
}
.icon-close {
display: none;
position: absolute;
top: 3px;
right: 5px;
cursor: pointer;
opacity: 0.75;
color: #999999;
font-weight: 600;
font-size: 14px;
font-style: normal;
font-family: element-icons !important;
//transform: scale(.7);
&:hover {
opacity: 1;
}
&:before {
content: "\e6db";
}
}
}
.avatar {
@extend .upload-img;
position: relative;
border-radius: 10px;
overflow: hidden;
.upload-list__item-actions {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
cursor: default;
text-align: center;
color: #fff;
opacity: 0;
font-size: 20px;
background-color: rgba(0, 0, 0, 0.5);
transition: opacity 0.3s;
&::after {
display: inline-block;
content: "";
height: 100%;
vertical-align: middle;
}
span {
display: none;
cursor: pointer;
}
span + span {
margin-left: 15px;
}
.upload-list__item-delete {
position: static;
font-size: inherit;
color: inherit;
}
&:hover {
opacity: 1;
span {
display: inline-block;
}
}
}
}
}
.upload-drag-small-wrap {
// TODO: 这个地方样式有待调整,设计稿还没出来
::v-deep .el-upload-dragger {
width:146px;
height:146px;
border-radius:4px;
border: 1px solid $color-999;
&.hover {
border: 1px solid $color-999;
}
.el-upload__text {
width: 100%;
height: 100%;
}
}
}
</style>