import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../../core/router/routes.dart'; import '../../../core/utils/format_utils.dart'; import '../../providers/user_providers.dart'; import '../../providers/asset_providers.dart'; import '../../providers/transfer_providers.dart'; import '../../widgets/qr_scanner_sheet.dart'; class SendSharesPage extends ConsumerStatefulWidget { const SendSharesPage({super.key}); @override ConsumerState createState() => _SendSharesPageState(); } class _SendSharesPageState extends ConsumerState { static const Color _orange = Color(0xFFFF6B00); static const Color _green = Color(0xFF10B981); static const Color _darkText = Color(0xFF1F2937); static const Color _grayText = Color(0xFF6B7280); static const Color _bgGray = Color(0xFFF3F4F6); static const Color _red = Color(0xFFEF4444); final _phoneController = TextEditingController(); final _amountController = TextEditingController(); final _memoController = TextEditingController(); bool _isRecipientVerified = false; String? _recipientNickname; @override void dispose() { _phoneController.dispose(); _amountController.dispose(); _memoController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final user = ref.watch(userNotifierProvider); final accountSequence = user.accountSequence ?? ''; final assetAsync = ref.watch(accountAssetProvider(accountSequence)); final transferState = ref.watch(transferNotifierProvider); final availableCash = assetAsync.valueOrNull?.availableCash ?? '0'; return Scaffold( backgroundColor: _bgGray, appBar: AppBar( backgroundColor: Colors.white, elevation: 0, leading: IconButton( icon: const Icon(Icons.arrow_back, color: _darkText), onPressed: () => context.pop(), ), title: const Text( '发送积分值', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: _darkText, ), ), centerTitle: true, actions: [ TextButton( onPressed: () => context.push(Routes.p2pTransferRecords), child: const Text( '转账记录', style: TextStyle( fontSize: 14, color: _orange, ), ), ), ], ), body: SingleChildScrollView( child: Column( children: [ const SizedBox(height: 16), // 收款方账号 _buildRecipientSection(transferState), const SizedBox(height: 16), // 转账金额 _buildAmountSection(availableCash), const SizedBox(height: 16), // 备注 _buildMemoSection(), const SizedBox(height: 32), // 发送按钮 _buildSendButton(transferState, availableCash), const SizedBox(height: 16), // 提示信息 _buildTips(), ], ), ), ); } Widget _buildRecipientSection(TransferState transferState) { return Container( margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '收款方账号', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: _darkText, ), ), const SizedBox(height: 12), Row( children: [ Expanded( child: TextField( controller: _phoneController, keyboardType: TextInputType.phone, maxLength: 11, inputFormatters: [FilteringTextInputFormatter.digitsOnly], decoration: InputDecoration( hintText: '请输入收款方账号', hintStyle: const TextStyle(color: _grayText), filled: true, fillColor: _bgGray, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: _orange, width: 2), ), counterText: '', suffixIcon: Row( mainAxisSize: MainAxisSize.min, children: [ // 扫码按钮 IconButton( icon: const Icon(Icons.qr_code_scanner, color: _orange), onPressed: _scanQrCode, tooltip: '扫描二维码', ), // 清除按钮 if (_phoneController.text.isNotEmpty) IconButton( icon: const Icon(Icons.clear, color: _grayText), onPressed: () { setState(() { _phoneController.clear(); _isRecipientVerified = false; _recipientNickname = null; }); }, ), ], ), ), onChanged: (value) { setState(() { _isRecipientVerified = false; _recipientNickname = null; }); }, ), ), const SizedBox(width: 12), SizedBox( height: 48, child: ElevatedButton( onPressed: transferState.isLoading ? null : _verifyRecipient, style: ElevatedButton.styleFrom( backgroundColor: _orange, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), padding: const EdgeInsets.symmetric(horizontal: 16), ), child: transferState.isLoading ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : const Text('验证'), ), ), ], ), if (_isRecipientVerified && _recipientNickname != null) ...[ const SizedBox(height: 12), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: _green.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Row( children: [ const Icon(Icons.check_circle, color: _green, size: 20), const SizedBox(width: 8), Expanded( child: Text( '收款方: $_recipientNickname', style: const TextStyle( fontSize: 14, color: _green, fontWeight: FontWeight.w500, ), ), ), ], ), ), ], if (transferState.error != null && !_isRecipientVerified && _phoneController.text.length == 11) ...[ const SizedBox(height: 12), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: _red.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Row( children: [ const Icon(Icons.error_outline, color: _red, size: 20), const SizedBox(width: 8), Expanded( child: Text( '用户不存在或无法转账', style: const TextStyle( fontSize: 14, color: _red, ), ), ), ], ), ), ], ], ), ); } Widget _buildAmountSection(String availableCash) { return Container( margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '转账数量', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: _darkText, ), ), Text( '可用: ${formatAmount(availableCash)}', style: const TextStyle( fontSize: 12, color: _grayText, ), ), ], ), const SizedBox(height: 12), TextField( controller: _amountController, keyboardType: const TextInputType.numberWithOptions(decimal: true), inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,8}')), ], decoration: InputDecoration( hintText: '请输入转账数量', hintStyle: const TextStyle(color: _grayText), filled: true, fillColor: _bgGray, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: _orange, width: 2), ), suffixIcon: TextButton( onPressed: () { _amountController.text = availableCash; }, child: const Text( '全部', style: TextStyle( color: _orange, fontWeight: FontWeight.w500, ), ), ), ), onChanged: (value) { setState(() {}); }, ), ], ), ); } Widget _buildMemoSection() { return Container( margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '备注 (可选)', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: _darkText, ), ), const SizedBox(height: 12), TextField( controller: _memoController, maxLength: 50, decoration: InputDecoration( hintText: '添加备注信息', hintStyle: const TextStyle(color: _grayText), filled: true, fillColor: _bgGray, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: _orange, width: 2), ), counterStyle: const TextStyle(color: _grayText), ), ), ], ), ); } Widget _buildSendButton(TransferState transferState, String availableCash) { final amount = double.tryParse(_amountController.text) ?? 0; final available = double.tryParse(availableCash) ?? 0; final isValid = _isRecipientVerified && amount > 0 && amount <= available; return Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: SizedBox( width: double.infinity, height: 50, child: ElevatedButton( onPressed: isValid && !transferState.isLoading ? _handleTransfer : null, style: ElevatedButton.styleFrom( backgroundColor: _orange, foregroundColor: Colors.white, disabledBackgroundColor: _orange.withOpacity(0.4), disabledForegroundColor: Colors.white70, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: transferState.isLoading ? const SizedBox( width: 24, height: 24, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : const Text( '确认发送', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ), ), ); } Widget _buildTips() { return Container( margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: _orange.withOpacity(0.05), borderRadius: BorderRadius.circular(12), ), child: const Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.info_outline, size: 16, color: _orange), SizedBox(width: 8), Text( '温馨提示', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: _orange, ), ), ], ), SizedBox(height: 8), Text( '1. 转账前请确认收款方账号正确\n2. 积分值转账不可撤销,请谨慎操作\n3. 转账后将从您的可用积分值中扣除\n4. 积分值是通过卖出积分股获得的,如需转账请先卖出积分股', style: TextStyle( fontSize: 12, color: _grayText, height: 1.5, ), ), ], ), ); } /// 扫描二维码 Future _scanQrCode() async { final result = await QrScannerSheet.show(context); if (result == null) return; // 解析二维码内容 final phone = parseTransferQrCode(result); if (phone != null) { setState(() { _phoneController.text = phone; _isRecipientVerified = false; _recipientNickname = null; }); // 自动验证收款方 _verifyRecipient(); } else { // 二维码格式不正确 if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('无效的转账二维码'), backgroundColor: _red, ), ); } } } Future _verifyRecipient() async { final phone = _phoneController.text.trim(); if (phone.length != 11) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('请输入正确的11位账号'), backgroundColor: _red, ), ); return; } // 检查是否是自己的账号 final user = ref.read(userNotifierProvider); if (phone == user.phone) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('不能转账给自己'), backgroundColor: _red, ), ); return; } final notifier = ref.read(transferNotifierProvider.notifier); final account = await notifier.lookupRecipient(phone); if (account != null && account.exists) { setState(() { _isRecipientVerified = true; _recipientNickname = account.nickname ?? _maskPhone(phone); }); // 显示验证成功提示 if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('收款方验证成功: ${account.nickname ?? _maskPhone(phone)}'), backgroundColor: _green, ), ); } } else { setState(() { _isRecipientVerified = false; _recipientNickname = null; }); // 显示验证失败提示 if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('用户不存在或无法转账'), backgroundColor: _red, ), ); } } } String _maskPhone(String phone) { if (phone.length != 11) return phone; return '${phone.substring(0, 3)}****${phone.substring(7)}'; } Future _handleTransfer() async { final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('确认转账'), content: Text( '确定要向 $_recipientNickname 发送 ${_amountController.text} 积分值吗?\n\n此操作不可撤销。', ), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('取消'), ), TextButton( onPressed: () => Navigator.pop(context, true), child: const Text( '确认', style: TextStyle(color: _orange), ), ), ], ), ); if (confirmed != true) return; final notifier = ref.read(transferNotifierProvider.notifier); final success = await notifier.transfer( toPhone: _phoneController.text.trim(), amount: _amountController.text.trim(), memo: _memoController.text.trim().isEmpty ? null : _memoController.text.trim(), ); if (success && mounted) { // 刷新资产数据 final user = ref.read(userNotifierProvider); ref.invalidate(accountAssetProvider(user.accountSequence ?? '')); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('转账成功'), backgroundColor: _green, ), ); context.pop(); } else if (mounted) { final error = ref.read(transferNotifierProvider).error; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('转账失败: ${error ?? '未知错误'}'), backgroundColor: _red, ), ); } } }