vue3.2中好用的图片裁剪工具
https://github.com/xyxiao001/vue-cropper
演示地址:按需保留需要的功能
http://github.xyxiao.cn/vue-cropper/example/
# 需要使用外层容器包裹并设置宽高
## 使用 注意: 需要关掉本地的mock服务, 不然图片转化会报错
## vue 3 安装使用
```
npm install vue-cropper@next
import 'vue-cropper/dist/index.css'
import { VueCropper } from "vue-cropper";
<template>
<div>
<el-row>
<el-col :span="12">
<!-- 图片上传区域-->
<div>
<div style="width:100%;height: 60vh;">
<!-- <vue-cropper-->
<!-- autoCrop-->
<!-- ref="cropperInstance"-->
<!-- centerBox/>-->
<vue-cropper
style=""
:canMove="option.canMove"
autoCrop
:img="option.img"
:centerBox="option.centerBox"
:original="option.original"
@realTime="realTime"
ref="cropperInstance"
:outputType="option.outputType"
centerBox/>
</div>
</div>
</el-col>
<div>
</div>
</div>
</div>
<!-- <div v-for="o in 4" :key="o" class="text item">{{ 'List item ' + o }}</div>-->
</el-card>
</div></el-col>
</el-row>
<!-- <el-button @click="getCropData" block ref="cropper">获取截图后的图片</el-button>-->
<el-alert
style="width: 50%"
title="使用方法"
type="success"
description="点击图片,通过鼠标滑轮控制图片缩放大小;外部裁剪框可拖动进行拉伸大小;点击图片和裁剪框非重叠部分,可以移动图片位置"
show-icon
>
</el-alert>
<label class="btn" for="uploads">选择Logo文件</label>
<input
type="file"
id="uploads"
ref="uploadsFileInstall"
style="position: absolute; clip: rect(0 0 0 0)"
accept="image/png, image/jpeg, image/gif, image/jpg"
@change="selectImg($event)"
/>
<el-button
size="small"
type="primary" plain
@click="changeScale(1)"
>
放大
</el-button
>
<el-button
size="small"
type="primary" plain
@click="changeScale(-1)"
>
缩小</el-button
>
<!-- <el-button size="mini" type="danger" plain icon="el-icon-delete" @click="clearImg">清空素材</el-button> -->
<el-button size="small" type="primary" plain @click="rotateLeft"
>↺ 左旋转</el-button
>
<el-button size="small" type="primary" plain @click="rotateRight"
>↻ 右旋转</el-button
>
<el-button
size="small"
type="success"
:loading="uploadLoading"
@click="uploadImg('blob')"
>上传logo <el-icon class="el-icon--right"><Upload /></el-icon></el-button>
<!-- <!–预览效果图–>-->
<!-- <div class="show-preview">-->
<!-- <div class="show-preview-title">预览</div>-->
<!-- <div :style="previews.div" class="preview">-->
<!-- <img v-show="previews.url" :src="previews.url" :style="previews.img" />-->
<!-- </div>-->
<!-- </div>-->
<!-- <div>-->
<!-- {{previews}}-->
<!-- </div>-->
<!-- <div>-->
<!-- <img v-show="previews.url" :src="previews.url" alt="">-->
<!-- </div>-->
<div>
<p>预览截图框内容:像素点:width:{{previews.w}}px;height:{{previews.h}}px</p>
</div>
<section class="pre-item" style="border: 1px solid black;background: rgba(0,0,0,.5);" :style="{'width': previews.w + 'px', 'height': previews.h + 'px'}">
<div class="show-preview" :style="{'width': previews.w + 'px', 'height': previews.h + 'px', 'overflow': 'hidden',
'margin': '0px'}">
<div :style="previews.div">
<img :src="previews.url" :style="previews.img">
</div>
</div>
</section>
<div style="width: 100%;height: 40px;background-color: #ecdcde;display: flex;align-items: center;justify-content: center">
<el-button type="primary">
医院详情
</el-button>
</div>
<div>
<!-- 分为2个区域:图片上传处理:医院信息编辑=1:1-->
<div>
<el-row >
<el-col :span="12">
<el-card>
<template >
<div class="clearfix">
<span>基本资料</span>
</div>
</template>
<el-tabs v-model="activeTabsName">
<el-tab-pane label="医院资料" name="first">
<el-form ref="formEditRef" :model="hospForm.editForm" label-width="80px">
<el-form-item label="医院id" prop="hospId" >
<el-input v-model="hospForm.editForm.hospId" disabled></el-input>
</el-form-item>
<el-form-item label="医院名称" prop="hospName" >
<el-input v-model="hospForm.editForm.hospName" ></el-input>
</el-form-item>
<el-form-item label="省ID" prop="provinceId" >
<el-input v-model="hospForm.editForm.provinceId" ></el-input>
</el-form-item>
<el-form-item label="市ID" prop="cityId" >
<el-input v-model="hospForm.editForm.cityId" ></el-input>
</el-form-item>
<el-form-item label="区ID" prop="areaId" >
<el-input v-model="hospForm.editForm.areaId" ></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="commitEditHospInfo">保存</el-button>
<!-- <el-button type="danger" @click="close">关闭</el-button>-->
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
</el-card>
</el-col>
<el-col :span="12">
<!-- <div>-->
<!-- <img style="background: white" :src="elImgSrc" alt="">-->
<!-- logo预览:{{elImgSrc}}-->
<!-- </div>-->
<div class="demo-image__preview">
<el-image
v-if="hosp.info.reportLogo"
crossorigin='anonymous'
style="background: white"
:src="elImgSrc"
:initial-index="4"
fit="cover"
>
</el-image>
{{hosp.info}}
</div>
</el-col>
</el-row>
</div>
</div>
</div>
</template>
<script setup>
import { Edit, Share, Delete, Search, Upload } from '@element-plus/icons-vue'
import "vue-cropper/dist/index.css";
import { VueCropper } from "vue-cropper";
import {onMounted, reactive, ref, watch} from "vue";
import axios from "axios";
import {editHospByBtn, getHospByBtn, uploadLogo} from "../../api/HospInfoByBtn/HospInfoByBtn";
import {ElMessage, ElMessageBox} from "element-plus";
import moment from "moment";
import router from "../../router";
const option = reactive({
img: '', //裁剪图片的地址
outputSize: 1, //裁剪生成图片的质量(可选0.1 - 1)
outputType: 'png', //裁剪生成图片的格式(jpeg || png || webp)
info: true, //图片大小信息
canScale: true, //图片是否允许滚轮缩放
autoCrop: false, //是否默认生成截图框
autoCropWidth: 230, //默认生成截图框宽度
autoCropHeight: 35, //默认生成截图框高度
fixed: false, //是否开启截图框宽高固定比例
fixedNumber: [7, 4], //截图框的宽高比例
full: true, //false按原比例裁切图片,不失真
fixedBox: true, //固定截图框大小,不允许改变
canMove: true, //上传图片是否可以移动
canMoveBox: true, //截图框能否拖动
original: true, //上传图片按照原始比例渲染
centerBox: false, //截图框是否被限制在图片里面
height: true, //是否按照设备的dpr 输出等比例图片
infoTrue: false, //true为展示真实输出图片宽高,false展示看到的截图框宽高
maxImgSize: 3000, //限制图片最大宽度和高度
enlarge: 1, //图片根据截图框输出比例倍数
mode: '280px 160px' //图片默认渲染方式
})
const chooseImageType = ref('jpg')
const cropper=ref(null)
const cropperInstance = ref(null) // 用于标识cropper组件实例 vue2.x 中的ref
const uploadLoading = ref(false)
const previews = ref({})
const previewStyle3 = ref({})
const activeTabsName =ref('first')
const test=JSON.parse(window.sessionStorage.getItem("arr_start_menuRouter_NoBtn"))
const hosp=reactive({
info:{
reportLogo:''
}
})
//上传图片需要添加的token
const SysUserToken= window.sessionStorage.getItem("SysUserToken")
const tokenHeader=ref({accessToken:SysUserToken})
// const elImgSrc=ref('')
const hospForm=reactive({
editForm:{
hospId:'',
hospName:'',
provinceId:'',
cityId:'',
areaId:'',
}
})
const formEditRef= ref(null)
const size = ref('')
const editHospDialogVisible=ref(false)
//时间处理:
const dateFormat=(date)=>{
return moment(date).format('YYYY-MM-DD HH:mm:ss') // 这里可修改的格式
}
//点击编辑医院信息
const editHospInfo=()=>{
//打开对话框
hospForm.editForm.hospId=hosp.info.hospId
hospForm.editForm.hospName=hosp.info.hospName
hospForm.editForm.provinceId=hosp.info.provinceId
hospForm.editForm.cityId=hosp.info.cityId
hospForm.editForm.areaId=hosp.info.areaId
editHospDialogVisible.value=true
}
//监听:编辑医院信息对话框关闭:清空表单,还原警告
const editHospDialogClosed=()=>{
//清空表单
formEditRef.value.resetFields();
}
//点击取消 编辑医院信息
const cancleEidtHospInfo=()=>{
//清空表单
formEditRef.value.resetFields();
//关闭弹出框
editHospDialogVisible.value=false
}
//编辑角色:点击保存按钮,提交所有的医院信息
const commitEditHospInfo=()=>{
//校验字段
//提交数据
const param ={
}
param.hospId=hospForm.editForm.hospId
param.hospName=hospForm.editForm.hospName
param.provinceId=hospForm.editForm.provinceId
param.cityId=hospForm.editForm.cityId
param.areaId=hospForm.editForm.areaId
//发起请求
editHospByBtn(param).then(res =>{
if (res.code === 200) {
ElMessage({
message: "修改医院信息成功",
type: 'success',
showClose: true,
duration: 1000,
})
//清空表单
// formEditRef.value.resetFields()
//刷新列表
getHospInfo()
// // console.log("重新请求列表成功")
// editHospDialogVisible.value =false //关闭对话框
}else {
ElMessage({
message: "修改医院信息失败"+res.msg,
type: 'error',
showClose: true,
duration: 1000,
})
//跳出
}
})
}
//编辑角色:点击提交按钮,提交所有的医院信息
const commitEditRoleInfo=()=>{
//校验字段
//提交数据
const param ={
}
param.hospId=hospForm.editForm.hospId
param.hospName=hospForm.editForm.hospName
param.provinceId=hospForm.editForm.provinceId
param.cityId=hospForm.editForm.cityId
param.areaId=hospForm.editForm.areaId
//发起请求
editHospByBtn(param).then(res =>{
if (res.code === 200) {
ElMessage({
message: "修改医院信息成功",
type: 'success',
showClose: true,
duration: 1000,
})
//清空表单
formEditRef.value.resetFields()
//刷新列表
getHospInfo()
// console.log("重新请求列表成功")
editHospDialogVisible.value =false //关闭对话框
}else {
ElMessage({
message: "修改医院信息失败"+res.msg,
type: 'error',
showClose: true,
duration: 1000,
})
//跳出
}
})
}
//获取医院信息
const getHospInfo=()=>{
//校验字段
//提交数据
const param ={
}
param.hospId=hosp.info.hospId
//发起请求
getHospByBtn(param).then(res =>{
if (res.code === 200) {
//更新医院信息
hosp.info=res.data
hospForm.editForm.hospId=hosp.info.hospId
hospForm.editForm.hospName=hosp.info.hospName
hospForm.editForm.provinceId=hosp.info.provinceId
hospForm.editForm.cityId=hosp.info.cityId
hospForm.editForm.areaId=hosp.info.areaId
//更新图片信息
elImgSrc.value='http://121.201.93.45:19002/reportLogo/'+hosp.info.reportLogo
}else {
ElMessage({
message: "获取医院信息失败"+res.msg,
type: 'error',
showClose: true,
duration: 1000,
})
//跳出
}
})
}
const handleClose =(done)=>{
// ElMessageBox.confirm('Are you sure to close this dialog?')
ElMessageBox.confirm('您确定要关闭此对话框吗?')
.then(() => {
done()
})
.catch(() => {
// catch error
})
}
// 图片上传成功之后的回调函数
const uploadFileSuccess=(response, file, fileList)=>{
// console.log(response)
if(response.code===200){
ElMessage({
message: "上传图片成功",
type: 'success',
showClose: true,
duration: 1000,
})
getHospInfo()
}else {
ElMessage({
message: response.msg,
type: 'error',
showClose: true,
duration: 1000,
})
}
}
// const elImgSrc=ref('')
const elImgSrc=ref('http://121.201.93.45:19002/reportLogo/'+hosp.info.reportLogo)
watch(hosp.info,()=>{
elImgSrc.value='http://121.201.93.45:19002/reportLogo/'+hosp.info.reportLogo
})
//文件上传之前进行校验
const beforeAvatarUpload=(file, fileList)=>{
//获取上传文件的后缀名
const fileSuffix = file.name.substring(file.name.lastIndexOf(".") + 1);
const whiteList = ["jpeg", "png", "bmp", "jpg"];
if (whiteList.indexOf(fileSuffix) === -1) {
ElMessage({
message: "文件格式错误,选上传:.jpeg,.png,.bmp,.jpg。",
type: 'error',
showClose: true,
duration: 1000,
})
file.abort() //取消上传请求
return;
}
//获取上传文件大小
let imgSize = Number(file.size / 1024 );
if (imgSize > 200) {
ElMessage({
message: "文件大小不能超过200k,请重新上传。",
type: 'error',
showClose: true,
duration: 1000,
})
file.abort() //取消上传请求
return;
}else {
}
}
//初始化
onMounted(() => {
/**
* 1、判断是否是路由跳转
*/
if(router.currentRoute.value.params.Report_ByHospital){
hosp.info=JSON.parse(router.currentRoute.value.params.Report_ByHospital)
elImgSrc.value='http://121.201.93.45:19002/reportLogo/'+hosp.info.reportLogo
getHospInfo()
}
/**
* 2、判断是否有缓存:医院管理
*/
else if(JSON.parse(window.sessionStorage.getItem("router_Report_HospInfoByBtn")) ){
hosp.info=JSON.parse(window.sessionStorage.getItem("router_Report_HospInfoByBtn"))
elImgSrc.value='http://121.201.93.45:19002/reportLogo/'+hosp.info.reportLogo
getHospInfo()
}
else {
hosp.info.hospId=''
hosp.info.reportLogo=''
hosp.info.hospName=''
hosp.info.provinceId=''
hosp.info.cityId=''
hosp.info.areaId=''
elImgSrc.value=''
}
//医院信息赋值
hospForm.editForm.hospId=hosp.info.hospId
hospForm.editForm.hospName=hosp.info.hospName
hospForm.editForm.provinceId=hosp.info.provinceId
hospForm.editForm.cityId=hosp.info.cityId
hospForm.editForm.areaId=hosp.info.areaId
})
const realTime = function(data){
previews.value = data
//固定高宽 计算 :按照比例计算
let x_scale=308 / previews.value.w
let y_scale=44 / previews.value.h
// console.log("previews.w",previews.value.w)
// console.log("previews.h",previews.value.h)
// console.log("x_scale",x_scale)
// console.log("y_scale",y_scale)
// let x_scale=0.5
// let y_scale=0.5
previewStyle3.value = {
width: previews.value.w + "px",
height: previews.value.h + "px",
overflow: "hidden",
margin: "0",
transform:"scale("+x_scale+","+y_scale+")",
background:"white",
// let zoom_value=100 / previews.value.w
// let zoom_value1=100 / zoom_value.w
}}
const getCropData=(data)=>{
console.log("data:",data)
console.log(cropper.value)
cropper.value.getCropData(data=>{
console.log(data)
})
}
// 选择图片
const selectImg = function(e){
if(!e.target.files.length) return
if (!/\.(jpg|jpeg|png|JPG|PNG)$/.test(e.target.value)) {
alert('图片类型要求:jpeg、jpg、png')
return false
}
let file = e.target.files[0]
let strArr = file.name.split('.')
chooseImageType.value = strArr[strArr.length-1]
let reader = new FileReader()
reader.onload = (e) => {
let data
if (typeof e.target.result === 'object') {
data = window.URL.createObjectURL(new Blob([e.target.result]))
} else {
data = e.target.result
}
option.img = data
}
//转化为base64
reader.readAsDataURL(file)
}
// 图片缩放
const changeScale = function(num){
num = num || 1
cropperInstance.value.changeScale(num)
}
// 向左旋转
const rotateLeft = function(){
cropperInstance.value.rotateLeft()
}
// 向右旋转
const rotateRight = function(){
cropperInstance.value.rotateRight()
}
// 上传图片
const uploadImg = function(type){
if(type !== 'blob') return
cropperInstance.value.getCropBlob(async (data)=>{
let formData = new FormData();
formData.append('logoFile',data,`DX.${chooseImageType.value}`)
formData.append('hospId',hosp.info.hospId)
//调用axios上传 将请求地址改为自己图片上传的地址
uploadLoading.value = true
uploadLogo(formData).then(res =>{
if (res.code === 200) {
ElMessage({
message: "logo上传成功",
type: 'success',
showClose: true,
duration: 1000,
})
getHospInfo()
uploadLoading.value = false
}else {
ElMessage({
message: +res.msg,
type: 'error',
showClose: true,
duration: 1000,
})
uploadLoading.value = false
}
})
})
}
</script>
<style lang="less" scoped>
/deep/ .cropper-move{
background: rgba(0,0,0,.5)!important;
}
.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;
-webkit-transition: .1s;
transition: .1s;
font-weight: 500;
padding: 8px 15px;
font-size: 12px;
border-radius: 3px;
color: #fff;
background-color: #409EFF;
border-color: #409EFF;
margin-right: 10px;
}
.show-preview{
background-color: #FFFFFF;
}
</style>