Files
dsh_shenxian/dsh-server-docs/04-调整方案/31-插件管理页双Tab与说明为主视觉.md
T
admin 5ad755116e chore(docs): 文档库并入代码仓(R4 选 a)+ 索引/台账跟进
1) dsh-server-docs/ 从工作区(原 E:\...\aliyun-dsh-server\dsh-server-docs)**整体并入本仓**,
   保留目录名 ⇒ 仓库内 dsh-server-docs/... 的相对引用天然继续有效;旧目录(含其 .git)已归档到
   工作区 _中间产物_待清理/,未随本提交带入。
2) .gitattributes:新增 `dsh-server-docs/** -text` —— 原文档库是 `* -text` + autocrlf=false,
   必须保持纯 LF,否则会被本仓的 CRLF 规则翻掉。
3) 活引用里的绝对路径已全部改到新位置(docs 的 INDEX / README / scripts / skills + 用户级 skills
   + ~/.workbuddy/settings.json 的 hooks);历史档案(04-调整方案/、archive/)按「只增不改」未动。
   ⚠️ hooks 路径改动需「完全重启会话」才生效(配置是会话启动快照)。
4) 交接单/T08:新增 §16「生产整体切换执行记录」(形态 / 落地动作 / **4 个只有真上线才暴露的真 bug** /
   验收证据 / 回滚命令 / 残留项);台账 T08 行 → 已完成并归档;03-路线图 §二 登记 T08 收尾项。
5) 统一称谓:**「本机」只指跑 WorkBuddy 的开发机**,47 / 106 一律写「远程服务器」。
2026-09-15 18:47:13 +08:00

103 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 31 · 插件管理页双 Tab + 说明为主视觉 + 目录字段用全
- 日期:2026-09-11
- 触发:用户提问「**导入到候选池是什么意思**」+「dsh 官方推荐插件 和 手动添加和管理插件 能否分两个 tab 展示」+「**官方插件优先展示应该是插件说明或中文名称**,需要一眼知道这个插件是干什么的」
- 状态:**✅ 已实施并视觉验证(commit `103471e`)**
## 一、需求与背景
三个问题指向同一件事:**档案 29 交付的插件管理页,信息层级与术语都没站在「看得懂」的角度设计。**
1. **术语费解**:「导入到候选池」中的「候选池」是内部叫法,admin 本人看不懂 —— 说明页面缺少对「投放 ≠ 生效」的解释。
2. **IA 混在一起**:官方目录挑选、手工上传、已投放管理三件事挤在一页,纵向下堆。
3. **信息层级错**:表格首列是插件名(`dsh-market/dsh-market` 这类技术标识),说明被挤到最后一列且单行截断 —— **看不出插件是干什么的**。
## 二、调研:官方目录还有哪些字段没用
拉 `plugins.json` 逐字段核对后发现,档案 29 只用了 7 个字段,**漏了 3 个**:
| 字段 | 覆盖 | 是否用上 | 说明 |
|---|---|---|---|
| `description.zh` | **3408/3408(100%)** | 部分(末列截断) | 中文说明,**平均 83 字** |
| `category` + 顶层 `categories` | 100%(23 类) | ❌ 只显示原始 id(`ui`) | 顶层 `categories = { id: { en, zh } }`,**官方只在这里给分类中文名**(如 `ui`→UI 增强、`tools`→工具与能力) |
| `page` | **3408/3408** | ❌ 未用 | 官方详情页 `awesome-dsh-plugin.com/p/<owner>/<name>/`(完整介绍 + 截图 + 评论) |
| `screenshots` | 594/3408(17.4%);可导入子集 421/1792(23.5%) | ❌ 未用 | 截图 URL 数组 |
| `added` | 3408 | ❌ 未用 | 收录日期(最早 2026-08-13 / 最新 2026-09-08)—— 跨度过窄,做「新收录」标记无意义,故未用 |
| `stars` / `downloads` | 1586 / 1653(可导入子集) | ✅ | — |
| **`tags` / `keywords`** | **0(不存在)** | — | **上游没有标签体系**,分类是唯一 taxonomy(23 类) |
## 三、实现(commit `103471e`)
### A. 双 Tab(`web/portal.html` → `#/plugins`)
- 样式按 `06-工作台UI规范.md` §4.4 **下划线式**(无背景无圆角,`border-bottom: 2px`,active 蓝字 + 蓝下划线,`margin-bottom: -1px` 压住分隔线)。
- **类名用 `.pg-tabs` / `.pg-tab`**,刻意避开全局 `.tab` —— 06 明确警告 `.tab` 被顶栏(胶囊式)与数据页(下划线式)二次定义,复用会拿到不可预期的样式。
- Tab:**「官方推荐插件」** / **「手动添加 / 管理」**(后者带已投放计数徽章)。
- **深链**:切 tab 用 `history.replaceState` 写 `#/plugins/<tab>`(**不触发 `hashchange`**,因此不会重跑 router);初始 tab 读 hash 后缀。现有 `parseHash()` 取 `parts[0]`,`#/plugins/manual` 依旧路由到 plugins,无需改路由。
### B. 说明为主视觉(用户核心诉求)
白名单表格首列由「插件名」改成 **「插件说明」**:
| 行 | 内容 | 样式 |
|---|---|---|
| 主行 | **中文说明**(2 行截断,`-webkit-line-clamp: 2`,`title` 悬停看全文;为空时回退显示插件名并加粗) | 14px / 正文色 |
| 副行 | `插件名 · npm包名 @ 版本 · ★N` | 12px / dim |
分类列改显示**中文分类名**;新增 **「详情 ↗」** 外链列指向官方 `page`;保留 `下载量`(热度是排序主信号),★ 下移到副行。
### C. 容器宽度
6 列信息量下表格**横向溢出 74px**。修法:给插件页单独放宽内容容器 —— `router()` 给 `#view` 打 `page-<name>` 类名,`#view.page-plugins { max-width: 1200px }`,**其他页仍 960 不受影响**。实测 `scrollWidth/clientWidth = 1105/1105`(无溢出)。
### D. 术语落到页面
「已投放插件」卡片加说明:
> **投放 ≠ 生效。** 这里只是把插件**收录到平台**,对任何用户都不生效;用户要真正用上,需在自己实例的「设置 → 功能插件」里勾选启用 —— 启用时平台才会把插件装进他的环境,重启实例后生效。
按钮文案 `导入到候选池` → **`导入到平台`**,确认弹窗同步改口径。
### E. 后端(`src/web/routes/whitelist.ts`)
- 解析顶层 `categories` → `categoryLabels`(id→中文名),随缓存留存;条目新增 `categoryLabel` / `hasShots`。
- `GET /api/plugins/whitelist` 的 `categories` 由 `string[]` 改为 **`[{id, label, count}]`**(按条目数降序),供前端下拉直接用中文 + 计数。
- **`CACHE_VERSION` 2 → 3**(新增字段必须 bump,否则 TTL 内旧格式缓存被复用 —— 档案 29 踩过这个坑)。
## 四、改动文件
| 文件 | 改动 |
|---|---|
| `src/web/routes/whitelist.ts` | 解析分类中文名映射;条目加 `categoryLabel`/`hasShots`;`categories` 改对象数组;`CACHE_VERSION` → 3 |
| `web/portal.html` | 双 Tab + `.pg-tab` 样式;白名单表格列重排;`page-plugins` 容器放宽;`router` 打页面类名;「投放 ≠ 生效」说明与文案 |
## 五、验证(独立 headless Chrome,2026-09-11)
**方法**:起**独立 headless Chrome**(`--headless=new --remote-debugging-port=9223 --user-data-dir=<独立 profile>`),CDP 直连(`BU_CDP_URL`),注入临时 admin session cookie(`poc-ui`,用后即删)。**不碰用户日常浏览器、无授权弹窗**。
| 用例 | 结果 |
|---|---|
| 后端 `GET whitelist?onlyImportable=1&category=ui` | 200;`categories[0..5]` = UI 增强(565) / 工具与能力(457) / 开发与运行时(262) / 会话与消息(215) / 工作流与自动化(207) / 用量与计费(187);条目带 `categoryLabel`/`page`/`hasShots` |
| Tab 渲染与切换 | `tabs=2`;点「手动添加 / 管理」→ URL `#/plugins/manual`、pane 互斥切换正确 |
| 深链 | 直接访问 `#/plugins/official` 落在正确 tab |
| 表格布局(1440×1000) | `scrollWidth/clientWidth = 1105/1105` → **无横向溢出**;6 列表头齐全 |
| 首行内容 | 说明为主 + 副行 `dsh-market · dshmarket @ 1.45.0 · ★ 3408` + 分类「插件市场与管理」+「npm 预构建」+ 383506 + 「详情 ↗」 |
| 静态文件即时生效 | 改 `portal.html` **无需重启**(服务启动时间未变);后端改动才 build + restart |
| 内联 JS 语法 | `node --check` 通过 |
截图:`p-official.png` / `p-manual.png`(本机临时目录,未入库)。
## 六、事故/踩坑记录
1. **`browser-harness` 反复弹 Chrome 授权**(用户明确抱怨):根因是**我每次都用 `python -m browser_harness.run` 直接起进程,绕过了 wrapper 脚本** —— wrapper 会设 `BH_RUNTIME_DIR_SHARED=1` 让 daemon 常驻复用;绕过它则每次新建连接 → Chrome 每次弹窗。**修法:起独立 Chrome 实例 + 固定调试端口 + `BU_CDP_URL` 直连,此后零弹窗。**
2. **`Emulation.setDeviceMetricsOverride` 按 target 生效**:先 `new_tab` 再设覆盖才对;设在旧标签上、再开新标签 = 覆盖丢失(截图只有 758×482 宽)。
3. **hash 残留影响验证**:页面停在上次的 `#/plugins/manual`,重载后 `#wlTbody` 不存在 → 元素查询返回 null(表现为 `0/0`)。验证脚本必须**显式导航到目标 tab**。
4. **移动 `.bak` 前先查是否被 git 跟踪**(档案 29 教训复用)。
5. **后端字段增删必须 bump `CACHE_VERSION`**(档案 29 教训复用)—— 本次主动执行,未再踩。
## 七、后续(未做,按需)
1. **截图预览**:`screenshots` 覆盖仅 17.4%(可导入子集 23.5%),现已置 `hasShots` 但未展示;若要「图看效果」,可在「详情」列改为悬停出缩略图(覆盖率不高,需评估是否值得)。
2. **描述完整展示**:现在 2 行截断(约 60 字 / 平均 83 字)+ 悬停看全文。若要求「不悬停也全看到」,可加行高(3 行 clamp)或再放宽容器。
3. **「候选池」术语**:已把按钮/说明改成「平台投放」口径,但**内部代码与 API 仍叫 business_plugins / 候选池**;是否统一改名待定。