feat: inventory and inout info develop

This commit is contained in:
louis 2024-03-26 15:30:43 +08:00
parent fc0187acf0
commit cf51cd8ada
26 changed files with 666 additions and 292 deletions

View File

@ -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<Response> login(String username, String password) {
return DioService.dio.post(
Urls.login,
data: {'username': username, 'password': password},
);
}
//
Future<Response> getUserInfo() {
return DioService.dio.get(
Urls.getUserInfo,
);
}
//
Future<Response<PaginationData>> getProjects(Map params) {
return DioService.dio.get<PaginationData>(Urls.getProjects,
queryParameters: {'page': 1, 'pageSize': 10, ...params});
}
//
Future<Response<PaginationData>> getProducts(Map params) {
return DioService.dio.get<PaginationData>(Urls.getProducts,
queryParameters: {'page': 1, 'pageSize': 10, ...params});
}
//
Future<Response<PaginationData>> getInventoryInout(Map params) {
return DioService.dio.get<PaginationData>(Urls.inventoryInout,
queryParameters: {'page': 1, 'pageSize': 10, ...(params)});
}
//
Future<Response> createInventoryInout(Map params) {
return DioService.dio.post(Urls.inventoryInout, data: params);
}
//
Future<Response> updateInventoryInout(int id, Map params) {
return DioService.dio.put('${Urls.inventoryInout}/$id', data: params);
}
//
Future<Response<PaginationData>> getInventory(Map params) {
return DioService.dio.get<PaginationData>(Urls.inventory,
queryParameters: {'page': 1, 'pageSize': 10, ...(params)});
}
// ()
Future<Response> getDictTypeAll(Map params) {
return DioService.dio.post(Urls.getDictType, data: params);
}
Future<Response> logout() {
return DioService.dio.post(
Urls.logout,
);
}
Future<Response> deleteAccount() {
return DioService.dio.post(
Urls.deleteAccount,
);
}
Future<Response> getAppConfig() {
Map<String, dynamic> query = {'ver': 0};
return DioService.dio.get(Urls.getAppConfig, queryParameters: query);
}
Future<Response> saveUserInfo(Map<String, dynamic> data) {
return DioService.dio.post(Urls.saveUserInfo, data: data);
}
Future<Response> 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<Response> updateAvatar(String filename) {
return DioService.dio.post(Urls.updateAvatar, data: {'avatarPath': filename});
}

110
lib/apis/index.dart Normal file
View File

@ -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<Response> login(String username, String password) {
return DioService.dio.post(
Urls.login,
data: {'username': username, 'password': password},
);
}
//
static Future<Response> getUserInfo() {
return DioService.dio.get(
Urls.getUserInfo,
);
}
//
static Future<Response<PaginationData>> getProjects(Map params) {
return DioService.dio.get<PaginationData>(Urls.projects,
queryParameters: {'page': 1, 'pageSize': 10, ...params});
}
//
static Future<Response<PaginationData>> getProducts(Map params) {
return DioService.dio.get<PaginationData>(Urls.products,
queryParameters: {'page': 1, 'pageSize': 10, ...params});
}
//
static Future<Response<PaginationData>> getInventoryInout(Map params) {
return DioService.dio.get<PaginationData>(Urls.inventoryInout,
queryParameters: {'page': 1, 'pageSize': 10, ...(params)});
}
//
static Future<Response> getInventoryInOutInfo(int id) {
return DioService.dio.get(
'${Urls.inventoryInout}/$id',
);
}
//
static Future<Response> createInventoryInout(Map params) {
return DioService.dio.post(Urls.inventoryInout, data: params);
}
//
static Future<Response> updateInventoryInout(int id, Map params) {
return DioService.dio.put('${Urls.inventoryInout}/$id', data: params);
}
//
static Future<Response<PaginationData>> getInventory(Map params) {
return DioService.dio.get<PaginationData>(Urls.inventory,
queryParameters: {'page': 1, 'pageSize': 10, ...(params)});
}
// ()
static Future<Response> getDictTypeAll(Map params) {
return DioService.dio.post(Urls.getDictType, data: params);
}
static Future<Response> logout() {
return DioService.dio.post(
Urls.logout,
);
}
static Future<Response> deleteAccount() {
return DioService.dio.post(
Urls.deleteAccount,
);
}
static Future<Response> getAppConfig() {
Map<String, dynamic> query = {'ver': 0};
return DioService.dio.get(Urls.getAppConfig, queryParameters: query);
}
static Future<Response> saveUserInfo(Map<String, dynamic> data) {
return DioService.dio.post(Urls.saveUserInfo, data: data);
}
static Future<Response> 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<Response> updateAvatar(String filename) {
return DioService.dio
.post(Urls.updateAvatar, data: {'avatarPath': filename});
}
}

