5 コミット ce8024ebf5 ... 625e12136b

作者 SHA1 メッセージ 日付
  Yue 625e12136b Add 添加WIN平台打包脚本 3 週間 前
  Yue 155d126466 Update 优化个体绑定页面离线模式可以手动输入批次家系号 3 週間 前
  Yue c6c188ed9a Update 优化PDA读取电子编号 3 週間 前
  Yue 2afbfaf48c Add 增加电子编号个体查询页面 3 週間 前
  Yue 8e4e1ecf52 Revert 回滚ReaderMethodCall的修改 3 週間 前
25 ファイル変更939 行追加122 行削除
  1. 8 0
      UI/CF.APP/chicken_farm/.env/.env.dev_no_pda_offline
  2. 8 0
      UI/CF.APP/chicken_farm/.env/.env.prod_no_pda_offline
  3. 1 0
      UI/CF.APP/chicken_farm/.gitignore
  4. 9 1
      UI/CF.APP/chicken_farm/.scripts/_build_cmd.txt
  5. 113 0
      UI/CF.APP/chicken_farm/.scripts/_win_build.bat
  6. 61 0
      UI/CF.APP/chicken_farm/.scripts/win_build_all.bat
  7. 3 3
      UI/CF.APP/chicken_farm/android/app/src/main/java/com/vber/chicken_farm/rfid/RfidService.java
  8. 12 0
      UI/CF.APP/chicken_farm/lib/apis/breeding/_query.dart
  9. 46 7
      UI/CF.APP/chicken_farm/lib/components/vb_electronic_id_field.dart
  10. 6 3
      UI/CF.APP/chicken_farm/lib/components/vb_win_electronic_id_field.dart
  11. 70 0
      UI/CF.APP/chicken_farm/lib/modes/breeding/chicken.dart
  12. 46 0
      UI/CF.APP/chicken_farm/lib/modes/breeding/chicken.g.dart
  13. 72 3
      UI/CF.APP/chicken_farm/lib/pages/breeding/batch_create_page_win.dart
  14. 1 1
      UI/CF.APP/chicken_farm/lib/pages/breeding/batch_culling_page_win.dart
  15. 341 0
      UI/CF.APP/chicken_farm/lib/pages/breeding/individual_query_page.dart
  16. 8 1
      UI/CF.APP/chicken_farm/lib/routes/app_routes.dart
  17. 7 0
      UI/CF.APP/chicken_farm/lib/stores/menu_store.dart
  18. 1 1
      UI/CF.APP/chicken_farm/pubspec.yaml
  19. BIN
      UI/CF.APP/chicken_farm/release/app/pda_cf-app_V1.0.0.apk
  20. BIN
      UI/CF.APP/chicken_farm/release/app/pda_no_cf-app_V1.0.0.apk
  21. BIN
      UI/CF.APP/chicken_farm/release/app/pda_offline_cf-app_V1.0.0.apk
  22. 9 5
      UI/CF.APP/chicken_farm/windows/runner/main.cpp
  23. 111 94
      UI/CF.APP/chicken_farm/windows/runner/reader/reader_method_call.cpp
  24. 6 3
      UI/CF.APP/chicken_farm/windows/runner/reader/reader_service.cpp
  25. BIN
      UI/CF.APP/chicken_farm/windows/runner/resources/app_icon.ico

+ 8 - 0
UI/CF.APP/chicken_farm/.env/.env.dev_no_pda_offline

@@ -0,0 +1,8 @@
+APP_ENV=dev.offline
+APP_NAME=养殖场管理系统(脱机)
+APP_VERSION=1.0.0-dev
+BASE_API_URL=http://192.168.0.81:8380
+CLIENT_ID=35aee70ae7224eb9a48bc527955ddedc
+IS_OFFLINE=true
+IS_PDA=false
+WIN_DB_PATH=.demo/data

+ 8 - 0
UI/CF.APP/chicken_farm/.env/.env.prod_no_pda_offline

@@ -0,0 +1,8 @@
+APP_ENV=prod.offline
+APP_NAME=养殖场管理系统(脱机)
+APP_VERSION=1.0.0
+BASE_API_URL=http://shvber.com:5068
+CLIENT_ID=35aee70ae7224eb9a48bc527955ddedc
+IS_OFFLINE=true
+IS_PDA=false
+WIN_DB_PATH=data/db

+ 1 - 0
UI/CF.APP/chicken_farm/.gitignore

@@ -46,3 +46,4 @@ app.*.map.json
 /android/app/profile
 /android/app/release
 
+/release

+ 9 - 1
UI/CF.APP/chicken_farm/.scripts/_build_cmd.txt

@@ -12,4 +12,12 @@ flutter build apk --target-platform android-arm --dart-define-from-file .env/.en
 copy build\app\outputs\flutter-apk\app-release.apk release\app\pda-offline_cf-app.apk
 
 // 安装 app目录下的 apk
-adb install -r release/app/cf-app_20251222111721.apk
+adb install -r release/app/cf-app_20251222111721.apk
+
+// 构建 windows
+flutter build windows --dart-define-from-file .env/.env.prod_no_pda
+// 构建 windows_offline
+flutter build windows --dart-define-from-file .env/.env.prod_no_pda_offline
+
+// 重新构建,model自动生成
+flutter pub run build_runner build

+ 113 - 0
UI/CF.APP/chicken_farm/.scripts/_win_build.bat

