# 产品需求文档 (PRD) ## 1. 项目概述 - **项目名称**: Jimeng Clone — AI 视频生成平台 - **一句话描述**: 1:1 还原即梦平台 (jimeng.jianying.com) 的 AI 视频生成页面,包含完整的用户认证体系、视频生成输入界面、后台管理系统和用户个人中心 - **目标用户**: 前端开发学习者、AI 视频产品原型验证团队、平台管理员 - **项目范围**: 视频生成输入栏(InputBar)及其交互逻辑 + Django 后端 API + 用户登录注册 + 后台管理(生成秒数统计与限制) + 用户个人中心(消费概览与记录) ### 开发阶段划分 | 阶段 | 范围 | 状态 | |------|------|------| | **Phase 1** | 纯前端视频生成输入界面(InputBar、工具栏、上传、模式切换) | ✅ 已完成 | | **Phase 2** | Django 后端 + 用户认证 + 前端路由 + 管理后台(基于调用次数) | ✅ 已完成 | | **Phase 3** | 计量单位变更(次数→秒数)+ 管理后台重做(多页面 Sidebar)+ 用户个人中心 | 🔲 待开发 | > **当前迭代范围**: Phase 3。Phase 1 和 Phase 2 功能已完成。Phase 3 在 Phase 2 基础上进行重大重构和新增功能。文档中 `[Phase 3]` 标记用于区分本次迭代的新增/修改内容。 ### Phase 3 核心变更摘要 1. **计量单位变更**: 所有「调用次数」改为「生成秒数」,用户每次生成视频消耗的是秒数(= 视频时长),不是次数 2. **后台管理系统重做**: 从单页面改为多页面 + 左侧 Sidebar 导航(仪表盘/用户管理/消费记录/系统设置) 3. **新增用户个人中心** `/profile`: 消费概览 + 消费记录 + 趋势迷你图 4. **图表库引入**: 使用 ECharts(echarts + echarts-for-react)展示趋势图、排行榜、环形进度条 5. **设计升级**: 管理后台深色主题(Linear/Vercel 风格)、骨架屏加载、页面过渡动画 ## 2. 功能需求 ### 2.1 核心功能(P0) #### 已有前端功能 - [x] **深色主题全屏页面** — 背景色 `#0a0a0f`,页面无滚动,输入栏固定在底部居中,最大宽度 ~900px - [x] **InputBar 容器** — 背景 `#16161e`,边框 `1px solid #2a2a38`,圆角 `20px`,内部分为上半区(上传+输入)和下半区(工具栏) - [x] **提示词多行文本输入框** — 自适应高度(min 1行,max ~6行),支持换行输入,placeholder 根据当前模式动态切换 - [x] **全能参考模式(默认模式)** — 左侧 [+ 参考内容] 上传按钮,支持上传 1-5 张图片/视频文件;上传后显示缩略图网格,每张标注「图片1」「图片2」等序号,每个缩略图右上角有 × 关闭按钮;上传区与文本输入框左右排列 - [x] **首尾帧模式** — 上传区变为 [首帧缩略图] ↔ [+ 尾帧] 双框布局,中间双向箭头图标连接;首帧和尾帧各上传一张图片 - [x] **模式切换下拉** — 工具栏中「全能参考/首尾帧」下拉菜单,点击切换模式,切换时联动:上传区 UI、比例选项、时长默认值 - [x] **工具栏按钮行** — 一行横排按钮,透明背景,灰色文字 `#8a8a9a`,hover 时微亮背景 - [x] **发送按钮** — 圆形按钮,内含上箭头图标;无内容时灰色不可点击,有内容(文字或上传文件)时变为蓝色 `#00b8e6` 可点击 #### [Phase 2] 用户认证系统 - [x] **用户注册页面** — 前端注册页 `/register`,包含用户名、邮箱、密码、确认密码字段,提交后调用后端注册 API - [x] **用户登录页面** — 前端登录页 `/login`,包含用户名/邮箱、密码字段,登录成功后获取 JWT Token 并存储到 localStorage - [x] **JWT 认证机制** — 后端签发 Access Token(有效期 2 小时)+ Refresh Token(有效期 7 天),前端请求自动附加 Authorization Bearer Token - [x] **登录状态保持** — 前端全局 Auth 状态管理(Zustand),未登录用户自动跳转到登录页,已登录用户显示用户信息和退出按钮 - [x] **Token 自动刷新** — Access Token 过期时自动使用 Refresh Token 刷新,刷新失败则跳转到登录页 #### [Phase 2] Django 后端服务 - [x] **Django 项目初始化** — 在 `backend/` 子目录创建 Django 项目,连接 MySQL 云数据库 - [x] **RESTful API** — 使用 Django REST Framework (DRF) 提供用户认证、视频生成记录等 API - [x] **CORS 配置** — 允许前端开发服务器(localhost:5173)跨域访问后端 API #### [Phase 3] 计量单位变更(次数 → 秒数) - [ ] **用户配额字段变更** — User 模型的 `daily_limit` / `monthly_limit`(次数)改为 `daily_seconds_limit` / `monthly_seconds_limit`(秒数),默认值分别为 600秒/日、6000秒/月 - [ ] **消费记录增加秒数字段** — GenerationRecord 模型新增 `seconds_consumed` 字段(FloatField),记录每次生成消耗的秒数(= 视频时长 duration) - [ ] **配额检查逻辑变更** — 后端配额检查从「今日调用次数 < daily_limit」改为「今日消费秒数 < daily_seconds_limit」 - [ ] **前端展示变更** — 所有显示「剩余次数」的地方改为「剩余秒数」,UserInfoBar 组件配额显示改为秒数 #### [Phase 3] 后台管理系统重做 - [ ] **管理后台布局** — 采用左侧 Sidebar + 右侧内容区的经典管理后台布局,Sidebar 固定宽度 240px,支持折叠 - [ ] **Sidebar 导航菜单** — 包含 4 个导航项:仪表盘、用户管理、消费记录、系统设置,当前项高亮,使用 react-router 嵌套路由 - [ ] **管理后台路由** — `/admin` 为管理后台根路由(重定向到 `/admin/dashboard`),子路由:`/admin/dashboard`、`/admin/users`、`/admin/records`、`/admin/settings` #### [Phase 3] 用户个人中心 - [ ] **个人中心页面** — 新增 `/profile` 路由,已登录用户可访问,展示个人消费概览和历史记录 - [ ] **消费概览卡片** — 显示已用秒数/总额度(环形进度条,使用 ECharts gauge)、今日已用/日限额、本月已用/月限额 ### 2.2 重要功能(P1) #### 已有前端功能 - [x] **视频生成下拉按钮** — 蓝色文字 `#00b8e6` + 视频图标 + 下拉箭头,点击展开下拉菜单(菜单项仅 UI 展示,如"视频生成"/"图片生成"等) - [x] **模型选择按钮** — 显示「Seedance 2.0」+ 钻石图标,点击展开模型选择下拉(仅 UI 展示,如 Seedance 2.0 / Seedance 2.0 Fast) - [x] **比例选择按钮** — 全能参考模式下显示屏幕图标 + 当前比例值,点击弹出选项:`16:9` / `9:16` / `1:1` / `21:9` / `4:3` / `3:4`,默认 `21:9`;首尾帧模式下显示「自动匹配」不可选择 - [x] **时长选择按钮** — 时钟图标 + 当前时长值,点击弹出选项:`5s` / `10s` / `15s`;全能参考模式默认 `15s`,首尾帧模式默认 `5s` - [x] **@ 按钮** — 仅在全能参考模式下显示,点击插入 `@` 符号到文本输入框光标位置 - [x] **文件上传交互** — 点击上传区触发文件选择器,accept 为 `image/*,video/*`;上传后生成本地预览缩略图;支持拖拽上传 - [x] **上传数量限制** — 全能参考模式最多 5 张,超出时 toast 提示;首尾帧模式首帧/尾帧各 1 张 #### [Phase 2] 后台管理与生成接入 - [x] **Django Admin 集成** — 启用 Django Admin 面板(`/admin/`),管理员可查看和管理所有用户、视频生成记录 - [x] **发送按钮接入后端** — 点击发送按钮时,调用后端 `/api/v1/video/generate` 接口(需登录),后端记录调用并检查配额 #### [Phase 3] 仪表盘页面(`/admin/dashboard`) - [ ] **核心指标卡片** — 4 个统计卡片:总用户数、今日新增用户、今日消费秒数、本月消费秒数。每个卡片显示数值 + 环比变化百分比 + 趋势箭头(↑绿色/↓红色) - [ ] **消费趋势折线图** — 使用 ECharts 折线图展示近 30 天每日消费秒数趋势,支持 tooltip 悬浮显示具体数值,X轴为日期,Y轴为秒数 - [ ] **用户消费排行柱状图** — 使用 ECharts 水平柱状图展示消费 Top 10 用户(按本月消费秒数降序),柱状图标签显示用户名和秒数 - [ ] **时间范围选择器** — 支持「今日 / 近7天 / 近30天 / 自定义时间范围」切换,图表数据联动更新 - [ ] **图表 Mock 数据** — 开发阶段使用 30 天的真实结构 Mock 数据(随机波动、周末低谷),确保图表有真实感 #### [Phase 3] 用户管理页面(`/admin/users`) - [ ] **用户列表表格** — 分页展示所有用户(每页 20 条),列:头像、用户名、邮箱、注册时间、状态(启用/禁用)、日限额(秒)、月限额(秒)、今日消费(秒)、本月消费(秒)、操作 - [ ] **搜索和筛选** — 支持按用户名/邮箱关键字搜索 + 按状态筛选(全部/启用/禁用) - [ ] **配额编辑** — 每个用户行的操作列有「编辑配额」按钮,点击弹出模态框,可修改日限额秒数和月限额秒数 - [ ] **用户状态管理** — 操作列有「启用/禁用」开关按钮,点击后调用 API 切换用户 `is_active` 状态 - [ ] **用户详情抽屉** — 点击用户名展开右侧抽屉面板,显示用户详情 + 该用户近期消费记录列表 #### [Phase 3] 消费记录页面(`/admin/records`) - [ ] **消费明细表格** — 分页展示所有用户的消费记录,列:时间、用户名、消费秒数、视频描述(prompt 截断)、生成模式、状态 - [ ] **时间范围筛选** — 日期选择器,支持选择起止日期筛选记录 - [ ] **用户筛选** — 支持按用户名搜索筛选特定用户的消费记录 - [ ] **导出功能** — 「导出 CSV」按钮,将当前筛选条件下的消费记录导出为 CSV 文件 #### [Phase 3] 系统设置页面(`/admin/settings`) - [ ] **全局默认配额设置** — 表单修改全局默认日限额秒数和月限额秒数(新注册用户自动获得此配额) - [ ] **系统公告管理** — 公告文本编辑框 + 启用/禁用开关,启用后公告内容展示在用户端页面顶部 #### [Phase 3] 用户个人中心详细功能 - [ ] **消费记录列表** — 分页展示当前用户的消费记录,每条记录显示:时间、消费秒数、生成的视频描述(prompt)、生成模式、状态 - [ ] **消费趋势迷你图** — 使用 ECharts Sparkline 样式展示近 7 天 / 近 30 天每日消费秒数趋势,可切换时间范围 - [ ] **配额提示** — 当日额度消费超过 80% 时显示黄色警告提示,超过 100% 时显示红色禁用提示 ### 2.3 锦上添花(P2) - [x] **下拉菜单动画** — 下拉展开/收起有 fade + slide 动画过渡 - [x] **文本输入框自动聚焦** — 页面加载后自动 focus 到文本输入框 - [x] **键盘快捷键** — `Ctrl/Cmd + Enter` 触发发送(等同点击发送按钮) - [ ] **上传进度条** — 文件上传时缩略图上显示加载进度 - [ ] **拖拽排序** — 全能参考模式下已上传的缩略图支持拖拽调整顺序 - [x] **响应式适配** — 移动端窄屏下工具栏按钮文字隐藏只显示图标,输入栏宽度自适应 - [ ] **Tooltip 提示** — 工具栏按钮 hover 显示功能说明 tooltip - [ ] **[Phase 2] 忘记密码** — 邮箱验证码找回密码流程 - [ ] **[Phase 2] 用户个人资料编辑** — 查看和修改个人信息(头像、昵称) - [ ] **[Phase 3] 页面切换过渡动画** — 路由切换时使用 fade/slide 过渡动画,提升体验流畅度 - [ ] **[Phase 3] 数据加载骨架屏** — 管理后台和个人中心的数据加载使用骨架屏(Skeleton)替代 loading spinner - [ ] **[Phase 3] Sidebar 折叠模式** — 管理后台 Sidebar 支持折叠为图标模式,增大内容区宽度 ## 3. 技术栈建议 ### 3.1 现有技术栈(保留) | 层级 | 技术选型 | 说明 | |------|---------|------| | 前端框架 | React 18 + TypeScript | 函数组件 + Hooks | | 构建工具 | Vite 5 | 极速 HMR,原生 ESM | | UI 组件库 | @arco-design/web-react | 字节跳动设计系统,即梦同款 | | 状态管理 | Zustand | 轻量、TypeScript 友好 | | 样式方案 | CSS Modules + Arco Design Token | 深色主题定制 | | 图标 | @arco-design/web-react/icon + 自定义 SVG | 工具栏图标 | | 文件处理 | 浏览器原生 File API | 本地预览、缩略图生成 | ### 3.2 [Phase 2] 已有前端依赖(保留) | 依赖 | 说明 | |------|------| | react-router-dom v7 | 前端路由(登录页、注册页、管理页) | | axios | HTTP 请求库,支持拦截器实现 Token 自动附加和刷新 | ### 3.3 [Phase 3] 新增前端依赖 | 依赖 | 说明 | |------|------| | echarts | 图表库核心,用于折线图、柱状图、环形图等数据可视化 | | echarts-for-react | ECharts 的 React 封装组件,声明式使用图表 | > **组件优先级**: 如果 Arco Design 内置了对应组件(如 Table、Modal、Skeleton、DatePicker),优先使用 Arco 组件,ECharts 仅用于复杂图表。 ### 3.4 [Phase 2] 后端技术栈(保留) | 层级 | 技术选型 | 说明 | |------|---------|------| | 后端框架 | Django 4.2+ (LTS) | Python Web 框架 | | API 框架 | Django REST Framework (DRF) | RESTful API 开发 | | 认证方案 | djangorestframework-simplejwt | JWT Token 签发与验证 | | 数据库 | MySQL 8.0(阿里云 RDS) | 云数据库,已提供连接信息 | | 数据库驱动 | mysqlclient | Django 官方推荐的 MySQL 驱动 | | CORS | django-cors-headers | 跨域请求支持 | | 后端管理 | Django Admin | 内置管理后台 | | 部署 | Gunicorn + Nginx | 生产环境部署方案 | > **后端代码目录**: 所有后端代码放在项目根目录下的 `backend/` 子目录中。 ### 3.5 数据库连接配置(不变) ```python # backend/config/settings.py DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'video_auto', 'USER': 'ai_video', 'PASSWORD': 'JogNQdtrd3WY8CBCAiYfYEGx', 'HOST': 'rm-7xv1uaw910558p1788o.mysql.rds.aliyuncs.com', 'PORT': '3306', 'OPTIONS': { 'charset': 'utf8mb4', 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", }, } } ``` ## 4. 页面列表 ### 4.1 [已有] 视频生成页 (`/`) **整体布局**: ``` ┌─────────────────────────────────────────────┐ │ [Phase 3 修改] 顶部用户信息: │ │ 用户名 | 剩余: 345s/600s(日) | [个人中心] [退出]│ │ │ │ 深色背景空白区域 │ │ #0a0a0f │ │ │ │ ┌─────────────────────────────────────┐ │ │ │ InputBar (底部固定) │ │ │ │ max-width: 900px, 居中 │ │ │ │ 背景: #16161e │ │ │ │ 边框: #2a2a38, 圆角: 20px │ │ │ │ │ │ │ │ ┌──────┐ ┌──────────────────────┐ │ │ │ │ │上传区 │ │ 提示词文本输入框 │ │ │ │ │ │ │ │ │ │ │ │ │ └──────┘ └──────────────────────┘ │ │ │ │ │ │ │ │ ─────────── 工具栏按钮行 ────────── │ │ │ │ [视频生成▼][模型][模式▼][比例][时长] │ │ │ │ [@] flex空白 [发送⬆] │ │ │ └─────────────────────────────────────┘ │ └─────────────────────────────────────────────┘ ``` - [Phase 3 修改] 顶部用户信息区的配额显示从「剩余 N 次」改为「剩余 N 秒」,增加「个人中心」链接 - [Phase 2] 需要登录才能访问,未登录重定向到 `/login` **全能参考模式 — 上传区细节**: ``` ┌──────────┐ │ + │ ← 初始状态: [+ 参考内容] 按钮 │ 参考内容 │ 虚线边框,点击触发上传 └──────────┘ ┌───┐┌───┐┌──────┐ │图1││图2││ + │ ← 已上传状态: 缩略图网格 + 添加按钮 │ × ││ × ││ │ 每张有序号标签和关闭按钮 └───┘└───┘└──────┘ ``` **首尾帧模式 — 上传区细节**: ``` ┌──────┐ ┌──────┐ │ 首帧 │ ↔ │+ 尾帧 │ ← 两个独立上传框 │ │ │ │ 中间双向箭头 └──────┘ └──────┘ ``` **工具栏按钮排布**: ``` 全能参考模式: [🎬 视频生成 ▼] [💎 Seedance 2.0] [✨ 全能参考 ▼] [🖥 21:9] [🕐 15s] [@] ——flex空白—— [⬆ 发送] 首尾帧模式: [🎬 视频生成 ▼] [💎 Seedance 2.0] [🔀 首尾帧 ▼] [自动匹配] [🕐 5s] ——flex空白—— [⬆ 发送] ``` ### 4.2 [已有] 登录页 (`/login`) ``` ┌─────────────────────────────────────────────┐ │ │ │ 深色背景 #0a0a0f │ │ │ │ ┌──────────────────────┐ │ │ │ Jimeng Clone │ │ │ │ │ │ │ │ 用户名/邮箱: [____] │ │ │ │ 密码: [____] │ │ │ │ │ │ │ │ [ 登录 ] │ │ │ │ │ │ │ │ 没有账号?去注册 → │ │ │ └──────────────────────┘ │ │ │ └─────────────────────────────────────────────┘ ``` - 表单验证:用户名/邮箱必填,密码最少 6 位 - 登录成功后跳转到 `/`(视频生成页) - 风格与主应用一致:深色主题,卡片式表单 ### 4.3 [已有] 注册页 (`/register`) ``` ┌─────────────────────────────────────────────┐ │ │ │ 深色背景 #0a0a0f │ │ │ │ ┌──────────────────────┐ │ │ │ 创建账号 │ │ │ │ │ │ │ │ 用户名: [____] │ │ │ │ 邮箱: [____] │ │ │ │ 密码: [____] │ │ │ │ 确认密码: [____] │ │ │ │ │ │ │ │ [ 注册 ] │ │ │ │ │ │ │ │ 已有账号?去登录 → │ │ │ └──────────────────────┘ │ │ │ └─────────────────────────────────────────────┘ ``` - 表单验证:用户名 3-20 位、邮箱格式校验、密码最少 6 位、两次密码一致 - 注册成功后自动登录并跳转到 `/` ### 4.4 [Phase 3] 管理后台布局(`/admin/*`) ``` ┌──────────────────────────────────────────────────────────────┐ │ Jimeng Admin [管理员名] [退出] │ ├────────────┬─────────────────────────────────────────────────┤ │ │ │ │ SIDEBAR │ CONTENT AREA │ │ 240px │ (根据子路由渲染不同页面) │ │ │ │ │ ┌──────┐ │ │ │ │ 📊 │ │ │ │ │仪表盘 │ │ │ │ ├──────┤ │ │ │ │ 👥 │ │ │ │ │用户 │ │ │ │ │管理 │ │ │ │ ├──────┤ │ │ │ │ 📋 │ │ │ │ │消费 │ │ │ │ │记录 │ │ │ │ ├──────┤ │ │ │ │ ⚙️ │ │ │ │ │系统 │ │ │ │ │设置 │ │ │ │ └──────┘ │ │ │ │ │ │ ─────── │ │ │ [返回首页] │ │ │ │ │ ├────────────┴─────────────────────────────────────────────────┤ │ Jimeng Clone Admin v3.0 │ └──────────────────────────────────────────────────────────────┘ ``` **设计规范(管理后台专用)**: - 整体风格参考 Linear / Vercel Dashboard,深色主题 - 背景色 `#0a0a0f`,Sidebar 背景 `#111118`,内容区背景 `#0a0a0f` - Sidebar 当前项背景 `rgba(255, 255, 255, 0.08)`,文字 `#ffffff` - 非当前项文字 `#8a8a9a`,hover 背景 `rgba(255, 255, 255, 0.04)` - 卡片背景 `#16161e`,边框 `1px solid #2a2a38`,圆角 `12px` - 数据加载时显示 Arco Skeleton 骨架屏 ### 4.5 [Phase 3] 仪表盘页面(`/admin/dashboard`) ``` ┌─────────────────────────────────────────────────────────────┐ │ │ │ 仪表盘 [今日] [近7天] [近30天] [自定义]│ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ 总用户数 │ │今日新增 │ │今日消费 │ │本月消费 │ │ │ │ 1,234 │ │ +23 │ │ 4,560s │ │ 89,010s │ │ │ │ ↑12% │ │ ↑15% │ │ ↓5% │ │ ↑8% │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ 消费趋势 (ECharts 折线图) │ │ │ │ │ │ │ │ Y轴: 秒数 ___/\___ │ │ │ │ / \___/\ │ │ │ │ ___/\___/ \___ │ │ │ │ │ │ │ │ X轴: 日期 (3/1 3/5 3/10 3/15 3/20 3/25 3/30) │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ 用户消费排行 Top 10 (ECharts 水平柱状图) │ │ │ │ │ │ │ │ user_a ████████████████████████ 2,340s │ │ │ │ user_b ██████████████████ 1,890s │ │ │ │ user_c ████████████████ 1,560s │ │ │ │ ... │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘ ``` - 仅 `is_staff=True` 的用户可访问 - 统计卡片使用 Arco Card 组件 + 自定义样式 - 折线图使用 ECharts `line` 类型,开启 `tooltip`、`dataZoom` 交互 - 柱状图使用 ECharts `bar` 类型,水平方向,标签显示用户名和秒数 - 时间范围选择器使用 Arco DatePicker.RangePicker - 数据来源:`GET /api/v1/admin/stats` ### 4.6 [Phase 3] 用户管理页面(`/admin/users`) ``` ┌─────────────────────────────────────────────────────────────┐ │ │ │ 用户管理 │ │ │ │ [🔍 搜索用户名/邮箱___________] [状态: 全部 ▼] [刷新] │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ 用户名 │ 邮箱 │ 注册时间 │ 状态 │ 日限额│ │ │ │ │ │ │ │ (秒) │ │ │ │ 月限额 │ 今日消费(秒) │ 本月消费(秒)│ 操作 │ │ │ ├──────────┼──────────────┼──────────┼──────┼──────┤ │ │ │ user_a │ a@test.com │ 3/1 │ ✅ │ 600 │ │ │ │ 6000 │ 123 │ 2345 │[编辑][禁用] │ │ │ ├──────────┼──────────────┼──────────┼──────┼──────┤ │ │ │ user_b │ b@test.com │ 3/5 │ ❌ │ 300 │ │ │ │ 3000 │ 0 │ 0 │[编辑][启用] │ │ │ └──────────┴──────────────┴──────────┴──────┴──────┘ │ │ │ │ 共 56 条 [< 1 2 3 >] │ │ │ └─────────────────────────────────────────────────────────────┘ ``` - 使用 Arco Table 组件(分页、排序) - 搜索使用 Arco Input.Search - 状态筛选使用 Arco Select - 编辑配额使用 Arco Modal 弹窗 - 用户详情使用 Arco Drawer 右侧抽屉 - 数据来源:`GET /api/v1/admin/users` ### 4.7 [Phase 3] 消费记录页面(`/admin/records`) ``` ┌─────────────────────────────────────────────────────────────┐ │ │ │ 消费记录 [导出 CSV] │ │ │ │ [用户名搜索____] [时间: 2026-03-01 ~ 2026-03-12] [查询] │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ 时间 │ 用户名 │ 消费秒数│ 视频描述 │ │ │ │ │ │ │ (prompt截断) │ │ │ │ 模式 │ 状态 │ │ │ ├───────────────────┼────────┼────────┼─────────────┤ │ │ │ 3/12 14:30:00 │ user_a │ 15s │ 一只猫在... │ │ │ │ 全能参考 │ 已完成 │ │ │ ├───────────────────┼────────┼────────┼─────────────┤ │ │ │ 3/12 14:25:00 │ user_b │ 5s │ 日落海边... │ │ │ │ 首尾帧 │ 生成中 │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ 共 1,234 条 [< 1 2 3 ... 62 >] │ │ │ └─────────────────────────────────────────────────────────────┘ ``` - 使用 Arco Table 组件(分页) - 时间范围筛选使用 Arco DatePicker.RangePicker - 导出 CSV:前端调用 API 获取全部数据并生成 CSV 文件下载 - 数据来源:`GET /api/v1/admin/records` ### 4.8 [Phase 3] 系统设置页面(`/admin/settings`) ``` ┌─────────────────────────────────────────────────────────────┐ │ │ │ 系统设置 │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ 全局默认配额 │ │ │ │ │ │ │ │ 默认每日限额 (秒): [____600____] │ │ │ │ 默认每月限额 (秒): [____6000___] │ │ │ │ │ │ │ │ [保存配额设置] │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ 系统公告 [启用公告: ON] │ │ │ │ │ │ │ │ ┌──────────────────────────────────────────────┐ │ │ │ │ │ │ │ │ │ │ │ 公告内容 (支持纯文本) │ │ │ │ │ │ │ │ │ │ │ └──────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ [保存公告] │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘ ``` - 使用 Arco Form、InputNumber、Switch、Input.TextArea 组件 - 保存后 showToast 提示「设置已保存」 - 数据来源:`GET/PUT /api/v1/admin/settings` ### 4.9 [Phase 3] 用户个人中心(`/profile`) ``` ┌─────────────────────────────────────────────────────────────┐ │ │ │ [← 返回首页] 个人中心 [用户名] [退出] │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ 消费概览 │ │ │ │ │ │ │ │ ┌────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ │ │ (环形图)│ │ 今日额度 │ │ 本月额度 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ 已用 │ │ 已用: 123s │ │ 已用: 2345s │ │ │ │ │ │ 345s │ │ 限额: 600s │ │ 限额: 6000s │ │ │ │ │ │ /600s │ │ ████████░░░ │ │ ████░░░░░░░ │ │ │ │ │ │ │ │ 20.5% │ │ 39.1% │ │ │ │ │ └────────┘ └──────────────┘ └──────────────┘ │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ 消费趋势 [近7天] [近30天] │ │ │ │ ___/\___/\___ (Sparkline 迷你折线图) │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ 消费记录 │ │ │ │ │ │ │ │ 3/12 14:30 │ 15s │ 一只猫在花园... │ 全能参考 │ 完成│ │ │ │ 3/12 14:25 │ 5s │ 日落海边散步... │ 首尾帧 │ 完成│ │ │ │ 3/12 13:00 │ 10s │ 城市夜景延时... │ 全能参考 │ 失败│ │ │ │ ... │ │ │ │ │ │ │ │ [加载更多] │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘ ``` - 环形进度条使用 ECharts `gauge` 类型(半环或全环) - 进度条使用 Arco Progress 组件 - 消费记录列表使用 Arco List 或 Table 组件,支持「加载更多」分页 - Sparkline 迷你图使用 ECharts `line` 类型(无坐标轴,仅曲线) - 数据来源:`GET /api/v1/profile/overview` + `GET /api/v1/profile/records` - 深色主题风格与主应用一致 ## 5. API 设计 ### 5.1 [已有] 视频生成(Phase 3 修改配额返回字段) ``` POST /api/v1/video/generate Content-Type: multipart/form-data Authorization: Bearer Request: { "prompt": string, "mode": "universal" | "keyframe", "model": "seedance_2.0" | "seedance_2.0_fast", "aspect_ratio": "16:9" | "9:16" | "1:1" | "21:9" | "4:3" | "3:4" | "auto", "duration": 5 | 10 | 15, "references": File[], "first_frame": File | null, "last_frame": File | null } Response: 202 Accepted { "task_id": "uuid", "status": "queued", "estimated_time": 120, "seconds_consumed": 15, ← [Phase 3] 本次消耗秒数 "remaining_seconds_today": 345 ← [Phase 3] 今日剩余秒数 } Error Response: 429 Too Many Requests { "error": "quota_exceeded", "message": "您今日的生成额度已用完", ← [Phase 3] 改为秒数描述 "daily_seconds_limit": 600, "daily_seconds_used": 600, "reset_at": "2026-03-13T00:00:00+08:00" } ``` ### 5.2 [已有] 文件上传(不变) ``` POST /api/v1/upload Content-Type: multipart/form-data Authorization: Bearer Request: { "file": File, "type": "image" | "video" } Response: 200 OK { "file_id": "uuid", "url": "https://cdn.example.com/...", "thumbnail_url": "https://cdn.example.com/.../thumb.jpg", "width": 1920, "height": 1080, "duration": 10.5 } ``` ### 5.3 [已有] 用户注册(不变) ``` POST /api/v1/auth/register Content-Type: application/json Request: { "username": "string (3-20字符)", "email": "string (合法邮箱)", "password": "string (最少6位)" } Response: 201 Created { "user": { "id": 1, "username": "johndoe", "email": "john@example.com" }, "tokens": { "access": "eyJ...", "refresh": "eyJ..." } } Error Response: 400 Bad Request { "username": ["该用户名已被注册"], "email": ["该邮箱已被注册"] } ``` ### 5.4 [已有] 用户登录(不变) ``` POST /api/v1/auth/login Content-Type: application/json Request: { "username": "string (用户名或邮箱)", "password": "string" } Response: 200 OK { "user": { "id": 1, "username": "johndoe", "email": "john@example.com", "is_staff": false }, "tokens": { "access": "eyJ...", "refresh": "eyJ..." } } Error Response: 401 Unauthorized { "error": "invalid_credentials", "message": "用户名或密码错误" } ``` ### 5.5 [已有] Token 刷新(不变) ``` POST /api/v1/auth/token/refresh Content-Type: application/json Request: { "refresh": "eyJ..." } Response: 200 OK { "access": "eyJ..." } ``` ### 5.6 [已有] 获取当前用户信息(Phase 3 修改配额字段) ``` GET /api/v1/auth/me Authorization: Bearer Response: 200 OK { "id": 1, "username": "johndoe", "email": "john@example.com", "is_staff": false, "quota": { "daily_seconds_limit": 600, ← [Phase 3] 改为秒数 "daily_seconds_used": 123, "monthly_seconds_limit": 6000, "monthly_seconds_used": 2345 } } ``` ### 5.7 [Phase 3] 管理后台 — 仪表盘统计 ``` GET /api/v1/admin/stats?period=30d Authorization: Bearer (requires is_staff=True) Query Parameters: period: "today" | "7d" | "30d" | "custom" start_date: "2026-03-01" (当 period=custom 时必填) end_date: "2026-03-12" (当 period=custom 时必填) Response: 200 OK { "total_users": 1234, "new_users_today": 23, "seconds_consumed_today": 4560, "seconds_consumed_this_month": 89010, "today_change_percent": -5.0, "month_change_percent": 8.0, "daily_trend": [ {"date": "2026-02-11", "seconds": 3200}, {"date": "2026-02-12", "seconds": 4100}, ... ], "top_users": [ { "user_id": 1, "username": "user_a", "seconds_consumed": 2340 }, ... ] } ``` ### 5.8 [Phase 3] 管理后台 — 用户列表 ``` GET /api/v1/admin/users?page=1&page_size=20&search=&status=&sort_by=created_at&order=desc Authorization: Bearer (requires is_staff=True) Query Parameters: page: int (默认 1) page_size: int (默认 20,最大 100) search: string (按用户名或邮箱模糊搜索) status: "active" | "disabled" | "" (空表示全部) sort_by: "created_at" | "seconds_today" | "seconds_month" (排序字段) order: "asc" | "desc" Response: 200 OK { "total": 56, "page": 1, "page_size": 20, "results": [ { "id": 1, "username": "user_a", "email": "a@test.com", "is_active": true, "date_joined": "2026-03-01T10:00:00+08:00", "daily_seconds_limit": 600, "monthly_seconds_limit": 6000, "seconds_today": 123, "seconds_this_month": 2345 }, ... ] } ``` ### 5.9 [Phase 3] 管理后台 — 用户详情 + 消费记录 ``` GET /api/v1/admin/users/:id Authorization: Bearer (requires is_staff=True) Response: 200 OK { "id": 1, "username": "user_a", "email": "a@test.com", "is_active": true, "is_staff": false, "date_joined": "2026-03-01T10:00:00+08:00", "daily_seconds_limit": 600, "monthly_seconds_limit": 6000, "seconds_today": 123, "seconds_this_month": 2345, "seconds_total": 5678, "recent_records": [ { "id": 101, "created_at": "2026-03-12T14:30:00+08:00", "seconds_consumed": 15, "prompt": "一只猫在花园里追蝴蝶", "mode": "universal", "model": "seedance_2.0", "status": "completed" }, ... ] } ``` ### 5.10 [Phase 3] 管理后台 — 修改用户配额 ``` PUT /api/v1/admin/users/:id/quota Authorization: Bearer (requires is_staff=True) Content-Type: application/json Request: { "daily_seconds_limit": 900, "monthly_seconds_limit": 9000 } Response: 200 OK { "user_id": 1, "username": "user_a", "daily_seconds_limit": 900, "monthly_seconds_limit": 9000, "updated_at": "2026-03-12T14:30:00+08:00" } ``` ### 5.11 [Phase 3] 管理后台 — 启用/禁用用户 ``` PATCH /api/v1/admin/users/:id/status Authorization: Bearer (requires is_staff=True) Content-Type: application/json Request: { "is_active": false } Response: 200 OK { "user_id": 1, "username": "user_a", "is_active": false, "updated_at": "2026-03-12T14:30:00+08:00" } ``` ### 5.12 [Phase 3] 管理后台 — 消费记录列表 ``` GET /api/v1/admin/records?page=1&page_size=20&search=&start_date=&end_date= Authorization: Bearer (requires is_staff=True) Query Parameters: page: int (默认 1) page_size: int (默认 20,最大 100) search: string (按用户名搜索) start_date: "2026-03-01" (起始日期) end_date: "2026-03-12" (结束日期) Response: 200 OK { "total": 1234, "page": 1, "page_size": 20, "results": [ { "id": 101, "created_at": "2026-03-12T14:30:00+08:00", "user_id": 1, "username": "user_a", "seconds_consumed": 15, "prompt": "一只猫在花园里追蝴蝶", "mode": "universal", "model": "seedance_2.0", "aspect_ratio": "16:9", "status": "completed" }, ... ] } ``` ### 5.13 [Phase 3] 管理后台 — 系统设置 ``` GET /api/v1/admin/settings Authorization: Bearer (requires is_staff=True) Response: 200 OK { "default_daily_seconds_limit": 600, "default_monthly_seconds_limit": 6000, "announcement": "系统将于今晚 22:00 进行维护", "announcement_enabled": true } PUT /api/v1/admin/settings Authorization: Bearer (requires is_staff=True) Content-Type: application/json Request: { "default_daily_seconds_limit": 600, "default_monthly_seconds_limit": 6000, "announcement": "系统将于今晚 22:00 进行维护", "announcement_enabled": true } Response: 200 OK { "default_daily_seconds_limit": 600, "default_monthly_seconds_limit": 6000, "announcement": "系统将于今晚 22:00 进行维护", "announcement_enabled": true, "updated_at": "2026-03-12T14:30:00+08:00" } ``` ### 5.14 [Phase 3] 用户个人中心 — 消费概览 ``` GET /api/v1/profile/overview?period=7d Authorization: Bearer Query Parameters: period: "7d" | "30d" (趋势数据的时间范围) Response: 200 OK { "daily_seconds_limit": 600, "daily_seconds_used": 123, "monthly_seconds_limit": 6000, "monthly_seconds_used": 2345, "total_seconds_used": 5678, "daily_trend": [ {"date": "2026-03-06", "seconds": 45}, {"date": "2026-03-07", "seconds": 120}, {"date": "2026-03-08", "seconds": 0}, {"date": "2026-03-09", "seconds": 88}, {"date": "2026-03-10", "seconds": 200}, {"date": "2026-03-11", "seconds": 156}, {"date": "2026-03-12", "seconds": 123} ] } ``` ### 5.15 [Phase 3] 用户个人中心 — 消费记录 ``` GET /api/v1/profile/records?page=1&page_size=20 Authorization: Bearer Query Parameters: page: int (默认 1) page_size: int (默认 20) Response: 200 OK { "total": 45, "page": 1, "page_size": 20, "results": [ { "id": 101, "created_at": "2026-03-12T14:30:00+08:00", "seconds_consumed": 15, "prompt": "一只猫在花园里追蝴蝶", "mode": "universal", "model": "seedance_2.0", "aspect_ratio": "16:9", "status": "completed" }, ... ] } ``` ## 6. 数据模型 ### 6.1 已有前端 Store 类型(保留) ```typescript // 创作模式 type CreationMode = 'universal' | 'keyframe'; // 模型选项 type ModelOption = 'seedance_2.0' | 'seedance_2.0_fast'; // 宽高比 type AspectRatio = '16:9' | '9:16' | '1:1' | '21:9' | '4:3' | '3:4'; // 时长 type Duration = 5 | 10 | 15; // 上传文件 interface UploadedFile { id: string; file: File; type: 'image' | 'video'; previewUrl: string; label: string; } // 输入栏状态 Store interface InputBarStore { mode: CreationMode; setMode: (mode: CreationMode) => void; model: ModelOption; setModel: (model: ModelOption) => void; aspectRatio: AspectRatio; setAspectRatio: (ratio: AspectRatio) => void; duration: Duration; setDuration: (duration: Duration) => void; prompt: string; setPrompt: (prompt: string) => void; references: UploadedFile[]; addReference: (file: File) => void; removeReference: (id: string) => void; clearReferences: () => void; firstFrame: UploadedFile | null; lastFrame: UploadedFile | null; setFirstFrame: (file: File | null) => void; setLastFrame: (file: File | null) => void; canSubmit: () => boolean; switchMode: (mode: CreationMode) => void; submit: () => void; reset: () => void; } ``` ### 6.2 已有下拉菜单配置(保留) ```typescript interface DropdownOption { label: string; value: string; icon?: string; disabled?: boolean; } const generationTypes: DropdownOption[] = [ { label: '视频生成', value: 'video', icon: 'video' }, { label: '图片生成', value: 'image', icon: 'image' }, ]; const modelOptions: DropdownOption[] = [ { label: 'Seedance 2.0', value: 'seedance_2.0', icon: 'diamond' }, { label: 'Seedance 2.0 Fast', value: 'seedance_2.0_fast', icon: 'diamond' }, ]; const modeOptions: DropdownOption[] = [ { label: '全能参考', value: 'universal', icon: 'sparkle' }, { label: '首尾帧', value: 'keyframe', icon: 'swap' }, ]; const aspectRatioOptions: DropdownOption[] = [ { label: '16:9', value: '16:9' }, { label: '9:16', value: '9:16' }, { label: '1:1', value: '1:1' }, { label: '21:9', value: '21:9' }, { label: '4:3', value: '4:3' }, { label: '3:4', value: '3:4' }, ]; const durationOptions: DropdownOption[] = [ { label: '5s', value: '5' }, { label: '10s', value: '10' }, { label: '15s', value: '15' }, ]; ``` ### 6.3 [Phase 2] 前端 Auth Store 类型(Phase 3 修改配额字段) ```typescript interface User { id: number; username: string; email: string; is_staff: boolean; } // [Phase 3] 配额字段改为秒数 interface Quota { daily_seconds_limit: number; // 原 daily_limit daily_seconds_used: number; // 原 daily_used monthly_seconds_limit: number; // 原 monthly_limit monthly_seconds_used: number; // 原 monthly_used } interface AuthStore { // 状态 user: User | null; accessToken: string | null; refreshToken: string | null; isAuthenticated: boolean; isLoading: boolean; // 操作 login: (username: string, password: string) => Promise; register: (username: string, email: string, password: string) => Promise; logout: () => void; refreshAccessToken: () => Promise; fetchUserInfo: () => Promise; // 配额 quota: Quota | null; fetchQuota: () => Promise; } ``` ### 6.4 [Phase 3] 前端新增类型 ```typescript // 管理后台统计数据 interface AdminStats { total_users: number; new_users_today: number; seconds_consumed_today: number; // 原 calls_today seconds_consumed_this_month: number; // 原 calls_this_month today_change_percent: number; month_change_percent: number; daily_trend: { date: string; seconds: number }[]; top_users: { user_id: number; username: string; seconds_consumed: number }[]; } // 管理后台用户列表项 interface AdminUser { id: number; username: string; email: string; is_active: boolean; date_joined: string; daily_seconds_limit: number; monthly_seconds_limit: number; seconds_today: number; seconds_this_month: number; } // 管理后台消费记录 interface AdminRecord { id: number; created_at: string; user_id: number; username: string; seconds_consumed: number; prompt: string; mode: CreationMode; model: ModelOption; aspect_ratio: string; status: 'queued' | 'processing' | 'completed' | 'failed'; } // 系统设置 interface SystemSettings { default_daily_seconds_limit: number; default_monthly_seconds_limit: number; announcement: string; announcement_enabled: boolean; } // 用户个人中心概览 interface ProfileOverview { daily_seconds_limit: number; daily_seconds_used: number; monthly_seconds_limit: number; monthly_seconds_used: number; total_seconds_used: number; daily_trend: { date: string; seconds: number }[]; } // 用户消费记录 interface ProfileRecord { id: number; created_at: string; seconds_consumed: number; prompt: string; mode: CreationMode; model: ModelOption; aspect_ratio: string; status: 'queued' | 'processing' | 'completed' | 'failed'; } // 分页响应 interface PaginatedResponse { total: number; page: number; page_size: number; results: T[]; } ``` ### 6.5 后端数据模型(Django Models) #### [Phase 2 → Phase 3 修改] 用户模型 ```python # backend/apps/accounts/models.py from django.contrib.auth.models import AbstractUser from django.db import models class User(AbstractUser): """扩展用户模型 — Phase 3: 配额单位改为秒数""" email = models.EmailField(unique=True, verbose_name='邮箱') # [Phase 3] 改为秒数限制 daily_seconds_limit = models.IntegerField(default=600, verbose_name='每日秒数上限') monthly_seconds_limit = models.IntegerField(default=6000, verbose_name='每月秒数上限') created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间') class Meta: verbose_name = '用户' verbose_name_plural = '用户' ``` > **迁移说明**: 需要创建 Django migration 将 `daily_limit` → `daily_seconds_limit`,`monthly_limit` → `monthly_seconds_limit`,并将现有数据按「原次数 × 15」换算为秒数(假设平均每次生成 15 秒)。 #### [Phase 2 → Phase 3 修改] 生成记录模型 ```python # backend/apps/generation/models.py import uuid from django.db import models from django.conf import settings class GenerationRecord(models.Model): """视频生成记录 — Phase 3: 新增消费秒数字段""" MODE_CHOICES = [ ('universal', '全能参考'), ('keyframe', '首尾帧'), ] MODEL_CHOICES = [ ('seedance_2.0', 'Seedance 2.0'), ('seedance_2.0_fast', 'Seedance 2.0 Fast'), ] STATUS_CHOICES = [ ('queued', '排队中'), ('processing', '生成中'), ('completed', '已完成'), ('failed', '失败'), ] user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='generation_records', verbose_name='用户' ) task_id = models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='任务ID') prompt = models.TextField(blank=True, verbose_name='提示词') mode = models.CharField(max_length=20, choices=MODE_CHOICES, verbose_name='创作模式') model = models.CharField(max_length=30, choices=MODEL_CHOICES, verbose_name='模型') aspect_ratio = models.CharField(max_length=10, verbose_name='宽高比') duration = models.IntegerField(verbose_name='视频时长(秒)') seconds_consumed = models.FloatField(default=0, verbose_name='消费秒数') # [Phase 3] 新增 status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='queued', verbose_name='状态') created_at = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='创建时间') class Meta: verbose_name = '生成记录' verbose_name_plural = '生成记录' ordering = ['-created_at'] indexes = [ models.Index(fields=['user', 'created_at']), ] ``` > **消费秒数逻辑**: `seconds_consumed` = 视频 `duration`(5/10/15秒),在调用生成 API 时自动设置。 #### [Phase 2 → Phase 3 修改] 配额配置模型 ```python # backend/apps/generation/models.py (续) class QuotaConfig(models.Model): """全局配额配置 — Phase 3: 改为秒数""" default_daily_seconds_limit = models.IntegerField(default=600, verbose_name='默认每日秒数上限') default_monthly_seconds_limit = models.IntegerField(default=6000, verbose_name='默认每月秒数上限') announcement = models.TextField(blank=True, default='', verbose_name='系统公告') # [Phase 3] 新增 announcement_enabled = models.BooleanField(default=False, verbose_name='启用公告') # [Phase 3] 新增 updated_at = models.DateTimeField(auto_now=True) class Meta: verbose_name = '系统配置' verbose_name_plural = '系统配置' def save(self, *args, **kwargs): # 单例模式,确保只有一条记录 self.pk = 1 super().save(*args, **kwargs) ``` ## 7. 非功能需求 ### 性能要求 - 首屏加载 < 2s(Vite 构建,代码分割) - 文件上传后缩略图预览 < 200ms(使用 URL.createObjectURL 本地生成) - 下拉菜单展开/收起动画 < 150ms - 文本输入无感知延迟(非受控组件或 debounce) - [Phase 2] 后端 API 响应时间 < 500ms(除文件上传外) - [Phase 2] JWT Token 验证 < 50ms - [Phase 3] ECharts 图表渲染 < 300ms(含数据加载) - [Phase 3] 管理后台页面切换 < 200ms(路由级代码分割) - [Phase 3] 分页列表请求 < 300ms ### 安全要求 - 文件上传仅限 image/* 和 video/* MIME 类型 - 文件大小限制:图片 < 20MB,视频 < 100MB - 使用 URL.createObjectURL 生成预览,组件卸载时调用 URL.revokeObjectURL 释放内存 - [Phase 2] 密码使用 Django 默认的 PBKDF2 算法加密存储 - [Phase 2] JWT Access Token 有效期 2 小时,Refresh Token 有效期 7 天 - [Phase 2] 数据库密码等敏感配置通过环境变量管理(生产环境),开发环境可硬编码在 settings 中 - [Phase 2] API 接口对未认证请求返回 401,对权限不足返回 403 - [Phase 2] 管理接口仅 `is_staff=True` 用户可访问 - [Phase 3] 管理后台操作(修改配额、禁用用户)需记录操作日志 - [Phase 3] CSV 导出需要防止 CSV 注入攻击(prompt 字段可能包含特殊字符) ### 响应式设计要求 - 桌面端(≥1024px):InputBar 最大宽度 900px 居中;管理后台 Sidebar 240px + 内容区自适应 - 平板端(768px-1023px):InputBar 宽度 90% 居中;管理后台 Sidebar 折叠为图标模式 - 移动端(<768px):InputBar 宽度 95%,工具栏按钮文字隐藏仅显示图标;管理后台 Sidebar 隐藏,使用汉堡菜单 ### 浏览器兼容 - Chrome 90+、Firefox 90+、Safari 15+、Edge 90+ ## 8. 验收标准 > **重要**: Phase 1 和 Phase 2 功能已完成。当前验收范围为 **Phase 3 全部功能**。Phase 1/2 功能保持不变,不重复验收。 ### Phase 1 验收标准(已通过) #### P0(已全部通过) 1. 页面打开后显示深色全屏背景 `#0a0a0f`,底部居中显示 InputBar 2. InputBar 样式与参考截图视觉一致:背景 `#16161e`、边框 `#2a2a38`、圆角 `20px` 3. 默认处于「全能参考」模式,显示 [+ 参考内容] 上传按钮 + 提示词输入框 4. 点击 [+ 参考内容] 可选择图片/视频文件,上传后显示带序号的缩略图 5. 上传 1-5 张文件后缩略图正确显示,每个有 × 关闭按钮可删除 6. 切换到「首尾帧」模式后,上传区变为首帧 ↔ 尾帧双框布局 7. 工具栏所有按钮正确显示,布局与参考截图一致 8. 发送按钮状态正确:无内容时灰色,有内容时蓝色 `#00b8e6` #### P1(已全部通过) 9. 「视频生成」下拉、「模型选择」下拉、「模式切换」下拉均可正常展开和选择 10. 比例选择按钮点击弹出 6 个选项,选中后按钮文字更新 11. 时长选择按钮点击弹出 3 个选项,选中后按钮文字更新 12. 切换模式时联动正确:全能参考→首尾帧时比例变为「自动匹配」、时长变为 5s、隐藏 @ 按钮 13. 文件上传支持拖拽 #### P2(已全部通过) 14. 下拉菜单有动画过渡效果 15. `Ctrl/Cmd + Enter` 可触发发送 16. 页面加载后文本输入框自动聚焦 17. 移动端下工具栏按钮自适应 ### Phase 2 验收标准(已通过) #### P0(已全部通过) 18. 未登录用户访问 `/` 自动跳转到 `/login` 19. 注册页表单验证正确,注册成功后自动登录跳转到首页 20. 登录页输入正确凭据后成功登录,获取 JWT Token 21. 后端 Django 服务正常启动,能连接 MySQL 数据库 22. 发送按钮点击后调用后端 API,后端记录调用并返回剩余配额 #### P1(已全部通过) 23. 管理员登录后可访问管理后台,普通用户无法访问 24. 管理后台正确显示用户总数、调用统计等指标 25. 管理员可修改用户的每日/每月调用限额 26. 超出限额时前端显示友好提示,后端返回 429 状态码 27. Django Admin 后台(`/admin/`)可管理用户和生成记录 ### Phase 3 验收标准(当前迭代) #### P0(必须全部通过) 28. 所有「调用次数」展示改为「生成秒数」—— UserInfoBar 显示「剩余 Ns/Ns(日)」而非「剩余 N 次」 29. 后端 User 模型字段 `daily_limit`/`monthly_limit` 迁移为 `daily_seconds_limit`/`monthly_seconds_limit` 30. GenerationRecord 模型新增 `seconds_consumed` 字段,生成 API 返回 `seconds_consumed` 和 `remaining_seconds_today` 31. 管理后台使用左侧 Sidebar + 右侧内容区布局,Sidebar 包含 4 个导航项(仪表盘/用户管理/消费记录/系统设置) 32. `/admin/dashboard` 仪表盘页面显示 4 个统计卡片(总用户、今日新增、今日消费秒数、本月消费秒数)+ ECharts 消费趋势折线图 + 用户消费排行柱状图 33. `/admin/users` 用户管理页面支持分页列表、搜索筛选、编辑配额、启用/禁用用户 34. `/profile` 用户个人中心显示消费概览(环形进度条)+ 消费记录列表 #### P1 35. `/admin/records` 消费记录页面显示所有用户消费明细,支持时间范围筛选和导出 CSV 36. `/admin/settings` 系统设置页面支持修改全局默认配额和管理系统公告 37. 用户个人中心显示消费趋势 Sparkline 迷你图(近 7天/30天 切换) 38. 仪表盘统计卡片显示环比变化百分比 + 趋势箭头 39. 用户管理页面点击用户名展开详情抽屉,显示用户详情 + 近期消费记录 40. 管理后台深色主题与 Linear/Vercel Dashboard 风格一致 41. 数据加载使用骨架屏(Skeleton) #### P2 42. 管理后台 Sidebar 支持折叠为图标模式 43. 页面切换有 fade/slide 过渡动画 44. 当日额度消费超过 80% 时用户端显示黄色警告提示 ## 9. 模式切换联动逻辑 | 切换动作 | 上传区 | 比例 | 时长 | @ 按钮 | Placeholder | |---------|--------|------|------|--------|-------------| | → 全能参考 | [+ 参考内容] 多文件上传 | 恢复用户之前选择(默认 21:9) | 恢复用户之前选择(默认 15s) | 显示 | "上传1-5张参考图或视频,输入文字,自由组合图、文、音、视频多元素,定义精彩互动。" | | → 首尾帧 | [首帧] ↔ [+ 尾帧] | 自动匹配(灰色不可选) | 5s(可切换) | 隐藏 | "输入描述,定义首帧到尾帧的运动过程" | > **注意**: 切换模式时不清空已输入的提示词文本,但上传的文件会被清空(因为两种模式的上传逻辑不同)。 ## 10. 设计规范(Design Token) ### 颜色 | Token | 值 | 用途 | |-------|-----|------| | `--color-bg-page` | `#0a0a0f` | 页面背景 | | `--color-bg-input-bar` | `#16161e` | InputBar 背景 | | `--color-border-input-bar` | `#2a2a38` | InputBar 边框 | | `--color-primary` | `#00b8e6` | 主强调色(发送按钮、视频生成文字) | | `--color-text-primary` | `#ffffff` | 主文字 | | `--color-text-secondary` | `#8a8a9a` | 次文字(工具栏按钮、placeholder) | | `--color-text-disabled` | `#4a4a5a` | 禁用文字 | | `--color-bg-hover` | `rgba(255, 255, 255, 0.06)` | 按钮 hover 背景 | | `--color-bg-dropdown` | `#1e1e2a` | 下拉菜单背景 | | `--color-bg-upload` | `rgba(255, 255, 255, 0.04)` | 上传区背景 | | `--color-border-upload` | `#2a2a38` | 上传区虚线边框 | | `--color-btn-send-disabled` | `#3a3a4a` | 发送按钮禁用状态 | | `--color-btn-send-active` | `#00b8e6` | 发送按钮激活状态 | ### [Phase 3] 管理后台专用颜色 | Token | 值 | 用途 | |-------|-----|------| | `--color-bg-sidebar` | `#111118` | Sidebar 背景 | | `--color-sidebar-active` | `rgba(255, 255, 255, 0.08)` | Sidebar 当前项背景 | | `--color-sidebar-hover` | `rgba(255, 255, 255, 0.04)` | Sidebar hover 背景 | | `--color-bg-card` | `#16161e` | 卡片/面板背景 | | `--color-border-card` | `#2a2a38` | 卡片边框 | | `--color-success` | `#00b894` | 正向指标(↑绿色) | | `--color-danger` | `#e74c3c` | 负向指标(↓红色) / 禁用状态 | | `--color-warning` | `#f39c12` | 警告提示(额度 80%+) | ### 圆角 | Token | 值 | 用途 | |-------|-----|------| | `--radius-input-bar` | `20px` | InputBar 容器 | | `--radius-btn` | `8px` | 工具栏按钮 | | `--radius-send-btn` | `50%` | 发送按钮(圆形) | | `--radius-thumbnail` | `8px` | 缩略图 | | `--radius-dropdown` | `12px` | 下拉菜单 | | `--radius-card` | `12px` | [Phase 3] 管理后台卡片 | ### 尺寸 | Token | 值 | 用途 | |-------|-----|------| | `--input-bar-max-width` | `900px` | InputBar 最大宽度 | | `--send-btn-size` | `36px` | 发送按钮直径 | | `--thumbnail-size` | `80px` | 上传缩略图尺寸 | | `--toolbar-height` | `44px` | 工具栏行高 | | `--toolbar-btn-height` | `32px` | 工具栏按钮高度 | | `--sidebar-width` | `240px` | [Phase 3] Sidebar 宽度 | | `--sidebar-collapsed-width` | `64px` | [Phase 3] Sidebar 折叠宽度 | ## 11. 组件树结构 ``` App ├── AuthProvider // [Phase 2] 认证上下文 ├── Router // [Phase 2] 路由 │ ├── /login → LoginPage // [Phase 2] 登录页 │ ├── /register → RegisterPage // [Phase 2] 注册页 │ ├── / → ProtectedRoute // [Phase 2] 需要登录 │ │ └── VideoGenerationPage │ │ ├── UserInfoBar // [Phase 2] 顶部用户信息 + [Phase 3] 秒数配额 + 个人中心链接 │ │ ├── PageBackground │ │ └── InputBar │ │ ├── InputArea │ │ │ ├── UploadSection │ │ │ │ ├── UniversalUpload │ │ │ │ │ ├── UploadButton │ │ │ │ │ └── ThumbnailGrid │ │ │ │ │ └── ThumbnailItem │ │ │ │ └── KeyframeUpload │ │ │ │ ├── FrameUpload │ │ │ │ ├── ArrowIcon │ │ │ │ └── FrameUpload │ │ │ └── PromptInput │ │ └── Toolbar │ │ ├── GenerationTypeDropdown │ │ ├── ModelSelector │ │ ├── ModeDropdown │ │ ├── AspectRatioSelector │ │ ├── DurationSelector │ │ ├── AtButton │ │ ├── FlexSpacer │ │ └── SendButton │ ├── /profile → ProtectedRoute // [Phase 3] 用户个人中心 │ │ └── ProfilePage │ │ ├── ProfileHeader // 返回首页 + 用户信息 │ │ ├── ConsumptionOverview // 消费概览卡片 │ │ │ ├── EChartsGauge // 环形进度条 │ │ │ ├── DailyQuotaCard // 今日额度进度条 │ │ │ └── MonthlyQuotaCard // 本月额度进度条 │ │ ├── ConsumptionTrend // Sparkline 迷你趋势图 │ │ └── ConsumptionRecordList // 消费记录列表 │ ├── /admin → ProtectedRoute (requireAdmin) // [Phase 3] 管理后台 │ │ └── AdminLayout // Sidebar + Content 布局 │ │ ├── AdminSidebar // 左侧导航栏 │ │ │ ├── SidebarLogo // Logo │ │ │ ├── SidebarNav // 导航菜单 │ │ │ │ ├── NavItem (仪表盘) │ │ │ │ ├── NavItem (用户管理) │ │ │ │ ├── NavItem (消费记录) │ │ │ │ └── NavItem (系统设置) │ │ │ └── SidebarFooter // 返回首页链接 │ │ └── AdminContent // 右侧内容区 (Outlet) │ │ ├── /admin/dashboard → DashboardPage │ │ │ ├── StatsCards // 4 个指标卡片 │ │ │ ├── TrendLineChart // ECharts 折线图 │ │ │ └── TopUsersBarChart // ECharts 柱状图 │ │ ├── /admin/users → UsersPage │ │ │ ├── UserSearchBar // 搜索 + 筛选 │ │ │ ├── UserTable // Arco Table │ │ │ ├── QuotaEditModal // 配额编辑弹窗 │ │ │ └── UserDetailDrawer // 用户详情抽屉 │ │ ├── /admin/records → RecordsPage │ │ │ ├── RecordFilters // 筛选条件 │ │ │ ├── RecordTable // Arco Table │ │ │ └── ExportButton // 导出 CSV │ │ └── /admin/settings → SettingsPage │ │ ├── QuotaSettingsCard // 全局配额表单 │ │ └── AnnouncementCard // 公告管理 │ └── * → Navigate to / ``` ## 12. 后端项目结构 ``` backend/ ├── manage.py ├── requirements.txt ├── config/ # Django 项目配置 │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ ├── wsgi.py │ └── asgi.py ├── apps/ │ ├── accounts/ # 用户认证模块 │ │ ├── __init__.py │ │ ├── models.py # User 扩展模型 (Phase 3: 秒数字段) │ │ ├── serializers.py # 注册/登录序列化器 │ │ ├── views.py # 注册/登录/Token刷新 API │ │ ├── urls.py │ │ └── admin.py # 用户管理 Admin │ └── generation/ # 视频生成模块 │ ├── __init__.py │ ├── models.py # GenerationRecord (Phase 3: +seconds_consumed) + QuotaConfig (Phase 3: +announcement) │ ├── serializers.py # [Phase 3] 新增管理后台 + 个人中心序列化器 │ ├── views.py # [Phase 3] 新增管理后台 API (stats/users/records/settings) + 个人中心 API (overview/records) │ ├── urls.py # [Phase 3] 新增路由 │ ├── admin.py # 生成记录 Admin │ └── middleware.py # 配额检查中间件 (Phase 3: 改为秒数检查) ``` ## 13. 参考截图说明 参考截图存放于 `/Users/maidong/Desktop/zyc/研究openclaw/视频生成平台/` 目录: | 文件 | 内容描述 | |------|---------| | `20260311-154443.jpeg` | **空状态全貌** — 完整展示了 InputBar 在无输入时的样式 | | `20260311-154432.jpeg` | **已上传状态** — 展示了上传 1 张图片后的 InputBar | | `20260311-154407.jpeg` | **有内容状态** — 展示了输入完整提示词 + 多张图片引用后的 InputBar | ## 14. 修订历史 | 日期 | 版本 | 变更内容 | |------|------|---------| | 2026-03-11 | v1.0 | 初始版本 — 纯前端视频生成输入界面 | | 2026-03-12 | v2.0 | 增量迭代 — 新增 Django 后端、用户认证系统、后台管理系统 | | 2026-03-12 | v2.1 | **需求修订(BUG-002)** — 引入开发阶段划分(Phase 1 / Phase 2),验收标准按阶段分组 | | 2026-03-12 | v2.2 | **需求修订(BUG-002 后续)** — Phase 2 功能已完成开发,更新阶段状态和验收范围 | | 2026-03-12 | v3.0 | **重大迭代 — Phase 3** — 计量单位从「调用次数」改为「生成秒数」;管理后台从单页面重做为多页面 Sidebar 布局(仪表盘/用户管理/消费记录/系统设置);新增用户个人中心(/profile)含消费概览、环形进度条、消费趋势 Sparkline、消费记录列表;引入 ECharts 图表库;管理后台深色主题升级(参考 Linear/Vercel 风格);新增骨架屏加载和页面过渡动画;新增 8 个 API 端点、修改 3 个已有 API 端点 |