412 lines
14 KiB
Dart
412 lines
14 KiB
Dart
|
import 'dart:io';
|
||
|
|
||
|
import 'package:flutter/material.dart';
|
||
|
import 'package:get/get.dart';
|
||
|
import 'package:image_picker/image_picker.dart';
|
||
|
import 'package:sk_base_mobile/apis/api.dart';
|
||
|
import 'package:sk_base_mobile/app_theme.dart';
|
||
|
import 'package:sk_base_mobile/constants/bg_color.dart';
|
||
|
import 'package:sk_base_mobile/constants/enum.dart';
|
||
|
import 'package:sk_base_mobile/models/role.model.dart';
|
||
|
import 'package:sk_base_mobile/models/user_info.model.dart';
|
||
|
import 'package:sk_base_mobile/router/router.util.dart';
|
||
|
import 'package:sk_base_mobile/screens/hr_manage/components/dept_picker.dart';
|
||
|
import 'package:sk_base_mobile/util/loading_util.dart';
|
||
|
import 'package:sk_base_mobile/util/logger_util.dart';
|
||
|
import 'package:sk_base_mobile/util/media_util.dart';
|
||
|
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||
|
import 'package:sk_base_mobile/util/snack_bar.util.dart';
|
||
|
import 'package:sk_base_mobile/util/util.dart';
|
||
|
import 'package:sk_base_mobile/widgets/common/multi-picker/models/value_item.dart';
|
||
|
import 'package:sk_base_mobile/widgets/core/sk_cascade_picker.dart';
|
||
|
import 'package:sk_base_mobile/widgets/core/sk_dialog_header.dart';
|
||
|
import 'package:sk_base_mobile/widgets/form_item/sk_multi_picker.dart';
|
||
|
import 'package:sk_base_mobile/widgets/form_item/sk_text_input.dart';
|
||
|
import 'package:sk_base_mobile/widgets/gradient_button.dart';
|
||
|
import 'package:sk_base_mobile/widgets/loading_indicator.dart';
|
||
|
|
||
|
/// 编辑用户信息
|
||
|
class EditUserInfo extends StatelessWidget {
|
||
|
final int userId;
|
||
|
final EditUserInfoController controller;
|
||
|
EditUserInfo({super.key, required this.userId})
|
||
|
: controller = Get.put(EditUserInfoController(userId));
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Scaffold(
|
||
|
// appBar: PreferredSize(
|
||
|
// preferredSize: Size.fromHeight(40),
|
||
|
// child: AppBar(elevation: 0, title: const Text('首页')),
|
||
|
// ),
|
||
|
backgroundColor: AppTheme.nearlyWhite,
|
||
|
body: buildBody(),
|
||
|
);
|
||
|
// return buildBody()
|
||
|
}
|
||
|
|
||
|
Widget buildBody() {
|
||
|
return Column(
|
||
|
children: [
|
||
|
const SkDialogHeader(
|
||
|
title: '编辑员工信息',
|
||
|
),
|
||
|
Expanded(
|
||
|
child: GestureDetector(
|
||
|
onTap: () {
|
||
|
FocusScope.of(Get.context!).requestFocus(FocusNode());
|
||
|
},
|
||
|
child: Obx(() => controller.loading.value
|
||
|
? const LoadingIndicator(
|
||
|
common: true,
|
||
|
)
|
||
|
: SingleChildScrollView(
|
||
|
child: Container(
|
||
|
padding: EdgeInsets.symmetric(
|
||
|
horizontal: ScreenAdaper.height(15),
|
||
|
vertical: ScreenAdaper.height(15)),
|
||
|
child: Column(children: [
|
||
|
buildAvatar(),
|
||
|
SizedBox(
|
||
|
height: ScreenAdaper.height(defaultPadding),
|
||
|
),
|
||
|
|
||
|
/// 姓名
|
||
|
SkTextInput(
|
||
|
isDense: true,
|
||
|
isRequired: true,
|
||
|
textController: controller.nicknameEditController,
|
||
|
customLabel: true,
|
||
|
labelText: '姓名',
|
||
|
),
|
||
|
SizedBox(
|
||
|
height: ScreenAdaper.height(defaultPadding),
|
||
|
),
|
||
|
|
||
|
/// 部门
|
||
|
SkTextInput(
|
||
|
isDense: true,
|
||
|
customLabel: true,
|
||
|
isRequired: true,
|
||
|
keyboardType: TextInputType.none,
|
||
|
textController: controller.deptEditController,
|
||
|
labelText: '所属部门',
|
||
|
onTap: (_) async {
|
||
|
Get.bottomSheet(
|
||
|
DeptPicker(onSelected: (CascadeItem item) {
|
||
|
controller.deptEditController.text =
|
||
|
item.label;
|
||
|
controller.deptId = item.value;
|
||
|
}));
|
||
|
},
|
||
|
),
|
||
|
SizedBox(
|
||
|
height: ScreenAdaper.height(defaultPadding),
|
||
|
),
|
||
|
|
||
|
/// 角色
|
||
|
SkMultiPickerDropdown<int>(
|
||
|
isDense: true,
|
||
|
customLabel: true,
|
||
|
labelText: '角色',
|
||
|
isRequired: true,
|
||
|
hint: '选择角色',
|
||
|
options: controller.roles,
|
||
|
onOptionSelected:
|
||
|
(List<ValueItem<int>> selectedData) => {
|
||
|
controller.roleSelection.assignAll(selectedData)
|
||
|
},
|
||
|
selectedOptions: controller.roleSelection,
|
||
|
),
|
||
|
SizedBox(
|
||
|
height: ScreenAdaper.height(defaultPadding),
|
||
|
),
|
||
|
SkTextInput(
|
||
|
isDense: true,
|
||
|
isRequired: true,
|
||
|
textController: controller.usernameEditController,
|
||
|
customLabel: true,
|
||
|
labelText: '登录用户名',
|
||
|
),
|
||
|
SizedBox(
|
||
|
height: ScreenAdaper.height(defaultPadding),
|
||
|
),
|
||
|
SkTextInput(
|
||
|
isDense: true,
|
||
|
customLabel: true,
|
||
|
textController: controller.phoneEditController,
|
||
|
labelText: '手机号',
|
||
|
),
|
||
|
SizedBox(
|
||
|
height: ScreenAdaper.height(defaultPadding),
|
||
|
),
|
||
|
SkTextInput(
|
||
|
isDense: true,
|
||
|
customLabel: true,
|
||
|
textController: controller.emailEditController,
|
||
|
labelText: '邮箱',
|
||
|
),
|
||
|
]),
|
||
|
),
|
||
|
)))),
|
||
|
SizedBox(
|
||
|
height: ScreenAdaper.height(10),
|
||
|
),
|
||
|
GradientButton(
|
||
|
borderRadius: BorderRadius.zero,
|
||
|
onPressed: controller.submit,
|
||
|
buttonText: '保存',
|
||
|
)
|
||
|
],
|
||
|
);
|
||
|
}
|
||
|
|
||
|
Widget buildAvatar() {
|
||
|
return Column(
|
||
|
children: [
|
||
|
SizedBox(
|
||
|
height: ScreenAdaper.height(10),
|
||
|
),
|
||
|
Obx(() => controller.filePath.isEmpty &&
|
||
|
controller.userInfo.value.avatar == null
|
||
|
? buildImageUploader()
|
||
|
: builderImagePreview()),
|
||
|
],
|
||
|
);
|
||
|
}
|
||
|
|
||
|
Widget builderImagePreview() {
|
||
|
return Stack(
|
||
|
children: [
|
||
|
Container(
|
||
|
margin: EdgeInsets.symmetric(horizontal: ScreenAdaper.width(5)),
|
||
|
width: ScreenAdaper.width(180),
|
||
|
height: ScreenAdaper.width(180),
|
||
|
decoration: BoxDecoration(
|
||
|
borderRadius: BorderRadius.circular(20),
|
||
|
border: Border.all(width: 1.0, color: AppTheme.dividerColor),
|
||
|
// color: AppTheme.primaryColor,
|
||
|
),
|
||
|
child: Center(
|
||
|
child: Container(
|
||
|
decoration: BoxDecoration(
|
||
|
borderRadius: BorderRadius.circular(10),
|
||
|
image: DecorationImage(
|
||
|
fit: BoxFit.cover,
|
||
|
image: controller.filePath.value.isNotEmpty
|
||
|
? FileImage(File(controller.filePath.value))
|
||
|
: NetworkImage(MediaUtil.getMediaUrl(
|
||
|
controller.userInfo.value.avatar))
|
||
|
as ImageProvider<Object>),
|
||
|
)),
|
||
|
),
|
||
|
),
|
||
|
Positioned(
|
||
|
top: ScreenAdaper.height(5),
|
||
|
right: ScreenAdaper.width(5),
|
||
|
child: GestureDetector(
|
||
|
onTap: () {
|
||
|
controller.filePath.value = '';
|
||
|
controller.userInfo.value.avatar = null;
|
||
|
},
|
||
|
child: Icon(
|
||
|
Icons.close,
|
||
|
shadows: const [
|
||
|
Shadow(
|
||
|
color: Colors.black,
|
||
|
offset: Offset(1, 1),
|
||
|
blurRadius: 3,
|
||
|
),
|
||
|
Shadow(
|
||
|
color: Colors.black,
|
||
|
offset: Offset(-1, -1),
|
||
|
blurRadius: 3,
|
||
|
),
|
||
|
],
|
||
|
size: ScreenAdaper.height(40),
|
||
|
color: AppTheme.nearlyWhite,
|
||
|
),
|
||
|
))
|
||
|
],
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// 上传照片控制器
|
||
|
Widget buildImageUploader() {
|
||
|
return GestureDetector(
|
||
|
onTap: () {
|
||
|
controller.photoPicker();
|
||
|
},
|
||
|
child: Container(
|
||
|
margin: EdgeInsets.symmetric(horizontal: ScreenAdaper.width(5)),
|
||
|
width: ScreenAdaper.width(180),
|
||
|
height: ScreenAdaper.width(180),
|
||
|
decoration: BoxDecoration(
|
||
|
borderRadius: BorderRadius.circular(100),
|
||
|
border: Border.all(width: 1.0, color: AppTheme.dividerColor),
|
||
|
// color: AppTheme.primaryColor,
|
||
|
),
|
||
|
child: Center(
|
||
|
child: Icon(
|
||
|
Icons.add_a_photo_rounded,
|
||
|
size: ScreenAdaper.height(60),
|
||
|
color: AppTheme.primaryColor,
|
||
|
))));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class EditUserInfoController extends GetxController {
|
||
|
final int userId;
|
||
|
EditUserInfoController(this.userId);
|
||
|
|
||
|
final deptEditController = TextEditingController();
|
||
|
final nicknameEditController = TextEditingController();
|
||
|
final usernameEditController = TextEditingController();
|
||
|
final phoneEditController = TextEditingController();
|
||
|
final emailEditController = TextEditingController();
|
||
|
final userInfo = Rx<UserInfoModel>(UserInfoModel());
|
||
|
final RxList<ValueItem<int>> roles = RxList([]);
|
||
|
final RxBool loading = false.obs;
|
||
|
final List<ValueItem<int>> roleSelection = [];
|
||
|
int? deptId;
|
||
|
final filePath = ''.obs;
|
||
|
@override
|
||
|
onReady() {
|
||
|
init();
|
||
|
super.onReady();
|
||
|
}
|
||
|
|
||
|
init() async {
|
||
|
loading.value = true;
|
||
|
await Future.wait([getUserInfo(), getRoles()]);
|
||
|
loading.value = false;
|
||
|
}
|
||
|
|
||
|
Future<void> submit() async {
|
||
|
Map data = {
|
||
|
'nickname': nicknameEditController.text,
|
||
|
'deptId': deptId,
|
||
|
'username': usernameEditController.text,
|
||
|
'phone': phoneEditController.text,
|
||
|
'roleIds': roleSelection.map((e) => e.value).toList()
|
||
|
};
|
||
|
if (nicknameEditController.text.isEmpty) {
|
||
|
SnackBarUtil().error(
|
||
|
'姓名不能为空',
|
||
|
);
|
||
|
return;
|
||
|
}
|
||
|
if (deptId == null) {
|
||
|
SnackBarUtil().error(
|
||
|
'部门不能为空',
|
||
|
);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (usernameEditController.text.isEmpty) {
|
||
|
SnackBarUtil().error(
|
||
|
'登录名不能为空',
|
||
|
);
|
||
|
return;
|
||
|
}
|
||
|
if (roleSelection.map((e) => e.value).toList().isEmpty) {
|
||
|
SnackBarUtil().error(
|
||
|
'角色至少选择一个基础员工角色',
|
||
|
);
|
||
|
return;
|
||
|
}
|
||
|
await LoadingUtil.to.show(status: '保存中请稍后...');
|
||
|
try {
|
||
|
if (filePath.value.isNotEmpty) {
|
||
|
// 批量同时上传
|
||
|
final res = await MediaUtil().uploadImg(File(filePath.value));
|
||
|
if (res != null) {
|
||
|
data['avatar'] = res.path;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
await Api.updateUserInfo(userInfo.value.id!, data);
|
||
|
await LoadingUtil.to.dismiss();
|
||
|
final resUser = await Api.getUserInfo(userInfo.value.id!);
|
||
|
if (resUser.data != null) {
|
||
|
userInfo.value = UserInfoModel.fromJson(resUser.data);
|
||
|
}
|
||
|
await RouterUtil.back<UserInfoModel>(result: userInfo.value);
|
||
|
SnackBarUtil().success(
|
||
|
'保存成功',
|
||
|
);
|
||
|
} catch (e) {
|
||
|
LoggerUtil().error(e);
|
||
|
} finally {
|
||
|
LoadingUtil.to.dismiss();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Future<void> getRoles() async {
|
||
|
try {
|
||
|
await Future.delayed(const Duration(milliseconds: 500));
|
||
|
final res =
|
||
|
await Api.getRoles({'page': 1, 'pageSize': 30, 'useForSelect': 1});
|
||
|
if (res.data != null) {
|
||
|
List<ValueItem<int>> result = res.data!.items.map<ValueItem<int>>((e) {
|
||
|
RoleModel data = RoleModel.fromJson(e);
|
||
|
return ValueItem(
|
||
|
label: data.name, value: data.id!, subLabel: data.remark);
|
||
|
}).toList();
|
||
|
final rootRole = userInfo.value.roles
|
||
|
.firstWhereOrNull((element) => element.id == ROOT_ROLE_ID);
|
||
|
if (rootRole != null) {
|
||
|
result.add(ValueItem(
|
||
|
label: rootRole.name,
|
||
|
value: rootRole.id,
|
||
|
subLabel: rootRole.remark));
|
||
|
}
|
||
|
|
||
|
roles.assignAll(result);
|
||
|
}
|
||
|
} catch (e) {
|
||
|
LoggerUtil().error(e);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Future<void> getUserInfo() async {
|
||
|
try {
|
||
|
final response = await Api.getUserInfo(userId);
|
||
|
if (response.data != null) {
|
||
|
userInfo.value = UserInfoModel.fromJson(response.data);
|
||
|
nicknameEditController.text = userInfo.value.nickname ?? '';
|
||
|
usernameEditController.text = userInfo.value.username ?? '';
|
||
|
deptEditController.text = userInfo.value.dept?.name ?? '';
|
||
|
phoneEditController.text = userInfo.value.phone ?? '';
|
||
|
emailEditController.text = userInfo.value.email ?? '';
|
||
|
deptId = userInfo.value.dept?.id;
|
||
|
roleSelection.assignAll(userInfo.value!.roles
|
||
|
.map<ValueItem<int>>((data) => ValueItem(
|
||
|
label: data.name,
|
||
|
value: data.id!,
|
||
|
))
|
||
|
.toList());
|
||
|
}
|
||
|
} catch (e) {
|
||
|
SnackBarUtil().error('$e');
|
||
|
} finally {}
|
||
|
}
|
||
|
|
||
|
Future<void> photoPicker() async {
|
||
|
await MediaUtil().showPicker(callback: (path) async {
|
||
|
if (path != null) {
|
||
|
filePath.value = path;
|
||
|
// 拿到照片大小多少MB
|
||
|
final file = File(path);
|
||
|
final size = await file.length();
|
||
|
final sizeInMb = size / (1024 * 1024);
|
||
|
print(sizeInMb);
|
||
|
// if (sizeInMb > 2) {
|
||
|
// SnackBarUtil().error('图片大小不能超过2MB');
|
||
|
// filePath.value = '';
|
||
|
// }
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|