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
+37 -120
View File
@@ -1,127 +1,44 @@
{% extends "base.html" %}
{% block content %}
<section class="catalog-header panel catalog-header-tight catalog-page-header">
<div class="catalog-header-copy">
<p class="eyebrow">Vehicle</p>
<h1>载具成本目录</h1>
<p class="hero-text">支持分页、来源筛选、成本排序与编辑。</p>
</div>
<div class="catalog-header-side">
<div class="catalog-metrics">
<div class="summary-card compact">
<span class="summary-label">总记录</span>
<strong>{{ total_catalog_count }}</strong>
</div>
<div class="summary-card compact">
<span class="summary-label">当前结果</span>
<strong>{{ total_count }}</strong>
</div>
<div class="summary-card compact">
<span class="summary-label">当前页</span>
<strong>{{ page }} / {{ total_pages }}</strong>
</div>
</div>
</div>
</section>
<div class="ph-row fi">
<div class="ph">
<div class="ey">Reference Data</div>
<h1>载具成本</h1>
</div>
<a class="b ac" href="{{ url_for('web.vehicle_new') }}">+ New</a>
</div>
<section class="panel catalog-actions-panel top-gap">
<div>
<p class="eyebrow">Actions</p>
<h2>新增内容</h2>
</div>
<a class="primary-button" href="{{ url_for('web.vehicle_new') }}">新增载具成本</a>
</section>
<div class="fb fi">
<form method="get" action="{{ url_for('web.vehicle_list') }}" style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="text" name="q" value="{{ search_term }}" placeholder="Search...">
<select name="source" onchange="this.form.submit()">
<option value="">All Sources</option>
{% for s in source_options %}<option value="{{ s }}" {% if s == source_filter %}selected{% endif %}>{{ s }}</option>{% endfor %}
</select>
<button class="b" type="submit">Search</button>
</form>
</div>
<section class="panel panel-wide top-gap">
<form class="search-form filter-row" method="get">
<input type="search" name="q" value="{{ search_term }}" placeholder="Vehicle / Source">
<table class="fi">
<thead><tr><th>Name</th><th>Launch Cost</th><th>Source</th><th>Action</th></tr></thead>
<tbody>
{% for row in rows %}
<tr>
<td><a href="{{ url_for('web.vehicle_edit', vehicle_id=row.id) }}">{{ row.vehicle_name }}</a></td>
<td>{{ row.launch_cost_funds or '—' }} funds</td>
<td class="mt">{{ row.source or '—' }}</td>
<td><div class="act-btn"><a href="{{ url_for('web.vehicle_edit', vehicle_id=row.id) }}">Edit</a></div></td>
</tr>
{% endfor %}
</tbody>
</table>
<select name="source">
<option value="">全部来源</option>
{% for option in source_options %}
<option value="{{ option }}" {% if option == source_filter %}selected{% endif %}>{{ option }}</option>
{% endfor %}
</select>
<select name="sort">
{% for option in sort_options %}
<option value="{{ option.value }}" {% if option.value == sort_option %}selected{% endif %}>{{ option.label }}</option>
{% endfor %}
</select>
<button class="primary-button" type="submit">应用</button>
<a class="secondary-button" href="{{ url_for('web.vehicle_list') }}">重置</a>
</form>
{% if rows %}
<div class="table-shell top-gap">
<table class="data-table compact-table">
<thead>
<tr>
<th>Vehicle</th>
<th>Launch Price</th>
<th>Source</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for row in rows %}
<tr>
<td><strong>{{ row.vehicle_name }}</strong></td>
<td>{{ row.launch_price if row.launch_price is not none else '-' }}</td>
<td>{{ row.source or '-' }}</td>
<td>
<div class="table-actions">
<a class="table-link" href="{{ url_for('web.vehicle_edit', vehicle_id=row.id) }}">编辑</a>
<form
method="post"
action="{{ url_for('web.vehicle_delete', vehicle_id=row.id) }}"
class="inline-form"
onsubmit="return confirm('确认删除 {{ row.vehicle_name }} ?')"
>
<button class="danger-link" type="submit">删除</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if total_pages > 1 %}
<nav class="pagination-bar" aria-label="Vehicle pagination">
{% if has_prev %}
<a class="pagination-link" href="{{ prev_url }}">上一页</a>
{% else %}
<span class="pagination-link disabled">上一页</span>
{% endif %}
<div class="pagination-pages">
{% for item in page_links %}
{% if item.ellipsis %}
<span class="pagination-ellipsis">...</span>
{% elif item.current %}
<span class="pagination-link current">{{ item.number }}</span>
{% else %}
<a class="pagination-link" href="{{ item.url }}">{{ item.number }}</a>
{% endif %}
{% endfor %}
</div>
{% if has_next %}
<a class="pagination-link" href="{{ next_url }}">下一页</a>
{% else %}
<span class="pagination-link disabled">下一页</span>
{% endif %}
</nav>
{% endif %}
{% else %}
<section class="empty-state compact-empty">
<h2>没有命中结果</h2>
<p>调整筛选条件或重置后重试。</p>
</section>
{% endif %}
</section>
{% if total_pages > 1 %}
<div class="pg-bar fi">
{% if has_prev %}<a href="{{ prev_url }}">← Prev</a>{% endif %}
{% for link in page_links %}{% if link.is_current %}<span class="cur">{{ link.page }}</span>{% elif link.is_ellipsis %}<span>...</span>{% else %}<a href="{{ link.url }}">{{ link.page }}</a>{% endif %}{% endfor %}
{% if has_next %}<a href="{{ next_url }}">Next →</a>{% endif %}
</div>
{% endif %}
{% endblock %}