feat: develop create inventory inout history dialog and develop core common form field.
This commit is contained in:
parent
09385a4d77
commit
f725a72a9d
|
@ -21,12 +21,18 @@ Future<Response> 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> logout() {
|
||||
return DioService.dio.post(
|
||||
Urls.logout,
|
||||
|
|
|
@ -23,11 +23,50 @@ class AppTheme {
|
|||
static const String fontName = 'NotoSans';
|
||||
static const Color activeNavigationBarColor = primaryColor;
|
||||
static const Color notActiveNavigationBarColor = Colors.grey;
|
||||
static const Color dividerColor = Color.fromARGB(255, 197, 197, 197);
|
||||
static const Color dividerColor = Color.fromARGB(255, 224, 224, 224);
|
||||
}
|
||||
|
||||
final theme = ThemeData(
|
||||
primarySwatch: MaterialColor(AppTheme.primaryColor.value, const {
|
||||
50: AppTheme.primaryColorLight,
|
||||
100: AppTheme.primaryColorLight,
|
||||
200: AppTheme.primaryColorLight,
|
||||
300: AppTheme.primaryColorLight,
|
||||
400: AppTheme.primaryColorLight,
|
||||
500: AppTheme.primaryColor,
|
||||
600: AppTheme.primaryColorDark,
|
||||
700: AppTheme.primaryColorDark,
|
||||
800: AppTheme.primaryColorDark,
|
||||
900: AppTheme.primaryColorDark,
|
||||
}),
|
||||
fontFamily: AppTheme.fontName,
|
||||
datePickerTheme: DatePickerThemeData(
|
||||
confirmButtonStyle: ButtonStyle(
|
||||
textStyle: MaterialStateProperty.resolveWith<TextStyle?>(
|
||||
(Set<MaterialState> states) {
|
||||
return TextStyle(color: AppTheme.primaryColor);
|
||||
},
|
||||
),
|
||||
),
|
||||
todayBorder: const BorderSide(color: AppTheme.primaryColor),
|
||||
todayBackgroundColor: MaterialStateProperty.resolveWith<Color?>(
|
||||
(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
return AppTheme.primaryColor;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
rangeSelectionBackgroundColor: AppTheme.primaryColor,
|
||||
dayBackgroundColor: MaterialStateProperty.resolveWith<Color?>(
|
||||
(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
return AppTheme.primaryColor;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
primaryColor: AppTheme.primaryColor,
|
||||
primaryColorDark: AppTheme.primaryColorDark,
|
||||
|
@ -41,7 +80,7 @@ final theme = ThemeData(
|
|||
unselectedLabelStyle: TextStyle(fontSize: ScreenAdaper.sp(20)),
|
||||
selectedLabelStyle: TextStyle(fontSize: ScreenAdaper.sp(20)),
|
||||
selectedItemColor: AppTheme.primaryColor),
|
||||
tabBarTheme: TabBarTheme(
|
||||
tabBarTheme: const TabBarTheme(
|
||||
indicator: BoxDecoration(
|
||||
border:
|
||||
Border(bottom: BorderSide(width: 3, color: AppTheme.primaryColorDark)),
|
||||
|
@ -67,7 +106,7 @@ final theme = ThemeData(
|
|||
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15))),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15)),
|
||||
borderSide: BorderSide(
|
||||
borderSide: const BorderSide(
|
||||
color: AppTheme.primaryColorLight, width: 2), // 选中时的边框颜色和宽度
|
||||
),
|
||||
),
|
||||
|
|
|
@ -7,5 +7,6 @@ class Urls {
|
|||
static String saveUserInfo = 'user/saveUserInfo';
|
||||
static String getUserInfo = 'account/profile';
|
||||
static String getProjects = 'project';
|
||||
static String getProducts = 'product';
|
||||
static String updateAvatar = 'user/updateAvatar';
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import 'file.model.dart';
|
||||
|
||||
class CompanyModel {
|
||||
CompanyModel({
|
||||
required this.id,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
required this.name,
|
||||
required this.isDelete,
|
||||
required this.files,
|
||||
});
|
||||
|
||||
final int? id;
|
||||
final DateTime? createdAt;
|
||||
final DateTime? updatedAt;
|
||||
final String? name;
|
||||
final int? isDelete;
|
||||
final List<FileModel> files;
|
||||
|
||||
factory CompanyModel.fromJson(Map<String, dynamic> json) {
|
||||
return CompanyModel(
|
||||
id: json["id"],
|
||||
createdAt: DateTime.tryParse(json["createdAt"] ?? ""),
|
||||
updatedAt: DateTime.tryParse(json["updatedAt"] ?? ""),
|
||||
name: json["name"],
|
||||
isDelete: json["isDelete"],
|
||||
files: json["files"] == null
|
||||
? []
|
||||
: List<FileModel>.from(
|
||||
json["files"]!.map((x) => FileModel.fromJson(x))),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"id": id,
|
||||
"createdAt": createdAt?.toIso8601String(),
|
||||
"updatedAt": updatedAt?.toIso8601String(),
|
||||
"name": name,
|
||||
"isDelete": isDelete,
|
||||
"files": files.map((x) => x?.toJson()).toList(),
|
||||
};
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
class DictItemModel {
|
||||
DictItemModel({
|
||||
required this.id,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
required this.creator,
|
||||
required this.updater,
|
||||
required this.label,
|
||||
required this.value,
|
||||
required this.orderNo,
|
||||
required this.status,
|
||||
required this.remark,
|
||||
});
|
||||
|
||||
final int? id;
|
||||
final DateTime? createdAt;
|
||||
final DateTime? updatedAt;
|
||||
final String? creator;
|
||||
final String? updater;
|
||||
final String? label;
|
||||
final String? value;
|
||||
final int? orderNo;
|
||||
final int? status;
|
||||
final String? remark;
|
||||
|
||||
factory DictItemModel.fromJson(Map<String, dynamic> json) {
|
||||
return DictItemModel(
|
||||
id: json["id"],
|
||||
createdAt: DateTime.tryParse(json["createdAt"] ?? ""),
|
||||
updatedAt: DateTime.tryParse(json["updatedAt"] ?? ""),
|
||||
creator: json["creator"],
|
||||
updater: json["updater"],
|
||||
label: json["label"],
|
||||
value: json["value"],
|
||||
orderNo: json["orderNo"],
|
||||
status: json["status"],
|
||||
remark: json["remark"],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"id": id,
|
||||
"createdAt": createdAt?.toIso8601String(),
|
||||
"updatedAt": updatedAt?.toIso8601String(),
|
||||
"creator": creator,
|
||||
"updater": updater,
|
||||
"label": label,
|
||||
"value": value,
|
||||
"orderNo": orderNo,
|
||||
"status": status,
|
||||
"remark": remark,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
class FileModel {
|
||||
FileModel({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.extName,
|
||||
required this.path,
|
||||
required this.type,
|
||||
required this.size,
|
||||
required this.createdAt,
|
||||
required this.username,
|
||||
required this.bussinessRecordId,
|
||||
required this.bussinessModule,
|
||||
});
|
||||
|
||||
final int? id;
|
||||
final String? name;
|
||||
final String? extName;
|
||||
final String? path;
|
||||
final String? type;
|
||||
final String? size;
|
||||
final DateTime? createdAt;
|
||||
final String? username;
|
||||
final int? bussinessRecordId;
|
||||
final String? bussinessModule;
|
||||
|
||||
factory FileModel.fromJson(Map<String, dynamic> json) {
|
||||
return FileModel(
|
||||
id: json["id"],
|
||||
name: json["name"],
|
||||
extName: json["extName"],
|
||||
path: json["path"],
|
||||
type: json["type"],
|
||||
size: json["size"],
|
||||
createdAt: DateTime.tryParse(json["createdAt"] ?? ""),
|
||||
username: json["username"],
|
||||
bussinessRecordId: json["bussinessRecordId"],
|
||||
bussinessModule: json["bussinessModule"],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"id": id,
|
||||
"name": name,
|
||||
"extName": extName,
|
||||
"path": path,
|
||||
"type": type,
|
||||
"size": size,
|
||||
"createdAt": createdAt?.toIso8601String(),
|
||||
"username": username,
|
||||
"bussinessRecordId": bussinessRecordId,
|
||||
"bussinessModule": bussinessModule,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
library models;
|
||||
|
||||
export './project.model.dart';
|
||||
export './product.model.dart';
|
||||
export './dict_item.model.dart';
|
||||
export './company.model.dart';
|
|
@ -0,0 +1,49 @@
|
|||
class InventoryInOutCreateModel {
|
||||
InventoryInOutCreateModel({
|
||||
required this.inOrOut,
|
||||
required this.productId,
|
||||
required this.projectId,
|
||||
required this.time,
|
||||
required this.quantity,
|
||||
required this.unitPrice,
|
||||
required this.amount,
|
||||
required this.agent,
|
||||
this.remark,
|
||||
});
|
||||
|
||||
final int? inOrOut;
|
||||
final int? productId;
|
||||
final int? projectId;
|
||||
final DateTime? time;
|
||||
final int? quantity;
|
||||
final int? unitPrice;
|
||||
final int? amount;
|
||||
final String? agent;
|
||||
final String? remark;
|
||||
|
||||
factory InventoryInOutCreateModel.fromJson(Map<String, dynamic> json) {
|
||||
return InventoryInOutCreateModel(
|
||||
inOrOut: json["inOrOut"],
|
||||
productId: json["productId"],
|
||||
projectId: json["projectId"],
|
||||
time: DateTime.tryParse(json["time"] ?? ""),
|
||||
quantity: json["quantity"],
|
||||
unitPrice: json["unitPrice"],
|
||||
amount: json["amount"],
|
||||
agent: json["agent"],
|
||||
remark: json["remark"],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"inOrOut": inOrOut,
|
||||
"productId": productId,
|
||||
"projectId": projectId,
|
||||
"time": time?.toIso8601String(),
|
||||
"quantity": quantity,
|
||||
"unitPrice": unitPrice,
|
||||
"amount": amount,
|
||||
"agent": agent,
|
||||
"remark": remark,
|
||||
};
|
||||
}
|
|
@ -1,129 +1,71 @@
|
|||
import 'package:sk_base_mobile/models/dict_item.model.dart';
|
||||
import 'package:sk_base_mobile/models/file.model.dart';
|
||||
|
||||
import 'company.model.dart';
|
||||
|
||||
class ProductModel {
|
||||
int? id;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
String? name;
|
||||
String? remark;
|
||||
int? isDelete;
|
||||
int? companyId;
|
||||
int? unitId;
|
||||
String? namePinyin;
|
||||
List<Files>? files;
|
||||
Company? company;
|
||||
Unit? unit;
|
||||
|
||||
ProductModel(
|
||||
{this.id,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.name,
|
||||
this.remark,
|
||||
this.isDelete,
|
||||
this.companyId,
|
||||
this.unitId,
|
||||
this.namePinyin,
|
||||
this.files,
|
||||
this.company,
|
||||
this.unit});
|
||||
|
||||
ProductModel.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
name = json['name'];
|
||||
remark = json['remark'];
|
||||
isDelete = json['isDelete'];
|
||||
companyId = json['companyId'];
|
||||
unitId = json['unitId'];
|
||||
namePinyin = json['namePinyin'];
|
||||
if (json['files'] != null) {
|
||||
files = <Files>[];
|
||||
json['files'].forEach((v) {
|
||||
files!.add(new Files.fromJson(v));
|
||||
ProductModel({
|
||||
required this.id,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
required this.name,
|
||||
required this.remark,
|
||||
required this.isDelete,
|
||||
required this.companyId,
|
||||
required this.unitId,
|
||||
required this.namePinyin,
|
||||
required this.files,
|
||||
required this.company,
|
||||
required this.unit,
|
||||
});
|
||||
}
|
||||
company =
|
||||
json['company'] != null ? new Company.fromJson(json['company']) : null;
|
||||
unit = json['unit'] != null ? new Unit.fromJson(json['unit']) : null;
|
||||
|
||||
final int? id;
|
||||
final DateTime? createdAt;
|
||||
final DateTime? updatedAt;
|
||||
final String? name;
|
||||
final String? remark;
|
||||
final int? isDelete;
|
||||
final int? companyId;
|
||||
final int? unitId;
|
||||
final String? namePinyin;
|
||||
final List<FileModel> files;
|
||||
final CompanyModel? company;
|
||||
final DictItemModel? unit;
|
||||
|
||||
factory ProductModel.fromJson(Map<String, dynamic> json) {
|
||||
return ProductModel(
|
||||
id: json["id"],
|
||||
createdAt: DateTime.tryParse(json["createdAt"] ?? ""),
|
||||
updatedAt: DateTime.tryParse(json["updatedAt"] ?? ""),
|
||||
name: json["name"],
|
||||
remark: json["remark"],
|
||||
isDelete: json["isDelete"],
|
||||
companyId: json["companyId"],
|
||||
unitId: json["unitId"],
|
||||
namePinyin: json["namePinyin"],
|
||||
files: json["files"] == null
|
||||
? []
|
||||
: List<FileModel>.from(
|
||||
json["files"]!.map((x) => FileModel.fromJson(x))),
|
||||
company: json["company"] == null
|
||||
? null
|
||||
: CompanyModel.fromJson(json["company"]),
|
||||
unit: json["unit"] == null ? null : DictItemModel.fromJson(json["unit"]),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['id'] = this.id;
|
||||
data['createdAt'] = this.createdAt;
|
||||
data['updatedAt'] = this.updatedAt;
|
||||
data['name'] = this.name;
|
||||
data['remark'] = this.remark;
|
||||
data['isDelete'] = this.isDelete;
|
||||
data['companyId'] = this.companyId;
|
||||
data['unitId'] = this.unitId;
|
||||
data['namePinyin'] = this.namePinyin;
|
||||
if (this.files != null) {
|
||||
data['files'] = this.files!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
if (this.company != null) {
|
||||
data['company'] = this.company!.toJson();
|
||||
}
|
||||
if (this.unit != null) {
|
||||
data['unit'] = this.unit!.toJson();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class Files {
|
||||
int? id;
|
||||
String? path;
|
||||
|
||||
Files({this.id, this.path});
|
||||
|
||||
Files.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
path = json['path'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['id'] = this.id;
|
||||
data['path'] = this.path;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class Company {
|
||||
int? id;
|
||||
String? name;
|
||||
|
||||
Company({this.id, this.name});
|
||||
|
||||
Company.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
name = json['name'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['id'] = this.id;
|
||||
data['name'] = this.name;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class Unit {
|
||||
int? id;
|
||||
String? label;
|
||||
|
||||
Unit({this.id, this.label});
|
||||
|
||||
Unit.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
label = json['label'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['id'] = this.id;
|
||||
data['label'] = this.label;
|
||||
return data;
|
||||
}
|
||||
Map<String, dynamic> toJson() => {
|
||||
"id": id,
|
||||
"createdAt": createdAt?.toIso8601String(),
|
||||
"updatedAt": updatedAt?.toIso8601String(),
|
||||
"name": name,
|
||||
"remark": remark,
|
||||
"isDelete": isDelete,
|
||||
"companyId": companyId,
|
||||
"unitId": unitId,
|
||||
"namePinyin": namePinyin,
|
||||
"files": files.map((x) => x?.toJson()).toList(),
|
||||
"company": company?.toJson(),
|
||||
"unit": unit?.toJson(),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ class DateTimeInput extends StatelessWidget {
|
|||
height: defaultPadding / 2,
|
||||
),
|
||||
InkWell(
|
||||
onTap: () => controller.showDatePick(context),
|
||||
onTap: () => controller.showDatePick(),
|
||||
child: Obx(() => DateTimeContainer(
|
||||
text: controller.selectedDate.isEmpty
|
||||
? 'dd/mm/yyyy'
|
||||
|
|
|
@ -3,10 +3,13 @@ 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/models/product.model.dart';
|
||||
import 'package:sk_base_mobile/models/project.model.dart';
|
||||
import 'package:sk_base_mobile/widgets/core/search_select.dart';
|
||||
import 'package:sk_base_mobile/widgets/empty.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/util/date.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/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';
|
||||
|
@ -35,19 +38,19 @@ class NewInventoryInout extends StatelessWidget {
|
|||
behavior: HitTestBehavior.translucent,
|
||||
onTap: () {
|
||||
// 点击空白处时收起键盘
|
||||
FocusScope.of(Get.context!).unfocus();
|
||||
// FocusScope.of(Get.context!).unfocus();
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
buildTitle(),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
SingleChildScrollView(
|
||||
child: buildForm(),
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
Obx(() => GradientButton(
|
||||
onPressed: () => {controller.insertTask(context)},
|
||||
isLoading: controller.loading.value,
|
||||
|
@ -55,7 +58,8 @@ class NewInventoryInout extends StatelessWidget {
|
|||
],
|
||||
))
|
||||
],
|
||||
))),
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -89,6 +93,8 @@ class NewInventoryInout extends StatelessWidget {
|
|||
}
|
||||
|
||||
Widget buildForm() {
|
||||
double formVerticalGap = 20.0;
|
||||
double formHorizontalGap = 20.0;
|
||||
final children = [
|
||||
Row(children: [
|
||||
Expanded(
|
||||
|
@ -96,13 +102,37 @@ class NewInventoryInout extends StatelessWidget {
|
|||
child: buildProjectPicker(),
|
||||
),
|
||||
SizedBox(
|
||||
width: ScreenAdaper.width(10),
|
||||
width: ScreenAdaper.width(formHorizontalGap),
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: buildProductPicker(),
|
||||
)
|
||||
),
|
||||
]),
|
||||
SizedBox(
|
||||
height: ScreenAdaper.height(formVerticalGap),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(flex: 1, child: buildDatePicker()),
|
||||
SizedBox(
|
||||
width: ScreenAdaper.width(formHorizontalGap),
|
||||
),
|
||||
Expanded(flex: 1, child: buildQuantity()),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: ScreenAdaper.height(formVerticalGap),
|
||||
),
|
||||
Row(
|
||||
children: [Expanded(child: buildAgent())],
|
||||
),
|
||||
SizedBox(
|
||||
height: ScreenAdaper.height(formVerticalGap),
|
||||
),
|
||||
Row(
|
||||
children: [Expanded(child: buildRemark())],
|
||||
),
|
||||
];
|
||||
|
||||
final child = Column(
|
||||
|
@ -122,147 +152,95 @@ class NewInventoryInout extends StatelessWidget {
|
|||
));
|
||||
}
|
||||
|
||||
/// 项目
|
||||
Widget buildProjectPicker() {
|
||||
return SearchSelectComponent(
|
||||
return ZtSearchSelect(
|
||||
isRequired: true,
|
||||
textController: controller.projectTextController,
|
||||
labelText: '项目',
|
||||
itemTextBuilder: <ProjectModel>(ProjectModel itemData) =>
|
||||
Text('${itemData?.name}'),
|
||||
);
|
||||
// return TypeAheadField<ProjectModel>(
|
||||
// hideOnUnfocus: false,
|
||||
// controller: controller.projectTextController,
|
||||
// suggestionsCallback: (String keyword) {
|
||||
// return controller.getProjects(keyword: keyword);
|
||||
// },
|
||||
// builder: (context, _, focusNode) {
|
||||
// return TextFormField(
|
||||
// focusNode: focusNode,
|
||||
// controller: _,
|
||||
// decoration: InputDecoration(
|
||||
// focusedBorder: OutlineInputBorder(
|
||||
// borderSide: const BorderSide(
|
||||
// color: AppTheme.primaryColorLight, width: 2),
|
||||
// borderRadius: BorderRadius.circular(ScreenAdaper.sp(15))),
|
||||
// suffixIcon: _.text.isNotEmpty && focusNode.hasFocus
|
||||
// ? IconButton(
|
||||
// icon: const Icon(Icons.clear),
|
||||
// // 当点击这个按钮时,清除TextFormField的值
|
||||
// onPressed: () => _.clear(),
|
||||
// )
|
||||
// : const SizedBox(),
|
||||
// floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
// label: const Row(
|
||||
// crossAxisAlignment: CrossAxisAlignment.center,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// Text(
|
||||
// "*",
|
||||
// style: TextStyle(color: Colors.red),
|
||||
// ),
|
||||
// Text(
|
||||
// "项目",
|
||||
// ),
|
||||
// ])));
|
||||
// },
|
||||
// emptyBuilder: (_) => Container(
|
||||
// alignment: Alignment.center,
|
||||
// width: ScreenAdaper.width(200),
|
||||
// height: ScreenAdaper.height(50),
|
||||
// child: Text(
|
||||
// '未找到项目',
|
||||
// style: TextStyle(fontSize: ScreenAdaper.sp(20)),
|
||||
// ),
|
||||
// ),
|
||||
// itemBuilder: (context, project) {
|
||||
// return ListTile(
|
||||
// title: Text(project.name ?? ''),
|
||||
// );
|
||||
// },
|
||||
// onSelected: (ProjectModel project) {
|
||||
// controller.projectTextController.text = project.name ?? '';
|
||||
// controller.payload['project'] = project.id;
|
||||
// });
|
||||
// return Obx(() => DropdownButtonFormField<int>(
|
||||
// isExpanded: true,
|
||||
// isDense: false,
|
||||
// style: TextStyle(
|
||||
// fontSize: ScreenAdaper.sp(22), color: AppTheme.nearlyBlack),
|
||||
// // value: _dropdownValue,
|
||||
// decoration: InputDecoration(
|
||||
// contentPadding: EdgeInsets.symmetric(
|
||||
// vertical: ScreenAdaper.height(10),
|
||||
// horizontal: ScreenAdaper.width(10)),
|
||||
// floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
// border: OutlineInputBorder(
|
||||
// borderRadius: BorderRadius.circular(ScreenAdaper.sp(15))),
|
||||
// label: const Row(
|
||||
// crossAxisAlignment: CrossAxisAlignment.center,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// Text(
|
||||
// "*",
|
||||
// style: TextStyle(color: Colors.red),
|
||||
// ),
|
||||
// Text("项目"),
|
||||
// ]),
|
||||
// ),
|
||||
// onChanged: (int? newValue) {
|
||||
// // setState(() {
|
||||
// // _dropdownValue = newValue;
|
||||
// // });
|
||||
// },
|
||||
// // icon: Icon(
|
||||
// // Icons.arrow_drop_down_outlined,
|
||||
// // size: ScreenAdaper.sp(40),
|
||||
// // ),
|
||||
// items: controller.projects
|
||||
// .map<DropdownMenuItem<int>>((ProjectModel model) {
|
||||
// return DropdownMenuItem<int>(
|
||||
// value: model.id,
|
||||
// child: Text(
|
||||
// model.name ?? '',
|
||||
// style: TextStyle(
|
||||
// fontSize: ScreenAdaper.sp(30),
|
||||
// overflow: TextOverflow.ellipsis),
|
||||
// ),
|
||||
// );
|
||||
// }).toList(),
|
||||
// ));
|
||||
}
|
||||
|
||||
Widget buildProductPicker() {
|
||||
return TypeAheadField<ProductModel>(suggestionsCallback: (search) {
|
||||
return [ProductModel(name: 'aaa'), ProductModel(name: 'bbb')];
|
||||
}, builder: (context, controller, focusNode) {
|
||||
return TextField(
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
decoration: InputDecoration(
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15))),
|
||||
label: const Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"*",
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
Text("产品"),
|
||||
])));
|
||||
}, itemBuilder: (context, product) {
|
||||
return ListTile(
|
||||
title: Text(product.name ?? ''),
|
||||
subtitle: Text(product.name ?? ''),
|
||||
);
|
||||
}, onSelected: (city) {
|
||||
// Navigator.of(context).push<void>(
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) => CityPage(city: city),
|
||||
// ),
|
||||
// );
|
||||
itemBuilder: (itemData) => Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: ScreenAdaper.height(15),
|
||||
horizontal: ScreenAdaper.width(20)),
|
||||
child: Text(
|
||||
'${itemData?.name}',
|
||||
style: TextStyle(fontSize: ScreenAdaper.sp(20)),
|
||||
)),
|
||||
suggestionsCallback: (String keyword) {
|
||||
return controller.getProjects(keyword: keyword);
|
||||
},
|
||||
onClear: () {
|
||||
controller.payload.remove('projectId');
|
||||
},
|
||||
onSelected: (ProjectModel project) {
|
||||
controller.projectTextController.text = project.name ?? '';
|
||||
controller.payload['projectId'] = project.id;
|
||||
});
|
||||
}
|
||||
|
||||
/// 产品
|
||||
Widget buildProductPicker() {
|
||||
return ZtSearchSelect(
|
||||
isRequired: true,
|
||||
textController: controller.productTextController,
|
||||
labelText: '产品',
|
||||
itemBuilder: (itemData) => ListTile(
|
||||
title: Text('${itemData?.name}'),
|
||||
subtitle: Text('${itemData?.company?.name ?? ''}'),
|
||||
),
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
/// 时间
|
||||
Widget buildDatePicker() {
|
||||
return ZtDatePicker(
|
||||
textController: controller.dateTextController,
|
||||
onClear: () {
|
||||
controller.payload.remove('time');
|
||||
},
|
||||
onDateSelected: (date) {
|
||||
if (date != null) {
|
||||
controller.dateTextController.text = DateUtil.format(date);
|
||||
controller.payload['time'] = controller.dateTextController.text;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// 数量
|
||||
Widget buildQuantity() {
|
||||
return ZtNumberInput(
|
||||
textController: controller.quantityTextController,
|
||||
labelText: '数量',
|
||||
isRequired: true,
|
||||
);
|
||||
}
|
||||
|
||||
/// 经办人
|
||||
Widget buildAgent() {
|
||||
return ZtTextInput(
|
||||
textController: controller.agentTextController,
|
||||
labelText: '经办人',
|
||||
isRequired: true,
|
||||
);
|
||||
}
|
||||
|
||||
/// 备注
|
||||
Widget buildRemark() {
|
||||
return ZtTextInput(
|
||||
textController: controller.remarkTextController,
|
||||
labelText: '备注',
|
||||
isRequired: true,
|
||||
isTextArea: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:get/get.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/screens/inventory_inout/inventory_inout_controller.dart';
|
||||
|
@ -30,13 +31,14 @@ class NewInventoryInoutController extends GetxController {
|
|||
final description = TextEditingController().obs;
|
||||
final category = TextEditingController().obs;
|
||||
RxList<ProjectModel> projects = <ProjectModel>[].obs;
|
||||
RxList<ProductModel> products = <ProductModel>[].obs;
|
||||
final projectTextController = TextEditingController();
|
||||
final productTextController = TextEditingController();
|
||||
final dateTextController = TextEditingController();
|
||||
final quantityTextController = TextEditingController();
|
||||
final agentTextController = TextEditingController();
|
||||
final remarkTextController = TextEditingController();
|
||||
Map<String, dynamic> payload = {};
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
getProjects();
|
||||
}
|
||||
|
||||
Future<List<ProjectModel>> getProjects({String? keyword}) async {
|
||||
final res =
|
||||
|
@ -48,6 +50,16 @@ class NewInventoryInoutController extends GetxController {
|
|||
return projects;
|
||||
}
|
||||
|
||||
Future<List<ProductModel>> getProducts({String? keyword}) async {
|
||||
final res =
|
||||
await Api.getProducts({'page': 1, 'pageSize': 10, 'name': keyword});
|
||||
if (res.data != null) {
|
||||
products.assignAll(
|
||||
res.data!.items.map((e) => ProductModel.fromJson(e)).toList());
|
||||
}
|
||||
return products;
|
||||
}
|
||||
|
||||
picStartTime(BuildContext context) async {
|
||||
var picker =
|
||||
await showTimePicker(context: context, initialTime: TimeOfDay.now());
|
||||
|
@ -66,11 +78,11 @@ class NewInventoryInoutController extends GetxController {
|
|||
}
|
||||
}
|
||||
|
||||
showDatePick(BuildContext context) async {
|
||||
var picker = await showDatePicker(
|
||||
context: context,
|
||||
showDatePick() async {
|
||||
final picker = await showDatePicker(
|
||||
context: Get.context!,
|
||||
initialDate: DateTime.now(),
|
||||
firstDate: DateTime.now(),
|
||||
firstDate: DateTime(2000, 1, 1),
|
||||
lastDate: DateTime.now().add(const Duration(days: 7)));
|
||||
if (picker != null) {
|
||||
pickedDate = picker;
|
||||
|
@ -125,7 +137,7 @@ class NewInventoryInoutController extends GetxController {
|
|||
if (selectedDate.isEmpty) {
|
||||
// selectedDate.value =
|
||||
// '${Utils.addPrefix(DateTime.now().day.toString())}/${Utils.addPrefix(DateTime.now().month.toString())}/${Utils.addPrefix(DateTime.now().year.toString())}';
|
||||
showDatePick(context);
|
||||
showDatePick();
|
||||
}
|
||||
if (startTime.isEmpty) {
|
||||
picStartTime(context);
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import 'package:date_format/date_format.dart';
|
||||
|
||||
class DateUtil {
|
||||
/// 格式化日期 默认 YYYY-MM-DD
|
||||
static String format(DateTime date, {List<String>? formats}) {
|
||||
return formatDate(date, formats ?? ['yyyy', '-', 'mm', '-', 'dd']);
|
||||
}
|
||||
|
||||
/// 获取几月
|
||||
static String getMonth(DateTime date) {
|
||||
String formattedDate = '${date.month}月';
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get/get_core/src/get_main.dart';
|
||||
import 'package:sk_base_mobile/util/date.util.dart';
|
||||
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||||
|
||||
class ZtDatePicker extends StatelessWidget {
|
||||
final TextEditingController textController;
|
||||
final VoidCallback? onClear;
|
||||
final Function? onDateSelected;
|
||||
const ZtDatePicker({
|
||||
super.key,
|
||||
this.onClear,
|
||||
this.onDateSelected,
|
||||
required this.textController,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextFormField(
|
||||
controller: textController,
|
||||
onTapOutside: (event) {
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
prefixIcon: Icon(
|
||||
Icons.date_range_outlined,
|
||||
size: ScreenAdaper.sp(40),
|
||||
),
|
||||
suffixIcon: textController.text.isNotEmpty
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.clear),
|
||||
// 当点击这个按钮时,清除TextFormField的值
|
||||
onPressed: () {
|
||||
textController.clear();
|
||||
if (onClear != null) {
|
||||
onClear!();
|
||||
}
|
||||
},
|
||||
)
|
||||
: const SizedBox(),
|
||||
hintText: '请选择',
|
||||
label: const Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"*",
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
Text(
|
||||
'日期',
|
||||
),
|
||||
])),
|
||||
keyboardType: TextInputType.none,
|
||||
onTap: () async {
|
||||
final picker = await showDatePicker(
|
||||
context: Get.overlayContext!,
|
||||
initialDate: DateTime.now(),
|
||||
helpText: '选择的日期',
|
||||
confirmText: '确定',
|
||||
cancelText: '取消',
|
||||
firstDate: DateTime(2000, 1, 1),
|
||||
lastDate: DateTime.now().add(const Duration(days: 7)));
|
||||
if (onDateSelected != null) {
|
||||
onDateSelected!(picker);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sk_base_mobile/app_theme.dart';
|
||||
import 'package:sk_base_mobile/constants/bg_color.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 ZtNumberInput extends StatelessWidget {
|
||||
final TextEditingController textController;
|
||||
final VoidCallback? onTap;
|
||||
final bool isRequired;
|
||||
final String labelText;
|
||||
final String? hint;
|
||||
ZtNumberInput(
|
||||
{super.key,
|
||||
required this.textController,
|
||||
this.onTap,
|
||||
this.hint,
|
||||
this.isRequired = false,
|
||||
this.labelText = ''});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextFormField(
|
||||
controller: textController,
|
||||
onTapOutside: (event) {
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
onTap: onTap ?? () {},
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: InputDecoration(
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
label: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (isRequired)
|
||||
const Text(
|
||||
"*",
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
Text(
|
||||
labelText,
|
||||
),
|
||||
]),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide:
|
||||
const BorderSide(color: AppTheme.primaryColorLight, width: 2),
|
||||
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15))),
|
||||
hintText: hint ?? '请输入',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,22 +1,25 @@
|
|||
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/util/screen_adaper_util.dart';
|
||||
|
||||
class SearchSelectComponent extends StatelessWidget {
|
||||
class ZtSearchSelect extends StatelessWidget {
|
||||
final TextEditingController textController;
|
||||
final Function? suggestionsCallback;
|
||||
final Function? onSelected;
|
||||
final VoidCallback? onClear;
|
||||
final String labelText;
|
||||
final bool isRequired;
|
||||
final Widget Function<T>(T itemData)? itemTextBuilder;
|
||||
const SearchSelectComponent(
|
||||
final Widget Function(dynamic itemData)? itemBuilder;
|
||||
const ZtSearchSelect(
|
||||
{super.key,
|
||||
required this.textController,
|
||||
required this.labelText,
|
||||
required this.itemTextBuilder,
|
||||
this.suggestionsCallback,
|
||||
required this.itemBuilder,
|
||||
required this.suggestionsCallback,
|
||||
this.onSelected,
|
||||
this.onClear,
|
||||
this.isRequired = false});
|
||||
|
||||
@override
|
||||
|
@ -29,10 +32,17 @@ class SearchSelectComponent extends StatelessWidget {
|
|||
? suggestionsCallback!(keyword)
|
||||
: [];
|
||||
},
|
||||
debounceDuration: const Duration(milliseconds: 500),
|
||||
itemSeparatorBuilder: (context, index) => const Divider(
|
||||
color: AppTheme.dividerColor,
|
||||
),
|
||||
builder: (context, _, focusNode) {
|
||||
return TextFormField(
|
||||
focusNode: focusNode,
|
||||
controller: _,
|
||||
onTapOutside: (event) {
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(
|
||||
|
@ -42,10 +52,16 @@ class SearchSelectComponent extends StatelessWidget {
|
|||
? IconButton(
|
||||
icon: const Icon(Icons.clear),
|
||||
// 当点击这个按钮时,清除TextFormField的值
|
||||
onPressed: () => _.clear(),
|
||||
onPressed: () {
|
||||
_.clear();
|
||||
if (onClear != null) {
|
||||
onClear!();
|
||||
}
|
||||
},
|
||||
)
|
||||
: const SizedBox(),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
hintText: '请选择',
|
||||
label: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
@ -70,12 +86,14 @@ class SearchSelectComponent extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
itemBuilder: (context, itemData) {
|
||||
return ListTile(
|
||||
title: itemTextBuilder!(itemData),
|
||||
);
|
||||
return itemBuilder!(itemData);
|
||||
},
|
||||
onSelected: (selectedItemData) {
|
||||
onSelected ?? onSelected!(selectedItemData);
|
||||
// 键盘关闭
|
||||
FocusScope.of(context).unfocus();
|
||||
if (onSelected != null) {
|
||||
onSelected!(selectedItemData);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sk_base_mobile/app_theme.dart';
|
||||
import 'package:sk_base_mobile/constants/bg_color.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 ZtTextInput extends StatelessWidget {
|
||||
final TextEditingController textController;
|
||||
final VoidCallback? onTap;
|
||||
final bool isRequired;
|
||||
final String labelText;
|
||||
final String? hint;
|
||||
final bool isTextArea;
|
||||
const ZtTextInput(
|
||||
{super.key,
|
||||
required this.textController,
|
||||
this.onTap,
|
||||
this.hint,
|
||||
this.isRequired = false,
|
||||
this.labelText = '',
|
||||
this.isTextArea = false});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextFormField(
|
||||
controller: textController,
|
||||
onTapOutside: (event) {
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
maxLines: isTextArea ? null : 1, // 添加这行代码
|
||||
onTap: onTap ?? () {},
|
||||
decoration: InputDecoration(
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
label: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (isRequired)
|
||||
const Text(
|
||||
"*",
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
Text(
|
||||
labelText,
|
||||
),
|
||||
]),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide:
|
||||
const BorderSide(color: AppTheme.primaryColorLight, width: 2),
|
||||
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15))),
|
||||
hintText: hint ?? '请输入',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -3,8 +3,8 @@ import 'package:flutter/material.dart';
|
|||
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||||
|
||||
class Empty extends StatelessWidget {
|
||||
String? text;
|
||||
Empty({super.key, this.text});
|
||||
final String? text;
|
||||
const Empty({super.key, this.text});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -13,7 +13,7 @@ class Empty extends StatelessWidget {
|
|||
Center(
|
||||
child: Image(
|
||||
height: ScreenAdaper.height(130),
|
||||
image: AssetImage('assets/images/empty_icon.png'))),
|
||||
image: const AssetImage('assets/images/empty_icon.png'))),
|
||||
Text(
|
||||
text ?? '',
|
||||
textAlign: TextAlign.center,
|
||||
|
|
Loading…
Reference in New Issue