Files

113 lines
7.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: T-638
title: 商品套图卖点输入自动保存与自适应高度
phase: 7
deps: [T-637]
status: DONE
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生成、③更新蝦皮、④账号管理或⑤设置。
## 执行记录
- 2026-07-16:⑥商品套图为每个 `SuiteTaskState` 增加独立的 500ms 单次保存定时器和最近成功保存快照;连续输入只写最终内容,定时回调按 `state.key` 找回原任务,不依赖届时显示中的任务。切换任务、关闭任务、关闭程序、切换项目、创建项目和开始本地工作前会停止待执行定时并同步补保存。
- 2026-07-16:未创建项目时卖点只保存在当前任务内存,不因输入文字创建临时草稿;创建正式项目或临时草稿时把当前卖点写入 `draft_prompt`。AI 帮写结果继续通过统一项目持久化入口立即保存,失败时保留内存文本并显示中文 danger 状态。
- 2026-07-16:新增 `AutoHeightPlainTextEdit`,关闭内部横向和纵向滚动条,按每个实际换行的文本行高度动态增高;空内容保持 96px,删除内容可缩回,宽度变化时重新计算,由现有 `suiteConfigScroll` 统一承载页面滚动。同步更新 `docs/routes.md`。
- 2026-07-16:`tests/test_product_suite_gui.py` 新增 6 项场景测试,覆盖防抖去重、任务切换/关闭不串写、无项目不建草稿、保存失败、AI 帮写保存和自适应高度。定向 25 项通过;隔离工作树全量 524 项 unittest、Ruff、compileall、`git diff --check` 全部通过。当前主工作区全量测试仅有 3 项原有封面默认模板 `papa1` 改名导致的失败,该未提交用户改动未纳入本任务。