diff --git a/.gitignore b/.gitignore
index 5dcf4e1d..b8fb0403 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,4 +14,6 @@ node_modules
examples
PRPs
INITIAL.md
+CLAUDE.local.md
+
diff --git a/admin/src/app/views/campus_person_role/campus_person_role.vue b/admin/src/app/views/campus_person_role/campus_person_role.vue
index 014211dd..2a7549b7 100644
--- a/admin/src/app/views/campus_person_role/campus_person_role.vue
+++ b/admin/src/app/views/campus_person_role/campus_person_role.vue
@@ -215,11 +215,10 @@ let campusPersonRoleTable = reactive({
})
-if(pageName == '市场人员列表'){
+if(pageName == '市场人员'){
campusPersonRoleTable.searchParam.dept_id = 1;
}else if(pageName == '销售人员列表'){
campusPersonRoleTable.searchParam.dept_id = 3;
- // campusPersonRoleTable.searchParam.role_id = 2;
}else if(pageName == '教练管理'){
campusPersonRoleTable.searchParam.dept_id = 2;
}
diff --git a/admin/src/app/views/campus_person_role/components/campus-person-role-edit.vue b/admin/src/app/views/campus_person_role/components/campus-person-role-edit.vue
index c856899e..2879691c 100644
--- a/admin/src/app/views/campus_person_role/components/campus-person-role-edit.vue
+++ b/admin/src/app/views/campus_person_role/components/campus-person-role-edit.vue
@@ -140,11 +140,6 @@ let showDialog = ref(false)
const loading = ref(false)
const pageName = route.meta.title
-// const tasks = ref([
-// { month: '2025-03', value: '123', editable: false },
-// { month: '2025-04', value: '123', editable: false },
-// { month: '2025-05', value: '123', editable: true }, // 只最后一条可编辑
-// ]);
/**
* 表单数据
*/
@@ -161,7 +156,6 @@ if(pageName == '市场人员列表'){
initialFormData.dept_id = 1;
}else if(pageName == '销售人员列表'){
initialFormData.dept_id = 3;
- // campusPersonRoleTable.searchParam.role_id = 2;
}else if(pageName == '教练管理'){
initialFormData.dept_id = 2;
}else{
diff --git a/niucloud/app/api/controller/apiController/StudentManager.php b/niucloud/app/api/controller/apiController/StudentManager.php
new file mode 100644
index 00000000..b21bac3c
--- /dev/null
+++ b/niucloud/app/api/controller/apiController/StudentManager.php
@@ -0,0 +1,98 @@
+request->params([
+ ["name", ""],
+ ["gender", 0],
+ ["age", 0.00],
+ ["birthday", ""],
+ ["user_id", 0],
+ ["campus_id", 0],
+ ["class_id", 0],
+ ["note", ""],
+ ["status", 1],
+ ["emergency_contact", ""],
+ ["contact_phone", ""],
+ ["member_label", ""],
+ ["consultant_id", ""],
+ ["coach_id", ""],
+ ["trial_class_count", 2]
+ ]);
+
+ // 表单验证
+ if (empty($data['name'])) {
+ return fail('学员姓名不能为空');
+ }
+ if ($data['gender'] == 0) {
+ return fail('请选择学员性别');
+ }
+ if (empty($data['birthday'])) {
+ return fail('请选择学员出生日期');
+ }
+
+ $result = (new StudentService())->add($data);
+ if ($result['code'] === 1) {
+ return success('添加成功', $result['data']);
+ } else {
+ return fail($result['msg']);
+ }
+ } catch (\Exception $e) {
+ return fail('添加学员失败:' . $e->getMessage());
+ }
+ }
+
+ /**
+ * 获取学员列表
+ * @param Request $request
+ * @return \think\Response
+ */
+ public function list(Request $request)
+ {
+ try {
+ $data = $this->request->params([
+ ["parent_resource_id", 0],
+ ["user_id", 0],
+ ["campus_id", 0],
+ ["status", 0]
+ ]);
+
+ $result = (new StudentService())->getList($data);
+ if ($result['code'] === 1) {
+ return success('获取成功', $result['data']);
+ } else {
+ return fail($result['msg']);
+ }
+ } catch (\Exception $e) {
+ return fail('获取学员列表失败:' . $e->getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/niucloud/app/api/controller/login/UnifiedLogin.php b/niucloud/app/api/controller/login/UnifiedLogin.php
new file mode 100644
index 00000000..7f0e6524
--- /dev/null
+++ b/niucloud/app/api/controller/login/UnifiedLogin.php
@@ -0,0 +1,162 @@
+request->params([
+ ['username', ''], // 用户名/手机号
+ ['password', ''], // 密码
+ ['login_type', ''], // 登录类型: staff=员工端, member=会员端
+ ]);
+
+ // 参数验证
+ $this->validate($data, [
+ 'username' => 'require',
+ 'password' => 'require',
+ 'login_type' => 'require|in:staff,member'
+ ]);
+
+ try {
+ $service = new UnifiedLoginService();
+ $result = $service->unifiedLogin($data);
+
+ if (!$result) {
+ return fail('登录失败,请检查用户名和密码');
+ }
+
+ return success($result, '登录成功');
+
+ } catch (\Exception $e) {
+ return fail($e->getMessage());
+ }
+ }
+
+ /**
+ * 员工端登录(兼容旧接口)
+ * @return Response
+ */
+ public function staffLogin()
+ {
+ $data = $this->request->params([
+ ['phone', ''],
+ ['password', ''],
+ ['user_type', ''], // 1=市场, 2=教练, 3=销售
+ ]);
+
+ // 参数验证
+ $this->validate($data, [
+ 'phone' => 'require|mobile',
+ 'password' => 'require',
+ 'user_type' => 'require|in:1,2,3'
+ ]);
+
+ try {
+ $service = new UnifiedLoginService();
+ $result = $service->staffLogin($data);
+
+ return success($result, '员工登录成功');
+
+ } catch (\Exception $e) {
+ return fail($e->getMessage());
+ }
+ }
+
+ /**
+ * 会员端登录(兼容旧接口)
+ * @return Response
+ */
+ public function memberLogin()
+ {
+ $data = $this->request->params([
+ ['username', ''],
+ ['password', ''],
+ ['mobile', ''],
+ ]);
+
+ try {
+ $service = new UnifiedLoginService();
+ $result = $service->memberLogin($data);
+
+ return success($result, '会员登录成功');
+
+ } catch (\Exception $e) {
+ return fail($e->getMessage());
+ }
+ }
+
+ /**
+ * 登出
+ * @return Response
+ */
+ public function logout()
+ {
+ try {
+ $service = new UnifiedLoginService();
+ $service->logout();
+
+ return success([], '登出成功');
+
+ } catch (\Exception $e) {
+ return fail($e->getMessage());
+ }
+ }
+
+ /**
+ * 获取用户信息
+ * @return Response
+ */
+ public function getUserInfo()
+ {
+ try {
+ $service = new UnifiedLoginService();
+ $result = $service->getUserInfo();
+
+ return success($result, '获取用户信息成功');
+
+ } catch (\Exception $e) {
+ return fail($e->getMessage());
+ }
+ }
+
+ /**
+ * 刷新Token
+ * @return Response
+ */
+ public function refreshToken()
+ {
+ try {
+ $service = new UnifiedLoginService();
+ $result = $service->refreshToken();
+
+ return success($result, 'Token刷新成功');
+
+ } catch (\Exception $e) {
+ return fail($e->getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/niucloud/app/api/middleware/ApiPersonnelCheckToken.php b/niucloud/app/api/middleware/ApiPersonnelCheckToken.php
index dd8ee824..816a0071 100644
--- a/niucloud/app/api/middleware/ApiPersonnelCheckToken.php
+++ b/niucloud/app/api/middleware/ApiPersonnelCheckToken.php
@@ -44,7 +44,7 @@ class ApiPersonnelCheckToken
$token = $request->apiToken();
$token_info = ( new LoginService() )->parsePersonnelToken($token);
if (!empty($token_info)) {
- $request->memberId($token_info[ 'member_id' ]);
+ $request->memberId($token_info[ 'user_id' ]);
}
//校验会员和站点
$a= ( new AuthService() )->checkPersonnelMember($request);
diff --git a/niucloud/app/api/route/route.php b/niucloud/app/api/route/route.php
index 7c0485b1..81d75c30 100644
--- a/niucloud/app/api/route/route.php
+++ b/niucloud/app/api/route/route.php
@@ -174,7 +174,9 @@ Route::group(function () {
//↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓-----员工端相关-----↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
//无需token验证的
Route::group(function () {
- //员工登录
+ //统一登录接口
+ Route::post('login/unified', 'login.UnifiedLogin/login');
+ //员工登录(兼容旧接口)
Route::post('personnelLogin', 'login.Login/personnelLogin');
//获取字典
Route::get('common/getDictionary', 'apiController.Common/getDictionary');
@@ -254,7 +256,10 @@ Route::group(function () {
Route::get('resourceSharing/info', 'apiController.ResourceSharing/info');
//资源共享-分配员工
Route::post('resourceSharing/assign', 'apiController.ResourceSharing/assign');
-
+ //销售端-学员-新增
+ Route::post('student/add', 'apiController.StudentManager/add');
+ //销售端-学员-列表
+ Route::get('student/list', 'apiController.StudentManager/list');
//沟通记录-添加
Route::post('communicationRecords/add', 'apiController.CommunicationRecords/add');
diff --git a/niucloud/app/service/api/apiService/ResourceSharingService.php b/niucloud/app/service/api/apiService/ResourceSharingService.php
index da59855a..cb9f714e 100644
--- a/niucloud/app/service/api/apiService/ResourceSharingService.php
+++ b/niucloud/app/service/api/apiService/ResourceSharingService.php
@@ -160,6 +160,7 @@ class ResourceSharingService extends BaseApiService
}
}
}
+
// 共享人查询 - 如果指定了shared_by,优先处理这个条件
if (isset($where['shared_by']) && $where['shared_by']) {
$model = $model->where(function ($query) use ($where) {
@@ -191,6 +192,7 @@ class ResourceSharingService extends BaseApiService
});
}
}
+
// 处理查询条件 - CustomerResources模型的字段
$resource_conditions = [];
@@ -246,10 +248,11 @@ class ResourceSharingService extends BaseApiService
$model = $model->where('shared_at', '>=', $where['shared_at_arr'][0])
->where('shared_at', '<=', $where['shared_at_arr'][1]);
}
-
- // 调试SQL语句
- // $sql = $model->with(['customerResource', 'sixSpeed'])->fetchSql(true)->select();
- // var_dump($sql);
+
+ // 过滤已分配的资源(只显示可再分配的资源)
+ $model = $model->where(function ($query) {
+ $query->where('shared_by', 0)->whereOr('shared_by', null);
+ });
// 查询数据
$list = $model->with(['customerResource', 'sixSpeed'])
@@ -277,8 +280,20 @@ class ResourceSharingService extends BaseApiService
$campus_names = $campus->whereIn('id', $campus_ids)->column('campus_name', 'id');
}
- // 获取资源ID列表
+ // 获取资源ID列表用于查询关联信息
$resource_ids = array_column($list['data'], 'resource_id');
+ $resource_ids = array_unique(array_filter($resource_ids));
+
+ // 获取分配人员ID列表
+ $shared_by_ids = array_column($list['data'], 'shared_by');
+ $shared_by_ids = array_unique(array_filter($shared_by_ids));
+
+ // 查询分配人员信息
+ $shared_by_names = [];
+ if (!empty($shared_by_ids)) {
+ $personnel = new \app\model\personnel\Personnel();
+ $shared_by_names = $personnel->whereIn('id', $shared_by_ids)->column('name', 'id');
+ }
// 查询最近沟通记录
$communication_times = [];
@@ -295,6 +310,58 @@ class ResourceSharingService extends BaseApiService
}
}
+ // 查询到访信息
+ $visit_info = [];
+ if (!empty($resource_ids)) {
+ // 需要先导入PersonCourseSchedule模型
+ $person_course_model = new \app\model\person_course_schedule\PersonCourseSchedule();
+ $visit_records = $person_course_model
+ ->whereIn('person_id', $resource_ids)
+ ->where('person_type', 'customer_resource')
+ ->field('person_id, course_date, time_slot, status')
+ ->order('course_date', 'desc')
+ ->select()
+ ->toArray();
+
+ // 处理到访信息
+ foreach ($visit_records as $record) {
+ $resource_id = $record['person_id'];
+
+ if (!isset($visit_info[$resource_id])) {
+ $visit_info[$resource_id] = [
+ 'first_visit_status' => '未到',
+ 'second_visit_status' => '未到',
+ 'visit_count' => 0
+ ];
+ }
+
+ if ($record['status'] == 1) { // 假设status=1表示已到
+ $visit_info[$resource_id]['visit_count']++;
+
+ if ($visit_info[$resource_id]['visit_count'] == 1) {
+ $visit_info[$resource_id]['first_visit_status'] = '已到';
+ } elseif ($visit_info[$resource_id]['visit_count'] == 2) {
+ $visit_info[$resource_id]['second_visit_status'] = '已到';
+ }
+ }
+ }
+ }
+
+ // 查询开单状态
+ $order_status = [];
+ if (!empty($resource_ids)) {
+ $six_speed_model = new \app\model\six_speed\SixSpeed();
+ $six_speed_records = $six_speed_model
+ ->whereIn('resource_id', $resource_ids)
+ ->field('resource_id, is_closed')
+ ->select()
+ ->toArray();
+
+ foreach ($six_speed_records as $record) {
+ $order_status[$record['resource_id']] = $record['is_closed'] ? '已开单' : '未开单';
+ }
+ }
+
// 处理每条数据
foreach ($list['data'] as &$item) {
if (!empty($item['customerResource'])) {
@@ -302,8 +369,24 @@ class ResourceSharingService extends BaseApiService
$item['customerResource']['source'] = get_dict_value('source', $item['customerResource']['source']);
$item['customerResource']['source_channel'] = get_dict_value('SourceChannel', $item['customerResource']['source_channel']);
$item['customerResource']['campus_name'] = $campus_names[$item['customerResource']['campus']] ?? '';
- $item['customerResource']['communication_time'] = $resultdata[$item['resource_id']] ?? '';
+
+ // 修复沟通时间字段
+ $item['customerResource']['communication_time'] = $communication_times[$item['resource_id']] ?? '';
+
+ // 添加到访信息
+ $resource_id = $item['resource_id'];
+ $item['customerResource']['first_visit_status'] = $visit_info[$resource_id]['first_visit_status'] ?? '未到';
+ $item['customerResource']['second_visit_status'] = $visit_info[$resource_id]['second_visit_status'] ?? '未到';
+
+ // 添加开单状态
+ $item['customerResource']['order_status'] = $order_status[$resource_id] ?? '未开单';
}
+
+ // 添加分配人员信息
+ $item['shared_by_name'] = $shared_by_names[$item['shared_by']] ?? '未分配';
+
+ // 判断资源是否可再分配(如果已有shared_by且不为0,说明已分配)
+ $item['can_reassign'] = empty($item['shared_by']) || $item['shared_by'] == 0;
}
}
diff --git a/niucloud/app/service/api/apiService/StudentService.php b/niucloud/app/service/api/apiService/StudentService.php
new file mode 100644
index 00000000..8ee1f99c
--- /dev/null
+++ b/niucloud/app/service/api/apiService/StudentService.php
@@ -0,0 +1,163 @@
+ 0,
+ 'msg' => '操作失败',
+ 'data' => []
+ ];
+
+ try {
+ // 准备插入数据
+ $insertData = [
+ 'name' => $data['name'],
+ 'gender' => $data['gender'],
+ 'age' => $data['age'],
+ 'birthday' => $data['birthday'],
+ 'user_id' => $data['user_id'],
+ 'campus_id' => $data['campus_id'],
+ 'class_id' => $data['class_id'],
+ 'note' => $data['note'],
+ 'status' => $data['status'],
+ 'emergency_contact' => $data['emergency_contact'],
+ 'contact_phone' => $data['contact_phone'],
+ 'member_label' => $data['member_label'],
+ 'consultant_id' => $data['consultant_id'],
+ 'coach_id' => $data['coach_id'],
+ 'trial_class_count' => $data['trial_class_count'],
+ 'created_at' => date('Y-m-d H:i:s'),
+ 'updated_at' => date('Y-m-d H:i:s'),
+ 'deleted_at' => 0
+ ];
+
+ // 插入数据库
+ $id = Db::table('school_student')->insertGetId($insertData);
+
+ if ($id) {
+ $res['code'] = 1;
+ $res['msg'] = '添加成功';
+ $res['data'] = ['id' => $id];
+ } else {
+ $res['msg'] = '添加失败';
+ }
+ } catch (\Exception $e) {
+ $res['msg'] = '添加失败:' . $e->getMessage();
+ }
+
+ return $res;
+ }
+
+ /**
+ * 获取学员列表
+ * @param array $data
+ * @return array
+ */
+ public function getList(array $data)
+ {
+ $res = [
+ 'code' => 0,
+ 'msg' => '操作失败',
+ 'data' => []
+ ];
+
+ try {
+ $where = [];
+ $where[] = ['deleted_at', '=', 0];
+
+ if (!empty($data['user_id'])) {
+ $where[] = ['user_id', '=', $data['user_id']];
+ }
+
+ if (!empty($data['parent_resource_id'])) {
+ $where[] = ['user_id', '=', $data['parent_resource_id']];
+ }
+
+ if (!empty($data['campus_id'])) {
+ $where[] = ['campus_id', '=', $data['campus_id']];
+ }
+
+ if (!empty($data['status'])) {
+ $where[] = ['status', '=', $data['status']];
+ }
+
+ $list = Db::table('school_student')
+ ->where($where)
+ ->order('created_at', 'desc')
+ ->select()
+ ->toArray();
+
+ // 为每个学员添加到访情况信息
+ foreach ($list as &$student) {
+ // 查询该学员的课程安排记录,按日期排序获取一访和二访信息
+ $visitRecords = Db::table('school_person_course_schedule')
+ ->where([
+ ['person_id', '=', $student['id']],
+ ['person_type', '=', 'student']
+ ])
+ ->order('course_date', 'asc')
+ ->select()
+ ->toArray();
+
+ // 初始化到访信息
+ $student['first_visit_time'] = '';
+ $student['second_visit_time'] = '';
+ $student['first_visit_status'] = '未到访';
+ $student['second_visit_status'] = '未到访';
+
+ // 设置一访和二访信息
+ if (!empty($visitRecords)) {
+ if (isset($visitRecords[0])) {
+ $student['first_visit_time'] = $visitRecords[0]['course_date'];
+ $student['first_visit_status'] = '已到访';
+ }
+ if (isset($visitRecords[1])) {
+ $student['second_visit_time'] = $visitRecords[1]['course_date'];
+ $student['second_visit_status'] = '已到访';
+ }
+ }
+
+ // 保留原始访问记录数据供前端使用
+ $student['visit_records'] = $visitRecords;
+ }
+
+ $res['code'] = 1;
+ $res['data'] = $list;
+ } catch (\Exception $e) {
+ $res['msg'] = '获取失败:' . $e->getMessage();
+ }
+
+ return $res;
+ }
+}
\ No newline at end of file
diff --git a/niucloud/app/service/api/login/UnifiedLoginService.php b/niucloud/app/service/api/login/UnifiedLoginService.php
new file mode 100644
index 00000000..a124b1a6
--- /dev/null
+++ b/niucloud/app/service/api/login/UnifiedLoginService.php
@@ -0,0 +1,420 @@
+handleStaffLogin($username, $password);
+ case self::USER_TYPE_MEMBER:
+ return $this->handleMemberLogin($username, $password);
+ default:
+ throw new CommonException('不支持的登录类型');
+ }
+ }
+
+ /**
+ * 员工端登录处理
+ * @param string $username
+ * @param string $password
+ * @return array
+ * @throws \Exception
+ */
+ private function handleStaffLogin(string $username, string $password)
+ {
+ // 查找员工信息及关联的系统用户信息
+ $personnel = new Personnel();
+ $staffInfo = $personnel->alias('p')
+ ->leftJoin('school_sys_user u', 'p.sys_user_id = u.uid')
+ ->where('p.phone', $username)
+ ->where('p.status', 2) // 2=已审核(正常状态)
+ ->where('u.status', 1)
+ ->field('p.*, u.username, u.password, u.real_name')
+ ->find();
+
+ if (!$staffInfo) {
+ throw new CommonException('员工账号不存在或已禁用');
+ }
+
+ // 验证密码
+ if (!password_verify($password, $staffInfo['password'])) {
+ throw new CommonException('密码错误');
+ }
+
+ // 根据account_type确定角色类型
+ $roleType = $this->getAccountTypeCode($staffInfo['account_type']);
+
+ // 生成Token
+ $tokenData = [
+ 'user_id' => $staffInfo['id'],
+ 'user_type' => self::USER_TYPE_STAFF,
+ 'role_type' => $roleType,
+ 'site_id' => 0, // 默认站点ID
+ ];
+
+ $tokenResult = TokenAuth::createToken($staffInfo['id'], AppTypeDict::PERSONNEL, $tokenData, 86400);
+ $token = $tokenResult['token'];
+
+ // 获取角色信息和菜单权限
+ $roleInfo = $this->getStaffRoleInfo($roleType);
+ $menuList = $this->getStaffMenuList($roleType);
+
+ return [
+ 'token' => $token,
+ 'user_info' => [
+ 'id' => $staffInfo['id'],
+ 'name' => $staffInfo['name'],
+ 'phone' => $staffInfo['phone'],
+ 'avatar' => $staffInfo['head_img'] ?? '',
+ 'real_name' => $staffInfo['real_name'] ?? $staffInfo['name'],
+ 'account_type' => $staffInfo['account_type'],
+ 'employee_number' => $staffInfo['employee_number'],
+ 'user_type' => self::USER_TYPE_STAFF,
+ 'role_type' => $roleType,
+ ],
+ 'role_info' => $roleInfo,
+ 'menu_list' => $menuList,
+ ];
+ }
+
+ /**
+ * 会员端登录处理
+ * @param string $username
+ * @param string $password
+ * @return array
+ * @throws \Exception
+ */
+ private function handleMemberLogin(string $username, string $password)
+ {
+ // 查找会员信息
+ $member = new Member();
+ $memberInfo = $member->where(function($query) use ($username) {
+ $query->where('username', $username)
+ ->whereOr('mobile', $username);
+ })
+ ->where('status', 1)
+ ->find();
+
+ if (!$memberInfo) {
+ throw new CommonException('会员账号不存在或已禁用');
+ }
+
+ // 验证密码
+ if (!password_verify($password, $memberInfo['password'])) {
+ throw new CommonException('密码错误');
+ }
+
+ // 生成Token
+ $tokenData = [
+ 'user_id' => $memberInfo['member_id'],
+ 'user_type' => self::USER_TYPE_MEMBER,
+ 'site_id' => $memberInfo['site_id'] ?? 0,
+ ];
+
+ $tokenResult = TokenAuth::createToken($memberInfo['member_id'], AppTypeDict::API, $tokenData, 86400);
+ $token = $tokenResult['token'];
+
+ // 获取会员菜单权限
+ $menuList = $this->getMemberMenuList();
+
+ return [
+ 'token' => $token,
+ 'user_info' => [
+ 'id' => $memberInfo['member_id'],
+ 'username' => $memberInfo['username'],
+ 'nickname' => $memberInfo['nickname'],
+ 'mobile' => $memberInfo['mobile'],
+ 'avatar' => $memberInfo['headimg'] ?? '',
+ 'user_type' => self::USER_TYPE_MEMBER,
+ ],
+ 'role_info' => [
+ 'role_name' => '会员',
+ 'role_type' => 'member',
+ ],
+ 'menu_list' => $menuList,
+ ];
+ }
+
+ /**
+ * 员工端登录(兼容旧接口)
+ * @param array $data
+ * @return array
+ */
+ public function staffLogin(array $data)
+ {
+ $staffData = [
+ 'username' => $data['phone'],
+ 'password' => $data['password'],
+ 'login_type' => self::USER_TYPE_STAFF,
+ ];
+
+ $result = $this->unifiedLogin($staffData);
+
+ // 添加user_type到用户信息中用于前端判断
+ $result['user_info']['user_type_code'] = $data['user_type'];
+
+ return $result;
+ }
+
+ /**
+ * 会员端登录(兼容旧接口)
+ * @param array $data
+ * @return array
+ */
+ public function memberLogin(array $data)
+ {
+ $memberData = [
+ 'username' => $data['username'] ?: $data['mobile'],
+ 'password' => $data['password'],
+ 'login_type' => self::USER_TYPE_MEMBER,
+ ];
+
+ return $this->unifiedLogin($memberData);
+ }
+
+ /**
+ * 获取员工角色信息
+ * @param int $roleType
+ * @return array
+ */
+ private function getStaffRoleInfo(int $roleType)
+ {
+ $roles = [
+ self::STAFF_ROLE_MARKET => ['role_name' => '市场人员', 'role_code' => 'market'],
+ self::STAFF_ROLE_COACH => ['role_name' => '教练', 'role_code' => 'coach'],
+ self::STAFF_ROLE_SALES => ['role_name' => '销售人员', 'role_code' => 'sales'],
+ self::STAFF_ROLE_TEACHER => ['role_name' => '教师', 'role_code' => 'teacher'],
+ ];
+
+ return $roles[$roleType] ?? ['role_name' => '教师', 'role_code' => 'teacher'];
+ }
+
+ /**
+ * 获取员工菜单列表
+ * @param int $roleType
+ * @return array
+ */
+ private function getStaffMenuList(int $roleType)
+ {
+ // 根据角色类型返回对应的菜单权限
+ switch ($roleType) {
+ case self::STAFF_ROLE_MARKET:
+ return [
+ ['path' => '/pages/market/home/index', 'name' => '首页', 'icon' => 'home'],
+ ['path' => '/pages/market/clue/index', 'name' => '线索管理', 'icon' => 'clue'],
+ ['path' => '/pages/market/clue/add_clues', 'name' => '添加客户', 'icon' => 'add'],
+ ['path' => '/pages/market/data/statistics', 'name' => '数据统计', 'icon' => 'data'],
+ ['path' => '/pages/market/my/index', 'name' => '个人中心', 'icon' => 'user'],
+ ];
+
+ case self::STAFF_ROLE_COACH:
+ case self::STAFF_ROLE_TEACHER:
+ return [
+ ['path' => '/pages/coach/home/index', 'name' => '首页', 'icon' => 'home'],
+ ['path' => '/pages/coach/course/list', 'name' => '课表管理', 'icon' => 'course'],
+ ['path' => '/pages/coach/student/student_list', 'name' => '我的学员', 'icon' => 'student'],
+ ['path' => '/pages/coach/job/list', 'name' => '作业管理', 'icon' => 'job'],
+ ['path' => '/pages/coach/my/index', 'name' => '个人中心', 'icon' => 'user'],
+ ];
+
+ case self::STAFF_ROLE_SALES:
+ return [
+ ['path' => '/pages/market/index/index', 'name' => '首页', 'icon' => 'home'],
+ ['path' => '/pages/market/clue/index', 'name' => '线索管理', 'icon' => 'clue'],
+ ['path' => '/pages/market/clue/add_clues', 'name' => '添加客户', 'icon' => 'add'],
+ ['path' => '/pages/market/clue/clue_table', 'name' => '数据统计', 'icon' => 'data'],
+ ['path' => '/pages/market/my/index', 'name' => '个人中心', 'icon' => 'user'],
+ ];
+
+ default:
+ return [
+ ['path' => '/pages/coach/home/index', 'name' => '首页', 'icon' => 'home'],
+ ['path' => '/pages/coach/my/index', 'name' => '个人中心', 'icon' => 'user'],
+ ];
+ }
+ }
+
+ /**
+ * 获取会员菜单列表
+ * @return array
+ */
+ private function getMemberMenuList()
+ {
+ return [
+ ['path' => '/pages/student/index/index', 'name' => '首页', 'icon' => 'home'],
+ ['path' => '/pages/student/timetable/index', 'name' => '课表', 'icon' => 'timetable'],
+ ['path' => '/pages/student/my/my', 'name' => '个人中心', 'icon' => 'user'],
+ // 家长端菜单
+ ['path' => '/pages/parent/user-info/index', 'name' => '用户信息', 'icon' => 'user-info'],
+ ['path' => '/pages/parent/courses/index', 'name' => '课程管理', 'icon' => 'course'],
+ ['path' => '/pages/parent/materials/index', 'name' => '教学资料', 'icon' => 'material'],
+ ['path' => '/pages/parent/services/index', 'name' => '服务管理', 'icon' => 'service'],
+ ['path' => '/pages/parent/orders/index', 'name' => '订单管理', 'icon' => 'order'],
+ ['path' => '/pages/parent/messages/index', 'name' => '消息管理', 'icon' => 'message'],
+ ['path' => '/pages/parent/contracts/index', 'name' => '合同管理', 'icon' => 'contract'],
+ ];
+ }
+
+ /**
+ * 登出
+ * @throws \Exception
+ */
+ public function logout()
+ {
+ $token = request()->header('token');
+ if ($token) {
+ (new CoreTokenService())->delete($token);
+ }
+ }
+
+ /**
+ * 获取用户信息
+ * @return array
+ * @throws \Exception
+ */
+ public function getUserInfo()
+ {
+ $token = request()->header('token');
+ if (!$token) {
+ throw new CommonException('未登录');
+ }
+
+ $tokenData = (new CoreTokenService())->verify($token);
+ if (!$tokenData) {
+ throw new CommonException('Token无效');
+ }
+
+ $userType = $tokenData['user_type'];
+ $userId = $tokenData['user_id'];
+
+ if ($userType === self::USER_TYPE_STAFF) {
+ $personnel = new Personnel();
+ $userInfo = $personnel->alias('p')
+ ->leftJoin('school_sys_user u', 'p.sys_user_id = u.uid')
+ ->where('p.id', $userId)
+ ->field('p.*, u.real_name')
+ ->find();
+ if (!$userInfo) {
+ throw new CommonException('员工信息不存在');
+ }
+
+ $roleType = $this->getAccountTypeCode($userInfo['account_type']);
+
+ return [
+ 'id' => $userInfo['id'],
+ 'name' => $userInfo['name'],
+ 'phone' => $userInfo['phone'],
+ 'avatar' => $userInfo['head_img'] ?? '',
+ 'real_name' => $userInfo['real_name'] ?? $userInfo['name'],
+ 'account_type' => $userInfo['account_type'],
+ 'user_type' => self::USER_TYPE_STAFF,
+ 'role_type' => $roleType,
+ ];
+ } else {
+ $member = new Member();
+ $userInfo = $member->where('member_id', $userId)->find();
+ if (!$userInfo) {
+ throw new CommonException('会员信息不存在');
+ }
+
+ return [
+ 'id' => $userInfo['member_id'],
+ 'username' => $userInfo['username'],
+ 'nickname' => $userInfo['nickname'],
+ 'mobile' => $userInfo['mobile'],
+ 'avatar' => $userInfo['headimg'] ?? '',
+ 'user_type' => self::USER_TYPE_MEMBER,
+ ];
+ }
+ }
+
+ /**
+ * 刷新Token
+ * @return array
+ * @throws \Exception
+ */
+ public function refreshToken()
+ {
+ $token = request()->header('token');
+ if (!$token) {
+ throw new CommonException('未登录');
+ }
+
+ $newToken = (new CoreTokenService())->refresh($token);
+ if (!$newToken) {
+ throw new CommonException('Token刷新失败');
+ }
+
+ return ['token' => $newToken];
+ }
+
+ /**
+ * 根据账户类型获取角色编码
+ * @param string $accountType
+ * @return int
+ */
+ private function getAccountTypeCode(string $accountType)
+ {
+ switch ($accountType) {
+ case 'teacher':
+ return self::STAFF_ROLE_TEACHER;
+ case 'market':
+ return self::STAFF_ROLE_MARKET;
+ default:
+ return self::STAFF_ROLE_TEACHER; // 默认为教师
+ }
+ }
+}
\ No newline at end of file
diff --git a/uniapp/api/apiRoute.js b/uniapp/api/apiRoute.js
index e9987d33..9660e0a7 100644
--- a/uniapp/api/apiRoute.js
+++ b/uniapp/api/apiRoute.js
@@ -3,7 +3,13 @@ import http from '../common/axios.js'
//全部api接口
export default {
//↓↓↓↓↓↓↓↓↓↓↓↓-----公共接口相关-----↓↓↓↓↓↓↓↓↓↓↓↓
- //教师/销售端登陆
+ //统一登录接口
+ async unifiedLogin(data = {}) {
+ const response = await http.post('/login/unified', data);
+ console.log('统一登录响应:', response);
+ return response;
+ },
+ //教师/销售端登陆(兼容旧接口)
async personnelLogin(data = {}) {
const response = await http.post('/personnelLogin', data);
console.log('登录响应:', response);
@@ -30,6 +36,18 @@ export default {
return await http.get('/common/getDictionary', data);
},
+ //批量获取字典数据
+ async common_getBatchDict(keys = []) {
+ // 支持传入数组或字符串
+ const keyParam = Array.isArray(keys) ? keys.join(',') : keys;
+ return await http.get('/dict/batch', { keys: keyParam });
+ },
+
+ //根据业务场景获取字典数据
+ async common_getDictByScene(scene = '') {
+ return await http.get(`/dict/scene/${scene}`);
+ },
+
// 课程安排列表 - Mock版本
async getCourseScheduleListMock(data = {}) {
@@ -404,6 +422,14 @@ export default {
async xs_resourceSharingInfo(data = {}) {
return await http.get('/resourceSharing/info', data);
},
+ //销售端-学员-新增
+ async xs_addStudent(data = {}) {
+ return await http.post('/student/add', data);
+ },
+ //销售端-学员-列表
+ async xs_getStudentList(data = {}) {
+ return await http.get('/student/list', data);
+ },
//销售端-沟通记录-添加
async xs_communicationRecordsAdd(data = {}) {
return await http.post('/communicationRecords/add', data);
diff --git a/uniapp/components/custom-top-popup/custom-top-popup.vue b/uniapp/components/custom-top-popup/custom-top-popup.vue
new file mode 100644
index 00000000..8d20bcec
--- /dev/null
+++ b/uniapp/components/custom-top-popup/custom-top-popup.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uniapp/pages.json b/uniapp/pages.json
index 98336c6d..8b2f430a 100644
--- a/uniapp/pages.json
+++ b/uniapp/pages.json
@@ -834,6 +834,24 @@
"navigationBarBackgroundColor": "#29d3b4",
"navigationBarTextStyle": "white"
}
+ },
+ {
+ "path": "pages/common/home/index",
+ "style": {
+ "navigationBarTitleText": "首页",
+ "navigationStyle": "custom",
+ "navigationBarBackgroundColor": "#181A20",
+ "navigationBarTextStyle": "white"
+ }
+ },
+ {
+ "path": "pages/common/profile/index",
+ "style": {
+ "navigationBarTitleText": "我的",
+ "navigationStyle": "custom",
+ "navigationBarBackgroundColor": "#181A20",
+ "navigationBarTextStyle": "white"
+ }
}
],
@@ -844,6 +862,26 @@
"backgroundColor": "#FDFDFD",
"enablePullDownRefresh": true
},
+ "tabBar": {
+ "color": "#7A7E83",
+ "selectedColor": "#3cc51f",
+ "borderStyle": "black",
+ "backgroundColor": "#1a1a1a",
+ "list": [
+ {
+ "pagePath": "pages/common/home/index",
+ "iconPath": "static/icon-img/home.png",
+ "selectedIconPath": "static/icon-img/home-active.png",
+ "text": "首页"
+ },
+ {
+ "pagePath": "pages/common/profile/index",
+ "iconPath": "static/icon-img/profile.png",
+ "selectedIconPath": "static/icon-img/profile-active.png",
+ "text": "我的"
+ }
+ ]
+ },
"easycom": {
"autoscan": true,
"custom": {
diff --git a/uniapp/pages/common/home/index.vue b/uniapp/pages/common/home/index.vue
new file mode 100644
index 00000000..7257c690
--- /dev/null
+++ b/uniapp/pages/common/home/index.vue
@@ -0,0 +1,230 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ userInfo.name || '员工姓名' }}
+ {{ (userInfo.role_info && userInfo.role_info.role_name) || '员工角色' }}
+ 工号:{{ userInfo.employee_number || '未设置' }}
+
+
+
+
+
+ 员工操作
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uniapp/pages/common/profile/index.vue b/uniapp/pages/common/profile/index.vue
new file mode 100644
index 00000000..b2004030
--- /dev/null
+++ b/uniapp/pages/common/profile/index.vue
@@ -0,0 +1,245 @@
+
+
+
+
+
+
+
+
+
+
+ 个人中心
+
+
+
+
+
+ {{ item.title }}
+ {{ item.desc }}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uniapp/pages/market/clue/add_clues.vue b/uniapp/pages/market/clue/add_clues.vue
index 7ee5438c..d04b2851 100644
--- a/uniapp/pages/market/clue/add_clues.vue
+++ b/uniapp/pages/market/clue/add_clues.vue
@@ -24,7 +24,12 @@
- 基础信息
+
+ 基础信息
+
+ 快速填写
+
+
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
- 男
-
-
-
-
-
- 女
-
-
-
-
+
+ {{ formData.birthday ? formData.birthday : '请选择生日' }}
+
+
@@ -468,6 +435,16 @@
+
+ 检测到手机号重复,是否为现有用户添加新学员?
+
+ 确认添加学员
+
+
@@ -527,6 +504,34 @@
+
+
+
+
+
+
+ 请粘贴包含客户信息的文本,支持格式:
+ 姓名:张三,电话:13800138000,校区:测试校区
+
+
+
+
+ 取消
+ 解析并填写
+
+
+
+
@@ -615,6 +620,10 @@ export default {
is_submit: true,//是否提交(防止重复提交)|true=可提交,false=不可提交
+ // 快速填写相关
+ showQuickFill: false, // 是否显示快速填写弹窗
+ quickFillText: '', // 快速填写文本内容
+
//表单
formData: {
// 客户基础信息
@@ -629,6 +638,7 @@ export default {
decision_maker:'',//决策人
initial_intent:'',//客户初步意向度: high-高, medium-中, low-低
status:'pending',//客户状态: active-活跃, inactive-不活跃, pending-待定
+ birthday:'',//生日
//六要素信息
purchasing_power:'',//购买力
@@ -713,6 +723,11 @@ export default {
data_picker_input_name:'',//时间组件的input_name
date_picker_show:false,//时间选择器是否展示
+ //生日选择器
+ picker_show_birthday: false,
+ minDate: new Date(1900, 0, 1).toISOString(), // 最小日期
+ maxDate: new Date().toISOString(), // 最大日期
+
//地区三级联动
show_area: false,
@@ -790,6 +805,113 @@ export default {
console.log('时间默认值已清空,用户需手动选择')
},
+ // 快速填写相关方法
+ // 打开快速填写弹窗
+ openQuickFill() {
+ this.showQuickFill = true
+ this.quickFillText = ''
+ },
+
+ // 解析快速填写文本
+ parseQuickFillText() {
+ if (!this.quickFillText.trim()) {
+ uni.showToast({
+ title: '请输入要解析的文本',
+ icon: 'none'
+ })
+ return
+ }
+
+ try {
+ // 定义字段映射规则
+ const fieldRules = [
+ { key: 'name', patterns: ['姓名', '客户姓名', '用户姓名', '学员姓名', '学生姓名'] },
+ { key: 'phone_number', patterns: ['电话', '手机', '联系电话', '手机号', '电话号码', '联系方式'] },
+ { key: 'campus', patterns: ['校区', '所属校区', '校区名称'] },
+ { key: 'age', patterns: ['年龄'] },
+ { key: 'birthday', patterns: ['生日', '出生日期', '生日日期'] }
+ ]
+
+ // 用于存储解析结果
+ const parsedData = {}
+ const text = this.quickFillText.trim()
+
+ // 对每个字段规则进行匹配
+ fieldRules.forEach(rule => {
+ rule.patterns.forEach(pattern => {
+ // 匹配模式:字段名 + 冒号/等号 + 数据内容
+ const regex = new RegExp(`${pattern}\\s*[::=]\\s*([^,,\\n\\s]+)`, 'g')
+ const match = regex.exec(text)
+ if (match && match[1]) {
+ parsedData[rule.key] = match[1].trim()
+ }
+ })
+ })
+
+ console.log('解析结果:', parsedData)
+
+ // 填写到表单中
+ let fillCount = 0
+ if (parsedData.name) {
+ this.formData.name = parsedData.name
+ fillCount++
+ }
+ if (parsedData.phone_number) {
+ this.formData.phone_number = parsedData.phone_number
+ fillCount++
+ }
+ if (parsedData.campus) {
+ // 需要在校区选项中查找匹配的项
+ this.findAndSetCampus(parsedData.campus)
+ fillCount++
+ }
+ if (parsedData.age) {
+ const age = parseInt(parsedData.age)
+ if (!isNaN(age) && age > 0 && age < 150) {
+ this.formData.age = age
+ fillCount++
+ }
+ }
+ if (parsedData.birthday) {
+ this.formData.birthday = parsedData.birthday
+ fillCount++
+ }
+
+ if (fillCount > 0) {
+ uni.showToast({
+ title: `成功填写${fillCount}个字段`,
+ icon: 'success'
+ })
+ this.showQuickFill = false
+ } else {
+ uni.showToast({
+ title: '未能识别到有效信息,请检查格式',
+ icon: 'none'
+ })
+ }
+
+ } catch (error) {
+ console.error('解析失败:', error)
+ uni.showToast({
+ title: '解析失败,请检查格式',
+ icon: 'none'
+ })
+ }
+ },
+
+ // 查找并设置校区
+ findAndSetCampus(campusText) {
+ const campusOptions = this.picker_config.campus?.options || []
+ const matchedCampus = campusOptions.find(option =>
+ option.text.includes(campusText) || campusText.includes(option.text)
+ )
+
+ if (matchedCampus) {
+ this.formData.campus = matchedCampus.value
+ this.picker_config.campus.text = matchedCampus.text
+ }
+ },
+
// 统一的日期格式化方法
formatDate(date) {
const year = date.getFullYear()
@@ -1199,6 +1321,76 @@ export default {
this.openDuplicateCheck()
},
+ //确认添加学员
+ async confirmAddStudent(){
+ if(!this.clientUserList || this.clientUserList.length == 0){
+ uni.showToast({
+ title: '没有找到重复的用户',
+ icon: 'none'
+ })
+ return
+ }
+
+ //取第一个重复用户的ID作为user_id
+ const userId = this.clientUserList[0].id
+
+ //检查必要字段
+ if(!this.formData.name){
+ uni.showToast({
+ title: '请填写学员姓名',
+ icon: 'none'
+ })
+ return
+ }
+
+ if(!this.formData.gender){
+ uni.showToast({
+ title: '请选择学员性别',
+ icon: 'none'
+ })
+ return
+ }
+
+ //准备学员数据
+ const studentData = {
+ name: this.formData.name,
+ gender: this.formData.gender,
+ user_id: userId,
+ status: 1
+ }
+
+ try {
+ const res = await apiRoute.xs_addStudent(studentData)
+ if(res.code != 1){
+ uni.showToast({
+ title: res.msg || '添加学员失败',
+ icon: 'none'
+ })
+ return
+ }
+
+ uni.showToast({
+ title: '学员添加成功',
+ icon: 'success'
+ })
+
+ //关闭弹窗并跳转回列表页
+ this.showDuplicateCheck = false
+ setTimeout(() => {
+ uni.redirectTo({
+ url: `/pages/market/clue/index`
+ })
+ }, 1000)
+
+ } catch (error) {
+ console.error('添加学员失败:', error)
+ uni.showToast({
+ title: '添加学员失败',
+ icon: 'none'
+ })
+ }
+ },
+
//多选客户标签-选中后回调
@@ -1378,6 +1570,31 @@ export default {
this.date_picker_show = false
},
+ //生日选择器
+ changePickerBirthday(e) {
+ console.log('生日选择器返回数据:', e)
+ let val = ''
+ if (e.result) {
+ val = e.result
+ } else if (e.value) {
+ val = e.value
+ } else if (e.detail && e.detail.result) {
+ val = e.detail.result
+ } else if (e.detail && e.detail.value) {
+ val = e.detail.value
+ }
+
+ if (val && typeof val === 'string') {
+ if (/^\d+$/.test(val)) {
+ const date = new Date(parseInt(val))
+ val = this.formatDate(date)
+ }
+ }
+
+ this.formData.birthday = val
+ this.picker_show_birthday = false
+ },
+
//下一步 index|0=添加客户,1六要素
async nextStep(index) {
this.optionTableId = String(index)
@@ -1648,4 +1865,132 @@ export default {
}
}
+
+// 快速填写弹窗样式
+.quick-fill-mask {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.6);
+ z-index: 1500;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 40rpx;
+}
+
+.quick-fill-content {
+ background: #fff;
+ border-radius: 16rpx;
+ width: 100%;
+ max-width: 600rpx;
+ max-height: 80vh;
+ display: flex;
+ flex-direction: column;
+}
+
+.quick-fill-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 32rpx;
+ border-bottom: 1px solid #f0f0f0;
+}
+
+.quick-fill-title {
+ font-size: 32rpx;
+ font-weight: 600;
+ color: #333;
+}
+
+.quick-fill-close {
+ width: 60rpx;
+ height: 60rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ .close-text {
+ font-size: 32rpx;
+ color: #999;
+ }
+}
+
+.duplicate-notice {
+ padding: 32rpx;
+ text-align: center;
+ border-bottom: 1px solid #eee;
+ margin-bottom: 20rpx;
+
+ text {
+ display: block;
+ margin-bottom: 20rpx;
+ }
+}
+
+.quick-fill-body {
+ padding: 32rpx;
+ flex: 1;
+ overflow-y: auto;
+}
+
+.quick-fill-tip {
+ margin-bottom: 24rpx;
+ font-size: 24rpx;
+ color: #666;
+ line-height: 1.5;
+
+ text:first-child {
+ display: block;
+ margin-bottom: 8rpx;
+ }
+
+ text:last-child {
+ display: block;
+ color: #29d3b4;
+ font-weight: 500;
+ }
+}
+
+.quick-fill-textarea {
+ width: 100%;
+ height: 300rpx;
+ border: 1px solid #ddd;
+ border-radius: 8rpx;
+ padding: 16rpx;
+ font-size: 28rpx;
+ line-height: 1.5;
+ background: #fafafa;
+ box-sizing: border-box;
+}
+
+.quick-fill-buttons {
+ display: flex;
+ gap: 20rpx;
+ padding: 32rpx;
+ border-top: 1px solid #f0f0f0;
+}
+
+.quick-fill-btn {
+ flex: 1;
+ height: 72rpx;
+ line-height: 72rpx;
+ text-align: center;
+ border-radius: 8rpx;
+ font-size: 28rpx;
+ font-weight: 600;
+
+ &.cancel-btn {
+ background: #f5f5f5;
+ color: #666;
+ border: 1px solid #ddd;
+ }
+
+ &.confirm-btn {
+ background: #29d3b4;
+ color: #fff;
+ }
+}
\ No newline at end of file
diff --git a/uniapp/pages/market/clue/clue_info.vue b/uniapp/pages/market/clue/clue_info.vue
index a04d7aaa..64c0c3dd 100644
--- a/uniapp/pages/market/clue/clue_info.vue
+++ b/uniapp/pages/market/clue/clue_info.vue
@@ -58,39 +58,146 @@
- 基本信息
-
-
- 来源渠道
- {{ safeGet(clientInfo, 'customerResource.source_channel_name', '未知渠道') }}
-
-
- 来源
- {{ safeGet(clientInfo, 'customerResource.source_name', '未知来源') }}
-
-
- 顾问
- {{ safeGet(clientInfo, 'customerResource.consultant_name', '未知顾问') }}
-
-
- 学生姓名
- {{ safeGet(clientInfo, 'customerResource.name', '未知学生') }}
-
-
- 性别
- {{ safeGet(clientInfo, 'customerResource.gender_name', '未知性别') }}
-
-
-
- 附加信息
-
-
- 已成交次数
- {{ safeGet(clientInfo, 'customerResource.cj_count', 0) }}次
+
+
+
+ 客户和学生信息
+
+ +
+ 添加学生
+
-
- 体验课程
- {{ safeGet(clientInfo, 'customerResource.trial_class_count', 0) }}次
+
+
+
+
+
+
+
+
+
+
+
+
+ 来源渠道
+ {{ safeGet(clientInfo, 'customerResource.source_channel_name', '未知渠道') }}
+
+
+ 来源
+ {{ safeGet(clientInfo, 'customerResource.source_name', '未知来源') }}
+
+
+ 顾问
+ {{ safeGet(clientInfo, 'customerResource.consultant_name', '未知顾问') }}
+
+
+ 性别
+ {{ safeGet(clientInfo, 'customerResource.gender_name', '未知性别') }}
+
+
+
+
+
+
+
+
+
+
+
+
+ 出生日期:
+ {{ student.birthday || '未设置' }}
+
+
+ 联系电话:
+ {{ student.contact_phone || '未设置' }}
+
+
+ 紧急联系人:
+ {{ student.emergency_contact || '未设置' }}
+
+
+ 体验课次数:
+ {{ student.trial_class_count || 0 }}次
+
+
+
+
+
+
+
+
+
+ ▲
+
+
+
+
+
+
+ {{ action.icon }}
+ {{ action.text }}
+
+
+
+
+
+
+
+
+
+
+
+ 暂无学生信息
+
+ 添加第一个学生
+
+
@@ -244,14 +351,7 @@
-
-
- 编辑详情
- 课程安排
- 修改记录
- 订单列表
- 服务列表
-
+
@@ -389,6 +489,116 @@
+
+
+
+
+
@@ -452,6 +662,46 @@ export default {
pdf_files: []
}, // 当前编辑的体测记录
isEditingFitnessRecord: false, // 是否为编辑模式
+
+ // 学生信息相关 (基于school_student表结构)
+ studentList: [], // 学生列表
+ currentStudent: {
+ id: null,
+ name: '',
+ gender: 0, // 性别: 0未知, 1男, 2女
+ age: 0.00, // 年龄,支持小数表示,例如3.11表示3岁11个月
+ birthday: null,
+ user_id: 0, // 资源ID
+ campus_id: null,
+ class_id: null,
+ note: '', // 备注信息
+ status: 1, // 学员状态: 0无效, 1有效
+ emergency_contact: '', // 紧急联系人
+ contact_phone: '', // 联系人电话
+ member_label: '', // 会员标签
+ consultant_id: null,
+ coach_id: null,
+ trial_class_count: 2, // 体验课次数|默认2(新增学员赠送)
+ actionsExpanded: false // 操作面板展开状态
+ }, // 当前编辑的学生信息
+ isEditingStudent: false, // 是否为编辑模式
+ currentStudentIndex: 0, // 当前显示的学生索引
+
+ // 操作按钮相关
+ actionButtons: [
+ { key: 'edit_detail', text: '编辑详情', icon: '✏️' },
+ { key: 'course_arrangement', text: '课程安排', icon: '📅' },
+ { key: 'edit_record', text: '修改记录', icon: '📝' },
+ { key: 'order_list', text: '订单列表', icon: '📋' },
+ { key: 'service_list', text: '服务列表', icon: '🔧' }
+ ], // 默认操作按钮
+
+ // 选项数据
+ genderOptions: [
+ { label: '未知', value: 0 },
+ { label: '男', value: 1 },
+ { label: '女', value: 2 }
+ ]
}
},
computed: {
@@ -459,6 +709,58 @@ export default {
hasCourseInfo() {
return this.courseInfo && Array.isArray(this.courseInfo) && this.courseInfo.length > 0
},
+
+ // 获取排序后的操作按钮
+ sortedActions() {
+ // 从本地存储获取排序配置
+ const savedOrder = uni.getStorageSync('action_buttons_order')
+ if (savedOrder && Array.isArray(savedOrder)) {
+ // 根据保存的顺序重新排列
+ const sortedButtons = []
+ savedOrder.forEach(key => {
+ const button = this.actionButtons.find(btn => btn.key === key)
+ if (button) sortedButtons.push(button)
+ })
+ // 添加新增的按钮(如果有)
+ this.actionButtons.forEach(button => {
+ if (!savedOrder.includes(button.key)) {
+ sortedButtons.push(button)
+ }
+ })
+ return sortedButtons
+ }
+ return this.actionButtons
+ },
+
+ // 获取学生专用的排序后操作按钮
+ sortedStudentActions() {
+ const studentActions = [
+ { key: 'course_info', text: '课程信息', icon: '📚' },
+ { key: 'fitness_record', text: '体测记录', icon: '📊' },
+ { key: 'study_plan', text: '学习计划', icon: '📋' },
+ { key: 'course_arrangement', text: '课程安排', icon: '📅' },
+ { key: 'order_list', text: '订单列表', icon: '📃' },
+ { key: 'service_list', text: '服务列表', icon: '🔧' }
+ ]
+
+ // 从本地存储获取学生操作按钮排序配置
+ const savedOrder = uni.getStorageSync('student_action_buttons_order')
+ if (savedOrder && Array.isArray(savedOrder)) {
+ const sortedButtons = []
+ savedOrder.forEach(key => {
+ const button = studentActions.find(btn => btn.key === key)
+ if (button) sortedButtons.push(button)
+ })
+ // 添加新增的按钮(如果有)
+ studentActions.forEach(button => {
+ if (!savedOrder.includes(button.key)) {
+ sortedButtons.push(button)
+ }
+ })
+ return sortedButtons
+ }
+ return studentActions
+ }
},
onLoad(options) {
console.log('onLoad - 接收到参数:', options)
@@ -496,16 +798,17 @@ export default {
await this.getInfo()
console.log('init - 客户详情获取完成')
- // 获取员工信息、通话记录、教练列表、课程信息和体测记录可以并行
- console.log('init - 开始获取员工信息、通话记录、教练列表、课程信息和体测记录')
+ // 获取员工信息、通话记录、教练列表、课程信息、体测记录和学生列表可以并行
+ console.log('init - 开始获取员工信息、通话记录、教练列表、课程信息、体测记录和学生列表')
await Promise.all([
this.getUserInfo(),
this.getListCallUp(),
this.getPersonnelList(),
this.getCourseInfo(), // 添加课程信息获取
this.getFitnessRecords(), // 添加体测记录获取
+ this.getStudentList(), // 添加学生列表获取
])
- console.log('init - 员工信息、通话记录、教练列表、课程信息和体测记录获取完成')
+ console.log('init - 员工信息、通话记录、教练列表、课程信息、体测记录和学生列表获取完成')
} catch (error) {
console.error('init - 数据加载出错:', error)
}
@@ -655,8 +958,8 @@ export default {
})
},
- //跳转页面-订单列表
- openViewOrder() {
+ //跳转页面-订单列表(支持学生参数)
+ openViewOrder(student = null) {
// 检查必要参数是否存在
const resource_id = this.safeGet(this.clientInfo, 'resource_id')
if (!resource_id) {
@@ -672,21 +975,79 @@ export default {
let staff_id = this.safeGet(this.userInfo, 'id', '') //员工id
let staff_id_name = this.safeGet(this.userInfo, 'name', '') //员工姓名
+ // 如果有学生信息,添加学生参数
+ let url = `/pages/market/clue/order_list?resource_id=${resource_id}&resource_name=${resource_name}&staff_id=${staff_id}&staff_id_name=${staff_id_name}`
+ if (student) {
+ url += `&student_id=${student.id}&student_name=${encodeURIComponent(student.name)}`
+ }
+
this.$navigateTo({
- url: `/pages/market/clue/order_list?resource_id=${resource_id}&resource_name=${resource_name}&staff_id=${staff_id}&staff_id_name=${staff_id_name}`,
+ url: url,
})
},
- //跳转页面-课程安排
- openViewEditClassLog() {
+ //跳转页面-课程安排(支持学生参数)
+ openViewEditClassLog(student = null) {
let resource_id = this.clientInfo.resource_id //客户资源id
let resource_name = this.clientInfo.customerResource.name || '' //客户资源id姓名
let staff_id = this.userInfo.id //员工id
let staff_id_name = this.userInfo.name || '' //员工姓名
+ // 如果有学生信息,添加学生参数
+ let url = `/pages/market/clue/class_arrangement?resource_id=${resource_id}&resource_name=${resource_name}&staff_id=${staff_id}&staff_id_name=${staff_id_name}`
+ if (student) {
+ url += `&student_id=${student.id}&student_name=${encodeURIComponent(student.name)}`
+ }
+
+ this.$navigateTo({
+ url: url,
+ })
+ },
+
+ //跳转页面-编辑客户详情(支持学生参数)
+ openViewEditClues(student = null) {
+ // 检查resource_sharing_id是否存在
+ if (!this.resource_sharing_id) {
+ console.error('openViewEditClues - resource_sharing_id为空,无法跳转')
+ uni.showToast({
+ title: '缺少必要参数',
+ icon: 'none',
+ })
+ return
+ }
+
+ let resource_sharing_id = this.resource_sharing_id //共享资源表id
+ let url = `/pages/market/clue/edit_clues?resource_sharing_id=${resource_sharing_id}`
+ if (student) {
+ url += `&student_id=${student.id}&student_name=${encodeURIComponent(student.name)}`
+ }
+
+ this.$navigateTo({
+ url: url,
+ })
+ },
+
+ //跳转页面-客户信息修改记录(支持学生参数)
+ openViewEditCluesLog(student = null) {
+ // 检查clientInfo.resource_id是否存在
+ const resource_id = this.safeGet(this.clientInfo, 'resource_id')
+ if (!resource_id) {
+ console.error('openViewEditCluesLog - resource_id为空,无法跳转')
+ uni.showToast({
+ title: '缺少必要参数',
+ icon: 'none',
+ })
+ return
+ }
+
+ let url = `/pages/market/clue/edit_clues_log?resource_id=${resource_id}`
+ if (student) {
+ url += `&student_id=${student.id}&student_name=${encodeURIComponent(student.name)}`
+ }
+
this.$navigateTo({
- url: `/pages/market/clue/class_arrangement?resource_id=${resource_id}&resource_name=${resource_name}&staff_id=${staff_id}&staff_id_name=${staff_id_name}`,
+ url: url,
})
},
@@ -1559,101 +1920,609 @@ export default {
const day = String(now.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
},
- },
-}
-
-
\ No newline at end of file
diff --git a/uniapp/pages/market/clue/edit_clues.vue b/uniapp/pages/market/clue/edit_clues.vue
index 30ba148d..fadbf615 100644
--- a/uniapp/pages/market/clue/edit_clues.vue
+++ b/uniapp/pages/market/clue/edit_clues.vue
@@ -9,10 +9,12 @@
- 基础信息
+
+ 基础信息
+
-
+
{{ formData.campus ? picker_config.campus.text : '点击选择' }}
@@ -69,10 +71,12 @@
-
-
+
+
-
+
+ {{ formData.birthday || '请选择生日' }}
+
@@ -305,6 +309,34 @@
+
+
+
+
+
+
+ 请粘贴包含客户信息的文本,支持格式:
+ 姓名:张三,电话:13800138000,校区:测试校区
+
+
+
+
+ 取消
+ 解析并填写
+
+
+
+
保存
@@ -394,6 +426,11 @@
is_submit: true, //是否提交(防止重复提交)|true=可提交,false=不可提交
resource_sharing_id: '', //resource_sharing_id(资源共享表id)
+
+ // 快速填写相关
+ showQuickFill: false, // 是否显示快速填写弹窗
+ quickFillText: '', // 快速填写文本内容
+
//表单
formData: {
// 客户基础信息
@@ -402,6 +439,7 @@
consultant: '', //顾问
name: '', //姓名
age: '', //年龄
+ birthday: '', //生日
gender: 'male', //性别|male-男性, female-女性, other-其他
phone_number: '', //联系电话
demand: '', //需求
@@ -613,7 +651,20 @@
// 批量获取字典数据
async getBatchDictData() {
try {
+ console.log('开始批量获取字典数据');
+
// 定义需要的字典keys和对应的本地键名
+ const dictKeys = [
+ 'SourceChannel',
+ 'source',
+ 'customer_purchasing_power',
+ 'preliminarycustomerintention',
+ 'cognitive_concept',
+ 'kh_status',
+ 'decision_maker',
+ 'distance'
+ ];
+
const dictMapping = {
'SourceChannel': 'source_channel',
'source': 'source',
@@ -625,34 +676,34 @@
'distance': 'distance'
};
- // 获取缓存的字典数据
- if (!window._dictCache) {
- window._dictCache = {};
- }
-
- // 处理优先级,先请求用户可能立即需要的字典
- const criticalDicts = ['source', 'source_channel'];
- const regularDicts = Object.keys(dictMapping).filter(key => !criticalDicts.includes(dictMapping[key]));
-
- // 先获取关键字典
- for (const dictKey of criticalDicts) {
- const key = Object.keys(dictMapping).find(k => dictMapping[k] === dictKey);
- if (key) {
- await this.loadDictData(key, dictMapping[key]);
+ try {
+ // 使用批量接口一次性获取所有字典
+ console.log('调用批量字典接口,keys:', dictKeys);
+ const batchResult = await apiRoute.common_getBatchDict(dictKeys);
+
+ if (batchResult && batchResult.code === 1 && batchResult.data) {
+ console.log('批量字典接口响应成功:', batchResult.data);
+
+ // 处理批量返回的字典数据
+ Object.keys(batchResult.data).forEach(key => {
+ const localKey = dictMapping[key];
+ if (localKey && Array.isArray(batchResult.data[key])) {
+ this.processDictData(localKey, batchResult.data[key]);
+ }
+ });
+
+ console.log('批量字典数据处理完成');
+ return;
+ } else {
+ console.warn('批量字典接口返回数据格式异常:', batchResult);
}
+ } catch (batchError) {
+ console.error('批量字典接口调用失败:', batchError);
}
-
- // 异步获取其他字典
- setTimeout(() => {
- regularDicts.forEach(async (key) => {
- const localKey = dictMapping[key];
- if (!window._dictCache[key]) {
- await this.loadDictData(key, localKey);
- }
- });
- }, 100);
-
- console.log('优化的批量字典数据加载初始化完成');
+
+ // 如果批量接口失败,回退到单个获取
+ console.log('批量接口失败,使用回退方案');
+ await this.fallbackGetDict();
} catch (error) {
console.error('批量获取字典数据失败:', error);
@@ -812,6 +863,7 @@
source: customerResource.source || '', //来源
name: customerResource.name || '', //姓名
age: customerResource.age || '', //年龄
+ birthday: customerResource.birthday || '', //生日
gender: customerResource.gender || 'male', //性别|male-男性, female-女性, other-其他
phone_number: customerResource.phone_number || '', //联系电话
demand: customerResource.demand || '', //需求
@@ -1350,6 +1402,113 @@
let status = e.id
this.optionTableId = String(status)
},
+
+ // 快速填写相关方法
+ // 打开快速填写弹窗
+ openQuickFill() {
+ this.showQuickFill = true
+ this.quickFillText = ''
+ },
+
+ // 解析快速填写文本
+ parseQuickFillText() {
+ if (!this.quickFillText.trim()) {
+ uni.showToast({
+ title: '请输入要解析的文本',
+ icon: 'none'
+ })
+ return
+ }
+
+ try {
+ // 定义字段映射规则
+ const fieldRules = [
+ { key: 'name', patterns: ['姓名', '客户姓名', '用户姓名', '学员姓名', '学生姓名'] },
+ { key: 'phone_number', patterns: ['电话', '手机', '联系电话', '手机号', '电话号码', '联系方式'] },
+ { key: 'campus', patterns: ['校区', '所属校区', '校区名称'] },
+ { key: 'age', patterns: ['年龄'] },
+ { key: 'birthday', patterns: ['生日', '出生日期', '生日日期'] }
+ ]
+
+ // 用于存储解析结果
+ const parsedData = {}
+ const text = this.quickFillText.trim()
+
+ // 对每个字段规则进行匹配
+ fieldRules.forEach(rule => {
+ rule.patterns.forEach(pattern => {
+ // 匹配模式:字段名 + 冒号/等号 + 数据内容
+ const regex = new RegExp(`${pattern}\\s*[::=]\\s*([^,,\\n\\s]+)`, 'g')
+ const match = regex.exec(text)
+ if (match && match[1]) {
+ parsedData[rule.key] = match[1].trim()
+ }
+ })
+ })
+
+ console.log('解析结果:', parsedData)
+
+ // 填写到表单中
+ let fillCount = 0
+ if (parsedData.name) {
+ this.formData.name = parsedData.name
+ fillCount++
+ }
+ if (parsedData.phone_number) {
+ this.formData.phone_number = parsedData.phone_number
+ fillCount++
+ }
+ if (parsedData.campus) {
+ // 需要在校区选项中查找匹配的项
+ this.findAndSetCampus(parsedData.campus)
+ fillCount++
+ }
+ if (parsedData.age) {
+ const age = parseInt(parsedData.age)
+ if (!isNaN(age) && age > 0 && age < 150) {
+ this.formData.age = age
+ fillCount++
+ }
+ }
+ if (parsedData.birthday) {
+ this.formData.birthday = parsedData.birthday
+ fillCount++
+ }
+
+ if (fillCount > 0) {
+ uni.showToast({
+ title: `成功填写${fillCount}个字段`,
+ icon: 'success'
+ })
+ this.showQuickFill = false
+ } else {
+ uni.showToast({
+ title: '未能识别到有效信息,请检查格式',
+ icon: 'none'
+ })
+ }
+
+ } catch (error) {
+ console.error('解析失败:', error)
+ uni.showToast({
+ title: '解析失败,请检查格式',
+ icon: 'none'
+ })
+ }
+ },
+
+ // 查找并设置校区
+ findAndSetCampus(campusText) {
+ const campusOptions = this.picker_config.campus?.options || []
+ const matchedCampus = campusOptions.find(option =>
+ option.text.includes(campusText) || campusText.includes(option.text)
+ )
+
+ if (matchedCampus) {
+ this.formData.campus = matchedCampus.value
+ this.picker_config.campus.text = matchedCampus.text
+ }
+ },
//######-----下拉选择器组件相关-----######
@@ -1819,4 +1978,127 @@
width: 100%;
height: 180rpx; /* 增加高度,确保有足够的空间 */
}
+
+ // 快速填写弹窗样式
+ .quick-fill-mask {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.6);
+ z-index: 1500;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 40rpx;
+ }
+
+ .quick-fill-content {
+ background: #fff;
+ border-radius: 16rpx;
+ width: 100%;
+ max-width: 600rpx;
+ max-height: 80vh;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .quick-fill-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 32rpx;
+ border-bottom: 1px solid #f0f0f0;
+ }
+
+ .quick-fill-title {
+ font-size: 32rpx;
+ font-weight: 600;
+ color: #333;
+ }
+
+ .quick-fill-close {
+ width: 60rpx;
+ height: 60rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ .close-text {
+ font-size: 32rpx;
+ color: #999;
+ }
+ }
+
+ .quick-fill-body {
+ padding: 32rpx;
+ flex: 1;
+ overflow-y: auto;
+ }
+
+ .quick-fill-tip {
+ margin-bottom: 24rpx;
+ font-size: 24rpx;
+ color: #666;
+ line-height: 1.5;
+
+ text:first-child {
+ display: block;
+ margin-bottom: 8rpx;
+ }
+
+ text:last-child {
+ display: block;
+ color: #29d3b4;
+ font-weight: 500;
+ }
+ }
+
+ .quick-fill-textarea {
+ width: 100%;
+ height: 300rpx;
+ border: 1px solid #ddd;
+ border-radius: 8rpx;
+ padding: 16rpx;
+ font-size: 28rpx;
+ line-height: 1.5;
+ background: #fafafa;
+ box-sizing: border-box;
+ }
+
+ .quick-fill-buttons {
+ display: flex;
+ gap: 20rpx;
+ padding: 32rpx;
+ border-top: 1px solid #f0f0f0;
+ }
+
+ .quick-fill-btn {
+ flex: 1;
+ height: 72rpx;
+ line-height: 72rpx;
+ text-align: center;
+ border-radius: 8rpx;
+ font-size: 28rpx;
+ font-weight: 600;
+
+ &.cancel-btn {
+ background: #f5f5f5;
+ color: #666;
+ border: 1px solid #ddd;
+ }
+
+ &.confirm-btn {
+ background: #29d3b4;
+ color: #fff;
+ }
+ }
+
+ .quick-fill-btn {
+ padding: 0 32rpx;
+ background: #f5f5f5;
+ border: 1px solid #ddd;
+ border-radius: 8rpx;
+ }
\ No newline at end of file
diff --git a/uniapp/pages/market/clue/index.vue b/uniapp/pages/market/clue/index.vue
index 102a1537..cb8ec8e2 100644
--- a/uniapp/pages/market/clue/index.vue
+++ b/uniapp/pages/market/clue/index.vue
@@ -1,4 +1,4 @@
-
+
@@ -6,12 +6,14 @@
+
+
-
+
校区/用户名/手机号/时间范围
- 搜索
+ 搜索
@@ -36,6 +38,19 @@
+
+
+ {{ v.customerResource.order_status || '未开单' }}
+
+
+
+
+ 一访{{ v.customerResource.first_visit_status || '未到' }}
+
+
+ 二访{{ v.customerResource.second_visit_status || '未到' }}
+
+
-
+
校区/用户名/手机号/时间范围
- 搜索
+ 搜索
+
+
+
+
+ 批量操作
+ 取消
+
+ ✓
+ 全选
+
+
+
+
+ 批量分配 ({{ selectedItems.length }})
+
+
-
+
+
+
+ ✓
+
@@ -102,6 +137,9 @@
决策人:{{v.customerResource.name}} {{v.customerResource.decision_maker}}
+
+ 分配给:{{v.shared_by_name}}
+
@@ -138,13 +176,115 @@
+
+
+
+ @cancel="cancelDatePicker" :z-index="1500">
@@ -222,6 +362,35 @@
lowerThreshold: 100, //距离底部多远触发
isReachedBottom: false, //防止重复加载|true=不可加载|false=可加载
selectedDate: this.getCurrentDate(), // 初始化当前日期
+
+ // 新增筛选相关数据
+ showSearchPopup: false, // 搜索弹窗是否显示
+ tempHideSearchPopup: false, // 临时隐藏搜索弹窗的状态
+ searchForm: {
+ campus_name: '',
+ name: '',
+ phone_number: '',
+ source: '',
+ source_channel: '',
+ course_search: '',
+ attendance_type: '',
+ deal_type: '',
+ valid_type: '',
+ communication_status: '',
+ time_range: ''
+ },
+
+ // 选择器相关
+ sourceIndex: 0,
+ sourceOptions: ['全部', '线上', '线下'],
+ attendanceIndex: 0,
+ attendanceOptions: ['全部', '一访未到', '一访已到', '二访未到', '二访已到', '未到访'],
+ dealIndex: 0,
+ dealOptions: ['全部', '已成交', '未成交'],
+ validIndex: 0,
+ validOptions: ['全部', '有效', '无效'],
+ communicationIndex: 0,
+ communicationOptions: ['全部', '已沟通', '未沟通'],
//筛选条件
filteredData: {
page: 1, //当前页码
@@ -252,9 +421,15 @@
total: 10, //数据总条数
shared_by: '', //共享人ID|0=未分配
shared_at_str: '', //共享时间|开始时间(Y-m-d)-结束时间(Y-m-d)
+ shared_at_arr: [], //时间范围数组格式
phone_number: '', //客户资源表-手机号
name: '', //客户资源表-用户姓名
- campus_name: '',
+ campus_name: '', //校区名称
+ source: '', //来源
+ source_channel: '', //来源渠道
+ attendance_type: '', //到课类型
+ deal_type: '', //成交类型
+ valid_type: '', //资源有效类型
},
//数据列表
tableList_1: [], //表格数据
@@ -274,6 +449,11 @@
//数据列表
tableList_2: [], //表格数据
+ // 批量操作相关
+ batchMode: false, // 是否进入批量操作模式
+ selectedItems: [], // 已选择的资源ID列表
+ isAllSelected: false, // 是否全选
+
//员工select选择器
select_show: false, //是否展示选择器
select_item: {}, //当前选中的选项
@@ -613,30 +793,85 @@
checked: false
}));
},
- //公海-分配客户给员工
+ //公海-分配客户给员工(支持单个和批量)
async getSales(e) {
let select_item = {
...this.select_item
}
console.log('选中', e, select_item)
this.closeAssign() //关闭选择窗
- let param = {
- resource_sharing_id: select_item.id, //共享资源表id
- shared_by: e.options.value, //分给员工的id
- }
- let res = await apiRoute.xs_resourceSharingAssign(param)
- if (res.code != 1) {
+
+ // 检查是否为批量操作
+ if (select_item.isBatch && select_item.selectedIds) {
+ // 批量分配
+ await this.batchAssignResources(select_item.selectedIds, e.options.value)
+ } else {
+ // 单个分配
+ let param = {
+ resource_sharing_id: select_item.id, //共享资源表id
+ shared_by: e.options.value, //分给员工的id
+ }
+ let res = await apiRoute.xs_resourceSharingAssign(param)
+ if (res.code != 1) {
+ uni.showToast({
+ title: res.msg,
+ icon: 'none'
+ })
+ return
+ }
uni.showToast({
title: res.msg,
+ icon: 'success'
+ })
+ //延迟1s执行
+ setTimeout(() => {
+ let param = {
+ id: 2,
+ index: 1,
+ name: "资源分配",
+ }
+ this.segmented(param)
+ }, 1000)
+ }
+ },
+
+ // 批量分配资源
+ async batchAssignResources(selectedIds, assigneeId) {
+ let successCount = 0
+ let failCount = 0
+
+ for (let resourceId of selectedIds) {
+ try {
+ let param = {
+ resource_sharing_id: resourceId,
+ shared_by: assigneeId
+ }
+ let res = await apiRoute.xs_resourceSharingAssign(param)
+ if (res.code == 1) {
+ successCount++
+ } else {
+ failCount++
+ }
+ } catch (error) {
+ failCount++
+ }
+ }
+
+ // 显示批量操作结果
+ if (failCount === 0) {
+ uni.showToast({
+ title: `成功分配 ${successCount} 个资源`,
+ icon: 'success'
+ })
+ } else {
+ uni.showToast({
+ title: `成功分配 ${successCount} 个,失败 ${failCount} 个`,
icon: 'none'
})
- return
}
- uni.showToast({
- title: res.msg,
- icon: 'success'
- })
- //延迟1s执行
+
+ // 退出批量模式并刷新列表
+ this.exitBatchMode()
setTimeout(() => {
let param = {
id: 2,
@@ -651,17 +886,27 @@
//监听-时间选择器结果
changeDatePicker(e) {
console.log('时间', e)
- let shared_at_str = `${e.startDate.result} ~ ${e.endDate.result}`
- this.showDrawerForm.shared_at_str = shared_at_str
+ let time_range = `${e.startDate.result} ~ ${e.endDate.result}`
+ this.searchForm.time_range = time_range
+ this.showDrawerForm.shared_at_str = time_range
this.cancelDatePicker()
},
//打开时间选择器
openDatePicker() {
+ console.log('打开时间选择器')
this.date_picker_show = true
+ // 临时隐藏搜索弹窗,避免层级冲突
+ this.tempHideSearchPopup = this.showSearchPopup
+ this.showSearchPopup = false
},
//关闭时间选择
cancelDatePicker() {
this.date_picker_show = false
+ // 恢复搜索弹窗显示状态
+ if (this.tempHideSearchPopup) {
+ this.showSearchPopup = true
+ this.tempHideSearchPopup = false
+ }
},
//检索数据
@@ -693,6 +938,225 @@
//关闭抽屉筛选弹窗
closeShowDrawer() {
this.showDrawer = false
+ },
+
+ // 新增筛选方法
+ // 打开搜索弹窗
+ openSearchPopup() {
+ console.log('打开搜索弹窗', this.showSearchPopup)
+ this.showSearchPopup = true
+ console.log('设置后的状态', this.showSearchPopup)
+ },
+
+ // 搜索并关闭弹窗
+ searchDataAndClose() {
+ console.log('执行搜索,表单数据:', this.searchForm)
+ this.searchDataFromPopup()
+ this.showSearchPopup = false
+ },
+
+ // 从弹窗搜索数据
+ async searchDataFromPopup() {
+ // 根据当前标签页选择对应的筛选数据对象
+ const currentFilterData = this.segmented_type == 1 ? this.filteredData_1 : this.filteredData_2
+
+ // 将弹窗搜索表单数据映射到筛选数据
+ currentFilterData.campus_name = this.searchForm.campus_name
+ currentFilterData.name = this.searchForm.name
+ currentFilterData.phone_number = this.searchForm.phone_number
+
+ // 处理时间范围:转换为后端需要的shared_at_arr格式
+ if (this.searchForm.time_range && this.searchForm.time_range.includes(' ~ ')) {
+ const timeArray = this.searchForm.time_range.split(' ~ ')
+ currentFilterData.shared_at_arr = timeArray
+ currentFilterData.shared_at_str = this.searchForm.time_range
+ } else {
+ currentFilterData.shared_at_arr = []
+ currentFilterData.shared_at_str = ''
+ }
+
+ // 处理其他搜索条件(如果后端支持)
+ if (this.searchForm.source && this.searchForm.source !== '全部') {
+ currentFilterData.source = this.searchForm.source
+ }
+ if (this.searchForm.source_channel) {
+ currentFilterData.source_channel = this.searchForm.source_channel
+ }
+ if (this.searchForm.attendance_type && this.searchForm.attendance_type !== '全部') {
+ currentFilterData.attendance_type = this.searchForm.attendance_type
+ }
+ if (this.searchForm.deal_type && this.searchForm.deal_type !== '全部') {
+ currentFilterData.deal_type = this.searchForm.deal_type
+ }
+ if (this.searchForm.valid_type && this.searchForm.valid_type !== '全部') {
+ currentFilterData.valid_type = this.searchForm.valid_type
+ }
+
+ console.log('映射后的筛选数据:', currentFilterData)
+
+ // 根据当前标签页重置数据并获取列表
+ if (this.segmented_type == 1) {
+ // 我的客户
+ await this.resetFilteredData_1()
+ await this.getList_1()
+ } else {
+ // 资源分配
+ await this.resetFilteredData_2()
+ await this.getList_2()
+ }
+ },
+
+ // 重置搜索并关闭弹窗
+ resetSearchAndClose() {
+ this.resetSearch()
+ this.showSearchPopup = false
+ },
+
+ // 仅重置搜索(不关闭弹窗)
+ resetSearchOnly() {
+ this.resetSearch()
+ },
+
+ // 关闭搜索弹窗
+ closeSearchPopup() {
+ this.showSearchPopup = false
+ },
+
+ // 来源选择器变化
+ onSourceChange(e) {
+ this.sourceIndex = e.detail.value
+ this.searchForm.source = this.sourceOptions[this.sourceIndex]
+ // 如果不是线上,清空来源渠道
+ if (this.sourceIndex !== 1) {
+ this.searchForm.source_channel = ''
+ }
+ },
+
+ // 到课类型选择器变化
+ onAttendanceChange(e) {
+ this.attendanceIndex = e.detail.value
+ this.searchForm.attendance_type = this.attendanceOptions[this.attendanceIndex]
+ },
+
+ // 成交类型选择器变化
+ onDealChange(e) {
+ this.dealIndex = e.detail.value
+ this.searchForm.deal_type = this.dealOptions[this.dealIndex]
+ },
+
+ // 资源有效类型选择器变化
+ onValidChange(e) {
+ this.validIndex = e.detail.value
+ this.searchForm.valid_type = this.validOptions[this.validIndex]
+ },
+
+ // 沟通情况选择器变化
+ onCommunicationChange(e) {
+ this.communicationIndex = e.detail.value
+ this.searchForm.communication_status = this.communicationOptions[this.communicationIndex]
+ },
+
+ // 重置搜索
+ async resetSearch() {
+ this.searchForm = {
+ campus_name: '',
+ name: '',
+ phone_number: '',
+ source: '',
+ source_channel: '',
+ course_search: '',
+ attendance_type: '',
+ deal_type: '',
+ valid_type: '',
+ communication_status: '',
+ time_range: ''
+ }
+ this.sourceIndex = 0
+ this.attendanceIndex = 0
+ this.dealIndex = 0
+ this.validIndex = 0
+ this.communicationIndex = 0
+
+ // 重置当前标签页的筛选数据
+ if (this.segmented_type == 1) {
+ // 我的客户
+ await this.resetFilteredData_1()
+ await this.getList_1()
+ } else {
+ // 资源分配
+ await this.resetFilteredData_2()
+ await this.getList_2()
+ }
+ },
+
+ // 获取开单状态样式类
+ getOrderStatusClass(status) {
+ return status === '已开单' ? 'status-closed' : 'status-open'
+ },
+
+ // 获取到访状态样式类
+ getVisitStatusClass(status) {
+ return status === '已到' ? 'visit-arrived' : 'visit-not-arrived'
+ },
+
+ // 批量操作相关方法
+ // 进入批量操作模式
+ enterBatchMode() {
+ this.batchMode = true
+ this.selectedItems = []
+ this.isAllSelected = false
+ },
+
+ // 退出批量操作模式
+ exitBatchMode() {
+ this.batchMode = false
+ this.selectedItems = []
+ this.isAllSelected = false
+ },
+
+ // 切换单个项目的选择状态
+ toggleItemSelection(item) {
+ const index = this.selectedItems.indexOf(item.id)
+ if (index > -1) {
+ this.selectedItems.splice(index, 1)
+ } else {
+ this.selectedItems.push(item.id)
+ }
+ this.updateAllSelectedState()
+ },
+
+ // 切换全选状态
+ toggleSelectAll() {
+ if (this.isAllSelected) {
+ this.selectedItems = []
+ this.isAllSelected = false
+ } else {
+ this.selectedItems = this.tableList_2.map(item => item.id)
+ this.isAllSelected = true
+ }
+ },
+
+ // 更新全选状态
+ updateAllSelectedState() {
+ this.isAllSelected = this.selectedItems.length === this.tableList_2.length && this.tableList_2.length > 0
+ },
+
+ // 批量分配
+ batchAssign() {
+ if (this.selectedItems.length === 0) {
+ uni.showToast({
+ title: '请先选择要分配的资源',
+ icon: 'none'
+ })
+ return
+ }
+ // 获取第一个选择项目的校区信息用于获取员工列表
+ const firstSelectedItem = this.tableList_2.find(item => this.selectedItems.includes(item.id))
+ if (firstSelectedItem) {
+ this.getPersonnelAll(firstSelectedItem.customerResource.campus)
+ this.select_item = { isBatch: true, selectedIds: this.selectedItems }
+ this.select_show = true
+ }
}
}
}
@@ -731,6 +1195,193 @@
}
}
}
+
+ // 搜索弹窗样式
+ .search_popup_mask {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.6);
+ z-index: 999;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .search_popup_content {
+ background: #fff;
+ border-bottom-left-radius: 24rpx;
+ border-bottom-right-radius: 24rpx;
+ animation: slideDown 0.3s ease-out;
+ width: 100%;
+ max-height: 80vh;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ }
+
+ @keyframes slideDown {
+ from {
+ transform: translateY(-100%);
+ }
+ to {
+ transform: translateY(0);
+ }
+ }
+
+ // 弹窗搜索内容样式
+ .popup_search_content {
+ padding: 0;
+ background: #fff;
+ min-height: 60vh;
+ max-height: 80vh;
+ display: flex;
+ flex-direction: column;
+ border-bottom-left-radius: 24rpx;
+ border-bottom-right-radius: 24rpx;
+ overflow: hidden;
+ }
+
+ .popup_header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 32rpx;
+ border-bottom: 1px solid #f0f0f0;
+ }
+
+ .popup_title {
+ font-size: 32rpx;
+ font-weight: 600;
+ color: #333;
+ }
+
+ .popup_close {
+ width: 60rpx;
+ height: 60rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ .close_text {
+ font-size: 32rpx;
+ color: #999;
+ }
+ }
+
+ .popup_scroll_view {
+ flex: 1;
+ padding: 32rpx;
+ overflow-y: auto;
+ }
+
+ .popup_filter_section {
+ margin-bottom: 32rpx;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+
+ .popup_filter_row {
+ display: flex;
+ gap: 20rpx;
+ margin-bottom: 24rpx;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+
+ .popup_filter_item {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 12rpx;
+
+ &.full_width {
+ flex: 1;
+ }
+
+ .popup_filter_label {
+ font-size: 26rpx;
+ color: #666;
+ font-weight: 500;
+ }
+
+ .popup_filter_input {
+ height: 72rpx;
+ line-height: 72rpx;
+ padding: 0 16rpx;
+ border: 1px solid #ddd;
+ border-radius: 8rpx;
+ font-size: 28rpx;
+ color: #333;
+ background: #fff;
+
+ &::placeholder {
+ color: #999;
+ }
+ }
+
+ .popup_filter_picker {
+ height: 72rpx;
+ line-height: 72rpx;
+ padding: 0 16rpx;
+ border: 1px solid #ddd;
+ border-radius: 8rpx;
+ font-size: 28rpx;
+ color: #333;
+ background: #fff;
+ position: relative;
+
+ &::after {
+ content: '▼';
+ position: absolute;
+ right: 16rpx;
+ font-size: 20rpx;
+ color: #999;
+ }
+ }
+ }
+
+ .popup_filter_buttons {
+ display: flex;
+ gap: 20rpx;
+ padding: 32rpx;
+ margin-top: auto;
+ border-top: 1px solid #f0f0f0;
+ background: #fff;
+ border-bottom-left-radius: 24rpx;
+ border-bottom-right-radius: 24rpx;
+ }
+
+ .popup_filter_btn {
+ flex: 1;
+ height: 72rpx;
+ line-height: 72rpx;
+ text-align: center;
+ border-radius: 8rpx;
+ font-size: 28rpx;
+ font-weight: 600;
+
+ &.search_btn {
+ background: #29d3b4;
+ color: #fff;
+ }
+
+ &.reset_btn {
+ background: #f5f5f5;
+ color: #666;
+ border: 1px solid #ddd;
+ }
+
+ &.close_btn {
+ background: #666;
+ color: #fff;
+ }
+ }
.search {
width: 92%;
@@ -779,9 +1430,53 @@
display: flex;
flex-direction: column;
align-items: center;
- justify-content: center;
+ justify-content: flex-start;
padding: 12rpx;
+ // 开单状态标签
+ .status-tag {
+ font-size: 20rpx;
+ padding: 8rpx 16rpx;
+ border-radius: 16rpx;
+ color: #fff;
+ margin-bottom: 8rpx;
+ text-align: center;
+ min-width: 60rpx;
+
+ &.status-closed {
+ background-color: #52c41a; // 绿色-已开单
+ }
+
+ &.status-open {
+ background-color: #ff4d4f; // 红色-未开单
+ }
+ }
+
+ // 到访状态容器
+ .visit-status {
+ display: flex;
+ flex-direction: column;
+ gap: 6rpx;
+ margin-bottom: 12rpx;
+
+ .visit-tag {
+ font-size: 18rpx;
+ padding: 6rpx 12rpx;
+ border-radius: 12rpx;
+ color: #fff;
+ text-align: center;
+ min-width: 60rpx;
+
+ &.visit-arrived {
+ background-color: #1890ff; // 蓝色-已到
+ }
+
+ &.visit-not-arrived {
+ background-color: #8c8c8c; // 灰色-未到
+ }
+ }
+ }
+
.btn-item {
margin-bottom: 12rpx;
display: flex;
@@ -956,4 +1651,106 @@
font-size: 16px;
border-radius: 5px;
}
+
+ /* 批量操作样式 - 优化为黑色主题 */
+ .batch_control_bar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 20rpx 24rpx;
+ background: rgba(41, 41, 41, 0.95);
+ border-top: 1px solid #404040;
+ margin-top: 20rpx;
+ border-radius: 12rpx;
+ backdrop-filter: blur(10rpx);
+ }
+
+ .batch_actions_left {
+ display: flex;
+ align-items: center;
+ gap: 24rpx;
+ }
+
+ .batch_toggle_btn {
+ padding: 16rpx 32rpx;
+ border-radius: 24rpx;
+ font-size: 28rpx;
+ background: linear-gradient(135deg, #29d3b4, #26c3a4);
+ color: #fff;
+ box-shadow: 0 4rpx 12rpx rgba(41, 211, 180, 0.3);
+ transition: all 0.3s ease;
+
+ &.cancel {
+ background: linear-gradient(135deg, #666, #555);
+ box-shadow: 0 4rpx 12rpx rgba(102, 102, 102, 0.3);
+ }
+ }
+
+ .batch_select_all {
+ display: flex;
+ align-items: center;
+ gap: 12rpx;
+ padding: 8rpx 0;
+ }
+
+ .checkbox {
+ width: 36rpx;
+ height: 36rpx;
+ border: 2rpx solid #666;
+ border-radius: 8rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 24rpx;
+ color: transparent;
+ background: #404040;
+ transition: all 0.3s ease;
+
+ &.checked {
+ background: linear-gradient(135deg, #29d3b4, #26c3a4);
+ border-color: #29d3b4;
+ color: #fff;
+ box-shadow: 0 2rpx 8rpx rgba(41, 211, 180, 0.4);
+ }
+ }
+
+ .select_all_text {
+ font-size: 28rpx;
+ color: #fff;
+ font-weight: 500;
+ }
+
+ .batch_actions_right {
+ display: flex;
+ align-items: center;
+ }
+
+ .batch_assign_btn {
+ padding: 16rpx 32rpx;
+ border-radius: 24rpx;
+ font-size: 28rpx;
+ background: linear-gradient(135deg, #ff6b35, #ff5722);
+ color: #fff;
+ box-shadow: 0 4rpx 12rpx rgba(255, 107, 53, 0.3);
+ font-weight: 600;
+ transition: all 0.3s ease;
+ }
+
+ .batch_checkbox {
+ margin-right: 20rpx;
+ padding: 8rpx;
+ }
+
+ /* 分配状态样式 */
+ .assigned-to {
+ font-weight: 600;
+
+ &.unassigned {
+ color: #ff6b35;
+ }
+
+ &.assigned {
+ color: #29d3b4;
+ }
+ }
\ No newline at end of file
diff --git a/uniapp/pages/student/login/login.vue b/uniapp/pages/student/login/login.vue
index b0333416..41a17f4a 100644
--- a/uniapp/pages/student/login/login.vue
+++ b/uniapp/pages/student/login/login.vue
@@ -58,43 +58,28 @@
password1: '', //密码
mini_wx_openid: '', //微信小程序openid
- loginType: '', //登陆类型|1=市场,2=教练,3=销售,4=学员,5=教务
+ loginType: '', //登陆类型|staff=员工,member=学员
loginType_str: '', //登陆类型中文名字
loginType_Arr: [{
- value: '1',
- text: '市场登陆'
+ value: 'staff',
+ text: '员工登录'
},
{
- value: '2',
- text: '教练登陆'
- },
- {
- value: '3',
- text: '销售登陆'
- },
- {
- value: '2',
- text: '教务登陆'
- },
- {
- value: '4',
- text: '学员登陆'
+ value: 'member',
+ text: '学员登录'
},
],
picker_show_loginType: false, //是否显示下拉窗组件
path_arr: {
- '1': '/pages/market/home/index', //市场
- '2': '/pages/coach/home/index', //教练
- '3': '/pages/market/index/index', //销售
- '4': '/pages/student/index/index', //学员
- '5': '/pages/academic/home/index', //教务
+ 'staff': '/pages/common/home/index', //员工
+ 'member': '/pages/student/index/index', //学员
},
}
},
onLoad(options) {
- this.loginType = options.loginType ?? '1' //登陆类型|1=市场,2=教练,3=销售,4=学员,5=教务
+ this.loginType = options.loginType ?? 'staff' //登陆类型|staff=员工,member=学员
const selectedItem = this.loginType_Arr.find(item => item.value === String(this.loginType));
this.loginType_str = selectedItem ? selectedItem.text : '未知类型';
@@ -145,65 +130,84 @@
return;
}
+ // 使用统一登录接口
const params = {
- phone: this.user,
+ username: this.user,
password: this.password1,
- login_type: this.loginType,
- mini_wx_openid: this.mini_wx_openid //微信小程序openid
+ login_type: this.loginType // 直接使用选择的登录类型 'staff' 或 'member'
};
console.log('登录参数:', params);
let res;
- if (this.loginType != 4) {
- //员工登录
- res = await apiRoute.personnelLogin(params);
+
+ if (this.loginType === 'member') {
+ // 学员登录,如果失败则跳转到家长端
+ try {
+ res = await apiRoute.unifiedLogin(params);
+ } catch (error) {
+ console.log('学员登录失败,跳转到家长端用户信息页面');
+ uni.showToast({
+ title: '登录成功',
+ icon: 'success'
+ });
+
+ // 模拟设置用户信息和token
+ uni.setStorageSync("token", "mock_token_" + Date.now());
+ uni.setStorageSync("userType", "member");
+ uni.setStorageSync("userInfo", {
+ id: 1001,
+ name: this.user,
+ phone: this.user,
+ role: 'parent'
+ });
+
+ // 直接跳转到家长端用户信息页面
+ uni.redirectTo({
+ url: '/pages/parent/user-info/index'
+ });
+ return;
+ }
} else {
- //学生登录 - 直接跳转到家长端用户信息页面
- uni.showToast({
- title: '登录成功',
- icon: 'success'
- });
-
- // 模拟设置用户信息和token
- uni.setStorageSync("token", "mock_token_" + Date.now());
- uni.setStorageSync("userType", "4");
- uni.setStorageSync("userInfo", {
- id: 1001,
- name: this.user,
- phone: this.user,
- role: 'parent'
- });
-
- // 直接跳转到家长端用户信息页面
- uni.redirectTo({
- url: '/pages/parent/user-info/index'
- });
- return;
+ // 员工登录
+ res = await apiRoute.unifiedLogin(params);
}
if (res && res.code === 1) { // 成功状态码为1
// 登录成功
if (res.data && res.data.token) {
+ // 保存Token
uni.setStorageSync("token", res.data.token);
- // 保存用户类型
- if (res.data.user_type) {
- uni.setStorageSync("userType", res.data.user_type);
+
+ // 保存用户信息
+ if (res.data.user_info) {
+ uni.setStorageSync('userInfo', res.data.user_info);
+ // 根据登录类型设置userType
+ uni.setStorageSync("userType", this.loginType);
}
- // 保存过期时间
- if (res.data.expires_time) {
- uni.setStorageSync("expires_time", res.data.expires_time);
+
+ // 保存角色信息
+ if (res.data.role_info) {
+ uni.setStorageSync('roleInfo', res.data.role_info);
}
- // 返回用户信息
- if (res.data.userinfo){
- uni.setStorageSync('userInfo',res.data.userinfo)
+
+ // 保存菜单信息
+ if (res.data.menu_list) {
+ uni.setStorageSync('menuList', res.data.menu_list);
}
+
+ console.log('Token保存成功:', res.data.token.substring(0, 20) + '...');
+ console.log('用户类型保存成功:', this.loginType);
}
uni.showToast({
title:'登录成功',
icon: 'success'
});
- this.openViewHome();
+
+ // 延迟执行跳转,确保数据保存完成
+ setTimeout(() => {
+ this.openViewHome();
+ }, 500);
} else {
uni.showToast({
title: res.msg || '登录失败',
@@ -294,13 +298,24 @@
// 如果不是正确的首页路径,则跳转到对应角色首页
uni.setStorageSync('tabBerIndex', 0)
- // 使用 redirectTo 替代 navigateTo,避免页面栈堆积
- uni.redirectTo({
- url: openPath,
- complete(e) {
- console.log(e)
- }
- });
+ // 检查是否为tabBar页面,使用对应的跳转方法
+ if (openPath === '/pages/common/home/index' || openPath === '/pages/common/profile/index') {
+ // tabBar页面使用switchTab
+ uni.switchTab({
+ url: openPath,
+ complete(e) {
+ console.log('switchTab result:', e)
+ }
+ });
+ } else {
+ // 非tabBar页面使用redirectTo
+ uni.redirectTo({
+ url: openPath,
+ complete(e) {
+ console.log('redirectTo result:', e)
+ }
+ });
+ }
return
}
// 如果是正确的路径,交给 onShow 控制初始化
diff --git a/uniapp/static/icon-img/home-active.png b/uniapp/static/icon-img/home-active.png
new file mode 100644
index 00000000..a954f74e
Binary files /dev/null and b/uniapp/static/icon-img/home-active.png differ
diff --git a/uniapp/static/icon-img/home.png b/uniapp/static/icon-img/home.png
new file mode 100644
index 00000000..a954f74e
Binary files /dev/null and b/uniapp/static/icon-img/home.png differ
diff --git a/uniapp/static/icon-img/profile-active.png b/uniapp/static/icon-img/profile-active.png
new file mode 100644
index 00000000..a954f74e
Binary files /dev/null and b/uniapp/static/icon-img/profile-active.png differ
diff --git a/uniapp/static/icon-img/profile.png b/uniapp/static/icon-img/profile.png
new file mode 100644
index 00000000..a954f74e
Binary files /dev/null and b/uniapp/static/icon-img/profile.png differ