Browse Source

修改 bug

master
王泽彦 8 months ago
parent
commit
eac8be7a58
  1. 28
      niucloud/app/api/controller/apiController/OrderTable.php
  2. 28
      niucloud/app/api/controller/student/CourseBookingController.php
  3. 10
      niucloud/app/api/controller/sys/Index.php
  4. 1
      niucloud/app/api/route/pay.php
  5. 17
      niucloud/app/api/route/route.php
  6. 14
      niucloud/app/api/route/student.php
  7. 2
      niucloud/app/common.php
  8. 1
      niucloud/app/dict/sys/FileDict.php
  9. 6
      niucloud/app/service/api/apiService/OrderTableService.php
  10. 5
      niucloud/app/service/api/pay/PayService.php
  11. 31
      uniapp/App.vue
  12. 5
      uniapp/api/apiRoute.js
  13. 2
      uniapp/common/config.js
  14. 47
      uniapp/components/course-info-card/index.vue
  15. 46
      uniapp/components/fitness-record-popup/fitness-record-popup.vue
  16. 113
      uniapp/pages-market/clue/class_arrangement_detail.vue
  17. 2
      uniapp/pages-market/clue/clue_info.vue
  18. 2
      uniapp/pages-market/clue/index.vue
  19. 1
      uniapp/pages-student/knowledge/index.vue
  20. 783
      uniapp/pages-student/orders/detail.vue
  21. 348
      uniapp/pages-student/orders/index.vue
  22. 9
      uniapp/pages.json
  23. 173
      uniapp/pages/common/dashboard/webview.vue
  24. 97
      uniapp/pages/market/clue/clue_info.vue

28
niucloud/app/api/controller/apiController/OrderTable.php

@ -181,4 +181,32 @@ class OrderTable extends BaseApiService
return success($res['data']); return success($res['data']);
} }
//查询订单支付状态
public function checkOrderPaymentStatus(Request $request)
{
$order_no = $request->param('order_no', ''); // 订单号
if (empty($order_no)) {
return fail('缺少订单号参数');
}
// 查询订单状态
$order = \app\model\order_table\OrderTable::where('payment_id', $order_no)->find();
if (!$order) {
return fail('订单不存在');
}
$orderData = $order->toArray();
return success([
'order_id' => $orderData['id'],
'order_no' => $order_no,
'order_status' => $orderData['order_status'],
'order_amount' => $orderData['order_amount'],
'payment_type' => $orderData['payment_type'],
'updated_at' => $orderData['updated_at']
]);
}
} }

28
niucloud/app/api/controller/student/CourseBookingController.php

@ -38,14 +38,32 @@ class CourseBookingController extends BaseController
return fail('学员ID参数无效'); return fail('学员ID参数无效');
} }
$this->validate($data, [ // 清理"undefined"参数
foreach (['date', 'start_date', 'end_date', 'coach_id', 'venue_id', 'course_type'] as $field) {
if (isset($data[$field]) && ($data[$field] === 'undefined' || $data[$field] === null)) {
$data[$field] = '';
}
}
// 构建验证规则,只对非空值进行验证
$rules = [
'student_id' => 'require|integer|gt:0', 'student_id' => 'require|integer|gt:0',
'date' => 'date',
'start_date' => 'date',
'end_date' => 'date',
'page' => 'integer|egt:1', 'page' => 'integer|egt:1',
'limit' => 'integer|between:1,50' 'limit' => 'integer|between:1,50'
]); ];
// 只对非空的日期字段进行验证
if (!empty($data['date'])) {
$rules['date'] = 'date';
}
if (!empty($data['start_date'])) {
$rules['start_date'] = 'date';
}
if (!empty($data['end_date'])) {
$rules['end_date'] = 'date';
}
$this->validate($data, $rules);
try { try {
$service = new CourseBookingService(); $service = new CourseBookingService();

10
niucloud/app/api/controller/sys/Index.php

@ -12,6 +12,8 @@
namespace app\api\controller\sys; namespace app\api\controller\sys;
use app\listener\personnel\CalculatePerformance; use app\listener\personnel\CalculatePerformance;
use app\model\order_table\OrderTable;
use app\service\api\apiService\OrderTableService;
use core\base\BaseController; use core\base\BaseController;
use think\facade\App; use think\facade\App;
@ -111,4 +113,12 @@ class Index extends BaseController
]); ]);
dd(123); dd(123);
} }
public function testfun1()
{
$order_id = $this->request->get('order_id');
$order_info = OrderTable::where(['id' => $order_id])->find();
(new OrderTableService())->handlePaymentSuccess($order_info->toArray());
dd($order_info);
}
} }

1
niucloud/app/api/route/pay.php

@ -22,7 +22,6 @@ Route::any('pay/notify/:channel/:type/:action', 'pay.Pay/notify')
Route::any('pay/qrcodenotify/order_id/:order_id', 'pay.Pay/qrcodeNotify') Route::any('pay/qrcodenotify/order_id/:order_id', 'pay.Pay/qrcodeNotify')
->middleware(ApiChannel::class) ->middleware(ApiChannel::class)
->middleware(ApiCheckToken::class)
->middleware(ApiLog::class); ->middleware(ApiLog::class);
/** /**
* 路由 * 路由

17
niucloud/app/api/route/route.php

@ -41,6 +41,7 @@ Route::group(function () {
// 发送验证码不需要token验证 // 发送验证码不需要token验证
Route::post('send/mobile/:type', 'login.Login/sendMobileCode'); Route::post('send/mobile/:type', 'login.Login/sendMobileCode');
Route::get('testfun1', 'sys.Index/testfun1');
}); });
/** /**
@ -300,6 +301,8 @@ Route::group(function () {
Route::post('orderTable/add', 'apiController.OrderTable/add'); Route::post('orderTable/add', 'apiController.OrderTable/add');
//员工端-订单管理-更新支付状态 //员工端-订单管理-更新支付状态
Route::post('orderTable/updatePaymentStatus', 'apiController.OrderTable/updatePaymentStatus'); Route::post('orderTable/updatePaymentStatus', 'apiController.OrderTable/updatePaymentStatus');
//员工端-查询订单支付状态
Route::get('checkOrderPaymentStatus', 'apiController.OrderTable/checkOrderPaymentStatus');
//员工端-更新学员课程人员配置 //员工端-更新学员课程人员配置
Route::post('updateStudentCoursePersonnel', 'apiController.Course/updateStudentCoursePersonnel'); Route::post('updateStudentCoursePersonnel', 'apiController.Course/updateStudentCoursePersonnel');
@ -570,20 +573,6 @@ Route::group(function () {
})->middleware(ApiChannel::class) })->middleware(ApiChannel::class)
->middleware(ApiPersonnelCheckToken::class, true) ->middleware(ApiPersonnelCheckToken::class, true)
->middleware(ApiLog::class); ->middleware(ApiLog::class);
//↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑-----学生用户端相关-----↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
// 学员端公开接口(无需认证)
Route::group(function () {
//学生端-订单管理-列表(新接口,公开访问)
Route::get('xy/student/orders', 'app\api\controller\student\OrderController@getOrderList');
//学生端-订单管理-详情(新接口,公开访问)
Route::get('xy/student/orders/detail', 'app\api\controller\student\OrderController@getOrderDetail');
//学生端-订单管理-统计(新接口,公开访问)
Route::get('xy/student/orders/stats', 'app\api\controller\student\OrderController@getOrderStats');
})->middleware(ApiChannel::class)
->middleware(ApiLog::class);
//学员端路由 //学员端路由
include_once __DIR__ . '/student.php'; include_once __DIR__ . '/student.php';

14
niucloud/app/api/route/student.php

@ -71,13 +71,13 @@ Route::group('course-schedule', function () {
})->middleware(['ApiCheckToken']); })->middleware(['ApiCheckToken']);
// 订单管理 // 订单管理
Route::group('order', function () { Route::group('xy', function () {
// 获取订单列表 //学生端-订单管理-列表(新接口,公开访问)
Route::get('list', 'app\api\controller\student\OrderController@getOrderList'); Route::get('student/orders', 'app\api\controller\student\OrderController@getOrderList');
// 获取订单详情 //学生端-订单管理-详情(新接口,公开访问)
Route::get('detail/:order_id', 'app\api\controller\student\OrderController@getOrderDetail'); Route::get('student/orders/detail', 'app\api\controller\student\OrderController@getOrderDetail');
// 获取订单统计 //学生端-订单管理-统计(新接口,公开访问)
Route::get('stats', 'app\api\controller\student\OrderController@getOrderStats'); Route::get('student/orders/stats', 'app\api\controller\student\OrderController@getOrderStats');
})->middleware(['ApiCheckToken']); })->middleware(['ApiCheckToken']);
// 支付管理 // 支付管理

2
niucloud/app/common.php

@ -1104,7 +1104,7 @@ function return_pay_config($campus_id, $order_id)
$sysConfig = new SysConfig(); $sysConfig = new SysConfig();
$vx_config = $sysConfig->where(['config_key' => 'WECHAT'])->value("value"); $vx_config = $sysConfig->where(['config_key' => 'weapp'])->value("value");
if (!$vx_config || !$pay_config) { if (!$vx_config || !$pay_config) {
throw new \Exception('当前校区支付配置不存在'); throw new \Exception('当前校区支付配置不存在');

1
niucloud/app/dict/sys/FileDict.php

@ -73,6 +73,7 @@ class FileDict
self::APPLET,//小程序包上传 self::APPLET,//小程序包上传
self::EXCEL,//excel导入 self::EXCEL,//excel导入
self::PDF,//PDF文档 self::PDF,//PDF文档
self::DOCUMENT,//文档上传
]; ];
} }

6
niucloud/app/service/api/apiService/OrderTableService.php

@ -403,7 +403,11 @@ class OrderTableService extends BaseApiService
$course_name = $course['course_name']; $course_name = $course['course_name'];
} }
} }
$status = Db::table('school_pay')->where('out_trade_no', $payment_id)->find();
if ($status){
\think\facade\Log::warning('创建支付记录失败:支付单号已存在', ['order_id' => $order_id, 'payment_id' => $payment_id]);
return false;
}
$now = time(); $now = time();
$insertData = [ $insertData = [
'main_id' => $order_id, // 订单ID 'main_id' => $order_id, // 订单ID

5
niucloud/app/service/api/pay/PayService.php

@ -20,6 +20,7 @@ use app\model\order_table\OrderTable;
use app\model\pay\Pay; use app\model\pay\Pay;
use app\model\student\Student; use app\model\student\Student;
use app\model\sys\Poster; use app\model\sys\Poster;
use app\service\api\apiService\OrderTableService;
use app\service\core\member\CoreMemberService; use app\service\core\member\CoreMemberService;
use app\service\core\pay\CorePayService; use app\service\core\pay\CorePayService;
use core\base\BaseApiService; use core\base\BaseApiService;
@ -165,7 +166,7 @@ class PayService extends BaseApiService
*/ */
public function qrcodeNotify($data,$order_id) public function qrcodeNotify($data,$order_id)
{ {
Log::debug('qrcodeNotify',$data);
$order = new OrderTable(); $order = new OrderTable();
$order_info = $order->where(['id' => $order_id])->find(); $order_info = $order->where(['id' => $order_id])->find();
if($order_info['order_status'] == 'pending' and !empty($order_info['ipv3'])){ if($order_info['order_status'] == 'pending' and !empty($order_info['ipv3'])){
@ -187,7 +188,7 @@ class PayService extends BaseApiService
'pay_time' => time() 'pay_time' => time()
]); ]);
event('Student', ['event_type' => 'add','data' => $order_info]); (new OrderTableService())->handlePaymentSuccess($order_info->toArray());
} }
} }