View File

@ -6,8 +6,8 @@ class Urls {
static String deleteAccount = 'user/deleteAccount'; static String deleteAccount = 'user/deleteAccount';
static String saveUserInfo = 'user/saveUserInfo'; static String saveUserInfo = 'user/saveUserInfo';
static String getUserInfo = 'account/profile'; static String getUserInfo = 'account/profile';
static String getProjects = 'project'; static String projects = 'project';
static String getProducts = 'product'; static String products = 'product';
static String inventoryInout = 'materials-in-out'; static String inventoryInout = 'materials-in-out';
static String inventory = 'materials-inventory'; static String inventory = 'materials-inventory';
static String updateAvatar = 'user/updateAvatar'; static String updateAvatar = 'user/updateAvatar';

View File

@ -1,6 +1,7 @@
import 'package:sk_base_mobile/constants/enum.dart'; import 'package:sk_base_mobile/constants/enum.dart';
import 'package:sk_base_mobile/models/file.model.dart'; import 'package:sk_base_mobile/models/file.model.dart';
import 'package:sk_base_mobile/models/index.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/product.model.dart';
import 'package:sk_base_mobile/models/project.model.dart'; import 'package:sk_base_mobile/models/project.model.dart';
@ -24,6 +25,7 @@ class InventoryInOutModel {
required this.files, required this.files,
required this.project, required this.project,
required this.product, required this.product,
this.inventory,
}); });
final int? id; final int? id;
@ -41,10 +43,10 @@ class InventoryInOutModel {
final String? remark; final String? remark;
final int? projectId; final int? projectId;
final int? isDelete; final int? isDelete;
final List<FileModel?> files; final List<FileModel> files;
final ProjectModel? project; final ProjectModel? project;
final ProductModel? product; final ProductModel? product;
final InventoryModel? inventory;
factory InventoryInOutModel.fromJson(Map<String, dynamic> json) { factory InventoryInOutModel.fromJson(Map<String, dynamic> json) {
return InventoryInOutModel( return InventoryInOutModel(
id: json["id"], id: json["id"],
@ -72,6 +74,9 @@ class InventoryInOutModel {
product: json["product"] == null product: json["product"] == null
? null ? null
: ProductModel.fromJson(json["product"]), : 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(), "files": files.map((x) => x).toList(),
"project": project?.toJson(), "project": project?.toJson(),
"product": product?.toJson(), "product": product?.toJson(),
"inventory": inventory?.toJson(),
}; };
} }

View File

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

View File

@ -16,138 +16,144 @@ class InventoryInoutCard extends StatelessWidget {
final controller = Get.find<InventoryInoutController>(); final controller = Get.find<InventoryInoutController>();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return InkWell(
margin: EdgeInsets.symmetric( onTap: () {
vertical: ScreenAdaper.height(15), controller
horizontal: ScreenAdaper.width(15)), .showInventoryInoutInfoDialog(controller.list[ind][index].id!);
padding: EdgeInsets.symmetric( },
horizontal: ScreenAdaper.width(defaultPadding), child: Container(
vertical: ScreenAdaper.height(defaultPadding)), margin: EdgeInsets.symmetric(
decoration: BoxDecoration( vertical: ScreenAdaper.height(15),
boxShadow: [ horizontal: ScreenAdaper.width(15)),
BoxShadow( padding: EdgeInsets.symmetric(
color: AppTheme.barrierColor.withOpacity(0.2), horizontal: ScreenAdaper.width(defaultPadding),
offset: Offset(0, ScreenAdaper.height(5)), vertical: ScreenAdaper.height(defaultPadding)),
blurRadius: ScreenAdaper.sp(10)), decoration: BoxDecoration(
], boxShadow: [
color: AppTheme.nearlyWhite, BoxShadow(
borderRadius: BorderRadius.circular(ScreenAdaper.sp(30))), color: AppTheme.barrierColor.withOpacity(0.2),
child: Row( offset: Offset(0, ScreenAdaper.height(5)),
crossAxisAlignment: CrossAxisAlignment.center, blurRadius: ScreenAdaper.sp(10)),
children: [ ],
buildImage(), color: AppTheme.nearlyWhite,
Expanded(child: buildContent()), borderRadius: BorderRadius.circular(ScreenAdaper.sp(30))),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
buildImage(),
Expanded(child: buildContent()),
// const Spacer( // const Spacer(
// flex: 2, // flex: 2,
// ), // ),
// controller.list[ind][index].inOrOut == // controller.list[ind][index].inOrOut ==
// InventoryInOrOutEnumValues[InventoryInOrOutEnum.In] // InventoryInOrOutEnumValues[InventoryInOrOutEnum.In]
// ? Container( // ? Container(
// height: ScreenAdaper.height(40), // height: ScreenAdaper.height(40),
// width: ScreenAdaper.width(40), // width: ScreenAdaper.width(40),
// decoration: BoxDecoration( // decoration: BoxDecoration(
// shape: BoxShape.circle, // shape: BoxShape.circle,
// gradient: const LinearGradient( // gradient: const LinearGradient(
// begin: Alignment.topCenter, // begin: Alignment.topCenter,
// end: Alignment.bottomCenter, // end: Alignment.bottomCenter,
// colors: [ // colors: [
// lightOrange, // lightOrange,
// darkOrange, // darkOrange,
// ]), // ]),
// boxShadow: [ // boxShadow: [
// BoxShadow( // BoxShadow(
// color: lightOrange, // color: lightOrange,
// offset: Offset(0, ScreenAdaper.height(10)), // offset: Offset(0, ScreenAdaper.height(10)),
// blurRadius: ScreenAdaper.sp(10)) // blurRadius: ScreenAdaper.sp(10))
// ]), // ]),
// child: const Icon( // child: const Icon(
// Icons.done, // Icons.done,
// color: Colors.white, // color: Colors.white,
// ), // ),
// ) // )
// : Align( // : Align(
// alignment: Alignment.topRight, // alignment: Alignment.topRight,
// child: Padding( // child: Padding(
// padding: EdgeInsets.only(top: ScreenAdaper.height(20)), // padding: EdgeInsets.only(top: ScreenAdaper.height(20)),
// child: PopupMenuButton( // child: PopupMenuButton(
// onSelected: (value) => { // onSelected: (value) => {
// // controller.onTaskComplete(value, index, ind, // // controller.onTaskComplete(value, index, ind,
// // controller.list[ind][index].key, context) // // controller.list[ind][index].key, context)
// }, // },
// surfaceTintColor: Colors.white, // surfaceTintColor: Colors.white,
// padding: EdgeInsets.zero, // padding: EdgeInsets.zero,
// icon: Icon( // icon: Icon(
// Icons.more_vert_rounded, // Icons.more_vert_rounded,
// color: Colors.grey, // color: Colors.grey,
// size: ScreenAdaper.sp(24), // size: ScreenAdaper.sp(24),
// ), // ),
// shape: OutlineInputBorder( // shape: OutlineInputBorder(
// borderRadius: // borderRadius:
// BorderRadius.circular(ScreenAdaper.sp(20)), // BorderRadius.circular(ScreenAdaper.sp(20)),
// borderSide: BorderSide.none, // borderSide: BorderSide.none,
// ), // ),
// itemBuilder: (context) { // itemBuilder: (context) {
// return [ // return [
// PopupMenuItem( // PopupMenuItem(
// height: ScreenAdaper.height(20), // height: ScreenAdaper.height(20),
// value: 1, // value: 1,
// child: Row( // child: Row(
// children: [ // children: [
// Icon( // Icon(
// Icons.edit_note, // Icons.edit_note,
// color: Colors.orange, // color: Colors.orange,
// size: ScreenAdaper.sp(14), // size: ScreenAdaper.sp(14),
// ), // ),
// SizedBox( // SizedBox(
// width: // width:
// ScreenAdaper.width(defaultPadding) / // ScreenAdaper.width(defaultPadding) /
// 2, // 2,
// ), // ),
// const Text('Edit') // const Text('Edit')
// ], // ],
// )), // )),
// PopupMenuItem( // PopupMenuItem(
// height: ScreenAdaper.height(25), // height: ScreenAdaper.height(25),
// value: 2, // value: 2,
// child: Row( // child: Row(
// children: [ // children: [
// Icon( // Icon(
// Icons.delete_outline, // Icons.delete_outline,
// color: Colors.orange, // color: Colors.orange,
// size: ScreenAdaper.sp(14), // size: ScreenAdaper.sp(14),
// ), // ),
// SizedBox( // SizedBox(
// width: // width:
// ScreenAdaper.width(defaultPadding) / // ScreenAdaper.width(defaultPadding) /
// 2, // 2,
// ), // ),
// Text('Delete') // Text('Delete')
// ], // ],
// )), // )),
// PopupMenuItem( // PopupMenuItem(
// height: ScreenAdaper.height(25), // height: ScreenAdaper.height(25),
// value: 3, // value: 3,
// child: Row( // child: Row(
// children: [ // children: [
// Icon( // Icon(
// Icons.done_all_outlined, // Icons.done_all_outlined,
// color: Colors.orange, // color: Colors.orange,
// size: ScreenAdaper.sp(14), // size: ScreenAdaper.sp(14),
// ), // ),
// SizedBox( // SizedBox(
// width: // width:
// ScreenAdaper.width(defaultPadding) / // ScreenAdaper.width(defaultPadding) /
// 2, // 2,
// ), // ),
// Text('Complete') // Text('Complete')
// ], // ],
// )), // )),
// ]; // ];
// }, // },
// )), // )),
// ), // ),
], ],
),
), ),
); );
} }
@ -224,9 +230,8 @@ class InventoryInoutCard extends StatelessWidget {
child: FadeInCacheImage( child: FadeInCacheImage(
width: ScreenAdaper.width(100), width: ScreenAdaper.width(100),
height: ScreenAdaper.width(100), height: ScreenAdaper.width(100),
url: controller.list[ind][index].product?.files.isNotEmpty ?? url: controller.list[ind][index].files.isNotEmpty
false ? '${GloablConfig.OSS_URL}${controller.list[ind][index].files[0]?.path}'
? '${GloablConfig.OSS_URL}${controller.list[ind][index].product?.files[0].path}'
: ''), : ''),
)); ));
} }

