modify icon & add notify function
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import './message_model.dart';
|
||||
import '../services/generic_api_service.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class MessageApiService {
|
||||
final GenericApiService _apiService = GenericApiService();
|
||||
|
||||
// 獲取個人通知訊息紀錄 (僅顯示 30 天內)
|
||||
Future<List<EipMessage>> fetchMessages(String userId) async {
|
||||
// 計算 30 天前的日期,作為查詢參數
|
||||
final thirtyDaysAgo = DateTime.now().subtract(const Duration(days: 30));
|
||||
final dateParam = DateFormat('yyyy-MM-dd').format(thirtyDaysAgo);
|
||||
|
||||
// 格式:當前頁^每頁筆數^排序欄位^關鍵字欄位^關鍵字內容
|
||||
// 排序:按通知日期 (create_date) 降冪排列
|
||||
// 實務上可根據您的 Generic API 支援度,將 dateParam 傳入作為過濾條件
|
||||
// String queryFilter = "1^100^create_date^*^^^receivers^$userId^after^$dateParam";
|
||||
String queryFilter = "1^100^create_date^*^^^receivers^$userId^^";
|
||||
|
||||
return await _apiService.fetchList<EipMessage>(
|
||||
tableName: "eip_message", // 指向訊息資料表
|
||||
pk: "id", //
|
||||
queryFilter: queryFilter,
|
||||
fromJson: (json) => EipMessage.fromJson(json),
|
||||
);
|
||||
}
|
||||
|
||||
// (選用) 更新訊息為已讀狀態
|
||||
Future<bool> markAsRead(int messageId) async {
|
||||
// 實作呼叫更新 msg_status 的 API 邏輯
|
||||
// ...
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import './message_model.dart';
|
||||
|
||||
class MessageDetail extends StatelessWidget {
|
||||
final EipMessage message;
|
||||
|
||||
const MessageDetail({required this.message, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('訊息內容'),
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
elevation: 0.5,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 主旨區塊
|
||||
Text(
|
||||
message.subjectLine,
|
||||
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold, height: 1.4),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// 寄件資訊區塊
|
||||
Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
backgroundColor: Colors.blueGrey.shade100,
|
||||
child: const Icon(Icons.person, color: Colors.blueGrey),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
message.sender, //
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
|
||||
),
|
||||
Text(
|
||||
message.formattedDate,
|
||||
style: const TextStyle(fontSize: 14, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 20.0),
|
||||
child: Divider(),
|
||||
),
|
||||
|
||||
// 訊息內文區塊
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade50,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
message.msgContent.isNotEmpty ? message.msgContent : '無內容', //
|
||||
style: const TextStyle(fontSize: 16, height: 1.6, color: Colors.black87),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import './message_model.dart';
|
||||
import './message_api.dart';
|
||||
import './message_detail.dart';
|
||||
|
||||
class MessageManager extends StatefulWidget {
|
||||
final String currentUserId;
|
||||
const MessageManager({required this.currentUserId, super.key});
|
||||
|
||||
@override
|
||||
State<MessageManager> createState() => _MessageManagerState();
|
||||
}
|
||||
|
||||
class _MessageManagerState extends State<MessageManager> {
|
||||
late MessageApiService _apiService;
|
||||
late Future<List<EipMessage>> _messageFuture;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_apiService = MessageApiService();
|
||||
_refreshList();
|
||||
}
|
||||
|
||||
void _refreshList() {
|
||||
setState(() {
|
||||
_messageFuture = _apiService.fetchMessages(widget.currentUserId);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('通知訊息'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: _refreshList,
|
||||
)
|
||||
],
|
||||
),
|
||||
body: FutureBuilder<List<EipMessage>>(
|
||||
future: _messageFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||
return const Center(child: Text('近 30 天內無任何通知訊息'));
|
||||
}
|
||||
return ListView.builder(
|
||||
itemCount: snapshot.data!.length,
|
||||
itemBuilder: (ctx, i) => _buildMessageCard(snapshot.data![i]),
|
||||
);
|
||||
},
|
||||
),
|
||||
// 此為唯讀功能,因此移除 FloatingActionButton
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMessageCard(EipMessage item) {
|
||||
return Card(
|
||||
elevation: 0.5,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(color: Colors.grey.shade200),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: ListTile(
|
||||
onTap: () async {
|
||||
// 點擊進入詳情
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MessageDetail(message: item)),
|
||||
);
|
||||
// 若在詳情頁有觸發「已讀」,返回時可重新整理列表
|
||||
_refreshList();
|
||||
},
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: item.statusColor.withOpacity(0.1),
|
||||
child: Icon(
|
||||
item.isRead ? Icons.mark_email_read : Icons.mark_email_unread,
|
||||
color: item.statusColor,
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
item.subjectLine, //
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontWeight: item.isRead ? FontWeight.normal : FontWeight.bold,
|
||||
),
|
||||
),
|
||||
subtitle: Text(item.formattedDate),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class EipMessage {
|
||||
final int id; // id (Primary Key)
|
||||
final String sender; // sender (發件人)
|
||||
final String receivers; // receivers (收件人)
|
||||
final String subjectLine; // subject_line (主旨)
|
||||
final String msgContent; // msg_content (內容)
|
||||
final String msgStatus; // msg_status (閱讀狀態)
|
||||
final DateTime? createDate; // create_date (通知日期)
|
||||
|
||||
EipMessage({
|
||||
required this.id,
|
||||
this.sender = '',
|
||||
this.receivers = '',
|
||||
this.subjectLine = '',
|
||||
this.msgContent = '',
|
||||
this.msgStatus = '0',
|
||||
this.createDate,
|
||||
});
|
||||
|
||||
factory EipMessage.fromJson(Map<String, dynamic> json) {
|
||||
return EipMessage(
|
||||
id: int.tryParse(json['id']?.toString() ?? '0') ?? 0,
|
||||
sender: json['sender'] as String? ?? '系統通知',
|
||||
receivers: json['receivers'] as String? ?? '',
|
||||
subjectLine: json['subject_line'] as String? ?? '無主旨',
|
||||
msgContent: json['msg_content'] as String? ?? '',
|
||||
msgStatus: json['msg_status'] as String? ?? '0',
|
||||
createDate: json['create_date'] != null ? DateTime.tryParse(json['create_date']) : null,
|
||||
);
|
||||
}
|
||||
|
||||
// 格式化顯示通知日期
|
||||
String get formattedDate {
|
||||
if (createDate == null) return '未知時間';
|
||||
final df = DateFormat('yyyy/MM/dd HH:mm');
|
||||
return df.format(createDate!);
|
||||
}
|
||||
|
||||
// 判斷是否已讀 (假設 '1' 為已讀,'0' 為未讀)
|
||||
bool get isRead => msgStatus == '1';
|
||||
|
||||
// 狀態視覺呈現
|
||||
Color get statusColor => isRead ? Colors.grey : Colors.blueAccent;
|
||||
String get statusText => isRead ? '已讀' : '未讀';
|
||||
}
|
||||
Reference in New Issue
Block a user