feat:更新图片url路径

This commit is contained in:
2025-11-19 10:40:02 +08:00
parent 7ede1c2d89
commit 625a872d2f
13 changed files with 150 additions and 150 deletions

View File

@@ -5,5 +5,10 @@ class AppConfig {
// http://14.103.235.151:81/food-service // 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://14.103.235.151:81/food-service";
// static const String baseApiUrl = "http://192.168.1.3:8100"; // static const String baseApiUrl = "http://192.168.1.3:8100";
static const String baseApiUrl = "https://cxx0822.iepose.cn/food-api"; // 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/';
} }

View File

@@ -87,44 +87,37 @@ Widget buildBottomSheetBody(BuildContext context) {
ListTile( ListTile(
leading: Icon(Icons.book, color: colors.primary), leading: Icon(Icons.book, color: colors.primary),
title: const Text('新增菜谱'), title: const Text('新增菜谱'),
onTap: () { onTap: () => _handleAddRecipe(context, provider),
Navigator.pop(context);
_handleAddRecipe(context);
},
), ),
ListTile( ListTile(
leading: Icon(Icons.note_add, color: colors.primary), leading: Icon(Icons.note_add, color: colors.primary),
title: const Text('新增记录'), title: const Text('新增记录'),
onTap: () { onTap: () => _handleAddRecord(context, provider),
provider.resetRecordForm();
Navigator.pop(context);
Navigator.pushNamed(context, "/recordForm");
},
), ),
ListTile( ListTile(
leading: Icon(Icons.group, color: colors.primary), leading: Icon(Icons.group, color: colors.primary),
title: const Text('发布朋友圈'), title: const Text('发布朋友圈'),
onTap: () { onTap: () => _handleAddMoment(context, provider),
Navigator.pop(context);
_handleAddRecord(context);
},
), ),
], ],
); );
} }
// 处理添加菜谱 void _handleAddRecipe(BuildContext context, FoodProvider provider) {
void _handleAddRecipe(BuildContext context) { // provider.resetRecordForm();
// 这里添加跳转或处理添加菜谱的逻辑 // Navigator.pop(context);
ScaffoldMessenger.of( // Navigator.pushNamed(context, "/recordForm");
context,
).showSnackBar(const SnackBar(content: Text('添加菜谱功能')));
} }
// 处理添加记录 void _handleAddRecord(BuildContext context, FoodProvider provider) {
void _handleAddRecord(BuildContext context) { provider.resetRecordForm();
// 这里添加跳转或处理添加记录的逻辑 provider.isEditing = false;
ScaffoldMessenger.of( Navigator.pop(context);
context, Navigator.pushNamed(context, "/recordForm");
).showSnackBar(const SnackBar(content: Text('添加记录功能'))); }
void _handleAddMoment(BuildContext context, FoodProvider provider) {
// provider.resetRecordForm();
// Navigator.pop(context);
// Navigator.pushNamed(context, "/recordForm");
} }

View File

@@ -235,6 +235,26 @@ class RecipeDetail {
_$RecipeDetailFromJson(json); _$RecipeDetailFromJson(json);
Map<String, dynamic> toJson() => _$RecipeDetailToJson(this); Map<String, dynamic> 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 } enum ViewType { recipe, calendar, timeline }

View File

