135 lines
5.0 KiB
Dart
135 lines
5.0 KiB
Dart
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;
|
|
|
|
Person({required this.userId, required this.name});
|
|
|
|
factory Person.fromJson(Map<String, dynamic> json) {
|
|
return Person(
|
|
userId: json['USER_ID'] as String,
|
|
name: json['NAME'] as String,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 回调签名,返回选中用户的 USER_ID 和 NAME
|
|
typedef PersonSelectCallback = void Function(String userId, String name);
|
|
|
|
/// 底部弹窗人员选择器(使用预先传入的原始数据列表,不做接口请求)
|
|
class DepartmentPersonPicker {
|
|
/// 显示人员选择弹窗
|
|
///
|
|
/// [personsData]: 已拉取并缓存的原始 Map 列表
|
|
/// [onSelected]: 选中后回调 USER_ID 和 NAME
|
|
static Future<void> show(
|
|
BuildContext context, {
|
|
required List<Map<String, dynamic>> personsData,
|
|
required PersonSelectCallback onSelected,
|
|
}) async {
|
|
// 转换为模型
|
|
final List<Person> _all =
|
|
personsData.map((e) => Person.fromJson(e)).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(() {
|
|
_filtered = q.isEmpty
|
|
? List.from(_all)
|
|
: _all
|
|
.where((p) => p.name.toLowerCase().contains(q))
|
|
.toList();
|
|
});
|
|
}
|
|
|
|
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();
|
|
onSelected(_selectedId, _selectedName);
|
|
},
|
|
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),
|
|
trailing:
|
|
selected ? const Icon(Icons.check, color: Colors.green) : null,
|
|
onTap: () => setState(() {
|
|
_selectedId = person.userId;
|
|
_selectedName = person.name;
|
|
}),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|