aa8af6c2e0
Flask + SQLite + Bootstrap5 暗色主题 - 仪表盘: 在线/离线统计 + 最近代理 + 操作日志 - 代理 CRUD: 增删改 + 启用/禁用 + 单/批量健康检测(SOCKS5) - 分组管理: 创建/删除/筛选 - 操作日志: 分页查询 - RESTful API + Web 界面 - 管理员登录认证 (SM_ADMIN_PASSWORD 环境变量)
78 lines
3.2 KiB
HTML
78 lines
3.2 KiB
HTML
{% extends "base.html" %}
|
||
{% block title %}分组管理 · SOCK Manager{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||
<h4 class="mb-0"><i class="bi bi-collection"></i> 分组管理</h4>
|
||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addGroupModal">
|
||
<i class="bi bi-plus-lg"></i> 添加分组</button>
|
||
</div>
|
||
|
||
<div class="row g-3">
|
||
{% for g in groups %}
|
||
<div class="col-md-4 col-lg-3">
|
||
<div class="card" style="border-left:4px solid {{ g.color }}">
|
||
<div class="card-body">
|
||
<h6 style="color:{{ g.color }}">{{ g.name }}</h6>
|
||
<p class="text-muted small mb-1">{{ g.description or '无描述' }}</p>
|
||
<div class="d-flex justify-content-between align-items-center">
|
||
<span class="badge bg-secondary">{{ g.proxies|length }} 个代理</span>
|
||
<button class="btn btn-outline-danger btn-sm" onclick="deleteGroup({{ g.id }}, '{{ g.name }}')">
|
||
<i class="bi bi-trash"></i></button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
|
||
{% if not groups %}
|
||
<div class="text-center py-5 text-muted">暂无分组</div>
|
||
{% endif %}
|
||
|
||
<!-- Add Group Modal -->
|
||
<div class="modal fade" id="addGroupModal">
|
||
<div class="modal-dialog modal-sm">
|
||
<div class="modal-content" style="background:#1a1d27;border-color:#2a2d3a">
|
||
<div class="modal-header" style="border-color:#2a2d3a">
|
||
<h6 class="modal-title">添加分组</h6>
|
||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="mb-3"><label class="form-label">名称</label>
|
||
<input id="gName" class="form-control" required></div>
|
||
<div class="mb-3"><label class="form-label">颜色</label>
|
||
<input id="gColor" type="color" class="form-control form-control-color" value="#6366f1"></div>
|
||
<div class="mb-3"><label class="form-label">描述</label>
|
||
<input id="gDesc" class="form-control"></div>
|
||
</div>
|
||
<div class="modal-footer" style="border-color:#2a2d3a">
|
||
<button type="button" class="btn btn-sm btn-outline-secondary" data-bs-dismiss="modal">取消</button>
|
||
<button type="button" class="btn btn-sm btn-primary" onclick="addGroup()">添加</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{% endblock %}
|
||
|
||
{% block extra_script %}
|
||
<script>
|
||
async function addGroup() {
|
||
const name = document.getElementById('gName').value.trim();
|
||
if (!name) return;
|
||
try {
|
||
await fetch('/groups/add', {method:'POST', headers:{'Content-Type':'application/x-www-form-urlencoded'},
|
||
body:`name=${encodeURIComponent(name)}&color=${document.getElementById('gColor').value}&description=${encodeURIComponent(document.getElementById('gDesc').value)}`});
|
||
toast('分组已添加', 'success'); location.reload();
|
||
} catch(e) { toast('添加失败', 'danger'); }
|
||
}
|
||
async function deleteGroup(id, name) {
|
||
if (!confirm(`确认删除分组 "${name}"?`)) return;
|
||
const r = await fetch(`/groups/${id}`, {method:'DELETE'});
|
||
const j = await r.json();
|
||
if (j.error) { toast(j.error, 'warning'); } else { toast('分组已删除', 'success'); location.reload(); }
|
||
}
|
||
</script>
|
||
{% endblock %}
|