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

8.1 KiB
Raw Blame History

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 / 候选池;是否统一改名待定。