| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |  | import 'dart:math'; | 
					
						
							|  |  |  |  | import 'package:flutter/material.dart'; | 
					
						
							|  |  |  |  | import 'package:package_info_plus/package_info_plus.dart'; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | int getRandomWithNum(int min, int max) { | 
					
						
							|  |  |  |  |   final random = Random(); | 
					
						
							|  |  |  |  |   return random.nextInt(max) + min; // 生成随机数
 | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | double screenWidth(BuildContext context) { | 
					
						
							|  |  |  |  |   double screenWidth = MediaQuery.of(context).size.width; | 
					
						
							|  |  |  |  |   return screenWidth; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-17 16:10:46 +08:00
										 |  |  |  | Future<T?> pushPage<T>(Widget page, BuildContext context) { | 
					
						
							|  |  |  |  |   return Navigator.push<T>( | 
					
						
							|  |  |  |  |     context, | 
					
						
							|  |  |  |  |     MaterialPageRoute(builder: (_) => page), | 
					
						
							|  |  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | void present(Widget page, BuildContext context) { | 
					
						
							| 
									
										
										
										
											2025-07-28 16:50:40 +08:00
										 |  |  |  |   Navigator.of(context).push( | 
					
						
							|  |  |  |  |     PageRouteBuilder( | 
					
						
							|  |  |  |  |       pageBuilder: (context, animation, secondaryAnimation) => page, | 
					
						
							|  |  |  |  |       transitionDuration: Duration.zero, | 
					
						
							|  |  |  |  |       reverseTransitionDuration: Duration.zero, | 
					
						
							|  |  |  |  |     ), | 
					
						
							|  |  |  |  |   ); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | void presentOpaque(Widget page, BuildContext context) { | 
					
						
							|  |  |  |  |   Navigator.of(context).push( | 
					
						
							|  |  |  |  |     PageRouteBuilder( | 
					
						
							|  |  |  |  |       opaque: false, // 允许下层透出
 | 
					
						
							|  |  |  |  |       barrierColor: Colors.black.withOpacity(0.5), //路由遮罩色
 | 
					
						
							|  |  |  |  |       pageBuilder: (context, animation, secondaryAnimation) => page, | 
					
						
							|  |  |  |  |       transitionDuration: Duration.zero, | 
					
						
							|  |  |  |  |       reverseTransitionDuration: Duration.zero, | 
					
						
							|  |  |  |  |     ), | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |  |   ); | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-07-28 16:50:40 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  | class FocusHelper { | 
					
						
							|  |  |  |  |   static final FocusNode _emptyNode = FocusNode(); | 
					
						
							|  |  |  |  |   /// 延迟一帧后再移交焦点,避免不生效的问题
 | 
					
						
							|  |  |  |  |   static void clearFocus(BuildContext context) { | 
					
						
							|  |  |  |  |     WidgetsBinding.instance.addPostFrameCallback((_) { | 
					
						
							|  |  |  |  |       FocusScope.of(context).requestFocus(_emptyNode); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-07-15 08:32:50 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |  | /// 文本样式工具类,返回 Text Widget
 | 
					
						
							|  |  |  |  | class HhTextStyleUtils { | 
					
						
							|  |  |  |  |   /// 主要标题,返回 Text
 | 
					
						
							|  |  |  |  |   /// [text]: 文本内容
 | 
					
						
							|  |  |  |  |   /// [color]: 文本颜色,默认黑色
 | 
					
						
							|  |  |  |  |   /// [fontSize]: 字体大小,默认16.0
 | 
					
						
							|  |  |  |  |   /// [bold]: 是否加粗,默认true
 | 
					
						
							|  |  |  |  |   static Text mainTitle( | 
					
						
							| 
									
										
										
										
											2025-07-15 08:32:50 +08:00
										 |  |  |  |     String text, { | 
					
						
							|  |  |  |  |     Color color = Colors.black, | 
					
						
							|  |  |  |  |     double fontSize = 16.0, | 
					
						
							|  |  |  |  |     bool bold = true, | 
					
						
							|  |  |  |  |   }) { | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |  |     return Text( | 
					
						
							|  |  |  |  |       text, | 
					
						
							|  |  |  |  |       style: TextStyle( | 
					
						
							|  |  |  |  |         color: color, | 
					
						
							|  |  |  |  |         fontSize: fontSize, | 
					
						
							|  |  |  |  |         fontWeight: bold ? FontWeight.bold : FontWeight.normal, | 
					
						
							|  |  |  |  |       ), | 
					
						
							|  |  |  |  |     ); | 
					
						
							|  |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-07-15 08:32:50 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   static TextStyle secondaryTitleStyle = TextStyle( | 
					
						
							|  |  |  |  |     color: Colors.black54, | 
					
						
							|  |  |  |  |     fontSize: 15.0, | 
					
						
							|  |  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   /// 次要标题,返回 Text
 | 
					
						
							|  |  |  |  |   /// [text]: 文本内容
 | 
					
						
							|  |  |  |  |   /// [color]: 文本颜色,默认深灰
 | 
					
						
							|  |  |  |  |   /// [fontSize]: 字体大小,默认14.0
 | 
					
						
							|  |  |  |  |   /// [bold]: 是否加粗,默认false
 | 
					
						
							|  |  |  |  |   static Text secondaryTitle( | 
					
						
							| 
									
										
										
										
											2025-07-15 08:32:50 +08:00
										 |  |  |  |     String text, { | 
					
						
							|  |  |  |  |     Color color = Colors.black54, | 
					
						
							|  |  |  |  |     double fontSize = 14.0, | 
					
						
							|  |  |  |  |     bool bold = false, | 
					
						
							|  |  |  |  |   }) { | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |  |     return Text( | 
					
						
							|  |  |  |  |       text, | 
					
						
							|  |  |  |  |       style: TextStyle( | 
					
						
							|  |  |  |  |         color: color, | 
					
						
							|  |  |  |  |         fontSize: fontSize, | 
					
						
							|  |  |  |  |         fontWeight: bold ? FontWeight.bold : FontWeight.normal, | 
					
						
							|  |  |  |  |       ), | 
					
						
							|  |  |  |  |     ); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /// 小文字,返回 Text
 | 
					
						
							|  |  |  |  |   /// [text]: 文本内容
 | 
					
						
							|  |  |  |  |   /// [color]: 文本颜色,默认灰色
 | 
					
						
							|  |  |  |  |   /// [fontSize]: 字体大小,默认12.0
 | 
					
						
							|  |  |  |  |   /// [bold]: 是否加粗,默认false
 | 
					
						
							|  |  |  |  |   static Text smallText( | 
					
						
							| 
									
										
										
										
											2025-07-15 08:32:50 +08:00
										 |  |  |  |     String text, { | 
					
						
							|  |  |  |  |     Color color = Colors.black54, | 
					
						
							|  |  |  |  |     double fontSize = 12.0, | 
					
						
							|  |  |  |  |     bool bold = false, | 
					
						
							|  |  |  |  |   }) { | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |  |     return Text( | 
					
						
							|  |  |  |  |       text, | 
					
						
							|  |  |  |  |       style: TextStyle( | 
					
						
							|  |  |  |  |         color: color, | 
					
						
							|  |  |  |  |         fontSize: fontSize, | 
					
						
							|  |  |  |  |         fontWeight: bold ? FontWeight.bold : FontWeight.normal, | 
					
						
							|  |  |  |  |       ), | 
					
						
							|  |  |  |  |     ); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /// 版本信息模型类
 | 
					
						
							|  |  |  |  | class AppVersionInfo { | 
					
						
							|  |  |  |  |   final String versionName; // 版本名称(如 1.0.0)
 | 
					
						
							|  |  |  |  |   final String buildNumber; // 构建号(如 1)
 | 
					
						
							|  |  |  |  |   final String fullVersion; // 完整版本(如 1.0.0+1)
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   AppVersionInfo({ | 
					
						
							|  |  |  |  |     required this.versionName, | 
					
						
							|  |  |  |  |     required this.buildNumber, | 
					
						
							|  |  |  |  |     required this.fullVersion, | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   @override | 
					
						
							|  |  |  |  |   String toString() { | 
					
						
							|  |  |  |  |     return fullVersion; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // 获取应用版本信息的方法
 | 
					
						
							|  |  |  |  | Future<AppVersionInfo> getAppVersion() async { | 
					
						
							|  |  |  |  |   try { | 
					
						
							|  |  |  |  |     final packageInfo = await PackageInfo.fromPlatform(); | 
					
						
							|  |  |  |  |     return AppVersionInfo( | 
					
						
							|  |  |  |  |       versionName: packageInfo.version, | 
					
						
							|  |  |  |  |       buildNumber: packageInfo.buildNumber, | 
					
						
							|  |  |  |  |       fullVersion: '${packageInfo.version}+${packageInfo.buildNumber}', | 
					
						
							|  |  |  |  |     ); | 
					
						
							|  |  |  |  |   } catch (e) { | 
					
						
							|  |  |  |  |     // 获取失败时返回默认值
 | 
					
						
							|  |  |  |  |     return AppVersionInfo( | 
					
						
							|  |  |  |  |       versionName: '1.0.0', | 
					
						
							|  |  |  |  |       buildNumber: '1', | 
					
						
							|  |  |  |  |       fullVersion: '1.0.0+0', | 
					
						
							|  |  |  |  |     ); | 
					
						
							|  |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-07-15 08:32:50 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /// ------------------------------------------------------
 | 
					
						
							|  |  |  |  | /// 全局会话管理
 | 
					
						
							|  |  |  |  | /// ------------------------------------------------------
 | 
					
						
							|  |  |  |  | class SessionService { | 
					
						
							|  |  |  |  |   SessionService._(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   static final SessionService instance = SessionService._(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   String? corpinfoId; | 
					
						
							|  |  |  |  |   String? loginUserId; | 
					
						
							|  |  |  |  |   Map<String, dynamic>? loginUser; | 
					
						
							|  |  |  |  |   String? deptId; | 
					
						
							|  |  |  |  |   String? deptLevel; | 
					
						
							|  |  |  |  |   String? postId; | 
					
						
							|  |  |  |  |   String? username; | 
					
						
							|  |  |  |  |   String? version; | 
					
						
							|  |  |  |  |   String? basePath; | 
					
						
							|  |  |  |  |   String? isRest; | 
					
						
							|  |  |  |  |   List<dynamic>? permission; | 
					
						
							|  |  |  |  |   bool updateInfo = false; | 
					
						
							| 
									
										
										
										
											2025-07-23 20:14:38 +08:00
										 |  |  |  |   String? dangerJson; | 
					
						
							|  |  |  |  |   String? riskJson; | 
					
						
							| 
									
										
										
										
											2025-07-24 11:18:47 +08:00
										 |  |  |  |   String? departmentJsonStr; | 
					
						
							| 
									
										
										
										
											2025-07-25 18:06:37 +08:00
										 |  |  |  |   String? departmentHiddenTypeJsonStr; | 
					
						
							| 
									
										
										
										
											2025-07-30 10:47:52 +08:00
										 |  |  |  |   String? customRecordDangerJson; | 
					
						
							| 
									
										
										
										
											2025-08-01 16:40:59 +08:00
										 |  |  |  |   String? unqualifiedInspectionItemID; | 
					
						
							| 
									
										
										
										
											2025-07-15 08:32:50 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   /// 如果以下任何一项为空,则跳转到登录页
 | 
					
						
							|  |  |  |  |   void loginSession(BuildContext context) { | 
					
						
							|  |  |  |  |     if (corpinfoId == null || loginUserId == null || loginUser == null) { | 
					
						
							|  |  |  |  |       Navigator.pushReplacementNamed(context, '/login'); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // setters
 | 
					
						
							|  |  |  |  |   void setLoginUser(Map<String, dynamic> user) => loginUser = user; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void setLoginUserId(String id) => loginUserId = id; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void setCorpinfoId(String id) => corpinfoId = id; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void setDeptId(String id) => deptId = id; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void setDeptLevel(String level) => deptLevel = level; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void setPostId(String id) => postId = id; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void setUsername(String name) => username = name; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void setVersion(String ver) => version = ver; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void setBasePath(String url) => basePath = url; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void setIsRest(String rest) => isRest = rest; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void setPermission(List<dynamic> list) => permission = list; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void setUpdateInfo(bool flag) => updateInfo = flag; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-23 20:14:38 +08:00
										 |  |  |  |   void setDangerWaitInfo(String json) => dangerJson = json; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void setRiskWaitInfo(String json) => riskJson = json; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 11:18:47 +08:00
										 |  |  |  |   void setDepartmentJsonStr(String json) => departmentJsonStr = json; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-30 10:47:52 +08:00
										 |  |  |  |   void setCustomRecordDangerJson(String json) => customRecordDangerJson = json; | 
					
						
							| 
									
										
										
										
											2025-07-15 08:32:50 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-01 16:40:59 +08:00
										 |  |  |  |   void setUnqualifiedInspectionItemIDJson(String json) => unqualifiedInspectionItemID = json; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-15 08:32:50 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /// ------------------------------------------------------
 | 
					
						
							|  |  |  |  | /// 日期格式化
 | 
					
						
							|  |  |  |  | /// ------------------------------------------------------
 | 
					
						
							|  |  |  |  | String formatDate(DateTime? date, String fmt) { | 
					
						
							|  |  |  |  |   if (date == null) return ''; | 
					
						
							|  |  |  |  |   String twoDigits(int n) => n.toString().padLeft(2, '0'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   final replacements = <String, String>{ | 
					
						
							|  |  |  |  |     'yyyy': date.year.toString(), | 
					
						
							|  |  |  |  |     'yy': date.year.toString().substring(2), | 
					
						
							|  |  |  |  |     'MM': twoDigits(date.month), | 
					
						
							|  |  |  |  |     'M': date.month.toString(), | 
					
						
							|  |  |  |  |     'dd': twoDigits(date.day), | 
					
						
							|  |  |  |  |     'd': date.day.toString(), | 
					
						
							|  |  |  |  |     'hh': twoDigits(date.hour), | 
					
						
							|  |  |  |  |     'h': date.hour.toString(), | 
					
						
							|  |  |  |  |     'mm': twoDigits(date.minute), | 
					
						
							|  |  |  |  |     'm': date.minute.toString(), | 
					
						
							|  |  |  |  |     'ss': twoDigits(date.second), | 
					
						
							|  |  |  |  |     's': date.second.toString(), | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   String result = fmt; | 
					
						
							|  |  |  |  |   replacements.forEach((key, value) { | 
					
						
							|  |  |  |  |     result = result.replaceAllMapped(RegExp(key), (_) => value); | 
					
						
							|  |  |  |  |   }); | 
					
						
							|  |  |  |  |   return result; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /// ------------------------------------------------------
 | 
					
						
							|  |  |  |  | /// 防多次点击
 | 
					
						
							|  |  |  |  | /// ------------------------------------------------------
 | 
					
						
							|  |  |  |  | class ClickUtil { | 
					
						
							|  |  |  |  |   ClickUtil._(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   static bool _canClick = true; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /// 调用示例:
 | 
					
						
							|  |  |  |  |   /// ClickUtil.noMultipleClicks(() { /* your code */ });
 | 
					
						
							|  |  |  |  |   static void noMultipleClicks(VoidCallback fn, {int delayMs = 2000}) { | 
					
						
							|  |  |  |  |     if (_canClick) { | 
					
						
							|  |  |  |  |       _canClick = false; | 
					
						
							|  |  |  |  |       fn(); | 
					
						
							|  |  |  |  |       Future.delayed(Duration(milliseconds: delayMs), () { | 
					
						
							|  |  |  |  |         _canClick = true; | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |     } else { | 
					
						
							|  |  |  |  |       debugPrint('请稍后点击'); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | void presentPage(BuildContext context, Widget page) { | 
					
						
							|  |  |  |  |   Navigator.push( | 
					
						
							|  |  |  |  |     context, | 
					
						
							|  |  |  |  |     MaterialPageRoute(fullscreenDialog: true, builder: (_) => page), | 
					
						
							|  |  |  |  |   ); | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-07-16 08:37:08 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | class LoadingDialogHelper { | 
					
						
							| 
									
										
										
										
											2025-07-29 08:50:41 +08:00
										 |  |  |  |   /// 显示 loading,对话框中可选文字
 | 
					
						
							|  |  |  |  |   static void show(BuildContext context, {String? message}) { | 
					
						
							| 
									
										
										
										
											2025-07-16 08:37:08 +08:00
										 |  |  |  |     showDialog( | 
					
						
							|  |  |  |  |       context: context, | 
					
						
							|  |  |  |  |       barrierDismissible: false, | 
					
						
							| 
									
										
										
										
											2025-07-29 08:50:41 +08:00
										 |  |  |  |       builder: (_) => Center( | 
					
						
							|  |  |  |  |         child: Container( | 
					
						
							|  |  |  |  |           padding: EdgeInsets.all(20), | 
					
						
							|  |  |  |  |           decoration: BoxDecoration( | 
					
						
							|  |  |  |  |             color: Colors.black87, | 
					
						
							|  |  |  |  |             borderRadius: BorderRadius.circular(8), | 
					
						
							|  |  |  |  |           ), | 
					
						
							|  |  |  |  |           child: Column( | 
					
						
							|  |  |  |  |             mainAxisSize: MainAxisSize.min, | 
					
						
							|  |  |  |  |             children: [ | 
					
						
							|  |  |  |  |               const CircularProgressIndicator( | 
					
						
							|  |  |  |  |                 valueColor: AlwaysStoppedAnimation(Colors.white), | 
					
						
							|  |  |  |  |               ), | 
					
						
							|  |  |  |  |               if (message != null) ...[ | 
					
						
							|  |  |  |  |                 const SizedBox(height: 12), | 
					
						
							|  |  |  |  |                 Text( | 
					
						
							|  |  |  |  |                   message, | 
					
						
							|  |  |  |  |                   style: TextStyle(color: Colors.white), | 
					
						
							|  |  |  |  |                 ), | 
					
						
							|  |  |  |  |               ], | 
					
						
							|  |  |  |  |             ], | 
					
						
							|  |  |  |  |           ), | 
					
						
							|  |  |  |  |         ), | 
					
						
							|  |  |  |  |       ), | 
					
						
							| 
									
										
										
										
											2025-07-16 08:37:08 +08:00
										 |  |  |  |     ); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 08:50:41 +08:00
										 |  |  |  |   /// 隐藏 loading
 | 
					
						
							| 
									
										
										
										
											2025-07-16 08:37:08 +08:00
										 |  |  |  |   static void hide(BuildContext context) { | 
					
						
							|  |  |  |  |     if (Navigator.canPop(context)) { | 
					
						
							|  |  |  |  |       Navigator.of(context).pop(); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-07-29 08:50:41 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-17 16:10:46 +08:00
										 |  |  |  | /// 将秒数转换为 “HH:MM:SS” 格式
 | 
					
						
							|  |  |  |  | String secondsCount(dynamic seconds) { | 
					
						
							|  |  |  |  |   // 先尝试解析出一个 double 值
 | 
					
						
							|  |  |  |  |   double totalSeconds; | 
					
						
							|  |  |  |  |   if (seconds == null) { | 
					
						
							|  |  |  |  |     totalSeconds = 0; | 
					
						
							|  |  |  |  |   } else if (seconds is num) { | 
					
						
							|  |  |  |  |     totalSeconds = seconds.toDouble(); | 
					
						
							|  |  |  |  |   } else { | 
					
						
							|  |  |  |  |     // seconds 是字符串或其他,尝试 parse
 | 
					
						
							|  |  |  |  |     totalSeconds = double.tryParse(seconds.toString()) ?? 0.0; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 取整秒,向下取整
 | 
					
						
							|  |  |  |  |   final int secs = totalSeconds.floor(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   final int h = (secs ~/ 3600) % 24; | 
					
						
							|  |  |  |  |   final int m = (secs ~/ 60) % 60; | 
					
						
							|  |  |  |  |   final int s = secs % 60; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // padLeft 保证两位数
 | 
					
						
							|  |  |  |  |   final String hh = h.toString().padLeft(2, '0'); | 
					
						
							|  |  |  |  |   final String mm = m.toString().padLeft(2, '0'); | 
					
						
							|  |  |  |  |   final String ss = s.toString().padLeft(2, '0'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return '$hh:$mm:$ss'; | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | /// 表单处理
 | 
					
						
							|  |  |  |  | class FormUtils { | 
					
						
							|  |  |  |  |   /// 判断 [data] 中的 [key] 是否存在“有效值”:
 | 
					
						
							|  |  |  |  |   /// - key 不存在或值为 null -> false
 | 
					
						
							|  |  |  |  |   /// - String:去掉首尾空白后非空 -> true
 | 
					
						
							|  |  |  |  |   /// - Iterable / Map:非空 -> true
 | 
					
						
							|  |  |  |  |   /// - 其它类型(int、double、bool 等)只要不为 null 就算有值 -> true
 | 
					
						
							|  |  |  |  |   static bool hasValue(Map<String, dynamic> data, String key) { | 
					
						
							|  |  |  |  |     if (!data.containsKey(key)) return false; | 
					
						
							|  |  |  |  |     final val = data[key]; | 
					
						
							|  |  |  |  |     if (val == null) return false; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if (val is String) { | 
					
						
							|  |  |  |  |       return val.trim().isNotEmpty; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     if (val is Iterable || val is Map) { | 
					
						
							|  |  |  |  |       return val.isNotEmpty; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     // 数字、布尔等其它非空即可
 | 
					
						
							|  |  |  |  |     return true; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | class NoDataWidget { | 
					
						
							|  |  |  |  |   static Widget show() { | 
					
						
							|  |  |  |  |     return Center( | 
					
						
							|  |  |  |  |       child: Column( | 
					
						
							|  |  |  |  |         mainAxisAlignment: MainAxisAlignment.center, | 
					
						
							|  |  |  |  |         children: [ | 
					
						
							|  |  |  |  |           Image.asset('assets/images/null.png', width: 200,), | 
					
						
							|  |  |  |  |           Text('暂无数据', style: TextStyle(color: Colors.grey)), | 
					
						
							|  |  |  |  |         ], | 
					
						
							|  |  |  |  |       ), | 
					
						
							|  |  |  |  |     ); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | } |