消息列表优化

This commit is contained in:
minbb 2025-10-29 18:10:34 +08:00
parent 7e53ecfbf5
commit 8a8c81b7d2
7 changed files with 450 additions and 118 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -150,6 +150,7 @@ class AppColors {
static Color openPassageModeColor = const Color(0xFFEB2A3B); // () static Color openPassageModeColor = const Color(0xFFEB2A3B); // ()
static Color listTimeYellowColor = const Color(0xFFF3BA37); // () static Color listTimeYellowColor = const Color(0xFFF3BA37); // ()
static Color messageTipsColor = const Color.fromRGBO(202, 220, 247, 1); //
static Color get lockDetailBottomBtnUneable => static Color get lockDetailBottomBtnUneable =>
const Color(0xFF808080); // () const Color(0xFF808080); // ()

View File

@ -29,6 +29,38 @@ class _MessageListPageState extends State<MessageListPage>
final MessageListLogic logic = Get.put(MessageListLogic()); final MessageListLogic logic = Get.put(MessageListLogic());
final MessageListState state = Get.find<MessageListLogic>().state; final MessageListState state = Get.find<MessageListLogic>().state;
// _showCheckboxes
final RxBool _showCheckboxes = false.obs;
//
final RxList<bool> _selectedItems = <bool>[].obs;
//
bool showNotificationBanner = true;
//
final RxBool _pushNotificationEnabled = false.obs;
//
void deleteSelectedMessages() {
final List<MessageItemEntity> selectedMessages = [];
for (int i = 0; i < state.itemDataList.value.length; i++) {
if (_selectedItems[i]) {
selectedMessages.add(state.itemDataList.value[i]);
}
}
//
//logic.deletMessageDataRequest(selectedMessages);
//
_selectedItems.clear();
_selectedItems.addAll(
List.generate(state.itemDataList.value.length, (index) => false));
_showCheckboxes.value = false;
setState(() {});
}
void getHttpData() { void getHttpData() {
logic.messageListDataRequest().then((MessageListEntity value) { logic.messageListDataRequest().then((MessageListEntity value) {
setState(() {}); setState(() {});
@ -39,38 +71,60 @@ class _MessageListPageState extends State<MessageListPage>
void initState() { void initState() {
super.initState(); super.initState();
//
_loadPushNotificationStatus();
getHttpData(); getHttpData();
} }
//
void _loadPushNotificationStatus() async {
final bool? enabled = await Storage.getBool('push_notification_enabled');
_pushNotificationEnabled.value = enabled ?? false;
}
//
Map<String, List<MessageItemEntity>> _groupMessagesByDate() {
Map<String, List<MessageItemEntity>> grouped = {};
state.itemDataList.forEach((item) {
String date = DateTool().dateToYMDString(item.createdAt!.toString());
if (!grouped.containsKey(date)) {
grouped[date] = [];
}
grouped[date]!.add(item);
});
return grouped;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: AppColors.mainBackgroundColor, backgroundColor: AppColors.mainBackgroundColor,
appBar: widget.showAppBar appBar: widget.showAppBar
? TitleAppBar( ? TitleAppBar(
barTitle: '消息'.tr, barTitle: '消息'.tr,
haveBack: true, haveBack: true,
actionsList: <Widget>[ actionsList: <Widget>[
TextButton( TextButton(
child: Text( child: Text(
'清空'.tr, '清空'.tr,
style: TextStyle(color: Colors.white, fontSize: 24.sp), style: TextStyle(color: Colors.white, fontSize: 24.sp),
), ),
onPressed: () async { onPressed: () async {
final bool? isDemoMode = final bool? isDemoMode =
await Storage.getBool(ifIsDemoModeOrNot); await Storage.getBool(ifIsDemoModeOrNot);
if (isDemoMode == false) { if (isDemoMode == false) {
ShowTipView().showIosTipWithContentDialog('是否清空?'.tr, ShowTipView().showIosTipWithContentDialog('是否清空?'.tr,
() async { () async {
logic.deletAllMessageDataRequest(); logic.deletAllMessageDataRequest();
}); });
} else { } else {
logic.showToast('演示模式'.tr); logic.showToast('演示模式'.tr);
} }
}, },
), ),
], ],
backgroundColor: AppColors.mainColor) backgroundColor: AppColors.mainColor)
: null, : null,
body: EasyRefreshTool(onRefresh: () { body: EasyRefreshTool(onRefresh: () {
logic.pageNo = 1; logic.pageNo = 1;
@ -80,116 +134,354 @@ class _MessageListPageState extends State<MessageListPage>
}, child: Obx(() { }, child: Obx(() {
return state.itemDataList.value.isEmpty return state.itemDataList.value.isEmpty
? NoData() ? NoData()
: SlidableAutoCloseBehavior( : Stack(
children: [
Positioned(
top: 0,
left: 0,
right: 0,
child: Container(
height: showNotificationBanner ? 100 : 70,
child: Column(children: [
showNotificationBanner
? Container(
padding:
EdgeInsets.only(left: 10, right: 10),
color: AppColors.messageTipsColor,
height: 30,
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text('开启消息通知开关,及时获取通知',
style: TextStyle(
color: Colors.black,
fontSize: 20.sp)),
Row(children: [
!_pushNotificationEnabled.value
? GestureDetector(child: Text('去开启',
style: TextStyle(
color: Colors.blue,
fontSize: 20.sp)),
onTap: (){
}) : SizedBox.shrink(),
SizedBox(width: 10),
InkWell(
child: Image.asset(
'images/mine/icon_message_close.png',
width: 24.w,
height: 24.w),
onTap: () {
//
setState(() {
showNotificationBanner =
false;
});
}),
])
]))
: SizedBox.shrink(),
Container(
padding:
EdgeInsets.only(left: 15.w, right: 24.w, top: 20.h),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text('告警',
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 30.sp)),
//
GestureDetector(
onTap: () {
// api接口使
// if (_showCheckboxes.value) {
// deleteSelectedMessages();
// } else {
// setState(() {
// _showCheckboxes.value =
// !_showCheckboxes.value;
// });
// }
},
child: _showCheckboxes.value
? Text('删除',
style: TextStyle(
fontSize: 24.sp,
color: Colors.red))
: Image.asset(
'images/mine/icon_message_checbox.png',
width: 30.w,
height: 30.h),
)
]),
)
]))),
Container(
child: Container(
padding: EdgeInsets.only(
top: showNotificationBanner ? 80 : 50),
child: ListView.builder( child: ListView.builder(
itemCount: state.itemDataList.value.length, itemCount: _buildGroupedListItems().length,
itemBuilder: (BuildContext c, int index) { itemBuilder: (BuildContext context, int index) {
final MessageItemEntity messageItemEntity = var item = _buildGroupedListItems()[index];
state.itemDataList.value[index];
return Slidable( if (item is String) {
key: ValueKey(messageItemEntity.id), //
endActionPane: ActionPane( return Container(
extentRatio: 0.2, padding: EdgeInsets.symmetric(
motion: const ScrollMotion(), horizontal: 16.w, vertical: 10.h),
children: <Widget>[ color: AppColors.mainBackgroundColor,
SlidableAction( child: RichText(
onPressed: (BuildContext context) { text: TextSpan(
logic.deletMessageDataRequest( children: [
messageItemEntity.id!, () { TextSpan(
logic.pageNo = 1; text: item.substring(8, 10),
getHttpData(); style: TextStyle(
}); color: Colors.black,
}, fontSize: 36.sp,
backgroundColor: Colors.red, fontWeight: FontWeight.w600,
foregroundColor: Colors.white, ),
label: '删除'.tr, ),
padding: EdgeInsets.only(left: 5.w, right: 5.w), TextSpan(
), text: ' ',
], ),
TextSpan(
text: item.substring(5, 7),
style: TextStyle(
color: Colors.grey, fontSize: 20.sp, fontWeight: FontWeight.w400),
),
],
),
), ),
child: _messageListItem(messageItemEntity, () {
Get.toNamed(Routers.messageDetailPage,
arguments: <String, MessageItemEntity>{
'messageItemEntity': messageItemEntity
});
}),
); );
}), } else if (item is MessageItemEntity) {
); //
return _messageListItem(item, () {
Get.toNamed(Routers.messageDetailPage,
arguments: <String, MessageItemEntity>{
'messageItemEntity': item
});
});
}
return Container();
},
),
),
)
],
);
})), })),
); );
} }
//
List<dynamic> _buildGroupedListItems() {
List<dynamic> items = [];
Map<String, List<MessageItemEntity>> grouped = _groupMessagesByDate();
grouped.forEach((date, messages) {
items.add(date); //
items.addAll(messages.map((message) => message)); //
});
//
if (_selectedItems.length != state.itemDataList.value.length) {
_selectedItems.clear();
_selectedItems.addAll(
List.generate(state.itemDataList.value.length, (index) => false));
}
return items;
}
Widget _messageListItem( Widget _messageListItem(
MessageItemEntity messageItemEntity, Function() action) { MessageItemEntity messageItemEntity, Function() action) {
final int index = state.itemDataList.value.indexOf(messageItemEntity);
//
Map<String, List<MessageItemEntity>> grouped = _groupMessagesByDate();
bool isLastInGroupSimple = false;
//
for (var entry in grouped.entries) {
int messageIndex = entry.value.indexWhere((msg) => msg.id == messageItemEntity.id);
if (messageIndex != -1) {
//
isLastInGroupSimple = messageIndex == entry.value.length - 1;
break;
}
}
return GestureDetector( return GestureDetector(
onTap: action, onTap: () {
child: Container( //
height: 90.h, if (_showCheckboxes.value) {
width: 1.sw, _selectedItems[index] = !_selectedItems[index];
margin: EdgeInsets.only(bottom: 2.h), setState(() {});
decoration: BoxDecoration( } else {
color: Colors.white, //
borderRadius: BorderRadius.circular(10.w), action();
), }
child: Container( },
width: 1.sw, child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
margin: EdgeInsets.only(left: 20.w, right: 20.w), Container(
child: Column( padding: EdgeInsets.only(left: 10),
mainAxisAlignment: MainAxisAlignment.center, transform: Matrix4.translationValues(0, 20, 0),
children: <Widget>[ child: Column(
Row( children: [
children: <Widget>[ Image.asset(
if (messageItemEntity.readAt! == 0) messageItemEntity.readAt! == 0
? 'images/mine/icon_message_unread.png'
: 'images/mine/icon_message_readed.png',
width: 18.w,
height: 18.h),
// 线
if (!isLastInGroupSimple)
Container( Container(
width: 10.w, width: 0.5,
height: 10.w, height: 190.h,
decoration: BoxDecoration( color: AppColors.placeholderTextColor,
color: Colors.red,
borderRadius: BorderRadius.circular(5.w),
),
) )
else ],
Container(), )),
if (messageItemEntity.readAt! == 0) Expanded(
SizedBox(width: 5.w) child: Slidable(
else key: Key(messageItemEntity.id.toString()), // item添加唯一key
Container(), endActionPane: ActionPane(
Flexible( motion: const ScrollMotion(),
child: Text( children: [
messageItemEntity.data!, SlidableAction(
maxLines: 1, onPressed: (context) {
overflow: TextOverflow.ellipsis, //
style: TextStyle( logic.deletMessageDataRequest(
fontSize: 22.sp, messageItemEntity.id!, () {
color: messageItemEntity.readAt! == 0 logic.pageNo = 1;
? AppColors.blackColor getHttpData();
: AppColors.placeholderTextColor), });
), },
backgroundColor: Colors.red,
foregroundColor: Colors.white,
icon: Icons.delete,
label: '删除',
),
],
), child: Container(
width: 1.sw,
margin: EdgeInsets.all(20.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.w),
), ),
], child: Container(
), width: 1.sw,
SizedBox(height: 10.h), margin: EdgeInsets.only(left: 20.w, right: 20.w, top: 8.h, bottom: 12.h),
Row( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
// Image.asset('images/mine/icon_mine_gatewaySignal_strong.png', width: 40.w, height: 40.w,), SizedBox(height: 4.h),
// SizedBox(width: 10.w,), Row(
Text( children: <Widget>[
DateTool().dateToYMDHNString( Flexible(
messageItemEntity.createdAt!.toString()), child: Text(
style: TextStyle( //
fontSize: 18.sp, '远程开门请求',
color: messageItemEntity.readAt! == 0 maxLines: 1,
? AppColors.blackColor overflow: TextOverflow.ellipsis,
: AppColors.placeholderTextColor)), style: TextStyle(
], fontSize: 22.sp,
), color: messageItemEntity.readAt! == 0
SizedBox(width: 20.h), ? AppColors.blackColor
], : AppColors.placeholderTextColor),
), ),
), ),
), ],
); ),
SizedBox(height: 4.h),
Wrap(
children: <Widget>[
// if (messageItemEntity.readAt! == 0)
// Container(
// width: 10.w,
// height: 10.w,
// decoration: BoxDecoration(
// color: Colors.red,
// borderRadius: BorderRadius.circular(5.w),
// ),
// )
// else
// Container(),
// if (messageItemEntity.readAt! == 0)
// SizedBox(width: 5.w)
// else
// Container(),
Container(
margin: EdgeInsets.only(top: 4.h),
child: Text(
DateTool().dateToHnString(messageItemEntity.createdAt!.toString()),
style: TextStyle(
fontSize: 18.sp,
color: messageItemEntity.readAt! == 0
? AppColors.blackColor
: AppColors.placeholderTextColor,
),
),
),
Container(
width: 1,
height: 10,
margin: EdgeInsets.only(left: 5.w, right: 5.w, top: 8.h),
color: messageItemEntity.readAt! == 0
? AppColors.blackColor
: AppColors.placeholderTextColor,
),
Container(transform: Matrix4.translationValues(0, -18, 0),
child: Text(' ${messageItemEntity.data!}',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 18.sp,
color: messageItemEntity.readAt! == 0
? AppColors.blackColor
: AppColors.placeholderTextColor,
),
)),
Container(child: GestureDetector(
child: Text('点击查看', style: TextStyle(fontWeight: FontWeight.w500, fontSize: 18.sp, color: AppColors.mainColor),),
),alignment: Alignment.centerRight,)
],
),
// SizedBox(height: 5.h),
// Row(
// mainAxisAlignment: MainAxisAlignment.start,
// children: <Widget>[
// // Image.asset('images/mine/icon_mine_gatewaySignal_strong.png', width: 40.w, height: 40.w,),
// // SizedBox(width: 10.w,),
// Text(
// DateTool().dateToHnString(messageItemEntity
// .createdAt!
// .toString()),
// style: TextStyle(
// fontSize: 18.sp,
// color: messageItemEntity.readAt! == 0
// ? AppColors.blackColor
// : AppColors.placeholderTextColor)),
// ],
// ),
SizedBox(width: 20.h),
]))))),
//
if (_showCheckboxes.value)
Checkbox(
value: _selectedItems[index],
activeColor: Colors.blue,
onChanged: (val) {
_selectedItems[index] = val!;
setState(() {});
},
)
]));
} }
} }

