refactor(components): refactor avatar (#3364)

This commit is contained in:
三咲智子 2021-09-13 10:46:12 +08:00 committed by GitHub
parent d26070d939
commit 19752ba778
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 116 additions and 120 deletions

View File

@ -6,7 +6,7 @@ import {
mockImageEvent,
} from '@element-plus/test-utils'
import Avatar from '../src/index.vue'
import Avatar from '../src/avatar.vue'
describe('Avatar.vue', () => {
mockImageEvent()

View File

@ -1,12 +1,7 @@
import { App } from 'vue'
import type { SFCWithInstall } from '@element-plus/utils/types'
import Avatar from './src/index.vue'
import Avatar from './src/avatar.vue'
import { withInstall } from '@element-plus/utils/with-install'
Avatar.install = (app: App): void => {
app.component(Avatar.name, Avatar)
}
export const ElAvatar = withInstall(Avatar)
export default ElAvatar
const _Avatar = Avatar as SFCWithInstall<typeof Avatar>
export default _Avatar
export const ElAvatar = _Avatar
export * from './src/avatar'

View File

@ -0,0 +1,35 @@
import { buildProp } from '@element-plus/utils/props'
import type { CSSProperties, ExtractPropTypes } from 'vue'
export const avatarProps = {
size: buildProp({
type: [Number, String],
values: ['large', 'medium', 'small'],
default: 'large',
validator: (val): val is number => typeof val === 'number',
} as const),
shape: buildProp({
type: String,
values: ['circle', 'square'],
default: 'circle',
} as const),
icon: String,
src: {
type: String,
default: '',
},
alt: String,
srcSet: String,
fit: buildProp<CSSProperties['objectFit']>({
type: String,
default: 'cover',
} as const),
} as const
export type AvatarProps = ExtractPropTypes<typeof avatarProps>
export const avatarEmits = {
error: (evt: Event) => evt instanceof Event,
}
export type AvatarEmits = typeof avatarEmits

View File

@ -0,0 +1,75 @@
<template>
<span :class="avatarClass" :style="sizeStyle">
<img
v-if="(src || srcSet) && !hasLoadError"
:src="src"
:alt="alt"
:srcset="srcSet"
:style="fitStyle"
@error="handleError"
/>
<i v-else-if="icon" :class="icon"></i>
<slot v-else></slot>
</span>
</template>
<script lang="ts">
import { defineComponent, computed, ref, watch } from 'vue'
import { avatarEmits, avatarProps } from './avatar'
import type { CSSProperties } from 'vue'
export default defineComponent({
name: 'ElAvatar',
props: avatarProps,
emits: avatarEmits,
setup(props, { emit }) {
const hasLoadError = ref(false)
const avatarClass = computed(() => {
const { size, icon, shape } = props
const classList = ['el-avatar']
if (size && typeof size === 'string') classList.push(`el-avatar--${size}`)
if (icon) classList.push('el-avatar--icon')
if (shape) classList.push(`el-avatar--${shape}`)
return classList
})
const sizeStyle = computed<CSSProperties>(() => {
const { size } = props
return typeof size === 'number'
? {
height: `${size}px`,
width: `${size}px`,
lineHeight: `${size}px`,
}
: {}
})
const fitStyle = computed<CSSProperties>(() => ({
objectFit: props.fit,
}))
// need reset hasLoadError to false if src changed
watch(
() => props.src,
() => (hasLoadError.value = false)
)
function handleError(e: Event) {
hasLoadError.value = true
emit('error', e)
}
return {
hasLoadError,
avatarClass,
sizeStyle,
fitStyle,
handleError,
}
},
})
</script>

View File

@ -1,109 +0,0 @@
<template>
<span :class="avatarClass" :style="sizeStyle">
<img
v-if="(src || srcSet) && !hasLoadError"
:src="src"
:alt="alt"
:srcset="srcSet"
:style="fitStyle"
@error="handleError"
/>
<i v-else-if="icon" :class="icon"></i>
<slot v-else></slot>
</span>
</template>
<script lang="ts">
import { defineComponent, computed, ref, watch, toRef } from 'vue'
import type { CSSProperties, PropType } from 'vue'
const ERROR_EVENT = 'error'
export default defineComponent({
name: 'ElAvatar',
props: {
size: {
type: [Number, String] as PropType<number | string>,
validator(this: never, val: unknown) {
if (typeof val === 'string') {
return ['large', 'medium', 'small'].includes(val)
}
return typeof val === 'number'
},
default: 'large',
},
shape: {
type: String,
default: 'circle',
validator(this: never, val: string) {
return ['circle', 'square'].includes(val)
},
},
icon: String,
src: {
type: String,
default: '',
},
alt: String,
srcSet: String,
fit: {
type: String,
default: 'cover',
},
},
emits: [ERROR_EVENT],
setup(props, { emit }) {
const hasLoadError = ref(false)
const src = toRef(props, 'src')
// need reset hasLoadError to false if src changed
watch(src, () => {
hasLoadError.value = false
})
const avatarClass = computed(() => {
const { size, icon, shape } = props
const classList = ['el-avatar']
if (size && typeof size === 'string') {
classList.push(`el-avatar--${size}`)
}
if (icon) {
classList.push('el-avatar--icon')
}
if (shape) {
classList.push(`el-avatar--${shape}`)
}
return classList
})
const sizeStyle = computed(() => {
const { size } = props
return typeof size === 'number'
? {
height: `${size}px`,
width: `${size}px`,
lineHeight: `${size}px`,
}
: {}
})
const fitStyle = computed(
() =>
({
objectFit: props.fit,
} as CSSProperties)
)
function handleError(e: Event) {
hasLoadError.value = true
emit(ERROR_EVENT, e)
}
return {
hasLoadError,
avatarClass,
sizeStyle,
handleError,
fitStyle,
}
},
})
</script>