102 lines
8.1 KiB
Markdown
102 lines
8.1 KiB
Markdown
# 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 / 候选池**;是否统一改名待定。
|