@@ -0,0 +1,113 @@
+@echo off
+chcp 65001 >nul 2>&1
+setlocal enabledelayedexpansion
+
+:: ========== 1. 项目根目录初始化 ==========
+set "PROJECT_ROOT=%~dp0\.."
+for %%i in ("%PROJECT_ROOT%") do set "PROJECT_ROOT=%%~fi"
+
+:: ========== 2. 参数校验 ==========
+if "%~1"=="" (
+  echo [Error] 请使用命令: _build_win.bat [prod^|dev] [version] [^|offline]
+  echo [示例]  _build_win.bat prod v1.0.0 offline
+  echo [示例]  _build_win.bat prod v1.0.0
+  pause
+  exit /b 1
+)
+
+if /i not "%~1"=="prod" if /i not "%~1"=="dev" (
+  echo [Error] 参数1无效,仅支持 prod 或 dev
+  pause
+  exit /b 1
+)
+
+:: ========== 3. 环境变量加载 ==========
+set "ENV=%~1_no_pda"
+if not "%~3"=="" if /i "%~3"=="offline" (
+  set "ENV=%~1_no_pda_offline"
+) else if not "%~3"=="" (
+  set "ENV=%~1_no_pda_%~3"
+)
+
+set "BUILD_PLATFORM=windows-x64"
+set "APP_NAME=cf-app"
+
+:: 根据参数组合设置APP_NAME
+if not "%~3"=="" (
+  :: 如果提供了第三个参数
+  if "%~3"=="offline" (
+    set "APP_NAME=win_offline_cf-app"
+  ) else (
+    set "APP_NAME=win_%~3_cf-app"
+  )
+) else (
+  set "APP_NAME=win_cf-app"
+)
+
+set "APP_VERSION=V1.0.0"
+:: 添加版本号到APP_NAME
+if not "%~2"=="" (
+  set "APP_VERSION=%~2"
+)
+
+set "ENV_FILE=%PROJECT_ROOT%\.env\.env.%ENV%"
+if not exist "%ENV_FILE%" (
+  echo [Error] 环境文件不存在: %ENV_FILE%
+  pause
+  exit /b 1
+)
+
+:: ========== 4. 构建参数生成 ==========
+set "DEFINE_ARGS="
+for /f "tokens=1,* delims==" %%a in ('type "%ENV_FILE%" ^| findstr /v "^#" ^| findstr /v "^$"') do (
+  set "DEFINE_ARGS=!DEFINE_ARGS! --dart-define=%%a=%%b"
+)
+
+:: ========== 5. 执行构建 ==========
+echo ==============================================
+echo [INFO] 开始构建: 环境=%ENV% ^| 平台=%BUILD_PLATFORM% ^| 应用=%APP_NAME%
+cd /d "%PROJECT_ROOT%"
+call flutter build windows !DEFINE_ARGS!
+if !errorlevel! neq 0 (
+  echo [Error] Flutter构建失败
+  pause
+  exit /b 1
+)
+
+:: ========== 6. 文件处理优化 ==========
+:: 正确的构建输出路径
+set "WIN_SOURCE=%PROJECT_ROOT%\build\windows\x64\runner\Release"
+set "WIN_DEST=%PROJECT_ROOT%\release\windows\%APP_NAME%_%APP_VERSION%"
+
+if exist "%WIN_SOURCE%" (
+  if not exist "%PROJECT_ROOT%\release\windows" (
+    mkdir "%PROJECT_ROOT%\release\windows"
+  )
+  xcopy /E /I /Y "%WIN_SOURCE%" "%WIN_DEST%"
+  echo ==============================================
+  echo [INFO] Windows应用打包成功: %WIN_DEST%
+  echo ==============================================
+) else (
+  echo [Error] Windows应用源文件不存在: %WIN_SOURCE%
+  pause
+  exit /b 1
+)
+
+:: ========== 7. 打包发布文件 ==========
+echo ==============================================
+echo [INFO] 正在压缩应用包...
+cd /d "%PROJECT_ROOT%"
+
+set "ZIP_NAME=%APP_NAME%_%APP_VERSION%.zip"
+set "ZIP_PATH=%PROJECT_ROOT%\release\windows\%ZIP_NAME%"
+
+:: 使用 PowerShell 命令进行 ZIP 压缩
+powershell -Command "Compress-Archive -Path '%WIN_DEST%\*' -DestinationPath '%ZIP_PATH%' -Force"
+
+if exist "%ZIP_PATH%" (
+  echo [INFO] 应用已成功压缩: %ZIP_PATH%
+) else (
+  echo [Error] 压缩失败,请检查系统是否支持 Compress-Archive
+  pause
+  exit /b 1
+)

+ 61 - 0
UI/CF.APP/chicken_farm/.scripts/win_build_all.bat

@@ -0,0 +1,61 @@
+@echo off
+chcp 936 >nul 2>&1
+setlocal enabledelayedexpansion
+
+:: ========== 全局配置(修复路径问题) ==========
+:: 当前脚本目录(.scripts)
+set "SCRIPT_DIR=%~dp0"
+:: 项目根目录(.scripts 的上级目录)
+set "PROJECT_ROOT=%SCRIPT_DIR%.."
+:: 编译脚本路径(直接指向当前目录的 _win_build.bat,无需重复拼接 .scripts)
+set "BUILD_SCRIPT=%SCRIPT_DIR%_win_build.bat"
+set "VERSION=V1.0.0"
+:: ========== 前置检查 ==========
+if not exist "%BUILD_SCRIPT%" (
+  echo [错误] 未找到编译脚本:%BUILD_SCRIPT%
+  pause
+  exit /b 1
+)
+
+:: ========== 清理缓存 ==========
+call flutter clean
+call flutter pub get
+
+:: ========== 依次执行编译命令 ==========
+echo ==============================================
+echo 开始批量编译所有WIN应用(共2个环境)
+echo ==============================================
+
+:: 1. 编译 prod 基础环境
+echo.
+echo [第1个] 开始编译:prod 基础环境
+call "%BUILD_SCRIPT%" prod "%VERSION%"
+if !errorlevel! neq 0 (
+  echo [错误] prod 基础环境编译失败!
+  pause
+  exit /b 1
+)
+echo [第1个] prod 基础环境编译完成 ??
+
+
+:: 2. 编译 prod_offline 环境
+echo.
+echo [第2个] 开始编译:prod offline 环境
+call "%BUILD_SCRIPT%" prod "%VERSION%" offline
+if !errorlevel! neq 0 (
+  echo [错误] prod offline 环境编译失败!
+  pause
+  exit /b 1
+)
+echo [第2个] prod offline 环境编译完成 ??
+
+:: ========== 批量编译完成 ==========
+echo.
+echo ==============================================
+echo 所有WIN应用编译完成!?
+echo 生成的文件位于:%PROJECT_ROOT%\release\windows\
+echo ==============================================
+
+
+pause
+endlocal

+ 3 - 3
UI/CF.APP/chicken_farm/android/app/src/main/java/com/vber/chicken_farm/rfid/RfidService.java

@@ -658,9 +658,9 @@ public class RfidService extends Service {
                 return false;
             }
             Log.d(RfidConstants.TAG, "RFID读卡已启动");
-            // 1 秒后停止读卡
+            // 5 秒后停止读卡
             idleHandler.postDelayed(() -> {
-                Log.d(RfidConstants.TAG, "1秒RFID读卡自动停止");
+                Log.d(RfidConstants.TAG, "5秒RFID读卡自动停止");
                 if (isScanning) {
                     stopScan();
                     if (listener != null
@@ -668,7 +668,7 @@ public class RfidService extends Service {
                         listener.onScanError("未读卡到电子编号");
                     }
                 }
-            }, 1 * 1000);
+            }, 5 * 1000);
         } catch (Exception e) {
             Log.e(RfidConstants.TAG, "读卡失败:", e);
             isScanning = false;

+ 12 - 0
UI/CF.APP/chicken_farm/lib/apis/breeding/_query.dart

@@ -1,5 +1,6 @@
 import 'package:chicken_farm/core/api/api_service.dart';
 import 'package:chicken_farm/modes/breeding/batch.dart';
+import 'package:chicken_farm/modes/breeding/chicken.dart';
 import 'package:chicken_farm/modes/breeding/family.dart';
 import 'package:chicken_farm/modes/api/page_model.dart';
 
@@ -45,4 +46,15 @@ class BreedQueryApi {
     }
     return PageResultModel.empty();
   }
