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/tools/tools.dart'; // ----- Models ----- class Category { final String ELECTRONIC_FENCE_AREA_ID; final String POSITIONS; final String name; final List children; Category({ required this.ELECTRONIC_FENCE_AREA_ID, required this.name, this.POSITIONS = '', this.children = const [], }); factory Category.fromJson(Map json) { // 保护式读取,避免 null 转 List、String 时抛异常 final id = (json['ELECTRONIC_FENCE_AREA_ID'] ?? '').toString(); final name = (json['name'] ?? '').toString(); final positions = (json['POSITIONS'] ?? '').toString(); // children 仍然尝试解析(兼容旧结构),但在扁平模式下不会使用 final rawChildren = json['children']; List childrenList = []; if (rawChildren is List) { childrenList = rawChildren .where((e) => e != null) .map((e) { if (e is Map) { return Category.fromJson(e); } else if (e is Map) { return Category.fromJson(Map.from(e)); } else { return null; } }) .whereType() .toList(); } return Category( ELECTRONIC_FENCE_AREA_ID: id, name: name, POSITIONS: positions, children: childrenList, ); } } typedef DeptSelectCallback = void Function(String id, String POSITIONS, String name); class WorkAreaPicker extends StatefulWidget { final DeptSelectCallback onSelected; const WorkAreaPicker({Key? key, required this.onSelected}) : super(key: key); @override _WorkAreaPickerState createState() => _WorkAreaPickerState(); } class _WorkAreaPickerState extends State { String selectedId = ''; String selectedName = ''; String selected_POSITIONS = ''; List original = []; List filtered = []; bool loading = true; final TextEditingController _searchController = TextEditingController(); @override void initState() { super.initState(); selectedId = ''; selectedName = ''; selected_POSITIONS = ''; _searchController.addListener(_onSearchChanged); _loadData(); } @override void dispose() { _searchController.removeListener(_onSearchChanged); _searchController.dispose(); super.dispose(); } Future _loadData() async { try { final result = await ApiService.getWorkAreaList(); List raw = result['varList'] ?? []; // 解析为 Category 列表(可能包含 children) final parsed = raw .map((e) { if (e is Map) return Category.fromJson(e); if (e is Map) return Category.fromJson(Map.from(e)); return null; }) .whereType() .toList(); // 扁平化:父与子都放到同一列表中(以兼容后端可能还是两层结构) final List flat = []; for (final c in parsed) { flat.add(c); if (c.children.isNotEmpty) { flat.addAll(c.children); } } // 规范化 id:如果后端 ID 为空,则为该项赋予一个独一无二的占位 id, // 避免与默认 selectedId = '' 冲突导致“全部被选中”的问题。 final List normalized = []; for (var i = 0; i < flat.length; i++) { final c = flat[i]; final rawId = (c.ELECTRONIC_FENCE_AREA_ID ?? '').toString().trim(); if (rawId.isEmpty) { // 生成占位 id(基于索引和 name hash,足够唯一且不会是空字符串) final generatedId = '__generated_${i}_${c.name.hashCode}'; normalized.add(Category( ELECTRONIC_FENCE_AREA_ID: generatedId, name: c.name, POSITIONS: c.POSITIONS, children: const [], )); } else { // 保持原 id normalized.add(Category( ELECTRONIC_FENCE_AREA_ID: rawId, name: c.name, POSITIONS: c.POSITIONS, children: const [], )); } } setState(() { original = normalized; filtered = original; loading = false; }); } catch (e, st) { debugPrint('WorkAreaPicker._loadData error: $e\n$st'); setState(() => loading = false); } } void _onSearchChanged() { final query = _searchController.text.toLowerCase().trim(); setState(() { filtered = query.isEmpty ? original : _filterCategories(original, query); }); } // 扁平过滤:在 name 或 POSITIONS 中搜索 List _filterCategories(List list, String query) { if (query.isEmpty) return list; final q = query.toLowerCase(); return list.where((c) { final name = (c.name ?? '').toLowerCase(); final pos = (c.POSITIONS ?? '').toLowerCase(); return name.contains(q) || pos.contains(q); }).toList(); } Widget _buildRow(Category cat) { final isSelected = selectedId == cat.ELECTRONIC_FENCE_AREA_ID; return InkWell( onTap: () { setState(() { selectedId = cat.ELECTRONIC_FENCE_AREA_ID; selectedName = cat.name; selected_POSITIONS = cat.POSITIONS; }); }, child: Container( color: Colors.white, padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12), child: Row( children: [ Expanded(child: Text(cat.name)), Icon( isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked, color: isSelected ? Colors.green : Colors.grey, ), ], ), ), ); } @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, selected_POSITIONS, selectedName, ); }, child: const Text( '确定', style: TextStyle(fontSize: 16, color: Colors.green), ), ), ], ), ), const Divider(height: 1), Expanded( child: loading ? const Center(child: CircularProgressIndicator()) : (filtered.isEmpty ? const Center(child: Text('没有找到匹配的工作区域')) : Container( color: Colors.white, child: ListView.builder( itemCount: filtered.length, itemBuilder: (ctx, idx) => _buildRow(filtered[idx]), ), )), ), ], ), ); } }