el-upload+VueCropper实现的图片上传裁剪功能(带压缩)

      前端组件部分:

效果图如下:

        使用之前需要 安装 VueCropper 和 image-conversion
        

npm i vue-cropper
npm i image-conversion

        由于这两个包本文章都是局部引用,所以无需再全局配置

        组件代码如下:可能更具自己的样式再去做对应的修改

<!--
 * @描述: 证件照上传 图片部分(对接上传裁剪功能)
-->
<template>
  <!-- :limit="limits" -->
  <el-upload ref="uploadFiles" action="*" :before-upload="beforeAvatarUpload" :on-remove="handleRemove"
    :on-success="handleSuccess" :on-change="handleChange" :multiple="multiple" :http-request="selectPicUpload"
    :show-file-list="false" drag>
    <div class="pic_uni" :style="{
    'width': width,
    'height': height,
    'aspect-ratio': aspectRatio_out
  }">
      <img v-if="modelValue" class="" :src="modelValue" alt="" srcset="">
      <div v-else class="def_box">
        <slot>
          <el-icon :size="34">
            <Camera />
          </el-icon>
          <p style="font-size: 16px;">{{ describe }}</p>
        </slot>
      </div>


    </div>
  </el-upload>

  <el-dialog destroy-on-close :title="title" v-model="dialogVisibleCorpper" width="800px" append-to-body>
    <el-row>
      <el-col :span="12" style="height: 300px">
        <VueCropper ref="cropper" :img="options.img" :info="true" :autoCrop="options.autoCrop"
          :autoCropWidth="options.autoCropWidth" :autoCropHeight="options.autoCropHeight" :fixedBox="options.fixedBox"
          :fixed="options.fixed" :centerBox="options.centerBox" :fixedNumber="options.fixedNumber"
          :outputType="options.outputType" @realTime="realTime" />
      </el-col>
      <el-col :span="12" style="height: 300px;display: flex;align-items: center;justify-content: center;">
        <!-- :style="{ 'aspect-ratio': aspectRatio }" -->
        <div class="preview-box" :style="previews.img">
          <!--  -->
          <img v-if="previews.url" style="width: 100%;" :src="previews.url" />
        </div>
      </el-col>
    </el-row>
    <el-row style="margin-top: 12px">
      <el-col :span="12">
        <el-row>
          <el-col :span="8">
            <el-upload action="#" :http-request="selectPicUpload" :before-upload="beforeUpload" :show-file-list="false">
              <el-button>选择</el-button>
            </el-upload>
          </el-col>
          <el-col :span="4">
            <el-button :icon="Plus" @click="changeScale(1)"></el-button>
          </el-col>
          <el-col :span="4">
            <el-button :icon="Minus" @click="changeScale(-1)"></el-button>
          </el-col>
          <el-col :span="4">
            <el-button :icon="RefreshLeft" @click="rotateLeft()"></el-button>
          </el-col>
          <el-col :span="4">
            <el-button :icon="RefreshRight" @click="rotateRight()"></el-button>
          </el-col>
        </el-row>
      </el-col>
      <el-col :span="4" :offset="8" style="margin-left: 22.3%">
        <el-button type="primary" @click="determine(cropperBlob)">提 交</el-button>
      </el-col>
    </el-row>
  </el-dialog>
</template>

<!-- 简单使用模版
 <uploadPicUni v-model="businessPic" width="285px" describe="点击上传营业执照"></uploadPicUni>
-->

<script setup lang="ts">
import 'vue-cropper/dist/index.css'
import { VueCropper } from "vue-cropper";
import * as imageConversion from "image-conversion";
import { ref, reactive, getCurrentInstance, computed } from 'vue'
import type { UploadRequestOptions } from 'element-plus'

import {
  Plus,
  Minus,
  RefreshLeft,
  RefreshRight,
} from '@element-plus/icons-vue'
const { proxy } = getCurrentInstance() as any;
import { OssUpload as CryptoTools } from '@/utils/crypto'
const props = defineProps({
  limits: {
    type: Number,
    default: 1,
  },
  multiple: {
    type: Boolean,
    default: false,
  },
  fixed: {
    type: Boolean,
    default: true,
  },
  width: {
    type: String,
    default: '200px',
  },
  height: {
    type: String,
    default: 'auto',
  },
  modelValue: {
    type: String,
  },
  /**width 和 height 都给的情况下不生效*/
  fixedNumber: {
    type: Array,
    default: [5, 3],
  },
  aspectRatio_out: {
    type: String,
    default: '5 / 3',
  },
  title: {
    type: String,
    default: '上传图片',
  },
  describe: {
    type: String,
    default: '上传图片',
  }
})

