From 625a872d2f38a289b1929540ae4fdf223ab4e6a1 Mon Sep 17 00:00:00 2001 From: Cxx0822 <1556464090@qq.com> Date: Wed, 19 Nov 2025 10:40:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=9B=B4=E6=96=B0=E5=9B=BE=E7=89=87url?= =?UTF-8?q?=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/config/app_config.dart | 9 +++- lib/layout/nav_bar.dart | 43 +++++++-------- lib/models/recipe.dart | 20 +++++++ lib/provider/food_provider.dart | 2 + lib/utils/minio_utils.dart | 11 ++-- lib/views/recipe_detail.dart | 18 +------ lib/views/record_form.dart | 23 ++++---- lib/widgets/common/image.dart | 29 +++++----- lib/widgets/common/index.dart | 4 +- lib/widgets/moment/card.dart | 4 +- lib/widgets/recipe/calendar.dart | 93 ++++++++++++++++++-------------- lib/widgets/recipe/card.dart | 19 +++---- lib/widgets/recipe/timeline.dart | 25 +++------ 13 files changed, 150 insertions(+), 150 deletions(-) diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index 68e7068..10cf5e5 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -5,5 +5,10 @@ class AppConfig { // http://14.103.235.151:81/food-service // static const String baseApiUrl = "http://14.103.235.151:81/food-service"; // static const String baseApiUrl = "http://192.168.1.3:8100"; - static const String baseApiUrl = "https://cxx0822.iepose.cn/food-api"; -} \ No newline at end of file + // static const String baseApiUrl = "https://cxx0822.iepose.cn/food-api"; + static const String baseApiUrl = "http://127.0.0.1:8083"; + static const String rustfsIp = '14.103.235.151'; + static const String rustfsFileUrl = 'http://14.103.235.151:9100'; + static const String bucketName = 'food'; + static const String imageBaseUrl = '$rustfsFileUrl/$bucketName/'; +} diff --git a/lib/layout/nav_bar.dart b/lib/layout/nav_bar.dart index d1a211f..2d78513 100644 --- a/lib/layout/nav_bar.dart +++ b/lib/layout/nav_bar.dart @@ -87,44 +87,37 @@ Widget buildBottomSheetBody(BuildContext context) { ListTile( leading: Icon(Icons.book, color: colors.primary), title: const Text('新增菜谱'), - onTap: () { - Navigator.pop(context); - _handleAddRecipe(context); - }, + onTap: () => _handleAddRecipe(context, provider), ), ListTile( leading: Icon(Icons.note_add, color: colors.primary), title: const Text('新增记录'), - onTap: () { - provider.resetRecordForm(); - Navigator.pop(context); - Navigator.pushNamed(context, "/recordForm"); - }, + onTap: () => _handleAddRecord(context, provider), ), ListTile( leading: Icon(Icons.group, color: colors.primary), title: const Text('发布朋友圈'), - onTap: () { - Navigator.pop(context); - _handleAddRecord(context); - }, + onTap: () => _handleAddMoment(context, provider), ), ], ); } -// 处理添加菜谱 -void _handleAddRecipe(BuildContext context) { - // 这里添加跳转或处理添加菜谱的逻辑 - ScaffoldMessenger.of( - context, - ).showSnackBar(const SnackBar(content: Text('添加菜谱功能'))); +void _handleAddRecipe(BuildContext context, FoodProvider provider) { + // provider.resetRecordForm(); + // Navigator.pop(context); + // Navigator.pushNamed(context, "/recordForm"); } -// 处理添加记录 -void _handleAddRecord(BuildContext context) { - // 这里添加跳转或处理添加记录的逻辑 - ScaffoldMessenger.of( - context, - ).showSnackBar(const SnackBar(content: Text('添加记录功能'))); +void _handleAddRecord(BuildContext context, FoodProvider provider) { + provider.resetRecordForm(); + provider.isEditing = false; + Navigator.pop(context); + Navigator.pushNamed(context, "/recordForm"); +} + +void _handleAddMoment(BuildContext context, FoodProvider provider) { + // provider.resetRecordForm(); + // Navigator.pop(context); + // Navigator.pushNamed(context, "/recordForm"); } diff --git a/lib/models/recipe.dart b/lib/models/recipe.dart index 6ead219..2a0ac4d 100644 --- a/lib/models/recipe.dart +++ b/lib/models/recipe.dart @@ -235,6 +235,26 @@ class RecipeDetail { _$RecipeDetailFromJson(json); Map toJson() => _$RecipeDetailToJson(this); + + static RecipeDetail getEmpty() { + return RecipeDetail( + id: 0, + name: '', + category: '', + recommendRate: 0, + remark: '', + isShare: false, + userId: 0, + username: '', + avatar: '', + materialList: [], + stepList: [], + recordList: [], + likeList: [], + favouriteList: [], + commentList: [], + ); + } } enum ViewType { recipe, calendar, timeline } diff --git a/lib/provider/food_provider.dart b/lib/provider/food_provider.dart index 6b960f1..d233944 100644 --- a/lib/provider/food_provider.dart +++ b/lib/provider/food_provider.dart @@ -4,6 +4,8 @@ import 'package:food_hub_app/models/recipe.dart'; class FoodProvider with ChangeNotifier { late FoodRecord _recordFormItem; + late bool isEditing; + FoodRecord get recordFormItem => _recordFormItem; void resetRecordForm() { diff --git a/lib/utils/minio_utils.dart b/lib/utils/minio_utils.dart index c5ad380..4b81bc9 100644 --- a/lib/utils/minio_utils.dart +++ b/lib/utils/minio_utils.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:crypto/crypto.dart'; import 'package:file_picker/file_picker.dart'; +import 'package:food_hub_app/config/app_config.dart'; import 'package:minio/io.dart'; import 'package:minio/minio.dart'; @@ -10,13 +11,9 @@ class MinIOHelper { factory MinIOHelper() => _instance; - final String rustfsIp = '14.103.235.151'; - final String rustfsFileUrl = 'http://14.103.235.151:9100'; - final String bucketName = 'food'; - MinIOHelper._internal() { _minio = Minio( - endPoint: rustfsIp, + endPoint: AppConfig.rustfsIp, port: 9100, accessKey: "tHSFfcDW8qpCzKa2Xg6Y", secretKey: "oq79EeYJ4jdczRp2IHUMCnbKtSw58NgDlG3sOkvX", @@ -26,8 +23,6 @@ class MinIOHelper { late Minio _minio; - String get imageBaseUrl => '$rustfsFileUrl/$bucketName/'; - Future uploadFile({ required PlatformFile file, Function(double)? onProgress, @@ -36,7 +31,7 @@ class MinIOHelper { String hashName = await _generateMD5HashName(file.path!); String fileName = '$hashName${_getFileExtension(file.name)}'; - await _minio.fPutObject(bucketName, fileName, file.path!); + await _minio.fPutObject(AppConfig.bucketName, fileName, file.path!); return fileName; } catch (e) { throw Exception('文件上传失败: $e'); diff --git a/lib/views/recipe_detail.dart b/lib/views/recipe_detail.dart index bb28c53..32102cb 100644 --- a/lib/views/recipe_detail.dart +++ b/lib/views/recipe_detail.dart @@ -11,23 +11,7 @@ class RecipeDetailPage extends StatefulWidget { } class _RecipeDetailState extends State { - RecipeDetail recipe = RecipeDetail( - id: 0, - name: '', - category: '', - recommendRate: 0, - remark: '', - isShare: false, - userId: 0, - username: '', - avatar: '', - materialList: [], - stepList: [], - recordList: [], - likeList: [], - favouriteList: [], - commentList: [], - ); + RecipeDetail recipe = RecipeDetail.getEmpty(); // 定义三个分类列表 List mainMaterialList = []; // 主料 diff --git a/lib/views/record_form.dart b/lib/views/record_form.dart index 6174e19..d26c2a2 100644 --- a/lib/views/record_form.dart +++ b/lib/views/record_form.dart @@ -1,6 +1,7 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:food_hub_app/config/app_config.dart'; import 'package:food_hub_app/provider/food_provider.dart'; import 'package:food_hub_app/utils/minio_utils.dart'; import 'package:food_hub_app/widgets/common/form.dart'; @@ -32,10 +33,14 @@ class _RecordFormPageState extends State { @override Widget build(BuildContext context) { final colors = Theme.of(context).colorScheme; + final provider = context.watch(); + + final String title = provider.isEditing ? '编辑记录' : '新增记录'; + print(provider.recordFormItem.imageUrl); return Scaffold( appBar: AppBar( - title: const Text('新增记录', style: TextStyle(color: Colors.white)), + title: Text(title, style: TextStyle(color: Colors.white)), backgroundColor: colors.primary, leading: IconButton( icon: const Icon(Icons.arrow_back, color: Colors.white), @@ -57,7 +62,6 @@ class _RecordFormPageState extends State { return FormBuilder( key: _formKey, - autovalidateMode: AutovalidateMode.onUserInteraction, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -65,6 +69,8 @@ class _RecordFormPageState extends State { const SizedBox(height: 8), FormBuilderTextField( name: _nameField, + initialValue: provider.recordFormItem.name, + enabled: !provider.isEditing, focusNode: _focusNode, onChanged: (value) { setState(() { @@ -94,6 +100,7 @@ class _RecordFormPageState extends State { FormBuilderTextField( name: _dateField, readOnly: true, + initialValue: provider.recordFormItem.date, decoration: buildInputDecoration( context: context, hintText: '请选择完成时间', @@ -124,7 +131,7 @@ class _RecordFormPageState extends State { buildImagePreviewItem( context: context, imageUrl: - '${MinIOHelper().imageBaseUrl}${provider.recordFormItem.imageUrl}', + '${AppConfig.imageBaseUrl}${provider.recordFormItem.imageUrl}', onRemoveImage: () => _removeImage(provider), ), ], @@ -186,16 +193,12 @@ class _RecordFormPageState extends State { void _submitForm(FoodProvider provider) { if (_formKey.currentState?.saveAndValidate() ?? false) { if (provider.recordFormItem.imageUrl.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('请上传图片'), - backgroundColor: Colors.red, - behavior: SnackBarBehavior.floating, - ), - ); + showErrorToast('请上传图片'); return; } + print(provider.recordFormItem.name); + print(provider.recordFormItem.date); print(provider.recordFormItem.imageUrl); } } diff --git a/lib/widgets/common/image.dart b/lib/widgets/common/image.dart index d13cdc1..568b50c 100644 --- a/lib/widgets/common/image.dart +++ b/lib/widgets/common/image.dart @@ -107,20 +107,23 @@ Widget buildFileImage(BuildContext context, File imageFile) { } Widget buildNetworkImage(BuildContext context, String url) { - return ClipRRect( - borderRadius: BorderRadius.circular(8), - child: GestureDetector( - onTap: () { - showFullScreenImage(context, NetworkImage(url)); - }, - child: Image.network( - url, - fit: BoxFit.cover, - loadingBuilder: (context, child, loadingProgress) { - if (loadingProgress == null) return child; - return buildImageLoadingIndicator(loadingProgress); + return AspectRatio( + aspectRatio: 1.5, + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: GestureDetector( + onTap: () { + showFullScreenImage(context, NetworkImage(url)); }, - errorBuilder: (context, error, stackTrace) => _buildErrorImage(), + child: Image.network( + url, + fit: BoxFit.cover, + loadingBuilder: (context, child, loadingProgress) { + if (loadingProgress == null) return child; + return buildImageLoadingIndicator(loadingProgress); + }, + errorBuilder: (context, error, stackTrace) => _buildErrorImage(), + ), ), ), ); diff --git a/lib/widgets/common/index.dart b/lib/widgets/common/index.dart index 5c243b8..0c74cd7 100644 --- a/lib/widgets/common/index.dart +++ b/lib/widgets/common/index.dart @@ -153,7 +153,7 @@ void showSuccessToast(String message) { Fluttertoast.showToast( msg: message, toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.CENTER, + gravity: ToastGravity.TOP, timeInSecForIosWeb: 1, backgroundColor: Colors.green, textColor: Colors.white, @@ -166,7 +166,7 @@ void showErrorToast(String message) { Fluttertoast.showToast( msg: message, toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.CENTER, + gravity: ToastGravity.TOP, timeInSecForIosWeb: 1, backgroundColor: Colors.red, textColor: Colors.white, diff --git a/lib/widgets/moment/card.dart b/lib/widgets/moment/card.dart index ac6a499..1f05d76 100644 --- a/lib/widgets/moment/card.dart +++ b/lib/widgets/moment/card.dart @@ -57,7 +57,7 @@ class MomentCard extends StatelessWidget { size: TDAvatarSize.medium, type: TDAvatarType.normal, fit: BoxFit.contain, - avatarUrl: '${AppConfig.baseApiUrl}/${moment.avatar}', + avatarUrl: '${AppConfig.imageBaseUrl}/${moment.avatar}', ); } } @@ -101,7 +101,7 @@ class MomentCard extends StatelessWidget { // 生成图片URL列表(用于预览时切换) final List imageUrls = moment.imageList - .map((path) => '${AppConfig.baseApiUrl}/$path') + .map((path) => '${AppConfig.imageBaseUrl}$path') .toList(); void imageTapClick(int index) { diff --git a/lib/widgets/recipe/calendar.dart b/lib/widgets/recipe/calendar.dart index 0b7729c..b9f93fe 100644 --- a/lib/widgets/recipe/calendar.dart +++ b/lib/widgets/recipe/calendar.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; import 'package:food_hub_app/apis/recipe.dart'; import 'package:food_hub_app/models/recipe.dart'; +import 'package:food_hub_app/provider/food_provider.dart'; import 'package:food_hub_app/utils/date_util.dart'; import 'package:food_hub_app/utils/index.dart'; import 'package:food_hub_app/widgets/common/index.dart'; import 'package:table_calendar/table_calendar.dart'; +import 'package:provider/provider.dart'; class RecipeCalendar extends StatefulWidget { const RecipeCalendar({super.key}); @@ -23,10 +25,10 @@ class _RecipeCalendarState extends State { @override void initState() { super.initState(); - refreshRecord(); + _refreshRecord(); } - Future refreshRecord() async { + Future _refreshRecord() async { final result = await queryRecordApi( getFirstDayOfMonth(_focusedDay), getLastDayOfMonth(_focusedDay), @@ -40,11 +42,11 @@ class _RecipeCalendarState extends State { _selectedDay = _focusedDay; } - refreshSelectRecord(); + _refreshSelectRecord(); }); } - void refreshSelectRecord() { + void _refreshSelectRecord() { setState(() { selectRecordList = recordList @@ -53,7 +55,7 @@ class _RecipeCalendarState extends State { }); } - TableCalendar recipeCalendar() { + TableCalendar _recipeCalendar() { return TableCalendar( locale: 'zh_CN', headerStyle: const HeaderStyle( @@ -69,13 +71,13 @@ class _RecipeCalendarState extends State { setState(() { _selectedDay = selectedDay; _focusedDay = focusedDay; - refreshSelectRecord(); + _refreshSelectRecord(); }); } }, onPageChanged: (focusedDay) { _focusedDay = focusedDay; - refreshRecord(); + _refreshRecord(); }, // 自定义日期单元格构建器 calendarBuilders: CalendarBuilders( @@ -89,7 +91,7 @@ class _RecipeCalendarState extends State { ); } - Widget dailyItem(BuildContext context) { + Widget _dailyItem(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -102,14 +104,49 @@ class _RecipeCalendarState extends State { physics: NeverScrollableScrollPhysics(), itemCount: selectRecordList.length, itemBuilder: (context, index) { - return recipeRecordItem(context, selectRecordList[index]); + return _buildRecordItem(context, selectRecordList[index]); }, ), ], ); } - Widget recipeRecordItem(BuildContext context, FoodRecord record) { + Widget _buildRecordItemAvatar(String name, Color color) { + return CircleAvatar( + radius: 20, + backgroundColor: color, + child: Text( + name, + style: TextStyle( + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ); + } + + Widget _buildRecordItemBody(String name, String category) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(name, style: TextStyle(fontWeight: FontWeight.bold)), + SizedBox(height: 5), + Text(category, style: TextStyle(color: Colors.grey)), + ], + ); + } + + void _handleEditRecord(BuildContext context, FoodRecord record) { + final provider = context.read(); + + provider.initRecordForm(record); + provider.isEditing = true; + Navigator.pushNamed(context, "/recordForm"); + } + + Widget _buildRecordItem(BuildContext context, FoodRecord record) { final colors = Theme.of(context).colorScheme; return Card( @@ -119,37 +156,13 @@ class _RecipeCalendarState extends State { padding: EdgeInsets.all(10), child: Row( children: [ - CircleAvatar( - radius: 20, - backgroundColor: colors.primary, - child: Text( - record.category[0], - style: TextStyle( - color: Colors.white, - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - ), + _buildRecordItemAvatar(record.category[0], colors.primary), SizedBox(width: 10), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - record.name, - style: TextStyle(fontWeight: FontWeight.bold), - ), - SizedBox(height: 5), - Text(record.category, style: TextStyle(color: Colors.grey)), - ], - ), - ), + Expanded(child: _buildRecordItemBody(record.name, record.category)), circleIconButton( context: context, icon: Icons.chevron_right, - onPressed: () => {}, + onPressed: () => _handleEditRecord(context, record), ), ], ), @@ -205,8 +218,10 @@ class _RecipeCalendarState extends State { Widget build(BuildContext context) { return Column( children: [ - buildCard(context: context, child: recipeCalendar()), - Expanded(child: buildCard(context: context, child: dailyItem(context))), + buildCard(context: context, child: _recipeCalendar()), + Expanded( + child: buildCard(context: context, child: _dailyItem(context)), + ), ], ); } diff --git a/lib/widgets/recipe/card.dart b/lib/widgets/recipe/card.dart index 7302c69..430e1a8 100644 --- a/lib/widgets/recipe/card.dart +++ b/lib/widgets/recipe/card.dart @@ -18,16 +18,6 @@ class RecipeCard extends StatelessWidget { Widget build(BuildContext context) { final String firstImageUrl = recipe.recordList.first.imageUrl; - Widget buildRecipeImage() { - return AspectRatio( - aspectRatio: 1.5, - child: buildNetworkImage( - context, - '${AppConfig.baseApiUrl}/$firstImageUrl', - ), - ); - } - Widget buildRecipeContent(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -96,12 +86,15 @@ class RecipeCard extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - buildRecipeImage(), + buildNetworkImage( + context, + '${AppConfig.imageBaseUrl}/$firstImageUrl', + ), SizedBox(height: 8), GestureDetector( onTap: () => navigatorToRecipeDetail(context), - child: buildRecipeContent(context) - ) + child: buildRecipeContent(context), + ), ], ), ); diff --git a/lib/widgets/recipe/timeline.dart b/lib/widgets/recipe/timeline.dart index 3ecaf15..c034d1b 100644 --- a/lib/widgets/recipe/timeline.dart +++ b/lib/widgets/recipe/timeline.dart @@ -31,17 +31,7 @@ class _RecipeTimeline extends State { } Widget buildTimelineCard(BuildContext context, FoodRecord record) { - final imageUrls = ['${AppConfig.baseApiUrl}/${record.imageUrl}']; - - void imageTapClick() { - Navigator.push( - context, - MaterialPageRoute( - builder: - (context) => ImagePreviewPage(images: imageUrls, initialIndex: 0), - ), - ); - } + final imageUrl = '${AppConfig.imageBaseUrl}${record.imageUrl}'; return buildCard( context: context, @@ -50,7 +40,10 @@ class _RecipeTimeline extends State { children: [ Row( children: [ - Icon(Icons.date_range, color: Theme.of(context).colorScheme.primary), + Icon( + Icons.date_range, + color: Theme.of(context).colorScheme.primary, + ), SizedBox(width: 5), Text( record.date, @@ -65,13 +58,7 @@ class _RecipeTimeline extends State { style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 5), - GestureDetector( - onTap: () => imageTapClick(), - child: AspectRatio( - aspectRatio: 1.5, - child: buildNetworkImage(context, imageUrls.first), - ), - ), + buildNetworkImage(context, imageUrl), ], ), ],