<template>
<div>
<form :class="uploadBtnClass" class="image-upload-form" v-show="!hasImage" method="post" enctype="multipart/form-data" action="">
<input type="file" class="image-upload-form-input" :id="'upload_btn_' + formID" @change="change" title=" ">
<slot></slot>
</form>
<div class="image-crop-wrap" ref="imgShape" v-show="hasImage" :id="'upload_crop_box_' + formID">
<div v-if="crop">
<div class="crop-cloth"></div>
<div class="image-crop-content fade">
<div class="image-crop-content-main">
<div class="img-origin-div">
<img class="img-origin" :id="'img_origin_'+formID" alt="Picture"/>
</div>
<p class="txt">头像预览</p>
<div class="img-preview-small-div" :class="previewClass" :id="'preview_'+formID">
</div>
<div style="clear:both"></div>
<div class="docs-buttons image-crop-foot">
<span class="btn-grey quit" @click="cancel">{{cropBtn.cancel}}</span>
<span class="btn-red save" @click="handleImageCropped">
<a class="clip-down">{{cropBtn.ok}}</a>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style lang="scss">
@import "ossUpload.css";
</style>
<script>
import $ from "jquery";
import props from "./props";
import {getOssUploadToken} from "@/service/getData";
import cropper from 'cropper';
import Promise from 'es6-promise';
Promise.polyfill();
export default {
name: "",
props: props,
data() {
return {
hasImage: false,
formID: (Math.random() * 10000 + "").split(".")[0],
imageCropper: "", //cropper操作对象
options: {
aspectRatio: this.aspectRatio,
dragCrop: false,
viewMode: 1,
zoomable: false,
movable: false,
zoomOnTouch: false,
zoomOnWheel: false
}
};
},
watch: {},
mounted() {
$("body").append($(".image-crop-wrap"));
},
methods: {
_dispatch(name, res) {
this.$emit && this.$emit(name, res);
},
//初始化剪裁工具
initCropper: function () {
let that = this;
let o = "#upload_crop_box_" + that.formID;
that.imageCropper = $(o).find("#img_origin_" + that.formID);
let $dataX = $(o).find("#dataX");
let $dataY = $(o).find("#dataY");
let $dataHeight = $(o).find("#dataHeight");
let $dataWidth = $(o).find("#dataWidth");
let $dataScaleX = $(o).find("#dataScaleX");
let $dataScaleY = $(o).find("#dataScaleY");
this.options.crop = function (e) {
$dataX.val(Math.round(e.x));
$dataY.val(Math.round(e.y));
$dataHeight.val(Math.round(e.height));
$dataWidth.val(Math.round(e.width));
$dataScaleX.val(e.scaleX);
$dataScaleY.val(e.scaleY);
};
this.options.preview = "#preview_" + that.formID;
/*cropper*/
// this.imageCropper.cropper(this.options);
},
//选择了上传的文件
change: function (e) {
if (URL) {
this.imageCropper = $("#img_origin_" + this.formID);
let uploadedImageURL;
let files = e.target.files;
if (files && files.length) {
let file = files[0];
let fileExt = file.name.substring(file.name.lastIndexOf(".") + 1);
const extensionsArr = this.extensions.split(",");
if (extensionsArr.length > 1) {
var reg = new RegExp("^[" + extensionsArr.join("|") + "]+$", "i");
if (!reg.test(fileExt)) {
this._dispatch("promptTip", {type: "warning", info: "请上传图片文件"});
return;
} else {
if (this.crop) {
this.initCropper();
if (uploadedImageURL) {
URL.revokeObjectURL(uploadedImageURL);
}
uploadedImageURL = URL.createObjectURL(file);
this.imageCropper
.cropper("destroy")
.attr("src", uploadedImageURL)
.cropper(this.options);
this.hasImage = true;
} else {
this.getPolicyOfOSS(file);
}
document.querySelector("#upload_btn_" + this.formID).value = "";
}
}
}
}
},
//取消上传
cancel: function () {
this.hasImage = false;
},
//处理剪裁的文件为Blob对象
handleImageCropped: function () {
let result = this.imageCropper.cropper("getCroppedCanvas");
this.getPolicyOfOSS(
this.convertBase64UrlToBlob(result.toDataURL("image/jpeg"))
);
},
//将base64编码转换为Blob
convertBase64UrlToBlob: function (urlData) {
let bytes = window.atob(urlData.split(",")[1]); //去掉url的头,并转换为byte
//处理异常,将ascii码小于0的转换为大于0
let ab = new ArrayBuffer(bytes.length);
let ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], {type: "image/jpeg", filename: "upload.jpg"});
},
//url转为base64编码
convertImgToBase64: function (url, callback, outputFormat) {
url+='?random='+Math.random();
let canvas = document.createElement("CANVAS"),
ctx = canvas.getContext("2d"),
img = new Image();
img.crossOrigin = "";
// img.setAttribute("crossOrigin", "anonymous");
img.onload = function () {
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL(outputFormat || "image/png");
callback.call(this, dataURL);
canvas = null;
};
img.src = url;
},
//获取上传所需的TOKEN
getPolicyOfOSS: function (blob,fn) {
let that = this;
let data = {
type: this.type,
fetchNum: this.fetchNum,
data: this.bodyData
};
//获取OSS
if(this.isTemp){
$.ajax({
type: "POST",
url: that.url,
headers: {
"Access-Token": that.accessToken,
"Content-Type": "application/json;charset=UTF-8"
},
data: JSON.stringify(data),
success: function (res) {
if (res.code === 200) {
let obj = res.data.token;
let data = {
key: obj.dir,
policy: obj.policy,
OSSAccessKeyId: obj.accessid,
success_action_status: "200",
callback: obj.callback,
signature: obj.signature,
verify: obj.verify,
host: obj.host
};
that.uploadImageToOSS(data, blob,fn);
} else {
}
}
});
}else{
$.ajax({
type: "GET",
url: that.url,
headers: {
"Access-Token": that.accessToken,
"Content-Type": "application/json;charset=UTF-8"
},
success: function (res) {
let obj = res;
let data = {
key: obj.dir,
policy: obj.policy,
OSSAccessKeyId: obj.accessid,
success_action_status: "200",
callback: obj.callback,
signature: obj.signature,
verify: obj.verify,
host: obj.host
};
that.uploadImageToOSS(data, blob,fn);
}
});
}
},
//上传OSS协议
uploadImageToOSS: function (option, blob = "",fn) {
let that = this;
return new Promise((resolve, reject) => {
let formData = new FormData(document.forms[0]); //这里连带form里的其他参数也一起提交了,如果不需要提交其他参数可以直接FormData无参数的构造函数
formData.append("key", option.key);
formData.append("policy", option.policy);
formData.append("OSSAccessKeyId", option.OSSAccessKeyId);
formData.append("success_action_status", option.success_action_status);
formData.append("Cache-Control", 'no-cache');
if(option.callback) formData.append("callback", option.callback);
if(option.verify) formData.append("x:verify", option.verify);
formData.append("signature", option.signature);
formData.append("file", blob); //append函数的第一个参数是后台获取数据的参数名,和html标签的input的name属性功能相同
let xmlHttpReq = null;
//IE浏览器使用ActiveX
if (window.ActiveXObject) {
xmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP");
} else if (window.XMLHttpRequest) {
//其它浏览器使用window的子对象XMLHttpRequest
xmlHttpReq = new XMLHttpRequest();
}
if (xmlHttpReq !== null) {
//设置回调,当请求的状态发生变化时,就会被调用
xmlHttpReq.onreadystatechange = () => {
//等待上传结果
if (xmlHttpReq.readyState == 1) {
}
// 上传成功,返回的文件名,设置到div的背景中
if (xmlHttpReq.readyState === 4 && xmlHttpReq.status === 200) {
let data = {};
try {
if (xmlHttpReq.statusText == "OK") {
if (xmlHttpReq.responseText) {
data = JSON.parse(xmlHttpReq.responseText);
that._dispatch("changeImage", data.data);
} else {
that._dispatch("changeImage", data.responseURL);
}
that.hasImage = false;
if (fn) fn(data.data);
resolve(data);
}
} catch (e) {
reject({code: 407, data: "数据类型出错"});
that._dispatch("promptTip", {
type: "warning",
info: "图片上传失败"
});
}
}
};
//设置请求(没有真正打开),true:表示异步
xmlHttpReq.open("POST", option.host, true);
//不要缓存
//xmlHttpReq.setRequestHeader("If-Modified-Since", "0");
//提交请求
xmlHttpReq.send(formData);
//清除掉,否则下一次选择同样的文件就进入不到onchange函数中了
}
});
}
}
};
</script>
export default {
url: {
type: String
},
type: {
type: String,
default: 'USER_AVATAR'
},
extensions: {
type: String,
default: 'png,jpg,jpeg,gif,svg,webp'
},
crop: {
type: Boolean,
default: false
},
aspectRatio: {
type: Number,
default: 1
},
uploadBtnClass: {
type: String,
default: ""
},
previewClass: {
type: String,
default: "circle"
},
cropBtn: {
type: Object,
default: function () {
return {
ok: '保存',
cancel: '取消',
}
}
},
isTemp:{
type:Boolean,
default:true
},
uid: {
type: Number,
default: 0
},
accessToken: {
type: String,
default: ""
},
fetchNum: {
type: Number,
default: 1
},
bodyData: {
type: Object,
default: function () {
return {}
}
.crop-cloth{position: fixed;top:0;bottom: 0;left: 0;width:100%;height:100%;background:rgba(0,0,0,.7);z-index: 1000}
.image-crop-content{position: fixed;left:50%;top:50%;margin-left: -400px;margin-top:-250px;transition: all 0.1s;width: 800px;height: 500px;z-index: 3000;background-color: #222226;padding:25px;box-shadow: 0 0 3px 1px rgba(0,0,0,.7);transition: all 0.1s;}
.image-crop-content-main{padding: 10px;position: relative;}
.image-crop-content-main .txt{color: #fff;position: absolute;top:8px;right:8px;display: inline-block;width:186px;text-align: center;font-size: 15.5px;line-height: 30px;}
.img-origin-div{width:502px;height:372px;border: 1px solid #191919;}
/*.img-origin-div .img-origin{width:100%;height:100%}*/
/*.img-origin-div img{max-width:100%;max-height:100%}*/
.img-origin-div img{max-width:502px;max-height:372px}
.image-crop-foot>span{font-size: 19px;width:145px;height:40px;letter-spacing: 2px;margin:10px 18px 0 18px;border: 1px solid #121214;cursor: pointer;
background-color:#2F2F34;font-weight: bold;line-height: 38px;vertical-align: top;
}
.image-crop-foot .quit{color: #ff5454;}
.image-crop-foot .save{background-color: #ff5454;border-color: #ff5454;}
.image-crop-foot .save a{color: #27272B!important;display:block;text-decoration: none;font-weight: bold;}
.image-crop-foot span:hover{box-shadow:0 0 0 1px #313035 inset,0 0 0 2px #2B2A2F inset;border:1px solid #39383D;background: #2B2B30;}
.image-crop-foot .save:hover a{color: #ff5454!important;}
/*预览*/
.img-preview-small-div{
width: 142px;
height: 142px;
position: absolute;
top: 50px;
right: 25px;
border-radius: 50%;
overflow: hidden;
display: inline-block;
vertical-align: middle;
}
.img-preview-small{
top: 50px;
}
.img-preview-small.rect .img-preview{width:186px!important;height:45px!important;}
.img-preview-small.circle{width:142px!important;height:142px!important;margin-right:24px;border-radius: 50% !important;}
.img-preview-small.circle .img-preview{width:142px!important;height:142px!important;border-radius: 50% !important;}
/*确认*/
.docs-buttons{text-align: center;}
/*截图界面弹出*/
/*.image-crop-content.show{display: block;transition: all 0.1s;}*/
.file-input-btn{position:absolute;top:0;left:0;overflow:hidden;width:100%;height:100%;font-size:0;cursor:pointer;opacity:0}
.image-upload-form{width:100%;height:100%;position:relative}
.image-upload-form input[type="file"]{width: 100%;height: 100%;padding:0;margin:0;border:none;position:absolute;top:0;left:0;overflow:hidden;opacity:0;z-index:5000}
/* cropper */
.cropper-container {
direction: ltr;
font-size: 0;
line-height: 0;
position: relative;
-ms-touch-action: none;
touch-action: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.cropper-container img {/*Avoid margin top issue (Occur only when margin-top <= -height)
*/
display: block;
height: 100%;
image-orientation: 0deg;
max-height: none !important;
max-width: none !important;
min-height: 0 !important;
min-width: 0 !important;
width: 100%;
}
.cropper-wrap-box,
.cropper-canvas,
.cropper-drag-box,
.cropper-crop-box,
.cropper-modal {
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
}
.cropper-wrap-box,
.cropper-canvas {
overflow: hidden;
}
.cropper-drag-box {
background-color: #fff;
opacity: 0;
}
.cropper-modal {
background-color: #000;
opacity: .5;
}
.cropper-view-box {
display: block;
height: 100%;
outline-color: rgba(51, 153, 255, 0.75);
outline: 1px solid #39f;
overflow: hidden;
width: 100%;
}
.cropper-dashed {
border: 0 dashed #eee;
display: block;
opacity: .5;
position: absolute;
}
.cropper-dashed.dashed-h {
border-bottom-width: 1px;
border-top-width: 1px;
height: 33.33333%;
left: 0;
top: 33.33333%;
width: 100%;
}
.cropper-dashed.dashed-v {
border-left-width: 1px;
border-right-width: 1px;
height: 100%;
left: 33.33333%;
top: 0;
width: 33.33333%;
}
.cropper-center {
display: block;
height: 0;
left: 50%;
opacity: .75;
position: absolute;
top: 50%;
width: 0;
}
.cropper-center:before,
.cropper-center:after {
background-color: #eee;
content: ' ';
display: block;
position: absolute;
}
.cropper-center:before {
height: 1px;
left: -3px;
top: 0;
width: 7px;
}
.cropper-center:after {
height: 7px;
left: 0;
top: -3px;
width: 1px;
}
.cropper-face,
.cropper-line,
.cropper-point {
display: block;
height: 100%;
opacity: .1;
position: absolute;
width: 100%;
}
.cropper-face {
background-color: #fff;
left: 0;
top: 0;
}
.cropper-line {
background-color: #39f;
}
.cropper-line.line-e {
cursor: e-resize;
right: -3px;
top: 0;
width: 5px;
}
.cropper-line.line-n {
cursor: n-resize;
height: 5px;
left: 0;
top: -3px;
}
.cropper-line.line-w {
cursor: w-resize;
left: -3px;
top: 0;
width: 5px;
}
.cropper-line.line-s {
bottom: -3px;
cursor: s-resize;
height: 5px;
left: 0;
}
.cropper-point {
background-color: #39f;
height: 5px;
opacity: .75;
width: 5px;
}
.cropper-point.point-e {
cursor: e-resize;
margin-top: -3px;
right: -3px;
top: 50%;
}
.cropper-point.point-n {
cursor: n-resize;
left: 50%;
margin-left: -3px;
top: -3px;
}
.cropper-point.point-w {
cursor: w-resize;
left: -3px;
margin-top: -3px;
top: 50%;
}
.cropper-point.point-s {
bottom: -3px;
cursor: s-resize;
left: 50%;
margin-left: -3px;
}
.cropper-point.point-ne {
cursor: ne-resize;
right: -3px;
top: -3px;
}
.cropper-point.point-nw {
cursor: nw-resize;
left: -3px;
top: -3px;
}
.cropper-point.point-sw {
bottom: -3px;
cursor: sw-resize;
left: -3px;
}
.cropper-point.point-se {
bottom: -3px;
cursor: se-resize;
height: 20px;
opacity: 1;
right: -3px;
width: 20px;
}
@media (min-width: 768px) {
.cropper-point.point-se {
height: 15px;
width: 15px;
}
}
@media (min-width: 992px) {
.cropper-point.point-se {
height: 10px;
width: 10px;
}
}
@media (min-width: 1200px) {
.cropper-point.point-se {
height: 5px;
opacity: .75;
width: 5px;
}
}
.cropper-point.point-se:before {
background-color: #39f;
bottom: -50%;
content: ' ';
display: block;
height: 200%;
opacity: 0;
position: absolute;
right: -50%;
width: 200%;
}
.cropper-invisible {
opacity: 0;
}
.cropper-bg {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMzTjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC');
}
.cropper-hide {
display: block;
height: 0;
position: absolute;
width: 0;
}
.cropper-hidden {
display: none !important;
}
.cropper-move {
cursor: move;
}
.cropper-crop {
cursor: crosshair;
}
.cropper-disabled .cropper-drag-box,
.cropper-disabled .cropper-face,
.cropper-disabled .cropper-line,
.cropper-disabled .cropper-point {
cursor: not-allowed;
}
} }