View File

@ -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<InventoryInOutModel?>(null);
int inventoryInoutId;
InventoryInouInfoController(this.inventoryInoutId);
@override
onReady() {
getData();
}
Future<void> 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);
}
}
}

View File

@ -1,14 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.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/app_theme.dart';
import 'package:sk_base_mobile/constants/enum.dart'; import 'package:sk_base_mobile/constants/enum.dart';
import 'package:sk_base_mobile/db_helper/dbHelper.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/screens/new_inventory_inout/new_inventory_inout.dart';
import 'package:sk_base_mobile/util/date.util.dart'; import 'package:sk_base_mobile/util/date.util.dart';
import 'package:sk_base_mobile/util/logger_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/modal.util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart'; import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/apis/api.dart' as Api;
import '../../models/index.dart'; import '../../models/index.dart';
import 'components/responsive.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<InventoryInouInfoController>()});
}
Future<List<InventoryInOutModel>> getInoutHistory() async { Future<List<InventoryInOutModel>> getInoutHistory() async {
final res = await Api.getInventoryInout({}); final res = await Api.getInventoryInout({});
printInfo(info: res.toString()); printInfo(info: res.toString());

View File

@ -21,7 +21,9 @@ class LandingPage extends StatelessWidget {
child: Obx(() => Scaffold( child: Obx(() => Scaffold(
floatingActionButtonLocation: floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked, FloatingActionButtonLocation.centerDocked,
floatingActionButton: controller.currentIndex.value == 0 floatingActionButton: [0].indexWhere(
(item) => item == controller.currentIndex.value) >
-1
? FloatingCreateButton() ? FloatingCreateButton()
: null, : null,
bottomNavigationBar: BottomNavBar(), bottomNavigationBar: BottomNavBar(),

View File

@ -2,7 +2,7 @@ import 'package:flutter/cupertino.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:sk_base_mobile/models/user_info.model.dart'; import 'package:sk_base_mobile/models/user_info.model.dart';
import 'package:sk_base_mobile/store/auth.store.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 { class UserInfoController extends GetxController {
final nickNameController = TextEditingController(text: ''); final nickNameController = TextEditingController(text: '');

View File

@ -3,13 +3,13 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pull_to_refresh/pull_to_refresh.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/app_theme.dart';
import 'package:sk_base_mobile/constants/bg_color.dart'; import 'package:sk_base_mobile/constants/bg_color.dart';
import 'package:sk_base_mobile/models/index.dart'; import 'package:sk_base_mobile/models/index.dart';
import 'package:sk_base_mobile/models/inventory.model.dart'; import 'package:sk_base_mobile/models/inventory.model.dart';
import 'package:sk_base_mobile/util/debouncer.dart'; import 'package:sk_base_mobile/util/debouncer.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart'; import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/apis/api.dart' as Api;
class InventorySearch extends StatelessWidget { class InventorySearch extends StatelessWidget {
Function(InventoryModel)? onInventorySelected; Function(InventoryModel)? onInventorySelected;

View File

@ -3,12 +3,12 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pull_to_refresh/pull_to_refresh.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/app_theme.dart';
import 'package:sk_base_mobile/constants/bg_color.dart'; import 'package:sk_base_mobile/constants/bg_color.dart';
import 'package:sk_base_mobile/models/index.dart'; import 'package:sk_base_mobile/models/index.dart';
import 'package:sk_base_mobile/util/debouncer.dart'; import 'package:sk_base_mobile/util/debouncer.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart'; import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/apis/api.dart' as Api;
class ProductSearch extends StatelessWidget { class ProductSearch extends StatelessWidget {
Function(ProductModel)? onProductSelected; Function(ProductModel)? onProductSelected;

View File

@ -202,34 +202,35 @@ class NewInventoryInout extends StatelessWidget {
)); ));
} else { } else {
ModalUtil.showGeneralDialog( ModalUtil.showGeneralDialog(
content: InventorySearch( content: InventorySearch(
beforeSelectedCheck: (InventoryModel itemData) { beforeSelectedCheck: (InventoryModel itemData) {
final isValid = (itemData.quantity ?? 0) > 0; final isValid = (itemData.quantity ?? 0) > 0;
if (!isValid) { if (!isValid) {
SnackBarUtil().warning('库存不足'); SnackBarUtil().warning('库存不足');
} }
return (itemData.quantity ?? 0) > 0; return (itemData.quantity ?? 0) > 0;
}, },
onInventorySelected: (InventoryModel inventory) { onInventorySelected: (InventoryModel inventory) {
Get.back(); Get.back();
controller.payload['inventoryId'] = inventory.id; controller.payload['inventoryId'] = inventory.id;
String productName = String productName =
'${inventory.product!.name!}(¥${double.parse('${inventory.unitPrice}')})(${inventory.product?.company?.name})'; '${inventory.product!.name!}(¥${double.parse('${inventory.unitPrice}')})(${inventory.product?.company?.name})';
if ((inventory.product?.productSpecification ?? '') if ((inventory.product?.productSpecification ?? '')
.isNotEmpty) { .isNotEmpty) {
productName += productName +=
' (${inventory.product?.productSpecification})'; ' (${inventory.product?.productSpecification})';
} }
controller.projectTextController.text = controller.projectTextController.text =
inventory.project!.name!; inventory.project!.name!;
controller.payload['projectId'] = inventory.project?.id; controller.payload['projectId'] = inventory.project?.id;
controller.quantityTextController.text = controller.quantityTextController.text =
'${inventory.quantity}'; '${inventory.quantity}';
controller.productTextController.text = productName; controller.productTextController.text = productName;
controller.payload['productId'] = inventory.product?.id; controller.payload['productId'] = inventory.product?.id;
}, },
), ),
width: Get.width - ScreenAdaper.width(50)); width: Get.width - ScreenAdaper.width(50))
.then((value) => Get.delete<InventorySearchController>());
} }
}); });
} }

