效果图
上传后
图片上传
视频上传
项目使用vue+element-ui,实现了表单多图上传图片,上传视频,以及表单图片回显,视频回显,表格渲染图片等功能
效果图 上传后图片可回显,视频可播放,,这时候全部缓存在页面,并没有提交到后端服务器,只要到用户提交的那一步才确定上传,减低不必要的服务器开支
图片上传前端缓存base64方便回显,以及后台上传,视频上传则使用file类型去上传(base64对视频编码会导致请求参数过长)
<!--
描述:图片上传, 基于 element-ui 组件
-->
<template>
<div>
<div class="upload">
<div
class="img_mode"
v-for="(item, index) in path_list"
:key="index"
:style="'height:' + height + ';width:' + width"
@mouseenter.stop="over(index)"
@mouseleave="out"
>
<img :src="item.path" />
<transition name="el-fade-in-linear">
<div v-show="curIndex == index" class="transition-box">
<div class="mask">
<i class="el-icon-delete" @click="remove(index)"></i>
<i
class="el-icon-zoom-in"
@click="enlarge(index)"
v-if="bigBox"
></i>
</div>
</div>
</transition>
</div>
<div
class="select_mode"
:style="'height:' + height + ';width:' + width"
v-if="isLimit"
v-loading="load"
>
<input
type="file"
@change="change($event)"
ref="file"
:multiple="isMultiple"
/>
<img
:src="selectImgPath || selectImg"
:width="selectImgWidth || '50%'"
/>
</div>
</div>
<el-dialog :visible.sync="isShow" center>
<div class="big_img_mode">
<img :src="bigImg" />
</div>
</el-dialog>
</div>
</template>
<script>
//这里是图片上传时候的图标,可以自行替换
import selectImg from "../../assets/img/selectedImg.png";
export default {
props: {
height: String,
width: String, // 组件预览图片大小
selectImgPath: String, // 未选择图片时显示的图片
selectImgWidth: String,
bigBox: Boolean, // 是否显示图片放大按钮
/* 多图上传 */
isMultiple: Boolean, // 是否可多选图片
limit_size: Number, // 限制上传数量
quality: Number, // 图片压缩率
limit: Number, // 图片超过 (limit * 1024)kb 将进行压缩
isCompress: Boolean, // 是否开启图片压缩
/* 单图上传 */
isChangeUpload: false, // 是否选择文件后触发上传
action: "", // 单图上传路径,
param: "", // 上传的参数名
data: Object, // 单图上传附带的参数
success: Function, // 单图上传成功后的回调函数
},
data() {
return {
selectImg,
path_list: [],
curIndex: -1,
bigImg: "",
isShow: false,
isLimit: true,
load: false,
};
},
watch: {
path_list() {
if (this.path_list.length >= this.limit_size) {
this.isLimit = false;
} else {
this.isLimit = true;
}
this.load = false;
this.curIndex = -1;
},
},
mounted() {},
methods: {
// 鼠标移入
over(index) {
this.curIndex = index;
},
// 鼠标移出
out() {
this.curIndex = -1;
},
// 选择图片
change() {
this.load = true;
var That = this;
let fileList = this.$refs.file.files;
if (!fileList.length) {
this.load = false;
return;
}
for (var i = 0; i < fileList.length; i++) {
var file_temp = fileList[i];
let name = file_temp.name.toLowerCase();
if (!/\.(jpg|jpeg|image|img|png|bmp|)$/.test(name)) {
this.$message.warning("请上传图片");
this.clean();
}
let reader = new FileReader(); //html5读文件
reader.fileName = file_temp.name;
reader.readAsDataURL(file_temp);
reader.onload = (data) => {
//读取完毕后调用接口
// 图片压缩
this.canvasDataURL(
data.currentTarget.result,
{ fileSize: data.total, quality: this.quality },
(baseCode) => {
if (this.isChangeUpload) {
this.changeUpload(baseCode, reader.fileName);
} else {
this.path_list.push({
path: baseCode,
fileName: reader.fileName,
});
}
}
);
};
}
this.$emit("change", this.path_list);
},
// 移除图片
remove(index) {
this.path_list.splice(index, 1);
},
//清空图片
clean() {
this.path_list = [];
},
//后台添加图片
addPathList(urls) {
console.log(urls);
let arr = urls.split(",");
if (arr.length == 1) {
let obj = { path: this.$common.getUrl() + arr[0] };
this.path_list.splice(0, 1, obj);
} else {
arr.forEach((item, index) => {
let obj2 = { path: this.$common.getUrl() + item };
this.path_list.splice(index, 1, obj2);
});
}
},
//后台添加图片
addUrlPathList(urls) {
console.log(urls);
let arr = urls.split(";");
console.log("数组" + arr);
if (arr.length == 1) {
let obj = { path: this.$common.getUrl() + arr[0] };
this.path_list.splice(0, 1, obj);
} else {
arr.forEach((item, index) => {
let obj2 = { path: this.$common.getUrl() + item };
this.path_list.splice(index, 1, obj2);
});
}
},
//重新图片
clearImgs() {
this.path_list = [];
},
// 放大图片
enlarge(index) {
this.isShow = true;
this.bigImg = this.path_list[index].path;
},
// 单图上传
changeUpload(baseCode, fileName) {
let formData = new FormData();
formData.append(this.param, baseCode);
formData.append("fileName", fileName);
for (var item in this.data) {
formData.append(item, this.data[item]);
}
this.$axios
.post(this.action, formData)
.then((response) => {
if (response.data.message == "succ") {
this.success(); // 上传成功后的回调函数
}
this.load = false;
})
.catch((e) => {
console.log(e);
this.load = false;
});
},
/**
* @uploadPath 上传的路径
* @path_list base64 集合, 为空则调用 this.path_list
* @callback 上传成功后的回调函数, 返回上传成功后的路径
*/
upload(uploadPath, path_list, callback) {
var formData = new FormData();
if (!path_list) {
this.path_list.forEach((item) => {
formData.append(
"files",
this.convertBase64UrlToBlob(item.path),
item.fileName
);
});
} else {
path_list.forEach((item) => {
formData.append(
"files",
this.convertBase64UrlToBlob(item.path),
item.fileName
);
});
}
let headers = { headers: { "Content-Type": "multipart/form-data" } };
this.$axios
.post(uploadPath, formData, headers)
.then((response) => {
if (response.data.message == "succ") {
// 回调函数返回上传成功后的路径
callback("succ", response.data.result);
} else {
this.$message.error("文件上传失败");
callback("error");
}
})
.catch((error) => {
if (error == "timeout") {
this.$message.error("文件上传超时");
}
this.$message.error("文件上传异常");
console.log(error);
callback("error");
});
},
getName() {
return this.path_list.fileName;
},
// 获取base64 集合
getPaths() {
let paths = [];
if (this.path_list.length == 1) {
return this.path_list[0].path;
} else if (this.path_list.length == 0) {
return "";
} else {
for (var i = 0; i < this.path_list.length; i++) {
paths.push(this.path_list[i].path);
// if(i == 0){
// paths = this.path_list[i].path
// }else{
// arr.push(this.path_list[i].path)
// }
}
}
return paths;
},
photoCompress() {},
// 图片压缩
canvasDataURL(path, obj, callback) {
var suffix = path.match(/\/(\S*);/)[1];
var img = new Image();
img.src = path;
img.onload = function () {
var that = this;
// 默认按比例压缩
var w = that.width,
h = that.height,
scale = w / h;
w = obj.width || w;
h = obj.height || w / scale;
var quality = 0.7; // 默认图片质量为0.7
this.limit = this.limit ? this.limit : 1; // 默认为超过1M 进行压缩
//生成canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
// 创建属性节点
var anw = document.createAttribute("width");
anw.nodeValue = w;
var anh = document.createAttribute("height");
anh.nodeValue = h;
canvas.setAttributeNode(anw);
canvas.setAttributeNode(anh);
ctx.drawImage(that, 0, 0, w, h);
// 图像质量
if (obj.quality && obj.quality <= 1 && obj.quality > 0)
quality = obj.quality;
var base64 = "";
if (this.isCompress) {
// quality值越小,所绘制出的图像越模糊
base64 =
obj.fileSize > this.limit * 1024
? canvas.toDataURL("image" + suffix, quality)
: canvas.toDataURL("image" + suffix);
} else {
base64 = canvas.toDataURL("image/" + suffix, quality);
}
// 回调函数返回base64的值
callback(base64);
};
},
// base64 转 blob 对象
convertBase64UrlToBlob(urlData) {
//去掉url的头,并转换为byte
var bytes = window.atob(urlData.split(",")[1]);
//处理异常,将ascii码小于0的转换为大于0
var ab = new ArrayBuffer(bytes.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], { type: "image/png" });
},
},
};
</script>
<style scoped="upload">
.upload {
width: 100%;
overflow: hidden;
display: flex;
flex-wrap: wrap;
padding: 10px 4px;
box-sizing: border-box;
}
.select_mode {
width: 120px;
height: 120px;
display: flex;
justify-content: center;
align-items: center;
/* border: 1px dashed #cfcfcf; */
border-radius: 10px;
position: relative;
box-sizing: border-box;
/* background-color: #fcfbfe; */
}
.select_mode:hover {
/* border: 1px dashed #00A2E9; */
}
.select_mode img {
width: 100%;
position: absolute;
z-index: 1;
cursor: pointer;
}
.select_mode input[type="file"] {
width: 100%;
height: 100%;
opacity: 0;
cursor: pointer;
z-index: 2;
}
.img_mode {
margin-right: 10px;
margin-bottom: 10px;
box-shadow: 0px 1px 5px #cccccc;
position: relative;
}
.mask {
background: rgb(0, 0, 0, 0.5);
height: 100%;
width: 100%;
position: absolute;
top: 0px;
left: 0px;
display: flex;
justify-content: center;
align-items: center;
}
.img_mode,
.img_mode img {
height: 120px;
max-width: 200px;
}
.mask i {
font-family: element-icons !important;
color: #ffffff;
font-size: 25px;
margin: 0px 8px;
cursor: pointer;
}
.big_img_mode {
text-align: center;
width: 100%;
height: 100%;
max-height: 400px;
}
.big_img_mode img {
max-width: 400px;
max-height: 400px;
}
</style>
使用方法:
引入上面的模块,然后在 components里面注册,即可用标签使用
import Upload from "../../../components/utils/Upload.vue";
components: { editor: editor, Upload },
<el-form-item label="轮播图:" prop="img">
//这里面的参数在上面的模块里面去看,ref就是你绑定的对象
<upload
ref="addBanner"
:limit_size="4"
:isCompress="true"
:bigBox="true"
></upload>
</el-form-item>
上传之前获取base64图片编码,存到表单中,然后直接提交给后台
this.addForm.img = this.$refs.addBanner.getPaths();
视频上传注意:这里上传后会是一段base64字符串,如果是多图,会是一段字符串数组,后端可以直接用jsonArray接收
//表单
<el-form-item label="视频:" prop="video">
<video class="video" controls v-if="videoShow" :src="videoShow" />
<input
ref="videoFile"
@change="fileChange($event)"
type="file"
id="video_file"
accept="video/*"
/>
</el-form-item>
//方法
fileChange(e) {
var files = e.target.files || e.dataTransfer.files;
if (!files.length) return;
let name = files[0].name.toLowerCase();
if (
!/\.(avi|wmv|mpeg|mp4|mov|mkv|flv|f4v|m4v|rmvb|rm|3gp|dat|ts|mts|vob)$/.test(
name
)
) {
this.$message.warning("请上传视频");
return;
}
if (files[0].size > 1024 * 1024 * 20) {
this.$message.warning("视频大小不能大于20M");
return;
}
//这里是file文件
this.addForm.video = files[0];
var reader = new FileReader();
reader.readAsDataURL(files[0]);
reader.onload = () => {
//这里是一段base64,用于视频回显用
this.videoShow = reader.result;
};
},
表格渲染相关
表格内多图渲染
这段代码的意思是渲染当前行img字段的列表,遍历生成img图片标签
因为img字段返回的是一个数组,不能直接渲染,所以加上一个JSON.parse解析一下
另外加了一个判断最多渲染不超过三张,防止界面过长超出
<el-table-column prop="name" label="轮播图" align="center" width="300px">
<template slot-scope="scope">
<el-image
v-for="(item,index) in JSON.parse(scope.row.img)"
:src="item"
v-if="index<3"
style="width: 30%;height: 100%;margin-right: 5px"
></el-image>
</template>
</el-table-column>
以上为个人经验,希望能给大家一个参考,也希望大家多多支持软件开发网。