feat: 个人中心管理(编辑信息 + 修改密码 + 退出登录)
- 新增 /profile 路由:两个 Tab(个人信息 / 修改密码)+ 退出按钮 - Sidebar 底部用户区改造:绑定真实登录用户(头像/姓名/角色)+ 加 Settings icon 跳 /profile - useAuthStore 加 refreshUser:保存信息后立即同步 Sidebar 显示 - 个人信息表单:姓名/手机/邮箱可改,部门/角色只读,复用 updateMember - 密码修改:原密 + 新密(≥8位且≠原密) + 确认 三档校验,眼睛 icon 切换可见,下次登录生效 - 退出登录:confirm → useAuthStore.logout → /login Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { Inbox, Package, FolderKanban, Tag, Users, LayoutGrid, Search, Lightbulb, Clock, Shield } from 'lucide-react';
|
||||
import { Inbox, Package, FolderKanban, Tag, Users, LayoutGrid, Search, Lightbulb, Clock, Shield, Settings } from 'lucide-react';
|
||||
import { useHasPermission } from '@/components/auth/Guard';
|
||||
import { useAuthStore } from '@/stores/useAuthStore';
|
||||
import { useMemberStore } from '@/stores/useMemberStore';
|
||||
|
||||
const NAV_GROUPS = [
|
||||
{
|
||||
@@ -58,20 +60,46 @@ export function Sidebar() {
|
||||
</nav>
|
||||
|
||||
<div className="border-t border-[var(--line)] p-2">
|
||||
<button className="flex w-full items-center gap-2.5 rounded-lg px-2 py-1.5 text-left transition-colors hover:bg-[var(--bg-subtle)]">
|
||||
<div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-[var(--accent)] text-[11px] font-semibold text-white">
|
||||
A
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-[13px] font-medium text-[var(--ink)]">Admin</p>
|
||||
<p className="truncate text-[11px] text-[var(--ink-muted)]">管理员</p>
|
||||
</div>
|
||||
</button>
|
||||
<UserBlock />
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
|
||||
function UserBlock() {
|
||||
const router = useRouter();
|
||||
const user = useAuthStore((s) => s.user);
|
||||
const role = useMemberStore((s) => s.roles.find((r) => r.id === user?.roleId));
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
<div className="flex w-full items-center gap-2.5 rounded-lg px-2 py-1.5 text-left">
|
||||
<div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-[var(--bg-subtle)] text-[11px] font-semibold text-[var(--ink-muted)]">?</div>
|
||||
<p className="truncate text-[13px] text-[var(--ink-muted)]">未登录</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex w-full items-center gap-2.5 rounded-lg px-2 py-1.5">
|
||||
<div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-[var(--accent)] text-[11px] font-semibold text-white">
|
||||
{user.name?.[0] ?? '?'}
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-[13px] font-medium text-[var(--ink)]">{user.name}</p>
|
||||
<p className="truncate text-[11px] text-[var(--ink-muted)]">{role?.name ?? '-'}</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => router.push('/profile')}
|
||||
className="p-1 rounded hover:bg-[var(--bg-subtle)] text-[var(--ink-muted)] hover:text-[var(--accent)]"
|
||||
title="个人中心"
|
||||
>
|
||||
<Settings className="h-4 w-4" strokeWidth={1.75} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function NavGroup({ label, items, isActive, onNavigate }: {
|
||||
label: string;
|
||||
items: { label: string; path: string; icon: any; permission: string | null }[];
|
||||
|
||||
Reference in New Issue
Block a user