// 侧栏管理:内置块元数据与默认布局(管理页与前台注册表共用,勿在此引入组件) export type SidebarScope = "home" | "board" | "post"; export interface SidebarBlockRef { key?: string; widget?: number; } export interface SidebarScopeConfig { left?: SidebarBlockRef[] | null; right?: SidebarBlockRef[] | null; } export interface SidebarConfig { version: number; scopes: Partial>; } export interface SidebarWidgetPublic { id: number; name: string; html: string; css: string; js: string; } export interface SidebarPublicPayload { config: SidebarConfig; widgets: SidebarWidgetPublic[]; } export interface SidebarWidgetRecord { id: number; name: string; html: string; css: string; js: string; enabled: boolean; created_at: string; updated_at: string; } // 每侧最多放置块数(与后端 SidebarMaxBlocksPerSide 一致) export const SIDEBAR_MAX_BLOCKS_PER_SIDE = 24; export const SIDEBAR_SCOPE_LABELS: Record = { home: "首页", board: "板块页", post: "帖子详情", }; export const SIDEBAR_SIDE_LABELS: Record<"left" | "right", string> = { left: "左栏", right: "右栏", }; // 各页面范围可用的内置块(key → 展示名),与后端 sidebarBuiltinKeys 保持一致 export const SIDEBAR_WIDGET_META: Record> = { home: { board_nav: "板块目录", pulse: "站点数据", announcements: "站点公告", ads: "自助推广", sponsors: "赞助商", hot: "热门讨论", new_users: "最新注册", active_users: "活跃成员", friend_links: "友情链接", about_site: "关于本站", }, board: { board_moderators: "板块管理员", board_intro: "板块概况", board_pulse: "板块数据", board_hot: "本板热门", board_active: "活跃成员", }, post: { post_board: "所属板块", post_author: "作者名片", post_author_posts: "作者其他帖", post_toc: "文章目录", }, }; // 内置块 icon(字符串引用,渲染方 import lucide-react 时映射) // 保持轻量:不在 sidebarMeta 里引入组件 export const SIDEBAR_BLOCK_ICON: Record = { board_nav: "layout-grid", pulse: "bar-chart-3", announcements: "megaphone", ads: "circle-dollar-sign", sponsors: "handshake", hot: "flame", new_users: "user-plus", active_users: "users", friend_links: "link2", about_site: "info", board_moderators: "shield-check", board_intro: "file-text", board_pulse: "bar-chart-3", board_hot: "flame", board_active: "users", post_board: "layers", post_author: "user", post_author_posts: "pen-line", post_toc: "list", }; // 默认布局(= 历史硬编码渲染;后端 DefaultSidebarConfig 的前端镜像) export function defaultSidebarConfig(): SidebarConfig { const refs = (keys: string[]): SidebarBlockRef[] => keys.map((key) => ({ key })); return { version: 1, scopes: { home: { left: refs(["board_nav"]), right: refs([ "pulse", "announcements", "ads", "sponsors", "hot", "new_users", "active_users", "friend_links", "about_site", ]), }, board: { left: [], right: refs(["board_moderators", "board_intro", "board_pulse", "board_hot", "board_active"]), }, post: { left: refs(["post_board", "post_author", "post_author_posts"]), right: refs(["post_toc"]), }, }, }; } // 按页面范围解析左右两列的块列表: // - 未配置(nil)的侧回落默认布局,显式空数组 = 站长清空该侧; // - board 范围左栏跟随 home.left(板块态仅管理右栏)。 export function sidebarScopeLists( config: SidebarConfig, scope: SidebarScope ): { left: SidebarBlockRef[]; right: SidebarBlockRef[] } { const def = defaultSidebarConfig().scopes; if (scope === "board") { return { left: config.scopes.home?.left ?? def.home?.left ?? [], right: config.scopes.board?.right ?? def.board?.right ?? [], }; } return { left: config.scopes[scope]?.left ?? def[scope]?.left ?? [], right: config.scopes[scope]?.right ?? def[scope]?.right ?? [], }; } // 块的展示名:内置取 META,自定义取备注名;未知返回 null(渲染时跳过) export function sidebarBlockLabel( ref: SidebarBlockRef, widgets: { id: number; name: string }[] ): { label: string; custom: boolean } | null { if (ref.widget) { const w = widgets.find((x) => x.id === ref.widget); return w ? { label: w.name, custom: true } : null; } if (ref.key) { for (const scope of Object.keys(SIDEBAR_WIDGET_META) as SidebarScope[]) { const label = SIDEBAR_WIDGET_META[scope][ref.key]; if (label) return { label, custom: false }; } } return null; }