feat: develop multi picker common widget
This commit is contained in:
parent
865035e17f
commit
9361c8a9df
|
@ -33,19 +33,24 @@ class Api {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取我的个人信息
|
/// 获取我的个人信息
|
||||||
static Future<Response> getMyProfile() {
|
static Future<Response> getMyProfile() {
|
||||||
return DioService.dio.get(
|
return DioService.dio.get(
|
||||||
Urls.userInfo,
|
Urls.myProfile,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取个人信息
|
/// 获取个人信息
|
||||||
static Future<Response> getUserInfo(int userid) {
|
static Future<Response> getUserInfo(int userid) {
|
||||||
return DioService.dio.get('${Urls.userInfo}/$userid');
|
return DioService.dio.get('${Urls.sysUser}/$userid');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分页获取项目列表
|
/// 更新个人信息
|
||||||
|
static Future<Response> updateUserInfo(int userid, Map params) {
|
||||||
|
return DioService.dio.put('${Urls.sysUser}/$userid', data: params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 分页获取项目列表
|
||||||
static Future<Response<PaginationData>> getProjects(Map params) {
|
static Future<Response<PaginationData>> getProjects(Map params) {
|
||||||
return DioService.dio.get<PaginationData>(Urls.projects,
|
return DioService.dio.get<PaginationData>(Urls.projects,
|
||||||
queryParameters: {'page': 1, 'pageSize': 10, ...params});
|
queryParameters: {'page': 1, 'pageSize': 10, ...params});
|
||||||
|
|
|
@ -121,7 +121,7 @@ final theme = ThemeData(
|
||||||
color: AppTheme.grey,
|
color: AppTheme.grey,
|
||||||
fontSize: ScreenAdaper.height(25),
|
fontSize: ScreenAdaper.height(25),
|
||||||
),
|
),
|
||||||
fillColor: AppTheme.inputFillColor,
|
fillColor: Colors.transparent,
|
||||||
filled: true,
|
filled: true,
|
||||||
labelStyle: TextStyle(
|
labelStyle: TextStyle(
|
||||||
fontSize: ScreenAdaper.height(25),
|
fontSize: ScreenAdaper.height(25),
|
||||||
|
|
|
@ -17,3 +17,6 @@ class StorageBussinessModuleEnum {
|
||||||
static const Product = 'product';
|
static const Product = 'product';
|
||||||
static const Project = 'project';
|
static const Project = 'project';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 超级管理员角色 id */
|
||||||
|
final ROOT_ROLE_ID = 1;
|
||||||
|
|
|
@ -15,7 +15,6 @@ class Urls {
|
||||||
static String uploadAttachemnt = 'tools/upload';
|
static String uploadAttachemnt = 'tools/upload';
|
||||||
static String systemParamConfig = 'system/param-config';
|
static String systemParamConfig = 'system/param-config';
|
||||||
static String accountMenus = 'account/menus';
|
static String accountMenus = 'account/menus';
|
||||||
static String userInfo = 'system/users';
|
|
||||||
static String depts = 'system/depts';
|
static String depts = 'system/depts';
|
||||||
static String roles = 'system/roles';
|
static String roles = 'system/roles';
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,16 +22,16 @@ class UserInfoModel {
|
||||||
final int? id;
|
final int? id;
|
||||||
final DateTime? createdAt;
|
final DateTime? createdAt;
|
||||||
final DateTime? updatedAt;
|
final DateTime? updatedAt;
|
||||||
final String? username;
|
String? username;
|
||||||
final String? psalt;
|
String? psalt;
|
||||||
final String? nickname;
|
String? nickname;
|
||||||
final String? avatar;
|
String? avatar;
|
||||||
final String? qq;
|
String? qq;
|
||||||
final String? email;
|
String? email;
|
||||||
final String? phone;
|
String? phone;
|
||||||
final String? remark;
|
String? remark;
|
||||||
final int? status;
|
int? status;
|
||||||
final DeptModel? dept;
|
DeptModel? dept;
|
||||||
List<RoleModel> roles;
|
List<RoleModel> roles;
|
||||||
|
|
||||||
factory UserInfoModel.fromJson(Map<String, dynamic> json) {
|
factory UserInfoModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_cascade_picker.dart';
|
import 'package:sk_base_mobile/widgets/core/sk_cascade_picker.dart';
|
||||||
|
|
||||||
class DeptPicker extends StatelessWidget {
|
class DeptPicker extends StatelessWidget {
|
||||||
final Function(String)? onSelected;
|
final Function(CascadeItem<int, DeptModel>)? onSelected;
|
||||||
DeptPicker({super.key, this.onSelected});
|
DeptPicker({super.key, this.onSelected});
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -29,14 +29,10 @@ class DeptPicker extends StatelessWidget {
|
||||||
},
|
},
|
||||||
onConfirm: (List<CascadeItem<int, DeptModel>> value) {
|
onConfirm: (List<CascadeItem<int, DeptModel>> value) {
|
||||||
if (onSelected != null && value.isNotEmpty) {
|
if (onSelected != null && value.isNotEmpty) {
|
||||||
onSelected!(value.last.label);
|
onSelected!(value.last);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
maxPageNum: 10,
|
maxPageNum: 10,
|
||||||
tabTitleStyle:
|
|
||||||
TextStyle(fontSize: ScreenAdaper.height(26), color: Colors.black),
|
|
||||||
itemTitleStyle:
|
|
||||||
TextStyle(fontSize: ScreenAdaper.height(26), color: Colors.black),
|
|
||||||
selectedIcon: const Icon(Icons.check, color: AppTheme.primaryColorLight),
|
selectedIcon: const Icon(Icons.check, color: AppTheme.primaryColorLight),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,21 +2,26 @@ import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.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/apis/api.dart';
|
||||||
import 'package:sk_base_mobile/app_theme.dart';
|
import 'package:sk_base_mobile/app_theme.dart';
|
||||||
import 'package:sk_base_mobile/constants/bg_color.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/role.model.dart';
|
||||||
import 'package:sk_base_mobile/models/user_info.model.dart';
|
import 'package:sk_base_mobile/models/user_info.model.dart';
|
||||||
import 'package:sk_base_mobile/screens/hr_manage/components/dept_picker.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/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/screen_adaper_util.dart';
|
||||||
import 'package:sk_base_mobile/util/snack_bar.util.dart';
|
import 'package:sk_base_mobile/util/snack_bar.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_cascade_picker.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_dialog_header.dart';
|
import 'package:sk_base_mobile/widgets/core/sk_dialog_header.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_multi_picker.dart';
|
import 'package:sk_base_mobile/widgets/form_item/sk_multi_picker.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_multi_picker_dialog.dart';
|
import 'package:sk_base_mobile/widgets/form_item/sk_text_input.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_text_input.dart';
|
|
||||||
import 'package:sk_base_mobile/widgets/gradient_button.dart';
|
import 'package:sk_base_mobile/widgets/gradient_button.dart';
|
||||||
|
import 'package:sk_base_mobile/widgets/loading_indicator.dart';
|
||||||
|
|
||||||
/// 编辑用户信息
|
/// 编辑用户信息
|
||||||
class EditUserInfo extends StatelessWidget {
|
class EditUserInfo extends StatelessWidget {
|
||||||
|
@ -27,123 +32,129 @@ class EditUserInfo extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return Scaffold(
|
||||||
onTap: () {
|
// appBar: PreferredSize(
|
||||||
FocusScope.of(context).requestFocus(FocusNode());
|
// preferredSize: Size.fromHeight(40),
|
||||||
},
|
// child: AppBar(elevation: 0, title: const Text('首页')),
|
||||||
child: buildBody(),
|
// ),
|
||||||
|
backgroundColor: AppTheme.nearlyWhite,
|
||||||
|
body: buildBody(),
|
||||||
);
|
);
|
||||||
|
// return buildBody()
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildBody() {
|
Widget buildBody() {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
const SkDialogHeader(title: '编辑员工信息'),
|
const SkDialogHeader(
|
||||||
|
title: '编辑员工信息',
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: GestureDetector(
|
||||||
child: Container(
|
onTap: () {
|
||||||
padding: EdgeInsets.symmetric(
|
FocusScope.of(Get.context!).requestFocus(FocusNode());
|
||||||
horizontal: ScreenAdaper.height(15),
|
|
||||||
vertical: ScreenAdaper.height(15)),
|
|
||||||
child: Column(children: [
|
|
||||||
buildAvatar(),
|
|
||||||
SizedBox(
|
|
||||||
height: ScreenAdaper.height(defaultPadding),
|
|
||||||
),
|
|
||||||
|
|
||||||
/// 姓名
|
|
||||||
SkTextInput(
|
|
||||||
isDense: true,
|
|
||||||
textController: controller.nameEditController,
|
|
||||||
customLabel: true,
|
|
||||||
labelText: '姓名',
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: ScreenAdaper.height(defaultPadding),
|
|
||||||
),
|
|
||||||
|
|
||||||
/// 部门
|
|
||||||
SkTextInput(
|
|
||||||
isDense: true,
|
|
||||||
customLabel: true,
|
|
||||||
keyboardType: TextInputType.none,
|
|
||||||
textController: controller.deptEditController,
|
|
||||||
labelText: '所属部门',
|
|
||||||
onTap: (_) async {
|
|
||||||
Get.bottomSheet(DeptPicker(onSelected: (String label) {
|
|
||||||
controller.deptEditController.text = label;
|
|
||||||
}));
|
|
||||||
},
|
},
|
||||||
),
|
child: Obx(() => controller.loading.value
|
||||||
SizedBox(
|
? const LoadingIndicator(
|
||||||
height: ScreenAdaper.height(defaultPadding),
|
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),
|
||||||
|
),
|
||||||
|
|
||||||
/// 角色
|
/// 姓名
|
||||||
SkMultiPickerDropdown(
|
SkTextInput(
|
||||||
isDense: true,
|
isDense: true,
|
||||||
customLabel: true,
|
isRequired: true,
|
||||||
keyboardType: TextInputType.none,
|
textController: controller.nicknameEditController,
|
||||||
textController: controller.roleEditController,
|
customLabel: true,
|
||||||
labelText: '角色',
|
labelText: '姓名',
|
||||||
hint: '选择角色',
|
),
|
||||||
onTap: (_) async {
|
SizedBox(
|
||||||
Get.bottomSheet(SkMutiPickerDialog(
|
height: ScreenAdaper.height(defaultPadding),
|
||||||
onSelected: (List<PickerItem> selectedData) {
|
),
|
||||||
controller.roleEditController.text;
|
|
||||||
}, getData: () async {
|
/// 部门
|
||||||
try {
|
SkTextInput(
|
||||||
final res =
|
isDense: true,
|
||||||
await Api.getRoles({'page': 1, 'pageSize': 30});
|
customLabel: true,
|
||||||
if (res.data != null) {
|
isRequired: true,
|
||||||
List<PickerItem<int>> result =
|
keyboardType: TextInputType.none,
|
||||||
res.data!.items.map<PickerItem<int>>((e) {
|
textController: controller.deptEditController,
|
||||||
RoleModel data = RoleModel.fromJson(e);
|
labelText: '所属部门',
|
||||||
return PickerItem(
|
onTap: (_) async {
|
||||||
label: data.name,
|
Get.bottomSheet(
|
||||||
value: data.id!,
|
DeptPicker(onSelected: (CascadeItem item) {
|
||||||
subLabel: data.remark,
|
controller.deptEditController.text =
|
||||||
checked: false);
|
item.label;
|
||||||
}).toList();
|
controller.deptId = item.value;
|
||||||
return result;
|
}));
|
||||||
}
|
},
|
||||||
return [];
|
),
|
||||||
} catch (e) {
|
SizedBox(
|
||||||
LoggerUtil().error(e);
|
height: ScreenAdaper.height(defaultPadding),
|
||||||
return [];
|
),
|
||||||
}
|
|
||||||
})).then(
|
/// 角色
|
||||||
(value) => Get.delete<SkMutiPickerDialogController>());
|
SkMultiPickerDropdown<int>(
|
||||||
}),
|
isDense: true,
|
||||||
SizedBox(
|
customLabel: true,
|
||||||
height: ScreenAdaper.height(defaultPadding),
|
labelText: '角色',
|
||||||
),
|
isRequired: true,
|
||||||
SkTextInput(
|
hint: '选择角色',
|
||||||
isDense: true,
|
options: controller.roles,
|
||||||
textController: controller.nickNameEditController,
|
onOptionSelected:
|
||||||
customLabel: true,
|
(List<ValueItem<int>> selectedData) => {
|
||||||
labelText: '登录用户名',
|
controller.roleSelection.assignAll(selectedData)
|
||||||
),
|
},
|
||||||
SizedBox(
|
selectedOptions: controller.roleSelection,
|
||||||
height: ScreenAdaper.height(defaultPadding),
|
),
|
||||||
),
|
SizedBox(
|
||||||
SkTextInput(
|
height: ScreenAdaper.height(defaultPadding),
|
||||||
isDense: true,
|
),
|
||||||
customLabel: true,
|
SkTextInput(
|
||||||
textController: TextEditingController(),
|
isDense: true,
|
||||||
labelText: '手机号',
|
isRequired: true,
|
||||||
),
|
textController: controller.usernameEditController,
|
||||||
]),
|
customLabel: true,
|
||||||
),
|
labelText: '登录用户名',
|
||||||
)),
|
),
|
||||||
const GradientButton(
|
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,
|
borderRadius: BorderRadius.zero,
|
||||||
buttonText: '提交',
|
onPressed: controller.submit,
|
||||||
|
buttonText: '保存',
|
||||||
)
|
)
|
||||||
// Container(
|
|
||||||
// decoration: BoxDecoration(color: AppTheme.primaryColor),
|
|
||||||
// height: 100,
|
|
||||||
// )
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -154,12 +165,15 @@ class EditUserInfo extends StatelessWidget {
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: ScreenAdaper.height(10),
|
height: ScreenAdaper.height(10),
|
||||||
),
|
),
|
||||||
buildImageUploader(),
|
Obx(() => controller.filePath.isEmpty &&
|
||||||
|
controller.userInfo.value.avatar == null
|
||||||
|
? buildImageUploader()
|
||||||
|
: builderImagePreview()),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget builderImagePreview(String path, String type) {
|
Widget builderImagePreview() {
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
|
@ -173,14 +187,16 @@ class EditUserInfo extends StatelessWidget {
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
image: DecorationImage(
|
image: DecorationImage(
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
image: FileImage(File(path)),
|
image: controller.filePath.value.isNotEmpty
|
||||||
),
|
? FileImage(File(controller.filePath.value))
|
||||||
),
|
: NetworkImage(MediaUtil.getMediaUrl(
|
||||||
),
|
controller.userInfo.value!.avatar!))
|
||||||
|
as ImageProvider<Object>),
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
|
@ -188,11 +204,8 @@ class EditUserInfo extends StatelessWidget {
|
||||||
right: ScreenAdaper.width(5),
|
right: ScreenAdaper.width(5),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (type == 'agent') {
|
controller.filePath.value = '';
|
||||||
// controller.uploadAgentImgFilesPath.remove(path);
|
controller.userInfo.value.avatar = null;
|
||||||
} else {
|
|
||||||
// controller.uploadProductImgFilesPath.remove(path);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.close,
|
Icons.close,
|
||||||
|
@ -220,7 +233,7 @@ class EditUserInfo extends StatelessWidget {
|
||||||
Widget buildImageUploader() {
|
Widget buildImageUploader() {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// controller.photoPicker(type);
|
controller.photoPicker();
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: EdgeInsets.symmetric(horizontal: ScreenAdaper.width(5)),
|
margin: EdgeInsets.symmetric(horizontal: ScreenAdaper.width(5)),
|
||||||
|
@ -241,29 +254,145 @@ class EditUserInfo extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class EditUserInfoController extends GetxController {
|
class EditUserInfoController extends GetxController {
|
||||||
int userId;
|
final int userId;
|
||||||
EditUserInfoController(this.userId);
|
EditUserInfoController(this.userId);
|
||||||
final nameEditController = TextEditingController();
|
|
||||||
final deptEditController = TextEditingController();
|
|
||||||
final roleEditController = TextEditingController();
|
|
||||||
final nickNameEditController = TextEditingController();
|
|
||||||
final userInfo = Rxn<UserInfoModel>();
|
|
||||||
|
|
||||||
|
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
|
@override
|
||||||
onReady() {
|
onReady() {
|
||||||
getUserInfo();
|
init();
|
||||||
super.onReady();
|
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);
|
||||||
|
final resUser = await Api.getUserInfo(userInfo.value!.id!);
|
||||||
|
if (resUser.data != null) {
|
||||||
|
userInfo.value = UserInfoModel.fromJson(resUser.data);
|
||||||
|
}
|
||||||
|
Get.back(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 {
|
Future<void> getUserInfo() async {
|
||||||
try {
|
try {
|
||||||
final response = await Api.getUserInfo(userId);
|
final response = await Api.getUserInfo(userId);
|
||||||
if (response.data != null) {
|
if (response.data != null) {
|
||||||
userInfo.value = UserInfoModel.fromJson(response.data);
|
userInfo.value = UserInfoModel.fromJson(response.data);
|
||||||
nameEditController.text = userInfo.value?.nickname ?? '';
|
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) {
|
} catch (e) {
|
||||||
SnackBarUtil().error('$e');
|
SnackBarUtil().error('$e');
|
||||||
} finally {}
|
} finally {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> photoPicker() async {
|
||||||
|
XFile? pickedFile = await MediaUtil().getImageFromGallery();
|
||||||
|
if (pickedFile != null) {
|
||||||
|
filePath.value = pickedFile.path;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import 'package:sk_base_mobile/constants/bg_color.dart';
|
||||||
import 'package:sk_base_mobile/constants/constants.dart';
|
import 'package:sk_base_mobile/constants/constants.dart';
|
||||||
import 'package:sk_base_mobile/models/user_info.model.dart';
|
import 'package:sk_base_mobile/models/user_info.model.dart';
|
||||||
import 'package:sk_base_mobile/screens/hr_manage/components/edit_userinfo.dart';
|
import 'package:sk_base_mobile/screens/hr_manage/components/edit_userinfo.dart';
|
||||||
import 'package:sk_base_mobile/util/common.util.dart';
|
|
||||||
import 'package:sk_base_mobile/util/date.util.dart';
|
import 'package:sk_base_mobile/util/date.util.dart';
|
||||||
import 'package:sk_base_mobile/util/debouncer.dart';
|
import 'package:sk_base_mobile/util/debouncer.dart';
|
||||||
import 'package:sk_base_mobile/util/device.util.dart';
|
import 'package:sk_base_mobile/util/device.util.dart';
|
||||||
|
@ -18,7 +18,7 @@ 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/snack_bar.util.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_ink.dart';
|
import 'package:sk_base_mobile/widgets/core/sk_ink.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_tag.dart';
|
import 'package:sk_base_mobile/widgets/core/sk_tag.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_text_input.dart';
|
import 'package:sk_base_mobile/widgets/form_item/sk_text_input.dart';
|
||||||
import 'package:sk_base_mobile/widgets/empty.dart';
|
import 'package:sk_base_mobile/widgets/empty.dart';
|
||||||
import 'package:sk_base_mobile/widgets/fade_in_cache_image.dart';
|
import 'package:sk_base_mobile/widgets/fade_in_cache_image.dart';
|
||||||
import 'package:sk_base_mobile/widgets/loading_indicator.dart';
|
import 'package:sk_base_mobile/widgets/loading_indicator.dart';
|
||||||
|
@ -103,6 +103,7 @@ class HrManagePage extends StatelessWidget {
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: ScreenAdaper.height(70),
|
height: ScreenAdaper.height(70),
|
||||||
child: SkTextInput(
|
child: SkTextInput(
|
||||||
|
fillColor: AppTheme.inputFillColor,
|
||||||
textController: controller.searchBarTextConroller,
|
textController: controller.searchBarTextConroller,
|
||||||
onChanged: (value) => doSearch(value),
|
onChanged: (value) => doSearch(value),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
|
@ -243,10 +244,15 @@ class HrManagePage extends StatelessWidget {
|
||||||
/// 编辑
|
/// 编辑
|
||||||
buildActionButton(
|
buildActionButton(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
ModalUtil.showGeneralDialog(
|
ModalUtil.showGeneralDialog<UserInfoModel>(
|
||||||
content: EditUserInfo(
|
content: EditUserInfo(
|
||||||
userId: controller.list[index].id!,
|
userId: controller.list[index].id!,
|
||||||
)).then((value) => Get.delete<EditUserInfoController>());
|
)).then((value) {
|
||||||
|
Get.delete<EditUserInfoController>();
|
||||||
|
if (value != null) {
|
||||||
|
controller.list[index] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Get.toNamed(RouteConfig.employeeDetail,
|
// Get.toNamed(RouteConfig.employeeDetail,
|
||||||
// arguments: controller.list[index]);
|
// arguments: controller.list[index]);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:sk_base_mobile/app_theme.dart';
|
||||||
import 'package:sk_base_mobile/screens/new_inventory_inout/components/inventory_search.dart';
|
import 'package:sk_base_mobile/screens/new_inventory_inout/components/inventory_search.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
|
import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
|
||||||
|
|
||||||
|
@ -10,6 +11,7 @@ class InventoryPage extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: SkAppbar(title: '库存管理', hideLeading: isPage),
|
appBar: SkAppbar(title: '库存管理', hideLeading: isPage),
|
||||||
|
backgroundColor: AppTheme.nearlyWhite,
|
||||||
body: InventorySearch(),
|
body: InventorySearch(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,11 +237,10 @@ class InventoryInoutController extends GetxController {
|
||||||
// 出入库详情dialog
|
// 出入库详情dialog
|
||||||
Future showInventoryInoutInfoDialog(int id) async {
|
Future showInventoryInoutInfoDialog(int id) async {
|
||||||
ModalUtil.showGeneralDialog(
|
ModalUtil.showGeneralDialog(
|
||||||
width: ScreenAdaper.screenShortDistance() - ScreenAdaper.width(100),
|
width: ScreenAdaper.screenShortDistance() - ScreenAdaper.width(100),
|
||||||
height: Get.height - 100 < 400 ? 400 : Get.height - 100,
|
height: Get.height - 100 < 400 ? 400 : Get.height - 100,
|
||||||
content: InventoryInoutInfo(inventoryInoutId: id),
|
content: InventoryInoutInfo(inventoryInoutId: id),
|
||||||
offset: const Offset(0, -1))
|
).then((value) => {Get.delete<InventoryInouInfoController>()});
|
||||||
.then((value) => {Get.delete<InventoryInouInfoController>()});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<InventoryInOutModel>> getInoutHistory() async {
|
Future<List<InventoryInOutModel>> getInoutHistory() async {
|
||||||
|
|
|
@ -3,17 +3,17 @@ import 'package:get/get.dart';
|
||||||
import 'package:sk_base_mobile/store/auth.store.dart';
|
import 'package:sk_base_mobile/store/auth.store.dart';
|
||||||
|
|
||||||
class UserInfoController extends GetxController {
|
class UserInfoController extends GetxController {
|
||||||
final nickNameController = TextEditingController(text: '');
|
final nicknameController = TextEditingController(text: '');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onReady() async {
|
void onReady() async {
|
||||||
nickNameController.text = AuthStore.to.userInfo.value.nickname ?? '';
|
nicknameController.text = AuthStore.to.userInfo.value.nickname ?? '';
|
||||||
super.onReady();
|
super.onReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveUserInfo() async {
|
Future<void> saveUserInfo() async {
|
||||||
Map<String, dynamic> data = {
|
Map<String, dynamic> data = {
|
||||||
'nickname': nickNameController.text,
|
'nickname': nicknameController.text,
|
||||||
};
|
};
|
||||||
await AuthStore.to.saveUserInfo(data);
|
await AuthStore.to.saveUserInfo(data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ class UserInfoPage extends StatelessWidget {
|
||||||
child: MyAvatarWidget(),
|
child: MyAvatarWidget(),
|
||||||
),
|
),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: _controller.nickNameController,
|
controller: _controller.nicknameController,
|
||||||
cursorColor: const Color.fromARGB(255, 87, 86, 86),
|
cursorColor: const Color.fromARGB(255, 87, 86, 86),
|
||||||
style:
|
style:
|
||||||
TextStyle(fontSize: ScreenAdaper.height(18), color: Colors.black),
|
TextStyle(fontSize: ScreenAdaper.height(18), color: Colors.black),
|
||||||
|
|
|
@ -15,7 +15,7 @@ class ProductSearch extends StatelessWidget {
|
||||||
ProductSearch({super.key, this.onProductSelected});
|
ProductSearch({super.key, this.onProductSelected});
|
||||||
final controller = Get.put(ProductSearchController());
|
final controller = Get.put(ProductSearchController());
|
||||||
final listTitleTextStyle =
|
final listTitleTextStyle =
|
||||||
TextStyle(fontSize: ScreenAdaper.height(20), fontWeight: FontWeight.w600);
|
TextStyle(fontSize: ScreenAdaper.height(30), fontWeight: FontWeight.w600);
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
|
@ -121,7 +121,7 @@ class ProductSearch extends StatelessWidget {
|
||||||
BoxConstraints(minWidth: ScreenAdaper.width(100)),
|
BoxConstraints(minWidth: ScreenAdaper.width(100)),
|
||||||
child: Text(
|
child: Text(
|
||||||
itemData.productNumber!,
|
itemData.productNumber!,
|
||||||
style: TextStyle(fontSize: ScreenAdaper.height(20)),
|
style: TextStyle(fontSize: ScreenAdaper.height(25)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
|
@ -133,12 +133,12 @@ class ProductSearch extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'${itemData.name}',
|
'${itemData.name}',
|
||||||
style: TextStyle(fontSize: ScreenAdaper.height(20)),
|
style: TextStyle(fontSize: ScreenAdaper.height(28)),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'${itemData.company?.name}',
|
'${itemData.company?.name}',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: ScreenAdaper.height(15),
|
fontSize: ScreenAdaper.height(18),
|
||||||
color: AppTheme.grey),
|
color: AppTheme.grey),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -150,7 +150,9 @@ class ProductSearch extends StatelessWidget {
|
||||||
//最小宽度
|
//最小宽度
|
||||||
constraints:
|
constraints:
|
||||||
BoxConstraints(minWidth: ScreenAdaper.width(100)),
|
BoxConstraints(minWidth: ScreenAdaper.width(100)),
|
||||||
child: Text(itemData.productSpecification ?? ''),
|
child: Text(itemData.productSpecification ?? '',
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: ScreenAdaper.height(25))),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -13,10 +13,10 @@ import 'package:sk_base_mobile/screens/new_inventory_inout/components/inventory_
|
||||||
import 'package:sk_base_mobile/screens/new_inventory_inout/components/product_search.dart';
|
import 'package:sk_base_mobile/screens/new_inventory_inout/components/product_search.dart';
|
||||||
import 'package:sk_base_mobile/store/dict.store.dart';
|
import 'package:sk_base_mobile/store/dict.store.dart';
|
||||||
import 'package:sk_base_mobile/util/util.dart';
|
import 'package:sk_base_mobile/util/util.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_number_input.dart';
|
import 'package:sk_base_mobile/widgets/form_item/sk_number_input.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_search_select.dart';
|
import 'package:sk_base_mobile/widgets/form_item/sk_search_select.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_date_picker.dart';
|
import 'package:sk_base_mobile/widgets/form_item/sk_date_picker.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_text_input.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/gradient_button.dart';
|
||||||
import 'package:sk_base_mobile/screens/new_inventory_inout/new_inventory_inout_controller.dart';
|
import 'package:sk_base_mobile/screens/new_inventory_inout/new_inventory_inout_controller.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
|
import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
|
||||||
|
@ -32,26 +32,31 @@ class NewInventoryInout extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
top: false, // 设置为false以避免保留顶部状态栏的空间
|
top: false, // 设置为false以避免保留顶部状态栏的空间
|
||||||
child: Scaffold(
|
child: GestureDetector(
|
||||||
appBar: SkAppbar(
|
onTap: () {
|
||||||
title: inOrOut == InventoryInOrOutEnum.In ? '入库登记' : '出库登记',
|
FocusScope.of(context).requestFocus(FocusNode());
|
||||||
),
|
},
|
||||||
resizeToAvoidBottomInset: true,
|
child: Scaffold(
|
||||||
body: SingleChildScrollView(
|
appBar: SkAppbar(
|
||||||
child: Container(
|
title: inOrOut == InventoryInOrOutEnum.In ? '入库登记' : '出库登记',
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
),
|
||||||
child: Column(
|
resizeToAvoidBottomInset: true,
|
||||||
children: [
|
body: SingleChildScrollView(
|
||||||
buildForm(),
|
child: Container(
|
||||||
GradientButton(
|
decoration: const BoxDecoration(
|
||||||
buttonText: TextEnum.createInventoryInOutBtnText,
|
color: Colors.white,
|
||||||
onPressed: () => {controller.create()},
|
),
|
||||||
)
|
child: Column(
|
||||||
],
|
children: [
|
||||||
),
|
buildForm(),
|
||||||
))));
|
GradientButton(
|
||||||
|
buttonText: TextEnum.createInventoryInOutBtnText,
|
||||||
|
onPressed: () => {controller.create()},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
))),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildForm() {
|
Widget buildForm() {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import 'package:sk_base_mobile/models/base_search_more_controller.dart';
|
||||||
import 'package:sk_base_mobile/models/sale_quotation.model.dart';
|
import 'package:sk_base_mobile/models/sale_quotation.model.dart';
|
||||||
import 'package:sk_base_mobile/screens/sale_quotation/components/sale_quotation_group_search.dart';
|
import 'package:sk_base_mobile/screens/sale_quotation/components/sale_quotation_group_search.dart';
|
||||||
import 'package:sk_base_mobile/services/storage.service.dart';
|
import 'package:sk_base_mobile/services/storage.service.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_muti_search_more.dart';
|
import 'package:sk_base_mobile/widgets/form_item/sk_multi_search_more.dart';
|
||||||
import 'package:sk_base_mobile/util/modal.util.dart';
|
import 'package:sk_base_mobile/util/modal.util.dart';
|
||||||
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||||||
import 'package:pinyin/pinyin.dart';
|
import 'package:pinyin/pinyin.dart';
|
||||||
|
|
|
@ -9,8 +9,8 @@ import 'package:sk_base_mobile/screens/sale_quotation/sale_quotation.controller.
|
||||||
import 'package:sk_base_mobile/util/modal.util.dart';
|
import 'package:sk_base_mobile/util/modal.util.dart';
|
||||||
import 'package:sk_base_mobile/util/screen_adaper_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/snack_bar.util.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_number_input.dart';
|
import 'package:sk_base_mobile/widgets/form_item/sk_number_input.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_text_input.dart';
|
import 'package:sk_base_mobile/widgets/form_item/sk_text_input.dart';
|
||||||
import 'package:sk_base_mobile/widgets/empty.dart';
|
import 'package:sk_base_mobile/widgets/empty.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
|
import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
|
||||||
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
|
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:sk_base_mobile/app_theme.dart';
|
||||||
import 'package:sk_base_mobile/constants/router.dart';
|
import 'package:sk_base_mobile/constants/router.dart';
|
||||||
import 'package:sk_base_mobile/screens/workbench/workbench_controller.dart';
|
import 'package:sk_base_mobile/screens/workbench/workbench_controller.dart';
|
||||||
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||||||
|
@ -16,6 +17,7 @@ class WorkBenchPage extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
backgroundColor: AppTheme.nearlyWhite,
|
||||||
appBar: const SkAppbar(title: '工作台', hideLeading: true),
|
appBar: const SkAppbar(title: '工作台', hideLeading: true),
|
||||||
body: buildList());
|
body: buildList());
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,10 +118,10 @@ class DioService extends get_package.GetxService {
|
||||||
options.headers['model'] = StorageService.to
|
options.headers['model'] = StorageService.to
|
||||||
.getString(CacheKeys.deviceModel, isWithUser: false); // 设备型号
|
.getString(CacheKeys.deviceModel, isWithUser: false); // 设备型号
|
||||||
}
|
}
|
||||||
// if (GloablConfig.DEBUG && (options.data is! FormData)) {
|
if (GloablConfig.DEBUG && (options.data is! FormData)) {
|
||||||
// LoggerUtil().info(
|
LoggerUtil().info(
|
||||||
// '[Service-dio] url: ${options.path}, params: ${jsonEncode(options.queryParameters)}, body: ${jsonEncode(options.data)}, Header:${jsonEncode(options.headers)}');
|
'[Service-dio] url: ${options.path}, params: ${jsonEncode(options.queryParameters)}, body: ${jsonEncode(options.data)}, Header:${jsonEncode(options.headers)}');
|
||||||
// }
|
}
|
||||||
|
|
||||||
handler.next(options);
|
handler.next(options);
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,7 @@ class DioService extends get_package.GetxService {
|
||||||
}
|
}
|
||||||
if (response.data != null && response.data is Map) {
|
if (response.data != null && response.data is Map) {
|
||||||
if (response.data['code'] == 200) {
|
if (response.data['code'] == 200) {
|
||||||
// if (GloablConfig.DEBUG) LoggerUtil().info(response.data['data']);
|
if (GloablConfig.DEBUG) LoggerUtil().info(response.data['data']);
|
||||||
response.data = response.data['data'];
|
response.data = response.data['data'];
|
||||||
// 分页数据处理
|
// 分页数据处理
|
||||||
if (response.data != null &&
|
if (response.data != null &&
|
||||||
|
|
|
@ -10,9 +10,9 @@ import 'package:sk_base_mobile/models/upload_result.model.dart';
|
||||||
import 'package:sk_base_mobile/services/service.dart';
|
import 'package:sk_base_mobile/services/service.dart';
|
||||||
|
|
||||||
class MediaUtil {
|
class MediaUtil {
|
||||||
static String? getMediaUrl(String? url) {
|
static String getMediaUrl(String? url) {
|
||||||
if ((url ?? '').isEmpty) {
|
if ((url ?? '').isEmpty) {
|
||||||
return null;
|
return '';
|
||||||
} else {
|
} else {
|
||||||
return '${GloablConfig.OSS_URL}$url';
|
return '${GloablConfig.OSS_URL}$url';
|
||||||
}
|
}
|
||||||
|
@ -45,9 +45,8 @@ class MediaUtil {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
AppInfoService.to.isCameraing.value = true;
|
AppInfoService.to.isCameraing.value = true;
|
||||||
XFile? pickedFile = await ImagePicker().pickImage(
|
XFile? pickedFile = await ImagePicker()
|
||||||
source: ImageSource.gallery,
|
.pickImage(source: ImageSource.gallery, imageQuality: 60);
|
||||||
);
|
|
||||||
AppInfoService.to.isCameraing.value = false;
|
AppInfoService.to.isCameraing.value = false;
|
||||||
return pickedFile;
|
return pickedFile;
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,25 +161,40 @@ class ModalUtil {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
static Future<void> showGeneralDialog(
|
static Future<T?> showGeneralDialog<T>(
|
||||||
{required Widget content,
|
{required Widget content,
|
||||||
double? width,
|
double? width,
|
||||||
double? height,
|
double? height,
|
||||||
Offset? offset}) {
|
Offset? offset}) {
|
||||||
return Get.generalDialog(
|
return Get.generalDialog<T>(
|
||||||
barrierLabel: "generalDialog",
|
barrierLabel: "generalDialog",
|
||||||
barrierDismissible: true,
|
barrierDismissible: true,
|
||||||
transitionDuration: const Duration(milliseconds: 400),
|
transitionDuration: const Duration(milliseconds: 400),
|
||||||
pageBuilder: (_, __, ___) {
|
pageBuilder: (_, __, ___) {
|
||||||
return Center(
|
return SafeArea(
|
||||||
child: ClipRRect(
|
child: ScreenAdaper.isTablet()
|
||||||
borderRadius: BorderRadius.circular(ScreenAdaper.sp(30)),
|
? Center(
|
||||||
child: Material(
|
child: ClipRRect(
|
||||||
child: SizedBox(
|
borderRadius:
|
||||||
height: height ?? Get.height - ScreenAdaper.height(150),
|
BorderRadius.circular(ScreenAdaper.sp(30)),
|
||||||
width: width ?? Get.width - ScreenAdaper.width(150),
|
child: Material(
|
||||||
child: content,
|
child: SizedBox(
|
||||||
))));
|
height:
|
||||||
|
height ?? Get.height - ScreenAdaper.height(150),
|
||||||
|
width: width ?? Get.width - ScreenAdaper.width(150),
|
||||||
|
child: content,
|
||||||
|
),
|
||||||
|
)))
|
||||||
|
: Container(
|
||||||
|
margin: EdgeInsets.only(top: Get.height / 10),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(ScreenAdaper.sp(30)),
|
||||||
|
topRight: Radius.circular(ScreenAdaper.sp(30))),
|
||||||
|
child: Material(
|
||||||
|
child: content,
|
||||||
|
),
|
||||||
|
)));
|
||||||
},
|
},
|
||||||
transitionBuilder: (_, anim, __, child) {
|
transitionBuilder: (_, anim, __, child) {
|
||||||
Tween<Offset> tween;
|
Tween<Offset> tween;
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/// [SelectionType]
|
||||||
|
/// SelectionType enum for the selection type of the dropdown items.
|
||||||
|
/// * [single]: single selection
|
||||||
|
/// * [multi]: multi selection
|
||||||
|
enum SelectionType {
|
||||||
|
single,
|
||||||
|
multi,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [WrapType]
|
||||||
|
/// WrapType enum for the wrap type of the selected items.
|
||||||
|
/// * [WrapType.scroll]: scroll the selected items horizontally
|
||||||
|
/// * [WrapType.wrap]: wrap the selected items in both directions
|
||||||
|
enum WrapType { scroll, wrap }
|
||||||
|
|
||||||
|
/// [RequestMethod]
|
||||||
|
/// RequestMethod enum for the request method of the dropdown items.
|
||||||
|
/// * [RequestMethod.get]: get request
|
||||||
|
/// * [RequestMethod.post]: post request
|
||||||
|
/// * [RequestMethod.put]: put request
|
||||||
|
/// * [RequestMethod.delete]: delete request
|
||||||
|
/// * [RequestMethod.patch]: patch request
|
||||||
|
enum RequestMethod { get, post, put, patch, delete }
|
|
@ -0,0 +1,75 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../enum/app_enums.dart';
|
||||||
|
|
||||||
|
/// Configuration for the chip.
|
||||||
|
/// [backgroundColor] is the background color of the chip. Defaults to [Colors.white].
|
||||||
|
/// [padding] is the padding of the chip.
|
||||||
|
/// [radius] is the radius of the chip. Defaults to [BorderRadius.circular(18)].
|
||||||
|
///
|
||||||
|
/// [labelStyle] is the style of the label.
|
||||||
|
/// [labelPadding] is the padding of the label.
|
||||||
|
///
|
||||||
|
/// [deleteIcon] is the icon that is used to delete the chip.
|
||||||
|
/// [deleteIconColor] is the color of the delete icon.
|
||||||
|
///
|
||||||
|
/// [separator] is the separator between the chips. Default is a sized box with width of 8.
|
||||||
|
/// [spacing] is the width of the separator. If separator is provided, this value is ignored.
|
||||||
|
///
|
||||||
|
/// [wrapType] is the type of the chip. Default is [WrapType.scroll]. [WrapType.wrap] will wrap the chips to next line if there is not enough space. [WrapType.scroll] will scroll the chips.
|
||||||
|
/// * [WrapType.scroll] is used to scroll the chips.
|
||||||
|
/// * [WrapType.wrap] is used to wrap the chips in a row.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// An example of a [ChipConfig] is:
|
||||||
|
/// ```dart
|
||||||
|
/// const ChipConfig(
|
||||||
|
/// deleteIcon: Icon(Icons.delete, color: Colors.red),
|
||||||
|
/// wrapType: WrapType.scroll,
|
||||||
|
/// separator: const Divider(),
|
||||||
|
/// padding: const EdgeInsets.all(8),
|
||||||
|
/// labelStyle: TextStyle(fontSize: 16),
|
||||||
|
/// labelPadding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
/// radius: BorderRadius.circular(18),
|
||||||
|
/// backgroundColor: Colors.white,
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
class ChipConfig {
|
||||||
|
final Icon? deleteIcon;
|
||||||
|
|
||||||
|
final Color deleteIconColor;
|
||||||
|
final Color labelColor;
|
||||||
|
final Color? backgroundColor;
|
||||||
|
|
||||||
|
final TextStyle? labelStyle;
|
||||||
|
final EdgeInsets padding;
|
||||||
|
final EdgeInsets labelPadding;
|
||||||
|
|
||||||
|
final double radius;
|
||||||
|
final double spacing;
|
||||||
|
final double runSpacing;
|
||||||
|
|
||||||
|
final Widget? separator;
|
||||||
|
|
||||||
|
final WrapType wrapType;
|
||||||
|
|
||||||
|
final bool autoScroll;
|
||||||
|
|
||||||
|
const ChipConfig({
|
||||||
|
this.deleteIcon,
|
||||||
|
this.deleteIconColor = Colors.white,
|
||||||
|
this.backgroundColor,
|
||||||
|
this.padding = const EdgeInsets.only(left: 12, top: 0, right: 4, bottom: 0),
|
||||||
|
this.radius = 18,
|
||||||
|
this.spacing = 8,
|
||||||
|
this.runSpacing = 8,
|
||||||
|
this.separator,
|
||||||
|
this.labelColor = Colors.white,
|
||||||
|
this.labelStyle,
|
||||||
|
this.wrapType = WrapType.scroll,
|
||||||
|
this.labelPadding = EdgeInsets.zero,
|
||||||
|
this.autoScroll = false,
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import '../enum/app_enums.dart';
|
||||||
|
|
||||||
|
/// Configuration for the network.
|
||||||
|
///
|
||||||
|
/// [url] is the url of the network.
|
||||||
|
/// [method] is the request method of the network.
|
||||||
|
/// [headers] is the headers of the network.
|
||||||
|
/// [body] is the body of the network.
|
||||||
|
/// [queryParameters] is the query parameters of the network.
|
||||||
|
|
||||||
|
class NetworkConfig {
|
||||||
|
final String url;
|
||||||
|
final RequestMethod method;
|
||||||
|
final Map<String, String>? headers;
|
||||||
|
final Map<String, dynamic>? body;
|
||||||
|
final Map<String, dynamic>? queryParameters;
|
||||||
|
|
||||||
|
NetworkConfig({
|
||||||
|
required this.url,
|
||||||
|
this.method = RequestMethod.get,
|
||||||
|
this.headers = const {},
|
||||||
|
this.body,
|
||||||
|
this.queryParameters = const {},
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
/// [label] is the item that is displayed in the list. [value] is the value that is returned when the item is selected.
|
||||||
|
/// If the [value] is not provided, the [label] is used as the value.
|
||||||
|
/// subLabel is the subtitle that is displayed in the list.
|
||||||
|
/// An example of a [ValueItem] is:
|
||||||
|
/// ```dart
|
||||||
|
/// const ValueItem(label: 'Option 1', value: '1')
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
class ValueItem<T> {
|
||||||
|
/// The label of the value item
|
||||||
|
final String label;
|
||||||
|
final String? subLabel;
|
||||||
|
|
||||||
|
/// The value of the value item
|
||||||
|
final T? value;
|
||||||
|
|
||||||
|
/// Default constructor for [ValueItem]
|
||||||
|
const ValueItem({required this.label, required this.value, this.subLabel});
|
||||||
|
|
||||||
|
/// toString method for [ValueItem]
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'ValueItem(label: $label, value: $value, subLabel: $subLabel)';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// toMap method for [ValueItem]
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {'label': label, 'value': value, 'subLabel': subLabel};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// fromMap method for [ValueItem]
|
||||||
|
factory ValueItem.fromMap(Map<String, dynamic> map) {
|
||||||
|
return ValueItem<T>(
|
||||||
|
label: map['label'] ?? '',
|
||||||
|
value: map['value'],
|
||||||
|
subLabel: map['subLabel'] ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// toJson method for [ValueItem]
|
||||||
|
String toJson() => json.encode(toMap());
|
||||||
|
|
||||||
|
/// fromJson method for [ValueItem]
|
||||||
|
factory ValueItem.fromJson(String source) =>
|
||||||
|
ValueItem<T>.fromMap(json.decode(source));
|
||||||
|
|
||||||
|
/// Equality operator for [ValueItem]
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other is ValueItem<T> && other.value == value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hashcode for [ValueItem]
|
||||||
|
@override
|
||||||
|
int get hashCode => label.hashCode ^ value.hashCode ^ subLabel.hashCode;
|
||||||
|
|
||||||
|
/// CopyWith method for [ValueItem]
|
||||||
|
ValueItem<T> copyWith({
|
||||||
|
String? label,
|
||||||
|
T? value,
|
||||||
|
}) {
|
||||||
|
return ValueItem<T>(
|
||||||
|
label: label ?? this.label,
|
||||||
|
value: value ?? this.value,
|
||||||
|
subLabel: subLabel ?? this.subLabel);
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,36 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
/// [HintText] is a hint text builder.
|
||||||
|
/// It is used to build the hint text.
|
||||||
|
class HintText extends StatelessWidget {
|
||||||
|
final TextStyle? hintStyle;
|
||||||
|
final String hintText;
|
||||||
|
final Color? hintColor;
|
||||||
|
final EdgeInsetsGeometry? hintPadding;
|
||||||
|
|
||||||
|
const HintText({
|
||||||
|
Key? key,
|
||||||
|
this.hintStyle,
|
||||||
|
required this.hintText,
|
||||||
|
this.hintColor,
|
||||||
|
this.hintPadding,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
static const EdgeInsetsGeometry hintPaddingDefault =
|
||||||
|
EdgeInsets.symmetric(horizontal: 10.0);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: hintPadding ?? hintPaddingDefault,
|
||||||
|
child: Text(
|
||||||
|
hintText,
|
||||||
|
style: hintStyle ??
|
||||||
|
TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: hintColor ?? Colors.grey.shade300,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../multiselect_dropdown.dart';
|
||||||
|
|
||||||
|
/// [SelectionChip] is a selected option chip builder.
|
||||||
|
/// It is used to build the selected option chip.
|
||||||
|
class SelectionChip<T> extends StatelessWidget {
|
||||||
|
final ChipConfig chipConfig;
|
||||||
|
final Function(ValueItem<T>) onItemDelete;
|
||||||
|
final ValueItem<T> item;
|
||||||
|
|
||||||
|
const SelectionChip({
|
||||||
|
Key? key,
|
||||||
|
required this.chipConfig,
|
||||||
|
required this.item,
|
||||||
|
required this.onItemDelete,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Chip(
|
||||||
|
padding: chipConfig.padding,
|
||||||
|
label: Text(item.label),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(chipConfig.radius),
|
||||||
|
),
|
||||||
|
deleteIcon: chipConfig.deleteIcon,
|
||||||
|
deleteIconColor: chipConfig.deleteIconColor,
|
||||||
|
labelPadding: chipConfig.labelPadding,
|
||||||
|
backgroundColor:
|
||||||
|
chipConfig.backgroundColor ?? Theme.of(context).primaryColor,
|
||||||
|
labelStyle: chipConfig.labelStyle ??
|
||||||
|
TextStyle(color: chipConfig.labelColor, fontSize: 14),
|
||||||
|
onDeleted: () => onItemDelete(item),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
/// [SingleSelectedItem] is a selected item builder.
|
||||||
|
/// It is used to build the selected item.
|
||||||
|
class SingleSelectedItem extends StatelessWidget {
|
||||||
|
/// [label] is the selected item label.
|
||||||
|
final String label;
|
||||||
|
|
||||||
|
final TextStyle? style;
|
||||||
|
|
||||||
|
const SingleSelectedItem({
|
||||||
|
required this.label,
|
||||||
|
this.style,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: style ??
|
||||||
|
TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: Colors.grey.shade700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:sk_base_mobile/app_theme.dart';
|
import 'package:sk_base_mobile/app_theme.dart';
|
||||||
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||||||
|
import 'package:sk_base_mobile/widgets/core/sk_ink.dart';
|
||||||
import 'package:sk_base_mobile/widgets/gradient_button.dart';
|
import 'package:sk_base_mobile/widgets/gradient_button.dart';
|
||||||
import 'package:sk_base_mobile/widgets/loading_indicator.dart';
|
import 'package:sk_base_mobile/widgets/loading_indicator.dart';
|
||||||
|
|
||||||
|
@ -18,9 +19,9 @@ class SkCascadePicker<T, Z> extends StatelessWidget {
|
||||||
final int maxPageNum;
|
final int maxPageNum;
|
||||||
final Color tabColor;
|
final Color tabColor;
|
||||||
final double tabHeight;
|
final double tabHeight;
|
||||||
final TextStyle tabTitleStyle;
|
final TextStyle? tabTitleStyle;
|
||||||
final double itemHeight;
|
final double itemHeight;
|
||||||
final TextStyle itemTitleStyle;
|
final TextStyle? itemTitleStyle;
|
||||||
final Color itemColor;
|
final Color itemColor;
|
||||||
final Color activeColor;
|
final Color activeColor;
|
||||||
final Widget? selectedIcon;
|
final Widget? selectedIcon;
|
||||||
|
@ -36,10 +37,10 @@ class SkCascadePicker<T, Z> extends StatelessWidget {
|
||||||
this.tabHeight = 40,
|
this.tabHeight = 40,
|
||||||
this.activeColor = AppTheme.primaryColor,
|
this.activeColor = AppTheme.primaryColor,
|
||||||
this.tabColor = Colors.white,
|
this.tabColor = Colors.white,
|
||||||
this.tabTitleStyle = const TextStyle(color: Colors.black, fontSize: 14),
|
this.tabTitleStyle,
|
||||||
this.itemHeight = 40,
|
this.itemHeight = 40,
|
||||||
this.itemColor = Colors.white,
|
this.itemColor = Colors.white,
|
||||||
this.itemTitleStyle = const TextStyle(color: Colors.black, fontSize: 14),
|
this.itemTitleStyle,
|
||||||
this.selectedIcon})
|
this.selectedIcon})
|
||||||
: cascadeController = Get.put(SkCascadePickerController<T, Z>(
|
: cascadeController = Get.put(SkCascadePickerController<T, Z>(
|
||||||
initialPageData: initialPageData,
|
initialPageData: initialPageData,
|
||||||
|
@ -57,27 +58,65 @@ class SkCascadePicker<T, Z> extends StatelessWidget {
|
||||||
common: true,
|
common: true,
|
||||||
)
|
)
|
||||||
: Column(children: [
|
: Column(children: [
|
||||||
GradientButton(
|
Container(
|
||||||
buttonText: '确定',
|
padding: EdgeInsets.symmetric(
|
||||||
onPressed: () {
|
horizontal: ScreenAdaper.width(20),
|
||||||
Get.back();
|
vertical: ScreenAdaper.height(10)),
|
||||||
if (onConfirm != null) {
|
child: Row(
|
||||||
onConfirm!(cascadeController.selectedTabs
|
children: [
|
||||||
.where((e) =>
|
SkInk(
|
||||||
e.label != SkCascadePickerController.newTabName)
|
onTap: () {
|
||||||
.toList() as List<CascadeItem<T, Z>>);
|
Get.back();
|
||||||
}
|
},
|
||||||
|
child: Text(
|
||||||
// 已选中的titles
|
'取消',
|
||||||
// List<CascadeItem<T,Z>> selectedTitles =
|
style: TextStyle(fontSize: ScreenAdaper.height(30)),
|
||||||
// cascadeController.selectedTabs;
|
)),
|
||||||
// print("已选中的titles: $selectedTitles");
|
const Spacer(),
|
||||||
// 已选中的序号
|
SkInk(
|
||||||
// List<int> selectedIndexes =
|
onTap: () {
|
||||||
// cascadeController.selectedIndexes;
|
Get.back();
|
||||||
// print("已选中的序号:$selectedIndexes");
|
if (onConfirm != null) {
|
||||||
},
|
onConfirm!(cascadeController.selectedTabs
|
||||||
|
.where((e) =>
|
||||||
|
e.label !=
|
||||||
|
SkCascadePickerController.newTabName)
|
||||||
|
.toList() as List<CascadeItem<T, Z>>);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
'确定',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: ScreenAdaper.height(30),
|
||||||
|
color: AppTheme.primaryColor),
|
||||||
|
))
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
const Divider(
|
||||||
|
height: 1,
|
||||||
|
),
|
||||||
|
// GradientButton(
|
||||||
|
// buttonText: '确定',
|
||||||
|
// onPressed: () {
|
||||||
|
// Get.back();
|
||||||
|
// if (onConfirm != null) {
|
||||||
|
// onConfirm!(cascadeController.selectedTabs
|
||||||
|
// .where((e) =>
|
||||||
|
// e.label != SkCascadePickerController.newTabName)
|
||||||
|
// .toList() as List<CascadeItem<T, Z>>);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 已选中的titles
|
||||||
|
// // List<CascadeItem<T,Z>> selectedTitles =
|
||||||
|
// // cascadeController.selectedTabs;
|
||||||
|
// // print("已选中的titles: $selectedTitles");
|
||||||
|
// // 已选中的序号
|
||||||
|
// // List<int> selectedIndexes =
|
||||||
|
// // cascadeController.selectedIndexes;
|
||||||
|
// // print("已选中的序号:$selectedIndexes");
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -100,9 +139,6 @@ class SkCascadePicker<T, Z> extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Divider(
|
|
||||||
height: 1,
|
|
||||||
),
|
|
||||||
ValueListenableBuilder<double>(
|
ValueListenableBuilder<double>(
|
||||||
valueListenable: cascadeController.sliderFixMargin,
|
valueListenable: cascadeController.sliderFixMargin,
|
||||||
builder: (_, margin, __) => Positioned(
|
builder: (_, margin, __) => Positioned(
|
||||||
|
@ -181,8 +217,15 @@ class SkCascadePicker<T, Z> extends StatelessWidget {
|
||||||
child: Text(
|
child: Text(
|
||||||
cascadeController.selectedTabs[i].label,
|
cascadeController.selectedTabs[i].label,
|
||||||
style: cascadeController.urrentSelectPage == i
|
style: cascadeController.urrentSelectPage == i
|
||||||
? tabTitleStyle.copyWith(color: activeColor)
|
? (tabTitleStyle ??
|
||||||
: tabTitleStyle,
|
TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: ScreenAdaper.height(30)))
|
||||||
|
.copyWith(color: activeColor)
|
||||||
|
: (tabTitleStyle ??
|
||||||
|
TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: ScreenAdaper.height(30))),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
|
@ -235,8 +278,15 @@ class SkCascadePicker<T, Z> extends StatelessWidget {
|
||||||
: SizedBox(),
|
: SizedBox(),
|
||||||
Text(item.label,
|
Text(item.label,
|
||||||
style: item == cascadeController.selectedTabs[page]
|
style: item == cascadeController.selectedTabs[page]
|
||||||
? itemTitleStyle.copyWith(color: activeColor)
|
? (itemTitleStyle ??
|
||||||
: itemTitleStyle),
|
TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: ScreenAdaper.height(30)))
|
||||||
|
.copyWith(color: activeColor)
|
||||||
|
: (itemTitleStyle ??
|
||||||
|
TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: ScreenAdaper.height(30)))),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
|
|
|
@ -24,7 +24,7 @@ class SkDialogHeader extends StatelessWidget {
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
trailing ??
|
leading ??
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: ScreenAdaper.width(100),
|
width: ScreenAdaper.width(100),
|
||||||
),
|
),
|
||||||
|
|
|
@ -24,7 +24,7 @@ class SkTag extends StatelessWidget {
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: color ?? AppTheme.primaryColorLight,
|
color: color ?? AppTheme.primaryColorLight,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
fontSize: ScreenAdaper.sp(30)),
|
fontSize: ScreenAdaper.height(25)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,15 +4,16 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
abstract class SkBaseFieldWidget extends StatelessWidget {
|
abstract class SkBaseFieldWidget extends StatelessWidget {
|
||||||
FocusNode focusNode = FocusNode();
|
final FocusNode focusNode = FocusNode();
|
||||||
late SkBaseFieldController baseFieldController;
|
late final SkBaseFieldController baseFieldController;
|
||||||
final bool customLabel;
|
final bool customLabel;
|
||||||
final String? labelText;
|
final String? labelText;
|
||||||
|
final Color? fillColor;
|
||||||
SkBaseFieldWidget({
|
SkBaseFieldWidget({
|
||||||
super.key,
|
super.key,
|
||||||
this.customLabel = false,
|
this.customLabel = false,
|
||||||
this.labelText,
|
this.labelText,
|
||||||
|
this.fillColor,
|
||||||
autoFocus = false,
|
autoFocus = false,
|
||||||
}) {
|
}) {
|
||||||
baseFieldController = Get.put(
|
baseFieldController = Get.put(
|
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart';
|
import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart';
|
||||||
import 'package:sk_base_mobile/app_theme.dart';
|
import 'package:sk_base_mobile/app_theme.dart';
|
||||||
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_base_field.dart';
|
import 'package:sk_base_mobile/widgets/form_item/sk_base_field.dart';
|
||||||
|
|
||||||
class SkFormItem extends StatelessWidget {
|
class SkFormItem extends StatelessWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
@ -34,17 +34,21 @@ class SkFormItem extends StatelessWidget {
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.red, fontSize: ScreenAdaper.height(25)),
|
color: Colors.red, fontSize: ScreenAdaper.height(25)),
|
||||||
),
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: ScreenAdaper.width(5),
|
||||||
|
),
|
||||||
Obx(() => Text(
|
Obx(() => Text(
|
||||||
labelText ?? '',
|
labelText ?? '',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: ScreenAdaper.height(25),
|
fontSize: ScreenAdaper.height(25),
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
color: controller.isFocus.value
|
color: controller.isFocus.value
|
||||||
? AppTheme.primaryColor
|
? AppTheme.primaryColor
|
||||||
: AppTheme.nearlyBlack),
|
: AppTheme.nearlyBlack),
|
||||||
)),
|
)),
|
||||||
]),
|
]),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: ScreenAdaper.height(5),
|
height: ScreenAdaper.height(8),
|
||||||
),
|
),
|
||||||
child
|
child
|
||||||
],
|
],
|
|
@ -2,16 +2,13 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:sk_base_mobile/app_theme.dart';
|
import 'package:sk_base_mobile/app_theme.dart';
|
||||||
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_base_field.dart';
|
import 'package:sk_base_mobile/widgets/common/multi-picker/multiselect_dropdown.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_form_item.dart';
|
import 'package:sk_base_mobile/widgets/form_item/sk_form_item.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_tag.dart';
|
import 'package:sk_base_mobile/widgets/form_item/sk_base_field.dart';
|
||||||
import 'package:multi_dropdown/multiselect_dropdown.dart';
|
|
||||||
|
|
||||||
class SkMultiPickerDropdown<T> extends SkBaseFieldWidget {
|
class SkMultiPickerDropdown<T> extends SkBaseFieldWidget {
|
||||||
final TextEditingController textController;
|
|
||||||
final Function(FocusNode)? onTap;
|
final Function(FocusNode)? onTap;
|
||||||
final bool isRequired;
|
final bool isRequired;
|
||||||
final String? labelText;
|
|
||||||
final String? hint;
|
final String? hint;
|
||||||
final bool isTextArea;
|
final bool isTextArea;
|
||||||
final bool isDense;
|
final bool isDense;
|
||||||
|
@ -19,29 +16,30 @@ class SkMultiPickerDropdown<T> extends SkBaseFieldWidget {
|
||||||
final Function(String)? onChanged;
|
final Function(String)? onChanged;
|
||||||
final String? Function(String?)? validator;
|
final String? Function(String?)? validator;
|
||||||
final EdgeInsetsGeometry? contentPadding;
|
final EdgeInsetsGeometry? contentPadding;
|
||||||
final ValueChanged<String>? onFieldSubmitted;
|
final void Function(List<ValueItem<T>>)? onOptionSelected;
|
||||||
final Icon? prefix;
|
final Icon? prefix;
|
||||||
final Widget? suffixIcon;
|
final Widget? suffixIcon;
|
||||||
final InputBorder? border;
|
final InputBorder? border;
|
||||||
final FloatingLabelBehavior? floatingLabelBehavior;
|
final List<ValueItem<T>>? options;
|
||||||
final TextInputType? keyboardType;
|
final List<ValueItem<T>>? selectedOptions;
|
||||||
|
final MultiSelectController<T> multiSelectController =
|
||||||
|
MultiSelectController<T>();
|
||||||
SkMultiPickerDropdown(
|
SkMultiPickerDropdown(
|
||||||
{super.key,
|
{super.key,
|
||||||
required this.textController,
|
|
||||||
super.customLabel = false,
|
super.customLabel = false,
|
||||||
super.autoFocus = false,
|
super.autoFocus = false,
|
||||||
|
super.labelText,
|
||||||
|
this.options,
|
||||||
|
this.selectedOptions,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
|
this.onOptionSelected,
|
||||||
this.hint,
|
this.hint,
|
||||||
this.onFieldSubmitted,
|
|
||||||
this.isRequired = false,
|
this.isRequired = false,
|
||||||
this.onTapOutside,
|
this.onTapOutside,
|
||||||
this.keyboardType,
|
|
||||||
this.labelText,
|
|
||||||
this.prefix,
|
this.prefix,
|
||||||
this.suffixIcon,
|
this.suffixIcon,
|
||||||
this.onChanged,
|
this.onChanged,
|
||||||
this.border,
|
this.border,
|
||||||
this.floatingLabelBehavior = FloatingLabelBehavior.always,
|
|
||||||
this.isTextArea = false,
|
this.isTextArea = false,
|
||||||
this.contentPadding,
|
this.contentPadding,
|
||||||
this.isDense = false,
|
this.isDense = false,
|
||||||
|
@ -50,35 +48,37 @@ class SkMultiPickerDropdown<T> extends SkBaseFieldWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SkFormItem(
|
return SkFormItem(
|
||||||
child: MultiSelectDropDown<int>(
|
customLabel: customLabel,
|
||||||
focusNode: focusNode,
|
labelText: labelText,
|
||||||
onOptionSelected: (List<ValueItem> selectedOptions) {},
|
isRequired: isRequired,
|
||||||
options: <ValueItem<int>>[
|
controller: baseFieldController,
|
||||||
ValueItem(label: 'Option 1', value: 1),
|
child: MultiSelectDropDown<T>(
|
||||||
ValueItem(label: 'Option 2', value: 2),
|
controller: multiSelectController,
|
||||||
ValueItem(label: 'Option 3', value: 3),
|
focusNode: focusNode,
|
||||||
ValueItem(label: 'Option 4', value: 4),
|
selectedOptions: selectedOptions ?? [],
|
||||||
ValueItem(label: 'Option 5', value: 5),
|
dropdownHeight: ScreenAdaper.height(300),
|
||||||
ValueItem(label: 'Option 6', value: 6),
|
onOptionSelected: onOptionSelected,
|
||||||
],
|
options: options ?? <ValueItem<T>>[],
|
||||||
borderColor: AppTheme.nearlyBlack,
|
borderColor: AppTheme.nearlyBlack,
|
||||||
fieldBackgroundColor: AppTheme.inputFillColor,
|
fieldBackgroundColor: fillColor,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderRadius: ScreenAdaper.sp(15),
|
borderRadius: ScreenAdaper.sp(15),
|
||||||
focusedBorderWidth: 2,
|
focusedBorderWidth: 2,
|
||||||
focusedBorderColor: AppTheme.primaryColorLight,
|
focusedBorderColor: AppTheme.primaryColorLight,
|
||||||
hint: '请选择',
|
hint: '请选择',
|
||||||
hintStyle: Theme.of(Get.context!).inputDecorationTheme.hintStyle,
|
hintStyle: Theme.of(Get.context!).inputDecorationTheme.hintStyle,
|
||||||
hintPadding: EdgeInsets.symmetric(horizontal: ScreenAdaper.width(5)),
|
hintPadding: EdgeInsets.symmetric(horizontal: ScreenAdaper.width(5)),
|
||||||
selectionType: SelectionType.multi,
|
selectionType: SelectionType.multi,
|
||||||
chipConfig: const ChipConfig(wrapType: WrapType.scroll),
|
chipConfig: ChipConfig(
|
||||||
optionTextStyle: const TextStyle(fontSize: 16),
|
wrapType: WrapType.scroll,
|
||||||
selectedOptionIcon: const Icon(Icons.check_circle),
|
labelStyle: TextStyle(
|
||||||
|
fontSize: ScreenAdaper.height(26), color: AppTheme.white),
|
||||||
|
backgroundColor: AppTheme.primaryColor,
|
||||||
),
|
),
|
||||||
customLabel: customLabel,
|
optionTextStyle: TextStyle(fontSize: ScreenAdaper.height(26)),
|
||||||
labelText: labelText,
|
selectedOptionIcon: const Icon(Icons.check_circle),
|
||||||
isRequired: isRequired,
|
),
|
||||||
controller: baseFieldController);
|
);
|
||||||
|
|
||||||
// return DropdownButtonFormField<String>(
|
// return DropdownButtonFormField<String>(
|
||||||
// focusNode: focusNode,
|
// focusNode: focusNode,
|
|
@ -1,7 +1,8 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:sk_base_mobile/app_theme.dart';
|
||||||
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_base_field.dart';
|
import 'package:sk_base_mobile/widgets/form_item/sk_form_item.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_form_item.dart';
|
import 'package:sk_base_mobile/widgets/form_item/sk_base_field.dart';
|
||||||
|
|
||||||
class SkTextInput extends SkBaseFieldWidget {
|
class SkTextInput extends SkBaseFieldWidget {
|
||||||
final TextEditingController textController;
|
final TextEditingController textController;
|
||||||
|
@ -28,6 +29,7 @@ class SkTextInput extends SkBaseFieldWidget {
|
||||||
super.customLabel = false,
|
super.customLabel = false,
|
||||||
super.autoFocus = false,
|
super.autoFocus = false,
|
||||||
super.labelText,
|
super.labelText,
|
||||||
|
super.fillColor,
|
||||||
required this.textController,
|
required this.textController,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.hint,
|
this.hint,
|
||||||
|
@ -75,6 +77,8 @@ class SkTextInput extends SkBaseFieldWidget {
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: prefix,
|
prefixIcon: prefix,
|
||||||
suffixIcon: suffixIcon,
|
suffixIcon: suffixIcon,
|
||||||
|
fillColor: fillColor,
|
||||||
|
filled: true,
|
||||||
errorStyle: const TextStyle(fontSize: 0, height: 0.01),
|
errorStyle: const TextStyle(fontSize: 0, height: 0.01),
|
||||||
contentPadding: contentPadding,
|
contentPadding: contentPadding,
|
||||||
isDense: isDense,
|
isDense: isDense,
|
|
@ -608,14 +608,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.5"
|
||||||
multi_dropdown:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: multi_dropdown
|
|
||||||
sha256: b63ff339fcc875d667f8688c8ef62853545b580dd2b6fe78b73339783268afd8
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.4"
|
|
||||||
octo_image:
|
octo_image:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -68,7 +68,6 @@ dependencies:
|
||||||
math_expressions: ^2.4.0
|
math_expressions: ^2.4.0
|
||||||
install_plugin: ^2.1.0
|
install_plugin: ^2.1.0
|
||||||
url_launcher: ^6.2.5
|
url_launcher: ^6.2.5
|
||||||
multi_dropdown: ^2.1.4
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
Loading…
Reference in New Issue