From cf51cd8ada8daa175c8e4db13aec6b581b0d27df Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Tue, 26 Mar 2024 15:30:43 +0800 Subject: [PATCH] feat: inventory and inout info develop --- lib/apis/api.dart | 99 ------- lib/apis/index.dart | 110 +++++++ lib/constants/global_url.dart | 4 +- lib/models/inventory_inout.model.dart | 10 +- lib/screens/inventory/inventory.dart | 14 + .../components/inventory_inout_card.dart | 273 +++++++++--------- .../components/inventory_inout_info.dart | 190 ++++++++++++ .../inventory_inout_controller.dart | 11 +- lib/screens/landing/landing.dart | 4 +- .../mine/useinfo/userinfo.controller.dart | 2 +- .../components/inventory_search.dart | 2 +- .../components/product_search.dart | 2 +- .../new_inventory_inout.dart | 57 ++-- .../new_inventory_inout_controller.dart | 2 +- lib/services/app_info.service.dart | 5 +- lib/services/dio.service.dart | 2 +- lib/store/auth.store.dart | 3 +- lib/store/dict.store.dart | 2 +- lib/util/loading_util.dart | 8 +- lib/util/media_util.dart | 4 +- lib/util/modal.util.dart | 4 +- lib/widgets/image_preview.dart | 112 +++++++ lib/widgets/loading_indicator.dart | 18 +- lib/widgets/my_avatar.dart | 2 +- pubspec.lock | 16 + pubspec.yaml | 2 + 26 files changed, 666 insertions(+), 292 deletions(-) delete mode 100644 lib/apis/api.dart create mode 100644 lib/apis/index.dart create mode 100644 lib/screens/inventory/inventory.dart create mode 100644 lib/screens/inventory_inout/components/inventory_inout_info.dart create mode 100644 lib/widgets/image_preview.dart diff --git a/lib/apis/api.dart b/lib/apis/api.dart deleted file mode 100644 index f759aa3..0000000 --- a/lib/apis/api.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'dart:async'; -import 'dart:io'; -import 'package:dio/dio.dart'; -import 'package:sk_base_mobile/constants/global_url.dart'; -import 'package:sk_base_mobile/models/base_response.dart'; -import 'package:sk_base_mobile/models/dict_type.model.dart'; -import 'package:sk_base_mobile/services/dio.service.dart'; -import '../constants/constants.dart'; - -// 登录 -Future login(String username, String password) { - return DioService.dio.post( - Urls.login, - data: {'username': username, 'password': password}, - ); -} - -// 获取个人信息 -Future getUserInfo() { - return DioService.dio.get( - Urls.getUserInfo, - ); -} - -// 分页获取项目列表 -Future> getProjects(Map params) { - return DioService.dio.get(Urls.getProjects, - queryParameters: {'page': 1, 'pageSize': 10, ...params}); -} - -// 分页获取产品列表 -Future> getProducts(Map params) { - return DioService.dio.get(Urls.getProducts, - queryParameters: {'page': 1, 'pageSize': 10, ...params}); -} - -// 分页获取原材料出入库列表 -Future> getInventoryInout(Map params) { - return DioService.dio.get(Urls.inventoryInout, - queryParameters: {'page': 1, 'pageSize': 10, ...(params)}); -} - -// 创建出入库记录 -Future createInventoryInout(Map params) { - return DioService.dio.post(Urls.inventoryInout, data: params); -} - -// 更新出入库记录 -Future updateInventoryInout(int id, Map params) { - return DioService.dio.put('${Urls.inventoryInout}/$id', data: params); -} - -// 分页获取库存列表 -Future> getInventory(Map params) { - return DioService.dio.get(Urls.inventory, - queryParameters: {'page': 1, 'pageSize': 10, ...(params)}); -} - -// 一次性获取所有的字典类型(不分页) -Future getDictTypeAll(Map params) { - return DioService.dio.post(Urls.getDictType, data: params); -} - -Future logout() { - return DioService.dio.post( - Urls.logout, - ); -} - -Future deleteAccount() { - return DioService.dio.post( - Urls.deleteAccount, - ); -} - -Future getAppConfig() { - Map query = {'ver': 0}; - return DioService.dio.get(Urls.getAppConfig, queryParameters: query); -} - -Future saveUserInfo(Map data) { - return DioService.dio.post(Urls.saveUserInfo, data: data); -} - -Future uploadImg(File file, - {String? bussinessModule, String? bussinessRecordId}) async { - String fileName = file.path.split('/').last; - final filePath = await MultipartFile.fromFile(file.path, filename: fileName); - final formData = FormData.fromMap({ - 'bussinessRecordId': bussinessRecordId, - 'bussinessModule': bussinessModule, - 'file': filePath, - }); - return DioService.dio.post(Urls.uploadAttachemnt, data: formData); -} - -Future updateAvatar(String filename) { - return DioService.dio.post(Urls.updateAvatar, data: {'avatarPath': filename}); -} diff --git a/lib/apis/index.dart b/lib/apis/index.dart new file mode 100644 index 0000000..fba6c7d --- /dev/null +++ b/lib/apis/index.dart @@ -0,0 +1,110 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:dio/dio.dart'; +import 'package:sk_base_mobile/constants/global_url.dart'; +import 'package:sk_base_mobile/models/base_response.dart'; +import 'package:sk_base_mobile/models/dict_type.model.dart'; +import 'package:sk_base_mobile/services/dio.service.dart'; +import '../constants/constants.dart'; + +class Api { +// 登录 + static Future login(String username, String password) { + return DioService.dio.post( + Urls.login, + data: {'username': username, 'password': password}, + ); + } + +// 获取个人信息 + static Future getUserInfo() { + return DioService.dio.get( + Urls.getUserInfo, + ); + } + +// 分页获取项目列表 + static Future> getProjects(Map params) { + return DioService.dio.get(Urls.projects, + queryParameters: {'page': 1, 'pageSize': 10, ...params}); + } + +// 分页获取产品列表 + static Future> getProducts(Map params) { + return DioService.dio.get(Urls.products, + queryParameters: {'page': 1, 'pageSize': 10, ...params}); + } + +// 分页获取原材料出入库列表 + static Future> getInventoryInout(Map params) { + return DioService.dio.get(Urls.inventoryInout, + queryParameters: {'page': 1, 'pageSize': 10, ...(params)}); + } + +// 获取原材料出入库详情 + static Future getInventoryInOutInfo(int id) { + return DioService.dio.get( + '${Urls.inventoryInout}/$id', + ); + } + +// 创建出入库记录 + static Future createInventoryInout(Map params) { + return DioService.dio.post(Urls.inventoryInout, data: params); + } + +// 更新出入库记录 + static Future updateInventoryInout(int id, Map params) { + return DioService.dio.put('${Urls.inventoryInout}/$id', data: params); + } + +// 分页获取库存列表 + static Future> getInventory(Map params) { + return DioService.dio.get(Urls.inventory, + queryParameters: {'page': 1, 'pageSize': 10, ...(params)}); + } + +// 一次性获取所有的字典类型(不分页) + static Future getDictTypeAll(Map params) { + return DioService.dio.post(Urls.getDictType, data: params); + } + + static Future logout() { + return DioService.dio.post( + Urls.logout, + ); + } + + static Future deleteAccount() { + return DioService.dio.post( + Urls.deleteAccount, + ); + } + + static Future getAppConfig() { + Map query = {'ver': 0}; + return DioService.dio.get(Urls.getAppConfig, queryParameters: query); + } + + static Future saveUserInfo(Map data) { + return DioService.dio.post(Urls.saveUserInfo, data: data); + } + + static Future uploadImg(File file, + {String? bussinessModule, String? bussinessRecordId}) async { + String fileName = file.path.split('/').last; + final filePath = + await MultipartFile.fromFile(file.path, filename: fileName); + final formData = FormData.fromMap({ + 'bussinessRecordId': bussinessRecordId, + 'bussinessModule': bussinessModule, + 'file': filePath, + }); + return DioService.dio.post(Urls.uploadAttachemnt, data: formData); + } + + static Future updateAvatar(String filename) { + return DioService.dio + .post(Urls.updateAvatar, data: {'avatarPath': filename}); + } +} diff --git a/lib/constants/global_url.dart b/lib/constants/global_url.dart index 993c508..76f7522 100644 --- a/lib/constants/global_url.dart +++ b/lib/constants/global_url.dart @@ -6,8 +6,8 @@ class Urls { static String deleteAccount = 'user/deleteAccount'; static String saveUserInfo = 'user/saveUserInfo'; static String getUserInfo = 'account/profile'; - static String getProjects = 'project'; - static String getProducts = 'product'; + static String projects = 'project'; + static String products = 'product'; static String inventoryInout = 'materials-in-out'; static String inventory = 'materials-inventory'; static String updateAvatar = 'user/updateAvatar'; diff --git a/lib/models/inventory_inout.model.dart b/lib/models/inventory_inout.model.dart index c4d29a2..c8809e2 100644 --- a/lib/models/inventory_inout.model.dart +++ b/lib/models/inventory_inout.model.dart @@ -1,6 +1,7 @@ import 'package:sk_base_mobile/constants/enum.dart'; import 'package:sk_base_mobile/models/file.model.dart'; import 'package:sk_base_mobile/models/index.dart'; +import 'package:sk_base_mobile/models/inventory.model.dart'; import 'package:sk_base_mobile/models/product.model.dart'; import 'package:sk_base_mobile/models/project.model.dart'; @@ -24,6 +25,7 @@ class InventoryInOutModel { required this.files, required this.project, required this.product, + this.inventory, }); final int? id; @@ -41,10 +43,10 @@ class InventoryInOutModel { final String? remark; final int? projectId; final int? isDelete; - final List files; + final List files; final ProjectModel? project; final ProductModel? product; - + final InventoryModel? inventory; factory InventoryInOutModel.fromJson(Map json) { return InventoryInOutModel( id: json["id"], @@ -72,6 +74,9 @@ class InventoryInOutModel { product: json["product"] == null ? null : ProductModel.fromJson(json["product"]), + inventory: json['inventory'] == null + ? null + : InventoryModel.fromJson(json["inventory"]), ); } @@ -94,5 +99,6 @@ class InventoryInOutModel { "files": files.map((x) => x).toList(), "project": project?.toJson(), "product": product?.toJson(), + "inventory": inventory?.toJson(), }; } diff --git a/lib/screens/inventory/inventory.dart b/lib/screens/inventory/inventory.dart new file mode 100644 index 0000000..1ffa79c --- /dev/null +++ b/lib/screens/inventory/inventory.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; +import 'package:sk_base_mobile/screens/new_inventory_inout/components/inventory_search.dart'; + +class InventoryPage extends StatelessWidget { + const InventoryPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('库存管理')), + body: InventorySearch(), + ); + } +} diff --git a/lib/screens/inventory_inout/components/inventory_inout_card.dart b/lib/screens/inventory_inout/components/inventory_inout_card.dart index 2e69563..95a1efc 100644 --- a/lib/screens/inventory_inout/components/inventory_inout_card.dart +++ b/lib/screens/inventory_inout/components/inventory_inout_card.dart @@ -16,138 +16,144 @@ class InventoryInoutCard extends StatelessWidget { final controller = Get.find(); @override Widget build(BuildContext context) { - return Container( - margin: EdgeInsets.symmetric( - vertical: ScreenAdaper.height(15), - horizontal: ScreenAdaper.width(15)), - padding: EdgeInsets.symmetric( - horizontal: ScreenAdaper.width(defaultPadding), - vertical: ScreenAdaper.height(defaultPadding)), - decoration: BoxDecoration( - boxShadow: [ - BoxShadow( - color: AppTheme.barrierColor.withOpacity(0.2), - offset: Offset(0, ScreenAdaper.height(5)), - blurRadius: ScreenAdaper.sp(10)), - ], - color: AppTheme.nearlyWhite, - borderRadius: BorderRadius.circular(ScreenAdaper.sp(30))), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - buildImage(), - Expanded(child: buildContent()), + return InkWell( + onTap: () { + controller + .showInventoryInoutInfoDialog(controller.list[ind][index].id!); + }, + child: Container( + margin: EdgeInsets.symmetric( + vertical: ScreenAdaper.height(15), + horizontal: ScreenAdaper.width(15)), + padding: EdgeInsets.symmetric( + horizontal: ScreenAdaper.width(defaultPadding), + vertical: ScreenAdaper.height(defaultPadding)), + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: AppTheme.barrierColor.withOpacity(0.2), + offset: Offset(0, ScreenAdaper.height(5)), + blurRadius: ScreenAdaper.sp(10)), + ], + color: AppTheme.nearlyWhite, + borderRadius: BorderRadius.circular(ScreenAdaper.sp(30))), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + buildImage(), + Expanded(child: buildContent()), - // const Spacer( - // flex: 2, - // ), - // controller.list[ind][index].inOrOut == - // InventoryInOrOutEnumValues[InventoryInOrOutEnum.In] - // ? Container( - // height: ScreenAdaper.height(40), - // width: ScreenAdaper.width(40), - // decoration: BoxDecoration( - // shape: BoxShape.circle, - // gradient: const LinearGradient( - // begin: Alignment.topCenter, - // end: Alignment.bottomCenter, - // colors: [ - // lightOrange, - // darkOrange, - // ]), - // boxShadow: [ - // BoxShadow( - // color: lightOrange, - // offset: Offset(0, ScreenAdaper.height(10)), - // blurRadius: ScreenAdaper.sp(10)) - // ]), - // child: const Icon( - // Icons.done, - // color: Colors.white, - // ), - // ) - // : Align( - // alignment: Alignment.topRight, - // child: Padding( - // padding: EdgeInsets.only(top: ScreenAdaper.height(20)), - // child: PopupMenuButton( - // onSelected: (value) => { - // // controller.onTaskComplete(value, index, ind, - // // controller.list[ind][index].key, context) - // }, - // surfaceTintColor: Colors.white, - // padding: EdgeInsets.zero, - // icon: Icon( - // Icons.more_vert_rounded, - // color: Colors.grey, - // size: ScreenAdaper.sp(24), - // ), - // shape: OutlineInputBorder( - // borderRadius: - // BorderRadius.circular(ScreenAdaper.sp(20)), - // borderSide: BorderSide.none, - // ), - // itemBuilder: (context) { - // return [ - // PopupMenuItem( - // height: ScreenAdaper.height(20), - // value: 1, - // child: Row( - // children: [ - // Icon( - // Icons.edit_note, - // color: Colors.orange, - // size: ScreenAdaper.sp(14), - // ), - // SizedBox( - // width: - // ScreenAdaper.width(defaultPadding) / - // 2, - // ), - // const Text('Edit') - // ], - // )), - // PopupMenuItem( - // height: ScreenAdaper.height(25), - // value: 2, - // child: Row( - // children: [ - // Icon( - // Icons.delete_outline, - // color: Colors.orange, - // size: ScreenAdaper.sp(14), - // ), - // SizedBox( - // width: - // ScreenAdaper.width(defaultPadding) / - // 2, - // ), - // Text('Delete') - // ], - // )), - // PopupMenuItem( - // height: ScreenAdaper.height(25), - // value: 3, - // child: Row( - // children: [ - // Icon( - // Icons.done_all_outlined, - // color: Colors.orange, - // size: ScreenAdaper.sp(14), - // ), - // SizedBox( - // width: - // ScreenAdaper.width(defaultPadding) / - // 2, - // ), - // Text('Complete') - // ], - // )), - // ]; - // }, - // )), - // ), - ], + // const Spacer( + // flex: 2, + // ), + // controller.list[ind][index].inOrOut == + // InventoryInOrOutEnumValues[InventoryInOrOutEnum.In] + // ? Container( + // height: ScreenAdaper.height(40), + // width: ScreenAdaper.width(40), + // decoration: BoxDecoration( + // shape: BoxShape.circle, + // gradient: const LinearGradient( + // begin: Alignment.topCenter, + // end: Alignment.bottomCenter, + // colors: [ + // lightOrange, + // darkOrange, + // ]), + // boxShadow: [ + // BoxShadow( + // color: lightOrange, + // offset: Offset(0, ScreenAdaper.height(10)), + // blurRadius: ScreenAdaper.sp(10)) + // ]), + // child: const Icon( + // Icons.done, + // color: Colors.white, + // ), + // ) + // : Align( + // alignment: Alignment.topRight, + // child: Padding( + // padding: EdgeInsets.only(top: ScreenAdaper.height(20)), + // child: PopupMenuButton( + // onSelected: (value) => { + // // controller.onTaskComplete(value, index, ind, + // // controller.list[ind][index].key, context) + // }, + // surfaceTintColor: Colors.white, + // padding: EdgeInsets.zero, + // icon: Icon( + // Icons.more_vert_rounded, + // color: Colors.grey, + // size: ScreenAdaper.sp(24), + // ), + // shape: OutlineInputBorder( + // borderRadius: + // BorderRadius.circular(ScreenAdaper.sp(20)), + // borderSide: BorderSide.none, + // ), + // itemBuilder: (context) { + // return [ + // PopupMenuItem( + // height: ScreenAdaper.height(20), + // value: 1, + // child: Row( + // children: [ + // Icon( + // Icons.edit_note, + // color: Colors.orange, + // size: ScreenAdaper.sp(14), + // ), + // SizedBox( + // width: + // ScreenAdaper.width(defaultPadding) / + // 2, + // ), + // const Text('Edit') + // ], + // )), + // PopupMenuItem( + // height: ScreenAdaper.height(25), + // value: 2, + // child: Row( + // children: [ + // Icon( + // Icons.delete_outline, + // color: Colors.orange, + // size: ScreenAdaper.sp(14), + // ), + // SizedBox( + // width: + // ScreenAdaper.width(defaultPadding) / + // 2, + // ), + // Text('Delete') + // ], + // )), + // PopupMenuItem( + // height: ScreenAdaper.height(25), + // value: 3, + // child: Row( + // children: [ + // Icon( + // Icons.done_all_outlined, + // color: Colors.orange, + // size: ScreenAdaper.sp(14), + // ), + // SizedBox( + // width: + // ScreenAdaper.width(defaultPadding) / + // 2, + // ), + // Text('Complete') + // ], + // )), + // ]; + // }, + // )), + // ), + ], + ), ), ); } @@ -224,9 +230,8 @@ class InventoryInoutCard extends StatelessWidget { child: FadeInCacheImage( width: ScreenAdaper.width(100), height: ScreenAdaper.width(100), - url: controller.list[ind][index].product?.files.isNotEmpty ?? - false - ? '${GloablConfig.OSS_URL}${controller.list[ind][index].product?.files[0].path}' + url: controller.list[ind][index].files.isNotEmpty + ? '${GloablConfig.OSS_URL}${controller.list[ind][index].files[0]?.path}' : ''), )); } diff --git a/lib/screens/inventory_inout/components/inventory_inout_info.dart b/lib/screens/inventory_inout/components/inventory_inout_info.dart new file mode 100644 index 0000000..b7d0de0 --- /dev/null +++ b/lib/screens/inventory_inout/components/inventory_inout_info.dart @@ -0,0 +1,190 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; +import 'package:sk_base_mobile/apis/index.dart'; +import 'package:sk_base_mobile/app_theme.dart'; +import 'package:sk_base_mobile/config.dart'; +import 'package:sk_base_mobile/constants/bg_color.dart'; +import 'package:sk_base_mobile/constants/constants.dart'; +import 'package:sk_base_mobile/models/index.dart'; +import 'package:sk_base_mobile/models/inventory.model.dart'; +import 'package:sk_base_mobile/util/date.util.dart'; +import 'package:sk_base_mobile/util/screen_adaper_util.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/image_preview.dart'; +import 'package:sk_base_mobile/widgets/loading_indicator.dart'; + +class InventoryInoutInfo extends StatelessWidget { + late int inventoryInoutId; + late InventoryInouInfoController controller; + InventoryInoutInfo({Key? key, required this.inventoryInoutId}) + : controller = Get.put(InventoryInouInfoController(inventoryInoutId)), + super(key: key); + + @override + Widget build(BuildContext context) { + return SizedBox( + child: Column(children: [ + Container( + padding: EdgeInsets.symmetric( + horizontal: ScreenAdaper.width(10), + vertical: ScreenAdaper.height(15)), + decoration: const BoxDecoration(color: AppTheme.primaryColor), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '出入库详情', + style: TextStyle( + fontSize: ScreenAdaper.sp(25), color: AppTheme.nearlyWhite), + ), + ], + ), + ), + Expanded( + child: Obx(() => controller.inventoryInoutInfo.value == null + ? const Center(child: LoadingIndicator(common: true)) + : SingleChildScrollView( + child: Column( + children: [ + buildListItem( + leading: '出入库类型', + trailing: InventoryInOrOutEnum.In == + controller.inventoryInoutInfo.value?.inOrOut + ? '入库' + : '出库'), + customDivider(), + if (InventoryInOrOutEnum.In == + controller.inventoryInoutInfo.value?.inOrOut) ...[ + buildListItem( + leading: '存放位置', + trailing: controller + .inventoryInoutInfo.value?.inventory?.position), + customDivider(), + ], + buildListItem( + leading: '出入库时间', + trailing: DateUtil.format( + controller.inventoryInoutInfo.value!.time!)), + customDivider(), + buildListItem( + leading: '所属公司', + trailing: controller.inventoryInoutInfo.value?.product + ?.company?.name), + customDivider(), + buildListItem( + leading: '产品名称', + trailing: controller + .inventoryInoutInfo.value?.product?.name), + customDivider(), + buildListItem( + leading: '产品规格', + trailing: controller.inventoryInoutInfo.value?.product + ?.productSpecification), + customDivider(), + buildListItem( + leading: '数量', + trailing: + '${controller.inventoryInoutInfo.value?.quantity}${controller.inventoryInoutInfo.value?.product?.unit?.label ?? ''}'), + customDivider(), + buildListItem( + leading: '单价', + trailing: + '¥${double.parse('${controller.inventoryInoutInfo.value?.unitPrice ?? 0}')}'), + customDivider(), + buildListItem( + leading: '操作人', + trailing: controller.inventoryInoutInfo.value?.agent), + customDivider(), + buildListItem( + leading: '备注', + trailing: + controller.inventoryInoutInfo.value?.remark), + customDivider(), + buildListItem( + leading: '照片', + ), + buildAttachmentsPreview() + ], + )))) + ]), + ); + } + + Widget buildAttachmentsPreview() { + return Container( + padding: EdgeInsets.symmetric(vertical: ScreenAdaper.height(20)), + child: Wrap( + spacing: ScreenAdaper.width(10), + runSpacing: ScreenAdaper.height(10), + children: (controller.inventoryInoutInfo.value?.files ?? []) + .map((e) => buildImage(e.path)) + .toList(), + ), + ); + } + + Widget buildImage(String? path) { + return path == null + ? const SizedBox() + : InkWell( + onTap: () => { + if (controller.inventoryInoutInfo.value!.files.isNotEmpty) + { + Get.dialog(ImagePreivew( + galleryItems: controller.inventoryInoutInfo.value!.files, + initialIndex: controller.inventoryInoutInfo.value!.files + .indexWhere((element) => element.path == path))) + } + }, + child: Container( + margin: EdgeInsets.only( + right: ScreenAdaper.width(defaultPadding) / 2), + child: ClipRRect( + borderRadius: BorderRadius.circular(ScreenAdaper.sp(15)), + child: FadeInCacheImage( + width: ScreenAdaper.width(300), + height: ScreenAdaper.width(300), + url: '${GloablConfig.OSS_URL}$path'), + )), + ); + } + + Widget customDivider() { + return Divider( + height: ScreenAdaper.height(1), + color: AppTheme.dividerColor, + ); + } + + Widget buildListItem({String? leading, dynamic trailing}) { + return ListTile( + leading: Text( + '${leading ?? ''}: ', + style: TextStyle(fontSize: ScreenAdaper.sp(25)), + ), + trailing: Text('${trailing ?? ''}', + style: TextStyle(fontSize: ScreenAdaper.sp(25)))); + } +} + +class InventoryInouInfoController extends GetxController { + final inventoryInoutInfo = Rx(null); + int inventoryInoutId; + InventoryInouInfoController(this.inventoryInoutId); + + @override + onReady() { + getData(); + } + + Future getData() async { + printInfo(info: '$inventoryInoutId'); + await Future.delayed(const Duration(milliseconds: 500)); + final res = await Api.getInventoryInOutInfo(inventoryInoutId); + if (res.data != null) { + inventoryInoutInfo.value = InventoryInOutModel.fromJson(res.data); + } + } +} diff --git a/lib/screens/inventory_inout/inventory_inout_controller.dart b/lib/screens/inventory_inout/inventory_inout_controller.dart index cb72e6e..76a01c4 100644 --- a/lib/screens/inventory_inout/inventory_inout_controller.dart +++ b/lib/screens/inventory_inout/inventory_inout_controller.dart @@ -1,14 +1,15 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:sk_base_mobile/apis/index.dart'; import 'package:sk_base_mobile/app_theme.dart'; import 'package:sk_base_mobile/constants/enum.dart'; import 'package:sk_base_mobile/db_helper/dbHelper.dart'; +import 'package:sk_base_mobile/screens/inventory_inout/components/inventory_inout_info.dart'; import 'package:sk_base_mobile/screens/new_inventory_inout/new_inventory_inout.dart'; import 'package:sk_base_mobile/util/date.util.dart'; import 'package:sk_base_mobile/util/logger_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 '../../models/index.dart'; import 'components/responsive.dart'; @@ -243,6 +244,14 @@ class InventoryInoutController extends GetxController { ); } + Future showInventoryInoutInfoDialog(int id) async { + ModalUtil.showGeneralDialog( + width: ScreenAdaper.width(Get.width - 100), + height: ScreenAdaper.height(Get.height - 100), + content: InventoryInoutInfo(inventoryInoutId: id)) + .then((value) => {Get.delete()}); + } + Future> getInoutHistory() async { final res = await Api.getInventoryInout({}); printInfo(info: res.toString()); diff --git a/lib/screens/landing/landing.dart b/lib/screens/landing/landing.dart index 1b3a244..1a5f223 100644 --- a/lib/screens/landing/landing.dart +++ b/lib/screens/landing/landing.dart @@ -21,7 +21,9 @@ class LandingPage extends StatelessWidget { child: Obx(() => Scaffold( floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, - floatingActionButton: controller.currentIndex.value == 0 + floatingActionButton: [0].indexWhere( + (item) => item == controller.currentIndex.value) > + -1 ? FloatingCreateButton() : null, bottomNavigationBar: BottomNavBar(), diff --git a/lib/screens/mine/useinfo/userinfo.controller.dart b/lib/screens/mine/useinfo/userinfo.controller.dart index ecbf77f..3a70b65 100644 --- a/lib/screens/mine/useinfo/userinfo.controller.dart +++ b/lib/screens/mine/useinfo/userinfo.controller.dart @@ -2,7 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:get/get.dart'; import 'package:sk_base_mobile/models/user_info.model.dart'; import 'package:sk_base_mobile/store/auth.store.dart'; -import 'package:sk_base_mobile/apis/api.dart' as Api; +import 'package:sk_base_mobile/apis/index.dart'; class UserInfoController extends GetxController { final nickNameController = TextEditingController(text: ''); diff --git a/lib/screens/new_inventory_inout/components/inventory_search.dart b/lib/screens/new_inventory_inout/components/inventory_search.dart index 8c66892..413091b 100644 --- a/lib/screens/new_inventory_inout/components/inventory_search.dart +++ b/lib/screens/new_inventory_inout/components/inventory_search.dart @@ -3,13 +3,13 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; +import 'package:sk_base_mobile/apis/index.dart'; import 'package:sk_base_mobile/app_theme.dart'; import 'package:sk_base_mobile/constants/bg_color.dart'; 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/apis/api.dart' as Api; class InventorySearch extends StatelessWidget { Function(InventoryModel)? onInventorySelected; diff --git a/lib/screens/new_inventory_inout/components/product_search.dart b/lib/screens/new_inventory_inout/components/product_search.dart index 2f45e22..bb2ac82 100644 --- a/lib/screens/new_inventory_inout/components/product_search.dart +++ b/lib/screens/new_inventory_inout/components/product_search.dart @@ -3,12 +3,12 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; +import 'package:sk_base_mobile/apis/index.dart'; import 'package:sk_base_mobile/app_theme.dart'; import 'package:sk_base_mobile/constants/bg_color.dart'; import 'package:sk_base_mobile/models/index.dart'; import 'package:sk_base_mobile/util/debouncer.dart'; import 'package:sk_base_mobile/util/screen_adaper_util.dart'; -import 'package:sk_base_mobile/apis/api.dart' as Api; class ProductSearch extends StatelessWidget { Function(ProductModel)? onProductSelected; diff --git a/lib/screens/new_inventory_inout/new_inventory_inout.dart b/lib/screens/new_inventory_inout/new_inventory_inout.dart index 4fd82d0..0525534 100644 --- a/lib/screens/new_inventory_inout/new_inventory_inout.dart +++ b/lib/screens/new_inventory_inout/new_inventory_inout.dart @@ -202,34 +202,35 @@ class NewInventoryInout extends StatelessWidget { )); } else { ModalUtil.showGeneralDialog( - content: InventorySearch( - beforeSelectedCheck: (InventoryModel itemData) { - final isValid = (itemData.quantity ?? 0) > 0; - if (!isValid) { - SnackBarUtil().warning('库存不足'); - } - return (itemData.quantity ?? 0) > 0; - }, - onInventorySelected: (InventoryModel inventory) { - Get.back(); - controller.payload['inventoryId'] = inventory.id; - String productName = - '${inventory.product!.name!}(¥${double.parse('${inventory.unitPrice}')})(${inventory.product?.company?.name})'; - if ((inventory.product?.productSpecification ?? '') - .isNotEmpty) { - productName += - ' (${inventory.product?.productSpecification})'; - } - controller.projectTextController.text = - inventory.project!.name!; - controller.payload['projectId'] = inventory.project?.id; - controller.quantityTextController.text = - '${inventory.quantity}'; - controller.productTextController.text = productName; - controller.payload['productId'] = inventory.product?.id; - }, - ), - width: Get.width - ScreenAdaper.width(50)); + content: InventorySearch( + beforeSelectedCheck: (InventoryModel itemData) { + final isValid = (itemData.quantity ?? 0) > 0; + if (!isValid) { + SnackBarUtil().warning('库存不足'); + } + return (itemData.quantity ?? 0) > 0; + }, + onInventorySelected: (InventoryModel inventory) { + Get.back(); + controller.payload['inventoryId'] = inventory.id; + String productName = + '${inventory.product!.name!}(¥${double.parse('${inventory.unitPrice}')})(${inventory.product?.company?.name})'; + if ((inventory.product?.productSpecification ?? '') + .isNotEmpty) { + productName += + ' (${inventory.product?.productSpecification})'; + } + controller.projectTextController.text = + inventory.project!.name!; + controller.payload['projectId'] = inventory.project?.id; + controller.quantityTextController.text = + '${inventory.quantity}'; + controller.productTextController.text = productName; + controller.payload['productId'] = inventory.product?.id; + }, + ), + width: Get.width - ScreenAdaper.width(50)) + .then((value) => Get.delete()); } }); } 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 7ac2cf2..10d9ba6 100644 --- a/lib/screens/new_inventory_inout/new_inventory_inout_controller.dart +++ b/lib/screens/new_inventory_inout/new_inventory_inout_controller.dart @@ -4,11 +4,11 @@ import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:sk_base_mobile/apis/index.dart'; import 'package:sk_base_mobile/constants/enum.dart'; 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/apis/api.dart' as Api; import 'package:sk_base_mobile/util/date.util.dart'; import 'package:sk_base_mobile/util/logger_util.dart'; import 'package:sk_base_mobile/util/media_util.dart'; diff --git a/lib/services/app_info.service.dart b/lib/services/app_info.service.dart index 9d2426b..8b1a16e 100644 --- a/lib/services/app_info.service.dart +++ b/lib/services/app_info.service.dart @@ -7,8 +7,9 @@ import 'package:package_info/package_info.dart'; import 'package:sk_base_mobile/app_theme.dart'; import 'package:sk_base_mobile/models/app_bottom_nav_item.dart'; import 'package:sk_base_mobile/models/app_config.dart'; +import 'package:sk_base_mobile/screens/inventory/inventory.dart'; import 'package:sk_base_mobile/screens/inventory_inout/inventory_inout.dart'; -import 'package:sk_base_mobile/apis/api.dart' as Api; +import 'package:sk_base_mobile/apis/index.dart'; import 'package:sk_base_mobile/screens/workbench/workbench.dart'; import 'package:sk_base_mobile/services/service.dart'; import 'package:sk_base_mobile/store/auth.store.dart'; @@ -36,7 +37,7 @@ class AppInfoService extends GetxService { icon: Icons.inventory_outlined, activeIcon: Icons.inventory_rounded, label: '库存', - page: InvenotryInoutPage()), + page: InventoryPage()), AppBottomNavItem( icon: Icons.widgets_outlined, activeIcon: Icons.widgets_rounded, diff --git a/lib/services/dio.service.dart b/lib/services/dio.service.dart index ea64f4b..1e6f2fe 100644 --- a/lib/services/dio.service.dart +++ b/lib/services/dio.service.dart @@ -16,7 +16,7 @@ class DioService extends Get.GetxService { static late Dio _dio; List whiteList = [Urls.login]; BaseOptions dioBaseOptions = BaseOptions( - connectTimeout: const Duration(seconds: 5), + connectTimeout: const Duration(seconds: 20), baseUrl: '${GloablConfig.BASE_URL}', followRedirects: true); diff --git a/lib/store/auth.store.dart b/lib/store/auth.store.dart index 02835b5..b5fe0df 100644 --- a/lib/store/auth.store.dart +++ b/lib/store/auth.store.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'package:dio/dio.dart' as Dio; import 'package:get/get.dart'; -import 'package:sk_base_mobile/apis/api.dart' as Api; +import 'package:sk_base_mobile/apis/index.dart'; import 'package:sk_base_mobile/util/logger_util.dart'; import 'package:sk_base_mobile/widgets/tap_to_dismiss_keyboard.dart'; import 'package:sk_base_mobile/models/auth.dart'; @@ -53,7 +53,6 @@ class AuthStore extends GetxController { } Future logout({bool force = false}) async { - LoadingUtil.to.show(status: 'Logout...'); await StorageService.to.remove(CacheKeys.token, isWithUser: false); await StorageService.to.remove(CacheKeys.userInfo, isWithUser: false); try { diff --git a/lib/store/dict.store.dart b/lib/store/dict.store.dart index 11e52a1..bbf6dd0 100644 --- a/lib/store/dict.store.dart +++ b/lib/store/dict.store.dart @@ -3,7 +3,7 @@ import 'package:get/get_state_manager/get_state_manager.dart'; import 'package:sk_base_mobile/constants/dict_enum.dart'; import 'package:sk_base_mobile/models/dict_item.model.dart'; import 'package:sk_base_mobile/models/dict_type.model.dart'; -import 'package:sk_base_mobile/apis/api.dart' as Api; +import 'package:sk_base_mobile/apis/index.dart'; import 'package:sk_base_mobile/util/logger_util.dart'; const needCachedKey = [ diff --git a/lib/util/loading_util.dart b/lib/util/loading_util.dart index 08ca671..5a6acfa 100644 --- a/lib/util/loading_util.dart +++ b/lib/util/loading_util.dart @@ -4,9 +4,8 @@ import 'package:loading_animation_widget/loading_animation_widget.dart'; import 'package:sk_base_mobile/app_theme.dart'; import 'package:sk_base_mobile/util/screen_adaper_util.dart'; -class LoadingUtil extends GetxController { +class LoadingUtil extends GetxService { static LoadingUtil get to => Get.find(); - OverlayEntry? _loadingOverlay; Future init() async { @@ -53,8 +52,9 @@ class LoadingUtil extends GetxController { )); }, ); - - Overlay.of(Get.overlayContext!).insert(_loadingOverlay!); + if (Get.overlayContext != null) { + Overlay.of(Get.overlayContext!).insert(_loadingOverlay!); + } } hideLoading() { diff --git a/lib/util/media_util.dart b/lib/util/media_util.dart index 3dde929..3846b57 100644 --- a/lib/util/media_util.dart +++ b/lib/util/media_util.dart @@ -1,6 +1,6 @@ import 'dart:io'; import 'package:dio/dio.dart' as Dio; -import 'package:sk_base_mobile/apis/api.dart' as Api; +import 'package:sk_base_mobile/apis/index.dart'; import 'package:image_picker/image_picker.dart'; import 'package:sk_base_mobile/models/upload_result.model.dart'; import 'package:sk_base_mobile/services/service.dart'; @@ -15,7 +15,7 @@ class MediaUtil { } AppInfoService.to.isCameraing.value = true; pickedFile = await ImagePicker() - .pickImage(source: ImageSource.camera, maxWidth: maxWidth ?? 400); + .pickImage(source: ImageSource.camera, maxWidth: maxWidth ?? 2000); AppInfoService.to.isCameraing.value = false; } catch (e) { AppInfoService.to.isCameraing.value = false; diff --git a/lib/util/modal.util.dart b/lib/util/modal.util.dart index 96d8e00..a7b0a60 100644 --- a/lib/util/modal.util.dart +++ b/lib/util/modal.util.dart @@ -68,7 +68,7 @@ class ModalUtil { ); static Future showGeneralDialog( - {required Widget content, double? width}) { + {required Widget content, double? width, double? height}) { return Get.generalDialog( barrierLabel: "productPicker", barrierDismissible: true, @@ -79,7 +79,7 @@ class ModalUtil { borderRadius: BorderRadius.circular(ScreenAdaper.sp(30)), child: Material( child: SizedBox( - height: Get.height - ScreenAdaper.height(150), + height: height ?? Get.height - ScreenAdaper.height(150), width: width ?? Get.width - ScreenAdaper.width(150), child: content, )))); diff --git a/lib/widgets/image_preview.dart b/lib/widgets/image_preview.dart new file mode 100644 index 0000000..f682877 --- /dev/null +++ b/lib/widgets/image_preview.dart @@ -0,0 +1,112 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:photo_view/photo_view.dart'; +import 'package:photo_view/photo_view_gallery.dart'; +import 'package:sk_base_mobile/config.dart'; +import 'package:sk_base_mobile/models/file.model.dart'; +import 'package:sk_base_mobile/util/screen_adaper_util.dart'; +import 'package:sk_base_mobile/widgets/fade_in_cache_image.dart'; +import 'package:sk_base_mobile/widgets/loading_indicator.dart'; + +class ImagePreivew extends StatefulWidget { + ImagePreivew({ + super.key, + required this.galleryItems, + this.backgroundDecoration, + this.minScale, + this.maxScale, + this.initialIndex = 0, + this.scrollDirection = Axis.horizontal, + }) : pageController = PageController(initialPage: initialIndex); + final BoxDecoration? backgroundDecoration; + final dynamic minScale; + final dynamic maxScale; + final int initialIndex; + final PageController pageController; + final List galleryItems; + final Axis scrollDirection; + @override + State createState() => _ImagePreivewState(); +} + +class _ImagePreivewState extends State { + late int currentIndex = widget.initialIndex; + @override + Widget build(BuildContext context) { + return Container( + decoration: widget.backgroundDecoration, + constraints: BoxConstraints.expand( + height: MediaQuery.of(context).size.height, + ), + child: Stack( + alignment: Alignment.bottomRight, + children: [ + PhotoViewGallery.builder( + scrollPhysics: const BouncingScrollPhysics(), + builder: _buildItem, + itemCount: widget.galleryItems.length, + loadingBuilder: (_, _1) => const LoadingIndicator(common: true), + backgroundDecoration: widget.backgroundDecoration, + pageController: widget.pageController, + onPageChanged: onPageChanged, + scrollDirection: widget.scrollDirection, + ), + Container( + padding: const EdgeInsets.all(20.0), + child: Text( + "照片 ${currentIndex + 1}", + style: TextStyle( + decoration: TextDecoration.none, + color: Colors.white, + fontSize: ScreenAdaper.sp(20), + ), + ), + ) + ], + ), + ); + } + + PhotoViewGalleryPageOptions _buildItem(BuildContext context, int index) { + final FileModel item = widget.galleryItems[index]; + return PhotoViewGalleryPageOptions( + onTapUp: (_, _1, _2) { + Get.back(); + }, + imageProvider: Image.network('${GloablConfig.OSS_URL}${item.path}').image, + initialScale: PhotoViewComputedScale.contained, + minScale: PhotoViewComputedScale.contained * (0.5 + index / 10), + maxScale: PhotoViewComputedScale.covered * 4.1, + heroAttributes: PhotoViewHeroAttributes(tag: '${item.id}'), + ); + // return item.isSvg + // ? PhotoViewGalleryPageOptions.customChild( + // child: Container( + // width: 300, + // height: 300, + // child: SvgPicture.asset( + // item.resource, + // height: 200.0, + // ), + // ), + // childSize: const Size(300, 300), + // initialScale: PhotoViewComputedScale.contained, + // minScale: PhotoViewComputedScale.contained * (0.5 + index / 10), + // maxScale: PhotoViewComputedScale.covered * 4.1, + // heroAttributes: PhotoViewHeroAttributes(tag: item.id), + // ) + // : PhotoViewGalleryPageOptions( + // imageProvider: AssetImage(item.resource), + // initialScale: PhotoViewComputedScale.contained, + // minScale: PhotoViewComputedScale.contained * (0.5 + index / 10), + // maxScale: PhotoViewComputedScale.covered * 4.1, + // heroAttributes: PhotoViewHeroAttributes(tag: item.id), + // ); + } + + void onPageChanged(int index) { + setState(() { + currentIndex = index; + }); + } +} diff --git a/lib/widgets/loading_indicator.dart b/lib/widgets/loading_indicator.dart index 1034add..29a9a19 100644 --- a/lib/widgets/loading_indicator.dart +++ b/lib/widgets/loading_indicator.dart @@ -1,17 +1,23 @@ import 'package:flutter/cupertino.dart'; +import 'package:loading_animation_widget/loading_animation_widget.dart'; import 'package:sk_base_mobile/app_theme.dart'; import 'package:sk_base_mobile/util/screen_adaper_util.dart'; class LoadingIndicator extends StatelessWidget { final bool animating; - const LoadingIndicator({super.key, this.animating = true}); + final bool common; + const LoadingIndicator( + {super.key, this.animating = true, this.common = false}); @override Widget build(BuildContext context) { - return CupertinoActivityIndicator( - animating: animating, - color: AppTheme.primaryColor, - radius: ScreenAdaper.sp(20), - ); + return common + ? LoadingAnimationWidget.fourRotatingDots( + color: AppTheme.primaryColorLight, size: ScreenAdaper.sp(40)) + : CupertinoActivityIndicator( + animating: animating, + color: AppTheme.primaryColor, + radius: ScreenAdaper.sp(20), + ); } } diff --git a/lib/widgets/my_avatar.dart b/lib/widgets/my_avatar.dart index dd8f9d9..16ed71f 100644 --- a/lib/widgets/my_avatar.dart +++ b/lib/widgets/my_avatar.dart @@ -1,5 +1,5 @@ import 'dart:io'; -import 'package:sk_base_mobile/apis/api.dart' as Api; +import 'package:sk_base_mobile/apis/index.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:image_picker/image_picker.dart'; diff --git a/pubspec.lock b/pubspec.lock index e6ec4ea..9f42515 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -416,6 +416,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.7" + image_gallery_saver: + dependency: "direct main" + description: + name: image_gallery_saver + sha256: "0aba74216a4d9b0561510cb968015d56b701ba1bd94aace26aacdd8ae5761816" + url: "https://pub.dev" + source: hosted + version: "2.0.3" image_picker: dependency: "direct main" description: @@ -680,6 +688,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.2" + photo_view: + dependency: "direct main" + description: + name: photo_view + sha256: "8036802a00bae2a78fc197af8a158e3e2f7b500561ed23b4c458107685e645bb" + url: "https://pub.dev" + source: hosted + version: "0.14.0" platform: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 2870902..1ad22cb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -58,6 +58,8 @@ dependencies: path_provider: ^2.1.2 flutter_typeahead: ^5.2.0 decimal: ^2.3.3 + photo_view: ^0.14.0 + image_gallery_saver: ^2.0.3 dev_dependencies: