Files
dsh_shenxian/dsh-server-docs/04-调整方案/67-功能管理section按UI规范重做.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

12 KiB
Raw Blame History

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():

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 渲染只能由用户确认)。