// ============================================ // 0. 生成背景粒子 // ============================================ (function initParticles() { var container = document.getElementById('bgParticles'); if (!container) return; for (var i = 0; i < 25; i++) { var p = document.createElement('div'); p.className = 'particle'; var size = 4 + Math.random() * 14; p.style.width = size + 'px'; p.style.height = size + 'px'; p.style.left = Math.random() * 100 + '%'; p.style.top = Math.random() * 100 + '%'; p.style.animationDelay = (Math.random() * 20) + 's'; p.style.animationDuration = (15 + Math.random() * 20) + 's'; container.appendChild(p); } })(); // ============================================ // 1. 轮播功能(纯图片版) // ============================================ (function initCarousel() { var slider = document.querySelector('.hero-image-slider'); if (!slider) return; var track = document.getElementById('heroTrack'); var dots = document.querySelectorAll('.hero-image-dots .dot'); if (!track || !dots.length) return; var currentIndex = 0; var total = dots.length; var intervalId = null; function goToSlide(index) { if (index < 0) index = total - 1; if (index >= total) index = 0; currentIndex = index; track.style.transform = 'translateX(-' + (currentIndex * 100 / total) + '%)'; dots.forEach(function(dot, i) { dot.classList.toggle('active', i === currentIndex); }); } function nextSlide() { goToSlide(currentIndex + 1); } function startAutoPlay() { if (intervalId) clearInterval(intervalId); intervalId = setInterval(nextSlide, 5000); } function stopAutoPlay() { if (intervalId) { clearInterval(intervalId); intervalId = null; } } // 点击圆点切换 dots.forEach(function(dot, index) { dot.addEventListener('click', function() { stopAutoPlay(); goToSlide(index); startAutoPlay(); }); }); // 鼠标悬停暂停 slider.addEventListener('mouseenter', stopAutoPlay); slider.addEventListener('mouseleave', startAutoPlay); // 初始化 goToSlide(0); startAutoPlay(); })(); // ============================================ // 2. 导航栏滚动变色 // ============================================ (function initNavbar() { var navbar = document.getElementById('navbar'); if (!navbar) return; window.addEventListener('scroll', function() { navbar.classList.toggle('scrolled', window.scrollY > 60); }); })(); // ============================================ // 3. 数字滚动 // ============================================ (function initStats() { var statCards = document.querySelectorAll('.stat-card'); if (!statCards.length) return; var numberEls = document.querySelectorAll('.stat-card .number'); var targets = []; var counted = false; statCards.forEach(function(card, index) { var count = parseInt(card.getAttribute('data-count'), 10); targets[index] = count; }); function animateNumbers() { if (counted) return; counted = true; numberEls.forEach(function(el, idx) { var target = targets[idx]; var current = 0; var steps = 50; var increment = Math.ceil(target / steps); var timer = setInterval(function() { current += increment; if (current >= target) { current = target; clearInterval(timer); } if (idx === 3) { el.textContent = current + '%'; } else { el.textContent = current; } }, 30); }); } var statsSection = document.querySelector('.stats-section'); if (statsSection) { var observer = new IntersectionObserver(function(entries) { entries.forEach(function(entry) { if (entry.isIntersecting) { animateNumbers(); observer.unobserve(entry.target); } }); }, { threshold: 0.3 }); observer.observe(statsSection); } })(); // ============================================ // 4. 卡片入场动画 // ============================================ (function initFadeUp() { var elements = document.querySelectorAll( '.ability-card, .group-card, .footer-stat-item, .partners-section' ); if (!elements.length) return; var observer = new IntersectionObserver(function(entries) { entries.forEach(function(entry) { if (entry.isIntersecting) { entry.target.classList.add('visible'); } }); }, { threshold: 0.1 }); elements.forEach(function(el) { el.classList.add('fade-up'); observer.observe(el); }); })(); // ============================================ // 5. 全局登录态导航切换(核心修复) // ============================================ (function initAuth() { // ===== 检测 URL 中的 logout=1 参数,跨页清除登录态 ===== (function checkLogoutParam() { try { var params = new URLSearchParams(window.location.search); if (params.has('logout')) { localStorage.removeItem('MedEvidenceBench_user'); var cleanUrl = window.location.pathname + window.location.hash; window.history.replaceState({}, document.title, cleanUrl); console.log('已通过 URL 参数清除登录态'); } } catch (e) { // 忽略解析错误 } })(); // 获取登录状态 function getLoginStatus() { return localStorage.getItem('MedEvidenceBench_user') === 'true'; } // 获取当前页面文件名 function getCurrentPage() { var path = window.location.pathname; var file = path.split('/').pop(); return file || 'index.html'; } // ===== 核心函数:统一渲染导航栏 ===== function renderNavAuth() { var isLogin = getLoginStatus(); var loggedOutEls = document.querySelectorAll('.nav-auth.logged-out'); var loggedInEls = document.querySelectorAll('.nav-auth.logged-in'); var profile = null; try { profile = JSON.parse(localStorage.getItem('MedEvidenceBench_profile') || 'null'); } catch (e) { profile = null; } var displayName = profile && profile.username ? profile.username : '我的'; if (isLogin) { loggedOutEls.forEach(function(el) { el.style.display = 'none'; }); loggedInEls.forEach(function(el) { el.style.display = ''; var toggle = el.querySelector('.dropdown-toggle'); if (toggle) { toggle.innerHTML = ' '; var name = toggle.querySelector('.nav-user-name'); name.textContent = displayName; name.title = displayName; } }); } else { loggedOutEls.forEach(function(el) { el.style.display = 'flex'; }); loggedInEls.forEach(function(el) { el.style.display = 'none'; }); } console.log('renderNavAuth 执行, 登录状态:', isLogin, '当前页面:', getCurrentPage()); } // ===== 立即执行一次 ===== renderNavAuth(); // ===== 事件监听 ===== function syncServerAuth() { // Flask session is the source of truth; localStorage only keeps the // existing static-page navigation responsive while the request runs. fetch('/api/me', {credentials: 'same-origin'}) .then(function(response) { return response.ok ? response.json() : null; }) .then(function(result) { if (!result) return; if (result.authenticated) { localStorage.setItem('MedEvidenceBench_user', 'true'); if (result.user) localStorage.setItem('MedEvidenceBench_profile', JSON.stringify(result.user)); } else { localStorage.removeItem('MedEvidenceBench_user'); localStorage.removeItem('MedEvidenceBench_profile'); } renderNavAuth(); }) .catch(function() {}); } document.addEventListener('DOMContentLoaded', function() { renderNavAuth(); syncServerAuth(); }); window.addEventListener('pageshow', renderNavAuth); // ===== 登录按钮 ===== var loginBtn = document.getElementById('loginBtn'); if (loginBtn) { loginBtn.addEventListener('click', function(e) { e.preventDefault(); var isInPages = window.location.pathname.includes('/pages/'); var redirect = window.location.pathname; var loginUrl = isInPages ? 'login.html?redirect=' + encodeURIComponent(redirect) : 'pages/login.html?redirect=' + encodeURIComponent(redirect); window.location.href = loginUrl; }); } // ===== 注册按钮 ===== var registerBtn = document.getElementById('registerBtn'); if (registerBtn) { registerBtn.addEventListener('click', function(e) { e.preventDefault(); var isInPages = window.location.pathname.includes('/pages/'); window.location.href = isInPages ? 'register.html' : 'pages/register.html'; }); } // ===== 下拉菜单切换 ===== var userMenuBtn = document.getElementById('userMenuBtn'); var userDropdown = document.getElementById('userDropdown'); var backdrop = document.createElement('div'); backdrop.className = 'dropdown-backdrop'; document.body.appendChild(backdrop); if (userMenuBtn && userDropdown) { userMenuBtn.addEventListener('click', function(e) { e.stopPropagation(); var isOpen = userDropdown.classList.toggle('open'); userMenuBtn.classList.toggle('open'); backdrop.style.display = isOpen ? 'block' : 'none'; }); backdrop.addEventListener('click', function() { userDropdown.classList.remove('open'); userMenuBtn.classList.remove('open'); backdrop.style.display = 'none'; }); userDropdown.addEventListener('click', function(e) { e.stopPropagation(); }); } // ============================================================ // 退出登录 - 使用事件捕获(capture: true)确保100%触发 // ============================================================ document.addEventListener('click', function(e) { // 检查点击的元素或其父级是否匹配 #logoutBtn var target = e.target.closest('#logoutBtn'); if (!target) return; // 阻止默认行为和冒泡(实际上捕获阶段冒泡还未发生) e.preventDefault(); e.stopPropagation(); // 先关闭下拉菜单和 backdrop,避免界面残留 if (userDropdown) userDropdown.classList.remove('open'); if (userMenuBtn) userMenuBtn.classList.remove('open'); if (backdrop) backdrop.style.display = 'none'; if (confirm('确定要退出登录吗?')) { localStorage.removeItem('MedEvidenceBench_user'); localStorage.removeItem('MedEvidenceBench_profile'); var isInPages = window.location.pathname.includes('/pages/'); var targetUrl = isInPages ? '../index.html' : 'index.html'; targetUrl += (targetUrl.includes('?') ? '&' : '?') + 'logout=1&t=' + Date.now(); var logoutUrl = isInPages ? '../api/logout' : 'api/logout'; fetch(logoutUrl, {method: 'POST', credentials: 'same-origin'}) .catch(function() {}) .finally(function() { window.location.href = targetUrl; }); } }, true); // capture: true 确保在捕获阶段处理,不被冒泡阶段干扰 // ===== 页面级登录保护 ===== var PROTECTED_PAGES = ['submit.html', 'my-evaluations.html']; function checkPageAuth() { var currentPage = getCurrentPage(); if (PROTECTED_PAGES.indexOf(currentPage) !== -1) { var isLogin = getLoginStatus(); if (!isLogin) { var isInPages = window.location.pathname.includes('/pages/'); var loginUrl = isInPages ? 'login.html' : 'pages/login.html'; window.location.href = loginUrl; return false; } } return true; } document.addEventListener('DOMContentLoaded', function() { checkPageAuth(); }); // ============================================================ // 新增:强制解除所有遮罩层的鼠标拦截,确保退出按钮可点击 // ============================================================ (function removeOverlayPointerEvents() { // 选择所有可能遮挡的层 var overlays = document.querySelectorAll('.slide-overlay, .dropdown-backdrop, .carousel-section .slide-overlay'); overlays.forEach(function(el) { el.style.pointerEvents = 'none'; }); // 特别确保 backdrop 不拦截点击 var backdropEl = document.querySelector('.dropdown-backdrop'); if (backdropEl) { backdropEl.style.pointerEvents = 'none'; } console.log('已强制解除遮罩层鼠标拦截'); })(); // 暴露全局接口 window.medEvidenceBenchAuth = { login: function() { localStorage.setItem('MedEvidenceBench_user', 'true'); renderNavAuth(); }, logout: function() { localStorage.removeItem('MedEvidenceBench_user'); renderNavAuth(); window.location.reload(); }, isLoggedIn: getLoginStatus, renderNavAuth: renderNavAuth, checkPageAuth: checkPageAuth }; })(); // ============================================ // 6. 轮播按钮 // ============================================ window.handleSlideClick = function(action) { alert('您点击了“' + action + '”,此处可跳转至对应页面。'); }; // ============================================ // 7. 导航链接拦截 // ============================================ document.addEventListener('DOMContentLoaded', function() { document.querySelectorAll('.nav-links a').forEach(function(link) { link.addEventListener('click', function(e) { var href = this.getAttribute('href'); if (href && (href.includes('.html') || href.includes('?redirect=') || href === '#')) { return; } e.preventDefault(); alert('导航:' + this.textContent.trim() + ' 功能开发中'); }); }); document.querySelectorAll('.footer-links a').forEach(function(link) { link.addEventListener('click', function(e) { var href = this.getAttribute('href'); if (href && href.startsWith('mailto:')) return; if (href && (href.includes('.html') || href.includes('?redirect='))) { return; } e.preventDefault(); alert('页脚:' + this.textContent.trim() + ' 功能开发中'); }); }); // 自动高亮当前导航 (function highlightNav() { var currentPath = window.location.pathname; var currentFile = currentPath.split('/').pop(); if (!currentFile || currentFile === '') { currentFile = 'index.html'; } document.querySelectorAll('.nav-links a').forEach(function(link) { var href = link.getAttribute('href'); if (!href) return; var linkFile = href.split('?')[0].split('/').pop(); if (linkFile === currentFile) { link.classList.add('active'); } else { link.classList.remove('active'); } }); })(); }); // ============================================ // 8. 邀请共建“加入我们”按钮弹窗 // ============================================ document.addEventListener('DOMContentLoaded', function() { var inviteBtn = document.querySelector('.invite-btn'); if (inviteBtn) { inviteBtn.addEventListener('click', function(e) { e.preventDefault(); alert('请将您的简历发送至邮箱:MedEvidenceBench@MedEvidenceBase.cn,我们会在收到后尽快与您联系。'); }); } }); // ============================================ // 平台特点 - 堆叠卡片,点击居中(旋转数组,平滑过渡) // ============================================ document.addEventListener('DOMContentLoaded', function() { var wrapper = document.querySelector('.feature-overlap-wrapper'); if (!wrapper) return; var cards = wrapper.querySelectorAll('.feature-overlap-item'); if (!cards.length) return; var total = cards.length; var centerIndex = 2; // 中间位置索引(0,1,2,3,4) var cardWidth = 280; var overlap = 50; var step = cardWidth - overlap; // 当前顺序(卡片索引数组) var order = [0, 1, 2, 3, 4]; // 是否正在动画中 var isAnimating = false; function updateCards() { cards.forEach(function(card, i) { var pos = order.indexOf(i); if (pos === -1) return; var offset = pos - centerIndex; var translateX = offset * step; var scale = 1 - Math.abs(offset) * 0.06; scale = Math.max(scale, 0.78); var opacity = 1 - Math.abs(offset) * 0.14; opacity = Math.max(opacity, 0.45); var zIndex = total - Math.abs(offset); card.style.transform = 'translateX(' + translateX + 'px) scale(' + scale + ')'; card.style.opacity = opacity; card.style.zIndex = zIndex; if (offset === 0) { card.style.borderColor = '#0d9488'; card.style.boxShadow = '0 16px 48px rgba(13,148,136,0.2)'; card.classList.add('active'); card.classList.remove('dimmed'); var title = card.querySelector('h3'); if (title) title.style.color = '#0d9488'; } else { card.style.borderColor = '#e9edf2'; card.style.boxShadow = '0 4px 20px rgba(0,0,0,0.04)'; card.classList.remove('active'); card.classList.add('dimmed'); var title = card.querySelector('h3'); if (title) title.style.color = '#0f172a'; } }); } // 旋转数组,使 targetIndex 位于 centerIndex function rotateOrder(targetIndex) { var pos = order.indexOf(targetIndex); if (pos === -1) return; // 计算需要旋转的步数(向左或向右旋转最少步数) var steps = pos - centerIndex; var len = order.length; // 取模 steps = ((steps % len) + len) % len; // 如果步数大于一半,可以反方向旋转更少 if (steps > len / 2) steps = steps - len; // 执行旋转(左移 steps 步) if (steps > 0) { // 向左旋转 steps 步 var rotated = order.slice(steps).concat(order.slice(0, steps)); order = rotated; } else if (steps < 0) { // 向右旋转 -steps 步 var r = -steps; var rotated2 = order.slice(-r).concat(order.slice(0, -r)); order = rotated2; } // steps === 0 则不变 } // 点击卡片 cards.forEach(function(card, index) { card.addEventListener('click', function() { if (isAnimating) return; var pos = order.indexOf(index); if (pos === centerIndex) { // 微反馈 this.style.transform = this.style.transform + ' scale(1.02)'; setTimeout(function() { updateCards(); }, 200); return; } isAnimating = true; rotateOrder(index); updateCards(); // 滚动到视口中间 var headerHeight = document.querySelector('.navbar')?.offsetHeight || 80; var wrapperRect = wrapper.getBoundingClientRect(); var targetScroll = wrapperRect.top + window.pageYOffset - headerHeight - 60; window.scrollTo({ top: targetScroll, behavior: 'smooth' }); setTimeout(function() { isAnimating = false; }, 500); }); }); // 初始化 updateCards(); // 窗口大小变化处理 var resizeTimer; window.addEventListener('resize', function() { clearTimeout(resizeTimer); resizeTimer = setTimeout(function() { if (window.innerWidth < 1024) { cards.forEach(function(card) { card.style.transform = ''; card.style.opacity = ''; card.style.zIndex = ''; card.style.borderColor = ''; card.style.boxShadow = ''; card.classList.remove('active', 'dimmed'); var title = card.querySelector('h3'); if (title) title.style.color = '#0f172a'; }); } else { // 重新计算但保持顺序不变 updateCards(); } }, 200); }); if (window.innerWidth >= 1024) { setTimeout(updateCards, 100); } }); // ============================================ // 6大分组体系 - 侧边栏点击切换 // ============================================ document.addEventListener('DOMContentLoaded', function() { var sidebarItems = document.querySelectorAll('.group-sidebar-item'); var panels = document.querySelectorAll('.group-content-panel'); if (!sidebarItems.length || !panels.length) return; sidebarItems.forEach(function(item) { item.addEventListener('click', function() { var target = this.dataset.group; if (!target) return; // 切换左侧激活状态 sidebarItems.forEach(function(si) { si.classList.remove('active'); }); this.classList.add('active'); // 切换右侧内容 panels.forEach(function(panel) { var panelTarget = panel.dataset.panel; panel.classList.toggle('active', panelTarget === target); }); }); }); }); console.log('medEvidenceBench 已加载 (v12 - 强制解除遮罩层拦截)');