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>