diff --git a/tdesign-component/example/lib/page/td_cascader_page.dart b/tdesign-component/example/lib/page/td_cascader_page.dart index 2ea5b6c7a..f122151f0 100644 --- a/tdesign-component/example/lib/page/td_cascader_page.dart +++ b/tdesign-component/example/lib/page/td_cascader_page.dart @@ -1,4 +1,7 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; import '../../annotation/demo.dart'; import '../../base/example_widget.dart'; @@ -144,14 +147,16 @@ class _TDCascaderPageState extends State { String? _initData_3; String _selected_3 = ''; - List _data_3 = [ + final List _data_3 = [ { "label": '技术部门', "value": '110000', + "segmentValue": 'J', "children": [ { "value": '110100', "label": '部门一', + "segmentValue": 'B', "children": [ {"value": '110101', "label": '洪磊', "segmentValue": 'H'}, {"value": '110102', "label": '洪磊2', "segmentValue": 'H'}, @@ -159,22 +164,23 @@ class _TDCascaderPageState extends State { {"value": '110105', "label": '洪磊4', "segmentValue": 'H'}, {"value": '110106', "label": '郭天1', "segmentValue": 'G'}, {"value": '110107', "label": '郭天2', "segmentValue": 'G'}, - {"value": '110108', "label": '郭天3', "segmentValue": 'G'}, {"value": '110109', "label": '冯笑1', "segmentValue": 'F'}, + {"value": '110108', "label": '郭天3', "segmentValue": 'G'}, ], }, { "value": '110200', "label": '部门二', + "segmentValue": 'B', "children": [ - {"value": '110201', "label": '张雷1'}, - {"value": '110202', "label": '张雷2'}, - {"value": '1102022', "label": '张雷3'}, - {"value": '110205', "label": '张雷4'}, - {"value": '110206', "label": '张雷5'}, - {"value": '110207', "label": '张雷6'}, - {"value": '110208', "label": '张雷7'}, - {"value": '110209', "label": '张雷8'}, + {"value": '110201', "label": '洪磊', "segmentValue": 'H'}, + {"value": '110205', "label": '洪磊4', "segmentValue": 'H'}, + {"value": '110206', "label": '郭天1', "segmentValue": 'G'}, + {"value": '110207', "label": '郭天2', "segmentValue": 'G'}, + {"value": '110208', "label": '郭天3', "segmentValue": 'G'}, + {"value": '110209', "label": '冯笑1', "segmentValue": 'F'}, + {"value": '110202', "label": '洪磊2', "segmentValue": 'H'}, + {"value": '1102022', "label": '洪磊3', "segmentValue": 'H'}, ], }, ], @@ -182,26 +188,91 @@ class _TDCascaderPageState extends State { { "label": '行政部门', "value": '120000', + "segmentValue": 'X', "children": [ { "value": '120100', "label": '部门一', + "segmentValue": 'B', + "children": [ + {"value": '120201', "label": '洪磊', "segmentValue": 'H'}, + {"value": '120205', "label": '洪磊4', "segmentValue": 'H'}, + {"value": '120206', "label": '郭天1', "segmentValue": 'G'}, + {"value": '120207', "label": '郭天2', "segmentValue": 'G'}, + {"value": '120208', "label": '郭天3', "segmentValue": 'G'}, + {"value": '120209', "label": '冯笑1', "segmentValue": 'F'}, + {"value": '120202', "label": '洪磊2', "segmentValue": 'H'}, + {"value": '1202022', "label": '洪磊3', "segmentValue": 'H'}, + ], + }, + ], + }, + ]; + + String? _initData_4; + String _selected_4 = ''; + final List _data_4 = [ + { + "label": '技术部门', + "value": '110000', + "children": [ + { + "value": '110100', + "label": '部门一', + "children": [ + {"value": '110201', "label": '后勤部门', "children":[ + { + "value": '110301', "label": '后勤A组',"children":[ + { + "value": '110401', "label": '一组',"children":[ + {"value": '110501', "label": '洪磊',}, + {"value": '110502', "label": '洪磊2'}, + {"value": '110506', "label": '郭天1'}, + {"value": '110507', "label": '郭天2'}, + {"value": '110508', "label": '郭天3'}, + {"value": '110509', "label": '冯笑1'}, + {"value": '1105022', "label": '洪磊3'}, + {"value": '110505', "label": '洪磊4'}, + ] + } + ] + } + ]}, + ], + }, + { + "value": '120100', + "label": '部门二', "children": [ - {"value": '120101', "label": '张雷1'}, - {"value": '120102', "label": '张雷2'}, - {"value": '120103', "label": '张雷3'}, - {"value": '120104', "label": '张雷4'}, - {"value": '120105', "label": '张雷5'}, - {"value": '120106', "label": '张雷6'}, - {"value": '120110', "label": '张雷7'}, - {"value": '120111', "label": '张雷8'}, - {"value": '120112', "label": '张雷9'}, + {"value": '120201', "label": '后勤部门', "children":[ + { + "value": '120301', "label": '后勤A组',"children":[ + { + "value": '120401', "label": '一组',"children":[ + {"value": '120501', "label": '张雷1'}, + {"value": '120502', "label": '张雷2'}, + {"value": '1205022', "label": '张雷3'}, + {"value": '120505', "label": '张雷4'}, + {"value": '120506', "label": '张雷5'}, + {"value": '120507', "label": '张雷6'}, + {"value": '120508', "label": '张雷7'}, + {"value": '120509', "label": '张雷8'}, + ] + } + ] + } + ]}, ], }, ], }, ]; @override + void initState() { + // TODO: implement initState + super.initState(); + } + @override Widget build(BuildContext context) { return Container( color: TDTheme.of(context).whiteColor1, @@ -211,18 +282,17 @@ class _TDCascaderPageState extends State { desc: '用于多层级数据的逐级选择', children: [ ExampleModule(title: '组件类型', children: [ - ExampleItem(desc: '垂直级联选择器', builder: _buildVerticalCascader), - ExampleItem(desc: '垂直级联选择器-带字母定位', builder: _buildVerticalLetterCascader), + ExampleItem(desc: '垂直级联选择器', builder: _buildVerticalCascader), + ExampleItem(desc: '垂直级联选择器-带字母定位', builder: _buildVerticalLetterCascader), ExampleItem(desc: '水平级联选择器', builder: _buildHorizontalCascader), - ExampleItem(desc: '水平级联选择器-带字母定位', builder: _buildHorizontalLetterCascader), + ExampleItem(desc: '水平级联选择器-带字母定位', builder: _buildHorizontalLetterCascader), ExampleItem(desc: '水平级联选择器-部门', builder: _buildHorizontalCompanyCascader), ExampleItem(desc: '垂直级联选择器-部门', builder: _buildVerticalCompanyCascader), ]), ], test: [ - ExampleItem( - desc: '测试使用次标题', - builder: _buildVerticalSubTitleCascader), + ExampleItem(desc: '测试使用次标题', builder: _buildVerticalSubTitleCascader), + ExampleItem(desc: '垂直级联选择器-部门', builder: _buildTestVerticalCompanyCascader), ], ), ); @@ -305,8 +375,12 @@ class _TDCascaderPageState extends State { Widget _buildHorizontalLetterCascader(BuildContext context) { return GestureDetector( onTap: () { - TDCascader.showMultiCascader(context, title: '选择地址', data: _data_2, initialData: _initData_2, theme: 'tab', - onChange: (List selectData) { + TDCascader.showMultiCascader(context, + title: '选择地址', + data: _data_2, + initialData: _initData_2, + isLetterSort: true, + theme: 'tab', onChange: (List selectData) { setState(() { List result = []; int len = selectData.length; @@ -328,7 +402,7 @@ class _TDCascaderPageState extends State { Widget _buildHorizontalCompanyCascader(BuildContext context) { return GestureDetector( onTap: () { - TDCascader.showMultiCascader(context, title: '选择部门人员', data: _data_3, initialData: _initData_3, theme: 'tab', + TDCascader.showMultiCascader(context, title: '选择部门人员', data: _data_3,isLetterSort: true, initialData: _initData_3, theme: 'tab', onChange: (List selectData) { setState(() { List result = []; @@ -351,7 +425,7 @@ class _TDCascaderPageState extends State { Widget _buildVerticalCompanyCascader(BuildContext context) { return GestureDetector( onTap: () { - TDCascader.showMultiCascader(context, title: '选择部门人员', data: _data_3, initialData: _initData_3, theme: 'step', + TDCascader.showMultiCascader(context, title: '选择部门人员', data: _data_3,isLetterSort: true, initialData: _initData_3, theme: 'step', onChange: (List selectData) { setState(() { List result = []; @@ -396,7 +470,31 @@ class _TDCascaderPageState extends State { child: _buildSelectRow(context, _selected_1, '选择地区'), ); } - + @Demo(group: 'cascader') + Widget _buildTestVerticalCompanyCascader(BuildContext context) { + return GestureDetector( + onTap: () { + TDCascader.showMultiCascader(context, + title: '选择部门人员', + data: _data_4, + initialData: _initData, + theme: 'step', onChange: (List selectData) { + setState(() { + List result = []; + int len = selectData.length; + _initData = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_4 = result.join('/'); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + child: _buildSelectRow(context, _selected_4, '选择部门人员'), + ); + } Widget _buildSelectRow(BuildContext context, String output, String title) { return Container( color: TDTheme.of(context).whiteColor1, diff --git a/tdesign-component/lib/src/components/cascader/td_cascader.dart b/tdesign-component/lib/src/components/cascader/td_cascader.dart index 67a82e592..ee35a94ff 100644 --- a/tdesign-component/lib/src/components/cascader/td_cascader.dart +++ b/tdesign-component/lib/src/components/cascader/td_cascader.dart @@ -15,6 +15,7 @@ class TDCascader { double cascaderHeight = 500, String? initialData, String? closeText, + bool isLetterSort=false, List? subTitles, Function? onClose}) { showModalBottomSheet( @@ -32,6 +33,7 @@ class TDCascader { onChange: onChange, closeText: closeText, theme: theme, + isLetterSort:isLetterSort, subTitles: subTitles); }); } diff --git a/tdesign-component/lib/src/components/cascader/td_multi_cascader.dart b/tdesign-component/lib/src/components/cascader/td_multi_cascader.dart index 5d77e19f7..f793f95ae 100644 --- a/tdesign-component/lib/src/components/cascader/td_multi_cascader.dart +++ b/tdesign-component/lib/src/components/cascader/td_multi_cascader.dart @@ -34,6 +34,9 @@ class TDMultiCascader extends StatefulWidget { /// 顶部圆角 final double? topRadius; + /// 是否开启字母排序 + final bool isLetterSort; + /// 关闭按钮文本 final String? closeText; @@ -55,6 +58,7 @@ class TDMultiCascader extends StatefulWidget { this.backgroundColor, this.topRadius, this.closeText, + this.isLetterSort = false, this.onClose, required this.onChange}); @@ -63,7 +67,6 @@ class TDMultiCascader extends StatefulWidget { } class _TDMultiCascaderState extends State with TickerProviderStateMixin { - List _tabListData = []; /// 当前tab选中的值 @@ -90,17 +93,19 @@ class _TDMultiCascaderState extends State with TickerProviderSt MultiCascaderListModel item = MultiCascaderListModel( label: widget.data[index]['label'], value: widget.data[index]['value'], - segmentValue:widget.data[index]['segmentValue'], + segmentValue: widget.data[index]['segmentValue'], level: 0, ); _listData.add(item); + if (widget.data[index]['children'] != null && widget.data[index]['children'].length > 0) { _buildRecursiveList(1, widget.data[index]['value'], widget.data[index]['children']); } }); - _listDataSegmenter(); + if (widget.isLetterSort) { + _listDataSegmenter(); + } _selectListData = _listData.where((element) => element.level == 0).toList(); - _tabListData.add(MultiCascaderListModel( label: '选择选项', )); @@ -108,7 +113,7 @@ class _TDMultiCascaderState extends State with TickerProviderSt _tabListData.clear(); _initLocation(widget.initialData!); _currentTabIndex = _tabListData.length - 1; - _level=_currentTabIndex; + _level = _currentTabIndex; _tabListData = _tabListData.reversed.toList(); _selectTabValue = widget.initialData; _selectListData = @@ -131,11 +136,7 @@ class _TDMultiCascaderState extends State with TickerProviderSt ), child: Column( mainAxisSize: MainAxisSize.min, - children: [ - _buildTitle(context), - _buildTabThemeBox(context), - Expanded(child: _buildContentBox(context)) - ], + children: [_buildTitle(context), _buildTabThemeBox(context), Expanded(child: _buildContentBox(context))], ), ); } @@ -169,11 +170,11 @@ class _TDMultiCascaderState extends State with TickerProviderSt } } - void _listDataSegmenter(){ - _listData.sort((a,b){ - if(a.segmentValue==null||b.segmentValue==null){ - return 0; - }else{ + void _listDataSegmenter() { + _listData.sort((a, b) { + if (a.segmentValue == null || b.segmentValue == null) { + return 0; + } else { return a.segmentValue!.toLowerCase().compareTo(b.segmentValue!.toLowerCase()); } }); @@ -185,7 +186,7 @@ class _TDMultiCascaderState extends State with TickerProviderSt label: data[index]['label'], value: data[index]['value'], parentValue: parentValue, - segmentValue:data[index]['segmentValue'], + segmentValue: data[index]['segmentValue'], level: depth, ); _listData.add(item); @@ -224,14 +225,19 @@ class _TDMultiCascaderState extends State with TickerProviderSt child: Container( height: 58, alignment: Alignment.center, - child:Padding( + child: Padding( padding: const EdgeInsets.only(left: 2, right: 16), - child: widget.closeText==null ? Icon( - TDIcons.close, - color: TDTheme.of(context).fontGyColor1, - ):TDText(widget.closeText,style:TextStyle( - fontSize: TDTheme.of(context).fontTitleMedium!.size, - color: TDTheme.of(context).fontGyColor1),), + child: widget.closeText == null + ? Icon( + TDIcons.close, + color: TDTheme.of(context).fontGyColor1, + ) + : TDText( + widget.closeText, + style: TextStyle( + fontSize: TDTheme.of(context).fontTitleMedium!.size, + color: TDTheme.of(context).fontGyColor1), + ), ), ))), ], @@ -247,7 +253,7 @@ class _TDMultiCascaderState extends State with TickerProviderSt Widget _buildStepBox(BuildContext context) { var maxWidth = MediaQuery.of(context).size.width; return Container( - decoration: BoxDecoration(border: Border(bottom: BorderSide(color: Color.fromRGBO(0, 0, 0, 0.1),width: 0.5))), + decoration: BoxDecoration(border: Border(bottom: BorderSide(color: Color.fromRGBO(0, 0, 0, 0.1), width: 0.5))), padding: EdgeInsets.only(bottom: 11), width: maxWidth, child: ListView( @@ -278,7 +284,7 @@ class _TDMultiCascaderState extends State with TickerProviderSt style: TextStyle( fontSize: 14, color: _currentTabIndex == index ? TDTheme.of(context).brandNormalColor : Colors.black), - fontWeight: _currentTabIndex == index?FontWeight.w600:FontWeight.w400, + fontWeight: _currentTabIndex == index ? FontWeight.w600 : FontWeight.w400, ), ), Padding( @@ -298,7 +304,7 @@ class _TDMultiCascaderState extends State with TickerProviderSt var maxWidth = MediaQuery.of(context).size.width; return Container( height: 48, - decoration: BoxDecoration(border: Border(bottom: BorderSide(color: Color.fromRGBO(0, 0, 0, 0.1),width: 0.5))), + decoration: BoxDecoration(border: Border(bottom: BorderSide(color: Color.fromRGBO(0, 0, 0, 0.1), width: 0.5))), width: maxWidth, child: TDCustomTab( tabs: List.generate(_tabListData.length, (index) { @@ -317,92 +323,101 @@ class _TDMultiCascaderState extends State with TickerProviderSt return Container( width: maxWidth, padding: EdgeInsets.only(left: 16, right: 16), - child:Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if(widget.subTitles!=null) - Container( - height: 50, - padding: EdgeInsets.only(top: 20,), - child:TDText(widget.subTitles![_level],style: TextStyle(color: Color.fromRGBO(0, 0, 0, 0.4)),font: TDTheme.of(context).fontTitleSmall,) //, - ), - Expanded(child: PageView( - scrollDirection: Axis.horizontal, - reverse: false, - controller: PageController(initialPage: 1, keepPage: false), - children: List.generate(1, (index) { - return ListView.builder( - controller: _scrollListController, - itemCount: _selectListData.length, - itemBuilder: (context, index) { - MultiCascaderListModel item = _selectListData[index]; - MultiCascaderListModel preItem =index==0?MultiCascaderListModel():_selectListData[index-1]; - return GestureDetector( - onTap: () { - int level = 0; - if (_tabListData.length > 2 && _currentTabIndex == 0) { - _tabListData.clear(); - _tabListData.add(MultiCascaderListModel( - label: '选择选项', - )); - } - if (item.level != null) { - level = item.level!; - } - - if(widget.subTitles!=null&&widget.subTitles!.length-1>_level){ - _level=level+1; - } - List isList = _tabListData.where((element) => element.level == item.level).toList(); - if (isList.isNotEmpty) { - _tabListData.removeAt(level); - } - setState(() { - _tabListData.insert(level, item); - _selectTabValue = item.value; - //下一级查询 - _getChildrenListData(level + 1, item.value!); - }); - }, - child: Container( - height: 56, - decoration: BoxDecoration(border: Border.all(color: Colors.transparent)), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - if(item.segmentValue!=null) - SizedBox( - width:32, - child:item.segmentValue!=preItem.segmentValue?TDText( - '${item.segmentValue}', - font: Font(size: 16, lineHeight: 24), - ):null, - ), - TDText( - '${item.label}', - font: Font(size: 16, lineHeight: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (widget.subTitles != null) + Container( + height: 50, + padding: EdgeInsets.only( + top: 20, + ), + child: TDText( + widget.subTitles![_level], + style: TextStyle(color: Color.fromRGBO(0, 0, 0, 0.4)), + font: TDTheme.of(context).fontTitleSmall, + ) //, + ), + Expanded( + child: PageView( + scrollDirection: Axis.horizontal, + reverse: false, + controller: PageController(initialPage: 1, keepPage: false), + children: List.generate(1, (index) { + return ListView.builder( + controller: _scrollListController, + itemCount: _selectListData.length, + itemBuilder: (context, index) { + MultiCascaderListModel item = _selectListData[index]; + MultiCascaderListModel preItem = index == 0 ? MultiCascaderListModel() : _selectListData[index - 1]; + return GestureDetector( + onTap: () { + int level = 0; + if (_tabListData.length > 2 && _currentTabIndex == 0) { + _tabListData.clear(); + _tabListData.add(MultiCascaderListModel( + label: '选择选项', + )); + } + if (item.level != null) { + level = item.level!; + } + + if (widget.subTitles != null && widget.subTitles!.length - 1 > _level) { + _level = level + 1; + } + List isList = _tabListData.where((element) => element.level == item.level).toList(); + if (isList.isNotEmpty) { + _tabListData.removeAt(level); + } + setState(() { + _tabListData.insert(level, item); + _selectTabValue = item.value; + //下一级查询 + _getChildrenListData(level + 1, item.value!); + }); + }, + child: Container( + height: 56, + decoration: BoxDecoration(border: Border.all(color: Colors.transparent)), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + if (item.segmentValue != null) + SizedBox( + width: 32, + child: item.segmentValue != preItem.segmentValue + ? TDText( + '${item.segmentValue}', + font: Font(size: 16, lineHeight: 24), + ) + : null, ), - ], - ), + TDText( + '${item.label}', + font: Font(size: 16, lineHeight: 24), + ), + ], ), - if (_selectTabValue == item.value) - Icon( - TDIcons.check, - color: TDTheme.of(context).brandNormalColor, - ) - ], - )), - ); - }, - ); - }), - )) - ], + ), + if (_selectTabValue == item.value) + Icon( + TDIcons.check, + color: TDTheme.of(context).brandNormalColor, + ) + ], + )), + ); + }, + ); + }), + )) + ], )); } @@ -415,10 +430,10 @@ class _TDMultiCascaderState extends State with TickerProviderSt if (index < _tabListData.length - 1) { _getFindListData(level: tabItem.level!, value: tabItem.value); } else { - int cruIndex=index>0?index-1:index; + int cruIndex = index > 0 ? index - 1 : index; _getFindListData(level: index, parentValue: _tabListData[cruIndex].value); } - _level=index; + _level = index; setState(() {}); } @@ -428,8 +443,7 @@ class _TDMultiCascaderState extends State with TickerProviderSt //判断下级是否存在 if (selectLevelData.isNotEmpty) { //获取下级数据 - var childList = - selectLevelData.where((element) => element.parentValue == value).toList(); + var childList = selectLevelData.where((element) => element.parentValue == value).toList(); _selectListData = childList; _currentTabIndex += 1; } else { @@ -457,7 +471,7 @@ class _TDMultiCascaderState extends State with TickerProviderSt } /// 定位选项在列表中位置 - void _scrollToListIndex(int index) async{ + void _scrollToListIndex(int index) async { // 计算列表中特定索引的位置 double scrollTo = index * 56.0; // 每个列表项的高度是56.0 _scrollListController.animateTo( @@ -545,5 +559,5 @@ class MultiCascaderListModel { String? segmentValue; int? level; - MultiCascaderListModel({this.label, this.value, this.parentValue, this.level,this.segmentValue}); + MultiCascaderListModel({this.label, this.value, this.parentValue, this.level, this.segmentValue}); }