<template>
<div class="component-upload-image">
<el-upload
ref="uploadImage"
:action="uploadImgUrl"
:list-type="listType"
:headers="headers"
:data="params"
:on-success="handleUploadSuccess"
:before-upload="handleBeforeUpload"
:on-error="handleUploadError"
:on-remove="removeImage"
:on-preview="handlePreview"
:on-exceed="handleExceed"
:file-list="imageList"
:multiple="multiple"
:limit="limit"
:disabled="disabled"
:show-file-list="showFileList"
:accept="accept"
:class="{
'up-img-medium': size == 'medium',
'up-img-small': size == 'small',
'up-img-mini': size == 'mini',
'hide-add-icon': !addShow || fileList.length === limit || disabled,
'hide-del-icon': !delShow,
'hide-status-icon': disabled
}"
>
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible" title="" width="100%" append-to-body class="upload-prev-image">
<div class="image-box" ref="maskBox" @mousedown="onmousedownHandle">
<img v-if="dialogVisible" id="dialogImage" :src="dialogImageUrl" @wheel="handleWheel" @drag="handleDrag" class="pre-img" :class="{'pre-img-max': originalScale}" :style="{
'transform': 'scale(' + scaleTimes + ') rotate(' + rotateAngle + 'deg) translate(' + translateX + 'px, ' + translateY + 'px)','top': top + 'px', 'left': left + 'px',
}">
</div>
<div class="handle-list">
<div class="handle-list-btn">
<i class="el-icon-zoom-out" @click="handleReduce"></i>
<i class="el-icon-zoom-in" @click="handleAmplify"></i>
<i :class="originalScale ? 'el-icon-full-screen' : 'el-icon-c-scale-to-original'" @click="originalScale = !originalScale"></i>
<i class="el-icon-refresh-left" @click="handleAnticlockwise"></i>
<i class="el-icon-refresh-right" @click="handleClockwise"></i>
<i class="el-icon-arrow-left" v-if="preImgList.length > 1" @click="handleBack"></i>
<i class="el-icon-arrow-right" v-if="preImgList.length > 1" @click="handleNext"></i>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
/**
*
* 上传使用方法:
* <ImageUpload :limit="3" :fileList="form.imgList" :omen="1" />
*
* 预览使用方法:
* <ImageUpload :fileList="[detail.imgurl]" :disabled="true" />
*
* fileList 对应的值需为数组 1:['','',...] 或 2:[{name:'',url:''}]
*
*/
import { getToken } from "@/utils/auth";
import * as imageConversion from 'image-conversion';
export default {
props: {
// 是否多选
multiple: {
type: Boolean,
default: true
},
// value: text/picture/picture-card
listType: {
type: String,
default: "picture-card"
},
fileList: {
type: Array,
default: () => {
return []
}
},
limit: {
type: Number,
default: 10
},
// 上传格式
accept: {
type: String,
default: ".jpeg,.jpg,.png"
},
showFileList: {
type: Boolean,
default: true
},
// 默认是返回url数组 ['',''] 否则为返回对象 {name: '', url: ''}
returnObj: {
type: Boolean,
default: false
},
// 大小 medium small mini
size: {
type: String,
default: "mini"
},
// 是否显示删除按钮
delShow: {
type: Boolean,
default: true
},
// 是否显示添加按钮
addShow: {
type: Boolean,
default: true
},
// 单张图片大小 默认10m 小于1m用小数点代替 例:500kb == 0.5
omen: {
type: Number,
default: 10
},
disabled: {
type: Boolean,
default: false
},
// 上传时需要的参数
params: {
type: Object,
default: function () {
return {}
}
},
addressUrl: {
type: String,
default: '/tool/oss/upload'
},
// 预览开启水印(false禁止true允许)
WaterMarkShow: {
type: Boolean,
default: false
},
// 预览水印配置
objmsg: {
type: Object,
default: function () {
return {
rotate: 20, //旋转角度 默认20
fontcolor: "255, 255, 255, 0.8", //字体颜色 rgba类型 默认 255, 255, 255, 0.2(黑色)
density: 3, //稠密度 数值越大,水印越多
str: ["XXX专用"], //水印文字 数组类型 最大三行(即lingth<=3)[必传]
}
}
},
// 图片预览数组
previewList: {
type: Array,
default: () => {
return []
}
},
// 压缩宽度
compressWidth: {
type: Number,
default: 300
},
// 压缩高度
compressHeight: {
type: Number,
default: 300
},
// 是否压缩尺寸
compressSize: {
type: Boolean,
default: false
},
},
data() {
return {
OSSURL: process.env.VUE_APP_BASE_FILEURL,
dialogVisible: false,
uploadImgUrl: process.env.VUE_APP_BASE_API + this.addressUrl, // 上传的图片服务器地址
headers: {
Authorization: "Bearer " + getToken(),
// 'X-Route-Label': 'quanzai'
},
dialogImageUrl: '',
imageList: [],
count: 0,
scaleTimes: 1, // 缩放倍数
rotateAngle: 0, // 旋转角度
previewIndex: 0, // 当前预览图片下标
originalScale: true, // 是否原始比例
translateX: 0, // 横向平移距离
translateY: 0, // 纵向平移距离
preImgList: [],
top: 0,
left: 0,
};
},
watch: {
fileList: {
handler(val) {
if (val) {
if (this.fileList.length > 0 && !this.fileList[0].url && this.count === 0) { // 默认只执行一次,避免显示重复
this.formatFileList();
this.count = 1;
}
} else {
this.imageList = [];
return [];
}
// if (!val.length && this.$refs.uploadImage) {
// this.$refs.uploadImage.clearFiles();
// }
},
deep: true,
immediate: true
},
previewList: {
handler(val) {
if(this.previewList.length) {
this.preImgList = JSON.parse(JSON.stringify(this.previewList));
}else {
this.preImgList = [];
}
}
}
},
created() {
// if (this.fileList.length > 0 && !this.fileList[0].url) {
// this.formatFileList()
// }
},
methods: {
// 清空图片
clearFiles() {
this.$refs.uploadImage.clearFiles();
},
// 格式化图片
formatFileList() {
this.fileList.forEach(v => {
let params = {
name: '',
url: v
}
if (typeof v == 'object'){
params.id = v.id;
params.url = v.qrcodeUrl;
}
this.imageList.push(params)
})
console.log(this.imageList)
},
// 移除图片
removeImage(e, a) {
this.$emit("on-remove", e);
let index = this.fileList.findIndex(v => v == e.url)
this.fileList.splice(index, 1)
if(!this.fileList.length) {
this.$refs.uploadImage.clearFiles();
}
console.log(e, a)
},
// 上传成功
handleUploadSuccess(res, e) {
if (this.returnObj) { // 返回对象
this.fileList.push({
name: e.name,
url: this.OSSURL + res.msg
})
}else { // 返回地址数据
this.fileList.push(this.OSSURL + res.msg)
}
this.$emit("on-success", this.OSSURL + res.msg);
this.loading.close();
},
// 获取文件后缀
getFileSuffix(fileName) {
return fileName.match(/\.\w+$/)[0].toLowerCase();
},
// 上传中
handleBeforeUpload(file) {
if (!this.accept.includes(this.getFileSuffix(file.name))) {
this.$message.error("请上传【" + this.accept + "】格式的图片");
return false;
}
if (file.size / 1024 > (this.omen * 1024)) {
let unit = '', om = ''
if (this.omen < 1) {
unit = 'KB'
om = this.omen * 1000
} else {
unit = 'MB'
om = this.omen
}
this.$message.error(`图片大小不能超过${om}${unit}`);
return false;
}
this.loading = this.$loading({
lock: true,
text: "上传中",
background: "rgba(0, 0, 0, 0.7)",
});
let index = this.accept.indexOf('.png');
if(this.returnObj || index == -1) return;
return new Promise((resolve, reject) => {
let _URL = window.URL || window.webkitURL;
let isLt1M = file.size / 1024 / 1024 > 1; // 判定图片大小是否大于1MB
// 这里需要计算出图片的长宽
let img = new Image();
img.onload = ()=> {
if(this.compressSize) {
file.width = img.width; // 获取到width放在了file属性上
file.height = img.height; // 获取到height放在了file属性上
let valid = img.width > this.compressWidth || img.height > this.compressHeight; // 图片宽度/高度超出
// 这里我只判断了图片的宽度,compressAccurately有多个参数时传入对象
if (valid || isLt1M) {
imageConversion.compressAccurately(file, { size: 100, width: this.compressWidth, height: this.compressHeight }).then(res => {
resolve(res);
})
} else resolve(file);
}else {
imageConversion.compress(file, 0.85).then(res=>{
resolve(res);
})
}
}
// 需要把图片赋值
img.src = _URL.createObjectURL(file);
})
},
// 上传失败
handleUploadError() {
this.$message({
type: "error",
message: "上传失败",
});
this.loading.close();
},
// 超出提示
handleExceed() {
this.$message({
type: "error",
message: "超出上传文件数量,最多上传" + this.limit + '张',
});
},
// 预览图片
handlePreview(file) {
this.loading = this.$loading({
lock: true,
text: "加载中",
background: "rgba(0, 0, 0, 0.7)",
});
this.dialogImageUrl = '';
if(!this.WaterMarkShow) {
if(file.response) {
this.dialogImageUrl = this.editFormatFileUrl(file.response.msg);
}else {
this.dialogImageUrl = file.url;
}
};
this.dialogVisible = true;
if(file.resetIndex !== 1) { // 点击图片预览
this.previewIndex = 0;
}
if(!this.WaterMarkShow) {
this.loading.close();
this.$nextTick(() => {
let node = document.getElementById('dialogImage');
this.left = (window.innerWidth/ 2) - (node.width/2) < 0 ? 0 : (window.innerWidth/ 2) - (node.width/2);
this.top = (window.innerHeight/ 2) - (node.height/2) < 0 ? 0 : (window.innerHeight/ 2) - (node.height/2);
})
return false;
};
var drawWaterMark = {};
drawWaterMark.init = (objmsg) => {
console.log('--------------',objmsg)
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
img.src = objmsg.imgpath;
img.setAttribute("crossOrigin", 'Anonymous');
img.onload = () => {
//绘制和图片大小相同的canvas
canvas.width = img.width;
canvas.height = img.height;
this.left = (window.innerWidth/ 2) - (img.width/2) < 0 ? 0 : (window.innerWidth/ 2) - (img.width/2);
this.top = (window.innerHeight/ 2) - (img.height/2) < 0 ? 0 : (window.innerHeight/ 2) - (img.height/2);
//canvas绘制图片,0 0 为左上角坐标原点
ctx.drawImage(img, 0, 0);
//绘制水印
if (objmsg.rotate != undefined && objmsg.rotate != null) {//旋转角度[默认20]
ctx.rotate((Math.PI / 120) * -objmsg.rotate);
} else {
ctx.rotate((Math.PI / 120) * -20);
};
var fontsize = 20;
// if (objmsg.fontsize != undefined && objmsg.fontsize != null) { //字体大小[默认20px]
// fontsize = objmsg.fontsize;
// };
if (img.width >= 3456) { // 根据图片大小改变水印文案字体大小
fontsize = 50;
} else if (img.width >= 2700) {
fontsize = 30;
} else if (img.width >= 2000) {
fontsize = 26;
} else if (img.width >= 1436) {
fontsize = 20;
} else if (img.width >= 800) {
fontsize = 12;
} else if (img.width >= 500) {
fontsize = 10;
} else {
fontsize = 8;
};
ctx.font = fontsize + "px Microsoft Yahei";
var fontcolor = '255, 255, 255, 0.2';
if (objmsg.fontcolor != undefined && objmsg.fontcolor != null) {//字体颜色透明度[默认白色]
fontcolor = objmsg.fontcolor;
};
ctx.fillStyle = "rgba(" + fontcolor + ")";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
var density = 3;
if (objmsg.density != undefined && objmsg.density != null) {//稠密度[默认3]
density = objmsg.density
};
let maxpixel;
let minPixel;
if(img.width > img.height) {
maxpixel = img.width;
minPixel = img.height;
}else {
maxpixel = img.height;
minPixel = img.width
};
for (var i = -1000; i < maxpixel; i += minPixel / density) {
for (var k = 0; k < maxpixel; k += minPixel / density) {
var str = objmsg.str;
if (str.length == 1) {
ctx.fillText(str[0], i, k);
} else if(str.length==2){
ctx.fillText(str[0], i, k);
ctx.fillText(str[1], i, k + (fontsize-0+5));//多行
} else if (str.length == 3 || str.length > 3) {
ctx.fillText(str[0], i, k);
ctx.fillText(str[1], i, k + (fontsize - 0 + 5));//多行
ctx.fillText(str[2], i, k + (fontsize*2 - 0 + 5));//多行
}
}
};
var base64 = canvas.toDataURL("image/jpeg");//添加过水印的base64图片
if (objmsg.domid != undefined && objmsg.domid != null) {//id图片
let url1 = this.getBase64URL(base64)
this.dialogImageUrl = url1;
let preUrl = '';
if(file.response) {
preUrl = this.editFormatFileUrl(file.response.msg);
}else {
preUrl = file.url;
}
let preIndex = this.preImgList.findIndex(v => v == preUrl);
this.preImgList[preIndex] = this.dialogImageUrl;
this.loading.close();
};
if (objmsg.cb != undefined && objmsg.cb != null) {//回调函数
objmsg.cb(base64);//回调函数
this.loading.close();
}
}
};
let that = this;
var xhr = new XMLHttpRequest();
xhr.open('get', file.url, true);
// 设置请求头(这一步得设置不然oss图片还是跨域)
xhr.setRequestHeader("Cache-Control", "no-cache");
xhr.responseType = 'blob';
xhr.onload = function () {
if (this.status == 200) {
let imgFile = URL.createObjectURL(this.response);
that.objmsg.imgpath = imgFile; //需要添加水印图片路径 [必传]
that.objmsg.domid = "dialogImage"; //图片dom的id 用来更换添加水印后的图片
drawWaterMark.init(that.objmsg);
}
};
xhr.send();
},
// 将文件转为url格式
getBase64URL(pic) {
const blob = this.base64ImgtoFile(pic)
const blobUrl = window.URL.createObjectURL(blob);
return blobUrl
},
base64ImgtoFile (dataurl, filename = 'file') {
//将base64格式分割:['data:image/png;base64','XXXX']
const arr = dataurl.split(',')
// .*? 表示匹配任意字符到下一个符合条件的字符 刚好匹配到:
// image/png
const mime = arr[0].match(/:(.*?);/)[1] //image/png
//[image,png] 获取图片类型后缀
const suffix = mime.split('/')[1] //png
const bstr = atob(arr[1]) //atob() 方法用于解码使用 base-64 编码的字符串
let n = bstr.length
const u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], `${filename}.${suffix}`, {
type: mime
})
},
// 清空预览数组
handleClearList() {
this.imageList = [];
},
// 预览图片鼠标滚轮事件
handleWheel(e) {
if(e.deltaY < 0) { // 向下滚动 放大图片
this.scaleTimes = this.accAdd(this.scaleTimes, 0.015);
}else { // 向上滚动 缩小图片
if(this.scaleTimes > 0.2) {
this.scaleTimes = this.accSub(this.scaleTimes, 0.015);
}
}
},
// 缩小按钮操作
handleReduce() {
if(this.scaleTimes > 0.2) {
this.scaleTimes = this.accSub(this.scaleTimes, 0.2);
}
},
// 放大按钮操作
handleAmplify() {
this.scaleTimes = this.accAdd(this.scaleTimes, 0.2);
},
// 逆时针旋转
handleAnticlockwise() {
this.rotateAngle = this.accSub(this.rotateAngle, 90);
},
// 顺时针旋转
handleClockwise() {
this.rotateAngle = this.accAdd(this.rotateAngle, 90);
},
// 上一张
handleBack() {
if(this.previewIndex == 0) {
this.previewIndex = this.preImgList.length - 1;
}else {
let index = this.preImgList.findIndex(v => v == this.dialogImageUrl);
this.previewIndex = index - 1;
}
console.log(this.preImgList, this.previewIndex)
this.scaleTimes = 1;
this.rotateAngle = 0;
this.originalScale = true;
this.handlePreview({ url: this.preImgList[this.previewIndex], resetIndex: 1 });
},
// 下一张
handleNext() {
if(this.previewIndex == this.preImgList.length - 1) {
this.previewIndex = 0;
}else {
let index = this.preImgList.findIndex(v => v == this.dialogImageUrl);
this.previewIndex = index + 1;
}
console.log(this.preImgList, this.previewIndex)
this.scaleTimes = 1;
this.rotateAngle = 0;
this.originalScale = true;
this.handlePreview({ url: this.preImgList[this.previewIndex], resetIndex: 1 });
},
handleDrag(e) {
// console.log(e.clientX, e.clientY)
// this.translateX = e.clientX;
// this.translateY = e.clientY;
},
// 鼠标按下
onmousedownHandle(e) {
this.$refs.maskBox.onmousemove = (el) => {
const ev = el || window.event; // 阻止默认事件
ev.preventDefault();
this.left += ev.movementX;
this.top += ev.movementY;
};
this.$refs.maskBox.onmouseup = () => {
// 鼠标抬起时将操作区域的鼠标按下和抬起事件置为null 并初始化
this.$refs.maskBox.onmousemove = null;
this.$refs.maskBox.onmouseup = null;
};
if (e.preventDefault) {
e.preventDefault();
} else {
return false;
}
},
}
};
</script>
<style lang="scss">
.up-img-medium {
.el-upload--picture-card {
line-height: 160px;
}
.el-upload--picture-card, .el-upload-list--picture-card .el-upload-list__item {
width: 150px;
height: 150px;
}
}
.up-img-small {
.el-upload--picture-card {
line-height: 110px;
}
.el-upload--picture-card, .el-upload-list--picture-card .el-upload-list__item {
width: 100px;
height: 100px;
}
}
.up-img-mini {
.el-upload--picture-card {
line-height: 66px;
}
.el-upload--picture-card, .el-upload-list--picture-card .el-upload-list__item {
width: 60px;
height: 60px;
}
.el-icon-plus {
font-size: 23px;
}
.el-upload-list--picture-card .el-upload-list__item-actions span + span {
margin-left: 8px;
}
}
.hide-add-icon {
.el-upload {
display: none !important;
}
}
.hide-del-icon {
.el-upload-list__item-delete {
display: none !important;
}
}
.hide-status-icon {
.el-upload-list__item-status-label {
display: none !important;
}
}
.upload-prev-image {
.el-dialog {
background: transparent;
box-shadow: none;
margin-top: 0 !important;
margin-bottom: 0 !important;
}
.el-dialog__close {
color: #fff;
font-size: 35px;
position: fixed;
top: 20px;
right: 20px;
z-index: 10;
}
.el-dialog__header{
padding: 0 !important;
}
.el-dialog__footer{
display: none !important;
}
.el-dialog__body{
height: 100vh;
max-height: 100vh;
padding: 0 !important;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
}
.el-upload-list--picture-card {
line-height: 1.2;
}
.pre-img{
transition: transform 0.3s ease 0s;
display: block;
margin: 0 auto;
}
.pre-img-max{
max-width: 100%;
max-height: 100%;
}
.handle-list{
position: absolute;
left: 50%;
bottom: 30px;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
width: 282px;
height: 44px;
padding: 0 23px;
background-color: #606266;
border-color: #FFFFFF;
border-radius: 22px;
.handle-list-btn{
position: relative;
height: 100%;
font-size: 23px;
display: flex;
justify-content: space-around;
align-items: center;
z-index: 1;
color: #FFFFFF;
opacity: 0.8;
i{
cursor: pointer;
}
}
}
.el-loading-mask{
z-index: 99999 !important;
}
.image-box {
position: relative;
width: 100%;
height: 100vh;
margin: 0 auto;
border: 1px solid #333;
overflow: hidden;
}
.image-box img {
position: absolute;
cursor: pointer;
}
</style>
随手记:上传组件,类型支持图片,视频;功能支持查看,放大缩小,旋转,拖拽,添加水印
最新推荐文章于 2024-07-18 17:31:30 发布