diff --git a/docs/tasks/T-581.md b/docs/tasks/T-581.md index ebb9319..813de6c 100644 --- a/docs/tasks/T-581.md +++ b/docs/tasks/T-581.md @@ -23,22 +23,27 @@ created: 2026-07-10 | 状态 | 打开目录 | | --- | --- | -| 选中了某行 | `///`(该行任务对应账号的图片,直达图片) | +| 选中了某行 | 优先打开该行已有 `new_cover_path` / `old_cover_path` 的真实父目录;若没有已有图片路径,再回退到 `///`(该行任务对应账号的图片,直达图片) | | 未选行、批次筛选=具体批次 | `//`(整批,含各账号子文件夹,供整批打包) | | 未选行、批次筛选=全部批次 | `/`(所有批次根) | -- 选中多行时取第一/当前行的账号 slug(单一目录,不同时开多个)。 -- slug 目录路径复用 `image_paths.task_image_path(image_root, task, account, "new")` 取 `os.path.dirname(...)`,保证与图片实际落盘位置一致;账号解析复用②既有 account 解析(`_account_for_task`/`account_by_alias` 同款)。 +- 选中多行时以表格当前焦点行(`currentIndex/currentRow`)为准;若当前行无效,再取 selectionModel 的第一条选中行。只打开一个目录,不同时打开多个目录。 +- 选中行有历史图片路径时,优先打开真实存在图片文件的父目录,避免老数据或路径结构调整后“有图但打开空目录”。如果已有路径不存在或为空,再用规范路径回退。 +- slug 目录路径复用 `image_paths.task_image_path(image_root, task, account, "new")` 取 `os.path.dirname(...)`,保证与新图片实际落盘位置一致;账号解析复用②既有 account 解析(`_account_for_task`/`account_by_alias` 同款)。 +- 若选中行缺少账号 alias、或 alias 找不到账号,不能猜测 slug;直接中文提示「选中任务没有匹配账号,无法定位图片文件夹」,不打开、不创建目录。 +- 未选行时只按批次筛选级联:具体批次打开 `//`,全部批次打开 `/`,不需要账号解析。 ### `app/gui/tabs/generate.py` — 按钮与布局 - 新增 `self.open_image_dir_button = QPushButton("打开图片文件夹")`,放在**筛选行**(`filter_layout`)紧挨 `refresh_button` 右侧(`generate.py:895` 后)。理由:级联依据(批次筛选、选中行)都在这一行,且它是只读导航动作,不与「开始生成/停止/重置生成结果」这类消耗点数/改数据的执行按钮混排。 - tooltip:「选中某行→打开该账号图片文件夹;未选中→打开当前批次文件夹;全部批次→打开图片根目录」。 -- 点击 handler:按上表算出目标目录 → 若目录**不存在**:弹提示(如「该批次/账号还没有图片」),**不自动创建目录**、不打开 → 存在则调用跨平台打开 helper。 +- 点击 handler:按上表算出目标目录 → 若目录**不存在**:弹提示(如「该批次/账号还没有图片」或「图片根目录不存在,请先采集或生成图片」),**不自动创建目录**、不打开 → 存在则调用跨平台打开 helper。 ### 跨平台打开 helper(可 mock) -- 新增小工具(如 `app/gui/widgets.py` 或合适模块)`open_in_file_manager(path)`:Windows 用 `os.startfile(path)`;其他平台用 `subprocess.Popen(["xdg-open", path])`/macOS `open`(防御性,产品面向 Windows)。 +- 新增小工具,优先放在独立模块 `app/gui/file_manager.py`,提供 `open_in_file_manager(path)`,便于单测 mock 且不把系统调用塞进通用控件模块。 +- `open_in_file_manager(path)` 先把路径转为绝对路径并确认 `os.path.isdir(path)`;目录不存在时抛出可中文化处理的错误,不负责创建目录。 +- Windows 用 `os.startfile(path)`;macOS 用 `subprocess.Popen(["open", path], stdout=DEVNULL, stderr=DEVNULL, shell=False)`;Linux/其它平台用 `subprocess.Popen(["xdg-open", path], stdout=DEVNULL, stderr=DEVNULL, shell=False)`。不拼 shell 字符串、不用 `shell=True`,避免路径空格/特殊字符问题;测试环境 mock 掉真实调用。 - handler 调用该 helper,单测通过 `mock.patch` 断言「用正确目标路径调用了 helper」,**不真正打开**(Linux CI/WSL 不炸)。 ### 文档同步 @@ -47,11 +52,13 @@ created: 2026-07-10 ## 验收要点 -- 选中某行 → 用 `///`(= 该行 new 图 dirname)调用打开 helper。 +- 选中某行 → 若已有图片路径存在,优先用该图片父目录调用打开 helper;否则用 `///`(= 该行 new 图 dirname)调用。 +- 多选时以当前焦点行打开,不因 selectionModel 返回顺序导致打开到非当前行。 +- 选中行账号无法匹配 → 中文提示,不调用打开 helper、不猜路径。 - 未选行 + 批次筛选具体批次 → 用 `//` 调用。 - 未选行 + 全部批次 → 用 `/` 调用。 - 目标目录不存在 → 弹提示、**不调用打开 helper、不创建目录**。 -- 打开 helper 被 mock:单测断言目标路径正确、平台分支正确;不真实执行 `os.startfile`。 +- 打开 helper 被 mock:单测断言目标路径正确、平台分支正确;不真实执行 `os.startfile` / `open` / `xdg-open`。 - 按钮位于筛选行、不在生成/重置按钮排;tooltip 说明级联。 - 生成运行中按钮仍可用(只读动作,不必禁用)。 - 验证命令(unittest,不引入 pytest): @@ -68,6 +75,7 @@ created: 2026-07-10 - 不改画廊弹窗(T-567)、生成/重置链路、批次筛选逻辑本身。 - 不自动创建缺失目录、不删除任何文件。 - 不改 ③/①/CDP/DB/cmhub。 +- 不把打开目录失败写成任务失败,不修改任务状态、批次状态或图片路径字段。 ## 执行记录