diff --git a/lib/api/api_path.dart b/lib/api/api_path.dart index baf687f..214cfc7 100644 --- a/lib/api/api_path.dart +++ b/lib/api/api_path.dart @@ -1,6 +1,7 @@ class ApiPath { static const String sendValidationCode = "/v1/common/sendValidationCode"; static const String validationCodeLogin = "/v1/user/codeLogin"; + static const String userDetail = "/v1/user/detail"; static const String passwordLogin = "/v1/user/pwdLogin"; static const String allTeamList = "/v1/team/teamListAll"; static const String sceneList = "/v1/team/sceneList"; @@ -9,4 +10,10 @@ class ApiPath { static const String bindTeamStarCloudAccount = "/v1/team/bindStarCloudAccount"; static const String updateTeamInfo = "/v1/team/updateTeam"; static const String getInviteInfo = "/v1/team/getInviteInfo"; + static const String getTeamInviteConfig = "/v1/team/teamApplyConfig"; + static const String updateTeamInviteConfig = "/v1/team/teamPersonConfigUpdate"; + static const String departList = "/v1/team/departList"; + static const String departCreate = "/v1/team/departCreate"; + static const String roleList = "/v1/team/roleList"; + static const String roleCreate = "/v1/team/roleCreate"; } diff --git a/lib/api/model/team/request/create_new_depart_request.dart b/lib/api/model/team/request/create_new_depart_request.dart new file mode 100644 index 0000000..762383e --- /dev/null +++ b/lib/api/model/team/request/create_new_depart_request.dart @@ -0,0 +1,32 @@ +class CreateNewDepartRequest { + String departName; + String parentDepartNo; + List leader; + + CreateNewDepartRequest({ + required this.departName, + required this.parentDepartNo, + required this.leader, + }); + + Map toJson() { + return { + "departName": departName, + "parentDepartNo": parentDepartNo, + "leader": leader, + }; + } + + static CreateNewDepartRequest fromJson(Map json) { + return CreateNewDepartRequest( + departName: json["departName"], + parentDepartNo: json["parentDepartNo"], + leader: json["leader"], + ); + } + + @override + String toString() { + return 'CreateNewDepartRequest{departName: $departName, parentDepartNo: $parentDepartNo, leader: $leader}'; + } +} diff --git a/lib/api/model/team/request/create_new_role_request.dart b/lib/api/model/team/request/create_new_role_request.dart new file mode 100644 index 0000000..3b87064 --- /dev/null +++ b/lib/api/model/team/request/create_new_role_request.dart @@ -0,0 +1,32 @@ +class CreateNewRoleRequest { + String roleName; // 必填 + String? roleDesc; // 可选 + List? personNos; // 可选 + + CreateNewRoleRequest({ + required this.roleName, // 必填字段使用 required + this.roleDesc, // 可选字段不使用 required + this.personNos, // 可选字段不使用 required + }); + + Map toJson() { + final Map data = {}; + data['roleName'] = roleName; + + // 只有当字段不为 null 时才添加到 JSON 中 + if (roleDesc != null) { + data['roleDesc'] = roleDesc; + } + + if (personNos != null) { + data['personNos'] = personNos; + } + + return data; + } + + @override + String toString() { + return 'CreateNewRoleRequest{roleName: $roleName, roleDesc: $roleDesc, personNos: $personNos}'; + } +} diff --git a/lib/api/model/team/request/get_depart_list_request.dart b/lib/api/model/team/request/get_depart_list_request.dart new file mode 100644 index 0000000..d131a76 --- /dev/null +++ b/lib/api/model/team/request/get_depart_list_request.dart @@ -0,0 +1,19 @@ +class GetDepartListRequest { + String departNo; + + GetDepartListRequest({ + required this.departNo, + }); + + Map toJson() { + return { + "departNo": departNo, + }; + } + + factory GetDepartListRequest.fromJson(Map json) { + return GetDepartListRequest( + departNo: json['departNo'], + ); + } +} diff --git a/lib/api/model/team/request/role_list_request.dart b/lib/api/model/team/request/role_list_request.dart new file mode 100644 index 0000000..55106ed --- /dev/null +++ b/lib/api/model/team/request/role_list_request.dart @@ -0,0 +1,18 @@ +class RoleListRequest { + String teamNo; + + RoleListRequest({ + required this.teamNo, + }); + + Map toJson() { + final Map data = {}; + data['teamNo'] = teamNo; + return data; + } + + @override + String toString() { + return 'RoleListRequest{teamNo: $teamNo}'; + } +} diff --git a/lib/api/model/team/request/update_invite_parameter_config_request.dart b/lib/api/model/team/request/update_invite_parameter_config_request.dart new file mode 100644 index 0000000..3fc84a5 --- /dev/null +++ b/lib/api/model/team/request/update_invite_parameter_config_request.dart @@ -0,0 +1,27 @@ +class UpdateInviteParameterConfigRequest { + final int joinTeamInputFace; + final int applyJoinTeamAudit; + final int teamInviteUserValidFalse; + + UpdateInviteParameterConfigRequest({ + required this.joinTeamInputFace, + required this.applyJoinTeamAudit, + required this.teamInviteUserValidFalse, + }); + + Map toJson() { + return { + "joinTeamInputFace": joinTeamInputFace, + "applyJoinTeamAudit": applyJoinTeamAudit, + "teamInviteUserValidFalse": teamInviteUserValidFalse, + }; + } + + factory UpdateInviteParameterConfigRequest.fromJson(Map json) { + return UpdateInviteParameterConfigRequest( + joinTeamInputFace: json['joinTeamInputFace'] as int, + applyJoinTeamAudit: json['applyJoinTeamAudit'] as int, + teamInviteUserValidFalse: json['teamInviteUserValidFalse'] as int, + ); + } +} diff --git a/lib/api/model/team/response/depart_list_reponse.dart b/lib/api/model/team/response/depart_list_reponse.dart new file mode 100644 index 0000000..23c0dbd --- /dev/null +++ b/lib/api/model/team/response/depart_list_reponse.dart @@ -0,0 +1,170 @@ +import 'package:starwork_flutter/api/model/team/response/role_list_response.dart'; + +class DepartListResponse { + final List? departList; + + DepartListResponse({this.departList}); + + factory DepartListResponse.fromJson(Map json) { + if (json['departList'] == null) { + return DepartListResponse(departList: []); + } + + final List list = json['departList']; + return DepartListResponse( + departList: list.map((item) => DepartItem.fromJson(item as Map)).toList(), + ); + } + + Map toJson() { + return { + 'departList': departList?.map((item) => item.toJson()).toList(), + }; + } + + @override + String toString() { + return 'DepartListResponse{departList: $departList}'; + } +} + +class DepartItem { + final int? id; + final String? teamNo; + final String? departNo; + final String? departName; + final int? parentId; + final PersonItem? leader; + final int? level; + final int? sort; + final bool? hasLeaf; + final int? personNum; + final List? persons; // 新增字段 + + DepartItem({ + this.id, + this.teamNo, + this.departNo, + this.departName, + this.parentId, + this.leader, + this.level, + this.sort, + this.hasLeaf, + this.personNum, + this.persons, + }); + + factory DepartItem.fromJson(Map json) { + List? personsList; + if (json['persons'] != null && json['persons'] is List) { + personsList = (json['persons'] as List).map((item) => PersonItem.fromJson(item as Map)).toList(); + } + // 处理 leader 字段(单个 PersonItem) + PersonItem? leaderItem; + if (json['leader'] != null && json['leader'] is Map) { + try { + leaderItem = PersonItem.fromJson(json['leader'] as Map); + } catch (e) { + // 如果转换失败,leaderItem 保持为 null + leaderItem = null; + } + } + + return DepartItem( + id: json['id'] as int?, + teamNo: json['teamNo'] as String?, + departNo: json['departNo'] as String?, + departName: json['departName'] as String?, + parentId: json['parentId'] as int?, + leader: leaderItem, + level: json['level'] as int?, + sort: json['sort'] as int?, + hasLeaf: json['hasLeaf'] as bool?, + personNum: json['personNum'] as int?, + persons: personsList, + ); + } + + Map toJson() { + return { + 'id': id, + 'teamNo': teamNo, + 'departNo': departNo, + 'departName': departName, + 'parentId': parentId, + 'leader': leader?.toJson(), // 序列化单个 leader 对象 + 'level': level, + 'sort': sort, + 'hasLeaf': hasLeaf, + 'personNum': personNum, + 'persons': persons?.map((item) => item.toJson()).toList(), + }; + } + + @override + String toString() { + return 'DepartItem{id: $id, teamNo: $teamNo, departNo: $departNo, departName: $departName, parentId: $parentId, leader: $leader, level: $level, sort: $sort, hasLeaf: $hasLeaf, personNum: $personNum, persons: $persons}'; + } +} + +// 新增 PersonItem 类 +class PersonItem { + final int? id; + final String? personName; + final int? userId; + final int? jobNumber; + final int? sex; + final String? personNo; + final String? phone; + final List? roles; + + PersonItem({ + this.id, + this.personName, + this.userId, + this.jobNumber, + this.sex, + this.personNo, + this.phone, + this.roles, + }); + + factory PersonItem.fromJson(Map json) { + List? rolesList; + if (json['roles'] != null && json['roles'] is List) { + rolesList = (json['roles'] as List) + .map((item) => RoleListResponse.fromJson(item as Map)) + .toList(); + } + + return PersonItem( + id: json['id'] as int?, + personName: json['personName'] as String?, + userId: json['userId'] as int?, + jobNumber: json['jobNumber'] as int?, + sex: json['sex'] as int?, + personNo: json['personNo'] as String?, + phone: json['phone'] as String?, + roles: rolesList, + ); + } + + Map toJson() { + return { + 'id': id, + 'personName': personName, + 'userId': userId, + 'jobNumber': jobNumber, + 'sex': sex, + 'personNo': personNo, + 'phone': phone, + 'roles': roles?.map((item) => item.toJson()).toList(), + }; + } + + @override + String toString() { + return 'PersonItem{id: $id, personName: $personName, userId: $userId, jobNumber: $jobNumber, sex: $sex, personNo: $personNo, phone: $phone, roles: $roles}'; + } +} diff --git a/lib/api/model/team/response/role_list_response.dart b/lib/api/model/team/response/role_list_response.dart new file mode 100644 index 0000000..9ee5b56 --- /dev/null +++ b/lib/api/model/team/response/role_list_response.dart @@ -0,0 +1,79 @@ + + +import 'package:starwork_flutter/api/model/team/response/depart_list_reponse.dart'; + +class RoleListResponse { + int? id; + String? teamNo; + String? roleName; + String? roleDesc; + int? roleStatus; + String? roleStatusName; + int? isDefault; + int? isSuper; + int? isOperationRole; + List? personList; // 添加 personList 属性 + + + RoleListResponse({ + this.id, + this.teamNo, + this.roleName, + this.roleDesc, + this.roleStatus, + this.roleStatusName, + this.isDefault, + this.isSuper, + this.isOperationRole, + }); + + RoleListResponse.fromJson(Map json) { + id = json['id']; + teamNo = json['teamNo']; + roleName = json['roleName']; + roleDesc = json['roleDesc']; + roleStatus = json['roleStatus']; + roleStatusName = json['roleStatusName']; + isDefault = json['isDefault']; + isSuper = json['isSuper']; + isOperationRole = json['isOperationRole']; + // 处理 personList + if (json['personList'] != null && json['personList'] is List) { + personList = (json['personList'] as List) + .map((item) => PersonItem.fromJson(item as Map)) + .toList(); + } + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['teamNo'] = teamNo; + data['roleName'] = roleName; + data['roleDesc'] = roleDesc; + data['roleStatus'] = roleStatus; + data['roleStatusName'] = roleStatusName; + data['isDefault'] = isDefault; + data['isSuper'] = isSuper; + data['isOperationRole'] = isOperationRole; + // 序列化 personList + if (personList != null) { + data['personList'] = personList!.map((item) => item.toJson()).toList(); + } + return data; + } + // 添加序列化集合并返回 List> 的静态方法 + static List> toJsonList(List items) { + return items.map((item) => item.toJson()).toList(); + } + + // 添加从 List 反序列化集合并返回 List 的静态方法 + static List fromJsonList(List jsonList) { + return jsonList.map((item) => RoleListResponse.fromJson(item as Map)).toList(); + } + + @override + String toString() { + return 'RoleListResponse{id: $id, teamNo: $teamNo, roleName: $roleName, roleDesc: $roleDesc, roleStatus: $roleStatus, roleStatusName: $roleStatusName, isDefault: $isDefault, isSuper: $isSuper, isOperationRole: $isOperationRole}'; + } +} diff --git a/lib/api/model/team/response/team_invite_parameter_config_response.dart b/lib/api/model/team/response/team_invite_parameter_config_response.dart new file mode 100644 index 0000000..ec5f19a --- /dev/null +++ b/lib/api/model/team/response/team_invite_parameter_config_response.dart @@ -0,0 +1,36 @@ +class TeamInviteParameterConfigResponse { + String? teamNo; + int? applyJoinTeamAudit; + int? joinTeamInputFace; + int? teamInviteUserValidFalse; + + TeamInviteParameterConfigResponse({ + this.teamNo, + this.applyJoinTeamAudit, + this.joinTeamInputFace, + this.teamInviteUserValidFalse, + }); + + factory TeamInviteParameterConfigResponse.fromJson(Map json) { + return TeamInviteParameterConfigResponse( + teamNo: json['teamNo'] as String?, + applyJoinTeamAudit: json['applyJoinTeamAudit'] as int?, + joinTeamInputFace: json['joinTeamInputFace'] as int?, + teamInviteUserValidFalse: json['teamInviteUserValidFalse'] as int?, + ); + } + + Map toJson() { + return { + 'teamNo': teamNo, + 'applyJoinTeamAudit': applyJoinTeamAudit, + 'joinTeamInputFace': joinTeamInputFace, + 'teamInviteUserValidFalse': teamInviteUserValidFalse, + }; + } + + @override + String toString() { + return 'TeamInviteParameterConfigResponse{teamNo: $teamNo, applyJoinTeamAudit: $applyJoinTeamAudit, joinTeamInputFace: $joinTeamInputFace, teamInviteUserValidFalse: $teamInviteUserValidFalse}'; + } +} diff --git a/lib/api/model/user/response/user_info_response.dart b/lib/api/model/user/response/user_info_response.dart new file mode 100644 index 0000000..680e9bb --- /dev/null +++ b/lib/api/model/user/response/user_info_response.dart @@ -0,0 +1,132 @@ +class UserInfoResponse { + final int? id; + final String? accountNo; + final String? countryCode; + final String? phone; + final String? nickname; + final String? headUrl; + final String? createdAt; + final String? lastLoginTime; + final String? currentTeamNo; + final List? teamList; + + UserInfoResponse({ + this.id, + this.accountNo, + this.countryCode, + this.phone, + this.nickname, + this.headUrl, + this.createdAt, + this.lastLoginTime, + this.currentTeamNo, + this.teamList, + }); + + factory UserInfoResponse.fromJson(Map json) { + List? teamList; + if (json['teamList'] != null && json['teamList'] is List) { + teamList = (json['teamList'] as List) + .map((item) => TeamInfo.fromJson(item as Map)) + .toList(); + } + + return UserInfoResponse( + id: json['id'] as int?, + accountNo: json['accountNo'] as String?, + countryCode: json['countryCode'] as String?, + phone: json['phone'] as String?, + nickname: json['nickname'] as String?, + headUrl: json['headUrl'] as String?, + createdAt: json['createdAt'] as String?, + lastLoginTime: json['lastLoginTime'] as String?, + currentTeamNo: json['currentTeamNo'] as String?, + teamList: teamList, + ); + } + + Map toJson() { + return { + 'id': id, + 'accountNo': accountNo, + 'countryCode': countryCode, + 'phone': phone, + 'nickname': nickname, + 'headUrl': headUrl, + 'createdAt': createdAt, + 'lastLoginTime': lastLoginTime, + 'currentTeamNo': currentTeamNo, + 'teamList': teamList?.map((item) => item.toJson()).toList(), + }; + } + + @override + String toString() { + return 'UserInfoResponse{id: $id, accountNo: $accountNo, countryCode: $countryCode, phone: $phone, nickname: $nickname, headUrl: $headUrl, createdAt: $createdAt, lastLoginTime: $lastLoginTime, currentTeamNo: $currentTeamNo, teamList: $teamList}'; + } +} + +class TeamInfo { + final int? id; + final String? teamNo; + final String? teamName; + final String? teamCode; + final String? owner; + final String? createdAt; + final int? scene; + final String? sceneCustomName; + final String? personNo; + final String? personName; + final bool? isOwner; + + TeamInfo({ + this.id, + this.teamNo, + this.teamName, + this.teamCode, + this.owner, + this.createdAt, + this.scene, + this.sceneCustomName, + this.personNo, + this.personName, + this.isOwner, + }); + + factory TeamInfo.fromJson(Map json) { + return TeamInfo( + id: json['id'] as int?, + teamNo: json['teamNo'] as String?, + teamName: json['teamName'] as String?, + teamCode: json['teamCode'] as String?, + owner: json['owner'] as String?, + createdAt: json['createdAt'] as String?, + scene: json['scene'] as int?, + sceneCustomName: json['sceneCustomName'] as String?, + personNo: json['personNo'] as String?, + personName: json['personName'] as String?, + isOwner: json['isOwner'] as bool?, + ); + } + + Map toJson() { + return { + 'id': id, + 'teamNo': teamNo, + 'teamName': teamName, + 'teamCode': teamCode, + 'owner': owner, + 'createdAt': createdAt, + 'scene': scene, + 'sceneCustomName': sceneCustomName, + 'personNo': personNo, + 'personName': personName, + 'isOwner': isOwner, + }; + } + + @override + String toString() { + return 'TeamInfo{id: $id, teamNo: $teamNo, teamName: $teamName, teamCode: $teamCode, owner: $owner, createdAt: $createdAt, scene: $scene, sceneCustomName: $sceneCustomName, personNo: $personNo, personName: $personName, isOwner: $isOwner}'; + } +} diff --git a/lib/api/service/team_api_service.dart b/lib/api/service/team_api_service.dart index 45841cb..6bc713d 100644 --- a/lib/api/service/team_api_service.dart +++ b/lib/api/service/team_api_service.dart @@ -4,13 +4,21 @@ import 'package:starwork_flutter/api/api_response.dart'; import 'package:starwork_flutter/api/base_api_service.dart'; import 'package:starwork_flutter/api/model/team/request/bind_team_star_cloud_account_request.dart'; import 'package:starwork_flutter/api/model/team/request/change_current_team_request.dart'; +import 'package:starwork_flutter/api/model/team/request/create_new_depart_request.dart'; +import 'package:starwork_flutter/api/model/team/request/create_new_role_request.dart'; import 'package:starwork_flutter/api/model/team/request/create_team_request.dart'; +import 'package:starwork_flutter/api/model/team/request/get_depart_list_request.dart'; import 'package:starwork_flutter/api/model/team/request/invite_info_request.dart'; +import 'package:starwork_flutter/api/model/team/request/role_list_request.dart'; +import 'package:starwork_flutter/api/model/team/request/update_invite_parameter_config_request.dart'; import 'package:starwork_flutter/api/model/team/request/update_team_info_request.dart'; import 'package:starwork_flutter/api/model/team/response/all_team_list_response.dart'; import 'package:starwork_flutter/api/model/team/response/create_team_response.dart'; +import 'package:starwork_flutter/api/model/team/response/depart_list_reponse.dart'; import 'package:starwork_flutter/api/model/team/response/invite_info_response.dart'; +import 'package:starwork_flutter/api/model/team/response/role_list_response.dart'; import 'package:starwork_flutter/api/model/team/response/scene_info_response.dart'; +import 'package:starwork_flutter/api/model/team/response/team_invite_parameter_config_response.dart'; import 'package:starwork_flutter/api/model/user/request/validation_code_login.dart'; import 'package:starwork_flutter/api/model/user/response/token_response.dart'; import 'package:starwork_flutter/common/constant/http_constant.dart'; @@ -25,7 +33,6 @@ class TeamApiService { required CreateTeamRequest request, }) { return _api.makeRequest( - // 通过实例调用 path: ApiPath.createTeam, method: HttpConstant.post, data: request.toJson(), @@ -36,7 +43,6 @@ class TeamApiService { // 所有团队列表 Future> requestAllTeamInfoList() { return _api.makeRequest( - // 通过实例调用 path: ApiPath.allTeamList, method: HttpConstant.post, fromJson: (data) => AllTeamListResponse.fromJson(data), @@ -46,7 +52,6 @@ class TeamApiService { // 所有团队使用场景列表 Future> requestAllSceneInfoList() { return _api.makeRequest( - // 通过实例调用 path: ApiPath.sceneList, method: HttpConstant.post, fromJson: (data) => SceneInfoResponseList.fromJson(data), @@ -58,7 +63,6 @@ class TeamApiService { required ChangeCurrentTeamRequest request, }) { return _api.makeRequest( - // 通过实例调用 path: ApiPath.changeTeam, method: HttpConstant.post, data: request.toJson(), @@ -71,7 +75,6 @@ class TeamApiService { required BindTeamStarCloudAccountRequest request, }) { return _api.makeRequest( - // 通过实例调用 path: ApiPath.bindTeamStarCloudAccount, method: HttpConstant.post, data: request.toJson(), @@ -84,7 +87,6 @@ class TeamApiService { required UpdateTeamInfoRequest request, }) { return _api.makeRequest( - // 通过实例调用 path: ApiPath.updateTeamInfo, method: HttpConstant.post, data: request.toJson(), @@ -97,11 +99,76 @@ class TeamApiService { required InviteInfoRequest request, }) { return _api.makeRequest( - // 通过实例调用 path: ApiPath.getInviteInfo, method: HttpConstant.post, data: request.toJson(), fromJson: (data) => InviteInfoResponse.fromJson(data), ); } + + // 请求邀请参数信息配置 + Future> requestInviteParameterConfig() { + return _api.makeRequest( + path: ApiPath.getTeamInviteConfig, + method: HttpConstant.post, + fromJson: (data) => TeamInviteParameterConfigResponse.fromJson(data), + ); + } + + // 请求邀请参数信息修改 + Future> requestUpdateInviteParameterConfig({ + required UpdateInviteParameterConfigRequest request, + }) { + return _api.makeRequest( + path: ApiPath.updateTeamInviteConfig, + method: HttpConstant.post, + data: request.toJson(), + fromJson: (data) {}, + ); + } + + // 获取组织列表 + Future> requestDepartList({ + required GetDepartListRequest request, + }) { + return _api.makeRequest( + path: ApiPath.departList, + method: HttpConstant.post, + data: request.toJson(), + fromJson: (data) => DepartListResponse.fromJson(data), + ); + } + + // 获取组织列表 + Future> requestCreateDepart({ + required CreateNewDepartRequest request, + }) { + return _api.makeRequest( + path: ApiPath.departCreate, + method: HttpConstant.post, + data: request.toJson(), + fromJson: (data) {}, + ); + } + + // 获取团队角色列表 + Future>> requestTeamRoleList() { + return _api.makeRequest( + path: ApiPath.roleList, + method: HttpConstant.post, + fromJson: (data) => RoleListResponse.fromJsonList(data), + ); + } + + // 创建新的角色 + Future> requestCreateTeamRole({ + required CreateNewRoleRequest request, + }) { + return _api.makeRequest( + path: ApiPath.roleCreate, + method: HttpConstant.post, + data: request.toJson(), + fromJson: (data) {}, + ); + } } diff --git a/lib/api/service/user_api_service.dart b/lib/api/service/user_api_service.dart index dfa157a..291a3fb 100644 --- a/lib/api/service/user_api_service.dart +++ b/lib/api/service/user_api_service.dart @@ -4,6 +4,7 @@ import 'package:starwork_flutter/api/api_response.dart'; import 'package:starwork_flutter/api/base_api_service.dart'; import 'package:starwork_flutter/api/model/user/request/validation_code_login.dart'; import 'package:starwork_flutter/api/model/user/response/token_response.dart'; +import 'package:starwork_flutter/api/model/user/response/user_info_response.dart'; import 'package:starwork_flutter/common/constant/http_constant.dart'; class UserApiService { @@ -23,4 +24,14 @@ class UserApiService { fromJson: (data) => LoginResponse.fromJson(data), ); } + + // 用户信息 + Future> requestUserInfo() { + return _api.makeRequest( + // 通过实例调用 + path: ApiPath.userDetail, + method: HttpConstant.post, + fromJson: (data) => UserInfoResponse.fromJson(data), + ); + } } diff --git a/lib/app.dart b/lib/app.dart index 692c0a2..26d5847 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -40,14 +40,14 @@ class _AppState extends State { checkboxTheme: CheckboxThemeData( side: const BorderSide( color: Colors.grey, - width: 1, // 设置边框粗细 + width: 0.5, // 设置边框粗细 ), visualDensity: VisualDensity.compact, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.r), + borderRadius: BorderRadius.circular(10.r), ), checkColor: MaterialStateProperty.all(Colors.white), - fillColor: MaterialStateProperty.all(Colors.blue), + fillColor: MaterialStateProperty.all(Colors.white), ), appBarTheme: AppBarTheme( backgroundColor: Colors.white, diff --git a/lib/common/constant/app_view_parameter_keys.dart b/lib/common/constant/app_view_parameter_keys.dart index 34b0c0f..0800969 100644 --- a/lib/common/constant/app_view_parameter_keys.dart +++ b/lib/common/constant/app_view_parameter_keys.dart @@ -3,5 +3,6 @@ class AppViewParameterKeys { static const String lockInfo = "lockInfo"; static const String networkInfo = "networkInfo"; static const String teamInfo = "teamInfo"; + static const String departItem = "departItem"; } \ No newline at end of file diff --git a/lib/common/constant/cache_keys.dart b/lib/common/constant/cache_keys.dart index e26af58..fee15fb 100644 --- a/lib/common/constant/cache_keys.dart +++ b/lib/common/constant/cache_keys.dart @@ -8,5 +8,6 @@ class CacheKeys { static const String starCloudPassword = 'starCloudPassword'; static const String starCloudUid = 'starCloudUid'; static const String starCloudUserLoginInfo = 'starCloudUserLoginInfo'; + static const String userAccountInfo = 'userAccountInfo'; } diff --git a/lib/flavors.dart b/lib/flavors.dart index 64fc357..7da7cca 100644 --- a/lib/flavors.dart +++ b/lib/flavors.dart @@ -21,7 +21,7 @@ class F { // Release环境的API地址 switch (appFlavor) { case Flavor.sky: - return 'https://192.168.1.138:8112/api'; // 生产环境API + return 'https://192.168.1.137:8112/api'; // 生产环境API case Flavor.xhj: return 'https://api.xhjcn.ltd/api'; // 生产环境API } @@ -29,7 +29,7 @@ class F { // Debug/Profile环境的API地址(开发环境) switch (appFlavor) { case Flavor.sky: - return 'http://192.168.1.138:8112/api'; + return 'http://192.168.1.137:8112/api'; case Flavor.xhj: return 'https://loacl.work.star-lock.cn/api'; } @@ -67,7 +67,7 @@ class F { // Debug/Profile环境的StarCloud地址(开发环境) switch (appFlavor) { case Flavor.sky: - return 'http://192.168.1.138:8111/sdk'; + return 'http://192.168.1.137:8111/sdk'; case Flavor.xhj: return 'http://local.cloud.star-lock.cn'; } diff --git a/lib/routes/app_pages.dart b/lib/routes/app_pages.dart index 368e8eb..06ad2c1 100644 --- a/lib/routes/app_pages.dart +++ b/lib/routes/app_pages.dart @@ -36,12 +36,22 @@ import 'package:starwork_flutter/views/main/main_binding.dart'; import 'package:starwork_flutter/views/main/main_view.dart'; import 'package:starwork_flutter/views/team/addPerson/add_person_binding.dart'; import 'package:starwork_flutter/views/team/addPerson/add_person_view.dart'; +import 'package:starwork_flutter/views/team/addPerson/selectRole/select_role_binding.dart'; +import 'package:starwork_flutter/views/team/addPerson/selectRole/select_role_view.dart'; +import 'package:starwork_flutter/views/team/addRole/add_role_binding.dart'; +import 'package:starwork_flutter/views/team/addRole/add_role_view.dart'; import 'package:starwork_flutter/views/team/inviteTeamMember/invitationSettings/invitation_settings_binding.dart'; import 'package:starwork_flutter/views/team/inviteTeamMember/invitationSettings/invitation_settings_view.dart'; import 'package:starwork_flutter/views/team/inviteTeamMember/invite_team_member_binding.dart'; import 'package:starwork_flutter/views/team/inviteTeamMember/invite_team_member_view.dart'; import 'package:starwork_flutter/views/team/joinTeam/join_team_binding.dart'; import 'package:starwork_flutter/views/team/joinTeam/join_team_view.dart'; +import 'package:starwork_flutter/views/team/personnelManage/personnel_manage_binding.dart'; +import 'package:starwork_flutter/views/team/personnelManage/personnel_manage_view.dart'; +import 'package:starwork_flutter/views/team/roleManage/role_manage_binding.dart'; +import 'package:starwork_flutter/views/team/roleManage/role_manage_view.dart'; +import 'package:starwork_flutter/views/team/selectOrganization/select_organization_binding.dart'; +import 'package:starwork_flutter/views/team/selectOrganization/select_organization_view.dart'; import 'package:starwork_flutter/views/team/teamManage/teamInfo/team_info_binding.dart'; import 'package:starwork_flutter/views/team/teamManage/teamInfo/team_info_view.dart'; import 'package:starwork_flutter/views/team/teamManage/team_manage_binding.dart'; @@ -207,5 +217,30 @@ class AppPages { page: () => AddPersonView(), binding: AddPersonBinding(), ), + GetPage( + name: AppRoutes.teamRoleManage, + page: () => RoleManageView(), + binding: RoleManageBinding(), + ), + GetPage( + name: AppRoutes.teamPersonnelManage, + page: () => PersonnelManageView(), + binding: PersonnelManageBinding(), + ), + GetPage( + name: AppRoutes.teamSelectOrganization, + page: () => SelectOrganizationView(), + binding: SelectOrganizationBinding(), + ), + GetPage( + name: AppRoutes.teamAddRole, + page: () => AddRoleView(), + binding: AddRoleBinding(), + ), + GetPage( + name: AppRoutes.teamSelectRole, + page: () => SelectRoleView(), + binding: SelectRoleBinding(), + ), ]; } diff --git a/lib/routes/app_routes.dart b/lib/routes/app_routes.dart index 19a0d77..f480351 100644 --- a/lib/routes/app_routes.dart +++ b/lib/routes/app_routes.dart @@ -18,6 +18,11 @@ class AppRoutes{ static const String teamInviteTeamMember = '/team/inviteTeamMember'; static const String teamInvitationSettings = '/team/invitationSettings'; static const String teamAddPerson = '/team/addPerson'; + static const String teamRoleManage = '/team/roleManage'; + static const String teamSelectOrganization = '/team/selectOrganization'; + static const String teamPersonnelManage = '/team/personnelManage'; + static const String teamAddRole = '/team/addRole'; + static const String teamSelectRole = '/team/selectRole'; static const String deviceManage = '/device/deviceManage'; static const String searchDevice = '/device/searchDevice'; static const String confirmPairDevice = '/device/confirmPairDevice'; diff --git a/lib/views/home/widget/home_statistics_row_widget.dart b/lib/views/home/widget/home_statistics_row_widget.dart index 3ec7628..4c02e22 100644 --- a/lib/views/home/widget/home_statistics_row_widget.dart +++ b/lib/views/home/widget/home_statistics_row_widget.dart @@ -24,6 +24,9 @@ class HomeStatisticsRowWidget extends StatelessWidget { backgroundColor: const Color(0xFFCEF2F5), textColor: const Color(0xFF134347), buttonText: '人员管理', + onTap: () { + Get.toNamed(AppRoutes.teamPersonnelManage); + }, ), SizedBox(width: 8.w), // 卡片之间的间距 _buildStatisticsCard( @@ -33,6 +36,9 @@ class HomeStatisticsRowWidget extends StatelessWidget { backgroundColor: const Color(0xFFD4E0FF), textColor: const Color(0xFF172A5B), buttonText: '设备管理', + onTap: () { + Get.toNamed(AppRoutes.deviceManage); + }, ), ], ); @@ -45,13 +51,12 @@ class HomeStatisticsRowWidget extends StatelessWidget { required Color backgroundColor, required Color textColor, required String buttonText, + required GestureTapCallback onTap, }) { return Expanded( // 使用Expanded让卡片自适应宽度 child: GestureDetector( - onTap: (){ - Get.toNamed(AppRoutes.deviceManage); - }, + onTap: onTap, child: Container( height: 62.h, padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), diff --git a/lib/views/main/main_controller.dart b/lib/views/main/main_controller.dart index 94f2d44..d177871 100644 --- a/lib/views/main/main_controller.dart +++ b/lib/views/main/main_controller.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; @@ -11,12 +12,16 @@ import 'package:starcloud/sdk/starcloud.dart'; import 'package:starwork_flutter/api/model/team/request/bind_team_star_cloud_account_request.dart'; import 'package:starwork_flutter/api/model/team/request/change_current_team_request.dart'; import 'package:starwork_flutter/api/model/team/response/team_info_response.dart'; +import 'package:starwork_flutter/api/model/user/response/user_info_response.dart'; import 'package:starwork_flutter/api/service/team_api_service.dart'; +import 'package:starwork_flutter/api/service/user_api_service.dart'; import 'package:starwork_flutter/base/app_logger.dart'; import 'package:starwork_flutter/base/base_controller.dart'; import 'package:starwork_flutter/common/constant/app_images.dart'; +import 'package:starwork_flutter/common/constant/cache_keys.dart'; import 'package:starwork_flutter/common/events/refresh_device_list_event.dart'; import 'package:starwork_flutter/common/utils/event_bus_util.dart'; +import 'package:starwork_flutter/common/utils/shared_preferences_utils.dart'; import 'package:starwork_flutter/routes/app_routes.dart'; import 'package:starwork_flutter/views/home/home_controller.dart'; import 'package:starwork_flutter/views/home/home_view.dart'; @@ -25,6 +30,7 @@ import 'package:starwork_flutter/views/mine/mine_view.dart'; class MainController extends BaseController { final teamApi = Get.find(); + final userApi = Get.find(); GlobalKey scaffoldKey = GlobalKey(); @@ -62,6 +68,9 @@ class MainController extends BaseController { /// 请求设备列表 _requestTeamDeviceList(); + + /// 请求账户信息 + requestUserAccountInfo(); // 监听刷新设备列表事件 _refreshDeviceListSubscription = EventBusUtil().instance.on().listen((event) { _requestTeamDeviceList(); @@ -232,4 +241,23 @@ class MainController extends BaseController { }, ); } + + void requestUserAccountInfo() async { + var userInfo = await userApi.requestUserInfo(); + if (userInfo.isSuccess) { + // 保存到缓存 + // 方式1: 直接存储 JSON 字符串(推荐) + await SharedPreferencesUtils.setString(CacheKeys.userAccountInfo, jsonEncode(userInfo.data!.toJson())); + // 从缓存中读取 + // String? cachedJson = await SharedPreferencesUtils.getString(CacheKeys.userAccountInfo); + // if (cachedJson != null) { + // try { + // Map jsonMap = jsonDecode(cachedJson); + // UserInfoResponse userInfoResponse = UserInfoResponse.fromJson(jsonMap); + // } catch (e) { + // AppLogger.error('JSON 解析错误: $e'); + // } + // } + } + } } diff --git a/lib/views/team/addPerson/add_person_controller.dart b/lib/views/team/addPerson/add_person_controller.dart index 372e8d4..8c64d63 100644 --- a/lib/views/team/addPerson/add_person_controller.dart +++ b/lib/views/team/addPerson/add_person_controller.dart @@ -1,8 +1,44 @@ +import 'package:flutter/widgets.dart'; import 'package:get/get.dart'; import 'package:get/get_rx/get_rx.dart'; import 'package:get/get_rx/src/rx_types/rx_types.dart'; +import 'package:starwork_flutter/api/model/team/response/depart_list_reponse.dart'; +import 'package:starwork_flutter/api/model/team/response/role_list_response.dart'; import 'package:starwork_flutter/base/base_controller.dart'; +import 'package:starwork_flutter/common/constant/app_view_parameter_keys.dart'; class AddPersonController extends BaseController { RxString selectedGender = 'male'.obs; + + var selectedDepartItem = DepartItem().obs; // 当前选中的组织 + + TextEditingController nameInputController = TextEditingController(); + TextEditingController jobNoInputController = TextEditingController(); // 工号 + TextEditingController postInputController = TextEditingController(); // 职务 + TextEditingController idCardInputController = TextEditingController(); // 身份证号码 + var isOpeningAccount = false.obs; // 是否开通账户 + var selectedRoles = [].obs; + + + @override + void onReady() { + super.onReady(); + // 读取参数 + final args = Get.arguments; + if (args != null && args.containsKey(AppViewParameterKeys.departItem)) { + final json = args[AppViewParameterKeys.departItem]; + selectedDepartItem.value = DepartItem.fromJson(json); + } + } + // 获取选中角色的显示文本 + String getSelectedRoleDisplayText() { + if (selectedRoles.isEmpty) { + return '请选择'; // 如果没有选中角色,显示"请选择" + } else { + // 将所有选中角色的名称用逗号连接 + return selectedRoles + .map((role) => role.roleName ?? '') + .join('、'); // 使用顿号或逗号分隔 + } + } } diff --git a/lib/views/team/addPerson/add_person_view.dart b/lib/views/team/addPerson/add_person_view.dart index d3d2c44..8b7267b 100644 --- a/lib/views/team/addPerson/add_person_view.dart +++ b/lib/views/team/addPerson/add_person_view.dart @@ -2,460 +2,501 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; +import 'package:starwork_flutter/api/model/team/response/depart_list_reponse.dart'; +import 'package:starwork_flutter/api/model/team/response/role_list_response.dart'; +import 'package:starwork_flutter/base/app_logger.dart'; import 'package:starwork_flutter/common/constant/app_colors.dart'; import 'package:starwork_flutter/common/widgets/custom_cell_list_widget.dart'; import 'package:starwork_flutter/common/widgets/custom_cell_widget.dart'; import 'package:starwork_flutter/common/widgets/custome_app_bar_wdiget.dart'; import 'package:starwork_flutter/extension/function_extension.dart'; +import 'package:starwork_flutter/routes/app_routes.dart'; import 'add_person_controller.dart'; class AddPersonView extends GetView { @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: AppColors.scaffoldBackgroundColor, - appBar: CustomAppBarWidget( - title: '添加人员'.tr, + return GestureDetector( + onTap: () { + FocusScope.of(context).requestFocus(FocusNode()); + }, + child: Scaffold( backgroundColor: AppColors.scaffoldBackgroundColor, - ), - body: SafeArea( - child: SingleChildScrollView( - child: Padding( - padding: EdgeInsets.only( - left: 10.w, - right: 10.w, - bottom: 10.h, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '基本信息'.tr, - style: TextStyle( - fontSize: 12.sp, - color: Colors.black54, - fontWeight: FontWeight.w400, - ), - ), - SizedBox( - height: 6.h, - ), - CustomCellListWidget( - children: [ - CustomCellWidget( - onTap: () {}, - leftText: '组织'.tr, - leftIcon: Icon( - Icons.circle, - size: 4.w, - color: Colors.red, - ), - rightWidget: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - '请选择', - style: TextStyle( - fontSize: 14.sp, - color: Colors.black54, - fontWeight: FontWeight.w400, - ), - ), - SizedBox( - width: 4.w, - ), - Icon( - Icons.arrow_forward_ios_rounded, - size: 16.sp, - color: Colors.grey[300], - ) - ], - ), - ), - CustomCellWidget( - onTap: () {}, - leftText: '姓名'.tr, - leftIcon: Icon( - Icons.circle, - size: 4.w, - color: Colors.red, - ), - rightWidget: Expanded( - flex: 3, - child: TextField( - keyboardType: TextInputType.text, - textInputAction: TextInputAction.next, - textAlign: TextAlign.end, - style: TextStyle( - fontSize: 14.sp, - ), - decoration: InputDecoration( - isCollapsed: true, - hintText: '请输入姓名'.tr, - hintStyle: TextStyle( - fontSize: 14.sp, - color: Colors.black54, - fontWeight: FontWeight.w400, - ), - // 设置无边框 - border: InputBorder.none, - contentPadding: EdgeInsets.zero, - // 获取焦点时的边框 - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, - ), - ), - ), - ), - CustomCellWidget( - onTap: () {}, - leftText: '开通账号'.tr, - leftSubText: '可登录并使用应用或管理功能', - leftIcon: Icon( - Icons.circle, - size: 4.w, - color: Colors.red, - ), - rightWidget: CupertinoSwitch( - value: false, - onChanged: (bool value) {}, - activeColor: Colors.blue, // 可选:打开时的颜色(iOS 默认为系统蓝色,可自定义) - trackColor: Colors.grey, // 可选:关闭时的背景轨道颜色 - ), - ), - CustomCellWidget( - onTap: () {}, - leftText: '分配权限'.tr, - leftIcon: Icon( - Icons.circle, - size: 4.w, - color: Colors.red, - ), - rightWidget: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - '企业员工', - style: TextStyle( - fontSize: 14.sp, - color: Colors.black54, - fontWeight: FontWeight.w400, - ), - ), - SizedBox( - width: 4.w, - ), - Icon( - Icons.arrow_forward_ios_rounded, - size: 16.sp, - color: Colors.grey[300], - ) - ], - ), - ) - ], - ), - SizedBox( - height: 10.h, - ), - Text( - '扩展信息'.tr, - style: TextStyle( - fontSize: 12.sp, - color: Colors.black54, - fontWeight: FontWeight.w400, - ), - ), - SizedBox( - height: 10.h, - ), - CustomCellListWidget( - children: [ - CustomCellWidget( - onTap: () {}, - leftText: '工号'.tr, - rightWidget: Expanded( - flex: 3, - child: TextField( - keyboardType: TextInputType.text, - textInputAction: TextInputAction.next, - textAlign: TextAlign.end, - style: TextStyle( - fontSize: 14.sp, - ), - decoration: InputDecoration( - isCollapsed: true, - hintText: '选填'.tr, - hintStyle: TextStyle( - fontSize: 14.sp, - color: Colors.black54, - fontWeight: FontWeight.w400, - ), - // 设置无边框 - border: InputBorder.none, - contentPadding: EdgeInsets.zero, - // 获取焦点时的边框 - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, - ), - ), - ), - ), - CustomCellWidget( - onTap: () {}, - leftText: '性别'.tr, - rightWidget: Obx( - () => Row( - children: [ - Radio( - value: 'male', - activeColor: Colors.blue, - groupValue: controller.selectedGender.value, - visualDensity: VisualDensity.compact, - onChanged: (value) { - controller.selectedGender.value = value!; - }, - ), - Text('男'), - Radio( - value: 'female', - activeColor: Colors.blue, - groupValue: controller.selectedGender.value, - visualDensity: VisualDensity.compact, - onChanged: (value) { - controller.selectedGender.value = value!; - }, - ), - Text('女'), - ], - ), - ), - ), - CustomCellWidget( - onTap: () {}, - leftText: '有效期'.tr, - rightWidget: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - '请选择', - style: TextStyle( - fontSize: 14.sp, - color: Colors.black54, - fontWeight: FontWeight.w400, - ), - ), - SizedBox( - width: 4.w, - ), - Icon( - Icons.arrow_forward_ios_rounded, - size: 16.sp, - color: Colors.grey[300], - ) - ], - ), - ), - CustomCellWidget( - onTap: () {}, - leftText: '职务'.tr, - rightWidget: Expanded( - flex: 3, - child: TextField( - keyboardType: TextInputType.text, - textInputAction: TextInputAction.next, - textAlign: TextAlign.end, - style: TextStyle( - fontSize: 14.sp, - ), - decoration: InputDecoration( - isCollapsed: true, - hintText: '选填'.tr, - hintStyle: TextStyle( - fontSize: 14.sp, - color: Colors.black54, - fontWeight: FontWeight.w400, - ), - // 设置无边框 - border: InputBorder.none, - contentPadding: EdgeInsets.zero, - // 获取焦点时的边框 - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, - ), - ), - ), - ), - CustomCellWidget( - onTap: () {}, - leftText: '备注'.tr, - rightWidget: Expanded( - flex: 3, - child: TextField( - keyboardType: TextInputType.text, - textInputAction: TextInputAction.next, - textAlign: TextAlign.end, - style: TextStyle( - fontSize: 14.sp, - ), - decoration: InputDecoration( - isCollapsed: true, - hintText: '选填'.tr, - hintStyle: TextStyle( - fontSize: 14.sp, - color: Colors.black54, - fontWeight: FontWeight.w400, - ), - // 设置无边框 - border: InputBorder.none, - contentPadding: EdgeInsets.zero, - // 获取焦点时的边框 - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, - ), - ), - ), - ), - CustomCellWidget( - onTap: () {}, - leftText: '身份证号码'.tr, - rightWidget: Expanded( - flex: 3, - child: TextField( - keyboardType: TextInputType.text, - textInputAction: TextInputAction.next, - textAlign: TextAlign.end, - style: TextStyle( - fontSize: 14.sp, - ), - decoration: InputDecoration( - isCollapsed: true, - hintText: '选填'.tr, - hintStyle: TextStyle( - fontSize: 14.sp, - color: Colors.black54, - fontWeight: FontWeight.w400, - ), - // 设置无边框 - border: InputBorder.none, - contentPadding: EdgeInsets.zero, - // 获取焦点时的边框 - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, - ), - ), - ), - ), - ], - ), - SizedBox( - height: 10.h, - ), - SizedBox( - width: double.infinity, - child: Text( - textAlign: TextAlign.center, - '温馨提示:人脸/指纹/卡片信息需先保存后录入', + appBar: CustomAppBarWidget( + title: '添加人员'.tr, + backgroundColor: AppColors.scaffoldBackgroundColor, + ), + body: SafeArea( + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.only( + left: 10.w, + right: 10.w, + bottom: 10.h, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '基本信息'.tr, style: TextStyle( fontSize: 12.sp, - color: Colors.grey, + color: Colors.black54, fontWeight: FontWeight.w400, ), ), - ), - SizedBox( - height: 10.h, - ), - CustomCellListWidget( - children: [ - CustomCellWidget( - onTap: () { - controller.showFunctionNotOpen(); - }, - leftText: '其他添加方式'.tr, - rightWidget: Icon( - Icons.arrow_forward_ios_rounded, - size: 16.sp, - color: Colors.grey, - ), - ), - ], - ), - ], - ), - Column( - children: [ - Row( - children: [ - Checkbox( - value: true, - // 转换为 bool - onChanged: (bool? value) {}, - ), - Text( - '向用户发送短信邀请通知', - style: TextStyle( - fontSize: 12.sp, - color: Colors.grey, - fontWeight: FontWeight.w400, - ), - ) - ], - ), - SizedBox( - width: double.infinity, - child: Row( + SizedBox( + height: 6.h, + ), + CustomCellListWidget( children: [ - Expanded( - child: ElevatedButton( - onPressed: () {}.debounce(), - style: ElevatedButton.styleFrom( - backgroundColor: Colors.grey[100], - padding: EdgeInsets.symmetric(vertical: 12.h), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.r), + CustomCellWidget( + onTap: () async { + final result = await Get.toNamed(AppRoutes.teamSelectOrganization); + if (result != null) { + AppLogger.highlight('result:${result}'); + if (result is DepartItem) { + controller.selectedDepartItem.value = result; + } + } + }, + leftText: '组织'.tr, + leftIcon: Icon( + Icons.circle, + size: 4.w, + color: Colors.red, + ), + rightWidget: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Obx( + () => Text( + controller.selectedDepartItem.value.departName ?? '请选择', + style: TextStyle( + fontSize: 14.sp, + color: Colors.black54, + fontWeight: FontWeight.w400, + ), + ), ), - ), - child: Text( - '保存'.tr, + SizedBox( + width: 4.w, + ), + Icon( + Icons.arrow_forward_ios_rounded, + size: 16.sp, + color: Colors.grey[300], + ) + ], + ), + ), + CustomCellWidget( + onTap: () {}, + leftText: '姓名'.tr, + leftIcon: Icon( + Icons.circle, + size: 4.w, + color: Colors.red, + ), + rightWidget: Expanded( + flex: 3, + child: TextField( + controller: controller.nameInputController, + keyboardType: TextInputType.text, + textInputAction: TextInputAction.next, + textAlign: TextAlign.end, style: TextStyle( - fontSize: 16.sp, - color: Colors.blue, - fontWeight: FontWeight.w500, + fontSize: 14.sp, + ), + decoration: InputDecoration( + isCollapsed: true, + hintText: '请输入姓名'.tr, + hintStyle: TextStyle( + fontSize: 14.sp, + color: Colors.black54, + fontWeight: FontWeight.w400, + ), + // 设置无边框 + border: InputBorder.none, + contentPadding: EdgeInsets.zero, + // 获取焦点时的边框 + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, ), ), ), ), - SizedBox( - width: 10.w, + CustomCellWidget( + onTap: () {}, + leftText: '开通账号'.tr, + leftSubText: '可登录并使用应用或管理功能', + leftIcon: Icon( + Icons.circle, + size: 4.w, + color: Colors.red, + ), + rightWidget: CupertinoSwitch( + value: true, + onChanged: (bool value) {}, + activeColor: Colors.blue, // 可选:打开时的颜色(iOS 默认为系统蓝色,可自定义) + trackColor: Colors.grey, // 可选:关闭时的背景轨道颜色 + ), ), - Expanded( - child: ElevatedButton( - onPressed: () {}.debounce(), - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, - padding: EdgeInsets.symmetric(vertical: 12.h), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.r)), + CustomCellWidget( + onTap: () async { + var result = + await Get.toNamed(AppRoutes.teamSelectRole, arguments: controller.selectedRoles); + if (result != null) { + // 处理返回的角色数据 + if (result is List) { + controller.selectedRoles.value = result; + controller.selectedRoles.refresh(); + } + } + }, + leftText: '分配权限'.tr, + leftIcon: Icon( + Icons.circle, + size: 4.w, + color: Colors.red, + ), + rightWidget: Container( + constraints: BoxConstraints( + maxWidth: 200.w, ), - child: Text( - '保存并继续添加'.tr, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Obx( + () => Expanded( + child: Text( + controller.getSelectedRoleDisplayText(), + style: TextStyle( + fontSize: 14.sp, + color: Colors.black54, + fontWeight: FontWeight.w400, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ), + SizedBox( + width: 4.w, + ), + Icon( + Icons.arrow_forward_ios_rounded, + size: 16.sp, + color: Colors.grey[300], + ) + ], + ), + ), + ) + ], + ), + SizedBox( + height: 10.h, + ), + Text( + '扩展信息'.tr, + style: TextStyle( + fontSize: 12.sp, + color: Colors.black54, + fontWeight: FontWeight.w400, + ), + ), + SizedBox( + height: 10.h, + ), + CustomCellListWidget( + children: [ + CustomCellWidget( + onTap: () {}, + leftText: '工号'.tr, + rightWidget: Expanded( + flex: 3, + child: TextField( + keyboardType: TextInputType.text, + textInputAction: TextInputAction.next, + textAlign: TextAlign.end, style: TextStyle( - fontSize: 16.sp, - color: Colors.white, - fontWeight: FontWeight.w500, + fontSize: 14.sp, + ), + decoration: InputDecoration( + isCollapsed: true, + hintText: '选填'.tr, + hintStyle: TextStyle( + fontSize: 14.sp, + color: Colors.black54, + fontWeight: FontWeight.w400, + ), + // 设置无边框 + border: InputBorder.none, + contentPadding: EdgeInsets.zero, + // 获取焦点时的边框 + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + ), + ), + ), + ), + CustomCellWidget( + onTap: () {}, + leftText: '性别'.tr, + rightWidget: Obx( + () => Row( + children: [ + Radio( + value: 'male', + activeColor: Colors.blue, + groupValue: controller.selectedGender.value, + visualDensity: VisualDensity.compact, + onChanged: (value) { + controller.selectedGender.value = value!; + }, + ), + Text('男'), + Radio( + value: 'female', + activeColor: Colors.blue, + groupValue: controller.selectedGender.value, + visualDensity: VisualDensity.compact, + onChanged: (value) { + controller.selectedGender.value = value!; + }, + ), + Text('女'), + ], + ), + ), + ), + CustomCellWidget( + onTap: () {}, + leftText: '有效期'.tr, + rightWidget: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + '请选择', + style: TextStyle( + fontSize: 14.sp, + color: Colors.black54, + fontWeight: FontWeight.w400, + ), + ), + SizedBox( + width: 4.w, + ), + Icon( + Icons.arrow_forward_ios_rounded, + size: 16.sp, + color: Colors.grey[300], + ) + ], + ), + ), + CustomCellWidget( + onTap: () {}, + leftText: '职务'.tr, + rightWidget: Expanded( + flex: 3, + child: TextField( + keyboardType: TextInputType.text, + textInputAction: TextInputAction.next, + textAlign: TextAlign.end, + style: TextStyle( + fontSize: 14.sp, + ), + decoration: InputDecoration( + isCollapsed: true, + hintText: '选填'.tr, + hintStyle: TextStyle( + fontSize: 14.sp, + color: Colors.black54, + fontWeight: FontWeight.w400, + ), + // 设置无边框 + border: InputBorder.none, + contentPadding: EdgeInsets.zero, + // 获取焦点时的边框 + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + ), + ), + ), + ), + CustomCellWidget( + onTap: () {}, + leftText: '备注'.tr, + rightWidget: Expanded( + flex: 3, + child: TextField( + keyboardType: TextInputType.text, + textInputAction: TextInputAction.next, + textAlign: TextAlign.end, + style: TextStyle( + fontSize: 14.sp, + ), + decoration: InputDecoration( + isCollapsed: true, + hintText: '选填'.tr, + hintStyle: TextStyle( + fontSize: 14.sp, + color: Colors.black54, + fontWeight: FontWeight.w400, + ), + // 设置无边框 + border: InputBorder.none, + contentPadding: EdgeInsets.zero, + // 获取焦点时的边框 + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + ), + ), + ), + ), + CustomCellWidget( + onTap: () {}, + leftText: '身份证号码'.tr, + rightWidget: Expanded( + flex: 3, + child: TextField( + keyboardType: TextInputType.text, + textInputAction: TextInputAction.next, + textAlign: TextAlign.end, + style: TextStyle( + fontSize: 14.sp, + ), + decoration: InputDecoration( + isCollapsed: true, + hintText: '选填'.tr, + hintStyle: TextStyle( + fontSize: 14.sp, + color: Colors.black54, + fontWeight: FontWeight.w400, + ), + // 设置无边框 + border: InputBorder.none, + contentPadding: EdgeInsets.zero, + // 获取焦点时的边框 + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, ), ), ), ), ], ), - ), - ], - ) - ], + SizedBox( + height: 10.h, + ), + SizedBox( + width: double.infinity, + child: Text( + textAlign: TextAlign.center, + '温馨提示:人脸/指纹/卡片信息需先保存后录入', + style: TextStyle( + fontSize: 12.sp, + color: Colors.grey, + fontWeight: FontWeight.w400, + ), + ), + ), + SizedBox( + height: 10.h, + ), + CustomCellListWidget( + children: [ + CustomCellWidget( + onTap: () { + controller.showFunctionNotOpen(); + }, + leftText: '其他添加方式'.tr, + rightWidget: Icon( + Icons.arrow_forward_ios_rounded, + size: 16.sp, + color: Colors.grey, + ), + ), + ], + ), + ], + ), + Column( + children: [ + Row( + children: [ + Checkbox( + value: true, + // 转换为 bool + onChanged: (bool? value) {}, + ), + Text( + '向用户发送短信邀请通知', + style: TextStyle( + fontSize: 12.sp, + color: Colors.grey, + fontWeight: FontWeight.w400, + ), + ) + ], + ), + SizedBox( + width: double.infinity, + child: Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: () {}.debounce(), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.grey[100], + padding: EdgeInsets.symmetric(vertical: 12.h), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.r), + ), + ), + child: Text( + '保存'.tr, + style: TextStyle( + fontSize: 16.sp, + color: Colors.blue, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + SizedBox( + width: 10.w, + ), + Expanded( + child: ElevatedButton( + onPressed: () {}.debounce(), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + padding: EdgeInsets.symmetric(vertical: 12.h), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.r)), + ), + child: Text( + '保存并继续添加'.tr, + style: TextStyle( + fontSize: 16.sp, + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + ], + ), + ), + ], + ) + ], + ), ), ), ), diff --git a/lib/views/team/addPerson/selectRole/select_role_binding.dart b/lib/views/team/addPerson/selectRole/select_role_binding.dart new file mode 100644 index 0000000..c3580c4 --- /dev/null +++ b/lib/views/team/addPerson/selectRole/select_role_binding.dart @@ -0,0 +1,10 @@ +import 'package:get/get.dart'; + +import 'select_role_controller.dart'; + +class SelectRoleBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => SelectRoleController()); + } +} \ No newline at end of file diff --git a/lib/views/team/addPerson/selectRole/select_role_controller.dart b/lib/views/team/addPerson/selectRole/select_role_controller.dart new file mode 100644 index 0000000..01a58fd --- /dev/null +++ b/lib/views/team/addPerson/selectRole/select_role_controller.dart @@ -0,0 +1,58 @@ +import 'package:get/get.dart'; +import 'package:starwork_flutter/api/model/team/response/role_list_response.dart'; +import 'package:starwork_flutter/api/service/team_api_service.dart'; +import 'package:starwork_flutter/base/app_logger.dart'; +import 'package:starwork_flutter/base/base_controller.dart'; + +class SelectRoleController extends BaseController { + final teamApi = Get.find(); + var roleList = [].obs; + + var selectRoleIndexList = [].obs; + + // 存储从参数传递过来的已选角色 + List? initialSelectedRoles; + + @override + void onInit() { + super.onInit(); + // 获取传递的参数 + var arguments = Get.arguments; + if (arguments != null && arguments is List) { + initialSelectedRoles = arguments; + } + requestRoleList(); + } + + @override + void onReady() { + super.onReady(); + } + + void requestRoleList() async { + var response = await teamApi.requestTeamRoleList(); + if (response.isSuccess) { + roleList.value = response.data ?? []; + roleList.refresh(); + _setInitialSelectedIndexes(); + } + } + + // 根据初始选中的角色设置对应的索引 + void _setInitialSelectedIndexes() { + if (initialSelectedRoles != null && initialSelectedRoles!.isNotEmpty) { + for (var selectedRole in initialSelectedRoles!) { + // 在角色列表中查找对应的角色并设置选中状态 + for (int i = 0; i < roleList.length; i++) { + if (roleList[i].id == selectedRole.id) { + if (!selectRoleIndexList.contains(i)) { + selectRoleIndexList.add(i); + } + break; + } + } + } + } + selectRoleIndexList.refresh(); + } +} diff --git a/lib/views/team/addPerson/selectRole/select_role_view.dart b/lib/views/team/addPerson/selectRole/select_role_view.dart new file mode 100644 index 0000000..9c1a8b5 --- /dev/null +++ b/lib/views/team/addPerson/selectRole/select_role_view.dart @@ -0,0 +1,236 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:starwork_flutter/api/model/team/response/role_list_response.dart'; +import 'package:starwork_flutter/base/app_logger.dart'; +import 'package:starwork_flutter/common/constant/app_colors.dart'; +import 'package:starwork_flutter/common/widgets/custome_app_bar_wdiget.dart'; +import 'package:starwork_flutter/extension/function_extension.dart'; +import 'package:starwork_flutter/routes/app_routes.dart'; +import 'select_role_controller.dart'; + +class SelectRoleView extends GetView { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBarWidget( + title: '按照角色分配权限'.tr, + backgroundColor: AppColors.scaffoldBackgroundColor, + ), + backgroundColor: AppColors.scaffoldBackgroundColor, + body: SafeArea( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: () async { + var result = await Get.toNamed(AppRoutes.teamAddRole); + if (result != null && result == true) { + controller.requestRoleList(); + } + }, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8.r), + ), + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '新建角色并分配'.tr, + style: TextStyle( + fontSize: 16.sp, + color: Colors.black, + ), + ), + Text( + '按新角色的权限分配'.tr, + style: TextStyle( + fontSize: 14.sp, + color: Colors.grey, + ), + ), + ], + ), + const Spacer(), + Icon( + Icons.arrow_forward_ios_rounded, + size: 20.w, + color: Colors.grey, + ) + ], + ), + ), + ), + SizedBox( + height: 10.h, + ), + Text( + '按以下已有角色的权限分配'.tr, + style: TextStyle( + fontSize: 12.sp, + color: Colors.grey, + ), + ), + SizedBox( + height: 10.h, + ), + Obx( + () => Flexible( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8.r), + ), + child: ListView.separated( + shrinkWrap: true, + itemBuilder: (context, index) { + RoleListResponse roleList = controller.roleList[index]; + return GestureDetector( + onTap: () { + // 避免重复添加 + if (!controller.selectRoleIndexList.contains(index)) { + controller.selectRoleIndexList.add(index); + } else { + controller.selectRoleIndexList.remove(index); + } + controller.selectRoleIndexList.refresh(); + }, + child: Padding( + padding: EdgeInsets.symmetric( + vertical: 10.h, + horizontal: 10.w, + ), + child: Row( + children: [ + Obx( + () => Checkbox( + value: controller.selectRoleIndexList.contains(index), + activeColor: Colors.blue, + onChanged: (value) { + if (value == true) { + // 避免重复添加 + if (!controller.selectRoleIndexList.contains(index)) { + controller.selectRoleIndexList.add(index); + } + } else { + controller.selectRoleIndexList.remove(index); + } + controller.selectRoleIndexList.refresh(); + }, + ), + ), + Container( + decoration: BoxDecoration( + color: Colors.blue[50], + borderRadius: BorderRadius.circular(8.r), + ), + width: 36.w, + height: 36.w, + child: const Icon( + Icons.group_rounded, + color: Colors.blue, + ), + ), + SizedBox( + width: 10.w, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + roleList.roleName ?? '', + style: TextStyle( + fontSize: 16.sp, + color: Colors.black, + ), + ), + Text( + roleList.roleDesc ?? '', + style: TextStyle( + fontSize: 12.sp, + color: Colors.grey, + ), + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + ], + ), + ), + ); + }, + separatorBuilder: (context, index) { + return Padding( + padding: EdgeInsets.symmetric( + horizontal: 10.w, + ), + child: Divider( + height: 0.5.h, + thickness: 0.5.h, + color: Colors.grey[100], + ), + ); + }, + itemCount: controller.roleList.length, + ), + ), + ), + ), + SizedBox( + height: 10.h, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Obx( + () => Text( + '已选中:${controller.selectRoleIndexList.length}个', + style: TextStyle( + fontSize: 14.sp, + color: Colors.grey, + ), + ), + ), + ElevatedButton( + onPressed: () { + Get.back( + result: controller.selectRoleIndexList.map((e) => controller.roleList[e]).toList(), + ); + }.debounce(), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + padding: EdgeInsets.zero, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.r), + ), + ), + child: Text( + '确认'.tr, + style: TextStyle( + fontSize: 16.sp, + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + ) + ], + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/views/team/addRole/add_role_binding.dart b/lib/views/team/addRole/add_role_binding.dart new file mode 100644 index 0000000..d08e188 --- /dev/null +++ b/lib/views/team/addRole/add_role_binding.dart @@ -0,0 +1,10 @@ +import 'package:get/get.dart'; + +import 'add_role_controller.dart'; + +class AddRoleBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => AddRoleController()); + } +} \ No newline at end of file diff --git a/lib/views/team/addRole/add_role_controller.dart b/lib/views/team/addRole/add_role_controller.dart new file mode 100644 index 0000000..c8a679e --- /dev/null +++ b/lib/views/team/addRole/add_role_controller.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:starwork_flutter/api/model/team/request/create_new_role_request.dart'; +import 'package:starwork_flutter/api/service/team_api_service.dart'; +import 'package:starwork_flutter/base/base_controller.dart'; + +class AddRoleController extends BaseController { + final teamApi = Get.find(); + + TextEditingController roleNameInputController = TextEditingController(); + TextEditingController roleDescribeInputController = TextEditingController(); + + void createTeamRole() async { + var response = await teamApi.requestCreateTeamRole( + request: CreateNewRoleRequest( + roleName: roleNameInputController.text.trim(), + roleDesc: roleDescribeInputController.text.trim(), + ), + ); + if (response.isSuccess) { + showSuccess(message: '创建成功'); + Get.back(result: true); + } else { + showError(message: response.errorMsg!); + } + } +} diff --git a/lib/views/team/addRole/add_role_view.dart b/lib/views/team/addRole/add_role_view.dart new file mode 100644 index 0000000..26a318e --- /dev/null +++ b/lib/views/team/addRole/add_role_view.dart @@ -0,0 +1,157 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:starwork_flutter/common/constant/app_colors.dart'; +import 'package:starwork_flutter/common/widgets/custom_cell_list_widget.dart'; +import 'package:starwork_flutter/common/widgets/custom_cell_widget.dart'; +import 'package:starwork_flutter/common/widgets/custome_app_bar_wdiget.dart'; +import 'package:starwork_flutter/extension/function_extension.dart'; +import 'add_role_controller.dart'; + +class AddRoleView extends GetView { + @override + Widget build(BuildContext context) { + // 即使不使用,只是引用一下 controller 就能触发初始化 + final _ = controller; // 添加这一行 + return GestureDetector( + onTap: () { + FocusScope.of(context).requestFocus(FocusNode()); + }, + child: Scaffold( + appBar: CustomAppBarWidget( + title: '新建角色'.tr, + backgroundColor: AppColors.scaffoldBackgroundColor, + ), + backgroundColor: AppColors.scaffoldBackgroundColor, + body: SafeArea( + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: 10.w, + vertical: 10.h, + ), + child: Column( + children: [ + CustomCellListWidget( + children: [ + CustomCellWidget( + onTap: () {}, + leftText: '角色名称'.tr, + leftIcon: Icon( + Icons.circle, + size: 4.w, + color: Colors.red, + ), + rightWidget: Expanded( + flex: 3, + child: TextField( + controller: controller.roleNameInputController, + keyboardType: TextInputType.text, + textInputAction: TextInputAction.next, + textAlign: TextAlign.end, + style: TextStyle( + fontSize: 14.sp, + ), + decoration: InputDecoration( + isCollapsed: true, + hintText: '请输入角色名称'.tr, + hintStyle: TextStyle( + fontSize: 14.sp, + color: Colors.black54, + fontWeight: FontWeight.w400, + ), + // 设置无边框 + border: InputBorder.none, + contentPadding: EdgeInsets.zero, + // 获取焦点时的边框 + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + ), + ), + ), + ) + ], + ), + SizedBox( + height: 10.h, + ), + CustomCellListWidget( + children: [ + CustomCellWidget( + onTap: () {}, + leftText: '角色描述'.tr, + leftIcon: Icon( + Icons.circle, + size: 4.w, + color: Colors.red, + ), + rightWidget: Expanded( + flex: 3, + child: TextField( + controller: controller.roleDescribeInputController, + keyboardType: TextInputType.text, + textInputAction: TextInputAction.next, + textAlign: TextAlign.end, + style: TextStyle( + fontSize: 14.sp, + ), + decoration: InputDecoration( + isCollapsed: true, + hintText: '请输入角色描述'.tr, + hintStyle: TextStyle( + fontSize: 14.sp, + color: Colors.black54, + fontWeight: FontWeight.w400, + ), + // 设置无边框 + border: InputBorder.none, + contentPadding: EdgeInsets.zero, + // 获取焦点时的边框 + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + ), + ), + ), + ) + ], + ), + SizedBox( + height: 10.h, + ), + Spacer(), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: () { + if (controller.roleNameInputController.text.isEmpty) { + controller.showToast('请先输入角色名称'); + return; + } + if (controller.roleDescribeInputController.text.isEmpty) { + controller.showToast('请先输入角色描述'); + return; + } + controller.createTeamRole(); + }.debounce(), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + padding: EdgeInsets.symmetric(vertical: 12.h), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.r)), + ), + child: Text( + '保存'.tr, + style: TextStyle( + fontSize: 16.sp, + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + ), + ) + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/views/team/createTeam/create_team_controller.dart b/lib/views/team/createTeam/create_team_controller.dart index 1715df8..fbb0456 100644 --- a/lib/views/team/createTeam/create_team_controller.dart +++ b/lib/views/team/createTeam/create_team_controller.dart @@ -25,6 +25,8 @@ class CreateTeamController extends BaseController { requestAllSceneInfoList(); } + + /// 请求团队使用场景信息 void requestAllSceneInfoList() async { showLoading(); diff --git a/lib/views/team/inviteTeamMember/invitationSettings/invitation_settings_controller.dart b/lib/views/team/inviteTeamMember/invitationSettings/invitation_settings_controller.dart index c42c2ca..e26de47 100644 --- a/lib/views/team/inviteTeamMember/invitationSettings/invitation_settings_controller.dart +++ b/lib/views/team/inviteTeamMember/invitationSettings/invitation_settings_controller.dart @@ -1,6 +1,40 @@ import 'package:get/get.dart'; +import 'package:starwork_flutter/api/model/team/request/update_invite_parameter_config_request.dart'; +import 'package:starwork_flutter/api/model/team/response/team_invite_parameter_config_response.dart'; +import 'package:starwork_flutter/api/service/team_api_service.dart'; import 'package:starwork_flutter/base/base_controller.dart'; +import 'package:starwork_flutter/views/main/main_controller.dart'; class InvitationSettingsController extends BaseController { - // TODO: 在这里添加业务逻辑 -} \ No newline at end of file + final teamApi = Get.find(); + final mainController = Get.find(); + var inviteConfig = TeamInviteParameterConfigResponse().obs; + + @override + void onReady() { + super.onReady(); + requestInvitationSettings(); + } + + requestInvitationSettings() async { + var response = await teamApi.requestInviteParameterConfig(); + if (response.isSuccess) { + inviteConfig.value = response.data!; + } + } + + requestUpdateInvitationSettings() async { + var response = await teamApi.requestUpdateInviteParameterConfig( + request: UpdateInviteParameterConfigRequest( + joinTeamInputFace: inviteConfig.value.joinTeamInputFace!, + applyJoinTeamAudit: inviteConfig.value.applyJoinTeamAudit!, + teamInviteUserValidFalse: inviteConfig.value.teamInviteUserValidFalse!, + ), + ); + if (response.isSuccess) { + inviteConfig.refresh(); + } else { + showError(message: response.errorMsg!); + } + } +} diff --git a/lib/views/team/inviteTeamMember/invitationSettings/invitation_settings_view.dart b/lib/views/team/inviteTeamMember/invitationSettings/invitation_settings_view.dart index 2adda64..167046d 100644 --- a/lib/views/team/inviteTeamMember/invitationSettings/invitation_settings_view.dart +++ b/lib/views/team/inviteTeamMember/invitationSettings/invitation_settings_view.dart @@ -11,6 +11,9 @@ import 'invitation_settings_controller.dart'; class InvitationSettingsView extends GetView { @override Widget build(BuildContext context) { + // 即使不使用,只是引用一下 controller 就能触发初始化 + final _ = controller; // 添加这一行 + return Scaffold( backgroundColor: AppColors.scaffoldBackgroundColor, appBar: CustomAppBarWidget( @@ -31,31 +34,58 @@ class InvitationSettingsView extends GetView { onTap: () {}, leftText: '人员配置'.tr, leftSubText: '关闭审核后,新申请的用户无需审核即可加入成功', - rightWidget: CupertinoSwitch( - value: false, - onChanged: (bool value) {}, - activeColor: Colors.blue, // 可选:打开时的颜色(iOS 默认为系统蓝色,可自定义) - trackColor: Colors.grey, // 可选:关闭时的背景轨道颜色 + rightWidget: Obx( + () => CupertinoSwitch( + value: controller.inviteConfig.value.applyJoinTeamAudit == 1, + onChanged: (bool value) { + if (value) { + controller.inviteConfig.value.applyJoinTeamAudit = 1; + } else { + controller.inviteConfig.value.applyJoinTeamAudit = 2; + } + controller.requestUpdateInvitationSettings(); + }, + activeColor: Colors.blue, + trackColor: Colors.grey, + ), ), ), CustomCellWidget( onTap: () {}, leftText: '申请加入时可录入人脸'.tr, - rightWidget: CupertinoSwitch( - value: false, - onChanged: (bool value) {}, - activeColor: Colors.blue, // 可选:打开时的颜色(iOS 默认为系统蓝色,可自定义) - trackColor: Colors.grey, // 可选:关闭时的背景轨道颜色 + rightWidget: Obx( + () => CupertinoSwitch( + value: controller.inviteConfig.value.joinTeamInputFace == 1, + onChanged: (bool value) { + if (value) { + controller.inviteConfig.value.joinTeamInputFace = 1; + } else { + controller.inviteConfig.value.joinTeamInputFace = 2; + } + controller.requestUpdateInvitationSettings(); + }, + activeColor: Colors.blue, + trackColor: Colors.grey, + ), ), ), CustomCellWidget( onTap: () {}, leftText: '邀请信息30天后自动失效'.tr, - rightWidget: CupertinoSwitch( - value: false, - onChanged: (bool value) {}, - activeColor: Colors.blue, // 可选:打开时的颜色(iOS 默认为系统蓝色,可自定义) - trackColor: Colors.grey, // 可选:关闭时的背景轨道颜色 + rightWidget: Obx( + () => CupertinoSwitch( + value: controller.inviteConfig.value.teamInviteUserValidFalse == 1, + onChanged: (bool value) { + if (value) { + controller.inviteConfig.value.teamInviteUserValidFalse = 1; + } else { + controller.inviteConfig.value.teamInviteUserValidFalse = 2; + } + controller.requestUpdateInvitationSettings(); + }, + activeColor: Colors.blue, // 可选:打开时的颜色(iOS 默认为系统蓝色,可自定义) + trackColor: Colors.grey, // 可选:关闭时的背景轨道颜色 + ), ), ), ], diff --git a/lib/views/team/inviteTeamMember/invite_team_member_view.dart b/lib/views/team/inviteTeamMember/invite_team_member_view.dart index 9fd72d1..29e7d65 100644 --- a/lib/views/team/inviteTeamMember/invite_team_member_view.dart +++ b/lib/views/team/inviteTeamMember/invite_team_member_view.dart @@ -82,22 +82,25 @@ class InviteTeamMemberView extends GetView { ), ), SizedBox(height: 10.h), - Container( - padding: EdgeInsets.all(10.r), // 添加边距 - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8.r), // 为 Container 添加圆角 - color: Colors.white, // 添加背景色以突出圆角效果 - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(8.r), - child: Obx( - () => Image( - width: 200.w, // 调整为 200 以适应 padding - height: 200.w, - image: NetworkImage( - controller.inviteInfo.value.inviteInfo ?? '', + Obx( + () => Visibility( + visible: controller.inviteInfo.value.inviteInfo != null, + child: Container( + padding: EdgeInsets.all(10.r), // 添加边距 + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.r), // 为 Container 添加圆角 + color: Colors.white, // 添加背景色以突出圆角效果 + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(8.r), + child: Image( + width: 200.w, // 调整为 200 以适应 padding + height: 200.w, + image: NetworkImage( + controller.inviteInfo.value.inviteInfo ?? '', + ), + fit: BoxFit.cover, ), - fit: BoxFit.cover, ), ), ), diff --git a/lib/views/team/personnelManage/personnel_manage_binding.dart b/lib/views/team/personnelManage/personnel_manage_binding.dart new file mode 100644 index 0000000..eff8c6e --- /dev/null +++ b/lib/views/team/personnelManage/personnel_manage_binding.dart @@ -0,0 +1,10 @@ +import 'package:get/get.dart'; + +import 'personnel_manage_controller.dart'; + +class PersonnelManageBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => PersonnelManageController()); + } +} \ No newline at end of file diff --git a/lib/views/team/personnelManage/personnel_manage_controller.dart b/lib/views/team/personnelManage/personnel_manage_controller.dart new file mode 100644 index 0000000..020b510 --- /dev/null +++ b/lib/views/team/personnelManage/personnel_manage_controller.dart @@ -0,0 +1,137 @@ +import 'dart:convert'; + +import 'package:animated_tree_view/animated_tree_view.dart'; +import 'package:animated_tree_view/tree_view/tree_node.dart'; +import 'package:get/get.dart'; +import 'package:starwork_flutter/api/model/team/request/create_new_depart_request.dart'; +import 'package:starwork_flutter/api/model/team/request/get_depart_list_request.dart'; +import 'package:starwork_flutter/api/model/team/response/depart_list_reponse.dart'; +import 'package:starwork_flutter/api/model/user/response/user_info_response.dart'; +import 'package:starwork_flutter/api/service/team_api_service.dart'; +import 'package:starwork_flutter/base/app_logger.dart'; +import 'package:starwork_flutter/base/base_controller.dart'; +import 'package:starwork_flutter/common/constant/cache_keys.dart'; +import 'package:starwork_flutter/common/utils/shared_preferences_utils.dart'; + +class PersonnelManageController extends BaseController { + final teamApi = Get.find(); + final Rx treeData = Rx(null); + TreeViewController? treeViewController; + var selectedDepartItem = DepartItem().obs; // 当前选中的组织 + var cacheUserInfo = UserInfoResponse().obs; // 缓存的登录用户信息 + final RxInt totalPersonCount = 0.obs; + + @override + void onReady() { + super.onReady(); + requestDepartList(); + getCacheUserInfo(); + } + + requestDepartList() async { + var response = await teamApi.requestDepartList( + request: GetDepartListRequest(departNo: ''), + ); + if (response.isSuccess) { + // 转换数据为树形结构 + final tree = _convertToTree(response.data!.departList ?? []); + treeData.value = tree; + // 统计总人数 + totalPersonCount.value = _calculateTotalPersonCount(response.data!.departList ?? []); + + treeViewController?.expandAllChildren(treeData.value ?? TreeNode.root()); + } + } + + // 计算总人数 + int _calculateTotalPersonCount(List departList) { + int total = 0; + for (final depart in departList) { + total += depart.personNum ?? 0; + } + return total; + } + + TreeNode _convertToTree(List departList) { + // 创建根节点 + final root = TreeNode.root(); + + // 如果列表为空,直接返回根节点 + if (departList.isEmpty) { + return root; + } + + // 创建一个映射来存储所有部门节点,方便查找 + final Map departNodeMap = {}; + + // 先创建所有部门节点 + for (final depart in departList) { + final node = TreeNode( + key: depart.id.toString(), + data: depart, + ); + departNodeMap[depart.id!] = node; + } + + // 建立部门间的父子关系 + for (final depart in departList) { + final currentNode = departNodeMap[depart.id]; + if (currentNode == null) continue; + + // 建立部门间的父子关系 + if (depart.parentId == -1) { + // 顶级部门添加到根节点下 + root.add(currentNode); + } else { + // 子部门添加到对应父部门下 + final parentNode = departNodeMap[depart.parentId]; + parentNode?.add(currentNode); + } + } + + // 将人员节点添加到对应的部门节点下(在所有部门结构建立完成后再添加) + for (final depart in departList) { + final currentNode = departNodeMap[depart.id]; + if (currentNode == null) continue; + + // 将该部门下的所有人员作为子节点添加 + if (depart.persons != null && depart.persons!.isNotEmpty) { + // 对人员按某种规则排序(例如按姓名或ID) + final sortedPersons = List.from(depart.persons!); + // 这里可以根据需要添加排序逻辑,例如按姓名排序: + // sortedPersons.sort((a, b) => (a.personName ?? '').compareTo(b.personName ?? '')); + + for (final person in sortedPersons) { + final personNode = TreeNode( + key: 'person_${person.id}', + data: person, + ); + currentNode.add(personNode); + } + } + } + + return root; + } + + requestCreateDepart() async { + var response = await teamApi.requestCreateDepart( + request: CreateNewDepartRequest(departName: '测试2', parentDepartNo: '-1', leader: ['CY93685899']), + ); + if (response.isSuccess) {} + } + + void getCacheUserInfo() async { + String? cachedJson = await SharedPreferencesUtils.getString(CacheKeys.userAccountInfo); + if (cachedJson != null) { + try { + Map jsonMap = jsonDecode(cachedJson); + UserInfoResponse userInfoResponse = UserInfoResponse.fromJson(jsonMap); + cacheUserInfo.value = userInfoResponse; + cacheUserInfo.refresh(); + } catch (e) { + AppLogger.error('JSON 解析错误: $e'); + } + } + } +} diff --git a/lib/views/team/personnelManage/personnel_manage_view.dart b/lib/views/team/personnelManage/personnel_manage_view.dart new file mode 100644 index 0000000..d1f253a --- /dev/null +++ b/lib/views/team/personnelManage/personnel_manage_view.dart @@ -0,0 +1,428 @@ +import 'package:animated_tree_view/animated_tree_view.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/painting.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:starwork_flutter/api/model/team/response/depart_list_reponse.dart'; +import 'package:starwork_flutter/base/app_logger.dart'; +import 'package:starwork_flutter/common/constant/app_colors.dart'; +import 'package:starwork_flutter/common/constant/app_images.dart'; +import 'package:starwork_flutter/common/constant/app_view_parameter_keys.dart'; +import 'package:starwork_flutter/common/widgets/custome_app_bar_wdiget.dart'; +import 'package:starwork_flutter/extension/function_extension.dart'; +import 'package:starwork_flutter/routes/app_routes.dart'; +import 'personnel_manage_controller.dart'; + +class PersonnelManageView extends GetView { + PersonnelManageView({super.key}); + + @override + Widget build(BuildContext context) { + // 即使不使用,只是引用一下 controller 就能触发初始化 + final _ = controller; // 添加这一行 + + return Scaffold( + backgroundColor: AppColors.scaffoldBackgroundColor, + appBar: CustomAppBarWidget( + title: '人员管理'.tr, + backgroundColor: AppColors.scaffoldBackgroundColor, + ), + body: SafeArea( + child: Column( + children: [ + Container( + margin: EdgeInsets.symmetric( + horizontal: 10.w, + vertical: 10.h, + ), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8.r), + ), + padding: EdgeInsets.symmetric( + horizontal: 10.w, + vertical: 10.h, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Column( + children: [ + Icon( + Icons.person, + ), + SizedBox( + height: 4.h, + ), + Text( + '人脸信息'.tr, + style: TextStyle( + fontSize: 10.sp, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + GestureDetector( + onTap: () { + Get.toNamed(AppRoutes.teamRoleManage); + }, + child: Column( + children: [ + Icon( + Icons.person, + ), + SizedBox( + height: 4.h, + ), + Text( + '角色管理'.tr, + style: TextStyle( + fontSize: 10.sp, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + Column( + children: [ + Icon( + Icons.person, + ), + SizedBox( + height: 4.h, + ), + Text( + '新用户审核'.tr, + style: TextStyle( + fontSize: 10.sp, + fontWeight: FontWeight.w500, + ), + ), + ], + ) + ], + ), + ), + Container( + margin: EdgeInsets.symmetric( + horizontal: 10.w, + ), + alignment: Alignment.centerLeft, + child: Text( + '组织架构', + textAlign: TextAlign.left, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w500, + color: Colors.grey, + ), + ), + ), + SizedBox( + height: 10.h, + ), + Expanded( + child: Obx( + () => Container( + margin: EdgeInsets.symmetric( + horizontal: 10.w, + ), + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(8.0)), + ), + child: TreeView.simple( + tree: controller.treeData.value ?? TreeNode.root(), + showRootNode: false, + expansionIndicatorBuilder: noExpansionIndicatorBuilder, + indentation: const Indentation(style: IndentStyle.roundJoint), + onItemTap: (item) {}, + onTreeReady: (c) { + controller.treeViewController = c; + }, + builder: (context, node) { + String title = ""; + DepartItem departInfo = DepartItem(); + int personNum = 0; + bool hasChildren = node.childrenAsList.isNotEmpty; // 判断是否有子节点 + bool isPersonItem = node.data is PersonItem; // 判断当前节点是否是人员节点 + bool isOneself = false; + bool isRootNode = false; + bool isSuper = false; + + if (node.data is DepartItem) { + final depart = node.data as DepartItem; + title = depart.departName ?? "未命名部门"; + personNum = depart.personNum ?? 0; + departInfo = depart; + if (hasChildren) { + title = title + "($personNum)"; + } else { + title = title + "(0)"; + } + if (depart.parentId == -1) { + isRootNode = true; + } + } else if (node.data is PersonItem) { + // 处理人员节点 + final person = node.data as PersonItem; + title = person.personName ?? "未命名人员"; + var personUserId = person.userId; + person.roles?.forEach((role) { + if (role.isSuper == 1) { + isSuper = true; + } + }); + if (personUserId != null && personUserId == controller.cacheUserInfo.value.id) { + isOneself = true; + } + } + + return Container( + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 6.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Row( + children: [ + !isPersonItem + ? Container( + width: 34.w, + height: 34.w, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(8.r), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(8.r), + child: Icon( + Icons.folder, + size: 22.w, + color: Colors.blue, + ), + ), + ) + : Container( + width: 34.w, + height: 34.w, + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: BorderRadius.circular(8.r), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(8.r), + child: Image( + image: const AssetImage(AppImages.defaultAvatar), + width: 22.w, + height: 22.w, + fit: BoxFit.cover, + gaplessPlayback: true, + filterQuality: FilterQuality.medium, + errorBuilder: (context, error, stackTrace) { + return Icon( + Icons.person, + size: 30.sp, + color: Colors.grey[400], + ); + }, + ), + ), + ), + SizedBox( + width: 10.w, + ), + Expanded( + child: Text( + title, + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w500, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + Visibility( + visible: isOneself, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 2.h), + decoration: BoxDecoration( + color: Colors.blue[50], + borderRadius: BorderRadius.circular(4.r), + border: Border.all( + color: Colors.blue, + width: 1.w, + ), + ), + child: Text( + '自己', + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w400, + color: Colors.blue, + ), + ), + ), + ), + Visibility( + visible: isSuper && isOneself, + child: SizedBox( + width: 4.w, + ), + ), + Visibility( + visible: isSuper, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 2.h), + decoration: BoxDecoration( + color: Colors.blue[50], + borderRadius: BorderRadius.circular(4.r), + border: Border.all( + color: Colors.blue, + width: 1.w, + ), + ), + child: Text( + '超管', + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w400, + color: Colors.blue, + ), + ), + ), + ), + ], + ), + ), + Visibility( + visible: !isPersonItem, + child: Obx( + () => Radio( + value: departInfo, + groupValue: controller.selectedDepartItem.value, + activeColor: Colors.blue, + visualDensity: VisualDensity.compact, + onChanged: (value) { + controller.selectedDepartItem.value = value as DepartItem; + controller.selectedDepartItem.refresh(); + }, + ), + ), + ) + ], + ), + ); + }, + ), + ), + ), + ), + SizedBox( + height: 10.h, + ), + Obx( + () => Text( + '共${controller.totalPersonCount.value}人', + style: TextStyle( + fontSize: 14.sp, + color: Colors.grey[500], + fontWeight: FontWeight.w500, + ), + ), + ), + SizedBox( + height: 10.h, + ), + Container( + margin: EdgeInsets.symmetric( + horizontal: 10.w, + ), + child: Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: () { + if (controller.selectedDepartItem.value.departNo == null) { + controller.showToast('请先选择一个组织'); + return; + } + Get.toNamed(AppRoutes.teamAddPerson, arguments: { + AppViewParameterKeys.departItem: controller.selectedDepartItem.value.toJson(), + }); + }.debounce(), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + padding: EdgeInsets.symmetric(vertical: 10.h), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.r)), + ), + child: Text( + '添加人员'.tr, + style: TextStyle( + fontSize: 16.sp, + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + SizedBox(width: 10.w), + Expanded( + child: ElevatedButton( + onPressed: () { + if (controller.selectedDepartItem.value.departNo == null) { + controller.showToast('请先选择一个组织'); + return; + } + }.debounce(), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.grey[50], + padding: EdgeInsets.symmetric(vertical: 10.h), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.r)), + ), + child: Text( + '添加子组织'.tr, + style: TextStyle( + fontSize: 16.sp, + color: Colors.blue, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + SizedBox(width: 10.w), + Expanded( + child: ElevatedButton( + onPressed: () {}.debounce(), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.grey[50], + padding: EdgeInsets.symmetric(vertical: 10.h), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.r)), + ), + child: Text( + '更多管理'.tr, + style: TextStyle( + fontSize: 16.sp, + color: Colors.blue, + fontWeight: FontWeight.w500, + ), + ), + ), + ) + ], + ), + ), + SizedBox( + height: 10.h, + ), + ], + ), + ), + ); + } +} diff --git a/lib/views/team/roleManage/role_manage_binding.dart b/lib/views/team/roleManage/role_manage_binding.dart new file mode 100644 index 0000000..cc29745 --- /dev/null +++ b/lib/views/team/roleManage/role_manage_binding.dart @@ -0,0 +1,10 @@ +import 'package:get/get.dart'; + +import 'role_manage_controller.dart'; + +class RoleManageBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => RoleManageController()); + } +} \ No newline at end of file diff --git a/lib/views/team/roleManage/role_manage_controller.dart b/lib/views/team/roleManage/role_manage_controller.dart new file mode 100644 index 0000000..79b601d --- /dev/null +++ b/lib/views/team/roleManage/role_manage_controller.dart @@ -0,0 +1,42 @@ +import 'dart:convert'; + +import 'package:get/get.dart'; +import 'package:starwork_flutter/api/model/team/request/role_list_request.dart'; +import 'package:starwork_flutter/api/model/team/response/role_list_response.dart'; +import 'package:starwork_flutter/api/model/user/response/user_info_response.dart'; +import 'package:starwork_flutter/api/service/team_api_service.dart'; +import 'package:starwork_flutter/base/app_logger.dart'; +import 'package:starwork_flutter/base/base_controller.dart'; +import 'package:starwork_flutter/common/constant/cache_keys.dart'; +import 'package:starwork_flutter/common/utils/shared_preferences_utils.dart'; + +class RoleManageController extends BaseController { + final teamApi = Get.find(); + var roleList = [].obs; + var cacheUserInfo = UserInfoResponse().obs; // 缓存的登录用户信息 + @override + void onReady() async { + super.onReady(); + requestRoleList(); + + String? cachedJson = await SharedPreferencesUtils.getString(CacheKeys.userAccountInfo); + if (cachedJson != null) { + try { + Map jsonMap = jsonDecode(cachedJson); + UserInfoResponse userInfoResponse = UserInfoResponse.fromJson(jsonMap); + cacheUserInfo.value = userInfoResponse; + cacheUserInfo.refresh(); + } catch (e) { + AppLogger.error('JSON 解析错误: $e'); + } + } + } + + void requestRoleList() async { + var response = await teamApi.requestTeamRoleList(); + if (response.isSuccess) { + roleList.value = response.data ?? []; + roleList.refresh(); + } + } +} diff --git a/lib/views/team/roleManage/role_manage_view.dart b/lib/views/team/roleManage/role_manage_view.dart new file mode 100644 index 0000000..50c31f1 --- /dev/null +++ b/lib/views/team/roleManage/role_manage_view.dart @@ -0,0 +1,345 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:starwork_flutter/api/model/team/response/role_list_response.dart'; +import 'package:starwork_flutter/common/constant/app_colors.dart'; +import 'package:starwork_flutter/common/widgets/custome_app_bar_wdiget.dart'; +import 'package:starwork_flutter/extension/function_extension.dart'; +import 'package:starwork_flutter/routes/app_routes.dart'; +import 'role_manage_controller.dart'; + +class RoleManageView extends GetView { + @override + Widget build(BuildContext context) { + // 即使不使用,只是引用一下 controller 就能触发初始化 + final _ = controller; // 添加这一行 + + return Scaffold( + backgroundColor: AppColors.scaffoldBackgroundColor, + appBar: CustomAppBarWidget( + title: '角色分配'.tr, + backgroundColor: AppColors.scaffoldBackgroundColor, + ), + body: SafeArea( + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: 10.w, + ), + child: Column( + children: [ + Row( + children: [ + RichText( + text: TextSpan( + children: [ + TextSpan( + text: '超级管理员'.tr, + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + const TextSpan( + text: ' ', + ), + TextSpan( + text: '具有团队全部管理权限'.tr, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w400, + color: Colors.grey[500], + ), + ), + ], + ), + ) + ], + ), + SizedBox( + height: 10.h, + ), + Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8.r), + ), + padding: EdgeInsets.symmetric( + horizontal: 10.w, + vertical: 10.h, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Obx( + () => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + controller.cacheUserInfo.value.nickname ?? '', + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + Text( + controller.cacheUserInfo.value.accountNo ?? '', + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w400, + color: Colors.grey[500], + ), + ), + ], + ), + ), + ElevatedButton( + onPressed: () { + controller.showFunctionNotOpen(); + }.debounce(), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + padding: EdgeInsets.zero, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.r)), + ), + child: Text( + '转让'.tr, + style: TextStyle( + fontSize: 16.sp, + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + ) + ], + ), + ), + SizedBox( + height: 10.h, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + RichText( + text: TextSpan( + children: [ + TextSpan( + text: '角色'.tr, + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + const TextSpan( + text: ' ', + ), + TextSpan( + text: '用于给其他用户快捷分配权限'.tr, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w400, + color: Colors.grey[500], + ), + ), + ], + ), + ), + GestureDetector( + onTap: () async { + var result = await Get.toNamed(AppRoutes.teamAddRole); + if (result != null && result == true) { + controller.requestRoleList(); + } + }, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '新建角色', + style: TextStyle( + fontSize: 14.sp, + color: Colors.blue, + ), + ), + ], + ), + ), + ], + ), + SizedBox( + height: 10.h, + ), + _buildRoleList(), + ], + ), + ), + ), + ); + } + + _buildRoleList() { + return Obx( + () => Flexible( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8.r), + ), + child: ListView.separated( + shrinkWrap: true, + itemCount: controller.roleList.length, + itemBuilder: (context, index) { + RoleListResponse role = controller.roleList[index]; + return _buildRoleItem(role); + }, + separatorBuilder: (context, index) { + return Padding( + padding: EdgeInsets.symmetric( + horizontal: 10.w, + ), + child: Divider( + height: 0.5.h, + thickness: 0.5.h, + color: Colors.grey[100], + ), + ); + }, + ), + ), + ), + ); + } + + _buildRoleItem(RoleListResponse role) { + // 假设 role 对象中有 personList 字段,且每个 person 有 personName 属性 + // 如果数据结构不同,请根据实际情况调整 + String personNames = ''; + + // 示例:如果 role.personList 存在且不为空 + if (role.personList != null && role.personList!.isNotEmpty) { + // 提取所有 personName 并用逗号连接 + personNames = role.personList!.map((person) => person.personName ?? '').join(','); // 使用顿号或逗号分隔 + } else { + // 默认值或空值处理 + personNames = '暂无用户'; + } + return Container( + width: double.infinity, + padding: EdgeInsets.symmetric( + horizontal: 10.w, + vertical: 10.h, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Icon( + Icons.group_rounded, + color: Colors.blue, + ), + SizedBox( + width: 6.w, + ), + Text( + role.roleName ?? '', + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w600, + ), + ), + const Spacer(), + Icon( + Icons.arrow_forward_ios_rounded, + color: Colors.grey, + size: 16.sp, + ) + ], + ), + SizedBox( + height: 10.h, + ), + RichText( + textAlign: TextAlign.left, + maxLines: 1, + overflow: TextOverflow.ellipsis, + text: TextSpan( + children: [ + TextSpan( + text: '角色用户:'.tr, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w400, + color: Colors.grey[500], + ), + ), + TextSpan( + text: personNames, + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w400, + color: Colors.black, + ), + ) + ], + ), + ), + RichText( + textAlign: TextAlign.left, + maxLines: 1, + overflow: TextOverflow.ellipsis, + text: TextSpan( + children: [ + TextSpan( + text: '分配权限:'.tr, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w400, + color: Colors.grey[500], + ), + ), + TextSpan( + text: '全部权限'.tr, + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w400, + color: Colors.black, + ), + ) + ], + ), + ), + RichText( + textAlign: TextAlign.left, + maxLines: 2, + overflow: TextOverflow.ellipsis, + text: TextSpan( + children: [ + TextSpan( + text: '角色描述:'.tr, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w400, + color: Colors.grey[500], + ), + ), + TextSpan( + text: role.roleDesc ?? '', + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w400, + color: Colors.black, + ), + ) + ], + ), + ) + ], + ), + ); + } +} diff --git a/lib/views/team/selectOrganization/select_organization_binding.dart b/lib/views/team/selectOrganization/select_organization_binding.dart new file mode 100644 index 0000000..eae5b07 --- /dev/null +++ b/lib/views/team/selectOrganization/select_organization_binding.dart @@ -0,0 +1,10 @@ +import 'package:get/get.dart'; + +import 'select_organization_controller.dart'; + +class SelectOrganizationBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => SelectOrganizationController()); + } +} \ No newline at end of file diff --git a/lib/views/team/selectOrganization/select_organization_controller.dart b/lib/views/team/selectOrganization/select_organization_controller.dart new file mode 100644 index 0000000..64a8763 --- /dev/null +++ b/lib/views/team/selectOrganization/select_organization_controller.dart @@ -0,0 +1,76 @@ +import 'package:animated_tree_view/animated_tree_view.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:starwork_flutter/api/model/team/request/get_depart_list_request.dart'; +import 'package:starwork_flutter/api/model/team/response/depart_list_reponse.dart'; +import 'package:starwork_flutter/api/service/team_api_service.dart'; +import 'package:starwork_flutter/base/base_controller.dart'; + +class SelectOrganizationController extends BaseController { + final teamApi = Get.find(); + final Rx treeData = Rx(null); + TreeViewController? treeViewController; + // 搜索输入框 + TextEditingController searchInputController = TextEditingController(); + + @override + void onReady() { + super.onReady(); + requestDepartList(); + } + + requestDepartList() async { + var response = await teamApi.requestDepartList( + request: GetDepartListRequest(departNo: ''), + ); + if (response.isSuccess) { + // 转换数据为树形结构 + final tree = _convertToTree(response.data!.departList ?? []); + treeData.value = tree; + + treeViewController?.expandAllChildren(treeData.value ?? TreeNode.root()); + } + } + + TreeNode _convertToTree(List departList) { + // 创建根节点 + final root = TreeNode.root(); + + // 如果列表为空,直接返回根节点 + if (departList.isEmpty) { + return root; + } + + // 创建一个映射来存储所有部门节点,方便查找 + final Map departNodeMap = {}; + + // 先创建所有部门节点 + for (final depart in departList) { + final node = TreeNode( + key: depart.id.toString(), + data: depart, + ); + departNodeMap[depart.id!] = node; + } + + // 建立部门间的父子关系 + for (final depart in departList) { + final currentNode = departNodeMap[depart.id]; + if (currentNode == null) continue; + + // 建立部门间的父子关系 + if (depart.parentId == -1) { + // 顶级部门添加到根节点下 + root.add(currentNode); + } else { + // 子部门添加到对应父部门下 + final parentNode = departNodeMap[depart.parentId]; + parentNode?.add(currentNode); + } + } + + + + return root; + } +} diff --git a/lib/views/team/selectOrganization/select_organization_view.dart b/lib/views/team/selectOrganization/select_organization_view.dart new file mode 100644 index 0000000..b1a9014 --- /dev/null +++ b/lib/views/team/selectOrganization/select_organization_view.dart @@ -0,0 +1,204 @@ +import 'package:animated_tree_view/animated_tree_view.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:starwork_flutter/api/model/team/response/depart_list_reponse.dart'; +import 'package:starwork_flutter/base/app_logger.dart'; +import 'package:starwork_flutter/common/constant/app_colors.dart'; +import 'package:starwork_flutter/common/constant/app_images.dart'; +import 'package:starwork_flutter/common/widgets/custome_app_bar_wdiget.dart'; +import 'select_organization_controller.dart'; + +class SelectOrganizationView extends GetView { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.scaffoldBackgroundColor, + appBar: CustomAppBarWidget( + title: '选择组织'.tr, + backgroundColor: AppColors.scaffoldBackgroundColor, + leading: IconButton( + icon: const Icon( + Icons.clear_rounded, + color: Colors.black, + ), + onPressed: () => Navigator.of(context).pop(), + ), + ), + body: SafeArea( + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: 10.w, + vertical: 10.h, + ), + child: Column( + children: [ + _buildSearchBar(), + SizedBox( + height: 10.h, + ), + Container( + alignment: Alignment.centerLeft, + child: Text( + '组织架构', + textAlign: TextAlign.left, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w500, + color: Colors.grey, + ), + ), + ), + SizedBox( + height: 10.h, + ), + Expanded( + child: Obx( + () => Container( + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(8.0)), + ), + child: TreeView.simple( + tree: controller.treeData.value ?? TreeNode.root(), + showRootNode: false, + expansionIndicatorBuilder: noExpansionIndicatorBuilder, + indentation: const Indentation(style: IndentStyle.roundJoint), + onItemTap: (item) { + AppLogger.highlight('message:${item.data}'); + if (item.data is DepartItem) { + Get.back(result: item.data); + } + }, + onTreeReady: (c) { + controller.treeViewController = c; + }, + builder: (context, node) { + String title = ""; + DepartItem departInfo = DepartItem(); + bool isPersonItem = node.data is PersonItem; // 判断当前节点是否是人员节点 + + if (node.data is DepartItem) { + final depart = node.data as DepartItem; + title = depart.departName ?? "未命名部门"; + } + + return Container( + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 6.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Row( + children: [ + !isPersonItem + ? Container( + width: 34.w, + height: 34.w, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(8.r), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(8.r), + child: Icon( + Icons.folder, + size: 22.w, + color: Colors.blue, + ), + ), + ) + : Container( + width: 34.w, + height: 34.w, + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: BorderRadius.circular(8.r), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(8.r), + child: Image( + image: const AssetImage(AppImages.defaultAvatar), + width: 22.w, + height: 22.w, + fit: BoxFit.cover, + gaplessPlayback: true, + filterQuality: FilterQuality.medium, + errorBuilder: (context, error, stackTrace) { + return Icon( + Icons.person, + size: 30.sp, + color: Colors.grey[400], + ); + }, + ), + ), + ), + SizedBox( + width: 10.w, + ), + Expanded( + child: Text( + title, + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w500, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ), + ], + ), + ); + }, + ), + ), + ), + ) + ], + ), + ), + ), + ); + } + + _buildSearchBar() { + return TextField( + controller: controller.searchInputController, + textInputAction: TextInputAction.search, + decoration: InputDecoration( + hintText: '请输入设备名称'.tr, + hintStyle: TextStyle( + fontSize: 14.sp, + color: const Color(0xFF999999), + ), + prefixIcon: const Icon( + Icons.search, + color: Color(0xFF999999), + ), + filled: true, + // 启用背景填充 + fillColor: const Color(0xFFf0f0f0), + // 灰色背景(可调整色值) + border: InputBorder.none, + // 设置内边距 + contentPadding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), + focusedBorder: OutlineInputBorder( + borderSide: const BorderSide( + color: Colors.blue, + width: 1.5, + ), + borderRadius: BorderRadius.circular(8.0.r), + ), + enabledBorder: OutlineInputBorder( + borderSide: const BorderSide(color: Colors.transparent), + borderRadius: BorderRadius.circular(8.0.r), + ), + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 25d1c87..9bd246b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + animated_tree_view: + dependency: "direct main" + description: + name: animated_tree_view + sha256: ed982be7fa2cf51b62bb76e95b6a0f423cde12f1da8745a1da938e82a7baacf2 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.3.0" archive: dependency: transitive description: @@ -105,6 +113,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.7.11" + diffutil_dart: + dependency: transitive + description: + name: diffutil_dart + sha256: "5e74883aedf87f3b703cb85e815bdc1ed9208b33501556e4a8a5572af9845c81" + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.0.1" dio: dependency: "direct main" description: @@ -153,6 +169,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -509,6 +533,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.28.0" + scroll_to_index: + dependency: transitive + description: + name: scroll_to_index + sha256: b707546e7500d9f070d63e5acf74fd437ec7eeeb68d3412ef7b0afada0b4f176 + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.1" shared_preferences: dependency: "direct main" description: @@ -657,6 +689,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.1.0" + uuid: + dependency: transitive + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.5.1" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 40cd4c6..9ec6a24 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -43,7 +43,8 @@ dependencies: # 星云sdk starcloud: path: ../starcloud-sdk-flutter - + # 树形结构 + animated_tree_view: ^2.3.0