diff --git a/docs/tasks/T-638.md b/docs/tasks/T-638.md new file mode 100644 index 0000000..a55deb7 --- /dev/null +++ b/docs/tasks/T-638.md @@ -0,0 +1,107 @@ +--- +id: T-638 +title: 商品套图卖点输入自动保存与自适应高度 +phase: 7 +deps: [T-637] +status: TODO +created: 2026-07-16 +--- + +## 问题 / 背景 + +⑥「商品套图」的「商品卖点与要求」输入框当前存在两个体验问题: + +1. `textChanged` 只把文本更新到当前 `SuiteTaskState.prompt`,不会立即写入 SQLite。只有修改生成设置、开始生成等后续动作碰巧调用 `_persist_state()` 时才会保存;用户仅修改文本后直接关闭任务、关闭软件,内容可能丢失。 +2. 输入框当前固定在约 `96~112px` 高度,内容较多时依赖内部竖向滚动条。左侧配置区已经由外层 `suiteConfigScroll` 统一滚动,继续保留输入框内部滚动会形成嵌套滚动,运营用户不容易判断是否还有未看到的内容。 + +本任务把卖点文本改为可靠的防抖自动保存,并让输入框根据完整文本高度自然向下展开。 + +## 方案 + +### 1. 卖点文本使用 500ms 防抖自动保存 + +修改 `app/gui/tabs/product_suite.py`: + +- `prompt_edit.textChanged` 后继续立即更新当前 `SuiteTaskState.prompt`,同时启动约 `500ms` 的单次防抖保存。 +- 连续输入只在最后一次变更稳定后写入 SQLite,避免每个字符都触发数据库更新。 +- 防抖任务必须绑定触发变更的 `state.key` 或等价稳定标识;定时器触发时不得直接读取届时的 `_displayed_state`,避免用户快速切换商品任务后把文本写入错误项目。 +- 仅当该状态已有 `project_id` 时调用既有 `image_studio.update_project_prompt()` 或等价统一持久化入口。 +- 尚未创建项目时只保存在当前任务内存,不因输入一个字符自动创建临时项目,避免产生大量空草稿;后续添加图片、绑定商品或开始生成并创建项目时,必须把当前文本一并写入。 +- 程序化加载项目文本期间继续受 `_loading` 或信号阻断保护,不得把回填动作识别成用户修改。 +- 保存成功保持安静,不连续弹窗或刷状态栏;保存失败通过现有中文 danger 状态提示,并保留内存文本供用户继续操作。 +- 可记录每个任务最近一次成功保存的文本,内容未变化时不重复写库。 + +### 2. 离开关键路径前同步补保存 + +防抖保存之外,下列操作前同步刷新控件状态并尝试保存已有项目,作为关闭窗口和快速切换时的兜底: + +- 切换套图任务; +- 关闭套图任务; +- 关闭程序; +- 开始生成; +- AI 帮写结果写回后; +- 创建或绑定项目完成后。 + +若有尚未触发的防抖计时,先停止该计时,再保存对应任务,避免之后重复写入或保存错对象。历史生成 job 的 `prompt` 快照语义不变。 + +### 3. 输入框关闭内部滚动并自然增高 + +- 关闭「商品卖点与要求」输入框的横向和纵向滚动条。 +- 保留约 `96px` 的空内容最低高度,删除当前 `112px` 最大高度限制。 +- 根据文本换行后的文档实际高度动态调整控件高度,保证全部内容可见;不能只隐藏滚动条而保留固定高度。 +- 文本增加时向下撑开,删除文本时允许缩回最低高度。 +- 输入框宽度、窗口尺寸、字体或 Windows DPI 变化导致换行变化时重新计算高度。 +- 动态高度计算应包含 frame、contents margin 和少量安全余量,避免最后一行被裁切;高度未变化时不要反复设置几何尺寸。 +- 页面过长时继续由已有外层 `suiteConfigScroll` 承担滚动,不增加新的嵌套滚动区域。 + +实现可在 `app/gui/tabs/product_suite.py` 内增加仅供本页使用的自适应 `QPlainTextEdit` 子类,或放入现有 GUI widgets 模块复用;不为了单一控件引入复杂抽象。 + +### 4. 文档同步 + +- 更新 `docs/routes.md` 中⑥「商品套图」说明:商品卖点会自动保存,输入框随内容自然增高并由外层配置区统一滚动。 +- 如果持久化调用边界有调整,在本任务执行记录中写明,不修改 SQLite schema。 + +## 验收要点 + +- [ ] 已打开正式项目或临时草稿时,修改卖点内容并停止输入约 `500ms` 后,SQLite `draft_prompt` 自动更新。 +- [ ] 连续快速输入只保存最终稳定内容,不按每个字符重复写库。 +- [ ] 修改后立即切换到另一套图任务,原任务内容保存到原项目,不会串写到新任务。 +- [ ] 修改后立即关闭任务或关闭程序,再次打开项目仍能看到修改后的内容。 +- [ ] 尚未创建项目时输入内容不会自动新增临时项目;后续添加图片或开始生成创建项目后,当前内容正确保存。 +- [ ] AI 帮写结果写回输入框后同样自动保存,不需要额外点击按钮。 +- [ ] 空内容时输入框高度不低于约 `96px`;输入多行内容后完整向下展开,不显示内部横向或纵向滚动条。 +- [ ] 删除多行内容后输入框能缩回;窗口变窄导致换行增加时高度重新计算,最后一行不被裁切。 +- [ ] 长内容由 `suiteConfigScroll` 统一滚动,下方套图结构配置仍可访问,页面不存在嵌套滚动。 +- [ ] 自动保存失败时显示中文错误提示,内存中的编辑内容不被清空。 +- [ ] 不影响 T-637 提示词设置弹窗、模板渲染、AI 帮写、生成任务 prompt 快照和历史重试。 + +## 测试 + +- `tests/test_product_suite_gui.py` 覆盖: + - 已有项目防抖自动保存及最终文本; + - 快速切换任务不会串写; + - 关闭前同步保存; + - 无项目输入不自动创建草稿,项目建立后保存; + - AI 帮写写回后的保存路径; + - 输入框内部滚动条关闭; + - 空文本、多行文本、删除文本和宽度变化时的动态高度。 +- 运行: + +```bash +py -3.10 -m unittest tests.test_product_suite_gui +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、项目表字段或历史数据迁移。 +- 不在用户刚输入文本时自动创建临时项目。 +- 不增加显式「保存卖点」按钮,不为每次成功保存弹窗。 +- 不修改 T-637 的全局套图提示词模板文件、占位符、校验器或预览弹窗。 +- 不修改 cmhub 提交、轮询、下载、计费、并发或超时。 +- 不修改 CDP、蝦皮主图拉取、①导入采集、②AI生成、③更新蝦皮、④账号管理或⑤设置。 + +## 执行记录