login.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. // 登录页面JavaScript逻辑
  2. class LoginManager {
  3. constructor() {
  4. this.init();
  5. }
  6. init () {
  7. // 检查登录状态
  8. this.checkLoginStatus();
  9. // 绑定事件
  10. this.bindEvents();
  11. }
  12. bindEvents () {
  13. // 钉钉登录按钮点击事件
  14. const dingtalkBtn = document.getElementById('dingtalk-login');
  15. if (dingtalkBtn) {
  16. dingtalkBtn.addEventListener('click', () => {
  17. this.loginWithDingtalkOAuth();
  18. });
  19. }
  20. }
  21. async checkLoginStatus () {
  22. try {
  23. const response = await fetch('/api/auth/ping');
  24. const data = await response.json();
  25. if (data.authenticated) {
  26. // 已登录,跳转到主页
  27. window.location.href = '/';
  28. }
  29. } catch (error) {
  30. console.error('检查登录状态失败:', error);
  31. }
  32. }
  33. async loginWithDingtalkOAuth () {
  34. try {
  35. this.showLoginStatus();
  36. const urlResp = await fetch('/api/auth/dingtalk/login_url');
  37. const urlData = await urlResp.json();
  38. if (!urlResp.ok || !urlData.success) {
  39. this.hideLoginStatus();
  40. if (urlData.error === 'DingTalk未配置') {
  41. this.showError('钉钉登录功能暂未启用,请联系管理员配置钉钉OAuth');
  42. } else {
  43. this.showError(urlData.error || '钉钉登录失败,请重试');
  44. }
  45. return;
  46. }
  47. window.location.href = urlData.url;
  48. this.hideLoginStatus();
  49. } catch (error) {
  50. console.error('钉钉登录失败:', error);
  51. this.showError(error.message || '登录失败,请重试');
  52. this.hideLoginStatus();
  53. }
  54. }
  55. showError (message) {
  56. const existingError = document.querySelector('.error-message');
  57. if (existingError) {
  58. existingError.remove();
  59. }
  60. const errorDiv = document.createElement('div');
  61. errorDiv.className = 'error-message';
  62. errorDiv.textContent = message;
  63. const loginMethods = document.querySelector('.login-methods');
  64. if (loginMethods) {
  65. loginMethods.appendChild(errorDiv);
  66. }
  67. // 3秒后自动移除错误信息
  68. setTimeout(() => {
  69. if (errorDiv.parentNode) {
  70. errorDiv.parentNode.removeChild(errorDiv);
  71. }
  72. }, 3000);
  73. }
  74. showLoginStatus () {
  75. const status = document.getElementById('login-status');
  76. if (status) {
  77. status.style.display = 'flex';
  78. }
  79. }
  80. hideLoginStatus () {
  81. const status = document.getElementById('login-status');
  82. if (status) {
  83. status.style.display = 'none';
  84. }
  85. }
  86. }
  87. // 页面加载完成后初始化
  88. document.addEventListener('DOMContentLoaded', () => {
  89. // 初始化登录管理器
  90. window.loginManager = new LoginManager();
  91. // 添加视觉优化效果
  92. addVisualEffects();
  93. });
  94. // 视觉优化效果函数
  95. function addVisualEffects () {
  96. // 页面加载动画
  97. animatePageLoad();
  98. // 添加鼠标移动视差效果
  99. addParallaxEffect();
  100. // 添加按钮点击效果
  101. addButtonEffects();
  102. // 添加卡片悬停效果
  103. addCardHoverEffects();
  104. }
  105. // 页面加载动画
  106. function animatePageLoad () {
  107. const elements = document.querySelectorAll('.login-card, .floating-shapes .shape');
  108. elements.forEach((element, index) => {
  109. element.style.opacity = '0';
  110. element.style.transform = 'translateY(30px)';
  111. setTimeout(() => {
  112. element.style.transition = 'all 0.8s cubic-bezier(0.4, 0, 0.2, 1)';
  113. element.style.opacity = '1';
  114. element.style.transform = 'translateY(0)';
  115. }, index * 100);
  116. });
  117. }
  118. // 添加视差效果
  119. function addParallaxEffect () {
  120. const shapes = document.querySelectorAll('.shape');
  121. document.addEventListener('mousemove', (e) => {
  122. const mouseX = e.clientX / window.innerWidth;
  123. const mouseY = e.clientY / window.innerHeight;
  124. shapes.forEach((shape, index) => {
  125. const speed = (index + 1) * 0.5;
  126. const x = (mouseX - 0.5) * speed * 20;
  127. const y = (mouseY - 0.5) * speed * 20;
  128. shape.style.transform = `translate(${x}px, ${y}px)`;
  129. });
  130. });
  131. }
  132. // 添加按钮效果
  133. function addButtonEffects () {
  134. const buttons = document.querySelectorAll('.login-btn');
  135. buttons.forEach(button => {
  136. button.addEventListener('mousedown', function () {
  137. this.style.transform = 'scale(0.95)';
  138. });
  139. button.addEventListener('mouseup', function () {
  140. this.style.transform = 'scale(1)';
  141. });
  142. button.addEventListener('mouseleave', function () {
  143. this.style.transform = 'scale(1)';
  144. });
  145. });
  146. }
  147. // 添加卡片悬停效果
  148. function addCardHoverEffects () {
  149. const card = document.querySelector('.login-card');
  150. const method = document.querySelector('.login-method');
  151. if (card) {
  152. card.addEventListener('mouseenter', function () {
  153. this.style.transform = 'scale(1.02)';
  154. });
  155. card.addEventListener('mouseleave', function () {
  156. this.style.transform = 'scale(1)';
  157. });
  158. }
  159. if (method) {
  160. method.addEventListener('mouseenter', function () {
  161. this.style.transform = 'translateY(-4px) scale(1.02)';
  162. });
  163. method.addEventListener('mouseleave', function () {
  164. this.style.transform = 'translateY(0) scale(1)';
  165. });
  166. }
  167. }
  168. // 添加触摸设备支持
  169. if ('ontouchstart' in window) {
  170. document.body.classList.add('touch-device');
  171. // 为触摸设备优化按钮
  172. const buttons = document.querySelectorAll('.login-btn');
  173. buttons.forEach(button => {
  174. button.addEventListener('touchstart', function () {
  175. this.style.transform = 'scale(0.95)';
  176. });
  177. button.addEventListener('touchend', function () {
  178. this.style.transform = 'scale(1)';
  179. });
  180. });
  181. }
  182. // 性能优化:节流函数
  183. function throttle (func, limit) {
  184. let inThrottle;
  185. return function () {
  186. const args = arguments;
  187. const context = this;
  188. if (!inThrottle) {
  189. func.apply(context, args);
  190. inThrottle = true;
  191. setTimeout(() => inThrottle = false, limit);
  192. }
  193. }
  194. }
  195. // 优化视差效果性能
  196. const optimizedParallax = throttle(addParallaxEffect, 16);
  197. document.addEventListener('mousemove', optimizedParallax);