This commit is contained in:
@@ -38,6 +38,16 @@
|
||||
</style>
|
||||
{% csrf_token %}
|
||||
<script>
|
||||
const IS_ADMIN = {{ is_admin|yesno:"true,false" }};
|
||||
const HAS_MANAGE_KEY = {{ has_manage_key|yesno:"true,false" }};
|
||||
const CAN_MANAGE_REG = {{ can_manage_registration_codes|yesno:"true,false" }};
|
||||
const MY_KEYS_RAW = JSON.parse('{{ my_keys_json|default:"[]"|escapejs }}');
|
||||
const MY_KEYS_SET = new Set((Array.isArray(MY_KEYS_RAW) ? MY_KEYS_RAW : []).map(v => String(v || '').trim()).filter(Boolean));
|
||||
const MY_MANAGE_KEYS_RAW = JSON.parse('{{ manage_keys_json|default:"[]"|escapejs }}');
|
||||
const MY_MANAGE_KEYS_SET = new Set((Array.isArray(MY_MANAGE_KEYS_RAW) ? MY_MANAGE_KEYS_RAW : []).map(v => String(v || '').trim()).filter(Boolean));
|
||||
const ALLOWED_MANAGE_KEYS_RAW = JSON.parse('{{ allowed_manage_keys_json|default:"[]"|escapejs }}');
|
||||
const ALLOWED_MANAGE_KEYS_SET = new Set((Array.isArray(ALLOWED_MANAGE_KEYS_RAW) ? ALLOWED_MANAGE_KEYS_RAW : []).map(v => String(v || '').trim()).filter(Boolean));
|
||||
|
||||
function getCookie(name){const v=`; ${document.cookie}`;const p=v.split(`; ${name}=`);if(p.length===2) return p.pop().split(';').shift();}
|
||||
async function loadKeys(){
|
||||
const resp=await fetch('/elastic/registration-codes/keys/');
|
||||
@@ -48,8 +58,17 @@
|
||||
keySel.innerHTML=''; mkeySel.innerHTML='';
|
||||
opts.forEach(k=>{
|
||||
const o=document.createElement('option'); o.value=k; o.textContent=k; keySel.appendChild(o);
|
||||
const o2=document.createElement('option'); o2.value=k; o2.textContent=k; mkeySel.appendChild(o2);
|
||||
const o2=document.createElement('option'); o2.value=k; o2.textContent=k;
|
||||
if ((!IS_ADMIN) && HAS_MANAGE_KEY) {
|
||||
const v = String(k || '').trim();
|
||||
if (v && !ALLOWED_MANAGE_KEYS_SET.has(v)) o2.disabled = true;
|
||||
}
|
||||
mkeySel.appendChild(o2);
|
||||
});
|
||||
if ((!IS_ADMIN) && HAS_MANAGE_KEY) {
|
||||
Array.from(keySel.options).forEach(o => { if (MY_KEYS_SET.has(String(o.value || '').trim())) o.selected = true; });
|
||||
Array.from(mkeySel.options).forEach(o => { o.selected = false; });
|
||||
}
|
||||
}
|
||||
async function addKey(){
|
||||
const keyName=(document.getElementById('newKey').value||'').trim();
|
||||
@@ -58,7 +77,12 @@
|
||||
const resp=await fetch('/elastic/registration-codes/keys/add/',{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json','X-CSRFToken':csrftoken||''},body:JSON.stringify({key:keyName})});
|
||||
const data=await resp.json();
|
||||
const msg=document.getElementById('msg');
|
||||
if(resp.ok && data.status==='success'){msg.textContent='新增key成功'; msg.className='notice success'; msg.style.display='block'; document.getElementById('newKey').value=''; loadKeys();}
|
||||
if(resp.ok && data.status==='success'){
|
||||
if ((!IS_ADMIN) && HAS_MANAGE_KEY) {
|
||||
ALLOWED_MANAGE_KEYS_SET.add(keyName);
|
||||
}
|
||||
msg.textContent='新增key成功'; msg.className='notice success'; msg.style.display='block'; document.getElementById('newKey').value=''; loadKeys();
|
||||
}
|
||||
else{msg.textContent=data.message||'新增失败'; msg.className='notice error'; msg.style.display='block';}
|
||||
}
|
||||
async function deleteSelectedKey(){
|
||||
@@ -72,20 +96,28 @@
|
||||
alert('请先在下方列表中选择一个要删除的Key');
|
||||
return;
|
||||
}
|
||||
|
||||
if ((!IS_ADMIN) && HAS_MANAGE_KEY) {
|
||||
const v = String(selectedKey || '').trim();
|
||||
if (!v || !ALLOWED_MANAGE_KEYS_SET.has(v)) {
|
||||
const msg=document.getElementById('msg');
|
||||
msg.textContent='只能删除自己新增的 key';
|
||||
msg.className='notice error';
|
||||
msg.style.display='block';
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(!confirm(`确定要全局删除Key \"${selectedKey}\" 吗?\n该操作将:\n1. 从全局可选Key列表中移除\n2. 从所有包含此Key的注册码中同步清除\n此操作不可恢复!`)) return;
|
||||
|
||||
const ov=document.getElementById('overlay'); ov.style.display='flex';
|
||||
const csrftoken=getCookie('csrftoken');
|
||||
const resp=await fetch('/elastic/registration-codes/keys/remove/',{
|
||||
method:'POST',
|
||||
credentials:'same-origin',
|
||||
headers:{'Content-Type':'application/json','X-CSRFToken':csrftoken||''},
|
||||
body:JSON.stringify({key:selectedKey})
|
||||
});
|
||||
const url = '/elastic/registration-codes/keys/remove/';
|
||||
const resp=await fetch(url,{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json','X-CSRFToken':csrftoken||''},body:JSON.stringify({key:selectedKey})});
|
||||
const data=await resp.json();
|
||||
const msg=document.getElementById('msg');
|
||||
if(resp.ok && data.status==='success'){
|
||||
if ((!IS_ADMIN) && HAS_MANAGE_KEY) {
|
||||
ALLOWED_MANAGE_KEYS_SET.delete(String(selectedKey||'').trim());
|
||||
}
|
||||
msg.textContent = data.message || '删除成功';
|
||||
msg.className='notice success';
|
||||
msg.style.display='block';
|
||||
@@ -98,14 +130,51 @@
|
||||
}
|
||||
ov.style.display='none';
|
||||
}
|
||||
function selectedValues(sel){return Array.from(sel.selectedOptions).map(o=>o.value);}
|
||||
function enableToggleSelect(sel){ sel.addEventListener('mousedown',function(e){ if(e.target && e.target.tagName==='OPTION'){ e.preventDefault(); const op=e.target; op.selected=!op.selected; this.dispatchEvent(new Event('change',{bubbles:true})); } }); }
|
||||
function clearSelection(id){ const sel=document.getElementById(id); Array.from(sel.options).forEach(o=>o.selected=false); }
|
||||
function selectedValues(sel){return Array.from(sel.selectedOptions).map(o=>o.value);}
|
||||
function enableToggleSelect(sel){
|
||||
sel.addEventListener('mousedown', function(e){
|
||||
if(e.target && e.target.tagName==='OPTION'){
|
||||
e.preventDefault();
|
||||
const op=e.target;
|
||||
if (op.disabled) return;
|
||||
op.selected = !op.selected;
|
||||
this.dispatchEvent(new Event('change',{bubbles:true}));
|
||||
}
|
||||
});
|
||||
}
|
||||
function clearSelection(id){
|
||||
const sel=document.getElementById(id);
|
||||
Array.from(sel.options).forEach(o=>{ o.selected = false; });
|
||||
}
|
||||
async function generateCode(){
|
||||
const ov=document.getElementById('overlay'); ov.style.display='flex';
|
||||
const csrftoken=getCookie('csrftoken');
|
||||
const keys=selectedValues(document.getElementById('keys'));
|
||||
const manageKeys=selectedValues(document.getElementById('manageKeys'));
|
||||
const keySel = document.getElementById('keys');
|
||||
let keys=selectedValues(keySel);
|
||||
if ((!IS_ADMIN) && HAS_MANAGE_KEY) {
|
||||
const selected = new Set(keys.map(k=>String(k||'').trim()).filter(Boolean));
|
||||
const missing = Array.from(MY_KEYS_SET).filter(k => !selected.has(k));
|
||||
if (missing.length) {
|
||||
const msg=document.getElementById('msg');
|
||||
msg.textContent = `必须选择导师原有的 key:${missing.join('、')}`;
|
||||
msg.className='notice error';
|
||||
msg.style.display='block';
|
||||
ov.style.display='none';
|
||||
return;
|
||||
}
|
||||
}
|
||||
let manageKeys=selectedValues(document.getElementById('manageKeys'));
|
||||
if ((!IS_ADMIN) && HAS_MANAGE_KEY) {
|
||||
const hasForbidden = manageKeys.some(k => !ALLOWED_MANAGE_KEYS_SET.has(String(k || '').trim()));
|
||||
if (hasForbidden) {
|
||||
const msg=document.getElementById('msg');
|
||||
msg.textContent='manage_key 只能选择本页新增的 key';
|
||||
msg.className='notice error';
|
||||
msg.style.display='block';
|
||||
ov.style.display='none';
|
||||
return;
|
||||
}
|
||||
}
|
||||
const mode=document.getElementById('expireMode').value;
|
||||
let days=30; if(mode==='month') days=30; else if(mode==='fouryears') days=1460; else { const d=parseInt(document.getElementById('customDays').value||'30'); days=isNaN(d)?30:Math.max(1,d);}
|
||||
const resp=await fetch('/elastic/registration-codes/generate/',{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json','X-CSRFToken':csrftoken||''},body:JSON.stringify({keys,manage_keys:manageKeys,expires_in_days:days})});
|
||||
@@ -138,7 +207,12 @@
|
||||
function formatDate(t){ if(!t) return ''; try{ const d = new Date(t); if(String(d)!='Invalid Date'){ const p=n=>String(n).padStart(2,'0'); return `${d.getFullYear()}-${p(d.getMonth()+1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;} }catch(e){} return ''; }
|
||||
async function revokeCode(code){ const csrftoken=getCookie('csrftoken'); const resp=await fetch('/elastic/registration-codes/revoke/',{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json','X-CSRFToken':csrftoken||''},body:JSON.stringify({code})}); const msg=document.getElementById('msg'); const data=await resp.json(); if(resp.ok && data.status==='success'){ msg.textContent='已作废'; msg.className='notice success'; msg.style.display='block'; loadCodes(); } else { msg.textContent=data.message||'作废失败'; msg.className='notice error'; msg.style.display='block'; } }
|
||||
document.addEventListener('click',function(e){ const btn=e.target; if(btn && btn.matches('button[data-code]')){ revokeCode(btn.getAttribute('data-code')); }});
|
||||
document.addEventListener('DOMContentLoaded',()=>{loadKeys(); enableToggleSelect(document.getElementById('keys')); enableToggleSelect(document.getElementById('manageKeys')); loadCodes();});
|
||||
document.addEventListener('DOMContentLoaded',()=>{
|
||||
loadKeys();
|
||||
enableToggleSelect(document.getElementById('keys'));
|
||||
enableToggleSelect(document.getElementById('manageKeys'));
|
||||
loadCodes();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
@@ -155,16 +229,20 @@
|
||||
<div class="main">
|
||||
<div class="card fade-in">
|
||||
<h2>管理注册码</h2>
|
||||
{% if is_admin or has_manage_key or can_manage_registration_codes %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label>管理 Key</label>
|
||||
<div style="display:flex; gap:8px;">
|
||||
<input id="newKey" type="text" placeholder="输入新的key进行新增,或在下方选择后删除" style="flex: 1;" />
|
||||
<button class="btn btn-secondary" onclick="addKey()">新增 Key</button>
|
||||
{% if is_admin or has_manage_key %}
|
||||
<button class="btn btn-danger" onclick="deleteSelectedKey()">删除选中 Key</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row" style="margin-top:12px;">
|
||||
<div class="col">
|
||||
<label>选择 keys</label>
|
||||
@@ -384,4 +462,4 @@
|
||||
loadRecent();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -163,7 +163,7 @@
|
||||
|
||||
.modal-content {
|
||||
background-color: white;
|
||||
margin: 10% auto;
|
||||
margin: 6% auto;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
width: 80%;
|
||||
@@ -207,6 +207,71 @@
|
||||
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>
|
||||
@@ -306,7 +371,7 @@
|
||||
<label for="username">用户名</label>
|
||||
<input type="text" id="username" name="username" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-group" id="permissionGroup">
|
||||
<label for="permission">权限</label>
|
||||
<select id="permission" name="permission" required>
|
||||
<option value="0">管理员</option>
|
||||
@@ -314,6 +379,28 @@
|
||||
</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>
|
||||
@@ -340,6 +427,14 @@
|
||||
</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}`;
|
||||
@@ -430,10 +525,7 @@
|
||||
if (!select) return;
|
||||
select.innerHTML = '<option value="">全部Key</option>';
|
||||
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 || [];
|
||||
const keys = await fetchKeyOptions();
|
||||
keys.forEach(k => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = String(k || '').trim();
|
||||
@@ -448,22 +540,205 @@
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 打开添加用户模态框
|
||||
function openAddModal() {
|
||||
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';
|
||||
}
|
||||
|
||||
// 打开编辑用户模态框
|
||||
function openEditModal(user) {
|
||||
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';
|
||||
@@ -499,10 +774,15 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const data = {
|
||||
username: username,
|
||||
permission: parseInt(permission)
|
||||
};
|
||||
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;
|
||||
@@ -539,7 +819,9 @@
|
||||
if (result.status === 'success') {
|
||||
showNotification(userId ? '用户更新成功' : '用户添加成功');
|
||||
document.getElementById('userModal').style.display = 'none';
|
||||
loadUsers();
|
||||
const searchTerm = (document.getElementById('searchInput') || {}).value || '';
|
||||
const key = (document.getElementById('keyFilter') || {}).value || '';
|
||||
loadUsers(searchTerm, key);
|
||||
} else {
|
||||
showNotification(result.message || '操作失败', false);
|
||||
}
|
||||
@@ -624,6 +906,31 @@
|
||||
});
|
||||
}
|
||||
|
||||
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');
|
||||
|
||||
Reference in New Issue
Block a user