消息中心

This commit is contained in:
2026-01-07 17:43:22 +08:00
parent 953e5e773c
commit 7d9c879a4e
6 changed files with 295 additions and 1 deletions

View File

@@ -0,0 +1,106 @@
import 'package:get/get.dart';
import 'package:getx_scaffold/getx_scaffold.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'model.dart';
class MessageController extends GetxController {
final RefreshController refreshController = RefreshController(initialRefresh: false);
final RxList<MessageModel> messageList = <MessageModel>[].obs;
int _pageNum = 1;
final int _pageSize = 10;
// 模拟所有已读状态,实际应根据接口判断
final RxBool allRead = false.obs;
@override
void onInit() {
super.onInit();
_loadData(isRefresh: true);
}
Future<void> _loadData({bool isRefresh = false}) async {
if (isRefresh) {
_pageNum = 1;
} else {
_pageNum++;
}
try {
// 模拟请求延迟
await Future.delayed(const Duration(milliseconds: 500));
// 模拟数据 (请替换为实际接口请求)
// var response = await HttpService.to.post('message/list', data: {'pageNum': _pageNum, 'pageSize': _pageSize});
List<MessageModel> newData = List.generate(10, (index) {
int id = (_pageNum - 1) * _pageSize + index;
return MessageModel(
id: id.toString(),
title: '加氢预约失败通知',
content: '1月6日14时30分-1月6日15时30分北京朝阳加氢站加氢预约失败。原因设备维护',
createTime: '刚刚',
isRead: index % 3 == 0 ? 1 : 0,
);
});
if (isRefresh) {
messageList.assignAll(newData);
refreshController.refreshCompleted();
} else {
if (newData.isEmpty) {
refreshController.loadNoData();
} else {
messageList.addAll(newData);
refreshController.loadComplete();
}
}
_checkAllRead();
} catch (e) {
if (isRefresh) {
refreshController.refreshFailed();
} else {
refreshController.loadFailed();
}
}
}
void onRefresh() => _loadData(isRefresh: true);
void onLoading() => _loadData(isRefresh: false);
// 标记全部已读
void markAllRead() async {
showLoading('正在标记...');
try {
// await HttpService.to.post('message/readAll');
await Future.delayed(const Duration(milliseconds: 500));
for (var msg in messageList) {
msg.isRead = 1;
}
messageList.refresh();
allRead.value = true;
dismissLoading();
showSuccessToast('全部已读');
} catch (e) {
dismissLoading();
showErrorToast('操作失败');
}
}
// 标记单条已读(弹窗显示时调用)
void markRead(MessageModel message) async {
if (message.isRead == 1) return;
// await HttpService.to.post('message/read', data: {'id': message.id});
message.isRead = 1;
messageList.refresh();
_checkAllRead();
}
void _checkAllRead() {
allRead.value = messageList.every((msg) => msg.isRead == 1);
}
}

View File

@@ -0,0 +1,25 @@
class MessageModel {
final String id;
final String title;
final String content;
final String createTime;
int isRead; // 0: 未读, 1: 已读
MessageModel({
required this.id,
required this.title,
required this.content,
required this.createTime,
required this.isRead,
});
factory MessageModel.fromJson(Map<String, dynamic> json) {
return MessageModel(
id: json['id']?.toString() ?? '',
title: json['title']?.toString() ?? '消息通知',
content: json['content']?.toString() ?? '',
createTime: json['createTime']?.toString() ?? '',
isRead: json['isRead'] as int? ?? 0,
);
}
}

View File

