|
|
|
@ -12,23 +12,21 @@ |
|
|
|
<el-option |
|
|
|
v-for="item in campusList" |
|
|
|
:key="item.id" |
|
|
|
:label="item.name" |
|
|
|
:label="item.campus_name" |
|
|
|
:value="item.id" |
|
|
|
/> |
|
|
|
</el-select> |
|
|
|
<el-button @click="prevWeek" icon="el-icon-arrow-left">上一周</el-button> |
|
|
|
<el-date-picker |
|
|
|
v-model="dateRange" |
|
|
|
type="daterange" |
|
|
|
range-separator="至" |
|
|
|
start-placeholder="开始日期" |
|
|
|
end-placeholder="结束日期" |
|
|
|
format="YYYY-MM-DD" |
|
|
|
value-format="YYYY-MM-DD" |
|
|
|
@change="handleDateRangeChange" |
|
|
|
v-model="weekDate" |
|
|
|
type="week" |
|
|
|
format="YYYY 第 ww 周" |
|
|
|
placeholder="选择周" |
|
|
|
class="week-picker" |
|
|
|
@change="handleWeekChange" |
|
|
|
/> |
|
|
|
<el-button type="primary" class="ml-2" @click="fetchData" |
|
|
|
>查询</el-button |
|
|
|
> |
|
|
|
<el-button @click="nextWeek" icon="el-icon-arrow-right">下一周</el-button> |
|
|
|
<el-button type="primary" class="ml-2" @click="fetchData">查询</el-button> |
|
|
|
</div> |
|
|
|
<el-button @click="addSchedule">添加课程</el-button> |
|
|
|
</div> |
|
|
|
@ -102,54 +100,243 @@ |
|
|
|
}} |
|
|
|
</p> |
|
|
|
</el-dialog> |
|
|
|
|
|
|
|
<!-- 添加课程弹窗 --> |
|
|
|
<el-dialog v-model="addDialogVisible" title="添加课程" width="600px"> |
|
|
|
<el-form |
|
|
|
ref="addFormRef" |
|
|
|
:model="addForm" |
|
|
|
:rules="addFormRules" |
|
|
|
label-width="120px" |
|
|
|
> |
|
|
|
<el-form-item label="校区" prop="campus_id"> |
|
|
|
<el-select |
|
|
|
v-model="addForm.campus_id" |
|
|
|
placeholder="请选择校区" |
|
|
|
@change="handleAddCampusChange" |
|
|
|
> |
|
|
|
<el-option |
|
|
|
v-for="item in campusList" |
|
|
|
:key="item.id" |
|
|
|
:label="item.campus_name" |
|
|
|
:value="item.id" |
|
|
|
/> |
|
|
|
</el-select> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="场地" prop="venue_id"> |
|
|
|
<el-select |
|
|
|
v-model="addForm.venue_id" |
|
|
|
placeholder="请选择场地" |
|
|
|
@change="calculateAvailableCapacity" |
|
|
|
> |
|
|
|
<el-option |
|
|
|
v-for="item in venueList" |
|
|
|
:key="item.id" |
|
|
|
:label="item.venue_name" |
|
|
|
:value="item.id" |
|
|
|
/> |
|
|
|
</el-select> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="上课日期" prop="course_date"> |
|
|
|
<el-date-picker |
|
|
|
v-model="addForm.course_date" |
|
|
|
type="date" |
|
|
|
placeholder="选择日期" |
|
|
|
format="YYYY-MM-DD" |
|
|
|
value-format="YYYY-MM-DD" |
|
|
|
/> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="上课时段" prop="time_slot"> |
|
|
|
<el-select |
|
|
|
v-model="addForm.time_slot" |
|
|
|
placeholder="请选择上课时段" |
|
|
|
@change="calculateAvailableCapacity" |
|
|
|
> |
|
|
|
<el-option |
|
|
|
v-for="(timeSlot, index) in timeSlotOptions" |
|
|
|
:key="index" |
|
|
|
:label="timeSlot" |
|
|
|
:value="timeSlot" |
|
|
|
/> |
|
|
|
</el-select> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="剩余空位" v-if="addForm.venue_id && addForm.time_slot"> |
|
|
|
<span>{{ availableCapacity }}</span> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="上课类型" prop="course_type"> |
|
|
|
<el-radio-group v-model="addForm.course_type" @change="handleCourseTypeChange"> |
|
|
|
<el-radio label="class">班级</el-radio> |
|
|
|
<el-radio label="student">学员</el-radio> |
|
|
|
<el-radio label="trial">试课</el-radio> |
|
|
|
</el-radio-group> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="班级" prop="class_ids" v-if="addForm.course_type === 'class'"> |
|
|
|
<el-checkbox-group v-model="addForm.class_ids" @change="handleClassChange"> |
|
|
|
<el-checkbox |
|
|
|
v-for="item in classList" |
|
|
|
:key="item.id" |
|
|
|
:label="item.id" |
|
|
|
> |
|
|
|
{{ item.class_name }} |
|
|
|
</el-checkbox> |
|
|
|
</el-checkbox-group> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="学员" prop="student_ids" v-if="addForm.course_type === 'student' || addForm.course_type === 'trial'"> |
|
|
|
<el-input |
|
|
|
v-model="studentSearchKeyword" |
|
|
|
placeholder="搜索学员名称" |
|
|
|
@input="searchStudents" |
|
|
|
/> |
|
|
|
<div class="student-checkbox-list"> |
|
|
|
<el-checkbox-group v-model="addForm.student_ids"> |
|
|
|
<el-checkbox |
|
|
|
v-for="item in filteredStudentList" |
|
|
|
:key="item.id" |
|
|
|
:label="item.id" |
|
|
|
> |
|
|
|
{{ item.name }} |
|
|
|
</el-checkbox> |
|
|
|
</el-checkbox-group> |
|
|
|
</div> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="课程名称" prop="course_name"> |
|
|
|
<el-input v-model="addForm.course_name" placeholder="请输入课程名称" /> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="上课教练" prop="coach_id"> |
|
|
|
<el-select v-model="addForm.coach_id" placeholder="请选择教练"> |
|
|
|
<el-option |
|
|
|
v-for="item in coachList" |
|
|
|
:key="item.id" |
|
|
|
:label="item.name" |
|
|
|
:value="item.id" |
|
|
|
/> |
|
|
|
</el-select> |
|
|
|
</el-form-item> |
|
|
|
</el-form> |
|
|
|
<template #footer> |
|
|
|
<el-button @click="addDialogVisible = false">取消</el-button> |
|
|
|
<el-button type="primary" @click="submitAddForm">确定</el-button> |
|
|
|
</template> |
|
|
|
</el-dialog> |
|
|
|
</div> |
|
|
|
</el-card> |
|
|
|
</template> |
|
|
|
|
|
|
|
<script setup> |
|
|
|
import { ref, onMounted } from 'vue' |
|
|
|
import { ref, onMounted, computed } from 'vue' |
|
|
|
import { getTimetables } from '@/app/api/course_schedule' |
|
|
|
import { getCampusList } from '@/app/api/campus' |
|
|
|
import { getWithCampusList } from '@/app/api/venue' |
|
|
|
import { getVenueList } from '@/app/api/venue' |
|
|
|
import { getClassroomList, getWithPersonnelList } from '@/app/api/classroom' |
|
|
|
import { addCourseSchedule } from '@/app/api/course_schedule' |
|
|
|
import { ElMessage } from 'element-plus' |
|
|
|
|
|
|
|
// 校区列表 |
|
|
|
const campusList = ref([]) |
|
|
|
const selectedCampus = ref('') |
|
|
|
|
|
|
|
// 日期范围 |
|
|
|
const dateRange = ref([ |
|
|
|
getMonday(new Date()).toISOString().split('T')[0], |
|
|
|
getSunday(new Date()).toISOString().split('T')[0], |
|
|
|
]) |
|
|
|
// 添加课程相关数据 |
|
|
|
const addDialogVisible = ref(false) |
|
|
|
const addFormRef = ref(null) |
|
|
|
const venueList = ref([]) |
|
|
|
const classList = ref([]) |
|
|
|
const studentList = ref([]) |
|
|
|
const coachList = ref([]) |
|
|
|
const timeSlotOptions = ref(['9:00-10:00', '10:00-11:00', '11:00-12:00', '14:00-15:00', '15:00-16:00', '16:00-17:00']) |
|
|
|
const availableCapacity = ref(0) |
|
|
|
const studentSearchKeyword = ref('') |
|
|
|
const filteredStudentList = ref([]) |
|
|
|
|
|
|
|
const addForm = ref({ |
|
|
|
campus_id: '', |
|
|
|
venue_id: '', |
|
|
|
course_date: '', |
|
|
|
time_slot: '', |
|
|
|
course_type: 'class', |
|
|
|
class_ids: [], |
|
|
|
student_ids: [], |
|
|
|
course_name: '', |
|
|
|
coach_id: '' |
|
|
|
}) |
|
|
|
|
|
|
|
const addFormRules = { |
|
|
|
campus_id: [{ required: true, message: '请选择校区', trigger: 'change' }], |
|
|
|
venue_id: [{ required: true, message: '请选择场地', trigger: 'change' }], |
|
|
|
course_date: [{ required: true, message: '请选择上课日期', trigger: 'change' }], |
|
|
|
time_slot: [{ required: true, message: '请选择上课时段', trigger: 'change' }], |
|
|
|
course_type: [{ required: true, message: '请选择上课类型', trigger: 'change' }], |
|
|
|
coach_id: [{ required: true, message: '请选择上课教练', trigger: 'change' }], |
|
|
|
course_name: [{ required: true, message: '请输入课程名称', trigger: 'blur' }] |
|
|
|
} |
|
|
|
|
|
|
|
// 周日期选择 |
|
|
|
const weekDate = ref(new Date()) |
|
|
|
// 日期范围(根据周计算) |
|
|
|
const dateRange = computed(() => { |
|
|
|
if (!weekDate.value) return [null, null] |
|
|
|
|
|
|
|
// 获取选择的周一和周日 |
|
|
|
const date = new Date(weekDate.value) |
|
|
|
const day = date.getDay() || 7 |
|
|
|
// 设置为该周的周一 |
|
|
|
date.setDate(date.getDate() - day + 1) |
|
|
|
const monday = new Date(date) |
|
|
|
// 设置为该周的周日 |
|
|
|
date.setDate(date.getDate() + 6) |
|
|
|
const sunday = new Date(date) |
|
|
|
|
|
|
|
return [ |
|
|
|
monday.toISOString().split('T')[0], |
|
|
|
sunday.toISOString().split('T')[0] |
|
|
|
] |
|
|
|
}) |
|
|
|
|
|
|
|
// 课程表数据 |
|
|
|
const days = ref([]) |
|
|
|
const dialogVisible = ref(false) |
|
|
|
const selectedCourse = ref(null) |
|
|
|
|
|
|
|
// 获取本周一的日期 |
|
|
|
function getMonday(date) { |
|
|
|
const day = date.getDay() || 7 |
|
|
|
if (day !== 1) { |
|
|
|
date.setHours(-24 * (day - 1)) |
|
|
|
// 上一周 |
|
|
|
const prevWeek = () => { |
|
|
|
const date = new Date(weekDate.value) |
|
|
|
date.setDate(date.getDate() - 7) |
|
|
|
weekDate.value = date |
|
|
|
fetchData() |
|
|
|
} |
|
|
|
return date |
|
|
|
|
|
|
|
// 下一周 |
|
|
|
const nextWeek = () => { |
|
|
|
const date = new Date(weekDate.value) |
|
|
|
date.setDate(date.getDate() + 7) |
|
|
|
weekDate.value = date |
|
|
|
fetchData() |
|
|
|
} |
|
|
|
|
|
|
|
// 获取本周日的日期 |
|
|
|
function getSunday(date) { |
|
|
|
const day = date.getDay() || 7 |
|
|
|
if (day !== 0) { |
|
|
|
date.setHours(24 * (7 - day)) |
|
|
|
// 周变化事件 |
|
|
|
const handleWeekChange = () => { |
|
|
|
fetchData() |
|
|
|
} |
|
|
|
return date |
|
|
|
|
|
|
|
// 校区变化事件 |
|
|
|
const handleCampusChange = () => { |
|
|
|
fetchData() |
|
|
|
} |
|
|
|
|
|
|
|
// 获取校区列表 |
|
|
|
const fetchCampusList = async () => { |
|
|
|
try { |
|
|
|
const response = await getCampusList({}) |
|
|
|
if (response.data && response.data.list) { |
|
|
|
campusList.value = response.data.list |
|
|
|
const response = await getWithCampusList({}) |
|
|
|
if (response.data) { |
|
|
|
campusList.value = response.data |
|
|
|
// 如果有校区数据,默认选择第一个 |
|
|
|
if (campusList.value.length > 0) { |
|
|
|
selectedCampus.value = campusList.value[0].id |
|
|
|
@ -160,16 +347,6 @@ const fetchCampusList = async () => { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 校区变化事件 |
|
|
|
const handleCampusChange = (val) => { |
|
|
|
selectedCampus.value = val |
|
|
|
} |
|
|
|
|
|
|
|
// 日期范围变化事件 |
|
|
|
const handleDateRangeChange = (val) => { |
|
|
|
dateRange.value = val |
|
|
|
} |
|
|
|
|
|
|
|
// 从服务器获取数据 |
|
|
|
const fetchData = async () => { |
|
|
|
try { |
|
|
|
@ -206,8 +383,7 @@ const getStudentName = (studentId) => { |
|
|
|
|
|
|
|
// 添加课程 |
|
|
|
const addSchedule = () => { |
|
|
|
// 跳转到添加课程页面或打开添加课程弹窗 |
|
|
|
console.log('添加课程') |
|
|
|
addDialogVisible.value = true |
|
|
|
} |
|
|
|
|
|
|
|
// 合并单元格方法 |
|
|
|
@ -249,10 +425,209 @@ const handleCellClick = (row, column, cell, event) => { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 添加课程相关事件 |
|
|
|
const handleAddCampusChange = async () => { |
|
|
|
// 更新场地列表 |
|
|
|
try { |
|
|
|
const response = await getVenueList({ campus_id: addForm.value.campus_id }) |
|
|
|
if (response.data && response.data.list) { |
|
|
|
venueList.value = response.data.list |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('获取场地列表失败:', error) |
|
|
|
} |
|
|
|
|
|
|
|
// 更新班级列表 |
|
|
|
try { |
|
|
|
const response = await getClassroomList({ campus_id: addForm.value.campus_id }) |
|
|
|
if (response.data && response.data.list) { |
|
|
|
classList.value = response.data.list |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('获取班级列表失败:', error) |
|
|
|
} |
|
|
|
|
|
|
|
// 重置相关字段 |
|
|
|
addForm.value.venue_id = '' |
|
|
|
addForm.value.class_ids = [] |
|
|
|
availableCapacity.value = 0 |
|
|
|
} |
|
|
|
|
|
|
|
const calculateAvailableCapacity = async () => { |
|
|
|
if (!addForm.value.venue_id || !addForm.value.time_slot) { |
|
|
|
availableCapacity.value = 0 |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
// 获取场地容量 |
|
|
|
const venueInfo = venueList.value.find(item => item.id === addForm.value.venue_id) |
|
|
|
if (venueInfo) { |
|
|
|
// 设置默认容量 |
|
|
|
availableCapacity.value = venueInfo.capacity || 0 |
|
|
|
|
|
|
|
// 获取该时段已安排的课程占用情况 |
|
|
|
if (addForm.value.course_date) { |
|
|
|
const params = { |
|
|
|
venue_id: addForm.value.venue_id, |
|
|
|
course_date: addForm.value.course_date, |
|
|
|
time_slot: addForm.value.time_slot |
|
|
|
} |
|
|
|
|
|
|
|
const response = await getTimetables(params) |
|
|
|
if (response.data && response.data.length > 0) { |
|
|
|
// 查找对应场地和时间的课程 |
|
|
|
const matchingDay = response.data.find(day => |
|
|
|
day.date.includes(addForm.value.course_date) |
|
|
|
) |
|
|
|
|
|
|
|
if (matchingDay) { |
|
|
|
const matchingTimeSlot = matchingDay.timeSlots.find(slot => |
|
|
|
slot.timeRange === addForm.value.time_slot |
|
|
|
) |
|
|
|
|
|
|
|
if (matchingTimeSlot && matchingTimeSlot.course) { |
|
|
|
// 更新可用容量 |
|
|
|
availableCapacity.value = matchingTimeSlot.course.hasnumber |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('计算可用容量失败:', error) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const handleCourseTypeChange = () => { |
|
|
|
// 清空相关字段 |
|
|
|
addForm.value.class_ids = [] |
|
|
|
addForm.value.student_ids = [] |
|
|
|
addForm.value.course_name = '' |
|
|
|
|
|
|
|
// 如果选择的是班级类型,加载学员列表 |
|
|
|
if (addForm.value.course_type === 'student' || addForm.value.course_type === 'trial') { |
|
|
|
loadStudentList() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const handleClassChange = () => { |
|
|
|
// 当选择班级时,设置课程名称为班级名称 |
|
|
|
if (addForm.value.class_ids.length > 0) { |
|
|
|
const selectedClass = classList.value.find(item => item.id === addForm.value.class_ids[0]) |
|
|
|
if (selectedClass) { |
|
|
|
addForm.value.course_name = selectedClass.class_name |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 加载学员列表 |
|
|
|
const loadStudentList = async () => { |
|
|
|
try { |
|
|
|
// 这里应该调用获取学员列表的接口 |
|
|
|
// 由于没有看到明确的接口,暂时使用模拟数据 |
|
|
|
// const response = await fetch('/student_courses/student_all') |
|
|
|
// const data = await response.json() |
|
|
|
// if (data.data) { |
|
|
|
// studentList.value = data.data |
|
|
|
// filteredStudentList.value = [...studentList.value] |
|
|
|
// } |
|
|
|
|
|
|
|
// 模拟数据 |
|
|
|
studentList.value = [ |
|
|
|
{ id: 1, name: '学员1' }, |
|
|
|
{ id: 2, name: '学员2' }, |
|
|
|
{ id: 3, name: '学员3' } |
|
|
|
] |
|
|
|
filteredStudentList.value = [...studentList.value] |
|
|
|
} catch (error) { |
|
|
|
console.error('获取学员列表失败:', error) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 加载教练列表 |
|
|
|
const loadCoachList = async () => { |
|
|
|
try { |
|
|
|
// 使用classroom的人员列表接口 |
|
|
|
const response = await getWithPersonnelList({}) |
|
|
|
if (response.data) { |
|
|
|
coachList.value = response.data |
|
|
|
} else { |
|
|
|
// 模拟数据 |
|
|
|
coachList.value = [ |
|
|
|
{ id: 1, name: '教练1' }, |
|
|
|
{ id: 2, name: '教练2' }, |
|
|
|
{ id: 3, name: '教练3' } |
|
|
|
] |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('获取教练列表失败:', error) |
|
|
|
// 模拟数据 |
|
|
|
coachList.value = [ |
|
|
|
{ id: 1, name: '教练1' }, |
|
|
|
{ id: 2, name: '教练2' }, |
|
|
|
{ id: 3, name: '教练3' } |
|
|
|
] |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const searchStudents = () => { |
|
|
|
if (!studentSearchKeyword.value) { |
|
|
|
filteredStudentList.value = [...studentList.value] |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
filteredStudentList.value = studentList.value.filter(student => |
|
|
|
student.name.includes(studentSearchKeyword.value) |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
const submitAddForm = () => { |
|
|
|
addFormRef.value.validate(async (valid) => { |
|
|
|
if (!valid) return |
|
|
|
|
|
|
|
try { |
|
|
|
const params = { |
|
|
|
campus_id: addForm.value.campus_id, |
|
|
|
venue_id: addForm.value.venue_id, |
|
|
|
course_date: addForm.value.course_date, |
|
|
|
time_slot: addForm.value.time_slot, |
|
|
|
course_id: 0, // 这里可能需要根据实际情况创建课程 |
|
|
|
coach_id: addForm.value.coach_id, |
|
|
|
participants: addForm.value.course_type, |
|
|
|
available_capacity: availableCapacity.value, |
|
|
|
status: 'active', |
|
|
|
} |
|
|
|
|
|
|
|
// 根据不同类型设置学员信息 |
|
|
|
if (addForm.value.course_type === 'class') { |
|
|
|
params.class_ids = addForm.value.class_ids |
|
|
|
// 获取班级包含的学员 |
|
|
|
const classStudents = [] |
|
|
|
// 这里需要根据实际接口调整 |
|
|
|
params.student_ids = classStudents |
|
|
|
} else { |
|
|
|
params.student_ids = addForm.value.student_ids |
|
|
|
} |
|
|
|
|
|
|
|
const response = await addCourseSchedule(params) |
|
|
|
if (response.code === 0) { |
|
|
|
ElMessage.success('添加课程成功') |
|
|
|
addDialogVisible.value = false |
|
|
|
fetchData() // 刷新课程表 |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('添加课程失败:', error) |
|
|
|
ElMessage.error('添加课程失败') |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
// 页面加载时获取数据 |
|
|
|
onMounted(() => { |
|
|
|
fetchCampusList() |
|
|
|
fetchData() |
|
|
|
loadCoachList() |
|
|
|
}) |
|
|
|
</script> |
|
|
|
|
|
|
|
@ -292,4 +667,17 @@ onMounted(() => { |
|
|
|
.classroom-name { |
|
|
|
margin-bottom: 5px; |
|
|
|
} |
|
|
|
|
|
|
|
.week-picker { |
|
|
|
width: 180px; |
|
|
|
} |
|
|
|
|
|
|
|
.student-checkbox-list { |
|
|
|
max-height: 200px; |
|
|
|
overflow-y: auto; |
|
|
|
border: 1px solid #EBEEF5; |
|
|
|
border-radius: 4px; |
|
|
|
padding: 10px; |
|
|
|
margin-top: 8px; |
|
|
|
} |
|
|
|
</style> |
|
|
|
|