feat: 站点访问统计与可配置侧边栏组件
- 新增访问统计:visitstats 服务与查询、visit_capture 中间件、爬虫/来源识别、管理端 analytics 页面 - 新增侧边栏:sidebar 服务与 handler、管理端 sidebar 配置页、前端侧边栏组件 - 新增 widget 嵌入代码生成(widgetCode)与 widgetApi - 遥测逻辑迁移至 handler 层,删除 service/telemetry - env 示例补充 Docker 部署可信代理 IP 说明
This commit is contained in:
40
frontend/components/sidebar/SidebarRenderer.tsx
Normal file
40
frontend/components/sidebar/SidebarRenderer.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { Fragment } from "react";
|
||||
import type { SidebarBlockRef, SidebarWidgetPublic } from "@/lib/sidebarMeta";
|
||||
import { renderSidebarBlock, type SidebarBlockProps } from "./registry";
|
||||
|
||||
// 页面从本文件导入该类型(registry 中定义)
|
||||
export type { SidebarBlockProps };
|
||||
import CustomWidgetBlock from "./CustomWidgetBlock";
|
||||
|
||||
// 按管理端配置渲染一列侧栏块:
|
||||
// - 内置块经注册表解析,未知 key / 数据缺失静默跳过;
|
||||
// - 自定义工具引用已删除或停用(公开载荷不含)时静默跳过;
|
||||
// - className 缺省时输出 Fragment,布局容器(gap/space-y、sticky、限高)由页面自行决定。
|
||||
export default function SidebarRenderer({
|
||||
blocks,
|
||||
widgets,
|
||||
props,
|
||||
className,
|
||||
}: {
|
||||
blocks: SidebarBlockRef[];
|
||||
widgets: SidebarWidgetPublic[];
|
||||
props: SidebarBlockProps;
|
||||
className?: string;
|
||||
}) {
|
||||
const items = blocks
|
||||
.map((ref, i) => {
|
||||
if (ref.widget) {
|
||||
const widget = widgets.find((x) => x.id === ref.widget);
|
||||
if (!widget) return null;
|
||||
return <CustomWidgetBlock key={`w-${widget.id}-${i}`} widget={widget} viewer={props.user} />;
|
||||
}
|
||||
if (!ref.key) return null;
|
||||
const node = renderSidebarBlock(ref.key, props);
|
||||
if (node === null) return null;
|
||||
return <Fragment key={`k-${ref.key}-${i}`}>{node}</Fragment>;
|
||||
})
|
||||
.filter((x) => x !== null);
|
||||
|
||||
if (items.length === 0) return null;
|
||||
return className ? <div className={className}>{items}</div> : <>{items}</>;
|
||||
}
|
||||
Reference in New Issue
Block a user