import 'dart:convert'; import 'package:flutter/material.dart'; import '../api/oa_client.dart'; import '../im/chat_local_store.dart'; import '../nav/notice_route.dart'; import '../session/session.dart'; import '../theme.dart'; import '../widgets/wecom.dart'; class SystemNoticePage extends StatefulWidget { const SystemNoticePage( {super.key, required this.session, required this.api, this.conversationId}); final SessionStore session; final OaClient api; final String? conversationId; @override State createState() => _SystemNoticePageState(); } class _SystemNoticePageState extends State { List> _items = []; bool _loading = true; Map _metaOf(Map row) { final raw = row['meta']; if (raw is Map) return Map.from(raw); if (raw is String && raw.isNotEmpty) { try { final decoded = jsonDecode(raw); if (decoded is Map) return Map.from(decoded); } catch (_) {} } return {}; } @override void initState() { super.initState(); _load(); } Future _load() async { try { final raw = await widget.api.get('/im/messages', query: { 'peerId': '0', if (widget.conversationId != null) 'conversationId': widget.conversationId!, }); final remote = asMaps(raw); final cid = widget.conversationId ?? (raw is Map ? '${raw['conversationId'] ?? ''}' : ''); final local = await ChatLocalStore.load(cid); final byKey = >{}; for (final e in [...local, ...remote]) { final fp = '${e['fingerprint'] ?? ''}'; byKey[fp.isNotEmpty ? 'fp:$fp' : 'id:${e['id'] ?? ''}'] = e; } final merged = byKey.values.toList() ..sort((a, b) => '${a['createdAt'] ?? ''}'.compareTo('${b['createdAt'] ?? ''}')); if (!mounted) return; setState(() { _items = merged; _loading = false; }); await ChatLocalStore.save(cid, merged); if (widget.conversationId != null) { await widget.api .post('/im/read', {'conversationId': widget.conversationId}); } } catch (_) { if (mounted) setState(() => _loading = false); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF5F6F8), appBar: AppBar(title: const Text('系统通知'), backgroundColor: Colors.white), body: _loading ? const Center(child: CircularProgressIndicator()) : RefreshIndicator( onRefresh: _load, child: ListView.builder( padding: const EdgeInsets.fromLTRB(14, 14, 14, 28), itemCount: _items.isEmpty ? 1 : _items.length, itemBuilder: (context, index) { if (_items.isEmpty) return const Padding( padding: EdgeInsets.only(top: 100), child: Center( child: Text('暂无系统通知', style: TextStyle(color: kMute)))); final r = _items[index]; final title = '${r['title'] ?? r['body'] ?? '系统通知'}'; final body = '${r['body'] ?? ''}'; final time = '${r['createdAt'] ?? ''}' .replaceFirst('T', ' ') .replaceFirst(RegExp(r'\.\d+Z$'), ''); return Card( elevation: 0, margin: const EdgeInsets.only(bottom: 10), child: InkWell( onTap: () { final meta = _metaOf(r); openNoticeTarget( context, widget.api, session: widget.session, kind: '${meta['kind'] ?? r['kind'] ?? 'todo'}', bizType: '${meta['bizType'] ?? ''}', bizId: '${meta['bizId'] ?? ''}', title: '${meta['title'] ?? r['title'] ?? r['body'] ?? '系统通知'}', ); }, child: Padding( padding: const EdgeInsets.fromLTRB(14, 12, 14, 12), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SquareAvatar( label: '系', color: Color(0xFF07C160), icon: Icons.notifications, size: 40), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600)), if (body.isNotEmpty && body != title) ...[ const SizedBox(height: 5), Text(body, style: const TextStyle(color: kMute)) ], const SizedBox(height: 7), Text(time, style: const TextStyle( fontSize: 12, color: kMute)), ])), ]), ), ), ); }, ), ), ); } }