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>

文件上传和消息提示使用的是我其他封装的组件,见其他博客

posted @ 2024-05-19 21:05  静坐仰望星空  阅读(468)  评论(0编辑  收藏  举报