diff --git a/admin/src/app/views/campus/campus.vue b/admin/src/app/views/campus/campus.vue index 45934fe1..9ac858ee 100644 --- a/admin/src/app/views/campus/campus.vue +++ b/admin/src/app/views/campus/campus.vue @@ -139,7 +139,7 @@ @@ -232,7 +232,7 @@ const sealDialog = reactive({ }) // 上传配置 -const uploadAction = `${import.meta.env.VITE_APP_BASE_URL}/adminapi/upload/file` +const uploadAction = `${import.meta.env.VITE_APP_BASE_URL}sys/image` const uploadHeaders = { token: getToken() } @@ -398,4 +398,48 @@ const saveSeal = async () => { -webkit-line-clamp: 2; -webkit-box-orient: vertical; } + +/* 修复上传组件在弹窗中的样式 */ +.upload-demo { + width: 100%; + + :deep(.el-upload-list--picture-card) { + --el-upload-list-picture-card-size: 100px; + display: flex; + flex-wrap: wrap; + margin: 0 0 10px 0; + } + + :deep(.el-upload--picture-card) { + --el-upload-picture-card-size: 100px; + width: var(--el-upload-picture-card-size); + height: var(--el-upload-picture-card-size); + line-height: calc(var(--el-upload-picture-card-size) - 2px); + margin-bottom: 10px; + } + + :deep(.el-upload-list__item) { + width: var(--el-upload-list-picture-card-size); + height: var(--el-upload-list-picture-card-size); + margin: 0 8px 10px 0; + } + + /* 确保上传区域不超出弹窗 */ + :deep(.el-upload-dragger) { + width: 100%; + height: 100px; + } + + /* 修复提示文字被盖住的问题 */ + :deep(.el-upload__tip) { + margin-top: 25%; + line-height: 1.4; + color: var(--el-text-color-regular); + font-size: 12px; + position: relative; + z-index: 1; + clear: both; + display: block; + } +} diff --git a/niucloud/app/api/controller/apiController/Contract.php b/niucloud/app/api/controller/apiController/Contract.php index c4a55787..087189b4 100644 --- a/niucloud/app/api/controller/apiController/Contract.php +++ b/niucloud/app/api/controller/apiController/Contract.php @@ -254,6 +254,44 @@ class Contract extends BaseApiService } } + /** + * 根据签署记录ID获取学生合同签署表单配置(新方法) + * 支持按签署关系区分不同订单的签署字段 + * @param Request $request + * @return mixed + */ + public function getStudentContractSignFormBySignId(Request $request) + { + $contract_sign_id = $request->param('contract_sign_id', 0); + $student_id = $request->param('student_id', 0); + + if (empty($contract_sign_id)) { + return fail('合同签署记录ID不能为空'); + } + + if (empty($student_id)) { + return fail('学生ID不能为空'); + } + + $params = [ + 'contract_sign_id' => $contract_sign_id, + 'student_id' => $student_id + ]; + + try { + $service = new ContractSignFormService(); + $res = $service->getStudentContractSignFormBySignId($params); + + if (!$res['code']) { + return fail($res['msg']); + } + + return success($res['data']); + } catch (\Exception $e) { + return fail('获取签署表单失败:' . $e->getMessage()); + } + } + /** * 提交学生合同签署 * @param Request $request diff --git a/niucloud/app/api/route/route.php b/niucloud/app/api/route/route.php index 71efffc0..3233a243 100644 --- a/niucloud/app/api/route/route.php +++ b/niucloud/app/api/route/route.php @@ -464,6 +464,8 @@ Route::group(function () { // 学生合同签署相关接口 Route::get('contract/getStudentContractSignForm', 'apiController.Contract/getStudentContractSignForm'); Route::post('contract/submitStudentContractSign', 'apiController.Contract/submitStudentContractSign'); + // 新增:根据签署记录ID获取签署表单(支持按签署关系区分字段) + Route::get('contract/getStudentContractSignFormBySignId', 'apiController.Contract/getStudentContractSignFormBySignId'); // 前端期望的路由路径(映射到相同的控制器方法) Route::get('student/contract/sign-form', 'apiController.Contract/getStudentContractSignForm'); diff --git a/niucloud/app/service/api/apiService/ContractSignFormService.php b/niucloud/app/service/api/apiService/ContractSignFormService.php index f6d2974e..640a6429 100644 --- a/niucloud/app/service/api/apiService/ContractSignFormService.php +++ b/niucloud/app/service/api/apiService/ContractSignFormService.php @@ -28,6 +28,103 @@ class ContractSignFormService extends BaseApiService parent::__construct(); } + /** + * 根据合同签署记录ID获取签署表单配置 + * 优先使用contract_sign_id获取该签署关系专属的字段配置 + * + * @param array $params 请求参数 + * - contract_sign_id: 合同签署记录ID (优先) + * - contract_id: 合同模板ID (兼容旧版本) + * - student_id: 学员ID (必填) + * @return array 返回格式化的表单配置数据 + */ + public function getStudentContractSignFormBySignId(array $params) + { + try { + // 验证必要参数 + if (empty($params['contract_sign_id']) || empty($params['student_id'])) { + throw new \Exception('缺少必要参数:contract_sign_id 和 student_id'); + } + + $contract_sign_id = (int)$params['contract_sign_id']; + $student_id = (int)$params['student_id']; + + Log::info('根据签署记录ID获取合同签署表单', [ + 'contract_sign_id' => $contract_sign_id, + 'student_id' => $student_id + ]); + + // 1. 从签署记录获取合同ID + $sign_record = Db::table('school_contract_sign') + ->where('id', $contract_sign_id) + ->where('student_id', $student_id) + ->find(); + + if (!$sign_record) { + throw new \Exception('合同签署记录不存在'); + } + + $contract_id = $sign_record['contract_id']; + + // 2. 获取合同模板基本信息 + $contract = $this->getContractInfo($contract_id); + if (!$contract) { + throw new \Exception('合同模板不存在或已删除'); + } + + // 3. 获取学员基本信息 + $student = $this->getStudentInfo($student_id); + if (!$student) { + throw new \Exception('学员信息不存在'); + } + + // 4. 从document_data_source_config表获取该签署关系专属的字段配置 + $form_fields = $this->getSignSpecificFormFields($contract_sign_id, $student); + + // 5. 组装返回数据 + $result = [ + 'contract_id' => $contract_id, + 'contract_sign_id' => $contract_sign_id, + 'contract_name' => $contract['contract_name'], + 'contract_type' => $contract['contract_type'], + 'contract_content' => $contract['contract_content'] ?? '', + 'form_fields' => $form_fields, + 'student_info' => [ + 'id' => $student['id'], + 'name' => $student['name'], + 'phone' => $student['contact_phone'] ?? '', + 'user_id' => $student['user_id'] + ] + ]; + + Log::info('根据签署记录ID获取表单成功', [ + 'contract_sign_id' => $contract_sign_id, + 'student_id' => $student_id, + 'form_fields_count' => count($form_fields) + ]); + + return [ + 'code' => 1, + 'msg' => '获取成功', + 'data' => $result + ]; + + } catch (\Exception $e) { + Log::error('根据签署记录ID获取表单失败', [ + 'contract_sign_id' => $params['contract_sign_id'] ?? 0, + 'student_id' => $params['student_id'] ?? 0, + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + + return [ + 'code' => 0, + 'msg' => $e->getMessage(), + 'data' => [] + ]; + } + } + /** * 获取学员合同签署表单配置 * 该方法为移动端提供合同签署表单的完整配置信息 @@ -598,4 +695,204 @@ class ContractSignFormService extends BaseApiService return false; } } + + /** + * 获取签署关系专属的表单字段 + * @param int $contract_sign_id 合同签署记录ID + * @param array $student 学员信息 + * @return array + */ + private function getSignSpecificFormFields($contract_sign_id, $student) + { + try { + // 从document_data_source_config表获取该签署关系专属的字段配置 + $configs = Db::table('school_document_data_source_config') + ->where('contract_sign_id', $contract_sign_id) + ->select() + ->toArray(); + + if (empty($configs)) { + Log::warning('该签署记录无字段配置', ['contract_sign_id' => $contract_sign_id]); + return []; + } + + $form_fields = []; + + foreach ($configs as $config) { + // 基础字段信息 + $field = [ + 'name' => $config['placeholder'] ?? '', + 'placeholder' => $config['placeholder'] ?? '', + 'data_type' => $config['data_type'] ?? 'user_input', + 'field_type' => $config['field_type'] ?? 'text', + 'is_required' => (int)($config['is_required'] ?? 0), + 'default_value' => '', + 'validation_rule' => $config['validation_rule'] ?? '', + 'sign_party' => $config['sign_party'] ?? '', + ]; + + // 根据数据类型处理默认值 + switch ($config['data_type'] ?? 'user_input') { + case 'database': + // 数据库类型:从相关表获取数据 + $field['default_value'] = $this->getDatabaseFieldValueFromConfig($config, $student); + break; + + case 'system': + // 系统函数类型:调用系统函数获取值 + $field['default_value'] = $this->getSystemFunctionValueFromConfig($config); + break; + + case 'user_input': + // 用户输入类型:使用配置的默认值 + $field['default_value'] = $config['default_value'] ?? ''; + break; + + case 'signature': + // 电子签名类型:无默认值,需要用户手写签名 + $field['signature_type'] = 'handwrite'; + $field['default_value'] = ''; + break; + + case 'sign_img': + // 签名图片类型:无默认值,需要用户上传或选择 + $field['sign_image_source'] = 'upload'; + $field['default_value'] = ''; + break; + + default: + $field['default_value'] = $config['default_value'] ?? ''; + break; + } + + $form_fields[] = $field; + } + + Log::info('获取签署关系专属字段完成', [ + 'contract_sign_id' => $contract_sign_id, + 'fields_count' => count($form_fields) + ]); + + return $form_fields; + + } catch (\Exception $e) { + Log::error('获取签署关系专属字段失败', [ + 'contract_sign_id' => $contract_sign_id, + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + return []; + } + } + + /** + * 从配置中获取数据库字段值 + * @param array $config 字段配置 + * @param array $student 学员信息 + * @return string + */ + private function getDatabaseFieldValueFromConfig($config, $student) + { + try { + $table_name = $config['table_name'] ?? ''; + $field_name = $config['field_name'] ?? ''; + + if (empty($table_name) || empty($field_name)) { + return ''; + } + + $value = ''; + + switch ($table_name) { + case 'school_student': + // 学员表:直接从学员信息获取 + $value = $student[$field_name] ?? ''; + break; + + case 'school_customer_resources': + // 用户表:通过学员的user_id关联查询 + if (!empty($student['user_id'])) { + $value = Db::table('school_customer_resources') + ->where('id', $student['user_id']) + ->value($field_name) ?? ''; + } + break; + + case 'school_order_table': + // 订单表:查询该学员最新的订单信息 + $value = Db::table('school_order_table') + ->where('student_id', $student['id']) + ->order('created_at', 'desc') + ->value($field_name) ?? ''; + break; + + case 'school_personnel': + // 员工表:这里可能需要根据业务逻辑确定关联的员工 + // 暂时返回空值,具体业务逻辑需要根据实际需求调整 + $value = ''; + break; + + default: + Log::warning('不支持的数据库表', [ + 'table_name' => $table_name, + 'field_name' => $field_name + ]); + break; + } + + // 对特殊字段进行格式化处理 + $value = $this->formatFieldValue($field_name, $value); + + return (string)$value; + + } catch (\Exception $e) { + Log::error('从配置获取数据库字段值失败', [ + 'config' => $config, + 'student_id' => $student['id'], + 'error' => $e->getMessage() + ]); + return ''; + } + } + + /** + * 从配置中获取系统函数值 + * @param array $config 字段配置 + * @return string + */ + private function getSystemFunctionValueFromConfig($config) + { + try { + $system_function = $config['system_function'] ?? ''; + + if (empty($system_function)) { + return ''; + } + + // 检查函数是否存在 + if (!function_exists($system_function)) { + Log::warning('系统函数不存在', [ + 'function_name' => $system_function + ]); + return ''; + } + + // 调用系统函数 + $value = call_user_func($system_function); + + Log::info('系统函数调用成功', [ + 'function_name' => $system_function, + 'result' => $value + ]); + + return (string)$value; + + } catch (\Exception $e) { + Log::error('从配置获取系统函数值失败', [ + 'config' => $config, + 'error' => $e->getMessage() + ]); + return ''; + } + } } \ No newline at end of file diff --git a/niucloud/app/service/api/apiService/OrderTableService.php b/niucloud/app/service/api/apiService/OrderTableService.php index 5bf3fd3b..6cab81d1 100644 --- a/niucloud/app/service/api/apiService/OrderTableService.php +++ b/niucloud/app/service/api/apiService/OrderTableService.php @@ -411,7 +411,7 @@ class OrderTableService extends BaseApiService 'contract_sign_id' => $result // 合同签署记录ID ]); // 创建签署记录成功后,为甲乙双方生成数据源配置记录 - $this->createDocumentDataSourceConfig($course['contract_id'], $orderData); + $this->createDocumentDataSourceConfig($course['contract_id'], $result, $orderData); \think\facade\Log::info('合同签署记录创建成功', [ 'student_id' => $student_id, @@ -433,11 +433,12 @@ class OrderTableService extends BaseApiService /** * 为甲乙双方创建文档数据源配置记录 - * @param int $contract_id 合同ID + * @param int $contract_id 合同模板ID + * @param int $contract_sign_id 合同签署记录ID * @param array $orderData 订单数据 * @return bool */ - private function createDocumentDataSourceConfig($contract_id, $orderData) + private function createDocumentDataSourceConfig($contract_id, $contract_sign_id, $orderData) { try { // 获取合同模板的占位符配置 @@ -456,10 +457,11 @@ class OrderTableService extends BaseApiService $insert_data = []; $now = date('Y-m-d H:i:s'); - // 为每个占位符配置创建数据源记录 + // 为每个占位符配置创建数据源记录,关联到具体的签署记录 foreach ($placeholder_config as $config) { $data_source_record = [ 'contract_id' => $contract_id, + 'contract_sign_id' => $contract_sign_id, // 新增:关联到具体的签署记录 'placeholder' => $config['placeholder'] ?? '', 'data_type' => $config['data_type'] ?? 'user_input', 'system_function' => $config['system_function'] ?? '', @@ -483,6 +485,7 @@ class OrderTableService extends BaseApiService if ($result) { \think\facade\Log::info('文档数据源配置创建成功', [ 'contract_id' => $contract_id, + 'contract_sign_id' => $contract_sign_id, 'order_id' => $orderData['id'], 'records_count' => count($insert_data) ]); @@ -495,6 +498,7 @@ class OrderTableService extends BaseApiService } catch (\Exception $e) { \think\facade\Log::error('创建文档数据源配置异常', [ 'contract_id' => $contract_id, + 'contract_sign_id' => $contract_sign_id, 'order_data' => $orderData, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() diff --git a/uniapp/api/apiRoute.js b/uniapp/api/apiRoute.js index df420cab..a9bd79fd 100644 --- a/uniapp/api/apiRoute.js +++ b/uniapp/api/apiRoute.js @@ -1726,6 +1726,14 @@ export default { }) }, + // 根据签署记录ID获取合同签署表单配置(新方法-支持按签署关系区分字段) + async getStudentContractSignFormBySignId(data = {}) { + return await http.get('/contract/getStudentContractSignFormBySignId', { + contract_sign_id: data.contract_sign_id, + student_id: data.student_id, + }) + }, + // 提交合同签署 async signStudentContract(data = {}) { return await http.post('/student/contract/sign', { diff --git a/uniapp/pages-market/clue/clue_info.vue b/uniapp/pages-market/clue/clue_info.vue index 61c2a2e2..af2cbe4c 100644 --- a/uniapp/pages-market/clue/clue_info.vue +++ b/uniapp/pages-market/clue/clue_info.vue @@ -1199,6 +1199,9 @@ export default { course_count: order.total_hours || 0, // 使用课时数 status: this.mapOrderStatus(order.order_status), create_time: order.created_at, + // 新增合同相关字段 + contract_id: order.contract_id, + contract_sign_id: order.contract_sign_id, // 保留原始数据 _raw: order })) @@ -1575,7 +1578,8 @@ ${orderInfo.paid_at ? '支付时间:' + this.formatOrderTime(orderInfo.paid_at goToContractSign(order) { // 检查必要参数 const studentId = order.student_id || this.currentStudent?.id - const contractId = order._raw?.contract_id || order.contract_id + const contractId = order.contract_id || order._raw?.contract_id + const contractSignId = order.contract_sign_id || order._raw?.contract_sign_id if (!studentId) { uni.showToast({ @@ -1586,15 +1590,22 @@ ${orderInfo.paid_at ? '支付时间:' + this.formatOrderTime(orderInfo.paid_at } if (!contractId) { - // 如果订单中没有合同ID,尝试从课程信息或其他地方获取 - // 这里可能需要调用API获取合同信息 + // 如果订单中没有合同模板ID,尝试从课程信息获取 this.getContractByOrder(order, studentId) return } - // 跳转到合同签署页面 + // 构建跳转参数 + let url = `/pages-student/contracts/sign?contract_id=${contractId}&student_id=${studentId}&contract_name=${encodeURIComponent(order.product_name + '合同')}` + + // 如果有合同签署记录ID,也传递过去(用于已存在的签署记录) + if (contractSignId) { + url += `&contract_sign_id=${contractSignId}` + } + + // 跳转到学员端合同签署页面 uni.navigateTo({ - url: `/pages-student/contracts/sign?contract_id=${contractId}&student_id=${studentId}&contract_name=${encodeURIComponent(order.product_name + '合同')}` + url: url }) }, diff --git a/uniapp/pages-student/contracts/sign.vue b/uniapp/pages-student/contracts/sign.vue index 0db54738..14beb9af 100644 --- a/uniapp/pages-student/contracts/sign.vue +++ b/uniapp/pages-student/contracts/sign.vue @@ -168,6 +168,7 @@ return { contractId: 0, studentId: 0, + contractSignId: 0, // 新增:合同签署记录ID contractName: '', contractInfo: null, contractContent: '', @@ -264,6 +265,8 @@ this.contractId = parseInt(options.contract_id) || 0 this.studentId = parseInt(options.student_id) || 0 this.contractName = decodeURIComponent(options.contract_name || '') + // 新增:支持合同签署记录ID(用于已存在的签署记录) + this.contractSignId = parseInt(options.contract_sign_id) || 0 if (this.contractId && this.studentId) { this.loadSignForm() @@ -298,14 +301,30 @@ async loadSignForm() { this.loading = true try { - console.log('加载签署表单:', { contractId: this.contractId, studentId: this.studentId }) - - // 获取签署表单配置 - const response = await apiRoute.getStudentContractSignForm({ - contract_id: this.contractId, - student_id: this.studentId + console.log('加载签署表单:', { + contractId: this.contractId, + studentId: this.studentId, + contractSignId: this.contractSignId }) + let response; + + // 优先使用contract_sign_id获取签署表单(支持按签署关系区分字段) + if (this.contractSignId && this.contractSignId > 0) { + console.log('使用签署记录ID获取表单配置') + response = await apiRoute.getStudentContractSignFormBySignId({ + contract_sign_id: this.contractSignId, + student_id: this.studentId + }) + } else { + console.log('使用合同模板ID获取表单配置(兼容模式)') + // 兼容旧版本:使用contract_id获取签署表单 + response = await apiRoute.getStudentContractSignForm({ + contract_id: this.contractId, + student_id: this.studentId + }) + } + if (response.code === 1) { const data = response.data this.contractInfo = {