Files

4.8 KiB
Raw Permalink Blame History

id, title, phase, deps, status, created
id title phase deps status created
T-692 商品套图原图拖放重复项修复 7
T-691
DONE 2026-07-23

问题 / 背景

T-691 为使参考图可以拖到第一位,给每个 QListWidgetItem 添加了 Qt.ItemIsDropEnabled 并继续调用 QListWidget.InternalMove 的默认 dropEvent。在图标列表中,拖到已有图片上会变成“投放到该项”的模型插入/复制语义,而非单纯的行移动;同一商品原图 ID 因此短暂出现两次。

ProductOriginalList 随后把重复的 asset_ids() 传给 image_studio.reorder_original_assets()。服务层的唯一性校验正确阻止了 SQLite 写入,显示“商品原图排序不能包含重复图片”,但用户无法完成主图调整。

方案

1. 受控单项内部移动

  • 真实原图项恢复为可选择、可勾选、可拖动,不再用 ItemIsDropEnabled 把“拖到图片上”交给 Qt 默认复制式模型处理。
  • ProductOriginalList 自行管理内部拖拽源:开始拖拽时记录当前原图项及其 asset ID;拖放时只接受来源为当前列表、且排序未锁定的单项移动。
  • 根据 drop 坐标和目标缩略图中心计算插入行;使用 takeItem() + insertItem() 移动同一个 QListWidgetItem,不调用 Qt 默认 super().dropEvent() 的内部复制路径。
  • 只在移动后最终 asset_ids 与移动前不同、ID 数量与集合数量一致时发出一次 orderChanged;异常、无效位置、跨列表、锁定状态或重复 ID 一律不写库。

2. 既有行为保持

  • 外部文件 URL 拖入继续发送 filesDropped 并导入图片;剪贴板、勾选、预览、删除、悬停删除和主图样式不变。
  • 生成中、拉取 URL 或图片下载未完成时继续拒绝内部排序;完成后可重新拖放。
  • 成功排序仍复用 reorder_originals() 与 SQLite 单事务服务,刷新后首项显示“主图”;服务层的重复 ID 校验保留为最后防线。

3. 自动回归

  • 为列表内部移动增加无需系统原生拖放平台的 Qt 单元回归:第二项移动到首项前后,列表顺序唯一且只发一次 orderChanged。
  • 覆盖向下移动、原地 drop、无拖拽源、锁定状态、外部 URL 拖入和重复 ID 防御;项目级回归验证最终排序同步到 SQLite,首项更新为主图。

验收要点

  • 将任意参考图拖到第一张位置后,列表仅保留每张图片一次,参考图成为“主图”,不再出现重复图片错误。
  • 向其它位置拖动同样只移动原项,不复制项;原地释放不写库、不提示成功。
  • orderChanged 只携带唯一 asset ID 顺序;服务层仍拒绝任何异常重复输入。
  • 外部图片拖入、生成锁定和拉取锁定语义不变。
  • 自动验证:
    • py -3.10 -m unittest tests.test_product_suite_gui tests.test_image_studio tests.test_product_suite
    • py -3.10 -m unittest discover -s tests
    • py -3.10 -m ruff check app tests main.py
    • py -3.10 -m compileall app main.py
    • git diff --check

边界(不改什么)

  • 不改 SQLite schema、图片文件、已生成 job 的主图/参考图快照、提示词、cmhub、Shopee CDP 或①至⑤模块。
  • 不放宽 reorder_original_assets() 的重复 ID 校验,不改变外部文件导入来源和原图上限。
  • 不实现跨商品、跨任务或多项拖放;本任务只处理当前列表单张原图的可靠排序。

执行记录

  • 2026-07-23 完成。
  • 移除 T-691 对真实原图项追加的 ItemIsDropEnabled,避免 Qt 将拖到缩略图上的动作走默认复制/插入路径。ProductOriginalList 现在在 startDrag() 记录单一拖拽项,在内部 dragEnter/dragMove/drop 中只接受本列表来源的移动。
  • 内部 drop 不再调用 super().dropEvent():按目标缩略图和坐标计算插入行,以 takeItem() + insertItem() 移动原 QListWidgetItem;移动前后均检查 asset ID 唯一性,仅顺序实际变化时发送一次 orderChanged。原地释放、无拖拽源、跨列表和已存在异常重复 ID 都不回写排序。
  • 外部文件 URL 拖入继续只发送 filesDropped;拉取或生成中的锁定逻辑、SQLite 服务层重复 ID 最后防线、主图刷新样式和历史 job 快照均未改动。已同步 docs/04-architecture.md 与 docs/routes.md。
  • 新增 GUI 回归覆盖第二张移到第一张、向后移动、原地释放、无拖拽源和已有重复 ID 防御;现有项目级测试继续验证 SQLite 排序、主图标签和外部文件导入隔离。
  • 验证通过:py -3.10 -m unittest tests.test_product_suite_gui tests.test_image_studio tests.test_product_suite(108项)、py -3.10 -m unittest discover -s tests(675项)、py -3.10 -m ruff check app tests main.py、py -3.10 -m compileall app main.py、git diff --check。离屏 PySide6 有字体目录/插件告警,但无测试失败。