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
+62 -108
View File
@@ -1,114 +1,68 @@
{% extends "base.html" %}
{% from "_datetime_picker_field.html" import datetime_picker_field %}
{% block content %}
{% include "_mission_ops_tabs.html" %}
{% include "_mission_preview_header.html" %}
<div class="ph-row fi">
<div class="ph">
<div class="ey">Mission Operations</div>
<h1>任务资产</h1>
</div>
<a class="b ac" href="{{ url_for('web.asset_new'
) }}">+ New Asset</a>
</div>
<section class="panel catalog-actions-panel top-gap">
<div>
<p class="eyebrow">Actions</p>
<h2>新增内容</h2>
</div>
<div class="button-row">
<a class="primary-button" href="{{ url_for('web.asset_new', sim_time=simulation_time_input) }}">新增任务资产</a>
<form method="post" action="{{ url_for('web.asset_import_log_book') }}" class="inline-form">
<input type="hidden" name="sim_time" value="{{ simulation_time_input }}">
<button class="secondary-button" type="submit">导入 Log Book</button>
</form>
</div>
</section>
<div class="fb fi">
<form method="get" action="{{ url_for('web.asset_list') }}" style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="hidden" name="sim_time" value="{{ simulation_time_input }}">
<input type="text" name="q" value="{{ search_term }}" placeholder="Search assets...">
<select name="asset_type" onchange="this.form.submit()">
<option value="">All Types</option>
{% for t in asset_type_options %}<option value="{{ t }}" {% if t == asset_type_filter %}selected{% endif %}>{{ t }}</option>{% endfor %}
</select>
<select name="sort" onchange="this.form.submit()">
{% for s in sort_options %}<option value="{{ s.value }}" {% if s.value == sort_option %}selected{% endif %}>{{ s.label }}</option>{% endfor %}
</select>
<button class="b" type="submit">Filter</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="Asset / Type / Program / Region">
<table class="fi">
<thead><tr>
<th>Name</th>
<th>Type</th>
<th>Location</th>
<th>Current Event</th>
<th>Entries</th>
<th>Action</th>
</tr></thead>
<tbody>
{% for row in rows %}
<tr>
<td><a href="{{ url_for('web.asset_detail', asset_id=row.asset.id
) }}">{{ row.asset.name }}</a></td>
<td>{{ row.asset.asset_type or '—' }}</td>
<td class="mt">{{ row.current_location or '—' }}</td>
<td>{{ row.current_event or '—' }}</td>
<td>{{ row.entry_count }}</td>
<td><div class="act-btn">
<a href="{{ url_for('web.asset_edit', asset_id=row.asset.id
) }}">Edit</a>
<a href="{{ url_for('web.asset_entry_new', asset_id=row.asset.id
) }}">+Entry</a>
</div></td>
</tr>
{% endfor %}
</tbody>
</table>
<select name="asset_type">
<option value="">全部类型</option>
{% for option in asset_type_options %}
<option value="{{ option }}" {% if option == asset_type_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.asset_list') }}">重置</a>
</form>
{% if rows %}
<div class="table-shell top-gap">
<table class="data-table compact-table">
<thead>
<tr>
<th>Asset</th>
<th>Type</th>
<th>Program</th>
<th>Current Event</th>
<th>Next Event</th>
<th>Entries</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for row in rows %}
<tr>
<td>
<strong><a class="table-link" href="{{ url_for('web.asset_detail', asset_id=row.asset.id, sim_time=simulation_time_input) }}">{{ row.asset.name }}</a></strong>
</td>
<td>{{ row.asset.asset_type }}</td>
<td>{{ row.asset.program or '-' }}</td>
<td>{{ row.current_event }}</td>
<td>{{ row.next_event }}</td>
<td>{{ row.entry_count }}</td>
<td>
<div class="table-actions">
<a class="table-link" href="{{ url_for('web.asset_edit', asset_id=row.asset.id, sim_time=simulation_time_input) }}">编辑</a>
<a class="table-link" href="{{ url_for('web.asset_entry_new', asset_id=row.asset.id, sim_time=simulation_time_input) }}">加条目</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if total_pages > 1 %}
<nav class="pagination-bar" aria-label="Asset 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>
{% endblock %}
{% 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 %}