avatar.vue
<template>
<div class="user-head-portrait">
<div class="avatar-wrapper">
<img v-if="images.avatarImg === '' && imgSrc === ''" src="../../image/user.png" alt="" class="avatar-img" width="150" height="150" />
<img v-else :src="images.avatarImg || imgSrc" alt="" class="avatar-img" width="150" height="150" />
<p class="upload-wrapper">
<a href="javscript:;" class="upload-btn">上传头像</a>
<input type="file" name="avatar" value="" accept="image/*" class="avatar-input" @change="imgOnChange($event, 'avatar')" />
</p>
</div>
<!-- 选择头像的弹窗 -->
<div v-if="images.isShowImageEditor" class="personal-pops">
<div class="pops-content">
<h4>
头像剪裁
<a class="fr pops-close" href="javascript:;" @click="closeImageEditor">
<i class="el-dialog__close el-icon el-icon-close"></i>
</a>
</h4>
<div class="editor">
<img id="editor" :src="images.imageWaitingForCrop" alt="">
</div>
<p class="btn-row dialog-btn">
<el-button type="primary" @click="confirmCrop">确定</el-button>
<el-button @click="closeImageEditor">取消</el-button>
</p>
</div>
</div>
</div>
</template>
<script>
import Cropper from 'cropperjs'
const qiniu = require('qiniu-js')
export default {
props: {
imgSrc: {
type: String,
default: ''
}
},
data() {
return {
images: {
coverImg: '',
coverObj: null,
coverBtn: null,
coverUploading: false,
avatarImg: '',
avatarObj: null,
avatarBtn: null,
avatarUploading: false,
isShowImageEditor: false,
editImageType: '',
imageWaitingForCrop: '',
cropper: null
}
}
},
methods: {
imgOnChange(e, type) {
const images = this.images
const target = e.target
const btnType = type + 'Btn'
const objType = type + 'Obj'
images.editImageType = type
images.isShowImageEditor = true
images[btnType] = target
if (target.files) {
images[objType] = target.files[0]
images.imageWaitingForCrop = this.createLocalURL(images[objType])
} else {
target.select()
images.imageWaitingForCrop = document.selection.createRange().text
}
this.$nextTick(() => {
this.initCropper()
})
},
initCropper() {
const options = {
cropBoxResizable: false
}
options.aspectRatio = 1 / 1
options.minCropBoxWidth = 128
options.minCropBoxHeight = 128
this.images.cropper = new Cropper(document.querySelector('#editor'), options)
},
// 裁剪图片确定函数
async confirmCrop() {
const me = this
const options = {
imageSmoothingEnabled: false,
imageSmoothingQuality: 'high'
}
const type = this.images.editImageType
const loading = type + 'Uploading'
this.images[loading] = true
this.images.avatarImg = this.images.cropper.getCroppedCanvas(options).toDataURL('image/png')
this.$request.get('resource/upload', { params: { type: 1, size: 0 }}).then(data => {
// 获取到token,上传图片到七牛
const file = this.dataURLtoFile(this.images.avatarImg)
const observable = qiniu.upload(file, data.key, data.token)
observable.subscribe({
complete(res) {
me.$emit('getImgInfo', res.url)
me.closeImageEditor()
},
next(res) {
console.log("next:", res)
},
error(err) {
console.log("error:", err)
}
})
}).catch(error => {
console.error(error)
})
},
closeImageEditor() {
const images = this.images
this.destroyLocalURL(this.imageWaitingForCrop)
images.isShowImageEditor = !images.isShowImageEditor
images[images.editImageType + 'Btn'].value = ''
images.cropper.destroy()
},
createLocalURL(file) {
if (window.createObjectURL) {
return window.createObjectURL(file)
} else if (window.URL) {
return window.URL.createObjectURL(file)
} else if (window.webkitURL.createObjectURL) {
return window.webkitURL.createObjectURL(file)
}
},
destroyLocalURL(url) {
if (window.revokeObjectURL) {
return window.revokeObjectURL(url)
} else if (window.URL) {
return window.URL.revokeObjectURL(url)
} else if (window.webkitURL.revokeObjectURL) {
return window.webkitURL.revokeObjectURL(url)
}
},
// 将base64图片转换成文件的形式
dataURLtoFile(dataurl, filename = 'file') {
let arr = dataurl.split(',')
let mime = arr[0].match(/:(.*?);/)[1]
let suffix = mime.split('/')[1]
let bstr = atob(arr[1])
let n = bstr.length
let u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], `${filename}.${suffix}`, {
type: mime
})
}
}
}
</script>
<style scoped>
@import './index.scss';
</style>
<style lang="scss" scoped>
.avatar-wrapper {
position: relative;
.avatar-img {
display: block;
border-radius: 50%;
}
.upload-wrapper {
position: relative;
.upload-btn{
margin: 22px auto 0 auto;
width: 80px;
height: 36px;
font-size: 14px;
line-height: 36px;
display: block;
color: #fff;
text-align: center;
background:rgba(255,128,17,1);
border-radius:20px;
}
.avatar-input {
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
width: 80px;
height: 36px;
opacity: 0;
cursor: pointer;
z-index: 10
}
}
}
.personal-pops{
position: fixed;
left: 0;
top: 0;
bottom: 0;
right: 0;
background: rgba(0,0,0,0.6);
z-index: 10000;
.pops-content{
width: 488px;
height: 340px;
padding: 20px 24px;
background: #fff;
position: absolute;
left: 50%;
top:50%;
transform:translate(-50%,-50%);
h4 {
position: relative;
font-size:20px;
color:rgba(49,50,56,1);
line-height:26px;
font-weight: 400;
}
.pops-close {
position: absolute;
right: 0;
}
.el-dialog__close {
font-size: 24px;
}
}
}
.editor{
height: 70%;
margin: 15px 0;
img{
width: 100%;
height: 100%;
}
}
.btn-row{
text-align: right;
.btn-list {
margin-right: 10px;
width: 76px !important;
&:last-child {
margin-right: 0;
}
}
}
</style>
index.scss
.cropper-container {
font-size: 0;
line-height: 0;
position: relative;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
direction: ltr;
-ms-touch-action: none;
touch-action: none;
}
.cropper-container img {
display: block;
min-width: 0 !important;
max-width: none !important;
min-height: 0 !important;
max-height: none !important;
width: 100%;
height: 100%;
image-orientation: 0deg;
}
.cropper-wrap-box,
.cropper-canvas,
.cropper-drag-box,
.cropper-crop-box,
.cropper-modal {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.cropper-wrap-box {
overflow: hidden;
}
.cropper-drag-box {
opacity: 0;
background-color: #fff;
}
.cropper-modal {
opacity: 0.5;
background-color: #000;
}
.cropper-view-box {
display: block;
overflow: hidden;
width: 100%;
height: 100%;
outline: 1px solid #39f;
outline-color: rgba(51, 153, 255, 0.75);
}
.cropper-dashed {
position: absolute;
display: block;
opacity: 0.5;
border: 0 dashed #eee;
}
.cropper-dashed.dashed-h {
top: 33.33333%;
left: 0;
width: 100%;
height: 33.33333%;
border-top-width: 1px;
border-bottom-width: 1px;
}
.cropper-dashed.dashed-v {
top: 0;
left: 33.33333%;
width: 33.33333%;
height: 100%;
border-right-width: 1px;
border-left-width: 1px;
}
.cropper-center {
position: absolute;
top: 50%;
left: 50%;
display: block;
width: 0;
height: 0;
opacity: 0.75;
}
.cropper-center:before,
.cropper-center:after {
position: absolute;
display: block;
content: " ";
background-color: #eee;
}
.cropper-center:before {
top: 0;
left: -3px;
width: 7px;
height: 1px;
}
.cropper-center:after {
top: -3px;
left: 0;
width: 1px;
height: 7px;
}
.cropper-face,
.cropper-line,
.cropper-point {
position: absolute;
display: block;
width: 100%;
height: 100%;
opacity: 0.1;
}
.cropper-face {
top: 0;
left: 0;
background-color: #fff;
}
.cropper-line {
background-color: #39f;
}
.cropper-line.line-e {
top: 0;
right: -3px;
width: 5px;
cursor: e-resize;
}
.cropper-line.line-n {
top: -3px;
left: 0;
height: 5px;
cursor: n-resize;
}
.cropper-line.line-w {
top: 0;
left: -3px;
width: 5px;
cursor: w-resize;
}
.cropper-line.line-s {
bottom: -3px;
left: 0;
height: 5px;
cursor: s-resize;
}
.cropper-point {
width: 5px;
height: 5px;
opacity: 0.75;
background-color: #39f;
}
.cropper-point.point-e {
top: 50%;
right: -3px;
margin-top: -3px;
cursor: e-resize;
}
.cropper-point.point-n {
top: -3px;
left: 50%;
margin-left: -3px;
cursor: n-resize;
}
.cropper-point.point-w {
top: 50%;
left: -3px;
margin-top: -3px;
cursor: w-resize;
}
.cropper-point.point-s {
bottom: -3px;
left: 50%;
margin-left: -3px;
cursor: s-resize;
}
.cropper-point.point-ne {
top: -3px;
right: -3px;
cursor: ne-resize;
}
.cropper-point.point-nw {
top: -3px;
left: -3px;
cursor: nw-resize;
}
.cropper-point.point-sw {
bottom: -3px;
left: -3px;
cursor: sw-resize;
}
.cropper-point.point-se {
right: -3px;
bottom: -3px;
width: 20px;
height: 20px;
cursor: se-resize;
opacity: 1;
}
@media (min-width: 768px) {
.cropper-point.point-se {
width: 15px;
height: 15px;
}
}
@media (min-width: 992px) {
.cropper-point.point-se {
width: 10px;
height: 10px;
}
}
@media (min-width: 1200px) {
.cropper-point.point-se {
width: 5px;
height: 5px;
opacity: 0.75;
}
}
.cropper-point.point-se:before {
position: absolute;
right: -50%;
bottom: -50%;
display: block;
width: 200%;
height: 200%;
content: " ";
opacity: 0;
background-color: #39f;
}
.cropper-invisible {
opacity: 0;
}
.cropper-bg {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMzTjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC");
}
.cropper-hide {
position: absolute;
display: block;
width: 0;
height: 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;
}
.el-form--inline .el-form-item__content {
width: 50%;
display: inline-block;
vertical-align: top;
}