初始化项目

This commit is contained in:
wyf
2025-04-11 08:47:46 +08:00
parent e0e1055d65
commit 9396f18d09
199 changed files with 6516 additions and 216 deletions

View File

@@ -0,0 +1,35 @@
class CommonVariables {
static bool isNetWorkOn = false;
static bool test = false;
static String supabaseUrl = "https://mht1.he-info.cn";
// static String wsUrl = "ws://mht-server.he-info.cn/ws";
// static String apiUrl = "https://mht-server.he-info.cn";
// static String shoph5Url = "https://mht-web.he-info.cn";
// static String supabaseUrl = "https://zhmht.swes.com.cn:3443";
// static String wsUrl = "wss://zhmht.swes.com.cn:8089/ws";
// static String apiUrl = "https://zhmht.swes.com.cn:8089";
static String wsUrl = "ws://192.168.1.129:8088/ws";
static String apiUrl = "http://192.168.1.129:8088";
static String shoph5Url = "https://zhmht.swes.com.cn:1443";
static String sleepUrl = "https://alltoone.he-info.cn";
static String efKey = "ef_key";
// // android app 内部更新地址
// static String androidInternalUpgradeUrl = "http://192.168.0.112:1234";
// 企业微信客服拉起的url地址
static String wxKfUrl = "https://work.weixin.qq.com/kfid/kfc7d2337b9c07b1269";
// 企业微信ID
static String wxCorpId = "ww51feda6026280cd0";
//ICP备案号
static String ICPRightCode = "皖ICP备2024068219号-1A";
//公司名称
static String enterpriseName = "合肥眠花糖家具有限责任公司";
//备案时间
static String ICPTime = "2022-2025";
// 分享复制文字信息
static String shareText = "您的朋友邀请您使用《智慧眠花糖》APP请复制后面链接在浏览器中打开 " +
shoph5Url +
"/#/pages/download/download";
}

View File

@@ -0,0 +1,43 @@
class FitTool {
static double rpx = 0;
static bool isInit = false;
static init(double v) {
if (v < 1) {
return;
}
if (isInit == false) {
isInit = true;
rpx = v / 750.0;
}
}
static double getPx(double size) {
return rpx * size * 2.0;
}
static double getRpx(double size) {
return rpx * size;
}
}
extension FitInt on int {
double get px {
return FitTool.getPx(this.toDouble());
}
double get rpx {
return FitTool.getRpx(this.toDouble());
}
}
extension FitDouble on double {
double get px {
return FitTool.getPx(this);
}
double get rpx {
return FitTool.getRpx(this);
}
}

View File

@@ -0,0 +1,327 @@
import 'dart:async';
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:vbvs_app/common/util/CommonVariables.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:url_launcher/url_launcher.dart';
Future<void> initDataEf({String key = ""}) async {
await ef.init(
'${CommonVariables.supabaseUrl}/',
key,
nosqlEnableWebsocket: false,
);
}
class MyUtils {
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 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,
);
}
}
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,
// textColor: Colors.white,
// 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: Colors.white,
),
),
),
);
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());
}
String storagePubSrc =
"${CommonVariables.supabaseUrl}/storage/v1/object/public/";
getStorageResourceUrl(String v) {
if (v.contains('http')) {
return v;
}
return storagePubSrc + v;
}
enum LoadingDialogIcon { ble, wifi, none }
class LoadingDialog {
static show(String name, {LoadingDialogIcon icon = LoadingDialogIcon.none}) {
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: const TextStyle(
fontSize: 16,
color: Colors.white,
decoration: TextDecoration.none),
),
SizedBox(
height: 30.rpx,
),
const CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
],
),
)
],
),
),
),
barrierDismissible: false,
barrierColor: Color.fromRGBO(0, 0, 0, 0.8));
}
static hide() {
Get.back();
}
}

View File

@@ -0,0 +1,113 @@
import 'dart:async';
import 'dart:convert';
import 'package:ef/ef.dart';
import 'package:vbvs_app/controller/user_info_controller.dart';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
class WebsocketProp {
late WebSocketChannel channel;
bool isConnecting = false;
bool isOpen = false;
bool isClose = false; //是否关闭
late String url;
late Map fun;
WebsocketProp initState(String url_, Map funn) {
print(url_);
fun = funn;
url = url_;
isConnecting = false;
_connect();
return this;
}
void _connect() {
isOpen = false;
channel = IOWebSocketChannel.connect(
"$url?token=${Get.find<UserInfoController>().model.token}");
channel.stream.listen((message) {
// print('Received message: $message');
// Handle incoming messages here
heartCheck();
if (isOpen == false) {
fun['open']?.call();
print('Received message: $message');
isOpen = true;
isConnecting = false;
// Get.find<BedController>().websocketSend(1);
return;
}
if (message == "ht") {
print('Received message: $message');
return;
}
fun["message"]?.call(message);
}, onError: (error) {
// Handle connection error and initiate reconnection
print('Error: $error');
isOpen = false;
_reconnect();
}, onDone: () {
// Handle connection closed
print('Connection closed');
isOpen = false;
_reconnect();
});
}
Timer? timeoutObj;
Timer? serverTimeoutObj;
int timeout = 10;
void heartCheck() {
timeoutObj?.cancel();
serverTimeoutObj?.cancel();
timeoutObj = Timer(Duration(seconds: timeout), () {
sendMessage("ht");
serverTimeoutObj = Timer(Duration(seconds: timeout), () {
dispose();
});
});
}
void _reconnect() {
if (isClose) {
return;
}
if (!isConnecting) {
isConnecting = true;
_connect();
Timer(const Duration(seconds: 5), () {
isConnecting = false;
if (isOpen == false) {
_reconnect();
}
});
}
}
void sendMessage(data) {
if (isOpen == false) {
// showToast("websocket 已断开");
return;
}
if (data is String) {
channel.sink.add(data);
} else {
String s = jsonEncode(data);
// print("发送 $s");
channel.sink.add(s);
}
}
sendWebSocketMessageCodeN(code, data) {
sendMessage({"code": code, "data": data});
}
void dispose() {
isClose = true;
channel.sink.close();
}
}

