zyc ffe92f7b15 Initial commit: 即梦视频生成平台
- web/: React + Vite + TypeScript 前端
- backend/: Django + DRF + SimpleJWT 后端
- prototype/: HTML 设计原型
- docs/: PRD 和设计评审文档
- test: 单元测试 + E2E 极限测试
2026-03-13 09:59:33 +08:00

1612 lines
73 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 产品需求文档 (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. **图表库引入**: 使用 EChartsecharts + 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 FormInputNumberSwitchInput.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 <access_token>
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 <access_token>
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 <access_token>
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 <access_token>
(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 <access_token>
(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 <access_token>
(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 <access_token>
(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 <access_token>
(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 <access_token>
(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 <access_token>
(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 <access_token>
(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 <access_token>
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 <access_token>
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<void>;
register: (username: string, email: string, password: string) => Promise<void>;
logout: () => void;
refreshAccessToken: () => Promise<void>;
fetchUserInfo: () => Promise<void>;
// 配额
quota: Quota | null;
fetchQuota: () => Promise<void>;
}
```
### 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<T> {
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. 非功能需求
### 性能要求
- 首屏加载 < 2sVite 构建代码分割
- 文件上传后缩略图预览 < 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 字段可能包含特殊字符
### 响应式设计要求
- 桌面端(≥1024pxInputBar 最大宽度 900px 居中管理后台 Sidebar 240px + 内容区自适应
- 平板端768px-1023pxInputBar 宽度 90% 居中管理后台 Sidebar 折叠为图标模式
- 移动端<768pxInputBar 宽度 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 端点 |