AI 摘要
本摘要由 AI 生成,仅供参考,内容请以原文为准。
博客课程表功能实现详解
最近给博客添加了一个课程表功能,包括完整的课程表页面和侧边栏实时组件。这篇文章将详细介绍实现过程。
功能概述
课程表功能包含两个主要部分:
- 课程表页面 (
/pages/timetable) - 完整的周视图课程表 - 侧边栏组件 - 显示今日课程状态和倒计时
数据格式
课程表数据采用 JSON 格式存储,参考了 Android 课程表应用的数据结构:
json
{
"settings": {
"startDate": "2025-02-17",
"maxWeek": 18,
"nodes": 11,
"weekendDisplay": {
"enabled": true,
"weeks": [10, 13, 14],
"days": ["sat"]
}
},
"timeTable": [
{ "node": 1, "startTime": "08:00", "endTime": "08:45" },
{ "node": 2, "startTime": "08:50", "endTime": "09:35" }
],
"courseList": [
{ "id": 1, "courseName": "高等数学", "color": "#FF7589" }
],
"scheduleList": [
{
"id": 1,
"day": 1,
"startNode": 1,
"step": 2,
"startWeek": 1,
"endWeek": 18,
"teacher": "张老师",
"room": "A101"
}
]
}字段说明
settings - 基础设置
startDate: 学期开始日期maxWeek: 最大周数nodes: 每天节数weekendDisplay: 控制特定周次显示周末
timeTable - 每节课的时间安排
courseList - 课程定义列表
scheduleList - 课程安排(包含时间、地点、周次范围)
核心工具函数
1. 数据解析器
javascript
// timetableUtils.mjs
export function parseTimetableData(jsonData) {
return {
settings: jsonData.settings,
timeTable: jsonData.timeTable,
courses: jsonData.courseList,
schedules: jsonData.scheduleList
};
}2. 当前周次计算
javascript
export function resolveCurrentWeek(startDate, maxWeek) {
const start = new Date(startDate);
const now = new Date();
const diffDays = Math.floor((now - start) / (1000 * 60 * 60 * 24));
const week = Math.floor(diffDays / 7) + 1;
return Math.min(Math.max(1, week), maxWeek);
}3. 视图模型构建
javascript
export function buildTimetableViewModel(data, selectedWeek) {
const { settings, timeTable, courses, schedules } = data;
// 过滤当前周的课程
const weekSchedules = schedules.filter(s =>
selectedWeek >= s.startWeek && selectedWeek <= s.endWeek
);
// 按星期分组
const coursesByDay = {};
for (let day = 1; day <= 7; day++) {
coursesByDay[day] = weekSchedules
.filter(s => s.day === day)
.map(s => ({
...s,
courseName: courses.find(c => c.id === s.id)?.courseName,
color: courses.find(c => c.id === s.id)?.color,
timeText: getTimeText(timeTable, s.startNode, s.step)
}))
.sort((a, b) => a.startNode - b.startNode);
}
return {
currentWeek: selectedWeek,
maxWeek: settings.maxWeek,
coursesByDay,
nodeRows: timeTable
};
}课程表页面实现
页面结构
vue
<template>
<div class="timetable-page">
<!-- 周次选择器 -->
<div class="week-selector">
<button @click="previousWeek" :disabled="selectedWeek <= 1">
<svg>...</svg>
</button>
<span>第 {{ selectedWeek }} 周</span>
<span v-if="isCurrentWeek" class="current-badge">当前周</span>
<button @click="nextWeek" :disabled="selectedWeek >= maxWeek">
<svg>...</svg>
</button>
</div>
<!-- 课程表网格 -->
<table class="timetable-grid">
<thead>
<tr>
<th>节次</th>
<th v-for="day in dayColumns" :key="day.day">{{ day.label }}</th>
</tr>
</thead>
<tbody>
<tr v-for="row in nodeGroups" :key="row.startNode">
<td>
<div>第 {{ row.startNode }}-{{ row.endNode }} 节</div>
<div class="time-range">{{ row.timeText }}</div>
</td>
<td v-for="day in dayColumns" :key="day.day">
<div v-for="course in getCourses(day.day, row.startNode)"
:key="course.id"
class="course-card"
:style="{ borderColor: course.color }">
<div class="course-name">{{ course.courseName }}</div>
<div class="course-info">
<span>{{ course.room }}</span>
<span>{{ course.teacher }}</span>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</template>样式特点
- 使用 CSS Grid 布局课程卡片
- 课程颜色作为左边框标识
- 响应式设计:桌面端表格视图,移动端列表视图
- 支持深色模式
侧边栏组件实现
侧边栏组件是课程的亮点,提供实时状态更新:
状态类型
- 课前 - 显示第一节课倒计时
- 上课中 - 显示当前课程和剩余时间
- 课间 - 显示下节课倒计时
- 今日课毕 - 显示明天/下周第一节课
核心逻辑
vue
<script setup>
const courseStatus = computed(() => {
const now = currentMinute.value;
const courses = todayCourses.value;
for (let i = 0; i < courses.length; i++) {
const course = courses[i];
const timeRange = getCourseTimeRange(course);
// 上课中
if (now >= timeRange.start && now < timeRange.end) {
return {
type: 'in-class',
course,
remainMinutes: timeRange.end - now
};
}
// 课间
if (i < courses.length - 1) {
const nextCourse = courses[i + 1];
const nextRange = getCourseTimeRange(nextCourse);
if (now >= timeRange.end && now < nextRange.start) {
return {
type: 'break',
course: nextCourse,
remainMinutes: nextRange.start - now
};
}
}
}
// 今日课毕
return { type: 'finished' };
});
</script>智能下一节课提示
今日课毕后,组件会智能判断:
- 如果明天有课 → 显示明天第一节课
- 如果明天没课 → 显示下周第一节课
- 精确到秒的倒计时
javascript
const tomorrowFirstCourse = computed(() => {
const tomorrowDay = currentDay.value === 7 ? 1 : currentDay.value + 1;
const courses = viewModel.value.coursesByDay[tomorrowDay] || [];
return courses.sort((a, b) => a.startNode - b.startNode)[0];
});技术亮点
1. 实时更新
使用 setInterval 每秒更新当前时间,实现动态倒计时:
javascript
let timer = null;
onMounted(() => {
timer = setInterval(() => {
currentTime.value = new Date();
}, 1000);
});
onUnmounted(() => {
clearInterval(timer);
});2. 计算属性缓存
利用 Vue 的计算属性自动缓存,避免重复计算:
javascript
const todayCourses = computed(() => {
// 只在 viewModel 变化时重新计算
return viewModel.value.coursesByDay[currentDay.value] || [];
});3. 时间解析
解析课程时间文本为分钟数,便于比较:
javascript
function parseTimeToMinute(timeStr) {
const [hour, minute] = timeStr.split(':').map(Number);
return hour * 60 + minute;
}使用方式
1. 准备数据
将课程数据保存为 public/大三下.json:
json
{
"settings": { ... },
"timeTable": [ ... ],
"courseList": [ ... ],
"scheduleList": [ ... ]
}2. 访问页面
- 完整课程表:
/pages/timetable - 支持 URL 参数:
/pages/timetable?week=10
3. 侧边栏自动显示
组件会自动加载数据并显示今日课程状态。
总结
通过这个项目,我实现了:
- 完整的数据解析流程 - 从 JSON 到视图模型
- 灵活的周次切换 - 支持任意周次查看
- 实时状态组件 - 自动判断课前/课中/课后状态
- 智能下一节课提示 - 优先显示明天课程,其次下周
- 响应式设计 - 适配桌面端和移动端
代码已开源,欢迎参考使用。
赞赏博主
关注博主 评论 隐私政策