QinGang_interested/lib/customWidget/department_person_picker.dart

232 lines
9.8 KiB
Dart
Raw Permalink 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/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;
}),
);
},
),
),
],
),
),
);
},
);
},
);
}
}