From 7ede1c2d896cf13b34ed004590d685a8640f501d Mon Sep 17 00:00:00 2001 From: Cxx0822 <1556464090@qq.com> Date: Tue, 18 Nov 2025 23:18:24 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=9B=B4=E6=96=B0=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E8=A1=A8=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/layout/nav_bar.dart | 6 ++ lib/utils/minio_utils.dart | 4 +- lib/views/record_form.dart | 174 ++++++++++++++-------------------- lib/widgets/common/image.dart | 19 ++++ 4 files changed, 101 insertions(+), 102 deletions(-) diff --git a/lib/layout/nav_bar.dart b/lib/layout/nav_bar.dart index b8ea016..d1a211f 100644 --- a/lib/layout/nav_bar.dart +++ b/lib/layout/nav_bar.dart @@ -1,10 +1,14 @@ import 'package:flutter/material.dart'; import 'package:food_hub_app/models/layout.dart'; +import 'package:food_hub_app/models/recipe.dart'; +import 'package:food_hub_app/provider/food_provider.dart'; import 'package:food_hub_app/views/moment.dart'; import 'package:food_hub_app/views/profile.dart'; import 'package:food_hub_app/views/record.dart'; import 'package:food_hub_app/views/stats.dart'; +import 'package:provider/provider.dart'; + final List pageInfos = [ PageInfo( label: "记录", @@ -69,6 +73,7 @@ void buildBottomSheet(BuildContext context) { Widget buildBottomSheetBody(BuildContext context) { final colors = Theme.of(context).colorScheme; + final provider = context.watch(); return Column( mainAxisSize: MainAxisSize.min, @@ -91,6 +96,7 @@ Widget buildBottomSheetBody(BuildContext context) { leading: Icon(Icons.note_add, color: colors.primary), title: const Text('新增记录'), onTap: () { + provider.resetRecordForm(); Navigator.pop(context); Navigator.pushNamed(context, "/recordForm"); }, diff --git a/lib/utils/minio_utils.dart b/lib/utils/minio_utils.dart index 0f50c97..c5ad380 100644 --- a/lib/utils/minio_utils.dart +++ b/lib/utils/minio_utils.dart @@ -12,7 +12,7 @@ class MinIOHelper { final String rustfsIp = '14.103.235.151'; final String rustfsFileUrl = 'http://14.103.235.151:9100'; - final String bucketName = 'flisp'; + final String bucketName = 'food'; MinIOHelper._internal() { _minio = Minio( @@ -26,6 +26,8 @@ class MinIOHelper { late Minio _minio; + String get imageBaseUrl => '$rustfsFileUrl/$bucketName/'; + Future uploadFile({ required PlatformFile file, Function(double)? onProgress, diff --git a/lib/views/record_form.dart b/lib/views/record_form.dart index 4cd0359..6174e19 100644 --- a/lib/views/record_form.dart +++ b/lib/views/record_form.dart @@ -6,9 +6,7 @@ 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/image.dart'; import 'package:food_hub_app/widgets/common/index.dart'; -import 'package:food_hub_app/models/recipe.dart'; import 'package:intl/intl.dart'; -import 'dart:io'; import 'package:provider/provider.dart'; @@ -24,7 +22,6 @@ class _RecordFormPageState extends State { final _focusNode = FocusNode(); static const String _nameField = 'name'; static const String _dateField = 'date'; - File? imageFile; @override void dispose() { @@ -56,7 +53,7 @@ class _RecordFormPageState extends State { } Widget _buildFormBuilder() { - final foodProvider = context.watch(); + final provider = context.watch(); return FormBuilder( key: _formKey, @@ -66,101 +63,85 @@ class _RecordFormPageState extends State { children: [ buildFormLabel('菜谱名称', required: true), const SizedBox(height: 8), - _buildNameTextField(), + FormBuilderTextField( + name: _nameField, + focusNode: _focusNode, + onChanged: (value) { + setState(() { + provider.recordFormItem.name = value!; + }); + }, + decoration: buildInputDecoration( + context: context, + hintText: '请输入菜谱名称', + prefixIcon: const Icon( + Icons.title, + size: 20, + color: Color(0xFF86909C), + ), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return '请输入菜谱名称'; + } + return null; + }, + ), const SizedBox(height: 10), buildFormLabel('完成时间', required: true), const SizedBox(height: 8), - _buildDateTextField(), + FormBuilderTextField( + name: _dateField, + readOnly: true, + decoration: buildInputDecoration( + context: context, + hintText: '请选择完成时间', + prefixIcon: const Icon( + Icons.calendar_month, + size: 20, + color: Color(0xFF86909C), + ), + ), + onTap: () => _onSelectDate(provider), + validator: (value) { + if (value == null || value.isEmpty) { + return '请选择完成时间'; + } + return null; + }, + ), const SizedBox(height: 10), buildFormLabel('上传图片', required: true), const SizedBox(height: 10), - _buildImageUploadArea(), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (provider.recordFormItem.imageUrl.isEmpty) + buildImageUploadButton(onPickImage: () => _pickImage(provider)) + else + buildImagePreviewItem( + context: context, + imageUrl: + '${MinIOHelper().imageBaseUrl}${provider.recordFormItem.imageUrl}', + onRemoveImage: () => _removeImage(provider), + ), + ], + ), const SizedBox(height: 10), buildFormButtonGroup( context: context, - onConfirm: () => _submitForm(), + onConfirm: () => _submitForm(provider), ), ], ), ); } - /// 菜谱名称输入框 - Widget _buildNameTextField() { - return FormBuilderTextField( - name: _nameField, - focusNode: _focusNode, - decoration: buildInputDecoration( - context: context, - hintText: '请输入菜谱名称', - prefixIcon: const Icon(Icons.title, size: 20, color: Color(0xFF86909C)), - ), - validator: (value) { - if (value == null || value.isEmpty) { - return '请输入菜谱名称'; - } - return null; - }, - ); - } - - /// 完成时间选择框 - Widget _buildDateTextField() { - return FormBuilderTextField( - name: _dateField, - readOnly: true, - decoration: buildInputDecoration( - context: context, - hintText: '请选择完成时间', - prefixIcon: const Icon( - Icons.calendar_month, - size: 20, - color: Color(0xFF86909C), - ), - ), - onTap: () => _onSelectDate(), - validator: (value) { - if (value == null || value.isEmpty) { - return '请选择完成时间'; - } - return null; - }, - ); - } - - /// 图片上传区域(预览+上传按钮) - Widget _buildImageUploadArea() { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (imageFile != null) - _buildImagePreviewItem() - else - buildImageUploadButton(onPickImage: _pickImage), - ], - ); - } - - Widget _buildImagePreviewItem() { - return Container( - decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)), - child: ClipRRect( - borderRadius: BorderRadius.circular(8), - child: Stack( - children: [ - buildFileImage(context, imageFile!), - buildDeleteImage(onRemoveImage: _removeImage), - ], - ), - ), - ); - } - /// 选择日期 - Future _onSelectDate() async { + Future _onSelectDate(FoodProvider provider) async { final DateTime? picked = await showDatePicker( context: context, initialDate: DateTime.now(), @@ -169,14 +150,16 @@ class _RecordFormPageState extends State { ); if (picked != null) { - _formKey.currentState?.fields[_dateField]?.didChange( - DateFormat('yyyy-MM-dd').format(picked), - ); + final String date = DateFormat('yyyy-MM-dd').format(picked); + _formKey.currentState?.fields[_dateField]?.didChange(date); + setState(() { + provider.recordFormItem.date = date; + }); } } /// 选择图片(限制单张) - Future _pickImage() async { + Future _pickImage(FoodProvider provider) async { FilePickerResult? result = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: ['jpg', 'jpeg', 'png'], @@ -187,22 +170,22 @@ class _RecordFormPageState extends State { PlatformFile file = result.files.first; final fileName = await MinIOHelper().uploadFile(file: file); setState(() { - // imageFile = File(image.path); + provider.recordFormItem.imageUrl = fileName; }); } } /// 移除图片 - void _removeImage() { + void _removeImage(FoodProvider provider) { setState(() { - imageFile = null; + provider.recordFormItem.imageUrl = ''; }); } /// 提交表单 - void _submitForm() { + void _submitForm(FoodProvider provider) { if (_formKey.currentState?.saveAndValidate() ?? false) { - if (imageFile == null) { + if (provider.recordFormItem.imageUrl.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('请上传图片'), @@ -213,18 +196,7 @@ class _RecordFormPageState extends State { return; } - final formData = { - ..._formKey.currentState!.value, - 'imageUrl': imageFile?.path, - }; - - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('提交成功!'), - behavior: SnackBarBehavior.floating, - ), - ); - print('表单数据: $formData'); + print(provider.recordFormItem.imageUrl); } } } diff --git a/lib/widgets/common/image.dart b/lib/widgets/common/image.dart index 0b21140..d13cdc1 100644 --- a/lib/widgets/common/image.dart +++ b/lib/widgets/common/image.dart @@ -126,6 +126,25 @@ Widget buildNetworkImage(BuildContext context, String url) { ); } +Widget buildImagePreviewItem({ + required BuildContext context, + required String imageUrl, + required VoidCallback onRemoveImage, +}) { + return Container( + decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)), + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: Stack( + children: [ + buildNetworkImage(context, imageUrl), + buildDeleteImage(onRemoveImage: onRemoveImage), + ], + ), + ), + ); +} + Widget buildImageLoadingIndicator(ImageChunkEvent? loadingProgress) { return Center( child: SizedBox(