|
@@ -0,0 +1,203 @@
|
|
|
|
+<!-- eslint-disable @typescript-eslint/no-non-null-assertion -->
|
|
|
|
+<script lang="ts" setup>
|
|
|
|
+import 'vue-cropper/dist/index.css'
|
|
|
|
+import { VueCropper } from 'vue-cropper'
|
|
|
|
+import type { UploadFile } from 'element-plus'
|
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
|
+import type { UploadInstance } from 'element-plus'
|
|
|
|
+
|
|
|
|
+const uploadRef = ref<UploadInstance>()
|
|
|
|
+interface Options {
|
|
|
|
+ img: string | ArrayBuffer | null // 裁剪图片的地址
|
|
|
|
+ info: true // 裁剪框的大小信息
|
|
|
|
+ outputSize: number // 裁剪生成图片的质量 [1至0.1]
|
|
|
|
+ outputType: string // 裁剪生成图片的格式
|
|
|
|
+ canScale: boolean // 图片是否允许滚轮缩放
|
|
|
|
+ autoCrop: boolean // 是否默认生成截图框
|
|
|
|
+ autoCropWidth: number // 默认生成截图框宽度
|
|
|
|
+ autoCropHeight: number // 默认生成截图框高度
|
|
|
|
+ fixedBox: boolean // 固定截图框大小 不允许改变
|
|
|
|
+ fixed: boolean // 是否开启截图框宽高固定比例
|
|
|
|
+ fixedNumber: Array<number> // 截图框的宽高比例 需要配合centerBox一起使用才能生效
|
|
|
|
+ full: boolean // 是否输出原图比例的截图
|
|
|
|
+ canMoveBox: boolean // 截图框能否拖动
|
|
|
|
+ original: boolean // 上传图片按照原始比例渲染
|
|
|
|
+ centerBox: boolean // 截图框是否被限制在图片里面
|
|
|
|
+ infoTrue: boolean // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
|
|
|
|
+ accept: string // 上传允许的格式
|
|
|
|
+ mode: string
|
|
|
|
+}
|
|
|
|
+// 父组件传参props
|
|
|
|
+const props = defineProps<{
|
|
|
|
+ dialogVisible: boolean
|
|
|
|
+ circleUrl: string
|
|
|
|
+}>()
|
|
|
|
+
|
|
|
|
+const dialogVisible = ref(props.dialogVisible)
|
|
|
|
+
|
|
|
|
+const emits = defineEmits(['closeAvatarDialog'])
|
|
|
|
+// 裁剪组件需要使用到的参数
|
|
|
|
+const options = reactive<Options>({
|
|
|
|
+ img: props.circleUrl, // 需要剪裁的图片
|
|
|
|
+ autoCrop: true, // 是否默认生成截图框
|
|
|
|
+ autoCropWidth: 200, // 默认生成截图框的宽度
|
|
|
|
+ autoCropHeight: 200, // 默认生成截图框的长度
|
|
|
|
+ fixedBox: true, // 是否固定截图框的大小 不允许改变
|
|
|
|
+ info: true, // 裁剪框的大小信息
|
|
|
|
+ outputSize: 1, // 裁剪生成图片的质量 [1至0.1]
|
|
|
|
+ outputType: 'png', // 裁剪生成图片的格式
|
|
|
|
+ canScale: true, // 图片是否允许滚轮缩放
|
|
|
|
+ fixed: true, // 是否开启截图框宽高固定比例
|
|
|
|
+ fixedNumber: [1, 1], // 截图框的宽高比例 需要配合centerBox一起使用才能生效 1比1
|
|
|
|
+ full: false, // 是否输出原图比例的截图
|
|
|
|
+ canMoveBox: false, // 截图框能否拖动
|
|
|
|
+ original: true, // 上传图片按照原始比例渲染
|
|
|
|
+ centerBox: true, // 截图框是否被限制在图片里面
|
|
|
|
+ infoTrue: false, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
|
|
|
|
+ accept: 'image/jpeg,image/jpg,image/png,image/gif,image/x-icon',
|
|
|
|
+ mode: 'contain'
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+const beforeUpload = (file: UploadFile) => {
|
|
|
|
+ console.log(file)
|
|
|
|
+ const { size } = file
|
|
|
|
+ const fileType = file.raw?.type
|
|
|
|
+ const type = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/x-icon']
|
|
|
|
+ if (type.indexOf(fileType!) === -1) {
|
|
|
|
+ ElMessage.warning('仅支持jpeg,jpg,png,gif,icon格式上传')
|
|
|
|
+ uploadRef.value?.clearFiles()
|
|
|
|
+ } else if (Number(size) / 1024 / 1024 > 2) {
|
|
|
|
+ ElMessage.error('图片大小不能超过2MB!')
|
|
|
|
+ uploadRef.value?.clearFiles()
|
|
|
|
+ } else {
|
|
|
|
+ const reader = new FileReader()
|
|
|
|
+ // 转化为base64
|
|
|
|
+ reader.readAsDataURL(file.raw!)
|
|
|
|
+ reader.onload = () => {
|
|
|
|
+ // 获取到需要剪裁的图片 展示到剪裁框中
|
|
|
|
+ options.img = reader.result
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const previews: any = ref({})
|
|
|
|
+const showBoxStyle: any = ref({})
|
|
|
|
+
|
|
|
|
+const cropperRef: any = ref({})
|
|
|
|
+const previewHandle = (data: any) => {
|
|
|
|
+ previews.value = data // 预览img图片
|
|
|
|
+ showBoxStyle.value = {
|
|
|
|
+ width: data.w + 'px',
|
|
|
|
+ height: data.h + 'px',
|
|
|
|
+ overflow: 'hidden',
|
|
|
|
+ margin: '0',
|
|
|
|
+ zoom: 200 / data.h
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const goSave = () => {
|
|
|
|
+ cropperRef.value.getCropData((data: any) => {
|
|
|
|
+ emits('closeAvatarDialog', data)
|
|
|
|
+ })
|
|
|
|
+ cropperRef.value.getCropBlob((data: any) => {
|
|
|
|
+ // console.log(data)
|
|
|
|
+ })
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const changeScale = (num: number) => {
|
|
|
|
+ const scale = num || 1
|
|
|
|
+ cropperRef.value.changeScale(scale)
|
|
|
|
+}
|
|
|
|
+const rotateLeft = () => {
|
|
|
|
+ cropperRef.value.rotateLeft()
|
|
|
|
+}
|
|
|
|
+const rotateRight = () => {
|
|
|
|
+ cropperRef.value.rotateRight()
|
|
|
|
+}
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<template>
|
|
|
|
+ <el-dialog
|
|
|
|
+ title="上传头像"
|
|
|
|
+ v-model="dialogVisible"
|
|
|
|
+ :show-close="false"
|
|
|
|
+ :close-on-click-modal="false"
|
|
|
|
+ :close-on-press-escape="false"
|
|
|
|
+ destroy-on-close
|
|
|
|
+ width="65%"
|
|
|
|
+ >
|
|
|
|
+ <div class="croWrap">
|
|
|
|
+ <div style="width: 600px; height: 400px">
|
|
|
|
+ <vueCropper
|
|
|
|
+ ref="cropperRef"
|
|
|
|
+ :img="options.img"
|
|
|
|
+ :info="true"
|
|
|
|
+ :autoCropWidth="options.autoCropWidth"
|
|
|
|
+ :autoCropHeight="options.autoCropHeight"
|
|
|
|
+ :info-true="options.infoTrue"
|
|
|
|
+ :auto-crop="options.autoCrop"
|
|
|
|
+ :fixed-box="options.fixedBox"
|
|
|
|
+ :can-move="options.canMoveBox"
|
|
|
|
+ :can-scale="options.canScale"
|
|
|
|
+ :fixed-number="options.fixedNumber"
|
|
|
|
+ :fixed="options.fixed"
|
|
|
|
+ :full="options.full"
|
|
|
|
+ :center-box="options.centerBox"
|
|
|
|
+ :mode="options.mode"
|
|
|
|
+ @real-time="previewHandle"
|
|
|
|
+ />
|
|
|
|
+ </div>
|
|
|
|
+ <div class="previewBox">
|
|
|
|
+ <div :style="showBoxStyle">
|
|
|
|
+ <img :src="previews.url" :style="previews.img" />
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="btnWrap">
|
|
|
|
+ <el-upload
|
|
|
|
+ ref="uploadRef"
|
|
|
|
+ class="upload-demo"
|
|
|
|
+ action="#"
|
|
|
|
+ :auto-upload="false"
|
|
|
|
+ :on-change="beforeUpload"
|
|
|
|
+ :show-file-list="false"
|
|
|
|
+ >
|
|
|
|
+ <template #trigger>
|
|
|
|
+ <el-button type="primary">选择图片</el-button>
|
|
|
|
+ </template>
|
|
|
|
+ </el-upload>
|
|
|
|
+ <el-button @click="changeScale(1)">放大</el-button>
|
|
|
|
+ <el-button @click="changeScale(-1)">缩小</el-button>
|
|
|
|
+ <el-button @click="rotateLeft">向左</el-button>
|
|
|
|
+ <el-button @click="rotateRight">向右</el-button>
|
|
|
|
+ </div>
|
|
|
|
+ <template #footer>
|
|
|
|
+ <span class="dialog-footer">
|
|
|
|
+ <el-button @click="dialogVisible = false">Cancel</el-button>
|
|
|
|
+ <el-button type="primary" @click="goSave">确定</el-button>
|
|
|
|
+ </span>
|
|
|
|
+ </template>
|
|
|
|
+ </el-dialog>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<style lang="scss" scoped>
|
|
|
|
+.btnWrap {
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ margin-top: 12px;
|
|
|
|
+ .upload-demo {
|
|
|
|
+ margin-right: 12px;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+.croWrap {
|
|
|
|
+ display: flex;
|
|
|
|
+ justify-content: space-between;
|
|
|
|
+ .previewBox {
|
|
|
|
+ width: 200px;
|
|
|
|
+ height: 200px;
|
|
|
|
+ border: 1px solid #ccc;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ border-radius: 50%;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+</style>
|