feat: 组件
This commit is contained in:
239
packages/upload-avatar/index.vue
Normal file
239
packages/upload-avatar/index.vue
Normal 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,且格式为jpeg、jpg、png或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>
|
Reference in New Issue
Block a user