feat: develop multi picker common widget

This commit is contained in:
louis 2024-04-10 11:04:27 +08:00
parent 865035e17f
commit 9361c8a9df
42 changed files with 2088 additions and 311 deletions

View File

@ -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});

View File

@ -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),

View File

@ -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;

View File

@ -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';
} }

View File

@ -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) {

View File

@ -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),
); );
} }

View File

@ -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,20 +32,33 @@ 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(
onTap: () {
FocusScope.of(Get.context!).requestFocus(FocusNode());
},
child: Obx(() => controller.loading.value
? const LoadingIndicator(
common: true,
)
: SingleChildScrollView(
child: Container( child: Container(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: ScreenAdaper.height(15), horizontal: ScreenAdaper.height(15),
@ -54,7 +72,8 @@ class EditUserInfo extends StatelessWidget {
/// ///
SkTextInput( SkTextInput(
isDense: true, isDense: true,
textController: controller.nameEditController, isRequired: true,
textController: controller.nicknameEditController,
customLabel: true, customLabel: true,
labelText: '姓名', labelText: '姓名',
), ),
@ -66,12 +85,16 @@ class EditUserInfo extends StatelessWidget {
SkTextInput( SkTextInput(
isDense: true, isDense: true,
customLabel: true, customLabel: true,
isRequired: true,
keyboardType: TextInputType.none, keyboardType: TextInputType.none,
textController: controller.deptEditController, textController: controller.deptEditController,
labelText: '所属部门', labelText: '所属部门',
onTap: (_) async { onTap: (_) async {
Get.bottomSheet(DeptPicker(onSelected: (String label) { Get.bottomSheet(
controller.deptEditController.text = label; DeptPicker(onSelected: (CascadeItem item) {
controller.deptEditController.text =
item.label;
controller.deptId = item.value;
})); }));
}, },
), ),
@ -80,47 +103,26 @@ class EditUserInfo extends StatelessWidget {
), ),
/// ///
SkMultiPickerDropdown( SkMultiPickerDropdown<int>(
isDense: true, isDense: true,
customLabel: true, customLabel: true,
keyboardType: TextInputType.none,
textController: controller.roleEditController,
labelText: '角色', labelText: '角色',
isRequired: true,
hint: '选择角色', hint: '选择角色',
onTap: (_) async { options: controller.roles,
Get.bottomSheet(SkMutiPickerDialog( onOptionSelected:
onSelected: (List<PickerItem> selectedData) { (List<ValueItem<int>> selectedData) => {
controller.roleEditController.text; controller.roleSelection.assignAll(selectedData)
}, getData: () async { },
try { selectedOptions: controller.roleSelection,
final res = ),
await Api.getRoles({'page': 1, 'pageSize': 30});
if (res.data != null) {
List<PickerItem<int>> result =
res.data!.items.map<PickerItem<int>>((e) {
RoleModel data = RoleModel.fromJson(e);
return PickerItem(
label: data.name,
value: data.id!,
subLabel: data.remark,
checked: false);
}).toList();
return result;
}
return [];
} catch (e) {
LoggerUtil().error(e);
return [];
}
})).then(
(value) => Get.delete<SkMutiPickerDialogController>());
}),
SizedBox( SizedBox(
height: ScreenAdaper.height(defaultPadding), height: ScreenAdaper.height(defaultPadding),
), ),
SkTextInput( SkTextInput(
isDense: true, isDense: true,
textController: controller.nickNameEditController, isRequired: true,
textController: controller.usernameEditController,
customLabel: true, customLabel: true,
labelText: '登录用户名', labelText: '登录用户名',
), ),
@ -130,20 +132,29 @@ class EditUserInfo extends StatelessWidget {
SkTextInput( SkTextInput(
isDense: true, isDense: true,
customLabel: true, customLabel: true,
textController: TextEditingController(), textController: controller.phoneEditController,
labelText: '手机号', labelText: '手机号',
), ),
SizedBox(
height: ScreenAdaper.height(defaultPadding),
),
SkTextInput(
isDense: true,
customLabel: true,
textController: controller.emailEditController,
labelText: '邮箱',
),
]), ]),
), ),
)), )))),
const GradientButton( 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(
@ -177,10 +191,12 @@ class EditUserInfo extends StatelessWidget {
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;
}
}
} }

View File

@ -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]);

View File

@ -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(),
); );
} }

View File

@ -240,8 +240,7 @@ class InventoryInoutController extends GetxController {
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 {

View File

@ -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);
} }

View File

@ -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),

View File

@ -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))),
) )
], ],
), ),

View File

