Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 
 

220 řádky
9.0 KiB

  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.0">
  6. <title>登录 - 北京维康慈善基金会管理系统</title>
  7. <style>
  8. *,*::before,*::after{margin:0;padding:0;box-sizing:border-box;}
  9. :root{
  10. --primary:#E8751A;
  11. --primary-dark:#C96012;
  12. --primary-light:#F5A623;
  13. --blue:#1A3550;
  14. --white:#fff;
  15. --bg:#f0f2f5;
  16. --text:#303133;
  17. --text-secondary:#909399;
  18. --border:#dcdfe6;
  19. --danger:#f56c6c;
  20. --font-cn:"Source Han Sans SC","Noto Sans SC","PingFang SC","Microsoft YaHei",sans-serif;
  21. --font-en:"Roboto",sans-serif;
  22. }
  23. html,body{height:100%;font-family:var(--font-cn);color:var(--text);}
  24. .login-wrapper{display:flex;height:100vh;}
  25. /* 左侧品牌区 */
  26. .login-left{
  27. flex:0 0 50%;display:flex;flex-direction:column;align-items:center;justify-content:center;
  28. background:linear-gradient(135deg,var(--primary) 0%,var(--primary-dark) 100%);
  29. position:relative;overflow:hidden;
  30. }
  31. .login-left::before{
  32. content:"";position:absolute;width:600px;height:600px;border-radius:50%;
  33. background:rgba(255,255,255,.06);top:-120px;right:-180px;
  34. }
  35. .login-left::after{
  36. content:"";position:absolute;width:400px;height:400px;border-radius:50%;
  37. background:rgba(255,255,255,.04);bottom:-80px;left:-100px;
  38. }
  39. .brand{position:relative;z-index:1;text-align:center;color:var(--white);padding:0 60px;}
  40. .brand-logo{width:80px;height:80px;background:rgba(255,255,255,.15);border-radius:20px;
  41. display:flex;align-items:center;justify-content:center;margin:0 auto 28px;backdrop-filter:blur(10px);}
  42. .brand-logo svg{width:44px;height:44px;}
  43. .brand h1{font-size:28px;font-weight:700;margin-bottom:12px;letter-spacing:1px;}
  44. .brand p{font-size:14px;opacity:.75;line-height:1.8;max-width:360px;}
  45. /* 右侧表单区 */
  46. .login-right{flex:1;display:flex;align-items:center;justify-content:center;background:var(--bg);}
  47. .login-box{
  48. width:400px;background:var(--white);border-radius:12px;
  49. box-shadow:0 2px 12px rgba(0,0,0,.08);padding:48px 40px;
  50. }
  51. .login-box h2{font-size:22px;font-weight:600;color:var(--text);margin-bottom:6px;}
  52. .login-box .subtitle{font-size:13px;color:var(--text-secondary);margin-bottom:32px;}
  53. /* 表单 */
  54. .form-item{margin-bottom:22px;}
  55. .form-item label{display:block;font-size:13px;color:var(--text);font-weight:500;margin-bottom:8px;}
  56. .input-wrap{
  57. position:relative;display:flex;align-items:center;
  58. border:1px solid var(--border);border-radius:8px;transition:.2s;background:var(--white);
  59. }
  60. .input-wrap:focus-within{border-color:var(--primary);box-shadow:0 0 0 3px rgba(232,117,26,.1);}
  61. .input-wrap .icon{position:absolute;left:12px;color:var(--text-secondary);display:flex;align-items:center;}
  62. .input-wrap .icon svg{width:18px;height:18px;}
  63. .input-wrap input{
  64. width:100%;border:none;outline:none;padding:11px 12px 11px 40px;
  65. font-size:14px;font-family:var(--font-cn);color:var(--text);background:transparent;border-radius:8px;
  66. }
  67. .input-wrap input::placeholder{color:#c0c4cc;}
  68. .input-wrap .toggle-pwd{
  69. position:absolute;right:12px;cursor:pointer;color:var(--text-secondary);
  70. display:flex;align-items:center;background:none;border:none;padding:0;
  71. }
  72. .input-wrap .toggle-pwd:hover{color:var(--primary);}
  73. .input-wrap .toggle-pwd svg{width:18px;height:18px;}
  74. .form-options{display:flex;align-items:center;justify-content:space-between;margin-bottom:28px;}
  75. .remember{display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;color:var(--text-secondary);}
  76. .remember input[type="checkbox"]{width:16px;height:16px;accent-color:var(--primary);cursor:pointer;}
  77. .btn-login{
  78. width:100%;padding:12px 0;border:none;border-radius:8px;
  79. background:linear-gradient(135deg,var(--primary),var(--primary-dark));
  80. color:var(--white);font-size:15px;font-weight:600;cursor:pointer;
  81. transition:.25s;letter-spacing:1px;font-family:var(--font-cn);
  82. }
  83. .btn-login:hover{transform:translateY(-1px);box-shadow:0 6px 20px rgba(232,117,26,.35);}
  84. .btn-login:active{transform:translateY(0);}
  85. .btn-login:disabled{opacity:.6;cursor:not-allowed;transform:none;box-shadow:none;}
  86. .login-footer{text-align:center;margin-top:24px;font-size:12px;color:var(--text-secondary);}
  87. .error-msg{color:var(--danger);font-size:13px;margin-bottom:16px;display:none;}
  88. .error-msg.show{display:block;}
  89. @media(max-width:960px){
  90. .login-left{display:none;}
  91. .login-right{background:linear-gradient(135deg,#fdf0e6 0%,var(--bg) 100%);}
  92. }
  93. @media(max-width:480px){
  94. .login-box{width:100%;margin:0 20px;padding:36px 28px;}
  95. }
  96. </style>
  97. </head>
  98. <body>
  99. <div class="login-wrapper">
  100. <div class="login-left">
  101. <div class="brand">
  102. <div class="brand-logo">
  103. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
  104. <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>
  105. </svg>
  106. </div>
  107. <h1>北京维康慈善基金会</h1>
  108. <p>官网内容管理系统<br>致力于妇幼健康、患者关爱、卫生健康促进等公益事业</p>
  109. </div>
  110. </div>
  111. <div class="login-right">
  112. <div class="login-box">
  113. <h2>欢迎回来</h2>
  114. <p class="subtitle">请登录您的管理员账号</p>
  115. <form id="loginForm">
  116. <div class="error-msg" id="errorMsg"></div>
  117. <div class="form-item">
  118. <label>用户名</label>
  119. <div class="input-wrap">
  120. <span class="icon">
  121. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
  122. </span>
  123. <input type="text" name="username" id="username" placeholder="请输入用户名" autocomplete="username" required>
  124. </div>
  125. </div>
  126. <div class="form-item">
  127. <label>密码</label>
  128. <div class="input-wrap">
  129. <span class="icon">
  130. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
  131. </span>
  132. <input type="password" name="password" id="password" placeholder="请输入密码" autocomplete="current-password" required>
  133. <button type="button" class="toggle-pwd" onclick="togglePassword()" aria-label="显示密码">
  134. <svg id="eyeIcon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
  135. </button>
  136. </div>
  137. </div>
  138. <div class="form-options">
  139. <label class="remember">
  140. <input type="checkbox" name="remember" id="remember"> 记住我
  141. </label>
  142. </div>
  143. <button type="submit" class="btn-login" id="submitBtn">登 录</button>
  144. </form>
  145. <div class="login-footer">
  146. © <span id="currentYear"></span> 北京维康慈善基金会 · 内容管理系统
  147. </div>
  148. </div>
  149. </div>
  150. </div>
  151. <script>
  152. document.getElementById('currentYear').textContent = new Date().getFullYear();
  153. function togglePassword(){
  154. const input = document.getElementById('password');
  155. input.type = input.type === 'password' ? 'text' : 'password';
  156. }
  157. document.getElementById('loginForm').addEventListener('submit', async function(e) {
  158. e.preventDefault();
  159. const btn = document.getElementById('submitBtn');
  160. const errorMsg = document.getElementById('errorMsg');
  161. const username = document.getElementById('username').value.trim();
  162. const password = document.getElementById('password').value;
  163. const remember = document.getElementById('remember').checked;
  164. if (!username || !password) {
  165. errorMsg.textContent = '请输入用户名和密码';
  166. errorMsg.classList.add('show');
  167. return;
  168. }
  169. btn.disabled = true;
  170. btn.textContent = '登录中...';
  171. errorMsg.classList.remove('show');
  172. try {
  173. const res = await fetch('/admin/auth/login', {
  174. method: 'POST',
  175. headers: { 'Content-Type': 'application/json' },
  176. body: JSON.stringify({ username, password, remember })
  177. });
  178. const data = await res.json();
  179. if (data.code === 0) {
  180. window.location.href = '/admin/dashboard.html';
  181. } else {
  182. errorMsg.textContent = data.msg || '登录失败';
  183. errorMsg.classList.add('show');
  184. }
  185. } catch (err) {
  186. errorMsg.textContent = '网络错误,请稍后重试';
  187. errorMsg.classList.add('show');
  188. } finally {
  189. btn.disabled = false;
  190. btn.textContent = '登 录';
  191. }
  192. });
  193. </script>
  194. </body>
  195. </html>