说明:本例采用的是将图片视频全都先传到前端,再一次性提交给后端的思路,另外还做了一个上传前的预览
效果图
![在这里插入图片描述](https://img-blog.csdnimg.cn/f9d4775cd7024a779e399cce17cdf134.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBATGFsb19peQ==,size_20,color_FFFFFF,t_70,g_se,x_16)
vue页面代码
{{ collect_drawer_title }}
*产品图片/视频
保存
取消
data数据定义
data() {
//这里存放数据
return {
//视频展示框是否显示
videoVisible: false,
//图片展示框是否显示
dialogVisible: false,
//图片展示框对应url
dialogImageUrl: undefined,
//图片视频文件实际存放数组
pro_files: [],
//记录删除前的文件队列
update_src: [],
//控制抽屉
collect_drawer: false,
//抽屉标题
collect_drawer_title: undefined,
//实体类
collect: {
collect_id: undefined,
note: undefined,
price: undefined,
url: undefined,
source: undefined,
seller: undefined,
spec: undefined,
},
//表单校验
collectRules: {},
},
methods方法定义
methods: {
bindBeforeunLoad() {
window.onbeforeunload = this.perforresult;
},
unBindBeforeunLoad() {
window.onbeforeunload = null;
},
perforresult() {
return "";
},
deleteFile(index) {
//由于删除的时候
this.update_src = [];
for (var i = index + 1; i
this.update_src.push(this.$refs["video" + (i + 1)][0].src);
} else {
this.update_src.push(this.$refs["image" + (i + 1)][0].src);
}
}
this.pro_files.splice(index, 1);
this.$nextTick(() => {
for (var j = 0; j
if (this.pro_files[i].type === "video") {
this.$refs["video" + (i + 1)][0].src = this.update_src[j];
} else {
this.$refs["image" + (i + 1)][0].src = this.update_src[j];
}
break;
}
}
});
this.$refs.myfile.value = null;
},
/**开启全屏loading */
startLoading() {
this.loading = this.$loading({
lock: true,
text: "加载中...",
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.8)",
});
},
/**关闭loading**/
closeLoading() {
this.loading.close();
},
//用于关闭之后重置视频
resetVideo() {
this.$refs.showVideo.pause();
this.videoVisible = false;
},
//上传视频方法
uploadMyFile() {
const that = this;
//获取到 input file
let myfile = that.$refs.myfile;
//获取到上传的file文件
let file = myfile.files[0];
if (myfile.files[0]) {
//判断如果是视频
if (file.type.indexOf("video") != -1) {
//先将视频文件放到视频文件总数组
that.$set(that.pro_files, that.pro_files.length, {
file: file,
type: "video",
});
//因为我根据v-for总数组来渲染的,所以当我去设置数组,会有延迟渲染,取不到ref,用nextTick,等渲染结束再操作
that.$nextTick(() => {
//获取到当前上传的文件的虚拟dom,用filReader读取文件,并且把视频对应的file信息装填上去。
let video = that.$refs["video" + that.pro_files.length][0];
if (window.FileReader) {
var fr = new FileReader();
// fr.onprogress = function (e) {
// that.$set(
// that.pro_files[that.pro_files.length - 1],
// "loaded",
// (e.loaded / file.size).toFixed(2) * 100
// );
// };
fr.onloadend = function (e) {
//上传完成之后
video.src = e.target.result;
video.title = myfile.files[0].name;
};
fr.readAsDataURL(myfile.files[0]);
}
});
} else {
//与上传视频几乎同理,不再赘述
that.$set(that.pro_files, that.pro_files.length, {
file: file,
type: "image",
});
that.$nextTick(() => {
let image = that.$refs["image" + that.pro_files.length][0];
if (window.FileReader) {
var fr = new FileReader();
// fr.onprogress = function (e) {
// that.$set(
// that.pro_files[that.pro_files.length - 1],
// "loaded",
// (e.loaded / file.size).toFixed(2) * 100
// );
// };
fr.onloadend = function (e) {
image.src = e.target.result;
image.title = myfile.files[0].name;
};
fr.readAsDataURL(myfile.files[0]);
}
});
}
}
},
//预览视频
getPlayer(index) {
this.videoVisible = true;
this.$nextTick(() => {
console.log(window.height);
let source = this.$refs["video" + index][0].src;
this.$refs.showVideo.src = source;
this.$refs.showVideo.play();
});
},
//预览图片
getImage(index) {
this.dialogVisible = true;
this.$nextTick(() => {
let source = this.$refs["image" + index][0].src;
this.dialogImageUrl = source;
});
},
//打开抽屉
toAddCollect() {
this.collect_drawer = true;
this.collect_drawer_title = "新增款式";
},
//关闭抽屉
closeDrawer() {
this.collect_drawer = false;
},
/**
* 总体上传方法,思路是使用FormData来封装
* 文件:都在一个数组里,所以循环设置即可,用同一个名字后台好接收
* 表单:使用new Blob将json转成二进制文件
*/
saveOrUpdate() {
//我这里是因为图片或视频必填
if (this.pro_files.length == 0) {
this.$message.error({ message: "产品图片/视频文件不能为空" });
return;
}
var addData = new FormData();
this.pro_files.forEach((file) => {
addData.append("files", file.file);
});
addData.append(
"productCollect",
new Blob([JSON.stringify(this.collect)], { type: "application/json" })
);
//点上传之后打开全屏loading 不让操作
this.startLoading();
//防止用户关闭当前窗口,弹窗防止,如果你想改弹出来的文字说明...(oh,我也想改呢)
this.bindBeforeunLoad();
this.postRequest("/api/proCollect", addData, {
"Content-Type": "multipart/form-data",
}).then((res) => {
if (res.success) {
this.$message.success({ message: res.message });
this.collect_drawer = false;
}
this.closeLoading();
this.unBindBeforeunLoad();
});
},
}
css样式
::v-deep .el-upload-list--picture-card .el-upload-list__item {
width: 100px;
height: 100px;
}
::v-deep .el-upload--picture-card {
width: 100px;
height: 100px;
line-height: 110px;
}
#file_up {
background-color: #fbfdff;
border: 1px dashed #c0ccda;
text-align: center;
border-radius: 6px;
box-sizing: border-box;
width: 130px;
height: 130px;
line-height: 130px;
cursor: pointer;
vertical-align: top;
display: inline-block;
}
#file_up:hover,
#file_up:focus {
border-color: #409eff;
color: #409eff;
}
#file_up i {
font-size: 28px;
color: #8c939d;
}
.play-icon {
background: grey;
font-size: 20px;
cursor: pointer;
z-index: 100;
border-radius: 50%;
position: absolute;
left: 50%;
top: 50%;
margin-left: -16px;
margin-top: -16px;
color: #fff;
border: 6px solid grey;
}
.play-icon:hover {
opacity: 0.8;
}
.video_border {
margin-right: 10px;
margin-bottom: 10px;
}
.delete_icon {
color: #fff;
line-height: inherit;
font-size: 17px;
cursor: pointer;
}
.delete_icon:hover {
color: #f56c6c;
line-height: inherit;
font-size: 17px;
cursor: pointer;
}
.delete-div {
height: 0px;
position: absolute;
width: 100%;
background: #000;
opacity: 0.7;
bottom: 0;
text-align: center;
transition: height 0.2s ease 0s;
}
.crop:hover .delete-div {
height: 30%;
}
.rect_img {
position: absolute;
clip: rect(0px 130px 130px 0px);
width: 130px;
height: 130px;
margin-right: 10px;
cursor: pointer;
object-fit: cover;
}
后台接口
@PostMapping
@ApiOperation("新增款式收集")
public Result addCollect(
@RequestPart(value = "productCollect") ProductCollect productCollect,
@RequestPart(value = "files", required = true) MultipartFile[] files)
{
return cProductCollectService.addCollectFlow(productCollect,files);
}
记录学习日常,欢迎交流 ~
补充说明
其实我还做了一个进度条,就是你看到注释掉的那些,进度通过 fileReader 类的 onProgress 可以获取到,但效果并不是很理想(而且怪难看的) 这个例子里我没有做任何的文件处理(压缩、分片),可以自行完善 顺便一提,如果有很大的文件,这样上传要卡挺久的,所以我要改成一个个往后端传了,告辞
|