feat: improve mission asset operations

This commit is contained in:
2026-05-28 11:32:11 +08:00
parent 1a8af63383
commit fee9b13ada
12 changed files with 562 additions and 134 deletions
+33 -27
View File
@@ -3,19 +3,22 @@
{% block content %}
{% include "_mission_ops_tabs.html" %}
{% macro render_end_at_field(end_at_value) %}
{{ datetime_picker_field('End At', 'end_at', end_at_value, field_id='entry-end-at-input', wrapper_class='field-span-two mission-datetime-field', wrapper_id='entry-end-at-field') }}
{% set field_error_map = field_errors|default({}, true) %}
{% macro render_end_at_field(end_at_value, has_error=False) %}
{{ datetime_picker_field('End At', 'end_at', end_at_value, field_id='entry-end-at-input', wrapper_class='field-span-two mission-datetime-field', wrapper_id='entry-end-at-field', error=has_error) }}
{% endmacro %}
{% macro render_state_node_row(node, row_index) %}
<div class="state-node-row" data-state-node-row>
{% macro render_state_node_row(node, row_index, field_error_map) %}
{% set title_error = field_error_map.get('state_node_title:' ~ row_index) %}
{% set at_error = field_error_map.get('state_node_at:' ~ row_index) %}
<div class="state-node-row{% if title_error or at_error %} state-node-row-error{% endif %}" data-state-node-row{% if title_error or at_error %} data-field-error="true"{% endif %}>
<input type="hidden" name="state_node_id" value="{{ node.id if node else '' }}">
<label>
<label class="{% if title_error %}field-error-shell{% endif %}"{% if title_error %} data-field-error="true"{% endif %}>
<span class="field-label">Node Title</span>
<input type="text" name="state_node_title" value="{{ node.title if node else '' }}" placeholder="State Node Title">
<input class="{% if title_error %}field-error-input{% endif %}" type="text" name="state_node_title" value="{{ node.title if node else '' }}" placeholder="State Node Title">
</label>
{{ datetime_picker_field('Node At', 'state_node_at', node.at if node else '', field_id='state-node-at-' ~ row_index, wrapper_class='state-node-datetime') }}
{{ datetime_picker_field('Node At', 'state_node_at', node.at if node else '', field_id='state-node-at-' ~ row_index, wrapper_class='state-node-datetime', error=at_error) }}
<label class="state-node-detail-field">
<span class="field-label">Node Detail</span>
@@ -43,69 +46,70 @@
<input type="text" value="{{ asset.name }}" disabled>
</label>
<label>
<label class="{% if field_error_map.get('entry_kind') %}field-error-shell{% endif %}"{% if field_error_map.get('entry_kind') %} data-field-error="true"{% endif %}>
<span class="field-label">Entry Type</span>
<select id="entry-kind-select" name="entry_kind">
<select id="entry-kind-select" name="entry_kind" class="{% if field_error_map.get('entry_kind') %}field-error-input{% endif %}">
{% for option in kind_options %}
<option value="{{ option.value }}" {% if option.value == form_values.entry_kind %}selected{% endif %}>{{ option.label }}</option>
{% endfor %}
</select>
</label>
<label>
<label class="{% if field_error_map.get('location') %}field-error-shell{% endif %}"{% if field_error_map.get('location') %} data-field-error="true"{% endif %}>
<span class="field-label">Location</span>
<input type="text" name="location" value="{{ form_values.location }}">
<input class="{% if field_error_map.get('location') %}field-error-input{% endif %}" type="text" name="location" value="{{ form_values.location }}">
</label>
<label class="field-span-three">
<label class="field-span-three{% if field_error_map.get('title') %} field-error-shell{% endif %}"{% if field_error_map.get('title') %} data-field-error="true"{% endif %}>
<span class="field-label">Title</span>
<input type="text" name="title" value="{{ form_values.title }}" required>
<input class="{% if field_error_map.get('title') %}field-error-input{% endif %}" type="text" name="title" value="{{ form_values.title }}" required>
</label>
<label>
<label class="{% if field_error_map.get('state_label') %}field-error-shell{% endif %}"{% if field_error_map.get('state_label') %} data-field-error="true"{% endif %}>
<span class="field-label">State Label</span>
<input type="text" name="state_label" value="{{ form_values.state_label }}">
<input class="{% if field_error_map.get('state_label') %}field-error-input{% endif %}" type="text" name="state_label" value="{{ form_values.state_label }}">
<small class="field-hint">可单独设置显示标签;多个标签用分号分隔。State Interval 留空时使用 Title。</small>
</label>
<label class="field-span-two">
<label class="field-span-two{% if field_error_map.get('mission_label') %} field-error-shell{% endif %}"{% if field_error_map.get('mission_label') %} data-field-error="true"{% endif %}>
<span class="field-label">Mission Label</span>
<input type="text" name="mission_label" value="{{ form_values.mission_label }}">
<input class="{% if field_error_map.get('mission_label') %}field-error-input{% endif %}" type="text" name="mission_label" value="{{ form_values.mission_label }}">
<small class="field-hint">多个 Mission Label 可用分号分隔。</small>
</label>
{{ datetime_picker_field('At' if is_event_kind else 'Start At', 'start_at', form_values.start_at, field_id='entry-start-at-input', required=True, wrapper_class='field-span-two mission-datetime-field', wrapper_id='entry-start-at-field', label_id='entry-start-at-label') }}
{{ datetime_picker_field('At' if is_event_kind else 'Start At', 'start_at', form_values.start_at, field_id='entry-start-at-input', required=True, wrapper_class='field-span-two mission-datetime-field', wrapper_id='entry-start-at-field', label_id='entry-start-at-label', error=field_error_map.get('start_at')) }}
{% if not is_event_kind %}
{{ render_end_at_field(form_values.end_at) }}
{{ render_end_at_field(form_values.end_at, field_error_map.get('end_at')) }}
{% endif %}
<div class="field-span-three state-node-panel" id="state-node-panel" {% if is_event_kind %}hidden{% endif %}>
<div class="field-span-three state-node-panel{% if field_error_map.get('state_nodes') %} field-error-shell{% endif %}" id="state-node-panel" {% if is_event_kind %}hidden{% endif %}{% if field_error_map.get('state_nodes') %} data-field-error="true"{% endif %}>
<div class="state-node-panel-header">
<div>
<span class="field-label">State Nodes</span>
<p class="field-hint">每个 State Node 包含时间、标题和 Node Detail,并会在 Timeline 的 Event Point 泳道中显示。</p>
<p class="field-hint">State Node 时间必须落在当前 State Interval 的 Start At 和 End At 之间。</p>
</div>
<button class="secondary-button" type="button" data-state-node-add>新增 State Node</button>
</div>
<div class="state-node-list" data-state-node-list>
{% for node in form_values.state_nodes %}
{{ render_state_node_row(node, loop.index0) }}
{{ render_state_node_row(node, loop.index0, field_error_map) }}
{% endfor %}
</div>
<p class="small-muted state-node-empty" data-state-node-empty {% if form_values.state_nodes %}hidden{% endif %}>当前没有 State Node,可按需添加。</p>
</div>
<label class="field-span-three">
<label class="field-span-three{% if field_error_map.get('summary') %} field-error-shell{% endif %}"{% if field_error_map.get('summary') %} data-field-error="true"{% endif %}>
<span class="field-label">Summary</span>
<textarea name="summary" rows="4">{{ form_values.summary }}</textarea>
<textarea class="{% if field_error_map.get('summary') %}field-error-input{% endif %}" name="summary" rows="4">{{ form_values.summary }}</textarea>
</label>
<label class="field-span-three">
<label class="field-span-three{% if field_error_map.get('note') %} field-error-shell{% endif %}"{% if field_error_map.get('note') %} data-field-error="true"{% endif %}>
<span class="field-label">Note</span>
<textarea name="note" rows="4">{{ form_values.note }}</textarea>
<textarea class="{% if field_error_map.get('note') %}field-error-input{% endif %}" name="note" rows="4">{{ form_values.note }}</textarea>
</label>
</div>
@@ -127,13 +131,15 @@
{% endif %}
<template id="entry-end-at-template">
{{ render_end_at_field(form_values.end_at) }}
{{ render_end_at_field(form_values.end_at, false) }}
</template>
<template id="state-node-row-template">
{{ render_state_node_row(none, '__INDEX__') }}
{{ render_state_node_row(none, '__INDEX__', {}) }}
</template>
{% include "_validation_dialog.html" %}
<script>
(() => {
const kindField = document.getElementById("entry-kind-select");