flutter_integrated_whb/lib/pages/home/tap/workArea_picker.dart

288 lines
8.6 KiB
Dart
Raw 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 '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<Category> children;
Category({
required this.ELECTRONIC_FENCE_AREA_ID,
required this.name,
this.POSITIONS = '',
this.children = const [],
});
factory Category.fromJson(Map<String, dynamic> json) {
// 保护式读取,避免 null 转 List、String 时抛异常
final id = (json['ELECTRONIC_FENCE_AREA_ID'] ?? '').toString();
final name = (json['name'] ?? '').toString();
final positions = (json['POSITIONS'] ?? '').toString();
// children 可能不存在、为 null、或已经是 List
final rawChildren = json['children'];
List<Category> childrenList = [];
if (rawChildren is List) {
childrenList = rawChildren
.where((e) => e != null)
.map((e) {
// e 可能已经是 Map<String,dynamic> 或 dynamic
if (e is Map<String, dynamic>) {
return Category.fromJson(e);
} else if (e is Map) {
return Category.fromJson(Map<String, dynamic>.from(e));
} else {
// 无法解析的项跳过,或返回空占位
return null;
}
})
.whereType<Category>()
.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<WorkAreaPicker> {
String selectedId = '';
String selectedName = '';
String selected_POSITIONS = '';
Set<String> expandedSet = {};
List<Category> original = [];
List<Category> filtered = [];
bool loading = true;
final TextEditingController _searchController = TextEditingController();
@override
void initState() {
super.initState();
// 初始均为空
selectedId = '';
selectedName = '';
selected_POSITIONS = '';
expandedSet = {};
_searchController.addListener(_onSearchChanged);
_loadData();
}
@override
void dispose() {
_searchController.removeListener(_onSearchChanged);
_searchController.dispose();
super.dispose();
}
Future<void> _loadData() async {
try {
final result = await ApiService.getWorkAreaList();
final dynamic nodesField = result['zTreeNodes'];
// nodesField 可能是已经解析过的 List 或者是 StringJSON 字符串)
List<dynamic> raw;
if (nodesField is String) {
raw = json.decode(nodesField) as List<dynamic>;
} else if (nodesField is List) {
raw = nodesField;
} else {
raw = [];
}
// debug 打印(运行中可查看控制台)
// print('raw length = ${raw.length}');
setState(() {
original = raw
.map((e) {
if (e is Map<String, dynamic>) return Category.fromJson(e);
if (e is Map) return Category.fromJson(Map<String, dynamic>.from(e));
return null;
})
.whereType<Category>()
.toList();
filtered = original;
loading = false;
});
} catch (e, st) {
// 打印错误以便调试
// print('loadData error: $e\n$st');
setState(() => loading = false);
}
}
void _onSearchChanged() {
final query = _searchController.text.toLowerCase().trim();
setState(() {
filtered = query.isEmpty ? original : _filterCategories(original, query);
});
}
List<Category> _filterCategories(List<Category> list, String query) {
List<Category> result = [];
for (var cat in list) {
final children = _filterCategories(cat.children, query);
if (cat.name.toLowerCase().contains(query) || children.isNotEmpty) {
result.add(
Category(
ELECTRONIC_FENCE_AREA_ID: cat.ELECTRONIC_FENCE_AREA_ID,
name: cat.name,
children: children,
),
);
}
}
return result;
}
Widget _buildRow(Category cat, int indent) {
final hasChildren = cat.children.isNotEmpty;
final isExpanded = expandedSet.contains(cat.ELECTRONIC_FENCE_AREA_ID);
final isSelected = cat.ELECTRONIC_FENCE_AREA_ID == selectedId;
return Column(
children: [
InkWell(
onTap: () {
setState(() {
if (hasChildren) {
isExpanded
? expandedSet.remove(cat.ELECTRONIC_FENCE_AREA_ID)
: expandedSet.add(cat.ELECTRONIC_FENCE_AREA_ID);
}
selectedId = cat.ELECTRONIC_FENCE_AREA_ID;
selectedName = cat.name;
selected_POSITIONS = cat.POSITIONS;
});
},
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.green,
),
),
],
),
),
),
if (hasChildren && isExpanded)
...cat.children.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,
selected_POSITIONS,
selectedName,
);
},
child: const Text(
'确定',
style: TextStyle(fontSize: 16, color: Colors.green),
),
),
],
),
),
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),
),
),
),
],
),
);
}
}