排课部分发送已经完成
This commit is contained in:
parent
7d125a491a
commit
440117b417
|
|
@ -72,3 +72,8 @@ export function courseAvailableRooms(params: Record<string, any>) {
|
||||||
export function courseSchedule(params: Record<string, any>) {
|
export function courseSchedule(params: Record<string, any>) {
|
||||||
return request.post({ url: '/course.schedule', params })
|
return request.post({ url: '/course.schedule', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取已有排课
|
||||||
|
export function courseScheduleList(params: Record<string, any>) {
|
||||||
|
return request.get({ url: '/course.schedule/list', params })
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,25 @@
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="当前周数" prop="weekNumber">
|
||||||
|
<el-select
|
||||||
|
v-model="selectedWeekNumber"
|
||||||
|
:disabled="!queryParams.semesterId || weekOptions.length === 0"
|
||||||
|
class="w-[120px]"
|
||||||
|
placeholder="请选择周数"
|
||||||
|
@change="handleWeekChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="week in weekOptions"
|
||||||
|
:key="week"
|
||||||
|
:label="`第${week}周`"
|
||||||
|
:value="week"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<div class="text-xs text-gray-500 ml-2">
|
||||||
|
{{ currentWeekDateInfo }}
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
<!-- 选中时间段信息显示和清除按钮 -->
|
<!-- 选中时间段信息显示和清除按钮 -->
|
||||||
<el-form-item v-if="selectedTimeSlot" label="已选中时间段">
|
<el-form-item v-if="selectedTimeSlot" label="已选中时间段">
|
||||||
<el-tag type="info" size="large" closable @close="clearSelectedTimeSlot">
|
<el-tag type="info" size="large" closable @close="clearSelectedTimeSlot">
|
||||||
|
|
@ -138,7 +157,7 @@
|
||||||
<div v-else class="space-y-4 max-h-[700px] overflow-y-auto">
|
<div v-else class="space-y-4 max-h-[700px] overflow-y-auto">
|
||||||
<el-card
|
<el-card
|
||||||
v-for="item in getCourseLists.lists"
|
v-for="item in getCourseLists.lists"
|
||||||
:key="item.id"
|
:key="`course-${item.id}`"
|
||||||
shadow="hover"
|
shadow="hover"
|
||||||
:class="['cursor-pointer transition-all duration-200', selectedCourseId === item.id ? 'selected-card' : 'hover:bg-blue-50']"
|
:class="['cursor-pointer transition-all duration-200', selectedCourseId === item.id ? 'selected-card' : 'hover:bg-blue-50']"
|
||||||
@click="handleCourseClick(item)"
|
@click="handleCourseClick(item)"
|
||||||
|
|
@ -150,10 +169,13 @@
|
||||||
课程:{{ item.courseName }}
|
课程:{{ item.courseName }}
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
|
<el-tag v-if="selectedCourseId === item.id" size="small" type="info">
|
||||||
|
共{{ item.totalWeeks }}周
|
||||||
|
</el-tag>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between text-xs text-gray-600">
|
<div class="flex justify-between text-xs text-gray-600">
|
||||||
<span>教师: {{ item.teacherName }}</span>
|
<span>教师: {{ item.teacherName }}</span>
|
||||||
<span>周学时: {{ item.totalWeeks }}</span>
|
<span>总学时: {{ item.totalHours }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="selectedCourseId === item.id"
|
v-if="selectedCourseId === item.id"
|
||||||
|
|
@ -161,19 +183,13 @@
|
||||||
>
|
>
|
||||||
<div class="text-xs text-gray-700">
|
<div class="text-xs text-gray-700">
|
||||||
<div class="flex items-center mb-1">
|
<div class="flex items-center mb-1">
|
||||||
<span class="font-medium">总学时:</span>
|
<span class="font-medium">周学时:</span>
|
||||||
<span>{{ item.totalHours }}</span>
|
<span>{{ Math.round(item.totalHours / item.totalWeeks) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="font-medium">周数:</span>
|
<span class="font-medium">任务代码:</span>
|
||||||
<span>{{ item.totalWeeks }}周</span>
|
<span>{{ item.taskCode }}</span>
|
||||||
</div>
|
</div>
|
||||||
<p
|
|
||||||
class="text-xs text-gray-500"
|
|
||||||
style="color: darkgray"
|
|
||||||
>
|
|
||||||
{{ item.taskCode }}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -201,7 +217,7 @@
|
||||||
<div v-else class="space-y-4 max-h-[700px] overflow-y-auto">
|
<div v-else class="space-y-4 max-h-[700px] overflow-y-auto">
|
||||||
<el-card
|
<el-card
|
||||||
v-for="item in getRoomLists.lists"
|
v-for="item in getRoomLists.lists"
|
||||||
:key="item.id"
|
:key="`room-${item.id}`"
|
||||||
shadow="hover"
|
shadow="hover"
|
||||||
:class="['cursor-pointer transition-all duration-200', selectedRoomId === item.id ? 'selected-card' : 'hover:bg-blue-50']"
|
:class="['cursor-pointer transition-all duration-200', selectedRoomId === item.id ? 'selected-card' : 'hover:bg-blue-50']"
|
||||||
@click="handleClassroomClick(item)"
|
@click="handleClassroomClick(item)"
|
||||||
|
|
@ -247,10 +263,18 @@
|
||||||
<span class="font-medium mr-2">选中的时间段:</span>
|
<span class="font-medium mr-2">选中的时间段:</span>
|
||||||
<span>{{ getSelectedTimeSlotText() }}</span>
|
<span>{{ getSelectedTimeSlotText() }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center mb-2">
|
||||||
|
<span class="font-medium mr-2">具体日期:</span>
|
||||||
|
<span>{{ selectedDate || '请选择周数' }}</span>
|
||||||
|
</div>
|
||||||
<div class="flex items-center mb-2">
|
<div class="flex items-center mb-2">
|
||||||
<span class="font-medium mr-2">选中的课程:</span>
|
<span class="font-medium mr-2">选中的课程:</span>
|
||||||
<span>{{ selectedCourseName || '未选择' }}</span>
|
<span>{{ selectedCourseName || '未选择' }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center mb-2">
|
||||||
|
<span class="font-medium mr-2">授课教师:</span>
|
||||||
|
<span>{{ selectedCourseTeacherName || '未选择' }}</span>
|
||||||
|
</div>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="font-medium mr-2">选中的教室:</span>
|
<span class="font-medium mr-2">选中的教室:</span>
|
||||||
<span>{{ selectedRoomName || '未选择' }}</span>
|
<span>{{ selectedRoomName || '未选择' }}</span>
|
||||||
|
|
@ -259,7 +283,7 @@
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
size="large"
|
size="large"
|
||||||
:disabled="!selectedCourseId"
|
:disabled="!selectedCourseId || !selectedTimeSlot || !selectedWeekNumber"
|
||||||
@click="handleScheduleCourse"
|
@click="handleScheduleCourse"
|
||||||
plain
|
plain
|
||||||
>
|
>
|
||||||
|
|
@ -268,7 +292,7 @@
|
||||||
<el-button
|
<el-button
|
||||||
type="danger"
|
type="danger"
|
||||||
size="large"
|
size="large"
|
||||||
:disabled="!selectedTimeSlot.hasCourse"
|
:disabled="!selectedTimeSlot || !selectedTimeSlot.hasCourse"
|
||||||
@click="handleRemoveCourse"
|
@click="handleRemoveCourse"
|
||||||
plain
|
plain
|
||||||
>
|
>
|
||||||
|
|
@ -281,7 +305,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, reactive, ref } from 'vue'
|
import { computed, reactive, ref, onBeforeUnmount } from 'vue'
|
||||||
import { Check } from '@element-plus/icons-vue'
|
import { Check } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
import { classLists } from '@/api/class'
|
import { classLists } from '@/api/class'
|
||||||
|
|
@ -292,8 +316,65 @@ import { timeCurrentSemester } from '@/api/time'
|
||||||
import { usePaging } from '@/hooks/usePaging'
|
import { usePaging } from '@/hooks/usePaging'
|
||||||
import feedback from '@/utils/feedback'
|
import feedback from '@/utils/feedback'
|
||||||
|
|
||||||
|
// 星期类型
|
||||||
|
interface WeekDay {
|
||||||
|
label: string
|
||||||
|
value: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 课程数据类型
|
||||||
|
interface CourseItem {
|
||||||
|
id: number
|
||||||
|
courseId: number
|
||||||
|
courseName: string
|
||||||
|
taskCode: string
|
||||||
|
totalHours: number
|
||||||
|
teacherId: number
|
||||||
|
teacherName: string
|
||||||
|
totalWeeks: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 教室数据类型
|
||||||
|
interface ClassroomItem {
|
||||||
|
id: number
|
||||||
|
classroomName: string
|
||||||
|
classroomCode: string
|
||||||
|
classroomTypeName: string
|
||||||
|
location: string
|
||||||
|
capacity: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选中时间段类型
|
||||||
|
interface SelectedTimeSlot {
|
||||||
|
id: number
|
||||||
|
rowIndex: number
|
||||||
|
dayOfWeek: number
|
||||||
|
startTime: string
|
||||||
|
endTime: string
|
||||||
|
hasCourse: boolean
|
||||||
|
courseData?: any
|
||||||
|
}
|
||||||
|
|
||||||
|
// 学期配置类型
|
||||||
|
interface SemesterConfigItem {
|
||||||
|
id: number
|
||||||
|
academicYear: string
|
||||||
|
semesterCode: string
|
||||||
|
config: string
|
||||||
|
startDate: string
|
||||||
|
endDate: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 班级数据类型
|
||||||
|
interface ClassItem {
|
||||||
|
id: number
|
||||||
|
className: string
|
||||||
|
classCode: string
|
||||||
|
maxStudentCount: number
|
||||||
|
}
|
||||||
|
|
||||||
// 星期数组
|
// 星期数组
|
||||||
const weekDays = [
|
const weekDays: WeekDay[] = [
|
||||||
{ label: '星期一', value: 1 },
|
{ label: '星期一', value: 1 },
|
||||||
{ label: '星期二', value: 2 },
|
{ label: '星期二', value: 2 },
|
||||||
{ label: '星期三', value: 3 },
|
{ label: '星期三', value: 3 },
|
||||||
|
|
@ -303,19 +384,8 @@ const weekDays = [
|
||||||
{ label: '星期日', value: 7 }
|
{ label: '星期日', value: 7 }
|
||||||
]
|
]
|
||||||
|
|
||||||
const getConfigLists = ref<{
|
const getConfigLists = ref<{ lists: SemesterConfigItem[] }>({ lists: [] })
|
||||||
lists: Array<{
|
const getClassLists = ref<{ lists: ClassItem[] }>({ lists: [] })
|
||||||
id: number
|
|
||||||
academicYear: string
|
|
||||||
semesterCode: string
|
|
||||||
config: string
|
|
||||||
startDate: string
|
|
||||||
endDate: string
|
|
||||||
}>
|
|
||||||
}>({ lists: [] })
|
|
||||||
const getClassLists = ref<{
|
|
||||||
lists: Array<{ id: number; className: string; classCode: string; maxStudentCount: number }>
|
|
||||||
}>({ lists: [] })
|
|
||||||
const classSearchLoading = ref(false)
|
const classSearchLoading = ref(false)
|
||||||
const classSearchTimer = ref<number | null>(null)
|
const classSearchTimer = ref<number | null>(null)
|
||||||
const activeTab = ref('course')
|
const activeTab = ref('course')
|
||||||
|
|
@ -332,64 +402,122 @@ const getTimeData = ref<{
|
||||||
}>
|
}>
|
||||||
}>({ lists: [] })
|
}>({ lists: [] })
|
||||||
|
|
||||||
// 课程列表数据
|
// 课程列表数据 - 明确类型
|
||||||
const getCourseLists = ref<{
|
const getCourseLists = ref<{ lists: CourseItem[] }>({ lists: [] })
|
||||||
lists: Array<{
|
|
||||||
id: number
|
|
||||||
courseId: number
|
|
||||||
courseName: string
|
|
||||||
taskCode: string
|
|
||||||
totalHours: number
|
|
||||||
teacherId: number
|
|
||||||
teacherName: string
|
|
||||||
totalWeeks: number
|
|
||||||
}>
|
|
||||||
}>({ lists: [] })
|
|
||||||
// 课程加载状态
|
// 课程加载状态
|
||||||
const courseLoading = ref(false)
|
const courseLoading = ref(false)
|
||||||
// 选中的课程ID
|
// 选中的课程ID
|
||||||
const selectedCourseId = ref<number | null>(null)
|
const selectedCourseId = ref<number | null>(null)
|
||||||
// 选中的课程名称
|
|
||||||
const selectedCourseName = computed(() => {
|
// 选中的课程完整对象
|
||||||
if (!selectedCourseId.value) return ''
|
const selectedCourse = computed(() => {
|
||||||
const course = getCourseLists.value.lists.find(item => item.id === selectedCourseId.value)
|
if (!selectedCourseId.value) return null
|
||||||
return course ? course.courseName : ''
|
return getCourseLists.value.lists.find(item => item.id === selectedCourseId.value) || null
|
||||||
})
|
})
|
||||||
|
|
||||||
// 教室列表数据
|
// 选中的课程名称
|
||||||
const getRoomLists = ref<{
|
const selectedCourseName = computed(() => {
|
||||||
lists: Array<{
|
return selectedCourse.value?.courseName || '未选择'
|
||||||
id: number
|
})
|
||||||
classroomName: string
|
|
||||||
classroomCode: string
|
// 选中的课程教师ID
|
||||||
classroomTypeName: string
|
const selectedCourseTeacherId = computed(() => {
|
||||||
location: string
|
return selectedCourse.value?.teacherId || null
|
||||||
capacity: number
|
})
|
||||||
}>
|
|
||||||
}>({ lists: [] })
|
// 选中的课程教师姓名
|
||||||
// 教室加载状态
|
const selectedCourseTeacherName = computed(() => {
|
||||||
|
return selectedCourse.value?.teacherName || '未选择'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 教室列表数据 - 明确类型
|
||||||
|
const getRoomLists = ref<{ lists: ClassroomItem[] }>({ lists: [] })
|
||||||
const roomLoading = ref(false)
|
const roomLoading = ref(false)
|
||||||
// 选中的教室ID
|
|
||||||
const selectedRoomId = ref<number | null>(null)
|
const selectedRoomId = ref<number | null>(null)
|
||||||
// 选中的教室名称
|
// 选中的教室名称
|
||||||
const selectedRoomName = computed(() => {
|
const selectedRoomName = computed(() => {
|
||||||
if (!selectedRoomId.value) return ''
|
if (!selectedRoomId.value) return '未选择'
|
||||||
const room = getRoomLists.value.lists.find(item => item.id === selectedRoomId.value)
|
const room = getRoomLists.value.lists.find(item => item.id === selectedRoomId.value)
|
||||||
return room ? room.classroomName : ''
|
return room ? room.classroomName : '未选择'
|
||||||
})
|
})
|
||||||
|
|
||||||
// 选中的时间段
|
// 选中的时间段
|
||||||
interface SelectedTimeSlot {
|
|
||||||
id: number
|
|
||||||
rowIndex: number
|
|
||||||
dayOfWeek: number
|
|
||||||
startTime: string
|
|
||||||
endTime: string
|
|
||||||
hasCourse: boolean
|
|
||||||
courseData?: any
|
|
||||||
}
|
|
||||||
const selectedTimeSlot = ref<SelectedTimeSlot | null>(null)
|
const selectedTimeSlot = ref<SelectedTimeSlot | null>(null)
|
||||||
|
|
||||||
|
// 新增:选中的周数
|
||||||
|
const selectedWeekNumber = ref<number>(1)
|
||||||
|
|
||||||
|
// 新增:当前学期配置
|
||||||
|
const currentSemesterConfig = computed(() => {
|
||||||
|
if (!queryParams.semesterId) return null
|
||||||
|
return getConfigLists.value.lists.find(item => item.id.toString() === queryParams.semesterId.toString())
|
||||||
|
})
|
||||||
|
|
||||||
|
// 新增:周数选项
|
||||||
|
const weekOptions = computed(() => {
|
||||||
|
if (!selectedCourse.value) return []
|
||||||
|
const weeks = selectedCourse.value.totalWeeks
|
||||||
|
return Array.from({ length: weeks }, (_, i) => i + 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 新增:当前周对应的日期信息
|
||||||
|
const currentWeekDateInfo = computed(() => {
|
||||||
|
if (!selectedWeekNumber.value || !currentSemesterConfig.value) return ''
|
||||||
|
|
||||||
|
const startDate = new Date(currentSemesterConfig.value.startDate)
|
||||||
|
if (isNaN(startDate.getTime())) return '日期格式错误'
|
||||||
|
|
||||||
|
// 计算当前周第一天的日期(星期一)
|
||||||
|
// startDate可能是学期开始日期,我们需要根据周数计算
|
||||||
|
const weekStartDate = new Date(startDate)
|
||||||
|
// 将开始日期调整到星期一
|
||||||
|
const dayOfWeek = weekStartDate.getDay() // 0 = 周日, 1 = 周一, ..., 6 = 周六
|
||||||
|
const daysToMonday = dayOfWeek === 0 ? -6 : 1 - dayOfWeek // 调整到星期一
|
||||||
|
|
||||||
|
// 根据周数计算目标周的第一天(星期一)
|
||||||
|
const targetWeekStart = new Date(weekStartDate)
|
||||||
|
targetWeekStart.setDate(targetWeekStart.getDate() + daysToMonday + (selectedWeekNumber.value - 1) * 7)
|
||||||
|
|
||||||
|
// 格式化日期
|
||||||
|
const formatDate = (date: Date) => {
|
||||||
|
return `${date.getMonth() + 1}月${date.getDate()}日`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算本周日期范围
|
||||||
|
const weekEnd = new Date(targetWeekStart)
|
||||||
|
weekEnd.setDate(weekEnd.getDate() + 6) // 周日
|
||||||
|
|
||||||
|
return `${formatDate(targetWeekStart)} - ${formatDate(weekEnd)}`
|
||||||
|
})
|
||||||
|
|
||||||
|
// 新增:选中的具体日期(用于排课)
|
||||||
|
const selectedDate = computed(() => {
|
||||||
|
if (!selectedWeekNumber.value || !currentSemesterConfig.value || !selectedTimeSlot.value) return ''
|
||||||
|
|
||||||
|
const startDate = new Date(currentSemesterConfig.value.startDate)
|
||||||
|
if (isNaN(startDate.getTime())) return '日期格式错误'
|
||||||
|
|
||||||
|
// 计算当前周第一天的日期(星期一)
|
||||||
|
const weekStartDate = new Date(startDate)
|
||||||
|
const dayOfWeek = weekStartDate.getDay() // 0 = 周日, 1 = 周一, ..., 6 = 周六
|
||||||
|
const daysToMonday = dayOfWeek === 0 ? -6 : 1 - dayOfWeek // 调整到星期一
|
||||||
|
|
||||||
|
// 根据周数计算目标周的第一天(星期一)
|
||||||
|
const targetWeekStart = new Date(weekStartDate)
|
||||||
|
targetWeekStart.setDate(targetWeekStart.getDate() + daysToMonday + (selectedWeekNumber.value - 1) * 7)
|
||||||
|
|
||||||
|
// 根据星期几计算具体日期
|
||||||
|
const targetDate = new Date(targetWeekStart)
|
||||||
|
targetDate.setDate(targetDate.getDate() + selectedTimeSlot.value.dayOfWeek - 1) // -1是因为星期一对应dayOfWeek=1
|
||||||
|
|
||||||
|
// 格式化日期为YYYY-MM-DD
|
||||||
|
const year = targetDate.getFullYear()
|
||||||
|
const month = String(targetDate.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(targetDate.getDate()).padStart(2, '0')
|
||||||
|
|
||||||
|
return `${year}-${month}-${day}`
|
||||||
|
})
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const queryParams = reactive({
|
const queryParams = reactive({
|
||||||
classId: '',
|
classId: '',
|
||||||
|
|
@ -397,338 +525,356 @@ const queryParams = reactive({
|
||||||
maxStudentCount: 0
|
maxStudentCount: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
// 班级变化处理
|
// 周数变化处理
|
||||||
const handleClassChange = async () => {
|
const handleWeekChange = () => {
|
||||||
if (queryParams.classId && queryParams.semesterId) {
|
// 周数变化时,可以在这里处理相关逻辑
|
||||||
await fetchCourseLists()
|
console.log('周数变化为:', selectedWeekNumber.value)
|
||||||
await fetchRoomLists()
|
|
||||||
}
|
|
||||||
// 切换班级时清空选中
|
|
||||||
clearSelectedTimeSlot()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleStudentClick = (item: any) => {
|
// 班级变化处理
|
||||||
queryParams.maxStudentCount = item.maxStudentCount || 0
|
const handleClassChange = async () => {
|
||||||
|
// 切换班级时,清空选中的课程、教室、时间段和周数
|
||||||
|
selectedCourseId.value = null
|
||||||
|
selectedRoomId.value = null
|
||||||
|
selectedWeekNumber.value = 1
|
||||||
|
clearSelectedTimeSlot()
|
||||||
|
|
||||||
|
if (queryParams.classId && queryParams.semesterId) {
|
||||||
|
await fetchCourseLists()
|
||||||
|
await fetchRoomLists()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 学期变化处理
|
// 学期变化处理
|
||||||
const handleSemesterChange = async () => {
|
const handleSemesterChange = async () => {
|
||||||
if (queryParams.classId && queryParams.semesterId) {
|
// 切换学期时,清空选中的课程、教室、时间段和周数
|
||||||
await fetchCourseLists()
|
selectedCourseId.value = null
|
||||||
}
|
selectedRoomId.value = null
|
||||||
// 切换学期时清空选中
|
selectedWeekNumber.value = 1
|
||||||
clearSelectedTimeSlot()
|
clearSelectedTimeSlot()
|
||||||
|
|
||||||
|
if (queryParams.classId && queryParams.semesterId) {
|
||||||
|
await fetchCourseLists()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleStudentClick = (item: ClassItem) => {
|
||||||
|
queryParams.maxStudentCount = item.maxStudentCount || 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取班级列表
|
// 获取班级列表
|
||||||
const fetchClassLists = async (params: string) => {
|
const fetchClassLists = async (params: string) => {
|
||||||
try {
|
try {
|
||||||
const res = await classLists({
|
const res = await classLists({
|
||||||
className: params.trim()
|
className: params.trim()
|
||||||
})
|
})
|
||||||
getClassLists.value = res
|
getClassLists.value = res
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
feedback.msgError('获取课程列表失败')
|
feedback.msgError('获取班级列表失败')
|
||||||
getClassLists.value = { lists: [] }
|
getClassLists.value = { lists: [] }
|
||||||
} finally {
|
} finally {
|
||||||
classSearchLoading.value = false
|
classSearchLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const handleClassRemoteSearch = (keyword: string) => {
|
|
||||||
if (classSearchTimer.value) clearTimeout(classSearchTimer.value)
|
|
||||||
|
|
||||||
classSearchTimer.value = setTimeout(async () => {
|
const handleClassRemoteSearch = (keyword: string) => {
|
||||||
classSearchLoading.value = true
|
if (classSearchTimer.value) clearTimeout(classSearchTimer.value)
|
||||||
await fetchClassLists(keyword)
|
|
||||||
}, 300)
|
classSearchTimer.value = setTimeout(async () => {
|
||||||
|
classSearchLoading.value = true
|
||||||
|
await fetchClassLists(keyword)
|
||||||
|
}, 300) as unknown as number
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取学期列表
|
// 获取学期列表
|
||||||
const fetchConfigLists = async () => {
|
const fetchConfigLists = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await configLists()
|
const res = await configLists()
|
||||||
const handleData = JSON.parse(JSON.stringify(res))
|
const handleData = JSON.parse(JSON.stringify(res))
|
||||||
handleData.lists = handleData.lists.map((item: any) => ({
|
handleData.lists = handleData.lists.map((item: SemesterConfigItem) => ({
|
||||||
...item,
|
...item,
|
||||||
config: `${item.academicYear}${item.semesterCode == 'SPRING' ? ' 春' : ' 秋'}`
|
config: `${item.academicYear}${item.semesterCode === 'SPRING' ? ' 春' : ' 秋'}`
|
||||||
}))
|
}))
|
||||||
getConfigLists.value = handleData
|
getConfigLists.value = handleData
|
||||||
await fetchSemester()
|
await fetchSemester()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
feedback.msgError('获取学期列表失败')
|
feedback.msgError('获取学期列表失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchSemester = async () => {
|
const fetchSemester = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await timeCurrentSemester()
|
const res = await timeCurrentSemester()
|
||||||
if (res) {
|
if (res) {
|
||||||
queryParams.semesterId = res.id
|
queryParams.semesterId = res.id
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取学期信息失败', error)
|
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取学期信息失败', error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取课程列表
|
// 获取课程列表
|
||||||
const fetchCourseLists = async () => {
|
const fetchCourseLists = async () => {
|
||||||
try {
|
if (!queryParams.classId || !queryParams.semesterId) return
|
||||||
courseLoading.value = true
|
|
||||||
// 使用taskLists接口获取课程列表
|
try {
|
||||||
const response = await taskLists({
|
courseLoading.value = true
|
||||||
classId: queryParams.classId,
|
const response = await taskLists({
|
||||||
semesterId: queryParams.semesterId
|
classId: queryParams.classId,
|
||||||
})
|
semesterId: queryParams.semesterId
|
||||||
// 映射新接口返回的数据格式到组件所需格式
|
})
|
||||||
const mappedCourses = response.lists.map(
|
const mappedCourses: CourseItem[] = response.lists.map(
|
||||||
(task: {
|
(task: any) => ({
|
||||||
id: any
|
id: task.id,
|
||||||
courseId: any
|
courseId: task.courseId,
|
||||||
courseName: any
|
courseName: task.courseName,
|
||||||
taskCode: any
|
taskCode: task.taskCode,
|
||||||
weeklyHours: number
|
totalHours: task.weeklyHours * task.totalWeeks,
|
||||||
totalWeeks: number
|
teacherId: task.teacherId,
|
||||||
teacherId: any
|
teacherName: task.teacherName,
|
||||||
teacherName: any
|
totalWeeks: task.totalWeeks
|
||||||
}) => ({
|
})
|
||||||
id: task.id,
|
)
|
||||||
courseId: task.courseId,
|
getCourseLists.value = { lists: mappedCourses }
|
||||||
courseName: task.courseName,
|
|
||||||
taskCode: task.taskCode,
|
// 课程列表刷新后,校验选中的课程是否仍存在,不存在则清空
|
||||||
totalHours: task.weeklyHours * task.totalWeeks, // 总学时=周学时×总周数
|
if (selectedCourseId.value && !mappedCourses.find(item => item.id === selectedCourseId.value)) {
|
||||||
teacherId: task.teacherId,
|
selectedCourseId.value = null
|
||||||
teacherName: task.teacherName,
|
selectedWeekNumber.value = 1 // 清空周数选择
|
||||||
totalWeeks: task.totalWeeks
|
|
||||||
})
|
|
||||||
)
|
|
||||||
getCourseLists.value = { lists: mappedCourses }
|
|
||||||
} catch (err) {
|
|
||||||
console.log('获取课程列表失败', err)
|
|
||||||
feedback.msgError('获取课程列表失败')
|
|
||||||
getCourseLists.value = { lists: [] }
|
|
||||||
} finally {
|
|
||||||
courseLoading.value = false
|
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('获取课程列表失败', err)
|
||||||
|
feedback.msgError('获取课程列表失败')
|
||||||
|
getCourseLists.value = { lists: [] }
|
||||||
|
} finally {
|
||||||
|
courseLoading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 课程点击处理
|
|
||||||
const handleCourseClick = (course: any) => {
|
// 课程点击处理(切换选中/取消选中)
|
||||||
selectedCourseId.value = selectedCourseId.value === course.id ? null : course.id
|
const handleCourseClick = (course: CourseItem) => {
|
||||||
|
selectedCourseId.value = selectedCourseId.value === course.id ? null : course.id
|
||||||
|
selectedWeekNumber.value = 1
|
||||||
|
activeTab.value = 'course'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取教室列表
|
// 获取教室列表
|
||||||
const fetchRoomLists = async () => {
|
const fetchRoomLists = async () => {
|
||||||
try {
|
try {
|
||||||
roomLoading.value = true
|
roomLoading.value = true
|
||||||
// 使用taskLists接口获取教室列表
|
const response = await courseAvailableRooms({
|
||||||
const response = await courseAvailableRooms({
|
num: queryParams.maxStudentCount
|
||||||
num: queryParams.maxStudentCount
|
})
|
||||||
})
|
const mappedRooms: ClassroomItem[] = response.map(
|
||||||
// 映射新接口返回的数据格式到组件所需格式
|
(task: any) => ({
|
||||||
const mappedRooms = response.map(
|
id: task.id,
|
||||||
(task: {
|
classroomName: task.classroomName,
|
||||||
id: number
|
classroomCode: task.classroomCode,
|
||||||
classroomName: string
|
classroomTypeName: task.classroomTypeName,
|
||||||
classroomCode: string
|
location: task.location,
|
||||||
classroomTypeName: string
|
capacity: task.capacity
|
||||||
location: string
|
})
|
||||||
capacity: number
|
)
|
||||||
}) => ({
|
getRoomLists.value = { lists: mappedRooms }
|
||||||
id: task.id,
|
|
||||||
classroomName: task.classroomName,
|
// 教室列表刷新后,校验选中的教室是否仍存在,不存在则清空
|
||||||
classroomCode: task.classroomCode,
|
if (selectedRoomId.value && !mappedRooms.find(item => item.id === selectedRoomId.value)) {
|
||||||
classroomTypeName: task.classroomTypeName,
|
selectedRoomId.value = null
|
||||||
location: task.location,
|
|
||||||
capacity: task.capacity
|
|
||||||
})
|
|
||||||
)
|
|
||||||
getRoomLists.value = { lists: mappedRooms }
|
|
||||||
} catch (err) {
|
|
||||||
console.log('获取教室列表失败', err)
|
|
||||||
feedback.msgError('获取教室列表失败')
|
|
||||||
getRoomLists.value = { lists: [] }
|
|
||||||
} finally {
|
|
||||||
roomLoading.value = false
|
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('获取教室列表失败', err)
|
||||||
|
feedback.msgError('获取教室列表失败')
|
||||||
|
getRoomLists.value = { lists: [] }
|
||||||
|
} finally {
|
||||||
|
roomLoading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 教室点击处理
|
// 教室点击处理
|
||||||
const handleClassroomClick = (classroom: any) => {
|
const handleClassroomClick = (classroom: ClassroomItem) => {
|
||||||
selectedRoomId.value = selectedRoomId.value === classroom.id ? null : classroom.id
|
selectedRoomId.value = selectedRoomId.value === classroom.id ? null : classroom.id
|
||||||
|
activeTab.value = 'classroom'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理时间段点击
|
// 处理时间段点击
|
||||||
const handleTimeSlotClick = (rowIndex: number, dayOfWeek: number, cellData: any) => {
|
const handleTimeSlotClick = (rowIndex: number, dayOfWeek: number, cellData: any) => {
|
||||||
const row = tableData.value[rowIndex]
|
const row = tableData.value[rowIndex]
|
||||||
const id = cellData.id
|
const id = cellData?.id || Date.now()
|
||||||
const startTime = getRowStartTime(row)
|
const startTime = getRowStartTime(row)
|
||||||
const endTime = getRowEndTime(row)
|
const endTime = getRowEndTime(row)
|
||||||
|
|
||||||
// 如果点击的是已经选中的时间段,则取消选中
|
// 取消选中(点击已选中的时间段)
|
||||||
if (selectedTimeSlot.value &&
|
if (selectedTimeSlot.value &&
|
||||||
selectedTimeSlot.value.rowIndex === rowIndex &&
|
selectedTimeSlot.value.rowIndex === rowIndex &&
|
||||||
selectedTimeSlot.value.dayOfWeek === dayOfWeek) {
|
selectedTimeSlot.value.dayOfWeek === dayOfWeek) {
|
||||||
selectedTimeSlot.value = null
|
selectedTimeSlot.value = null
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置选中的时间段
|
// 设置选中的时间段
|
||||||
selectedTimeSlot.value = {
|
selectedTimeSlot.value = {
|
||||||
id,
|
id,
|
||||||
rowIndex,
|
rowIndex,
|
||||||
dayOfWeek,
|
dayOfWeek,
|
||||||
startTime,
|
startTime,
|
||||||
endTime,
|
endTime,
|
||||||
hasCourse: !!cellData,
|
hasCourse: !!cellData,
|
||||||
courseData: cellData
|
courseData: cellData
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果有课程数据,自动选中对应的课程
|
// 自动选中对应课程(基于courseId精准匹配)
|
||||||
if (cellData && cellData.courseId) {
|
if (cellData && cellData.courseId) {
|
||||||
const course = getCourseLists.value.lists.find(item => item.courseId === cellData.courseId)
|
const targetCourse = getCourseLists.value.lists.find(item => item.courseId === cellData.courseId)
|
||||||
if (course) {
|
if (targetCourse) {
|
||||||
selectedCourseId.value = course.id
|
selectedCourseId.value = targetCourse.id
|
||||||
}
|
// 如果有课程,设置默认周数为第1周
|
||||||
|
if (targetCourse.totalWeeks > 0) {
|
||||||
|
selectedWeekNumber.value = 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断时间段是否被选中
|
// 判断时间段是否被选中
|
||||||
const isTimeSlotSelected = (rowIndex: number, dayOfWeek: number, cellData: any) => {
|
const isTimeSlotSelected = (rowIndex: number, dayOfWeek: number, cellData: any) => {
|
||||||
return selectedTimeSlot.value &&
|
return selectedTimeSlot.value &&
|
||||||
selectedTimeSlot.value.rowIndex === rowIndex &&
|
selectedTimeSlot.value.rowIndex === rowIndex &&
|
||||||
selectedTimeSlot.value.dayOfWeek === dayOfWeek
|
selectedTimeSlot.value.dayOfWeek === dayOfWeek
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清除选中的时间段
|
// 清除选中的时间段
|
||||||
const clearSelectedTimeSlot = () => {
|
const clearSelectedTimeSlot = () => {
|
||||||
selectedTimeSlot.value = null
|
selectedTimeSlot.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取选中时间段的显示文本
|
// 获取选中时间段的显示文本
|
||||||
const getSelectedTimeSlotText = () => {
|
const getSelectedTimeSlotText = () => {
|
||||||
if (!selectedTimeSlot.value) return ''
|
const slot = selectedTimeSlot.value
|
||||||
|
if (!slot) return ''
|
||||||
const dayText = weekDays.find(day => day.value === selectedTimeSlot.value!.dayOfWeek)?.label || ''
|
const dayText = weekDays.find(day => day.value === slot.dayOfWeek)?.label || ''
|
||||||
return `第${selectedTimeSlot.value.rowIndex + 1}节 ${dayText} ${selectedTimeSlot.value.startTime}-${selectedTimeSlot.value.endTime}`
|
return `第${slot.rowIndex + 1}节 ${dayText} ${slot.startTime}-${slot.endTime}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 安排课程
|
// 安排课程
|
||||||
const handleScheduleCourse = async () => {
|
const handleScheduleCourse = async () => {
|
||||||
if (!selectedTimeSlot.value || !selectedCourseId.value) {
|
// 增强校验:确保时间段、课程、教师ID、周数都存在
|
||||||
feedback.msgError('请先选择时间段和课程')
|
if (!selectedTimeSlot.value || !selectedCourseId.value || !selectedCourseTeacherId.value || !selectedWeekNumber.value) {
|
||||||
return
|
feedback.msgError('请先选择有效的时间段、课程和周数')
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 调用排课API
|
// 排课API参数:新增 teacherId 和 date 传递
|
||||||
await courseSchedule({
|
await courseSchedule({
|
||||||
timeSlotId: selectedTimeSlot.value.id,
|
timeSlotId: selectedTimeSlot.value.id,
|
||||||
courseId: selectedCourseId.value,
|
courseId: selectedCourseId.value,
|
||||||
// date:
|
classroomId: selectedRoomId.value,
|
||||||
classroomId: selectedRoomId.value,
|
semesterId: queryParams.semesterId,
|
||||||
semesterId: queryParams.semesterId,
|
classId: queryParams.classId,
|
||||||
classId: queryParams.classId,
|
teacherId: selectedCourseTeacherId.value,
|
||||||
// teacherId:
|
date: selectedDate.value // 使用计算出的具体日期
|
||||||
})
|
})
|
||||||
|
|
||||||
feedback.msgSuccess('排课成功')
|
feedback.msgSuccess('排课成功')
|
||||||
// 刷新课表数据
|
// 刷新课表数据
|
||||||
await getTimeLists()
|
await getTimeLists()
|
||||||
// 清空选中状态
|
// 清空选中状态,提升用户体验
|
||||||
clearSelectedTimeSlot()
|
clearSelectedTimeSlot()
|
||||||
selectedCourseId.value = null
|
selectedCourseId.value = null
|
||||||
selectedRoomId.value = null
|
selectedRoomId.value = null
|
||||||
} catch (error) {
|
selectedWeekNumber.value = 1
|
||||||
feedback.msgError('排课失败')
|
} catch (error) {
|
||||||
}
|
console.error('排课失败', error)
|
||||||
|
feedback.msgError('排课失败,请稍后重试')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 移除课程
|
// 移除课程
|
||||||
const handleRemoveCourse = async () => {
|
const handleRemoveCourse = async () => {
|
||||||
if (!selectedTimeSlot.value || !selectedTimeSlot.value.hasCourse) {
|
if (!selectedTimeSlot.value || !selectedTimeSlot.value.hasCourse) {
|
||||||
feedback.msgError('请先选择一个有课程的时间段')
|
feedback.msgError('请先选择一个有课程的时间段')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// TODO: 这里调用移除课程API
|
// TODO: 调用移除课程API
|
||||||
// 示例:await removeCourse({
|
// 示例:await removeCourse({ timeSlotId: selectedTimeSlot.value.id })
|
||||||
// timeSlotId: selectedTimeSlot.value.timeSlotId
|
|
||||||
// })
|
|
||||||
|
|
||||||
feedback.msgSuccess('课程移除成功')
|
feedback.msgSuccess('课程移除成功')
|
||||||
// 刷新课表数据
|
await getTimeLists()
|
||||||
await getTimeLists()
|
clearSelectedTimeSlot()
|
||||||
// 清空选中状态
|
} catch (error) {
|
||||||
clearSelectedTimeSlot()
|
console.error('移除课程失败', error)
|
||||||
} catch (error) {
|
feedback.msgError('移除课程失败,请稍后重试')
|
||||||
feedback.msgError('移除课程失败')
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理定时器
|
// 清理定时器
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
if (classSearchTimer.value) clearTimeout(classSearchTimer.value)
|
if (classSearchTimer.value) clearTimeout(classSearchTimer.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 所有数据列表
|
// 所有数据列表
|
||||||
const allLists = ref<any[]>([])
|
const allLists = ref<any[]>([])
|
||||||
|
|
||||||
const { pager, getLists } = usePaging({
|
const { pager, getLists } = usePaging({
|
||||||
fetchFun: taskLists,
|
fetchFun: taskLists,
|
||||||
params: queryParams,
|
params: queryParams,
|
||||||
size: 7
|
size: 7
|
||||||
})
|
})
|
||||||
|
|
||||||
// 请求数据列表
|
// 请求数据列表
|
||||||
const getTimeLists = async () => {
|
const getTimeLists = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const res = await courseAvailableSlots()
|
const res = await courseAvailableSlots()
|
||||||
allLists.value = res
|
allLists.value = res
|
||||||
return Promise.resolve(res)
|
return Promise.resolve(res)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return Promise.reject(err)
|
console.error('获取时间段列表失败', err)
|
||||||
} finally {
|
return Promise.reject(err)
|
||||||
loading.value = false
|
} finally {
|
||||||
}
|
loading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理表格数据,按节次分组,构建星期矩阵
|
// 处理表格数据,按节次分组,构建星期矩阵
|
||||||
const tableData = computed(() => {
|
const tableData = computed(() => {
|
||||||
const timeSlotMap = new Map<string, Record<number, any>>()
|
const timeSlotMap = new Map<string, Record<number, any>>()
|
||||||
allLists.value.forEach((item) => {
|
allLists.value.forEach((item) => {
|
||||||
const key = `${item.startTime}-${item.endTime}`
|
const key = `${item.startTime}-${item.endTime}`
|
||||||
if (!timeSlotMap.has(key)) {
|
if (!timeSlotMap.has(key)) {
|
||||||
timeSlotMap.set(key, {})
|
timeSlotMap.set(key, {})
|
||||||
}
|
}
|
||||||
const slotGroup = timeSlotMap.get(key)!
|
const slotGroup = timeSlotMap.get(key)!
|
||||||
slotGroup[item.dayOfWeek] = item
|
slotGroup[item.dayOfWeek] = item
|
||||||
})
|
})
|
||||||
|
|
||||||
return Array.from(timeSlotMap.values()).sort((a, b) => {
|
return Array.from(timeSlotMap.values()).sort((a, b) => {
|
||||||
const aTime = Object.values(a)[0]?.startTime || ''
|
const aTime = Object.values(a)[0]?.startTime || ''
|
||||||
const bTime = Object.values(b)[0]?.startTime || ''
|
const bTime = Object.values(b)[0]?.startTime || ''
|
||||||
return aTime.localeCompare(bTime)
|
return aTime.localeCompare(bTime)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const getRowBaseInfo = (row: Record<number, any>) => {
|
const getRowBaseInfo = (row: Record<number, any>) => {
|
||||||
return Object.values(row).find((item) => item) || {}
|
return Object.values(row).find((item) => item) || {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getRowStartTime = (row: Record<number, any>) => {
|
const getRowStartTime = (row: Record<number, any>) => {
|
||||||
const baseInfo = getRowBaseInfo(row)
|
const baseInfo = getRowBaseInfo(row)
|
||||||
const start = baseInfo?.startTime
|
const start = baseInfo?.startTime
|
||||||
if (start == null) return ''
|
if (start == null) return ''
|
||||||
const s = String(start)
|
const s = String(start)
|
||||||
return s.length > 3 ? s.slice(0, -3) : ''
|
return s.length > 3 ? s.slice(0, -3) : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const getRowEndTime = (row: Record<number, any>) => {
|
const getRowEndTime = (row: Record<number, any>) => {
|
||||||
const baseInfo = getRowBaseInfo(row)
|
const baseInfo = getRowBaseInfo(row)
|
||||||
const end = baseInfo?.endTime
|
const end = baseInfo?.endTime
|
||||||
if (end == null) return ''
|
if (end == null) return ''
|
||||||
const s = String(end)
|
const s = String(end)
|
||||||
return s.length > 3 ? s.slice(0, -3) : ''
|
return s.length > 3 ? s.slice(0, -3) : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化时获取数据
|
// 初始化时获取数据
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue