feat(invite): add invite code registration system
This commit is contained in:
103
app/templates/admin_invites.html
Normal file
103
app/templates/admin_invites.html
Normal file
@@ -0,0 +1,103 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}邀请码管理 - 星笺{% endblock %}
|
||||
{% block content %}
|
||||
<div class="section-header">
|
||||
<h1>邀请码管理</h1>
|
||||
</div>
|
||||
|
||||
{% if success %}
|
||||
<div class="alert alert-success">{{ success }}</div>
|
||||
{% endif %}
|
||||
{% if error %}
|
||||
<div class="alert alert-error">{{ error }}</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Add new code -->
|
||||
<div style="margin-bottom:2rem;padding:1.5rem;background:var(--card-bg);border-radius:var(--radius);border:1px solid var(--border)">
|
||||
<h3 style="margin-bottom:1rem;font-size:1rem">创建邀请码</h3>
|
||||
<form method="post" action="/admin/invites/add" style="display:flex;flex-wrap:wrap;gap:.75rem;align-items:end">
|
||||
<div style="flex:1;min-width:200px">
|
||||
<label style="display:block;font-size:.8rem;color:var(--text-muted);margin-bottom:.25rem">邀请码(留空自动生成)</label>
|
||||
<input type="text" name="code" maxlength="8" placeholder="自动生成" style="text-transform:uppercase">
|
||||
</div>
|
||||
<div style="min-width:140px">
|
||||
<label style="display:block;font-size:.8rem;color:var(--text-muted);margin-bottom:.25rem">限制类型</label>
|
||||
<select name="limit_type" id="limit_type" onchange="toggleLimitFields()">
|
||||
<option value="unlimited">无限制</option>
|
||||
<option value="uses">使用次数</option>
|
||||
<option value="expires">有效期</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="max_uses_field" style="display:none;min-width:120px">
|
||||
<label style="display:block;font-size:.8rem;color:var(--text-muted);margin-bottom:.25rem">最大使用次数</label>
|
||||
<input type="number" name="max_uses" min="1" value="1">
|
||||
</div>
|
||||
<div id="expires_field" style="display:none;min-width:180px">
|
||||
<label style="display:block;font-size:.8rem;color:var(--text-muted);margin-bottom:.25rem">过期时间</label>
|
||||
<input type="datetime-local" name="expires_at">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">创建</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Invite code list -->
|
||||
{% if codes %}
|
||||
<div class="postcard-list">
|
||||
{% for c in codes %}
|
||||
<div class="postcard-row">
|
||||
<div style="flex:1;display:flex;align-items:center;gap:1rem;flex-wrap:wrap">
|
||||
<strong style="font-family:monospace;font-size:1.1rem">{{ c.code }}</strong>
|
||||
<span style="font-size:.8rem;color:var(--text-muted)">创建者: {{ c.creator.username if c.creator else '-' }}</span>
|
||||
<span style="font-size:.8rem;color:var(--text-muted)">{{ c.created_at.strftime('%Y-%m-%d %H:%M') }}</span>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:.75rem;flex-wrap:wrap">
|
||||
{% if c.max_uses is not none %}
|
||||
<span class="badge">已用 {{ c.use_count }} / {{ c.max_uses }}</span>
|
||||
{% else %}
|
||||
<span class="badge">已用 {{ c.use_count }} / ∞</span>
|
||||
{% endif %}
|
||||
{% if c.expires_at %}
|
||||
<span class="badge">过期: {{ c.expires_at.strftime('%Y-%m-%d %H:%M') }}</span>
|
||||
{% else %}
|
||||
<span class="badge">永不过期</span>
|
||||
{% endif %}
|
||||
<form method="post" action="/admin/invites/{{ c.id }}/edit" style="display:inline-flex;align-items:center;gap:.3rem;flex-wrap:wrap" id="edit-form-{{ c.id }}">
|
||||
<select name="limit_type" style="width:auto;padding:2px 4px;font-size:.75rem" onchange="toggleEditFields({{ c.id }}, this.value)">
|
||||
<option value="unlimited"{% if c.max_uses is none and not c.expires_at %} selected{% endif %}>无限制</option>
|
||||
<option value="uses"{% if c.max_uses is not none %} selected{% endif %}>次数限制</option>
|
||||
<option value="expires"{% if c.expires_at %} selected{% endif %}>有效期</option>
|
||||
</select>
|
||||
<input type="number" name="max_uses" value="{{ c.max_uses or 1 }}" min="1" style="width:60px;padding:2px 4px;font-size:.75rem;display:{% if c.max_uses is not none %}inline{% else %}none{% endif %}">
|
||||
<input type="datetime-local" name="expires_at" value="{{ c.expires_at.strftime('%Y-%m-%dT%H:%M') if c.expires_at else '' }}" style="font-size:.75rem;display:{% if c.expires_at %}inline{% else %}none{% endif %}">
|
||||
<button type="submit" class="btn btn-sm">保存</button>
|
||||
</form>
|
||||
<form method="post" action="/admin/invites/{{ c.id }}/delete" style="display:inline" onsubmit="return confirm('确定删除该邀请码?')">
|
||||
<button type="submit" class="btn btn-danger btn-sm">删除</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">🔑</div>
|
||||
<p>暂无邀请码</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
function toggleLimitFields() {
|
||||
var t = document.getElementById('limit_type').value;
|
||||
document.getElementById('max_uses_field').style.display = t === 'uses' ? '' : 'none';
|
||||
document.getElementById('expires_field').style.display = t === 'expires' ? '' : 'none';
|
||||
}
|
||||
function toggleEditFields(id, val) {
|
||||
var form = document.getElementById('edit-form-' + id);
|
||||
var inputs = form.querySelectorAll('input[name="max_uses"], input[name="expires_at"]');
|
||||
inputs.forEach(function(inp) {
|
||||
if (inp.name === 'max_uses') inp.style.display = val === 'uses' ? 'inline' : 'none';
|
||||
if (inp.name === 'expires_at') inp.style.display = val === 'expires' ? 'inline' : 'none';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user