login.html 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. <!doctype html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1">
  6. <title>登录</title>
  7. <style>
  8. body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, PingFang SC, Microsoft YaHei, sans-serif; background: #0b1020; color: #e8ecf1; }
  9. .container { min-height: 100vh; display: flex; align-items: center; justify-content: center; }
  10. .card { width: 360px; background: #121a2a; border: 1px solid #1e2a44; border-radius: 12px; padding: 28px; box-shadow: 0 10px 30px rgba(0,0,0,.35); }
  11. .title { font-size: 20px; font-weight: 600; margin-bottom: 18px; }
  12. label { font-size: 13px; color: #9fb0ca; display: block; margin: 12px 0 6px; }
  13. input { width: 100%; height: 40px; border-radius: 8px; border: 1px solid #2a3b5f; background: #0e1424; color: #e8ecf1; padding: 0 12px; outline: none; }
  14. input:focus { border-color: #3c7bff; box-shadow: 0 0 0 3px rgba(60,123,255,.2); }
  15. .btn { margin-top: 16px; width: 100%; height: 40px; border-radius: 8px; border: none; background: linear-gradient(135deg, #2a6bff, #6b9dff); color: #fff; font-weight: 600; cursor: pointer; }
  16. .btn:disabled { background: #2a3b5f; cursor: not-allowed; }
  17. .error { color: #ff6b6b; font-size: 13px; margin-top: 8px; min-height: 18px; }
  18. </style>
  19. <script>
  20. async function handleLogin(event) {
  21. event.preventDefault();
  22. const btn = document.getElementById('submitBtn');
  23. btn.disabled = true;
  24. const username = document.getElementById('username').value.trim();
  25. const password = document.getElementById('password').value;
  26. const errorEl = document.getElementById('error');
  27. errorEl.textContent = '';
  28. try {
  29. const resp = await fetch('/api/v1/auth/login', {
  30. method: 'POST',
  31. headers: { 'Content-Type': 'application/json' },
  32. body: JSON.stringify({ username, password })
  33. });
  34. if (!resp.ok) {
  35. const msg = await resp.text();
  36. throw new Error(msg || '登录失败');
  37. }
  38. const data = await resp.json();
  39. const token = data.access_token;
  40. if (!token) throw new Error('未获取到token');
  41. localStorage.setItem('access_token', token);
  42. window.location.href = '/dashboard';
  43. } catch (e) {
  44. errorEl.textContent = e.message;
  45. } finally {
  46. btn.disabled = false;
  47. }
  48. }
  49. </script>
  50. </head>
  51. <body>
  52. <div class="container">
  53. <form class="card" onsubmit="handleLogin(event)">
  54. <div class="title">系统登录</div>
  55. <label for="username">用户名</label>
  56. <input id="username" name="username" placeholder="请输入用户名" required />
  57. <label for="password">密码</label>
  58. <input id="password" name="password" type="password" placeholder="请输入密码" required />
  59. <button id="submitBtn" class="btn" type="submit">登录</button>
  60. <div id="error" class="error"></div>
  61. </form>
  62. </div>
  63. </body>
  64. </html>