QinGang_interested/lib/pages/home/doorAndCar/person_selection_page.dart

496 lines
14 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import 'package:flutter/material.dart';
import 'package:lpinyin/lpinyin.dart';
import 'package:qhd_prevention/customWidget/search_bar_widget.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
// 人员模型类
class Person {
final String id;
final String name;
final String group;
Person({required this.id, required this.name, required this.group});
}
// 选择结果类
class SelectionPersonResult {
final List<Person> selectedPersons;
final Map<String, List<Person>> groupedSelected;
SelectionPersonResult({
required this.selectedPersons,
required this.groupedSelected,
});
}
class PersonSelectionPage extends StatefulWidget {
const PersonSelectionPage(this.oldList, {super.key});
final List<Person> oldList;
@override
State<PersonSelectionPage> createState() => _PersonSelectionPageState();
}
class _PersonSelectionPageState extends State<PersonSelectionPage> {
// 字母表(将#放在最后)
final List<String> alphabet = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '#'
];
// 人员数据
late final Map<String, List<Person>> groupedPersons;
// 存储排序后的分组键列表
late final List<String> sortedGroupKeys;
// 存储每个分组的GlobalKey
final Map<String, GlobalKey> groupKeys = {};
final List<Map<String, dynamic>> list = [
{
'id': 'p1',
'name': '',
},
{
'id': 'p2',
'name': '',
},
{
'id': 'p3',
'name': '',
},
{
'id': 'p4',
'name': '',
},
{
'id': 'p5',
'name': '',
},
{
'id': 'p6',
'name': '',
},
{
'id': 'p7',
'name': '',
},
{
'id': 'p8',
'name': '',
},
{
'id': 'p9',
'name': '',
},
{
'id': 'p10',
'name': '饿',
},
{
'id': 'p11',
'name': '',
},
{
'id': 'p12',
'name': '',
},
{
'id': 'p13',
'name': '123', // 测试数字
},
{
'id': 'p14',
'name': '@@@', // 测试特殊字符
},
];
// 选中状态
final Map<String, bool> selectedStates = {};
// 分组选中状态
final Map<String, bool> groupSelectedStates = {};
// 列表控制器
final ScrollController _scrollController = ScrollController();
final TextEditingController _searchController = TextEditingController();
// 存储每个分组在列表中的位置索引
late final Map<String, int> _groupIndexMap = {};
// 创建oldList的ID集合用于快速查找
late final Set<String> _oldSelectedIds;
@override
void initState() {
super.initState();
// 创建oldList的ID集合
_oldSelectedIds = widget.oldList.map((person) => person.id).toSet();
_initData();
}
void _initData() {
// 根据姓名首字母分组
groupedPersons = {};
for (var item in list) {
String name = item['name'];
String id = item['id'];
// 使用lpinyin获取姓名首字母
String firstLetter = _getFirstLetter(name);
// 创建Person对象
Person person = Person(
id: id,
name: name,
group: firstLetter,
);
// 添加到对应分组
if (!groupedPersons.containsKey(firstLetter)) {
groupedPersons[firstLetter] = [];
}
groupedPersons[firstLetter]!.add(person);
}
// 对每个分组内的人员按姓名排序
groupedPersons.forEach((key, value) {
value.sort((a, b) => a.name.compareTo(b.name));
});
// 对分组键进行排序(确保#在最后)
sortedGroupKeys = groupedPersons.keys.toList()..sort((a, b) {
// 如果a是#a应该在后面
if (a == '#') return 1;
// 如果b是#b应该在后面
if (b == '#') return -1;
// 其他情况正常排序
return a.compareTo(b);
});
// 构建分组索引映射
for (int i = 0; i < sortedGroupKeys.length; i++) {
_groupIndexMap[sortedGroupKeys[i]] = i;
}
// 初始化选中状态和为每个分组创建GlobalKey
for (var group in groupedPersons.keys) {
for (var person in groupedPersons[group]!) {
// 检查当前人员是否在oldList中如果在则设置为true
selectedStates[person.id] = _oldSelectedIds.contains(person.id);
}
// 初始化分组选中状态
groupSelectedStates[group] = false;
groupKeys[group] = GlobalKey();
}
// 更新每个分组的全选状态
for (var group in groupedPersons.keys) {
_updateGroupSelection(group);
}
}
// 使用lpinyin获取姓名的首字母
String _getFirstLetter(String name) {
if (name.isEmpty) return '#';
try {
// 检查第一个字符是否是英文字母
String firstChar = name.substring(0, 1);
RegExp englishLetter = RegExp(r'^[A-Za-z]$');
if (englishLetter.hasMatch(firstChar)) {
return firstChar.toUpperCase();
}
// 检查第一个字符是否是数字或特殊字符
RegExp nonChinese = RegExp(r'^[0-9!@#\$%^&*()_+\-=\[\]{};:"\\|,.<>\/?~`]');
if (nonChinese.hasMatch(firstChar)) {
return '#';
}
// 获取拼音 - 使用WITHOUT_TONE格式获取完整拼音
String pinyin = PinyinHelper.getPinyin(name,
separator: ' ', format: PinyinFormat.WITHOUT_TONE);
if (pinyin.isNotEmpty) {
// 获取第一个字的拼音首字母
List<String> pinyinParts = pinyin.split(' ');
if (pinyinParts.isNotEmpty) {
String firstCharPinyin = pinyinParts[0];
if (firstCharPinyin.isNotEmpty) {
String firstLetter = firstCharPinyin.substring(0, 1).toUpperCase();
// 检查是否是英文字母
if (englishLetter.hasMatch(firstLetter)) {
return firstLetter;
}
}
}
}
} catch (e) {
// 异常处理,返回#
print('拼音转换错误: $e');
}
// 如果无法识别,返回#
return '#';
}
// 滚动到指定分组
void _scrollToGroup(String letter) {
if (!groupedPersons.containsKey(letter)) return;
// 方法1使用索引跳转更精确
int? index = _groupIndexMap[letter];
if (index != null && _scrollController.hasClients) {
// 计算大概的滚动位置(每个分组标题高度 + 每个人item高度
double position = 0;
for (int i = 0; i < index; i++) {
String group = sortedGroupKeys[i];
// 分组标题高度56每个人item高度56
position += 56; // 分组标题高度
position += (groupedPersons[group]?.length ?? 0) * 56; // 人员列表高度
}
_scrollController.animateTo(
position,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
return;
}
// 方法2使用GlobalKey备选方案
final key = groupKeys[letter];
if (key?.currentContext != null) {
Scrollable.ensureVisible(
key!.currentContext!,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
alignment: 0, // 滚动到顶部
);
}
}
// 更新分组的全选状态
void _updateGroupSelection(String group) {
final groupPersons = groupedPersons[group]!;
final allSelected = groupPersons.every((person) => selectedStates[person.id] == true);
final anySelected = groupPersons.any((person) => selectedStates[person.id] == true);
setState(() {
groupSelectedStates[group] = allSelected;
});
}
// 切换分组的全选/全不选
void _toggleGroupSelection(String group) {
final newState = !(groupSelectedStates[group] ?? false);
final groupPersons = groupedPersons[group]!;
setState(() {
groupSelectedStates[group] = newState;
for (var person in groupPersons) {
selectedStates[person.id] = newState;
}
});
}
// 获取选中的人员
SelectionPersonResult _getSelectedResult() {
List<Person> selectedPersons = [];
Map<String, List<Person>> groupedSelected = {};
for (var group in groupedPersons.keys) {
final groupPersons = groupedPersons[group]!;
final selectedInGroup = groupPersons
.where((person) => selectedStates[person.id] == true)
.toList();
if (selectedInGroup.isNotEmpty) {
groupedSelected[group] = selectedInGroup;
selectedPersons.addAll(selectedInGroup);
}
}
return SelectionPersonResult(
selectedPersons: selectedPersons,
groupedSelected: groupedSelected,
);
}
// 确认添加并返回
void _confirmSelection() {
final result = _getSelectedResult();
Navigator.pop(context, result);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: MyAppbar(title: '选择人员', actions: [
TextButton(
onPressed: () {
_confirmSelection();
},
child: Text(
"确认添加",
style: TextStyle(color: Colors.white, fontSize: 16),
),
),
],),
body: Column(
children: [
Container(
color: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 8),
child: SearchBarWidget(
showResetButton: true,
hintText: '请输入关键字',
resetButtonText: '重置',
onReset: () {
FocusScope.of(context).unfocus();
_searchController.text = '';
_search();
},
onSearch: (text) {
FocusScope.of(context).unfocus();
_search();
},
controller: _searchController,
),
),
// 人员列表
Expanded(
child: Row(
children: [
// 左侧人员列表
Expanded(
child: ListView.builder(
controller: _scrollController,
itemCount: sortedGroupKeys.length,
itemBuilder: (context, index) {
String group = sortedGroupKeys[index];
List<Person> persons = groupedPersons[group]!;
return _buildGroupSection(group, persons);
},
),
),
// 右侧字母导航
Container(
width: 30,
color: Colors.grey.shade50,
child: ListView.builder(
itemCount: alphabet.length,
itemBuilder: (context, index) {
final letter = alphabet[index];
final hasGroup = groupedPersons.containsKey(letter);
return GestureDetector(
onTap: hasGroup ? () => _scrollToGroup(letter) : null,
child: Container(
height: 30,
alignment: Alignment.center,
child: Text(
letter,
style: TextStyle(
fontSize: 12,
color: hasGroup
? Colors.blue
: Colors.grey.shade400,
fontWeight: hasGroup
? FontWeight.bold
: FontWeight.normal,
),
),
),
);
},
),
),
],
),
),
],
),
);
}
// 构建分组区域
Widget _buildGroupSection(String group, List<Person> persons) {
return Container(
key: groupKeys[group],
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 分组标题
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
color: Colors.grey.shade100,
height: 56, // 固定高度便于计算
child: Row(
children: [
Text(
group,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const Spacer(),
],
),
),
// 人员列表
...persons.map((person) => _buildPersonItem(person)),
],
),
);
}
// 构建人员项
Widget _buildPersonItem(Person person) {
return Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.grey.shade200,
width: 0.5,
),
),
),
height: 56, // 固定高度便于计算
child: CheckboxListTile(
title: Text(
person.name,
style: const TextStyle(fontSize: 14),
),
value: selectedStates[person.id] ?? false,
onChanged: (bool? value) {
setState(() {
selectedStates[person.id] = value ?? false;
_updateGroupSelection(person.group);
});
},
activeColor: Colors.blue,
controlAffinity: ListTileControlAffinity.leading,
contentPadding: const EdgeInsets.symmetric(horizontal: 0),
),
);
}
void _search() {
// searchKeywords = _searchController.text.trim();
// currentPage = 1;
// list.clear();
// _fetchData();
}
}