27 changed files with 3101 additions and 305 deletions
@ -0,0 +1,625 @@ |
|||
<!--教练服务详情页--> |
|||
<template> |
|||
<view class="container"> |
|||
<!-- 如果没有指定ID,显示服务列表 --> |
|||
<view class="content" v-if="!id"> |
|||
<view class="service-list"> |
|||
<view class="section-title">我的服务列表</view> |
|||
<view class="empty-list" v-if="serviceList.length === 0"> |
|||
<text>暂无服务记录</text> |
|||
</view> |
|||
<view class="service-item" v-for="(item, index) in serviceList" :key="index" @click="viewServiceDetail(item.id)"> |
|||
<view class="service-header"> |
|||
<view class="user-info"> |
|||
<image class="user-avatar" src="/static/icon-img/avatar.png"></image> |
|||
<text class="user-name">{{item.service_id}} {{item.resource_id}}</text> |
|||
</view> |
|||
</view> |
|||
<view class="service-content"> |
|||
<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">{{item.source || '线上'}}</text> |
|||
</view> |
|||
<view class="info-row"> |
|||
<text class="info-label">来源渠道:</text> |
|||
<text class="info-value">{{item.channel || '地推'}}</text> |
|||
</view> |
|||
</view> |
|||
<view class="service-footer"> |
|||
<text class="contact-status">暂未联系</text> |
|||
<view class="effective-status" :class="{'status-yes': item.status === 1}"> |
|||
是否有效: {{item.status === 1 ? '有效' : '无效'}} |
|||
<view class="status-dot" :class="{'dot-yes': item.status === 1}"></view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 如果指定了ID,显示服务详情 --> |
|||
<view class="content detail-content" v-else-if="serviceInfo"> |
|||
<view class="service-detail-card"> |
|||
<view class="detail-header"> |
|||
<view class="user-info"> |
|||
<image class="user-avatar" src="/static/icon-img/avatar.png"></image> |
|||
<text class="user-name">{{serviceInfo.service_id}}</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="detail-content"> |
|||
<view class="card-section"> |
|||
<view class="section-title">基本信息</view> |
|||
<view class="info-card"> |
|||
<view class="info-item"> |
|||
<text class="item-label">服务内容</text> |
|||
<text class="item-value">{{serviceInfo.id}}</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="item-label">学员姓名</text> |
|||
<text class="item-value">{{serviceInfo.resource_id}}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="card-section"> |
|||
<view class="section-title">服务详情</view> |
|||
<view class="info-card"> |
|||
<view class="info-item"> |
|||
<text class="item-label">服务备注</text> |
|||
<text class="item-value">{{serviceInfo.service_remark || '暂无备注'}}</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="item-label">服务状态</text> |
|||
<text class="item-value" :class="{'status-completed': serviceInfo.status === 1}"> |
|||
{{serviceInfo.status === 1 ? '已完成服务' : '待完成服务'}} |
|||
</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="item-label">服务人员ID</text> |
|||
<text class="item-value">{{serviceInfo.staff_id}}</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="item-label">评分</text> |
|||
<view class="score-box"> |
|||
<view class="star-box"> |
|||
<uni-icons v-for="i in 5" :key="i" :type="i <= serviceInfo.score ? 'star-filled' : 'star'" size="18" color="#FFB800"></uni-icons> |
|||
</view> |
|||
<text class="score-value">{{serviceInfo.score}}分</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="card-section"> |
|||
<view class="section-title">用户反馈</view> |
|||
<view class="info-card"> |
|||
<view class="feedback-content" v-if="serviceInfo.feedback"> |
|||
<text>{{serviceInfo.feedback}}</text> |
|||
</view> |
|||
<view class="empty-feedback" v-else> |
|||
<text>暂无用户反馈</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="card-section"> |
|||
<view class="section-title">时间信息</view> |
|||
<view class="info-card"> |
|||
<view class="info-item"> |
|||
<text class="item-label">创建时间</text> |
|||
<text class="item-value">{{formatTime(serviceInfo.created_at)}}</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="item-label">反馈时间</text> |
|||
<text class="item-value">{{formatTime(serviceInfo.feedback_time)}}</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="item-label">更新时间</text> |
|||
<text class="item-value">{{formatTime(serviceInfo.updated_at)}}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="detail-footer" v-if="serviceInfo.status !== 1"> |
|||
<button class="btn primary" @click="completeService">完成服务</button> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view v-else class="loading-box"> |
|||
<text>加载中...</text> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js'; |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
id: null, |
|||
serviceInfo: null, |
|||
serviceList: [] |
|||
} |
|||
}, |
|||
onLoad(options) { |
|||
if (options.id) { |
|||
this.id = options.id; |
|||
this.getServiceDetail(); |
|||
} else { |
|||
this.getServiceList(); |
|||
} |
|||
}, |
|||
methods: { |
|||
navigateBack() { |
|||
uni.navigateBack(); |
|||
}, |
|||
async getServiceList() { |
|||
try { |
|||
// 接口未开发,使用模拟数据 |
|||
// const res = await apiRoute.getServiceList(); |
|||
// if(res.code == 1) { |
|||
// this.serviceList = res.data || []; |
|||
// } else { |
|||
// uni.showToast({ |
|||
// title: res.msg || '获取服务列表失败', |
|||
// icon: 'none' |
|||
// }); |
|||
// } |
|||
|
|||
// 使用模拟数据 |
|||
setTimeout(() => { |
|||
this.serviceList = [ |
|||
{ |
|||
id: '1', |
|||
resource_id: '美团用户1', |
|||
course_id: '2001', |
|||
service_id: '18645873651', |
|||
service_remark: '针对学员英语口语发音进行专项辅导', |
|||
status: 1, |
|||
staff_id: '3001', |
|||
score: 4, |
|||
feedback: '老师很耐心,讲解很清楚,孩子进步很大', |
|||
created_at: '2023-10-15 14:30:00', |
|||
feedback_time: '2023-10-16 18:20:00', |
|||
updated_at: '2023-10-16 18:20:00', |
|||
campus: '总部校区', |
|||
source: '线上', |
|||
channel: '地推' |
|||
}, |
|||
{ |
|||
id: '2', |
|||
resource_id: '李柏辉', |
|||
course_id: '2002', |
|||
service_id: '13876337043', |
|||
service_remark: '解答家长关于课程安排的问题', |
|||
status: 0, |
|||
staff_id: '3001', |
|||
score: 0, |
|||
feedback: '', |
|||
created_at: '2023-10-18 09:15:00', |
|||
feedback_time: '', |
|||
updated_at: '2023-10-18 09:15:00', |
|||
campus: '西区校区', |
|||
source: '地推', |
|||
channel: '' |
|||
}, |
|||
{ |
|||
id: '3', |
|||
resource_id: '张小明', |
|||
course_id: '2003', |
|||
service_id: '13912345678', |
|||
service_remark: '为学员制定个性化学习计划', |
|||
status: 0, |
|||
staff_id: '3001', |
|||
score: 0, |
|||
feedback: '', |
|||
created_at: '2023-10-20 16:45:00', |
|||
feedback_time: '', |
|||
updated_at: '2023-10-20 16:45:00', |
|||
campus: '东区校区', |
|||
source: '线下', |
|||
channel: '朋友介绍' |
|||
} |
|||
]; |
|||
}, 500); |
|||
} catch(error) { |
|||
console.error('获取服务列表失败:', error); |
|||
uni.showToast({ |
|||
title: '获取服务列表失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}, |
|||
async getServiceDetail() { |
|||
try { |
|||
// 接口未开发,使用模拟数据 |
|||
// const res = await apiRoute.getServiceDetail({id: this.id}); |
|||
// if(res.code == 1) { |
|||
// this.serviceInfo = res.data || null; |
|||
// } else { |
|||
// uni.showToast({ |
|||
// title: res.msg || '获取服务详情失败', |
|||
// icon: 'none' |
|||
// }); |
|||
// } |
|||
|
|||
// 使用模拟数据 |
|||
setTimeout(() => { |
|||
// 模拟数据库 |
|||
const mockServices = { |
|||
'1': { |
|||
id: '1', |
|||
resource_id: '美团用户1', |
|||
course_id: '2001', |
|||
service_id: '18645873651', |
|||
service_remark: '针对学员英语口语发音进行专项辅导', |
|||
status: 1, |
|||
staff_id: '3001', |
|||
score: 4, |
|||
feedback: '老师很耐心,讲解很清楚,孩子进步很大', |
|||
created_at: '2023-10-15 14:30:00', |
|||
feedback_time: '2023-10-16 18:20:00', |
|||
updated_at: '2023-10-16 18:20:00', |
|||
campus: '总部校区', |
|||
source: '线上', |
|||
channel: '地推' |
|||
}, |
|||
'2': { |
|||
id: '2', |
|||
resource_id: '李柏辉', |
|||
course_id: '2002', |
|||
service_id: '13876337043', |
|||
service_remark: '解答家长关于课程安排的问题', |
|||
status: 0, |
|||
staff_id: '3001', |
|||
score: 0, |
|||
feedback: '', |
|||
created_at: '2023-10-18 09:15:00', |
|||
feedback_time: '', |
|||
updated_at: '2023-10-18 09:15:00', |
|||
campus: '西区校区', |
|||
source: '地推', |
|||
channel: '' |
|||
}, |
|||
'3': { |
|||
id: '3', |
|||
resource_id: '张小明', |
|||
course_id: '2003', |
|||
service_id: '13912345678', |
|||
service_remark: '为学员制定个性化学习计划', |
|||
status: 0, |
|||
staff_id: '3001', |
|||
score: 0, |
|||
feedback: '', |
|||
created_at: '2023-10-20 16:45:00', |
|||
feedback_time: '', |
|||
updated_at: '2023-10-20 16:45:00', |
|||
campus: '东区校区', |
|||
source: '线下', |
|||
channel: '朋友介绍' |
|||
} |
|||
}; |
|||
|
|||
// 根据ID获取对应服务详情 |
|||
this.serviceInfo = mockServices[this.id] || null; |
|||
|
|||
if (!this.serviceInfo) { |
|||
uni.showToast({ |
|||
title: '未找到服务详情', |
|||
icon: 'none' |
|||
}); |
|||
setTimeout(() => { |
|||
this.navigateBack(); |
|||
}, 1500); |
|||
} |
|||
}, 500); |
|||
} catch(error) { |
|||
console.error('获取服务详情失败:', error); |
|||
uni.showToast({ |
|||
title: '获取服务详情失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}, |
|||
formatTime(timeStr) { |
|||
if (!timeStr) return '暂无'; |
|||
return timeStr; |
|||
}, |
|||
viewServiceDetail(id) { |
|||
uni.navigateTo({ |
|||
url: `/pages/coach/my/service_detail?id=${id}` |
|||
}); |
|||
}, |
|||
async completeService() { |
|||
uni.showModal({ |
|||
title: '确认完成', |
|||
content: '确定要将此服务标记为已完成吗?', |
|||
success: async (res) => { |
|||
if (res.confirm) { |
|||
try { |
|||
// 接口未开发,使用模拟数据 |
|||
// const result = await apiRoute.completeService({id: this.id}); |
|||
// if(result.code == 1) { |
|||
// this.serviceInfo.status = 1; |
|||
// uni.showToast({ |
|||
// title: '服务已完成', |
|||
// icon: 'success' |
|||
// }); |
|||
// } else { |
|||
// uni.showToast({ |
|||
// title: result.msg || '操作失败', |
|||
// icon: 'none' |
|||
// }); |
|||
// } |
|||
|
|||
// 模拟API调用 |
|||
setTimeout(() => { |
|||
this.serviceInfo.status = 1; |
|||
uni.showToast({ |
|||
title: '服务已完成', |
|||
icon: 'success' |
|||
}); |
|||
}, 500); |
|||
} catch(error) { |
|||
console.error('完成服务失败:', error); |
|||
uni.showToast({ |
|||
title: '操作失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.container { |
|||
min-height: 100vh; |
|||
height: 100vh; |
|||
background-color: #292929; |
|||
padding-bottom: 120rpx; |
|||
position: relative; |
|||
box-sizing: border-box; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.content { |
|||
padding: 20rpx; |
|||
flex: 1; |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
.detail-content { |
|||
padding: 0; |
|||
} |
|||
|
|||
.section-title { |
|||
font-size: 30rpx; |
|||
font-weight: bold; |
|||
color: #FFFFFF; |
|||
margin-bottom: 20rpx; |
|||
padding-left: 10rpx; |
|||
} |
|||
|
|||
.service-list { |
|||
padding-bottom: 30rpx; |
|||
} |
|||
|
|||
.service-item { |
|||
background-color: #333; |
|||
border-radius: 12rpx; |
|||
padding: 20rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.service-header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.user-info { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.user-avatar { |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
border-radius: 50%; |
|||
margin-right: 15rpx; |
|||
background-color: #29D3B4; |
|||
} |
|||
|
|||
.user-name { |
|||
color: #fff; |
|||
font-size: 30rpx; |
|||
} |
|||
|
|||
.service-content { |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.info-row { |
|||
display: flex; |
|||
margin-bottom: 10rpx; |
|||
font-size: 28rpx; |
|||
color: #ccc; |
|||
} |
|||
|
|||
.info-label { |
|||
color: #999; |
|||
margin-right: 10rpx; |
|||
} |
|||
|
|||
.info-value { |
|||
color: #fff; |
|||
} |
|||
|
|||
.service-footer { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
border-top: 1px solid #444; |
|||
padding-top: 15rpx; |
|||
font-size: 26rpx; |
|||
} |
|||
|
|||
.contact-status { |
|||
color: #999; |
|||
} |
|||
|
|||
.effective-status { |
|||
color: #ff6b6b; |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.status-yes { |
|||
color: #29D3B4; |
|||
} |
|||
|
|||
.status-dot { |
|||
width: 16rpx; |
|||
height: 16rpx; |
|||
border-radius: 50%; |
|||
background-color: #ff6b6b; |
|||
margin-left: 10rpx; |
|||
} |
|||
|
|||
.dot-yes { |
|||
background-color: #29D3B4; |
|||
} |
|||
|
|||
.service-detail-card { |
|||
background-color: #333; |
|||
margin: 20rpx; |
|||
border-radius: 12rpx; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.detail-header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 30rpx 20rpx; |
|||
border-bottom: 1px solid #444; |
|||
} |
|||
|
|||
.detail-content { |
|||
padding: 30rpx 20rpx; |
|||
} |
|||
|
|||
.card-section { |
|||
margin-bottom: 30rpx; |
|||
} |
|||
|
|||
.info-card { |
|||
background-color: #3a3a3a; |
|||
border-radius: 12rpx; |
|||
padding: 20rpx; |
|||
} |
|||
|
|||
.info-item { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 20rpx 10rpx; |
|||
border-bottom: 1rpx solid #444; |
|||
} |
|||
|
|||
.info-item:last-child { |
|||
border-bottom: none; |
|||
} |
|||
|
|||
.item-label { |
|||
color: #999; |
|||
font-size: 28rpx; |
|||
} |
|||
|
|||
.item-value { |
|||
color: #fff; |
|||
font-size: 28rpx; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.score-box { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.star-box { |
|||
display: flex; |
|||
margin-right: 10rpx; |
|||
} |
|||
|
|||
.score-value { |
|||
color: #FFB800; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.feedback-content { |
|||
padding: 20rpx 10rpx; |
|||
font-size: 28rpx; |
|||
color: #fff; |
|||
line-height: 1.6; |
|||
} |
|||
|
|||
.empty-feedback { |
|||
padding: 40rpx 0; |
|||
text-align: center; |
|||
color: #999; |
|||
font-size: 28rpx; |
|||
} |
|||
|
|||
.detail-footer { |
|||
padding: 20rpx; |
|||
border-top: 1px solid #444; |
|||
} |
|||
|
|||
.btn { |
|||
height: 80rpx; |
|||
line-height: 80rpx; |
|||
border-radius: 40rpx; |
|||
font-size: 30rpx; |
|||
margin-bottom: 20rpx; |
|||
background-color: transparent; |
|||
} |
|||
|
|||
.primary { |
|||
background-color: #29D3B4; |
|||
color: #FFFFFF; |
|||
} |
|||
|
|||
.loading-box { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
height: 300rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
.empty-list { |
|||
padding: 60rpx 0; |
|||
text-align: center; |
|||
color: #999; |
|||
font-size: 28rpx; |
|||
background-color: #333; |
|||
border-radius: 12rpx; |
|||
} |
|||
|
|||
.status-completed { |
|||
color: #29D3B4; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,499 @@ |
|||
<template> |
|||
<view class="container"> |
|||
<uni-nav-bar fixed status-bar left-icon="left" title="学员详情" @clickLeft="navigateBack"></uni-nav-bar> |
|||
<view class="content" v-if="studentInfo"> |
|||
<view class="student-header"> |
|||
<view class="avatar-box"> |
|||
<image :src="studentInfo.avatar || '/static/icon-img/avatar.png'" mode="aspectFill" class="avatar-img"></image> |
|||
</view> |
|||
<view class="name-box"> |
|||
<text class="name">{{studentInfo.name}}</text> |
|||
<text class="id">ID: {{studentInfo.id}}</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="card-section"> |
|||
<view class="section-title">基本信息</view> |
|||
<view class="info-card"> |
|||
<view class="info-item"> |
|||
<text class="item-label">所属校区</text> |
|||
<text class="item-value">{{studentInfo.campus}}</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="item-label">联系电话</text> |
|||
<text class="item-value">{{studentInfo.phone}}</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="item-label">年龄</text> |
|||
<text class="item-value">{{studentInfo.age}}岁</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="item-label">入学时间</text> |
|||
<text class="item-value">{{studentInfo.enrollmentDate}}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="card-section"> |
|||
<view class="section-title">课程信息</view> |
|||
<view class="info-card"> |
|||
<view class="info-item"> |
|||
<text class="item-label">剩余课程</text> |
|||
<text class="item-value highlight">{{studentInfo.remainingCourses}}节</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="item-label">课程到期时间</text> |
|||
<text class="item-value highlight">{{studentInfo.expiryDate}}</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="item-label">已消课程</text> |
|||
<text class="item-value">{{studentInfo.completedCourses}}节</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="item-label">报名课程</text> |
|||
<text class="item-value">{{studentInfo.courseName}}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="card-section"> |
|||
<view class="section-title">最近上课记录</view> |
|||
<view class="info-card" v-if="studentInfo.recentClasses && studentInfo.recentClasses.length > 0"> |
|||
<view class="class-item" v-for="(item, index) in studentInfo.recentClasses" :key="index"> |
|||
<view class="class-date">{{item.date}}</view> |
|||
<view class="class-info"> |
|||
<text class="class-name">{{item.courseName}}</text> |
|||
<text class="class-time">{{item.timeSlot}}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<view class="empty-class" v-else> |
|||
<text>暂无上课记录</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="btn-group"> |
|||
<button class="btn primary" @click="contactStudent">联系学员</button> |
|||
<button class="btn secondary" @click="viewSchedule">查看课表</button> |
|||
</view> |
|||
</view> |
|||
|
|||
<view v-else class="loading-box"> |
|||
<text>加载中...</text> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
id: null, |
|||
studentInfo: null |
|||
} |
|||
}, |
|||
onLoad(options) { |
|||
if (options.id) { |
|||
this.id = options.id; |
|||
this.getStudentDetail(); |
|||
} else { |
|||
uni.showToast({ |
|||
title: '参数错误', |
|||
icon: 'none' |
|||
}); |
|||
setTimeout(() => { |
|||
this.navigateBack(); |
|||
}, 1500); |
|||
} |
|||
}, |
|||
methods: { |
|||
navigateBack() { |
|||
uni.navigateBack(); |
|||
}, |
|||
getStudentDetail() { |
|||
// 模拟数据,实际开发中应该从API获取 |
|||
// try { |
|||
// const res = await memberApi.getStudentDetail({id: this.id}); |
|||
// if(res.code == 1) { |
|||
// this.studentInfo = res.data || null; |
|||
// } else { |
|||
// uni.showToast({ |
|||
// title: res.msg || '获取学员详情失败', |
|||
// icon: 'none' |
|||
// }); |
|||
// } |
|||
// } catch(error) { |
|||
// console.error('获取学员详情失败:', error); |
|||
// uni.showToast({ |
|||
// title: '获取学员详情失败', |
|||
// icon: 'none' |
|||
// }); |
|||
// } |
|||
|
|||
// 使用模拟数据 |
|||
setTimeout(() => { |
|||
// 定义一些模拟数据对象 |
|||
const studentData = { |
|||
'1': { |
|||
id: '1', |
|||
name: '张三', |
|||
avatar: '/static/icon-img/avatar.png', |
|||
campus: '总部校区', |
|||
phone: '13812341000', |
|||
age: 11, |
|||
enrollmentDate: '2023-01-11', |
|||
remainingCourses: 10, |
|||
expiryDate: '2023-12-31', |
|||
completedCourses: 20, |
|||
courseName: '少儿英语基础班', |
|||
recentClasses: [ |
|||
{ |
|||
date: '2023-09-15', |
|||
courseName: '少儿英语基础班', |
|||
timeSlot: '15:30-17:00' |
|||
}, |
|||
{ |
|||
date: '2023-09-08', |
|||
courseName: '少儿英语基础班', |
|||
timeSlot: '15:30-17:00' |
|||
}, |
|||
{ |
|||
date: '2023-09-01', |
|||
courseName: '少儿英语基础班', |
|||
timeSlot: '15:30-17:00' |
|||
} |
|||
] |
|||
}, |
|||
'2': { |
|||
id: '2', |
|||
name: '李四', |
|||
avatar: '/static/icon-img/avatar.png', |
|||
campus: '西区校区', |
|||
phone: '13812341001', |
|||
age: 12, |
|||
enrollmentDate: '2023-01-12', |
|||
remainingCourses: 5, |
|||
expiryDate: '2023-11-15', |
|||
completedCourses: 25, |
|||
courseName: '少儿英语进阶班', |
|||
recentClasses: [ |
|||
{ |
|||
date: '2023-09-14', |
|||
courseName: '少儿英语进阶班', |
|||
timeSlot: '14:00-15:30' |
|||
}, |
|||
{ |
|||
date: '2023-09-07', |
|||
courseName: '少儿英语进阶班', |
|||
timeSlot: '14:00-15:30' |
|||
} |
|||
] |
|||
}, |
|||
'3': { |
|||
id: '3', |
|||
name: '王五', |
|||
avatar: '/static/icon-img/avatar.png', |
|||
campus: '东区校区', |
|||
phone: '13812341002', |
|||
age: 13, |
|||
enrollmentDate: '2023-01-13', |
|||
remainingCourses: 15, |
|||
expiryDate: '2024-01-20', |
|||
completedCourses: 15, |
|||
courseName: '少儿英语口语班', |
|||
recentClasses: [ |
|||
{ |
|||
date: '2023-09-16', |
|||
courseName: '少儿英语口语班', |
|||
timeSlot: '10:00-11:30' |
|||
}, |
|||
{ |
|||
date: '2023-09-09', |
|||
courseName: '少儿英语口语班', |
|||
timeSlot: '10:00-11:30' |
|||
}, |
|||
{ |
|||
date: '2023-09-02', |
|||
courseName: '少儿英语口语班', |
|||
timeSlot: '10:00-11:30' |
|||
} |
|||
] |
|||
}, |
|||
'4': { |
|||
id: '4', |
|||
name: '赵六', |
|||
avatar: '/static/icon-img/avatar.png', |
|||
campus: '南区校区', |
|||
phone: '13812341003', |
|||
age: 10, |
|||
enrollmentDate: '2023-02-15', |
|||
remainingCourses: 8, |
|||
expiryDate: '2023-11-30', |
|||
completedCourses: 12, |
|||
courseName: '少儿英语基础班', |
|||
recentClasses: [ |
|||
{ |
|||
date: '2023-09-13', |
|||
courseName: '少儿英语基础班', |
|||
timeSlot: '16:00-17:30' |
|||
}, |
|||
{ |
|||
date: '2023-09-06', |
|||
courseName: '少儿英语基础班', |
|||
timeSlot: '16:00-17:30' |
|||
} |
|||
] |
|||
}, |
|||
'5': { |
|||
id: '5', |
|||
name: '刘七', |
|||
avatar: '/static/icon-img/avatar.png', |
|||
campus: '北区校区', |
|||
phone: '13812341004', |
|||
age: 14, |
|||
enrollmentDate: '2023-03-20', |
|||
remainingCourses: 20, |
|||
expiryDate: '2024-02-15', |
|||
completedCourses: 10, |
|||
courseName: '少儿英语进阶班', |
|||
recentClasses: [ |
|||
{ |
|||
date: '2023-09-12', |
|||
courseName: '少儿英语进阶班', |
|||
timeSlot: '17:00-18:30' |
|||
}, |
|||
{ |
|||
date: '2023-09-05', |
|||
courseName: '少儿英语进阶班', |
|||
timeSlot: '17:00-18:30' |
|||
} |
|||
] |
|||
}, |
|||
'6': { |
|||
id: '6', |
|||
name: '陈八', |
|||
avatar: '/static/icon-img/avatar.png', |
|||
campus: '总部校区', |
|||
phone: '13812341005', |
|||
age: 9, |
|||
enrollmentDate: '2023-04-05', |
|||
remainingCourses: 3, |
|||
expiryDate: '2023-10-30', |
|||
completedCourses: 27, |
|||
courseName: '少儿英语口语班', |
|||
recentClasses: [ |
|||
{ |
|||
date: '2023-09-11', |
|||
courseName: '少儿英语口语班', |
|||
timeSlot: '14:30-16:00' |
|||
}, |
|||
{ |
|||
date: '2023-09-04', |
|||
courseName: '少儿英语口语班', |
|||
timeSlot: '14:30-16:00' |
|||
} |
|||
] |
|||
} |
|||
}; |
|||
|
|||
// 根据ID获取对应的学员数据 |
|||
this.studentInfo = studentData[this.id] || { |
|||
id: this.id, |
|||
name: '未知学员', |
|||
avatar: '/static/icon-img/avatar.png', |
|||
campus: '未知校区', |
|||
phone: '暂无', |
|||
age: '暂无', |
|||
enrollmentDate: '暂无', |
|||
remainingCourses: 0, |
|||
expiryDate: '暂无', |
|||
completedCourses: 0, |
|||
courseName: '暂无', |
|||
recentClasses: [] |
|||
}; |
|||
}, 500); |
|||
}, |
|||
contactStudent() { |
|||
if (this.studentInfo && this.studentInfo.phone) { |
|||
uni.makePhoneCall({ |
|||
phoneNumber: this.studentInfo.phone.replace(/\*/g, '0') |
|||
}); |
|||
} |
|||
}, |
|||
viewSchedule() { |
|||
uni.navigateTo({ |
|||
url: `/pages/coach/student/timetable?id=${this.id}` |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
.container { |
|||
min-height: 100vh; |
|||
background-color: #F5F5F5; |
|||
} |
|||
|
|||
.content { |
|||
padding: 20rpx; |
|||
} |
|||
|
|||
.loading-box { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
height: 80vh; |
|||
color: #999; |
|||
font-size: 28rpx; |
|||
} |
|||
|
|||
.student-header { |
|||
display: flex; |
|||
align-items: center; |
|||
background-color: #FFFFFF; |
|||
border-radius: 12rpx; |
|||
padding: 40rpx; |
|||
margin-bottom: 20rpx; |
|||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); |
|||
|
|||
.avatar-box { |
|||
width: 150rpx; |
|||
height: 150rpx; |
|||
border-radius: 75rpx; |
|||
overflow: hidden; |
|||
margin-right: 30rpx; |
|||
|
|||
.avatar-img { |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
} |
|||
|
|||
.name-box { |
|||
display: flex; |
|||
flex-direction: column; |
|||
|
|||
.name { |
|||
font-size: 36rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
margin-bottom: 10rpx; |
|||
} |
|||
|
|||
.id { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.card-section { |
|||
margin-bottom: 20rpx; |
|||
|
|||
.section-title { |
|||
font-size: 30rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
margin: 30rpx 10rpx 20rpx; |
|||
} |
|||
|
|||
.info-card { |
|||
background-color: #FFFFFF; |
|||
border-radius: 12rpx; |
|||
padding: 20rpx 30rpx; |
|||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); |
|||
} |
|||
|
|||
.info-item { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
padding: 20rpx 0; |
|||
border-bottom: 1px solid #F5F5F5; |
|||
|
|||
&:last-child { |
|||
border-bottom: none; |
|||
} |
|||
|
|||
.item-label { |
|||
color: #666; |
|||
font-size: 28rpx; |
|||
} |
|||
|
|||
.item-value { |
|||
color: #333; |
|||
font-size: 28rpx; |
|||
|
|||
&.highlight { |
|||
color: #FF6600; |
|||
font-weight: bold; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.class-item { |
|||
display: flex; |
|||
padding: 20rpx 0; |
|||
border-bottom: 1px solid #F5F5F5; |
|||
|
|||
&:last-child { |
|||
border-bottom: none; |
|||
} |
|||
|
|||
.class-date { |
|||
width: 180rpx; |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
} |
|||
|
|||
.class-info { |
|||
flex: 1; |
|||
display: flex; |
|||
flex-direction: column; |
|||
|
|||
.class-name { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
margin-bottom: 10rpx; |
|||
} |
|||
|
|||
.class-time { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.empty-class { |
|||
padding: 40rpx 0; |
|||
text-align: center; |
|||
color: #999; |
|||
font-size: 28rpx; |
|||
} |
|||
} |
|||
|
|||
.btn-group { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
margin: 40rpx 0; |
|||
|
|||
.btn { |
|||
width: 48%; |
|||
height: 80rpx; |
|||
line-height: 80rpx; |
|||
border-radius: 40rpx; |
|||
font-size: 30rpx; |
|||
|
|||
&.primary { |
|||
background-color: #3B7CF9; |
|||
color: #FFFFFF; |
|||
} |
|||
|
|||
&.secondary { |
|||
background-color: #FFFFFF; |
|||
color: #3B7CF9; |
|||
border: 1px solid #3B7CF9; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,216 @@ |
|||
<template> |
|||
<view class="container"> |
|||
<view class="content"> |
|||
<view v-if="studentList.length === 0" class="empty-box"> |
|||
<image src="/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 || '/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">{{item.remainingCourses}}节</text> |
|||
</view> |
|||
<view class="info-row"> |
|||
<text class="info-label">到期时间:</text> |
|||
<text class="info-value">{{item.expiryDate}}</text> |
|||
</view> |
|||
</view> |
|||
<view class="arrow-right"> |
|||
<uni-icons type="right" size="16" color="#CCCCCC"></uni-icons> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<AQTabber /> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import AQTabber from "@/components/AQ/AQTabber.vue" |
|||
export default { |
|||
components: { |
|||
AQTabber, |
|||
}, |
|||
data() { |
|||
return { |
|||
studentList: [] |
|||
} |
|||
}, |
|||
onLoad() { |
|||
this.getStudentList(); |
|||
}, |
|||
methods: { |
|||
navigateBack() { |
|||
uni.navigateBack(); |
|||
}, |
|||
getStudentList() { |
|||
// 模拟数据,实际开发中应该从API获取 |
|||
// const res = await memberApi.getStudentList({}); |
|||
// if(res.code == 1) { |
|||
// this.studentList = res.data || []; |
|||
// } else { |
|||
// uni.showToast({ |
|||
// title: res.msg || '获取学员列表失败', |
|||
// icon: 'none' |
|||
// }); |
|||
// } |
|||
|
|||
// 使用模拟数据 |
|||
this.studentList = [ |
|||
{ |
|||
id: 1, |
|||
name: '张三', |
|||
avatar: '/static/icon-img/avatar.png', |
|||
campus: '总部校区', |
|||
remainingCourses: 10, |
|||
expiryDate: '2023-12-31' |
|||
}, |
|||
{ |
|||
id: 2, |
|||
name: '李四', |
|||
avatar: '/static/icon-img/avatar.png', |
|||
campus: '西区校区', |
|||
remainingCourses: 5, |
|||
expiryDate: '2023-11-15' |
|||
}, |
|||
{ |
|||
id: 3, |
|||
name: '王五', |
|||
avatar: '/static/icon-img/avatar.png', |
|||
campus: '东区校区', |
|||
remainingCourses: 15, |
|||
expiryDate: '2024-01-20' |
|||
}, |
|||
{ |
|||
id: 4, |
|||
name: '赵六', |
|||
avatar: '/static/icon-img/avatar.png', |
|||
campus: '南区校区', |
|||
remainingCourses: 8, |
|||
expiryDate: '2023-11-30' |
|||
}, |
|||
{ |
|||
id: 5, |
|||
name: '刘七', |
|||
avatar: '/static/icon-img/avatar.png', |
|||
campus: '北区校区', |
|||
remainingCourses: 20, |
|||
expiryDate: '2024-02-15' |
|||
}, |
|||
{ |
|||
id: 6, |
|||
name: '陈八', |
|||
avatar: '/static/icon-img/avatar.png', |
|||
campus: '总部校区', |
|||
remainingCourses: 3, |
|||
expiryDate: '2023-10-30' |
|||
} |
|||
]; |
|||
}, |
|||
goToDetail(student) { |
|||
uni.navigateTo({ |
|||
url: `/pages/market/clue/clue_info?resource_sharing_id=25` |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
.container { |
|||
min-height: 100vh; |
|||
background-color: #F5F5F5; |
|||
} |
|||
|
|||
.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: #FFFFFF; |
|||
border-radius: 12rpx; |
|||
padding: 30rpx; |
|||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); |
|||
} |
|||
|
|||
.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: #333; |
|||
} |
|||
|
|||
.info-row { |
|||
display: flex; |
|||
font-size: 26rpx; |
|||
margin-top: 8rpx; |
|||
|
|||
.info-label { |
|||
color: #666; |
|||
} |
|||
|
|||
.info-value { |
|||
color: #333; |
|||
} |
|||
} |
|||
|
|||
.arrow-right { |
|||
padding-left: 20rpx; |
|||
} |
|||
} |
|||
</style> |
|||
Loading…
Reference in new issue