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) {
|
Future<Response<PaginationData>> getProjects(Map params) {
|
||||||
return DioService.dio.get<PaginationData>(Urls.getProjects,
|
return DioService.dio.get<PaginationData>(Urls.getProjects,
|
||||||
queryParameters: {'page': 1, 'pageSize': 10, ...params});
|
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() {
|
Future<Response> logout() {
|
||||||
return DioService.dio.post(
|
return DioService.dio.post(
|
||||||
Urls.logout,
|
Urls.logout,
|
||||||
|
|
|
@ -23,11 +23,50 @@ class AppTheme {
|
||||||
static const String fontName = 'NotoSans';
|
static const String fontName = 'NotoSans';
|
||||||
static const Color activeNavigationBarColor = primaryColor;
|
static const Color activeNavigationBarColor = primaryColor;
|
||||||
static const Color notActiveNavigationBarColor = Colors.grey;
|
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(
|
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,
|
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,
|
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||||
primaryColor: AppTheme.primaryColor,
|
primaryColor: AppTheme.primaryColor,
|
||||||
primaryColorDark: AppTheme.primaryColorDark,
|
primaryColorDark: AppTheme.primaryColorDark,
|
||||||
|
@ -41,7 +80,7 @@ final theme = ThemeData(
|
||||||
unselectedLabelStyle: TextStyle(fontSize: ScreenAdaper.sp(20)),
|
unselectedLabelStyle: TextStyle(fontSize: ScreenAdaper.sp(20)),
|
||||||
selectedLabelStyle: TextStyle(fontSize: ScreenAdaper.sp(20)),
|
selectedLabelStyle: TextStyle(fontSize: ScreenAdaper.sp(20)),
|
||||||
selectedItemColor: AppTheme.primaryColor),
|
selectedItemColor: AppTheme.primaryColor),
|
||||||
tabBarTheme: TabBarTheme(
|
tabBarTheme: const TabBarTheme(
|
||||||
indicator: BoxDecoration(
|
indicator: BoxDecoration(
|
||||||
border:
|
border:
|
||||||
Border(bottom: BorderSide(width: 3, color: AppTheme.primaryColorDark)),
|
Border(bottom: BorderSide(width: 3, color: AppTheme.primaryColorDark)),
|
||||||
|
@ -67,7 +106,7 @@ final theme = ThemeData(
|
||||||
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15))),
|
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15))),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15)),
|
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15)),
|
||||||
borderSide: BorderSide(
|
borderSide: const BorderSide(
|
||||||
color: AppTheme.primaryColorLight, width: 2), // 选中时的边框颜色和宽度
|
color: AppTheme.primaryColorLight, width: 2), // 选中时的边框颜色和宽度
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -7,5 +7,6 @@ class Urls {
|
||||||
static String saveUserInfo = 'user/saveUserInfo';
|
static String saveUserInfo = 'user/saveUserInfo';
|
||||||
static String getUserInfo = 'account/profile';
|
static String getUserInfo = 'account/profile';
|
||||||
static String getProjects = 'project';
|
static String getProjects = 'project';
|
||||||
|
static String getProducts = 'product';
|
||||||
static String updateAvatar = 'user/updateAvatar';
|
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 {
|
class ProductModel {
|
||||||
int? id;
|
ProductModel({
|
||||||
String? createdAt;
|
required this.id,
|
||||||
String? updatedAt;
|
required this.createdAt,
|
||||||
String? name;
|
required this.updatedAt,
|
||||||
String? remark;
|
required this.name,
|
||||||
int? isDelete;
|
required this.remark,
|
||||||
int? companyId;
|
required this.isDelete,
|
||||||
int? unitId;
|
required this.companyId,
|
||||||
String? namePinyin;
|
required this.unitId,
|
||||||
List<Files>? files;
|
required this.namePinyin,
|
||||||
Company? company;
|
required this.files,
|
||||||
Unit? unit;
|
required this.company,
|
||||||
|
required this.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));
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
company =
|
final int? id;
|
||||||
json['company'] != null ? new Company.fromJson(json['company']) : null;
|
final DateTime? createdAt;
|
||||||
unit = json['unit'] != null ? new Unit.fromJson(json['unit']) : null;
|
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() {
|
Map<String, dynamic> toJson() => {
|
||||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
"id": id,
|
||||||
data['id'] = this.id;
|
"createdAt": createdAt?.toIso8601String(),
|
||||||
data['createdAt'] = this.createdAt;
|
"updatedAt": updatedAt?.toIso8601String(),
|
||||||
data['updatedAt'] = this.updatedAt;
|
"name": name,
|
||||||
data['name'] = this.name;
|
"remark": remark,
|
||||||
data['remark'] = this.remark;
|
"isDelete": isDelete,
|
||||||
data['isDelete'] = this.isDelete;
|
"companyId": companyId,
|
||||||
data['companyId'] = this.companyId;
|
"unitId": unitId,
|
||||||
data['unitId'] = this.unitId;
|
"namePinyin": namePinyin,
|
||||||
data['namePinyin'] = this.namePinyin;
|
"files": files.map((x) => x?.toJson()).toList(),
|
||||||
if (this.files != null) {
|
"company": company?.toJson(),
|
||||||
data['files'] = this.files!.map((v) => v.toJson()).toList();
|
"unit": unit?.toJson(),
|
||||||
}
|
};
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ class DateTimeInput extends StatelessWidget {
|
||||||
height: defaultPadding / 2,
|
height: defaultPadding / 2,
|
||||||
),
|
),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () => controller.showDatePick(context),
|
onTap: () => controller.showDatePick(),
|
||||||
child: Obx(() => DateTimeContainer(
|
child: Obx(() => DateTimeContainer(
|
||||||
text: controller.selectedDate.isEmpty
|
text: controller.selectedDate.isEmpty
|
||||||
? 'dd/mm/yyyy'
|
? 'dd/mm/yyyy'
|
||||||
|
|
|
@ -3,10 +3,13 @@ import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:sk_base_mobile/app_theme.dart';
|
import 'package:sk_base_mobile/app_theme.dart';
|
||||||
import 'package:sk_base_mobile/constants/constants.dart';
|
import 'package:sk_base_mobile/constants/constants.dart';
|
||||||
import 'package:sk_base_mobile/models/product.model.dart';
|
import 'package:sk_base_mobile/models/index.dart';
|
||||||
import 'package:sk_base_mobile/models/project.model.dart';
|
import 'package:sk_base_mobile/screens/new_inventory_inout/components/date_time.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/search_select.dart';
|
import 'package:sk_base_mobile/util/date.util.dart';
|
||||||
import 'package:sk_base_mobile/widgets/empty.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/widgets/gradient_button.dart';
|
||||||
import 'package:sk_base_mobile/screens/new_inventory_inout/new_inventory_inout_controller.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';
|
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||||||
|
@ -35,19 +38,19 @@ class NewInventoryInout extends StatelessWidget {
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// 点击空白处时收起键盘
|
// 点击空白处时收起键盘
|
||||||
FocusScope.of(Get.context!).unfocus();
|
// FocusScope.of(Get.context!).unfocus();
|
||||||
},
|
},
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: Column(
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
buildTitle(),
|
buildTitle(),
|
||||||
Expanded(
|
SingleChildScrollView(
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: buildForm(),
|
child: buildForm(),
|
||||||
),
|
),
|
||||||
),
|
Spacer(),
|
||||||
Obx(() => GradientButton(
|
Obx(() => GradientButton(
|
||||||
onPressed: () => {controller.insertTask(context)},
|
onPressed: () => {controller.insertTask(context)},
|
||||||
isLoading: controller.loading.value,
|
isLoading: controller.loading.value,
|
||||||
|
@ -55,7 +58,8 @@ class NewInventoryInout extends StatelessWidget {
|
||||||
],
|
],
|
||||||
))
|
))
|
||||||
],
|
],
|
||||||
))),
|
),
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,6 +93,8 @@ class NewInventoryInout extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildForm() {
|
Widget buildForm() {
|
||||||
|
double formVerticalGap = 20.0;
|
||||||
|
double formHorizontalGap = 20.0;
|
||||||
final children = [
|
final children = [
|
||||||
Row(children: [
|
Row(children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -96,13 +102,37 @@ class NewInventoryInout extends StatelessWidget {
|
||||||
child: buildProjectPicker(),
|
child: buildProjectPicker(),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: ScreenAdaper.width(10),
|
width: ScreenAdaper.width(formHorizontalGap),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 3,
|
flex: 3,
|
||||||
child: buildProductPicker(),
|
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(
|
final child = Column(
|
||||||
|
@ -122,147 +152,95 @@ class NewInventoryInout extends StatelessWidget {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 项目
|
||||||
Widget buildProjectPicker() {
|
Widget buildProjectPicker() {
|
||||||
return SearchSelectComponent(
|
return ZtSearchSelect(
|
||||||
|
isRequired: true,
|
||||||
textController: controller.projectTextController,
|
textController: controller.projectTextController,
|
||||||
labelText: '项目',
|
labelText: '项目',
|
||||||
itemTextBuilder: <ProjectModel>(ProjectModel itemData) =>
|
itemBuilder: (itemData) => Container(
|
||||||
Text('${itemData?.name}'),
|
padding: EdgeInsets.symmetric(
|
||||||
);
|
vertical: ScreenAdaper.height(15),
|
||||||
// return TypeAheadField<ProjectModel>(
|
horizontal: ScreenAdaper.width(20)),
|
||||||
// hideOnUnfocus: false,
|
child: Text(
|
||||||
// controller: controller.projectTextController,
|
'${itemData?.name}',
|
||||||
// suggestionsCallback: (String keyword) {
|
style: TextStyle(fontSize: ScreenAdaper.sp(20)),
|
||||||
// return controller.getProjects(keyword: keyword);
|
)),
|
||||||
// },
|
suggestionsCallback: (String keyword) {
|
||||||
// builder: (context, _, focusNode) {
|
return controller.getProjects(keyword: keyword);
|
||||||
// return TextFormField(
|
},
|
||||||
// focusNode: focusNode,
|
onClear: () {
|
||||||
// controller: _,
|
controller.payload.remove('projectId');
|
||||||
// decoration: InputDecoration(
|
},
|
||||||
// focusedBorder: OutlineInputBorder(
|
onSelected: (ProjectModel project) {
|
||||||
// borderSide: const BorderSide(
|
controller.projectTextController.text = project.name ?? '';
|
||||||
// color: AppTheme.primaryColorLight, width: 2),
|
controller.payload['projectId'] = project.id;
|
||||||
// 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),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 产品
|
||||||
|
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:get/get.dart';
|
||||||
import 'package:sk_base_mobile/db_helper/dbHelper.dart';
|
import 'package:sk_base_mobile/db_helper/dbHelper.dart';
|
||||||
import 'package:sk_base_mobile/models/base_response.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/project.model.dart';
|
||||||
import 'package:sk_base_mobile/models/task_model.dart';
|
import 'package:sk_base_mobile/models/task_model.dart';
|
||||||
import 'package:sk_base_mobile/screens/inventory_inout/inventory_inout_controller.dart';
|
import 'package:sk_base_mobile/screens/inventory_inout/inventory_inout_controller.dart';
|
||||||
|
@ -30,13 +31,14 @@ class NewInventoryInoutController extends GetxController {
|
||||||
final description = TextEditingController().obs;
|
final description = TextEditingController().obs;
|
||||||
final category = TextEditingController().obs;
|
final category = TextEditingController().obs;
|
||||||
RxList<ProjectModel> projects = <ProjectModel>[].obs;
|
RxList<ProjectModel> projects = <ProjectModel>[].obs;
|
||||||
|
RxList<ProductModel> products = <ProductModel>[].obs;
|
||||||
final projectTextController = TextEditingController();
|
final projectTextController = TextEditingController();
|
||||||
|
final productTextController = TextEditingController();
|
||||||
|
final dateTextController = TextEditingController();
|
||||||
|
final quantityTextController = TextEditingController();
|
||||||
|
final agentTextController = TextEditingController();
|
||||||
|
final remarkTextController = TextEditingController();
|
||||||
Map<String, dynamic> payload = {};
|
Map<String, dynamic> payload = {};
|
||||||
@override
|
|
||||||
void onReady() {
|
|
||||||
super.onReady();
|
|
||||||
getProjects();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<ProjectModel>> getProjects({String? keyword}) async {
|
Future<List<ProjectModel>> getProjects({String? keyword}) async {
|
||||||
final res =
|
final res =
|
||||||
|
@ -48,6 +50,16 @@ class NewInventoryInoutController extends GetxController {
|
||||||
return projects;
|
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 {
|
picStartTime(BuildContext context) async {
|
||||||
var picker =
|
var picker =
|
||||||
await showTimePicker(context: context, initialTime: TimeOfDay.now());
|
await showTimePicker(context: context, initialTime: TimeOfDay.now());
|
||||||
|
@ -66,11 +78,11 @@ class NewInventoryInoutController extends GetxController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showDatePick(BuildContext context) async {
|
showDatePick() async {
|
||||||
var picker = await showDatePicker(
|
final picker = await showDatePicker(
|
||||||
context: context,
|
context: Get.context!,
|
||||||
initialDate: DateTime.now(),
|
initialDate: DateTime.now(),
|
||||||
firstDate: DateTime.now(),
|
firstDate: DateTime(2000, 1, 1),
|
||||||
lastDate: DateTime.now().add(const Duration(days: 7)));
|
lastDate: DateTime.now().add(const Duration(days: 7)));
|
||||||
if (picker != null) {
|
if (picker != null) {
|
||||||
pickedDate = picker;
|
pickedDate = picker;
|
||||||
|
@ -125,7 +137,7 @@ class NewInventoryInoutController extends GetxController {
|
||||||
if (selectedDate.isEmpty) {
|
if (selectedDate.isEmpty) {
|
||||||
// selectedDate.value =
|
// selectedDate.value =
|
||||||
// '${Utils.addPrefix(DateTime.now().day.toString())}/${Utils.addPrefix(DateTime.now().month.toString())}/${Utils.addPrefix(DateTime.now().year.toString())}';
|
// '${Utils.addPrefix(DateTime.now().day.toString())}/${Utils.addPrefix(DateTime.now().month.toString())}/${Utils.addPrefix(DateTime.now().year.toString())}';
|
||||||
showDatePick(context);
|
showDatePick();
|
||||||
}
|
}
|
||||||
if (startTime.isEmpty) {
|
if (startTime.isEmpty) {
|
||||||
picStartTime(context);
|
picStartTime(context);
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import 'package:date_format/date_format.dart';
|
import 'package:date_format/date_format.dart';
|
||||||
|
|
||||||
class DateUtil {
|
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) {
|
static String getMonth(DateTime date) {
|
||||||
String formattedDate = '${date.month}月';
|
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/material.dart';
|
||||||
import 'package:flutter_typeahead/flutter_typeahead.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/app_theme.dart';
|
||||||
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||||||
|
|
||||||
class SearchSelectComponent extends StatelessWidget {
|
class ZtSearchSelect extends StatelessWidget {
|
||||||
final TextEditingController textController;
|
final TextEditingController textController;
|
||||||
final Function? suggestionsCallback;
|
final Function? suggestionsCallback;
|
||||||
final Function? onSelected;
|
final Function? onSelected;
|
||||||
|
final VoidCallback? onClear;
|
||||||
final String labelText;
|
final String labelText;
|
||||||
final bool isRequired;
|
final bool isRequired;
|
||||||
final Widget Function<T>(T itemData)? itemTextBuilder;
|
final Widget Function(dynamic itemData)? itemBuilder;
|
||||||
const SearchSelectComponent(
|
const ZtSearchSelect(
|
||||||
{super.key,
|
{super.key,
|
||||||
required this.textController,
|
required this.textController,
|
||||||
required this.labelText,
|
required this.labelText,
|
||||||
required this.itemTextBuilder,
|
required this.itemBuilder,
|
||||||
this.suggestionsCallback,
|
required this.suggestionsCallback,
|
||||||
this.onSelected,
|
this.onSelected,
|
||||||
|
this.onClear,
|
||||||
this.isRequired = false});
|
this.isRequired = false});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -29,10 +32,17 @@ class SearchSelectComponent extends StatelessWidget {
|
||||||
? suggestionsCallback!(keyword)
|
? suggestionsCallback!(keyword)
|
||||||
: [];
|
: [];
|
||||||
},
|
},
|
||||||
|
debounceDuration: const Duration(milliseconds: 500),
|
||||||
|
itemSeparatorBuilder: (context, index) => const Divider(
|
||||||
|
color: AppTheme.dividerColor,
|
||||||
|
),
|
||||||
builder: (context, _, focusNode) {
|
builder: (context, _, focusNode) {
|
||||||
return TextFormField(
|
return TextFormField(
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
controller: _,
|
controller: _,
|
||||||
|
onTapOutside: (event) {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
},
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderSide: const BorderSide(
|
borderSide: const BorderSide(
|
||||||
|
@ -42,10 +52,16 @@ class SearchSelectComponent extends StatelessWidget {
|
||||||
? IconButton(
|
? IconButton(
|
||||||
icon: const Icon(Icons.clear),
|
icon: const Icon(Icons.clear),
|
||||||
// 当点击这个按钮时,清除TextFormField的值
|
// 当点击这个按钮时,清除TextFormField的值
|
||||||
onPressed: () => _.clear(),
|
onPressed: () {
|
||||||
|
_.clear();
|
||||||
|
if (onClear != null) {
|
||||||
|
onClear!();
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
: const SizedBox(),
|
: const SizedBox(),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||||
|
hintText: '请选择',
|
||||||
label: Row(
|
label: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
@ -70,12 +86,14 @@ class SearchSelectComponent extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
itemBuilder: (context, itemData) {
|
itemBuilder: (context, itemData) {
|
||||||
return ListTile(
|
return itemBuilder!(itemData);
|
||||||
title: itemTextBuilder!(itemData),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
onSelected: (selectedItemData) {
|
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';
|
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||||||
|
|
||||||
class Empty extends StatelessWidget {
|
class Empty extends StatelessWidget {
|
||||||
String? text;
|
final String? text;
|
||||||
Empty({super.key, this.text});
|
const Empty({super.key, this.text});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -13,7 +13,7 @@ class Empty extends StatelessWidget {
|
||||||
Center(
|
Center(
|
||||||
child: Image(
|
child: Image(
|
||||||
height: ScreenAdaper.height(130),
|
height: ScreenAdaper.height(130),
|
||||||
image: AssetImage('assets/images/empty_icon.png'))),
|
image: const AssetImage('assets/images/empty_icon.png'))),
|
||||||
Text(
|
Text(
|
||||||
text ?? '',
|
text ?? '',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
|
Loading…
Reference in New Issue