Router.register('/settings', async () => { const me = await API.me().catch(()=>({})); const users = (me && me.role === 'superadmin') ? await API.adminUsers().catch(()=>({list:[]})) : {list:[]}; const userList = (users.list||[]).map(u=>`
  • ${u.username}${u.role}
  • `).join('') || '
  • 暂无用户
  • '; const html = `

    👤 账户设置

    当前登录用户
    ${(me && me.username) ? me.username : '未登录'}
    头像设置
    头像预览
    支持 JPG、PNG、GIF(动图)、WEBP 等图片格式
    最大 5MB,建议尺寸 200x200 像素
    ${(me && me.role === 'superadmin') ? `

    👥 用户管理

    ➕ 添加新用户
    🔑 修改用户密码
    📋 用户列表
      ${userList}

    ⚙️ 系统配置

    💧 水印设置
    💡 水印用于防止数据泄露和责任追溯,建议保持启用状态

    📊 数据管理

    📈 数据概览
    良/不良统计:加载中
    不良明细:加载中
    MAC与批次:加载中
    发货记录:加载中
    设备状态:加载中
    人员信息:加载中
    质检报告:加载中
    生产时间:加载中
    🗑️ 清空上传数据
    🗑️ 清空扩展采集
    ` : ''}
    `; setTimeout(() => { // 头像文件选择预览 const avatarFileInput = document.getElementById('avatar-file'); const previewAvatar = document.getElementById('preview-avatar'); avatarFileInput?.addEventListener('change', (e) => { const file = e.target.files?.[0]; if (file) { // 验证文件类型 if (!file.type.startsWith('image/')) { API.toast('请选择图片文件'); avatarFileInput.value = ''; return; } // 验证文件大小(限制5MB) if (file.size > 5 * 1024 * 1024) { API.toast('图片大小不能超过5MB'); avatarFileInput.value = ''; return; } // 预览图片 const reader = new FileReader(); reader.onload = (e) => { previewAvatar.src = e.target.result; }; reader.readAsDataURL(file); } }); // 上传头像 const uploadAvatarBtn = document.getElementById('upload-avatar-btn'); uploadAvatarBtn?.addEventListener('click', async () => { const file = avatarFileInput?.files?.[0]; if (!file) { return API.toast('请先选择头像图片'); } uploadAvatarBtn.disabled = true; try { const formData = new FormData(); formData.append('avatar', file); const res = await fetch('/api/user/upload-avatar', { method: 'POST', body: formData, credentials: 'include' }); const data = await res.json(); if (res.ok && data.ok) { API.toast('头像上传成功'); // 更新顶部用户头像显示 const avatarImg = document.getElementById('user-avatar-img'); if (avatarImg && data.avatar_url) { avatarImg.src = data.avatar_url; } // 如果有全局更新函数,调用它 if (window.updateUserDisplay) { const updatedUser = await API.me().catch(() => null); window.updateUserDisplay(updatedUser); } // 清空文件选择 avatarFileInput.value = ''; } else { API.toast(data.error || '上传失败'); } } catch(e) { API.toast('上传失败:' + e.message); } finally { uploadAvatarBtn.disabled = false; } }); // 恢复默认头像 const resetAvatarBtn = document.getElementById('reset-avatar-btn'); resetAvatarBtn?.addEventListener('click', async () => { resetAvatarBtn.disabled = true; try { const res = await fetch('/api/user/reset-avatar', { method: 'POST', credentials: 'include' }); const data = await res.json(); if (res.ok && data.ok) { API.toast('已恢复默认头像'); previewAvatar.src = './assets/user-avatar.svg'; const avatarImg = document.getElementById('user-avatar-img'); if (avatarImg) { avatarImg.src = './assets/user-avatar.svg'; } if (avatarFileInput) avatarFileInput.value = ''; // 如果有全局更新函数,调用它 if (window.updateUserDisplay) { const updatedUser = await API.me().catch(() => null); window.updateUserDisplay(updatedUser); } } else { API.toast(data.error || '操作失败'); } } catch(e) { API.toast('操作失败:' + e.message); } finally { resetAvatarBtn.disabled = false; } }); // 添加新用户 const addUserBtn = document.getElementById('add-user-btn'); addUserBtn?.addEventListener('click', async () => { const usernameEl = document.getElementById('new-username'); const passwordEl = document.getElementById('new-password'); const roleEl = document.getElementById('new-role'); const username = usernameEl?.value?.trim(); const password = passwordEl?.value; const role = roleEl?.value || 'admin'; if (!username || !password) { return API.toast('请输入用户名和密码'); } if (password.length < 6) { return API.toast('密码长度至少6位'); } addUserBtn.disabled = true; try { const res = await fetch('/api/admin/add-user', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ username, password, role }) }); const data = await res.json(); if (res.ok && data.ok) { API.toast(data.message || '用户创建成功'); // 清空输入框 if (usernameEl) usernameEl.value = ''; if (passwordEl) passwordEl.value = ''; // 刷新页面以更新用户列表 setTimeout(() => Router.navigate('/settings'), 1000); } else { API.toast(data.error || '创建失败'); } } catch(e) { API.toast('创建失败:' + e.message); } finally { addUserBtn.disabled = false; } }); // 修改密码 const change = document.getElementById('change-btn'); change?.addEventListener('click', async () => { const uEl = document.getElementById('reset-user'); const pEl = document.getElementById('reset-pass'); const u = uEl ? uEl.value : ''; const p = pEl ? pEl.value : ''; if (!u || !p) return API.toast('请输入用户与新密码'); change.disabled = true; try { await API.changePassword(u, p); API.toast('已修改'); } catch(e) { API.toast('修改失败'); } finally { change.disabled = false; } }); // 水印开关 const watermarkToggle = document.getElementById('watermark-toggle'); watermarkToggle?.addEventListener('change', (e) => { const enabled = e.target.checked; if (window.toggleWatermark) { window.toggleWatermark(enabled); API.toast(enabled ? '水印已启用' : '水印已关闭'); } }); // 删除用户 document.querySelectorAll('button[data-delete-user]')?.forEach(btn => { btn.addEventListener('click', async () => { const username = btn.getAttribute('data-delete-user'); if (!confirm(`确定要删除用户 "${username}" 吗?此操作不可恢复。`)) { return; } btn.disabled = true; try { await API.deleteUser(username); API.toast('用户已删除'); // 刷新页面以更新用户列表 setTimeout(() => Router.navigate('/settings'), 1000); } catch(e) { API.toast('删除失败'); } finally { btn.disabled = false; } }); }); document.querySelectorAll('button[data-clear]')?.forEach(btn => { btn.addEventListener('click', async () => { const mod = btn.getAttribute('data-clear'); btn.disabled = true; try{ await API.clearModule(mod); API.toast('已清空'); }catch(e){ API.toast('清空失败'); }finally{ btn.disabled = false; } }); }); (async ()=>{ const ov = await API.overview().catch(()=>null); const setText=(id, text)=>{const el=document.getElementById(id); if(el) el.textContent=text;} if (ov) { setText('overview-stats', `良/不良统计:${ov.stats.records} 条(良 ${ov.stats.goodTotal} / 不良 ${ov.stats.badTotal})`); setText('overview-defects', `不良明细:${ov.defects} 条`); setText('overview-mac', `MAC与批次:${ov.mac} 条`); setText('overview-shipments', `发货记录:${ov.shipments.records} 条(总量 ${ov.shipments.qtyTotal})`); setText('overview-devices', `设备状态:${ov.devices} 条`); setText('overview-personnel', `人员信息:${ov.personnel} 条`); setText('overview-qa', `质检报告:${ov.qa} 条`); setText('overview-production', `生产时间:${ov.production} 条`); } })(); }, 0); return html; });