diff --git a/uniapp/api/apiRoute.js b/uniapp/api/apiRoute.js index 73c3c5df..31c3852b 100644 --- a/uniapp/api/apiRoute.js +++ b/uniapp/api/apiRoute.js @@ -1277,6 +1277,11 @@ export default { return await http.post('/student/attendance/cancel', data) }, + // 将等待位学员转为正式课学员(保持接口兼容性) + async convertWaitingToFormal(data = {}) { + return await http.post('/course/convertWaitingToFormal', data) + }, + //↓↓↓↓↓↓↓↓↓↓↓↓-----学员合同管理相关接口-----↓↓↓↓↓↓↓↓↓↓↓↓ // 获取学员合同列表 diff --git a/uniapp/components/schedule/ScheduleDetail.vue b/uniapp/components/schedule/ScheduleDetail.vue index 9b787adb..5fa535be 100644 --- a/uniapp/components/schedule/ScheduleDetail.vue +++ b/uniapp/components/schedule/ScheduleDetail.vue @@ -110,10 +110,16 @@ 等待位 ({{ waitingStudents.length }}人) + + 点击卡片转为正式课学员 + + + + 待续费 @@ -200,6 +206,30 @@ + + + + + + + + 是否将等待位学员 {{ upgradeStudent.name }} 升级为正式学员? + + + 升级后将占用一个正式位 + + + + + + 取消 + + + 确定升级 + + + + @@ -222,11 +252,11 @@ // 分离正式学员和等待位学员 formalStudents() { if (!this.scheduleInfo || !this.scheduleInfo.students) return []; - return this.scheduleInfo.students.filter(student => student.course_type !== 3); + return this.scheduleInfo.students.filter(student => student.schedule_type === 1 || student.schedule_type === null); }, waitingStudents() { if (!this.scheduleInfo || !this.scheduleInfo.students) return []; - return this.scheduleInfo.students.filter(student => student.course_type === 3); + return this.scheduleInfo.students.filter(student => student.schedule_type === 2); }, statusClass() { const statusMap = { @@ -271,7 +301,10 @@ scheduleInfo: null, showAttendanceModal: false, selectedStudent: null, - selectedStudentIndex: -1 + selectedStudentIndex: -1, + showUpgradeConfirm: false, + upgradeStudent: null, + upgradeStudentIndex: -1 } }, watch: { @@ -400,9 +433,90 @@ // 学员点击处理 handleStudentClick(student, index) { - this.selectedStudent = student; - this.selectedStudentIndex = index; - this.showAttendanceModal = true; + console.log('点击了学员:', student) + // 检查是否是等待位学员 + if (student.schedule_type === 2) { + // 等待位学员 - 询问是否转为正式课 + this.handleWaitingStudentClick(student, index); + } else { + // 正式学员 - 进行签到/请假操作 + this.selectedStudent = student; + this.selectedStudentIndex = index; + this.showAttendanceModal = true; + } + }, + + // 处理等待位学员点击 + handleWaitingStudentClick(student, index) { + this.upgradeStudent = student; + this.upgradeStudentIndex = index; + this.showUpgradeConfirm = true; + }, + + // 确认升级等待位学员 + confirmUpgrade() { + this.showUpgradeConfirm = false; + if (this.upgradeStudent && this.upgradeStudentIndex >= 0) { + this.convertWaitingToFormal(this.upgradeStudent, this.upgradeStudentIndex); + } + }, + + // 取消升级等待位学员 + cancelUpgrade() { + this.showUpgradeConfirm = false; + this.upgradeStudent = null; + this.upgradeStudentIndex = -1; + }, + + // 将等待位学员转为正式课学员 + async convertWaitingToFormal(student, index) { + try { + uni.showLoading({ + title: '升级中...' + }); + + // 构建升级参数,参考class_arrangement_detail页面的逻辑 + 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 // 等待位课程类型改为正式课 + }; + + // 调用升级接口 + const response = await api.upgradeStudentSchedule(upgradeData); + + if (response.code === 1) { + // 转换成功,更新学员类型 + student.course_type = 1; // 改为正式学员 + student.courseStatus = '正式课'; + student.courseType = 'temporary'; // 临时课 + + // 重新获取课程详情以更新学员列表 + await this.fetchScheduleDetail(); + + uni.showToast({ + title: '升级成功', + icon: 'success' + }); + } else { + uni.showToast({ + title: response.msg || '升级失败', + icon: 'none' + }); + } + } catch (error) { + console.error('升级等待位学员失败:', error); + uni.showToast({ + title: '升级失败,请重试', + icon: 'none' + }); + } finally { + uni.hideLoading(); + } }, // 关闭点名弹窗 @@ -648,6 +762,21 @@ font-weight: 500; } + .waiting-tip { + display: flex; + align-items: center; + padding: 8rpx 16rpx; + background: rgba(139, 92, 246, 0.2); + border-radius: 6rpx; + border: 1px solid rgba(139, 92, 246, 0.3); + } + + .tip-text { + font-size: 22rpx; + color: #8b5cf6; + font-weight: 500; + } + .info-item { display: flex; margin-bottom: 16rpx; @@ -889,6 +1018,39 @@ .student-card.waiting-filled { border-color: #8b5cf6; background: #2a2a3a; + position: relative; + } + + /* 转换提示图标 */ + .convert-icon { + position: absolute; + top: 8rpx; + left: 8rpx; + width: 32rpx; + height: 32rpx; + background: linear-gradient(45deg, #8b5cf6, #a855f7); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 16rpx; + z-index: 5; + animation: pulse 2s infinite; + } + + @keyframes pulse { + 0% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.1); + opacity: 0.8; + } + 100% { + transform: scale(1); + opacity: 1; + } } .student-card:active { @@ -1023,4 +1185,100 @@ border-radius: 4rpx; transition: width 0.3s ease; } + + /* 升级确认弹窗样式 */ + .upgrade-confirm-modal { + padding: 40rpx 30rpx; + text-align: center; + } + + .confirm-content { + margin-bottom: 40rpx; + } + + .upgrade-icon { + font-size: 60rpx; + margin-bottom: 20rpx; + background: linear-gradient(45deg, #8b5cf6, #a855f7); + background-clip: text; + -webkit-background-clip: text; + color: transparent; + animation: glow 2s ease-in-out infinite alternate; + } + + @keyframes glow { + from { + filter: drop-shadow(0 0 5rpx #8b5cf6); + } + to { + filter: drop-shadow(0 0 15rpx #a855f7); + } + } + + .confirm-text { + font-size: 32rpx; + color: #fff; + line-height: 1.5; + margin-bottom: 16rpx; + } + + .student-name-highlight { + color: #8b5cf6; + font-weight: bold; + } + + .confirm-tip { + font-size: 26rpx; + color: #999; + line-height: 1.4; + } + + .confirm-buttons { + display: flex; + gap: 20rpx; + justify-content: center; + } + + .confirm-btn { + flex: 1; + padding: 24rpx 32rpx; + border-radius: 12rpx; + cursor: pointer; + transition: all 0.3s ease; + text-align: center; + max-width: 200rpx; + } + + .confirm-btn text { + font-size: 28rpx; + font-weight: 600; + } + + .cancel-btn { + background: #4a4a4a; + border: 1px solid #666; + } + + .cancel-btn text { + color: #ccc; + } + + .cancel-btn:active { + background: #5a5a5a; + transform: scale(0.98); + } + + .upgrade-btn { + background: linear-gradient(45deg, #8b5cf6, #a855f7); + border: 1px solid #8b5cf6; + } + + .upgrade-btn text { + color: #fff; + } + + .upgrade-btn:active { + background: linear-gradient(45deg, #7c3aed, #9333ea); + transform: scale(0.98); + } \ No newline at end of file diff --git a/uniapp/pages-coach/coach/schedule/adjust_course.vue b/uniapp/pages-coach/coach/schedule/adjust_course.vue index 7b2caf27..3afa3e1e 100644 --- a/uniapp/pages-coach/coach/schedule/adjust_course.vue +++ b/uniapp/pages-coach/coach/schedule/adjust_course.vue @@ -31,10 +31,29 @@ 上课场地: {{ scheduleInfo.venue_name }} + + 所属班级: + {{ scheduleInfo.class_info ? scheduleInfo.class_info.class_name : '未指定班级' }} + 调整后信息 + + + + + {{ selectedClass ? selectedClass.class_name : (scheduleInfo.class_info ? scheduleInfo.class_info.class_name : '请选择班级') }} + + + + + classItem.id === this.scheduleInfo.class_id); + this.classPickerIndex = this.classOptions.findIndex(classItem => classItem.id === this.scheduleInfo.class_id); + if (this.classPickerIndex === -1) this.classPickerIndex = 0; + } + // 查找当前教练 if (this.scheduleInfo.coach_id) { this.selectedCoach = this.coachOptions.find(coach => coach.id === this.scheduleInfo.coach_id); @@ -276,6 +310,15 @@ export default { }, // 选择器事件处理 + onClassSelect(e) { + const index = e.detail.value; + this.classPickerIndex = index; + if (index >= 0 && index < this.classOptions.length) { + this.selectedClass = this.classOptions[index]; + this.formData.class_id = this.selectedClass.id; + } + }, + onCoachSelect(e) { const index = e.detail.value; this.coachPickerIndex = index; @@ -317,7 +360,8 @@ export default { // 表单验证 validateForm() { // 检查是否有任何修改 - const hasChanges = this.formData.coach_id !== this.scheduleInfo.coach_id || + const hasChanges = this.formData.class_id !== this.scheduleInfo.class_id || + this.formData.coach_id !== this.scheduleInfo.coach_id || this.formData.venue_id !== this.scheduleInfo.venue_id || this.formData.course_date !== this.scheduleInfo.course_date || this.formData.time_slot !== this.scheduleInfo.time_slot ||