import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:qhd_prevention/customWidget/search_bar_widget.dart'; import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/services/SessionService.dart'; import '../tools/tools.dart'; // 包含 SessionService // 数据模型 class Category { final String id; final String name; final Map extValues; final String departmentId; final String parentId; final List childrenList; Category({ required this.id, required this.name, required this.childrenList, required this.extValues, required this.departmentId, required this.parentId, }); factory Category.fromJson(Map json) { // 安全读取并兼容字符串或数字类型的 id String parseString(dynamic v) { if (v == null) return ''; if (v is String) return v; return v.toString(); } final rawChildren = json['childrenList']; List children = []; if (rawChildren is List) { try { children = rawChildren .where((e) => e != null) .map((e) => Category.fromJson(Map.from(e as Map))) .toList(); } catch (e) { // 如果内部解析出错,保持 children 为空并继续 children = []; } } // extValues 有可能为 null 或不是 Map final extRaw = json['extValues']; Map extMap = {}; if (extRaw is Map) { extMap = Map.from(extRaw); } return Category( id: parseString(json['id']), name: parseString(json['name']), childrenList: children, extValues: extMap, departmentId: parseString(json['departmentId']), parentId: parseString(json['parentId']), ); } } /// 弹窗回调签名:返回选中项的 id 和 name typedef DeptSelectCallback = void Function(String id, String name); class DepartmentPicker extends StatefulWidget { /// 回调,返回选中部门 id 与 name final DeptSelectCallback onSelected; const DepartmentPicker({Key? key, required this.onSelected}) : super(key: key); @override _DepartmentPickerState createState() => _DepartmentPickerState(); } class _DepartmentPickerState extends State { String selectedId = ''; String selectedName = ''; Set expandedSet = {}; List original = []; List filtered = []; bool loading = true; final TextEditingController _searchController = TextEditingController(); @override void initState() { super.initState(); // 初始均为空 selectedId = ''; selectedName = ''; expandedSet = {}; _searchController.addListener(_onSearchChanged); _loadData(); } @override void dispose() { _searchController.removeListener(_onSearchChanged); _searchController.dispose(); super.dispose(); } Future _loadData() async { try { final result = await BasicInfoApi.getDeptTree({}); final raw = result['data'] as List; print(raw); setState(() { original = raw.map((e) => Category.fromJson(e as Map)).toList(); filtered = original; loading = false; }); } catch (e) { setState(() => loading = false); } } void _onSearchChanged() { final query = _searchController.text.toLowerCase().trim(); setState(() { filtered = query.isEmpty ? original : _filterCategories(original, query); }); } List _filterCategories(List list, String query) { List result = []; for (var cat in list) { final children = _filterCategories(cat.childrenList, query); if (cat.name.toLowerCase().contains(query) || children.isNotEmpty) { result.add( Category( id: cat.id, name: cat.name, childrenList: children, extValues: cat.extValues, departmentId: cat.departmentId, parentId: cat.parentId, ), ); } } return result; } Widget _buildRow(Category cat, int indent) { final hasChildren = cat.childrenList.isNotEmpty; final isExpanded = expandedSet.contains(cat.id); final isSelected = cat.id == selectedId; return Column( children: [ InkWell( onTap: () { setState(() { if (hasChildren) { isExpanded ? expandedSet.remove(cat.id) : expandedSet.add(cat.id); } selectedId = cat.id; selectedName = cat.name; }); }, child: Container( color: Colors.white, child: Row( children: [ SizedBox(width: 16.0 * indent), SizedBox( width: 24, child: hasChildren ? Icon( isExpanded ? Icons.arrow_drop_down_rounded : Icons.arrow_right_rounded, size: 35, color: Colors.grey[600], ) : const SizedBox.shrink(), ), const SizedBox(width: 5), Expanded( child: Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: Text(cat.name), ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Icon( isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked, color: Colors.blue, ), ), ], ), ), ), if (hasChildren && isExpanded) ...cat.childrenList.map((c) => _buildRow(c, indent + 1)), ], ); } @override Widget build(BuildContext context) { return Container( width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height * 0.7, color: Colors.white, child: Column( children: [ Container( color: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Row( children: [ GestureDetector( onTap: () => Navigator.of(context).pop(), child: const Text('取消', style: TextStyle(fontSize: 16)), ), Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12), child: SearchBarWidget( controller: _searchController, isShowSearchButton: false, onSearch: (keyboard) {}, ), ), ), GestureDetector( onTap: () { Navigator.of(context).pop(); widget.onSelected(selectedId, selectedName); }, child: const Text( '确定', style: TextStyle(fontSize: 16, color: Colors.blue), ), ), ], ), ), Divider(), Expanded( child: loading ? const Center(child: CircularProgressIndicator()) : Container( color: Colors.white, child: ListView.builder( itemCount: filtered.length, itemBuilder: (ctx, idx) => _buildRow(filtered[idx], 0), ), ), ), ], ), ); } }