@ -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,6 +32,10 @@ class NewInventoryInout extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SafeArea( return SafeArea(
top: false, // false以避免保留顶部状态栏的空间 top: false, // false以避免保留顶部状态栏的空间
child: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Scaffold( child: Scaffold(
appBar: SkAppbar( appBar: SkAppbar(
title: inOrOut == InventoryInOrOutEnum.In ? '入库登记' : '出库登记', title: inOrOut == InventoryInOrOutEnum.In ? '入库登记' : '出库登记',
@ -51,7 +55,8 @@ class NewInventoryInout extends StatelessWidget {
) )
], ],
), ),
)))); ))),
));
} }
Widget buildForm() { Widget buildForm() {

View File

@ -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';

View File

@ -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';

View File

@ -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());
} }

View File

@ -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 &&

View File

@ -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;
} }

View File

@ -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: ScreenAdaper.isTablet()
? Center(
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(ScreenAdaper.sp(30)), borderRadius:
BorderRadius.circular(ScreenAdaper.sp(30)),
child: Material( child: Material(
child: SizedBox( child: SizedBox(
height: height ?? Get.height - ScreenAdaper.height(150), height:
height ?? Get.height - ScreenAdaper.height(150),
width: width ?? Get.width - ScreenAdaper.width(150), width: width ?? Get.width - ScreenAdaper.width(150),
child: content, 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;

View File

@ -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 }

View File

@ -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,
});
}

View File

@ -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 {},
});
}

View File

@ -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

View File

@ -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,
),
),
);
}
}

View File

@ -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),
);
}
}

View File

@ -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,
),
),
);
}
}

View File

@ -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),
vertical: ScreenAdaper.height(10)),
child: Row(
children: [
SkInk(
onTap: () {
Get.back();
},
child: Text(
'取消',
style: TextStyle(fontSize: ScreenAdaper.height(30)),
)),
const Spacer(),
SkInk(
onTap: () {
Get.back(); Get.back();
if (onConfirm != null) { if (onConfirm != null) {
onConfirm!(cascadeController.selectedTabs onConfirm!(cascadeController.selectedTabs
.where((e) => .where((e) =>
e.label != SkCascadePickerController.newTabName) e.label !=
SkCascadePickerController.newTabName)
.toList() as List<CascadeItem<T, Z>>); .toList() as List<CascadeItem<T, Z>>);
} }
// titles
// List<CascadeItem<T,Z>> selectedTitles =
// cascadeController.selectedTabs;
// print("已选中的titles: $selectedTitles");
//
// List<int> selectedIndexes =
// cascadeController.selectedIndexes;
// print("已选中的序号:$selectedIndexes");
}, },
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)))),
], ],
)), )),
), ),

View File

@ -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),
), ),

View File

@ -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)),
), ),
); );
} }

View File

@ -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(

View File

@ -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
], ],

View File

@ -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,19 +48,19 @@ class SkMultiPickerDropdown<T> extends SkBaseFieldWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SkFormItem( return SkFormItem(
child: MultiSelectDropDown<int>( customLabel: customLabel,
labelText: labelText,
isRequired: isRequired,
controller: baseFieldController,
child: MultiSelectDropDown<T>(
controller: multiSelectController,
focusNode: focusNode, focusNode: focusNode,
onOptionSelected: (List<ValueItem> selectedOptions) {}, selectedOptions: selectedOptions ?? [],
options: <ValueItem<int>>[ dropdownHeight: ScreenAdaper.height(300),
ValueItem(label: 'Option 1', value: 1), onOptionSelected: onOptionSelected,
ValueItem(label: 'Option 2', value: 2), options: options ?? <ValueItem<T>>[],
ValueItem(label: 'Option 3', value: 3),
ValueItem(label: 'Option 4', value: 4),
ValueItem(label: 'Option 5', value: 5),
ValueItem(label: 'Option 6', value: 6),
],
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,
@ -71,14 +69,16 @@ class SkMultiPickerDropdown<T> extends SkBaseFieldWidget {
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,
labelStyle: TextStyle(
fontSize: ScreenAdaper.height(26), color: AppTheme.white),
backgroundColor: AppTheme.primaryColor,
),
optionTextStyle: TextStyle(fontSize: ScreenAdaper.height(26)),
selectedOptionIcon: const Icon(Icons.check_circle), selectedOptionIcon: const Icon(Icons.check_circle),
), ),
customLabel: customLabel, );
labelText: labelText,
isRequired: isRequired,
controller: baseFieldController);
// return DropdownButtonFormField<String>( // return DropdownButtonFormField<String>(
// focusNode: focusNode, // focusNode: focusNode,

View File

@ -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,

View File

@ -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:

View File

@ -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