From eac8be7a58cc65a96e812096e19aa21308ee764d Mon Sep 17 00:00:00 2001 From: zeyan <258785420@qq.com> Date: Mon, 4 Aug 2025 23:10:00 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/apiController/OrderTable.php | 28 + .../student/CourseBookingController.php | 28 +- niucloud/app/api/controller/sys/Index.php | 10 + niucloud/app/api/route/pay.php | 1 - niucloud/app/api/route/route.php | 17 +- niucloud/app/api/route/student.php | 14 +- niucloud/app/common.php | 2 +- niucloud/app/dict/sys/FileDict.php | 1 + .../api/apiService/OrderTableService.php | 6 +- niucloud/app/service/api/pay/PayService.php | 5 +- uniapp/App.vue | 97 ++- uniapp/api/apiRoute.js | 5 + uniapp/common/config.js | 2 +- uniapp/components/course-info-card/index.vue | 49 +- .../fitness-record-popup.vue | 46 +- .../clue/class_arrangement_detail.vue | 121 ++- uniapp/pages-market/clue/clue_info.vue | 2 +- uniapp/pages-market/clue/index.vue | 2 +- uniapp/pages-student/knowledge/index.vue | 1 + uniapp/pages-student/orders/detail.vue | 783 ++++++++++++++++++ uniapp/pages-student/orders/index.vue | 360 ++++++-- uniapp/pages.json | 9 + uniapp/pages/common/dashboard/webview.vue | 545 ++++++------ uniapp/pages/market/clue/clue_info.vue | 97 +++ 24 files changed, 1775 insertions(+), 456 deletions(-) create mode 100644 uniapp/pages-student/orders/detail.vue diff --git a/niucloud/app/api/controller/apiController/OrderTable.php b/niucloud/app/api/controller/apiController/OrderTable.php index 472c895f..00bf3a3a 100644 --- a/niucloud/app/api/controller/apiController/OrderTable.php +++ b/niucloud/app/api/controller/apiController/OrderTable.php @@ -181,4 +181,32 @@ class OrderTable extends BaseApiService 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'] + ]); + } } diff --git a/niucloud/app/api/controller/student/CourseBookingController.php b/niucloud/app/api/controller/student/CourseBookingController.php index 9b72323d..5d47948d 100644 --- a/niucloud/app/api/controller/student/CourseBookingController.php +++ b/niucloud/app/api/controller/student/CourseBookingController.php @@ -38,14 +38,32 @@ class CourseBookingController extends BaseController 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', - 'date' => 'date', - 'start_date' => 'date', - 'end_date' => 'date', 'page' => 'integer|egt:1', '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 { $service = new CourseBookingService(); diff --git a/niucloud/app/api/controller/sys/Index.php b/niucloud/app/api/controller/sys/Index.php index 591a9c94..d526ebd6 100644 --- a/niucloud/app/api/controller/sys/Index.php +++ b/niucloud/app/api/controller/sys/Index.php @@ -12,6 +12,8 @@ namespace app\api\controller\sys; use app\listener\personnel\CalculatePerformance; +use app\model\order_table\OrderTable; +use app\service\api\apiService\OrderTableService; use core\base\BaseController; use think\facade\App; @@ -111,4 +113,12 @@ class Index extends BaseController ]); 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); + } } diff --git a/niucloud/app/api/route/pay.php b/niucloud/app/api/route/pay.php index f420ab85..ac88efb5 100644 --- a/niucloud/app/api/route/pay.php +++ b/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') ->middleware(ApiChannel::class) - ->middleware(ApiCheckToken::class) ->middleware(ApiLog::class); /** * 路由 diff --git a/niucloud/app/api/route/route.php b/niucloud/app/api/route/route.php index eb1c39d8..62e8558f 100644 --- a/niucloud/app/api/route/route.php +++ b/niucloud/app/api/route/route.php @@ -41,6 +41,7 @@ Route::group(function () { // 发送验证码不需要token验证 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/updatePaymentStatus', 'apiController.OrderTable/updatePaymentStatus'); + //员工端-查询订单支付状态 + Route::get('checkOrderPaymentStatus', 'apiController.OrderTable/checkOrderPaymentStatus'); //员工端-更新学员课程人员配置 Route::post('updateStudentCoursePersonnel', 'apiController.Course/updateStudentCoursePersonnel'); @@ -570,20 +573,6 @@ Route::group(function () { })->middleware(ApiChannel::class) ->middleware(ApiPersonnelCheckToken::class, true) ->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'; diff --git a/niucloud/app/api/route/student.php b/niucloud/app/api/route/student.php index 0ecc8d79..b87c80d1 100644 --- a/niucloud/app/api/route/student.php +++ b/niucloud/app/api/route/student.php @@ -71,13 +71,13 @@ Route::group('course-schedule', function () { })->middleware(['ApiCheckToken']); // 订单管理 -Route::group('order', function () { - // 获取订单列表 - Route::get('list', 'app\api\controller\student\OrderController@getOrderList'); - // 获取订单详情 - Route::get('detail/:order_id', 'app\api\controller\student\OrderController@getOrderDetail'); - // 获取订单统计 - Route::get('stats', 'app\api\controller\student\OrderController@getOrderStats'); +Route::group('xy', function () { + //学生端-订单管理-列表(新接口,公开访问) + Route::get('student/orders', 'app\api\controller\student\OrderController@getOrderList'); + //学生端-订单管理-详情(新接口,公开访问) + Route::get('student/orders/detail', 'app\api\controller\student\OrderController@getOrderDetail'); + //学生端-订单管理-统计(新接口,公开访问) + Route::get('student/orders/stats', 'app\api\controller\student\OrderController@getOrderStats'); })->middleware(['ApiCheckToken']); // 支付管理 diff --git a/niucloud/app/common.php b/niucloud/app/common.php index c453928d..94a8c5bd 100644 --- a/niucloud/app/common.php +++ b/niucloud/app/common.php @@ -1104,7 +1104,7 @@ function return_pay_config($campus_id, $order_id) $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) { throw new \Exception('当前校区支付配置不存在'); diff --git a/niucloud/app/dict/sys/FileDict.php b/niucloud/app/dict/sys/FileDict.php index 9039fa50..23cc8572 100644 --- a/niucloud/app/dict/sys/FileDict.php +++ b/niucloud/app/dict/sys/FileDict.php @@ -73,6 +73,7 @@ class FileDict self::APPLET,//小程序包上传 self::EXCEL,//excel导入 self::PDF,//PDF文档 + self::DOCUMENT,//文档上传 ]; } diff --git a/niucloud/app/service/api/apiService/OrderTableService.php b/niucloud/app/service/api/apiService/OrderTableService.php index 656bc2f9..5ed3449d 100644 --- a/niucloud/app/service/api/apiService/OrderTableService.php +++ b/niucloud/app/service/api/apiService/OrderTableService.php @@ -403,7 +403,11 @@ class OrderTableService extends BaseApiService $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(); $insertData = [ 'main_id' => $order_id, // 订单ID diff --git a/niucloud/app/service/api/pay/PayService.php b/niucloud/app/service/api/pay/PayService.php index e9b6db80..618b24af 100644 --- a/niucloud/app/service/api/pay/PayService.php +++ b/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\student\Student; use app\model\sys\Poster; +use app\service\api\apiService\OrderTableService; use app\service\core\member\CoreMemberService; use app\service\core\pay\CorePayService; use core\base\BaseApiService; @@ -165,7 +166,7 @@ class PayService extends BaseApiService */ public function qrcodeNotify($data,$order_id) { - + Log::debug('qrcodeNotify',$data); $order = new OrderTable(); $order_info = $order->where(['id' => $order_id])->find(); if($order_info['order_status'] == 'pending' and !empty($order_info['ipv3'])){ @@ -187,7 +188,7 @@ class PayService extends BaseApiService 'pay_time' => time() ]); - event('Student', ['event_type' => 'add','data' => $order_info]); + (new OrderTableService())->handlePaymentSuccess($order_info->toArray()); } } diff --git a/uniapp/App.vue b/uniapp/App.vue index a9eb0f14..987df1eb 100644 --- a/uniapp/App.vue +++ b/uniapp/App.vue @@ -52,39 +52,49 @@ checkForUpdate() { // #ifdef MP-WEIXIN try { - const updateManager = uni.getUpdateManager(); - - // 监听向微信后台请求检查更新结果事件 - updateManager.onCheckForUpdate((res) => { - console.log('检查更新结果:', res); - if (res.hasUpdate) { - console.log('发现新版本,准备下载'); - uni.showToast({ - title: '发现新版本', - icon: 'none', - duration: 2000 + // 双重检查确保在微信小程序环境中 + if (typeof uni !== 'undefined' && typeof uni.getUpdateManager === 'function') { + const updateManager = uni.getUpdateManager(); + + // 确保updateManager对象存在且有所需方法 + if (updateManager && typeof updateManager.checkForUpdate === 'function') { + // 监听向微信后台请求检查更新结果事件 + updateManager.onCheckForUpdate((res) => { + console.log('检查更新结果:', res); + if (res.hasUpdate) { + console.log('发现新版本,准备下载'); + uni.showToast({ + title: '发现新版本', + icon: 'none', + duration: 2000 + }); + } }); + + // 监听小程序有版本更新事件 + updateManager.onUpdateReady(() => { + console.log('新版本下载完成,准备重启应用'); + this.showUpdateDialog(); + }); + + // 监听小程序更新失败事件 + updateManager.onUpdateFailed(() => { + console.log('新版本下载失败'); + uni.showToast({ + title: '更新失败,请检查网络', + icon: 'none', + duration: 2000 + }); + }); + + // 主动检查更新 + updateManager.checkForUpdate(); + } else { + console.log('updateManager 或 checkForUpdate 方法不可用'); } - }); - - // 监听小程序有版本更新事件 - updateManager.onUpdateReady(() => { - console.log('新版本下载完成,准备重启应用'); - this.showUpdateDialog(); - }); - - // 监听小程序更新失败事件 - updateManager.onUpdateFailed(() => { - console.log('新版本下载失败'); - uni.showToast({ - title: '更新失败,请检查网络', - icon: 'none', - duration: 2000 - }); - }); - - // 主动检查更新 - updateManager.checkForUpdate(); + } else { + console.log('uni.getUpdateManager 不可用,可能不在微信小程序环境中'); + } } catch (error) { console.error('更新管理器初始化失败:', error); } @@ -124,8 +134,29 @@ applyUpdate() { // #ifdef MP-WEIXIN try { - const updateManager = uni.getUpdateManager(); - updateManager.applyUpdate(); + // 双重检查确保在微信小程序环境中 + if (typeof uni !== 'undefined' && typeof uni.getUpdateManager === 'function') { + const updateManager = uni.getUpdateManager(); + + // 确保updateManager对象存在且有所需方法 + if (updateManager && typeof updateManager.applyUpdate === 'function') { + 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) { console.error('应用更新失败:', error); uni.showToast({ diff --git a/uniapp/api/apiRoute.js b/uniapp/api/apiRoute.js index 5f523498..acc2031a 100644 --- a/uniapp/api/apiRoute.js +++ b/uniapp/api/apiRoute.js @@ -806,6 +806,11 @@ export default { return await http.get('/getQrcode', data); }, + // 查询订单支付状态 + async checkOrderPaymentStatus(data = {}) { + return await http.get('/checkOrderPaymentStatus', data); + }, + //↓↓↓↓↓↓↓↓↓↓↓↓-----课程预约相关接口-----↓↓↓↓↓↓↓↓↓↓↓↓ // 获取可预约课程列表 async getAvailableCourses(data = {}) { diff --git a/uniapp/common/config.js b/uniapp/common/config.js index c98a2253..6dfd5008 100644 --- a/uniapp/common/config.js +++ b/uniapp/common/config.js @@ -1,5 +1,5 @@ // 环境变量配置 -const env = 'development' +const env = 'prod' // const env = 'prod' const isMockEnabled = false // 默认禁用Mock优先模式,仅作为回退 const isDebug = false // 默认启用调试模式 diff --git a/uniapp/components/course-info-card/index.vue b/uniapp/components/course-info-card/index.vue index 60da0bde..15a9f7c2 100644 --- a/uniapp/components/course-info-card/index.vue +++ b/uniapp/components/course-info-card/index.vue @@ -5,7 +5,7 @@ @@ -15,7 +15,7 @@ {{ getStatusText(course.status) }} - + ✏️ @@ -252,15 +252,57 @@ export default { methods: { // 查看课程详情 viewCourseDetail(course) { + if (!course) { + console.error('viewCourseDetail: course参数为空') + uni.showToast({ + title: '课程信息不存在', + icon: 'none' + }) + return + } 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) { console.log('编辑课程数据:', course) + // 首先检查course参数是否存在 + if (!course) { + console.error('editCourse: course参数为空') + uni.showToast({ + title: '课程信息不存在,请重新加载页面', + icon: 'none' + }) + return + } + // 检查必要的数据 if (!course.id && !course.student_course_id) { + console.error('editCourse: 缺少课程ID', { id: course.id, student_course_id: course.student_course_id }) uni.showToast({ title: '课程信息不完整,请重新加载', icon: 'none' @@ -269,6 +311,7 @@ export default { } if (!course.resource_id) { + console.error('editCourse: 缺少resource_id', { resource_id: course.resource_id }) uni.showToast({ title: '缺少学员信息,无法编辑', icon: 'none' @@ -769,6 +812,7 @@ export default { // 获取进度百分比 getProgressPercent(course) { + if (!course) return 0 if (!course.total_count || course.total_count === 0) return 0 const used = course.used_count || 0 return Math.round((used / course.total_count) * 100) @@ -776,6 +820,7 @@ export default { // 获取剩余课时 getRemainingCount(course) { + if (!course) return 0 const total = course.total_count || 0 const used = course.used_count || 0 return Math.max(0, total - used) diff --git a/uniapp/components/fitness-record-popup/fitness-record-popup.vue b/uniapp/components/fitness-record-popup/fitness-record-popup.vue index 492807c7..d76a9db0 100644 --- a/uniapp/components/fitness-record-popup/fitness-record-popup.vue +++ b/uniapp/components/fitness-record-popup/fitness-record-popup.vue @@ -193,7 +193,7 @@ export default { test_date: this.recordData.test_date, height: this.recordData.height, weight: this.recordData.weight, - // 将PDF文件转换为服务器需要的格式 + // 将PDF文件转换为服务器需要的格式 - 使用server_path或url physical_test_report: this.recordData.pdf_files .map(file => file.server_path || file.url) .filter(path => path) @@ -216,7 +216,7 @@ export default { this.close() } catch (error) { - console.error('保存体测记录失败:', error) + console.error('执行保存体测记录失败:', error) uni.showToast({ title: '保存失败,请重试', icon: 'none' @@ -331,7 +331,8 @@ export default { console.error('上传PDF文件失败:', error) uni.showToast({ title: '文件上传失败: ' + (error.msg || error.message || '网络异常'), - icon: 'none' + icon: 'none', + duration: 3000 }) } } else { @@ -375,45 +376,63 @@ export default { const { Api_url } = require('@/common/config.js') 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) => { uni.uploadFile({ - url: Api_url + '/memberUploadDocument', // 使用通用文档上传接口 + url: Api_url + '/memberUploadDocument', // 使用学生端文档上传接口 filePath: file.path, name: 'file', formData: { - type: 'pdf' // 指定PDF文档类型 + 'type': 'document' // 指定为文档类型 }, header: { 'token': token + // 不设置 Content-Type,让uni-app自动设置为 multipart/form-data }, success: (res) => { + console.log('PDF上传原始响应:', res) let response try { // 去除 BOM 字符并解析 JSON response = JSON.parse(res.data.replace(/\ufeff/g, '') || '{}') - console.log('PDF上传响应:', response) + console.log('PDF上传解析后响应:', response) } catch (e) { console.error('PDF上传响应解析失败:', e, 'raw response:', res.data) reject({ code: 0, - msg: '服务器响应格式错误', + msg: '服务器响应格式错误: ' + res.data, error: e }) return } 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({ code: 1, msg: response.msg || '上传成功', data: { - url: response.data.url, - name: response.data.name || file.name, + url: fileUrl, + name: response.data.name || response.data.original_name || file.name, ext: response.data.ext || 'pdf', size: file.size } }) } else if (response.code === 401) { + console.error('PDF上传认证失败:', response) uni.showToast({ title: response.msg || '登录已过期', icon: 'none' @@ -423,10 +442,15 @@ export default { }, 1500) reject(response) } else { - console.error('上传失败响应:', response) + console.error('PDF上传失败响应:', response) + // 提供更详细的错误信息 + let errorMsg = response.msg || 'PDF上传失败' + if (response.code === 0 && response.msg.includes('路由未定义')) { + errorMsg = '文件上传接口暂不可用,请联系技术支持' + } reject({ code: response.code || 0, - msg: response.msg || 'PDF上传失败' + msg: errorMsg }) } }, diff --git a/uniapp/pages-market/clue/class_arrangement_detail.vue b/uniapp/pages-market/clue/class_arrangement_detail.vue index 961bde78..2b6f01fd 100644 --- a/uniapp/pages-market/clue/class_arrangement_detail.vue +++ b/uniapp/pages-market/clue/class_arrangement_detail.vue @@ -200,27 +200,39 @@ - - - 请假原因 - - + + + + 请假申请 - - 取消 - 提交 + + 请假原因 + + + + + 取消 + 提交 + - + + + \ No newline at end of file diff --git a/uniapp/pages-student/orders/index.vue b/uniapp/pages-student/orders/index.vue index 7a426012..649ca280 100644 --- a/uniapp/pages-student/orders/index.vue +++ b/uniapp/pages-student/orders/index.vue @@ -81,7 +81,7 @@ v-if="order.status === 'pending_payment'" background="#29d3b4" size="small" - @click.stop="payOrder(order)" + @click="(e) => payOrder(order, e)" > 立即付款 @@ -91,27 +91,36 @@ background="transparent" color="#999" size="small" - @click.stop="cancelOrder(order)" + @click="(e) => cancelOrder(order, e)" > 取消订单 查看详情 + + 查看合同 + + 查看退款 @@ -244,7 +253,8 @@ // 状态文本映射 STATUS_TEXT_MAP: { 'pending_payment': '待付款', - 'paid': '已付款', + 'paid': '已付款', + 'signed': '已签署', 'completed': '已完成', 'cancelled': '已取消', 'refunding': '退款中', @@ -296,11 +306,20 @@ // 获取当前登录学员信息 const userInfo = uni.getStorageSync('userInfo') if (userInfo && userInfo.id) { - this.studentId = userInfo.id + // 如果URL没有传递student_id,才使用本地存储的用户ID + if (!this.studentId) { + this.studentId = userInfo.id + } + this.studentInfo = { - id: userInfo.id, + id: this.studentId, // 使用确定的学员ID name: userInfo.name || userInfo.nickname || '学员' } + + console.log('学员信息设置完成:', { + studentId: this.studentId, + userName: this.studentInfo.name + }) } else { // 如果没有用户信息,跳转到登录页 uni.showToast({ @@ -369,25 +388,44 @@ // 处理订单数据,将后端数据转换为前端需要的格式 processOrderData(rawData) { 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 { id: item.id, - order_no: item.order_no || item.order_number, - product_name: item.course_name || item.product_name || '课程订单', + order_no: orderNo, + product_name: productName, product_specs: item.course_specs || item.product_specs || '', quantity: item.quantity || 1, - total_amount: item.total_amount || item.amount || '0.00', - status: this.mapOrderStatus(item.status), - create_time: item.create_time || item.created_at, - payment_method: this.mapPaymentMethod(item.payment_method), + total_amount: orderAmount, + status: orderStatus, + create_time: item.created_at || item.create_time, + payment_method: this.mapPaymentMethod(item.payment_type || item.payment_method), payment_time: item.payment_time || item.paid_at, refund_time: item.refund_time, refund_amount: item.refund_amount, - // 添加其他可能需要的字段 - course_count: item.course_count || 0, - used_count: item.used_count || 0, + // 课程相关字段 + course_count: item.total_hours || item.course_count || 0, + used_count: item.use_total_hours || item.used_count || 0, remaining_count: item.remaining_count || 0, - cancel_reason: item.cancel_reason || '', - remark: item.remark || '' + gift_hours: item.gift_hours || 0, + 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) { const statusMap = { + // 数字状态映射 '0': 'pending_payment', // 待付款 '1': 'completed', // 已完成 '2': 'cancelled', // 已取消 '3': 'refunded', // 已退款 - 'pending': 'pending_payment', - 'paid': 'completed', - 'cancelled': 'cancelled', - 'refunded': 'refunded' + // 字符串状态映射 + 'pending': 'pending_payment', // 待付款 + 'paid': 'paid', // 已付款 + 'signed': 'completed', // 已签署/已完成 + 'completed': 'completed', // 已完成 + 'transfer': 'cancelled', // 转让/已取消 + 'cancelled': 'cancelled', // 已取消 + 'refunded': 'refunded', // 已退款 + 'refunding': 'refunding' // 退款中 } return statusMap[status] || 'pending_payment' }, @@ -418,6 +462,15 @@ } return methodMap[method] || method || '' }, + + // 支付方式代码转文本 + mapPaymentMethodText(method) { + const methodMap = { + 'wxpay': '微信支付', + 'alipay': '支付宝' + } + return methodMap[method] || method || '' + }, async loadMoreOrders() { if (this.loadingMore || !this.hasMore) return @@ -535,46 +588,32 @@ } }, - async viewOrderDetail(order) { + async viewOrderDetail(order, event) { + // 阻止事件冒泡 + if (event && event.stopPropagation) { + event.stopPropagation() + } try { - uni.showLoading({ title: '加载中...' }) - - // 调用学员端订单详情接口 - console.log('调用学员端订单详情接口,参数:', { id: order.id }); - const res = await apiRoute.xy_getStudentOrderDetail({ - id: order.id + // 直接跳转到订单详情页面,传递学员ID和订单ID + uni.navigateTo({ + url: `/pages-student/orders/detail?id=${order.id}&student_id=${this.studentId}` }) - - uni.hideLoading() - - if (res.code === 1) { - // 跳转到订单详情页面 - uni.navigateTo({ - url: `/pages-student/orders/detail?id=${order.id}` - }) - } 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) { - uni.hideLoading() - console.error('获取订单详情失败:', error) - - // 如果接口调用失败,显示简单的详情弹窗 - uni.showModal({ - title: '订单详情', - content: `订单号:${order.order_no}\n商品:${order.product_name}\n金额:¥${order.total_amount}\n状态:${this.getStatusText(order.status)}`, - showCancel: false + console.error('跳转订单详情失败:', error) + uni.showToast({ + title: '跳转失败', + icon: 'none' }) } }, - payOrder(order) { + payOrder(order, event) { + // 阻止事件冒泡 + if (event && event.stopPropagation) { + event.stopPropagation() + } + this.selectedOrder = order this.showPaymentPopup = true }, @@ -592,27 +631,175 @@ async confirmPayment() { if (!this.selectedOrder || this.paying) return - await this.executeOrderOperation( - { - loadingKey: 'paying', - additionalUpdates: { - payment_method: this.selectedPaymentMethod, + this.paying = true + + try { + // 调用真实支付API + const paymentData = { + order_id: this.selectedOrder.id, + student_id: this.studentId, + 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() + }) + + 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 + } + }, + + // 微信支付处理 + async processWechatPayment(paymentData) { + try { + // 调用后端接口获取微信支付参数 + const response = await apiRoute.xs_orderTableUpdatePaymentStatus({ + order_id: paymentData.order_id, + 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: '支付成功' } } - }, - { - order: this.selectedOrder, - statusKey: 'status', - statusValue: 'paid', - delay: 2000, - successMessage: '支付成功', - errorMessage: '支付失败', - callback: () => this.closePaymentPopup() + } else { + throw new Error(response.msg || '获取支付信息失败') } - ) + } catch (error) { + console.error('微信支付处理失败:', error) + throw error + } + }, + + // 支付宝支付处理 + 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 cancelOrder(order) { + // 调用微信支付 + 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({ title: '确认取消', content: '确定要取消此订单吗?', @@ -634,8 +821,35 @@ }) }, - viewRefundDetail(order) { - const refundInfo = `订单号:${order.order_no}\n退款金额:¥${order.refund_amount || order.total_amount}\n退款时间:${order.refund_time || '处理中'}` + // 查看合同 + 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 || '处理中'}` uni.showModal({ title: '退款详情', content: refundInfo, diff --git a/uniapp/pages.json b/uniapp/pages.json index ffecded0..d8e66895 100644 --- a/uniapp/pages.json +++ b/uniapp/pages.json @@ -110,6 +110,15 @@ "navigationBarTextStyle": "white" } }, + { + "path": "orders/detail", + "style": { + "navigationBarTitleText": "订单详情", + "navigationStyle": "custom", + "navigationBarBackgroundColor": "#29d3b4", + "navigationBarTextStyle": "white" + } + }, { "path": "contracts/index", "style": { diff --git a/uniapp/pages/common/dashboard/webview.vue b/uniapp/pages/common/dashboard/webview.vue index 811c99df..ef377488 100644 --- a/uniapp/pages/common/dashboard/webview.vue +++ b/uniapp/pages/common/dashboard/webview.vue @@ -1,280 +1,281 @@ \ No newline at end of file diff --git a/uniapp/pages/market/clue/clue_info.vue b/uniapp/pages/market/clue/clue_info.vue index 21e0060c..a800a7aa 100644 --- a/uniapp/pages/market/clue/clue_info.vue +++ b/uniapp/pages/market/clue/clue_info.vue @@ -347,6 +347,8 @@ export default { // 二维码支付弹窗 showQRCodeModal: false, qrCodePaymentData: null, + // 支付状态轮询定时器 + paymentStatusTimer: null, // 编辑相关 remark_content: '', @@ -423,6 +425,11 @@ export default { onShow() { this.init() }, + + onUnload() { + // 页面销毁时清除定时器,防止内存泄漏 + this.stopPaymentStatusPolling() + }, methods: { async init() { @@ -1273,6 +1280,9 @@ export default { this.qrCodePaymentData = paymentData this.showQRCodeModal = true this.$refs.qrCodePopup.open() + + // 启动支付状态轮询定时器 - 每15秒查询一次 + this.startPaymentStatusPolling() }, // 关闭二维码支付弹窗 @@ -1280,6 +1290,9 @@ export default { this.showQRCodeModal = false this.qrCodePaymentData = null this.$refs.qrCodePopup.close() + + // 清除支付状态轮询定时器 + this.stopPaymentStatusPolling() }, // 确认二维码支付完成 @@ -1438,6 +1451,90 @@ ${orderInfo.paid_at ? '支付时间:' + this.formatOrderTime(orderInfo.paid_at } catch (e) { 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() + } + } + }) } }, }