🎨 refactor: UserInfoHeader layout and styling
- Restructure avatar-name-tags layout to left-right alignment - Avatar positioned on the left - Name aligned to avatar top, tags aligned to avatar bottom - Remove margin-top usage in favor of flexbox justify-between - Simplify desktop statistics cards to single-line layout - Format as "icon + label: value" without stacked layout - Remove custom color classes for cleaner styling - Update UI component styling - Increase tag size from small to large - Reduce cover height from responsive to fixed 32 - Add Badge import for future enhancements - Clean up icon and text color classes - Maintain responsive behavior and accessibility
This commit is contained in:
BIN
web/public/cover-4.webp
Normal file
BIN
web/public/cover-4.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
@@ -18,7 +18,7 @@ For commercial licensing, please contact support@quantumnous.com
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Avatar, Card, Tag, Divider, Typography } from '@douyinfe/semi-ui';
|
import { Avatar, Card, Tag, Divider, Typography, Badge } from '@douyinfe/semi-ui';
|
||||||
import { isRoot, isAdmin, renderQuota, stringToColor } from '../../../../helpers';
|
import { isRoot, isAdmin, renderQuota, stringToColor } from '../../../../helpers';
|
||||||
import { Coins, BarChart2, Users } from 'lucide-react';
|
import { Coins, BarChart2, Users } from 'lucide-react';
|
||||||
|
|
||||||
@@ -35,137 +35,143 @@ const UserInfoHeader = ({ t, userState }) => {
|
|||||||
const getAvatarText = () => {
|
const getAvatarText = () => {
|
||||||
const username = getUsername();
|
const username = getUsername();
|
||||||
if (username && username.length > 0) {
|
if (username && username.length > 0) {
|
||||||
// 获取前两个字符,支持中文和英文
|
|
||||||
return username.slice(0, 2).toUpperCase();
|
return username.slice(0, 2).toUpperCase();
|
||||||
}
|
}
|
||||||
return 'NA';
|
return 'NA';
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="!rounded-2xl with-pastel-balls">
|
<Card
|
||||||
<div className="relative text-gray-600 dark:text-gray-300">
|
className="!rounded-2xl overflow-hidden"
|
||||||
<div className="flex justify-between items-start mb-4 sm:mb-6">
|
cover={
|
||||||
<div className="flex items-center flex-1 min-w-0">
|
<div
|
||||||
|
className="relative h-32"
|
||||||
|
style={{
|
||||||
|
'--palette-primary-darkerChannel': '0 75 80',
|
||||||
|
backgroundImage: `linear-gradient(0deg, rgba(var(--palette-primary-darkerChannel) / 80%), rgba(var(--palette-primary-darkerChannel) / 80%)), url('/cover-4.webp')`,
|
||||||
|
backgroundSize: 'cover',
|
||||||
|
backgroundPosition: 'center',
|
||||||
|
backgroundRepeat: 'no-repeat'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* 用户信息内容 */}
|
||||||
|
<div className="relative z-10 h-full flex flex-col justify-end p-6">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="flex items-stretch gap-3 sm:gap-4 flex-1 min-w-0">
|
||||||
<Avatar
|
<Avatar
|
||||||
size='large'
|
size='large'
|
||||||
className="mr-3 sm:mr-4 shadow-md flex-shrink-0"
|
|
||||||
color={stringToColor(getUsername())}
|
color={stringToColor(getUsername())}
|
||||||
>
|
>
|
||||||
{getAvatarText()}
|
{getAvatarText()}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0 flex flex-col justify-between">
|
||||||
<div className="text-base !text-3xl font-semibold truncate text-gray-800 dark:text-gray-100">
|
<div className="text-3xl font-bold truncate" style={{ color: 'white' }}>{getUsername()}</div>
|
||||||
{getUsername()}
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
</div>
|
|
||||||
<div className="mt-1 flex flex-wrap gap-1 sm:gap-2">
|
|
||||||
{isRoot() ? (
|
{isRoot() ? (
|
||||||
<Tag
|
<Tag
|
||||||
size='small'
|
size='large'
|
||||||
className="!rounded-full bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300"
|
shape='circle'
|
||||||
style={{ fontWeight: '500' }}
|
style={{ color: 'white' }}
|
||||||
>
|
>
|
||||||
{t('超级管理员')}
|
{t('超级管理员')}
|
||||||
</Tag>
|
</Tag>
|
||||||
) : isAdmin() ? (
|
) : isAdmin() ? (
|
||||||
<Tag
|
<Tag
|
||||||
size='small'
|
size='large'
|
||||||
className="!rounded-full bg-gray-50 dark:bg-gray-700 text-gray-600 dark:text-gray-300"
|
shape='circle'
|
||||||
style={{ fontWeight: '500' }}
|
style={{ color: 'white' }}
|
||||||
>
|
>
|
||||||
{t('管理员')}
|
{t('管理员')}
|
||||||
</Tag>
|
</Tag>
|
||||||
) : (
|
) : (
|
||||||
<Tag
|
<Tag
|
||||||
size='small'
|
size='large'
|
||||||
className="!rounded-full bg-slate-50 dark:bg-slate-700 text-slate-600 dark:text-slate-300"
|
shape='circle'
|
||||||
style={{ fontWeight: '500' }}
|
style={{ color: 'white' }}
|
||||||
>
|
>
|
||||||
{t('普通用户')}
|
{t('普通用户')}
|
||||||
</Tag>
|
</Tag>
|
||||||
)}
|
)}
|
||||||
<Tag
|
<Tag
|
||||||
size='small'
|
size='large'
|
||||||
className="!rounded-full bg-slate-100 dark:bg-slate-700 text-slate-600 dark:text-slate-300"
|
shape='circle'
|
||||||
style={{ fontWeight: '500' }}
|
style={{ color: 'white' }}
|
||||||
>
|
>
|
||||||
ID: {userState?.user?.id}
|
ID: {userState?.user?.id}
|
||||||
</Tag>
|
</Tag>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 右上角统计信息(Semi UI 卡片) */}
|
|
||||||
<div className="hidden sm:block flex-shrink-0 ml-2">
|
|
||||||
<Card size="small" className="!rounded-xl shadow-sm" bodyStyle={{ padding: '8px 12px' }}>
|
|
||||||
<div className="flex items-center gap-3 lg:gap-4">
|
|
||||||
<div className="flex items-center justify-end gap-2">
|
|
||||||
<Coins size={16} className="text-slate-600 dark:text-slate-300" />
|
|
||||||
<div className="text-right">
|
|
||||||
<Typography.Text size="small" type="tertiary">{t('历史消耗')}</Typography.Text>
|
|
||||||
<div className="text-xs sm:text-sm font-semibold text-gray-800 dark:text-gray-100">{renderQuota(userState?.user?.used_quota)}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Divider layout="vertical" />
|
|
||||||
<div className="flex items-center justify-end gap-2">
|
|
||||||
<BarChart2 size={16} className="text-slate-600 dark:text-slate-300" />
|
|
||||||
<div className="text-right">
|
|
||||||
<Typography.Text size="small" type="tertiary">{t('请求次数')}</Typography.Text>
|
|
||||||
<div className="text-xs sm:text-sm font-semibold text-gray-800 dark:text-gray-100">{userState.user?.request_count || 0}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Divider layout="vertical" />
|
|
||||||
<div className="flex items-center justify-end gap-2">
|
|
||||||
<Users size={16} className="text-slate-600 dark:text-slate-300" />
|
|
||||||
<div className="text-right">
|
|
||||||
<Typography.Text size="small" type="tertiary">{t('用户分组')}</Typography.Text>
|
|
||||||
<div className="text-xs sm:text-sm font-semibold text-gray-800 dark:text-gray-100">{userState?.user?.group || t('默认')}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
}
|
||||||
</div>
|
>
|
||||||
</div>
|
{/* 当前余额和桌面版统计信息 */}
|
||||||
|
<div className="flex items-start justify-between gap-6">
|
||||||
<div className="mb-4 sm:mb-6">
|
{/* 当前余额显示 */}
|
||||||
<div className="text-xs sm:text-sm mb-1 sm:mb-2 text-gray-500 dark:text-gray-400">
|
<Badge count={t('当前余额')} position='rightTop' type='danger'>
|
||||||
{t('当前余额')}
|
<div className="text-2xl sm:text-3xl md:text-4xl font-bold tracking-wide">
|
||||||
</div>
|
|
||||||
<div className="text-2xl sm:text-3xl md:text-4xl font-bold tracking-wide text-gray-900 dark:text-gray-100">
|
|
||||||
{renderQuota(userState?.user?.quota)}
|
{renderQuota(userState?.user?.quota)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Badge>
|
||||||
|
|
||||||
{/* 移动端统计信息卡片(仅 xs 可见) */}
|
{/* 桌面版统计信息(Semi UI 卡片) */}
|
||||||
<div className="sm:hidden">
|
<div className="hidden lg:block flex-shrink-0">
|
||||||
<Card size="small" className="!rounded-xl shadow-sm" bodyStyle={{ padding: '10px 12px' }}>
|
<Card size="small" className="!rounded-xl" bodyStyle={{ padding: '12px 16px' }}>
|
||||||
<div className="space-y-2">
|
<div className="flex items-center gap-4">
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Coins size={16} className="text-slate-600" />
|
<Coins size={16} />
|
||||||
<Typography.Text size="small" type="tertiary">{t('历史消耗')}</Typography.Text>
|
<Typography.Text size="small" type="tertiary">{t('历史消耗')}</Typography.Text>
|
||||||
|
<Typography.Text size="small" type="tertiary" strong>{renderQuota(userState?.user?.used_quota)}</Typography.Text>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm font-semibold text-gray-800">{renderQuota(userState?.user?.used_quota)}</div>
|
<Divider layout="vertical" />
|
||||||
</div>
|
|
||||||
<Divider margin='8px' />
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<BarChart2 size={16} className="text-slate-600" />
|
<BarChart2 size={16} />
|
||||||
<Typography.Text size="small" type="tertiary">{t('请求次数')}</Typography.Text>
|
<Typography.Text size="small" type="tertiary">{t('请求次数')}</Typography.Text>
|
||||||
|
<Typography.Text size="small" type="tertiary" strong>{userState.user?.request_count || 0}</Typography.Text>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm font-semibold text-gray-800">{userState.user?.request_count || 0}</div>
|
<Divider layout="vertical" />
|
||||||
</div>
|
|
||||||
<Divider margin='8px' />
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Users size={16} className="text-slate-600" />
|
<Users size={16} />
|
||||||
<Typography.Text size="small" type="tertiary">{t('用户分组')}</Typography.Text>
|
<Typography.Text size="small" type="tertiary">{t('用户分组')}</Typography.Text>
|
||||||
</div>
|
<Typography.Text size="small" type="tertiary" strong>{userState?.user?.group || t('默认')}</Typography.Text>
|
||||||
<div className="text-sm font-semibold text-gray-800">{userState?.user?.group || t('默认')}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 移动端和中等屏幕统计信息卡片 */}
|
||||||
|
<div className="lg:hidden mt-2">
|
||||||
|
<Card size="small" className="!rounded-xl" bodyStyle={{ padding: '12px 16px' }} >
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Coins size={16} />
|
||||||
|
<Typography.Text size="small" type="tertiary">{t('历史消耗')}</Typography.Text>
|
||||||
|
</div>
|
||||||
|
<Typography.Text size="small" type="tertiary" strong>{renderQuota(userState?.user?.used_quota)}</Typography.Text>
|
||||||
|
</div>
|
||||||
|
<Divider margin='8px' />
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<BarChart2 size={16} />
|
||||||
|
<Typography.Text size="small" type="tertiary">{t('请求次数')}</Typography.Text>
|
||||||
|
</div>
|
||||||
|
<Typography.Text size="small" type="tertiary" strong>{userState.user?.request_count || 0}</Typography.Text>
|
||||||
|
</div>
|
||||||
|
<Divider margin='8px' />
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Users size={16} />
|
||||||
|
<Typography.Text size="small" type="tertiary">{t('用户分组')}</Typography.Text>
|
||||||
|
</div>
|
||||||
|
<Typography.Text size="small" type="tertiary" strong>{userState?.user?.group || t('默认')}</Typography.Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user