16 changed files with 1687 additions and 405 deletions
@ -0,0 +1,216 @@ |
|||||
|
<?php |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | Niucloud-admin 企业快速开发的多应用管理平台 |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | 官方网址:https://www.niucloud.com |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | niucloud团队 版权所有 开源版本可自由商用 |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | Author: Niucloud Team |
||||
|
// +---------------------------------------------------------------------- |
||||
|
|
||||
|
namespace app\api\controller\apiController; |
||||
|
|
||||
|
use app\Request; |
||||
|
use app\service\api\apiService\CourseScheduleService; |
||||
|
use core\base\BaseApiService; |
||||
|
|
||||
|
/** |
||||
|
* 课程安排相关接口 |
||||
|
* Class CourseSchedule |
||||
|
* @package app\api\controller\apiController |
||||
|
*/ |
||||
|
class CourseSchedule extends BaseApiService |
||||
|
{ |
||||
|
/** |
||||
|
* 获取课程安排列表 |
||||
|
* @param Request $request |
||||
|
* @return \think\Response |
||||
|
*/ |
||||
|
public function getScheduleList(Request $request) |
||||
|
{ |
||||
|
$data = $request->all(); |
||||
|
return success((new CourseScheduleService())->getScheduleList($data)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取课程安排详情 |
||||
|
* @param Request $request |
||||
|
* @return \think\Response |
||||
|
*/ |
||||
|
public function getScheduleInfo(Request $request) |
||||
|
{ |
||||
|
$data = $this->request->params([ |
||||
|
["id", 0] |
||||
|
]); |
||||
|
$result = (new CourseScheduleService())->getScheduleInfo($data['id']); |
||||
|
if (isset($result['code']) && $result['code'] === 0) { |
||||
|
return fail($result['msg']); |
||||
|
} |
||||
|
return success('SUCCESS', $result); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建课程安排 |
||||
|
* @param Request $request |
||||
|
* @return \think\Response |
||||
|
*/ |
||||
|
public function createSchedule(Request $request) |
||||
|
{ |
||||
|
$data = $request->all(); |
||||
|
$result = (new CourseScheduleService())->createSchedule($data); |
||||
|
if (!$result['code']) { |
||||
|
return fail($result['msg']); |
||||
|
} |
||||
|
return success($result['msg'] ?? '创建成功', $result['data'] ?? []); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 批量创建课程安排 |
||||
|
* @param Request $request |
||||
|
* @return \think\Response |
||||
|
*/ |
||||
|
public function batchCreateSchedule(Request $request) |
||||
|
{ |
||||
|
$data = $request->all(); |
||||
|
$result = (new CourseScheduleService())->batchCreateSchedule($data); |
||||
|
if (!$result['code']) { |
||||
|
return fail($result['msg']); |
||||
|
} |
||||
|
return success($result['msg'] ?? '批量创建成功', $result['data'] ?? []); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 更新课程安排 |
||||
|
* @param Request $request |
||||
|
* @return \think\Response |
||||
|
*/ |
||||
|
public function updateSchedule(Request $request) |
||||
|
{ |
||||
|
$data = $request->all(); |
||||
|
$result = (new CourseScheduleService())->updateSchedule($data); |
||||
|
if (!$result['code']) { |
||||
|
return fail($result['msg']); |
||||
|
} |
||||
|
return success($result['msg'] ?? '更新成功', $result['data'] ?? []); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除课程安排 |
||||
|
* @param Request $request |
||||
|
* @return \think\Response |
||||
|
*/ |
||||
|
public function deleteSchedule(Request $request) |
||||
|
{ |
||||
|
$data = $this->request->params([ |
||||
|
["id", 0] |
||||
|
]); |
||||
|
$result = (new CourseScheduleService())->deleteSchedule($data['id']); |
||||
|
if (!$result['code']) { |
||||
|
return fail($result['msg']); |
||||
|
} |
||||
|
return success($result['msg'] ?? '删除成功'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取场地列表 |
||||
|
* @param Request $request |
||||
|
* @return \think\Response |
||||
|
*/ |
||||
|
public function getVenueList(Request $request) |
||||
|
{ |
||||
|
$data = $request->all(); |
||||
|
return success((new CourseScheduleService())->getVenueList($data)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取场地可用时间 |
||||
|
* @param Request $request |
||||
|
* @return \think\Response |
||||
|
*/ |
||||
|
public function getVenueAvailableTime(Request $request) |
||||
|
{ |
||||
|
$data = $this->request->params([ |
||||
|
["venue_id", 0], |
||||
|
["date", ""] |
||||
|
]); |
||||
|
return success((new CourseScheduleService())->getVenueAvailableTime($data)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查教练时间冲突 |
||||
|
* @param Request $request |
||||
|
* @return \think\Response |
||||
|
*/ |
||||
|
public function checkCoachConflict(Request $request) |
||||
|
{ |
||||
|
$data = $this->request->params([ |
||||
|
["coach_id", 0], |
||||
|
["date", ""], |
||||
|
["time_slot", ""], |
||||
|
["schedule_id", 0] // 排除当前正在编辑的课程安排 |
||||
|
]); |
||||
|
return success((new CourseScheduleService())->checkCoachConflict($data)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取课程安排统计 |
||||
|
* @param Request $request |
||||
|
* @return \think\Response |
||||
|
*/ |
||||
|
public function getScheduleStatistics(Request $request) |
||||
|
{ |
||||
|
$data = $request->all(); |
||||
|
return success((new CourseScheduleService())->getScheduleStatistics($data)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 学员加入课程安排 |
||||
|
* @param Request $request |
||||
|
* @return \think\Response |
||||
|
*/ |
||||
|
public function joinSchedule(Request $request) |
||||
|
{ |
||||
|
$data = $this->request->params([ |
||||
|
["schedule_id", 0], |
||||
|
["student_id", 0], |
||||
|
["course_type", 0], // 0-正常, 1-加课, 2-补课, 3-等待位 |
||||
|
["resources_id", 0] |
||||
|
]); |
||||
|
$result = (new CourseScheduleService())->joinSchedule($data); |
||||
|
if (!$result['code']) { |
||||
|
return fail($result['msg']); |
||||
|
} |
||||
|
return success($result['msg'] ?? '添加成功', $result['data'] ?? []); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 学员退出课程安排 |
||||
|
* @param Request $request |
||||
|
* @return \think\Response |
||||
|
*/ |
||||
|
public function leaveSchedule(Request $request) |
||||
|
{ |
||||
|
$data = $this->request->params([ |
||||
|
["schedule_id", 0], |
||||
|
["student_id", 0], |
||||
|
["remark", ""] |
||||
|
]); |
||||
|
$result = (new CourseScheduleService())->leaveSchedule($data); |
||||
|
if (!$result['code']) { |
||||
|
return fail($result['msg']); |
||||
|
} |
||||
|
return success($result['msg'] ?? '操作成功'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取筛选选项 |
||||
|
* @param Request $request |
||||
|
* @return \think\Response |
||||
|
*/ |
||||
|
public function getFilterOptions(Request $request) |
||||
|
{ |
||||
|
$data = $request->all(); |
||||
|
return success((new CourseScheduleService())->getFilterOptions($data)); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,517 @@ |
|||||
|
<template> |
||||
|
<view class="schedule-detail" v-if="visible"> |
||||
|
<!-- <view class="popup-wrapper">--> |
||||
|
<!-- <view class="popup-header">--> |
||||
|
<!-- <text class="popup-title">课次详情</text>--> |
||||
|
<!-- <view class="close-btn" @click="closePopup">--> |
||||
|
<!-- <text class="close-icon">×</text>--> |
||||
|
<!-- </view>--> |
||||
|
<!-- </view>--> |
||||
|
|
||||
|
<!-- <view class="popup-content" v-if="loading">--> |
||||
|
<!-- <view class="loading">--> |
||||
|
<!-- <fui-loading></fui-loading>--> |
||||
|
<!-- <text class="loading-text">加载中...</text>--> |
||||
|
<!-- </view>--> |
||||
|
<!-- </view>--> |
||||
|
|
||||
|
<!-- <view class="popup-content" v-else-if="error">--> |
||||
|
<!-- <view class="error-message">--> |
||||
|
<!-- <text>{{ errorMessage }}</text>--> |
||||
|
<!-- <view class="retry-btn" @click="fetchScheduleDetail">--> |
||||
|
<!-- <text>重试</text>--> |
||||
|
<!-- </view>--> |
||||
|
<!-- </view>--> |
||||
|
<!-- </view>--> |
||||
|
|
||||
|
<!-- <view class="popup-content" v-else>--> |
||||
|
<!-- <view class="course-title">--> |
||||
|
<!-- <text>{{ scheduleDetail.title || '暂无课程名称' }}</text>--> |
||||
|
<!-- </view>--> |
||||
|
|
||||
|
<!-- <view class="course-time">--> |
||||
|
<!-- <text>{{ scheduleDetail.course_date || '' }} {{ scheduleDetail.time_slot || '' }}</text>--> |
||||
|
<!-- </view>--> |
||||
|
|
||||
|
<!-- <view class="schedule-info">--> |
||||
|
<!-- <view class="info-item">--> |
||||
|
<!-- <text class="info-label">授课教师:</text>--> |
||||
|
<!-- <text class="info-value">{{ scheduleDetail.coach?.name || '未设置' }}</text>--> |
||||
|
<!-- </view>--> |
||||
|
|
||||
|
<!-- <view class="info-item">--> |
||||
|
<!-- <text class="info-label">教室:</text>--> |
||||
|
<!-- <text class="info-value">{{ scheduleDetail.venue?.venue_name || '未设置' }}</text>--> |
||||
|
<!-- </view>--> |
||||
|
|
||||
|
<!-- <view class="info-item">--> |
||||
|
<!-- <text class="info-label">当前人数:</text>--> |
||||
|
<!-- <text class="info-value">{{ studentCount }}/{{ scheduleDetail.venue?.capacity || 0 }}</text>--> |
||||
|
<!-- </view>--> |
||||
|
|
||||
|
<!-- <view class="info-item">--> |
||||
|
<!-- <text class="info-label">课程内容:</text>--> |
||||
|
<!-- <text class="info-value">{{ scheduleDetail.content || '未设置上课内容' }}</text>--> |
||||
|
<!-- </view>--> |
||||
|
|
||||
|
<!-- <view class="info-item" v-if="scheduleDetail.remark">--> |
||||
|
<!-- <text class="info-label">备注:</text>--> |
||||
|
<!-- <text class="info-value">{{ scheduleDetail.remark }}</text>--> |
||||
|
<!-- </view>--> |
||||
|
|
||||
|
<!-- <!– 学员列表 –>--> |
||||
|
<!-- <view class="student-list-section">--> |
||||
|
<!-- <view class="section-title">--> |
||||
|
<!-- <text>学员列表</text>--> |
||||
|
<!-- <text class="status-tag" :class="statusClass">{{ statusText }}</text>--> |
||||
|
<!-- </view>--> |
||||
|
<!-- <view class="student-list" v-if="scheduleDetail.student_courses && scheduleDetail.student_courses.length > 0">--> |
||||
|
<!-- <view class="student-item" v-for="(student, index) in scheduleDetail.student_courses" :key="index">--> |
||||
|
<!-- <view class="student-avatar">--> |
||||
|
<!-- <image :src="$util.img(student.avatar)" mode="aspectFill"></image>--> |
||||
|
<!-- </view>--> |
||||
|
<!-- <view class="student-info">--> |
||||
|
<!-- <text class="student-name">{{ student.name }}</text>--> |
||||
|
<!-- <text class="student-status" :class="{'signed': student.status === 'signed'}">--> |
||||
|
<!-- {{ student.status === 'signed' ? '已签到' : '未签到' }}--> |
||||
|
<!-- </text>--> |
||||
|
<!-- </view>--> |
||||
|
<!-- </view>--> |
||||
|
<!-- </view>--> |
||||
|
<!-- <view class="empty-list" v-else>--> |
||||
|
<!-- <text>暂无学员参与此课程</text>--> |
||||
|
<!-- </view>--> |
||||
|
<!-- </view>--> |
||||
|
<!-- </view>--> |
||||
|
<!-- </view>--> |
||||
|
|
||||
|
<!-- <view class="popup-footer">--> |
||||
|
<!-- <view class="action-btn adjust-btn" @click="handleAdjustClass">--> |
||||
|
<!-- <text>调课</text>--> |
||||
|
<!-- </view>--> |
||||
|
<!-- <view class="action-btn sign-btn" @click="handleSignIn">--> |
||||
|
<!-- <text>点名</text>--> |
||||
|
<!-- </view>--> |
||||
|
<!-- </view>--> |
||||
|
<!-- </view>--> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import apiRoute from '@/api/apiRoute.js'; |
||||
|
|
||||
|
export default { |
||||
|
name: 'ScheduleDetail', |
||||
|
props: { |
||||
|
visible: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
scheduleId: { |
||||
|
type: [String, Number], |
||||
|
default: '' |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
loading: false, |
||||
|
error: false, |
||||
|
errorMessage: '加载失败,请重试', |
||||
|
scheduleDetail: {}, |
||||
|
studentCount: 0 |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
// 课程状态文本和样式 |
||||
|
statusText() { |
||||
|
if (!this.scheduleDetail.student_courses || !this.scheduleDetail.student_courses[0]) { |
||||
|
return '未开始'; |
||||
|
} |
||||
|
|
||||
|
const now = new Date(); |
||||
|
const startDate = this.scheduleDetail.student_courses[0].start_date ? |
||||
|
new Date(this.scheduleDetail.student_courses[0].start_date) : null; |
||||
|
const endDate = this.scheduleDetail.student_courses[0].end_date ? |
||||
|
new Date(this.scheduleDetail.student_courses[0].end_date) : null; |
||||
|
|
||||
|
if (startDate && endDate) { |
||||
|
if (now >= startDate && now <= endDate) { |
||||
|
return '上课中'; |
||||
|
} else if (now > endDate) { |
||||
|
return '已结束'; |
||||
|
} else if (now < startDate) { |
||||
|
return '未开始'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return '未开始'; |
||||
|
}, |
||||
|
statusClass() { |
||||
|
switch (this.statusText) { |
||||
|
case '上课中': |
||||
|
return 'status-in-progress'; |
||||
|
case '已结束': |
||||
|
return 'status-ended'; |
||||
|
case '未开始': |
||||
|
default: |
||||
|
return 'status-not-started'; |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
watch: { |
||||
|
// 监听课程ID变化,重新获取数据 |
||||
|
scheduleId: { |
||||
|
immediate: true, |
||||
|
handler(newVal) { |
||||
|
if (newVal && this.visible) { |
||||
|
this.fetchScheduleDetail(); |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
// 监听弹窗可见性变化 |
||||
|
visible(newVal) { |
||||
|
if (newVal && this.scheduleId) { |
||||
|
this.fetchScheduleDetail(); |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
// 获取课程详情 |
||||
|
async fetchScheduleDetail() { |
||||
|
if (!this.scheduleId) { |
||||
|
this.error = true; |
||||
|
this.errorMessage = '课程ID不能为空'; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
this.loading = true; |
||||
|
this.error = false; |
||||
|
|
||||
|
try { |
||||
|
// 使用新接口获取课程安排详情 |
||||
|
const res = await apiRoute.getCourseScheduleInfo({ |
||||
|
id: this.scheduleId |
||||
|
}); |
||||
|
|
||||
|
if (res.code === 1 && res.data) { |
||||
|
this.scheduleDetail = res.data; |
||||
|
// 计算学生数量 |
||||
|
this.studentCount = this.scheduleDetail.student_courses ? |
||||
|
this.scheduleDetail.student_courses.length : 0; |
||||
|
} else { |
||||
|
// 如果新接口不可用,尝试使用旧接口 |
||||
|
const fallbackRes = await apiRoute.courseInfo({ |
||||
|
id: this.scheduleId |
||||
|
}); |
||||
|
|
||||
|
if (fallbackRes.code === 1 && fallbackRes.data) { |
||||
|
this.scheduleDetail = fallbackRes.data; |
||||
|
this.studentCount = this.scheduleDetail.student_courses ? |
||||
|
this.scheduleDetail.student_courses.length : 0; |
||||
|
} else { |
||||
|
throw new Error(res.msg || fallbackRes.msg || '获取课程详情失败'); |
||||
|
} |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('获取课程详情失败:', error); |
||||
|
this.error = true; |
||||
|
this.errorMessage = error.message || '获取课程详情失败,请重试'; |
||||
|
} finally { |
||||
|
this.loading = false; |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 关闭弹窗 |
||||
|
closePopup() { |
||||
|
this.$emit('update:visible', false); |
||||
|
}, |
||||
|
|
||||
|
// 点名功能 |
||||
|
handleSignIn() { |
||||
|
// 如果课程已结束,显示提示 |
||||
|
if (this.statusText === '已结束') { |
||||
|
uni.showToast({ |
||||
|
title: '课程已结束,无法点名', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// 如果没有学生,显示提示 |
||||
|
if (!this.scheduleDetail.student_courses || this.scheduleDetail.student_courses.length === 0) { |
||||
|
uni.showToast({ |
||||
|
title: '暂无学员,无法点名', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// 触发点名事件,由父组件处理 |
||||
|
this.$emit('sign-in', { |
||||
|
scheduleId: this.scheduleId, |
||||
|
scheduleDetail: this.scheduleDetail |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 调课功能 |
||||
|
handleAdjustClass() { |
||||
|
// 如果课程已结束,显示提示 |
||||
|
if (this.statusText === '已结束') { |
||||
|
uni.showToast({ |
||||
|
title: '课程已结束,无法调课', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// 触发调课事件,由父组件处理 |
||||
|
this.$emit('adjust-class', { |
||||
|
scheduleId: this.scheduleId, |
||||
|
scheduleDetail: this.scheduleDetail |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="less" scoped> |
||||
|
.schedule-detail { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
background-color: rgba(0, 0, 0, 0.5); |
||||
|
z-index: 999; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.popup-wrapper { |
||||
|
width: 90%; |
||||
|
max-height: 80vh; |
||||
|
background-color: #434544; |
||||
|
border-radius: 16rpx; |
||||
|
overflow: hidden; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.popup-header { |
||||
|
padding: 30rpx; |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
border-bottom: 1px solid #555; |
||||
|
} |
||||
|
|
||||
|
.popup-title { |
||||
|
font-size: 32rpx; |
||||
|
font-weight: bold; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.close-btn { |
||||
|
width: 60rpx; |
||||
|
height: 60rpx; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.close-icon { |
||||
|
font-size: 40rpx; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.popup-content { |
||||
|
flex: 1; |
||||
|
padding: 30rpx; |
||||
|
overflow-y: auto; |
||||
|
} |
||||
|
|
||||
|
.loading, .error-message { |
||||
|
height: 300rpx; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.loading-text { |
||||
|
margin-top: 20rpx; |
||||
|
font-size: 28rpx; |
||||
|
color: #ccc; |
||||
|
} |
||||
|
|
||||
|
.error-message { |
||||
|
color: #ff6b6b; |
||||
|
font-size: 28rpx; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
.retry-btn { |
||||
|
margin-top: 30rpx; |
||||
|
padding: 12rpx 30rpx; |
||||
|
background-color: #29d3b4; |
||||
|
border-radius: 8rpx; |
||||
|
color: #fff; |
||||
|
font-size: 24rpx; |
||||
|
} |
||||
|
|
||||
|
.course-title { |
||||
|
font-size: 36rpx; |
||||
|
font-weight: bold; |
||||
|
color: #fff; |
||||
|
margin-bottom: 16rpx; |
||||
|
} |
||||
|
|
||||
|
.course-time { |
||||
|
font-size: 28rpx; |
||||
|
color: #FAD24E; |
||||
|
margin-bottom: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.schedule-info { |
||||
|
background-color: #333; |
||||
|
border-radius: 12rpx; |
||||
|
padding: 24rpx; |
||||
|
} |
||||
|
|
||||
|
.info-item { |
||||
|
display: flex; |
||||
|
margin-bottom: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.info-label { |
||||
|
width: 160rpx; |
||||
|
font-size: 26rpx; |
||||
|
color: #ccc; |
||||
|
} |
||||
|
|
||||
|
.info-value { |
||||
|
flex: 1; |
||||
|
font-size: 26rpx; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.student-list-section { |
||||
|
margin-top: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.section-title { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding-bottom: 16rpx; |
||||
|
border-bottom: 1px solid #555; |
||||
|
margin-bottom: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.section-title text { |
||||
|
font-size: 28rpx; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.status-tag { |
||||
|
font-size: 24rpx; |
||||
|
padding: 4rpx 16rpx; |
||||
|
border-radius: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.status-in-progress { |
||||
|
background-color: #FAD24E; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.status-ended { |
||||
|
background-color: #e2e2e2; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.status-not-started { |
||||
|
background-color: #1cd188; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.student-list { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
gap: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.student-item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.student-avatar { |
||||
|
width: 80rpx; |
||||
|
height: 80rpx; |
||||
|
border-radius: 50%; |
||||
|
overflow: hidden; |
||||
|
background-color: #555; |
||||
|
margin-right: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.student-avatar image { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
} |
||||
|
|
||||
|
.student-info { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.student-name { |
||||
|
font-size: 28rpx; |
||||
|
color: #fff; |
||||
|
margin-bottom: 6rpx; |
||||
|
} |
||||
|
|
||||
|
.student-status { |
||||
|
font-size: 24rpx; |
||||
|
color: #ff6b6b; |
||||
|
} |
||||
|
|
||||
|
.student-status.signed { |
||||
|
color: #1cd188; |
||||
|
} |
||||
|
|
||||
|
.empty-list { |
||||
|
padding: 40rpx 0; |
||||
|
text-align: center; |
||||
|
color: #999; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
|
||||
|
.popup-footer { |
||||
|
display: flex; |
||||
|
padding: 30rpx; |
||||
|
gap: 20rpx; |
||||
|
border-top: 1px solid #555; |
||||
|
} |
||||
|
|
||||
|
.action-btn { |
||||
|
flex: 1; |
||||
|
height: 80rpx; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
border-radius: 8rpx; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
|
||||
|
.adjust-btn { |
||||
|
background-color: #555; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.sign-btn { |
||||
|
background-color: #29d3b4; |
||||
|
color: #fff; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,481 @@ |
|||||
|
<template> |
||||
|
<view class="schedule-detail-container"> |
||||
|
<!-- 页面加载状态 --> |
||||
|
<view class="loading-container" v-if="loading"> |
||||
|
<fui-loading></fui-loading> |
||||
|
<text class="loading-text">加载中...</text> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 错误状态显示 --> |
||||
|
<view class="error-container" v-else-if="error"> |
||||
|
<text class="error-text">{{ errorMessage }}</text> |
||||
|
<view class="retry-btn" @click="fetchScheduleDetail"> |
||||
|
<text>重试</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="schedule-content" v-else> |
||||
|
<!-- 课程基本信息 --> |
||||
|
<view class="schedule-header"> |
||||
|
<view class="course-title"> |
||||
|
<text>{{ scheduleDetail.title || '暂无课程名称' }}</text> |
||||
|
<text class="status-tag" :class="statusClass">{{ statusText }}</text> |
||||
|
</view> |
||||
|
<view class="course-time">{{ scheduleDetail.course_date || '' }} {{ scheduleDetail.time_slot || '' }}</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 课程详细信息 --> |
||||
|
<view class="info-card"> |
||||
|
<view class="info-item"> |
||||
|
<text class="info-label">授课教师:</text> |
||||
|
<text class="info-value">{{ scheduleDetail.coach?.name || '未设置' }}</text> |
||||
|
</view> |
||||
|
<view class="info-item"> |
||||
|
<text class="info-label">教室:</text> |
||||
|
<text class="info-value">{{ scheduleDetail.venue?.venue_name || '未设置' }}</text> |
||||
|
</view> |
||||
|
<view class="info-item"> |
||||
|
<text class="info-label">当前人数:</text> |
||||
|
<text class="info-value">{{ studentCount }}/{{ scheduleDetail.venue?.capacity || 0 }}</text> |
||||
|
</view> |
||||
|
<view class="info-item"> |
||||
|
<text class="info-label">课程内容:</text> |
||||
|
<text class="info-value">{{ scheduleDetail.content || '未设置上课内容' }}</text> |
||||
|
</view> |
||||
|
<view class="info-item" v-if="scheduleDetail.remark"> |
||||
|
<text class="info-label">备注:</text> |
||||
|
<text class="info-value">{{ scheduleDetail.remark }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 学员列表 --> |
||||
|
<view class="student-list-section"> |
||||
|
<view class="section-title"> |
||||
|
<text>学员列表</text> |
||||
|
</view> |
||||
|
<view class="student-list" v-if="scheduleDetail.student_courses && scheduleDetail.student_courses.length > 0"> |
||||
|
<view class="student-item" v-for="(student, index) in scheduleDetail.student_courses" :key="index"> |
||||
|
<view class="student-avatar"> |
||||
|
<image :src="$util.img(student.avatar)" mode="aspectFill"></image> |
||||
|
</view> |
||||
|
<view class="student-info"> |
||||
|
<text class="student-name">{{ student.name }}</text> |
||||
|
<text class="student-status" :class="{'signed': student.status === 'signed'}"> |
||||
|
{{ student.status === 'signed' ? '已签到' : '未签到' }} |
||||
|
</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="empty-list" v-else> |
||||
|
<text>暂无学员参与此课程</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 底部按钮区域 --> |
||||
|
<view class="footer-actions"> |
||||
|
<view class="action-btn adjust-btn" @click="handleAdjustClass"> |
||||
|
<text>调课</text> |
||||
|
</view> |
||||
|
<view class="action-btn sign-btn" @click="handleSignIn"> |
||||
|
<text>点名</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 引入课程详情组件 --> |
||||
|
<schedule-detail |
||||
|
:visible="showDetailPopup" |
||||
|
:scheduleId="scheduleId" |
||||
|
@update:visible="showDetailPopup = $event" |
||||
|
@sign-in="onSignIn" |
||||
|
@adjust-class="onAdjustClass" |
||||
|
></schedule-detail> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import apiRoute from '@/api/apiRoute.js' |
||||
|
import ScheduleDetail from '@/components/schedule/ScheduleDetail.vue' |
||||
|
|
||||
|
export default { |
||||
|
components: { |
||||
|
ScheduleDetail |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
scheduleId: '', // 课程安排ID |
||||
|
loading: false, |
||||
|
error: false, |
||||
|
errorMessage: '加载失败,请重试', |
||||
|
scheduleDetail: {}, |
||||
|
studentCount: 0, |
||||
|
showDetailPopup: false |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
// 课程状态文本和样式 |
||||
|
statusText() { |
||||
|
if (!this.scheduleDetail.student_courses || !this.scheduleDetail.student_courses[0]) { |
||||
|
return '未开始'; |
||||
|
} |
||||
|
|
||||
|
const now = new Date(); |
||||
|
const startDate = this.scheduleDetail.student_courses[0].start_date ? |
||||
|
new Date(this.scheduleDetail.student_courses[0].start_date) : null; |
||||
|
const endDate = this.scheduleDetail.student_courses[0].end_date ? |
||||
|
new Date(this.scheduleDetail.student_courses[0].end_date) : null; |
||||
|
|
||||
|
if (startDate && endDate) { |
||||
|
if (now >= startDate && now <= endDate) { |
||||
|
return '上课中'; |
||||
|
} else if (now > endDate) { |
||||
|
return '已结束'; |
||||
|
} else if (now < startDate) { |
||||
|
return '未开始'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return '未开始'; |
||||
|
}, |
||||
|
statusClass() { |
||||
|
switch (this.statusText) { |
||||
|
case '上课中': |
||||
|
return 'status-in-progress'; |
||||
|
case '已结束': |
||||
|
return 'status-ended'; |
||||
|
case '未开始': |
||||
|
default: |
||||
|
return 'status-not-started'; |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
onLoad(options) { |
||||
|
if (options.id) { |
||||
|
this.scheduleId = options.id; |
||||
|
this.fetchScheduleDetail(); |
||||
|
} else { |
||||
|
this.error = true; |
||||
|
this.errorMessage = '未找到课程安排ID'; |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
// 获取课程详情 |
||||
|
async fetchScheduleDetail() { |
||||
|
if (!this.scheduleId) { |
||||
|
this.error = true; |
||||
|
this.errorMessage = '课程ID不能为空'; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
this.loading = true; |
||||
|
this.error = false; |
||||
|
|
||||
|
try { |
||||
|
// 使用新接口获取课程安排详情 |
||||
|
const res = await apiRoute.getCourseScheduleInfo({ |
||||
|
id: this.scheduleId |
||||
|
}); |
||||
|
|
||||
|
if (res.code === 1 && res.data) { |
||||
|
this.scheduleDetail = res.data; |
||||
|
// 计算学生数量 |
||||
|
this.studentCount = this.scheduleDetail.student_courses ? |
||||
|
this.scheduleDetail.student_courses.length : 0; |
||||
|
} else { |
||||
|
// 如果新接口不可用,尝试使用旧接口 |
||||
|
const fallbackRes = await apiRoute.courseInfo({ |
||||
|
id: this.scheduleId |
||||
|
}); |
||||
|
|
||||
|
if (fallbackRes.code === 1 && fallbackRes.data) { |
||||
|
this.scheduleDetail = fallbackRes.data; |
||||
|
this.studentCount = this.scheduleDetail.student_courses ? |
||||
|
this.scheduleDetail.student_courses.length : 0; |
||||
|
} else { |
||||
|
throw new Error(res.msg || fallbackRes.msg || '获取课程详情失败'); |
||||
|
} |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('获取课程详情失败:', error); |
||||
|
this.error = true; |
||||
|
this.errorMessage = error.message || '获取课程详情失败,请重试'; |
||||
|
} finally { |
||||
|
this.loading = false; |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 点名功能 |
||||
|
handleSignIn() { |
||||
|
// 如果课程已结束,显示提示 |
||||
|
if (this.statusText === '已结束') { |
||||
|
uni.showToast({ |
||||
|
title: '课程已结束,无法点名', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// 如果没有学生,显示提示 |
||||
|
if (!this.scheduleDetail.student_courses || this.scheduleDetail.student_courses.length === 0) { |
||||
|
uni.showToast({ |
||||
|
title: '暂无学员,无法点名', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// 显示点名界面或跳转至点名页面 |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/coach/schedule/sign_in?id=${this.scheduleId}` |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 调课功能 |
||||
|
handleAdjustClass() { |
||||
|
// 如果课程已结束,显示提示 |
||||
|
if (this.statusText === '已结束') { |
||||
|
uni.showToast({ |
||||
|
title: '课程已结束,无法调课', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// 跳转至调课页面 |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/coach/schedule/adjust_course?id=${this.scheduleId}` |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 从弹窗组件中点名处理 |
||||
|
onSignIn(data) { |
||||
|
console.log('处理点名:', data); |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/coach/schedule/sign_in?id=${data.scheduleId}` |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 从弹窗组件中调课处理 |
||||
|
onAdjustClass(data) { |
||||
|
console.log('处理调课:', data); |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/coach/schedule/adjust_course?id=${data.scheduleId}` |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 显示弹窗 |
||||
|
showPopup() { |
||||
|
this.showDetailPopup = true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="less" scoped> |
||||
|
.schedule-detail-container { |
||||
|
background-color: #292929; |
||||
|
min-height: 100vh; |
||||
|
padding-bottom: 140rpx; // 为底部按钮留出空间 |
||||
|
} |
||||
|
|
||||
|
.loading-container, .error-container { |
||||
|
height: 100vh; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.loading-text, .error-text { |
||||
|
margin-top: 30rpx; |
||||
|
font-size: 28rpx; |
||||
|
color: #ccc; |
||||
|
} |
||||
|
|
||||
|
.retry-btn { |
||||
|
margin-top: 30rpx; |
||||
|
padding: 12rpx 30rpx; |
||||
|
background-color: #29d3b4; |
||||
|
border-radius: 8rpx; |
||||
|
color: #fff; |
||||
|
font-size: 24rpx; |
||||
|
} |
||||
|
|
||||
|
.schedule-content { |
||||
|
padding: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.schedule-header { |
||||
|
margin-bottom: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.course-title { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
font-size: 36rpx; |
||||
|
font-weight: bold; |
||||
|
color: #fff; |
||||
|
margin-bottom: 16rpx; |
||||
|
} |
||||
|
|
||||
|
.status-tag { |
||||
|
font-size: 24rpx; |
||||
|
padding: 4rpx 16rpx; |
||||
|
border-radius: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.status-in-progress { |
||||
|
background-color: #FAD24E; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.status-ended { |
||||
|
background-color: #e2e2e2; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.status-not-started { |
||||
|
background-color: #1cd188; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.course-time { |
||||
|
font-size: 28rpx; |
||||
|
color: #FAD24E; |
||||
|
margin-bottom: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.info-card { |
||||
|
background-color: #434544; |
||||
|
border-radius: 16rpx; |
||||
|
padding: 24rpx; |
||||
|
margin-bottom: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.info-item { |
||||
|
display: flex; |
||||
|
margin-bottom: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.info-label { |
||||
|
width: 160rpx; |
||||
|
font-size: 26rpx; |
||||
|
color: #ccc; |
||||
|
} |
||||
|
|
||||
|
.info-value { |
||||
|
flex: 1; |
||||
|
font-size: 26rpx; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.student-list-section { |
||||
|
background-color: #434544; |
||||
|
border-radius: 16rpx; |
||||
|
padding: 24rpx; |
||||
|
} |
||||
|
|
||||
|
.section-title { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding-bottom: 16rpx; |
||||
|
border-bottom: 1px solid #555; |
||||
|
margin-bottom: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.section-title text { |
||||
|
font-size: 28rpx; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.student-list { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
gap: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.student-item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.student-avatar { |
||||
|
width: 80rpx; |
||||
|
height: 80rpx; |
||||
|
border-radius: 50%; |
||||
|
overflow: hidden; |
||||
|
background-color: #555; |
||||
|
margin-right: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.student-avatar image { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
} |
||||
|
|
||||
|
.student-info { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.student-name { |
||||
|
font-size: 28rpx; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.student-status { |
||||
|
font-size: 24rpx; |
||||
|
color: #ff6b6b; |
||||
|
background: rgba(255, 107, 107, 0.1); |
||||
|
padding: 4rpx 12rpx; |
||||
|
border-radius: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.student-status.signed { |
||||
|
color: #1cd188; |
||||
|
background: rgba(28, 209, 136, 0.1); |
||||
|
} |
||||
|
|
||||
|
.empty-list { |
||||
|
padding: 40rpx 0; |
||||
|
text-align: center; |
||||
|
color: #999; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
|
||||
|
.footer-actions { |
||||
|
position: fixed; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
display: flex; |
||||
|
padding: 30rpx; |
||||
|
gap: 20rpx; |
||||
|
background-color: #292929; |
||||
|
border-top: 1px solid #434544; |
||||
|
} |
||||
|
|
||||
|
.action-btn { |
||||
|
flex: 1; |
||||
|
height: 80rpx; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
border-radius: 8rpx; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
|
||||
|
.adjust-btn { |
||||
|
background-color: #555; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.sign-btn { |
||||
|
background-color: #29d3b4; |
||||
|
color: #fff; |
||||
|
} |
||||
|
</style> |
||||
@ -1,263 +0,0 @@ |
|||||
<template> |
|
||||
<view class="container"> |
|
||||
<view class="header"> |
|
||||
<text class="title">字典功能测试</text> |
|
||||
</view> |
|
||||
|
|
||||
<view class="test-section"> |
|
||||
<view class="section-title">1. 测试单个字典获取</view> |
|
||||
<button class="test-btn" @click="testSingleDict">测试获取单个字典</button> |
|
||||
<view class="result" v-if="singleResult"> |
|
||||
<text class="result-title">结果:</text> |
|
||||
<text class="result-content">{{ JSON.stringify(singleResult, null, 2) }}</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<view class="test-section"> |
|
||||
<view class="section-title">2. 测试批量字典获取</view> |
|
||||
<button class="test-btn" @click="testBatchDict">测试批量获取字典</button> |
|
||||
<view class="result" v-if="batchResult"> |
|
||||
<text class="result-title">结果:</text> |
|
||||
<text class="result-content">{{ JSON.stringify(batchResult, null, 2) }}</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<view class="test-section"> |
|
||||
<view class="section-title">3. 测试字典缓存</view> |
|
||||
<button class="test-btn" @click="testCache">测试缓存机制</button> |
|
||||
<view class="result" v-if="cacheResult"> |
|
||||
<text class="result-title">缓存测试结果:</text> |
|
||||
<text class="result-content">{{ cacheResult }}</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<view class="test-section"> |
|
||||
<view class="section-title">4. 测试静默请求</view> |
|
||||
<button class="test-btn" @click="testQuietRequest">测试静默请求</button> |
|
||||
<view class="result" v-if="quietResult"> |
|
||||
<text class="result-title">静默请求结果:</text> |
|
||||
<text class="result-content">{{ JSON.stringify(quietResult, null, 2) }}</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<view class="test-section"> |
|
||||
<view class="section-title">5. 清除缓存</view> |
|
||||
<button class="test-btn clear" @click="clearAllCache">清除所有缓存</button> |
|
||||
</view> |
|
||||
</view> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
import dictUtil from '@/common/dictUtil.js' |
|
||||
import axiosQuiet from '@/common/axiosQuiet.js' |
|
||||
|
|
||||
export default { |
|
||||
data() { |
|
||||
return { |
|
||||
singleResult: null, |
|
||||
batchResult: null, |
|
||||
cacheResult: null, |
|
||||
quietResult: null |
|
||||
} |
|
||||
}, |
|
||||
methods: { |
|
||||
// 测试单个字典获取 |
|
||||
async testSingleDict() { |
|
||||
try { |
|
||||
console.log('开始测试单个字典获取') |
|
||||
const result = await dictUtil.getDict('source') |
|
||||
this.singleResult = result |
|
||||
console.log('单个字典获取结果:', result) |
|
||||
|
|
||||
uni.showToast({ |
|
||||
title: '单个字典测试完成', |
|
||||
icon: 'success' |
|
||||
}) |
|
||||
} catch (error) { |
|
||||
console.error('单个字典获取失败:', error) |
|
||||
this.singleResult = { error: error.message || '获取失败' } |
|
||||
|
|
||||
uni.showToast({ |
|
||||
title: '单个字典测试失败', |
|
||||
icon: 'none' |
|
||||
}) |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
// 测试批量字典获取 |
|
||||
async testBatchDict() { |
|
||||
try { |
|
||||
console.log('开始测试批量字典获取') |
|
||||
const keys = ['source', 'SourceChannel', 'customer_purchasing_power'] |
|
||||
const result = await dictUtil.getBatchDict(keys) |
|
||||
this.batchResult = result |
|
||||
console.log('批量字典获取结果:', result) |
|
||||
|
|
||||
uni.showToast({ |
|
||||
title: '批量字典测试完成', |
|
||||
icon: 'success' |
|
||||
}) |
|
||||
} catch (error) { |
|
||||
console.error('批量字典获取失败:', error) |
|
||||
this.batchResult = { error: error.message || '获取失败' } |
|
||||
|
|
||||
uni.showToast({ |
|
||||
title: '批量字典测试失败', |
|
||||
icon: 'none' |
|
||||
}) |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
// 测试缓存机制 |
|
||||
async testCache() { |
|
||||
try { |
|
||||
console.log('开始测试缓存机制') |
|
||||
|
|
||||
// 清除缓存 |
|
||||
dictUtil.clearCache(['source']) |
|
||||
|
|
||||
// 第一次获取(应该从接口获取) |
|
||||
const start1 = Date.now() |
|
||||
await dictUtil.getDict('source', true) |
|
||||
const time1 = Date.now() - start1 |
|
||||
|
|
||||
// 第二次获取(应该从缓存获取) |
|
||||
const start2 = Date.now() |
|
||||
await dictUtil.getDict('source', true) |
|
||||
const time2 = Date.now() - start2 |
|
||||
|
|
||||
this.cacheResult = `第一次获取: ${time1}ms, 第二次获取: ${time2}ms, 缓存提升: ${Math.round((time1 - time2) / time1 * 100)}%` |
|
||||
|
|
||||
uni.showToast({ |
|
||||
title: '缓存测试完成', |
|
||||
icon: 'success' |
|
||||
}) |
|
||||
} catch (error) { |
|
||||
console.error('缓存测试失败:', error) |
|
||||
this.cacheResult = '缓存测试失败: ' + (error.message || '未知错误') |
|
||||
|
|
||||
uni.showToast({ |
|
||||
title: '缓存测试失败', |
|
||||
icon: 'none' |
|
||||
}) |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
// 测试静默请求 |
|
||||
async testQuietRequest() { |
|
||||
try { |
|
||||
console.log('开始测试静默请求') |
|
||||
const result = await axiosQuiet.get('/dict/batch', { |
|
||||
keys: 'source,SourceChannel' |
|
||||
}) |
|
||||
this.quietResult = result |
|
||||
console.log('静默请求结果:', result) |
|
||||
|
|
||||
uni.showToast({ |
|
||||
title: '静默请求测试完成', |
|
||||
icon: 'success' |
|
||||
}) |
|
||||
} catch (error) { |
|
||||
console.error('静默请求失败:', error) |
|
||||
this.quietResult = { error: error.message || error.msg || '请求失败' } |
|
||||
|
|
||||
uni.showToast({ |
|
||||
title: '静默请求测试失败', |
|
||||
icon: 'none' |
|
||||
}) |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
// 清除所有缓存 |
|
||||
clearAllCache() { |
|
||||
dictUtil.clearCache() |
|
||||
this.singleResult = null |
|
||||
this.batchResult = null |
|
||||
this.cacheResult = null |
|
||||
this.quietResult = null |
|
||||
|
|
||||
uni.showToast({ |
|
||||
title: '缓存已清除', |
|
||||
icon: 'success' |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style lang="scss" scoped> |
|
||||
.container { |
|
||||
padding: 20rpx; |
|
||||
background: #f5f5f5; |
|
||||
min-height: 100vh; |
|
||||
} |
|
||||
|
|
||||
.header { |
|
||||
background: #fff; |
|
||||
padding: 30rpx; |
|
||||
border-radius: 12rpx; |
|
||||
margin-bottom: 20rpx; |
|
||||
text-align: center; |
|
||||
|
|
||||
.title { |
|
||||
font-size: 36rpx; |
|
||||
font-weight: 600; |
|
||||
color: #333; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.test-section { |
|
||||
background: #fff; |
|
||||
border-radius: 12rpx; |
|
||||
padding: 30rpx; |
|
||||
margin-bottom: 20rpx; |
|
||||
} |
|
||||
|
|
||||
.section-title { |
|
||||
font-size: 28rpx; |
|
||||
font-weight: 600; |
|
||||
color: #333; |
|
||||
margin-bottom: 20rpx; |
|
||||
} |
|
||||
|
|
||||
.test-btn { |
|
||||
background: #29d3b4; |
|
||||
color: #fff; |
|
||||
border: none; |
|
||||
border-radius: 8rpx; |
|
||||
padding: 16rpx 32rpx; |
|
||||
font-size: 26rpx; |
|
||||
margin-bottom: 20rpx; |
|
||||
|
|
||||
&.clear { |
|
||||
background: #ff6b6b; |
|
||||
} |
|
||||
|
|
||||
&:active { |
|
||||
opacity: 0.8; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.result { |
|
||||
background: #f8f9fa; |
|
||||
border-radius: 8rpx; |
|
||||
padding: 20rpx; |
|
||||
border-left: 4rpx solid #29d3b4; |
|
||||
|
|
||||
.result-title { |
|
||||
font-size: 24rpx; |
|
||||
color: #666; |
|
||||
display: block; |
|
||||
margin-bottom: 10rpx; |
|
||||
} |
|
||||
|
|
||||
.result-content { |
|
||||
font-size: 22rpx; |
|
||||
color: #333; |
|
||||
word-break: break-all; |
|
||||
white-space: pre-wrap; |
|
||||
font-family: monospace; |
|
||||
line-height: 1.5; |
|
||||
} |
|
||||
} |
|
||||
</style> |
|
||||
Loading…
Reference in new issue