feat: timeline refactor, docking UI, location/state dropdowns, sim_time cleanup

Templates:
- mission_timeline_preview: client-side rendering with lane stacking, wheel zoom,
  drag pan, percentage-based layout, event dedup, scale labels
- asset_detail: dock/undock modals with time selection, future event warning,
  Mission Label column replacing Location/State
- asset_entry_form: all_locations expanded to 72 items (draft board hierarchy),
  all_states dynamic from DB, event point dropdown uses same list
- Remove sim_time from all template url_for links
- base.html: remove Today button

JS:
- app.js: sim_time controls use POST /api/v1/sim-time API instead of URL param

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 15:29:10 +08:00
co-authored by Claude Opus 4.7
parent cc4c460840
commit 2fef743e83
15 changed files with 1791 additions and 1966 deletions
+38 -73
View File
@@ -1,80 +1,45 @@
{% extends "base.html" %}
{% block content %}
<section class="hero-panel">
<div class="hero-copy">
<p class="eyebrow">项目起步版本</p>
<h1>先把数据模型和导入边界定死,再做 CRUD。</h1>
<p class="hero-text">
这个骨架现在已经提供 Flask 应用、规范化数据库模型、Docker 运行基线、Excel 巡检脚本,
以及第一块可直接使用的 Fuel Chart 页面工具。
</p>
</div>
<div class="hero-callout">
<p class="callout-label">关键风险</p>
<p class="callout-number">{{ duplicate_conflicts }}</p>
<p class="callout-text">组 Engine + Config Name 在原始工作簿中存在重复,不能作为数据库硬唯一键。</p>
<p class="callout-subtext">即便补上 Fuel Type,仍然还有 {{ duplicate_conflicts_with_fuel }} 组冲突。</p>
</div>
</section>
<div class="ph fi">
<div class="ey">Mission Operations</div>
<h1>任务总览</h1>
<p>Simulation: <strong>{{ simulation_time_display }}</strong> · All metrics derived from asset logs</p>
</div>
<section class="metric-grid">
{% for metric in metrics %}
<article class="metric-card">
<span class="metric-value">{{ metric.value }}</span>
<span class="metric-label">{{ metric.label }}</span>
</article>
<div class="mg fi">
<div class="mc ac"><div class="l">Tracked Assets</div><div class="v">{{ total_assets }}</div><div class="s">{{ mothership_count }} Motherships · {{ station_count }} Stations</div></div>
<div class="mc gn"><div class="l">Active Missions</div><div class="v">{{ active_missions }}</div></div>
<div class="mc am"><div class="l">Upcoming (30d)</div><div class="v">{{ upcoming_count }}</div><div class="s">{{ upcoming_summary }}</div></div>
</div>
<div class="stl fi">Status Board</div>
<div class="sgr fi">
{% for group in status_groups %}
<div class="sgi">
<div class="sgh">
<span class="sdt {{ group.css_class }}"></span>{{ group.label }}
<span class="scc">{{ group.assets|length }}</span>
</div>
{% for asset in group.assets %}
<div class="srw">
<a class="an" href="{{ url_for('web.asset_detail', asset_id=asset.id
) }}">{{ asset.name }}</a>
<span class="am">{{ asset.mission }}</span>
<span class="al">{{ asset.location }}</span>
</div>
{% endfor %}
</section>
</div>
{% endfor %}
</div>
<section class="content-grid">
<article class="panel panel-wide">
<div class="panel-heading">
<p class="eyebrow">建模决策</p>
<h2>当前推荐的数据库拆分</h2>
</div>
<div class="decision-grid">
<div>
<h3>engine_families</h3>
<p>只放经 Excel 校验后稳定的字段:Engine、Name、Cycle、Size、entry Cost。</p>
</div>
<div>
<h3>engine_variants</h3>
<p>放所有配置级字段:Fuel Type、Work Env、ISP、推力、TVC、价格、点火次数、Tech、备注。</p>
</div>
<div>
<h3>代理主键</h3>
<p>所有核心表使用 UUID 主键,导入时保留原始自然键,并单独输出重复告警。</p>
</div>
<div>
<h3>Fuel Chart</h3>
<p>不先入库,先作为常量配置和 API 小工具,后面再看是否要抽成字典表。</p>
</div>
</div>
</article>
<article class="panel">
<div class="panel-heading">
<p class="eyebrow">模块状态</p>
<h2>第一批交付</h2>
</div>
<div class="module-list">
{% for module in modules %}
<div class="module-card {{ module.status }}">
<div class="module-meta">
<span class="module-status">{{ module.status }}</span>
<h3>{{ module.title }}</h3>
</div>
<p>{{ module.description }}</p>
{% if module.endpoint %}
<a class="module-link" href="{{ url_for(module.endpoint) }}">立即打开</a>
{% else %}
<span class="module-link muted">下一阶段实现</span>
{% endif %}
</div>
{% endfor %}
</div>
</article>
</section>
<div class="stl fi">Upcoming Events</div>
<div class="evl fi">
{% for ev in upcoming_events %}
<div class="evr">
<span class="ed">{{ ev.date }}</span>{{ ev.title }}
{% if ev.within_30d %}<span class="eb">30d</span>{% endif %}
</div>
{% endfor %}
</div>
{% endblock %}