feat: 增加(添加人员功能页面完善与接口对接、添加子组织页面完善与接口对接、选择用户页面开发)
This commit is contained in:
parent
9b4d112825
commit
9e10952bef
@ -16,4 +16,5 @@ class ApiPath {
|
||||
static const String departCreate = "/v1/team/departCreate";
|
||||
static const String roleList = "/v1/team/roleList";
|
||||
static const String roleCreate = "/v1/team/roleCreate";
|
||||
static const String createPerson = "/v1/team/personCreate";
|
||||
}
|
||||
|
||||
@ -56,9 +56,9 @@ class BaseApiService {
|
||||
if (kDebugMode) {
|
||||
final uri = Uri.parse('${dio.options.baseUrl}$path');
|
||||
final queryString = queryParameters != null ? '?${uri.queryParameters.toString()}' : '';
|
||||
debugPrint('🟦 API Request: $method ${uri.toString()}$queryString');
|
||||
AppLogger.debug('🟦 API Request: $method ${uri.toString()}$queryString');
|
||||
if (data != null) {
|
||||
debugPrint('🟦 Request Body: $data');
|
||||
AppLogger.debug('🟦 Request Body: $data');
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,8 +86,8 @@ class BaseApiService {
|
||||
|
||||
// ✅ 打印响应
|
||||
if (kDebugMode) {
|
||||
debugPrint('🟩 API Response [${response.statusCode}] ${response.requestOptions.path}');
|
||||
debugPrint('🟩 Response Data: ${response.data}');
|
||||
AppLogger.debug('🟩 API Response [${response.statusCode}] ${response.requestOptions.path}');
|
||||
AppLogger.debug('🟩 Response Data: ${response.data}');
|
||||
}
|
||||
|
||||
if (response.statusCode! >= 200 && response.statusCode! < 300) {
|
||||
@ -116,10 +116,10 @@ class BaseApiService {
|
||||
} on dioAlias.DioException catch (e) {
|
||||
// ❌ 打印错误
|
||||
if (kDebugMode) {
|
||||
debugPrint('🟥 API Error: ${e.type} - ${e.message}');
|
||||
AppLogger.error('🟥 API Error: ${e.type} - ${e.message}', error: e);
|
||||
if (e.response != null) {
|
||||
debugPrint('🟥 Error Response: ${e.response?.data}');
|
||||
debugPrint('🟥 Status Code: ${e.response?.statusCode}');
|
||||
AppLogger.error('🟥 Error Response: ${e.response?.data}');
|
||||
AppLogger.error('🟥 Status Code: ${e.response?.statusCode}');
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,7 +141,7 @@ class BaseApiService {
|
||||
return ApiResponse.error(message, errorCode: statusCode);
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
debugPrint('🟥 Unexpected Error: $e');
|
||||
AppLogger.error('🟥 Unexpected Error: $e', error: e);
|
||||
}
|
||||
return ApiResponse.error('Unexpected error: $e');
|
||||
} finally {
|
||||
|
||||
81
lib/api/model/team/request/create_new_person.dart
Normal file
81
lib/api/model/team/request/create_new_person.dart
Normal file
@ -0,0 +1,81 @@
|
||||
class CreateNewPerson {
|
||||
String? departNo;
|
||||
String? personName;
|
||||
bool? createUser;
|
||||
String? phone;
|
||||
int? sex;
|
||||
String? position;
|
||||
String? remark;
|
||||
List<String>? associateUsers;
|
||||
int? limitType;
|
||||
String? limitStartTime;
|
||||
String? limitEndTime;
|
||||
String? idCard;
|
||||
bool? isConfirm;
|
||||
List<int>? roleIds;
|
||||
String? jobNumber;
|
||||
|
||||
CreateNewPerson({
|
||||
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.isConfirm,
|
||||
this.roleIds,
|
||||
this.jobNumber,
|
||||
});
|
||||
|
||||
factory CreateNewPerson.fromJson(Map<String, dynamic> json) {
|
||||
return CreateNewPerson(
|
||||
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'] != null
|
||||
? List<String>.from(json['associateUsers'])
|
||||
: null,
|
||||
limitType: json['limitType'] as int?,
|
||||
limitStartTime: json['limitStartTime'] as String?,
|
||||
limitEndTime: json['limitEndTime'] as String?,
|
||||
idCard: json['idCard'] as String?,
|
||||
isConfirm: json['isConfirm'] as bool?,
|
||||
roleIds: json['roleIds'] != null
|
||||
? List<int>.from(json['roleIds'])
|
||||
: null,
|
||||
jobNumber: json['jobNumber'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
|
||||
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 (isConfirm != null) data['isConfirm'] = isConfirm;
|
||||
if (roleIds != null) data['roleIds'] = roleIds;
|
||||
if (jobNumber != null) data['jobNumber'] = jobNumber;
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ 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_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/get_depart_list_request.dart';
|
||||
@ -139,7 +140,7 @@ class TeamApiService {
|
||||
);
|
||||
}
|
||||
|
||||
// 获取组织列表
|
||||
// 创建新的组织
|
||||
Future<ApiResponse<void>> requestCreateDepart({
|
||||
required CreateNewDepartRequest request,
|
||||
}) {
|
||||
@ -171,4 +172,16 @@ class TeamApiService {
|
||||
fromJson: (data) {},
|
||||
);
|
||||
}
|
||||
|
||||
// 添加新的人员
|
||||
Future<ApiResponse<void>> requestAddPerson({
|
||||
required CreateNewPerson request,
|
||||
}) {
|
||||
return _api.makeRequest(
|
||||
path: ApiPath.createPerson,
|
||||
method: HttpConstant.post,
|
||||
data: request.toJson(),
|
||||
fromJson: (data) {},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,4 +68,5 @@ class BaseController extends GetxController {
|
||||
}
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -4,5 +4,8 @@ class AppViewParameterKeys {
|
||||
static const String networkInfo = "networkInfo";
|
||||
static const String teamInfo = "teamInfo";
|
||||
static const String departItem = "departItem";
|
||||
static const String isLongTerm = "isLongTerm";
|
||||
static const String startDate = "startDate";
|
||||
static const String endDate = "endDate";
|
||||
|
||||
}
|
||||
@ -21,7 +21,7 @@ class F {
|
||||
// Release环境的API地址
|
||||
switch (appFlavor) {
|
||||
case Flavor.sky:
|
||||
return 'https://192.168.1.137:8112/api'; // 生产环境API
|
||||
return 'https://192.168.1.136: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.137:8112/api';
|
||||
return 'http://192.168.1.136: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.137:8111/sdk';
|
||||
return 'http://192.168.1.136:8111/sdk';
|
||||
case Flavor.xhj:
|
||||
return 'http://local.cloud.star-lock.cn';
|
||||
}
|
||||
|
||||
@ -34,8 +34,12 @@ import 'package:starwork_flutter/views/login/login_binding.dart';
|
||||
import 'package:starwork_flutter/views/login/login_view.dart';
|
||||
import 'package:starwork_flutter/views/main/main_binding.dart';
|
||||
import 'package:starwork_flutter/views/main/main_view.dart';
|
||||
import 'package:starwork_flutter/views/team/addOrganization/add_organization_binding.dart';
|
||||
import 'package:starwork_flutter/views/team/addOrganization/add_organization_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/editValidity/edit_validity_binding.dart';
|
||||
import 'package:starwork_flutter/views/team/addPerson/editValidity/edit_validity_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';
|
||||
@ -52,6 +56,8 @@ 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/selectPerson/select_person_binding.dart';
|
||||
import 'package:starwork_flutter/views/team/selectPerson/select_person_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';
|
||||
@ -242,5 +248,20 @@ class AppPages {
|
||||
page: () => SelectRoleView(),
|
||||
binding: SelectRoleBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: AppRoutes.teamAddPersonEditValidity,
|
||||
page: () => EditValidityView(),
|
||||
binding: EditValidityBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: AppRoutes.teamAddOrganization,
|
||||
page: () => AddOrganizationView(),
|
||||
binding: AddOrganizationBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: AppRoutes.teamSelectPerson,
|
||||
page: () => SelectPersonView(),
|
||||
binding: SelectPersonBinding(),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@ -20,9 +20,12 @@ class AppRoutes{
|
||||
static const String teamAddPerson = '/team/addPerson';
|
||||
static const String teamRoleManage = '/team/roleManage';
|
||||
static const String teamSelectOrganization = '/team/selectOrganization';
|
||||
static const String teamSelectPerson = '/team/selectPerson';
|
||||
static const String teamPersonnelManage = '/team/personnelManage';
|
||||
static const String teamAddRole = '/team/addRole';
|
||||
static const String teamSelectRole = '/team/selectRole';
|
||||
static const String teamAddPersonEditValidity = '/team/addPerson/editValidity';
|
||||
static const String teamAddOrganization = '/team/addOrganization';
|
||||
static const String deviceManage = '/device/deviceManage';
|
||||
static const String searchDevice = '/device/searchDevice';
|
||||
static const String confirmPairDevice = '/device/confirmPairDevice';
|
||||
|
||||
10
lib/views/team/addOrganization/add_organization_binding.dart
Normal file
10
lib/views/team/addOrganization/add_organization_binding.dart
Normal file
@ -0,0 +1,10 @@
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import 'add_organization_controller.dart';
|
||||
|
||||
class AddOrganizationBinding extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut(() => AddOrganizationController());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
import 'package:flutter/material.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/response/depart_list_reponse.dart';
|
||||
import 'package:starwork_flutter/api/service/team_api_service.dart';
|
||||
import 'package:starwork_flutter/base/base_controller.dart';
|
||||
import 'package:starwork_flutter/common/constant/app_view_parameter_keys.dart';
|
||||
|
||||
class AddOrganizationController extends BaseController {
|
||||
TextEditingController organizationNameInputController = TextEditingController();
|
||||
var selectedDepartItem = DepartItem().obs; // 当前选中的组织
|
||||
var selectedPerson = <PersonItem>[].obs; // 当前选中的用户
|
||||
final teamApi = Get.find<TeamApiService>();
|
||||
|
||||
@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 getSelectedPersonDisplayText() {
|
||||
if (selectedPerson.isEmpty) {
|
||||
return '请选择组织负责人(选填)'; // 如果没有选中角色,显示"请选择"
|
||||
} else {
|
||||
// 将所有选中角色的名称用逗号连接
|
||||
return selectedPerson.map((person) => person.personName ?? '').join('、'); // 使用顿号或逗号分隔
|
||||
}
|
||||
}
|
||||
|
||||
requestCreateDepart() async {
|
||||
var response = await teamApi.requestCreateDepart(
|
||||
request: CreateNewDepartRequest(
|
||||
departName: organizationNameInputController.text.trim(),
|
||||
parentDepartNo: selectedDepartItem.value.departNo!,
|
||||
leader: selectedPerson
|
||||
.map((e) => e.personNo)
|
||||
.whereType<String>() // 只保留非 null 的 String
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
if (response.isSuccess) {
|
||||
showSuccess();
|
||||
} else {
|
||||
showError(message: response.errorMsg!);
|
||||
}
|
||||
}
|
||||
}
|
||||
200
lib/views/team/addOrganization/add_organization_view.dart
Normal file
200
lib/views/team/addOrganization/add_organization_view.dart
Normal file
@ -0,0 +1,200 @@
|
||||
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/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_organization_controller.dart';
|
||||
|
||||
class AddOrganizationView extends GetView<AddOrganizationController> {
|
||||
@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: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
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.organizationNameInputController,
|
||||
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 {
|
||||
final result = await Get.toNamed(AppRoutes.teamSelectOrganization);
|
||||
if (result != null) {
|
||||
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 ?? '请选择上级组织'.tr,
|
||||
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 {
|
||||
final result =
|
||||
await Get.toNamed(AppRoutes.teamSelectPerson, arguments: controller.selectedPerson);
|
||||
if (result != null && result is List<PersonItem>) {
|
||||
controller.selectedPerson.value = result;
|
||||
}
|
||||
},
|
||||
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.getSelectedPersonDisplayText(),
|
||||
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],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: () async {
|
||||
if (controller.organizationNameInputController.text.isEmpty) {
|
||||
controller.showToast('请先输入组织名称'.tr);
|
||||
return;
|
||||
}
|
||||
if (controller.selectedDepartItem.value.departNo == null) {
|
||||
controller.showToast('请先选择上级组织'.tr);
|
||||
return;
|
||||
}
|
||||
await controller.requestCreateDepart();
|
||||
}.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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 10.h,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -2,24 +2,30 @@ 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/request/create_new_person.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/base_controller.dart';
|
||||
import 'package:starwork_flutter/common/constant/app_view_parameter_keys.dart';
|
||||
|
||||
class AddPersonController extends BaseController {
|
||||
RxString selectedGender = 'male'.obs;
|
||||
|
||||
final teamApi = Get.find<TeamApiService>();
|
||||
var selectedDepartItem = DepartItem().obs; // 当前选中的组织
|
||||
|
||||
TextEditingController nameInputController = TextEditingController();
|
||||
TextEditingController phoneInputController = TextEditingController();
|
||||
TextEditingController jobNoInputController = TextEditingController(); // 工号
|
||||
TextEditingController postInputController = TextEditingController(); // 职务
|
||||
TextEditingController idCardInputController = TextEditingController(); // 身份证号码
|
||||
TextEditingController remarkInputController = TextEditingController(); // 备注
|
||||
var isOpeningAccount = false.obs; // 是否开通账户
|
||||
var isLongTerm = true.obs; // 有效期是否为长期
|
||||
var startDate = 0.obs; // 使用时间戳表示开始时间
|
||||
var endDate = 0.obs; // 使用时间戳表示结束时间
|
||||
var selectedRoles = <RoleListResponse>[].obs;
|
||||
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
@ -30,15 +36,77 @@ class AddPersonController extends BaseController {
|
||||
selectedDepartItem.value = DepartItem.fromJson(json);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取选中角色的显示文本
|
||||
String getSelectedRoleDisplayText() {
|
||||
if (selectedRoles.isEmpty) {
|
||||
return '请选择'; // 如果没有选中角色,显示"请选择"
|
||||
} else {
|
||||
// 将所有选中角色的名称用逗号连接
|
||||
return selectedRoles
|
||||
.map((role) => role.roleName ?? '')
|
||||
.join('、'); // 使用顿号或逗号分隔
|
||||
return selectedRoles.map((role) => role.roleName ?? '').join('、'); // 使用顿号或逗号分隔
|
||||
}
|
||||
}
|
||||
|
||||
/// 保存员工信息
|
||||
/// [isSustain] 是否为持续保存
|
||||
void savePerson({
|
||||
bool isSustain = false,
|
||||
}) async {
|
||||
List<int?> list = selectedRoles.map((role) => role.id).toList();
|
||||
var response = await teamApi.requestAddPerson(
|
||||
request: CreateNewPerson(
|
||||
departNo: selectedDepartItem.value.departNo,
|
||||
personName: nameInputController.text.trim(),
|
||||
phone: phoneInputController.text.trim(),
|
||||
sex: selectedGender.value == 'male' ? 1 : 2,
|
||||
position: postInputController.text.trim(),
|
||||
idCard: idCardInputController.text.trim(),
|
||||
remark: remarkInputController.text.trim(),
|
||||
jobNumber: jobNoInputController.text.trim(),
|
||||
limitType: isLongTerm.value ? 1 : 2,
|
||||
limitStartTime: isLongTerm.value ? null : formatTimestamp(startDate.value),
|
||||
limitEndTime: isLongTerm.value ? null : formatTimestamp(endDate.value),
|
||||
isConfirm: true,
|
||||
roleIds: list.whereType<int>().toList(),
|
||||
),
|
||||
);
|
||||
if (response.isSuccess) {
|
||||
showSuccess();
|
||||
if (!isSustain) {
|
||||
Get.back(result: true);
|
||||
} else {
|
||||
resetForm();
|
||||
}
|
||||
} else {
|
||||
showError(message: response.errorMsg!);
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助方法:将时间戳转换为日期字符串
|
||||
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 '请选择';
|
||||
}
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
void resetForm() {
|
||||
nameInputController.text = '';
|
||||
phoneInputController.text = '';
|
||||
jobNoInputController.text = '';
|
||||
postInputController.text = '';
|
||||
idCardInputController.text = '';
|
||||
remarkInputController.text = '';
|
||||
selectedGender.value = 'male';
|
||||
isOpeningAccount.value = false;
|
||||
isLongTerm.value = true;
|
||||
startDate.value = 0;
|
||||
endDate.value = 0;
|
||||
selectedRoles.clear();
|
||||
selectedDepartItem.value = DepartItem();
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import 'package:starwork_flutter/api/model/team/response/depart_list_reponse.dar
|
||||
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/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';
|
||||
@ -16,6 +17,8 @@ import 'add_person_controller.dart';
|
||||
class AddPersonView extends GetView<AddPersonController> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// 即使不使用,只是引用一下 controller 就能触发初始化
|
||||
final _ = controller; // 添加这一行
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
FocusScope.of(context).requestFocus(FocusNode());
|
||||
@ -51,136 +54,33 @@ class AddPersonView extends GetView<AddPersonController> {
|
||||
SizedBox(
|
||||
height: 6.h,
|
||||
),
|
||||
CustomCellListWidget(
|
||||
children: [
|
||||
CustomCellWidget(
|
||||
onTap: () async {
|
||||
final result = await Get.toNamed(AppRoutes.teamSelectOrganization);
|
||||
if (result != null) {
|
||||
AppLogger.highlight('result:${result}');
|
||||
if (result is DepartItem) {
|
||||
Obx(
|
||||
() => CustomCellListWidget(
|
||||
children: [
|
||||
CustomCellWidget(
|
||||
onTap: () async {
|
||||
final result = await Get.toNamed(AppRoutes.teamSelectOrganization);
|
||||
|
||||
if (result != null && 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,
|
||||
),
|
||||
),
|
||||
),
|
||||
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,
|
||||
),
|
||||
},
|
||||
leftText: '组织'.tr,
|
||||
leftIcon: Icon(
|
||||
Icons.circle,
|
||||
size: 4.w,
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
),
|
||||
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, // 可选:关闭时的背景轨道颜色
|
||||
),
|
||||
),
|
||||
CustomCellWidget(
|
||||
onTap: () async {
|
||||
var result =
|
||||
await Get.toNamed(AppRoutes.teamSelectRole, arguments: controller.selectedRoles);
|
||||
if (result != null) {
|
||||
// 处理返回的角色数据
|
||||
if (result is List<RoleListResponse>) {
|
||||
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: Row(
|
||||
rightWidget: 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,
|
||||
() => Text(
|
||||
controller.selectedDepartItem.value.departName ?? '请选择',
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
color: Colors.black54,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -195,8 +95,157 @@ class AddPersonView extends GetView<AddPersonController> {
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
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,
|
||||
leftIcon: controller.isOpeningAccount.value
|
||||
? Icon(
|
||||
Icons.circle,
|
||||
size: 4.w,
|
||||
color: Colors.red,
|
||||
)
|
||||
: null,
|
||||
rightWidget: 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: controller.isOpeningAccount.value ? '若开通账号则必填'.tr : '请输入手机号'.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: Obx(
|
||||
() => CupertinoSwitch(
|
||||
value: controller.isOpeningAccount.value,
|
||||
onChanged: (bool value) {
|
||||
controller.isOpeningAccount.value = value;
|
||||
},
|
||||
activeColor: Colors.blue, // 可选:打开时的颜色(iOS 默认为系统蓝色,可自定义)
|
||||
trackColor: Colors.grey, // 可选:关闭时的背景轨道颜色
|
||||
),
|
||||
),
|
||||
),
|
||||
CustomCellWidget(
|
||||
onTap: () async {
|
||||
var result =
|
||||
await Get.toNamed(AppRoutes.teamSelectRole, arguments: controller.selectedRoles);
|
||||
if (result != null) {
|
||||
// 处理返回的角色数据
|
||||
if (result is List<RoleListResponse>) {
|
||||
controller.selectedRoles.value = result;
|
||||
controller.selectedRoles.refresh();
|
||||
// 收起键盘
|
||||
FocusScope.of(Get.context!).unfocus();
|
||||
}
|
||||
}
|
||||
},
|
||||
leftText: '分配权限'.tr,
|
||||
leftIcon: Icon(
|
||||
Icons.circle,
|
||||
size: 4.w,
|
||||
color: Colors.red,
|
||||
),
|
||||
rightWidget: Container(
|
||||
alignment: Alignment.centerRight,
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: 200.w,
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Obx(
|
||||
() => Expanded(
|
||||
child: Text(
|
||||
textAlign: TextAlign.end,
|
||||
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,
|
||||
@ -275,17 +324,35 @@ class AddPersonView extends GetView<AddPersonController> {
|
||||
),
|
||||
),
|
||||
CustomCellWidget(
|
||||
onTap: () {},
|
||||
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: [
|
||||
Text(
|
||||
'请选择',
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
color: Colors.black54,
|
||||
fontWeight: FontWeight.w400,
|
||||
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(
|
||||
@ -335,6 +402,7 @@ class AddPersonView extends GetView<AddPersonController> {
|
||||
rightWidget: Expanded(
|
||||
flex: 3,
|
||||
child: TextField(
|
||||
controller: controller.remarkInputController,
|
||||
keyboardType: TextInputType.text,
|
||||
textInputAction: TextInputAction.next,
|
||||
textAlign: TextAlign.end,
|
||||
@ -365,6 +433,7 @@ class AddPersonView extends GetView<AddPersonController> {
|
||||
rightWidget: Expanded(
|
||||
flex: 3,
|
||||
child: TextField(
|
||||
controller: controller.idCardInputController,
|
||||
keyboardType: TextInputType.text,
|
||||
textInputAction: TextInputAction.next,
|
||||
textAlign: TextAlign.end,
|
||||
@ -451,7 +520,26 @@ class AddPersonView extends GetView<AddPersonController> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {}.debounce(),
|
||||
onPressed: () {
|
||||
if (controller.selectedDepartItem.value.departNo == null) {
|
||||
controller.showToast('请先选择组织'.tr);
|
||||
return;
|
||||
}
|
||||
if (controller.nameInputController.text.isEmpty) {
|
||||
controller.showToast('请输入姓名'.tr);
|
||||
return;
|
||||
}
|
||||
if (controller.phoneInputController.text.isEmpty &&
|
||||
controller.isOpeningAccount.isTrue) {
|
||||
controller.showToast('请输入手机号'.tr);
|
||||
return;
|
||||
}
|
||||
if (controller.selectedRoles.isEmpty) {
|
||||
controller.showToast('请先选择角色'.tr);
|
||||
return;
|
||||
}
|
||||
controller.savePerson();
|
||||
}.debounce(),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.grey[100],
|
||||
padding: EdgeInsets.symmetric(vertical: 12.h),
|
||||
@ -474,7 +562,26 @@ class AddPersonView extends GetView<AddPersonController> {
|
||||
),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {}.debounce(),
|
||||
onPressed: () {
|
||||
if (controller.selectedDepartItem.value.departNo == null) {
|
||||
controller.showToast('请先选择组织'.tr);
|
||||
return;
|
||||
}
|
||||
if (controller.nameInputController.text.isEmpty) {
|
||||
controller.showToast('请输入姓名'.tr);
|
||||
return;
|
||||
}
|
||||
if (controller.phoneInputController.text.isEmpty &&
|
||||
controller.isOpeningAccount.isTrue) {
|
||||
controller.showToast('请输入手机号'.tr);
|
||||
return;
|
||||
}
|
||||
if (controller.selectedRoles.isEmpty) {
|
||||
controller.showToast('请先选择角色'.tr);
|
||||
return;
|
||||
}
|
||||
controller.savePerson(isSustain: true);
|
||||
}.debounce(),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue,
|
||||
padding: EdgeInsets.symmetric(vertical: 12.h),
|
||||
|
||||
@ -0,0 +1,10 @@
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import 'edit_validity_controller.dart';
|
||||
|
||||
class EditValidityBinding extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut(() => EditValidityController());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:starwork_flutter/base/base_controller.dart';
|
||||
import 'package:starwork_flutter/common/constant/app_view_parameter_keys.dart';
|
||||
|
||||
class EditValidityController extends BaseController {
|
||||
var isLongTerm = true.obs; // 是否开启长期有效
|
||||
|
||||
var startDate = 0.obs; // 使用时间戳表示开始时间
|
||||
var endDate = 0.obs; // 使用时间戳表示结束时间
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
// 读取参数
|
||||
final args = Get.arguments;
|
||||
if (args != null && args.containsKey(AppViewParameterKeys.isLongTerm)) {
|
||||
isLongTerm.value = args[AppViewParameterKeys.isLongTerm];
|
||||
}
|
||||
if (args != null && args.containsKey(AppViewParameterKeys.startDate)) {
|
||||
startDate.value = args[AppViewParameterKeys.startDate];
|
||||
}
|
||||
if (args != null && args.containsKey(AppViewParameterKeys.endDate)) {
|
||||
endDate.value = args[AppViewParameterKeys.endDate];
|
||||
}
|
||||
}
|
||||
}
|
||||
289
lib/views/team/addPerson/editValidity/edit_validity_view.dart
Normal file
289
lib/views/team/addPerson/editValidity/edit_validity_view.dart
Normal file
@ -0,0 +1,289 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_datetime_picker_plus/flutter_datetime_picker_plus.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_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 'edit_validity_controller.dart';
|
||||
|
||||
class EditValidityView extends GetView<EditValidityController> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// 即使不使用,只是引用一下 controller 就能触发初始化
|
||||
final _ = controller; // 添加这一行
|
||||
return Scaffold(
|
||||
appBar: CustomAppBarWidget(
|
||||
title: '编辑有效期'.tr,
|
||||
backgroundColor: AppColors.scaffoldBackgroundColor,
|
||||
),
|
||||
backgroundColor: AppColors.scaffoldBackgroundColor,
|
||||
body: SafeArea(
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.all(Radius.circular(8.r)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'长期有效'.tr,
|
||||
style: TextStyle(
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 10.h,
|
||||
),
|
||||
Text(
|
||||
'关闭后可限制通行门禁的生效-失效日期'.tr,
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
Obx(
|
||||
() => CupertinoSwitch(
|
||||
value: controller.isLongTerm.value,
|
||||
onChanged: (bool value) {
|
||||
controller.isLongTerm.value = value;
|
||||
},
|
||||
activeColor: Colors.blue, // 可选:打开时的颜色(iOS 默认为系统蓝色,可自定义)
|
||||
trackColor: Colors.grey, // 可选:关闭时的背景轨道颜色
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => Visibility(
|
||||
visible: controller.isLongTerm.value == false,
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(top: 10.h),
|
||||
child: CustomCellListWidget(
|
||||
children: [
|
||||
CustomCellWidget(
|
||||
onTap: () async {
|
||||
DatePicker.showDatePicker(
|
||||
context,
|
||||
showTitleActions: true,
|
||||
minTime: DateTime.now(),
|
||||
maxTime: DateTime.now().add(const Duration(days: 365 * 20)),
|
||||
onChanged: (date) {},
|
||||
onConfirm: (date) {
|
||||
controller.startDate.value = date.millisecondsSinceEpoch;
|
||||
},
|
||||
currentTime: DateTime.now(),
|
||||
locale: LocaleType.zh,
|
||||
);
|
||||
},
|
||||
leftText: '生效日期'.tr,
|
||||
rightWidget: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Obx(() {
|
||||
String displayText = '请选择';
|
||||
if (controller.startDate.value > 0) {
|
||||
displayText = DateTime.fromMillisecondsSinceEpoch(controller.startDate.value)
|
||||
.toString()
|
||||
.split(' ')[0]; // 只显示日期部分
|
||||
}
|
||||
return Text(
|
||||
displayText,
|
||||
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 {
|
||||
DatePicker.showDatePicker(
|
||||
context,
|
||||
showTitleActions: true,
|
||||
minTime: DateTime.now(),
|
||||
maxTime: DateTime.now().add(const Duration(days: 365 * 20)),
|
||||
onChanged: (date) {},
|
||||
onConfirm: (date) {
|
||||
controller.endDate.value = date.millisecondsSinceEpoch;
|
||||
},
|
||||
currentTime: DateTime.now(),
|
||||
locale: LocaleType.zh,
|
||||
);
|
||||
},
|
||||
leftText: '失效日期'.tr,
|
||||
rightWidget: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Obx(() {
|
||||
String displayText = '请选择';
|
||||
if (controller.endDate.value > 0) {
|
||||
displayText = DateTime.fromMillisecondsSinceEpoch(controller.endDate.value)
|
||||
.toString()
|
||||
.split(' ')[0]; // 只显示日期部分
|
||||
}
|
||||
return Text(
|
||||
displayText,
|
||||
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],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 10.h),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.all(Radius.circular(8.r)),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.info_outline,
|
||||
size: 16.sp,
|
||||
),
|
||||
SizedBox(
|
||||
width: 4.w,
|
||||
),
|
||||
Text(
|
||||
'有效期为通行门禁的生效~失效日期'.tr,
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 4.h,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'使用场景举例:',
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'1.健身卡到期后自动失效无法进入健身房',
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'2.医院病房,陪护人员到期后自动失效无法通行',
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
if (controller.startDate.value == 0 || controller.endDate.value == 0) {
|
||||
controller.showToast('请先选择有效日期'.tr);
|
||||
return;
|
||||
}
|
||||
if (controller.startDate.value > controller.endDate.value) {
|
||||
controller.showToast('生效日期不能大于失效日期'.tr);
|
||||
return;
|
||||
}
|
||||
if (controller.startDate.value == controller.endDate.value) {
|
||||
controller.showToast('生效日期不能等于失效日期'.tr);
|
||||
return;
|
||||
}
|
||||
Get.back(result: {
|
||||
AppViewParameterKeys.isLongTerm: controller.isLongTerm.value,
|
||||
AppViewParameterKeys.startDate: controller.startDate.value,
|
||||
AppViewParameterKeys.endDate: controller.endDate.value,
|
||||
});
|
||||
}.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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 10.h,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -36,6 +36,8 @@ class PersonnelManageController extends BaseController {
|
||||
// 转换数据为树形结构
|
||||
final tree = _convertToTree(response.data!.departList ?? []);
|
||||
treeData.value = tree;
|
||||
|
||||
treeData.refresh();
|
||||
// 统计总人数
|
||||
totalPersonCount.value = _calculateTotalPersonCount(response.data!.departList ?? []);
|
||||
|
||||
@ -114,12 +116,7 @@ class PersonnelManageController extends BaseController {
|
||||
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);
|
||||
|
||||
@ -127,204 +127,219 @@ class PersonnelManageView extends GetView<PersonnelManageController> {
|
||||
),
|
||||
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;
|
||||
() {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: 10.w,
|
||||
),
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.all(Radius.circular(8.0)),
|
||||
),
|
||||
child: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await controller.requestDepartList();
|
||||
},
|
||||
color: const Color(0xFF4A90E2),
|
||||
backgroundColor: Colors.white,
|
||||
displacement: 60.0,
|
||||
edgeOffset: 0.0,
|
||||
triggerMode: RefreshIndicatorTriggerMode.onEdge,
|
||||
strokeWidth: 2.5,
|
||||
semanticsLabel: '下拉刷新首页内容',
|
||||
semanticsValue: '刷新中...',
|
||||
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 (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;
|
||||
}
|
||||
}
|
||||
});
|
||||
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),
|
||||
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,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
child: Icon(
|
||||
Icons.folder,
|
||||
size: 22.w,
|
||||
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,
|
||||
),
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
width: 34.w,
|
||||
height: 34.w,
|
||||
),
|
||||
),
|
||||
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.grey[200],
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
color: Colors.blue[50],
|
||||
borderRadius: BorderRadius.circular(4.r),
|
||||
border: Border.all(
|
||||
color: Colors.blue,
|
||||
width: 1.w,
|
||||
),
|
||||
),
|
||||
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],
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
'超管',
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Colors.blue,
|
||||
),
|
||||
),
|
||||
),
|
||||
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<DepartItem>(
|
||||
value: departInfo,
|
||||
groupValue: controller.selectedDepartItem.value,
|
||||
activeColor: Colors.blue,
|
||||
visualDensity: VisualDensity.compact,
|
||||
onChanged: (value) {
|
||||
controller.selectedDepartItem.value = value as DepartItem;
|
||||
controller.selectedDepartItem.refresh();
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: !isPersonItem,
|
||||
child: Obx(
|
||||
() => Radio<DepartItem>(
|
||||
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,
|
||||
height: 4.h,
|
||||
),
|
||||
Obx(
|
||||
() => Text(
|
||||
@ -337,7 +352,7 @@ class PersonnelManageView extends GetView<PersonnelManageController> {
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 10.h,
|
||||
height: 4.h,
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(
|
||||
@ -347,14 +362,17 @@ class PersonnelManageView extends GetView<PersonnelManageController> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
onPressed: () async {
|
||||
if (controller.selectedDepartItem.value.departNo == null) {
|
||||
controller.showToast('请先选择一个组织');
|
||||
return;
|
||||
}
|
||||
Get.toNamed(AppRoutes.teamAddPerson, arguments: {
|
||||
var result = await Get.toNamed(AppRoutes.teamAddPerson, arguments: {
|
||||
AppViewParameterKeys.departItem: controller.selectedDepartItem.value.toJson(),
|
||||
});
|
||||
if (result != null && result == true) {
|
||||
await controller.requestDepartList();
|
||||
}
|
||||
}.debounce(),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue,
|
||||
@ -379,6 +397,9 @@ class PersonnelManageView extends GetView<PersonnelManageController> {
|
||||
controller.showToast('请先选择一个组织');
|
||||
return;
|
||||
}
|
||||
Get.toNamed(AppRoutes.teamAddOrganization, arguments: {
|
||||
AppViewParameterKeys.departItem: controller.selectedDepartItem.value.toJson(),
|
||||
});
|
||||
}.debounce(),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.grey[50],
|
||||
@ -398,7 +419,9 @@ class PersonnelManageView extends GetView<PersonnelManageController> {
|
||||
SizedBox(width: 10.w),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {}.debounce(),
|
||||
onPressed: () {
|
||||
controller.showFunctionNotOpen();
|
||||
}.debounce(),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.grey[50],
|
||||
padding: EdgeInsets.symmetric(vertical: 10.h),
|
||||
@ -418,7 +441,7 @@ class PersonnelManageView extends GetView<PersonnelManageController> {
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 10.h,
|
||||
height: 20.h,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -65,7 +65,6 @@ class SelectOrganizationView extends GetView<SelectOrganizationController> {
|
||||
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);
|
||||
}
|
||||
|
||||
10
lib/views/team/selectPerson/select_person_binding.dart
Normal file
10
lib/views/team/selectPerson/select_person_binding.dart
Normal file
@ -0,0 +1,10 @@
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import 'select_person_controller.dart';
|
||||
|
||||
class SelectPersonBinding extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut(() => SelectPersonController());
|
||||
}
|
||||
}
|
||||
149
lib/views/team/selectPerson/select_person_controller.dart
Normal file
149
lib/views/team/selectPerson/select_person_controller.dart
Normal file
@ -0,0 +1,149 @@
|
||||
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 SelectPersonController extends BaseController {
|
||||
final teamApi = Get.find<TeamApiService>();
|
||||
final Rx<TreeNode?> treeData = Rx<TreeNode?>(null);
|
||||
TreeViewController? treeViewController;
|
||||
|
||||
// 搜索输入框
|
||||
TextEditingController searchInputController = TextEditingController();
|
||||
|
||||
var allPersonList = <PersonItem>[].obs;
|
||||
var selectPersonNoList = <String>[].obs;
|
||||
|
||||
// 存储从参数传递过来的已选角色
|
||||
List<PersonItem>? initialSelectedPersons;
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
// 获取传递的参数
|
||||
var arguments = Get.arguments;
|
||||
if (arguments != null && arguments is List<PersonItem>) {
|
||||
initialSelectedPersons = arguments;
|
||||
}
|
||||
requestDepartList();
|
||||
_setInitialSelectedIndexes();
|
||||
}
|
||||
|
||||
requestDepartList() async {
|
||||
var response = await teamApi.requestDepartList(
|
||||
request: GetDepartListRequest(departNo: ''),
|
||||
);
|
||||
if (response.isSuccess) {
|
||||
// 转换数据为树形结构
|
||||
final tree = _convertToTree(response.data!.departList ?? []);
|
||||
treeData.value = tree;
|
||||
// 提取所有人员信息并更新 personList
|
||||
final allPersons = extractAllPersons();
|
||||
allPersonList.value = allPersons;
|
||||
|
||||
treeViewController?.expandAllChildren(treeData.value ?? TreeNode.root());
|
||||
}
|
||||
}
|
||||
|
||||
TreeNode _convertToTree(List<DepartItem> departList) {
|
||||
// 创建根节点
|
||||
final root = TreeNode.root();
|
||||
|
||||
// 如果列表为空,直接返回根节点
|
||||
if (departList.isEmpty) {
|
||||
return root;
|
||||
}
|
||||
|
||||
// 创建一个映射来存储所有部门节点,方便查找
|
||||
final Map<int, TreeNode> 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<PersonItem>.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;
|
||||
}
|
||||
|
||||
// 提取所有人员信息的方法
|
||||
List<PersonItem> extractAllPersons() {
|
||||
final List<PersonItem> persons = [];
|
||||
|
||||
// 递归遍历树节点
|
||||
void traverseTree(TreeNode? node) {
|
||||
if (node == null) return;
|
||||
|
||||
// 如果当前节点是人员节点,添加到列表中
|
||||
if (node.data is PersonItem) {
|
||||
persons.add(node.data as PersonItem);
|
||||
}
|
||||
|
||||
// 递归遍历所有子节点
|
||||
for (final child in node.childrenAsList) {
|
||||
traverseTree(child as TreeNode?);
|
||||
}
|
||||
}
|
||||
|
||||
// 从根节点开始遍历
|
||||
traverseTree(treeData.value);
|
||||
|
||||
return persons;
|
||||
}
|
||||
|
||||
// 根据初始选中的角色设置对应的索引
|
||||
void _setInitialSelectedIndexes() {
|
||||
if (initialSelectedPersons != null && initialSelectedPersons!.isNotEmpty) {
|
||||
for (var selectedPerson in initialSelectedPersons!) {
|
||||
if (!selectPersonNoList.contains(selectedPerson.personNo)) {
|
||||
selectPersonNoList.add(selectedPerson.personNo!);
|
||||
}
|
||||
}
|
||||
}
|
||||
selectPersonNoList.refresh();
|
||||
}
|
||||
}
|
||||
332
lib/views/team/selectPerson/select_person_view.dart
Normal file
332
lib/views/team/selectPerson/select_person_view.dart
Normal file
@ -0,0 +1,332 @@
|
||||
import 'package:animated_tree_view/animated_tree_view.dart';
|
||||
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/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 'package:starwork_flutter/extension/function_extension.dart';
|
||||
import 'select_person_controller.dart';
|
||||
|
||||
class SelectPersonView extends GetView<SelectPersonController> {
|
||||
@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(
|
||||
() {
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.all(Radius.circular(8.0)),
|
||||
),
|
||||
child: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await controller.requestDepartList();
|
||||
},
|
||||
color: const Color(0xFF4A90E2),
|
||||
backgroundColor: Colors.white,
|
||||
displacement: 60.0,
|
||||
edgeOffset: 0.0,
|
||||
triggerMode: RefreshIndicatorTriggerMode.onEdge,
|
||||
strokeWidth: 2.5,
|
||||
semanticsLabel: '下拉刷新首页内容',
|
||||
semanticsValue: '刷新中...',
|
||||
child: TreeView.simple(
|
||||
tree: controller.treeData.value ?? TreeNode.root(),
|
||||
showRootNode: false,
|
||||
expansionIndicatorBuilder: noExpansionIndicatorBuilder,
|
||||
indentation: const Indentation(style: IndentStyle.roundJoint),
|
||||
onItemTap: (item) {
|
||||
if (item.data is PersonItem) {
|
||||
PersonItem personInfo = item.data;
|
||||
// 避免重复添加
|
||||
if (!controller.selectPersonNoList.contains(personInfo.personNo) &&
|
||||
personInfo.personNo != null) {
|
||||
controller.selectPersonNoList.add(personInfo.personNo!);
|
||||
} else {
|
||||
controller.selectPersonNoList.remove(personInfo.personNo);
|
||||
}
|
||||
|
||||
controller.selectPersonNoList.refresh();
|
||||
}
|
||||
},
|
||||
onTreeReady: (c) {
|
||||
controller.treeViewController = c;
|
||||
},
|
||||
builder: (context, node) {
|
||||
String title = "";
|
||||
DepartItem departInfo = DepartItem();
|
||||
PersonItem personInfo = PersonItem();
|
||||
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) {
|
||||
// 处理人员节点
|
||||
personInfo = node.data as PersonItem;
|
||||
title = personInfo.personName ?? "未命名人员";
|
||||
|
||||
personInfo.roles?.forEach((role) {
|
||||
if (role.isSuper == 1) {
|
||||
isSuper = 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: isPersonItem,
|
||||
child: Obx(
|
||||
() => Checkbox(
|
||||
value: controller.selectPersonNoList.contains(personInfo.personNo),
|
||||
activeColor: Colors.blue,
|
||||
onChanged: (value) {
|
||||
if (value == true) {
|
||||
// 避免重复添加
|
||||
if (!controller.selectPersonNoList.contains(personInfo.personNo) &&
|
||||
personInfo.id != null) {
|
||||
controller.selectPersonNoList.add(personInfo.personNo!);
|
||||
}
|
||||
} else {
|
||||
controller.selectPersonNoList.remove(personInfo.personNo);
|
||||
}
|
||||
controller.selectPersonNoList.refresh();
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 10.h,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Obx(
|
||||
() => Text(
|
||||
'已选择:${controller.selectPersonNoList.length}人',
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.blue,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 20.w,
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
if (controller.selectPersonNoList.isEmpty) {
|
||||
controller.showToast('请先选择一个用户'.tr);
|
||||
return;
|
||||
}
|
||||
List<PersonItem> selectPersonList = [];
|
||||
// 通过controller.selectPersonNoList 判断controoler.allPersonList对应选中的personInfo
|
||||
controller.selectPersonNoList.forEach((personNo) {
|
||||
controller.allPersonList.forEach((personInfo) {
|
||||
if (personInfo.personNo == personNo) {
|
||||
selectPersonList.add(personInfo);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Get.back(result: selectPersonList);
|
||||
}.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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_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),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -230,6 +230,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "5.0.1"
|
||||
flutter_datetime_picker_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_datetime_picker_plus
|
||||
sha256: "7d82da02c4e070bb28a9107de119ad195e2319b45c786fecc13482a9ffcc51da"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
flutter_easyloading:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@ -40,6 +40,7 @@ dependencies:
|
||||
event_bus: ^2.0.1
|
||||
# 选择器
|
||||
flutter_picker: ^2.1.0
|
||||
flutter_datetime_picker_plus: ^2.2.0
|
||||
# 星云sdk
|
||||
starcloud:
|
||||
path: ../starcloud-sdk-flutter
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user