feat: project filter for inventory management
This commit is contained in:
parent
88be83aceb
commit
8a06486f93
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/widgets.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sk_base_mobile/util/snack_bar.util.dart';
|
||||
import '../../store/auth.store.dart';
|
||||
// import 'package:sentry/sentry.dart';
|
||||
|
||||
|
@ -11,10 +12,10 @@ class LoginController extends GetxController {
|
|||
String password = '';
|
||||
bool loading = false;
|
||||
Future<void> doLogin() async {
|
||||
if (!formKey.currentState!.validate()) {
|
||||
if (username.isEmpty || password.isEmpty) {
|
||||
SnackBarUtil().warning('请填写用户名和密码');
|
||||
return;
|
||||
}
|
||||
|
||||
// 拿出form中的数据
|
||||
AuthStore.to.login(username: username, password: password);
|
||||
}
|
||||
|
|
|
@ -180,11 +180,6 @@ class LoginScreen extends StatelessWidget {
|
|||
Icons.person_2_outlined,
|
||||
size: ScreenAdaper.height(40),
|
||||
),
|
||||
errorStyle: TextStyle(fontSize: ScreenAdaper.height(20)),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
vertical: ScreenAdaper.height(10),
|
||||
horizontal: ScreenAdaper.width(30),
|
||||
),
|
||||
hintText: '用户名',
|
||||
hintStyle: TextStyle(fontSize: ScreenAdaper.height(25)),
|
||||
border: InputBorder.none,
|
||||
|
@ -197,7 +192,6 @@ class LoginScreen extends StatelessWidget {
|
|||
onChanged: (value) {
|
||||
_controller.username = value;
|
||||
},
|
||||
validator: (value) => value!.isEmpty ? '用户名不能为空' : null,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -225,7 +219,6 @@ class LoginScreen extends StatelessWidget {
|
|||
_controller.password = value;
|
||||
},
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
validator: (value) => value!.isEmpty ? '密码不能为空' : null,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import 'package:sk_base_mobile/models/index.dart';
|
|||
import 'package:sk_base_mobile/models/inventory.model.dart';
|
||||
import 'package:sk_base_mobile/util/debouncer.dart';
|
||||
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||||
import 'package:sk_base_mobile/widgets/core/zt_search_select.dart';
|
||||
import 'package:sk_base_mobile/widgets/empty.dart';
|
||||
|
||||
class InventorySearch extends StatelessWidget {
|
||||
|
@ -23,19 +24,23 @@ class InventorySearch extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: ScreenAdaper.width(20),
|
||||
vertical: ScreenAdaper.height(20)),
|
||||
child: Column(
|
||||
children: [
|
||||
buildSearchBar(),
|
||||
SizedBox(
|
||||
height: ScreenAdaper.height(defaultPadding) / 2,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: ScreenAdaper.width(20),
|
||||
vertical: ScreenAdaper.height(20)),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
buildSearchBar(),
|
||||
SizedBox(
|
||||
height: ScreenAdaper.height(defaultPadding) / 2,
|
||||
),
|
||||
Expanded(child: buildInventoryList())
|
||||
],
|
||||
),
|
||||
Expanded(child: buildInventoryList())
|
||||
],
|
||||
),
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
Widget buildSearchBar() {
|
||||
|
@ -74,13 +79,76 @@ class InventorySearch extends StatelessWidget {
|
|||
),
|
||||
))),
|
||||
SizedBox(
|
||||
width: ScreenAdaper.width(20),
|
||||
width: ScreenAdaper.width(5),
|
||||
),
|
||||
buildProjectPicker(),
|
||||
SizedBox(
|
||||
width: ScreenAdaper.width(5),
|
||||
),
|
||||
buildHasInventoryPicker()
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// 项目选择
|
||||
Widget buildProjectPicker() {
|
||||
return Container(
|
||||
width: ScreenAdaper.width(250),
|
||||
constraints: BoxConstraints(minWidth: ScreenAdaper.width(250)),
|
||||
child: Obx(
|
||||
() => DropdownButtonFormField<int>(
|
||||
value: controller.projectId.value,
|
||||
isExpanded: true,
|
||||
items: [
|
||||
DropdownMenuItem(
|
||||
child: Text('所有项目'),
|
||||
value: 0,
|
||||
),
|
||||
...controller.projects.map((element) => DropdownMenuItem(
|
||||
child: Text('${element.name}'), value: element.id))
|
||||
],
|
||||
onChanged: (value) {
|
||||
controller.projectId.value = value;
|
||||
controller.refreshController.requestRefresh();
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
vertical: ScreenAdaper.height(15),
|
||||
horizontal: ScreenAdaper.width(15)),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15)),
|
||||
),
|
||||
)),
|
||||
));
|
||||
// return Container(
|
||||
// width: ScreenAdaper.width(200),
|
||||
// constraints: BoxConstraints(minWidth: ScreenAdaper.width(200)),
|
||||
// child: ZtSearchSelect<ProjectModel>(
|
||||
// isRequired: true,
|
||||
// contentPadding:
|
||||
// EdgeInsets.symmetric(horizontal: ScreenAdaper.width(15)),
|
||||
// textController: controller.projectTextController,
|
||||
// hintText: '请选择所属项目',
|
||||
// itemBuilder: (_, itemData) => Container(
|
||||
// padding: EdgeInsets.symmetric(
|
||||
// vertical: ScreenAdaper.height(15),
|
||||
// horizontal: ScreenAdaper.width(20)),
|
||||
// child: Text(
|
||||
// '${itemData.name}',
|
||||
// style: TextStyle(fontSize: ScreenAdaper.height(20)),
|
||||
// )),
|
||||
// suggestionsCallback: (String keyword) {
|
||||
// return controller.getProjects(keyword: keyword);
|
||||
// },
|
||||
// onClear: () {
|
||||
// controller.projectId = null;
|
||||
// },
|
||||
// onSelected: (ProjectModel project) {
|
||||
// controller.projectTextController.text = project.name ?? '';
|
||||
// controller.projectId = project.id;
|
||||
// }));
|
||||
}
|
||||
|
||||
Widget buildHasInventoryPicker() {
|
||||
return Container(
|
||||
width: ScreenAdaper.width(200),
|
||||
|
@ -134,14 +202,19 @@ class InventorySearch extends StatelessWidget {
|
|||
: Table(columnWidths: {
|
||||
0: MinColumnWidth(
|
||||
FixedColumnWidth(80), FixedColumnWidth(80)),
|
||||
1: FlexColumnWidth(ScreenAdaper.screenShortDistance() / 4),
|
||||
2: FlexColumnWidth(ScreenAdaper.screenShortDistance() / 4),
|
||||
3: MinColumnWidth(
|
||||
1: MinColumnWidth(
|
||||
FixedColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 5),
|
||||
FixedColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 5)),
|
||||
2: FlexColumnWidth(ScreenAdaper.screenShortDistance() / 4),
|
||||
3: FlexColumnWidth(ScreenAdaper.screenShortDistance() / 4),
|
||||
4: MinColumnWidth(
|
||||
FixedColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 5),
|
||||
FixedColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 5)),
|
||||
5: MinColumnWidth(
|
||||
FixedColumnWidth(
|
||||
ScreenAdaper.screenShortDistance() / 6),
|
||||
FixedColumnWidth(
|
||||
|
@ -165,6 +238,13 @@ class InventorySearch extends StatelessWidget {
|
|||
style: listTitleTextStyle,
|
||||
),
|
||||
)),
|
||||
TableCell(
|
||||
verticalAlignment:
|
||||
TableCellVerticalAlignment.middle,
|
||||
child: Text(
|
||||
'所属项目',
|
||||
style: listTitleTextStyle,
|
||||
)),
|
||||
TableCell(
|
||||
verticalAlignment:
|
||||
TableCellVerticalAlignment.middle,
|
||||
|
@ -211,6 +291,21 @@ class InventorySearch extends StatelessWidget {
|
|||
style: textStyle,
|
||||
),
|
||||
itemData: itemData),
|
||||
|
||||
// 入库时所属项目
|
||||
buildTableCell(
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${itemData.project?.name}',
|
||||
style: textStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
itemData: itemData),
|
||||
// 产品
|
||||
buildTableCell(
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
@ -282,6 +377,7 @@ class InventorySearch extends StatelessWidget {
|
|||
|
||||
class InventorySearchController extends GetxController {
|
||||
RxList<InventoryModel> inventories = RxList([]);
|
||||
final projectTextController = TextEditingController();
|
||||
RxString searchKey = ''.obs;
|
||||
final searchBarTextConroller = TextEditingController();
|
||||
RefreshController refreshController = RefreshController(initialRefresh: true);
|
||||
|
@ -289,6 +385,14 @@ class InventorySearchController extends GetxController {
|
|||
int limit = 15;
|
||||
int total = 0;
|
||||
RxInt hasInventoryStatus = RxInt(1);
|
||||
RxList<ProjectModel> projects = <ProjectModel>[].obs;
|
||||
RxnInt projectId = RxnInt(0);
|
||||
@override
|
||||
onReady() {
|
||||
super.onReady();
|
||||
getProjects();
|
||||
}
|
||||
|
||||
Future<List<InventoryModel>> getData({bool isRefresh = false}) async {
|
||||
if (isRefresh == true) {
|
||||
page = 1;
|
||||
|
@ -299,6 +403,7 @@ class InventorySearchController extends GetxController {
|
|||
'page': page,
|
||||
'pageSize': 15,
|
||||
'keyword': searchKey.value,
|
||||
'projectId': projectId.value == 0 ? null : projectId.value,
|
||||
'isCreateInout': true,
|
||||
'isHasInventory': hasInventoryStatus.value,
|
||||
});
|
||||
|
@ -311,6 +416,16 @@ class InventorySearchController extends GetxController {
|
|||
return newList;
|
||||
}
|
||||
|
||||
Future<List<ProjectModel>> getProjects({String? keyword}) async {
|
||||
final res =
|
||||
await Api.getProjects({'page': 1, 'pageSize': 10, 'name': keyword});
|
||||
if (res.data != null) {
|
||||
projects.assignAll(
|
||||
res.data!.items.map((e) => ProjectModel.fromJson(e)).toList());
|
||||
}
|
||||
return projects;
|
||||
}
|
||||
|
||||
Future<void> onRefresh() async {
|
||||
await getData(isRefresh: true).then((_) {
|
||||
refreshController.refreshCompleted(resetFooterState: true);
|
||||
|
|
|
@ -49,11 +49,10 @@ class NewInventoryInout extends StatelessWidget {
|
|||
child: Column(
|
||||
children: [
|
||||
buildForm(),
|
||||
Obx(() => GradientButton(
|
||||
buttonText: TextEnum.createInventoryInOutBtnText,
|
||||
onPressed: () => {controller.create()},
|
||||
isLoading: controller.loading.value,
|
||||
))
|
||||
GradientButton(
|
||||
buttonText: TextEnum.createInventoryInOutBtnText,
|
||||
onPressed: () => {controller.create()},
|
||||
)
|
||||
],
|
||||
),
|
||||
))));
|
||||
|
@ -455,15 +454,14 @@ class NewInventoryInout extends StatelessWidget {
|
|||
height: ScreenAdaper.height(10),
|
||||
),
|
||||
Obx(() => SingleChildScrollView(
|
||||
controller: controller.uploadScrollController,
|
||||
controller: controller.productUploadScrollController,
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
...controller.uploadProductImgFilesPath
|
||||
.map((String path) =>
|
||||
builderImagePreview(path, 'product'))
|
||||
.toList(),
|
||||
...controller.uploadProductImgFilesPath.map(
|
||||
(String path) =>
|
||||
builderImagePreview(path, 'product')),
|
||||
buildImageUploader('product')
|
||||
],
|
||||
),
|
||||
|
@ -483,15 +481,13 @@ class NewInventoryInout extends StatelessWidget {
|
|||
height: ScreenAdaper.height(10),
|
||||
),
|
||||
Obx(() => SingleChildScrollView(
|
||||
controller: controller.uploadScrollController,
|
||||
controller: controller.agentUploadScrollController,
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
...controller.uploadAgentImgFilesPath
|
||||
.map((String path) =>
|
||||
builderImagePreview(path, 'agent'))
|
||||
.toList(),
|
||||
...controller.uploadAgentImgFilesPath.map(
|
||||
(String path) => builderImagePreview(path, 'agent')),
|
||||
buildImageUploader('agent')
|
||||
],
|
||||
),
|
||||
|
|
|
@ -10,6 +10,7 @@ import 'package:sk_base_mobile/db_helper/dbHelper.dart';
|
|||
import 'package:sk_base_mobile/models/index.dart';
|
||||
import 'package:sk_base_mobile/screens/inventory_inout/inventory_inout_controller.dart';
|
||||
import 'package:sk_base_mobile/util/date.util.dart';
|
||||
import 'package:sk_base_mobile/util/loading_util.dart';
|
||||
import 'package:sk_base_mobile/util/logger_util.dart';
|
||||
import 'package:sk_base_mobile/util/media_util.dart';
|
||||
import 'package:sk_base_mobile/util/snack_bar.util.dart';
|
||||
|
@ -27,7 +28,6 @@ class NewInventoryInoutController extends GetxController {
|
|||
RxString selectedDate = ''.obs;
|
||||
RxString startTime = ''.obs;
|
||||
RxString endTime = ''.obs;
|
||||
RxBool loading = false.obs;
|
||||
final inventoryInoutController = Get.find<InventoryInoutController>();
|
||||
final label = TextEditingController().obs;
|
||||
final description = TextEditingController().obs;
|
||||
|
@ -46,7 +46,8 @@ class NewInventoryInoutController extends GetxController {
|
|||
final uploadProductImgFilesPath = <String>[].obs;
|
||||
final uploadAgentImgFilesPath = <String>[].obs;
|
||||
// final uploadImgFilesPath = <String>[].obs
|
||||
final uploadScrollController = ScrollController();
|
||||
final productUploadScrollController = ScrollController();
|
||||
final agentUploadScrollController = ScrollController();
|
||||
Map<String, dynamic> payload = {};
|
||||
NewInventoryInoutController({this.inOrOut});
|
||||
|
||||
|
@ -171,7 +172,7 @@ class NewInventoryInoutController extends GetxController {
|
|||
}
|
||||
|
||||
payload['remark'] = remarkTextController.text;
|
||||
loading.value = true;
|
||||
await LoadingUtil.to.show(status: '提交中请稍后...');
|
||||
// uploadImgFilesPath
|
||||
|
||||
try {
|
||||
|
@ -202,7 +203,7 @@ class NewInventoryInoutController extends GetxController {
|
|||
} catch (e) {
|
||||
LoggerUtil().error(e);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
LoadingUtil.to.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,17 +213,25 @@ class NewInventoryInoutController extends GetxController {
|
|||
if (pickedFile != null) {
|
||||
if (type == 'product') {
|
||||
uploadProductImgFilesPath.add(pickedFile.path);
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
// 滚动到最右边
|
||||
productUploadScrollController.animateTo(
|
||||
productUploadScrollController.position.maxScrollExtent,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
});
|
||||
} else {
|
||||
uploadAgentImgFilesPath.add(pickedFile.path);
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
// 滚动到最右边
|
||||
agentUploadScrollController.animateTo(
|
||||
agentUploadScrollController.position.maxScrollExtent,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
});
|
||||
}
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
// 滚动到最右边
|
||||
uploadScrollController.animateTo(
|
||||
uploadScrollController.position.maxScrollExtent,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
});
|
||||
}
|
||||
// await PhotoPickerUtil().showPicker(callback: (XFile pickedFile) async {
|
||||
// await LoadingUtil.to.show(status: '上传中...');
|
||||
|
@ -331,7 +340,6 @@ class NewInventoryInoutController extends GetxController {
|
|||
picEndTime(context);
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
|
||||
// db
|
||||
// .insert(TaskModel(
|
||||
|
|
|
@ -43,8 +43,9 @@ class LoadingUtil extends GetxService {
|
|||
if (status != null)
|
||||
Text(status,
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.none,
|
||||
color: AppTheme.primaryColor,
|
||||
fontSize: ScreenAdaper.height(20)))
|
||||
fontSize: ScreenAdaper.height(25)))
|
||||
],
|
||||
),
|
||||
)
|
||||
|
|
|
@ -9,26 +9,27 @@ class ZtSearchSelect<T> extends StatelessWidget {
|
|||
final Function? suggestionsCallback;
|
||||
final void Function(T)? onSelected;
|
||||
final VoidCallback? onClear;
|
||||
final String labelText;
|
||||
final String? labelText;
|
||||
final String hintText;
|
||||
|
||||
final EdgeInsetsGeometry? contentPadding;
|
||||
final bool isRequired;
|
||||
final Widget Function(BuildContext, T) itemBuilder;
|
||||
const ZtSearchSelect(
|
||||
{super.key,
|
||||
required this.textController,
|
||||
required this.labelText,
|
||||
required this.itemBuilder,
|
||||
required this.suggestionsCallback,
|
||||
this.hintText = '请选择',
|
||||
this.onSelected,
|
||||
this.labelText,
|
||||
this.onClear,
|
||||
this.contentPadding,
|
||||
this.isRequired = false});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TypeAheadField(
|
||||
hideOnUnfocus: false,
|
||||
hideOnUnfocus: true,
|
||||
controller: textController,
|
||||
suggestionsCallback: (String keyword) {
|
||||
return suggestionsCallback != null
|
||||
|
@ -44,6 +45,7 @@ class ZtSearchSelect<T> extends StatelessWidget {
|
|||
focusNode: focusNode,
|
||||
controller: _,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: contentPadding,
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(
|
||||
color: AppTheme.primaryColorLight, width: 2),
|
||||
|
@ -59,25 +61,28 @@ class ZtSearchSelect<T> extends StatelessWidget {
|
|||
}
|
||||
},
|
||||
)
|
||||
: const SizedBox(),
|
||||
: null,
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
hintText: hintText,
|
||||
label: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (isRequired)
|
||||
Text(
|
||||
"*",
|
||||
style: TextStyle(
|
||||
color: Colors.red,
|
||||
fontSize: ScreenAdaper.height(30)),
|
||||
),
|
||||
Text(
|
||||
labelText,
|
||||
style: TextStyle(fontSize: ScreenAdaper.height(30)),
|
||||
),
|
||||
])));
|
||||
label: labelText != null
|
||||
? Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (isRequired)
|
||||
Text(
|
||||
"*",
|
||||
style: TextStyle(
|
||||
color: Colors.red,
|
||||
fontSize: ScreenAdaper.height(30)),
|
||||
),
|
||||
Text(
|
||||
labelText ?? '',
|
||||
style: TextStyle(
|
||||
fontSize: ScreenAdaper.height(30)),
|
||||
),
|
||||
])
|
||||
: SizedBox()));
|
||||
},
|
||||
emptyBuilder: (_) => Container(
|
||||
alignment: Alignment.center,
|
||||
|
|
Loading…
Reference in New Issue