docs: 初始化 cmshopee 文档、设计与项目骨架
- docs/ 完整 harness coding 文档集(愿景/需求/技术栈/架构/编码规则/任务/api/routes/current-state) - 5 Tab 流水线设计 + UI 效果图 SVG(docs/ui/) - cdp.py CDP 底座;prototypes/ 已验证原型脚本(待 editor.py 移植后清理) - AGENTS.md/CLAUDE.md 入口、progress.md 执行流水、.gitignore(排除凭证/DB/图片) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
# AI 开发入口
|
||||
|
||||
> 给 AI coding agent 的项目入口。这里负责导航和流程,硬性编码规则见 [`05-coding-rules.md`](05-coding-rules.md)。
|
||||
|
||||
## 一句话定位
|
||||
|
||||
cmshopee 是一个 Windows 本地桌面自动化工具(Tkinter,5 Tab),让运营管理多个 Shopee 账号,并用 CDP 驱动 Chrome + AI 批量改商品标题、换商品封面。
|
||||
|
||||
5 Tab 流水线(工作流优先顺序):
|
||||
**① 导入采集 → ② AI生成 → ③ 更新shopee → ④ 账号管理 → ⑤ 设置**
|
||||
|
||||
目标闭环:④ 配账号并登录 → ① 导入 Excel(按“别名”列关联账号)、采集旧标题/旧封面并回写 → ② 用提示词 AI 生成新标题/新封面(无人工确认)→ ③ 对已生成任务改标题+换封面并直接点「更新」提交(无开关)→ 结果实时存 SQLite、批量回写原 Excel。
|
||||
|
||||
存储:应用设置 `config.json`(含 AI Key)+ 业务数据 SQLite `cmshopee.db` + Excel 用 openpyxl + 图片存本地 `images/`。AI 服务商待定。
|
||||
|
||||
## 必读顺序
|
||||
|
||||
每次开始写代码前,按这个顺序建立上下文:
|
||||
|
||||
1. [`01-vision.md`](01-vision.md):为什么做、为谁做、什么不做。
|
||||
2. [`02-requirements.md`](02-requirements.md):MVP 要什么、怎么算达成。
|
||||
3. [`03-tech-stack.md`](03-tech-stack.md):既定技术选型(Python + 自研 CDP + Tkinter 待确认)。
|
||||
4. [`04-architecture.md`](04-architecture.md):模块职责、账号数据模型、**第四节 CDP 已验证事实(重点)**。
|
||||
5. [`05-coding-rules.md`](05-coding-rules.md):写代码前必须遵守的规则。
|
||||
6. [`06-tasks.md`](06-tasks.md):领取本轮唯一任务。
|
||||
7. [`../progress.md`](../progress.md):历史执行记录、验证结果、阻塞点和关键决策。
|
||||
8. [`current-state.md`](current-state.md):当前代码现实、可运行命令、下一步任务。
|
||||
|
||||
如果仓库根目录有 `AGENTS.md`、`CLAUDE.md`,也必须先读,仓库级规则优先。
|
||||
|
||||
## 当前阶段
|
||||
|
||||
当前项目处于:**单账号流程已验证,正扩展多账号管理 + GUI**。
|
||||
|
||||
优先路径:
|
||||
|
||||
1. Phase 0:把已验证流程模块化(`editor.py`),定义账号配置。
|
||||
2. Phase 1:账号绑定 user-data-dir、Chrome 启动、登录保活、加载商品页。
|
||||
3. Phase 2:GUI 跑通单账号改标题 + 换封面闭环。
|
||||
4. Phase 3:批量与多账号并行(V2)。
|
||||
|
||||
## 领取任务规则
|
||||
|
||||
从 [`06-tasks.md`](06-tasks.md) 领取任务时:
|
||||
|
||||
- 只领取第一个状态为 `TODO` 且依赖均为 `DONE` 的任务。
|
||||
- 开始前把该任务状态改为 `DOING`。
|
||||
- 本轮只完成这一个任务。
|
||||
- 验收通过后把状态改为 `DONE`。
|
||||
- 完成后把执行记录追加到 [`../progress.md`](../progress.md),覆盖更新 [`current-state.md`](current-state.md)。
|
||||
- 做完即停,汇报验证结果,等待下一步指令。
|
||||
|
||||
如果代码实际状态和任务看板冲突,先说明冲突,不要擅自跳步。
|
||||
|
||||
## MVP 边界
|
||||
|
||||
MVP 只做:
|
||||
|
||||
- 账号配置(增删改 1..N 个 Shopee 账号)。
|
||||
- 每账号绑定 `chrome_user_data_dir/<slug>` 独立配置目录。
|
||||
- 一键启动 Chrome 供首次人工登录、登录态持久化。
|
||||
- 用某账号打开指定商品详情页,改标题、换封面。
|
||||
- 显式确认后点击「更新」提交(默认不提交)。
|
||||
|
||||
MVP 不做:
|
||||
|
||||
- 自动登录 / 自动填账号密码。
|
||||
- 绕过验证码、风控、限流。
|
||||
- 批量、多账号并行、爬取、数据库(属 V2 及之后)。
|
||||
|
||||
## 事实来源
|
||||
|
||||
项目事实只信:
|
||||
|
||||
- [`04-architecture.md`](04-architecture.md) 第四节:CDP 交互已验证结论(选择器、就绪判断、上传/拖拽方式)。
|
||||
- [`04-architecture.md`](04-architecture.md) 4.1/4.2/4.3:`config.json`、SQLite schema、Excel 模板。
|
||||
- 真实页面探查结果(用 `prototypes/inspect_images.py` / `prototypes/cookies.py` 实地确认)。
|
||||
- 已验证脚本 `cdp.py`、`prototypes/demo.py`、`prototypes/set_title.py`、`prototypes/set_cover.py` 中跑通的逻辑。
|
||||
|
||||
不要把以下当事实来源:
|
||||
|
||||
- 旧脚本里可能已失效的选择器(Shopee 页面会变)。
|
||||
- 未经实测的猜测。
|
||||
- 临时探查脚本(`/tmp` 下的一次性脚本)。
|
||||
|
||||
## 常见任务该看哪里
|
||||
|
||||
做 GUI:
|
||||
|
||||
- 先看 `02-requirements.md` 的对应验收标准。
|
||||
- 再看 `routes.md` 的窗口职责与操作流程。
|
||||
- 最后看 `04-architecture.md` 的模块边界(GUI 不写业务逻辑)。
|
||||
|
||||
做 CDP / 浏览器操作:
|
||||
|
||||
- 先看 `04-architecture.md` 第四节已验证事实。
|
||||
- 再看 `api.md` 的 `cdp` / `editor` 模块合约。
|
||||
- 复用 `cdp.py`,不重写一套。
|
||||
|
||||
做账号配置 / Chrome 启动:
|
||||
|
||||
- 先看 `04-architecture.md` 3.1 数据模型与 `api.md` 的 `config` / `chrome` 合约。
|
||||
- Chrome 启动参数严格按第四节“Chrome 启动参数”一条。
|
||||
|
||||
## 验证命令
|
||||
|
||||
```bash
|
||||
python -m py_compile *.py # 语法检查
|
||||
python prototypes/demo.py # 单账号闭环验证(分步,不提交)
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- 改 CDP / editor 逻辑后:在测试商品(ITEM_ID 51100639510)上跑 `prototypes/demo.py` 实测。
|
||||
- 改账号配置后:验证 SQLite 账号读写与 user-data-dir 创建;改应用设置后验证 `config.json` 读写。
|
||||
- 如果命令当前不可运行(如 GUI 未建),在回复里如实说明。
|
||||
@@ -0,0 +1,44 @@
|
||||
# 项目愿景
|
||||
|
||||
## 一、核心目标
|
||||
|
||||
cmshopee 要解决:**运营要在多个 Shopee 卖家账号下,逐个商品手动改标题、换封面,效率低、易出错、来回切换登录痛苦**。
|
||||
|
||||
> 让**电商运营**能够在一个工具里集中管理多个 Shopee 账号,并自动完成商品标题修改和封面图替换,无需反复登录、手动逐项操作。
|
||||
|
||||
它不是一个通用爬虫,也不是 Shopee 官方 API 客户端,而是一个**驱动已登录的真实 Chrome 浏览器、在卖家中心页面上代为操作**的本地自动化工具。
|
||||
|
||||
## 二、目标用户
|
||||
|
||||
- **电商运营 / 店铺管理员**:管理 1 个或多个 Shopee 卖家账号,需要批量调整商品标题和主图。
|
||||
- **小型卖家团队负责人**:希望把重复的商品维护操作标准化、半自动化。
|
||||
- **本项目维护者(含 AI agent)**:需要在已验证的单账号流程上,扩展出多账号管理与 GUI。
|
||||
|
||||
## 三、产品原则
|
||||
|
||||
遇到取舍时,以这些原则为准:
|
||||
|
||||
- **核心流程优先**:先把「单账号改标题 + 换封面」做稳,再扩展多账号和批量。
|
||||
- **真实环境优先**:操作真实的已登录 Chrome,不伪造登录、不绕过风控。
|
||||
- **小步交付**:每一步都能运行、能验证、能回退;默认不提交线上,显式开关才落库。
|
||||
- **少即是稳**:MVP 不追求全自动,只追求最小可靠闭环;首次登录由人工完成。
|
||||
- **凭证安全**:登录态保存在各账号独立的 Chrome user-data-dir 里,不导出、不外传、不写进代码或文档。
|
||||
|
||||
## 四、核心价值主张
|
||||
|
||||
| 价值点 | 说明 |
|
||||
| --- | --- |
|
||||
| 多账号集中管理 | 一处配置多个 Shopee 账号,各自绑定独立浏览器配置目录,互不串号 |
|
||||
| 免反复登录 | 每个账号的登录态持久化在自己的 user-data-dir,启动即带登录 Cookie |
|
||||
| 自动改标题/换封面 | CDP 驱动浏览器自动改标题、上传新图并拖拽设为封面 |
|
||||
| 可控可回退 | 默认只改不提交,显式确认/开关才点「更新」,演示与试错零风险 |
|
||||
|
||||
## 五、不做什么(非目标)
|
||||
|
||||
- 不做 Shopee 账号的**自动登录 / 自动填账号密码**(首次登录人工完成)。
|
||||
- 不**绕过验证码、风控、限流或平台权限校验**。
|
||||
- 不做商品数据的**批量爬取 / 导出**。
|
||||
- 不做云端服务、数据库、多人协作后台(MVP 为本地单机工具)。
|
||||
- 不接管标题/封面以外的商品字段编辑。
|
||||
|
||||
> MVP 的具体功能范围与验收标准,见 [需求](02-requirements.md)。
|
||||
@@ -0,0 +1,112 @@
|
||||
# 需求
|
||||
|
||||
> 本文只描述**要什么**与**怎么算达成**,用产品 / 用户语言表达,**不涉及技术实现**。
|
||||
> 技术方案、数据结构、CDP 细节见 [架构设计](04-architecture.md)。
|
||||
|
||||
## 一、业务现状
|
||||
|
||||
| 项 | 状态 |
|
||||
| --- | --- |
|
||||
| 用户 | 电商运营,手动在多个 Shopee 卖家账号下逐个改标题、换封面,效率低 |
|
||||
| 数据 | 商品数据在 Shopee 卖家中心;本工具不落库,只在浏览器页面上操作 |
|
||||
| 现有系统 | 已用 CDP 验证单账号流程(改标题、上传图、拖拽换封面、可选更新),见 `current-state.md` |
|
||||
| 约束 | 依赖真实 Chrome 与已登录态;受 Shopee 页面结构、限流、风控影响;首次登录需人工 |
|
||||
|
||||
## 二、用户角色
|
||||
|
||||
- **运营操作员**:配置账号、为账号登录、对选定商品执行改标题/换封面。
|
||||
- **未登录账号**:已在配置中但其 user-data-dir 尚未登录 Shopee;执行任务前必须先人工登录。
|
||||
|
||||
## 三、功能清单
|
||||
|
||||
### 第一版 MVP(最小闭环)
|
||||
|
||||
| 功能 | 用户能做什么 | 优先级 |
|
||||
| --- | --- | --- |
|
||||
| 账号配置 | 在 GUI 里新增/编辑/删除 1 个或多个 Shopee 账号(名称、地区、备注) | P0 |
|
||||
| 绑定配置目录 | 为每个账号在项目 `chrome_user_data_dir/<账号标识>` 下创建并绑定独立 user-data-dir | P0 |
|
||||
| 启动并登录 | 一键用某账号的 user-data-dir 启动 Chrome(带调试参数),首次人工登录 Shopee 后登录态持久化 | P0 |
|
||||
| 生成快捷方式 | 可选:为每账号生成桌面快捷方式,目标带该账号 user-data-dir,供手动打开对应账号 Chrome | P1 |
|
||||
| 加载商品页 | 选定账号,用其已登录 Chrome 打开指定商品详情页 | P0 |
|
||||
| 改标题 | 对该商品按规则修改标题(如去掉结尾若干字 / 指定新标题) | P0 |
|
||||
| 换封面 | 上传一张本地图片,自动设为商品封面(满 9 张时先删原封面再传) | P0 |
|
||||
| 提交更新 | 在显式确认后点击「更新」,把改动提交到线上(默认不提交) | P0 |
|
||||
|
||||
### 第二版(多账号 + Excel + AI,5 Tab 流水线)
|
||||
|
||||
5 个 Tab,顺序:① 导入采集 → ② AI生成 → ③ 更新shopee → ④ 账号管理 → ⑤ 设置。
|
||||
|
||||
| 功能 | 用户能做什么 | 优先级 |
|
||||
| --- | --- | --- |
|
||||
| 账号管理(④) | 增删改账号:账号名/别名/地区/数据目录/端口/密码(加密仅参考)/备注;登录状态;启动登录、检测登录 | P0 |
|
||||
| 导入(①) | 导入多个 Excel,按模板解析输入列(账号名/别名/商品id)为任务列表 | P0 |
|
||||
| 导入校验汇总(①) | 导入后展示文件数、解析行数(原始数据量)、有效/无效行、匹配账号行数(按账号细分)、未匹配行数;跑采集前先纠错 | P0 |
|
||||
| 采集旧数据(①) | 程序只读打开商品页,抓取旧标题、下载旧封面到本地,回写 Excel 旧字段 | P0 |
|
||||
| AI 生成(②) | 输入标题/封面提示词,AI 据提示词+旧标题生成新标题、据提示词+旧封面生成新封面 | P0 |
|
||||
| 提示词管理(②) | 标题提示词「保存」到 `title_prompt.txt` 并启动回显;封面提示词多模板(下拉 + 新建/保存/另存为/重命名/删除)+ 插入 `{新标题}` + 预览(变量替换) | P0 |
|
||||
| 查看对照(②) | 双击任务弹窗查看新旧封面(纯查看,无人工确认);可选对单行重生成 | P0 |
|
||||
| 更新 shopee(③) | 按批次/店铺/状态筛选;对筛选出的已生成任务打开编辑页换标题+封面,并直接点「更新」提交线上(无开关);可按状态=失败重试 | P0 |
|
||||
| 结果存储与回写 | 各阶段结果实时存 SQLite;该文件全部完成后把旧/新数据+状态批量回写原 Excel | P0 |
|
||||
| 设置(⑤) | AI 模型管理(下拉+新增/删除/详情/测试连接,至少各一个文本+图像模型);标题/图片大模型角色选择;分辨率(512/1k/2k/4k,返回超时随分辨率自动);并发/重试/jpg质量;图片目录/Chrome 路径/端口 | P0 |
|
||||
| 首次引导保护 | 未配账号/未登录时,① ③ 执行按钮禁用并提示去④ | P0 |
|
||||
|
||||
### 后续迭代
|
||||
|
||||
| 功能 | 描述 | 阶段 |
|
||||
| --- | --- | --- |
|
||||
| 多账号并行 | 多个账号各自端口同时运行 | V2 |
|
||||
| 运行日志 / dry-run | 操作留痕、可先空跑预览 | V2 |
|
||||
| 标题规则模板 | 预设多种改标题规则(前缀、替换、截断等) | V3 |
|
||||
|
||||
## 四、核心用户故事(MVP)
|
||||
|
||||
1. 作为运营,我打开工具后能看到已配置的账号列表,并能新增一个账号。
|
||||
2. 我为某账号点击「启动并登录」,工具用它专属的配置目录打开 Chrome,我手动登录一次 Shopee 卖家中心。
|
||||
3. 之后我选中该账号、填入商品 ID,工具自动打开该商品详情页(带登录态)。
|
||||
4. 工具自动把标题改成目标值,并上传我指定的图片、把它拖到第一位设为封面。
|
||||
5. 我核对无误后点击「更新」(或让工具在确认后点击),改动提交线上;不确认则刷新即还原。
|
||||
6. 当账号尚未登录、商品页加载失败或封面已满 9 张时,工具给出明确提示并安全处理。
|
||||
|
||||
## 五、验收标准(MVP)
|
||||
|
||||
- **账号配置**:新增/编辑/删除账号后,配置持久化到本地文件,重启工具仍在。
|
||||
- **绑定配置目录**:每个账号对应唯一的 `chrome_user_data_dir/<账号标识>` 目录;不同账号互不共用、不串号。
|
||||
- **启动并登录**:点击后 Chrome 用对应 user-data-dir 启动并开启调试端口;人工登录一次后,再次启动无需重新登录。
|
||||
- **加载商品页**:选定账号执行时,能用该账号登录态打开目标商品详情页,标题框与图片管理器渲染就绪。
|
||||
- **改标题**:写入后标题框 `value` 与 `modelvalue` 均等于目标值(确认页面模型已更新)。
|
||||
- **换封面**:上传成功后图片张数 +1(满 9 张时先删第一张再传);目标图被拖到第一位成为封面。
|
||||
- **提交更新**:默认不点「更新」;开启提交时,按钮可点才点击,禁用态则不点并提示原因。
|
||||
- **安全**:任意失败路径不崩溃、不误删、不在无确认下提交线上。
|
||||
|
||||
## 六、范围边界与决策
|
||||
|
||||
| 问题 | 决策 |
|
||||
| --- | --- |
|
||||
| 第一版平台 | Windows 桌面(Chrome 与各 user-data-dir 同机) |
|
||||
| 多账号隔离方式 | 每账号独立 user-data-dir(非 Chrome profile);理由见 [架构 3.0](04-architecture.md) |
|
||||
| 是否需要账号 | 是;管理多个 Shopee 卖家账号,但登录由人工首次完成 |
|
||||
| 存储 | 应用设置用 `config.json`;账号/任务/结果用 SQLite;Excel 读写用 openpyxl |
|
||||
| 账号↔任务绑定 | 以 Excel“别名”列为权威(非文件名);匹配不到的略过并最后弹窗汇总 |
|
||||
| 是否提交更新 | ③ 执行时总是点「更新」提交线上(无人工确认、无开关) |
|
||||
| 执行方式 | 多账号串行、逐任务执行,单条失败继续 |
|
||||
| 旧标题/旧封面 | 程序在「采集」阶段改前抓取的快照(输出列),运营不填 |
|
||||
| 新标题/新封面 | AI 生成(输出列),直接用于更新(无人工确认);本地留档+回写 Excel 供追溯 |
|
||||
| AI 服务 | 文本+图像生成,服务商/模型待定;Key 加密存于配置;见 [技术栈](03-tech-stack.md) |
|
||||
| 本地图片 | 旧封面下载、新封面生成存本地图片目录,路径记 DB |
|
||||
| 结果落库时机 | 各阶段处理完立即写 SQLite;该 Excel 全部完成后批量回写原文件 |
|
||||
| 原文件被占用 | 回写时若原 Excel 被锁定,提示关闭重试或另存副本(SQLite 为事实来源) |
|
||||
| 暂不支持 | 自动登录、多账号并行(V2)、爬取 |
|
||||
|
||||
## 七、待确认 / 风险点
|
||||
|
||||
- **账号 / 凭证风险**:登录态存在各账号 user-data-dir,等同账号凭证;目录不得提交版本库、不得外传。
|
||||
- **密码存储风险**:Tab2 的密码加密存于 SQLite,仅供人工参考,**绝不自动登录/自动填**;`cmshopee.db`、`config.json` 必须 gitignore;加密密钥不得与密文同存(见 [任务 T-403](06-tasks.md))。
|
||||
- **别名匹配风险**:别名以 Excel 列为准;不匹配的行略过并最后弹窗,执行起始在日志逐条打印「文件→匹配账号」留痕,防误改。
|
||||
- **AI 主图风险(高,已知并接受)**:本设计**无人工确认、无提交开关**,AI 生成的标题/封面经 ③ 执行即直接上线;主图若失真/夸大/侵权可能违反 Shopee 规则甚至下架。唯一缓解:新图本地留档 + 回写 Excel 供事后追溯;强烈建议先在测试商品验证再批量。
|
||||
- **AI 成本与依赖**:接入文本+图像 AI = 新外部依赖 + API 费用 + Key 管理;服务商/模型/合规待 [技术栈](03-tech-stack.md) 确认。
|
||||
- **AI Key 安全**:Key 加密存于 `config.json`,不写日志、不提交版本库。
|
||||
- **第三方平台风险**:Shopee 页面结构、class 名、接口随时可能变;限流、风控、封号风险存在,禁止高频批量。
|
||||
- **自动化边界风险**:③ 执行会自动改标题、上传图片、拖拽并**直接点更新提交线上**(无确认、无开关);点「开始更新」前需自行确保任务来源与 AI 产出可接受。
|
||||
- **合规风险**:仅在自有/授权账号上操作;遵守 Shopee 卖家条款;不绕过任何平台限制。
|
||||
- **GUI 选型待确认**:默认 Tkinter(零依赖),是否改用 PySide/Web 由维护者确认,见 [技术栈](03-tech-stack.md)。
|
||||
- **多账号并行待确认**:MVP 单账号单端口;并行的端口分配与资源占用在 V2 评估。
|
||||
@@ -0,0 +1,68 @@
|
||||
# 技术栈(Tech Stack)
|
||||
|
||||
> “用什么”的统一速查表。选型与理由在此集中维护;“怎么把它们搭起来”见 [架构设计](04-architecture.md)。
|
||||
> 未定项必须标为待定,不要让 agent 在代码里自行决定。
|
||||
|
||||
## 一、技术栈一览
|
||||
|
||||
| 维度 | 选型 | 状态 | 理由 / 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| 语言 | Python 3.10+ | 已定 | 已有脚本均为 Python;标准库够用 |
|
||||
| 运行平台 | Windows(生产);WSL 可用于开发 | 已定 | Chrome 与各 user-data-dir 在 Windows;GUI 与 Chrome 同机,CDP 走 `localhost` |
|
||||
| 浏览器自动化 | 自研 CDP 客户端 `cdp.py` | 已定 | 基于 websocket-client + requests 手写;不引入 playwright/selenium,规避代理与 Origin 坑 |
|
||||
| WebSocket | `websocket-client`(import `websocket`) | 已定 | 讲 CDP 协议;用 `suppress_origin=True` 绕过 403 |
|
||||
| HTTP | `requests` | 已定 | 读 `/json` 拿 tab 列表;`trust_env=False` 忽略代理 |
|
||||
| 浏览器 | Google Chrome(已安装) | 已定 | 带 `--remote-debugging-port` 启动 |
|
||||
| GUI 框架 | Tkinter(标准库,`ttk.Notebook` 5 Tab) | 待定(推荐) | 零依赖、单机够用;如需更现代 UI 再评估 PySide6 / Web |
|
||||
| 应用配置 | `config.json`(JSON,stdlib) | 已定 | 少量应用级设置:Chrome 路径、目录根、端口、DB 路径等 |
|
||||
| 业务数据 | SQLite(stdlib `sqlite3`,`cmshopee.db`) | 已定 | 账号、任务、结果:成行增长、要查询/统计/导出 |
|
||||
| Excel 读写 | `openpyxl` | 已定 | 导入任务、回写结果;stdlib 读不了 .xlsx |
|
||||
| AI 模型注册 | `config/ai_models.json` 多模型清单(HTTP 调用) | 已定(结构) | 每模型 name/category(text/image)/url/model/key/api_type/连接超时;⑤ 设置可增删改+测试连接 |
|
||||
| AI 文本生成 | 选 `default_text_model`(category=text) | 选型在配置 | 提示词+旧标题→新标题 |
|
||||
| AI 图像生成 | 选 `default_image_model`(category=image,image-to-image) | 选型在配置 | 提示词+旧封面→新封面;分辨率 512/1k/2k/4k,返回超时随分辨率 |
|
||||
| 并发 | 标准库 `concurrent.futures.ThreadPoolExecutor` | 已定 | 标题/图片分别按并发数并行;可停止、可重试 |
|
||||
| 图片处理 | `requests`(下载)+ `Pillow`(按分辨率/jpg质量存盘) | 部分待定 | 下载旧封面;新封面按 resolution 生成、jpg_quality 存盘 |
|
||||
| 测试 | `python -m py_compile` + 手动 CDP 验证 | 待完善 | 纯 UI/浏览器交互难以单测;关键逻辑(配置读写、参数拼装)可加 `pytest` |
|
||||
|
||||
## 二、决策记录与演进
|
||||
|
||||
- **CDP 自研而非 playwright**:当前手写 `cdp.py`,因为它零重依赖、完全可控,并已在开发环境绕开了代理(`*_proxy` 指向本地 :1080)和 Chrome 的 Origin 403 两个坑。未来若交互复杂度大幅上升,再评估 playwright。
|
||||
- **GUI 选 Tkinter**:MVP 是单机配置 + 触发操作的轻量界面,Tkinter 零依赖即可。若后续需要表格、拖拽、复杂布局,再评估 PySide6。**该选型在动代码前需维护者确认。**
|
||||
- **存储拆两层**:应用设置进 `config.json`,账号/任务/结果进 SQLite。判据:少量人改无需查询 → 配置文件;成行增长要查询/导出 → DB。同一事实只存一处,不重复。取代早期的 `accounts.json` 方案。
|
||||
- **Excel 用 openpyxl**:运营用真实 .xlsx;stdlib 无法读写 xlsx,引入一个轻依赖比改用 CSV 更贴合用户习惯。
|
||||
- **多账号隔离用独立 user-data-dir,不用 Chrome profile**:profile 共享同一 user-data-dir/进程/调试端口,无法每账号独立 CDP 与并行;独立 user-data-dir 才契合自动化。详见 [架构 3.0](04-architecture.md)。
|
||||
- **快捷方式生成用 PowerShell(无额外依赖)**:用 `WScript.Shell.CreateShortcut` 生成 `.lnk`,不引入 `pywin32` 等依赖。
|
||||
- **AI 服务商待定**:需选支持文本生成 + 图像 image-to-image 的服务;选型要权衡能力、合规(电商主图)、计费、Key 管理。**未确认前不在代码里写死某家 SDK**,先在 `ai.py` 留稳定接口(`gen_title`/`gen_cover`)。
|
||||
- **AI 产出无人工确认**:生成的新标题/新封面经 ③ 执行即直接上线(无确认、无提交开关);本地留档 + 回写 Excel 供追溯。
|
||||
- **不引入数据库(指外部 DB)**:用 stdlib SQLite 足够;不引入 Postgres/MySQL 等。
|
||||
- **生产在 Windows 直跑**:开发期我们用过 WSL→Windows 的 `netsh portproxy`(9333→9222)连 CDP;但 GUI 与 Chrome 都在 Windows 时,直接连 `127.0.0.1:9222`,无需 portproxy。
|
||||
|
||||
## 三、构建与运行命令
|
||||
|
||||
| 用途 | 命令 |
|
||||
| --- | --- |
|
||||
| 安装依赖 | `pip install websocket-client requests openpyxl` |
|
||||
| 语法检查 | `python -m py_compile *.py` |
|
||||
| 跑单账号演示 | `python prototypes/demo.py`(分步)/ `set AUTO=1 && python prototypes/demo.py`(自动) |
|
||||
| 提交更新(真改线上) | `set UPDATE=1 && python prototypes/demo.py` |
|
||||
| 读取某账号 Cookie(调试) | `python prototypes/cookies.py` |
|
||||
|
||||
Windows PowerShell 下设置环境变量与 cmd 不同:
|
||||
|
||||
```powershell
|
||||
# PowerShell
|
||||
$env:AUTO=1; python prototypes/demo.py
|
||||
$env:UPDATE=1; python prototypes/demo.py
|
||||
```
|
||||
|
||||
```cmd
|
||||
:: cmd
|
||||
set AUTO=1 && python prototypes/demo.py
|
||||
```
|
||||
|
||||
## 四、依赖纪律
|
||||
|
||||
- 新增第三方依赖前,先在本文说明用途、替代方案和维护成本。
|
||||
- GUI 框架一旦确定(Tkinter 或其他),固定下来,不允许两套 UI 框架并存。
|
||||
- 不引入第二套浏览器自动化方案(不要 cdp.py 之外再混入 selenium/playwright)。
|
||||
- 不确定的技术选型先更新本文,再进入代码。
|
||||
@@ -0,0 +1,321 @@
|
||||
# 架构设计
|
||||
|
||||
> 本文讲“怎么把技术栈搭起来”:模块职责、存储模型、流水线、CDP 已验证事实、开发顺序。
|
||||
> 具体用了哪些库 / 平台,见 [技术栈](03-tech-stack.md)。
|
||||
|
||||
## 一、系统结构
|
||||
|
||||
Windows 本地桌面自动化工具,无后端服务,5 Tab GUI 驱动一条流水线。
|
||||
|
||||
```text
|
||||
运营(人)
|
||||
|
|
||||
v
|
||||
GUI(Tkinter ttk.Notebook,5 Tab)
|
||||
① 导入采集 ② AI生成 ③ 更新shopee ④ 账号管理 ⑤ 设置
|
||||
|
|
||||
v
|
||||
核心模块(Python)
|
||||
├── appconfig 读应用配置 config.json(Chrome 路径、目录根、AI 配置、端口范围…)
|
||||
├── db SQLite 读写:账号、任务、各阶段结果(cmshopee.db)
|
||||
├── excel openpyxl 导入输入列 / 回写输出列到原 Excel
|
||||
├── config 账号 ↔ user-data-dir 绑定、slug、目录创建
|
||||
├── chrome 按账号拼启动参数、启动/探测 Chrome、生成快捷方式
|
||||
├── cdp CDP 客户端(连接、找/开 tab、执行 JS、拖拽)
|
||||
├── editor 登录检测 / 采集旧标题旧封面 / 改标题 / 换封面 / 点更新
|
||||
└── ai 文本生成(提示词+旧标题→新标题)/ 图像生成(提示词+旧封面→新封面)
|
||||
|
|
||||
v
|
||||
Google Chrome(每账号独立 --user-data-dir + --remote-debugging-port) + AI 服务(外部)
|
||||
|
|
||||
v
|
||||
Shopee 卖家中心页面 / 本地图片目录
|
||||
```
|
||||
|
||||
真实组件:
|
||||
|
||||
- GUI 入口:`gui.py`(待建,Tkinter + `ttk.Notebook`,5 Tab)。
|
||||
- 核心模块:`appconfig.py`、`db.py`、`excel.py`、`config.py`、`chrome.py`、`editor.py`、`ai.py`(待建);CDP 底座 `cdp.py`(已有)。
|
||||
- 已验证脚本(重构进模块):`prototypes/demo.py`、`prototypes/set_title.py`、`prototypes/set_cover.py`、`prototypes/get_title.py`、`prototypes/cookies.py`、`prototypes/inspect_images.py`、`prototypes/grab.py`。
|
||||
- 外部依赖:本机 Google Chrome;Shopee;AI 服务(文本+图像,服务商待定);`openpyxl`。
|
||||
|
||||
## 二、流水线(核心)
|
||||
|
||||
每个任务(Excel 行)依次走过 5 个阶段,状态字段 `stage` 贯穿全程:
|
||||
|
||||
```text
|
||||
imported → collected → generated → applied
|
||||
(导入 Excel) (采集旧数据) (AI 生成新数据) (改 Shopee 并提交)
|
||||
① Tab ① Tab ② Tab ③ Tab
|
||||
```
|
||||
|
||||
- **imported**:openpyxl 解析输入列入库。
|
||||
- **collected**:只读打开商品页,读旧标题、下载旧封面到本地,写 `old_title/old_cover_path`,回写 Excel 旧字段。
|
||||
- **generated**:AI 用提示词+旧标题生成新标题;用提示词+旧封面生成新封面(image-to-image),新图存本地。**无人工确认环节**,生成完即可被 ③ 执行(新图留档本地 + 回写 Excel 供事后追溯)。
|
||||
- **applied**:打开编辑页换标题+封面,**总是点「更新」提交线上**(无开关),回写结果。
|
||||
|
||||
任意阶段失败 → `stage` 不前进、记 `error`、`result/skipped`,不影响其他任务。
|
||||
|
||||
## 三、职责划分
|
||||
|
||||
**GUI(5 Tab)**:见 [routes.md](routes.md)。只做交互与预览,不写业务逻辑;耗时操作走后台线程。**首次未配账号/未登录时,① ③ 执行按钮禁用并提示去 ④。**
|
||||
|
||||
**核心模块**
|
||||
|
||||
- `appconfig`:读写 `config.json`(Chrome 路径、`chrome_user_data_dir` 根、图片目录、AI 配置、端口、DB 路径、默认是否提交)。
|
||||
- `db`:SQLite 读写账号、任务、各阶段结果;建表/迁移。
|
||||
- `excel`:openpyxl 读输入列、把输出列回写原 Excel(处理文件锁)。
|
||||
- `config`:账号 ↔ user-data-dir 绑定;slug;目录创建。
|
||||
- `chrome`:拼接启动命令、启动、探测端口、(可选)生成快捷方式。
|
||||
- `cdp`:连接调试端口、找/开 tab、执行 JS、拖拽、注入文件。
|
||||
- `editor`:登录检测、**采集**(读旧标题、下载旧封面)、改标题、换封面、点更新。
|
||||
- `ai`:`gen_title(prompt, old_title)`、`gen_cover(prompt, old_cover_path)`(外部 AI;服务商待定)。
|
||||
|
||||
**存储(同一事实只存一处)**
|
||||
|
||||
- 应用配置(模型选择、生成参数、目录、Chrome 路径)→ `config.json`。
|
||||
- AI 模型清单(url/模型/密钥/类型/连接超时)→ `config/ai_models.json`(含密钥,必须 gitignore)。
|
||||
- 业务数据(账号、任务、各阶段结果)→ SQLite `cmshopee.db`。
|
||||
- 图片(采集的旧封面、AI 生成的新封面)→ 本地图片目录(路径记在 DB)。
|
||||
- 提示词 → 标题提示词存单文件 `title_prompt.txt`;封面提示词存多模板 `prompts/cover/<名称>.txt`。
|
||||
- 登录态 → 各账号 `chrome_user_data_dir/<slug>/`。
|
||||
|
||||
## 四、多账号隔离方案(决策)
|
||||
|
||||
采用**每账号独立 user-data-dir**(非 Chrome profile)。`--remote-debugging-port` 绑定在 user-data-dir/进程上,profile 方案无法每账号独立 CDP、串号风险高。启动主路径用程序 `subprocess` 直启(`--remote-debugging-port` + `--remote-allow-origins=*` + `--user-data-dir`);可选生成 `.lnk` 快捷方式(PowerShell `WScript.Shell`,参数写在「目标」字段)。
|
||||
|
||||
## 五、数据模型
|
||||
|
||||
### 5.1 应用配置 `config.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"chrome_path": "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
|
||||
"user_data_root": "chrome_user_data_dir",
|
||||
"image_dir": "images",
|
||||
"db_path": "cmshopee.db",
|
||||
"default_debug_port": 9222,
|
||||
"debug_port_range": [9222, 9260],
|
||||
"cdp_ready_timeout": 60,
|
||||
"ai": {
|
||||
"default_text_model": "GPT-5.5 文本",
|
||||
"default_image_model": "Nano Banana 2",
|
||||
"title_concurrency": 4,
|
||||
"image_concurrency": 4,
|
||||
"retry": 2,
|
||||
"jpg_quality": 90,
|
||||
"resolution": "1k",
|
||||
"resolution_timeouts": { "512": 180, "1k": 240, "2k": 360, "4k": 600 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`ai` 段只放**选择 + 全局生成参数**:
|
||||
|
||||
- `default_text_model` / `default_image_model`:引用 `ai_models.json` 里的模型名(标题用文本模型、封面用图像模型)。
|
||||
- `resolution`:当前分辨率,下拉 `512 / 1k / 2k / 4k`。
|
||||
- `resolution_timeouts`:分辨率 → **等待大模型返回超时(秒)** 的映射;用户选分辨率即自动套用,不单独填。
|
||||
- 模型本身的定义(url/key/类型/连接超时…)在 `config/ai_models.json`,见 5.1b。
|
||||
- 密钥不在 `config.json`:每个模型的 `api_key` 存于 `config/ai_models.json`,加密、打码、gitignore。
|
||||
|
||||
### 5.1b AI 模型清单 `config/ai_models.json`
|
||||
|
||||
模型定义清单("有哪些模型"),与 `config.json` 的 `ai` 段("选了哪个 + 全局参数")职责分开。
|
||||
|
||||
```json
|
||||
{
|
||||
"models": [
|
||||
{
|
||||
"name": "Nano Banana 2", // 服务商/模型名,唯一,作下拉显示与引用键
|
||||
"category": "image", // text | image —— 决定它出现在“标题/图片大模型”哪个下拉
|
||||
"enabled": true,
|
||||
"url": "https://api.vectorengine.ai/v1/chat/completions",
|
||||
"model": "gemini-3.1-flash-image-preview", // 模型 ID
|
||||
"api_key": "***", // 密钥:加密存、UI 打码、不入日志、不进版本库
|
||||
"api_type": "auto", // chat | images_edits | auto —— 决定请求构造方式
|
||||
"connect_timeout_seconds": 30, // 连接该服务超时(每模型,默认 30)
|
||||
"timeout_seconds": 0, // 返回超时:0/留空 = 运行时按 resolution_timeouts 取值
|
||||
"extra_body": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
关键事实:
|
||||
|
||||
- **`category`(文本/图像)是必需的**:角色下拉据此过滤(标题下拉只列 text、图片下拉只列 image),防止错配。
|
||||
- 约束:**至少各有一个 text 与一个 image 模型**;下拉默认最少一项、删到剩一项时禁用「删除」。
|
||||
- `connect_timeout_seconds`(连接超时)属于**模型**;返回超时由分辨率映射决定(不在模型上单设)。
|
||||
- `api_type` 反映不同 API 形状(`chat`/`images_edits`/`auto`),请求构造按它分支。
|
||||
- `name` 唯一;`api_key` 加密存、打码显示。
|
||||
|
||||
### 5.2 SQLite `cmshopee.db`
|
||||
|
||||
```sql
|
||||
-- 账号(④ 账号管理)
|
||||
CREATE TABLE accounts (
|
||||
id INTEGER PRIMARY KEY,
|
||||
account_name TEXT NOT NULL, -- Shopee 登录账号名(展示/参考)
|
||||
alias TEXT UNIQUE NOT NULL, -- 别名,Excel 用它匹配
|
||||
region_host TEXT NOT NULL,
|
||||
slug TEXT UNIQUE NOT NULL, -- user-data-dir 子目录名 [a-z0-9_]
|
||||
user_data_dir TEXT NOT NULL,
|
||||
debug_port INTEGER NOT NULL,
|
||||
password_enc TEXT, -- 加密存,仅参考,不自动登录
|
||||
note TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
last_login_at TEXT
|
||||
);
|
||||
|
||||
-- 任务 + 各阶段结果(贯穿流水线)
|
||||
CREATE TABLE tasks (
|
||||
id INTEGER PRIMARY KEY,
|
||||
batch_id TEXT NOT NULL,
|
||||
source_file TEXT,
|
||||
-- 输入列(Excel)
|
||||
account_name TEXT,
|
||||
alias TEXT NOT NULL,
|
||||
item_id TEXT NOT NULL,
|
||||
-- 采集输出(程序写,改前快照)
|
||||
old_title TEXT,
|
||||
old_cover_path TEXT, -- 旧封面本地图片路径
|
||||
-- AI 输出
|
||||
new_title TEXT,
|
||||
new_cover_path TEXT, -- 新封面本地图片路径
|
||||
-- 应用
|
||||
committed INTEGER DEFAULT 0, -- 是否成功点「更新」提交
|
||||
stage TEXT DEFAULT 'imported', -- imported/collected/generated/applied/failed/skipped
|
||||
error TEXT,
|
||||
updated_at TEXT
|
||||
);
|
||||
```
|
||||
|
||||
关键事实:
|
||||
|
||||
- `alias` 是账号↔任务**唯一关联键**;找不到账号 → stage=skipped,error=别名未匹配,最后弹窗汇总。
|
||||
- `account_name` 仅展示/参考;匹配以 `alias` 为准。
|
||||
- `old_title/old_cover_path`:程序**采集阶段抓取**的快照(输出)。
|
||||
- `new_title/new_cover_path`:**AI 生成**结果(输出),图片落本地,**无人工确认**,生成即可应用。
|
||||
- `committed`:③ 执行时**总是点「更新」提交**;该字段记录提交是否成功。
|
||||
- 各字段**每阶段处理完立即写回 SQLite**(实时落库);阶段结束后批量回写 Excel。
|
||||
|
||||
### 5.3 Excel 模板
|
||||
|
||||
| 列 | 含义 | 输入/输出 |
|
||||
| --- | --- | --- |
|
||||
| 账号名 | Shopee 账号名(展示) | 输入 |
|
||||
| 别名 | 关联 accounts.alias(匹配键) | 输入 |
|
||||
| 商品id | item_id | 输入 |
|
||||
| 旧标题 | 采集到的旧标题 | 输出,回写 |
|
||||
| 旧封面图片路径 | 采集到的旧封面本地路径 | 输出,回写 |
|
||||
| 新标题 | AI 生成的新标题 | 输出,回写 |
|
||||
| 新封面图片路径 | AI 生成的新封面本地路径 | 输出,回写 |
|
||||
| 更新状态 | 处理结果(成功/失败/跳过+原因) | 输出,回写 |
|
||||
|
||||
- 输入列(账号名/别名/商品id)运营填;其余为程序各阶段输出,**回写到原 Excel**(被锁→提示重试/另存副本)。
|
||||
- 别名以此列为匹配权威,不解析文件名。
|
||||
|
||||
### 5.4 本地图片目录
|
||||
|
||||
```text
|
||||
images/<slug>/<item_id>_old.<ext> # 采集下载的旧封面
|
||||
images/<slug>/<item_id>_new.<ext> # AI 生成的新封面
|
||||
```
|
||||
|
||||
路径记入 DB;上传新封面用本地 `<item_id>_new` 文件(Windows 绝对路径传 setFileInputFiles)。
|
||||
|
||||
## 六、关键流程细节
|
||||
|
||||
### 6.1 采集(① Tab,只读)
|
||||
|
||||
- 用账号 Chrome 打开商品页,等就绪,读旧标题(标题输入框 value)。
|
||||
- 旧封面:取第一张 itembox 的 `img.src`(CDN 链接),下载到 `images/<slug>/<item_id>_old`。
|
||||
- 写 `old_title/old_cover_path`、stage=collected;批量回写 Excel 旧字段。
|
||||
|
||||
### 6.2 AI 生成(② Tab)
|
||||
|
||||
单个「开始生成」按钮,**两段式、各自并发**(标题快、图片慢,分开并发更高效):
|
||||
|
||||
1. **并发生成标题**:线程池大小 = `title_concurrency`,用 `default_text_model` 调 `gen_title(标题提示词, old_title)` → new_title。
|
||||
2. **接着并发生成图片**:线程池大小 = `image_concurrency`,用 `default_image_model` 调 `gen_cover(封面提示词, old_cover_path, resolution, jpg_quality)` → 新图存 `images/<slug>/<item_id>_new.jpg`。
|
||||
- 连接超时取该模型 `connect_timeout_seconds`;**返回超时取 `resolution_timeouts[resolution]`**(512→180/1k→240/2k→360/4k→600)。
|
||||
|
||||
- 失败重试:每次调用失败按 `retry` 次重试,仍失败则记 error(不阻塞其余)。
|
||||
- 每条/每张完成**立即 `set_generated` 写库**(实时落库,停止或崩溃不丢已生成的)。
|
||||
- **「停止」**:取消未开始的任务,正在跑的少量完成或中断;停止后可再次「开始生成」对剩余继续。
|
||||
- 进度:`标题 x/n · 封面 x/n · 失败 n`。
|
||||
- 生成后 stage=generated;**无人工确认**。双击任务弹窗查看新旧封面(纯查看),可选对某行 `重生成`;新标题直接用 AI 输出(不可编辑)。
|
||||
- 并发数、重试、分辨率、jpg 质量、模型/Key 均来自 ⑤ 设置(`config.json` 的 `ai` 段)。
|
||||
|
||||
提示词管理:
|
||||
|
||||
- **标题提示词**:单个文本,「保存」写入 `title_prompt.txt`;软件启动时加载该文件回显到输入框(缺失则空)。
|
||||
- **封面提示词**:多模板。下拉选模板(读 `prompts/cover/*.txt`),图标工具栏 新建/保存/另存为/重命名/删除;重名校验、删除二次确认、删空给默认。
|
||||
- **变量**:封面提示词支持占位符 `{旧标题}`、`{新标题}`、`{商品id}`、`{店铺}`,生成前用该任务真实值替换(`render_prompt`)。「插入标题」= 在光标处插入 `{新标题}`;「预览」= 用某条任务的值替换变量后展示,确认实际发送给 AI 的内容。
|
||||
|
||||
### 6.3 应用更新(③ Tab)
|
||||
|
||||
- 对**已生成(generated)任务**:`open_product` → `change_title(new_title)`(如有)→ `replace_cover(new_cover_path)`(如有)→ **总是 `click_update` 提交**。
|
||||
- 串行、单条失败继续;每条立即写 SQLite;全部完成回写 Excel(新字段+状态)+ 弹窗汇总。
|
||||
|
||||
### 6.4 登录检测
|
||||
|
||||
- 打开卖家中心,重定向到登录页或缺会话 Cookie(`SPC_ST`/`SPC_U`)→ 未登录;不自动登录,提示人工登录。
|
||||
|
||||
## 七、CDP 已验证事实(务必遵守)
|
||||
|
||||
| 难点 | 已验证结论 |
|
||||
| --- | --- |
|
||||
| Chrome 启动参数 | 全关后带 `--remote-debugging-port=<port> --remote-allow-origins=* --user-data-dir=<dir>`;缺 allow-origins 则 WebSocket 403 |
|
||||
| 代理干扰 | 清除 `*_proxy`(requests `trust_env=False`),否则连本地 CDP 超时 |
|
||||
| WebSocket Origin | `websocket-client` `suppress_origin=True` |
|
||||
| SPA 就绪 | 不用 load 事件;轮询“标题输入框 + 图片 itembox + 上传输入框”三者都在 |
|
||||
| 标题输入框 | XPath `//input[@class='eds-input__input' and string-length(@modelvalue)>24]` |
|
||||
| 写标题 | 原生 setter + 派发 `input`/`change`;`value`==`modelvalue`==新值 |
|
||||
| 读旧封面 | 第一张 itembox 的 `img.src`(`susercontent` CDN),下载到本地 |
|
||||
| 上传输入框 | `.shopee-image-manager__upload input[type=file]`;`DOM.setFileInputFiles` 传 Windows 路径 |
|
||||
| 上传成功 | 张数 +1 且新图 src 为 `susercontent` |
|
||||
| 封面=第一位 | `Input.dispatchMouseEvent` 拖到第一位,落点 `第一张.left - 0.30*宽` |
|
||||
| 满 9 张 | 上限 9;换封面先删第一张(`.shopee-image-manager__icon--delete`,确认框待实测)|
|
||||
| 更新按钮 | `button.eds-button` 中 `<span>更新</span>`;③ 执行即点提交;禁用态(校验未过)记为失败 |
|
||||
| 登录检测 | 重定向到登录页或缺 `SPC_ST` → 未登录 |
|
||||
|
||||
高风险动作(删满 9 张封面、点更新提交、AI 图直接上线)先在测试商品验证。注意:本设计**无人工确认、无提交开关**,③ 一旦执行即把 AI 标题/封面提交线上,新图本地留档+回写 Excel 是唯一事后追溯手段。
|
||||
|
||||
## 八、推荐开发顺序
|
||||
|
||||
1. **地基**:`editor.py`(含采集)、`appconfig.py`+`config.json`、`db.py`、`.gitignore`。
|
||||
2. **账号与启动**(④):`config` 建目录、`chrome` 启动器/快捷方式、登录保活与检测、账号 CRUD。
|
||||
3. **导入采集**(①):`excel` 导入、采集旧标题/旧封面、回写。
|
||||
4. **AI 生成**(②):`ai` 模块、提示词、对照预览与确认。
|
||||
5. **更新 shopee**(③):对已生成任务换标题+封面、直接提交、回写。
|
||||
6. **设置**(⑤)+ 首次未配账号引导保护。
|
||||
|
||||
## 九、项目结构建议
|
||||
|
||||
```text
|
||||
cmshopee/
|
||||
├── docs/
|
||||
├── config.json # 应用配置(模型选择/生成参数/路径,gitignore)
|
||||
├── config/ai_models.json # AI 模型清单(含密钥,必须 gitignore)
|
||||
├── cmshopee.db # SQLite(账号/任务/结果,gitignore)
|
||||
├── chrome_user_data_dir/ # 各账号 Chrome 配置(含登录态,gitignore)
|
||||
├── images/ # 旧封面/新封面本地图片(建议 gitignore)
|
||||
├── title_prompt.txt # 标题提示词(单文件,启动回显)
|
||||
├── prompts/cover/<名称>.txt # 封面提示词模板(多个)
|
||||
├── appconfig.py / db.py / excel.py / config.py / chrome.py / editor.py / ai.py / prompts.py / gui.py # 待建
|
||||
├── cdp.py # CDP 底座(正式模块,已有)
|
||||
└── prototypes/ # 已验证原型/探查脚本(demo/set_*/get_title/cookies/inspect_images/grab/1.py)
|
||||
# 逻辑待并入 editor.py 后清理;见 prototypes/README.md
|
||||
```
|
||||
|
||||
> `config.json`、`config/ai_models.json`、`cmshopee.db`、`chrome_user_data_dir/`、`images/` 含密钥/凭证/业务数据,必须 gitignore。
|
||||
|
||||
## 十、架构纪律
|
||||
|
||||
- CDP 交互事实变化同步第七节。
|
||||
- 存储边界:应用设置→config.json,账号/任务/结果→SQLite,图片→本地目录并记路径于 DB,登录态→user-data-dir;同一事实只存一处。
|
||||
- 别名是账号↔任务唯一关联键。
|
||||
- 密码与 AI Key 加密存、不外传、不写日志;不自动登录。
|
||||
- AI 生成内容直接用于 ③ 更新(无人工确认);新图本地留档 + 回写 Excel 以备追溯。
|
||||
- 高风险模块先单独验证,再接入流水线。
|
||||
@@ -0,0 +1,84 @@
|
||||
# 编码规则(Coding Rules)
|
||||
|
||||
> 每次写代码前先读完本文件。这是让 AI 不跑偏、代码质量稳定的硬约束。
|
||||
> 与技术细节冲突时,以 [技术栈](03-tech-stack.md) / [架构设计](04-architecture.md) 的事实为准;与“该不该做”冲突时,以 [需求](02-requirements.md) 为准。
|
||||
|
||||
## 0. 黄金法则
|
||||
|
||||
1. **不臆造**:选择器、字段、文件、接口不确定就查证或先用 `prototypes/inspect_images.py` 探查页面,不要猜 class 名。
|
||||
2. **守范围**:只做当前任务要求的事,不顺手加批量/并行等后续功能。
|
||||
3. **照架构**:复用 `cdp.py`,遵守 `04-architecture.md` 第四节“已验证结论”,不另起一套 CDP 交互。
|
||||
4. **小步改**:一次只解决一个问题,不夹带无关重构。
|
||||
5. **可验证**:改完必须能 `py_compile`,关键路径能在测试商品上跑通,对得上验收标准。
|
||||
|
||||
## 1. 动手前
|
||||
|
||||
- 按链路确认:`vision` -> `requirements` -> `tech-stack` -> `architecture` -> `tasks`。
|
||||
- 找到本任务对应的验收标准,写之前就知道“怎么算做对”。
|
||||
- 复用优先:已有 `cdp.py` 的 `CDP`、`find_product_tab`、`create_tab`、`drag`;已有 `prototypes/demo.py`/`prototypes/set_*.py` 中验证过的 JS 片段。
|
||||
- 需求含糊或改动会偏离已验证事实时,先问。
|
||||
|
||||
## 2. 事实来源纪律
|
||||
|
||||
- CDP 交互只信 `04-architecture.md` 第四节的“已验证结论”和真实页面探查结果。
|
||||
- 不从旧脚本注释或记忆里推断仍然有效的选择器;Shopee 页面可能已变,必要时重新探查。
|
||||
- 不虚构 Shopee 接口、字段、按钮文案。
|
||||
- 选择器 / 流程变化必须同步更新 `04-architecture.md` 和相关任务。
|
||||
|
||||
## 3. 范围纪律
|
||||
|
||||
- MVP 只做 `02-requirements.md` 中列为 P0 的功能(账号配置、绑定目录、启动登录、加载页、改标题、换封面、可选更新)。
|
||||
- V2 / V3 功能(批量、多账号并行、日志、规则模板)只记录,不实现。
|
||||
- 需求明确排除的非目标(自动登录、绕风控、爬取、数据库)不得实现。
|
||||
|
||||
## 4. 架构纪律
|
||||
|
||||
- 技术栈以 `03-tech-stack.md` 为准;GUI 框架未确认前不要写大量 UI 代码。
|
||||
- 新增依赖前先说明理由;优先标准库(Tkinter、json、sqlite3、subprocess),Excel 用 openpyxl。
|
||||
- 存储边界:应用设置进 `config.json`,账号/任务/结果进 SQLite,登录态只在 user-data-dir,同一事实只存一处。
|
||||
- 模块职责以 `04-architecture.md` 为准:GUI 不写业务逻辑,业务在 `appconfig/db/excel/config/chrome/cdp/editor`。
|
||||
- 模块/CLI 合约以 `api.md` 为准。
|
||||
|
||||
## 5. 代码规范
|
||||
|
||||
- 标识符使用英文;UI 文案、注释、文档保持中文,与现有脚本一致。
|
||||
- 错误必须处理:CDP 超时、tab 找不到、上传失败、按钮禁用都要给明确提示,不吞错。
|
||||
- 注释解释“为什么”(尤其 CDP 的坑:代理、Origin、就绪判断、拖拽落点),不复述“做了什么”。
|
||||
- 复用现有脚本里已验证的 JS 字符串,不重写出不一致的版本。
|
||||
|
||||
## 6. 测试与验证
|
||||
|
||||
完成前至少检查:
|
||||
|
||||
- [ ] `python -m py_compile` 通过。
|
||||
- [ ] 涉及 CDP 的改动,在测试商品(ITEM_ID 51100639510)上实跑验证。
|
||||
- [ ] 对得上需求验收标准(如标题 `value`+`modelvalue` 双等于、封面在第一位)。
|
||||
- [ ] 没有夹带无关改动。
|
||||
- [ ] 涉及选择器/流程/配置 schema 变化时,文档已同步。
|
||||
- [ ] 回复里如实说明跑了什么命令、结果如何。
|
||||
|
||||
```bash
|
||||
python -m py_compile *.py
|
||||
python prototypes/demo.py # 单账号闭环验证(不提交)
|
||||
```
|
||||
|
||||
## 7. 绝不
|
||||
|
||||
- 绝不把真实账号、密码、Cookie、token 写进代码、文档或日志。
|
||||
- 绝不明文存密码;DB 里密码必须加密,且加密密钥不与密文同存。
|
||||
- 绝不把 `config.json`、`cmshopee.db`、`chrome_user_data_dir/` 提交版本库。
|
||||
- 绝不自动登录 / 自动填账号密码;登录由人工完成,程序只检测登录态。
|
||||
- 绝不在没有显式确认/开关的情况下点击「更新」提交线上。
|
||||
- 绝不擅自删除用户文件或重置 user-data-dir。
|
||||
- 绝不为通过验证而降低验收标准(如不验证 `modelvalue` 就当改成功)。
|
||||
|
||||
## 8. 安全与合规
|
||||
|
||||
- 登录凭证只存在于各账号 user-data-dir;不导出、不外传、不写入配置或日志。
|
||||
- 涉及 Shopee 时,遵守 `04-architecture.md` 写明的页面规则与限流边界;不高频批量、不绕风控/验证码。
|
||||
- 高风险动作(删满 9 张的封面、点击更新)必须有显式确认或开关,并先在测试商品验证。
|
||||
- 自动化默认支持“只改不提交”(dry-run 思路);提交、删除等不可逆动作要可控、可回退(刷新还原)。
|
||||
|
||||
## 9. 拿不准就问
|
||||
|
||||
问题要具体:说明卡在哪、有哪些选项、倾向哪个及原因。尤其 GUI 框架、端口分配、删图确认框结构这类影响后续的决策,先确认再写。
|
||||
@@ -0,0 +1,91 @@
|
||||
# 任务看板(Tasks)
|
||||
|
||||
> 把目标拆成小步、可独立交付的任务,让 AI 一步一步开发,避免一次生成整个项目。
|
||||
|
||||
## 使用规则
|
||||
|
||||
1. **一次只做一个任务**:每轮只领取一个 `TODO` 且依赖均 `DONE` 的任务,取最靠前的。
|
||||
2. **做完即停**:完成、自测通过、改 `DONE` 后汇报。
|
||||
3. **不跳步**:依赖未完成不开工。
|
||||
4. **完成定义**:以 [编码规则](05-coding-rules.md) 验证清单为准。
|
||||
5. **动手前**读 `00-ai-start-here.md`、`05-coding-rules.md`、`current-state.md`、`04-architecture.md`(尤其第七节已验证事实、第二节流水线)。
|
||||
6. **完成后**同步本文状态,追加 [`../progress.md`](../progress.md),覆盖 [`current-state.md`](current-state.md)。
|
||||
|
||||
## 状态图例
|
||||
|
||||
`TODO` · `DOING`(同一时间最多 1 个)· `DONE` · `BLOCKED`
|
||||
|
||||
---
|
||||
|
||||
## Phase 0 · 地基(模块化 + 存储)
|
||||
|
||||
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| T-001 | `editor.py`:改标题/换封面/点更新/登录检测/**采集(读旧标题+旧封面下载)**/apply_task,复用 `cdp.py` | - | 函数可调用,在测试商品跑通;与 `prototypes/demo.py` 行为一致 | TODO |
|
||||
| T-002 | `appconfig.py` + `config.json`(含 image_dir、ai 段、端口等默认值) | - | 读写正常;不存在则写默认;AI Key 加密占位 | TODO |
|
||||
| T-003 | `db.py` + SQLite 建表(accounts/tasks,含各阶段字段) | - | `init_db` 幂等;账号/任务/各 set_* 可用;schema 同架构 5.2 | TODO |
|
||||
| T-004 | `.gitignore`:排除 `config.json`、`cmshopee.db`、`chrome_user_data_dir/`、`images/` | T-002, T-003 | 凭证/业务/图片不被提交 | TODO |
|
||||
|
||||
## Phase 1 · 账号管理(④)
|
||||
|
||||
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| T-101 | `config` 生成 slug + 创建 `chrome_user_data_dir/<slug>` | T-003 | 别名→唯一 slug;目录按需建;路径绝对化 | TODO |
|
||||
| T-102 | `chrome.py` 启动器:拼参数并启动、探测端口 | T-101, T-002 | 含三参数;端口就绪可探测 | TODO |
|
||||
| T-103 | 首次登录保活 + 登录检测 `is_logged_in` | T-102, T-001 | 关闭再启动免重登;登录/未登录判断准确 | TODO |
|
||||
| T-104 | 五 Tab 主窗口骨架(`ttk.Notebook`,5 Tab 空壳) | T-002 | 五个 Tab 按顺序可切换 | TODO |
|
||||
| T-105 | Tab④ 账号增删改(账号名/别名/端口/密码加密)+ 启动登录 + 检测登录 | T-104, T-101, T-103 | 增删改入库、建目录;密码加密默认打码;状态列刷新 | TODO |
|
||||
| T-106 | 可选:为账号生成桌面快捷方式 | T-102 | `.lnk` 目标含该账号参数;双击进对应账号 | TODO |
|
||||
|
||||
## Phase 2 · 导入采集(①)
|
||||
|
||||
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| T-201 | `excel.py` 导入:解析多文件输入列入库 | T-003 | 按模板解析账号名/别名/商品id;脏数据容错;写 tasks(batch_id) | TODO |
|
||||
| T-202 | Tab① 任务列表 + 导入按钮 + 别名匹配标记 | T-201, T-105 | Treeview 显示账号/别名/商品id/阶段;未匹配标“略过” | TODO |
|
||||
| T-202b | Tab① 导入汇总栏 | T-202 | 导入后显示 文件数/解析行数/有效/无效/匹配(按账号)/未匹配;未匹配可点击筛出 | TODO |
|
||||
| T-203 | 采集旧标题+旧封面(只读),下载图片,立即写库 | T-202, T-001 | 逐条 set_collected;旧封面下载到 `images/<slug>/`;未登录/未匹配略过记原因 | TODO |
|
||||
| T-204 | 回写旧字段到原 Excel(含文件锁处理) | T-203, T-201 | 旧标题/旧封面回写原文件;被锁提示重试或 export_copy | TODO |
|
||||
| T-205 | 首次未配账号/未登录的引导保护 | T-105, T-203 | 无账号/未登录时 ① 执行按钮禁用并提示去④ | TODO |
|
||||
|
||||
## Phase 3 · AI 生成(②)
|
||||
|
||||
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| T-301 | 确定 AI 服务商/模型并接入 `ai.py`(`gen_title`/`gen_cover`,带重试/分辨率/jpg质量) | T-002 | 可调;Key 加密读取;`gen_cover` 支持 resolution+jpg_quality;失败按 retry 重试;错误明确 | TODO |
|
||||
| T-302 | Tab② 左右布局:左提示词(标题/封面),右按批次/店铺/状态筛选 + 任务列表 | T-301, T-203 | 左 ~1/4 提示词多行;右筛选+列表(店铺/商品id/旧标题/新标题/状态) | TODO |
|
||||
| T-302p | `prompts.py` + Tab② 提示词管理 | T-302 | 标题保存/启动回显 title_prompt.txt;封面多模板(下拉+新建/保存/另存为/重命名/删除,存 prompts/cover/);插入 `{新标题}`;预览变量替换;render_prompt 接入生成 | TODO |
|
||||
| T-303 | Tab② 开始生成(单按钮)+ 停止 + 进度:**先并发标题再并发图片** | T-302 | `generate_batch` 先 title_concurrency 并发标题、再 image_concurrency 并发图片;每条 set_generated 立即写库;停止可取消未开始项;进度 标题/封面/失败 计数;双击弹窗看新旧封面 | TODO |
|
||||
|
||||
## Phase 4 · 更新 shopee(③)
|
||||
|
||||
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| T-401 | Tab③ 列出已生成任务 + 按批次/店铺/状态筛选 | T-303 | 顶部批次/店铺/状态筛选;「开始更新」仅作用于当前筛选结果;状态=失败可重试;无提交开关;警示“执行即提交线上” | TODO |
|
||||
| T-402 | 串行执行 apply:换标题+换封面+**总是点更新提交**,单条失败继续,立即写库 | T-401, T-001 | 逐条 set_applied;恒提交;失败继续;未登录引导保护 | TODO |
|
||||
| T-403 | 回写结果到原 Excel(新标题/新封面/更新状态)+ 结束弹窗汇总 | T-402, T-204 | 回写原文件(锁处理);弹窗 成功/失败/略过 | TODO |
|
||||
|
||||
## Phase 5 · 设置与收尾
|
||||
|
||||
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| T-501 | Tab⑤ 设置 · AI 模型管理(master-detail):模型下拉 + 新增/删除 + 详情(服务商名/类别/api_type/模型ID/网址/密钥打码/连接超时) + 测试连接 | T-002 | 读写 `config/ai_models.json`;name 唯一;类别 text/image;至少各一个;密钥打码加密;测试连接可用 | TODO |
|
||||
| T-501b | Tab⑤ 设置 · 角色与生成参数:标题大模型/图片大模型下拉、分辨率(512/1k/2k/4k)、返回超时随分辨率、并发/重试/jpg质量、路径/端口 | T-501 | 角色下拉按类别过滤;分辨率选后返回超时按 resolution_timeouts 自动;持久化 config.json | TODO |
|
||||
| T-502 | 满 9 张封面:删第一张再上传 | T-001 | 实测删除确认框并处理;满 9 张流程正确 | TODO |
|
||||
| T-503 | 密码 / AI Key 加密密钥方案落地 | T-105, T-501 | 密钥不与密文同存;说明威胁模型 | TODO |
|
||||
| T-504 | 多账号并行 / dry-run / 运行日志(V2) | T-402 | 端口不冲突;dry-run 只预览;操作留痕 | TODO |
|
||||
|
||||
## 里程碑
|
||||
|
||||
- M1:editor(含采集)模块化、config.json + SQLite 就绪(Phase 0)。
|
||||
- M2:账号管理可用、能登录与检测(Phase 1)。
|
||||
- M3:导入 + 采集 + 回写旧数据(Phase 2)。
|
||||
- M4:AI 生成新标题/新封面(Phase 3)。
|
||||
- M5:对已生成任务更新 Shopee(直接提交)+ 回写结果(Phase 4)。
|
||||
|
||||
## 待办池(Backlog)
|
||||
|
||||
- 标题规则/提示词模板库。
|
||||
- 上传弹裁剪框商品的自动裁剪。
|
||||
- 账号登录态健康巡检。
|
||||
- AI 生成图的合规/质量自检。
|
||||
@@ -0,0 +1,36 @@
|
||||
# 项目文档导航
|
||||
|
||||
> cmshopee 的 harness coding 文档集合。agent 开始编程前从 [`00-ai-start-here.md`](00-ai-start-here.md) 进入。
|
||||
|
||||
## 一句话定位
|
||||
|
||||
cmshopee 是一个给**电商运营**使用的 Windows 桌面自动化工具,用于**管理多个 Shopee 卖家账号、并用 CDP 驱动 Chrome 批量修改商品标题、替换商品封面图**。第一版先跑通「配置账号 → 绑定独立 Chrome 配置目录(保留登录态)→ 用某账号打开指定商品详情页 → 改标题 + 换封面(可选提交更新)」闭环。
|
||||
|
||||
## 文档导航
|
||||
|
||||
- [`../AGENTS.md`](../AGENTS.md):Codex / 通用 AI coding agent 的仓库级入口。
|
||||
- [`../CLAUDE.md`](../CLAUDE.md):Claude Code 的薄入口,具体规则以 `AGENTS.md` 为准。
|
||||
- [`../progress.md`](../progress.md):执行历史流水,只追加记录任务执行、验证、阻塞和决策。
|
||||
- [AI 开发入口](00-ai-start-here.md):agent 每次开始工作的入口、阅读顺序和任务领取规则。
|
||||
- [项目愿景](01-vision.md):为什么做、为谁做、产品原则、非目标。
|
||||
- [需求](02-requirements.md):要什么、用户故事、验收标准。
|
||||
- [技术栈](03-tech-stack.md):语言、运行平台、CDP 方案、GUI 选型和运行命令。
|
||||
- [架构设计](04-architecture.md):模块职责、账号与配置目录数据模型、CDP 已验证事实和开发顺序。
|
||||
- [编码规则](05-coding-rules.md):写代码前必须遵守的硬约束。
|
||||
- [任务看板](06-tasks.md):按依赖拆分的小任务,agent 每轮只做一个。
|
||||
- [模块 / CLI 合约](api.md):本地模块接口、Chrome 启动参数、账号配置 schema。
|
||||
- [界面与流程结构](routes.md):GUI 窗口、操作流程、按钮职责(无前端路由,用 GUI 流程替代)。
|
||||
- [当前实现状态](current-state.md):当前代码现实、可运行命令、下一步可做任务。
|
||||
|
||||
## 任务 / 进度 / 当前状态
|
||||
|
||||
- `06-tasks.md` 维护任务看板:任务 ID、依赖、验收要点和状态。
|
||||
- `../progress.md` 维护执行进度:每轮实际做了什么、跑了什么验证、遇到什么阻塞、做了什么决策。
|
||||
- `current-state.md` 维护当前快照:当前目录、可运行命令、已完成摘要和下一个可领取任务。
|
||||
|
||||
## 维护原则
|
||||
|
||||
- 需求变化先改文档,再改代码。
|
||||
- CDP 交互的关键事实(选择器、就绪判断、上传/拖拽方式)一旦定稿,写入 `04-architecture.md`,代码不得另起一套。
|
||||
- 涉及账号、登录凭证、Cookie 时,文档与代码只用占位符或路径引用,绝不写入真实值。
|
||||
- agent 开始新任务前,必须从 `00-ai-start-here.md` 进入。
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
# 模块 / CLI 合约
|
||||
|
||||
> 本项目无后端 API,全部为**本地 Python 模块合约**。实现前可细化,但不要另起一套不兼容接口。
|
||||
|
||||
## 通用约定
|
||||
|
||||
- 形态:本地函数 + 子进程(Chrome)+ CDP(`127.0.0.1:<port>`)+ SQLite + openpyxl + AI 服务调用。
|
||||
- 编码:UTF-8;传 Chrome / `setFileInputFiles` 的路径为 **Windows 绝对路径**。
|
||||
- 凭证:登录态在 user-data-dir;密码、AI Key 加密存于 config/DB,不出现在日志/导出明文。
|
||||
- 失败处理:抛带中文说明的异常或返回状态字段;GUI 负责提示,不静默吞错。
|
||||
|
||||
## appconfig 模块(`appconfig.py`,待建)
|
||||
|
||||
读写 `config.json`(schema 见 [架构 5.1](04-architecture.md))。
|
||||
|
||||
```python
|
||||
load_config(path="config.json") -> dict # 不存在则写默认
|
||||
chrome_path() -> str
|
||||
user_data_root() -> str
|
||||
image_dir() -> str
|
||||
db_path() -> str
|
||||
ai_config() -> dict # default_text_model/default_image_model/
|
||||
# title_concurrency/image_concurrency/retry/jpg_quality/
|
||||
# resolution/resolution_timeouts
|
||||
response_timeout() -> int # = resolution_timeouts[resolution](返回超时,随分辨率)
|
||||
|
||||
# AI 模型清单 config/ai_models.json(含密钥;CRUD 由 ⑤ 设置)
|
||||
list_ai_models(category=None) -> list[dict] # category=text/image 过滤;含 connect_timeout_seconds 等
|
||||
add_ai_model(model) -> None # name 唯一校验
|
||||
update_ai_model(name, **fields) -> None
|
||||
delete_ai_model(name) -> None # 至少各留一个 text+image;删到剩一禁用
|
||||
test_ai_model(name) -> dict # 「测试连接」:用 key/url/model 发最小请求 -> {ok, error}
|
||||
get_model(name) -> dict # 解密 api_key 供调用
|
||||
```
|
||||
|
||||
## db 模块(`db.py`,待建)
|
||||
|
||||
SQLite 读写,表见 [架构 5.2](04-architecture.md)。
|
||||
|
||||
```python
|
||||
init_db(path)
|
||||
# 账号
|
||||
list_accounts() -> list[Account]
|
||||
get_account_by_alias(alias) -> Account|None
|
||||
add_account(account_name, alias, region_host, debug_port, password_enc=None, note=None) -> Account
|
||||
update_account(alias, **fields) -> None
|
||||
delete_account(alias) -> None
|
||||
# 任务 / 各阶段结果
|
||||
insert_tasks(batch_id, rows) -> int # 写输入列
|
||||
list_tasks(batch_id=None, stage=None) -> list[Task]
|
||||
set_collected(task_id, old_title, old_cover_path) -> None # 采集结果,立即写
|
||||
set_generated(task_id, new_title, new_cover_path) -> None # AI 结果,立即写
|
||||
set_applied(task_id, committed, error=None) -> None # 更新结果,立即写
|
||||
```
|
||||
|
||||
`stage` 随各 set_* 推进(imported→collected→generated→applied);任意步失败写 `error` 且 stage 标 failed/skipped。无 confirmed 阶段。
|
||||
|
||||
## excel 模块(`excel.py`,待建,依赖 openpyxl)
|
||||
|
||||
```python
|
||||
import_tasks(file_paths: list[str]) -> dict
|
||||
# 只解析【输入列】:账号名、别名、商品id(+ source_file);输出列运行时回写
|
||||
# -> {"rows": [...], "stats": {"files": int, "total": int, "valid": int, "invalid": int}}
|
||||
# invalid = 缺别名/商品id 或脏数据的行
|
||||
|
||||
match_summary(rows: list[dict], accounts: list) -> dict
|
||||
# 用 accounts 的别名对 rows 做匹配统计(导入汇总栏用)
|
||||
# -> {"matched": int, "unmatched": int, "by_account": {别名: 行数}, "unmatched_aliases": [..]}
|
||||
|
||||
write_back(batch_id, excel_path) -> str
|
||||
# 把【旧标题/旧封面/新标题/新封面/更新状态】批量回写到【原 Excel】
|
||||
# 原文件被占用(锁) → 抛错,调用方提示“请关闭后重试”,或改用 export_copy
|
||||
export_copy(batch_id, out_path) -> str # 退路:另存新结果文件,不动原文件
|
||||
```
|
||||
|
||||
列模板见 [架构 5.3](04-architecture.md);别名以“别名”列为权威。
|
||||
|
||||
## config 模块(`config.py`,待建)
|
||||
|
||||
```python
|
||||
make_slug(alias) -> str # 别名→唯一 slug [a-z0-9_]
|
||||
ensure_user_data_dir(slug) -> str # chrome_user_data_dir/<slug> 绝对路径,按需创建
|
||||
```
|
||||
|
||||
## chrome 模块(`chrome.py`,待建)
|
||||
|
||||
```python
|
||||
build_launch_args(account) -> list[str] # chrome + --remote-debugging-port + --remote-allow-origins=* + --user-data-dir
|
||||
launch_chrome(account) -> subprocess.Popen
|
||||
wait_debug_ready(port, timeout=60) -> bool
|
||||
is_running(port) -> bool
|
||||
create_shortcut(account, dest_dir=None) -> str # 可选 .lnk,PowerShell WScript.Shell
|
||||
```
|
||||
|
||||
## cdp 模块(`cdp.py`,已实现)
|
||||
|
||||
```python
|
||||
CDP_HOST: str
|
||||
http_get(path); find_product_tab(item_id); create_tab(url)
|
||||
class CDP: send/ev/val/object_id/drag/close # suppress_origin、trust_env=False
|
||||
```
|
||||
|
||||
## editor 模块(`editor.py`,待建,重构自现有脚本)
|
||||
|
||||
```python
|
||||
is_logged_in(account) -> bool # 重定向登录页或缺 SPC_ST → False
|
||||
open_product(account, item_id) -> CDP # 连端口、导航商品页、等就绪
|
||||
|
||||
# 采集(只读)
|
||||
read_title(cdp) -> str
|
||||
read_cover_src(cdp) -> str # 第一张 itembox 的 img.src
|
||||
download_cover(src, out_path) -> str # 下载旧封面到本地
|
||||
collect(account, task) -> dict # -> {old_title, old_cover_path}
|
||||
|
||||
# 应用
|
||||
change_title(cdp, new_title) -> dict # {ok, value, modelvalue},要求三者相等
|
||||
replace_cover(cdp, image_win_path) -> dict # 上传→等CDN→拖第一位;满9张先删第一张
|
||||
click_update(cdp) -> dict # {clicked, reason};禁用则记失败
|
||||
apply_task(account, task) -> dict # 对已生成任务:换标题+换封面+点「更新」提交(恒提交)
|
||||
# -> {committed, error}
|
||||
```
|
||||
|
||||
## ai 模块(`ai.py`,待建,外部 AI,服务商待定)
|
||||
|
||||
```python
|
||||
gen_title(title_prompt, old_title, retry=2) -> str
|
||||
# 文本生成:提示词 + 旧标题 → 新标题
|
||||
|
||||
gen_cover(cover_prompt, old_cover_path, out_path, resolution, jpg_quality, retry=2) -> str
|
||||
# 图像生成(image-to-image):提示词 + 旧封面 → 新封面,按 resolution 生成、jpg_quality 存盘,返回路径
|
||||
|
||||
generate_batch(tasks, prompts, ai_cfg, on_progress, should_stop) -> None
|
||||
# 编排:先以 title_concurrency 线程池并发跑 gen_title,再以 image_concurrency 并发跑 gen_cover
|
||||
# 每条完成即 db.set_generated(实时落库);should_stop() 为真则取消未开始项
|
||||
# on_progress(标题完成数, 封面完成数, 失败数) 回调刷新进度
|
||||
```
|
||||
|
||||
要点:
|
||||
|
||||
- 标题用 `default_text_model`、封面用 `default_image_model`(`appconfig.get_model` 取定义,含 url/key/api_type)。
|
||||
- 连接超时 = 模型 `connect_timeout_seconds`;**返回超时 = `appconfig.response_timeout()`(随分辨率:512/1k/2k/4k → 180/240/360/600)**。
|
||||
- 并发数/重试/分辨率/jpg 质量来自 `appconfig.ai_config()`;Key 加密存、不入日志。
|
||||
- 标题快、图片慢:分两段、各用各自并发数;失败按 `retry` 重试,仍失败记 error 不阻塞其余。
|
||||
- 调用有成本与失败可能:超时、限流、内容安全拒绝都要返回明确错误。
|
||||
- 生成结果**直接用于 ③ 更新**(无人工确认);本地留档 + 回写 Excel 供追溯。
|
||||
|
||||
## prompts 模块(`prompts.py`,待建)
|
||||
|
||||
```python
|
||||
# 标题提示词:单文件
|
||||
load_title_prompt(path="title_prompt.txt") -> str # 启动回显;缺失返回 ""
|
||||
save_title_prompt(text, path="title_prompt.txt") -> None # 「保存」按钮
|
||||
|
||||
# 封面提示词:多模板(prompts/cover/<名称>.txt)
|
||||
list_cover_templates() -> list[str] # 模板名列表(下拉用)
|
||||
load_cover_template(name) -> str
|
||||
save_cover_template(name, text) -> None # 保存 / 另存为
|
||||
rename_cover_template(old, new) -> None # 重名校验,重复则报错
|
||||
delete_cover_template(name) -> None # 删除(二次确认由 GUI 负责)
|
||||
|
||||
# 变量替换
|
||||
render_prompt(template_text, task) -> str
|
||||
# 占位符 {旧标题}/{新标题}/{商品id}/{店铺} → 该任务真实值;预览与生成时调用
|
||||
```
|
||||
|
||||
要点:
|
||||
|
||||
- 「插入标题」在封面提示词光标处插入 `{新标题}`;「预览」对选中任务调用 `render_prompt` 后展示。
|
||||
- 生成封面时 `gen_cover` 的 prompt = `render_prompt(当前封面模板, task)`。
|
||||
- 模板与 `title_prompt.txt` 均为可手改的纯文本文件。
|
||||
|
||||
## CLI / 触发合约(现有脚本,过渡期保留)
|
||||
|
||||
```bash
|
||||
python prototypes/demo.py # 单账号闭环:改标题+换封面,不提交
|
||||
set AUTO=1 && python prototypes/demo.py
|
||||
set UPDATE=1 && python prototypes/demo.py # 走完点击「更新」提交
|
||||
```
|
||||
|
||||
环境变量:`CDP_HOST`、`ITEM_ID`、`IMG_WIN`、`NEW_TITLE`、`AUTO`、`UPDATE`。
|
||||
|
||||
## 待实现时确认
|
||||
|
||||
- AI 服务商/模型/计费;图像 image-to-image 能力与合规。
|
||||
- 密码、AI Key 加密的密钥来源(机器派生 / 主口令)。
|
||||
- 满 9 张删除封面的确认框选择器(需实测)。
|
||||
- Excel 缺列/脏数据容错(整文件拒绝 vs 逐行跳过)。
|
||||
- 旧封面下载的图片格式/扩展名处理。
|
||||
@@ -0,0 +1,85 @@
|
||||
# 当前实现状态
|
||||
|
||||
> 本文是可覆盖的当前快照,记录代码与任务看板的现实状态。
|
||||
> 历史执行流水追加到 [`../progress.md`](../progress.md),不在本文重复完整日志。
|
||||
|
||||
## 当前快照
|
||||
|
||||
- 日期:2026-06-25
|
||||
- 阶段:单账号 CDP 流程已验证;多账号管理 + 5 Tab GUI + Excel/AI 流水线为既定设计,尚未开始编码。
|
||||
- 技术栈:Python 3.10+,自研 CDP(websocket-client + requests),SQLite(sqlite3)+ `config.json` + openpyxl + AI(服务商待定),GUI Tkinter 5 Tab(待最终确认)。
|
||||
- 生产代码:尚无 `appconfig/db/excel/config/chrome/editor/gui`;现有为验证脚本 + `cdp.py`。
|
||||
- 测试:以 `py_compile` + 在测试商品上手动跑 `prototypes/demo.py` 为主,无自动化测试。
|
||||
- 数据:无 `config.json`、`cmshopee.db`、`chrome_user_data_dir/`(待 Phase 0/1 建立)。
|
||||
|
||||
## 既定设计要点(文档已定)
|
||||
|
||||
- GUI:**5 Tab 流水线**,顺序 ① 导入采集 → ② AI生成 → ③ 更新shopee → ④ 账号管理 → ⑤ 设置。
|
||||
- 流水线阶段:imported → collected(采集旧标题/旧封面+下载+回写)→ generated(AI 提示词生成新标题/新封面)→ applied(改 Shopee 并直接提交)。**无 confirmed、无提交开关。**
|
||||
- 存储:`config.json`(应用设置含 AI Key)+ SQLite `cmshopee.db`(账号/任务/各阶段结果)+ openpyxl(Excel)+ 本地 `images/`(旧/新封面)。
|
||||
- 多账号隔离:每账号独立 user-data-dir(非 profile)。
|
||||
- 账号↔任务绑定:以 Excel“别名”列为权威;未匹配略过,结束弹窗汇总。
|
||||
- 执行:多账号串行、单条失败继续;③ 执行即点「更新」提交线上(无确认、无开关)。
|
||||
- AI:服务商待定;生成内容直接用于更新,本地留档+回写 Excel 供追溯。
|
||||
- 登录:人工登录 + 程序检测,不自动登录;首次未配账号/未登录时 ① ③ 禁用并引导去④。
|
||||
|
||||
## 当前目录要点
|
||||
|
||||
| 路径 | 状态 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `docs/` | 已有 | 本 harness coding 文档集合 |
|
||||
| `cdp.py` | 已有 | CDP 底座:连接/找 tab/开 tab/执行 JS/拖拽(正式模块,留根目录) |
|
||||
| `prototypes/` | 已有 | 已验证原型/探查脚本(demo/set_title/set_cover/get_title/cookies/inspect_images/grab/1.py),逻辑待并入 `editor.py` 后清理;见 `prototypes/README.md` |
|
||||
| `chrome-remote-debug-lan.md` | 已有 | WSL→Windows CDP 转发排查记录 |
|
||||
| `appconfig.py` / `db.py` / `excel.py` / `config.py` / `chrome.py` / `editor.py` / `gui.py` | 待建 | Phase 0-3 产出 |
|
||||
| `config.json` / `cmshopee.db` / `chrome_user_data_dir/` | 待建 | 含配置/业务/凭证,须 gitignore |
|
||||
|
||||
## 已验证能力(单账号)
|
||||
|
||||
- CDP 连接 Chrome、遍历 tab、读取 shopee.tw Cookie。
|
||||
- 找到/新建商品详情页 tab,等编辑器就绪。
|
||||
- 改标题:原生 setter + 派发事件,`value` 与 `modelvalue` 双等于新值。
|
||||
- 换封面:`setFileInputFiles` 上传(`.shopee-image-manager__upload input[type=file]`)→ 等 CDN 链接 → `Input.dispatchMouseEvent` 拖到第一位(落点 `first.left - 0.30*w`)。
|
||||
- 「更新」按钮:可点才点,禁用态识别;默认不提交。
|
||||
|
||||
## 任务看板状态
|
||||
|
||||
任务状态以 [`06-tasks.md`](06-tasks.md) 为准,历史记录见 [`../progress.md`](../progress.md)。
|
||||
|
||||
- 已完成:无(任务均为 TODO;单账号能力以脚本形式存在,待 T-001 模块化)。
|
||||
- 正在进行:无。
|
||||
- 下一个可领取任务:Phase 0 的 **T-001(`editor.py` 含采集)/ T-002(`appconfig.py`+`config.json`)/ T-003(`db.py`+SQLite)**——三者无相互依赖,可任选其一起步。
|
||||
|
||||
## 当前可运行内容
|
||||
|
||||
```bash
|
||||
# 语法检查
|
||||
python -m py_compile *.py
|
||||
|
||||
# 单账号闭环(不提交线上)
|
||||
python prototypes/demo.py # 分步(需根目录 cdp.py 可导入)
|
||||
set AUTO=1 && python prototypes/demo.py # 自动(cmd)
|
||||
|
||||
# 真实提交(谨慎)
|
||||
set UPDATE=1 && python prototypes/demo.py
|
||||
```
|
||||
|
||||
前置条件:
|
||||
|
||||
- Chrome 已用某 user-data-dir 带 `--remote-debugging-port` + `--remote-allow-origins=*` 启动并登录 Shopee。
|
||||
- 已 `pip install websocket-client requests`。
|
||||
- 默认连 `127.0.0.1:9222`(开发期可用 `CDP_HOST` 指向 WSL 转发的 `192.168.0.224:9333`)。
|
||||
|
||||
## 开始编码前检查
|
||||
|
||||
1. 读仓库级 `AGENTS.md` / `CLAUDE.md`(如有)。
|
||||
2. 读 `docs/00-ai-start-here.md`。
|
||||
3. 读 `docs/05-coding-rules.md` 与 `docs/04-architecture.md` 第四节。
|
||||
4. 在 `docs/06-tasks.md` 取第一个 `TODO` 且依赖 `DONE` 的任务。
|
||||
5. 将该任务状态改为 `DOING`。
|
||||
|
||||
## 维护规则
|
||||
|
||||
- 新建 `config/chrome/editor/gui` 或改 CDP 选择器后,更新本文与 `04-architecture.md`。
|
||||
- 任务状态变化同步 [`06-tasks.md`](06-tasks.md);执行记录追加 [`../progress.md`](../progress.md)。
|
||||
- 本文只保留当前快照,不保留完整历史。
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
# 界面与流程结构
|
||||
|
||||
> 桌面工具,无前端路由。用 **5 Tab GUI(Tkinter `ttk.Notebook`)+ 流水线** 约定界面职责与导航。
|
||||
|
||||
## Tab 顺序与职责(工作流优先)
|
||||
|
||||
```
|
||||
① 导入采集 │ ② AI生成 │ ③ 更新shopee │ ④ 账号管理 │ ⑤ 设置
|
||||
```
|
||||
|
||||
| Tab | 职责 | 风险 |
|
||||
| --- | --- | --- |
|
||||
| ① 导入采集 | 导入多个 Excel;任务列表;**采集**商品当前的旧标题/旧封面(只读),封面图下载本地;回写 Excel 旧字段 | 只读,低 |
|
||||
| ② AI生成 | 左侧标题/封面**提示词**;右侧按批次/店铺筛选任务列表;AI 生成新标题/新封面;双击看新旧封面 | 不触线上,中 |
|
||||
| ③ 更新shopee | 对**已生成**任务打开编辑页换标题+封面并**直接点「更新」提交**;结果回写 Excel | **写线上,高** |
|
||||
| ④ 账号管理 | Shopee 账号(账号名/别名/数据目录/端口/密码加密/登录状态);启动登录、检测登录、生成快捷方式 | 中 |
|
||||
| ⑤ 设置 | AI 模型/API Key、本地图片目录、Chrome 路径、默认端口等 | — |
|
||||
|
||||
任务的**阶段状态**贯穿各 Tab:`imported → collected → generated → applied`(或 `failed/skipped`)。**无人工确认、无提交开关**。各 Tab 聚焦各自阶段的列与按钮,但操作同一批任务(同一 batch)。
|
||||
|
||||
## 首次使用引导保护
|
||||
|
||||
- ① 导入采集 与 ③ 更新shopee 都依赖**账号已配置且已登录**(在 ④ 账号管理)。
|
||||
- 当无账号 / 账号未登录时:相关执行按钮**禁用**,并提示「请先到『账号管理』配置账号并登录」。
|
||||
- 老用户账号已就绪则无感。
|
||||
|
||||
## ① 导入采集
|
||||
|
||||
```
|
||||
┌ 导入采集 ─────────────────────────────────────────────────────┐
|
||||
│ [导入 Excel…] [移除] [清空] │
|
||||
│ ▸ 3 文件 · 128 行 · 有效125/无效3 · 匹配123 · 未匹配5⚠ │ ← 导入汇总栏
|
||||
│ 匹配明细:女装店60 · my主店40 · 饰品店23 │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ 账号名 别名 商品ID 阶段 旧标题 旧封面 │ │
|
||||
│ │ 主店A 女装店 51100639510 待采集 — — │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
│ [▶ 采集旧标题/旧封面] [■停止] [回写旧数据到 Excel] │
|
||||
│ 日志:逐条 文件→匹配账号、采集结果 │
|
||||
└───────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- 导入:openpyxl 解析**输入列**(账号名/别名/商品id)入 SQLite。
|
||||
- **导入汇总栏**(导入后即时刷新,跑采集前的校验关口):显示 文件数、解析行数(原始数据量)、有效/无效行、匹配账号行数(按账号细分)、未匹配行数。未匹配/无效数字标红可点,点击在列表筛出便于定位纠错。
|
||||
- 采集:用该账号 Chrome 只读打开商品页,读旧标题、下载旧封面到本地图片目录,写 `old_title/old_cover_path`,stage=collected。
|
||||
- 回写:采集完把旧标题/旧封面路径批量回写原 Excel(原文件被锁→提示重试/另存)。
|
||||
- 别名未匹配账号 / 账号未登录 → 该行 skipped 并记原因。
|
||||
|
||||
## ② AI生成
|
||||
|
||||
左右布局:左侧约 1/4 放提示词,右侧放筛选 + 任务列表。
|
||||
|
||||
```
|
||||
┌ AI生成 ───────────────────────────────────────────────────────┐
|
||||
│ ┌─左 ~1/4─┐ ┌──────────────── 右 3/4 ──────────────────────┐ │
|
||||
│ │标题提示词│ │ 批次[本次▼] 店铺[全部▼] 状态[全部▼] [筛选] │ │
|
||||
│ │[ ]│ │ ┌──────────────────────────────────────────┐ │ │
|
||||
│ │[ ]│ │ │ 店铺名 商品id 旧标题 新标题 状态 │ │ │
|
||||
│ │ │ │ │ 女装店 511..639 …T恤 …百搭 已生成 │ │ │
|
||||
│ │封面提示词│ │ │ 女装店 511..640 … — 待生成 │ │ │
|
||||
│ │[ ]│ │ └──────────────────────────────────────────┘ │ │
|
||||
│ │[ ]│ │ (双击某行 → 弹窗看 旧封面 | 新封面) │ │
|
||||
│ └──────────┘ └───────────────────────────────────────────────┘ │
|
||||
│ 进度:标题30/30 · 封面12/30 · 失败1 [▶ 开始生成] [■停止] │
|
||||
└───────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- 左侧(提示词管理,上下两块):
|
||||
- **标题提示词**:多行输入 + 「保存」(写 `title_prompt.txt`);启动时自动加载回显。
|
||||
- **封面提示词**:模板下拉(读 `prompts/cover/*.txt`)+ 图标工具栏(新建/保存/另存为/重命名/删除)+ 多行输入 + 「插入标题」(插 `{新标题}`)/「预览」(变量替换后查看)。
|
||||
- 变量:`{旧标题}`/`{新标题}`/`{商品id}`/`{店铺}`,生成前按任务替换。
|
||||
- 右上:按导入批次 / 店铺 / 状态筛选任务。
|
||||
- 右下:任务列表(店铺名、商品id、旧标题、新标题、状态);**双击某条 → 弹窗展示旧封面 | 新封面**(纯查看)。
|
||||
- 底部**单个「开始生成」+「停止」**:开始生成 = **先按 `title_concurrency` 并发生成标题,接着按 `image_concurrency` 并发生成封面**;进度实时显示 标题/封面/失败 计数。
|
||||
- 生成参数(标题/图片并发数、失败重试、分辨率、jpg 质量、模型/Key)在 **⑤ 设置**,不在本 Tab 重复。
|
||||
- 每条/每张完成即写库;「停止」取消未开始项,可再次「开始生成」对剩余继续。
|
||||
- **无人工确认环节**;新标题直接用 AI 输出(不可编辑);可选对单行 `重生成`。生成完即可进入 ③。
|
||||
|
||||
## ③ 更新shopee
|
||||
|
||||
```
|
||||
┌ 更新shopee ───────────────────────────────────────────────────┐
|
||||
│ 批次[本次▼] 店铺[全部▼] 状态[已生成▼] [筛选] │
|
||||
│ ⚠ 「开始更新」对【当前筛选结果】执行,即改标题+换封面并直接提交 │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ 店铺 商品ID 新标题 新封面 阶段 结果 │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
│ [▶ 开始更新] [■停止] [回写结果到 Excel] │
|
||||
└───────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- 顶部**按批次 / 店铺 / 状态筛选**(与 ①②一致);「开始更新」作用于**当前筛选结果**,是一道范围控制。
|
||||
- 店铺筛选:建议**逐店铺更新**(每店铺需先启动其 Chrome 并登录)。
|
||||
- 状态筛选:`已生成` 只跑未更新的;`失败` 用于**失败重试**;`已更新成功/略过` 仅查看。
|
||||
- 对筛选出的**已生成(generated)任务**执行:打开编辑页换标题+换封面 → **总是点「更新」提交**(无开关)。
|
||||
- 串行、单条失败继续;每条立即写回 SQLite(committed/状态/error)。
|
||||
- 全部完成 → 把新标题/新封面/更新状态批量回写原 Excel;弹窗汇总。
|
||||
|
||||
## ④ 账号管理
|
||||
|
||||
```
|
||||
┌ 账号管理 ─────────────────────────────────────────────────────┐
|
||||
│ 账号名 别名 地区 端口 登录状态 备注 │
|
||||
│ 主店A 女装店 seller.shopee.tw 9222 ●已登录 │
|
||||
│ [+新增][✎编辑][🗑删除] [▶启动并登录][🔄检测登录][⧉快捷方式] │
|
||||
└───────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
账号弹窗字段:账号名、别名(唯一,Excel 用它匹配)、地区域名、调试端口、密码(加密仅参考)、备注;配置目录按别名生成 slug 只读显示。
|
||||
|
||||
## ⑤ 设置
|
||||
|
||||
- AI:服务商 / 文本模型 / 图像模型 / API Key(加密存)。
|
||||
- AI 生成参数:**标题并发数、图片并发数、失败重试次数、分辨率、jpg 质量**。
|
||||
- 本地图片目录(旧封面下载、新封面生成的存放根目录)。
|
||||
- Chrome 路径、默认调试端口 / 端口范围、超时、DB 路径。
|
||||
|
||||
## 流程导航
|
||||
|
||||
```text
|
||||
④ 账号管理:配账号 + 启动登录(首次必做)
|
||||
│
|
||||
① 导入采集:导入 Excel → 采集旧标题/旧封面 → 回写旧字段
|
||||
│
|
||||
② AI生成:提示词 → 生成新标题/新封面(无确认)
|
||||
│
|
||||
③ 更新shopee:对已生成任务换标题+封面 → 直接点「更新」提交 → 回写结果
|
||||
```
|
||||
|
||||
- 未配账号/未登录:① ③ 的执行按钮禁用并提示去 ④。
|
||||
- 已生成的任务即可进 ③;③ 执行即提交线上(无确认、无开关)。
|
||||
- 任意步骤失败:记入该任务、日志标明,不影响其他任务。
|
||||
|
||||
## 组件建议(Tkinter)
|
||||
|
||||
| 组件 | 归属 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `MainNotebook` | 根窗口 | 5 个 Tab |
|
||||
| `CollectTab` | ① | 导入、任务表、采集、回写 |
|
||||
| `GenerateTab` | ② | 左提示词 + 右筛选/任务列表、双击看新旧封面、开始生成 |
|
||||
| `ApplyTab` | ③ | 已生成任务、换标题+封面+直接提交、回写 |
|
||||
| `AccountsTab` | ④ | 账号增删改、启动登录 |
|
||||
| `SettingsTab` | ⑤ | AI/目录/Chrome 配置 |
|
||||
|
||||
> 采集、生成、更新都是耗时操作,放后台线程,避免界面卡死。
|
||||
@@ -0,0 +1,14 @@
|
||||
# UI 效果图(SVG 线框)
|
||||
|
||||
cmshopee 5 Tab 桌面应用的界面效果图。SVG 可用浏览器直接打开查看。
|
||||
|
||||
| 文件 | 内容 |
|
||||
| --- | --- |
|
||||
| [overview-pipeline.svg](overview-pipeline.svg) | 5 Tab 流水线总览(stage:imported→collected→generated→confirmed→applied) |
|
||||
| [tab1-import-collect.svg](tab1-import-collect.svg) | ① 导入采集:导入 Excel、任务列表、采集旧标题/旧封面、回写、日志 |
|
||||
| [tab2-ai-generate.svg](tab2-ai-generate.svg) | ② AI生成:提示词、生成、新旧对照预览、确认/重生成 |
|
||||
| [tab3-update-shopee.svg](tab3-update-shopee.svg) | ③ 更新shopee:已确认任务、批次提交开关、执行结果、汇总 |
|
||||
| [tab4-accounts.svg](tab4-accounts.svg) | ④ 账号管理:账号列表、启动登录、检测登录、账号编辑弹窗 |
|
||||
| [tab5-settings.svg](tab5-settings.svg) | ⑤ 设置:AI 模型/Key、路径、端口 |
|
||||
|
||||
> 仅为线框效果图,最终样式以实现为准。界面职责与流程见 [../routes.md](../routes.md)。
|
||||
@@ -0,0 +1,91 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="940" height="420" viewBox="0 0 940 420" font-family="'Segoe UI','Microsoft YaHei',sans-serif">
|
||||
<defs>
|
||||
<marker id="arr" markerWidth="10" markerHeight="10" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" fill="#9aa3b2"/></marker>
|
||||
<style>
|
||||
.bg{fill:#f4f6f9;stroke:#d3d9e2}
|
||||
.h1{fill:#1f2733;font-size:18px;font-weight:700}
|
||||
.sub{fill:#8a92a0;font-size:12px}
|
||||
.card{fill:#fff;stroke:#cdd6e2}
|
||||
.tt{fill:#2f6fed;font-size:13px;font-weight:700}
|
||||
.st{fill:#1f2733;font-size:12px;font-weight:600}
|
||||
.ds{fill:#5c6573;font-size:11px}
|
||||
.stage{fill:#eaf3ff;stroke:#cfe2fb}
|
||||
.stageT{fill:#1f4e91;font-size:11px;font-weight:600}
|
||||
.gate{fill:#fff7e6;stroke:#f0d089}
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="bg" x="0.5" y="0.5" width="939" height="419" rx="10"/>
|
||||
<text class="h1" x="24" y="40">cmshopee · 5 Tab 流水线总览</text>
|
||||
<text class="sub" x="24" y="60">工作流优先顺序 · 对象是 Excel 里的每一行任务 · 状态字段 stage 贯穿全程</text>
|
||||
|
||||
<!-- stage chips row (无 confirmed、无提交开关) -->
|
||||
<g transform="translate(24,84)">
|
||||
<rect class="stage" x="0" y="0" width="200" height="26" rx="13"/><text class="stageT" x="66" y="17">imported</text>
|
||||
<rect class="stage" x="230" y="0" width="200" height="26" rx="13"/><text class="stageT" x="292" y="17">collected</text>
|
||||
<rect class="stage" x="460" y="0" width="200" height="26" rx="13"/><text class="stageT" x="520" y="17">generated</text>
|
||||
<rect class="stage" x="690" y="0" width="200" height="26" rx="13"/><text class="stageT" x="742" y="17">applied(直接提交)</text>
|
||||
</g>
|
||||
|
||||
<!-- 5 tab cards -->
|
||||
<g transform="translate(24,140)">
|
||||
<!-- card template -->
|
||||
<g>
|
||||
<rect class="card" x="0" y="0" width="168" height="210" rx="8"/>
|
||||
<text class="tt" x="14" y="28">① 导入采集</text>
|
||||
<text class="st" x="14" y="52">读 Excel 输入列</text>
|
||||
<text class="ds" x="14" y="72">账号名/别名/商品id</text>
|
||||
<text class="st" x="14" y="98">只读采集</text>
|
||||
<text class="ds" x="14" y="118">抓旧标题</text>
|
||||
<text class="ds" x="14" y="136">下载旧封面到本地</text>
|
||||
<text class="ds" x="14" y="154">回写 Excel 旧字段</text>
|
||||
<text class="ds" x="14" y="190" fill="#20a464">→ collected</text>
|
||||
</g>
|
||||
<g transform="translate(193,0)">
|
||||
<rect class="card" x="0" y="0" width="168" height="210" rx="8"/>
|
||||
<text class="tt" x="14" y="28">② AI生成</text>
|
||||
<text class="st" x="14" y="52">提示词输入</text>
|
||||
<text class="ds" x="14" y="72">标题/封面分开</text>
|
||||
<text class="st" x="14" y="98">AI 生成</text>
|
||||
<text class="ds" x="14" y="118">旧标题→新标题</text>
|
||||
<text class="ds" x="14" y="136">旧封面→新封面</text>
|
||||
<text class="ds" x="14" y="156">双击看新旧封面</text>
|
||||
<text class="ds" x="14" y="190" fill="#20a464">→ generated(无确认)</text>
|
||||
</g>
|
||||
<g transform="translate(386,0)">
|
||||
<rect class="card" x="0" y="0" width="168" height="210" rx="8"/>
|
||||
<text class="tt" x="14" y="28">③ 更新shopee</text>
|
||||
<text class="st" x="14" y="52">已生成任务</text>
|
||||
<text class="ds" x="14" y="72">打开编辑页</text>
|
||||
<text class="ds" x="14" y="90">换标题 + 换封面</text>
|
||||
<text class="ds" x="14" y="108">直接点更新提交</text>
|
||||
<text class="st" x="14" y="134">串行 · 失败继续</text>
|
||||
<text class="ds" x="14" y="154">实时写库 + 回写Excel</text>
|
||||
<text class="ds" x="14" y="190" fill="#20a464">→ applied</text>
|
||||
</g>
|
||||
<g transform="translate(579,0)">
|
||||
<rect class="card" x="0" y="0" width="168" height="210" rx="8"/>
|
||||
<text class="tt" x="14" y="28">④ 账号管理</text>
|
||||
<text class="ds" x="14" y="52">账号名/别名/端口</text>
|
||||
<text class="ds" x="14" y="72">密码加密·仅参考</text>
|
||||
<text class="ds" x="14" y="92">独立 user-data-dir</text>
|
||||
<text class="ds" x="14" y="112">启动并登录(人工)</text>
|
||||
<text class="ds" x="14" y="132">检测登录状态</text>
|
||||
<text class="ds" x="14" y="184" fill="#8a92a0">配一次,少改</text>
|
||||
</g>
|
||||
<g transform="translate(772,0)">
|
||||
<rect class="card" x="0" y="0" width="144" height="210" rx="8"/>
|
||||
<text class="tt" x="14" y="28">⑤ 设置</text>
|
||||
<text class="ds" x="14" y="52">AI 模型/Key</text>
|
||||
<text class="ds" x="14" y="72">本地图片目录</text>
|
||||
<text class="ds" x="14" y="92">Chrome 路径</text>
|
||||
<text class="ds" x="14" y="112">端口范围</text>
|
||||
<text class="ds" x="14" y="184" fill="#8a92a0">全局配置</text>
|
||||
</g>
|
||||
|
||||
<!-- arrows between 1-2-3 -->
|
||||
<line x1="168" y1="105" x2="191" y2="105" stroke="#9aa3b2" stroke-width="2" marker-end="url(#arr)"/>
|
||||
<line x1="361" y1="105" x2="384" y2="105" stroke="#9aa3b2" stroke-width="2" marker-end="url(#arr)"/>
|
||||
</g>
|
||||
|
||||
<text class="sub" x="24" y="394">① ③ 依赖账号已配置且已登录(④);未配/未登录则执行按钮禁用并提示去④。存储:config.json + SQLite + 本地 images/。</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.1 KiB |
@@ -0,0 +1,117 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="940" height="620" viewBox="0 0 940 620" font-family="'Segoe UI','Microsoft YaHei',sans-serif">
|
||||
<defs>
|
||||
<style>
|
||||
.win{fill:#f4f6f9;stroke:#d3d9e2}
|
||||
.hd{fill:#2b3a55}
|
||||
.title{fill:#fff;font-size:14px;font-weight:600}
|
||||
.tab{fill:#e8ebf0}
|
||||
.tabA{fill:#f4f6f9}
|
||||
.tabT{fill:#5c6573;font-size:13px}
|
||||
.tabTA{fill:#2f6fed;font-size:13px;font-weight:600}
|
||||
.sec{fill:#1f2733;font-size:13px;font-weight:600}
|
||||
.mut{fill:#8a92a0;font-size:11px}
|
||||
.cell{fill:#2a2f3a;font-size:12px}
|
||||
.th{fill:#5c6573;font-size:12px;font-weight:600}
|
||||
.btnP{fill:#2f6fed}
|
||||
.btnPT{fill:#fff;font-size:12px;font-weight:600}
|
||||
.btn{fill:#fff;stroke:#c5ccd6}
|
||||
.btnT{fill:#3a4150;font-size:12px}
|
||||
.panel{fill:#fff;stroke:#e2e6ec}
|
||||
.rowL{fill:#fafbfc}
|
||||
</style>
|
||||
</defs>
|
||||
|
||||
<!-- window -->
|
||||
<rect class="win" x="0.5" y="0.5" width="939" height="619" rx="8"/>
|
||||
<rect class="hd" x="0.5" y="0.5" width="939" height="40" rx="8"/>
|
||||
<rect class="hd" x="0.5" y="20" width="939" height="21"/>
|
||||
<text class="title" x="18" y="26">cmshopee · Shopee 商品批量改标题换封面</text>
|
||||
<circle cx="888" cy="20" r="6" fill="#f5bf4f"/><circle cx="910" cy="20" r="6" fill="#5ac26a"/><circle cx="866" cy="20" r="6" fill="#e0e4ea"/>
|
||||
|
||||
<!-- tab strip -->
|
||||
<g>
|
||||
<rect class="tab" x="0.5" y="41" width="939" height="38"/>
|
||||
<rect class="tabA" x="8" y="45" width="150" height="38"/>
|
||||
<rect x="8" y="45" width="150" height="3" fill="#2f6fed"/>
|
||||
<text class="tabTA" x="34" y="68">① 导入采集</text>
|
||||
<text class="tabT" x="186" y="68">② AI生成</text>
|
||||
<text class="tabT" x="330" y="68">③ 更新shopee</text>
|
||||
<text class="tabT" x="492" y="68">④ 账号管理</text>
|
||||
<text class="tabT" x="636" y="68">⑤ 设置</text>
|
||||
</g>
|
||||
|
||||
<!-- toolbar -->
|
||||
<g transform="translate(0,94)">
|
||||
<rect class="btn" x="18" y="0" width="110" height="30" rx="6"/><text class="btnT" x="34" y="20">导入 Excel…</text>
|
||||
<rect class="btn" x="136" y="0" width="64" height="30" rx="6"/><text class="btnT" x="152" y="20">移除</text>
|
||||
<rect class="btn" x="208" y="0" width="64" height="30" rx="6"/><text class="btnT" x="224" y="20">清空</text>
|
||||
<text class="mut" x="560" y="20">别名以 Excel“别名”列匹配账号 · 未匹配→略过</text>
|
||||
</g>
|
||||
|
||||
<!-- import summary bar -->
|
||||
<g transform="translate(18,130)">
|
||||
<rect x="0" y="0" width="904" height="40" rx="6" fill="#eef4ff" stroke="#cfe0fb"/>
|
||||
<text x="14" y="18" font-size="12.5" fill="#1f4e91" font-weight="600">▸ 导入汇总</text>
|
||||
<text x="86" y="18" font-size="12.5" fill="#2a2f3a">3 文件 · 128 行 · 有效 125 ·</text>
|
||||
<text x="300" y="18" font-size="12.5" fill="#c2473d">无效 3</text>
|
||||
<text x="350" y="18" font-size="12.5" fill="#2a2f3a">· 匹配 123 ·</text>
|
||||
<text x="448" y="18" font-size="12.5" fill="#c2473d">未匹配 5 ⚠</text>
|
||||
<text x="14" y="33" font-size="11" fill="#5c6573">匹配明细:女装店 60 · my主店 40 · 饰品店 23 (点“无效/未匹配”可在列表筛出定位)</text>
|
||||
</g>
|
||||
|
||||
<!-- task table -->
|
||||
<g transform="translate(18,180)">
|
||||
<rect class="panel" x="0" y="0" width="904" height="190" rx="6"/>
|
||||
<rect x="1" y="1" width="902" height="28" fill="#eef1f6"/>
|
||||
<text class="th" x="14" y="19">账号名</text>
|
||||
<text class="th" x="110" y="19">别名</text>
|
||||
<text class="th" x="210" y="19">商品ID</text>
|
||||
<text class="th" x="340" y="19">阶段</text>
|
||||
<text class="th" x="470" y="19">旧标题</text>
|
||||
<text class="th" x="770" y="19">旧封面</text>
|
||||
<!-- rows -->
|
||||
<g font-size="12">
|
||||
<rect class="rowL" x="1" y="29" width="902" height="32"/>
|
||||
<text class="cell" x="14" y="49">主店A</text><text class="cell" x="110" y="49">女装店</text>
|
||||
<text class="cell" x="210" y="49">51100639510</text>
|
||||
<circle cx="346" cy="45" r="4" fill="#20a464"/><text class="cell" x="356" y="49">已采集</text>
|
||||
<text class="cell" x="470" y="49">…顯瘦百搭T恤</text><text class="cell" x="770" y="49">old_…01.jpg</text>
|
||||
|
||||
<text class="cell" x="14" y="81">主店A</text><text class="cell" x="110" y="81">女装店</text>
|
||||
<text class="cell" x="210" y="81">51100640000</text>
|
||||
<circle cx="346" cy="77" r="4" fill="#b6bdc7"/><text class="cell" x="356" y="81">待采集</text>
|
||||
<text class="mut" x="470" y="81">—</text><text class="mut" x="770" y="81">—</text>
|
||||
|
||||
<rect class="rowL" x="1" y="93" width="902" height="32"/>
|
||||
<text class="cell" x="14" y="113">马来店</text><text class="cell" x="110" y="113">my主店</text>
|
||||
<text class="cell" x="210" y="113">52000000001</text>
|
||||
<circle cx="346" cy="109" r="4" fill="#20a464"/><text class="cell" x="356" y="113">已采集</text>
|
||||
<text class="cell" x="470" y="113">Korean Loose Tee</text><text class="cell" x="770" y="113">old_…07.jpg</text>
|
||||
|
||||
<text class="cell" x="14" y="145" fill="#c2473d">未知店</text><text class="cell" x="110" y="145" fill="#c2473d">x别名</text>
|
||||
<text class="cell" x="210" y="145">52999999</text>
|
||||
<circle cx="346" cy="141" r="4" fill="#e7a008"/><text class="cell" x="356" y="145" fill="#c2473d">略过·别名未匹配</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- action buttons -->
|
||||
<g transform="translate(18,382)">
|
||||
<rect class="btnP" x="0" y="0" width="178" height="34" rx="6"/><text class="btnPT" x="22" y="22">▶ 采集旧标题/旧封面</text>
|
||||
<rect class="btn" x="190" y="0" width="64" height="34" rx="6"/><text class="btnT" x="208" y="22">■ 停止</text>
|
||||
<rect class="btn" x="700" y="0" width="204" height="34" rx="6"/><text class="btnT" x="724" y="22">回写旧数据到 Excel</text>
|
||||
</g>
|
||||
|
||||
<!-- log -->
|
||||
<g transform="translate(18,432)">
|
||||
<text class="sec" x="0" y="12">运行日志</text>
|
||||
<rect class="panel" x="0" y="20" width="904" height="160" rx="6"/>
|
||||
<g font-family="Consolas,monospace" font-size="11.5" fill="#586073">
|
||||
<text x="14" y="42">[10:21:03] 文件 tw01.xlsx → 匹配账号「女装店」(9222)</text>
|
||||
<text x="14" y="62">[10:21:05] 51100639510 采集旧标题:…顯瘦百搭T恤</text>
|
||||
<text x="14" y="82">[10:21:07] 下载旧封面 → images/nuzhuang_01/51100639510_old.jpg</text>
|
||||
<text x="14" y="102">[10:21:08] set_collected ✓(已写 SQLite)</text>
|
||||
<text x="14" y="122">[10:21:14] 52999999 别名「x别名」未匹配账号 → 略过</text>
|
||||
<text x="14" y="142">[10:21:20] 采集完成 3/4,已回写 Excel 旧字段</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.4 KiB |
@@ -0,0 +1,105 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="940" height="620" viewBox="0 0 940 620" font-family="'Segoe UI','Microsoft YaHei',sans-serif">
|
||||
<defs>
|
||||
<style>
|
||||
.win{fill:#f4f6f9;stroke:#d3d9e2}.hd{fill:#2b3a55}.title{fill:#fff;font-size:14px;font-weight:600}
|
||||
.tab{fill:#e8ebf0}.tabA{fill:#f4f6f9}.tabT{fill:#5c6573;font-size:13px}.tabTA{fill:#2f6fed;font-size:13px;font-weight:600}
|
||||
.sec{fill:#1f2733;font-size:13px;font-weight:600}.mut{fill:#8a92a0;font-size:11px}
|
||||
.cell{fill:#2a2f3a;font-size:12px}.th{fill:#5c6573;font-size:12px;font-weight:600}
|
||||
.btnP{fill:#2f6fed}.btnPT{fill:#fff;font-size:13px;font-weight:600}
|
||||
.btn{fill:#fff;stroke:#c5ccd6}.btnT{fill:#3a4150;font-size:12px}
|
||||
.panel{fill:#fff;stroke:#e2e6ec}.inp{fill:#fff;stroke:#c5ccd6}.ta{fill:#fbfcfe;stroke:#c5ccd6}
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="win" x="0.5" y="0.5" width="939" height="619" rx="8"/>
|
||||
<rect class="hd" x="0.5" y="0.5" width="939" height="40" rx="8"/><rect class="hd" x="0.5" y="20" width="939" height="21"/>
|
||||
<text class="title" x="18" y="26">cmshopee · Shopee 商品批量改标题换封面</text>
|
||||
<circle cx="888" cy="20" r="6" fill="#f5bf4f"/><circle cx="910" cy="20" r="6" fill="#5ac26a"/><circle cx="866" cy="20" r="6" fill="#e0e4ea"/>
|
||||
|
||||
<rect class="tab" x="0.5" y="41" width="939" height="38"/>
|
||||
<rect class="tabA" x="166" y="45" width="138" height="38"/><rect x="166" y="45" width="138" height="3" fill="#2f6fed"/>
|
||||
<text class="tabT" x="34" y="68">① 导入采集</text>
|
||||
<text class="tabTA" x="196" y="68">② AI生成</text>
|
||||
<text class="tabT" x="330" y="68">③ 更新shopee</text>
|
||||
<text class="tabT" x="492" y="68">④ 账号管理</text>
|
||||
<text class="tabT" x="636" y="68">⑤ 设置</text>
|
||||
|
||||
<!-- LEFT ~1/4: prompts management -->
|
||||
<g transform="translate(18,96)">
|
||||
<rect class="panel" x="0" y="0" width="218" height="446" rx="6"/>
|
||||
|
||||
<!-- title prompt: single file + save -->
|
||||
<text class="sec" x="14" y="24">标题提示词</text>
|
||||
<rect class="btn" x="150" y="8" width="54" height="22" rx="5"/><text class="btnT" x="164" y="23">保存</text>
|
||||
<rect class="ta" x="14" y="36" width="190" height="78" rx="6"/>
|
||||
<text class="cell" x="22" y="56" fill="#586073" font-size="11">精简标题、去「T恤」、</text>
|
||||
<text class="cell" x="22" y="72" fill="#586073" font-size="11">突出韩版加绒保暖 ≤60字</text>
|
||||
<text class="mut" x="14" y="128">启动自动加载 title_prompt.txt 回显</text>
|
||||
|
||||
<!-- cover prompt: multi-template -->
|
||||
<text class="sec" x="14" y="160">封面提示词(模板)</text>
|
||||
<rect class="inp" x="14" y="170" width="190" height="24" rx="5"/><text class="cell" x="22" y="187" font-size="11">模板:韩版女装 ▼</text>
|
||||
<!-- icon toolbar -->
|
||||
<g font-size="11">
|
||||
<rect class="btn" x="14" y="200" width="34" height="24" rx="5"/><text class="btnT" x="22" y="216">+</text>
|
||||
<rect class="btn" x="52" y="200" width="34" height="24" rx="5"/><text class="btnT" x="60" y="216">💾</text>
|
||||
<rect class="btn" x="90" y="200" width="34" height="24" rx="5"/><text class="btnT" x="96" y="216">另存</text>
|
||||
<rect class="btn" x="128" y="200" width="34" height="24" rx="5"/><text class="btnT" x="136" y="216">改名</text>
|
||||
<rect class="btn" x="166" y="200" width="38" height="24" rx="5"/><text class="btnT" x="176" y="216">删除</text>
|
||||
</g>
|
||||
<rect class="ta" x="14" y="232" width="190" height="118" rx="6"/>
|
||||
<text class="cell" x="22" y="252" fill="#586073" font-size="11">纯白背景、保留主体、</text>
|
||||
<text class="cell" x="22" y="268" fill="#586073" font-size="11">呼应标题:</text>
|
||||
<text x="22" y="284" fill="#2f6fed" font-size="11">{新标题}</text>
|
||||
<!-- insert / preview -->
|
||||
<rect class="btn" x="14" y="360" width="92" height="26" rx="6"/><text class="btnT" x="26" y="377">插入标题</text>
|
||||
<rect class="btn" x="112" y="360" width="92" height="26" rx="6"/><text class="btnT" x="138" y="377">预览</text>
|
||||
<text class="mut" x="14" y="406">变量 {旧标题}{新标题}{商品id}{店铺}</text>
|
||||
<text class="mut" x="14" y="422">模型/并发/重试/分辨率/质量 → ⑤设置</text>
|
||||
</g>
|
||||
|
||||
<!-- RIGHT: filters + list -->
|
||||
<g transform="translate(248,96)">
|
||||
<!-- filter bar -->
|
||||
<rect class="inp" x="0" y="0" width="120" height="28" rx="6"/><text class="cell" x="12" y="18">批次:本次 ▼</text>
|
||||
<rect class="inp" x="130" y="0" width="120" height="28" rx="6"/><text class="cell" x="142" y="18">店铺:全部 ▼</text>
|
||||
<rect class="inp" x="260" y="0" width="120" height="28" rx="6"/><text class="cell" x="272" y="18">状态:全部 ▼</text>
|
||||
<rect class="btn" x="390" y="0" width="64" height="28" rx="6"/><text class="btnT" x="408" y="18">筛选</text>
|
||||
<text class="mut" x="470" y="18">双击某行 → 弹窗看 旧封面 | 新封面</text>
|
||||
|
||||
<!-- table -->
|
||||
<rect class="panel" x="0" y="40" width="656" height="362" rx="6"/>
|
||||
<rect x="1" y="41" width="654" height="28" fill="#eef1f6"/>
|
||||
<text class="th" x="14" y="59">店铺名</text><text class="th" x="96" y="59">商品ID</text>
|
||||
<text class="th" x="220" y="59">旧标题</text><text class="th" x="420" y="59">新标题</text><text class="th" x="600" y="59">状态</text>
|
||||
<g font-size="12">
|
||||
<rect x="1" y="69" width="654" height="30" fill="#fafbfc"/>
|
||||
<text class="cell" x="14" y="89">女装店</text><text class="cell" x="96" y="89">51100639510</text>
|
||||
<text class="cell" x="220" y="89">…顯瘦百搭T恤</text><text class="cell" x="420" y="89" fill="#1f6f43">…顯瘦百搭</text>
|
||||
<circle cx="606" cy="85" r="4" fill="#20a464"/><text class="cell" x="616" y="89">已生成</text>
|
||||
|
||||
<text class="cell" x="14" y="119">女装店</text><text class="cell" x="96" y="119">51100640000</text>
|
||||
<text class="cell" x="220" y="119">…加絨打底衫T恤</text><text class="mut" x="420" y="119">—</text>
|
||||
<circle cx="606" cy="115" r="4" fill="#b6bdc7"/><text class="cell" x="616" y="119">待生成</text>
|
||||
|
||||
<rect x="1" y="129" width="654" height="30" fill="#fafbfc"/>
|
||||
<text class="cell" x="14" y="149">my主店</text><text class="cell" x="96" y="149">52000000001</text>
|
||||
<text class="cell" x="220" y="149">Korean Loose Tee</text><text class="cell" x="420" y="149" fill="#1f6f43">Korean Oversize Tee</text>
|
||||
<circle cx="606" cy="145" r="4" fill="#20a464"/><text class="cell" x="616" y="149">已生成</text>
|
||||
|
||||
<text class="cell" x="14" y="179">饰品店</text><text class="cell" x="96" y="179">53000000009</text>
|
||||
<text class="cell" x="220" y="179">…百搭耳環飾品</text><text class="cell" x="420" y="179" fill="#2f6fed">生成中…</text>
|
||||
<circle cx="606" cy="175" r="4" fill="#2f6fed"/><text class="cell" x="616" y="179">处理中</text>
|
||||
</g>
|
||||
<text class="mut" x="14" y="392">新标题直接用 AI 输出(不可编辑)· 可对单行右键「重生成」· 无人工确认</text>
|
||||
</g>
|
||||
|
||||
<!-- bottom action -->
|
||||
<g transform="translate(0,556)">
|
||||
<text class="sec" x="248" y="24" font-size="12">进度:</text>
|
||||
<text class="cell" x="290" y="24">标题 30/30 · 封面 12/30 ·</text>
|
||||
<text x="470" y="24" font-size="12" fill="#c2473d">失败 1</text>
|
||||
<text class="mut" x="248" y="42">先并发生成标题 → 接着并发生成封面 · 每条完成即写库 · 停止可取消未开始项</text>
|
||||
<rect class="btnP" x="690" y="8" width="130" height="38" rx="6"/><text class="btnPT" x="724" y="32">▶ 开始生成</text>
|
||||
<rect class="btn" x="828" y="8" width="92" height="38" rx="6"/><text class="btnT" x="852" y="32">■ 停止</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.5 KiB |
@@ -0,0 +1,81 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="940" height="620" viewBox="0 0 940 620" font-family="'Segoe UI','Microsoft YaHei',sans-serif">
|
||||
<defs><style>
|
||||
.win{fill:#f4f6f9;stroke:#d3d9e2}.hd{fill:#2b3a55}.title{fill:#fff;font-size:14px;font-weight:600}
|
||||
.tab{fill:#e8ebf0}.tabA{fill:#f4f6f9}.tabT{fill:#5c6573;font-size:13px}.tabTA{fill:#2f6fed;font-size:13px;font-weight:600}
|
||||
.sec{fill:#1f2733;font-size:13px;font-weight:600}.mut{fill:#8a92a0;font-size:11px}
|
||||
.cell{fill:#2a2f3a;font-size:12px}.th{fill:#5c6573;font-size:12px;font-weight:600}
|
||||
.btnP{fill:#2f6fed}.btnPT{fill:#fff;font-size:12px;font-weight:600}.btn{fill:#fff;stroke:#c5ccd6}.btnT{fill:#3a4150;font-size:12px}
|
||||
.panel{fill:#fff;stroke:#e2e6ec}.warn{fill:#fff7e6;stroke:#f0d089}
|
||||
</style></defs>
|
||||
<rect class="win" x="0.5" y="0.5" width="939" height="619" rx="8"/>
|
||||
<rect class="hd" x="0.5" y="0.5" width="939" height="40" rx="8"/><rect class="hd" x="0.5" y="20" width="939" height="21"/>
|
||||
<text class="title" x="18" y="26">cmshopee · Shopee 商品批量改标题换封面</text>
|
||||
<circle cx="888" cy="20" r="6" fill="#f5bf4f"/><circle cx="910" cy="20" r="6" fill="#5ac26a"/><circle cx="866" cy="20" r="6" fill="#e0e4ea"/>
|
||||
|
||||
<rect class="tab" x="0.5" y="41" width="939" height="38"/>
|
||||
<rect class="tabA" x="304" y="45" width="162" height="38"/><rect x="304" y="45" width="162" height="3" fill="#2f6fed"/>
|
||||
<text class="tabT" x="34" y="68">① 导入采集</text><text class="tabT" x="186" y="68">② AI生成</text>
|
||||
<text class="tabTA" x="334" y="68">③ 更新shopee</text>
|
||||
<text class="tabT" x="492" y="68">④ 账号管理</text><text class="tabT" x="636" y="68">⑤ 设置</text>
|
||||
|
||||
<!-- filter bar -->
|
||||
<g transform="translate(18,94)">
|
||||
<rect class="btn" x="0" y="0" width="120" height="28" rx="6"/><text class="btnT" x="12" y="18">批次:本次 ▼</text>
|
||||
<rect class="btn" x="130" y="0" width="120" height="28" rx="6"/><text class="btnT" x="142" y="18">店铺:全部 ▼</text>
|
||||
<rect class="btn" x="260" y="0" width="130" height="28" rx="6"/><text class="btnT" x="272" y="18">状态:已生成 ▼</text>
|
||||
<rect class="btnP" x="400" y="0" width="64" height="28" rx="6"/><text class="btnPT" x="418" y="18">筛选</text>
|
||||
</g>
|
||||
<!-- warn bar -->
|
||||
<g transform="translate(18,128)">
|
||||
<rect x="0" y="0" width="904" height="24" rx="6" fill="#fdeceb" stroke="#f3c2bd"/>
|
||||
<text x="14" y="16" font-size="12" fill="#b23b30">⚠ 「开始更新」对【当前筛选结果】执行:改标题+换封面并直接提交线上(无确认、无开关)</text>
|
||||
</g>
|
||||
|
||||
<!-- table -->
|
||||
<g transform="translate(18,162)">
|
||||
<rect class="panel" x="0" y="0" width="904" height="250" rx="6"/>
|
||||
<rect x="1" y="1" width="902" height="28" fill="#eef1f6"/>
|
||||
<text class="th" x="14" y="19">别名</text><text class="th" x="120" y="19">商品ID</text>
|
||||
<text class="th" x="250" y="19">新标题</text><text class="th" x="560" y="19">新封面</text>
|
||||
<text class="th" x="690" y="19">阶段</text><text class="th" x="800" y="19">结果</text>
|
||||
<g font-size="12">
|
||||
<rect x="1" y="29" width="902" height="34" fill="#fafbfc"/>
|
||||
<text class="cell" x="14" y="51">女装店</text><text class="cell" x="120" y="51">51100639510</text>
|
||||
<text class="cell" x="250" y="51">…顯瘦百搭</text><text class="cell" x="560" y="51">new_…01.jpg</text>
|
||||
<circle cx="696" cy="47" r="4" fill="#20a464"/><text class="cell" x="706" y="51">已应用</text>
|
||||
<text class="cell" x="800" y="51" fill="#1f6f43">成功·已提交</text>
|
||||
|
||||
<text class="cell" x="14" y="85">女装店</text><text class="cell" x="120" y="85">51100640200</text>
|
||||
<text class="cell" x="250" y="85">…加絨上衣</text><text class="cell" x="560" y="85">new_…02.jpg</text>
|
||||
<circle cx="696" cy="81" r="4" fill="#2f6fed"/><text class="cell" x="706" y="85">处理中…</text>
|
||||
<text class="mut" x="800" y="85">—</text>
|
||||
|
||||
<rect x="1" y="97" width="902" height="34" fill="#fafbfc"/>
|
||||
<text class="cell" x="14" y="119">my主店</text><text class="cell" x="120" y="119">52000000001</text>
|
||||
<text class="cell" x="250" y="119">Korean Loose Tee</text><text class="cell" x="560" y="119">new_…07.jpg</text>
|
||||
<circle cx="696" cy="115" r="4" fill="#b6bdc7"/><text class="cell" x="706" y="119">待应用</text><text class="mut" x="800" y="119">—</text>
|
||||
|
||||
<text class="cell" x="14" y="153">饰品店</text><text class="cell" x="120" y="153">53000000009</text>
|
||||
<text class="cell" x="250" y="153">…百搭耳環</text><text class="cell" x="560" y="153">new_…03.jpg</text>
|
||||
<circle cx="696" cy="149" r="4" fill="#c2473d"/><text class="cell" x="706" y="153">失败</text>
|
||||
<text class="cell" x="800" y="153" fill="#c2473d">更新按钮禁用</text>
|
||||
</g>
|
||||
<text class="mut" x="14" y="234">串行执行 · 单条失败继续 · 每条立即写回 SQLite</text>
|
||||
</g>
|
||||
|
||||
<!-- actions -->
|
||||
<g transform="translate(18,420)">
|
||||
<rect class="btnP" x="0" y="0" width="120" height="34" rx="6"/><text class="btnPT" x="26" y="22">▶ 开始更新</text>
|
||||
<rect class="btn" x="132" y="0" width="64" height="34" rx="6"/><text class="btnT" x="150" y="22">■ 停止</text>
|
||||
<rect class="btn" x="700" y="0" width="204" height="34" rx="6"/><text class="btnT" x="724" y="22">回写结果到 Excel</text>
|
||||
</g>
|
||||
|
||||
<!-- summary box -->
|
||||
<g transform="translate(18,470)">
|
||||
<rect class="warn" x="0" y="0" width="904" height="138" rx="6"/>
|
||||
<text class="sec" x="16" y="26">结束汇总</text>
|
||||
<text class="cell" x="16" y="54">成功:2 失败:1(更新按钮禁用 / 校验未过) 略过:0</text>
|
||||
<text class="cell" x="16" y="80">结果已写回原 Excel(旧/新标题、旧/新封面、更新状态)。</text>
|
||||
<text class="mut" x="16" y="104">⚠ 原 Excel 被打开锁定时回写失败 → 提示关闭后重试,或另存副本(SQLite 为事实来源)。</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.8 KiB |
@@ -0,0 +1,72 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="940" height="620" viewBox="0 0 940 620" font-family="'Segoe UI','Microsoft YaHei',sans-serif">
|
||||
<defs><style>
|
||||
.win{fill:#f4f6f9;stroke:#d3d9e2}.hd{fill:#2b3a55}.title{fill:#fff;font-size:14px;font-weight:600}
|
||||
.tab{fill:#e8ebf0}.tabA{fill:#f4f6f9}.tabT{fill:#5c6573;font-size:13px}.tabTA{fill:#2f6fed;font-size:13px;font-weight:600}
|
||||
.sec{fill:#1f2733;font-size:13px;font-weight:600}.mut{fill:#8a92a0;font-size:11px}
|
||||
.cell{fill:#2a2f3a;font-size:12px}.th{fill:#5c6573;font-size:12px;font-weight:600}
|
||||
.btnP{fill:#2f6fed}.btnPT{fill:#fff;font-size:12px;font-weight:600}.btn{fill:#fff;stroke:#c5ccd6}.btnT{fill:#3a4150;font-size:12px}
|
||||
.panel{fill:#fff;stroke:#e2e6ec}.inp{fill:#fff;stroke:#c5ccd6}.dlg{fill:#fff;stroke:#cdd4de}
|
||||
</style></defs>
|
||||
<rect class="win" x="0.5" y="0.5" width="939" height="619" rx="8"/>
|
||||
<rect class="hd" x="0.5" y="0.5" width="939" height="40" rx="8"/><rect class="hd" x="0.5" y="20" width="939" height="21"/>
|
||||
<text class="title" x="18" y="26">cmshopee · Shopee 商品批量改标题换封面</text>
|
||||
<circle cx="888" cy="20" r="6" fill="#f5bf4f"/><circle cx="910" cy="20" r="6" fill="#5ac26a"/><circle cx="866" cy="20" r="6" fill="#e0e4ea"/>
|
||||
|
||||
<rect class="tab" x="0.5" y="41" width="939" height="38"/>
|
||||
<rect class="tabA" x="466" y="45" width="150" height="38"/><rect x="466" y="45" width="150" height="3" fill="#2f6fed"/>
|
||||
<text class="tabT" x="34" y="68">① 导入采集</text><text class="tabT" x="186" y="68">② AI生成</text>
|
||||
<text class="tabT" x="330" y="68">③ 更新shopee</text>
|
||||
<text class="tabTA" x="496" y="68">④ 账号管理</text>
|
||||
<text class="tabT" x="636" y="68">⑤ 设置</text>
|
||||
|
||||
<!-- table -->
|
||||
<g transform="translate(18,100)">
|
||||
<rect class="panel" x="0" y="0" width="904" height="220" rx="6"/>
|
||||
<rect x="1" y="1" width="902" height="28" fill="#eef1f6"/>
|
||||
<text class="th" x="14" y="19">账号名</text><text class="th" x="140" y="19">别名</text>
|
||||
<text class="th" x="270" y="19">地区</text><text class="th" x="470" y="19">端口</text>
|
||||
<text class="th" x="560" y="19">登录状态</text><text class="th" x="720" y="19">备注</text>
|
||||
<g font-size="12">
|
||||
<rect x="1" y="29" width="902" height="32" fill="#fafbfc"/>
|
||||
<text class="cell" x="14" y="49">shop_tw_a</text><text class="cell" x="140" y="49">女装店</text>
|
||||
<text class="cell" x="270" y="49">seller.shopee.tw</text><text class="cell" x="470" y="49">9222</text>
|
||||
<circle cx="566" cy="45" r="4" fill="#20a464"/><text class="cell" x="576" y="49">已登录</text><text class="cell" x="720" y="49">主力店</text>
|
||||
|
||||
<text class="cell" x="14" y="81">shop_my</text><text class="cell" x="140" y="81">my主店</text>
|
||||
<text class="cell" x="270" y="81">seller.shopee.my</text><text class="cell" x="470" y="81">9223</text>
|
||||
<circle cx="566" cy="77" r="4" fill="#b6bdc7"/><text class="cell" x="576" y="81">未登录</text>
|
||||
|
||||
<rect x="1" y="93" width="902" height="32" fill="#fafbfc"/>
|
||||
<text class="cell" x="14" y="113">shop_tw_b</text><text class="cell" x="140" y="113">饰品店</text>
|
||||
<text class="cell" x="270" y="113">seller.shopee.tw</text><text class="cell" x="470" y="113">9224</text>
|
||||
<circle cx="566" cy="109" r="4" fill="#e7a008"/><text class="cell" x="576" y="113">未启动</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- buttons -->
|
||||
<g transform="translate(18,332)">
|
||||
<rect class="btn" x="0" y="0" width="64" height="30" rx="6"/><text class="btnT" x="14" y="20">+新增</text>
|
||||
<rect class="btn" x="72" y="0" width="64" height="30" rx="6"/><text class="btnT" x="88" y="20">✎编辑</text>
|
||||
<rect class="btn" x="144" y="0" width="64" height="30" rx="6"/><text class="btnT" x="160" y="20">🗑删除</text>
|
||||
<rect class="btnP" x="430" y="0" width="120" height="30" rx="6"/><text class="btnPT" x="450" y="20">▶ 启动并登录</text>
|
||||
<rect class="btn" x="558" y="0" width="100" height="30" rx="6"/><text class="btnT" x="574" y="20">🔄 检测登录</text>
|
||||
<rect class="btn" x="666" y="0" width="120" height="30" rx="6"/><text class="btnT" x="682" y="20">⧉ 生成快捷方式</text>
|
||||
</g>
|
||||
|
||||
<!-- edit dialog overlay -->
|
||||
<g transform="translate(300,380)">
|
||||
<rect class="dlg" x="0" y="0" width="340" height="218" rx="8"/>
|
||||
<rect x="0" y="0" width="340" height="34" rx="8" fill="#eef1f6"/><rect x="0" y="20" width="340" height="14" fill="#eef1f6"/>
|
||||
<text class="sec" x="16" y="22">新增 / 编辑账号</text>
|
||||
<g font-size="12" fill="#2a2f3a">
|
||||
<text x="20" y="62">账号名*</text><rect class="inp" x="96" y="48" width="224" height="22" rx="4"/>
|
||||
<text x="20" y="92">别名*</text><rect class="inp" x="96" y="78" width="224" height="22" rx="4"/>
|
||||
<text x="20" y="122">地区域名</text><rect class="inp" x="96" y="108" width="224" height="22" rx="4"/>
|
||||
<text x="20" y="152">端口/密码</text><rect class="inp" x="96" y="138" width="108" height="22" rx="4"/>
|
||||
<rect class="inp" x="212" y="138" width="108" height="22" rx="4"/><text x="222" y="153" fill="#8a92a0">••••(加密)</text>
|
||||
</g>
|
||||
<text class="mut" x="20" y="182">配置目录(只读):chrome_user_data_dir\nuzhuang</text>
|
||||
<rect class="btn" x="180" y="186" width="64" height="24" rx="5"/><text class="btnT" x="198" y="202">取消</text>
|
||||
<rect class="btnP" x="252" y="186" width="68" height="24" rx="5"/><text class="btnPT" x="276" y="202">保存</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
@@ -0,0 +1,81 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="940" height="620" viewBox="0 0 940 620" font-family="'Segoe UI','Microsoft YaHei',sans-serif">
|
||||
<defs><style>
|
||||
.win{fill:#f4f6f9;stroke:#d3d9e2}.hd{fill:#2b3a55}.title{fill:#fff;font-size:14px;font-weight:600}
|
||||
.tab{fill:#e8ebf0}.tabA{fill:#f4f6f9}.tabT{fill:#5c6573;font-size:13px}.tabTA{fill:#2f6fed;font-size:13px;font-weight:600}
|
||||
.sec{fill:#1f2733;font-size:13px;font-weight:600}.mut{fill:#8a92a0;font-size:11px}
|
||||
.cell{fill:#2a2f3a;font-size:12px}.lbl{fill:#3a4150;font-size:12px}
|
||||
.btnP{fill:#2f6fed}.btnPT{fill:#fff;font-size:12px;font-weight:600}.btn{fill:#fff;stroke:#c5ccd6}.btnT{fill:#3a4150;font-size:12px}
|
||||
.panel{fill:#fff;stroke:#e2e6ec}.inp{fill:#fff;stroke:#c5ccd6}.sub{fill:#fafbfc;stroke:#eef1f6}
|
||||
</style></defs>
|
||||
<rect class="win" x="0.5" y="0.5" width="939" height="619" rx="8"/>
|
||||
<rect class="hd" x="0.5" y="0.5" width="939" height="40" rx="8"/><rect class="hd" x="0.5" y="20" width="939" height="21"/>
|
||||
<text class="title" x="18" y="26">cmshopee · Shopee 商品批量改标题换封面</text>
|
||||
<circle cx="888" cy="20" r="6" fill="#f5bf4f"/><circle cx="910" cy="20" r="6" fill="#5ac26a"/><circle cx="866" cy="20" r="6" fill="#e0e4ea"/>
|
||||
|
||||
<rect class="tab" x="0.5" y="41" width="939" height="38"/>
|
||||
<rect class="tabA" x="616" y="45" width="110" height="38"/><rect x="616" y="45" width="110" height="3" fill="#2f6fed"/>
|
||||
<text class="tabT" x="34" y="68">① 导入采集</text><text class="tabT" x="186" y="68">② AI生成</text>
|
||||
<text class="tabT" x="330" y="68">③ 更新shopee</text><text class="tabT" x="492" y="68">④ 账号管理</text>
|
||||
<text class="tabTA" x="640" y="68">⑤ 设置</text>
|
||||
|
||||
<!-- A. AI 模型管理 (master-detail) -->
|
||||
<g transform="translate(18,94)">
|
||||
<rect class="panel" x="0" y="0" width="904" height="170" rx="6"/>
|
||||
<text class="sec" x="14" y="24">AI 模型管理</text>
|
||||
|
||||
<!-- left: model selector -->
|
||||
<rect class="sub" x="14" y="36" width="250" height="120" rx="6"/>
|
||||
<text class="lbl" x="26" y="58">模型</text>
|
||||
<rect class="inp" x="26" y="64" width="226" height="26" rx="5"/><text class="cell" x="36" y="81">Nano Banana 2 ▼</text>
|
||||
<rect class="btn" x="26" y="98" width="70" height="26" rx="5"/><text class="btnT" x="44" y="115">新增</text>
|
||||
<rect class="btn" x="102" y="98" width="70" height="26" rx="5"/><text class="btnT" x="120" y="115">删除</text>
|
||||
<rect class="btnP" x="178" y="98" width="74" height="26" rx="5"/><text class="btnPT" x="190" y="115">测试连接</text>
|
||||
<text class="mut" x="26" y="142">至少各 1 个 文本/图像模型</text>
|
||||
|
||||
<!-- right: detail form -->
|
||||
<text class="lbl" x="286" y="56">服务商名称</text><rect class="inp" x="372" y="42" width="180" height="24" rx="4"/><text class="cell" x="382" y="59">Nano Banana 2</text>
|
||||
<text class="lbl" x="572" y="56">网址</text><rect class="inp" x="612" y="42" width="278" height="24" rx="4"/><text class="cell" x="620" y="59" font-size="11" fill="#586073">https://api.vectorengine.ai/v1/chat/completions</text>
|
||||
|
||||
<text class="lbl" x="286" y="92">类别</text><rect class="inp" x="372" y="78" width="92" height="24" rx="4"/><text class="cell" x="382" y="95">图像 ▼</text>
|
||||
<text class="lbl" x="478" y="92">api_type</text><rect class="inp" x="538" y="78" width="100" height="24" rx="4"/><text class="cell" x="548" y="95">auto ▼</text>
|
||||
<text class="lbl" x="656" y="92">连接超时</text><rect class="inp" x="724" y="78" width="56" height="24" rx="4"/><text class="cell" x="736" y="95">30</text><text class="mut" x="786" y="95">秒</text>
|
||||
|
||||
<text class="lbl" x="286" y="128">模型ID</text><rect class="inp" x="372" y="114" width="266" height="24" rx="4"/><text class="cell" x="382" y="131" font-size="11" fill="#586073">gemini-3.1-flash-image-preview</text>
|
||||
<text class="lbl" x="656" y="128">密钥</text><rect class="inp" x="724" y="114" width="140" height="24" rx="4"/><text class="cell" x="734" y="131" fill="#8a92a0">••••••••</text>
|
||||
<rect class="btn" x="868" y="114" width="22" height="24" rx="4"/><text class="btnT" x="874" y="131">👁</text>
|
||||
</g>
|
||||
|
||||
<!-- B. 角色 + 生成参数 -->
|
||||
<g transform="translate(18,272)">
|
||||
<rect class="panel" x="0" y="0" width="904" height="120" rx="6"/>
|
||||
<text class="sec" x="14" y="24">角色分配 + 生成参数</text>
|
||||
<text class="lbl" x="14" y="54">标题大模型</text><rect class="inp" x="100" y="40" width="190" height="24" rx="4"/><text class="cell" x="110" y="57">GPT-5.5 文本 ▼</text>
|
||||
<text class="lbl" x="320" y="54">图片大模型</text><rect class="inp" x="406" y="40" width="190" height="24" rx="4"/><text class="cell" x="416" y="57">Nano Banana 2 ▼</text>
|
||||
<text class="lbl" x="630" y="54">分辨率</text><rect class="inp" x="690" y="40" width="90" height="24" rx="4"/><text class="cell" x="700" y="57">1k ▼</text>
|
||||
<text class="mut" x="790" y="57">→ 返回超时 240s</text>
|
||||
|
||||
<text class="lbl" x="14" y="90">标题并发</text><rect class="inp" x="80" y="76" width="50" height="24" rx="4"/><text class="cell" x="98" y="93">4</text>
|
||||
<text class="lbl" x="150" y="90">图片并发</text><rect class="inp" x="216" y="76" width="50" height="24" rx="4"/><text class="cell" x="234" y="93">4</text>
|
||||
<text class="lbl" x="286" y="90">失败重试</text><rect class="inp" x="352" y="76" width="50" height="24" rx="4"/><text class="cell" x="370" y="93">2</text>
|
||||
<text class="lbl" x="422" y="90">jpg质量</text><rect class="inp" x="478" y="76" width="50" height="24" rx="4"/><text class="cell" x="496" y="93">90</text>
|
||||
<text class="mut" x="560" y="93">▸ 高级 分辨率→返回超时:512:180 1k:240 2k:360 4k:600(可改)</text>
|
||||
</g>
|
||||
|
||||
<!-- C. 路径与运行 -->
|
||||
<g transform="translate(18,400)">
|
||||
<rect class="panel" x="0" y="0" width="904" height="104" rx="6"/>
|
||||
<text class="sec" x="14" y="24">路径与运行</text>
|
||||
<text class="lbl" x="14" y="52">Chrome 路径</text><rect class="inp" x="100" y="38" width="520" height="24" rx="4"/>
|
||||
<text class="cell" x="110" y="55" font-size="11" fill="#586073">C:\Program Files\Google\Chrome\Application\chrome.exe</text>
|
||||
<rect class="btn" x="630" y="38" width="60" height="24" rx="4"/><text class="btnT" x="644" y="55">浏览…</text>
|
||||
<text class="lbl" x="14" y="88">图片目录</text><rect class="inp" x="100" y="74" width="180" height="24" rx="4"/><text class="cell" x="110" y="91" fill="#586073">images</text>
|
||||
<text class="lbl" x="300" y="88">DB</text><rect class="inp" x="334" y="74" width="160" height="24" rx="4"/><text class="cell" x="344" y="91" fill="#586073">cmshopee.db</text>
|
||||
<text class="lbl" x="520" y="88">端口范围</text><rect class="inp" x="586" y="74" width="70" height="24" rx="4"/><text class="cell" x="598" y="91">9222</text>
|
||||
<text class="lbl" x="662" y="88">–</text><rect class="inp" x="676" y="74" width="70" height="24" rx="4"/><text class="cell" x="688" y="91">9260</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0,512)">
|
||||
<rect class="btnP" x="822" y="0" width="100" height="34" rx="6"/><text class="btnPT" x="850" y="22">保存设置</text>
|
||||
<text class="mut" x="18" y="22">模型清单存 config/ai_models.json(密钥加密);选择与参数存 config.json。均 gitignore。</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.2 KiB |
Reference in New Issue
Block a user