From 85d9141d172c1da2dfd31ab702a31b053f9829be Mon Sep 17 00:00:00 2001 From: liyi Date: Tue, 14 Oct 2025 13:53:01 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E9=83=A8=E5=88=86?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/icon/icon_shot_face.jpg | Bin 0 -> 9767 bytes assets/icon/icon_shot_face_error_1.jpg | Bin 0 -> 6174 bytes assets/icon/icon_shot_face_error_2.jpg | Bin 0 -> 7218 bytes assets/icon/icon_shot_face_error_3.jpg | Bin 0 -> 4789 bytes lib/api/api_path.dart | 3 + .../request/edit_person_info_request.dart | 81 +++ .../team/request/pserson_list_request.dart | 62 ++ .../team/response/depart_list_reponse.dart | 4 +- .../response/person_details_response.dart | 147 ++++ .../team/response/person_list_response.dart | 140 ++++ lib/api/service/team_api_service.dart | 40 ++ lib/base/base_controller.dart | 2 + lib/common/constant/app_images.dart | 4 + .../constant/app_view_parameter_keys.dart | 6 + lib/common/widgets/custom_cell_widget.dart | 7 +- .../widgets/custome_app_bar_wdiget.dart | 2 + lib/routes/app_pages.dart | 41 ++ lib/routes/app_routes.dart | 6 + .../access_control_manage_view.dart | 4 + lib/views/main/main_controller.dart | 5 +- lib/views/main/main_view.dart | 4 + lib/views/team/addPerson/add_person_view.dart | 9 +- .../selectRole/select_role_controller.dart | 51 +- .../selectRole/select_role_view.dart | 31 +- .../team/editPerson/edit_person_binding.dart | 10 + .../editPerson/edit_person_controller.dart | 201 ++++++ .../team/editPerson/edit_person_view.dart | 652 ++++++++++++++++++ .../personInfo/person_info_binding.dart | 10 + .../personInfo/person_info_controller.dart | 17 + .../personInfo/person_info_view.dart | 193 ++++++ .../team/enterFace/enter_face_binding.dart | 10 + .../team/enterFace/enter_face_controller.dart | 16 + lib/views/team/enterFace/enter_face_view.dart | 375 ++++++++++ .../team/faceAudit/face_audit_binding.dart | 10 + .../team/faceAudit/face_audit_controller.dart | 6 + lib/views/team/faceAudit/face_audit_view.dart | 83 +++ .../team/faceInfo/face_info_binding.dart | 10 + .../team/faceInfo/face_info_controller.dart | 31 + lib/views/team/faceInfo/face_info_view.dart | 193 ++++++ .../new_person_auditing_binding.dart | 10 + .../new_person_auditing_controller.dart | 6 + .../new_person_auditing_view.dart | 23 + .../personnel_manage_view.dart | 84 ++- 43 files changed, 2527 insertions(+), 62 deletions(-) create mode 100644 assets/icon/icon_shot_face.jpg create mode 100644 assets/icon/icon_shot_face_error_1.jpg create mode 100644 assets/icon/icon_shot_face_error_2.jpg create mode 100644 assets/icon/icon_shot_face_error_3.jpg create mode 100644 lib/api/model/team/request/edit_person_info_request.dart create mode 100644 lib/api/model/team/request/pserson_list_request.dart create mode 100644 lib/api/model/team/response/person_details_response.dart create mode 100644 lib/api/model/team/response/person_list_response.dart create mode 100644 lib/views/team/editPerson/edit_person_binding.dart create mode 100644 lib/views/team/editPerson/edit_person_controller.dart create mode 100644 lib/views/team/editPerson/edit_person_view.dart create mode 100644 lib/views/team/editPerson/personInfo/person_info_binding.dart create mode 100644 lib/views/team/editPerson/personInfo/person_info_controller.dart create mode 100644 lib/views/team/editPerson/personInfo/person_info_view.dart create mode 100644 lib/views/team/enterFace/enter_face_binding.dart create mode 100644 lib/views/team/enterFace/enter_face_controller.dart create mode 100644 lib/views/team/enterFace/enter_face_view.dart create mode 100644 lib/views/team/faceAudit/face_audit_binding.dart create mode 100644 lib/views/team/faceAudit/face_audit_controller.dart create mode 100644 lib/views/team/faceAudit/face_audit_view.dart create mode 100644 lib/views/team/faceInfo/face_info_binding.dart create mode 100644 lib/views/team/faceInfo/face_info_controller.dart create mode 100644 lib/views/team/faceInfo/face_info_view.dart create mode 100644 lib/views/team/newPersonAuditing/new_person_auditing_binding.dart create mode 100644 lib/views/team/newPersonAuditing/new_person_auditing_controller.dart create mode 100644 lib/views/team/newPersonAuditing/new_person_auditing_view.dart diff --git a/assets/icon/icon_shot_face.jpg b/assets/icon/icon_shot_face.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0e1bcd5cbc5b184683bc028f29ee450565a5d5a1 GIT binary patch literal 9767 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 literal 0 HcmV?d00001 diff --git a/assets/icon/icon_shot_face_error_1.jpg b/assets/icon/icon_shot_face_error_1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f1af0dffdf577a77e4d1f0aa4f7ae65d6def9ece GIT binary patch literal 6174 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 literal 0 HcmV?d00001 diff --git a/assets/icon/icon_shot_face_error_2.jpg b/assets/icon/icon_shot_face_error_2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..41ec13b0ac4164575f4e1e643a9e59162dd7d70f GIT binary patch literal 7218 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 mJgSeV6 literal 0 HcmV?d00001 diff --git a/lib/api/api_path.dart b/lib/api/api_path.dart index 5b3f4e4..3d80cfc 100644 --- a/lib/api/api_path.dart +++ b/lib/api/api_path.dart @@ -10,6 +10,7 @@ class ApiPath { static const String bindTeamStarCloudAccount = "/v1/team/bindStarCloudAccount"; static const String updateTeamInfo = "/v1/team/updateTeam"; static const String teamDetail = "/v1/team/detail"; + static const String teamPersonDetail = "/v1/team/personDetail"; static const String getInviteInfo = "/v1/team/getInviteInfo"; static const String getTeamInviteConfig = "/v1/team/teamApplyConfig"; static const String updateTeamInviteConfig = "/v1/team/teamPersonConfigUpdate"; @@ -18,4 +19,6 @@ class ApiPath { static const String roleList = "/v1/team/roleList"; static const String roleCreate = "/v1/team/roleCreate"; static const String createPerson = "/v1/team/personCreate"; + static const String updatePerson = "/v1/team/personUpdate"; + static const String personList = "/v1/team/personList"; } diff --git a/lib/api/model/team/request/edit_person_info_request.dart b/lib/api/model/team/request/edit_person_info_request.dart new file mode 100644 index 0000000..d3a41fd --- /dev/null +++ b/lib/api/model/team/request/edit_person_info_request.dart @@ -0,0 +1,81 @@ +class EditPersonInfoRequest { + String? personNo; + String? departNo; + String? personName; + bool? createUser; + String? phone; + int? sex; + String? position; + String? remark; + List? associateUsers; + int? limitType; + String? limitStartTime; + String? limitEndTime; + String? idCard; + List? roleIds; + bool? isSendSms; + bool? isConfirm; + String? jobNumber; + + EditPersonInfoRequest({ + this.personNo, + this.departNo, + this.personName, + this.createUser, + this.phone, + this.sex, + this.position, + this.remark, + this.associateUsers, + this.limitType, + this.limitStartTime, + this.limitEndTime, + this.idCard, + this.roleIds, + this.isSendSms, + this.isConfirm, + this.jobNumber, + }); + + EditPersonInfoRequest.fromJson(Map json) { + personNo = json['personNo'] as String?; + departNo = json['departNo'] as String?; + personName = json['personName'] as String?; + createUser = json['createUser'] as bool?; + phone = json['phone'] as String?; + sex = json['sex'] as int?; + position = json['position'] as String?; + remark = json['remark'] as String?; + associateUsers = json['associateUsers']?.cast(); + limitType = json['limitType'] as int?; + limitStartTime = json['limitStartTime'] as String?; + limitEndTime = json['limitEndTime'] as String?; + idCard = json['idCard'] as String?; + roleIds = json['roleIds']?.cast(); + isSendSms = json['isSendSms'] as bool?; + isConfirm = json['isConfirm'] as bool?; + jobNumber = json['jobNumber'] as String?; + } + + Map toJson() { + final Map data = {}; + if (personNo != null) data['personNo'] = personNo; + if (departNo != null) data['departNo'] = departNo; + if (personName != null) data['personName'] = personName; + if (createUser != null) data['createUser'] = createUser; + if (phone != null) data['phone'] = phone; + if (sex != null) data['sex'] = sex; + if (position != null) data['position'] = position; + if (remark != null) data['remark'] = remark; + if (associateUsers != null) data['associateUsers'] = associateUsers; + if (limitType != null) data['limitType'] = limitType; + if (limitStartTime != null) data['limitStartTime'] = limitStartTime; + if (limitEndTime != null) data['limitEndTime'] = limitEndTime; + if (idCard != null) data['idCard'] = idCard; + if (roleIds != null) data['roleIds'] = roleIds; + if (isSendSms != null) data['isSendSms'] = isSendSms; + if (isConfirm != null) data['isConfirm'] = isConfirm; + if (jobNumber != null) data['jobNumber'] = jobNumber; + return data; + } +} diff --git a/lib/api/model/team/request/pserson_list_request.dart b/lib/api/model/team/request/pserson_list_request.dart new file mode 100644 index 0000000..5df86f3 --- /dev/null +++ b/lib/api/model/team/request/pserson_list_request.dart @@ -0,0 +1,62 @@ +class PersonListRequest { + int? pageNo; + int? pageSize; + String? departNo; + String? personName; + String? phone; + int? state; + String? jobNumber; + String? cardNo; + bool? hasLeafDepart; + int? userState; + List? passPorts; + + PersonListRequest({ + this.pageNo, + this.pageSize, + this.departNo, + this.personName, + this.phone, + this.state, + this.jobNumber, + this.cardNo, + this.hasLeafDepart, + this.userState, + this.passPorts, + }); + + PersonListRequest.fromJson(Map json) { + pageNo = json['pageNo'] as int?; + pageSize = json['pageSize'] as int?; + departNo = json['departNo'] as String?; + personName = json['personName'] as String?; + phone = json['phone'] as String?; + state = json['state'] as int?; + jobNumber = json['jobNumber'] as String?; + cardNo = json['cardNo'] as String?; + hasLeafDepart = json['hasLeafDepart'] as bool?; + userState = json['userState'] as int?; + passPorts = json['passPorts']?.cast(); + } + + Map toJson() { + final Map data = {}; + if (pageNo != null) data['pageNo'] = pageNo; + if (pageSize != null) data['pageSize'] = pageSize; + if (departNo != null) data['departNo'] = departNo; + if (personName != null) data['personName'] = personName; + if (phone != null) data['phone'] = phone; + if (state != null) data['state'] = state; + if (jobNumber != null) data['jobNumber'] = jobNumber; + if (cardNo != null) data['cardNo'] = cardNo; + if (hasLeafDepart != null) data['hasLeafDepart'] = hasLeafDepart; + if (userState != null) data['userState'] = userState; + if (passPorts != null) data['passPorts'] = passPorts; + return data; + } + + @override + String toString() { + return 'PersonListRequest{pageNo: $pageNo, pageSize: $pageSize, departNo: $departNo, personName: $personName, phone: $phone, state: $state, jobNumber: $jobNumber, cardNo: $cardNo, hasLeafDepart: $hasLeafDepart, userState: $userState, passPorts: $passPorts}'; + } +} diff --git a/lib/api/model/team/response/depart_list_reponse.dart b/lib/api/model/team/response/depart_list_reponse.dart index 23c0dbd..4a5112d 100644 --- a/lib/api/model/team/response/depart_list_reponse.dart +++ b/lib/api/model/team/response/depart_list_reponse.dart @@ -31,8 +31,8 @@ class DepartListResponse { class DepartItem { final int? id; final String? teamNo; - final String? departNo; - final String? departName; + String? departNo; + String? departName; final int? parentId; final PersonItem? leader; final int? level; diff --git a/lib/api/model/team/response/person_details_response.dart b/lib/api/model/team/response/person_details_response.dart new file mode 100644 index 0000000..760e3a5 --- /dev/null +++ b/lib/api/model/team/response/person_details_response.dart @@ -0,0 +1,147 @@ +import 'package:starwork_flutter/api/model/team/response/role_list_response.dart'; + +class PersonDetailsResponse { + int? id; + String? teamNo; + String? departNo; + String? departName; + String? personNo; + String? personName; + int? jobNumber; + String? phone; + int? sex; + String? sexName; + String? pathName; + String? state; + int? userState; + bool? isSuper; + bool? isOperationUser; + String? position; + String? remark; + String? idCard; + List? associateUsers; + List? roleList; + int? limitType; + String? limitStartTime; + String? limitEndTime; + + PersonDetailsResponse({ + this.id, + this.teamNo, + this.departNo, + this.departName, + this.personNo, + this.personName, + this.jobNumber, + this.phone, + this.sex, + this.sexName, + this.pathName, + this.state, + this.userState, + this.isSuper, + this.isOperationUser, + this.position, + this.remark, + this.idCard, + this.associateUsers, + this.roleList, + this.limitType, + this.limitStartTime, + this.limitEndTime, + }); + + PersonDetailsResponse.fromJson(Map json) { + id = json['id'] as int?; + teamNo = json['teamNo'] as String?; + departNo = json['departNo'] as String?; + departName = json['departName'] as String?; + personNo = json['personNo'] as String?; + personName = json['personName'] as String?; + jobNumber = json['jobNumber'] as int?; + phone = json['phone'] as String?; + sex = json['sex'] as int?; + sexName = json['sexName'] as String?; + pathName = json['pathName'] as String?; + state = json['state'] as String?; + userState = json['userState'] as int?; + isSuper = json['isSuper'] as bool?; + isOperationUser = json['isOperationUser'] as bool?; + position = json['position'] as String?; + remark = json['remark'] as String?; + idCard = json['idCard'] as String?; + limitType = json['limitType'] as int?; // 添加这一行 + limitStartTime = json['limitStartTime'] as String?; // 添加这一行 + limitEndTime = json['limitEndTime'] as String?; // 添加这一行 + + if (json['associateUsers'] != null) { + associateUsers = []; + json['associateUsers'].forEach((v) { + associateUsers!.add(AssociateUser.fromJson(v as Map)); + }); + } + + if (json['roleList'] != null) { + roleList = []; + json['roleList'].forEach((v) { + roleList!.add(RoleListResponse.fromJson(v as Map)); + }); + } + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['teamNo'] = teamNo; + data['departNo'] = departNo; + data['departName'] = departName; + data['personNo'] = personNo; + data['personName'] = personName; + data['jobNumber'] = jobNumber; + data['phone'] = phone; + data['sex'] = sex; + data['sexName'] = sexName; + data['pathName'] = pathName; + data['state'] = state; + data['userState'] = userState; + data['isSuper'] = isSuper; + data['isOperationUser'] = isOperationUser; + data['position'] = position; + data['remark'] = remark; + data['idCard'] = idCard; + data['limitType'] = limitType; // 添加这一行 + data['limitStartTime'] = limitStartTime; // 添加这一行 + data['limitEndTime'] = limitEndTime; // 添加这一行 + + if (associateUsers != null) { + data['associateUsers'] = associateUsers!.map((v) => v.toJson()).toList(); + } + + if (roleList != null) { + data['roleList'] = roleList!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class AssociateUser { + String? personNo; + String? personName; + String? avatarUrl; + + AssociateUser({this.personNo, this.personName, this.avatarUrl}); + + AssociateUser.fromJson(Map json) { + personNo = json['personNo'] as String?; + personName = json['personName'] as String?; + avatarUrl = json['avatarUrl'] as String?; + } + + Map toJson() { + final Map data = {}; + data['personNo'] = personNo; + data['personName'] = personName; + data['avatarUrl'] = avatarUrl; + return data; + } +} diff --git a/lib/api/model/team/response/person_list_response.dart b/lib/api/model/team/response/person_list_response.dart new file mode 100644 index 0000000..d82606b --- /dev/null +++ b/lib/api/model/team/response/person_list_response.dart @@ -0,0 +1,140 @@ +class PersonListResponse { + int? pageNo; + int? pageSize; + int? total; + int? pages; + List? list; + + PersonListResponse({ + this.pageNo, + this.pageSize, + this.total, + this.pages, + this.list, + }); + + + PersonListResponse.fromJson(Map json) { + pageNo = json['pageNo'] as int?; + pageSize = json['pageSize'] as int?; + total = json['total'] as int?; + pages = json['pages'] as int?; + + if (json['list'] != null) { + list = []; + json['list'].forEach((v) { + list!.add(PersonItem.fromJson(v as Map)); + }); + } + } + + Map toJson() { + final Map data = {}; + data['pageNo'] = pageNo; + data['pageSize'] = pageSize; + data['total'] = total; + data['pages'] = pages; + + if (list != null) { + data['list'] = list!.map((v) => v.toJson()).toList(); + } + + return data; + } + + @override + String toString() { + return 'PersonListResponse{pageNo: $pageNo, pageSize: $pageSize, total: $total, pages: $pages, list: $list}'; + } +} + +class PersonItem { + int? id; + String? teamNo; + String? departNo; + String? departName; + String? personNo; + String? personName; + int? jobNumber; + String? phone; + int? sex; + String? sexName; + String? pathName; + String? state; + int? userState; + int? faceCount; + int? fingerprintCount; + int? cardCount; + bool? isSuper; + bool? isOperationUser; + + PersonItem({ + this.id, + this.teamNo, + this.departNo, + this.departName, + this.personNo, + this.personName, + this.jobNumber, + this.phone, + this.sex, + this.sexName, + this.pathName, + this.state, + this.userState, + this.faceCount, + this.fingerprintCount, + this.cardCount, + this.isSuper, + this.isOperationUser, + }); + + PersonItem.fromJson(Map json) { + id = json['id'] as int?; + teamNo = json['teamNo'] as String?; + departNo = json['departNo'] as String?; + departName = json['departName'] as String?; + personNo = json['personNo'] as String?; + personName = json['personName'] as String?; + jobNumber = json['jobNumber'] as int?; + phone = json['phone'] as String?; + sex = json['sex'] as int?; + sexName = json['sexName'] as String?; + pathName = json['pathName'] as String?; + state = json['state'] as String?; + userState = json['userState'] as int?; + faceCount = json['faceCount'] as int?; + fingerprintCount = json['fingerprintCount'] as int?; + cardCount = json['cardCount'] as int?; + isSuper = json['isSuper'] as bool?; + isOperationUser = json['isOperationUser'] as bool?; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['teamNo'] = teamNo; + data['departNo'] = departNo; + data['departName'] = departName; + data['personNo'] = personNo; + data['personName'] = personName; + data['jobNumber'] = jobNumber; + data['phone'] = phone; + data['sex'] = sex; + data['sexName'] = sexName; + data['pathName'] = pathName; + data['state'] = state; + data['userState'] = userState; + data['faceCount'] = faceCount; + data['fingerprintCount'] = fingerprintCount; + data['cardCount'] = cardCount; + data['isSuper'] = isSuper; + data['isOperationUser'] = isOperationUser; + return data; + } + + @override + String toString() { + return 'PersonItem{id: $id, teamNo: $teamNo, departNo: $departNo, departName: $departName, personNo: $personNo, personName: $personName, jobNumber: $jobNumber, phone: $phone, sex: $sex, sexName: $sexName, pathName: $pathName, state: $state, userState: $userState, faceCount: $faceCount, fingerprintCount: $fingerprintCount, cardCount: $cardCount, isSuper: $isSuper, isOperationUser: $isOperationUser}'; + } +} diff --git a/lib/api/service/team_api_service.dart b/lib/api/service/team_api_service.dart index 424c673..1d42875 100644 --- a/lib/api/service/team_api_service.dart +++ b/lib/api/service/team_api_service.dart @@ -8,8 +8,10 @@ import 'package:starwork_flutter/api/model/team/request/create_new_depart_reques import 'package:starwork_flutter/api/model/team/request/create_new_person.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/edit_person_info_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/pserson_list_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'; @@ -17,6 +19,8 @@ import 'package:starwork_flutter/api/model/team/response/all_team_list_response. 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/person_details_response.dart'; +import 'package:starwork_flutter/api/model/team/response/person_list_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_details_response.dart'; @@ -105,6 +109,17 @@ class TeamApiService { ); } + // 查询人员详情 + Future> requestPersonDetails({ + required String personNo, + }) { + return _api.makeRequest( + path: ApiPath.teamPersonDetail, + method: HttpConstant.post, + data: {"personNo": personNo}, + fromJson: (data) => PersonDetailsResponse.fromJson(data), + ); + } /// 获取邀请信息 Future> requestInviteInfo({ @@ -195,4 +210,29 @@ class TeamApiService { fromJson: (data) {}, ); } + + // 编辑人员信息 + Future> requestEditPersonInfo({ + required EditPersonInfoRequest request, + }) { + return _api.makeRequest( + path: ApiPath.updatePerson, + method: HttpConstant.post, + data: request.toJson(), + fromJson: (data) {}, + ); + } + + // 获取人员列表 + Future> requestPersonList({ + required PersonListRequest request, + }) { + return _api.makeRequest( + path: ApiPath.personList, + method: HttpConstant.post, + data: request.toJson(), + fromJson: (data) => PersonListResponse.fromJson(data), + ); + } + } diff --git a/lib/base/base_controller.dart b/lib/base/base_controller.dart index 8e3c94d..a67a0f2 100644 --- a/lib/base/base_controller.dart +++ b/lib/base/base_controller.dart @@ -66,6 +66,8 @@ class BaseController extends GetxController { if (EasyLoading.isShow) { EasyLoading.dismiss(); } + // 收起键盘 + FocusScope.of(Get.overlayContext!).unfocus(); super.onClose(); } diff --git a/lib/common/constant/app_images.dart b/lib/common/constant/app_images.dart index 55488d9..fbc0567 100644 --- a/lib/common/constant/app_images.dart +++ b/lib/common/constant/app_images.dart @@ -55,6 +55,10 @@ class AppImages{ static const String iconTableMenu = 'assets/icon/icon_table_menu.png'; static const String iconVip = 'assets/icon/icon_vip.png'; static const String defaultAvatar = 'assets/images/default_avatar.png'; + static const String iconShotFace = 'assets/icon/icon_shot_face.jpg'; + static const String iconShotFaceError1 = 'assets/icon/icon_shot_face_error_1.jpg'; + static const String iconShotFaceError2 = 'assets/icon/icon_shot_face_error_2.jpg'; + static const String iconShotFaceError3 = 'assets/icon/icon_shot_face_error_3.jpg'; static const String iconLockGroupItem = 'assets/icon/icon_lockGroup_item.png'; static const String iconLockTypeDoorLock = 'assets/icon/lockType_doorLock.png'; static const String iconFace = 'assets/icon/icon_face.jpg'; diff --git a/lib/common/constant/app_view_parameter_keys.dart b/lib/common/constant/app_view_parameter_keys.dart index 8ada631..709932c 100644 --- a/lib/common/constant/app_view_parameter_keys.dart +++ b/lib/common/constant/app_view_parameter_keys.dart @@ -4,8 +4,14 @@ class AppViewParameterKeys { static const String networkInfo = "networkInfo"; static const String teamInfo = "teamInfo"; static const String departItem = "departItem"; + static const String departNo = "departNo"; + static const String personItem = "personItem"; static const String isLongTerm = "isLongTerm"; static const String startDate = "startDate"; static const String endDate = "endDate"; + static const String viewType = "viewType"; + static const String edit = "edit"; + static const String add = "add"; + static const String roleList = "roleList"; } \ No newline at end of file diff --git a/lib/common/widgets/custom_cell_widget.dart b/lib/common/widgets/custom_cell_widget.dart index 1ea1e7f..52e0741 100644 --- a/lib/common/widgets/custom_cell_widget.dart +++ b/lib/common/widgets/custom_cell_widget.dart @@ -8,6 +8,7 @@ class CustomCellWidget extends StatelessWidget { super.key, required this.leftText, this.leftSubText, + this.leftWidget, this.leftIcon, this.rightWidget, this.onTap, @@ -17,6 +18,7 @@ class CustomCellWidget extends StatelessWidget { final String leftText; final String? leftSubText; final Icon? leftIcon; + final Widget? leftWidget; final Widget? rightWidget; final GestureTapCallback? onTap; final bool visible; // 控制是否显示 @@ -48,8 +50,9 @@ class CustomCellWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ - if (leftIcon != null) leftIcon!, - if (leftIcon != null) SizedBox(width: 4.w), + if (leftWidget != null) leftWidget!, + if (leftWidget == null && leftIcon != null) leftIcon!, + if (leftWidget == null && leftIcon != null) SizedBox(width: 4.w), Expanded( child: Text( leftText, diff --git a/lib/common/widgets/custome_app_bar_wdiget.dart b/lib/common/widgets/custome_app_bar_wdiget.dart index 849aaa5..759de6a 100644 --- a/lib/common/widgets/custome_app_bar_wdiget.dart +++ b/lib/common/widgets/custome_app_bar_wdiget.dart @@ -55,6 +55,8 @@ class CustomAppBarWidget extends StatelessWidget implements PreferredSizeWidget centerTitle: false, backgroundColor: backgroundColor, elevation: elevation, + surfaceTintColor: Colors.transparent, // Material3 下控制表面颜色 + shadowColor: Colors.transparent, // 去掉阴影 ); } diff --git a/lib/routes/app_pages.dart b/lib/routes/app_pages.dart index a43dd0f..3b851b6 100644 --- a/lib/routes/app_pages.dart +++ b/lib/routes/app_pages.dart @@ -44,12 +44,24 @@ import 'package:starwork_flutter/views/team/addPerson/selectRole/select_role_bin 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/editPerson/edit_person_binding.dart'; +import 'package:starwork_flutter/views/team/editPerson/edit_person_view.dart'; +import 'package:starwork_flutter/views/team/editPerson/personInfo/person_info_binding.dart'; +import 'package:starwork_flutter/views/team/editPerson/personInfo/person_info_view.dart'; +import 'package:starwork_flutter/views/team/enterFace/enter_face_binding.dart'; +import 'package:starwork_flutter/views/team/enterFace/enter_face_view.dart'; +import 'package:starwork_flutter/views/team/faceAudit/face_audit_binding.dart'; +import 'package:starwork_flutter/views/team/faceAudit/face_audit_view.dart'; +import 'package:starwork_flutter/views/team/faceInfo/face_info_binding.dart'; +import 'package:starwork_flutter/views/team/faceInfo/face_info_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/newPersonAuditing/new_person_auditing_binding.dart'; +import 'package:starwork_flutter/views/team/newPersonAuditing/new_person_auditing_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'; @@ -263,5 +275,34 @@ class AppPages { page: () => SelectPersonView(), binding: SelectPersonBinding(), ), + GetPage( + name: AppRoutes.teamEditPerson, + page: () => EditPersonView(), + binding: EditPersonBinding(), + ), + GetPage( + name: AppRoutes.teamEditPersonInfo, + page: () => PersonInfoView(), + binding: PersonInfoBinding(), + ), + GetPage( + name: AppRoutes.teamFaceInfo, + page: () => FaceInfoView(), + binding: FaceInfoBinding(), + ), + GetPage( + name: AppRoutes.teamFaceAudit, + page: () => FaceAuditView(), + binding: FaceAuditBinding(), + ), + GetPage( + name: AppRoutes.teamEnterFace, + page: () => EnterFaceView(), + binding: EnterFaceBinding(), + ),GetPage( + name: AppRoutes.teamNewPersonAuditing, + page: () => NewPersonAuditingView(), + binding: NewPersonAuditingBinding(), + ), ]; } diff --git a/lib/routes/app_routes.dart b/lib/routes/app_routes.dart index 4b19782..bd44c5c 100644 --- a/lib/routes/app_routes.dart +++ b/lib/routes/app_routes.dart @@ -25,7 +25,13 @@ class AppRoutes{ static const String teamAddRole = '/team/addRole'; static const String teamSelectRole = '/team/selectRole'; static const String teamAddPersonEditValidity = '/team/addPerson/editValidity'; + static const String teamEditPerson = '/team/editPerson'; + static const String teamEditPersonInfo = '/team/editPerson/personInfo'; static const String teamAddOrganization = '/team/addOrganization'; + static const String teamFaceInfo = '/team/faceInfo'; + static const String teamFaceAudit = '/team/faceAudit'; + static const String teamEnterFace = '/team/enterFace'; + static const String teamNewPersonAuditing = '/team/newPersonAuditing'; static const String deviceManage = '/device/deviceManage'; static const String searchDevice = '/device/searchDevice'; static const String confirmPairDevice = '/device/confirmPairDevice'; diff --git a/lib/views/accessControlManage/access_control_manage_view.dart b/lib/views/accessControlManage/access_control_manage_view.dart index 19c27a2..bd31b64 100644 --- a/lib/views/accessControlManage/access_control_manage_view.dart +++ b/lib/views/accessControlManage/access_control_manage_view.dart @@ -30,6 +30,10 @@ class AccessControlManageView extends GetView { // 多于3个时建议 fixed selectedFontSize: 12.sp, unselectedFontSize: 12.sp, + // 选中时的颜色 + fixedColor: Colors.blue, + // 未选中时的颜色 + unselectedItemColor: Colors.grey, ), ), ); diff --git a/lib/views/main/main_controller.dart b/lib/views/main/main_controller.dart index f47384f..1d6ffbd 100644 --- a/lib/views/main/main_controller.dart +++ b/lib/views/main/main_controller.dart @@ -64,8 +64,7 @@ class MainController extends BaseController { late StreamSubscription _refreshDeviceListSubscription; @override - void onReady() { - super.onReady(); + void onInit() { /// 请求团队信息 requestAllTeamInfoList(); @@ -75,6 +74,8 @@ class MainController extends BaseController { /// 请求账户信息 requestUserAccountInfo(); + super.onInit(); + // 监听刷新设备列表事件 _refreshDeviceListSubscription = EventBusUtil().instance.on().listen((event) { _requestTeamDeviceList(); diff --git a/lib/views/main/main_view.dart b/lib/views/main/main_view.dart index b401e7a..3ab267e 100644 --- a/lib/views/main/main_view.dart +++ b/lib/views/main/main_view.dart @@ -31,6 +31,10 @@ class MainView extends GetView { // 多于3个时建议 fixed selectedFontSize: 12.sp, unselectedFontSize: 12.sp, + // 选中时的颜色 + fixedColor: Colors.blue, + // 未选中时的颜色 + unselectedItemColor: Colors.grey, ), ), drawer: Obx( diff --git a/lib/views/team/addPerson/add_person_view.dart b/lib/views/team/addPerson/add_person_view.dart index be4b987..df596a3 100644 --- a/lib/views/team/addPerson/add_person_view.dart +++ b/lib/views/team/addPerson/add_person_view.dart @@ -191,8 +191,13 @@ class AddPersonView extends GetView { ), CustomCellWidget( onTap: () async { - var result = - await Get.toNamed(AppRoutes.teamSelectRole, arguments: controller.selectedRoles); + var result = await Get.toNamed( + AppRoutes.teamSelectRole, + arguments: { + AppViewParameterKeys.viewType: AppViewParameterKeys.add, + AppViewParameterKeys.roleList: controller.selectedRoles + }, + ); if (result != null) { // 处理返回的角色数据 if (result is List) { diff --git a/lib/views/team/addPerson/selectRole/select_role_controller.dart b/lib/views/team/addPerson/selectRole/select_role_controller.dart index 01a58fd..57030a2 100644 --- a/lib/views/team/addPerson/selectRole/select_role_controller.dart +++ b/lib/views/team/addPerson/selectRole/select_role_controller.dart @@ -1,25 +1,48 @@ import 'package:get/get.dart'; +import 'package:starwork_flutter/api/model/team/request/edit_person_info_request.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/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/app_view_parameter_keys.dart'; class SelectRoleController extends BaseController { final teamApi = Get.find(); var roleList = [].obs; - var selectRoleIndexList = [].obs; + var selectRoleIdList = [].obs; + var selectedDepartNo = ''.obs; // 当前选中的组织 + + var viewType = ''.obs; // 存储从参数传递过来的已选角色 List? initialSelectedRoles; + final selectedPersonItem = Rx(null); @override void onInit() { super.onInit(); // 获取传递的参数 var arguments = Get.arguments; - if (arguments != null && arguments is List) { - initialSelectedRoles = arguments; + if (arguments != null && arguments is Map) { + // 从Map中提取角色列表 + var roleListArg = arguments[AppViewParameterKeys.roleList]; + + viewType.value = arguments[AppViewParameterKeys.viewType] ?? ''; + if (roleListArg != null && roleListArg is RxList) { + initialSelectedRoles = roleListArg; + } + if (viewType.value == AppViewParameterKeys.edit) { + var departNo = arguments[AppViewParameterKeys.departNo]; + var personItem = arguments[AppViewParameterKeys.personItem]; + if (departNo != null && departNo is String) { + selectedDepartNo.value = departNo; + } + if (personItem != null && personItem is PersonItem) { + selectedPersonItem.value = personItem; + } + } } requestRoleList(); } @@ -45,14 +68,30 @@ class SelectRoleController extends BaseController { // 在角色列表中查找对应的角色并设置选中状态 for (int i = 0; i < roleList.length; i++) { if (roleList[i].id == selectedRole.id) { - if (!selectRoleIndexList.contains(i)) { - selectRoleIndexList.add(i); + if (!selectRoleIdList.contains(selectedRole.id)) { + selectRoleIdList.add(selectedRole.id!); } break; } } } } - selectRoleIndexList.refresh(); + selectRoleIdList.refresh(); + } + + void requestEditRoles() async { + var response = await teamApi.requestEditPersonInfo( + request: EditPersonInfoRequest( + roleIds: selectRoleIdList, + departNo: selectedDepartNo.value, + personName: selectedPersonItem.value?.personName, + personNo: selectedPersonItem.value?.personNo, + ), + ); + if (response.isSuccess) { + showSuccess(); + } else { + showError(message: response.errorMsg!); + } } } diff --git a/lib/views/team/addPerson/selectRole/select_role_view.dart b/lib/views/team/addPerson/selectRole/select_role_view.dart index 9c1a8b5..6c66fd8 100644 --- a/lib/views/team/addPerson/selectRole/select_role_view.dart +++ b/lib/views/team/addPerson/selectRole/select_role_view.dart @@ -6,6 +6,7 @@ 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/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'; @@ -97,12 +98,12 @@ class SelectRoleView extends GetView { return GestureDetector( onTap: () { // 避免重复添加 - if (!controller.selectRoleIndexList.contains(index)) { - controller.selectRoleIndexList.add(index); + if (!controller.selectRoleIdList.contains(roleList.id)) { + controller.selectRoleIdList.add(roleList.id!); } else { - controller.selectRoleIndexList.remove(index); + controller.selectRoleIdList.remove(roleList.id); } - controller.selectRoleIndexList.refresh(); + controller.selectRoleIdList.refresh(); }, child: Padding( padding: EdgeInsets.symmetric( @@ -113,18 +114,18 @@ class SelectRoleView extends GetView { children: [ Obx( () => Checkbox( - value: controller.selectRoleIndexList.contains(index), + value: controller.selectRoleIdList.contains(roleList.id), activeColor: Colors.blue, onChanged: (value) { if (value == true) { // 避免重复添加 - if (!controller.selectRoleIndexList.contains(index)) { - controller.selectRoleIndexList.add(index); + if (!controller.selectRoleIdList.contains(roleList.id)) { + controller.selectRoleIdList.add(roleList.id!); } } else { - controller.selectRoleIndexList.remove(index); + controller.selectRoleIdList.remove(roleList.id); } - controller.selectRoleIndexList.refresh(); + controller.selectRoleIdList.refresh(); }, ), ), @@ -196,7 +197,7 @@ class SelectRoleView extends GetView { children: [ Obx( () => Text( - '已选中:${controller.selectRoleIndexList.length}个', + '已选中:${controller.selectRoleIdList.length}个', style: TextStyle( fontSize: 14.sp, color: Colors.grey, @@ -205,9 +206,13 @@ class SelectRoleView extends GetView { ), ElevatedButton( onPressed: () { - Get.back( - result: controller.selectRoleIndexList.map((e) => controller.roleList[e]).toList(), - ); + if (controller.viewType.value == AppViewParameterKeys.add) { + Get.back( + result: controller.selectRoleIdList.map((e) => controller.roleList[e]).toList(), + ); + } else if (controller.viewType.value == AppViewParameterKeys.edit) { + controller.requestEditRoles(); + } }.debounce(), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, diff --git a/lib/views/team/editPerson/edit_person_binding.dart b/lib/views/team/editPerson/edit_person_binding.dart new file mode 100644 index 0000000..b9b3e04 --- /dev/null +++ b/lib/views/team/editPerson/edit_person_binding.dart @@ -0,0 +1,10 @@ +import 'package:get/get.dart'; + +import 'edit_person_controller.dart'; + +class EditPersonBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => EditPersonController()); + } +} \ No newline at end of file diff --git a/lib/views/team/editPerson/edit_person_controller.dart b/lib/views/team/editPerson/edit_person_controller.dart new file mode 100644 index 0000000..a64e552 --- /dev/null +++ b/lib/views/team/editPerson/edit_person_controller.dart @@ -0,0 +1,201 @@ +import 'package:flutter/widgets.dart'; +import 'package:get/get.dart'; +import 'package:starwork_flutter/api/model/team/request/edit_person_info_request.dart'; +import 'package:starwork_flutter/api/model/team/response/depart_list_reponse.dart'; +import 'package:starwork_flutter/api/model/team/response/person_details_response.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'; +import 'package:starwork_flutter/common/constant/app_view_parameter_keys.dart'; + +class EditPersonController extends BaseController { + final selectedPersonItem = Rx(null); + RxString selectedGender = 'male'.obs; + final teamApi = Get.find(); + var selectedDepartItem = DepartItem().obs; // 当前选中的组织 + + var originalPersonData = Rx(null); // 人员原始的详情数据 + + TextEditingController nameInputController = TextEditingController(); + TextEditingController phoneInputController = TextEditingController(); + TextEditingController jobNoInputController = TextEditingController(); // 工号 + TextEditingController positionInputController = TextEditingController(); // 职务 + TextEditingController idCardInputController = TextEditingController(); // 身份证号码 + TextEditingController remarkInputController = TextEditingController(); // 备注 + var isLongTerm = true.obs; // 有效期是否为长期 + var startDate = 0.obs; // 使用时间戳表示开始时间 + var endDate = 0.obs; // 使用时间戳表示结束时间 + var userState = 0.obs; // 账号状态 + var selectedRoles = [].obs; + var isPhoneVisible = false.obs; // 控制手机号是否可见 + + @override + void onInit() async { + super.onInit(); + final args = Get.arguments; + if (args != null && args is PersonItem) { + selectedPersonItem.value = args; + } + + await requestPersonDetail(); + } + + requestPersonDetail() async { + if (selectedPersonItem.value != null && selectedPersonItem.value!.personNo != null) { + var response = await teamApi.requestPersonDetails(personNo: selectedPersonItem.value!.personNo!); + if (response.isSuccess) { + var data = response.data; + // 保存原始数据 + originalPersonData.value = data; + + nameInputController.text = data?.personName ?? ''; + phoneInputController.text = data?.phone ?? ''; + jobNoInputController.text = data?.jobNumber.toString() ?? ''; + positionInputController.text = data?.position ?? ''; + idCardInputController.text = data?.idCard ?? ''; + remarkInputController.text = data?.remark ?? ''; + selectedGender.value = data?.sex == 1 ? 'male' : 'female'; + isLongTerm.value = data?.limitType == 1; + startDate.value = _parseDateStringToTimestamp(data?.limitStartTime); + endDate.value = _parseDateStringToTimestamp(data?.limitEndTime); + userState.value = data?.userState ?? 2; + selectedRoles.value = data?.roleList ?? []; + selectedDepartItem.value.departNo = data?.departNo; + selectedDepartItem.value.departName = data?.departName; + selectedDepartItem.refresh(); + selectedRoles.refresh(); + } + } + } + + // 获取选中角色的显示文本 + String getSelectedRoleDisplayText() { + if (selectedRoles.isEmpty) { + return '请选择'; // 如果没有选中角色,显示"请选择" + } else { + // 将所有选中角色的名称用逗号连接 + return selectedRoles.map((role) => role.roleName ?? '').join('、'); // 使用顿号或逗号分隔 + } + } + + // 手机号脱敏处理方法 + String maskPhoneNumber(String? phone) { + if (phone == null || phone.isEmpty) return ''; + if (phone.length < 11) return phone; + + // 只显示前3位和后3位,中间5位用*代替 + return '${phone.substring(0, 3)}*****${phone.substring(8)}'; + } + + // 获取显示用的手机号(根据可见性决定是否脱敏) + String getDisplayPhone() { + final phone = selectedPersonItem.value?.phone ?? ''; + return isPhoneVisible.value ? phone : maskPhoneNumber(phone); + } + + // 辅助方法:将时间戳转换为日期字符串 + String formatTimestamp(int timestamp) { + if (timestamp <= 0) return '请选择'; + try { + final dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp); + return '${dateTime.year}-${dateTime.month.toString().padLeft(2, '0')}-${dateTime.day.toString().padLeft(2, '0')}'; + } catch (e) { + return '请选择'; + } + } + + // 将字符串日期转为时间戳 + int _parseDateStringToTimestamp(String? dateStr) { + if (dateStr == null || dateStr.isEmpty) return 0; + try { + // 假设日期格式为 "yyyy-MM-dd" 或其他标准格式 + final DateTime dateTime = DateTime.parse(dateStr); + return dateTime.millisecondsSinceEpoch; + } catch (e) { + // 如果解析失败,返回0 + return 0; + } + } + + void requestEditPersonInfo() async { + // 先检查是否有数据变更 + if (!_hasDataChanged()) { + AppLogger.debug('数据无变更,无需修改'); + return; + } + // 构建只包含变更字段的请求对象 + var request = EditPersonInfoRequest(); + request.personNo = selectedPersonItem.value?.personNo; + request.isConfirm = true; + + if (phoneInputController.text.trim() != (originalPersonData.value?.phone ?? '')) { + request.phone = phoneInputController.text.trim(); + } + + request.departNo = selectedDepartItem.value.departNo; + request.personName = nameInputController.text.trim(); + + if (selectedGender.value != (originalPersonData.value?.sex == 1 ? 'male' : 'female')) { + request.sex = selectedGender.value == 'male' ? 1 : 2; + } + + if (positionInputController.text.trim() != (originalPersonData.value?.position ?? '')) { + request.position = positionInputController.text.trim(); + } + + if (remarkInputController.text.trim() != (originalPersonData.value?.remark ?? '')) { + request.remark = remarkInputController.text.trim(); + } + + if (idCardInputController.text.trim() != (originalPersonData.value?.idCard ?? '')) { + request.idCard = idCardInputController.text.trim(); + } + + if (jobNoInputController.text.trim() != (originalPersonData.value?.jobNumber?.toString() ?? '')) { + request.jobNumber = jobNoInputController.text.trim(); + } + + if (isLongTerm.value != (originalPersonData.value?.limitType == 1)) { + request.limitType = isLongTerm.value ? 1 : 2; + } + + // 处理有效期时间字段 + if (!isLongTerm.value) { + var originalStartTime = _parseDateStringToTimestamp(originalPersonData.value?.limitStartTime); + var originalEndTime = _parseDateStringToTimestamp(originalPersonData.value?.limitEndTime); + + if (startDate.value != originalStartTime) { + request.limitStartTime = formatTimestamp(startDate.value); + } + + if (endDate.value != originalEndTime) { + request.limitEndTime = formatTimestamp(endDate.value); + } + } + var response = await teamApi.requestEditPersonInfo(request: request); + if (response.isSuccess) { + showSuccess(); + } else { + showError(message: response.errorMsg!); + } + } + + // 3. 创建比较方法判断是否有修改 + bool _hasDataChanged() { + if (originalPersonData.value == null) return true; + + return nameInputController.text.trim() != (originalPersonData.value?.personName ?? '') || + phoneInputController.text.trim() != (originalPersonData.value?.phone ?? '') || + jobNoInputController.text.trim() != (originalPersonData.value?.jobNumber?.toString() ?? '') || + positionInputController.text.trim() != (originalPersonData.value?.position ?? '') || + idCardInputController.text.trim() != (originalPersonData.value?.idCard ?? '') || + remarkInputController.text.trim() != (originalPersonData.value?.remark ?? '') || + selectedGender.value != (originalPersonData.value?.sex == 1 ? 'male' : 'female') || + selectedDepartItem.value.departNo != originalPersonData.value?.departNo || + isLongTerm.value != (originalPersonData.value?.limitType == 1) || + (!isLongTerm.value && + (startDate.value != _parseDateStringToTimestamp(originalPersonData.value?.limitStartTime) || + endDate.value != _parseDateStringToTimestamp(originalPersonData.value?.limitEndTime))); + } +} diff --git a/lib/views/team/editPerson/edit_person_view.dart b/lib/views/team/editPerson/edit_person_view.dart new file mode 100644 index 0000000..5316ee5 --- /dev/null +++ b/lib/views/team/editPerson/edit_person_view.dart @@ -0,0 +1,652 @@ +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/depart_list_reponse.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/constant/app_view_parameter_keys.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 'edit_person_controller.dart'; + +class EditPersonView extends GetView { + @override + Widget build(BuildContext context) { + // 即使不使用,只是引用一下 controller 就能触发初始化 + final _ = controller; // 添加这一行 + return Scaffold( + appBar: CustomAppBarWidget( + title: '编辑人员'.tr, + backgroundColor: AppColors.scaffoldBackgroundColor, + ), + 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: () async { + final result = await Get.toNamed(AppRoutes.teamSelectOrganization); + if (result != null && result is DepartItem) { + controller.selectedDepartItem.value = result; + controller.selectedDepartItem.refresh(); + } + }, + 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, + ), + ), + ), + 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: 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: controller.phoneInputController.text.isNotEmpty + ? Row( + children: [ + Text( + controller.getDisplayPhone(), + style: TextStyle( + fontSize: 14.sp, + color: Colors.black54, + fontWeight: FontWeight.w400, + ), + ), + SizedBox( + width: 10.w, + ), + GestureDetector( + onTap: () { + controller.isPhoneVisible.value = !controller.isPhoneVisible.value; + }, + child: Icon( + controller.isPhoneVisible.value ? Icons.visibility : Icons.visibility_off, + size: 16.sp, + color: Colors.grey, + ), + ), + ], + ) + : Expanded( + flex: 3, + child: TextField( + controller: controller.phoneInputController, + keyboardType: TextInputType.number, + 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: () { + Get.toNamed(AppRoutes.teamEditPersonInfo); + }, + leftText: '用户账号'.tr, + rightWidget: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Obx( + () => Text( + controller.userState.value == 1 + ? '已开通' + : controller.userState.value == 2 + ? '未开通' + : '已停用', + 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, + ), + SizedBox( + height: 68.w, + child: ListView( + scrollDirection: Axis.horizontal, + children: [ + _buildHorizontalItem(title: '人脸', count: 0), + SizedBox(width: 5.w), // 添加间距 + _buildHorizontalItem(title: '指纹', count: 0), + SizedBox(width: 5.w), // 添加间距 + _buildHorizontalItem(title: '卡片', count: 0), + SizedBox(width: 5.w), // 添加间距 + _buildHorizontalItem(title: '密码', count: 0), + ], + ), + ), + 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( + controller: controller.jobNoInputController, + 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: () async { + var result = await Get.toNamed(AppRoutes.teamAddPersonEditValidity, arguments: { + AppViewParameterKeys.isLongTerm: controller.isLongTerm.value, + AppViewParameterKeys.startDate: controller.startDate.value, + AppViewParameterKeys.endDate: controller.endDate.value, + }); + if (result != null) { + controller.isLongTerm.value = result[AppViewParameterKeys.isLongTerm]; + if (controller.isLongTerm.isFalse) { + controller.startDate.value = result[AppViewParameterKeys.startDate]; + controller.endDate.value = result[AppViewParameterKeys.endDate]; + } + } + }, + leftText: '有效期'.tr, + rightWidget: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Obx( + () => Text( + controller.isLongTerm.value + ? '长期'.tr + : '${controller.formatTimestamp(controller.startDate.value)} - ' + '${controller.formatTimestamp(controller.endDate.value)}', + 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( + controller: controller.positionInputController, + 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( + controller: controller.remarkInputController, + 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( + controller: controller.idCardInputController, + 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: () async {}, + 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: () async {}, + leftText: '人员ID'.tr, + rightWidget: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Obx( + () => Text( + controller.selectedPersonItem.value?.personNo ?? '', + style: TextStyle( + fontSize: 14.sp, + color: Colors.black54, + fontWeight: FontWeight.w400, + ), + ), + ), + ], + ), + ) + ], + ), + ], + ), + SizedBox( + height: 10.h, + ), + Column( + children: [ + SizedBox( + width: double.infinity, + child: Row( + children: [ + Obx( + () => Visibility( + visible: controller.selectedPersonItem.value!.phone!.isEmpty, + child: 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.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: () { + controller.requestEditPersonInfo(); + }.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, + ), + ), + ), + ), + ], + ), + ), + ], + ) + ], + ), + ), + ), + ), + ); + } + + _buildHorizontalItem({ + required String title, + required int count, + }) { + return Container( + width: 88.w, + height: 44.w, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8.r), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + count.toString(), + style: TextStyle( + fontSize: 22.sp, + fontWeight: FontWeight.w600, + ), + ), + SizedBox( + height: 2.h, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + title, + style: TextStyle( + fontSize: 12.sp, + color: Colors.black54, + fontWeight: FontWeight.w400, + ), + ), + Icon( + Icons.arrow_forward_ios_rounded, + size: 12.sp, + color: Colors.black54, + ) + ], + ), + ], + ), + ); + } +} diff --git a/lib/views/team/editPerson/personInfo/person_info_binding.dart b/lib/views/team/editPerson/personInfo/person_info_binding.dart new file mode 100644 index 0000000..afed945 --- /dev/null +++ b/lib/views/team/editPerson/personInfo/person_info_binding.dart @@ -0,0 +1,10 @@ +import 'package:get/get.dart'; + +import 'person_info_controller.dart'; + +class PersonInfoBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => PersonInfoController()); + } +} \ No newline at end of file diff --git a/lib/views/team/editPerson/personInfo/person_info_controller.dart b/lib/views/team/editPerson/personInfo/person_info_controller.dart new file mode 100644 index 0000000..aefee34 --- /dev/null +++ b/lib/views/team/editPerson/personInfo/person_info_controller.dart @@ -0,0 +1,17 @@ +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/base/base_controller.dart'; +import 'package:starwork_flutter/views/team/editPerson/edit_person_controller.dart'; + +class PersonInfoController extends BaseController { + final editPersonController = Get.find(); + + @override + void onReady() { + super.onReady(); + var isNotEmpty = editPersonController.phoneInputController.text.isNotEmpty; + AppLogger.highlight('message:${isNotEmpty}'); + } +} diff --git a/lib/views/team/editPerson/personInfo/person_info_view.dart b/lib/views/team/editPerson/personInfo/person_info_view.dart new file mode 100644 index 0000000..663453f --- /dev/null +++ b/lib/views/team/editPerson/personInfo/person_info_view.dart @@ -0,0 +1,193 @@ +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/constant/app_view_parameter_keys.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/routes/app_routes.dart'; +import 'person_info_controller.dart'; + +class PersonInfoView extends GetView { + @override + Widget build(BuildContext context) { + // 即使不使用,只是引用一下 controller 就能触发初始化 + final _ = controller; // 添加这一行 + return Scaffold( + appBar: CustomAppBarWidget( + title: '用户信息'.tr, + backgroundColor: AppColors.scaffoldBackgroundColor, + ), + backgroundColor: AppColors.scaffoldBackgroundColor, + body: Container( + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), + child: Column( + children: [ + CustomCellListWidget( + children: [ + CustomCellWidget( + onTap: () {}, + leftText: '手机号'.tr, + rightWidget: controller.editPersonController.phoneInputController.text.isNotEmpty + ? Row( + children: [ + Text( + controller.editPersonController.getDisplayPhone(), + style: TextStyle( + fontSize: 14.sp, + color: Colors.black54, + fontWeight: FontWeight.w400, + ), + ), + SizedBox( + width: 10.w, + ), + GestureDetector( + onTap: () { + controller.editPersonController.isPhoneVisible.value = + !controller.editPersonController.isPhoneVisible.value; + }, + child: Icon( + controller.editPersonController.isPhoneVisible.value + ? Icons.visibility + : Icons.visibility_off, + size: 16.sp, + color: Colors.grey, + ), + ), + ], + ) + : Expanded( + flex: 3, + child: TextField( + controller: controller.editPersonController.phoneInputController, + keyboardType: TextInputType.number, + 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: Row( + children: [ + Text( + controller.editPersonController.nameInputController.text.trim(), + style: TextStyle( + fontSize: 14.sp, + color: Colors.black54, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + CustomCellWidget( + onTap: () {}, + leftText: '账号状态'.tr, + rightWidget: Row( + children: [ + Obx( + () => Text( + controller.editPersonController.userState.value == 1 + ? '已开通' + : controller.editPersonController.userState.value == 2 + ? '未开通' + : '已停用', + style: TextStyle( + fontSize: 14.sp, + color: Colors.black54, + fontWeight: FontWeight.w400, + ), + ), + ), + ], + ), + ), + CustomCellWidget( + onTap: () async { + var result = await Get.toNamed( + AppRoutes.teamSelectRole, + arguments: { + AppViewParameterKeys.viewType: AppViewParameterKeys.edit, + AppViewParameterKeys.roleList: controller.editPersonController.selectedRoles, + AppViewParameterKeys.departNo: + controller.editPersonController.selectedDepartItem.value.departNo, + AppViewParameterKeys.personItem: controller.editPersonController.selectedPersonItem.value, + }, + ); + if (result != null) { + // 处理返回的角色数据 + if (result is List) { + controller.editPersonController.selectedRoles.value = result; + controller.editPersonController.selectedRoles.refresh(); + // 收起键盘 + FocusScope.of(Get.context!).unfocus(); + } + } + }, + leftText: '分配权限'.tr, + rightWidget: Container( + alignment: Alignment.centerRight, + constraints: BoxConstraints( + maxWidth: 200.w, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Obx( + () => Expanded( + child: Text( + textAlign: TextAlign.end, + controller.editPersonController.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], + ) + ], + ), + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/views/team/enterFace/enter_face_binding.dart b/lib/views/team/enterFace/enter_face_binding.dart new file mode 100644 index 0000000..3d8f169 --- /dev/null +++ b/lib/views/team/enterFace/enter_face_binding.dart @@ -0,0 +1,10 @@ +import 'package:get/get.dart'; + +import 'enter_face_controller.dart'; + +class EnterFaceBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => EnterFaceController()); + } +} \ No newline at end of file diff --git a/lib/views/team/enterFace/enter_face_controller.dart b/lib/views/team/enterFace/enter_face_controller.dart new file mode 100644 index 0000000..9435a0e --- /dev/null +++ b/lib/views/team/enterFace/enter_face_controller.dart @@ -0,0 +1,16 @@ +import 'package:get/get.dart'; +import 'package:starwork_flutter/api/model/team/response/person_list_response.dart'; +import 'package:starwork_flutter/base/base_controller.dart'; + +class EnterFaceController extends BaseController { + final selectedPersonItem = Rx(null); + + @override + void onInit() { + super.onInit(); + final args = Get.arguments; + if (args != null && args is PersonItem) { + selectedPersonItem.value = args; + } + } +} diff --git a/lib/views/team/enterFace/enter_face_view.dart b/lib/views/team/enterFace/enter_face_view.dart new file mode 100644 index 0000000..7213863 --- /dev/null +++ b/lib/views/team/enterFace/enter_face_view.dart @@ -0,0 +1,375 @@ +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/constant/app_images.dart'; +import 'package:starwork_flutter/common/widgets/custome_app_bar_wdiget.dart'; +import 'package:starwork_flutter/extension/function_extension.dart'; +import 'enter_face_controller.dart'; + +class EnterFaceView extends GetView { + @override + Widget build(BuildContext context) { + // 即使不使用,只是引用一下 controller 就能触发初始化 + final _ = controller; // 添加这一行 + + return Scaffold( + appBar: CustomAppBarWidget( + title: '人脸信息'.tr, + backgroundColor: AppColors.scaffoldBackgroundColor, + ), + backgroundColor: AppColors.scaffoldBackgroundColor, + body: Container( + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), + child: SingleChildScrollView( + child: Column( + children: [ + Container( + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8.r), + ), + child: Row( + children: [ + Container( + width: 34.w, + height: 34.w, + margin: EdgeInsets.only(right: 10.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], + ); + }, + ), + ), + ), + Text(controller.selectedPersonItem.value?.personName ?? ''), + const Spacer(), + Text( + '未录入'.tr, + style: TextStyle( + color: Colors.orange, + fontSize: 16.sp, + ), + ) + ], + ), + ), + SizedBox( + height: 10.h, + ), + Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8.r), + ), + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 20.h), + child: Column( + children: [ + Container( + width: 118.w, + height: 118.w, + margin: EdgeInsets.only(right: 10.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.iconShotFace), + width: 118.w, + height: 118.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( + height: 10.h, + ), + Text( + '拍摄须知'.tr, + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w500, + ), + ), + SizedBox( + height: 10.h, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '1、露出额头、耳朵,人脸正面、不戴帽子'.tr, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w500, + ), + ), + Text( + '2、面部光线均匀、无逆光无美颜处理'.tr, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w500, + ), + ), + Text( + '3、背部尽量简洁单一,建议均为白色'.tr, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w500, + ), + ), + Text( + '4、确保开启APP的相机与存储权限'.tr, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + SizedBox( + height: 10.h, + ), + Divider( + height: 0.5.h, + ), + SizedBox( + height: 10.h, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + '错误示例', + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w500, + color: Colors.grey, + ), + ), + SizedBox( + height: 10.h, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + children: [ + Container( + width: 88.w, + height: 88.w, + margin: EdgeInsets.only(right: 10.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.iconShotFaceError1), + width: 88.w, + height: 88.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( + height: 5.h, + ), + Text( + '人脸过小'.tr, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w500, + color: Colors.grey, + ), + ) + ], + ), + Column( + children: [ + Container( + width: 88.w, + height: 88.w, + margin: EdgeInsets.only(right: 10.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.iconShotFaceError2), + width: 88.w, + height: 88.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( + height: 5.h, + ), + Text( + '背景复杂'.tr, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w500, + color: Colors.grey, + ), + ) + ], + ), + Column( + children: [ + Container( + width: 88.w, + height: 88.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.iconShotFaceError3), + width: 88.w, + height: 88.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( + height: 5.h, + ), + Text( + '光线太暗'.tr, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w500, + color: Colors.grey, + ), + ) + ], + ), + ], + ) + ], + ) + ], + ), + ), + SizedBox( + height: 10.h, + ), + Container( + margin: EdgeInsets.symmetric( + horizontal: 10.w, + ), + child: Row( + children: [ + 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(width: 10.w), + Expanded( + child: ElevatedButton( + onPressed: () { + controller.showFunctionNotOpen(); + }.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, + ), + ), + ), + ) + ], + ), + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/views/team/faceAudit/face_audit_binding.dart b/lib/views/team/faceAudit/face_audit_binding.dart new file mode 100644 index 0000000..f4e5d7a --- /dev/null +++ b/lib/views/team/faceAudit/face_audit_binding.dart @@ -0,0 +1,10 @@ +import 'package:get/get.dart'; + +import 'face_audit_controller.dart'; + +class FaceAuditBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => FaceAuditController()); + } +} \ No newline at end of file diff --git a/lib/views/team/faceAudit/face_audit_controller.dart b/lib/views/team/faceAudit/face_audit_controller.dart new file mode 100644 index 0000000..fce20c2 --- /dev/null +++ b/lib/views/team/faceAudit/face_audit_controller.dart @@ -0,0 +1,6 @@ +import 'package:get/get.dart'; +import 'package:starwork_flutter/base/base_controller.dart'; + +class FaceAuditController extends BaseController { + +} \ No newline at end of file diff --git a/lib/views/team/faceAudit/face_audit_view.dart b/lib/views/team/faceAudit/face_audit_view.dart new file mode 100644 index 0000000..779b814 --- /dev/null +++ b/lib/views/team/faceAudit/face_audit_view.dart @@ -0,0 +1,83 @@ +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/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 'face_audit_controller.dart'; + +class FaceAuditView extends GetView { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBarWidget( + title: '人脸审核'.tr, + backgroundColor: AppColors.scaffoldBackgroundColor, + ), + backgroundColor: AppColors.scaffoldBackgroundColor, + body: Container( + padding: EdgeInsets.symmetric( + horizontal: 10.w, + vertical: 10.h, + ), + child: Column( + children: [ + CustomCellListWidget( + children: [ + CustomCellWidget( + leftText: '启用审核'.tr, + leftSubText: '若关闭审核,自助录入的人脸信息将自动生效'.tr, + rightWidget: CupertinoSwitch( + value: false, + onChanged: (value) { + controller.showFunctionNotOpen(); + }, + ), + ) + ], + ), + Expanded( + child: Center( + child: Text( + '暂无待审核人脸'.tr, + style: TextStyle( + fontSize: 16.sp, + color: Colors.grey, + ), + ), + ), + ), + SizedBox( + height: 10.h, + ), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: () { + controller.showFunctionNotOpen(); + }.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, + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/views/team/faceInfo/face_info_binding.dart b/lib/views/team/faceInfo/face_info_binding.dart new file mode 100644 index 0000000..0c0e02e --- /dev/null +++ b/lib/views/team/faceInfo/face_info_binding.dart @@ -0,0 +1,10 @@ +import 'package:get/get.dart'; + +import 'face_info_controller.dart'; + +class FaceInfoBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => FaceInfoController()); + } +} \ No newline at end of file diff --git a/lib/views/team/faceInfo/face_info_controller.dart b/lib/views/team/faceInfo/face_info_controller.dart new file mode 100644 index 0000000..292c417 --- /dev/null +++ b/lib/views/team/faceInfo/face_info_controller.dart @@ -0,0 +1,31 @@ +import 'package:flutter/widgets.dart'; +import 'package:get/get.dart'; +import 'package:starwork_flutter/api/model/team/request/pserson_list_request.dart'; +import 'package:starwork_flutter/api/model/team/response/person_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 FaceInfoController extends BaseController { + final teamApi = Get.find(); + + // 搜索输入框 + TextEditingController searchInputController = TextEditingController(); + + // 人员集合 + Rx personList = PersonListResponse().obs; + + @override + void onInit() { + requestPersonList(); + super.onInit(); + } + + void requestPersonList() async { + var response = await teamApi.requestPersonList(request: PersonListRequest()); + if (response.isSuccess) { + AppLogger.debug("response.data:${response.data}"); + personList.value = response.data!; + } + } +} diff --git a/lib/views/team/faceInfo/face_info_view.dart b/lib/views/team/faceInfo/face_info_view.dart new file mode 100644 index 0000000..ca6ce37 --- /dev/null +++ b/lib/views/team/faceInfo/face_info_view.dart @@ -0,0 +1,193 @@ +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/person_list_response.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/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 'face_info_controller.dart'; + +class FaceInfoView extends GetView { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBarWidget( + title: '人脸信息'.tr, + backgroundColor: AppColors.scaffoldBackgroundColor, + ), + backgroundColor: AppColors.scaffoldBackgroundColor, + body: Container( + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), + child: Column( + children: [ + _buildSearchBar(), + SizedBox( + height: 10.h, + ), + Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8.0.r), + ), + child: CustomCellListWidget( + children: [ + CustomCellWidget( + onTap: () { + Get.toNamed(AppRoutes.teamFaceAudit); + }, + leftText: '人脸审核'.tr, + rightWidget: Icon( + Icons.arrow_forward_ios_rounded, + size: 16.sp, + color: Colors.grey, + ), + ) + ], + ), + ), + SizedBox( + height: 10.h, + ), + Obx( + () => Expanded( + child: SingleChildScrollView( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8.0.r), + ), + child: CustomCellListWidget( + children: (controller.personList.value.list ?? []) + .map((e) => _buildPersonItem(e)) + .toList(), + ), + ), + ), + ), + ), + SizedBox( + height: 10.h, + ), + Obx( + () => Visibility( + visible: controller.personList.value.list?.isNotEmpty ?? false, + child: SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: () {}.debounce(), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + padding: EdgeInsets.symmetric(vertical: 10.h), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.r)), + ), + child: Text( + '一键提醒录入人脸(${controller.personList.value.list?.length}人)'.tr, + style: TextStyle( + fontSize: 16.sp, + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + ), + ), + ], + ), + ), + ); + } + + _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: Colors.white, + // 灰色背景(可调整色值) + 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), + ), + ), + ); + } + + _buildPersonItem(PersonItem personItem) { + return CustomCellWidget( + onTap: () { + Get.toNamed(AppRoutes.teamEnterFace, arguments: personItem); + }, + leftText: personItem.personName ?? '', + leftWidget: Container( + width: 34.w, + height: 34.w, + margin: EdgeInsets.only(right: 10.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], + ); + }, + ), + ), + ), + rightWidget: Row( + children: [ + Text( + personItem.faceCount == 0 ? '未录入'.tr : '已录入'.tr, + style: TextStyle( + fontSize: 16.sp, + color: personItem.faceCount == 0 ? Colors.orange : Colors.grey, + fontWeight: FontWeight.w500, + ), + ), + Icon( + Icons.arrow_forward_ios_rounded, + size: 16.sp, + color: Colors.grey, + ), + ], + ), + ); + } +} diff --git a/lib/views/team/newPersonAuditing/new_person_auditing_binding.dart b/lib/views/team/newPersonAuditing/new_person_auditing_binding.dart new file mode 100644 index 0000000..200747e --- /dev/null +++ b/lib/views/team/newPersonAuditing/new_person_auditing_binding.dart @@ -0,0 +1,10 @@ +import 'package:get/get.dart'; + +import 'new_person_auditing_controller.dart'; + +class NewPersonAuditingBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => NewPersonAuditingController()); + } +} \ No newline at end of file diff --git a/lib/views/team/newPersonAuditing/new_person_auditing_controller.dart b/lib/views/team/newPersonAuditing/new_person_auditing_controller.dart new file mode 100644 index 0000000..95d629b --- /dev/null +++ b/lib/views/team/newPersonAuditing/new_person_auditing_controller.dart @@ -0,0 +1,6 @@ +import 'package:get/get.dart'; +import 'package:starwork_flutter/base/base_controller.dart'; + +class NewPersonAuditingController extends BaseController { + // TODO: 在这里添加业务逻辑 +} \ No newline at end of file diff --git a/lib/views/team/newPersonAuditing/new_person_auditing_view.dart b/lib/views/team/newPersonAuditing/new_person_auditing_view.dart new file mode 100644 index 0000000..49eaf73 --- /dev/null +++ b/lib/views/team/newPersonAuditing/new_person_auditing_view.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:starwork_flutter/common/constant/app_colors.dart'; +import 'package:starwork_flutter/common/widgets/custome_app_bar_wdiget.dart'; +import 'new_person_auditing_controller.dart'; + +class NewPersonAuditingView extends GetView { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBarWidget( + title: '新用户审核'.tr, + backgroundColor: AppColors.scaffoldBackgroundColor, + ), + backgroundColor: AppColors.scaffoldBackgroundColor, + body: Container( + child: Center( + child: Text('暂无新的审核信息'), // 页面内容待实现 + ), + ), + ); + } +} diff --git a/lib/views/team/personnelManage/personnel_manage_view.dart b/lib/views/team/personnelManage/personnel_manage_view.dart index 6bc6167..cdce860 100644 --- a/lib/views/team/personnelManage/personnel_manage_view.dart +++ b/lib/views/team/personnelManage/personnel_manage_view.dart @@ -48,24 +48,29 @@ class PersonnelManageView extends GetView { child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - Column( - children: [ - Image.asset( - AppImages.iconFace, - width: 24.w, - fit: BoxFit.cover, - ), - SizedBox( - height: 4.h, - ), - Text( - '人脸信息'.tr, - style: TextStyle( - fontSize: 12.sp, - fontWeight: FontWeight.w500, + GestureDetector( + onTap: () { + Get.toNamed(AppRoutes.teamFaceInfo); + }, + child: Column( + children: [ + Image.asset( + AppImages.iconFace, + width: 24.w, + fit: BoxFit.cover, ), - ), - ], + SizedBox( + height: 4.h, + ), + Text( + '人脸信息'.tr, + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w500, + ), + ), + ], + ), ), GestureDetector( onTap: () { @@ -91,24 +96,29 @@ class PersonnelManageView extends GetView { ], ), ), - Column( - children: [ - Image.asset( - AppImages.iconNewPerson, - width: 24.w, - fit: BoxFit.cover, - ), - SizedBox( - height: 4.h, - ), - Text( - '新用户审核'.tr, - style: TextStyle( - fontSize: 10.sp, - fontWeight: FontWeight.w500, + GestureDetector( + onTap: () { + Get.toNamed(AppRoutes.teamNewPersonAuditing); + }, + child: Column( + children: [ + Image.asset( + AppImages.iconNewPerson, + width: 24.w, + fit: BoxFit.cover, ), - ), - ], + SizedBox( + height: 4.h, + ), + Text( + '新用户审核'.tr, + style: TextStyle( + fontSize: 10.sp, + fontWeight: FontWeight.w500, + ), + ), + ], + ), ) ], ), @@ -159,7 +169,11 @@ class PersonnelManageView extends GetView { showRootNode: false, expansionIndicatorBuilder: noExpansionIndicatorBuilder, indentation: const Indentation(style: IndentStyle.roundJoint), - onItemTap: (item) {}, + onItemTap: (item) { + if (item.data is PersonItem) { + Get.toNamed(AppRoutes.teamEditPerson, arguments: item.data); + } + }, onTreeReady: (c) { controller.treeViewController = c; },