@@ -0,0 +1,150 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'controller.dart';
import 'model.dart';
class MessagePage extends GetView<MessageController> {
const MessagePage({super.key});
@override
Widget build(BuildContext context) {
Get.put(MessageController());
return Scaffold(
backgroundColor: const Color(0xFFF5F5F5),
appBar: AppBar(title: const Text('消息通知'), centerTitle: true),
body: Column(
children: [
Expanded(
child: Obx(() => SmartRefresher(
controller: controller.refreshController,
enablePullUp: true,
onRefresh: controller.onRefresh,
onLoading: controller.onLoading,
child: ListView.separated(
padding: const EdgeInsets.all(12),
itemCount: controller.messageList.length,
separatorBuilder: (_, __) => const SizedBox(height: 12),
itemBuilder: (context, index) {
return _buildMessageItem(context, controller.messageList[index]);
},
),
)),
),
Obx(() => !controller.allRead.value
? Container(
padding: const EdgeInsets.all(16),
color: Colors.white,
child: ElevatedButton(
onPressed: controller.markAllRead,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
minimumSize: const Size(double.infinity, 44),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
child: const Text('全部标为已读', style: TextStyle(fontSize: 16, color: Colors.white)),
),
)
: const SizedBox.shrink()),
],
),
);
}
Widget _buildMessageItem(BuildContext context, MessageModel item) {
return GestureDetector(
onTap: () {
controller.markRead(item);
_showMessageDialog(context, item);
},
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.only(top: 6, right: 12),
width: 8,
height: 8,
decoration: BoxDecoration(
color: item.isRead == 1 ? Colors.grey[300] : const Color(0xFFFAAD14),
shape: BoxShape.circle,
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.title,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black87),
),
const SizedBox(height: 8),
Text(
item.content,
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 8),
Text(
item.createTime,
style: TextStyle(fontSize: 12, color: Colors.grey[400]),
),
],
),
),
],
),
),
);
}
void _showMessageDialog(BuildContext context, MessageModel item) {
showDialog(
context: context,
barrierDismissible: true,
builder: (context) {
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
child: Container(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.title,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
Text(
item.content,
style: const TextStyle(fontSize: 15, height: 1.5, color: Colors.black87),
),
const SizedBox(height: 24),
Align(
alignment: Alignment.centerRight,
child: OutlinedButton(
onPressed: () => Navigator.pop(context),
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Colors.blue),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
),
child: const Text('确认', style: TextStyle(color: Colors.blue)),
),
),
],
),
),
);
},
);
}
}

View File

@@ -3,6 +3,7 @@ import 'package:get/get.dart';
import 'package:getx_scaffold/common/index.dart'; import 'package:getx_scaffold/common/index.dart';
import 'package:getx_scaffold/common/widgets/index.dart'; import 'package:getx_scaffold/common/widgets/index.dart';
import 'package:ln_jq_app/common/styles/theme.dart'; import 'package:ln_jq_app/common/styles/theme.dart';
import 'package:ln_jq_app/pages/c_page/message/view.dart';
import 'package:ln_jq_app/storage_service.dart'; import 'package:ln_jq_app/storage_service.dart';
import 'controller.dart'; import 'controller.dart';
@@ -82,7 +83,8 @@ class MinePage extends GetView<MineController> {
), ),
IconButton( IconButton(
onPressed: () { onPressed: () {
// TODO: 跳转 // 跳转消息中心
Get.to(() => const MessagePage());
}, },
// 这里的 style 是为了模拟你图片里的灰色圆形背景 // 这里的 style 是为了模拟你图片里的灰色圆形背景
style: IconButton.styleFrom( style: IconButton.styleFrom(

View File

@@ -933,6 +933,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "6.0.3" version: "6.0.3"
pull_to_refresh:
dependency: "direct main"
description:
name: pull_to_refresh
sha256: bbadd5a931837b57739cf08736bea63167e284e71fb23b218c8c9a6e042aad12
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.0"
rational: rational:
dependency: transitive dependency: transitive
description: description:

View File

@@ -51,6 +51,9 @@ dependencies:
flutter_inappwebview: ^6.1.5 # WebView插件 flutter_inappwebview: ^6.1.5 # WebView插件
geolocator: ^14.0.2 # 获取精确定位 geolocator: ^14.0.2 # 获取精确定位
aliyun_push_flutter: ^1.3.6 aliyun_push_flutter: ^1.3.6
pull_to_refresh: ^2.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: