|
|
|
@ -329,7 +329,7 @@ |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 获取课程安排详情 |
|
|
|
// 获取课程安排详情(使用统一API - 对接admin端) |
|
|
|
async fetchScheduleDetail() { |
|
|
|
if (!this.scheduleId) { |
|
|
|
this.error = true; |
|
|
|
@ -342,8 +342,8 @@ |
|
|
|
this.scheduleInfo = null; |
|
|
|
|
|
|
|
try { |
|
|
|
// 调用真实API获取课程安排详情和学员信息 |
|
|
|
const res = await api.getCourseScheduleInfo({ |
|
|
|
// 调用新的统一API获取课程安排详情,与admin端保持一致 |
|
|
|
const res = await api.getCourseArrangementDetail({ |
|
|
|
schedule_id: this.scheduleId |
|
|
|
}); |
|
|
|
|
|
|
|
@ -351,57 +351,114 @@ |
|
|
|
// 处理课程安排基本信息 |
|
|
|
const data = res.data; |
|
|
|
|
|
|
|
// 使用新的API数据结构,data直接包含所有信息 |
|
|
|
this.scheduleInfo = { |
|
|
|
// 基本课程信息 |
|
|
|
id: data.id, |
|
|
|
course_name: data.course_name || '未命名课程', |
|
|
|
course_date: data.course_date, |
|
|
|
time_slot: data.time_slot, |
|
|
|
venue_name: data.venue_name || '未分配', |
|
|
|
campus_name: data.campus_name || '', |
|
|
|
// 教练信息 |
|
|
|
coach_name: data.coach_name || '未分配', |
|
|
|
coach_avatar: data.coach_avatar || '', |
|
|
|
// 课程状态 |
|
|
|
status: data.status, |
|
|
|
status_text: data.status_text || '待定', |
|
|
|
// 班级信息 |
|
|
|
class_info: data.class_info || null, |
|
|
|
// 时间信息 |
|
|
|
time_info: data.time_info || null, |
|
|
|
// 课程时长 |
|
|
|
course_duration: data.time_info?.duration || data.course_duration || 60, |
|
|
|
// 学员数据 |
|
|
|
students: (data.students || []).map(student => ({ |
|
|
|
...student, |
|
|
|
status_text: this.getStatusText(student.status || 0), |
|
|
|
// 确保包含课程进度数据 |
|
|
|
course_progress: student.course_progress || { |
|
|
|
total: student.totalHours || 0, |
|
|
|
used: student.usedHours || 0, |
|
|
|
remaining: student.remainingHours || 0, |
|
|
|
percentage: student.totalHours > 0 ? Math.round((student.usedHours / student.totalHours) * 100) : 0 |
|
|
|
}, |
|
|
|
// 确保包含续费和体验课标识 |
|
|
|
needsRenewal: student.needsRenewal || false, |
|
|
|
isTrialStudent: student.isTrialStudent || false, |
|
|
|
// 确保包含课程状态和类型 |
|
|
|
courseStatus: student.courseStatus || (student.person_type === 'student' ? '正式课' : '体验课'), |
|
|
|
courseType: student.schedule_type === 2 ? 'fixed' : 'temporary', |
|
|
|
// 确保包含年龄信息 |
|
|
|
age: student.age || 0, |
|
|
|
// 确保包含体验课时信息 |
|
|
|
trialClassCount: student.trialClassCount || 0, |
|
|
|
// 确保包含剩余课时和到期时间 |
|
|
|
remainingHours: student.remainingHours || 0, |
|
|
|
expiryDate: student.expiryDate || '' |
|
|
|
})), |
|
|
|
// 其他信息 |
|
|
|
available_capacity: data.available_capacity || 0, |
|
|
|
enrolled_count: data.enrolled_count || 0, |
|
|
|
remaining_capacity: data.remaining_capacity || 0 |
|
|
|
}; |
|
|
|
// 使用新的统一API数据结构,与admin端保持一致 |
|
|
|
if (data.schedule_info) { |
|
|
|
// 处理新的统一数据结构(包含schedule_info、formal_students、waiting_students) |
|
|
|
const allStudents = [...(data.formal_students || []), ...(data.waiting_students || [])]; |
|
|
|
|
|
|
|
this.scheduleInfo = { |
|
|
|
// 基本课程信息从schedule_info获取 |
|
|
|
id: data.schedule_info.id, |
|
|
|
course_name: data.schedule_info.course_name || '未命名课程', |
|
|
|
course_date: data.schedule_info.course_date, |
|
|
|
time_slot: data.schedule_info.time_slot, |
|
|
|
venue_name: data.schedule_info.venue_name || '未分配', |
|
|
|
campus_name: data.schedule_info.campus_name || '', |
|
|
|
// 教练信息 |
|
|
|
coach_name: data.schedule_info.coach_name || '未分配', |
|
|
|
coach_avatar: data.schedule_info.coach_avatar || '', |
|
|
|
// 课程状态 |
|
|
|
status: data.schedule_info.status, |
|
|
|
status_text: data.schedule_info.status_text || '待定', |
|
|
|
// 班级信息 |
|
|
|
class_info: data.schedule_info.class_info || null, |
|
|
|
// 时间信息 |
|
|
|
time_info: data.schedule_info.time_info || null, |
|
|
|
// 课程时长 |
|
|
|
course_duration: data.schedule_info.time_info?.duration || data.schedule_info.course_duration || 60, |
|
|
|
// 合并正式学员和等待位学员数据 |
|
|
|
students: allStudents.map(student => ({ |
|
|
|
...student, |
|
|
|
status_text: this.getStatusText(student.status || 0), |
|
|
|
// 确保包含课程进度数据 |
|
|
|
course_progress: student.course_progress || { |
|
|
|
total: student.totalHours || 0, |
|
|
|
used: student.usedHours || 0, |
|
|
|
remaining: student.remainingHours || 0, |
|
|
|
percentage: student.totalHours > 0 ? Math.round((student.usedHours / student.totalHours) * 100) : 0 |
|
|
|
}, |
|
|
|
// 确保包含续费和体验课标识 |
|
|
|
needsRenewal: student.needsRenewal || false, |
|
|
|
isTrialStudent: student.isTrialStudent || student.person_type !== 'student', |
|
|
|
// 确保包含课程状态和类型 |
|
|
|
courseStatus: student.courseStatus || (student.person_type === 'student' ? '正式课' : '体验课'), |
|
|
|
courseType: student.schedule_type === 2 ? 'waiting' : 'formal', |
|
|
|
// 确保包含年龄信息 |
|
|
|
age: student.age || 0, |
|
|
|
// 确保包含体验课时信息 |
|
|
|
trialClassCount: student.trialClassCount || 0, |
|
|
|
// 确保包含剩余课时和到期时间 |
|
|
|
remainingHours: student.remainingHours || student.course_progress?.remaining || 0, |
|
|
|
expiryDate: student.expiryDate || '' |
|
|
|
})), |
|
|
|
// 其他信息 |
|
|
|
available_capacity: data.available_capacity || 0, |
|
|
|
enrolled_count: allStudents.length, |
|
|
|
remaining_capacity: data.remaining_capacity || 0 |
|
|
|
}; |
|
|
|
} else { |
|
|
|
// 兼容旧的数据结构 |
|
|
|
this.scheduleInfo = { |
|
|
|
// 基本课程信息 |
|
|
|
id: data.id, |
|
|
|
course_name: data.course_name || '未命名课程', |
|
|
|
course_date: data.course_date, |
|
|
|
time_slot: data.time_slot, |
|
|
|
venue_name: data.venue_name || '未分配', |
|
|
|
campus_name: data.campus_name || '', |
|
|
|
// 教练信息 |
|
|
|
coach_name: data.coach_name || '未分配', |
|
|
|
coach_avatar: data.coach_avatar || '', |
|
|
|
// 课程状态 |
|
|
|
status: data.status, |
|
|
|
status_text: data.status_text || '待定', |
|
|
|
// 班级信息 |
|
|
|
class_info: data.class_info || null, |
|
|
|
// 时间信息 |
|
|
|
time_info: data.time_info || null, |
|
|
|
// 课程时长 |
|
|
|
course_duration: data.time_info?.duration || data.course_duration || 60, |
|
|
|
// 学员数据 |
|
|
|
students: (data.students || []).map(student => ({ |
|
|
|
...student, |
|
|
|
status_text: this.getStatusText(student.status || 0), |
|
|
|
// 确保包含课程进度数据 |
|
|
|
course_progress: student.course_progress || { |
|
|
|
total: student.totalHours || 0, |
|
|
|
used: student.usedHours || 0, |
|
|
|
remaining: student.remainingHours || 0, |
|
|
|
percentage: student.totalHours > 0 ? Math.round((student.usedHours / student.totalHours) * 100) : 0 |
|
|
|
}, |
|
|
|
// 确保包含续费和体验课标识 |
|
|
|
needsRenewal: student.needsRenewal || false, |
|
|
|
isTrialStudent: student.isTrialStudent || false, |
|
|
|
// 确保包含课程状态和类型 |
|
|
|
courseStatus: student.courseStatus || (student.person_type === 'student' ? '正式课' : '体验课'), |
|
|
|
courseType: student.schedule_type === 2 ? 'waiting' : 'formal', |
|
|
|
// 确保包含年龄信息 |
|
|
|
age: student.age || 0, |
|
|
|
// 确保包含体验课时信息 |
|
|
|
trialClassCount: student.trialClassCount || 0, |
|
|
|
// 确保包含剩余课时和到期时间 |
|
|
|
remainingHours: student.remainingHours || 0, |
|
|
|
expiryDate: student.expiryDate || '' |
|
|
|
})), |
|
|
|
// 其他信息 |
|
|
|
available_capacity: data.available_capacity || 0, |
|
|
|
enrolled_count: data.enrolled_count || 0, |
|
|
|
remaining_capacity: data.remaining_capacity || 0 |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
console.log('课程安排详情加载成功:', this.scheduleInfo); |
|
|
|
} else { |
|
|
|
@ -483,51 +540,66 @@ |
|
|
|
this.upgradeStudentIndex = -1; |
|
|
|
}, |
|
|
|
|
|
|
|
// 将等待位学员转为正式课学员 |
|
|
|
// 将等待位学员转为正式课学员(使用统一API - 对接admin端) |
|
|
|
async convertWaitingToFormal(student, index) { |
|
|
|
try { |
|
|
|
uni.showLoading({ |
|
|
|
title: '升级中...' |
|
|
|
}); |
|
|
|
|
|
|
|
// 构建升级参数,参考class_arrangement_detail页面的逻辑 |
|
|
|
// 使用新的统一升级API,与admin端保持一致 |
|
|
|
const upgradeData = { |
|
|
|
resources_id: student.resources_id || student.resource_id, |
|
|
|
schedule_id: this.scheduleId, |
|
|
|
student_id: student.student_id || student.id, |
|
|
|
from_schedule_type: 2, // 从等待位 |
|
|
|
to_schedule_type: 1, // 升级到正式位 |
|
|
|
position: this.formalStudents.length + 1, // 新的正式位位置 |
|
|
|
course_type: student.course_type === 3 ? 1 : student.course_type // 等待位课程类型改为正式课 |
|
|
|
person_id: student.person_id || student.id, |
|
|
|
person_type: student.person_type || 'customer_resource', |
|
|
|
from_type: 2, // 从等待位 |
|
|
|
to_type: 1 // 到正式位 |
|
|
|
}; |
|
|
|
|
|
|
|
// 调用升级接口 |
|
|
|
const response = await api.upgradeStudentSchedule(upgradeData); |
|
|
|
// 根据学员类型添加对应的ID |
|
|
|
if (student.person_type === 'student') { |
|
|
|
upgradeData.student_id = student.student_id || student.id; |
|
|
|
} else { |
|
|
|
upgradeData.resources_id = student.resources_id || student.resource_id || student.id; |
|
|
|
} |
|
|
|
|
|
|
|
if (response.code === 1) { |
|
|
|
// 转换成功,更新学员类型 |
|
|
|
student.course_type = 1; // 改为正式学员 |
|
|
|
student.courseStatus = '正式课'; |
|
|
|
student.courseType = 'temporary'; // 临时课 |
|
|
|
console.log('升级学员数据:', upgradeData); |
|
|
|
|
|
|
|
// 调用新的统一升级接口 |
|
|
|
const response = await api.upgradeStudentInArrangement(upgradeData); |
|
|
|
|
|
|
|
if (response.code === 1) { |
|
|
|
// 重新获取课程详情以更新学员列表 |
|
|
|
await this.fetchScheduleDetail(); |
|
|
|
|
|
|
|
uni.showToast({ |
|
|
|
title: '升级成功', |
|
|
|
icon: 'success' |
|
|
|
title: `学员 ${student.name} 升级成功!已移至正式位`, |
|
|
|
icon: 'success', |
|
|
|
duration: 3000 |
|
|
|
}); |
|
|
|
} else { |
|
|
|
uni.showToast({ |
|
|
|
title: response.msg || '升级失败', |
|
|
|
icon: 'none' |
|
|
|
icon: 'none', |
|
|
|
duration: 3000 |
|
|
|
}); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('升级等待位学员失败:', error); |
|
|
|
|
|
|
|
let errorMessage = '升级失败,请重试'; |
|
|
|
if (error.message) { |
|
|
|
if (error.message.includes('timeout')) { |
|
|
|
errorMessage = '升级超时,请重试'; |
|
|
|
} else if (error.message.includes('Network')) { |
|
|
|
errorMessage = '网络异常,请检查网络连接'; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
uni.showToast({ |
|
|
|
title: '升级失败,请重试', |
|
|
|
icon: 'none' |
|
|
|
title: errorMessage, |
|
|
|
icon: 'none', |
|
|
|
duration: 3000 |
|
|
|
}); |
|
|
|
} finally { |
|
|
|
uni.hideLoading(); |
|
|
|
@ -541,13 +613,13 @@ |
|
|
|
this.selectedStudentIndex = -1; |
|
|
|
}, |
|
|
|
|
|
|
|
// 处理点名操作 |
|
|
|
// 处理点名操作(使用统一API - 对接admin端) |
|
|
|
async handleAttendanceAction(action) { |
|
|
|
if (!this.selectedStudent) return; |
|
|
|
|
|
|
|
const actionMap = { |
|
|
|
'sign_in': { status: 1, text: '已签到', apiMethod: 'studentCheckin' }, |
|
|
|
'leave': { status: 2, text: '请假', apiMethod: 'studentLeave' } |
|
|
|
'sign_in': { status: 1, text: '已签到' }, |
|
|
|
'leave': { status: 2, text: '请假' } |
|
|
|
}; |
|
|
|
|
|
|
|
if (actionMap[action]) { |
|
|
|
@ -556,39 +628,31 @@ |
|
|
|
title: '处理中...' |
|
|
|
}); |
|
|
|
|
|
|
|
// 准备API调用参数 |
|
|
|
const apiData = { |
|
|
|
// 使用新的统一状态更新API,与admin端保持一致 |
|
|
|
const updateData = { |
|
|
|
schedule_id: this.scheduleId, |
|
|
|
person_id: this.selectedStudent.person_id || this.selectedStudent.id, |
|
|
|
person_type: this.selectedStudent.person_type || 'student', |
|
|
|
status: actionMap[action].status, |
|
|
|
reason: `移动端${actionMap[action].text}操作` |
|
|
|
}; |
|
|
|
|
|
|
|
// 根据学员类型传递不同的ID |
|
|
|
if (this.selectedStudent.student_id && this.selectedStudent.student_id > 0) { |
|
|
|
// 正式学员 |
|
|
|
apiData.student_id = this.selectedStudent.student_id; |
|
|
|
} else if (this.selectedStudent.resources_id && this.selectedStudent.resources_id > 0) { |
|
|
|
// 客户资源 |
|
|
|
apiData.resources_id = this.selectedStudent.resources_id; |
|
|
|
// 根据学员类型添加对应的ID |
|
|
|
if (this.selectedStudent.person_type === 'student') { |
|
|
|
updateData.student_id = this.selectedStudent.student_id || this.selectedStudent.id; |
|
|
|
} else { |
|
|
|
// 如果都没有,尝试使用id字段 |
|
|
|
if (this.selectedStudent.person_type === 'student') { |
|
|
|
apiData.student_id = this.selectedStudent.id; |
|
|
|
} else { |
|
|
|
apiData.resources_id = this.selectedStudent.id; |
|
|
|
} |
|
|
|
updateData.resources_id = this.selectedStudent.resources_id || this.selectedStudent.resource_id || this.selectedStudent.id; |
|
|
|
} |
|
|
|
|
|
|
|
// 添加调试信息 |
|
|
|
console.log('签到API调用参数:', apiData); |
|
|
|
console.log('selectedStudent:', this.selectedStudent); |
|
|
|
|
|
|
|
// 如果是请假,可以添加请假备注(暂时为空,后续可扩展) |
|
|
|
// 如果是请假,可以添加请假备注 |
|
|
|
if (action === 'leave') { |
|
|
|
apiData.remark = ''; |
|
|
|
updateData.reason = '移动端请假操作'; |
|
|
|
} |
|
|
|
|
|
|
|
// 调用对应的API |
|
|
|
const response = await api[actionMap[action].apiMethod](apiData); |
|
|
|
console.log('更新学员状态数据:', updateData); |
|
|
|
|
|
|
|
// 调用新的统一状态更新接口 |
|
|
|
const response = await api.updateStudentStatusInArrangement(updateData); |
|
|
|
|
|
|
|
if (response.code === 1) { |
|
|
|
// API调用成功,更新前端状态 |
|
|
|
@ -611,21 +675,34 @@ |
|
|
|
}); |
|
|
|
|
|
|
|
uni.showToast({ |
|
|
|
title: `${this.selectedStudent.name} ${actionMap[action].text}`, |
|
|
|
icon: 'success' |
|
|
|
title: `${this.selectedStudent.name} ${actionMap[action].text}成功`, |
|
|
|
icon: 'success', |
|
|
|
duration: 3000 |
|
|
|
}); |
|
|
|
} else { |
|
|
|
// API调用失败 |
|
|
|
uni.showToast({ |
|
|
|
title: response.msg || `${actionMap[action].text}失败`, |
|
|
|
icon: 'none' |
|
|
|
icon: 'none', |
|
|
|
duration: 3000 |
|
|
|
}); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error(`${action} API调用失败:`, error); |
|
|
|
|
|
|
|
let errorMessage = `${actionMap[action].text}失败,请重试`; |
|
|
|
if (error.message) { |
|
|
|
if (error.message.includes('timeout')) { |
|
|
|
errorMessage = `${actionMap[action].text}超时,请重试`; |
|
|
|
} else if (error.message.includes('Network')) { |
|
|
|
errorMessage = '网络异常,请检查网络连接'; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
uni.showToast({ |
|
|
|
title: `${actionMap[action].text}失败,请重试`, |
|
|
|
icon: 'none' |
|
|
|
title: errorMessage, |
|
|
|
icon: 'none', |
|
|
|
duration: 3000 |
|
|
|
}); |
|
|
|
} finally { |
|
|
|
uni.hideLoading(); |
|
|
|
@ -654,25 +731,37 @@ |
|
|
|
return statusTextMap[status] || '未知状态'; |
|
|
|
}, |
|
|
|
|
|
|
|
// 处理安排学员按钮点击 |
|
|
|
// 处理安排学员按钮点击(支持统一学员管理功能) |
|
|
|
handleArrangeStudent() { |
|
|
|
// 跳转到课程安排详情页面进行学员管理 |
|
|
|
const url = `/pages-market/clue/class_arrangement_detail?schedule_id=${this.scheduleId}`; |
|
|
|
console.log('跳转到学员管理页面:', url); |
|
|
|
uni.navigateTo({ |
|
|
|
url: url, |
|
|
|
success: () => { |
|
|
|
// 关闭当前弹窗 |
|
|
|
this.closePopup(); |
|
|
|
}, |
|
|
|
fail: (error) => { |
|
|
|
console.error('跳转到学员管理页面失败:', error); |
|
|
|
uni.showToast({ |
|
|
|
title: '跳转失败,请重试', |
|
|
|
icon: 'none' |
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
|
try { |
|
|
|
// 跳转到课程安排详情页面进行学员管理 |
|
|
|
const url = `/pages-market/clue/class_arrangement_detail?schedule_id=${this.scheduleId}`; |
|
|
|
console.log('跳转到学员管理页面:', url); |
|
|
|
|
|
|
|
uni.navigateTo({ |
|
|
|
url: url, |
|
|
|
success: () => { |
|
|
|
console.log('成功跳转到学员管理页面'); |
|
|
|
// 关闭当前弹窗 |
|
|
|
this.closePopup(); |
|
|
|
}, |
|
|
|
fail: (error) => { |
|
|
|
console.error('跳转到学员管理页面失败:', error); |
|
|
|
uni.showToast({ |
|
|
|
title: '跳转失败,请重试', |
|
|
|
icon: 'none', |
|
|
|
duration: 3000 |
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
|
} catch (error) { |
|
|
|
console.error('处理安排学员按钮失败:', error); |
|
|
|
uni.showToast({ |
|
|
|
title: '操作失败,请重试', |
|
|
|
icon: 'none', |
|
|
|
duration: 3000 |
|
|
|
}); |
|
|
|
} |
|
|
|
}, |
|
|
|
} |
|
|
|
} |
|
|
|
|