feat: inventory inout feature finsihed
This commit is contained in:
parent
7986fde5d3
commit
fc0187acf0
|
@ -36,7 +36,23 @@ Future<Response<PaginationData>> getProducts(Map params) {
|
|||
|
||||
// 分页获取原材料出入库列表
|
||||
Future<Response<PaginationData>> getInventoryInout(Map params) {
|
||||
return DioService.dio.get<PaginationData>(Urls.getInventoryInout,
|
||||
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)});
|
||||
}
|
||||
|
||||
|
@ -66,27 +82,16 @@ Future<Response> saveUserInfo(Map<String, dynamic> data) {
|
|||
return DioService.dio.post(Urls.saveUserInfo, data: data);
|
||||
}
|
||||
|
||||
Future<Response> uploadImg(File file) async {
|
||||
// DateTime dateTime = DateTime.now();
|
||||
// String fileName = file.path.split('/').last;
|
||||
// String format = fileName.split('.').last;
|
||||
// int imageTimeName = dateTime.millisecondsSinceEpoch +
|
||||
// (dateTime.microsecondsSinceEpoch ~/ 1000000);
|
||||
// String imageName = '$imageTimeName.$format';
|
||||
// String host = AppInfoService.to.ossPolicy.host!;
|
||||
// String dir = AppInfoService.to.ossPolicy.dir!;
|
||||
// final filePath = await MultipartFile.fromFile(file.path, filename: fileName);
|
||||
// final formData = FormData.fromMap({
|
||||
// 'ossaccessKeyId': AppInfoService.to.ossPolicy.accessKeyId,
|
||||
// 'policy': AppInfoService.to.ossPolicy.policy,
|
||||
// 'signature': AppInfoService.to.ossPolicy.signature,
|
||||
// 'callback': AppInfoService.to.ossPolicy.callback,
|
||||
// 'key': '$dir/$imageName',
|
||||
// 'file': filePath,
|
||||
// });
|
||||
|
||||
// return DioService.dio.post(host, data: formData);
|
||||
return {} as Response;
|
||||
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) {
|
||||
|
|
|
@ -4,10 +4,15 @@ enum BackActionEnum {
|
|||
|
||||
enum LoginEnum { apple, fastLogin }
|
||||
|
||||
enum InventoryInOrOutEnum { In, Out }
|
||||
class InventoryInOrOutEnum {
|
||||
static const int In = 0;
|
||||
static const int Out = 1;
|
||||
}
|
||||
|
||||
// 创建一个映射,将 InventoryInOrOutEnum 值与整数关联起来
|
||||
const Map<InventoryInOrOutEnum, int> InventoryInOrOutEnumValues = {
|
||||
InventoryInOrOutEnum.In: 0,
|
||||
InventoryInOrOutEnum.Out: 1,
|
||||
};
|
||||
class StorageBussinessModuleEnum {
|
||||
static const Contract = 'contract';
|
||||
static const Company = 'company';
|
||||
static const MaterialsInOut = 'materialsInOut';
|
||||
static const Product = 'product';
|
||||
static const Project = 'project';
|
||||
}
|
||||
|
|
|
@ -8,7 +8,9 @@ class Urls {
|
|||
static String getUserInfo = 'account/profile';
|
||||
static String getProjects = 'project';
|
||||
static String getProducts = 'product';
|
||||
static String getInventoryInout = 'materials-in-out';
|
||||
static String inventoryInout = 'materials-in-out';
|
||||
static String inventory = 'materials-inventory';
|
||||
static String updateAvatar = 'user/updateAvatar';
|
||||
static String getDictType = 'system/dict-type/all';
|
||||
static String uploadAttachemnt = 'tools/upload';
|
||||
}
|
||||
|
|
|
@ -4,4 +4,5 @@ class TextEnum {
|
|||
static const String backToTodayButtonText = '今天';
|
||||
static const String createInventoryInOutBtnText = '提交';
|
||||
static const String inventoryInOutDialogTitle = '出入库登记';
|
||||
static const String serverErrorMsg = ' 服务器繁忙,请重试或者联系管理员';
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
import 'package:sk_base_mobile/models/product.model.dart';
|
||||
import 'package:sk_base_mobile/models/project.model.dart';
|
||||
|
||||
class InventoryModel {
|
||||
InventoryModel({
|
||||
required this.id,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
required this.projectId,
|
||||
required this.productId,
|
||||
this.position,
|
||||
required this.quantity,
|
||||
required this.unitPrice,
|
||||
this.remark,
|
||||
required this.isDelete,
|
||||
required this.inventoryNumber,
|
||||
required this.project,
|
||||
required this.product,
|
||||
});
|
||||
|
||||
final int? id;
|
||||
final DateTime? createdAt;
|
||||
final DateTime? updatedAt;
|
||||
final int? projectId;
|
||||
final int? productId;
|
||||
final String? position;
|
||||
final int? quantity;
|
||||
final String? unitPrice;
|
||||
final dynamic remark;
|
||||
final int? isDelete;
|
||||
final String? inventoryNumber;
|
||||
final ProjectModel? project;
|
||||
final ProductModel? product;
|
||||
|
||||
factory InventoryModel.fromJson(Map<String, dynamic> json) {
|
||||
return InventoryModel(
|
||||
id: json["id"],
|
||||
createdAt: DateTime.tryParse(json["createdAt"] ?? ""),
|
||||
updatedAt: DateTime.tryParse(json["updatedAt"] ?? ""),
|
||||
projectId: json["projectId"],
|
||||
productId: json["productId"],
|
||||
position: json["position"],
|
||||
quantity: json["quantity"],
|
||||
unitPrice: json["unitPrice"],
|
||||
remark: json["remark"],
|
||||
isDelete: json["isDelete"],
|
||||
inventoryNumber: json["inventoryNumber"],
|
||||
project: json["project"] == null
|
||||
? null
|
||||
: ProjectModel.fromJson(json["project"]),
|
||||
product: json["product"] == null
|
||||
? null
|
||||
: ProductModel.fromJson(json["product"]),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"id": id,
|
||||
"createdAt": createdAt?.toIso8601String(),
|
||||
"updatedAt": updatedAt?.toIso8601String(),
|
||||
"projectId": projectId,
|
||||
"productId": productId,
|
||||
"position": position,
|
||||
"quantity": quantity,
|
||||
"unitPrice": unitPrice,
|
||||
"remark": remark,
|
||||
"isDelete": isDelete,
|
||||
"inventoryNumber": inventoryNumber,
|
||||
"project": project?.toJson(),
|
||||
"product": product?.toJson(),
|
||||
};
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
class UploadResultModel {
|
||||
UploadResultModel({
|
||||
required this.path,
|
||||
required this.id,
|
||||
});
|
||||
|
||||
final String? path;
|
||||
final int? id;
|
||||
|
||||
factory UploadResultModel.fromJson(Map<String, dynamic> json) {
|
||||
return UploadResultModel(
|
||||
path: json["path"],
|
||||
id: json["id"],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"path": path,
|
||||
"id": id,
|
||||
};
|
||||
}
|
|
@ -232,12 +232,12 @@ class InventoryInoutCard extends StatelessWidget {
|
|||
}
|
||||
|
||||
Widget buildInOrOut() {
|
||||
final bgColor = controller.list[ind][index].inOrOut ==
|
||||
InventoryInOrOutEnumValues[InventoryInOrOutEnum.In]
|
||||
final bgColor =
|
||||
controller.list[ind][index].inOrOut == InventoryInOrOutEnum.In
|
||||
? AppTheme.snackbarSuccessBackgroudColor
|
||||
: AppTheme.snackbarErrorBackgroudColor;
|
||||
final textInOut = controller.list[ind][index].inOrOut ==
|
||||
InventoryInOrOutEnumValues[InventoryInOrOutEnum.In]
|
||||
final textInOut =
|
||||
controller.list[ind][index].inOrOut == InventoryInOrOutEnum.In
|
||||
? '入'
|
||||
: '出';
|
||||
return Container(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.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/new_inventory_inout/new_inventory_inout.dart';
|
||||
import 'package:sk_base_mobile/util/date.util.dart';
|
||||
|
@ -33,14 +34,12 @@ class InventoryInoutController extends GetxController {
|
|||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
getTasks();
|
||||
// getInoutHistory();
|
||||
getData();
|
||||
}
|
||||
|
||||
Future<void> changePage(int index) async {
|
||||
LoggerUtil()
|
||||
.info(DateUtil.format(DateTime.now().add(Duration(days: -index))));
|
||||
|
||||
}
|
||||
|
||||
/// 打开出库还是入库选择框
|
||||
|
@ -80,7 +79,8 @@ class InventoryInoutController extends GetxController {
|
|||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
showInventoryInoutCreateDialog('in');
|
||||
showInventoryInoutCreateDialog(
|
||||
InventoryInOrOutEnum.In);
|
||||
},
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(
|
||||
|
@ -147,7 +147,8 @@ class InventoryInoutController extends GetxController {
|
|||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
showInventoryInoutCreateDialog('out');
|
||||
showInventoryInoutCreateDialog(
|
||||
InventoryInOrOutEnum.Out);
|
||||
},
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(
|
||||
|
@ -204,15 +205,17 @@ class InventoryInoutController extends GetxController {
|
|||
}
|
||||
|
||||
/// 打开新建库存出入库对话框
|
||||
Future<void> showInventoryInoutCreateDialog(String inOrOut) async {
|
||||
Future showInventoryInoutCreateDialog(int inOrOut) async {
|
||||
final isTablet = Responsive.isTablet(Get.context!);
|
||||
isTablet
|
||||
return isTablet
|
||||
? Get.generalDialog(
|
||||
barrierLabel: "CreateInventoryInout",
|
||||
barrierDismissible: true,
|
||||
transitionDuration: const Duration(milliseconds: 400),
|
||||
pageBuilder: (_, __, ___) {
|
||||
return NewInventoryInout();
|
||||
return NewInventoryInout(
|
||||
inOrOut: inOrOut,
|
||||
);
|
||||
},
|
||||
transitionBuilder: (_, anim, __, child) {
|
||||
Tween<Offset> tween;
|
||||
|
@ -224,7 +227,11 @@ class InventoryInoutController extends GetxController {
|
|||
child: child,
|
||||
);
|
||||
},
|
||||
)
|
||||
).then((value) {
|
||||
if (value != null) {
|
||||
getData();
|
||||
}
|
||||
})
|
||||
: showModalBottomSheet(
|
||||
elevation: 0,
|
||||
isScrollControlled: true,
|
||||
|
@ -250,7 +257,7 @@ class InventoryInoutController extends GetxController {
|
|||
// return queryResult.map((e) => TaskModel.fromMap(e)).toList();
|
||||
}
|
||||
|
||||
getTasks() async {
|
||||
getData() async {
|
||||
getInoutHistory().then((value) {
|
||||
model.value = value;
|
||||
getSepretLists();
|
||||
|
|
|
@ -0,0 +1,408 @@
|
|||
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/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;
|
||||
Function(InventoryModel)? beforeSelectedCheck;
|
||||
InventorySearch(
|
||||
{super.key, this.onInventorySelected, this.beforeSelectedCheck});
|
||||
final controller = Get.put(InventorySearchController());
|
||||
final listTitleTextStyle =
|
||||
TextStyle(fontSize: ScreenAdaper.sp(20), fontWeight: FontWeight.w600);
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: ScreenAdaper.width(20),
|
||||
vertical: ScreenAdaper.height(20)),
|
||||
child: Column(
|
||||
children: [
|
||||
buildSearchBar(),
|
||||
SizedBox(
|
||||
height: ScreenAdaper.height(defaultPadding) / 2,
|
||||
),
|
||||
Expanded(child: buildInventoryList())
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildSearchBar() {
|
||||
final doSearch = debouncer((String value) {
|
||||
controller.searchKey.value = value;
|
||||
controller.onRefresh();
|
||||
}, delayTime: 500);
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: TextField(
|
||||
controller: controller.searchBarTextConroller,
|
||||
onChanged: (value) => doSearch(value),
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
vertical: ScreenAdaper.height(10),
|
||||
horizontal: ScreenAdaper.width(10)),
|
||||
hintText: '名称,编号,规格',
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
prefixIcon: const Icon(Icons.search),
|
||||
// 当searchBarController有值时不显示
|
||||
suffixIcon: Obx(() => controller.searchKey.value.isEmpty
|
||||
? const SizedBox()
|
||||
: IconButton(
|
||||
icon: const Icon(Icons.clear),
|
||||
onPressed: () {
|
||||
controller.searchKey.value = '';
|
||||
controller.searchBarTextConroller.clear();
|
||||
doSearch('');
|
||||
},
|
||||
)),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
))),
|
||||
SizedBox(
|
||||
width: ScreenAdaper.width(20),
|
||||
),
|
||||
buildHasInventoryPicker()
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildHasInventoryPicker() {
|
||||
return Container(
|
||||
width: ScreenAdaper.width(150),
|
||||
constraints: BoxConstraints(minWidth: ScreenAdaper.width(150)),
|
||||
child: Obx(
|
||||
() => DropdownButtonFormField(
|
||||
value: controller.hasInventoryStatus.value,
|
||||
items: [
|
||||
DropdownMenuItem(
|
||||
child: Text('全部'),
|
||||
value: 0,
|
||||
),
|
||||
DropdownMenuItem(
|
||||
child: const Text('有库存'),
|
||||
value: 1,
|
||||
),
|
||||
DropdownMenuItem(
|
||||
child: const Text('无库存'),
|
||||
value: 2,
|
||||
),
|
||||
],
|
||||
onChanged: (value) {
|
||||
controller.hasInventoryStatus.value = value!;
|
||||
controller.onRefresh();
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
vertical: ScreenAdaper.height(15),
|
||||
horizontal: ScreenAdaper.width(15)),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15)),
|
||||
),
|
||||
)),
|
||||
));
|
||||
}
|
||||
|
||||
Widget buildInventoryList() {
|
||||
final textStyle = TextStyle(fontSize: ScreenAdaper.sp(22));
|
||||
return Obx(() => SmartRefresher(
|
||||
enablePullDown: true,
|
||||
enablePullUp: true,
|
||||
controller: controller.refreshController,
|
||||
onLoading: controller.onLoading,
|
||||
onRefresh: controller.onRefresh,
|
||||
child: Table(columnWidths: {
|
||||
0: FixedColumnWidth(100),
|
||||
1: FlexColumnWidth(2),
|
||||
2: MinColumnWidth(FixedColumnWidth(200), FixedColumnWidth(200)),
|
||||
3: MinColumnWidth(FixedColumnWidth(100), FixedColumnWidth(100)),
|
||||
4: MinColumnWidth(FixedColumnWidth(80), FixedColumnWidth(80)),
|
||||
}, children: [
|
||||
// table header
|
||||
TableRow(
|
||||
decoration: BoxDecoration(
|
||||
border:
|
||||
Border(bottom: BorderSide(color: AppTheme.dividerColor))),
|
||||
children: [
|
||||
TableCell(
|
||||
verticalAlignment: TableCellVerticalAlignment.middle,
|
||||
child: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
height: ScreenAdaper.height(60),
|
||||
child: Text(
|
||||
'库存编号',
|
||||
style: listTitleTextStyle,
|
||||
),
|
||||
)),
|
||||
TableCell(
|
||||
verticalAlignment: TableCellVerticalAlignment.middle,
|
||||
child: Text(
|
||||
'产品名称',
|
||||
style: listTitleTextStyle,
|
||||
)),
|
||||
TableCell(
|
||||
verticalAlignment: TableCellVerticalAlignment.middle,
|
||||
child: Text(
|
||||
'规格',
|
||||
style: listTitleTextStyle,
|
||||
),
|
||||
),
|
||||
TableCell(
|
||||
verticalAlignment: TableCellVerticalAlignment.middle,
|
||||
child: Text(
|
||||
'单价',
|
||||
style: listTitleTextStyle,
|
||||
),
|
||||
),
|
||||
TableCell(
|
||||
verticalAlignment: TableCellVerticalAlignment.middle,
|
||||
child: Text(
|
||||
'数量',
|
||||
textAlign: TextAlign.right,
|
||||
style: listTitleTextStyle,
|
||||
),
|
||||
),
|
||||
]),
|
||||
...controller.inventories.map((itemData) {
|
||||
return TableRow(
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(color: AppTheme.dividerColor))),
|
||||
children: [
|
||||
buildTableCell(
|
||||
Text(
|
||||
itemData.inventoryNumber!,
|
||||
style: textStyle,
|
||||
),
|
||||
itemData: itemData),
|
||||
buildTableCell(
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${itemData.product?.name}',
|
||||
style: textStyle,
|
||||
),
|
||||
Text(
|
||||
'${itemData.product?.company?.name}',
|
||||
style: TextStyle(
|
||||
fontSize: ScreenAdaper.sp(15),
|
||||
color: AppTheme.grey),
|
||||
)
|
||||
],
|
||||
),
|
||||
itemData: itemData),
|
||||
// 规格
|
||||
buildTableCell(
|
||||
Text(itemData.product?.productSpecification ?? '',
|
||||
style: textStyle),
|
||||
itemData: itemData),
|
||||
// 单价
|
||||
buildTableCell(
|
||||
Text(
|
||||
'¥${double.parse('${itemData.unitPrice}')}',
|
||||
style: textStyle,
|
||||
),
|
||||
itemData: itemData),
|
||||
|
||||
// 库存数量
|
||||
buildTableCell(
|
||||
Text(
|
||||
'${itemData.quantity}${itemData.product?.unit?.label ?? ''}',
|
||||
textAlign: TextAlign.right,
|
||||
style: textStyle),
|
||||
itemData: itemData,
|
||||
alignment: Alignment.centerRight),
|
||||
]);
|
||||
}).toList()
|
||||
])));
|
||||
}
|
||||
|
||||
Widget buildInventoryList1() {
|
||||
final textStyle = TextStyle(fontSize: ScreenAdaper.sp(22));
|
||||
return Obx(
|
||||
() => SmartRefresher(
|
||||
enablePullDown: true,
|
||||
enablePullUp: true,
|
||||
controller: controller.refreshController,
|
||||
onLoading: controller.onLoading,
|
||||
onRefresh: controller.onRefresh,
|
||||
child: ListView.separated(
|
||||
itemCount: controller.inventories.length,
|
||||
separatorBuilder: (context, index) => const Divider(
|
||||
height: 1,
|
||||
color: AppTheme.dividerColor,
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final itemData = controller.inventories[index];
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
if (onInventorySelected != null)
|
||||
onInventorySelected!(itemData);
|
||||
},
|
||||
child: Container(
|
||||
padding:
|
||||
EdgeInsets.symmetric(vertical: ScreenAdaper.height(10)),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
//最小宽度
|
||||
constraints:
|
||||
BoxConstraints(minWidth: ScreenAdaper.width(100)),
|
||||
child: Text(
|
||||
itemData.inventoryNumber!,
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: ScreenAdaper.width(10),
|
||||
),
|
||||
Expanded(
|
||||
flex: 8,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${itemData.product?.name}',
|
||||
style: textStyle,
|
||||
),
|
||||
Text(
|
||||
'${itemData.product?.company?.name}',
|
||||
style: TextStyle(
|
||||
fontSize: ScreenAdaper.sp(15),
|
||||
color: AppTheme.grey),
|
||||
)
|
||||
],
|
||||
)),
|
||||
SizedBox(
|
||||
width: ScreenAdaper.width(10),
|
||||
),
|
||||
// 规格
|
||||
Expanded(
|
||||
child: Container(
|
||||
constraints:
|
||||
BoxConstraints(minWidth: ScreenAdaper.width(100)),
|
||||
child: Text(
|
||||
itemData.product?.productSpecification ?? '',
|
||||
style: textStyle),
|
||||
)),
|
||||
SizedBox(
|
||||
width: ScreenAdaper.width(10),
|
||||
),
|
||||
// 单价
|
||||
Container(
|
||||
constraints:
|
||||
BoxConstraints(minWidth: ScreenAdaper.width(100)),
|
||||
child: Text(
|
||||
'¥${double.parse('${itemData.unitPrice}')}${double.parse('${itemData.unitPrice}')}${double.parse('${itemData.unitPrice}')}${double.parse('${itemData.unitPrice}')}',
|
||||
style: textStyle),
|
||||
),
|
||||
SizedBox(
|
||||
width: ScreenAdaper.width(10),
|
||||
),
|
||||
// 库存数量
|
||||
Expanded(
|
||||
child: Container(
|
||||
alignment: Alignment.centerRight,
|
||||
constraints:
|
||||
BoxConstraints(minWidth: ScreenAdaper.width(80)),
|
||||
child: Text(
|
||||
'${itemData.quantity}${itemData.quantity}${itemData.product?.unit?.label ?? ''}',
|
||||
style: textStyle),
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildTableCell(Widget child,
|
||||
{required InventoryModel itemData, AlignmentGeometry? alignment}) {
|
||||
return TableCell(
|
||||
verticalAlignment: TableCellVerticalAlignment.middle,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (beforeSelectedCheck != null) {
|
||||
if (!beforeSelectedCheck!(itemData)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (onInventorySelected != null) onInventorySelected!(itemData);
|
||||
},
|
||||
child: Container(
|
||||
constraints: BoxConstraints(minHeight: ScreenAdaper.height(80)),
|
||||
alignment: alignment ?? Alignment.centerLeft,
|
||||
child: child),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class InventorySearchController extends GetxController {
|
||||
RxList<InventoryModel> inventories = RxList([]);
|
||||
RxString searchKey = ''.obs;
|
||||
final searchBarTextConroller = TextEditingController();
|
||||
RefreshController refreshController = RefreshController(initialRefresh: true);
|
||||
int page = 1;
|
||||
int limit = 15;
|
||||
int total = 0;
|
||||
RxInt hasInventoryStatus = RxInt(1);
|
||||
Future<List<InventoryModel>> getData({bool isRefresh = false}) async {
|
||||
if (isRefresh == true) {
|
||||
page = 1;
|
||||
} else {
|
||||
page++;
|
||||
}
|
||||
final res = await Api.getInventory({
|
||||
'page': page,
|
||||
'pageSize': 15,
|
||||
'keyword': searchKey.value,
|
||||
'isCreateInout': true,
|
||||
'isHasInventory': hasInventoryStatus.value,
|
||||
});
|
||||
List<InventoryModel> newList =
|
||||
res.data!.items.map((e) => InventoryModel.fromJson(e)).toList();
|
||||
isRefresh == true
|
||||
? inventories.assignAll(newList)
|
||||
: inventories.addAll(newList);
|
||||
|
||||
return newList;
|
||||
}
|
||||
|
||||
Future<void> onRefresh() async {
|
||||
await getData(isRefresh: true).then((_) {
|
||||
refreshController.refreshCompleted(resetFooterState: true);
|
||||
}).catchError((_) {
|
||||
refreshController.refreshFailed();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> onLoading() async {
|
||||
await getData().then((_) {
|
||||
if (_.isEmpty) {
|
||||
refreshController.loadNoData();
|
||||
} else {
|
||||
refreshController.loadComplete();
|
||||
}
|
||||
}).catchError((_) {
|
||||
refreshController.loadFailed();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -4,14 +4,18 @@ import 'package:flutter/material.dart';
|
|||
import 'package:get/get.dart';
|
||||
import 'package:pull_to_refresh/pull_to_refresh.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 {
|
||||
ProductSearch({super.key});
|
||||
Function(ProductModel)? onProductSelected;
|
||||
ProductSearch({super.key, this.onProductSelected});
|
||||
final controller = Get.put(ProductSearchController());
|
||||
final listTitleTextStyle =
|
||||
TextStyle(fontSize: ScreenAdaper.sp(20), fontWeight: FontWeight.w600);
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
|
@ -19,20 +23,46 @@ class ProductSearch extends StatelessWidget {
|
|||
horizontal: ScreenAdaper.width(20),
|
||||
vertical: ScreenAdaper.height(20)),
|
||||
child: Column(
|
||||
children: [buildSearchBar(), Expanded(child: buildProductList())],
|
||||
children: [
|
||||
buildSearchBar(),
|
||||
SizedBox(
|
||||
height: ScreenAdaper.height(defaultPadding) / 2,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'产品编号',
|
||||
style: listTitleTextStyle,
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'名称',
|
||||
textAlign: TextAlign.center,
|
||||
style: listTitleTextStyle,
|
||||
)),
|
||||
Text(
|
||||
'规格',
|
||||
style: listTitleTextStyle,
|
||||
)
|
||||
],
|
||||
),
|
||||
Expanded(child: buildProductList())
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildSearchBar() {
|
||||
final doSearch = debouncer((_) {
|
||||
final doSearch = debouncer((String value) {
|
||||
controller.searchKey.value = value;
|
||||
controller.onRefresh();
|
||||
}, delayTime: 500);
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: controller.searchBarController,
|
||||
controller: controller.searchBarTextConroller,
|
||||
onChanged: (value) => doSearch(value),
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
|
@ -41,6 +71,17 @@ class ProductSearch extends StatelessWidget {
|
|||
hintText: '名称,编号',
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
prefixIcon: const Icon(Icons.search),
|
||||
// 当searchBarController有值时不显示
|
||||
suffixIcon: Obx(() => controller.searchKey.value.isEmpty
|
||||
? const SizedBox()
|
||||
: IconButton(
|
||||
icon: const Icon(Icons.clear),
|
||||
onPressed: () {
|
||||
controller.searchKey.value = '';
|
||||
controller.searchBarTextConroller.clear();
|
||||
doSearch('');
|
||||
},
|
||||
)),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
|
@ -66,16 +107,23 @@ class ProductSearch extends StatelessWidget {
|
|||
itemBuilder: (context, index) {
|
||||
final itemData = controller.products[index];
|
||||
return InkWell(
|
||||
onTap: () {},
|
||||
onTap: () {
|
||||
if (onProductSelected != null) onProductSelected!(itemData);
|
||||
},
|
||||
child: Container(
|
||||
padding:
|
||||
EdgeInsets.symmetric(vertical: ScreenAdaper.height(10)),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
Container(
|
||||
//最小宽度
|
||||
constraints:
|
||||
BoxConstraints(minWidth: ScreenAdaper.width(100)),
|
||||
child: Text(
|
||||
itemData.productNumber!,
|
||||
style: TextStyle(fontSize: ScreenAdaper.sp(20)),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: ScreenAdaper.width(10),
|
||||
),
|
||||
|
@ -95,6 +143,15 @@ class ProductSearch extends StatelessWidget {
|
|||
)
|
||||
],
|
||||
)),
|
||||
SizedBox(
|
||||
width: ScreenAdaper.width(10),
|
||||
),
|
||||
Container(
|
||||
//最小宽度
|
||||
constraints:
|
||||
BoxConstraints(minWidth: ScreenAdaper.width(100)),
|
||||
child: Text(itemData.productSpecification ?? ''),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -107,7 +164,8 @@ class ProductSearch extends StatelessWidget {
|
|||
|
||||
class ProductSearchController extends GetxController {
|
||||
RxList<ProductModel> products = RxList([]);
|
||||
TextEditingController searchBarController = TextEditingController(text: '');
|
||||
RxString searchKey = ''.obs;
|
||||
final searchBarTextConroller = TextEditingController();
|
||||
RefreshController refreshController = RefreshController(initialRefresh: true);
|
||||
int page = 1;
|
||||
int limit = 15;
|
||||
|
@ -119,7 +177,7 @@ class ProductSearchController extends GetxController {
|
|||
page++;
|
||||
}
|
||||
final res = await Api.getProducts(
|
||||
{'page': page, 'pageSize': 30, 'keyword': searchBarController.text});
|
||||
{'page': page, 'pageSize': 30, 'keyword': searchKey.value});
|
||||
List<ProductModel> newList =
|
||||
res.data!.items.map((e) => ProductModel.fromJson(e)).toList();
|
||||
isRefresh == true ? products.assignAll(newList) : products.addAll(newList);
|
||||
|
|
|
@ -1,34 +1,32 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:dropdown_search/dropdown_search.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sk_base_mobile/app_theme.dart';
|
||||
import 'package:sk_base_mobile/constants/constants.dart';
|
||||
import 'package:sk_base_mobile/constants/dict_enum.dart';
|
||||
import 'package:sk_base_mobile/models/index.dart';
|
||||
import 'package:sk_base_mobile/screens/new_inventory_inout/components/date_time.dart';
|
||||
import 'package:sk_base_mobile/models/inventory.model.dart';
|
||||
import 'package:sk_base_mobile/screens/new_inventory_inout/components/inventory_search.dart';
|
||||
import 'package:sk_base_mobile/screens/new_inventory_inout/components/product_search.dart';
|
||||
import 'package:sk_base_mobile/store/dict.store.dart';
|
||||
import 'package:sk_base_mobile/util/date.util.dart';
|
||||
import 'package:sk_base_mobile/util/modal.util.dart';
|
||||
import 'package:sk_base_mobile/widgets/core/zt_bottomsheet_picker.dart';
|
||||
import 'package:sk_base_mobile/util/util.dart';
|
||||
import 'package:sk_base_mobile/widgets/core/zt_number_input.dart';
|
||||
import 'package:sk_base_mobile/widgets/core/zt_search_select.dart';
|
||||
import 'package:sk_base_mobile/widgets/core/zk_date_picker.dart';
|
||||
import 'package:sk_base_mobile/widgets/core/zt_text_input.dart';
|
||||
import 'package:sk_base_mobile/widgets/empty.dart';
|
||||
import 'package:sk_base_mobile/widgets/gradient_button.dart';
|
||||
import 'package:sk_base_mobile/screens/new_inventory_inout/new_inventory_inout_controller.dart';
|
||||
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||||
|
||||
class NewInventoryInout extends StatelessWidget {
|
||||
final String inOrOut;
|
||||
NewInventoryInout({super.key, this.inOrOut = 'in'});
|
||||
|
||||
final controller = Get.put(NewInventoryInoutController());
|
||||
final NewInventoryInoutController controller;
|
||||
final dictService = Get.find<DictService>();
|
||||
final int inOrOut;
|
||||
NewInventoryInout({Key? key, this.inOrOut = InventoryInOrOutEnum.In})
|
||||
: controller = Get.put(NewInventoryInoutController(inOrOut: inOrOut)),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
|
@ -51,6 +49,7 @@ class NewInventoryInout extends StatelessWidget {
|
|||
children: [
|
||||
buildForm(),
|
||||
Obx(() => GradientButton(
|
||||
buttonText: TextEnum.createInventoryInOutBtnText,
|
||||
onPressed: () => {controller.create()},
|
||||
isLoading: controller.loading.value,
|
||||
))
|
||||
|
@ -65,15 +64,15 @@ class NewInventoryInout extends StatelessWidget {
|
|||
final children = [
|
||||
Row(children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: buildProjectPicker(),
|
||||
flex: 3,
|
||||
child: buildProductPicker(),
|
||||
),
|
||||
SizedBox(
|
||||
width: ScreenAdaper.width(formHorizontalGap),
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: buildProductPicker(),
|
||||
flex: 2,
|
||||
child: buildProjectPicker(),
|
||||
),
|
||||
]),
|
||||
SizedBox(
|
||||
|
@ -82,20 +81,36 @@ class NewInventoryInout extends StatelessWidget {
|
|||
Row(
|
||||
children: [
|
||||
Expanded(flex: 1, child: buildQuantity()),
|
||||
if (inOrOut == InventoryInOrOutEnum.In) ...[
|
||||
SizedBox(
|
||||
width: ScreenAdaper.width(formHorizontalGap),
|
||||
),
|
||||
Expanded(flex: 1, child: buildDatePicker()),
|
||||
Expanded(flex: 1, child: buildUnitPrice()),
|
||||
SizedBox(
|
||||
width: ScreenAdaper.width(formHorizontalGap),
|
||||
),
|
||||
Expanded(flex: 1, child: buildAgent()),
|
||||
Expanded(flex: 1, child: buildAmount())
|
||||
],
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: ScreenAdaper.height(formVerticalGap),
|
||||
),
|
||||
buildPositionBottomPicker(),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(flex: 1, child: buildDatePicker()),
|
||||
SizedBox(
|
||||
width: ScreenAdaper.width(formHorizontalGap),
|
||||
),
|
||||
Expanded(flex: 1, child: buildAgent()),
|
||||
if (inOrOut == InventoryInOrOutEnum.In) ...[
|
||||
SizedBox(
|
||||
width: ScreenAdaper.width(formHorizontalGap),
|
||||
),
|
||||
Expanded(flex: 1, child: buildPositionBottomPicker()),
|
||||
]
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: ScreenAdaper.height(formVerticalGap),
|
||||
),
|
||||
|
@ -128,6 +143,7 @@ class NewInventoryInout extends StatelessWidget {
|
|||
isRequired: true,
|
||||
textController: controller.projectTextController,
|
||||
labelText: '项目',
|
||||
hintText: inOrOut == InventoryInOrOutEnum.In ? '项目' : '请选择目标项目',
|
||||
itemBuilder: (_, itemData) => Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: ScreenAdaper.height(15),
|
||||
|
@ -151,167 +167,71 @@ class NewInventoryInout extends StatelessWidget {
|
|||
/// 产品
|
||||
Widget buildProductPicker() {
|
||||
return TextFormField(
|
||||
controller: controller.positionTextController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: '产品',
|
||||
controller: controller.productTextController,
|
||||
decoration: InputDecoration(
|
||||
hintText: '请选择产品',
|
||||
label: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"*",
|
||||
style: TextStyle(
|
||||
color: Colors.red, fontSize: ScreenAdaper.sp(30)),
|
||||
),
|
||||
Text(
|
||||
'产品',
|
||||
style: TextStyle(fontSize: ScreenAdaper.sp(30)),
|
||||
),
|
||||
]),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always),
|
||||
readOnly: true,
|
||||
onTap: () {
|
||||
Get.generalDialog(
|
||||
barrierLabel: "productPicker",
|
||||
barrierDismissible: true,
|
||||
transitionDuration: const Duration(milliseconds: 400),
|
||||
pageBuilder: (_, __, ___) {
|
||||
return Center(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
child: Material(
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(
|
||||
30,
|
||||
),
|
||||
topRight: Radius.circular(30)),
|
||||
child: Container(
|
||||
height: Get.height - ScreenAdaper.height(200),
|
||||
width: Get.width - ScreenAdaper.width(200),
|
||||
child: ProductSearch(),
|
||||
),
|
||||
))));
|
||||
if (inOrOut == InventoryInOrOutEnum.In) {
|
||||
ModalUtil.showGeneralDialog(content: ProductSearch(
|
||||
onProductSelected: (ProductModel product) {
|
||||
Get.back();
|
||||
String productName =
|
||||
'${product.productNumber} ${product.name!}';
|
||||
controller.payload['productId'] = product.id;
|
||||
if ((product.productSpecification ?? '').isNotEmpty) {
|
||||
productName += ' (${product.productSpecification})';
|
||||
}
|
||||
controller.productTextController.text = productName;
|
||||
},
|
||||
transitionBuilder: (_, anim, __, child) {
|
||||
Tween<Offset> tween;
|
||||
tween = Tween(begin: const Offset(0, 1), end: Offset.zero);
|
||||
return SlideTransition(
|
||||
position: tween.animate(
|
||||
CurvedAnimation(parent: anim, curve: Curves.easeInOut),
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
));
|
||||
} 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));
|
||||
}
|
||||
});
|
||||
// return DropdownSearch<ProductModel>(
|
||||
// asyncItems: (String filter) async {
|
||||
// return controller.getProducts(keyword: filter);
|
||||
// },
|
||||
|
||||
// dropdownDecoratorProps: const DropDownDecoratorProps(
|
||||
// dropdownSearchDecoration: InputDecoration(
|
||||
// labelText: "产品",
|
||||
// hintText: "请选择",
|
||||
// floatingLabelBehavior: FloatingLabelBehavior.always),
|
||||
// ),
|
||||
// popupProps: PopupProps.menu(
|
||||
// isFilterOnline: true,
|
||||
// searchFieldProps: TextFieldProps(
|
||||
// cursorColor: AppTheme.primaryColorLight,
|
||||
// decoration: InputDecoration(
|
||||
// contentPadding: EdgeInsets.symmetric(
|
||||
// vertical: ScreenAdaper.height(15),
|
||||
// horizontal: ScreenAdaper.width(10)))),
|
||||
// searchDelay: const Duration(milliseconds: 500),
|
||||
// emptyBuilder: (context, searchEntry) => const Empty(
|
||||
// text: '未找到',
|
||||
// ),
|
||||
// itemBuilder: (_, itemData, _1) => Container(
|
||||
// decoration: const BoxDecoration(
|
||||
// border: Border(top: BorderSide(color: AppTheme.dividerColor))),
|
||||
// padding: EdgeInsets.symmetric(
|
||||
// vertical: ScreenAdaper.height(5),
|
||||
// horizontal: ScreenAdaper.width(10)),
|
||||
// child: Row(
|
||||
// children: [
|
||||
// Text(
|
||||
// itemData.productNumber!,
|
||||
// style: TextStyle(fontSize: ScreenAdaper.sp(20)),
|
||||
// ),
|
||||
// SizedBox(
|
||||
// width: ScreenAdaper.width(10),
|
||||
// ),
|
||||
// Expanded(
|
||||
// child: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Text(
|
||||
// '${itemData.name}',
|
||||
// style: TextStyle(fontSize: ScreenAdaper.sp(20)),
|
||||
// ),
|
||||
// Text(
|
||||
// '${itemData.company?.name}',
|
||||
// style: TextStyle(
|
||||
// fontSize: ScreenAdaper.sp(15), color: AppTheme.grey),
|
||||
// )
|
||||
// ],
|
||||
// )),
|
||||
// // SizedBox(
|
||||
// // width: ScreenAdaper.width(10),
|
||||
// // ),
|
||||
// // Text(
|
||||
// // itemData.productNumber!,
|
||||
// // style: TextStyle(fontSize: ScreenAdaper.sp(20)),
|
||||
// // ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// showSearchBox: true,
|
||||
// ),
|
||||
// onChanged: print,
|
||||
// // selectedItem: "Brazil",
|
||||
// );
|
||||
// return ZtSearchSelect<ProductModel>(
|
||||
// isRequired: true,
|
||||
// textController: controller.productTextController,
|
||||
// labelText: '产品',
|
||||
// itemBuilder: (_, itemData) => Container(
|
||||
// padding: EdgeInsets.symmetric(
|
||||
// vertical: ScreenAdaper.height(5),
|
||||
// horizontal: ScreenAdaper.width(10)),
|
||||
// child: Row(
|
||||
// children: [
|
||||
// Text(
|
||||
// itemData.productNumber!,
|
||||
// style: TextStyle(fontSize: ScreenAdaper.sp(20)),
|
||||
// ),
|
||||
// SizedBox(
|
||||
// width: ScreenAdaper.width(10),
|
||||
// ),
|
||||
// Expanded(
|
||||
// child: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Text(
|
||||
// '${itemData.name}',
|
||||
// style: TextStyle(fontSize: ScreenAdaper.sp(20)),
|
||||
// ),
|
||||
// Text(
|
||||
// '${itemData.company?.name}',
|
||||
// style: TextStyle(
|
||||
// fontSize: ScreenAdaper.sp(15),
|
||||
// color: AppTheme.grey),
|
||||
// )
|
||||
// ],
|
||||
// )),
|
||||
// // SizedBox(
|
||||
// // width: ScreenAdaper.width(10),
|
||||
// // ),
|
||||
// // Text(
|
||||
// // itemData.productNumber!,
|
||||
// // style: TextStyle(fontSize: ScreenAdaper.sp(20)),
|
||||
// // ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// suggestionsCallback: (String keyword) {
|
||||
// return controller.getProducts(keyword: keyword);
|
||||
// },
|
||||
// onClear: () {
|
||||
// controller.payload.remove('productId');
|
||||
// },
|
||||
// onSelected: (ProductModel product) {
|
||||
// controller.productTextController.text = product.name ?? '';
|
||||
// controller.payload['productId'] = product.id;
|
||||
// });
|
||||
}
|
||||
|
||||
/// 时间
|
||||
|
@ -335,10 +255,35 @@ class NewInventoryInout extends StatelessWidget {
|
|||
return ZtNumberInput(
|
||||
textController: controller.quantityTextController,
|
||||
labelText: '数量',
|
||||
onChanged: (value) {
|
||||
controller.calculatePrice('quantity');
|
||||
},
|
||||
isRequired: true,
|
||||
);
|
||||
}
|
||||
|
||||
/// 单价
|
||||
Widget buildUnitPrice() {
|
||||
return ZtNumberInput(
|
||||
textController: controller.unitPriceTextController,
|
||||
labelText: '单价',
|
||||
onChanged: (value) {
|
||||
controller.calculatePrice('unitPrice');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// 金额
|
||||
Widget buildAmount() {
|
||||
return ZtNumberInput(
|
||||
textController: controller.amountTextController,
|
||||
labelText: '金额',
|
||||
onChanged: (value) {
|
||||
controller.calculatePrice('amount');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// 经办人
|
||||
Widget buildAgent() {
|
||||
return ZtTextInput(
|
||||
|
@ -405,23 +350,9 @@ class NewInventoryInout extends StatelessWidget {
|
|||
}
|
||||
// , ${secondLevelData[_1].label}, ${thirdtLevelData[_2].label}
|
||||
controller.positionTextController.text = position;
|
||||
controller.payload['position'] = position;
|
||||
});
|
||||
},
|
||||
onChanged: (_) {
|
||||
printInfo(info: _);
|
||||
});
|
||||
// return ZtTextInput(
|
||||
// textController: controller.agentTextController,
|
||||
// labelText: '库存位置',
|
||||
// isRequired: true,
|
||||
// onTap: () => ModalUtil.showBottomSheetPicker(
|
||||
// title: '库存位置',
|
||||
// firstLevel: dictService
|
||||
// .getDictItemsByCode(DictTypeEnum.InventoryRoom)
|
||||
// .map((e) => Text(e.label))
|
||||
// .toList(),
|
||||
// onChanged: () {}),
|
||||
// );
|
||||
}
|
||||
|
||||
/// 照片上传
|
||||
|
@ -434,7 +365,7 @@ class NewInventoryInout extends StatelessWidget {
|
|||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
'*请拍照上传产品照片',
|
||||
'*请拍照上传照片',
|
||||
style: TextStyle(
|
||||
fontSize: ScreenAdaper.sp(25),
|
||||
color: AppTheme.secondPrimaryColor),
|
||||
|
|
|
@ -1,26 +1,22 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
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/constants/enum.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';
|
||||
import 'package:sk_base_mobile/models/project.model.dart';
|
||||
import 'package:sk_base_mobile/models/task_model.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/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 {
|
||||
final formKey = GlobalKey<FormState>();
|
||||
|
||||
final int? inOrOut;
|
||||
DateTime? pickedDate;
|
||||
final DbHelper db = DbHelper();
|
||||
RxInt selectedImage = 0.obs;
|
||||
|
@ -42,12 +38,15 @@ class NewInventoryInoutController extends GetxController {
|
|||
final productTextController = TextEditingController();
|
||||
final dateTextController = TextEditingController();
|
||||
final quantityTextController = TextEditingController();
|
||||
final unitPriceTextController = TextEditingController();
|
||||
final amountTextController = TextEditingController();
|
||||
final agentTextController = TextEditingController();
|
||||
final remarkTextController = TextEditingController();
|
||||
final positionTextController = TextEditingController();
|
||||
final uploadImgFilesPath = <String>[].obs;
|
||||
final uploadScrollController = ScrollController();
|
||||
Map<String, dynamic> payload = {};
|
||||
NewInventoryInoutController({this.inOrOut});
|
||||
|
||||
Future<List<ProjectModel>> getProjects({String? keyword}) async {
|
||||
final res =
|
||||
|
@ -73,10 +72,47 @@ class NewInventoryInoutController extends GetxController {
|
|||
onReady() {
|
||||
super.onReady();
|
||||
dateTextController.text = DateUtil.format(DateTime.now());
|
||||
payload['time'] = DateUtil.format(DateTime.now());
|
||||
payload['inOrOut'] = inOrOut;
|
||||
}
|
||||
|
||||
/// 实时计算数量,单价,金额之间的关系
|
||||
void calculatePrice(String changedField) {
|
||||
Decimal quantity = Decimal.fromInt(0);
|
||||
Decimal unitPrice = Decimal.fromInt(0);
|
||||
Decimal amount = Decimal.fromInt(0);
|
||||
if (quantityTextController.text.isNotEmpty) {
|
||||
quantity = Decimal.parse(quantityTextController.text);
|
||||
}
|
||||
if (unitPriceTextController.text.isNotEmpty) {
|
||||
unitPrice = Decimal.parse(unitPriceTextController.text);
|
||||
}
|
||||
if (amountTextController.text.isNotEmpty) {
|
||||
amount = Decimal.parse(amountTextController.text);
|
||||
}
|
||||
if (InventoryInOrOutEnum.Out == inOrOut) {
|
||||
if (changedField != 'amount') {
|
||||
Decimal result = unitPrice * quantity;
|
||||
amountTextController.text =
|
||||
result != Decimal.zero ? result.toString() : '';
|
||||
}
|
||||
} else {
|
||||
// 入库一般是先输入总价和数量,然后计算单价
|
||||
if (changedField != 'unitPrice' && quantity != Decimal.zero) {
|
||||
Decimal result =
|
||||
(amount / quantity).toDecimal(scaleOnInfinitePrecision: 10);
|
||||
unitPriceTextController.text =
|
||||
result != Decimal.zero ? result.toString() : '';
|
||||
} else if (changedField != 'amount') {
|
||||
Decimal result = (unitPrice * quantity);
|
||||
amountTextController.text =
|
||||
result != Decimal.zero ? result.toString() : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 创建出入库记录
|
||||
create() {
|
||||
Future<void> create() async {
|
||||
if (payload['projectId'] == null) {
|
||||
SnackBarUtil().info(
|
||||
'项目不能为空',
|
||||
|
@ -109,7 +145,48 @@ class NewInventoryInoutController extends GetxController {
|
|||
);
|
||||
return;
|
||||
}
|
||||
LoggerUtil().info(payload);
|
||||
if (unitPriceTextController.text.isEmpty) {
|
||||
payload.remove('unitPrice');
|
||||
} else {
|
||||
payload['unitPrice'] = unitPriceTextController.text;
|
||||
}
|
||||
if (amountTextController.text.isEmpty) {
|
||||
payload.remove('amount');
|
||||
} else {
|
||||
payload['amount'] = amountTextController.text;
|
||||
}
|
||||
|
||||
payload['remark'] = remarkTextController.text;
|
||||
loading.value = true;
|
||||
// uploadImgFilesPath
|
||||
|
||||
try {
|
||||
// final recordId = res.data as int;
|
||||
// 批量同时上传
|
||||
final uploadRes = await Future.wait(uploadImgFilesPath
|
||||
.map((filePath) => MediaUtil().uploadImg(File(filePath),
|
||||
bussinessModule: StorageBussinessModuleEnum.MaterialsInOut,
|
||||
bussinessRecordId: null))
|
||||
.toList());
|
||||
List<int> fileIds = [];
|
||||
if (uploadRes.isNotEmpty) {
|
||||
fileIds = uploadRes.map((e) => e!.id!).toList();
|
||||
}
|
||||
final response = await Api.createInventoryInout(payload);
|
||||
// 保存照片
|
||||
if (fileIds.isNotEmpty) {
|
||||
await Api.updateInventoryInout(
|
||||
response.data, {'fileIds': uploadRes.map((e) => e!.id).toList()});
|
||||
}
|
||||
Get.back(result: true);
|
||||
SnackBarUtil().success(
|
||||
'提交成功',
|
||||
);
|
||||
} catch (e) {
|
||||
LoggerUtil().error(e);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> photoPicker() async {
|
||||
|
@ -250,7 +327,7 @@ class NewInventoryInoutController extends GetxController {
|
|||
// .then((value) {
|
||||
// Duration dif = pickedDate!.difference(DateTime(
|
||||
// DateTime.now().year, DateTime.now().month, DateTime.now().day));
|
||||
// inventoryInoutController.list[dif.inDays].add(value);
|
||||
// inventoryInoutlist[dif.inDays].add(value);
|
||||
// Timer(const Duration(seconds: 1), () {
|
||||
// loading.value = false;
|
||||
// Get.back();
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:get/get.dart' as Get;
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:sk_base_mobile/models/base_response.dart';
|
||||
|
@ -42,9 +41,10 @@ class DioService extends Get.GetxService {
|
|||
}
|
||||
if (e.type == DioExceptionType.connectionTimeout) {
|
||||
// It occurs when url is opened timeout.
|
||||
await SnackBarUtil().error(
|
||||
'The server is busy, please try again.',
|
||||
);
|
||||
await SnackBarUtil().error(TextEnum.serverErrorMsg);
|
||||
} else if (e.type == DioExceptionType.connectionError) {
|
||||
// It occurs when url is opened timeout.
|
||||
await SnackBarUtil().error(TextEnum.serverErrorMsg);
|
||||
} else if (e.type == DioExceptionType.sendTimeout) {
|
||||
// It occurs when url is sent timeout.
|
||||
// await SnackBarUtil().error(
|
||||
|
@ -134,18 +134,18 @@ class DioService extends Get.GetxService {
|
|||
return;
|
||||
}
|
||||
if (response.data != null) {
|
||||
try {
|
||||
if (response.data['code'] == 200) {
|
||||
if (GloablConfig.DEBUG) LoggerUtil().info(response.data['data']);
|
||||
response.data = response.data['data'];
|
||||
|
||||
// 分页数据处理
|
||||
if (response.data['meta'] != null && response.data['items'] != null) {
|
||||
if (response.data != null &&
|
||||
response.data is Map &&
|
||||
response.data['meta'] != null &&
|
||||
response.data['items'] != null) {
|
||||
response.data = PaginationData.fromJson(response.data);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
printError(info: e.toString());
|
||||
} else {
|
||||
await SnackBarUtil().error(response.data['message']);
|
||||
}
|
||||
}
|
||||
handler.next(response);
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:io';
|
|||
import 'package:dio/dio.dart' as Dio;
|
||||
import 'package:sk_base_mobile/apis/api.dart' as Api;
|
||||
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';
|
||||
|
||||
class MediaUtil {
|
||||
|
@ -35,12 +36,13 @@ class MediaUtil {
|
|||
return pickedFile;
|
||||
}
|
||||
|
||||
Future<String> uploadImg(File imgfile) async {
|
||||
Future<UploadResultModel?> uploadImg(File imgfile,
|
||||
{int? bussinessRecordId, String? bussinessModule}) async {
|
||||
Dio.Response response = await Api.uploadImg(imgfile);
|
||||
if (response.data != null && response.data['data']['filename'] != null) {
|
||||
return response.data["data"]["filename"];
|
||||
if (response.data != null && response.data['filename'] != null) {
|
||||
return UploadResultModel.fromJson(response.data["filename"]);
|
||||
}
|
||||
return '';
|
||||
return null;
|
||||
}
|
||||
|
||||
// // 上传一张照片
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||||
import 'package:sk_base_mobile/widgets/core/zt_bottomsheet_picker.dart';
|
||||
|
||||
class ModalUtil {
|
||||
|
@ -65,4 +66,34 @@ class ModalUtil {
|
|||
popupHeight: popupHeight ?? 400);
|
||||
},
|
||||
);
|
||||
|
||||
static Future<void> showGeneralDialog(
|
||||
{required Widget content, double? width}) {
|
||||
return Get.generalDialog(
|
||||
barrierLabel: "productPicker",
|
||||
barrierDismissible: true,
|
||||
transitionDuration: const Duration(milliseconds: 400),
|
||||
pageBuilder: (_, __, ___) {
|
||||
return Center(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(ScreenAdaper.sp(30)),
|
||||
child: Material(
|
||||
child: SizedBox(
|
||||
height: Get.height - ScreenAdaper.height(150),
|
||||
width: width ?? Get.width - ScreenAdaper.width(150),
|
||||
child: content,
|
||||
))));
|
||||
},
|
||||
transitionBuilder: (_, anim, __, child) {
|
||||
Tween<Offset> tween;
|
||||
tween = Tween(begin: const Offset(0, 1), end: Offset.zero);
|
||||
return SlideTransition(
|
||||
position: tween.animate(
|
||||
CurvedAnimation(parent: anim, curve: Curves.easeInOut),
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,13 +9,16 @@ import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
|||
class ZtNumberInput extends StatelessWidget {
|
||||
final TextEditingController textController;
|
||||
final VoidCallback? onTap;
|
||||
final Function(String)? onChanged;
|
||||
|
||||
final bool isRequired;
|
||||
final String labelText;
|
||||
final String? hint;
|
||||
ZtNumberInput(
|
||||
const ZtNumberInput(
|
||||
{super.key,
|
||||
required this.textController,
|
||||
this.onTap,
|
||||
this.onChanged,
|
||||
this.hint,
|
||||
this.isRequired = false,
|
||||
this.labelText = ''});
|
||||
|
@ -26,15 +29,16 @@ class ZtNumberInput extends StatelessWidget {
|
|||
onTapOutside: (event) {
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
onChanged: onChanged ?? (_) {},
|
||||
onTap: onTap ?? () {},
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: ScreenAdaper.sp(30)),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
FilteringTextInputFormatter.digitsOnly // 限制输入内容只能是数字
|
||||
// 限制输入内容只能是数字和小数点不限制位数
|
||||
FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,10}')),
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(vertical: ScreenAdaper.height(18)),
|
||||
// contentPadding: EdgeInsets.symmetric(vertical: ScreenAdaper.height(18)),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
label: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
|
|
|
@ -10,6 +10,8 @@ class ZtSearchSelect<T> extends StatelessWidget {
|
|||
final void Function(T)? onSelected;
|
||||
final VoidCallback? onClear;
|
||||
final String labelText;
|
||||
final String hintText;
|
||||
|
||||
final bool isRequired;
|
||||
final Widget Function(BuildContext, T) itemBuilder;
|
||||
const ZtSearchSelect(
|
||||
|
@ -18,6 +20,7 @@ class ZtSearchSelect<T> extends StatelessWidget {
|
|||
required this.labelText,
|
||||
required this.itemBuilder,
|
||||
required this.suggestionsCallback,
|
||||
this.hintText = '请选择',
|
||||
this.onSelected,
|
||||
this.onClear,
|
||||
this.isRequired = false});
|
||||
|
@ -61,7 +64,7 @@ class ZtSearchSelect<T> extends StatelessWidget {
|
|||
)
|
||||
: const SizedBox(),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
hintText: '请选择',
|
||||
hintText: hintText,
|
||||
label: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
|
|
@ -42,7 +42,7 @@ class _FadeInCacheImageState extends State<FadeInCacheImage> {
|
|||
theContext = context;
|
||||
|
||||
if ((widget.url == null || widget.url == '' || widget.url == 'null')) {
|
||||
return Container(
|
||||
return SizedBox(
|
||||
width: widget.width,
|
||||
height: widget.height,
|
||||
child: Icon(Icons.image_not_supported,
|
||||
|
@ -51,7 +51,6 @@ class _FadeInCacheImageState extends State<FadeInCacheImage> {
|
|||
}
|
||||
|
||||
return buildImg(widget.url);
|
||||
;
|
||||
}
|
||||
|
||||
// Future getImg() {
|
||||
|
@ -66,13 +65,15 @@ class _FadeInCacheImageState extends State<FadeInCacheImage> {
|
|||
height: widget.height,
|
||||
fit: widget.fit,
|
||||
placeholder: (context, url) => Container(
|
||||
decoration: BoxDecoration(color: AppTheme.grey),
|
||||
child: CupertinoActivityIndicator(),
|
||||
decoration: const BoxDecoration(color: AppTheme.grey),
|
||||
child: const CupertinoActivityIndicator(),
|
||||
),
|
||||
errorWidget: (context, error, stackTrace) => SizedBox(
|
||||
width: widget.width,
|
||||
height: widget.height,
|
||||
child: Icon(Icons.image_not_supported,
|
||||
size: ScreenAdaper.sp(100), color: AppTheme.grey),
|
||||
),
|
||||
errorWidget: (context, error, stackTrace) => Image(
|
||||
image: AssetImage(
|
||||
'assets/images/deer_detail_banner1.jpg',
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:sk_base_mobile/apis/api.dart' as Api;
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:sk_base_mobile/models/upload_result.model.dart';
|
||||
import 'package:sk_base_mobile/store/auth.store.dart';
|
||||
import 'package:sk_base_mobile/util/media_util.dart';
|
||||
import 'package:sk_base_mobile/util/photo_picker_util.dart';
|
||||
|
@ -83,15 +84,15 @@ class MyAvatarController extends GetxController {
|
|||
await PhotoPickerUtil().showPicker(callback: (XFile pickedFile) async {
|
||||
await LoadingUtil.to.show(status: 'Uploading...');
|
||||
try {
|
||||
String? filename = await MediaUtil().uploadImg(File(pickedFile.path));
|
||||
if (filename.isNotEmpty) {
|
||||
final res = await Api.updateAvatar(filename);
|
||||
if (res.data != null) {
|
||||
SnackBarUtil().success('Update avatar successfully');
|
||||
}
|
||||
}
|
||||
uploadImgFilePath(pickedFile.path);
|
||||
Get.back();
|
||||
// UploadResultModel? res = await MediaUtil().uploadImg(File(pickedFile.path));
|
||||
// if (res.pa.isNotEmpty) {
|
||||
// final res = await Api.updateAvatar(filename);
|
||||
// if (res.data != null) {
|
||||
// SnackBarUtil().success('Update avatar successfully');
|
||||
// }
|
||||
// }
|
||||
// uploadImgFilePath(pickedFile.path);
|
||||
// Get.back();
|
||||
} catch (e) {
|
||||
SnackBarUtil().error('Update avatar failed.');
|
||||
} finally {
|
||||
|
|
24
pubspec.lock
24
pubspec.lock
|
@ -169,6 +169,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.7"
|
||||
decimal:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: decimal
|
||||
sha256: "24a261d5d5c87e86c7651c417a5dbdf8bcd7080dd592533910e8d0505a279f21"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
dio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -177,14 +185,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.4.1"
|
||||
dropdown_search:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dropdown_search
|
||||
sha256: "55106e8290acaa97ed15bea1fdad82c3cf0c248dd410e651f5a8ac6870f783ab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.6"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -744,6 +744,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
rational:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rational
|
||||
sha256: ba58e9e18df9abde280e8b10051e4bce85091e41e8e7e411b6cde2e738d357cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -57,7 +57,7 @@ dependencies:
|
|||
path: ^1.8.3
|
||||
path_provider: ^2.1.2
|
||||
flutter_typeahead: ^5.2.0
|
||||
dropdown_search: ^5.0.6
|
||||
decimal: ^2.3.3
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
|
|
Loading…
Reference in New Issue