Files
iAOP/web/index.html
T
bot_dev1 7a2e45e7c4 feat: 全站 Ant Design Pro 风格重构 + 登录守卫与角色门户(issue #131/#134)
- 新增 web/shared:pro.css(AntD 5 设计系统,旧 CSS 变量统一映射 AntD 色板)、
  session.js(后端优先+本地账号降级双轨会话,IAOP_AUTH 兼容层)、
  pro-header.js(全站统一 Pro 导航条:logo/页题/用户角色 Tag/退出)
- 门户 web/index.html:未登录跳登录页(next 回跳),登录后按角色过滤
  可见模块(readonly→驾驶舱/助手/移动端;engineer→+配置台;admin→全部)
- 登录页 auth/login.html 重写为 Ant Design Pro 风格,本地降级模式自动
  播种演示账号(admin/engineer/viewer),保留 OIDC SSO 双轨入口
- user_store.js 新增 verify() + seedDefaults() 默认账号播种
- 六模块页统一接入 pro.css + 统一导航条 + 登录守卫(PRD 8.2)
- 驾驶舱主题 dark→light(Ant Design 5 色板):renderer.py THEME_TOKENS、
  ti-cl4/resin 模板 theme、告警三级配色对齐 AntD(P0 #ff4d4f/P1 #faad14/
  P2 #1677ff),渲染计划已重编(build_plans.py)
- 验证:core/cockpit 86 单测通过;JS 语法检查通过;静态冒烟全 200
2026-08-05 14:17:37 +08:00

186 lines
6.9 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">
<title>iAOP 云美工业AI优化平台 · 门户</title>
<link rel="stylesheet" href="shared/pro.css">
<style>
/* iAOP 平台门户(Ant Design Pro 风格重构,2026-08)。
* 未登录跳登录页(?next 回跳);登录后按角色过滤可见模块:
* readonly → 驾驶舱/对话助手/移动端
* engineer → + 模板配置台
* admin → + 管理控制台/用户与角色(全部 6 模块) */
body { background: var(--ant-bg-layout); min-height: 100vh;
display: flex; flex-direction: column; margin: 0; }
.page-container { flex: 1; max-width: 1200px; width: 100%;
margin: 0 auto; padding: 24px; }
/* ---- PageHeader(AntD Pro 页头:标题 + 描述 + 右侧状态) --------------- */
.page-header {
background: var(--ant-bg-container);
border-radius: var(--ant-radius-lg);
box-shadow: var(--ant-shadow-card);
padding: 20px 24px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin-bottom: 16px;
}
.page-header .ph-title { font-size: 20px; font-weight: 600; }
.page-header .ph-desc { margin-top: 4px; font-size: 13px;
color: var(--ant-text-tertiary); }
.page-header .ph-extra { display: flex; align-items: center; gap: 8px;
font-size: 13px; color: var(--ant-text-secondary); }
/* ---- 模块卡片网格 ------------------------------------------------------ */
.module-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 16px;
}
a.module-card {
display: block;
text-decoration: none;
background: var(--ant-bg-container);
border: 1px solid var(--ant-split);
border-radius: var(--ant-radius-lg);
box-shadow: var(--ant-shadow-card);
padding: 20px 24px;
transition: box-shadow .2s, transform .2s;
color: var(--ant-text);
}
a.module-card:hover {
box-shadow: 0 3px 6px -4px rgba(0,0,0,.12), 0 6px 16px 0 rgba(0,0,0,.08),
0 9px 28px 8px rgba(0,0,0,.05);
transform: translateY(-2px);
}
.module-card .mc-head { display: flex; align-items: center; gap: 10px; }
.module-card .mc-icon {
width: 40px; height: 40px;
border-radius: var(--ant-radius-lg);
background: #e6f4ff;
display: flex; align-items: center; justify-content: center;
font-size: 20px;
flex-shrink: 0;
}
.module-card h2 { font-size: 16px; font-weight: 600; margin: 0; }
.module-card .mc-desc { margin-top: 12px; font-size: 13px;
color: var(--ant-text-tertiary); line-height: 1.7;
min-height: 44px; }
.module-card .mc-foot { margin-top: 12px; display: flex;
justify-content: space-between; align-items: center; }
.module-card .mc-enter { font-size: 13px; color: var(--ant-primary); }
a.module-card:hover .mc-enter { text-decoration: underline; }
.portal-footer { text-align: center; padding: 24px; font-size: 12px;
color: var(--ant-text-tertiary); }
</style>
</head>
<body data-pro-title="平台门户" data-pro-home="index.html" data-pro-login="auth/login.html">
<!--
iAOP 平台门户(2026-08 Ant Design Pro 风格重构)。
守卫:IAOP_SESSION.requireLoginElseRedirect —— 未登录跳 auth/login.html。
模块可见性:IAOP_SESSION.modulesFor(role)(PRD 8.2 三级 RBAC)。
-->
<div class="page-container">
<div class="page-header">
<div>
<div class="ph-title" id="welcome">平台门户</div>
<div class="ph-desc">iAOP-Core 通用内核 · Template-Ti(海绵钛)一期 · 配置化驾驶舱 / 模板配置台 / LLM 助手</div>
</div>
<div class="ph-extra">
推理服务
<span id="health" class="pro-tag pro-tag-default">检测中…</span>
</div>
</div>
<div class="module-grid" id="moduleGrid" aria-busy="true"></div>
</div>
<div class="portal-footer">iAOP-Core v1.0 · 部署底座 K8s/Helm · 推理服务 :30800 · 前端 :8090</div>
<script src="auth/user_store.js"></script>
<script src="shared/session.js"></script>
<script src="shared/pro-header.js"></script>
<script>
(function () {
"use strict";
function renderModules(user) {
var grid = document.getElementById("moduleGrid");
grid.innerHTML = "";
grid.removeAttribute("aria-busy");
document.getElementById("welcome").textContent =
"你好," + user.username + ",欢迎使用 iAOP 平台";
var modules = IAOP_SESSION.modulesFor(user.role);
modules.forEach(function (m) {
var a = document.createElement("a");
a.className = "module-card";
a.href = m.url;
var head = document.createElement("div");
head.className = "mc-head";
var icon = document.createElement("div");
icon.className = "mc-icon";
icon.textContent = m.icon;
var h2 = document.createElement("h2");
h2.textContent = m.title;
head.appendChild(icon);
head.appendChild(h2);
var desc = document.createElement("div");
desc.className = "mc-desc";
desc.textContent = m.desc;
var foot = document.createElement("div");
foot.className = "mc-foot";
var tag = document.createElement("span");
tag.className = "pro-tag pro-tag-" + (m.tagColor || "default");
tag.textContent = m.tag;
var enter = document.createElement("span");
enter.className = "mc-enter";
enter.textContent = "进入 →";
foot.appendChild(tag);
foot.appendChild(enter);
a.appendChild(head);
a.appendChild(desc);
a.appendChild(foot);
grid.appendChild(a);
});
}
// 登录守卫:未登录 → auth/login.html?next=<回跳地址>
IAOP_SESSION.requireLoginElseRedirect("auth/login.html").then(function (user) {
if (!user) return; // 正在跳转登录页
renderModules(user);
});
// 推理服务健康检测(直连 NodePort,CORS 已放行)
fetch("http://39.101.182.167:30800/health", { method: "GET" })
.then(function (r) { return r.json(); })
.then(function (d) {
var el = document.getElementById("health");
if (d.status === "ok" || d.status === "dry-run") {
el.textContent = "在线 · " + d.backend;
el.className = "pro-tag pro-tag-green";
} else {
el.textContent = "异常";
el.className = "pro-tag pro-tag-red";
}
})
.catch(function () {
var el = document.getElementById("health");
el.textContent = "不可达";
el.className = "pro-tag pro-tag-red";
});
})();
</script>
</body>
</html>