feat: sale quotation and support phone
This commit is contained in:
parent
2cce68fe0c
commit
8e2737a5a4
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1713143223109" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7536" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M512.001023 2.193964c281.559068 0 509.805013 228.245945 509.805013 509.806036 0 281.557021-228.245945 509.805013-509.805013 509.805013S2.192941 793.557021 2.192941 512c0-281.560091 228.249015-509.806036 509.808082-509.806036zM313.40941 275.782516h-3.85581 3.85581z m-96.408555 11.56743c-7.706504 3.85888-15.422217 11.571523-15.422216 23.143047v385.611704c0 15.425287 11.5705 23.135884 26.994763 23.135883 50.126554-7.710597 161.958348-34.704337 262.217596 26.993741 0 0 3.85581 0 3.85581 3.85581V341.342543c-34.70536-26.99374-127.254011-96.408554-277.645953-53.992597z m586.137363 0c-146.531015-42.415957-242.934453 26.998857-277.639813 50.131671v412.608514s3.85888 0 3.85888-3.85581c100.255154-61.698077 212.082856-38.560147 266.070336-26.993741 11.5705 3.857857 26.99374-7.710597 26.99374-23.135883v-385.611704c-3.860927-11.571523-11.572547-19.283143-19.283143-23.143047z" fill="#E78800" p-id="7537"></path></svg>
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 96 KiB |
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
Binary file not shown.
After Width: | Height: | Size: 80 KiB |
|
@ -22,37 +22,37 @@ class SaleQuotationModel extends BaseSearchMoreModel {
|
||||||
class SaleQuotationItemModel extends BaseSearchMoreModel {
|
class SaleQuotationItemModel extends BaseSearchMoreModel {
|
||||||
final String name;
|
final String name;
|
||||||
// 规格
|
// 规格
|
||||||
final String? spec;
|
final String? componentSpecification;
|
||||||
final String? unit;
|
final String? unit;
|
||||||
final String? remark;
|
final String? remark;
|
||||||
// 成本
|
// 成本
|
||||||
num cost;
|
num unitPrice;
|
||||||
int quantity;
|
int quantity;
|
||||||
num amount;
|
num amount;
|
||||||
|
|
||||||
SaleQuotationItemModel(
|
SaleQuotationItemModel(
|
||||||
{required this.name,
|
{required this.name,
|
||||||
this.spec,
|
this.componentSpecification,
|
||||||
this.unit,
|
this.unit,
|
||||||
this.remark,
|
this.remark,
|
||||||
this.cost = 0,
|
this.unitPrice = 0,
|
||||||
this.quantity = 0,
|
this.quantity = 0,
|
||||||
this.amount = 0});
|
this.amount = 0});
|
||||||
SaleQuotationItemModel.fromJson(Map<String, dynamic> json)
|
SaleQuotationItemModel.fromJson(Map<String, dynamic> json)
|
||||||
: name = json['name'],
|
: name = json['name'],
|
||||||
spec = json['spec'],
|
componentSpecification = json['componentSpecification'],
|
||||||
unit = json['unit'],
|
unit = json['unit'],
|
||||||
remark = json['remark'],
|
remark = json['remark'],
|
||||||
cost = json['cost'],
|
unitPrice = json['unitPrice'],
|
||||||
quantity = json['quantity'],
|
quantity = json['quantity'],
|
||||||
amount = json['amount'];
|
amount = json['amount'];
|
||||||
// 生成tojson
|
// 生成tojson
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
'name': name,
|
'name': name,
|
||||||
'spec': spec,
|
'componentSpecification': componentSpecification,
|
||||||
'unit': unit,
|
'unit': unit,
|
||||||
'remark': remark,
|
'remark': remark,
|
||||||
'cost': cost,
|
'unitPrice': unitPrice,
|
||||||
'quantity': quantity,
|
'quantity': quantity,
|
||||||
'amount': amount,
|
'amount': amount,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:sk_base_mobile/models/user_info.model.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/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/components/employee_detail.dart';
|
||||||
import 'package:sk_base_mobile/screens/hr_manage/hr_manage.dart';
|
import 'package:sk_base_mobile/screens/hr_manage/hr_manage.dart';
|
||||||
import 'package:sk_base_mobile/screens/inventory/inventory.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 saleQuotation = '/workbench/sale_quotation';
|
||||||
static const String hrManage = '/workbench/hr_manage';
|
static const String hrManage = '/workbench/hr_manage';
|
||||||
static const String employeeDetail = '/employee_detail';
|
static const String employeeDetail = '/employee_detail';
|
||||||
|
static const String guide = '/guide';
|
||||||
|
static const String saleQuotationGuide = '/sale_quotation_guide';
|
||||||
|
|
||||||
static final List<GetPage> getPages = [
|
static final List<GetPage> getPages = [
|
||||||
|
GetPage(
|
||||||
|
name: saleQuotationGuide,
|
||||||
|
page: () => SaleQuotationGuide(),
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: guide,
|
||||||
|
page: () => GuidePage(),
|
||||||
|
),
|
||||||
GetPage(
|
GetPage(
|
||||||
name: login,
|
name: login,
|
||||||
page: () => LoginScreen(),
|
page: () => LoginScreen(),
|
||||||
|
|
|
@ -10,6 +10,7 @@ class RouterUtil {
|
||||||
RouteConfig.inventory,
|
RouteConfig.inventory,
|
||||||
RouteConfig.saleQuotation,
|
RouteConfig.saleQuotation,
|
||||||
];
|
];
|
||||||
|
// static List<String> whiteList = [RouteConfig.saleQuotationGuide];
|
||||||
static Future<T?> toNamed<T>(String routeName, {arguments}) async {
|
static Future<T?> toNamed<T>(String routeName, {arguments}) async {
|
||||||
//关闭键盘
|
//关闭键盘
|
||||||
if (Get.context != null) {
|
if (Get.context != null) {
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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/util/snack_bar.util.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_flat_button.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/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/form_item/sk_text_input.dart';
|
||||||
import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
|
import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ class SaleQuotationEndDrawer extends StatelessWidget {
|
||||||
Expanded(
|
Expanded(
|
||||||
child: buildTemplatePicker(),
|
child: buildTemplatePicker(),
|
||||||
),
|
),
|
||||||
// buildAction()
|
buildAction()
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -43,38 +44,72 @@ class SaleQuotationEndDrawer extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SkFlatButton(
|
child: SkFlatButton(
|
||||||
color: AppTheme.dangerColor,
|
color: Colors.green,
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
|
// ModalUtil.alert(
|
||||||
|
// title: '请注意',
|
||||||
|
// content: Text(
|
||||||
|
// '之前的模板保存了吗?不保存会丢失。',
|
||||||
|
// style: TextStyle(fontSize: ScreenAdaper.height(30)),
|
||||||
|
// ),
|
||||||
|
// onConfirm: () async {
|
||||||
controller.clearWorkbench();
|
controller.clearWorkbench();
|
||||||
RouterUtil.back();
|
RouterUtil.back();
|
||||||
|
// });
|
||||||
},
|
},
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.delete,
|
Icons.add,
|
||||||
color: AppTheme.nearlyWhite,
|
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(
|
Expanded(
|
||||||
child: SkFlatButton(
|
child: SkFlatButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (controller.templateName.value != '默认') {
|
if (!controller.checkIsValid()) {
|
||||||
await controller.saveToDatabase();
|
return;
|
||||||
await RouterUtil.back();
|
}
|
||||||
} else {
|
if (controller.templateName.value != '默认') {
|
||||||
templateNameDialog();
|
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(
|
icon: Icon(
|
||||||
Icons.save,
|
Icons.save,
|
||||||
color: AppTheme.nearlyWhite,
|
color: AppTheme.nearlyWhite,
|
||||||
size: ScreenAdaper.height(40),
|
size: ScreenAdaper.height(35),
|
||||||
),
|
),
|
||||||
buttonText: '保存模板',
|
buttonText: '保存模板',
|
||||||
))
|
)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -117,7 +152,7 @@ class SaleQuotationEndDrawer extends StatelessWidget {
|
||||||
|
|
||||||
Widget buildItem(int index) {
|
Widget buildItem(int index) {
|
||||||
return SkInk(
|
return SkInk(
|
||||||
onTap: controller.menus[index].onTap,
|
onTap: controller.menus[index].onTap ?? () {},
|
||||||
child: Container(
|
child: Container(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
|
@ -160,9 +195,16 @@ class SaleQuotationEndDrawer extends StatelessWidget {
|
||||||
height: ScreenAdaper.height(10),
|
height: ScreenAdaper.height(10),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView(
|
child: controller.templates.isEmpty
|
||||||
padding:
|
? Empty(
|
||||||
EdgeInsets.symmetric(horizontal: ScreenAdaper.height(20)),
|
onTap: () {
|
||||||
|
Get.back();
|
||||||
|
controller.addGroup();
|
||||||
|
},
|
||||||
|
text: '暂无模板,点我开始创建')
|
||||||
|
: ListView(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: ScreenAdaper.height(20)),
|
||||||
children: controller.templates
|
children: controller.templates
|
||||||
.mapIndexed((int index, element) =>
|
.mapIndexed((int index, element) =>
|
||||||
buildTemplateItem(element, index))
|
buildTemplateItem(element, index))
|
||||||
|
@ -199,7 +241,14 @@ class SaleQuotationEndDrawer extends StatelessWidget {
|
||||||
)),
|
)),
|
||||||
SkInk(
|
SkInk(
|
||||||
onTap: () {
|
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(
|
child: Icon(
|
||||||
Icons.edit,
|
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{
|
// class SaleQuotationDrawerController extends GetxController{
|
||||||
|
|
|
@ -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.model.dart';
|
||||||
import 'package:sk_base_mobile/models/sale_quotation_template.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/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/screens/sale_quotation/components/sale_quotation_group_search.dart';
|
||||||
import 'package:sk_base_mobile/services/dio.service.dart';
|
import 'package:sk_base_mobile/services/dio.service.dart';
|
||||||
import 'package:sk_base_mobile/services/storage.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/logger_util.dart';
|
||||||
import 'package:sk_base_mobile/util/snack_bar.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_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/widgets/form_item/sk_multi_search_more.dart';
|
||||||
import 'package:sk_base_mobile/util/modal.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/screen_adaper_util.dart';
|
||||||
import 'package:pinyin/pinyin.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';
|
import 'package:sk_base_mobile/widgets/loading_indicator.dart';
|
||||||
|
|
||||||
class SaleQuotationController extends GetxController {
|
class SaleQuotationController extends GetxController {
|
||||||
|
@ -44,6 +49,7 @@ class SaleQuotationController extends GetxController {
|
||||||
RxString templateName = '默认'.obs;
|
RxString templateName = '默认'.obs;
|
||||||
RxnInt templateId = RxnInt(null);
|
RxnInt templateId = RxnInt(null);
|
||||||
final downloadProgress = RxDouble(0.0);
|
final downloadProgress = RxDouble(0.0);
|
||||||
|
final RxBool loading = false.obs;
|
||||||
@override
|
@override
|
||||||
void onReady() {
|
void onReady() {
|
||||||
init();
|
init();
|
||||||
|
@ -52,6 +58,10 @@ class SaleQuotationController extends GetxController {
|
||||||
|
|
||||||
Future export() async {
|
Future export() async {
|
||||||
try {
|
try {
|
||||||
|
if (templateId.value == null) {
|
||||||
|
SnackBarUtil().warning('请点击选中模板,若无模板请先创建');
|
||||||
|
return;
|
||||||
|
}
|
||||||
final dir = await getDownloadsDirectory();
|
final dir = await getDownloadsDirectory();
|
||||||
if (dir != null) {
|
if (dir != null) {
|
||||||
String storagePath = dir.path;
|
String storagePath = dir.path;
|
||||||
|
@ -144,9 +154,15 @@ class SaleQuotationController extends GetxController {
|
||||||
menus.addAll([
|
menus.addAll([
|
||||||
WorkBenchModel(title: '导出明细', icon: 'export.svg', onTap: export),
|
WorkBenchModel(title: '导出明细', icon: 'export.svg', onTap: export),
|
||||||
// WorkBenchModel(title: '模板', icon: 'sale_quotation_template.svg'),
|
// 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: 'product.svg'),
|
||||||
WorkBenchModel(title: '分组管理', icon: 'sale_quotation_group.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');
|
String? salesQuotation = StorageService.to.getString('salesQuotation');
|
||||||
if (salesQuotation != null) {
|
if (salesQuotation != null) {
|
||||||
|
@ -202,29 +218,29 @@ class SaleQuotationController extends GetxController {
|
||||||
SaleQuotationItemModel calculateRow(
|
SaleQuotationItemModel calculateRow(
|
||||||
SaleQuotationItemModel data, String changedField) {
|
SaleQuotationItemModel data, String changedField) {
|
||||||
Decimal quantity = Decimal.fromInt(0);
|
Decimal quantity = Decimal.fromInt(0);
|
||||||
Decimal cost = Decimal.fromInt(0);
|
Decimal unitPrice = Decimal.fromInt(0);
|
||||||
Decimal amount = Decimal.fromInt(0);
|
Decimal amount = Decimal.fromInt(0);
|
||||||
if (data.quantity != 0) {
|
if (data.quantity != 0) {
|
||||||
quantity = Decimal.parse('${data.quantity}');
|
quantity = Decimal.parse('${data.quantity}');
|
||||||
}
|
}
|
||||||
if (data.cost != 0) {
|
if (data.unitPrice != 0) {
|
||||||
cost = Decimal.parse('${data.cost}');
|
unitPrice = Decimal.parse('${data.unitPrice}');
|
||||||
}
|
}
|
||||||
if (data.amount != 0) {
|
if (data.amount != 0) {
|
||||||
amount = Decimal.parse('${data.amount}');
|
amount = Decimal.parse('${data.amount}');
|
||||||
}
|
}
|
||||||
// 入库一般是先输入单价和数量,然后计算单价
|
// 入库一般是先输入单价和数量,然后计算单价
|
||||||
if (changedField != 'amount') {
|
if (changedField != 'amount') {
|
||||||
Decimal result = cost * quantity;
|
Decimal result = unitPrice * quantity;
|
||||||
data.amount = result != Decimal.zero ? result.toDouble() : 0;
|
data.amount = result != Decimal.zero ? result.toDouble() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changedField == 'amount' && quantity != Decimal.zero) {
|
if (changedField == 'amount' && quantity != Decimal.zero) {
|
||||||
Decimal result =
|
Decimal result =
|
||||||
(amount / quantity).toDecimal(scaleOnInfinitePrecision: 10);
|
(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') {
|
} else if (changedField != 'amount') {
|
||||||
Decimal result = (cost * quantity);
|
Decimal result = (unitPrice * quantity);
|
||||||
data.amount = result != Decimal.zero ? result.toDouble() : 0;
|
data.amount = result != Decimal.zero ? result.toDouble() : 0;
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
|
@ -253,7 +269,7 @@ class SaleQuotationController extends GetxController {
|
||||||
'data': groups.map((e) => e.toJson()).toList(),
|
'data': groups.map((e) => e.toJson()).toList(),
|
||||||
'totalCost': totalCost.value,
|
'totalCost': totalCost.value,
|
||||||
'totalPrice': totalPrice.value,
|
'totalPrice': totalPrice.value,
|
||||||
'formula': formula.value
|
'formula': formula.value,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (templateId.value != null) {
|
if (templateId.value != null) {
|
||||||
|
@ -262,9 +278,24 @@ class SaleQuotationController extends GetxController {
|
||||||
await StorageService.to.setString('salesQuotation', jsonEncode(data));
|
await StorageService.to.setString('salesQuotation', jsonEncode(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 检查当前模板是否合法
|
||||||
|
bool checkIsValid() {
|
||||||
|
if (groups.isEmpty) {
|
||||||
|
SnackBarUtil().warning('至少要创建一个组');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// 保存到数据库
|
/// 保存到数据库
|
||||||
Future<bool> saveToDatabase() async {
|
Future<bool> saveToDatabase({isSaveAs = false}) async {
|
||||||
if (templateId.value == null) {
|
if (groups.isEmpty) {
|
||||||
|
SnackBarUtil().warning('至少要创建一个组');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await LoadingUtil.to.show();
|
||||||
|
if (templateId.value == null || isSaveAs) {
|
||||||
await Api.createSaleQuotationTemplate({
|
await Api.createSaleQuotationTemplate({
|
||||||
'name': templateName.value,
|
'name': templateName.value,
|
||||||
'template': {
|
'template': {
|
||||||
|
@ -274,6 +305,9 @@ class SaleQuotationController extends GetxController {
|
||||||
'formula': formula.value
|
'formula': formula.value
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
await SnackBarUtil().success('已生成新的模板');
|
||||||
|
await getTemplates();
|
||||||
|
changeTemplate(templates.first);
|
||||||
} else {
|
} else {
|
||||||
await Api.updateSaleQuotationTemplate(templateId.value!, {
|
await Api.updateSaleQuotationTemplate(templateId.value!, {
|
||||||
'name': templateName.value,
|
'name': templateName.value,
|
||||||
|
@ -284,9 +318,12 @@ class SaleQuotationController extends GetxController {
|
||||||
'formula': formula.value
|
'formula': formula.value
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
await getTemplates();
|
|
||||||
await SnackBarUtil().success('已保存');
|
await SnackBarUtil().success('已保存');
|
||||||
|
await getTemplates();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await LoadingUtil.to.dismiss();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,6 +340,7 @@ class SaleQuotationController extends GetxController {
|
||||||
ModalUtil.showGeneralDialog(
|
ModalUtil.showGeneralDialog(
|
||||||
content: SkMutilSearchMore<SaleQuotationModel>(
|
content: SkMutilSearchMore<SaleQuotationModel>(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
|
title: '请选择分组',
|
||||||
enablePullUp: false,
|
enablePullUp: false,
|
||||||
enablePullDown: true,
|
enablePullDown: true,
|
||||||
onOk: (List<int> indexes) {
|
onOk: (List<int> indexes) {
|
||||||
|
@ -346,6 +384,7 @@ class SaleQuotationController extends GetxController {
|
||||||
content: SkMutilSearchMore<SaleQuotationItemModel>(
|
content: SkMutilSearchMore<SaleQuotationItemModel>(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
enablePullUp: false,
|
enablePullUp: false,
|
||||||
|
title: '请选择配件产品',
|
||||||
enablePullDown: true,
|
enablePullDown: true,
|
||||||
isDialog: true,
|
isDialog: true,
|
||||||
onOk: (List<int> indexes) {
|
onOk: (List<int> indexes) {
|
||||||
|
@ -353,14 +392,22 @@ class SaleQuotationController extends GetxController {
|
||||||
.items
|
.items
|
||||||
.addAll(controller.list.where((element) {
|
.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();
|
saveToLocal();
|
||||||
},
|
},
|
||||||
leadingBuilder: (index) {
|
leadingBuilder: (index) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(
|
||||||
horizontal: ScreenAdaper.width(5),
|
horizontal: ScreenAdaper.width(5),
|
||||||
vertical: ScreenAdaper.height(10)),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
@ -369,28 +416,41 @@ class SaleQuotationController extends GetxController {
|
||||||
Text(
|
Text(
|
||||||
controller.list[index].name,
|
controller.list[index].name,
|
||||||
style: TextStyle(
|
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(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'型号:${controller.list[index].spec ?? ''}',
|
'单位:${controller.list[index].unit ?? ''}',
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: TextStyle(
|
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(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'单价:¥${controller.list[index].cost}',
|
'单价:¥${controller.list[index].unitPrice}',
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: ScreenAdaper.height(25)),
|
fontSize: ScreenAdaper.height(23)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -401,7 +461,7 @@ class SaleQuotationController extends GetxController {
|
||||||
'备注:${controller.list[index].remark ?? ''}',
|
'备注:${controller.list[index].remark ?? ''}',
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: ScreenAdaper.height(25)),
|
fontSize: ScreenAdaper.height(23)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -427,6 +487,82 @@ class SaleQuotationController extends GetxController {
|
||||||
templateId.value = null;
|
templateId.value = null;
|
||||||
saveToLocal();
|
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
|
class GroupSearchMoreController
|
||||||
|
@ -472,30 +608,31 @@ class ItemSearchMoreController
|
||||||
List<SaleQuotationItemModel> newList = componets
|
List<SaleQuotationItemModel> newList = componets
|
||||||
.map((e) => SaleQuotationItemModel(
|
.map((e) => SaleQuotationItemModel(
|
||||||
name: e.name!,
|
name: e.name!,
|
||||||
cost: e.unitPrice ?? 0.0,
|
unitPrice: e.unitPrice ?? 0.0,
|
||||||
unit: e.unit?.label ?? '',
|
unit: e.unit?.label ?? '',
|
||||||
|
componentSpecification: e.componentSpecification,
|
||||||
remark: e.remark))
|
remark: e.remark))
|
||||||
.toList();
|
.toList();
|
||||||
// List<SaleQuotationItemModel> newList = [
|
// List<SaleQuotationItemModel> newList = [
|
||||||
// SaleQuotationItemModel(
|
// SaleQuotationItemModel(
|
||||||
// name: '矿用本安型支架控制器', unit: '台', spec: 'ZDYZ-Z', cost: 4700),
|
// name: '矿用本安型支架控制器', unit: '台', componentSpecification: 'ZDYZ-Z', unitPrice: 4700),
|
||||||
// SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', cost: 1200),
|
// SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', unitPrice: 1200),
|
||||||
// SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', cost: 5700),
|
// SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', unitPrice: 5700),
|
||||||
// SaleQuotationItemModel(name: '矿用本安型隔离耦合器', cost: 1200),
|
// SaleQuotationItemModel(name: '矿用本安型隔离耦合器', unitPrice: 1200),
|
||||||
// SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', cost: 600),
|
// SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', unitPrice: 600),
|
||||||
// SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-控制器', cost: 400),
|
// SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-控制器', unitPrice: 400),
|
||||||
// SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-驱动器', cost: 500),
|
// SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-驱动器', unitPrice: 500),
|
||||||
// SaleQuotationItemModel(
|
// SaleQuotationItemModel(
|
||||||
// name: '钢丝编织橡胶护套连接器', remark: '控制器-隔离耦合器', cost: 2000),
|
// name: '钢丝编织橡胶护套连接器', remark: '控制器-隔离耦合器', unitPrice: 2000),
|
||||||
// SaleQuotationItemModel(name: '矿用本安型支架控制器', cost: 4700),
|
// SaleQuotationItemModel(name: '矿用本安型支架控制器', unitPrice: 4700),
|
||||||
// SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', cost: 1200),
|
// SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', unitPrice: 1200),
|
||||||
// SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', cost: 5700),
|
// SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', unitPrice: 5700),
|
||||||
// SaleQuotationItemModel(
|
// SaleQuotationItemModel(
|
||||||
// name: '电液换向阀(10功能10接口)', remark: '中间过渡架主阀组', cost: 13200),
|
// name: '电液换向阀(10功能10接口)', remark: '中间过渡架主阀组', unitPrice: 13200),
|
||||||
// SaleQuotationItemModel(
|
// SaleQuotationItemModel(
|
||||||
// name: '电液换向阀(20功能20接口)', remark: '端头架主阀组', cost: 26500),
|
// name: '电液换向阀(20功能20接口)', remark: '端头架主阀组', unitPrice: 26500),
|
||||||
// SaleQuotationItemModel(
|
// SaleQuotationItemModel(
|
||||||
// name: '自动反冲洗过滤装置', remark: '流量:900L/min,过滤精度25μm', cost: 2000),
|
// name: '自动反冲洗过滤装置', remark: '流量:900L/min,过滤精度25μm', unitPrice: 2000),
|
||||||
// SaleQuotationItemModel(name: '全自动反冲洗过滤器电缆', remark: '控制器-自动反冲洗'),
|
// SaleQuotationItemModel(name: '全自动反冲洗过滤器电缆', remark: '控制器-自动反冲洗'),
|
||||||
// SaleQuotationItemModel(name: '矿用本安型位移传感器'),
|
// SaleQuotationItemModel(name: '矿用本安型位移传感器'),
|
||||||
// SaleQuotationItemModel(name: '矿用本安型压力传感器'),
|
// SaleQuotationItemModel(name: '矿用本安型压力传感器'),
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:sk_base_mobile/app_theme.dart';
|
import 'package:sk_base_mobile/app_theme.dart';
|
||||||
import 'package:sk_base_mobile/models/sale_quotation.model.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/components/sale_quotation_drawer.dart';
|
||||||
import 'package:sk_base_mobile/screens/sale_quotation/sale_quotation.controller.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/common.util.dart';
|
||||||
|
@ -37,6 +38,32 @@ class SaleQuotationPage extends StatelessWidget {
|
||||||
child: SaleQuotationEndDrawer(),
|
child: SaleQuotationEndDrawer(),
|
||||||
),
|
),
|
||||||
appBar: SkAppbar(
|
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}',
|
title: '报价计算-${controller.templateName}',
|
||||||
action: [
|
action: [
|
||||||
IconButton(
|
IconButton(
|
||||||
|
@ -378,7 +405,7 @@ class SaleQuotationPage extends StatelessWidget {
|
||||||
|
|
||||||
Widget buildRow(int groupIndex, int rowIndex) {
|
Widget buildRow(int groupIndex, int rowIndex) {
|
||||||
final subTextStyle =
|
final subTextStyle =
|
||||||
TextStyle(color: AppTheme.grey, fontSize: ScreenAdaper.height(25));
|
TextStyle(color: AppTheme.grey, fontSize: ScreenAdaper.height(23));
|
||||||
return Slidable(
|
return Slidable(
|
||||||
key: UniqueKey(),
|
key: UniqueKey(),
|
||||||
endActionPane: ActionPane(
|
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)
|
null)
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
|
// Text(
|
||||||
|
// '规格、型号: ',
|
||||||
|
// style: subTextStyle,
|
||||||
|
// ),
|
||||||
Text(
|
Text(
|
||||||
'规格、型号: ',
|
controller.groups[groupIndex].items[rowIndex]
|
||||||
style: subTextStyle,
|
.componentSpecification ??
|
||||||
),
|
|
||||||
Text(
|
|
||||||
controller.groups[groupIndex].items[rowIndex].spec ??
|
|
||||||
'',
|
'',
|
||||||
style: subTextStyle,
|
style: subTextStyle,
|
||||||
),
|
),
|
||||||
|
@ -509,16 +538,16 @@ class SaleQuotationPage extends StatelessWidget {
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
width: ScreenAdaper.width(unitPriceWidth),
|
width: ScreenAdaper.width(unitPriceWidth),
|
||||||
child: Text(
|
child: Text(
|
||||||
CommonUtil.toNumberWithout0(
|
CommonUtil.toNumberWithout0(controller
|
||||||
controller.groups[groupIndex].items[rowIndex].cost),
|
.groups[groupIndex].items[rowIndex].unitPrice),
|
||||||
style: TextStyle(fontSize: ScreenAdaper.height(25)),
|
style: TextStyle(fontSize: ScreenAdaper.height(25)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
groupIndex: groupIndex,
|
groupIndex: groupIndex,
|
||||||
rowIndex: rowIndex,
|
rowIndex: rowIndex,
|
||||||
field: 'cost',
|
field: 'unitPrice',
|
||||||
inputWidth: ScreenAdaper.width(unitPriceWidth),
|
inputWidth: ScreenAdaper.width(unitPriceWidth),
|
||||||
value: controller.groups[groupIndex].items[rowIndex].cost),
|
value: controller.groups[groupIndex].items[rowIndex].unitPrice),
|
||||||
buildEditCell<double>(
|
buildEditCell<double>(
|
||||||
Container(
|
Container(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||||||
|
|
||||||
class LoadingUtil extends GetxService {
|
class LoadingUtil extends GetxService {
|
||||||
static LoadingUtil get to => Get.find();
|
static LoadingUtil get to => Get.find();
|
||||||
|
// must use LoadingUtil.to.show() instead of LoadingUtil().show()
|
||||||
OverlayEntry? _loadingOverlay;
|
OverlayEntry? _loadingOverlay;
|
||||||
|
|
||||||
Future<LoadingUtil> init() async {
|
Future<LoadingUtil> init() async {
|
||||||
|
@ -17,17 +18,17 @@ class LoadingUtil extends GetxService {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> dismiss() async {
|
Future<void> dismiss() async {
|
||||||
return hideLoading();
|
return _hideLoading();
|
||||||
}
|
}
|
||||||
|
|
||||||
showLoading({String? status}) async {
|
showLoading({String? status}) async {
|
||||||
hideLoading();
|
_hideLoading();
|
||||||
|
|
||||||
_loadingOverlay = OverlayEntry(
|
_loadingOverlay = OverlayEntry(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return PopScope(
|
return PopScope(
|
||||||
onPopInvoked: (_) {
|
onPopInvoked: (_) {
|
||||||
hideLoading();
|
_hideLoading();
|
||||||
},
|
},
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
@ -60,7 +61,7 @@ class LoadingUtil extends GetxService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hideLoading() {
|
_hideLoading() {
|
||||||
_loadingOverlay?.remove();
|
_loadingOverlay?.remove();
|
||||||
_loadingOverlay = null;
|
_loadingOverlay = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,10 @@ class ModalUtil {
|
||||||
{String? title,
|
{String? title,
|
||||||
String? contentText,
|
String? contentText,
|
||||||
String? confirmText,
|
String? confirmText,
|
||||||
|
String? cancelText,
|
||||||
Widget? content,
|
Widget? content,
|
||||||
VoidCallback? onConfirm,
|
VoidCallback? onConfirm,
|
||||||
|
VoidCallback? onCancel,
|
||||||
Widget Function(BuildContext)? builder,
|
Widget Function(BuildContext)? builder,
|
||||||
EdgeInsetsGeometry? contentPadding,
|
EdgeInsetsGeometry? contentPadding,
|
||||||
bool showActions = true,
|
bool showActions = true,
|
||||||
|
@ -98,13 +100,14 @@ class ModalUtil {
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
confirmed = false;
|
confirmed = false;
|
||||||
|
if (onCancel != null) onCancel();
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(
|
||||||
vertical: ScreenAdaper.height(15)),
|
vertical: ScreenAdaper.height(15)),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Text(
|
child: Text(
|
||||||
'取消',
|
cancelText ?? '取消',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontSize: ScreenAdaper.height(30)),
|
fontSize: ScreenAdaper.height(30)),
|
||||||
|
@ -113,9 +116,9 @@ class ModalUtil {
|
||||||
Expanded(
|
Expanded(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (onConfirm != null) onConfirm();
|
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
confirmed = true;
|
confirmed = true;
|
||||||
|
if (onConfirm != null) onConfirm();
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(
|
||||||
|
|
|
@ -8,6 +8,7 @@ class SkAppbar extends StatelessWidget implements PreferredSizeWidget {
|
||||||
final PreferredSizeWidget? bottom;
|
final PreferredSizeWidget? bottom;
|
||||||
final Color? backgroundColor;
|
final Color? backgroundColor;
|
||||||
final Color? iconAndTextColor;
|
final Color? iconAndTextColor;
|
||||||
|
final Function()? onPop;
|
||||||
const SkAppbar(
|
const SkAppbar(
|
||||||
{super.key,
|
{super.key,
|
||||||
required this.title,
|
required this.title,
|
||||||
|
@ -15,6 +16,7 @@ class SkAppbar extends StatelessWidget implements PreferredSizeWidget {
|
||||||
this.hideLeading = false,
|
this.hideLeading = false,
|
||||||
this.backgroundColor,
|
this.backgroundColor,
|
||||||
this.iconAndTextColor,
|
this.iconAndTextColor,
|
||||||
|
this.onPop,
|
||||||
this.bottom});
|
this.bottom});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -34,8 +36,12 @@ class SkAppbar extends StatelessWidget implements PreferredSizeWidget {
|
||||||
size: ScreenAdaper.height(40),
|
size: ScreenAdaper.height(40),
|
||||||
color: iconAndTextColor,
|
color: iconAndTextColor,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
|
if (onPop != null) {
|
||||||
|
onPop!();
|
||||||
|
} else {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
|
|
|
@ -11,11 +11,12 @@ class SkAvatar extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ClipRRect(
|
return ClipRRect(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(40)),
|
borderRadius: BorderRadius.circular(ScreenAdaper.sp(80)),
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey[200],
|
||||||
border: Border.all(color: AppTheme.dividerColor),
|
border: Border.all(color: AppTheme.dividerColor),
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(40))),
|
borderRadius: BorderRadius.circular(ScreenAdaper.sp(80))),
|
||||||
child: FadeInCacheImage(
|
child: FadeInCacheImage(
|
||||||
defaultWidget: Icon(Icons.person_2_outlined,
|
defaultWidget: Icon(Icons.person_2_outlined,
|
||||||
size: ScreenAdaper.height(60), color: AppTheme.grey),
|
size: ScreenAdaper.height(60), color: AppTheme.grey),
|
||||||
|
|
|
@ -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')));
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,8 +12,12 @@ class Empty extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Center(
|
child: Container(
|
||||||
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
padding: EdgeInsets.symmetric(horizontal: ScreenAdaper.height(10)),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
Center(
|
Center(
|
||||||
child: icon ??
|
child: icon ??
|
||||||
Icon(Icons.error_outline,
|
Icon(Icons.error_outline,
|
||||||
|
@ -25,10 +29,9 @@ class Empty extends StatelessWidget {
|
||||||
Text(
|
Text(
|
||||||
text ?? '',
|
text ?? '',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(fontSize: ScreenAdaper.height(35)),
|
style: TextStyle(fontSize: ScreenAdaper.height(30)),
|
||||||
),
|
)
|
||||||
]),
|
]),
|
||||||
),
|
));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ class SkMutilSearchMore<T extends BaseSearchMoreModel> extends StatelessWidget {
|
||||||
final bool enablePullDown;
|
final bool enablePullDown;
|
||||||
final bool isDialog;
|
final bool isDialog;
|
||||||
final Function(int)? leadingBuilder;
|
final Function(int)? leadingBuilder;
|
||||||
|
final String? title;
|
||||||
SkMutilSearchMore(
|
SkMutilSearchMore(
|
||||||
{super.key,
|
{super.key,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
|
@ -28,6 +29,7 @@ class SkMutilSearchMore<T extends BaseSearchMoreModel> extends StatelessWidget {
|
||||||
this.isDialog = true,
|
this.isDialog = true,
|
||||||
this.beforeSelectedCheck,
|
this.beforeSelectedCheck,
|
||||||
this.leadingBuilder,
|
this.leadingBuilder,
|
||||||
|
this.title,
|
||||||
this.enablePullDown = true,
|
this.enablePullDown = true,
|
||||||
this.enablePullUp = true});
|
this.enablePullUp = true});
|
||||||
final listTitleTextStyle =
|
final listTitleTextStyle =
|
||||||
|
@ -78,7 +80,7 @@ class SkMutilSearchMore<T extends BaseSearchMoreModel> extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
Center(
|
Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'请选择',
|
title ?? '请选择',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: ScreenAdaper.height(30),
|
fontSize: ScreenAdaper.height(30),
|
||||||
fontWeight: FontWeight.w600),
|
fontWeight: FontWeight.w600),
|
||||||
|
|
Loading…
Reference in New Issue