feat: 组件

This commit is contained in:
2023-07-18 22:06:20 +08:00
parent bb673dfd8a
commit 30a7f03650
14 changed files with 613 additions and 0 deletions

View File

@ -0,0 +1,239 @@
<template>
<div class="upload-avatar">
<div class="el-upload el-upload--picture-card" @click="showDialog = true">
<image-avatar
v-if="avatarUrl"
:file-id="avatarUrl"
:size="146"
/>
<el-icon v-else class="avatar-uploader-icon">
<plus />
</el-icon>
</div>
<slot name="text">
<div class="el-upload__tip w-400px">
<slot name="tip">
单张图片不大于{{ fileSize }}MB且格式为jpegjpgpng或bmp只可上传1张
</slot>
</div>
</slot>
</div>
<el-dialog
v-model="showDialog"
title="上传头像"
width="800px"
append-to-body
@opened="modalOpened"
@closed="closeDialog"
>
<el-row>
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
<VueCropper
v-if="visible"
ref="cropperRef"
:img="cropperURL"
:info="true"
:auto-crop="options.autoCrop"
:auto-crop-width="autoCropWidth"
:auto-crop-height="autoCropHeight"
:fixed-box="options.fixedBox"
:output-type="options.outputType"
@real-time="realTime"
/>
</el-col>
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
<div
class="avatar-upload-preview"
:style="imageStyle"
>
<img
v-show="options.previews.url"
:src="options.previews.url"
:style="options.previews.img"
alt="实时预览图片"
>
</div>
</el-col>
</el-row>
<br>
<el-row>
<el-col :lg="2" :md="2">
<el-button @click="selectImage">
选择
<el-icon class="el-icon--right">
<Upload />
</el-icon>
</el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 2 }" :md="2">
<el-button icon="Plus" @click="changeScale(1)" />
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="Minus" @click="changeScale(-1)" />
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="RefreshLeft" @click="rotateLeft()" />
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="RefreshRight" @click="rotateRight()" />
</el-col>
<el-col :lg="{ span: 2, offset: 6 }" :md="2">
<el-button type="primary" :loading="loading" @click="confirmUpload">
</el-button>
</el-col>
</el-row>
</el-dialog>
</template>
<script lang="ts" setup name="ThUploadAvatar">
import { ElMessage } from 'element-plus'
import type { CSSProperties } from 'vue'
import { VueCropper } from 'vue-cropper'
const props = withDefaults(defineProps<{
accpet?: string
modelValue?: string
/**
* 图片大小单位MB
*
* @default 2
*/
fileSize?: number
/** 默认生成截图框宽度 */
autoCropHeight?: number
/** 默认生成截图框高度 */
autoCropWidth?: number
uploadFunction?: (...args: any) => Promise<FileVO>
}>(), {
accpet: '.jpeg,.jpg,.png,.bmp',
fileSize: 2,
autoCropHeight: 200,
autoCropWidth: 200,
uploadFunction: () => Promise.resolve({}),
})
const emit = defineEmits<{
(event: 'update:modelValue', modelValue?: string): void
(event: 'onSuccess', file: FileVO): void
(event: 'onError', error: Error): void
}>()
const avatarUrl = useVModel(props, 'modelValue', emit)
const imageStyle = computed(() => ({
height: `${props.autoCropHeight}px`,
width: `${props.autoCropWidth}px`,
}))
let loading = $ref(false)
interface PreviewData {
div: CSSProperties
h: number
html: string
img: CSSProperties
url: string
w: number
}
interface Option {
/** 裁剪图片的地址 */
img: any
/** 是否默认生成截图框 */
autoCrop: boolean
/** 固定截图框大小 不允许改变 */
fixedBox: boolean
/** 默认生成截图为PNG格式 */
outputType: string
/** 预览数据 */
previews: Partial<PreviewData>
visible: boolean
}
// 图片裁剪数据
const options = reactive<Option>({
img: '',
autoCrop: true,
fixedBox: true,
outputType: 'png',
previews: {},
visible: false,
})
const visible = ref(false)
const cropperRef = $ref<InstanceType<typeof VueCropper>>()
const { open, reset, onChange } = useFileDialog()
let showDialog = $ref(false)
/** 向左旋转 */
function rotateLeft() {
cropperRef?.rotateLeft()
}
/** 向右旋转 */
function rotateRight() {
cropperRef?.rotateRight()
}
/** 打开弹出层结束时的回调 */
function modalOpened() {
visible.value = true
}
/** 图片缩放 */
function changeScale(num?: number) {
cropperRef?.changeScale(num || 1)
}
/** 实时预览 */
function realTime(data: PreviewData) {
options.previews = data
}
const file = shallowRef()
const cropperURL = useObjectUrl(file)
onChange((param) => {
file.value = param?.item(0)
})
function selectImage() {
open({
accept: props.accpet,
multiple: false,
})
}
async function uploadImg(blob: Blob) {
if (blob.size > props.fileSize * 1024 * 1024) {
ElMessage.error('您的图片经过剪裁依然很大,请重新选择图片')
return
}
loading = true
const formData = new FormData()
formData.append('file', blob)
try {
const res = await props.uploadFunction(formData)
avatarUrl.value = res.url
showDialog = false
emit('onSuccess', res)
}
catch (err: any) {
emit('onError', err)
loading = false
}
}
function confirmUpload() {
cropperRef?.getCropBlob(uploadImg)
}
/** 关闭窗口 */
function closeDialog() {
options.visible = false
reset()
loading = false
}
</script>
<style>
@import url('vue-cropper/dist/index.css');
</style>