chengc 2 тижнів тому
батько
коміт
d961ba3492

+ 1 - 0
assets/i18n/en.json

@@ -104,6 +104,7 @@
     "draftSavedToast": "Draft saved",
     "saveFailed": "Save failed",
     "submitSuccess": "submitSuccess",
+    "submitFailed": "Submit Failed",
     "submitFailedRetry": "submitFailedRetry",
     "noCostTypeData": "No cost type data. Please add cost types in ERP first.",
     "submittedAwaitingApproval": "submittedAwaitingApproval",

+ 1 - 0
assets/i18n/zh_CN.json

@@ -101,6 +101,7 @@
     "draftSavedToast": "已保存为草稿",
     "saveFailed": "保存失败",
     "submitSuccess": "提交成功",
+    "submitFailed": "提交失败",
     "submitFailedRetry": "提交失败,请稍后重试",
     "submittedAwaitingApproval": "已提交,等待审批",
     "published": "公告发布成功",

+ 1 - 0
assets/i18n/zh_TW.json

@@ -104,6 +104,7 @@
     "draftSavedToast": "已保存為草稿",
     "saveFailed": "保存失敗",
     "submitSuccess": "提交成功",
+    "submitFailed": "提交失敗",
     "submitFailedRetry": "提交失敗,請稍后重試",
     "noCostTypeData": "暫無費用類型數據,請先前往ERP新增費用類型數據再嘗試",
     "submittedAwaitingApproval": "已提交,等待審批",

+ 26 - 2
lib/features/expense/expense_create_page.dart

@@ -5,7 +5,9 @@ import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter_riverpod/flutter_riverpod.dart';
 import 'package:tdesign_flutter/tdesign_flutter.dart';
+import 'package:dio/dio.dart';
 import 'package:go_router/go_router.dart';
+import '../../core/network/api_exception.dart';
 import '../../core/utils/responsive.dart';
 import '../../shared/widgets/form_section.dart';
 import '../../shared/widgets/form_field_row.dart';
@@ -1097,16 +1099,38 @@ class _ExpenseCreatePageState extends ConsumerState<ExpenseCreatePage> {
             );
             GoRouter.of(context).go('/expense/list');
           }
-        } catch (_) {
+        } catch (e) {
           if (mounted) {
             LoadingDialog.hide(context);
-            TDToast.showFail(l10n.get('submitFailedRetry'), context: context);
+            WidgetsBinding.instance.addPostFrameCallback((_) {
+              if (mounted) _showSubmitError(e, l10n);
+            });
           }
         }
       },
     );
   }
 
+  void _showSubmitError(Object e, AppLocalizations l10n) {
+    final message = _extractErrorMessage(e) ?? l10n.get('submitFailedRetry');
+    showGeneralDialog(
+      context: context,
+      pageBuilder: (ctx, animation, secondaryAnimation) => TDConfirmDialog(
+        title: l10n.get('submitFailed'),
+        content: message,
+        buttonStyle: TDDialogButtonStyle.text,
+      ),
+    );
+  }
+
+  String? _extractErrorMessage(Object e) {
+    if (e is DioException) {
+      if (e.error is ApiException) return (e.error as ApiException).message;
+      if (e.error is NetworkException) return (e.error as NetworkException).message;
+    }
+    return null;
+  }
+
   Future<void> _showAddDetailDialog(
     ExpenseCreateController controller, {
     int? editIndex,

+ 26 - 2
lib/features/expense_apply/expense_apply_create_page.dart

@@ -8,6 +8,8 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 import '../../core/i18n/app_localizations.dart';
 import '../../core/navigation/host_app_channel.dart';
 import '../../core/storage/draft_storage.dart';
+import 'package:dio/dio.dart';
+import '../../core/network/api_exception.dart';
 import '../../shared/widgets/action_bar.dart';
 import '../../shared/widgets/loading_dialog.dart';
 import '../../shared/widgets/form_section.dart';
@@ -985,16 +987,38 @@ class _ExpenseApplyCreatePageState extends ConsumerState<ExpenseApplyCreatePage>
             );
             GoRouter.of(context).go('/expense-apply/list');
           }
-        } catch (_) {
+        } catch (e) {
           if (mounted) {
             LoadingDialog.hide(context);
-            TDToast.showFail(l10n.get('submitFailedRetry'), context: context);
+            WidgetsBinding.instance.addPostFrameCallback((_) {
+              if (mounted) _showSubmitError(e, l10n);
+            });
           }
         }
       },
     );
   }
 
+  void _showSubmitError(Object e, AppLocalizations l10n) {
+    final message = _extractErrorMessage(e) ?? l10n.get('submitFailedRetry');
+    showGeneralDialog(
+      context: context,
+      pageBuilder: (ctx, animation, secondaryAnimation) => TDConfirmDialog(
+        title: l10n.get('submitFailed'),
+        content: message,
+        buttonStyle: TDDialogButtonStyle.text,
+      ),
+    );
+  }
+
+  String? _extractErrorMessage(Object e) {
+    if (e is DioException) {
+      if (e.error is ApiException) return (e.error as ApiException).message;
+      if (e.error is NetworkException) return (e.error as NetworkException).message;
+    }
+    return null;
+  }
+
   Map<String, dynamic> _buildSubmitData() {
     // 紧急程度映射:normal→1, urgent→2, critical→3
     String priority;