# Conflicts:
#	docs/plans/INBOX.md
#	docs/plans/PROGRESS.md
This commit is contained in:
Zhou Weidong committed 2026-10-20 04:23:52 +08:00
commit 923fab80e8
14 files changed
+4237 -13

No files matched your search

+87
View File
@@ -0,0 +1,87 @@
# 2026-10-19 K19 客户端账号中心
## 基线
- ReelShort `develop`: `7d68699`
- FruitServer `develop`: `5c93ba9`
- 只改 ReelShort;不改 FruitServer 业务代码、ReelShortWeb、specs、main。
## 范围
对接 `docs/api-phase1.md` §1.2 的六个个人中心接口:
| 方法 | 路径 | 客户端用途 |
| --- | --- | --- |
| GET | `/app/me` | 资料展示 |
| PUT | `/app/me` | 修改昵称、性别、生日 |
| GET | `/app/me/devices` | 只读登录设备列表 |
| GET | `/app/me/export` | 账号数据摘要与完整 JSON 复制 |
| POST | `/app/me/deactivate` | 申请注销并进入 15 天冷静期 |
| POST | `/app/me/deactivate/cancel` | 撤销待处理注销申请 |
## 接口口径
1. 资料响应仅使用:
`uid/nickname/avatarUrl/gender/countryCode/regionCode/language/timezone/currency/status/maskedTarget/createdAt/lastLoginAt/vipLevel/vipExpireAt`。
`vipLevel` 是整数;时间字段按 `LocalDateTime` 字符串解析。
2. 资料更新只发送本次变更字段:
`nickname/avatarUrl/gender/birthday/language/timezone/regionCode`。
本页只修改 `nickname/gender/birthday`;昵称 trim 后非空且不超过 30 字符,
性别取 `male/female/unknown`,生日格式 `yyyy-MM-dd`。
3. 设备数组只解析并展示
`deviceId/platform/deviceModel/firstSeenAt/lastSeenAt/status`;
`id/userId/deleted` 不展示、不打印。页面只读。
4. 导出按实际结构解析
`{profile, credentials, consents, devices}`,展示资料摘要、凭据、授权记录数和设备数;
“复制完整 JSON”只写系统剪贴板,不写本地文件。
5. 注销请求只发送 `reason`,不采集或上送密码;成功返回 `scheduledAt`。
成功后清本地会话,不调用 logout。撤销接口成功 `data=null`,失败原样透传后端文案。
6. 所有数值和 ID 使用 `jsonToId/jsonToInt`;错误由 `ApiException` 透传到逻辑层。
## 客户端改造
- `lib/core/data/remote/account_api.dart`
- `lib/core/data/models/account_profile.dart`
- `lib/core/data/models/account_device.dart`
- `lib/core/data/models/account_export.dart`
- `lib/core/data/repositories/account_repository.dart`
- `lib/pages/account/` 下四个页面及各自的 binding/logic
- `lib/pages/mine/mine_view.dart` 登录态新增“账号”卡
- `lib/core/data/services/auth_controller.dart` 新增 `applyProfile`
- `lib/app.dart` 注册 `AccountRepository` Provider
- `lib/l10n/app_{en,zh,id}.arb` 与生成文件
- 测试新增 `test/account/`、`test/support/fake_account_repository.dart`
## TDD 顺序
1. 先写 API/模型测试:六条路径、方法、请求体、字符串/数字 ID、缺字段;
运行定向测试确认红灯。
2. 实现 AccountApi、三个模型和 AccountRepository,定向测试转绿。
3. 先写 AuthController.applyProfile 测试:保留令牌、更新昵称/头像并持久化;
再实现。
4. 先写 profile/device/export/deactivate logic 测试:
资料保存回调登录态、注销成功清会话、撤销失败透传文案、导出复制内容;
再实现逻辑层。
5. 先写 widget 测试:我的页入口登录可见、资料回填与保存、设备空态、
注销二次确认、导出加载/失败/重试;再实现页面和入口。
6. 生成 l10n,跑全量门禁;最后一次全量测试与 APK 构建。
## 门禁
```text
dart analyze lib test
flutter test
flutter build apk --debug
```
Flutter 分支:`feature/flutter-k19-account`。
## 风险与待确认
- `GET /app/me` 不返回注销申请状态,UI 不猜测状态;撤销失败展示后端文案。
- 无设备下线接口,设备页只读;`TC-ACCOUNT-10` 的“单点下线”保留为缺口。
- 无头像上传接口,头像只展示;空值使用现有占位。
- 资料响应不含 `birthday`,也没有清空入口。本批生日只能设置,无法回填或清除;
不补后端字段,页面文案明确为本次设置。
- 注销成功后服务端立即吊销全部会话,本地同步清理;撤销入口在重新登录后仍可使用。
- 真机验证由用户执行,本批不提供开发机 curl 或设备侧结果作为证据。
+60
View File
@@ -0,0 +1,60 @@
# 2026-10-19 K20 客户端视觉统一
## 基线
- ReelShort `develop`: `42ce1d1`
- FruitServer `develop`: `810d9fc`
- 只改 ReelShort 视觉与布局;不改业务逻辑、接口、后端、Web、specs、main。
- 保留辣椒红 `AppColors.blue = #C4322A`。
## 统一 Token
新增 `lib/core/theme/app_dimens.dart`:
| Token | 值 |
| --- | --- |
| 页面左右边距 | 16 |
| 卡片圆角 / 内边距 | 16 / 16 |
| 内嵌块圆角 / 内边距 | 12 / 12 |
| 胶囊圆角 | 20 |
| caption / body / list / section / page | 12 / 14 / 15 / 16 / 20 |
| 导航标签 | 11 |
| 详情主标题 | 24 |
| 主按钮高度 | 48;整页主操作 52 |
| 点击区 | 44 × 44 |
| 导航高度 / 图标 | 64 / 22 |
| 进度条高度 / 圆角 | 4 / 4 |
| 账号卡高度 / 图标 / 标签 | 64 / 18 / 11 |
| 观看占位高度 | 114 |
## 改造顺序
1. 加 token 与主题:`AppDimens`、`AppColors.favorite`、深色唯一口径、
按钮最小高度、OutlinedButton 主题、AppBar/状态栏样式。
2. 新增 `AppBottomNav` / `AppBottomNavItem`,替换 home/discover/mine/player fallback。
3. 调整账号入口卡为 64 高、18 图标、11 标签、44 点击区,并回归 K19 首屏测试。
4. 收敛字号下限:业务文案 ≥12,导航 11;清理 9.5/10。
5. 收敛卡片圆角与间距:页面 16、卡片 16/12、区块 16/12。
6. 替换任务书列出的硬编码颜色;收藏红心使用 `AppColors.favorite`。
7. 统一详情、记录、下载进度条为 4 高、4 圆角、blueSoft/white24;播放器拖动条保持金色。
8. 统一页面/区块/列表标题层级与按钮高度,移除 mine 按钮 VisualDensity.compact。
9. 详情加载/失败分支补返回 AppBar,封面顶部加黑色渐变,详情边距 24→16。
10. 点赞/收藏选中态改实心图标;分类和 my tabs 补 selected 语义。
11. HeaderPill / LoginButton / 分类 Chip 点击区补到 44。
12. 列表页底部安全区补齐;观看记录占位高度 114,与真实卡片 padding 一致。
## 测试与门禁
- 新增 `AppDimens` token 测试与 `AppBottomNav` 组件测试。
- 不修改既有测试断言;视觉布局变化导致测试失败时改实现。
- `dart analyze lib test`
- `flutter test`
- `flutter build apk --debug`
## 风险与边界
- 账号卡升高到 64 可能重新挤压“我的”页首屏;必须保持 K19 语言设置和观看历史入口测试通过。
- 保留页面级深色 Theme 包装以降低大范围回归风险,但根 `themeMode` 必须固定 `dark`。
- 不碰 K21 范围:搜索、Android 返回键、播放器切票黑屏、登录后续操作、下拉刷新、
注销状态闭环、日期/时长格式、语言上送。
- 真机验收由用户执行,本批不提供设备侧截图或日志。
+70
View File
@@ -0,0 +1,70 @@
# 2026-10-19 K21 登录页 UI 优化
## 基线
- ReelShort `develop`: `e44fbdf`
- FruitServer `develop`: `af28161`
- 只改 `ReelShort/lib/pages/auth/**`、l10n 与必要的新增测试。
## 布局方案
- 用 `LayoutBuilder + SingleChildScrollView + ConstrainedBox + Center`,
让品牌与卡片在可视区域垂直居中,键盘弹起时仍可滚动。
- 保留 `auth-scroll`,底部 padding 继续使用 `28 + viewInsets.bottom`。
- 返回按钮移到顶部 `Positioned + SafeArea` 浮层,不再占据内容流。
- 内容最大宽度继续为 440,页面左右边距 16。
- 保留主 background glow(alpha ≤0.18),新增独立 Key 的 warm glow(alpha ≤0.06)。
## 品牌与卡片
- 品牌区居中竖排:48×48 splash mark、App 名 26/w800、tagline 14/blueSoft。
- `auth-brand`、`auth-brand-mark` 保留,mark 尺寸严格 48×48。
- 卡片结构保持:模式切换 → 标题 25/w800 → 副标题 → 字段 → 错误胶囊 → 提交。
- 登录继续使用 `authSubtitle`;注册使用新增 `authSignUpSubtitle`。
- 注册态密码框下显示 12px 的 `authPasswordHint`,登录态隐藏。
## 字段错误与交互
- `AuthLogic` 增加 `fieldError`;`validate()` 记录具体错误,输入变化或切换模式时清除。
- 邮箱/密码字段按 `fieldError` 使用 1.5px semantic error `errorBorder` /
`focusedErrorBorder`。
- 保留 `auth-error-capsule`、`auth-error` 的文案、圆角 12 和 alpha 0.1。
- `_submit()` 首行 busy 短路;异步 catch 中的 `setState` 前检查 mounted。
- 密码框 done 提交仍走同一 `_submit()`,busy 时自然忽略。
- 提交按钮增加极淡辣椒红阴影,背景和按下色保持不变。
## 不可变清单
```text
auth-brand-mark 48x48
auth-card radius 20 / border outlineVariantDark / width 1
input height 52 / radius 14 / fill bar / focus blue 1.5
submit height 52 / radius 26 / blue / bluePressed
auth-enter-animation <=220ms / tween 0 -> 1
mode switch keys, hidden compatibility copy
error capsule radius 12 / alpha 0.1 / semantic color
small screen 320x568 + keyboard 260 scroll reachability
```
## TDD
1. 新增测试:注册副标题、注册态密码提示、登录态不显示提示、字段错误红框。
2. 先确认红灯。
3. 补 `authSignUpSubtitle` 三语并生成 l10n。
4. 调整 AuthLogic、表单字段、品牌和页面布局。
5. 跑两份既有 Auth 测试与新增测试,再跑全量门禁。
## 门禁
```text
dart analyze lib test
flutter test
flutter build apk --debug
```
## 风险
- 居中布局与小屏滚动会互相影响,必须保持 `auth-submit` 可滚动到。
- 字段红框不能通过 errorText 增加字段高度,否则会破坏 52 高度断言。
- 品牌改为图片资源后仍必须保持 `auth-brand-mark` 为 48×48。
- 不改接口、后端、Web、specs、main;真机由用户验收。
+56
View File
@@ -0,0 +1,56 @@
# 2026-10-19 K22.1 后台收尾
## 基线
- ReelShortWeb `develop`: `5494190`
- FruitServer `develop`: `2807ae6`
- 只改 ReelShortWeb 的后台视觉与资源引用、构建配置及无引用文件。
- 不改接口契约、后端业务、客户端与 main。
## 登录页
1. 保留 hero 品牌 mark、kicker 与标题。
2. 删除标题下方说明段落及 `pointDrama/pointPublish/pointAccess` 三个标签。
3. 删除不再使用的三语 key 与 hero 列表样式。
4. 删除注册切换、注册表单、入口事件及相关导出和文案。
## 品牌与图标
1. `brand-mark.png` 复制到 `public`,作为浏览器 favicon 与首屏 loading。
2. 首屏背景改为 `#100C0A`,loading mark 增加圆角。
3. 删除 `public/logo.png`、`public/favicon.ico`。
4. 四个 env 均关闭在线图标,由 `PurgeIcons` 本地打包 Material Symbols。
5. 构建后扫描旧前缀、`<el-icon>`、Element Plus 图标引用,并检查浏览器实际图标。
## Mock 清理
1. dev/pro/test 环境关闭 `VITE_USE_MOCK`。
2. 删除 `mock/` 与 `viteMockServe` 配置。
3. 全仓确认不再存在 mock 环境分支和旧 mock 图标来源。
## 文件删除
删除前分别对目标路径执行全仓 grep,确认只命中自身或已同步处理的引用:
- `src/assets/imgs/logo.png`
- `src/assets/imgs/avatar.jpg`
- `public/logo.png`
- `public/favicon.ico`
- `src/views/permission/CustomMenuView.vue`
- `src/views/Login/components/RegisterForm.vue`
- `mock/`
禁止删除仍被路由、菜单、组件或构建配置引用的文件。
## 门禁
- `pnpm lint:eslint`
- `pnpm build:pro`
- 浏览器自查登录页、后台壳、菜单、顶栏、表格工具栏与首屏 loading。
- 控制台无 `/logo.png`、mock 或旧图标资源 404。
## 风险
- 在线图标关闭后必须确认 PurgeIcons 已本地打包全部实际使用图标。
- 删除 mock 后需确认开发环境只依赖真实后台接口。
- 删除文件前先处理所有引用,避免残留 import 或路由失败。
+78
View File
@@ -0,0 +1,78 @@
# 2026-10-19 K22 后台管理视觉与交互统一
## 基线
- ReelShortWeb `develop`: `5c087f4`
- FruitServer `develop`: `b025bfd`
- 只改 Web 视觉/交互与 FruitServer 菜单图标 SQL,不改接口、客户端、main。
## 图标体系
1. 新增 `src/constants/icons.ts`:
- `AppIcons` 保存语义名到 `material-symbols:*-rounded` 的映射;
- `normalizeAppIcon()` 兼容后端旧短名和历史 Iconify 名;
- `MenuIconOptions` 供菜单管理页选择。
2. 新增 `src/components/AppIcon`:
- 复用现有 `Icon` 组件;
- 默认尺寸 18,支持颜色/悬停色;
- 全局注册 `AppIcon`。
3. 替换 `src/components/**`、`src/views/**` 的旧图标前缀与 Element Plus 图标组件。
4. 对 `node_modules/@iconify/json/json/material-symbols.json` 做存在性校验。
任务书要求的 `language/view-column/lock/chevron-down` 在当前 JSON 中没有对应的
`*-rounded` 名,实施时使用同语义且存在的
`translate-rounded/combine-columns-rounded/lock-person-rounded/keyboard-arrow-down-rounded`。
## 菜单落库
更新 `sql/data.sql` 与 `sql/data-phase1.sql` 中 id 1/2/3/6/15/17/20/22/24/26/32:
| 菜单 | 图标 |
| --- | --- |
| 工作台 | `material-symbols:dashboard-rounded` |
| 内容管理 | `material-symbols:video-library-rounded` |
| 后台管理 | `material-symbols:settings-rounded` |
| 用户管理 | `material-symbols:group-rounded` |
| 创作者管理 | `material-symbols:movie-edit-rounded` |
| 内容治理 | `material-symbols:policy-rounded` |
| 订单与权益 | `material-symbols:payments-rounded` |
| 数据看板 | `material-symbols:monitoring-rounded` |
| 创作者中心 | `material-symbols:video-camera-front-rounded` |
| 运营管理 | `material-symbols:campaign-rounded` |
执行两份 SQL,管理员登录后请求 `/admin/auth/menus`,断言所有 icon 以
`material-symbols:` 开头。
## 品牌与登录
- 复制客户端 `splash_mark.png` 为 `src/assets/imgs/brand-mark.png`。
- Logo 组件展开/收起统一使用品牌 mark。
- 删除 admin/admin123 占位、记住密码持久化、固定 `FruitShorts Studio`。
- 记住我只保存用户名;store reset 时删除 loginInfo。
- 表单回车提交、loading 覆盖请求与动态路由、防止重复。
- 960 以下压缩 hero,登录卡首屏完整。
## 全局壳与主题
- 修复深色模式菜单、头部、标签关闭、主题开关、面包屑对比度。
- body 去掉 `min-width: 980px`,补 1200/768 断点。
- 顶栏、标签页、表格工具栏、弹窗统一 AppIcon。
- CSS token:一组字体变量、4/6/12 圆角、`#409eff !important` 改
`var(--el-color-primary)`,删除或合并未使用壳层选择器。
- 用户信息读取 `userStore`,不再使用固定 avatar/Archer。
- 清除缓存增加二次确认。
## 门禁与自查
- 静态扫描:无旧图标前缀、无 `<el-icon>`、无页面 `@element-plus/icons-vue` 引用。
- `node_modules/@iconify/json/json/material-symbols.json` 逐个校验新图标。
- `pnpm lint:eslint` 零错误。
- `pnpm build:pro` 成功。
- 浏览器自查:登录页、侧边栏展开/折叠、顶栏、标签页、浅/深色、1024/800/375。
## 风险
- 图标批量替换可能影响动态图标与右键菜单,保留 normalize 兜底。
- 窄屏下侧栏与固定内容宽度可能冲突,优先保证内容区不裁切。
- 登录 loading 必须覆盖动态路由生成,避免二次提交。
- 本轮不碰表格、筛选、分页、拦截器、删除确认、rules、权限与上传校验。
+32
View File
@@ -0,0 +1,32 @@
# 2026-10-19 K23.1 后台修正
## 基线
- ReelShortWeb `develop`: `fe1ae95`
- FruitServer `develop`: `b8676d0`
- 只改 ReelShortWeb 视觉与交互,不改接口、后端、客户端、main。
## 品牌文字
1. 浅色菜单品牌字与浅色顶栏文字固定为 `#303133`。
2. 处理本地持久化的旧 `inherit` 值,避免刷新后仍继承主色。
3. 检查 Logo、ToolHeader、Breadcrumb 的实际颜色。
## 删除按钮
1. 扫描所有删除、移除、退款、回收、冻结危险按钮。
2. 统一 `type="danger"`、表格内 `size="small"`,禁止 link/plain/text 和浅色 `danger-action`。
3. 删除确认按钮保持 `el-button--danger` 实心红底。
4. 调整过窄操作列,保证按钮不换行。
## 布局回归
1. 1440×900、1024×768、375×812。
2. 检查侧栏品牌、菜单 42px 基线、popper、短剧编辑弹窗、锁屏弹窗和表格操作列。
3. 检查顶栏、标签页、面包屑图标与文字基线。
## 门禁
- `pnpm lint:eslint`
- `pnpm build:pro`
- 浏览器控制台 0 error、0 404,页面无横向溢出。
+41
View File
@@ -0,0 +1,41 @@
# 2026-10-19 K23 后台视觉深化
## 基线
- ReelShortWeb `develop`: `3e8312e`
- FruitServer `develop`: `9c96e18`
- 只改后台视觉/交互与菜单图标种子,不改接口契约、权限和业务流程。
## 主色
1. Element Plus 主色改为辣椒红 `#c4322a`,同步 light-3/5/7/8/9 与 dark-2。
2. 清理后台源码中的默认 Element 蓝与蓝色透明背景。
3. 验证按钮、开关、链接、选中菜单、标签页和焦点环对比度。
## 菜单
1. 一级菜单保留 Material Symbols Rounded 图标;所有二级菜单图标置空。
2. 开发库幂等执行后通过 `/admin/auth/menus` 检查一级图标存在、二级 icon 为空。
3. 菜单管理只允许目录菜单选择图标,保存子菜单时强制清空。
4. 一级菜单行高 44,二级菜单行高 40;父子文字统一落在 42px 基线。
5. 二级菜单不渲染图标、不保留图标空位,折叠态和 popper 同样只显示文字。
## 短剧编辑弹窗
1. 680px 三区块表单:基础信息、付费设置、封面。
2. 表单增加 model/rules,数字输入固定 180px 且控件在右。
3. 封面改为预览卡:basename、状态、更换/移除、类型/大小校验、进度与失败提示、查看原图。
4. 保存按钮防重复,保存中与上传中禁止关闭。
## 锁屏弹窗
1. 440px 紧凑弹窗,移除全屏入口。
2. 重做标题、说明、内容和底部按钮。
3. 支持回车锁定,删除调试日志。
## 门禁
- 图标名逐个对照 `node_modules/@iconify/json/json/material-symbols.json`。
- `pnpm lint:eslint` 零错误。
- `pnpm build:pro` 通过。
- 浏览器自查登录页、工作台、短剧管理、锁屏;1024/375、明暗主题、控制台 0 error、0 404。
+102
View File
@@ -0,0 +1,102 @@
# K24 计划:客户端 UI 对齐设计稿
日期:2026-10-19
客户端基线:`ReelShort/develop` = `b1c69e2`
文档基线:`FruitServer/develop` = `93b2299`
分支:`feature/flutter-k24-design-system`
## 一、范围
本批只做设计系统、3 Tab 底部导航、首页沉浸层、发现页:
- 设计 token:Obsidian Flare 色板、字体、间距/圆角、玻璃面板。
- 底部导航:Home / Discover / Me 三项;My List 不再独立成 Tab,内容并入 Me 页。
- 首页:保留 `PlayerPage(homeMode: true)` 播放链路,只替换叠加层与信息卡。
- 发现页:品牌行、搜索、分类 chips、Continue Watching、4 个快捷入口、2 列 Trending。
不做:详情页、播放器、我的页、登录页的视觉重做(K25/K26);后端、Web、specs、main。
## 二、设计口径
权威来源:`docs/design/README.md` + `screens/01-home.html` + `screens/02-discover.html`。
不采用 `DESIGN.md` 顶部 YAML 的 `#ffb4a5/#cdbdff`。
| 语义 | 值 |
| --- | --- |
| background / ink / bar | `#0B0C10` |
| card | `#16181F` |
| surface-elevated / raised | `#1D1E28` |
| primary / blue | `#FF5B3D` |
| pressed / bluePressed | `#FF4321` |
| secondary / lavender / blueSoft | `#9278EA` |
| gold | `#FBBF24` |
| success | `#34D399` |
| on-surface | `#FFFFFF` / `#E3E2E8` |
| muted | `#A1A1AA` |
| subtle | `#71717A` |
| border | `rgba(255,255,255,0.08)` |
字体:Outfit(标题)、Plus Jakarta Sans(正文),OFL TTF 本地打包,不运行时联网。
下载失败时保留系统字体,并在回执与 PROGRESS 标注未完成项。
## 三、TDD 顺序
1. 设计 token / 主题:先更新 `test/theme/app_theme_test.dart` 期望值与新增 token 断言,再改
`app_colors.dart`、`app_theme.dart`、`app_dimens.dart`。
2. GlassPanel:先写组件测试(blur、渐变、描边、圆角),再实现 `glass_panel.dart`。
3. 3 Tab:先改导航测试为 `tab-home/tab-discover/tab-my`,覆盖选中态、映射、沉浸隐藏;
再改 `app_bottom_nav.dart` 与 `home_view.dart`。
4. 首页:先用现有 Player 测试保证播放/进度/收藏不回归,再新增首页结构断言
(剧名、HOT、右侧互动栏、底部玻璃卡、Follow Series、Episodes)。
5. 发现页:先写搜索过滤、2 列网格、Continue Watching 隐藏/显示测试,再改
`discover_view.dart` / `discover_logic.dart` / `home_drama_grid.dart`。
6. 回归:更新 4 Tab 与轮播挂载相关断言,保留播放、进度、收藏、举报等行为测试。
## 四、影响文件
- `lib/core/theme/app_colors.dart`
- `lib/core/theme/app_theme.dart`
- `lib/core/theme/app_dimens.dart`
- `lib/core/widgets/glass_panel.dart`(新增)
- `lib/core/widgets/app_bottom_nav.dart`
- `lib/pages/home/home_view.dart`
- `lib/pages/home/home_logic.dart`
- `lib/pages/discover/discover_view.dart`
- `lib/pages/discover/discover_logic.dart`
- `lib/pages/discover/widgets/home_drama_grid.dart`
- `lib/pages/player/player_view.dart`(仅 homeMode 叠加层)
- `lib/pages/mine/mine_view.dart`(仅把 My List 区块并入 Me)
- `pubspec.yaml`、`assets/fonts/*`
- 相关 `test/**`
## 五、数据缺口与降级
| 设计字段 | 现有数据 | 处理 |
| --- | --- | --- |
| 评分 | `Drama` 无评分字段 | 隐藏,不伪造 |
| 完结状态 | 无完结字段 | 隐藏 |
| 榜单名次 | 无字段 | 用 `isHot` + `viewCount` 排序展示,不编名次 |
| VIP 徽章 | 无 VIP 字段;`Episode.locked` 仅集级 | 列表不显示 VIP 徽章 |
| Free 徽章 | `freeEpisodes` 仅详情接口 | 有值才显示 |
| 点赞/评论数 | 互动接口无剧级计数 | 只显示图标或文案,不编数字 |
| Continue Watching 集数/剩余时长 | `WatchProgress` 有位置/时长,无集号 | 有记录才显示,集号缺失时省略 |
## 六、风险
- 首页叠加层改动可能影响沉浸模式、PiP、下载入口与既有 tooltip;通过保留原有行为入口并跑
`test/widget_test.dart`、`test/home/**`、`test/player/**` 控制风险。
- 3 Tab 合并会触碰多个导航测试;只调整 Tab 数量/Key 断言,不改播放与收藏断言。
- 新字体增加安装包体积;只打包 Regular/SemiBold/Bold 必要字重。
- 发现页数据源仍用 `CategoryController.visibleDramas`,不新增接口。
## 七、门禁
```bash
flutter pub get
dart analyze lib test
flutter test
flutter build apk --debug
```
提交作者/提交者:`Zhou Weidong <502333560@qq.com>`。
+79
View File
@@ -0,0 +1,79 @@
# K25 计划:客户端详情页 + 播放器对齐设计稿
日期:2026-10-19
基线:`ReelShort/develop` = `4553606`
分支:`feature/flutter-k25-detail-player`
## 一、范围
只改详情页与 `homeMode=false` 的播放器:
- 详情页:16:9 hero、玻璃导航、元数据、简介展开、CTA、选集 chips、2 列剧集卡。
- 卡片播放器:顶部导航、9:10 视频框、进度条、三按钮行、精选评论卡。
- `homeMode=true` 的 K24 首页沉浸 feed 保持现状。
不改:后端接口/模型字段、首页、发现页、我的页、登录页、`playback_session`/ticket 协议。
## 二、设计口径
沿用 K24 Obsidian Flare token、Outfit/Plus Jakarta Sans、`GlassPanel`。
详情页结构:
1. 16:9 hero(当前模型只有 `posterUrl`,作为 hero 图;无 `coverUrl` 字段)。
2. 左侧玻璃返回,右侧分享/收藏/更多(举报在更多菜单)。
3. 集数 pill、24px 标题、Season 副标题、viewCount、题材标签。
4. 16px 简介卡,2 行 + More/Less。
5. 橙色全宽 CTA + 48×48 收藏/下载,续看进度与错误重试保留。
6. Episodes 标题 + 范围 chip(无区间数据时显示 All)+ 46×44 横向集数 chips。
7. 2 列剧集预览卡:缩略图 96×64、Free/Coin、时长、锁态、下载/播放入口。
播放器结构(`homeMode=false`):
1. 顶部返回、剧名 + Ep.x、更多(下载/PiP)。
2. 9:10 圆角 16 视频框,Auto-Next/1.0x pill、中心橙色播放、右侧点赞/全屏。
3. 1.5px 进度条,白色缓冲、橙色播放、白色 thumb;保留拖动 seek。
4. Episodes / Comments / Next Ep 三按钮;末集 Next Ep 置灰。
5. 精选评论卡复用 `CommentController`,无评论隐藏;VIP/总评论数字段缺失时隐藏。
## 三、TDD 顺序
1. 详情 widget 测试:hero 16:9、CTA 行、简介展开、集数 chips、2 列剧集卡。
2. 详情实现:先复用现有 `DramaDetailLogic`,只重排视图和控件。
3. 播放器 widget 测试:卡片模式结构、三按钮行、进度条颜色/thumb、末集禁用。
4. 播放器实现:新增卡片布局,保留原全屏 feed;复用 ticket/session/queue。
5. 回归:PiP、票据错误、进度、收藏、下载、举报、评论、自动连播。
## 四、数据缺口
| 设计字段 | 模型状态 | 处理 |
| --- | --- | --- |
| coverUrl | 无(只有 posterUrl) | hero 使用 posterUrl |
| 评分 | 无 | 隐藏 |
| 完结状态 | 无 | 隐藏 |
| Top 排名 | 无 | 隐藏 |
| Trailer/4K | 无 | 隐藏 |
| VIP | 无 | 隐藏 |
| 剧级点赞/评论总数 | 无 | 只显示文案/已加载数量,不伪造 |
| 场景标签 | 无 | 隐藏 |
| 集数范围 | 无 | 显示 All chip |
| 倍速能力 | 无播放速率接口 | 显示 1.0x,不伪造切换能力 |
## 五、风险
- 卡片播放器切换全屏时复用同一 `PageController`;只在二者之一挂载,避免控制器绑定两个 PageView。
- 自动连播依赖 PageController 动画,卡片模式保留 PageView 作为视频底层。
- 评论卡新增一次评论首屏请求;无评论/失败时直接隐藏,不影响播放。
- 详情页 hero、2 列剧集卡在小屏下需自适应缩略图宽度,避免溢出。
## 六、门禁与 Git
```bash
dart analyze lib test
flutter test
flutter build apk --debug
```
完成后 `--no-ff` 合并回 `develop` 并 push;作者/提交者
`Zhou Weidong <502333560@qq.com>`。真机验收由用户执行。
+83
View File
@@ -0,0 +1,83 @@
# K26 计划:客户端按「移动端设计预览」校正六屏
日期:2026-10-19
基线:`ReelShort/develop` = `eefabb6`
分支:`feature/flutter-k26-mobile-preview`
## 一、范围
按本地 `/Users/jianghaifeng/Desktop/ReelShort/docs/移动端设计预览.html`
校正六屏:首页、发现、详情、播放器、我的、登录。该文件优先于此前 Stitch screens。
保留 K24 的 Outfit / Plus Jakarta Sans、Provider 业务逻辑、现有接口与数据模型。
不改后端、Web、specs、main。
## 二、Token
| 语义 | 值 |
| --- | --- |
| primary | `#FF5B3D` |
| secondary | `#9278EA` |
| background | `#0B0C10` |
| nav | `#0D0D11` |
| card | `#1B1C21` |
| cardAlt | `#1C1D21` |
| input / quick | `#25252A` / `#202126` |
| gold card | `#201B1E` |
| text / dim / accent | `#E6E1E3` / `#D2CDD0` / `#FFA490` |
| border | `white 8%` |
圆角:卡片 10-14、按钮 9、chips 20、封面 10。
新增紧凑字号 token(9/10/11/12/13/14/15/18/20)与按钮高度 token(28/32/34/40/44)。
## 三、六屏
1. 首页:剧名 + 搜索/菜单;Like/Comment/Share/Favorite 四互动;底部玻璃卡
(tags、第 x/共 y 集、13px 标题、34px 追剧/选集);去掉 HOT/Season/Follow。
2. 发现:Logo + 发现 + 搜索/头像;36px 搜索框;分类 chips;52×66 Continue Watching;
4 个 62px 快捷入口;热门短剧标题 + 2 列封面高 120 网格。
3. 详情:190px hero + 28px 返回;N 集、18px 标题、11px 简介、40px 开始观看、选集 chips;
收藏/下载/举报收进更多;删除统计行、预览卡与底部 Tab。
4. 播放器:全屏;46px 顶栏;中心 52px 播放 + 1.0x;3px 进度;32px 选集/评论/下一集;
一条精选评论卡;无右侧互动栏;下载/PiP/互动收进更多。
5. 我的:顶部 Logo + 我的 + 搜索/头像;48px 用户行;金币卡;Continue Watching;
历史/收藏/喜欢;语言行;底部 3 Tab。
6. 登录:64px 渐变 Logo + 水果短剧 + 副标题;Google/手机号/邮箱三个 44px 入口;
Google/手机号置灰提示即将开放,邮箱进入 K21 表单;底部法律文案。
## 四、TDD 顺序
1. Token / AppDimens / GlassPanel 测试。
2. 首页 overlay 结构与底部导航测试。
3. 发现页搜索、Continue Watching、快捷入口、2 列网格测试。
4. 详情页 190px hero、40px CTA、选集 chips 测试。
5. 播放器全屏顶栏、三按钮、精选评论测试。
6. 我的页用户行、金币卡、Continue Watching、语言行测试。
7. 登录入口与邮箱二级表单测试。
8. 回归播放、进度、收藏、下载、PiP、票据错误、评论与导航。
## 五、数据缺口
- 剧级点赞/评论数、已完结、Top 排名、VIP、评分、场景标签无模型字段:隐藏。
- Continue Watching 仅有 `episodeId/position/duration`;集号能从剧集列表匹配时显示。
- 金币余额与 Google/手机号登录后端未就绪:金币显示 0,Google/手机号入口提示即将开放。
- 剧集时长只有 `durationSec`;缺失则不显示。
## 六、风险
- 六屏同时调整会触碰大量既有 widget 测试;只改视觉/布局与入口,不改业务逻辑。
- 登录页改成入口页后,K21 邮箱表单测试需要先点击邮箱入口;表单本身与认证逻辑保持不变。
- 播放器仍复用 ticket/PlaybackSession/PlaybackQueue;只替换非首页的 overlay。
- 卡片圆角与按钮高度变化可能影响小屏布局,用 320×568、360×720、390×844 回归。
## 七、门禁
```bash
dart analyze lib test
flutter test
flutter build apk --debug
```
完成后 `--no-ff` 合并回 `develop` 并 push;作者/提交者
`Zhou Weidong <502333560@qq.com>`。真机验收由用户执行。
+1351
View File
File diff suppressed because it is too large. Load diff
+2180 -1
View File
File diff suppressed because it is too large. Load diff
+11 -8
View File
@@ -92,7 +92,7 @@ ON CONFLICT DO NOTHING;
-- component 指向 src/views/creator/ 下的页面,前端按这个字符串动态加载。
INSERT INTO sys_menu (id, parent_id, title, route_path, route_name, component, redirect, icon,
permission_code, type, always_show, affix, sort) VALUES
(17, 0, '创作者管理', '/creator', 'CreatorCenter', '#', '/creator/creators', 'ep:user-filled', 'creator:list', 'CATALOG', 1, 0, 5),
(17, 0, '创作者管理', '/creator', 'CreatorCenter', '#', '/creator/creators', 'material-symbols:movie-edit-rounded', 'creator:list', 'CATALOG', 1, 0, 5),
(18, 17, '创作者入驻', 'creators', 'creators', 'views/creator/CreatorAuditView', '', '', 'creator:list', 'MENU', 0, 0, 1),
(19, 17, '内容审核', 'submissions', 'submissions', 'views/creator/SubmissionAuditView', '', '', 'submission:list', 'MENU', 0, 0, 2)
ON CONFLICT (id) DO UPDATE SET parent_id = EXCLUDED.parent_id, title = EXCLUDED.title,
@@ -129,8 +129,8 @@ WHERE NOT EXISTS (SELECT 1 FROM product WHERE code = 'coin_1200');
-- component 指向 src/views/userManagement/AppUsersView.vue,前端按这个字符串动态加载。
INSERT INTO sys_menu (id, parent_id, title, route_path, route_name, component, redirect, icon,
permission_code, type, always_show, affix, sort) VALUES
(15, 0, '用户管理', '/user', 'UserCenter', '#', '/user/app-users', 'ep:user', 'app-user:list', 'CATALOG', 1, 0, 4),
(16, 15, 'App 用户', 'app-users', 'app-users', 'views/userManagement/AppUsersView', '', '', 'app-user:list', 'MENU', 0, 0, 1)
(15, 0, '用户管理', '/user', 'UserCenter', '#', '/user/app-users', 'material-symbols:group-rounded', 'app-user:list', 'CATALOG', 1, 0, 4),
(16, 15, 'App 用户', 'app-users', 'app-users', 'views/userManagement/AppUsersView', '', '', 'app-user:list', 'MENU', 0, 0, 1)
ON CONFLICT (id) DO UPDATE SET parent_id = EXCLUDED.parent_id, title = EXCLUDED.title,
route_path = EXCLUDED.route_path, route_name = EXCLUDED.route_name, component = EXCLUDED.component,
redirect = EXCLUDED.redirect, icon = EXCLUDED.icon, permission_code = EXCLUDED.permission_code,
@@ -150,11 +150,11 @@ ON CONFLICT DO NOTHING;
-- component 指向 src/views/governance|commerce|dashboard/ 下的页面,前端按字符串动态加载。
INSERT INTO sys_menu (id, parent_id, title, route_path, route_name, component, redirect, icon,
permission_code, type, always_show, affix, sort) VALUES
(20, 0, '内容治理', '/governance', 'Governance', '#', '/governance/content', 'ep:chat-dot-round', 'comment:list', 'CATALOG', 1, 0, 6),
(20, 0, '内容治理', '/governance', 'Governance', '#', '/governance/content', 'material-symbols:policy-rounded', 'comment:list', 'CATALOG', 1, 0, 6),
(21, 20, '评论与举报', 'content', 'governance-content', 'views/governance/ContentGovernanceView', '', '', 'comment:list', 'MENU', 0, 0, 1),
(22, 0, '订单与权益', '/commerce', 'Commerce', '#', '/commerce/orders', 'ep:coin', 'order:list', 'CATALOG', 1, 0, 7),
(22, 0, '订单与权益', '/commerce', 'Commerce', '#', '/commerce/orders', 'material-symbols:payments-rounded', 'order:list', 'CATALOG', 1, 0, 7),
(23, 22, '订单管理', 'orders', 'commerce-orders', 'views/commerce/OrderManagementView', '', '', 'order:list', 'MENU', 0, 0, 1),
(24, 0, '数据看板', '/board', 'BoardCenter', '#', '/board/overview', 'ep:data-line', 'dashboard:view', 'CATALOG', 1, 0, 8),
(24, 0, '数据看板', '/board', 'BoardCenter', '#', '/board/overview', 'material-symbols:monitoring-rounded', 'dashboard:view', 'CATALOG', 1, 0, 8),
(25, 24, '运营大盘', 'overview', 'board-overview', 'views/dashboard/BusinessDashboardView', '', '', 'dashboard:view', 'MENU', 0, 0, 1)
ON CONFLICT (id) DO UPDATE SET parent_id = EXCLUDED.parent_id, title = EXCLUDED.title,
route_path = EXCLUDED.route_path, route_name = EXCLUDED.route_name, component = EXCLUDED.component,
@@ -253,7 +253,7 @@ INSERT INTO sys_menu (id, parent_id, title, route_path, route_name, component, r
permission_code, type, always_show, affix, sort, visible) VALUES
-- route_name 必须全局唯一:17 号菜单(创作者管理)已经叫 CreatorCenter,
-- 同名会让 Vue Router 后者覆盖前者、整个 /creator-center 子树注册不上(超管点进去就 404)
(26, 0, '创作者中心', '/creator-center', 'CreatorWorkspace', '#', '/creator-center/workspace', 'ep:film', 'creator-center:view', 'CATALOG', 1, 0, 3, 1),
(26, 0, '创作者中心', '/creator-center', 'CreatorWorkspace', '#', '/creator-center/workspace', 'material-symbols:video-camera-front-rounded', 'creator-center:view', 'CATALOG', 1, 0, 3, 1),
(27, 26, '我的作品', 'workspace', 'creator-workspace', 'views/creator-center/WorkspaceView', '', '', 'creator-center:view', 'MENU', 0, 0, 1, 1),
(28, 26, '作品编辑', 'dramas/:id', 'creator-drama-edit', 'views/creator-center/DramaEditView', '', '', 'creator-center:drama:edit', 'MENU', 0, 0, 2, 0),
(29, 26, '收费配置', 'prices', 'creator-prices', 'views/creator-center/PriceConfigView', '', '', 'creator-center:price:update', 'MENU', 0, 0, 3, 1),
@@ -310,7 +310,7 @@ ON CONFLICT DO NOTHING;
INSERT INTO sys_menu (id, parent_id, title, route_path, route_name, component, redirect, icon,
permission_code, type, always_show, affix, sort) VALUES
(31, 22, '商品配置', 'products', 'commerce-products', 'views/commerce/ProductManagementView', '', '', 'product:list', 'MENU', 0, 0, 2),
(32, 0, '运营管理', '/operation', 'OperationCenter', '#', '/operation/notices', 'ep:bell', 'notice:list', 'CATALOG', 1, 0, 9),
(32, 0, '运营管理', '/operation', 'OperationCenter', '#', '/operation/notices', 'material-symbols:campaign-rounded', 'notice:list', 'CATALOG', 1, 0, 9),
(33, 32, '系统公告', 'notices', 'operation-notices', 'views/operation/NoticeManagementView', '', '', 'notice:list', 'MENU', 0, 0, 1)
ON CONFLICT (id) DO UPDATE SET parent_id = EXCLUDED.parent_id, title = EXCLUDED.title,
route_path = EXCLUDED.route_path, route_name = EXCLUDED.route_name, component = EXCLUDED.component,
@@ -333,3 +333,6 @@ INSERT INTO sys_config (id, config_key, config_value, remark) VALUES
(3, 'app.register.max-per-ip-per-day', '0', '0=不限制;生产建议 5'),
(4, 'app.register.max-per-device-per-day', '0', '0=不限制;生产建议 3')
ON CONFLICT (config_key) DO NOTHING;
-- K23:二级菜单统一只显示文字,历史图标值也一并清空。
UPDATE sys_menu SET icon = '' WHERE type = 'MENU';
+7 -4
View File
@@ -50,14 +50,14 @@ ON CONFLICT (id) DO UPDATE SET code = EXCLUDED.code, name = EXCLUDED.name,
-- ---------- 菜单 ----------
INSERT INTO sys_menu (id, parent_id, title, route_path, route_name, component, redirect, icon,
permission_code, type, always_show, affix, sort) VALUES
(1, 0, '工作台', '/dashboard', 'Dashboard', '#', '/dashboard/index', 'ant-design:dashboard-filled', 'drama:list', 'CATALOG', 0, 0, 1),
(2, 1, '工作台', 'index', 'dashboard', 'views/common/DashboardView', '', 'ant-design:dashboard-filled', 'drama:list', 'MENU', 0, 1, 1),
(3, 0, '内容管理', '/content', 'Content', '#', '/content/dramas', 'ep:video-camera', 'drama:list', 'CATALOG', 1, 0, 2),
(1, 0, '工作台', '/dashboard', 'Dashboard', '#', '/dashboard/index', 'material-symbols:dashboard-rounded', 'drama:list', 'CATALOG', 0, 0, 1),
(2, 1, '工作台', 'index', 'dashboard', 'views/common/DashboardView', '', '', 'drama:list', 'MENU', 0, 1, 1),
(3, 0, '内容管理', '/content', 'Content', '#', '/content/dramas', 'material-symbols:video-library-rounded', 'drama:list', 'CATALOG', 1, 0, 2),
(4, 3, '短剧管理', 'dramas', 'dramas', 'views/contentManagement/DramaManagementView', '', '', 'drama:list', 'MENU', 0, 0, 1),
(5, 3, '剧集管理', 'episodes', 'episodes', 'views/contentManagement/EpisodeManagementView', '', '', 'episode:list', 'MENU', 0, 0, 2),
(12, 3, '轮播图管理', 'banners', 'banners', 'views/contentManagement/BannerManagementView', '', '', 'banner:list', 'MENU', 0, 0, 3),
(13, 3, '类型管理', 'categories', 'categories', 'views/contentManagement/CategoryManagementView','', '', 'drama:list', 'MENU', 0, 0, 4),
(6, 0, '后台管理', '/system', 'System', '#', '/system/access', 'ep:setting', 'system:role', 'CATALOG', 1, 0, 3),
(6, 0, '后台管理', '/system', 'System', '#', '/system/access', 'material-symbols:settings-rounded', 'system:role', 'CATALOG', 1, 0, 3),
(7, 6, '角色权限配置', 'access', 'access', 'views/permission/RoleManagementView', '', '', 'system:role', 'MENU', 0, 0, 1),
(8, 6, '管理员用户列表','admin-users','admin-users', 'views/userManagement/AdminUsersView', '', '', 'system:admin-user','MENU', 0, 0, 2),
(9, 6, '菜单管理', 'menus', 'menus', 'views/permission/MenuManagementView', '', '', 'system:menu', 'MENU', 0, 0, 3),
@@ -77,6 +77,9 @@ ON CONFLICT (id) DO UPDATE SET parent_id = EXCLUDED.parent_id, title = EXCLUDED.
redirect = EXCLUDED.redirect, icon = EXCLUDED.icon, permission_code = EXCLUDED.permission_code,
type = EXCLUDED.type, always_show = EXCLUDED.always_show, affix = EXCLUDED.affix, sort = EXCLUDED.sort;
-- K23:二级菜单统一只显示文字,历史图标值也一并清空。
UPDATE sys_menu SET icon = '' WHERE type = 'MENU';
-- ---------- 角色-权限 ----------
INSERT INTO sys_role_permission (id, role_id, permission_id)
SELECT r.id * 1000000 + p.id, r.id, p.id FROM sys_role r CROSS JOIN sys_permission p WHERE r.code = 'super_admin'