diff --git a/apps/lib/features/settings/ui/screens/features_screen.dart b/apps/lib/features/settings/ui/screens/features_screen.dart new file mode 100644 index 0000000..1acbb77 --- /dev/null +++ b/apps/lib/features/settings/ui/screens/features_screen.dart @@ -0,0 +1,206 @@ +import 'package:flutter/material.dart'; +import '../../../../core/theme/design_tokens.dart'; +import '../../../../shared/widgets/page_header.dart' as widgets; + +class FeaturesScreen extends StatefulWidget { + const FeaturesScreen({super.key}); + + @override + State createState() => _FeaturesScreenState(); +} + +class _FeaturesScreenState extends State { + bool _dailyReminderEnabled = true; + bool _dailySummaryEnabled = false; + bool _weeklyReportEnabled = true; + bool _weeklyDigestEnabled = false; + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: const Color(0xFFF8FAFC), + body: SafeArea( + child: Column( + children: [ + const widgets.PageHeader(leading: widgets.BackButton()), + Expanded( + child: SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(20, 8, 20, 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildSectionTitle('每日'), + const SizedBox(height: 8), + _buildDailyList(), + const SizedBox(height: 16), + _buildSectionTitle('每周'), + const SizedBox(height: 8), + _buildWeeklyList(), + ], + ), + ), + ), + ], + ), + ), + ); + } + + Widget _buildSectionTitle(String title) { + return Text( + title, + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: AppColors.slate500, + ), + ); + } + + Widget _buildDailyList() { + return Column( + children: [ + _buildFeatureCard( + icon: Icons.alarm, + iconColor: const Color(0xFF14B8A6), + iconBg: const Color(0xFFECFEFF), + iconBorder: const Color(0xFFC9F4F2), + title: '会议提醒', + subtitle: '每次会议前 15 分钟提醒', + value: _dailyReminderEnabled, + onChanged: (v) => setState(() => _dailyReminderEnabled = v), + ), + const SizedBox(height: 10), + _buildFeatureCard( + icon: Icons.summarize, + iconColor: const Color(0xFF2563EB), + iconBg: const Color(0xFFEEF6FF), + iconBorder: const Color(0xFFDCEAFF), + title: '每日摘要', + subtitle: '每天 18:00 发送当日摘要', + value: _dailySummaryEnabled, + onChanged: (v) => setState(() => _dailySummaryEnabled = v), + ), + ], + ); + } + + Widget _buildWeeklyList() { + return Column( + children: [ + _buildFeatureCard( + icon: Icons.calendar_view_week, + iconColor: const Color(0xFF10B981), + iconBg: const Color(0xFFECFDF5), + iconBorder: const Color(0xFFCDEEDC), + title: '周报生成', + subtitle: '每周一自动生成周报', + value: _weeklyReportEnabled, + onChanged: (v) => setState(() => _weeklyReportEnabled = v), + ), + const SizedBox(height: 10), + _buildFeatureCard( + icon: Icons.article, + iconColor: const Color(0xFFF59E0B), + iconBg: const Color(0xFFFFF7ED), + iconBorder: const Color(0xFFFDE6CD), + title: '每周摘要', + subtitle: '每周日发送本周活动汇总', + value: _weeklyDigestEnabled, + onChanged: (v) => setState(() => _weeklyDigestEnabled = v), + ), + ], + ); + } + + Widget _buildFeatureCard({ + required IconData icon, + required Color iconColor, + required Color iconBg, + required Color iconBorder, + required String title, + required String subtitle, + required bool value, + required ValueChanged onChanged, + }) { + return Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: AppColors.white, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: const Color(0xFFE4ECF6)), + ), + child: Row( + children: [ + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: iconBg, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: iconBorder), + ), + child: Icon(icon, size: 18, color: iconColor), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: AppColors.slate900, + ), + ), + const SizedBox(height: 4), + Text( + subtitle, + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + color: AppColors.slate500, + ), + ), + ], + ), + ), + _buildToggle(value, onChanged), + ], + ), + ); + } + + Widget _buildToggle(bool value, ValueChanged onChanged) { + return GestureDetector( + onTap: () => onChanged(!value), + child: Container( + width: 44, + height: 24, + padding: const EdgeInsets.all(2), + decoration: BoxDecoration( + color: value ? const Color(0xFFBFDBFE) : const Color(0xFFF1F5FC), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: value ? const Color(0xFF93C5FD) : const Color(0xFFD5DFEE), + ), + ), + child: AnimatedAlign( + duration: const Duration(milliseconds: 150), + alignment: value ? Alignment.centerRight : Alignment.centerLeft, + child: Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: AppColors.white, + borderRadius: BorderRadius.circular(10), + border: Border.all(color: const Color(0xFFCCDDF8)), + ), + ), + ), + ), + ); + } +} diff --git a/apps/lib/features/settings/ui/screens/memory_screen.dart b/apps/lib/features/settings/ui/screens/memory_screen.dart new file mode 100644 index 0000000..c900e86 --- /dev/null +++ b/apps/lib/features/settings/ui/screens/memory_screen.dart @@ -0,0 +1,242 @@ +import 'package:flutter/material.dart'; +import '../../../../core/theme/design_tokens.dart'; +import '../../../../shared/widgets/page_header.dart' as widgets; + +class MemoryScreen extends StatefulWidget { + const MemoryScreen({super.key}); + + @override + State createState() => _MemoryScreenState(); +} + +class _MemoryScreenState extends State { + bool _memoryEnabled = true; + + final List _memoryItems = [ + MemoryItem(icon: Icons.language, title: '语言偏好', subtitle: '沟通时偏好使用中文'), + MemoryItem(icon: Icons.schedule, title: '工作时间', subtitle: '工作日 9:00-18:00'), + MemoryItem(icon: Icons.meeting_room, title: '会议习惯', subtitle: '偏好下午安排会议'), + ]; + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: const Color(0xFFF8FAFC), + body: SafeArea( + child: Column( + children: [ + widgets.PageHeader(leading: widgets.BackButton(), height: 56), + Expanded( + child: SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(20, 12, 20, 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildToggleCard(), + const SizedBox(height: 14), + _buildListTitle(), + const SizedBox(height: 8), + _buildMemoryList(), + const SizedBox(height: 20), + _buildManageButton(), + ], + ), + ), + ), + ], + ), + ), + ); + } + + Widget _buildToggleCard() { + return Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: AppColors.white, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: const Color(0xFFE2E8F0)), + ), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + '启用记忆', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.slate900, + ), + ), + _buildToggle(_memoryEnabled, (v) { + setState(() => _memoryEnabled = v); + }), + ], + ), + const SizedBox(height: 10), + const Align( + alignment: Alignment.centerLeft, + child: Text( + '开启后,将持续记录并更新你的长期偏好', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + color: Color(0xFF71839F), + ), + ), + ), + ], + ), + ); + } + + Widget _buildListTitle() { + return const Text( + '记忆条目', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: AppColors.slate500, + ), + ); + } + + Widget _buildMemoryList() { + return Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: AppColors.white, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: const Color(0xFFE1E8F3)), + ), + child: Column( + children: [ + for (int i = 0; i < _memoryItems.length; i++) ...[ + _buildMemoryItem(_memoryItems[i]), + if (i < _memoryItems.length - 1) const SizedBox(height: 10), + ], + ], + ), + ); + } + + Widget _buildMemoryItem(MemoryItem item) { + return GestureDetector( + onTap: () {}, + child: Container( + height: 74, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: const Color(0xFFF8FAFF), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: const Color(0xFFE8EDF7)), + ), + child: Row( + children: [ + Container( + width: 32, + height: 32, + decoration: BoxDecoration( + color: const Color(0xFFE8F3FF), + borderRadius: BorderRadius.circular(10), + ), + child: Icon(item.icon, size: 16, color: AppColors.blue500), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + item.title, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: AppColors.slate900, + ), + ), + const SizedBox(height: 4), + Text( + item.subtitle, + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + color: AppColors.slate500, + ), + ), + ], + ), + ), + const Icon(Icons.chevron_right, size: 16, color: Color(0xFF9AAAC1)), + ], + ), + ), + ); + } + + Widget _buildManageButton() { + return GestureDetector( + onTap: () {}, + child: Container( + height: 44, + decoration: BoxDecoration( + color: AppColors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: const Color(0xFFDCE6F4)), + ), + child: const Center( + child: Text( + '管理记忆条目', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: AppColors.slate700, + ), + ), + ), + ), + ); + } + + Widget _buildToggle(bool value, ValueChanged onChanged) { + return GestureDetector( + onTap: () => onChanged(!value), + child: Container( + width: 44, + height: 24, + padding: const EdgeInsets.all(2), + decoration: BoxDecoration( + color: value ? const Color(0xFFBFDBFE) : const Color(0xFFF1F5FC), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: value ? const Color(0xFF93C5FD) : const Color(0xFFD5DFEE), + ), + ), + child: AnimatedAlign( + duration: const Duration(milliseconds: 150), + alignment: value ? Alignment.centerRight : Alignment.centerLeft, + child: Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: AppColors.white, + borderRadius: BorderRadius.circular(10), + border: Border.all(color: const Color(0xFFCCDDF8)), + ), + ), + ), + ), + ); + } +} + +class MemoryItem { + final IconData icon; + final String title; + final String subtitle; + + MemoryItem({required this.icon, required this.title, required this.subtitle}); +} diff --git a/apps/lib/features/settings/ui/screens/settings_screen.dart b/apps/lib/features/settings/ui/screens/settings_screen.dart new file mode 100644 index 0000000..9d7c2b7 --- /dev/null +++ b/apps/lib/features/settings/ui/screens/settings_screen.dart @@ -0,0 +1,398 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import '../../../../core/theme/design_tokens.dart'; +import '../../../../shared/widgets/page_header.dart' as widgets; + +class SettingsScreen extends StatelessWidget { + const SettingsScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: const Color(0xFFF8FAFC), + body: SafeArea( + child: Column( + children: [ + const widgets.PageHeader(leading: widgets.BackButton()), + Expanded( + child: SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(20, 8, 20, 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildProfileHero(), + const SizedBox(height: 16), + _buildQuickActions(context), + const SizedBox(height: 16), + _buildSubscriptionCard(), + const SizedBox(height: 16), + _buildMenuCard(context), + ], + ), + ), + ), + ], + ), + ), + ); + } + + Widget _buildProfileHero() { + return Container( + width: double.infinity, + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + gradient: const LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + stops: [0, 1], + colors: [Color(0xFFFFFFFF), Color(0xFFF3F7FF)], + transform: GradientRotation(35 * 3.14159 / 180), + ), + borderRadius: BorderRadius.circular(22), + border: Border.all(color: const Color(0xFFE5ECF8)), + boxShadow: const [ + BoxShadow( + color: Color(0x1A0F172A), + blurRadius: 14, + offset: Offset(0, 4), + ), + ], + ), + child: Row( + children: [ + Container( + width: 72, + height: 72, + decoration: BoxDecoration( + gradient: const LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [Color(0xFFEAF1FF), Color(0xFFF8FBFF)], + ), + borderRadius: BorderRadius.circular(36), + border: Border.all(color: const Color(0xFFD9E5FA)), + ), + child: const Icon(Icons.person, size: 30, color: AppColors.blue500), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'Qiuzhiliang', + style: TextStyle( + fontSize: 17, + fontWeight: FontWeight.w600, + color: AppColors.slate900, + ), + ), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 4, + ), + decoration: BoxDecoration( + color: const Color(0xFFF8FAFF), + borderRadius: BorderRadius.circular(10), + border: Border.all(color: const Color(0xFFDEE7F6)), + ), + child: const Text( + 'Free', + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w500, + color: AppColors.slate500, + ), + ), + ), + ], + ), + const SizedBox(height: 4), + const Text( + 'qiuzhiliang@xunmee.com', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + color: AppColors.slate500, + ), + ), + ], + ), + ), + ], + ), + ); + } + + Widget _buildQuickActions(BuildContext context) { + return Container( + height: 120, + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: AppColors.white, + borderRadius: BorderRadius.circular(18), + border: Border.all(color: const Color(0xFFE7EDF6)), + ), + child: Row( + children: [ + Expanded( + child: _buildQuickActionCard( + icon: Icons.people, + iconColor: AppColors.blue600, + iconBg: const Color(0xFFF8FAFF), + iconBorder: const Color(0xFFE6ECF7), + title: '联系人', + subtitle: '已添加 1 位:Toki', + onTap: () => context.push('/contacts'), + ), + ), + const SizedBox(width: 10), + Expanded( + child: _buildQuickActionCard( + icon: Icons.auto_awesome, + iconColor: const Color(0xFF0EA5A4), + iconBg: const Color(0xFFF7FAFF), + iconBorder: const Color(0xFFE6ECF7), + title: '常用功能', + subtitle: '已启用:会议提醒', + onTap: () => context.push('/settings/features'), + ), + ), + ], + ), + ); + } + + Widget _buildQuickActionCard({ + required IconData icon, + required Color iconColor, + required Color iconBg, + required Color iconBorder, + required String title, + required String subtitle, + required VoidCallback onTap, + }) { + return GestureDetector( + onTap: onTap, + child: Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: iconBg, + borderRadius: BorderRadius.circular(14), + border: Border.all(color: iconBorder), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Icon(icon, size: 18, color: iconColor), + const SizedBox(width: 8), + Text( + title, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: Color(0xFF1E293B), + ), + ), + ], + ), + const Icon( + Icons.chevron_right, + size: 16, + color: AppColors.slate400, + ), + ], + ), + Text( + subtitle, + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w500, + color: AppColors.slate500, + ), + ), + ], + ), + ), + ); + } + + Widget _buildSubscriptionCard() { + return Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: AppColors.white, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: const Color(0xFFE3EAF6)), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + '套餐等级 Free', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: AppColors.slate900, + ), + ), + const SizedBox(height: 8), + const Text( + '已用积分 320 / 1000', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + color: AppColors.slate500, + ), + ), + const SizedBox(height: 8), + ClipRRect( + borderRadius: BorderRadius.circular(999), + child: const LinearProgressIndicator( + value: 0.32, + backgroundColor: Color(0xFFE8EEF8), + valueColor: AlwaysStoppedAnimation(AppColors.blue400), + minHeight: 8, + ), + ), + ], + ), + ), + const SizedBox(width: 12), + GestureDetector( + onTap: () {}, + child: Container( + width: 72, + height: 32, + decoration: BoxDecoration( + color: const Color(0xFFEAF3FF), + borderRadius: BorderRadius.circular(16), + border: Border.all(color: const Color(0xFFCFE1FB)), + ), + child: const Center( + child: Text( + '升级', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: AppColors.blue600, + ), + ), + ), + ), + ), + ], + ), + ); + } + + Widget _buildMenuCard(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: AppColors.white, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: const Color(0xFFE2E8F0)), + ), + child: Column( + children: [ + _buildMenuItem( + icon: Icons.calendar_today, + title: '日历', + trailing: 'Toki', + onTap: () {}, + ), + _buildDivider(), + _buildMenuItem( + icon: Icons.notifications, + title: '日程通知', + onTap: () {}, + ), + _buildDivider(), + _buildMenuItem( + icon: Icons.bookmark, + title: '我的记忆', + onTap: () => context.push('/settings/memory'), + ), + _buildDivider(), + _buildMenuItem(icon: Icons.person, title: '我的账户', onTap: () {}), + ], + ), + ); + } + + Widget _buildMenuItem({ + required IconData icon, + required String title, + String? trailing, + required VoidCallback onTap, + }) { + return GestureDetector( + onTap: onTap, + behavior: HitTestBehavior.opaque, + child: Container( + height: 56, + padding: const EdgeInsets.symmetric(horizontal: 14), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Icon(icon, size: 20, color: AppColors.slate500), + const SizedBox(width: 10), + Text( + title, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: AppColors.slate900, + ), + ), + ], + ), + Row( + children: [ + if (trailing != null) ...[ + Text( + trailing, + style: const TextStyle( + fontSize: 14, + color: AppColors.slate400, + ), + ), + const SizedBox(width: 6), + ], + const Icon( + Icons.chevron_right, + size: 18, + color: AppColors.slate400, + ), + ], + ), + ], + ), + ), + ); + } + + Widget _buildDivider() { + return Container( + height: 1, + margin: const EdgeInsets.symmetric(horizontal: 14), + color: const Color(0xFFEEF2F7), + ); + } +}