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:
@@ -1,109 +1,50 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="hero-panel compact page-header-compact">
|
||||
<div class="hero-copy">
|
||||
<p class="eyebrow">Fuel Chart 工具化</p>
|
||||
<h1>燃料体积 / 质量快速换算</h1>
|
||||
<p class="hero-text">
|
||||
系数直接来自工作簿第二行。输入升数可换算吨位,输入吨位可反推不同燃料对应的体积。
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<div class="ph fi">
|
||||
<div class="ey">Tools</div>
|
||||
<h1>燃料换算</h1>
|
||||
</div>
|
||||
|
||||
<section class="content-grid fuel-layout">
|
||||
<article class="panel">
|
||||
<div class="panel-heading">
|
||||
<p class="eyebrow">换算输入</p>
|
||||
<h2>输入一个值</h2>
|
||||
</div>
|
||||
|
||||
<form id="converter-form" class="converter-form">
|
||||
<label class="field-label" for="mode">换算方向</label>
|
||||
<select id="mode" name="mode">
|
||||
<option value="volume">体积 L -> 质量 t</option>
|
||||
<option value="mass">质量 t -> 体积 L</option>
|
||||
</select>
|
||||
|
||||
<label class="field-label" for="value">数值</label>
|
||||
<input id="value" name="value" type="number" min="0.000001" step="any" value="75" required>
|
||||
|
||||
<button class="primary-button" type="submit">执行换算</button>
|
||||
<p id="form-error" class="form-error" hidden></p>
|
||||
</form>
|
||||
</article>
|
||||
|
||||
<article class="panel panel-wide">
|
||||
<div class="panel-heading">
|
||||
<p class="eyebrow">换算结果</p>
|
||||
<h2>输出列表</h2>
|
||||
</div>
|
||||
|
||||
<div id="result-summary" class="result-summary">输入 75 t 或 75 L 后,这里会显示结果。</div>
|
||||
<div id="result-table" class="result-table"></div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-heading">
|
||||
<p class="eyebrow">当前系数</p>
|
||||
<h2>每升对应吨位</h2>
|
||||
</div>
|
||||
<div class="factor-grid">
|
||||
{% for factor in fuel_factors %}
|
||||
<article class="factor-card">
|
||||
<h3>{{ factor.fuel }}</h3>
|
||||
<p>{{ factor.tonnes_per_liter }} t / L</p>
|
||||
</article>
|
||||
<div class="cvw fi">
|
||||
<form id="conv-form">
|
||||
<label>Value<input type="number" id="cv-val" value="1000" step="1"></label>
|
||||
<label>Mode<select id="cv-mode"><option value="L2t">Liters → Tons</option><option value="t2L">Tons → Liters</option></select></label>
|
||||
<label>Fuel<select id="cv-fuel">
|
||||
{% for f in fuel_factors %}
|
||||
<option value="{{ f.factor }}">{{ f.fuel_type }} ({{ f.factor }} kg/L)</option>
|
||||
{% endfor %}
|
||||
</select></label>
|
||||
<button type="submit" class="b ac">Convert</button>
|
||||
</form>
|
||||
<div class="res">
|
||||
<table>
|
||||
<thead><tr><th>Fuel</th><th>Density (kg/L)</th><th>Result</th></tr></thead>
|
||||
<tbody id="cv-res">
|
||||
{% for f in fuel_factors %}
|
||||
<tr><td>{{ f.fuel_type }}</td><td>{{ f.factor }}</td><td id="cv-{{ loop.index }}">—</td></tr>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const form = document.getElementById("converter-form");
|
||||
const modeField = document.getElementById("mode");
|
||||
const valueField = document.getElementById("value");
|
||||
const resultSummary = document.getElementById("result-summary");
|
||||
const resultTable = document.getElementById("result-table");
|
||||
const formError = document.getElementById("form-error");
|
||||
const endpoint = "{{ url_for('api.convert_fuel') }}";
|
||||
|
||||
async function renderResults(mode, value) {
|
||||
formError.hidden = true;
|
||||
const query = new URLSearchParams({ mode, value }).toString();
|
||||
const response = await fetch(`${endpoint}?${query}`);
|
||||
const payload = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(payload.error || "换算失败");
|
||||
document.getElementById('conv-form').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const val = parseFloat(document.getElementById('cv-val').value) || 0;
|
||||
const mode = document.getElementById('cv-mode').value;
|
||||
const factors = {{ fuel_factors|tojson }};
|
||||
factors.forEach(function(f, i) {
|
||||
const cell = document.getElementById('cv-' + (i+1));
|
||||
if (cell) {
|
||||
cell.textContent = mode === 'L2t'
|
||||
? (val * f.factor / 1000).toFixed(4) + ' t'
|
||||
: (val * 1000 / f.factor).toFixed(0) + ' L';
|
||||
cell.style.color = 'var(--accent)';
|
||||
cell.style.fontFamily = 'var(--font-data)';
|
||||
}
|
||||
|
||||
const outputUnit = payload.results[0]?.unit || "";
|
||||
resultSummary.textContent = `输入 ${payload.input_value} ${payload.input_unit},输出单位 ${outputUnit}`;
|
||||
resultTable.innerHTML = payload.results
|
||||
.map((item) => `
|
||||
<div class="result-row">
|
||||
<span>${item.fuel}</span>
|
||||
<strong>${item.value}</strong>
|
||||
<span>${item.unit}</span>
|
||||
</div>
|
||||
`)
|
||||
.join("");
|
||||
}
|
||||
|
||||
form.addEventListener("submit", async (event) => {
|
||||
event.preventDefault();
|
||||
try {
|
||||
await renderResults(modeField.value, valueField.value);
|
||||
} catch (error) {
|
||||
formError.hidden = false;
|
||||
formError.textContent = error.message;
|
||||
}
|
||||
});
|
||||
|
||||
renderResults(modeField.value, valueField.value).catch((error) => {
|
||||
formError.hidden = false;
|
||||
formError.textContent = error.message;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user