Browse Source

修改 bug

master
王泽彦 9 months ago
parent
commit
bbd2867e1c
  1. 1
      niucloud/app/api/controller/apiController/Contract.php
  2. 216
      niucloud/app/api/controller/apiController/CourseSchedule.php
  3. 8
      uniapp/common/axios.js
  4. 66
      uniapp/common/util.js
  5. 5
      uniapp/components/AQ/AQUplodeImgMulti.vue
  6. 517
      uniapp/components/schedule/ScheduleDetail.vue
  7. 2
      uniapp/pages.json
  8. 26
      uniapp/pages/coach/my/index.vue
  9. 481
      uniapp/pages/coach/schedule/schedule_detail.vue
  10. 34
      uniapp/pages/coach/schedule/schedule_table.vue
  11. 306
      uniapp/pages/coach/student/student_list.vue
  12. 2
      uniapp/pages/common/contract/contract_detail.vue
  13. 51
      uniapp/pages/common/contract/contract_sign.vue
  14. 6
      uniapp/pages/common/contract/my_contract.vue
  15. 102
      uniapp/pages/market/clue/order_list.vue
  16. 263
      uniapp/pages/test/dict_test.vue

1
niucloud/app/api/controller/apiController/Contract.php

@ -107,7 +107,6 @@ class Contract extends BaseApiService
'personnel_id' => $this->member_id,
'sign_file' => $sign_file
];
try {
$service = new ContractService();
$res = $service->signContract($data);

216
niucloud/app/api/controller/apiController/CourseSchedule.php

@ -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));
}
}

8
uniapp/common/axios.js

