This commit is contained in:
2026-01-05 12:47:14 +08:00
commit 1fc846fae3
1614 changed files with 162035 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
import type { ExtractPropTypes } from 'vue'
import { commonProps, makeNumericProp, makeStringProp } from '../_utils'
import type { AvatarShape, AvatarSize } from './type'
export const avatarProps = {
...commonProps,
/**
* @description 头像的大小,可选值为:`large`、`normal`、`small`,支持直接输入数字
*/
size: makeNumericProp<AvatarSize | string | number | undefined>(undefined),
/**
* @description 头像的形状,可选值为:`square`、`round`
*/
shape: makeStringProp<AvatarShape | undefined>(undefined),
/**
* @description 背景色
*/
bgColor: makeStringProp('#eee'),
/**
* @description 字体颜色
*/
customColor: makeStringProp('#666'),
}
export type AvatarProps = ExtractPropTypes<typeof avatarProps>

View File

@@ -0,0 +1,139 @@
<script setup lang="ts">
import type { CSSProperties } from 'vue'
import { computed, defineComponent, getCurrentInstance, ref, watch } from 'vue'
import { PREFIX } from '../_constants'
import { useInject } from '../_hooks'
import { getMainClass, getMainStyle, pxCheck } from '../_utils'
import type { AvatarGroupProps } from '../avatargroup'
import { AVATAR_GROUP_KEY } from '../avatargroup'
import { avatarProps } from './avatar'
import type { AvatarFinalSize, AvatarShape, AvatarSize } from './type'
import { avatarSize } from './type'
const props = defineProps(avatarProps)
const instance = getCurrentInstance()
const { parent } = useInject<{ props: Required<AvatarGroupProps> }>(AVATAR_GROUP_KEY)
const show = ref(true)
const innerZIndex = ref<number | undefined>(undefined)
watch(() => ({
maxCount: parent?.props.maxCount,
children: parent?.internalChildren,
}), ({ maxCount, children }) => {
if (maxCount == null || Number(maxCount) <= 0 || children == null || instance == null) {
show.value = true
innerZIndex.value = undefined
return
}
const index = children.findIndex((item) => {
return item.uid === instance.uid && !(item.props.customClass as string)?.includes('avatar-fold')
})
if (index < 0) {
show.value = true
innerZIndex.value = undefined
return
}
show.value = index < Number(maxCount)
if (parent?.props.zIndex === 'right')
innerZIndex.value = children.length - index
else
innerZIndex.value = undefined
}, {
immediate: true,
deep: true,
})
function getTrulySize() {
if (props.size != null)
return props.size
if (parent != null && parent.props.size != null)
return parent.props.size
return 'normal'
}
const finalSize = computed<AvatarFinalSize>(() => {
const size: string | number = getTrulySize()
const preset: boolean = avatarSize.includes(size as AvatarSize)
return {
preset,
value: preset ? (size as AvatarSize) : pxCheck(size),
}
})
const finalShape = computed<AvatarShape>(() => {
if (props.shape != null)
return props.shape
if (parent != null && parent.props.shape != null)
return parent.props.shape
return 'round'
})
const classes = computed(() => {
const value: Record<string, boolean> = {
[`nut-avatar-${finalShape.value}`]: true,
'nut-hidden': !show.value,
}
if (finalSize.value.preset)
value[`nut-avatar-${finalSize.value.value}`] = true
return getMainClass(props, componentName, value)
})
const styles = computed(() => {
const value: CSSProperties = {
backgroundColor: props.bgColor,
color: props.customColor,
}
if (!finalSize.value.preset) {
value.width = finalSize.value.value
value.height = finalSize.value.value
}
if (parent?.props.span)
value.marginLeft = pxCheck(parent?.props.span)
if (innerZIndex.value !== undefined)
value.zIndex = innerZIndex.value
return getMainStyle(props, value)
})
</script>
<script lang="ts">
const componentName = `${PREFIX}-avatar`
export default defineComponent({
name: componentName,
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared',
},
})
</script>
<template>
<view :style="styles" :class="classes">
<slot />
</view>
</template>
<style lang="scss">
@import './index';
</style>

View File

@@ -0,0 +1,55 @@
.nut-avatar {
position: relative;
display: inline-block;
flex: 0 0 auto; // 防止被压缩
text-align: center;
vertical-align: top;
background-repeat: no-repeat;
background-position: center center;
background-size: 100% 100%;
image {
display: block;
width: 100%;
height: 100%;
}
.nut-icon {
position: absolute;
top: 50%;
left: 50%;
background-size: 100% 100%;
transform: translate(-50%, -50%);
}
}
.nut-avatar-large {
width: $avatar-large-width;
height: $avatar-large-height;
line-height: $avatar-large-height;
}
.nut-avatar-normal {
width: $avatar-normal-width;
height: $avatar-normal-height;
line-height: $avatar-normal-height;
}
.nut-avatar-small {
width: $avatar-small-width;
height: $avatar-small-height;
line-height: $avatar-small-height;
}
.nut-avatar-square {
border-radius: $avatar-square;
}
.nut-avatar-round {
border-radius: 50%;
}
.nut-avatar-square,
.nut-avatar-round {
overflow: hidden;
}

View File

@@ -0,0 +1,2 @@
export * from './avatar'
export * from './type'

View File

@@ -0,0 +1,16 @@
export const avatarSize = ['large', 'normal', 'small'] as const
export type AvatarSize = (typeof avatarSize)[number]
export const avatarShape = ['round', 'square'] as const
export type AvatarShape = (typeof avatarShape)[number]
export interface AvatarFinalSize {
/**
* 是否为预设尺寸
*/
preset: boolean
/**
* 尺寸值
*/
value: string
}