<template>
<div class="container">
<!-- 左侧课程列表 -->
<div class="course-list">
<div
v-for="course in courses"
:key="course.id"
class="course-item"
draggable="true"
@dragstart="handleDragStart($event, course)"
>
{{ course.name }}
</div>
</div>
<!-- 右侧可滑动课程表 -->
<div class="schedule-wrapper">
<div class="schedule-container" ref="scheduleContainer">
<div
v-for="(day, dayIdx) in visibleDays"
:key="day.date"
class="day-column"
>
<div class="day-header">{{ day.formattedDate }}</div>
<div
v-for="(slot, slotIdx) in timeSlots"
:key="slot"
class="time-slot"
:class="{
'active-cell': activeCell.day === dayIdx && activeCell.slot === slotIdx,
'has-course': day.slots[slotIdx].course
}"
@dragover.prevent="handleDragOver(dayIdx, slotIdx)"
@dragleave="handleDragLeave"
@drop="handleDrop($event, dayIdx, slotIdx)"
>
{{ slot }}
<div v-if="day.slots[slotIdx].course" class="course-name">
{{ day.slots[slotIdx].course.name }}
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
// 课程数据
const courses = ref([
{ id: 1, name: '数学课', color: '#ffeb3b' },
{ id: 2, name: '英语课', color: '#4caf50' },
{ id: 3, name: '物理课', color: '#2196f3' }
]);
// 时间槽配置
const timeSlots = ref([
'08:00', '09:00', '10:00',
'11:00', '14:00', '15:00'
]);
// 可见日期(示例生成一周)
const visibleDays = reactive([]);
// 拖拽相关状态
const draggedCourse = ref(null);
const activeCell = reactive({ day: null, slot: null });
// 初始化课程表数据
const initializeSchedule = () => {
const days = [];
const today = new Date();
for (let i = 0; i < 7; i++) {
const date = new Date(today);
date.setDate(today.getDate() + i);
days.push({
date: date.toISOString(),
formattedDate: date.toLocaleDateString('zh-CN', {
month: '2-digit',
day: '2-digit'
}),
slots: timeSlots.value.map(() => ({ course: null }))
});
}
visibleDays.push(...days);
};
// 拖拽开始
const handleDragStart = (e, course) => {
draggedCourse.value = course;
e.dataTransfer.setData('text/plain', course.id);
e.dataTransfer.effectAllowed = 'move';
};
// 拖拽经过
const handleDragOver = (dayIdx, slotIdx) => {
activeCell.day = dayIdx;
activeCell.slot = slotIdx;
};
// 拖拽离开
const handleDragLeave = () => {
activeCell.day = null;
activeCell.slot = null;
};
// 拖拽释放
const handleDrop = (e, dayIdx, slotIdx) => {
const courseId = e.dataTransfer.getData('text/plain');
const course = courses.value.find(c => c.id == courseId);
if (course) {
visibleDays[dayIdx].slots[slotIdx].course = course;
}
handleDragLeave();
};
onMounted(initializeSchedule);
</script>
<style scoped>
.container {
display: flex;
height: 100vh;
background: #f5f5f5;
}
.course-list {
width: 200px;
padding: 20px;
background: white;
border-right: 1px solid #ddd;
}
.course-item {
padding: 12px;
margin-bottom: 8px;
background: #fff;
border: 1px solid #eee;
border-radius: 4px;
cursor: move;
transition: all 0.2s;
}
.course-item:hover {
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.schedule-wrapper {
flex: 1;
overflow-x: auto;
padding: 20px;
}
.schedule-container {
display: flex;
gap: 20px;
min-width: fit-content;
}
.day-column {
width: 180px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.day-header {
padding: 12px;
background: #f8f9fa;
border-bottom: 1px solid #eee;
text-align: center;
font-weight: 500;
}
.time-slot {
height: 80px;
padding: 8px;
border-bottom: 1px solid #f0f0f0;
position: relative;
transition: all 0.2s;
}
.time-slot.active-cell {
background: #e3f2fd;
box-shadow: inset 0 0 0 2px #2196f3;
}
.time-slot.has-course {
background: #f0f4c3;
}
.course-name {
margin-top: 4px;
font-size: 12px;
color: #666;
}
</style>
按照日期分配课程
最新推荐文章于 2025-08-12 13:41:57 发布