diff --git a/admin/src/app/views/contract/contract.vue b/admin/src/app/views/contract/contract.vue
index 1471b27d..c5c0b395 100644
--- a/admin/src/app/views/contract/contract.vue
+++ b/admin/src/app/views/contract/contract.vue
@@ -126,11 +126,6 @@
-
-
-
-
-
@@ -294,29 +289,31 @@
-
-
-
+
+
+
-
-
+
+
-
+
+
-
+
+
-
+
diff --git a/niucloud/app/api/controller/apiController/Contract.php b/niucloud/app/api/controller/apiController/Contract.php
index 751ed165..5d650acd 100644
--- a/niucloud/app/api/controller/apiController/Contract.php
+++ b/niucloud/app/api/controller/apiController/Contract.php
@@ -425,4 +425,9 @@ class Contract extends BaseApiService
return fail('合同签署失败:' . $e->getMessage());
}
}
+
+ public function confirmGenerateContract(Request $request)
+ {
+ $contract_sign_id = $request->param('contract_sign_id', 0);
+ }
}
\ No newline at end of file
diff --git a/niucloud/app/api/route/route.php b/niucloud/app/api/route/route.php
index 02d21cb0..720e137c 100644
--- a/niucloud/app/api/route/route.php
+++ b/niucloud/app/api/route/route.php
@@ -613,7 +613,9 @@ Route::group(function () {
Route::post('student/attendance/leave', 'student.AttendanceController/leave');
// 学员取消
Route::post('student/attendance/cancel', 'student.AttendanceController/cancel');
-
+ // 员工端生成合同
+ Route::post('contract/confirmGenerateContract', 'apiController.Contract/confirmGenerateContract');
+
})->middleware(ApiChannel::class)
->middleware(ApiPersonnelCheckToken::class, true)
->middleware(ApiLog::class);
diff --git a/niucloud/app/service/api/apiService/ContractSignFormService.php b/niucloud/app/service/api/apiService/ContractSignFormService.php
index bd18441b..6797ab41 100644
--- a/niucloud/app/service/api/apiService/ContractSignFormService.php
+++ b/niucloud/app/service/api/apiService/ContractSignFormService.php
@@ -33,7 +33,7 @@ class ContractSignFormService extends BaseApiService
/**
* 根据合同签署记录ID获取签署表单配置
* 优先使用contract_sign_id获取该签署关系专属的字段配置
- *
+ *
* @param array $params 请求参数
* - contract_sign_id: 合同签署记录ID (优先)
* - contract_id: 合同模板ID (兼容旧版本)
@@ -90,6 +90,8 @@ class ContractSignFormService extends BaseApiService
'contract_name' => $contract['contract_name'],
'contract_type' => $contract['contract_type'],
'contract_content' => $contract['contract_content'] ?? '',
+ 'status' => $sign_record['status'],
+ 'sign_file' => $sign_record['sign_file'],
'form_fields' => $form_fields,
'student_info' => [
'id' => $student['id'],
@@ -131,7 +133,7 @@ class ContractSignFormService extends BaseApiService
* 获取学员合同签署表单配置
* 该方法为移动端提供合同签署表单的完整配置信息
* 包括合同基本信息、占位符字段配置、预填充数据等
- *
+ *
* @param array $params 请求参数
* - contract_id: 合同模板ID (必填)
* - student_id: 学员ID (必填)
@@ -218,7 +220,7 @@ class ContractSignFormService extends BaseApiService
/**
* 获取合同模板基本信息
* 从数据库中获取合同模板的基础信息
- *
+ *
* @param int $contract_id 合同模板ID
* @return array|null 合同信息数组,失败返回null
*/
@@ -251,7 +253,7 @@ class ContractSignFormService extends BaseApiService
/**
* 获取学员基本信息
* 从数据库中获取学员的详细信息,用于表单预填充
- *
+ *
* @param int $student_id 学员ID
* @return array|null 学员信息数组,失败返回null
*/
@@ -285,7 +287,7 @@ class ContractSignFormService extends BaseApiService
* 获取合同占位符配置
* 从合同模板中解析占位符配置信息
* 支持从 placeholder_config JSON字段获取配置
- *
+ *
* @param int $contract_id 合同模板ID
* @return array 占位符配置数组
*/
@@ -333,7 +335,7 @@ class ContractSignFormService extends BaseApiService
* 处理表单字段配置
* 根据占位符配置生成移动端可用的表单字段数据
* 对不同数据类型进行差异化处理,预填充相应的默认值
- *
+ *
* @param array $placeholder_config 占位符配置数组
* @param array $student 学员信息
* @return array 处理后的表单字段数组
@@ -428,7 +430,7 @@ class ContractSignFormService extends BaseApiService
* 获取数据库字段值
* 根据配置的表名和字段名从数据库获取对应的值
* 支持学员表、订单表、用户表、员工表等多种数据源
- *
+ *
* @param array $config 字段配置
* @param array $student 学员信息(用于关联查询)
* @return string 字段值
@@ -501,7 +503,7 @@ class ContractSignFormService extends BaseApiService
* 获取系统函数值
* 调用预定义的系统函数获取动态值
* 支持日期时间、业务信息、系统信息等多种函数
- *
+ *
* @param array $config 字段配置
* @return string 函数返回值
*/
@@ -545,7 +547,7 @@ class ContractSignFormService extends BaseApiService
* 格式化字段值
* 对特定类型的字段值进行格式化处理
* 例如日期格式化、金额格式化等
- *
+ *
* @param string $field_name 字段名
* @param mixed $value 原始值
* @return string 格式化后的值
@@ -587,7 +589,7 @@ class ContractSignFormService extends BaseApiService
/**
* 保存合同签署记录
* 将签署数据保存到数据库中
- *
+ *
* @param int $contract_id 合同ID
* @param int $student_id 学员ID
* @param array $form_data 表单数据
@@ -734,27 +736,140 @@ class ContractSignFormService extends BaseApiService
switch ($table_name) {
case 'school_student':
- // 学员表:直接从学员信息获取
- $service = new CoreFieldMappingService($config['table_name'], ['id'=>$student['id']]);
- $value = $service->getValue($field_name);
+ // 学员表:使用CoreFieldMappingService进行字段映射和转义
+ $service = new CoreFieldMappingService($config['table_name'], ['id' => $student['id']]);
+
+ // 配置枚举映射
+ $service->setFieldEnums([
+ 'gender' => [0 => '未知', 1 => '男', 2 => '女'],
+ 'status' => [0 => '无效', 1 => '有效', 2 => '过期', 3 => '结业']
+ ]);
+
+ // 配置关联字段映射
+ $service->setFieldRelations([
+ 'user_id' => [
+ 'table' => 'school_customer_resources',
+ 'display_field' => 'name',
+ 'key_field' => 'id'
+ ],
+ 'campus_id' => [
+ 'table' => 'school_campus',
+ 'display_field' => 'campus_name',
+ 'key_field' => 'id'
+ ],
+ 'class_id' => [
+ 'table' => 'school_class',
+ 'display_field' => 'class_name',
+ 'key_field' => 'id'
+ ],
+ 'consultant_id' => [
+ 'table' => 'school_personnel',
+ 'display_field' => 'name',
+ 'key_field' => 'id'
+ ],
+ 'coach_id' => [
+ 'table' => 'school_personnel',
+ 'display_field' => 'name',
+ 'key_field' => 'id'
+ ]
+ ]);
+
+ // 检查是否为关联字段,使用链式调用获取关联值
+ if (in_array($field_name, ['user_id', 'campus_id', 'class_id', 'consultant_id', 'coach_id'])) {
+ $relation_config = $service->getRelationMapping()[$field_name];
+ $value = $service->getValueWithChain($field_name)
+ ->withRelation(
+ $relation_config['table'],
+ $relation_config['display_field'],
+ $relation_config['key_field']
+ );
+ } else {
+ // 普通字段或枚举字段,自动转义枚举值
+ $value = $service->getValue($field_name);
+ }
break;
case 'school_customer_resources':
- // 用户表:通过学员的user_id关联查询
+ // 客户资源表:通过学员的user_id关联查询
if (!empty($student['user_id'])) {
- $service = new CoreFieldMappingService($config['table_name'], ['id'=>$student['user_id']]);
+ $service = new CoreFieldMappingService($config['table_name'], ['id' => $student['user_id']]);
+
+ // 配置枚举映射
+ $service->setFieldEnums([
+ 'gender' => ['male' => '男性', 'female' => '女性', 'other' => '其他'],
+ 'initial_intent' => ['high' => '高', 'medium' => '中', 'low' => '低'],
+ 'rf_type' => ['market' => '市场人员新增', 'sale' => '销售人员新增', 'teacher' => '教练新增'],
+ 'blacklist' => [1 => '可追单', 0 => '黑名单']
+ ]);
+
$value = $service->getValue($field_name);
}
break;
case 'school_order_table':
- // 订单表:查询该学员最新的订单信息
- $order = OrderTable::where('contract_sign_id',$config['contract_sign_id'])->find();
+ // 订单表:使用CoreFieldMappingService进行字段映射和转义
+ $service = new CoreFieldMappingService($config['table_name'], ['contract_sign_id' => $config['contract_sign_id']]);
+
+ // 配置枚举映射
+ $service->setFieldEnums([
+ 'order_type' => [1 => '新订单', 2 => '续费订单', 3 => '内部员工订单', 4 => '转校', 5 => '客户内转课订单'],
+ 'order_status' => ['pending' => '待支付', 'paid' => '已支付', 'signed' => '待签约', 'completed' => '已完成', 'transfer' => '转学'],
+ 'payment_type' => ['cash' => '现金支付', 'scan_code' => '扫码支付', 'subscription' => '订阅支付', 'wxpay_online' => '微信在线代付'],
+ 'gift_type' => [1 => '减现', 2 => '赠课']
+ ]);
- $value = Db::table('school_order_table')
- ->where('student_id', $student['id'])
- ->order('created_at', 'desc')
- ->value($field_name) ?? '';
+ // 配置关联字段映射
+ $service->setFieldRelations([
+ 'course_id' => [
+ 'table' => 'school_course',
+ 'display_field' => 'course_name',
+ 'key_field' => 'id'
+ ],
+ 'class_id' => [
+ 'table' => 'school_class',
+ 'display_field' => 'class_name',
+ 'key_field' => 'id'
+ ],
+ 'staff_id' => [
+ 'table' => 'school_personnel',
+ 'display_field' => 'name',
+ 'key_field' => 'id'
+ ],
+ 'resource_id' => [
+ 'table' => 'school_customer_resources',
+ 'display_field' => 'name',
+ 'key_field' => 'id'
+ ],
+ 'campus_id' => [
+ 'table' => 'school_campus',
+ 'display_field' => 'CONCAT(campus_name, campus_address)',
+ 'key_field' => 'id'
+ ],
+ 'gift_id' => [
+ 'table' => 'shcool_resources_gift',
+ 'display_field' => 'gift_name',
+ 'key_field' => 'id'
+ ],
+ 'course_plan_id' => [
+ 'table' => 'school_student_courses',
+ 'display_field' => 'CONCAT("正式课时数量:", total_hours, ",赠送课时", gift_hours, ",课程有效期为", start_date, "至", end_date)',
+ 'key_field' => 'id'
+ ]
+ ]);
+
+ // 检查是否为关联字段,使用链式调用获取关联值
+ if (in_array($field_name, ['course_id', 'class_id', 'staff_id', 'resource_id', 'campus_id', 'gift_id', 'course_plan_id'])) {
+ $relation_config = $service->getRelationMapping()[$field_name];
+ $value = $service->getValueWithChain($field_name)
+ ->withRelation(
+ $relation_config['table'],
+ $relation_config['display_field'],
+ $relation_config['key_field']
+ );
+ } else {
+ // 普通字段或枚举字段,自动转义枚举值
+ $value = $service->getValue($field_name);
+ }
break;
case 'school_personnel':
@@ -830,7 +945,7 @@ class ContractSignFormService extends BaseApiService
/**
* 员工端合同签署提交
* 处理员工端提交的合同签署表单数据,员工可以填写甲乙双方所有字段
- *
+ *
* @param array $params 提交参数
* - contract_id: 合同模板ID
* - contract_sign_id: 合同签署记录ID
@@ -916,7 +1031,7 @@ class ContractSignFormService extends BaseApiService
/**
* 学员端合同签署提交(新版本)
* 处理学员端提交的合同签署表单数据,学员只能填写乙方字段
- *
+ *
* @param array $params 提交参数
* - contract_id: 合同模板ID
* - contract_sign_id: 合同签署记录ID
@@ -1016,7 +1131,7 @@ class ContractSignFormService extends BaseApiService
/**
* 保存表单数据到 school_document_data_source_config 表的 default_value 字段
* 这是核心方法,确保数据正确写入到指定表中
- *
+ *
* @param int $contract_id 合同模板ID
* @param int $contract_sign_id 合同签署记录ID
* @param array $form_data 表单数据
@@ -1129,7 +1244,7 @@ class ContractSignFormService extends BaseApiService
/**
* 过滤学员端表单数据,只保留乙方字段
- *
+ *
* @param int $contract_id 合同模板ID
* @param int $contract_sign_id 合同签署记录ID
* @param array $form_data 原始表单数据
@@ -1149,7 +1264,7 @@ class ContractSignFormService extends BaseApiService
foreach ($form_data as $placeholder => $value) {
// 查找对应的字段配置
- $config = array_filter($configs, function($c) use ($placeholder) {
+ $config = array_filter($configs, function ($c) use ($placeholder) {
return $c['placeholder'] === $placeholder;
});
@@ -1202,7 +1317,7 @@ class ContractSignFormService extends BaseApiService
/**
* 更新合同签署记录状态
- *
+ *
* @param int $contract_sign_id 合同签署记录ID
* @param int|null $personnel_id 员工ID(员工签署时传入)
* @param string $signature_image 签名图片
diff --git a/niucloud/app/service/core/sys/CoreFieldMappingService.php b/niucloud/app/service/core/sys/CoreFieldMappingService.php
index be497f56..af3cc35e 100644
--- a/niucloud/app/service/core/sys/CoreFieldMappingService.php
+++ b/niucloud/app/service/core/sys/CoreFieldMappingService.php
@@ -299,6 +299,10 @@ class CoreFieldMappingService extends BaseCoreService
// 尝试从缓存获取
$cached_value = Cache::get($cache_key);
if ($cached_value !== false && $cached_value !== null) {
+ // 如果需要返回包装对象,将缓存值包装成FieldValue对象
+ if ($return_wrapper) {
+ return new FieldValue($cached_value, $this, $field_name);
+ }
return $cached_value;
}
}
diff --git a/uniapp/api/apiRoute.js b/uniapp/api/apiRoute.js
index 3699cf18..2af84deb 100644
--- a/uniapp/api/apiRoute.js
+++ b/uniapp/api/apiRoute.js
@@ -1755,6 +1755,13 @@ export default {
})
},
+ // 确认生成合同
+ async confirmGenerateContract(data = {}) {
+ return await http.post('/contract/confirmGenerateContract', {
+ contract_sign_id: data.contract_sign_id,
+ })
+ },
+
// 下载合同文件
async downloadStudentContract(data = {}) {
const params = {
diff --git a/uniapp/pages-student/contracts/sign.vue b/uniapp/pages-student/contracts/sign.vue
index 49bf1809..423f1ff2 100644
--- a/uniapp/pages-student/contracts/sign.vue
+++ b/uniapp/pages-student/contracts/sign.vue
@@ -149,15 +149,41 @@
-
+
+
+
+
+
+
+
+
+
+
+
@@ -180,6 +206,7 @@
formData: {},
loading: true,
submitting: false,
+ generating: false, // 生成合同状态
// 角色和权限控制
userInfo: null,
userRole: 'student', // 默认为学生角色
@@ -441,8 +468,11 @@
if (response.code === 1) {
const data = response.data
this.contractInfo = {
+ contract_sign_id: data.contract_sign_id,
contract_name: data.contract_name,
- contract_type: data.contract_type
+ contract_type: data.contract_type,
+ status: data.status,
+ sign_file: data.sign_file
}
this.contractContent = data.contract_content || ''
this.formFields = data.form_fields || []
@@ -694,6 +724,118 @@
} finally {
this.submitting = false
}
+ },
+
+ // 确认生成合同
+ async confirmGenerateContract() {
+ this.generating = true
+ try {
+ console.log('确认生成合同:', {
+ contractSignId: this.contractSignId
+ })
+
+ const response = await apiRoute.confirmGenerateContract({
+ contract_sign_id: this.contractSignId
+ })
+
+ if (response.code === 1) {
+ uni.showToast({
+ title: '合同生成成功',
+ icon: 'success',
+ duration: 2000
+ })
+
+ // 重新加载表单数据以更新合同状态
+ setTimeout(() => {
+ this.loadSignForm()
+ }, 2000)
+ } else {
+ uni.showToast({
+ title: response.msg || '合同生成失败',
+ icon: 'none'
+ })
+ }
+ } catch (error) {
+ console.error('合同生成失败:', error)
+ uni.showToast({
+ title: '合同生成失败',
+ icon: 'none'
+ })
+ } finally {
+ this.generating = false
+ }
+ },
+ // 下载合同
+ downloadContract() {
+ if (!this.contractInfo || !this.contractInfo.sign_file) {
+ uni.showToast({
+ title: '合同文件不存在',
+ icon: 'none'
+ })
+ return
+ }
+
+ // 下载合同文件
+ uni.showLoading({
+ title: '准备下载...',
+ mask: true
+ })
+
+ // 构建完整的文件URL
+ const fileUrl = this.contractInfo.sign_file.startsWith('http')
+ ? this.contractInfo.sign_file
+ : Api_url + this.contractInfo.sign_file
+
+ // 下载文件
+ uni.downloadFile({
+ url: fileUrl,
+ success: (res) => {
+ uni.hideLoading()
+ if (res.statusCode === 200) {
+ // 保存到相册或打开文件
+ uni.saveFile({
+ tempFilePath: res.tempFilePath,
+ success: (saveRes) => {
+ uni.showToast({
+ title: '下载成功',
+ icon: 'success'
+ })
+
+ // 打开文件
+ uni.openDocument({
+ filePath: saveRes.savedFilePath,
+ success: () => {
+ console.log('打开文档成功')
+ },
+ fail: (err) => {
+ console.error('打开文档失败:', err)
+ }
+ })
+ },
+ fail: (err) => {
+ console.error('保存文件失败:', err)
+ uni.showToast({
+ title: '保存失败',
+ icon: 'none'
+ })
+ }
+ })
+ } else {
+ uni.showToast({
+ title: '下载失败',
+ icon: 'none'
+ })
+ }
+ },
+ fail: (err) => {
+ uni.hideLoading()
+ console.error('下载失败:', err)
+ uni.showToast({
+ title: '下载失败',
+ icon: 'none'
+ })
+ }
+ })
}
}
}
@@ -1079,13 +1221,82 @@
bottom: 0;
left: 0;
right: 0;
- background: #fff;
+ background: rgba(255, 255, 255, 0.98);
+ backdrop-filter: blur(10rpx);
padding: 20rpx 32rpx 40rpx;
border-top: 1rpx solid #f0f0f0;
+ box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1);
+ z-index: 999;
// 小程序端适配底部安全区域
// #ifdef MP-WEIXIN
padding-bottom: calc(40rpx + env(safe-area-inset-bottom));
// #endif
+
+ // 为了确保完全不透明,添加伪元素背景
+ &::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: #fff;
+ z-index: -1;
+ }
+
+ // 按钮组布局
+ .button_group {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 20rpx;
+ }
+
+ // UniApp原生按钮样式
+ .uni-button {
+ height: 88rpx;
+ line-height: 88rpx;
+ border-radius: 44rpx;
+ font-size: 32rpx;
+ font-weight: 500;
+ border: none;
+ outline: none;
+
+ &.generate-btn {
+ flex: 1;
+ background: linear-gradient(135deg, #FF6B35 0%, #F7931E 100%);
+ color: #fff;
+ }
+
+ &.modify-btn {
+ flex: 1;
+ background: linear-gradient(135deg, #29D3B4 0%, #1BA89A 100%);
+ color: #fff;
+ }
+
+ &.download-btn {
+ width: 100%;
+ background: linear-gradient(135deg, #52C41A 0%, #389E0D 100%);
+ color: #fff;
+ }
+
+ &.submit-btn {
+ width: 100%;
+ background: linear-gradient(135deg, #FF6B35 0%, #F7931E 100%);
+ color: #fff;
+ }
+
+ // 按钮按下效果
+ &:active {
+ opacity: 0.8;
+ transform: scale(0.98);
+ }
+
+ // 加载状态
+ &[loading] {
+ opacity: 0.7;
+ }
+ }
}
\ No newline at end of file