squid_ui/templates/clients.html
2025-07-29 11:05:22 +08:00

252 lines
9.6 KiB
HTML

<!-- templates/clients.html -->
{% extends "base.html" %}
{% block title %}用户管理 - Squid代理用户管理系统{% endblock %}
{% block content %}
<h1>用户管理</h1>
<div class="header-actions">
<button id="createUserBtn" class="btn-primary">+ 添加新用户</button>
</div>
<table>
<tr>
<th>用户名</th>
<th>状态</th>
<th>操作</th>
</tr>
{% for user in users %}
<tr>
<td>{{ user.name }}</td>
<td>{{ "启用" if user.is_active else "禁用" }}</td>
<td>
<div class="actions">
<label class="switch">
<input type="checkbox" {% if user.is_active %}checked{% endif %} data-username="{{ user.name }}">
<span class="slider"></span>
</label>
<button class="btn-primary edit-btn">编辑</button>
<button class="btn-success copy-btn" data-username="{{ user.name }}">
<i class="icon-copy"></i> 复制代理
</button>
<button class="btn-danger delete-btn">删除</button>
</div>
</td>
</tr>
{% endfor %}
</table>
<!-- 分页导航 -->
<div class="pagination">
{% if page > 1 %}
<a href="{{ url_for('clients', page=page-1) }}">&laquo; 上一页</a>
{% endif %}
{% for p in range(1, total_pages + 1) %}
{% if p == page %}
<span class="current">{{ p }}</span>
{% else %}
<a href="{{ url_for('clients', page=p) }}">{{ p }}</a>
{% endif %}
{% endfor %}
{% if page < total_pages %}
<a href="{{ url_for('clients', page=page+1) }}">下一页 &raquo;</a>
{% endif %}
</div>
<!-- 编辑用户模态框 -->
<div id="editModal" class="modal">
<div class="modal-content">
<div class="modal-title">编辑用户信息</div>
<div class="form-group">
<label for="editUsername">用户名</label>
<input type="text" id="editUsername" readonly>
</div>
<div class="form-group">
<label for="editPassword">新密码</label>
<input type="password" id="editPassword" placeholder="请输入新密码">
</div>
<div class="modal-actions">
<button id="closeBtn" class="btn-danger">取消</button>
<button id="saveBtn" class="btn-success">保存更改</button>
</div>
</div>
</div>
<!-- 添加用户模态框 -->
<div id="createModal" class="modal">
<div class="modal-content">
<div class="modal-title">添加新用户</div>
<div class="form-group">
<label for="createUsername">用户名</label>
<input type="text" id="createUsername" placeholder="请输入用户名">
</div>
<div class="form-group">
<label for="createPassword">密码</label>
<input type="password" id="createPassword" placeholder="请输入密码">
</div>
<div class="modal-actions">
<button id="createCloseBtn" class="btn-danger">取消</button>
<button id="createSaveBtn" class="btn-success">确认添加</button>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// 切换用户状态
document.querySelectorAll('input[type="checkbox"]').forEach(sw => {
sw.addEventListener('change', function() {
const username = this.dataset.username;
fetch(`/toggle/${username}`, { method: 'POST' });
});
});
// 删除用户
document.querySelectorAll('.delete-btn').forEach(btn => {
btn.addEventListener('click', function() {
if(confirm('确定要删除这个用户吗?此操作不可撤销!')) {
const username = this.closest('tr').querySelector('td').textContent;
fetch(`/delete/${username}`, { method: 'POST' })
.then(() => location.reload());
}
});
});
// 编辑用户
document.querySelectorAll('.edit-btn').forEach(btn => {
btn.addEventListener('click', function() {
const username = this.closest('tr').querySelector('td').textContent;
document.getElementById('editUsername').value = username;
document.getElementById('editPassword').value = '';
document.getElementById('editModal').style.display = 'flex';
});
});
// 保存编辑
document.getElementById('saveBtn').addEventListener('click', function() {
const username = document.getElementById('editUsername').value;
const password = document.getElementById('editPassword').value;
if(!password) {
alert('密码不能为空!');
return;
}
fetch('/save_user', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
}).then(response => {
if(response.ok) location.reload();
});
});
// 关闭编辑模态框
document.getElementById('closeBtn').addEventListener('click', function() {
document.getElementById('editModal').style.display = 'none';
});
// 添加用户
document.getElementById('createSaveBtn').addEventListener('click', function() {
const username = document.getElementById('createUsername').value;
const password = document.getElementById('createPassword').value;
if(!username || !password) {
alert('用户名和密码不能为空!');
return;
}
fetch('/create_user', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
}).then(response => {
if(response.ok) {
location.reload();
} else {
response.json().then(data => alert(data.error || '创建用户失败'));
}
});
});
// 打开添加用户模态框
document.getElementById('createUserBtn').addEventListener('click', function() {
document.getElementById('createModal').style.display = 'flex';
});
// 关闭添加用户模态框
document.getElementById('createCloseBtn').addEventListener('click', function() {
document.getElementById('createModal').style.display = 'none';
});
// 复制代理地址
document.querySelectorAll('.copy-btn').forEach(btn => {
btn.addEventListener('click', function() {
const username = this.dataset.username;
fetch(`/copy_proxy/${username}`, { method: 'POST' })
.then(response => response.json())
.then(data => {
// 创建一个临时的textarea元素来复制文本
const textarea = document.createElement('textarea');
textarea.value = data.proxy_url;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
alert('代理地址已复制到剪贴板: ' + data.proxy_url);
});
});
});
// 退出系统
document.getElementById('logoutBtn').addEventListener('click', function() {
if(confirm('确定要退出系统吗?')) {
fetch('/logout').then(() => {
window.location.href = '/';
});
}
});
});
</script>
{% endblock %}
{% block styles %}
<style>
/* 添加复制按钮的绿色样式 */
.btn-success {
background-color: #28a745;
color: white;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
}
.btn-success:hover {
background-color: #218838;
}
.btn-success:active {
background-color: #1e7e34;
}
.icon-copy {
display: inline-block;
width: 14px;
height: 14px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/20000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: center;
margin-right: 5px;
vertical-align: middle;
}
</style>
{% endblock %}