diff --git a/admin/src/app/views/contract/contract.vue b/admin/src/app/views/contract/contract.vue
index 15fd02fb..5aec75f6 100644
--- a/admin/src/app/views/contract/contract.vue
+++ b/admin/src/app/views/contract/contract.vue
@@ -225,6 +225,7 @@
占位符 |
数据类型 |
数据配置 |
+ 签署方 |
是否必填 |
默认值 |
@@ -238,6 +239,8 @@
+
+
@@ -309,11 +312,29 @@
>
+
+
+ 图片文件上传字段
+
+
+
+
+ 手写签名字段,需跳转签名页面
+
+
请先选择数据类型
|
+
+
+
+ |
必填
|
@@ -810,6 +831,8 @@ const loadPlaceholderConfig = async (contractId: number) => {
system_function: config.system_function || '',
// 用户输入类型配置
user_input_value: config.user_input_value || '',
+ // 签署方配置
+ sign_party: config.sign_party || '',
// 通用配置
field_type: config.field_type || 'text',
is_required: config.is_required || config.required || 0,
@@ -826,6 +849,7 @@ const loadPlaceholderConfig = async (contractId: number) => {
field_name: config.field_name || '',
system_function: config.system_function || '',
user_input_value: config.user_input_value || '',
+ sign_party: config.sign_party || '',
field_type: config.field_type || 'text',
is_required: config.is_required || 0,
default_value: config.default_value || ''
@@ -841,6 +865,7 @@ const loadPlaceholderConfig = async (contractId: number) => {
field_name: '',
system_function: '',
user_input_value: '',
+ sign_party: '',
field_type: 'text',
is_required: 0,
default_value: ''
@@ -856,6 +881,7 @@ const loadPlaceholderConfig = async (contractId: number) => {
field_name: config.field_name || '',
system_function: config.system_function || '',
user_input_value: config.user_input_value || '',
+ sign_party: config.sign_party || '',
field_type: config.field_type || 'text',
is_required: config.is_required || 0,
default_value: config.default_value || ''
@@ -873,6 +899,7 @@ const loadPlaceholderConfig = async (contractId: number) => {
field_name: 'real_name',
system_function: '',
user_input_value: '',
+ sign_party: 'party_b',
field_type: 'text',
is_required: 1,
default_value: ''
@@ -884,6 +911,7 @@ const loadPlaceholderConfig = async (contractId: number) => {
field_name: 'amount',
system_function: '',
user_input_value: '',
+ sign_party: 'party_a',
field_type: 'money',
is_required: 1,
default_value: ''
@@ -895,9 +923,34 @@ const loadPlaceholderConfig = async (contractId: number) => {
field_name: '',
system_function: 'current_date',
user_input_value: '',
+ sign_party: 'party_b',
field_type: 'date',
is_required: 0,
default_value: '2025-01-01'
+ },
+ {
+ placeholder: '{{学员签名}}',
+ data_type: 'signature',
+ table_name: '',
+ field_name: '',
+ system_function: '',
+ user_input_value: '',
+ sign_party: 'party_b',
+ field_type: 'signature',
+ is_required: 1,
+ default_value: ''
+ },
+ {
+ placeholder: '{{机构名称}}',
+ data_type: 'user_input',
+ table_name: '',
+ field_name: '',
+ system_function: '',
+ user_input_value: '某某培训机构',
+ sign_party: 'party_a',
+ field_type: 'text',
+ is_required: 1,
+ default_value: ''
}
]
}
@@ -912,6 +965,7 @@ const loadPlaceholderConfig = async (contractId: number) => {
field_name: 'real_name',
system_function: '',
user_input_value: '',
+ sign_party: 'party_b',
field_type: 'text',
is_required: 1,
default_value: ''
@@ -923,6 +977,7 @@ const loadPlaceholderConfig = async (contractId: number) => {
field_name: 'amount',
system_function: '',
user_input_value: '',
+ sign_party: 'party_a',
field_type: 'money',
is_required: 1,
default_value: ''
@@ -934,9 +989,34 @@ const loadPlaceholderConfig = async (contractId: number) => {
field_name: '',
system_function: 'current_date',
user_input_value: '',
+ sign_party: 'party_b',
field_type: 'date',
is_required: 0,
default_value: '2025-01-01'
+ },
+ {
+ placeholder: '{{学员签名}}',
+ data_type: 'signature',
+ table_name: '',
+ field_name: '',
+ system_function: '',
+ user_input_value: '',
+ sign_party: 'party_b',
+ field_type: 'signature',
+ is_required: 1,
+ default_value: ''
+ },
+ {
+ placeholder: '{{机构名称}}',
+ data_type: 'user_input',
+ table_name: '',
+ field_name: '',
+ system_function: '',
+ user_input_value: '某某培训机构',
+ sign_party: 'party_a',
+ field_type: 'text',
+ is_required: 1,
+ default_value: ''
}
]
}
@@ -954,6 +1034,7 @@ const loadPlaceholderConfig = async (contractId: number) => {
field_name: 'real_name',
system_function: '',
user_input_value: '',
+ sign_party: 'party_b',
field_type: 'text',
is_required: 1,
default_value: ''
@@ -965,6 +1046,7 @@ const loadPlaceholderConfig = async (contractId: number) => {
field_name: 'amount',
system_function: '',
user_input_value: '',
+ sign_party: 'party_a',
field_type: 'money',
is_required: 1,
default_value: ''
@@ -976,9 +1058,34 @@ const loadPlaceholderConfig = async (contractId: number) => {
field_name: '',
system_function: 'current_date',
user_input_value: '',
+ sign_party: 'party_b',
field_type: 'date',
is_required: 0,
default_value: '2025-01-01'
+ },
+ {
+ placeholder: '{{学员签名}}',
+ data_type: 'signature',
+ table_name: '',
+ field_name: '',
+ system_function: '',
+ user_input_value: '',
+ sign_party: 'party_b',
+ field_type: 'signature',
+ is_required: 1,
+ default_value: ''
+ },
+ {
+ placeholder: '{{机构名称}}',
+ data_type: 'user_input',
+ table_name: '',
+ field_name: '',
+ system_function: '',
+ user_input_value: '某某培训机构',
+ sign_party: 'party_a',
+ field_type: 'text',
+ is_required: 1,
+ default_value: ''
}
]
} finally {
@@ -1001,6 +1108,9 @@ const onDataTypeChange = (config: any) => {
if (config.data_type !== 'user_input') {
config.user_input_value = ''
}
+
+ // 保留签署方选择,所有字段都支持甲方乙方选择
+ // config.sign_party 不需要清空
}
// 数据表变更处理
@@ -1015,7 +1125,7 @@ const handleConfigSuccess = async () => {
console.log('💾 开始保存占位符配置...')
console.log('📋 配置数据:', configList.value)
- // 构建保存数据,支持新的三种数据类型
+ // 构建保存数据,支持所有数据类型
const saveData = {
template_id: currentContractId.value,
configs: configList.value.map(config => ({
@@ -1028,6 +1138,8 @@ const handleConfigSuccess = async () => {
system_function: config.data_type === 'system' ? config.system_function : '',
// 用户输入类型配置
user_input_value: config.data_type === 'user_input' ? config.user_input_value : '',
+ // 签署方配置(所有字段都支持甲方乙方选择)
+ sign_party: config.sign_party || '',
// 通用配置
field_type: config.field_type || 'text',
is_required: config.is_required ? 1 : 0,
@@ -1923,4 +2035,28 @@ onMounted(() => {
color: #409eff;
border: 1px solid #b3d8ff;
}
+
+/* 新增配置信息样式 */
+.config-info {
+ color: #67c23a;
+ font-size: 12px;
+ padding: 4px 8px;
+ background: #f0f9ff;
+ border: 1px solid #b3d8ff;
+ border-radius: 4px;
+ display: inline-block;
+}
+
+
+/* 签署方选择样式 */
+.config-table td .form-select {
+ min-width: 100px;
+}
+
+/* 签名类型提示样式 */
+.data-config .config-info {
+ white-space: nowrap;
+ text-align: center;
+ min-width: 150px;
+}
diff --git a/niucloud/app/api/controller/student/ContractController.php b/niucloud/app/api/controller/student/ContractController.php
index eaf6ec1a..c92cfc13 100644
--- a/niucloud/app/api/controller/student/ContractController.php
+++ b/niucloud/app/api/controller/student/ContractController.php
@@ -11,6 +11,7 @@ namespace app\api\controller\student;
use app\service\api\student\ContractService;
use core\base\BaseController;
+use think\Request;
use think\Response;
/**
@@ -107,29 +108,35 @@ class ContractController extends BaseController
* 提交合同签署
* @return Response
*/
- public function signContract()
+ public function signContract(Request $request)
{
- $data = $this->request->params([
- ['contract_id', 0],
- ['student_id', 0],
- ['form_data', []],
- ['signature_image', '']
- ]);
+ $contract_id = $this->request->param('contract_id', 0);
+ $sign_file = $this->request->param('sign_file', '');
- $this->validate($data, [
- 'contract_id' => 'require|integer|gt:0',
- 'student_id' => 'require|integer|gt:0',
- 'form_data' => 'require|array'
- ]);
+ if (empty($contract_id)) {
+ return fail('合同ID不能为空');
+ }
+ if (empty($sign_file)) {
+ return fail('签名文件不能为空');
+ }
+
+ $data = [
+ 'contract_id' => $contract_id,
+ 'personnel_id' => $this->member_id,
+ 'sign_file' => $sign_file
+ ];
try {
- $service = new ContractService();
- $result = $service->signContract($data);
-
- return success($result, '合同签署成功');
-
+ $service = new \app\service\api\apiService\ContractService();
+ $res = $service->signContract($data);
+
+ if (!$res['code']) {
+ return fail($res['msg']);
+ }
+
+ return success($res['data']);
} catch (\Exception $e) {
- return fail($e->getMessage());
+ return fail('签订合同失败:' . $e->getMessage());
}
}
diff --git a/niucloud/app/api/controller/student/KnowledgeController.php b/niucloud/app/api/controller/student/KnowledgeController.php
index 53ebb294..20e1717a 100644
--- a/niucloud/app/api/controller/student/KnowledgeController.php
+++ b/niucloud/app/api/controller/student/KnowledgeController.php
@@ -55,7 +55,7 @@ class KnowledgeController extends BaseController
{
try {
$service = new KnowledgeService();
- $result = $service->getKnowledgeCategories();
+ $result = $service->getKnowledgeCategories('student');
return success($result, '获取知识分类成功');
diff --git a/niucloud/app/api/route/student.php b/niucloud/app/api/route/student.php
index b87c80d1..79f6610d 100644
--- a/niucloud/app/api/route/student.php
+++ b/niucloud/app/api/route/student.php
@@ -99,7 +99,7 @@ Route::group('contract', function () {
// 获取签署表单配置
Route::get('sign-form/:contract_id', 'app\api\controller\student\ContractController@getSignForm');
// 提交合同签署
- Route::post('sign', 'app\api\controller\student\ContractController@signContract');
+ Route::post('student/sign', 'app\api\controller\student\ContractController@signContract');
// 下载合同
Route::get('download/:contract_id', 'app\api\controller\student\ContractController@downloadContract');
// 获取学员基本信息
diff --git a/niucloud/app/service/admin/document/DocumentTemplateService.php b/niucloud/app/service/admin/document/DocumentTemplateService.php
index b71cc546..48e577f5 100644
--- a/niucloud/app/service/admin/document/DocumentTemplateService.php
+++ b/niucloud/app/service/admin/document/DocumentTemplateService.php
@@ -94,6 +94,7 @@ class DocumentTemplateService extends BaseAdminService
'field_name' => $config['field_name'] ?? '',
'system_function' => $config['system_function'] ?? '',
'user_input_value' => $config['user_input_value'] ?? '',
+ 'sign_party' => $config['sign_party'] ?? '',
'field_type' => $config['field_type'] ?? 'text',
'is_required' => $config['is_required'] ?? 0,
'default_value' => $config['default_value'] ?? ''
@@ -115,6 +116,7 @@ class DocumentTemplateService extends BaseAdminService
'field_name' => '',
'system_function' => '',
'user_input_value' => '',
+ 'sign_party' => '',
'field_type' => 'text',
'is_required' => 0,
'default_value' => ''
@@ -406,6 +408,7 @@ class DocumentTemplateService extends BaseAdminService
'field_name' => $config['field_name'] ?? '',
'system_function' => $config['system_function'] ?? '',
'user_input_value' => $config['user_input_value'] ?? '',
+ 'sign_party' => $config['sign_party'] ?? '',
'field_type' => $config['field_type'] ?? 'text',
'is_required' => $config['is_required'] ?? 0,
'default_value' => $config['default_value'] ?? ''
@@ -454,6 +457,7 @@ class DocumentTemplateService extends BaseAdminService
'table_name' => $settings['table_name'] ?? '',
'field_name' => $settings['field_name'] ?? '',
'system_function' => $settings['system_function'] ?? '',
+ 'sign_party' => $settings['sign_party'] ?? '',
'field_type' => $settings['field_type'] ?? 'text',
'is_required' => $settings['is_required'] ?? 0,
'default_value' => $settings['default_value'] ?? '',
@@ -1020,6 +1024,7 @@ class DocumentTemplateService extends BaseAdminService
'field_name' => '',
'system_function' => '',
'user_input_value' => '',
+ 'sign_party' => '',
'field_type' => 'text',
'is_required' => 0,
'default_value' => ''
@@ -1145,6 +1150,7 @@ class DocumentTemplateService extends BaseAdminService
'field_name' => '',
'system_function' => '',
'user_input_value' => '',
+ 'sign_party' => '',
'field_type' => 'text',
'is_required' => 0,
'default_value' => ''
diff --git a/niucloud/app/service/admin/student/StudentService.php b/niucloud/app/service/admin/student/StudentService.php
index b9875132..453e0b5f 100644
--- a/niucloud/app/service/admin/student/StudentService.php
+++ b/niucloud/app/service/admin/student/StudentService.php
@@ -117,7 +117,20 @@ class StudentService extends BaseAdminService
{
//会员标签
if (!empty($data['member_label'])) {
- $data['member_label_array'] = (new StudentLabelService())->getMemberLabelListByLabelIds($data['member_label']);
+ // 确保member_label是数组格式
+ $labelIds = $data['member_label'];
+ if (is_string($labelIds)) {
+ // 如果是JSON字符串,解析为数组
+ $labelIds = json_decode($labelIds, true) ?: [];
+ } elseif (is_numeric($labelIds)) {
+ // 如果是数字,转换为数组
+ $labelIds = [$labelIds];
+ } elseif (!is_array($labelIds)) {
+ // 其他情况设为空数组
+ $labelIds = [];
+ }
+
+ $data['member_label_array'] = (new StudentLabelService())->getMemberLabelListByLabelIds($labelIds);
}
return $data;
}
diff --git a/niucloud/app/service/api/student/ContractService.php b/niucloud/app/service/api/student/ContractService.php
index 27f7017f..52af3f6a 100644
--- a/niucloud/app/service/api/student/ContractService.php
+++ b/niucloud/app/service/api/student/ContractService.php
@@ -191,31 +191,48 @@ class ContractService extends BaseService
// 获取合同基本信息
$contract = Db::table('school_contract')
->where('id', $contractId)
+ ->field('id, contract_name, contract_type, contract_template, contract_content, placeholders')
->find();
if (!$contract) {
throw new CommonException('合同模板不存在');
}
- // 获取需要填写的字段配置
+ // 获取所有字段配置
$formFields = Db::table('school_document_data_source_config')
- ->where([
- ['contract_id', '=', $contractId],
- ['data_type', '=', 'manual']
- ])
- ->field('placeholder, field_type, is_required, default_value')
+ ->where('contract_id', $contractId)
+ ->field('id, placeholder, field_type, data_type, is_required, default_value, table_name, field_name, system_function, sign_party')
+ ->order('id ASC')
->select()
->toArray();
// 格式化表单字段
$fields = [];
foreach ($formFields as $field) {
+ // 根据数据类型预填充默认值
+ $defaultValue = '';
+ switch ($field['data_type']) {
+ case 'database':
+ $defaultValue = $this->getDataFromDatabase($field['table_name'], $field['field_name'], $studentId);
+ break;
+ case 'system':
+ $defaultValue = $this->getSystemValue($field['system_function']);
+ break;
+ case 'user_input':
+ default:
+ $defaultValue = $field['default_value'] ?: '';
+ break;
+ }
+
$fields[] = [
- 'name' => $field['placeholder'],
- 'type' => $field['field_type'],
- 'required' => (bool)$field['is_required'],
- 'default_value' => $field['default_value'] ?: '',
- 'placeholder' => '请输入' . $field['placeholder']
+ 'id' => $field['id'],
+ 'name' => $field['placeholder'], // 使用placeholder作为字段名称
+ 'placeholder' => $field['placeholder'],
+ 'field_type' => $field['field_type'],
+ 'data_type' => $field['data_type'],
+ 'is_required' => (bool)$field['is_required'],
+ 'default_value' => $defaultValue,
+ 'sign_party' => $field['sign_party']
];
}
@@ -223,6 +240,7 @@ class ContractService extends BaseService
'contract_id' => $contractId,
'contract_name' => $contract['contract_name'],
'contract_type' => $contract['contract_type'],
+ 'contract_content' => $contract['contract_content'] ?: '',
'form_fields' => $fields,
'contract_template_url' => $contract['contract_template'] ? get_image_url($contract['contract_template']) : ''
];
diff --git a/niucloud/app/service/api/student/KnowledgeService.php b/niucloud/app/service/api/student/KnowledgeService.php
index 877bac37..57395fd8 100644
--- a/niucloud/app/service/api/student/KnowledgeService.php
+++ b/niucloud/app/service/api/student/KnowledgeService.php
@@ -48,7 +48,9 @@ class KnowledgeService extends BaseService
'28' => ['name' => '套圈游戏', 'icon' => '⭕'],
'29' => ['name' => '鼓励方式', 'icon' => '👏']
];
-
+ private $typeArr = [
+ 'student'=>['13','14','15','27','28','29']
+ ];
/**
* 获取知识文章列表
* @param array $data
@@ -139,7 +141,7 @@ class KnowledgeService extends BaseService
* 获取知识分类列表
* @return array
*/
- public function getKnowledgeCategories()
+ public function getKnowledgeCategories($type)
{
$categories = [];
@@ -151,18 +153,33 @@ class KnowledgeService extends BaseService
->field('table_type, count(*) as count')
->select();
- foreach ($stats as $stat) {
- $tableType = $stat['table_type'];
- if (isset($this->tableTypeMap[$tableType])) {
- $categories[] = [
- 'value' => $tableType,
- 'text' => $this->tableTypeMap[$tableType]['name'],
- 'icon' => $this->tableTypeMap[$tableType]['icon'],
- 'count' => $stat['count']
- ];
+ if ($type === 'student'){
+ foreach ($stats as $stat) {
+ $tableType = $stat['table_type'];
+ if (in_array($tableType,$this->typeArr[$type])){
+ $categories[] = [
+ 'value' => $tableType,
+ 'text' => $this->tableTypeMap[$tableType]['name'],
+ 'icon' => $this->tableTypeMap[$tableType]['icon'],
+ 'count' => $stat['count']
+ ];
+ }
+ }
+ }else{
+ foreach ($stats as $stat) {
+ $tableType = $stat['table_type'];
+ if (isset($this->tableTypeMap[$tableType])){
+ $categories[] = [
+ 'value' => $tableType,
+ 'text' => $this->tableTypeMap[$tableType]['name'],
+ 'icon' => $this->tableTypeMap[$tableType]['icon'],
+ 'count' => $stat['count']
+ ];
+ }
}
}
+
return $categories;
}
diff --git a/uniapp/common/config.js b/uniapp/common/config.js
index 6dfd5008..c98a2253 100644
--- a/uniapp/common/config.js
+++ b/uniapp/common/config.js
@@ -1,5 +1,5 @@
// 环境变量配置
-const env = 'prod'
+const env = 'development'
// const env = 'prod'
const isMockEnabled = false // 默认禁用Mock优先模式,仅作为回退
const isDebug = false // 默认启用调试模式
diff --git a/uniapp/pages-common/contract/contract_sign.vue b/uniapp/pages-common/contract/contract_sign.vue
index fce59bcc..01e7ab13 100644
--- a/uniapp/pages-common/contract/contract_sign.vue
+++ b/uniapp/pages-common/contract/contract_sign.vue
@@ -10,8 +10,10 @@
- {{contractName}}
- 请在下方签名区域进行手写签名
+ {{fieldName}}
+ {{contractName}}
+ 请为此字段进行手写签名
+ 请在下方签名区域进行手写签名
@@ -85,7 +87,7 @@
@click="submitSignature"
:disabled="!hasSigned || submitting"
:loading="submitting">
- {{submitting ? '提交中...' : '确认签订'}}
+ {{submitting ? '提交中...' : (isFormField ? '确认签名' : '确认签订')}}
@@ -120,6 +122,9 @@ export default {
return {
contractId: 0,
contractName: '',
+ fieldName: '', // 字段名称
+ fieldPlaceholder: '', // 字段占位符
+ isFormField: false, // 是否来自表单字段
canvas: null,
ctx: null,
isDrawing: false,
@@ -142,13 +147,30 @@ export default {
}
},
onLoad(options) {
+ // 兼容两种调用方式
if (options.id) {
+ // 原有的合同签署方式
this.contractId = parseInt(options.id)
+ this.isFormField = false
}
+ if (options.contract_id) {
+ // 新的表单字段签名方式
+ this.contractId = parseInt(options.contract_id)
+ this.isFormField = true
+ }
+
if (options.contractName) {
this.contractName = decodeURIComponent(options.contractName)
}
+ if (options.field_name) {
+ this.fieldName = decodeURIComponent(options.field_name)
+ }
+
+ if (options.field_placeholder) {
+ this.fieldPlaceholder = decodeURIComponent(options.field_placeholder)
+ }
+
this.$nextTick(() => {
this.initCanvas()
})
@@ -348,7 +370,13 @@ export default {
}
this.generateSignatureImage(() => {
- this.uploadSignature()
+ if (this.isFormField) {
+ // 表单字段签名 - 返回签名数据
+ this.returnSignatureData()
+ } else {
+ // 原有合同签署 - 直接提交
+ this.uploadSignature()
+ }
})
},
@@ -404,6 +432,43 @@ export default {
this.submitting = false
}
},
+
+ // 返回签名数据给表单页面
+ returnSignatureData() {
+ if (!this.signatureImageUrl) {
+ uni.showToast({
+ title: '签名生成失败,请重试',
+ icon: 'none'
+ })
+ return
+ }
+
+ // 获取前一个页面的实例
+ const pages = getCurrentPages()
+ const prevPage = pages[pages.length - 2]
+
+ if (prevPage) {
+ // 将签名数据传递给前一个页面
+ if (!prevPage.data) {
+ prevPage.data = {}
+ }
+
+ prevPage.data.newSignature = {
+ fieldPlaceholder: this.fieldPlaceholder,
+ signatureData: this.signatureImageUrl
+ }
+ }
+
+ uni.showToast({
+ title: '签名完成',
+ icon: 'success',
+ duration: 1500
+ })
+
+ setTimeout(() => {
+ uni.navigateBack()
+ }, 1500)
+ },
// 上传签名文件
async uploadSignatureFile() {
diff --git a/uniapp/pages-student/contracts/index.vue b/uniapp/pages-student/contracts/index.vue
index 70d51200..3f37c4c7 100644
--- a/uniapp/pages-student/contracts/index.vue
+++ b/uniapp/pages-student/contracts/index.vue
@@ -48,10 +48,10 @@