|
|
@@ -0,0 +1,273 @@
|
|
|
+import 'package:chicken_farm/core/utils/logger.dart';
|
|
|
+import 'package:chicken_farm/apis/index.dart';
|
|
|
+import 'package:chicken_farm/components/vb_search_select.dart';
|
|
|
+import 'package:chicken_farm/components/vb_select.dart';
|
|
|
+import 'package:chicken_farm/modes/breeding/batch.dart';
|
|
|
+import 'package:chicken_farm/modes/breeding/family.dart';
|
|
|
+import 'package:flutter/material.dart';
|
|
|
+import 'package:chicken_farm/components/vb_app_bar.dart';
|
|
|
+import 'package:chicken_farm/core/utils/toast.dart';
|
|
|
+
|
|
|
+class BatchCreatePageWin extends StatefulWidget {
|
|
|
+ const BatchCreatePageWin({super.key});
|
|
|
+
|
|
|
+ @override
|
|
|
+ State<BatchCreatePageWin> createState() => _BatchCreatePageWinState();
|
|
|
+}
|
|
|
+
|
|
|
+class _BatchCreatePageWinState extends State<BatchCreatePageWin> {
|
|
|
+ final List<String> _rfids = [];
|
|
|
+ String? _selectedBatchNum;
|
|
|
+ String? _selectedFamilyId;
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ return Scaffold(
|
|
|
+ appBar: const VberAppBar(title: '个体绑定[Windows]', showLeftButton: true),
|
|
|
+ body: Padding(
|
|
|
+ padding: const EdgeInsets.all(16.0),
|
|
|
+ child: Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ children: [
|
|
|
+ // 批次选择
|
|
|
+ _buildBatchSearchSelect(),
|
|
|
+ const SizedBox(height: 10),
|
|
|
+ // 家系号选择
|
|
|
+ _buildFamilySearchSelect(),
|
|
|
+ const SizedBox(height: 20),
|
|
|
+ // 电子编号区域
|
|
|
+ _buildRfidSection(),
|
|
|
+ const SizedBox(height: 20),
|
|
|
+ // 提交按钮
|
|
|
+ SizedBox(
|
|
|
+ width: double.infinity,
|
|
|
+ child: ElevatedButton(
|
|
|
+ onPressed:
|
|
|
+ _rfids.isNotEmpty &&
|
|
|
+ _selectedBatchNum != null &&
|
|
|
+ _selectedFamilyId != null
|
|
|
+ ? _handleSubmit
|
|
|
+ : null,
|
|
|
+ style: ElevatedButton.styleFrom(
|
|
|
+ backgroundColor:
|
|
|
+ _rfids.isNotEmpty &&
|
|
|
+ _selectedBatchNum != null &&
|
|
|
+ _selectedFamilyId != null
|
|
|
+ ? Colors.blue
|
|
|
+ : Colors.grey,
|
|
|
+ foregroundColor: Colors.white,
|
|
|
+ ),
|
|
|
+ child: const Text('提交'),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 20),
|
|
|
+ // 已识别的电子编号列表
|
|
|
+ if (_rfids.isNotEmpty) ...[
|
|
|
+ Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
+ children: [
|
|
|
+ const Text(
|
|
|
+ '已识别的电子编号',
|
|
|
+ style: TextStyle(fontWeight: FontWeight.bold),
|
|
|
+ ),
|
|
|
+ IconButton(
|
|
|
+ icon: const Icon(Icons.clear, size: 18),
|
|
|
+ onPressed: _clearRfids,
|
|
|
+ tooltip: '清空编号',
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 10),
|
|
|
+ Expanded(
|
|
|
+ child: Container(
|
|
|
+ padding: const EdgeInsets.all(10),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ border: Border.all(color: Colors.grey),
|
|
|
+ borderRadius: BorderRadius.circular(8),
|
|
|
+ ),
|
|
|
+ child: ListView.builder(
|
|
|
+ padding: EdgeInsets.zero,
|
|
|
+ itemCount: _rfids.length,
|
|
|
+ itemBuilder: (context, index) {
|
|
|
+ return ListTile(
|
|
|
+ visualDensity: VisualDensity.compact,
|
|
|
+ contentPadding: const EdgeInsets.symmetric(
|
|
|
+ horizontal: 2,
|
|
|
+ vertical: 0,
|
|
|
+ ),
|
|
|
+ title: Text(_rfids[index]),
|
|
|
+ trailing: IconButton(
|
|
|
+ icon: const Icon(
|
|
|
+ Icons.delete,
|
|
|
+ size: 18,
|
|
|
+ color: Colors.red,
|
|
|
+ ),
|
|
|
+ onPressed: () => _removeRfid(index),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ const SizedBox(height: 20),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildBatchSearchSelect() {
|
|
|
+ return Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ children: [
|
|
|
+ const Text('选择批次', style: TextStyle(fontWeight: FontWeight.bold)),
|
|
|
+ const SizedBox(height: 10),
|
|
|
+ Container(
|
|
|
+ padding: const EdgeInsets.fromLTRB(16, 2, 16, 2),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ border: Border.all(color: Colors.grey),
|
|
|
+ borderRadius: BorderRadius.circular(8),
|
|
|
+ ),
|
|
|
+ child: VberSearchSelect<BatchModel>(
|
|
|
+ searchApi: ({required dynamic queryParams}) async {
|
|
|
+ final result = await apis.breeding.queryApi.queryPageBatchs(
|
|
|
+ queryParams,
|
|
|
+ );
|
|
|
+ return {'rows': result.rows, 'total': result.total};
|
|
|
+ },
|
|
|
+ converter: (BatchModel data) {
|
|
|
+ return SelectOption(
|
|
|
+ label: '${data.batchNum}(${data.batchName})',
|
|
|
+ value: data.batchNum,
|
|
|
+ extra: data,
|
|
|
+ );
|
|
|
+ },
|
|
|
+ value: _selectedBatchNum,
|
|
|
+ hint: '批次号',
|
|
|
+ onChanged: (String? value) {
|
|
|
+ setState(() {
|
|
|
+ _selectedBatchNum = value;
|
|
|
+ // 当切换批次时,重置后续选项和数据
|
|
|
+ _selectedFamilyId = null;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ hideUnderline: true,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildFamilySearchSelect() {
|
|
|
+ return Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ children: [
|
|
|
+ const Text('选择家系号', style: TextStyle(fontWeight: FontWeight.bold)),
|
|
|
+ const SizedBox(height: 10),
|
|
|
+ Container(
|
|
|
+ padding: const EdgeInsets.fromLTRB(16, 2, 16, 2),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ border: Border.all(color: Colors.grey),
|
|
|
+ borderRadius: BorderRadius.circular(8),
|
|
|
+ ),
|
|
|
+ child: VberSearchSelect<FamilyModel>(
|
|
|
+ searchApi: ({required dynamic queryParams}) async {
|
|
|
+ final result = await apis.breeding.queryApi.queryPageFamilys(
|
|
|
+ queryParams,
|
|
|
+ );
|
|
|
+ return {'rows': result.rows, 'total': result.total};
|
|
|
+ },
|
|
|
+ converter: (FamilyModel data) {
|
|
|
+ return SelectOption(
|
|
|
+ label: data.familyNum,
|
|
|
+ value: data.id.toString(),
|
|
|
+ extra: data,
|
|
|
+ );
|
|
|
+ },
|
|
|
+ value: _selectedFamilyId,
|
|
|
+ hint: '家系号',
|
|
|
+ onChanged: (String? value) {
|
|
|
+ setState(() {
|
|
|
+ _selectedFamilyId = value;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ hideUnderline: true,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildRfidSection() {
|
|
|
+ return Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ children: [
|
|
|
+ const Text('请输入或扫描RFID', style: TextStyle(fontWeight: FontWeight.bold)),
|
|
|
+ const SizedBox(height: 10),
|
|
|
+ Container(
|
|
|
+ padding: const EdgeInsets.fromLTRB(16, 2, 16, 2),
|
|
|
+ decoration: BoxDecoration(border: Border.all(color: Colors.grey)),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 移除指定索引的电子编号
|
|
|
+ void _removeRfid(int index) {
|
|
|
+ setState(() {
|
|
|
+ _rfids.removeAt(index);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清空所有已识别的电子编号
|
|
|
+ void _clearRfids() {
|
|
|
+ setState(() {
|
|
|
+ _rfids.clear();
|
|
|
+ });
|
|
|
+ ToastUtil.info('已清空所有电子编号');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 提交数据
|
|
|
+ void _handleSubmit() {
|
|
|
+ final data = _rfids.map((rfid) {
|
|
|
+ return {
|
|
|
+ 'rfid': rfid,
|
|
|
+ 'batchNum': _selectedBatchNum,
|
|
|
+ 'familyId': _selectedFamilyId,
|
|
|
+ };
|
|
|
+ }).toList();
|
|
|
+ apis.breeding.submitApi
|
|
|
+ .create(data)
|
|
|
+ .then((_) {
|
|
|
+ if (mounted) {
|
|
|
+ ScaffoldMessenger.of(context).showSnackBar(
|
|
|
+ const SnackBar(
|
|
|
+ content: Text('批量绑定个体成功'),
|
|
|
+ backgroundColor: Colors.green,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ // 提交后重置表单
|
|
|
+ setState(() {
|
|
|
+ _rfids.clear();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catchError((err) {
|
|
|
+ ToastUtil.error('批量绑定个体失败');
|
|
|
+ if (mounted && err != null) {
|
|
|
+ String errorMessage = err.toString();
|
|
|
+ if (err is Exception) {
|
|
|
+ errorMessage = err.toString();
|
|
|
+ }
|
|
|
+ logger.e(errorMessage);
|
|
|
+ ScaffoldMessenger.of(context).showSnackBar(
|
|
|
+ SnackBar(
|
|
|
+ content: Text(errorMessage),
|
|
|
+ backgroundColor: Colors.red,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+}
|