diff --git a/assets/icons/help_desk.png b/assets/icons/help_desk.png
new file mode 100644
index 0000000..fa971c1
Binary files /dev/null and b/assets/icons/help_desk.png differ
diff --git a/assets/icons/usage_guide.svg b/assets/icons/usage_guide.svg
new file mode 100644
index 0000000..ae440e5
--- /dev/null
+++ b/assets/icons/usage_guide.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/images/sale_quotation_guide1.jpg b/assets/images/sale_quotation_guide1.jpg
new file mode 100644
index 0000000..563f894
Binary files /dev/null and b/assets/images/sale_quotation_guide1.jpg differ
diff --git a/assets/images/sale_quotation_guide2.jpg b/assets/images/sale_quotation_guide2.jpg
new file mode 100644
index 0000000..27ba03f
Binary files /dev/null and b/assets/images/sale_quotation_guide2.jpg differ
diff --git a/assets/images/sale_quotation_guide3.jpg b/assets/images/sale_quotation_guide3.jpg
new file mode 100644
index 0000000..9c4b766
Binary files /dev/null and b/assets/images/sale_quotation_guide3.jpg differ
diff --git a/assets/images/sale_quotation_guide4.jpg b/assets/images/sale_quotation_guide4.jpg
new file mode 100644
index 0000000..c0adc87
Binary files /dev/null and b/assets/images/sale_quotation_guide4.jpg differ
diff --git a/lib/models/sale_quotation.model.dart b/lib/models/sale_quotation.model.dart
index 91397e9..45db788 100644
--- a/lib/models/sale_quotation.model.dart
+++ b/lib/models/sale_quotation.model.dart
@@ -22,37 +22,37 @@ class SaleQuotationModel extends BaseSearchMoreModel {
class SaleQuotationItemModel extends BaseSearchMoreModel {
final String name;
// 规格
- final String? spec;
+ final String? componentSpecification;
final String? unit;
final String? remark;
// 成本
- num cost;
+ num unitPrice;
int quantity;
num amount;
SaleQuotationItemModel(
{required this.name,
- this.spec,
+ this.componentSpecification,
this.unit,
this.remark,
- this.cost = 0,
+ this.unitPrice = 0,
this.quantity = 0,
this.amount = 0});
SaleQuotationItemModel.fromJson(Map json)
: name = json['name'],
- spec = json['spec'],
+ componentSpecification = json['componentSpecification'],
unit = json['unit'],
remark = json['remark'],
- cost = json['cost'],
+ unitPrice = json['unitPrice'],
quantity = json['quantity'],
amount = json['amount'];
// 生成tojson
Map toJson() => {
'name': name,
- 'spec': spec,
+ 'componentSpecification': componentSpecification,
'unit': unit,
'remark': remark,
- 'cost': cost,
+ 'unitPrice': unitPrice,
'quantity': quantity,
'amount': amount,
};
diff --git a/lib/router/router.dart b/lib/router/router.dart
index 9eb24f8..d7a3505 100644
--- a/lib/router/router.dart
+++ b/lib/router/router.dart
@@ -1,6 +1,8 @@
import 'package:get/get.dart';
import 'package:sk_base_mobile/models/user_info.model.dart';
import 'package:sk_base_mobile/router/auth_middleware.dart';
+import 'package:sk_base_mobile/screens/guide/components/sale_quotation_guide.dart';
+import 'package:sk_base_mobile/screens/guide/guide.dart';
import 'package:sk_base_mobile/screens/hr_manage/components/employee_detail.dart';
import 'package:sk_base_mobile/screens/hr_manage/hr_manage.dart';
import 'package:sk_base_mobile/screens/inventory/inventory.dart';
@@ -18,8 +20,18 @@ class RouteConfig {
static const String saleQuotation = '/workbench/sale_quotation';
static const String hrManage = '/workbench/hr_manage';
static const String employeeDetail = '/employee_detail';
+ static const String guide = '/guide';
+ static const String saleQuotationGuide = '/sale_quotation_guide';
static final List getPages = [
+ GetPage(
+ name: saleQuotationGuide,
+ page: () => SaleQuotationGuide(),
+ ),
+ GetPage(
+ name: guide,
+ page: () => GuidePage(),
+ ),
GetPage(
name: login,
page: () => LoginScreen(),
diff --git a/lib/router/router.util.dart b/lib/router/router.util.dart
index 0043046..c008498 100644
--- a/lib/router/router.util.dart
+++ b/lib/router/router.util.dart
@@ -10,6 +10,7 @@ class RouterUtil {
RouteConfig.inventory,
RouteConfig.saleQuotation,
];
+ // static List whiteList = [RouteConfig.saleQuotationGuide];
static Future toNamed(String routeName, {arguments}) async {
//关闭键盘
if (Get.context != null) {
diff --git a/lib/screens/guide/components/sale_quotation_guide.dart b/lib/screens/guide/components/sale_quotation_guide.dart
new file mode 100644
index 0000000..f9c9584
--- /dev/null
+++ b/lib/screens/guide/components/sale_quotation_guide.dart
@@ -0,0 +1,76 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:sk_base_mobile/app_theme.dart';
+import 'package:sk_base_mobile/util/screen_adaper_util.dart';
+import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
+import 'package:sk_base_mobile/widgets/custom_contact_icon.dart';
+
+class SaleQuotationGuide extends StatelessWidget {
+ final controller = Get.put(SaleQuotationGuideController());
+ SaleQuotationGuide({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: AppTheme.black,
+ appBar: SkAppbar(
+ title: '使用教程',
+ action: [
+ const CustomContactIcon(),
+ SizedBox(width: ScreenAdaper.width(20))
+ ],
+ ),
+ body: buildBody(),
+ );
+ }
+
+ Widget buildBody() {
+ return Stack(
+ alignment: AlignmentDirectional.bottomCenter,
+ children: [
+ Padding(
+ padding: EdgeInsets.symmetric(
+ vertical: ScreenAdaper.height(40),
+ horizontal: ScreenAdaper.width(20)),
+ child: PageView.builder(
+ controller: controller.pageController,
+ itemBuilder: (_, int index) => Container(
+ decoration: BoxDecoration(
+ image: DecorationImage(
+ image: AssetImage(
+ 'assets/images/sale_quotation_guide${index + 1}.jpg'),
+ ),
+ ),
+ ),
+ itemCount: 4,
+ onPageChanged: (index) {
+ controller.currentPage.value = index;
+ },
+ ),
+ ),
+ Obx(() => Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: List.generate(
+ 4,
+ (index) => Container(
+ margin: const EdgeInsets.all(4.0),
+ width: 10.0,
+ height: 10.0,
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ color: controller.currentPage.value == index
+ ? AppTheme.primaryColorLight
+ : Colors.grey,
+ ),
+ ),
+ ),
+ )),
+ ],
+ );
+ }
+}
+
+class SaleQuotationGuideController extends GetxController {
+ final currentPage = 0.obs;
+ final pageController = PageController();
+}
diff --git a/lib/screens/guide/guide.dart b/lib/screens/guide/guide.dart
new file mode 100644
index 0000000..f20350b
--- /dev/null
+++ b/lib/screens/guide/guide.dart
@@ -0,0 +1,33 @@
+import 'package:flutter/material.dart';
+import 'package:sk_base_mobile/router/router.dart';
+import 'package:sk_base_mobile/router/router.util.dart';
+import 'package:sk_base_mobile/widgets/core/sk_ink.dart';
+
+class GuideModel {
+ final String title;
+ final String route;
+ GuideModel({required this.title, required this.route});
+}
+
+class GuidePage extends StatelessWidget {
+ GuidePage({super.key});
+ final guides = [
+ GuideModel(title: 'Guide 1', route: '/guide/1'),
+ GuideModel(title: 'Guide 2', route: '/guide/2')
+ ];
+ @override
+ Widget build(BuildContext context) {
+ return ListView(
+ children: guides.map((e) => buildItem(e)).toList(),
+ );
+ }
+
+ Widget buildItem(GuideModel model) {
+ return SkInk(
+ onTap: () {
+ RouterUtil.toNamed(RouteConfig.guide);
+ },
+ child: Text(model.title),
+ );
+ }
+}
diff --git a/lib/screens/product/product.controller.dart b/lib/screens/new_inventory_inout/product/product.controller.dart
similarity index 100%
rename from lib/screens/product/product.controller.dart
rename to lib/screens/new_inventory_inout/product/product.controller.dart
diff --git a/lib/screens/sale_quotation/components/sale_quotation_drawer.dart b/lib/screens/sale_quotation/components/sale_quotation_drawer.dart
index faf8f8e..d782058 100644
--- a/lib/screens/sale_quotation/components/sale_quotation_drawer.dart
+++ b/lib/screens/sale_quotation/components/sale_quotation_drawer.dart
@@ -12,6 +12,7 @@ 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/core/sk_flat_button.dart';
import 'package:sk_base_mobile/widgets/core/sk_ink.dart';
+import 'package:sk_base_mobile/widgets/empty.dart';
import 'package:sk_base_mobile/widgets/form_item/sk_text_input.dart';
import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
@@ -33,7 +34,7 @@ class SaleQuotationEndDrawer extends StatelessWidget {
Expanded(
child: buildTemplatePicker(),
),
- // buildAction()
+ buildAction()
],
);
}
@@ -43,38 +44,72 @@ class SaleQuotationEndDrawer extends StatelessWidget {
children: [
Expanded(
child: SkFlatButton(
- color: AppTheme.dangerColor,
- onPressed: () {
+ color: Colors.green,
+ onPressed: () async {
+ // ModalUtil.alert(
+ // title: '请注意',
+ // content: Text(
+ // '之前的模板保存了吗?不保存会丢失。',
+ // style: TextStyle(fontSize: ScreenAdaper.height(30)),
+ // ),
+ // onConfirm: () async {
controller.clearWorkbench();
RouterUtil.back();
+ // });
},
icon: Icon(
- Icons.delete,
+ Icons.add,
color: AppTheme.nearlyWhite,
- size: ScreenAdaper.height(40),
+ size: ScreenAdaper.height(35),
),
- buttonText: '清空工作区',
+ buttonText: '新建模板',
+ )),
+ Expanded(
+ child: SkFlatButton(
+ color: AppTheme.nearlyWhite,
+ textColor: AppTheme.nearlyBlack,
+ onPressed: () async {
+ controller.openTemplateNameEditPopup(
+ title: '',
+ onConfirm: (name) async {
+ controller.templateName.value = name;
+ await controller.saveToLocal();
+ await controller.saveToDatabase(isSaveAs: true);
+ await controller.getTemplates();
+ });
+ },
+ icon: Icon(
+ Icons.save_as,
+ color: AppTheme.nearlyBlack,
+ size: ScreenAdaper.height(35),
+ ),
+ buttonText: '另存为',
)),
Expanded(
child: SkFlatButton(
onPressed: () async {
- if (controller.templateName.value != '默认') {
- await controller.saveToDatabase();
- await RouterUtil.back();
- } else {
- templateNameDialog();
+ if (!controller.checkIsValid()) {
+ return;
+ }
+ if (controller.templateName.value != '默认') {
+ final isSuccessed = await controller.saveToDatabase();
+ if (isSuccessed) await RouterUtil.back();
+ } else {
+ controller.openTemplateNameEditPopup(onConfirm: (name) async {
+ controller.templateName.value = name;
+ await controller.saveToLocal();
+ await controller.saveToDatabase();
+ await controller.getTemplates();
+ });
}
-
- // final isSuccessed = await controller.saveToDatabase();
- // if (isSuccessed) await RouterUtil.back();
},
icon: Icon(
Icons.save,
color: AppTheme.nearlyWhite,
- size: ScreenAdaper.height(40),
+ size: ScreenAdaper.height(35),
),
buttonText: '保存模板',
- ))
+ )),
],
);
}
@@ -117,7 +152,7 @@ class SaleQuotationEndDrawer extends StatelessWidget {
Widget buildItem(int index) {
return SkInk(
- onTap: controller.menus[index].onTap,
+ onTap: controller.menus[index].onTap ?? () {},
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.only(
@@ -160,14 +195,21 @@ class SaleQuotationEndDrawer extends StatelessWidget {
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(),
- ))
+ child: controller.templates.isEmpty
+ ? Empty(
+ onTap: () {
+ Get.back();
+ controller.addGroup();
+ },
+ text: '暂无模板,点我开始创建')
+ : ListView(
+ padding: EdgeInsets.symmetric(
+ horizontal: ScreenAdaper.height(20)),
+ children: controller.templates
+ .mapIndexed((int index, element) =>
+ buildTemplateItem(element, index))
+ .toList(),
+ ))
],
),
));
@@ -199,7 +241,14 @@ class SaleQuotationEndDrawer extends StatelessWidget {
)),
SkInk(
onTap: () {
- templateNameDialog(title: element.name);
+ controller.openTemplateNameEditPopup(
+ title: element.name,
+ onConfirm: (name) async {
+ controller.templateName.value = name;
+ await controller.saveToLocal();
+ await controller.saveToDatabase();
+ await controller.getTemplates();
+ });
},
child: Icon(
Icons.edit,
@@ -234,67 +283,6 @@ class SaleQuotationEndDrawer extends StatelessWidget {
),
));
}
-
- 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{
diff --git a/lib/screens/sale_quotation/sale_quotation.controller.dart b/lib/screens/sale_quotation/sale_quotation.controller.dart
index 34c3299..15a222e 100644
--- a/lib/screens/sale_quotation/sale_quotation.controller.dart
+++ b/lib/screens/sale_quotation/sale_quotation.controller.dart
@@ -18,16 +18,21 @@ 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/router/router.util.dart';
import 'package:sk_base_mobile/screens/sale_quotation/components/sale_quotation_group_search.dart';
import 'package:sk_base_mobile/services/dio.service.dart';
import 'package:sk_base_mobile/services/storage.service.dart';
+import 'package:sk_base_mobile/util/common.util.dart';
+import 'package:sk_base_mobile/util/loading_util.dart';
import 'package:sk_base_mobile/util/logger_util.dart';
import 'package:sk_base_mobile/util/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_multi_search_more.dart';
import 'package:sk_base_mobile/util/modal.util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:pinyin/pinyin.dart';
+import 'package:sk_base_mobile/widgets/form_item/sk_text_input.dart';
import 'package:sk_base_mobile/widgets/loading_indicator.dart';
class SaleQuotationController extends GetxController {
@@ -44,6 +49,7 @@ class SaleQuotationController extends GetxController {
RxString templateName = '默认'.obs;
RxnInt templateId = RxnInt(null);
final downloadProgress = RxDouble(0.0);
+ final RxBool loading = false.obs;
@override
void onReady() {
init();
@@ -52,6 +58,10 @@ class SaleQuotationController extends GetxController {
Future export() async {
try {
+ if (templateId.value == null) {
+ SnackBarUtil().warning('请点击选中模板,若无模板请先创建');
+ return;
+ }
final dir = await getDownloadsDirectory();
if (dir != null) {
String storagePath = dir.path;
@@ -144,9 +154,15 @@ class SaleQuotationController extends GetxController {
menus.addAll([
WorkBenchModel(title: '导出明细', icon: 'export.svg', onTap: export),
// WorkBenchModel(title: '模板', icon: 'sale_quotation_template.svg'),
+ WorkBenchModel(
+ title: '使用教程',
+ icon: 'usage_guide.svg',
+ onTap: () {
+ RouterUtil.toNamed(RouteConfig.saleQuotationGuide);
+ }),
WorkBenchModel(title: '配件管理', icon: 'product.svg'),
WorkBenchModel(title: '分组管理', icon: 'sale_quotation_group.svg'),
- WorkBenchModel(title: '计算公式', icon: 'sale_quotation_formula.svg'),
+ // WorkBenchModel(title: '计算公式', icon: 'sale_quotation_formula.svg'),
]);
String? salesQuotation = StorageService.to.getString('salesQuotation');
if (salesQuotation != null) {
@@ -202,29 +218,29 @@ class SaleQuotationController extends GetxController {
SaleQuotationItemModel calculateRow(
SaleQuotationItemModel data, String changedField) {
Decimal quantity = Decimal.fromInt(0);
- Decimal cost = Decimal.fromInt(0);
+ Decimal unitPrice = Decimal.fromInt(0);
Decimal amount = Decimal.fromInt(0);
if (data.quantity != 0) {
quantity = Decimal.parse('${data.quantity}');
}
- if (data.cost != 0) {
- cost = Decimal.parse('${data.cost}');
+ if (data.unitPrice != 0) {
+ unitPrice = Decimal.parse('${data.unitPrice}');
}
if (data.amount != 0) {
amount = Decimal.parse('${data.amount}');
}
// 入库一般是先输入单价和数量,然后计算单价
if (changedField != 'amount') {
- Decimal result = cost * quantity;
+ Decimal result = unitPrice * quantity;
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.toDouble() : 0.0;
+ data.unitPrice = result != Decimal.zero ? result.toDouble() : 0.0;
} else if (changedField != 'amount') {
- Decimal result = (cost * quantity);
+ Decimal result = (unitPrice * quantity);
data.amount = result != Decimal.zero ? result.toDouble() : 0;
}
return data;
@@ -253,7 +269,7 @@ class SaleQuotationController extends GetxController {
'data': groups.map((e) => e.toJson()).toList(),
'totalCost': totalCost.value,
'totalPrice': totalPrice.value,
- 'formula': formula.value
+ 'formula': formula.value,
}
};
if (templateId.value != null) {
@@ -262,31 +278,52 @@ class SaleQuotationController extends GetxController {
await StorageService.to.setString('salesQuotation', jsonEncode(data));
}
- /// 保存到数据库
- Future saveToDatabase() async {
- if (templateId.value == 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.value!, {
- 'name': templateName.value,
- 'template': {
- 'data': groups.toJson(),
- 'totalCost': totalCost.value,
- 'totalPrice': totalPrice.value,
- 'formula': formula.value
- }
- });
+ /// 检查当前模板是否合法
+ bool checkIsValid() {
+ if (groups.isEmpty) {
+ SnackBarUtil().warning('至少要创建一个组');
+ return false;
+ }
+ return true;
+ }
+
+ /// 保存到数据库
+ Future saveToDatabase({isSaveAs = false}) async {
+ if (groups.isEmpty) {
+ SnackBarUtil().warning('至少要创建一个组');
+ return false;
+ }
+ try {
+ await LoadingUtil.to.show();
+ if (templateId.value == null || isSaveAs) {
+ await Api.createSaleQuotationTemplate({
+ 'name': templateName.value,
+ 'template': {
+ 'data': groups.toJson(),
+ 'totalCost': totalCost.value,
+ 'totalPrice': totalPrice.value,
+ 'formula': formula.value
+ }
+ });
+ await SnackBarUtil().success('已生成新的模板');
+ await getTemplates();
+ changeTemplate(templates.first);
+ } else {
+ await Api.updateSaleQuotationTemplate(templateId.value!, {
+ 'name': templateName.value,
+ 'template': {
+ 'data': groups.toJson(),
+ 'totalCost': totalCost.value,
+ 'totalPrice': totalPrice.value,
+ 'formula': formula.value
+ }
+ });
+ await SnackBarUtil().success('已保存');
+ await getTemplates();
+ }
+ } finally {
+ await LoadingUtil.to.dismiss();
}
- await getTemplates();
- await SnackBarUtil().success('已保存');
return true;
}
@@ -303,6 +340,7 @@ class SaleQuotationController extends GetxController {
ModalUtil.showGeneralDialog(
content: SkMutilSearchMore(
controller: controller,
+ title: '请选择分组',
enablePullUp: false,
enablePullDown: true,
onOk: (List indexes) {
@@ -346,21 +384,30 @@ class SaleQuotationController extends GetxController {
content: SkMutilSearchMore(
controller: controller,
enablePullUp: false,
+ title: '请选择配件产品',
enablePullDown: true,
isDialog: true,
onOk: (List indexes) {
groups[groupIndex]
.items
.addAll(controller.list.where((element) {
- return indexes.contains(controller.list.indexOf(element));
- }));
+ return indexes.contains(controller.list.indexOf(element));
+ }).map((e) {
+ e.quantity = 1;
+
+ Decimal amount = Decimal.parse(e.unitPrice.toString()) *
+ Decimal.fromInt(e.quantity);
+ e.amount = amount.toDouble();
+ return e;
+ }));
+ calculateTotal();
saveToLocal();
},
leadingBuilder: (index) {
return Container(
padding: EdgeInsets.symmetric(
- horizontal: ScreenAdaper.width(5),
- vertical: ScreenAdaper.height(10)),
+ horizontal: ScreenAdaper.width(5),
+ ),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -369,28 +416,41 @@ class SaleQuotationController extends GetxController {
Text(
controller.list[index].name,
style: TextStyle(
- fontSize: ScreenAdaper.height(30)),
+ fontSize: ScreenAdaper.height(30),
+ fontWeight: FontWeight.w600),
),
],
),
- if (controller.list[index].spec != null)
+ if (controller.list[index].unit != null)
Row(
children: [
Text(
- '型号:${controller.list[index].spec ?? ''}',
+ '单位:${controller.list[index].unit ?? ''}',
textAlign: TextAlign.start,
style: TextStyle(
- fontSize: ScreenAdaper.height(25)),
+ fontSize: ScreenAdaper.height(23)),
+ ),
+ ],
+ ),
+ if (controller.list[index].componentSpecification !=
+ null)
+ Row(
+ children: [
+ Text(
+ '规格、型号及说明:${controller.list[index].componentSpecification ?? ''}',
+ textAlign: TextAlign.start,
+ style: TextStyle(
+ fontSize: ScreenAdaper.height(23)),
),
],
),
Row(
children: [
Text(
- '单价:¥${controller.list[index].cost}',
+ '单价:¥${controller.list[index].unitPrice}',
textAlign: TextAlign.start,
style: TextStyle(
- fontSize: ScreenAdaper.height(25)),
+ fontSize: ScreenAdaper.height(23)),
),
],
),
@@ -401,7 +461,7 @@ class SaleQuotationController extends GetxController {
'备注:${controller.list[index].remark ?? ''}',
textAlign: TextAlign.start,
style: TextStyle(
- fontSize: ScreenAdaper.height(25)),
+ fontSize: ScreenAdaper.height(23)),
),
],
),
@@ -427,6 +487,82 @@ class SaleQuotationController extends GetxController {
templateId.value = null;
saveToLocal();
}
+
+ void openTemplateNameEditPopup(
+ {String? title = '',
+ bool isSaveAs = false,
+ Function(String)? onConfirm,
+ Function? onCancel}) {
+ if (!checkIsValid()) {
+ return;
+ }
+ 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;
+ }
+ await RouterUtil.back();
+ onConfirm?.call(textController.text);
+ // templateName.value = textController.text;
+ // await RouterUtil.back();
+ // await saveToLocal();
+ // await saveToDatabase(isSaveAs: isSaveAs);
+ // await getTemplates();
+ },
+ child: const Text('确定'),
+ ))
+ ],
+ )
+ ]),
+ ));
+ }
+ // /// 检查是否已经保存
+ // void checkIsSaved() {
+ // if (templateId.value == null) {
+ // return false;
+ // }
+ // }
}
class GroupSearchMoreController
@@ -472,30 +608,31 @@ class ItemSearchMoreController
List newList = componets
.map((e) => SaleQuotationItemModel(
name: e.name!,
- cost: e.unitPrice ?? 0.0,
+ unitPrice: e.unitPrice ?? 0.0,
unit: e.unit?.label ?? '',
+ componentSpecification: e.componentSpecification,
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),
+ // name: '矿用本安型支架控制器', unit: '台', componentSpecification: 'ZDYZ-Z', unitPrice: 4700),
+ // SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', unitPrice: 1200),
+ // SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', unitPrice: 5700),
+ // SaleQuotationItemModel(name: '矿用本安型隔离耦合器', unitPrice: 1200),
+ // SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', unitPrice: 600),
+ // SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-控制器', unitPrice: 400),
+ // SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-驱动器', unitPrice: 500),
// SaleQuotationItemModel(
- // name: '钢丝编织橡胶护套连接器', remark: '控制器-隔离耦合器', cost: 2000),
- // SaleQuotationItemModel(name: '矿用本安型支架控制器', cost: 4700),
- // SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', cost: 1200),
- // SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', cost: 5700),
+ // name: '钢丝编织橡胶护套连接器', remark: '控制器-隔离耦合器', unitPrice: 2000),
+ // SaleQuotationItemModel(name: '矿用本安型支架控制器', unitPrice: 4700),
+ // SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', unitPrice: 1200),
+ // SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', unitPrice: 5700),
// SaleQuotationItemModel(
- // name: '电液换向阀(10功能10接口)', remark: '中间过渡架主阀组', cost: 13200),
+ // name: '电液换向阀(10功能10接口)', remark: '中间过渡架主阀组', unitPrice: 13200),
// SaleQuotationItemModel(
- // name: '电液换向阀(20功能20接口)', remark: '端头架主阀组', cost: 26500),
+ // name: '电液换向阀(20功能20接口)', remark: '端头架主阀组', unitPrice: 26500),
// SaleQuotationItemModel(
- // name: '自动反冲洗过滤装置', remark: '流量:900L/min,过滤精度25μm', cost: 2000),
+ // name: '自动反冲洗过滤装置', remark: '流量:900L/min,过滤精度25μm', unitPrice: 2000),
// SaleQuotationItemModel(name: '全自动反冲洗过滤器电缆', remark: '控制器-自动反冲洗'),
// SaleQuotationItemModel(name: '矿用本安型位移传感器'),
// SaleQuotationItemModel(name: '矿用本安型压力传感器'),
diff --git a/lib/screens/sale_quotation/sale_quotation.dart b/lib/screens/sale_quotation/sale_quotation.dart
index abd5c66..b8054e6 100644
--- a/lib/screens/sale_quotation/sale_quotation.dart
+++ b/lib/screens/sale_quotation/sale_quotation.dart
@@ -4,6 +4,7 @@ import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:get/get.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/models/sale_quotation.model.dart';
+import 'package:sk_base_mobile/router/router.util.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';
@@ -37,6 +38,32 @@ class SaleQuotationPage extends StatelessWidget {
child: SaleQuotationEndDrawer(),
),
appBar: SkAppbar(
+ onPop: () async {
+ // 在这里判断是否允许用户返回
+ // 如果返回 true,用户可以返回
+ // 如果返回 false,用户不能返回
+ if (controller.templateName.value == '默认' &&
+ controller.groups.isNotEmpty) {
+ await ModalUtil.alert(
+ confirmText: '保存',
+ cancelText: '继续退出',
+ contentText: '是否保存当前模板?',
+ onConfirm: () {
+ controller.openTemplateNameEditPopup(
+ onConfirm: (title) async {
+ controller.templateName.value = title;
+ await controller.saveToLocal();
+ await controller.saveToDatabase();
+ RouterUtil.back();
+ });
+ },
+ onCancel: () {
+ RouterUtil.back();
+ });
+ } else {
+ RouterUtil.back();
+ }
+ },
title: '报价计算-${controller.templateName}',
action: [
IconButton(
@@ -378,7 +405,7 @@ class SaleQuotationPage extends StatelessWidget {
Widget buildRow(int groupIndex, int rowIndex) {
final subTextStyle =
- TextStyle(color: AppTheme.grey, fontSize: ScreenAdaper.height(25));
+ TextStyle(color: AppTheme.grey, fontSize: ScreenAdaper.height(23));
return Slidable(
key: UniqueKey(),
endActionPane: ActionPane(
@@ -429,16 +456,18 @@ class SaleQuotationPage extends StatelessWidget {
),
],
),
- if (controller.groups[groupIndex].items[rowIndex].spec !=
+ if (controller.groups[groupIndex].items[rowIndex]
+ .componentSpecification !=
null)
Row(
children: [
+ // Text(
+ // '规格、型号: ',
+ // style: subTextStyle,
+ // ),
Text(
- '规格、型号: ',
- style: subTextStyle,
- ),
- Text(
- controller.groups[groupIndex].items[rowIndex].spec ??
+ controller.groups[groupIndex].items[rowIndex]
+ .componentSpecification ??
'',
style: subTextStyle,
),
@@ -509,16 +538,16 @@ class SaleQuotationPage extends StatelessWidget {
alignment: Alignment.center,
width: ScreenAdaper.width(unitPriceWidth),
child: Text(
- CommonUtil.toNumberWithout0(
- controller.groups[groupIndex].items[rowIndex].cost),
+ CommonUtil.toNumberWithout0(controller
+ .groups[groupIndex].items[rowIndex].unitPrice),
style: TextStyle(fontSize: ScreenAdaper.height(25)),
),
),
groupIndex: groupIndex,
rowIndex: rowIndex,
- field: 'cost',
+ field: 'unitPrice',
inputWidth: ScreenAdaper.width(unitPriceWidth),
- value: controller.groups[groupIndex].items[rowIndex].cost),
+ value: controller.groups[groupIndex].items[rowIndex].unitPrice),
buildEditCell(
Container(
alignment: Alignment.center,
diff --git a/lib/util/loading_util.dart b/lib/util/loading_util.dart
index 07d97e7..f4a9b55 100644
--- a/lib/util/loading_util.dart
+++ b/lib/util/loading_util.dart
@@ -6,6 +6,7 @@ import 'package:sk_base_mobile/util/screen_adaper_util.dart';
class LoadingUtil extends GetxService {
static LoadingUtil get to => Get.find();
+ // must use LoadingUtil.to.show() instead of LoadingUtil().show()
OverlayEntry? _loadingOverlay;
Future init() async {
@@ -17,17 +18,17 @@ class LoadingUtil extends GetxService {
}
Future dismiss() async {
- return hideLoading();
+ return _hideLoading();
}
showLoading({String? status}) async {
- hideLoading();
+ _hideLoading();
_loadingOverlay = OverlayEntry(
builder: (BuildContext context) {
return PopScope(
onPopInvoked: (_) {
- hideLoading();
+ _hideLoading();
},
child: Stack(
children: [
@@ -60,7 +61,7 @@ class LoadingUtil extends GetxService {
}
}
- hideLoading() {
+ _hideLoading() {
_loadingOverlay?.remove();
_loadingOverlay = null;
}
diff --git a/lib/util/modal.util.dart b/lib/util/modal.util.dart
index 559fc6a..984ab0e 100644
--- a/lib/util/modal.util.dart
+++ b/lib/util/modal.util.dart
@@ -10,8 +10,10 @@ class ModalUtil {
{String? title,
String? contentText,
String? confirmText,
+ String? cancelText,
Widget? content,
VoidCallback? onConfirm,
+ VoidCallback? onCancel,
Widget Function(BuildContext)? builder,
EdgeInsetsGeometry? contentPadding,
bool showActions = true,
@@ -98,13 +100,14 @@ class ModalUtil {
onTap: () {
Navigator.of(context).pop();
confirmed = false;
+ if (onCancel != null) onCancel();
},
child: Container(
padding: EdgeInsets.symmetric(
vertical: ScreenAdaper.height(15)),
alignment: Alignment.center,
child: Text(
- '取消',
+ cancelText ?? '取消',
style: TextStyle(
color: Colors.black,
fontSize: ScreenAdaper.height(30)),
@@ -113,9 +116,9 @@ class ModalUtil {
Expanded(
child: InkWell(
onTap: () {
- if (onConfirm != null) onConfirm();
Navigator.pop(context);
confirmed = true;
+ if (onConfirm != null) onConfirm();
},
child: Container(
padding: EdgeInsets.symmetric(
diff --git a/lib/widgets/core/sk_appbar.dart b/lib/widgets/core/sk_appbar.dart
index 380f95f..f760f2a 100644
--- a/lib/widgets/core/sk_appbar.dart
+++ b/lib/widgets/core/sk_appbar.dart
@@ -8,6 +8,7 @@ class SkAppbar extends StatelessWidget implements PreferredSizeWidget {
final PreferredSizeWidget? bottom;
final Color? backgroundColor;
final Color? iconAndTextColor;
+ final Function()? onPop;
const SkAppbar(
{super.key,
required this.title,
@@ -15,6 +16,7 @@ class SkAppbar extends StatelessWidget implements PreferredSizeWidget {
this.hideLeading = false,
this.backgroundColor,
this.iconAndTextColor,
+ this.onPop,
this.bottom});
@override
@@ -34,8 +36,12 @@ class SkAppbar extends StatelessWidget implements PreferredSizeWidget {
size: ScreenAdaper.height(40),
color: iconAndTextColor,
),
- onPressed: () {
- Navigator.pop(context);
+ onPressed: () async {
+ if (onPop != null) {
+ onPop!();
+ } else {
+ Navigator.pop(context);
+ }
},
),
title: Text(
diff --git a/lib/widgets/core/sk_avatar.dart b/lib/widgets/core/sk_avatar.dart
index fadb486..9f00dfa 100644
--- a/lib/widgets/core/sk_avatar.dart
+++ b/lib/widgets/core/sk_avatar.dart
@@ -11,11 +11,12 @@ class SkAvatar extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ClipRRect(
- borderRadius: const BorderRadius.all(Radius.circular(40)),
+ borderRadius: BorderRadius.circular(ScreenAdaper.sp(80)),
child: Container(
decoration: BoxDecoration(
+ color: Colors.grey[200],
border: Border.all(color: AppTheme.dividerColor),
- borderRadius: const BorderRadius.all(Radius.circular(40))),
+ borderRadius: BorderRadius.circular(ScreenAdaper.sp(80))),
child: FadeInCacheImage(
defaultWidget: Icon(Icons.person_2_outlined,
size: ScreenAdaper.height(60), color: AppTheme.grey),
diff --git a/lib/widgets/custom_contact_icon.dart b/lib/widgets/custom_contact_icon.dart
new file mode 100644
index 0000000..899552f
--- /dev/null
+++ b/lib/widgets/custom_contact_icon.dart
@@ -0,0 +1,20 @@
+import 'package:flutter/material.dart';
+import 'package:sk_base_mobile/config.dart';
+import 'package:sk_base_mobile/util/device.util.dart';
+import 'package:sk_base_mobile/util/screen_adaper_util.dart';
+import 'package:sk_base_mobile/widgets/core/sk_ink.dart';
+
+class CustomContactIcon extends StatelessWidget {
+ const CustomContactIcon({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return SkInk(
+ onTap: () {
+ DeviceUtil.callPhone(GloablConfig.SUPPORT_PHONE);
+ },
+ child: Image(
+ height: ScreenAdaper.height(70),
+ image: AssetImage('assets/icons/help_desk.png')));
+ }
+}
diff --git a/lib/widgets/empty.dart b/lib/widgets/empty.dart
index c58092d..56cb8f2 100644
--- a/lib/widgets/empty.dart
+++ b/lib/widgets/empty.dart
@@ -11,24 +11,27 @@ class Empty extends StatelessWidget {
@override
Widget build(BuildContext context) {
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)),
- ),
- ]),
- ),
- );
+ onTap: onTap,
+ child: Container(
+ padding: EdgeInsets.symmetric(horizontal: ScreenAdaper.height(10)),
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ 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(30)),
+ )
+ ]),
+ ));
}
}
diff --git a/lib/widgets/form_item/sk_multi_search_more.dart b/lib/widgets/form_item/sk_multi_search_more.dart
index 22c84cb..d6faa64 100644
--- a/lib/widgets/form_item/sk_multi_search_more.dart
+++ b/lib/widgets/form_item/sk_multi_search_more.dart
@@ -21,6 +21,7 @@ class SkMutilSearchMore extends StatelessWidget {
final bool enablePullDown;
final bool isDialog;
final Function(int)? leadingBuilder;
+ final String? title;
SkMutilSearchMore(
{super.key,
required this.controller,
@@ -28,6 +29,7 @@ class SkMutilSearchMore extends StatelessWidget {
this.isDialog = true,
this.beforeSelectedCheck,
this.leadingBuilder,
+ this.title,
this.enablePullDown = true,
this.enablePullUp = true});
final listTitleTextStyle =
@@ -78,7 +80,7 @@ class SkMutilSearchMore extends StatelessWidget {
children: [
Center(
child: Text(
- '请选择',
+ title ?? '请选择',
style: TextStyle(
fontSize: ScreenAdaper.height(30),
fontWeight: FontWeight.w600),