feat: 头像上传组件

This commit is contained in:
2023-07-20 17:16:56 +08:00
parent fe587dbb3f
commit d1dcea534e
4 changed files with 61 additions and 41 deletions

View File

@ -1,57 +1,34 @@
<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"
/>
<div class="el-upload el-upload--picture-card" @click="showDialog = !disabled">
<el-avatar v-if="avatarUrl" :src="avatarUrl" :size="146" alt="头像" />
<el-icon v-else class="avatar-uploader-icon">
<plus />
<Plus />
</el-icon>
</div>
<slot name="text">
<div class="el-upload__tip w-400px">
<div v-show="!disabled" 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-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"
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"
absolute h-200px w-200px rd="1/2" top="1/2" overflow-hidden
class="translate-x-1/2 -translate-y-1/2" :style="imageStyle"
>
<img
v-show="options.previews.url"
:src="options.previews.url"
:style="options.previews.img"
alt="实时预览图片"
>
<img v-show="options.previews.url" :src="options.previews.url" :style="options.previews.img" alt="实时预览图片">
</div>
</el-col>
</el-row>
@ -66,16 +43,16 @@
</el-button>
</el-col>
<el-col :lg="{ span: 1, offset: 2 }" :md="2">
<el-button icon="Plus" @click="changeScale(1)" />
<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-button :icon="Minus" @click="changeScale(-1)" />
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="RefreshLeft" @click="rotateLeft()" />
<el-button :icon="RefreshLeft" @click="rotateLeft()" />
</el-col>
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
<el-button icon="RefreshRight" @click="rotateRight()" />
<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">
@ -90,6 +67,7 @@
import { ElMessage } from 'element-plus'
import type { CSSProperties } from 'vue'
import { VueCropper } from 'vue-cropper'
import { Minus, Plus, RefreshLeft, RefreshRight, Upload } from '@element-plus/icons-vue'
const props = withDefaults(defineProps<{
accpet?: string
@ -104,7 +82,8 @@ const props = withDefaults(defineProps<{
autoCropHeight?: number
/** 默认生成截图框高度 */
autoCropWidth?: number
uploadFunction?: (...args: any) => Promise<FileVO>
uploadFunction?: (formData: FormData) => Promise<FileVO>
disabled?: boolean
}>(), {
accpet: '.jpeg,.jpg,.png,.bmp',
fileSize: 2,
@ -124,6 +103,7 @@ const avatarUrl = useVModel(props, 'modelValue', emit)
const imageStyle = computed(() => ({
height: `${props.autoCropHeight}px`,
width: `${props.autoCropWidth}px`,
boxShadow: '0 0 4px #ccc',
}))
let loading = $ref(false)
@ -138,7 +118,7 @@ interface PreviewData {
}
interface Option {
/** 裁剪图片的地址 */
/** 裁剪图片的地址 */
img: any
/** 是否默认生成截图框 */
autoCrop: boolean
@ -236,4 +216,5 @@ function closeDialog() {
<style>
@import url('vue-cropper/dist/index.css');
@import url('element-plus/theme-chalk/el-upload.css');
</style>

View File

@ -1,13 +1,49 @@
/*
* @Author: zhaojinfeng 121016171@qq.com
* @Date: 2023-07-18 12:28:36
* @LastEditors: zhaojinfeng 121016171@qq.com
* @LastEditTime: 2023-07-20 17:13:00
* @FilePath: \vue3\stories\UploadAvatar.stories.ts
* @Description:
*
*/
import type { Meta, StoryObj } from '@storybook/vue3'
import ThUploadAvatar from '../packages/upload-avatar/index.vue'
import avatar from './assets/avatar.jpeg'
const meta = {
title: '表单组件/UploadAvatar',
component: ThUploadAvatar,
tags: ['autodocs'],
args: {
modelValue: '',
accpet: '.jpeg,.jpg,.png,.bmp',
disabled: false,
fileSize: 2,
autoCropHeight: 200,
autoCropWidth: 200,
uploadFunction() {
return Promise.resolve({
id: 0,
url: avatar,
})
},
onOnSuccess() {
ElMessage.success('模拟上传成功')
},
},
} satisfies Meta<typeof ThUploadAvatar>
export default meta
type Story = StoryObj<typeof meta>
export const Base: Story = {}
export const Base: Story = {
storyName: '基本使用',
}
export const Preview: Story = {
storyName: '启用disabled当做预览组件',
args: {
modelValue: avatar,
disabled: false,
},
}

BIN
stories/assets/avatar.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

View File

@ -12,6 +12,7 @@ declare global {
const $shallowRef: typeof import('vue/macros')['$shallowRef']
const $toRef: typeof import('vue/macros')['$toRef']
const EffectScope: typeof import('vue')['EffectScope']
const ElMessage: typeof import('element-plus/es')['ElMessage']
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
const computed: typeof import('vue')['computed']
@ -306,6 +307,7 @@ declare module 'vue' {
readonly $shallowRef: UnwrapRef<typeof import('vue/macros')['$shallowRef']>
readonly $toRef: UnwrapRef<typeof import('vue/macros')['$toRef']>
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
readonly ElMessage: UnwrapRef<typeof import('element-plus/es')['ElMessage']>
readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
readonly computed: UnwrapRef<typeof import('vue')['computed']>
@ -594,6 +596,7 @@ declare module '@vue/runtime-core' {
readonly $shallowRef: UnwrapRef<typeof import('vue/macros')['$shallowRef']>
readonly $toRef: UnwrapRef<typeof import('vue/macros')['$toRef']>
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
readonly ElMessage: UnwrapRef<typeof import('element-plus/es')['ElMessage']>
readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
readonly computed: UnwrapRef<typeof import('vue')['computed']>