|
|
<template>
|
|
|
<div class="course-table-container">
|
|
|
<div class="course-header">
|
|
|
<h1 class="page-title">课程表</h1>
|
|
|
<div class="course-actions">
|
|
|
<el-button type="primary" @click="handleAddCourse" v-if="isLoggedIn">
|
|
|
<el-icon><Plus /></el-icon>添加课程
|
|
|
</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="course-filters">
|
|
|
<el-card shadow="never" class="filter-card">
|
|
|
<div class="filter-row">
|
|
|
<div class="semester-selector">
|
|
|
<span class="filter-label">学期:</span>
|
|
|
<el-select v-model="currentSemester" placeholder="选择学期">
|
|
|
<el-option label="2023-2024学年第一学期" value="2023-1"></el-option>
|
|
|
<el-option label="2023-2024学年第二学期" value="2023-2"></el-option>
|
|
|
</el-select>
|
|
|
</div>
|
|
|
<div class="week-selector">
|
|
|
<span class="filter-label">周次:</span>
|
|
|
<el-select v-model="currentWeek" placeholder="选择周次">
|
|
|
<el-option v-for="week in 20" :key="week" :label="`第${week}周`" :value="week"></el-option>
|
|
|
</el-select>
|
|
|
</div>
|
|
|
<div class="view-selector">
|
|
|
<el-radio-group v-model="viewMode" size="small">
|
|
|
<el-radio-button label="week">周视图</el-radio-button>
|
|
|
<el-radio-button label="day">日视图</el-radio-button>
|
|
|
</el-radio-group>
|
|
|
</div>
|
|
|
</div>
|
|
|
</el-card>
|
|
|
</div>
|
|
|
|
|
|
<!-- 周视图 -->
|
|
|
<div v-if="viewMode === 'week'" class="week-view">
|
|
|
<el-card shadow="hover" class="timetable-card">
|
|
|
<div class="timetable">
|
|
|
<!-- 时间列 -->
|
|
|
<div class="time-column">
|
|
|
<div class="header-cell"></div>
|
|
|
<div class="time-cell" v-for="time in timeSlots" :key="time.id">
|
|
|
{{ time.label }}
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 星期列 -->
|
|
|
<div v-for="day in 7" :key="day" class="day-column">
|
|
|
<div class="header-cell">{{ getDayLabel(day) }}</div>
|
|
|
<div
|
|
|
class="course-cell"
|
|
|
v-for="time in timeSlots"
|
|
|
:key="time.id"
|
|
|
@click="handleCellClick(day, time.id)"
|
|
|
>
|
|
|
<div
|
|
|
v-for="course in getCoursesForTimeSlot(day, time.id)"
|
|
|
:key="course.id"
|
|
|
class="course-item"
|
|
|
:style="{ backgroundColor: course.color || '#409EFF' }"
|
|
|
@click.stop="handleCourseClick(course)"
|
|
|
>
|
|
|
<div class="course-name">{{ course.name }}</div>
|
|
|
<div class="course-info">{{ course.location }}</div>
|
|
|
<div class="course-info">{{ course.teacher }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</el-card>
|
|
|
</div>
|
|
|
|
|
|
<!-- 日视图 -->
|
|
|
<div v-else class="day-view">
|
|
|
<el-card shadow="hover" class="day-card">
|
|
|
<div class="day-header">
|
|
|
<el-button-group>
|
|
|
<el-button @click="previousDay">
|
|
|
<el-icon><ArrowLeft /></el-icon>
|
|
|
</el-button>
|
|
|
<el-button>{{ getDayLabel(currentDay) }}</el-button>
|
|
|
<el-button @click="nextDay">
|
|
|
<el-icon><ArrowRight /></el-icon>
|
|
|
</el-button>
|
|
|
</el-button-group>
|
|
|
</div>
|
|
|
|
|
|
<div class="day-timetable">
|
|
|
<div class="time-column">
|
|
|
<div class="time-cell" v-for="time in timeSlots" :key="time.id">
|
|
|
{{ time.label }}
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="day-courses-column">
|
|
|
<div
|
|
|
class="course-cell"
|
|
|
v-for="time in timeSlots"
|
|
|
:key="time.id"
|
|
|
@click="handleCellClick(currentDay, time.id)"
|
|
|
>
|
|
|
<div
|
|
|
v-for="course in getCoursesForTimeSlot(currentDay, time.id)"
|
|
|
:key="course.id"
|
|
|
class="course-item day-course-item"
|
|
|
:style="{ backgroundColor: course.color || '#409EFF' }"
|
|
|
@click.stop="handleCourseClick(course)"
|
|
|
>
|
|
|
<div class="course-name">{{ course.name }}</div>
|
|
|
<div class="course-info">{{ course.location }}</div>
|
|
|
<div class="course-info">{{ course.teacher }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</el-card>
|
|
|
</div>
|
|
|
|
|
|
<!-- 添加/编辑课程对话框 -->
|
|
|
<el-dialog
|
|
|
v-model="courseDialogVisible"
|
|
|
:title="isEditing ? '编辑课程' : '添加课程'"
|
|
|
width="500px"
|
|
|
>
|
|
|
<el-form :model="courseForm" label-width="80px" :rules="courseRules" ref="courseFormRef">
|
|
|
<el-form-item label="课程名称" prop="name">
|
|
|
<el-input v-model="courseForm.name" placeholder="请输入课程名称"></el-input>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="教师" prop="teacher">
|
|
|
<el-input v-model="courseForm.teacher" placeholder="请输入教师姓名"></el-input>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="地点" prop="location">
|
|
|
<el-input v-model="courseForm.location" placeholder="请输入上课地点"></el-input>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="星期" prop="dayOfWeek">
|
|
|
<el-select v-model="courseForm.dayOfWeek" placeholder="请选择星期">
|
|
|
<el-option v-for="day in 7" :key="day" :label="getDayLabel(day)" :value="day"></el-option>
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="开始时间" prop="startTime">
|
|
|
<el-time-select
|
|
|
v-model="courseForm.startTime"
|
|
|
placeholder="请选择开始时间"
|
|
|
start="08:00"
|
|
|
step="00:30"
|
|
|
end="22:00"
|
|
|
></el-time-select>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="结束时间" prop="endTime">
|
|
|
<el-time-select
|
|
|
v-model="courseForm.endTime"
|
|
|
placeholder="请选择结束时间"
|
|
|
start="08:00"
|
|
|
step="00:30"
|
|
|
end="22:00"
|
|
|
:min-time="courseForm.startTime"
|
|
|
></el-time-select>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="开始周次" prop="startWeek">
|
|
|
<el-input-number v-model="courseForm.startWeek" :min="1" :max="20"></el-input-number>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="结束周次" prop="endWeek">
|
|
|
<el-input-number v-model="courseForm.endWeek" :min="courseForm.startWeek" :max="20"></el-input-number>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="颜色" prop="color">
|
|
|
<el-color-picker v-model="courseForm.color"></el-color-picker>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
<template #footer>
|
|
|
<span class="dialog-footer">
|
|
|
<el-button @click="courseDialogVisible = false">取消</el-button>
|
|
|
<el-button type="primary" @click="submitCourse" :loading="submitting">
|
|
|
保存
|
|
|
</el-button>
|
|
|
</span>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
|
|
|
<!-- 课程详情对话框 -->
|
|
|
<el-dialog
|
|
|
v-model="courseDetailVisible"
|
|
|
title="课程详情"
|
|
|
width="400px"
|
|
|
>
|
|
|
<div v-if="selectedCourse" class="course-detail">
|
|
|
<h3 class="detail-title">{{ selectedCourse.name }}</h3>
|
|
|
<div class="detail-item">
|
|
|
<span class="detail-label">教师:</span>
|
|
|
<span>{{ selectedCourse.teacher || '未设置' }}</span>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<span class="detail-label">地点:</span>
|
|
|
<span>{{ selectedCourse.location || '未设置' }}</span>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<span class="detail-label">时间:</span>
|
|
|
<span>{{ getDayLabel(selectedCourse.dayOfWeek) }} {{ selectedCourse.startTime }} - {{ selectedCourse.endTime }}</span>
|
|
|
</div>
|
|
|
<div class="detail-item">
|
|
|
<span class="detail-label">周次:</span>
|
|
|
<span>第{{ selectedCourse.startWeek }}周 - 第{{ selectedCourse.endWeek }}周</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
<template #footer>
|
|
|
<span class="dialog-footer">
|
|
|
<el-button @click="courseDetailVisible = false">关闭</el-button>
|
|
|
<el-button type="primary" @click="handleEditCourse" v-if="isOwner">编辑</el-button>
|
|
|
<el-button type="danger" @click="handleDeleteCourse" v-if="isOwner">删除</el-button>
|
|
|
</span>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
import { ref, reactive, computed, onMounted } from 'vue';
|
|
|
import { ElMessage, ElMessageBox, FormInstance } from 'element-plus';
|
|
|
import { scheduleApi } from '@/api';
|
|
|
import { useUserStore } from '@/stores';
|
|
|
import { Plus, ArrowLeft, ArrowRight } from '@element-plus/icons-vue';
|
|
|
|
|
|
const userStore = useUserStore();
|
|
|
const isLoggedIn = computed(() => userStore.isLoggedIn);
|
|
|
|
|
|
// 课程表数据
|
|
|
const courses = ref<any[]>([]);
|
|
|
const loading = ref(false);
|
|
|
const currentSemester = ref('2023-1');
|
|
|
const currentWeek = ref(1);
|
|
|
const viewMode = ref('week');
|
|
|
const currentDay = ref(1); // 1-7 表示周一到周日
|
|
|
|
|
|
// 时间段定义
|
|
|
const timeSlots = [
|
|
|
{ id: 1, label: '第1节 8:00-8:45' },
|
|
|
{ id: 2, label: '第2节 8:55-9:40' },
|
|
|
{ id: 3, label: '第3节 10:00-10:45' },
|
|
|
{ id: 4, label: '第4节 10:55-11:40' },
|
|
|
{ id: 5, label: '第5节 13:30-14:15' },
|
|
|
{ id: 6, label: '第6节 14:25-15:10' },
|
|
|
{ id: 7, label: '第7节 15:30-16:15' },
|
|
|
{ id: 8, label: '第8节 16:25-17:10' },
|
|
|
{ id: 9, label: '第9节 18:30-19:15' },
|
|
|
{ id: 10, label: '第10节 19:25-20:10' },
|
|
|
{ id: 11, label: '第11节 20:20-21:05' },
|
|
|
{ id: 12, label: '第12节 21:15-22:00' }
|
|
|
];
|
|
|
|
|
|
// 课程表单
|
|
|
const courseDialogVisible = ref(false);
|
|
|
const courseFormRef = ref<FormInstance>();
|
|
|
const submitting = ref(false);
|
|
|
const isEditing = ref(false);
|
|
|
const courseForm = reactive({
|
|
|
id: undefined as number | undefined,
|
|
|
name: '',
|
|
|
teacher: '',
|
|
|
location: '',
|
|
|
dayOfWeek: 1,
|
|
|
startTime: '',
|
|
|
endTime: '',
|
|
|
startWeek: 1,
|
|
|
endWeek: 16,
|
|
|
color: '#409EFF'
|
|
|
});
|
|
|
|
|
|
const courseRules = {
|
|
|
name: [{ required: true, message: '请输入课程名称', trigger: 'blur' }],
|
|
|
dayOfWeek: [{ required: true, message: '请选择星期', trigger: 'change' }],
|
|
|
startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
|
|
|
endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }],
|
|
|
startWeek: [{ required: true, message: '请输入开始周次', trigger: 'blur' }],
|
|
|
endWeek: [{ required: true, message: '请输入结束周次', trigger: 'blur' }]
|
|
|
};
|
|
|
|
|
|
// 课程详情
|
|
|
const courseDetailVisible = ref(false);
|
|
|
const selectedCourse = ref<any>(null);
|
|
|
const isOwner = computed(() => {
|
|
|
if (!selectedCourse.value || !userStore.user) return false;
|
|
|
return selectedCourse.value.userId === userStore.user.id;
|
|
|
});
|
|
|
|
|
|
// 初始化
|
|
|
onMounted(async () => {
|
|
|
await fetchCourses();
|
|
|
});
|
|
|
|
|
|
// 获取课程列表
|
|
|
const fetchCourses = async () => {
|
|
|
loading.value = true;
|
|
|
try {
|
|
|
const res = await scheduleApi.getAllCourses();
|
|
|
if (res.code === 200) {
|
|
|
courses.value = res.data.list;
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('获取课程列表失败:', error);
|
|
|
ElMessage.error('获取课程列表失败');
|
|
|
} finally {
|
|
|
loading.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 获取星期标签
|
|
|
const getDayLabel = (day: number) => {
|
|
|
const days = ['', '周一', '周二', '周三', '周四', '周五', '周六', '周日'];
|
|
|
return days[day] || '';
|
|
|
};
|
|
|
|
|
|
// 获取指定时间段的课程
|
|
|
const getCoursesForTimeSlot = (day: number, timeSlotId: number) => {
|
|
|
return courses.value.filter(course => {
|
|
|
// 检查星期是否匹配
|
|
|
if (course.dayOfWeek !== day) return false;
|
|
|
|
|
|
// 检查周次是否匹配
|
|
|
if (currentWeek.value < course.startWeek || currentWeek.value > course.endWeek) return false;
|
|
|
|
|
|
// 检查时间段是否匹配
|
|
|
const courseStartHour = parseInt(course.startTime.split(':')[0]);
|
|
|
const courseStartMinute = parseInt(course.startTime.split(':')[1]);
|
|
|
const courseEndHour = parseInt(course.endTime.split(':')[0]);
|
|
|
const courseEndMinute = parseInt(course.endTime.split(':')[1]);
|
|
|
|
|
|
const slotStartHour = parseInt(timeSlots[timeSlotId - 1].label.split(' ')[1].split('-')[0].split(':')[0]);
|
|
|
const slotStartMinute = parseInt(timeSlots[timeSlotId - 1].label.split(' ')[1].split('-')[0].split(':')[1]);
|
|
|
const slotEndHour = parseInt(timeSlots[timeSlotId - 1].label.split(' ')[1].split('-')[1].split(':')[0]);
|
|
|
const slotEndMinute = parseInt(timeSlots[timeSlotId - 1].label.split(' ')[1].split('-')[1].split(':')[1]);
|
|
|
|
|
|
const courseStartTime = courseStartHour * 60 + courseStartMinute;
|
|
|
const courseEndTime = courseEndHour * 60 + courseEndMinute;
|
|
|
const slotStartTime = slotStartHour * 60 + slotStartMinute;
|
|
|
const slotEndTime = slotEndHour * 60 + slotEndMinute;
|
|
|
|
|
|
// 如果课程时间与时间段有重叠,则返回true
|
|
|
return (courseStartTime <= slotEndTime && courseEndTime >= slotStartTime);
|
|
|
});
|
|
|
};
|
|
|
|
|
|
// 处理单元格点击
|
|
|
const handleCellClick = (day: number, timeSlotId: number) => {
|
|
|
if (!isLoggedIn.value) {
|
|
|
ElMessage.warning('请先登录');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 设置表单初始值
|
|
|
isEditing.value = false;
|
|
|
courseForm.id = undefined;
|
|
|
courseForm.name = '';
|
|
|
courseForm.teacher = '';
|
|
|
courseForm.location = '';
|
|
|
courseForm.dayOfWeek = day;
|
|
|
|
|
|
// 设置时间
|
|
|
const timeSlot = timeSlots[timeSlotId - 1];
|
|
|
const timeRange = timeSlot.label.split(' ')[1].split('-');
|
|
|
courseForm.startTime = timeRange[0];
|
|
|
courseForm.endTime = timeRange[1];
|
|
|
|
|
|
courseForm.startWeek = currentWeek.value;
|
|
|
courseForm.endWeek = currentWeek.value + 15 > 20 ? 20 : currentWeek.value + 15;
|
|
|
courseForm.color = getRandomColor();
|
|
|
|
|
|
courseDialogVisible.value = true;
|
|
|
};
|
|
|
|
|
|
// 处理课程点击
|
|
|
const handleCourseClick = (course: any) => {
|
|
|
selectedCourse.value = course;
|
|
|
courseDetailVisible.value = true;
|
|
|
};
|
|
|
|
|
|
// 处理添加课程
|
|
|
const handleAddCourse = () => {
|
|
|
if (!isLoggedIn.value) {
|
|
|
ElMessage.warning('请先登录');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
isEditing.value = false;
|
|
|
courseForm.id = undefined;
|
|
|
courseForm.name = '';
|
|
|
courseForm.teacher = '';
|
|
|
courseForm.location = '';
|
|
|
courseForm.dayOfWeek = 1;
|
|
|
courseForm.startTime = '08:00';
|
|
|
courseForm.endTime = '09:40';
|
|
|
courseForm.startWeek = 1;
|
|
|
courseForm.endWeek = 16;
|
|
|
courseForm.color = getRandomColor();
|
|
|
|
|
|
courseDialogVisible.value = true;
|
|
|
};
|
|
|
|
|
|
// 处理编辑课程
|
|
|
const handleEditCourse = () => {
|
|
|
if (!selectedCourse.value) return;
|
|
|
|
|
|
isEditing.value = true;
|
|
|
courseForm.id = selectedCourse.value.id;
|
|
|
courseForm.name = selectedCourse.value.name;
|
|
|
courseForm.teacher = selectedCourse.value.teacher || '';
|
|
|
courseForm.location = selectedCourse.value.location || '';
|
|
|
courseForm.dayOfWeek = selectedCourse.value.dayOfWeek;
|
|
|
courseForm.startTime = selectedCourse.value.startTime;
|
|
|
courseForm.endTime = selectedCourse.value.endTime;
|
|
|
courseForm.startWeek = selectedCourse.value.startWeek;
|
|
|
courseForm.endWeek = selectedCourse.value.endWeek;
|
|
|
courseForm.color = selectedCourse.value.color || '#409EFF';
|
|
|
|
|
|
courseDetailVisible.value = false;
|
|
|
courseDialogVisible.value = true;
|
|
|
};
|
|
|
|
|
|
// 处理删除课程
|
|
|
const handleDeleteCourse = () => {
|
|
|
if (!selectedCourse.value) return;
|
|
|
|
|
|
ElMessageBox.confirm(
|
|
|
'确定要删除该课程吗?此操作不可恢复',
|
|
|
'删除确认',
|
|
|
{
|
|
|
confirmButtonText: '确定',
|
|
|
cancelButtonText: '取消',
|
|
|
type: 'warning'
|
|
|
}
|
|
|
).then(async () => {
|
|
|
try {
|
|
|
const res = await scheduleApi.deleteCourse(selectedCourse.value.id);
|
|
|
|
|
|
if (res.code === 200) {
|
|
|
ElMessage.success('课程删除成功');
|
|
|
courseDetailVisible.value = false;
|
|
|
fetchCourses();
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('删除课程失败:', error);
|
|
|
ElMessage.error('删除课程失败');
|
|
|
}
|
|
|
}).catch(() => {
|
|
|
// 用户取消删除
|
|
|
});
|
|
|
};
|
|
|
|
|
|
// 提交课程表单
|
|
|
const submitCourse = async () => {
|
|
|
if (!courseFormRef.value) return;
|
|
|
|
|
|
await courseFormRef.value.validate(async (valid) => {
|
|
|
if (valid) {
|
|
|
submitting.value = true;
|
|
|
|
|
|
try {
|
|
|
// 检查时间冲突
|
|
|
const conflictParams = {
|
|
|
dayOfWeek: courseForm.dayOfWeek,
|
|
|
startTime: courseForm.startTime,
|
|
|
endTime: courseForm.endTime,
|
|
|
excludeCourseId: courseForm.id
|
|
|
};
|
|
|
|
|
|
const conflictRes = await scheduleApi.checkCourseConflict(conflictParams);
|
|
|
|
|
|
if (conflictRes.code === 200 && conflictRes.data.hasConflict) {
|
|
|
ElMessageBox.confirm(
|
|
|
`检测到时间冲突,有${conflictRes.data.conflictCount}门课程与当前时间段冲突,是否继续?`,
|
|
|
'时间冲突',
|
|
|
{
|
|
|
confirmButtonText: '继续',
|
|
|
cancelButtonText: '取消',
|
|
|
type: 'warning'
|
|
|
}
|
|
|
).then(() => {
|
|
|
saveCourse();
|
|
|
}).catch(() => {
|
|
|
submitting.value = false;
|
|
|
});
|
|
|
} else {
|
|
|
saveCourse();
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('检查时间冲突失败:', error);
|
|
|
ElMessage.error('检查时间冲突失败');
|
|
|
submitting.value = false;
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
};
|
|
|
|
|
|
// 保存课程
|
|
|
const saveCourse = async () => {
|
|
|
try {
|
|
|
let res;
|
|
|
if (isEditing.value && courseForm.id) {
|
|
|
// 更新课程
|
|
|
res = await scheduleApi.updateCourse(courseForm.id, {
|
|
|
name: courseForm.name,
|
|
|
teacher: courseForm.teacher,
|
|
|
location: courseForm.location,
|
|
|
dayOfWeek: courseForm.dayOfWeek,
|
|
|
startTime: courseForm.startTime,
|
|
|
endTime: courseForm.endTime,
|
|
|
startWeek: courseForm.startWeek,
|
|
|
endWeek: courseForm.endWeek,
|
|
|
color: courseForm.color
|
|
|
});
|
|
|
} else {
|
|
|
// 创建课程
|
|
|
res = await scheduleApi.createCourse({
|
|
|
name: courseForm.name,
|
|
|
teacher: courseForm.teacher,
|
|
|
location: courseForm.location,
|
|
|
dayOfWeek: courseForm.dayOfWeek,
|
|
|
startTime: courseForm.startTime,
|
|
|
endTime: courseForm.endTime,
|
|
|
startWeek: courseForm.startWeek,
|
|
|
endWeek: courseForm.endWeek,
|
|
|
color: courseForm.color
|
|
|
});
|
|
|
}
|
|
|
|
|
|
if (res.code === 200) {
|
|
|
ElMessage.success(isEditing.value ? '课程更新成功' : '课程添加成功');
|
|
|
courseDialogVisible.value = false;
|
|
|
fetchCourses();
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error(isEditing.value ? '更新课程失败:' : '添加课程失败:', error);
|
|
|
ElMessage.error(isEditing.value ? '更新课程失败' : '添加课程失败');
|
|
|
} finally {
|
|
|
submitting.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 切换到前一天
|
|
|
const previousDay = () => {
|
|
|
currentDay.value = currentDay.value === 1 ? 7 : currentDay.value - 1;
|
|
|
};
|
|
|
|
|
|
// 切换到后一天
|
|
|
const nextDay = () => {
|
|
|
currentDay.value = currentDay.value === 7 ? 1 : currentDay.value + 1;
|
|
|
};
|
|
|
|
|
|
// 获取随机颜色
|
|
|
const getRandomColor = () => {
|
|
|
const colors = [
|
|
|
'#409EFF', // 蓝色
|
|
|
'#67C23A', // 绿色
|
|
|
'#E6A23C', // 黄色
|
|
|
'#F56C6C', // 红色
|
|
|
'#909399', // 灰色
|
|
|
'#9966CC', // 紫色
|
|
|
'#FF9900', // 橙色
|
|
|
'#19CAAD', // 青色
|
|
|
'#8CC7B5', // 浅绿
|
|
|
'#A0EEE1', // 浅青
|
|
|
'#BEE7E9', // 浅蓝
|
|
|
'#BEEDC7', // 浅绿
|
|
|
'#D6D5B7', // 浅黄
|
|
|
'#D1BA74', // 浅棕
|
|
|
'#E6CEAC', // 浅橙
|
|
|
'#ECAD9E' // 浅红
|
|
|
];
|
|
|
return colors[Math.floor(Math.random() * colors.length)];
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
.course-table-container {
|
|
|
padding: 20px;
|
|
|
}
|
|
|
|
|
|
.course-header {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
margin-bottom: 20px;
|
|
|
}
|
|
|
|
|
|
.page-title {
|
|
|
font-size: 24px;
|
|
|
margin: 0;
|
|
|
}
|
|
|
|
|
|
.course-filters {
|
|
|
margin-bottom: 20px;
|
|
|
}
|
|
|
|
|
|
.filter-card {
|
|
|
border-radius: 8px;
|
|
|
}
|
|
|
|
|
|
.filter-row {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
flex-wrap: wrap;
|
|
|
gap: 16px;
|
|
|
}
|
|
|
|
|
|
.filter-label {
|
|
|
margin-right: 10px;
|
|
|
font-weight: 500;
|
|
|
}
|
|
|
|
|
|
/* 周视图样式 */
|
|
|
.week-view {
|
|
|
margin-top: 20px;
|
|
|
}
|
|
|
|
|
|
.timetable-card {
|
|
|
border-radius: 8px;
|
|
|
}
|
|
|
|
|
|
.timetable {
|
|
|
display: flex;
|
|
|
min-height: 800px;
|
|
|
}
|
|
|
|
|
|
.time-column, .day-column {
|
|
|
flex: 1;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
border-right: 1px solid #eee;
|
|
|
}
|
|
|
|
|
|
.time-column {
|
|
|
flex: 0 0 120px;
|
|
|
}
|
|
|
|
|
|
.header-cell {
|
|
|
height: 50px;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
font-weight: bold;
|
|
|
background-color: #f5f7fa;
|
|
|
border-bottom: 1px solid #eee;
|
|
|
}
|
|
|
|
|
|
.time-cell {
|
|
|
height: 60px;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
border-bottom: 1px solid #eee;
|
|
|
font-size: 12px;
|
|
|
}
|
|
|
|
|
|
.course-cell {
|
|
|
height: 60px;
|
|
|
border-bottom: 1px solid #eee;
|
|
|
position: relative;
|
|
|
cursor: pointer;
|
|
|
}
|
|
|
|
|
|
.course-item {
|
|
|
position: absolute;
|
|
|
top: 2px;
|
|
|
left: 2px;
|
|
|
right: 2px;
|
|
|
bottom: 2px;
|
|
|
border-radius: 4px;
|
|
|
padding: 4px;
|
|
|
color: white;
|
|
|
font-size: 12px;
|
|
|
overflow: hidden;
|
|
|
cursor: pointer;
|
|
|
transition: all 0.3s;
|
|
|
}
|
|
|
|
|
|
.course-item:hover {
|
|
|
transform: scale(1.02);
|
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
|
}
|
|
|
|
|
|
.course-name {
|
|
|
font-weight: bold;
|
|
|
margin-bottom: 2px;
|
|
|
white-space: nowrap;
|
|
|
overflow: hidden;
|
|
|
text-overflow: ellipsis;
|
|
|
}
|
|
|
|
|
|
.course-info {
|
|
|
white-space: nowrap;
|
|
|
overflow: hidden;
|
|
|
text-overflow: ellipsis;
|
|
|
font-size: 10px;
|
|
|
}
|
|
|
|
|
|
/* 日视图样式 */
|
|
|
.day-view {
|
|
|
margin-top: 20px;
|
|
|
}
|
|
|
|
|
|
.day-card {
|
|
|
border-radius: 8px;
|
|
|
}
|
|
|
|
|
|
.day-header {
|
|
|
display: flex;
|
|
|
justify-content: center;
|
|
|
margin-bottom: 20px;
|
|
|
}
|
|
|
|
|
|
.day-timetable {
|
|
|
display: flex;
|
|
|
min-height: 800px;
|
|
|
}
|
|
|
|
|
|
.day-courses-column {
|
|
|
flex: 1;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
}
|
|
|
|
|
|
.day-course-item {
|
|
|
padding: 8px;
|
|
|
}
|
|
|
|
|
|
/* 课程详情样式 */
|
|
|
.course-detail {
|
|
|
padding: 10px;
|
|
|
}
|
|
|
|
|
|
.detail-title {
|
|
|
font-size: 18px;
|
|
|
margin-bottom: 15px;
|
|
|
color: #303133;
|
|
|
}
|
|
|
|
|
|
.detail-item {
|
|
|
margin-bottom: 10px;
|
|
|
display: flex;
|
|
|
}
|
|
|
|
|
|
.detail-label {
|
|
|
font-weight: 500;
|
|
|
width: 60px;
|
|
|
}
|
|
|
|
|
|
.dialog-footer {
|
|
|
display: flex;
|
|
|
justify-content: flex-end;
|
|
|
gap: 10px;
|
|
|
}
|
|
|
</style>
|