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/utils/format_utils.dart'; import '../../providers/c2c_providers.dart'; import '../../providers/user_providers.dart'; import '../../providers/asset_providers.dart'; import '../../providers/trading_providers.dart'; class C2cPublishPage extends ConsumerStatefulWidget { const C2cPublishPage({super.key}); @override ConsumerState createState() => _C2cPublishPageState(); } class _C2cPublishPageState extends ConsumerState { static const Color _orange = Color(0xFFFF6B00); static const Color _green = Color(0xFF10B981); static const Color _red = Color(0xFFEF4444); static const Color _darkText = Color(0xFF1F2937); static const Color _grayText = Color(0xFF6B7280); static const Color _bgGray = Color(0xFFF3F4F6); int _selectedType = 1; // 0: 买入, 1: 卖出 final _priceController = TextEditingController(); final _quantityController = TextEditingController(); final _remarkController = TextEditingController(); // 收款信息(卖单必填) Set _selectedPaymentMethods = {'GREEN_POINTS'}; // 绿积分默认选中且不可取消 final _paymentAccountController = TextEditingController(); final _paymentRealNameController = TextEditingController(); @override void dispose() { _priceController.dispose(); _quantityController.dispose(); _remarkController.dispose(); _paymentAccountController.dispose(); _paymentRealNameController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final user = ref.watch(userNotifierProvider); final accountSequence = user.accountSequence ?? ''; final assetAsync = ref.watch(accountAssetProvider(accountSequence)); final priceAsync = ref.watch(currentPriceProvider); final c2cState = ref.watch(c2cTradingNotifierProvider); final asset = assetAsync.valueOrNull; final currentPrice = priceAsync.valueOrNull?.price ?? '0'; final availableShares = asset?.availableShares ?? '0'; final availableCash = asset?.availableCash ?? '0'; // 设置默认价格 if (_priceController.text.isEmpty && currentPrice != '0') { _priceController.text = currentPrice; } 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, ), body: SingleChildScrollView( child: Column( children: [ const SizedBox(height: 16), // 交易类型选择 _buildTypeSelector(), const SizedBox(height: 16), // 可用余额 _buildBalanceCard(availableShares, availableCash), const SizedBox(height: 16), // 价格输入 _buildPriceInput(currentPrice), const SizedBox(height: 16), // 数量输入 _buildQuantityInput(availableShares, availableCash, currentPrice), const SizedBox(height: 16), // 收款信息(卖单必填) if (_selectedType == 1) ...[ _buildPaymentInfoInput(), const SizedBox(height: 16), ], // 备注 _buildRemarkInput(), const SizedBox(height: 16), // 预估信息 _buildEstimateCard(), const SizedBox(height: 24), // 发布按钮 _buildPublishButton(c2cState), const SizedBox(height: 16), // 提示信息 _buildTips(), const SizedBox(height: 24), ], ), ), ); } Widget _buildTypeSelector() { return Container( margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.all(4), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), ), child: Row( children: [ Expanded( child: GestureDetector( onTap: () => setState(() => _selectedType = 0), child: Container( padding: const EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( color: _selectedType == 0 ? _green : Colors.transparent, borderRadius: BorderRadius.circular(8), ), child: Text( '我要买入', textAlign: TextAlign.center, style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: _selectedType == 0 ? Colors.white : _grayText, ), ), ), ), ), Expanded( child: GestureDetector( onTap: () => setState(() => _selectedType = 1), child: Container( padding: const EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( color: _selectedType == 1 ? _red : Colors.transparent, borderRadius: BorderRadius.circular(8), ), child: Text( '我要卖出', textAlign: TextAlign.center, style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: _selectedType == 1 ? Colors.white : _grayText, ), ), ), ), ), ], ), ); } Widget _buildBalanceCard(String availableShares, String availableCash) { // C2C交易的是积分值,买入广告需要买家有绿积分(积分值)来支付 // 卖出广告需要卖家有积分值来出售 return Container( margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '可用积分值', style: TextStyle(fontSize: 14, color: _grayText), ), Text( formatAmount(availableCash), style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: _green, ), ), ], ), ); } Widget _buildPriceInput(String currentPrice) { 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( '当前价: ${formatPrice(currentPrice)}', style: const TextStyle(fontSize: 12, color: _grayText), ), ], ), const SizedBox(height: 12), TextField( controller: _priceController, 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), ), suffixText: '元/积分值', suffixStyle: const TextStyle(color: _grayText, fontSize: 14), ), onChanged: (_) => setState(() {}), ), ], ), ); } Widget _buildQuantityInput( String availableShares, String availableCash, String currentPrice, ) { // C2C交易的是积分值 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: _quantityController, keyboardType: const TextInputType.numberWithOptions(decimal: true), inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,4}')), ], 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: () { // 填入全部可用积分值 _quantityController.text = availableCash; setState(() {}); }, child: const Text( '全部', style: TextStyle( color: _orange, fontWeight: FontWeight.w500, ), ), ), suffixText: '积分值', suffixStyle: const TextStyle(color: _grayText, fontSize: 14), ), onChanged: (_) => setState(() {}), ), ], ), ); } Widget _buildPaymentInfoInput() { 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( children: const [ Text( '收款信息', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: _darkText, ), ), SizedBox(width: 8), Text( '(必填)', style: TextStyle(fontSize: 12, color: _red), ), ], ), const SizedBox(height: 16), // 收款方式选择(多选) const Text( '收款方式(可多选)', style: TextStyle(fontSize: 14, color: _grayText), ), const SizedBox(height: 8), Wrap( spacing: 12, runSpacing: 8, children: [ // 绿积分 - 默认选中且不可取消 _buildPaymentMethodCheckbox('GREEN_POINTS', '绿积分', Icons.eco, isLocked: true), _buildPaymentMethodCheckbox('ALIPAY', '支付宝', Icons.account_balance_wallet), _buildPaymentMethodCheckbox('WECHAT', '微信', Icons.chat_bubble), _buildPaymentMethodCheckbox('BANK', '银行卡', Icons.credit_card), ], ), const SizedBox(height: 16), // 账户ID(自动读取,只读) const Text( '账户ID', style: TextStyle(fontSize: 14, color: _grayText), ), const SizedBox(height: 4), const Text( '买家将通过此账户向您转账绿积分', style: TextStyle(fontSize: 12, color: _grayText), ), const SizedBox(height: 8), Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), decoration: BoxDecoration( color: _bgGray, borderRadius: BorderRadius.circular(12), ), child: Text( ref.watch(userNotifierProvider).accountSequence ?? '', style: const TextStyle(fontSize: 15, color: _darkText), ), ), // 只有选择了非绿积分支付方式才显示收款账号和姓名 if (_selectedPaymentMethods.any((m) => m != 'GREEN_POINTS')) ...[ const SizedBox(height: 16), // 收款账号 const Text( '收款账号(其他支付方式)', style: TextStyle(fontSize: 14, color: _grayText), ), const SizedBox(height: 8), TextField( controller: _paymentAccountController, 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), ), ), ), const SizedBox(height: 16), // 收款人实名 const Text( '收款人姓名', style: TextStyle(fontSize: 14, color: _grayText), ), const SizedBox(height: 8), TextField( controller: _paymentRealNameController, 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), ), ), ), ], const SizedBox(height: 12), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: _green.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Row( children: const [ Icon(Icons.eco, size: 16, color: _green), SizedBox(width: 8), Expanded( child: Text( '绿积分支付:买家将在1.0系统中向您的ID转账绿积分', style: TextStyle(fontSize: 12, color: _green), ), ), ], ), ), ], ), ); } Widget _buildPaymentMethodCheckbox(String value, String label, IconData icon, {bool isLocked = false}) { final isSelected = _selectedPaymentMethods.contains(value); return GestureDetector( onTap: isLocked ? null : () { setState(() { if (isSelected) { _selectedPaymentMethods.remove(value); } else { _selectedPaymentMethods.add(value); } }); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: isSelected ? (value == 'GREEN_POINTS' ? _green : _orange) : _bgGray, borderRadius: BorderRadius.circular(8), border: isSelected ? null : Border.all(color: Colors.grey.shade300), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( isSelected ? Icons.check_circle : Icons.circle_outlined, size: 18, color: isSelected ? Colors.white : _grayText, ), const SizedBox(width: 6), Icon( icon, size: 16, color: isSelected ? Colors.white : _grayText, ), const SizedBox(width: 4), Text( label, style: TextStyle( fontSize: 13, color: isSelected ? Colors.white : _darkText, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), ), if (isLocked) ...[ const SizedBox(width: 4), Icon( Icons.lock, size: 12, color: isSelected ? Colors.white70 : _grayText, ), ], ], ), ), ); } Widget _buildRemarkInput() { 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: _remarkController, maxLength: 100, maxLines: 2, 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 _buildEstimateCard() { final price = double.tryParse(_priceController.text) ?? 0; final quantity = double.tryParse(_quantityController.text) ?? 0; final total = price * quantity; final isBuy = _selectedType == 0; return Container( margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: _orange.withOpacity(0.05), borderRadius: BorderRadius.circular(12), border: Border.all(color: _orange.withOpacity(0.2)), ), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('交易总额', style: TextStyle(fontSize: 14, color: _grayText)), Text( '${formatAmount(total.toString())} 积分值', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: _orange, ), ), ], ), const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('积分值数量', style: TextStyle(fontSize: 14, color: _grayText)), Text( '${formatAmount(quantity.toString())} 积分值', style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: _green, ), ), ], ), const SizedBox(height: 8), Divider(color: _orange.withOpacity(0.2)), const SizedBox(height: 8), Text( isBuy ? '发布后,其他用户可以接单卖出积分值给您' : '发布后,其他用户可以接单购买您的积分值', style: const TextStyle(fontSize: 12, color: _grayText), ), ], ), ); } Widget _buildPublishButton(C2cTradingState c2cState) { final price = double.tryParse(_priceController.text) ?? 0; final quantity = double.tryParse(_quantityController.text) ?? 0; final isSell = _selectedType == 1; // 验证条件 bool isValid = price > 0 && quantity > 0; if (isSell) { // 如果选择了其他支付方式,还需要填写收款账号和姓名 if (_selectedPaymentMethods.any((m) => m != 'GREEN_POINTS')) { isValid = isValid && _paymentAccountController.text.trim().isNotEmpty && _paymentRealNameController.text.trim().isNotEmpty; } } return Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: SizedBox( width: double.infinity, height: 50, child: ElevatedButton( onPressed: isValid && !c2cState.isLoading ? _handlePublish : null, style: ElevatedButton.styleFrom( backgroundColor: _selectedType == 0 ? _green : _red, foregroundColor: Colors.white, disabledBackgroundColor: (_selectedType == 0 ? _green : _red).withOpacity(0.4), disabledForegroundColor: Colors.white70, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: c2cState.isLoading ? const SizedBox( width: 24, height: 24, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : Text( _selectedType == 0 ? '发布买入广告' : '发布卖出广告', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ), ), ); } Widget _buildTips() { return Container( margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: const [ Icon(Icons.info_outline, size: 16, color: _orange), SizedBox(width: 8), Text( '交易说明', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: _orange, ), ), ], ), const SizedBox(height: 8), const Text( '1. 发布广告后,您的积分值将被冻结直到交易完成或取消\n' '2. 其他用户接单后,需在规定时间内完成交易\n' '3. 买方需在1.0系统中向卖方转账绿积分\n' '4. 卖方确认收到绿积分后,积分值自动划转给买方\n' '5. 如遇问题,请联系客服处理', style: TextStyle( fontSize: 12, color: _grayText, height: 1.5, ), ), ], ), ); } Future _handlePublish() async { final price = _priceController.text.trim(); final quantity = _quantityController.text.trim(); final remark = _remarkController.text.trim(); final type = _selectedType == 0 ? 'BUY' : 'SELL'; final isSell = _selectedType == 1; // 卖单验证收款信息 if (isSell) { // 如果选择了其他支付方式,需要填写收款账号和姓名 if (_selectedPaymentMethods.any((m) => m != 'GREEN_POINTS')) { if (_paymentAccountController.text.trim().isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('请填写收款账号'), backgroundColor: _red), ); return; } if (_paymentRealNameController.text.trim().isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('请填写收款人姓名'), backgroundColor: _red), ); return; } } } // 构建支付方式显示文本 final paymentMethodTexts = _selectedPaymentMethods.map((m) { switch (m) { case 'GREEN_POINTS': return '绿积分'; case 'ALIPAY': return '支付宝'; case 'WECHAT': return '微信'; case 'BANK': return '银行卡'; default: return m; } }).join('、'); // 确认对话框 final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: Text(_selectedType == 0 ? '确认发布买入广告' : '确认发布卖出广告'), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('单价: $price 积分值'), const SizedBox(height: 8), Text('数量: $quantity 积分值'), const SizedBox(height: 8), Text( '总额: ${formatAmount((double.parse(price) * double.parse(quantity)).toString())} 积分值', style: const TextStyle(fontWeight: FontWeight.bold), ), if (isSell) ...[ const SizedBox(height: 16), const Divider(), const SizedBox(height: 8), Text('收款方式: $paymentMethodTexts'), const SizedBox(height: 4), Text('账户ID: ${ref.read(userNotifierProvider).accountSequence ?? ''}'), if (_selectedPaymentMethods.any((m) => m != 'GREEN_POINTS')) ...[ const SizedBox(height: 4), Text('收款账号: ${_paymentAccountController.text.trim()}'), const SizedBox(height: 4), Text('收款人: ${_paymentRealNameController.text.trim()}'), ], ], const SizedBox(height: 16), const Text( '发布后,您的积分值将被冻结', style: TextStyle(fontSize: 12, color: _grayText), ), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('取消'), ), TextButton( onPressed: () => Navigator.pop(context, true), child: Text( '确认发布', style: TextStyle(color: _selectedType == 0 ? _green : _red), ), ), ], ), ); if (confirmed != true) return; final notifier = ref.read(c2cTradingNotifierProvider.notifier); final success = await notifier.createOrder( type: type, price: price, quantity: quantity, // 收款信息(卖单时传递) paymentMethod: isSell ? _selectedPaymentMethods.join(',') : null, paymentAccount: isSell ? _paymentAccountController.text.trim() : null, paymentRealName: isSell ? _paymentRealNameController.text.trim() : null, remark: remark.isEmpty ? null : remark, ); if (success && mounted) { // 刷新列表 ref.invalidate(c2cOrdersProvider(type)); ref.invalidate(myC2cOrdersProvider); // 刷新资产 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(c2cTradingNotifierProvider).error; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('发布失败: ${error ?? '未知错误'}'), backgroundColor: _red, ), ); } } }