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.
708 lines
16 KiB
708 lines
16 KiB
<template>
|
|
<view class="container safe-area">
|
|
<view class="search-bar" @click="showSearch = true">
|
|
<uni-icons type="search" size="22" color="#00d18c" />
|
|
<text class="search-placeholder">搜索学员...</text>
|
|
</view>
|
|
<view class="content">
|
|
<view v-if="studentList.length === 0" class="empty-box">
|
|
<image :src="$util.img('/static/icon-img/empty.png')" mode="aspectFit" class="empty-img"></image>
|
|
<text class="empty-text">暂无学员数据</text>
|
|
</view>
|
|
<view v-else class="student-list">
|
|
<view v-for="(item, index) in studentList" :key="index" class="student-item" @click="goToDetail(item)">
|
|
<view class="student-card">
|
|
<view class="student-avatar">
|
|
<image :src="item.avatar || $util.img('/static/icon-img/avatar.png')" mode="aspectFill" class="avatar-img"></image>
|
|
</view>
|
|
<view class="student-info">
|
|
<view class="student-name">{{item.name}}</view>
|
|
<view class="info-row">
|
|
<text class="info-label">所属校区:</text>
|
|
<text class="info-value">{{item.campus}}</text>
|
|
</view>
|
|
<view class="info-row">
|
|
<text class="info-label">剩余课程:</text>
|
|
<text class="info-value">{{ getRemainingCourses(item) }}节</text>
|
|
</view>
|
|
<view class="info-row">
|
|
<text class="info-label">到期时间:</text>
|
|
<text class="info-value">{{item.end_date}}</text>
|
|
</view>
|
|
</view>
|
|
<view class="arrow-right">
|
|
<uni-icons type="right" size="16" color="#CCCCCC"></uni-icons>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<!-- 搜索弹窗 -->
|
|
<view v-if="showSearch" class="search_popup_mask" @tap="showSearch=false">
|
|
<view class="search_popup_content" @tap.stop>
|
|
<view class="popup_search_content">
|
|
<view class="popup_header">
|
|
<view class="popup_title">学员筛选</view>
|
|
<view class="popup_close" @tap="showSearch=false">
|
|
<text class="close_text">✕</text>
|
|
</view>
|
|
</view>
|
|
|
|
<scroll-view :scroll-y="true" class="popup_scroll_view">
|
|
<!-- 第一筛选区域 -->
|
|
<view class="popup_filter_section">
|
|
<view class="popup_filter_row">
|
|
<view class="popup_filter_item">
|
|
<text class="popup_filter_label">学生姓名</text>
|
|
<input class="popup_filter_input" placeholder="请输入学生姓名" v-model="searchForm.name" />
|
|
</view>
|
|
<view class="popup_filter_item">
|
|
<text class="popup_filter_label">联系电话</text>
|
|
<input class="popup_filter_input" placeholder="请输入联系电话" v-model="searchForm.phone" />
|
|
</view>
|
|
</view>
|
|
|
|
<view class="popup_filter_row">
|
|
<view class="popup_filter_item">
|
|
<text class="popup_filter_label">课时数量</text>
|
|
<input class="popup_filter_input" placeholder="请输入课时数量" v-model="searchForm.lessonCount" />
|
|
</view>
|
|
<view class="popup_filter_item">
|
|
<text class="popup_filter_label">请假次数</text>
|
|
<input class="popup_filter_input" placeholder="请输入请假次数" v-model="searchForm.leaveCount" />
|
|
</view>
|
|
</view>
|
|
|
|
<view class="popup_filter_row">
|
|
<view class="popup_filter_item">
|
|
<text class="popup_filter_label">课程名称</text>
|
|
<view class="popup_filter_picker" @click="showCoursePicker = true">
|
|
{{ selectedCourseName || '请选择课程' }}
|
|
</view>
|
|
</view>
|
|
<view class="popup_filter_item">
|
|
<text class="popup_filter_label">班级</text>
|
|
<view class="popup_filter_picker" @click="showClassPicker = true">
|
|
{{ selectedClassName || '请选择班级' }}
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
|
|
<view class="popup_filter_buttons">
|
|
<view class="popup_filter_btn reset_btn" @click="resetSearch">重置</view>
|
|
<view class="popup_filter_btn search_btn" @click="doSearchAndClose">搜索</view>
|
|
<view class="popup_filter_btn close_btn" @click="closeSearch">关闭</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 选择器组件 -->
|
|
<single-picker
|
|
:show.sync="showCoursePicker"
|
|
:data="courseList"
|
|
valueKey="id"
|
|
textKey="course_name"
|
|
title="选择课程"
|
|
@change="onCourseChange"
|
|
@cancel="showCoursePicker = false"
|
|
></single-picker>
|
|
<single-picker
|
|
:show.sync="showClassPicker"
|
|
:data="classList"
|
|
valueKey="id"
|
|
:textKey="['campus_name', 'class_name']"
|
|
textSeparator="-"
|
|
title="选择班级"
|
|
@change="onClassChange"
|
|
@cancel="showClassPicker = false"
|
|
></single-picker>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import memberApi from '@/api/member.js'
|
|
import apiRoute from '@/api/apiRoute.js';
|
|
import SinglePicker from "@/components/custom-picker/single-picker.vue"
|
|
export default {
|
|
components: {
|
|
SinglePicker
|
|
},
|
|
data() {
|
|
return {
|
|
studentList: [],
|
|
showSearch: false,
|
|
showCoursePicker: false,
|
|
showClassPicker: false,
|
|
searchForm: {
|
|
name: '',
|
|
phone: '',
|
|
lessonCount: '',
|
|
leaveCount: '',
|
|
courseId: null,
|
|
classId: null,
|
|
},
|
|
selectedCourseName: '',
|
|
selectedClassName: '',
|
|
courseList: [],
|
|
classList: [],
|
|
}
|
|
},
|
|
onLoad() {
|
|
this.getStudentList();
|
|
this.getClassesList();
|
|
},
|
|
methods: {
|
|
navigateBack() {
|
|
uni.navigateBack();
|
|
},
|
|
async getStudentList() {
|
|
try {
|
|
// 使用教练端专用接口,合并搜索表单参数
|
|
const res = await apiRoute.coach_getMyStudents(this.searchForm);
|
|
console.log('获取教练端学员列表响应:', res);
|
|
if(res.code == 1) {
|
|
this.studentList = res.data || [];
|
|
console.log('教练端学员列表更新成功:', this.studentList);
|
|
} else {
|
|
console.error('API返回错误:', res);
|
|
uni.showToast({
|
|
title: res.msg || '获取学员列表失败',
|
|
icon: 'none'
|
|
});
|
|
}
|
|
}catch ( error) {
|
|
console.error('获取学员列表错误', error);
|
|
uni.showToast({
|
|
title: '获取学员列表失败',
|
|
icon: 'none'
|
|
});
|
|
return;
|
|
}
|
|
},
|
|
goToDetail(student) {
|
|
// 检查是否有resource_sharing_id
|
|
if (!student.resource_sharing_id) {
|
|
uni.showToast({
|
|
title: '该学员暂无关联的客户资源信息',
|
|
icon: 'none'
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 跳转到客户详情页面(包含学员信息)
|
|
this.$navigateToPage(`/pages-market/clue/clue_info`, {
|
|
resource_sharing_id: student.resource_sharing_id
|
|
});
|
|
},
|
|
getRemainingCourses(item) {
|
|
const totalHours = (item.total_hours || 0)
|
|
+ (item.gift_hours || 0);
|
|
const usedHours = (item.use_total_hours ||
|
|
0) + (item.use_gift_hours || 0);
|
|
return totalHours - usedHours;
|
|
},
|
|
|
|
// 输入处理方法
|
|
onNameInput(e) {
|
|
this.searchForm.name = e;
|
|
},
|
|
onPhoneInput(e) {
|
|
this.searchForm.phone = e;
|
|
},
|
|
onLessonCountInput(e) {
|
|
this.searchForm.lessonCount = e;
|
|
},
|
|
onLeaveCountInput(e) {
|
|
this.searchForm.leaveCount = e;
|
|
},
|
|
|
|
// 选择器变更处理
|
|
onCourseChange(e) {
|
|
this.searchForm.courseId = e.value;
|
|
this.selectedCourseName = e.text;
|
|
},
|
|
onClassChange(e) {
|
|
this.searchForm.classId = e.value;
|
|
this.selectedClassName = e.text;
|
|
},
|
|
|
|
// 清除选择器选中项
|
|
clearCourseSelection(e) {
|
|
e.stopPropagation(); // 阻止事件冒泡到点击打开选择器
|
|
this.searchForm.courseId = null;
|
|
this.selectedCourseName = '';
|
|
},
|
|
clearClassSelection(e) {
|
|
e.stopPropagation(); // 阻止事件冒泡到点击打开选择器
|
|
this.searchForm.classId = null;
|
|
this.selectedClassName = '';
|
|
},
|
|
|
|
// 关闭搜索窗口
|
|
closeSearch() {
|
|
this.showSearch = false;
|
|
},
|
|
|
|
// 监听遮罩层点击事件
|
|
onMaskClick() {
|
|
this.showSearch = false;
|
|
},
|
|
|
|
// 获取班级列表
|
|
async getClassesList() {
|
|
try {
|
|
const res = await apiRoute.jlGetClassesList();
|
|
if (res.code == 1) {
|
|
// 确保API返回的数据是数组格式
|
|
this.classList = res.data.classes || [];
|
|
this.courseList = res.data.course || [];
|
|
} else {
|
|
uni.showToast({
|
|
title: res.msg || '获取班级列表失败',
|
|
icon: 'none'
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error('获取班级列表错误', error);
|
|
uni.showToast({
|
|
title: '获取班级列表失败',
|
|
icon: 'none'
|
|
});
|
|
}
|
|
},
|
|
|
|
doSearch() {
|
|
// 这里可以根据 searchForm 的内容进行筛选或请求
|
|
this.showSearch = false;
|
|
this.getStudentList()
|
|
},
|
|
|
|
doSearchAndClose() {
|
|
this.doSearch();
|
|
},
|
|
|
|
resetSearch() {
|
|
this.searchForm = {
|
|
name: '',
|
|
phone: '',
|
|
lessonCount: '',
|
|
leaveCount: '',
|
|
courseId: null,
|
|
classId: null,
|
|
};
|
|
this.selectedCourseName = '';
|
|
this.selectedClassName = '';
|
|
this.getStudentList();
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.container {
|
|
background-color: #18181c;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.safe-area {
|
|
padding-top: var(--status-bar-height);
|
|
padding-bottom: 120rpx;
|
|
}
|
|
|
|
.search-bar {
|
|
display: flex;
|
|
align-items: center;
|
|
background: #23232a;
|
|
border-radius: 12rpx;
|
|
padding: 18rpx 24rpx;
|
|
margin: 24rpx 24rpx 0 24rpx;
|
|
color: #bdbdbd;
|
|
font-size: 28rpx;
|
|
box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.08);
|
|
&:active {
|
|
background: #2a2a31;
|
|
}
|
|
}
|
|
.search-placeholder {
|
|
margin-left: 12rpx;
|
|
color: #bdbdbd;
|
|
flex: 1;
|
|
}
|
|
|
|
.content {
|
|
padding: 20rpx;
|
|
}
|
|
|
|
.empty-box {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding-top: 200rpx;
|
|
|
|
.empty-img {
|
|
width: 200rpx;
|
|
height: 200rpx;
|
|
}
|
|
|
|
.empty-text {
|
|
margin-top: 20rpx;
|
|
font-size: 28rpx;
|
|
color: #999;
|
|
}
|
|
}
|
|
|
|
.student-list {
|
|
.student-item {
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.student-card {
|
|
display: flex;
|
|
align-items: center;
|
|
background-color: #23232a;
|
|
border-radius: 12rpx;
|
|
padding: 30rpx;
|
|
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.12);
|
|
transition: box-shadow 0.2s;
|
|
&:active {
|
|
box-shadow: 0 4rpx 20rpx rgba(0,255,128,0.12);
|
|
}
|
|
}
|
|
|
|
.student-avatar {
|
|
width: 120rpx;
|
|
height: 120rpx;
|
|
border-radius: 60rpx;
|
|
overflow: hidden;
|
|
margin-right: 30rpx;
|
|
|
|
.avatar-img {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
}
|
|
|
|
.student-info {
|
|
flex: 1;
|
|
}
|
|
|
|
.student-name {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
margin-bottom: 10rpx;
|
|
color: #fff;
|
|
}
|
|
|
|
.info-row {
|
|
display: flex;
|
|
font-size: 26rpx;
|
|
margin-top: 8rpx;
|
|
|
|
.info-label {
|
|
color: #bdbdbd;
|
|
}
|
|
|
|
.info-value {
|
|
color: #fff;
|
|
}
|
|
}
|
|
|
|
.arrow-right {
|
|
padding-left: 20rpx;
|
|
}
|
|
}
|
|
|
|
.popup-content {
|
|
background: #23232a;
|
|
color: #fff;
|
|
padding: 32rpx 24rpx;
|
|
border-radius: 16rpx;
|
|
min-width: 300rpx;
|
|
text-align: left;
|
|
}
|
|
.popup-title {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #00d18c;
|
|
margin-bottom: 24rpx;
|
|
text-align: center;
|
|
}
|
|
.picker-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 18rpx 0;
|
|
border-bottom: 1px solid #333;
|
|
.picker-label {
|
|
color: #bdbdbd;
|
|
font-size: 28rpx;
|
|
}
|
|
.picker-value {
|
|
color: #fff;
|
|
font-size: 28rpx;
|
|
}
|
|
}
|
|
.search-btn {
|
|
width: 100%;
|
|
background: #00d18c;
|
|
color: #fff;
|
|
border: none;
|
|
border-radius: 12rpx;
|
|
font-size: 30rpx;
|
|
padding: 20rpx 0;
|
|
margin-top: 32rpx;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.fui-picker__input {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
height: 72rpx;
|
|
padding: 0 24rpx;
|
|
background-color: #2c2c34;
|
|
border-radius: 8rpx;
|
|
|
|
text {
|
|
font-size: 28rpx;
|
|
color: #fff;
|
|
}
|
|
}
|
|
|
|
.fui-btn__box {
|
|
margin-top: 50rpx;
|
|
padding: 0 30rpx;
|
|
}
|
|
|
|
.fui-page__bd {
|
|
padding: 30rpx;
|
|
padding-top: 70rpx;
|
|
background-color: #23232a;
|
|
position: relative;
|
|
border-top-left-radius: 24rpx;
|
|
border-top-right-radius: 24rpx;
|
|
}
|
|
|
|
.fui-section__title {
|
|
font-size: 36rpx;
|
|
color: #00d18c;
|
|
font-weight: bold;
|
|
margin-top: 30rpx;
|
|
text-align: center;
|
|
}
|
|
|
|
.search-close-icon {
|
|
position: absolute;
|
|
top: 24rpx;
|
|
right: 24rpx;
|
|
z-index: 10;
|
|
padding: 10rpx;
|
|
}
|
|
|
|
.custom-picker-input {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 24rpx;
|
|
border-radius: 8rpx;
|
|
border-bottom: 2rpx solid #00d18c;
|
|
}
|
|
|
|
.picker-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10rpx;
|
|
}
|
|
|
|
// 搜索弹窗样式 - 参考 market/clue 页面
|
|
.search_popup_mask {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: rgba(0, 0, 0, 0.6);
|
|
z-index: 999;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.search_popup_content {
|
|
background: #fff;
|
|
border-bottom-left-radius: 24rpx;
|
|
border-bottom-right-radius: 24rpx;
|
|
animation: slideDown 0.3s ease-out;
|
|
width: 100%;
|
|
max-height: 80vh;
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
@keyframes slideDown {
|
|
from {
|
|
transform: translateY(-100%);
|
|
}
|
|
to {
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
// 弹窗搜索内容样式
|
|
.popup_search_content {
|
|
padding: 0;
|
|
background: #fff;
|
|
min-height: 60vh;
|
|
max-height: 80vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
border-bottom-left-radius: 24rpx;
|
|
border-bottom-right-radius: 24rpx;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.popup_header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 32rpx;
|
|
border-bottom: 1px solid #f0f0f0;
|
|
}
|
|
|
|
.popup_title {
|
|
font-size: 32rpx;
|
|
font-weight: 600;
|
|
color: #333;
|
|
}
|
|
|
|
.popup_close {
|
|
width: 60rpx;
|
|
height: 60rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
.close_text {
|
|
font-size: 32rpx;
|
|
color: #999;
|
|
}
|
|
}
|
|
|
|
.popup_scroll_view {
|
|
flex: 1;
|
|
padding: 32rpx;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.popup_filter_section {
|
|
margin-bottom: 32rpx;
|
|
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
}
|
|
|
|
.popup_filter_row {
|
|
display: flex;
|
|
gap: 20rpx;
|
|
margin-bottom: 24rpx;
|
|
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
}
|
|
|
|
.popup_filter_item {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12rpx;
|
|
|
|
&.full_width {
|
|
flex: 1;
|
|
}
|
|
|
|
.popup_filter_label {
|
|
font-size: 26rpx;
|
|
color: #666;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.popup_filter_input {
|
|
height: 72rpx;
|
|
line-height: 72rpx;
|
|
padding: 0 16rpx;
|
|
border: 1px solid #ddd;
|
|
border-radius: 8rpx;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
background: #fff;
|
|
|
|
&::placeholder {
|
|
color: #999;
|
|
}
|
|
}
|
|
|
|
.popup_filter_picker {
|
|
height: 72rpx;
|
|
line-height: 72rpx;
|
|
padding: 0 16rpx;
|
|
border: 1px solid #ddd;
|
|
border-radius: 8rpx;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
background: #fff;
|
|
position: relative;
|
|
|
|
&::after {
|
|
content: '▼';
|
|
position: absolute;
|
|
right: 16rpx;
|
|
font-size: 20rpx;
|
|
color: #999;
|
|
}
|
|
}
|
|
}
|
|
|
|
.popup_filter_buttons {
|
|
display: flex;
|
|
gap: 20rpx;
|
|
padding: 32rpx;
|
|
margin-top: auto;
|
|
border-top: 1px solid #f0f0f0;
|
|
background: #fff;
|
|
border-bottom-left-radius: 24rpx;
|
|
border-bottom-right-radius: 24rpx;
|
|
}
|
|
|
|
.popup_filter_btn {
|
|
flex: 1;
|
|
height: 72rpx;
|
|
line-height: 72rpx;
|
|
text-align: center;
|
|
border-radius: 8rpx;
|
|
font-size: 28rpx;
|
|
font-weight: 600;
|
|
|
|
&.search_btn {
|
|
background: #00d18c;
|
|
color: #fff;
|
|
}
|
|
|
|
&.reset_btn {
|
|
background: #f5f5f5;
|
|
color: #666;
|
|
border: 1px solid #ddd;
|
|
}
|
|
|
|
&.close_btn {
|
|
background: #666;
|
|
color: #fff;
|
|
}
|
|
}
|
|
</style>
|