feat: 实时消息与管理端运营能力

新增群聊/私信与公共 WebSocket 总线,站长用户内容档案,并将通知与待审角标改为推送驱动;同步精简管理仪表盘并修复主题换肤 DOM 冲突。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-15 22:58:16 +08:00
parent 29db9ae9b5
commit 42c395a864
55 changed files with 7607 additions and 346 deletions

View File

@@ -21,6 +21,7 @@ import {
isSuperOrOwner,
type RoleUserLike,
} from "@/lib/roles";
import { realtime, RT_MODERATION_CHANGED } from "@/lib/realtime";
import Avatar from "@/components/Avatar";
import RoleBadge from "@/components/RoleBadge";
@@ -69,7 +70,7 @@ function isActive(pathname: string, href: string, exact?: boolean) {
return exact ? pathname === href : pathname === href || pathname.startsWith(`${href}/`);
}
/** 待审核数量轮询(30 秒);仅内容审核入口可见时启用 */
/** 待审核数量:首屏 + WS 推送 + 回前台校准(无定时轮询) */
function usePendingCounts(enabled: boolean) {
const [counts, setCounts] = useState({ posts: 0, comments: 0 });
useEffect(() => {
@@ -83,14 +84,14 @@ function usePendingCounts(enabled: boolean) {
.catch(() => {});
};
load();
const t = setInterval(load, 30_000);
const off = realtime.on(RT_MODERATION_CHANGED, () => load());
const onVisible = () => {
if (document.visibilityState === "visible") load();
};
document.addEventListener("visibilitychange", onVisible);
return () => {
alive = false;
clearInterval(t);
off();
document.removeEventListener("visibilitychange", onVisible);
};
}, [enabled]);