From b5cce74f4910b83eb8a87ebd7d957ec0af6c7a91 Mon Sep 17 00:00:00 2001 From: qzl Date: Wed, 25 Feb 2026 11:05:42 +0800 Subject: [PATCH] feat(messages): add invite list and detail screens --- .../screens/message_invite_detail_screen.dart | 293 ++++++++++++++++++ .../screens/message_invite_list_screen.dart | 136 ++++++++ 2 files changed, 429 insertions(+) create mode 100644 apps/lib/features/messages/ui/screens/message_invite_detail_screen.dart create mode 100644 apps/lib/features/messages/ui/screens/message_invite_list_screen.dart diff --git a/apps/lib/features/messages/ui/screens/message_invite_detail_screen.dart b/apps/lib/features/messages/ui/screens/message_invite_detail_screen.dart new file mode 100644 index 0000000..15fde8c --- /dev/null +++ b/apps/lib/features/messages/ui/screens/message_invite_detail_screen.dart @@ -0,0 +1,293 @@ +import 'package:flutter/material.dart' hide BackButton; +import 'package:go_router/go_router.dart'; +import '../../../../core/theme/design_tokens.dart'; +import '../../../../shared/widgets/page_header.dart'; + +class MessageInviteDetailScreen extends StatefulWidget { + const MessageInviteDetailScreen({super.key}); + + @override + State createState() => + _MessageInviteDetailScreenState(); +} + +class _MessageInviteDetailScreenState extends State { + final _reasonController = TextEditingController(); + + @override + void dispose() { + _reasonController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.messageBg, + body: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PageHeader(leading: BackButton(onPressed: () => context.pop())), + Expanded( + child: SingleChildScrollView( + padding: const EdgeInsets.only(left: 20, right: 20, bottom: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildSummaryCard(), + const SizedBox(height: 14), + _buildCalendarTip(), + const SizedBox(height: 14), + _buildActionRow(), + const SizedBox(height: 14), + _buildRejectReasonCard(), + ], + ), + ), + ), + ], + ), + ), + ); + } + + Widget _buildSummaryCard() { + return Container( + width: double.infinity, + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: AppColors.messageCardBg, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.messageCardBorder), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + Text( + '日历邀请详情', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.slate900, + ), + ), + SizedBox(height: 10), + Text( + '事件:产品评审会', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: AppColors.slate700, + ), + ), + SizedBox(height: 10), + Text( + '邀请人:李文浩(产品经理)', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.normal, + color: AppColors.slate500, + ), + ), + SizedBox(height: 10), + Text( + '时间:2026-02-12 14:00 - 15:30', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.normal, + color: AppColors.slate500, + ), + ), + SizedBox(height: 10), + Text( + '地点:3F-A 会议室 / 腾讯会议 231-889-100', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.normal, + color: AppColors.slate500, + ), + ), + SizedBox(height: 10), + Text( + '备注:请提前准备本周版本风险与排期结论。', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.normal, + color: AppColors.slate500, + ), + ), + ], + ), + ); + } + + Widget _buildCalendarTip() { + return Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + decoration: BoxDecoration( + color: AppColors.messageTipBg, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: AppColors.messageTipBorder), + ), + child: Row( + children: const [ + Icon(Icons.info_outline, size: 14, color: AppColors.slate500), + SizedBox(width: 8), + Expanded( + child: Text( + '更多附件与相关链接,请在订阅后在日历中查看', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + color: AppColors.slate500, + ), + ), + ), + ], + ), + ); + } + + Widget _buildActionRow() { + return SizedBox( + height: 46, + child: Row( + children: [ + Expanded( + child: GestureDetector( + onTap: _handleReject, + child: Container( + decoration: BoxDecoration( + color: AppColors.messageCardBg, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: AppColors.messageRejectBorder), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: const [ + Text( + '×', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w700, + color: AppColors.red400, + ), + ), + SizedBox(width: 6), + Text( + '拒绝', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: AppColors.red400, + ), + ), + ], + ), + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: GestureDetector( + onTap: _handleAccept, + child: Container( + decoration: BoxDecoration( + color: AppColors.messageTagBg, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: AppColors.messageAcceptBorder), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: const [ + Text( + '√', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w700, + color: AppColors.blue600, + ), + ), + SizedBox(width: 6), + Text( + '同意', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: AppColors.blue600, + ), + ), + ], + ), + ), + ), + ), + ], + ), + ); + } + + Widget _buildRejectReasonCard() { + return Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: AppColors.messageCardBg, + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.messageReasonBorder), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + '拒绝补充信息(可选)', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: AppColors.slate600, + ), + ), + const SizedBox(height: 8), + Container( + height: 92, + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: AppColors.messageBtnWrap, + borderRadius: BorderRadius.circular(10), + border: Border.all(color: AppColors.messageInputBorder), + ), + child: TextField( + controller: _reasonController, + maxLines: null, + expands: true, + style: const TextStyle(fontSize: 12, color: AppColors.slate700), + decoration: const InputDecoration( + border: InputBorder.none, + hintText: '可填写拒绝原因,例如:该时间段已有客户会议,无法参加。', + hintStyle: TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + color: AppColors.messagePlaceholder, + ), + contentPadding: EdgeInsets.zero, + ), + ), + ), + ], + ), + ); + } + + void _handleReject() { + final reason = _reasonController.text; + debugPrint('Reject with reason: $reason'); + context.pop(); + } + + void _handleAccept() { + debugPrint('Accept invitation'); + context.pop(); + } +} diff --git a/apps/lib/features/messages/ui/screens/message_invite_list_screen.dart b/apps/lib/features/messages/ui/screens/message_invite_list_screen.dart new file mode 100644 index 0000000..8eac696 --- /dev/null +++ b/apps/lib/features/messages/ui/screens/message_invite_list_screen.dart @@ -0,0 +1,136 @@ +import 'package:flutter/material.dart' hide BackButton; +import 'package:go_router/go_router.dart'; +import '../../../../core/theme/design_tokens.dart'; +import '../../../../shared/widgets/page_header.dart'; + +class MessageInviteListScreen extends StatelessWidget { + const MessageInviteListScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.messageBg, + body: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PageHeader(leading: BackButton(onPressed: () => context.pop())), + Expanded( + child: Padding( + padding: const EdgeInsets.only(left: 20, right: 20, bottom: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildRemindTag(), + const SizedBox(height: 12), + _buildInviteCard(context), + const Spacer(), + ], + ), + ), + ), + ], + ), + ), + ); + } + + Widget _buildRemindTag() { + return Container( + height: 24, + padding: const EdgeInsets.symmetric(horizontal: 10), + decoration: BoxDecoration( + color: AppColors.messageTagBg, + borderRadius: BorderRadius.circular(12), + ), + child: const Center( + child: Text( + '消息提醒', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: AppColors.blue600, + ), + ), + ), + ); + } + + Widget _buildInviteCard(BuildContext context) { + return GestureDetector( + onTap: () => context.push('/messages/invite/detail'), + child: Container( + width: double.infinity, + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: AppColors.messageCardBg, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.messageCardBorder), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: AppColors.messageCalendarBg, + borderRadius: BorderRadius.circular(12), + ), + child: const Icon( + Icons.calendar_today_outlined, + size: 20, + color: AppColors.blue500, + ), + ), + ], + ), + const SizedBox(height: 8), + const Text( + '事件:产品评审会 2026-02-12 14:00', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + color: AppColors.slate700, + ), + ), + const SizedBox(height: 8), + const Text( + '邀请人:李文浩', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.normal, + color: AppColors.slate500, + ), + ), + const SizedBox(height: 8), + const Text( + '点击查看详情并处理邀请', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + color: AppColors.slate400, + ), + ), + ], + ), + ), + const SizedBox(width: 8), + Icon( + Icons.chevron_right, + size: 16, + color: AppColors.messageArrowColor, + ), + ], + ), + ), + ); + } +}