|
|
|
@ -25,7 +25,7 @@ |
|
|
|
<el-select |
|
|
|
v-model="form.venue_id" |
|
|
|
placeholder="请选择场地" |
|
|
|
@change="calculateAvailableCapacity" |
|
|
|
@change="handleVenueChange" |
|
|
|
> |
|
|
|
<el-option |
|
|
|
v-for="item in venueList" |
|
|
|
@ -51,7 +51,7 @@ |
|
|
|
<el-select |
|
|
|
v-model="form.time_slot" |
|
|
|
placeholder="请选择上课时段" |
|
|
|
@change="calculateAvailableCapacity" |
|
|
|
@change="handleTimeSlotChange" |
|
|
|
> |
|
|
|
<el-option |
|
|
|
v-for="(timeSlot, index) in timeSlotOptions" |
|
|
|
@ -62,10 +62,6 @@ |
|
|
|
</el-select> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="剩余空位" v-if="form.venue_id && form.time_slot"> |
|
|
|
<span>{{ availableCapacity }}</span> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="上课类型" prop="course_type"> |
|
|
|
<el-radio-group v-model="form.course_type" @change="handleCourseTypeChange"> |
|
|
|
<el-radio label="class">班级</el-radio> |
|
|
|
@ -74,16 +70,13 @@ |
|
|
|
</el-radio-group> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="班级" prop="class_ids" v-if="form.course_type === 'class'"> |
|
|
|
<el-checkbox-group v-model="form.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 label="位置选择" v-if="form.venue_id && form.time_slot && form.course_type === 'class'"> |
|
|
|
<SeatSelector |
|
|
|
:venueId="form.venue_id" |
|
|
|
:capacity="venueCapacity" |
|
|
|
v-model:selectedSeats="form.selectedSeats" |
|
|
|
@change="handleSeatSelectionChange" |
|
|
|
/> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
<el-form-item label="学员" prop="student_ids" v-if="form.course_type === 'student' || form.course_type === 'trial'"> |
|
|
|
@ -129,11 +122,12 @@ |
|
|
|
|
|
|
|
<script setup> |
|
|
|
import { ref, defineProps, defineEmits, watch } from 'vue' |
|
|
|
import {getAllVenueList, getVenueList} from '@/app/api/venue' |
|
|
|
import { getClassroomList, getWithPersonnelList } from '@/app/api/classroom' |
|
|
|
import {getAllVenueList} from '@/app/api/venue' |
|
|
|
import { getAllClassroomList, getClassroompeople, getWithPersonnelList, getClassroompeopleCount } from '@/app/api/classroom' |
|
|
|
import { addCourseSchedule } from '@/app/api/course_schedule' |
|
|
|
import { getTimetables } from '@/app/api/course_schedule' |
|
|
|
import { ElMessage } from 'element-plus' |
|
|
|
import SeatSelector from './seat-selector.vue' |
|
|
|
|
|
|
|
const props = defineProps({ |
|
|
|
visible: { |
|
|
|
@ -155,10 +149,13 @@ 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 timeSlotOptions = ref([]) |
|
|
|
const availableCapacity = ref(0) |
|
|
|
const venueCapacity = ref(0) |
|
|
|
const studentSearchKeyword = ref('') |
|
|
|
const filteredStudentList = ref([]) |
|
|
|
const occupiedSeats = ref([]) |
|
|
|
const participants = ref([]) // 存储getClassroompeopleCount接口返回的人员信息 |
|
|
|
|
|
|
|
// 监听visible属性变化 |
|
|
|
watch(() => props.visible, (newVal) => { |
|
|
|
@ -186,7 +183,8 @@ const form = ref({ |
|
|
|
class_ids: [], |
|
|
|
student_ids: [], |
|
|
|
course_name: '', |
|
|
|
coach_id: '' |
|
|
|
coach_id: '', |
|
|
|
selectedSeats: [] |
|
|
|
}) |
|
|
|
|
|
|
|
const rules = { |
|
|
|
@ -216,9 +214,9 @@ const handleCampusChange = async () => { |
|
|
|
|
|
|
|
// 更新班级列表 |
|
|
|
try { |
|
|
|
const response = await getClassroomList({ campus_id: form.value.campus_id }) |
|
|
|
if (response.data && response.data.list) { |
|
|
|
classList.value = response.data.list |
|
|
|
const response = await getAllClassroomList({ campus_id: form.value.campus_id }) |
|
|
|
if (response.data && response.data) { |
|
|
|
classList.value = response.data |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('获取班级列表失败:', error) |
|
|
|
@ -227,13 +225,127 @@ const handleCampusChange = async () => { |
|
|
|
// 重置相关字段 |
|
|
|
form.value.venue_id = '' |
|
|
|
form.value.class_ids = [] |
|
|
|
form.value.time_slot = '' |
|
|
|
timeSlotOptions.value = [] |
|
|
|
availableCapacity.value = 0 |
|
|
|
venueCapacity.value = 0 |
|
|
|
occupiedSeats.value = [] |
|
|
|
} |
|
|
|
|
|
|
|
// 根据场地时间范围类型生成可用时间段 |
|
|
|
const generateTimeSlots = (venueInfo) => { |
|
|
|
if (!venueInfo) return [] |
|
|
|
|
|
|
|
const timeSlots = [] |
|
|
|
|
|
|
|
// 根据time_range_type生成不同的时间段 |
|
|
|
switch (venueInfo.time_range_type) { |
|
|
|
case 'fixed': |
|
|
|
// 从fixed_time_ranges中获取固定时间段 |
|
|
|
try { |
|
|
|
const fixedRanges = JSON.parse(venueInfo.fixed_time_ranges || '[]') |
|
|
|
fixedRanges.forEach(range => { |
|
|
|
timeSlots.push(`${range.start_time}-${range.end_time}`) |
|
|
|
}) |
|
|
|
} catch (error) { |
|
|
|
console.error('解析固定时间段失败:', error) |
|
|
|
} |
|
|
|
break |
|
|
|
|
|
|
|
case 'all': |
|
|
|
// 全天可用,但中午12:30-14:00不可用 |
|
|
|
// 早上8点到中午12:30,步长30分钟 |
|
|
|
for (let hour = 8; hour <= 12; hour++) { |
|
|
|
for (let minute = 0; minute < 60; minute += 60) { |
|
|
|
if (hour === 12 && minute === 30) continue // 跳过12:30 |
|
|
|
|
|
|
|
const startHour = hour |
|
|
|
const startMinute = minute |
|
|
|
const endHour = minute === 30 ? hour + 1 : hour |
|
|
|
const endMinute = minute === 30 ? 0 : 30 |
|
|
|
|
|
|
|
const startTime = `${startHour.toString().padStart(2, '0')}:${startMinute.toString().padStart(2, '0')}` |
|
|
|
const endTime = `${endHour.toString().padStart(2, '0')}:${endMinute.toString().padStart(2, '0')}` |
|
|
|
|
|
|
|
timeSlots.push(`${startTime}-${endTime}`) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 下午14:00到晚上22:00,步长30分钟 |
|
|
|
for (let hour = 14; hour < 22; hour++) { |
|
|
|
for (let minute = 0; minute < 60; minute += 60) { |
|
|
|
const startHour = hour |
|
|
|
const startMinute = minute |
|
|
|
const endHour = minute === 30 ? hour + 1 : hour |
|
|
|
const endMinute = minute === 30 ? 0 : 30 |
|
|
|
|
|
|
|
const startTime = `${startHour.toString().padStart(2, '0')}:${startMinute.toString().padStart(2, '0')}` |
|
|
|
const endTime = `${endHour.toString().padStart(2, '0')}:${endMinute.toString().padStart(2, '0')}` |
|
|
|
|
|
|
|
timeSlots.push(`${startTime}-${endTime}`) |
|
|
|
} |
|
|
|
} |
|
|
|
break |
|
|
|
|
|
|
|
case 'range': |
|
|
|
// 使用指定的时间范围,步长10分钟 |
|
|
|
if (venueInfo.time_range_start && venueInfo.time_range_end) { |
|
|
|
const startTimeParts = venueInfo.time_range_start.split(':') |
|
|
|
const endTimeParts = venueInfo.time_range_end.split(':') |
|
|
|
|
|
|
|
if (startTimeParts.length === 2 && endTimeParts.length === 2) { |
|
|
|
const startHour = parseInt(startTimeParts[0]) |
|
|
|
const startMinute = parseInt(startTimeParts[1]) |
|
|
|
const endHour = parseInt(endTimeParts[0]) |
|
|
|
const endMinute = parseInt(endTimeParts[1]) |
|
|
|
|
|
|
|
// 计算总分钟数 |
|
|
|
const startTotalMinutes = startHour * 60 + startMinute |
|
|
|
const endTotalMinutes = endHour * 60 + endMinute |
|
|
|
|
|
|
|
// 以10分钟为步长生成时间段 |
|
|
|
for (let minutes = startTotalMinutes; minutes < endTotalMinutes - 9; minutes += 10) { |
|
|
|
const startHour = Math.floor(minutes / 60) |
|
|
|
const startMinute = minutes % 60 |
|
|
|
const endHour = Math.floor((minutes + 10) / 60) |
|
|
|
const endMinute = (minutes + 10) % 60 |
|
|
|
|
|
|
|
const startTime = `${startHour.toString().padStart(2, '0')}:${startMinute.toString().padStart(2, '0')}` |
|
|
|
const endTime = `${endHour.toString().padStart(2, '0')}:${endMinute.toString().padStart(2, '0')}` |
|
|
|
|
|
|
|
timeSlots.push(`${startTime}-${endTime}`) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
break |
|
|
|
} |
|
|
|
|
|
|
|
return timeSlots |
|
|
|
} |
|
|
|
|
|
|
|
// 场地变化事件 |
|
|
|
const handleVenueChange = () => { |
|
|
|
form.value.time_slot = '' |
|
|
|
|
|
|
|
// 根据选择的场地生成可用时间段 |
|
|
|
const selectedVenue = venueList.value.find(item => item.id === form.value.venue_id) |
|
|
|
if (selectedVenue) { |
|
|
|
timeSlotOptions.value = generateTimeSlots(selectedVenue) |
|
|
|
venueCapacity.value = selectedVenue.capacity || 0 |
|
|
|
} else { |
|
|
|
timeSlotOptions.value = [] |
|
|
|
venueCapacity.value = 0 |
|
|
|
} |
|
|
|
|
|
|
|
availableCapacity.value = 0 |
|
|
|
occupiedSeats.value = [] |
|
|
|
} |
|
|
|
|
|
|
|
// 计算可用容量 |
|
|
|
const calculateAvailableCapacity = async () => { |
|
|
|
if (!form.value.venue_id || !form.value.time_slot) { |
|
|
|
availableCapacity.value = 0 |
|
|
|
occupiedSeats.value = [] |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
@ -267,6 +379,16 @@ const calculateAvailableCapacity = async () => { |
|
|
|
if (matchingTimeSlot && matchingTimeSlot.course) { |
|
|
|
// 更新可用容量 |
|
|
|
availableCapacity.value = matchingTimeSlot.course.hasnumber |
|
|
|
|
|
|
|
// 如果API返回了已占用座位信息,更新占用座位数据 |
|
|
|
if (matchingTimeSlot.course.occupiedSeats) { |
|
|
|
occupiedSeats.value = matchingTimeSlot.course.occupiedSeats |
|
|
|
} else { |
|
|
|
// 模拟一些占用的座位 |
|
|
|
const totalSeats = venueCapacity.value; |
|
|
|
const occupiedCount = totalSeats - availableCapacity.value; |
|
|
|
occupiedSeats.value = generateRandomOccupiedSeats(occupiedCount); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -277,6 +399,29 @@ const calculateAvailableCapacity = async () => { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 生成随机占用的座位(用于演示) |
|
|
|
const generateRandomOccupiedSeats = (count) => { |
|
|
|
const occupied = []; |
|
|
|
const rows = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']; |
|
|
|
const cols = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; |
|
|
|
|
|
|
|
for (let i = 0; i < count; i++) { |
|
|
|
const row = rows[Math.floor(Math.random() * rows.length)]; |
|
|
|
const col = cols[Math.floor(Math.random() * cols.length)]; |
|
|
|
const seat = `${row}-${col}`; |
|
|
|
if (!occupied.includes(seat)) { |
|
|
|
occupied.push(seat); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return occupied; |
|
|
|
}; |
|
|
|
|
|
|
|
// 处理座位选择变化 |
|
|
|
const handleSeatSelectionChange = (selectedSeats) => { |
|
|
|
console.log('选中的座位:', selectedSeats); |
|
|
|
}; |
|
|
|
|
|
|
|
// 上课类型变化事件 |
|
|
|
const handleCourseTypeChange = () => { |
|
|
|
// 清空相关字段 |
|
|
|
@ -360,6 +505,41 @@ const cancel = () => { |
|
|
|
dialogVisible.value = false |
|
|
|
} |
|
|
|
|
|
|
|
// 更新时间段选择并获取人员信息 |
|
|
|
const handleTimeSlotChange = async () => { |
|
|
|
if (!form.value.venue_id || !form.value.time_slot || !form.value.course_date) { |
|
|
|
participants.value = [] |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// 计算可用容量 |
|
|
|
await calculateAvailableCapacity() |
|
|
|
|
|
|
|
// 获取人员信息 |
|
|
|
try { |
|
|
|
const params = { |
|
|
|
course_date: form.value.course_date, |
|
|
|
time_slot: form.value.time_slot |
|
|
|
} |
|
|
|
|
|
|
|
const response = await getClassroompeopleCount(form.value.venue_id, params) |
|
|
|
if (response.data) { |
|
|
|
participants.value = response.data |
|
|
|
console.log('获取到的人员信息:', participants.value) |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('获取人员信息失败:', error) |
|
|
|
participants.value = [] |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 监听course_date变化 |
|
|
|
watch(() => form.value.course_date, (newVal) => { |
|
|
|
if (newVal && form.value.venue_id && form.value.time_slot) { |
|
|
|
handleTimeSlotChange() |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
// 提交表单 |
|
|
|
const submit = () => { |
|
|
|
formRef.value.validate(async (valid) => { |
|
|
|
@ -376,6 +556,8 @@ const submit = () => { |
|
|
|
participants: form.value.course_type, |
|
|
|
available_capacity: availableCapacity.value, |
|
|
|
status: 'active', |
|
|
|
selected_seats: form.value.selectedSeats, // 添加选中的座位信息 |
|
|
|
participants_info: participants.value // 添加人员信息 |
|
|
|
} |
|
|
|
|
|
|
|
// 根据不同类型设置学员信息 |
|
|
|
@ -414,7 +596,8 @@ const resetForm = () => { |
|
|
|
class_ids: [], |
|
|
|
student_ids: [], |
|
|
|
course_name: '', |
|
|
|
coach_id: '' |
|
|
|
coach_id: '', |
|
|
|
selectedSeats: [] |
|
|
|
} |
|
|
|
if (formRef.value) { |
|
|
|
formRef.value.resetFields() |
|
|
|
|