From d6ac0ed1e435b8ab71995c48aeecc544cb557f36 Mon Sep 17 00:00:00 2001 From: Developer Date: Tue, 31 Mar 2026 05:42:47 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + lib/views/favorites_page.dart | 2 +- lib/views/home/home_components.dart | 2 +- lib/views/home/home_page.dart | 41 +++ lib/views/home/home_part.dart | 1 - lib/views/main_navigation.dart | 13 +- lib/views/profile/components/pop-menu.dart | 122 +++++++ lib/views/profile/guide/tongji.dart | 243 +++++++++++++ lib/views/profile/level/poetry.dart | 8 + lib/views/profile/per_card.dart | 68 +++- lib/views/profile/profile_page.dart | 344 ++++++++++-------- linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 58 ++- pubspec.yaml | 3 +- .../flutter/generated_plugin_registrant.cc | 6 + windows/flutter/generated_plugins.cmake | 2 + 18 files changed, 751 insertions(+), 170 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e89517..98c27ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -131,3 +131,4 @@ All notable changes to this project will be documented in this file. ### 开发进度 - 🏗️ **HarmonyOS桌面小组件** - 开发中,包含2x2布局、天气显示、诗句展示等功能 - 优先级:3 +getx 进入 \ No newline at end of file diff --git a/lib/views/favorites_page.dart b/lib/views/favorites_page.dart index 34df562..5434141 100644 --- a/lib/views/favorites_page.dart +++ b/lib/views/favorites_page.dart @@ -24,7 +24,7 @@ class FavoritesPage extends StatefulWidget { class _FavoritesPageState extends State with SingleTickerProviderStateMixin { late TabController _tabController; - final List _categories = ['全部', '点赞', '笔记', '推送', '关注进度']; + final List _categories = ['全部', '点赞', '笔记', '推送', '每日一句']; final TextEditingController _searchBarController = TextEditingController(); bool _isGridView = true; diff --git a/lib/views/home/home_components.dart b/lib/views/home/home_components.dart index 0d100ee..feacbf4 100644 --- a/lib/views/home/home_components.dart +++ b/lib/views/home/home_components.dart @@ -230,7 +230,7 @@ class CopyUtils { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '受隐私权限管理,写入剪切板需告知用户', + '受隐私权限约束,频繁写入剪切板需告知用户', style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500), ), const SizedBox(height: 12), diff --git a/lib/views/home/home_page.dart b/lib/views/home/home_page.dart index acbb42b..57625b2 100644 --- a/lib/views/home/home_page.dart +++ b/lib/views/home/home_page.dart @@ -14,6 +14,7 @@ import '../../../utils/audio_manager.dart'; import 'home_part.dart'; import 'home_components.dart'; import 'home-load.dart'; +import '../profile/guide/tongji.dart'; class HomePage extends StatefulWidget { const HomePage({super.key}); @@ -123,6 +124,15 @@ class _HomePageState extends State final response = await PoetryApi.getRandomPoetry(); if (mounted && response.data != null) { + // 记录浏览统计 + try { + await StatisticsManager().recordView(); + await StatisticsManager().recordFirstUse(); + await StatisticsManager().recordTotalView(); + } catch (e) { + // 忽略错误 + } + setState(() { _poetryData = response.data; _keywordList = PoetryDataUtils.extractKeywords(response.data); @@ -160,6 +170,15 @@ class _HomePageState extends State final poetryData = await offlineDataManager.getNextPoetry(); if (mounted && poetryData != null) { + // 记录浏览统计 + try { + await StatisticsManager().recordView(); + await StatisticsManager().recordFirstUse(); + await StatisticsManager().recordTotalView(); + } catch (e) { + // 忽略错误 + } + setState(() { _poetryData = poetryData; _keywordList = PoetryDataUtils.extractKeywords(poetryData); @@ -243,6 +262,15 @@ class _HomePageState extends State final response = await PoetryApi.getPoetryById(poetryId); if (mounted && response.data != null) { + // 记录浏览统计 + try { + await StatisticsManager().recordView(); + await StatisticsManager().recordFirstUse(); + await StatisticsManager().recordTotalView(); + } catch (e) { + // 忽略错误 + } + setState(() { _poetryData = response.data; _keywordList = PoetryDataUtils.extractKeywords(response.data); @@ -339,6 +367,10 @@ class _HomePageState extends State if (_isLiked) { // 添加到点赞列表 await HistoryController.addToLiked(_poetryData!.toJson()); + // 记录今日点赞 + await StatisticsManager().recordTodayLike(); + // 记录累计点赞 + await StatisticsManager().recordTotalLike(); } else { // 从点赞列表移除 await HistoryController.removeLikedPoetry(_poetryData!.id.toString()); @@ -529,6 +561,15 @@ class _HomePageState extends State // 模拟分步加载过程 Future _simulateSectionLoading(PoetryData newPoetryData) async { + // 记录浏览统计 + try { + await StatisticsManager().recordView(); + await StatisticsManager().recordFirstUse(); + await StatisticsManager().recordTotalView(); + } catch (e) { + // 忽略错误 + } + // 1. 加载标题区域 setState(() { _sectionLoadingStates['title'] = false; diff --git a/lib/views/home/home_part.dart b/lib/views/home/home_part.dart index b70d307..bd29f8d 100644 --- a/lib/views/home/home_part.dart +++ b/lib/views/home/home_part.dart @@ -8,7 +8,6 @@ import 'package:flutter/services.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../../constants/app_constants.dart'; import '../../../utils/http/poetry_api.dart'; -import '../../../utils/flutter_compatibility_fix.dart'; import '../../../utils/audio_manager.dart'; import 'home_components.dart'; diff --git a/lib/views/main_navigation.dart b/lib/views/main_navigation.dart index aced8cb..60e6813 100644 --- a/lib/views/main_navigation.dart +++ b/lib/views/main_navigation.dart @@ -19,12 +19,14 @@ class MainNavigation extends StatefulWidget { class _MainNavigationState extends State { int _currentIndex = 0; + final GlobalKey> _profileKey = + GlobalKey>(); - final List _pages = [ + late final List _pages = [ const HomePage(), const DiscoverPage(), const FavoritesPage(), - const ProfilePage(), + ProfilePage(key: _profileKey), ]; final List _bottomNavItems = [ @@ -73,6 +75,13 @@ class _MainNavigationState extends State { setState(() { _currentIndex = index; }); + // 切换到个人页面时刷新数据 + if (index == 3) { + final profileState = _profileKey.currentState; + if (profileState != null && profileState.mounted) { + (profileState as dynamic).refreshData(); + } + } }, type: BottomNavigationBarType.fixed, selectedItemColor: AppConstants.primaryColor, diff --git a/lib/views/profile/components/pop-menu.dart b/lib/views/profile/components/pop-menu.dart index e69de29..13f0d93 100644 --- a/lib/views/profile/components/pop-menu.dart +++ b/lib/views/profile/components/pop-menu.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:share_plus/share_plus.dart'; +import '../../../constants/app_constants.dart'; + +class PopMenu extends StatelessWidget { + final VoidCallback? onRefresh; + final VoidCallback? onEdit; + final VoidCallback? onScanQr; + final VoidCallback? onDarkMode; + final VoidCallback? onSettings; + + const PopMenu({ + super.key, + this.onRefresh, + this.onEdit, + this.onScanQr, + this.onDarkMode, + this.onSettings, + }); + + static Future shareApp(BuildContext context) async { + try { + const String shareText = '诗词学习App - 一款优雅的诗词学习应用,包含丰富的诗词内容和答题功能'; + + final result = await Share.shareWithResult(shareText, subject: '诗词学习App'); + + if (result.status == ShareResultStatus.success) { + if (context.mounted) { + ScaffoldMessenger.of( + context, + ).showSnackBar(const SnackBar(content: Text('分享成功!'))); + } + } + } catch (e) { + if (context.mounted) { + ScaffoldMessenger.of( + context, + ).showSnackBar(const SnackBar(content: Text('该平台暂不支持分享功能'))); + } + } + } + + static void show( + BuildContext context, { + VoidCallback? onRefresh, + VoidCallback? onEdit, + VoidCallback? onScanQr, + VoidCallback? onDarkMode, + VoidCallback? onSettings, + }) { + showModalBottomSheet( + context: context, + backgroundColor: Colors.transparent, + builder: (context) => PopMenu( + onRefresh: onRefresh, + onEdit: onEdit, + onScanQr: onScanQr, + onDarkMode: onDarkMode, + onSettings: onSettings, + ), + ); + } + + @override + Widget build(BuildContext context) { + return Container( + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 顶部拖拽条 + Container( + width: 40, + height: 4, + margin: const EdgeInsets.only(top: 8), + decoration: BoxDecoration( + color: Colors.grey[300]!, + borderRadius: BorderRadius.circular(2), + ), + ), + // 选项列表 + _buildBottomSheetItem(context, '刷新数据', Icons.refresh, onRefresh), + _buildBottomSheetItem(context, '分享软件', Icons.share, () { + shareApp(context); + }), + _buildBottomSheetItem( + context, + '扫描二维码', + Icons.qr_code_scanner, + onScanQr, + ), + _buildBottomSheetItem(context, '夜间模式', Icons.dark_mode, onDarkMode), + const SizedBox(height: 20), + _buildBottomSheetItem(context, '设置', Icons.settings, onSettings), + ], + ), + ); + } + + Widget _buildBottomSheetItem( + BuildContext context, + String title, + IconData icon, + VoidCallback? onTap, + ) { + return ListTile( + leading: Icon(icon, color: AppConstants.primaryColor), + title: Text(title), + onTap: () { + Navigator.pop(context); + HapticFeedback.lightImpact(); + if (onTap != null) { + onTap(); + } + }, + ); + } +} diff --git a/lib/views/profile/guide/tongji.dart b/lib/views/profile/guide/tongji.dart index e69de29..e5f5456 100644 --- a/lib/views/profile/guide/tongji.dart +++ b/lib/views/profile/guide/tongji.dart @@ -0,0 +1,243 @@ +/// 时间: 2026-03-31 +/// 功能: 统计数据管理 +/// 介绍: 管理应用的各种统计数据,包括浏览次数、答题次数、点赞数等 +/// 最新变化: 新建文件 + +import 'package:shared_preferences/shared_preferences.dart'; + +class StatisticsManager { + static const String _todayViewsKey = 'today_views'; + static const String _weekViewsKey = 'week_views'; + static const String _firstUseTimeKey = 'first_use_time'; + static const String _todayLikesKey = 'today_likes'; + static const String _todayQuestionsKey = 'today_questions'; + static const String _lastViewDateKey = 'last_view_date'; + static const String _lastWeekKey = 'last_week_start'; + static const String _lastQuestionDateKey = 'last_question_date'; + + static StatisticsManager? _instance; + + StatisticsManager._internal(); + + factory StatisticsManager() { + _instance ??= StatisticsManager._internal(); + return _instance!; + } + + /// 记录一次浏览 + Future recordView() async { + final prefs = await SharedPreferences.getInstance(); + final now = DateTime.now(); + final today = _formatDate(now); + final weekStart = _getWeekStart(now); + + final lastViewDate = prefs.getString(_lastViewDateKey); + final lastWeekStart = prefs.getString(_lastWeekKey); + + if (lastViewDate != today) { + await prefs.setInt(_todayViewsKey, 0); + await prefs.setString(_lastViewDateKey, today); + } + + if (lastWeekStart != weekStart) { + await prefs.setInt(_weekViewsKey, 0); + await prefs.setString(_lastWeekKey, weekStart); + } + + final todayViews = prefs.getInt(_todayViewsKey) ?? 0; + final weekViews = prefs.getInt(_weekViewsKey) ?? 0; + + await prefs.setInt(_todayViewsKey, todayViews + 1); + await prefs.setInt(_weekViewsKey, weekViews + 1); + } + + /// 获取今日浏览次数 + Future getTodayViews() async { + final prefs = await SharedPreferences.getInstance(); + final today = _formatDate(DateTime.now()); + final lastViewDate = prefs.getString(_lastViewDateKey); + + if (lastViewDate != today) { + return 0; + } + + return prefs.getInt(_todayViewsKey) ?? 0; + } + + /// 获取本周浏览次数 + Future getWeekViews() async { + final prefs = await SharedPreferences.getInstance(); + final now = DateTime.now(); + final weekStart = _getWeekStart(now); + final lastWeekStart = prefs.getString(_lastWeekKey); + + if (lastWeekStart != weekStart) { + return 0; + } + + return prefs.getInt(_weekViewsKey) ?? 0; + } + + /// 记录首次使用时间 + Future recordFirstUse() async { + final prefs = await SharedPreferences.getInstance(); + if (!prefs.containsKey(_firstUseTimeKey)) { + await prefs.setString(_firstUseTimeKey, DateTime.now().toIso8601String()); + } + } + + /// 获取首次使用时间 + Future getFirstUseTime() async { + final prefs = await SharedPreferences.getInstance(); + final timeStr = prefs.getString(_firstUseTimeKey); + if (timeStr == null) { + return '未记录'; + } + final date = DateTime.parse(timeStr); + return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}'; + } + + /// 记录今日点赞 + Future recordTodayLike() async { + final prefs = await SharedPreferences.getInstance(); + final now = DateTime.now(); + final today = _formatDate(now); + final lastLikeDate = prefs.getString('last_like_date'); + + if (lastLikeDate != today) { + await prefs.setInt(_todayLikesKey, 0); + await prefs.setString('last_like_date', today); + } + + final todayLikes = prefs.getInt(_todayLikesKey) ?? 0; + await prefs.setInt(_todayLikesKey, todayLikes + 1); + } + + /// 获取今日点赞数 + Future getTodayLikes() async { + final prefs = await SharedPreferences.getInstance(); + final today = _formatDate(DateTime.now()); + final lastLikeDate = prefs.getString('last_like_date'); + + if (lastLikeDate != today) { + return 0; + } + + return prefs.getInt(_todayLikesKey) ?? 0; + } + + /// 获取数据占用空间 + Future getDataSize() async { + try { + final prefs = await SharedPreferences.getInstance(); + final keys = prefs.getKeys(); + int totalBytes = 0; + + for (final key in keys) { + final value = prefs.get(key); + if (value != null) { + totalBytes += value.toString().length * 2; + } + } + + if (totalBytes < 1024) { + return '$totalBytes B'; + } else if (totalBytes < 1024 * 1024) { + return '${(totalBytes / 1024).toStringAsFixed(1)} KB'; + } else { + return '${(totalBytes / (1024 * 1024)).toStringAsFixed(1)} MB'; + } + } catch (e) { + return '计算失败'; + } + } + + /// 获取累计浏览数 + Future getTotalViews() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getInt('total_views') ?? 0; + } + + /// 记录累计浏览 + Future recordTotalView() async { + final prefs = await SharedPreferences.getInstance(); + final totalViews = prefs.getInt('total_views') ?? 0; + await prefs.setInt('total_views', totalViews + 1); + } + + /// 获取累计点赞数 + Future getTotalLikes() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getInt('total_likes') ?? 0; + } + + /// 记录累计点赞 + Future recordTotalLike() async { + final prefs = await SharedPreferences.getInstance(); + final totalLikes = prefs.getInt('total_likes') ?? 0; + await prefs.setInt('total_likes', totalLikes + 1); + } + + /// 获取累计答题数 + Future getTotalQuestions() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getInt('totalQuestions') ?? 0; + } + + /// 记录今日答题 + Future recordTodayQuestion() async { + final prefs = await SharedPreferences.getInstance(); + final now = DateTime.now(); + final today = _formatDate(now); + final lastQuestionDate = prefs.getString(_lastQuestionDateKey); + + if (lastQuestionDate != today) { + await prefs.setInt(_todayQuestionsKey, 0); + await prefs.setString(_lastQuestionDateKey, today); + } + + final todayQuestions = prefs.getInt(_todayQuestionsKey) ?? 0; + await prefs.setInt(_todayQuestionsKey, todayQuestions + 1); + } + + /// 获取今日答题数 + Future getTodayQuestions() async { + final prefs = await SharedPreferences.getInstance(); + final today = _formatDate(DateTime.now()); + final lastQuestionDate = prefs.getString(_lastQuestionDateKey); + + if (lastQuestionDate != today) { + return 0; + } + + return prefs.getInt(_todayQuestionsKey) ?? 0; + } + + /// 获取使用天数 + Future getUseDays() async { + final prefs = await SharedPreferences.getInstance(); + final timeStr = prefs.getString(_firstUseTimeKey); + if (timeStr == null) { + return 1; + } + try { + final firstUseDate = DateTime.parse(timeStr); + final today = DateTime.now(); + final difference = today.difference(firstUseDate); + return difference.inDays + 1; + } catch (e) { + return 1; + } + } + + /// 格式化日期为 YYYY-MM-DD + String _formatDate(DateTime date) { + return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}'; + } + + /// 获取本周开始日期(周一) + String _getWeekStart(DateTime date) { + final monday = date.subtract(Duration(days: date.weekday - 1)); + return _formatDate(monday); + } +} diff --git a/lib/views/profile/level/poetry.dart b/lib/views/profile/level/poetry.dart index e629717..c247b4f 100644 --- a/lib/views/profile/level/poetry.dart +++ b/lib/views/profile/level/poetry.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import '../../../constants/app_constants.dart'; import '../../../controllers/shared_preferences_storage_controller.dart'; +import '../guide/tongji.dart'; import 'level-jilu.dart'; import 'flow-anim.dart'; import 'distinguish.dart'; @@ -231,6 +232,13 @@ class _PoetryLevelPageState extends State if (!mounted) return; + // 记录今日答题 + try { + await StatisticsManager().recordTodayQuestion(); + } catch (e) { + // 忽略错误 + } + setState(() { if (result.success) { _isAnswerCorrect = result.isCorrect; diff --git a/lib/views/profile/per_card.dart b/lib/views/profile/per_card.dart index 07dd325..7ae6dd5 100644 --- a/lib/views/profile/per_card.dart +++ b/lib/views/profile/per_card.dart @@ -5,6 +5,7 @@ import 'package:flutter/services.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../constants/app_constants.dart'; import '../../services/network_listener_service.dart'; +import 'guide/tongji.dart'; /// 时间: 2026-03-25 /// 功能: 个人信息卡片组件 @@ -28,20 +29,42 @@ class PersonalCard extends StatefulWidget { }); @override - State createState() => _PersonalCardState(); + State createState() => PersonalCardState(); } -class _PersonalCardState extends State { +class PersonalCardState extends State { late bool _isExpanded; late String _currentTip; bool _isOnline = true; + // 累计数据 + int _totalViews = 0; + int _totalLikes = 0; + int _totalQuestions = 0; + @override void initState() { super.initState(); _isExpanded = widget.isExpanded ?? false; _currentTip = _getRandomTip(); _loadOnlineStatus(); + _loadTotalStats(); + } + + Future _loadTotalStats() async { + final views = await StatisticsManager().getTotalViews(); + final likes = await StatisticsManager().getTotalLikes(); + final questions = await StatisticsManager().getTotalQuestions(); + setState(() { + _totalViews = views; + _totalLikes = likes; + _totalQuestions = questions; + }); + } + + // 刷新数据(公共方法,供外部调用) + Future refreshData() async { + await _loadTotalStats(); } Future _loadOnlineStatus() async { @@ -378,15 +401,38 @@ class _PersonalCardState extends State { overflow: TextOverflow.ellipsis, ), const SizedBox(height: 10), - // 统计信息 - Row( - children: [ - _buildStatItem('收藏', widget.userData['favorites'] ?? 0), - const SizedBox(width: 12), - _buildStatItem('点赞', widget.userData['likes'] ?? 0), - const SizedBox(width: 12), - _buildStatItem('浏览', widget.userData['views'] ?? 0), - ], + // 累计统计卡片 + Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 10, + ), + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(12), + ), + child: Column( + children: [ + Text( + '累计', + style: const TextStyle( + color: Colors.white70, + fontSize: 11, + ), + ), + const SizedBox(height: 8), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + _buildStatItem('浏览', _totalViews), + const SizedBox(width: 16), + _buildStatItem('点赞', _totalLikes), + const SizedBox(width: 16), + _buildStatItem('答题', _totalQuestions), + ], + ), + ], + ), ), ], ), diff --git a/lib/views/profile/profile_page.dart b/lib/views/profile/profile_page.dart index bbb1c44..17de39d 100644 --- a/lib/views/profile/profile_page.dart +++ b/lib/views/profile/profile_page.dart @@ -22,10 +22,12 @@ import 'app-info.dart'; import 'level/poetry.dart'; import 'guide/permission.dart'; import 'guide/app-data.dart'; +import 'guide/tongji.dart'; import 'theme/app-diy.dart'; import 'expand/vote.dart'; import 'expand/manu-script.dart'; import 'components/bug_list_page.dart'; +import 'components/pop-menu.dart'; class ProfilePage extends StatefulWidget { const ProfilePage({super.key}); @@ -35,20 +37,35 @@ class ProfilePage extends StatefulWidget { } class _ProfilePageState extends State - with TickerProviderStateMixin { + with TickerProviderStateMixin, WidgetsBindingObserver { late PageController _pageController; late TabController _tabController; int _currentPage = 1; // 默认显示第2页(设置) bool _isCardExpanded = false; // 个人卡片展开状态 + bool _isStatsHidden = false; // 统计数据隐藏状态 double _startY = 0.0; // 历史记录相关 List> _poetryHistory = []; + // PersonalCard 的 Key,用于调用其刷新方法 + final GlobalKey _personalCardKey = + GlobalKey(); + // 答题统计数据 int _correctAnswers = 0; - int _todayQuestions = 0; int _weekQuestions = 0; + // 统计数据 + int _todayViews = 0; + int _weekViews = 0; + String _firstUseTime = '未记录'; + int _useDays = 1; + String _dataSize = '0 B'; + int _noteCount = 0; + int _totalQuestions = 0; + int _todayQuestions = 0; + int _todayLikes = 0; + // 模拟用户数据 final Map _userData = { 'avatar': '👤', // 使用emoji代替网络图片 @@ -67,19 +84,41 @@ class _ProfilePageState extends State @override void initState() { super.initState(); + WidgetsBinding.instance.addObserver(this); _pageController = PageController(initialPage: 1); _tabController = TabController(length: 3, vsync: this); _loadPoetryHistory(); _loadPoetryStatistics(); + _loadStatistics(); } @override void dispose() { + WidgetsBinding.instance.removeObserver(this); _pageController.dispose(); _tabController.dispose(); super.dispose(); } + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.resumed) { + // 应用恢复时刷新数据 + refreshData(); + } + } + + // 刷新所有数据(公共方法,供外部调用) + Future refreshData() async { + await _loadPoetryStatistics(); + await _loadStatistics(); + // 同时刷新个人卡片的数据 + final personalCardState = _personalCardKey.currentState; + if (personalCardState != null && personalCardState.mounted) { + await personalCardState.refreshData(); + } + } + void _onPageChanged(int page) { setState(() { _currentPage = page; @@ -146,6 +185,33 @@ class _ProfilePageState extends State } } + // === 加载统计数据 === + Future _loadStatistics() async { + try { + // 加载浏览统计 + _todayViews = await StatisticsManager().getTodayViews(); + _weekViews = await StatisticsManager().getWeekViews(); + _firstUseTime = await StatisticsManager().getFirstUseTime(); + _useDays = await StatisticsManager().getUseDays(); + _dataSize = await StatisticsManager().getDataSize(); + _todayLikes = await StatisticsManager().getTodayLikes(); + _todayQuestions = await StatisticsManager().getTodayQuestions(); + + // 加载笔记总数 + _noteCount = await HistoryController.getNotesCount(); + + // 加载累计答题数 + _totalQuestions = await SharedPreferencesStorageController.getInt( + 'totalQuestions', + defaultValue: 0, + ); + + setState(() {}); + } catch (e) { + // 加载失败 + } + } + @override Widget build(BuildContext context) { return Scaffold( @@ -222,6 +288,7 @@ class _ProfilePageState extends State Widget _buildProfileHeader() { // === 个人信息头部:可收起/张开的个人卡片 === return PersonalCard( + key: _personalCardKey, userData: _userData, isExpanded: _isCardExpanded, onExpandChanged: (value) { @@ -277,97 +344,130 @@ class _ProfilePageState extends State ), const SizedBox(width: 8), Text( - '统计*隐藏', + '统计', style: TextStyle( color: AppConstants.primaryColor, fontSize: 16, fontWeight: FontWeight.bold, ), ), + const Spacer(), + IconButton( + icon: Icon( + _isStatsHidden + ? Icons.visibility_off_outlined + : Icons.visibility_outlined, + color: Colors.grey[600], + size: 20, + ), + onPressed: () { + setState(() { + _isStatsHidden = !_isStatsHidden; + }); + HapticFeedback.lightImpact(); + }, + tooltip: _isStatsHidden ? '显示数据' : '隐藏数据', + ), ], ), const SizedBox(height: 16), // === 信息列表 === - _buildInfoItem('用户ID', '123123'), - _buildInfoItem('注册时间', '2023-03-21'), - _buildInfoItem('会员等级', 'VIP会员'), - _buildInfoItem('积分余额', '2,580'), - _buildInfoItem('创作诗词', '156首'), - _buildInfoItem('获赞总数', '2,560'), + if (!_isStatsHidden) ...[ + _buildInfoItem('今日浏览', '$_todayViews 条'), + _buildInfoItem('本周浏览', '$_weekViews 条'), + _buildInfoItem('天数', '$_useDays 天'), + _buildInfoItem('数据', _dataSize), + _buildInfoItem('笔记', '$_noteCount 个'), + _buildInfoItem('今日答题', '$_todayQuestions 题'), + _buildInfoItem('今日点赞', '$_todayLikes 个'), + ] else + Center( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Text( + '数据已隐藏', + style: TextStyle(color: Colors.grey[500], fontSize: 14), + ), + ), + ), ], ), ), const SizedBox(height: 16), // === 互动统计卡片 === - Container( - width: double.infinity, - padding: const EdgeInsets.all(20), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(16), - boxShadow: [ - BoxShadow( - color: Colors.black.withValues(alpha: 0.08), - blurRadius: 10, - offset: const Offset(0, 2), - ), - ], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon( - Icons.analytics_outlined, - color: AppConstants.primaryColor, - size: 20, - ), - const SizedBox(width: 8), - Text( - '诗词挑战', - style: TextStyle( - color: AppConstants.primaryColor, - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - const SizedBox(height: 16), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - _buildInteractionItem('今日答题', '$_todayQuestions'), - _buildInteractionItem('本周答题', '$_weekQuestions'), - _buildInteractionItem('答对次数', '$_correctAnswers'), - ], - ), - const SizedBox(height: 16), - SizedBox( - width: double.infinity, - child: ElevatedButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => const PoetryLevelPage(), - ), - ); - }, - style: ElevatedButton.styleFrom( - backgroundColor: AppConstants.primaryColor, - padding: const EdgeInsets.symmetric(vertical: 12), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - child: const Text('开始诗词答题', style: TextStyle(fontSize: 16)), + if (!_isStatsHidden) + Container( + width: double.infinity, + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.08), + blurRadius: 10, + offset: const Offset(0, 2), ), - ), - ], + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.analytics_outlined, + color: AppConstants.primaryColor, + size: 20, + ), + const SizedBox(width: 8), + Text( + '诗词挑战', + style: TextStyle( + color: AppConstants.primaryColor, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + _buildInteractionItem('今日答题', '$_todayQuestions'), + _buildInteractionItem('本周答题', '$_weekQuestions'), + _buildInteractionItem('答对次数', '$_correctAnswers'), + ], + ), + const SizedBox(height: 16), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const PoetryLevelPage(), + ), + ); + }, + style: ElevatedButton.styleFrom( + backgroundColor: AppConstants.primaryColor, + padding: const EdgeInsets.symmetric(vertical: 12), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + '开始诗词答题', + style: TextStyle(fontSize: 16), + ), + ), + ), + ], + ), ), - ), ], ), ); @@ -577,7 +677,7 @@ class _ProfilePageState extends State mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - '标签', + label, style: const TextStyle( fontSize: 14, color: Color(0xFF9E9E9E), // 使用具体颜色值代替Colors.grey[600] @@ -768,80 +868,22 @@ class _ProfilePageState extends State } void _showMoreOptions() { - showModalBottomSheet( - context: context, - backgroundColor: Colors.transparent, - builder: (context) => Container( - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.vertical(top: Radius.circular(20)), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // 顶部拖拽条 - Container( - width: 40, - height: 4, - margin: const EdgeInsets.only(top: 8), - decoration: BoxDecoration( - color: Colors.grey[300]!, - borderRadius: BorderRadius.circular(2), - ), - ), - // 选项列表 - _buildBottomSheetItem( - '分享主页', - Icons.share, - () => _showSnackBar('分享主页'), - ), - _buildBottomSheetItem( - '编辑资料', - Icons.edit, - () => _showSnackBar('编辑资料'), - ), - _buildBottomSheetItem( - '扫描二维码', - Icons.qr_code_scanner, - () => _showSnackBar('扫描二维码'), - ), - _buildBottomSheetItem( - '夜间模式', - Icons.dark_mode, - () => _showSnackBar('夜间模式'), - ), - const SizedBox(height: 20), - _buildBottomSheetItem('设置', Icons.settings, () { - // 延迟执行,确保底部弹窗完全关闭 - Future.delayed(const Duration(milliseconds: 100), () { - if (mounted && _pageController.hasClients) { - _pageController.animateToPage( - 1, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - } - }); - }), - ], - ), - ), - ); - } - - Widget _buildBottomSheetItem( - String title, - IconData icon, - VoidCallback onTap, - ) { - // === 底部弹窗选项项 === - return ListTile( - leading: Icon(icon, color: AppConstants.primaryColor), - title: Text(title), - onTap: () { - Navigator.pop(context); - HapticFeedback.lightImpact(); - onTap(); + PopMenu.show( + context, + onRefresh: () => refreshData(), + onEdit: () => _showSnackBar('编辑资料'), + onScanQr: () => _showSnackBar('扫描二维码'), + onDarkMode: () => _showSnackBar('夜间模式'), + onSettings: () { + Future.delayed(const Duration(milliseconds: 100), () { + if (mounted && _pageController.hasClients) { + _pageController.animateToPage( + 1, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + } + }); }, ); } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 1830e5c..cc10c4d 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,9 +7,13 @@ #include "generated_plugin_registrant.h" #include +#include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin"); audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar); + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index e9abb91..8e2a190 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_linux + url_launcher_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 95e82b8..8baf9d9 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,11 +8,13 @@ import Foundation import audioplayers_darwin import device_info_plus import path_provider_foundation +import share_plus import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 6b2630d..59b284f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -121,6 +121,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "4.0.9" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.3.5+2" crypto: dependency: transitive description: @@ -316,10 +324,10 @@ packages: dependency: transitive description: name: mime - sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.0" + version: "1.0.6" path: dependency: transitive description: @@ -410,6 +418,20 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.1.8" + share_plus: + dependency: "direct main" + description: + path: "packages/flutter_plus_plugins/packages/share_plus/share_plus" + relative: true + source: path + version: "7.2.0" + share_plus_platform_interface: + dependency: transitive + description: + path: "packages/flutter_plus_plugins/packages/share_plus/share_plus_platform_interface" + relative: true + source: path + version: "3.3.0" shared_preferences: dependency: "direct main" description: @@ -553,6 +575,38 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.3.1" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.2.2" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.5" uuid: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 852c831..c1d79d4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,7 +47,8 @@ dependencies: url: https://gitcode.com/openharmony-sig/flutter_audioplayers.git path: packages/audioplayers - + share_plus: + path: packages/flutter_plus_plugins/packages/share_plus/share_plus dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 09e8e2c..ae488a2 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,8 +7,14 @@ #include "generated_plugin_registrant.h" #include +#include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { AudioplayersWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); + SharePlusWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 375535c..ed0a61c 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,8 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_windows + share_plus + url_launcher_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST