Files
jiang13-bbs/frontend/components/RoleBadge.tsx
freefire 5507552ca5 polish(frontend): 侧栏与徽章可读性,智能悬停提示
降权全站字重;优化右栏公告/数据块;类型状态徽章用固定浅色区分;TipHost 仅在文案不一致、非文字或省略时显示。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-16 03:54:14 +08:00

71 lines
2.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"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-medium 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>
);
}