这一版本优化了很多
This commit is contained in:
19
node_modules/wot-design-uni/components/wd-avatar/common.scss
generated
vendored
Normal file
19
node_modules/wot-design-uni/components/wd-avatar/common.scss
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Avatar Group Item 共享样式
|
||||
* 用于 wd-avatar 和 wd-avatar-group 组件中的重叠效果
|
||||
*/
|
||||
.wd-avatar-group__item {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
// 第一个元素不需要负 margin
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
// 其他元素使用负 margin 实现重叠效果
|
||||
& + & {
|
||||
margin-left: $-avatar-group-overlap;
|
||||
}
|
||||
}
|
||||
75
node_modules/wot-design-uni/components/wd-avatar/index.scss
generated
vendored
Normal file
75
node_modules/wot-design-uni/components/wd-avatar/index.scss
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
@import '../common/abstracts/variable';
|
||||
@import '../common/abstracts/mixin';
|
||||
@import './common';
|
||||
|
||||
.wot-theme-dark {
|
||||
@include b(avatar) {
|
||||
background-color: $-dark-background4;
|
||||
color: $-dark-color;
|
||||
}
|
||||
}
|
||||
|
||||
@include b(avatar) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: $-avatar-size;
|
||||
height: $-avatar-size;
|
||||
color: $-avatar-text-color;
|
||||
font-weight: $-avatar-font-weight;
|
||||
font-size: $-avatar-font-size;
|
||||
line-height: $-avatar-line-height;
|
||||
text-align: center;
|
||||
background-color: $-avatar-bg-color;
|
||||
border-radius: $-avatar-border-radius;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
vertical-align: middle;
|
||||
flex-shrink: 0;
|
||||
|
||||
@include when(round) {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
@include when(square) {
|
||||
border-radius: $-avatar-border-radius;
|
||||
}
|
||||
|
||||
@include when(large) {
|
||||
width: $-avatar-size-large;
|
||||
height: $-avatar-size-large;
|
||||
font-size: $-avatar-font-size-large;
|
||||
}
|
||||
|
||||
@include when(medium) {
|
||||
width: $-avatar-size-medium;
|
||||
height: $-avatar-size-medium;
|
||||
font-size: $-avatar-font-size-medium;
|
||||
}
|
||||
|
||||
@include when(normal) {
|
||||
width: $-avatar-size;
|
||||
height: $-avatar-size;
|
||||
font-size: $-avatar-font-size;
|
||||
}
|
||||
|
||||
@include when(small) {
|
||||
width: $-avatar-size-small;
|
||||
height: $-avatar-size-small;
|
||||
font-size: $-avatar-font-size-small;
|
||||
}
|
||||
|
||||
@include e(text) {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@include edeep(icon) {
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
@include edeep(img) {
|
||||
border-radius: inherit;
|
||||
}
|
||||
}
|
||||
90
node_modules/wot-design-uni/components/wd-avatar/types.ts
generated
vendored
Normal file
90
node_modules/wot-design-uni/components/wd-avatar/types.ts
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* @Author: weisheng
|
||||
* @Date: 2025-12-30
|
||||
* @LastEditTime: 2025-12-30
|
||||
* @LastEditors: weisheng
|
||||
* @Description: Avatar 头像组件类型定义
|
||||
* @FilePath: /wot-design-uni/src/uni_modules/wot-design-uni/components/wd-avatar/types.ts
|
||||
* 记得注释
|
||||
*/
|
||||
import type { ExtractPropTypes, PropType } from 'vue'
|
||||
import { baseProps, makeNumericProp, makeStringProp, numericProp } from '../common/props'
|
||||
import type { ImageMode } from '../wd-img/types'
|
||||
|
||||
export type AvatarSize = 'large' | 'medium' | 'normal' | 'small'
|
||||
export type AvatarShape = 'square' | 'round'
|
||||
|
||||
export const avatarProps = {
|
||||
...baseProps,
|
||||
|
||||
/**
|
||||
* 图片地址
|
||||
* 类型: string
|
||||
* 默认值: 空字符串
|
||||
*/
|
||||
src: makeStringProp(''),
|
||||
|
||||
/**
|
||||
* 文本内容
|
||||
* 类型: string
|
||||
* 默认值: 空字符串
|
||||
*/
|
||||
text: makeStringProp(''),
|
||||
|
||||
/**
|
||||
* 头像尺寸,支持预设尺寸(large/medium/normal/small)或带单位的字符串(如 40px、100rpx)
|
||||
* 类型: string | number
|
||||
* 默认值: 'normal'
|
||||
*/
|
||||
size: makeNumericProp('normal'),
|
||||
|
||||
/**
|
||||
* 头像形状,可选值: round(圆形) / square(方形)
|
||||
* 类型: string
|
||||
* 默认值: 'round'
|
||||
*/
|
||||
shape: makeStringProp<AvatarShape>('round'),
|
||||
|
||||
/**
|
||||
* 背景颜色
|
||||
* 类型: string
|
||||
* 默认值: 空字符串
|
||||
*/
|
||||
bgColor: makeStringProp(''),
|
||||
|
||||
/**
|
||||
* 文字颜色
|
||||
* 类型: string
|
||||
* 默认值: 空字符串
|
||||
*/
|
||||
color: makeStringProp(''),
|
||||
|
||||
/**
|
||||
* 图标名称,使用 wd-icon 组件
|
||||
* 类型: string
|
||||
* 默认值: 空字符串
|
||||
*/
|
||||
icon: makeStringProp(''),
|
||||
|
||||
/**
|
||||
* 图片加载失败时的占位文本
|
||||
* 类型: string
|
||||
* 默认值: 空字符串
|
||||
*/
|
||||
alt: makeStringProp(''),
|
||||
|
||||
/**
|
||||
* 图片填充模式,同 uni-app image 组件的 mode
|
||||
* 类型: ImageMode
|
||||
* 默认值: 'aspectFill'
|
||||
*/
|
||||
mode: makeStringProp<ImageMode>('aspectFill'),
|
||||
|
||||
/**
|
||||
* 内部使用,不注册到 parent
|
||||
* @private
|
||||
*/
|
||||
_internal: Boolean
|
||||
}
|
||||
|
||||
export type AvatarProps = ExtractPropTypes<typeof avatarProps>
|
||||
203
node_modules/wot-design-uni/components/wd-avatar/wd-avatar.vue
generated
vendored
Normal file
203
node_modules/wot-design-uni/components/wd-avatar/wd-avatar.vue
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
<!--
|
||||
* @Author: North
|
||||
* @Date: 2026-01-01
|
||||
* @LastEditTime: 2026-01-01
|
||||
* @LastEditors: North
|
||||
* @Description: Avatar 头像组件,支持图片、文本或图标展示
|
||||
* @FilePath: /wot-design-uni/src/uni_modules/wot-design-uni/components/wd-avatar/wd-avatar.vue
|
||||
-->
|
||||
<template>
|
||||
<view v-if="isShow" :class="rootClass" :style="rootStyle" @click="handleClick">
|
||||
<!-- 默认插槽优先 -->
|
||||
<slot v-if="hasDefaultSlot"></slot>
|
||||
<!-- 图片 -->
|
||||
<wd-img v-else-if="src" :src="src" :width="imgSize" :height="imgSize" :mode="props.mode" custom-class="wd-avatar__img" @error="handleError" />
|
||||
<!-- 文本 -->
|
||||
<text v-else-if="text" class="wd-avatar__text">{{ text }}</text>
|
||||
<!-- 图标 -->
|
||||
<wd-icon v-else-if="icon" :name="icon" custom-class="wd-avatar__icon" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'wd-avatar',
|
||||
options: {
|
||||
addGlobalClass: true,
|
||||
virtualHost: true,
|
||||
styleIsolation: 'shared'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, useSlots, type CSSProperties, ref } from 'vue'
|
||||
import wdIcon from '../wd-icon/wd-icon.vue'
|
||||
import wdImg from '../wd-img/wd-img.vue'
|
||||
import { addUnit, isDef, objToStyle } from '../common/util'
|
||||
import { avatarProps, type AvatarSize } from './types'
|
||||
import { useParent } from '../composables/useParent'
|
||||
import { AVATAR_GROUP_KEY } from '../wd-avatar-group/types'
|
||||
|
||||
const props = defineProps(avatarProps)
|
||||
const emit = defineEmits(['error', 'click'])
|
||||
const slots = useSlots()
|
||||
|
||||
// _internal 用于 avatar-group 内部的溢出计数头像,跳过父组件上下文
|
||||
const { parent, index } = props._internal ? { parent: null, index: ref(-1) } : useParent(AVATAR_GROUP_KEY)
|
||||
|
||||
const SIZE_MAP: Record<AvatarSize, number> = {
|
||||
large: 76,
|
||||
medium: 64,
|
||||
normal: 54,
|
||||
small: 48
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否显示该头像
|
||||
*/
|
||||
const isShow = computed(() => {
|
||||
if (!parent) {
|
||||
return true
|
||||
}
|
||||
// 在 avatar-group 中,根据 maxCount 判断
|
||||
const maxCount = parent.props.maxCount
|
||||
if (!isDef(maxCount)) {
|
||||
return true
|
||||
}
|
||||
const count = typeof maxCount === 'number' ? maxCount : parseInt(maxCount, 10)
|
||||
// 检查 count 是否为有效数字
|
||||
if (isNaN(count) || count <= 0) {
|
||||
return true
|
||||
}
|
||||
return index.value < count
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取实际尺寸
|
||||
* 在 avatar-group 中优先使用 parent 的 size
|
||||
*/
|
||||
const actualSize = computed(() => {
|
||||
if (parent && isDef(parent.props.size)) {
|
||||
return parent.props.size
|
||||
}
|
||||
return props.size
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取实际像素尺寸
|
||||
*/
|
||||
const imgSize = computed(() => {
|
||||
const size = actualSize.value
|
||||
if (!isDef(size)) {
|
||||
return SIZE_MAP.normal
|
||||
}
|
||||
|
||||
if (typeof size === 'string' && size in SIZE_MAP) {
|
||||
return SIZE_MAP[size as AvatarSize]
|
||||
}
|
||||
|
||||
return size
|
||||
})
|
||||
|
||||
const hasDefaultSlot = computed(() => !!slots.default)
|
||||
|
||||
const rootClass = computed(() => {
|
||||
const classes = ['wd-avatar', props.customClass]
|
||||
|
||||
// 形状类 - 在 avatar-group 中优先使用 parent 的 shape
|
||||
const shape = parent && isDef(parent.props.shape) ? parent.props.shape : props.shape
|
||||
classes.push(`wd-avatar--${shape}`)
|
||||
|
||||
// 尺寸类仅预设尺寸
|
||||
const size = actualSize.value
|
||||
if (typeof size === 'string' && ['large', 'medium', 'normal', 'small'].includes(size)) {
|
||||
classes.push(`wd-avatar--${size}`)
|
||||
}
|
||||
|
||||
// 在 avatar-group 中时,添加 item 类
|
||||
if (parent) {
|
||||
classes.push('wd-avatar-group__item')
|
||||
}
|
||||
|
||||
return classes.join(' ')
|
||||
})
|
||||
|
||||
/**
|
||||
* 根节点样式
|
||||
*/
|
||||
const rootStyle = computed(() => {
|
||||
const style: CSSProperties = {}
|
||||
|
||||
let size = ''
|
||||
const sizeValue = actualSize.value
|
||||
if (typeof sizeValue === 'string' && sizeValue in SIZE_MAP) {
|
||||
size = addUnit(SIZE_MAP[sizeValue as AvatarSize])
|
||||
} else if (isDef(sizeValue)) {
|
||||
size = addUnit(sizeValue)
|
||||
}
|
||||
if (size) {
|
||||
style.width = size
|
||||
style.height = size
|
||||
style.fontSize = `calc(${size} * 0.45)`
|
||||
|
||||
if (parent) {
|
||||
style['--wot-avatar-group-overlap' as any] = `calc(${size} * -0.22)`
|
||||
}
|
||||
}
|
||||
|
||||
// 形状 - 在 avatar-group 中优先使用 parent 的 shape
|
||||
const shape = parent && isDef(parent.props.shape) ? parent.props.shape : props.shape
|
||||
if (shape === 'round') {
|
||||
style.borderRadius = '50%'
|
||||
}
|
||||
|
||||
// 处理层叠效果的 z-index
|
||||
if (parent) {
|
||||
const cascading = parent.props.cascading
|
||||
if (cascading === 'left-up') {
|
||||
// 左侧在上,越后面越大
|
||||
style.zIndex = index.value + 1
|
||||
} else if (cascading === 'right-up') {
|
||||
// 右侧在上,越前面越大
|
||||
const maxCount = parent.props.maxCount
|
||||
let count = parent.children?.length ?? 0
|
||||
|
||||
if (isDef(maxCount)) {
|
||||
const parsedCount = typeof maxCount === 'number' ? maxCount : parseInt(maxCount, 10)
|
||||
if (!isNaN(parsedCount) && parsedCount > 0) {
|
||||
count = parsedCount
|
||||
}
|
||||
}
|
||||
|
||||
style.zIndex = count - index.value
|
||||
}
|
||||
}
|
||||
|
||||
if (props.color) {
|
||||
style.color = props.color
|
||||
}
|
||||
|
||||
if (props.bgColor) {
|
||||
style.backgroundColor = props.bgColor
|
||||
// 有背景色但无文字色时,默认白色
|
||||
if (!props.color) {
|
||||
style.color = '#fff'
|
||||
}
|
||||
}
|
||||
|
||||
return `${objToStyle(style)} ${props.customStyle}`
|
||||
})
|
||||
|
||||
const handleError = (event: any) => {
|
||||
emit('error', event)
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
emit('click')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
||||
Reference in New Issue
Block a user