1.创建vueCropper 子组件
<template>
<div class="Cropper">
<div class="cropper-container">
<div class="cropper-el" :style="geth">
<vue-cropper
ref="cropper"
:img="cropperImg"
:output-size="option.size"
:output-type="option.outputType"
:info="true"
:can-move="option.canMove"
:can-move-box="option.canMoveBox"
:fixed-box="option.fixedBox"
:auto-crop="option.autoCrop"
:auto-crop-width="optionWidth"
:auto-crop-height="optionHeigth"
:center-box="option.centerBox"
:high="option.high"
:info-true="option.infoTrue"
@realTime="realTime"
:enlarge="option.enlarge"
:fixed="option.fixed"
:fixed-number="option.fixedNumber"
:limitMinSize="option.limitMinSize"
@imgLoad="imgLoad"
/>
</div>
</div>
</div>
</template>
<script>
import { VueCropper } from 'vue-cropper';
export default {
name: 'Cropper',
components: {
VueCropper
},
props: [
'cropperImg',
'optionWidth',
'optionHeigth',
'geth',
'isDel'
],
data() {
return {
previews: {},
option: {
img: '',
size: 1,
outputType: 'png',
canMove: false,
fixedBox: false,
canMoveBox: false,
autoCrop: true,
autoCropWidth: this.optionWidth,
autoCropHeight: this.optionHeigth,
centerBox: true,
high: true,
enlarge: 1,
mode: 'contain',
maxImgSize: 1920,
limitMinSize: [10, 10],
original: true,
height: true,
infoTrue: false,
fixed: false,
fixedNumber: [1, 1]
}
};
},
methods: {
realTime(data) {
const that = this;
console.log('', data);
this.$refs.cropper.getCropBlob(data => {
console.log(data);
this.blobToDataURI(data, function(res) {
console.log(res);
});
});
this.$refs.cropper.clearCrop();
},
imgLoad(e) {
console.log('裁剪成功', e);
},
blobToDataURI(blob, callback) {
var reader = new FileReader();
reader.readAsDataURL(blob);
reader.onload = function(e) {
callback(e.target.result);
};
}
}
};
</script>
<style lang="scss" scoped>
.Cropper {
.cropper-el {
flex-shrink: 0;
}
.cropper-container {
display: block;
justify-content: space-between;
.prive-el {
flex: 1;
align-self: center;
text-align: center;
.prive-style {
margin: 0 auto;
flex: 1;
-webkit-flex: 1;
display: flex;
display: -webkit-flex;
justify-content: center;
-webkit-justify-content: center;
overflow: hidden;
background: #ededed;
margin-left: 40px;
}
.preview {
overflow: hidden;
}
.el-button {
margin-top: 20px;
}
}
}
}
</style>
<style lang="scss">
.cropper-box-canvas img {
width: 100% !important;
height: 100% !important;
}
</style>
2.在父组件中使用
<template>
<div>
<el-dialog
:title="type == 'add' ? '添加' : '编辑'"
:visible.sync="dialogVisible"
width="764px"
:close-on-click-modal="false"
>
<div>
<el-form
:model="ruleForm"
:rules="rules"
ref="ruleForm"
label-width="100px"
class="demo-ruleForm"
>
<el-form-item label="姓名" prop="visitorName">
<el-input v-model="ruleForm.visitorName"></el-input>
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input v-model.number="ruleForm.phone"></el-input>
</el-form-item>
<el-form-item label="所属单位" prop="companyId">
<el-select
v-model="ruleForm.companyId"
placeholder="单位名称"
@change="toSelect"
>
<el-option
v-for="item in options"
:key="item.id"
:label="item.companyName"
:value="item.id"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="身份信息" prop="idCard">
<el-input v-model="ruleForm.idCard"></el-input>
</el-form-item>
<el-form-item prop="cardPhoto">
<div style="display:flex;">
<img-upload
:url="ruleForm.cardPhoto"
@setImg="setImg"
></img-upload>
<div class="imgbox" v-if="resImg">
<el-image :src="resImg" :preview-src-list="[resImg]">
</el-image>
</div>
</div>
<div
class="cropper"
:style="{
width: `${imgObj.width}px`,
height: `${imgObj.height}px`
}"
>
<vueCropper
ref="cropper"
:img="option.img"
:outputSize="option.outputSize"
:outputType="option.outputType"
:canScale="option.canScale"
:autoCrop="option.autoCrop"
:autoCropWidth="option.autoCropWidth"
:autoCropHeight="option.autoCropHeight"
:canMoveBox="option.canMoveBox"
:canMove="option.canMove"
:centerBox="option.centerBox"
:info="option.info"
:fixedBox="option.fixedBox"
@realTime="realTime"
></vueCropper>
</div>
</el-form-item>
</el-form>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="resetForm('ruleForm')">取 消</el-button>
<el-button type="primary" @click="submitForm('ruleForm')"
>确 定</el-button
>
</span>
</el-dialog>
</div>
</template>
<script>
import imgUpload from './imgUpload.vue';
import {
companyList,
addAgreementVisitor,
agreementVisitorDetail,
editorAgreementVisitor
} from '@/api/visitor/protocol.js';
import { OBSuploads, OBSupload, base64toFile } from '@/utils/upload';
import { VueCropper } from 'vue-cropper';
import { number } from 'echarts';
export default {
name: 'addVisitorModel',
props: ['type', 'editId'],
components: { imgUpload, VueCropper },
data() {
var validateidCard = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入身份证号'));
} else {
let reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
if (!reg.test(this.ruleForm.idCard)) {
callback(new Error('请输入正确的身份证号'));
// this.$refs.ruleForm.validateField('checkPass');
}
callback();
}
};
return {
dialogVisible: false,
mode: 'view',
options: [],
ruleForm: {
visitorName: '',
phone: '',
companyName: '',
companyId: '',
idCard: '',
cardPhoto: '',
cutPhoto: ''
},
option: {
img: '', // 裁剪图片地址,这里可以本地图片或者链接,链接不用require
outputSize: 1, // 裁剪生成图片质量
outputType: 'png', // 裁剪生成图片格式
canScale: true, // 图片是否允许滚轮播放
autoCrop: true, // 是否默认生成截图框 false
info: false, // 是否展示截图框信息
autoCropWidth: 70, // 生成截图框的宽度
autoCropHeight: 90, // 生成截图框的高度
canMoveBox: true, // 截图框是否可以拖动
fixedBox: false, // 固定截图框的大小
canMove: true, // 上传图片是否可拖动
centerBox: true // 截图框限制在图片里面
},
resImg: null, //截图后图片
previewImg: null, // 预览后的图片
// previewObj: {
// width: 60,
// height: 80
// },
imgObj: {
width: 182,
height: 100
},
rules: {
visitorName: [
{ required: true, message: '请输入活动名称', trigger: 'blur' }
],
phone: [
{
required: true,
message: '请输入手机号'
},
{
//通过正则校验
pattern: /^1[3|5|7|8|9]\d{9}$/,
//不通过的提示信息
message: '请输入正确的手机号'
}
],
idCard: [
// { required: true, message: '请输入身份信息', trigger: 'blur' }
{ validator: validateidCard, trigger: 'blur' }
],
companyId: [
{ required: true, message: '请选择活动区域', trigger: 'change' }
],
cardPhoto: [{ required: true, message: '请上传身份证' }]
}
};
},
watch: {
dialogVisible(val) {
this.resImg = '';
this.companyList();
if (this.type == 'edit' && this.editId) {
this.agreementVisitorDetail();
} else {
this.reset();
}
},
// 'ruleForm.cutPhoto': {
// handler(newValue, oldValue) {
// this.resImg = newValue;
// console.log('22222', this.resImg);
// }
// },
'ruleForm.idCard': {
handler(newValue, oldValue) {
this.ruleForm.idCard = newValue.replace(
/[\u4e00-\u9fa5/\s+/]|[^a-zA-Z0-9\u4E00-\u9FA5]/g,
''
);
}
},
'option.img': {
handler: function(val) {
const that = this;
const img = new Image();
img.src = val;
img.onload = function() {
that.imgObj.width = 180;
that.imgObj.height = 100;
};
},
immediate: true
}
},
methods: {
agreementVisitorDetail() {
agreementVisitorDetail({ id: this.editId }).then(res => {
this.reset();
this.ruleForm = { ...res.data };
this.resImg = res.data.cutPhoto;
//为了避免剪辑框显示的图片跨域,座椅将图片重现转换成base64,再进行展示
this.setAvatarBase64(res.data.cardPhoto, base64 => {
this.option.img = base64;
});
// this.option.img = res.data.cardPhoto;
});
},
toSelect() {
let obj = {};
obj = this.options.find(item => {
return item.id === this.ruleForm.companyId;
});
this.ruleForm.companyName = obj.companyName;
},
companyList() {
companyList().then(res => {
this.options = res.data;
});
},
setAvatarBase64(src, callback) {
let _this = this;
let image = new Image();
// 处理缓存
image.src = src + '?v=' + Math.random();
// 支持跨域图片
image.crossOrigin = '*';
image.onload = function() {
let base64 = _this.transBase64FromImage(image);
callback && callback(base64);
};
},
transBase64FromImage(image) {
let canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
let ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0, image.width, image.height);
// 可选其他值 image/jpeg
return canvas.toDataURL('image/png');
},
realTime(data) {
const that = this;
// this.$refs.cropper.goAutoCrop(); // 重新生成裁剪框
this.$refs.cropper.getCropBlob(data => {
let name = `.${data.type.split('/')[1]}`;
// 这里data数据为Blob类型,blobToDataURI方法转换成base64
// this.blobToDataURI(data, function(res) {
// that.previewImg = res;
// });
this.$nextTick(() => {
// 设置自动裁剪框的宽高和位置
this.$refs.cropper.cropOffsertX = 89; // x1
this.$refs.cropper.cropOffsertY = 0; // y1
});
this.$refs.cropper.getCropData(data => {
this.resImg = data;
let formData = new FormData();
formData.append('file', data);
OBSupload({
file: base64toFile(data),
fileName: name
})
.then(res => {
// this.resImg = res;
this.ruleForm.cutPhoto = res;
// this.$emit('setImg', res);
})
.catch(e => {});
// this.handleDownload(data);
});
});
},
// handleClick() {
// this.$refs.cropper.getCropData(data => {
// // console.log('2222', data);
// this.resImg = data;
// // this.handleDownload(data);
// });
// },
handleDownload(url) {
var a = document.createElement('a'); // 生成一个a元素
var event = new MouseEvent('click'); // 创建一个单击事件
a.download = 'photo'; // 设置图片名称, 这里可以自定义,也可以获取图片名称进行修改
a.href = url; // 将生成的URL设置为a.href属性
a.dispatchEvent(event); // 触发a的单击事件
},
blobToDataURI(blob, callback) {
var reader = new FileReader();
reader.readAsDataURL(blob);
reader.onload = function(e) {
callback(e.target.result);
};
},
reset() {
this.ruleForm = {
visitorName: '',
phone: '',
companyName: '',
companyId: '',
idCard: '',
cardPhoto: '',
cutPhoto: ''
};
this.resImg = '';
this.option.img = '';
},
setImg(data) {
this.ruleForm.cardPhoto = data;
this.option.img = data;
},
submitForm(formName) {
console.log(this.ruleForm);
this.$refs[formName].validate(valid => {
if (valid) {
if (this.type == 'add') {
const loading = this.$loading({
lock: true,
text: '加载中',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
addAgreementVisitor(this.ruleForm)
.then(res => {
this.dialogVisible = false;
this.$emit('success', true);
this.reset();
loading.close();
})
.catch(e => {
loading.close();
this.dialogVisible = true;
});
} else {
editorAgreementVisitor(this.ruleForm).then(res => {
this.dialogVisible = false;
this.$emit('success', true);
});
}
} else {
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
this.dialogVisible = false;
},
show() {
this.dialogVisible = true;
this.$nextTick(() => {
this.$refs['ruleForm'].clearValidate();
});
},
hide() {
this.dialogVisible = false;
}
}
};
</script>
<style lang="less" scoped>
.imgbox {
width: 60px;
height: 80px;
text-align: center;
margin-top: 20px;
margin-left: 20px;
}
.cropper {
margin-top: 10px;
}
.previewImg {
width: 100%;
height: 100%;
object-fit: cover;
// border-radius: 50%;
}
</style>