管理页新增:员工删除功能 + 直接选择 CSV 文件导入(不用复制粘贴)
- server.js 新增 POST /api/admin/delete:移除本地账号并逐平台 force_logout - admin.html 员工行新增删除按钮(带确认);导入区新增选择文件导入,自动去 BOM
This commit is contained in:
@@ -39,6 +39,7 @@ POST /api/login
|
||||
| POST | `/api/admin/disable` | 停用 `{staffNo}`,立即无法登录并被踢下线 |
|
||||
| POST | `/api/admin/enable` | 启用 `{staffNo}` |
|
||||
| POST | `/api/admin/reset_password` | 重置密码 `{staffNo, password}` |
|
||||
| POST | `/api/admin/delete` | 删除 `{staffNo}`,立即无法登录并被踢下线(OpenIM 侧同名用户保留但本服务不再为其签发令牌,等于不可用) |
|
||||
|
||||
管理页:浏览器打开 `http://<服务器IP>:10010/`,输入管理口令即可操作,无需写接口。
|
||||
|
||||
|
||||
@@ -54,7 +54,9 @@
|
||||
<div class="hint">支持逗号或 Tab 分隔(Excel 复制出来直接粘贴即可);重复工号会覆盖原账号(姓名与密码重置、状态恢复为启用)。</div>
|
||||
<div class="row" style="margin-top:10px">
|
||||
<button onclick="doImport()">导入</button>
|
||||
<button class="ghost" onclick="$('fileInput').click()">选择文件导入</button>
|
||||
<button class="ghost" onclick="downloadTemplate()">下载导入模板</button>
|
||||
<input id="fileInput" type="file" accept=".csv,.txt" style="display:none" onchange="importFile(this)">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -110,6 +112,7 @@ async function loadList() {
|
||||
? `<button class="danger" onclick="toggle('${e.staffNo}', false)">停用</button>`
|
||||
: `<button class="ghost" onclick="toggle('${e.staffNo}', true)">启用</button>`}
|
||||
<button class="ghost" onclick="resetPwd('${e.staffNo}')">重置密码</button>
|
||||
<button class="danger" onclick="deleteEmp('${e.staffNo}')">删除</button>
|
||||
</td>
|
||||
</tr>`).join('') || '<tr><td colspan="5" style="color:#98a2b3">暂无员工,请先导入</td></tr>';
|
||||
} catch (e) { show(e.message, false); }
|
||||
@@ -151,6 +154,30 @@ async function doImport() {
|
||||
} catch (e) { show(e.message, false); }
|
||||
}
|
||||
|
||||
// 直接选择模板 CSV 文件导入,不用复制粘贴
|
||||
async function importFile(input) {
|
||||
const file = input.files[0];
|
||||
input.value = '';
|
||||
if (!file) return;
|
||||
try {
|
||||
let text = await file.text();
|
||||
if (text.charCodeAt(0) === 0xFEFF) text = text.slice(1); // 去 BOM
|
||||
const d = await api('/api/admin/import', { csv: text });
|
||||
const bad = d.results.filter(r => !r.ok);
|
||||
show('文件「' + file.name + '」导入完成:成功 ' + d.imported + ' / ' + d.total + (bad.length ? '\n失败明细:\n' + bad.map(b => b.line + ' → ' + b.msg).join('\n') : ''), bad.length === 0);
|
||||
loadList();
|
||||
} catch (e) { show(e.message, false); }
|
||||
}
|
||||
|
||||
async function deleteEmp(staffNo) {
|
||||
if (!confirm('确定删除 ' + staffNo + ' 吗?删除后该员工立即无法登录,需要重新添加才能恢复。')) return;
|
||||
try {
|
||||
await api('/api/admin/delete', { staffNo });
|
||||
show('已删除 ' + staffNo, true);
|
||||
loadList();
|
||||
} catch (e) { show(e.message, false); }
|
||||
}
|
||||
|
||||
// 下载 CSV 导入模板(加 BOM 让 Excel 正确识别中文)
|
||||
function downloadTemplate() {
|
||||
const csv = '\uFEFF工号,姓名,初始密码\n10001,张三,abc123456\n10002,李四,abc123456\n';
|
||||
|
||||
@@ -260,6 +260,17 @@ const routes = {
|
||||
ok(res, publicView(emp, body.staffNo));
|
||||
},
|
||||
|
||||
// 删除员工:本地账号直接移除(立即无法再登录并被踢下线)。
|
||||
// 注意:OpenIM 侧的同名用户不删(其公开 API 不提供删除),但删除后本服务不再为其签发令牌,等于不可用。
|
||||
'POST /api/admin/delete': async (req, res, body) => {
|
||||
const emp = employees[body.staffNo];
|
||||
if (!emp) return fail(res, '员工不存在');
|
||||
delete employees[body.staffNo];
|
||||
saveStore();
|
||||
forceLogoutAll(body.staffNo); // 异步踢下线,不等结果
|
||||
ok(res, { staffNo: body.staffNo, deleted: true });
|
||||
},
|
||||
|
||||
'POST /api/admin/reset_password': async (req, res, body) => {
|
||||
const emp = employees[body.staffNo];
|
||||
if (!emp) return fail(res, '员工不存在');
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
Reference in New Issue
Block a user