feat(components): 添加图片裁剪组件

- 新增 RenderCropper组件,用于图片上传和裁剪功能
- 在 knowledge 页面中集成图片裁剪组件
- 在主文件中注册图片裁剪组件- 优化了 label 样式,注释掉行高属性
This commit is contained in:
陈昱达
2025-04-29 11:08:25 +08:00
parent 464a6b1735
commit 8c5970fa76
3 changed files with 204 additions and 0 deletions

View File

@@ -0,0 +1,201 @@
<template>
<div>
<el-upload
action="#"
list-type="picture-card"
:auto-upload="false"
:limit="1"
:file-list="fileList"
:on-change="change"
>
<i slot="default" class="el-icon-plus"></i>
<div slot="file" slot-scope="{ file }">
<img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
<span class="el-upload-list__item-actions">
<span class="el-upload-list__item-delete" @click="handleRemove(file)">
<i class="el-icon-delete"></i>
</span>
</span>
</div>
</el-upload>
<r-dialog
title="图片裁剪"
:visible.sync="visible"
@confirm="confirm"
width="600px"
>
<div class="flex">
<cropper-canvas style="width: 100%;flex:1" cover image>
<cropper-image
:src="file.url"
alt="Picture"
rotatable
scalable
skewable
translatable
></cropper-image>
<cropper-shade hidden></cropper-shade>
<cropper-handle action="select" plain></cropper-handle>
<cropper-selection
id="cropperSelection"
movable
resizable
x="150"
y="100"
width="275"
height="275"
ref="selection"
>
<cropper-handle
action="move"
theme-color="rgba(255, 255, 255, 0.35)"
></cropper-handle>
<cropper-handle action="n-resize"></cropper-handle>
<cropper-handle action="e-resize"></cropper-handle>
<cropper-handle action="s-resize"></cropper-handle>
<cropper-handle action="w-resize"></cropper-handle>
<cropper-handle action="ne-resize"></cropper-handle>
<cropper-handle action="nw-resize"></cropper-handle>
<cropper-handle action="se-resize"></cropper-handle>
<cropper-handle action="sw-resize"></cropper-handle>
</cropper-selection>
</cropper-canvas>
<div
class="cropperSelection"
style="flex:0;border-radius: 8px; border: 1px solid #ccc;overflow: hidden"
>
<!-- 修改: 添加 selection 属性并绑定到 file.url -->
<cropper-viewer
style="width: 100%;height: 100%"
selection="#cropperSelection"
initial-aspect-ratio="1.5"
initial-coverage="0.5"
></cropper-viewer>
</div>
</div>
</r-dialog>
</div>
</template>
<script>
import Cropper from 'cropperjs'
export default {
name: 'index',
data() {
return {
fileList: [],
cropper: null,
imageUrl: '',
visible: false,
file: {
url: ''
}
}
},
props: {},
watch: {},
components: {},
filters: {},
methods: {
handleRemove() {
this.fileList = []
},
change(file) {
this.file = file
this.fileList = []
this.visible = true
// setTimeout(() => {
// this.cropper = new Cropper('#image', {})
// }, 300)
},
confirm() {
this.$refs.selection.$toCanvas().then(canvas => {
// 根据canvas 生成一个图片 并转换成file
const image = canvas.toDataURL()
this.dataURLtoFile(image, this.file.name).then(res => {
// 文件生成 bolburl
const blobUrl = URL.createObjectURL(res)
res.url = blobUrl
this.fileList.push(res)
this.$emit('getFiles', this.fileList)
this.visible = false
})
// 根据canvas 生成一个图片
})
},
// 修改: 优化 base64 转文件的逻辑,增加压缩功能
dataURLtoFile(dataurl, filename) {
let arr = dataurl.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
// 创建临时 canvas 元素用于压缩图片
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
const img = new Image()
// 返回一个 Promise确保异步操作完成后再返回文件
return new Promise(resolve => {
img.src = dataurl
img.onload = () => {
// 限制最大宽度或高度为 800px可根据需求调整
const maxWidthOrHeight = 800
let width = img.width
let height = img.height
if (width > height) {
if (width > maxWidthOrHeight) {
height *= maxWidthOrHeight / width
width = maxWidthOrHeight
}
} else {
if (height > maxWidthOrHeight) {
width *= maxWidthOrHeight / height
height = maxWidthOrHeight
}
}
canvas.width = width
canvas.height = height
ctx.drawImage(img, 0, 0, width, height)
// 将 canvas 转换为 base64质量设置为 0.8(可根据需求调整)
const compressedDataUrl = canvas.toDataURL(mime, 0.8)
// 将压缩后的 base64 转换为文件
const compressedArr = compressedDataUrl.split(',')
const compressedBstr = atob(compressedArr[1])
const compressedN = compressedBstr.length
const compressedU8arr = new Uint8Array(compressedN)
for (let i = 0; i < compressedN; i++) {
compressedU8arr[i] = compressedBstr.charCodeAt(i)
}
resolve(new File([compressedU8arr], filename, { type: mime }))
}
})
}
},
created() {},
mounted() {},
computed: {}
}
</script>
<style lang="scss">
cropper-canvas {
height: 50vh;
}
.cropperSelection {
//width: 20vw;
max-height: 30vw !important;
}
</style>