feat: 添加 Admin Dashboard — React 19 SPA,包含 7 个页面

- Dashboard: 健康状态轮询、状态卡片、内存统计、快捷操作
- Login: 二维码展示 + 3 秒自动轮询 + 倒计时 + 登出
- Browser: 探索/搜索/用户三标签页,Feed 网格、详情面板、评论树
- Publish: 图文/视频发布表单,支持标签、可见性、定时发布
- Interactions: 点赞/取消点赞、收藏、评论、回复 + 操作日志
- API Tester: 端点选择器、请求体编辑器、cURL 生成、响应查看、历史记录
- Settings: Token 配置、服务器 URL 设置

后端改动:
- app.ts: 生产环境提供 dist/web/ 静态文件服务 + SPA fallback
- Dockerfile: 添加 web 构建阶段
- package.json: 添加 build:web、build:all、dev:web 脚本

技术栈: React 19 + TypeScript + Vite 6 + Tailwind CSS(暗色主题)
产物: 85.5 KB gzip JS + 4 KB gzip CSS,零重型依赖
This commit is contained in:
2026-03-01 13:58:55 +08:00
parent 6d35387e2b
commit c6a8177718
51 changed files with 5665 additions and 1 deletions
+39
View File
@@ -0,0 +1,39 @@
import { cn } from '@/lib/cn';
import type { SelectHTMLAttributes } from 'react';
interface Option {
value: string;
label: string;
}
interface Props extends SelectHTMLAttributes<HTMLSelectElement> {
label?: string;
options: Option[];
}
export function Select({ label, options, className, id, ...rest }: Props) {
const selectId = id || label?.toLowerCase().replace(/\s+/g, '-');
return (
<div className="flex flex-col gap-1.5">
{label && (
<label htmlFor={selectId} className="text-sm text-dark-muted">
{label}
</label>
)}
<select
id={selectId}
className={cn(
'bg-dark-bg border border-dark-border rounded-lg px-3 py-2 text-sm text-dark-text focus:outline-none focus:border-dark-accent transition-colors',
className,
)}
{...rest}
>
{options.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
);
}