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

538 lines
16 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/customWidget/toast_util.dart';
import 'package:qhd_prevention/http/modules/doorAndCar_api.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
// 人员模型类
class Person {
final String employeePersonUserId;
final String employeePersonUserName;
final String group;
final String personCorpId;
final String personCorpName;
final String personDepartmentId;
final String personDepartmentName;
final String userFaceUrl;
final String userPhone;
final String userCard;
Person({required this.employeePersonUserId, required this.employeePersonUserName, required this.group,
required this.personCorpId, required this.personCorpName, required this.personDepartmentId,
required this.personDepartmentName, required this.userFaceUrl, required this.userPhone,
required this.userCard, });
// 添加 toJson 方法
Map<String, dynamic> toJson() {
return {
'employeePersonUserId': employeePersonUserId,
'employeePersonUserName': employeePersonUserName,
'group': group,
'personCorpId': personCorpId,
'personCorpName': personCorpName,
'personDepartmentId': personDepartmentId,
'personDepartmentName': personDepartmentName,
'userFaceUrl': userFaceUrl,
'userPhone': userPhone,
'userCard': userCard,
};
}
}
// 选择结果类
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,this.id, {super.key,this.isMoreSelect=true,});
final List<Person> oldList;
final String id;
final bool isMoreSelect;//是否多选
@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 = {};
List< dynamic> list =[];
late Future<void> _dataFuture;
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.employeePersonUserId).toSet();
_dataFuture = _initData();
}
Future<void> _initData() async {
await _getProJectUserList(widget.id);
// 根据姓名首字母分组
groupedPersons = {};
for (var item in list) {
String id = item['userId']??'';
String name = item['userName']??'';
// 使用lpinyin获取姓名首字母
String firstLetter = _getFirstLetter(name);
// 创建Person对象
Person person = Person(
employeePersonUserId: id,
employeePersonUserName: name,
group: firstLetter,
personCorpId: item['deptId']??'',
personCorpName: item['deptName']??'',
personDepartmentId: item['deptId']??'',
personDepartmentName: item['deptName']??'',
userFaceUrl: item['deptName']??'',
userPhone: item['phone']??'',
userCard: item['deptName']??'',
);
// 添加到对应分组
if (!groupedPersons.containsKey(firstLetter)) {
groupedPersons[firstLetter] = [];
}
groupedPersons[firstLetter]!.add(person);
}
// 对每个分组内的人员按姓名排序
groupedPersons.forEach((key, value) {
value.sort((a, b) => a.employeePersonUserName.compareTo(b.employeePersonUserName));
});
// 对分组键进行排序(确保#在最后)
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.employeePersonUserId] = _oldSelectedIds.contains(person.employeePersonUserId);
}
// 初始化分组选中状态
groupSelectedStates[group] = false;
groupKeys[group] = GlobalKey();
}
// 更新每个分组的全选状态
for (var group in groupedPersons.keys) {
_updateGroupSelection(group);
}
}
Future<void> _getProJectUserList(String id) async {
try {
final Map<String, dynamic> result = await DoorAndCarApi.getPeopleinProject(id);
if (result['success'] ) {
setState(() {
list = result['data'];
});
}else{
ToastUtil.showNormal(context, '加载数据失败');
// _showMessage('加载数据失败');
}
} catch (e) {
print('Error fetching data: $e');
}
}
// 使用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.employeePersonUserId] == true);
final anySelected = groupPersons.any((person) => selectedStates[person.employeePersonUserId] == 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.employeePersonUserId] = 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.employeePersonUserId] == true)
.toList();
if (selectedInGroup.isNotEmpty) {
groupedSelected[group] = selectedInGroup;
selectedPersons.addAll(selectedInGroup);
}
}
return SelectionPersonResult(
selectedPersons: selectedPersons,
groupedSelected: groupedSelected,
);
}
// 确认添加并返回
void _confirmSelection() {
final result = _getSelectedResult();
// ,如果没有选择人员,提示用户
if (result.selectedPersons.isEmpty) {
ToastUtil.showNormal(context, '请选择一个人');
return;
}
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:
FutureBuilder(
future: _dataFuture,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return const Center(child: CircularProgressIndicator());
}
return 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.employeePersonUserName,
style: const TextStyle(fontSize: 14),
),
value: selectedStates[person.employeePersonUserId] ?? false,
onChanged: (bool? value) {
setState(() {
if(widget.isMoreSelect){
selectedStates[person.employeePersonUserId] = value ?? false;
_updateGroupSelection(person.group);
}else{
// 清空所有选中状态
for (var group in groupedPersons.keys) {
for (var p in groupedPersons[group]!) {
selectedStates[p.employeePersonUserId] = false;
}
}
// 设置当前选中
selectedStates[person.employeePersonUserId] = true;
}
});
},
activeColor: Colors.blue,
controlAffinity: ListTileControlAffinity.leading,
contentPadding: const EdgeInsets.symmetric(horizontal: 0),
),
);
}
void _search() {
// searchKeywords = _searchController.text.trim();
// currentPage = 1;
// list.clear();
// _fetchData();
}
}