Vue3使用vue-cropper截圖上傳

!!!!!!!已使用另一個截圖上傳工具,稍後更新博客!!!!!!
使用vue-cropper進行截圖上傳

先安裝

npm i vue-cropper

編寫組件

在components中添加imageCropper.vue

代碼如下

<template>
  <div>
    <div>
      <img
        v-if="value"
        class="mb-2"
        style="max-width: 200px; max-height: 200px"
        :src="value"
      />
    </div>
    <input type="hidden" v-model="value" :name="props.name" />
    <el-button size="large" type="primary" @click="updateImage"
      >上傳圖片</el-button
    >
    <el-dialog v-model="dialogVisible" title="上傳圖片" width="50%" v-loading="loading">
      <div class="d-flex">
        <div class="flex-grow-1" style="height: 400px">
          <vueCropper
            style="width: 100%; height: 100%"
            ref="cropper"
            @realTime="realTime"
            :img="option.img"
            :outputSize="option.outputSize"
            :outputType="option.outputType"
            :info="option.info"
            :canScale="option.canScale"
            :autoCrop="option.autoCrop"
            :autoCropWidth="option.autoCropWidth"
            :autoCropHeight="option.autoCropHeight"
            :fixedBox="option.fixedBox"
            :fixed="option.fixed"
            :fixedNumber="option.fixedNumber"
            :canMove="option.canMove"
            :canMoveBox="option.canMoveBox"
            :original="option.original"
            :centerBox="option.centerBox"
            :infoTrue="option.infoTrue"
            :full="option.full"
            :enlarge="option.enlarge"
            :mode="option.mode"
            :maxImgSize="option.maxImgSize"
            :limitMinSize="option.limitMinSize"
          >
          </vueCropper>
        </div>
        <div style="flex-basis: 250px">
          <div class="d-flex justify-content-center">
            <div v-if="option.img" :style="previewStyle" class="mb-4">
              <div :style="previews.div">
                <img :src="previews.url" :style="previews.img" />
              </div>
            </div>
          </div>
          <div class="text-center">
            <a
              href="javascript:;"
              class="btn btn-primary"
              style="width: 180px"
              @click="selectFile"
              >重新選擇圖片</a
            >
            <input
              type="file"
              style="width: 0; height: 0"
              accept="image/*"
              ref="ref1"
              @change="handleFileChange"
            />
          </div>
          <div class="text-center mt-4">
            <el-button type="primary" @click="chengeSize(1)" size="large"
              ><i class="fas fa-search-plus text-white me-1"></i>放大</el-button
            >
            <el-button type="primary" @click="chengeSize(-1)" size="large"
              ><i class="fas fa-search-minus text-white me-1"></i
              >縮小</el-button
            >
          </div>
          <div class="text-center mt-4">
            <el-button type="primary" @click="rotateLeft()" size="large"
              ><i class="bi bi-arrow-counterclockwise text-white me-1"></i>左轉</el-button
            >
            <el-button type="primary" @click="rotateRight()" size="large"
              ><i class="bi bi-arrow-clockwise text-white me-1"></i
              >右轉</el-button
            >
          </div>
          <div class="text-center mt-4">
            截圖大小:{{ option.autoCropWidth }}px *
            {{ option.autoCropHeight }}px
          </div>
        </div>
      </div>
      <template #footer>
        <span class="dialog-footer">
          <el-button size="large" @click="dialogVisible = false"
            >取消</el-button
          >
          <el-button size="large" ref="submitButton" type="primary" @click="getImage">
            確定選擇
          </el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import "vue-cropper/dist/index.css";
import { VueCropper } from "vue-cropper";
import { type ComponentInternalInstance, getCurrentInstance, nextTick, onMounted, reactive, ref } from "vue";
import ApiService from "@/core/services/ApiService";
import { computed, defineEmits } from "vue";
import swalMessage from "@/core/helpers/swalMessage";

const { proxy } = getCurrentInstance() as ComponentInternalInstance;

const props = defineProps([
  "modelValue",
  "name",
  "success",
  "width",
  "height",
  "fixedBox",
]);

