feat: 完成头像上传、站点主题配置与UI优化
- 新增附件模型与上传服务,拆分头像上传/管理逻辑 - 重构用户资料接口,移除直接修改头像字段,改用专用上传接口 - 添加站点公开设置API与管理员外观配置页 - 全局替换hover背景为accent-soft,优化交互一致性 - 完善头像组件,支持图片加载失败回退与src更新重置 - 新增用户状态事件总线,解决同页头像更新不及时问题 - 调整依赖包顺序与CI依赖版本 - 重构板块列表接口,新增帖子计数 - 迁移账号设置到用户主页tab,合并重复代码
This commit is contained in:
@@ -19,9 +19,11 @@ import {
|
||||
User as UserIcon,
|
||||
ChevronDown,
|
||||
Megaphone,
|
||||
Palette,
|
||||
} from "lucide-react";
|
||||
import { apiLogout, apiMe, type User } from "@/lib/api";
|
||||
import { hasAuthCookieHint } from "@/lib/cookies";
|
||||
import { onUserUpdate } from "@/lib/userEvents";
|
||||
import NotificationBell from "./NotificationBell";
|
||||
import Avatar from "./Avatar";
|
||||
|
||||
@@ -53,6 +55,11 @@ export default function Header({
|
||||
setUnread(initialUnread);
|
||||
}, [initialUnread]);
|
||||
|
||||
// 同页内资料变更(如上传/删除头像)立即生效,不依赖 router.refresh 时序
|
||||
useEffect(() => onUserUpdate((patch) => {
|
||||
setUser((prev) => (prev ? { ...prev, ...patch } : prev));
|
||||
}), []);
|
||||
|
||||
// 挂载后静默校正一次(仅一次):覆盖 SSR 期间后端不可用、旧 Path refresh
|
||||
// 半登录等边缘情况。正常 F5 下结果与 SSR 一致,不产生可见闪动;
|
||||
// 无可读 CSRF cookie 说明必然是游客,跳过请求
|
||||
@@ -274,9 +281,14 @@ export default function Header({
|
||||
)}
|
||||
{user && <MobileLink href="/settings">账号设置</MobileLink>}
|
||||
{user?.role === "admin" && (
|
||||
<MobileLink href="/admin/announcements">
|
||||
<span className="inline-flex items-center gap-2"><Megaphone size={15} /> 公告管理</span>
|
||||
</MobileLink>
|
||||
<>
|
||||
<MobileLink href="/admin/announcements">
|
||||
<span className="inline-flex items-center gap-2"><Megaphone size={15} /> 公告管理</span>
|
||||
</MobileLink>
|
||||
<MobileLink href="/admin/appearance">
|
||||
<span className="inline-flex items-center gap-2"><Palette size={15} /> 外观设置</span>
|
||||
</MobileLink>
|
||||
</>
|
||||
)}
|
||||
{!user && <MobileLink href="/login">登录</MobileLink>}
|
||||
{!user && <MobileLink href="/register">注册</MobileLink>}
|
||||
@@ -373,7 +385,7 @@ function UserMenu({ user, onLogout }: { user: User; onLogout: () => void }) {
|
||||
}, [open]);
|
||||
|
||||
const itemCls =
|
||||
"w-full flex items-center gap-2.5 px-4 py-2.5 text-[13.5px] font-semibold transition-colors hover:bg-[var(--panel-2)]";
|
||||
"w-full flex items-center gap-2.5 px-4 py-2.5 text-[13.5px] font-semibold transition-colors hover:bg-[var(--accent-soft)] focus-visible:bg-[var(--accent-soft)]";
|
||||
|
||||
return (
|
||||
<div className="relative hidden lg:block" ref={menuRef}>
|
||||
@@ -387,7 +399,7 @@ function UserMenu({ user, onLogout }: { user: User; onLogout: () => void }) {
|
||||
background: open ? "var(--accent-soft)" : "var(--panel)",
|
||||
}}
|
||||
>
|
||||
<Avatar name={user.nickname || user.username} size={28} />
|
||||
<Avatar name={user.nickname || user.username} src={user.avatar || undefined} size={28} />
|
||||
<span
|
||||
className="text-[13px] font-semibold max-w-[90px] truncate"
|
||||
style={{ color: open ? "var(--accent)" : "var(--ink)" }}
|
||||
@@ -419,7 +431,7 @@ function UserMenu({ user, onLogout }: { user: User; onLogout: () => void }) {
|
||||
className="flex items-center gap-3 px-4 py-3.5"
|
||||
style={{ borderBottom: "1px solid var(--line)" }}
|
||||
>
|
||||
<Avatar name={user.nickname || user.username} size={38} />
|
||||
<Avatar name={user.nickname || user.username} src={user.avatar || undefined} size={38} />
|
||||
<div className="min-w-0">
|
||||
<p className="text-[14px] font-bold truncate" style={{ color: "var(--ink)" }}>
|
||||
{user.nickname}
|
||||
@@ -440,6 +452,9 @@ function UserMenu({ user, onLogout }: { user: User; onLogout: () => void }) {
|
||||
<Link href="/admin/announcements" onClick={() => setOpen(false)} className={itemCls} role="menuitem">
|
||||
<Megaphone size={15} style={{ color: "var(--accent)" }} /> 公告管理
|
||||
</Link>
|
||||
<Link href="/admin/appearance" onClick={() => setOpen(false)} className={itemCls} role="menuitem">
|
||||
<Palette size={15} style={{ color: "var(--accent)" }} /> 外观设置
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
<div className="p-1.5" style={{ borderTop: "1px solid var(--line)" }}>
|
||||
|
||||
Reference in New Issue
Block a user