mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-16 12:50:48 +08:00
重构 Admin 控制台信息架构与页面骨架,按业务域重排导航并统一配置页分节布局。
本次在保持现有主题配色不变的前提下,提升了可读性与操作效率,补充了页内分节导航与长页滚动定位体验。 Made-with: Cursor
This commit is contained in:
parent
37a2ef35f4
commit
85ce1194c9
3 changed files with 273 additions and 92 deletions
|
|
@ -1309,6 +1309,58 @@ function mount(node) {
|
||||||
c.appendChild(node);
|
c.appendChild(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderPageShell(opts = {}, children = []) {
|
||||||
|
const title = String(opts.title || "").trim();
|
||||||
|
const subtitle = String(opts.subtitle || "").trim();
|
||||||
|
const actions = Array.isArray(opts.actions) ? opts.actions.filter(Boolean) : [];
|
||||||
|
const sections = Array.isArray(opts.sections) ? opts.sections.filter((x) => x && x.id && x.label) : [];
|
||||||
|
const head = [];
|
||||||
|
if (title || subtitle || actions.length) {
|
||||||
|
const left = [];
|
||||||
|
if (title) left.push(el("h1", { class: "page-shell__title", text: title }));
|
||||||
|
if (subtitle) left.push(el("div", { class: "page-shell__subtitle muted", text: subtitle }));
|
||||||
|
head.push(
|
||||||
|
el("div", { class: "page-shell__header" }, [
|
||||||
|
el("div", { class: "page-shell__headline" }, left),
|
||||||
|
el("div", { class: "page-shell__actions row" }, actions),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
if (sections.length > 1) {
|
||||||
|
head.push(
|
||||||
|
el(
|
||||||
|
"div",
|
||||||
|
{ class: "page-shell__toc", role: "navigation", "aria-label": "page sections" },
|
||||||
|
sections.map((s) =>
|
||||||
|
el("button", {
|
||||||
|
class: "page-shell__tocItem",
|
||||||
|
type: "button",
|
||||||
|
text: String(s.label),
|
||||||
|
onclick: () => {
|
||||||
|
const target = document.getElementById(String(s.id));
|
||||||
|
if (!target) return;
|
||||||
|
target.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return el("section", { class: "page-shell" }, [...head, ...children.filter(Boolean)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSectionCard(title, subtitle, bodyNodes = [], opts = {}) {
|
||||||
|
const nodes = [el("div", { class: "card__title", text: title })];
|
||||||
|
if (subtitle) nodes.push(el("div", { class: "muted", text: subtitle }));
|
||||||
|
if (Array.isArray(opts.actions) && opts.actions.length) {
|
||||||
|
nodes.push(el("div", { class: "row section-card__actions" }, opts.actions));
|
||||||
|
}
|
||||||
|
bodyNodes.filter(Boolean).forEach((node) => nodes.push(node));
|
||||||
|
const attrs = { class: "card section-card" };
|
||||||
|
if (opts.id) attrs.id = String(opts.id);
|
||||||
|
return el("div", attrs, nodes);
|
||||||
|
}
|
||||||
|
|
||||||
function shortText(v, maxLen = 200) {
|
function shortText(v, maxLen = 200) {
|
||||||
const s = String(v ?? "");
|
const s = String(v ?? "");
|
||||||
if (s.length <= maxLen) return s;
|
if (s.length <= maxLen) return s;
|
||||||
|
|
@ -3836,25 +3888,32 @@ async function renderModels() {
|
||||||
// Show secret migration helper when legacy secrets exist.
|
// Show secret migration helper when legacy secrets exist.
|
||||||
topBits.push(secretsCard);
|
topBits.push(secretsCard);
|
||||||
|
|
||||||
return el("div", {}, [
|
return renderPageShell({
|
||||||
|
title: t("title.models"),
|
||||||
|
subtitle: "模型切换、绑定与评估配置",
|
||||||
|
sections: [
|
||||||
|
{ id: "models-overview", label: "概览" },
|
||||||
|
{ id: "models-bindings", label: "绑定" },
|
||||||
|
{ id: "models-api", label: "API配置" },
|
||||||
|
{ id: "models-experts", label: "Experts" },
|
||||||
|
{ id: "models-eval", label: "评估" },
|
||||||
|
],
|
||||||
|
}, [
|
||||||
...topBits,
|
...topBits,
|
||||||
el("div", { class: "card" }, [
|
el("div", { class: "page-grid page-grid--two" }, [
|
||||||
el("div", { class: "card__title", text: t("models.sectionActive") }),
|
renderSectionCard(t("models.sectionActive"), "", [
|
||||||
el("div", { class: "row" }, [el("label", { text: t("models.pickModel") }), activeSelect]),
|
el("div", { class: "row" }, [el("label", { text: t("models.pickModel") }), activeSelect]),
|
||||||
|
], { id: "models-overview" }),
|
||||||
|
renderSectionCard(t("models.sectionNew"), "", [
|
||||||
|
el("div", { class: "row" }, [newName, newMode, btnCreate]),
|
||||||
|
]),
|
||||||
]),
|
]),
|
||||||
el("div", { class: "card" }, [
|
renderSectionCard(t("models.sectionBindings"), t("models.agentBindingHelp"), [
|
||||||
el("div", { class: "card__title", text: t("models.sectionBindings") }),
|
|
||||||
el("div", { class: "muted", text: t("models.agentBindingHelp") }),
|
|
||||||
el("div", { class: "muted", text: t("models.bindingsExtraHint") }),
|
el("div", { class: "muted", text: t("models.bindingsExtraHint") }),
|
||||||
el("div", { class: "muted", text: t("models.bindingsScopeHint") }),
|
el("div", { class: "muted", text: t("models.bindingsScopeHint") }),
|
||||||
bindingWrap,
|
bindingWrap,
|
||||||
]),
|
], { id: "models-bindings" }),
|
||||||
el("div", { class: "card" }, [
|
renderSectionCard(t("models.sectionApi"), "", [
|
||||||
el("div", { class: "card__title", text: t("models.sectionNew") }),
|
|
||||||
el("div", { class: "row" }, [newName, newMode, btnCreate]),
|
|
||||||
]),
|
|
||||||
el("div", { class: "card" }, [
|
|
||||||
el("div", { class: "card__title", text: t("models.sectionApi") }),
|
|
||||||
builtinCap,
|
builtinCap,
|
||||||
readonlyProfileHint,
|
readonlyProfileHint,
|
||||||
el("div", { class: "row" }, [el("label", { text: t("models.profileName") }), profName]),
|
el("div", { class: "row" }, [el("label", { text: t("models.profileName") }), profName]),
|
||||||
|
|
@ -3867,18 +3926,16 @@ async function renderModels() {
|
||||||
el("div", { class: "row" }, [el("label", { text: t("models.apiKey") }), keyInp]),
|
el("div", { class: "row" }, [el("label", { text: t("models.apiKey") }), keyInp]),
|
||||||
el("div", { class: "row" }, [el("label", { text: t("models.rememberKey") }), rememberCb]),
|
el("div", { class: "row" }, [el("label", { text: t("models.rememberKey") }), rememberCb]),
|
||||||
el("div", { class: "row" }, [btnSave, btnDelete]),
|
el("div", { class: "row" }, [btnSave, btnDelete]),
|
||||||
]),
|
], { id: "models-api" }),
|
||||||
el("div", { class: "card" }, [
|
renderSectionCard("Experts (runtime/workspaces)", "Only SOUL.md and ROLE_SYSTEM.md are maintained in Admin. SOUL.md is required.", [
|
||||||
el("div", { class: "card__title", text: "Experts (runtime/workspaces)" }),
|
|
||||||
el("div", { class: "muted", text: "Only SOUL.md and ROLE_SYSTEM.md are maintained in Admin. SOUL.md is required." }),
|
|
||||||
el("div", { class: "row" }, [el("label", { text: "Existing" }), expertsSelect, btnExpertDelete]),
|
el("div", { class: "row" }, [el("label", { text: "Existing" }), expertsSelect, btnExpertDelete]),
|
||||||
el("div", { class: "row" }, [el("label", { text: "Create" }), expertNewId, btnExpertCreate]),
|
el("div", { class: "row" }, [el("label", { text: "Create" }), expertNewId, btnExpertCreate]),
|
||||||
el("div", { class: "row" }, [el("label", { text: "SOUL.md" }), expertSoul]),
|
el("div", { class: "row" }, [el("label", { text: "SOUL.md" }), expertSoul]),
|
||||||
el("div", { class: "row" }, [el("label", { text: "ROLE_SYSTEM.md" }), expertRoleSystem]),
|
el("div", { class: "row" }, [el("label", { text: "ROLE_SYSTEM.md" }), expertRoleSystem]),
|
||||||
el("div", { class: "row" }, [btnExpertSave]),
|
el("div", { class: "row" }, [btnExpertSave]),
|
||||||
expertsStatus,
|
expertsStatus,
|
||||||
]),
|
], { id: "models-experts" }),
|
||||||
el("div", { class: "card" }, [evalDetails]),
|
el("div", { class: "card section-card", id: "models-eval" }, [evalDetails]),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5618,24 +5675,28 @@ async function renderPlugins() {
|
||||||
bindingReverseWrap,
|
bindingReverseWrap,
|
||||||
bindingStatus,
|
bindingStatus,
|
||||||
]);
|
]);
|
||||||
const pluginsPageHeader = el("div", { class: "card" }, [
|
return renderPageShell({
|
||||||
el("div", { class: "card__title", text: t("plugins.title") }),
|
title: t("plugins.title"),
|
||||||
el("div", {
|
subtitle: "按功能分区展示,默认折叠;表格支持分页(每页 " + PLUGINS_PAGE_SIZE + " 条)。",
|
||||||
class: "muted",
|
sections: [
|
||||||
text: "以下按功能分为多个区块,默认折叠;表格均支持翻页(每页 " + PLUGINS_PAGE_SIZE + " 条)。",
|
{ id: "plugins-tool-policy", label: "策略" },
|
||||||
}),
|
{ id: "plugins-market", label: "市场" },
|
||||||
]);
|
{ id: "plugins-install", label: "安装" },
|
||||||
return el("div", {}, [
|
{ id: "plugins-instances", label: "实例" },
|
||||||
pluginsPageHeader,
|
{ id: "plugins-binding", label: "专家绑定" },
|
||||||
foldToolPolicy,
|
],
|
||||||
foldMcpMarket,
|
}, [
|
||||||
foldMcpInstall,
|
el("div", { class: "page-grid page-grid--single" }, [
|
||||||
foldMcpInstalled,
|
el("div", { id: "plugins-tool-policy" }, [foldToolPolicy]),
|
||||||
foldMcpUsage,
|
el("div", { id: "plugins-market" }, [foldMcpMarket]),
|
||||||
foldWireGlobal,
|
el("div", { id: "plugins-install" }, [foldMcpInstall]),
|
||||||
foldWireTools,
|
el("div", { id: "plugins-instances" }, [foldMcpInstalled]),
|
||||||
foldExpertBindingDash,
|
foldMcpUsage,
|
||||||
foldMcpBinding,
|
foldWireGlobal,
|
||||||
|
foldWireTools,
|
||||||
|
foldExpertBindingDash,
|
||||||
|
el("div", { id: "plugins-binding" }, [foldMcpBinding]),
|
||||||
|
]),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6163,13 +6224,17 @@ async function renderSessionMonitor() {
|
||||||
detailModal.addEventListener("click", (ev) => {
|
detailModal.addEventListener("click", (ev) => {
|
||||||
if (ev.target === detailModal) closeDetailModal();
|
if (ev.target === detailModal) closeDetailModal();
|
||||||
});
|
});
|
||||||
return el("div", {}, [
|
return renderPageShell({
|
||||||
el("div", { class: "card" }, [
|
title: t("title.sessionMonitor"),
|
||||||
el("div", { class: "card__title", text: t("sessionMonitor.totals") }),
|
subtitle: "按用户、会话与消息详情进行巡检与导出",
|
||||||
totalsWrap,
|
sections: [
|
||||||
]),
|
{ id: "sm-totals", label: "总览" },
|
||||||
el("div", { class: "card" }, [
|
{ id: "sm-users", label: "用户" },
|
||||||
el("div", { class: "card__title", text: t("sessionMonitor.userStats") }),
|
{ id: "sm-sessions", label: "会话" },
|
||||||
|
],
|
||||||
|
}, [
|
||||||
|
renderSectionCard(t("sessionMonitor.totals"), "", [totalsWrap], { id: "sm-totals" }),
|
||||||
|
renderSectionCard(t("sessionMonitor.userStats"), "", [
|
||||||
el("div", { class: "row" }, [userQ, btnQueryUsers]),
|
el("div", { class: "row" }, [userQ, btnQueryUsers]),
|
||||||
el("div", { class: "table-wrap" }, [
|
el("div", { class: "table-wrap" }, [
|
||||||
el("table", { class: "table table--compact" }, [
|
el("table", { class: "table table--compact" }, [
|
||||||
|
|
@ -6189,9 +6254,8 @@ async function renderSessionMonitor() {
|
||||||
userBody,
|
userBody,
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
]),
|
], { id: "sm-users" }),
|
||||||
el("div", { class: "card" }, [
|
renderSectionCard(t("sessionMonitor.sessions"), "", [
|
||||||
el("div", { class: "card__title", text: t("sessionMonitor.sessions") }),
|
|
||||||
el("div", { class: "row" }, [sessQ, activeOnlyLabel, btnQuerySessions]),
|
el("div", { class: "row" }, [sessQ, activeOnlyLabel, btnQuerySessions]),
|
||||||
selectedHint,
|
selectedHint,
|
||||||
sessionPagerTop,
|
sessionPagerTop,
|
||||||
|
|
@ -6212,7 +6276,7 @@ async function renderSessionMonitor() {
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
sessionPagerBottom,
|
sessionPagerBottom,
|
||||||
]),
|
], { id: "sm-sessions" }),
|
||||||
detailModal,
|
detailModal,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
@ -6582,42 +6646,53 @@ async function renderAttachments() {
|
||||||
inputEl,
|
inputEl,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return el("div", {}, [
|
return renderPageShell({
|
||||||
el("div", { class: "card" }, [
|
title: t("attachments.title"),
|
||||||
el("div", { class: "card__title", text: t("attachments.title") }),
|
subtitle: "附件处理阈值、预算与回放策略配置",
|
||||||
el("div", { class: "muted", text: t("attachments.excelPolicy") }),
|
actions: [loadBtn, resetBtn, saveBtn],
|
||||||
el("div", { style: "height:8px" }),
|
sections: [
|
||||||
hint,
|
{ id: "attach-overview", label: "说明" },
|
||||||
el("div", { style: "height:10px" }),
|
{ id: "attach-table", label: "表格" },
|
||||||
inputRow(t("attachments.maxRowsRead"), rowInput),
|
{ id: "attach-media", label: "图像/视频" },
|
||||||
inputRow(t("attachments.maxColumns"), colInput),
|
{ id: "attach-archive", label: "压缩包" },
|
||||||
inputRow(t("attachments.maxCellChars"), cellInput),
|
],
|
||||||
inputRow(t("attachments.maxExcelSheets"), maxSheetsInput),
|
}, [
|
||||||
inputRow(t("attachments.largePreviewRows"), largePreviewRowsInput),
|
renderSectionCard("策略说明", t("attachments.excelPolicy"), [hint], { id: "attach-overview" }),
|
||||||
el("label", { class: "muted", style: "display:flex;gap:8px;align-items:center;margin-top:10px;cursor:pointer;" }, [
|
el("div", { class: "page-grid page-grid--two" }, [
|
||||||
toolEnabledInput,
|
renderSectionCard("表格与工具模式", "", [
|
||||||
el("span", { text: t("attachments.toolModeEnabled") }),
|
inputRow(t("attachments.maxRowsRead"), rowInput),
|
||||||
]),
|
inputRow(t("attachments.maxColumns"), colInput),
|
||||||
inputRow(t("attachments.toolModeMinRows"), toolMinRowsInput),
|
inputRow(t("attachments.maxCellChars"), cellInput),
|
||||||
inputRow(t("attachments.toolModeMaxBytes"), toolMaxBytesInput),
|
inputRow(t("attachments.maxExcelSheets"), maxSheetsInput),
|
||||||
inputRow(t("attachments.sqlTimeoutMs"), sqlTimeoutInput),
|
inputRow(t("attachments.largePreviewRows"), largePreviewRowsInput),
|
||||||
el("div", { class: "muted", style: "margin-top:6px;line-height:1.45;", text: t("attachments.sqlTimeoutHint") }),
|
el("label", { class: "muted", style: "display:flex;gap:8px;align-items:center;margin-top:10px;cursor:pointer;" }, [
|
||||||
inputRow(t("attachments.imageReplayCapChars"), imageReplayCapInput),
|
toolEnabledInput,
|
||||||
el("div", { class: "muted", style: "margin-top:6px;line-height:1.45;", text: t("attachments.imageReplayCapHint") }),
|
el("span", { text: t("attachments.toolModeEnabled") }),
|
||||||
inputRow(t("attachments.videoReplayCapChars"), videoReplayCapInput),
|
]),
|
||||||
el("div", { class: "muted", style: "margin-top:6px;line-height:1.45;", text: t("attachments.videoReplayCapHint") }),
|
inputRow(t("attachments.toolModeMinRows"), toolMinRowsInput),
|
||||||
inputRow(t("attachments.videoTranscriptChunkSize"), videoTranscriptChunkSizeInput),
|
inputRow(t("attachments.toolModeMaxBytes"), toolMaxBytesInput),
|
||||||
inputRow(t("attachments.videoTranscriptChunkOverlap"), videoTranscriptChunkOverlapInput),
|
inputRow(t("attachments.sqlTimeoutMs"), sqlTimeoutInput),
|
||||||
el("div", { class: "muted", style: "margin-top:6px;line-height:1.45;", text: t("attachments.videoTranscriptChunkHint") }),
|
el("div", { class: "muted", style: "margin-top:6px;line-height:1.45;", text: t("attachments.sqlTimeoutHint") }),
|
||||||
|
], { id: "attach-table" }),
|
||||||
|
renderSectionCard("图像与视频策略", "", [
|
||||||
|
inputRow(t("attachments.imageReplayCapChars"), imageReplayCapInput),
|
||||||
|
el("div", { class: "muted", style: "margin-top:6px;line-height:1.45;", text: t("attachments.imageReplayCapHint") }),
|
||||||
|
inputRow(t("attachments.videoReplayCapChars"), videoReplayCapInput),
|
||||||
|
el("div", { class: "muted", style: "margin-top:6px;line-height:1.45;", text: t("attachments.videoReplayCapHint") }),
|
||||||
|
inputRow(t("attachments.videoTranscriptChunkSize"), videoTranscriptChunkSizeInput),
|
||||||
|
inputRow(t("attachments.videoTranscriptChunkOverlap"), videoTranscriptChunkOverlapInput),
|
||||||
|
el("div", { class: "muted", style: "margin-top:6px;line-height:1.45;", text: t("attachments.videoTranscriptChunkHint") }),
|
||||||
|
], { id: "attach-media" }),
|
||||||
|
]),
|
||||||
|
renderSectionCard("压缩包预算策略", "", [
|
||||||
inputRow(t("attachments.archiveMaxDepth"), archiveMaxDepthInput),
|
inputRow(t("attachments.archiveMaxDepth"), archiveMaxDepthInput),
|
||||||
inputRow(t("attachments.archiveMaxFileCount"), archiveMaxFileCountInput),
|
inputRow(t("attachments.archiveMaxFileCount"), archiveMaxFileCountInput),
|
||||||
inputRow(t("attachments.archiveMaxEntryBytes"), archiveMaxEntryBytesInput),
|
inputRow(t("attachments.archiveMaxEntryBytes"), archiveMaxEntryBytesInput),
|
||||||
inputRow(t("attachments.archiveMaxTotalBytes"), archiveMaxTotalBytesInput),
|
inputRow(t("attachments.archiveMaxTotalBytes"), archiveMaxTotalBytesInput),
|
||||||
el("div", { class: "muted", style: "margin-top:6px;line-height:1.45;", text: t("attachments.archivePolicyHint") }),
|
el("div", { class: "muted", style: "margin-top:6px;line-height:1.45;", text: t("attachments.archivePolicyHint") }),
|
||||||
el("div", { class: "row", style: "gap:8px;margin-top:12px;flex-wrap:wrap;" }, [loadBtn, resetBtn, saveBtn]),
|
|
||||||
el("div", { style: "height:8px" }),
|
el("div", { style: "height:8px" }),
|
||||||
status,
|
status,
|
||||||
]),
|
], { id: "attach-archive" }),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,19 +55,31 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<nav class="nav">
|
<nav class="nav">
|
||||||
<a class="nav__item" data-page="models" href="#/models" data-i18n="nav.models">模型管理</a>
|
<div class="nav__group">
|
||||||
<a class="nav__item" data-page="api-grants" href="#/api-grants" data-i18n="nav.apiGrants">API 使用授权</a>
|
<div class="nav__groupTitle">系统与运行</div>
|
||||||
<a class="nav__item" data-page="stack" href="#/stack" data-i18n="nav.stack">Runtime</a>
|
<a class="nav__item" data-page="stack" href="#/stack" data-i18n="nav.stack">Runtime</a>
|
||||||
<a class="nav__item" data-page="users" href="#/users" data-i18n="nav.users">用户管理</a>
|
<a class="nav__item" data-page="session-monitor" href="#/session-monitor" data-i18n="nav.sessionMonitor">会话监控</a>
|
||||||
<a class="nav__item" data-page="workspace-paths" href="#/workspace-paths" data-i18n="nav.workspacePaths">工作区路径</a>
|
<a class="nav__item" data-page="workspace-paths" href="#/workspace-paths" data-i18n="nav.workspacePaths">工作区路径</a>
|
||||||
<a class="nav__item" data-page="memory" href="#/memory" data-i18n="nav.memory">Memory</a>
|
<a class="nav__item" data-page="attachments" href="#/attachments" data-i18n="nav.attachments">附件</a>
|
||||||
<a class="nav__item" data-page="audit" href="#/audit" data-i18n="nav.audit">Audit & Trace</a>
|
</div>
|
||||||
<a class="nav__item" data-page="session-monitor" href="#/session-monitor" data-i18n="nav.sessionMonitor">会话监控</a>
|
<div class="nav__group">
|
||||||
<a class="nav__item" data-page="admin-audit" href="#/admin-audit" data-i18n="nav.adminAudit">Admin Audit</a>
|
<div class="nav__groupTitle">模型与插件</div>
|
||||||
<a class="nav__item" data-page="plugins" href="#/plugins" data-i18n="nav.plugins">Plugins</a>
|
<a class="nav__item" data-page="models" href="#/models" data-i18n="nav.models">模型管理</a>
|
||||||
<a class="nav__item" data-page="skills" href="#/skills" data-i18n="nav.skills">Skills</a>
|
<a class="nav__item" data-page="api-grants" href="#/api-grants" data-i18n="nav.apiGrants">API 使用授权</a>
|
||||||
<a class="nav__item" data-page="attachments" href="#/attachments" data-i18n="nav.attachments">附件</a>
|
<a class="nav__item" data-page="plugins" href="#/plugins" data-i18n="nav.plugins">Plugins</a>
|
||||||
<a class="nav__item" data-page="profile" href="#/profile" data-i18n="nav.profile">设置</a>
|
<a class="nav__item" data-page="skills" href="#/skills" data-i18n="nav.skills">Skills</a>
|
||||||
|
</div>
|
||||||
|
<div class="nav__group">
|
||||||
|
<div class="nav__groupTitle">治理与审计</div>
|
||||||
|
<a class="nav__item" data-page="memory" href="#/memory" data-i18n="nav.memory">Memory</a>
|
||||||
|
<a class="nav__item" data-page="audit" href="#/audit" data-i18n="nav.audit">Audit & Trace</a>
|
||||||
|
<a class="nav__item" data-page="admin-audit" href="#/admin-audit" data-i18n="nav.adminAudit">Admin Audit</a>
|
||||||
|
</div>
|
||||||
|
<div class="nav__group">
|
||||||
|
<div class="nav__groupTitle">用户与配置</div>
|
||||||
|
<a class="nav__item" data-page="users" href="#/users" data-i18n="nav.users">用户管理</a>
|
||||||
|
<a class="nav__item" data-page="profile" href="#/profile" data-i18n="nav.profile">设置</a>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="sidebar__footer">
|
<div class="sidebar__footer">
|
||||||
<div class="muted" data-i18n="notice.noLogin">v1 无登录:请仅在内网访问</div>
|
<div class="muted" data-i18n="notice.noLogin">v1 无登录:请仅在内网访问</div>
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,19 @@ body {
|
||||||
letter-spacing: 0.02em;
|
letter-spacing: 0.02em;
|
||||||
}
|
}
|
||||||
.brand__sub { font-size: 12px; color: rgba(226,232,240,0.7); margin-top: 4px; }
|
.brand__sub { font-size: 12px; color: rgba(226,232,240,0.7); margin-top: 4px; }
|
||||||
.nav { padding: 8px; display: flex; flex-direction: column; gap: 4px; }
|
.nav { padding: 8px; display: flex; flex-direction: column; gap: 12px; overflow: auto; }
|
||||||
|
.nav__group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.nav__groupTitle {
|
||||||
|
font-size: 11px;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(226,232,240,0.55);
|
||||||
|
padding: 6px 12px 2px;
|
||||||
|
}
|
||||||
.nav__item { text-decoration: none; color: rgba(226,232,240,0.85); padding: 10px 12px; border-radius: 10px; }
|
.nav__item { text-decoration: none; color: rgba(226,232,240,0.85); padding: 10px 12px; border-radius: 10px; }
|
||||||
.nav__item:hover { background: rgba(148,163,184,0.12); }
|
.nav__item:hover { background: rgba(148,163,184,0.12); }
|
||||||
.nav__item--active { background: rgba(59,130,246,0.22); color: #e2e8f0; }
|
.nav__item--active { background: rgba(59,130,246,0.22); color: #e2e8f0; }
|
||||||
|
|
@ -83,6 +95,82 @@ body {
|
||||||
.btn--danger { background: rgba(239,68,68,0.18); border-color: rgba(239,68,68,0.35); }
|
.btn--danger { background: rgba(239,68,68,0.18); border-color: rgba(239,68,68,0.35); }
|
||||||
.content { padding: 18px; overflow: auto; color: #e2e8f0; }
|
.content { padding: 18px; overflow: auto; color: #e2e8f0; }
|
||||||
|
|
||||||
|
.page-shell {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.page-shell__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 2px 2px 4px;
|
||||||
|
}
|
||||||
|
.page-shell__headline {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.page-shell__title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 1.2;
|
||||||
|
font-weight: 650;
|
||||||
|
}
|
||||||
|
.page-shell__subtitle {
|
||||||
|
max-width: 920px;
|
||||||
|
line-height: 1.45;
|
||||||
|
}
|
||||||
|
.page-shell__actions {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
.page-shell__toc {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.page-shell__tocItem {
|
||||||
|
text-decoration: none;
|
||||||
|
color: rgba(226,232,240,0.82);
|
||||||
|
border: 1px solid rgba(148,163,184,0.22);
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.page-shell__tocItem:hover {
|
||||||
|
background: rgba(148,163,184,0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.page-grid--single {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
.page-grid--two {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
.section-card {
|
||||||
|
margin-bottom: 0;
|
||||||
|
scroll-margin-top: 76px;
|
||||||
|
}
|
||||||
|
.section-card__actions {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
.page-grid > [id] {
|
||||||
|
scroll-margin-top: 76px;
|
||||||
|
}
|
||||||
|
.page-shell__header .page-shell__actions {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 3;
|
||||||
|
padding: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
.card { background: rgba(15,23,42,0.9); border: 1px solid rgba(148,163,184,0.18); border-radius: 14px; padding: 14px; margin-bottom: 12px; }
|
.card { background: rgba(15,23,42,0.9); border: 1px solid rgba(148,163,184,0.18); border-radius: 14px; padding: 14px; margin-bottom: 12px; }
|
||||||
.card__title { font-weight: 650; margin-bottom: 10px; }
|
.card__title { font-weight: 650; margin-bottom: 10px; }
|
||||||
.row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
|
.row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
|
||||||
|
|
@ -157,6 +245,12 @@ body {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.page-grid--two {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
.session-monitor-row--active {
|
.session-monitor-row--active {
|
||||||
background: rgba(94, 179, 255, 0.12);
|
background: rgba(94, 179, 255, 0.12);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue