diff --git a/assets/icons/export.svg b/assets/icons/export.svg
new file mode 100644
index 0000000..b23df1a
--- /dev/null
+++ b/assets/icons/export.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/sale_quotation_formula.svg b/assets/icons/sale_quotation_formula.svg
new file mode 100644
index 0000000..96e254b
--- /dev/null
+++ b/assets/icons/sale_quotation_formula.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/sale_quotation_group.svg b/assets/icons/sale_quotation_group.svg
new file mode 100644
index 0000000..1048b03
--- /dev/null
+++ b/assets/icons/sale_quotation_group.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/sale_quotation_template.svg b/assets/icons/sale_quotation_template.svg
new file mode 100644
index 0000000..17afccf
--- /dev/null
+++ b/assets/icons/sale_quotation_template.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/lib/apis/api.dart b/lib/apis/api.dart
index 7ca0889..92100ca 100644
--- a/lib/apis/api.dart
+++ b/lib/apis/api.dart
@@ -15,6 +15,33 @@ class Api {
);
}
+ /// 删除报价模板列表
+ static Future> deleteSaleQuotationTemplate(int id) {
+ return DioService.dio
+ .delete('${Urls.saleQuotationTemplate}/$id');
+ }
+
+ /// 获取报价模板列表
+ static Future> getSaleQuotationTemplate(
+ {Map? params}) {
+ return DioService.dio.get(Urls.saleQuotationTemplate,
+ queryParameters: {'page': 1, 'pageSize': 99, ...(params ?? {})});
+ }
+
+ /// 获取报价配件列表
+ static Future> getSaleQuotationComponents(
+ {Map? params}) {
+ return DioService.dio.get(Urls.saleQuotationComponent,
+ queryParameters: {'page': 1, 'pageSize': 99, ...(params ?? {})});
+ }
+
+ /// 获取报价分组列表
+ static Future> getSaleQuotationGroups(
+ {Map? params}) {
+ return DioService.dio.get(Urls.saleQuotationGroup,
+ queryParameters: {'page': 1, 'pageSize': 99, ...(params ?? {})});
+ }
+
/// 获取权限资源
static Future getResources() {
return DioService.dio.get(Urls.accountResources);
@@ -61,48 +88,59 @@ class Api {
queryParameters: {'page': 1, 'pageSize': 10, ...params});
}
-// 分页获取产品列表
+ /// 分页获取产品列表
static Future> getProducts(Map params) {
return DioService.dio.get(Urls.products,
queryParameters: {'page': 1, 'pageSize': 10, ...params});
}
-// 分页获取原材料出入库列表
+ /// 分页获取原材料出入库列表
static Future> getInventoryInout(Map params) {
return DioService.dio.get(Urls.inventoryInout,
queryParameters: {'page': 1, 'pageSize': 10, ...(params)});
}
-// 获取原材料出入库详情
+ /// 获取原材料出入库详情
static Future getInventoryInOutInfo(int id) {
return DioService.dio.get(
'${Urls.inventoryInout}/$id',
);
}
-// 创建出入库记录
+ /// 创建出入库记录
static Future createInventoryInout(Map params) {
return DioService.dio.post(Urls.inventoryInout, data: params);
}
-// 更新出入库记录
+ /// 创建报价计算模板
+ static Future createSaleQuotationTemplate(Map params) {
+ return DioService.dio.post(Urls.saleQuotationTemplate, data: params);
+ }
+
+ /// 更新报价计算模板
+ static Future updateSaleQuotationTemplate(int id, Map params) {
+ return DioService.dio
+ .put('${Urls.saleQuotationTemplate}/$id', data: params);
+ }
+
+ /// 更新出入库记录
static Future updateInventoryInout(int id, Map params) {
return DioService.dio.put('${Urls.inventoryInout}/$id', data: params);
}
-// 分页获取库存列表
+ /// 分页获取库存列表
static Future> getInventory(Map params) {
return DioService.dio.get(Urls.inventory,
queryParameters: {'page': 1, 'pageSize': 10, ...(params)});
}
-// 分页获取用户
+ /// 分页获取用户
static Future> getUsers(Map params) {
return DioService.dio.get(Urls.sysUser,
queryParameters: {'page': 1, 'pageSize': 10, ...(params)});
}
-// 一次性获取所有的字典类型(不分页)
+ /// 一次性获取所有的字典类型(不分页)
static Future getDictTypeAll(Map params) {
return DioService.dio.post(Urls.getDictType, data: params);
}
diff --git a/lib/app_theme.dart b/lib/app_theme.dart
index 70506b6..4fd48e0 100644
--- a/lib/app_theme.dart
+++ b/lib/app_theme.dart
@@ -4,6 +4,7 @@ import 'package:sk_base_mobile/util/screen_adaper_util.dart';
class AppTheme {
AppTheme._();
static const Color primaryColor = Color(0xFFC89607);
+ static const Color onPrimaryColor = Color(0xFFFEFEFE);
static const Color primaryColorLight = Color.fromARGB(255, 255, 206, 70);
static const Color primaryColorDark = Color.fromARGB(255, 163, 120, 0);
static const Color primaryTextColorWithBg = Color(0x00000000);
@@ -27,7 +28,7 @@ class AppTheme {
static const Color dividerColor = Color.fromARGB(255, 224, 224, 224);
static const Color appbarBgColor = AppTheme.primaryColor;
static const Color scaffoldBackgroundColor = Color(0XFFe9f0fd);
- static const Color inputFillColor = Color(0xfffefefe);
+ static const Color inputFillColor = Color(0xFFf5f8ff);
}
final theme = ThemeData(
@@ -63,7 +64,7 @@ final theme = ThemeData(
),
dialogBackgroundColor: AppTheme.nearlyWhite,
colorScheme: ColorScheme.fromSeed(
- onPrimary: AppTheme.nearlyWhite,
+ onPrimary: AppTheme.onPrimaryColor,
seedColor: AppTheme.primaryColor,
primary: AppTheme.primaryColor),
datePickerTheme: DatePickerThemeData(
diff --git a/lib/config.dart b/lib/config.dart
index 605fa68..02b0257 100644
--- a/lib/config.dart
+++ b/lib/config.dart
@@ -2,11 +2,11 @@
// Global config
class GloablConfig {
- static const BASE_URL = "http://10.0.2.2:8001/api/";
- static const OSS_URL = "http://10.0.2.2:8001";
+ // static const BASE_URL = "http://10.0.2.2:8001/api/";
+ // static const OSS_URL = "http://10.0.2.2:8001";
- // static const BASE_URL = "http://144.123.43.138:3001/api/";
- // static const OSS_URL = "http://144.123.43.138:3001";
+ static const BASE_URL = "http://144.123.43.138:3001/api/";
+ static const OSS_URL = "http://144.123.43.138:3001";
// static const BASE_URL = "http://192.168.60.220:8001/api/";
// static const OSS_URL = "http://192.168.60.220:8001";
static const DOMAIN_NAME = "山矿通";
diff --git a/lib/constants/global_url.dart b/lib/constants/global_url.dart
index 4f4e398..ba443bc 100644
--- a/lib/constants/global_url.dart
+++ b/lib/constants/global_url.dart
@@ -17,4 +17,9 @@ class Urls {
static String accountResources = 'account/menus';
static String depts = 'system/depts';
static String roles = 'system/roles';
+ static String saleQuotationGroup = 'sale_quotation/sale_quotation_group';
+ static String saleQuotationComponent =
+ 'sale_quotation/sale_quotation_component';
+ static String saleQuotationTemplate =
+ 'sale_quotation/sale_quotation_template';
}
diff --git a/lib/models/base_search_more_controller.dart b/lib/models/base_search_more_controller.dart
index b16d35e..2ea7c08 100644
--- a/lib/models/base_search_more_controller.dart
+++ b/lib/models/base_search_more_controller.dart
@@ -1,19 +1,38 @@
import 'package:flutter/material.dart';
import 'package:get/get_rx/src/rx_types/rx_types.dart';
+import 'package:get/get_state_manager/src/simple/get_controllers.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:sk_base_mobile/models/base_search_more.model.dart';
-mixin BaseSearchMoreController {
+class BaseSearchMoreController
+ extends GetxController {
RxList list = RxList([]);
RxString searchKey = ''.obs;
final searchBarTextConroller = TextEditingController();
- RefreshController refreshController = RefreshController(initialRefresh: true);
+ RefreshController refreshController =
+ RefreshController(initialRefresh: false);
int page = 1;
int limit = 15;
int total = 0;
RxList selectedIndex = RxList([]);
+ final loading = false.obs;
getData({bool isRefresh = false}) {}
+ @override
+ onReady() {
+ super.onReady();
+ initData();
+ }
+
+ initData() async {
+ loading.value = true;
+ try {
+ await getData(isRefresh: true);
+ } finally {
+ loading.value = false;
+ }
+ }
+
Future onRefresh() async {
await getData(isRefresh: true).then((_) {
refreshController.refreshCompleted(resetFooterState: true);
diff --git a/lib/models/sale_quotaion_component.model.dart b/lib/models/sale_quotaion_component.model.dart
new file mode 100644
index 0000000..67518ec
--- /dev/null
+++ b/lib/models/sale_quotaion_component.model.dart
@@ -0,0 +1,75 @@
+class SaleQuotationComponentModel {
+ SaleQuotationComponentModel({
+ required this.id,
+ required this.createdAt,
+ required this.updatedAt,
+ required this.name,
+ required this.componentSpecification,
+ required this.unitId,
+ required this.unitPrice,
+ required this.remark,
+ required this.isDelete,
+ required this.unit,
+ });
+
+ final int? id;
+ final DateTime? createdAt;
+ final DateTime? updatedAt;
+ final String? name;
+ final String? componentSpecification;
+ final int? unitId;
+ final double? unitPrice;
+ final String? remark;
+ final int? isDelete;
+ final Unit? unit;
+
+ factory SaleQuotationComponentModel.fromJson(Map json) {
+ return SaleQuotationComponentModel(
+ id: json["id"],
+ createdAt: DateTime.tryParse(json["createdAt"] ?? ""),
+ updatedAt: DateTime.tryParse(json["updatedAt"] ?? ""),
+ name: json["name"],
+ componentSpecification: json["componentSpecification"],
+ unitId: json["unitId"],
+ unitPrice: double.parse(json["unitPrice"]),
+ remark: json["remark"],
+ isDelete: json["isDelete"],
+ unit: json["unit"] == null ? null : Unit.fromJson(json["unit"]),
+ );
+ }
+
+ Map toJson() => {
+ "id": id,
+ "createdAt": createdAt?.toIso8601String(),
+ "updatedAt": updatedAt?.toIso8601String(),
+ "name": name,
+ "componentSpecification": componentSpecification,
+ "unitId": unitId,
+ "unitPrice": unitPrice,
+ "remark": remark,
+ "isDelete": isDelete,
+ "unit": unit?.toJson(),
+ };
+}
+
+class Unit {
+ Unit({
+ required this.id,
+ required this.label,
+ });
+
+ final int? id;
+ final String? label;
+
+ factory Unit.fromJson(Map json) {
+ return Unit(
+ id: json["id"],
+ label: json["label"],
+ );
+ }
+
+ Map toJson() => {
+ "id": id,
+ "label": label,
+ };
+}
diff --git a/lib/models/sale_quotaion_group.model.dart b/lib/models/sale_quotaion_group.model.dart
new file mode 100644
index 0000000..a322499
--- /dev/null
+++ b/lib/models/sale_quotaion_group.model.dart
@@ -0,0 +1,33 @@
+class SaleQuotationGroupModel {
+ SaleQuotationGroupModel({
+ required this.id,
+ required this.createdAt,
+ required this.updatedAt,
+ required this.name,
+ required this.isDelete,
+ });
+
+ final int? id;
+ final DateTime? createdAt;
+ final DateTime? updatedAt;
+ final String? name;
+ final int? isDelete;
+
+ factory SaleQuotationGroupModel.fromJson(Map json) {
+ return SaleQuotationGroupModel(
+ id: json["id"],
+ createdAt: DateTime.tryParse(json["createdAt"] ?? ""),
+ updatedAt: DateTime.tryParse(json["updatedAt"] ?? ""),
+ name: json["name"],
+ isDelete: json["isDelete"],
+ );
+ }
+
+ Map toJson() => {
+ "id": id,
+ "createdAt": createdAt?.toIso8601String(),
+ "updatedAt": updatedAt?.toIso8601String(),
+ "name": name,
+ "isDelete": isDelete,
+ };
+}
diff --git a/lib/models/sale_quotation.model.dart b/lib/models/sale_quotation.model.dart
index 2b37b5e..91397e9 100644
--- a/lib/models/sale_quotation.model.dart
+++ b/lib/models/sale_quotation.model.dart
@@ -26,9 +26,9 @@ class SaleQuotationItemModel extends BaseSearchMoreModel {
final String? unit;
final String? remark;
// 成本
- int cost;
+ num cost;
int quantity;
- int amount;
+ num amount;
SaleQuotationItemModel(
{required this.name,
diff --git a/lib/models/sale_quotation_template.model.dart b/lib/models/sale_quotation_template.model.dart
new file mode 100644
index 0000000..02f41e8
--- /dev/null
+++ b/lib/models/sale_quotation_template.model.dart
@@ -0,0 +1,64 @@
+import 'package:sk_base_mobile/models/sale_quotation.model.dart';
+
+class SaleQuotationTemplateModel {
+ SaleQuotationTemplateModel({
+ this.id,
+ this.name,
+ required this.template,
+ this.isDelete,
+ });
+
+ final int? id;
+ final String? name;
+ final Template template;
+ final int? isDelete;
+
+ factory SaleQuotationTemplateModel.fromJson(Map json) {
+ return SaleQuotationTemplateModel(
+ id: json["id"],
+ name: json["name"],
+ template: Template.fromJson(json["template"]),
+ isDelete: json["isDelete"],
+ );
+ }
+
+ Map toJson() => {
+ "id": id,
+ "name": name,
+ "template": template.toJson(),
+ "isDelete": isDelete,
+ };
+}
+
+class Template {
+ Template({
+ required this.data,
+ required this.formula,
+ required this.totalCost,
+ required this.totalPrice,
+ });
+
+ List data;
+ String formula;
+ num? totalCost;
+ num? totalPrice;
+
+ factory Template.fromJson(Map json) {
+ return Template(
+ data: json["data"] == null
+ ? []
+ : List.from(
+ json["data"]!.map((x) => SaleQuotationModel.fromJson(x))),
+ formula: json["formula"],
+ totalCost: json["totalCost"],
+ totalPrice: json["totalPrice"],
+ );
+ }
+
+ Map toJson() => {
+ "data": data.map((x) => x.toJson()).toList(),
+ "formula": formula,
+ "totalCost": totalCost,
+ "totalPrice": totalPrice,
+ };
+}
diff --git a/lib/models/workbench.model.dart b/lib/models/workbench.model.dart
index 4af6efa..6c3537e 100644
--- a/lib/models/workbench.model.dart
+++ b/lib/models/workbench.model.dart
@@ -1,7 +1,10 @@
+import 'dart:ui';
+
class WorkBenchModel {
final String title;
final String icon;
- final String route;
+ final String? route;
+ final VoidCallback? onTap;
WorkBenchModel(
- {required this.title, required this.route, required this.icon});
+ {required this.title, required this.icon, this.route, this.onTap});
}
diff --git a/lib/router/router.util.dart b/lib/router/router.util.dart
index 4112bc1..0043046 100644
--- a/lib/router/router.util.dart
+++ b/lib/router/router.util.dart
@@ -1,9 +1,15 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
+import 'package:sk_base_mobile/constants/constants.dart';
import 'package:sk_base_mobile/store/resource.store.dart';
import 'package:sk_base_mobile/util/snack_bar.util.dart';
class RouterUtil {
+ static List hasPermissionRoute = [
+ RouteConfig.hrManage,
+ RouteConfig.inventory,
+ RouteConfig.saleQuotation,
+ ];
static Future toNamed(String routeName, {arguments}) async {
//关闭键盘
if (Get.context != null) {
@@ -12,12 +18,21 @@ class RouterUtil {
bool isExsited = ResourceService.to.resources
.firstWhereOrNull((element) => element.path == routeName) !=
null;
-
- if (!isExsited) {
+ bool hasPermission = isExsited || !hasPermissionRoute.contains(routeName);
+ if (!hasPermission) {
SnackBarUtil().info('您没有权限,请联系管理员分配权限,后期将隐藏无权限的菜单');
return null;
}
- return await Get.toNamed(routeName, arguments: arguments);
+ return Get.toNamed(routeName, arguments: arguments);
+ }
+
+ /// 返回
+ static Future back({T? result}) async {
+ //关闭键盘
+ if (Get.context != null) {
+ FocusScope.of(Get.context!).requestFocus(FocusNode());
+ }
+ Get.key.currentState?.pop(result);
}
}
diff --git a/lib/screens/hr_manage/components/edit_userinfo.dart b/lib/screens/hr_manage/components/edit_userinfo.dart
index 759134c..6818708 100644
--- a/lib/screens/hr_manage/components/edit_userinfo.dart
+++ b/lib/screens/hr_manage/components/edit_userinfo.dart
@@ -9,6 +9,7 @@ import 'package:sk_base_mobile/constants/bg_color.dart';
import 'package:sk_base_mobile/constants/enum.dart';
import 'package:sk_base_mobile/models/role.model.dart';
import 'package:sk_base_mobile/models/user_info.model.dart';
+import 'package:sk_base_mobile/router/router.util.dart';
import 'package:sk_base_mobile/screens/hr_manage/components/dept_picker.dart';
import 'package:sk_base_mobile/util/loading_util.dart';
import 'package:sk_base_mobile/util/logger_util.dart';
@@ -323,12 +324,13 @@ class EditUserInfoController extends GetxController {
}
}
- await Api.updateUserInfo(userInfo.value!.id!, data);
- final resUser = await Api.getUserInfo(userInfo.value!.id!);
+ await Api.updateUserInfo(userInfo.value.id!, data);
+ await LoadingUtil.to.dismiss();
+ final resUser = await Api.getUserInfo(userInfo.value.id!);
if (resUser.data != null) {
userInfo.value = UserInfoModel.fromJson(resUser.data);
}
- Get.back(result: userInfo.value);
+ await RouterUtil.back(result: userInfo.value);
SnackBarUtil().success(
'保存成功',
);
diff --git a/lib/screens/hr_manage/hr_manage.dart b/lib/screens/hr_manage/hr_manage.dart
index b1b1640..3a78711 100644
--- a/lib/screens/hr_manage/hr_manage.dart
+++ b/lib/screens/hr_manage/hr_manage.dart
@@ -150,6 +150,7 @@ class HrManagePage extends StatelessWidget {
Widget buildUserCard(int index) {
return SkInk(
+ color: AppTheme.nearlyWhite,
onTap: () {
RouterUtil.toNamed(RouteConfig.employeeDetail,
arguments: controller.list[index]);
diff --git a/lib/screens/inventory_inout/inventory_inout_controller.dart b/lib/screens/inventory_inout/inventory_inout_controller.dart
index d14484a..6661769 100644
--- a/lib/screens/inventory_inout/inventory_inout_controller.dart
+++ b/lib/screens/inventory_inout/inventory_inout_controller.dart
@@ -5,6 +5,7 @@ import 'package:sk_base_mobile/apis/api.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/constants/enum.dart';
import 'package:sk_base_mobile/db_helper/db_help.dart';
+import 'package:sk_base_mobile/router/router.util.dart';
import 'package:sk_base_mobile/screens/inventory_inout/components/inventory_inout_info.dart';
import 'package:sk_base_mobile/screens/new_inventory_inout/new_inventory_inout.dart';
import 'package:sk_base_mobile/util/date.util.dart';
@@ -76,7 +77,7 @@ class InventoryInoutController extends GetxController {
),
child: ElevatedButton(
onPressed: () {
- Get.back();
+ RouterUtil.back();
showInventoryInoutCreateDialog(
InventoryInOrOutEnum.In);
},
@@ -145,7 +146,7 @@ class InventoryInoutController extends GetxController {
),
child: ElevatedButton(
onPressed: () {
- Get.back();
+ RouterUtil.back();
showInventoryInoutCreateDialog(
InventoryInOrOutEnum.Out);
},
diff --git a/lib/screens/new_inventory_inout/new_inventory_inout.dart b/lib/screens/new_inventory_inout/new_inventory_inout.dart
index 1a7b261..6d2fe9a 100644
--- a/lib/screens/new_inventory_inout/new_inventory_inout.dart
+++ b/lib/screens/new_inventory_inout/new_inventory_inout.dart
@@ -8,6 +8,7 @@ import 'package:sk_base_mobile/constants/dict_enum.dart';
import 'package:sk_base_mobile/models/index.dart';
import 'package:sk_base_mobile/models/inventory.model.dart';
import 'package:sk_base_mobile/models/user_info.model.dart';
+import 'package:sk_base_mobile/router/router.util.dart';
import 'package:sk_base_mobile/screens/new_inventory_inout/components/agent_search.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';
@@ -242,7 +243,7 @@ class NewInventoryInout extends StatelessWidget {
if (inOrOut == InventoryInOrOutEnum.In) {
ModalUtil.showGeneralDialog(content: ProductSearch(
onProductSelected: (ProductModel product) {
- Get.back();
+ RouterUtil.back();
String productName =
'${product.productNumber} ${product.name!}';
controller.payload['productId'] = product.id;
@@ -263,7 +264,7 @@ class NewInventoryInout extends StatelessWidget {
return (itemData.quantity ?? 0) > 0;
},
onInventorySelected: (InventoryModel inventory) {
- Get.back();
+ RouterUtil.back();
controller.payload['inventoryId'] = inventory.id;
String productName =
'${inventory.product!.name!}(¥${double.parse('${inventory.unitPrice}')})(${inventory.product?.company?.name})';
@@ -364,7 +365,7 @@ class NewInventoryInout extends StatelessWidget {
ModalUtil.showGeneralDialog(
content: AgentSearch(
onSelected: (UserInfoModel userInfo) {
- Get.back();
+ RouterUtil.back();
controller.payload['agent'] = userInfo.nickname;
controller.agentTextController.text = userInfo.nickname!;
},
diff --git a/lib/screens/new_inventory_inout/new_inventory_inout_controller.dart b/lib/screens/new_inventory_inout/new_inventory_inout_controller.dart
index c404798..4431acd 100644
--- a/lib/screens/new_inventory_inout/new_inventory_inout_controller.dart
+++ b/lib/screens/new_inventory_inout/new_inventory_inout_controller.dart
@@ -8,6 +8,7 @@ import 'package:sk_base_mobile/apis/api.dart';
import 'package:sk_base_mobile/constants/enum.dart';
import 'package:sk_base_mobile/db_helper/db_help.dart';
import 'package:sk_base_mobile/models/index.dart';
+import 'package:sk_base_mobile/router/router.util.dart';
import 'package:sk_base_mobile/screens/inventory_inout/inventory_inout_controller.dart';
import 'package:sk_base_mobile/util/date.util.dart';
import 'package:sk_base_mobile/util/loading_util.dart';
@@ -190,7 +191,7 @@ class NewInventoryInoutController extends GetxController {
await Api.updateInventoryInout(
response.data, {'fileIds': uploadRes.map((e) => e!.id).toList()});
}
- Get.back(result: true);
+ await RouterUtil.back(result: true);
SnackBarUtil().success(
'提交成功',
);
@@ -238,7 +239,7 @@ class NewInventoryInoutController extends GetxController {
// }
// }
// // uploadImgFilePath(pickedFile.path);
- // Get.back();
+ // await RouterUtil.back();
// } catch (e) {
// SnackBarUtil().error('上传失败,请重试');
// } finally {
@@ -354,7 +355,7 @@ class NewInventoryInoutController extends GetxController {
// inventoryInoutlist[dif.inDays].add(value);
// Timer(const Duration(seconds: 1), () {
// loading.value = false;
- // Get.back();
+ // await RouterUtil.back();
// SnackBarUtil().success(
// 'Successful',
// message: 'Task is created',
diff --git a/lib/screens/sale_quotation/components/sale_quotation_drawer.dart b/lib/screens/sale_quotation/components/sale_quotation_drawer.dart
index c70cd9e..0225047 100644
--- a/lib/screens/sale_quotation/components/sale_quotation_drawer.dart
+++ b/lib/screens/sale_quotation/components/sale_quotation_drawer.dart
@@ -1,16 +1,27 @@
+import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_svg/svg.dart';
+import 'package:get/get.dart';
import 'package:sk_base_mobile/app_theme.dart';
+import 'package:sk_base_mobile/models/sale_quotation_template.model.dart';
+import 'package:sk_base_mobile/router/router.util.dart';
+import 'package:sk_base_mobile/screens/sale_quotation/sale_quotation.controller.dart';
+import 'package:sk_base_mobile/util/modal.util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
-import 'package:sk_base_mobile/widgets/gradient_button.dart';
+import 'package:sk_base_mobile/util/snack_bar.util.dart';
+import 'package:sk_base_mobile/widgets/core/sk_flat_button.dart';
+import 'package:sk_base_mobile/widgets/core/sk_ink.dart';
+import 'package:sk_base_mobile/widgets/form_item/sk_text_input.dart';
import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
class SaleQuotationEndDrawer extends StatelessWidget {
- const SaleQuotationEndDrawer({super.key});
+ final controller = Get.find();
+ SaleQuotationEndDrawer({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
- appBar: SkAppbar(title: '工具栏', hideLeading: true),
+ appBar: const SkAppbar(title: '', hideLeading: true),
body: buildBody(),
);
}
@@ -18,24 +29,280 @@ class SaleQuotationEndDrawer extends StatelessWidget {
Widget buildBody() {
return Column(
children: [
- ListTile(
- title: Text('选择报价模板'),
- onTap: () {},
+ buildToolbar(),
+ Expanded(
+ child: buildTemplatePicker(),
),
- Divider(
- color: AppTheme.dividerColor,
- height: 1,
- ),
- Spacer(),
- GradientButton(
+ buildAction()
+ ],
+ );
+ }
+
+ Widget buildAction() {
+ return Row(
+ children: [
+ Expanded(
+ child: SkFlatButton(
+ color: AppTheme.dangerColor,
+ onPressed: () {
+ controller.clearWorkbench();
+ RouterUtil.back();
+ },
+ icon: Icon(
+ Icons.delete,
+ color: AppTheme.nearlyWhite,
+ size: ScreenAdaper.height(40),
+ ),
+ buttonText: '清空工作区',
+ )),
+ Expanded(
+ child: SkFlatButton(
+ onPressed: () async {
+ if (controller.templateName.value != '默认') {
+ await RouterUtil.back();
+ await controller.saveToDatabase();
+ } else {
+ templateNameDialog();
+ }
+
+ // final isSuccessed = await controller.saveToDatabase();
+ // if (isSuccessed) await RouterUtil.back();
+ },
icon: Icon(
Icons.save,
color: AppTheme.nearlyWhite,
size: ScreenAdaper.height(40),
),
- buttonText: '保存为模板',
- )
+ buttonText: '保存模板',
+ ))
],
);
}
+
+ Widget buildToolbar() {
+ return Container(
+ decoration: BoxDecoration(
+ color: AppTheme.nearlyWhite,
+ borderRadius: BorderRadius.circular(ScreenAdaper.sp(20))),
+ margin: EdgeInsets.symmetric(
+ horizontal: ScreenAdaper.height(20),
+ vertical: ScreenAdaper.height(20)),
+ child: Column(
+ children: [
+ buildGroupHeader('工具栏'),
+ Row(
+ children: controller.menus
+ .mapIndexed(
+ (index, item) => Expanded(child: buildItem(index)))
+ .toList())
+ ],
+ ),
+ );
+ }
+
+ Widget buildGroupHeader(String text) {
+ return Container(
+ padding: EdgeInsets.only(
+ right: ScreenAdaper.width(20),
+ left: ScreenAdaper.width(20),
+ top: ScreenAdaper.height(20)),
+ alignment: Alignment.centerLeft,
+ child: Text(
+ text,
+ style: TextStyle(
+ fontSize: ScreenAdaper.height(25), color: Colors.grey[600]),
+ ),
+ );
+ }
+
+ Widget buildItem(int index) {
+ return SkInk(
+ onTap: () {
+ // final route = RouteConfig.getPages
+ // .map((e) => e.name)
+ // .firstWhereOrNull((name) => name == controller.menus[index].route);
+ // if (route != null) {
+ // } else {
+ // SnackBarUtil().info('您没有权限,请联系管理员。后期会隐藏没有权限的功能');
+ // }
+ },
+ child: Container(
+ alignment: Alignment.center,
+ padding: EdgeInsets.only(
+ top: ScreenAdaper.height(20), bottom: ScreenAdaper.height(20)),
+ child: Column(
+ children: [
+ SvgPicture.asset(
+ 'assets/icons/${controller.menus[index].icon}',
+ width: ScreenAdaper.width(75),
+ height: ScreenAdaper.width(75),
+ ),
+ SizedBox(
+ height: ScreenAdaper.height(10),
+ ),
+ Text(
+ controller.menus[index].title,
+ style: TextStyle(
+ fontSize: ScreenAdaper.height(22),
+ ),
+ )
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget buildTemplatePicker() {
+ return Obx(() => Container(
+ decoration: BoxDecoration(
+ color: AppTheme.nearlyWhite,
+ borderRadius: BorderRadius.circular(ScreenAdaper.sp(20))),
+ margin: EdgeInsets.only(
+ left: ScreenAdaper.height(20),
+ right: ScreenAdaper.height(20),
+ bottom: ScreenAdaper.height(20)),
+ child: Column(
+ children: [
+ buildGroupHeader('请选择模板'),
+ SizedBox(
+ height: ScreenAdaper.height(10),
+ ),
+ Expanded(
+ child: ListView(
+ padding:
+ EdgeInsets.symmetric(horizontal: ScreenAdaper.height(20)),
+ children: controller.templates
+ .mapIndexed((int index, element) =>
+ buildTemplateItem(element, index))
+ .toList(),
+ ))
+ ],
+ ),
+ ));
+ }
+
+ Widget buildTemplateItem(SaleQuotationTemplateModel element, int index) {
+ return SkInk(
+ onTap: () async {
+ await Future.delayed(const Duration(milliseconds: 150));
+ controller.changeTemplate(element);
+ await RouterUtil.back();
+ },
+ margin: EdgeInsets.symmetric(
+ horizontal: ScreenAdaper.width(10),
+ vertical: ScreenAdaper.height(10)),
+ border: Border.all(color: AppTheme.dividerColor),
+ borderRadius: BorderRadius.circular(ScreenAdaper.sp(15)),
+ child: Container(
+ padding: EdgeInsets.symmetric(
+ vertical: ScreenAdaper.height(20),
+ horizontal: ScreenAdaper.width(20)),
+ child: Row(children: [
+ Expanded(
+ child: Text(
+ '${element.name}',
+ style: TextStyle(fontSize: ScreenAdaper.height(25)),
+ )),
+ SkInk(
+ onTap: () {
+ templateNameDialog(title: element.name);
+ },
+ child: Icon(
+ Icons.edit,
+ color: Colors.grey[600],
+ size: ScreenAdaper.height(40),
+ ),
+ ),
+ SizedBox(
+ width: ScreenAdaper.width(10),
+ ),
+ SkInk(
+ color: Colors.transparent,
+ onTap: () {
+ ModalUtil.alert(
+ title: '删除模板',
+ content: Text(
+ '确定删除模板${element.name}吗?',
+ style: TextStyle(fontSize: ScreenAdaper.height(30)),
+ ),
+ onConfirm: () async {
+ await controller.deleteTemplate(element.id!);
+ controller.templates.removeAt(index);
+ });
+ },
+ child: Icon(
+ Icons.delete,
+ color: Colors.grey[600],
+ size: ScreenAdaper.height(40),
+ ),
+ ),
+ ]),
+ ),
+ );
+ }
+
+ void templateNameDialog({String? title = ''}) {
+ final textController = TextEditingController(text: title);
+ Get.dialog(AlertDialog(
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(ScreenAdaper.sp(20)),
+ ),
+ contentPadding: EdgeInsets.only(
+ top: ScreenAdaper.height(10),
+ right: ScreenAdaper.height(20),
+ left: ScreenAdaper.height(20)),
+ content: Column(mainAxisSize: MainAxisSize.min, children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ SkInk(
+ onTap: () {
+ RouterUtil.back();
+ },
+ child: const Icon(Icons.close))
+ ],
+ ),
+ SkTextInput(
+ height: ScreenAdaper.height(100),
+ textController: textController,
+ customLabel: true,
+ labelText: '模板名称',
+ ),
+ Row(
+ children: [
+ Expanded(
+ child: TextButton(
+ onPressed: () {
+ RouterUtil.back();
+ },
+ child: const Text(
+ '取消',
+ style: TextStyle(color: AppTheme.nearlyBlack),
+ ),
+ ),
+ ),
+ Expanded(
+ child: TextButton(
+ onPressed: () async {
+ if (textController.text.isEmpty) {
+ SnackBarUtil().error('模板名不能为空');
+ return;
+ }
+ controller.templateName.value = textController.text;
+ await RouterUtil.back();
+ await controller.saveToLocal();
+ await controller.saveToDatabase();
+ await controller.getTemplates();
+ },
+ child: const Text('确定'),
+ ))
+ ],
+ )
+ ]),
+ ));
+ }
}
+
+// class SaleQuotationDrawerController extends GetxController{
+
+// }
\ No newline at end of file
diff --git a/lib/screens/sale_quotation/components/sale_quotation_group_search.dart b/lib/screens/sale_quotation/components/sale_quotation_group_search.dart
index 36f7a0f..ef35c2e 100644
--- a/lib/screens/sale_quotation/components/sale_quotation_group_search.dart
+++ b/lib/screens/sale_quotation/components/sale_quotation_group_search.dart
@@ -111,34 +111,10 @@ class SaleQuotationGroupSearch extends StatelessWidget {
vertical: ScreenAdaper.height(10)),
child: Row(
children: [
- // ClipRRect(
- // borderRadius: BorderRadius.circular(ScreenAdaper.sp(15)),
- // child: FadeInCacheImage(
- // url: controller.list[index].avatar ?? '',
- // width: ScreenAdaper.width(60),
- // height: ScreenAdaper.width(60),
- // ),
- // ),
- // SizedBox(
- // width: ScreenAdaper.width(20),
- // ),
Text(
- '${controller.list[index].name}',
+ controller.list[index].name,
style: TextStyle(fontSize: ScreenAdaper.height(25)),
),
- // if (controller.list[index].dept != null) ...[
- // SizedBox(
- // width: ScreenAdaper.width(20),
- // ),
- // Container(
- // padding: EdgeInsets.symmetric(
- // horizontal: ScreenAdaper.width(10),
- // vertical: ScreenAdaper.height(5)),
- // decoration: BoxDecoration(
- // color: Colors.grey[300],
- // borderRadius: BorderRadius.circular(ScreenAdaper.sp(10))),
- // child: Text('${controller.list[index].dept?.name}'),
- // )
],
),
),
diff --git a/lib/screens/sale_quotation/sale_quotation.controller.dart b/lib/screens/sale_quotation/sale_quotation.controller.dart
index c4ba64b..468f707 100644
--- a/lib/screens/sale_quotation/sale_quotation.controller.dart
+++ b/lib/screens/sale_quotation/sale_quotation.controller.dart
@@ -4,10 +4,16 @@ import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:math_expressions/math_expressions.dart';
+import 'package:sk_base_mobile/apis/api.dart';
import 'package:sk_base_mobile/models/base_search_more_controller.dart';
+import 'package:sk_base_mobile/models/sale_quotaion_component.model.dart';
+import 'package:sk_base_mobile/models/sale_quotaion_group.model.dart';
import 'package:sk_base_mobile/models/sale_quotation.model.dart';
+import 'package:sk_base_mobile/models/sale_quotation_template.model.dart';
+import 'package:sk_base_mobile/models/workbench.model.dart';
import 'package:sk_base_mobile/screens/sale_quotation/components/sale_quotation_group_search.dart';
import 'package:sk_base_mobile/services/storage.service.dart';
+import 'package:sk_base_mobile/util/snack_bar.util.dart';
import 'package:sk_base_mobile/widgets/form_item/sk_multi_search_more.dart';
import 'package:sk_base_mobile/util/modal.util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
@@ -17,39 +23,21 @@ class SaleQuotationController extends GetxController {
static SaleQuotationController get to => Get.find();
final RxList editingcell = RxList([null, null, null]);
final GlobalKey scaffoldKey = GlobalKey();
- RxList products = RxList([
- SaleQuotationItemModel(
- name: '矿用本安型支架控制器', unit: '台', spec: 'ZDYZ-Z', cost: 4700),
- SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', cost: 1200),
- SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', cost: 5700),
- SaleQuotationItemModel(name: '矿用本安型隔离耦合器', cost: 1200),
- SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', cost: 600),
- SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-控制器', cost: 400),
- SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-驱动器', cost: 500),
- SaleQuotationItemModel(
- name: '钢丝编织橡胶护套连接器', remark: '控制器-隔离耦合器', cost: 2000),
- SaleQuotationItemModel(name: '矿用本安型支架控制器', cost: 4700),
- SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', cost: 1200),
- SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', cost: 5700),
- SaleQuotationItemModel(
- name: '电液换向阀(10功能10接口)', remark: '中间过渡架主阀组', cost: 13200),
- SaleQuotationItemModel(
- name: '电液换向阀(20功能20接口)', remark: '端头架主阀组', cost: 26500),
- SaleQuotationItemModel(
- name: '自动反冲洗过滤装置', remark: '流量:900L/min,过滤精度25μm', cost: 2000),
- SaleQuotationItemModel(name: '全自动反冲洗过滤器电缆', remark: '控制器-自动反冲洗'),
- SaleQuotationItemModel(name: '矿用本安型位移传感器'),
- SaleQuotationItemModel(name: '矿用本安型压力传感器'),
- SaleQuotationItemModel(name: '矿用本安型红外发射器'),
- SaleQuotationItemModel(name: '矿用本安型LED信号灯'),
- SaleQuotationItemModel(name: '倾角传感器'),
- SaleQuotationItemModel(name: '各类安装附件'),
- ]);
RxList groups = RxList([]);
- RxInt totalCost = RxInt(0);
+ RxDouble totalCost = RxDouble(0.0);
RxDouble totalPrice = RxDouble(0.0);
RxBool isFormulaEditing = false.obs;
RxString formula = '成本 * 1.3 / 0.864'.obs;
+ RxList templates = RxList([]);
+ final List menus = [
+ WorkBenchModel(title: '导出明细', icon: 'export.svg'),
+ // WorkBenchModel(title: '模板', icon: 'sale_quotation_template.svg'),
+ WorkBenchModel(title: '配件管理', icon: 'product.svg'),
+ WorkBenchModel(title: '分组管理', icon: 'sale_quotation_group.svg'),
+ WorkBenchModel(title: '计算公式', icon: 'sale_quotation_formula.svg'),
+ ];
+ RxString templateName = '默认'.obs;
+ int? templateId;
@override
void onReady() {
init();
@@ -59,18 +47,43 @@ class SaleQuotationController extends GetxController {
Future init() async {
String? salesQuotation = StorageService.to.getString('salesQuotation');
if (salesQuotation != null) {
- groups.assignAll((jsonDecode(salesQuotation) as List)
- .map((e) => SaleQuotationModel.fromJson(e))
- .toList());
- calculateTotal();
+ SaleQuotationTemplateModel editTemplate =
+ SaleQuotationTemplateModel.fromJson(jsonDecode(salesQuotation));
+ parseTemplateModel(editTemplate);
}
+ await getTemplates();
}
+ void parseTemplateModel(SaleQuotationTemplateModel editTemplate) {
+ groups.assignAll(editTemplate.template.data);
+ templateName.value = editTemplate.name ?? '';
+ formula.value = editTemplate.template.formula;
+ totalPrice.value = editTemplate.template.totalPrice?.toDouble() ?? 0.0;
+ totalCost.value = editTemplate.template.totalCost?.toDouble() ?? 0.0;
+ templateId = editTemplate.id;
+ }
+
+ /// 切换报价模板
+ void changeTemplate(SaleQuotationTemplateModel templateModel) {
+ parseTemplateModel(templateModel);
+ saveToLocal();
+ }
+
+ /// 获取报价模板
+ Future getTemplates() async {
+ final res = await Api.getSaleQuotationTemplate();
+ List newList = res.data!.items
+ .map((e) => SaleQuotationTemplateModel.fromJson(e))
+ .toList();
+ templates.assignAll(newList);
+ }
+
+ /// 实时计算总数
void calculateTotal({String? newFormula}) {
//计算groups中所有items中的amout总和
- totalCost.value = groups.fold(0, (previousValue, element) {
+ totalCost.value = groups.fold(0, (previousValue, element) {
return previousValue +
- element.items.fold(0, (previousValue, element) {
+ element.items.fold(0, (previousValue, element) {
return previousValue + element.amount;
});
});
@@ -100,39 +113,87 @@ class SaleQuotationController extends GetxController {
// 入库一般是先输入单价和数量,然后计算单价
if (changedField != 'amount') {
Decimal result = cost * quantity;
- data.amount = result != Decimal.zero ? result.toBigInt().toInt() : 0;
+ data.amount = result != Decimal.zero ? result.toDouble() : 0;
}
if (changedField == 'amount' && quantity != Decimal.zero) {
Decimal result =
(amount / quantity).toDecimal(scaleOnInfinitePrecision: 10);
- data.cost = result != Decimal.zero ? result.toBigInt().toInt() : 0;
+ data.cost = result != Decimal.zero ? result.toDouble() : 0.0;
} else if (changedField != 'amount') {
Decimal result = (cost * quantity);
- data.amount = result != Decimal.zero ? result.toBigInt().toInt() : 0;
+ data.amount = result != Decimal.zero ? result.toDouble() : 0;
}
return data;
}
- void saveChanges(int groupIndex, int rowIndex, SaleQuotationItemModel data,
- String changedField) {
+ /// 处理行数据变化
+ void afterRowChanges(int groupIndex, int rowIndex,
+ SaleQuotationItemModel data, String changedField) {
data = calculateRow(data, changedField);
groups[groupIndex].items[rowIndex] = data;
calculateTotal();
stopEditing();
}
+ /// 停止编辑
void stopEditing() {
editingcell.assignAll([null, null, null]);
- save();
+ saveToLocal();
}
- Future save() async {
- await StorageService.to
- .setString('salesQuotation', jsonEncode(groups.toJson()));
- // SnackBarUtil().success('已保存到本地');
+ /// 保存到本地持久化
+ Future saveToLocal() async {
+ Map data = {
+ 'name': templateName.value,
+ 'template': {
+ 'data': groups.map((e) => e.toJson()).toList(),
+ 'totalCost': totalCost.value,
+ 'totalPrice': totalPrice.value,
+ 'formula': formula.value
+ }
+ };
+ if (templateId != null) {
+ data['id'] = templateId;
+ }
+ await StorageService.to.setString('salesQuotation', jsonEncode(data));
}
+ /// 保存到数据库
+ Future saveToDatabase() async {
+ if (templateId == null) {
+ await Api.createSaleQuotationTemplate({
+ 'name': templateName.value,
+ 'template': {
+ 'data': groups.toJson(),
+ 'totalCost': totalCost.value,
+ 'totalPrice': totalPrice.value,
+ 'formula': formula.value
+ }
+ });
+ } else {
+ await Api.updateSaleQuotationTemplate(templateId!, {
+ 'name': templateName.value,
+ 'template': {
+ 'data': groups.toJson(),
+ 'totalCost': totalCost.value,
+ 'totalPrice': totalPrice.value,
+ 'formula': formula.value
+ }
+ });
+ }
+ await getTemplates();
+ await SnackBarUtil().success('已保存');
+ return true;
+ }
+
+ /// 删除模板
+ Future deleteTemplate(int templateId) async {
+ await Api.deleteSaleQuotationTemplate(templateId);
+ SnackBarUtil().success('已删除');
+ }
+
+ /// 添加分组
void addGroup() async {
final controller = Get.put(GroupSearchMoreController());
// 选择组件 选择分组
@@ -145,7 +206,7 @@ class SaleQuotationController extends GetxController {
groups.addAll(controller.list.where((element) {
return indexes.contains(controller.list.indexOf(element));
}));
- save();
+ saveToLocal();
},
leadingBuilder: (index) {
return Container(
@@ -168,15 +229,16 @@ class SaleQuotationController extends GetxController {
calculateTotal();
}
+ /// 移除分组
void removeGroup(int index) {
groups.removeAt(index);
calculateTotal();
- save();
+ saveToLocal();
}
void addItems(int groupIndex) async {
final controller = Get.put(ItemSearchMoreController());
- // 选择产品
+ // 选择配件
ModalUtil.showGeneralDialog(
content: SkMutilSearchMore(
controller: controller,
@@ -184,10 +246,12 @@ class SaleQuotationController extends GetxController {
enablePullDown: true,
isDialog: true,
onOk: (List indexes) {
- groups[groupIndex].items.addAll(products.where((element) {
- return indexes.contains(products.indexOf(element));
+ groups[groupIndex]
+ .items
+ .addAll(controller.list.where((element) {
+ return indexes.contains(controller.list.indexOf(element));
}));
- save();
+ saveToLocal();
},
leadingBuilder: (index) {
return Container(
@@ -250,22 +314,41 @@ class SaleQuotationController extends GetxController {
groups[groupIndex].items.removeAt(rowIndex);
calculateTotal();
}
+
+ void clearWorkbench() {
+ groups.value = [];
+ totalCost.value = 0.0;
+ totalPrice.value = 0.0;
+ formula.value = '成本 * 1.3 / 0.864';
+ templateName.value = '默认';
+ templateId = null;
+ saveToLocal();
+ }
}
-class GroupSearchMoreController extends GetxController
- with BaseSearchMoreController {
+class GroupSearchMoreController
+ extends BaseSearchMoreController {
@override
Future> getData({bool isRefresh = false}) async {
- List newList = [
- SaleQuotationModel(name: '中间过渡架电控部分', items: RxList([])),
- SaleQuotationModel(name: '端头架电控部分', items: RxList([])),
- SaleQuotationModel(name: '主阀部分', items: RxList([])),
- SaleQuotationModel(name: '自动反冲洗过滤器部分', items: RxList([])),
- SaleQuotationModel(name: '位移测量部分', items: RxList([])),
- SaleQuotationModel(name: '压力检测部分', items: RxList([])),
- SaleQuotationModel(name: '煤机定位部分', items: RxList([])),
- SaleQuotationModel(name: '姿态检测部分', items: RxList([])),
- ];
+ await Future.delayed(const Duration(seconds: 1));
+ final res = await Api.getSaleQuotationGroups();
+ List groups = res.data!.items
+ .map((e) => SaleQuotationGroupModel.fromJson(e))
+ .toList();
+
+ List newList = groups
+ .map((e) => SaleQuotationModel(name: e.name!, items: RxList([])))
+ .toList();
+ // List newList = [
+ // SaleQuotationModel(name: '中间过渡架电控部分', items: RxList([])),
+ // SaleQuotationModel(name: '端头架电控部分', items: RxList([])),
+ // SaleQuotationModel(name: '主阀部分', items: RxList([])),
+ // SaleQuotationModel(name: '自动反冲洗过滤器部分', items: RxList([])),
+ // SaleQuotationModel(name: '位移测量部分', items: RxList([])),
+ // SaleQuotationModel(name: '压力检测部分', items: RxList([])),
+ // SaleQuotationModel(name: '煤机定位部分', items: RxList([])),
+ // SaleQuotationModel(name: '姿态检测部分', items: RxList([])),
+ // ];
list.assignAll(newList
.where((element) => PinyinHelper.getPinyin(element.name, separator: '')
.contains(searchKey.value))
@@ -274,42 +357,58 @@ class GroupSearchMoreController extends GetxController
}
}
-class ItemSearchMoreController extends GetxController
- with BaseSearchMoreController {
+class ItemSearchMoreController
+ extends BaseSearchMoreController {
@override
Future> getData({bool isRefresh = false}) async {
- List newList = [
- SaleQuotationItemModel(
- name: '矿用本安型支架控制器', unit: '台', spec: 'ZDYZ-Z', cost: 4700),
- SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', cost: 1200),
- SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', cost: 5700),
- SaleQuotationItemModel(name: '矿用本安型隔离耦合器', cost: 1200),
- SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', cost: 600),
- SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-控制器', cost: 400),
- SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-驱动器', cost: 500),
- SaleQuotationItemModel(
- name: '钢丝编织橡胶护套连接器', remark: '控制器-隔离耦合器', cost: 2000),
- SaleQuotationItemModel(name: '矿用本安型支架控制器', cost: 4700),
- SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', cost: 1200),
- SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', cost: 5700),
- SaleQuotationItemModel(
- name: '电液换向阀(10功能10接口)', remark: '中间过渡架主阀组', cost: 13200),
- SaleQuotationItemModel(
- name: '电液换向阀(20功能20接口)', remark: '端头架主阀组', cost: 26500),
- SaleQuotationItemModel(
- name: '自动反冲洗过滤装置', remark: '流量:900L/min,过滤精度25μm', cost: 2000),
- SaleQuotationItemModel(name: '全自动反冲洗过滤器电缆', remark: '控制器-自动反冲洗'),
- SaleQuotationItemModel(name: '矿用本安型位移传感器'),
- SaleQuotationItemModel(name: '矿用本安型压力传感器'),
- SaleQuotationItemModel(name: '矿用本安型红外发射器'),
- SaleQuotationItemModel(name: '矿用本安型LED信号灯'),
- SaleQuotationItemModel(name: '倾角传感器'),
- SaleQuotationItemModel(name: '各类安装附件'),
- ];
- list.assignAll(newList
- .where((element) => PinyinHelper.getPinyin(element.name, separator: '')
- .contains(searchKey.value))
- .toList());
- return newList;
+ try {
+ final res = await Api.getSaleQuotationComponents();
+ List componets = res.data!.items
+ .map((e) => SaleQuotationComponentModel.fromJson(e))
+ .toList();
+ List newList = componets
+ .map((e) => SaleQuotationItemModel(
+ name: e.name!,
+ cost: e.unitPrice ?? 0.0,
+ unit: e.unit?.label ?? '',
+ remark: e.remark))
+ .toList();
+ // List newList = [
+ // SaleQuotationItemModel(
+ // name: '矿用本安型支架控制器', unit: '台', spec: 'ZDYZ-Z', cost: 4700),
+ // SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', cost: 1200),
+ // SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', cost: 5700),
+ // SaleQuotationItemModel(name: '矿用本安型隔离耦合器', cost: 1200),
+ // SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', cost: 600),
+ // SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-控制器', cost: 400),
+ // SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-驱动器', cost: 500),
+ // SaleQuotationItemModel(
+ // name: '钢丝编织橡胶护套连接器', remark: '控制器-隔离耦合器', cost: 2000),
+ // SaleQuotationItemModel(name: '矿用本安型支架控制器', cost: 4700),
+ // SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', cost: 1200),
+ // SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', cost: 5700),
+ // SaleQuotationItemModel(
+ // name: '电液换向阀(10功能10接口)', remark: '中间过渡架主阀组', cost: 13200),
+ // SaleQuotationItemModel(
+ // name: '电液换向阀(20功能20接口)', remark: '端头架主阀组', cost: 26500),
+ // SaleQuotationItemModel(
+ // name: '自动反冲洗过滤装置', remark: '流量:900L/min,过滤精度25μm', cost: 2000),
+ // SaleQuotationItemModel(name: '全自动反冲洗过滤器电缆', remark: '控制器-自动反冲洗'),
+ // SaleQuotationItemModel(name: '矿用本安型位移传感器'),
+ // SaleQuotationItemModel(name: '矿用本安型压力传感器'),
+ // SaleQuotationItemModel(name: '矿用本安型红外发射器'),
+ // SaleQuotationItemModel(name: '矿用本安型LED信号灯'),
+ // SaleQuotationItemModel(name: '倾角传感器'),
+ // SaleQuotationItemModel(name: '各类安装附件'),
+ // ];
+ list.assignAll(newList
+ .where((element) =>
+ PinyinHelper.getPinyin(element.name, separator: '')
+ .contains(searchKey.value))
+ .toList());
+ return newList;
+ } catch (e) {
+ return [];
+ }
}
}
diff --git a/lib/screens/sale_quotation/sale_quotation.dart b/lib/screens/sale_quotation/sale_quotation.dart
index 4e82895..c3047ef 100644
--- a/lib/screens/sale_quotation/sale_quotation.dart
+++ b/lib/screens/sale_quotation/sale_quotation.dart
@@ -6,12 +6,11 @@ import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/models/sale_quotation.model.dart';
import 'package:sk_base_mobile/screens/sale_quotation/components/sale_quotation_drawer.dart';
import 'package:sk_base_mobile/screens/sale_quotation/sale_quotation.controller.dart';
+import 'package:sk_base_mobile/util/common.util.dart';
import 'package:sk_base_mobile/util/modal.util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
-import 'package:sk_base_mobile/util/snack_bar.util.dart';
import 'package:sk_base_mobile/widgets/form_item/sk_number_input.dart';
import 'package:sk_base_mobile/widgets/form_item/sk_text_input.dart';
-import 'package:sk_base_mobile/widgets/empty.dart';
import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
import 'package:math_expressions/math_expressions.dart';
@@ -22,93 +21,75 @@ class SaleQuotationPage extends StatelessWidget {
final quantityWidth = 140.0;
final unitPriceWidth = 140.0;
final amountWidth = 140.0;
- final headerTitleStyle = TextStyle(
+ final TextStyle? headerTitleStyle = TextStyle(
fontSize: ScreenAdaper.height(30),
fontWeight: FontWeight.w600,
- color: AppTheme.nearlyBlack);
- final headerBgcolor = const Color.fromARGB(255, 238, 238, 238);
+ color: Theme.of(Get.context!).colorScheme.onPrimary);
+ final headerBgcolor = AppTheme.appbarBgColor;
@override
Widget build(BuildContext context) {
- return Scaffold(
- key: controller.scaffoldKey,
- endDrawer: const Drawer(
- // 从右到左出现
- child: SaleQuotationEndDrawer(),
- ),
- appBar: SkAppbar(
- title: '报价计算',
- action: [
- IconButton(
- onPressed: () {
- controller.scaffoldKey.currentState?.openEndDrawer();
- },
- icon: const Icon(Icons.more_horiz_outlined, color: AppTheme.white),
+ return Obx(() => Scaffold(
+ key: controller.scaffoldKey,
+ backgroundColor: AppTheme.nearlyWhite,
+ endDrawer: Drawer(
+ // 从右到左出现
+ child: SaleQuotationEndDrawer(),
),
- Container(
- padding: EdgeInsets.symmetric(
- vertical: ScreenAdaper.height(10),
- horizontal: ScreenAdaper.width(20)),
- child: Row(mainAxisAlignment: MainAxisAlignment.end, children: [
- GestureDetector(
- onTap: () {
- controller.addGroup();
+ appBar: SkAppbar(
+ title: '报价计算-${controller.templateName}',
+ action: [
+ IconButton(
+ onPressed: () {
+ controller.scaffoldKey.currentState?.openEndDrawer();
},
- child: Icon(
- Icons.add,
- size: ScreenAdaper.height(40),
- ),
- )
- ]),
- ),
- ],
- ),
- body: SafeArea(
- bottom: ScreenAdaper.isLandspace() ? false : true,
- child: Stack(children: [
- Column(
- children: [
- builderHeader(),
- Expanded(
- child: Obx(() => controller.groups.isEmpty
- ? Empty(text: '请先添加分组')
- : CustomScrollView(
- slivers: controller.groups
- .mapIndexed(
- (index, e) => buildBody(index))
- .toList(),
- ))),
- // 当键盘弹起时,不显示
-
- SizedBox(
- height: ScreenAdaper.height(100),
- child: buildTotalCostRow(),
+ icon: const Icon(Icons.more_horiz_outlined,
+ color: AppTheme.white),
),
- SizedBox(
- height: ScreenAdaper.height(100),
- child: buildTotalSalesPriceRow(),
- )
],
),
- // Positioned(
- // bottom: ScreenAdaper.height(220),
- // right: ScreenAdaper.height(10),
- // child: IconButton(
- // padding: EdgeInsets.all(ScreenAdaper.height(20)),
- // style: ButtonStyle(
- // backgroundColor: MaterialStateProperty.all(
- // AppTheme.primaryColorLight)),
- // onPressed: () {
- // controller.addGroup();
- // },
- // icon: Icon(
- // Icons.add,
- // size: ScreenAdaper.height(40),
- // ),
- // ))
- ]),
- ),
- );
+ body: SafeArea(
+ bottom: ScreenAdaper.isLandspace() ? false : true,
+ child: Stack(children: [
+ Column(
+ children: [
+ builderHeader(),
+ Expanded(
+ child: Obx(() => controller.groups.isEmpty
+ ? Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ '请右上角选择模板',
+ style: TextStyle(
+ fontSize: ScreenAdaper.height(30)),
+ ),
+ Text('或者左上角加号添加分组',
+ style: TextStyle(
+ fontSize: ScreenAdaper.height(30)))
+ ],
+ )
+ : CustomScrollView(
+ slivers: controller.groups
+ .mapIndexed(
+ (index, e) => buildBody(index))
+ .toList(),
+ ))),
+ // 当键盘弹起时,不显示
+
+ SizedBox(
+ height: ScreenAdaper.height(100),
+ child: buildTotalCostRow(),
+ ),
+ SizedBox(
+ height: ScreenAdaper.height(100),
+ child: buildTotalSalesPriceRow(),
+ )
+ ],
+ ),
+ ]),
+ ),
+ ));
}
Widget buildTotalSalesPriceRow() {
@@ -254,6 +235,19 @@ class SaleQuotationPage extends StatelessWidget {
bottom: ScreenAdaper.height(10)),
child: Row(
children: [
+ GestureDetector(
+ onTap: () {
+ controller.addGroup();
+ },
+ child: Icon(
+ Icons.add_circle_outline_outlined,
+ size: ScreenAdaper.height(40),
+ color: Theme.of(Get.context!).colorScheme.onPrimary,
+ ),
+ ),
+ SizedBox(
+ width: ScreenAdaper.width(5),
+ ),
Text(
'名称',
style: headerTitleStyle,
@@ -315,8 +309,13 @@ class SaleQuotationPage extends StatelessWidget {
color: AppTheme.nearlyBlack,
fontWeight: FontWeight.w600);
return Container(
+ decoration: BoxDecoration(
+ color: const Color.fromARGB(255, 235, 235, 235)
+ .withOpacity(1.0 - state.scrollPercentage),
+ border: const Border(
+ top: BorderSide(width: 1, color: AppTheme.dividerColor),
+ bottom: BorderSide(width: 1, color: AppTheme.dividerColor))),
height: ScreenAdaper.height(80),
- color: headerBgcolor.withOpacity(1.0 - state.scrollPercentage),
alignment: Alignment.centerLeft,
child: InkWell(
onTap: () {
@@ -469,7 +468,9 @@ class SaleQuotationPage extends StatelessWidget {
),
Expanded(
child: Text(
- '${controller.groups[groupIndex].items[rowIndex].remark ?? ''}${controller.groups[groupIndex].items[rowIndex].remark ?? ''}${controller.products[rowIndex].remark ?? ''}',
+ controller.groups[groupIndex].items[rowIndex]
+ .remark ??
+ '',
overflow: TextOverflow.ellipsis,
style: subTextStyle,
),
@@ -496,18 +497,19 @@ class SaleQuotationPage extends StatelessWidget {
value: controller.groups[groupIndex].items[rowIndex].quantity,
func: (value) {
// 取消失去焦点,并且弹窗警告
- bool isValid = value > 0;
- if (!isValid) {
- SnackBarUtil().error('数量必须>0,若想删除产品0,请左滑');
- }
- return isValid;
+ // bool isValid = (value ?? 0) > 0;
+ // if (!isValid) {
+ // SnackBarUtil().error('数量必须>0,若想删除产品0,请左滑');
+ // }
+ return true;
}),
- buildEditCell(
+ buildEditCell(
Container(
alignment: Alignment.center,
width: ScreenAdaper.width(unitPriceWidth),
child: Text(
- '${controller.groups[groupIndex].items[rowIndex].cost}',
+ CommonUtil.toNumberWithout0(
+ controller.groups[groupIndex].items[rowIndex].cost),
style: TextStyle(fontSize: ScreenAdaper.height(25)),
),
),
@@ -516,12 +518,13 @@ class SaleQuotationPage extends StatelessWidget {
field: 'cost',
inputWidth: ScreenAdaper.width(unitPriceWidth),
value: controller.groups[groupIndex].items[rowIndex].cost),
- buildEditCell(
+ buildEditCell(
Container(
alignment: Alignment.center,
width: ScreenAdaper.width(amountWidth),
child: Text(
- '${controller.groups[groupIndex].items[rowIndex].amount}',
+ CommonUtil.toNumberWithout0(
+ controller.groups[groupIndex].items[rowIndex].amount),
style: TextStyle(fontSize: ScreenAdaper.height(25)),
),
),
@@ -558,22 +561,24 @@ class SaleQuotationPage extends StatelessWidget {
onFieldSubmitted: (value) {
final editingData =
controller.groups[groupIndex].items[rowIndex].toJson();
- editingData[field] = value as T;
- controller.saveChanges(groupIndex, rowIndex,
+ editingData[field] = value;
+ controller.afterRowChanges(groupIndex, rowIndex,
SaleQuotationItemModel.fromJson(editingData), field);
},
onTapOutside: (dynamic value) {
+ value = value ?? 0;
final editingData =
controller.groups[groupIndex].items[rowIndex].toJson();
editingData[field] = value as T;
- controller.saveChanges(groupIndex, rowIndex,
+ controller.afterRowChanges(groupIndex, rowIndex,
SaleQuotationItemModel.fromJson(editingData), field);
},
contentPadding: EdgeInsets.symmetric(
horizontal: ScreenAdaper.width(20),
vertical: ScreenAdaper.height(10)),
- textController:
- TextEditingController(text: '${value == 0 ? '' : value}')),
+ textController: TextEditingController(
+ text:
+ value == 0 ? '' : CommonUtil.toNumberWithout0(value))),
)
: InkWell(
onTap: () {
diff --git a/lib/screens/workbench/workbench.dart b/lib/screens/workbench/workbench.dart
index 7c5095a..9f96a6c 100644
--- a/lib/screens/workbench/workbench.dart
+++ b/lib/screens/workbench/workbench.dart
@@ -43,7 +43,7 @@ class WorkBenchPage extends StatelessWidget {
.map((e) => e.name)
.firstWhereOrNull((name) => name == controller.menus[index].route);
if (route != null) {
- RouterUtil.toNamed(controller.menus[index].route);
+ RouterUtil.toNamed(controller.menus[index].route!);
} else {
SnackBarUtil().info('您没有权限,请联系管理员。后期会隐藏没有权限的功能');
}
diff --git a/lib/services/app_info.service.dart b/lib/services/app_info.service.dart
index c8e9dd9..48ace23 100644
--- a/lib/services/app_info.service.dart
+++ b/lib/services/app_info.service.dart
@@ -11,6 +11,7 @@ import 'package:sk_base_mobile/apis/api.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/config.dart';
import 'package:sk_base_mobile/models/app_config.dart';
+import 'package:sk_base_mobile/router/router.util.dart';
import 'package:sk_base_mobile/services/service.dart';
import 'package:sk_base_mobile/util/device.util.dart';
import 'package:sk_base_mobile/util/logger_util.dart';
@@ -125,6 +126,7 @@ class AppInfoService extends GetxService {
ModalUtil.alert(
// title: '有新版本',
builder: (_) => UpgradeConfirm(
+ version: newVersion,
onConfirm: () {
upgradeApp(newVersion);
},
@@ -156,8 +158,7 @@ class AppInfoService extends GetxService {
try {
/// 下载
downloadProgress.value = 0;
- Get.back();
-
+ await RouterUtil.back();
ModalUtil.alert(
barrierDismissible: false,
contentPadding: EdgeInsets.symmetric(vertical: 0),
@@ -167,7 +168,7 @@ class AppInfoService extends GetxService {
Obx(
() => Stack(
children: [
- Container(
+ SizedBox(
height: ScreenAdaper.height(40),
child: LinearProgressIndicator(
value: downloadProgress.value,
@@ -191,7 +192,7 @@ class AppInfoService extends GetxService {
responseType: ResponseType.bytes,
followRedirects: false,
));
- Get.back();
+ await RouterUtil.back();
InstallPlugin.install(
file.path,
).then((result) {}).catchError((error) {});
diff --git a/lib/services/dio.service.dart b/lib/services/dio.service.dart
index e0f4dd7..5ad7b16 100644
--- a/lib/services/dio.service.dart
+++ b/lib/services/dio.service.dart
@@ -135,7 +135,7 @@ class DioService extends get_package.GetxService {
}
if (response.data != null && response.data is Map) {
if (response.data['code'] == 200) {
- if (GloablConfig.DEBUG) LoggerUtil().info(response.data['data']);
+ // if (GloablConfig.DEBUG) LoggerUtil().info(response.data['data']);
response.data = response.data['data'];
// 分页数据处理
if (response.data != null &&
diff --git a/lib/util/common.util.dart b/lib/util/common.util.dart
index 7ecdb39..898c4b2 100644
--- a/lib/util/common.util.dart
+++ b/lib/util/common.util.dart
@@ -36,6 +36,10 @@ class CommonUtil {
}
return result;
}
+
+ static String toNumberWithout0(num num) {
+ return num.toStringAsFixed(num.truncateToDouble() == num ? 0 : 2);
+ }
}
class TreeNode {
diff --git a/lib/util/modal.util.dart b/lib/util/modal.util.dart
index 41bbc7e..559fc6a 100644
--- a/lib/util/modal.util.dart
+++ b/lib/util/modal.util.dart
@@ -33,12 +33,12 @@ class ModalUtil {
titlePadding: EdgeInsets.zero,
contentPadding: contentPadding ??
EdgeInsets.symmetric(
- vertical: ScreenAdaper.height(10),
- ),
+ vertical: ScreenAdaper.height(15),
+ horizontal: ScreenAdaper.width(25)),
title: title != null
? Container(
padding: EdgeInsets.symmetric(
- vertical: ScreenAdaper.height(20),
+ vertical: ScreenAdaper.height(10),
),
decoration: const BoxDecoration(
border: Border(
@@ -59,7 +59,7 @@ class ModalUtil {
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.w500,
- fontSize: ScreenAdaper.height(30),
+ fontSize: ScreenAdaper.height(25),
color: AppTheme.black,
),
)
diff --git a/lib/util/photo_picker_util.dart b/lib/util/photo_picker_util.dart
index 7b81aa4..c39a339 100644
--- a/lib/util/photo_picker_util.dart
+++ b/lib/util/photo_picker_util.dart
@@ -1,6 +1,7 @@
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
+import 'package:sk_base_mobile/router/router.util.dart';
import 'package:sk_base_mobile/util/media_util.dart';
import 'package:sk_base_mobile/app_theme.dart';
@@ -13,7 +14,7 @@ class PhotoPickerUtil {
title: Text(title),
cancelButton: CupertinoActionSheetAction(
onPressed: () {
- Get.back();
+ RouterUtil.back();
},
child: const Text(
'取消',
@@ -22,7 +23,7 @@ class PhotoPickerUtil {
actions: ['拍照', '相册']
.map((item) => CupertinoActionSheetAction(
onPressed: () async {
- Get.back();
+ await RouterUtil.back();
XFile? pickedFile = item == '拍照'
? await MediaUtil().getImageFromCamera()
: await MediaUtil().getImageFromGallery();
diff --git a/lib/util/snack_bar.util.dart b/lib/util/snack_bar.util.dart
index 1b958bb..e19c56b 100644
--- a/lib/util/snack_bar.util.dart
+++ b/lib/util/snack_bar.util.dart
@@ -23,7 +23,7 @@ class SnackBarUtil {
}
}
if (Get.isSnackbarOpen) {
- await Get.closeCurrentSnackbar();
+ Get.back();
}
Get.rawSnackbar(
snackPosition: SnackPosition.TOP,
@@ -46,7 +46,7 @@ class SnackBarUtil {
Future success(String title, {String? message}) async {
if (checkIsSnackBarInit()) {
if (Get.isSnackbarOpen) {
- await Get.closeCurrentSnackbar();
+ Get.back();
}
Get.rawSnackbar(
message: title,
diff --git a/lib/widgets/core/sk_base_date_picker.dart b/lib/widgets/core/sk_base_date_picker.dart
index d15def2..489f901 100644
--- a/lib/widgets/core/sk_base_date_picker.dart
+++ b/lib/widgets/core/sk_base_date_picker.dart
@@ -1,6 +1,6 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
-import 'package:get/get.dart';
+import 'package:sk_base_mobile/router/router.util.dart';
import 'package:sk_base_mobile/util/logger_util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
@@ -32,7 +32,7 @@ class SkBaseDatePicker extends StatelessWidget {
children: [
TextButton(
onPressed: () {
- Get.back();
+ RouterUtil.back();
},
child: Text(
'取消',
@@ -41,7 +41,7 @@ class SkBaseDatePicker extends StatelessWidget {
const Spacer(),
TextButton(
onPressed: () {
- Get.back();
+ RouterUtil.back();
final year = 2000 + yearController.selectedItem;
String month = '${monthController.selectedItem + 1}';
if (int.parse(month) < 10) {
diff --git a/lib/widgets/core/sk_bottomsheet_picker.dart b/lib/widgets/core/sk_bottomsheet_picker.dart
index 60c4443..af629b5 100644
--- a/lib/widgets/core/sk_bottomsheet_picker.dart
+++ b/lib/widgets/core/sk_bottomsheet_picker.dart
@@ -1,7 +1,7 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
-import 'package:get/get.dart';
import 'package:sk_base_mobile/app_theme.dart';
+import 'package:sk_base_mobile/router/router.util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
class SkBottomSheetPicker extends StatelessWidget {
@@ -39,7 +39,7 @@ class SkBottomSheetPicker extends StatelessWidget {
children: [
TextButton(
onPressed: () {
- Get.back();
+ RouterUtil.back();
},
child: Text(
'取消',
@@ -57,7 +57,7 @@ class SkBottomSheetPicker extends StatelessWidget {
)),
TextButton(
onPressed: () {
- Get.back();
+ RouterUtil.back();
onChanged!(
firstLevelControlller.selectedItem,
(secondLevel ?? []).isNotEmpty
diff --git a/lib/widgets/core/sk_cascade_picker.dart b/lib/widgets/core/sk_cascade_picker.dart
index 19f5838..8d9fc2e 100644
--- a/lib/widgets/core/sk_cascade_picker.dart
+++ b/lib/widgets/core/sk_cascade_picker.dart
@@ -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/router/router.util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/widgets/core/sk_ink.dart';
import 'package:sk_base_mobile/widgets/loading_indicator.dart';
@@ -65,7 +66,7 @@ class SkCascadePicker extends StatelessWidget {
children: [
SkInk(
onTap: () {
- Get.back();
+ RouterUtil.back();
},
child: Text(
'取消',
@@ -74,7 +75,7 @@ class SkCascadePicker extends StatelessWidget {
const Spacer(),
SkInk(
onTap: () {
- Get.back();
+ RouterUtil.back();
if (onConfirm != null) {
onConfirm!(cascadeController.selectedTabs
.where((e) =>
@@ -98,7 +99,7 @@ class SkCascadePicker extends StatelessWidget {
// GradientButton(
// buttonText: '确定',
// onPressed: () {
- // Get.back();
+ // await RouterUtil.back();
// if (onConfirm != null) {
// onConfirm!(cascadeController.selectedTabs
// .where((e) =>
diff --git a/lib/widgets/core/sk_dialog_header.dart b/lib/widgets/core/sk_dialog_header.dart
index 5613e9d..e154156 100644
--- a/lib/widgets/core/sk_dialog_header.dart
+++ b/lib/widgets/core/sk_dialog_header.dart
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
-import 'package:get/get.dart';
import 'package:sk_base_mobile/app_theme.dart';
+import 'package:sk_base_mobile/router/router.util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
class SkDialogHeader extends StatelessWidget {
@@ -39,11 +39,11 @@ class SkDialogHeader extends StatelessWidget {
),
),
trailing ??
- Container(
+ SizedBox(
width: ScreenAdaper.width(100),
child: IconButton(
onPressed: () {
- Get.back();
+ RouterUtil.back();
},
icon: Icon(
Icons.close,
diff --git a/lib/widgets/core/sk_flat_button.dart b/lib/widgets/core/sk_flat_button.dart
new file mode 100644
index 0000000..b73d390
--- /dev/null
+++ b/lib/widgets/core/sk_flat_button.dart
@@ -0,0 +1,56 @@
+import 'package:flutter/material.dart';
+import 'package:loading_animation_widget/loading_animation_widget.dart';
+import 'package:sk_base_mobile/app_theme.dart';
+import 'package:sk_base_mobile/constants/constants.dart';
+import 'package:sk_base_mobile/util/screen_adaper_util.dart';
+import 'package:sk_base_mobile/widgets/core/sk_ink.dart';
+
+class SkFlatButton extends StatelessWidget {
+ final VoidCallback? onPressed;
+ final bool isLoading;
+ final String buttonText;
+ final Icon? icon;
+ final Color? color;
+ final BorderRadiusGeometry? borderRadius;
+ const SkFlatButton(
+ {super.key,
+ this.buttonText = TextEnum.createInventoryInOutBtnText,
+ this.onPressed,
+ this.icon,
+ this.color,
+ this.borderRadius,
+ this.isLoading = false});
+ @override
+ Widget build(BuildContext context) {
+ return SkInk(
+ onTap: onPressed ?? () {},
+ color: color ?? AppTheme.primaryColor,
+ child: SizedBox(
+ height: ScreenAdaper.height(80),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ if (icon != null) ...[
+ icon!,
+ SizedBox(
+ width: ScreenAdaper.width(10),
+ )
+ ],
+ isLoading
+ ? LoadingAnimationWidget.fourRotatingDots(
+ color: AppTheme.nearlyWhite,
+ size: ScreenAdaper.height(40),
+ )
+ : Text(
+ buttonText,
+ style: TextStyle(
+ color: AppTheme.nearlyWhite,
+ fontWeight: FontWeight.bold,
+ fontSize: ScreenAdaper.height(25),
+ ),
+ )
+ ])),
+ );
+ }
+}
diff --git a/lib/widgets/core/sk_ink.dart b/lib/widgets/core/sk_ink.dart
index 9c14a6f..ef84c57 100644
--- a/lib/widgets/core/sk_ink.dart
+++ b/lib/widgets/core/sk_ink.dart
@@ -29,11 +29,11 @@ class SkInk extends StatelessWidget {
child: ClipRRect(
borderRadius: borderRadius ?? BorderRadius.zero,
child: Material(
+ color: color ?? Colors.transparent,
child: Ink(
padding: padding,
decoration: BoxDecoration(
border: border,
- color: color ?? AppTheme.nearlyWhite,
gradient: gradient,
borderRadius: borderRadius,
),
diff --git a/lib/widgets/empty.dart b/lib/widgets/empty.dart
index ddb2aa2..c58092d 100644
--- a/lib/widgets/empty.dart
+++ b/lib/widgets/empty.dart
@@ -1,27 +1,34 @@
-import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
+import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
class Empty extends StatelessWidget {
final String? text;
- const Empty({super.key, this.text});
+ final Widget? icon;
+ final void Function()? onTap;
+ const Empty({super.key, this.text, this.icon, this.onTap});
@override
Widget build(BuildContext context) {
- return Center(
- child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
- Center(
- child: Icon(Icons.error_outline,
- size: ScreenAdaper.height(40), color: Colors.red)),
- SizedBox(
- width: ScreenAdaper.width(10),
- ),
- Text(
- text ?? '',
- textAlign: TextAlign.center,
- style: TextStyle(fontSize: ScreenAdaper.height(35)),
- ),
- ]),
+ return GestureDetector(
+ onTap: onTap,
+ child: Center(
+ child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
+ Center(
+ child: icon ??
+ Icon(Icons.error_outline,
+ size: ScreenAdaper.height(40),
+ color: AppTheme.dangerColor)),
+ SizedBox(
+ width: ScreenAdaper.width(10),
+ ),
+ Text(
+ text ?? '',
+ textAlign: TextAlign.center,
+ style: TextStyle(fontSize: ScreenAdaper.height(35)),
+ ),
+ ]),
+ ),
);
}
}
diff --git a/lib/widgets/form_item/sk_multi_search_more.dart b/lib/widgets/form_item/sk_multi_search_more.dart
index 6e97cf0..22c84cb 100644
--- a/lib/widgets/form_item/sk_multi_search_more.dart
+++ b/lib/widgets/form_item/sk_multi_search_more.dart
@@ -5,10 +5,12 @@ import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/constants/bg_color.dart';
import 'package:sk_base_mobile/models/base_search_more.model.dart';
import 'package:sk_base_mobile/models/base_search_more_controller.dart';
+import 'package:sk_base_mobile/router/router.util.dart';
import 'package:sk_base_mobile/util/debouncer.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/widgets/empty.dart';
import 'package:sk_base_mobile/widgets/gradient_button.dart';
+import 'package:sk_base_mobile/widgets/loading_indicator.dart';
class SkMutilSearchMore extends StatelessWidget {
final Function(List)? onOk;
@@ -18,7 +20,6 @@ class SkMutilSearchMore extends StatelessWidget {
final bool enablePullUp;
final bool enablePullDown;
final bool isDialog;
-
final Function(int)? leadingBuilder;
SkMutilSearchMore(
{super.key,
@@ -33,42 +34,45 @@ class SkMutilSearchMore extends StatelessWidget {
TextStyle(fontSize: ScreenAdaper.height(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: [
- buildDialogHeader(),
- SizedBox(
- height: ScreenAdaper.height(defaultPadding) / 2,
- ),
- buildSearchBar(),
- SizedBox(
- height: ScreenAdaper.height(defaultPadding) / 2,
- ),
- Expanded(child: buildList()),
- SizedBox(
- height: ScreenAdaper.height(defaultPadding) / 2,
- ),
- GradientButton(
- onPressed: () {
- if (onOk != null) {
- onOk!(controller.selectedIndex);
- }
- if (isDialog) {
- Get.back();
- }
- controller.selectedIndex.clear();
- },
- buttonText: '确定',
- )
- ],
- ));
+ return Column(
+ children: [
+ Expanded(
+ child: Container(
+ padding: EdgeInsets.symmetric(
+ horizontal: ScreenAdaper.width(20),
+ vertical: ScreenAdaper.height(20)),
+ child: Column(
+ children: [
+ buildDialogHeader(),
+ SizedBox(
+ height: ScreenAdaper.height(defaultPadding) / 2,
+ ),
+ buildSearchBar(),
+ SizedBox(
+ height: ScreenAdaper.height(defaultPadding) / 2,
+ ),
+ Expanded(child: buildList()),
+ ],
+ )),
+ ),
+ GradientButton(
+ onPressed: () {
+ if (onOk != null) {
+ onOk!(controller.selectedIndex);
+ }
+ if (isDialog) {
+ RouterUtil.back();
+ }
+ controller.selectedIndex.clear();
+ },
+ buttonText: '确定',
+ )
+ ],
+ );
}
Widget buildDialogHeader() {
- return Container(
+ return SizedBox(
height: ScreenAdaper.height(60),
child: Stack(
children: [
@@ -84,7 +88,7 @@ class SkMutilSearchMore extends StatelessWidget {
right: 0,
child: GestureDetector(
onTap: () {
- Get.back();
+ RouterUtil.back();
},
child: Icon(
Icons.close,
@@ -135,23 +139,25 @@ class SkMutilSearchMore extends StatelessWidget {
}
Widget buildList() {
- return Obx(() => SmartRefresher(
- enablePullDown: enablePullDown,
- enablePullUp: enablePullUp,
- controller: controller.refreshController,
- onLoading: controller.onLoading,
- onRefresh: controller.onRefresh,
- child: controller.list.isEmpty
- ? const Center(
- child: Empty(text: '暂无数据'),
- )
- : ListView.separated(
- separatorBuilder: (context, index) => const Divider(
- color: AppTheme.dividerColor,
- height: 1,
- ),
- itemCount: controller.list.length,
- itemBuilder: (_, index) => buildItem(index))));
+ return Obx(() => controller.loading.value
+ ? const LoadingIndicator(common: true)
+ : SmartRefresher(
+ enablePullDown: enablePullDown,
+ enablePullUp: enablePullUp,
+ controller: controller.refreshController,
+ onLoading: controller.onLoading,
+ onRefresh: controller.onRefresh,
+ child: controller.list.isEmpty
+ ? const Center(
+ child: Empty(text: '暂无数据'),
+ )
+ : ListView.separated(
+ separatorBuilder: (context, index) => const Divider(
+ color: AppTheme.dividerColor,
+ height: 1,
+ ),
+ itemCount: controller.list.length,
+ itemBuilder: (_, index) => buildItem(index))));
}
Widget buildItem(int index) {
diff --git a/lib/widgets/form_item/sk_number_input.dart b/lib/widgets/form_item/sk_number_input.dart
index 2ff57c9..5106c36 100644
--- a/lib/widgets/form_item/sk_number_input.dart
+++ b/lib/widgets/form_item/sk_number_input.dart
@@ -7,7 +7,7 @@ class SkNumberInput extends StatefulWidget {
final TextEditingController textController;
final Function(FocusNode)? onTap;
final Function(String)? onChanged;
- final Function(dynamic)? onTapOutside;
+ final void Function(dynamic)? onTapOutside;
final Function(dynamic)? validator;
final bool isDense;
@@ -16,7 +16,7 @@ class SkNumberInput extends StatefulWidget {
final bool isRequired;
final String labelText;
final String? hint;
- final ValueChanged? onFieldSubmitted;
+ final ValueChanged? onFieldSubmitted;
const SkNumberInput(
{super.key,
required this.textController,
@@ -76,13 +76,13 @@ class _SkNumberInputState extends State {
},
onTapOutside: (event) {
if (widget.onTapOutside != null) {
- dynamic value;
+ dynamic value = widget.textController.text;
if (T == double) {
- value = double.tryParse(widget.textController.text);
+ value = double.tryParse(widget.textController.text) ?? 0.0;
widget.onTapOutside!(value as T);
} else {
- value = int.tryParse(widget.textController.text);
+ value = int.tryParse(widget.textController.text) ?? 0;
}
if (widget.validator != null && !widget.validator!(value)) {
return;
diff --git a/lib/widgets/form_item/sk_single_search_more.dart b/lib/widgets/form_item/sk_single_search_more.dart
index 561706d..acb301d 100644
--- a/lib/widgets/form_item/sk_single_search_more.dart
+++ b/lib/widgets/form_item/sk_single_search_more.dart
@@ -4,6 +4,7 @@ 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/base_search_more_controller.dart';
+import 'package:sk_base_mobile/router/router.util.dart';
import 'package:sk_base_mobile/util/debouncer.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/widgets/empty.dart';
@@ -52,7 +53,7 @@ class SkSingleSearchMore extends StatelessWidget {
}
Widget buildDialogHeader() {
- return Container(
+ return SizedBox(
height: ScreenAdaper.height(60),
child: Stack(
children: [
@@ -68,7 +69,7 @@ class SkSingleSearchMore extends StatelessWidget {
right: 0,
child: GestureDetector(
onTap: () {
- Get.back();
+ RouterUtil.back();
},
child: Icon(
Icons.close,
diff --git a/lib/widgets/form_item/sk_text_input.dart b/lib/widgets/form_item/sk_text_input.dart
index c3664e0..096ec22 100644
--- a/lib/widgets/form_item/sk_text_input.dart
+++ b/lib/widgets/form_item/sk_text_input.dart
@@ -22,7 +22,7 @@ class SkTextInput extends SkBaseFieldWidget {
final InputBorder? border;
final FloatingLabelBehavior? floatingLabelBehavior;
final TextInputType? keyboardType;
-
+ final double? height;
SkTextInput(
{super.key,
super.customLabel = false,
@@ -40,6 +40,7 @@ class SkTextInput extends SkBaseFieldWidget {
this.suffixIcon,
this.onChanged,
this.border,
+ this.height,
this.floatingLabelBehavior = FloatingLabelBehavior.always,
this.isTextArea = false,
this.contentPadding,
@@ -48,69 +49,76 @@ class SkTextInput extends SkBaseFieldWidget {
@override
Widget build(BuildContext context) {
- Widget field = TextFormField(
- focusNode: focusNode,
- controller: textController,
- onChanged: (String value) {
- if (onChanged != null) {
- onChanged!(value);
- }
- },
- onTapOutside: (event) {
- if (onTapOutside != null) {
- onTapOutside!(textController.text);
- FocusScope.of(context).unfocus();
- }
- },
- maxLines: isTextArea ? 2 : 1, // 添加这行代码
- onTap: () {
- if (onTap != null) {
- onTap!(focusNode);
- }
- },
- keyboardType: keyboardType,
- onFieldSubmitted: onFieldSubmitted,
- autovalidateMode: AutovalidateMode.onUserInteraction,
- validator: validator,
+ Widget field = SizedBox(
+ height: height,
+ child: TextFormField(
+ focusNode: focusNode,
+ controller: textController,
+ onChanged: (String value) {
+ if (onChanged != null) {
+ onChanged!(value);
+ }
+ },
+ onTapOutside: (event) {
+ if (onTapOutside != null) {
+ onTapOutside!(textController.text);
+ FocusScope.of(context).unfocus();
+ }
+ },
+ maxLines: isTextArea ? 2 : 1, // 添加这行代码
+ onTap: () {
+ if (onTap != null) {
+ onTap!(focusNode);
+ }
+ },
+ keyboardType: keyboardType,
+ onFieldSubmitted: onFieldSubmitted,
+ autovalidateMode: AutovalidateMode.onUserInteraction,
+ validator: validator,
- decoration: InputDecoration(
- prefixIcon: prefix,
- suffixIcon: suffixIcon,
- fillColor: fillColor,
- filled: true,
- errorStyle: const TextStyle(fontSize: 0, height: 0.01),
- contentPadding: contentPadding,
- isDense: isDense,
- border: border,
- floatingLabelBehavior: floatingLabelBehavior,
- label: labelText != null && !customLabel
- ? Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- mainAxisSize: MainAxisSize.min,
- children: [
- if (isRequired)
+ decoration: InputDecoration(
+ prefixIcon: prefix,
+ suffixIcon: suffixIcon,
+ fillColor: fillColor,
+ filled: true,
+ errorStyle: const TextStyle(fontSize: 0, height: 0.01),
+ contentPadding: contentPadding,
+ isDense: isDense,
+ border: border,
+ floatingLabelBehavior: floatingLabelBehavior,
+ label: labelText != null && !customLabel
+ ? Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ if (isRequired)
+ Text(
+ "*",
+ style: TextStyle(
+ color: Colors.red,
+ fontSize: ScreenAdaper.height(30)),
+ ),
Text(
- "*",
- style: TextStyle(
- color: Colors.red,
- fontSize: ScreenAdaper.height(30)),
+ labelText!,
+ style: TextStyle(fontSize: ScreenAdaper.height(30)),
),
- Text(
- labelText!,
- style: TextStyle(fontSize: ScreenAdaper.height(30)),
- ),
- ])
- : null,
- hintText: hint ?? '请输入',
+ ])
+ : null,
+ hintText: hint ?? '请输入',
+ ),
),
);
- return SkFormItem(
- controller: baseFieldController,
- customLabel: customLabel,
- labelText: labelText,
- isRequired: isRequired,
- child: field,
- );
+ if (customLabel) {
+ return SkFormItem(
+ controller: baseFieldController,
+ customLabel: customLabel,
+ labelText: labelText,
+ isRequired: isRequired,
+ child: field,
+ );
+ } else {
+ return field;
+ }
}
}
diff --git a/lib/widgets/gradient_button.dart b/lib/widgets/gradient_button.dart
index 363791a..0d7a040 100644
--- a/lib/widgets/gradient_button.dart
+++ b/lib/widgets/gradient_button.dart
@@ -10,7 +10,7 @@ class GradientButton extends StatelessWidget {
final bool isLoading;
final String buttonText;
final Icon? icon;
- final BorderRadiusGeometry? borderRadius;
+ final BorderRadius? borderRadius;
const GradientButton(
{super.key,
this.buttonText = TextEnum.createInventoryInOutBtnText,
@@ -21,6 +21,7 @@ class GradientButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SkInk(
+ borderRadius: borderRadius,
onTap: onPressed ?? () {},
gradient: const LinearGradient(
colors: [AppTheme.primaryColorLight, AppTheme.primaryColor]),
diff --git a/lib/widgets/image_preview.dart b/lib/widgets/image_preview.dart
index da5318c..9dfc368 100644
--- a/lib/widgets/image_preview.dart
+++ b/lib/widgets/image_preview.dart
@@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
-import 'package:get/get.dart';
import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/config.dart';
import 'package:sk_base_mobile/models/file.model.dart';
+import 'package:sk_base_mobile/router/router.util.dart';
import 'package:sk_base_mobile/util/loading_util.dart';
import 'package:sk_base_mobile/util/media_util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
@@ -96,7 +96,7 @@ class _ImagePreivewState extends State {
final FileModel item = widget.galleryItems[index];
return PhotoViewGalleryPageOptions(
onTapUp: (_, tapUpDetails, value) {
- Get.back();
+ RouterUtil.back();
},
imageProvider: Image.network('${GloablConfig.OSS_URL}${item.path}').image,
initialScale: PhotoViewComputedScale.contained,
diff --git a/lib/widgets/my_avatar.dart b/lib/widgets/my_avatar.dart
index e73e07d..6944726 100644
--- a/lib/widgets/my_avatar.dart
+++ b/lib/widgets/my_avatar.dart
@@ -80,7 +80,7 @@ class MyAvatarController extends GetxController {
// }
// }
// uploadImgFilePath(pickedFile.path);
- // Get.back();
+ // await RouterUtil.back();
} catch (e) {
SnackBarUtil().error('Update avatar failed.');
} finally {
diff --git a/lib/widgets/upgrade_confirm.dart b/lib/widgets/upgrade_confirm.dart
index e5610c1..4f71b8c 100644
--- a/lib/widgets/upgrade_confirm.dart
+++ b/lib/widgets/upgrade_confirm.dart
@@ -1,13 +1,19 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sk_base_mobile/app_theme.dart';
+import 'package:sk_base_mobile/router/router.util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/widgets/gradient_button.dart';
class UpgradeConfirm extends StatelessWidget {
final void Function()? onConfirm;
final bool forceUpgrade;
- const UpgradeConfirm({super.key, this.onConfirm, this.forceUpgrade = true});
+ final String version;
+ const UpgradeConfirm(
+ {super.key,
+ this.onConfirm,
+ this.forceUpgrade = true,
+ required this.version});
@override
Widget build(BuildContext context) {
@@ -33,7 +39,7 @@ class UpgradeConfirm extends StatelessWidget {
child: Column(
children: [
Text(
- '有新的版本更新啦',
+ '有新的版本v_${version}更新啦',
style: TextStyle(
decoration: TextDecoration.none,
fontSize: ScreenAdaper.height(35),
@@ -83,7 +89,7 @@ class UpgradeConfirm extends StatelessWidget {
child: // 更新内容
IconButton(
onPressed: () {
- Get.back();
+ RouterUtil.back();
},
icon: const Icon(Icons.close)),
),
diff --git a/pubspec.yaml b/pubspec.yaml
index 3f23455..8fa785f 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.devz
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
-version: 1.0.0+1
+version: 1.0.2+1
environment:
sdk: '>=3.2.6 <4.0.0'