import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/network/api_exception.dart'; import '../../core/theme/app_theme.dart'; import '../../providers/asset_provider.dart'; import '../../services/asset_service.dart'; import '../feed/widgets/feed_overlays.dart'; import 'assets_screen.dart'; /// 提现页(《03-接口文档》9.5/9.6):提现表单 + 提现记录 class WithdrawScreen extends ConsumerStatefulWidget { const WithdrawScreen({super.key}); @override ConsumerState createState() => _WithdrawScreenState(); } class _WithdrawScreenState extends ConsumerState { final _amountCtrl = TextEditingController(); final _accountCtrl = TextEditingController(); int _channel = 1; bool _submitting = false; static const _channels = [ {'value': 1, 'label': '支付宝', 'hint': '支付宝账号(手机号/邮箱)'}, {'value': 2, 'label': '微信', 'hint': '微信收款账号'}, {'value': 3, 'label': 'USDT', 'hint': 'USDT 收款地址(TRC20)'}, ]; @override void dispose() { _amountCtrl.dispose(); _accountCtrl.dispose(); super.dispose(); } Map get _channelCfg => _channels.firstWhere((c) => c['value'] == _channel); @override Widget build(BuildContext context) { final asset = ref.watch(assetProvider); final records = ref.watch(withdrawRecordsProvider); final wallet = asset.wallet; return Scaffold( appBar: AppBar(title: const Text('申请提现')), body: ListView( padding: const EdgeInsets.all(16), children: [ _buildForm(wallet), const SizedBox(height: 24), const Text( '提现记录', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700), ), const SizedBox(height: 8), if (records.isLoading && records.items.isEmpty) const Padding( padding: EdgeInsets.symmetric(vertical: 32), child: Center(child: CircularProgressIndicator()), ) else if (records.items.isEmpty) const Padding( padding: EdgeInsets.symmetric(vertical: 32), child: Center( child: Text('暂无提现记录', style: TextStyle(color: AppColors.textWeak)), ), ) else ...[ for (final r in records.items) _WithdrawTile(record: r), if (records.hasMore) TextButton( onPressed: () => ref.read(withdrawRecordsProvider.notifier).loadMore(), child: const Text('加载更多'), ), ], ], ), ); } Widget _buildForm(AssetWallet? wallet) { final hasPending = wallet?.hasPendingWithdraw ?? false; return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( '可提资产 ${fmtAssets(wallet?.assets ?? 0)}', style: const TextStyle(color: AppColors.textSecondary), ), const Spacer(), Text( '最低 ${wallet?.minWithdrawAssets ?? 100} 资产', style: const TextStyle(color: AppColors.textWeak, fontSize: 12), ), ], ), const SizedBox(height: 12), // 金额 TextField( controller: _amountCtrl, enabled: !hasPending, keyboardType: const TextInputType.numberWithOptions(decimal: true), inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,2}')), ], decoration: InputDecoration( labelText: '提现金额(资产)', suffix: TextButton( onPressed: hasPending || wallet == null ? null : () => _amountCtrl.text = fmtAssets(wallet.assets), child: const Text('全部提现'), ), ), ), const SizedBox(height: 12), // 渠道 Row( children: _channels.map((c) { final active = _channel == c['value']; return Padding( padding: const EdgeInsets.only(right: 8), child: ChoiceChip( label: Text(c['label'] as String), selected: active, onSelected: hasPending ? null : (_) => setState(() => _channel = c['value'] as int), selectedColor: AppColors.primary, backgroundColor: AppColors.surface, labelStyle: TextStyle( color: active ? Colors.white : AppColors.textSecondary, ), ), ); }).toList(), ), const SizedBox(height: 12), // 账号 TextField( controller: _accountCtrl, enabled: !hasPending, decoration: InputDecoration( labelText: '收款账号', hintText: _channelCfg['hint'] as String, ), ), if (hasPending) ...[ const SizedBox(height: 12), const Text( '有一笔提现审核中,审核完成后才能再次申请', style: TextStyle(color: AppColors.gold, fontSize: 12), ), ], const SizedBox(height: 16), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _submitting || hasPending ? null : () => _submit(wallet), style: ElevatedButton.styleFrom( minimumSize: const Size(0, 44), ), child: Text(_submitting ? '提交中…' : '提交申请'), ), ), ], ), ); } Future _submit(AssetWallet? wallet) async { final amount = double.tryParse(_amountCtrl.text.trim()); final account = _accountCtrl.text.trim(); final minAssets = wallet?.minWithdrawAssets ?? 100; if (amount == null || amount <= 0) { showTopToast(context, '请输入提现金额'); return; } if (amount < minAssets) { showTopToast(context, '最低提现 $minAssets 资产'); return; } if (wallet != null && amount > wallet.assets) { showTopToast(context, '超出可提资产余额'); return; } if (account.isEmpty) { showTopToast(context, '请输入收款账号'); return; } setState(() => _submitting = true); try { final result = await AssetService.applyWithdraw( assets: amount, channel: _channel, account: account, ); if (!mounted) return; showTopToast( context, '提现申请已提交:¥${result.amountYuan.toStringAsFixed(2)},等待审核', ); _amountCtrl.clear(); // 刷新资产与记录 ref.read(assetProvider.notifier).load(); ref.read(withdrawRecordsProvider.notifier).load(); } on ApiException catch (e) { if (mounted) showTopToast(context, e.message); } catch (_) { if (mounted) showTopToast(context, '提交失败,请稍后重试'); } finally { if (mounted) setState(() => _submitting = false); } } } class _WithdrawTile extends StatelessWidget { const _WithdrawTile({required this.record}); final WithdrawRecord record; Color get _statusColor => switch (record.status) { 1 => Colors.greenAccent, 2 => AppColors.primary, _ => AppColors.gold, }; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '¥${record.amountYuan.toStringAsFixed(2)}(${fmtAssets(record.assets)} 资产)', style: const TextStyle(fontSize: 15), ), Text( '${record.channelLabel} · ${record.withdrawNo}', maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( color: AppColors.textWeak, fontSize: 12, ), ), Text( record.createdAt, style: const TextStyle( color: AppColors.textWeak, fontSize: 11, ), ), ], ), ), Text( record.statusText, style: TextStyle(color: _statusColor, fontSize: 13), ), ], ), ); } }