@@ -4,6 +4,8 @@ import 'package:food_hub_app/models/recipe.dart';
class FoodProvider with ChangeNotifier { class FoodProvider with ChangeNotifier {
late FoodRecord _recordFormItem; late FoodRecord _recordFormItem;
late bool isEditing;
FoodRecord get recordFormItem => _recordFormItem; FoodRecord get recordFormItem => _recordFormItem;
void resetRecordForm() { void resetRecordForm() {

View File

@@ -1,6 +1,7 @@
import 'dart:io'; import 'dart:io';
import 'package:crypto/crypto.dart'; import 'package:crypto/crypto.dart';
import 'package:file_picker/file_picker.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/io.dart';
import 'package:minio/minio.dart'; import 'package:minio/minio.dart';
@@ -10,13 +11,9 @@ class MinIOHelper {
factory MinIOHelper() => _instance; 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() { MinIOHelper._internal() {
_minio = Minio( _minio = Minio(
endPoint: rustfsIp, endPoint: AppConfig.rustfsIp,
port: 9100, port: 9100,
accessKey: "tHSFfcDW8qpCzKa2Xg6Y", accessKey: "tHSFfcDW8qpCzKa2Xg6Y",
secretKey: "oq79EeYJ4jdczRp2IHUMCnbKtSw58NgDlG3sOkvX", secretKey: "oq79EeYJ4jdczRp2IHUMCnbKtSw58NgDlG3sOkvX",
@@ -26,8 +23,6 @@ class MinIOHelper {
late Minio _minio; late Minio _minio;
String get imageBaseUrl => '$rustfsFileUrl/$bucketName/';
Future<String> uploadFile({ Future<String> uploadFile({
required PlatformFile file, required PlatformFile file,
Function(double)? onProgress, Function(double)? onProgress,
@@ -36,7 +31,7 @@ class MinIOHelper {
String hashName = await _generateMD5HashName(file.path!); String hashName = await _generateMD5HashName(file.path!);
String fileName = '$hashName${_getFileExtension(file.name)}'; String fileName = '$hashName${_getFileExtension(file.name)}';
await _minio.fPutObject(bucketName, fileName, file.path!); await _minio.fPutObject(AppConfig.bucketName, fileName, file.path!);
return fileName; return fileName;
} catch (e) { } catch (e) {
throw Exception('文件上传失败: $e'); throw Exception('文件上传失败: $e');

View File

@@ -11,23 +11,7 @@ class RecipeDetailPage extends StatefulWidget {
} }
class _RecipeDetailState extends State<RecipeDetailPage> { class _RecipeDetailState extends State<RecipeDetailPage> {
RecipeDetail recipe = RecipeDetail( RecipeDetail recipe = RecipeDetail.getEmpty();
id: 0,
name: '',
category: '',
recommendRate: 0,
remark: '',
isShare: false,
userId: 0,
username: '',
avatar: '',
materialList: [],
stepList: [],
recordList: [],
likeList: [],
favouriteList: [],
commentList: [],
);
// 定义三个分类列表 // 定义三个分类列表
List<RecipeMaterial> mainMaterialList = []; // 主料 List<RecipeMaterial> mainMaterialList = []; // 主料

View File

@@ -1,6 +1,7 @@
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.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/provider/food_provider.dart';
import 'package:food_hub_app/utils/minio_utils.dart'; import 'package:food_hub_app/utils/minio_utils.dart';
import 'package:food_hub_app/widgets/common/form.dart'; import 'package:food_hub_app/widgets/common/form.dart';
@@ -32,10 +33,14 @@ class _RecordFormPageState extends State<RecordFormPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme; final colors = Theme.of(context).colorScheme;
final provider = context.watch<FoodProvider>();
final String title = provider.isEditing ? '编辑记录' : '新增记录';
print(provider.recordFormItem.imageUrl);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('新增记录', style: TextStyle(color: Colors.white)), title: Text(title, style: TextStyle(color: Colors.white)),
backgroundColor: colors.primary, backgroundColor: colors.primary,
leading: IconButton( leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.white), icon: const Icon(Icons.arrow_back, color: Colors.white),
@@ -57,7 +62,6 @@ class _RecordFormPageState extends State<RecordFormPage> {
return FormBuilder( return FormBuilder(
key: _formKey, key: _formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@@ -65,6 +69,8 @@ class _RecordFormPageState extends State<RecordFormPage> {
const SizedBox(height: 8), const SizedBox(height: 8),
FormBuilderTextField( FormBuilderTextField(
name: _nameField, name: _nameField,
initialValue: provider.recordFormItem.name,
enabled: !provider.isEditing,
focusNode: _focusNode, focusNode: _focusNode,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
@@ -94,6 +100,7 @@ class _RecordFormPageState extends State<RecordFormPage> {
FormBuilderTextField( FormBuilderTextField(
name: _dateField, name: _dateField,
readOnly: true, readOnly: true,
initialValue: provider.recordFormItem.date,
decoration: buildInputDecoration( decoration: buildInputDecoration(
context: context, context: context,
hintText: '请选择完成时间', hintText: '请选择完成时间',
@@ -124,7 +131,7 @@ class _RecordFormPageState extends State<RecordFormPage> {
buildImagePreviewItem( buildImagePreviewItem(
context: context, context: context,
imageUrl: imageUrl:
'${MinIOHelper().imageBaseUrl}${provider.recordFormItem.imageUrl}', '${AppConfig.imageBaseUrl}${provider.recordFormItem.imageUrl}',
onRemoveImage: () => _removeImage(provider), onRemoveImage: () => _removeImage(provider),
), ),
], ],
@@ -186,16 +193,12 @@ class _RecordFormPageState extends State<RecordFormPage> {
void _submitForm(FoodProvider provider) { void _submitForm(FoodProvider provider) {
if (_formKey.currentState?.saveAndValidate() ?? false) { if (_formKey.currentState?.saveAndValidate() ?? false) {
if (provider.recordFormItem.imageUrl.isEmpty) { if (provider.recordFormItem.imageUrl.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( showErrorToast('请上传图片');
const SnackBar(
content: Text('请上传图片'),
backgroundColor: Colors.red,
behavior: SnackBarBehavior.floating,
),
);
return; return;
} }
print(provider.recordFormItem.name);
print(provider.recordFormItem.date);
print(provider.recordFormItem.imageUrl); print(provider.recordFormItem.imageUrl);
} }
} }

View File

@@ -107,20 +107,23 @@ Widget buildFileImage(BuildContext context, File imageFile) {
} }
Widget buildNetworkImage(BuildContext context, String url) { Widget buildNetworkImage(BuildContext context, String url) {
return ClipRRect( return AspectRatio(
borderRadius: BorderRadius.circular(8), aspectRatio: 1.5,
child: GestureDetector( child: ClipRRect(
onTap: () { borderRadius: BorderRadius.circular(8),
showFullScreenImage(context, NetworkImage(url)); child: GestureDetector(
}, onTap: () {
child: Image.network( showFullScreenImage(context, NetworkImage(url));
url,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return buildImageLoadingIndicator(loadingProgress);
}, },
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(),
),
), ),
), ),
); );

View File

@@ -153,7 +153,7 @@ void showSuccessToast(String message) {
Fluttertoast.showToast( Fluttertoast.showToast(
msg: message, msg: message,
toastLength: Toast.LENGTH_SHORT, toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER, gravity: ToastGravity.TOP,
timeInSecForIosWeb: 1, timeInSecForIosWeb: 1,
backgroundColor: Colors.green, backgroundColor: Colors.green,
textColor: Colors.white, textColor: Colors.white,
@@ -166,7 +166,7 @@ void showErrorToast(String message) {
Fluttertoast.showToast( Fluttertoast.showToast(
msg: message, msg: message,
toastLength: Toast.LENGTH_SHORT, toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER, gravity: ToastGravity.TOP,
timeInSecForIosWeb: 1, timeInSecForIosWeb: 1,
backgroundColor: Colors.red, backgroundColor: Colors.red,
textColor: Colors.white, textColor: Colors.white,

View File

@@ -57,7 +57,7 @@ class MomentCard extends StatelessWidget {
size: TDAvatarSize.medium, size: TDAvatarSize.medium,
type: TDAvatarType.normal, type: TDAvatarType.normal,
fit: BoxFit.contain, fit: BoxFit.contain,
avatarUrl: '${AppConfig.baseApiUrl}/${moment.avatar}', avatarUrl: '${AppConfig.imageBaseUrl}/${moment.avatar}',
); );
} }
} }
@@ -101,7 +101,7 @@ class MomentCard extends StatelessWidget {
// 生成图片URL列表用于预览时切换 // 生成图片URL列表用于预览时切换
final List<String> imageUrls = final List<String> imageUrls =
moment.imageList moment.imageList
.map((path) => '${AppConfig.baseApiUrl}/$path') .map((path) => '${AppConfig.imageBaseUrl}$path')
.toList(); .toList();
void imageTapClick(int index) { void imageTapClick(int index) {

View File

@@ -1,10 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:food_hub_app/apis/recipe.dart'; import 'package:food_hub_app/apis/recipe.dart';
import 'package:food_hub_app/models/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/date_util.dart';
import 'package:food_hub_app/utils/index.dart'; import 'package:food_hub_app/utils/index.dart';
import 'package:food_hub_app/widgets/common/index.dart'; import 'package:food_hub_app/widgets/common/index.dart';
import 'package:table_calendar/table_calendar.dart'; import 'package:table_calendar/table_calendar.dart';
import 'package:provider/provider.dart';
class RecipeCalendar extends StatefulWidget { class RecipeCalendar extends StatefulWidget {
const RecipeCalendar({super.key}); const RecipeCalendar({super.key});
@@ -23,10 +25,10 @@ class _RecipeCalendarState extends State<RecipeCalendar> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
refreshRecord(); _refreshRecord();
} }
Future<void> refreshRecord() async { Future<void> _refreshRecord() async {
final result = await queryRecordApi( final result = await queryRecordApi(
getFirstDayOfMonth(_focusedDay), getFirstDayOfMonth(_focusedDay),
getLastDayOfMonth(_focusedDay), getLastDayOfMonth(_focusedDay),
@@ -40,11 +42,11 @@ class _RecipeCalendarState extends State<RecipeCalendar> {
_selectedDay = _focusedDay; _selectedDay = _focusedDay;
} }
refreshSelectRecord(); _refreshSelectRecord();
}); });
} }
void refreshSelectRecord() { void _refreshSelectRecord() {
setState(() { setState(() {
selectRecordList = selectRecordList =
recordList recordList
@@ -53,7 +55,7 @@ class _RecipeCalendarState extends State<RecipeCalendar> {
}); });
} }
TableCalendar recipeCalendar() { TableCalendar _recipeCalendar() {
return TableCalendar( return TableCalendar(
locale: 'zh_CN', locale: 'zh_CN',
headerStyle: const HeaderStyle( headerStyle: const HeaderStyle(
@@ -69,13 +71,13 @@ class _RecipeCalendarState extends State<RecipeCalendar> {
setState(() { setState(() {
_selectedDay = selectedDay; _selectedDay = selectedDay;
_focusedDay = focusedDay; _focusedDay = focusedDay;
refreshSelectRecord(); _refreshSelectRecord();
}); });
} }
}, },
onPageChanged: (focusedDay) { onPageChanged: (focusedDay) {
_focusedDay = focusedDay; _focusedDay = focusedDay;
refreshRecord(); _refreshRecord();
}, },
// 自定义日期单元格构建器 // 自定义日期单元格构建器
calendarBuilders: CalendarBuilders( calendarBuilders: CalendarBuilders(
@@ -89,7 +91,7 @@ class _RecipeCalendarState extends State<RecipeCalendar> {
); );
} }
Widget dailyItem(BuildContext context) { Widget _dailyItem(BuildContext context) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@@ -102,14 +104,49 @@ class _RecipeCalendarState extends State<RecipeCalendar> {
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),
itemCount: selectRecordList.length, itemCount: selectRecordList.length,
itemBuilder: (context, index) { 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<FoodProvider>();
provider.initRecordForm(record);
provider.isEditing = true;
Navigator.pushNamed(context, "/recordForm");
}
Widget _buildRecordItem(BuildContext context, FoodRecord record) {
final colors = Theme.of(context).colorScheme; final colors = Theme.of(context).colorScheme;
return Card( return Card(
@@ -119,37 +156,13 @@ class _RecipeCalendarState extends State<RecipeCalendar> {
padding: EdgeInsets.all(10), padding: EdgeInsets.all(10),
child: Row( child: Row(
children: [ children: [
CircleAvatar( _buildRecordItemAvatar(record.category[0], colors.primary),
radius: 20,
backgroundColor: colors.primary,
child: Text(
record.category[0],
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
SizedBox(width: 10), SizedBox(width: 10),
Expanded( Expanded(child: _buildRecordItemBody(record.name, record.category)),
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)),
],
),
),
circleIconButton( circleIconButton(
context: context, context: context,
icon: Icons.chevron_right, icon: Icons.chevron_right,
onPressed: () => {}, onPressed: () => _handleEditRecord(context, record),
), ),
], ],
), ),
@@ -205,8 +218,10 @@ class _RecipeCalendarState extends State<RecipeCalendar> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
children: [ children: [
buildCard(context: context, child: recipeCalendar()), buildCard(context: context, child: _recipeCalendar()),
Expanded(child: buildCard(context: context, child: dailyItem(context))), Expanded(
child: buildCard(context: context, child: _dailyItem(context)),
),
], ],
); );
} }

View File

@@ -18,16 +18,6 @@ class RecipeCard extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final String firstImageUrl = recipe.recordList.first.imageUrl; final String firstImageUrl = recipe.recordList.first.imageUrl;
Widget buildRecipeImage() {
return AspectRatio(
aspectRatio: 1.5,
child: buildNetworkImage(
context,
'${AppConfig.baseApiUrl}/$firstImageUrl',
),
);
}
Widget buildRecipeContent(BuildContext context) { Widget buildRecipeContent(BuildContext context) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -96,12 +86,15 @@ class RecipeCard extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
buildRecipeImage(), buildNetworkImage(
context,
'${AppConfig.imageBaseUrl}/$firstImageUrl',
),
SizedBox(height: 8), SizedBox(height: 8),
GestureDetector( GestureDetector(
onTap: () => navigatorToRecipeDetail(context), onTap: () => navigatorToRecipeDetail(context),
child: buildRecipeContent(context) child: buildRecipeContent(context),
) ),
], ],
), ),
); );

View File

@@ -31,17 +31,7 @@ class _RecipeTimeline extends State<RecipeTimeline> {
} }
Widget buildTimelineCard(BuildContext context, FoodRecord record) { Widget buildTimelineCard(BuildContext context, FoodRecord record) {
final imageUrls = ['${AppConfig.baseApiUrl}/${record.imageUrl}']; final imageUrl = '${AppConfig.imageBaseUrl}${record.imageUrl}';
void imageTapClick() {
Navigator.push(
context,
MaterialPageRoute(
builder:
(context) => ImagePreviewPage(images: imageUrls, initialIndex: 0),
),
);
}
return buildCard( return buildCard(
context: context, context: context,
@@ -50,7 +40,10 @@ class _RecipeTimeline extends State<RecipeTimeline> {
children: [ children: [
Row( Row(
children: [ children: [
Icon(Icons.date_range, color: Theme.of(context).colorScheme.primary), Icon(
Icons.date_range,
color: Theme.of(context).colorScheme.primary,
),
SizedBox(width: 5), SizedBox(width: 5),
Text( Text(
record.date, record.date,
@@ -65,13 +58,7 @@ class _RecipeTimeline extends State<RecipeTimeline> {
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
), ),
const SizedBox(height: 5), const SizedBox(height: 5),
GestureDetector( buildNetworkImage(context, imageUrl),
onTap: () => imageTapClick(),
child: AspectRatio(
aspectRatio: 1.5,
child: buildNetworkImage(context, imageUrls.first),
),
),
], ],
), ),
], ],