feat(web): 更新 UI 组件和个人资料页

- 更新个人资料页支持头像上传
- 优化 Header、Sidebar、UserNav 组件
- 更新表单验证规则
- 调整常量配置

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
charilezhou
2026-01-19 12:37:08 +08:00
parent 4e1be2b494
commit 9c65d39dd2
6 changed files with 27 additions and 16 deletions

View File

@@ -4,6 +4,7 @@ import { RefreshCw } from 'lucide-react';
import { useState } from 'react';
import { toast } from 'sonner';
import { AvatarUpload } from '@/components/forms/AvatarUpload';
import { PasswordForm } from '@/components/forms/PasswordForm';
import { ProfileForm } from '@/components/forms/ProfileForm';
import { Button } from '@/components/ui/button';
@@ -42,6 +43,16 @@ export default function ProfilePage() {
</div>
<div className="grid gap-6 md:grid-cols-2">
<Card>
<CardHeader>
<CardTitle></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<AvatarUpload />
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle></CardTitle>

View File

@@ -1,13 +1,11 @@
'use client';
import { Menu } from 'lucide-react';
import Link from 'next/link';
import { ThemeToggle } from './ThemeToggle';
import { UserNav } from './UserNav';
import { Button } from '@/components/ui/button';
import { siteConfig } from '@/config/site';
import { cn } from '@/lib/utils';
import { useUIStore } from '@/stores';
@@ -40,9 +38,9 @@ export function Header({ showSidebarToggle = true, className }: HeaderProps) {
<span className="sr-only"></span>
</Button>
)}
<Link href="/" className="flex items-center space-x-2">
<span className="font-bold text-xl">{siteConfig.name}</span>
</Link>
{/*<Link href="/" className="flex items-center space-x-2">*/}
{/* <span className="font-bold text-xl">{siteConfig.name}</span>*/}
{/*</Link>*/}
</div>
{/* 中间:可扩展区域 */}

View File

@@ -251,9 +251,7 @@ export function Sidebar() {
)}
>
{!sidebarCollapsed && (
<Link href="/dashboard" className="font-bold text-lg">
{siteConfig.name}
</Link>
<span className="font-bold text-lg">{siteConfig.name}</span>
)}
<Button
variant="ghost"
@@ -288,13 +286,7 @@ export function MobileSidebar() {
<SheetContent side="left" className="w-64 p-0">
<SheetTitle className="sr-only"></SheetTitle>
<div className="flex h-14 items-center border-b px-4">
<Link
href="/dashboard"
className="font-bold text-lg"
onClick={() => setSidebarOpen(false)}
>
{siteConfig.name}
</Link>
<span className="font-bold text-lg">{siteConfig.name}</span>
</div>
<SidebarContent onItemClick={() => setSidebarOpen(false)} />
</SheetContent>

View File

@@ -3,7 +3,7 @@
import { LogOut, User, Settings } from 'lucide-react';
import { useRouter } from 'next/navigation';
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
@@ -14,12 +14,14 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { useFileUrl } from '@/hooks/useFileUrl';
import { performLogout } from '@/lib/auth';
import { useAuthStore } from '@/stores';
export function UserNav() {
const router = useRouter();
const { user } = useAuthStore();
const { data: avatarData } = useFileUrl(user?.avatarId);
const handleLogout = () => {
performLogout({ showToast: true });
@@ -43,6 +45,7 @@ export function UserNav() {
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="relative h-9 w-9 rounded-full">
<Avatar className="h-9 w-9">
<AvatarImage src={avatarData?.url} alt={user.name || '用户头像'} />
<AvatarFallback className="bg-primary text-primary-foreground">
{initials}
</AvatarFallback>

View File

@@ -7,8 +7,11 @@ export const API_ENDPOINTS = {
ME: '/auth/me',
PRIVILEGE: '/auth/privilege',
LOGOUT: '/auth/logout',
SEND_RESET_EMAIL: '/auth/send-reset-email',
RESET_PASSWORD: '/auth/reset-password',
},
USERS: '/users',
FILES: '/files',
CAPTCHA: '/captcha',
ROLES: '/roles',
PERMISSIONS: '/permissions',

View File

@@ -80,6 +80,10 @@ export type ForgotPasswordFormValues = z.infer<typeof forgotPasswordSchema>;
// ==================== 重置密码表单 ====================
export const resetPasswordSchema = z.object({
emailCode: z
.string()
.min(1, '请输入邮箱验证码')
.length(6, '邮箱验证码为 6 位'),
password: z
.string()
.min(1, '请输入新密码')