View File

@ -4,11 +4,11 @@ import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart'; import 'package: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/constants/enum.dart';
import 'package:sk_base_mobile/db_helper/dbHelper.dart'; import 'package:sk_base_mobile/db_helper/dbHelper.dart';
import 'package:sk_base_mobile/models/index.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/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/date.util.dart';
import 'package:sk_base_mobile/util/logger_util.dart'; import 'package:sk_base_mobile/util/logger_util.dart';
import 'package:sk_base_mobile/util/media_util.dart'; import 'package:sk_base_mobile/util/media_util.dart';

View File

@ -7,8 +7,9 @@ import 'package:package_info/package_info.dart';
import 'package:sk_base_mobile/app_theme.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_bottom_nav_item.dart';
import 'package:sk_base_mobile/models/app_config.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/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/screens/workbench/workbench.dart';
import 'package:sk_base_mobile/services/service.dart'; import 'package:sk_base_mobile/services/service.dart';
import 'package:sk_base_mobile/store/auth.store.dart'; import 'package:sk_base_mobile/store/auth.store.dart';
@ -36,7 +37,7 @@ class AppInfoService extends GetxService {
icon: Icons.inventory_outlined, icon: Icons.inventory_outlined,
activeIcon: Icons.inventory_rounded, activeIcon: Icons.inventory_rounded,
label: '库存', label: '库存',
page: InvenotryInoutPage()), page: InventoryPage()),
AppBottomNavItem( AppBottomNavItem(
icon: Icons.widgets_outlined, icon: Icons.widgets_outlined,
activeIcon: Icons.widgets_rounded, activeIcon: Icons.widgets_rounded,

View File

@ -16,7 +16,7 @@ class DioService extends Get.GetxService {
static late Dio _dio; static late Dio _dio;
List<String> whiteList = [Urls.login]; List<String> whiteList = [Urls.login];
BaseOptions dioBaseOptions = BaseOptions( BaseOptions dioBaseOptions = BaseOptions(
connectTimeout: const Duration(seconds: 5), connectTimeout: const Duration(seconds: 20),
baseUrl: '${GloablConfig.BASE_URL}', baseUrl: '${GloablConfig.BASE_URL}',
followRedirects: true); followRedirects: true);

View File

@ -2,7 +2,7 @@ import 'dart:convert';
import 'package:dio/dio.dart' as Dio; import 'package:dio/dio.dart' as Dio;
import 'package:get/get.dart'; 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/util/logger_util.dart';
import 'package:sk_base_mobile/widgets/tap_to_dismiss_keyboard.dart'; import 'package:sk_base_mobile/widgets/tap_to_dismiss_keyboard.dart';
import 'package:sk_base_mobile/models/auth.dart'; import 'package:sk_base_mobile/models/auth.dart';
@ -53,7 +53,6 @@ class AuthStore extends GetxController {
} }
Future<void> logout({bool force = false}) async { Future<void> logout({bool force = false}) async {
LoadingUtil.to.show(status: 'Logout...');
await StorageService.to.remove(CacheKeys.token, isWithUser: false); await StorageService.to.remove(CacheKeys.token, isWithUser: false);
await StorageService.to.remove(CacheKeys.userInfo, isWithUser: false); await StorageService.to.remove(CacheKeys.userInfo, isWithUser: false);
try { try {

View File

@ -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/constants/dict_enum.dart';
import 'package:sk_base_mobile/models/dict_item.model.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/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'; import 'package:sk_base_mobile/util/logger_util.dart';
const needCachedKey = [ const needCachedKey = [

View File

@ -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/app_theme.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.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(); static LoadingUtil get to => Get.find();
OverlayEntry? _loadingOverlay; OverlayEntry? _loadingOverlay;
Future<LoadingUtil> init() async { Future<LoadingUtil> init() async {
@ -53,8 +52,9 @@ class LoadingUtil extends GetxController {
)); ));
}, },
); );
if (Get.overlayContext != null) {
Overlay.of(Get.overlayContext!).insert(_loadingOverlay!); Overlay.of(Get.overlayContext!).insert(_loadingOverlay!);
}
} }
hideLoading() { hideLoading() {

View File

@ -1,6 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'package:dio/dio.dart' as Dio; 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:image_picker/image_picker.dart';
import 'package:sk_base_mobile/models/upload_result.model.dart'; import 'package:sk_base_mobile/models/upload_result.model.dart';
import 'package:sk_base_mobile/services/service.dart'; import 'package:sk_base_mobile/services/service.dart';
@ -15,7 +15,7 @@ class MediaUtil {
} }
AppInfoService.to.isCameraing.value = true; AppInfoService.to.isCameraing.value = true;
pickedFile = await ImagePicker() pickedFile = await ImagePicker()
.pickImage(source: ImageSource.camera, maxWidth: maxWidth ?? 400); .pickImage(source: ImageSource.camera, maxWidth: maxWidth ?? 2000);
AppInfoService.to.isCameraing.value = false; AppInfoService.to.isCameraing.value = false;
} catch (e) { } catch (e) {
AppInfoService.to.isCameraing.value = false; AppInfoService.to.isCameraing.value = false;

View File

@ -68,7 +68,7 @@ class ModalUtil {
); );
static Future<void> showGeneralDialog( static Future<void> showGeneralDialog(
{required Widget content, double? width}) { {required Widget content, double? width, double? height}) {
return Get.generalDialog( return Get.generalDialog(
barrierLabel: "productPicker", barrierLabel: "productPicker",
barrierDismissible: true, barrierDismissible: true,
@ -79,7 +79,7 @@ class ModalUtil {
borderRadius: BorderRadius.circular(ScreenAdaper.sp(30)), borderRadius: BorderRadius.circular(ScreenAdaper.sp(30)),
child: Material( child: Material(
child: SizedBox( child: SizedBox(
height: Get.height - ScreenAdaper.height(150), height: height ?? Get.height - ScreenAdaper.height(150),
width: width ?? Get.width - ScreenAdaper.width(150), width: width ?? Get.width - ScreenAdaper.width(150),
child: content, child: content,
)))); ))));

View File

@ -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<FileModel> galleryItems;
final Axis scrollDirection;
@override
State<ImagePreivew> createState() => _ImagePreivewState();
}
class _ImagePreivewState extends State<ImagePreivew> {
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: <Widget>[
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;
});
}
}

