394 lines
12 KiB
Dart
394 lines
12 KiB
Dart
import 'dart:async';
|
||
|
||
import 'package:ef/ef.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:intl/intl.dart';
|
||
import 'package:url_launcher/url_launcher.dart';
|
||
import 'package:vbvs_app/common/color/app_uri_status.dart';
|
||
import 'package:vbvs_app/common/util/FitTool.dart';
|
||
import 'package:vbvs_app/controller/setting/language/language_controller.dart';
|
||
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
|
||
import 'package:vbvs_app/model/api_response.dart';
|
||
|
||
ThemeController themeController = Get.find();
|
||
LanguageController languageController = Get.find();
|
||
|
||
class MyUtils {
|
||
static ApiResponse formatResponse(
|
||
ApiResponse res,
|
||
String successMsg,
|
||
String errorMsg,
|
||
) {
|
||
if (res.code == HttpStatusCodes.ok) {
|
||
// 成功但 msg 为空时填默认成功提示
|
||
if (res.msg == null || res.msg!.isEmpty) {
|
||
res.msg = successMsg;
|
||
}
|
||
} else {
|
||
// 失败且 msg 为空时填默认失败提示
|
||
if (res.msg == null || res.msg!.isEmpty) {
|
||
res.msg = errorMsg;
|
||
}
|
||
}
|
||
return res;
|
||
}
|
||
|
||
static String timestampToDateString(int timestamp) {
|
||
DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp);
|
||
|
||
String formattedDate =
|
||
'${dateTime.year}-${dateTime.month.toString().padLeft(2, '0')}-${dateTime.day.toString().padLeft(2, '0')} '
|
||
'${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}:${dateTime.second.toString().padLeft(2, '0')}';
|
||
|
||
return formattedDate;
|
||
}
|
||
|
||
static String hidePhoneNumber(String phoneNumber) {
|
||
if (phoneNumber.length != 11) {
|
||
// 检查手机号是否为11位
|
||
throw Exception("手机号格式不正确");
|
||
}
|
||
|
||
// 将中间四位替换为星号
|
||
return phoneNumber.replaceRange(3, 7, '****');
|
||
}
|
||
|
||
static double initialScrollOffset = 0.0;
|
||
// 判断手机号格式是否正确的方法
|
||
static bool isValidPhoneNumber(String phoneNumber) {
|
||
final RegExp phoneRegExp = RegExp(r'^1[3-9]\d{9}$');
|
||
return phoneRegExp.hasMatch(phoneNumber);
|
||
}
|
||
|
||
static bool isValidEmail(String email) {
|
||
final RegExp emailRegExp = RegExp(
|
||
r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
|
||
);
|
||
return emailRegExp.hasMatch(email);
|
||
}
|
||
|
||
static Future<void> makePhoneCall(String phoneNumber) async {
|
||
final Uri launchUri = Uri(
|
||
scheme: 'tel',
|
||
path: phoneNumber,
|
||
);
|
||
|
||
if (await canLaunchUrl(launchUri)) {
|
||
await launchUrl(launchUri);
|
||
} else {
|
||
throw '无法拨打电话';
|
||
}
|
||
}
|
||
|
||
static String formatDateTime(DateTime dateTime) {
|
||
// 定义日期格式
|
||
// dateTime.toLocal();
|
||
final DateFormat formatter = DateFormat('yyyy-MM-dd HH:mm');
|
||
// 返回格式化后的字符串
|
||
return formatter.format(dateTime);
|
||
}
|
||
|
||
static void scrollToFocusedInput(FocusNode focusNode, _scrollController) {
|
||
// 获取输入框相对于整个页面的偏移量
|
||
RenderObject? object = focusNode.context?.findRenderObject();
|
||
if (object != null) {
|
||
final RenderBox box = object as RenderBox;
|
||
final Offset position = box.localToGlobal(Offset.zero);
|
||
|
||
// 将页面滚动到使输入框显示在屏幕上的位置
|
||
_scrollController.animateTo(
|
||
position.dy - MediaQuery.of(focusNode.context!).size.height / 4,
|
||
duration: Duration(milliseconds: 300),
|
||
curve: Curves.easeInOut,
|
||
);
|
||
}
|
||
}
|
||
|
||
static void resetScrollPosition(_scrollController) {
|
||
_scrollController.animateTo(
|
||
initialScrollOffset,
|
||
duration: Duration(milliseconds: 300),
|
||
curve: Curves.easeInOut,
|
||
);
|
||
}
|
||
|
||
static String formatBindTime(DateTime d) {
|
||
final DateFormat formatter = DateFormat('yyyy/MM/dd');
|
||
return formatter.format(d);
|
||
}
|
||
|
||
static DateTime? formatBirthdayTime(String? device) {
|
||
if (device == null || device.isEmpty) return null;
|
||
|
||
try {
|
||
return DateTime.parse(device.replaceAll('/', '-')); // 替换为标准格式
|
||
} catch (e) {
|
||
return null; // 解析失败时返回 null
|
||
}
|
||
}
|
||
|
||
static int getAgeByDate(DateTime? formatBirthdayTime) {
|
||
if (formatBirthdayTime == null) return 0;
|
||
|
||
final now = DateTime.now();
|
||
int age = now.year - formatBirthdayTime.year;
|
||
|
||
// 如果还没到今年生日,减一岁
|
||
if (now.month < formatBirthdayTime.month ||
|
||
(now.month == formatBirthdayTime.month &&
|
||
now.day < formatBirthdayTime.day)) {
|
||
age--;
|
||
}
|
||
|
||
return age;
|
||
}
|
||
|
||
static String formatDateTimeWeek(DateTime date) {
|
||
DateTime now = DateTime.now();
|
||
// 去除时间部分,仅比较年月日
|
||
DateTime today = DateTime(now.year, now.month, now.day);
|
||
DateTime target = DateTime(date.year, date.month, date.day);
|
||
|
||
if (target == today) {
|
||
return '今日';
|
||
}
|
||
|
||
const List<String> weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
|
||
return weekdays[date.weekday % 7]; // Dart中星期日是7,要映射到索引0
|
||
}
|
||
|
||
/// 返回 MM/dd 格式
|
||
static String formatDateTimeDay(DateTime date) {
|
||
String twoDigits(int n) => n.toString().padLeft(2, '0');
|
||
return '${twoDigits(date.month)}/${twoDigits(date.day)}';
|
||
}
|
||
}
|
||
|
||
Color stringToColor(String hexColor) {
|
||
String formattedColor = hexColor.replaceAll("#", "");
|
||
int colorValue = int.parse("0xFF$formattedColor");
|
||
return Color(colorValue);
|
||
}
|
||
|
||
//字节转16进制
|
||
String ab2str(List<int> buffer) {
|
||
return buffer.map((x) => x.toRadixString(16).padLeft(2, '0')).join('');
|
||
}
|
||
|
||
enum ToastColor { success, error, warn }
|
||
|
||
ToastColor color_success = ToastColor.success;
|
||
ToastColor color_warning = ToastColor.warn;
|
||
ToastColor color_error = ToastColor.error;
|
||
|
||
showToast(String msg,
|
||
{ToastColor color = ToastColor.error, int closeTime = 3}) {
|
||
// Fluttertoast.showToast(
|
||
// msg: msg,
|
||
// toastLength: Toast.LENGTH_LONG,
|
||
// gravity: ToastGravity.CENTER,
|
||
// timeInSecForIosWeb: 1,
|
||
// backgroundColor: color == null ? color_error : color,
|
||
// fontSize: 14.0,
|
||
// );
|
||
final context = Get.overlayContext; // 获取 Overlay 的上下文
|
||
Color background = Colors.red;
|
||
Color color_text = stringToColor("#FA5A4C");
|
||
String icon = "";
|
||
if (color == color_success) {
|
||
background = stringToColor("#DBF1E1");
|
||
icon = "success";
|
||
color_text = stringToColor("#31CA79");
|
||
} else if (color == color_warning) {
|
||
background = stringToColor("#FDF6EC");
|
||
icon = "warn";
|
||
color_text = stringToColor("#F8AE00");
|
||
} else if (color == color_error) {
|
||
background = stringToColor("#FEF0F0");
|
||
icon = "error";
|
||
color_text = stringToColor("#FA5A4C");
|
||
}
|
||
if (context == null) return;
|
||
OverlayEntry overlayEntry = OverlayEntry(
|
||
builder: (context) => Positioned(
|
||
top: MediaQuery.of(context).size.height * 0.10,
|
||
left: MediaQuery.of(context).size.width * 0.5 - 225.rpx,
|
||
child: Material(
|
||
color: Colors.transparent,
|
||
child: Container(
|
||
width: 450.rpx,
|
||
padding: EdgeInsets.only(top: 20.rpx, bottom: 20.rpx),
|
||
decoration: BoxDecoration(
|
||
color: background,
|
||
borderRadius: BorderRadius.circular(10.rpx),
|
||
),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Image.asset(
|
||
"assets/images/toast/${icon}_.png",
|
||
width: 30.rpx,
|
||
height: 30.rpx,
|
||
),
|
||
SizedBox(
|
||
width: 18.rpx,
|
||
),
|
||
Container(
|
||
constraints: BoxConstraints(maxWidth: 380.rpx),
|
||
child: Text(
|
||
msg,
|
||
maxLines: 9,
|
||
style: TextStyle(
|
||
color: color_text,
|
||
fontSize: 28.rpx,
|
||
fontWeight: FontWeight.w400),
|
||
),
|
||
)
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
Overlay.of(context)?.insert(overlayEntry);
|
||
Future.delayed(Duration(seconds: closeTime), () {
|
||
overlayEntry.remove();
|
||
});
|
||
}
|
||
|
||
var closeIcon = GestureDetector(
|
||
onTapDown: (f) {
|
||
Get.back();
|
||
},
|
||
child: Container(
|
||
padding: EdgeInsets.fromLTRB(10.rpx, 18.rpx, 10.rpx, 10.rpx),
|
||
child: Container(
|
||
height: 42.rpx,
|
||
width: 42.rpx,
|
||
alignment: Alignment.center,
|
||
child: Icon(
|
||
Icons.close,
|
||
),
|
||
),
|
||
),
|
||
);
|
||
|
||
var closeIconWhite = GestureDetector(
|
||
onTapDown: (f) {
|
||
Get.back();
|
||
},
|
||
child: Container(
|
||
padding: EdgeInsets.all(10.rpx),
|
||
child: Container(
|
||
height: 42.rpx,
|
||
width: 42.rpx,
|
||
alignment: Alignment.center,
|
||
child: Icon(
|
||
Icons.close,
|
||
color: themeController.currentColor.sc3,
|
||
),
|
||
),
|
||
),
|
||
);
|
||
|
||
var returnIconButtom = IconButton(
|
||
// padding: EdgeInsets.zero, // 去除默认 padding
|
||
// constraints: BoxConstraints(), // 去除最小尺寸限制
|
||
onPressed: () => Get.back(),
|
||
icon: Icon(Icons.navigate_before, size: 60.rpx),
|
||
);
|
||
|
||
var returnIconButtomAddCallback = (returnCallBack) {
|
||
return IconButton(
|
||
onPressed: () {
|
||
returnCallBack?.call();
|
||
Get.back();
|
||
},
|
||
icon: Icon(Icons.navigate_before, size: 60.rpx),
|
||
);
|
||
};
|
||
|
||
String time_08_Formatter(String time) {
|
||
if (time == null || time == "") {
|
||
return "";
|
||
}
|
||
return DateFormat("yyyy-MM-dd HH:mm:ss")
|
||
.format(DateTime.parse(time).toLocal());
|
||
}
|
||
|
||
String time_08_Formatter_pattern(String time, String pattern) {
|
||
if (time == null || time == "") {
|
||
return "";
|
||
}
|
||
return DateFormat(pattern).format(DateTime.parse(time).toLocal());
|
||
}
|
||
|
||
|
||
enum LoadingDialogIcon { ble, wifi, none }
|
||
|
||
class LoadingDialog {
|
||
static show(String name, {LoadingDialogIcon icon = LoadingDialogIcon.none}) {
|
||
ThemeController themeController = Get.find();
|
||
String iconUrl = "";
|
||
if (icon == LoadingDialogIcon.wifi) {
|
||
iconUrl = "wifi";
|
||
} else if (icon == LoadingDialogIcon.ble) {
|
||
iconUrl = "ble";
|
||
}
|
||
Get.dialog(
|
||
PopScope(
|
||
canPop: false,
|
||
child: Container(
|
||
// color: Colors.transparent,
|
||
child: Column(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Container(
|
||
constraints:
|
||
BoxConstraints(minWidth: 300.rpx, maxWidth: 500.rpx),
|
||
decoration:
|
||
BoxDecoration(borderRadius: BorderRadius.circular(8)),
|
||
child: Column(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
if (iconUrl.isNotEmpty)
|
||
Container(
|
||
width: 120.rpx,
|
||
height: 120.rpx,
|
||
margin: EdgeInsets.only(bottom: 60.rpx),
|
||
child:
|
||
Image.asset("assets/images/toast/${iconUrl}.png"),
|
||
),
|
||
Text(
|
||
textAlign: TextAlign.center,
|
||
name,
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
color: themeController.currentColor.sc3,
|
||
decoration: TextDecoration.none),
|
||
),
|
||
SizedBox(
|
||
height: 30.rpx,
|
||
),
|
||
CircularProgressIndicator(
|
||
strokeWidth: 2,
|
||
valueColor: AlwaysStoppedAnimation<Color>(
|
||
themeController.currentColor.sc3,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
)
|
||
],
|
||
),
|
||
),
|
||
),
|
||
barrierDismissible: false,
|
||
barrierColor: Color.fromRGBO(0, 0, 0, 0.8));
|
||
}
|
||
|
||
static hide() {
|
||
Get.back();
|
||
}
|
||
}
|