Browse Source

修改 bug

master
王泽彦 8 months ago
parent
commit
f014221319
  1. 62
      admin/src/app/views/dict/components/dict.vue
  2. 95
      niucloud/app/adminapi/controller/service_logs/ServiceLogsDistribution.php
  3. 14
      niucloud/app/adminapi/route/service_logs.php
  4. 91
      niucloud/app/api/controller/personnel/WechatBind.php
  5. 9
      niucloud/app/api/route/route.php
  6. 13
      niucloud/app/dict/schedule/schedule.php
  7. 180
      niucloud/app/job/schedule/ServiceLogsDistribution.php
  8. 4
      niucloud/app/job/schedule/TeachingPersonnelSync.php
  9. 349
      niucloud/app/service/admin/service_logs/ServiceLogsDistributionService.php
  10. 512
      niucloud/app/service/api/personnel/WechatBindService.php
  11. 18
      uniapp/api/apiRoute.js
  12. 4
      uniapp/common/config.js
  13. 161
      uniapp/pages-common/profile/personal_info.vue
  14. 117
      uniapp/pages-common/webview/wechat_bind.vue
  15. 270
      uniapp/pages-market/my/set_up.vue
  16. 3
      uniapp/pages-student/contracts/sign.vue
  17. 9
      uniapp/pages.json

62
admin/src/app/views/dict/components/dict.vue

@ -216,7 +216,67 @@ const setFormData = async (row: any = null) => {
id.value = row.id
name.value = row.name
const data = await (await getDictInfo(row.id)).data
tableDate.value = data.dictionary
// dictionary
try {
if (data.dictionary) {
let parsedData = data.dictionary
// dictionary使
if (Array.isArray(parsedData)) {
tableDate.value = parsedData
}
//
else if (typeof parsedData === 'string') {
//
let firstParse = JSON.parse(parsedData)
//
if (Array.isArray(firstParse)) {
// 使
tableDate.value = firstParse
} else if (typeof firstParse === 'string') {
//
tableDate.value = JSON.parse(firstParse)
} else {
// 使
tableDate.value = []
}
}
// 使
else {
tableDate.value = []
}
//
if (!Array.isArray(tableDate.value)) {
tableDate.value = []
}
//
console.log('原始dictionary:', data.dictionary)
console.log('dictionary类型:', typeof data.dictionary)
console.log('最终tableDate.value:', tableDate.value)
if (typeof data.dictionary === 'string') {
try {
const firstParse = JSON.parse(data.dictionary)
console.log('第一次解析结果:', firstParse)
console.log('第一次解析结果类型:', typeof firstParse)
if (typeof firstParse === 'string') {
console.log('第二次解析结果:', JSON.parse(firstParse))
}
} catch (e) {
console.log('解析测试失败:', e)
}
}
} else {
tableDate.value = []
}
} catch (e) {
console.warn('解析字典数据失败:', e)
tableDate.value = []
}
loading.value = false
}

95
niucloud/app/adminapi/controller/service_logs/ServiceLogsDistribution.php

@ -1,95 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\adminapi\controller\service_logs;
use app\service\admin\service_logs\ServiceLogsDistributionService;
use core\base\BaseAdminController;
/**
* 服务记录分发管理控制器
* Class ServiceLogsDistribution
* @package app\adminapi\controller\service_logs
*/
class ServiceLogsDistribution extends BaseAdminController
{
/**
* 手动执行分发任务
* @return \think\Response
*/
public function executeDistribution()
{
$result = (new ServiceLogsDistributionService())->executeDistribution();
return success($result);
}
/**
* 获取分发统计信息
* @return \think\Response
*/
public function getDistributionStats()
{
$result = (new ServiceLogsDistributionService())->getDistributionStats();
return success($result);
}
/**
* 获取待分发的服务记录列表
* @return \think\Response
*/
public function getPendingDistributionList()
{
$data = $this->request->params([
["distribution_status", ""],
["date_range", ""]
]);
$where = [];
if (!empty($data['distribution_status'])) {
$where['distribution_status'] = $data['distribution_status'];
}
if (!empty($data['date_range'])) {
$where['date_range'] = $data['date_range'];
}
$result = (new ServiceLogsDistributionService())->getPendingDistributionList($where);
return success($result);
}
/**
* 重置分发状态
* @return \think\Response
*/
public function resetDistributionStatus()
{
$data = $this->request->params([
["ids", []],
["type", "both"]
]);
if (empty($data['ids'])) {
return error('请选择要重置的记录');
}
$result = (new ServiceLogsDistributionService())->resetDistributionStatus($data['ids'], $data['type']);
return success($result);
}
/**
* 获取教务和教练人员列表
* @return \think\Response
*/
public function getStaffList()
{
$result = (new ServiceLogsDistributionService())->getStaffList();
return success($result);
}
}

14
niucloud/app/adminapi/route/service_logs.php

@ -23,20 +23,6 @@ Route::group('service_logs', function () {
Route::get('service_logs', 'service_logs.ServiceLogs/lists');
// 服务记录分发管理
Route::group('distribution', function () {
// 手动执行分发任务
Route::post('execute', 'service_logs.ServiceLogsDistribution/executeDistribution');
// 获取分发统计信息
Route::get('stats', 'service_logs.ServiceLogsDistribution/getDistributionStats');
// 获取待分发的服务记录列表
Route::get('pending', 'service_logs.ServiceLogsDistribution/getPendingDistributionList');
// 重置分发状态
Route::post('reset', 'service_logs.ServiceLogsDistribution/resetDistributionStatus');
// 获取教务和教练人员列表
Route::get('staff', 'service_logs.ServiceLogsDistribution/getStaffList');
});
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,

91
niucloud/app/api/controller/personnel/WechatBind.php

@ -0,0 +1,91 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\api\controller\personnel;
use app\service\api\personnel\WechatBindService;
use core\base\BaseApiController;
/**
* 人员微信绑定控制器
* Class WechatBind
* @package app\api\controller\personnel
*/
class WechatBind extends BaseApiController
{
/**
* 获取微信openid(小程序code换openid)
* @return \think\Response
*/
public function getWechatOpenid()
{
$data = $this->request->params([
['code', ''],
['type', 'miniprogram'] // miniprogram: 小程序, official: 公众号
]);
return success('获取成功', (new WechatBindService())->getWechatOpenid($data['code'], $data['type']));
}
/**
* 绑定微信openid
* @return \think\Response
*/
public function bindWechatOpenid()
{
$data = $this->request->params([
['miniprogram_openid', ''],
['official_openid', ''],
['type', 'miniprogram'] // miniprogram: 小程序绑定, official: 公众号绑定, both: 同时绑定
]);
return success('绑定成功', (new WechatBindService())->bindWechatOpenid($data));
}
/**
* 微信授权跳转
* @return \think\Response
*/
public function wechatAuthorize()
{
$data = $this->request->params([
['redirect_uri', ''],
['state', '']
]);
return (new WechatBindService())->wechatAuthorize($data['redirect_uri'], $data['state']);
}
/**
* 微信授权回调
* @return \think\Response
*/
public function wechatCallback()
{
$data = $this->request->params([
['code', ''],
['state', '']
]);
return (new WechatBindService())->wechatCallback($data['code'], $data['state']);
}
/**
* 获取绑定状态
* @return \think\Response
*/
public function getBindStatus()
{
return success('获取成功', (new WechatBindService())->getBindStatus());
}
}

9
niucloud/app/api/route/route.php

@ -48,6 +48,10 @@ Route::group(function () {
//员工端-添加新员工信息
Route::post('personnel/add', 'apiController.Personnel/add');
//微信公众号授权相关接口(不需要token验证)
Route::get('personnel/wechatAuthorize', 'personnel.WechatBind/wechatAuthorize');
Route::get('personnel/wechatCallback', 'personnel.WechatBind/wechatCallback');
});
/**
@ -464,6 +468,11 @@ Route::group(function () {
Route::get('personnel/serviceLogDetail', 'apiController.Personnel/serviceLogDetail');
Route::post('personnel/updateServiceRemark', 'apiController.Personnel/updateServiceRemark');
//微信绑定相关接口(需要人员token验证)
Route::post('personnel/getWechatOpenid', 'personnel.WechatBind/getWechatOpenid');
Route::post('personnel/bindWechatOpenid', 'personnel.WechatBind/bindWechatOpenid');
Route::get('personnel/getBindStatus', 'personnel.WechatBind/getBindStatus');
Route::get('getQrcode', 'pay.Pay/getQrcode');

13
niucloud/app/dict/schedule/schedule.php

@ -66,4 +66,17 @@ return [
'class' => 'app\job\schedule\TeachingPersonnelSync',
'function' => 'doJob'
],
[
'key' => 'ServiceLogsDistribution',
'name' => '服务分发',
'desc' => '检查服务记录是否分配给正确的用户',
'time' => [
'type' => 'day',
'day' => 1,
'hour' => 2,
'min' => 0
],
'class' => 'app\job\schedule\ServiceLogsDistribution',
'function' => 'doJob'
],
];

180
niucloud/app/job/schedule/ServiceLogsDistribution.php

@ -13,8 +13,10 @@ namespace app\job\schedule;
use app\model\service_logs\ServiceLogs;
use app\model\personnel\Personnel;
use app\service\admin\service_logs\ServiceLogsDistributionService;
use app\service\core\notice\NoticeService;
use core\base\BaseJob;
use think\facade\Db;
use think\facade\Log;
/**
@ -32,8 +34,12 @@ class ServiceLogsDistribution extends BaseJob
Log::write('服务记录分发任务开始 ' . date('Y-m-d H:i:s'));
try {
$this->distributeToAcademicAffairs();
$this->distributeToCoaches();
// 检查并更新人员变更
$this->checkPersonnelChanges();
// 生成新的服务记录
$this->generateNewServiceLogs();
Log::write('服务记录分发任务完成 ' . date('Y-m-d H:i:s'));
} catch (\Exception $e) {
Log::write('服务记录分发任务异常: ' . $e->getMessage());
@ -44,97 +50,54 @@ class ServiceLogsDistribution extends BaseJob
}
/**
* 给教务分发服务记录
* 检查人员变更
*/
private function distributeToAcademicAffairs()
private function checkPersonnelChanges()
{
// 获取需要分发的服务记录(最近24小时内完成的记录)
$yesterday = date('Y-m-d H:i:s', strtotime('-1 day'));
$serviceLogs = ServiceLogs::where('status', 1) // 已完成状态
->where('created_at', '>=', $yesterday)
->where('is_distributed_to_academic', 0) // 未分发给教务
->with(['service', 'staff'])
->select();
if ($serviceLogs->isEmpty()) {
Log::write('没有需要分发给教务的服务记录');
return;
}
// 获取教务人员列表
$academicStaff = Personnel::where('status', Personnel::STATUS_NORMAL)
->where('position', 'like', '%教务%')
->select();
$service = new ServiceLogsDistributionService();
$result = $service->checkAndUpdatePersonnelChanges();
if ($academicStaff->isEmpty()) {
Log::write('未找到教务人员');
return;
if ($result['success']) {
Log::write("人员变更检查完成,更新了 {$result['updated_count']} 条记录");
} else {
Log::write("人员变更检查失败: {$result['message']}");
}
$distributionCount = 0;
foreach ($serviceLogs as $serviceLog) {
foreach ($academicStaff as $staff) {
// 发送通知给教务
$this->sendServiceLogNotice($staff, $serviceLog, 'academic');
$distributionCount++;
}
// 标记为已分发给教务
$serviceLog->is_distributed_to_academic = 1;
$serviceLog->distribution_time = time();
$serviceLog->save();
Log::write("已分发给教务,服务记录ID: {$serviceLog->id}");
}
Log::write("成功分发给教务 {$distributionCount} 条服务记录通知");
}
/**
* 给教练分发服务记录
* 生成新的服务记录
*/
private function distributeToCoaches()
private function generateNewServiceLogs()
{
// 获取需要分发的服务记录(最近24小时内完成的记录)
$yesterday = date('Y-m-d H:i:s', strtotime('-1 day'));
$serviceLogs = ServiceLogs::where('status', 1) // 已完成状态
->where('created_at', '>=', $yesterday)
->where('is_distributed_to_coach', 0) // 未分发给教练
->with(['service', 'staff'])
->select();
if ($serviceLogs->isEmpty()) {
Log::write('没有需要分发给教练的服务记录');
return;
}
// 获取教练人员列表
$coaches = Personnel::where('status', Personnel::STATUS_NORMAL)
->where('position', 'like', '%教练%')
->select();
if ($coaches->isEmpty()) {
Log::write('未找到教练人员');
return;
}
$distributionCount = 0;
foreach ($serviceLogs as $serviceLog) {
// 找到对应的教练(根据服务记录中的staff_id)
$coach = $coaches->where('id', $serviceLog->staff_id)->first();
if ($coach) {
// 发送通知给教练
$this->sendServiceLogNotice($coach, $serviceLog, 'coach');
$distributionCount++;
// 获取所有有效的学员课程,但之前没有教练或教务,现在有了的
$courses = Db::table('school_student_courses')
->where('status', 1)
->where(function ($query) {
$query->where('main_coach_id', '>', 0)
->whereOr('education_id', '>', 0);
})
->select()
->toArray();
$service = new ServiceLogsDistributionService();
$totalGenerated = 0;
foreach ($courses as $course) {
// 检查是否已经为这个课程生成过服务记录
$existingLogs = Db::table('school_service_logs')
->where('course_id', $course['id'])
->count();
// 如果没有服务记录,说明是新设置的教练/教务,需要生成服务记录
if ($existingLogs == 0) {
$result = $service->generateServiceLogs($course);
if ($result['success']) {
$totalGenerated += $result['generated_count'];
}
}
// 标记为已分发给教练
$serviceLog->is_distributed_to_coach = 1;
$serviceLog->distribution_time = time();
$serviceLog->save();
Log::write("已分发给教练,服务记录ID: {$serviceLog->id}");
}
Log::write("成功分发给教练 {$distributionCount} 条服务记录通知");
Log::write("本次任务为新课程生成了 {$totalGenerated} 条服务记录");
}
/**
@ -145,59 +108,8 @@ class ServiceLogsDistribution extends BaseJob
*/
private function sendServiceLogNotice($staff, $serviceLog, $type)
{
try {
// 准备通知数据
$noticeData = [
'staff_name' => $staff->name,
'service_name' => $serviceLog->service->service_name ?? '未知服务',
'service_remark' => $serviceLog->service_remark ?? '',
'score' => $serviceLog->score ?? 0,
'feedback' => $serviceLog->feedback ?? '',
'created_at' => date('Y-m-d H:i:s', $serviceLog->created_at),
'type' => $type
];
// 根据类型选择不同的通知模板
$noticeKey = $type === 'academic' ? 'service_log_academic_notice' : 'service_log_coach_notice';
// 发送通知
NoticeService::send($noticeKey, $noticeData);
Log::write("成功发送服务记录通知给 {$staff->name},服务记录ID: {$serviceLog->id}");
} catch (\Exception $e) {
Log::write("发送服务记录通知失败: {$e->getMessage()}");
}
// 可以在这里实现通知功能
// 比如发送短信、邮件、站内通知等
}
/**
* 获取分发统计信息
* @return array
*/
public function getDistributionStats()
{
$yesterday = date('Y-m-d H:i:s', strtotime('-1 day'));
$stats = [
'total_completed' => ServiceLogs::where('status', 1)
->where('created_at', '>=', $yesterday)
->count(),
'distributed_to_academic' => ServiceLogs::where('status', 1)
->where('created_at', '>=', $yesterday)
->where('is_distributed_to_academic', 1)
->count(),
'distributed_to_coach' => ServiceLogs::where('status', 1)
->where('created_at', '>=', $yesterday)
->where('is_distributed_to_coach', 1)
->count(),
'academic_staff_count' => Personnel::where('status', Personnel::STATUS_NORMAL)
->where('position', 'like', '%教务%')
->count(),
'coach_count' => Personnel::where('status', Personnel::STATUS_NORMAL)
->where('position', 'like', '%教练%')
->count(),
];
return $stats;
}
}

4
niucloud/app/job/schedule/TeachingPersonnelSync.php

@ -59,7 +59,7 @@ class TeachingPersonnelSync
]);
}
return $message;
return true;
} catch (\Exception $e) {
$error = '教研管理人员同步任务执行失败: ' . $e->getMessage();
@ -69,7 +69,7 @@ class TeachingPersonnelSync
'trace' => $e->getTraceAsString()
]);
return $error;
return false;
}
}

349
niucloud/app/service/admin/service_logs/ServiceLogsDistributionService.php

@ -15,6 +15,7 @@ use app\job\schedule\ServiceLogsDistribution;
use app\model\service_logs\ServiceLogs;
use app\model\personnel\Personnel;
use core\base\BaseAdminService;
use think\facade\Db;
use think\facade\Log;
/**
@ -30,155 +31,321 @@ class ServiceLogsDistributionService extends BaseAdminService
}
/**
* 手动执行分发任务
* 为学员课程生成服务记录
* @param object $studentCourse school_student_courses表的模型对象
* @return array
*/
public function executeDistribution()
public function generateServiceLogs($studentCourse)
{
try {
$job = new ServiceLogsDistribution();
$result = $job->doJob();
$result = [
'success' => true,
'generated_count' => 0,
'message' => '服务记录生成成功'
];
if ($result) {
return ['code' => 1, 'msg' => '分发任务执行成功'];
} else {
return ['code' => 0, 'msg' => '分发任务执行失败'];
// 获取所有启用的服务
$services = Db::table('school_service')
->where('status', 1)
->where('deleted_at', 0)
->select()
->toArray();
if (empty($services)) {
$result['message'] = '没有找到启用的服务';
return $result;
}
foreach ($services as $service) {
$generatedCount = $this->generateServiceLogsForService($studentCourse, $service);
$result['generated_count'] += $generatedCount;
}
Log::write("为课程ID:{$studentCourse['id']} 学员ID:{$studentCourse['student_id']} 生成了 {$result['generated_count']} 条服务记录");
return $result;
} catch (\Exception $e) {
Log::write('手动执行分发任务异常: ' . $e->getMessage());
return ['code' => 0, 'msg' => '分发任务执行异常:' . $e->getMessage()];
Log::write('生成服务记录异常: ' . $e->getMessage());
return [
'success' => false,
'generated_count' => 0,
'message' => '生成服务记录失败: ' . $e->getMessage()
];
}
}
/**
* 获取分发统计信息
* @return array
* 为特定服务生成服务记录
* @param object $studentCourse 学员课程对象
* @param array $service 服务信息
* @return int 生成的记录数量
*/
public function getDistributionStats()
private function generateServiceLogsForService($studentCourse, $service)
{
try {
$job = new ServiceLogsDistribution();
$stats = $job->getDistributionStats();
$generatedCount = 0;
return [
'code' => 1,
'data' => $stats,
'msg' => '获取统计信息成功'
];
} catch (\Exception $e) {
Log::write('获取分发统计信息异常: ' . $e->getMessage());
return ['code' => 0, 'msg' => '获取统计信息失败:' . $e->getMessage()];
// 根据服务类型确定负责人员
$staffId = null;
if ($service['service_type'] == 1) { // 教务服务
$staffId = $studentCourse['education_id'];
} elseif ($service['service_type'] == 2) { // 教练服务
$staffId = $studentCourse['main_coach_id'];
}
// 如果没有对应的人员ID,跳过处理
if (empty($staffId)) {
return 0;
}
// 计算需要生成的服务记录时间点
$serviceDates = $this->calculateServiceDates($studentCourse['start_date'], $studentCourse['end_date'], $service['execution_rules']);
foreach ($serviceDates as $serviceDate) {
// 检查是否已经存在相同的服务记录
$existingLog = Db::table('school_service_logs')
->where('course_id', $studentCourse['id'])
->where('service_id', $service['id'])
->where('staff_id', $staffId)
->where('student_id', $studentCourse['student_id'])
->where('created_at', $serviceDate)
->find();
if (!$existingLog) {
// 插入新的服务记录
Db::table('school_service_logs')->insert([
'resource_id' => $studentCourse['resource_id'],
'course_id' => $studentCourse['id'],
'service_id' => $service['id'],
'student_id' => $studentCourse['student_id'],
'staff_id' => $staffId,
'status' => 0, // 未服务状态
'created_at' => $serviceDate,
'updated_at' => date('Y-m-d H:i:s')
]);
$generatedCount++;
}
}
return $generatedCount;
}
/**
* 获取待分发的服务记录列表
* @param array $where
* @return array
* 根据执行规则计算服务时间点
* @param string $startDate 开始日期
* @param string $endDate 结束日期
* @param string $executionRule 执行规则
* @return array 时间点数组
*/
public function getPendingDistributionList(array $where = [])
private function calculateServiceDates($startDate, $endDate, $executionRule)
{
$field = 'id,service_id,staff_id,status,service_remark,feedback,score,created_at,updated_at,is_distributed_to_academic,is_distributed_to_coach,distribution_time';
$search_model = ServiceLogs::alias("a")
->join(['school_service' => 'b'], 'a.service_id = b.id', 'left')
->join(['school_personnel' => 'c'], 'a.staff_id = c.id', 'left')
->where('a.status', 1) // 已完成状态
->field("a.{$field},b.service_name,c.name as staff_name")
->order('a.created_at desc');
// 添加筛选条件
if (isset($where['distribution_status'])) {
switch ($where['distribution_status']) {
case 'pending_academic':
$search_model->where('a.is_distributed_to_academic', 0);
break;
case 'pending_coach':
$search_model->where('a.is_distributed_to_coach', 0);
break;
case 'distributed':
$search_model->where('a.is_distributed_to_academic', 1)
->where('a.is_distributed_to_coach', 1);
break;
}
$dates = [];
$start = strtotime($startDate);
$end = strtotime($endDate);
// 根据执行规则确定间隔天数
$intervalDays = $this->getIntervalDaysByRule($executionRule);
if ($intervalDays === 0) {
return $dates;
}
if (isset($where['date_range']) && !empty($where['date_range'])) {
$search_model->whereTime('a.created_at', $where['date_range']);
$currentTime = $start;
while ($currentTime <= $end) {
$dates[] = date('Y-m-d H:i:s', $currentTime);
$currentTime += ($intervalDays * 24 * 60 * 60); // 增加间隔天数
}
$list = $this->pageQuery($search_model);
return $dates;
}
return $list;
/**
* 根据执行规则获取间隔天数
* @param string $executionRule 执行规则
* @return int 间隔天数
*/
private function getIntervalDaysByRule($executionRule)
{
switch ($executionRule) {
case 'daily_once':
return 1; // 每天一次
case 'weekly_once':
return 7; // 每周一次
case 'twice_per_week':
return 3; // 一周两次,约每3-4天一次,这里简化为3天
case 'biweekly_once':
return 14; // 两周一次
case 'monthly_once':
return 30; // 每月一次,约30天
default:
return 0; // 未知规则
}
}
/**
* 重置分发状态
* @param array $ids 服务记录ID数组
* @param string $type 重置类型 (academic|coach|both)
* 检查并更新人员变更
* @return array
*/
public function resetDistributionStatus(array $ids, string $type = 'both')
public function checkAndUpdatePersonnelChanges()
{
try {
$updateData = [];
$updatedCount = 0;
if ($type === 'academic' || $type === 'both') {
$updateData['is_distributed_to_academic'] = 0;
}
// 获取所有未服务的服务记录
$pendingLogs = Db::table('school_service_logs')
->alias('sl')
->leftJoin('school_student_courses sc', 'sl.course_id = sc.id')
->leftJoin('school_service s', 'sl.service_id = s.id')
->where('sl.status', 0) // 未服务状态
->where('sc.status', 1) // 课程有效
->where('s.status', 1) // 服务有效
->where('s.deleted_at', 0)
->field('sl.*, sc.main_coach_id, sc.education_id, s.service_type')
->select()
->toArray();
if ($type === 'coach' || $type === 'both') {
$updateData['is_distributed_to_coach'] = 0;
}
foreach ($pendingLogs as $log) {
$newStaffId = null;
// 根据服务类型确定新的负责人员
if ($log['service_type'] == 1) { // 教务服务
$newStaffId = $log['education_id'];
} elseif ($log['service_type'] == 2) { // 教练服务
$newStaffId = $log['main_coach_id'];
}
if ($type === 'both') {
$updateData['distribution_time'] = 0;
// 如果人员发生变更且新人员不为空
if ($newStaffId && $log['staff_id'] != $newStaffId) {
Db::table('school_service_logs')
->where('id', $log['id'])
->update([
'staff_id' => $newStaffId,
'updated_at' => date('Y-m-d H:i:s')
]);
$updatedCount++;
}
}
$result = ServiceLogs::whereIn('id', $ids)->update($updateData);
Log::write("检查人员变更完成,更新了 {$updatedCount} 条服务记录");
return [
'success' => true,
'updated_count' => $updatedCount,
'message' => "人员变更检查完成,更新了 {$updatedCount} 条记录"
];
if ($result) {
Log::write("重置分发状态成功,记录ID: " . implode(',', $ids) . ",类型: {$type}");
return ['code' => 1, 'msg' => '重置分发状态成功'];
} else {
return ['code' => 0, 'msg' => '重置分发状态失败'];
}
} catch (\Exception $e) {
Log::write('重置分发状态异常: ' . $e->getMessage());
return ['code' => 0, 'msg' => '重置分发状态异常:' . $e->getMessage()];
Log::write('检查人员变更异常: ' . $e->getMessage());
return [
'success' => false,
'updated_count' => 0,
'message' => '检查人员变更失败: ' . $e->getMessage()
];
}
}
/**
* 获取教务和教练人员列表
* 根据员工ID获取服务记录
* @param int $staffId 员工ID
* @param int $page 页码
* @param int $limit 每页数量
* @param int $status 状态筛选 (null:全部, 0:未服务, 1:已服务)
* @return array
*/
public function getStaffList()
public function getServiceLogsByStaffId($staffId, $page = 1, $limit = 20, $status = null)
{
try {
$academicStaff = Personnel::where('status', Personnel::STATUS_NORMAL)
->where('position', 'like', '%教务%')
->field('id,name,position,phone')
$query = Db::table('school_service_logs')
->alias('sl')
->leftJoin('school_service s', 'sl.service_id = s.id')
->leftJoin('school_student_courses sc', 'sl.course_id = sc.id')
->leftJoin('school_student st', 'sl.student_id = st.id')
->where('sl.staff_id', $staffId)
->where('s.deleted_at', 0);
if ($status !== null) {
$query->where('sl.status', $status);
}
$total = $query->count();
$list = $query->field('sl.*, s.service_name, s.description as service_desc, st.name as student_name, sc.start_date, sc.end_date')
->order('sl.created_at DESC')
->page($page, $limit)
->select()
->toArray();
$coaches = Personnel::where('status', Personnel::STATUS_NORMAL)
->where('position', 'like', '%教练%')
->field('id,name,position,phone')
return [
'success' => true,
'data' => [
'list' => $list,
'total' => $total,
'page' => $page,
'limit' => $limit
],
'message' => '查询成功'
];
} catch (\Exception $e) {
Log::write('查询员工服务记录异常: ' . $e->getMessage());
return [
'success' => false,
'data' => [],
'message' => '查询失败: ' . $e->getMessage()
];
}
}
/**
* 根据学员ID获取服务记录
* @param int $studentId 学员ID
* @param int $page 页码
* @param int $limit 每页数量
* @param int $status 状态筛选 (null:全部, 0:未服务, 1:已服务)
* @return array
*/
public function getServiceLogsByStudentId($studentId, $page = 1, $limit = 20, $status = null)
{
try {
$query = Db::table('school_service_logs')
->alias('sl')
->leftJoin('school_service s', 'sl.service_id = s.id')
->leftJoin('school_student_courses sc', 'sl.course_id = sc.id')
->leftJoin('school_personnel p', 'sl.staff_id = p.id')
->where('sl.student_id', $studentId)
->where('s.deleted_at', 0);
if ($status !== null) {
$query->where('sl.status', $status);
}
$total = $query->count();
$list = $query->field('sl.*, s.service_name, s.description as service_desc, p.name as staff_name, sc.start_date, sc.end_date')
->order('sl.created_at DESC')
->page($page, $limit)
->select()
->toArray();
return [
'code' => 1,
'success' => true,
'data' => [
'academic_staff' => $academicStaff,
'coaches' => $coaches
'list' => $list,
'total' => $total,
'page' => $page,
'limit' => $limit
],
'msg' => '获取人员列表成功'
'message' => '查询成功'
];
} catch (\Exception $e) {
Log::write('获取人员列表异常: ' . $e->getMessage());
return ['code' => 0, 'msg' => '获取人员列表失败:' . $e->getMessage()];
Log::write('查询学员服务记录异常: ' . $e->getMessage());
return [
'success' => false,
'data' => [],
'message' => '查询失败: ' . $e->getMessage()
];
}
}
}

512
niucloud/app/service/api/personnel/WechatBindService.php

@ -0,0 +1,512 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\service\api\personnel;
use app\model\school\SchoolPersonnel;
use app\service\core\sys\CoreConfigService;
use think\facade\Db;
use core\base\BaseApiService;
use core\exception\ApiException;
use app\service\core\wechat\CoreWechatService;
use app\service\core\wechat\CoreWechatConfigService;
use app\service\core\wechat\CoreWechatServeService;
use think\Response;
/**
* 人员微信绑定服务类
* Class WechatBindService
* @package app\service\api\personnel
*/
class WechatBindService extends BaseApiService
{
/**
* 获取微信openid
* @param string $code 微信授权code
* @param string $type 类型 miniprogram: 小程序, official: 公众号
* @return array
*/
public function getWechatOpenid(string $code, string $type = 'miniprogram')
{
if (empty($code)) {
throw new ApiException('授权码不能为空');
}
try {
if ($type === 'miniprogram') {
// 小程序获取openid
$openid = $this->getMiniProgramOpenid($code);
} else {
// 公众号获取openid
$openid = $this->getOfficialOpenid($code);
}
return [
'openid' => $openid,
'type' => $type
];
} catch (\Exception $e) {
throw new ApiException('获取openid失败:' . $e->getMessage());
}
}
/**
* 绑定微信openid到人员
* @param array $data
* @return array
*/
public function bindWechatOpenid(array $data)
{
$personnel_id = $this->request->memberId(); // 从token中获取当前登录的人员ID
if (empty($personnel_id)) {
throw new ApiException('用户未登录');
}
// 查找人员记录
$personnel = SchoolPersonnel::where('id', $personnel_id)->find();
if (!$personnel) {
throw new ApiException('人员信息不存在');
}
$update_data = [];
// 根据类型绑定不同的openid
if ($data['type'] === 'miniprogram' && !empty($data['miniprogram_openid'])) {
// 检查小程序openid是否已被其他用户绑定
$exists = SchoolPersonnel::where('wxminiopenid', $data['miniprogram_openid'])
->where('id', '<>', $personnel_id)
->find();
if ($exists) {
throw new ApiException('该微信小程序已绑定其他账号');
}
$update_data['wxminiopenid'] = $data['miniprogram_openid'];
} elseif ($data['type'] === 'official' && !empty($data['official_openid'])) {
// 检查公众号openid是否已被其他用户绑定
$exists = SchoolPersonnel::where('wxgzhopenid', $data['official_openid'])
->where('id', '<>', $personnel_id)
->find();
if ($exists) {
throw new ApiException('该微信公众号已绑定其他账号');
}
$update_data['wxgzhopenid'] = $data['official_openid'];
} elseif ($data['type'] === 'both') {
// 同时绑定两个openid
if (!empty($data['miniprogram_openid'])) {
$exists = SchoolPersonnel::where('wxminiopenid', $data['miniprogram_openid'])
->where('id', '<>', $personnel_id)
->find();
if ($exists) {
throw new ApiException('该微信小程序已绑定其他账号');
}
$update_data['wxminiopenid'] = $data['miniprogram_openid'];
}
if (!empty($data['official_openid'])) {
$exists = SchoolPersonnel::where('wxgzhopenid', $data['official_openid'])
->where('id', '<>', $personnel_id)
->find();
if ($exists) {
throw new ApiException('该微信公众号已绑定其他账号');
}
$update_data['wxgzhopenid'] = $data['official_openid'];
}
}
if (empty($update_data)) {
throw new ApiException('没有可绑定的openid');
}
// 更新人员微信信息
$personnel->save($update_data);
return [
'personnel_id' => $personnel_id,
'bind_data' => $update_data,
'message' => '绑定成功'
];
}
/**
* 微信公众号授权跳转
* @param string $redirect_uri 回调地址
* @param string $state 状态参数
* @return Response
*/
public function wechatAuthorize(string $redirect_uri, string $state)
{
if (empty($redirect_uri)) {
throw new ApiException('回调地址不能为空');
}
try {
// 使用现有的微信授权服务
$core_wechat_service = new CoreWechatServeService();
$callback_url = $core_wechat_service->authorization($redirect_uri . '?state=' . urlencode($state), 'snsapi_userinfo');
// 直接跳转到微信授权页面
return redirect($callback_url);
} catch (\Exception $e) {
throw new ApiException('生成授权链接失败:' . $e->getMessage());
}
}
/**
* 微信授权回调处理
* @param string $code 授权回调code
* @param string $state 状态参数
* @return Response
*/
public function wechatCallback(string $code, string $state)
{
try {
if (empty($code)) {
return $this->buildCallbackPage('wechat_bind_cancel', '用户取消授权');
}
// 解析state参数
$state_data = json_decode(urldecode($state), true);
if (!$state_data || !isset($state_data['personnel_id'])) {
return $this->buildCallbackPage('wechat_bind_error', '参数错误');
}
// 获取公众号openid
$official_openid = $this->getOfficialOpenid($code);
// 绑定公众号openid
$bind_result = $this->bindOfficialOpenid($state_data['personnel_id'], $official_openid);
if ($bind_result) {
// 根据来源返回不同的页面
if (isset($state_data['from']) && $state_data['from'] === 'h5') {
// H5环境,返回简单的成功页面
return $this->buildH5SuccessPage('微信绑定成功!');
} else {
// 小程序webview环境
return $this->buildCallbackPage('wechat_bind_success', '微信公众号绑定成功');
}
} else {
return $this->buildCallbackPage('wechat_bind_error', '绑定失败');
}
} catch (\Exception $e) {
return $this->buildCallbackPage('wechat_bind_error', '绑定失败:' . $e->getMessage());
}
}
/**
* 获取绑定状态
* @return array
*/
public function getBindStatus()
{
$personnel_id = $this->request->memberId();
if (empty($personnel_id)) {
throw new ApiException('用户未登录');
}
$personnel = SchoolPersonnel::where('id', $personnel_id)->find();
if (!$personnel) {
throw new ApiException('人员信息不存在');
}
return [
'personnel_id' => $personnel_id,
'miniprogram_bound' => !empty($personnel->wxminiopenid),
'official_bound' => !empty($personnel->wxgzhopenid),
'miniprogram_openid' => $personnel->wxminiopenid ?? '',
'official_openid' => $personnel->wxgzhopenid ?? ''
];
}
/**
* 获取小程序openid
* @param string $code
* @return string
*/
private function getMiniProgramOpenid(string $code)
{
// 获取小程序配置
$weapp_config = $this->getWeappConfig();
// 构建请求URL
$url = "https://api.weixin.qq.com/sns/jscode2session?appid=" . $weapp_config['app_id'] .
"&secret=" . $weapp_config['secret'] .
"&js_code=" . $code .
"&grant_type=authorization_code";
// 使用cURL发起HTTP请求,更稳定
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($response === false) {
throw new ApiException('获取小程序openid失败:网络请求失败');
}
if ($http_code !== 200) {
throw new ApiException('获取小程序openid失败:HTTP状态码' . $http_code);
}
$result = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new ApiException('获取小程序openid失败:响应数据格式错误');
}
if (isset($result['errcode']) && $result['errcode'] != 0) {
throw new ApiException('获取小程序openid失败:' . ($result['errmsg'] ?? '未知错误') . '(错误码:' . $result['errcode'] . ')');
}
if (empty($result['openid'])) {
throw new ApiException('获取小程序openid失败:返回数据中无openid');
}
return $result['openid'];
}
/**
* 获取公众号openid
* @param string $code
* @return string
*/
private function getOfficialOpenid(string $code)
{
// 使用现有的微信服务获取用户信息
$core_wechat_service = new CoreWechatServeService();
$user = $core_wechat_service->userFromCode($code);
if (!$user) {
throw new ApiException('获取公众号openid失败');
}
return $user->getId(); // openid
}
/**
* 绑定公众号openid
* @param int $personnel_id
* @param string $official_openid
* @return bool
*/
private function bindOfficialOpenid(int $personnel_id, string $official_openid)
{
// 检查公众号openid是否已被其他用户绑定
$exists = SchoolPersonnel::where('wxgzhopenid', $official_openid)
->where('id', '<>', $personnel_id)
->find();
if ($exists) {
throw new ApiException('该微信公众号已绑定其他账号');
}
// 更新人员微信公众号openid
$personnel = SchoolPersonnel::where('id', $personnel_id)->find();
if (!$personnel) {
throw new ApiException('人员信息不存在');
}
$personnel->wxgzhopenid = $official_openid;
return $personnel->save();
}
/**
* 构建回调页面
* @param string $type
* @param string $message
* @return Response
*/
private function buildCallbackPage(string $type, string $message)
{
$html = '<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>微信绑定结果</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; padding: 50px; }
.success { color: #52c41a; }
.error { color: #f5222d; }
.cancel { color: #faad14; }
</style>
</head>
<body>
<h2 class="' . ($type === 'wechat_bind_success' ? 'success' : ($type === 'wechat_bind_error' ? 'error' : 'cancel')) . '">' . $message . '</h2>
<p>页面将在3秒后自动关闭</p>
<script>
// 向小程序发送消息
if (window.wx && window.wx.miniProgram) {
wx.miniProgram.postMessage({
data: {
type: "' . $type . '",
msg: "' . $message . '"
}
});
// 延时关闭webview
setTimeout(() => {
wx.miniProgram.navigateBack();
}, 3000);
} else {
// 非小程序环境,延时关闭窗口
setTimeout(() => {
window.close();
}, 3000);
}
</script>
</body>
</html>';
return Response::create($html, 'html');
}
/**
* 构建H5成功页面(用于微信浏览器直接访问)
* @param string $message
* @return Response
*/
private function buildH5SuccessPage(string $message)
{
$html = '<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>微信绑定成功</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
text-align: center;
padding: 80px 20px 20px 20px;
margin: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
color: #fff;
}
.container {
background: rgba(255,255,255,0.95);
border-radius: 20px;
padding: 40px 20px;
max-width: 400px;
margin: 0 auto;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
color: #333;
}
.success-icon {
width: 60px;
height: 60px;
background: #52c41a;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 20px auto;
font-size: 30px;
}
.success { color: #52c41a; font-size: 24px; font-weight: bold; margin-bottom: 15px; }
.message { color: #666; font-size: 16px; line-height: 1.5; margin-bottom: 30px; }
.close-btn {
background: #1890ff;
color: white;
border: none;
padding: 12px 30px;
border-radius: 25px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
}
.close-btn:hover { background: #40a9ff; }
</style>
</head>
<body>
<div class="container">
<div class="success-icon"></div>
<h2 class="success">' . $message . '</h2>
<p class="message">您已成功绑定微信公众号,现在可以享受更多便捷功能。</p>
<button class="close-btn" onclick="closeWindow()">完成</button>
</div>
<script>
function closeWindow() {
// 尝试关闭当前窗口
if (window.history.length > 1) {
window.history.back();
} else {
window.close();
}
// 如果无法关闭,显示提示
setTimeout(() => {
alert("绑定完成!请手动返回应用。");
}, 100);
}
// 3秒后自动执行关闭操作
setTimeout(() => {
closeWindow();
}, 3000);
</script>
</body>
</html>';
return Response::create($html, 'html');
}
/**
* 获取微信公众号配置
* @return array
*/
private function getWechatConfig()
{
$core_wechat_config = new CoreWechatConfigService();
return $core_wechat_config->getWechatConfig();
}
/**
* 获取小程序配置
* @return array
*/
private function getWeappConfig()
{
// 直接查询school_sys_config表(使用table避免重复前缀)
$config = Db::table('school_sys_config')
->where('config_key', 'weapp')
->value('value');
if (empty($config)) {
throw new ApiException('微信小程序未配置');
}
$config_data = is_string($config) ? json_decode($config, true) : $config;
if (empty($config_data['app_id']) || empty($config_data['app_secret'])) {
throw new ApiException('微信小程序配置不完整,缺少app_id或app_secret');
}
return [
'app_id' => $config_data['app_id'] ?? '',
'secret' => $config_data['app_secret'] ?? '',
'token' => $config_data['token'] ?? '',
'aes_key' => $config_data['encoding_aes_key'] ?? ''
];
}
}

18
uniapp/api/apiRoute.js

@ -306,6 +306,24 @@ export default {
return await http.get('/auth/wechat/auth_url', data)
},
//↓↓↓↓↓↓↓↓↓↓↓↓-----微信绑定相关接口-----↓↓↓↓↓↓↓↓↓↓↓↓
// 获取微信openid(支持小程序和公众号)
async getWechatOpenid(data = {}) {
return await http.post('/personnel/getWechatOpenid', data)
},
// 绑定微信openid到人员
async bindWechatOpenid(data = {}) {
return await http.post('/personnel/bindWechatOpenid', data)
},
// 微信公众号授权跳转
async wechatAuthorize(data = {}) {
return await http.get('/personnel/wechatAuthorize', data)
},
// 获取微信绑定状态
async getWechatBindStatus(data = {}) {
return await http.get('/personnel/getBindStatus', data)
},
//↓↓↓↓↓↓↓↓↓↓↓↓-----教练接口相关-----↓↓↓↓↓↓↓↓↓↓↓↓
//获取服务列表
async getServiceList(data = {}) {

4
uniapp/common/config.js

@ -1,6 +1,6 @@
// 环境变量配置
const env = 'development'
// const env = 'prod'
// const env = 'development'
const env = 'prod'
const isMockEnabled = false // 默认禁用Mock优先模式,仅作为回退
const isDebug = false // 默认启用调试模式
const devurl = 'http://localhost:20080/api'

161
uniapp/pages-common/profile/personal_info.vue

@ -722,111 +722,36 @@ export default {
})
},
// 使
async uploadAvatar(filePath) {
const { Api_url } = require('@/common/config.js')
const token = uni.getStorageSync('token') || ''
// 使 uploadFile
uploadAvatar(filePath) {
uni.showLoading({
title: '上传头像中...',
mask: true
})
try {
const result = await this.uploadImageFile(filePath, token, Api_url)
if (result && result.code === 1) {
this.formData.head_img = result.data.url
uploadFile(
filePath,
//
(fileData) => {
this.formData.head_img = fileData.url
uni.showToast({
title: '头像上传成功',
icon: 'success'
})
} else {
throw new Error(result.msg || '头像上传失败')
uni.hideLoading()
},
//
(error) => {
console.error('上传头像失败:', error)
uni.showToast({
title: error.msg || error.message || '头像上传失败',
icon: 'none'
})
uni.hideLoading()
}
} catch (error) {
console.error('上传头像失败:', error)
uni.showToast({
title: error.msg || error.message || '头像上传失败',
icon: 'none'
})
} finally {
uni.hideLoading()
}
)
},
//
uploadImageFile(filePath, token, apiUrl) {
//
const userType = uni.getStorageSync('userType') || '1' //
let uploadEndpoint = '/uploadImage' //
// 使
if (userType === '3') { //
uploadEndpoint = '/memberUploadImage'
}
return new Promise((resolve, reject) => {
uni.uploadFile({
url: apiUrl + uploadEndpoint, //
filePath: filePath,
name: 'file',
header: {
'token': token
},
success: (res) => {
let response
try {
// BOM JSON
response = JSON.parse(res.data.replace(/\ufeff/g, '') || '{}')
console.log('头像上传响应:', response)
} catch (e) {
console.error('头像上传响应解析失败:', e, 'raw response:', res.data)
reject({
code: 0,
msg: '服务器响应格式错误',
error: e
})
return
}
if (response.code === 1) {
resolve({
code: 1,
msg: response.msg || '上传成功',
data: {
url: response.data.url,
name: response.data.name || 'avatar',
ext: response.data.ext || 'jpg'
}
})
} else if (response.code === 401) {
uni.showToast({
title: response.msg || '登录已过期',
icon: 'none'
})
setTimeout(() => {
uni.navigateTo({ url: '/pages/student/login/login' })
}, 1500)
reject(response)
} else {
console.error('头像上传失败响应:', response)
reject({
code: response.code || 0,
msg: response.msg || '头像上传失败'
})
}
},
fail: (err) => {
console.error('头像上传网络失败:', err)
reject({
code: 0,
msg: err.errMsg || '网络异常,请检查网络连接',
error: err
})
}
})
})
},
//
onGenderChange(e) {
@ -905,10 +830,8 @@ export default {
})
},
// 使
async uploadIdCardFile(filePath, type) {
const { Api_url } = require('@/common/config.js')
const token = uni.getStorageSync('token') || ''
// 使 uploadFile
uploadIdCardFile(filePath, type) {
const title = type === 'front' ? '身份证正面' : '身份证反面'
console.log(`开始上传${title}:`, filePath)
@ -918,37 +841,39 @@ export default {
mask: true
})
try {
const result = await this.uploadImageFile(filePath, token, Api_url)
if (result && result.code === 1) {
console.log(`${title}上传成功:`, result.data)
uploadFile(
filePath,
//
(fileData) => {
console.log(`${title}上传成功:`, fileData)
if (type === 'front') {
this.formData.id_card_front = result.data.url
this.formData.id_card_front = fileData.url
} else if (type === 'back') {
this.formData.id_card_back = result.data.url
this.formData.id_card_back = fileData.url
}
uni.showToast({
title: `${title}上传成功`,
icon: 'success'
})
} else {
throw new Error(result.msg || `${title}上传失败`)
}
} catch (error) {
console.error(`上传${title}失败:`, error)
//
const errorMsg = error?.msg || error?.message || '上传失败'
uni.showToast({
title: `${title}${errorMsg}`,
icon: 'none',
duration: 3000
})
} finally {
uni.hideLoading()
}
uni.hideLoading()
},
//
(error) => {
console.error(`上传${title}失败:`, error)
const errorMsg = error?.msg || error?.message || '上传失败'
uni.showToast({
title: `${title}${errorMsg}`,
icon: 'none',
duration: 3000
})
uni.hideLoading()
}
)
},
//

117
uniapp/pages-common/webview/wechat_bind.vue

@ -0,0 +1,117 @@
<!--微信绑定WebView页面-->
<template>
<view class="webview-container">
<web-view
:src="webviewUrl"
@message="onMessage"
@load="onLoad"
@error="onError">
</web-view>
</view>
</template>
<script>
export default {
data() {
return {
webviewUrl: ''
}
},
onLoad(options) {
console.log('webview页面参数:', options)
if (options.url) {
this.webviewUrl = decodeURIComponent(options.url)
console.log('webview URL:', this.webviewUrl)
} else {
uni.showToast({
title: '参数错误',
icon: 'none'
})
setTimeout(() => {
uni.navigateBack()
}, 2000)
}
},
methods: {
// webview
onMessage(event) {
console.log('收到webview消息:', event)
const detail = event.detail
if (detail && detail.data && detail.data.length > 0) {
const message = detail.data[0]
this.handleWebviewMessage(message)
}
},
// webview
handleWebviewMessage(message) {
console.log('处理webview消息:', message)
if (message.type === 'wechat_bind_success') {
//
uni.showToast({
title: '微信绑定成功!',
icon: 'success',
duration: 2000
})
setTimeout(() => {
//
uni.navigateBack({
delta: 2 //
})
}, 2000)
} else if (message.type === 'wechat_bind_error') {
//
uni.showToast({
title: message.msg || '绑定失败',
icon: 'none',
duration: 3000
})
setTimeout(() => {
uni.navigateBack()
}, 3000)
} else if (message.type === 'wechat_bind_cancel') {
//
uni.showToast({
title: '用户取消授权',
icon: 'none'
})
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
},
// webview
onLoad() {
console.log('webview加载完成')
},
// webview
onError(error) {
console.error('webview加载错误:', error)
uni.showToast({
title: '页面加载失败',
icon: 'none'
})
setTimeout(() => {
uni.navigateBack()
}, 2000)
}
}
}
</script>
<style lang="less" scoped>
.webview-container {
width: 100%;
height: 100vh;
}
</style>

270
uniapp/pages-market/my/set_up.vue

@ -3,6 +3,7 @@
<view class="assemble">
<view style="height: 30rpx;"></view>
<view class="option" @click="update_pass()">修改密码</view>
<view class="option" @click="buildwx()"> 绑定微信</view>
<view class="option" @click="privacy_agreement(1)">用户协议</view>
<view class="option" @click="privacy_agreement(2)">隐私策略</view>
<view class="option" @click="clearCache()">清空缓存</view>
@ -14,6 +15,8 @@
</template>
<script>
import {Api_url} from '@/common/config.js'
import apiRoute from '@/api/apiRoute.js'
export default {
data() {
return {
@ -82,7 +85,272 @@
duration: 2000
});
}
}
},
//
async buildwx(){
try {
console.log('开始微信绑定流程')
//
// #ifdef MP-WEIXIN
console.log('微信小程序环境')
await this.bindWeixinInMiniProgram()
// #endif
// #ifdef H5
console.log('H5环境')
if (this.isWeixinBrowser()) {
console.log('微信浏览器环境')
await this.bindWeixinInH5()
} else {
console.log('非微信浏览器环境')
uni.showModal({
title: '提示',
content: '请在微信中打开此页面进行微信绑定',
showCancel: false,
confirmText: '我知道了'
})
return
}
// #endif
// #ifndef MP-WEIXIN || H5
//
uni.showToast({
title: '当前环境不支持微信绑定',
icon: 'none'
})
// #endif
} catch (error) {
console.error('绑定微信失败:', error)
uni.hideLoading()
uni.showToast({
title: '绑定失败,请重试',
icon: 'none'
})
}
},
// - openidwebview
async bindWeixinInMiniProgram() {
uni.showLoading({
title: '正在获取微信信息...'
})
// 1. openid
console.log('步骤1: 获取小程序openid')
const miniProgramOpenid = await this.getMiniProgramOpenid()
console.log('获取到的openid:', miniProgramOpenid)
if (!miniProgramOpenid) {
uni.hideLoading()
uni.showToast({
title: '获取小程序openid失败',
icon: 'none'
})
return
}
// 2. openid
console.log('步骤2: 绑定小程序openid')
const bindResult = await this.bindMiniProgramOpenid(miniProgramOpenid)
console.log('绑定结果:', bindResult)
if (!bindResult) {
uni.hideLoading()
return
}
uni.hideLoading()
// 3. webviewopenid
console.log('步骤3: 跳转webview绑定公众号')
const webviewUrl = this.buildWebviewUrl(miniProgramOpenid)
console.log('webview URL:', webviewUrl)
uni.navigateTo({
url: `/pages-common/webview/wechat_bind?url=${encodeURIComponent(webviewUrl)}`
})
},
// H5
async bindWeixinInH5() {
uni.showLoading({
title: '正在跳转微信授权...'
})
try {
// H5
const baseUrl = Api_url
const redirectUri = encodeURIComponent(`${baseUrl}/api/personnel/wechatCallback`)
const state = encodeURIComponent(JSON.stringify({
personnel_id: this.$store.state.userInfo.id,
from: 'h5',
timestamp: Date.now()
}))
const authUrl = `${baseUrl}/api/personnel/wechatAuthorize?redirect_uri=${redirectUri}&state=${state}`
console.log('H5授权URL:', authUrl)
uni.hideLoading()
//
location.href = authUrl
} catch (error) {
console.error('H5微信绑定失败:', error)
uni.hideLoading()
uni.showToast({
title: '授权跳转失败',
icon: 'none'
})
}
},
// openid
getMiniProgramOpenid() {
return new Promise((resolve, reject) => {
console.log('开始获取小程序openid')
// #ifdef MP-WEIXIN
console.log('在微信小程序环境中')
uni.login({
provider: 'weixin',
success: (loginResult) => {
console.log('微信登录成功,code:', loginResult.code)
if (loginResult.code) {
// openid
apiRoute.getWechatOpenid({
code: loginResult.code,
type: 'miniprogram'
}).then(res => {
console.log('获取openid接口响应:', res)
if (res.code === 1) {
resolve(res.data.openid)
} else {
uni.showToast({
title: res.msg || '获取openid失败',
icon: 'none'
})
resolve(null)
}
}).catch(error => {
console.error('获取openid接口调用失败:', error)
resolve(null)
})
} else {
uni.showToast({
title: '微信登录失败',
icon: 'none'
})
resolve(null)
}
},
fail: (error) => {
console.error('微信登录失败:', error)
uni.showToast({
title: '微信登录失败',
icon: 'none'
})
resolve(null)
}
})
// #endif
// #ifndef MP-WEIXIN
console.log('非微信小程序环境,启用测试模式')
// 使codeAPI
uni.showModal({
title: '测试模式',
content: '当前非小程序环境,是否使用测试code调试接口?',
success: (res) => {
if (res.confirm) {
console.log('用户选择测试模式')
// 使code
apiRoute.getWechatOpenid({
code: 'test_code_' + Date.now(),
type: 'miniprogram'
}).then(res => {
console.log('测试模式接口响应:', res)
if (res.code === 1) {
resolve(res.data.openid)
} else {
console.log('API返回错误:', res.msg)
uni.showToast({
title: '测试模式:' + (res.msg || '获取openid失败'),
icon: 'none',
duration: 3000
})
// 使openid
resolve('test_openid_' + Date.now())
}
}).catch(error => {
console.error('测试模式接口调用失败:', error)
// openid
resolve('test_openid_' + Date.now())
})
} else {
console.log('用户取消测试')
resolve(null)
}
}
})
// #endif
})
},
// openid
bindMiniProgramOpenid(openid) {
return new Promise((resolve) => {
apiRoute.bindWechatOpenid({
miniprogram_openid: openid,
type: 'miniprogram'
}).then(res => {
if (res.code === 1) {
uni.showToast({
title: '小程序绑定成功',
icon: 'success'
})
resolve(true)
} else {
uni.showToast({
title: res.msg || '小程序绑定失败',
icon: 'none'
})
resolve(false)
}
}).catch(error => {
console.error('绑定小程序openid失败:', error)
uni.showToast({
title: '绑定失败,请重试',
icon: 'none'
})
resolve(false)
})
})
},
// webview URL
buildWebviewUrl(miniOpenid) {
const baseUrl = Api_url
const redirectUri = encodeURIComponent(`${baseUrl}/api/personnel/wechatCallback`)
const state = encodeURIComponent(JSON.stringify({
mini_openid: miniOpenid,
personnel_id: this.$store.state.userInfo.id,
from: 'miniprogram',
timestamp: Date.now()
}))
return `${baseUrl}/api/personnel/wechatAuthorize?redirect_uri=${redirectUri}&state=${state}`
},
//
isWeixinBrowser() {
const ua = navigator.userAgent.toLowerCase()
return ua.includes('micromessenger')
}
}
}
</script>

3
uniapp/pages-student/contracts/sign.vue

@ -161,6 +161,7 @@
<script>
import apiRoute from '@/api/apiRoute.js'
import { Api_url } from '@/common/config.js'
export default {
data() {
@ -341,7 +342,7 @@
//
const uploadResult = await new Promise((resolve, reject) => {
uni.uploadFile({
url: 'http://localhost:20080/api/upload/image',
url: Api_url + '/api/upload/image',
filePath: tempFilePath,
name: 'image',
header: {

9
uniapp/pages.json

@ -666,6 +666,15 @@
"navigationBarBackgroundColor": "#181A20",
"navigationBarTextStyle": "white"
}
},
{
"path": "webview/wechat_bind",
"style": {
"navigationBarTitleText": "微信绑定",
"navigationStyle": "default",
"navigationBarBackgroundColor": "#29d3b4",
"navigationBarTextStyle": "white"
}
}
]
}

Loading…
Cancel
Save