cs / templates /user_management.html
deeme's picture
Upload 26 files
bab9c3b verified
{% extends "base.html" %}
{% block title %}用户管理{% endblock %}
{% block content %}
<div class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold text-gray-800">用户列表</h2>
<button id="sycGatewayBtn" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded">
一键同步GPT网关
</button>
<button id="addUserBtn" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded">
添加用户
</button>
</div>
<!-- 用户列表 -->
<table class="min-w-full">
<thead>
<tr>
<th
class="px-6 py-3 border-b-2 border-gray-200 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
用户名
</th>
<th
class="px-6 py-3 border-b-2 border-gray-200 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
角色
</th>
<th
class="px-6 py-3 border-b-2 border-gray-200 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
绑定GPT账号
</th>
<th
class="px-6 py-3 border-b-2 border-gray-200 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
GPT过期时间
</th>
<th
class="px-6 py-3 border-b-2 border-gray-200 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
绑定Claude账号
</th>
<th
class="px-6 py-3 border-b-2 border-gray-200 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
Claude过期时间
</th>
<th
class="px-6 py-3 border-b-2 border-gray-200 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
操作
</th>
</tr>
</thead>
<tbody id="userTableBody">
<!-- 用户数据将动态插入这里 -->
</tbody>
</table>
<!-- 用户表单模态框 -->
<div id="userModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden">
<div class="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
<div class="mt-3">
<h3 class="text-lg font-medium text-gray-900 mb-4">用户信息</h3>
<form id="userForm">
<input type="hidden" id="userId">
<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2" for="username">
用户名
</label>
<input
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="username" type="text" required>
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2" for="password">
密码
</label>
<input
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="password" type="password">
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2" for="role">
角色
</label>
<select
class="shadow border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="role" required>
<option value="admin">管理员</option>
<option value="user">普通用户</option>
</select>
</div>
<!-- 添加GPT过期时间选择 -->
<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2" for="expirationDate">
GPT过期时间
</label>
<input
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="expirationDate" type="datetime-local">
</div>
<!-- 添加Claude过期时间选择 -->
<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2" for="expirationClaude">
Claude过期时间
</label>
<input
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="expirationClaude" type="datetime-local">
</div>
<div class="flex items-center justify-end">
<button type="button" onclick="closeModal()"
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline mr-2">
取消
</button>
<button type="submit"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
保存
</button>
</div>
</form>
</div>
</div>
</div>
<!-- 绑定账号模态框 -->
<div id="bindModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden">
<div class="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
<div class="mt-3">
<h3 class="text-lg font-medium text-gray-900 mb-4">请选择要绑定的账号</h3>
<form id="bindForm">
<input type="hidden" id="userId">
<div class="mb-4">
<select
class="shadow border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="email">
</select>
</div>
<div class="mb-4">
<select
class="shadow border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="claudeEmail">
</select>
</div>
<div class="flex items-center justify-end">
<button type="button" onclick="closeBindModal()"
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline mr-2">
取消
</button>
<button type="submit"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
保存
</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
let users = [];
const bindModal = document.getElementById('bindModal');
const modal = document.getElementById('userModal');
const userForm = document.getElementById('userForm');
const bindForm = document.getElementById('bindForm');
const addUserBtn = document.getElementById('addUserBtn');
const sycGateway = document.getElementById('sycGatewayBtn');
// 加载用户列表
function loadUsers() {
fetch('/api/users')
.then(response => response.json())
.then(data => {
users = data;
renderUsers();
})
.catch(error => console.error('Error:', error));
}
// 渲染用户列表
function renderUsers() {
const tbody = document.getElementById('userTableBody');
tbody.innerHTML = '';
users.forEach(user => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td class="px-6 py-4 whitespace-nowrap">${user.username}</td>
<td class="px-6 py-4 whitespace-nowrap">${user.role}</td>
<td class="px-6 py-4 whitespace-nowrap">${user.bind_email}</td>
<td class="px-6 py-4 whitespace-nowrap">${user.expiration_time}</td>
<td class="px-6 py-4 whitespace-nowrap">${user.bind_claude_email}</td>
<td class="px-6 py-4 whitespace-nowrap">${user.claude_expiration_time}</td>
<td class="px-6 py-4 whitespace-nowrap">
<button onclick="bindChatGPT('${user.id}')" class="text-blue-600 hover:text-blue-900 mr-4">绑定GPT</button>
<button onclick="bindClaude('${user.id}')" class="text-blue-600 hover:text-blue-900 mr-4">绑定Claude</button>
<button onclick="editUser('${user.id}')" class="text-blue-600 hover:text-blue-900 mr-4">编辑</button>
<button onclick="deleteUser('${user.id}')" class="text-red-600 hover:text-red-900">删除</button>
<button onclick="deleteBind('${user.id}')" class="text-red-600 hover:text-red-900">解绑GPT</button>
<button onclick="deleteClaudeBind('${user.id}')" class="text-red-600 hover:text-red-900">解绑Claude</button>
</td>
`;
tbody.appendChild(tr);
});
}
// 打开用户模态框
function openModal(user = null) {
const form = document.getElementById('userForm');
if (user) {
document.getElementById('userId').value = user.id;
document.getElementById('username').value = user.username;
document.getElementById('password').value = '';
document.getElementById('role').value = user.role;
document.getElementById('expirationDate').value = user.expiration_time;
document.getElementById('expirationClaude').value = user.claude_expiration_time;
} else {
form.reset();
document.getElementById('userId').value = '';
}
modal.classList.remove('hidden');
}
// 关闭用户模态框
function closeModal() {
modal.classList.add('hidden');
}
// 编辑用户
function editUser(userId) {
const user = users.find(u => u.id === userId);
if (user) {
openModal(user);
}
}
// 打开绑定模态框
function openBindModal(user = null,type) {
if(type == 1){
url = "/api/all_email"
fetch(url)
.then(response => response.json())
.then(data => {
const emailSelect = document.getElementById('email');
document.getElementById('email').classList.remove('hidden')
document.getElementById('claudeEmail').classList.add('hidden')
emailSelect.innerHTML = '';
document.getElementById('userId').value = user.id;
data.forEach(email => {
const option = document.createElement('option');
option.value = email;
option.textContent = email;
emailSelect.appendChild(option);
});
if (user && user.email) {
emailSelect.value = user.email;
}
})
.catch(error => console.error('Error:', error));
}else{
url = "/api/all_claude_email"
fetch(url)
.then(response => response.json())
.then(data => {
const emailSelect = document.getElementById('claudeEmail');
document.getElementById('claudeEmail').classList.remove('hidden')
document.getElementById('email').classList.add('hidden')
emailSelect.innerHTML = '';
document.getElementById('userId').value = user.id;
data.forEach(email => {
const option = document.createElement('option');
option.value = email;
option.textContent = email;
emailSelect.appendChild(option);
});
if (user && user.email) {
emailSelect.value = user.email;
}
})
.catch(error => console.error('Error:', error));
}
bindModal.classList.remove('hidden');
}
// 关闭绑定模态框
function closeBindModal() {
bindModal.classList.add('hidden');
}
// 绑定GPT账号
function bindChatGPT(userId) {
const user = users.find(u => u.id === userId);
if (user) {
openBindModal(user,1);
}
}
// 绑定Claude账号
function bindClaude(userId) {
const user = users.find(u => u.id === userId);
if (user) {
openBindModal(user,0);
}
}
// 删除用户
function deleteUser(userId) {
if (confirm('确定要删除这个用户吗?')) {
fetch(`/api/users/${userId}`, {
method: 'DELETE'
})
.then(response => response.json())
.then(data => {
if (data.success) {
loadUsers();
}
})
.catch(error => console.error('Error:', error));
}
}
// 解绑GPT账号
function deleteBind(userId) {
if (confirm('确定要解除这个用户绑定的ChatGPT账号吗?')) {
fetch(`/api/del_bind/${userId}`, {
method: 'DELETE'
})
.then(response => response.json())
.then(data => {
if (data.success) {
loadUsers();
}
})
.catch(error => console.error('Error:', error));
}
}
// 解绑Claude账号
function deleteClaudeBind(userId) {
if (confirm('确定要解除这个用户绑定的Claude账号吗?')) {
fetch(`/api/del_bindClaude/${userId}`, {
method: 'DELETE'
})
.then(response => response.json())
.then(data => {
if (data.success) {
loadUsers();
}
})
.catch(error => console.error('Error:', error));
}
}
// 表单提交处理
userForm.addEventListener('submit', function (e) {
e.preventDefault();
const userId = document.getElementById('userId').value;
const userData = {
username: document.getElementById('username').value,
password: document.getElementById('password').value,
expiration_time: document.getElementById('expirationDate').value,
claude_expiration_time: document.getElementById('expirationClaude').value,
role: document.getElementById('role').value
};
const url = userId ? `/api/users/${userId}` : '/api/users';
const method = userId ? 'PUT' : 'POST';
fetch(url, {
method: method,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(userData)
})
.then(response => response.json())
.then(data => {
if (data.success) {
closeModal();
loadUsers();
}
})
.catch(error => console.error('Error:', error));
});
// 表单提交处理
bindForm.addEventListener('submit', function (e) {
e.preventDefault();
const userId = document.getElementById('userId').value;
const emailSelect = document.getElementById('email');
const emailSelectClaude = document.getElementById('claudeEmail');
const bindEmail = {email: emailSelect.value};
const bindClaudeEmail = {email: emailSelectClaude.value};
const method = 'PUT';
if(document.getElementById('claudeEmail').classList.contains('hidden')){
url = `/api/bind/${userId}`;
fetch(url, {
method: method,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(bindEmail)
})
.then(response => response.json())
.then(data => {
if (data.success) {
closeBindModal();
loadUsers();
}
})
.catch(error => console.error('Error:', error));
}else{
url = `/api/bindClaude/${userId}`;
fetch(url, {
method: method,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(bindClaudeEmail)
})
.then(response => response.json())
.then(data => {
if (data.success) {
closeBindModal();
loadUsers();
}
})
.catch(error => console.error('Error:', error));
}
});
// 添加用户按钮点击事件
addUserBtn.addEventListener('click', () => openModal());
// 一键同步
function syc() {
if (confirm('确定是否同步网关?')) {
fetch(`/api/syc`, {
method: 'GET'
})
.then(response => response.json())
.then(data => {
if (data.success) {
loadUsers();
alert('同步成功!'); // 添加弹窗
} else {
alert('同步失败!'); // 可选,处理失败情况
}
})
.catch(error => console.error('Error:', error));
}
}
// 一键同步按钮点击事件
sycGateway.addEventListener('click', () => syc());
// 初始加载用户列表
loadUsers();
</script>
{% endblock %}