From 8f0f641d37da2b52438b0db82b94dc5742b0bf10 Mon Sep 17 00:00:00 2001 From: Cxx0822 <1556464090@qq.com> Date: Thu, 20 Nov 2025 13:37:56 +0800 Subject: [PATCH] =?UTF-8?q?factor:=E5=B8=83=E5=B1=80=E9=A2=9C=E8=89=B2?= =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 2 +- lib/pages/blog_page.dart | 5 +- lib/pages/category_list_page.dart | 5 +- lib/pages/category_page.dart | 5 +- lib/pages/stats_page.dart | 53 ++++++------ lib/pages/timeline_page.dart | 11 ++- lib/utils/index.dart | 25 ++++++ lib/widget/blog.dart | 136 +++++++++++++++--------------- lib/widget/common.dart | 27 +++--- lib/widget/search.dart | 8 +- 10 files changed, 159 insertions(+), 118 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 256d8eb..c6ef3f0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -27,7 +27,7 @@ class MyApp extends StatelessWidget { home: HomePage(), debugShowCheckedModeBanner: false, theme: ThemeData( - scaffoldBackgroundColor: Color(0xFFF5F5F5), + useMaterial3: true, fontFamily: 'CustomFont', ), ); diff --git a/lib/pages/blog_page.dart b/lib/pages/blog_page.dart index 287010e..4ea1ebb 100644 --- a/lib/pages/blog_page.dart +++ b/lib/pages/blog_page.dart @@ -84,7 +84,7 @@ class _BlogPageState extends State { ); } - return ListView.builder( + return ListView.separated( controller: _scrollController, itemCount: provider.blogList.length, itemBuilder: (context, index) { @@ -94,6 +94,9 @@ class _BlogPageState extends State { child: BlogCard(blog: blog), ); }, + separatorBuilder: (BuildContext context, int index) { + return SizedBox(height: 8); + }, ); } diff --git a/lib/pages/category_list_page.dart b/lib/pages/category_list_page.dart index 1717aae..e3c6568 100644 --- a/lib/pages/category_list_page.dart +++ b/lib/pages/category_list_page.dart @@ -24,7 +24,7 @@ class _CategoryListPageState extends State { } Widget _buildBlogList(BlogProvider provider) { - return ListView.builder( + return ListView.separated( itemCount: provider.blogList.length, itemBuilder: (context, index) { final blog = provider.blogList[index]; @@ -33,6 +33,9 @@ class _CategoryListPageState extends State { child: buildBlogListItem(context, blog, index + 1), ); }, + separatorBuilder: (BuildContext context, int index) { + return SizedBox(height: 8); + }, ); } diff --git a/lib/pages/category_page.dart b/lib/pages/category_page.dart index 9f99d4e..6a8aef2 100644 --- a/lib/pages/category_page.dart +++ b/lib/pages/category_page.dart @@ -36,12 +36,15 @@ class _CategoryPageState extends State { SizedBox(height: 10), // 分类列表 Expanded( - child: ListView.builder( + child: ListView.separated( itemCount: provider.categoryList.length, itemBuilder: (context, index) { final category = provider.categoryList[index]; return buildCategoryCard(context, category); }, + separatorBuilder: (BuildContext context, int index) { + return SizedBox(height: 8); + }, ), ), ], diff --git a/lib/pages/stats_page.dart b/lib/pages/stats_page.dart index 23774fe..fedf58e 100644 --- a/lib/pages/stats_page.dart +++ b/lib/pages/stats_page.dart @@ -1,3 +1,4 @@ +import 'package:blog_app/utils/index.dart'; import 'package:provider/provider.dart'; import 'package:blog_app/provider/blog.dart'; import 'package:blog_app/widget/blog.dart'; @@ -131,29 +132,35 @@ class StatsPageState extends State { shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), childAspectRatio: 2, + mainAxisSpacing: 8, + crossAxisSpacing: 8, children: [ buildStatsCard( + icon: Icons.article, context: context, title: '博客数量', - count: provider.overviewStats.blogCount, + count: provider.overviewStats.blogCount.toString(), unit: '篇', ), buildStatsCard( + icon: Icons.folder, context: context, title: '分类数量', - count: provider.overviewStats.categoryCount, + count: provider.overviewStats.categoryCount.toString(), unit: '个', ), buildStatsCard( + icon: Icons.remove_red_eye, context: context, title: '访问量', - count: provider.overviewStats.visitCount, + count: formatChineseDecimal(provider.overviewStats.visitCount), unit: '次', ), buildStatsCard( + icon: Icons.text_fields, context: context, title: '总字数', - count: provider.overviewStats.wordCount, + count: formatChineseDecimal(provider.overviewStats.wordCount), unit: '字', ), ], @@ -181,44 +188,32 @@ class StatsPageState extends State { }); }, ), - const SizedBox(height: 6), + const SizedBox(height: 8), _buildBlogStatsGrid(provider), - const SizedBox(height: 6), + const SizedBox(height: 8), SizedBox( height: chartHeight, - child: buildCard( - context: context, - child: _buildApprovedStats(provider), - ), + child: BuildCard(child: _buildApprovedStats(provider)), ), - const SizedBox(height: 6), + const SizedBox(height: 8), SizedBox( height: chartHeight, - child: buildCard(context: context, child: _buildVisitStats(provider)), + child: BuildCard(child: _buildVisitStats(provider)), ), - const SizedBox(height: 6), + const SizedBox(height: 8), SizedBox( height: chartHeight, - child: buildCard( - context: context, - child: _buildVisitRankStats(provider), - ), + child: BuildCard(child: _buildVisitRankStats(provider)), ), - const SizedBox(height: 6), + const SizedBox(height: 8), SizedBox( height: chartHeight, - child: buildCard( - context: context, - child: _buildCategoryStats(provider), - ), + child: BuildCard(child: _buildCategoryStats(provider)), ), - const SizedBox(height: 6), + const SizedBox(height: 8), SizedBox( height: chartHeight, - child: buildCard( - context: context, - child: _buildReadRankStats(provider), - ), + child: BuildCard(child: _buildReadRankStats(provider)), ), ], ); @@ -233,8 +228,8 @@ class StatsPageState extends State { if (blogProvider.isLoading) buildLoadingIndicator() else - SingleChildScrollView(child: _buildContent(blogProvider)) - ] + SingleChildScrollView(child: _buildContent(blogProvider)), + ], ); } } diff --git a/lib/pages/timeline_page.dart b/lib/pages/timeline_page.dart index 55b62a0..766b882 100644 --- a/lib/pages/timeline_page.dart +++ b/lib/pages/timeline_page.dart @@ -28,7 +28,7 @@ class TimelinePageState extends State { return Timeline.tileBuilder( theme: TimelineThemeData(nodePosition: 0, indicatorPosition: 0), - padding: EdgeInsets.all(6), + padding: EdgeInsets.all(0), builder: TimelineTileBuilder.connected( itemCount: provider.blogList.length, connectorBuilder: @@ -39,9 +39,12 @@ class TimelinePageState extends State { }, contentsBuilder: (context, index) { final blog = provider.blogList[index]; - return InkWell( - onTap: () => navigatorToBlogDetail(context, blog.id), - child: buildBlogListItem(context, blog, index + 1), + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4.0), + child: InkWell( + onTap: () => navigatorToBlogDetail(context, blog.id), + child: buildBlogListItem(context, blog, index + 1), + ), ); }, ), diff --git a/lib/utils/index.dart b/lib/utils/index.dart index 925286c..36921f1 100644 --- a/lib/utils/index.dart +++ b/lib/utils/index.dart @@ -26,3 +26,28 @@ PageResult convertPageResponse( 'Expected a Map for page response conversion, but got ${data.runtimeType}', ); } + +/// 将数字格式化为中文单位(万/亿) +String formatChineseDecimal(num number, {int decimalPlaces = 2}) { + if (number < 10000) { + return number.toString(); + } else if (number < 100000000) { + // 万单位 + double result = number / 10000.0; + return '${_formatDecimal(result, decimalPlaces)}万'; + } else { + // 亿单位 + double result = number / 100000000.0; + return '${_formatDecimal(result, decimalPlaces)}亿'; + } +} + +String _formatDecimal(double number, int decimalPlaces) { + // 去除末尾的0和小数点 + String formatted = number.toStringAsFixed(decimalPlaces); + if (formatted.contains('.')) { + formatted = formatted.replaceAll(RegExp(r'0*$'), ''); + formatted = formatted.replaceAll(RegExp(r'\.$'), ''); + } + return formatted; +} diff --git a/lib/widget/blog.dart b/lib/widget/blog.dart index ac8218c..0c70bff 100644 --- a/lib/widget/blog.dart +++ b/lib/widget/blog.dart @@ -53,7 +53,7 @@ class BlogCard extends StatelessWidget { alignment: WrapAlignment.center, children: [ if (blog.isGreat) - _buildBlogLabelItem('精品', Icons.workspace_premium, Colors.amber), + _buildBlogLabelItem('精品', Icons.workspace_premium, Colors.deepOrange), if (blog.topValue > 1) _buildBlogLabelItem('置顶', Icons.push_pin, Colors.red), _buildBlogLabelItem(blog.category, Icons.folder, Colors.blue), @@ -114,26 +114,20 @@ class BlogCard extends StatelessWidget { @override Widget build(BuildContext context) { - return buildCard( - context: context, - child: Padding( - padding: EdgeInsets.all(12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - _buildBlogTitle(context), - SizedBox(height: 16), - _buildBlogLabel(), - SizedBox(height: 16), - Container( - height: 1, - width: 80, - color: Theme.of(context).colorScheme.primary, - ), - SizedBox(height: 16), - _buildBlogSummary(), - ], - ), + final colors = Theme.of(context).colorScheme; + + return BuildCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _buildBlogTitle(context), + SizedBox(height: 16), + _buildBlogLabel(), + SizedBox(height: 16), + Container(height: 1, width: 80, color: colors.primary), + SizedBox(height: 16), + _buildBlogSummary(), + ], ), ); } @@ -164,8 +158,7 @@ Widget buildCategoryTitle(BuildContext context, int count) { Widget buildCategoryCard(BuildContext context, BlogCategory category) { final colors = Theme.of(context).colorScheme; - return buildCard( - context: context, + return BuildCard( child: ListTile( leading: Container( width: 50, @@ -222,18 +215,12 @@ Widget buildListTitle(int count) { Widget buildBlogListItem(BuildContext context, Blog blog, int index) { final colors = Theme.of(context).colorScheme; - return buildCard( - context: context, + return BuildCard( child: Padding( - padding: EdgeInsets.all(10), + padding: EdgeInsets.all(0), child: Row( children: [ - Text( - '$index.', - style: TextStyle( - fontWeight: FontWeight.w500 - ), - ), + Text('$index.', style: TextStyle(fontWeight: FontWeight.w500)), SizedBox(width: 10), Text( formatDateString(blog.createTime), @@ -260,48 +247,65 @@ Widget buildBlogListItem(BuildContext context, Blog blog, int index) { Widget buildStatsCard({ required BuildContext context, + required IconData icon, required String title, - required int count, + required String count, required String unit, }) { - return buildCard( - context: context, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, + final colors = Theme.of(context).colorScheme; + + return BuildCard( + child: Row( children: [ - Text( - title, - style: TextStyle( - fontSize: 16, - color: Colors.grey.shade600, - fontWeight: FontWeight.w500, + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: colors.primary.withAlpha(50), + shape: BoxShape.circle, ), + child: Icon(icon, size: 26, color: colors.primary), ), - const SizedBox(height: 12), - Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - count.toString(), - style: const TextStyle( - fontSize: 28, - fontWeight: FontWeight.bold, - color: Colors.black87, - height: 1.0, + SizedBox(width: 6), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + title, + style: TextStyle( + fontSize: 16, + color: Colors.grey.shade600, + fontWeight: FontWeight.w500, + ), ), - ), - const SizedBox(width: 4), - Text( - unit, - style: TextStyle( - fontSize: 14, - color: Colors.grey.shade600, - fontWeight: FontWeight.w500, + const SizedBox(height: 6), + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + count, + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + color: colors.primary, + height: 1.0, + ), + ), + const SizedBox(width: 4), + Text( + unit, + style: TextStyle( + fontSize: 14, + color: Colors.grey.shade600, + fontWeight: FontWeight.w500, + ), + ), + ], ), - ), - ], + ], + ), ), ], ), diff --git a/lib/widget/common.dart b/lib/widget/common.dart index cf26c57..46a6083 100644 --- a/lib/widget/common.dart +++ b/lib/widget/common.dart @@ -1,20 +1,23 @@ import 'package:flutter/material.dart'; -Widget buildCard({required BuildContext context, required Widget child}) { - final colors = Theme.of(context).colorScheme; - return Card( - elevation: 0, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), - child: Container( - width: double.infinity, +class BuildCard extends StatelessWidget { + final Widget? child; + + const BuildCard({super.key, this.child}); + + @override + Widget build(BuildContext context) { + final colors = Theme.of(context).colorScheme; + + return Container( decoration: BoxDecoration( - color: colors.surface, + color: colors.surfaceContainer, borderRadius: BorderRadius.circular(12), - border: Border.all(color: colors.outline.withAlpha(50), width: 1), ), - child: Padding(padding: EdgeInsets.all(8), child: child), - ), - ); + padding: const EdgeInsets.all(16), + child: child, + ); + } } Widget circleIconButton({ diff --git a/lib/widget/search.dart b/lib/widget/search.dart index 3138159..a59fe49 100644 --- a/lib/widget/search.dart +++ b/lib/widget/search.dart @@ -27,8 +27,7 @@ Widget buildSearchField({ } Widget buildSearchResultItem(BuildContext context, BlogSearch blog) { - return buildCard( - context: context, + return BuildCard( child: ListTile( title: Text( blog.title, @@ -56,11 +55,14 @@ Widget buildSearchResultList( BuildContext context, List searchResultList, ) { - return ListView.builder( + return ListView.separated( itemCount: searchResultList.length, itemBuilder: (context, index) { return buildSearchResultItem(context, searchResultList[index]); }, + separatorBuilder: (BuildContext context, int index) { + return SizedBox(height: 8); + }, ); }