47 lines
1.2 KiB
TypeScript
47 lines
1.2 KiB
TypeScript
'use client';
|
||
|
||
import { useState } from 'react';
|
||
import { cn, resolveUploadUrl } from '@/lib/utils';
|
||
|
||
interface AvatarProps {
|
||
src?: string | null;
|
||
name?: string | null;
|
||
size?: number;
|
||
className?: string;
|
||
}
|
||
|
||
/**
|
||
* 头像组件:有 src 显示图片(onError 兜底),否则显示首字符圆形。
|
||
*/
|
||
export function Avatar({ src, name, size = 32, className }: AvatarProps) {
|
||
const [imgError, setImgError] = useState(false);
|
||
const showImg = Boolean(src) && !imgError;
|
||
const initial = (name?.trim()?.charAt(0) ?? '?').toUpperCase();
|
||
const fontSize = Math.max(12, Math.round(size * 0.45));
|
||
|
||
if (showImg) {
|
||
return (
|
||
// eslint-disable-next-line @next/next/no-img-element
|
||
<img
|
||
src={resolveUploadUrl(src ?? '')}
|
||
alt={name ?? 'avatar'}
|
||
onError={() => setImgError(true)}
|
||
style={{ width: size, height: size }}
|
||
className={cn('rounded-full object-cover', className)}
|
||
/>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div
|
||
style={{ width: size, height: size, fontSize }}
|
||
className={cn(
|
||
'flex shrink-0 items-center justify-center rounded-full bg-brand-600 font-medium text-white',
|
||
className,
|
||
)}
|
||
>
|
||
{initial}
|
||
</div>
|
||
);
|
||
}
|