import 'dart:convert'; 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'; import 'package:pinyin/pinyin.dart'; class SaleQuotationController extends GetxController { static SaleQuotationController get to => Get.find(); final RxList editingcell = RxList([null, null, null]); final GlobalKey scaffoldKey = GlobalKey(); RxList groups = RxList([]); 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(); super.onReady(); } Future init() async { String? salesQuotation = StorageService.to.getString('salesQuotation'); if (salesQuotation != null) { 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) { return previousValue + element.items.fold(0, (previousValue, element) { return previousValue + element.amount; }); }); Parser p = Parser(); Expression exp = p.parse(newFormula ?? formula.value); Variable x = Variable('成本'); ContextModel cm = ContextModel()..bindVariable(x, Number(totalCost.value)); double eval = exp.evaluate(EvaluationType.REAL, cm); totalPrice.value = eval.toPrecision(2); } /// 实时计算数量,单价,总价之间的关系 SaleQuotationItemModel calculateRow( SaleQuotationItemModel data, String changedField) { Decimal quantity = Decimal.fromInt(0); Decimal cost = 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.amount != 0) { amount = Decimal.parse('${data.amount}'); } // 入库一般是先输入单价和数量,然后计算单价 if (changedField != 'amount') { Decimal result = cost * 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; } else if (changedField != 'amount') { Decimal result = (cost * quantity); data.amount = result != Decimal.zero ? result.toDouble() : 0; } return data; } /// 处理行数据变化 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]); saveToLocal(); } /// 保存到本地持久化 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()); // 选择组件 选择分组 ModalUtil.showGeneralDialog( content: SkMutilSearchMore( controller: controller, enablePullUp: false, enablePullDown: true, onOk: (List indexes) { groups.addAll(controller.list.where((element) { return indexes.contains(controller.list.indexOf(element)); })); saveToLocal(); }, leadingBuilder: (index) { return Container( padding: EdgeInsets.symmetric( horizontal: ScreenAdaper.width(5), vertical: ScreenAdaper.height(10)), child: Row( children: [ Text( controller.list[index].name, style: TextStyle(fontSize: ScreenAdaper.height(30)), ), ], ), ); }, ), width: Get.width - ScreenAdaper.width(50)) .then((value) => Get.delete()); calculateTotal(); } /// 移除分组 void removeGroup(int index) { groups.removeAt(index); calculateTotal(); saveToLocal(); } void addItems(int groupIndex) async { final controller = Get.put(ItemSearchMoreController()); // 选择配件 ModalUtil.showGeneralDialog( content: SkMutilSearchMore( controller: controller, enablePullUp: false, enablePullDown: true, isDialog: true, onOk: (List indexes) { groups[groupIndex] .items .addAll(controller.list.where((element) { return indexes.contains(controller.list.indexOf(element)); })); saveToLocal(); }, leadingBuilder: (index) { return Container( padding: EdgeInsets.symmetric( horizontal: ScreenAdaper.width(5), vertical: ScreenAdaper.height(10)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( controller.list[index].name, style: TextStyle( fontSize: ScreenAdaper.height(30)), ), ], ), if (controller.list[index].spec != null) Row( children: [ Text( '型号:${controller.list[index].spec ?? ''}', textAlign: TextAlign.start, style: TextStyle( fontSize: ScreenAdaper.height(25)), ), ], ), Row( children: [ Text( '单价:¥${controller.list[index].cost}', textAlign: TextAlign.start, style: TextStyle( fontSize: ScreenAdaper.height(25)), ), ], ), if (controller.list[index].remark != null) Row( children: [ Text( '备注:${controller.list[index].remark ?? ''}', textAlign: TextAlign.start, style: TextStyle( fontSize: ScreenAdaper.height(25)), ), ], ), ])); }, ), width: Get.width - ScreenAdaper.width(50)) .then((value) => Get.delete()); calculateTotal(); } void removeItem(int groupIndex, int rowIndex) { 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 BaseSearchMoreController { @override Future> getData({bool isRefresh = false}) async { 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)) .toList()); return newList; } } class ItemSearchMoreController extends BaseSearchMoreController { @override Future> getData({bool isRefresh = false}) async { 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 []; } } }