fix: 修复保存满分后清登录;配置双写;设置显示账号/密码;抽取比例改按人数加权
- 修复:仅当连接信息(base_url/api_prefix/username/password/verify_ssl)真变化时才清 token + 重连; 保存满分/布局等不应触发登录卷王(bug:之前一律 unlink token.json,导致保存后所有用户都被踢) - 修复:默认满分不再误写当前卷;改为显式 apply_pid + apply_score 才会改 full_scores - 配置双写(DB+文件):避免 DB 不可用时静默回退到出厂默认 admin/123456; ny_save_config 同时写文件+DB;ny_load_config 在 DB 可用但无记录时从文件自动迁回 - 设置弹窗新增:登录账号可编辑、密码 type=password 不回显、留空表示不修改; 保存后根据 ping 自动给出成功/失败反馈(错误密码不再静默) - 抽取比例改为按组人数加权(默认)+ 各组均分 + 自定义比例 + 不分组,最大余额法守恒分配 - 抽出比例 UI:删除 5:5 复选框、改 #balanceMode 下拉 + 按组渲染比例输入框 - Db::lastError() 暴露 DB 错误用于诊断(config_get 返回 db_driver / db_connected / db_error) - e2e:test-draw-alloc 18→29 测试用例(加权/自定义/守恒 fuzz); 新增 test-config-save e2e 27 用例全过(连接信息变化检测 + 自动 ping + 密码不回显 + apply_pid) - mock-server:账户校验从 'admin' 改为 'mock' 接受 e2e 账号
This commit is contained in:
+40
-17
@@ -550,11 +550,17 @@
|
||||
// 满分:默认 + 各试卷独立
|
||||
$('#cfgFull').value = c.full_score || 100;
|
||||
renderFullScores((c.full_scores && typeof c.full_scores === 'object') ? c.full_scores : {});
|
||||
const pid = $('#projectSel').value;
|
||||
const pObj = state.projects.find(p => p.id === pid);
|
||||
$('#cfgFullHint').textContent = pid
|
||||
? ('当前选中「' + (pObj ? pObj.name : pid) + '」,如需单独设满分请在上方"各试卷独立满分"修改')
|
||||
: '未选中试卷时,作为所有试卷的默认值';
|
||||
// 连接信息(账号回显;密码不回显)
|
||||
$('#cfgBase').value = (c.base_url || '') + (c.api_prefix || '/api');
|
||||
$('#cfgUser').value = c.username || '';
|
||||
$('#cfgPass').value = '';
|
||||
const conn = c.db_configured
|
||||
? (c.db_connected
|
||||
? '数据库:已连接 (' + (c.db_driver || '') + ')'
|
||||
: ('数据库:未连接 · ' + (c.db_error || '未知错误')))
|
||||
: '数据库:未配置(使用文件存储)';
|
||||
$('#cfgConnInfo').textContent = '配置来源:' + (c.storage || 'default') + ' · ' + conn;
|
||||
$('#pingResult').classList.add('hidden');
|
||||
} catch (e) { showAlert(e.message); }
|
||||
$('#settingsModal').classList.add('open');
|
||||
}
|
||||
@@ -698,6 +704,7 @@
|
||||
|
||||
/* 设置保存 */
|
||||
$('#btnSaveCfg').addEventListener('click', async () => {
|
||||
const pass = $('#cfgPass').value;
|
||||
const payload = {
|
||||
page_size: parseInt($('#cfgPageSize').value, 10) || 200,
|
||||
refresh_interval: parseInt($('#cfgInterval').value, 10) || 15,
|
||||
@@ -709,21 +716,37 @@
|
||||
full_score: parseInt($('#cfgFull').value, 10) || 100,
|
||||
layout: (document.querySelector('input[name=cfgLayout]:checked') || {}).value || 'default',
|
||||
full_scores: collectFullScores(),
|
||||
project_id: $('#projectSel').value
|
||||
username: $('#cfgUser').value.trim(),
|
||||
};
|
||||
if (pass !== '') payload.password = pass; // 留空 = 不修改
|
||||
let resp;
|
||||
try {
|
||||
await api('config_save', { method: 'POST', body: payload });
|
||||
resp = await api('config_save', { method: 'POST', body: payload });
|
||||
} catch (e) { showAlert(e.message); return; }
|
||||
$('#cfgPass').value = ''; // 提交后清空密码框
|
||||
const note = (resp && resp.conn_changed) ? '账号/连接已更新' : '设置已保存';
|
||||
if (resp && resp.ping) {
|
||||
if (resp.ping.ok) {
|
||||
showAlert(note + ',测连接成功(' + resp.ping.projects + ' 个项目 / ' + resp.ping.elapsed + 'ms)', 'info');
|
||||
} else {
|
||||
showAlert(note + ',但测连接失败:' + resp.ping.msg, 'error');
|
||||
}
|
||||
} else {
|
||||
showAlert(note, 'info');
|
||||
}
|
||||
// 应用布局偏好(写入容器 + localStorage)
|
||||
applyLayout(payload.layout);
|
||||
const iv = payload.refresh_interval;
|
||||
const opts = $$('#intervalSel option').map(o => parseInt(o.value, 10));
|
||||
let best = opts[0];
|
||||
opts.forEach(v => { if (Math.abs(v - iv) < Math.abs(best - iv)) best = v; });
|
||||
$('#intervalSel').value = String(best);
|
||||
schedule();
|
||||
loadProjects();
|
||||
// ping 失败时保留弹窗,让用户能立即改回
|
||||
if (!resp || !resp.ping || resp.ping.ok) {
|
||||
$('#settingsModal').classList.remove('open');
|
||||
// 应用布局偏好(写入容器 + localStorage)
|
||||
applyLayout(payload.layout);
|
||||
const iv = payload.refresh_interval;
|
||||
const opts = $$('#intervalSel option').map(o => parseInt(o.value, 10));
|
||||
let best = opts[0];
|
||||
opts.forEach(v => { if (Math.abs(v - iv) < Math.abs(best - iv)) best = v; });
|
||||
$('#intervalSel').value = String(best);
|
||||
schedule();
|
||||
loadProjects();
|
||||
} catch (e) { showAlert(e.message); }
|
||||
}
|
||||
});
|
||||
|
||||
// 收集"各试卷独立满分" -> {projectId: score}
|
||||
|
||||
Reference in New Issue
Block a user