Move skills system documentation from bottom to top of CLAUDE.md for better organization. Refactor Flutter asset page by extracting UI components into separate files and updating import structure for improved modularity.
603 lines
21 KiB
Dart
603 lines
21 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:shadcn_ui/shadcn_ui.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
|
import 'package:provider/provider.dart';
|
|
import '../../../../core/theme/app_color_scheme.dart';
|
|
import '../../../../core/theme/app_spacing.dart';
|
|
import '../../../../core/utils/toast_utils.dart';
|
|
import '../../../../providers/asset_provider.dart';
|
|
import '../../../components/glass_panel.dart';
|
|
import '../../../components/neon_glow.dart';
|
|
import '../../../shared/ui_constants.dart';
|
|
|
|
// ============================================
|
|
// Dialog helpers — shared sub-widgets
|
|
// ============================================
|
|
|
|
/// 信息行 — 用于对话框中显示 label/value 键值对
|
|
class InfoRow extends StatelessWidget {
|
|
final String label;
|
|
final String value;
|
|
final bool isBold;
|
|
|
|
const InfoRow({
|
|
super.key,
|
|
required this.label,
|
|
required this.value,
|
|
this.isBold = false,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
|
|
return Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
label,
|
|
style: GoogleFonts.inter(
|
|
fontSize: 12,
|
|
color: colorScheme.onSurfaceVariant,
|
|
),
|
|
),
|
|
Text(
|
|
value,
|
|
style: GoogleFonts.inter(
|
|
fontSize: 12,
|
|
fontWeight: isBold ? FontWeight.bold : FontWeight.normal,
|
|
color: colorScheme.onSurface,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 钱包地址卡片 — 用于充值结果对话框中展示钱包地址
|
|
class WalletAddressCard extends StatelessWidget {
|
|
final String address;
|
|
final String network;
|
|
|
|
const WalletAddressCard({
|
|
super.key,
|
|
required this.address,
|
|
required this.network,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
|
|
return Container(
|
|
padding: const EdgeInsets.all(AppSpacing.md),
|
|
decoration: BoxDecoration(
|
|
color: colorScheme.surfaceContainerHigh,
|
|
borderRadius: BorderRadius.circular(AppRadius.md),
|
|
border: Border.all(
|
|
color: colorScheme.outlineVariant.withValues(alpha: 0.3),
|
|
),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
address,
|
|
style: const TextStyle(
|
|
fontFamily: 'monospace',
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
),
|
|
GestureDetector(
|
|
onTap: () {
|
|
Clipboard.setData(ClipboardData(text: address));
|
|
ToastUtils.show('地址已复制到剪贴板');
|
|
},
|
|
child: Container(
|
|
padding: const EdgeInsets.all(AppSpacing.xs),
|
|
decoration: BoxDecoration(
|
|
color: colorScheme.primary.withValues(alpha: 0.1),
|
|
borderRadius: BorderRadius.circular(AppRadius.sm),
|
|
),
|
|
child: Icon(
|
|
LucideIcons.copy,
|
|
size: 16,
|
|
color: colorScheme.primary,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: AppSpacing.sm),
|
|
Text(
|
|
'网络: $network',
|
|
style: GoogleFonts.inter(
|
|
fontSize: 11,
|
|
color: colorScheme.onSurfaceVariant,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// Dialog functions — kept from original with style updates
|
|
// ============================================
|
|
|
|
/// 充值对话框
|
|
void showDepositDialog(BuildContext context) {
|
|
final amountController = TextEditingController();
|
|
final formKey = GlobalKey<ShadFormState>();
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
|
|
showShadDialog(
|
|
context: context,
|
|
builder: (ctx) => Dialog(
|
|
backgroundColor: Colors.transparent,
|
|
child: GlassPanel(
|
|
borderRadius: BorderRadius.circular(AppRadius.lg),
|
|
padding: const EdgeInsets.all(AppSpacing.lg),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'充值',
|
|
style: GoogleFonts.inter(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w700,
|
|
color: colorScheme.onSurface,
|
|
),
|
|
),
|
|
const SizedBox(height: AppSpacing.xs),
|
|
Text(
|
|
'Asset: USDT',
|
|
style: GoogleFonts.inter(
|
|
fontSize: 12,
|
|
color: colorScheme.onSurfaceVariant,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
Container(
|
|
padding: const EdgeInsets.all(AppSpacing.sm),
|
|
decoration: BoxDecoration(
|
|
color: colorScheme.surfaceContainerHigh,
|
|
borderRadius: BorderRadius.circular(AppRadius.md),
|
|
),
|
|
child: Icon(
|
|
LucideIcons.wallet,
|
|
color: colorScheme.secondary,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: AppSpacing.lg),
|
|
ShadForm(
|
|
key: formKey,
|
|
child: ShadInputFormField(
|
|
id: 'amount',
|
|
controller: amountController,
|
|
label: const Text('充值金额'),
|
|
placeholder: const Text('最低 1000 USDT'),
|
|
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
|
validator: (v) {
|
|
if (v == null || v.isEmpty) return '请输入金额';
|
|
final n = double.tryParse(v);
|
|
if (n == null || n <= 0) return '请输入有效金额';
|
|
if (n < 1000) return '单笔最低充值1000 USDT';
|
|
return null;
|
|
},
|
|
),
|
|
),
|
|
const SizedBox(height: AppSpacing.lg),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: NeonButton(
|
|
text: '取消',
|
|
type: NeonButtonType.outline,
|
|
onPressed: () => Navigator.of(ctx).pop(),
|
|
height: 48,
|
|
showGlow: false,
|
|
),
|
|
),
|
|
const SizedBox(width: AppSpacing.sm),
|
|
Expanded(
|
|
child: NeonButton(
|
|
text: '下一步',
|
|
type: NeonButtonType.primary,
|
|
onPressed: () async {
|
|
if (formKey.currentState!.saveAndValidate()) {
|
|
Navigator.of(ctx).pop();
|
|
final response = await context.read<AssetProvider>().deposit(
|
|
amount: amountController.text,
|
|
);
|
|
if (context.mounted) {
|
|
if (response.success && response.data != null) {
|
|
showDepositResultDialog(context, response.data!);
|
|
} else {
|
|
showResultDialog(context, '申请失败', response.message);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
height: 48,
|
|
showGlow: true,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 充值结果对话框 — 展示钱包地址和确认打款
|
|
void showDepositResultDialog(BuildContext context, Map<String, dynamic> data) {
|
|
final orderNo = data['orderNo'] as String? ?? '';
|
|
final amount = data['amount']?.toString() ?? '0.00';
|
|
final walletAddress = data['walletAddress'] as String? ?? '';
|
|
final walletNetwork = data['walletNetwork'] as String? ?? 'TRC20';
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
|
|
|
showShadDialog(
|
|
context: context,
|
|
builder: (ctx) => Dialog(
|
|
backgroundColor: Colors.transparent,
|
|
child: GlassPanel(
|
|
borderRadius: BorderRadius.circular(AppRadius.lg),
|
|
padding: const EdgeInsets.all(AppSpacing.lg),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
NeonIcon(
|
|
icon: Icons.check_circle,
|
|
color: AppColorScheme.getUpColor(isDark),
|
|
size: 24,
|
|
),
|
|
const SizedBox(width: AppSpacing.sm),
|
|
Text(
|
|
'充值申请成功',
|
|
style: GoogleFonts.inter(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w700,
|
|
color: colorScheme.onSurface,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: AppSpacing.lg),
|
|
InfoRow(label: '订单号', value: orderNo),
|
|
const SizedBox(height: AppSpacing.sm),
|
|
InfoRow(label: '充值金额', value: '$amount USDT', isBold: true),
|
|
const SizedBox(height: AppSpacing.lg),
|
|
Text(
|
|
'请向以下地址转账:',
|
|
style: GoogleFonts.inter(
|
|
fontSize: 12,
|
|
color: colorScheme.onSurfaceVariant,
|
|
),
|
|
),
|
|
const SizedBox(height: AppSpacing.sm),
|
|
WalletAddressCard(address: walletAddress, network: walletNetwork),
|
|
const SizedBox(height: AppSpacing.md),
|
|
Container(
|
|
padding: const EdgeInsets.all(AppSpacing.sm),
|
|
decoration: BoxDecoration(
|
|
color: AppColorScheme.warning.withValues(alpha: 0.1),
|
|
borderRadius: BorderRadius.circular(AppRadius.md),
|
|
border: Border.all(
|
|
color: AppColorScheme.warning.withValues(alpha: 0.2),
|
|
),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(Icons.info_outline, size: 16, color: AppColorScheme.warning),
|
|
const SizedBox(width: AppSpacing.sm),
|
|
Expanded(
|
|
child: Text(
|
|
'转账完成后请点击"已打款"按钮确认',
|
|
style: GoogleFonts.inter(fontSize: 12, color: AppColorScheme.warning),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: AppSpacing.lg),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: NeonButton(
|
|
text: '稍后确认',
|
|
type: NeonButtonType.outline,
|
|
onPressed: () => Navigator.of(ctx).pop(),
|
|
height: 44,
|
|
showGlow: false,
|
|
),
|
|
),
|
|
const SizedBox(width: AppSpacing.sm),
|
|
Expanded(
|
|
child: NeonButton(
|
|
text: '已打款',
|
|
type: NeonButtonType.primary,
|
|
onPressed: () async {
|
|
Navigator.of(ctx).pop();
|
|
final response = await context.read<AssetProvider>().confirmPay(orderNo);
|
|
if (context.mounted) {
|
|
showResultDialog(
|
|
context,
|
|
response.success ? '确认成功' : '确认失败',
|
|
response.success ? '请等待管理员审核' : response.message,
|
|
);
|
|
}
|
|
},
|
|
height: 44,
|
|
showGlow: true,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 提现对话框
|
|
void showWithdrawDialog(BuildContext context, String? balance) {
|
|
final amountController = TextEditingController();
|
|
final addressController = TextEditingController();
|
|
final contactController = TextEditingController();
|
|
final formKey = GlobalKey<ShadFormState>();
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
|
|
showShadDialog(
|
|
context: context,
|
|
builder: (ctx) => Dialog(
|
|
backgroundColor: Colors.transparent,
|
|
child: GlassPanel(
|
|
borderRadius: BorderRadius.circular(AppRadius.lg),
|
|
padding: const EdgeInsets.all(AppSpacing.lg),
|
|
child: SingleChildScrollView(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(AppSpacing.sm),
|
|
decoration: BoxDecoration(
|
|
color: colorScheme.primary.withValues(alpha: 0.1),
|
|
borderRadius: BorderRadius.circular(AppRadius.md),
|
|
),
|
|
child: Icon(
|
|
LucideIcons.wallet,
|
|
color: colorScheme.primary,
|
|
),
|
|
),
|
|
const SizedBox(width: AppSpacing.sm),
|
|
Text(
|
|
'提现',
|
|
style: GoogleFonts.inter(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w700,
|
|
color: colorScheme.onSurface,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: AppSpacing.xs),
|
|
Text(
|
|
'安全地将您的资产转移到外部钱包地址',
|
|
style: GoogleFonts.inter(
|
|
fontSize: 12,
|
|
color: colorScheme.onSurfaceVariant,
|
|
),
|
|
),
|
|
if (balance != null) ...[
|
|
const SizedBox(height: AppSpacing.md),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: AppSpacing.md,
|
|
vertical: AppSpacing.sm,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: AppColorScheme.up.withValues(alpha: 0.1),
|
|
borderRadius: BorderRadius.circular(AppRadius.full),
|
|
border: Border.all(
|
|
color: AppColorScheme.up.withValues(alpha: 0.2),
|
|
),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
'可用余额: ',
|
|
style: GoogleFonts.inter(
|
|
fontSize: 10,
|
|
color: colorScheme.onSurfaceVariant,
|
|
),
|
|
),
|
|
Text(
|
|
'$balance USDT',
|
|
style: GoogleFonts.inter(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.bold,
|
|
color: AppColorScheme.up,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
const SizedBox(height: AppSpacing.lg),
|
|
ShadForm(
|
|
key: formKey,
|
|
child: Column(
|
|
children: [
|
|
ShadInputFormField(
|
|
id: 'amount',
|
|
controller: amountController,
|
|
label: const Text('提现金额'),
|
|
placeholder: const Text('请输入提现金额(USDT)'),
|
|
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
|
validator: Validators.amount,
|
|
),
|
|
const SizedBox(height: AppSpacing.md),
|
|
ShadInputFormField(
|
|
id: 'address',
|
|
controller: addressController,
|
|
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('联系方式'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: AppSpacing.lg),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: NeonButton(
|
|
text: '取消',
|
|
type: NeonButtonType.outline,
|
|
onPressed: () => Navigator.of(ctx).pop(),
|
|
height: 44,
|
|
showGlow: false,
|
|
),
|
|
),
|
|
const SizedBox(width: AppSpacing.sm),
|
|
Expanded(
|
|
child: NeonButton(
|
|
text: '提交',
|
|
type: NeonButtonType.primary,
|
|
onPressed: () async {
|
|
if (formKey.currentState!.saveAndValidate()) {
|
|
Navigator.of(ctx).pop();
|
|
final response = await context.read<AssetProvider>().withdraw(
|
|
amount: amountController.text,
|
|
withdrawAddress: addressController.text,
|
|
withdrawContact: contactController.text.isNotEmpty
|
|
? contactController.text
|
|
: null,
|
|
);
|
|
if (context.mounted) {
|
|
showResultDialog(
|
|
context,
|
|
response.success ? '申请成功' : '申请失败',
|
|
response.success ? '请等待管理员审批' : response.message,
|
|
);
|
|
}
|
|
}
|
|
},
|
|
height: 44,
|
|
showGlow: true,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: AppSpacing.md),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Icon(
|
|
Icons.verified_user,
|
|
size: 12,
|
|
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.5),
|
|
),
|
|
const SizedBox(width: AppSpacing.xs),
|
|
Text(
|
|
'End-to-End Encrypted Transaction',
|
|
style: GoogleFonts.inter(
|
|
fontSize: 10,
|
|
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.5),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 通用结果对话框 — 展示操作成功/失败信息
|
|
void showResultDialog(BuildContext context, String title, String? message) {
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
|
|
showShadDialog(
|
|
context: context,
|
|
builder: (ctx) => Dialog(
|
|
backgroundColor: Colors.transparent,
|
|
child: GlassPanel(
|
|
borderRadius: BorderRadius.circular(AppRadius.lg),
|
|
padding: const EdgeInsets.all(AppSpacing.lg),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
title,
|
|
style: GoogleFonts.inter(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w700,
|
|
color: colorScheme.onSurface,
|
|
),
|
|
),
|
|
if (message != null) ...[
|
|
const SizedBox(height: AppSpacing.sm),
|
|
Text(
|
|
message,
|
|
style: GoogleFonts.inter(color: colorScheme.onSurfaceVariant),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
],
|
|
const SizedBox(height: AppSpacing.lg),
|
|
SizedBox(
|
|
width: double.infinity,
|
|
child: NeonButton(
|
|
text: '确定',
|
|
type: NeonButtonType.primary,
|
|
onPressed: () => Navigator.of(ctx).pop(),
|
|
height: 44,
|
|
showGlow: false,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|