const aspectRatio = computed(() => {
  return props.fixed ? `${props.fixedNumber[0]} / ${props.fixedNumber[1]}` : ''
})
const options = reactive({
  img: null, // 裁剪图片的地址
  autoCropWidth: 200, // 默认生成截图框宽度 默认容器的 80%
  autoCropHeight: 200, // 默认生成截图框高度 默认容器的 80%
  outputType: 'png', // 裁剪生成图片的格式 jpeg, png, webp
  autoCrop: true, // 是否默认生成截图框
  fixedBox: false, // 固定截图框大小
  fixedNumber: props.fixedNumber,
  fixed: props.fixed,
  centerBox: true,
})
const previews = ref({
  url: ''
})
/**裁剪后的图片的blob格式文件流 */
const cropperBlob = ref<Blob>()
const emit = defineEmits(['update:modelValue'])
/**节点 */
const uploadFiles = ref();
const cropper = ref();
const dialogVisibleCorpper = ref<boolean>(false);
const beforeAvatarUpload = () => { }
const handleRemove = () => { }
const handleSuccess = () => { }
const handleChange = () => { }

// 实时预览事件
const realTime = (data) => {
  previews.value.img = {
    width: data.w + 'px',
    height: data.h + 'px',
  }
  cropper.value.getCropBlob(blob => {
    cropperBlob.value = blob;
    const url: string = URL.createObjectURL(blob);
    previews.value.url = url;
  })
}
const selectPicUpload = (obj: UploadRequestOptions) => {
  let rawFile = obj.file;
  if (rawFile.type !== "image/jpeg" && rawFile.type !== "image/png") {
    proxy.$ShowMsg.error("只能上传jpg与png格式");
    return false;
  }

  new Promise((resolve) => {
    imageConversion.compress(rawFile, 0.4).then((res) => {
      resolve(res);
    });
  }).then((res: Blob) => {
    // 将 Blob 对象转换为 URL
    const url = URL.createObjectURL(res);
    options.img = url;
    dialogVisibleCorpper.value = true;
  })
}
// 修改图片大小 正数为变大 负数变小
const changeScale = (num) => {
  num = num || 1
  cropper.value.changeScale(num)
}
// 向左边旋转90度
const rotateLeft = () => {
  cropper.value.rotateLeft()
}
// 向右边旋转90度
const rotateRight = () => {
  cropper.value.rotateRight()
}

/**裁剪后的提交 */
const determine = (res: Blob) => {
  CryptoTools.OSSUpload(res, ((data: any) => {
    if (data.Success) {
      dialogVisibleCorpper.value = false;
      emit('update:modelValue', data.rows.url)
    }
  }))
}
</script>

<style scoped lang="less">
:deep(.el-upload-dragger) {
  display: inline-block !important;
  padding: 0;
}

:deep(.el-upload.is-drag) {
  display: inline-block !important;
}

.el-upload {

  .pic_uni {
    aspect-ratio: 16/9;

    img {
      width: 100%;
      height: 100%;
    }
  }
}

.avatar-container {
  .img-box {
    border: 1px solid #ccc;
    width: 10vw;
    height: 10vw;
  }
}

.preview-box {
  border: 1px solid #ccc;
  overflow: hidden;
}

.def_box {
  width: 100%;
  height: 100%;
  background-color: #f6f6f6;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: #cfcfcf;
}
</style>

使用方式如下:
 

// businessPic就是双向绑定的图片 url
<uploadPicUni v-model="businessPic" width="285px" describe="点击上传营业执照"></uploadPicUni>

阿里云oss直传部分:

        有部分小伙伴使用阿里云的oss直传可以参考下(使用了crypto进行解密后端返回的oss相关信息,依据自己的开发情况而定)

  代码如下:

npm install dayjs
npm install ali-oss
npm install crypto-js
import CryptoJS from 'crypto-js';
import OSS from 'ali-oss';
import { Post } from '@/api/module/http';

import dayjs from 'dayjs';
// 加密
// // keyStr 设置的长度要不小于14位
export function encrypt(data: any, key: any = '123456789', iv: any = '123456789') {
    if (typeof data === 'object') {
        // 如果传入的data是json对象,先转义为json字符串
        try {
            data = JSON.stringify(data);
        } catch (error) {
            console.log('error:', error);
        }
    }

    // 统一将传入的字符串转成UTF8编码
    const dataHex = CryptoJS.enc.Utf8.parse(data); // 需要加密的数据
    const keyHex = CryptoJS.enc.Utf8.parse(key); // 秘钥
    const ivHex = CryptoJS.enc.Utf8.parse(iv); // 偏移量
    const encrypted = CryptoJS.AES.encrypt(dataHex, keyHex, {
        iv: ivHex,
        mode: CryptoJS.mode.CBC, // 加密模式
        padding: CryptoJS.pad.Pkcs7
    });
    let encryptedVal = encrypted.ciphertext.toString();
    return encryptedVal; //  返回加密后的值
}