+
+  Future<ChickenModel?> queryChicken(String electronicId) async {
+    final result = await ApiService().get(
+      '$apiPrefix/getChicken/$electronicId',
+    );
+    if (result.success && result.data != null) {
+      return ChickenModel.fromJson(result.data);
+    } else {
+      return null;
+    }
+  }
 }

+ 46 - 7
UI/CF.APP/chicken_farm/lib/components/vb_electronic_id_field.dart

@@ -1,3 +1,5 @@
+import 'dart:async';
+
 import 'package:chicken_farm/core/utils/logger.dart';
 import 'package:chicken_farm/modes/rfid/rfid_model.dart';
 import 'package:flutter/material.dart';
@@ -21,6 +23,7 @@ class VberElectronicIdsField extends StatefulWidget {
   final String placeholder; // 未识别时显示的占位符文本
   final String multiplePlaceholder; // 多个模式下未识别时显示的占位符文本
   final String multipleFormat; // 多个模式下有值时的显示格式,包含一个%d占位符表示数量
+  final int scanMilliseconds; // 扫描时间,到达时间后自动停止扫描
 
   const VberElectronicIdsField({
     super.key,
@@ -36,6 +39,7 @@ class VberElectronicIdsField extends StatefulWidget {
     this.placeholder = '未识别',
     this.multiplePlaceholder = '未识别',
     this.multipleFormat = '已识别 %d 枚电子编号',
+    this.scanMilliseconds = 3000,
   });
 
   @override
@@ -45,6 +49,8 @@ class VberElectronicIdsField extends StatefulWidget {
 class _VberElectronicIdsFieldState extends State<VberElectronicIdsField> {
   bool _isScanning = false; // 内部管理识别状态
   bool _isMultiple = false; // 内部管理识别状态
+  Timer? _autoStopTimer; // 自动停止扫描的定时器
+  int _scanMilliseconds = 3000;
 
   @override
   void initState() {
@@ -59,19 +65,24 @@ class _VberElectronicIdsFieldState extends State<VberElectronicIdsField> {
       },
     );
     _isMultiple = widget.multipleScan ?? widget.multiple;
+    _scanMilliseconds = widget.scanMilliseconds;
   }
 
   @override
   void dispose() {
+    // 取消自动停止定时器
+    _autoStopTimer?.cancel();
+    _autoStopTimer = null;
     RfidManager.instance.disposeRfid();
+
     super.dispose();
   }
 
   void _handleRfidScanned(String rfid) {
     logger.d('识别成功,已识别电子编号:$rfid');
-    setState(() {
-      _isScanning = false;
-    });
+    // setState(() {
+    //   _isScanning = false;
+    // });
     if (widget.onIdScanned != null) {
       widget.onIdScanned!(rfid);
     }
@@ -79,9 +90,9 @@ class _VberElectronicIdsFieldState extends State<VberElectronicIdsField> {
 
   void _handleRfidsScanned(List<RfidModel> rfidList) {
     logger.d('识别成功,已识别枚${rfidList.length}电子编号');
-    setState(() {
-      _isScanning = false;
-    });
+    // setState(() {
+    //   _isScanning = false;
+    // });
     if (widget.onIdsScanned != null) {
       widget.onIdsScanned!(rfidList);
     }
@@ -96,7 +107,11 @@ class _VberElectronicIdsFieldState extends State<VberElectronicIdsField> {
 
   void _handleKeyPress(String keyCode) {
     if (keyCode == 'KEY_619') {
-      _scanRfid();
+      if (_isScanning) {
+        _stopScan();
+      } else {
+        _scanRfid();
+      }
     }
   }
 
@@ -116,6 +131,30 @@ class _VberElectronicIdsFieldState extends State<VberElectronicIdsField> {
       setState(() {
         _isScanning = false;
       });
+    } else {
+      _autoStopTimer = Timer(Duration(milliseconds: _scanMilliseconds), () {
+        if (_isScanning) {
+          // 确保仍在扫描状态
+          _stopScan();
+        }
+      });
+    }
+  }
+
+  void _stopScan() async {
+    try {
+      if (_isScanning) {
+        await RfidManager.instance.stopRead();
+      }
+      // 取消自动停止定时器
+      _autoStopTimer?.cancel();
+      _autoStopTimer = null;
+    } catch (e) {
+      // ignore: avoid_print
+    } finally {
+      setState(() {
+        _isScanning = false;
+      });
     }
   }
 

+ 6 - 3
UI/CF.APP/chicken_farm/lib/components/vb_win_electronic_id_field.dart

@@ -20,6 +20,7 @@ class VbWinElectronicIdField extends StatefulWidget {
   final bool multiple; // 是否为多标签模式
   final bool? multipleScan; // 是否开启多标签扫描模式
   final ValueChanged<int>? onDeleteTag; // 仅在multiple模式下使用
+  final int scanMilliseconds; // 扫描时间,到达时间后自动停止扫描
 
   const VbWinElectronicIdField({
     super.key,
@@ -34,6 +35,7 @@ class VbWinElectronicIdField extends StatefulWidget {
     this.multiple = false,
     this.multipleScan,
     this.onDeleteTag,
+    this.scanMilliseconds = 1100,
   });
 
   @override
@@ -45,12 +47,13 @@ class _VbWinElectronicIdFieldState extends State<VbWinElectronicIdField> {
   bool _isMultiple = false; // 内部管理多标签扫描模式
   final List<String> _scannedTags = []; // 存储扫描到的标签
   Timer? _autoStopTimer; // 自动停止扫描的定时器
+  int _scanMilliseconds = 1100;
 
   @override
   void initState() {
     super.initState();
     _isMultiple = widget.multipleScan ?? widget.multiple;
-
+    _scanMilliseconds = widget.scanMilliseconds;
     WinReaderManager.instance.registerCallbacks(
       onDataReceived: _handleTagReceived,
       onError: _handleScanError,
@@ -138,7 +141,7 @@ class _VbWinElectronicIdFieldState extends State<VbWinElectronicIdField> {
         _isScanning = false;
       });
     }
-    _autoStopTimer = Timer(const Duration(milliseconds: 1100), () {
+    _autoStopTimer = Timer(Duration(milliseconds: _scanMilliseconds), () {
       if (_isScanning) {
         // 确保仍在扫描状态
         _stopScan();
@@ -147,7 +150,7 @@ class _VbWinElectronicIdFieldState extends State<VbWinElectronicIdField> {
   }
 
   void _stopScan() async {
-    ToastUtil.info("停止识别电子标签");
+    // ToastUtil.info("停止识别电子标签");
     await WinReaderManager.instance.stopRead();
     // 取消自动停止定时器
     _autoStopTimer?.cancel();

+ 70 - 0
UI/CF.APP/chicken_farm/lib/modes/breeding/chicken.dart

@@ -0,0 +1,70 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'chicken.g.dart';
+
+@JsonSerializable()
+class ChickenModel {
+  int id;
+
+  String electronicId;
+  String batchNum;
+
+  int gender;
+
+  DateTime hatchDate;
+
+  // String wingTagNum;
+
+  // String legTag;
+
+  // int familyId;
+
+  String familyNum;
+
+  // int cageId;
+
+  String? cageNum;
+
+  DateTime? cullTime;
+
+  int? status;
+
+  int? cullReason;
+
+  int? disposalMethod;
+
+  int? currentAge;
+
+  String? feedSopName;
+
+  String? drugSopName;
+
+  String? vaccineSopName;
+
+  ChickenModel({
+    required this.id,
+    required this.electronicId,
+    required this.batchNum,
+    required this.gender,
+    required this.hatchDate,
+    // required this.wingTagNum,
+    // required this.legTag,
+    // required this.familyId,
+    required this.familyNum,
+    // required this.cageId,
+    this.cageNum,
+    this.cullTime,
+    this.status,
+    this.cullReason,
+    this.disposalMethod,
+    this.currentAge,
+    this.feedSopName,
+    this.drugSopName,
+    this.vaccineSopName,
+  });
+
+  factory ChickenModel.fromJson(Map<String, dynamic> json) =>
+      _$ChickenModelFromJson(json);
+
+  Map<String, dynamic> toJson() => _$ChickenModelToJson(this);
+}

+ 46 - 0
UI/CF.APP/chicken_farm/lib/modes/breeding/chicken.g.dart

@@ -0,0 +1,46 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'chicken.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+ChickenModel _$ChickenModelFromJson(Map<String, dynamic> json) => ChickenModel(
+  id: (json['id'] as num).toInt(),
+  electronicId: json['electronicId'] as String,
+  batchNum: json['batchNum'] as String,
+  gender: (json['gender'] as num).toInt(),
+  hatchDate: DateTime.parse(json['hatchDate'] as String),
+  familyNum: json['familyNum'] as String,
+  cageNum: json['cageNum'] as String,
+  cullTime: json['cullTime'] == null
+      ? null
+      : DateTime.parse(json['cullTime'] as String),
+  status: (json['status'] as num?)?.toInt(),
+  cullReason: (json['cullReason'] as num?)?.toInt(),
+  disposalMethod: (json['disposalMethod'] as num?)?.toInt(),
+  currentAge: (json['currentAge'] as num?)?.toInt(),
+  feedSopName: json['feedSopName'] as String?,
+  drugSopName: json['drugSopName'] as String?,
+  vaccineSopName: json['vaccineSopName'] as String?,
+);
+
+Map<String, dynamic> _$ChickenModelToJson(ChickenModel instance) =>
+    <String, dynamic>{
+      'id': instance.id,
+      'electronicId': instance.electronicId,
+      'batchNum': instance.batchNum,
+      'gender': instance.gender,
+      'hatchDate': instance.hatchDate.toIso8601String(),
+      'familyNum': instance.familyNum,
+      'cageNum': instance.cageNum,
+      'cullTime': instance.cullTime?.toIso8601String(),
+      'status': instance.status,
+      'cullReason': instance.cullReason,
+      'disposalMethod': instance.disposalMethod,
+      'currentAge': instance.currentAge,
+      'feedSopName': instance.feedSopName,
+      'drugSopName': instance.drugSopName,
+      'vaccineSopName': instance.vaccineSopName,
+    };

+ 72 - 3
UI/CF.APP/chicken_farm/lib/pages/breeding/batch_create_page_win.dart

@@ -1,3 +1,4 @@
+import 'package:chicken_farm/core/config/app_config.dart';
 import 'package:chicken_farm/core/utils/datetime_util.dart';
 import 'package:chicken_farm/apis/index.dart';
 import 'package:chicken_farm/components/vb_search_select.dart';
@@ -24,17 +25,17 @@ class _BatchCreatePageWinState extends State<BatchCreatePageWin> {
   @override
   Widget build(BuildContext context) {
     return Scaffold(
-      appBar: const VberAppBar(title: '个体绑定[Windows]', showLeftButton: true),
+      appBar: const VberAppBar(title: '个体绑定', showLeftButton: true),
       body: SingleChildScrollView(
         padding: const EdgeInsets.all(16.0),
         child: Column(
           crossAxisAlignment: CrossAxisAlignment.start,
           children: [
             // 批次选择
-            _buildBatchSearchSelect(),
+            _buildBatchInput(),
             const SizedBox(height: 10),
             // 家系号选择
-            _buildFamilySearchSelect(),
+            _buildFamilyInput(),
             const SizedBox(height: 20),
             // 电子编号区域
             _buildElectronicIdSection(),
@@ -117,6 +118,74 @@ class _BatchCreatePageWinState extends State<BatchCreatePageWin> {
     );
   }
 
+  Widget _buildBatchInput() {
+    if (AppConfig.isOffline) {
+      // 离线模式下使用文本输入框
+      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: TextField(
+              decoration: const InputDecoration(
+                hintText: '请输入批次号',
+                border: InputBorder.none,
+              ),
+              onChanged: (value) {
+                setState(() {
+                  _batchNum = value.isNotEmpty ? value : null;
+                });
+              },
+            ),
+          ),
+        ],
+      );
+    } else {
+      // 在线模式下使用原来的搜索选择器
+      return _buildBatchSearchSelect();
+    }
+  }
+
+  Widget _buildFamilyInput() {
+    if (AppConfig.isOffline) {
+      // 离线模式下使用文本输入框
+      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: TextField(
+              decoration: const InputDecoration(
+                hintText: '请输入家系号',
+                border: InputBorder.none,
+              ),
+              onChanged: (value) {
+                setState(() {
+                  _familyNum = value.isNotEmpty ? value : null;
+                });
+              },
+            ),
+          ),
+        ],
+      );
+    } else {
+      // 在线模式下使用原来的搜索选择器
+      return _buildFamilySearchSelect();
+    }
+  }
+
   Widget _buildBatchSearchSelect() {
     return Column(
       crossAxisAlignment: CrossAxisAlignment.start,

+ 1 - 1
UI/CF.APP/chicken_farm/lib/pages/breeding/batch_culling_page_win.dart

@@ -21,7 +21,7 @@ class _BatchCullingPageWinState extends State<BatchCullingPageWin> {
   @override
   Widget build(BuildContext context) {
     return Scaffold(
-      appBar: const VberAppBar(title: '批量淘汰[Windows]', showLeftButton: true),
+      appBar: const VberAppBar(title: '批量淘汰', showLeftButton: true),
       body: SingleChildScrollView(
         padding: const EdgeInsets.all(16.0),
         child: Column(

+ 341 - 0
UI/CF.APP/chicken_farm/lib/pages/breeding/individual_query_page.dart

@@ -0,0 +1,341 @@
+import 'dart:async';
+import 'package:chicken_farm/components/vb_dict_label.dart';
+import 'package:chicken_farm/core/utils/logger.dart';
+import 'package:flutter/material.dart';
+import 'package:chicken_farm/apis/breeding/_query.dart';
+import 'package:chicken_farm/components/vb_electronic_id_field.dart';
+import 'package:chicken_farm/core/utils/toast.dart';
+import 'package:chicken_farm/modes/breeding/chicken.dart';
+import 'package:intl/intl.dart';
+
+class IndividualQueryPage extends StatefulWidget {
+  const IndividualQueryPage({super.key});
+
+  @override
+  State<IndividualQueryPage> createState() => _IndividualQueryPageState();
+}
+
+class _IndividualQueryPageState extends State<IndividualQueryPage> {
+  final BreedQueryApi _breedQueryApi = BreedQueryApi();
+  ChickenModel? _chicken;
+  String? _electronicId;
+  bool _isLoading = false;
+  final TextEditingController _idController = TextEditingController();
+  Timer? _debounceTimer; // 添加Timer变量用于管理延时操作
+
+  // 添加手动模式状态
+  bool _isManualMode = false;
+
+  @override
+  void initState() {
+    super.initState();
+  }
+
+  @override
+  void dispose() {
+    // 取消任何待处理的计时器
+    if (_debounceTimer != null) {
+      _debounceTimer!.cancel();
+    }
+    _idController.dispose();
+    super.dispose();
+  }
+
+  void _queryChickenOnce(String id) {
+    if (_debounceTimer != null) {
+      _debounceTimer!.cancel();
+    }
+
+    _debounceTimer = Timer(Duration(milliseconds: 800), () {
+      _queryChicken(id);
+    });
+  }
+
+  Future<void> _queryChicken(String id) async {
+    if (id.isEmpty) {
+      ToastUtil.warning('请输入电子编号');
+      return;
+    }
+
+    setState(() {
+      _isLoading = true;
+    });
+
+    try {
+      final chicken = await _breedQueryApi.queryChicken(id.trim());
+      if (chicken != null) {
+        if (mounted) {
+          // 遵循Flutter异步操作与UI更新安全规范
+          setState(() {
+            _chicken = chicken;
+          });
+        }
+      } else {
+        if (mounted) {
+          // 遵循Flutter异步操作与UI更新安全规范
+          ToastUtil.error('未找到该编号对应的个体信息');
+          setState(() {
+            _chicken = null;
+          });
+        }
+      }
+    } catch (e) {
+      if (mounted) {
+        // 遵循Flutter异步操作与UI更新安全规范
+        ToastUtil.error('查询失败: $e');
+        logger.e(e);
+        setState(() {
+          _chicken = null;
+        });
+      }
+    } finally {
+      if (mounted) {
+        // 遵循Flutter异步操作与UI更新安全规范
+        setState(() {
+          _isLoading = false;
+        });
+      }
+    }
+  }
+
+  // 手动查询方法
+  void _manualQueryChicken() {
+    if (_idController.text.isEmpty) {
+      ToastUtil.warning('请输入电子编号');
+      return;
+    }
+    _queryChicken(_idController.text);
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(title: const Text('个体查询')),
+      body: Padding(
+        padding: const EdgeInsets.all(16.0),
+        child: Column(
+          children: [
+            // 按钮行 - 手动/自动切换按钮
+            Row(
+              mainAxisAlignment: MainAxisAlignment.end, // 靠右对齐
+              children: [
+                // 手动/自动切换按钮
+                SizedBox(
+                  // 设置固定宽度
+                  width: 120,
+                  child: ElevatedButton(
+                    onPressed: () {
+                      setState(() {
+                        _isManualMode = !_isManualMode;
+                        _idController.clear();
+                        _chicken = null;
+                        _electronicId = null;
+                      });
+                    },
+                    style: ElevatedButton.styleFrom(
+                      backgroundColor: _isManualMode
+                          ? Colors.blue
+                          : Colors.orange,
+                      foregroundColor: Colors.white,
+                    ),
+                    child: Text(_isManualMode ? '识别查询' : '手动查询'),
+                  ),
+                ),
+              ],
+            ),
+            const SizedBox(height: 8), // 添加一点间距
+            // 电子标签输入区域 - 根据模式动态显示
+            if (!_isManualMode) ...[
+              _buildPlatformSpecificField(),
+            ] else ...[
+              // 手动输入模式
+              Row(
+                children: [
+                  Expanded(
+                    child: TextField(
+                      controller: _idController,
+                      decoration: const InputDecoration(
+                        labelText: '电子编号',
+                        border: OutlineInputBorder(),
+                        hintText: '请输入电子编号',
+                      ),
+                    ),
+                  ),
+                  const SizedBox(width: 8),
+                  ElevatedButton(
+                    onPressed: _manualQueryChicken,
+                    style: ElevatedButton.styleFrom(
+                      backgroundColor: Colors.green,
+                      foregroundColor: Colors.white,
+                    ),
+                    child: const Text('查询'),
+                  ),
+                ],
+              ),
+            ],
+            const SizedBox(height: 16),
+            // 个体信息展示区域
+            if (_chicken != null) ...[
+              Expanded(
+                child: SingleChildScrollView(
+                  child: Card(
+                    child: Padding(
+                      padding: const EdgeInsets.all(16.0),
+                      child: Column(
+                        crossAxisAlignment: CrossAxisAlignment.start,
+                        children: [
+                          // 基本信息
+                          const Text(
+                            '基本信息',
+                            style: TextStyle(
+                              fontSize: 18,
+                              fontWeight: FontWeight.bold,
+                            ),
+                          ),
+                          const SizedBox(height: 10),
+                          _buildInfoRow('电子编号:', _chicken!.electronicId),
+                          _buildInfoRow('批次号:', _chicken!.batchNum),
+                          _buildInfoRow('家系编号:', _chicken!.familyNum),
+                          _buildInfoRow('笼号:', _chicken!.cageNum ?? '无'),
+                          _buildInfoRow2(
+                            '性别:',
+                            VberDictLabel(
+                              dictType: "chicken_gender",
+                              value: _chicken!.gender.toString(),
+                            ),
+                          ),
+                          _buildInfoRow(
+                            '孵化日期:',
+                            DateFormat(
+                              'yyyy-MM-dd',
+                            ).format(_chicken!.hatchDate),
+                          ),
+                          if (_chicken!.currentAge != null)
+                            _buildInfoRow('当前日龄:', '${_chicken!.currentAge} 天'),
+                          _buildInfoRow2(
+                            '养殖状态:',
+                            VberDictLabel(
+                              dictType: "chicken_status",
+                              value: _chicken!.status.toString(),
+                            ),
+                          ),
+
+                          if (_chicken!.cullReason != null)
+                            _buildInfoRow2(
+                              '淘汰原因:',
+                              VberDictLabel(
+                                dictType: "chicken_cull_reason",
+                                value: _chicken!.cullReason.toString(),
+                              ),
+                            ),
+                          if (_chicken!.disposalMethod != null)
+                            _buildInfoRow2(
+                              '处理方式:',
+                              VberDictLabel(
+                                dictType: "chicken_disposal_method",
+                                value: _chicken!.disposalMethod.toString(),
+                              ),
+                            ),
+                          if (_chicken!.cullTime != null)
+                            _buildInfoRow(
+                              '淘汰时间:',
+                              DateFormat(
+                                'yyyy-MM-dd HH:mm:ss',
+                              ).format(_chicken!.cullTime!),
+                            ),
+                          const SizedBox(height: 20),
+
+                          // SOP信息
+                          // const Text(
+                          //   'SOP信息',
+                          //   style: TextStyle(
+                          //     fontSize: 18,
+                          //     fontWeight: FontWeight.bold,
+                          //   ),
+                          // ),
+                          // const SizedBox(height: 10),
+                          // _buildInfoRow('饲料SOP:', _chicken!.feedSopName ?? '无'),
+                          // _buildInfoRow('用药SOP:', _chicken!.drugSopName ?? '无'),
+                          // _buildInfoRow(
+                          //   '疫苗SOP:',
+                          //   _chicken!.vaccineSopName ?? '无',
+                          // ),
+                        ],
+                      ),
+                    ),
+                  ),
+                ),
+              ),
+            ] else if (!_isLoading)
+              Expanded(
+                child: Center(
+                  child: Text(
+                    '请${_isManualMode ? "输入" : "识别"}电子编号查询',
+                    style: Theme.of(
+                      context,
+                    ).textTheme.titleMedium?.copyWith(color: Colors.grey[600]),
+                  ),
+                ),
+              ),
+          ],
+        ),
+      ),
+    );
+  }
+
+  // 根据平台构建特定字段组件
+  Widget _buildPlatformSpecificField() {
+    return VberElectronicIdsField(
+      electronicId: _electronicId,
+      scanMilliseconds: 1000,
+      onIdScanned: (id) {
+        setState(() {
+          _electronicId = id;
+        });
+        _queryChickenOnce(id);
+      },
+      label: '电子编号',
+      placeholder: '未识别',
+    );
+  }
+
+  Widget _buildInfoRow(String label, String value) {
+    return Padding(
+      padding: const EdgeInsets.only(bottom: 8.0),
+      child: Row(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          SizedBox(
+            width: 80,
+            child: Text(
+              label,
+              style: const TextStyle(fontWeight: FontWeight.w500),
+            ),
+          ),
+          Expanded(
+            child: Text(value, style: const TextStyle(color: Colors.black87)),
+          ),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildInfoRow2(String label, Widget value) {
+    return Padding(
+      padding: const EdgeInsets.only(bottom: 8.0),
+      child: Row(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          SizedBox(
+            width: 80,
+            child: Text(
+              label,
+              style: const TextStyle(fontWeight: FontWeight.w500),
+            ),
+          ),
+          value,
+        ],
+      ),
+    );
+  }
+}

+ 8 - 1
UI/CF.APP/chicken_farm/lib/routes/app_routes.dart

@@ -17,6 +17,7 @@ import '../pages/breeding/batch_create_page_win.dart';
 import '../pages/breeding/cage_change_page.dart';
 import '../pages/breeding/individual_weighing_page.dart';
 import '../pages/breeding/individual_culling_page.dart';
+import '../pages/breeding/individual_query_page.dart';
 import '../pages/breeding/batch_culling_page.dart';
 import '../pages/breeding/batch_culling_page_win.dart';
 import '../pages/upload/upload_page.dart';
@@ -33,6 +34,7 @@ class AppRouteNames {
   static const String cageChange = '/cage_change';
   static const String individualWeighing = '/individual_weighing';
   static const String individualCulling = '/individual_culling';
+  static const String individualQuery = '/individual_query'; // 个体查询
   static const String batchCulling = '/batch_culling';
   static const String batchCullingWin = '/batch_culling_win'; // Windows平台批量淘汰
   static const String sample = '/sample';
@@ -106,6 +108,11 @@ class AppRoutes {
       name: AppRouteNames.individualCulling,
       builder: (context, state) => const IndividualCullingPage(),
     ),
+    GoRoute(
+      path: AppRouteNames.individualQuery,
+      name: AppRouteNames.individualQuery,
+      builder: (context, state) => const IndividualQueryPage(),
+    ),
     GoRoute(
       path: AppRouteNames.batchCulling,
       name: AppRouteNames.batchCulling,
@@ -172,4 +179,4 @@ class SplashScreen extends ConsumerWidget {
     });
     return const Scaffold(body: Center(child: CircularProgressIndicator()));
   }
-}
+}

+ 7 - 0
UI/CF.APP/chicken_farm/lib/stores/menu_store.dart

@@ -45,6 +45,13 @@ class MenuStore {
       permission: PermissionKeys.chickenCulling,
       platform: 1,
     ),
+    MenuItem(
+      name: '个体查询',
+      routeName: AppRouteNames.individualQuery,
+      icon: Icons.search,
+      permission: "",
+      platform: 1,
+    ),
     MenuItem(
       name: '点检签到',
       routeName: AppRouteNames.checkin,

+ 1 - 1
UI/CF.APP/chicken_farm/pubspec.yaml

@@ -1,5 +1,5 @@
 name: chicken_farm
-description: "A new Flutter project."
+description: "广明养殖管理系统平台"
 # The following line prevents the package from being accidentally published to
 # pub.dev using `flutter pub publish`. This is preferred for private packages.
 publish_to: 'none' # Remove this line if you wish to publish to pub.dev

BIN
UI/CF.APP/chicken_farm/release/app/pda_cf-app_V1.0.0.apk


BIN
UI/CF.APP/chicken_farm/release/app/pda_no_cf-app_V1.0.0.apk


BIN
UI/CF.APP/chicken_farm/release/app/pda_offline_cf-app_V1.0.0.apk


+ 9 - 5
UI/CF.APP/chicken_farm/windows/runner/main.cpp

@@ -1,4 +1,4 @@
-#include <flutter/dart_project.h>
+#include <flutter/dart_project.h>
 #include <flutter/flutter_view_controller.h>
 #include <windows.h>
 
@@ -6,10 +6,12 @@
 #include "utils.h"
 
 int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
-                      _In_ wchar_t *command_line, _In_ int show_command) {
+                      _In_ wchar_t *command_line, _In_ int show_command)
+{
   // Attach to console when present (e.g., 'flutter run') or create a
   // new console when running with a debugger.
-  if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
+  if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent())
+  {
     CreateAndAttachConsole();
   }
 
@@ -27,13 +29,15 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
   FlutterWindow window(project);
   Win32Window::Point origin(10, 10);
   Win32Window::Size size(1280, 720);
-  if (!window.Create(L"chicken_farm", origin, size)) {
+  if (!window.Create(L"广明养殖管理平台", origin, size))
+  {
     return EXIT_FAILURE;
   }
   window.SetQuitOnClose(true);
 
   ::MSG msg;
-  while (::GetMessage(&msg, nullptr, 0, 0)) {
+  while (::GetMessage(&msg, nullptr, 0, 0))
+  {
     ::TranslateMessage(&msg);
     ::DispatchMessage(&msg);
   }

+ 111 - 94
UI/CF.APP/chicken_farm/windows/runner/reader/reader_method_call.cpp

@@ -1,10 +1,11 @@
 #include "reader_method_call.h"
 #include "reader_service.h"
 
+#include <flutter/flutter_engine.h>
 #include <flutter/method_channel.h>
-#include <flutter/plugin_registrar_windows.h>
 #include <flutter/standard_method_codec.h>
 #include <flutter/event_channel.h>
+#include <flutter/event_stream_handler_functions.h>
 
 #include <memory>
 #include <string>
@@ -12,124 +13,140 @@
 #include <functional>
 #include <map>
 
-// 全局变量存储事件发送器
-static std::unique_ptr<flutter::EventSink<flutter::EncodableValue>> event_sink = nullptr;
+static std::unique_ptr<flutter::EventChannel<flutter::EncodableValue>> g_event_channel = nullptr;
+static std::unique_ptr<flutter::StreamHandler<flutter::EncodableValue>> g_stream_handler = nullptr;
 
 void ReaderMethodCall::RegisterWindowsAPI(flutter::FlutterEngine *engine)
 {
-  // 注册MethodChannel用于方法调用
   auto channel = std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
       engine->messenger(), "com.vber.chicken_farm/win_reader",
       &flutter::StandardMethodCodec::GetInstance());
-
   channel->SetMethodCallHandler([](const auto &call, auto result)
                                 { HandleMethodCall(call, std::move(result)); });
 
-  // 注册EventChannel用于数据回调
-  auto event_channel = std::make_unique<flutter::EventChannel<flutter::EncodableValue>>(
+  g_event_channel = std::make_unique<flutter::EventChannel<flutter::EncodableValue>>(
       engine->messenger(), "com.vber.chicken_farm/win_reader_events",
       &flutter::StandardMethodCodec::GetInstance());
 
-  event_channel->SetStreamHandler(std::make_unique<flutter::StreamHandler<flutter::EncodableValue>>(
-      [engine](const flutter::EncodableValue* arguments,
-               std::unique_ptr<flutter::EventSink<flutter::EncodableValue>>&& sink) -> std::unique_ptr<flutter::StreamHandlerError<flutter::EncodableValue>> {
-        event_sink = std::move(sink);
-        
-        // 设置数据回调
-        ReaderService::GetInstance()->SetDataCallback([](const std::string& data) {
-          if (event_sink) {
-            // 将数据发送到Flutter端
-            event_sink->Success(flutter::EncodableValue(data));
-          }
-        });
+  g_stream_handler =
+      std::make_unique<flutter::StreamHandlerFunctions<flutter::EncodableValue>>(
+          [](const flutter::EncodableValue *arguments,
+             std::unique_ptr<flutter::EventSink<flutter::EncodableValue>> &&events)
+              -> std::unique_ptr<flutter::StreamHandlerError<flutter::EncodableValue>>
+          {
+            auto events_ptr = std::shared_ptr<flutter::EventSink<flutter::EncodableValue>>(std::move(events));
+            ReaderService::GetInstance()->SetDataCallback([events_ptr](const std::string &data)
+                                                          { events_ptr->Success(flutter::EncodableValue(data)); });
+            return nullptr;
+          },
+          [](const flutter::EncodableValue *arguments)
+              -> std::unique_ptr<flutter::StreamHandlerError<flutter::EncodableValue>>
+          {
+            ReaderService::GetInstance()->SetDataCallback(nullptr);
+            return nullptr;
+          });
 
-        return nullptr;
-      },
-      [](const flutter::EncodableValue* arguments) -> std::unique_ptr<flutter::StreamHandlerError<flutter::EncodableValue>> {
-        event_sink = nullptr;
-        return nullptr;
-      }));
+  g_event_channel->SetStreamHandler(std::move(g_stream_handler));
 }
 
 void ReaderMethodCall::HandleMethodCall(
     const flutter::MethodCall<flutter::EncodableValue> &method_call,
     std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
 {
-  // 使用映射表来处理不同的方法调用
-  static std::map<std::string, std::function<void(const flutter::MethodCall<flutter::EncodableValue>&, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>>)>> handlers = {
-    {"scanUsb", [](const flutter::MethodCall<flutter::EncodableValue>& method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
-      // 调用读取器服务的扫描USB方法
-      std::vector<std::string> usb = ReaderService::GetInstance()->ScanUsb();
+  // Use a map to handle different method calls
+  static std::map<std::string, std::function<void(const flutter::MethodCall<flutter::EncodableValue> &method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)>> handlers = {
+      {"scanUsb", [](const flutter::MethodCall<flutter::EncodableValue> &method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
+       {
+         // Call the scan USB method of the reader service
+         std::vector<std::string> usb = ReaderService::GetInstance()->ScanUsb();
 
-      // 创建列表以存储USB设备名称
-      flutter::EncodableList device_list;
-      for (const auto &device : usb)
-      {
-        device_list.push_back(flutter::EncodableValue(device));
-      }
+         // Create a list to store USB device names
+         flutter::EncodableList device_list;
+         for (const auto &device : usb)
+         {
+           device_list.push_back(flutter::EncodableValue(device));
+         }
 
-      // 将USB设备列表返回给Flutter(如果没有找到设备则为空)
-      result->Success(flutter::EncodableValue(device_list));
-    }},
-    {"connect", [](const flutter::MethodCall<flutter::EncodableValue>& method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
-      // 获取参数
-      const auto *arguments = std::get_if<flutter::EncodableMap>(method_call.arguments());
-      if (arguments) {
-        auto device_index_it = arguments->find(flutter::EncodableValue("deviceIndex"));
-        if (device_index_it != arguments->end()) {
-          int device_index = device_index_it->second.LongValue();
-          bool success = ReaderService::GetInstance()->Connect(device_index);
-          result->Success(flutter::EncodableValue(success));
-          return;
-        }
-      }
-      result->Error("Invalid arguments", "Device index not provided");
-    }},
-    {"disconnect", [](const flutter::MethodCall<flutter::EncodableValue>& method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
-      ReaderService::GetInstance()->Disconnect();
-      result->Success();
-    }},
-    {"startRead", [](const flutter::MethodCall<flutter::EncodableValue>& method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
-      bool success = ReaderService::GetInstance()->StartRead();
-      result->Success(flutter::EncodableValue(success));
-    }},
-    {"stopRead", [](const flutter::MethodCall<flutter::EncodableValue>& method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
-      ReaderService::GetInstance()->StopRead();
-      result->Success();
-    }},
-    {"getDeviceInfo", [](const flutter::MethodCall<flutter::EncodableValue>& method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
-      std::string deviceInfo = ReaderService::GetInstance()->GetDeviceInfo();
-      result->Success(flutter::EncodableValue(deviceInfo));
-    }},
-    {"getPower", [](const flutter::MethodCall<flutter::EncodableValue>& method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
-      unsigned char power = 0;
-      bool success = ReaderService::GetInstance()->GetPower(power);
-      if (success) {
-        result->Success(flutter::EncodableValue(static_cast<int>(power)));
-      } else {
-        result->Success(flutter::EncodableValue());
-      }
-    }},
-    {"setPower", [](const flutter::MethodCall<flutter::EncodableValue>& method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
-      const auto *arguments = std::get_if<flutter::EncodableMap>(method_call.arguments());
-      if (arguments) {
-        auto power_it = arguments->find(flutter::EncodableValue("power"));
-        if (power_it != arguments->end()) {
-          int power = power_it->second.LongValue();
-          bool success = ReaderService::GetInstance()->SetPower(static_cast<unsigned char>(power));
-          result->Success(flutter::EncodableValue(success));
-          return;
-        }
-      }
-      result->Error("Invalid arguments", "Power value not provided");
-    }}
+         // Return the list of USB devices to Flutter (empty if no devices found)
+         result->Success(flutter::EncodableValue(device_list));
+       }},
+      {"connect", [](const flutter::MethodCall<flutter::EncodableValue> &method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
+       {
+         const auto *args = std::get_if<flutter::EncodableMap>(method_call.arguments());
+         if (args)
+         {
+           auto deviceIndexIt = args->find(flutter::EncodableValue("deviceIndex"));
+           if (deviceIndexIt != args->end())
+           {
+             int deviceIndex = std::get<int>(deviceIndexIt->second);
+             bool success = ReaderService::GetInstance()->Connect(deviceIndex);
+             result->Success(flutter::EncodableValue(success));
+             return;
+           }
+         }
+         result->Success(flutter::EncodableValue(false));
+       }},
+      {"disconnect", [](const flutter::MethodCall<flutter::EncodableValue> &method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
+       {
+         ReaderService::GetInstance()->Disconnect();
+         result->Success();
+       }},
+      {"startRead", [](const flutter::MethodCall<flutter::EncodableValue> &method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
+       {
+         bool success = ReaderService::GetInstance()->StartRead();
+         result->Success(flutter::EncodableValue(success));
+       }},
+      {"stopRead", [](const flutter::MethodCall<flutter::EncodableValue> &method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
+       {
+         ReaderService::GetInstance()->StopRead();
+         result->Success();
+       }},
+      {"getDeviceInfo", [](const flutter::MethodCall<flutter::EncodableValue> &method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
+       {
+         std::string deviceInfo = ReaderService::GetInstance()->GetDeviceInfo();
+         result->Success(flutter::EncodableValue(deviceInfo));
+       }},
+      {"getPower", [](const flutter::MethodCall<flutter::EncodableValue> &method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
+       {
+         unsigned char power = 0;
+         bool success = ReaderService::GetInstance()->GetPower(power);
+         if (success)
+         {
+           result->Success(flutter::EncodableValue(static_cast<int>(power)));
+         }
+         else
+         {
+           result->Success(flutter::EncodableValue());
+         }
+       }},
+      {"setPower", [](const flutter::MethodCall<flutter::EncodableValue> &method_call, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
+       {
+         const auto *args = std::get_if<flutter::EncodableMap>(method_call.arguments());
+         if (args)
+         {
+           auto powerIt = args->find(flutter::EncodableValue("power"));
+           if (powerIt != args->end())
+           {
+             int power = std::get<int>(powerIt->second);
+             bool success = ReaderService::GetInstance()->SetPower(static_cast<unsigned char>(power));
+             result->Success(flutter::EncodableValue(success));
+             return;
+           }
+         }
+         result->Success(flutter::EncodableValue(false));
+       }}
+      // More method handlers can be added here
+      // {"methodName", [](auto result) { /* Handle method */ }}
   };
 
-  // 查找并执行对应的方法处理器
+  // Find and execute the corresponding method handler
   auto handler = handlers.find(method_call.method_name());
-  if (handler != handlers.end()) {
+  if (handler != handlers.end())
+  {
     handler->second(method_call, std::move(result));
-  } else {
+  }
+  else
+  {
     result->NotImplemented();
   }
 }

+ 6 - 3
UI/CF.APP/chicken_farm/windows/runner/reader/reader_service.cpp

@@ -9,8 +9,11 @@
 
 void PrintLogMessage(const std::string &message)
 {
-  CreateAndAttachConsole();
-  std::cout << message << std::endl;
+  if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent())
+  {
+    CreateAndAttachConsole();
+    std::cout << message << std::endl;
+  }
 }
 
 ReaderService *ReaderService::GetInstance()
@@ -132,7 +135,7 @@ bool ReaderService::StartRead()
                                                    {
     while (timer_running_) {
       SendTagList();
-      std::this_thread::sleep_for(std::chrono::seconds(1));
+      std::this_thread::sleep_for(std::chrono::milliseconds(500));
     } });
 
   // // Start a separate thread to automatically stop reading after 3 seconds

BIN
UI/CF.APP/chicken_farm/windows/runner/resources/app_icon.ico