Files
cmshoppe/docs/tasks/T-567.md
T

5.9 KiB
Raw Blame History

id, title, phase, deps, status, created
id title phase deps status created
T-567 ②封面画廊基础:候选图查看、单选保存、原图弹窗 7
T-566
DONE 2026-07-09

问题 / 背景

运营需要在软件内对比同一商品多次 AI 生图的候选,并把其中一张设为当前生效封面。当前②列表双击打开的「封面对照」弹窗只显示旧封面 + 当前新封面各一张,无法看到历史候选,也不能保存选中的历史图。

T-566 已提供数据层能力:重置封面时归档旧图、db.update_generated_cover() 切换当前封面指针、image_paths.list_task_cover_candidates() 枚举候选图。本任务只做 GUI 第一阶段:画廊查看 + 单选保存 + 原图弹窗。

方案(改哪个文件、改成什么)

app/gui/tabs/generate.py — 重构封面对照弹窗

  • 将现有 show_task_images 改为封面画廊弹窗(可拆成独立 QDialog 类,如 CoverGalleryDialog)。
  • 布局:
    • 左侧保留「旧封面」参照,推荐缩略图约 300x300;
    • 右侧为候选画廊:横向 QScrollArea,每个候选项包含缩略图、QRadioButton、时间/当前标记;
    • 底部按钮第一阶段只保留「保存」「取消」;不做「重新生成」「上一条/下一条」。
  • 尺寸:
    • 默认 resize(min(960x580, 屏幕 availableGeometry 的 90%));
    • 不用 setFixedSize(),拉伸增量给画廊区域;
    • 小屏下窗口标题栏必须完整可见。

候选加载与默认选中

  • 打开弹窗时调用 T-566 list_task_cover_candidates(image_root, task, account) 现场扫目录。
  • 默认选中 = DB 当前 task.new_cover_path 指向的文件;不是无条件选最新。
  • 如果 DB 当前路径不存在但候选列表不空:
    • 默认不自动改库;
    • 画廊显示候选但不预选,状态 label 提示“当前生效封面文件不存在,请选择一张后保存”。
  • 如果无候选图:
    • 右侧显示空状态“暂无生成封面图片”;
    • 「保存」置灰。
  • 缩略图加载必须用 QImage 读入内存后转 QPixmap,不长期持有文件句柄,避免后续重置/重生时 Windows 文件锁影响归档改名。

保存

  • 选中项与 DB 当前 new_cover_path 相同:直接关闭。
  • 选中项不同:调用 db.update_generated_cover(task_id, selected_path);刷新②列表该行;关闭弹窗。
  • 若该任务 committed=1 或 stage='applied',保存前必须弹确认:
    • 文案说明“本地换封面不会回滚蝦皮,重复更新会再次提交线上”;
    • 用户取消则不写库、不关闭。
  • 保存失败(文件不存在、DB 错误等)应弹中文错误并保留弹窗,不吞异常。

原图弹窗

  • 双击任一候选缩略图打开原图弹窗:
    • QDialog + QScrollArea 展示原始尺寸图片;
    • 窗口最大不超过屏幕 availableGeometry 的 90%,超出可滚动;
    • 标题显示图片文件名和原始分辨率,如 1024x1024。

验收要点

  • 双击②任务列表仍能打开封面查看入口;旧封面仍作为参照显示。
  • 画廊列出该任务全部 _new* 候选,不显示 _old。
  • 默认选中 DB 当前 new_cover_path;DB 当前路径缺失时不自动改库。
  • 选择另一张并保存会调用 db.update_generated_cover(),②列表新封面路径刷新;未变更直接关闭。
  • 已提交线上任务保存前弹二次确认;取消不写库。
  • 双击缩略图可打开原图弹窗,超出屏幕可滚动,标题含分辨率。
  • 缩略图加载不持文件句柄;Windows 下打开画廊后仍可重命名候选文件。
  • 验证命令(unittest,不引入 pytest):
    • py -3.10 -m unittest tests.test_gui tests.test_db tests.test_image_paths
    • python -m ruff check app tests main.py
    • py -3.10 -m compileall app main.py
    • py -3.10 -m unittest discover -s tests
    • git diff --check

边界(不改什么)

  • 不做单任务重新生成(T-568)。
  • 不做上一条/下一条切换和未保存三态拦截(T-569)。
  • 不改 T-566 的数据层契约。
  • 不改 GenerateWorker、generate_batch、cmhub 请求链路。
  • 不改 ③更新/Excel 回写/CDP/Shopee。

执行记录

  • 2026-07-09 完成 T-567。
  • 修改 app/gui/tabs/generate.py:
    • 将②任务列表双击封面查看入口重构为 CoverGalleryDialog,左侧显示旧封面参照,右侧横向滚动画廊显示该任务全部 _new*.jpg 候选。
    • 画廊打开时调用 T-566 的 image_paths.list_task_cover_candidates() 现场扫描目录;默认选中 DB 当前 new_cover_path,当前路径缺失时不自动改库并提示选择后保存;无候选时显示空状态并禁用保存。
    • 保存时未变更直接关闭;变更时调用 db.update_generated_cover(),保存成功后刷新②任务列表。已提交线上任务保存前弹中文二次确认,说明本地换封面不会回滚蝦皮、重复更新会再次提交线上。
    • 缩略图和旧封面预览统一用 QImage 读入内存后转 QPixmap,不长期持有文件句柄;候选缩略图双击打开 OriginalImageDialog 原图弹窗,标题显示文件名与原始分辨率,超出屏幕时可滚动。
  • 修改 app/gui/widgets.py:补充 QImage、QButtonGroup、QRadioButton 聚合导入,供画廊弹窗使用。
  • 修改 tests/test_gui.py:
    • 覆盖候选图展示和 _old 过滤、当前封面默认选中、打开画廊后候选文件可重命名、保存切换当前封面、已提交任务取消确认不写库、当前路径缺失/无候选提示和禁用保存、原图弹窗分辨率标题。
  • 验证通过:
    • py -3.10 -m unittest tests.test_gui
    • py -3.10 -m unittest tests.test_gui tests.test_db tests.test_image_paths
    • python -m ruff check app tests main.py
    • py -3.10 -m compileall app main.py
    • py -3.10 -m unittest discover -s tests(290 tests;PySide6 字体目录警告不影响结果)
    • git diff --check