瀏覽代碼

Update 优化页面UI,文件路径适配

Yue 1 月之前
父節點
當前提交
bb6917cb9c

+ 76 - 22
UI/CF.APP/chicken_farm/lib/core/db/sqlite_manager.dart

@@ -7,6 +7,9 @@ import 'package:path/path.dart';
 import 'package:sqflite/sqflite.dart';
 import 'package:path_provider/path_provider.dart';
 
+// 为桌面平台添加 ffi 支持
+import 'package:sqflite_common_ffi/sqflite_ffi.dart';
+
 class SqliteManager {
   // 单例模式
   static final SqliteManager _instance = SqliteManager._internal();
@@ -28,6 +31,14 @@ class SqliteManager {
   /// [dbName] 数据库名称
   /// [version] 数据库版本(默认1)
   Future<void> init({required String dbName, int version = 1}) async {
+    // 在桌面平台上初始化 ffi 数据库工厂
+    if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) {
+      // 初始化 FFI 数据库工厂
+      sqfliteFfiInit();
+      // 设置数据库工厂为 FFI 工厂
+      databaseFactory = databaseFactoryFfi;
+    }
+
     _dbName = dbName;
     _dbVersion = version;
     if (TableConfig.defaultConfigs().isEmpty) {
@@ -43,30 +54,56 @@ class SqliteManager {
     if (_dbName == null) {
       throw Exception('请先调用 init 方法初始化数据库配置');
     }
-    // 获取数据库路径
-    // Directory documentsDir = await getApplicationDocumentsDirectory();
-    // String dbPath = join(documentsDir.path, _dbName!);
 
-    // 1. 获取应用文档根目录
-    Directory? documentsDir = await getExternalStorageDirectory();
-    // logger.d('应用文档根目录1:${documentsDir?.path}');
-    documentsDir ??= await getApplicationDocumentsDirectory();
-    // logger.d('应用文档根目录2:${documentsDir.path}');
-    // 2. 拼接自定义的 db 子文件夹路径
-    String dbFolderPath = join(documentsDir.path, 'db');
-    // 3. 检查并创建 db 文件夹(不存在则创建)
-    Directory dbFolder = Directory(dbFolderPath);
-    if (!await dbFolder.exists()) {
-      await dbFolder.create(recursive: true); // recursive: true 支持多级文件夹创建
-      logger.d('已创建 db 文件夹:$dbFolderPath');
-    }
-
-    // 4. 拼接最终的数据库文件路径(db文件夹 + 数据库名)
+    String dbFolderPath = await _dbFolderPath();
     String dbPath = join(dbFolderPath, _dbName!);
     logger.d('数据库文件路径:$dbPath');
     return dbPath;
   }
 
+  Future<String> _dbFolderPath() async {
+    String dbFolderPath = "";
+    if (Platform.isAndroid) {
+      // 1. 获取应用文档根目录
+      Directory? documentsDir = await getExternalStorageDirectory();
+      // logger.d('应用文档根目录1:${documentsDir?.path}');
+      documentsDir ??= await getApplicationDocumentsDirectory();
+      // logger.d('应用文档根目录2:${documentsDir.path}');
+      // 2. 拼接自定义的 db 子文件夹路径
+      dbFolderPath = join(documentsDir.path, 'db');
+      // 3. 检查并创建 db 文件夹(不存在则创建)
+      Directory dbFolder = Directory(dbFolderPath);
+      if (!await dbFolder.exists()) {
+        await dbFolder.create(recursive: true); // recursive: true 支持多级文件夹创建
+        logger.d('已创建 db 文件夹:$dbFolderPath');
+      }
+    } else if (Platform.isIOS) {
+    } else if (Platform.isMacOS || Platform.isLinux) {
+      // macOS 和 Linux 使用文档目录
+      Directory documentsDir = await getApplicationDocumentsDirectory();
+      dbFolderPath = join(documentsDir.path, 'db');
+      // 检查并创建 db 文件夹(不存在则创建)
+      Directory dbFolder = Directory(dbFolderPath);
+      if (!await dbFolder.exists()) {
+        await dbFolder.create(recursive: true);
+        logger.d('已创建 db 文件夹:$dbFolderPath');
+      }
+    } else if (Platform.isWindows) {
+      // Windows 使用应用运行目录下的Data目录
+      dbFolderPath = join(Directory.current.path, 'Data');
+      // 检查并创建 Data 文件夹(不存在则创建)
+      Directory dbFolder = Directory(dbFolderPath);
+      if (!await dbFolder.exists()) {
+        await dbFolder.create(recursive: true);
+        logger.d('已创建 Data 文件夹:$dbFolderPath');
+      }
+    } else {
+      throw Exception('不支持的操作系统');
+    }
+
+    return dbFolderPath;
+  }
+
   /// 内部方法:获取数据库连接(自动创建/升级)
   Future<Database> _getDatabase() async {
     if (_dbPath.isEmpty) {
@@ -334,10 +371,27 @@ class SqliteManager {
       throw Exception('请先调用 init 方法初始化数据库配置');
     }
     await closeDb();
-    Directory documentsDir = await getApplicationDocumentsDirectory();
-    String dbPath = join(documentsDir.path, _dbName!);
-    await deleteDatabase(dbPath);
-    logger.d('数据库文件已删除: $dbPath');
+
+    // // 使用正确的平台相关路径删除数据库
+    // String dbPath;
+    // if (Platform.isAndroid) {
+    //   Directory? documentsDir = await getExternalStorageDirectory();
+    //   documentsDir ??= await getApplicationDocumentsDirectory();
+    //   dbPath = join(documentsDir.path, 'db', _dbName!);
+    // } else if (Platform.isIOS) {
+    //   Directory documentsDir = await getApplicationDocumentsDirectory();
+    //   dbPath = join(documentsDir.path, _dbName!);
+    // } else if (Platform.isMacOS || Platform.isLinux) {
+    //   Directory documentsDir = await getApplicationDocumentsDirectory();
+    //   dbPath = join(documentsDir.path, 'db', _dbName!);
+    // } else if (Platform.isWindows) {
+    //   dbPath = join(Directory.current.path, 'Data', _dbName!);
+    // } else {
+    //   throw Exception('不支持的操作系统');
+    // }
+
+    await databaseFactory.deleteDatabase(_dbPath);
+    logger.d('数据库文件已删除: $_dbPath');
   }
 
   /// 清空表数据

+ 46 - 36
UI/CF.APP/chicken_farm/lib/core/utils/excel_export_util.dart

@@ -179,48 +179,58 @@ class ExcelExportUtil {
 
   /// 安全获取系统公共下载目录(修复Android路径问题)
   static Future<Directory> _getSafeDownloadDirectory() async {
-    // 获取下载目录
-    Directory? downloadDir = await getDownloadsDirectory();
+    Directory? downloadDir;
+    if (Platform.isAndroid) {
+      // 获取下载目录
+      downloadDir = await getDownloadsDirectory();
+      // 安全地处理 Android 下载目录路径
+      if (downloadDir != null) {
+        try {
+          // 安全地访问父目录层级
+          Directory? parentDir = downloadDir;
+          int levelsUp = 0;
+          const maxLevelsUp = 4; // 最多向上4级目录
 
-    // 安全地处理 Android 下载目录路径
-    if (downloadDir != null) {
-      try {
-        // 安全地访问父目录层级
-        Directory? parentDir = downloadDir;
-        int levelsUp = 0;
-        const maxLevelsUp = 4; // 最多向上4级目录
+          // 尝试向上访问最多4级目录
+          while (parentDir != null && levelsUp <= maxLevelsUp) {
+            if (parentDir.parent.path != parentDir.path) {
+              parentDir = parentDir.parent;
+              levelsUp++;
+            } else {
+              // 已经到达根目录,无法继续向上
+              break;
+            }
+          }
 
-        // 尝试向上访问最多4级目录
-        while (parentDir != null && levelsUp <= maxLevelsUp) {
-          if (parentDir.parent.path != parentDir.path) {
-            parentDir = parentDir.parent;
-            levelsUp++;
+          // 如果成功向上访问了足够的层级,则构造新的下载目录路径
+          if (levelsUp >= 4) {
+            downloadDir = Directory('${parentDir!.path}/Download');
+            // logger.i("重构后的 Android 下载目录: ${downloadDir.path}");
           } else {
-            // 已经到达根目录,无法继续向上
-            break;
+            // 向上访问层级不够,使用原始下载目录
+            logger.w("无法安全重构下载目录路径,使用原始下载目录: ${downloadDir.path}");
           }
+        } catch (e) {
+          // 处理异常
+          logger.e("获取下载目录异常:$e");
+          downloadDir = await getExternalStorageDirectory();
+          downloadDir = Directory('${downloadDir?.path}/Download');
         }
-
-        // 如果成功向上访问了足够的层级,则构造新的下载目录路径
-        if (levelsUp >= 4) {
-          downloadDir = Directory('${parentDir!.path}/Download');
-          // logger.i("重构后的 Android 下载目录: ${downloadDir.path}");
-        } else {
-          // 向上访问层级不够,使用原始下载目录
-          logger.w("无法安全重构下载目录路径,使用原始下载目录: ${downloadDir.path}");
-        }
-      } catch (e) {
-        // 处理异常
-        logger.e("获取下载目录异常:$e");
-        downloadDir = await getExternalStorageDirectory();
-        downloadDir = Directory('${downloadDir?.path}/Download');
       }
-    }
-
-    // 确保目录存在
-    downloadDir ??= await getApplicationDocumentsDirectory();
-    if (!await downloadDir.exists()) {
-      await downloadDir.create(recursive: true);
+      // 确保目录存在
+      downloadDir ??= await getApplicationDocumentsDirectory();
+      if (!await downloadDir.exists()) {
+        await downloadDir.create(recursive: true);
+      }
+    } else if (Platform.isIOS) {
+      // iOS 获取下载目录
+      downloadDir = await getApplicationDocumentsDirectory();
+      downloadDir = Directory('${downloadDir.path}/Download');
+    } else if (Platform.isWindows || Platform.isMacOS) {
+      // Windows 获取下载目录
+      downloadDir = Directory.current;
+    } else {
+      downloadDir = await getApplicationDocumentsDirectory();
     }
 
     // logger.i("最终下载目录:${downloadDir.path}");

+ 44 - 13
UI/CF.APP/chicken_farm/lib/pages/account/config_dialog.dart

@@ -1,5 +1,6 @@
 import 'package:chicken_farm/core/api/api_client.dart';
 import 'package:chicken_farm/core/config/app_config.dart';
+import 'package:chicken_farm/core/utils/toast.dart';
 import 'package:flutter/material.dart';
 
 class ConfigDialog extends StatefulWidget {
@@ -13,6 +14,7 @@ class _ConfigDialogState extends State<ConfigDialog> {
   final _formKey = GlobalKey<FormState>();
   late TextEditingController _baseUrlController;
   late TextEditingController _clientIdController;
+  bool _isSaving = false; // 添加保存状态标识
 
   @override
   void initState() {
@@ -76,31 +78,60 @@ class _ConfigDialogState extends State<ConfigDialog> {
       ),
       actions: [
         TextButton(
-          onPressed: () {
+          onPressed: _isSaving ? null : () {
+            // 保存过程中禁用取消按钮
             Navigator.of(context).pop();
           },
           child: const Text('取消'),
         ),
         ElevatedButton(
-          onPressed: () async {
+          onPressed: _isSaving ? null : () async {
+            // 保存过程中禁用保存按钮
             if (_formKey.currentState!.validate()) {
-              // 保存配置
-              await AppConfig.save(
-                _baseUrlController.text.trim(),
-                _clientIdController.text.trim(),
-              );
+              // 保存前取消当前焦点,避免输入法相关问题
+              FocusManager.instance.primaryFocus?.unfocus();
+              
+              setState(() {
+                _isSaving = true;
+              });
+              
+              try {
+                // 稍微延迟一下确保焦点完全失去
+                await Future.delayed(const Duration(milliseconds: 50));
+                
+                // 保存配置
+                await AppConfig.save(
+                  _baseUrlController.text.trim(),
+                  _clientIdController.text.trim(),
+                );
 
-              // 重新初始化API客户端
-              ApiClient.clearDio();
+                // 重新初始化API客户端
+                ApiClient.clearDio();
 
-              if (context.mounted) {
-                Navigator.of(context).pop(true);
+                if (context.mounted) {
+                  Navigator.of(context).pop(true);
+                  ToastUtil.success('配置保存成功');
+                }
+              } catch (e) {
+                // 发生错误时显示提示
+                if (context.mounted) {
+                  ToastUtil.error('保存配置失败: ${e.toString()}');
+                }
+                setState(() {
+                  _isSaving = false;
+                });
               }
             }
           },
-          child: const Text('保存'),
+          child: _isSaving 
+              ? const SizedBox(
+                  width: 20,
+                  height: 20,
+                  child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white),
+                )
+              : const Text('保存'),
         ),
       ],
     );
   }
-}
+}

+ 12 - 2
UI/CF.APP/chicken_farm/lib/pages/home/menu_buttons.dart

@@ -1,3 +1,5 @@
+import 'dart:io' show Platform;
+
 import 'package:chicken_farm/stores/menu_store.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -10,7 +12,12 @@ class MenuButtons extends ConsumerWidget {
   Widget build(BuildContext context, WidgetRef ref) {
     final visibleMenuItems = MenuStore.getVisibleMenuItems(ref);
     final screenWidth = MediaQuery.of(context).size.width;
-    final buttonWidth = screenWidth * 0.8;
+    double buttonWidth = screenWidth * 0.8;
+    buttonWidth = Platform.isAndroid
+        ? buttonWidth
+        : buttonWidth > 350
+        ? 350
+        : buttonWidth;
 
     return SingleChildScrollView(
       child: Center(
@@ -35,7 +42,10 @@ class MenuButtons extends ConsumerWidget {
                   label: Text(visibleMenuItems[i].name),
                 ),
               ),
-              if (i < visibleMenuItems.length - 1) const SizedBox(height: 20),
+              if (i < visibleMenuItems.length - 1) ...[
+                const SizedBox(height: 20.00),
+                if (!Platform.isAndroid) ...[const SizedBox(height: 10.00)],
+              ],
             ],
           ],
         ),

+ 47 - 48
UI/CF.APP/chicken_farm/lib/pages/home/profile.dart

@@ -1,3 +1,5 @@
+import 'dart:io';
+
 import 'package:chicken_farm/core/config/app_config.dart';
 import 'package:chicken_farm/stores/auth_store.dart';
 import 'package:flutter/material.dart';
@@ -53,56 +55,53 @@ class ProfilePage extends ConsumerWidget {
     }
 
     return Scaffold(
+      key: super.key,
       body: Padding(
-        padding: const EdgeInsets.all(16.0),
-        child: Column(
-          crossAxisAlignment: CrossAxisAlignment.start,
-          children: [
-            const SizedBox(height: 20),
-            if (authState.state == AuthState.authenticated &&
-                authState.user != null) ...[
-              Expanded(
-                child: SingleChildScrollView(
-                  child: Column(
-                    crossAxisAlignment: CrossAxisAlignment.start,
-                    children: [
-                      if (AppConfig.isOffline) ...[
-                        GestureDetector(
-                          onTap: handleUserCardTap,
-                          child: UserInfoCard(user: authState.user!),
-                        ),
-                        const SizedBox(height: 20),
-                        if (showDictConfig) ...[
-                          const DictConfigButton(),
-                          const SizedBox(height: 20),
-                        ],
-                        const ClearCacheButton(),
-                        const SizedBox(height: 20),
-                        const ExportDataButton(),
-                        const SizedBox(height: 20),
-                      ] else ...[
-                        UserInfoCard(user: authState.user!),
-                        const UploadDataButton(),
-                        const SizedBox(height: 20),
-                        const ClearCacheButton(),
-                        const SizedBox(height: 20),
-                        const ConfigButton(),
-                        const SizedBox(height: 20),
-                        // const RfidConfigButton(),
-                        // const SizedBox(height: 20),
-                        // const ScannerLightButton(),
-                        // const SizedBox(height: 20),
-                        const LogoutButton(),
-                        const SizedBox(height: 20),
-                      ],
-                    ],
+        padding: const EdgeInsets.all(20),
+
+        child: SizedBox(
+          width: Platform.isAndroid ? MediaQuery.of(context).size.width : 300,
+          height: MediaQuery.of(context).size.height,
+          child: SingleChildScrollView(
+            child: Column(
+              crossAxisAlignment: CrossAxisAlignment.center,
+              mainAxisAlignment: MainAxisAlignment.center,
+              children: [
+                if (AppConfig.isOffline) ...[
+                  GestureDetector(
+                    onTap: handleUserCardTap,
+                    child: UserInfoCard(user: authState.user!),
                   ),
-                ),
-              ),
-            ] else ...[
-              const Center(child: Text('加载中...')),
-            ],
-          ],
+                  const SizedBox(height: 20),
+                  if (showDictConfig) ...[
+                    const DictConfigButton(),
+                    const SizedBox(height: 20),
+                  ],
+                  const ClearCacheButton(),
+                  const SizedBox(height: 20),
+                  const ExportDataButton(),
+                  const SizedBox(height: 20),
+                ] else ...[
+                  UserInfoCard(user: authState.user!),
+                  const SizedBox(height: 20),
+                  if (AppConfig.isPda) ...[
+                    const UploadDataButton(),
+                    const SizedBox(height: 20),
+                  ],
+                  const ClearCacheButton(),
+                  const SizedBox(height: 20),
+                  const ConfigButton(),
+                  const SizedBox(height: 20),
+                  // const RfidConfigButton(),
+                  // const SizedBox(height: 20),
+                  // const ScannerLightButton(),
+                  // const SizedBox(height: 20),
+                  const LogoutButton(),
+                  const SizedBox(height: 20),
+                ],
+              ],
+            ),
+          ),
         ),
       ),
     );

+ 22 - 15
UI/CF.APP/chicken_farm/lib/pages/upload/upload_page.dart

@@ -100,10 +100,12 @@ class _UploadPageState extends State<UploadPage> with WidgetsBindingObserver {
     logger.d('准备上传');
     // 检查网络
     if (!await ServiceChecker().checkService()) {
-      setState(() {
-        _isUploading = false;
-        _status = UploadStatus.error; // 使用状态常量
-      });
+      if (mounted) {
+        setState(() {
+          _isUploading = false;
+          _status = UploadStatus.error; // 使用状态常量
+        });
+      }
       return;
     }
     // 开始上传
@@ -113,22 +115,27 @@ class _UploadPageState extends State<UploadPage> with WidgetsBindingObserver {
       await _uploadService.startUpload();
     } catch (e) {
       logger.e(e);
-      setState(() {
-        _isUploading = false;
-        _status = UploadStatus.exception; // 使用状态常量
-      });
+      if (mounted) {
+        setState(() {
+          _isUploading = false;
+          _status = UploadStatus.exception; // 使用状态常量
+        });
+      }
     }
   }
 
   void _registerUploadCallbacks() {
     _uploadService.showUpload();
-    setState(() {
-      _isUploading = true;
-      _status = UploadStatus.uploading; // 使用状态常量
-      _uploadedOperations = 0;
-      _totalOperations = 0;
-      _uploadCompleted = false;
-    });
+    if (mounted) {
+      setState(() {
+        _isUploading = true;
+        _status = UploadStatus.uploading; // 使用状态常量
+        _uploadedOperations = 0;
+        _totalOperations = 0;
+        _uploadCompleted = false;
+      });
+    }
+
     int t = DateTime.now().millisecondsSinceEpoch;
     logger.d('开始上传,$t');
     // 注册回调

+ 10 - 7
UI/CF.APP/chicken_farm/lib/test/test.dart

@@ -6,10 +6,9 @@ class Test {
     for (int i = 0; i < 2; i++) {
       apis.breeding.submitApi
           .bindChicken({
-            'rfids': ['test_1$i', 'test_2$i', 'test_3$i'],
+            'electronicIds': ['test_1$i', 'test_2$i', 'test_3$i'],
             'batchNum': '666',
-            'familyId': '888',
-            'date': '2025-12-11',
+            'familyNum': '888',
           })
           .then(
             (r) => {
@@ -21,7 +20,7 @@ class Test {
           );
       apis.breeding.submitApi
           .cageChange({
-            'rfids': ['test_1$i', 'test_2$i', 'test_3$i'],
+            'electronicIds': ['test_1$i', 'test_2$i', 'test_3$i'],
             'targetCage': '23333',
             'date': '2025-12-11',
           })
@@ -34,7 +33,11 @@ class Test {
             },
           );
       apis.breeding.submitApi
-          .weight({'rfid': 'test_$i', 'weight': '1.23', 'date': '2025-12-11'})
+          .weight({
+            'electronicId': 'test_$i',
+            'weight': '1.23',
+            'date': '2025-12-11',
+          })
           .then(
             (r) => {
               if (r.success)
@@ -45,7 +48,7 @@ class Test {
           );
       apis.breeding.submitApi
           .cull({
-            'rfid': 'test_$i',
+            'electronicId': 'test_$i',
             'cullReason': '1',
             'disposalMethod': '1',
             'date': '2025-12-11',
@@ -60,7 +63,7 @@ class Test {
           );
       apis.breeding.submitApi
           .batchCull({
-            'rfids': ['test_1$i', 'test_2$i', 'test_3$i'],
+            'electronicIds': ['test_1$i', 'test_2$i', 'test_3$i'],
             'cullReason': '1',
             'disposalMethod': '1',
             'date': '2025-12-11',