Files
Achievement_Inputing/elastic/templates/elastic/users.html
DSQ 01a3b2dfdb
All checks were successful
CI / docker-ci (push) Successful in 30s
部分修改[0.2.8.13][ci]
2026-03-23 13:08:42 +08:00

962 lines
33 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>用户管理</title>
<style>
body {margin: 0;font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;background: #fafafa;}
/* 导航栏样式 */
.sidebar {position: fixed;top: 0;left: 0;width: 180px;height: 100vh;background: #1e1e2e;color: white;padding: 20px;box-shadow: 2px 0 5px rgba(0,0,0,0.1);z-index: 1000;display: flex;
flex-direction: column;align-items: center;}
.user-id {text-align: center;margin-bottom: 0px;}
.sidebar h3 {margin-top: 0;font-size: 18px;color: #add8e6;text-align: center; margin-bottom: 20px;}
.navigation-links {width: 100%;margin-top: 60px;}
.sidebar a,
.sidebar button {display: block;color: #8be9fd;text-decoration: none;margin: 10px 0;font-size: 16px;padding: 15px;border-radius: 4px;background: transparent;
border: none;cursor: pointer; width: calc(100% - 40px);text-align: left;transition: all 0.2s ease;}
.sidebar a:hover,
.sidebar button:hover {color: #ff79c6;background-color: rgba(139, 233, 253, 0.2);}
/* 主内容区 */
.main-content {
margin-left: 200px;
padding: 20px;
color: #333;
}
.card {
background: #fff;
border-radius: 14px;
box-shadow: 0 10px 24px rgba(31,35,40,0.08);
padding: 20px;
margin-bottom: 20px;
}
.header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.btn {
padding: 8px 12px;
border: none;
border-radius: 8px;
cursor: pointer;
margin: 0 4px;
}
.btn-primary {
background: #4f46e5;
color: #fff;
}
.btn-danger {
background: #ef4444;
color: #fff;
}
.btn-success {
background: #22c55e;
color: #fff;
}
.table-container {
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
th, td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #e5e7eb;
}
th {
background-color: #f9fafb;
font-weight: bold;
color: #374151;
}
tr:hover {
background-color: #f3f4f6;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.form-group input, .form-group select {
width: 100%;
padding: 8px 12px;
border: 1px solid #d1d5db;
border-radius: 6px;
box-sizing: border-box;
}
.form-row {
display: flex;
gap: 15px;
margin-bottom: 15px;
}
.form-row .form-group {
flex: 1;
margin-bottom: 0;
}
.action-buttons {
display: flex;
gap: 8px;
}
.search-container {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.search-container input {
flex: 1;
padding: 8px 12px;
border: 1px solid #d1d5db;
border-radius: 6px;
}
.search-container select {
padding: 8px 12px;
border: 1px solid #d1d5db;
border-radius: 6px;
background: #fff;
}
.search-container button {
padding: 8px 15px;
background: #4f46e5;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
}
.modal {
display: none;
position: fixed;
z-index: 2000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
}
.modal-content {
background-color: white;
margin: 6% auto;
padding: 20px;
border-radius: 8px;
width: 80%;
max-width: 500px;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.close:hover {
color: black;
}
.notification {
padding: 10px;
margin-bottom: 15px;
border-radius: 4px;
display: none;
}
.notification.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.notification.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.nav-error {
color: #ef4444;
font-size: 12px;
margin-top: 5px;
text-align: center;
}
.keys-box {
max-height: 140px;
overflow: auto;
border: 1px solid #d1d5db;
border-radius: 6px;
padding: 8px 10px;
background: #fff;
}
.key-item {
display: flex;
align-items: center;
gap: 8px;
padding: 4px 0;
font-size: 14px;
color: #111827;
user-select: none;
}
.key-item input[type="checkbox"] {
width: auto;
padding: 0;
margin: 0;
}
.key-edit-row {
display: flex;
gap: 10px;
align-items: center;
}
.selected-keys {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 10px;
}
.key-tag {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 10px;
border-radius: 999px;
background: #eef2ff;
color: #1f2937;
border: 1px solid #c7d2fe;
font-size: 13px;
}
.key-tag button {
border: none;
background: transparent;
cursor: pointer;
color: #4b5563;
font-size: 14px;
line-height: 1;
}
.key-tag.locked {
background: #f3f4f6;
border: 1px solid #e5e7eb;
color: #374151;
}
</style>
</head>
<body>
<!-- 左侧固定栏目 -->
<div class="sidebar">
<div class="user-id">
<h3>你好,{{ username|default:"访客" }}</h3>
</div>
<div class="navigation-links">
<a href="{% url 'main:home' %}" onclick="return handleNavClick(this, '/');">返回主页</a>
<a id="logoutBtn">退出登录</a>
<div id="logoutMsg"></div>
{% csrf_token %}
</div>
</div>
<div class="main-content">
<div class="card">
<div class="header">
<h2>用户管理</h2>
{% if is_admin %}<button id="addUserBtn" class="btn btn-primary">添加用户</button>{% endif %}
</div>
<div class="notification success" id="successNotification">操作成功!</div>
<div class="notification error" id="errorNotification">操作失败!</div>
<div class="search-container">
<input type="text" id="searchInput" placeholder="搜索用户名...">
<select id="keyFilter"></select>
<button id="searchBtn">搜索</button>
<button id="resetBtn">重置</button>
<button id="clearKeyBtn">清空Key</button>
</div>
<div class="table-container">
<table id="usersTable">
<thead>
<tr>
<th>用户ID</th>
<th>用户名</th>
<th>Key</th>
<th>Manage Key</th>
<th>权限</th>
<th>操作</th>
</tr>
</thead>
<tbody id="usersTableBody"></tbody>
</table>
</div>
</div>
</div>
<!-- 添加/编辑用户模态框 -->
<div id="userModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<h2 id="modalTitle">添加用户</h2>
<form id="userForm">
<input type="hidden" id="userId" name="user_id">
<div class="form-row">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" id="username" name="username" required>
</div>
<div class="form-group" id="permissionGroup">
<label for="permission">权限</label>
<select id="permission" name="permission" required>
<option value="0">管理员</option>
<option value="1">普通用户</option>
</select>
</div>
</div>
<div class="form-group">
<label>Key从已有 Key 中选择)</label>
<div class="key-edit-row">
<select id="userKeySelect"></select>
<button type="button" id="addUserKeyBtn" class="btn btn-primary">添加</button>
<button type="button" id="clearUserKeyBtn" class="btn">清空</button>
</div>
<div id="userKeysSelected" class="selected-keys"></div>
<div id="userKeysReadonlyGroup" style="display:none; margin-top: 10px;">
<div style="font-weight: 600; color: #374151; font-size: 13px; margin-bottom: 6px;">导师Key不可修改</div>
<div id="userKeysReadonly" class="selected-keys"></div>
</div>
</div>
<div class="form-group" id="manageKeyGroup">
<label>Manage Key从已有 Key 中选择)</label>
<div class="key-edit-row">
<select id="userManageKeySelect"></select>
<button type="button" id="addUserManageKeyBtn" class="btn btn-primary">添加</button>
<button type="button" id="clearUserManageKeyBtn" class="btn">清空</button>
</div>
<div id="userManageKeysSelected" class="selected-keys"></div>
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" id="password" name="password" required>
</div>
<div class="form-group">
<label for="confirmPassword">确认密码</label>
<input type="password" id="confirmPassword" name="confirmPassword" required>
</div>
<button type="submit" class="btn btn-primary">保存</button>
</form>
</div>
</div>
<!-- 确认删除模态框 -->
<div id="deleteModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<h2>确认删除</h2>
<p>确定要删除用户 <strong id="deleteUserName"></strong> 吗?此操作不可撤销。</p>
<input type="hidden" id="deleteUserId">
<button id="confirmDeleteBtn" class="btn btn-danger">确认删除</button>
<button class="btn">取消</button>
</div>
</div>
<script>
const IS_ADMIN = {{ is_admin|yesno:"true,false" }};
const IS_TUTOR = {{ is_tutor|yesno:"true,false" }};
const MY_MANAGE_KEYS_RAW = JSON.parse('{{ manage_keys_json|default:"[]"|escapejs }}');
const MY_KEYS_RAW = JSON.parse('{{ my_keys_json|default:"[]"|escapejs }}');
let KEY_OPTIONS_CACHE = null;
let MODAL_SELECTED_KEYS = [];
let MODAL_SELECTED_MANAGE_KEYS = [];
// 获取CSRF令牌的函数
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
// 导航点击处理函数提供备用URL
function handleNavClick(element, fallbackUrl) {
// 尝试使用Django模板生成的URL如果失败则使用备用URL
try {
// 如果模板渲染正常直接返回true让默认行为处理
return true;
} catch (e) {
// 如果模板渲染有问题使用备用URL
window.location.href = fallbackUrl;
return false;
}
}
// 显示通知
function showNotification(message, isSuccess = true) {
const notification = isSuccess ?
document.getElementById('successNotification') :
document.getElementById('errorNotification');
notification.textContent = message;
notification.style.display = 'block';
setTimeout(() => {
notification.style.display = 'none';
}, 3000);
}
// 获取所有用户
async function loadUsers(searchTerm = '', key = '') {
try {
const params = new URLSearchParams();
if ((searchTerm || '').trim()) params.set('search', (searchTerm || '').trim());
if ((key || '').trim()) params.set('key', (key || '').trim());
const url = params.toString() ? `/elastic/users/?${params.toString()}` : '/elastic/users/';
const response = await fetch(url);
const result = await response.json();
if (result.status === 'success') {
const tbody = document.getElementById('usersTableBody');
tbody.innerHTML = '';
// 处理不同格式的API响应
const users = result.data || result.users || [];
users.forEach(user => {
const row = document.createElement('tr');
// 根据权限值显示权限名称
const permissionText = Number(user.permission) === 0 ? '管理员' : '普通用户';
const keys = Array.isArray(user.key) ? user.key : (user.key ? [user.key] : []);
const keysText = keys.map(k => String(k || '').trim()).filter(Boolean).join('、') || '-';
const manageKeys = Array.isArray(user.manage_key) ? user.manage_key : (user.manage_key ? [user.manage_key] : []);
const manageKeysText = manageKeys.map(k => String(k || '').trim()).filter(Boolean).join('、') || '-';
row.innerHTML = `
<td>${user.user_id}</td>
<td>${user.username}</td>
<td>${keysText}</td>
<td>${manageKeysText}</td>
<td>${permissionText}</td>
<td class="action-buttons">
<button class="btn btn-success edit-btn" data-user='${JSON.stringify(user)}'>编辑</button>
<button class="btn btn-danger delete-btn" data-username="${user.username}" data-userid="${user.user_id}">删除</button>
</td>
`;
tbody.appendChild(row);
});
} else {
showNotification('获取用户列表失败', false);
}
} catch (error) {
console.error('加载用户列表失败:', error);
showNotification('获取用户列表失败', false);
}
}
async function initKeyFilter() {
const select = document.getElementById('keyFilter');
if (!select) return;
select.innerHTML = '<option value="">全部Key</option>';
try {
const keys = await fetchKeyOptions();
keys.forEach(k => {
const opt = document.createElement('option');
opt.value = String(k || '').trim();
opt.textContent = String(k || '').trim();
if (opt.value) select.appendChild(opt);
});
} catch (e) {
}
select.addEventListener('change', () => {
const searchTerm = document.getElementById('searchInput').value;
loadUsers(searchTerm, select.value);
});
}
function normalizeStr(v) {
return String(v || '').trim();
}
const MY_MANAGE_KEYS = (Array.isArray(MY_MANAGE_KEYS_RAW) ? MY_MANAGE_KEYS_RAW : [])
.map(normalizeStr)
.filter(Boolean);
const MY_MANAGE_KEYS_SET = new Set(MY_MANAGE_KEYS);
const MY_KEYS = (Array.isArray(MY_KEYS_RAW) ? MY_KEYS_RAW : [])
.map(normalizeStr)
.filter(Boolean);
const MY_KEYS_SET = new Set(MY_KEYS);
async function fetchKeyOptions() {
if (Array.isArray(KEY_OPTIONS_CACHE)) return KEY_OPTIONS_CACHE;
try {
const resp = await fetch('/elastic/keys-for-filter/', { credentials: 'same-origin' });
const data = await resp.json();
if (data.status !== 'success') return [];
const keys = (data.data || []).map(normalizeStr).filter(Boolean);
KEY_OPTIONS_CACHE = keys;
return keys;
} catch (e) {
return [];
}
}
function setSelectOptions(selectId, options) {
const select = document.getElementById(selectId);
if (!select) return;
select.innerHTML = '<option value="">请选择Key</option>';
(options || []).forEach(k => {
const s = normalizeStr(k);
if (!s) return;
const opt = document.createElement('option');
opt.value = s;
opt.textContent = s;
select.appendChild(opt);
});
}
function setSelectOptionsMixed(selectId, enabledOptions, disabledOptions) {
const select = document.getElementById(selectId);
if (!select) return;
select.innerHTML = '<option value="">请选择Key</option>';
(enabledOptions || []).forEach(k => {
const s = normalizeStr(k);
if (!s) return;
const opt = document.createElement('option');
opt.value = s;
opt.textContent = s;
select.appendChild(opt);
});
(disabledOptions || []).forEach(k => {
const s = normalizeStr(k);
if (!s) return;
const opt = document.createElement('option');
opt.value = s;
opt.textContent = s;
opt.disabled = true;
select.appendChild(opt);
});
}
function renderSelectedTags(containerId, selectedArr) {
const container = document.getElementById(containerId);
if (!container) return;
container.innerHTML = '';
(selectedArr || []).forEach(k => {
const tag = document.createElement('span');
tag.className = 'key-tag';
const text = document.createElement('span');
text.textContent = k;
const btn = document.createElement('button');
btn.type = 'button';
btn.textContent = '×';
btn.addEventListener('click', () => {
const idx = selectedArr.indexOf(k);
if (idx >= 0) selectedArr.splice(idx, 1);
renderSelectedTags(containerId, selectedArr);
});
tag.appendChild(text);
tag.appendChild(btn);
container.appendChild(tag);
});
}
function renderReadonlyTags(containerId, keysArr) {
const container = document.getElementById(containerId);
if (!container) return;
container.innerHTML = '';
(keysArr || []).forEach(k => {
const tag = document.createElement('span');
tag.className = 'key-tag locked';
const text = document.createElement('span');
text.textContent = k;
tag.appendChild(text);
container.appendChild(tag);
});
}
function setReadonlyKeysVisible(visible) {
const group = document.getElementById('userKeysReadonlyGroup');
if (group) group.style.display = visible ? '' : 'none';
}
function setKeyEditorDisabled(prefix, disabled) {
const select = document.getElementById(prefix + 'Select');
const addBtn = document.getElementById('add' + prefix.charAt(0).toUpperCase() + prefix.slice(1) + 'Btn');
const clearBtn = document.getElementById('clear' + prefix.charAt(0).toUpperCase() + prefix.slice(1) + 'Btn');
if (select) select.disabled = !!disabled;
if (addBtn) addBtn.disabled = !!disabled;
if (clearBtn) clearBtn.disabled = !!disabled;
}
function addFromSelect(selectId, selectedArr, renderId) {
const select = document.getElementById(selectId);
if (!select) return;
const v = normalizeStr(select.value);
if (!v) return;
if (!selectedArr.includes(v)) selectedArr.push(v);
renderSelectedTags(renderId, selectedArr);
}
function clearSelected(selectedArr, renderId) {
selectedArr.length = 0;
renderSelectedTags(renderId, selectedArr);
}
// 打开添加用户模态框
async function openAddModal() {
document.getElementById('modalTitle').textContent = '添加用户';
document.getElementById('userForm').reset();
document.getElementById('userId').value = '';
document.getElementById('username').disabled = false;
document.getElementById('permission').disabled = false;
document.getElementById('permissionGroup').style.display = '';
document.getElementById('manageKeyGroup').style.display = '';
const options = await fetchKeyOptions();
if ((!IS_ADMIN) && IS_TUTOR) {
const enabled = (options || []).map(normalizeStr).filter(k => k && !MY_KEYS_SET.has(k));
setSelectOptionsMixed('userKeySelect', enabled, MY_KEYS);
} else {
setSelectOptions('userKeySelect', options);
}
setSelectOptions('userManageKeySelect', options);
MODAL_SELECTED_KEYS = [];
MODAL_SELECTED_MANAGE_KEYS = [];
renderSelectedTags('userKeysSelected', MODAL_SELECTED_KEYS);
renderSelectedTags('userManageKeysSelected', MODAL_SELECTED_MANAGE_KEYS);
setReadonlyKeysVisible(false);
renderReadonlyTags('userKeysReadonly', []);
setKeyEditorDisabled('userKey', false);
setKeyEditorDisabled('userManageKey', false);
document.getElementById('password').required = true;
document.getElementById('confirmPassword').required = true;
document.getElementById('userModal').style.display = 'block';
}
// 打开编辑用户模态框
async function openEditModal(user) {
document.getElementById('modalTitle').textContent = '编辑用户';
document.getElementById('username').value = user.username;
document.getElementById('userId').value = user.user_id;
document.getElementById('permission').value = user.permission;
const options = await fetchKeyOptions();
setSelectOptions('userManageKeySelect', options);
const allUserKeys = (Array.isArray(user.key) ? user.key : (user.key ? [user.key] : [])).map(normalizeStr).filter(Boolean);
const lockedKeys = allUserKeys.filter(k => MY_KEYS_SET.has(k));
if ((!IS_ADMIN) && IS_TUTOR) {
const enabled = (options || []).map(normalizeStr).filter(k => k && !MY_KEYS_SET.has(k));
setSelectOptionsMixed('userKeySelect', enabled, MY_KEYS);
} else {
setSelectOptions('userKeySelect', options);
}
MODAL_SELECTED_KEYS = IS_ADMIN ? allUserKeys : allUserKeys.filter(k => !MY_KEYS_SET.has(k));
MODAL_SELECTED_MANAGE_KEYS = (Array.isArray(user.manage_key) ? user.manage_key : (user.manage_key ? [user.manage_key] : [])).map(normalizeStr).filter(Boolean);
MODAL_SELECTED_KEYS = Array.from(new Set(MODAL_SELECTED_KEYS));
MODAL_SELECTED_MANAGE_KEYS = Array.from(new Set(MODAL_SELECTED_MANAGE_KEYS));
renderSelectedTags('userKeysSelected', MODAL_SELECTED_KEYS);
renderSelectedTags('userManageKeysSelected', MODAL_SELECTED_MANAGE_KEYS);
setReadonlyKeysVisible((!IS_ADMIN) && IS_TUTOR && lockedKeys.length > 0);
renderReadonlyTags('userKeysReadonly', ((!IS_ADMIN) && IS_TUTOR) ? Array.from(new Set(lockedKeys)) : []);
if (IS_ADMIN) {
document.getElementById('username').disabled = false;
document.getElementById('permission').disabled = false;
document.getElementById('permissionGroup').style.display = '';
document.getElementById('manageKeyGroup').style.display = '';
setKeyEditorDisabled('userKey', false);
setKeyEditorDisabled('userManageKey', false);
} else {
document.getElementById('username').disabled = true;
document.getElementById('permission').disabled = true;
document.getElementById('permissionGroup').style.display = 'none';
document.getElementById('manageKeyGroup').style.display = 'none';
setKeyEditorDisabled('userKey', !IS_TUTOR);
setKeyEditorDisabled('userManageKey', true);
}
document.getElementById('password').required = false;
document.getElementById('confirmPassword').required = false;
document.getElementById('userModal').style.display = 'block';
}
// 打开删除确认模态框
function openDeleteModal(username, userId) {
document.getElementById('deleteUserName').textContent = username;
document.getElementById('deleteUserId').value = userId;
document.getElementById('deleteModal').style.display = 'block';
}
// 保存用户(添加或编辑)
async function saveUser(event) {
event.preventDefault();
const formData = new FormData(event.target);
const userId = formData.get('user_id');
const username = formData.get('username');
const permission = formData.get('permission');
const password = formData.get('password');
const confirmPassword = formData.get('confirmPassword');
// 验证密码
if (password !== confirmPassword) {
showNotification('密码和确认密码不匹配', false);
return;
}
// 验证密码长度(如果提供了密码)
if (password && password.length < 6) {
showNotification('密码长度至少为6位', false);
return;
}
const data = {};
if (IS_ADMIN) {
data.username = username;
data.permission = parseInt(permission);
data.key = MODAL_SELECTED_KEYS;
data.manage_key = MODAL_SELECTED_MANAGE_KEYS;
} else {
data.key = MODAL_SELECTED_KEYS;
}
if (password) {
data.password = password;
}
try {
const csrftoken = getCookie('csrftoken');
let response;
if (userId) {
response = await fetch(`/elastic/users/${userId}/update/`, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken
},
body: JSON.stringify(data)
});
} else {
response = await fetch('/elastic/users/add/', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken
},
body: JSON.stringify(data)
});
}
const result = await response.json();
if (result.status === 'success') {
showNotification(userId ? '用户更新成功' : '用户添加成功');
document.getElementById('userModal').style.display = 'none';
const searchTerm = (document.getElementById('searchInput') || {}).value || '';
const key = (document.getElementById('keyFilter') || {}).value || '';
loadUsers(searchTerm, key);
} else {
showNotification(result.message || '操作失败', false);
}
} catch (error) {
console.error('保存用户失败:', error);
showNotification('保存用户失败', false);
}
}
// 删除用户
async function deleteUser() {
const userId = document.getElementById('deleteUserId').value;
try {
const csrftoken = getCookie('csrftoken');
const response = await fetch(`/elastic/users/${userId}/delete/`, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken
}
});
const result = await response.json();
if (result.status === 'success') {
showNotification('用户删除成功');
document.getElementById('deleteModal').style.display = 'none';
loadUsers();
} else {
showNotification(result.message || '删除失败', false);
}
} catch (error) {
console.error('删除用户失败:', error);
showNotification('删除用户失败', false);
}
}
// 事件监听器
const addBtn = document.getElementById('addUserBtn');
if (addBtn) {
addBtn.addEventListener('click', openAddModal);
}
document.getElementById('userForm').addEventListener('submit', saveUser);
document.getElementById('confirmDeleteBtn').addEventListener('click', deleteUser);
document.querySelectorAll('.close').forEach(closeBtn => {
closeBtn.addEventListener('click', function() {
this.parentElement.parentElement.style.display = 'none';
});
});
const searchBtn = document.getElementById('searchBtn');
if (searchBtn) {
searchBtn.addEventListener('click', function() {
const searchTerm = document.getElementById('searchInput').value;
const key = (document.getElementById('keyFilter') || {}).value || '';
loadUsers(searchTerm, key);
});
}
const resetBtn = document.getElementById('resetBtn');
if (resetBtn) {
resetBtn.addEventListener('click', function() {
document.getElementById('searchInput').value = '';
const select = document.getElementById('keyFilter');
if (select) select.value = '';
loadUsers('', '');
});
}
const clearKeyBtn = document.getElementById('clearKeyBtn');
if (clearKeyBtn) {
clearKeyBtn.addEventListener('click', function() {
const select = document.getElementById('keyFilter');
if (select) select.value = '';
const searchTerm = document.getElementById('searchInput').value;
loadUsers(searchTerm, '');
});
}
const addUserKeyBtn = document.getElementById('addUserKeyBtn');
if (addUserKeyBtn) {
addUserKeyBtn.addEventListener('click', function() {
addFromSelect('userKeySelect', MODAL_SELECTED_KEYS, 'userKeysSelected');
});
}
const clearUserKeyBtn = document.getElementById('clearUserKeyBtn');
if (clearUserKeyBtn) {
clearUserKeyBtn.addEventListener('click', function() {
clearSelected(MODAL_SELECTED_KEYS, 'userKeysSelected');
});
}
const addUserManageKeyBtn = document.getElementById('addUserManageKeyBtn');
if (addUserManageKeyBtn) {
addUserManageKeyBtn.addEventListener('click', function() {
addFromSelect('userManageKeySelect', MODAL_SELECTED_MANAGE_KEYS, 'userManageKeysSelected');
});
}
const clearUserManageKeyBtn = document.getElementById('clearUserManageKeyBtn');
if (clearUserManageKeyBtn) {
clearUserManageKeyBtn.addEventListener('click', function() {
clearSelected(MODAL_SELECTED_MANAGE_KEYS, 'userManageKeysSelected');
});
}
// 点击模态框外部关闭模态框
window.addEventListener('click', function(event) {
const modals = document.querySelectorAll('.modal');
modals.forEach(modal => {
if (event.target === modal) {
modal.style.display = 'none';
}
});
});
// 登出功能
document.getElementById('logoutBtn').addEventListener('click', async () => {
const msg = document.getElementById('logoutMsg');
msg.textContent = '';
const csrftoken = getCookie('csrftoken');
try {
const resp = await fetch('/accounts/logout/', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken || ''
},
body: JSON.stringify({})
});
const data = await resp.json();
if (!resp.ok || !data.ok) {
throw new Error('登出失败');
}
document.cookie = 'sessionid=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
document.cookie = 'csrftoken=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
window.location.href = data.redirect_url;
} catch (e) {
msg.textContent = e.message || '发生错误';
}
});
// 页面加载时获取用户列表
document.addEventListener('DOMContentLoaded', function() {
initKeyFilter();
const tbody = document.getElementById('usersTableBody');
if (tbody) {
const select = document.getElementById('keyFilter');
loadUsers('', select ? select.value : '');
}
});
// 为表格中的编辑和删除按钮添加事件监听器
document.addEventListener('click', function(e) {
if (e.target.classList.contains('edit-btn')) {
const user = JSON.parse(e.target.getAttribute('data-user'));
openEditModal(user);
}
if (e.target.classList.contains('delete-btn')) {
const username = e.target.getAttribute('data-username');
const userId = e.target.getAttribute('data-userid');
openDeleteModal(username, userId);
}
});
</script>
</body>
</html>