View File

@ -194,6 +194,45 @@ class DateTool {
return appointmentDate; return appointmentDate;
} }
///
String dateToMMString(String? timestamp) {
timestamp ??= '0';
int time = int.parse(timestamp);
if (timestamp.length == 10) {
time = time * 1000;
}
final DateTime nowDate = DateTime.fromMillisecondsSinceEpoch(time);
final String appointmentDate =
formatDate(nowDate, <String>[mm]);
return appointmentDate;
}
///
String dateToDDString(String? timestamp) {
timestamp ??= '0';
int time = int.parse(timestamp);
if (timestamp.length == 10) {
time = time * 1000;
}
final DateTime nowDate = DateTime.fromMillisecondsSinceEpoch(time);
final String appointmentDate =
formatDate(nowDate, <String>[dd]);
return appointmentDate;
}
///
String dateToHnString(String? timestamp) {
timestamp ??= '0';
int time = int.parse(timestamp);
if (timestamp.length == 10) {
time = time * 1000;
}
final DateTime nowDate = DateTime.fromMillisecondsSinceEpoch(time);
final String appointmentDate =
formatDate(nowDate, <String>[HH, ':', nn]);
return appointmentDate;
}
/// (-- :) /// (-- :)
String dateIntToYMDHNString(int? time) { String dateIntToYMDHNString(int? time) {
time ??= 0; time ??= 0;