feat: 站点访问统计与可配置侧边栏组件
- 新增访问统计:visitstats 服务与查询、visit_capture 中间件、爬虫/来源识别、管理端 analytics 页面 - 新增侧边栏:sidebar 服务与 handler、管理端 sidebar 配置页、前端侧边栏组件 - 新增 widget 嵌入代码生成(widgetCode)与 widgetApi - 遥测逻辑迁移至 handler 层,删除 service/telemetry - env 示例补充 Docker 部署可信代理 IP 说明
This commit is contained in:
155
frontend/components/sidebar/registry.tsx
Normal file
155
frontend/components/sidebar/registry.tsx
Normal file
@@ -0,0 +1,155 @@
|
||||
import type {
|
||||
ActiveUser,
|
||||
AnnouncementSummary,
|
||||
Board,
|
||||
BoardModerator,
|
||||
BoardSidebarStats,
|
||||
BoardWithCount,
|
||||
CheckinStatus,
|
||||
NewUser,
|
||||
OverviewStats,
|
||||
PostListItem,
|
||||
PublicAdItem,
|
||||
PublicSponsorItem,
|
||||
SidebarPageItem,
|
||||
User,
|
||||
UserProfile,
|
||||
} from "@/lib/api";
|
||||
import PostToc, { type TocItem } from "@/components/PostToc";
|
||||
import { WorkspaceNavPanel } from "@/components/home/WorkspaceNav";
|
||||
import {
|
||||
PulseCard,
|
||||
Announcements,
|
||||
AdsPanel,
|
||||
SponsorsPanel,
|
||||
HotRanking,
|
||||
NewUsers,
|
||||
ActiveUsers,
|
||||
FriendLinks,
|
||||
AboutSite,
|
||||
} from "@/components/home/CommunityPanels";
|
||||
import {
|
||||
Moderators,
|
||||
BoardIntro,
|
||||
BoardPulse,
|
||||
BoardHot,
|
||||
BoardActive,
|
||||
} from "@/components/home/BoardPanels";
|
||||
import {
|
||||
BoardCard as PostBoardCard,
|
||||
AuthorRailCard as PostAuthorCard,
|
||||
AuthorPosts as PostAuthorPosts,
|
||||
} from "@/components/post/PostRailBlocks";
|
||||
|
||||
// 侧栏块 props 汇总:页面把已有数据整包传入,块按需取用;
|
||||
// 某块所需数据缺失(如板块态没传 stats)时对应块渲染为空,安全跳过。
|
||||
export interface SidebarBlockProps {
|
||||
allowComments: boolean;
|
||||
user?: User | null;
|
||||
checkin?: CheckinStatus | null;
|
||||
|
||||
// home 范围
|
||||
boards?: BoardWithCount[];
|
||||
stats?: OverviewStats;
|
||||
announcements?: AnnouncementSummary[];
|
||||
canManageAnnouncements?: boolean;
|
||||
ads?: PublicAdItem[];
|
||||
adsPanelTitle?: string;
|
||||
adsEnabled?: boolean;
|
||||
sponsors?: PublicSponsorItem[];
|
||||
sponsorsPanelTitle?: string;
|
||||
sponsorsEnabled?: boolean;
|
||||
hot?: PostListItem[];
|
||||
newUsers?: NewUser[];
|
||||
activeUsers?: ActiveUser[];
|
||||
sidebarPages?: SidebarPageItem[];
|
||||
siteDescription?: string;
|
||||
canManagePages?: boolean;
|
||||
|
||||
// board 范围
|
||||
board?: BoardWithCount;
|
||||
moderators?: BoardModerator[];
|
||||
boardStats?: BoardSidebarStats;
|
||||
boardHot?: PostListItem[];
|
||||
boardActiveUsers?: ActiveUser[];
|
||||
|
||||
// post 范围
|
||||
postBoard?: Board;
|
||||
authorProfile?: UserProfile;
|
||||
authorPosts?: PostListItem[];
|
||||
toc?: TocItem[];
|
||||
}
|
||||
|
||||
// 内置块注册表:key → 组件。数据未就绪或未知 key 返回 null(渲染层静默跳过)。
|
||||
export function renderSidebarBlock(key: string, p: SidebarBlockProps) {
|
||||
switch (key) {
|
||||
case "board_nav":
|
||||
return p.boards ? <WorkspaceNavPanel boards={p.boards} user={p.user} /> : null;
|
||||
case "pulse":
|
||||
return p.stats ? (
|
||||
<PulseCard stats={p.stats} checkin={p.checkin} allowComments={p.allowComments} />
|
||||
) : null;
|
||||
case "announcements":
|
||||
return p.announcements ? (
|
||||
<Announcements items={p.announcements} canManageAnnouncements={p.canManageAnnouncements} />
|
||||
) : null;
|
||||
case "ads":
|
||||
return p.ads && p.adsEnabled !== false ? (
|
||||
<AdsPanel items={p.ads} title={p.adsPanelTitle || "自助推广"} enabled />
|
||||
) : null;
|
||||
case "sponsors":
|
||||
return p.sponsors && p.sponsorsEnabled !== false ? (
|
||||
<SponsorsPanel
|
||||
items={p.sponsors}
|
||||
title={p.sponsorsPanelTitle || "赞助商"}
|
||||
enabled
|
||||
/>
|
||||
) : null;
|
||||
case "hot":
|
||||
return p.hot ? <HotRanking posts={p.hot} allowComments={p.allowComments} /> : null;
|
||||
case "new_users":
|
||||
return p.newUsers ? <NewUsers users={p.newUsers} /> : null;
|
||||
case "active_users":
|
||||
return p.activeUsers ? (
|
||||
<ActiveUsers users={p.activeUsers} allowComments={p.allowComments} />
|
||||
) : null;
|
||||
case "friend_links":
|
||||
return <FriendLinks />;
|
||||
case "about_site":
|
||||
return p.sidebarPages || p.canManagePages ? (
|
||||
<AboutSite
|
||||
pages={p.sidebarPages ?? []}
|
||||
siteDescription={p.siteDescription}
|
||||
canManagePages={p.canManagePages}
|
||||
/>
|
||||
) : null;
|
||||
case "board_moderators":
|
||||
return p.moderators ? <Moderators items={p.moderators} /> : null;
|
||||
case "board_intro":
|
||||
return p.board ? <BoardIntro board={p.board} /> : null;
|
||||
case "board_pulse":
|
||||
return p.boardStats ? (
|
||||
<BoardPulse stats={p.boardStats} checkin={p.checkin} allowComments={p.allowComments} />
|
||||
) : null;
|
||||
case "board_hot":
|
||||
return p.boardHot ? (
|
||||
<BoardHot posts={p.boardHot} allowComments={p.allowComments} />
|
||||
) : null;
|
||||
case "board_active":
|
||||
return p.boardActiveUsers ? (
|
||||
<BoardActive users={p.boardActiveUsers} allowComments={p.allowComments} />
|
||||
) : null;
|
||||
case "post_board":
|
||||
return p.postBoard ? <PostBoardCard board={p.postBoard} /> : null;
|
||||
case "post_author":
|
||||
return p.authorProfile ? (
|
||||
<PostAuthorCard profile={p.authorProfile} allowComments={p.allowComments} />
|
||||
) : null;
|
||||
case "post_author_posts":
|
||||
return p.authorPosts ? <PostAuthorPosts posts={p.authorPosts} /> : null;
|
||||
case "post_toc":
|
||||
return p.toc && p.toc.length > 0 ? <PostToc items={p.toc} /> : null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user