补齐问答采纳/重开、投票匿名与有票禁编、悬赏过期退回、抽奖回帖开奖,并接入积分账本与发帖附件可见性。 Co-authored-by: Cursor <cursoragent@cursor.com>
71 lines
2.3 KiB
TypeScript
71 lines
2.3 KiB
TypeScript
"use client";
|
||
|
||
import { Crown, BadgeCheck, ShieldCheck, Shield, Users, type LucideIcon } from "lucide-react";
|
||
import { roleMeta, type RoleIconKey } from "@/lib/roles";
|
||
|
||
// 角色图标集中映射(图标库不含 ShieldStar,超管使用 BadgeCheck 表达"全权限认证")
|
||
const ICONS: Record<RoleIconKey, LucideIcon> = {
|
||
crown: Crown,
|
||
"shield-star": BadgeCheck,
|
||
shield: ShieldCheck,
|
||
"shield-board": Shield,
|
||
user: Users,
|
||
};
|
||
|
||
/**
|
||
* 角色徽章:图标 + 文案,配色走角色元数据中的 CSS token。
|
||
* - board_admin 额外展示授权板块数(或板块名)
|
||
* - variant=plain 时仅图标+文字不着底色(用于表头/文本行)
|
||
*/
|
||
export default function RoleBadge({
|
||
role,
|
||
boardIds,
|
||
boardNames,
|
||
size = 11,
|
||
className = "",
|
||
title,
|
||
truncate = false,
|
||
}: {
|
||
role?: string | null;
|
||
boardIds?: number[] | null;
|
||
/** id -> 名称映射;提供时板块管理员展示板块名(多选时折叠为 N 个板块) */
|
||
boardNames?: Map<number, string> | Record<number, string>;
|
||
size?: number;
|
||
className?: string;
|
||
title?: string;
|
||
/** 窄列场景:限制最大宽度并省略过长文案 */
|
||
truncate?: boolean;
|
||
}) {
|
||
const meta = roleMeta(role);
|
||
const Icon = ICONS[meta.icon];
|
||
|
||
let label = meta.label;
|
||
if (role === "board_admin") {
|
||
if (boardNames) {
|
||
const names = (boardIds ?? [])
|
||
.map((id) => (boardNames instanceof Map ? boardNames.get(id) : boardNames[id]))
|
||
.filter(Boolean) as string[];
|
||
if (names.length === 1) {
|
||
label = `${meta.label} · ${names[0]}`;
|
||
} else if (names.length > 1) {
|
||
label = `${meta.label} · ${names.length} 个板块`;
|
||
}
|
||
} else if (boardIds && boardIds.length > 0) {
|
||
label = `${meta.label} · ${boardIds.length} 个板块`;
|
||
}
|
||
}
|
||
|
||
return (
|
||
<span
|
||
className={`inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[11px] font-bold leading-[1.6] ${
|
||
truncate ? "max-w-full whitespace-nowrap" : "whitespace-nowrap"
|
||
} ${className}`}
|
||
style={{ background: `var(${meta.softToken})`, color: `var(${meta.colorToken})` }}
|
||
data-tip={title ?? label}
|
||
>
|
||
<Icon size={size} className="shrink-0" />
|
||
{truncate ? <span className="overflow-hidden text-ellipsis">{label}</span> : label}
|
||
</span>
|
||
);
|
||
}
|