// 解密
// 解密数据
export function decrypt(data: any, key: any = '123456789', iv: any = '123456789') {
    /*
  传入的key和iv需要和加密时候传入的key一致  
*/
    // 统一将传入的字符串转成UTF8编码

    let encryptedHexStr = CryptoJS.enc.Hex.parse(data);
    // console.log(encryptedHexStr);
    let srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr);

    const keyHex = CryptoJS.enc.Utf8.parse(key); // 秘钥
    const ivHex = CryptoJS.enc.Utf8.parse(iv); // 偏移量
    let decrypt = CryptoJS.AES.decrypt(srcs, keyHex, {
        iv: ivHex,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });
    let decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);

    return decryptedStr.toString();
}
/**==========OSS上传========== */
export const OssUpload = {
    /**解密 */
    encryptionValue(encryptedText: any, key = 'admin', iv = 'admin') {
        // 将密钥和初始向量解析为 CryptoJS 的 WordArray
        const keyBytes = CryptoJS.enc.Utf8.parse(key);
        const ivBytes = CryptoJS.enc.Utf8.parse(iv);
        // 使用 DES 解密
        const decrypted = CryptoJS.DES.decrypt(encryptedText, keyBytes, {
            iv: ivBytes,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });
        // 将解密后的 WordArray 转换为字符串
        const decryptedText = decrypted.toString(CryptoJS.enc.Utf8);
        return decryptedText;
    },

    /**获取上传文件名称(本部分是后端生成的文件名 例如:936ad6a8-83b9-4bfe-92a9-b17a683cec97) 小伙伴可以自行生成,不能重复*/
    GetFileName(callback: any) {
        Post('/Oss/GetFileName', {}).then((res: any) => {
            return callback(res.rows);
        });
    },

    /**获取oss 配置参数(本文是在除login页面之后调用一次后端接口然后存放在了localStorage中,避免多次获取此信息) */
    GetOssPara(callback: any) {
        // console.log(parent.OSSCONFIG);
        callback(JSON.parse(this.encryptionValue(localStorage.getItem('ossInfo'))));
    },

    /**调用oss官方 ali-oss库进行上传操作 */
    uploadToOSS(file: any, oss: any, callback: any) {
        var fileType = '';
        if (file.name == undefined) {
            switch (file.type) {
                case 'image/jpeg':
                    fileType = 'jpg';
                    break;
                case 'image/png':
                    fileType = 'png';
                    break;
                default:
                    fileType = 'jpg';
            }
        } else {
            const arr = file.name.split('.');
            fileType = arr[arr.length - 1];
        }
        // 导入 ali-oss 库
        const regionRegex = /\/\/([^\.]+)\.aliyuncs\.com/i;
        const match = oss.Endpoint.match(regionRegex);

        // 配置阿里云OSS参数
        const client = new OSS({
            region: match ? match[1] : oss.Endpoint,
            accessKeyId: oss.AccessKeyId,
            accessKeySecret: oss.AccessKeySecret,
            bucket: oss.BucketName
        });
        try {
            this.GetFileName(async (fileName: any) => {
                const uniqueFileName = `${dayjs(new Date()).format(
                    'YYYY-MM-DD'
                )}/${fileName}.${fileType}`;

                // 上传文件到OSS
                const result = await client.put(uniqueFileName, file);
                const d = { Success: true, rows: result };
                callback(d);
            });
        } catch (error) {
            const d = { Success: false, rows: error };
            callback(d);
        }
    },

    /**oss直传整体实现
     * @param {*} file 文件流数据
     * @param {*} callback 回调函数
     */
    OSSUpload(file: any, callback: any) {
        this.GetOssPara((oss: any) => {
            this.uploadToOSS(file, oss, (data: any) => {
                callback(data);
            });
        });
    }
};

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
你的问题不是很明确,但我猜测你想问的是如何在 Vue使用 Element UI 的 el-uploadel-table 组件。如果是这样,可以按照以下步骤: 1. 安装 Element UI:在你的 Vue 项目中使用 npm 或 yarn 安装 Element UI。 2. 引入 el-uploadel-table 组件:在需要使用这两个组件的 Vue 文件中,通过 import 语句将它们引入。 3. 在模板中使用组件:在模板中使用 el-uploadel-table 组件,可以通过 v-model 绑定数据、通过 props 设置属性等。 以下是一个简单的例子,展示了如何使用 el-uploadel-table 组件: ```html <template> <div> <el-upload class="upload-demo" action="/upload" :on-success="handleUploadSuccess" :before-upload="beforeUpload"> <el-button size="small" type="primary">点击上传</el-button> </el-upload> <el-table :data="tableData" style="width: 100%"> <el-table-column prop="name" label="姓名"></el-table-column> <el-table-column prop="age" label="年龄"></el-table-column> </el-table> </div> </template> <script> import { ElUpload, ElButton, ElTable, ElTableColumn } from 'element-ui'; export default { components: { ElUpload, ElButton, ElTable, ElTableColumn }, data() { return { tableData: [] }; }, methods: { handleUploadSuccess(response, file, fileList) { console.log(response, file, fileList); }, beforeUpload(file) { console.log(file); } } } </script> ``` 这个例子中,我们使用el-upload 组件来上传文件,使用el-table 组件来展示数据。在 el-upload 组件中,我们设置了上传成功的回调函数 handleUploadSuccess 和上传前的回调函数 beforeUpload。在 el-table 组件中,我们绑定了数据 tableData,并设置了表格列的属性。 希望这个例子能够帮到你。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值