外观设置改为单页分区;发帖草稿与板块校验优化,WS 延迟释放避开 Strict Mode 误断。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-20 11:27:39 +08:00
parent 68e90ff6ff
commit 1f2e4b2a33
8 changed files with 684 additions and 618 deletions

View File

@@ -41,7 +41,6 @@ import Modal from "@/components/Modal";
import RoleAccountModal from "@/components/RoleAccountModal";
import {
AdminEmpty,
AdminIconButton,
AdminPageHeader,
AdminPager,
AdminSearchField,
@@ -523,7 +522,8 @@ export default function UsersAdmin({
frags.push("100px");
break;
case "actions":
frags.push("minmax(148px,1.8fr)");
// 带文案按钮:档案 / 主页 / 角色与账号
frags.push("minmax(268px,2.2fr)");
break;
}
}
@@ -665,30 +665,35 @@ export default function UsersAdmin({
const renderActions = (u: AdminUser, isSelf: boolean) => {
const canAudit = isOwner(currentUserRole);
const auditBtn = canAudit ? (
<AdminIconButton
<Link
href={`/admin/users/${u.id}`}
tip="内容档案"
ariaLabel={`查看 ${u.username} 的内容档案`}
tone="accent"
className="btn btn-line btn-sm gap-1"
aria-label={`查看 ${u.username} 的内容档案`}
>
<FolderSearch size={15} />
</AdminIconButton>
<FolderSearch size={13} />
内容档案
</Link>
) : null;
if (u.role === ROLES.OWNER) {
return (
<div className="flex items-center gap-1">
<div className="flex items-center gap-1.5 flex-wrap">
{auditBtn}
<AdminIconButton href={`/u/${u.id}`} tip="查看个人主页" ariaLabel={`查看 ${u.nickname} 的个人主页`}>
<ExternalLink size={15} />
</AdminIconButton>
<Link href={`/u/${u.id}`} className="btn btn-line btn-sm gap-1" aria-label={`查看 ${u.nickname} 的个人主页`}>
<ExternalLink size={13} />
主页
</Link>
</div>
);
}
if (isSelf) {
return (
<div className="flex items-center gap-1">
<div className="flex items-center gap-1.5 flex-wrap">
{auditBtn}
<Link href={`/u/${u.id}`} className="btn btn-line btn-sm gap-1" aria-label={`查看 ${u.nickname} 的个人主页`}>
<ExternalLink size={13} />
主页
</Link>
<span
className="inline-flex items-center rounded-full px-2.5 py-1 text-[11px] whitespace-nowrap"
style={{ background: "var(--panel-2)", color: "var(--ink-3)" }}
@@ -699,26 +704,28 @@ export default function UsersAdmin({
);
}
const busy = actingId === u.id;
const roleTone = u.banned ? "danger" : u.role !== ROLES.USER ? "accent" : "default";
// 「角色与账号」用主题色突出主操作;封禁态改用危险色提示需处理
const roleStyle = u.banned
? { color: "var(--danger)", borderColor: "var(--danger)" }
: { color: "var(--accent)", borderColor: "var(--accent)" };
return (
<div className="flex items-center gap-1">
<div className="flex items-center gap-1.5 flex-wrap">
{auditBtn}
<AdminIconButton
href={`/u/${u.id}`}
tip="查看个人主页"
ariaLabel={`查看 ${u.nickname} 的个人主页`}
>
<ExternalLink size={15} />
</AdminIconButton>
<AdminIconButton
tip="角色与账号"
ariaLabel={`设置 ${u.username} 的角色与账号状态`}
tone={roleTone}
<Link href={`/u/${u.id}`} className="btn btn-line btn-sm gap-1" aria-label={`查看 ${u.nickname} 的个人主页`}>
<ExternalLink size={13} />
主页
</Link>
<button
type="button"
disabled={busy}
onClick={() => setRoleTarget(u)}
className="btn btn-line btn-sm gap-1"
style={roleStyle}
aria-label={`设置 ${u.username} 的角色与账号状态`}
>
<ShieldCheck size={15} />
</AdminIconButton>
<ShieldCheck size={13} />
角色与账号
</button>
</div>
);
};
@@ -971,41 +978,7 @@ export default function UsersAdmin({
{u.last_login_at ? ` · ${formatRelative(u.last_login_at)}` : ""}
</button>
</p>
<div className="flex items-center gap-2 mt-3 flex-wrap">
{isOwner(currentUserRole) && (
<Link href={`/admin/users/${u.id}`} className="btn btn-line btn-sm">
<FolderSearch size={13} /> 内容档案
</Link>
)}
{(isSelf || u.role === ROLES.OWNER) && (
<Link href={`/u/${u.id}`} className="btn btn-line btn-sm">
<ExternalLink size={13} /> 主页
</Link>
)}
{!isSelf && u.role !== ROLES.OWNER && (
<>
<Link href={`/u/${u.id}`} className="btn btn-line btn-sm">
<ExternalLink size={13} /> 主页
</Link>
<button
type="button"
disabled={actingId === u.id}
onClick={() => setRoleTarget(u)}
className="btn btn-line btn-sm"
style={
u.banned
? { color: "var(--danger)", borderColor: "var(--danger)" }
: u.role !== ROLES.USER
? { color: "var(--accent)", borderColor: "var(--accent)" }
: undefined
}
>
<ShieldCheck size={13} />
角色与账号
</button>
</>
)}
</div>
<div className="mt-3">{renderActions(u, isSelf)}</div>
</div>
</div>
);