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