|
|
|
@ -4,30 +4,24 @@ |
|
|
|
<view class="title">课程安排详情</view> |
|
|
|
<view class="date">日期:{{ course_info.course_date }} {{course_info.time_slot}}</view> |
|
|
|
</view> |
|
|
|
<!-- 正式学员列表 --> |
|
|
|
<view class="section"> |
|
|
|
<view class="section-title">学员列表</view> |
|
|
|
<view class="student-list" v-if="course_info && course_info.available_capacity"> |
|
|
|
<!-- 显示已安排的学员 --> |
|
|
|
<view |
|
|
|
v-for="(stu, idx) in students" |
|
|
|
:key="idx" |
|
|
|
class="student-item" |
|
|
|
@tap="viewStudent(stu)" |
|
|
|
> |
|
|
|
<view class="section-title">正式学员</view> |
|
|
|
<view class="student-list" v-if="course_info"> |
|
|
|
<!-- 显示已安排的正式学员 --> |
|
|
|
<view v-for="(stu, idx) in formalStudents" :key="idx" class="student-item" |
|
|
|
@tap="viewStudent(stu)"> |
|
|
|
<view class="avatar">{{ stu.name && stu.name.charAt(0) }}</view> |
|
|
|
<view class="info"> |
|
|
|
<view class="name">{{ stu.name }}</view> |
|
|
|
<view class="desc">{{ getStatusText(stu.status) }}</view> |
|
|
|
<view class="desc">{{ getCourseTypeText(stu.course_type) }} | {{ getStatusText(stu.status) }} |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 显示空位 --> |
|
|
|
<view |
|
|
|
v-for="index in emptySeats" |
|
|
|
:key="index" |
|
|
|
class="student-item empty" |
|
|
|
@tap="addStudent($event, index)" |
|
|
|
> |
|
|
|
<!-- 显示正式学员空位 --> |
|
|
|
<view v-for="index in formalEmptySeats" :key="index" class="student-item empty" |
|
|
|
@tap="addStudent($event, index, 'formal')"> |
|
|
|
<view class="avatar empty-avatar">+</view> |
|
|
|
<view class="info"> |
|
|
|
<view class="name">空位</view> |
|
|
|
@ -42,16 +36,46 @@ |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 等待位学员列表 --> |
|
|
|
<view class="section"> |
|
|
|
<view class="section-title waiting-title">等待位</view> |
|
|
|
<view class="student-list"> |
|
|
|
<!-- 显示等待位学员 --> |
|
|
|
<view v-for="(stu, idx) in waitingStudents" :key="idx" class="student-item waiting" |
|
|
|
@tap="viewStudent(stu)"> |
|
|
|
<view class="avatar waiting-avatar">{{ stu.name && stu.name.charAt(0) }}</view> |
|
|
|
<view class="info"> |
|
|
|
<view class="name">{{ stu.name }}</view> |
|
|
|
<view class="desc">{{ getCourseTypeText(stu.course_type) }} | {{ getStatusText(stu.status) }} |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 等待位空位(固定2个) --> |
|
|
|
<view v-for="index in waitingEmptySeats" :key="index" |
|
|
|
class="student-item empty waiting" @tap="addStudent($event, index, 'waiting')"> |
|
|
|
<view class="avatar empty-avatar waiting-avatar">+</view> |
|
|
|
<view class="info"> |
|
|
|
<view class="name">等待位</view> |
|
|
|
<view class="desc">点击添加学员</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 请假原因弹窗 --> |
|
|
|
<fui-modal ref="leaveReasonModal" :buttons="[]" width="600" title="请假申请"> |
|
|
|
<view class="leave-form"> |
|
|
|
<view class="leave-label">请假原因</view> |
|
|
|
<view class="leave-input"> |
|
|
|
<fui-textarea v-model="leaveReason" placeholder="请输入请假原因" :isCounter="true" :maxlength="200" :minHeight="200" :isAutoHeight="true"></fui-textarea> |
|
|
|
<fui-textarea v-model="leaveReason" placeholder="请输入请假原因" :isCounter="true" :maxlength="200" |
|
|
|
:minHeight="200" :isAutoHeight="true"></fui-textarea> |
|
|
|
</view> |
|
|
|
<view class="leave-buttons"> |
|
|
|
<fui-button background="#434544" color="#fff" borderColor="#666" btnSize="medium" @tap="$refs.leaveReasonModal.close()">取消</fui-button> |
|
|
|
<fui-button background="#29d3b4" color="#fff" btnSize="medium" @tap="submitLeaveRequest">提交</fui-button> |
|
|
|
<fui-button background="#434544" color="#fff" borderColor="#666" btnSize="medium" |
|
|
|
@tap="$refs.leaveReasonModal.close()">取消</fui-button> |
|
|
|
<fui-button background="#29d3b4" color="#fff" btnSize="medium" |
|
|
|
@tap="submitLeaveRequest">提交</fui-button> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</fui-modal> |
|
|
|
@ -63,23 +87,17 @@ |
|
|
|
export default { |
|
|
|
data() { |
|
|
|
return { |
|
|
|
course_id:'', |
|
|
|
course_info:[], |
|
|
|
course_id: '', |
|
|
|
course_info: [], |
|
|
|
date: '', |
|
|
|
students: [ |
|
|
|
// { |
|
|
|
// name: '张三', |
|
|
|
// desc: '已签到' |
|
|
|
// }, |
|
|
|
// { |
|
|
|
// name: '李四', |
|
|
|
// desc: '未签到' |
|
|
|
// }, |
|
|
|
], |
|
|
|
resource_id:'', |
|
|
|
students: [], // 所有学员数据 |
|
|
|
formalStudents: [], // 正式学员 |
|
|
|
waitingStudents: [], // 等待位学员 |
|
|
|
resource_id: '', |
|
|
|
leaveReason: '', // 请假原因 |
|
|
|
currentStudent: null, // 当前操作的学生 |
|
|
|
emptySeats: [] // 空位数组 |
|
|
|
formalEmptySeats: [], // 正式学员空位数组 |
|
|
|
waitingEmptySeats: [1, 2] // 等待位固定2个空位 |
|
|
|
}; |
|
|
|
}, |
|
|
|
onLoad(query) { |
|
|
|
@ -90,62 +108,71 @@ |
|
|
|
this.courseInfo(); |
|
|
|
}, |
|
|
|
methods: { |
|
|
|
viewStudent(stu){ |
|
|
|
viewStudent(stu) { |
|
|
|
console.log(stu, this.course_info); |
|
|
|
|
|
|
|
// 判断学员类型 |
|
|
|
if (stu.person_type === 'customer_resource') { |
|
|
|
// 如果是客户资源,弹出确认取消课程的弹窗 |
|
|
|
uni.showModal({ |
|
|
|
title: '取消课程', |
|
|
|
content: `是否取消学员 ${stu.name} 的课程?`, |
|
|
|
success: async (res) => { |
|
|
|
if (res.confirm) { |
|
|
|
// 用户点击确定,调用schedule_del接口 |
|
|
|
try { |
|
|
|
uni.showLoading({ |
|
|
|
title: '处理中...' |
|
|
|
}); |
|
|
|
|
|
|
|
const params = { |
|
|
|
resources_id: stu.resources_id, |
|
|
|
id: this.course_info.id |
|
|
|
}; |
|
|
|
|
|
|
|
const result = await apiRoute.schedule_del(params); |
|
|
|
|
|
|
|
uni.hideLoading(); |
|
|
|
|
|
|
|
if (result.code === 1) { |
|
|
|
uni.showToast({ |
|
|
|
title: '取消课程成功', |
|
|
|
icon: 'success' |
|
|
|
}); |
|
|
|
|
|
|
|
// 刷新数据 |
|
|
|
await this.courseInfo(); |
|
|
|
} else { |
|
|
|
uni.showToast({ |
|
|
|
title: result.msg || '取消课程失败', |
|
|
|
icon: 'none' |
|
|
|
}); |
|
|
|
// 显示操作选项弹窗 |
|
|
|
uni.showActionSheet({ |
|
|
|
title: `学员: ${stu.name}`, |
|
|
|
itemList: stu.person_type === 'customer_resource' ? ['取消课程'] : ['申请请假'], |
|
|
|
success: (res) => { |
|
|
|
if (res.tapIndex === 0) { |
|
|
|
// 判断学员类型 |
|
|
|
if (stu.person_type === 'customer_resource') { |
|
|
|
// 如果是客户资源,弹出确认取消课程的弹窗 |
|
|
|
uni.showModal({ |
|
|
|
title: '取消课程', |
|
|
|
content: `是否取消学员 ${stu.name} 的课程?`, |
|
|
|
success: async (res) => { |
|
|
|
if (res.confirm) { |
|
|
|
// 用户点击确定,调用schedule_del接口 |
|
|
|
try { |
|
|
|
uni.showLoading({ |
|
|
|
title: '处理中...' |
|
|
|
}); |
|
|
|
|
|
|
|
const params = { |
|
|
|
resources_id: stu.resources_id, |
|
|
|
id: this.course_info.id |
|
|
|
}; |
|
|
|
|
|
|
|
const result = await apiRoute.schedule_del(params); |
|
|
|
|
|
|
|
uni.hideLoading(); |
|
|
|
|
|
|
|
if (result.code === 1) { |
|
|
|
uni.showToast({ |
|
|
|
title: '取消课程成功', |
|
|
|
icon: 'success' |
|
|
|
}); |
|
|
|
|
|
|
|
// 刷新数据 |
|
|
|
await this.courseInfo(); |
|
|
|
} else { |
|
|
|
uni.showToast({ |
|
|
|
title: result.msg || '取消课程失败', |
|
|
|
icon: 'none' |
|
|
|
}); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
uni.hideLoading(); |
|
|
|
uni.showToast({ |
|
|
|
title: '操作失败,请重试', |
|
|
|
icon: 'none' |
|
|
|
}); |
|
|
|
console.error('取消课程失败:', error); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
uni.hideLoading(); |
|
|
|
uni.showToast({ |
|
|
|
title: '操作失败,请重试', |
|
|
|
icon: 'none' |
|
|
|
}); |
|
|
|
console.error('取消课程失败:', error); |
|
|
|
} |
|
|
|
}); |
|
|
|
} else if (stu.person_type === 'student') { |
|
|
|
// 如果是学生,弹出请假原因输入框 |
|
|
|
this.$refs.leaveReasonModal.open(); |
|
|
|
this.currentStudent = stu; // 保存当前操作的学生信息 |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
} else if (stu.person_type === 'student') { |
|
|
|
// 如果是学生,弹出请假原因输入框 |
|
|
|
this.$refs.leaveReasonModal.open(); |
|
|
|
this.currentStudent = stu; // 保存当前操作的学生信息 |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}, |
|
|
|
getStatusText(status) { |
|
|
|
const statusMap = { |
|
|
|
@ -155,6 +182,36 @@ |
|
|
|
}; |
|
|
|
return statusMap[status] || status; |
|
|
|
}, |
|
|
|
// 获取课程类型文本 |
|
|
|
getCourseTypeText(courseType) { |
|
|
|
const courseTypeMap = { |
|
|
|
1: '正式课', |
|
|
|
2: '体验课', |
|
|
|
3: '补课', |
|
|
|
4: '试听课' |
|
|
|
}; |
|
|
|
return courseTypeMap[courseType] || '未知'; |
|
|
|
}, |
|
|
|
// 分类学员数据 |
|
|
|
categorizeStudents() { |
|
|
|
// 根据schedule_type分类学员 |
|
|
|
// schedule_type: 1=正式位, 2=等待位 |
|
|
|
this.formalStudents = this.students.filter(stu => stu.schedule_type === 1 || stu.schedule_type === null); |
|
|
|
this.waitingStudents = this.students.filter(stu => stu.schedule_type === 2); |
|
|
|
|
|
|
|
// 确保学员名称存在,如果不存在则返回未知学员 |
|
|
|
this.formalStudents.forEach(stu => { |
|
|
|
if (!stu.name) stu.name = '未知学员'; |
|
|
|
}); |
|
|
|
this.waitingStudents.forEach(stu => { |
|
|
|
if (!stu.name) stu.name = '未知学员'; |
|
|
|
}); |
|
|
|
|
|
|
|
console.log('学员分类完成:', { |
|
|
|
formal: this.formalStudents.length, |
|
|
|
waiting: this.waitingStudents.length |
|
|
|
}); |
|
|
|
}, |
|
|
|
async scheduleList() { |
|
|
|
try { |
|
|
|
console.log('开始获取学员列表, schedule_id:', this.course_id); |
|
|
|
@ -174,8 +231,13 @@ |
|
|
|
// 否则按照ID排序 |
|
|
|
return a.id - b.id; |
|
|
|
}); |
|
|
|
|
|
|
|
// 分类学员 |
|
|
|
this.categorizeStudents(); |
|
|
|
} else { |
|
|
|
this.students = []; |
|
|
|
this.formalStudents = []; |
|
|
|
this.waitingStudents = []; |
|
|
|
} |
|
|
|
|
|
|
|
// 更新可用容量 |
|
|
|
@ -185,6 +247,8 @@ |
|
|
|
} catch (error) { |
|
|
|
console.error('获取学员列表失败:', error); |
|
|
|
this.students = []; |
|
|
|
this.formalStudents = []; |
|
|
|
this.waitingStudents = []; |
|
|
|
return []; |
|
|
|
} |
|
|
|
}, |
|
|
|
@ -221,30 +285,39 @@ |
|
|
|
}, |
|
|
|
// 更新可用容量 |
|
|
|
updateAvailableCapacity() { |
|
|
|
// 确保course_info和students都已加载 |
|
|
|
if (!this.course_info || !this.students) return; |
|
|
|
// 确保course_info已加载 |
|
|
|
if (!this.course_info) return; |
|
|
|
|
|
|
|
console.log('更新可用容量 - 课程总容量:', this.course_info.available_capacity, '学员数量:', this.students.length); |
|
|
|
console.log('更新可用容量 - 课程总容量:', this.course_info.available_capacity, '正式学员数量:', this.formalStudents.length); |
|
|
|
|
|
|
|
// 计算已占用的位置数 |
|
|
|
const occupiedSeats = this.students.length; |
|
|
|
// 计算正式位已占用的位置数 |
|
|
|
const occupiedFormalSeats = this.formalStudents.length; |
|
|
|
|
|
|
|
// 获取课程总容量 |
|
|
|
// 获取课程总容量(正式位) |
|
|
|
const totalCapacity = parseInt(this.course_info.available_capacity) || 0; |
|
|
|
|
|
|
|
// 计算可用容量 |
|
|
|
this.course_info.available_capacity = Math.max(0, totalCapacity - occupiedSeats); |
|
|
|
// 计算正式位剩余空位 |
|
|
|
const remainingFormalSeats = Math.max(0, totalCapacity - occupiedFormalSeats); |
|
|
|
|
|
|
|
console.log('计算后的正式位剩余容量:', remainingFormalSeats); |
|
|
|
|
|
|
|
console.log('计算后的可用容量:', this.course_info.available_capacity); |
|
|
|
// 初始化正式位空位数组 |
|
|
|
this.formalEmptySeats = []; |
|
|
|
for (let i = 1; i <= remainingFormalSeats; i++) { |
|
|
|
this.formalEmptySeats.push(i); |
|
|
|
} |
|
|
|
|
|
|
|
// 初始化空位数组 |
|
|
|
this.emptySeats = []; |
|
|
|
for (let i = 1; i <= this.course_info.available_capacity; i++) { |
|
|
|
this.emptySeats.push(i); |
|
|
|
// 更新等待位空位(减去已有的等待位学员) |
|
|
|
const remainingWaitingSeats = Math.max(0, 2 - this.waitingStudents.length); |
|
|
|
this.waitingEmptySeats = []; |
|
|
|
for (let i = 1; i <= remainingWaitingSeats; i++) { |
|
|
|
this.waitingEmptySeats.push(i); |
|
|
|
} |
|
|
|
|
|
|
|
console.log('等待位剩余空位:', remainingWaitingSeats); |
|
|
|
}, |
|
|
|
async addStudent(e, index) { |
|
|
|
console.log('添加学员到位置:', index); |
|
|
|
async addStudent(e, index, type = 'formal') { |
|
|
|
console.log('添加学员到位置:', index, '类型:', type); |
|
|
|
|
|
|
|
const data = { |
|
|
|
'resources_id': this.resource_id, |
|
|
|
@ -252,7 +325,9 @@ |
|
|
|
'schedule_id': this.course_id, |
|
|
|
'course_date': this.course_info.course_date, |
|
|
|
'time_slot': this.course_info.time_slot, |
|
|
|
'position': index // 添加位置信息 |
|
|
|
'position': index, // 位置信息 |
|
|
|
'schedule_type': type === 'waiting' ? 2 : 1, // 1=正式位, 2=等待位 |
|
|
|
'course_type': type === 'waiting' ? 2 : 1 // 1=正式课, 2=体验课等 |
|
|
|
}; |
|
|
|
|
|
|
|
try { |
|
|
|
@ -264,7 +339,7 @@ |
|
|
|
|
|
|
|
uni.hideLoading(); |
|
|
|
|
|
|
|
if(res.code == 1){ |
|
|
|
if (res.code == 1) { |
|
|
|
uni.showToast({ |
|
|
|
title: '添加成功', |
|
|
|
icon: 'success' |
|
|
|
@ -272,7 +347,7 @@ |
|
|
|
|
|
|
|
// 刷新数据并更新可用容量 |
|
|
|
await this.courseInfo(); |
|
|
|
}else{ |
|
|
|
} else { |
|
|
|
uni.showToast({ |
|
|
|
title: res.msg || '添加失败', |
|
|
|
icon: 'none' |
|
|
|
@ -395,7 +470,9 @@ |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
padding: 18rpx 24rpx; |
|
|
|
min-width: 260rpx; |
|
|
|
width: calc(50% - 10rpx); |
|
|
|
/* 一行两个,减去gap的一半 */ |
|
|
|
box-sizing: border-box; |
|
|
|
|
|
|
|
.avatar { |
|
|
|
width: 60rpx; |
|
|
|
@ -411,15 +488,24 @@ |
|
|
|
} |
|
|
|
|
|
|
|
.info { |
|
|
|
flex: 1; |
|
|
|
overflow: hidden; |
|
|
|
|
|
|
|
.name { |
|
|
|
color: #fff; |
|
|
|
font-size: 28rpx; |
|
|
|
white-space: nowrap; |
|
|
|
overflow: hidden; |
|
|
|
text-overflow: ellipsis; |
|
|
|
} |
|
|
|
|
|
|
|
.desc { |
|
|
|
color: #bdbdbd; |
|
|
|
font-size: 22rpx; |
|
|
|
margin-top: 4rpx; |
|
|
|
white-space: nowrap; |
|
|
|
overflow: hidden; |
|
|
|
text-overflow: ellipsis; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -441,6 +527,41 @@ |
|
|
|
color: #ffd86b; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 等待位样式 |
|
|
|
&.waiting { |
|
|
|
background: #2a2a3a; // 不同的背景色 |
|
|
|
border: 1rpx solid #8a7fff; |
|
|
|
|
|
|
|
.avatar, |
|
|
|
.avatar.waiting-avatar { |
|
|
|
background: #8a7fff; |
|
|
|
color: #fff; |
|
|
|
} |
|
|
|
|
|
|
|
&.empty { |
|
|
|
border: 2rpx dashed #8a7fff; |
|
|
|
background: #1a1a2a; |
|
|
|
|
|
|
|
.avatar.empty-avatar { |
|
|
|
background: #8a7fff; |
|
|
|
color: #1a1a2a; |
|
|
|
} |
|
|
|
|
|
|
|
.info .name { |
|
|
|
color: #8a7fff; |
|
|
|
} |
|
|
|
|
|
|
|
.info .desc { |
|
|
|
color: #8a7fff; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 等待位标题样式 |
|
|
|
.waiting-title { |
|
|
|
color: #8a7fff !important; |
|
|
|
} |
|
|
|
|
|
|
|
// 请假弹窗样式 |
|
|
|
|