This commit is contained in:
2026-05-08 15:16:04 +08:00
parent 042b626ccb
commit 507fb9789a
71 changed files with 14944 additions and 0 deletions
+316
View File
@@ -0,0 +1,316 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Asset Log Prototype</title>
<link rel="stylesheet" href="./styles.css">
</head>
<body data-page="asset-log">
<div class="site-shell">
<header class="topbar">
<a class="brand" href="./index.html">
<span class="brand-kicker">Mission Ops Prototype</span>
<span class="brand-name">Asset Log</span>
</a>
<nav class="topbar-nav">
<a href="./index.html">Overview</a>
<a href="./status-board.html">Status Board</a>
<a href="./location-board.html">Location Board</a>
<a class="active" href="./asset-log.html">Asset Log</a>
<a href="./timeline-view.html">Timeline View</a>
</nav>
</header>
<section class="hero">
<div class="hero-copy">
<p class="eyebrow">Single Asset Detail</p>
<h1>让单个资产的事件点和状态段在一页里同时可读</h1>
<p>这页不是普通日志表,而是当前状态、未来计划、历史记录和录入入口的组合页。这里用 XH-02 Taibai 作为样例对象。</p>
</div>
<div class="hero-side">
<section class="time-card">
<span class="label">Simulation Time</span>
<p class="time-value" data-sim-time-display>2060-03-12 09:00</p>
<p class="hero-note">Current state block and highlighted entry are both interpreted against this clock.</p>
</section>
<div class="metric-grid">
<article class="metric-card">
<span class="metric-label">Current Status</span>
<strong class="metric-value" data-current-state>Transit</strong>
</article>
<article class="metric-card">
<span class="metric-label">Next Event</span>
<strong class="metric-value" data-next-event-date>03-29</strong>
</article>
<article class="metric-card">
<span class="metric-label">Entries</span>
<strong class="metric-value">8</strong>
</article>
</div>
</div>
</section>
<section class="panel toolbar">
<div class="toolbar-row">
<label class="clock-field">
<span class="eyebrow">Simulation Time</span>
<input type="datetime-local" data-sim-time-input>
</label>
<button class="ghost-button" type="button" data-time-shift="-30">- 30d</button>
<button class="ghost-button" type="button" data-time-shift="-7">- 7d</button>
<button class="ghost-button" type="button" data-time-shift="7">+ 7d</button>
<button class="ghost-button" type="button" data-time-shift="30">+ 30d</button>
</div>
<div class="pill-row">
<span class="pill">Relative labels are derived from selected time</span>
<span class="pill">No manual Confirmed / Planned field on the log view</span>
</div>
</section>
<div class="split-layout">
<main class="stack">
<section class="panel stack">
<div class="asset-summary-grid">
<article class="asset-summary-card stack">
<div class="asset-summary-head">
<div>
<p class="eyebrow">Asset Summary</p>
<h2>XH-02 Taibai</h2>
<p class="subtle">Exploration Mothership · Mars Expedition Program</p>
</div>
<span class="status-pill" data-current-state-pill>In Transit</span>
</div>
<dl class="meta-grid">
<div class="meta-item">
<dt>Current Location</dt>
<dd data-current-location>Earth to Mars Transfer</dd>
</div>
<div class="meta-item">
<dt>Current Mission</dt>
<dd>Support Mars One Campus</dd>
</div>
<div class="meta-item">
<dt>State Window</dt>
<dd data-current-window>2060-02-26 06:00 → 2060-03-29 18:00</dd>
</div>
<div class="meta-item">
<dt>Next Planned Event</dt>
<dd data-next-event>Mars Arrival · 2060-03-29 18:00</dd>
</div>
</dl>
<div class="asset-chip-row">
<span class="chip strong">Crew Transfer Support</span>
<span class="chip">Mars System</span>
<span class="chip">Heavy Logistics</span>
</div>
</article>
<article class="asset-current-card stack">
<div class="section-head">
<div>
<p class="eyebrow">Current Derived State</p>
<h2>Operational Snapshot</h2>
</div>
</div>
<dl class="meta-grid">
<div class="meta-item">
<dt>Status</dt>
<dd>In Transit</dd>
</div>
<div class="meta-item">
<dt>Planned Arrival</dt>
<dd data-next-event-short>2060-03-29</dd>
</div>
<div class="meta-item">
<dt>Transfer Duration</dt>
<dd data-current-duration>31 days</dd>
</div>
<div class="meta-item">
<dt>Relative Mode</dt>
<dd data-relative-mode>Derived from selected clock</dd>
</div>
</dl>
</article>
</div>
</section>
<section class="panel stack">
<div class="section-head">
<div>
<p class="eyebrow">History</p>
<h2>Timeline Entries</h2>
<p>Event Point 和 State Interval 混排,但视觉上明确区分。</p>
</div>
</div>
<div class="entry-list">
<article class="entry event" data-kind="event" data-start="2058-03-15T10:20" data-title="First module delivered to Star Port" data-location="Star Port">
<div class="entry-rail"><span class="entry-dot"></span></div>
<div class="entry-copy">
<div class="entry-head">
<div>
<span class="entry-time">2058-03-15 10:20</span>
<h3>First module delivered to Star Port</h3>
</div>
<span class="tone-pill completed" data-entry-badge>Event Point</span>
</div>
<p>Core transfer spine and habitat ring unloaded for final assembly at Star Port.</p>
</div>
</article>
<article class="entry event" data-kind="event" data-start="2059-08-06T09:00" data-title="Commissioned" data-location="Star Port">
<div class="entry-rail"><span class="entry-dot"></span></div>
<div class="entry-copy">
<div class="entry-head">
<div>
<span class="entry-time">2059-08-06 09:00</span>
<h3>Commissioned</h3>
</div>
<span class="tone-pill completed" data-entry-badge>Event Point</span>
</div>
<p>Flight certification signed. Taibai was cleared for Mars transport duties.</p>
</div>
</article>
<article class="entry state" data-kind="state" data-start="2059-08-08T04:10" data-end="2059-10-17T13:40" data-title="Outbound transfer carrying Mars Expedition 3" data-location="Earth to Mars Transfer" data-state-label="Outbound Transfer">
<div class="entry-rail"><span class="entry-dot"></span></div>
<div class="entry-copy">
<div class="entry-head">
<div>
<span class="entry-time">2059-08-08 04:10 → 2059-10-17 13:40</span>
<h3>Outbound transfer carrying Mars Expedition 3</h3>
</div>
<span class="tone-pill completed" data-entry-badge>State Interval</span>
</div>
<p>Departure from Earth with full crew and descent stack attached. This is the kind of interval that should drive board state during the whole travel window.</p>
</div>
</article>
<article class="entry event" data-kind="event" data-start="2059-10-17T13:40" data-title="Mars Arrival" data-location="Mars Orbit">
<div class="entry-rail"><span class="entry-dot"></span></div>
<div class="entry-copy">
<div class="entry-head">
<div>
<span class="entry-time">2059-10-17 13:40</span>
<h3>Mars Arrival</h3>
</div>
<span class="tone-pill completed" data-entry-badge>Event Point</span>
</div>
<p>Insertion burn nominal. Crew descent staging began next day.</p>
</div>
</article>
<article class="entry state" data-kind="state" data-start="2059-10-18T08:00" data-end="2059-10-19T06:10" data-title="Mars orbit standby and crew pickup window" data-location="Mars Orbit" data-state-label="Mars Orbit Standby">
<div class="entry-rail"><span class="entry-dot"></span></div>
<div class="entry-copy">
<div class="entry-head">
<div>
<span class="entry-time">2059-10-18 08:00 → 2059-10-19 06:10</span>
<h3>Mars orbit standby and crew pickup window</h3>
</div>
<span class="tone-pill completed" data-entry-badge>State Interval</span>
</div>
<p>Received Mars Expedition 1 return crew after descent vehicle separation.</p>
</div>
</article>
<article class="entry state" data-kind="state" data-start="2060-02-26T06:00" data-end="2060-03-29T18:00" data-title="Support mission transfer to Mars One Campus" data-location="Earth to Mars Transfer" data-state-label="Support Transit">
<div class="entry-rail"><span class="entry-dot"></span></div>
<div class="entry-copy">
<div class="entry-head">
<div>
<span class="entry-time">2060-02-26 06:00 → 2060-03-29 18:00</span>
<h3>Support mission transfer to Mars One Campus</h3>
</div>
<span class="tone-pill active" data-entry-badge>State Interval</span>
</div>
<p>Carrying structural truss and greenhouse kit; 105 km/s, 31-day transition. This interval is the one currently lighting up Status Board.</p>
</div>
</article>
<article class="entry event" data-kind="event" data-start="2060-03-29T18:00" data-title="Mars Arrival" data-location="Mars Orbit">
<div class="entry-rail"><span class="entry-dot"></span></div>
<div class="entry-copy">
<div class="entry-head">
<div>
<span class="entry-time">2060-03-29 18:00</span>
<h3>Mars Arrival</h3>
</div>
<span class="tone-pill planned" data-entry-badge>Event Point</span>
</div>
<p>Planned capture burn and cargo handoff to surface fleet.</p>
</div>
</article>
</div>
</section>
</main>
<aside class="stack">
<section class="mini-panel stack">
<div class="section-head">
<div>
<p class="eyebrow">Authoring</p>
<h2>Entry Composer Mock</h2>
</div>
</div>
<div class="composer">
<label>
<span class="eyebrow">Mode</span>
<select>
<option>State Interval</option>
<option>Event Point</option>
</select>
</label>
<label>
<span class="eyebrow">Current Location</span>
<input type="text" value="Earth to Mars Transfer">
</label>
<label class="span-2">
<span class="eyebrow">Title</span>
<input type="text" value="Support mission transfer to Mars One Campus">
</label>
<label>
<span class="eyebrow">Start</span>
<input type="text" value="2060-02-26 06:00">
</label>
<label>
<span class="eyebrow">End</span>
<input type="text" value="2060-03-29 18:00">
</label>
<label>
<span class="eyebrow">From</span>
<input type="text" value="Earth">
</label>
<label>
<span class="eyebrow">To</span>
<input type="text" value="Mars">
</label>
<label class="span-2">
<span class="eyebrow">Summary</span>
<textarea>Carrying structural truss and greenhouse kit; 105 km/s, 31-day transition.</textarea>
</label>
<span class="button span-2">Save Prototype Entry</span>
</div>
</section>
<section class="mini-panel stack">
<div class="section-head">
<div>
<p class="eyebrow">Future</p>
<h2>Why This Page Matters</h2>
</div>
</div>
<div class="info-list subtle">
<p>当前状态不必单独维护一份,因为它可以从日志自动推导。</p>
<p>手动修正状态以后可以加,但第一版应该以日志推导为主。</p>
<p>Event Point 和 State Interval 分开录入,能显著减少歧义。</p>
<p>Past / Current / Upcoming 标签应当是相对于当前 Simulation Time 自动计算,而不是手工选 Confirmed 或 Planned。</p>
</div>
</section>
</aside>
</div>
</div>
<script src="./script.js"></script>
</body>
</html>
+206
View File
@@ -0,0 +1,206 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Mission Operations Prototype</title>
<link rel="stylesheet" href="./styles.css">
</head>
<body data-page="overview">
<div class="site-shell">
<header class="topbar">
<a class="brand" href="./index.html">
<span class="brand-kicker">KSP Static Prototype</span>
<span class="brand-name">Mission Operations</span>
</a>
<nav class="topbar-nav">
<a class="active" href="./index.html">Overview</a>
<a href="./status-board.html">Status Board</a>
<a href="./location-board.html">Location Board</a>
<a href="./asset-log.html">Asset Log</a>
<a href="./timeline-view.html">Timeline View</a>
</nav>
</header>
<section class="hero">
<div class="hero-copy">
<p class="eyebrow">Standalone Review Build</p>
<h1>载具与设施任务运营原型</h1>
<p>这是一套独立静态原型,不依赖 Flask 模板渲染。你现在双击这些 HTML 文件,浏览器里看到的就是页面设计本身,而不是 Jinja 源码。</p>
<div class="pill-row">
<span class="pill">Asset-first model</span>
<span class="pill">Event Point + State Interval</span>
<span class="pill">Simulation Time driven</span>
<span class="pill">Planned + Current + Past</span>
</div>
</div>
<div class="hero-side">
<section class="time-card">
<span class="label">Prototype Clock</span>
<p class="time-value" data-sim-time-display>2060-03-12 09:00</p>
<p class="hero-note">原型里所有当前状态、未来事件和时间线高亮,都围绕这个手动设置的 Simulation Time 进行解释。</p>
</section>
<div class="metric-grid">
<article class="metric-card">
<span class="metric-label">Tracked Assets</span>
<strong class="metric-value">14</strong>
</article>
<article class="metric-card">
<span class="metric-label">Active Missions</span>
<strong class="metric-value">6</strong>
</article>
<article class="metric-card">
<span class="metric-label">Upcoming 30d</span>
<strong class="metric-value">9</strong>
</article>
</div>
</div>
</section>
<section class="panel toolbar">
<div class="toolbar-row">
<label class="clock-field">
<span class="eyebrow">Simulation Time</span>
<input type="datetime-local" data-sim-time-input>
</label>
<button class="ghost-button" type="button" data-time-shift="-30">- 30d</button>
<button class="ghost-button" type="button" data-time-shift="-7">- 7d</button>
<button class="ghost-button" type="button" data-time-shift="7">+ 7d</button>
<button class="ghost-button" type="button" data-time-shift="30">+ 30d</button>
</div>
<div class="pill-row">
<span class="pill">Current state and next event are always relative to selected clock</span>
<span class="pill">Overview now includes recent past and upcoming event lists</span>
</div>
</section>
<section class="command-layout">
<main class="stack">
<section class="panel stack">
<div class="section-head">
<div>
<p class="eyebrow">Mission Center</p>
<h2>当前正在运行的关键任务</h2>
<p>首页主区域不再是页面目录,而是更像值班台,优先告诉你当前系统里什么任务正在跑。</p>
</div>
</div>
<div class="ops-list" id="active-ops-list">
<article class="ops-item">
<div class="card-top">
<div>
<h3>Loading active operations</h3>
<p class="asset-type">Preparing mission center feed</p>
</div>
</div>
<p class="summary-text">This list will update relative to the selected clock.</p>
</article>
</div>
</section>
<section class="panel stack">
<div class="section-head">
<div>
<p class="eyebrow">Attention Queue</p>
<h2>接下来需要处理的事项</h2>
<p>这一栏更接近任务队列,不是简单的未来事件列表。它应该告诉你“值班时下一步要盯什么”。</p>
</div>
</div>
<div class="attention-list" id="attention-queue-list">
<article class="attention-item">
<div class="card-top">
<div>
<h3>Loading attention queue</h3>
<p class="asset-type">Preparing next actions</p>
</div>
</div>
<p class="summary-text">This queue will show near-term actions relative to the selected clock.</p>
</article>
</div>
</section>
<section class="panel stack">
<div class="section-head">
<div>
<p class="eyebrow">Operational Shortcuts</p>
<h2>工作面板入口</h2>
<p>四个详细页面仍然保留,但在首页里它们应该是任务中心的工作入口,而不是首页主内容本身。</p>
</div>
</div>
<div class="shortcut-grid">
<a class="link-card" href="./status-board.html">
<strong>Status Board</strong>
<h3>按状态看当前全局</h3>
<p>快速扫描“现在有哪些资产正在干什么”。</p>
</a>
<a class="link-card" href="./location-board.html">
<strong>Location Board</strong>
<h3>按地点看资产分布</h3>
<p>适合从 Earth System / Mars System 角度看当前部署。</p>
</a>
<a class="link-card" href="./asset-log.html">
<strong>Asset Log</strong>
<h3>进入单个资产日志</h3>
<p>查看某个站、舰船、基地完整的事件和状态演变。</p>
</a>
<a class="link-card" href="./timeline-view.html">
<strong>Timeline View</strong>
<h3>多资产并行时间线</h3>
<p>在时间范围内看多个关键资产的联动关系。</p>
</a>
</div>
</section>
</main>
<aside class="stack">
<section class="panel stack">
<div class="section-head">
<div>
<p class="eyebrow">Recent Past</p>
<h2>最近发生的事件</h2>
</div>
</div>
<div class="upcoming-list" id="recent-past-events">
<article class="upcoming-item">
<time>Loading...</time>
<h3>Preparing recent events</h3>
<p class="subtle">This feed will update relative to the selected clock.</p>
</article>
</div>
</section>
<section class="panel stack">
<div class="section-head">
<div>
<p class="eyebrow">Upcoming</p>
<h2>接下来将发生的事件</h2>
</div>
</div>
<div class="upcoming-list" id="recent-future-events">
<article class="upcoming-item">
<time>Loading...</time>
<h3>Preparing future events</h3>
<p class="subtle">This feed will update relative to the selected clock.</p>
</article>
</div>
</section>
<section class="panel stack">
<div class="section-head">
<div>
<p class="eyebrow">Control Notes</p>
<h2>任务中心的设计原则</h2>
</div>
</div>
<div class="info-list subtle">
<p>首页应该先回答“当前要盯什么”,而不是“系统里有几页”。</p>
<p>当前运行任务和待处理事项应占据主舞台。</p>
<p>过去与未来事件保留在右侧,作为辅助判断时间上下文的侧栏。</p>
<p>四个详细页面退到工作面板入口,符合 Mission Control 的使用习惯。</p>
</div>
</section>
</aside>
</section>
</div>
<script src="./script.js"></script>
</body>
</html>
+260
View File
@@ -0,0 +1,260 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Location Board Prototype</title>
<link rel="stylesheet" href="./styles.css">
</head>
<body data-page="location-board">
<div class="site-shell">
<header class="topbar">
<a class="brand" href="./index.html">
<span class="brand-kicker">Mission Ops Prototype</span>
<span class="brand-name">Location Board</span>
</a>
<nav class="topbar-nav">
<a href="./index.html">Overview</a>
<a href="./status-board.html">Status Board</a>
<a class="active" href="./location-board.html">Location Board</a>
<a href="./asset-log.html">Asset Log</a>
<a href="./timeline-view.html">Timeline View</a>
</nav>
</header>
<section class="hero">
<div class="hero-copy">
<p class="eyebrow">Location-first Board</p>
<h1>按地点分组看每个系统当前都聚集了什么资产</h1>
<p>这张板子和 Status Board 是并列关系,不是一个页面里的切换模式。它更适合回答“Earth System 现在有什么、Mars System 现在有什么”。</p>
</div>
<div class="hero-side">
<section class="time-card">
<span class="label">Simulation Time</span>
<p class="time-value" data-sim-time-display>2060-03-12 09:00</p>
<p class="hero-note">Grouping is based on current location hierarchy. Status still appears on each card so the operational meaning remains visible.</p>
</section>
<div class="metric-grid">
<article class="metric-card">
<span class="metric-label">Location Groups</span>
<strong class="metric-value">4</strong>
</article>
<article class="metric-card">
<span class="metric-label">Earth System</span>
<strong class="metric-value">3</strong>
</article>
<article class="metric-card">
<span class="metric-label">Mars System</span>
<strong class="metric-value">2</strong>
</article>
</div>
</div>
</section>
<section class="panel toolbar">
<div class="toolbar-row">
<label class="clock-field">
<span class="eyebrow">Simulation Time</span>
<input type="datetime-local" data-sim-time-input>
</label>
<button class="ghost-button" type="button" data-time-shift="-30">- 30d</button>
<button class="ghost-button" type="button" data-time-shift="-7">- 7d</button>
<button class="ghost-button" type="button" data-time-shift="7">+ 7d</button>
<button class="ghost-button" type="button" data-time-shift="30">+ 30d</button>
</div>
<div class="toolbar-grid">
<label>
<span class="eyebrow">Location Layer</span>
<select>
<option>Top-level Systems</option>
</select>
</label>
<label>
<span class="eyebrow">Status Filter</span>
<select>
<option>All Active States</option>
</select>
</label>
<label>
<span class="eyebrow">Asset Type</span>
<select>
<option>All Asset Types</option>
</select>
</label>
<label>
<span class="eyebrow">Visibility</span>
<select>
<option>Confirmed + Planned</option>
</select>
</label>
<span class="button">Apply</span>
<span class="ghost-button">Reset</span>
</div>
<div class="pill-row">
<span class="pill">Grouping: Location hierarchy</span>
<span class="pill">Status preserved on each asset card</span>
<span class="pill">Supports Station, Port, Surface Site later</span>
</div>
</section>
<div class="split-layout">
<main class="stack">
<section class="panel stack">
<div class="location-head">
<div>
<p class="eyebrow">Location Group</p>
<h2>Earth System</h2>
<p>Ports, orbital stations, and near-Earth transfer lanes.</p>
</div>
<span class="count-badge">3 Assets</span>
</div>
<div class="location-grid">
<article class="location-item">
<div class="card-top">
<div>
<h3>Lingxiao Station</h3>
<p class="asset-type">Station</p>
</div>
<span class="status-pill">Docked / On Station</span>
</div>
<p class="summary-text">Low Kerbin Orbit. Continuous habitation and logistics hub.</p>
<div class="location-foot">
<span>Next: Expedition 16 Arrival · 2060-03-30</span>
<span>Current crew handover prep</span>
</div>
</article>
<article class="location-item">
<div class="card-top">
<div>
<h3>XH-03 Zhuque</h3>
<p class="asset-type">Shuttle</p>
</div>
<span class="status-pill">At Port</span>
</div>
<p class="summary-text">Star Port. Refit phase for Jupiter relay deployment mission.</p>
<div class="location-foot">
<span>Next: Rollout Review · 2060-04-12</span>
<span>Bay 4</span>
</div>
</article>
<article class="location-item">
<div class="card-top">
<div>
<h3>Amalthea Crew Vehicle 4</h3>
<p class="asset-type">Shuttle</p>
</div>
<span class="status-pill">In Transit</span>
</div>
<p class="summary-text">LKO transfer corridor. Planned crew rotation for Lingxiao Station.</p>
<div class="location-foot">
<span>Next: Docking Window · 2060-03-30</span>
<span>10 crew manifest</span>
</div>
</article>
</div>
</section>
<section class="panel stack">
<div class="location-head">
<div>
<p class="eyebrow">Location Group</p>
<h2>Mars System</h2>
<p>Orbital operations and deployed surface infrastructure around Mars.</p>
</div>
<span class="count-badge">2 Assets</span>
</div>
<div class="location-grid">
<article class="location-item">
<div class="card-top">
<div>
<h3>Mars One Campus Alpha</h3>
<p class="asset-type">Surface Outpost</p>
</div>
<span class="status-pill">Surface Ops</span>
</div>
<p class="summary-text">Primary surface site. Habitat expansion, ISRU staging, and logistics intake point.</p>
<div class="location-foot">
<span>Next: Taibai Supply Window · 2060-03-29</span>
<span>Needs truss + greenhouse kit</span>
</div>
</article>
<article class="location-item">
<div class="card-top">
<div>
<h3>XH-02 Taibai</h3>
<p class="asset-type">Exploration Mothership</p>
</div>
<span class="status-pill">In Transit</span>
</div>
<p class="summary-text">Mars transfer lane. Support mission inbound for Mars One Campus surface expansion.</p>
<div class="location-foot">
<span>Next: Mars Arrival · 2060-03-29</span>
<span>31-day transfer</span>
</div>
</article>
</div>
</section>
<section class="panel stack">
<div class="location-head">
<div>
<p class="eyebrow">Location Group</p>
<h2>Deep Space / Future Hooks</h2>
<p>Reserved for future hierarchy like Jupiter System, relay chains, and transit corridors.</p>
</div>
<span class="count-badge">Prototype Placeholder</span>
</div>
<div class="warning-list subtle">
<article class="warning-item">
<strong>Location hierarchy will matter</strong>
Earth, Star Port, Low Kerbin Orbit, Lingxiao Station should eventually form a parent-child location structure.
</article>
<article class="warning-item">
<strong>Facilities are first-class assets</strong>
Stations, ports, and surface outposts appear here exactly like ships, which is why the model must be Asset-first.
</article>
</div>
</section>
</main>
<aside class="stack">
<section class="mini-panel stack">
<div class="section-head">
<div>
<p class="eyebrow">Interpretation</p>
<h2>Board Reading Notes</h2>
</div>
</div>
<div class="info-list subtle">
<p>这张板子优先回答“地点上现在有什么”,不是“资产现在什么状态”。</p>
<p>状态信息仍然保留在卡片右上角,避免地点视图失去任务语义。</p>
<p>层级地点会在正式开发里成为关键模型,而不是临时筛选项。</p>
</div>
</section>
<section class="mini-panel stack">
<div class="section-head">
<div>
<p class="eyebrow">Upcoming</p>
<h2>Upcoming Mars-side Events</h2>
</div>
</div>
<div class="upcoming-list">
<article class="upcoming-item">
<time>2060-03-29</time>
<h3>XH-02 Taibai Mars Arrival</h3>
<p class="subtle">Mars System</p>
</article>
<article class="upcoming-item">
<time>2060-04-03</time>
<h3>Campus Alpha Structural Unload</h3>
<p class="subtle">Mars Surface</p>
</article>
</div>
</section>
</aside>
</div>
</div>
<script src="./script.js"></script>
</body>
</html>
+629
View File
@@ -0,0 +1,629 @@
const SIM_TIME_STORAGE_KEY = "mission-ops-sim-time";
const DEFAULT_SIM_TIME = "2060-03-12T09:00";
const OVERVIEW_EVENTS = [
{ when: "2059-12-27 09:00", title: "XH-02 Taibai parked to Star Port", scope: "Earth System" },
{ when: "2059-12-27 12:00", title: "Mars Expedition 1 crew returned to Earth", scope: "Earth System" },
{ when: "2060-02-26 06:00", title: "XH-02 Taibai departed Star Port for Mars support mission", scope: "Earth System" },
{ when: "2060-03-18 10:00", title: "Tianhe Cargo Pod 7 undock from Lingxiao Station", scope: "Lingxiao Station" },
{ when: "2060-03-29 18:00", title: "XH-02 Taibai arrives at Mars", scope: "Mars System" },
{ when: "2060-03-30 08:00", title: "Expedition 16 crew arrives via Amalthea Crew Vehicle", scope: "Lingxiao Station" },
{ when: "2060-04-02 09:00", title: "Expedition 15 crew departs Lingxiao Station", scope: "Lingxiao Station" },
{ when: "2060-04-12 14:00", title: "XH-03 Zhuque rollout review", scope: "Star Port" },
].map((event) => ({
...event,
date: parseDateTime(event.when),
}));
const OVERVIEW_ACTIVE_OPERATIONS = [
{
asset: "XH-02 Taibai",
assetType: "Exploration Mothership",
location: "Earth to Mars Transfer",
mission: "Support Mars One Campus",
start: "2060-02-26T06:00",
end: "2060-03-29T18:00",
next: "Mars Arrival · 2060-03-29 18:00",
note: "Carrying structural truss, greenhouse kit, and support cargo.",
},
{
asset: "Lingxiao Station",
assetType: "Station",
location: "Low Kerbin Orbit",
mission: "Continuous habitation and logistics hub",
start: "2059-07-23T00:00",
end: "",
next: "Expedition 16 Arrival · 2060-03-30 08:00",
note: "Crew handover preparation and consumables balancing in progress.",
},
{
asset: "Tianhe Cargo Pod 7",
assetType: "Cargo Shuttle",
location: "Lingxiao Forward Berth",
mission: "Dry cargo replenishment",
start: "2060-03-01T12:00",
end: "2060-03-18T10:00",
next: "Undock · 2060-03-18 10:00",
note: "Return trash loading remains before departure.",
},
{
asset: "XH-03 Zhuque",
assetType: "Shuttle",
location: "Star Port",
mission: "Jupiter relay refit",
start: "2060-01-08T09:00",
end: "2060-04-12T14:00",
next: "Rollout Review · 2060-04-12 14:00",
note: "Main engine refurbishment at bay 4 and payload integration queued.",
},
{
asset: "Mars One Campus Alpha",
assetType: "Surface Outpost",
location: "Mars Surface",
mission: "Habitat expansion and ISRU staging",
start: "2059-10-20T08:00",
end: "",
next: "Taibai Supply Window · 2060-03-29 18:00",
note: "Awaiting new structural truss and greenhouse package delivery.",
},
];
const OVERVIEW_ATTENTION_ITEMS = [
{
title: "Review Tianhe Cargo Pod 7 undock checklist",
asset: "Tianhe Cargo Pod 7",
priority: "high",
due: "2060-03-18T10:00",
summary: "Finalize return trash manifest and station berth reconfiguration.",
},
{
title: "Finalize Expedition 16 docking prep",
asset: "Lingxiao Station",
priority: "high",
due: "2060-03-26T18:00",
summary: "Confirm medical bay allocation, handover timeline, and EVA support window.",
},
{
title: "Prepare XH-02 Taibai Mars arrival burn package",
asset: "XH-02 Taibai",
priority: "high",
due: "2060-03-29T18:00",
summary: "Review capture burn, cargo handoff staging, and surface drop order.",
},
{
title: "Approve XH-03 Zhuque rollout readiness package",
asset: "XH-03 Zhuque",
priority: "medium",
due: "2060-04-12T14:00",
summary: "Complete avionics validation and relay payload handling plan.",
},
];
const TIMELINE_ASSET_CATALOG = [
{ id: "taibai", name: "XH-02 Taibai", type: "Exploration Mothership", region: "Mars System", note: "Mars support transfer" },
{ id: "lingxiao", name: "Lingxiao Station", type: "Station", region: "Earth System", note: "Crew rotation hub" },
{ id: "campus-alpha", name: "Mars One Campus Alpha", type: "Surface Outpost", region: "Mars System", note: "Surface habitat expansion" },
{ id: "amalthea-4", name: "Amalthea Crew Vehicle 4", type: "Shuttle", region: "Earth System", note: "Crew rotation shuttle" },
{ id: "zhuque", name: "XH-03 Zhuque", type: "Shuttle", region: "Earth System", note: "Relay deployment refit" },
{ id: "tianhe-7", name: "Tianhe Cargo Pod 7", type: "Cargo Shuttle", region: "Earth System", note: "Station logistics shuttle" },
];
const TIMELINE_PRESETS = {
active: ["taibai", "lingxiao", "campus-alpha"],
earth: ["lingxiao", "amalthea-4", "zhuque", "tianhe-7"],
mars: ["taibai", "campus-alpha"],
crew: ["lingxiao", "amalthea-4", "taibai"],
};
let selectedTimelineAssets = new Set(["taibai", "lingxiao", "campus-alpha"]);
function parseDateTime(value) {
if (!value) {
return null;
}
const normalized = value.includes("T") ? value : value.replace(" ", "T");
const parsed = new Date(normalized);
return Number.isNaN(parsed.getTime()) ? null : parsed;
}
function padNumber(value) {
return String(value).padStart(2, "0");
}
function toInputValue(date) {
return [
date.getFullYear(),
"-",
padNumber(date.getMonth() + 1),
"-",
padNumber(date.getDate()),
"T",
padNumber(date.getHours()),
":",
padNumber(date.getMinutes()),
].join("");
}
function toDisplayValue(date) {
return [
date.getFullYear(),
"-",
padNumber(date.getMonth() + 1),
"-",
padNumber(date.getDate()),
" ",
padNumber(date.getHours()),
":",
padNumber(date.getMinutes()),
].join("");
}
function toShortDate(date) {
return [padNumber(date.getMonth() + 1), "-", padNumber(date.getDate())].join("");
}
function shiftDays(date, dayOffset) {
const nextDate = new Date(date);
nextDate.setDate(nextDate.getDate() + dayOffset);
return nextDate;
}
function formatRelativeDayLabel(simDate, targetDate) {
const dayDelta = Math.round((targetDate - simDate) / (1000 * 60 * 60 * 24));
if (dayDelta < 0) {
return `${Math.abs(dayDelta)}d ago`;
}
if (dayDelta === 0) {
return "Today";
}
return `in ${dayDelta}d`;
}
function renderEventFeed(container, events, emptyLabel) {
if (!container) {
return;
}
if (!events.length) {
container.innerHTML = `
<article class="upcoming-item">
<time>No events</time>
<h3>${emptyLabel}</h3>
<p class="subtle">Change the simulation time to inspect a different period.</p>
</article>
`;
return;
}
container.innerHTML = events
.map(
(event) => `
<article class="upcoming-item">
<time>${event.when}</time>
<h3>${event.title}</h3>
<p class="subtle">${event.scope}</p>
</article>
`
)
.join("");
}
function renderOverviewOperations(simDate) {
const container = document.getElementById("active-ops-list");
if (!container) {
return;
}
const activeOperations = OVERVIEW_ACTIVE_OPERATIONS.filter((item) => {
const start = parseDateTime(item.start);
const end = parseDateTime(item.end || "");
return start && start <= simDate && (!end || simDate < end);
});
if (!activeOperations.length) {
container.innerHTML = `
<article class="ops-item">
<div class="card-top">
<div>
<h3>No active operations</h3>
<p class="asset-type">No State Interval covers the selected clock</p>
</div>
</div>
</article>
`;
return;
}
container.innerHTML = activeOperations
.map(
(item) => `
<article class="ops-item">
<div class="card-top">
<div>
<h3>${item.asset}</h3>
<p class="asset-type">${item.assetType}</p>
</div>
<span class="status-pill">${item.location}</span>
</div>
<dl class="meta-grid">
<div class="meta-item">
<dt>Mission</dt>
<dd>${item.mission}</dd>
</div>
<div class="meta-item">
<dt>Next Event</dt>
<dd>${item.next}</dd>
</div>
</dl>
<p class="summary-text">${item.note}</p>
</article>
`
)
.join("");
}
function renderAttentionQueue(simDate) {
const container = document.getElementById("attention-queue-list");
if (!container) {
return;
}
const queueItems = OVERVIEW_ATTENTION_ITEMS
.map((item) => ({ ...item, dueDate: parseDateTime(item.due) }))
.filter((item) => item.dueDate && item.dueDate >= shiftDays(simDate, -7))
.sort((left, right) => left.dueDate - right.dueDate)
.slice(0, 4);
if (!queueItems.length) {
container.innerHTML = `
<article class="attention-item">
<div class="card-top">
<div>
<h3>No pending queue items</h3>
<p class="asset-type">Nothing falls into the current operational window</p>
</div>
</div>
</article>
`;
return;
}
container.innerHTML = queueItems
.map((item) => {
const priorityClass = item.dueDate < simDate ? "overdue" : item.priority;
const priorityLabel = item.dueDate < simDate ? "Overdue" : item.priority;
return `
<article class="attention-item">
<div class="card-top">
<div>
<h3>${item.title}</h3>
<p class="asset-type">${item.asset}</p>
</div>
<span class="priority-chip ${priorityClass}">${priorityLabel}</span>
</div>
<dl class="meta-grid">
<div class="meta-item">
<dt>Due</dt>
<dd>${toDisplayValue(item.dueDate)}</dd>
</div>
<div class="meta-item">
<dt>Relative</dt>
<dd>${formatRelativeDayLabel(simDate, item.dueDate)}</dd>
</div>
</dl>
<p class="summary-text">${item.summary}</p>
</article>
`;
})
.join("");
}
function updateOverview(simDate) {
if (document.body.dataset.page !== "overview") {
return;
}
const pastEvents = OVERVIEW_EVENTS
.filter((event) => event.date && event.date <= simDate)
.sort((left, right) => right.date - left.date)
.slice(0, 4)
.map((event) => ({ ...event, when: toDisplayValue(event.date) }));
const futureEvents = OVERVIEW_EVENTS
.filter((event) => event.date && event.date > simDate)
.sort((left, right) => left.date - right.date)
.slice(0, 4)
.map((event) => ({ ...event, when: toDisplayValue(event.date) }));
renderOverviewOperations(simDate);
renderAttentionQueue(simDate);
renderEventFeed(document.getElementById("recent-past-events"), pastEvents, "No recent past events");
renderEventFeed(document.getElementById("recent-future-events"), futureEvents, "No upcoming events");
}
function updateAssetLog(simDate) {
if (document.body.dataset.page !== "asset-log") {
return;
}
const entries = Array.from(document.querySelectorAll(".entry[data-start]"));
let currentStateEntry = null;
let nextEventEntry = null;
entries.forEach((entry) => {
const badge = entry.querySelector("[data-entry-badge]");
const start = parseDateTime(entry.dataset.start);
const end = parseDateTime(entry.dataset.end || "");
const kind = entry.dataset.kind;
if (!badge || !start) {
return;
}
badge.classList.remove("completed", "planned", "active");
if (kind === "state") {
if (start <= simDate && (!end || simDate < end)) {
badge.textContent = "Current Interval";
badge.classList.add("active");
currentStateEntry = entry;
} else if (simDate < start) {
badge.textContent = "Upcoming Interval";
badge.classList.add("planned");
} else {
badge.textContent = "Past Interval";
badge.classList.add("completed");
}
} else if (simDate < start) {
badge.textContent = "Upcoming Event";
badge.classList.add("planned");
} else {
badge.textContent = "Past Event";
badge.classList.add("completed");
}
if (start > simDate && (!nextEventEntry || start < parseDateTime(nextEventEntry.dataset.start))) {
nextEventEntry = entry;
}
});
const currentStateText = document.querySelector("[data-current-state]");
const currentStatePill = document.querySelector("[data-current-state-pill]");
const currentLocation = document.querySelector("[data-current-location]");
const currentWindow = document.querySelector("[data-current-window]");
const nextEventText = document.querySelector("[data-next-event]");
const nextEventShort = document.querySelector("[data-next-event-short]");
const nextEventDate = document.querySelector("[data-next-event-date]");
const currentDuration = document.querySelector("[data-current-duration]");
const relativeMode = document.querySelector("[data-relative-mode]");
if (currentStateEntry) {
const start = parseDateTime(currentStateEntry.dataset.start);
const end = parseDateTime(currentStateEntry.dataset.end || "");
const stateLabel = currentStateEntry.dataset.stateLabel || currentStateEntry.dataset.title;
if (currentStateText) currentStateText.textContent = stateLabel;
if (currentStatePill) currentStatePill.textContent = stateLabel;
if (currentLocation) currentLocation.textContent = currentStateEntry.dataset.location || "Unknown";
if (currentWindow) currentWindow.textContent = `${toDisplayValue(start)}${end ? toDisplayValue(end) : "Open"}`;
if (currentDuration && end) {
const totalDays = Math.round((end - start) / (1000 * 60 * 60 * 24));
currentDuration.textContent = `${totalDays} days`;
}
if (relativeMode) relativeMode.textContent = "Derived from active State Interval";
} else {
if (currentStateText) currentStateText.textContent = "No Active Interval";
if (currentStatePill) currentStatePill.textContent = "No Active Interval";
if (currentLocation) currentLocation.textContent = "Unknown";
if (currentWindow) currentWindow.textContent = "No interval covers selected time";
if (currentDuration) currentDuration.textContent = "-";
if (relativeMode) relativeMode.textContent = "Derived: no active interval at selected time";
}
if (nextEventEntry) {
const nextDate = parseDateTime(nextEventEntry.dataset.start);
const nextLabel = `${nextEventEntry.dataset.title} · ${toDisplayValue(nextDate)}`;
if (nextEventText) nextEventText.textContent = nextLabel;
if (nextEventShort) nextEventShort.textContent = toDisplayValue(nextDate);
if (nextEventDate) nextEventDate.textContent = toShortDate(nextDate);
} else {
if (nextEventText) nextEventText.textContent = "No future event recorded";
if (nextEventShort) nextEventShort.textContent = "-";
if (nextEventDate) nextEventDate.textContent = "-";
}
}
function getTimelineFilteredAssets() {
const searchInput = document.querySelector("[data-asset-search]");
const typeFilter = document.querySelector("[data-asset-type-filter]");
const regionFilter = document.querySelector("[data-asset-region-filter]");
const searchTerm = (searchInput?.value || "").trim().toLowerCase();
const selectedType = typeFilter?.value || "all";
const selectedRegion = regionFilter?.value || "all";
return TIMELINE_ASSET_CATALOG.filter((asset) => {
const matchesSearch = !searchTerm || `${asset.name} ${asset.type} ${asset.region} ${asset.note}`.toLowerCase().includes(searchTerm);
const matchesType = selectedType === "all" || asset.type === selectedType;
const matchesRegion = selectedRegion === "all" || asset.region === selectedRegion;
return matchesSearch && matchesType && matchesRegion;
});
}
function updateTimelineSelection() {
if (document.body.dataset.page !== "timeline") {
return;
}
const rows = Array.from(document.querySelectorAll(".timeline-row[data-asset]"));
const selectedAssets = Array.from(selectedTimelineAssets);
const selectedNames = TIMELINE_ASSET_CATALOG.filter((asset) => selectedTimelineAssets.has(asset.id)).map((asset) => asset.name);
rows.forEach((row) => {
row.classList.toggle("is-hidden", !selectedAssets.includes(row.dataset.asset));
});
const selectedCount = document.querySelector("[data-selected-count]");
const selectedSummary = document.querySelector("[data-selected-assets]");
if (selectedCount) {
selectedCount.textContent = String(selectedAssets.length);
}
if (selectedSummary) {
selectedSummary.textContent = selectedNames.length ? `Selected: ${selectedNames.join(", ")}` : "Selected: none";
}
}
function renderTimelineSelector() {
if (document.body.dataset.page !== "timeline") {
return;
}
const resultsContainer = document.querySelector("[data-asset-results]");
const selectedContainer = document.querySelector("[data-selected-asset-list]");
if (!resultsContainer || !selectedContainer) {
return;
}
const filteredAssets = getTimelineFilteredAssets();
const selectedAssetsData = TIMELINE_ASSET_CATALOG.filter((asset) => selectedTimelineAssets.has(asset.id));
resultsContainer.innerHTML = filteredAssets.length
? filteredAssets
.map(
(asset) => `
<article class="asset-result-card">
<div class="card-top">
<div>
<h3>${asset.name}</h3>
<p class="asset-type">${asset.type}</p>
</div>
<span class="status-pill">${asset.region}</span>
</div>
<p class="summary-text">${asset.note}</p>
<div class="asset-result-foot">
<span>${selectedTimelineAssets.has(asset.id) ? "Already selected" : "Available to add"}</span>
<button class="selector-add" type="button" data-add-asset="${asset.id}" ${selectedTimelineAssets.has(asset.id) ? "disabled" : ""}>${selectedTimelineAssets.has(asset.id) ? "Added" : "Add to Timeline"}</button>
</div>
</article>
`
)
.join("")
: `
<article class="asset-result-card">
<div class="card-top">
<div>
<h3>No matching assets</h3>
<p class="asset-type">Try another search or filter combination</p>
</div>
</div>
</article>
`;
selectedContainer.innerHTML = selectedAssetsData.length
? selectedAssetsData
.map(
(asset) => `
<article class="selected-asset-card">
<div class="card-top">
<div>
<h3>${asset.name}</h3>
<p class="asset-type">${asset.type}</p>
</div>
<span class="status-pill">${asset.region}</span>
</div>
<div class="selected-asset-foot">
<span>${asset.note}</span>
<button class="selector-remove" type="button" data-remove-asset="${asset.id}">Remove</button>
</div>
</article>
`
)
.join("")
: `
<article class="selected-asset-card">
<strong>No assets selected</strong>
<p class="summary-text">Add assets from the search results to build the current timeline focus set.</p>
</article>
`;
resultsContainer.querySelectorAll("[data-add-asset]").forEach((button) => {
button.addEventListener("click", () => {
selectedTimelineAssets.add(button.dataset.addAsset);
renderTimelineSelector();
updateTimelineSelection();
});
});
selectedContainer.querySelectorAll("[data-remove-asset]").forEach((button) => {
button.addEventListener("click", () => {
selectedTimelineAssets.delete(button.dataset.removeAsset);
renderTimelineSelector();
updateTimelineSelection();
});
});
}
function setupTimelineSelection() {
if (document.body.dataset.page !== "timeline") {
return;
}
document.querySelectorAll("[data-asset-search], [data-asset-type-filter], [data-asset-region-filter]").forEach((control) => {
control.addEventListener(control.tagName === "INPUT" ? "input" : "change", renderTimelineSelector);
});
document.querySelectorAll("[data-asset-preset]").forEach((button) => {
button.addEventListener("click", () => {
selectedTimelineAssets = new Set(TIMELINE_PRESETS[button.dataset.assetPreset] || []);
renderTimelineSelector();
updateTimelineSelection();
});
});
renderTimelineSelector();
updateTimelineSelection();
}
function syncSimulationTime(simDate) {
const inputValue = toInputValue(simDate);
const displayValue = toDisplayValue(simDate);
document.querySelectorAll("[data-sim-time-input]").forEach((input) => {
if (input.value !== inputValue) {
input.value = inputValue;
}
});
document.querySelectorAll("[data-sim-time-display]").forEach((display) => {
display.textContent = displayValue;
});
localStorage.setItem(SIM_TIME_STORAGE_KEY, inputValue);
updateOverview(simDate);
updateAssetLog(simDate);
}
document.addEventListener("DOMContentLoaded", () => {
let simDate = parseDateTime(localStorage.getItem(SIM_TIME_STORAGE_KEY)) || parseDateTime(DEFAULT_SIM_TIME);
document.querySelectorAll("[data-sim-time-input]").forEach((input) => {
input.addEventListener("input", (event) => {
const nextDate = parseDateTime(event.target.value);
if (!nextDate) {
return;
}
simDate = nextDate;
syncSimulationTime(simDate);
});
});
document.querySelectorAll("[data-time-shift]").forEach((button) => {
button.addEventListener("click", () => {
simDate = shiftDays(simDate, Number(button.dataset.timeShift));
syncSimulationTime(simDate);
});
});
syncSimulationTime(simDate);
setupTimelineSelection();
});
+398
View File
@@ -0,0 +1,398 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Status Board Prototype</title>
<link rel="stylesheet" href="./styles.css">
</head>
<body data-page="status-board">
<div class="site-shell">
<header class="topbar">
<a class="brand" href="./index.html">
<span class="brand-kicker">Mission Ops Prototype</span>
<span class="brand-name">Status Board</span>
</a>
<nav class="topbar-nav">
<a href="./index.html">Overview</a>
<a class="active" href="./status-board.html">Status Board</a>
<a href="./location-board.html">Location Board</a>
<a href="./asset-log.html">Asset Log</a>
<a href="./timeline-view.html">Timeline View</a>
</nav>
</header>
<section class="hero">
<div class="hero-copy">
<p class="eyebrow">Current State Board</p>
<h1>按状态分组看当前有哪些资产在执行什么任务</h1>
<p>这个页面只回答一件事:在当前 Simulation Time 下,每个 Asset 正处于哪个状态、在哪里、在做什么,以及下一个关键事件是什么。</p>
</div>
<div class="hero-side">
<section class="time-card">
<span class="label">Simulation Time</span>
<p class="time-value" data-sim-time-display>2060-03-12 09:00</p>
<p class="hero-note">Current status is derived from active State Interval. Planned events remain visible on the board.</p>
</section>
<div class="metric-grid">
<article class="metric-card">
<span class="metric-label">Active Assets</span>
<strong class="metric-value">8</strong>
</article>
<article class="metric-card">
<span class="metric-label">In Transit</span>
<strong class="metric-value">2</strong>
</article>
<article class="metric-card">
<span class="metric-label">Upcoming 7d</span>
<strong class="metric-value">3</strong>
</article>
</div>
</div>
</section>
<section class="panel toolbar">
<div class="toolbar-row">
<label class="clock-field">
<span class="eyebrow">Simulation Time</span>
<input type="datetime-local" data-sim-time-input>
</label>
<button class="ghost-button" type="button" data-time-shift="-30">- 30d</button>
<button class="ghost-button" type="button" data-time-shift="-7">- 7d</button>
<button class="ghost-button" type="button" data-time-shift="7">+ 7d</button>
<button class="ghost-button" type="button" data-time-shift="30">+ 30d</button>
</div>
<div class="toolbar-row">
<span class="button">Set Simulation Time</span>
</div>
<div class="toolbar-grid">
<label>
<span class="eyebrow">Asset Type</span>
<select>
<option>All Asset Types</option>
</select>
</label>
<label>
<span class="eyebrow">Location Filter</span>
<select>
<option>Earth System</option>
</select>
</label>
<label>
<span class="eyebrow">Program</span>
<select>
<option>All Programs</option>
</select>
</label>
<label>
<span class="eyebrow">Records</span>
<select>
<option>Confirmed + Planned</option>
</select>
</label>
<span class="button">Apply</span>
<span class="ghost-button">Reset</span>
</div>
<div class="pill-row">
<span class="pill">Grouping: Current State</span>
<span class="pill">Location used as secondary filter</span>
<span class="pill">Future planned events remain visible</span>
</div>
</section>
<div class="split-layout">
<main class="stack">
<section class="panel stack">
<div class="group-head">
<div>
<p class="eyebrow">State Group</p>
<h2>In Transit</h2>
<p>Assets currently travelling between systems or operational nodes.</p>
</div>
<span class="count-badge">2 Assets</span>
</div>
<div class="group-stack">
<article class="status-card blue">
<div class="card-top">
<div>
<h3>XH-02 Taibai</h3>
<p class="asset-type">Exploration Mothership</p>
</div>
<span class="tone-pill active">Confirmed</span>
</div>
<dl class="meta-grid">
<div class="meta-item">
<dt>Location</dt>
<dd>Earth to Mars Transfer</dd>
</div>
<div class="meta-item">
<dt>Mission</dt>
<dd>Support Mars One Campus</dd>
</div>
<div class="meta-item">
<dt>State Window</dt>
<dd>2060-02-26 → 2060-03-29</dd>
</div>
<div class="meta-item">
<dt>Next Event</dt>
<dd>Mars Arrival · 2060-03-29</dd>
</div>
</dl>
<p class="summary-text">31-day transition carrying structural truss, greenhouse kit, and support consumables for Mars One Campus Alpha.</p>
<div class="card-foot">
<span>Derived from active State Interval</span>
<span>Open asset log</span>
</div>
</article>
<article class="status-card amber">
<div class="card-top">
<div>
<h3>Amalthea Crew Vehicle 4</h3>
<p class="asset-type">Shuttle</p>
</div>
<span class="tone-pill planned">Planned</span>
</div>
<dl class="meta-grid">
<div class="meta-item">
<dt>Location</dt>
<dd>LKO Transfer Corridor</dd>
</div>
<div class="meta-item">
<dt>Mission</dt>
<dd>Expedition 16 Crew Rotation</dd>
</div>
<div class="meta-item">
<dt>State Window</dt>
<dd>2060-03-28 → 2060-03-30</dd>
</div>
<div class="meta-item">
<dt>Next Event</dt>
<dd>Docking Window · 2060-03-30</dd>
</div>
</dl>
<p class="summary-text">Planned departure with 10-crew manifest for Lingxiao Station rotation and medical cargo transfer.</p>
<div class="card-foot">
<span>Visible before execution because Planned stays on board</span>
<span>Open asset log</span>
</div>
</article>
</div>
</section>
<section class="panel stack">
<div class="group-head">
<div>
<p class="eyebrow">State Group</p>
<h2>Docked / On Station</h2>
<p>Stationary orbital assets and docked support craft.</p>
</div>
<span class="count-badge">2 Assets</span>
</div>
<div class="group-stack">
<article class="status-card teal">
<div class="card-top">
<div>
<h3>Lingxiao Station</h3>
<p class="asset-type">Station</p>
</div>
<span class="tone-pill active">Active</span>
</div>
<dl class="meta-grid">
<div class="meta-item">
<dt>Location</dt>
<dd>Low Kerbin Orbit</dd>
</div>
<div class="meta-item">
<dt>Mission</dt>
<dd>Continuous habitation and logistics hub</dd>
</div>
<div class="meta-item">
<dt>State Window</dt>
<dd>2059-07-23 → Present</dd>
</div>
<div class="meta-item">
<dt>Next Event</dt>
<dd>Expedition 16 Crew Arrival · 2060-03-30</dd>
</div>
</dl>
<p class="summary-text">Expedition 15 currently onboard. Crew handover prep and life-support reserve balancing underway.</p>
<div class="card-foot">
<span>Can later host nested docked assets</span>
<span>Open asset log</span>
</div>
</article>
<article class="status-card soft">
<div class="card-top">
<div>
<h3>Tianhe Cargo Pod 7</h3>
<p class="asset-type">Cargo Shuttle</p>
</div>
<span class="tone-pill completed">Confirmed</span>
</div>
<dl class="meta-grid">
<div class="meta-item">
<dt>Location</dt>
<dd>Lingxiao Forward Berth</dd>
</div>
<div class="meta-item">
<dt>Mission</dt>
<dd>Dry cargo replenishment</dd>
</div>
<div class="meta-item">
<dt>State Window</dt>
<dd>2060-03-01 → 2060-03-18</dd>
</div>
<div class="meta-item">
<dt>Next Event</dt>
<dd>Undock · 2060-03-18</dd>
</div>
</dl>
<p class="summary-text">Food, EVA packs, filter cartridges, and station spares are already unloaded. Return trash loading remains.</p>
<div class="card-foot">
<span>Useful test case for attached support craft</span>
<span>Open asset log</span>
</div>
</article>
</div>
</section>
<section class="panel stack">
<div class="group-head">
<div>
<p class="eyebrow">State Group</p>
<h2>At Port / Surface Ops</h2>
<p>Idle, refit, maintenance, and deployed surface infrastructure.</p>
</div>
<span class="count-badge">2 Assets</span>
</div>
<div class="group-stack">
<article class="status-card soft">
<div class="card-top">
<div>
<h3>XH-03 Zhuque</h3>
<p class="asset-type">Shuttle</p>
</div>
<span class="tone-pill active">Confirmed</span>
</div>
<dl class="meta-grid">
<div class="meta-item">
<dt>Location</dt>
<dd>Star Port</dd>
</div>
<div class="meta-item">
<dt>Mission</dt>
<dd>Refit for Jupiter relay deployment</dd>
</div>
<div class="meta-item">
<dt>State Window</dt>
<dd>2060-01-08 → 2060-04-12</dd>
</div>
<div class="meta-item">
<dt>Next Event</dt>
<dd>Rollout Review · 2060-04-12</dd>
</div>
</dl>
<p class="summary-text">Main engine refurbishment at bay 4. Payload integration queued after avionics test completion.</p>
<div class="card-foot">
<span>Example of a non-flight operational state</span>
<span>Open asset log</span>
</div>
</article>
<article class="status-card teal">
<div class="card-top">
<div>
<h3>Mars One Campus Alpha</h3>
<p class="asset-type">Surface Outpost</p>
</div>
<span class="tone-pill active">Active</span>
</div>
<dl class="meta-grid">
<div class="meta-item">
<dt>Location</dt>
<dd>Mars Surface</dd>
</div>
<div class="meta-item">
<dt>Mission</dt>
<dd>Habitat expansion and ISRU staging</dd>
</div>
<div class="meta-item">
<dt>State Window</dt>
<dd>2059-10-20 → Present</dd>
</div>
<div class="meta-item">
<dt>Next Event</dt>
<dd>Taibai Supply Window · 2060-03-29</dd>
</div>
</dl>
<p class="summary-text">Surface crew awaiting structural truss shipment, greenhouse package, and two new power skids.</p>
<div class="card-foot">
<span>Long-duration persistent state example</span>
<span>Open asset log</span>
</div>
</article>
</div>
</section>
</main>
<aside class="stack">
<section class="mini-panel stack">
<div class="section-head">
<div>
<p class="eyebrow">Upcoming</p>
<h2>Upcoming Events</h2>
</div>
</div>
<div class="upcoming-list">
<article class="upcoming-item">
<time>2060-03-18</time>
<h3>Tianhe Cargo Pod 7 Undock</h3>
<p class="subtle">Lingxiao Station</p>
</article>
<article class="upcoming-item">
<time>2060-03-29</time>
<h3>XH-02 Taibai Mars Arrival</h3>
<p class="subtle">Mars System</p>
</article>
<article class="upcoming-item">
<time>2060-03-30</time>
<h3>Expedition 16 Crew Arrival</h3>
<p class="subtle">Lingxiao Station</p>
</article>
<article class="upcoming-item">
<time>2060-04-02</time>
<h3>Expedition 15 Crew Departure</h3>
<p class="subtle">Lingxiao Station</p>
</article>
</div>
</section>
<section class="mini-panel stack">
<div class="section-head">
<div>
<p class="eyebrow">Integrity</p>
<h2>Warnings</h2>
</div>
</div>
<div class="warning-list subtle">
<article class="warning-item">
<strong>No conflicts in current preview</strong>
This is where overlapping State Intervals or missing end markers should appear later.
</article>
<article class="warning-item">
<strong>Planned event visibility</strong>
Status Board intentionally keeps future-planned events visible beside currently active states.
</article>
</div>
</section>
</aside>
</div>
</div>
<script src="./script.js"></script>
</body>
</html>
File diff suppressed because it is too large Load Diff
+281
View File
@@ -0,0 +1,281 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Timeline View Prototype</title>
<link rel="stylesheet" href="./styles.css">
</head>
<body data-page="timeline">
<div class="site-shell">
<header class="topbar">
<a class="brand" href="./index.html">
<span class="brand-kicker">Mission Ops Prototype</span>
<span class="brand-name">Timeline View</span>
</a>
<nav class="topbar-nav">
<a href="./index.html">Overview</a>
<a href="./status-board.html">Status Board</a>
<a href="./location-board.html">Location Board</a>
<a href="./asset-log.html">Asset Log</a>
<a class="active" href="./timeline-view.html">Timeline View</a>
</nav>
</header>
<section class="hero">
<div class="hero-copy">
<p class="eyebrow">Multi-Asset Range View</p>
<h1>把多个资产在同一时间范围内的行为画到一张真正可读的时间线里</h1>
<p>这里是甘特式原型。State Interval 用长条表示,Event Point 用节点表示,当前生效中的状态段会高亮。</p>
</div>
<div class="hero-side">
<section class="time-card">
<span class="label">Simulation Time</span>
<p class="time-value" data-sim-time-display>2060-03-12 09:00</p>
<p class="hero-note">Current active state segment is highlighted relative to 2060-03-12 09:00.</p>
</section>
<div class="metric-grid">
<article class="metric-card">
<span class="metric-label">Selected Assets</span>
<strong class="metric-value" data-selected-count>3</strong>
</article>
<article class="metric-card">
<span class="metric-label">State Segments</span>
<strong class="metric-value">7</strong>
</article>
<article class="metric-card">
<span class="metric-label">Event Points</span>
<strong class="metric-value">9</strong>
</article>
</div>
</div>
</section>
<section class="panel toolbar">
<div class="toolbar-row">
<label class="clock-field">
<span class="eyebrow">Simulation Time</span>
<input type="datetime-local" data-sim-time-input>
</label>
<button class="ghost-button" type="button" data-time-shift="-30">- 30d</button>
<button class="ghost-button" type="button" data-time-shift="-7">- 7d</button>
<button class="ghost-button" type="button" data-time-shift="7">+ 7d</button>
<button class="ghost-button" type="button" data-time-shift="30">+ 30d</button>
</div>
<div class="toolbar-grid">
<label>
<span class="eyebrow">Start</span>
<input type="text" value="2059-08-01 00:00">
</label>
<label>
<span class="eyebrow">End</span>
<input type="text" value="2060-03-31 23:59">
</label>
<label>
<span class="eyebrow">Records</span>
<select>
<option>Completed + Planned</option>
</select>
</label>
<span class="button">Apply</span>
<span class="ghost-button">Export</span>
</div>
<div class="legend-row">
<span class="legend-item"><span class="legend-swatch transit"></span>Transit</span>
<span class="legend-item"><span class="legend-swatch ops"></span>Continuous Ops</span>
<span class="legend-item"><span class="legend-swatch hold"></span>Hold / Port / Orbit</span>
<span class="legend-item"><span class="legend-swatch current"></span>Current State Highlight</span>
</div>
<div class="pill-row">
<span class="pill" data-selected-assets>Selected: XH-02 Taibai, Lingxiao Station, Mars One Campus Alpha</span>
</div>
</section>
<section class="panel stack" style="margin-top: 16px;">
<div class="section-head">
<div>
<p class="eyebrow">Asset Selection</p>
<h2>用搜索、筛选和已选集合来组织 Timeline 对象</h2>
<p>如果以后 Asset 很多,就不能把所有对象直接平铺出来。所以这里改成一个更接近“选择工作区”的原型。</p>
</div>
</div>
<div class="selector-controls-grid">
<label>
<span class="eyebrow">Search Asset</span>
<input type="search" placeholder="Search by asset name" data-asset-search>
</label>
<label>
<span class="eyebrow">Asset Type</span>
<select data-asset-type-filter>
<option value="all">All Types</option>
<option value="Exploration Mothership">Exploration Mothership</option>
<option value="Station">Station</option>
<option value="Surface Outpost">Surface Outpost</option>
<option value="Shuttle">Shuttle</option>
<option value="Cargo Shuttle">Cargo Shuttle</option>
</select>
</label>
<label>
<span class="eyebrow">System / Region</span>
<select data-asset-region-filter>
<option value="all">All Regions</option>
<option value="Earth System">Earth System</option>
<option value="Mars System">Mars System</option>
</select>
</label>
</div>
<div class="preset-chip-row">
<button class="ghost-button selector-preset" type="button" data-asset-preset="active">Active Missions</button>
<button class="ghost-button selector-preset" type="button" data-asset-preset="earth">Earth System</button>
<button class="ghost-button selector-preset" type="button" data-asset-preset="mars">Mars System</button>
<button class="ghost-button selector-preset" type="button" data-asset-preset="crew">Crew Logistics</button>
</div>
<div class="selector-layout">
<section class="mini-panel stack">
<div class="section-head compact-head">
<div>
<p class="eyebrow">Search Results</p>
<h2>Matching Assets</h2>
</div>
</div>
<div class="selector-results-list" data-asset-results>
<article class="asset-result-card">
<div class="card-top">
<div>
<h3>Loading asset search</h3>
<p class="asset-type">Preparing searchable asset catalog</p>
</div>
</div>
</article>
</div>
</section>
<section class="mini-panel stack">
<div class="section-head compact-head">
<div>
<p class="eyebrow">Selected Assets</p>
<h2>Timeline Focus Set</h2>
</div>
</div>
<div class="selected-assets-list" data-selected-asset-list>
<article class="selected-asset-card">
<strong>Loading selected assets</strong>
</article>
</div>
</section>
</div>
</section>
<section class="panel timeline-panel" style="margin-top: 16px;">
<div class="timeline-toolbar">
<div>
<p class="eyebrow">Timeline Canvas</p>
<h2>Mission Flow Across Assets</h2>
<p>每个 Asset 一条泳道,节点用于关键事件,条带用于持续状态。</p>
</div>
</div>
<div class="timeline-scroll">
<div class="timeline-frame">
<div class="timeline-scale">
<div></div>
<div class="timeline-scale-strip">
<span>2059-08</span>
<span>2059-09</span>
<span>2059-10</span>
<span>2059-11</span>
<span>2059-12</span>
<span>2060-01</span>
<span>2060-02</span>
<span>2060-03</span>
</div>
</div>
<div class="timeline-rows">
<div class="timeline-row" data-asset="taibai">
<article class="timeline-row-label">
<h3>XH-02 Taibai</h3>
<p>Exploration Mothership</p>
</article>
<div class="timeline-lane">
<div class="timeline-segment transit" style="grid-column: 6 / span 22;">Outbound Transfer</div>
<div class="timeline-segment hold" style="grid-column: 29 / span 6;">Mars Orbit Standby</div>
<div class="timeline-segment transit" style="grid-column: 38 / span 21;">Return Transfer</div>
<div class="timeline-segment hold" style="grid-column: 61 / span 8;">Star Port Stay</div>
<div class="timeline-segment transit current" style="grid-column: 73 / span 22;">Support Transit to Mars</div>
<div class="timeline-event" style="grid-column: 2;"><span>Commissioned</span></div>
<div class="timeline-event" style="grid-column: 28;"><span>Mars Arrival</span></div>
<div class="timeline-event" style="grid-column: 59;"><span>Earth Arrival</span></div>
<div class="timeline-event" style="grid-column: 95;"><span>Next Mars Arrival</span></div>
</div>
</div>
<div class="timeline-row" data-asset="lingxiao">
<article class="timeline-row-label">
<h3>Lingxiao Station</h3>
<p>Station</p>
</article>
<div class="timeline-lane">
<div class="timeline-segment ops current" style="grid-column: 1 / span 96;">Continuous Station Operations</div>
<div class="timeline-event" style="grid-column: 10;"><span>Crew 9 Arrival</span></div>
<div class="timeline-event" style="grid-column: 62;"><span>Expedition 15 Arrival</span></div>
<div class="timeline-event" style="grid-column: 88;"><span>Expedition 16 Arrival</span></div>
</div>
</div>
<div class="timeline-row" data-asset="campus-alpha">
<article class="timeline-row-label">
<h3>Mars One Campus Alpha</h3>
<p>Surface Outpost</p>
</article>
<div class="timeline-lane">
<div class="timeline-segment ops" style="grid-column: 34 / span 62;">Surface Expansion and ISRU Staging</div>
<div class="timeline-event" style="grid-column: 37;"><span>First Crew Descent</span></div>
<div class="timeline-event" style="grid-column: 95;"><span>Taibai Supply Window</span></div>
</div>
</div>
<div class="timeline-row is-hidden" data-asset="amalthea-4">
<article class="timeline-row-label">
<h3>Amalthea Crew Vehicle 4</h3>
<p>Shuttle</p>
</article>
<div class="timeline-lane">
<div class="timeline-segment transit" style="grid-column: 84 / span 8;">Crew Rotation Transfer</div>
<div class="timeline-event" style="grid-column: 92;"><span>Lingxiao Docking</span></div>
</div>
</div>
<div class="timeline-row is-hidden" data-asset="zhuque">
<article class="timeline-row-label">
<h3>XH-03 Zhuque</h3>
<p>Shuttle</p>
</article>
<div class="timeline-lane">
<div class="timeline-segment hold" style="grid-column: 64 / span 22;">Port Refit</div>
<div class="timeline-segment transit" style="grid-column: 88 / span 8;">Relay Deployment Window</div>
<div class="timeline-event" style="grid-column: 86;"><span>Rollout Review</span></div>
</div>
</div>
<div class="timeline-row is-hidden" data-asset="tianhe-7">
<article class="timeline-row-label">
<h3>Tianhe Cargo Pod 7</h3>
<p>Cargo Shuttle</p>
</article>
<div class="timeline-lane">
<div class="timeline-segment hold" style="grid-column: 74 / span 6;">Station Docked Stay</div>
<div class="timeline-event" style="grid-column: 80;"><span>Undock</span></div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
<script src="./script.js"></script>
</body>
</html>