111
This commit is contained in:
@@ -13,7 +13,7 @@ import 'components/records_link_row.dart';
|
||||
import '../orders/fund_orders_page.dart';
|
||||
import 'transfer_page.dart';
|
||||
|
||||
/// 资产页面 - Matching .pen design spec (CMcqs)
|
||||
/// 資產頁面 - Matching .pen design spec (CMcqs)
|
||||
class AssetPage extends StatefulWidget {
|
||||
const AssetPage({super.key});
|
||||
|
||||
@@ -78,24 +78,24 @@ class _AssetPageState extends State<AssetPage> with AutomaticKeepAliveClientMixi
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16, bottom: 8),
|
||||
child: Text(
|
||||
'资产',
|
||||
'資產',
|
||||
style: AppTextStyles.displaySmall(context),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
// 资金账户 + 交易账户 左右并排
|
||||
// 資金賬戶 + 交易賬戶 左右並排
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: BalanceCard(
|
||||
label: '资金账户',
|
||||
label: '資金賬戶',
|
||||
balance: provider.fundAccount?.balance ?? provider.overview?.fundBalance ?? '0.00',
|
||||
),
|
||||
),
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
Expanded(
|
||||
child: BalanceCard(
|
||||
label: '交易账户',
|
||||
label: '交易賬戶',
|
||||
balance: _calculateTradeTotal(provider),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -3,7 +3,7 @@ import '../../../../core/theme/app_theme.dart';
|
||||
import '../../../../core/theme/app_theme_extension.dart';
|
||||
import '../../../../core/theme/app_spacing.dart';
|
||||
|
||||
/// 账户标签切换器 — .pen node UE6xC
|
||||
/// 賬戶標籤切換器 — .pen node UE6xC
|
||||
/// height: 40, padding: 3, cornerRadius: md, fill: $bg-tertiary
|
||||
/// activeTab: fill $bg-primary, cornerRadius sm, shadow blur 3, color #0000000D, offset y 1
|
||||
/// activeTabText: 14px, fontWeight 600, fill $text-primary
|
||||
@@ -31,13 +31,13 @@ class AccountTabSwitcher extends StatelessWidget {
|
||||
children: [
|
||||
_buildTab(
|
||||
context: context,
|
||||
label: '资金账户',
|
||||
label: '資金賬戶',
|
||||
isSelected: selectedIndex == 0,
|
||||
onTap: () => onChanged(0),
|
||||
),
|
||||
_buildTab(
|
||||
context: context,
|
||||
label: '交易账户',
|
||||
label: '交易賬戶',
|
||||
isSelected: selectedIndex == 1,
|
||||
onTap: () => onChanged(1),
|
||||
),
|
||||
|
||||
@@ -4,7 +4,7 @@ import '../../../../core/theme/app_spacing.dart';
|
||||
import '../../../../core/theme/app_theme.dart';
|
||||
import '../../../../core/theme/app_theme_extension.dart';
|
||||
|
||||
/// 操作按钮行 — .pen node pIpHe
|
||||
/// 操作按鈕行 — .pen node pIpHe
|
||||
/// gap: 12, three buttons evenly distributed
|
||||
/// Each button: circle 48x48 fill $bg-tertiary, cornerRadius 24
|
||||
/// icon: 20px $accent-primary (lucide: arrow-up-right / arrow-down-left / repeat)
|
||||
@@ -38,7 +38,7 @@ class ActionButtonsRow extends StatelessWidget {
|
||||
const SizedBox(width: 12),
|
||||
ActionButton(
|
||||
icon: LucideIcons.arrowDownLeft,
|
||||
label: '提现',
|
||||
label: '提現',
|
||||
accentColor: accentColor,
|
||||
bgColor: bgColor,
|
||||
onTap: onWithdraw,
|
||||
@@ -46,7 +46,7 @@ class ActionButtonsRow extends StatelessWidget {
|
||||
const SizedBox(width: 12),
|
||||
ActionButton(
|
||||
icon: LucideIcons.repeat,
|
||||
label: '划转',
|
||||
label: '劃轉',
|
||||
accentColor: accentColor,
|
||||
bgColor: bgColor,
|
||||
onTap: onTransfer,
|
||||
@@ -56,7 +56,7 @@ class ActionButtonsRow extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 单个操作按钮 — matching .pen btn1/btn2/btn3
|
||||
/// 單個操作按鈕 — matching .pen btn1/btn2/btn3
|
||||
class ActionButton extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String label;
|
||||
|
||||
@@ -17,7 +17,7 @@ import '../../../shared/ui_constants.dart';
|
||||
// Dialog helpers — shared sub-widgets
|
||||
// ============================================
|
||||
|
||||
/// 信息行 — 用于对话框中显示 label/value 键值对
|
||||
/// 信息行 — 用於對話框中顯示 label/value 鍵值對
|
||||
class InfoRow extends StatelessWidget {
|
||||
final String label;
|
||||
final String value;
|
||||
@@ -54,7 +54,7 @@ class InfoRow extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 钱包地址卡片 — 用于充值结果对话框中展示钱包地址
|
||||
/// 錢包地址卡片 — 用於充值結果對話框中展示錢包地址
|
||||
class WalletAddressCard extends StatelessWidget {
|
||||
final String address;
|
||||
final String network;
|
||||
@@ -94,7 +94,7 @@ class WalletAddressCard extends StatelessWidget {
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: address));
|
||||
ToastUtils.show('地址已复制到剪贴板');
|
||||
ToastUtils.show('地址已複製到剪貼板');
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(AppSpacing.xs),
|
||||
@@ -113,7 +113,7 @@ class WalletAddressCard extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
Text(
|
||||
'网络: $network',
|
||||
'網絡: $network',
|
||||
style: AppTextStyles.bodySmall(context),
|
||||
),
|
||||
],
|
||||
@@ -126,7 +126,7 @@ class WalletAddressCard extends StatelessWidget {
|
||||
// Dialog functions — kept from original with style updates
|
||||
// ============================================
|
||||
|
||||
/// 充值对话框
|
||||
/// 充值對話框
|
||||
void showDepositDialog(BuildContext context) {
|
||||
final amountController = TextEditingController();
|
||||
final formKey = GlobalKey<ShadFormState>();
|
||||
@@ -183,14 +183,14 @@ void showDepositDialog(BuildContext context) {
|
||||
child: ShadInputFormField(
|
||||
id: 'amount',
|
||||
controller: amountController,
|
||||
label: const Text('充值金额'),
|
||||
label: const Text('充值金額'),
|
||||
placeholder: const Text('最低 1000 USDT'),
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
validator: (v) {
|
||||
if (v == null || v.isEmpty) return '请输入金额';
|
||||
if (v == null || v.isEmpty) return '請輸入金額';
|
||||
final n = double.tryParse(v);
|
||||
if (n == null || n <= 0) return '请输入有效金额';
|
||||
if (n < 1000) return '单笔最低充值1000 USDT';
|
||||
if (n == null || n <= 0) return '請輸入有效金額';
|
||||
if (n < 1000) return '單筆最低充值1000 USDT';
|
||||
return null;
|
||||
},
|
||||
),
|
||||
@@ -222,7 +222,7 @@ void showDepositDialog(BuildContext context) {
|
||||
if (response.success && response.data != null) {
|
||||
showDepositResultDialog(context, response.data!);
|
||||
} else {
|
||||
showResultDialog(context, '申请失败', response.message);
|
||||
showResultDialog(context, '申請失敗', response.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,7 +240,7 @@ void showDepositDialog(BuildContext context) {
|
||||
);
|
||||
}
|
||||
|
||||
/// 充值结果对话框 — 展示钱包地址和确认打款
|
||||
/// 充值結果對話框 — 展示錢包地址和確認打款
|
||||
void showDepositResultDialog(BuildContext context, Map<String, dynamic> data) {
|
||||
final orderNo = data['orderNo'] as String? ?? '';
|
||||
final amount = data['amount']?.toString() ?? '0.00';
|
||||
@@ -268,7 +268,7 @@ void showDepositResultDialog(BuildContext context, Map<String, dynamic> data) {
|
||||
),
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
Text(
|
||||
'充值申请成功',
|
||||
'充值申請成功',
|
||||
style: AppTextStyles.headlineLarge(context).copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
@@ -276,12 +276,12 @@ void showDepositResultDialog(BuildContext context, Map<String, dynamic> data) {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
InfoRow(label: '订单号', value: orderNo),
|
||||
InfoRow(label: '訂單號', value: orderNo),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
InfoRow(label: '充值金额', value: '$amount USDT', isBold: true),
|
||||
InfoRow(label: '充值金額', value: '$amount USDT', isBold: true),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
Text(
|
||||
'请向以下地址转账:',
|
||||
'請向以下地址轉賬:',
|
||||
style: AppTextStyles.bodyMedium(context).copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
@@ -304,7 +304,7 @@ void showDepositResultDialog(BuildContext context, Map<String, dynamic> data) {
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'转账完成后请点击"已打款"按钮确认',
|
||||
'轉賬完成後請點擊"已打款"按鈕確認',
|
||||
style: AppTextStyles.bodyMedium(context).copyWith(
|
||||
color: AppColorScheme.warning,
|
||||
),
|
||||
@@ -318,7 +318,7 @@ void showDepositResultDialog(BuildContext context, Map<String, dynamic> data) {
|
||||
children: [
|
||||
Expanded(
|
||||
child: NeonButton(
|
||||
text: '稍后确认',
|
||||
text: '稍後確認',
|
||||
type: NeonButtonType.outline,
|
||||
onPressed: () => Navigator.of(ctx).pop(),
|
||||
height: 44,
|
||||
@@ -336,8 +336,8 @@ void showDepositResultDialog(BuildContext context, Map<String, dynamic> data) {
|
||||
if (context.mounted) {
|
||||
showResultDialog(
|
||||
context,
|
||||
response.success ? '确认成功' : '确认失败',
|
||||
response.success ? '请等待管理员审核' : response.message,
|
||||
response.success ? '確認成功' : '確認失敗',
|
||||
response.success ? '請等待管理員審核' : response.message,
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -354,13 +354,13 @@ void showDepositResultDialog(BuildContext context, Map<String, dynamic> data) {
|
||||
);
|
||||
}
|
||||
|
||||
/// 提现对话框
|
||||
/// 提現對話框
|
||||
void showWithdrawDialog(BuildContext context, String? balance) {
|
||||
final amountController = TextEditingController();
|
||||
final addressController = TextEditingController();
|
||||
final contactController = TextEditingController();
|
||||
final formKey = GlobalKey<ShadFormState>();
|
||||
final feeNotifier = ValueNotifier<String>('提现将扣除10%手续费');
|
||||
final feeNotifier = ValueNotifier<String>('提現將扣除10%手續費');
|
||||
final networksNotifier = ValueNotifier<List<String>>([]);
|
||||
final selectedNetworkNotifier = ValueNotifier<String?>(null);
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
@@ -370,13 +370,13 @@ void showWithdrawDialog(BuildContext context, String? balance) {
|
||||
if (amount > 0) {
|
||||
final fee = amount * 0.1;
|
||||
final receivable = amount - fee;
|
||||
feeNotifier.value = '手续费(10%): -${fee.toStringAsFixed(2)} USDT | 应收款: ${receivable.toStringAsFixed(2)} USDT';
|
||||
feeNotifier.value = '手續費(10%): -${fee.toStringAsFixed(2)} USDT | 應收款: ${receivable.toStringAsFixed(2)} USDT';
|
||||
} else {
|
||||
feeNotifier.value = '提现将扣除10%手续费';
|
||||
feeNotifier.value = '提現將扣除10%手續費';
|
||||
}
|
||||
});
|
||||
|
||||
// 获取网络列表
|
||||
// 獲取網絡列表
|
||||
context.read<AssetProvider>().getWalletNetworks().then((list) {
|
||||
networksNotifier.value = list;
|
||||
if (list.isNotEmpty) {
|
||||
@@ -411,7 +411,7 @@ void showWithdrawDialog(BuildContext context, String? balance) {
|
||||
),
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
Text(
|
||||
'提现',
|
||||
'提現',
|
||||
style: AppTextStyles.headlineLarge(context).copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
@@ -420,7 +420,7 @@ void showWithdrawDialog(BuildContext context, String? balance) {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.xs),
|
||||
Text(
|
||||
'安全地将您的资产转移到外部钱包地址',
|
||||
'安全地將您的資產轉移到外部錢包地址',
|
||||
style: AppTextStyles.bodyMedium(context).copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
@@ -443,7 +443,7 @@ void showWithdrawDialog(BuildContext context, String? balance) {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'可用余额: ',
|
||||
'可用餘額: ',
|
||||
style: AppTextStyles.bodySmall(context).copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
@@ -467,13 +467,13 @@ void showWithdrawDialog(BuildContext context, String? balance) {
|
||||
ShadInputFormField(
|
||||
id: 'amount',
|
||||
controller: amountController,
|
||||
label: const Text('提现金额'),
|
||||
placeholder: const Text('请输入提现金额(USDT)'),
|
||||
label: const Text('提現金額'),
|
||||
placeholder: const Text('請輸入提現金額(USDT)'),
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
validator: Validators.amount,
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
// 手续费/应收款提示
|
||||
// 手續費/應收款提示
|
||||
ValueListenableBuilder<String>(
|
||||
valueListenable: feeNotifier,
|
||||
builder: (_, feeText, __) {
|
||||
@@ -501,7 +501,7 @@ void showWithdrawDialog(BuildContext context, String? balance) {
|
||||
},
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
// 提现网络选择
|
||||
// 提現網絡選擇
|
||||
ValueListenableBuilder<List<String>>(
|
||||
valueListenable: networksNotifier,
|
||||
builder: (_, networks, __) {
|
||||
@@ -509,7 +509,7 @@ void showWithdrawDialog(BuildContext context, String? balance) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('提现网络', style: AppTextStyles.bodyMedium(context).copyWith(
|
||||
Text('提現網絡', style: AppTextStyles.bodyMedium(context).copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
)),
|
||||
const SizedBox(height: AppSpacing.xs),
|
||||
@@ -517,7 +517,7 @@ void showWithdrawDialog(BuildContext context, String? balance) {
|
||||
valueListenable: selectedNetworkNotifier,
|
||||
builder: (_, selected, __) {
|
||||
return ShadSelect<String>(
|
||||
placeholder: const Text('选择提现网络'),
|
||||
placeholder: const Text('選擇提現網絡'),
|
||||
initialValue: selected,
|
||||
selectedOptionBuilder: (context, val) => Text(val),
|
||||
onChanged: (value) {
|
||||
@@ -535,16 +535,16 @@ void showWithdrawDialog(BuildContext context, String? balance) {
|
||||
ShadInputFormField(
|
||||
id: 'address',
|
||||
controller: addressController,
|
||||
label: const Text('目标地址'),
|
||||
placeholder: const Text('请输入提现地址'),
|
||||
validator: (v) => Validators.required(v, '提现地址'),
|
||||
label: const Text('目標地址'),
|
||||
placeholder: const Text('請輸入提現地址'),
|
||||
validator: (v) => Validators.required(v, '提現地址'),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
ShadInputFormField(
|
||||
id: 'contact',
|
||||
controller: contactController,
|
||||
label: const Text('联系方式(可选)'),
|
||||
placeholder: const Text('联系方式'),
|
||||
label: const Text('聯繫方式(可選)'),
|
||||
placeholder: const Text('聯繫方式'),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -580,8 +580,8 @@ void showWithdrawDialog(BuildContext context, String? balance) {
|
||||
if (context.mounted) {
|
||||
showResultDialog(
|
||||
context,
|
||||
response.success ? '申请成功' : '申请失败',
|
||||
response.success ? '请等待管理员审批' : response.message,
|
||||
response.success ? '申請成功' : '申請失敗',
|
||||
response.success ? '請等待管理員審批' : response.message,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -612,7 +612,7 @@ void showWithdrawDialog(BuildContext context, String? balance) {
|
||||
);
|
||||
}
|
||||
|
||||
/// 通用结果对话框 — 展示操作成功/失败信息
|
||||
/// 通用結果對話框 — 展示操作成功/失敗信息
|
||||
void showResultDialog(BuildContext context, String title, String? message) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
@@ -646,7 +646,7 @@ void showResultDialog(BuildContext context, String title, String? message) {
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: NeonButton(
|
||||
text: '确定',
|
||||
text: '確定',
|
||||
type: NeonButtonType.primary,
|
||||
onPressed: () => Navigator.of(ctx).pop(),
|
||||
height: 44,
|
||||
|
||||
@@ -4,7 +4,7 @@ import '../../../../core/theme/app_theme_extension.dart';
|
||||
import '../../../../core/theme/app_spacing.dart';
|
||||
import '../../../components/glass_panel.dart';
|
||||
|
||||
/// 余额卡片 — 显示单个账户的 USDT 余额
|
||||
/// 餘額卡片 — 顯示單個賬戶的 USDT 餘額
|
||||
class BalanceCard extends StatelessWidget {
|
||||
final String label;
|
||||
final String balance;
|
||||
@@ -20,7 +20,7 @@ class BalanceCard extends StatelessWidget {
|
||||
final displayBalance = balance;
|
||||
|
||||
return SizedBox(
|
||||
width: double.infinity, // 确保卡片撑满宽度
|
||||
width: double.infinity, // 確保卡片撐滿寬度
|
||||
child: GlassPanel(
|
||||
padding: const EdgeInsets.all(20),
|
||||
borderRadius: BorderRadius.circular(AppRadius.lg),
|
||||
|
||||
@@ -5,8 +5,8 @@ import '../../../../core/theme/app_spacing.dart';
|
||||
import '../../../../data/models/account_models.dart';
|
||||
import '../../../components/glass_panel.dart';
|
||||
|
||||
/// 持仓区域
|
||||
/// Header: "我的资产" + "查看全部 >"
|
||||
/// 持倉區域
|
||||
/// Header: "我的資產" + "查看全部 >"
|
||||
/// Holdings Card: cornerRadius lg, fill $surface-card, stroke $border-default 1px
|
||||
class HoldingsSection extends StatelessWidget {
|
||||
final List holdings;
|
||||
@@ -19,14 +19,14 @@ class HoldingsSection extends StatelessWidget {
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
// Header row: "我的资产" + "查看全部 >"
|
||||
// Header row: "我的資產" + "查看全部 >"
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: AppSpacing.sm),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'我的资产',
|
||||
'我的資產',
|
||||
style: AppTextStyles.headlineLarge(context),
|
||||
),
|
||||
Text(
|
||||
@@ -44,7 +44,7 @@ class HoldingsSection extends StatelessWidget {
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(AppSpacing.xl),
|
||||
child: Text(
|
||||
'暂无持仓',
|
||||
'暫無持倉',
|
||||
style: AppTextStyles.bodyLarge(context).copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
@@ -78,7 +78,7 @@ class HoldingsSection extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 持仓行分隔线 — .pen node BCCbR / yejhE
|
||||
/// 持倉行分隔線 — .pen node BCCbR / yejhE
|
||||
/// fill: $border-default, height: 1, opacity: 0.5
|
||||
class HoldingDivider extends StatelessWidget {
|
||||
const HoldingDivider({super.key});
|
||||
@@ -94,7 +94,7 @@ class HoldingDivider extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 持仓行 — matching .pen nodes dAt4j / eK6vq / jiSUK
|
||||
/// 持倉行 — matching .pen nodes dAt4j / eK6vq / jiSUK
|
||||
/// padding [14, 16], space_between layout
|
||||
/// Left: avatar circle (36x36, radius 18, fill $accent-light) + coin info (gap 2)
|
||||
/// Right: value + pnl (gap 2, align end)
|
||||
|
||||
@@ -5,7 +5,7 @@ import '../../../../core/theme/app_theme_extension.dart';
|
||||
import '../../../../core/theme/app_spacing.dart';
|
||||
import '../../../components/glass_panel.dart';
|
||||
|
||||
/// 订单记录链接行
|
||||
/// 訂單記錄鏈接行
|
||||
/// cornerRadius: lg, fill: $surface-card, padding: [14, 16], stroke: $border-default 1px
|
||||
class RecordsLinkRow extends StatelessWidget {
|
||||
final VoidCallback onTap;
|
||||
@@ -25,7 +25,7 @@ class RecordsLinkRow extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'订单记录',
|
||||
'訂單記錄',
|
||||
style: AppTextStyles.headlineMedium(context).copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
|
||||
@@ -8,7 +8,7 @@ import '../../../core/theme/app_spacing.dart';
|
||||
import '../../../providers/asset_provider.dart';
|
||||
import '../../../data/models/account_models.dart';
|
||||
|
||||
/// 划转页面
|
||||
/// 劃轉頁面
|
||||
class TransferPage extends StatefulWidget {
|
||||
const TransferPage({super.key});
|
||||
|
||||
@@ -19,7 +19,7 @@ class TransferPage extends StatefulWidget {
|
||||
class _TransferPageState extends State<TransferPage> {
|
||||
final _amountController = TextEditingController();
|
||||
final _focusNode = FocusNode();
|
||||
int _direction = 1; // 1: 资金→交易, 2: 交易→资金
|
||||
int _direction = 1; // 1: 資金→交易, 2: 交易→資金
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
@@ -38,26 +38,26 @@ class _TransferPageState extends State<TransferPage> {
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 数据访问
|
||||
// 數據訪問
|
||||
// ============================================
|
||||
|
||||
/// 获取资金账户余额(安全检查)
|
||||
/// 獲取資金賬戶餘額(安全檢查)
|
||||
String get _fundBalance {
|
||||
try {
|
||||
final provider = context.read<AssetProvider>();
|
||||
final balance = provider.fundAccount?.balance ?? provider.overview?.fundBalance ?? '0.00';
|
||||
// 确保返回的是有效的数字格式
|
||||
// 確保返回的是有效的數字格式
|
||||
return _formatBalance(balance);
|
||||
} catch (e) {
|
||||
return '0.00';
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取交易账户 USDT 余额(安全检查)
|
||||
/// 獲取交易賬戶 USDT 餘額(安全檢查)
|
||||
String get _tradeUsdtBalance {
|
||||
try {
|
||||
final provider = context.read<AssetProvider>();
|
||||
// 先检查列表是否为空
|
||||
// 先檢查列表是否為空
|
||||
if (provider.tradeAccounts.isEmpty) {
|
||||
return '0.00';
|
||||
}
|
||||
@@ -81,32 +81,32 @@ class _TransferPageState extends State<TransferPage> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取当前可用余额(根据方向)
|
||||
/// 獲取當前可用餘額(根據方向)
|
||||
String get _availableBalance => _direction == 1 ? _fundBalance : _tradeUsdtBalance;
|
||||
|
||||
/// 从账户名
|
||||
String get _fromLabel => _direction == 1 ? '资金账户' : '交易账户';
|
||||
String get _toLabel => _direction == 1 ? '交易账户' : '资金账户';
|
||||
/// 從賬戶名
|
||||
String get _fromLabel => _direction == 1 ? '資金賬戶' : '交易賬戶';
|
||||
String get _toLabel => _direction == 1 ? '交易賬戶' : '資金賬戶';
|
||||
String get _fromBalance => _direction == 1 ? _fundBalance : _tradeUsdtBalance;
|
||||
String get _toBalance => _direction == 1 ? _tradeUsdtBalance : _fundBalance;
|
||||
|
||||
// ============================================
|
||||
// 业务逻辑
|
||||
// 業務邏輯
|
||||
// ============================================
|
||||
|
||||
/// 执行划转
|
||||
/// 執行劃轉
|
||||
Future<void> _doTransfer() async {
|
||||
final amount = _amountController.text;
|
||||
final available = double.tryParse(_availableBalance) ?? 0;
|
||||
final transferAmount = double.tryParse(amount) ?? 0;
|
||||
|
||||
if (transferAmount <= 0) {
|
||||
_showSnackBar('请输入有效的划转金额');
|
||||
_showSnackBar('請輸入有效的劃轉金額');
|
||||
return;
|
||||
}
|
||||
|
||||
if (transferAmount > available) {
|
||||
_showSnackBar('余额不足');
|
||||
_showSnackBar('餘額不足');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -121,10 +121,10 @@ class _TransferPageState extends State<TransferPage> {
|
||||
if (mounted) {
|
||||
if (response.success) {
|
||||
_amountController.clear();
|
||||
_showSnackBar('划转成功');
|
||||
_showSnackBar('劃轉成功');
|
||||
Navigator.of(context).pop(true);
|
||||
} else {
|
||||
_showSnackBar(response.message ?? '划转失败');
|
||||
_showSnackBar(response.message ?? '劃轉失敗');
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@@ -140,14 +140,14 @@ class _TransferPageState extends State<TransferPage> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 设置快捷百分比金额
|
||||
/// 設置快捷百分比金額
|
||||
void _setQuickAmount(double percent) {
|
||||
final available = double.tryParse(_availableBalance) ?? 0;
|
||||
final amount = available * percent;
|
||||
_amountController.text = amount.toStringAsFixed(8).replaceAll(RegExp(r'\.?0+$'), '');
|
||||
}
|
||||
|
||||
/// 切换方向
|
||||
/// 切換方向
|
||||
void _toggleDirection() {
|
||||
setState(() {
|
||||
_direction = _direction == 1 ? 2 : 1;
|
||||
@@ -155,7 +155,7 @@ class _TransferPageState extends State<TransferPage> {
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 构建 UI
|
||||
// 構建 UI
|
||||
// ============================================
|
||||
|
||||
@override
|
||||
@@ -173,7 +173,7 @@ class _TransferPageState extends State<TransferPage> {
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
title: Text(
|
||||
'账户划转',
|
||||
'賬戶劃轉',
|
||||
style: AppTextStyles.headlineLarge(context),
|
||||
),
|
||||
centerTitle: true,
|
||||
@@ -205,7 +205,7 @@ class _TransferPageState extends State<TransferPage> {
|
||||
|
||||
Widget _buildTransferDirectionCard() {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
// 金色主题感知: Dark #D4AF37 (primary), Light #F59E0B (secondary)
|
||||
// 金色主題感知: Dark #D4AF37 (primary), Light #F59E0B (secondary)
|
||||
final goldAccent = colorScheme.brightness == Brightness.dark
|
||||
? colorScheme.primary
|
||||
: colorScheme.secondary;
|
||||
@@ -225,7 +225,7 @@ class _TransferPageState extends State<TransferPage> {
|
||||
key: 'src-$_direction',
|
||||
beginOffset: const Offset(0, -1),
|
||||
child: _buildAccountRow(
|
||||
label: '从',
|
||||
label: '從',
|
||||
accountName: _fromLabel,
|
||||
balance: _fromBalance,
|
||||
),
|
||||
@@ -239,7 +239,7 @@ class _TransferPageState extends State<TransferPage> {
|
||||
height: 40,
|
||||
margin: const EdgeInsets.symmetric(vertical: AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
// 椭圆形半透明金色背景
|
||||
// 橢圓形半透明金色背景
|
||||
color: goldAccent.withValues(alpha: 0.15),
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
@@ -272,7 +272,7 @@ class _TransferPageState extends State<TransferPage> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 统一的 AnimatedSwitcher 构造
|
||||
/// 統一的 AnimatedSwitcher 構造
|
||||
Widget _animatedSwitcher({
|
||||
required String key,
|
||||
required Offset beginOffset,
|
||||
@@ -313,7 +313,7 @@ class _TransferPageState extends State<TransferPage> {
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
label == '从' ? LucideIcons.wallet : LucideIcons.trendingUp,
|
||||
label == '從' ? LucideIcons.wallet : LucideIcons.trendingUp,
|
||||
size: 18,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
@@ -342,14 +342,14 @@ class _TransferPageState extends State<TransferPage> {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Label row: "划转金额" + "全部划转"
|
||||
// Label row: "劃轉金額" + "全部劃轉"
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('划转金额', style: AppTextStyles.headlineSmall(context).copyWith(color: colorScheme.onSurfaceVariant)),
|
||||
Text('劃轉金額', style: AppTextStyles.headlineSmall(context).copyWith(color: colorScheme.onSurfaceVariant)),
|
||||
GestureDetector(
|
||||
onTap: () => _setQuickAmount(1.0),
|
||||
child: Text('全部划转', style: AppTextStyles.labelLarge(context).copyWith(
|
||||
child: Text('全部劃轉', style: AppTextStyles.labelLarge(context).copyWith(
|
||||
color: colorScheme.secondary,
|
||||
fontWeight: FontWeight.w600,
|
||||
)),
|
||||
@@ -461,7 +461,7 @@ class _TransferPageState extends State<TransferPage> {
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'划转即时到账,无需手续费',
|
||||
'劃轉即時到賬,無需手續費',
|
||||
style: AppTextStyles.bodyMedium(context).copyWith(color: upColor),
|
||||
),
|
||||
),
|
||||
@@ -472,7 +472,7 @@ class _TransferPageState extends State<TransferPage> {
|
||||
|
||||
Widget _buildConfirmButton() {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
// 金色主题感知: Dark #D4AF37 (primary), Light #F59E0B (secondary)
|
||||
// 金色主題感知: Dark #D4AF37 (primary), Light #F59E0B (secondary)
|
||||
final goldAccent = colorScheme.brightness == Brightness.dark
|
||||
? colorScheme.primary
|
||||
: colorScheme.secondary;
|
||||
@@ -498,7 +498,7 @@ class _TransferPageState extends State<TransferPage> {
|
||||
),
|
||||
)
|
||||
: const Text(
|
||||
'确认划转',
|
||||
'確認劃轉',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
|
||||
@@ -49,13 +49,13 @@ class _LoginPageState extends State<LoginPage> {
|
||||
key: formKey,
|
||||
child: Column(
|
||||
children: [
|
||||
// 顶部品牌区域
|
||||
// 頂部品牌區域
|
||||
_buildBrandSection(),
|
||||
const SizedBox(height: AppSpacing.xxl),
|
||||
// 表单区域
|
||||
// 表單區域
|
||||
_buildFormSection(),
|
||||
const SizedBox(height: AppSpacing.xl),
|
||||
// 底部注册链接
|
||||
// 底部註冊鏈接
|
||||
_buildRegisterRow(),
|
||||
],
|
||||
),
|
||||
@@ -66,13 +66,13 @@ class _LoginPageState extends State<LoginPage> {
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 品牌区域 - Logo + 品牌名 + 标语
|
||||
// 品牌區域 - Logo + 品牌名 + 標語
|
||||
// ============================================
|
||||
|
||||
Widget _buildBrandSection() {
|
||||
return Column(
|
||||
children: [
|
||||
// Logo 圆形:渐变 #1F2937 → #374151,内含 "M"
|
||||
// Logo 圓形:漸變 #1F2937 → #374151,內含 "M"
|
||||
Container(
|
||||
width: _logoCircleSize,
|
||||
height: _logoCircleSize,
|
||||
@@ -105,9 +105,9 @@ class _LoginPageState extends State<LoginPage> {
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
// 标语
|
||||
// 標語
|
||||
Text(
|
||||
'虚拟货币模拟交易平台',
|
||||
'虛擬貨幣模擬交易平臺',
|
||||
style: AppTextStyles.bodyLarge(context).copyWith(
|
||||
color: context.colors.onSurfaceVariant,
|
||||
),
|
||||
@@ -118,7 +118,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 表单区域 - 用户名 + 密码 + 登录按钮
|
||||
// 表單區域 - 用戶名 + 密碼 + 登錄按鈕
|
||||
// ============================================
|
||||
|
||||
Widget _buildFormSection() {
|
||||
@@ -139,7 +139,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||
height: _inputHeight,
|
||||
child: ShadInputFormField(
|
||||
id: 'username',
|
||||
placeholder: const Text('请输入用户名'),
|
||||
placeholder: const Text('請輸入用戶名'),
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.only(right: AppSpacing.sm),
|
||||
child: Icon(LucideIcons.user, size: 18, color: context.appColors.onSurfaceMuted),
|
||||
@@ -166,7 +166,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||
height: _inputHeight,
|
||||
child: ShadInputFormField(
|
||||
id: 'password',
|
||||
placeholder: const Text('请输入密码'),
|
||||
placeholder: const Text('請輸入密碼'),
|
||||
obscureText: _obscurePassword,
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.only(right: AppSpacing.sm),
|
||||
@@ -196,7 +196,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||
}
|
||||
|
||||
Widget _buildLoginButton() {
|
||||
// 设计稿: accent-primary = light:#1F2937 / dark:#D4AF37
|
||||
// 設計稿: accent-primary = light:#1F2937 / dark:#D4AF37
|
||||
final buttonColor = context.appColors.accentPrimary;
|
||||
final textColor = context.colors.onPrimary;
|
||||
|
||||
@@ -222,7 +222,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
'登录',
|
||||
'登錄',
|
||||
style: AppTextStyles.headlineLarge(context).copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: textColor,
|
||||
@@ -235,7 +235,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 底部注册链接
|
||||
// 底部註冊鏈接
|
||||
// ============================================
|
||||
|
||||
Widget _buildRegisterRow() {
|
||||
@@ -249,7 +249,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'还没有账户?',
|
||||
'還沒有賬戶?',
|
||||
style: AppTextStyles.bodyLarge(context).copyWith(
|
||||
color: secondaryTextColor,
|
||||
),
|
||||
@@ -258,7 +258,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||
GestureDetector(
|
||||
onTap: _navigateToRegister,
|
||||
child: Text(
|
||||
'立即注册',
|
||||
'立即註冊',
|
||||
style: AppTextStyles.bodyLarge(context).copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: goldColor,
|
||||
@@ -276,20 +276,20 @@ class _LoginPageState extends State<LoginPage> {
|
||||
|
||||
String? _validateUsername(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return '请输入用户名';
|
||||
return '請輸入用戶名';
|
||||
}
|
||||
if (value.length < 3) {
|
||||
return '用户名至少 3 个字符';
|
||||
return '用戶名至少 3 個字符';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? _validatePassword(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return '请输入密码';
|
||||
return '請輸入密碼';
|
||||
}
|
||||
if (value.length < 6) {
|
||||
return '密码至少 6 个字符';
|
||||
return '密碼至少 6 個字符';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -312,7 +312,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||
if (response.success) {
|
||||
_navigateToMainPage();
|
||||
} else {
|
||||
_showErrorDialog(response.message ?? '用户名或密码错误');
|
||||
_showErrorDialog(response.message ?? '用戶名或密碼錯誤');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,11 +334,11 @@ class _LoginPageState extends State<LoginPage> {
|
||||
showShadDialog(
|
||||
context: context,
|
||||
builder: (context) => ShadDialog.alert(
|
||||
title: const Text('登录失败'),
|
||||
title: const Text('登錄失敗'),
|
||||
description: Text(message),
|
||||
actions: [
|
||||
ShadButton(
|
||||
child: const Text('确定'),
|
||||
child: const Text('確定'),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -11,7 +11,7 @@ import '../../components/glass_panel.dart';
|
||||
import '../../components/neon_glow.dart';
|
||||
import '../main/main_page.dart';
|
||||
|
||||
/// 注册页面(两步注册:账号信息 + 身份证上传)
|
||||
/// 註冊頁面(兩步註冊:賬號信息 + 身份證上傳)
|
||||
class RegisterPage extends StatefulWidget {
|
||||
const RegisterPage({super.key});
|
||||
|
||||
@@ -20,7 +20,7 @@ class RegisterPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _RegisterPageState extends State<RegisterPage> {
|
||||
int _currentStep = 0; // 0: 账号信息, 1: 身份证上传
|
||||
int _currentStep = 0; // 0: 賬號信息, 1: 身份證上傳
|
||||
|
||||
// 第一步
|
||||
final _usernameController = TextEditingController();
|
||||
@@ -96,11 +96,11 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// 步骤指示器
|
||||
// 步驟指示器
|
||||
_buildStepIndicator(colorScheme),
|
||||
SizedBox(height: AppSpacing.xl),
|
||||
|
||||
// 内容区
|
||||
// 內容區
|
||||
_currentStep == 0 ? _buildStep1(colorScheme) : _buildStep2(colorScheme),
|
||||
],
|
||||
),
|
||||
@@ -114,7 +114,7 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
children: [
|
||||
_buildStepCircle(
|
||||
number: '1',
|
||||
label: '账号信息',
|
||||
label: '賬號信息',
|
||||
isActive: true,
|
||||
isComplete: _currentStep > 0,
|
||||
colorScheme: colorScheme,
|
||||
@@ -129,7 +129,7 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
),
|
||||
_buildStepCircle(
|
||||
number: '2',
|
||||
label: '身份验证',
|
||||
label: '身份驗證',
|
||||
isActive: _currentStep >= 1,
|
||||
isComplete: false,
|
||||
colorScheme: colorScheme,
|
||||
@@ -191,17 +191,17 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 第一步:账号信息
|
||||
/// 第一步:賬號信息
|
||||
Widget _buildStep1(ColorScheme colorScheme) {
|
||||
return Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// 标题
|
||||
// 標題
|
||||
Center(
|
||||
child: Text(
|
||||
'创建账号',
|
||||
'創建賬號',
|
||||
style: AppTextStyles.displaySmall(context).copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: colorScheme.onSurface,
|
||||
@@ -210,30 +210,30 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
),
|
||||
SizedBox(height: AppSpacing.xxl),
|
||||
|
||||
// 用户名
|
||||
// 用戶名
|
||||
TextFormField(
|
||||
controller: _usernameController,
|
||||
style: TextStyle(color: colorScheme.onSurface),
|
||||
decoration: InputDecoration(
|
||||
hintText: '请输入账号(4-20位字母数字)',
|
||||
hintText: '請輸入賬號(4-20位字母數字)',
|
||||
prefixIcon: Icon(Icons.person_outline, color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) return '请输入账号';
|
||||
if (value.length < 4) return '账号至少4位';
|
||||
if (value.length > 20) return '账号最多20位';
|
||||
if (value == null || value.isEmpty) return '請輸入賬號';
|
||||
if (value.length < 4) return '賬號至少4位';
|
||||
if (value.length > 20) return '賬號最多20位';
|
||||
return null;
|
||||
},
|
||||
),
|
||||
SizedBox(height: AppSpacing.md),
|
||||
|
||||
// 密码
|
||||
// 密碼
|
||||
TextFormField(
|
||||
controller: _passwordController,
|
||||
obscureText: _obscurePassword,
|
||||
style: TextStyle(color: colorScheme.onSurface),
|
||||
decoration: InputDecoration(
|
||||
hintText: '请输入密码(至少6位)',
|
||||
hintText: '請輸入密碼(至少6位)',
|
||||
prefixIcon: Icon(Icons.lock_outline, color: colorScheme.onSurfaceVariant),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
@@ -244,20 +244,20 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) return '请输入密码';
|
||||
if (value.length < 6) return '密码至少6位';
|
||||
if (value == null || value.isEmpty) return '請輸入密碼';
|
||||
if (value.length < 6) return '密碼至少6位';
|
||||
return null;
|
||||
},
|
||||
),
|
||||
SizedBox(height: AppSpacing.md),
|
||||
|
||||
// 确认密码
|
||||
// 確認密碼
|
||||
TextFormField(
|
||||
controller: _confirmPasswordController,
|
||||
obscureText: _obscureConfirmPassword,
|
||||
style: TextStyle(color: colorScheme.onSurface),
|
||||
decoration: InputDecoration(
|
||||
hintText: '请再次输入密码',
|
||||
hintText: '請再次輸入密碼',
|
||||
prefixIcon: Icon(Icons.lock_outline, color: colorScheme.onSurfaceVariant),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
@@ -268,25 +268,25 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) return '请再次输入密码';
|
||||
if (value != _passwordController.text) return '两次密码不一致';
|
||||
if (value == null || value.isEmpty) return '請再次輸入密碼';
|
||||
if (value != _passwordController.text) return '兩次密碼不一致';
|
||||
return null;
|
||||
},
|
||||
),
|
||||
SizedBox(height: AppSpacing.md),
|
||||
|
||||
// 推广码(可选)
|
||||
// 推廣碼(可選)
|
||||
TextFormField(
|
||||
controller: _referralCodeController,
|
||||
style: TextStyle(color: colorScheme.onSurface),
|
||||
decoration: InputDecoration(
|
||||
hintText: '推广码(选填)',
|
||||
hintText: '推廣碼(選填)',
|
||||
prefixIcon: Icon(Icons.card_giftcard, color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
),
|
||||
SizedBox(height: AppSpacing.xl),
|
||||
|
||||
// 下一步按钮
|
||||
// 下一步按鈕
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: NeonButton(
|
||||
@@ -303,11 +303,11 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
),
|
||||
SizedBox(height: AppSpacing.md),
|
||||
|
||||
// 登录链接
|
||||
// 登錄鏈接
|
||||
Center(
|
||||
child: TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text('已有账号?立即登录', style: AppTextStyles.headlineMedium(context)),
|
||||
child: Text('已有賬號?立即登錄', style: AppTextStyles.headlineMedium(context)),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -315,12 +315,12 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 第二步:身份证上传
|
||||
/// 第二步:身份證上傳
|
||||
Widget _buildStep2(ColorScheme colorScheme) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// 标题区
|
||||
// 標題區
|
||||
GlassPanel(
|
||||
padding: EdgeInsets.all(AppSpacing.lg),
|
||||
child: Column(
|
||||
@@ -345,7 +345,7 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'身份验证',
|
||||
'身份驗證',
|
||||
style: AppTextStyles.headlineLarge(context).copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: colorScheme.onSurface,
|
||||
@@ -353,7 +353,7 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
),
|
||||
SizedBox(height: AppSpacing.xs),
|
||||
Text(
|
||||
'上传身份证正反面完成注册',
|
||||
'上傳身份證正反面完成註冊',
|
||||
style: AppTextStyles.bodyMedium(context).copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
@@ -364,9 +364,9 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
),
|
||||
SizedBox(height: AppSpacing.xl),
|
||||
|
||||
// 身份证正面
|
||||
// 身份證正面
|
||||
Text(
|
||||
'身份证正面(人像面)',
|
||||
'身份證正面(人像面)',
|
||||
style: AppTextStyles.bodyLarge(context).copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: colorScheme.onSurface,
|
||||
@@ -382,9 +382,9 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
),
|
||||
SizedBox(height: AppSpacing.lg),
|
||||
|
||||
// 身份证反面
|
||||
// 身份證反面
|
||||
Text(
|
||||
'身份证反面(国徽面)',
|
||||
'身份證反面(國徽面)',
|
||||
style: AppTextStyles.bodyLarge(context).copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: colorScheme.onSurface,
|
||||
@@ -394,19 +394,19 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
_buildUploadZone(
|
||||
imageFile: _backFile,
|
||||
imageBytes: _backBytes,
|
||||
label: '国徽面',
|
||||
label: '國徽面',
|
||||
onTap: () => _pickImage(false),
|
||||
colorScheme: colorScheme,
|
||||
),
|
||||
SizedBox(height: AppSpacing.xl),
|
||||
|
||||
// 注册按钮
|
||||
// 註冊按鈕
|
||||
Consumer<AuthProvider>(
|
||||
builder: (context, auth, _) {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: NeonButton(
|
||||
text: _isLoading ? '注册中...' : '完成注册',
|
||||
text: _isLoading ? '註冊中...' : '完成註冊',
|
||||
type: NeonButtonType.primary,
|
||||
onPressed: _canSubmit && !auth.isLoading ? _handleRegister : null,
|
||||
height: 48,
|
||||
@@ -434,7 +434,7 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
SizedBox(width: AppSpacing.sm),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'您的身份信息将被加密存储,仅用于身份验证',
|
||||
'您的身份信息將被加密存儲,僅用於身份驗證',
|
||||
style: AppTextStyles.bodySmall(context).copyWith(
|
||||
color: AppColorScheme.up.withValues(alpha: 0.8),
|
||||
),
|
||||
@@ -502,7 +502,7 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'$label已选择',
|
||||
'$label已選擇',
|
||||
style: AppTextStyles.bodyMedium(context).copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColorScheme.darkOnPrimary,
|
||||
@@ -551,7 +551,7 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
),
|
||||
SizedBox(height: AppSpacing.sm),
|
||||
Text(
|
||||
'点击上传$label',
|
||||
'點擊上傳$label',
|
||||
style: AppTextStyles.bodyLarge(context).copyWith(
|
||||
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.6),
|
||||
),
|
||||
@@ -597,11 +597,11 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
showShadDialog(
|
||||
context: context,
|
||||
builder: (ctx) => ShadDialog.alert(
|
||||
title: const Text('注册失败'),
|
||||
description: Text(response.message ?? '请稍后重试'),
|
||||
title: const Text('註冊失敗'),
|
||||
description: Text(response.message ?? '請稍後重試'),
|
||||
actions: [
|
||||
ShadButton(
|
||||
child: const Text('确定'),
|
||||
child: const Text('確定'),
|
||||
onPressed: () => Navigator.of(ctx).pop(),
|
||||
),
|
||||
],
|
||||
@@ -613,11 +613,11 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
showShadDialog(
|
||||
context: context,
|
||||
builder: (ctx) => ShadDialog.alert(
|
||||
title: const Text('注册失败'),
|
||||
title: const Text('註冊失敗'),
|
||||
description: Text(e.toString()),
|
||||
actions: [
|
||||
ShadButton(
|
||||
child: const Text('确定'),
|
||||
child: const Text('確定'),
|
||||
onPressed: () => Navigator.of(ctx).pop(),
|
||||
),
|
||||
],
|
||||
@@ -630,7 +630,7 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 虚线边框画笔
|
||||
/// 虛線邊框畫筆
|
||||
class _DashedBorderPainter extends CustomPainter {
|
||||
final Color color;
|
||||
final double borderRadius;
|
||||
|
||||
@@ -9,7 +9,7 @@ import '../../../data/models/account_models.dart';
|
||||
import '../../../data/services/bonus_service.dart';
|
||||
import '../../../providers/asset_provider.dart';
|
||||
|
||||
/// 账单页面 — 代币盈亏账单 + 新人福利账单 + 推广福利账单
|
||||
/// 賬單頁面 — 代幣盈虧賬單 + 新人福利賬單 + 推廣福利賬單
|
||||
class BillsPage extends StatefulWidget {
|
||||
const BillsPage({super.key});
|
||||
|
||||
@@ -42,7 +42,7 @@ class _BillsPageState extends State<BillsPage> with SingleTickerProviderStateMix
|
||||
final provider = context.read<AssetProvider>();
|
||||
final bonusService = context.read<BonusService>();
|
||||
|
||||
// 并行加载持仓和福利记录
|
||||
// 並行加載持倉和福利記錄
|
||||
await provider.loadTradeAccount(force: true);
|
||||
final welfareResponse = await bonusService.getWelfareStatus();
|
||||
|
||||
@@ -73,7 +73,7 @@ class _BillsPageState extends State<BillsPage> with SingleTickerProviderStateMix
|
||||
if (newUser != null) {
|
||||
final claimed = newUser['claimed'] as bool? ?? false;
|
||||
final eligible = newUser['eligible'] as bool? ?? false;
|
||||
// 状态: 1=已领取, 0=可领取(待领取), 2=不可用(未解锁)
|
||||
// 狀態: 1=已領取, 0=可領取(待領取), 2=不可用(未解鎖)
|
||||
final int status;
|
||||
if (claimed) {
|
||||
status = 1;
|
||||
@@ -91,15 +91,15 @@ class _BillsPageState extends State<BillsPage> with SingleTickerProviderStateMix
|
||||
});
|
||||
}
|
||||
|
||||
// 推广福利列表
|
||||
// 推廣福利列表
|
||||
final referralRewards = data['referralRewards'] as List<dynamic>? ?? [];
|
||||
for (var r in referralRewards) {
|
||||
final map = r as Map<String, dynamic>;
|
||||
final username = map['username'] as String? ?? '用户';
|
||||
final username = map['username'] as String? ?? '用戶';
|
||||
final milestones = map['milestones'] as List<dynamic>? ?? [];
|
||||
final claimableCount = map['claimableCount'] as int? ?? 0;
|
||||
|
||||
// 每个 milestone 生成一条记录
|
||||
// 每個 milestone 生成一條記錄
|
||||
for (var m in milestones) {
|
||||
final ms = m as Map<String, dynamic>;
|
||||
final earned = ms['earned'] as bool? ?? false;
|
||||
@@ -108,26 +108,26 @@ class _BillsPageState extends State<BillsPage> with SingleTickerProviderStateMix
|
||||
|
||||
final int status;
|
||||
if (earned) {
|
||||
status = 1; // 已领取
|
||||
status = 1; // 已領取
|
||||
} else if (claimable) {
|
||||
status = 0; // 可领取
|
||||
status = 0; // 可領取
|
||||
} else {
|
||||
status = 2; // 未达标
|
||||
status = 2; // 未達標
|
||||
}
|
||||
records.add({
|
||||
'type': 'referral',
|
||||
'title': '推广福利 - $username (${milestoneVal}000)',
|
||||
'title': '推廣福利 - $username (${milestoneVal}000)',
|
||||
'amount': '100.00',
|
||||
'status': status,
|
||||
'time': ms['claimTime'] ?? ms['createTime'],
|
||||
});
|
||||
}
|
||||
|
||||
// 如果没有 milestone 但有 claimableCount,也生成记录
|
||||
// 如果沒有 milestone 但有 claimableCount,也生成記錄
|
||||
if (milestones.isEmpty && claimableCount > 0) {
|
||||
records.add({
|
||||
'type': 'referral',
|
||||
'title': '推广福利 - $username',
|
||||
'title': '推廣福利 - $username',
|
||||
'amount': '${claimableCount * 100}',
|
||||
'status': 0,
|
||||
'time': null,
|
||||
@@ -150,7 +150,7 @@ class _BillsPageState extends State<BillsPage> with SingleTickerProviderStateMix
|
||||
icon: const Icon(LucideIcons.arrowLeft, size: 20),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
title: Text('我的账单', style: AppTextStyles.headlineLarge(context)),
|
||||
title: Text('我的賬單', style: AppTextStyles.headlineLarge(context)),
|
||||
backgroundColor: _isDark ? AppColorScheme.darkBackground : AppColorScheme.lightBackground,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
@@ -163,9 +163,9 @@ class _BillsPageState extends State<BillsPage> with SingleTickerProviderStateMix
|
||||
labelStyle: AppTextStyles.headlineMedium(context).copyWith(fontWeight: FontWeight.w600),
|
||||
unselectedLabelStyle: AppTextStyles.headlineMedium(context),
|
||||
tabs: const [
|
||||
Tab(text: '代币盈亏'),
|
||||
Tab(text: '代幣盈虧'),
|
||||
Tab(text: '新人福利'),
|
||||
Tab(text: '推广福利'),
|
||||
Tab(text: '推廣福利'),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -183,16 +183,16 @@ class _BillsPageState extends State<BillsPage> with SingleTickerProviderStateMix
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 代币盈亏账单
|
||||
// 代幣盈虧賬單
|
||||
// ============================================
|
||||
Widget _buildCoinProfitTab() {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
if (_holdings.isEmpty) {
|
||||
return _buildEmptyState(LucideIcons.wallet, '暂无持仓记录');
|
||||
return _buildEmptyState(LucideIcons.wallet, '暫無持倉記錄');
|
||||
}
|
||||
|
||||
// 汇总统计
|
||||
// 彙總統計
|
||||
double totalCost = 0;
|
||||
double totalValue = 0;
|
||||
double totalProfit = 0;
|
||||
@@ -210,7 +210,7 @@ class _BillsPageState extends State<BillsPage> with SingleTickerProviderStateMix
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
children: [
|
||||
// 汇总卡片
|
||||
// 彙總卡片
|
||||
Container(
|
||||
padding: const EdgeInsets.all(AppSpacing.lg),
|
||||
decoration: BoxDecoration(
|
||||
@@ -224,7 +224,7 @@ class _BillsPageState extends State<BillsPage> with SingleTickerProviderStateMix
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Text('总盈亏 (USDT)', style: AppTextStyles.bodyMedium(context).copyWith(
|
||||
Text('總盈虧 (USDT)', style: AppTextStyles.bodyMedium(context).copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
)),
|
||||
const SizedBox(height: AppSpacing.xs),
|
||||
@@ -239,9 +239,9 @@ class _BillsPageState extends State<BillsPage> with SingleTickerProviderStateMix
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_buildSummaryItem('总成本', totalCost.toStringAsFixed(2)),
|
||||
_buildSummaryItem('總成本', totalCost.toStringAsFixed(2)),
|
||||
Container(width: 1, height: 16, color: colorScheme.outlineVariant.withValues(alpha: 0.3)),
|
||||
_buildSummaryItem('总市值', totalValue.toStringAsFixed(2)),
|
||||
_buildSummaryItem('總市值', totalValue.toStringAsFixed(2)),
|
||||
Container(width: 1, height: 16, color: colorScheme.outlineVariant.withValues(alpha: 0.3)),
|
||||
_buildSummaryItem('收益率', '${profitRate >= 0 ? '+' : ''}${profitRate.toStringAsFixed(2)}%'),
|
||||
],
|
||||
@@ -251,7 +251,7 @@ class _BillsPageState extends State<BillsPage> with SingleTickerProviderStateMix
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
|
||||
// 各币种盈亏明细
|
||||
// 各幣種盈虧明細
|
||||
..._holdings.map((h) => _buildCoinProfitCard(h)),
|
||||
],
|
||||
),
|
||||
@@ -296,7 +296,7 @@ class _BillsPageState extends State<BillsPage> with SingleTickerProviderStateMix
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
// 币名 + 盈亏金额
|
||||
// 幣名 + 盈虧金額
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
@@ -333,11 +333,11 @@ class _BillsPageState extends State<BillsPage> with SingleTickerProviderStateMix
|
||||
],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
// 明细行
|
||||
// 明細行
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('均价: ${h.avgPrice}', style: AppTextStyles.bodySmall(context).copyWith(
|
||||
Text('均價: ${h.avgPrice}', style: AppTextStyles.bodySmall(context).copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
)),
|
||||
Text('市值: ${h.currentValue} USDT', style: AppTextStyles.bodySmall(context).copyWith(
|
||||
@@ -355,7 +355,7 @@ class _BillsPageState extends State<BillsPage> with SingleTickerProviderStateMix
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 福利账单
|
||||
// 福利賬單
|
||||
// ============================================
|
||||
Widget _buildWelfareTab(String type) {
|
||||
final records = _welfareRecords.where((r) => r['type'] == type).toList();
|
||||
@@ -363,7 +363,7 @@ class _BillsPageState extends State<BillsPage> with SingleTickerProviderStateMix
|
||||
if (records.isEmpty) {
|
||||
return _buildEmptyState(
|
||||
LucideIcons.gift,
|
||||
type == 'new_user' ? '暂无新人福利记录' : '暂无推广福利记录',
|
||||
type == 'new_user' ? '暫無新人福利記錄' : '暫無推廣福利記錄',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -382,20 +382,20 @@ class _BillsPageState extends State<BillsPage> with SingleTickerProviderStateMix
|
||||
final amount = double.tryParse(record['amount']?.toString() ?? '0') ?? 0;
|
||||
final status = record['status'] as int? ?? 0;
|
||||
|
||||
// status: 0=待领取, 1=已领取, 2=未达标
|
||||
// status: 0=待領取, 1=已領取, 2=未達標
|
||||
String statusText;
|
||||
Color statusColor;
|
||||
switch (status) {
|
||||
case 1:
|
||||
statusText = '已领取';
|
||||
statusText = '已領取';
|
||||
statusColor = context.appColors.up;
|
||||
break;
|
||||
case 2:
|
||||
statusText = '未达标';
|
||||
statusText = '未達標';
|
||||
statusColor = colorScheme.onSurfaceVariant;
|
||||
break;
|
||||
default:
|
||||
statusText = '待领取';
|
||||
statusText = '待領取';
|
||||
statusColor = AppColorScheme.warning;
|
||||
}
|
||||
|
||||
@@ -463,7 +463,7 @@ class _BillsPageState extends State<BillsPage> with SingleTickerProviderStateMix
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 通用组件
|
||||
// 通用組件
|
||||
// ============================================
|
||||
Widget _buildEmptyState(IconData icon, String text) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
@@ -6,7 +6,7 @@ import '../../../core/theme/app_color_scheme.dart';
|
||||
import '../../../core/theme/app_spacing.dart';
|
||||
import '../../../providers/auth_provider.dart';
|
||||
|
||||
/// 首页顶栏 - Logo + 搜索/通知/头像
|
||||
/// 首頁頂欄 - Logo + 搜索/通知/頭像
|
||||
class HeaderBar extends StatelessWidget {
|
||||
const HeaderBar({super.key});
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import 'hot_coins_section.dart';
|
||||
import 'profit_analysis_page.dart';
|
||||
import 'bills_page.dart';
|
||||
|
||||
/// 首页
|
||||
/// 首頁
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({super.key});
|
||||
|
||||
@@ -105,13 +105,13 @@ class _HomePageState extends State<HomePage>
|
||||
// Header
|
||||
HeaderBar(),
|
||||
SizedBox(height: AppSpacing.md),
|
||||
// 资产卡片(含预估盈亏)
|
||||
// 資產卡片(含預估盈虧)
|
||||
_AssetCard(
|
||||
overview: provider.overview,
|
||||
onDeposit: _showDeposit,
|
||||
),
|
||||
SizedBox(height: AppSpacing.md),
|
||||
// 快捷操作栏
|
||||
// 快捷操作欄
|
||||
QuickActionsRow(
|
||||
onDeposit: _showDeposit,
|
||||
onWithdraw: _showWithdraw,
|
||||
@@ -129,10 +129,10 @@ class _HomePageState extends State<HomePage>
|
||||
),
|
||||
),
|
||||
SizedBox(height: AppSpacing.lg),
|
||||
// 热门币种
|
||||
// 熱門幣種
|
||||
HotCoinsSection(),
|
||||
SizedBox(height: AppSpacing.lg),
|
||||
// 持仓
|
||||
// 持倉
|
||||
_HoldingsSection(holdings: provider.holdings),
|
||||
],
|
||||
),
|
||||
@@ -174,7 +174,7 @@ class _HomePageState extends State<HomePage>
|
||||
}
|
||||
}
|
||||
|
||||
/// 资产卡片(含预估盈亏)
|
||||
/// 資產卡片(含預估盈虧)
|
||||
class _AssetCard extends StatefulWidget {
|
||||
final AssetOverview? overview;
|
||||
final VoidCallback onDeposit;
|
||||
@@ -197,7 +197,7 @@ class _AssetCardState extends State<_AssetCard> {
|
||||
@override
|
||||
void didUpdateWidget(covariant _AssetCard oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
// overview 更新时重新加载盈亏数据
|
||||
// overview 更新時重新加載盈虧數據
|
||||
if (widget.overview != oldWidget.overview) {
|
||||
_loadProfit();
|
||||
}
|
||||
@@ -239,7 +239,7 @@ class _AssetCardState extends State<_AssetCard> {
|
||||
final upColor = context.appColors.up;
|
||||
final downColor = context.appColors.down;
|
||||
|
||||
// 总资产
|
||||
// 總資產
|
||||
final totalAsset = widget.overview?.totalAsset ?? '0.00';
|
||||
final displayAsset = _formatAsset(totalAsset);
|
||||
|
||||
@@ -249,12 +249,12 @@ class _AssetCardState extends State<_AssetCard> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 顶部行:总资产标签 + 充值按钮
|
||||
// 頂部行:總資產標籤 + 充值按鈕
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'预估总资产(USDT)',
|
||||
'預估總資產(USDT)',
|
||||
style: AppTextStyles.bodyLarge(context),
|
||||
),
|
||||
GestureDetector(
|
||||
@@ -284,7 +284,7 @@ class _AssetCardState extends State<_AssetCard> {
|
||||
),
|
||||
SizedBox(height: AppSpacing.sm),
|
||||
|
||||
// 总资产金额
|
||||
// 總資產金額
|
||||
Text(
|
||||
displayAsset,
|
||||
style: AppTextStyles.displayLarge(context).copyWith(
|
||||
@@ -293,13 +293,13 @@ class _AssetCardState extends State<_AssetCard> {
|
||||
),
|
||||
SizedBox(height: AppSpacing.md),
|
||||
|
||||
// 盈亏统计区:预估今日盈亏 | 预估总盈亏
|
||||
// 盈虧統計區:預估今日盈虧 | 預估總盈虧
|
||||
Row(
|
||||
children: [
|
||||
// 预估今日盈亏
|
||||
// 預估今日盈虧
|
||||
Expanded(
|
||||
child: _ProfitStatCard(
|
||||
label: '预估今日盈亏',
|
||||
label: '預估今日盈虧',
|
||||
value: _todayProfit,
|
||||
upColor: upColor,
|
||||
downColor: downColor,
|
||||
@@ -310,10 +310,10 @@ class _AssetCardState extends State<_AssetCard> {
|
||||
),
|
||||
),
|
||||
SizedBox(width: AppSpacing.sm),
|
||||
// 预估总盈亏
|
||||
// 預估總盈虧
|
||||
Expanded(
|
||||
child: _ProfitStatCard(
|
||||
label: '预估总盈亏',
|
||||
label: '預估總盈虧',
|
||||
value: _totalProfit,
|
||||
upColor: upColor,
|
||||
downColor: downColor,
|
||||
@@ -357,7 +357,7 @@ class _WelfareCard extends StatelessWidget {
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
// 左侧图标
|
||||
// 左側圖標
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
@@ -372,7 +372,7 @@ class _WelfareCard extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
SizedBox(width: AppSpacing.md),
|
||||
// 中间文字
|
||||
// 中間文字
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -386,14 +386,14 @@ class _WelfareCard extends StatelessWidget {
|
||||
SizedBox(height: AppSpacing.xs),
|
||||
Text(
|
||||
totalClaimable > 0
|
||||
? '您有 $totalClaimable 个奖励待领取'
|
||||
: '首充奖励 + 推广奖励',
|
||||
? '您有 $totalClaimable 個獎勵待領取'
|
||||
: '首充獎勵 + 推廣獎勵',
|
||||
style: AppTextStyles.bodyMedium(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// 右侧按钮
|
||||
// 右側按鈕
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
@@ -440,7 +440,7 @@ class _WelfareCard extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 持仓部分
|
||||
/// 持倉部分
|
||||
class _HoldingsSection extends StatelessWidget {
|
||||
final List<AccountTrade> holdings;
|
||||
|
||||
@@ -454,7 +454,7 @@ class _HoldingsSection extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'我的持仓',
|
||||
'我的持倉',
|
||||
style: AppTextStyles.headlineLarge(context).copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
@@ -467,7 +467,7 @@ class _HoldingsSection extends StatelessWidget {
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Text('资产详情',
|
||||
Text('資產詳情',
|
||||
style: AppTextStyles.headlineSmall(context).copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
)),
|
||||
@@ -486,7 +486,7 @@ class _HoldingsSection extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 空持仓
|
||||
/// 空持倉
|
||||
class _EmptyHoldings extends StatelessWidget {
|
||||
const _EmptyHoldings();
|
||||
|
||||
@@ -505,7 +505,7 @@ class _EmptyHoldings extends StatelessWidget {
|
||||
Icon(LucideIcons.wallet, size: 48, color: context.colors.onSurfaceVariant),
|
||||
SizedBox(height: AppSpacing.md),
|
||||
Text(
|
||||
'暂无持仓',
|
||||
'暫無持倉',
|
||||
style: AppTextStyles.headlineMedium(context).copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
@@ -521,7 +521,7 @@ class _EmptyHoldings extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 持仓列表
|
||||
/// 持倉列表
|
||||
class _HoldingsList extends StatelessWidget {
|
||||
final List<AccountTrade> holdings;
|
||||
|
||||
@@ -554,7 +554,7 @@ class _HoldingsList extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 持仓项
|
||||
/// 持倉項
|
||||
class _HoldingItem extends StatelessWidget {
|
||||
final AccountTrade holding;
|
||||
|
||||
@@ -615,7 +615,7 @@ class _HoldingItem extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 盈亏统计小卡片
|
||||
/// 盈虧統計小卡片
|
||||
class _ProfitStatCard extends StatelessWidget {
|
||||
final String label;
|
||||
final double value;
|
||||
|
||||
@@ -4,7 +4,7 @@ import '../../../core/theme/app_theme.dart';
|
||||
import '../../../core/theme/app_spacing.dart';
|
||||
import '../../../core/theme/app_theme_extension.dart';
|
||||
|
||||
/// 首页热门币种区块
|
||||
/// 首頁熱門幣種區塊
|
||||
class HotCoinsSection extends StatelessWidget {
|
||||
const HotCoinsSection({super.key});
|
||||
|
||||
@@ -19,7 +19,7 @@ class HotCoinsSection extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'热门币种',
|
||||
'熱門幣種',
|
||||
style: AppTextStyles.headlineLarge(context),
|
||||
),
|
||||
Text(
|
||||
|
||||
@@ -6,7 +6,7 @@ import '../../../core/theme/app_spacing.dart';
|
||||
import '../../../core/theme/app_theme_extension.dart';
|
||||
import '../../../data/services/asset_service.dart';
|
||||
|
||||
/// 盈亏分析页面 - 月度盈亏日历
|
||||
/// 盈虧分析頁面 - 月度盈虧日曆
|
||||
class ProfitAnalysisPage extends StatefulWidget {
|
||||
const ProfitAnalysisPage({super.key});
|
||||
|
||||
@@ -27,7 +27,7 @@ class _ProfitAnalysisPageState extends State<ProfitAnalysisPage> {
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 数据加载
|
||||
// 數據加載
|
||||
// ============================================
|
||||
|
||||
Future<void> _loadProfit() async {
|
||||
@@ -64,7 +64,7 @@ class _ProfitAnalysisPageState extends State<ProfitAnalysisPage> {
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 盈亏数据解析
|
||||
// 盈虧數據解析
|
||||
// ============================================
|
||||
|
||||
double? _getDayProfit(int day) {
|
||||
@@ -86,7 +86,7 @@ class _ProfitAnalysisPageState extends State<ProfitAnalysisPage> {
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 构建 UI
|
||||
// 構建 UI
|
||||
// ============================================
|
||||
|
||||
@override
|
||||
@@ -98,7 +98,7 @@ class _ProfitAnalysisPageState extends State<ProfitAnalysisPage> {
|
||||
return Scaffold(
|
||||
backgroundColor: context.colors.surface,
|
||||
appBar: AppBar(
|
||||
title: const Text('盈亏分析'),
|
||||
title: const Text('盈虧分析'),
|
||||
backgroundColor: context.colors.surface,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
@@ -116,19 +116,19 @@ class _ProfitAnalysisPageState extends State<ProfitAnalysisPage> {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// 月度盈亏摘要
|
||||
// 月度盈虧摘要
|
||||
_buildSummarySection(),
|
||||
SizedBox(height: AppSpacing.md),
|
||||
|
||||
// 月份导航
|
||||
// 月份導航
|
||||
_buildMonthNavigation(isCurrentMonth),
|
||||
SizedBox(height: AppSpacing.sm),
|
||||
|
||||
// 星期标题
|
||||
// 星期標題
|
||||
_buildWeekdayHeaders(),
|
||||
SizedBox(height: AppSpacing.xs),
|
||||
|
||||
// 日历网格
|
||||
// 日曆網格
|
||||
if (_isLoading)
|
||||
_buildLoadingIndicator()
|
||||
else
|
||||
@@ -143,7 +143,7 @@ class _ProfitAnalysisPageState extends State<ProfitAnalysisPage> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 月度盈亏摘要
|
||||
/// 月度盈虧摘要
|
||||
Widget _buildSummarySection() {
|
||||
final upColor = context.appColors.up;
|
||||
final downColor = context.appColors.down;
|
||||
@@ -153,7 +153,7 @@ class _ProfitAnalysisPageState extends State<ProfitAnalysisPage> {
|
||||
return Column(
|
||||
children: [
|
||||
Text(
|
||||
'月度盈亏',
|
||||
'月度盈虧',
|
||||
style: AppTextStyles.bodyMedium(context).copyWith(
|
||||
color: context.colors.onSurfaceVariant,
|
||||
),
|
||||
@@ -172,7 +172,7 @@ class _ProfitAnalysisPageState extends State<ProfitAnalysisPage> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 月份导航行
|
||||
/// 月份導航行
|
||||
Widget _buildMonthNavigation(bool isCurrentMonth) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@@ -193,14 +193,14 @@ class _ProfitAnalysisPageState extends State<ProfitAnalysisPage> {
|
||||
),
|
||||
),
|
||||
),
|
||||
// 当前年月
|
||||
// 當前年月
|
||||
Text(
|
||||
'${_currentMonth.year}年${_currentMonth.month}月',
|
||||
style: AppTextStyles.headlineMedium(context).copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
// 下一月(当前月禁用)
|
||||
// 下一月(當前月禁用)
|
||||
GestureDetector(
|
||||
onTap: isCurrentMonth ? null : _nextMonth,
|
||||
child: Container(
|
||||
@@ -224,7 +224,7 @@ class _ProfitAnalysisPageState extends State<ProfitAnalysisPage> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 星期标题行
|
||||
/// 星期標題行
|
||||
Widget _buildWeekdayHeaders() {
|
||||
return Row(
|
||||
children: ['一', '二', '三', '四', '五', '六', '日'].map((d) {
|
||||
@@ -243,7 +243,7 @@ class _ProfitAnalysisPageState extends State<ProfitAnalysisPage> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 加载指示器
|
||||
/// 加載指示器
|
||||
Widget _buildLoadingIndicator() {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: AppSpacing.xxl),
|
||||
@@ -260,7 +260,7 @@ class _ProfitAnalysisPageState extends State<ProfitAnalysisPage> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 日历网格
|
||||
/// 日曆網格
|
||||
List<Widget> _buildCalendarGrid(
|
||||
DateTime now,
|
||||
bool isCurrentMonth,
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
import '../../../core/theme/app_theme.dart';
|
||||
import '../../../core/theme/app_spacing.dart';
|
||||
|
||||
/// 首页快捷操作栏 - 充值/提现/划转/盈亏/账单
|
||||
/// 首頁快捷操作欄 - 充值/提現/劃轉/盈虧/賬單
|
||||
class QuickActionsRow extends StatelessWidget {
|
||||
const QuickActionsRow({
|
||||
super.key,
|
||||
@@ -56,25 +56,25 @@ class QuickActionsRow extends StatelessWidget {
|
||||
),
|
||||
_ActionItem(
|
||||
icon: LucideIcons.arrowDownLeft,
|
||||
label: '提现',
|
||||
label: '提現',
|
||||
colorScheme: colorScheme,
|
||||
onTap: onWithdraw,
|
||||
),
|
||||
_ActionItem(
|
||||
icon: LucideIcons.repeat,
|
||||
label: '划转',
|
||||
label: '劃轉',
|
||||
colorScheme: colorScheme,
|
||||
onTap: onTransfer,
|
||||
),
|
||||
_ActionItem(
|
||||
icon: LucideIcons.chartPie,
|
||||
label: '盈亏',
|
||||
label: '盈虧',
|
||||
colorScheme: colorScheme,
|
||||
onTap: onProfit,
|
||||
),
|
||||
_ActionItem(
|
||||
icon: LucideIcons.fileText,
|
||||
label: '账单',
|
||||
label: '賬單',
|
||||
colorScheme: colorScheme,
|
||||
onTap: onBills,
|
||||
),
|
||||
@@ -105,7 +105,7 @@ class _ActionItem extends StatelessWidget {
|
||||
? const Color(0xFF1E293B)
|
||||
: const Color(0xFFF3F4F6);
|
||||
|
||||
// Light: #4B5563, Dark: 根据主题
|
||||
// Light: #4B5563, Dark: 根據主題
|
||||
final iconColor = isDark
|
||||
? colorScheme.onSurfaceVariant
|
||||
: const Color(0xFF4B5563);
|
||||
|
||||
@@ -12,7 +12,7 @@ import '../trade/trade_page.dart';
|
||||
import '../asset/asset_page.dart';
|
||||
import '../mine/mine_page.dart';
|
||||
|
||||
/// 底部导航项
|
||||
/// 底部導航項
|
||||
class _NavItem {
|
||||
final String label;
|
||||
final IconData icon;
|
||||
@@ -20,7 +20,7 @@ class _NavItem {
|
||||
const _NavItem({required this.label, required this.icon});
|
||||
}
|
||||
|
||||
/// 主页面 - "The Kinetic Vault" 设计风格
|
||||
/// 主頁面 - "The Kinetic Vault" 設計風格
|
||||
class MainPage extends StatefulWidget {
|
||||
const MainPage({super.key});
|
||||
|
||||
@@ -31,10 +31,10 @@ class MainPage extends StatefulWidget {
|
||||
class MainPageState extends State<MainPage> {
|
||||
int _currentIndex = 0;
|
||||
final Set<int> _loadedPages = {0};
|
||||
String? _tradeCoinCode; // 交易页面选中的币种代码
|
||||
String? _tradeCoinCode; // 交易頁面選中的幣種代碼
|
||||
late final List<Widget> _pages;
|
||||
|
||||
// 防抖:记录上次刷新时间,同一 Tab 500ms 内不重复刷新
|
||||
// 防抖:記錄上次刷新時間,同一 Tab 500ms 內不重複刷新
|
||||
final Map<int, DateTime> _lastRefreshTime = {};
|
||||
|
||||
@override
|
||||
@@ -55,13 +55,13 @@ class MainPageState extends State<MainPage> {
|
||||
_currentIndex = index;
|
||||
_loadedPages.add(index);
|
||||
});
|
||||
// 切换到已加载的 Tab 时刷新数据
|
||||
// 切換到已加載的 Tab 時刷新數據
|
||||
if (wasLoaded) {
|
||||
_refreshTab(index);
|
||||
}
|
||||
}
|
||||
|
||||
/// 刷新对应 Tab 的数据(带防抖)
|
||||
/// 刷新對應 Tab 的數據(帶防抖)
|
||||
void _refreshTab(int index) {
|
||||
final now = DateTime.now();
|
||||
final last = _lastRefreshTime[index];
|
||||
@@ -69,30 +69,33 @@ class MainPageState extends State<MainPage> {
|
||||
_lastRefreshTime[index] = now;
|
||||
|
||||
switch (index) {
|
||||
case 0: // 首页 - 刷新资产概览
|
||||
case 0: // 首頁 - 刷新資產概覽
|
||||
context.read<AssetProvider>().loadOverview(force: true);
|
||||
break;
|
||||
case 1: // 行情 - 刷新币种列表
|
||||
case 1: // 行情 - 刷新幣種列表
|
||||
context.read<MarketProvider>().loadCoins(force: true);
|
||||
break;
|
||||
case 3: // 资产 - 刷新全部资产
|
||||
case 3: // 資產 - 刷新全部資產
|
||||
context.read<AssetProvider>().refreshAll(force: true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// 切换到交易页面并选中指定币种
|
||||
/// 切換到交易頁面並選中指定幣種
|
||||
void switchToTrade(String coinCode) {
|
||||
setState(() {
|
||||
_tradeCoinCode = coinCode;
|
||||
_currentIndex = 2; // 交易页面索引
|
||||
_currentIndex = 2; // 交易頁面索引
|
||||
_loadedPages.add(2);
|
||||
// 重新构建交易页面
|
||||
_pages[2] = TradePage(initialCoinCode: _tradeCoinCode);
|
||||
// 重新構建交易頁面(用 Key 強制創建新 State)
|
||||
_pages[2] = TradePage(
|
||||
key: ValueKey(_tradeCoinCode),
|
||||
initialCoinCode: _tradeCoinCode,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// 切换到指定 tab
|
||||
/// 切換到指定 tab
|
||||
void switchToTab(int index) {
|
||||
setState(() {
|
||||
_currentIndex = index;
|
||||
@@ -101,10 +104,10 @@ class MainPageState extends State<MainPage> {
|
||||
}
|
||||
|
||||
static const _navItems = [
|
||||
_NavItem(label: '首页', icon: LucideIcons.house),
|
||||
_NavItem(label: '首頁', icon: LucideIcons.house),
|
||||
_NavItem(label: '行情', icon: LucideIcons.trendingUp),
|
||||
_NavItem(label: '交易', icon: LucideIcons.arrowLeftRight),
|
||||
_NavItem(label: '资产', icon: LucideIcons.wallet),
|
||||
_NavItem(label: '資產', icon: LucideIcons.wallet),
|
||||
_NavItem(label: '我的', icon: LucideIcons.user),
|
||||
];
|
||||
|
||||
@@ -128,7 +131,7 @@ class MainPageState extends State<MainPage> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 底部导航栏 - 专业信任主题
|
||||
/// 底部導航欄 - 專業信任主題
|
||||
class _BottomNavBar extends StatelessWidget {
|
||||
final List<_NavItem> items;
|
||||
final int currentIndex;
|
||||
@@ -175,7 +178,7 @@ class _BottomNavBar extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 导航项组件
|
||||
/// 導航項組件
|
||||
class _NavItemWidget extends StatelessWidget {
|
||||
final _NavItem item;
|
||||
final bool isSelected;
|
||||
@@ -220,7 +223,7 @@ class _NavItemWidget extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 懒加载 IndexedStack - 只渲染已访问过的页面
|
||||
/// 懶加載 IndexedStack - 只渲染已訪問過的頁面
|
||||
class LazyIndexedStack extends StatefulWidget {
|
||||
final int index;
|
||||
final Set<int> loadedIndexes;
|
||||
|
||||
@@ -10,7 +10,7 @@ import '../../../providers/market_provider.dart';
|
||||
import '../../components/glass_panel.dart';
|
||||
import '../main/main_page.dart';
|
||||
|
||||
/// 行情页面
|
||||
/// 行情頁面
|
||||
class MarketPage extends StatefulWidget {
|
||||
const MarketPage({super.key});
|
||||
|
||||
@@ -62,7 +62,7 @@ class _MarketPageState extends State<MarketPage>
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 页面标题 "行情"
|
||||
// 頁面標題 "行情"
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: AppSpacing.sm),
|
||||
child: Text(
|
||||
@@ -71,13 +71,13 @@ class _MarketPageState extends State<MarketPage>
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
// 精选区域:BTC + ETH 卡片
|
||||
// 精選區域:BTC + ETH 卡片
|
||||
_buildFeaturedSection(provider),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
// 分区标题:全部币种 + 更多
|
||||
// 分區標題:全部幣種 + 更多
|
||||
_buildSectionHeader(),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
// 币种列表卡片
|
||||
// 幣種列表卡片
|
||||
_buildCoinList(provider),
|
||||
],
|
||||
),
|
||||
@@ -88,7 +88,7 @@ class _MarketPageState extends State<MarketPage>
|
||||
);
|
||||
}
|
||||
|
||||
/// 精选区域:BTC + ETH 大卡片
|
||||
/// 精選區域:BTC + ETH 大卡片
|
||||
Widget _buildFeaturedSection(MarketProvider provider) {
|
||||
final featured = provider.featuredCoins;
|
||||
if (featured.isEmpty) return const SizedBox.shrink();
|
||||
@@ -111,13 +111,13 @@ class _MarketPageState extends State<MarketPage>
|
||||
);
|
||||
}
|
||||
|
||||
/// 分区标题:全部币种 + 更多
|
||||
/// 分區標題:全部幣種 + 更多
|
||||
Widget _buildSectionHeader() {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'全部币种',
|
||||
'全部幣種',
|
||||
style: AppTextStyles.headlineLarge(context),
|
||||
),
|
||||
Text(
|
||||
@@ -130,14 +130,14 @@ class _MarketPageState extends State<MarketPage>
|
||||
);
|
||||
}
|
||||
|
||||
/// 币种列表
|
||||
/// 幣種列表
|
||||
Widget _buildCoinList(MarketProvider provider) {
|
||||
final coins = provider.otherCoins;
|
||||
|
||||
if (coins.isEmpty) {
|
||||
return _EmptyState(
|
||||
icon: LucideIcons.coins,
|
||||
message: '暂无数据',
|
||||
message: '暫無數據',
|
||||
onRetry: () => provider.refresh(),
|
||||
);
|
||||
}
|
||||
@@ -167,7 +167,7 @@ class _MarketPageState extends State<MarketPage>
|
||||
);
|
||||
}
|
||||
|
||||
/// 错误状态
|
||||
/// 錯誤狀態
|
||||
Widget _buildErrorState(MarketProvider provider) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
@@ -178,14 +178,14 @@ class _MarketPageState extends State<MarketPage>
|
||||
Icon(LucideIcons.circleAlert, size: 48, color: context.colors.error),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
Text(
|
||||
provider.error ?? '加载失败',
|
||||
provider.error ?? '加載失敗',
|
||||
style: TextStyle(color: context.colors.error),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
ShadButton(
|
||||
onPressed: () => provider.refresh(),
|
||||
child: const Text('重试'),
|
||||
child: const Text('重試'),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -194,7 +194,7 @@ class _MarketPageState extends State<MarketPage>
|
||||
}
|
||||
}
|
||||
|
||||
/// 精选卡片:BTC / ETH (130px 高度,含迷你柱状图)
|
||||
/// 精選卡片:BTC / ETH (130px 高度,含迷你柱狀圖)
|
||||
class _FeaturedCard extends StatelessWidget {
|
||||
final Coin coin;
|
||||
|
||||
@@ -216,13 +216,13 @@ class _FeaturedCard extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
// 第一行:币种名称 + 涨跌徽章
|
||||
// 第一行:幣種名稱 + 漲跌徽章
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'${coin.code}/USDT',
|
||||
style: AppTextStyles.labelLarge(context), // 缩小文字
|
||||
style: AppTextStyles.labelLarge(context), // 縮小文字
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xs + 2, vertical: 2),
|
||||
@@ -234,27 +234,27 @@ class _FeaturedCard extends StatelessWidget {
|
||||
coin.formattedChange,
|
||||
style: AppTextStyles.labelSmall(context).copyWith(
|
||||
color: changeColor,
|
||||
fontSize: 10, // 缩小文字
|
||||
fontSize: 10, // 縮小文字
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// 第二行:价格
|
||||
// 第二行:價格
|
||||
Text(
|
||||
'\$${_formatFeaturedPrice(coin)}',
|
||||
style: AppTextStyles.headlineLarge(context).copyWith( // 缩小文字
|
||||
style: AppTextStyles.headlineLarge(context).copyWith( // 縮小文字
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
// 第三行:币种全名
|
||||
// 第三行:幣種全名
|
||||
Text(
|
||||
coin.name,
|
||||
style: AppTextStyles.bodySmall(context).copyWith( // 缩小文字
|
||||
style: AppTextStyles.bodySmall(context).copyWith( // 縮小文字
|
||||
color: context.colors.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
// 第四行:迷你柱状图
|
||||
// 第四行:迷你柱狀圖
|
||||
Expanded(
|
||||
child: _MiniBarChart(isUp: isUp, seed: coin.code.hashCode),
|
||||
),
|
||||
@@ -263,7 +263,7 @@ class _FeaturedCard extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
/// 精选卡片使用简短价格格式(带逗号)
|
||||
/// 精選卡片使用簡短價格格式(帶逗號)
|
||||
String _formatFeaturedPrice(Coin coin) {
|
||||
if (coin.price >= 1000) {
|
||||
return _addCommas(coin.price.toStringAsFixed(2));
|
||||
@@ -288,7 +288,7 @@ class _FeaturedCard extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 迷你柱状图(模拟价格走势)
|
||||
/// 迷你柱狀圖(模擬價格走勢)
|
||||
class _MiniBarChart extends StatelessWidget {
|
||||
final bool isUp;
|
||||
final int seed;
|
||||
@@ -301,7 +301,7 @@ class _MiniBarChart extends StatelessWidget {
|
||||
? context.appColors.up
|
||||
: context.appColors.down;
|
||||
|
||||
// 生成随机但确定的高度序列
|
||||
// 生成隨機但確定的高度序列
|
||||
final heights = _generateHeights();
|
||||
|
||||
return Row(
|
||||
@@ -310,7 +310,7 @@ class _MiniBarChart extends StatelessWidget {
|
||||
children: heights.map((h) {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 2), // 增加间距
|
||||
padding: const EdgeInsets.only(left: 2), // 增加間距
|
||||
child: Container(
|
||||
height: h,
|
||||
decoration: BoxDecoration(
|
||||
@@ -326,13 +326,13 @@ class _MiniBarChart extends StatelessWidget {
|
||||
|
||||
List<double> _generateHeights() {
|
||||
final random = Random(seed);
|
||||
final base = 6.0; // 降低基础高度
|
||||
final range = 12.0; // 降低范围
|
||||
final base = 6.0; // 降低基礎高度
|
||||
final range = 12.0; // 降低範圍
|
||||
return List.generate(6, (_) => base + random.nextDouble() * range);
|
||||
}
|
||||
}
|
||||
|
||||
/// 币种列表行
|
||||
/// 幣種列表行
|
||||
class _CoinRow extends StatelessWidget {
|
||||
final Coin coin;
|
||||
|
||||
@@ -354,10 +354,10 @@ class _CoinRow extends StatelessWidget {
|
||||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md, vertical: 14),
|
||||
child: Row(
|
||||
children: [
|
||||
// 头像:圆形字母头像
|
||||
// 頭像:圓形字母頭像
|
||||
_CoinAvatar(letter: coin.displayIcon, code: coin.code),
|
||||
const SizedBox(width: AppSpacing.sm + AppSpacing.xs),
|
||||
// 币种信息:交易对 + 全名
|
||||
// 幣種信息:交易對 + 全名
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -375,7 +375,7 @@ class _CoinRow extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
// 右侧:价格 + 涨跌标签
|
||||
// 右側:價格 + 漲跌標籤
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -412,7 +412,7 @@ class _CoinRow extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 币种头像组件
|
||||
/// 幣種頭像組件
|
||||
class _CoinAvatar extends StatelessWidget {
|
||||
final String letter;
|
||||
final String code;
|
||||
@@ -421,7 +421,7 @@ class _CoinAvatar extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// 使用主题变量而非硬编码
|
||||
// 使用主題變量而非硬編碼
|
||||
final bgColor = context.colors.primary.withValues(alpha: context.appColors.glowOpacity);
|
||||
|
||||
return Container(
|
||||
@@ -456,7 +456,7 @@ class _CoinAvatar extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 空状态
|
||||
/// 空狀態
|
||||
class _EmptyState extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String message;
|
||||
@@ -481,7 +481,7 @@ class _EmptyState extends StatelessWidget {
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
ShadButton(
|
||||
onPressed: onRetry,
|
||||
child: const Text('重试'),
|
||||
child: const Text('重試'),
|
||||
),
|
||||
],
|
||||
],
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import '../../../../core/theme/app_spacing.dart';
|
||||
import '../../../../core/theme/app_theme.dart';
|
||||
|
||||
/// 信息行组件(用于关于对话框)
|
||||
/// 信息行組件(用於關於對話框)
|
||||
class InfoRow extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String text;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// 圆形头像组件
|
||||
/// 圓形頭像組件
|
||||
///
|
||||
/// 显示用户首字母或默认比特币符号。通过 [radius] 控制大小,
|
||||
/// [fontSize] 控制文字大小,[text] 可传入用户头像文字。
|
||||
/// 顯示用戶首字母或默認比特幣符號。通過 [radius] 控制大小,
|
||||
/// [fontSize] 控制文字大小,[text] 可傳入用戶頭像文字。
|
||||
class AvatarCircle extends StatelessWidget {
|
||||
final double radius;
|
||||
final double fontSize;
|
||||
|
||||
@@ -3,7 +3,7 @@ import '../../../../core/theme/app_color_scheme.dart';
|
||||
import '../../../../core/theme/app_spacing.dart';
|
||||
import '../../../../core/theme/app_theme.dart';
|
||||
|
||||
/// 退出登录按钮
|
||||
/// 退出登錄按鈕
|
||||
class LogoutButton extends StatelessWidget {
|
||||
final VoidCallback onLogout;
|
||||
const LogoutButton({super.key, required this.onLogout});
|
||||
@@ -24,7 +24,7 @@ class LogoutButton extends StatelessWidget {
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'退出登录',
|
||||
'退出登錄',
|
||||
style: AppTextStyles.headlineMedium(context).copyWith(
|
||||
color: AppColorScheme.down,
|
||||
),
|
||||
|
||||
@@ -10,7 +10,7 @@ import 'menu_group_container.dart';
|
||||
import 'menu_row.dart';
|
||||
import 'menu_trailing_widgets.dart';
|
||||
|
||||
/// 菜单分组1 - 福利中心 / 实名认证 / 安全设置 / 消息通知
|
||||
/// 菜單分組1 - 福利中心 / 實名認證 / 安全設置 / 消息通知
|
||||
class MenuGroup1 extends StatelessWidget {
|
||||
final int kycStatus;
|
||||
final void Function(String) onShowComingSoon;
|
||||
@@ -39,11 +39,11 @@ class MenuGroup1 extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
const Divider(height: 1),
|
||||
// 实名认证
|
||||
// 實名認證
|
||||
MenuRow(
|
||||
icon: LucideIcons.shieldCheck,
|
||||
iconColor: context.appColors.up,
|
||||
title: '实名认证',
|
||||
title: '實名認證',
|
||||
trailing: KycBadge(kycStatus: kycStatus),
|
||||
onTap: () {
|
||||
if (kycStatus == 2) {
|
||||
@@ -57,12 +57,12 @@ class MenuGroup1 extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
const Divider(height: 1),
|
||||
// 安全设置
|
||||
// 安全設置
|
||||
MenuRow(
|
||||
icon: LucideIcons.lock,
|
||||
iconColor: context.colors.onSurfaceVariant,
|
||||
title: '安全设置',
|
||||
onTap: () => onShowComingSoon('安全设置'),
|
||||
title: '安全設置',
|
||||
onTap: () => onShowComingSoon('安全設置'),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
// 消息通知
|
||||
@@ -79,7 +79,7 @@ class MenuGroup1 extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 显示 KYC 认证状态对话框
|
||||
/// 顯示 KYC 認證狀態對話框
|
||||
void showKycStatusDialog(BuildContext context) {
|
||||
showShadDialog(
|
||||
context: context,
|
||||
@@ -88,13 +88,13 @@ void showKycStatusDialog(BuildContext context) {
|
||||
children: [
|
||||
Icon(Icons.check_circle, color: AppColorScheme.up, size: 20),
|
||||
SizedBox(width: AppSpacing.sm),
|
||||
const Text('实名认证'),
|
||||
const Text('實名認證'),
|
||||
],
|
||||
),
|
||||
description: const Text('您的实名认证已通过'),
|
||||
description: const Text('您的實名認證已通過'),
|
||||
actions: [
|
||||
ShadButton(
|
||||
child: const Text('确定'),
|
||||
child: const Text('確定'),
|
||||
onPressed: () => Navigator.of(ctx).pop(),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'menu_group_container.dart';
|
||||
import 'menu_row.dart';
|
||||
import 'menu_trailing_widgets.dart';
|
||||
|
||||
/// 菜单分组2 - 深色模式 / 系统设置 / 关于我们
|
||||
/// 菜單分組2 - 深色模式 / 系統設置 / 關於我們
|
||||
class MenuGroup2 extends StatelessWidget {
|
||||
final VoidCallback onShowAbout;
|
||||
|
||||
@@ -20,21 +20,21 @@ class MenuGroup2 extends StatelessWidget {
|
||||
// 深色模式
|
||||
const DarkModeRow(),
|
||||
const Divider(height: 1),
|
||||
// 系统设置
|
||||
// 系統設置
|
||||
MenuRow(
|
||||
icon: LucideIcons.settings,
|
||||
iconColor: colorScheme.onSurfaceVariant,
|
||||
title: '系统设置',
|
||||
title: '系統設置',
|
||||
onTap: () {
|
||||
// TODO: 系统设置
|
||||
// TODO: 系統設置
|
||||
},
|
||||
),
|
||||
const Divider(height: 1),
|
||||
// 关于我们
|
||||
// 關於我們
|
||||
MenuRow(
|
||||
icon: LucideIcons.info,
|
||||
iconColor: colorScheme.onSurfaceVariant,
|
||||
title: '关于我们',
|
||||
title: '關於我們',
|
||||
onTap: onShowAbout,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -2,10 +2,10 @@ import 'package:flutter/material.dart';
|
||||
import '../../../../core/theme/app_spacing.dart';
|
||||
import '../../../../core/theme/app_theme_extension.dart';
|
||||
|
||||
/// 菜单分组容器 - 统一的圆角卡片样式
|
||||
/// 菜單分組容器 - 統一的圓角卡片樣式
|
||||
///
|
||||
/// 所有菜单分组共享相同的容器样式:背景色、圆角、边框。
|
||||
/// 通过 [child] 传入菜单项 Column。
|
||||
/// 所有菜單分組共享相同的容器樣式:背景色、圓角、邊框。
|
||||
/// 通過 [child] 傳入菜單項 Column。
|
||||
class MenuGroupContainer extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ import '../../../../core/theme/app_spacing.dart';
|
||||
import '../../../../core/theme/app_theme.dart';
|
||||
import '../../../../core/theme/app_theme_extension.dart';
|
||||
|
||||
/// 单行菜单项:图标 + 标题 + 尾部组件 (chevron)
|
||||
/// 單行菜單項:圖標 + 標題 + 尾部組件 (chevron)
|
||||
///
|
||||
/// 图标颜色 (通常是使用主题色)
|
||||
/// 圖標顏色 (通常是使用主題色)
|
||||
class MenuRow extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final Color iconColor;
|
||||
|
||||
@@ -7,12 +7,12 @@ import '../../../../core/theme/app_theme.dart';
|
||||
import '../../../../core/theme/app_theme_extension.dart';
|
||||
import '../../../../providers/theme_provider.dart';
|
||||
|
||||
/// KYC 状态徽章 (e.g. "已认证" green badge + chevron)
|
||||
/// KYC 狀態徽章 (e.g. "已認證" green badge + chevron)
|
||||
///
|
||||
/// 根据 [kycStatus] 显示不同状态:
|
||||
/// - 2: 已认证(绿色)
|
||||
/// - 1: 审核中(橙色)
|
||||
/// - 其他: 仅显示 chevron
|
||||
/// 根據 [kycStatus] 顯示不同狀態:
|
||||
/// - 2: 已認證(綠色)
|
||||
/// - 1: 審核中(橙色)
|
||||
/// - 其他: 僅顯示 chevron
|
||||
class KycBadge extends StatelessWidget {
|
||||
final int kycStatus;
|
||||
const KycBadge({super.key, required this.kycStatus});
|
||||
@@ -32,7 +32,7 @@ class KycBadge extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(AppRadius.sm),
|
||||
),
|
||||
child: Text(
|
||||
'已认证',
|
||||
'已認證',
|
||||
style: AppTextStyles.labelMedium(context).copyWith(
|
||||
color: green,
|
||||
),
|
||||
@@ -59,7 +59,7 @@ class KycBadge extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(AppRadius.sm),
|
||||
),
|
||||
child: Text(
|
||||
'审核中',
|
||||
'審核中',
|
||||
style: AppTextStyles.labelMedium(context).copyWith(
|
||||
color: AppColorScheme.warning,
|
||||
),
|
||||
@@ -83,7 +83,7 @@ class KycBadge extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 红点指示器 - 消息通知 + chevron
|
||||
/// 紅點指示器 - 消息通知 + chevron
|
||||
class RedDotIndicator extends StatelessWidget {
|
||||
const RedDotIndicator({super.key});
|
||||
|
||||
@@ -111,7 +111,7 @@ class RedDotIndicator extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 深色模式切换行
|
||||
/// 深色模式切換行
|
||||
class DarkModeRow extends StatelessWidget {
|
||||
const DarkModeRow({super.key});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import '../../../../core/theme/app_theme.dart';
|
||||
import '../../../../core/theme/app_theme_extension.dart';
|
||||
import 'avatar_circle.dart';
|
||||
|
||||
/// 用户资料卡片 - 头像 + 用户名 + 徽章 + chevron
|
||||
/// 用戶資料卡片 - 頭像 + 用戶名 + 徽章 + chevron
|
||||
class ProfileCard extends StatelessWidget {
|
||||
final dynamic user;
|
||||
const ProfileCard({super.key, required this.user});
|
||||
@@ -37,12 +37,12 @@ class ProfileCard extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
user?.username ?? '未登录',
|
||||
user?.username ?? '未登錄',
|
||||
style: AppTextStyles.headlineLarge(context),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'普通用户',
|
||||
'普通用戶',
|
||||
style: AppTextStyles.bodyMedium(context).copyWith(
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
|
||||
@@ -11,7 +11,7 @@ import '../../../providers/auth_provider.dart';
|
||||
import '../../components/glass_panel.dart';
|
||||
import '../../components/neon_glow.dart';
|
||||
|
||||
/// KYC 实名认证页面
|
||||
/// KYC 實名認證頁面
|
||||
class KycPage extends StatefulWidget {
|
||||
final bool returnToWithdraw;
|
||||
|
||||
@@ -64,7 +64,7 @@ class _KycPageState extends State<KycPage> {
|
||||
backgroundColor: colorScheme.surface.withOpacity(0.0),
|
||||
elevation: 0,
|
||||
title: Text(
|
||||
'实名认证',
|
||||
'實名認證',
|
||||
style: AppTextStyles.headlineLarge(context),
|
||||
),
|
||||
leading: IconButton(
|
||||
@@ -77,17 +77,17 @@ class _KycPageState extends State<KycPage> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 步骤指示器
|
||||
// 步驟指示器
|
||||
_buildStepIndicator(colorScheme),
|
||||
SizedBox(height: AppSpacing.xl),
|
||||
|
||||
// 主表单区
|
||||
// 主表單區
|
||||
GlassPanel(
|
||||
padding: EdgeInsets.all(AppSpacing.lg),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 标题区
|
||||
// 標題區
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
@@ -107,12 +107,12 @@ class _KycPageState extends State<KycPage> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'身份验证',
|
||||
'身份驗證',
|
||||
style: AppTextStyles.headlineLarge(context),
|
||||
),
|
||||
SizedBox(height: 2),
|
||||
Text(
|
||||
'上传身份证正反面完成实名认证',
|
||||
'上傳身份證正反面完成實名認證',
|
||||
style: AppTextStyles.bodyMedium(context),
|
||||
),
|
||||
],
|
||||
@@ -121,9 +121,9 @@ class _KycPageState extends State<KycPage> {
|
||||
),
|
||||
SizedBox(height: AppSpacing.xl),
|
||||
|
||||
// 身份证正面上传区
|
||||
// 身份證正面上傳區
|
||||
Text(
|
||||
'身份证正面(人像面)',
|
||||
'身份證正面(人像面)',
|
||||
style: AppTextStyles.headlineSmall(context),
|
||||
),
|
||||
SizedBox(height: AppSpacing.sm),
|
||||
@@ -136,26 +136,26 @@ class _KycPageState extends State<KycPage> {
|
||||
),
|
||||
SizedBox(height: AppSpacing.lg),
|
||||
|
||||
// 身份证反面上传区
|
||||
// 身份證反面上傳區
|
||||
Text(
|
||||
'身份证反面(国徽面)',
|
||||
'身份證反面(國徽面)',
|
||||
style: AppTextStyles.headlineSmall(context),
|
||||
),
|
||||
SizedBox(height: AppSpacing.sm),
|
||||
_buildUploadZone(
|
||||
imageFile: _backFile,
|
||||
imageBytes: _backBytes,
|
||||
label: '国徽面',
|
||||
label: '國徽面',
|
||||
onTap: () => _pickImage(false),
|
||||
colorScheme: colorScheme,
|
||||
),
|
||||
SizedBox(height: AppSpacing.xl),
|
||||
|
||||
// 提交按钮
|
||||
// 提交按鈕
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: NeonButton(
|
||||
text: _isSubmitting ? '提交中...' : '提交认证',
|
||||
text: _isSubmitting ? '提交中...' : '提交認證',
|
||||
type: NeonButtonType.primary,
|
||||
onPressed: _canSubmit ? _submitKyc : null,
|
||||
height: 48,
|
||||
@@ -187,7 +187,7 @@ class _KycPageState extends State<KycPage> {
|
||||
SizedBox(width: AppSpacing.sm),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'您的身份信息将被加密存储,仅用于身份验证',
|
||||
'您的身份信息將被加密存儲,僅用於身份驗證',
|
||||
style: AppTextStyles.bodySmall(context).copyWith(
|
||||
color: AppColorScheme.up.withOpacity(0.8),
|
||||
),
|
||||
@@ -209,7 +209,7 @@ class _KycPageState extends State<KycPage> {
|
||||
children: [
|
||||
_buildStepCircle(
|
||||
number: '1',
|
||||
label: '上传证件',
|
||||
label: '上傳證件',
|
||||
isActive: true,
|
||||
isComplete: isComplete,
|
||||
colorScheme: colorScheme,
|
||||
@@ -224,7 +224,7 @@ class _KycPageState extends State<KycPage> {
|
||||
),
|
||||
_buildStepCircle(
|
||||
number: '2',
|
||||
label: '认证完成',
|
||||
label: '認證完成',
|
||||
isActive: false,
|
||||
isComplete: false,
|
||||
colorScheme: colorScheme,
|
||||
@@ -317,12 +317,12 @@ class _KycPageState extends State<KycPage> {
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
// 图片预览 - 使用 memory 以兼容 Web
|
||||
// 圖片預覽 - 使用 memory 以兼容 Web
|
||||
Image.memory(
|
||||
imageBytes!,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
// 底部渐变遮罩 + 文字
|
||||
// 底部漸變遮罩 + 文字
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
@@ -348,7 +348,7 @@ class _KycPageState extends State<KycPage> {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'$label已选择',
|
||||
'$label已選擇',
|
||||
style: AppTextStyles.labelLarge(context).copyWith(
|
||||
color: colorScheme.onPrimary,
|
||||
),
|
||||
@@ -400,7 +400,7 @@ class _KycPageState extends State<KycPage> {
|
||||
),
|
||||
SizedBox(height: AppSpacing.sm),
|
||||
Text(
|
||||
'点击上传$label',
|
||||
'點擊上傳$label',
|
||||
style: AppTextStyles.bodyLarge(context).copyWith(
|
||||
color: colorScheme.onSurfaceVariant.withOpacity(0.6),
|
||||
),
|
||||
@@ -442,13 +442,13 @@ class _KycPageState extends State<KycPage> {
|
||||
size: 20,
|
||||
),
|
||||
SizedBox(width: AppSpacing.sm),
|
||||
const Text('认证成功'),
|
||||
const Text('認證成功'),
|
||||
],
|
||||
),
|
||||
description: const Text('您的实名认证已通过,现在可以进行提现操作'),
|
||||
description: const Text('您的實名認證已通過,現在可以進行提現操作'),
|
||||
actions: [
|
||||
ShadButton(
|
||||
child: const Text('确定'),
|
||||
child: const Text('確定'),
|
||||
onPressed: () {
|
||||
Navigator.of(ctx).pop();
|
||||
Navigator.of(context).pop();
|
||||
@@ -461,11 +461,11 @@ class _KycPageState extends State<KycPage> {
|
||||
showShadDialog(
|
||||
context: context,
|
||||
builder: (ctx) => ShadDialog.alert(
|
||||
title: const Text('认证失败'),
|
||||
description: Text(response.message ?? '请稍后重试'),
|
||||
title: const Text('認證失敗'),
|
||||
description: Text(response.message ?? '請稍後重試'),
|
||||
actions: [
|
||||
ShadButton(
|
||||
child: const Text('确定'),
|
||||
child: const Text('確定'),
|
||||
onPressed: () => Navigator.of(ctx).pop(),
|
||||
),
|
||||
],
|
||||
@@ -477,11 +477,11 @@ class _KycPageState extends State<KycPage> {
|
||||
showShadDialog(
|
||||
context: context,
|
||||
builder: (ctx) => ShadDialog.alert(
|
||||
title: const Text('认证失败'),
|
||||
title: const Text('認證失敗'),
|
||||
description: Text(e.toString()),
|
||||
actions: [
|
||||
ShadButton(
|
||||
child: const Text('确定'),
|
||||
child: const Text('確定'),
|
||||
onPressed: () => Navigator.of(ctx).pop(),
|
||||
),
|
||||
],
|
||||
@@ -494,7 +494,7 @@ class _KycPageState extends State<KycPage> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 虚线边框画笔
|
||||
/// 虛線邊框畫筆
|
||||
class _DashedBorderPainter extends CustomPainter {
|
||||
final Color color;
|
||||
final double borderRadius;
|
||||
|
||||
@@ -13,7 +13,7 @@ import 'components/menu_group1.dart';
|
||||
import 'components/menu_group2.dart';
|
||||
import 'components/profile_card.dart';
|
||||
|
||||
/// 我的页面 - 匹配 .pen 设计稿
|
||||
/// 我的頁面 - 匹配 .pen 設計稿
|
||||
class MinePage extends StatefulWidget {
|
||||
const MinePage({super.key});
|
||||
|
||||
@@ -77,10 +77,10 @@ class _MinePageState extends State<MinePage>
|
||||
children: [
|
||||
Icon(Icons.construction, color: AppColorScheme.warning, size: 20),
|
||||
SizedBox(width: AppSpacing.sm),
|
||||
const Text('功能开发中'),
|
||||
const Text('功能開發中'),
|
||||
],
|
||||
),
|
||||
description: Text('$feature功能正在开发中,敬请期待~'),
|
||||
description: Text('$feature功能正在開發中,敬請期待~'),
|
||||
actions: [
|
||||
ShadButton(
|
||||
child: const Text('知道了'),
|
||||
@@ -100,7 +100,7 @@ class _MinePageState extends State<MinePage>
|
||||
children: [
|
||||
AvatarCircle(radius: 20, fontSize: 16),
|
||||
SizedBox(width: AppSpacing.sm + AppSpacing.xs),
|
||||
const Text('模拟所'),
|
||||
const Text('模擬所'),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
@@ -108,7 +108,7 @@ class _MinePageState extends State<MinePage>
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'虚拟货币模拟交易平台',
|
||||
'虛擬貨幣模擬交易平臺',
|
||||
style: TextStyle(color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
SizedBox(height: AppSpacing.md),
|
||||
@@ -121,7 +121,7 @@ class _MinePageState extends State<MinePage>
|
||||
),
|
||||
actions: [
|
||||
ShadButton(
|
||||
child: const Text('确定'),
|
||||
child: const Text('確定'),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
],
|
||||
@@ -133,8 +133,8 @@ class _MinePageState extends State<MinePage>
|
||||
showShadDialog(
|
||||
context: context,
|
||||
builder: (ctx) => ShadDialog.alert(
|
||||
title: const Text('确认退出'),
|
||||
description: const Text('确定要退出登录吗?'),
|
||||
title: const Text('確認退出'),
|
||||
description: const Text('確定要退出登錄嗎?'),
|
||||
actions: [
|
||||
ShadButton.outline(
|
||||
child: const Text('取消'),
|
||||
|
||||
@@ -11,7 +11,7 @@ import '../../../core/event/app_event_bus.dart';
|
||||
import '../../../data/services/bonus_service.dart';
|
||||
import '../../../providers/asset_provider.dart';
|
||||
|
||||
/// 福利中心页面
|
||||
/// 福利中心頁面
|
||||
class WelfareCenterPage extends StatefulWidget {
|
||||
const WelfareCenterPage({super.key});
|
||||
|
||||
@@ -45,10 +45,10 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 容器样式辅助
|
||||
// 容器樣式輔助
|
||||
// ============================================
|
||||
|
||||
/// 标准卡片容器
|
||||
/// 標準卡片容器
|
||||
BoxDecoration _cardDecoration({Color? borderColor}) {
|
||||
return BoxDecoration(
|
||||
color: context.appColors.surfaceCard,
|
||||
@@ -59,11 +59,11 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 金色渐变卡片容器
|
||||
/// 金色漸變卡片容器
|
||||
BoxDecoration _goldGradientDecoration() {
|
||||
final isDark = context.isDark;
|
||||
// Light Mode: 琥珀色渐变(从 amber 15% 到深灰 5%)
|
||||
// Dark Mode: 金色渐变
|
||||
// Light Mode: 琥珀色漸變(從 amber 15% 到深灰 5%)
|
||||
// Dark Mode: 金色漸變
|
||||
final gradientColors = isDark
|
||||
? [
|
||||
context.appColors.accentPrimary.withValues(alpha: 0.15),
|
||||
@@ -90,7 +90,7 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 状态胶囊标签
|
||||
/// 狀態膠囊標籤
|
||||
Widget _statusBadge(String text, Color textColor, Color bgColor) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6), // [4,10] → [6,14]
|
||||
@@ -105,7 +105,7 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 全宽按钮
|
||||
/// 全寬按鈕
|
||||
Widget _fullWidthButton({
|
||||
required String text,
|
||||
required Color backgroundColor,
|
||||
@@ -192,7 +192,7 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 推广码卡片(金色渐变边框 Hero Card)
|
||||
// 推廣碼卡片(金色漸變邊框 Hero Card)
|
||||
// ============================================
|
||||
|
||||
Widget _buildReferralCodeCard(BuildContext context) {
|
||||
@@ -206,13 +206,13 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Header Row: gift icon + 标题
|
||||
// Header Row: gift icon + 標題
|
||||
Row(
|
||||
children: [
|
||||
Icon(LucideIcons.gift, color: goldAccent, size: 24),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
'我的邀请码',
|
||||
'我的邀請碼',
|
||||
style: AppTextStyles.headlineLarge(context).copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
@@ -221,9 +221,9 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
referralCode.isEmpty ? '暂无邀请码' : referralCode,
|
||||
referralCode.isEmpty ? '暫無邀請碼' : referralCode,
|
||||
style: AppTextStyles.displayMedium(context).copyWith(
|
||||
fontSize: 24, // 明确设置为 24px
|
||||
fontSize: 24, // 明確設置為 24px
|
||||
fontWeight: FontWeight.w800,
|
||||
color: goldAccent,
|
||||
letterSpacing: 2,
|
||||
@@ -238,7 +238,7 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
? null
|
||||
: () {
|
||||
Clipboard.setData(ClipboardData(text: referralCode));
|
||||
ToastUtils.show('邀请码已复制');
|
||||
ToastUtils.show('邀請碼已複製');
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: goldAccent,
|
||||
@@ -250,7 +250,7 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
disabledBackgroundColor: goldAccent.withValues(alpha: 0.4),
|
||||
),
|
||||
child: Text(
|
||||
'复制邀请码',
|
||||
'複製邀請碼',
|
||||
style: AppTextStyles.headlineMedium(context).copyWith(color: context.colors.onPrimary),
|
||||
),
|
||||
),
|
||||
@@ -272,7 +272,7 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
final claimed = newUserBonus?['claimed'] as bool? ?? false;
|
||||
final deposited = newUserBonus?['deposited'] as bool? ?? false;
|
||||
|
||||
// 状态判定
|
||||
// 狀態判定
|
||||
String badgeText;
|
||||
bool showAvailableBadge;
|
||||
String buttonText;
|
||||
@@ -280,23 +280,23 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
String description;
|
||||
|
||||
if (claimed) {
|
||||
badgeText = '已领取';
|
||||
badgeText = '已領取';
|
||||
showAvailableBadge = false;
|
||||
buttonText = '已领取';
|
||||
buttonText = '已領取';
|
||||
canClaim = false;
|
||||
description = '新人福利已领取';
|
||||
description = '新人福利已領取';
|
||||
} else if (eligible) {
|
||||
badgeText = '可领取';
|
||||
badgeText = '可領取';
|
||||
showAvailableBadge = true;
|
||||
buttonText = '立即领取';
|
||||
buttonText = '立即領取';
|
||||
canClaim = true;
|
||||
description = '完成首次充值即可领取';
|
||||
description = '完成首次充值即可領取';
|
||||
} else {
|
||||
badgeText = deposited ? '已充值' : '待解锁';
|
||||
badgeText = deposited ? '已充值' : '待解鎖';
|
||||
showAvailableBadge = false;
|
||||
buttonText = '未解锁';
|
||||
buttonText = '未解鎖';
|
||||
canClaim = false;
|
||||
description = deposited ? '已充值,等待系统确认' : '完成首次充值即可领取';
|
||||
description = deposited ? '已充值,等待系統確認' : '完成首次充值即可領取';
|
||||
}
|
||||
|
||||
return Container(
|
||||
@@ -306,7 +306,7 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Header: 标题 + 状态标签
|
||||
// Header: 標題 + 狀態標籤
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
@@ -348,14 +348,14 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 推广奖励列表
|
||||
// 推廣獎勵列表
|
||||
// ============================================
|
||||
|
||||
Widget _buildReferralRewardsSection(BuildContext context) {
|
||||
final referralRewards =
|
||||
_welfareData?['referralRewards'] as List<dynamic>? ?? [];
|
||||
|
||||
// 汇总统计
|
||||
// 彙總統計
|
||||
int totalEarned = 0;
|
||||
int totalClaimable = 0;
|
||||
for (var r in referralRewards) {
|
||||
@@ -373,17 +373,17 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
children: [
|
||||
// Section Header
|
||||
Text(
|
||||
'推广奖励',
|
||||
'推廣獎勵',
|
||||
style: AppTextStyles.headlineLarge(context),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'每邀请一位好友充值达标,奖励100 USDT',
|
||||
'每邀請一位好友充值達標,獎勵100 USDT',
|
||||
style: AppTextStyles.bodySmall(context).copyWith(
|
||||
color: context.appColors.onSurfaceMuted,
|
||||
),
|
||||
),
|
||||
// 汇总统计
|
||||
// 彙總統計
|
||||
if (referralRewards.isNotEmpty) ...[
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
@@ -395,12 +395,12 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
_buildStatItem('已邀请', '${referralRewards.length}人'),
|
||||
_buildStatItem('已邀請', '${referralRewards.length}人'),
|
||||
Container(width: 1, height: 20, color: context.appColors.ghostBorder),
|
||||
_buildStatItem('已获得', '${totalEarned * 100} USDT'),
|
||||
_buildStatItem('已獲得', '${totalEarned * 100} USDT'),
|
||||
if (totalClaimable > 0) ...[
|
||||
Container(width: 1, height: 20, color: context.appColors.ghostBorder),
|
||||
_buildStatItem('待领取', '$totalClaimable个', highlight: true),
|
||||
_buildStatItem('待領取', '$totalClaimable個', highlight: true),
|
||||
],
|
||||
],
|
||||
),
|
||||
@@ -408,7 +408,7 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
],
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// 推广列表卡片
|
||||
// 推廣列表卡片
|
||||
Container(
|
||||
width: double.infinity,
|
||||
decoration: _cardDecoration(),
|
||||
@@ -450,7 +450,7 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'暂无推广用户',
|
||||
'暫無推廣用戶',
|
||||
style: AppTextStyles.bodyLarge(context).copyWith(
|
||||
color: context.colors.onSurfaceVariant,
|
||||
),
|
||||
@@ -470,18 +470,21 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
final claimableCount = data['claimableCount'] as int? ?? 0;
|
||||
final milestones = data['milestones'] as List<dynamic>? ?? [];
|
||||
final userId = data['userId'] as int? ?? 0;
|
||||
final indirectRefCount = data['indirectRefCount'] as int? ?? 0;
|
||||
final indirectClaimableCount = data['indirectClaimableCount'] as int? ?? 0;
|
||||
final indirectMilestones = data['indirectMilestones'] as List<dynamic>? ?? [];
|
||||
final isLast = index == referralRewards.length - 1;
|
||||
|
||||
// 进度计算
|
||||
// 進度計算
|
||||
final progress = _computeProgress(milestones, totalDeposit);
|
||||
// 操作按钮
|
||||
// 操作按鈕
|
||||
final actionWidget = _buildReferralAction(
|
||||
data: data,
|
||||
claimableCount: claimableCount,
|
||||
milestones: milestones,
|
||||
progress: progress,
|
||||
);
|
||||
// 进度条颜色
|
||||
// 進度條顏色
|
||||
final progressColor = _referralProgressColor(claimableCount, progress);
|
||||
|
||||
return Column(
|
||||
@@ -526,11 +529,21 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
),
|
||||
),
|
||||
),
|
||||
// 里程碑详情
|
||||
// 里程碑詳情
|
||||
if (milestones.isNotEmpty) ...[
|
||||
const SizedBox(height: 12),
|
||||
_buildMilestoneDetails(milestones, userId),
|
||||
],
|
||||
// 間接推廣獎勵
|
||||
if (indirectRefCount > 0) ...[
|
||||
const SizedBox(height: 12),
|
||||
_buildIndirectSection(
|
||||
userId,
|
||||
indirectRefCount,
|
||||
indirectClaimableCount,
|
||||
indirectMilestones,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -546,7 +559,7 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 里程碑详情行 — 显示每个里程碑状态
|
||||
/// 里程碑詳情行 — 顯示每個里程碑狀態
|
||||
Widget _buildMilestoneDetails(List<dynamic> milestones, int userId) {
|
||||
final upColor = context.appColors.up;
|
||||
|
||||
@@ -625,7 +638,7 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 计算推荐奖励进度
|
||||
/// 計算推薦獎勵進度
|
||||
double _computeProgress(List<dynamic> milestones, String totalDeposit) {
|
||||
if (milestones.isNotEmpty) {
|
||||
int earnedCount = milestones.where((m) {
|
||||
@@ -638,14 +651,14 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
return (deposit / 1000).clamp(0.0, 1.0);
|
||||
}
|
||||
|
||||
/// 根据状态获取进度条颜色
|
||||
/// 根據狀態獲取進度條顏色
|
||||
Color _referralProgressColor(int claimableCount, double progress) {
|
||||
if (claimableCount > 0) return context.appColors.up;
|
||||
if (progress > 0) return context.appColors.accentPrimary;
|
||||
return context.appColors.surfaceCardHigh;
|
||||
}
|
||||
|
||||
/// 构建推荐奖励的操作按钮
|
||||
/// 構建推薦獎勵的操作按鈕
|
||||
Widget? _buildReferralAction({
|
||||
required Map<String, dynamic> data,
|
||||
required int claimableCount,
|
||||
@@ -665,21 +678,136 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => _claimReferralBonus(data['userId'] as int, milestoneValue),
|
||||
child: _statusBadge('领取', profitGreen, profitGreenBg),
|
||||
child: _statusBadge('領取', profitGreen, profitGreenBg),
|
||||
);
|
||||
}
|
||||
if (progress > 0) {
|
||||
final warningColor = AppColorScheme.warning;
|
||||
return _statusBadge('进行中', warningColor, warningColor.withValues(alpha: 0.15));
|
||||
return _statusBadge('進行中', warningColor, warningColor.withValues(alpha: 0.15));
|
||||
}
|
||||
return Text(
|
||||
'待达标',
|
||||
'待達標',
|
||||
style: AppTextStyles.labelLarge(context).copyWith(color: context.appColors.onSurfaceMuted),
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 奖励规则卡片
|
||||
// 間接推廣獎勵區塊
|
||||
// ============================================
|
||||
|
||||
Widget _buildIndirectSection(
|
||||
int directReferralId,
|
||||
int indirectRefCount,
|
||||
int indirectClaimableCount,
|
||||
List<dynamic> indirectMilestones,
|
||||
) {
|
||||
final goldAccent = context.appColors.accentPrimary;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: context.appColors.surfaceCardHigh.withValues(alpha: 0.5),
|
||||
borderRadius: BorderRadius.circular(AppRadius.md),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(LucideIcons.users, size: 14, color: goldAccent),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
'已推廣 $indirectRefCount 人',
|
||||
style: AppTextStyles.bodyMedium(context).copyWith(
|
||||
color: context.colors.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
if (indirectClaimableCount > 0) ...[
|
||||
const Spacer(),
|
||||
_statusBadge(
|
||||
'$indirectClaimableCount 個可領',
|
||||
context.appColors.up,
|
||||
context.appColors.upBackground,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
// 間接里程碑標籤
|
||||
if (indirectMilestones.isNotEmpty) ...[
|
||||
const SizedBox(height: 8),
|
||||
_buildIndirectMilestoneTags(directReferralId, indirectMilestones),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildIndirectMilestoneTags(int directReferralId, List<dynamic> milestones) {
|
||||
final upColor = context.appColors.up;
|
||||
|
||||
return Wrap(
|
||||
spacing: 6,
|
||||
runSpacing: 6,
|
||||
children: milestones.map((m) {
|
||||
final ms = m as Map<String, dynamic>;
|
||||
final milestoneVal = ms['milestone'] as int? ?? 1;
|
||||
final earned = ms['earned'] as bool? ?? false;
|
||||
final claimed = ms['claimed'] as bool? ?? false;
|
||||
final claimable = ms['claimable'] as bool? ?? false;
|
||||
final indirectReferredUserId = ms['indirectReferredUserId'] as int? ?? 0;
|
||||
|
||||
// 不顯示未達標的里程碑
|
||||
if (!earned && !claimed) return const SizedBox.shrink();
|
||||
|
||||
Color bgColor;
|
||||
Color textColor;
|
||||
String label;
|
||||
|
||||
if (claimed) {
|
||||
bgColor = upColor.withValues(alpha: 0.1);
|
||||
textColor = upColor;
|
||||
label = '50\u2713';
|
||||
} else if (claimable) {
|
||||
bgColor = context.appColors.accentPrimary.withValues(alpha: 0.15);
|
||||
textColor = context.appColors.accentPrimary;
|
||||
label = '50\u{1F381}';
|
||||
} else {
|
||||
bgColor = context.appColors.surfaceCardHigh;
|
||||
textColor = context.colors.onSurfaceVariant;
|
||||
label = '50';
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onTap: claimable
|
||||
? () => _claimIndirectReferralBonus(
|
||||
directReferralId,
|
||||
indirectReferredUserId,
|
||||
milestoneVal,
|
||||
)
|
||||
: null,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: bgColor,
|
||||
borderRadius: BorderRadius.circular(AppRadius.sm),
|
||||
border: claimable ? Border.all(color: textColor.withValues(alpha: 0.3)) : null,
|
||||
),
|
||||
child: Text(
|
||||
label,
|
||||
style: AppTextStyles.bodySmall(context).copyWith(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: textColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 獎勵規則卡片
|
||||
// ============================================
|
||||
|
||||
Widget _buildRulesCard(BuildContext context) {
|
||||
@@ -694,13 +822,14 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'奖励规则',
|
||||
'獎勵規則',
|
||||
style: AppTextStyles.headlineSmall(context),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildRuleItem('新用户注册完成实名认证奖励 100 USDT'),
|
||||
_buildRuleItem('邀请好友充值每达 1000 USDT,双方各获得 100 USDT'),
|
||||
_buildRuleItem('奖励直接发放至资金账户'),
|
||||
_buildRuleItem('新用戶註冊完成實名認證獎勵 100 USDT'),
|
||||
_buildRuleItem('邀請好友充值每達 1000 USDT,獎勵 100 USDT'),
|
||||
_buildRuleItem('好友推廣的人充值每達 1000 USDT,額外獎勵 50 USDT'),
|
||||
_buildRuleItem('獎勵直接發放至資金賬戶'),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -719,7 +848,7 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 业务逻辑
|
||||
// 業務邏輯
|
||||
// ============================================
|
||||
|
||||
Future<void> _claimNewUserBonus() async {
|
||||
@@ -731,13 +860,13 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
if (response.success) {
|
||||
context.read<AssetProvider>().refreshAll(force: true);
|
||||
context.read<AppEventBus>().fire(AppEventType.assetChanged);
|
||||
ToastUtils.show('领取成功!100 USDT 已到账');
|
||||
ToastUtils.show('領取成功!100 USDT 已到賬');
|
||||
_loadData();
|
||||
} else {
|
||||
ToastUtils.show(response.message ?? '领取失败');
|
||||
ToastUtils.show(response.message ?? '領取失敗');
|
||||
}
|
||||
} catch (e) {
|
||||
ToastUtils.show('领取失败: $e');
|
||||
ToastUtils.show('領取失敗: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -753,13 +882,40 @@ class _WelfareCenterPageState extends State<WelfareCenterPage> {
|
||||
if (response.success) {
|
||||
context.read<AssetProvider>().refreshAll(force: true);
|
||||
context.read<AppEventBus>().fire(AppEventType.assetChanged);
|
||||
ToastUtils.show('领取成功!100 USDT 已到账');
|
||||
ToastUtils.show('領取成功!100 USDT 已到賬');
|
||||
_loadData();
|
||||
} else {
|
||||
ToastUtils.show(response.message ?? '领取失败');
|
||||
ToastUtils.show(response.message ?? '領取失敗');
|
||||
}
|
||||
} catch (e) {
|
||||
ToastUtils.show('领取失败: $e');
|
||||
ToastUtils.show('領取失敗: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _claimIndirectReferralBonus(
|
||||
int directReferralId,
|
||||
int indirectReferredUserId,
|
||||
int milestone,
|
||||
) async {
|
||||
try {
|
||||
final bonusService = context.read<BonusService>();
|
||||
final response = await bonusService.claimIndirectReferralBonus(
|
||||
directReferralId,
|
||||
indirectReferredUserId,
|
||||
milestone,
|
||||
);
|
||||
if (!mounted) return;
|
||||
|
||||
if (response.success) {
|
||||
context.read<AssetProvider>().refreshAll(force: true);
|
||||
context.read<AppEventBus>().fire(AppEventType.assetChanged);
|
||||
ToastUtils.show('領取成功!50 USDT 已到賬');
|
||||
_loadData();
|
||||
} else {
|
||||
ToastUtils.show(response.message ?? '領取失敗');
|
||||
}
|
||||
} catch (e) {
|
||||
ToastUtils.show('領取失敗: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@ import '../../../core/theme/app_theme.dart';
|
||||
import '../../../core/theme/app_theme_extension.dart';
|
||||
import '../../../core/storage/local_storage.dart';
|
||||
|
||||
/// 引导页数据模型
|
||||
/// 引導頁數據模型
|
||||
class _OnboardingItem {
|
||||
final String title;
|
||||
final String description;
|
||||
final IconData? icon; // 图标(二选一)
|
||||
final String? imagePath; // 图片路径(二选一)
|
||||
final IconData? icon; // 圖標(二選一)
|
||||
final String? imagePath; // 圖片路徑(二選一)
|
||||
final List<Color> gradientColors;
|
||||
|
||||
const _OnboardingItem({
|
||||
@@ -23,7 +23,7 @@ class _OnboardingItem {
|
||||
});
|
||||
}
|
||||
|
||||
/// 首次启动引导页
|
||||
/// 首次啟動引導頁
|
||||
class OnboardingPage extends StatefulWidget {
|
||||
final VoidCallback onComplete;
|
||||
|
||||
@@ -39,27 +39,27 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
||||
|
||||
final _items = const [
|
||||
_OnboardingItem(
|
||||
title: '实时行情',
|
||||
description: '全球市场行情实时更新,把握每一个投资机会',
|
||||
imagePath: 'assets/images/onboarding_1.png', // 替换为你的图片
|
||||
title: '實時行情',
|
||||
description: '全球市場行情實時更新,把握每一個投資機會',
|
||||
imagePath: 'assets/images/onboarding_1.png', // 替換為你的圖片
|
||||
gradientColors: [AppColorScheme.darkPrimary, AppColorScheme.darkPrimaryContainer],
|
||||
),
|
||||
_OnboardingItem(
|
||||
title: '模拟交易',
|
||||
description: '零风险体验真实交易,学习投资策略',
|
||||
imagePath: 'assets/images/onboarding_2.png', // 替换为你的图片
|
||||
title: '模擬交易',
|
||||
description: '零風險體驗真實交易,學習投資策略',
|
||||
imagePath: 'assets/images/onboarding_2.png', // 替換為你的圖片
|
||||
gradientColors: [AppColorScheme.darkTertiary, AppColorScheme.darkTertiaryContainer],
|
||||
),
|
||||
_OnboardingItem(
|
||||
title: '资产管理',
|
||||
description: '清晰的资产概览,轻松管理你的投资组合',
|
||||
imagePath: 'assets/images/onboarding_3.png', // 替换为你的图片
|
||||
title: '資產管理',
|
||||
description: '清晰的資產概覽,輕鬆管理你的投資組合',
|
||||
imagePath: 'assets/images/onboarding_3.png', // 替換為你的圖片
|
||||
gradientColors: [AppColorScheme.darkSecondary, AppColorScheme.darkSecondaryFixed],
|
||||
),
|
||||
_OnboardingItem(
|
||||
title: '安全可靠',
|
||||
description: '数据加密存储,保护你的隐私安全',
|
||||
imagePath: 'assets/images/onboarding_4.png', // 替换为你的图片
|
||||
description: '數據加密存儲,保護你的隱私安全',
|
||||
imagePath: 'assets/images/onboarding_4.png', // 替換為你的圖片
|
||||
gradientColors: [AppColorScheme.darkPrimaryFixed, AppColorScheme.darkPrimaryFixedDim],
|
||||
),
|
||||
];
|
||||
@@ -97,7 +97,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
// 顶部跳过按钮
|
||||
// 頂部跳過按鈕
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.lg,
|
||||
@@ -109,7 +109,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
||||
TextButton(
|
||||
onPressed: _skip,
|
||||
child: Text(
|
||||
'跳过',
|
||||
'跳過',
|
||||
style: AppTextStyles.headlineMedium(context).copyWith(
|
||||
color: context.colors.onSurfaceVariant,
|
||||
),
|
||||
@@ -118,7 +118,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
||||
],
|
||||
),
|
||||
),
|
||||
// 页面内容
|
||||
// 頁面內容
|
||||
Expanded(
|
||||
child: PageView.builder(
|
||||
controller: _pageController,
|
||||
@@ -133,7 +133,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
||||
},
|
||||
),
|
||||
),
|
||||
// 底部指示器和按钮
|
||||
// 底部指示器和按鈕
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
AppSpacing.lg,
|
||||
@@ -143,7 +143,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
// 页面指示器
|
||||
// 頁面指示器
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: List.generate(
|
||||
@@ -152,7 +152,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.xl),
|
||||
// 下一步/开始按钮
|
||||
// 下一步/開始按鈕
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 56,
|
||||
@@ -167,7 +167,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
||||
elevation: 0,
|
||||
),
|
||||
child: Text(
|
||||
_currentPage == _items.length - 1 ? '开始使用' : '下一步',
|
||||
_currentPage == _items.length - 1 ? '開始使用' : '下一步',
|
||||
style: AppTextStyles.headlineLarge(context).copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
@@ -189,7 +189,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// 图标/图片容器
|
||||
// 圖標/圖片容器
|
||||
Container(
|
||||
width: 200,
|
||||
height: 200,
|
||||
@@ -217,7 +217,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
||||
height: 180,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
// 图片加载失败时显示图标
|
||||
// 圖片加載失敗時顯示圖標
|
||||
return Icon(
|
||||
item.icon ?? LucideIcons.image,
|
||||
size: 72,
|
||||
@@ -234,7 +234,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.xxl + AppSpacing.lg),
|
||||
// 标题
|
||||
// 標題
|
||||
Text(
|
||||
item.title,
|
||||
style: AppTextStyles.displaySmall(context).copyWith(
|
||||
|
||||
@@ -14,7 +14,7 @@ class _FundOrderCard extends StatelessWidget {
|
||||
|
||||
Color _getStatusColor(int status, bool isDeposit) {
|
||||
if (isDeposit) {
|
||||
// 充值状态: 1=待付款, 2=待确认, 3=已完成, 4=已驳回, 5=已取消
|
||||
// 充值狀態: 1=待付款, 2=待確認, 3=已完成, 4=已駁回, 5=已取消
|
||||
switch (status) {
|
||||
case 1:
|
||||
return AppColorScheme.warning;
|
||||
@@ -30,7 +30,7 @@ class _FundOrderCard extends StatelessWidget {
|
||||
return AppColorScheme.muted;
|
||||
}
|
||||
} else {
|
||||
// 提现状态: 1=待审批, 2=已出款, 3=已驳回, 4=已取消, 5=待财务审核
|
||||
// 提現狀態: 1=待審批, 2=已出款, 3=已駁回, 4=已取消, 5=待財務審核
|
||||
switch (status) {
|
||||
case 1:
|
||||
return AppColorScheme.warning;
|
||||
@@ -54,11 +54,11 @@ class _FundOrderCard extends StatelessWidget {
|
||||
case 1:
|
||||
return '待付款';
|
||||
case 2:
|
||||
return '待确认';
|
||||
return '待確認';
|
||||
case 3:
|
||||
return '已完成';
|
||||
case 4:
|
||||
return '已驳回';
|
||||
return '已駁回';
|
||||
case 5:
|
||||
return '已取消';
|
||||
default:
|
||||
@@ -67,15 +67,15 @@ class _FundOrderCard extends StatelessWidget {
|
||||
} else {
|
||||
switch (status) {
|
||||
case 1:
|
||||
return '待审批';
|
||||
return '待審批';
|
||||
case 2:
|
||||
return '已出款';
|
||||
case 3:
|
||||
return '已驳回';
|
||||
return '已駁回';
|
||||
case 4:
|
||||
return '已取消';
|
||||
case 5:
|
||||
return '待财务审核';
|
||||
return '待財務審核';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
@@ -121,7 +121,7 @@ class _FundOrderCard extends StatelessWidget {
|
||||
if (order.fee != null) ...[
|
||||
Row(
|
||||
children: [
|
||||
Text('手续费(10%): ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text('手續費(10%): ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text('-${order.fee} USDT', style: AppTextStyles.bodyMedium(context)),
|
||||
],
|
||||
),
|
||||
@@ -130,7 +130,7 @@ class _FundOrderCard extends StatelessWidget {
|
||||
if (order.receivableAmount != null) ...[
|
||||
Row(
|
||||
children: [
|
||||
Text('到账金额: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text('到賬金額: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text('${order.receivableAmount} USDT', style: AppTextStyles.bodyMedium(context).copyWith(fontWeight: FontWeight.w700)),
|
||||
],
|
||||
),
|
||||
@@ -139,7 +139,7 @@ class _FundOrderCard extends StatelessWidget {
|
||||
if (order.network != null) ...[
|
||||
Row(
|
||||
children: [
|
||||
Text('提现网络: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text('提現網絡: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text(order.network!, style: AppTextStyles.bodyMedium(context)),
|
||||
],
|
||||
),
|
||||
@@ -148,7 +148,7 @@ class _FundOrderCard extends StatelessWidget {
|
||||
if (order.walletAddress != null) ...[
|
||||
Row(
|
||||
children: [
|
||||
Text('提现地址: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text('提現地址: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Expanded(
|
||||
child: Text(
|
||||
order.walletAddress!,
|
||||
@@ -164,16 +164,16 @@ class _FundOrderCard extends StatelessWidget {
|
||||
],
|
||||
Row(
|
||||
children: [
|
||||
Text('订单号: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text('訂單號: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text(order.orderNo, style: AppTextStyles.bodyMedium(context)),
|
||||
],
|
||||
),
|
||||
SizedBox(height: AppSpacing.xs),
|
||||
Row(
|
||||
children: [
|
||||
Text('创建时间: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text('創建時間: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text(
|
||||
order.createTime?.toString() ?? '无',
|
||||
order.createTime?.toString() ?? '無',
|
||||
style: AppTextStyles.bodyMedium(context),
|
||||
),
|
||||
],
|
||||
@@ -182,7 +182,7 @@ class _FundOrderCard extends StatelessWidget {
|
||||
SizedBox(height: AppSpacing.xs),
|
||||
Row(
|
||||
children: [
|
||||
Text('驳回原因: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text('駁回原因: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Expanded(
|
||||
child: Text(
|
||||
order.rejectReason!,
|
||||
@@ -206,7 +206,7 @@ class _FundOrderCard extends StatelessWidget {
|
||||
Expanded(
|
||||
child: ShadButton.outline(
|
||||
onPressed: () => _handleCancel(context),
|
||||
child: const Text('取消订单'),
|
||||
child: const Text('取消訂單'),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -222,12 +222,12 @@ class _FundOrderCard extends StatelessWidget {
|
||||
if (context.mounted) {
|
||||
if (response.success) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('已确认打款,请等待审核')),
|
||||
const SnackBar(content: Text('已確認打款,請等待審核')),
|
||||
);
|
||||
context.read<AssetProvider>().loadFundOrders();
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(response.message ?? '确认失败')),
|
||||
SnackBar(content: Text(response.message ?? '確認失敗')),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -238,12 +238,12 @@ class _FundOrderCard extends StatelessWidget {
|
||||
if (context.mounted) {
|
||||
if (response.success) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('订单已取消')),
|
||||
const SnackBar(content: Text('訂單已取消')),
|
||||
);
|
||||
context.read<AssetProvider>().loadFundOrders();
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(response.message ?? '取消失败')),
|
||||
SnackBar(content: Text(response.message ?? '取消失敗')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ class FundOrdersList extends StatelessWidget {
|
||||
if (orders.isEmpty) {
|
||||
return _EmptyState(
|
||||
icon: LucideIcons.receipt,
|
||||
message: '暂无订单记录',
|
||||
message: '暫無訂單記錄',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ class FundOrdersList extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 空状态组件
|
||||
/// 空狀態組件
|
||||
class _EmptyState extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String message;
|
||||
@@ -70,7 +70,7 @@ class _EmptyState extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 充值订单卡片 - 公开类
|
||||
/// 充值訂單卡片 - 公開類
|
||||
class FundOrderCard extends StatelessWidget {
|
||||
final OrderFund order;
|
||||
|
||||
@@ -78,12 +78,12 @@ class FundOrderCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// 直接使用 _FundOrderCard 的实现
|
||||
// 直接使用 _FundOrderCard 的實現
|
||||
return _FundOrderCardContent(order: order);
|
||||
}
|
||||
}
|
||||
|
||||
/// 订单卡片内容
|
||||
/// 訂單卡片內容
|
||||
class _FundOrderCardContent extends StatelessWidget {
|
||||
final OrderFund order;
|
||||
|
||||
@@ -127,11 +127,11 @@ class _FundOrderCardContent extends StatelessWidget {
|
||||
case 1:
|
||||
return '待付款';
|
||||
case 2:
|
||||
return '待确认';
|
||||
return '待確認';
|
||||
case 3:
|
||||
return '已完成';
|
||||
case 4:
|
||||
return '已驳回';
|
||||
return '已駁回';
|
||||
case 5:
|
||||
return '已取消';
|
||||
default:
|
||||
@@ -140,11 +140,11 @@ class _FundOrderCardContent extends StatelessWidget {
|
||||
} else {
|
||||
switch (status) {
|
||||
case 1:
|
||||
return '待审批';
|
||||
return '待審批';
|
||||
case 2:
|
||||
return '已出款';
|
||||
case 3:
|
||||
return '已驳回';
|
||||
return '已駁回';
|
||||
case 4:
|
||||
return '已取消';
|
||||
default:
|
||||
@@ -192,7 +192,7 @@ class _FundOrderCardContent extends StatelessWidget {
|
||||
if (order.fee != null) ...[
|
||||
Row(
|
||||
children: [
|
||||
Text('手续费(10%): ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text('手續費(10%): ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text('-${order.fee} USDT', style: AppTextStyles.bodyMedium(context)),
|
||||
],
|
||||
),
|
||||
@@ -201,7 +201,7 @@ class _FundOrderCardContent extends StatelessWidget {
|
||||
if (order.receivableAmount != null) ...[
|
||||
Row(
|
||||
children: [
|
||||
Text('到账金额: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text('到賬金額: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text('${order.receivableAmount} USDT', style: AppTextStyles.bodyMedium(context).copyWith(fontWeight: FontWeight.w700)),
|
||||
],
|
||||
),
|
||||
@@ -210,7 +210,7 @@ class _FundOrderCardContent extends StatelessWidget {
|
||||
if (order.network != null) ...[
|
||||
Row(
|
||||
children: [
|
||||
Text('提现网络: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text('提現網絡: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text(order.network!, style: AppTextStyles.bodyMedium(context)),
|
||||
],
|
||||
),
|
||||
@@ -219,7 +219,7 @@ class _FundOrderCardContent extends StatelessWidget {
|
||||
if (order.walletAddress != null) ...[
|
||||
Row(
|
||||
children: [
|
||||
Text('提现地址: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text('提現地址: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Expanded(
|
||||
child: Text(
|
||||
order.walletAddress!,
|
||||
@@ -234,16 +234,16 @@ class _FundOrderCardContent extends StatelessWidget {
|
||||
],
|
||||
Row(
|
||||
children: [
|
||||
Text('订单号: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text('訂單號: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text(order.orderNo, style: AppTextStyles.bodyMedium(context)),
|
||||
],
|
||||
),
|
||||
SizedBox(height: AppSpacing.xs),
|
||||
Row(
|
||||
children: [
|
||||
Text('创建时间: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text('創建時間: ', style: AppTextStyles.bodyMedium(context).copyWith(color: theme.colorScheme.mutedForeground)),
|
||||
Text(
|
||||
order.createTime?.toString() ?? '无',
|
||||
order.createTime?.toString() ?? '無',
|
||||
style: AppTextStyles.bodyMedium(context),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -13,7 +13,7 @@ import '../../../core/event/app_event_bus.dart';
|
||||
import '../../../providers/asset_provider.dart';
|
||||
import '../../../data/models/order_models.dart';
|
||||
|
||||
/// 充提订单页面
|
||||
/// 充提訂單頁面
|
||||
class FundOrdersPage extends StatefulWidget {
|
||||
const FundOrdersPage({super.key});
|
||||
|
||||
@@ -22,7 +22,7 @@ class FundOrdersPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
int _activeTab = 0; // 0=全部, 1=充值, 2=提现
|
||||
int _activeTab = 0; // 0=全部, 1=充值, 2=提現
|
||||
StreamSubscription<AppEvent>? _eventSub;
|
||||
|
||||
@override
|
||||
@@ -53,7 +53,7 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 构建 UI
|
||||
// 構建 UI
|
||||
// ============================================
|
||||
|
||||
@override
|
||||
@@ -65,7 +65,7 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
icon: const Icon(LucideIcons.arrowLeft, size: 20),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
title: Text('充提记录', style: AppTextStyles.headlineLarge(context).copyWith(color: context.colors.onSurface)),
|
||||
title: Text('充提記錄', style: AppTextStyles.headlineLarge(context).copyWith(color: context.colors.onSurface)),
|
||||
backgroundColor: context.colors.surface,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
@@ -97,7 +97,7 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
children: [
|
||||
_buildPillTab('全部', 0),
|
||||
_buildPillTab('充值', 1),
|
||||
_buildPillTab('提现', 2),
|
||||
_buildPillTab('提現', 2),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -153,7 +153,7 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
children: [
|
||||
Icon(LucideIcons.inbox, size: 64, color: context.appColors.onSurfaceMuted),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
Text('暂无订单记录', style: AppTextStyles.headlineMedium(context).copyWith(color: context.colors.onSurfaceVariant)),
|
||||
Text('暫無訂單記錄', style: AppTextStyles.headlineMedium(context).copyWith(color: context.colors.onSurfaceVariant)),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -199,7 +199,7 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
],
|
||||
if (order.withdrawContact != null) ...[
|
||||
const SizedBox(height: AppSpacing.sm - AppSpacing.xs),
|
||||
_buildDetailRow('联系方式', order.withdrawContact!),
|
||||
_buildDetailRow('聯繫方式', order.withdrawContact!),
|
||||
],
|
||||
if (order.receivableAmount != null && !order.isDeposit) ...[
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
@@ -214,13 +214,13 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'创建: ${_formatTime(order.createTime)}',
|
||||
'創建: ${_formatTime(order.createTime)}',
|
||||
style: AppTextStyles.bodySmall(context).copyWith(color: context.appColors.onSurfaceMuted),
|
||||
),
|
||||
if (order.rejectReason != null)
|
||||
Expanded(
|
||||
child: Text(
|
||||
'驳回: ${order.rejectReason}',
|
||||
'駁回: ${order.rejectReason}',
|
||||
style: AppTextStyles.bodySmall(context).copyWith(color: context.appColors.down),
|
||||
textAlign: TextAlign.right,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
@@ -241,7 +241,7 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
order.isDeposit ? '充值' : '提现',
|
||||
order.isDeposit ? '充值' : '提現',
|
||||
style: AppTextStyles.headlineMedium(context).copyWith(color: context.colors.onSurface),
|
||||
),
|
||||
_buildStatusBadge(order),
|
||||
@@ -261,7 +261,7 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
if (order.isDeposit) {
|
||||
switch (order.status) {
|
||||
case 1: // 待付款
|
||||
case 2: // 待确认
|
||||
case 2: // 待確認
|
||||
bgColor = AppColorScheme.warning.withValues(alpha: 0.12);
|
||||
textColor = AppColorScheme.warning;
|
||||
break;
|
||||
@@ -269,14 +269,14 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
bgColor = upBg;
|
||||
textColor = upColor;
|
||||
break;
|
||||
default: // 已驳回/已取消
|
||||
default: // 已駁回/已取消
|
||||
bgColor = downBg;
|
||||
textColor = downColor;
|
||||
}
|
||||
} else {
|
||||
switch (order.status) {
|
||||
case 1: // 待审批
|
||||
case 5: // 待财务审核
|
||||
case 1: // 待審批
|
||||
case 5: // 待財務審核
|
||||
bgColor = AppColorScheme.warning.withValues(alpha: 0.12);
|
||||
textColor = AppColorScheme.warning;
|
||||
break;
|
||||
@@ -284,7 +284,7 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
bgColor = upBg;
|
||||
textColor = upColor;
|
||||
break;
|
||||
default: // 已驳回/已取消
|
||||
default: // 已駁回/已取消
|
||||
bgColor = downBg;
|
||||
textColor = downColor;
|
||||
}
|
||||
@@ -320,10 +320,10 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
Widget _buildDetailRows(OrderFund order) {
|
||||
return Column(
|
||||
children: [
|
||||
_buildDetailRow('订单号', order.orderNo),
|
||||
_buildDetailRow('訂單號', order.orderNo),
|
||||
const SizedBox(height: AppSpacing.sm - AppSpacing.xs),
|
||||
if (order.network != null) ...[
|
||||
_buildDetailRow('网络', order.network!),
|
||||
_buildDetailRow('網絡', order.network!),
|
||||
const SizedBox(height: AppSpacing.sm - AppSpacing.xs),
|
||||
],
|
||||
if (order.walletAddress != null) ...[
|
||||
@@ -333,7 +333,7 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
trailing: GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: order.walletAddress!));
|
||||
ToastUtils.show('地址已复制');
|
||||
ToastUtils.show('地址已複製');
|
||||
},
|
||||
child: Icon(LucideIcons.copy, size: 14, color: context.appColors.onSurfaceMuted),
|
||||
),
|
||||
@@ -341,11 +341,11 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
const SizedBox(height: AppSpacing.sm - AppSpacing.xs),
|
||||
],
|
||||
if (order.fee != null && !order.isDeposit) ...[
|
||||
_buildDetailRow('手续费', '${order.fee}%'),
|
||||
_buildDetailRow('手續費', '${order.fee}%'),
|
||||
const SizedBox(height: AppSpacing.sm - AppSpacing.xs),
|
||||
],
|
||||
_buildDetailRow(
|
||||
'时间',
|
||||
'時間',
|
||||
_formatTime(order.createTime),
|
||||
),
|
||||
],
|
||||
@@ -383,7 +383,7 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
// ---------------------------------------------------------------------------
|
||||
Widget _buildRejectionReason(OrderFund order) {
|
||||
return Text(
|
||||
'拒绝原因: ${order.rejectReason}',
|
||||
'拒絕原因: ${order.rejectReason}',
|
||||
style: AppTextStyles.bodyMedium(context).copyWith(color: context.appColors.down),
|
||||
);
|
||||
}
|
||||
@@ -401,7 +401,7 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('应付金额', style: AppTextStyles.headlineSmall(context).copyWith(color: context.colors.onSurfaceVariant)),
|
||||
Text('應付金額', style: AppTextStyles.headlineSmall(context).copyWith(color: context.colors.onSurfaceVariant)),
|
||||
Text('${order.receivableAmount} USDT', style: AppTextStyles.headlineMedium(context).copyWith(color: context.colors.onSurface)),
|
||||
],
|
||||
),
|
||||
@@ -427,7 +427,7 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
borderRadius: AppRadius.radiusSm,
|
||||
border: Border.all(color: downColor, width: 1),
|
||||
),
|
||||
child: Text('取消订单', style: AppTextStyles.headlineSmall(context).copyWith(color: downColor)),
|
||||
child: Text('取消訂單', style: AppTextStyles.headlineSmall(context).copyWith(color: downColor)),
|
||||
),
|
||||
),
|
||||
if (order.canCancel && order.canConfirmPay)
|
||||
@@ -467,14 +467,14 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
void _confirmPay(OrderFund order) async {
|
||||
final confirmed = await showShadConfirmDialog(
|
||||
context: context,
|
||||
title: '确认已打款',
|
||||
description: '确认您已完成向指定地址的转账?',
|
||||
title: '確認已打款',
|
||||
description: '確認您已完成向指定地址的轉賬?',
|
||||
);
|
||||
|
||||
if (confirmed == true && mounted) {
|
||||
final response = await context.read<AssetProvider>().confirmPay(order.orderNo);
|
||||
if (mounted) {
|
||||
BotToast.showText(text: response.success ? '确认成功,请等待审核' : response.message ?? '确认失败');
|
||||
BotToast.showText(text: response.success ? '確認成功,請等待審核' : response.message ?? '確認失敗');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -482,15 +482,15 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
void _cancelOrder(OrderFund order) async {
|
||||
final confirmed = await showShadConfirmDialog(
|
||||
context: context,
|
||||
title: '取消订单',
|
||||
description: '确定要取消订单 ${order.orderNo} 吗?',
|
||||
title: '取消訂單',
|
||||
description: '確定要取消訂單 ${order.orderNo} 嗎?',
|
||||
destructive: true,
|
||||
);
|
||||
|
||||
if (confirmed == true && mounted) {
|
||||
final response = await context.read<AssetProvider>().cancelOrder(order.orderNo);
|
||||
if (mounted) {
|
||||
BotToast.showText(text: response.success ? '订单已取消' : response.message ?? '取消失败');
|
||||
BotToast.showText(text: response.success ? '訂單已取消' : response.message ?? '取消失敗');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -512,7 +512,7 @@ class _FundOrdersPageState extends State<FundOrdersPage> {
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
),
|
||||
TextButton(
|
||||
child: Text(destructive ? '确定取消' : '确认'),
|
||||
child: Text(destructive ? '確定取消' : '確認'),
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -8,7 +8,7 @@ import '../../../core/theme/app_theme_extension.dart';
|
||||
import '../../../providers/asset_provider.dart';
|
||||
import 'fund_orders_list.dart';
|
||||
|
||||
/// 订单管理页面
|
||||
/// 訂單管理頁面
|
||||
class OrdersPage extends StatefulWidget {
|
||||
const OrdersPage({super.key});
|
||||
|
||||
@@ -50,7 +50,7 @@ class _OrdersPageState extends State<OrdersPage> with AutomaticKeepAliveClientMi
|
||||
child: Column(
|
||||
children: [
|
||||
TabSelector(
|
||||
tabs: const ['订单记录', '交易记录'],
|
||||
tabs: const ['訂單記錄', '交易記錄'],
|
||||
selectedIndex: _activeTab,
|
||||
onChanged: (index) => setState(() => _activeTab = index),
|
||||
),
|
||||
@@ -68,7 +68,7 @@ class _OrdersPageState extends State<OrdersPage> with AutomaticKeepAliveClientMi
|
||||
}
|
||||
}
|
||||
|
||||
/// Tab 选择器
|
||||
/// Tab 選擇器
|
||||
class TabSelector extends StatelessWidget {
|
||||
final List<String> tabs;
|
||||
final int selectedIndex;
|
||||
@@ -129,7 +129,7 @@ class TabSelector extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 交易订单列表
|
||||
/// 交易訂單列表
|
||||
class TradeOrdersList extends StatelessWidget {
|
||||
final AssetProvider provider;
|
||||
|
||||
@@ -147,7 +147,7 @@ class TradeOrdersList extends StatelessWidget {
|
||||
children: [
|
||||
Icon(LucideIcons.receipt, size: 48, color: theme.colorScheme.mutedForeground),
|
||||
SizedBox(height: AppSpacing.sm + AppSpacing.xs),
|
||||
Text('暂无交易记录', style: theme.textTheme.muted),
|
||||
Text('暫無交易記錄', style: theme.textTheme.muted),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -3,10 +3,10 @@ import '../../../../core/theme/app_color_scheme.dart';
|
||||
import '../../../../core/theme/app_spacing.dart';
|
||||
import '../../../../core/theme/app_theme.dart';
|
||||
|
||||
/// 金额输入框组件(含超额提示)
|
||||
/// 金額輸入框組件(含超額提示)
|
||||
///
|
||||
/// 设计稿:bg-tertiary,圆角md,高48。
|
||||
/// 输入金额超过可用 USDT 余额时显示警告提示。
|
||||
/// 設計稿:bg-tertiary,圓角md,高48。
|
||||
/// 輸入金額超過可用 USDT 餘額時顯示警告提示。
|
||||
class AmountInput extends StatefulWidget {
|
||||
final TextEditingController amountController;
|
||||
final String maxAmount;
|
||||
@@ -63,7 +63,7 @@ class _AmountInputState extends State<AmountInput> {
|
||||
Container(
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.surfaceContainerHighest.withOpacity(0.5), // 调整透明度
|
||||
color: colorScheme.surfaceContainerHighest.withOpacity(0.5), // 調整透明度
|
||||
borderRadius: BorderRadius.circular(AppRadius.md),
|
||||
),
|
||||
child: TextField(
|
||||
@@ -74,7 +74,7 @@ class _AmountInputState extends State<AmountInput> {
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintText: '请输入金额',
|
||||
hintText: '請輸入金額',
|
||||
hintStyle: AppTextStyles.numberMedium(context).copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
color: colorScheme.onSurfaceVariant.withOpacity(0.5),
|
||||
@@ -94,7 +94,7 @@ class _AmountInputState extends State<AmountInput> {
|
||||
Icon(Icons.error_outline, size: 13, color: warningColor),
|
||||
SizedBox(width: AppSpacing.xs),
|
||||
Text(
|
||||
'超出可用USDT余额',
|
||||
'超出可用USDT餘額',
|
||||
style: AppTextStyles.bodySmall(context).copyWith(
|
||||
color: warningColor,
|
||||
),
|
||||
|
||||
@@ -2,9 +2,9 @@ import 'package:flutter/material.dart';
|
||||
import '../../../../core/theme/app_spacing.dart';
|
||||
import '../../../../core/theme/app_theme.dart';
|
||||
|
||||
/// 币种头像组件
|
||||
/// 幣種頭像組件
|
||||
///
|
||||
/// 显示币种图标或首字母的圆形头像,带主题色边框和背景。
|
||||
/// 顯示幣種圖標或首字母的圓形頭像,帶主題色邊框和背景。
|
||||
class CoinAvatar extends StatelessWidget {
|
||||
final String? icon;
|
||||
const CoinAvatar({super.key, this.icon});
|
||||
|
||||
@@ -6,9 +6,9 @@ import '../../../../core/theme/app_theme_extension.dart';
|
||||
import '../../../../data/models/coin.dart';
|
||||
import 'coin_avatar.dart';
|
||||
|
||||
/// 币种选择器组件
|
||||
/// 幣種選擇器組件
|
||||
///
|
||||
/// 显示当前选中的币种交易对,点击弹出底部弹窗选择币种。
|
||||
/// 顯示當前選中的幣種交易對,點擊彈出底部彈窗選擇幣種。
|
||||
class CoinSelector extends StatelessWidget {
|
||||
final Coin? selectedCoin;
|
||||
final List<Coin> coins;
|
||||
@@ -38,7 +38,7 @@ class CoinSelector extends StatelessWidget {
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
// 币种信息:交易对 + 名称
|
||||
// 幣種信息:交易對 + 名稱
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -46,19 +46,19 @@ class CoinSelector extends StatelessWidget {
|
||||
Text(
|
||||
selectedCoin != null
|
||||
? '${selectedCoin!.code}/USDT'
|
||||
: '选择币种',
|
||||
: '選擇幣種',
|
||||
style: AppTextStyles.headlineLarge(context),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
selectedCoin?.name ?? '点击选择交易对',
|
||||
selectedCoin?.name ?? '點擊選擇交易對',
|
||||
style: AppTextStyles.bodyMedium(context).copyWith(
|
||||
color: context.colors.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// 下拉箭头
|
||||
// 下拉箭頭
|
||||
Icon(LucideIcons.chevronDown,
|
||||
size: 16, color: context.colors.onSurfaceVariant),
|
||||
],
|
||||
@@ -83,7 +83,7 @@ class CoinSelector extends StatelessWidget {
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
// 拖动指示器
|
||||
// 拖動指示器
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: AppSpacing.sm),
|
||||
width: 40,
|
||||
@@ -93,13 +93,13 @@ class CoinSelector extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(AppRadius.sm),
|
||||
),
|
||||
),
|
||||
// 标题栏
|
||||
// 標題欄
|
||||
Padding(
|
||||
padding: EdgeInsets.all(AppSpacing.lg),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('选择币种',
|
||||
Text('選擇幣種',
|
||||
style: AppTextStyles.headlineLarge(context)),
|
||||
GestureDetector(
|
||||
onTap: () => Navigator.of(ctx).pop(),
|
||||
@@ -110,7 +110,7 @@ class CoinSelector extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Divider(height: 1, color: ctx.colors.outlineVariant.withValues(alpha: 0.2)),
|
||||
// 币种列表
|
||||
// 幣種列表
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
padding: EdgeInsets.symmetric(vertical: AppSpacing.sm),
|
||||
@@ -150,7 +150,7 @@ class CoinSelector extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 第一行:币种代码 + USDT + 价格 + 涨跌幅
|
||||
// 第一行:幣種代碼 + USDT + 價格 + 漲跌幅
|
||||
Row(
|
||||
children: [
|
||||
Text(coin.code,
|
||||
@@ -164,7 +164,7 @@ class CoinSelector extends StatelessWidget {
|
||||
Text('\$${coin.formattedPrice}',
|
||||
style: AppTextStyles.numberMedium(context)),
|
||||
SizedBox(width: AppSpacing.sm),
|
||||
// 涨跌幅徽章
|
||||
// 漲跌幅徽章
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
@@ -185,7 +185,7 @@ class CoinSelector extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
SizedBox(height: 3),
|
||||
// 第二行:币种名称
|
||||
// 第二行:幣種名稱
|
||||
Text(coin.name,
|
||||
style: AppTextStyles.bodyMedium(context).copyWith(
|
||||
color: context.colors.onSurfaceVariant,
|
||||
|
||||
@@ -5,10 +5,10 @@ import '../../../../core/theme/app_theme_extension.dart';
|
||||
import '../../../components/glass_panel.dart';
|
||||
import '../../../components/neon_glow.dart';
|
||||
|
||||
/// 交易确认对话框
|
||||
/// 交易確認對話框
|
||||
///
|
||||
/// 显示交易详情(交易对、委托价格、交易金额、交易数量),
|
||||
/// 用户确认后执行交易。
|
||||
/// 顯示交易詳情(交易對、委託價格、交易金額、交易數量),
|
||||
/// 用戶確認後執行交易。
|
||||
class ConfirmDialog extends StatelessWidget {
|
||||
final bool isBuy;
|
||||
final String coinCode;
|
||||
@@ -42,19 +42,19 @@ class ConfirmDialog extends StatelessWidget {
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
'确认${isBuy ? '买入' : '卖出'}',
|
||||
'確認${isBuy ? '買入' : '賣出'}',
|
||||
style: AppTextStyles.headlineLarge(context),
|
||||
),
|
||||
),
|
||||
SizedBox(height: AppSpacing.lg),
|
||||
_dialogRow(context, '交易对', '$coinCode/USDT'),
|
||||
_dialogRow(context, '交易對', '$coinCode/USDT'),
|
||||
SizedBox(height: AppSpacing.sm),
|
||||
_dialogRow(context, '委托价格', '$price USDT'),
|
||||
_dialogRow(context, '委託價格', '$price USDT'),
|
||||
SizedBox(height: AppSpacing.sm),
|
||||
_dialogRow(context, '交易金额', '$amount USDT',
|
||||
_dialogRow(context, '交易金額', '$amount USDT',
|
||||
valueColor: actionColor),
|
||||
SizedBox(height: AppSpacing.sm),
|
||||
_dialogRow(context, '交易数量', '$quantity $coinCode'),
|
||||
_dialogRow(context, '交易數量', '$quantity $coinCode'),
|
||||
SizedBox(height: AppSpacing.lg),
|
||||
Row(
|
||||
children: [
|
||||
@@ -70,7 +70,7 @@ class ConfirmDialog extends StatelessWidget {
|
||||
SizedBox(width: AppSpacing.sm),
|
||||
Expanded(
|
||||
child: NeonButton(
|
||||
text: '确认${isBuy ? '买入' : '卖出'}',
|
||||
text: '確認${isBuy ? '買入' : '賣出'}',
|
||||
type: isBuy ? NeonButtonType.tertiary : NeonButtonType.error,
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
height: 44,
|
||||
|
||||
@@ -3,9 +3,9 @@ import '../../../../core/theme/app_spacing.dart';
|
||||
import '../../../../core/theme/app_theme.dart';
|
||||
import '../../../../core/theme/app_theme_extension.dart';
|
||||
|
||||
/// 占位卡片组件
|
||||
/// 佔位卡片組件
|
||||
///
|
||||
/// 当未选择币种时显示的占位提示卡片。
|
||||
/// 當未選擇幣種時顯示的佔位提示卡片。
|
||||
class PlaceholderCard extends StatelessWidget {
|
||||
final String message;
|
||||
const PlaceholderCard({
|
||||
|
||||
@@ -4,10 +4,10 @@ import '../../../../core/theme/app_theme.dart';
|
||||
import '../../../../core/theme/app_theme_extension.dart';
|
||||
import '../../../../data/models/coin.dart';
|
||||
|
||||
/// 价格卡片组件
|
||||
/// 價格卡片組件
|
||||
///
|
||||
/// 显示当前币种价格和 24h 涨跌幅。
|
||||
/// 布局:大号价格(32px bold) + 涨跌幅徽章(圆角sm,涨绿背景) + "24h 变化" 副标题。
|
||||
/// 顯示當前幣種價格和 24h 漲跌幅。
|
||||
/// 佈局:大號價格(32px bold) + 漲跌幅徽章(圓角sm,漲綠背景) + "24h 變化" 副標題。
|
||||
class PriceCard extends StatelessWidget {
|
||||
final Coin coin;
|
||||
const PriceCard({super.key, required this.coin});
|
||||
@@ -34,7 +34,7 @@ class PriceCard extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 价格行:大号价格 + 涨跌幅徽章
|
||||
// 價格行:大號價格 + 漲跌幅徽章
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
@@ -42,10 +42,10 @@ class PriceCard extends StatelessWidget {
|
||||
style: AppTextStyles.numberLarge(context).copyWith(fontSize: 32),
|
||||
),
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
// 涨跌幅徽章 - 圆角sm,涨绿背景
|
||||
// 漲跌幅徽章 - 圓角sm,漲綠背景
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8, vertical: 4), // 调整 padding
|
||||
horizontal: 8, vertical: 4), // 調整 padding
|
||||
decoration: BoxDecoration(
|
||||
color: changeBgColor,
|
||||
borderRadius: BorderRadius.circular(AppRadius.sm),
|
||||
@@ -61,9 +61,9 @@ class PriceCard extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
// 副标题
|
||||
// 副標題
|
||||
Text(
|
||||
'24h 变化',
|
||||
'24h 變化',
|
||||
style: AppTextStyles.bodySmall(context),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -3,9 +3,9 @@ import '../../../../core/theme/app_color_scheme.dart';
|
||||
import '../../../../core/theme/app_spacing.dart';
|
||||
import '../../../../core/theme/app_theme.dart';
|
||||
|
||||
/// 交易按钮组件
|
||||
/// 交易按鈕組件
|
||||
///
|
||||
/// CTA 买入/卖出按钮。profit-green底 / sell-red底,圆角lg,高48,买入白字/卖出红字16px bold。
|
||||
/// CTA 買入/賣出按鈕。profit-green底 / sell-red底,圓角lg,高48,買入白字/賣出紅字16px bold。
|
||||
class TradeButton extends StatelessWidget {
|
||||
final bool isBuy;
|
||||
final String? coinCode;
|
||||
@@ -27,7 +27,7 @@ class TradeButton extends StatelessWidget {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final fillColor =
|
||||
isBuy ? AppColorScheme.buyButtonFill : AppColorScheme.sellButtonFill;
|
||||
// 买入按钮文字为白色,卖出按钮文字为红色
|
||||
// 買入按鈕文字為白色,賣出按鈕文字為紅色
|
||||
final textColor = isBuy
|
||||
? Colors.white
|
||||
: (enabled ? AppColorScheme.sellButtonFill : colorScheme.onSurface.withOpacity(0.3));
|
||||
@@ -52,7 +52,7 @@ class TradeButton extends StatelessWidget {
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
'${isBuy ? '买入' : '卖出'} ${coinCode ?? ""}',
|
||||
'${isBuy ? '買入' : '賣出'} ${coinCode ?? ""}',
|
||||
style: AppTextStyles.headlineLarge(context).copyWith(
|
||||
color: enabled
|
||||
? textColor
|
||||
|
||||
@@ -6,10 +6,10 @@ import '../../../../core/theme/app_theme_extension.dart';
|
||||
import '../../../../data/models/coin.dart';
|
||||
import 'amount_input.dart';
|
||||
|
||||
/// 交易表单卡片组件
|
||||
/// 交易表單卡片組件
|
||||
///
|
||||
/// 包含买入/卖出切换、金额输入、可用余额、快捷比例按钮、计算数量行。
|
||||
/// card背景 + 圆角lg + border + padding:20 + gap:16
|
||||
/// 包含買入/賣出切換、金額輸入、可用餘額、快捷比例按鈕、計算數量行。
|
||||
/// card背景 + 圓角lg + border + padding:20 + gap:16
|
||||
class TradeFormCard extends StatelessWidget {
|
||||
final int tradeType;
|
||||
final Coin? selectedCoin;
|
||||
@@ -43,7 +43,7 @@ class TradeFormCard extends StatelessWidget {
|
||||
? context.appColors.up
|
||||
: context.appColors.down;
|
||||
|
||||
// 设计稿中 card 背景色
|
||||
// 設計稿中 card 背景色
|
||||
final cardBgColor = context.appColors.surfaceCard;
|
||||
|
||||
return Container(
|
||||
@@ -59,13 +59,13 @@ class TradeFormCard extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// ---- 买入/卖出切换 ----
|
||||
// 设计稿:ClipRRect + 圆角md,两等宽按钮
|
||||
// ---- 買入/賣出切換 ----
|
||||
// 設計稿:ClipRRect + 圓角md,兩等寬按鈕
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(AppRadius.md),
|
||||
child: Row(
|
||||
children: [
|
||||
// 买入按钮
|
||||
// 買入按鈕
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () => onTradeTypeChanged(0),
|
||||
@@ -84,7 +84,7 @@ class TradeFormCard extends StatelessWidget {
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'买入',
|
||||
'買入',
|
||||
style: AppTextStyles.headlineMedium(context).copyWith(
|
||||
color: isBuy
|
||||
? Colors.white
|
||||
@@ -95,7 +95,7 @@ class TradeFormCard extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
// 卖出按钮
|
||||
// 賣出按鈕
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () => onTradeTypeChanged(1),
|
||||
@@ -114,7 +114,7 @@ class TradeFormCard extends StatelessWidget {
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'卖出',
|
||||
'賣出',
|
||||
style: AppTextStyles.headlineMedium(context).copyWith(
|
||||
color: !isBuy
|
||||
? Colors.white
|
||||
@@ -130,11 +130,11 @@ class TradeFormCard extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md + AppSpacing.sm),
|
||||
|
||||
// ---- 交易金额 label 行 ----
|
||||
// ---- 交易金額 label 行 ----
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('交易金额',
|
||||
Text('交易金額',
|
||||
style: AppTextStyles.bodyMedium(context).copyWith(
|
||||
color: context.colors.onSurfaceVariant,
|
||||
)),
|
||||
@@ -144,7 +144,7 @@ class TradeFormCard extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
|
||||
// ---- 金额输入框 ----
|
||||
// ---- 金額輸入框 ----
|
||||
AmountInput(
|
||||
amountController: amountController,
|
||||
maxAmount: maxAmount,
|
||||
@@ -154,7 +154,7 @@ class TradeFormCard extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
|
||||
// ---- 可用余额 ----
|
||||
// ---- 可用餘額 ----
|
||||
Text(
|
||||
isBuy
|
||||
? '可用: $availableUsdt USDT'
|
||||
@@ -165,8 +165,8 @@ class TradeFormCard extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
|
||||
// ---- 快捷比例按钮 25% 50% 75% 100% ----
|
||||
// 设计稿:gap:8,圆角sm,bg-tertiary,高32
|
||||
// ---- 快捷比例按鈕 25% 50% 75% 100% ----
|
||||
// 設計稿:gap:8,圓角sm,bg-tertiary,高32
|
||||
Row(
|
||||
children: [
|
||||
_buildPctButton(context, '25%', 0.25),
|
||||
@@ -180,11 +180,11 @@ class TradeFormCard extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md + AppSpacing.sm),
|
||||
|
||||
// ---- 计算数量行 ----
|
||||
// ---- 計算數量行 ----
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('交易数量',
|
||||
Text('交易數量',
|
||||
style: AppTextStyles.bodyMedium(context).copyWith(
|
||||
color: context.colors.onSurfaceVariant,
|
||||
)),
|
||||
@@ -199,7 +199,7 @@ class TradeFormCard extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
/// 百分比按钮 - 设计稿:圆角sm,bg-tertiary,高32
|
||||
/// 百分比按鈕 - 設計稿:圓角sm,bg-tertiary,高32
|
||||
Widget _buildPctButton(BuildContext context, String label, double pct) {
|
||||
return Expanded(
|
||||
child: GestureDetector(
|
||||
@@ -207,7 +207,7 @@ class TradeFormCard extends StatelessWidget {
|
||||
child: Container(
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: context.colors.surfaceContainerHighest.withOpacity(0.5), // 更柔和的颜色
|
||||
color: context.colors.surfaceContainerHighest.withOpacity(0.5), // 更柔和的顏色
|
||||
borderRadius: BorderRadius.circular(AppRadius.sm),
|
||||
),
|
||||
child: Center(
|
||||
|
||||
@@ -15,14 +15,14 @@ import 'components/trade_form_card.dart';
|
||||
import 'components/trade_button.dart';
|
||||
import 'components/confirm_dialog.dart';
|
||||
|
||||
/// 交易页面
|
||||
/// 交易頁面
|
||||
///
|
||||
/// 设计稿 Trade 页面,布局结构:
|
||||
/// - 币种选择器卡片(Coin Selector Card)
|
||||
/// - 价格卡片(Price Card):大号价格 + 涨跌幅徽章 + 副标题
|
||||
/// - 买入/卖出切换(Buy/Sell Toggle)
|
||||
/// - 交易表单卡片(Trade Form Card):金额输入 + 快捷比例 + 计算数量
|
||||
/// - CTA 买入/卖出按钮(Buy/Sell Button)
|
||||
/// 設計稿 Trade 頁面,佈局結構:
|
||||
/// - 幣種選擇器卡片(Coin Selector Card)
|
||||
/// - 價格卡片(Price Card):大號價格 + 漲跌幅徽章 + 副標題
|
||||
/// - 買入/賣出切換(Buy/Sell Toggle)
|
||||
/// - 交易表單卡片(Trade Form Card):金額輸入 + 快捷比例 + 計算數量
|
||||
/// - CTA 買入/賣出按鈕(Buy/Sell Button)
|
||||
class TradePage extends StatefulWidget {
|
||||
final String? initialCoinCode;
|
||||
|
||||
@@ -34,7 +34,7 @@ class TradePage extends StatefulWidget {
|
||||
|
||||
class _TradePageState extends State<TradePage>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
int _tradeType = 0; // 0=买入, 1=卖出
|
||||
int _tradeType = 0; // 0=買入, 1=賣出
|
||||
Coin? _selectedCoin;
|
||||
final _amountController = TextEditingController();
|
||||
bool _isSubmitting = false;
|
||||
@@ -71,14 +71,14 @@ class _TradePageState extends State<TradePage>
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/// 获取交易账户中 USDT 可用余额
|
||||
/// 獲取交易賬戶中 USDT 可用餘額
|
||||
String get _availableUsdt {
|
||||
final holdings = context.read<AssetProvider>().holdings;
|
||||
final usdt = holdings.where((h) => h.coinCode == 'USDT').firstOrNull;
|
||||
return usdt?.quantity ?? '0';
|
||||
}
|
||||
|
||||
/// 获取交易账户中当前币种的持仓数量
|
||||
/// 獲取交易賬戶中當前幣種的持倉數量
|
||||
String get _availableCoinQty {
|
||||
if (_selectedCoin == null) return '0';
|
||||
final holdings = context.read<AssetProvider>().holdings;
|
||||
@@ -88,7 +88,7 @@ class _TradePageState extends State<TradePage>
|
||||
return pos?.quantity ?? '0';
|
||||
}
|
||||
|
||||
/// 计算可买入/卖出的最大 USDT 金额
|
||||
/// 計算可買入/賣出的最大 USDT 金額
|
||||
String get _maxAmount {
|
||||
if (_selectedCoin == null) return '0';
|
||||
final price = _selectedCoin!.price;
|
||||
@@ -102,7 +102,7 @@ class _TradePageState extends State<TradePage>
|
||||
}
|
||||
}
|
||||
|
||||
/// 计算数量
|
||||
/// 計算數量
|
||||
String get _calculatedQuantity {
|
||||
final amount = double.tryParse(_amountController.text) ?? 0;
|
||||
final price = _selectedCoin?.price ?? 0;
|
||||
@@ -121,11 +121,11 @@ class _TradePageState extends State<TradePage>
|
||||
return SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
AppSpacing.md, AppSpacing.md, AppSpacing.md, AppSpacing.xl + AppSpacing.sm, // 添加顶部间距
|
||||
AppSpacing.md, AppSpacing.md, AppSpacing.md, AppSpacing.xl + AppSpacing.sm, // 添加頂部間距
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
// 币种选择器卡片
|
||||
// 幣種選擇器卡片
|
||||
CoinSelector(
|
||||
selectedCoin: _selectedCoin,
|
||||
coins: market.allCoins
|
||||
@@ -143,16 +143,16 @@ class _TradePageState extends State<TradePage>
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
|
||||
// 价格卡片
|
||||
// 價格卡片
|
||||
if (_selectedCoin != null)
|
||||
PriceCard(coin: _selectedCoin!)
|
||||
else
|
||||
PlaceholderCard(
|
||||
message: '请先选择交易币种',
|
||||
message: '請先選擇交易幣種',
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
|
||||
// 交易表单卡片(内含买入/卖出切换 + 表单)
|
||||
// 交易表單卡片(內含買入/賣出切換 + 表單)
|
||||
TradeFormCard(
|
||||
tradeType: _tradeType,
|
||||
selectedCoin: _selectedCoin,
|
||||
@@ -170,7 +170,7 @@ class _TradePageState extends State<TradePage>
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
|
||||
// CTA 买入/卖出按钮
|
||||
// CTA 買入/賣出按鈕
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 48,
|
||||
@@ -195,7 +195,7 @@ class _TradePageState extends State<TradePage>
|
||||
if (_selectedCoin == null) return false;
|
||||
final amount = double.tryParse(_amountController.text) ?? 0;
|
||||
if (amount <= 0) return false;
|
||||
// 买入时校验不超过可用USDT
|
||||
// 買入時校驗不超過可用USDT
|
||||
if (_tradeType == 0) {
|
||||
final available = double.tryParse(_availableUsdt) ?? 0;
|
||||
if (amount > available) return false;
|
||||
@@ -243,16 +243,16 @@ class _TradePageState extends State<TradePage>
|
||||
|
||||
if (response.success) {
|
||||
_amountController.clear();
|
||||
// 刷新资产数据
|
||||
// 刷新資產數據
|
||||
context.read<AssetProvider>().refreshAll(force: true);
|
||||
_showResultDialog(true, '${isBuy ? '买入' : '卖出'}成功',
|
||||
_showResultDialog(true, '${isBuy ? '買入' : '賣出'}成功',
|
||||
'$quantity $coinCode @ $price USDT');
|
||||
} else {
|
||||
_showResultDialog(false, '交易失败', response.message ?? '请稍后重试');
|
||||
_showResultDialog(false, '交易失敗', response.message ?? '請稍後重試');
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
_showResultDialog(false, '交易失败', e.toString());
|
||||
_showResultDialog(false, '交易失敗', e.toString());
|
||||
}
|
||||
} finally {
|
||||
if (mounted) setState(() => _isSubmitting = false);
|
||||
@@ -279,7 +279,7 @@ class _TradePageState extends State<TradePage>
|
||||
description: Text(message),
|
||||
actions: [
|
||||
ShadButton(
|
||||
child: const Text('确定'),
|
||||
child: const Text('確定'),
|
||||
onPressed: () => Navigator.of(ctx).pop(),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user