From db75a64d5e200b230b6790a42a64a8d9a2c20129 Mon Sep 17 00:00:00 2001
From: zeyan <258785420@qq.com>
Date: Mon, 4 Aug 2025 12:25: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
---
admin/src/api/contract.ts | 12 +
admin/src/app/views/contract/contract.vue | 328 +++++++++++++++++-
.../controller/document/DocumentTemplate.php | 36 ++
.../app/adminapi/route/document_template.php | 2 +
.../api/controller/apiController/Course.php | 1 +
.../apiController/PersonCourseSchedule.php | 2 +
niucloud/app/api/route/student.php | 2 +-
.../document/DocumentDataSourceConfig.php | 2 +-
.../course_schedule/CourseScheduleService.php | 3 +
.../document/DocumentTemplateService.php | 220 +++++++++++-
.../service/api/apiService/CourseService.php | 102 ++----
.../PersonCourseScheduleService.php | 12 +-
.../service/api/apiService/StudentService.php | 17 +-
uniapp/App.vue | 203 ++++++++++-
.../fitness-record-popup.vue | 175 +++++++---
.../student-edit-popup/student-edit-popup.vue | 13 +-
.../coach/student/student_list.vue | 3 +
.../pages-common/contract/contract_sign.vue | 75 +++-
.../clue/class_arrangement_detail.vue | 15 +-
uniapp/pages-market/clue/clue_info.vue | 7 +-
uniapp/pages-student/contracts/index.vue | 30 +-
uniapp/pages.json | 3 +-
uniapp/pages/student/contracts/index.vue | 223 +++++++++---
uniapp/pages/student/profile/index.vue | 3 +-
24 files changed, 1260 insertions(+), 229 deletions(-)
diff --git a/admin/src/api/contract.ts b/admin/src/api/contract.ts
index 53cd6308..5e7456c1 100644
--- a/admin/src/api/contract.ts
+++ b/admin/src/api/contract.ts
@@ -61,6 +61,18 @@ export const contractTemplateApi = {
savePlaceholderConfig: (contractId: number, data: any) =>
request.post(`/document_template/config/save`, data),
+ // 重新识别占位符
+ reidentifyPlaceholders: (contractId: number) =>
+ request.post(`/document_template/reidentify/${contractId}`),
+
+ // 更新模板Word文档
+ updateTemplateFile: (contractId: number, data: FormData) =>
+ request.post(`/document_template/update_file/${contractId}`, data, {
+ headers: {
+ 'Content-Type': 'multipart/form-data'
+ }
+ }),
+
// 保存数据源配置到独立表
saveDataSourceConfig: (contractId: number, data: any) =>
request.post(`/document_template/config/datasource/save`, { contract_id: contractId, configs: data }),
diff --git a/admin/src/app/views/contract/contract.vue b/admin/src/app/views/contract/contract.vue
index 9eeb34d7..15fd02fb 100644
--- a/admin/src/app/views/contract/contract.vue
+++ b/admin/src/app/views/contract/contract.vue
@@ -159,7 +159,65 @@
-
检测到的占位符 (合同ID: {{ currentContractId }})
+
+
+
+
@@ -412,8 +470,17 @@ const currentContract = ref(null)
const uploading = ref(false)
const configLoading = ref(false)
const configList = ref([])
+const reidentifying = ref(false)
const fileInputKey = ref(0)
const fileInput = ref()
+
+// 重新上传Word文档相关状态
+const showReuploadSection = ref(false)
+const reuploadFile = ref(null)
+const reuploadFileName = ref('')
+const reuploading = ref(false)
+const reuploadFileInput = ref()
+const currentTemplateInfo = ref(null)
const staffList = ref([])
const filteredStaffList = ref([])
const staffLoading = ref(false)
@@ -519,7 +586,10 @@ const updateStatus = async (row: ContractTemplate) => {
const configPlaceholder = async (row: ContractTemplate) => {
currentContractId.value = row.id
+ currentTemplateInfo.value = row // 保存当前模板信息
showConfigDialog.value = true
+ showReuploadSection.value = false // 重置重新上传区域
+ clearReuploadFile() // 清空重新上传的文件
// 加载占位符配置数据
await loadPlaceholderConfig(row.id)
}
@@ -665,12 +735,66 @@ const handleUploadSuccess = () => {
getList()
}
+// 重新识别占位符
+const reidentifyPlaceholders = async () => {
+ if (!currentContractId.value) {
+ ElMessage.error('未找到合同ID')
+ return
+ }
+
+ reidentifying.value = true
+ try {
+ console.log('🔍 开始重新识别占位符, 合同ID:', currentContractId.value)
+
+ // 调用重新识别接口
+ const { data } = await contractTemplateApi.reidentifyPlaceholders(currentContractId.value)
+ console.log('✅ 重新识别成功, 返回数据:', data)
+
+ // 展示详细的成功信息
+ const { placeholders, placeholder_count, new_placeholders, removed_placeholders } = data
+ let message = `成功识别到 ${placeholder_count} 个占位符`
+
+ if (new_placeholders && new_placeholders.length > 0) {
+ message += `,新增 ${new_placeholders.length} 个`
+ }
+ if (removed_placeholders && removed_placeholders.length > 0) {
+ message += `,移除 ${removed_placeholders.length} 个`
+ }
+
+ ElMessage.success(message)
+
+ // 重新加载配置数据
+ await loadPlaceholderConfig(currentContractId.value)
+
+ } catch (error) {
+ console.error('❌ 重新识别失败:', error)
+
+ // 根据错误类型显示不同的提示
+ let errorMessage = error.message || '未知错误'
+
+ if (errorMessage.includes('模板未上传Word文档')) {
+ ElMessage.error('请先上传Word文档模板后再进行占位符识别')
+ } else if (errorMessage.includes('模板文件不存在')) {
+ ElMessage.error('模板文件不存在,请重新上传')
+ } else {
+ ElMessage.error(`重新识别失败: ${errorMessage}`)
+ }
+ } finally {
+ reidentifying.value = false
+ }
+}
+
// 加载占位符配置
const loadPlaceholderConfig = async (contractId: number) => {
configLoading.value = true
try {
const { data } = await contractTemplateApi.getPlaceholderConfig(contractId)
console.log('API返回数据:', data)
+
+ // 更新当前模板信息
+ if (data && typeof data === 'object') {
+ currentTemplateInfo.value = data
+ }
// 处理API返回的数据格式,支持新的三种数据类型
if (data && typeof data === 'object') {
@@ -995,6 +1119,100 @@ const clearFile = () => {
// fileInputKey.value += 1 // 注释掉,避免意外重新渲染导致文件丢失
}
+// 重新上传文件选择处理
+const handleReuploadFileSelect = (event: Event) => {
+ const target = event.target as HTMLInputElement
+ const file = target.files?.[0]
+
+ console.log('📁 重新上传文件选择:', file)
+
+ if (!file) {
+ reuploadFile.value = null
+ reuploadFileName.value = ''
+ return
+ }
+
+ // 检查文件类型
+ const fileName = file.name.toLowerCase()
+ const allowedExtensions = ['.docx', '.doc']
+ const isValidType = allowedExtensions.some(ext => fileName.endsWith(ext))
+
+ if (!isValidType) {
+ ElMessage.error('只支持上传 .docx 和 .doc 格式的文件!')
+ clearReuploadFile()
+ return
+ }
+
+ // 检查文件大小 (10MB)
+ if (file.size > 10 * 1024 * 1024) {
+ ElMessage.error('文件大小不能超过 10MB!')
+ clearReuploadFile()
+ return
+ }
+
+ // 存储文件信息
+ reuploadFile.value = file
+ reuploadFileName.value = file.name
+
+ console.log('✅ 重新上传文件选择成功:', { name: file.name, size: file.size })
+}
+
+// 清空重新上传文件
+const clearReuploadFile = () => {
+ reuploadFile.value = null
+ reuploadFileName.value = ''
+ if (reuploadFileInput.value) {
+ reuploadFileInput.value.value = ''
+ }
+}
+
+// 提交重新上传
+const submitReupload = async () => {
+ if (!reuploadFile.value) {
+ ElMessage.error('请选择Word文档')
+ return
+ }
+
+ if (!currentContractId.value) {
+ ElMessage.error('未找到模板ID')
+ return
+ }
+
+ reuploading.value = true
+ try {
+ console.log('🚀 开始重新上传Word文档...', {
+ templateId: currentContractId.value,
+ fileName: reuploadFile.value.name,
+ fileSize: reuploadFile.value.size
+ })
+
+ // 构建FormData
+ const formData = new FormData()
+ formData.append('file', reuploadFile.value)
+
+ // 调用更新模板文档的API
+ await contractTemplateApi.updateTemplateFile(currentContractId.value, formData)
+
+ console.log('✅ 重新上传成功')
+ ElMessage.success('Word文档更新成功')
+
+ // 重置状态
+ showReuploadSection.value = false
+ clearReuploadFile()
+
+ // 重新加载模板信息和配置
+ await loadPlaceholderConfig(currentContractId.value)
+ // 更新列表中的模板信息
+ getList()
+
+ } catch (error) {
+ console.error('❌ 重新上传失败:', error)
+ ElMessage.error(`更新文档失败: ${error.message || '未知错误'}`)
+ } finally {
+ reuploading.value = false
+ }
+}
+
// 提交上传
const submitUpload = async () => {
console.log('🚀 开始上传流程...')
@@ -1187,6 +1405,114 @@ onMounted(() => {
margin-bottom: 15px;
}
+.config-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 15px;
+}
+
+.config-header h4 {
+ margin: 0;
+ color: #303133;
+}
+
+.header-buttons {
+ display: flex;
+ gap: 8px;
+ align-items: center;
+}
+
+.btn-reidentify {
+ padding: 6px 12px;
+ border: 1px solid #67c23a;
+ background: #67c23a;
+ color: white;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 12px;
+ transition: all 0.3s;
+}
+
+.btn-reidentify:hover:not(:disabled) {
+ background: #85ce61;
+ border-color: #85ce61;
+}
+
+.btn-reidentify:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+}
+
+.btn-upload {
+ padding: 6px 12px;
+ border: 1px solid #409eff;
+ background: #409eff;
+ color: white;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 12px;
+ transition: all 0.3s;
+}
+
+.btn-upload:hover:not(:disabled) {
+ background: #66b1ff;
+ border-color: #66b1ff;
+}
+
+.btn-upload:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+}
+
+.btn-secondary {
+ padding: 6px 12px;
+ border: 1px solid #909399;
+ background: #909399;
+ color: white;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 12px;
+ transition: all 0.3s;
+}
+
+.btn-secondary:hover:not(:disabled) {
+ background: #a6a9ad;
+ border-color: #a6a9ad;
+}
+
+.btn-secondary:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+}
+
+.reupload-section {
+ margin: 20px 0;
+ padding: 15px;
+ background: #f9f9f9;
+ border: 1px solid #e4e7ed;
+ border-radius: 4px;
+}
+
+.reupload-section h5 {
+ margin: 0 0 15px 0;
+ color: #303133;
+ font-size: 14px;
+ font-weight: 500;
+}
+
+.reupload-form {
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+}
+
+.reupload-actions {
+ display: flex;
+ gap: 10px;
+ justify-content: flex-end;
+}
+
.config-table {
width: 100%;
border-collapse: collapse;
diff --git a/niucloud/app/adminapi/controller/document/DocumentTemplate.php b/niucloud/app/adminapi/controller/document/DocumentTemplate.php
index fa61cacd..fa33c0dd 100644
--- a/niucloud/app/adminapi/controller/document/DocumentTemplate.php
+++ b/niucloud/app/adminapi/controller/document/DocumentTemplate.php
@@ -309,4 +309,40 @@ class DocumentTemplate extends BaseAdminController
return fail($e->getMessage());
}
}
+
+ /**
+ * 重新识别占位符
+ * @param int $id
+ * @return \think\Response
+ */
+ public function reidentify(int $id)
+ {
+ try {
+ $result = (new DocumentTemplateService())->reidentifyPlaceholders($id);
+ return success('重新识别成功', $result);
+ } catch (\Exception $e) {
+ return fail($e->getMessage());
+ }
+ }
+
+ /**
+ * 更新模板Word文档
+ * @param int $id
+ * @return \think\Response
+ */
+ public function updateFile(int $id)
+ {
+ try {
+ // 获取上传的文件
+ $file = Request::file('file');
+ if (!$file) {
+ return fail('请选择要上传的Word文档');
+ }
+
+ $result = (new DocumentTemplateService())->updateTemplateFile($id, $file);
+ return success('文档更新成功', $result);
+ } catch (\Exception $e) {
+ return fail($e->getMessage());
+ }
+ }
}
\ No newline at end of file
diff --git a/niucloud/app/adminapi/route/document_template.php b/niucloud/app/adminapi/route/document_template.php
index 131d7f10..75a3ba73 100644
--- a/niucloud/app/adminapi/route/document_template.php
+++ b/niucloud/app/adminapi/route/document_template.php
@@ -34,6 +34,8 @@ Route::group('document_template', function () {
Route::post('config/save', 'document.DocumentTemplate/savePlaceholderConfig');
Route::post('config/datasource/save', 'document.DocumentTemplate/saveDataSourceConfig');
Route::get('datasources', 'document.DocumentTemplate/getDataSources');
+ Route::post('reidentify/:id', 'document.DocumentTemplate/reidentify');
+ Route::post('update_file/:id', 'document.DocumentTemplate/updateFile');
// 文档生成
Route::post('generate', 'document.DocumentTemplate/generateDocument');
diff --git a/niucloud/app/api/controller/apiController/Course.php b/niucloud/app/api/controller/apiController/Course.php
index 68fe4504..eaa0a2d3 100644
--- a/niucloud/app/api/controller/apiController/Course.php
+++ b/niucloud/app/api/controller/apiController/Course.php
@@ -104,6 +104,7 @@ class Course extends BaseApiService
public function addSchedule(Request $request){
$data = $this->request->params([
["resources_id",''],
+ ["student_id",''],
["person_type",''],
["schedule_id",''],
["course_date",''],
diff --git a/niucloud/app/api/controller/apiController/PersonCourseSchedule.php b/niucloud/app/api/controller/apiController/PersonCourseSchedule.php
index a402f265..01203298 100644
--- a/niucloud/app/api/controller/apiController/PersonCourseSchedule.php
+++ b/niucloud/app/api/controller/apiController/PersonCourseSchedule.php
@@ -160,6 +160,7 @@ class PersonCourseSchedule extends BaseApiService
public function getStudentCourseInfo(Request $request){
$resource_id = $request->param('resource_id', '');//客户资源ID
$member_id = $request->param('member_id', '');//会员ID
+ $student_id = $request->param('student_id', '');//学生ID
if (empty($resource_id)) {
return fail('缺少参数resource_id');
@@ -168,6 +169,7 @@ class PersonCourseSchedule extends BaseApiService
$where = [
'resource_id' => $resource_id,
'member_id' => $member_id,
+ 'student_id' => $student_id,
];
$res = (new PersonCourseScheduleService())->getStudentCourseInfo($where);
diff --git a/niucloud/app/api/route/student.php b/niucloud/app/api/route/student.php
index af0b2315..fe4c6622 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('download/:contract_id', 'app\api\controller\student\ContractController@downloadContract');
// 获取学员基本信息
Route::get('student-info', 'app\api\controller\student\ContractController@getStudentInfo');
-});
+})->middleware(['ApiCheckToken']);
// 知识库(测试版本,无需token)
Route::group('knowledge-test', function () {
diff --git a/niucloud/app/model/document/DocumentDataSourceConfig.php b/niucloud/app/model/document/DocumentDataSourceConfig.php
index 2295ef0f..2ee049e7 100644
--- a/niucloud/app/model/document/DocumentDataSourceConfig.php
+++ b/niucloud/app/model/document/DocumentDataSourceConfig.php
@@ -11,7 +11,7 @@ use app\model\contract\Contract;
class DocumentDataSourceConfig extends BaseModel
{
protected $pk = 'id';
- protected $name = 'school_document_data_source_config';
+ protected $name = 'document_data_source_config';
/**
* 关联合同表
diff --git a/niucloud/app/service/admin/course_schedule/CourseScheduleService.php b/niucloud/app/service/admin/course_schedule/CourseScheduleService.php
index ec212ec9..848652c5 100644
--- a/niucloud/app/service/admin/course_schedule/CourseScheduleService.php
+++ b/niucloud/app/service/admin/course_schedule/CourseScheduleService.php
@@ -328,8 +328,10 @@ class CourseScheduleService extends BaseAdminService
public function addSchedule(array $data){
$CourseSchedule = new CourseSchedule();
$personCourseSchedule = new PersonCourseSchedule();
+ dd($data);
if($personCourseSchedule->where([
'resources_id' => $data['resources_id'],
+ 'student_id' => $data['student_id'],
'schedule_id' => $data['schedule_id']
])->find()){
return fail("重复添加");
@@ -337,6 +339,7 @@ class CourseScheduleService extends BaseAdminService
$personCourseSchedule->insert([
'resources_id' => $data['resources_id'],
+ 'student_id' => $data['student_id'],
'person_id' => 1,
'person_type' => $data['person_type'],
'schedule_id' => $data['schedule_id'],
diff --git a/niucloud/app/service/admin/document/DocumentTemplateService.php b/niucloud/app/service/admin/document/DocumentTemplateService.php
index 5a70de3e..b71cc546 100644
--- a/niucloud/app/service/admin/document/DocumentTemplateService.php
+++ b/niucloud/app/service/admin/document/DocumentTemplateService.php
@@ -454,7 +454,6 @@ class DocumentTemplateService extends BaseAdminService
'table_name' => $settings['table_name'] ?? '',
'field_name' => $settings['field_name'] ?? '',
'system_function' => $settings['system_function'] ?? '',
- 'user_input_value' => $settings['user_input_value'] ?? '',
'field_type' => $settings['field_type'] ?? 'text',
'is_required' => $settings['is_required'] ?? 0,
'default_value' => $settings['default_value'] ?? '',
@@ -961,4 +960,223 @@ class DocumentTemplateService extends BaseAdminService
return $template->save();
}
+
+ /**
+ * 重新识别占位符
+ * @param int $id 模板ID
+ * @return array
+ * @throws \Exception
+ */
+ public function reidentifyPlaceholders(int $id)
+ {
+ $template = $this->contractModel->find($id);
+ if (!$template) {
+ throw new \Exception('模板不存在');
+ }
+
+ // 检查模板文件路径是否存在
+ if (empty($template['contract_template'])) {
+ throw new \Exception('模板未上传Word文档,无法识别占位符');
+ }
+
+ // 检查模板文件是否存在
+ $templatePath = public_path() . '/upload/' . $template['contract_template'];
+ if (!file_exists($templatePath)) {
+ throw new \Exception('模板文件不存在:' . $template['contract_template']);
+ }
+
+ // 检查是否为文件而不是目录
+ if (is_dir($templatePath)) {
+ throw new \Exception('模板路径指向的是目录而不是文件:' . $template['contract_template']);
+ }
+
+ try {
+ // 重新解析Word文档内容和占位符
+ $parseResult = $this->parseWordTemplate($templatePath);
+
+ // 更新数据库中的占位符列表
+ $template->placeholders = json_encode($parseResult['placeholders']);
+ $template->contract_content = $parseResult['content'];
+ $template->updated_at = date('Y-m-d H:i:s');
+ $template->save();
+
+ // 获取现有的占位符配置
+ $existingConfig = [];
+ if ($template['placeholder_config']) {
+ $existingConfig = json_decode($template['placeholder_config'], true) ?: [];
+ }
+
+ // 为新的占位符创建默认配置,保留现有配置
+ $newConfig = [];
+ foreach ($parseResult['placeholders'] as $placeholder) {
+ if (isset($existingConfig[$placeholder])) {
+ // 保留现有配置
+ $newConfig[$placeholder] = $existingConfig[$placeholder];
+ } else {
+ // 为新占位符创建默认配置
+ $newConfig[$placeholder] = [
+ 'data_type' => 'user_input',
+ 'table_name' => '',
+ 'field_name' => '',
+ 'system_function' => '',
+ 'user_input_value' => '',
+ 'field_type' => 'text',
+ 'is_required' => 0,
+ 'default_value' => ''
+ ];
+ }
+ }
+
+ // 更新占位符配置
+ if (!empty($newConfig)) {
+ $template->placeholder_config = json_encode($newConfig);
+ $template->save();
+
+ // 同步更新数据源配置表
+ $this->saveConfigToDataSourceTable($id, $newConfig);
+ }
+
+ return [
+ 'placeholders' => $parseResult['placeholders'],
+ 'placeholder_count' => count($parseResult['placeholders']),
+ 'new_placeholders' => array_diff($parseResult['placeholders'], array_keys($existingConfig)),
+ 'removed_placeholders' => array_diff(array_keys($existingConfig), $parseResult['placeholders'])
+ ];
+
+ } catch (\Exception $e) {
+ Log::error('重新识别占位符失败:' . $e->getMessage());
+ throw new \Exception('重新识别占位符失败:' . $e->getMessage());
+ }
+ }
+
+ /**
+ * 更新模板Word文档
+ * @param int $id 模板ID
+ * @param $file 上传的文件
+ * @return array
+ * @throws \Exception
+ */
+ public function updateTemplateFile(int $id, $file)
+ {
+ $template = $this->contractModel->find($id);
+ if (!$template) {
+ throw new \Exception('模板不存在');
+ }
+
+ // 验证文件类型
+ $allowedTypes = ['docx', 'doc'];
+ $extension = strtolower($file->getOriginalExtension());
+
+ if (!in_array($extension, $allowedTypes)) {
+ throw new \Exception('只支持 .docx 和 .doc 格式的Word文档');
+ }
+
+ // 获取文件信息
+ $fileSize = $file->getSize();
+ $realPath = $file->getRealPath();
+
+ // 验证文件大小 (ORM大10MB)
+ $maxSize = 10 * 1024 * 1024;
+ if ($fileSize > $maxSize) {
+ throw new \Exception('文件大小不能超过10MB');
+ }
+
+ // 生成文件hash防重复
+ $fileHash = md5_file($realPath);
+
+ try {
+ // 删除旧文件
+ if ($template['contract_template']) {
+ $oldFilePath = public_path() . '/upload/' . $template['contract_template'];
+ if (file_exists($oldFilePath) && is_file($oldFilePath)) {
+ unlink($oldFilePath);
+ }
+ }
+
+ // 生成保存路径
+ $uploadDir = 'contract_templates/' . date('Ymd');
+ $uploadPath = public_path() . '/upload/' . $uploadDir;
+
+ // 确保目录存在
+ if (!is_dir($uploadPath)) {
+ mkdir($uploadPath, 0777, true);
+ }
+
+ // 生成文件名
+ $fileName = md5(time() . $file->getOriginalName()) . '.' . $extension;
+ $fullPath = $uploadPath . '/' . $fileName;
+ $savePath = $uploadDir . '/' . $fileName;
+
+ // 移动文件到目标位置
+ if (!move_uploaded_file($realPath, $fullPath)) {
+ throw new \Exception('文件保存失败');
+ }
+
+ // 解析Word文档内容和占位符
+ $parseResult = $this->parseWordTemplate($fullPath);
+
+ // 更新数据库记录
+ $template->contract_template = $savePath;
+ $template->contract_content = $parseResult['content'];
+ $template->original_filename = $file->getOriginalName();
+ $template->file_size = $fileSize;
+ $template->file_hash = $fileHash;
+ $template->placeholders = json_encode($parseResult['placeholders']);
+ $template->updated_at = date('Y-m-d H:i:s');
+ $template->save();
+
+ // 获取现有的占位符配置
+ $existingConfig = [];
+ if ($template['placeholder_config']) {
+ $existingConfig = json_decode($template['placeholder_config'], true) ?: [];
+ }
+
+ // 为新的占位符创建默认配置,保留现有配置
+ $newConfig = [];
+ foreach ($parseResult['placeholders'] as $placeholder) {
+ if (isset($existingConfig[$placeholder])) {
+ // 保留现有配置
+ $newConfig[$placeholder] = $existingConfig[$placeholder];
+ } else {
+ // 为新占位符创建默认配置
+ $newConfig[$placeholder] = [
+ 'data_type' => 'user_input',
+ 'table_name' => '',
+ 'field_name' => '',
+ 'system_function' => '',
+ 'user_input_value' => '',
+ 'field_type' => 'text',
+ 'is_required' => 0,
+ 'default_value' => ''
+ ];
+ }
+ }
+
+ // 更新占位符配置
+ if (!empty($newConfig)) {
+ $template->placeholder_config = json_encode($newConfig);
+ $template->save();
+
+ // 同步更新数据源配置表
+ $this->saveConfigToDataSourceTable($id, $newConfig);
+ }
+
+ return [
+ 'id' => $template->id,
+ 'template_name' => $template->contract_name,
+ 'file_path' => $savePath,
+ 'placeholders' => $parseResult['placeholders'],
+ 'placeholder_count' => count($parseResult['placeholders']),
+ 'new_placeholders' => array_diff($parseResult['placeholders'], array_keys($existingConfig)),
+ 'removed_placeholders' => array_diff(array_keys($existingConfig), $parseResult['placeholders'])
+ ];
+
+ } catch (\Exception $e) {
+ // 如果保存失败,删除已上传的文件
+ if (isset($fullPath) && file_exists($fullPath)) {
+ unlink($fullPath);
+ }
+ throw new \Exception('模板文档更新失败:' . $e->getMessage());
+ }
+ }
}
\ No newline at end of file
diff --git a/niucloud/app/service/api/apiService/CourseService.php b/niucloud/app/service/api/apiService/CourseService.php
index e017ed0f..0792edba 100644
--- a/niucloud/app/service/api/apiService/CourseService.php
+++ b/niucloud/app/service/api/apiService/CourseService.php
@@ -431,82 +431,55 @@ class CourseService extends BaseApiService
public function addSchedule(array $data){
$CourseSchedule = new CourseSchedule();
$personCourseSchedule = new PersonCourseSchedule();
-
- // 根据person_type确定正确的student_id和resources_id
- $student_id = 0;
- $resources_id = 0;
-
- if ($data['person_type'] == 'student') {
- // 如果是学员类型,从传入的数据中获取student_id,然后查询对应的resources_id
- $student_id = $data['resources_id']; // 前端传来的是student.id
-
- // 通过student表查询对应的user_id(即customer_resources.id)
- $Student = new Student();
- $student = $Student->where('id', $student_id)->find();
- if (!$student) {
- return fail("学员不存在");
- }
- $resources_id = $student['user_id']; // student.user_id = customer_resources.id
-
- } else if ($data['person_type'] == 'customer_resource') {
- // 如果是客户资源类型,直接使用传入的resources_id
- $resources_id = $data['resources_id'];
-
- // 验证客户资源是否存在
- $customerResource = Db::name('customer_resources')
- ->where('id', $resources_id)
- ->find();
-
- if (!$customerResource) {
- return fail("客户资源不存在");
- }
-
- // 通过customer_resources.id查找对应的学生记录
- // school_student.user_id = school_customer_resources.id
- $Student = new Student();
- $student = $Student->where('user_id', $resources_id)->find();
-
- if (!$student) {
- return fail("该客户资源没有关联的学生记录");
- }
-
- $student_id = $student['id'];
- } else {
- return fail("无效的人员类型");
+ $student = Student::where('id', $data['student_id'])->find();
+ if (!$student) {
+ return fail("学员不存在");
}
-
- // 检查重复添加 - 根据person_type使用不同的检查逻辑
- $checkWhere = ['schedule_id' => $data['schedule_id']];
- if ($data['person_type'] == 'student') {
- $checkWhere['student_id'] = $student_id;
- } else {
- $checkWhere['resources_id'] = $resources_id;
+
+ $studentCourse = StudentCourses::where('student_id', $student->id)
+ ->order('id', 'desc')
+ ->find();
+ if ($studentCourse){
+ $person_type = 'student';
+ }else{
+ $person_type = 'customer_resource';
}
-
- if($personCourseSchedule->where($checkWhere)->find()){
- return fail("重复添加");
+
+ // 检查重复添加 - 根据person_type使用不同的检查逻辑
+ $checkWhere = [
+ 'schedule_id' => $data['schedule_id'],
+ 'student_id'=>$student->id,
+ 'resources_id'=>$data['resources_id'],
+ ];
+ $course = $personCourseSchedule->where($checkWhere)->find();
+ if($course){
+ if ($course->schedule_type == 2 && $course->course_type == 1) {
+ $course->schedule_type == 1;
+ $course->save();
+ }else{
+ return fail("重复添加");
+ }
}
- // 调试:插入前记录变量值
- error_log("Debug: Before insert - student_id = " . $student_id . ", resources_id = " . $resources_id);
-
$insertData = [
- 'student_id' => $student_id, // 正确设置student_id
- 'resources_id' => $resources_id, // 正确设置resources_id
+ 'student_id' => $student->id, // 正确设置student_id
+ 'resources_id' => $data['resources_id'], // 正确设置resources_id
'person_id' => $this->member_id,
- 'person_type' => $data['person_type'],
+ 'person_type' => $person_type,
'schedule_id' => $data['schedule_id'],
'course_date' => $data['course_date'],
'time_slot' => $data['time_slot'],
'schedule_type' => $data['schedule_type'] ?? 1, // 1=正式位, 2=等待位
- 'course_type' => $data['course_type'] ?? 1, // 1=正式课, 2=体验课, 3=等待位
+ 'course_type' => empty($course) ? 2 : 1, // 1=正式学员, 2=体验课学员
'remark' => $data['remark'] ?? '' // 备注
];
-
- error_log("Debug: Insert data = " . json_encode($insertData));
-
+
$personCourseSchedule->insert($insertData);
- $CourseSchedule->where(['id' => $data['schedule_id']])->dec("available_capacity")->update();
+ $student_ids = $personCourseSchedule->where(['schedule_id' => $data['schedule_id']])->column('student_id');
+ $CourseSchedule->where(['id' => $data['schedule_id']])->update([
+ 'student_ids'=>$student_ids,
+ 'available_capacity'=>count($student_ids)
+ ]);
return success("添加成功");
}
@@ -543,7 +516,8 @@ class CourseService extends BaseApiService
// 查询记录
$record = $personCourseSchedule->where([
'schedule_id' => $data['id'],
- 'resources_id' => $data['resources_id']
+ 'resources_id' => $data['resources_id'],
+
])->find();
if (!$record) {
diff --git a/niucloud/app/service/api/apiService/PersonCourseScheduleService.php b/niucloud/app/service/api/apiService/PersonCourseScheduleService.php
index 0e9693cc..e84917d5 100644
--- a/niucloud/app/service/api/apiService/PersonCourseScheduleService.php
+++ b/niucloud/app/service/api/apiService/PersonCourseScheduleService.php
@@ -366,9 +366,15 @@ class PersonCourseScheduleService extends BaseApiService
'data' => []
];
- // 直接通过resource_id查询学员课程表
- $studentCourses = StudentCourses::where('resource_id', $where['resource_id'])
- ->with([
+ // 构建查询条件
+ $query = StudentCourses::where('resource_id', $where['resource_id']);
+
+ // 如果传入了student_id参数,添加student_id条件
+ if (!empty($where['student_id'])) {
+ $query = $query->where('student_id', $where['student_id']);
+ }
+
+ $studentCourses = $query->with([
'course' => function($query) {
$query->field('id,course_name');
},
diff --git a/niucloud/app/service/api/apiService/StudentService.php b/niucloud/app/service/api/apiService/StudentService.php
index c0d981d7..2b54ed61 100644
--- a/niucloud/app/service/api/apiService/StudentService.php
+++ b/niucloud/app/service/api/apiService/StudentService.php
@@ -95,18 +95,8 @@ class StudentService extends BaseApiService
try {
// 获取当前登录人员的ID
$currentUserId = $this->getUserId();
-
- if (empty($currentUserId)) {
- $res['code'] = 1;
- $res['data'] = [];
- $res['msg'] = '获取成功';
- return $res;
- }
- // 查询符合条件的学生ID集合
- $studentIds = $this->getStudentIds($currentUserId, $data);
-
- if (empty($studentIds)) {
+ if (empty($currentUserId)) {
$res['code'] = 1;
$res['data'] = [];
$res['msg'] = '获取成功';
@@ -116,9 +106,12 @@ class StudentService extends BaseApiService
// 构建学员基础查询条件
$where = [];
$where[] = ['s.deleted_at', '=', 0];
- $where[] = ['s.id', 'in', $studentIds];
// 支持搜索参数
+ if (!empty($data['parent_resource_id'])){
+ $where[] = ['s.user_id', '=', $data['parent_resource_id']];
+ }
+
if (!empty($data['name'])) {
$where[] = ['s.name', 'like', '%' . $data['name'] . '%'];
}
diff --git a/uniapp/App.vue b/uniapp/App.vue
index 27a1b37b..a9eb0f14 100644
--- a/uniapp/App.vue
+++ b/uniapp/App.vue
@@ -8,6 +8,9 @@
var wxtoken = new WxToken();
export default {
async onLaunch() {
+ // 检查小程序更新
+ this.checkForUpdate();
+
// #ifdef MP-WEIXIN
uni.login({
provider: 'weixin',
@@ -36,8 +39,206 @@
//微信公众号获取token -必须是认证的服务号
// #endif
},
- onShow: function() {},
+ onShow: function() {
+ // 应用切换到前台时再次检查更新
+ this.checkForUpdate();
+ },
onHide: function() {},
+
+ methods: {
+ /**
+ * 检查小程序更新
+ */
+ 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
+ });
+ }
+ });
+
+ // 监听小程序有版本更新事件
+ updateManager.onUpdateReady(() => {
+ console.log('新版本下载完成,准备重启应用');
+ this.showUpdateDialog();
+ });
+
+ // 监听小程序更新失败事件
+ updateManager.onUpdateFailed(() => {
+ console.log('新版本下载失败');
+ uni.showToast({
+ title: '更新失败,请检查网络',
+ icon: 'none',
+ duration: 2000
+ });
+ });
+
+ // 主动检查更新
+ updateManager.checkForUpdate();
+ } catch (error) {
+ console.error('更新管理器初始化失败:', error);
+ }
+ // #endif
+
+ // #ifdef H5
+ // H5环境下的更新检查(可选)
+ this.checkH5Update();
+ // #endif
+ },
+
+ /**
+ * 显示更新对话框
+ */
+ showUpdateDialog() {
+ uni.showModal({
+ title: '更新提示',
+ content: '新版本已下载完成,是否立即重启应用以更新到最新版本?',
+ confirmText: '立即重启',
+ cancelText: '稍后重启',
+ success: (res) => {
+ if (res.confirm) {
+ console.log('用户选择立即重启');
+ this.applyUpdate();
+ } else {
+ console.log('用户选择稍后重启');
+ // 可以设置定时器在一定时间后自动重启
+ this.scheduleDelayedUpdate();
+ }
+ }
+ });
+ },
+
+ /**
+ * 应用更新并重启
+ */
+ applyUpdate() {
+ // #ifdef MP-WEIXIN
+ try {
+ const updateManager = uni.getUpdateManager();
+ updateManager.applyUpdate();
+ } catch (error) {
+ console.error('应用更新失败:', error);
+ uni.showToast({
+ title: '重启失败,请手动重启',
+ icon: 'none',
+ duration: 2000
+ });
+ }
+ // #endif
+ },
+
+ /**
+ * 安排延迟更新
+ */
+ scheduleDelayedUpdate() {
+ // 5分钟后自动重启
+ setTimeout(() => {
+ console.log('自动应用更新');
+ this.applyUpdate();
+ }, 5 * 60 * 1000);
+
+ uni.showToast({
+ title: '将在5分钟后自动重启',
+ icon: 'none',
+ duration: 3000
+ });
+ },
+
+ /**
+ * H5环境下的更新检查(可选功能)
+ */
+ checkH5Update() {
+ // #ifdef H5
+ try {
+ // 检查页面缓存版本
+ const currentVersion = uni.getStorageSync('app_version') || '1.0.0';
+ const buildTime = uni.getStorageSync('build_time') || Date.now();
+ const now = Date.now();
+
+ // 每小时检查一次更新
+ if (now - buildTime > 60 * 60 * 1000) {
+ console.log('H5环境检查更新');
+ // 这里可以调用API检查是否有新版本
+ // 如果有新版本,可以提示用户刷新页面
+ this.checkVersionFromServer();
+ }
+ } catch (error) {
+ console.error('H5更新检查失败:', error);
+ }
+ // #endif
+ },
+
+ /**
+ * 从服务器检查版本信息(H5环境)
+ */
+ async checkVersionFromServer() {
+ try {
+ // 这里应该调用你的API来获取最新版本信息
+ // const response = await uni.request({
+ // url: Api_url.domain + '/api/app/version',
+ // method: 'GET'
+ // });
+ //
+ // if (response.data.code === 1) {
+ // const serverVersion = response.data.data.version;
+ // const currentVersion = uni.getStorageSync('app_version') || '1.0.0';
+ //
+ // if (this.compareVersion(serverVersion, currentVersion) > 0) {
+ // this.showH5UpdateDialog();
+ // }
+ // }
+
+ console.log('从服务器检查H5版本更新');
+ } catch (error) {
+ console.error('服务器版本检查失败:', error);
+ }
+ },
+
+ /**
+ * 显示H5更新对话框
+ */
+ showH5UpdateDialog() {
+ uni.showModal({
+ title: '发现新版本',
+ content: '检测到新版本,建议刷新页面以获得最佳体验',
+ confirmText: '立即刷新',
+ cancelText: '稍后刷新',
+ success: (res) => {
+ if (res.confirm) {
+ location.reload();
+ }
+ }
+ });
+ },
+
+ /**
+ * 版本号比较工具
+ */
+ compareVersion(version1, version2) {
+ const v1 = version1.split('.').map(Number);
+ const v2 = version2.split('.').map(Number);
+
+ for (let i = 0; i < Math.max(v1.length, v2.length); i++) {
+ const a = v1[i] || 0;
+ const b = v2[i] || 0;
+
+ if (a > b) return 1;
+ if (a < b) return -1;
+ }
+
+ return 0;
+ }
+ }
}
diff --git a/uniapp/components/fitness-record-popup/fitness-record-popup.vue b/uniapp/components/fitness-record-popup/fitness-record-popup.vue
index ea4049e8..492807c7 100644
--- a/uniapp/components/fitness-record-popup/fitness-record-popup.vue
+++ b/uniapp/components/fitness-record-popup/fitness-record-popup.vue
@@ -227,80 +227,147 @@ export default {
// 选择PDF文件
selectPDFFiles() {
- uni.chooseFile({
+ // 动态检测并选择合适的文件选择API
+ let chooseFileMethod = null;
+
+ // #ifdef MP-WEIXIN
+ // 微信小程序优先使用 chooseMessageFile
+ if (typeof uni.chooseMessageFile === 'function') {
+ chooseFileMethod = uni.chooseMessageFile;
+ } else if (typeof wx !== 'undefined' && typeof wx.chooseMessageFile === 'function') {
+ chooseFileMethod = wx.chooseMessageFile;
+ }
+ // #endif
+
+ // #ifndef MP-WEIXIN
+ // 其他平台使用 chooseFile
+ if (typeof uni.chooseFile === 'function') {
+ chooseFileMethod = uni.chooseFile;
+ }
+ // #endif
+
+ // 如果没有找到合适的方法,显示错误提示
+ if (!chooseFileMethod) {
+ uni.showModal({
+ title: '不支持文件选择',
+ content: '当前平台不支持文件选择功能,请手动输入文件信息或联系技术支持',
+ showCancel: false
+ });
+ return;
+ }
+
+ // 调用选择的文件选择方法
+ chooseFileMethod({
count: 5,
type: 'file',
extension: ['pdf'],
success: async (res) => {
console.log('选择的文件:', res.tempFiles)
+ await this.handleSelectedFiles(res.tempFiles)
+ },
+ fail: (err) => {
+ console.error('选择文件失败:', err)
+ // 提供更详细的错误信息
+ let errorMsg = '选择文件失败';
+ if (err.errMsg) {
+ if (err.errMsg.includes('cancel')) {
+ errorMsg = '用户取消了文件选择';
+ } else if (err.errMsg.includes('limit')) {
+ errorMsg = '文件数量或大小超出限制';
+ } else {
+ errorMsg = '选择文件失败: ' + err.errMsg;
+ }
+ }
- // 显示上传进度
- uni.showLoading({
- title: '上传中...',
- mask: true
+ uni.showToast({
+ title: errorMsg,
+ icon: 'none',
+ duration: 3000
})
-
- let successCount = 0
- let totalCount = res.tempFiles.length
-
- for (let file of res.tempFiles) {
- if (file.type === 'application/pdf') {
- try {
- // 立即上传PDF文件到服务器(使用通用文档上传接口)
- const uploadResult = await this.uploadPdfFile(file)
- if (uploadResult && uploadResult.code === 1) {
- const pdfFile = {
- id: Date.now() + Math.random(),
- name: file.name,
- size: file.size,
- url: uploadResult.data.url, // 使用标准响应中的url字段
- server_path: uploadResult.data.url, // 服务器可访问路径
- upload_time: new Date().toLocaleString(),
- ext: uploadResult.data.ext || 'pdf',
- original_name: uploadResult.data.name || file.name
- }
- this.recordData.pdf_files.push(pdfFile)
- successCount++
- } else {
- console.error('文件上传失败:', uploadResult)
- uni.showToast({
- title: uploadResult.msg || '文件上传失败',
- icon: 'none'
- })
- }
- } catch (error) {
- console.error('上传PDF文件失败:', error)
- uni.showToast({
- title: '文件上传失败: ' + (error.msg || error.message || '网络异常'),
- icon: 'none'
- })
+ }
+ })
+ },
+
+ // 处理选择的文件(提取公共逻辑)
+ async handleSelectedFiles(tempFiles) {
+ // 显示上传进度
+ uni.showLoading({
+ title: '上传中...',
+ mask: true
+ })
+
+ let successCount = 0
+ let totalCount = tempFiles.length
+
+ for (let file of tempFiles) {
+ // 检查文件类型和扩展名
+ const isValidPDF = this.isValidPDFFile(file)
+
+ if (isValidPDF) {
+ try {
+ // 立即上传PDF文件到服务器(使用通用文档上传接口)
+ const uploadResult = await this.uploadPdfFile(file)
+ if (uploadResult && uploadResult.code === 1) {
+ const pdfFile = {
+ id: Date.now() + Math.random(),
+ name: file.name,
+ size: file.size,
+ url: uploadResult.data.url, // 使用标准响应中的url字段
+ server_path: uploadResult.data.url, // 服务器可访问路径
+ upload_time: new Date().toLocaleString(),
+ ext: uploadResult.data.ext || 'pdf',
+ original_name: uploadResult.data.name || file.name
}
+ this.recordData.pdf_files.push(pdfFile)
+ successCount++
} else {
+ console.error('文件上传失败:', uploadResult)
uni.showToast({
- title: '请选择PDF格式文件',
+ title: uploadResult.msg || '文件上传失败',
icon: 'none'
})
}
- }
-
- uni.hideLoading()
-
- // 显示上传结果
- if (successCount > 0) {
+ } catch (error) {
+ console.error('上传PDF文件失败:', error)
uni.showToast({
- title: `成功上传 ${successCount}/${totalCount} 个文件`,
- icon: 'success'
+ title: '文件上传失败: ' + (error.msg || error.message || '网络异常'),
+ icon: 'none'
})
}
- },
- fail: (err) => {
- console.error('选择文件失败:', err)
+ } else {
uni.showToast({
- title: '选择文件失败',
+ title: '请选择PDF格式文件',
icon: 'none'
})
}
- })
+ }
+
+ uni.hideLoading()
+
+ // 显示上传结果
+ if (successCount > 0) {
+ uni.showToast({
+ title: `成功上传 ${successCount}/${totalCount} 个文件`,
+ icon: 'success'
+ })
+ }
+ },
+
+ // 验证文件是否为有效的PDF文件
+ isValidPDFFile(file) {
+ // 检查MIME类型
+ if (file.type && file.type === 'application/pdf') {
+ return true
+ }
+
+ // 检查文件扩展名(作为备用验证)
+ const fileName = file.name || ''
+ const extension = fileName.toLowerCase().split('.').pop()
+ if (extension === 'pdf') {
+ return true
+ }
+
+ return false
},
// 上传PDF文件到服务器(使用通用文档上传接口)
diff --git a/uniapp/components/student-edit-popup/student-edit-popup.vue b/uniapp/components/student-edit-popup/student-edit-popup.vue
index f782b5b9..5de1a87b 100644
--- a/uniapp/components/student-edit-popup/student-edit-popup.vue
+++ b/uniapp/components/student-edit-popup/student-edit-popup.vue
@@ -220,10 +220,21 @@ export default {
},
methods: {
// 打开弹窗 - 新增模式
- openAdd() {
+ openAdd(clientData = null) {
this.isEditing = false
this.resetStudentData()
this.studentData.user_id = this.resourceId
+
+ // 如果传递了客户信息,自动填充相关字段
+ if (clientData) {
+ if (clientData.contact_phone) {
+ this.studentData.contact_phone = clientData.contact_phone
+ }
+ if (clientData.emergency_contact) {
+ this.studentData.emergency_contact = clientData.emergency_contact
+ }
+ }
+
this.$refs.popup.open()
},
diff --git a/uniapp/pages-coach/coach/student/student_list.vue b/uniapp/pages-coach/coach/student/student_list.vue
index ef3b04f2..b46f81ed 100644
--- a/uniapp/pages-coach/coach/student/student_list.vue
+++ b/uniapp/pages-coach/coach/student/student_list.vue
@@ -306,11 +306,13 @@ import apiRoute from '@/api/apiRoute.js';
background-color: #18181c;
display: flex;
flex-direction: column;
+ min-height: 100vh;
}
.safe-area {
padding-top: var(--status-bar-height);
padding-bottom: 120rpx;
+ flex: 1;
}
.search-bar {
@@ -335,6 +337,7 @@ import apiRoute from '@/api/apiRoute.js';
.content {
padding: 20rpx;
+ background-color: #18181c;
}
.empty-box {
diff --git a/uniapp/pages-common/contract/contract_sign.vue b/uniapp/pages-common/contract/contract_sign.vue
index 5e6538f9..fce59bcc 100644
--- a/uniapp/pages-common/contract/contract_sign.vue
+++ b/uniapp/pages-common/contract/contract_sign.vue
@@ -112,7 +112,7 @@
@@ -874,6 +925,44 @@
display: flex;
gap: 16rpx;
justify-content: flex-end;
+
+ .action_button {
+ padding: 16rpx 32rpx;
+ border-radius: 8rpx;
+ font-size: 24rpx;
+ text-align: center;
+ color: #fff;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ border: none;
+
+ &.secondary_button {
+ background: #f8f9fa;
+ color: #666;
+
+ &:active {
+ background: #e9ecef;
+ }
+ }
+
+ &.primary_button {
+ background: #29D3B4;
+ color: #fff;
+
+ &:active {
+ background: #26c6a0;
+ }
+ }
+
+ &.sign_button {
+ background: #ff6b35;
+ color: #fff;
+
+ &:active {
+ background: #e55a2b;
+ }
+ }
+ }
}
}
}
@@ -882,6 +971,27 @@
// 加载更多
.load_more_section {
padding: 40rpx 20rpx 80rpx;
+
+ .load_more_button {
+ padding: 24rpx 32rpx;
+ background: transparent;
+ color: #666;
+ font-size: 28rpx;
+ text-align: center;
+ border: 1rpx solid #e0e0e0;
+ border-radius: 8rpx;
+ cursor: pointer;
+ transition: all 0.3s ease;
+
+ &:active {
+ background: #f5f5f5;
+ }
+
+ &.loading {
+ color: #999;
+ cursor: not-allowed;
+ }
+ }
}
// 合同详情弹窗
@@ -979,8 +1089,33 @@
gap: 16rpx;
border-top: 1px solid #f0f0f0;
- fui-button {
+ .popup_button {
flex: 1;
+ padding: 16rpx 24rpx;
+ border-radius: 8rpx;
+ font-size: 28rpx;
+ text-align: center;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ border: none;
+
+ &.primary_popup_button {
+ background: #3498db;
+ color: #fff;
+
+ &:active {
+ background: #2980b9;
+ }
+ }
+
+ &.secondary_popup_button {
+ background: #f8f9fa;
+ color: #666;
+
+ &:active {
+ background: #e9ecef;
+ }
+ }
}
}
}
diff --git a/uniapp/pages/student/profile/index.vue b/uniapp/pages/student/profile/index.vue
index 04ca56ed..d14a36fc 100644
--- a/uniapp/pages/student/profile/index.vue
+++ b/uniapp/pages/student/profile/index.vue
@@ -60,7 +60,8 @@