搞学术的都知道,错过一个顶会 deadline 意味着什么——再等一年。
之前一直用 aideadlin.es 查会议截止日期,UI 挺好看,但有个致命问题:数据更新不及时。打开一看,2025 年的会议还挂在上面,2026 年的反而没几条。对于需要精确规划投稿的人来说,这不太能接受。
所以,我决定给自己的博客加一个会议追踪页面。
🎯 需求分析
在动手之前,我先明确了几个核心需求:
- 数据要新:必须有 2025-2026 年的会议
- 领域覆盖:AI、数据库、软件工程、网络安全、系统、图形学、HCI
- 筛选能力:按领域、按时间(30/60/90天内)
- 极简风格:与博客整体黑白调性一致
- 纯前端:不需要后端,静态托管即可
📊 数据源选型
最初尝试直接爬 aideadlin.es 的数据,发现它用的是一个 JSON 文件,但确实很久没更新了。
后来找到了 ccfddl/ccf-deadlines —— 这是一个由中国学术社区维护的开源项目,专门追踪 CCF 推荐会议的截止日期。优势明显:
- ✅ 活跃维护:几乎每天都有更新
- ✅ 结构清晰:按领域分类的 YAML 文件
- ✅ 信息完整:包含 CCF 等级、会议地点、多个截止日期
- ✅ 覆盖广:10 个领域,数百个会议
数据结构长这样:
- title: NeurIPS
description: Conference on Neural Information Processing Systems
sub: ML
rank:
ccf: A
dblp: neurips
confs:
- year: 2026
id: neurips26
link: https://neurips.cc/
timeline:
- abstract_deadline: '2026-05-11 23:59:59'
deadline: '2026-05-18 23:59:59'
timezone: UTC-12
date: December 7-13, 2026
place: San Diego, CA, USA
完美契合我的需求。
🏗️ 技术实现
整体架构
Zola 静态站点
├── content/cfp/_index.md # 页面入口
├── templates/cfp.html # 页面模板
├── static/js/cfp.js # 核心逻辑(~500行)
└── static/css/cfp.css # 样式
核心思路:Zola 生成静态 HTML 壳,JavaScript 运行时从 GitHub 拉取数据渲染。
数据获取流程
// 1. 获取各领域的 YAML 文件列表
const categories = ['AI', 'DB', 'NW', 'SC', 'SE', 'DS', 'CT', 'CG', 'HI', 'MX'];
async function fetchCategoryConferences(category) {
// 通过 GitHub API 获取目录下所有 YAML 文件
const apiUrl = `https://api.github.com/repos/ccfddl/ccf-deadlines/contents/conference/${category}`;
const files = await fetch(apiUrl).then(r => r.json());
// 并行获取所有 YAML 文件内容
const conferences = await Promise.all(
files.filter(f => f.name.endsWith('.yml'))
.map(f => fetchAndParseYaml(f.download_url))
);
return conferences.flat();
}
截止日期解析
ccfddl 的时间格式比较灵活,需要处理多种情况:
function parseDeadline(timeline, timezone) {
// 优先使用 abstract_deadline,没有则用 deadline
const deadlineStr = timeline.abstract_deadline || timeline.deadline;
// 处理时区(如 UTC-12 表示 AoE - Anywhere on Earth)
const date = new Date(deadlineStr);
if (timezone === 'UTC-12') {
date.setHours(date.getHours() + 12); // 转换为本地时间
}
return date;
}
本地缓存
为了减少 API 调用,实现了 6 小时缓存:
const CACHE_KEY = 'cfp_conferences_cache';
const CACHE_DURATION = 6 * 60 * 60 * 1000; // 6 hours
function getCachedData() {
const cached = localStorage.getItem(CACHE_KEY);
if (cached) {
const { data, timestamp } = JSON.parse(cached);
if (Date.now() - timestamp < CACHE_DURATION) {
return data;
}
}
return null;
}
🎨 UI 设计迭代
第一版:太花哨
最初照着 aideadlin.es 的风格做了个"Premium Academic Design"——渐变背景、大色块倒计时、hover 阴影...
结果一看效果,和博客的黑白极简风格完全不搭,用户反馈"辣眼睛"。
第二版:Editorial Minimalism
推倒重来,遵循几个原则:
- 无色块:背景透明,只用边框和文字颜色区分
- Typography-first:倒计时不再是大色块,而是纯粹的数字
- 胶囊徽章:CCF 等级和领域标签用空心胶囊样式
核心 CSS:
/* 卡片 - 简约边框 + 细左侧条 */
.cfp-card {
background: #fff;
border: 1px solid #e8e8ef;
border-radius: 8px;
position: relative;
}
.cfp-card::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3px;
background: #8b8ba7;
border-radius: 3px 0 0 3px;
}
/* 倒计时 - 纯 typography,颜色表达紧急程度 */
.countdown-value {
font-family: 'SF Mono', monospace;
font-size: 1.75rem;
font-weight: 700;
color: #1a1a2e;
}
.urgency-critical .countdown-value { color: #dc3545; }
.urgency-warning .countdown-value { color: #fd7e14; }
/* 徽章 - 空心胶囊 */
.cfp-card-rank {
padding: 3px 10px;
background: transparent;
border: 1.5px solid;
border-radius: 50px;
font-size: 0.65rem;
font-weight: 600;
}
响应式布局
根据屏幕宽度自适应列数:
| 屏幕宽度 | 列数 |
|---|---|
| < 640px | 1 列 |
| 640-1023px | 2 列 |
| 1024-1279px | 3 列 |
| ≥ 1280px | 4 列 |
@media (min-width: 1280px) {
.cfp-grid {
grid-template-columns: repeat(4, 1fr);
}
}
📱 最终效果
访问 polly.wang/cfp 可以看到:
- 🔍 搜索:按会议名称、描述、地点搜索
- 📂 领域筛选:AI / Database / Software / Network / Security / Graphics / HCI / Systems
- ⏰ 时间筛选:30天内 / 60天内 / 90天内 / 全部
- 🏷️ CCF 等级:A / B / C / N 用不同颜色的胶囊徽章标识
- 📍 会议信息:日期、地点、官网链接、CFP 链接
💡 经验总结
- 数据源选择很重要:与其自己维护,不如找一个活跃的社区项目
- 设计要服务于内容:极简不是目的,让用户快速获取信息才是
- 纯前端方案的好处:无需服务器,静态托管即可,维护成本为零
- 缓存是必需的:GitHub API 有频率限制,本地缓存既省流量又提升体验
🔗 相关链接
- CFP 页面
- ccfddl/ccf-deadlines - 数据源
- aideadlin.es - 设计参考
下次再也不用担心错过 NeurIPS 的 deadline 了。😎
