You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
671 lines
19 KiB
671 lines
19 KiB
<template>
|
|
<view class="add-schedule-container">
|
|
<view class="form-container">
|
|
<fui-form>
|
|
<!-- 课程选择 -->
|
|
<fui-form-item label="课程" required>
|
|
<view class="selector-input" @click="showCoursePicker = true">
|
|
<text>{{ selectedCourse ? selectedCourse.course_name : '请选择课程' }}</text>
|
|
<fui-icon name="arrowdown" :size="32" color="#CCCCCC"></fui-icon>
|
|
</view>
|
|
<single-picker
|
|
:show="showCoursePicker"
|
|
:data="courseOptions"
|
|
valueKey="id"
|
|
textKey="course_name"
|
|
title="选择课程"
|
|
@change="onCourseSelect"
|
|
@cancel="showCoursePicker = false"
|
|
@update:show="showCoursePicker = $event"
|
|
></single-picker>
|
|
</fui-form-item>
|
|
|
|
<!-- 班级选择 -->
|
|
<fui-form-item label="班级">
|
|
<view class="selector-input" @click="showClassPicker = true">
|
|
<text>{{ selectedClass ? selectedClass.class_name : '请选择班级(可选)' }}</text>
|
|
<fui-icon name="arrowdown" :size="32" color="#CCCCCC"></fui-icon>
|
|
</view>
|
|
<single-picker
|
|
:show="showClassPicker"
|
|
:data="classOptions"
|
|
valueKey="id"
|
|
textKey="class_name"
|
|
title="选择班级"
|
|
@change="onClassSelect"
|
|
@cancel="showClassPicker = false"
|
|
@update:show="showClassPicker = $event"
|
|
></single-picker>
|
|
</fui-form-item>
|
|
|
|
<!-- 教练选择 -->
|
|
<fui-form-item label="授课教练" required>
|
|
<view class="selector-input" @click="showCoachPicker = true">
|
|
<text>{{ selectedCoach ? selectedCoach.name : '请选择教练' }}</text>
|
|
<fui-icon name="arrowdown" :size="32" color="#CCCCCC"></fui-icon>
|
|
</view>
|
|
<single-picker
|
|
:show="showCoachPicker"
|
|
:data="coachOptions"
|
|
valueKey="id"
|
|
textKey="name"
|
|
title="选择教练"
|
|
@change="onCoachSelect"
|
|
@cancel="showCoachPicker = false"
|
|
@update:show="showCoachPicker = $event"
|
|
></single-picker>
|
|
</fui-form-item>
|
|
|
|
<!-- 场地选择 -->
|
|
<fui-form-item label="上课场地" required>
|
|
<view class="selector-input" @click="showVenuePicker = true">
|
|
<text>{{ selectedVenue ? selectedVenue.venue_name : '请选择场地' }}</text>
|
|
<fui-icon name="arrowdown" :size="32" color="#CCCCCC"></fui-icon>
|
|
</view>
|
|
<single-picker
|
|
:show="showVenuePicker"
|
|
:data="venueOptions"
|
|
valueKey="id"
|
|
textKey="venue_name"
|
|
title="选择场地"
|
|
@change="onVenueSelect"
|
|
@cancel="showVenuePicker = false"
|
|
@update:show="showVenuePicker = $event"
|
|
></single-picker>
|
|
</fui-form-item>
|
|
|
|
<!-- 日期选择 -->
|
|
<fui-form-item label="上课日期" required>
|
|
<view class="selector-input" @click="showDatePicker = true">
|
|
<text>{{ formData.course_date || '请选择日期' }}</text>
|
|
<fui-icon name="calendar" :size="32" color="#CCCCCC"></fui-icon>
|
|
</view>
|
|
<fui-date-picker
|
|
:show="showDatePicker"
|
|
type="3"
|
|
@confirm="onDateSelect"
|
|
@cancel="showDatePicker = false"
|
|
:value="formData.course_date"
|
|
></fui-date-picker>
|
|
</fui-form-item>
|
|
|
|
<!-- 时间选择 -->
|
|
<fui-form-item label="上课时间" required>
|
|
<view class="selector-input" @click="showTimePicker = true">
|
|
<text>{{ formData.time_slot || '请选择时间段' }}</text>
|
|
<fui-icon name="time" :size="32" color="#CCCCCC"></fui-icon>
|
|
</view>
|
|
<single-picker
|
|
:show="showTimePicker"
|
|
:data="timeSlotOptions"
|
|
valueKey="value"
|
|
textKey="text"
|
|
title="选择时间"
|
|
@change="onTimeSelect"
|
|
@cancel="showTimePicker = false"
|
|
@update:show="showTimePicker = $event"
|
|
></single-picker>
|
|
</fui-form-item>
|
|
|
|
<!-- 容量设置 -->
|
|
<fui-form-item label="课程容量" required>
|
|
<view class="capacity-container">
|
|
<text class="capacity-text">{{ formData.available_capacity || '0' }}</text>
|
|
<text class="capacity-hint">(根据场地自动设置)</text>
|
|
</view>
|
|
</fui-form-item>
|
|
|
|
<!-- 备注信息 -->
|
|
<fui-form-item label="备注">
|
|
<fui-textarea
|
|
:value="formData.remark"
|
|
placeholder="请输入备注信息(可选)"
|
|
@input="formData.remark = $event"
|
|
maxlength="200"
|
|
></fui-textarea>
|
|
</fui-form-item>
|
|
</fui-form>
|
|
|
|
<!-- 提交按钮 -->
|
|
<view class="btn-container">
|
|
<fui-button type="primary" @click="submitForm" :loading="submitting">创建课程安排</fui-button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import api from '@/api/apiRoute.js';
|
|
import SinglePicker from '@/components/custom-picker/single-picker.vue';
|
|
|
|
export default {
|
|
components: {
|
|
SinglePicker
|
|
},
|
|
data() {
|
|
return {
|
|
// 表单数据
|
|
formData: {
|
|
course_id: '',
|
|
class_id: '',
|
|
coach_id: '',
|
|
venue_id: '',
|
|
course_date: '',
|
|
time_slot: '',
|
|
available_capacity: '',
|
|
remark: ''
|
|
},
|
|
|
|
// 选择器数据
|
|
showCoursePicker: false,
|
|
showClassPicker: false,
|
|
showCoachPicker: false,
|
|
showVenuePicker: false,
|
|
showDatePicker: false,
|
|
showTimePicker: false,
|
|
|
|
// 选项数据
|
|
courseOptions: [],
|
|
classOptions: [],
|
|
coachOptions: [],
|
|
venueOptions: [],
|
|
timeSlotOptions: [],
|
|
|
|
// 选中的数据对象
|
|
selectedCourse: null,
|
|
selectedClass: null,
|
|
selectedCoach: null,
|
|
selectedVenue: null,
|
|
|
|
// 状态标记
|
|
submitting: false,
|
|
|
|
// 预填充数据
|
|
prefillDate: '',
|
|
prefillTime: '',
|
|
prefillTimeSlot: ''
|
|
};
|
|
},
|
|
|
|
onLoad(options) {
|
|
// 从路由参数获取预填充数据
|
|
if (options.date) {
|
|
this.prefillDate = options.date;
|
|
this.formData.course_date = options.date;
|
|
}
|
|
|
|
if (options.time) {
|
|
this.prefillTime = options.time;
|
|
}
|
|
|
|
if (options.time_slot) {
|
|
this.prefillTimeSlot = options.time_slot;
|
|
this.formData.time_slot = options.time_slot;
|
|
}
|
|
|
|
// 加载初始数据
|
|
this.loadFilterOptions();
|
|
},
|
|
|
|
methods: {
|
|
// 返回上一页
|
|
goBack() {
|
|
uni.navigateBack();
|
|
},
|
|
|
|
// 加载选项数据
|
|
async loadFilterOptions() {
|
|
uni.showLoading({
|
|
title: '加载数据中...'
|
|
});
|
|
|
|
try {
|
|
// 使用统一接口获取所有选项数据(自动根据登录用户校区过滤)
|
|
const res = await api.getAllScheduleOptions();
|
|
|
|
if (res.code === 1) {
|
|
const data = res.data || {};
|
|
|
|
// 设置各种选项
|
|
this.courseOptions = data.courses || [];
|
|
this.classOptions = data.classes || [];
|
|
this.coachOptions = data.coaches || [];
|
|
this.venueOptions = data.venues || [];
|
|
|
|
console.log('加载的数据:', {
|
|
courses: this.courseOptions.length,
|
|
classes: this.classOptions.length,
|
|
coaches: this.coachOptions.length,
|
|
venues: this.venueOptions.length,
|
|
campus_info: data.campus_info
|
|
});
|
|
} else {
|
|
throw new Error(res.msg || '获取选项数据失败');
|
|
}
|
|
|
|
// 如果有预填充时间段,设置选中的时间段
|
|
if (this.prefillTimeSlot) {
|
|
this.formData.time_slot = this.prefillTimeSlot;
|
|
}
|
|
|
|
// 检查是否有必要的数据
|
|
if (this.courseOptions.length === 0) {
|
|
uni.showToast({
|
|
title: '暂无可用课程',
|
|
icon: 'none'
|
|
});
|
|
}
|
|
|
|
if (this.coachOptions.length === 0) {
|
|
uni.showToast({
|
|
title: '暂无可用教练',
|
|
icon: 'none'
|
|
});
|
|
}
|
|
|
|
if (this.venueOptions.length === 0) {
|
|
uni.showToast({
|
|
title: '暂无可用场地',
|
|
icon: 'none'
|
|
});
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('加载筛选选项失败:', error);
|
|
|
|
// 提供更详细的错误信息
|
|
let errorMsg = '加载数据失败';
|
|
if (error.response) {
|
|
errorMsg = `服务器错误: ${error.response.status}`;
|
|
} else if (error.request) {
|
|
errorMsg = '网络连接失败,请检查网络';
|
|
} else {
|
|
errorMsg = error.message || '未知错误';
|
|
}
|
|
|
|
uni.showToast({
|
|
title: errorMsg,
|
|
icon: 'none',
|
|
duration: 3000
|
|
});
|
|
} finally {
|
|
uni.hideLoading();
|
|
}
|
|
},
|
|
|
|
// 生成时间段选项
|
|
generateTimeSlotOptions() {
|
|
const timeSlots = [];
|
|
|
|
// 早上时间段
|
|
for (let hour = 8; hour < 12; hour++) {
|
|
const startHour = hour.toString().padStart(2, '0');
|
|
const endHour = (hour + 1).toString().padStart(2, '0');
|
|
timeSlots.push({
|
|
value: `${startHour}:00-${endHour}:00`,
|
|
text: `${startHour}:00-${endHour}:00`
|
|
});
|
|
}
|
|
|
|
// 下午时间段
|
|
for (let hour = 12; hour < 18; hour++) {
|
|
const startHour = hour.toString().padStart(2, '0');
|
|
const endHour = (hour + 1).toString().padStart(2, '0');
|
|
timeSlots.push({
|
|
value: `${startHour}:00-${endHour}:00`,
|
|
text: `${startHour}:00-${endHour}:00`
|
|
});
|
|
}
|
|
|
|
// 晚上时间段
|
|
for (let hour = 18; hour < 22; hour++) {
|
|
const startHour = hour.toString().padStart(2, '0');
|
|
const endHour = (hour + 1).toString().padStart(2, '0');
|
|
timeSlots.push({
|
|
value: `${startHour}:00-${endHour}:00`,
|
|
text: `${startHour}:00-${endHour}:00`
|
|
});
|
|
}
|
|
|
|
this.timeSlotOptions = timeSlots;
|
|
},
|
|
|
|
// 动态加载场地可用时间段
|
|
async loadTimeSlots() {
|
|
if (!this.formData.venue_id || !this.formData.course_date) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const res = await api.getVenueTimeSlots({
|
|
venue_id: this.formData.venue_id,
|
|
date: this.formData.course_date
|
|
});
|
|
|
|
if (res.code === 1) {
|
|
// 转换API返回的时间段格式为选择器需要的格式
|
|
this.timeSlotOptions = res.data.map(slot => ({
|
|
value: slot.time_slot,
|
|
text: slot.time_slot
|
|
}));
|
|
|
|
console.log('可用时间段:', this.timeSlotOptions);
|
|
} else {
|
|
// 如果API失败,则使用默认时间段
|
|
this.generateTimeSlotOptions();
|
|
uni.showToast({
|
|
title: res.msg || '获取可用时间段失败,使用默认时间段',
|
|
icon: 'none'
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error('加载时间段失败:', error);
|
|
// 如果API失败,则使用默认时间段
|
|
this.generateTimeSlotOptions();
|
|
|
|
let errorMsg = '获取可用时间段失败,使用默认时间段';
|
|
if (error.response && error.response.status === 404) {
|
|
errorMsg = '该场地暂无可用时间段';
|
|
} else if (error.request) {
|
|
errorMsg = '网络连接失败,使用默认时间段';
|
|
}
|
|
|
|
uni.showToast({
|
|
title: errorMsg,
|
|
icon: 'none',
|
|
duration: 2000
|
|
});
|
|
}
|
|
},
|
|
|
|
// 选择器处理方法
|
|
onCourseSelect(e) {
|
|
console.log('onCourseSelect', e);
|
|
if (e && e.item) {
|
|
this.selectedCourse = e.item;
|
|
this.formData.course_id = e.value;
|
|
}
|
|
},
|
|
|
|
onClassSelect(e) {
|
|
console.log('onClassSelect', e);
|
|
if (e && e.item) {
|
|
this.selectedClass = e.item;
|
|
this.formData.class_id = e.value;
|
|
} else {
|
|
this.selectedClass = null;
|
|
this.formData.class_id = '';
|
|
}
|
|
},
|
|
|
|
onCoachSelect(e) {
|
|
console.log('onCoachSelect', e);
|
|
if (e && e.item) {
|
|
this.selectedCoach = e.item;
|
|
this.formData.coach_id = e.value;
|
|
}
|
|
},
|
|
|
|
onVenueSelect(e) {
|
|
console.log('onVenueSelect', e);
|
|
if (e && e.item) {
|
|
this.selectedVenue = e.item;
|
|
this.formData.venue_id = e.value;
|
|
|
|
// 设置容量为场地容量,如果没有场地容量则设为0
|
|
this.formData.available_capacity = this.selectedVenue.capacity || 0;
|
|
|
|
// 如果已选择日期,则重新加载时间段
|
|
if (this.formData.course_date) {
|
|
this.loadTimeSlots();
|
|
}
|
|
} else {
|
|
// 如果未选择场地,容量设为0
|
|
this.formData.available_capacity = 0;
|
|
}
|
|
},
|
|
|
|
onDateSelect(e) {
|
|
this.formData.course_date = e.result;
|
|
this.showDatePicker = false;
|
|
|
|
// 如果已选择场地,则重新加载时间段
|
|
if (this.formData.venue_id) {
|
|
this.loadTimeSlots();
|
|
}
|
|
},
|
|
|
|
onTimeSelect(e) {
|
|
console.log('onTimeSelect', e);
|
|
if (e && e.item) {
|
|
this.formData.time_slot = e.value;
|
|
}
|
|
},
|
|
|
|
// 检查教练时间冲突
|
|
async checkCoachTimeConflict() {
|
|
if (!this.formData.coach_id || !this.formData.course_date || !this.formData.time_slot) {
|
|
return true; // 如果信息不完整,无需检查
|
|
}
|
|
|
|
try {
|
|
const res = await api.checkCoachConflict({
|
|
coach_id: this.formData.coach_id,
|
|
date: this.formData.course_date,
|
|
time_slot: this.formData.time_slot,
|
|
exclude_schedule_id: this.formData.id || 0 // 如果是编辑则排除当前课程
|
|
});
|
|
|
|
if (res.code === 1) {
|
|
if (res.data.has_conflict) {
|
|
uni.showModal({
|
|
title: '时间冲突提示',
|
|
content: `教练“${this.selectedCoach.name}”在选择的时间段已有其他课程安排,是否仍要继续?`,
|
|
confirmText: '继续添加',
|
|
cancelText: '重新选择',
|
|
success: (modalRes) => {
|
|
if (modalRes.confirm) {
|
|
this.submitForm(true); // 强制继续提交
|
|
}
|
|
}
|
|
});
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
} catch (error) {
|
|
console.error('检查教练时间冲突失败:', error);
|
|
return true; // 出错时允许继续,不阻止提交
|
|
}
|
|
},
|
|
|
|
// 表单验证
|
|
validateForm() {
|
|
if (!this.formData.course_id) {
|
|
uni.showToast({
|
|
title: '请选择课程',
|
|
icon: 'none'
|
|
});
|
|
return false;
|
|
}
|
|
|
|
if (!this.formData.coach_id) {
|
|
uni.showToast({
|
|
title: '请选择授课教练',
|
|
icon: 'none'
|
|
});
|
|
return false;
|
|
}
|
|
|
|
if (!this.formData.venue_id) {
|
|
uni.showToast({
|
|
title: '请选择上课场地',
|
|
icon: 'none'
|
|
});
|
|
return false;
|
|
}
|
|
|
|
if (!this.formData.course_date) {
|
|
uni.showToast({
|
|
title: '请选择上课日期',
|
|
icon: 'none'
|
|
});
|
|
return false;
|
|
}
|
|
|
|
if (!this.formData.time_slot) {
|
|
uni.showToast({
|
|
title: '请选择上课时间',
|
|
icon: 'none'
|
|
});
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
// 提交表单
|
|
async submitForm(ignoreConflict = false) {
|
|
if (!this.validateForm()) {
|
|
return;
|
|
}
|
|
|
|
// 如果未忽略冲突检查,先检查教练时间冲突
|
|
if (!ignoreConflict) {
|
|
const noConflict = await this.checkCoachTimeConflict();
|
|
if (!noConflict) {
|
|
return; // 如果有冲突且用户未选择继续,停止提交
|
|
}
|
|
}
|
|
|
|
this.submitting = true;
|
|
|
|
try {
|
|
// 准备提交的数据,确保字段名与API接口匹配
|
|
const submitData = {
|
|
campus_id: 1, // 默认校区ID,实际项目中应从用户信息获取
|
|
venue_id: this.formData.venue_id,
|
|
course_date: this.formData.course_date,
|
|
time_slot: this.formData.time_slot,
|
|
course_id: this.formData.course_id,
|
|
coach_id: this.formData.coach_id,
|
|
available_capacity: parseInt(this.formData.available_capacity),
|
|
class_id: this.formData.class_id || 0, // 可选字段
|
|
remarks: this.formData.remark || '', // 字段名转换
|
|
created_by: 'manual'
|
|
};
|
|
|
|
console.log('提交数据:', submitData);
|
|
|
|
const res = await api.createCourseSchedule(submitData);
|
|
|
|
if (res.code === 1) {
|
|
uni.showToast({
|
|
title: '创建成功',
|
|
icon: 'success'
|
|
});
|
|
|
|
// 延迟返回,让用户看到成功提示
|
|
setTimeout(() => {
|
|
// 返回上一页并发送刷新信号
|
|
const pages = getCurrentPages();
|
|
const prevPage = pages[pages.length - 2];
|
|
if (prevPage && prevPage.$vm && prevPage.$vm.loadScheduleList) {
|
|
// 返回并传递刷新信号
|
|
uni.navigateBack({
|
|
success: function() {
|
|
prevPage.$vm.loadScheduleList();
|
|
}
|
|
});
|
|
} else {
|
|
uni.navigateBack();
|
|
}
|
|
}, 1500);
|
|
} else {
|
|
uni.showToast({
|
|
title: res.msg || '创建失败',
|
|
icon: 'none'
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error('创建课程安排失败:', error);
|
|
|
|
// 提供更详细的错误信息
|
|
let errorMsg = '创建失败,请重试';
|
|
if (error.response) {
|
|
if (error.response.status === 401) {
|
|
errorMsg = '登录已过期,请重新登录';
|
|
} else if (error.response.status === 403) {
|
|
errorMsg = '没有权限执行此操作';
|
|
} else if (error.response.status >= 500) {
|
|
errorMsg = '服务器内部错误,请联系管理员';
|
|
} else {
|
|
errorMsg = `请求失败: ${error.response.status}`;
|
|
}
|
|
} else if (error.request) {
|
|
errorMsg = '网络连接失败,请检查网络后重试';
|
|
} else {
|
|
errorMsg = error.message || '创建失败,请重试';
|
|
}
|
|
|
|
uni.showToast({
|
|
title: errorMsg,
|
|
icon: 'none',
|
|
duration: 3000
|
|
});
|
|
} finally {
|
|
this.submitting = false;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.add-schedule-container {
|
|
min-height: 100vh;
|
|
background-color: #18181c;
|
|
}
|
|
|
|
.form-container {
|
|
padding: 30rpx;
|
|
}
|
|
|
|
.selector-input {
|
|
height: 80rpx;
|
|
background-color: #23232a;
|
|
border-radius: 8rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 24rpx;
|
|
font-size: 28rpx;
|
|
color: #fff;
|
|
}
|
|
|
|
.capacity-container {
|
|
height: 80rpx;
|
|
background-color: #23232a;
|
|
border-radius: 8rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0 24rpx;
|
|
font-size: 28rpx;
|
|
}
|
|
|
|
.capacity-text {
|
|
color: #fff;
|
|
font-size: 28rpx;
|
|
}
|
|
|
|
.capacity-hint {
|
|
color: #999999;
|
|
font-size: 24rpx;
|
|
margin-left: 16rpx;
|
|
}
|
|
|
|
.btn-container {
|
|
margin-top: 60rpx;
|
|
padding: 0 30rpx;
|
|
}
|
|
</style>
|