<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/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+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; }
} }