31
uniapp/App.vue

@ -52,8 +52,12 @@
checkForUpdate() { checkForUpdate() {
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
try { try {
//
if (typeof uni !== 'undefined' && typeof uni.getUpdateManager === 'function') {
const updateManager = uni.getUpdateManager(); const updateManager = uni.getUpdateManager();
// updateManager
if (updateManager && typeof updateManager.checkForUpdate === 'function') {
// //
updateManager.onCheckForUpdate((res) => { updateManager.onCheckForUpdate((res) => {
console.log('检查更新结果:', res); console.log('检查更新结果:', res);
@ -85,6 +89,12 @@
// //
updateManager.checkForUpdate(); updateManager.checkForUpdate();
} else {
console.log('updateManager 或 checkForUpdate 方法不可用');
}
} else {
console.log('uni.getUpdateManager 不可用,可能不在微信小程序环境中');
}
} catch (error) { } catch (error) {
console.error('更新管理器初始化失败:', error); console.error('更新管理器初始化失败:', error);
} }
@ -124,8 +134,29 @@
applyUpdate() { applyUpdate() {
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
try { try {
//
if (typeof uni !== 'undefined' && typeof uni.getUpdateManager === 'function') {
const updateManager = uni.getUpdateManager(); const updateManager = uni.getUpdateManager();
// updateManager
if (updateManager && typeof updateManager.applyUpdate === 'function') {
updateManager.applyUpdate(); updateManager.applyUpdate();
} else {
console.log('updateManager 或 applyUpdate 方法不可用');
uni.showToast({
title: '重启功能不可用',
icon: 'none',
duration: 2000
});
}
} else {
console.log('uni.getUpdateManager 不可用,可能不在微信小程序环境中');
uni.showToast({
title: '重启功能不可用',
icon: 'none',
duration: 2000
});
}
} catch (error) { } catch (error) {
console.error('应用更新失败:', error); console.error('应用更新失败:', error);
uni.showToast({ uni.showToast({

5
uniapp/api/apiRoute.js

@ -806,6 +806,11 @@ export default {
return await http.get('/getQrcode', data); return await http.get('/getQrcode', data);
}, },
// 查询订单支付状态
async checkOrderPaymentStatus(data = {}) {
return await http.get('/checkOrderPaymentStatus', data);
},
//↓↓↓↓↓↓↓↓↓↓↓↓-----课程预约相关接口-----↓↓↓↓↓↓↓↓↓↓↓↓ //↓↓↓↓↓↓↓↓↓↓↓↓-----课程预约相关接口-----↓↓↓↓↓↓↓↓↓↓↓↓
// 获取可预约课程列表 // 获取可预约课程列表
async getAvailableCourses(data = {}) { async getAvailableCourses(data = {}) {

2
uniapp/common/config.js

@ -1,5 +1,5 @@
// 环境变量配置 // 环境变量配置
const env = 'development' const env = 'prod'
// const env = 'prod' // const env = 'prod'
const isMockEnabled = false // 默认禁用Mock优先模式,仅作为回退 const isMockEnabled = false // 默认禁用Mock优先模式,仅作为回退
const isDebug = false // 默认启用调试模式 const isDebug = false // 默认启用调试模式

47
uniapp/components/course-info-card/index.vue

@ -15,7 +15,7 @@
<view :class="['course-status',getStatusClass(course.status)]"> <view :class="['course-status',getStatusClass(course.status)]">
{{ getStatusText(course.status) }} {{ getStatusText(course.status) }}
</view> </view>
<view class="edit-btn" @tap.stop="editCourse(course)"> <view class="edit-btn" @tap.stop="handleEditClick" :data-course-index="index">
<text class="edit-icon"></text> <text class="edit-icon"></text>
</view> </view>
</view> </view>
@ -252,15 +252,57 @@ export default {
methods: { methods: {
// //
viewCourseDetail(course) { viewCourseDetail(course) {
if (!course) {
console.error('viewCourseDetail: course参数为空')
uni.showToast({
title: '课程信息不存在',
icon: 'none'
})
return
}
this.$emit('view-detail', course) this.$emit('view-detail', course)
}, },
//
handleEditClick(e) {
console.log('编辑按钮点击事件:', e)
const courseIndex = parseInt(e.currentTarget.dataset.courseIndex)
console.log('课程索引:', courseIndex, '类型:', typeof courseIndex)
if (!isNaN(courseIndex) && this.courseList && this.courseList[courseIndex]) {
const course = this.courseList[courseIndex]
console.log('通过索引获取到的课程数据:', course)
this.editCourse(course)
} else {
console.error('无法获取课程数据', {
courseIndex,
courseListLength: this.courseList ? this.courseList.length : 0,
courseList: this.courseList
})
uni.showToast({
title: '课程信息获取失败',
icon: 'none'
})
}
},
// //
async editCourse(course) { async editCourse(course) {
console.log('编辑课程数据:', course) console.log('编辑课程数据:', course)
// course
if (!course) {
console.error('editCourse: course参数为空')
uni.showToast({
title: '课程信息不存在,请重新加载页面',
icon: 'none'
})
return
}
// //
if (!course.id && !course.student_course_id) { if (!course.id && !course.student_course_id) {
console.error('editCourse: 缺少课程ID', { id: course.id, student_course_id: course.student_course_id })
uni.showToast({ uni.showToast({
title: '课程信息不完整,请重新加载', title: '课程信息不完整,请重新加载',
icon: 'none' icon: 'none'
@ -269,6 +311,7 @@ export default {
} }
if (!course.resource_id) { if (!course.resource_id) {
console.error('editCourse: 缺少resource_id', { resource_id: course.resource_id })
uni.showToast({ uni.showToast({
title: '缺少学员信息,无法编辑', title: '缺少学员信息,无法编辑',
icon: 'none' icon: 'none'
@ -769,6 +812,7 @@ export default {
// //
getProgressPercent(course) { getProgressPercent(course) {
if (!course) return 0
if (!course.total_count || course.total_count === 0) return 0 if (!course.total_count || course.total_count === 0) return 0
const used = course.used_count || 0 const used = course.used_count || 0
return Math.round((used / course.total_count) * 100) return Math.round((used / course.total_count) * 100)
@ -776,6 +820,7 @@ export default {
// //
getRemainingCount(course) { getRemainingCount(course) {
if (!course) return 0
const total = course.total_count || 0 const total = course.total_count || 0
const used = course.used_count || 0 const used = course.used_count || 0
return Math.max(0, total - used) return Math.max(0, total - used)

46
uniapp/components/fitness-record-popup/fitness-record-popup.vue

@ -193,7 +193,7 @@ export default {
test_date: this.recordData.test_date, test_date: this.recordData.test_date,
height: this.recordData.height, height: this.recordData.height,
weight: this.recordData.weight, weight: this.recordData.weight,
// PDF // PDF - 使server_pathurl
physical_test_report: this.recordData.pdf_files physical_test_report: this.recordData.pdf_files
.map(file => file.server_path || file.url) .map(file => file.server_path || file.url)
.filter(path => path) .filter(path => path)
@ -216,7 +216,7 @@ export default {
this.close() this.close()
} catch (error) { } catch (error) {
console.error('保存体测记录失败:', error) console.error('执行保存体测记录失败:', error)
uni.showToast({ uni.showToast({
title: '保存失败,请重试', title: '保存失败,请重试',
icon: 'none' icon: 'none'
@ -331,7 +331,8 @@ export default {
console.error('上传PDF文件失败:', error) console.error('上传PDF文件失败:', error)
uni.showToast({ uni.showToast({
title: '文件上传失败: ' + (error.msg || error.message || '网络异常'), title: '文件上传失败: ' + (error.msg || error.message || '网络异常'),
icon: 'none' icon: 'none',
duration: 3000
}) })
} }
} else { } else {
@ -375,45 +376,63 @@ export default {
const { Api_url } = require('@/common/config.js') const { Api_url } = require('@/common/config.js')
const token = uni.getStorageSync('token') || '' const token = uni.getStorageSync('token') || ''
console.log('开始上传PDF文件:', {
fileName: file.name,
fileSize: file.size,
filePath: file.path,
uploadUrl: Api_url + '/memberUploadDocument',
hasToken: !!token
})
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
uni.uploadFile({ uni.uploadFile({
url: Api_url + '/memberUploadDocument', // 使 url: Api_url + '/memberUploadDocument', // 使
filePath: file.path, filePath: file.path,
name: 'file', name: 'file',
formData: { formData: {
type: 'pdf' // PDF 'type': 'document' //
}, },
header: { header: {
'token': token 'token': token
// Content-Typeuni-app multipart/form-data
}, },
success: (res) => { success: (res) => {
console.log('PDF上传原始响应:', res)
let response let response
try { try {
// BOM JSON // BOM JSON
response = JSON.parse(res.data.replace(/\ufeff/g, '') || '{}') response = JSON.parse(res.data.replace(/\ufeff/g, '') || '{}')
console.log('PDF上传响应:', response) console.log('PDF上传解析后响应:', response)
} catch (e) { } catch (e) {
console.error('PDF上传响应解析失败:', e, 'raw response:', res.data) console.error('PDF上传响应解析失败:', e, 'raw response:', res.data)
reject({ reject({
code: 0, code: 0,
msg: '服务器响应格式错误', msg: '服务器响应格式错误: ' + res.data,
error: e error: e
}) })
return return
} }
if (response.code === 1) { if (response.code === 1) {
console.log('PDF上传成功:', response.data)
//
let fileUrl = ''
if (response.data) {
fileUrl = response.data.url || response.data.file_path || response.data
}
resolve({ resolve({
code: 1, code: 1,
msg: response.msg || '上传成功', msg: response.msg || '上传成功',
data: { data: {
url: response.data.url, url: fileUrl,
name: response.data.name || file.name, name: response.data.name || response.data.original_name || file.name,
ext: response.data.ext || 'pdf', ext: response.data.ext || 'pdf',
size: file.size size: file.size
} }
}) })
} else if (response.code === 401) { } else if (response.code === 401) {
console.error('PDF上传认证失败:', response)
uni.showToast({ uni.showToast({
title: response.msg || '登录已过期', title: response.msg || '登录已过期',
icon: 'none' icon: 'none'
@ -423,10 +442,15 @@ export default {
}, 1500) }, 1500)
reject(response) reject(response)
} else { } else {
console.error('上传失败响应:', response) console.error('PDF上传失败响应:', response)
//
let errorMsg = response.msg || 'PDF上传失败'
if (response.code === 0 && response.msg.includes('路由未定义')) {
errorMsg = '文件上传接口暂不可用,请联系技术支持'
}
reject({ reject({
code: response.code || 0, code: response.code || 0,
msg: response.msg || 'PDF上传失败' msg: errorMsg
}) })
} }
}, },

113
uniapp/pages-market/clue/class_arrangement_detail.vue

@ -200,27 +200,39 @@
</view> </view>
<!-- 请假原因弹窗 --> <!-- 请假原因弹窗 -->
<fui-modal ref="leaveReasonModal" :buttons="[]" width="600" title="请假申请"> <uni-popup ref="leaveReasonModal" type="dialog">
<view class="leave-modal">
<view class="leave-modal-header">
<text class="leave-modal-title">请假申请</text>
</view>
<view class="leave-form"> <view class="leave-form">
<view class="leave-label">请假原因</view> <view class="leave-label">请假原因</view>
<view class="leave-input"> <view class="leave-input">
<fui-textarea v-model="leaveReason" placeholder="请输入请假原因" :isCounter="true" :maxlength="200" <textarea
minHeight="200" :isAutoHeight="true"></fui-textarea> v-model="leaveReason"
placeholder="请输入请假原因"
maxlength="200"
class="leave-textarea"
></textarea>
</view> </view>
<view class="leave-buttons"> <view class="leave-buttons">
<fui-button background="#434544" color="#fff" borderColor="#666" <view class="leave-btn cancel-btn" @tap="closeLeaveModal">取消</view>
@tap="$refs.leaveReasonModal.close()">取消</fui-button> <view class="leave-btn confirm-btn" @tap="submitLeaveRequest">提交</view>
<fui-button background="#29d3b4" color="#fff" </view>
@tap="submitLeaveRequest">提交</fui-button>
</view> </view>
</view> </view>
</fui-modal> </uni-popup>
</view> </view>
</template> </template>
<script> <script>
import apiRoute from '@/api/apiRoute.js'; import apiRoute from '@/api/apiRoute.js';
export default { export default {
name: 'ClassArrangementDetail',
components: {
// FirstUI
},
data() { data() {
return { return {
course_id: '', course_id: '',
@ -234,7 +246,7 @@
leaveReason: '', // leaveReason: '', //
currentStudent: null, // currentStudent: null, //
formalEmptySeats: [], // formalEmptySeats: [], //
waitingEmptySeats: [1, 2], // 2 waitingEmptySeats: [1], // 1
// //
showModal: false, showModal: false,
@ -850,8 +862,8 @@
let remainingFormalSeats = 0; let remainingFormalSeats = 0;
if (maxStudents === 0) { if (maxStudents === 0) {
// max_students0 // max_students0
// 6 // 1
remainingFormalSeats = Math.max(1, 6 - occupiedFormalSeats); remainingFormalSeats = 1;
} else { } else {
// //
remainingFormalSeats = Math.max(0, maxStudents - occupiedFormalSeats); remainingFormalSeats = Math.max(0, maxStudents - occupiedFormalSeats);
@ -865,14 +877,10 @@
this.formalEmptySeats.push(i); this.formalEmptySeats.push(i);
} }
// 2 // 1
const remainingWaitingSeats = Math.max(0, 2 - this.waitingStudents.length); this.waitingEmptySeats = [1];
this.waitingEmptySeats = [];
for (let i = 1; i <= remainingWaitingSeats; i++) {
this.waitingEmptySeats.push(i);
}
console.log('等待位剩余空位:', remainingWaitingSeats); console.log('等待位空位:', this.waitingEmptySeats.length);
}, },
async addStudent(e, index, type = 'formal') { async addStudent(e, index, type = 'formal') {
console.log('添加学员到位置:', index, '类型:', type); console.log('添加学员到位置:', index, '类型:', type);
@ -932,6 +940,12 @@
}); });
} }
}, },
//
closeLeaveModal() {
this.$refs.leaveReasonModal.close();
this.leaveReason = '';
},
// //
submitLeaveRequest() { submitLeaveRequest() {
if (!this.leaveReason.trim()) { if (!this.leaveReason.trim()) {
@ -964,8 +978,7 @@
content: '请假申请提交成功', content: '请假申请提交成功',
showCancel: false, showCancel: false,
}) })
this.$refs.leaveReasonModal.close(); this.closeLeaveModal();
this.leaveReason = ''; //
// //
this.getScheduleDetail().then(() => { this.getScheduleDetail().then(() => {
@ -1071,14 +1084,16 @@
.cards-grid { .cards-grid {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 20rpx; margin-right: -20rpx; /* 抵消最后一列的右边距 */
} }
.student-card { .student-card {
background: #333; background: #333;
border-radius: 12rpx; border-radius: 12rpx;
padding: 24rpx; padding: 24rpx;
width: calc(50% - 10rpx); width: calc(50% - 20rpx);
margin-right: 20rpx;
margin-bottom: 20rpx;
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
@ -1477,9 +1492,27 @@
} }
/* 请假弹窗样式 */ /* 请假弹窗样式 */
.leave-modal {
background: #fff;
border-radius: 16rpx;
width: 600rpx;
max-width: 80vw;
}
.leave-modal-header {
padding: 40rpx 40rpx 20rpx 40rpx;
text-align: center;
border-bottom: 1rpx solid #eee;
.leave-modal-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
}
.leave-form { .leave-form {
padding: 20rpx; padding: 40rpx;
width: 100%;
.leave-label { .leave-label {
font-size: 28rpx; font-size: 28rpx;
@ -1488,15 +1521,41 @@
} }
.leave-input { .leave-input {
margin-bottom: 30rpx; margin-bottom: 40rpx;
.leave-textarea {
width: 100%;
min-height: 200rpx;
padding: 20rpx;
border: 1rpx solid #ddd;
border-radius: 8rpx;
font-size: 28rpx;
box-sizing: border-box;
resize: none;
background: #f9f9f9;
}
} }
.leave-buttons { .leave-buttons {
display: flex; display: flex;
justify-content: space-between;
gap: 20rpx; gap: 20rpx;
.fui-button {
.leave-btn {
flex: 1; flex: 1;
padding: 24rpx;
text-align: center;
border-radius: 8rpx;
font-size: 28rpx;
&.cancel-btn {
background: #f5f5f5;
color: #666;
}
&.confirm-btn {
background: #29d3b4;
color: #fff;
}
} }
} }
} }

2
uniapp/pages-market/clue/clue_info.vue

@ -235,7 +235,7 @@
</view> </view>
</uni-popup> </uni-popup>
<FitnessRecordPopup ref="fitnessRecordPopup" :resource-id="clientInfo.resource_id" :student-id="currentStudent && currentStudent.id" @confirm="handleFitnessRecordConfirm" /> <FitnessRecordPopup ref="fitnessRecordPopup" :resource-id="String(clientInfo.resource_id)" :student-id="currentStudent && currentStudent.id" @confirm="handleFitnessRecordConfirm" />
<StudentEditPopup ref="studentEditPopup" :resource-id="clientInfo.resource_id" @confirm="handleStudentEditConfirm" /> <StudentEditPopup ref="studentEditPopup" :resource-id="clientInfo.resource_id" @confirm="handleStudentEditConfirm" />
<StudyPlanPopup ref="studyPlanPopup" :student-id="currentStudent && currentStudent.id" @confirm="handleStudyPlanConfirm" /> <StudyPlanPopup ref="studyPlanPopup" :student-id="currentStudent && currentStudent.id" @confirm="handleStudyPlanConfirm" />

2
uniapp/pages-market/clue/index.vue

@ -36,7 +36,7 @@
<view class="card-con" v-if="v.customerResource.source_channel"> <view class="card-con" v-if="v.customerResource.source_channel">
来源渠道{{ v.customerResource.source_channel }} 来源渠道{{ v.customerResource.source_channel }}
</view> </view>
<view class="card-con" v-if="v.sixSpeed.consultation_remark"> <view class="card-con" v-if="v.sixSpeed && v.sixSpeed.consultation_remark">
到访备注{{ v.sixSpeed.consultation_remark || '' }} 到访备注{{ v.sixSpeed.consultation_remark || '' }}
</view> </view>
</view> </view>

1
uniapp/pages-student/knowledge/index.vue

@ -192,6 +192,7 @@
<script> <script>
import apiRoute from '@/api/apiRoute.js' import apiRoute from '@/api/apiRoute.js'
import studentRoute from '@/api/studentRoute.js'
export default { export default {
data() { data() {

783
uniapp/pages-student/orders/detail.vue

@ -0,0 +1,783 @@
<!--学员订单详情页面-->
<template>
<view class="main_box">
<!-- 自定义导航栏 -->
<view class="navbar_section">
<view class="navbar_back" @click="goBack">
<text class="back_icon"></text>
</view>
<view class="navbar_title">订单详情</view>
<view class="navbar_action"></view>
</view>
<!-- 加载状态 -->
<view v-if="loading" class="loading_section">
<view class="loading_text">加载中...</view>
</view>
<!-- 订单详情内容 -->
<view v-else-if="orderDetail" class="detail_content">
<!-- 订单状态 -->
<view class="status_section">
<view class="status_icon" :class="orderDetail.status">
<text class="icon_text">{{ getStatusIcon(orderDetail.status) }}</text>
</view>
<view class="status_info">
<view class="status_text">{{ getStatusText(orderDetail.status) }}</view>
<view class="status_desc">{{ getStatusDesc(orderDetail.status) }}</view>
</view>
</view>
<!-- 订单信息 -->
<view class="order_info_section">
<view class="section_title">订单信息</view>
<view class="info_list">
<view class="info_item">
<text class="label">订单号</text>
<text class="value">{{ orderDetail.order_no }}</text>
</view>
<view class="info_item">
<text class="label">下单时间</text>
<text class="value">{{ formatDateTime(orderDetail.create_time) }}</text>
</view>
<view class="info_item" v-if="orderDetail.payment_time">
<text class="label">支付时间</text>
<text class="value">{{ formatDateTime(orderDetail.payment_time) }}</text>
</view>
<view class="info_item">
<text class="label">订单金额</text>
<text class="value amount">¥{{ orderDetail.total_amount }}</text>
</view>
<view class="info_item" v-if="orderDetail.payment_method">
<text class="label">支付方式</text>
<text class="value">{{ orderDetail.payment_method }}</text>
</view>
</view>
</view>
<!-- 课程信息 -->
<view class="course_info_section">
<view class="section_title">课程信息</view>
<view class="course_card">
<view class="course_header">
<view class="course_name">{{ orderDetail.product_name }}</view>
<view class="course_amount">¥{{ orderDetail.total_amount }}</view>
</view>
<view class="course_details">
<view class="detail_item" v-if="orderDetail.product_specs">
<text class="detail_label">课程规格</text>
<text class="detail_value">{{ orderDetail.product_specs }}</text>
</view>
<view class="detail_item">
<text class="detail_label">购买数量</text>
<text class="detail_value">{{ orderDetail.quantity }}</text>
</view>
<view class="detail_item" v-if="orderDetail.course_count">
<text class="detail_label">总课时</text>
<text class="detail_value">{{ orderDetail.course_count }}</text>
</view>
<view class="detail_item" v-if="orderDetail.used_count !== undefined">
<text class="detail_label">已使用</text>
<text class="detail_value">{{ orderDetail.used_count }}</text>
</view>
<view class="detail_item" v-if="orderDetail.remaining_count !== undefined">
<text class="detail_label">剩余课时</text>
<text class="detail_value">{{ orderDetail.remaining_count }}</text>
</view>
</view>
</view>
</view>
<!-- 备注信息 -->
<view class="remark_section" v-if="orderDetail.remark">
<view class="section_title">备注信息</view>
<view class="remark_text">{{ orderDetail.remark }}</view>
</view>
<!-- 退款信息 -->
<view class="refund_section" v-if="showRefundInfo">
<view class="section_title">退款信息</view>
<view class="refund_info">
<view class="info_item">
<text class="label">退款金额</text>
<text class="value amount">¥{{ orderDetail.refund_amount || orderDetail.total_amount }}</text>
</view>
<view class="info_item" v-if="orderDetail.refund_time">
<text class="label">退款时间</text>
<text class="value">{{ formatDateTime(orderDetail.refund_time) }}</text>
</view>
<view class="info_item" v-if="orderDetail.cancel_reason">
<text class="label">退款原因</text>
<text class="value">{{ orderDetail.cancel_reason }}</text>
</view>
</view>
</view>
</view>
<!-- 订单不存在 -->
<view v-else class="empty_section">
<view class="empty_icon">📋</view>
<view class="empty_text">订单不存在</view>
<view class="empty_hint">请检查订单信息或联系客服</view>
</view>
<!-- 操作按钮 -->
<view class="action_section" v-if="orderDetail">
<fui-button
v-if="orderDetail.status === 'pending_payment'"
background="#29d3b4"
@click="payOrder"
>
立即付款
</fui-button>
<fui-button
v-if="orderDetail.status === 'pending_payment'"
background="transparent"
color="#999"
@click="cancelOrder"
>
取消订单
</fui-button>
<fui-button
v-if="canViewContract"
background="#f39c12"
@click="viewContract"
>
查看合同
</fui-button>
<fui-button
v-if="orderDetail.status === 'refunding' || orderDetail.status === 'refunded'"
background="transparent"
color="#f39c12"
@click="viewRefundDetail"
>
查看退款
</fui-button>
</view>
</view>
</template>
<script>
import apiRoute from '@/api/apiRoute.js'
export default {
data() {
return {
orderId: 0,
studentId: 0,
orderDetail: null,
loading: false,
//
STATUS_TEXT_MAP: {
'pending_payment': '待付款',
'paid': '已付款',
'completed': '已完成',
'cancelled': '已取消',
'refunding': '退款中',
'refunded': '已退款'
},
//
STATUS_DESC_MAP: {
'pending_payment': '请在规定时间内完成支付',
'paid': '订单已支付成功',
'completed': '订单已完成,感谢您的使用',
'cancelled': '订单已取消',
'refunding': '退款处理中,请耐心等待',
'refunded': '退款已完成'
}
}
},
computed: {
showRefundInfo() {
return this.orderDetail && (this.orderDetail.status === 'refunding' || this.orderDetail.status === 'refunded')
},
canViewContract() {
//
return this.orderDetail && (this.orderDetail.status === 'paid' || this.orderDetail.status === 'completed')
}
},
onLoad(options) {
this.orderId = parseInt(options.id) || 0
this.studentId = parseInt(options.student_id) || 0
// ID
if (!this.studentId) {
const userInfo = uni.getStorageSync('userInfo')
if (userInfo && userInfo.id) {
this.studentId = userInfo.id
}
}
if (this.orderId && this.studentId) {
this.loadOrderDetail()
} else {
uni.showToast({
title: '参数错误',
icon: 'none'
})
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
},
methods: {
goBack() {
uni.navigateBack()
},
async loadOrderDetail() {
this.loading = true
try {
console.log('获取订单详情,参数:', {
id: this.orderId,
student_id: this.studentId
});
const response = await apiRoute.xy_getStudentOrderDetail({
id: this.orderId,
student_id: this.studentId
})
if (response.code === 1 && response.data) {
this.orderDetail = this.processOrderData(response.data)
} else {
//
console.warn('订单详情接口失败,尝试从列表接口获取:', response.msg)
await this.loadOrderFromList()
}
} catch (error) {
console.error('获取订单详情失败:', error)
//
await this.loadOrderFromList()
} finally {
this.loading = false
}
},
//
async loadOrderFromList() {
try {
const response = await apiRoute.xy_getStudentOrders({
student_id: this.studentId,
page: 1,
limit: 100
})
if (response.code === 1 && response.data && response.data.data) {
const orders = response.data.data
const targetOrder = orders.find(order => order.id == this.orderId)
if (targetOrder) {
this.orderDetail = this.processOrderData(targetOrder)
} else {
this.showError('订单不存在')
}
} else {
this.showError('获取订单信息失败')
}
} catch (error) {
console.error('从列表获取订单详情失败:', error)
this.showError('获取订单信息失败')
}
},
//
processOrderData(rawData) {
return {
id: rawData.id,
order_no: rawData.order_no || rawData.order_number || rawData.payment_id,
product_name: rawData.course_id_name || rawData.course_name || rawData.product_name || '课程订单',
product_specs: rawData.course_specs || rawData.product_specs || '',
quantity: rawData.quantity || 1,
total_amount: rawData.order_amount || rawData.total_amount || rawData.amount || '0.00',
status: this.mapOrderStatus(rawData.order_status || rawData.status),
create_time: rawData.created_at || rawData.create_time,
payment_method: rawData.payment_type ? this.mapPaymentMethod(rawData.payment_type) : '',
payment_time: rawData.payment_time || rawData.paid_at,
refund_time: rawData.refund_time,
refund_amount: rawData.refund_amount,
//
course_count: rawData.course_count || 0,
used_count: rawData.use_total_hours || rawData.used_count || 0,
remaining_count: rawData.remaining_count || 0,
cancel_reason: rawData.after_sales_reason || rawData.cancel_reason || '',
remark: rawData.remark || ''
}
},
//
mapOrderStatus(status) {
const statusMap = {
'pending': 'pending_payment',
'paid': 'paid',
'signed': 'completed',
'completed': 'completed',
'transfer': 'cancelled',
'cancelled': 'cancelled',
'refunded': 'refunded',
'refunding': 'refunding'
}
return statusMap[status] || 'pending_payment'
},
//
mapPaymentMethod(method) {
const methodMap = {
'cash': '现金支付',
'scan_code': '扫码支付',
'subscription': '预约支付',
'wxpay_online': '微信支付',
'wxpay': '微信支付',
'alipay': '支付宝',
'bank': '银行转账'
}
return methodMap[method] || method || ''
},
getStatusText(status) {
return this.STATUS_TEXT_MAP[status] || status
},
getStatusDesc(status) {
return this.STATUS_DESC_MAP[status] || ''
},
getStatusIcon(status) {
const iconMap = {
'pending_payment': '💰',
'paid': '✅',
'completed': '🎉',
'cancelled': '❌',
'refunding': '⏳',
'refunded': '↩️'
}
return iconMap[status] || '📋'
},
formatDateTime(dateString) {
if (!dateString) return ''
const date = new Date(dateString)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}`
},
showError(message) {
uni.showToast({
title: message,
icon: 'none'
})
},
//
payOrder() {
//
uni.navigateBack()
},
//
cancelOrder() {
uni.showModal({
title: '确认取消',
content: '确定要取消此订单吗?',
success: (res) => {
if (res.confirm) {
this.performCancelOrder()
}
}
})
},
async performCancelOrder() {
try {
uni.showLoading({ title: '处理中...' })
// API
//
await new Promise(resolve => setTimeout(resolve, 1000))
uni.hideLoading()
uni.showToast({
title: '取消成功',
icon: 'success'
})
//
this.loadOrderDetail()
} catch (error) {
uni.hideLoading()
console.error('取消订单失败:', error)
uni.showToast({
title: '取消失败',
icon: 'none'
})
}
},
//
async viewContract() {
try {
uni.showLoading({ title: '获取合同信息...' })
//
const contractResponse = await this.getContractInfo()
uni.hideLoading()
if (contractResponse && contractResponse.contract_id) {
//
uni.navigateTo({
url: `/pages-student/contract/detail?contract_id=${contractResponse.contract_id}&student_id=${this.studentId}`
})
} else {
uni.showModal({
title: '提示',
content: '该订单暂无关联合同,请联系客服处理',
showCancel: false
})
}
} catch (error) {
uni.hideLoading()
console.error('获取合同信息失败:', error)
uni.showToast({
title: '获取合同信息失败',
icon: 'none'
})
}
},
//
async getContractInfo() {
try {
//
const response = await apiRoute.getStudentContracts({
student_id: this.studentId,
order_id: this.orderId //
})
if (response.code === 1 && response.data && response.data.length > 0) {
//
return response.data[0]
}
return null
} catch (error) {
console.error('获取合同信息失败:', error)
return null
}
},
// 退
viewRefundDetail() {
const refundInfo = `订单号:${this.orderDetail.order_no}\n退款金额:¥${this.orderDetail.refund_amount || this.orderDetail.total_amount}\n退款时间:${this.orderDetail.refund_time ? this.formatDateTime(this.orderDetail.refund_time) : '处理中'}`
uni.showModal({
title: '退款详情',
content: refundInfo,
showCancel: false
})
}
}
}
</script>
<style lang="less" scoped>
.main_box {
background: #f8f9fa;
min-height: 100vh;
}
//
.navbar_section {
display: flex;
justify-content: space-between;
align-items: center;
background: #29D3B4;
padding: 40rpx 32rpx 20rpx;
//
// #ifdef MP-WEIXIN
padding-top: 80rpx;
// #endif
.navbar_back {
width: 60rpx;
.back_icon {
color: #fff;
font-size: 40rpx;
font-weight: 600;
}
}
.navbar_title {
color: #fff;
font-size: 32rpx;
font-weight: 600;
}
.navbar_action {
width: 60rpx;
}
}
//
.loading_section {
background: #fff;
margin: 20rpx;
border-radius: 16rpx;
padding: 80rpx 32rpx;
text-align: center;
.loading_text {
font-size: 28rpx;
color: #666;
}
}
//
.detail_content {
padding: 0 20rpx 120rpx;
}
//
.status_section {
background: #fff;
border-radius: 16rpx;
padding: 40rpx 32rpx;
margin: 20rpx 0;
display: flex;
align-items: center;
.status_icon {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 24rpx;
.icon_text {
font-size: 32rpx;
}
&.pending_payment {
background: rgba(243, 156, 18, 0.1);
}
&.paid {
background: rgba(52, 152, 219, 0.1);
}
&.completed {
background: rgba(39, 174, 96, 0.1);
}
&.cancelled {
background: rgba(149, 165, 166, 0.1);
}
&.refunded, &.refunding {
background: rgba(231, 76, 60, 0.1);
}
}
.status_info {
flex: 1;
.status_text {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 8rpx;
}
.status_desc {
font-size: 24rpx;
color: #666;
}
}
}
//
.order_info_section, .course_info_section, .remark_section, .refund_section {
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
margin: 20rpx 0;
.section_title {
font-size: 30rpx;
font-weight: 600;
color: #333;
margin-bottom: 24rpx;
}
}
//
.info_list {
.info_item {
display: flex;
align-items: center;
margin-bottom: 20rpx;
&:last-child {
margin-bottom: 0;
}
.label {
font-size: 26rpx;
color: #666;
min-width: 160rpx;
}
.value {
font-size: 26rpx;
color: #333;
flex: 1;
&.amount {
color: #e74c3c;
font-weight: 600;
}
}
}
}
//
.course_card {
.course_header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 20rpx;
.course_name {
flex: 1;
font-size: 28rpx;
font-weight: 600;
color: #333;
margin-right: 20rpx;
}
.course_amount {
font-size: 32rpx;
font-weight: 600;
color: #e74c3c;
}
}
.course_details {
.detail_item {
display: flex;
align-items: center;
margin-bottom: 12rpx;
&:last-child {
margin-bottom: 0;
}
.detail_label {
font-size: 24rpx;
color: #666;
min-width: 140rpx;
}
.detail_value {
font-size: 24rpx;
color: #333;
}
}
}
}
//
.remark_text {
font-size: 26rpx;
color: #666;
line-height: 1.6;
}
// 退
.refund_info {
.info_item {
display: flex;
align-items: center;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
.label {
font-size: 26rpx;
color: #666;
min-width: 160rpx;
}
.value {
font-size: 26rpx;
color: #333;
flex: 1;
&.amount {
color: #e74c3c;
font-weight: 600;
}
}
}
}
//
.empty_section {
background: #fff;
border-radius: 16rpx;
padding: 80rpx 32rpx;
text-align: center;
margin: 20rpx;
.empty_icon {
font-size: 80rpx;
margin-bottom: 24rpx;
}
.empty_text {
font-size: 28rpx;
color: #666;
margin-bottom: 12rpx;
}
.empty_hint {
font-size: 24rpx;
color: #999;
}
}
//
.action_section {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff;
padding: 20rpx 32rpx;
box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.1);
display: flex;
gap: 16rpx;
fui-button {
flex: 1;
}
}
</style>

348
uniapp/pages-student/orders/index.vue

@ -81,7 +81,7 @@
v-if="order.status === 'pending_payment'" v-if="order.status === 'pending_payment'"
background="#29d3b4" background="#29d3b4"
size="small" size="small"
@click.stop="payOrder(order)" @click="(e) => payOrder(order, e)"
> >
立即付款 立即付款
</fui-button> </fui-button>
@ -91,27 +91,36 @@
background="transparent" background="transparent"
color="#999" color="#999"
size="small" size="small"
@click.stop="cancelOrder(order)" @click="(e) => cancelOrder(order, e)"
> >
取消订单 取消订单
</fui-button> </fui-button>
<fui-button <fui-button
v-if="order.status === 'completed'"
background="transparent" background="transparent"
color="#666" color="#666"
size="small" size="small"
@click.stop="viewOrderDetail(order)" @click="(e) => viewOrderDetail(order, e)"
> >
查看详情 查看详情
</fui-button> </fui-button>
<fui-button
v-if="order.status === 'paid' || order.status === 'completed'"
background="#29d3b4"
color="#fff"
size="small"
@click="(e) => viewContract(order, e)"
>
查看合同
</fui-button>
<fui-button <fui-button
v-if="order.status === 'refunding' || order.status === 'refunded'" v-if="order.status === 'refunding' || order.status === 'refunded'"
background="transparent" background="transparent"
color="#f39c12" color="#f39c12"
size="small" size="small"
@click.stop="viewRefundDetail(order)" @click="(e) => viewRefundDetail(order, e)"
> >
查看退款 查看退款
</fui-button> </fui-button>
@ -245,6 +254,7 @@
STATUS_TEXT_MAP: { STATUS_TEXT_MAP: {
'pending_payment': '待付款', 'pending_payment': '待付款',
'paid': '已付款', 'paid': '已付款',
'signed': '已签署',
'completed': '已完成', 'completed': '已完成',
'cancelled': '已取消', 'cancelled': '已取消',
'refunding': '退款中', 'refunding': '退款中',
@ -296,11 +306,20 @@
// //
const userInfo = uni.getStorageSync('userInfo') const userInfo = uni.getStorageSync('userInfo')
if (userInfo && userInfo.id) { if (userInfo && userInfo.id) {
// URLstudent_id使ID
if (!this.studentId) {
this.studentId = userInfo.id this.studentId = userInfo.id
}
this.studentInfo = { this.studentInfo = {
id: userInfo.id, id: this.studentId, // 使ID
name: userInfo.name || userInfo.nickname || '学员' name: userInfo.name || userInfo.nickname || '学员'
} }
console.log('学员信息设置完成:', {
studentId: this.studentId,
userName: this.studentInfo.name
})
} else { } else {
// //
uni.showToast({ uni.showToast({
@ -369,25 +388,44 @@
// //
processOrderData(rawData) { processOrderData(rawData) {
return rawData.map(item => { return rawData.map(item => {
//
const orderAmount = item.order_amount || item.total_amount || item.amount || '0.00'
//
const orderNo = item.order_no || item.order_number || item.payment_id || `订单${item.id}`
//
const productName = item.course_id_name || item.course_name || item.product_name || '课程订单'
//
const orderStatus = this.mapOrderStatus(item.order_status || item.status)
return { return {
id: item.id, id: item.id,
order_no: item.order_no || item.order_number, order_no: orderNo,
product_name: item.course_name || item.product_name || '课程订单', product_name: productName,
product_specs: item.course_specs || item.product_specs || '', product_specs: item.course_specs || item.product_specs || '',
quantity: item.quantity || 1, quantity: item.quantity || 1,
total_amount: item.total_amount || item.amount || '0.00', total_amount: orderAmount,
status: this.mapOrderStatus(item.status), status: orderStatus,
create_time: item.create_time || item.created_at, create_time: item.created_at || item.create_time,
payment_method: this.mapPaymentMethod(item.payment_method), payment_method: this.mapPaymentMethod(item.payment_type || item.payment_method),
payment_time: item.payment_time || item.paid_at, payment_time: item.payment_time || item.paid_at,
refund_time: item.refund_time, refund_time: item.refund_time,
refund_amount: item.refund_amount, refund_amount: item.refund_amount,
// //
course_count: item.course_count || 0, course_count: item.total_hours || item.course_count || 0,
used_count: item.used_count || 0, used_count: item.use_total_hours || item.used_count || 0,
remaining_count: item.remaining_count || 0, remaining_count: item.remaining_count || 0,
cancel_reason: item.cancel_reason || '', gift_hours: item.gift_hours || 0,
remark: item.remark || '' use_gift_hours: item.use_gift_hours || 0,
//
cancel_reason: item.after_sales_reason || item.cancel_reason || '',
remark: item.remark || '',
//
staff_name: item.staff_id_name || '',
class_name: item.class_id_name || '',
campus_id: item.campus_id || 0
} }
}) })
}, },
@ -395,14 +433,20 @@
// //
mapOrderStatus(status) { mapOrderStatus(status) {
const statusMap = { const statusMap = {
//
'0': 'pending_payment', // '0': 'pending_payment', //
'1': 'completed', // '1': 'completed', //
'2': 'cancelled', // '2': 'cancelled', //
'3': 'refunded', // 退 '3': 'refunded', // 退
'pending': 'pending_payment', //
'paid': 'completed', 'pending': 'pending_payment', //
'cancelled': 'cancelled', 'paid': 'paid', //
'refunded': 'refunded' 'signed': 'completed', // /
'completed': 'completed', //
'transfer': 'cancelled', // /
'cancelled': 'cancelled', //
'refunded': 'refunded', // 退
'refunding': 'refunding' // 退
} }
return statusMap[status] || 'pending_payment' return statusMap[status] || 'pending_payment'
}, },
@ -419,6 +463,15 @@
return methodMap[method] || method || '' return methodMap[method] || method || ''
}, },
//
mapPaymentMethodText(method) {
const methodMap = {
'wxpay': '微信支付',
'alipay': '支付宝'
}
return methodMap[method] || method || ''
},
async loadMoreOrders() { async loadMoreOrders() {
if (this.loadingMore || !this.hasMore) return if (this.loadingMore || !this.hasMore) return
@ -535,46 +588,32 @@
} }
}, },
async viewOrderDetail(order) { async viewOrderDetail(order, event) {
//
if (event && event.stopPropagation) {
event.stopPropagation()
}
try { try {
uni.showLoading({ title: '加载中...' }) // IDID
//
console.log('调用学员端订单详情接口,参数:', { id: order.id });
const res = await apiRoute.xy_getStudentOrderDetail({
id: order.id
})
uni.hideLoading()
if (res.code === 1) {
//
uni.navigateTo({ uni.navigateTo({
url: `/pages-student/orders/detail?id=${order.id}` url: `/pages-student/orders/detail?id=${order.id}&student_id=${this.studentId}`
}) })
} else {
//
uni.showModal({
title: '订单详情',
content: `订单号:${order.order_no}\n商品:${order.product_name}\n金额:¥${order.total_amount}\n状态:${this.getStatusText(order.status)}`,
showCancel: false
})
}
} catch (error) { } catch (error) {
uni.hideLoading() console.error('跳转订单详情失败:', error)
console.error('获取订单详情失败:', error) uni.showToast({
title: '跳转失败',
// icon: 'none'
uni.showModal({
title: '订单详情',
content: `订单号:${order.order_no}\n商品:${order.product_name}\n金额:¥${order.total_amount}\n状态:${this.getStatusText(order.status)}`,
showCancel: false
}) })
} }
}, },
payOrder(order) { payOrder(order, event) {
//
if (event && event.stopPropagation) {
event.stopPropagation()
}
this.selectedOrder = order this.selectedOrder = order
this.showPaymentPopup = true this.showPaymentPopup = true
}, },
@ -592,27 +631,175 @@
async confirmPayment() { async confirmPayment() {
if (!this.selectedOrder || this.paying) return if (!this.selectedOrder || this.paying) return
await this.executeOrderOperation( this.paying = true
{
loadingKey: 'paying', try {
additionalUpdates: { // API
const paymentData = {
order_id: this.selectedOrder.id,
student_id: this.studentId,
payment_method: this.selectedPaymentMethod, payment_method: this.selectedPaymentMethod,
payment_amount: this.selectedOrder.total_amount
}
console.log('发起支付请求:', paymentData)
//
let paymentResponse
if (this.selectedPaymentMethod === 'wxpay') {
//
paymentResponse = await this.processWechatPayment(paymentData)
} else if (this.selectedPaymentMethod === 'alipay') {
//
paymentResponse = await this.processAlipayPayment(paymentData)
} else {
throw new Error('不支持的支付方式')
}
if (paymentResponse.code === 1) {
//
this.updateOrderStatus(this.selectedOrder.id, {
status: 'paid',
payment_method: this.mapPaymentMethodText(this.selectedPaymentMethod),
payment_time: this.getCurrentTimestamp() payment_time: this.getCurrentTimestamp()
})
uni.showToast({
title: '支付成功',
icon: 'success'
})
this.closePaymentPopup()
} else {
throw new Error(paymentResponse.msg || '支付失败')
}
} catch (error) {
console.error('支付失败:', error)
uni.showToast({
title: error.message || '支付失败',
icon: 'none'
})
} finally {
this.paying = false
} }
}, },
{
order: this.selectedOrder, //
statusKey: 'status', async processWechatPayment(paymentData) {
statusValue: 'paid', try {
delay: 2000, //
successMessage: '支付成功', const response = await apiRoute.xs_orderTableUpdatePaymentStatus({
errorMessage: '支付失败', order_id: paymentData.order_id,
callback: () => this.closePaymentPopup() payment_type: 'wxpay_online',
payment_amount: paymentData.payment_amount
})
if (response.code === 1) {
//
if (response.data && response.data.pay_params) {
//
const wxPayResult = await this.callWechatPay(response.data.pay_params)
return wxPayResult
} else {
//
return { code: 1, msg: '支付成功' }
}
} else {
throw new Error(response.msg || '获取支付信息失败')
}
} catch (error) {
console.error('微信支付处理失败:', error)
throw error
} }
)
}, },
async cancelOrder(order) { //
async processAlipayPayment(paymentData) {
try {
//
const response = await apiRoute.xs_orderTableUpdatePaymentStatus({
order_id: paymentData.order_id,
payment_type: 'alipay',
payment_amount: paymentData.payment_amount
})
if (response.code === 1) {
//
if (response.data && response.data.pay_params) {
//
const aliPayResult = await this.callAlipay(response.data.pay_params)
return aliPayResult
} else {
//
return { code: 1, msg: '支付成功' }
}
} else {
throw new Error(response.msg || '获取支付信息失败')
}
} catch (error) {
console.error('支付宝支付处理失败:', error)
throw error
}
},
//
async callWechatPay(payParams) {
return new Promise((resolve, reject) => {
// #ifdef MP-WEIXIN
wx.requestPayment({
...payParams,
success: (res) => {
console.log('微信支付成功:', res)
resolve({ code: 1, msg: '支付成功' })
},
fail: (err) => {
console.error('微信支付失败:', err)
if (err.errMsg.includes('cancel')) {
reject(new Error('用户取消支付'))
} else {
reject(new Error('微信支付失败'))
}
}
})
// #endif
// #ifndef MP-WEIXIN
//
console.log('非微信小程序环境,模拟支付成功')
resolve({ code: 1, msg: '支付成功' })
// #endif
})
},
//
async callAlipay(payParams) {
return new Promise((resolve, reject) => {
// #ifdef APP-PLUS
// App
plus.payment.request('alipay', payParams, (result) => {
console.log('支付宝支付成功:', result)
resolve({ code: 1, msg: '支付成功' })
}, (error) => {
console.error('支付宝支付失败:', error)
reject(new Error('支付宝支付失败'))
})
// #endif
// #ifndef APP-PLUS
//
console.log('非App环境,模拟支付成功')
resolve({ code: 1, msg: '支付成功' })
// #endif
})
},
async cancelOrder(order, event) {
//
if (event && event.stopPropagation) {
event.stopPropagation()
}
uni.showModal({ uni.showModal({
title: '确认取消', title: '确认取消',
content: '确定要取消此订单吗?', content: '确定要取消此订单吗?',
@ -634,7 +821,34 @@
}) })
}, },
viewRefundDetail(order) { //
async viewContract(order, event) {
//
if (event && event.stopPropagation) {
event.stopPropagation()
}
try {
//
// URL
uni.navigateTo({
url: `/pages-student/contracts/index?student_id=${this.studentId}&order_id=${order.id}`
})
} catch (error) {
console.error('跳转合同页面失败:', error)
uni.showToast({
title: '跳转合同页面失败',
icon: 'none'
})
}
},
viewRefundDetail(order, event) {
//
if (event && event.stopPropagation) {
event.stopPropagation()
}
const refundInfo = `订单号:${order.order_no}\n退款金额:¥${order.refund_amount || order.total_amount}\n退款时间:${order.refund_time || '处理中'}` const refundInfo = `订单号:${order.order_no}\n退款金额:¥${order.refund_amount || order.total_amount}\n退款时间:${order.refund_time || '处理中'}`
uni.showModal({ uni.showModal({
title: '退款详情', title: '退款详情',

9
uniapp/pages.json

@ -110,6 +110,15 @@
"navigationBarTextStyle": "white" "navigationBarTextStyle": "white"
} }
}, },
{
"path": "orders/detail",
"style": {
"navigationBarTitleText": "订单详情",
"navigationStyle": "custom",
"navigationBarBackgroundColor": "#29d3b4",
"navigationBarTextStyle": "white"
}
},
{ {
"path": "contracts/index", "path": "contracts/index",
"style": { "style": {

173
uniapp/pages/common/dashboard/webview.vue

@ -27,7 +27,9 @@
</template> </template>
<script> <script>
export default { import { img_domian } from '@/common/config.js'
export default {
data() { data() {
return { return {
pageType: '', pageType: '',
@ -35,58 +37,57 @@
isLoading: true, isLoading: true,
hasError: false, hasError: false,
errorMessage: '', errorMessage: '',
baseUrl: 'http://localhost:20080', // URL baseUrl: img_domian, // URL
userToken: '', userToken: '',
userInfo: {} userInfo: {},
} }
}, },
computed: { computed: {
webViewUrl() { webViewUrl() {
if (!this.userToken) { if (!this.userToken) {
return ''; return ''
} }
// WebView URLtoken // WebView URLtoken
const params = [ const params = [
`token=${encodeURIComponent(this.userToken)}`, `token=${encodeURIComponent(this.userToken)}`,
`type=${encodeURIComponent(this.pageType)}`, `type=${encodeURIComponent(this.pageType)}`,
`platform=uniapp` `platform=uniapp`,
]; ]
return `${this.baseUrl}/api/dashboard/webview?${params.join('&')}`; return `${this.baseUrl}/api/dashboard/webview?${params.join('&')}`
} },
}, },
onLoad(options) { onLoad(options) {
// URL // URL
this.pageType = options.type || 'my_data'; this.pageType = options.type || 'my_data'
// //
const titleMap = { const titleMap = {
'my_data': '我的数据', 'my_data': '我的数据',
'dept_data': '部门数据', 'dept_data': '部门数据',
'campus_data': '校区数据' 'campus_data': '校区数据',
}; }
this.pageTitle = titleMap[this.pageType] || '数据统计'; this.pageTitle = titleMap[this.pageType] || '数据统计'
// //
uni.setNavigationBarTitle({ uni.setNavigationBarTitle({
title: this.pageTitle title: this.pageTitle,
}); })
// WebView // WebView
this.initWebView(); this.initWebView()
}, },
methods: { methods: {
async initWebView() { async initWebView() {
try { try {
// token // token
await this.getUserInfo(); await this.getUserInfo()
// URL // URL
setTimeout(() => { setTimeout(() => {
this.isLoading = false; this.isLoading = false
}, 500); }, 500)
} catch (error) { } catch (error) {
console.error('初始化WebView失败:', error); console.error('初始化WebView失败:', error)
this.showError('初始化失败'); this.showError('初始化失败')
} }
}, },
@ -94,57 +95,57 @@
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
try { try {
// //
const userInfo = uni.getStorageSync('userInfo'); const userInfo = uni.getStorageSync('userInfo')
const token = uni.getStorageSync('token'); const token = uni.getStorageSync('token')
if (!token) { if (!token) {
// token使token // token使token
this.userToken = 'test123'; this.userToken = 'test123'
this.userInfo = { name: '测试用户' }; this.userInfo = { name: '测试用户' }
} else { } else {
this.userInfo = userInfo || {}; this.userInfo = userInfo || {}
this.userToken = token; this.userToken = token
} }
resolve(); resolve()
} catch (error) { } catch (error) {
reject(error); reject(error)
} }
}); })
}, },
handleMessage(event) { handleMessage(event) {
console.log('收到WebView消息:', event.detail.data); console.log('收到WebView消息:', event.detail.data)
const data = event.detail.data[0]; const data = event.detail.data[0]
// WebView // WebView
switch (data.type) { switch (data.type) {
case 'navigate': case 'navigate':
// //
this.handleNavigate(data.url); this.handleNavigate(data.url)
break; break
case 'refresh': case 'refresh':
// //
this.refresh(); this.refresh()
break; break
case 'share': case 'share':
// //
this.handleShare(data.content); this.handleShare(data.content)
break; break
default: default:
console.log('未处理的消息类型:', data.type); console.log('未处理的消息类型:', data.type)
} }
}, },
handleError(event) { handleError(event) {
console.error('WebView加载错误:', event); console.error('WebView加载错误:', event)
this.showError('页面加载失败,请检查网络连接'); this.showError('页面加载失败,请检查网络连接')
}, },
handleLoad(event) { handleLoad(event) {
console.log('WebView加载完成:', event); console.log('WebView加载完成:', event)
this.isLoading = false; this.isLoading = false
this.hasError = false; this.hasError = false
}, },
handleNavigate(url) { handleNavigate(url) {
@ -154,13 +155,13 @@
uni.navigateTo({ uni.navigateTo({
url: url, url: url,
fail: (err) => { fail: (err) => {
console.error('页面跳转失败:', err); console.error('页面跳转失败:', err)
uni.showToast({ uni.showToast({
title: '页面跳转失败', title: '页面跳转失败',
icon: 'none' icon: 'none',
}); })
} },
}); })
} else { } else {
// WebView // WebView
uni.showModal({ uni.showModal({
@ -170,11 +171,11 @@
if (res.confirm) { if (res.confirm) {
uni.showToast({ uni.showToast({
title: '功能开发中', title: '功能开发中',
icon: 'none' icon: 'none',
}); })
}
} }
}); },
})
} }
}, },
@ -182,64 +183,64 @@
// //
uni.showToast({ uni.showToast({
title: '分享功能开发中', title: '分享功能开发中',
icon: 'none' icon: 'none',
}); })
}, },
refresh() { refresh() {
// //
this.isLoading = true; this.isLoading = true
this.hasError = false; this.hasError = false
// //
setTimeout(() => { setTimeout(() => {
this.initWebView(); this.initWebView()
}, 300); }, 300)
}, },
retry() { retry() {
// //
this.hasError = false; this.hasError = false
this.isLoading = true; this.isLoading = true
this.initWebView(); this.initWebView()
}, },
showError(message) { showError(message) {
this.isLoading = false; this.isLoading = false
this.hasError = true; this.hasError = true
this.errorMessage = message; this.errorMessage = message
} },
} },
} }
</script> </script>
<style scoped> <style scoped>
.dashboard-webview { .dashboard-webview {
height: 100vh; height: 100vh;
background-color: #181A20; background-color: #181A20;
} }
.loading-container { .loading-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 100vh; height: 100vh;
background-color: #181A20; background-color: #181A20;
} }
.loading-text { .loading-text {
margin-top: 16px; margin-top: 16px;
font-size: 14px; font-size: 14px;
color: #29d3b4; color: #29d3b4;
} }
.webview-container { .webview-container {
height: 100vh; height: 100vh;
width: 100%; width: 100%;
} }
.error-container { .error-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@ -247,24 +248,24 @@
height: 100vh; height: 100vh;
background-color: #181A20; background-color: #181A20;
padding: 40px; padding: 40px;
} }
.error-title { .error-title {
margin-top: 16px; margin-top: 16px;
font-size: 18px; font-size: 18px;
font-weight: bold; font-weight: bold;
color: #fff; color: #fff;
} }
.error-desc { .error-desc {
margin-top: 8px; margin-top: 8px;
font-size: 14px; font-size: 14px;
color: rgba(255, 255, 255, 0.6); color: rgba(255, 255, 255, 0.6);
text-align: center; text-align: center;
line-height: 1.5; line-height: 1.5;
} }
.retry-btn { .retry-btn {
margin-top: 24px; margin-top: 24px;
padding: 12px 24px; padding: 12px 24px;
background-color: #29d3b4; background-color: #29d3b4;
@ -272,9 +273,9 @@
border-radius: 6px; border-radius: 6px;
border: none; border: none;
font-size: 14px; font-size: 14px;
} }
.retry-btn:active { .retry-btn:active {
background-color: #1a9b7c; background-color: #1a9b7c;
} }
</style> </style>

97
uniapp/pages/market/clue/clue_info.vue

@ -347,6 +347,8 @@ export default {
// //
showQRCodeModal: false, showQRCodeModal: false,
qrCodePaymentData: null, qrCodePaymentData: null,
//
paymentStatusTimer: null,
// //
remark_content: '', remark_content: '',
@ -423,6 +425,11 @@ export default {
onShow() { onShow() {
this.init() this.init()
}, },
onUnload() {
//
this.stopPaymentStatusPolling()
},
methods: { methods: {
async init() { async init() {
@ -1273,6 +1280,9 @@ export default {
this.qrCodePaymentData = paymentData this.qrCodePaymentData = paymentData
this.showQRCodeModal = true this.showQRCodeModal = true
this.$refs.qrCodePopup.open() this.$refs.qrCodePopup.open()
// - 15
this.startPaymentStatusPolling()
}, },
// //
@ -1280,6 +1290,9 @@ export default {
this.showQRCodeModal = false this.showQRCodeModal = false
this.qrCodePaymentData = null this.qrCodePaymentData = null
this.$refs.qrCodePopup.close() this.$refs.qrCodePopup.close()
//
this.stopPaymentStatusPolling()
}, },
// //
@ -1438,6 +1451,90 @@ ${orderInfo.paid_at ? '支付时间:' + this.formatOrderTime(orderInfo.paid_at
} catch (e) { } catch (e) {
return timeStr return timeStr
} }
},
//
startPaymentStatusPolling() {
//
this.stopPaymentStatusPolling()
//
this.checkPaymentStatus()
// 15
this.paymentStatusTimer = setInterval(() => {
this.checkPaymentStatus()
}, 15000)
console.log('支付状态轮询定时器已启动')
},
//
stopPaymentStatusPolling() {
if (this.paymentStatusTimer) {
clearInterval(this.paymentStatusTimer)
this.paymentStatusTimer = null
console.log('支付状态轮询定时器已停止')
}
},
//
async checkPaymentStatus() {
if (!this.qrCodePaymentData?.order) {
console.warn('无订单信息,停止轮询')
this.stopPaymentStatusPolling()
return
}
const order = this.qrCodePaymentData.order
try {
console.log('轮询检查支付状态,订单号:', order.order_no)
//
const res = await apiRoute.checkOrderPaymentStatus({
order_no: order.order_no
})
if (res.code === 1 && res.data) {
const orderStatus = res.data.order_status
if (orderStatus === 'paid') {
//
console.log('检测到支付成功')
this.handlePaymentSuccess(order)
} else {
console.log('订单状态:', orderStatus, '继续轮询...')
}
} else {
console.warn('查询支付状态失败:', res.msg)
}
} catch (error) {
console.error('查询支付状态异常:', error)
}
},
//
handlePaymentSuccess(order) {
//
this.stopPaymentStatusPolling()
//
uni.showModal({
title: '支付成功',
content: `订单 ${order.order_no} 已成功支付`,
showCancel: false,
confirmText: '确定',
success: (res) => {
if (res.confirm) {
//
this.closeQRCodeModal()
//
this.getOrderList()
}
}
})
} }
}, },
} }

Loading…
Cancel
Save