refactor: 将前端从 uni-app x 重构为 Flutter

变更内容:
- 删除 uni-app x 项目 (app/ 目录)
- 新增 Flutter 项目 (flutter_monisuo/ 目录)
- 新增部署脚本 (deploy/ 目录)

Flutter 项目功能:
- 用户登录/注册
- 首页资产概览
- 行情币种列表
- 交易买卖操作
- 资产账户管理
- 充值/提现/划转
- 深色主题
- JWT Token 认证

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
sion
2026-03-22 00:21:21 +08:00
parent 7694a34ade
commit ffac6fc267
67 changed files with 4396 additions and 3097 deletions

View File

@@ -0,0 +1,155 @@
import 'package:dio/dio.dart';
import '../storage/local_storage.dart';
import 'api_exception.dart';
/// API 响应模型
class ApiResponse<T> {
final bool success;
final String? message;
final T? data;
final String? code;
ApiResponse({
required this.success,
this.message,
this.data,
this.code,
});
factory ApiResponse.success(T data, [String? message]) {
return ApiResponse(
success: true,
data: data,
message: message,
code: '0000',
);
}
factory ApiResponse.fail(String message, [String? code]) {
return ApiResponse(
success: false,
message: message,
code: code,
);
}
factory ApiResponse.unauthorized(String message) {
return ApiResponse(
success: false,
message: message,
code: '0002',
);
}
factory ApiResponse.fromJson(
Map<String, dynamic> json,
T Function(dynamic)? fromJsonT,
) {
final code = json['code'] as String? ?? '';
final msg = json['msg'] as String? ?? '';
if (code == '0000') {
final data = json['data'];
if (fromJsonT != null && data != null) {
return ApiResponse.success(fromJsonT(data), msg);
}
return ApiResponse.success(data as T?, msg);
} else if (code == '0002') {
return ApiResponse.unauthorized(msg);
} else {
return ApiResponse.fail(msg, code);
}
}
}
/// Dio 网络客户端
class DioClient {
late final Dio _dio;
DioClient() {
_dio = Dio(BaseOptions(
baseUrl: 'http://8.155.172.147:5010',
connectTimeout: const Duration(seconds: 30),
receiveTimeout: const Duration(seconds: 30),
headers: {
'Content-Type': 'application/json',
},
));
_dio.interceptors.add(_AuthInterceptor());
_dio.interceptors.add(LogInterceptor(
requestHeader: false,
responseHeader: false,
error: true,
));
}
/// GET 请求
Future<ApiResponse<T>> get<T>(
String path, {
Map<String, dynamic>? queryParameters,
T Function(dynamic)? fromJson,
}) async {
try {
final response = await _dio.get(path, queryParameters: queryParameters);
return _handleResponse<T>(response, fromJson);
} on DioException catch (e) {
return _handleError<T>(e);
}
}
/// POST 请求
Future<ApiResponse<T>> post<T>(
String path, {
dynamic data,
T Function(dynamic)? fromJson,
}) async {
try {
final response = await _dio.post(path, data: data);
return _handleResponse<T>(response, fromJson);
} on DioException catch (e) {
return _handleError<T>(e);
}
}
/// 处理响应
ApiResponse<T> _handleResponse<T>(
Response response,
T Function(dynamic)? fromJson,
) {
final data = response.data;
if (data is Map<String, dynamic>) {
return ApiResponse.fromJson(data, fromJson);
}
return ApiResponse.fail('响应数据格式错误');
}
/// 处理错误
ApiResponse<T> _handleError<T>(DioException e) {
if (e.response?.statusCode == 401) {
LocalStorage.clearUserData();
return ApiResponse.unauthorized('登录已过期,请重新登录');
}
return ApiResponse.fail(e.message ?? '网络请求失败');
}
}
/// 认证拦截器
class _AuthInterceptor extends Interceptor {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
final token = LocalStorage.getToken();
if (token != null && token.isNotEmpty) {
options.headers['Authorization'] = 'Bearer $token';
}
super.onRequest(options, handler);
}
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
if (err.response?.statusCode == 401) {
LocalStorage.clearUserData();
}
super.onError(err, handler);
}
}