// ============================================
// 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 - 强制解除遮罩层拦截)');