Files
iAOP/web/auth/users.html
T
bot_dev1 cfa976cac3 feat: SPA 外壳——菜单切换零刷新,对齐 Ant Design Pro 体验(issue #131)
- web/index.html 重写为 SPA 外壳:Sider/顶栏常驻,模块页在
  #module-frame iframe 内容区加载,点菜单只换 iframe 地址,
  彻底消灭整页刷新与白闪(此前只是铺底色缓解)
- 新增 shared/shell.js:IAOP_SHELL.navigate() 导航、外壳 hash
  (#/admin/#models) 与 iframe 地址双向同步、登录守卫、移动端
  模块新标签打开;iframe 地址用 location.replace 避免污染
  joint history;250ms 轮询回同步(replace 不触发 load 事件);
  导航时乐观高亮(后台标签轮询被节流不能作为唯一途径);
  前进/后退若恢复了 iframe 旧地址则以外壳 hash 为准强制对齐
- pro-header.js 三模式:外壳(菜单点击走 IAOP_SHELL、导出
  IAOP_PRO.setActive)/被嵌(iframe 内,隐藏页内跨页链接与重复
  用户标识、保留 Tab-hash 联动)/独立(原行为);菜单渲染后派发
  pro:menu-rendered 事件消除导航先于渲染的高亮竞态
- session.js:logout/守卫重定向作用于顶层窗口(iframe 内不再
  只跳 iframe 自己)
- 修复 iframe 替换元素宽高 bug:fixed 时左右定位不拉伸(回退
  固有 300x150),改显式 calc 宽高
- 登录页 next 默认回外壳 ../index.html

实测(本地 http.server + WebBridge 真实浏览器):菜单切换/
子菜单切换/前进/后退/直达链接(#/admin/#models) URL、iframe、
菜单高亮、子菜单高亮全链路一致,Sider 节点全程不重建;
登录守卫 logout→顶层跳登录、登录后回外壳默认驾驶舱。
2026-08-05 22:43:47 +08:00

109 lines
5.7 KiB
HTML
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.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 首屏防闪(issue #131):跨页跳转时外部 CSS 未到的瞬间,
先用与最终布局一致的底色铺满画布(左 Sider 深色 + 内容区灰),
消除深色 Sider 区域的白闪(FOUC)。 -->
<style>html{background:#f0f2f5}@media (min-width:721px){html{background:linear-gradient(90deg,#001529 208px,#f0f2f5 208px)}}</style>
<!-- 被 SPA 外壳 iframe 嵌入时内容区无 Sider,立即改回纯灰底(首帧前生效) -->
<script>if(window!==window.top){document.documentElement.style.background="#f0f2f5"}</script>
<title>iAOP 用户/角色管理</title>
<style>
/* iAOP 用户/角色管理页(issue #134 / PRD 8.2):admin 专属 */
:root { --bg:#0b1220; --surface:#13203a; --fg:#e6edf6; --muted:#8aa0bd;
--accent:#18d3c8; --line:rgba(255,255,255,.06); }
* { box-sizing:border-box; }
body { margin:0; font-family:"Microsoft YaHei",sans-serif; background:var(--bg); color:var(--fg); }
#topbar { display:flex; align-items:center; justify-content:space-between;
padding:10px 16px; background:var(--surface); border-bottom:1px solid var(--line); }
h1 { font-size:18px; color:var(--accent); margin:0; }
h1 .sub { font-size:12px; color:var(--muted); font-weight:400; }
#topbar-right { display:flex; gap:10px; align-items:center; font-size:13px; color:var(--muted); }
main { padding:16px; display:grid; grid-template-columns:1fr 1fr; gap:12px; }
.panel { background:var(--surface); border:1px solid var(--line); border-radius:8px;
padding:14px 16px; }
.panel h2 { font-size:14px; color:var(--accent); margin:0 0 10px; }
table { width:100%; border-collapse:collapse; font-size:13px; }
th, td { padding:7px 8px; border-bottom:1px solid var(--line); text-align:left; }
th { color:var(--muted); font-weight:400; }
select, input { padding:6px 8px; border-radius:6px; border:1px solid var(--line);
background:var(--bg); color:var(--fg); font-size:13px; }
button { padding:5px 12px; border:none; border-radius:5px; cursor:pointer;
background:#1e293b; color:var(--fg); font-size:12px; }
button.primary { background:var(--accent); color:#04202a; }
button.danger { background:#7a1f1a; color:#ffb4ad; }
.row { display:flex; gap:8px; margin:10px 0; flex-wrap:wrap; align-items:center; }
.hint { font-size:11px; color:rgba(0,0,0,.45); }
.role-readonly { color:rgba(0,0,0,.45); } .role-engineer { color:#237804; }
.role-admin { color:#ad6800; }
.disabled-user { opacity:.45; }
#audit-list { max-height:420px; overflow-y:auto; font-size:12px; font-family:Consolas,monospace; }
#audit-list div { padding:3px 0; border-bottom:1px solid var(--line); color:var(--muted); }
@media (max-width:1000px) { main { grid-template-columns:1fr; } }
</style>
<link rel="stylesheet" href="../shared/pro.css">
</head>
<body data-pro-title="用户与角色管理" data-pro-home="../cockpit/" data-pro-login="login.html">
<header id="topbar">
<h1>用户/角色管理 <span class="sub">RBAC 三级:readonly / engineer / admin · PRD 8.2</span></h1>
<div id="topbar-right">
<span id="who"></span>
<a href="../studio/index.html" style="color:var(--accent);font-size:13px">返回配置台</a>
<button id="logout-btn">退出登录</button>
</div>
</header>
<main>
<div class="panel">
<h2>用户列表</h2>
<div class="row">
<input id="new-username" placeholder="用户名">
<input id="new-password" type="password" placeholder="初始密码(≥6 位)">
<select id="new-role">
<option value="readonly">Viewer(只读)</option>
<option value="engineer">Editor(配置)</option>
<option value="admin">Publisher(发布)</option>
</select>
<button id="btn-add" class="primary">新增用户</button>
</div>
<div class="hint" id="form-err"></div>
<table>
<thead><tr><th>用户名</th><th>角色</th><th>状态</th><th>创建时间</th><th>操作</th></tr></thead>
<tbody id="user-tbody"></tbody>
</table>
</div>
<div>
<div class="panel">
<h2>OIDC SSO 配置点(预留,PRD 8.2 双轨)</h2>
<div class="row"><input id="oidc-issuer" placeholder="issuer,如 https://iam.example.com" style="flex:2"></div>
<div class="row">
<input id="oidc-client" placeholder="client_id" style="flex:1">
<input id="oidc-redirect" placeholder="回调 redirect_uri(可空)" style="flex:1">
<button id="btn-oidc" class="primary">保存</button>
</div>
<div class="hint">配置后登录页出现「企业 SSO 登录」入口(标准 OIDC 授权码流程);
未配置走本地账号。令牌换会话的后端端点待 IAM 就绪后对接(见 README)。</div>
</div>
<div class="panel" style="margin-top:12px">
<h2>角色权限矩阵(PRD 5.7,对齐 rbac.py Resource×Action)</h2>
<table id="perm-matrix">
<thead><tr><th>角色</th><th>查看 view</th><th>配置 edit</th><th>发布/回滚 publish</th><th>用户管理 manage</th></tr></thead>
<tbody></tbody>
</table>
<div class="hint">矩阵即前端门控依据:配置台写按钮按会话角色置灰(Viewer 只读 /
Editor 可配置 / Publisher 可发布回滚),本页仅 admin 可见。</div>
</div>
<div class="panel" style="margin-top:12px">
<h2>审计日志(写操作落日志,最近 500 条)</h2>
<div id="audit-list"></div>
</div>
</div>
</main>
<script src="user_store.js"></script>
<script src="../shared/session.js"></script>
<script src="../shared/pro-header.js"></script>
<script src="users.js"></script>
</body>
</html>