Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

237 lines
8.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// main.js - 主逻辑(增强版)
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
// 初始渲染
refreshDashboard();
// 搜索功能(带防抖和动画)
const searchInput = document.getElementById('search-input');
if (searchInput) {
let searchTimer;
searchInput.addEventListener('input', function(e) {
clearTimeout(searchTimer);
// 添加输入时的微动效
this.style.transform = 'scale(1.02)';
setTimeout(() => {
this.style.transform = '';
}, 150);
searchTimer = setTimeout(() => {
applyFilters();
}, 300);
});
}
// 筛选按钮
const filterBtns = document.querySelectorAll('.filter-btn');
filterBtns.forEach(btn => {
btn.addEventListener('click', function() {
// 按钮点击动画
this.style.transform = 'scale(0.95)';
setTimeout(() => {
this.style.transform = '';
}, 150);
// 更新按钮状态
filterBtns.forEach(b => b.classList.remove('active'));
this.classList.add('active');
// 应用筛选
applyFilters();
});
});
// 卡片点击事件(使用事件委托)
document.addEventListener('click', function(e) {
const card = e.target.closest('.dev-card');
if (card) {
const devId = card.dataset.devId;
if (devId && window.showDetailPage) {
// 添加点击动画
card.style.transform = 'scale(0.98)';
card.style.opacity = '0.8';
setTimeout(() => {
card.style.transform = '';
card.style.opacity = '';
window.showDetailPage(devId);
}, 150);
}
}
});
// 键盘导航
document.addEventListener('keydown', function(e) {
// 获取所有可聚焦的卡片
const cards = Array.from(document.querySelectorAll('.dev-card'));
const currentIndex = cards.indexOf(document.activeElement);
// Tab键默认行为已支持我们只加样式
if (e.key === 'Tab') {
setTimeout(() => {
const focused = document.activeElement;
if (focused.classList.contains('dev-card')) {
focused.style.outline = '3px solid #ffd700';
focused.style.transform = 'scale(1.02)';
}
}, 50);
}
// 箭头键导航
if (e.key === 'ArrowRight' || e.key === 'ArrowLeft' ||
e.key === 'ArrowDown' || e.key === 'ArrowUp') {
e.preventDefault();
if (cards.length === 0) return;
const cols = Math.floor(document.querySelector('.developers-grid').offsetWidth / 300);
let nextIndex;
if (e.key === 'ArrowRight') {
nextIndex = currentIndex + 1;
} else if (e.key === 'ArrowLeft') {
nextIndex = currentIndex - 1;
} else if (e.key === 'ArrowDown') {
nextIndex = currentIndex + cols;
} else if (e.key === 'ArrowUp') {
nextIndex = currentIndex - cols;
}
if (nextIndex >= 0 && nextIndex < cards.length) {
cards[nextIndex].focus();
}
}
// Enter/Space 打开详情
if (e.key === 'Enter' || e.key === ' ') {
const target = e.target.closest('.dev-card');
if (target) {
e.preventDefault();
const devId = target.dataset.devId;
if (devId && window.showDetailPage) {
window.showDetailPage(devId);
}
}
}
// ESC返回总览
if (e.key === 'Escape') {
const detail = document.getElementById('detail-container');
if (detail && detail.style.display !== 'none' && window.hideDetailPage) {
window.hideDetailPage();
}
}
});
// 失去焦点时移除高亮
document.addEventListener('blur', function(e) {
if (e.target.classList.contains('dev-card')) {
e.target.style.outline = '';
e.target.style.transform = '';
}
}, true);
});
// 应用搜索和筛选
function applyFilters() {
const searchTerm = document.getElementById('search-input')?.value || '';
const activeFilter = document.querySelector('.filter-btn.active')?.dataset.filter || 'all';
// 获取筛选后的开发者
const filtered = filterDevelopers(searchTerm, activeFilter);
// 重新渲染卡片
const grid = document.getElementById('developers-grid');
if (grid) {
// 添加淡出动画
grid.style.opacity = '0.5';
setTimeout(() => {
if (filtered.length === 0) {
// 显示无结果提示
grid.innerHTML = '<div class="no-results">🔍 没有找到匹配的开发者</div>';
} else {
grid.innerHTML = renderDeveloperCards(filtered);
// 添加展开/收起动画
Array.from(grid.children).forEach((card, index) => {
card.style.animation = `cardPopIn 0.3s ease ${index * 0.05}s forwards`;
card.style.opacity = '0';
setTimeout(() => {
card.style.opacity = '1';
}, 50 + index * 20);
});
}
grid.style.opacity = '1';
}, 200);
}
// 更新排行榜(排行榜不筛选,但高亮匹配项)
const ranking = document.getElementById('ranking-list');
if (ranking) {
const allDevs = getDevelopers();
ranking.innerHTML = renderRanking(allDevs);
// 高亮筛选出的开发者
if (filtered.length < allDevs.length && filtered.length > 0) {
const filteredIds = filtered.map(d => d.id);
document.querySelectorAll('.ranking-item').forEach(item => {
const nameEl = item.querySelector('.rank-name');
if (nameEl) {
const dev = allDevs.find(d => d.name === nameEl.textContent);
if (dev && filteredIds.includes(dev.id)) {
item.classList.add('match-highlight');
// 添加数字滚动动画
const winsEl = item.querySelector('.rank-wins');
if (winsEl) {
const oldValue = parseInt(winsEl.textContent) || 0;
animateNumber(winsEl, oldValue, dev.wins, 500);
}
} else {
item.classList.remove('match-highlight');
}
}
});
} else {
document.querySelectorAll('.ranking-item').forEach(item => {
item.classList.remove('match-highlight');
});
}
}
}
// 数字滚动动画
function animateNumber(element, start, end, duration = 1000) {
const range = end - start;
const increment = range / (duration / 16);
let current = start;
const startTime = performance.now();
function update(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// 缓动函数
const easeOutQuart = 1 - Math.pow(1 - progress, 3);
current = start + (range * easeOutQuart);
element.textContent = Math.round(current) + (element.textContent.includes('连胜') ? '连胜' : '');
if (progress < 1) {
requestAnimationFrame(update);
} else {
element.textContent = end + (element.textContent.includes('连胜') ? '连胜' : '');
}
}
requestAnimationFrame(update);
}
// 导出函数
window.applyFilters = applyFilters;
window.animateNumber = animateNumber;