feat: split Orbit console into module views
This commit is contained in:
@@ -42,6 +42,8 @@
|
||||
|
||||
定时计划详情已接入当前版本、规则、下一次触发和 occurrence 历史;暂停/恢复沿用 `schedules:manage`、`expected_revision`、原因和幂等键。计划发布、跨命名空间管理身份和更高风险的交付控制仍按独立权限模型推进。
|
||||
|
||||
控制台五个核心模块已拆为独立 hash 视图,导航状态和页面标题随视图同步,刷新或直接打开对应 hash 后仍保持正确模块。数据请求继续由统一 Bearer Token 和当前队列范围驱动。
|
||||
|
||||
## 2. 持续推进方式
|
||||
|
||||
每个阶段按以下顺序推进:
|
||||
|
||||
@@ -14,6 +14,7 @@ http://127.0.0.1:8080/console/
|
||||
- 点击任务查看任务详情、执行尝试时间线、最终结果和操作审计摘要。
|
||||
- 在具备对应权限时提交任务取消和安全重试,沿用服务端的版本校验、幂等键、队列范围和审计规则。
|
||||
- 点击定时计划查看当前版本、规则、下一次触发和触发记录;在具备 `schedules:manage` 权限时可暂停或恢复计划。
|
||||
- 左侧五个模块是独立的控制台视图,使用 `/console/#/overview`、`#/tasks`、`#/schedules`、`#/workflows` 和 `#/subscriptions` 定位;切换视图不会把所有模块堆在一张长页面上。
|
||||
- 任务状态按 10 秒自动刷新;页面不会把凭据上传到独立服务,也不读取数据库。
|
||||
- 页面使用当前请求方的业务权限;没有权限的模块显示为空或等待权限,不绕过服务端授权。
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
const state = { token: '', queue: '', base: location.origin, timer: null, connected: false, loading: false, selectedTask: null, actionMode: '', selectedSchedule: null };
|
||||
const $ = (id) => document.getElementById(id);
|
||||
const text = (id, value) => { const el = $(id); if (el) el.textContent = value; };
|
||||
const pageNames = { overview: '总览', tasks: '任务执行', schedules: '定时计划', workflows: '流程实例', subscriptions: '结果订阅' };
|
||||
const sessionQueue = sessionStorage.getItem('orbit.console.queue') || '';
|
||||
$('api-url').value = location.origin;
|
||||
$('queue-id').value = sessionQueue;
|
||||
@@ -19,6 +20,18 @@
|
||||
function stateClass(value) { return String(value || '').toLowerCase(); }
|
||||
function extractItems(payload) { return payload?.data?.items || payload?.items || []; }
|
||||
function extractData(payload) { return payload?.data ?? payload ?? {}; }
|
||||
function currentView() {
|
||||
const raw = location.hash.replace(/^#\/?/, '').split('/')[0];
|
||||
return pageNames[raw] ? raw : 'overview';
|
||||
}
|
||||
function setView(view, updateHash = true) {
|
||||
const next = pageNames[view] ? view : 'overview';
|
||||
document.querySelectorAll('.page-view').forEach(page => page.classList.toggle('active', page.dataset.page === next));
|
||||
document.querySelectorAll('.nav-item').forEach(item => item.classList.toggle('active', item.dataset.view === next));
|
||||
text('page-title', pageNames[next]);
|
||||
if (updateHash && location.hash !== `#/${next}`) location.hash = `/${next}`;
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}
|
||||
async function request(path, options = {}) {
|
||||
const headers = { Accept: 'application/json', ...(options.headers || {}) };
|
||||
if (state.token) headers.Authorization = `Bearer ${state.token}`;
|
||||
@@ -69,7 +82,7 @@
|
||||
function renderSchedules(items) {
|
||||
text('metric-schedules', String(items.length));
|
||||
const el = $('schedule-list'); if (!items.length) { el.innerHTML = '<div class="empty-compact"><span>◷</span><div><b>当前没有定时计划</b><small>需要 schedules:read 权限</small></div></div>'; return; }
|
||||
el.innerHTML = items.slice(0, 4).map(item => `<div class="list-row schedule-row" data-schedule-id="${escapeHTML(item.id)}" tabindex="0" title="打开计划详情"><div class="list-avatar">◷</div><div class="list-copy"><b>${escapeHTML(item.name || shortID(item.id))}</b><small>${escapeHTML(item.queue_id ? shortID(item.queue_id) : '计划')}</small></div><div class="list-meta">${escapeHTML(item.paused ? '已暂停' : '运行中')}</div></div>`).join('');
|
||||
el.innerHTML = items.map(item => `<div class="list-row schedule-row" data-schedule-id="${escapeHTML(item.id)}" tabindex="0" title="打开计划详情"><div class="list-avatar">◷</div><div class="list-copy"><b>${escapeHTML(item.name || shortID(item.id))}</b><small>${escapeHTML(item.queue_id ? shortID(item.queue_id) : '计划')}</small></div><div class="list-meta">${escapeHTML(item.paused ? '已暂停' : '运行中')}</div></div>`).join('');
|
||||
el.querySelectorAll('.schedule-row').forEach(row => {
|
||||
row.addEventListener('click', () => openScheduleDetail(row.dataset.scheduleId));
|
||||
row.addEventListener('keydown', event => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); openScheduleDetail(row.dataset.scheduleId); } });
|
||||
@@ -78,14 +91,14 @@
|
||||
function renderWorkflows(items) {
|
||||
text('metric-workflows', String(items.length)); text('metric-workflows-sub', items.length ? '最近实例' : '当前没有实例');
|
||||
const el = $('workflow-list'); if (!items.length) { el.innerHTML = '<div class="empty-compact"><span>⌘</span><div><b>当前没有流程实例</b><small>需要 workflows:read 权限</small></div></div>'; return; }
|
||||
el.innerHTML = items.slice(0, 4).map(item => `<div class="list-row"><div class="list-avatar">⌘</div><div class="list-copy"><b>${escapeHTML(item.business_key || shortID(item.id))}</b><small>${escapeHTML(item.state || '—')}</small></div><div class="list-meta">${escapeHTML(formatTime(item.created_at))}</div></div>`).join('');
|
||||
el.innerHTML = items.map(item => `<div class="list-row"><div class="list-avatar">⌘</div><div class="list-copy"><b>${escapeHTML(item.business_key || shortID(item.id))}</b><small>${escapeHTML(item.state || '—')}</small></div><div class="list-meta">${escapeHTML(formatTime(item.created_at))}</div></div>`).join('');
|
||||
}
|
||||
function renderSubscriptions(items) {
|
||||
text('metric-subscriptions', String(items.length));
|
||||
const active = items.filter(item => item.enabled !== false).length; $('subscription-meter-fill').style.width = `${items.length ? active / items.length * 100 : 0}%`;
|
||||
text('metric-subscriptions-sub', items.length ? `${active} 个已启用 · 交付自动恢复` : '当前没有结果订阅');
|
||||
const el = $('subscription-list'); if (!items.length) { el.innerHTML = '<div class="empty-compact"><span>⌁</span><div><b>当前没有结果订阅</b><small>需要 events:read 权限</small></div></div>'; return; }
|
||||
el.innerHTML = items.slice(0, 4).map(item => `<div class="list-row"><div class="list-avatar">⌁</div><div class="list-copy"><b>${escapeHTML(item.name || shortID(item.id))}</b><small>待处理 ${escapeHTML(String(item.pending_count ?? 0))} · 上限 ${escapeHTML(String(item.max_pending ?? '—'))}</small></div><div class="list-meta">${item.enabled === false ? '已暂停' : '已启用'}</div></div>`).join('');
|
||||
el.innerHTML = items.map(item => `<div class="list-row"><div class="list-avatar">⌁</div><div class="list-copy"><b>${escapeHTML(item.name || shortID(item.id))}</b><small>待处理 ${escapeHTML(String(item.pending_count ?? 0))} · 上限 ${escapeHTML(String(item.max_pending ?? '—'))}</small></div><div class="list-meta">${item.enabled === false ? '已暂停' : '已启用'}</div></div>`).join('');
|
||||
}
|
||||
function escapeHTML(value) { return String(value ?? '').replace(/[&<>'"]/g, char => ({ '&': '&', '<': '<', '>': '>', "'": ''', '"': '"' }[char])); }
|
||||
function formatJSON(value) {
|
||||
@@ -220,6 +233,8 @@
|
||||
$('close-task-drawer').addEventListener('click', closeTaskDrawer); $('task-drawer-backdrop').addEventListener('click', event => { if (event.target.id === 'task-drawer-backdrop') closeTaskDrawer(); }); $('action-cancel').addEventListener('click', closeActionForm); $('task-action-form').addEventListener('submit', submitAction); $('task-cancel-button').addEventListener('click', () => openActionForm('cancel')); $('task-retry-button').addEventListener('click', () => openActionForm('retry')); $('close-schedule-drawer').addEventListener('click', closeScheduleDrawer); $('schedule-drawer-backdrop').addEventListener('click', event => { if (event.target.id === 'schedule-drawer-backdrop') closeScheduleDrawer(); }); $('schedule-control-button').addEventListener('click', openScheduleAction); $('schedule-action-cancel').addEventListener('click', closeScheduleAction); $('schedule-action-form').addEventListener('submit', submitScheduleAction); document.addEventListener('keydown', event => { if (event.key === 'Escape') { if (!$('task-action-form').hidden) closeActionForm(); else if (!$('schedule-action-form').hidden) closeScheduleAction(); else if (!$('task-drawer-backdrop').hidden) closeTaskDrawer(); else if (!$('schedule-drawer-backdrop').hidden) closeScheduleDrawer(); } });
|
||||
$('reveal-token').addEventListener('click', () => { const field = $('token'); field.type = field.type === 'password' ? 'text' : 'password'; $('reveal-token').textContent = field.type === 'password' ? '显示' : '隐藏'; });
|
||||
$('credential-file').addEventListener('change', event => { const file = event.target.files?.[0]; if (!file) return; const reader = new FileReader(); reader.onload = () => { try { const credential = JSON.parse(reader.result); $('token').value = credential.token || ''; $('queue-id').value = credential.queue_id || ''; showToast('凭据已读取,只保留在当前页面内'); } catch { showToast('凭据 JSON 无法解析'); } }; reader.readAsText(file); });
|
||||
document.querySelectorAll('[data-scroll]').forEach(button => button.addEventListener('click', () => { const target = $(button.dataset.scroll); if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' }); document.querySelectorAll('.nav-item').forEach(item => item.classList.toggle('active', item.dataset.scroll === button.dataset.scroll)); }));
|
||||
document.querySelectorAll('[data-view]').forEach(button => button.addEventListener('click', event => { event.preventDefault(); setView(button.dataset.view); }));
|
||||
window.addEventListener('hashchange', () => setView(currentView(), false));
|
||||
setView(currentView(), false);
|
||||
refresh(); state.timer = setInterval(refresh, 10000);
|
||||
})();
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
</div>
|
||||
<div class="workspace-label">CONTROL PLANE</div>
|
||||
<nav class="nav-list" aria-label="控制台导航">
|
||||
<button class="nav-item active" data-scroll="overview"><span class="nav-icon">⌂</span><span>总览</span><em>01</em></button>
|
||||
<button class="nav-item" data-scroll="tasks"><span class="nav-icon">◇</span><span>任务执行</span><em>02</em></button>
|
||||
<button class="nav-item" data-scroll="schedules"><span class="nav-icon">◷</span><span>定时计划</span><em>03</em></button>
|
||||
<button class="nav-item" data-scroll="workflows"><span class="nav-icon">⌘</span><span>流程实例</span><em>04</em></button>
|
||||
<button class="nav-item" data-scroll="subscriptions"><span class="nav-icon">⌁</span><span>结果订阅</span><em>05</em></button>
|
||||
<button class="nav-item active" data-view="overview"><span class="nav-icon">⌂</span><span>总览</span><em>01</em></button>
|
||||
<button class="nav-item" data-view="tasks"><span class="nav-icon">◇</span><span>任务执行</span><em>02</em></button>
|
||||
<button class="nav-item" data-view="schedules"><span class="nav-icon">◷</span><span>定时计划</span><em>03</em></button>
|
||||
<button class="nav-item" data-view="workflows"><span class="nav-icon">⌘</span><span>流程实例</span><em>04</em></button>
|
||||
<button class="nav-item" data-view="subscriptions"><span class="nav-icon">⌁</span><span>结果订阅</span><em>05</em></button>
|
||||
</nav>
|
||||
<div class="sidebar-bottom">
|
||||
<div class="rail-card">
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
<main class="main-content">
|
||||
<header class="topbar">
|
||||
<div class="crumbs"><span>Orbit</span><b>/</b><strong>控制台</strong></div>
|
||||
<div class="crumbs"><span>Orbit</span><b>/</b><strong id="page-title">总览</strong></div>
|
||||
<div class="topbar-actions">
|
||||
<span class="live-chip"><i></i><span id="live-label">本地环境</span></span>
|
||||
<span class="last-updated" id="last-updated">尚未刷新</span>
|
||||
@@ -44,54 +44,73 @@
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="hero" id="overview">
|
||||
<div class="hero-copy">
|
||||
<div class="eyebrow"><span class="eyebrow-line"></span> ORBIT CONTROL PLANE</div>
|
||||
<h1>让每一次执行<br><span>都清晰可见。</span></h1>
|
||||
<p>任务、定时、流程和交付状态,在一块屏幕上保持有序运转。</p>
|
||||
<div class="hero-actions">
|
||||
<button class="primary-button" data-scroll="tasks">查看任务 <span>↗</span></button>
|
||||
<button class="quiet-button" id="connect-hero">连接环境</button>
|
||||
<div class="page-view active" id="view-overview" data-page="overview">
|
||||
<section class="hero" id="overview">
|
||||
<div class="hero-copy">
|
||||
<div class="eyebrow"><span class="eyebrow-line"></span> ORBIT CONTROL PLANE</div>
|
||||
<h1>让每一次执行<br><span>都清晰可见。</span></h1>
|
||||
<p>任务、定时、流程和交付状态,在一块屏幕上保持有序运转。</p>
|
||||
<div class="hero-actions">
|
||||
<button class="primary-button" data-view="tasks">查看任务 <span>↗</span></button>
|
||||
<button class="quiet-button" id="connect-hero">连接环境</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-visual" aria-hidden="true">
|
||||
<div class="orbit-ring ring-one"></div>
|
||||
<div class="orbit-ring ring-two"></div>
|
||||
<div class="orbit-ring ring-three"></div>
|
||||
<div class="orbit-core"><span>O</span></div>
|
||||
<div class="orbit-node node-a"></div><div class="orbit-node node-b"></div><div class="orbit-node node-c"></div>
|
||||
<div class="hero-visual-label"><b>LIVE</b><span>任务流持续观测</span></div>
|
||||
</div>
|
||||
</section>
|
||||
<div class="hero-visual" aria-hidden="true">
|
||||
<div class="orbit-ring ring-one"></div>
|
||||
<div class="orbit-ring ring-two"></div>
|
||||
<div class="orbit-ring ring-three"></div>
|
||||
<div class="orbit-core"><span>O</span></div>
|
||||
<div class="orbit-node node-a"></div><div class="orbit-node node-b"></div><div class="orbit-node node-c"></div>
|
||||
<div class="hero-visual-label"><b>LIVE</b><span>任务流持续观测</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="metric-grid" aria-label="运行概览">
|
||||
<article class="metric-card metric-highlight"><div class="metric-top"><span>服务状态</span><span class="metric-icon green">✦</span></div><strong id="metric-health">—</strong><small id="metric-health-sub">等待就绪检查</small><div class="sparkline green-line"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div></article>
|
||||
<article class="metric-card"><div class="metric-top"><span>任务总览</span><span class="metric-icon purple">◇</span></div><strong id="metric-tasks">—</strong><small id="metric-tasks-sub">连接凭据后加载</small><div class="mini-bar"><i id="task-bar-succeeded"></i><i id="task-bar-running"></i><i id="task-bar-waiting"></i><i id="task-bar-other"></i></div></article>
|
||||
<article class="metric-card"><div class="metric-top"><span>流程实例</span><span class="metric-icon blue">⌘</span></div><strong id="metric-workflows">—</strong><small id="metric-workflows-sub">等待工作流权限</small><div class="sparkline blue-line"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div></article>
|
||||
<article class="metric-card"><div class="metric-top"><span>结果订阅</span><span class="metric-icon orange">⌁</span></div><strong id="metric-subscriptions">—</strong><small id="metric-subscriptions-sub">等待事件权限</small><div class="subscription-meter"><span id="subscription-meter-fill"></span></div></article>
|
||||
</section>
|
||||
<section class="metric-grid" aria-label="运行概览">
|
||||
<article class="metric-card metric-highlight"><div class="metric-top"><span>服务状态</span><span class="metric-icon green">✦</span></div><strong id="metric-health">—</strong><small id="metric-health-sub">等待就绪检查</small><div class="sparkline green-line"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div></article>
|
||||
<article class="metric-card"><div class="metric-top"><span>任务总览</span><span class="metric-icon purple">◇</span></div><strong id="metric-tasks">—</strong><small id="metric-tasks-sub">连接凭据后加载</small><div class="mini-bar"><i id="task-bar-succeeded"></i><i id="task-bar-running"></i><i id="task-bar-waiting"></i><i id="task-bar-other"></i></div></article>
|
||||
<article class="metric-card"><div class="metric-top"><span>流程实例</span><span class="metric-icon blue">⌘</span></div><strong id="metric-workflows">—</strong><small id="metric-workflows-sub">等待工作流权限</small><div class="sparkline blue-line"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div></article>
|
||||
<article class="metric-card"><div class="metric-top"><span>结果订阅</span><span class="metric-icon orange">⌁</span></div><strong id="metric-subscriptions">—</strong><small id="metric-subscriptions-sub">等待事件权限</small><div class="subscription-meter"><span id="subscription-meter-fill"></span></div></article>
|
||||
</section>
|
||||
|
||||
<section class="content-grid">
|
||||
<article class="panel wide-panel" id="tasks">
|
||||
<div class="panel-heading"><div><span class="panel-kicker">PULSE / TASKS</span><h2>任务执行流</h2></div><div class="panel-tools"><span class="table-count" id="task-count">0 项</span><button class="text-button" data-scroll="tasks">全部任务 <span>→</span></button></div></div>
|
||||
<div class="table-wrap"><table><thead><tr><th>任务</th><th>处理器</th><th>状态</th><th>修订</th><th>创建时间</th></tr></thead><tbody id="task-table"><tr><td colspan="5" class="empty-row"><span class="empty-icon">◇</span><b>还没有任务数据</b><small>连接一个具备 tasks:read 权限的凭据后自动加载</small></td></tr></tbody></table></div>
|
||||
</article>
|
||||
<article class="panel connect-panel" id="connection-panel">
|
||||
<div class="panel-heading"><div><span class="panel-kicker">ENVIRONMENT</span><h2>连接环境</h2></div><span class="connection-state" id="connection-state">未连接</span></div>
|
||||
<p class="panel-description">凭据只在当前浏览器会话中使用,不会上传到 Orbit Console。</p>
|
||||
<label class="field-label" for="api-url">API 地址</label><div class="input-wrap"><span class="input-prefix">↗</span><input id="api-url" type="url" value="" placeholder="http://127.0.0.1:8080"></div>
|
||||
<label class="field-label" for="queue-id">队列 ID</label><div class="input-wrap"><span class="input-prefix">#</span><input id="queue-id" type="text" placeholder="从凭据文件自动读取"></div>
|
||||
<label class="field-label" for="token">Bearer Token</label><div class="input-wrap"><span class="input-prefix">●</span><input id="token" type="password" placeholder="粘贴开发凭据 token"><button class="reveal-button" id="reveal-token" type="button">显示</button></div>
|
||||
<div class="connect-row"><label class="file-button"><input id="credential-file" type="file" accept="application/json">选择凭据 JSON</label><button class="primary-button compact" id="connect-button">连接 <span>→</span></button></div>
|
||||
<div class="security-note"><span>▣</span><span>当前页面只调用公开 API 和授权接口,不读取数据库。</span></div>
|
||||
</article>
|
||||
</section>
|
||||
<section class="content-grid single-column">
|
||||
<article class="panel connect-panel" id="connection-panel">
|
||||
<div class="panel-heading"><div><span class="panel-kicker">ENVIRONMENT</span><h2>连接环境</h2></div><span class="connection-state" id="connection-state">未连接</span></div>
|
||||
<p class="panel-description">凭据只在当前浏览器会话中使用,不会上传到 Orbit Console。</p>
|
||||
<label class="field-label" for="api-url">API 地址</label><div class="input-wrap"><span class="input-prefix">↗</span><input id="api-url" type="url" value="" placeholder="http://127.0.0.1:8080"></div>
|
||||
<label class="field-label" for="queue-id">队列 ID</label><div class="input-wrap"><span class="input-prefix">#</span><input id="queue-id" type="text" placeholder="从凭据文件自动读取"></div>
|
||||
<label class="field-label" for="token">Bearer Token</label><div class="input-wrap"><span class="input-prefix">●</span><input id="token" type="password" placeholder="粘贴开发凭据 token"><button class="reveal-button" id="reveal-token" type="button">显示</button></div>
|
||||
<div class="connect-row"><label class="file-button"><input id="credential-file" type="file" accept="application/json">选择凭据 JSON</label><button class="primary-button compact" id="connect-button">连接 <span>→</span></button></div>
|
||||
<div class="security-note"><span>▣</span><span>当前页面只调用公开 API 和授权接口,不读取数据库。</span></div>
|
||||
</article>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="lower-grid">
|
||||
<article class="panel" id="schedules"><div class="panel-heading"><div><span class="panel-kicker">PULSE / SCHEDULES</span><h2>定时计划</h2></div><button class="text-button" data-scroll="schedules">查看全部 <span>→</span></button></div><div class="schedule-list" id="schedule-list"><div class="empty-compact"><span>◷</span><div><b>等待计划数据</b><small>需要 schedules:read 权限</small></div></div></div></article>
|
||||
<article class="panel" id="workflows"><div class="panel-heading"><div><span class="panel-kicker">WEAVE / WORKFLOWS</span><h2>流程实例</h2></div><button class="text-button" data-scroll="workflows">查看全部 <span>→</span></button></div><div class="workflow-list" id="workflow-list"><div class="empty-compact"><span>⌘</span><div><b>等待流程数据</b><small>需要 workflows:read 权限</small></div></div></div></article>
|
||||
<article class="panel" id="subscriptions"><div class="panel-heading"><div><span class="panel-kicker">EVENTS / DELIVERY</span><h2>结果订阅</h2></div><span class="live-label-small"><i></i>自动刷新</span></div><div class="subscription-list" id="subscription-list"><div class="empty-compact"><span>⌁</span><div><b>等待订阅数据</b><small>需要 events:read 权限</small></div></div></div></article>
|
||||
</section>
|
||||
<div class="page-view" id="view-tasks" data-page="tasks">
|
||||
<section class="content-grid single-column">
|
||||
<article class="panel wide-panel" id="tasks">
|
||||
<div class="panel-heading"><div><span class="panel-kicker">PULSE / TASKS</span><h2>任务执行流</h2></div><div class="panel-tools"><span class="table-count" id="task-count">0 项</span><button class="text-button" data-view="overview">返回总览 <span>←</span></button></div></div>
|
||||
<div class="table-wrap"><table><thead><tr><th>任务</th><th>处理器</th><th>状态</th><th>修订</th><th>创建时间</th></tr></thead><tbody id="task-table"><tr><td colspan="5" class="empty-row"><span class="empty-icon">◇</span><b>还没有任务数据</b><small>连接一个具备 tasks:read 权限的凭据后自动加载</small></td></tr></tbody></table></div>
|
||||
</article>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="page-view" id="view-schedules" data-page="schedules">
|
||||
<section class="content-grid single-column">
|
||||
<article class="panel" id="schedules"><div class="panel-heading"><div><span class="panel-kicker">PULSE / SCHEDULES</span><h2>定时计划</h2></div><span class="table-count" id="schedule-page-hint">点击计划查看详情</span></div><div class="schedule-list" id="schedule-list"><div class="empty-compact"><span>◷</span><div><b>等待计划数据</b><small>需要 schedules:read 权限</small></div></div></div></article>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="page-view" id="view-workflows" data-page="workflows">
|
||||
<section class="content-grid single-column">
|
||||
<article class="panel" id="workflows"><div class="panel-heading"><div><span class="panel-kicker">WEAVE / WORKFLOWS</span><h2>流程实例</h2></div><span class="table-count">点击实例查看详情</span></div><div class="workflow-list" id="workflow-list"><div class="empty-compact"><span>⌘</span><div><b>等待流程数据</b><small>需要 workflows:read 权限</small></div></div></div></article>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="page-view" id="view-subscriptions" data-page="subscriptions">
|
||||
<section class="content-grid single-column">
|
||||
<article class="panel" id="subscriptions"><div class="panel-heading"><div><span class="panel-kicker">EVENTS / DELIVERY</span><h2>结果订阅</h2></div><span class="live-label-small"><i></i>自动刷新</span></div><div class="subscription-list" id="subscription-list"><div class="empty-compact"><span>⌁</span><div><b>等待订阅数据</b><small>需要 events:read 权限</small></div></div></div></article>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<footer class="page-footer"><span><span class="footer-orbit">◉</span> Orbit Console · 星轨控制台</span><span>数据每 10 秒刷新 · <a href="/version" target="_blank" rel="noreferrer">查看服务版本</a></span></footer>
|
||||
</main>
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user