Download app/templates/admin_activity_edit.html from cacode/cam: direct link, hf CLI and curl.
- Browser
- Download file 11.2 kB
-
https://huggingface.co/spaces/cacode/cam/resolve/main/app/templates/admin_activity_edit.html
- Command line
-
hf download hf://spaces/cacode/cam/app/templates/admin_activity_edit.html
-
curl -L -o admin_activity_edit.html https://huggingface.co/spaces/cacode/cam/resolve/main/app/templates/admin_activity_edit.html
11.2 kB
| {% extends 'base.html' %} | |
| {% block content %} | |
| <section class="hero-card admin-hero"> | |
| <div> | |
| <a class="ghost-link" href="/admin/activities">返回活动管理</a> | |
| <p class="eyebrow">Edit Activity</p> | |
| <h2>{{ activity.title }}</h2> | |
| <p class="lead">在这里可以修改活动时间、排行榜可见性、任务内容和图床链接,并删除不再需要的任务。管理员任务图片仅保留链接方式,用户提交图片仍保存在 Docker 本地目录。</p> | |
| </div> | |
| <div class="hero-badges"> | |
| <span class="pill">创建人 {{ activity.created_by.display_name }}</span> | |
| <span class="pill">{{ activity.tasks|length }} 个任务</span> | |
| <span class="pill">开始 {{ activity.start_at|datetime_local }}</span> | |
| <span class="pill">{{ '用户端可见' if activity.is_visible else '用户端隐藏' }}</span> | |
| </div> | |
| <div class="card-footer"> | |
| <button class="btn btn-danger" type="submit" form="delete-activity-form" onclick="return confirm('确定删除这个活动吗?相关任务、审核记录和本地提交图片都会一并删除。');">删除整个活动</button> | |
| </div> | |
| </section> | |
| <form method="post" action="/admin/activities/{{ activity.id }}/edit" class="form-stack"> | |
| <section class="glass-card form-panel wide-panel"> | |
| <div class="section-head"> | |
| <div> | |
| <p class="eyebrow">Activity Settings</p> | |
| <h3>活动信息</h3> | |
| </div> | |
| </div> | |
| <div class="form-grid cols-2"> | |
| <label> | |
| <span>活动标题</span> | |
| <input type="text" name="title" value="{{ activity.title }}" required /> | |
| </label> | |
| <label> | |
| <span>线索发布时间间隔(分钟)</span> | |
| <input type="number" name="clue_interval_minutes" min="0" value="{{ activity.clue_interval_minutes if activity.clue_interval_minutes is not none else '' }}" placeholder="留空表示与活动开始同步" /> | |
| </label> | |
| <label> | |
| <span>开始时间</span> | |
| <input type="datetime-local" name="start_at" value="{{ activity.start_at.strftime('%Y-%m-%dT%H:%M') }}" required /> | |
| </label> | |
| <label> | |
| <span>截止时间</span> | |
| <input type="datetime-local" name="deadline_at" value="{{ activity.deadline_at.strftime('%Y-%m-%dT%H:%M') }}" required /> | |
| </label> | |
| </div> | |
| <label> | |
| <span>活动说明</span> | |
| <textarea name="description" rows="3">{{ activity.description or '' }}</textarea> | |
| </label> | |
| <label class="checkbox-row"> | |
| <input type="checkbox" name="is_visible" {% if activity.is_visible %}checked{% endif %} /> | |
| <span>允许用户查看该活动</span> | |
| </label> | |
| <label class="checkbox-row"> | |
| <input type="checkbox" name="leaderboard_visible" {% if activity.leaderboard_visible %}checked{% endif %} /> | |
| <span>允许用户查看实时排行榜</span> | |
| </label> | |
| </section> | |
| <section class="glass-card leaderboard-card wide-panel"> | |
| <div class="section-head compact-head"> | |
| <div> | |
| <p class="eyebrow">Live Ranking</p> | |
| <h3>活动实时排行榜</h3> | |
| <p class="mini-note">管理员始终可见,用于现场统筹和审核判断;用户是否可见由上方开关控制。</p> | |
| </div> | |
| <div class="chip-row"> | |
| <span class="chip">活动{{ '可见' if activity.is_visible else '隐藏' }}</span> | |
| <span class="chip">排行榜{{ '可见' if activity.leaderboard_visible else '隐藏' }}</span> | |
| </div> | |
| </div> | |
| <div class="rank-table-wrap"> | |
| <table class="rank-table"> | |
| <thead> | |
| <tr> | |
| <th>排名</th> | |
| <th>小组</th> | |
| <th>完成</th> | |
| <th>人数</th> | |
| <th>总耗时</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {% for row in leaderboard %} | |
| <tr> | |
| <td>#{{ loop.index }}</td> | |
| <td>{{ row.group_name }}</td> | |
| <td>{{ row.completed_count }}</td> | |
| <td>{{ row.member_count }}</td> | |
| <td>{{ row.total_elapsed|duration_human }}</td> | |
| </tr> | |
| {% else %} | |
| <tr> | |
| <td colspan="5">当前还没有通过审核的小组打卡记录。</td> | |
| </tr> | |
| {% endfor %} | |
| </tbody> | |
| </table> | |
| </div> | |
| </section> | |
| <section class="glass-card form-panel wide-panel"> | |
| <div class="section-head"> | |
| <div> | |
| <p class="eyebrow">Existing Tasks</p> | |
| <h3>已有任务</h3> | |
| </div> | |
| </div> | |
| <div class="task-editor-list"> | |
| {% for task in activity.tasks %} | |
| <article class="task-builder-card task-editor-card"> | |
| <div class="builder-title-row"> | |
| <strong>任务 {{ loop.index }}</strong> | |
| <button | |
| class="btn btn-danger small-btn" | |
| type="submit" | |
| form="delete-task-{{ task.id }}" | |
| onclick="return confirm('确定删除这个任务吗?相关提交记录也会一并删除。');" | |
| >删除任务</button> | |
| </div> | |
| <input type="hidden" name="existing_task_id" value="{{ task.id }}" /> | |
| <div class="form-grid cols-2"> | |
| <label> | |
| <span>任务标题</span> | |
| <input type="text" name="existing_task_title" value="{{ task.title }}" required /> | |
| </label> | |
| <label> | |
| <span>主图图床链接</span> | |
| <input type="url" name="existing_task_image_url" value="{{ task.image_url or '' }}" placeholder="https://..." required /> | |
| </label> | |
| <label class="full-span"> | |
| <span>任务描述</span> | |
| <textarea name="existing_task_description" rows="2">{{ task.description or '' }}</textarea> | |
| </label> | |
| <label> | |
| <span>线索图图床链接</span> | |
| <input type="url" name="existing_task_clue_image_url" value="{{ task.clue_image_url or '' }}" placeholder="https://..." /> | |
| </label> | |
| <label class="checkbox-row align-end-checkbox"> | |
| <input type="checkbox" name="existing_task_remove_clue" value="{{ task.id }}" /> | |
| <span>移除当前线索图</span> | |
| </label> | |
| </div> | |
| <div class="editor-preview-grid"> | |
| <div> | |
| <span class="mini-note">当前主图</span> | |
| <img class="editor-thumb" src="{{ task.image_url if task.image_url else '/media/tasks/' ~ task.id ~ '/image' }}" alt="{{ task.title }} 主图" /> | |
| </div> | |
| <div> | |
| <span class="mini-note">当前线索图</span> | |
| {% if task.clue_image_url or task.clue_image_filename or task.clue_image_path %} | |
| <img class="editor-thumb" src="{{ task.clue_image_url if task.clue_image_url else '/media/tasks/' ~ task.id ~ '/clue' }}" alt="{{ task.title }} 线索图" /> | |
| {% else %} | |
| <div class="empty-thumb">未设置线索图</div> | |
| {% endif %} | |
| </div> | |
| </div> | |
| <div class="chip-row"> | |
| <span class="chip">提交记录 {{ task.submissions|length }} 条</span> | |
| <span class="chip">当前顺序 {{ task.display_order }}</span> | |
| <span class="chip">线索发布时间 {{ task.clue_release_at|datetime_local if task.clue_release_at else '未发布' }}</span> | |
| </div> | |
| </article> | |
| {% endfor %} | |
| </div> | |
| </section> | |
| <section class="glass-card form-panel wide-panel"> | |
| <div class="section-head tight-head"> | |
| <div> | |
| <p class="eyebrow">New Tasks</p> | |
| <h3>新增任务</h3> | |
| </div> | |
| <button class="btn btn-secondary" type="button" id="add-new-task-btn">新增任务卡片</button> | |
| </div> | |
| <div class="task-builder" id="new-task-builder"> | |
| <article class="task-builder-card" data-new-task-template> | |
| <div class="builder-title-row"> | |
| <strong>新增任务 1</strong> | |
| <button type="button" class="btn btn-ghost small-btn" data-remove-new-task>删除</button> | |
| </div> | |
| <div class="form-grid cols-2"> | |
| <label> | |
| <span>任务标题</span> | |
| <input type="text" name="new_task_title" /> | |
| </label> | |
| <label> | |
| <span>主图图床链接</span> | |
| <input type="url" name="new_task_image_url" placeholder="https://..." /> | |
| </label> | |
| <label class="full-span"> | |
| <span>任务描述</span> | |
| <textarea name="new_task_description" rows="2"></textarea> | |
| </label> | |
| <label class="full-span"> | |
| <span>线索图图床链接</span> | |
| <input type="url" name="new_task_clue_image_url" placeholder="https://..." /> | |
| </label> | |
| </div> | |
| </article> | |
| </div> | |
| <div class="card-footer"> | |
| <span class="mini-note">保存后会自动重排任务顺序,并按新的顺序刷新线索发布时间。</span> | |
| <button class="btn btn-primary" type="submit">保存活动修改</button> | |
| </div> | |
| </section> | |
| </form> | |
| <form id="delete-activity-form" method="post" action="/admin/activities/{{ activity.id }}/delete"></form> | |
| {% for task in activity.tasks %} | |
| <form id="delete-task-{{ task.id }}" method="post" action="/admin/tasks/{{ task.id }}/delete"></form> | |
| {% endfor %} | |
| <script> | |
| (() => { | |
| const builder = document.getElementById('new-task-builder'); | |
| const addBtn = document.getElementById('add-new-task-btn'); | |
| if (!builder || !addBtn) return; | |
| const renumber = () => { | |
| builder.querySelectorAll('[data-new-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-new-task]'); | |
| if (!removeBtn) return; | |
| removeBtn.addEventListener('click', () => { | |
| if (builder.querySelectorAll('[data-new-task-template]').length === 1) { | |
| card.querySelectorAll('input, textarea').forEach((field) => { | |
| field.value = ''; | |
| }); | |
| return; | |
| } | |
| card.remove(); | |
| renumber(); | |
| }); | |
| }; | |
| builder.querySelectorAll('[data-new-task-template]').forEach(attachRemove); | |
| addBtn.addEventListener('click', () => { | |
| const template = builder.querySelector('[data-new-task-template]'); | |
| const clone = template.cloneNode(true); | |
| clone.querySelectorAll('input, textarea').forEach((field) => { | |
| field.value = ''; | |
| }); | |
| attachRemove(clone); | |
| builder.appendChild(clone); | |
| renumber(); | |
| }); | |
| })(); | |
| </script> | |
| {% endblock %} | |