View File

@ -1,17 +1,23 @@
import 'package:flutter/cupertino.dart'; 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/app_theme.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart'; import 'package:sk_base_mobile/util/screen_adaper_util.dart';
class LoadingIndicator extends StatelessWidget { class LoadingIndicator extends StatelessWidget {
final bool animating; final bool animating;
const LoadingIndicator({super.key, this.animating = true}); final bool common;
const LoadingIndicator(
{super.key, this.animating = true, this.common = false});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CupertinoActivityIndicator( return common
animating: animating, ? LoadingAnimationWidget.fourRotatingDots(
color: AppTheme.primaryColor, color: AppTheme.primaryColorLight, size: ScreenAdaper.sp(40))
radius: ScreenAdaper.sp(20), : CupertinoActivityIndicator(
); animating: animating,
color: AppTheme.primaryColor,
radius: ScreenAdaper.sp(20),
);
} }
} }

View File

@ -1,5 +1,5 @@
import 'dart:io'; 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:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';

View File

@ -416,6 +416,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.1.7" 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: image_picker:
dependency: "direct main" dependency: "direct main"
description: description:
@ -680,6 +688,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.2" 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: platform:
dependency: transitive dependency: transitive
description: description:

View File

@ -58,6 +58,8 @@ dependencies:
path_provider: ^2.1.2 path_provider: ^2.1.2
flutter_typeahead: ^5.2.0 flutter_typeahead: ^5.2.0
decimal: ^2.3.3 decimal: ^2.3.3
photo_view: ^0.14.0
image_gallery_saver: ^2.0.3
dev_dependencies: dev_dependencies: