1. 程式人生 > 其它 >vue3.2中好用的圖片裁剪工具

vue3.2中好用的圖片裁剪工具

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>


    <!--    &lt;!&ndash;預覽效果圖&ndash;&gt;-->
    <!--    <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>