139 lines
12 KiB
Markdown
139 lines
12 KiB
Markdown
# 67 · 「功能管理」section 按 UI 规范重做(v0.2.4)
|
||||
|
|
|
|||
|
|
- 日期:2026-09-12
|
|||
|
|
- 触发:用户指出实例内「设置 → 功能管理」页「UI 交互太简陋、没参考 UI 规范、插件没重点显示中文说明」;核对成立后按方案执行
|
|||
|
|
- 结论一句话:**把信息层次反过来(用途说明做主视觉)、字号与组件对齐 `06-工作台UI规范.md`、空态补层次、加行 hover** —— 并顺带发现一个会阻塞用户启用插件的平台 bug。
|
|||
|
|
- 状态:✅ 已完成并部署(admin 已生效);⚠️ **guest 侧升级被一个平台 bug 阻塞**(见 §五)
|
|||
|
|
|
|||
|
|
> **TL;DR**|**结论**:规范第 5 行明确「含其 client 面 section」,本页在约束内,逐项核对确认「简陋」成立;主视觉原本是包名(说明只作 11px 灰字),与门户 plugins 页相反 —— 已反转。
|
|||
|
|
> **关键**:执行中发现 `node_modules` 有 **561 个 root 属主文件**,根因是**门户候选池的启用路径以 root 跑 pnpm**,导致该用户此后任何 `pnpm add` 都 EACCES。
|
|||
|
|
> **状态**:✅ UI 已上线(admin 验证);⏸ root 属主修复待确认(R7:561 > 10)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 一、核对结论(只读,详见 2026-09-12 日志)
|
|||
|
|
|
|||
|
|
`06-工作台UI规范.md` 第 5 行适用范围明确含「**其 client 面 section**」→ 本页无豁免。逐项对照 `poc/business-plugins/lib/client.js`:
|
|||
|
|
|
|||
|
|
| 规范 | 改前 |
|
|||
|
|
|---|---|
|
|||
|
|
| 正文 15-16px / 次要 13-14px | 容器 13px;副行与徽章 **11px**(低于阶梯下限) |
|
|||
|
|
| 空态:图标 + 标题 17px/600 + 说明 14px | **一行灰字** |
|
|||
|
|
| `.btn` 15px / `.btn-sm` 14px | 12px / `5px 10px` |
|
|||
|
|
| `.badge` `2px 8px` r10 13px | `1px 7px` r9 11px |
|
|||
|
|
| 列表行 hover 反馈 | 无 |
|
|||
|
|
| 第 7 行**强制**加载 `impeccable` + `taste-skill` | 二者在本机**不存在**(用户级技能目录只有 BT-* / dsh-*)→ 以规范文档为准(规范自称实测基线、优先级高于技能) |
|
|||
|
|
|
|||
|
|
**中文说明缺失的三层根因**(另见档案 66 附录与日志):
|
|||
|
|
1. 主次颠倒 —— 实例内包名做主视觉,门户反过来;
|
|||
|
|
2. `description` 取自插件 `package.json`(英文);
|
|||
|
|
3. **官方目录本来就有中文,但导入时没取用** —— 已在同批次修掉(见 §三.1)。
|
|||
|
|
|
|||
|
|
## 二、改动(v0.2.4)
|
|||
|
|
|
|||
|
|
`poc/business-plugins/lib/client.js` + `package.json`(版本 0.2.3 → 0.2.4,同名同版本改内容必须升版本号,否则 pnpm 缓存复用旧包):
|
|||
|
|
|
|||
|
|
| 项 | 改前 | 改后 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| **主视觉** | 包名 + 版本 13px | **用途说明 14px**(无说明时回退包名) |
|
|||
|
|
| 副行 | 说明 11px 灰字 | **包名 + 版本 12px**(有说明才渲染) |
|
|||
|
|
| 容器基准 | 13px | 14px(section 语境取 14/13/12 三档,不照搬页面级 16px) |
|
|||
|
|
| 徽章 | 11px / `1px 7px` / r9 | 12px / `2px 8px` / r10 |
|
|||
|
|
| 按钮 | 12px / `5px 10px` | 次要 13px / `5px 12px`;主按钮 14px / `7px 16px` |
|
|||
|
|
| 空态 / 加载态 | 一行灰字 | 「标题 15px/500 + 说明 13px」层次(规范 §4.9,padding 按 section 收窄) |
|
|||
|
|
| 列表行 hover | 无 | 注入式 CSS `.bp-row:hover`(规范 §4.3 / §5;内联样式表达不了 `:hover`) |
|
|||
|
|
| 按钮 hover | 无 | `.bp-btn:hover` 边框/文字转主色 |
|
|||
|
|
| 无匹配态 | 复用空态文案 | 独立文案(居中,13px) |
|
|||
|
|
| 布局 | gap 8px / padding `6px 8px` | gap 10px / padding `8px 10px`(规范 §2.4) |
|
|||
|
|
| 保留 | —— | `--dsw-*` design token(跟随 dsh 主题,**不**回退规范硬编码色)、i18n、探活刷新、rejected 行内提示 |
|
|||
|
|
|
|||
|
|
新增 locale key:`loadingDesc`、`emptyTitle`、`noMatch`(zh/en 同步,key 集一致)。搜索占位符改为「搜索插件名或用途」。
|
|||
|
|
|
|||
|
|
**交付**:`npm pack` → `business-plugins-0.2.4.tgz`(8951 B)→ `/opt/dsh/artifacts/` → `node scripts/ensure-biz-plugins.cjs --all --restart`。
|
|||
|
|
|
|||
|
|
## 三、同批次的其他修复
|
|||
|
|
|
|||
|
|
1. **中文说明(方案第一步)**:`src/web/routes/whitelist.ts` 导入入库改用目录条目中文(`entry.description`,建索引时已按 `zh ?? en` 归一),并回填已入库的 3 条 → 门户「已投放插件」表与实例内列表均显示中文。已部署验证。
|
|||
|
|
|
|||
|
|
## 四、部署结果
|
|||
|
|
|
|||
|
|
| 对象 | 结果 |
|
|||
|
|
|---|---|
|
|||
|
|
| **admin**(uid 114801) | ✅ 0.2.3 → 0.2.4 升级成功;bundles=6 含该包;当时无运行实例,下次访问即为新 UI |
|
|||
|
|
| **guest**(uid 100002) | ❌ `EACCES: permission denied, open '.../node_modules/.bin/modlens'` |
|
|||
|
|
|
|||
|
|
## 五、⚠️ 执行中发现的平台 bug:候选池启用路径污染属主
|
|||
|
|
|
|||
|
|
**现象**:guest 无法升级/启用任何插件 —— 以用户身份跑 `pnpm add` 时撞 `EACCES`。
|
|||
|
|
|
|||
|
|
**实测污染规模**:
|
|||
|
|
|
|||
|
|
| 用户 | profile 下 root 属主项 | 分布 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| guest | **561** | `.pnpm` 555 / `@liustack` 2 / `.bin` 2 / `dsh-univer-office` 1 / 一个 `.bak` |
|
|||
|
|
| admin | 5 | 全在 `.pnpm` |
|
|||
|
|
|
|||
|
|
**根因**:门户候选池的启停路径 `src/web/routes/business-plugins.ts` 的 `install()` / `uninstall()`:
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
execFileSync('pnpm', ['add', 'file:' + plugin.tgzPath, ...], { cwd: dir, env: pnpmEnv(user.id) })
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**没有 `setpriv` 降权** → 以平台进程身份(root)执行 pnpm → 装出来的文件属主是 root。此后用户自己的 `pnpm add/remove` 必然 EACCES。
|
|||
|
|
|
|||
|
|
**证据吻合**:`audit_log` 记录 guest 在 01:58 通过候选池启用 `@liustack/modlens` + `dsh-univer-office` —— 正是这批 `.pnpm`/`@liustack`/`dsh-univer-office` 项的来源。
|
|||
|
|
|
|||
|
|
**与既有档案的关系**:档案 43 已处理过「平台 root 污染用户工作区」并加了属主自愈,但**候选池这条路径未被覆盖**(`ensure-biz-plugins.cjs` 等平台脚本用的是 `setpriv` 正姿,路线不同所以没暴露)。
|
|||
|
|
|
|||
|
|
**修法(待确认)**:
|
|||
|
|
1. **短期解阻塞**:把该 profile 下 561 个 root 属主项 chown 给用户(`find <profile> -user root -exec chown <uid>:<uid> {} +`)。**R7:>10 文件,须先确认**。
|
|||
|
|
2. **长期修根因**:`install()` / `uninstall()` 改为 `setpriv --reuid <uid> --regid <uid> --clear-groups env HOME=<ws> pnpm …`,与平台脚本姿势对齐;并考虑给 `apply` 路径补一次属主自愈。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 追加(2026-09-12 22:2x):v0.2.6 —— 插件列表改为**卡片形式,一行 2 个**
|
|||
|
|
|
|||
|
|
- **触发**:用户「把设置中功能管理的插件列表改为卡片形式 一行放2个卡片 好看些」(2026-09-12 22:13)。
|
|||
|
|
- **落点**:`poc/business-plugins/lib/client.js`(client bundle)。
|
|||
|
|
- **改动**(4 处,全部是渲染层,不动数据与交互逻辑):
|
|||
|
|
1. **新增内层网格容器**:`display:grid; gridTemplateColumns:repeat(2, minmax(0,1fr)); gap:10px` —— **单独包一层**,**不直接改外层 `wrap`**(它同时装标题 / 搜索 / 按钮 / 提示等纵向块);插件项先收集进 `cards[]` 再统一 push。
|
|||
|
|
2. **每项由「行」改「卡片」**:`padding 12px 14px`、`borderRadius 12px`、`background: var(--dsw-alias-bg-layer-1, #fff)`、**常显 1px `--dsw-alias-border-1` 边框**(原来非错误项是 `transparent`)。
|
|||
|
|
3. **hover 反馈改按规范 §4.5**:`.bp-row:hover{ border-color: brand-primary; box-shadow: 0 4px 12px rgba(47,111,237,.15) }`(原为 hover 换背景色)。
|
|||
|
|
4. **卡片内 `alignItems: flex-start`** —— 复选框与徽章顶部对齐,长说明不再把行高撑歪。
|
|||
|
|
- **规范依据**:`06-工作台UI规范` §4.5(卡片 r12 + hover 上浮 / 主色扩散影)、§2.4(信息卡 `14px 16px`)、§2.3(大卡片 r12);颜色走 dsh 官方 `--dsw-*` token(本 section 嵌在 dsh 面板内,不照搬门户亮色基线)。
|
|||
|
|
- **部署**:版本 **0.2.5 → 0.2.6**;产物 `business-plugins-0.2.6.tgz` → `/opt/dsh/artifacts/` → `node scripts/ensure-biz-plugins.cjs --all`(**未加 `--restart`**,不主动打断在线实例)→ admin(`cce6d1cd…`)与 guest(`4092b965…`)均已确认装到 **0.2.6**、且包内含改动(`gridTemplateColumns` 命中 1)。
|
|||
|
|
- **生效条件**:client bundle 在**实例启动时**加载 ⇒ **用户需重启自己的实例**(实例页重启,或等下次冷启动)才看得到。
|
|||
|
|
- **未做**:**未做浏览器渲染验证**(本机无 Chromium;按规范 §6.5,此类 section 级渲染只能由用户硬刷新确认)⇒ **待用户目视验收**。
|
|||
|
|
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## v0.2.7 / v0.2.8 追加(2026-09-13,用户要求)
|
|||
|
|
|
|||
|
|
> 按「档案只增不改」追加。记录规范之外的两块新增交互:**内存预估** 与 **页内弹窗**。
|
|||
|
|
|
|||
|
|
### 触发(用户原话)
|
|||
|
|
- 「先不改挡位,先优化设置中 功能插件卡片的样式,把预估所占内存数值也写上去(卡片可以增加高度便于UI布局),在卡片列表上方 增加一个内存占用状态条(显示当前内存占用量,用户点击启用卡片时增加预估占用量,显示是否超出最大内存占用,让用户知道插件启用和内存占用预估状态)」
|
|||
|
|
- 「启用插件内存预估值超过上限 点击确认按钮弹窗需要有警告(还有所有弹窗改为页面弹窗 参考mcn工作台弹窗实现)」
|
|||
|
|
|
|||
|
|
### v0.2.7 · 内存预估(卡片 + 状态条)
|
|||
|
|
- 内存模型常量:`MEM_LIMIT_MIB=384`(档案 58)/ `MEM_BASE_MIB=285`(空载基线,smaps 实测)/ `MEM_TABLE={dsh-univer-office:64, dsh-plugin-mcn-suite:39}`(隔离 cgroup 实测 rss 增量)/ `MEM_FALLBACK_MIB=2`(轻量兜底)/ `MEM_TIGHT_RATIO=0.85`。
|
|||
|
|
- 卡片:`padding 14/16` + `minHeight 112`;信息列第三行加「预估内存 ≈ N MiB」徽章,**≥60 红 / ≥30 黄 / 其余次要色**。
|
|||
|
|
- 列表上方「内存预估」状态条:`当前 X MiB → 勾选后 Y MiB / 上限 384` + 三态词 + **双色进度条**(实心 = 服务端当前已启用那批 `savedIds`;浅色 = 本次勾选增量)+ 口径小字「预估(实测基准,非实时读数)」;超限时整条红边 + 警告行。
|
|||
|
|
- 实现要点:`load()` 时快照服务端已启用集合 `savedIds` —— 否则 `p.enabled` 被 toggle 就地改掉后无法区分「当前 / 勾选后」。
|
|||
|
|
|
|||
|
|
### v0.2.8 · 全部弹窗改页内弹窗
|
|||
|
|
- **弃用 `window.confirm`**;`applyChanges()` 拆为 `askApply()`(开弹窗)+ `doApply()`(真提交)。
|
|||
|
|
- 弹窗**照 MCN 工作台范式**(`dsh-plugin-mcn/lib/client.js`):`fixed inset:0` 遮罩(`rgba(0,0,0,.35)` / `zIndex 2000` / 居中)+ 面板(`radius 12` / `maxHeight 82vh` / `padding 18·22` / 阴影)+ **点遮罩关闭** + 面板 `stopPropagation` + ✕;三段结构:标题 / 正文(明说重启后果)/ 按钮行。
|
|||
|
|
- **超限时**:弹窗内内存块转**红底红边** + 红色标题「⚠ 本次勾选将超出单实例内存上限」+ 说明;**确认键转危险色**;**未点确认不发请求**。
|
|||
|
|
- 全文已无原生弹窗调用(`window.confirm/alert/prompt` 仅存于注释)。
|
|||
|
|
|
|||
|
|
### 口径(重要)
|
|||
|
|
- 状态条是**预估值**(客户端拿不到 cgroup 实测值)。做**实时读数**需平台加只读路由(读 `memory.usage_in_bytes` / `MemoryMax`)→ 平台代码改动 + build + 重启服务,**尚未做**。
|
|||
|
|
|
|||
|
|
### 部署与验证
|
|||
|
|
- 产物:`business-plugins-0.2.8.tgz`(14,358 B,md5 `3ccb05bc6b138231bcaf1f81d7b50549`);**0.2.7 未单独部署**(0.2.8 含其全部改动)。
|
|||
|
|
- 部署:`scp` → `/opt/dsh/artifacts/` → `node scripts/ensure-biz-plugins.cjs --all`(admin/guest 0.2.6 → 0.2.8)→ `--restart`(优雅停 scope,下次访问自动拉起)。
|
|||
|
|
- **线上端到端自证**:临时会话 `enter` → 实例页 200 → 抠出 combo bundle URL(含 `@dsh-local/business-plugins/client.js`,`rev=27340d41a454`)→ 拉取 **4,585,899 B** → 命中 `mem.title`×4 / `__confirm`×1 / `confirm.overTitle`×3 / `MEM_LIMIT_MIB`×5 /「预估内存」×5。
|
|||
|
|
- 仿真验证(临时 harness,用完即删):初始 `326/326` → 勾选 univer `390` → 点「应用更改」弹窗打开且**不发请求** → 点「确认应用」才 `POST /api/plugins/mine/apply`。
|
|||
|
|
- **未做**:浏览器视觉验收(按 §6.5,此类 section 渲染只能由用户确认)。
|