View File

@@ -0,0 +1,86 @@
import 'dart:async';
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'my_dialog_controller.dart';
class MyDialog extends GetView<MyDialogController> {
final String message;
final int seconds;
final Color? textColor; // 可选参数
MyDialog({
required this.message,
required this.seconds,
this.textColor, // 可选参数的赋值
});
@override
Widget build(BuildContext context) {
// 设置弹窗在2秒后自动关闭
Timer(Duration(seconds: seconds), () {
if (Navigator.canPop(context)) {
Navigator.of(context).pop();
}
});
return Dialog(
backgroundColor: Colors.transparent, // 使弹窗背景透明
elevation: 0, // 去除阴影
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
child: ConstrainedBox(
constraints: BoxConstraints(
minWidth: 232,
maxHeight: 400,
minHeight: 47,
maxWidth: 400,
),
child: Opacity(
opacity: 1, // 设置容器的透明度为80%
child: Container(
decoration: BoxDecoration(
// color: Color(0xFF182B7C),
color: Color(0xFFffebe9),
border: Border.all(
// color: Color(0xFFe60012), // 边框颜色
color: textColor ?? Color(0xFFe60012),
width: 1, // 边框宽度
),
borderRadius: BorderRadius.circular(4),
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 10,
offset: Offset(0, 4),
),
],
),
child: Padding(
padding: const EdgeInsets.all(4.0),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 8),
Text(
message,
style: TextStyle(
fontSize: 13,
// color: Color(0xFFe60012), // 边框颜色
color: textColor ?? Color(0xFFe60012),
),
textAlign: TextAlign.center,
),
],
),
),
),
),
),
),
);
}
}

View File

@@ -0,0 +1,71 @@
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
class ConfirmDialog extends GetView {
ConfirmDialog();
Future showConfirmCustomDialog(BuildContext context,
{String name = "是否确认取消?"}) async {
// Completer<String> completer = Completer<String>();
TextEditingController textEditingController = TextEditingController();
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: Container(
width: 340,
padding: EdgeInsets.fromLTRB(16, 0, 16, 16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(alignment: Alignment.centerRight, child: closeIcon),
Center(
child: Text(
'${name}',
style: TextStyle(fontSize: 16),
),
),
SizedBox(height: 30),
Container(
margin: EdgeInsets.only(top: 50.rpx, bottom: 40.rpx),
alignment: Alignment.center,
child: InkWell(
onTap: () {
Get.back(result: "confirm");
},
child: Container(
width: 280.rpx,
height: 60.rpx,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
color: stringToColor("#D3B684")),
child: Text(
'确定',
style: TextStyle(color: Colors.white, fontSize: 30.rpx),
),
),
),
)
],
),
),
);
},
);
// return completer.future;
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container();
}
}

View File

@@ -0,0 +1,44 @@
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:json_annotation/json_annotation.dart';
import 'MyDialog.dart';
part 'my_dialog_controller.g.dart';
@JsonSerializable()
class MyDialogModel {
//版本id
String? title_name = "标题"; //标题
String? message = "消息内容";
MyDialogModel();
static MyDialogModel fromJson(Map<String, dynamic> json) =>
_$MyDialogModelFromJson(json);
Map<String, dynamic> toJson() => _$MyDialogModelToJson(this);
}
class MyDialogController extends GetControllerEx<MyDialogModel> {
MyDialogController() {
attr = GetModel(MyDialogModel()).obs;
}
Future<void> showCustomDialog(
BuildContext context,
String message, {
Color? textColor, // 可选参数
}) async {
await showDialog(
barrierColor: Colors.transparent, // 设置上级页面不变暗
context: context,
builder: (BuildContext context) {
return MyDialog(
message: message,
seconds: 2,
textColor: textColor,
);
},
);
}
}

View File

@@ -0,0 +1,18 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'my_dialog_controller.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
MyDialogModel _$MyDialogModelFromJson(Map<String, dynamic> json) =>
MyDialogModel()
..title_name = json['title_name'] as String?
..message = json['message'] as String?;
Map<String, dynamic> _$MyDialogModelToJson(MyDialogModel instance) =>
<String, dynamic>{
'title_name': instance.title_name,
'message': instance.message,
};