QinGang_interested/lib/customWidget/department_person_picker.dart

232 lines
9.8 KiB
Dart
Raw Normal View History

2025-12-12 09:11:30 +08:00
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/search_bar_widget.dart';
/// 用户数据模型
class Person {
final String userId;
final String name;
final String departmentName;
final String phone;
Person({required this.userId, required this.name, required this.departmentName, required this.phone});
factory Person.fromJson(Map json) {
return Person(
userId: json['id'] ?? '' ,
name: json['username'] ?? '',
departmentName: json['departmentName'] ?? '',
phone: json['phone'] ?? '',
);
}
}
/// 原回调签名(向后兼容)
typedef PersonSelectCallback = void Function(String userId, String name);
/// 新回调签名,增加可选 indexint默认 0
typedef PersonSelectCallbackWithIndex = void Function(String userId, String name, int index);
/// 新增:带 data 的回调(不会替换原有 typedef向后兼容
typedef PersonSelectCallbackWithData = void Function(String userId, String name, Map<String, dynamic> data);
/// 新增:带 index 和 data 的回调
typedef PersonSelectCallbackWithIndexAndData = void Function(String userId, String name, int index, Map<String, dynamic> data);
/// 底部弹窗人员选择器(使用预先传入的原始数据列表,不做接口请求)
class DepartmentPersonPicker {
/// 显示人员选择弹窗
///
/// [personsData]: 已拉取并缓存的原始 Map 列表(每项最好是 Map
/// [onSelected]: 选中后回调 USER_ID 和 NAME向后兼容旧代码
/// [onSelectedWithIndex]: 可选的新回调,额外返回 indexindex 为在原始 personsData/_all 中的下标,找不到则为 0
/// [onSelectedWithData]: 可选回调,返回 userId/name + 选中项的完整原始 Map不影响旧回调
/// [onSelectedWithIndexWithData]: 可选回调,返回 userId/name/index + 选中项的完整原始 Map优先级最高
static Future<void> show(
BuildContext context, {
required List<dynamic> personsData,
PersonSelectCallback? onSelected,
PersonSelectCallbackWithIndex? onSelectedWithIndex,
PersonSelectCallbackWithData? onSelectedWithData,
PersonSelectCallbackWithIndexAndData? onSelectedWithIndexWithData,
}) async {
// 至少传入一个回调(保持对旧调用的兼容)
assert(
onSelected != null ||
onSelectedWithIndex != null ||
onSelectedWithData != null ||
onSelectedWithIndexWithData != null,
'请至少传入一个回调onSelected / onSelectedWithIndex / onSelectedWithData / onSelectedWithIndexWithData',
);
// 转换为模型personsData 可能包含非 Map 的条目)
final List<Person> _all = personsData.map((e) {
if (e is Map) {
return Person.fromJson(e);
} else {
// 非 map 情况按字符串处理
final s = e?.toString() ?? '';
return Person(userId: s, name: s, departmentName: '', phone: '');
}
}).toList();
List<Person> _filtered = List.from(_all);
String _selectedName = '';
String _selectedId = '';
final TextEditingController _searchController = TextEditingController();
await showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
),
builder: (ctx) {
return StatefulBuilder(
builder: (BuildContext ctx, StateSetter setState) {
// 搜索逻辑
void _onSearch(String v) {
final q = v.toLowerCase().trim();
setState(() {
if (q.isEmpty) {
_filtered = List.from(_all);
} else {
_filtered = _all.where((p) {
final nameLower = p.name.toLowerCase();
final phoneLower = p.phone.toString().toLowerCase();
return nameLower.contains(q) || phoneLower.contains(q);
}).toList();
}
});
}
// 根据选中的 userId 在原始 personsData 中找到对应的原始 Map若不存在则生成一个简单 Map
Map<String, dynamic> _findOriginalData(String userId) {
try {
final idx = personsData.indexWhere((raw) {
if (raw is Map) {
final id = raw['id']?.toString() ?? raw['userId']?.toString() ?? '';
return id == userId;
} else {
return raw?.toString() == userId;
}
});
if (idx >= 0) {
final raw = personsData[idx];
if (raw is Map) return Map<String, dynamic>.from(raw);
return {'id': userId, 'username': _selectedName};
} else {
// 找不到则返回一个最小信息 map
return {'id': userId, 'username': _selectedName};
}
} catch (e) {
return {'id': userId, 'username': _selectedName};
}
}
return SafeArea(
child: SizedBox(
height: MediaQuery.of(ctx).size.height * 0.75,
child: Column(
children: [
// 顶部:取消、搜索、确定
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
children: [
TextButton(
onPressed: () => Navigator.of(ctx).pop(),
child: const Text(
'取消',
style: TextStyle(fontSize: 16),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: SearchBarWidget(
controller: _searchController,
onTextChanged: _onSearch,
isShowSearchButton: false,
onSearch: (keyboard) {},
),
),
),
TextButton(
onPressed: _selectedId.isEmpty
? null
: () {
Navigator.of(ctx).pop();
// 计算 index在原始 _all 列表中的下标)
final idx = _all.indexWhere((p) => p.userId == _selectedId);
final validIndex = idx >= 0 ? idx : 0;
// 找到原始 dataMap
final dataMap = _findOriginalData(_selectedId);
// 优先调用带 index 和 data 的回调(最高优先)
if (onSelectedWithIndexWithData != null) {
onSelectedWithIndexWithData(_selectedId, _selectedName, validIndex, dataMap);
return;
}
// 然后是带 data 的回调(只返回 data不返回 index
if (onSelectedWithData != null) {
onSelectedWithData(_selectedId, _selectedName, dataMap);
return;
}
// 然后是带 index 的旧回调
if (onSelectedWithIndex != null) {
onSelectedWithIndex(_selectedId, _selectedName, validIndex);
return;
}
// 最后回退到最原始的回调(仅 userId + name
if (onSelected != null) {
onSelected(_selectedId, _selectedName);
return;
}
},
child: const Text(
'确定',
style: TextStyle(color: Colors.green, fontSize: 16),
),
),
],
),
),
const Divider(height: 1),
// 列表
Expanded(
child: ListView.separated(
itemCount: _filtered.length,
separatorBuilder: (_, __) => const Divider(height: 1),
itemBuilder: (context, index) {
final person = _filtered[index];
final selected = person.userId == _selectedId;
return ListTile(
titleAlignment: ListTileTitleAlignment.center,
title: Text('${person.name}-${person.phone}${person.departmentName}'),
trailing: selected ? const Icon(Icons.check, color: Colors.green) : null,
onTap: () => setState(() {
_selectedId = person.userId;
_selectedName = person.name;
}),
);
},
),
),
],
),
),
);
},
);
},
);
}
}