354 lines
8.3 KiB
Vue
354 lines
8.3 KiB
Vue
|
|
<template>
|
||
|
|
<view class="course-detail-page">
|
||
|
|
<!-- 课程基本信息 -->
|
||
|
|
<view class="info-section">
|
||
|
|
<view class="section-title">课程信息</view>
|
||
|
|
|
||
|
|
<view class="info-card">
|
||
|
|
<view class="info-row">
|
||
|
|
<text class="label">课程名称</text>
|
||
|
|
<text class="value">{{ courseInfo.courseName }}</text>
|
||
|
|
</view>
|
||
|
|
<view class="info-row">
|
||
|
|
<text class="label">课程状态</text>
|
||
|
|
<view class="status-tag" :class="'status-' + courseInfo.status">
|
||
|
|
{{ courseInfo.statusText }}
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
<view class="info-row">
|
||
|
|
<text class="label">上课时间</text>
|
||
|
|
<text class="value">{{ courseInfo.startTime }}</text>
|
||
|
|
</view>
|
||
|
|
<view class="info-row">
|
||
|
|
<text class="label">课程时长</text>
|
||
|
|
<text class="value">{{ courseInfo.duration }}分钟</text>
|
||
|
|
</view>
|
||
|
|
<view class="info-row">
|
||
|
|
<text class="label">上课地点</text>
|
||
|
|
<text class="value">{{ courseInfo.location }}</text>
|
||
|
|
</view>
|
||
|
|
<view class="info-row">
|
||
|
|
<text class="label">课程费用</text>
|
||
|
|
<text class="value price">¥{{ courseInfo.fee }}</text>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
|
||
|
|
<!-- 学生信息 -->
|
||
|
|
<view class="student-section">
|
||
|
|
<view class="section-title">学生信息</view>
|
||
|
|
|
||
|
|
<view class="student-card">
|
||
|
|
<image class="student-avatar" :src="studentInfo.avatar || '/static/avatar-default.jpg'" mode="aspectFill"></image>
|
||
|
|
<view class="student-info">
|
||
|
|
<text class="student-name">{{ studentInfo.name }}</text>
|
||
|
|
<text class="student-grade">{{ studentInfo.grade }}</text>
|
||
|
|
<text class="student-phone">{{ studentInfo.phone }}</text>
|
||
|
|
</view>
|
||
|
|
<button class="btn-contact" size="mini" @click="contactStudent">联系</button>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
|
||
|
|
<!-- 课程备注 -->
|
||
|
|
<view class="note-section" v-if="courseInfo.note">
|
||
|
|
<view class="section-title">课程备注</view>
|
||
|
|
<view class="note-content">
|
||
|
|
<text>{{ courseInfo.note }}</text>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
|
||
|
|
<!-- 操作按钮 -->
|
||
|
|
<view class="action-section">
|
||
|
|
<button
|
||
|
|
v-if="courseInfo.status === 'pending'"
|
||
|
|
class="action-btn primary"
|
||
|
|
@click="startCourse"
|
||
|
|
>
|
||
|
|
开始上课
|
||
|
|
</button>
|
||
|
|
<button
|
||
|
|
v-if="courseInfo.status === 'active'"
|
||
|
|
class="action-btn"
|
||
|
|
@click="endCourse"
|
||
|
|
>
|
||
|
|
结束课程
|
||
|
|
</button>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
export default {
|
||
|
|
data() {
|
||
|
|
return {
|
||
|
|
courseId: '',
|
||
|
|
courseInfo: {
|
||
|
|
courseName: '',
|
||
|
|
status: '',
|
||
|
|
statusText: '',
|
||
|
|
startTime: '',
|
||
|
|
duration: 0,
|
||
|
|
location: '',
|
||
|
|
fee: 0,
|
||
|
|
note: ''
|
||
|
|
},
|
||
|
|
studentInfo: {
|
||
|
|
name: '',
|
||
|
|
grade: '',
|
||
|
|
phone: '',
|
||
|
|
avatar: ''
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
onLoad(options) {
|
||
|
|
this.courseId = options.id
|
||
|
|
this.loadCourseDetail()
|
||
|
|
},
|
||
|
|
|
||
|
|
methods: {
|
||
|
|
async loadCourseDetail() {
|
||
|
|
uni.showLoading({ title: '加载中' })
|
||
|
|
try {
|
||
|
|
const res = await this.$api.providerApi.getCourseDetail(this.courseId)
|
||
|
|
if (res.code === 200 && res.data) {
|
||
|
|
this.courseInfo = res.data.courseInfo || {}
|
||
|
|
this.studentInfo = res.data.studentInfo || {}
|
||
|
|
} else {
|
||
|
|
// 模拟数据
|
||
|
|
this.courseInfo = {
|
||
|
|
courseName: '数学辅导课',
|
||
|
|
status: 'pending',
|
||
|
|
statusText: '待开始',
|
||
|
|
startTime: '2025-12-30 14:00',
|
||
|
|
duration: 120,
|
||
|
|
location: '北京市海淀区中关村大街1号',
|
||
|
|
fee: 200,
|
||
|
|
note: '学生数学基础较好,重点辅导应用题'
|
||
|
|
}
|
||
|
|
|
||
|
|
this.studentInfo = {
|
||
|
|
name: '张小明',
|
||
|
|
grade: '小学三年级',
|
||
|
|
phone: '138****8888',
|
||
|
|
avatar: ''
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('加载课程详情失败:', error)
|
||
|
|
uni.showToast({ title: '网络错误,请稍后重试', icon: 'none' })
|
||
|
|
} finally {
|
||
|
|
uni.hideLoading()
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
contactStudent() {
|
||
|
|
uni.showActionSheet({
|
||
|
|
itemList: ['拨打电话', '发送消息'],
|
||
|
|
success: (res) => {
|
||
|
|
if (res.tapIndex === 0) {
|
||
|
|
uni.makePhoneCall({
|
||
|
|
phoneNumber: this.studentInfo.phone
|
||
|
|
})
|
||
|
|
} else {
|
||
|
|
uni.showToast({ title: '消息功能开发中', icon: 'none' })
|
||
|
|
}
|
||
|
|
}
|
||
|
|
})
|
||
|
|
},
|
||
|
|
|
||
|
|
startCourse() {
|
||
|
|
uni.navigateTo({ url: `/pages/provider/course-start?id=${this.courseId}` })
|
||
|
|
},
|
||
|
|
|
||
|
|
endCourse() {
|
||
|
|
uni.showModal({
|
||
|
|
title: '结束课程',
|
||
|
|
content: '确认结束本次课程吗?',
|
||
|
|
success: async (res) => {
|
||
|
|
if (res.confirm) {
|
||
|
|
try {
|
||
|
|
uni.showLoading({ title: '处理中...' })
|
||
|
|
const result = await this.$api.providerApi.endCourse({ courseId: this.courseId })
|
||
|
|
uni.hideLoading()
|
||
|
|
|
||
|
|
if (result.code === 200) {
|
||
|
|
uni.showToast({ title: '课程已结束', icon: 'success' })
|
||
|
|
setTimeout(() => {
|
||
|
|
uni.navigateBack()
|
||
|
|
}, 1500)
|
||
|
|
} else {
|
||
|
|
uni.showToast({ title: result.message || '操作失败', icon: 'none' })
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
uni.hideLoading()
|
||
|
|
console.error('结束课程失败:', error)
|
||
|
|
uni.showToast({ title: '网络错误,请稍后重试', icon: 'none' })
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style lang="scss" scoped>
|
||
|
|
@import '@/static/css/common.scss';
|
||
|
|
|
||
|
|
.course-detail-page {
|
||
|
|
min-height: 100vh;
|
||
|
|
background: $bg-color;
|
||
|
|
padding-bottom: 120rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.info-section,
|
||
|
|
.student-section,
|
||
|
|
.note-section {
|
||
|
|
padding: 30rpx;
|
||
|
|
|
||
|
|
.section-title {
|
||
|
|
font-size: 32rpx;
|
||
|
|
font-weight: bold;
|
||
|
|
color: $text-color;
|
||
|
|
margin-bottom: 20rpx;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.info-card {
|
||
|
|
background: #fff;
|
||
|
|
border-radius: 16rpx;
|
||
|
|
padding: 24rpx;
|
||
|
|
|
||
|
|
.info-row {
|
||
|
|
display: flex;
|
||
|
|
justify-content: space-between;
|
||
|
|
align-items: center;
|
||
|
|
padding: 20rpx 0;
|
||
|
|
border-bottom: 1rpx solid #f0f0f0;
|
||
|
|
|
||
|
|
&:last-child {
|
||
|
|
border-bottom: none;
|
||
|
|
}
|
||
|
|
|
||
|
|
.label {
|
||
|
|
font-size: 26rpx;
|
||
|
|
color: $text-secondary;
|
||
|
|
}
|
||
|
|
|
||
|
|
.value {
|
||
|
|
font-size: 26rpx;
|
||
|
|
color: $text-color;
|
||
|
|
font-weight: 500;
|
||
|
|
|
||
|
|
&.price {
|
||
|
|
color: $secondary-color;
|
||
|
|
font-weight: bold;
|
||
|
|
font-size: 32rpx;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.status-tag {
|
||
|
|
padding: 4rpx 16rpx;
|
||
|
|
border-radius: 20rpx;
|
||
|
|
font-size: 22rpx;
|
||
|
|
|
||
|
|
&.status-pending {
|
||
|
|
background: rgba($warning-color, 0.1);
|
||
|
|
color: $warning-color;
|
||
|
|
}
|
||
|
|
|
||
|
|
&.status-active {
|
||
|
|
background: rgba($success-color, 0.1);
|
||
|
|
color: $success-color;
|
||
|
|
}
|
||
|
|
|
||
|
|
&.status-completed {
|
||
|
|
background: rgba(0, 0, 0, 0.05);
|
||
|
|
color: $text-secondary;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.student-card {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
background: #fff;
|
||
|
|
border-radius: 16rpx;
|
||
|
|
padding: 24rpx;
|
||
|
|
gap: 20rpx;
|
||
|
|
|
||
|
|
.student-avatar {
|
||
|
|
width: 100rpx;
|
||
|
|
height: 100rpx;
|
||
|
|
border-radius: 50%;
|
||
|
|
}
|
||
|
|
|
||
|
|
.student-info {
|
||
|
|
flex: 1;
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
gap: 8rpx;
|
||
|
|
|
||
|
|
.student-name {
|
||
|
|
font-size: 28rpx;
|
||
|
|
font-weight: bold;
|
||
|
|
color: $text-color;
|
||
|
|
}
|
||
|
|
|
||
|
|
.student-grade,
|
||
|
|
.student-phone {
|
||
|
|
font-size: 24rpx;
|
||
|
|
color: $text-secondary;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.btn-contact {
|
||
|
|
background: $primary-color;
|
||
|
|
color: #fff;
|
||
|
|
border: none;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.note-content {
|
||
|
|
background: #fff;
|
||
|
|
border-radius: 16rpx;
|
||
|
|
padding: 24rpx;
|
||
|
|
font-size: 26rpx;
|
||
|
|
color: $text-color;
|
||
|
|
line-height: 1.6;
|
||
|
|
}
|
||
|
|
|
||
|
|
.action-section {
|
||
|
|
position: fixed;
|
||
|
|
bottom: 0;
|
||
|
|
left: 0;
|
||
|
|
right: 0;
|
||
|
|
padding: 20rpx 30rpx;
|
||
|
|
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||
|
|
background: #fff;
|
||
|
|
box-shadow: 0 -4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||
|
|
|
||
|
|
.action-btn {
|
||
|
|
width: 100%;
|
||
|
|
height: 88rpx;
|
||
|
|
border-radius: 44rpx;
|
||
|
|
font-size: 32rpx;
|
||
|
|
font-weight: bold;
|
||
|
|
border: none;
|
||
|
|
|
||
|
|
&.primary {
|
||
|
|
background: $primary-color;
|
||
|
|
color: #fff;
|
||
|
|
}
|
||
|
|
|
||
|
|
&:not(.primary) {
|
||
|
|
background: #fff;
|
||
|
|
color: $primary-color;
|
||
|
|
border: 2rpx solid $primary-color;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</style>
|