1.装置引入依赖包

npm i vue-cropper

在组件vueCropper引入依赖包

import {VueCropper} from 'vue-cropper'

2.增加vueCropper.vue组件

<template>    <div>        <div class="cropper-content">            <!-- 剪裁框 -->            <div class="cropper">                <vueCropper ref="cropper" :img="option.img"                            :outputSize="option.size"                            :outputType="option.outputType"                            :info="true" :full="option.full"                            :canMove="option.canMove"                            :canMoveBox="option.canMoveBox"                            :original="option.original"                            :autoCrop="option.autoCrop"                            :autoCropWidth="option.autoCropWidth"                            :autoCropHeight="option.autoCropHeight"                            :fixedBox="option.fixedBox"                            @realTime="realTime"                            :fixed="option.fixed"                            :fixedNumber="fixedNumber"></vueCropper>            </div>            <!-- 预览框 -->            <div class="show-preview"                 :style="{'width': '300px', 'height': '300px',  'overflow': 'hidden', 'margin': '0 25px', 'display':'flex', 'align-items' : 'center'}">                <div :style="previews.div" class="preview">                    <img :src="previews.url" :style="previews.img">                </div>            </div>        </div>        <div class="footer-btn">            <!-- 缩放旋转按钮 -->            <div class="scope-btn">                <el-button type="primary" icon="el-icon-zoom-in" @click="changeScale(1)"></el-button>                <el-button type="primary" icon="el-icon-zoom-out" @click="changeScale(-1)"></el-button>                <el-button type="primary" @click="rotateLeft">逆时针旋转</el-button>                <el-button type="primary" @click="rotateRight">顺时针旋转</el-button>            </div>            <!-- 确认上传按钮 -->            <div class="upload-btn">                <el-button type="primary" @click="uploadImg('blob')">上传</el-button>            </div>        </div>    </div></template><script>    import {VueCropper} from 'vue-cropper'    export default {        data() {            return {                previews: {}, // 预览数据                option: {                    img: '', // 裁剪图片的地址  (默认:空)                    size: 1, // 裁剪生成图片的品质  (默认:1)                    full: false, // 是否输入原图比例的截图 选true生成的图片会十分大  (默认:false)                    outputType: 'jpg', // 裁剪生成图片的格局  (默认:jpg)                    canMove: true, // 上传图片是否能够挪动  (默认:true)                    original: true, // 上传图片依照原始比例渲染  (默认:false)                    canMoveBox: false, // 截图框是否拖动  (默认:true)                    autoCrop: true, // 是否默认生成截图框  (默认:false)                    autoCropWidth: this.imgW*(256/320), // 默认生成截图框宽度  (默认:80%)                    autoCropHeight: this.imgH*(256/320), // 默认生成截图框高度  (默认:80%)                    fixedBox: true, // 固定截图框大小 不容许扭转  (默认:false)                    fixed: false, // 是否开启截图框宽高固定比例  (默认:true)                    fixedNumber: [1, 1] // 截图框比例  (默认:[1:1])                },                downImg: '#'            }        },        props: ['imgFile', 'fixedNumber','imgW','imgH'],        methods: {            changeScale(num) {                // 图片缩放                num = num || 1                this.$refs.cropper.changeScale(num)            },            rotateLeft() {                // 向左旋转                this.$refs.cropper.rotateLeft()            },            rotateRight() {                // 向右旋转                this.$refs.cropper.rotateRight()            },            Update() {                // this.file = this.imgFile                this.option.img = this.imgFile.url            },            realTime(data) {                // 实时预览                this.previews = data            },            uploadImg(type) {                // 将剪裁好的图片回传给父组件                event.preventDefault()                let that = this                if (type === 'blob') {                    this.$refs.cropper.getCropBlob(data => {                        const reader = new FileReader();                        reader.readAsDataURL(data);                        reader.onload = (e) => {                            that.$emit('upload', reader.result)                        }                    })                } else {                    this.$refs.cropper.getCropData(data => {                        that.$emit('upload', data)                    })                }            }        },        components: {VueCropper}    }</script><style>    .cropper-content {        display: flex;        display: -webkit-flex;        justify-content: flex-end;        -webkit-justify-content: flex-end;    }    .cropper-content .cropper {        width: 350px;        height: 300px;    }    .cropper-content .show-preview {        flex: 1;        -webkit-flex: 1;        display: flex;        display: -webkit-flex;        justify-content: center;        -webkit-justify-content: center;        overflow: hidden;        border: 1px solid #cccccc;        background: #cccccc;        margin-left: 40px;    }    .preview {        overflow: hidden;        border: 1px solid #000000;        background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC");    }    .footer-btn {        margin-top: 30px;        display: flex;        display: -webkit-flex;        justify-content: flex-end;        -webkit-justify-content: flex-end;    }    .footer-btn .scope-btn {        width: 250px;        display: flex;        display: -webkit-flex;        justify-content: space-between;        -webkit-justify-content: space-between;    }    .footer-btn .upload-btn {        flex: 1;        -webkit-flex: 1;        display: flex;        display: -webkit-flex;        justify-content: center;        -webkit-justify-content: center;    }    .footer-btn .btn {        outline: none;        display: inline-block;        line-height: 1;        white-space: nowrap;        cursor: pointer;        -webkit-appearance: none;        text-align: center;        -webkit-box-sizing: border-box;        box-sizing: border-box;        outline: 0;        margin: 0;        -webkit-transition: 0.1s;        transition: 0.1s;        font-weight: 500;        padding: 8px 15px;        font-size: 12px;        border-radius: 3px;        color: #fff;        background-color: #67c23a;        border-color: #67c23a;    }</style>

3.上传组件
单张上传要在el-upload加上list-type="picture"属性

<el-upload        class="avatar-uploader"        style="width: 150px;height: 150px;"        action="http://upload-z2.qiniup.com/"        :auto-upload="false" :show-file-list="false"        list-type="picture"        :on-change="handleCrop"        :http-request="upload">    <img class="up-img" v-if="groupForm.logoUrl" :src="groupForm.logoUrl">    <i v-else class="el-icon-plus avatar-uploader-icon"></i></el-upload>

updated生命周期加上以下代码

updated () {    if (this.$refs.vueCropper) {        this.$refs.vueCropper.Update()    }},

4.el-upload的on-change事件函数 handleCrop办法

handleCrop(file, files) {    // 点击弹出剪裁框    this.cropperModel = true    this.file = file    this.upPost.key = file.name},

5.自定义el-upload上传办法 upload办法
应用原生xhr上传base64格局图片到七牛,this.upPost.key和than.upPost.token别离是上传文件名和七牛token,七牛token是从后端获取的token

upload(data) {    let than = this    //截取base64    data = data.substring(22);    let timestamp = Date.parse(new Date());    let key = timestamp + this.upPost.key    let url = "http://upload-z2.qiniup.com/putb64/" + this.fileSize(data) + '/key/' + this.baseCode64(key);    let xhr = new XMLHttpRequest();    xhr.onreadystatechange = function () {        if (xhr.readyState == 4) {            let json = JSON.parse(xhr.responseText)            than.groupForm.logoUrl = '七牛资源门路' + json.key            than.$message.success('上传胜利!')            than.cropperModel = false        }    }    xhr.open("POST", url, true);    xhr.setRequestHeader("Content-Type", "application/octet-stream");    xhr.setRequestHeader("Authorization", "UpToken " + than.upPost.token);    xhr.send(data);},

6.工具办法

//计算文件大小函数fileSize(str) {    let fileSize;    if (str.indexOf('=') > 0) {        let indexOf = str.indexOf('=');        str = str.substring(0, indexOf); //把开端的’=‘号去掉    }    fileSize = parseInt(str.length - (str.length / 8) * 2);    return fileSize;},//转换base64函数baseCode64(input){    var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";    var output = "";    var chr1, chr2, chr3, enc1, enc2, enc3, enc4;    var i = 0;    input = this._utf8_encode(input);    while (i < input.length) {        chr1 = input.charCodeAt(i++);        chr2 = input.charCodeAt(i++);        chr3 = input.charCodeAt(i++);        enc1 = chr1 >> 2;        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);        enc4 = chr3 & 63;        if (isNaN(chr2)) {            enc3 = enc4 = 64;        } else if (isNaN(chr3)) {            enc4 = 64;        }        output = output +            _keyStr.charAt(enc1) + _keyStr.charAt(enc2) +            _keyStr.charAt(enc3) + _keyStr.charAt(enc4);    }    return output;},//字节转换_utf8_encode(string) {    string = string.replace(/\r\n/g,"\n");    var utftext = "";    for (var n = 0; n < string.length; n++) {        var c = string.charCodeAt(n);        if (c < 128) {            utftext += String.fromCharCode(c);        } else if((c > 127) && (c < 2048)) {            utftext += String.fromCharCode((c >> 6) | 192);            utftext += String.fromCharCode((c & 63) | 128);        } else {            utftext += String.fromCharCode((c >> 12) | 224);            utftext += String.fromCharCode(((c >> 6) & 63) | 128);            utftext += String.fromCharCode((c & 63) | 128);        }    }    return utftext;},