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

262 lines
7.9 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 仍然尝试解析(兼容旧结构),但在扁平模式下不会使用
final rawChildren = json['children'];
List<Category> childrenList = [];
if (rawChildren is List) {
childrenList = rawChildren
.where((e) => e != null)
.map((e) {
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 = '';
List<Category> original = [];
List<Category> 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<void> _loadData() async {
try {
final result = await ApiService.getWorkAreaList();
List<dynamic> raw = result['varList'] ?? [];
// 解析为 Category 列表(可能包含 children
final parsed = 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();
// 扁平化:父与子都放到同一列表中(以兼容后端可能还是两层结构)
final List<Category> flat = [];
for (final c in parsed) {
flat.add(c);
if (c.children.isNotEmpty) {
flat.addAll(c.children);
}
}
// 规范化 id如果后端 ID 为空,则为该项赋予一个独一无二的占位 id
// 避免与默认 selectedId = '' 冲突导致“全部被选中”的问题。
final List<Category> 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<Category> _filterCategories(List<Category> 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]),
),
)),
),
],
),
);
}
}