Files
jiang13-forum/frontend/src/components/admin/AsideWidgetList.tsx
freefire 429956c594 feat: 管理端网站监控,浏览量写入独立 monitor.db
请求日志按日 JSONL;page_views 不进主库,避免统计数据撑大 jiang13.db。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-31 07:55:45 +08:00

80 lines
2.6 KiB
TypeScript

import AdminSortableList, { SortableDragHandle } from './AdminSortableList';
import type { AsideWidget, AsideWidgetId } from '../../api/types';
const WIDGET_META: Record<AsideWidgetId, { label: string; hint: string }> = {
tag_cloud: {
label: '标签云',
hint: '在右侧栏展示热门标签',
},
recent_comments: {
label: '最新评论',
hint: '在右侧栏展示最近回复',
},
recent_users: {
label: '最新注册',
hint: '在右侧栏展示最近注册的用户',
},
friend_links: {
label: '友情链接',
hint: '关闭后不在右侧栏展示,友链仍可在「友情链接」页面查看与申请',
},
showcase: {
label: '开源展柜',
hint: '展示精选公网部署;关闭后仍可直接访问 /showcase',
},
};
type Props = {
widgets: AsideWidget[];
onChange: (next: AsideWidget[]) => void;
};
export default function AsideWidgetList({ widgets, onChange }: Props) {
const handleToggle = (id: AsideWidgetId, enabled: boolean) => {
onChange(widgets.map(w => (w.id === id ? { ...w, enabled } : w)));
};
return (
<AdminSortableList
items={widgets}
getId={widget => widget.id}
onReorder={onChange}
showMoveButtons={false}
className="admin-sortable-list admin-sortable-list--boxed"
ariaLabel="右侧栏组件"
renderItem={(widget, _index, controls) => {
const meta = WIDGET_META[widget.id];
return (
<div
ref={controls.setNodeRef}
style={controls.style}
className={`admin-sortable-row admin-sortable-row--widget${controls.isDragging ? ' is-dragging' : ''}`}
>
<SortableDragHandle
label={`拖拽调整「${meta.label}」顺序`}
{...controls.dragHandleProps}
/>
<div className="admin-sortable-row__main">
<span className="admin-sortable-row__label" id={`aside-widget-label-${widget.id}`}>
{meta.label}
</span>
<span className="admin-sortable-row__hint">{meta.hint}</span>
</div>
<button
type="button"
id={`aside-widget-${widget.id}`}
role="switch"
aria-checked={widget.enabled}
aria-labelledby={`aside-widget-label-${widget.id}`}
className={`admin-settings-switch${widget.enabled ? ' is-on' : ''}`}
onClick={() => handleToggle(widget.id, !widget.enabled)}
>
<span className="admin-settings-switch-ui" aria-hidden />
</button>
</div>
);
}}
/>
);
}