const option = ref({
  img: "", // 裁剪圖片的地址 url 地址, base64, blob
  outputSize: 1, // 裁剪生成圖片的質量
  outputType: "png", // 裁剪生成圖片的格式 jpeg, png, webp
  info: true, // 裁剪框的大小信息
  canScale: true, // 圖片是否允許滾輪縮放
  autoCrop: true, // 是否默認生成截圖框
  autoCropWidth: 150, // 默認生成截圖框寬度
  autoCropHeight: 150, // 默認生成截圖框高度
  fixedBox: true, // 固定截圖框大小 不允許改變
  // fixedBox: props.fixedBox ?? true, // 固定截圖框大小 不允許改變
  fixed: true, // 是否開啓截圖框寬高固定比例,這個如果設置爲true,截圖框會是固定比例縮放的,如果設置爲false,則截圖框的寬高比例就不固定了
  fixedNumber: [1, 1], // 截圖框的寬高比例 [ 寬度 , 高度 ]
  canMove: true, // 上傳圖片是否可以移動
  canMoveBox: true, // 截圖框能否拖動
  original: false, // 上傳圖片按照原始比例渲染
  centerBox: false, // 截圖框是否被限制在圖片裏面
  infoTrue: true, // true 爲展示真實輸出圖片寬高 false 展示看到的截圖框寬高
  full: true, // 是否輸出原圖比例的截圖
  enlarge: 1, // 圖片根據截圖框輸出比例倍數
  mode: "contain", // 圖片默認渲染方式 contain , cover, 100px, 100% auto
  limitMinSize: [10,10], // 圖片默認渲染方式 contain , cover, 100px, 100% auto
  maxImgSize: 2000, // 圖片默認渲染方式 contain , cover, 100px, 100% auto
});
const ref1 = ref<any>([]);
const cropper = ref<any>({});

const emit = defineEmits(["update:modelValue", "onSuccess"]);

const value = computed({
  get() {
    return props.modelValue;
  },
  set(value) {
    emit("update:modelValue", value);
  },
});

const loading=ref(false);
const submitButton = ref<HTMLButtonElement | null>(null);

//修改大小
const chengeSize = (num) => {
  cropper.value.changeScale(num);
};
//向左邊旋轉90度
const rotateLeft = () => {
  cropper.value.rotateLeft();
};
//向右邊旋轉90度
const rotateRight = () => {
  cropper.value.rotateRight();
};
const selectFile = () => {
  ref1.value.click();
  option.value.img = "";
};
const updateImage = () => {
  let width=(props.width && props.width>0) ? props.width : 150;
  let height=(props.height && props.height>0) ? props.height : 150;

  option.value.autoCropWidth=width;
  option.value.autoCropHeight=height;
  option.value.fixedNumber=[1,height/width];
  option.value.limitMinSize=[width,height];

  dialogVisible.value = true;
  nextTick(() => {
    selectFile();
  });
};
const handleFileChange = (e) => {
  let file = e.target.files[0];
  const reader = new FileReader();
  reader.readAsDataURL(file);
  reader.onload = () => {
    option.value.img = reader.result as string;
  };
};
const getImage = async () => {
  loading.value = true;
  await nextTick();

  await cropper.value.getCropBlob(async (data2) => {
    data2.name = 'image.png';
    // console.log(data2);
    var data = await ApiService.uploadFile(data2);

    // loading.value=false;
    if (data.success) {
      if (import.meta.env.VITE_APP_API_URL != "/") {
        value.value = import.meta.env.VITE_APP_API_URL + data.url;
      } else {
        value.value = data.url;
      }
      dialogVisible.value = false;
      emit("onSuccess");
    } else {
      swalMessage.error("上傳失敗")
    }
  });
};
const dialogVisible = ref(false);

const show = () => {
  dialogVisible.value = true;
};
//實時預覽
const previewStyle = ref({});
const previews = ref<any>({});

const realTime = (data) => {
  previews.value = data as any;
  option.value.maxImgSize = option.value.autoCropWidth;
  option.value.full = option.value.autoCropWidth > previews.value.w;
  // 固定爲 100 寬度
  previewStyle.value = {
    overflow: "hidden",
    margin: "0",
    zoom: 200 / previews.value.w,
  };
};
</script>
<style scoped></style>

文件上傳和消息提示使用的是我其他封裝的組件,見其他博客

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章