From b54b68b4f6915ffbd171c3147f62cf53c227b175 Mon Sep 17 00:00:00 2001 From: zeyan <258785420@qq.com> Date: Mon, 18 Aug 2025 19:44:45 +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 --- .../api/controller/apiController/Contract.php | 89 +++ niucloud/app/api/route/route.php | 3 + .../apiService/ContractSignFormService.php | 524 +++++++++++++++--- uniapp/api/apiRoute.js | 25 +- uniapp/api/member.js | 35 +- uniapp/common/config.js | 4 +- uniapp/pages-market/clue/clue_info.vue | 4 +- uniapp/pages-student/contracts/sign.vue | 220 +++++++- uniapp/pages-student/messages/index.vue | 75 ++- uniapp/pages/student/home/index.vue | 18 +- 10 files changed, 872 insertions(+), 125 deletions(-) diff --git a/niucloud/app/api/controller/apiController/Contract.php b/niucloud/app/api/controller/apiController/Contract.php index 087189b4..751ed165 100644 --- a/niucloud/app/api/controller/apiController/Contract.php +++ b/niucloud/app/api/controller/apiController/Contract.php @@ -336,4 +336,93 @@ class Contract extends BaseApiService return fail('合同签署失败:' . $e->getMessage()); } } + + /** + * 员工端合同签署 + * @param Request $request + * @return mixed + */ + public function signStaffContract(Request $request) + { + $contract_id = $request->param('contract_id', 0); + $contract_sign_id = $request->param('contract_sign_id', 0); + $form_data = $request->param('form_data', []); + $signature_image = $request->param('signature_image', ''); + + if (empty($contract_id)) { + return fail('合同ID不能为空'); + } + + if (empty($contract_sign_id)) { + return fail('合同签署记录ID不能为空'); + } + + $params = [ + 'contract_id' => $contract_id, + 'contract_sign_id' => $contract_sign_id, + 'personnel_id' => $this->member_id, + 'form_data' => $form_data, + 'signature_image' => $signature_image + ]; + + try { + $service = new ContractSignFormService(); + $res = $service->submitStaffContractSign($params); + + if (!$res['code']) { + return fail($res['msg']); + } + + return success($res['data'], '合同签署成功'); + } catch (\Exception $e) { + return fail('合同签署失败:' . $e->getMessage()); + } + } + + /** + * 学员端合同签署 + * @param Request $request + * @return mixed + */ + public function signStudentContract(Request $request) + { + $contract_id = $request->param('contract_id', 0); + $contract_sign_id = $request->param('contract_sign_id', 0); + $student_id = $request->param('student_id', 0); + $form_data = $request->param('form_data', []); + $signature_image = $request->param('signature_image', ''); + + if (empty($contract_id)) { + return fail('合同ID不能为空'); + } + + if (empty($contract_sign_id)) { + return fail('合同签署记录ID不能为空'); + } + + if (empty($student_id)) { + return fail('学生ID不能为空'); + } + + $params = [ + 'contract_id' => $contract_id, + 'contract_sign_id' => $contract_sign_id, + 'student_id' => $student_id, + 'form_data' => $form_data, + 'signature_image' => $signature_image + ]; + + try { + $service = new ContractSignFormService(); + $res = $service->submitStudentContractSign($params); + + if (!$res['code']) { + return fail($res['msg']); + } + + return success($res['data'], '合同签署成功'); + } catch (\Exception $e) { + return fail('合同签署失败:' . $e->getMessage()); + } + } } \ No newline at end of file diff --git a/niucloud/app/api/route/route.php b/niucloud/app/api/route/route.php index 3233a243..02d21cb0 100644 --- a/niucloud/app/api/route/route.php +++ b/niucloud/app/api/route/route.php @@ -468,6 +468,9 @@ Route::group(function () { Route::get('contract/getStudentContractSignFormBySignId', 'apiController.Contract/getStudentContractSignFormBySignId'); // 前端期望的路由路径(映射到相同的控制器方法) Route::get('student/contract/sign-form', 'apiController.Contract/getStudentContractSignForm'); + // 员工端和学员端合同签署接口 + Route::post('contract/signStaffContract', 'apiController.Contract/signStaffContract'); + Route::post('contract/signStudentContract', 'apiController.Contract/signStudentContract'); //服务管理 diff --git a/niucloud/app/service/api/apiService/ContractSignFormService.php b/niucloud/app/service/api/apiService/ContractSignFormService.php index 640a6429..8c9d6457 100644 --- a/niucloud/app/service/api/apiService/ContractSignFormService.php +++ b/niucloud/app/service/api/apiService/ContractSignFormService.php @@ -581,79 +581,6 @@ class ContractSignFormService extends BaseApiService return (string)$value; } - /** - * 提交学员合同签署数据 - * 处理移动端提交的合同签署表单数据 - * 验证数据完整性并保存到数据库 - * - * @param array $params 提交参数 - * - contract_id: 合同模板ID - * - student_id: 学员ID - * - form_data: 表单数据数组 - * @return array 提交结果 - */ - public function submitStudentContractSign(array $params) - { - try { - // 验证必要参数 - if (empty($params['contract_id']) || empty($params['student_id']) || empty($params['form_data'])) { - throw new \Exception('缺少必要参数'); - } - - $contract_id = (int)$params['contract_id']; - $student_id = (int)$params['student_id']; - $form_data = $params['form_data']; - - Log::info('开始处理学员合同签署提交', [ - 'contract_id' => $contract_id, - 'student_id' => $student_id, - 'form_data_keys' => array_keys($form_data) - ]); - - // 验证合同和学员是否存在 - $contract = $this->getContractInfo($contract_id); - if (!$contract) { - throw new \Exception('合同模板不存在'); - } - - $student = $this->getStudentInfo($student_id); - if (!$student) { - throw new \Exception('学员信息不存在'); - } - - // 保存签署记录 - $sign_id = $this->saveContractSignRecord($contract_id, $student_id, $form_data); - - if ($sign_id) { - Log::info('学员合同签署成功', [ - 'contract_id' => $contract_id, - 'student_id' => $student_id, - 'sign_id' => $sign_id - ]); - - return [ - 'code' => 1, - 'msg' => '合同签署成功', - 'data' => ['sign_id' => $sign_id] - ]; - } else { - throw new \Exception('保存签署记录失败'); - } - - } catch (\Exception $e) { - Log::error('学员合同签署失败', [ - 'params' => $params, - 'error' => $e->getMessage(), - 'trace' => $e->getTraceAsString() - ]); - - return [ - 'code' => 0, - 'msg' => $e->getMessage(), - 'data' => [] - ]; - } - } /** * 保存合同签署记录 @@ -895,4 +822,455 @@ class ContractSignFormService extends BaseApiService return ''; } } + + /** + * 员工端合同签署提交 + * 处理员工端提交的合同签署表单数据,员工可以填写甲乙双方所有字段 + * + * @param array $params 提交参数 + * - contract_id: 合同模板ID + * - contract_sign_id: 合同签署记录ID + * - personnel_id: 员工ID + * - form_data: 表单数据数组 + * - signature_image: 签名图片数据 + * @return array 提交结果 + */ + public function submitStaffContractSign(array $params) + { + try { + // 验证必要参数 + if (empty($params['contract_id']) || empty($params['contract_sign_id']) || empty($params['personnel_id'])) { + throw new \Exception('缺少必要参数:contract_id、contract_sign_id、personnel_id'); + } + + $contract_id = (int)$params['contract_id']; + $contract_sign_id = (int)$params['contract_sign_id']; + $personnel_id = (int)$params['personnel_id']; + $form_data = $params['form_data'] ?? []; + $signature_image = $params['signature_image'] ?? ''; + + Log::info('开始处理员工端合同签署提交', [ + 'contract_id' => $contract_id, + 'contract_sign_id' => $contract_sign_id, + 'personnel_id' => $personnel_id, + 'form_data_keys' => array_keys($form_data) + ]); + + // 验证合同签署记录是否存在 + $sign_record = Db::table('school_contract_sign') + ->where('id', $contract_sign_id) + ->where('contract_id', $contract_id) + ->find(); + + if (!$sign_record) { + throw new \Exception('合同签署记录不存在'); + } + + // 验证员工是否存在 + $personnel = Db::table('school_personnel') + ->where('id', $personnel_id) + ->where('status', 1) + ->find(); + + if (!$personnel) { + throw new \Exception('员工信息不存在'); + } + + // 事务处理 + Db::startTrans(); + try { + // 1. 保存表单数据到 school_document_data_source_config 表 + $this->saveFormDataToConfig($contract_id, $contract_sign_id, $form_data, 'staff'); + + // 2. 更新合同签署记录状态 + $this->updateContractSignStatus($contract_sign_id, $personnel_id, $signature_image); + + Db::commit(); + + Log::info('员工端合同签署成功', [ + 'contract_id' => $contract_id, + 'contract_sign_id' => $contract_sign_id, + 'personnel_id' => $personnel_id + ]); + + return [ + 'code' => 1, + 'msg' => '合同签署成功', + 'data' => ['contract_sign_id' => $contract_sign_id] + ]; + + } catch (\Exception $e) { + Db::rollback(); + throw $e; + } + + } catch (\Exception $e) { + Log::error('员工端合同签署失败', [ + 'params' => $params, + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + + return [ + 'code' => 0, + 'msg' => $e->getMessage(), + 'data' => [] + ]; + } + } + + /** + * 学员端合同签署提交(新版本) + * 处理学员端提交的合同签署表单数据,学员只能填写乙方字段 + * + * @param array $params 提交参数 + * - contract_id: 合同模板ID + * - contract_sign_id: 合同签署记录ID + * - student_id: 学员ID + * - form_data: 表单数据数组 + * - signature_image: 签名图片数据 + * @return array 提交结果 + */ + public function submitStudentContractSign(array $params) + { + try { + // 验证必要参数 + if (empty($params['contract_id']) || empty($params['contract_sign_id']) || empty($params['student_id'])) { + throw new \Exception('缺少必要参数:contract_id、contract_sign_id、student_id'); + } + + $contract_id = (int)$params['contract_id']; + $contract_sign_id = (int)$params['contract_sign_id']; + $student_id = (int)$params['student_id']; + $form_data = $params['form_data'] ?? []; + $signature_image = $params['signature_image'] ?? ''; + + Log::info('开始处理学员端合同签署提交', [ + 'contract_id' => $contract_id, + 'contract_sign_id' => $contract_sign_id, + 'student_id' => $student_id, + 'form_data_keys' => array_keys($form_data) + ]); + + // 验证合同签署记录是否存在 + $sign_record = Db::table('school_contract_sign') + ->where('id', $contract_sign_id) + ->where('contract_id', $contract_id) + ->where('student_id', $student_id) + ->find(); + + if (!$sign_record) { + throw new \Exception('合同签署记录不存在'); + } + + // 验证学员是否存在 + $student = Db::table('school_student') + ->where('id', $student_id) + ->where('status', 1) + ->find(); + + if (!$student) { + throw new \Exception('学员信息不存在'); + } + + // 事务处理 + Db::startTrans(); + try { + // 1. 过滤学员端只能填写的字段(乙方字段) + $filtered_form_data = $this->filterStudentFormData($contract_id, $contract_sign_id, $form_data); + + // 2. 保存表单数据到 school_document_data_source_config 表 + $this->saveFormDataToConfig($contract_id, $contract_sign_id, $filtered_form_data, 'student'); + + // 3. 更新合同签署记录状态 + $this->updateContractSignStatus($contract_sign_id, null, $signature_image, $student_id); + + Db::commit(); + + Log::info('学员端合同签署成功', [ + 'contract_id' => $contract_id, + 'contract_sign_id' => $contract_sign_id, + 'student_id' => $student_id + ]); + + return [ + 'code' => 1, + 'msg' => '合同签署成功', + 'data' => ['contract_sign_id' => $contract_sign_id] + ]; + + } catch (\Exception $e) { + Db::rollback(); + throw $e; + } + + } catch (\Exception $e) { + Log::error('学员端合同签署失败', [ + 'params' => $params, + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + + return [ + 'code' => 0, + 'msg' => $e->getMessage(), + 'data' => [] + ]; + } + } + + /** + * 保存表单数据到 school_document_data_source_config 表的 default_value 字段 + * 这是核心方法,确保数据正确写入到指定表中 + * + * @param int $contract_id 合同模板ID + * @param int $contract_sign_id 合同签署记录ID + * @param array $form_data 表单数据 + * @param string $user_type 用户类型(staff/student) + * @return bool + */ + private function saveFormDataToConfig($contract_id, $contract_sign_id, $form_data, $user_type) + { + try { + Log::info('开始保存表单数据到 school_document_data_source_config', [ + 'contract_id' => $contract_id, + 'contract_sign_id' => $contract_sign_id, + 'user_type' => $user_type, + 'form_data_count' => count($form_data) + ]); + + $now = date('Y-m-d H:i:s'); + $success_count = 0; + $total_count = count($form_data); + + foreach ($form_data as $placeholder => $value) { + // 检查该字段配置是否已存在 + $existing_config = Db::table('school_document_data_source_config') + ->where('contract_id', $contract_id) + ->where('contract_sign_id', $contract_sign_id) + ->where('placeholder', $placeholder) + ->find(); + + if ($existing_config) { + // 更新已存在的配置记录的 default_value 字段 + $update_result = Db::table('school_document_data_source_config') + ->where('id', $existing_config['id']) + ->update([ + 'default_value' => (string)$value, + 'updated_at' => $now, + 'updated_by' => $user_type + ]); + + if ($update_result) { + $success_count++; + Log::info('更新字段配置成功', [ + 'config_id' => $existing_config['id'], + 'placeholder' => $placeholder, + 'value' => $value + ]); + } else { + Log::warning('更新字段配置失败', [ + 'config_id' => $existing_config['id'], + 'placeholder' => $placeholder, + 'value' => $value + ]); + } + } else { + // 创建新的配置记录(这种情况说明配置数据可能缺失) + $config_data = [ + 'contract_id' => $contract_id, + 'contract_sign_id' => $contract_sign_id, + 'placeholder' => $placeholder, + 'data_type' => 'user_input', // 默认为用户输入类型 + 'field_type' => 'text', // 默认为文本类型 + 'default_value' => (string)$value, + 'is_required' => 0, + 'sign_party' => '', // 需要根据字段名推断甲乙方 + 'created_at' => $now, + 'updated_at' => $now, + 'created_by' => $user_type, + 'updated_by' => $user_type, + 'deleted_at' => 0 + ]; + + $insert_result = Db::table('school_document_data_source_config')->insertGetId($config_data); + + if ($insert_result) { + $success_count++; + Log::info('创建字段配置成功', [ + 'config_id' => $insert_result, + 'placeholder' => $placeholder, + 'value' => $value + ]); + } else { + Log::warning('创建字段配置失败', [ + 'placeholder' => $placeholder, + 'value' => $value + ]); + } + } + } + + Log::info('表单数据保存完成', [ + 'contract_id' => $contract_id, + 'contract_sign_id' => $contract_sign_id, + 'user_type' => $user_type, + 'total_count' => $total_count, + 'success_count' => $success_count + ]); + + // 如果所有数据都保存成功,返回 true + return $success_count === $total_count; + + } catch (\Exception $e) { + Log::error('保存表单数据到配置表失败', [ + 'contract_id' => $contract_id, + 'contract_sign_id' => $contract_sign_id, + 'user_type' => $user_type, + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + throw $e; + } + } + + /** + * 过滤学员端表单数据,只保留乙方字段 + * + * @param int $contract_id 合同模板ID + * @param int $contract_sign_id 合同签署记录ID + * @param array $form_data 原始表单数据 + * @return array 过滤后的表单数据 + */ + private function filterStudentFormData($contract_id, $contract_sign_id, $form_data) + { + try { + // 获取该签署记录的字段配置 + $configs = Db::table('school_document_data_source_config') + ->where('contract_id', $contract_id) + ->where('contract_sign_id', $contract_sign_id) + ->select() + ->toArray(); + + $filtered_data = []; + + foreach ($form_data as $placeholder => $value) { + // 查找对应的字段配置 + $config = array_filter($configs, function($c) use ($placeholder) { + return $c['placeholder'] === $placeholder; + }); + + if (!empty($config)) { + $config = array_values($config)[0]; + $sign_party = $config['sign_party'] ?? ''; + + // 学员只能填写乙方字段 + if (in_array($sign_party, ['second_party', 'party_b', '乙方', ''])) { + $filtered_data[$placeholder] = $value; + Log::info('学员字段通过过滤', [ + 'placeholder' => $placeholder, + 'sign_party' => $sign_party, + 'value' => $value + ]); + } else { + Log::warning('学员字段被过滤', [ + 'placeholder' => $placeholder, + 'sign_party' => $sign_party, + 'reason' => '学员只能填写乙方字段' + ]); + } + } else { + // 如果没有找到配置,保守处理:允许提交但记录警告 + $filtered_data[$placeholder] = $value; + Log::warning('未找到字段配置,默认允许', [ + 'placeholder' => $placeholder, + 'value' => $value + ]); + } + } + + Log::info('学员表单数据过滤完成', [ + 'original_count' => count($form_data), + 'filtered_count' => count($filtered_data) + ]); + + return $filtered_data; + + } catch (\Exception $e) { + Log::error('过滤学员表单数据失败', [ + 'contract_id' => $contract_id, + 'contract_sign_id' => $contract_sign_id, + 'error' => $e->getMessage() + ]); + // 出现异常时返回原始数据,避免阻断流程 + return $form_data; + } + } + + /** + * 更新合同签署记录状态 + * + * @param int $contract_sign_id 合同签署记录ID + * @param int|null $personnel_id 员工ID(员工签署时传入) + * @param string $signature_image 签名图片 + * @param int|null $student_id 学员ID(学员签署时传入) + * @return bool + */ + private function updateContractSignStatus($contract_sign_id, $personnel_id = null, $signature_image = '', $student_id = null) + { + try { + $now = date('Y-m-d H:i:s'); + + $update_data = [ + 'sign_status' => 1, // 已签署 + 'sign_time' => $now, + 'updated_at' => $now + ]; + + // 如果是员工签署,记录员工ID + if ($personnel_id) { + $update_data['personnel_id'] = $personnel_id; + $update_data['signed_by'] = 'staff'; + } + + // 如果是学员签署,确保学员ID正确 + if ($student_id) { + $update_data['signed_by'] = 'student'; + } + + // 如果有签名图片,保存签名图片路径 + if (!empty($signature_image)) { + $update_data['signature_image'] = $signature_image; + } + + $result = Db::table('school_contract_sign') + ->where('id', $contract_sign_id) + ->update($update_data); + + if ($result) { + Log::info('合同签署记录状态更新成功', [ + 'contract_sign_id' => $contract_sign_id, + 'personnel_id' => $personnel_id, + 'student_id' => $student_id, + 'signature_image_length' => strlen($signature_image) + ]); + return true; + } else { + Log::warning('合同签署记录状态更新失败', [ + 'contract_sign_id' => $contract_sign_id, + 'update_data' => $update_data + ]); + return false; + } + + } catch (\Exception $e) { + Log::error('更新合同签署记录状态失败', [ + 'contract_sign_id' => $contract_sign_id, + 'personnel_id' => $personnel_id, + 'student_id' => $student_id, + 'error' => $e->getMessage() + ]); + throw $e; + } + } } \ No newline at end of file diff --git a/uniapp/api/apiRoute.js b/uniapp/api/apiRoute.js index a9bd79fd..5d743035 100644 --- a/uniapp/api/apiRoute.js +++ b/uniapp/api/apiRoute.js @@ -1757,9 +1757,12 @@ export default { // 获取学员基本信息 async getStudentInfo(data = {}) { - return await http.get('/student/student-info', { - student_id: data.student_id, - }) + return await http.get(`/student/info/${data.student_id}`) + }, + + // 获取当前用户的学员列表 + async getMyStudentList() { + return await http.get('/student/mychild') }, // 获取学员基本信息 @@ -2167,7 +2170,7 @@ export default { keyword: data.keyword, is_read: data.is_read, } - const response = await http.get(`/message-test/list/${data.student_id}`, params) + const response = await http.get(`/message/list/${data.student_id}`, params) return response } catch (error) { console.error('获取学员消息列表错误:', error) @@ -2182,7 +2185,7 @@ export default { const params = { student_id: data.student_id, } - const response = await http.get(`/message-test/detail/${data.message_id}`, params) + const response = await http.get(`/message/detail/${data.message_id}`, params) return response } catch (error) { console.error('获取消息详情错误:', error) @@ -2194,7 +2197,7 @@ export default { // 标记消息已读 async markStudentMessageRead(data = {}) { try { - const response = await http.post('/message-test/mark-read', { + const response = await http.post('/message/mark-read', { message_id: data.message_id, student_id: data.student_id, }) @@ -2213,7 +2216,7 @@ export default { // 批量标记消息已读 async markStudentMessageBatchRead(data = {}) { try { - const response = await http.post('/message-test/mark-batch-read', { + const response = await http.post('/message/mark-batch-read', { student_id: data.student_id, message_ids: data.message_ids, message_type: data.message_type, @@ -2233,7 +2236,7 @@ export default { // 获取学员消息统计 async getStudentMessageStats(data = {}) { try { - const response = await http.get(`/message-test/stats/${data.student_id}`) + const response = await http.get(`/message/stats/${data.student_id}`) return response } catch (error) { console.error('获取消息统计错误:', error) @@ -2252,7 +2255,7 @@ export default { page: data.page || 1, limit: data.limit || 20, } - const response = await http.get('/message-test/conversation', params) + const response = await http.get('/message/conversation', params) return response } catch (error) { console.error('获取对话消息错误:', error) @@ -2275,7 +2278,7 @@ export default { // 学员回复消息 async replyMessage(data = {}) { try { - const response = await http.post('/message-test/reply', { + const response = await http.post('/message/reply', { student_id: data.student_id, to_type: data.to_type, to_id: data.to_id, @@ -2314,7 +2317,7 @@ export default { page: data.page || 1, limit: data.limit || 10, } - const response = await http.get(`/message-test/search/${data.student_id}`, params) + const response = await http.get(`/message/search/${data.student_id}`, params) return response } catch (error) { console.error('搜索消息错误:', error) diff --git a/uniapp/api/member.js b/uniapp/api/member.js index 4c88af67..cf760614 100644 --- a/uniapp/api/member.js +++ b/uniapp/api/member.js @@ -28,15 +28,34 @@ export default { return await http.post('/student/avatar', data); }, - // 获取未读消息数量(如果有接口的话,暂时模拟) - async getUnreadMessageCount(data = {}) { - // 这里可以调用真实的消息接口,暂时返回模拟数据 - return { - code: 1, - data: { - unread_count: Math.floor(Math.random() * 5) + // 获取未读消息数量 + async getUnreadMessageCount(studentId) { + try { + if (!studentId) { + throw new Error('学员ID不能为空') } - }; + // 调用消息统计API + const response = await http.get(`/message/stats/${studentId}`) + if (response && response.code === 1) { + return { + code: 1, + data: { + unread_count: response.data.unread_messages || 0 + } + } + } else { + throw new Error(response?.msg || 'API调用失败') + } + } catch (error) { + console.error('获取未读消息数量失败:', error) + // 降级到0未读消息 + return { + code: 1, + data: { + unread_count: 0 + } + } + } }, //↓↓↓↓↓↓↓↓↓↓↓↓-----体测数据管理接口-----↓↓↓↓↓↓↓↓↓↓↓↓ diff --git a/uniapp/common/config.js b/uniapp/common/config.js index c98a2253..2459f37b 100644 --- a/uniapp/common/config.js +++ b/uniapp/common/config.js @@ -1,6 +1,6 @@ // 环境变量配置 -const env = 'development' -// const env = 'prod' +// const env = 'development' +const env = 'prod' const isMockEnabled = false // 默认禁用Mock优先模式,仅作为回退 const isDebug = false // 默认启用调试模式 const devurl = 'http://localhost:20080/api' diff --git a/uniapp/pages-market/clue/clue_info.vue b/uniapp/pages-market/clue/clue_info.vue index af2cbe4c..b7bb25ba 100644 --- a/uniapp/pages-market/clue/clue_info.vue +++ b/uniapp/pages-market/clue/clue_info.vue @@ -1596,7 +1596,7 @@ ${orderInfo.paid_at ? '支付时间:' + this.formatOrderTime(orderInfo.paid_at } // 构建跳转参数 - let url = `/pages-student/contracts/sign?contract_id=${contractId}&student_id=${studentId}&contract_name=${encodeURIComponent(order.product_name + '合同')}` + let url = `/pages-student/contracts/sign?contract_id=${contractId}&student_id=${studentId}&contract_name=${encodeURIComponent(order.product_name + '合同')}&user_role=staff` // 如果有合同签署记录ID,也传递过去(用于已存在的签署记录) if (contractSignId) { @@ -1626,7 +1626,7 @@ ${orderInfo.paid_at ? '支付时间:' + this.formatOrderTime(orderInfo.paid_at const contractInfo = res.data // 跳转到合同签署页面 uni.navigateTo({ - url: `/pages-student/contracts/sign?contract_id=${contractInfo.contract_id}&student_id=${studentId}&contract_name=${encodeURIComponent(contractInfo.contract_name || order.product_name + '合同')}` + url: `/pages-student/contracts/sign?contract_id=${contractInfo.contract_id}&student_id=${studentId}&contract_name=${encodeURIComponent(contractInfo.contract_name || order.product_name + '合同')}&user_role=staff` }) } else { uni.showToast({ diff --git a/uniapp/pages-student/contracts/sign.vue b/uniapp/pages-student/contracts/sign.vue index f5045954..d2b5c88b 100644 --- a/uniapp/pages-student/contracts/sign.vue +++ b/uniapp/pages-student/contracts/sign.vue @@ -36,13 +36,17 @@ 请填写以下信息 {{ field.name }} * + + {{ getPartyLabel(field) }} + @@ -85,8 +89,8 @@ type="text" :placeholder="field.placeholder || '请输入' + field.name" v-model="formData[field.placeholder]" - :disabled="field.data_type !== 'user_input'" - :class="field.data_type !== 'user_input' ? 'disabled' : ''" + :disabled="!canEditField(field)" + :class="!canEditField(field) ? 'disabled' : ''" /> @@ -115,8 +119,8 @@ type="number" :placeholder="field.placeholder || '请输入' + field.name" v-model="formData[field.placeholder]" - :disabled="field.data_type !== 'user_input'" - :class="field.data_type !== 'user_input' ? 'disabled' : ''" + :disabled="!canEditField(field)" + :class="!canEditField(field) ? 'disabled' : ''" /> @@ -125,8 +129,8 @@ @@ -176,11 +180,38 @@ formFields: [], formData: {}, loading: true, - submitting: false + submitting: false, + // 角色和权限控制 + userInfo: null, + userRole: 'student', // 默认为学生角色 + isStaffMode: false } }, computed: { + // 检查当前用户是否为员工 + isStaff() { + return this.userRole === 'staff' || this.isStaffMode + }, + + // 根据角色过滤表单字段 + filteredFormFields() { + if (!this.formFields || this.formFields.length === 0) { + return [] + } + + // 员工可以填写所有字段 + if (this.isStaff) { + return this.formFields + } + + // 学生只能填写乙方字段 + return this.formFields.filter(field => { + const signParty = field.sign_party || '' + return signParty === 'second_party' || signParty === 'party_b' || signParty === '乙方' + }) + }, + // 渲染合同内容,替换占位符为实际值 renderContractContent() { if (!this.contractContent) return '' @@ -268,6 +299,13 @@ // 新增:支持合同签署记录ID(用于已存在的签署记录) this.contractSignId = parseInt(options.contract_sign_id) || 0 + // 角色检测 + this.userRole = options.user_role || 'student' + this.isStaffMode = options.user_role === 'staff' + + // 获取用户信息进行角色验证 + this.getUserInfo() + if (this.contractId && this.studentId) { this.loadSignForm() } else { @@ -298,6 +336,82 @@ uni.navigateBack() }, + // 获取用户信息 + getUserInfo() { + try { + const userInfo = uni.getStorageSync('userInfo') + if (userInfo) { + this.userInfo = JSON.parse(userInfo) + // 如果缓存的用户类型与URL参数不符,以URL参数为准 + if (this.userInfo.user_type && this.userRole === 'student') { + this.userRole = this.userInfo.user_type === 'staff' ? 'staff' : 'student' + this.isStaffMode = this.userRole === 'staff' + } + } + } catch (error) { + console.error('获取用户信息失败:', error) + } + }, + + // 检查字段是否可编辑 + canEditField(field) { + // 非用户输入类型的字段都不可编辑 + if (field.data_type !== 'user_input') { + return false + } + + // 员工可以编辑所有用户输入字段 + if (this.isStaff) { + return true + } + + // 学生只能编辑乙方字段 + const signParty = field.sign_party || '' + return signParty === 'second_party' || signParty === 'party_b' || signParty === '乙方' + }, + + // 获取字段样式类 + getFieldClass(field) { + const classes = [] + + if (this.isStaff) { + const signParty = field.sign_party || '' + if (signParty === 'first_party' || signParty === 'party_a' || signParty === '甲方') { + classes.push('first_party_field') + } else if (signParty === 'second_party' || signParty === 'party_b' || signParty === '乙方') { + classes.push('second_party_field') + } + } + + if (!this.canEditField(field)) { + classes.push('readonly_field') + } + + return classes.join(' ') + }, + + // 获取甲乙方标签样式类 + getPartyClass(field) { + const signParty = field.sign_party || '' + if (signParty === 'first_party' || signParty === 'party_a' || signParty === '甲方') { + return 'party_first' + } else if (signParty === 'second_party' || signParty === 'party_b' || signParty === '乙方') { + return 'party_second' + } + return 'party_unknown' + }, + + // 获取甲乙方标签文本 + getPartyLabel(field) { + const signParty = field.sign_party || '' + if (signParty === 'first_party' || signParty === 'party_a' || signParty === '甲方') { + return '甲方' + } else if (signParty === 'second_party' || signParty === 'party_b' || signParty === '乙方') { + return '乙方' + } + return '未知' + }, + async loadSignForm() { this.loading = true try { @@ -517,12 +631,24 @@ formData: this.formData }) - // 提交签署数据 - const response = await apiRoute.signStudentContract({ - contract_id: this.contractId, - student_id: this.studentId, - form_data: this.formData - }) + // 根据角色提交签署数据 + let response + if (this.isStaff) { + // 员工端签署 + response = await apiRoute.signStaffContract({ + contract_id: this.contractId, + student_id: this.studentId, + form_data: this.formData, + personnel_id: this.userInfo?.id || 0 + }) + } else { + // 学生端签署 + response = await apiRoute.signStudentContract({ + contract_id: this.contractId, + student_id: this.studentId, + form_data: this.formData + }) + } if (response.code === 1) { uni.showToast({ @@ -863,6 +989,72 @@ 100% { transform: rotate(360deg); } } +// 甲乙方标识和字段样式 +.party_indicator { + display: inline-flex; + align-items: center; + margin-left: 12rpx; + + .party_tag { + font-size: 20rpx; + padding: 4rpx 8rpx; + border-radius: 6rpx; + font-weight: 500; + + &.party_first { + background: rgba(255, 152, 0, 0.1); + color: #ff9800; + border: 1rpx solid rgba(255, 152, 0, 0.3); + } + + &.party_second { + background: rgba(76, 175, 80, 0.1); + color: #4caf50; + border: 1rpx solid rgba(76, 175, 80, 0.3); + } + + &.party_unknown { + background: rgba(158, 158, 158, 0.1); + color: #9e9e9e; + border: 1rpx solid rgba(158, 158, 158, 0.3); + } + } +} + +// 字段样式区分 +.form_field { + &.first_party_field { + border-left: 4rpx solid #ff9800; + background: linear-gradient(90deg, rgba(255, 152, 0, 0.02) 0%, transparent 100%); + + .field_label { + color: #e65100; + } + } + + &.second_party_field { + border-left: 4rpx solid #4caf50; + background: linear-gradient(90deg, rgba(76, 175, 80, 0.02) 0%, transparent 100%); + + .field_label { + color: #2e7d32; + } + } + + &.readonly_field { + background: #f8f9fa; + + .field_label { + color: #6c757d; + } + + input, textarea { + background: #f8f9fa !important; + color: #6c757d !important; + } + } +} + // 提交按钮 .submit_section { position: fixed; diff --git a/uniapp/pages-student/messages/index.vue b/uniapp/pages-student/messages/index.vue index 7c388485..87098735 100644 --- a/uniapp/pages-student/messages/index.vue +++ b/uniapp/pages-student/messages/index.vue @@ -228,9 +228,9 @@ { value: 'all', text: '全部', count: 0 }, { value: 'system', text: '系统消息', count: 0 }, { value: 'notification', text: '通知公告', count: 0 }, - { value: 'homework', text: '作业任务', count: 0 }, - { value: 'feedback', text: '反馈评价', count: 0 }, - { value: 'reminder', text: '课程提醒', count: 0 } + // { value: 'homework', text: '作业任务', count: 0 }, + // { value: 'feedback', text: '反馈评价', count: 0 }, + // { value: 'reminder', text: '课程提醒', count: 0 } ] } }, @@ -241,12 +241,12 @@ } }, - onLoad(options) { + async onLoad(options) { // 使用本地缓存的用户信息而不是页面参数 const userInfo = uni.getStorageSync('userInfo') if (userInfo && userInfo.id) { - this.studentId = parseInt(userInfo.id) - this.initPage() + // userInfo.id 是会员ID,需要查找对应的学员ID + await this.findStudentIdByMemberId(parseInt(userInfo.id)) } else { // 如果没有用户信息,尝试使用页面参数作为备用 this.studentId = parseInt(options.student_id) || 0 @@ -267,6 +267,33 @@ uni.navigateBack() }, + async findStudentIdByMemberId(memberId) { + try { + console.log('查找会员ID对应的学员ID:', memberId) + + // 调用API获取当前用户的学员列表 + const response = await apiRoute.getMyStudentList() + + if (response && response.code === 1 && response.data && response.data.list && response.data.list.length > 0) { + // 使用第一个学员作为当前学员(通常家长账号下只有一个学员) + const firstStudent = response.data.list[0] + this.studentId = firstStudent.id || firstStudent.student_id + console.log('找到学员ID:', this.studentId, '学员姓名:', firstStudent.name) + this.initPage() + } else { + console.warn('当前用户没有关联的学员,使用会员ID作为学员ID') + // 降级处理,使用会员ID作为学员ID + this.studentId = memberId + this.initPage() + } + } catch (error) { + console.error('查找学员ID失败:', error) + // 降级处理,使用会员ID作为学员ID + this.studentId = memberId + this.initPage() + } + }, + async initPage() { await this.loadStudentInfo() await this.loadMessages() @@ -275,14 +302,37 @@ async loadStudentInfo() { try { - // 模拟获取学员信息 - const mockStudentInfo = { - id: this.studentId, - name: '小明' + console.log('加载学员信息:', this.studentId) + + // 调用真实API获取学员信息 + const response = await apiRoute.getStudentInfo({ + student_id: this.studentId + }) + + if (response && response.code === 1 && response.data && response.data.student_info) { + this.studentInfo = { + id: response.data.student_info.id, + name: response.data.student_info.name, + gender_text: response.data.student_info.gender_text, + birthday: response.data.student_info.birthday, + headimg: response.data.student_info.headimg + } + console.log('学员信息加载成功:', this.studentInfo) + } else { + console.warn('API返回失败,使用Mock数据:', response?.msg) + // 降级到Mock数据 + this.studentInfo = { + id: this.studentId, + name: '学员' + } } - this.studentInfo = mockStudentInfo } catch (error) { console.error('获取学员信息失败:', error) + // 网络错误时降级到Mock数据 + this.studentInfo = { + id: this.studentId, + name: '学员' + } } }, @@ -845,12 +895,13 @@ .search_box { .search_input { width: 100%; - padding: 16rpx 20rpx; + padding: 16rpx; border: 1px solid #e0e0e0; border-radius: 8rpx; font-size: 28rpx; background: #f8f9fa; margin-bottom: 16rpx; + height: 60rpx; &::placeholder { color: #999; diff --git a/uniapp/pages/student/home/index.vue b/uniapp/pages/student/home/index.vue index 54e36aef..193ea37e 100644 --- a/uniapp/pages/student/home/index.vue +++ b/uniapp/pages/student/home/index.vue @@ -216,12 +216,14 @@ this.setCurrentWeekDay() await this.loadUserInfo() await this.loadStudentList() - this.loadUnreadMessageCount() + // 在学员信息加载完成后再获取未读消息数量 + await this.loadUnreadMessageCount() }, async refreshData() { await this.loadStudentList() - this.loadUnreadMessageCount() + // 在学员信息加载完成后再获取未读消息数量 + await this.loadUnreadMessageCount() }, setCurrentWeekDay() { @@ -307,8 +309,18 @@ async loadUnreadMessageCount() { try { + // 需要学员ID才能获取未读消息数量 + if (!this.selectedStudent || !this.selectedStudent.id) { + console.warn('没有选中的学员,无法获取未读消息数量') + this.unreadCount = 0 + return + } + + const studentId = this.selectedStudent.student_id || this.selectedStudent.id + console.log('获取学员未读消息数量:', studentId) + // 调用真实API获取未读消息数量 - const response = await apiRoute.getUnreadMessageCount() + const response = await apiRoute.getUnreadMessageCount(studentId) console.log('未读消息数量API响应:', response) if (response.code === 1) {