当前位置: 首页 > news >正文

前端上传图片加裁剪功能

首先要下载安装cropperjs插件

npm install cropperjs

组件内引入

import Cropper from 'cropperjs';
import 'cropperjs/dist/cropper.css';

封装组件

<template><div class="upload_img"><ElDialogv-model="showUploadDialog":class="'create'":align-center="true":title="title"lock-scrollwidth="560"@close="onCancel"><div class="upload_img_wrap"><div class="upload_img_title"><ElUploadref="uploadRef"action="#"class="avatar-uploader":auto-upload="false":show-file-list="false":limit="1":accept="'image/*'"@change="handleBeforeUpload"><Icon :icon="`svg-icon:upload_img_icon`" :size="18" /><span class="upload_text">请选择要上传的图片</span></ElUpload></div><div class="upload_img_content"><div class="edit_wrap"><img v-if="defaultAvatar" :src="defaultAvatar" ref="imgRef" /><p v-else>{{ placeholder }}</p></div><div class="preview_wrap" ref="previewRef"></div></div></div><template #footer><FooterBar @confirm="onConfirm" @cancel="onCancel" /></template></ElDialog></div>
</template>
<script setup lang="ts">
import Cropper from 'cropperjs';
import 'cropperjs/dist/cropper.css';
import { Icon } from '@/components/pc/Icon';
import FooterBar from '@/components/pc/iForm/src/form_dialog/footer_bar.vue';
import { uploadApi } from '@/api/iform';
import { cropperProps } from '@/types/cropper_types';
import { dataURLtoBlob } from '@/utils/index';const { props } = defineProps<{ props: cropperProps }>();
const title = ref(props?.title ?? '上传图片');
const placeholder = ref(props?.placeholder ?? '选择一张本地图片进行编辑');
const aspectRatio = ref(props?.aspectRatio ?? 1);
const cropBoxResizable = ref(props?.cropBoxResizable ?? false);
const exportImgWidth = ref(props?.exportImgWidth ?? 300);
const exportImgHeight = ref(props?.exportImgHeight ?? 300);
const exportImgType = ref(props?.exportImgType ?? 'image/png');const showUploadDialog = ref(true);
const emit = defineEmits(['confirm', 'cancel']);
const defaultAvatar = ref('');
const imgRef = ref();
const previewRef = ref();
let cropper;const uploadRef = ref();
// 裁剪相关配置
const initCropper = () => {cropper && cropper.destroy();nextTick(() => {cropper = new Cropper(imgRef.value, {aspectRatio: unref(aspectRatio),preview: previewRef.value,guides: false,autoCrop: true,autoCropArea: 0.5,movable: true,cropBoxResizable: unref(cropBoxResizable),scalable: true,zoomable: true,rotatable: true,dragMode: 'none',viewMode: 1,minCropBoxWidth: 100,minCropBoxHeight: 100,ready: () => {uploadRef.value?.clearFiles();},});});
};const acceptList = ['JPG', 'PNG', 'JPEG'];
const handleBeforeUpload = async (file) => {const { raw } = file;const fileType = getFileType(raw?.name || '');if (!acceptList.includes(fileType)) {$message({message: `仅支持${acceptList.join(',')}类型`,type: 'warning',});uploadRef.value?.clearFiles();} else {defaultAvatar.value = URL.createObjectURL(raw);initCropper();}
};onBeforeUnmount(() => {cropper && cropper.destroy();
});const getFileType = (fileName: string) => {const decimalIndex = fileName.lastIndexOf('.');if (decimalIndex === -1) {return '';}return fileName.substring(decimalIndex + 1).toLocaleUpperCase();
};const onConfirm = async () => {const base64Data = cropper.getCroppedCanvas({width: unref(exportImgWidth),height: unref(exportImgHeight),}).toDataURL(unref(exportImgType));const { blob, name } = dataURLtoBlob(base64Data);const formData: any = new FormData();formData.append('file', blob, name); // 第三个参数传入生成的随机文件名formData.append('uploadSource', 1);const loading = ElLoading.service({fullscreen: true,lock: true,background: 'rgba(0, 0, 0, 0.7)',});const res: any = await uploadApi(formData);loading.close();if (res?.code == 200 && res?.data != undefined) {emit('confirm', res.data?.lastingFileUrl || '');$message({message: `保存成功`,type: 'success',});} else {$message({message: res?.msg || '操作失败, 请检查网络',type: 'error',});return false;}
};
const onCancel = () => {emit('cancel');
};
</script>
<style lang="less" scoped>
.upload_img {.upload_img_wrap {.upload_img_title {padding-bottom: 12px;.avatar-uploader {width: 190px;display: flex;align-items: center;justify-content: center;padding: 5px 10px;box-sizing: border-box;border-radius: 6px;border: 1px solid @border-color;.upload_text {margin-left: 2px;color: @btn-color;}}}.upload_img_content {display: flex;align-items: flex-start;justify-content: space-between;.edit_wrap {width: 360px;height: 360px;background-color: @input-bg-gray;position: relative;> img {display: block;width: 100%;}> p {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);font-weight: 400;font-size: 14px;color: @tab-btn-color;width: 100%;text-align: center;}}.preview_wrap {width: 160px;height: 160px;overflow: hidden;margin-left: 24px;background-color: @input-bg-gray;border-radius: 6px;}}}:deep(.el-dialog__body) {padding: 5px 20px;}
}
</style>
http://www.dtcms.com/a/572777.html

相关文章:

  • DevEco Studio 鸿蒙 引入lib中的文件
  • 简述数据库设计--范式、ER图
  • 【Linux】权限(1):初识权限与使用理解
  • 深圳专业做网站的公司河南企业建站系统信息
  • 企业门户网站设计报告wordpress接口
  • 基于大数据的天气分析与应用系统
  • spark读取table中的数据【hive】
  • 最后一轮征稿开启 | ACM出版 | 第二届大数据分析与人工智能应用学术会议(BDAIA2025)
  • 史诗级:在麒麟离线服务器上部署 Dify (含 Weaviate、Nginx 网关、FIP 及离线插件)
  • 潮州网站制作网站建设与管理怎么样
  • 一次实时采集任务延迟问题的完整复盘(Flink CDC)
  • Linux常用命令练习题
  • 常见的接口协议有哪些?(HTTP/HTTPS、REST、SOAP、WebSocket等)
  • Linux 进阶权限管理核心:权限掩码umask与粘滞位的深度解析
  • Flink+Paimon+StarRocks 构建实时分析
  • 快速搭建网站2020创业平台的选择
  • 使用C#代码添加或删除PPT页面
  • 12个月嵌入式进阶计划ZYNQ 系列芯片嵌入式与硬件系统知识学习全计划(基于国内视频资源)
  • 商务演示专用AI PPT工具深度测评:哪些软件能让汇报更出彩?
  • 公司网站空间做那个免费视频网站
  • 【RabbitMQ的应用】
  • RabbitMQ的使用
  • 从零搭建 C++ 在线五子棋对战项目:从环境到上线,全流程保姆级教程
  • 基于传输熵理论的通信网络拓扑结构推理算法matlab仿真
  • 【基于one-loop-per-thread的高并发服务器】--- 前置技术
  • 企业级调度器 LVS 基础知识总结
  • 多线程异常、MQ、Kafka(八股)
  • 好的做蛋糕网站如何做网站淘客推广
  • 中国风网站建设网站建设信息平台
  • IndexedDB开发示例:面向对象的方式