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:
159
flutter_monisuo/lib/providers/asset_provider.dart
Normal file
159
flutter_monisuo/lib/providers/asset_provider.dart
Normal file
@@ -0,0 +1,159 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../data/models/account_models.dart';
|
||||
import '../data/services/asset_service.dart';
|
||||
import '../data/services/fund_service.dart';
|
||||
import '../core/network/dio_client.dart';
|
||||
|
||||
/// 资产状态管理
|
||||
class AssetProvider extends ChangeNotifier {
|
||||
final AssetService _assetService;
|
||||
final FundService _fundService;
|
||||
|
||||
AssetOverview? _overview;
|
||||
AccountFund? _fundAccount;
|
||||
List<AccountTrade> _tradeAccounts = [];
|
||||
List<AccountFlow> _flows = [];
|
||||
bool _isLoading = false;
|
||||
bool _isLoadingFlows = false;
|
||||
String? _error;
|
||||
|
||||
AssetProvider(this._assetService, this._fundService);
|
||||
|
||||
// Getters
|
||||
AssetOverview? get overview => _overview;
|
||||
AccountFund? get fundAccount => _fundAccount;
|
||||
List<AccountTrade> get tradeAccounts => _tradeAccounts;
|
||||
List<AccountTrade> get holdings => _tradeAccounts;
|
||||
List<AccountFlow> get flows => _flows;
|
||||
bool get isLoading => _isLoading;
|
||||
bool get isLoadingFlows => _isLoadingFlows;
|
||||
String? get error => _error;
|
||||
|
||||
/// 加载资产总览
|
||||
Future<void> loadOverview() async {
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final response = await _assetService.getOverview();
|
||||
if (response.success) {
|
||||
_overview = response.data;
|
||||
} else {
|
||||
_error = response.message;
|
||||
}
|
||||
} catch (e) {
|
||||
_error = '加载失败: $e';
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 加载资金账户
|
||||
Future<void> loadFundAccount() async {
|
||||
try {
|
||||
final response = await _assetService.getFundAccount();
|
||||
if (response.success) {
|
||||
_fundAccount = response.data;
|
||||
notifyListeners();
|
||||
}
|
||||
} catch (_) {
|
||||
// 忽略错误
|
||||
}
|
||||
}
|
||||
|
||||
/// 加载交易账户
|
||||
Future<void> loadTradeAccount() async {
|
||||
try {
|
||||
final response = await _assetService.getTradeAccount();
|
||||
if (response.success) {
|
||||
_tradeAccounts = response.data ?? [];
|
||||
notifyListeners();
|
||||
}
|
||||
} catch (_) {
|
||||
// 忽略错误
|
||||
}
|
||||
}
|
||||
|
||||
/// 加载资金流水
|
||||
Future<void> loadFlows({int? flowType, int pageNum = 1}) async {
|
||||
_isLoadingFlows = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final response = await _assetService.getFlow(
|
||||
flowType: flowType,
|
||||
pageNum: pageNum,
|
||||
);
|
||||
if (response.success && response.data != null) {
|
||||
final list = response.data!['list'] as List?;
|
||||
_flows = list?.map((e) => AccountFlow.fromJson(e as Map<String, dynamic>)).toList() ?? [];
|
||||
}
|
||||
} catch (_) {
|
||||
// 忽略错误
|
||||
}
|
||||
|
||||
_isLoadingFlows = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 划转资金
|
||||
Future<ApiResponse<void>> transfer({
|
||||
required int direction,
|
||||
required String amount,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _assetService.transfer(
|
||||
direction: direction,
|
||||
amount: amount,
|
||||
);
|
||||
if (response.success) {
|
||||
// 刷新数据
|
||||
await loadOverview();
|
||||
await loadFundAccount();
|
||||
await loadTradeAccount();
|
||||
}
|
||||
return response;
|
||||
} catch (e) {
|
||||
return ApiResponse.fail('划转失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 充值
|
||||
Future<ApiResponse<void>> deposit({required String amount, String? remark}) async {
|
||||
try {
|
||||
final response = await _fundService.deposit(amount: amount, remark: remark);
|
||||
if (response.success) {
|
||||
await loadOverview();
|
||||
await loadFundAccount();
|
||||
}
|
||||
return response;
|
||||
} catch (e) {
|
||||
return ApiResponse.fail('充值申请失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 提现
|
||||
Future<ApiResponse<void>> withdraw({required String amount, String? remark}) async {
|
||||
try {
|
||||
final response = await _fundService.withdraw(amount: amount, remark: remark);
|
||||
if (response.success) {
|
||||
await loadOverview();
|
||||
await loadFundAccount();
|
||||
}
|
||||
return response;
|
||||
} catch (e) {
|
||||
return ApiResponse.fail('提现申请失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 刷新所有资产数据
|
||||
Future<void> refreshAll() async {
|
||||
await Future.wait([
|
||||
loadOverview(),
|
||||
loadFundAccount(),
|
||||
loadTradeAccount(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
151
flutter_monisuo/lib/providers/auth_provider.dart
Normal file
151
flutter_monisuo/lib/providers/auth_provider.dart
Normal file
@@ -0,0 +1,151 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../core/network/dio_client.dart';
|
||||
import '../core/storage/local_storage.dart';
|
||||
import '../data/models/user.dart';
|
||||
import '../data/services/user_service.dart';
|
||||
|
||||
/// 认证状态管理
|
||||
class AuthProvider extends ChangeNotifier {
|
||||
final UserService _userService;
|
||||
|
||||
User? _user;
|
||||
bool _isLoggedIn = false;
|
||||
bool _isLoading = false;
|
||||
String? _token;
|
||||
|
||||
AuthProvider(this._userService) {
|
||||
_checkAuth();
|
||||
}
|
||||
|
||||
// Getters
|
||||
User? get user => _user;
|
||||
bool get isLoggedIn => _isLoggedIn;
|
||||
bool get isLoading => _isLoading;
|
||||
String? get token => _token;
|
||||
|
||||
/// 检查登录状态
|
||||
Future<void> _checkAuth() async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
_token = LocalStorage.getToken();
|
||||
_isLoggedIn = _token != null && _token!.isNotEmpty;
|
||||
|
||||
if (_isLoggedIn) {
|
||||
final userJson = LocalStorage.getUserInfo();
|
||||
if (userJson != null) {
|
||||
_user = User.fromJson(userJson);
|
||||
}
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 登录
|
||||
Future<ApiResponse<User>> login(String username, String password) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final response = await _userService.login(username, password);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_token = response.data!['token'] as String?;
|
||||
final userJson = response.data!['user'] as Map<String, dynamic>? ??
|
||||
response.data!['userInfo'] as Map<String, dynamic>?;
|
||||
|
||||
if (_token != null) {
|
||||
await LocalStorage.saveToken(_token!);
|
||||
}
|
||||
if (userJson != null) {
|
||||
await LocalStorage.saveUserInfo(userJson);
|
||||
_user = User.fromJson(userJson);
|
||||
}
|
||||
|
||||
_isLoggedIn = true;
|
||||
notifyListeners();
|
||||
return ApiResponse.success(_user!, response.message);
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
return ApiResponse.fail(response.message ?? '登录失败');
|
||||
} catch (e) {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
return ApiResponse.fail('登录失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 注册
|
||||
Future<ApiResponse<User>> register(String username, String password) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final response = await _userService.register(username, password);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_token = response.data!['token'] as String?;
|
||||
final userJson = response.data!['userInfo'] as Map<String, dynamic>?;
|
||||
|
||||
if (_token != null) {
|
||||
await LocalStorage.saveToken(_token!);
|
||||
}
|
||||
if (userJson != null) {
|
||||
await LocalStorage.saveUserInfo(userJson);
|
||||
_user = User.fromJson(userJson);
|
||||
}
|
||||
|
||||
_isLoggedIn = true;
|
||||
notifyListeners();
|
||||
return ApiResponse.success(_user!, response.message);
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
return ApiResponse.fail(response.message ?? '注册失败');
|
||||
} catch (e) {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
return ApiResponse.fail('注册失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 退出登录
|
||||
Future<void> logout() async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
await _userService.logout();
|
||||
} catch (_) {
|
||||
// 忽略退出登录的接口错误
|
||||
}
|
||||
|
||||
await LocalStorage.clearUserData();
|
||||
_user = null;
|
||||
_token = null;
|
||||
_isLoggedIn = false;
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 刷新用户信息
|
||||
Future<void> refreshUserInfo() async {
|
||||
if (!_isLoggedIn) return;
|
||||
|
||||
try {
|
||||
final response = await _userService.getUserInfo();
|
||||
if (response.success && response.data != null) {
|
||||
_user = response.data;
|
||||
await LocalStorage.saveUserInfo(_user!.toJson());
|
||||
notifyListeners();
|
||||
}
|
||||
} catch (_) {
|
||||
// 忽略错误
|
||||
}
|
||||
}
|
||||
}
|
||||
105
flutter_monisuo/lib/providers/market_provider.dart
Normal file
105
flutter_monisuo/lib/providers/market_provider.dart
Normal file
@@ -0,0 +1,105 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../data/models/coin.dart';
|
||||
import '../data/services/market_service.dart';
|
||||
|
||||
/// 行情状态管理
|
||||
class MarketProvider extends ChangeNotifier {
|
||||
final MarketService _marketService;
|
||||
|
||||
List<Coin> _allCoins = [];
|
||||
List<Coin> _filteredCoins = [];
|
||||
String _activeTab = 'all';
|
||||
String _searchKeyword = '';
|
||||
bool _isLoading = false;
|
||||
String? _error;
|
||||
|
||||
MarketProvider(this._marketService);
|
||||
|
||||
// Getters
|
||||
List<Coin> get coins => _filteredCoins;
|
||||
List<Coin> get allCoins => _allCoins;
|
||||
bool get isLoading => _isLoading;
|
||||
String? get error => _error;
|
||||
String get activeTab => _activeTab;
|
||||
String get searchKeyword => _searchKeyword;
|
||||
|
||||
/// 加载币种列表
|
||||
Future<void> loadCoins() async {
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final response = await _marketService.getCoinList();
|
||||
|
||||
if (response.success) {
|
||||
_allCoins = response.data ?? [];
|
||||
_filterCoins();
|
||||
} else {
|
||||
_error = response.message;
|
||||
}
|
||||
} catch (e) {
|
||||
_error = '加载失败: $e';
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 设置分类标签
|
||||
void setTab(String tab) {
|
||||
_activeTab = tab;
|
||||
_filterCoins();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 搜索
|
||||
void search(String keyword) {
|
||||
_searchKeyword = keyword;
|
||||
_filterCoins();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 清除搜索
|
||||
void clearSearch() {
|
||||
_searchKeyword = '';
|
||||
_filterCoins();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 筛选币种
|
||||
void _filterCoins() {
|
||||
List<Coin> result = List.from(_allCoins);
|
||||
|
||||
// 按分类筛选
|
||||
if (_activeTab == 'realtime') {
|
||||
result = result.where((c) => c.isRealtime).toList();
|
||||
} else if (_activeTab == 'hot') {
|
||||
result = result.take(6).toList();
|
||||
}
|
||||
|
||||
// 按关键词筛选
|
||||
if (_searchKeyword.isNotEmpty) {
|
||||
final kw = _searchKeyword.toLowerCase();
|
||||
result = result.where((c) =>
|
||||
c.code.toLowerCase().contains(kw) ||
|
||||
c.name.toLowerCase().contains(kw)).toList();
|
||||
}
|
||||
|
||||
_filteredCoins = result;
|
||||
}
|
||||
|
||||
/// 根据代码获取币种
|
||||
Coin? getCoinByCode(String code) {
|
||||
try {
|
||||
return _allCoins.firstWhere((c) => c.code == code);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// 刷新
|
||||
Future<void> refresh() async {
|
||||
await loadCoins();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user