更改分享和解绑消息弹窗

This commit is contained in:
czz
2025-08-26 17:56:36 +08:00
parent 5ac6c891b3
commit daf782bf9f
10 changed files with 407 additions and 90 deletions

View File

@@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:EasyDartModule/EasyDartModule.dart';
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:flutterflow_ui/flutterflow_ui.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:vbvs_app/common/color/ServiceConstant.dart';
@@ -9,6 +10,8 @@ import 'package:vbvs_app/common/color/appConstants.dart';
import 'package:vbvs_app/common/color/app_uri_status.dart';
import 'package:vbvs_app/common/util/DailyLogUtils.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/common/util/requestWithLog.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/enum/APPPackageType.dart';
import 'package:vbvs_app/model/api_response.dart';
@@ -44,20 +47,36 @@ class MessageController extends GetControllerEx<MessageModel> {
RxList messageList = [].obs;
Future<ApiResponse> getMessageList({String? key}) async {
int bodyPage = 1;
int systemPage = 1;
Future<void> loadMore(String type) async {
if (type == "app_vsm") {
bodyPage++;
await getMessageList(key: "app_vsm", page: bodyPage, append: true);
} else {
systemPage++;
await getMessageList(key: "app_system", page: systemPage, append: true);
}
}
Future<ApiResponse> getMessageList(
{String? key, int page = 1, int limit = 20, bool append = false}) async {
try {
ApiResponse apiResponse = ApiResponse(code: -1, msg: "请求失败".tr);
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.message_list;
String messageType = "app_system";
if (model.type == 1) {
String messageType;
if (key != null && key.isNotEmpty) {
messageType = key;
} else if (model.type == 1) {
messageType = "app_vsm";
} else {
messageType = "app_system";
}
String queryUrl =
"${serviceAddress}${serviceName}${serviceApi}?type=${messageType}";
"${serviceAddress}${serviceName}${serviceApi}?type=${messageType}&page=$page&limit=$limit";
String? language = "";
if (AppConstants().ent_type == APPPackageType.MHT.code) {
if (mhLanguageController.selectLanguage != null) {
@@ -83,8 +102,12 @@ class MessageController extends GetControllerEx<MessageModel> {
ApiResponse.fromJson(responseData, (object) => object);
MyUtils.formatResponse(res, "请求成功".tr, "请求失败".tr);
if (res.code == HttpStatusCodes.ok) {
if (append) {
messageList.addAll(res.data ?? []);
} else {
messageList.assignAll(res.data ?? []);
}
updateAll();
messageList.value = res.data;
return res;
}
} else {
@@ -223,4 +246,45 @@ class MessageController extends GetControllerEx<MessageModel> {
return ApiResponse(code: -1, msg: "失败".tr);
}
}
Future<void> updateMessageReadStatus(BuildContext context, String messageType,
{String? mid, bool all = false}) async {
final serviceAddress = ServiceConstant.service_address;
final serviceName = ServiceConstant.server_service;
final serviceApi = ServiceConstant.message_read;
// 构建 query 参数
String queryUrl =
"$serviceAddress$serviceName$serviceApi?type=$messageType";
if (all) {
queryUrl += "&mid=ALL";
} else if (mid != null && mid.isNotEmpty) {
queryUrl += "&mid=$mid";
}
await requestWithLog(
logTitle: all ? '更新所有消息为已读' : '更新消息为已读',
method: MyHttpMethod.post,
queryUrl: queryUrl,
onSuccess: (res) {
if (res.code == HttpStatusCodes.ok) {
getMessageList(key: messageType);
getMessageStatus();
// ✅ 只有在 all 为 true 时才提示
if (all) {
TopSlideNotification.show(
context,
text: res.msg!,
textColor: Color(0XFF00C1AA),
);
}
}
},
onFailure: (res) {
TopSlideNotification.show(context,
text: res.msg!, textColor: Color(0xFFFF7159));
},
);
}
}

View File

@@ -46,20 +46,28 @@ class MhMessageController extends GetControllerEx<MhMessageModel> {
RxList bodyMessageList = [].obs;
RxList systemMessageList = [].obs;
Future<ApiResponse> getMessageList(String messageType) async {
int bodyPage = 1;
int systemPage = 1;
Future<void> loadMore(String type) async {
if (type == "app_vsm") {
bodyPage++;
await getMessageList("app_vsm", page: bodyPage, append: true);
} else {
systemPage++;
await getMessageList("app_system", page: systemPage, append: true);
}
}
Future<ApiResponse> getMessageList(String messageType,
{int page = 1, int limit = 20, bool append = false}) async {
try {
ApiResponse apiResponse = ApiResponse(code: -1, msg: "请求失败".tr);
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.message_list;
// String messageType = "app_system";
// if (model.type == 1) {
// messageType = "app_vsm";
// } else {
// messageType = "app_system";
// }
String queryUrl =
"${serviceAddress}${serviceName}${serviceApi}?type=${messageType}";
"${serviceAddress}${serviceName}${serviceApi}?type=${messageType}&page=$page&limit=$limit";
String? language = "";
if (mhLanguageController.selectLanguage != null) {
language = mhLanguageController.selectLanguage.value!.language_code;
@@ -78,22 +86,23 @@ class MhMessageController extends GetControllerEx<MhMessageModel> {
ApiResponse res =
ApiResponse.fromJson(responseData, (object) => object);
MyUtils.formatResponse(res, "请求成功".tr, "请求失败".tr);
if (res.code == HttpStatusCodes.ok) {
// updateAll();
// messageList.value = res.data;
if (res.code == HttpStatusCodes.ok) {
if (messageType == "app_vsm") {
if (append) {
bodyMessageList.addAll(res.data ?? []);
} else {
bodyMessageList.assignAll(res.data ?? []);
}
} else if (messageType == "app_system") {
if (append) {
systemMessageList.addAll(res.data ?? []);
} else {
systemMessageList.assignAll(res.data ?? []);
}
}
updateAll();
return res;
}
} else {
return ApiResponse(code: -1, msg: "失败".tr);
}
return apiResponse;
} catch (e) {

View File

@@ -213,17 +213,18 @@ Timer? _messageTimer;
// }
Future<void> startMessagePolling(int ent_type) async {
Get.put(MhMessageController());
MhMessageController messageController = Get.find();
Get.put(MessageController());
MessageController messageController = Get.find();
final Set<String> _poppedMessageIds = {}; // 本地已弹窗的消息 ID无论是否已读成功
final Set<String> _readMessageIds = {};
final controller = Get.find<MhMessageController>();
_messageTimer?.cancel();
if (ent_type == APPPackageType.MHT.code) {
if (Get.isRegistered<MhMessageController>()) {
Get.find<MhMessageController>().getMessageStatus();
await controller.getMessageStatus();
if (controller.model.system_message_read == 1) {
await controller.getMessageList("app_system");
await controller.getMessageList("app_system",
page: 1, limit: 100, append: false);
final unhandledShareMessages =
controller.systemMessageList.where((item) {
final data = item['data'];
@@ -313,7 +314,113 @@ Future<void> startMessagePolling(int ent_type) async {
}
} else {
if (Get.isRegistered<MessageController>()) {
Get.find<MessageController>().getMessageStatus();
await messageController.getMessageStatus();
if (messageController.model.system_message_read == 1) {
await messageController.getMessageList(
key: "app_system", page: 1, limit: 100, append: false);
final unhandledShareMessages =
messageController.messageList.where((item) {
final data = item['data'];
final id = item['_id']?.toString();
return data is Map &&
(data['type'] == 'share' || data['type'] == 'unShare') &&
id != null &&
!_poppedMessageIds.contains(id) &&
(item['read_time'] == null);
}).toList();
if (unhandledShareMessages.isNotEmpty) {
for (final message in unhandledShareMessages) {
final messageId = message['_id']?.toString();
if (messageId == null || _poppedMessageIds.contains(messageId))
continue;
// 新增:如果当前页面是消息页,则跳过弹窗
if (Get.currentRoute == '/messagePage') {
continue;
}
final data = message['data'] as Map<String, dynamic>;
final valList = data['val'] as List<dynamic>;
final messageType = data['type'];
// Extract device ID
final deviceIdEntry = valList.firstWhere(
(item) => item['k'] == '设备ID',
orElse: () => null,
);
final mac = deviceIdEntry?['v'] as String?;
_poppedMessageIds.add(messageId);
try {
await controller.updateMessageReadStatus(
Get.context!, message['type'],
mid: messageId);
} catch (e) {
print("标记消息 $messageId 为已读失败: $e");
}
String dialogTitle;
String msg;
if (messageType == 'share') {
dialogTitle = '设备分享提醒'.tr; // "Device Sharing Notification"
final result = await showMessageConfirmDialog(
title: dialogTitle,
colorsList: [
themeController.currentColor.sc1,
themeController.currentColor.sc2,
],
backgroundColor: themeController.currentColor.sc17,
textColor: themeController.currentColor.sc3);
if (result == 'confirm') {
messageController.model.type = 2;
Get.toNamed('/messagePage');
}
} else {
dialogTitle = '设备解绑提醒'.tr;
showUnShareMessageDialog(
title: dialogTitle,
mac: mac!,
colorsList: [
themeController.currentColor.sc1,
themeController.currentColor.sc2,
],
backgroundColor: themeController.currentColor.sc17,
textColor: themeController.currentColor.sc3,
);
}
}
try {
MHTHomeController homeController = Get.find();
//更新设备列表
homeController.getPersonList();
//请求绑定设备列表
// homeController.getSleepReport();
homeController.getDeviceNum();
homeController.getDeviceList(group: 'room').then((apiResponse) {
if (apiResponse.code != HttpStatusCodes.ok) {
try {
WebviewTestController webviewTestController = Get.find();
webviewTestController.web.jsbridge?.dart.alterDevice();
} catch (e) {
ef.log("[h5]通知列表更新报错:$e");
}
}
});
WidgetsBinding.instance.addPostFrameCallback((_) {
if (homeController.homeSleepDays.value.isNotEmpty) {
homeController.selectedDayIndex.value =
homeController.homeSleepDays.value.length - 1;
}
});
await homeController.getPersonList();
} catch (e) {
ef.log("更新失败:$e");
}
}
}
}
}
@@ -324,7 +431,8 @@ Future<void> startMessagePolling(int ent_type) async {
await controller.getMessageStatus();
if (controller.model.system_message_read == 1) {
await controller.getMessageList("app_system");
await controller.getMessageList("app_system",
page: 1, limit: 100, append: false);
final unhandledShareMessages =
controller.systemMessageList.where((item) {
final data = item['data'];
@@ -389,6 +497,113 @@ Future<void> startMessagePolling(int ent_type) async {
} else {
if (Get.isRegistered<MessageController>()) {
Get.find<MessageController>().getMessageStatus();
if (messageController.model.system_message_read == 1) {
await messageController.getMessageList(
key: "app_system", page: 1, limit: 100, append: false);
final unhandledShareMessages =
messageController.messageList.where((item) {
final data = item['data'];
final id = item['_id']?.toString();
return data is Map &&
(data['type'] == 'share' || data['type'] == 'unShare') &&
id != null &&
!_poppedMessageIds.contains(id) &&
(item['read_time'] == null);
}).toList();
if (unhandledShareMessages.isNotEmpty) {
for (final message in unhandledShareMessages) {
final messageId = message['_id']?.toString();
if (messageId == null || _poppedMessageIds.contains(messageId))
continue;
// 新增:如果当前页面是消息页,则跳过弹窗
if (Get.currentRoute == '/messagePage') {
continue;
}
final data = message['data'] as Map<String, dynamic>;
final valList = data['val'] as List<dynamic>;
final messageType = data['type'];
// Extract device ID
final deviceIdEntry = valList.firstWhere(
(item) => item['k'] == '设备ID',
orElse: () => null,
);
final mac = deviceIdEntry?['v'] as String?;
_poppedMessageIds.add(messageId);
try {
await controller.updateMessageReadStatus(
Get.context!, message['type'],
mid: messageId);
} catch (e) {
print("标记消息 $messageId 为已读失败: $e");
}
String dialogTitle;
String msg;
if (messageType == 'share') {
dialogTitle = '设备分享提醒'.tr; // "Device Sharing Notification"
final result = await showMessageConfirmDialog(
title: dialogTitle,
colorsList: [
themeController.currentColor.sc1,
themeController.currentColor.sc2,
],
backgroundColor: themeController.currentColor.sc17,
textColor: themeController.currentColor.sc3);
if (result == 'confirm') {
Get.toNamed(
'/messagePage',
);
}
} else {
dialogTitle = '设备解绑提醒'.tr;
showUnShareMessageDialog(
title: dialogTitle,
mac: mac!,
colorsList: [
themeController.currentColor.sc1,
themeController.currentColor.sc2,
],
backgroundColor: themeController.currentColor.sc17,
textColor: themeController.currentColor.sc3,
);
}
}
try {
MHTHomeController homeController = Get.find();
//更新设备列表
homeController.getPersonList();
//请求绑定设备列表
// homeController.getSleepReport();
homeController.getDeviceNum();
homeController.getDeviceList(group: 'room').then((apiResponse) {
if (apiResponse.code != HttpStatusCodes.ok) {
try {
WebviewTestController webviewTestController = Get.find();
webviewTestController.web.jsbridge?.dart.alterDevice();
} catch (e) {
ef.log("[h5]通知列表更新报错:$e");
}
}
});
WidgetsBinding.instance.addPostFrameCallback((_) {
if (homeController.homeSleepDays.value.isNotEmpty) {
homeController.selectedDayIndex.value =
homeController.homeSleepDays.value.length - 1;
}
});
await homeController.getPersonList();
} catch (e) {
ef.log("更新失败:$e");
}
}
}
}
}
} catch (e) {

View File

@@ -1469,6 +1469,13 @@ Future<void> showDeleteDeviceConfirmDialog({
// }
Future<String?> showMessageConfirmDialog({
required String title,
List<Color> colorsList = const [
Color(0xFF1592AA),
Color(0XFF0C83A7),
Color(0XFF006FA3)
],
Color backgroundColor = Colors.white,
Color textColor = Colors.black,
}) async {
return await Get.dialog<String>(
FrostedDialog(
@@ -1477,7 +1484,7 @@ Future<String?> showMessageConfirmDialog({
width: 520.rpx,
height: 460.rpx,
decoration: BoxDecoration(
color: Colors.white,
color: backgroundColor,
borderRadius: BorderRadius.circular(20.0),
),
child: Padding(
@@ -1489,18 +1496,18 @@ Future<String?> showMessageConfirmDialog({
children: [
Text(
title,
style: TextStyle(fontSize: 30.rpx, color: Colors.black),
style: TextStyle(fontSize: 30.rpx, color: textColor),
),
const SizedBox(height: 12),
RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: TextStyle(fontSize: 26.rpx, color: Colors.black87),
style: TextStyle(fontSize: 26.rpx, color: Colors.black),
children: [
TextSpan(
text: '有一条新的设备分享消息'.tr,
style:
TextStyle(color: Colors.black, fontSize: 26.rpx)),
TextStyle(color: textColor, fontSize: 26.rpx)),
],
),
),
@@ -1514,11 +1521,7 @@ Future<String?> showMessageConfirmDialog({
onTap: () {
Get.back(result: 'cancel');
},
colors: [
Color(0xFF1592AA),
Color(0XFF0C83A7),
Color(0XFF006FA3)
],
colors: colorsList,
child: Container(
width: 200.rpx,
height: 90.rpx,
@@ -1534,11 +1537,7 @@ Future<String?> showMessageConfirmDialog({
onTap: () {
Get.back(result: 'confirm');
},
colors: [
Color(0xFF1592AA),
Color(0XFF0C83A7),
Color(0XFF006FA3)
],
colors: colorsList,
child: Container(
width: 200.rpx,
height: 90.rpx,
@@ -1563,6 +1562,13 @@ Future<String?> showMessageConfirmDialog({
Future<String?> showUnShareMessageDialog({
required String title,
required String mac,
List<Color> colorsList = const [
Color(0xFF1592AA),
Color(0XFF0C83A7),
Color(0XFF006FA3)
],
Color backgroundColor = Colors.white,
Color textColor = Colors.black,
}) async {
return await Get.dialog<String>(
FrostedDialog(
@@ -1571,7 +1577,7 @@ Future<String?> showUnShareMessageDialog({
width: 520.rpx,
height: 460.rpx,
decoration: BoxDecoration(
color: Colors.white,
color: backgroundColor,
borderRadius: BorderRadius.circular(20.0),
),
child: Padding(
@@ -1583,21 +1589,20 @@ Future<String?> showUnShareMessageDialog({
children: [
Text(
title,
style: TextStyle(fontSize: 30.rpx, color: Colors.black),
style: TextStyle(fontSize: 30.rpx, color: textColor),
),
const SizedBox(height: 12),
RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: TextStyle(fontSize: 26.rpx, color: Colors.black87),
style: TextStyle(fontSize: 26.rpx, color: Colors.black),
children: [
TextSpan(
text: mac,
style: TextStyle(color: Colors.blue, fontSize: 26.rpx)),
TextSpan(
text: " " + '设备已被用户解绑,将取消当前分享!'.tr,
style:
TextStyle(color: Colors.black, fontSize: 26.rpx)),
style: TextStyle(color: textColor, fontSize: 26.rpx)),
],
),
),
@@ -1610,11 +1615,7 @@ Future<String?> showUnShareMessageDialog({
onTap: () {
Get.back(result: 'confirm');
},
colors: [
Color(0xFF1592AA),
Color(0XFF0C83A7),
Color(0XFF006FA3)
],
colors: colorsList,
child: Container(
width: 200.rpx,
height: 90.rpx,

View File

@@ -30,7 +30,7 @@ class _MessagePageState extends State<MessagePage> {
_pageController =
PageController(initialPage: messageController.model.type == 1 ? 0 : 1);
messageController.getMessageStatus();
_fetchMessageData();
// _fetchMessageData();
}
void _fetchMessageData() {
@@ -272,13 +272,13 @@ class _MessagePageState extends State<MessagePage> {
final list = messageController.messageList.value;
return list.isEmpty
? const NullDataWidget()
: _buildMessageListView(list);
: _buildMessageListView(list, "app_vsm");
}),
Obx(() {
final list = messageController.messageList.value;
return list.isEmpty
? const NullDataWidget()
: _buildMessageListView(list);
: _buildMessageListView(list, "app_system");
}),
],
),
@@ -289,21 +289,42 @@ class _MessagePageState extends State<MessagePage> {
);
}
Widget _buildMessageListView(List dataList) {
return Container(
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 30.rpx),
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(height: 30.rpx),
...dataList
.map((item) => MessageWidgetWidget(data: item))
.toList()
.divide(SizedBox(height: 30.rpx)),
SizedBox(height: 30.rpx),
],
),
// Widget _buildMessageListView(List dataList) {
// return Container(
// width: double.infinity,
// padding: EdgeInsets.symmetric(horizontal: 30.rpx),
// child: SingleChildScrollView(
// child: Column(
// children: [
// SizedBox(height: 30.rpx),
// ...dataList
// .map((item) => MessageWidgetWidget(data: item))
// .toList()
// .divide(SizedBox(height: 30.rpx)),
// SizedBox(height: 30.rpx),
// ],
// ),
// ),
// );
// }
Widget _buildMessageListView(List dataList, String type) {
return NotificationListener<ScrollNotification>(
onNotification: (scrollInfo) {
if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent) {
// 滑到底部,加载下一页
messageController.loadMore(type);
}
return true;
},
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 30.rpx, vertical: 30.rpx),
itemCount: dataList.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(bottom: 30.rpx),
child: MessageWidgetWidget(data: dataList[index]),
);
},
),
);
}

View File

@@ -907,11 +907,11 @@ class EditAddressPage extends GetView<AddressController>
TextFormField(
// autofocus: true,
onChanged:
(val) {
(value) {
controller
.model
.address =
val;
value;
},
initialValue:
address[

View File

@@ -182,13 +182,13 @@ class _MessagePageState extends State<MessagePage> {
final list = messageController.bodyMessageList;
return list.isEmpty
? const NullDataWidget()
: _buildMessageListView(list);
: _buildMessageListView(list, "app_vsm");
}),
Obx(() {
final list = messageController.systemMessageList;
return list.isEmpty
? const NullDataWidget()
: _buildMessageListView(list);
: _buildMessageListView(list, "app_system");
}),
],
),
@@ -253,7 +253,7 @@ class _MessagePageState extends State<MessagePage> {
alignment: Alignment.center,
child: Obx(() {
return ClickableContainer(
padding: EdgeInsets.zero,
padding: EdgeInsets.symmetric(horizontal: 16.rpx, vertical: 8.rpx),
backgroundColor: Colors.transparent,
highlightColor: themeController.currentColor.sc21,
borderRadius: 8.rpx,
@@ -325,7 +325,7 @@ class _MessagePageState extends State<MessagePage> {
alignment: Alignment.center,
child: Obx(() {
return ClickableContainer(
padding: EdgeInsets.zero,
padding: EdgeInsets.symmetric(horizontal: 16.rpx, vertical: 8.rpx),
backgroundColor: Colors.transparent,
highlightColor: themeController.currentColor.sc21,
borderRadius: 8.rpx,
@@ -386,21 +386,25 @@ class _MessagePageState extends State<MessagePage> {
);
}
Widget _buildMessageListView(List dataList) {
return Container(
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 30.rpx),
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(height: 30.rpx),
...dataList
.map((item) => MhMessageListWidget(data: item))
.toList()
.divide(SizedBox(height: 30.rpx)),
SizedBox(height: 30.rpx),
],
),
Widget _buildMessageListView(List dataList, String type) {
return NotificationListener<ScrollNotification>(
onNotification: (scrollInfo) {
if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent) {
// 滑到底部,加载下一页
messageController.loadMore(type);
}
return true;
},
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 30.rpx, vertical: 30.rpx),
itemCount: dataList.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(bottom: 30.rpx),
child: MhMessageListWidget(data: dataList[index]),
);
},
),
);
}