diff --git a/lib/app_theme.dart b/lib/app_theme.dart index 1911a30..28362d7 100644 --- a/lib/app_theme.dart +++ b/lib/app_theme.dart @@ -92,6 +92,10 @@ final theme = ThemeData( border: Border(bottom: BorderSide(width: 3, color: AppTheme.primaryColorDark)), )), + textSelectionTheme: const TextSelectionThemeData( + selectionHandleColor: AppTheme.primaryColorLight, + cursorColor: AppTheme.primaryColorLight, // 这里是你想要的颜色 + ), appBarTheme: AppBarTheme( centerTitle: true, iconTheme: const IconThemeData(color: Colors.white), diff --git a/lib/models/sale_quotation.model.dart b/lib/models/sale_quotation.model.dart new file mode 100644 index 0000000..54eef6b --- /dev/null +++ b/lib/models/sale_quotation.model.dart @@ -0,0 +1,28 @@ +import 'package:get/get_rx/src/rx_types/rx_types.dart'; + +class SaleQuotationModel { + final String name; + RxBool isExpanded = true.obs; + List items; + SaleQuotationModel({required this.name, required this.items}); +} + +class SaleQuotationItemModel { + final String name; + // 规格 + final String? spec; + final String? unit; + final String? remark; + // 成本 + final double cost; + final int quantity; + final double amount; + SaleQuotationItemModel( + {required this.name, + this.spec, + this.unit, + this.remark, + this.cost = 0, + this.quantity = 0, + this.amount = 0}); +} diff --git a/lib/screens/inventory_inout/components/inventory_inout_card.dart b/lib/screens/inventory_inout/components/inventory_inout_card.dart index 4d13077..f316c0b 100644 --- a/lib/screens/inventory_inout/components/inventory_inout_card.dart +++ b/lib/screens/inventory_inout/components/inventory_inout_card.dart @@ -165,7 +165,7 @@ class InventoryInoutCard extends StatelessWidget { child: Text( textInOut, style: TextStyle( - fontSize: ScreenAdaper.sp(25), + fontSize: ScreenAdaper.height(25), color: AppTheme.nearlyWhite, fontWeight: FontWeight.w600), ), diff --git a/lib/screens/new_inventory_inout/components/inventory_search.dart b/lib/screens/new_inventory_inout/components/inventory_search.dart index 3f7f401..b0ce48d 100644 --- a/lib/screens/new_inventory_inout/components/inventory_search.dart +++ b/lib/screens/new_inventory_inout/components/inventory_search.dart @@ -406,7 +406,7 @@ class InventorySearch extends StatelessWidget { child: Row(children: [ Text( '${itemData.inventoryNumber}', - style: TextStyle(fontSize: ScreenAdaper.sp(40)), + style: TextStyle(fontSize: ScreenAdaper.height(30)), ), SizedBox( width: ScreenAdaper.width(10), @@ -416,26 +416,29 @@ class InventorySearch extends StatelessWidget { children: [ Text( '${itemData.product?.name}${itemData.product?.productSpecification != '' ? '(${itemData.product?.productSpecification})' : ''}', - style: TextStyle(fontSize: ScreenAdaper.sp(40)), + style: TextStyle(fontSize: ScreenAdaper.height(30)), ), Text( '${itemData.product?.company?.name}', style: TextStyle( - fontSize: ScreenAdaper.sp(30), color: AppTheme.grey), + fontSize: ScreenAdaper.height(30), + color: AppTheme.grey), ), Text( '${itemData.project?.name}', style: TextStyle( - fontSize: ScreenAdaper.sp(30), color: AppTheme.grey), + fontSize: ScreenAdaper.height(30), + color: AppTheme.grey), ), Text('¥${double.parse('${itemData.unitPrice}')}', style: TextStyle( - fontSize: ScreenAdaper.sp(30), color: AppTheme.grey)) + fontSize: ScreenAdaper.height(30), + color: AppTheme.grey)) ], ), const Spacer(), Text('${itemData.quantity}${itemData.product?.unit?.label ?? ''}', - style: TextStyle(fontSize: ScreenAdaper.sp(40))), + style: TextStyle(fontSize: ScreenAdaper.height(30))), ]), ); }, diff --git a/lib/screens/sale_quotation/sale_quotation.controller.dart b/lib/screens/sale_quotation/sale_quotation.controller.dart new file mode 100644 index 0000000..2bc9a24 --- /dev/null +++ b/lib/screens/sale_quotation/sale_quotation.controller.dart @@ -0,0 +1,45 @@ +import 'package:get/get.dart'; +import 'package:sk_base_mobile/models/sale_quotation.model.dart'; + +class SaleQuotationController extends GetxController { + static SaleQuotationController get to => Get.find(); + final RxList editingcell = RxList([null, null, null]); + RxList products = RxList([ + SaleQuotationItemModel(name: '矿用本安型支架控制器', unit: '台', spec: 'ZDYZ-Z'), + SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器'), + SaleQuotationItemModel(name: '矿用隔爆兼本安型电源'), + SaleQuotationItemModel(name: '矿用本安型隔离耦合器'), + SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器'), + SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-控制器'), + SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-驱动器'), + SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-隔离耦合器'), + SaleQuotationItemModel(name: '矿用本安型支架无线遥控器'), + SaleQuotationItemModel(name: '矿用隔爆兼本安型电源'), + SaleQuotationItemModel(name: '电液换向阀(10功能10接口)', remark: '中间过渡架主阀组'), + SaleQuotationItemModel(name: '电液换向阀(20功能20接口)', remark: '端头架主阀组'), + SaleQuotationItemModel(name: '自动反冲洗过滤装置', remark: '流量:900L/min,过滤精度25μm'), + SaleQuotationItemModel(name: '全自动反冲洗过滤器电缆', remark: '控制器-自动反冲洗'), + SaleQuotationItemModel(name: '矿用本安型位移传感器'), + SaleQuotationItemModel(name: '矿用本安型压力传感器'), + SaleQuotationItemModel(name: '矿用本安型红外发射器'), + SaleQuotationItemModel(name: '矿用本安型LED信号灯'), + SaleQuotationItemModel(name: '倾角传感器'), + SaleQuotationItemModel(name: '各类安装附件'), + ]); + RxList groups = RxList([]); + @override + void onReady() { + groups.assignAll([ + SaleQuotationModel(name: '中间过渡架电控部分', items: products), + SaleQuotationModel(name: '端头架电控部分', items: products), + SaleQuotationModel(name: '主阀部分', items: products), + SaleQuotationModel(name: '自动反冲洗过滤器部分', items: products), + SaleQuotationModel(name: '位移测量部分', items: products), + SaleQuotationModel(name: '压力检测部分', items: products), + SaleQuotationModel(name: '煤机定位部分', items: products), + SaleQuotationModel(name: '姿态检测部分', items: products), + ]); + + super.onReady(); + } +} diff --git a/lib/screens/sale_quotation/sale_quotation.dart b/lib/screens/sale_quotation/sale_quotation.dart index 746fbc5..4c50062 100644 --- a/lib/screens/sale_quotation/sale_quotation.dart +++ b/lib/screens/sale_quotation/sale_quotation.dart @@ -1,12 +1,15 @@ -import 'dart:math'; - import 'package:collection/collection.dart'; +import 'package:date_format/date_format.dart'; import 'package:flutter/material.dart'; 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/screens/sale_quotation/sale_quotation.controller.dart'; import 'package:sk_base_mobile/util/modal.util.dart'; import 'package:sk_base_mobile/util/screen_adaper_util.dart'; +import 'package:sk_base_mobile/widgets/core/sk_number_input.dart'; +import 'package:sk_base_mobile/widgets/core/sk_text_input.dart'; import 'package:sk_base_mobile/widgets/sk_appbar.dart'; import 'package:flutter_sticky_header/flutter_sticky_header.dart'; @@ -17,10 +20,11 @@ class SaleQuotationPage extends StatelessWidget { final unitPriceWidth = 80.0; final amountWidth = 85.0; final headerTitleStyle = TextStyle( - fontSize: ScreenAdaper.sp(40), + fontSize: ScreenAdaper.height(30), fontWeight: FontWeight.w600, color: AppTheme.nearlyBlack); final headerBgcolor = Color.fromARGB(255, 238, 238, 238); + @override Widget build(BuildContext context) { return Scaffold( @@ -36,7 +40,7 @@ class SaleQuotationPage extends StatelessWidget { '添加组', style: TextStyle( color: AppTheme.white, - fontSize: ScreenAdaper.sp(35), + fontSize: ScreenAdaper.height(25), fontWeight: FontWeight.w600), ) ], @@ -106,11 +110,11 @@ class SaleQuotationPage extends StatelessWidget { Widget buildBody(SaleQuotationModel group, int index) { final titleStyle = TextStyle( - fontSize: ScreenAdaper.sp(40), + fontSize: ScreenAdaper.height(30), color: AppTheme.nearlyBlack, fontWeight: FontWeight.w600); final subTextStyle = - TextStyle(color: AppTheme.grey, fontSize: ScreenAdaper.sp(30)); + TextStyle(color: AppTheme.grey, fontSize: ScreenAdaper.height(30)); return SliverStickyHeader.builder( builder: (context, state) => Container( @@ -174,7 +178,7 @@ class SaleQuotationPage extends StatelessWidget { )) : SliverList( delegate: SliverChildBuilderDelegate( - (context, i) => Slidable( + (context, int i) => Slidable( key: UniqueKey(), endActionPane: ActionPane( extentRatio: 0.2, @@ -215,9 +219,10 @@ class SaleQuotationPage extends StatelessWidget { children: [ Expanded( child: Text( - '${group.items[i].name}', + group.items[i].name, style: TextStyle( - fontSize: ScreenAdaper.sp(35)), + fontWeight: FontWeight.w600, + fontSize: ScreenAdaper.height(30)), ), ), ], @@ -230,7 +235,7 @@ class SaleQuotationPage extends StatelessWidget { style: subTextStyle, ), Text( - '${group.items[i].spec ?? ''}', + group.items[i].spec ?? '', style: subTextStyle, ), ], @@ -243,7 +248,7 @@ class SaleQuotationPage extends StatelessWidget { style: subTextStyle, ), Text( - '${group.items[i].unit ?? ''}', + group.items[i].unit ?? '', style: subTextStyle, ), ], @@ -268,21 +273,42 @@ class SaleQuotationPage extends StatelessWidget { ), ), VerticalDivider(), - Container( - alignment: Alignment.center, - width: quantityWidth, - child: Text('13500'), - ), - Container( - alignment: Alignment.center, - width: unitPriceWidth, - child: Text('¥470000'), - ), - Container( - alignment: Alignment.center, - width: amountWidth, - child: Text('¥63450000'), - ), + buildEditCell( + Container( + alignment: Alignment.center, + width: quantityWidth, + child: Text( + '${controller.groups[index].items[i].quantity}'), + ), + groupIndex: index, + rowIndex: i, + field: 'quantity', + inputWidth: quantityWidth, + value: controller.groups[index].items[i].quantity), + buildEditCell( + Container( + alignment: Alignment.center, + width: unitPriceWidth, + child: Text( + '${controller.groups[index].items[i].cost}'), + ), + groupIndex: index, + rowIndex: i, + field: 'cost', + inputWidth: unitPriceWidth, + value: controller.groups[index].items[i].cost), + buildEditCell( + Container( + alignment: Alignment.center, + width: amountWidth, + child: Text( + '${controller.groups[index].items[i].amount}'), + ), + groupIndex: index, + rowIndex: i, + field: 'amount', + inputWidth: amountWidth, + value: controller.groups[index].items[i].amount), ], ), ), @@ -294,66 +320,32 @@ class SaleQuotationPage extends StatelessWidget { )), ); } -} -class SaleQuotationModel { - final String name; - RxBool isExpanded = true.obs; - List items; - SaleQuotationModel({required this.name, required this.items}); -} - -class SaleQuotationItemModel { - final String name; - // 规格 - final String? spec; - final String? unit; - final String? remark; - // 成本 - final double? cost; - SaleQuotationItemModel( - {required this.name, this.spec, this.unit, this.remark, this.cost}); -} - -class SaleQuotationController extends GetxController { - static SaleQuotationController get to => Get.find(); - - RxList products = RxList([ - SaleQuotationItemModel(name: '矿用本安型支架控制器', unit: '台', spec: 'ZDYZ-Z'), - SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器'), - SaleQuotationItemModel(name: '矿用隔爆兼本安型电源'), - SaleQuotationItemModel(name: '矿用本安型隔离耦合器'), - SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器'), - SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-控制器'), - SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-驱动器'), - SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-隔离耦合器'), - SaleQuotationItemModel(name: '矿用本安型支架无线遥控器'), - SaleQuotationItemModel(name: '矿用隔爆兼本安型电源'), - SaleQuotationItemModel(name: '电液换向阀(10功能10接口)', remark: '中间过渡架主阀组'), - SaleQuotationItemModel(name: '电液换向阀(20功能20接口)', remark: '端头架主阀组'), - SaleQuotationItemModel(name: '自动反冲洗过滤装置', remark: '流量:900L/min,过滤精度25μm'), - SaleQuotationItemModel(name: '全自动反冲洗过滤器电缆', remark: '控制器-自动反冲洗'), - SaleQuotationItemModel(name: '矿用本安型位移传感器'), - SaleQuotationItemModel(name: '矿用本安型压力传感器'), - SaleQuotationItemModel(name: '矿用本安型红外发射器'), - SaleQuotationItemModel(name: '矿用本安型LED信号灯'), - SaleQuotationItemModel(name: '倾角传感器'), - SaleQuotationItemModel(name: '各类安装附件'), - ]); - RxList groups = RxList([]); - @override - void onReady() { - groups.assignAll([ - SaleQuotationModel(name: '中间过渡架电控部分', items: products), - SaleQuotationModel(name: '端头架电控部分', items: products), - SaleQuotationModel(name: '主阀部分', items: products), - SaleQuotationModel(name: '自动反冲洗过滤器部分', items: products), - SaleQuotationModel(name: '位移测量部分', items: products), - SaleQuotationModel(name: '压力检测部分', items: products), - SaleQuotationModel(name: '煤机定位部分', items: products), - SaleQuotationModel(name: '姿态检测部分', items: products), - ]); - - super.onReady(); + Widget buildEditCell(Widget content, + {required int groupIndex, + required int rowIndex, + required String field, + required double inputWidth, + required dynamic value}) { + return Obx(() => controller.editingcell[0] == groupIndex && + controller.editingcell[1] == rowIndex && + controller.editingcell[2] == field + ? Container( + width: inputWidth, + child: SkNumberInput( + hint: '', + isDense: true, + autoFocus: true, + contentPadding: EdgeInsets.symmetric( + horizontal: ScreenAdaper.width(20), + vertical: ScreenAdaper.height(10)), + textController: TextEditingController(text: '${value}')), + ) + : InkWell( + onTap: () { + controller.editingcell.value = [groupIndex, rowIndex, field]; + }, + child: content, + )); } } diff --git a/lib/util/modal.util.dart b/lib/util/modal.util.dart index c3005b1..8ce91e0 100644 --- a/lib/util/modal.util.dart +++ b/lib/util/modal.util.dart @@ -44,7 +44,7 @@ class ModalUtil { Text( title, style: TextStyle( - fontSize: ScreenAdaper.sp(40), + fontSize: ScreenAdaper.height(30), fontWeight: FontWeight.w500), ) ]), @@ -66,7 +66,8 @@ class ModalUtil { child: Text( '取消', style: TextStyle( - color: Colors.black, fontSize: ScreenAdaper.sp(40)), + color: Colors.black, + fontSize: ScreenAdaper.height(30)), )), )), const VerticalDivider(), @@ -84,7 +85,8 @@ class ModalUtil { child: Text( '确定', style: TextStyle( - color: Colors.black, fontSize: ScreenAdaper.sp(40)), + color: Colors.black, + fontSize: ScreenAdaper.height(30)), ), ), )), diff --git a/lib/widgets/core/sk_number_input.dart b/lib/widgets/core/sk_number_input.dart index 1781362..2cd7c8e 100644 --- a/lib/widgets/core/sk_number_input.dart +++ b/lib/widgets/core/sk_number_input.dart @@ -3,31 +3,60 @@ import 'package:flutter/services.dart'; import 'package:sk_base_mobile/app_theme.dart'; import 'package:sk_base_mobile/util/screen_adaper_util.dart'; -class SkNumberInput extends StatelessWidget { +class SkNumberInput extends StatefulWidget { final TextEditingController textController; - final VoidCallback? onTap; + final Function(FocusNode)? onTap; final Function(String)? onChanged; - + final bool isDense; + final EdgeInsetsGeometry? contentPadding; + final bool autoFocus; final bool isRequired; final String labelText; final String? hint; - const SkNumberInput( - {super.key, - required this.textController, - this.onTap, - this.onChanged, - this.hint, - this.isRequired = false, - this.labelText = ''}); + + SkNumberInput({ + super.key, + required this.textController, + this.onTap, + this.onChanged, + this.hint, + this.isRequired = false, + this.labelText = '', + this.autoFocus = false, + this.contentPadding, + this.isDense = false, + }); + + @override + State createState() => _SkNumberInputState(); +} + +class _SkNumberInputState extends State { + late FocusNode focusNode = FocusNode(); + @override + void initState() { + super.initState(); + if (widget.autoFocus) { + WidgetsBinding.instance.addPostFrameCallback((_) { + focusNode.requestFocus(); + }); + } + } + @override Widget build(BuildContext context) { return TextFormField( - controller: textController, + focusNode: focusNode, + onTap: () { + if (widget.onTap == null) { + widget.onTap!(focusNode); + } + }, + controller: widget.textController, onTapOutside: (event) { FocusScope.of(context).unfocus(); }, - onChanged: onChanged ?? (_) {}, - onTap: onTap ?? () {}, + onChanged: widget.onChanged ?? (_) {}, textAlign: TextAlign.center, keyboardType: TextInputType.number, inputFormatters: [ @@ -35,20 +64,22 @@ class SkNumberInput extends StatelessWidget { FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,10}')), ], decoration: InputDecoration( + contentPadding: widget.contentPadding, + isDense: widget.isDense, // contentPadding: EdgeInsets.symmetric(vertical: ScreenAdaper.height(18)), floatingLabelBehavior: FloatingLabelBehavior.always, label: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ - if (isRequired) + if (widget.isRequired) Text( "*", style: TextStyle( color: Colors.red, fontSize: ScreenAdaper.height(30)), ), Text( - labelText, + widget.labelText, style: TextStyle(fontSize: ScreenAdaper.height(30)), ), ]), @@ -56,7 +87,7 @@ class SkNumberInput extends StatelessWidget { borderSide: const BorderSide(color: AppTheme.primaryColorLight, width: 2), borderRadius: BorderRadius.circular(ScreenAdaper.sp(15))), - hintText: hint ?? '请输入', + hintText: widget.hint ?? '请输入', ), ); } diff --git a/lib/widgets/core/sk_text_input.dart b/lib/widgets/core/sk_text_input.dart index 86e78e4..245bdd4 100644 --- a/lib/widgets/core/sk_text_input.dart +++ b/lib/widgets/core/sk_text_input.dart @@ -2,14 +2,17 @@ import 'package:flutter/material.dart'; import 'package:sk_base_mobile/app_theme.dart'; import 'package:sk_base_mobile/util/screen_adaper_util.dart'; -class SkTextInput extends StatelessWidget { +class SkTextInput extends StatefulWidget { final TextEditingController textController; - final VoidCallback? onTap; + final Function(FocusNode)? onTap; final bool isRequired; final String labelText; final String? hint; final bool isTextArea; - const SkTextInput({ + final bool isDense; + final EdgeInsetsGeometry? contentPadding; + final bool autoFocus; + SkTextInput({ super.key, required this.textController, this.onTap, @@ -17,30 +20,57 @@ class SkTextInput extends StatelessWidget { this.isRequired = false, this.labelText = '', this.isTextArea = false, + this.autoFocus = false, + this.contentPadding, + this.isDense = false, }); + + @override + State createState() => _SkTextInputState(); +} + +class _SkTextInputState extends State { + late FocusNode focusNode = FocusNode(); + @override + void initState() { + super.initState(); + if (widget.autoFocus) { + WidgetsBinding.instance.addPostFrameCallback((_) { + focusNode.requestFocus(); + }); + } + } + @override Widget build(BuildContext context) { return TextFormField( - controller: textController, + focusNode: focusNode, + controller: widget.textController, onTapOutside: (event) { FocusScope.of(context).unfocus(); }, - maxLines: isTextArea ? 2 : 1, // 添加这行代码 - onTap: onTap ?? () {}, + maxLines: widget.isTextArea ? 2 : 1, // 添加这行代码 + onTap: () { + if (widget.onTap == null) { + widget.onTap!(focusNode); + } + }, decoration: InputDecoration( + contentPadding: widget.contentPadding, + isDense: widget.isDense, floatingLabelBehavior: FloatingLabelBehavior.always, label: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ - if (isRequired) + if (widget.isRequired) Text( "*", style: TextStyle( color: Colors.red, fontSize: ScreenAdaper.height(30)), ), Text( - labelText, + widget.labelText, style: TextStyle(fontSize: ScreenAdaper.height(30)), ), ]), @@ -48,7 +78,7 @@ class SkTextInput extends StatelessWidget { borderSide: const BorderSide(color: AppTheme.primaryColorLight, width: 2), borderRadius: BorderRadius.circular(ScreenAdaper.sp(15))), - hintText: hint ?? '请输入', + hintText: widget.hint ?? '请输入', ), ); } diff --git a/lib/widgets/sk_appbar.dart b/lib/widgets/sk_appbar.dart index 5582892..b5c989a 100644 --- a/lib/widgets/sk_appbar.dart +++ b/lib/widgets/sk_appbar.dart @@ -11,7 +11,7 @@ class SkAppbar extends StatelessWidget implements PreferredSizeWidget { return AppBar( title: Text( title, - style: TextStyle(fontSize: ScreenAdaper.sp(40)), + style: TextStyle(fontSize: ScreenAdaper.height(30)), ), actions: action, );