diff --git a/.metadata b/.metadata index b5c370f..777c837 100644 --- a/.metadata +++ b/.metadata @@ -15,24 +15,9 @@ migration: - platform: root create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b - - platform: android - create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b - base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b - - platform: ios - create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b - base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b - - platform: linux - create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b - base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b - - platform: macos - create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b - base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b - platform: web create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b - - platform: windows - create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b - base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b # User provided section diff --git a/lib/apis/api.dart b/lib/apis/api.dart index d2fec8c..8b479c0 100644 --- a/lib/apis/api.dart +++ b/lib/apis/api.dart @@ -33,6 +33,12 @@ Future> getProducts(Map params) { queryParameters: {'page': 1, 'pageSize': 10, ...params}); } +// 分页获取原材料出入库列表 +Future> getInventoryInout(Map params) { + return DioService.dio.get(Urls.getInventoryInout, + queryParameters: {'page': 1, 'pageSize': 10, ...(params)}); +} + Future logout() { return DioService.dio.post( Urls.logout, diff --git a/lib/app_theme.dart b/lib/app_theme.dart index 0724b50..a5adb22 100644 --- a/lib/app_theme.dart +++ b/lib/app_theme.dart @@ -39,7 +39,13 @@ final theme = ThemeData( 800: AppTheme.primaryColorDark, 900: AppTheme.primaryColorDark, }), + primaryColorLight: AppTheme.primaryColorLight, fontFamily: AppTheme.fontName, + textButtonTheme: TextButtonThemeData( + style: ButtonStyle( + foregroundColor: + MaterialStateProperty.all(AppTheme.primaryColor), // 设置文本颜色为红色 + )), datePickerTheme: DatePickerThemeData( confirmButtonStyle: ButtonStyle( textStyle: MaterialStateProperty.resolveWith( @@ -57,7 +63,7 @@ final theme = ThemeData( return null; }, ), - rangeSelectionBackgroundColor: AppTheme.primaryColor, + // rangeSelectionBackgroundColor: AppTheme.primaryColor, dayBackgroundColor: MaterialStateProperty.resolveWith( (Set states) { if (states.contains(MaterialState.selected)) { @@ -91,7 +97,7 @@ final theme = ThemeData( backgroundColor: AppTheme.primaryColor, titleTextStyle: TextStyle( color: Colors.black, - fontSize: ScreenAdaper.sp(25), + fontSize: ScreenAdaper.sp(30), fontWeight: FontWeight.bold), ), inputDecorationTheme: InputDecorationTheme( diff --git a/lib/constants/global_url.dart b/lib/constants/global_url.dart index 70c1ccb..0276a32 100644 --- a/lib/constants/global_url.dart +++ b/lib/constants/global_url.dart @@ -8,5 +8,6 @@ class Urls { static String getUserInfo = 'account/profile'; static String getProjects = 'project'; static String getProducts = 'product'; + static String getInventoryInout = 'materials-in-out'; static String updateAvatar = 'user/updateAvatar'; } diff --git a/lib/screens/inventory_inout/components/custom_app_bar.dart b/lib/screens/inventory_inout/components/custom_app_bar.dart index 5a2c096..5b8b855 100644 --- a/lib/screens/inventory_inout/components/custom_app_bar.dart +++ b/lib/screens/inventory_inout/components/custom_app_bar.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:sk_base_mobile/app_theme.dart'; @@ -5,6 +6,7 @@ import 'package:sk_base_mobile/constants/constants.dart'; import 'package:sk_base_mobile/screens/inventory_inout/inventory_inout_controller.dart'; import 'package:sk_base_mobile/store/auth.store.dart'; import 'package:sk_base_mobile/util/screen_adaper_util.dart'; +import 'package:sk_base_mobile/widgets/core/zt_base_date_picker.dart'; class CustomAppBar extends StatelessWidget { CustomAppBar({super.key}); @@ -44,27 +46,69 @@ class CustomAppBar extends StatelessWidget { const Spacer( flex: 10, ), - Container( - height: ScreenAdaper.height(70), - width: ScreenAdaper.width(70), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - color: AppTheme.primaryColorLight, - boxShadow: [ - BoxShadow( - color: AppTheme.primaryColor, - blurRadius: ScreenAdaper.sp(20), - offset: Offset(0, ScreenAdaper.height(10))) - ]), - child: Icon( - Icons.account_circle_outlined, - size: ScreenAdaper.sp(40), - color: Colors.white, - ), - ), + buildDatePicker(), if (ScreenAdaper.isLandspace()) const Spacer(), ], ), ); } + + Widget buildDatePicker() { + return InkWell( + onTap: () async { + showCupertinoModalPopup( + context: Get.overlayContext!, + builder: (BuildContext context) { + return ZtBaseDatePicker(); + }, + ); + // showCupertinoModalPopup( + // context: Get.overlayContext!, + // builder: (BuildContext context) { + // return Container( + // height: 200, + // color: Colors.white, + // child: CupertinoDatePicker( + // mode: CupertinoDatePickerMode.date, + // initialDateTime: DateTime.now(), + // onDateTimeChanged: (DateTime newDateTime) { + // // 处理用户选择的日期 + // }, + // ), + // ); + // }, + // ); + // final picker = await CupertinoDatePicker( + // context: Get.overlayContext!, + // initialDate: DateTime.now(), + // helpText: '选择的日期', + // confirmText: '确定', + // cancelText: '取消', + // firstDate: DateTime(2000, 1, 1), + // lastDate: DateTime.now().add(const Duration(days: 7))); + }, + child: Container( + height: ScreenAdaper.height(70), + width: ScreenAdaper.width(70), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: AppTheme.primaryColorLight, + gradient: LinearGradient(colors: [ + AppTheme.primaryColorLight, + AppTheme.primaryColor, + ]), + boxShadow: [ + BoxShadow( + color: AppTheme.primaryColor, + blurRadius: ScreenAdaper.sp(20), + offset: Offset(0, ScreenAdaper.height(10))) + ]), + child: Icon( + Icons.calendar_month_outlined, + size: ScreenAdaper.sp(40), + color: Colors.white, + ), + ), + ); + } } diff --git a/lib/screens/inventory_inout/components/date_container.dart b/lib/screens/inventory_inout/components/date_container.dart index 8db307e..d17fa46 100644 --- a/lib/screens/inventory_inout/components/date_container.dart +++ b/lib/screens/inventory_inout/components/date_container.dart @@ -16,7 +16,7 @@ class DateContainer extends StatelessWidget { duration: const Duration(milliseconds: 200), height: ScreenAdaper.height(120), width: ScreenAdaper.width(110), - margin: EdgeInsets.only(left: ScreenAdaper.width(size.width) * 0.05), + margin: EdgeInsets.only(right: ScreenAdaper.width(size.width) * 0.05), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(ScreenAdaper.sp(20)), diff --git a/lib/screens/inventory_inout/components/dates.dart b/lib/screens/inventory_inout/components/dates.dart index 31fa95f..2ed19dc 100644 --- a/lib/screens/inventory_inout/components/dates.dart +++ b/lib/screens/inventory_inout/components/dates.dart @@ -1,5 +1,3 @@ -import 'dart:ui'; - import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:sk_base_mobile/screens/inventory_inout/inventory_inout_controller.dart'; @@ -16,37 +14,37 @@ class Dates extends StatelessWidget { children: [ Obx( () => Text( - DateUtil.getMonth(DateTime.now().add(Duration(days: index))), + DateUtil.getMonth(DateTime.now().add(Duration(days: -index))), style: TextStyle( color: controller.currentIndex.value == index ? Colors.white : Colors.black, fontWeight: FontWeight.bold, - fontSize: ScreenAdaper.sp(16), + fontSize: ScreenAdaper.sp(18), height: 0), ), ), Obx( () => Text( - DateUtil.getDate(DateTime.now().add(Duration(days: index))), + DateUtil.getDate(DateTime.now().add(Duration(days: -index))), style: TextStyle( color: controller.currentIndex.value == index ? Colors.white : Colors.black, fontWeight: FontWeight.bold, - fontSize: 26, + fontSize: ScreenAdaper.sp(30), height: 0), ), ), Obx( () => Text( - DateUtil.getDay(DateTime.now().add(Duration(days: index))), + DateUtil.getDay(DateTime.now().add(Duration(days: -index))), style: TextStyle( color: controller.currentIndex.value == index ? Colors.white : Colors.black, fontWeight: FontWeight.bold, - fontSize: 13), + fontSize: ScreenAdaper.sp(16)), ), ) ], diff --git a/lib/screens/inventory_inout/components/task_list.dart b/lib/screens/inventory_inout/components/task_list.dart index 382358d..e3a495d 100644 --- a/lib/screens/inventory_inout/components/task_list.dart +++ b/lib/screens/inventory_inout/components/task_list.dart @@ -56,6 +56,7 @@ class Grid extends StatelessWidget { ) : GridView.builder( padding: const EdgeInsets.only(top: 40), + reverse: true, itemCount: controller.list[ind].length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: crossAsis, childAspectRatio: ratio), diff --git a/lib/screens/inventory_inout/components/task_page_holder.dart b/lib/screens/inventory_inout/components/task_page_holder.dart index cb83548..6273ac0 100644 --- a/lib/screens/inventory_inout/components/task_page_holder.dart +++ b/lib/screens/inventory_inout/components/task_page_holder.dart @@ -9,32 +9,36 @@ class TaskPageBody extends StatelessWidget { Widget build(BuildContext context) { return Stack( children: [ - Positioned.fill( - child: Container( - margin: EdgeInsets.only(top: ScreenAdaper.height(25)), - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - topRight: Radius.circular(ScreenAdaper.sp(40)), - topLeft: Radius.circular(ScreenAdaper.sp(40))), - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Colors.white.withOpacity(.6), - Colors.white.withOpacity(.5), - Colors.white.withOpacity(.4), - Colors.white.withOpacity(.3), - Colors.white.withOpacity(.2), - Colors.white.withOpacity(.0), - Colors.white.withOpacity(.0), - Colors.white.withOpacity(.0), - Colors.white.withOpacity(.0), - Colors.white.withOpacity(.0), - ])), - child: TaskPageView(), - )), + buildBackground(), ChangeButtonRow(), ], ); } + + Widget buildBackground() { + return Positioned.fill( + child: Container( + margin: EdgeInsets.only(top: ScreenAdaper.height(25)), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topRight: Radius.circular(ScreenAdaper.sp(40)), + topLeft: Radius.circular(ScreenAdaper.sp(40))), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.white.withOpacity(.6), + Colors.white.withOpacity(.5), + Colors.white.withOpacity(.4), + Colors.white.withOpacity(.3), + Colors.white.withOpacity(.2), + Colors.white.withOpacity(.0), + Colors.white.withOpacity(.0), + Colors.white.withOpacity(.0), + Colors.white.withOpacity(.0), + Colors.white.withOpacity(.0), + ])), + child: TaskPageView(), + )); + } } diff --git a/lib/screens/inventory_inout/components/upper_body.dart b/lib/screens/inventory_inout/components/upper_body.dart index 52f4391..65afb6d 100644 --- a/lib/screens/inventory_inout/components/upper_body.dart +++ b/lib/screens/inventory_inout/components/upper_body.dart @@ -21,8 +21,9 @@ class UperBody extends StatelessWidget { height: ScreenAdaper.height(150), child: ListView.builder( controller: controller.scrollController, - itemCount: 7, shrinkWrap: true, + reverse: true, + itemCount: 20, scrollDirection: Axis.horizontal, padding: EdgeInsets.only( bottom: ScreenAdaper.height(20), top: defaultPadding), diff --git a/lib/screens/inventory_inout/inventory_inout_controller.dart b/lib/screens/inventory_inout/inventory_inout_controller.dart index 1c009c1..4ea9ef2 100644 --- a/lib/screens/inventory_inout/inventory_inout_controller.dart +++ b/lib/screens/inventory_inout/inventory_inout_controller.dart @@ -3,11 +3,10 @@ import 'package:get/get.dart'; import 'package:sk_base_mobile/app_theme.dart'; import 'package:sk_base_mobile/db_helper/dbHelper.dart'; import 'package:sk_base_mobile/screens/new_inventory_inout/new_inventory_inout.dart'; -import 'package:sk_base_mobile/screens/new_inventory_inout/new_inventory_inout_controller.dart'; import 'package:sk_base_mobile/util/date.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/apis/api.dart' as Api; import 'components/responsive.dart'; class InventoryInoutController extends GetxController { @@ -192,20 +191,12 @@ class InventoryInoutController extends GetxController { Future showInventoryInoutCreateDialog(String inOrOut) async { final isTablet = Responsive.isTablet(Get.context!); isTablet - ? showGeneralDialog( - context: Get.overlayContext!, + ? Get.generalDialog( barrierLabel: "CreateInventoryInout", barrierDismissible: true, transitionDuration: const Duration(milliseconds: 400), pageBuilder: (_, __, ___) { - return Container( - margin: EdgeInsets.symmetric( - horizontal: ScreenAdaper.width(100), - vertical: ScreenAdaper.height(60)), - child: ClipRRect( - borderRadius: BorderRadius.circular(30), - child: Material(child: NewInventoryInout())), - ); + return NewInventoryInout(); }, transitionBuilder: (_, anim, __, child) { Tween tween; @@ -217,7 +208,7 @@ class InventoryInoutController extends GetxController { child: child, ); }, - ).then((value) => Get.delete()) + ) : showModalBottomSheet( elevation: 0, isScrollControlled: true, @@ -229,6 +220,14 @@ class InventoryInoutController extends GetxController { ); } + Future getInoutHistory() async { + final res = await Api.getInventoryInout({}); + + // final List> queryResult = + // await dbClient!.query('Tasks'); + // return queryResult.map((e) => TaskModel.fromMap(e)).toList(); + } + getTasks() async { db.getData().then((value) { model.value = value; @@ -262,14 +261,14 @@ class InventoryInoutController extends GetxController { } onMoveNextPage() { - if (currentIndex.value < 7) { - setIndex(currentIndex.value + 1); + if (currentIndex.value > 0) { + setIndex(currentIndex.value - 1); } } onMoveBack() { - if (currentIndex.value > 0) { - setIndex(currentIndex.value - 1); + if (currentIndex.value < 20) { + setIndex(currentIndex.value + 1); } } diff --git a/lib/screens/landing/landing.dart b/lib/screens/landing/landing.dart index 1fee953..1b3a244 100644 --- a/lib/screens/landing/landing.dart +++ b/lib/screens/landing/landing.dart @@ -17,6 +17,7 @@ class LandingPage extends StatelessWidget { child: Stack(children: [ const BackColors(), SafeArea( + top: false, child: Obx(() => Scaffold( floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, diff --git a/lib/screens/new_inventory_inout/new_inventory_inout.dart b/lib/screens/new_inventory_inout/new_inventory_inout.dart index 33778d5..507b651 100644 --- a/lib/screens/new_inventory_inout/new_inventory_inout.dart +++ b/lib/screens/new_inventory_inout/new_inventory_inout.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:get/get.dart'; @@ -22,74 +24,32 @@ class NewInventoryInout extends StatelessWidget { @override Widget build(BuildContext context) { - return Container( - height: ScreenAdaper.height(800), - width: Get.width, - decoration: const BoxDecoration( - color: Colors.white, - ), - child: ClipRRect( - borderRadius: BorderRadius.only( - topLeft: Radius.circular( - ScreenAdaper.sp(30), + return SafeArea( + top: false, // 设置为false以避免保留顶部状态栏的空间 + child: Scaffold( + appBar: PreferredSize( + preferredSize: Size.fromHeight(ScreenAdaper.height( + kToolbarHeight)), // 这里使用kToolbarHeight,它是不包含状态栏的AppBar的推荐高度 + child: AppBar( + title: const Text('出入库登记'), ), - topRight: Radius.circular(ScreenAdaper.sp(30))), - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () { - // 点击空白处时收起键盘 - // FocusScope.of(Get.context!).unfocus(); - }, - child: Stack( - children: [ - Positioned.fill( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - buildTitle(), - SingleChildScrollView( - child: buildForm(), - ), - Spacer(), - Obx(() => GradientButton( - onPressed: () => {controller.insertTask(context)}, - isLoading: controller.loading.value, - )) - ], - )) - ], ), - )), - ); - } - - Widget buildTitle() { - return Container( - decoration: const BoxDecoration( - border: Border(bottom: BorderSide(color: AppTheme.dividerColor))), - padding: EdgeInsets.symmetric( - vertical: ScreenAdaper.height(5), horizontal: ScreenAdaper.width(20)), - child: Row( - children: [ - Text( - TextEnum.inventoryInOutDialogTitle, - style: TextStyle( - fontSize: ScreenAdaper.sp(25), - fontWeight: FontWeight.w700, - ), - ), - const Spacer(), - IconButton( - onPressed: () { - Get.back(); - }, - icon: Icon( - Icons.close, - size: ScreenAdaper.sp(30), - )) - ], - ), - ); + resizeToAvoidBottomInset: true, + body: SingleChildScrollView( + child: Container( + decoration: const BoxDecoration( + color: Colors.white, + ), + child: Column( + children: [ + buildForm(), + Obx(() => GradientButton( + onPressed: () => {controller.create()}, + isLoading: controller.loading.value, + )) + ], + ), + )))); } Widget buildForm() { @@ -114,25 +74,27 @@ class NewInventoryInout extends StatelessWidget { ), Row( children: [ + Expanded(flex: 1, child: buildQuantity()), + SizedBox( + width: ScreenAdaper.width(formHorizontalGap), + ), Expanded(flex: 1, child: buildDatePicker()), SizedBox( width: ScreenAdaper.width(formHorizontalGap), ), - Expanded(flex: 1, child: buildQuantity()), + Expanded(flex: 1, child: buildAgent()), ], ), SizedBox( height: ScreenAdaper.height(formVerticalGap), ), - Row( - children: [Expanded(child: buildAgent())], - ), - SizedBox( - height: ScreenAdaper.height(formVerticalGap), - ), Row( children: [Expanded(child: buildRemark())], ), + SizedBox( + height: ScreenAdaper.height(formVerticalGap), + ), + buildImageUploadQueue() ]; final child = Column( @@ -239,8 +201,120 @@ class NewInventoryInout extends StatelessWidget { return ZtTextInput( textController: controller.remarkTextController, labelText: '备注', - isRequired: true, isTextArea: true, ); } + + /// 照片上传 + Widget buildImageUploadQueue() { + return Container( + padding: EdgeInsets.symmetric( + vertical: ScreenAdaper.height(20), + horizontal: ScreenAdaper.width(20)), + alignment: Alignment.center, + child: Column( + children: [ + Text( + '*请拍照上传产品照片', + style: TextStyle( + fontSize: ScreenAdaper.sp(30), + color: AppTheme.secondPrimaryColor), + ), + SizedBox( + height: ScreenAdaper.height(10), + ), + Obx(() => SingleChildScrollView( + controller: controller.uploadScrollController, + scrollDirection: Axis.horizontal, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ...controller.uploadImgFilesPath + .map((String path) => builderImagePreview(path)) + .toList(), + buildImageUploader() + ], + ), + )), + ], + ), + ); + } + + /// 照片预览队列Item + Widget builderImagePreview(String path) { + return Stack( + children: [ + Container( + margin: EdgeInsets.symmetric(horizontal: ScreenAdaper.width(5)), + width: ScreenAdaper.width(200), + height: ScreenAdaper.width(200), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all(width: 1.0, color: AppTheme.dividerColor), + // color: AppTheme.primaryColor, + ), + child: Center( + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + image: DecorationImage( + fit: BoxFit.cover, + image: FileImage(File(path)), + ), + ), + ), + ), + ), + Positioned( + top: ScreenAdaper.height(5), + right: ScreenAdaper.width(5), + child: GestureDetector( + onTap: () { + controller.uploadImgFilesPath.remove(path); + }, + child: Icon( + Icons.close, + shadows: [ + Shadow( + color: Colors.black, + offset: Offset(1, 1), + blurRadius: 3, + ), + Shadow( + color: Colors.black, + offset: Offset(-1, -1), + blurRadius: 3, + ), + ], + size: ScreenAdaper.sp(40), + color: AppTheme.nearlyWhite, + ), + )) + ], + ); + } + + // 上传照片控制器 + Widget buildImageUploader() { + return GestureDetector( + onTap: () { + controller.photoPicker(); + }, + child: Container( + margin: EdgeInsets.symmetric(horizontal: ScreenAdaper.width(5)), + width: ScreenAdaper.width(200), + height: ScreenAdaper.width(200), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all(width: 1.0, color: AppTheme.dividerColor), + // color: AppTheme.primaryColor, + ), + child: Center( + child: Icon( + Icons.add_a_photo_rounded, + size: ScreenAdaper.sp(60), + color: AppTheme.primaryColor, + )))); + } } diff --git a/lib/screens/new_inventory_inout/new_inventory_inout_controller.dart b/lib/screens/new_inventory_inout/new_inventory_inout_controller.dart index 22bbbd9..ac17d05 100644 --- a/lib/screens/new_inventory_inout/new_inventory_inout_controller.dart +++ b/lib/screens/new_inventory_inout/new_inventory_inout_controller.dart @@ -1,7 +1,9 @@ import 'dart:async'; +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:image_picker/image_picker.dart'; import 'package:sk_base_mobile/db_helper/dbHelper.dart'; import 'package:sk_base_mobile/models/base_response.dart'; import 'package:sk_base_mobile/models/product.model.dart'; @@ -10,6 +12,10 @@ import 'package:sk_base_mobile/models/task_model.dart'; import 'package:sk_base_mobile/screens/inventory_inout/inventory_inout_controller.dart'; import 'package:sk_base_mobile/apis/api.dart' as Api; 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/photo_picker_util.dart'; import 'package:sk_base_mobile/util/snack_bar.util.dart'; class NewInventoryInoutController extends GetxController { @@ -38,6 +44,8 @@ class NewInventoryInoutController extends GetxController { final quantityTextController = TextEditingController(); final agentTextController = TextEditingController(); final remarkTextController = TextEditingController(); + final uploadImgFilesPath = [].obs; + final uploadScrollController = ScrollController(); Map payload = {}; Future> getProjects({String? keyword}) async { @@ -60,6 +68,82 @@ class NewInventoryInoutController extends GetxController { return products; } + @override + onReady() { + super.onReady(); + dateTextController.text = DateUtil.format(DateTime.now()); + } + + /// 创建出入库记录 + create() { + if (payload['projectId'] == null) { + SnackBarUtil().info( + '项目不能为空', + ); + return; + } + if (payload['productId'] == null) { + SnackBarUtil().info( + '产品不能为空', + ); + return; + } + if (payload['time'] == null) { + SnackBarUtil().info( + '时间不能为空', + ); + return; + } + payload['quantity'] = quantityTextController.text; + if (payload['quantity'].isEmpty) { + SnackBarUtil().info( + '数量不能为空', + ); + return; + } + payload['agent'] = agentTextController.text; + if (payload['agent'].isEmpty) { + SnackBarUtil().info( + '经办人不能为空', + ); + return; + } + LoggerUtil().info(payload); + } + + Future photoPicker() async { + XFile? pickedFile = await MediaUtil().getImageFromCamera(); + if (pickedFile != null) { + uploadImgFilesPath.add(pickedFile.path); + 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: '上传中...'); + // try { + // String? filename = await MediaUtil().uploadImg(File(pickedFile.path)); + // if (filename.isNotEmpty) { + // final res = await Api.updateAvatar(filename); + // if (res.data != null) { + // SnackBarUtil().success('上传成功'); + // } + // } + // // uploadImgFilePath(pickedFile.path); + // Get.back(); + // } catch (e) { + // SnackBarUtil().error('上传失败,请重试'); + // } finally { + // await LoadingUtil.to.dismiss(); + // } + // }); + } + picStartTime(BuildContext context) async { var picker = await showTimePicker(context: context, initialTime: TimeOfDay.now()); diff --git a/lib/screens/workbench/workbench.dart b/lib/screens/workbench/workbench.dart index 2b0b644..2e467fc 100644 --- a/lib/screens/workbench/workbench.dart +++ b/lib/screens/workbench/workbench.dart @@ -48,25 +48,33 @@ class WorkBenchPage extends StatelessWidget { @override Widget build(BuildContext context) { - return SingleChildScrollView( - child: Column(children: [ - Container( - padding: EdgeInsets.all(ScreenAdaper.width(20)), - child: GridView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: works.length, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: Get.width > 800 ? 5 : 3, - crossAxisSpacing: ScreenAdaper.width(20), - mainAxisSpacing: ScreenAdaper.height(20), - childAspectRatio: 1.0), - itemBuilder: (BuildContext context, int index) { - return buildCard(index); - }, + return Scaffold( + appBar: AppBar( + leading: const SizedBox(), + title: const Text( + '工作台', ), - ) - ])); + ), + body: SingleChildScrollView( + child: Column(children: [ + Container( + padding: EdgeInsets.all(ScreenAdaper.width(20)), + child: GridView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: works.length, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: Get.width > 800 ? 5 : 3, + crossAxisSpacing: ScreenAdaper.width(20), + mainAxisSpacing: ScreenAdaper.height(20), + childAspectRatio: 1.0), + itemBuilder: (BuildContext context, int index) { + return buildCard(index); + }, + ), + ) + ])), + ); } Widget buildCard(int index) { diff --git a/lib/util/photo_picker_util.dart b/lib/util/photo_picker_util.dart index 02c45b5..e13d60f 100644 --- a/lib/util/photo_picker_util.dart +++ b/lib/util/photo_picker_util.dart @@ -7,25 +7,25 @@ import 'package:sk_base_mobile/util/media_util.dart'; import 'package:sk_base_mobile/app_theme.dart'; class PhotoPickerUtil { - showPicker({Function? callback}) async { + showPicker({Function? callback, String title = '请选择上传方式'}) async { return showCupertinoModalPopup( context: Get.context!, builder: (BuildContext ctx) { return CupertinoActionSheet( - title: const Text('Change avatar'), + title: Text(title), cancelButton: CupertinoActionSheetAction( onPressed: () { Get.back(); }, child: const Text( - 'Cancel', + '取消', style: TextStyle(color: Color(0xffcdcdcd)), )), - actions: ['Photography', 'Album'] + actions: ['拍照', '相册'] .map((item) => CupertinoActionSheetAction( onPressed: () async { Get.back(); - XFile? pickedFile = item == 'Photography' + XFile? pickedFile = item == '拍照' ? await MediaUtil().getImageFromCamera() : await MediaUtil().getImageFromGallery(); if (pickedFile != null) { diff --git a/lib/widgets/core/zk_date_picker.dart b/lib/widgets/core/zk_date_picker.dart index 650bfaa..cf7ebfa 100644 --- a/lib/widgets/core/zk_date_picker.dart +++ b/lib/widgets/core/zk_date_picker.dart @@ -8,10 +8,15 @@ class ZtDatePicker extends StatelessWidget { final TextEditingController textController; final VoidCallback? onClear; final Function? onDateSelected; + final bool isRequired; + final String labelText; + const ZtDatePicker({ super.key, this.onClear, this.onDateSelected, + this.labelText = '日期', + this.isRequired = false, required this.textController, }); @@ -41,16 +46,19 @@ class ZtDatePicker extends StatelessWidget { ) : const SizedBox(), hintText: '请选择', - label: const Row( + label: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ + if (isRequired) + Text( + "*", + style: TextStyle( + color: Colors.red, fontSize: ScreenAdaper.sp(30)), + ), Text( - "*", - style: TextStyle(color: Colors.red), - ), - Text( - '日期', + labelText, + style: TextStyle(fontSize: ScreenAdaper.sp(30)), ), ])), keyboardType: TextInputType.none, @@ -63,6 +71,7 @@ class ZtDatePicker extends StatelessWidget { cancelText: '取消', firstDate: DateTime(2000, 1, 1), lastDate: DateTime.now().add(const Duration(days: 7))); + if (onDateSelected != null) { onDateSelected!(picker); } diff --git a/lib/widgets/core/zt_base_date_picker.dart b/lib/widgets/core/zt_base_date_picker.dart new file mode 100644 index 0000000..a81ca25 --- /dev/null +++ b/lib/widgets/core/zt_base_date_picker.dart @@ -0,0 +1,101 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:sk_base_mobile/util/logger_util.dart'; +import 'package:sk_base_mobile/util/screen_adaper_util.dart'; + +class ZtBaseDatePicker extends StatelessWidget { + final Function? onDateTimeChanged; + final yearController = + FixedExtentScrollController(initialItem: DateTime.now().year - 2000); + final monthController = + FixedExtentScrollController(initialItem: DateTime.now().month - 1); + final dayController = + FixedExtentScrollController(initialItem: DateTime.now().day - 1); + ZtBaseDatePicker({super.key, this.onDateTimeChanged}); + + @override + Widget build(BuildContext context) { + return Container( + height: ScreenAdaper.height(250), + color: Colors.white, + child: Column( + children: [ + Row( + children: [ + TextButton( + onPressed: () { + Get.back(); + }, + child: Text( + '取消', + style: TextStyle(fontSize: ScreenAdaper.sp(25)), + )), + const Spacer(), + TextButton( + onPressed: () { + Get.back(); + final year = 2000 + yearController.selectedItem; + String month = '${monthController.selectedItem + 1}'; + if (int.parse(month) < 10) { + month = '0$month'; + } + final day = dayController.selectedItem + 1; + if (onDateTimeChanged != null) { + onDateTimeChanged!('$year-$month-$day'); + } + LoggerUtil().info('$year-$month-$day'); + }, + child: Text( + '确定', + style: TextStyle(fontSize: ScreenAdaper.sp(25)), + )) + ], + ), + Expanded( + child: Row( + children: [ + Expanded( + child: CupertinoPicker( + scrollController: yearController, + itemExtent: ScreenAdaper.height(60), + onSelectedItemChanged: (int index) { + // 处理用户选择的年份 + }, + children: List.generate(DateTime.now().year - 1999, + (int index) { + return Center(child: Text('${2000 + index}年')); + }), + ), + ), + Expanded( + child: CupertinoPicker( + scrollController: monthController, + itemExtent: ScreenAdaper.height(60), + onSelectedItemChanged: (int index) { + // 处理用户选择的月份 + }, + children: List.generate(12, (int index) { + return Center(child: Text('${index + 1}月')); + }), + ), + ), + Expanded( + child: CupertinoPicker( + scrollController: dayController, + itemExtent: ScreenAdaper.height(60), + onSelectedItemChanged: (int index) { + // 处理用户选择的日期 + }, + children: List.generate(31, (int index) { + return Center(child: Text('${index + 1}日')); + }), + ), + ), + ], + )) + ], + ), + ); + } +} diff --git a/lib/widgets/core/zt_number_input.dart b/lib/widgets/core/zt_number_input.dart index 883c13f..c1569de 100644 --- a/lib/widgets/core/zt_number_input.dart +++ b/lib/widgets/core/zt_number_input.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:sk_base_mobile/app_theme.dart'; import 'package:sk_base_mobile/constants/bg_color.dart'; @@ -26,20 +27,28 @@ class ZtNumberInput extends StatelessWidget { FocusScope.of(context).unfocus(); }, onTap: onTap ?? () {}, + textAlign: TextAlign.center, + style: TextStyle(fontSize: ScreenAdaper.sp(30)), keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly // 限制输入内容只能是数字 + ], decoration: InputDecoration( + contentPadding: EdgeInsets.symmetric(vertical: ScreenAdaper.height(18)), floatingLabelBehavior: FloatingLabelBehavior.always, label: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ if (isRequired) - const Text( + Text( "*", - style: TextStyle(color: Colors.red), + style: TextStyle( + color: Colors.red, fontSize: ScreenAdaper.sp(30)), ), Text( labelText, + style: TextStyle(fontSize: ScreenAdaper.sp(30)), ), ]), focusedBorder: OutlineInputBorder( diff --git a/lib/widgets/core/zt_search_select.dart b/lib/widgets/core/zt_search_select.dart index bdb0813..b151159 100644 --- a/lib/widgets/core/zt_search_select.dart +++ b/lib/widgets/core/zt_search_select.dart @@ -67,12 +67,15 @@ class ZtSearchSelect extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ if (isRequired) - const Text( + Text( "*", - style: TextStyle(color: Colors.red), + style: TextStyle( + color: Colors.red, + fontSize: ScreenAdaper.sp(30)), ), Text( labelText, + style: TextStyle(fontSize: ScreenAdaper.sp(30)), ), ]))); }, diff --git a/lib/widgets/core/zt_text_input.dart b/lib/widgets/core/zt_text_input.dart index 34d6c6d..9147924 100644 --- a/lib/widgets/core/zt_text_input.dart +++ b/lib/widgets/core/zt_text_input.dart @@ -27,7 +27,7 @@ class ZtTextInput extends StatelessWidget { onTapOutside: (event) { FocusScope.of(context).unfocus(); }, - maxLines: isTextArea ? null : 1, // 添加这行代码 + maxLines: isTextArea ? 3 : 1, // 添加这行代码 onTap: onTap ?? () {}, decoration: InputDecoration( floatingLabelBehavior: FloatingLabelBehavior.always, @@ -36,12 +36,14 @@ class ZtTextInput extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ if (isRequired) - const Text( + Text( "*", - style: TextStyle(color: Colors.red), + style: TextStyle( + color: Colors.red, fontSize: ScreenAdaper.sp(30)), ), Text( labelText, + style: TextStyle(fontSize: ScreenAdaper.sp(30)), ), ]), focusedBorder: OutlineInputBorder( diff --git a/lib/widgets/gradient_button.dart b/lib/widgets/gradient_button.dart index a1c9e25..a41ab56 100644 --- a/lib/widgets/gradient_button.dart +++ b/lib/widgets/gradient_button.dart @@ -41,7 +41,7 @@ class GradientButton extends StatelessWidget { : Text( buttonText, style: TextStyle( - color: Colors.white, + color: AppTheme.nearlyWhite, fontWeight: FontWeight.bold, fontSize: ScreenAdaper.sp(25), ), diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..d5ca9de --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:sk_base_mobile/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/web/favicon.png b/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/web/favicon.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/web/icons/Icon-512.png differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000..eb9b4d7 Binary files /dev/null and b/web/icons/Icon-maskable-192.png differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000..d69c566 Binary files /dev/null and b/web/icons/Icon-maskable-512.png differ diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..727ccde --- /dev/null +++ b/web/index.html @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + sk_base_mobile + + + + + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 0000000..51268b7 --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "sk_base_mobile", + "short_name": "sk_base_mobile", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +}