File size: 8,949 Bytes
ce0719e
 
 
 
 
 
 
 
 
 
 
3d6b7f2
ce0719e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4fb3744
 
 
 
ce0719e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3d6b7f2
 
ce0719e
 
 
 
 
 
3d6b7f2
 
ce0719e
 
 
 
 
 
 
 
cfce07c
 
ce0719e
 
 
26d04d9
ce0719e
 
cfce07c
ce0719e
cfce07c
36b4039
 
 
 
cfce07c
36b4039
 
 
 
 
 
 
cfce07c
36b4039
cfce07c
36b4039
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4fb3744
3d06737
cfce07c
36b4039
 
 
 
cfce07c
 
 
4fb3744
 
36b4039
 
cfce07c
26d04d9
cfce07c
3d06737
 
36b4039
 
 
cfce07c
36b4039
 
cfce07c
36b4039
 
 
ce0719e
 
cfce07c
 
36b4039
cfce07c
ce0719e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4fb3744
 
 
 
 
ce0719e
 
 
 
 
 
 
26d04d9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
{% extends 'base.html' %}

{% block content %}
  <section class="page-grid admin-page-grid admin-activity-grid">
    <article class="glass-card form-panel wide-panel">
      <div class="section-head">
        <div>
          <p class="eyebrow">Create Activity</p>
          <h3>发布活动与打卡任务</h3>
        </div>
      </div>
      <form method="post" action="/admin/activities" class="form-stack" id="activity-form">
        <div class="form-grid cols-2">
          <label>
            <span>活动标题</span>
            <input type="text" name="title" required />
          </label>
          <label>
            <span>线索发布时间间隔(分钟)</span>
            <input type="number" name="clue_interval_minutes" min="0" placeholder="留空表示与活动开始同步" />
          </label>
          <label>
            <span>开始时间</span>
            <input type="datetime-local" name="start_at" required />
          </label>
          <label>
            <span>截止时间</span>
            <input type="datetime-local" name="deadline_at" required />
          </label>
        </div>
        <label>
          <span>活动说明</span>
          <textarea name="description" rows="3" placeholder="介绍活动安排、打卡要求和注意事项"></textarea>
        </label>
        <label class="checkbox-row">
          <input type="checkbox" name="is_visible" checked />
          <span>允许用户查看该活动</span>
        </label>
        <label class="checkbox-row">
          <input type="checkbox" name="leaderboard_visible" checked />
          <span>允许用户查看实时排行榜</span>
        </label>

        <div class="section-head tight-head">
          <div>
            <p class="eyebrow">Tasks</p>
            <h3>任务卡片</h3>
          </div>
          <button class="btn btn-secondary" type="button" id="add-task-btn">新增任务卡片</button>
        </div>

        <div class="task-builder" id="task-builder">
          <article class="task-builder-card" data-task-template>
            <div class="builder-title-row">
              <strong>任务 1</strong>
              <button type="button" class="btn btn-ghost small-btn" data-remove-task>删除</button>
            </div>
            <div class="form-grid cols-2">
              <label>
                <span>任务标题</span>
                <input type="text" name="task_title" required />
              </label>
              <label>
                <span>主图图床链接</span>
                <input type="url" name="task_image_url" placeholder="https://..." required />
              </label>
              <label class="full-span">
                <span>任务描述</span>
                <textarea name="task_description" rows="2"></textarea>
              </label>
              <label class="full-span">
                <span>线索图图床链接</span>
                <input type="url" name="task_clue_image_url" placeholder="https://..." />
              </label>
            </div>
          </article>
        </div>
        <button class="btn btn-primary" type="submit">发布活动</button>
      </form>
    </article>

    <article class="glass-card table-panel activity-catalog-panel">
      <div class="section-head activity-catalog-head">
        <div>
          <p class="eyebrow">Published Activities</p>
          <h3>已发布活动</h3>
          <p class="mini-note"></p>
        </div>
      </div>
      <div class="published-activity-list">
        {% for activity in activities %}
          <article class="published-activity-card">
            <div class="published-activity-hero">
              <div class="published-activity-title-wrap">
                <p class="eyebrow">Activity {{ loop.index }}</p>
                <h3 class="published-activity-title">{{ activity.title }}</h3>
              </div>
              <div class="published-activity-badges">
                <span class="status-badge {% if activity.is_visible %}status-approved{% else %}status-rejected{% endif %}">
                  {{ '活动可见' if activity.is_visible else '活动隐藏' }}
                </span>
                <span class="status-badge {% if activity.leaderboard_visible %}status-approved{% else %}status-rejected{% endif %}">
                  {{ '排行可见' if activity.leaderboard_visible else '排行隐藏' }}
                </span>
              </div>
            </div>

            <div class="published-activity-metrics">
              <div class="activity-metric-card">
                <span>开始时间</span>
                <strong>{{ activity.start_at|datetime_local }}</strong>
              </div>
              <div class="activity-metric-card">
                <span>截止时间</span>
                <strong>{{ activity.deadline_at|datetime_local }}</strong>
              </div>
              <div class="activity-metric-card">
                <span>任务数量</span>
                <strong>{{ activity.tasks|length }} 个任务</strong>
              </div>
              <div class="activity-metric-card">
                <span>创建人</span>
                <strong>{{ activity.created_by.display_name }}</strong>
              </div>
              <div class="activity-metric-card">
                <span>线索间隔</span>
                <strong>{{ activity.clue_interval_minutes if activity.clue_interval_minutes is not none else '与活动开始同步' }}</strong>
              </div>
            </div>

            <form method="post" action="/admin/activities/{{ activity.id }}/visibility" class="published-settings-form">
              <div class="published-toggle-grid">
                <label class="published-toggle-card">
                  <div class="published-toggle-copy">
                    <strong>活动对用户可见</strong>
                    <span>关闭后,用户端不会显示,也不能直接进入此活动。</span>
                  </div>
                  <input type="checkbox" name="is_visible" {% if activity.is_visible %}checked{% endif %} />
                </label>
                <label class="published-toggle-card">
                  <div class="published-toggle-copy">
                    <strong>排行榜对用户可见</strong>
                    <span>管理员始终可见,只控制普通用户是否能看到排行榜。</span>
                  </div>
                  <input type="checkbox" name="leaderboard_visible" {% if activity.leaderboard_visible %}checked{% endif %} />
                </label>
              </div>
              <div class="published-action-row">
                <a class="btn btn-primary" href="/admin/activities/{{ activity.id }}/edit">编辑活动</a>
                <button class="btn btn-secondary" type="submit">保存显示设置</button>
              </div>
            </form>

            <form method="post" action="/admin/activities/{{ activity.id }}/delete" class="published-delete-form">
              <button class="btn btn-danger" type="submit" onclick="return confirm('确定删除这个活动吗?相关任务、审核记录和本地提交图片都会一并删除。');">删除活动</button>
            </form>
          </article>
        {% else %}
          <article class="empty-state">
            <h3>还没有活动</h3>
            <p>先在上方发布一个活动,这里会自动整理成清晰的管理卡片。</p>
          </article>
        {% endfor %}
      </div>
    </article>
  </section>

  <script>
    (() => {
      const builder = document.getElementById('task-builder');
      const addBtn = document.getElementById('add-task-btn');
      if (!builder || !addBtn) return;

      const renumber = () => {
        builder.querySelectorAll('[data-task-template]').forEach((card, index) => {
          const title = card.querySelector('.builder-title-row strong');
          if (title) title.textContent = `任务 ${index + 1}`;
        });
      };

      const attachRemove = (card) => {
        const removeBtn = card.querySelector('[data-remove-task]');
        if (!removeBtn) return;
        removeBtn.addEventListener('click', () => {
          if (builder.querySelectorAll('[data-task-template]').length === 1) return;
          card.remove();
          renumber();
        });
      };

      builder.querySelectorAll('[data-task-template]').forEach(attachRemove);

      addBtn.addEventListener('click', () => {
        const template = builder.querySelector('[data-task-template]');
        const clone = template.cloneNode(true);
        clone.querySelectorAll('input, textarea').forEach((field) => {
          if (field.type === 'checkbox') {
            field.checked = false;
          } else {
            field.value = '';
          }
        });
        attachRemove(clone);
        builder.appendChild(clone);
        renumber();
      });
    })();
  </script>
{% endblock %}