feat:更新美食记录表单
This commit is contained in:
@@ -1,245 +0,0 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:food_hub_app/config/app_config.dart';
|
||||
import 'package:food_hub_app/utils/sp_util.dart';
|
||||
import 'package:food_hub_app/widgets/common/index.dart';
|
||||
|
||||
import 'log_util.dart';
|
||||
|
||||
class HttpUtil {
|
||||
static final HttpUtil _instance = HttpUtil._internal();
|
||||
|
||||
factory HttpUtil() => _instance;
|
||||
|
||||
late Dio _dio;
|
||||
|
||||
// 请求头配置
|
||||
Map<String, dynamic> headers = {
|
||||
'Content-Type': 'application/json;charset=UTF-8',
|
||||
'Accept': 'application/json',
|
||||
};
|
||||
|
||||
// 超时时间
|
||||
final int timeout = 5;
|
||||
|
||||
HttpUtil._internal() {
|
||||
// 初始化Dio实例
|
||||
BaseOptions options = BaseOptions(
|
||||
baseUrl: AppConfig.baseApiUrl,
|
||||
connectTimeout: Duration(seconds: timeout),
|
||||
receiveTimeout: Duration(seconds: timeout),
|
||||
headers: headers,
|
||||
);
|
||||
|
||||
_dio = Dio(options);
|
||||
|
||||
// 添加请求拦截器
|
||||
_dio.interceptors.add(
|
||||
InterceptorsWrapper(
|
||||
onRequest: (options, handler) {
|
||||
logger.d("请求URL: ${options.uri}");
|
||||
if (options.data != null) {
|
||||
logger.d("请求参数: ${options.data}");
|
||||
}
|
||||
|
||||
if (SPUtil.getString('token').isNotEmpty) {
|
||||
options.headers['satoken'] = SPUtil.getString('token');
|
||||
}
|
||||
return handler.next(options);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// 添加响应拦截器
|
||||
_dio.interceptors.add(
|
||||
InterceptorsWrapper(
|
||||
onResponse: (response, handler) {
|
||||
logger.d("响应状态码: ${response.statusCode}");
|
||||
// logger.d("响应数据: ${response.data}");
|
||||
return handler.next(response);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// 添加错误拦截器
|
||||
_dio.interceptors.add(
|
||||
InterceptorsWrapper(
|
||||
onError: (DioException e, handler) {
|
||||
_handleError(e);
|
||||
return handler.next(e);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 基础请求方法(处理所有类型的请求)
|
||||
Future<T> _request<T>(
|
||||
String path, {
|
||||
required String method,
|
||||
dynamic data,
|
||||
Map<String, dynamic>? queryParameters,
|
||||
T Function(dynamic data)? converter,
|
||||
}) async {
|
||||
try {
|
||||
Response response = await _dio.request(
|
||||
path,
|
||||
data: data,
|
||||
queryParameters: queryParameters,
|
||||
options: Options(method: method),
|
||||
);
|
||||
|
||||
// 处理响应数据
|
||||
if (converter != null) {
|
||||
return converter(response.data);
|
||||
}
|
||||
|
||||
// 没有转换器时尝试直接返回(可能不安全,建议提供转换器)
|
||||
return response.data;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// GET请求
|
||||
Future<T> get<T>(
|
||||
String path, {
|
||||
Map<String, dynamic>? queryParameters,
|
||||
T Function(dynamic data)? converter,
|
||||
}) => _request(
|
||||
path,
|
||||
method: "GET",
|
||||
queryParameters: queryParameters,
|
||||
converter: converter,
|
||||
);
|
||||
|
||||
// POST请求
|
||||
Future<T> post<T>(
|
||||
String path, {
|
||||
dynamic data,
|
||||
Map<String, dynamic>? queryParameters,
|
||||
T Function(dynamic data)? converter,
|
||||
}) => _request(
|
||||
path,
|
||||
method: "POST",
|
||||
data: data,
|
||||
queryParameters: queryParameters,
|
||||
converter: converter,
|
||||
);
|
||||
|
||||
// PUT请求
|
||||
Future<T> put<T>(
|
||||
String path, {
|
||||
dynamic data,
|
||||
Map<String, dynamic>? queryParameters,
|
||||
T Function(dynamic data)? converter,
|
||||
}) => _request(
|
||||
path,
|
||||
method: "PUT",
|
||||
data: data,
|
||||
queryParameters: queryParameters,
|
||||
converter: converter,
|
||||
);
|
||||
|
||||
// DELETE请求
|
||||
Future<T> delete<T>(
|
||||
String path, {
|
||||
dynamic data,
|
||||
Map<String, dynamic>? queryParameters,
|
||||
T Function(dynamic data)? converter,
|
||||
}) => _request(
|
||||
path,
|
||||
method: "DELETE",
|
||||
data: data,
|
||||
queryParameters: queryParameters,
|
||||
converter: converter,
|
||||
);
|
||||
|
||||
// 文件上传
|
||||
Future<dynamic> upload(
|
||||
String path,
|
||||
String filePath, {
|
||||
Map<String, dynamic>? queryParameters,
|
||||
}) async {
|
||||
try {
|
||||
FormData formData = FormData.fromMap({
|
||||
'file': await MultipartFile.fromFile(filePath),
|
||||
});
|
||||
|
||||
Response response = await _dio.post(
|
||||
path,
|
||||
data: formData,
|
||||
queryParameters: queryParameters,
|
||||
);
|
||||
return response.data;
|
||||
} catch (e) {
|
||||
_handleError(e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// 错误处理
|
||||
void _handleError(dynamic error) {
|
||||
String errorMessage = '未知错误';
|
||||
if (error is DioException) {
|
||||
switch (error.type) {
|
||||
case DioExceptionType.connectionTimeout:
|
||||
errorMessage = '连接超时,请检查网络连接';
|
||||
break;
|
||||
case DioExceptionType.sendTimeout:
|
||||
errorMessage = '发送超时,请检查网络连接';
|
||||
break;
|
||||
case DioExceptionType.receiveTimeout:
|
||||
errorMessage = '接收超时,请检查网络连接';
|
||||
break;
|
||||
case DioExceptionType.cancel:
|
||||
errorMessage = '请求已取消';
|
||||
break;
|
||||
case DioExceptionType.badCertificate:
|
||||
errorMessage = '证书验证失败';
|
||||
break;
|
||||
case DioExceptionType.badResponse:
|
||||
// 处理HTTP响应错误(4xx, 5xx)
|
||||
final statusCode = error.response?.statusCode ?? 0;
|
||||
final responseData = error.response?.data;
|
||||
|
||||
if (responseData is Map<String, dynamic> && responseData.containsKey('message')) {
|
||||
// 服务器返回了自定义错误消息
|
||||
errorMessage = responseData['message']?.toString() ?? '未知错误';
|
||||
} else {
|
||||
errorMessage = _getHttpErrorMessage(statusCode);
|
||||
}
|
||||
break;
|
||||
case DioExceptionType.connectionError:
|
||||
errorMessage = '网络连接错误,请检查网络设置';
|
||||
break;
|
||||
case DioExceptionType.unknown:
|
||||
if (error.error != null) {
|
||||
errorMessage = '未知错误: ${error.error.toString()}';
|
||||
}
|
||||
errorMessage = '发生未知错误';
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
errorMessage = '非Dio错误: $error';
|
||||
}
|
||||
|
||||
print(errorMessage);
|
||||
showErrorToast(errorMessage);
|
||||
}
|
||||
|
||||
String _getHttpErrorMessage(int statusCode) {
|
||||
// 根据HTTP状态码返回对应的错误消息
|
||||
switch (statusCode) {
|
||||
case 400: return '错误请求,请检查参数';
|
||||
case 401: return '未授权,请登录';
|
||||
case 403: return '禁止访问,权限不足';
|
||||
case 404: return '资源不存在';
|
||||
case 405: return '方法不允许';
|
||||
case 408: return '请求超时';
|
||||
case 500: return '服务器内部错误';
|
||||
case 502: return '网关错误';
|
||||
case 503: return '服务不可用';
|
||||
case 504: return '网关超时';
|
||||
default: return 'HTTP错误: $statusCode';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,18 +16,7 @@ String formatDateTime(DateTime dateTime, [String format = 'yyyy-MM-dd']) {
|
||||
return Intl.withLocale('zh_CN', () => formatter.format(dateTime));
|
||||
}
|
||||
|
||||
/// 通用列表转换函数
|
||||
List<T> convertListResponse<T>(
|
||||
dynamic data,
|
||||
T Function(Map<String, dynamic>) fromJson,
|
||||
) {
|
||||
if (data is List) {
|
||||
return data.map((item) => fromJson(item as Map<String, dynamic>)).toList();
|
||||
}
|
||||
throw FormatException(
|
||||
'Expected a list of items for conversion, but got ${data.runtimeType}',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
List<String> convertListStringResponse(dynamic data) {
|
||||
if (data is List) {
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import 'package:logger/logger.dart';
|
||||
|
||||
// 全局日志实例,在整个项目中共享
|
||||
final Logger logger = Logger(
|
||||
printer: PrettyPrinter(
|
||||
methodCount: 1,
|
||||
colors: true,
|
||||
dateTimeFormat: DateTimeFormat.dateAndTime,
|
||||
),
|
||||
// 可选:配置输出到文件(需配合文件操作库)
|
||||
// output: FileOutput(file: File('logs/app.log')),
|
||||
);
|
||||
@@ -1,54 +0,0 @@
|
||||
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';
|
||||
|
||||
class MinIOHelper {
|
||||
static final MinIOHelper _instance = MinIOHelper._internal();
|
||||
|
||||
factory MinIOHelper() => _instance;
|
||||
|
||||
MinIOHelper._internal() {
|
||||
_minio = Minio(
|
||||
endPoint: AppConfig.rustfsIp,
|
||||
port: 9100,
|
||||
accessKey: "tHSFfcDW8qpCzKa2Xg6Y",
|
||||
secretKey: "oq79EeYJ4jdczRp2IHUMCnbKtSw58NgDlG3sOkvX",
|
||||
useSSL: false,
|
||||
);
|
||||
}
|
||||
|
||||
late Minio _minio;
|
||||
|
||||
Future<String> uploadFile({
|
||||
required PlatformFile file,
|
||||
Function(double)? onProgress,
|
||||
}) async {
|
||||
try {
|
||||
String hashName = await _generateMD5HashName(file.path!);
|
||||
String fileName = '$hashName${_getFileExtension(file.name)}';
|
||||
|
||||
await _minio.fPutObject(AppConfig.bucketName, fileName, file.path!);
|
||||
return fileName;
|
||||
} catch (e) {
|
||||
throw Exception('文件上传失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
String _getFileExtension(String fileName) {
|
||||
if (fileName.contains('.')) {
|
||||
return '.${fileName.split('.').last.toLowerCase()}';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
Future<String> _generateMD5HashName(String filePath) async {
|
||||
final file = File(filePath);
|
||||
final bytes = await file.readAsBytes();
|
||||
final hash = md5.convert(bytes);
|
||||
return hash.toString();
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import 'dart:typed_data';
|
||||
import 'dart:ui' as ui;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:food_hub_app/widgets/common/index.dart';
|
||||
import 'package:flutter_common/utils/toast_util.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
@@ -115,10 +115,10 @@ class ScreenshotUtil {
|
||||
// 分享后删除临时文件
|
||||
await imageFile.delete();
|
||||
} else {
|
||||
showErrorToast('生成图片失败,请重试');
|
||||
ToastUtil.error('生成图片失败,请重试');
|
||||
}
|
||||
} catch (e) {
|
||||
showErrorToast('分享失败: $e');
|
||||
ToastUtil.error('分享失败: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class SPUtil {
|
||||
static late SharedPreferences _prefs;
|
||||
|
||||
/// 初始化
|
||||
static Future<void> init() async {
|
||||
_prefs = await SharedPreferences.getInstance();
|
||||
}
|
||||
|
||||
/// 存储数据
|
||||
static Future<bool> set(String key, dynamic value) {
|
||||
if (value is String) return _prefs.setString(key, value);
|
||||
if (value is int) return _prefs.setInt(key, value);
|
||||
if (value is bool) return _prefs.setBool(key, value);
|
||||
if (value is double) return _prefs.setDouble(key, value);
|
||||
if (value is List<String>) return _prefs.setStringList(key, value);
|
||||
throw ArgumentError('Unsupported value type: ${value.runtimeType}');
|
||||
}
|
||||
|
||||
/// 获取数据
|
||||
static dynamic get(String key, [dynamic defaultValue]) {
|
||||
if (!_prefs.containsKey(key)) return defaultValue;
|
||||
return _prefs.get(key) ?? defaultValue;
|
||||
}
|
||||
|
||||
/// 获取字符串
|
||||
static String getString(String key, [String defaultValue = '']) {
|
||||
return _prefs.getString(key) ?? defaultValue;
|
||||
}
|
||||
|
||||
/// 获取布尔值
|
||||
static bool getBool(String key, [bool defaultValue = false]) {
|
||||
return _prefs.getBool(key) ?? defaultValue;
|
||||
}
|
||||
|
||||
/// 获取整数
|
||||
static int getInt(String key, [int defaultValue = 0]) {
|
||||
return _prefs.getInt(key) ?? defaultValue;
|
||||
}
|
||||
|
||||
/// 获取浮点数
|
||||
static double getDouble(String key, [double defaultValue = 0.0]) {
|
||||
return _prefs.getDouble(key) ?? defaultValue;
|
||||
}
|
||||
|
||||
/// 获取字符串列表
|
||||
static List<String> getStringList(String key, [List<String> defaultValue = const []]) {
|
||||
return _prefs.getStringList(key) ?? defaultValue;
|
||||
}
|
||||
|
||||
/// 删除数据
|
||||
static Future<bool> remove(String key) {
|
||||
return _prefs.remove(key);
|
||||
}
|
||||
|
||||
/// 清空所有数据
|
||||
static Future<bool> clear() {
|
||||
return _prefs.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ThemeColor {
|
||||
final String name;
|
||||
final Color primaryColor;
|
||||
final MaterialColor materialColor;
|
||||
|
||||
ThemeColor({
|
||||
required this.name,
|
||||
required this.primaryColor,
|
||||
required this.materialColor,
|
||||
});
|
||||
}
|
||||
|
||||
final List<ThemeColor> defaultThemes = [
|
||||
ThemeColor(
|
||||
name: '梦幻紫',
|
||||
primaryColor: Color(0xFF8B5CF6),
|
||||
materialColor: MaterialColor(0xFF8B5CF6, {
|
||||
50: Color(0xFFF5F3FF),
|
||||
100: Color(0xFFEDE9FE),
|
||||
200: Color(0xFFDDD6FE),
|
||||
300: Color(0xFFC4B5FD),
|
||||
400: Color(0xFFA78BFA),
|
||||
500: Color(0xFF8B5CF6),
|
||||
600: Color(0xFF7C3AED),
|
||||
700: Color(0xFF6D28D9),
|
||||
800: Color(0xFF5B21B6),
|
||||
900: Color(0xFF4C1D95),
|
||||
}),
|
||||
),
|
||||
ThemeColor(
|
||||
name: '活力橙',
|
||||
primaryColor: Color(0xFFF59E0B),
|
||||
materialColor: MaterialColor(0xFFF59E0B, {
|
||||
50: Color(0xFFFFFBEB),
|
||||
100: Color(0xFFFEF3C7),
|
||||
200: Color(0xFFFDE68A),
|
||||
300: Color(0xFFFCD34D),
|
||||
400: Color(0xFFFBBF24),
|
||||
500: Color(0xFFF59E0B),
|
||||
600: Color(0xFFD97706),
|
||||
700: Color(0xFFB45309),
|
||||
800: Color(0xFF92400E),
|
||||
900: Color(0xFF78350F),
|
||||
}),
|
||||
),
|
||||
ThemeColor(
|
||||
name: '浪漫粉',
|
||||
primaryColor: Color(0xFFEC4899),
|
||||
materialColor: MaterialColor(0xFFEC4899, {
|
||||
50: Color(0xFFFDF2F8),
|
||||
100: Color(0xFFFCE7F3),
|
||||
200: Color(0xFFFBCFE8),
|
||||
300: Color(0xFFF9A8D4),
|
||||
400: Color(0xFFF472B6),
|
||||
500: Color(0xFFEC4899),
|
||||
600: Color(0xFFDB2777),
|
||||
700: Color(0xFFBE185D),
|
||||
800: Color(0xFF9D174D),
|
||||
900: Color(0xFF831843),
|
||||
}),
|
||||
),
|
||||
ThemeColor(
|
||||
name: '清新青',
|
||||
primaryColor: Color(0xFF06B6D4),
|
||||
materialColor: MaterialColor(0xFF06B6D4, {
|
||||
50: Color(0xFFF0FDFA),
|
||||
100: Color(0xFFCCFBF1),
|
||||
200: Color(0xFF99F6E4),
|
||||
300: Color(0xFF5EEAD4),
|
||||
400: Color(0xFF2DD4BF),
|
||||
500: Color(0xFF06B6D4),
|
||||
600: Color(0xFF0891B2),
|
||||
700: Color(0xFF0E7490),
|
||||
800: Color(0xFF155E75),
|
||||
900: Color(0xFF164E63),
|
||||
}),
|
||||
),
|
||||
];
|
||||
|
||||
ThemeColor getThemeColor(String themeName) {
|
||||
return defaultThemes.firstWhere(
|
||||
(theme) => theme.name == themeName,
|
||||
orElse: () => defaultThemes[0],
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user