@ -152,19 +152,11 @@ export default {
uni.showLoading({
title: '加载中...'
});
console.log('请求配置:', interceptedConfig);
console.log('请求URL:', interceptedConfig.url);
console.log('请求方法:', interceptedConfig.method);
console.log('请求数据:', interceptedConfig.data);
uni.request({
...interceptedConfig,
success: (res) => {
try {
console.log('原始响应数据:', res);
const response = responseInterceptor(res);
console.log('处理后的响应数据:', response);
resolve(response);
} catch (error) {
console.error('请求处理失败:', error);

66
uniapp/common/util.js

@ -1,4 +1,4 @@
import {img_domian} from "./config";
import {img_domian,Api_url} from "./config";
import marketApi from '@/api/apiRoute.js';
function formatTime(time) {
@ -326,6 +326,67 @@ async function getDict(dictKey) {
}
}
/**
* 上传文件通用方法
* @param {string} filePath 文件路径
* @param {Function} successCallback 成功回调
* @param {Function} errorCallback 失败回调
*/
export function uploadFile(filePath, successCallback, errorCallback) {
const token = uni.getStorageSync('token') || '';
uni.uploadFile({
url: Api_url + '/file/image', // 上传地址
filePath: filePath,
name: 'file',
header: {
'token': token
},
success: (res) => {
let response;
try {
// 去除 BOM 字符并解析 JSON
response = JSON.parse(res.data.replace(/\ufeff/g, '') || '{}');
} catch (e) {
uni.showToast({ title: '响应格式错误', icon: 'none' });
if (errorCallback) errorCallback(e);
return;
}
if (response.code === 1) {
const fileData = {
url: response.data.url,
extname: response.data.ext,
name: response.data.name
};
if (successCallback) {
successCallback(fileData);
}
} else if (response.code === 401) {
uni.showToast({ title: response.msg, icon: 'none' });
setTimeout(() => {
uni.navigateTo({ url: '/pages/student/login/login' });
}, 1000);
} else {
uni.showToast({ title: response.msg || '上传失败', icon: 'none' });
if (errorCallback) errorCallback(response);
}
},
fail: (err) => {
uni.showToast({ title: err.errMsg || '网络异常', icon: 'none' });
if (errorCallback) errorCallback(err);
}
});
}
/**
* 获取服务器上的资源完整 url
* @return {string} 完整的资源 URL
*/
function getResourceUrl(resource) {
//如果没有 http 协议,则加上 http 协议+服务域名
return resource.indexOf('http') === -1 ? 'https://' + img_domian + resource : resource;
}
module.exports = {
loginOut,
openHomeView,
@ -336,5 +397,6 @@ module.exports = {
hexToRgba,
img,
formatToDateTime,
getDict
getDict,
uploadFile
}

5
uniapp/components/AQ/AQUplodeImgMulti.vue

@ -159,12 +159,8 @@ import {Api_url} from "../../common/config";
header: {
'token': `${token}`,//token
},
// formData: {
// 'age': ''
// },
success: (uploadFileRes) => {
let res = JSON.parse(uploadFileRes.data.replace(/\ufeff/g, "") || "{}")
console.log('上传成功1',res);
if (res.code == 1){
let _arr = {}
// 3uni-app
@ -173,7 +169,6 @@ import {Api_url} from "../../common/config";
_arr.name = res.data.name
console.log('xxx',_arr)
this.fileList.push(_arr)
console.log('上传成功2',_arr);
this.filePathArr.push(res.data.url)
//
this.emitUploadSuccess(this.filePathArr)

517
uniapp/components/schedule/ScheduleDetail.vue

@ -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>-->
<!-- &lt;!&ndash; 学员列表 &ndash;&gt;-->
<!-- <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>

2
uniapp/pages.json

@ -669,7 +669,7 @@
{
"path": "pages/coach/schedule/schedule_table",
"style": {
"navigationBarTitleText": "课程安排",
"navigationBarTitleText": "课程安排",
"navigationBarBackgroundColor": "#292929",
"navigationBarTextStyle": "white"
}

26
uniapp/pages/coach/my/index.vue

@ -38,13 +38,6 @@
</view>
</view>
</view>
<view class="bottom">
月授课数
<text>{{statisticsInfo.courseMonthNum}}</text>
月负责学员
<text>{{statisticsInfo.studentMonthNum}}</text>
</view>
</view>
</view>
<view class="bg_box bg_top"></view>
@ -56,11 +49,6 @@
<view class="main_section">
<view class="section_box">
<!-- <view class="item" @click="openViewSportsVenue()">
<view>我的体育场</view>
<view>xxx场馆</view>
</view> -->
<view class="item" @click="openViewDueSoon()">
<view>即将到期</view>
<view></view>
@ -88,10 +76,15 @@
<view></view>
</view>
<!-- <view class="item" @click="openServiceDetail()">
<view class="item" @click="openServiceDetail()">
<view>服务详情</view>
<view></view>
</view> -->
</view>
<view class="item" @click="goCourseSchedule()">
<view>课程安排</view>
<view></view>
</view>
<view class="item" @click="my_contract()">
<view>我的合同</view>
@ -254,6 +247,11 @@
showCancel: false
})
},
goCourseSchedule(){
this.$navigateTo({
url: '/pages/coach/schedule/schedule_table'
})
}
}
}
</script>

481
uniapp/pages/coach/schedule/schedule_detail.vue

@ -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>

34
uniapp/pages/coach/schedule/schedule_table.vue

@ -230,13 +230,26 @@
</view>
</view>
</fui-modal>
<!-- 课程详情弹窗 -->
<schedule-detail
:visible="showScheduleDetail"
:scheduleId="selectedScheduleId"
@update:visible="showScheduleDetail = $event"
@sign-in="handleSignIn"
@adjust-class="handleAdjustClass"
></schedule-detail>
</view>
</template>
<script>
import api from '@/api/apiRoute.js'
import ScheduleDetail from '@/components/schedule/ScheduleDetail.vue'
export default {
components: {
ScheduleDetail
},
data() {
return {
//
@ -284,6 +297,10 @@ export default {
//
loading: false,
//
selectedScheduleId: null,
showScheduleDetail: false,
//
filterParams: {
start_date: '',
@ -727,9 +744,22 @@ export default {
//
viewScheduleDetail(scheduleId) {
//
//
this.selectedScheduleId = scheduleId;
this.showScheduleDetail = true;
},
//
handleSignIn(data) {
uni.navigateTo({
url: `/pages/coach/schedule/sign_in?id=${data.scheduleId}`,
})
},
//
handleAdjustClass(data) {
uni.navigateTo({
url: `/pages/coach/schedule/schedule_detail?id=${scheduleId}`,
url: `/pages/coach/schedule/adjust_course?id=${data.scheduleId}`,
})
},
},

306
uniapp/pages/coach/student/student_list.vue

@ -1,5 +1,9 @@
<template>
<view class="container">
<view class="search-bar" @click="showSearch = true">
<uni-icons type="search" size="22" color="#00d18c" />
<text class="search-placeholder">搜索学员...</text>
</view>
<view class="content">
<view v-if="studentList.length === 0" class="empty-box">
<image src="/static/icon-img/empty.png" mode="aspectFit" class="empty-img"></image>
@ -19,9 +23,7 @@
</view>
<view class="info-row">
<text class="info-label">剩余课程</text>
<text class="info-value">{{
(item.total_hours + item.gift_hours) - (item.use_total_hours + item.use_gift_hours)
}}</text>
<text class="info-value">{{ getRemainingCourses(item) }}</text>
</view>
<view class="info-row">
<text class="info-label">到期时间</text>
@ -35,6 +37,65 @@
</view>
</view>
</view>
<fui-drawer :show="showSearch" position="top" @close="closeSearch">
<view class="fui-page__bd">
<view class="fui-section__title">学员搜索</view>
<fui-form>
<fui-form-item label="学生姓名" required>
<fui-input :value="searchForm.name" placeholder="请输入学生姓名" @input="onNameInput"></fui-input>
</fui-form-item>
<fui-form-item label="联系电话">
<fui-input :value="searchForm.phone" placeholder="请输入联系电话" type="number" @input="onPhoneInput"></fui-input>
</fui-form-item>
<fui-form-item label="课时数量">
<fui-input :value="searchForm.lessonCount" placeholder="请输入课时数量" type="number" @input="onLessonCountInput"></fui-input>
</fui-form-item>
<fui-form-item label="请假次数">
<fui-input :value="searchForm.leaveCount" placeholder="请输入请假次数" type="number" @input="onLeaveCountInput"></fui-input>
</fui-form-item>
<fui-form-item label="课程名称">
<view class="custom-picker-input" @click="showCoursePicker = true">
<text>{{ searchForm.courseIndex >= 0 ? courseList[searchForm.courseIndex].name : '请选择' }}</text>
<fui-icon name="arrowdown" :size="32" color="#CCCCCC"></fui-icon>
</view>
<!-- 使用独立的 picker 组件 -->
<picker
v-if="showCoursePicker"
mode="selector"
:range="courseList"
range-key="name"
@change="onCourseChange"
@cancel="showCoursePicker = false"
></picker>
</fui-form-item>
<fui-form-item label="班级">
<view class="custom-picker-input" @click="showClassPicker = true">
<text>{{ searchForm.classIndex >= 0 ? classList[searchForm.classIndex].name : '请选择' }}</text>
<fui-icon name="arrowdown" :size="32" color="#CCCCCC"></fui-icon>
</view>
<!-- 使用独立的 picker 组件 -->
<picker
v-if="showClassPicker"
mode="selector"
:range="classList"
range-key="name"
@change="onClassChange"
@cancel="showClassPicker = false"
></picker>
</fui-form-item>
</fui-form>
<view class="fui-btn__box">
<fui-button type="primary" @click="doSearch">搜索</fui-button>
</view>
</view>
</fui-drawer>
<AQTabber />
</view>
</template>
@ -48,7 +109,28 @@
},
data() {
return {
studentList: []
studentList: [],
showSearch: false,
showCoursePicker: false,
showClassPicker: false,
searchForm: {
name: '',
phone: '',
lessonCount: '',
leaveCount: '',
courseIndex: -1,
classIndex: -1,
},
courseList: [
{ name: '英语基础班' },
{ name: '数学提高班' },
{ name: '编程兴趣班' },
],
classList: [
{ name: '一班' },
{ name: '二班' },
{ name: '三班' },
],
}
},
onLoad() {
@ -69,63 +151,64 @@
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=`+student.resource_sharing_id
});
},
getRemainingCourses(item) {
const totalHours = (item.total_hours || 0)
+ (item.gift_hours || 0);
const usedHours = (item.use_total_hours ||
0) + (item.use_gift_hours || 0);
return totalHours - usedHours;
},
//
onNameInput(e) {
this.searchForm.name = e;
},
onPhoneInput(e) {
this.searchForm.phone = e;
},
onLessonCountInput(e) {
this.searchForm.lessonCount = e;
},
onLeaveCountInput(e) {
this.searchForm.leaveCount = e;
},
//
onCourseChange(e) {
this.searchForm.courseIndex = e.detail.value;
},
onClassChange(e) {
this.searchForm.classIndex = e.detail.value;
},
//
closeSearch() {
this.showSearch = false;
},
doSearch() {
// searchForm
this.showSearch = false;
//
uni.showLoading({
title: '搜索中...'
});
//
setTimeout(() => {
uni.hideLoading();
uni.showToast({
title: '搜索功能待实现',
icon: 'none'
});
}, 800);
}
}
}
@ -134,7 +217,27 @@
<style lang="scss">
.container {
min-height: 100vh;
background-color: #F5F5F5;
background-color: #18181c;
}
.search-bar {
display: flex;
align-items: center;
background: #23232a;
border-radius: 12rpx;
padding: 18rpx 24rpx;
margin: 24rpx 24rpx 0 24rpx;
color: #bdbdbd;
font-size: 28rpx;
box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.08);
&:active {
background: #2a2a31;
}
}
.search-placeholder {
margin-left: 12rpx;
color: #bdbdbd;
flex: 1;
}
.content {
@ -168,10 +271,14 @@
.student-card {
display: flex;
align-items: center;
background-color: #FFFFFF;
background-color: #23232a;
border-radius: 12rpx;
padding: 30rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.12);
transition: box-shadow 0.2s;
&:active {
box-shadow: 0 4rpx 20rpx rgba(0,255,128,0.12);
}
}
.student-avatar {
@ -195,7 +302,7 @@
font-size: 32rpx;
font-weight: bold;
margin-bottom: 10rpx;
color: #333;
color: #fff;
}
.info-row {
@ -204,11 +311,11 @@
margin-top: 8rpx;
.info-label {
color: #666;
color: #bdbdbd;
}
.info-value {
color: #333;
color: #fff;
}
}
@ -216,4 +323,79 @@
padding-left: 20rpx;
}
}
.popup-content {
background: #23232a;
color: #fff;
padding: 32rpx 24rpx;
border-radius: 16rpx;
min-width: 300rpx;
text-align: left;
}
.popup-title {
font-size: 32rpx;
font-weight: bold;
color: #00d18c;
margin-bottom: 24rpx;
text-align: center;
}
.picker-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 18rpx 0;
border-bottom: 1px solid #333;
.picker-label {
color: #bdbdbd;
font-size: 28rpx;
}
.picker-value {
color: #fff;
font-size: 28rpx;
}
}
.search-btn {
width: 100%;
background: #00d18c;
color: #fff;
border: none;
border-radius: 12rpx;
font-size: 30rpx;
padding: 20rpx 0;
margin-top: 32rpx;
margin-bottom: 8rpx;
}
.fui-picker__input {
display: flex;
justify-content: space-between;
align-items: center;
height: 72rpx;
padding: 0 24rpx;
background-color: #2c2c34;
border-radius: 8rpx;
text {
font-size: 28rpx;
color: #fff;
}
}
.fui-btn__box {
margin-top: 40rpx;
padding: 0 24rpx;
}
.fui-page__bd {
padding: 30rpx;
background-color: #18181c;
}
.fui-section__title {
font-size: 36rpx;
color: #00d18c;
font-weight: bold;
margin-bottom: 30rpx;
text-align: center;
}
</style>

2
uniapp/pages/common/contract/contract_detail.vue

@ -174,7 +174,7 @@ export default {
id: this.contractId
})
if (response.data.code === 1) {
if (response.code === 1) {
this.contractData = response.data.data || {}
} else {
uni.showToast({

51
uniapp/pages/common/contract/contract_sign.vue

@ -113,6 +113,7 @@
<script>
import apiRoute from '@/common/axios.js'
import { uploadFile } from '@/common/util.js';
export default {
data() {
@ -335,12 +336,12 @@ export default {
}
//
const response = await apiRoute.post('/contract/sign', {
contract_id: this.contractId,
const response = await apiRoute.post('/member/contract_sign', {
contract_sign_id: this.contractId,
sign_file: uploadResult.url
})
if (response.data.code === 1) {
if (response.code === 1) {
uni.showToast({
title: '签名提交成功',
icon: 'success',
@ -368,44 +369,18 @@ export default {
},
//
uploadSignatureFile() {
async uploadSignatureFile() {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: this.$baseUrl + '/uploadImage',
filePath: this.signatureImageUrl,
name: 'file',
header: {
'Authorization': uni.getStorageSync('token')
uploadFile(
this.signatureImageUrl,
(fileData) => {
resolve({ success: true, url: fileData.url });
},
success: (res) => {
try {
const data = JSON.parse(res.data)
if (data.code === 1) {
resolve({
success: true,
url: data.data.url
})
} else {
resolve({
success: false,
message: data.msg || '上传失败'
})
(err) => {
resolve({ success: false, message: '上传失败' });
}
} catch (error) {
resolve({
success: false,
message: '上传响应解析失败'
})
}
},
fail: (error) => {
resolve({
success: false,
message: '上传请求失败'
})
}
})
})
);
});
},
}

6
uniapp/pages/common/contract/my_contract.vue

@ -142,10 +142,8 @@ export default {
page: this.currentPage,
limit: this.pageSize
})
if (response.data.code === 1) {
const newContracts = response.data.data.data || []
if (response.code === 1) {
const newContracts = response.data.data || []
if (refresh) {
this.contracts = newContracts
} else {

102
uniapp/pages/market/clue/order_list.vue

@ -58,6 +58,17 @@
<view class="content">{{ v.payment_time || '' }}</view>
</view>
<!-- 已支付状态下显示的按钮区域 -->
<view class="action-buttons" v-if="v.order_status !== 'pending'">
<view class="action-btn contract-btn" @click.stop="handleContractSign(v)">
<fui-icon name="calendar" color="#29D3B4" size="32"></fui-icon>
<text>合同签订</text>
</view>
<view class="action-btn invoice-btn" @click.stop="handleInvoiceDownload(v)">
<fui-icon name="download" color="#29D3B4" size="32"></fui-icon>
<text>发票下载</text>
</view>
</view>
</view>
</view>
@ -892,8 +903,70 @@ export default {
})
},
handleButtonClick(){
//
handleButtonClick() {
this.showCustomModal = false
uni.showToast({
title: '二维码已发送',
icon: 'success'
})
},
//
handleContractSign(orderData) {
console.log('处理合同签订:', orderData)
//
event.stopPropagation()
//
uni.showLoading({
title: '加载中...'
})
//
setTimeout(() => {
uni.hideLoading()
//
uni.navigateTo({
url: `/pages/market/clue/contract_sign?order_id=${orderData.id}`
}).catch(err => {
//
console.error('导航错误:', err)
uni.showToast({
title: '合同签订功能开发中',
icon: 'none'
})
})
}, 500)
},
//
handleInvoiceDownload(orderData) {
console.log('处理发票下载:', orderData)
//
event.stopPropagation()
//
if (!orderData.invoice_url) {
uni.showToast({
title: '暂无电子发票',
icon: 'none'
})
return
}
//
uni.showLoading({
title: '准备下载...'
})
//
this.downloadFile(orderData.invoice_url || this.$util.img(orderData.file_data))
setTimeout(() => {
uni.hideLoading()
}, 1000)
}
},
}
@ -985,6 +1058,33 @@ export default {
width: 100%;
}
}
//
.action-buttons {
display: flex;
justify-content: space-between;
margin-top: 30rpx;
padding-top: 20rpx;
border-top: 1px solid #555;
.action-btn {
display: flex;
align-items: center;
justify-content: center;
flex: 1;
padding: 16rpx 0;
text {
margin-left: 10rpx;
color: #29D3B4;
font-size: 28rpx;
}
&.contract-btn {
border-right: 1px solid #555;
}
}
}
}
}

263
uniapp/pages/test/dict_test.vue

@ -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…
Cancel
Save