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/empty.dart'; import 'package:sk_base_mobile/widgets/sk_appbar.dart'; import 'package:flutter_sticky_header/flutter_sticky_header.dart'; class SaleQuotationPage extends StatelessWidget { SaleQuotationPage({super.key}); final controller = Get.put(SaleQuotationController()); final quantityWidth = 55.0; final unitPriceWidth = 80.0; final amountWidth = 85.0; final headerTitleStyle = TextStyle( 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( floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, floatingActionButton: FloatingActionButton( onPressed: () { controller.addGroup(); }, child: const Icon(Icons.add), ), appBar: SkAppbar( title: '报价计算', action: [ TextButton( onPressed: () { controller.addGroup(); }, child: Row( children: [ Icon(Icons.save, color: AppTheme.white), SizedBox( width: ScreenAdaper.width(10), ), Text( '保存', style: TextStyle( color: AppTheme.white, fontSize: ScreenAdaper.height(25), fontWeight: FontWeight.w600), ) ], )) ], ), body: SafeArea( bottom: ScreenAdaper.isLandspace() ? false : true, child: Column( children: [ builderHeader(), Expanded( child: Obx(() => controller.groups.isEmpty ? Empty(text: '请先添加分组') : CustomScrollView( slivers: controller.groups .mapIndexed( (index, e) => buildBody(e, index)) .toList(), ))), ], ), ), ); } Widget builderHeader() { return Container( decoration: BoxDecoration( color: headerBgcolor, border: Border( bottom: BorderSide(color: AppTheme.nearlyBlack.withOpacity(0.5)))), padding: EdgeInsets.only( left: ScreenAdaper.width(20), top: ScreenAdaper.height(10), bottom: ScreenAdaper.height(10)), child: Row( children: [ Text( '名称', style: headerTitleStyle, ), const Spacer(), Container( alignment: Alignment.center, width: quantityWidth, // constraints: BoxConstraints(minWidth: quantityWidth), child: Text( '数量', style: headerTitleStyle, ), ), Container( alignment: Alignment.center, width: unitPriceWidth, // constraints: BoxConstraints(minWidth: unitPriceWidth), child: Text( '单价', style: headerTitleStyle, ), ), Container( alignment: Alignment.center, width: amountWidth, child: Text( '总价', style: headerTitleStyle, ), ), ], )); } Widget buildBody(SaleQuotationModel group, int index) { final titleStyle = TextStyle( fontSize: ScreenAdaper.height(30), color: AppTheme.nearlyBlack, fontWeight: FontWeight.w600); final subTextStyle = TextStyle(color: AppTheme.grey, fontSize: ScreenAdaper.height(30)); return SliverStickyHeader.builder( builder: (context, state) => Container( height: ScreenAdaper.height(80), color: headerBgcolor.withOpacity(1.0 - state.scrollPercentage), alignment: Alignment.centerLeft, child: InkWell( onTap: () { controller.groups[index].isExpanded.value = !controller.groups[index].isExpanded.value; }, child: Row( children: [ Obx(() => controller.groups[index].isExpanded.value ? IconButton( padding: EdgeInsets.zero, onPressed: () { controller.groups[index].isExpanded.value = false; }, icon: const Icon( Icons.expand_more, color: AppTheme.nearlyBlack, ), ) : IconButton( padding: EdgeInsets.zero, onPressed: () { controller.groups[index].isExpanded.value = true; }, icon: const Icon( Icons.chevron_right, color: AppTheme.nearlyBlack, ), )), Text( group.name, style: titleStyle, ), const Spacer(), IconButton( onPressed: () { ModalUtil.confirm( title: '确定要删除此组吗?', onConfirm: () { controller.removeGroup(index); }); }, icon: const Icon( Icons.remove_circle_outline_outlined, color: AppTheme.nearlyBlack, )), IconButton( onPressed: () { controller.addItems(); }, icon: const Icon( Icons.add_circle_outline, color: AppTheme.nearlyBlack, )), ], ))), sliver: Obx(() => !controller.groups[index].isExpanded.value ? SliverList( delegate: SliverChildBuilderDelegate( (context, i) => SizedBox(), childCount: 0, )) : SliverList( delegate: SliverChildBuilderDelegate( (context, int i) => Slidable( key: UniqueKey(), endActionPane: ActionPane( extentRatio: 0.2, motion: const DrawerMotion(), children: [ SlidableAction( padding: EdgeInsets.zero, onPressed: (_) {}, backgroundColor: AppTheme.dangerColor, foregroundColor: Colors.white, icon: Icons.delete, label: 'Delete', ), ], ), child: Container( padding: EdgeInsets.only( left: ScreenAdaper.width(20), top: ScreenAdaper.height(15), bottom: ScreenAdaper.height(15), ), constraints: BoxConstraints(minHeight: ScreenAdaper.height(100)), decoration: BoxDecoration( border: Border( bottom: BorderSide(width: 1, color: Colors.grey[200]!), ), ), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( group.items[i].name, style: TextStyle( fontWeight: FontWeight.w600, fontSize: ScreenAdaper.height(30)), ), ), ], ), if (group.items[i].spec != null) Row( children: [ Text( '规格、型号: ', style: subTextStyle, ), Text( group.items[i].spec ?? '', style: subTextStyle, ), ], ), if (group.items[i].unit != null) Row( children: [ Text( '单位: ', style: subTextStyle, ), Text( group.items[i].unit ?? '', style: subTextStyle, ), ], ), if (group.items[i].remark != null) Row( children: [ Text( '备注: ', style: subTextStyle, ), Expanded( child: Text( '${group.items[i].remark ?? ''}${group.items[i].remark ?? ''}${controller.products[i].remark ?? ''}', overflow: TextOverflow.ellipsis, style: subTextStyle, ), ), ], ), ], ), ), VerticalDivider(), buildEditCell( Container( alignment: Alignment.center, width: quantityWidth, child: Text( '${controller.groups[index].items[i].quantity}', style: TextStyle( fontSize: ScreenAdaper.height(25)), ), ), 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}', style: TextStyle( fontSize: ScreenAdaper.height(25)), ), ), 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}', style: TextStyle( fontSize: ScreenAdaper.height(25)), ), ), groupIndex: index, rowIndex: i, field: 'amount', inputWidth: amountWidth, value: controller.groups[index].items[i].amount), ], ), ), ), childCount: !controller.groups[index].isExpanded.value ? 0 : group.items.length, ), )), ); } 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, )); } }