batch_culling_page.dart 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. import 'package:chicken_farm/apis/index.dart';
  2. import 'package:chicken_farm/components/vb_rfid_field.dart';
  3. import 'package:chicken_farm/modes/rfid/rfid_model.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:chicken_farm/components/vb_app_bar.dart';
  6. import 'package:chicken_farm/components/vb_dict_select.dart';
  7. import 'package:chicken_farm/core/utils/toast.dart';
  8. class BatchCullingPage extends StatefulWidget {
  9. const BatchCullingPage({super.key});
  10. @override
  11. State<BatchCullingPage> createState() => _BatchCullingPageState();
  12. }
  13. class _BatchCullingPageState extends State<BatchCullingPage> {
  14. final List<String> _rfids = [];
  15. String? _cullReason;
  16. String? _disposalMethod;
  17. @override
  18. Widget build(BuildContext context) {
  19. return Scaffold(
  20. appBar: const VberAppBar(title: '批量淘汰', showLeftButton: true),
  21. body: Padding(
  22. padding: const EdgeInsets.all(16.0),
  23. child: Column(
  24. crossAxisAlignment: CrossAxisAlignment.start,
  25. children: [
  26. // 电子编号区域
  27. _buildRfidSection(),
  28. const SizedBox(height: 20),
  29. // 淘汰原因
  30. _buildCullReasonSection(),
  31. const SizedBox(height: 20),
  32. // 处置方式
  33. _buildDisposalMethodSection(),
  34. const SizedBox(height: 20),
  35. // 提交按钮
  36. SizedBox(
  37. width: double.infinity,
  38. child: ElevatedButton(
  39. onPressed:
  40. _rfids.isNotEmpty &&
  41. _cullReason != null &&
  42. _disposalMethod != null
  43. ? _handleSubmit
  44. : null,
  45. style: ElevatedButton.styleFrom(
  46. backgroundColor:
  47. _rfids.isNotEmpty &&
  48. _cullReason != null &&
  49. _disposalMethod != null
  50. ? Colors.blue
  51. : Colors.grey,
  52. foregroundColor: Colors.white,
  53. ),
  54. child: const Text('提交'),
  55. ),
  56. ),
  57. const SizedBox(height: 20),
  58. // 已识别的电子编号列表
  59. if (_rfids.isNotEmpty) ...[
  60. Row(
  61. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  62. children: [
  63. const Text(
  64. '已识别的电子编号',
  65. style: TextStyle(fontWeight: FontWeight.bold),
  66. ),
  67. IconButton(
  68. icon: const Icon(Icons.clear, size: 18),
  69. onPressed: _clearRfids,
  70. tooltip: '清空编号',
  71. ),
  72. ],
  73. ),
  74. const SizedBox(height: 10),
  75. Expanded(
  76. child: Container(
  77. padding: const EdgeInsets.all(10),
  78. decoration: BoxDecoration(
  79. border: Border.all(color: Colors.grey),
  80. borderRadius: BorderRadius.circular(8),
  81. ),
  82. child: ListView.builder(
  83. padding: EdgeInsets.zero,
  84. itemCount: _rfids.length,
  85. itemBuilder: (context, index) {
  86. return ListTile(
  87. visualDensity: VisualDensity.compact,
  88. contentPadding: const EdgeInsets.symmetric(
  89. horizontal: 2,
  90. vertical: 0,
  91. ),
  92. title: Text(_rfids[index]),
  93. trailing: IconButton(
  94. icon: const Icon(
  95. Icons.delete,
  96. size: 18,
  97. color: Colors.red,
  98. ),
  99. onPressed: () => _removeRfid(index),
  100. ),
  101. );
  102. },
  103. ),
  104. ),
  105. ),
  106. ],
  107. const SizedBox(height: 20),
  108. ],
  109. ),
  110. ),
  111. );
  112. }
  113. Widget _buildRfidSection() {
  114. return VberRfidField(
  115. rfids: _rfids,
  116. onRfidsScanned: (List<RfidModel> scannedRfids) {
  117. // 过滤出未存在的RFID
  118. final newRfids = scannedRfids
  119. .where((rfid) => !_rfids.contains(rfid.uid))
  120. .toList();
  121. if (newRfids.isNotEmpty) {
  122. setState(() {
  123. // 将新的RFID添加到列表中
  124. for (var rfid in newRfids) {
  125. _rfids.insert(0, rfid.uid);
  126. }
  127. });
  128. ToastUtil.success("新增 ${newRfids.length} 枚电子编号");
  129. } else {
  130. // 所有RFID都已存在
  131. ToastUtil.info("电子编号已存在");
  132. }
  133. },
  134. multiple: true,
  135. label: '电子编号',
  136. multiplePlaceholder: '未识别',
  137. multipleFormat: '已识别 %d 枚电子编号',
  138. );
  139. }
  140. Widget _buildCullReasonSection() {
  141. return Column(
  142. crossAxisAlignment: CrossAxisAlignment.start,
  143. children: [
  144. const Text('淘汰原因', style: TextStyle(fontWeight: FontWeight.bold)),
  145. const SizedBox(height: 10),
  146. Container(
  147. padding: const EdgeInsets.fromLTRB(16, 2, 16, 2),
  148. decoration: BoxDecoration(
  149. border: Border.all(color: Colors.grey),
  150. borderRadius: BorderRadius.circular(8),
  151. ),
  152. child: VberDictSelect(
  153. dictType: 'chicken_cull_reason',
  154. value: _cullReason,
  155. onChanged: (value) {
  156. setState(() {
  157. _cullReason = value;
  158. });
  159. },
  160. hint: '请选择淘汰原因',
  161. hideUnderline: true,
  162. ),
  163. ),
  164. ],
  165. );
  166. }
  167. Widget _buildDisposalMethodSection() {
  168. return Column(
  169. crossAxisAlignment: CrossAxisAlignment.start,
  170. children: [
  171. const Text('处置方式', style: TextStyle(fontWeight: FontWeight.bold)),
  172. const SizedBox(height: 10),
  173. Container(
  174. padding: const EdgeInsets.fromLTRB(16, 2, 16, 2),
  175. decoration: BoxDecoration(
  176. border: Border.all(color: Colors.grey),
  177. borderRadius: BorderRadius.circular(8),
  178. ),
  179. child: VberDictSelect(
  180. dictType: 'chicken_disposal_method',
  181. value: _disposalMethod,
  182. onChanged: (value) {
  183. setState(() {
  184. _disposalMethod = value;
  185. });
  186. },
  187. hint: '请选择处置方式',
  188. hideUnderline: true,
  189. ),
  190. ),
  191. ],
  192. );
  193. }
  194. // 移除指定索引的电子编号
  195. void _removeRfid(int index) {
  196. setState(() {
  197. _rfids.removeAt(index);
  198. });
  199. }
  200. // 清空所有已识别的电子编号
  201. void _clearRfids() {
  202. setState(() {
  203. _rfids.clear();
  204. });
  205. ToastUtil.info('已清空所有电子编号');
  206. }
  207. // 提交数据
  208. void _handleSubmit() {
  209. final data = {
  210. 'rfids': _rfids,
  211. 'disposal_method': _disposalMethod,
  212. 'cull_reason': _cullReason,
  213. };
  214. apis.breeding.submitApi
  215. .weight(data)
  216. .then((_) {
  217. if (mounted) {
  218. ScaffoldMessenger.of(context).showSnackBar(
  219. const SnackBar(
  220. content: Text('批量淘汰提交成功'),
  221. backgroundColor: Colors.green,
  222. ),
  223. );
  224. // 提交后重置表单
  225. setState(() {
  226. _rfids.clear();
  227. _disposalMethod = null;
  228. _cullReason = null;
  229. });
  230. }
  231. })
  232. .catchError((err) {
  233. ToastUtil.error('批量淘汰提交失败');
  234. if (mounted && err != null) {
  235. String errorMessage = err.toString();
  236. if (err is Exception) {
  237. errorMessage = err.toString();
  238. }
  239. ScaffoldMessenger.of(context).showSnackBar(
  240. SnackBar(
  241. content: Text(errorMessage),
  242. backgroundColor: Colors.red,
  243. ),
  244. );
  245. }
  246. });
  247. }
  248. }