diff --git a/frontend/mobile-app/lib/features/transfer/presentation/pages/transfer_detail_page.dart b/frontend/mobile-app/lib/features/transfer/presentation/pages/transfer_detail_page.dart index 07e81750..5f2b70d5 100644 --- a/frontend/mobile-app/lib/features/transfer/presentation/pages/transfer_detail_page.dart +++ b/frontend/mobile-app/lib/features/transfer/presentation/pages/transfer_detail_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; import '../../../../core/di/injection_container.dart'; import '../../../../core/services/transfer_service.dart'; @@ -98,16 +99,36 @@ class _TransferDetailPageState extends ConsumerState { builder: (ctx) { String text = ''; return AlertDialog( - title: const Text('取消转让'), + backgroundColor: const Color(0xFFFFF7E6), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + title: const Text( + '取消转让', + style: TextStyle(color: Color(0xFF5D4037), fontWeight: FontWeight.w700), + ), content: TextField( onChanged: (v) => text = v, - decoration: const InputDecoration(hintText: '取消原因(可选)'), + style: const TextStyle(color: Color(0xFF5D4037)), + decoration: InputDecoration( + hintText: '取消原因(可选)', + hintStyle: const TextStyle(color: Color(0xFF745D43)), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: const BorderSide(color: Color(0xFFEAE0C8)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: const BorderSide(color: Color(0xFFD4AF37)), + ), + ), ), actions: [ - TextButton(onPressed: () => Navigator.pop(ctx), child: const Text('返回')), + TextButton( + onPressed: () => Navigator.pop(ctx), + child: const Text('返回', style: TextStyle(color: Color(0xFF745D43))), + ), TextButton( onPressed: () => Navigator.pop(ctx, text.isEmpty ? '用户主动取消' : text), - child: const Text('确认取消', style: TextStyle(color: Colors.red)), + child: const Text('确认取消', style: TextStyle(color: Color(0xFFE65100))), ), ], ); @@ -135,16 +156,78 @@ class _TransferDetailPageState extends ConsumerState { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: const Color(0xFF1A1A2E), - appBar: AppBar( - backgroundColor: const Color(0xFF1A1A2E), - foregroundColor: Colors.white, - title: const Text( - '转让详情', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Colors.white), + body: Container( + width: double.infinity, + height: double.infinity, + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xFFFFF7E6), + Color(0xFFEAE0C8), + ], + ), + ), + child: SafeArea( + child: Column( + children: [ + _buildHeader(), + Expanded(child: _buildBody()), + ], + ), ), ), - body: _buildBody(), + ); + } + + Widget _buildHeader() { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Row( + children: [ + GestureDetector( + onTap: () => context.pop(), + child: Container( + width: 32, + height: 32, + alignment: Alignment.center, + child: const Icon( + Icons.arrow_back_ios, + color: Color(0xFFD4AF37), + size: 20, + ), + ), + ), + const SizedBox(width: 4), + GestureDetector( + onTap: () => context.pop(), + child: const Text( + '返回', + style: TextStyle( + fontSize: 16, + fontFamily: 'Inter', + height: 1.5, + color: Color(0xFFD4AF37), + ), + ), + ), + const SizedBox(width: 42), + const Expanded( + child: Text( + '转让详情', + style: TextStyle( + fontSize: 18, + fontFamily: 'Inter', + fontWeight: FontWeight.w700, + height: 1.25, + letterSpacing: -0.27, + color: Color(0xFF5D4037), + ), + ), + ), + ], + ), ); } @@ -157,8 +240,21 @@ class _TransferDetailPageState extends ConsumerState { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text('加载失败', style: TextStyle(color: Colors.white.withValues(alpha: 0.7))), - TextButton(onPressed: _loadData, child: const Text('重试', style: TextStyle(color: Color(0xFFD4AF37)))), + const Icon(Icons.error_outline, color: Color(0xFFE65100), size: 32), + const SizedBox(height: 8), + const Text('加载失败', style: TextStyle(color: Color(0xFF5D4037), fontSize: 16)), + const SizedBox(height: 8), + GestureDetector( + onTap: _loadData, + child: const Text( + '点击重试', + style: TextStyle( + color: Color(0xFFD4AF37), + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), ], ), ); @@ -191,9 +287,20 @@ class _TransferDetailPageState extends ConsumerState { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: const Color(0xFF16213E), + color: const Color(0x99FFFFFF), borderRadius: BorderRadius.circular(12), - border: Border.all(color: Colors.white.withValues(alpha: 0.08)), + boxShadow: const [ + BoxShadow( + color: Color(0x1A000000), + blurRadius: 6, + offset: Offset(0, 4), + ), + BoxShadow( + color: Color(0x1A000000), + blurRadius: 4, + offset: Offset(0, 2), + ), + ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -205,7 +312,7 @@ class _TransferDetailPageState extends ConsumerState { Expanded( child: Text( order.transferOrderNo, - style: const TextStyle(fontSize: 14, color: Colors.white70, fontFamily: 'Inter'), + style: const TextStyle(fontSize: 14, color: Color(0xFF745D43), fontFamily: 'Inter'), overflow: TextOverflow.ellipsis, ), ), @@ -236,16 +343,14 @@ class _TransferDetailPageState extends ConsumerState { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(label, style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: 0.5))), - Text(value, style: const TextStyle(fontSize: 13, color: Colors.white, fontFamily: 'Inter')), + Text(label, style: const TextStyle(fontSize: 13, color: Color(0xFF745D43))), + Text(value, style: const TextStyle(fontSize: 13, color: Color(0xFF5D4037), fontFamily: 'Inter')), ], ), ); } Widget _buildSagaTimeline(TransferOrderDetail order) { - final currentStatusStr = order.status.name.toUpperCase(); - // Map enum name to API status string final statusApiMap = { 'pending': 'PENDING', 'sellerConfirmed': 'SELLER_CONFIRMED', @@ -257,7 +362,7 @@ class _TransferDetailPageState extends ConsumerState { 'paymentSettled': 'PAYMENT_SETTLED', 'completed': 'COMPLETED', }; - final apiStatus = statusApiMap[order.status.name] ?? currentStatusStr; + final apiStatus = statusApiMap[order.status.name] ?? order.status.name.toUpperCase(); final currentIdx = _sagaSteps.indexWhere((s) => s['status'] == apiStatus); final isFailed = order.status == TransferOrderStatus.failed || order.status == TransferOrderStatus.rollingBack || @@ -266,16 +371,27 @@ class _TransferDetailPageState extends ConsumerState { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: const Color(0xFF16213E), + color: const Color(0x99FFFFFF), borderRadius: BorderRadius.circular(12), - border: Border.all(color: Colors.white.withValues(alpha: 0.08)), + boxShadow: const [ + BoxShadow( + color: Color(0x1A000000), + blurRadius: 6, + offset: Offset(0, 4), + ), + BoxShadow( + color: Color(0x1A000000), + blurRadius: 4, + offset: Offset(0, 2), + ), + ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Saga 进度', - style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF5D4037)), ), const SizedBox(height: 16), ...List.generate(_sagaSteps.length, (idx) { @@ -284,8 +400,8 @@ class _TransferDetailPageState extends ConsumerState { final isCurrent = idx == currentIdx && !order.isTerminal && !isFailed; final isFailedStep = isFailed && idx == currentIdx; - Color dotColor = Colors.white.withValues(alpha: 0.2); - Color dotBorder = Colors.white.withValues(alpha: 0.2); + Color dotColor = const Color(0xFFEAE0C8); + Color dotBorder = const Color(0xFFEAE0C8); if (isCompleted) { dotColor = const Color(0xFF52C41A); dotBorder = const Color(0xFF52C41A); @@ -330,7 +446,7 @@ class _TransferDetailPageState extends ConsumerState { height: 20, color: isCompleted ? const Color(0xFF52C41A).withValues(alpha: 0.4) - : Colors.white.withValues(alpha: 0.1), + : const Color(0xFFEAE0C8), ), ], ), @@ -345,14 +461,14 @@ class _TransferDetailPageState extends ConsumerState { fontSize: 14, fontWeight: (isCurrent || isCompleted) ? FontWeight.w600 : FontWeight.w400, color: (isCurrent || isCompleted) - ? Colors.white - : Colors.white.withValues(alpha: 0.4), + ? const Color(0xFF5D4037) + : const Color(0xFF745D43), ), ), if (logTime != null) Text( _formatDateTime(logTime), - style: TextStyle(fontSize: 11, color: Colors.white.withValues(alpha: 0.3)), + style: const TextStyle(fontSize: 11, color: Color(0xFF745D43)), ), ], ), @@ -370,16 +486,27 @@ class _TransferDetailPageState extends ConsumerState { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: const Color(0xFF16213E), + color: const Color(0x99FFFFFF), borderRadius: BorderRadius.circular(12), - border: Border.all(color: Colors.white.withValues(alpha: 0.08)), + boxShadow: const [ + BoxShadow( + color: Color(0x1A000000), + blurRadius: 6, + offset: Offset(0, 4), + ), + BoxShadow( + color: Color(0x1A000000), + blurRadius: 4, + offset: Offset(0, 2), + ), + ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '状态变更日志', - style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF5D4037)), ), const SizedBox(height: 12), ...order.statusLogs.map((log) => Padding( @@ -390,13 +517,13 @@ class _TransferDetailPageState extends ConsumerState { flex: 2, child: Text( '${log.fromStatus} → ${log.toStatus}', - style: const TextStyle(fontSize: 12, color: Colors.white70, fontFamily: 'Inter'), + style: const TextStyle(fontSize: 12, color: Color(0xFF745D43), fontFamily: 'Inter'), ), ), Expanded( child: Text( _formatDateTime(log.createdAt), - style: TextStyle(fontSize: 11, color: Colors.white.withValues(alpha: 0.4)), + style: const TextStyle(fontSize: 11, color: Color(0xFF745D43)), textAlign: TextAlign.right, ), ), @@ -416,30 +543,64 @@ class _TransferDetailPageState extends ConsumerState { // 卖方确认按钮 if (order.isPendingSellerConfirm) Expanded( - child: ElevatedButton( - onPressed: _isActionLoading ? null : _handleSellerConfirm, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFD4AF37), - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(vertical: 14), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: GestureDetector( + onTap: _isActionLoading ? null : _handleSellerConfirm, + child: Container( + height: 56, + decoration: BoxDecoration( + color: _isActionLoading + ? const Color(0xFFD4AF37).withValues(alpha: 0.5) + : const Color(0xFFD4AF37), + borderRadius: BorderRadius.circular(12), + boxShadow: !_isActionLoading + ? const [ + BoxShadow( + color: Color(0x1A000000), + blurRadius: 15, + offset: Offset(0, 10), + ), + ] + : null, + ), + child: Center( + child: Text( + _isActionLoading ? '处理中...' : '确认转让', + style: const TextStyle( + fontSize: 16, + fontFamily: 'Inter', + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ), ), - child: Text(_isActionLoading ? '处理中...' : '确认转让'), ), ), if (order.isPendingSellerConfirm) const SizedBox(width: 12), // 取消按钮 if (order.isCancellable) Expanded( - child: OutlinedButton( - onPressed: _isActionLoading ? null : _handleCancel, - style: OutlinedButton.styleFrom( - foregroundColor: Colors.red, - side: const BorderSide(color: Colors.red), - padding: const EdgeInsets.symmetric(vertical: 14), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: GestureDetector( + onTap: _isActionLoading ? null : _handleCancel, + child: Container( + height: 56, + decoration: BoxDecoration( + color: const Color(0x99FFFFFF), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: const Color(0xFFE65100)), + ), + child: Center( + child: const Text( + '取消转让', + style: TextStyle( + fontSize: 16, + fontFamily: 'Inter', + fontWeight: FontWeight.w500, + color: Color(0xFFE65100), + ), + ), + ), ), - child: const Text('取消转让'), ), ), ], diff --git a/frontend/mobile-app/lib/features/transfer/presentation/pages/transfer_initiate_page.dart b/frontend/mobile-app/lib/features/transfer/presentation/pages/transfer_initiate_page.dart index 0a59c17c..c2d2230e 100644 --- a/frontend/mobile-app/lib/features/transfer/presentation/pages/transfer_initiate_page.dart +++ b/frontend/mobile-app/lib/features/transfer/presentation/pages/transfer_initiate_page.dart @@ -69,7 +69,12 @@ class _TransferInitiatePageState extends ConsumerState { final confirmed = await showDialog( context: context, builder: (ctx) => AlertDialog( - title: const Text('确认发起转让'), + backgroundColor: const Color(0xFFFFF7E6), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + title: const Text( + '确认发起转让', + style: TextStyle(color: Color(0xFF5D4037), fontWeight: FontWeight.w700), + ), content: Text( '买方账号: $buyer\n' '转让棵数: $_treeCount 棵\n' @@ -78,12 +83,20 @@ class _TransferInitiatePageState extends ConsumerState { '手续费: ${_feeAmount.toStringAsFixed(2)} USDT (${(_feeRate * 100).toStringAsFixed(0)}%)\n' '您将收到: ${_sellerReceive.toStringAsFixed(2)} USDT\n\n' '转让发起后需要卖方确认,确认后 Saga 流程将自动执行。', + style: const TextStyle(color: Color(0xFF5D4037), fontSize: 14, height: 1.6), ), actions: [ - TextButton(onPressed: () => Navigator.pop(ctx, false), child: const Text('取消')), + TextButton( + onPressed: () => Navigator.pop(ctx, false), + child: const Text('取消', style: TextStyle(color: Color(0xFF745D43))), + ), ElevatedButton( onPressed: () => Navigator.pop(ctx, true), - style: ElevatedButton.styleFrom(backgroundColor: const Color(0xFFD4AF37)), + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFFD4AF37), + foregroundColor: Colors.white, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + ), child: const Text('确认发起'), ), ], @@ -128,140 +141,242 @@ class _TransferInitiatePageState extends ConsumerState { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: const Color(0xFF1A1A2E), - appBar: AppBar( - backgroundColor: const Color(0xFF1A1A2E), - foregroundColor: Colors.white, - title: const Text( - '发起转让', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Colors.white), + body: Container( + width: double.infinity, + height: double.infinity, + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xFFFFF7E6), + Color(0xFFEAE0C8), + ], + ), + ), + child: SafeArea( + child: Column( + children: [ + _buildHeader(), + Expanded( + child: SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 说明卡片 + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: const Color(0xFFD4AF37).withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: const Color(0xFFD4AF37).withValues(alpha: 0.3)), + ), + child: const Row( + children: [ + Icon(Icons.info_outline, color: Color(0xFFD4AF37), size: 20), + SizedBox(width: 8), + Expanded( + child: Text( + '转让已认种的果树所有权。发起后需卖方确认,系统将自动完成资金冻结、所有权变更、算力调整等流程。', + style: TextStyle(fontSize: 13, color: Color(0xFF8B5A2B)), + ), + ), + ], + ), + ), + const SizedBox(height: 24), + + // 买方账号 + _buildLabel('买方账号'), + const SizedBox(height: 8), + _buildTextField( + controller: _buyerController, + hintText: '输入买方账号(如 D25121400002)', + keyboardType: TextInputType.text, + ), + const SizedBox(height: 20), + + // 转让棵数 + _buildLabel('转让棵数'), + const SizedBox(height: 8), + _buildTextField( + controller: _treeCountController, + hintText: '输入棵数', + keyboardType: TextInputType.number, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + onChanged: (_) => setState(() {}), + ), + const SizedBox(height: 20), + + // 每棵售价 + _buildLabel('每棵售价 (USDT)'), + const SizedBox(height: 8), + _buildTextField( + controller: _priceController, + hintText: '输入每棵售价', + keyboardType: const TextInputType.numberWithOptions(decimal: true), + inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[\d.]'))], + onChanged: (_) => setState(() {}), + ), + const SizedBox(height: 24), + + // 费用计算卡片 + if (_pricePerTree > 0) ...[ + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: const Color(0x99FFFFFF), + borderRadius: BorderRadius.circular(12), + boxShadow: const [ + BoxShadow( + color: Color(0x1A000000), + blurRadius: 6, + offset: Offset(0, 4), + ), + ], + ), + child: Column( + children: [ + _feeRow('总价', '${_totalPrice.toStringAsFixed(0)} USDT'), + _feeRow('手续费 (${(_feeRate * 100).toStringAsFixed(0)}%)', '${_feeAmount.toStringAsFixed(2)} USDT'), + const Divider(color: Color(0xFFEAE0C8)), + _feeRow('您将收到', '${_sellerReceive.toStringAsFixed(2)} USDT', + valueColor: const Color(0xFFD4AF37)), + ], + ), + ), + const SizedBox(height: 24), + ], + + // 错误信息 + if (_errorMessage != null) ...[ + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: const Color(0xFFFFF3E0), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: const Color(0xFFE65100)), + ), + child: Row( + children: [ + const Icon(Icons.error_outline, color: Color(0xFFE65100), size: 16), + const SizedBox(width: 8), + Expanded( + child: Text(_errorMessage!, style: const TextStyle(color: Color(0xFFE65100), fontSize: 13)), + ), + ], + ), + ), + const SizedBox(height: 16), + ], + + // 提交按钮 + GestureDetector( + onTap: _isSubmitting ? null : _handleSubmit, + child: Container( + width: double.infinity, + height: 56, + decoration: BoxDecoration( + color: _isSubmitting + ? const Color(0xFFD4AF37).withValues(alpha: 0.5) + : const Color(0xFFD4AF37), + borderRadius: BorderRadius.circular(12), + boxShadow: !_isSubmitting + ? const [ + BoxShadow( + color: Color(0x1A000000), + blurRadius: 15, + offset: Offset(0, 10), + ), + BoxShadow( + color: Color(0x1A000000), + blurRadius: 6, + offset: Offset(0, 4), + ), + ] + : null, + ), + child: Center( + child: _isSubmitting + ? const SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.white, + ), + ) + : const Text( + '发起转让', + style: TextStyle( + fontSize: 18, + fontFamily: 'Inter', + fontWeight: FontWeight.w500, + height: 1.56, + color: Colors.white, + ), + ), + ), + ), + ), + ], + ), + ), + ), + ], + ), ), ), - body: SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // 说明卡片 - Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: const Color(0xFFD4AF37).withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(12), - border: Border.all(color: const Color(0xFFD4AF37).withValues(alpha: 0.3)), - ), - child: const Row( - children: [ - Icon(Icons.info_outline, color: Color(0xFFD4AF37), size: 20), - SizedBox(width: 8), - Expanded( - child: Text( - '转让已认种的果树所有权。发起后需卖方确认,系统将自动完成资金冻结、所有权变更、算力调整等流程。', - style: TextStyle(fontSize: 13, color: Color(0xFFD4AF37)), - ), - ), - ], + ); + } + + Widget _buildHeader() { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Row( + children: [ + GestureDetector( + onTap: () => context.pop(), + child: Container( + width: 32, + height: 32, + alignment: Alignment.center, + child: const Icon( + Icons.arrow_back_ios, + color: Color(0xFFD4AF37), + size: 20, ), ), - const SizedBox(height: 24), - - // 买方账号 - _buildLabel('买方账号'), - const SizedBox(height: 8), - _buildTextField( - controller: _buyerController, - hintText: '输入买方账号(如 D25121400002)', - keyboardType: TextInputType.text, - ), - const SizedBox(height: 20), - - // 转让棵数 - _buildLabel('转让棵数'), - const SizedBox(height: 8), - _buildTextField( - controller: _treeCountController, - hintText: '输入棵数', - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - onChanged: (_) => setState(() {}), - ), - const SizedBox(height: 20), - - // 每棵售价 - _buildLabel('每棵售价 (USDT)'), - const SizedBox(height: 8), - _buildTextField( - controller: _priceController, - hintText: '输入每棵售价', - keyboardType: const TextInputType.numberWithOptions(decimal: true), - inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[\d.]'))], - onChanged: (_) => setState(() {}), - ), - const SizedBox(height: 24), - - // 费用计算卡片 - if (_pricePerTree > 0) ...[ - Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: const Color(0xFF16213E), - borderRadius: BorderRadius.circular(12), - border: Border.all(color: Colors.white.withValues(alpha: 0.08)), - ), - child: Column( - children: [ - _feeRow('总价', '${_totalPrice.toStringAsFixed(0)} USDT'), - _feeRow('手续费 (${(_feeRate * 100).toStringAsFixed(0)}%)', '${_feeAmount.toStringAsFixed(2)} USDT'), - const Divider(color: Colors.white24), - _feeRow('您将收到', '${_sellerReceive.toStringAsFixed(2)} USDT', - valueColor: const Color(0xFFD4AF37)), - ], - ), - ), - const SizedBox(height: 24), - ], - - // 错误信息 - if (_errorMessage != null) ...[ - Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: Colors.red.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(8), - ), - child: Row( - children: [ - const Icon(Icons.error_outline, color: Colors.red, size: 16), - const SizedBox(width: 8), - Expanded( - child: Text(_errorMessage!, style: const TextStyle(color: Colors.red, fontSize: 13)), - ), - ], - ), - ), - const SizedBox(height: 16), - ], - - // 提交按钮 - SizedBox( - width: double.infinity, - child: ElevatedButton( - onPressed: _isSubmitting ? null : _handleSubmit, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFD4AF37), - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(vertical: 16), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), - disabledBackgroundColor: const Color(0xFFD4AF37).withValues(alpha: 0.4), - ), - child: Text( - _isSubmitting ? '提交中...' : '发起转让', - style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600), - ), + ), + const SizedBox(width: 4), + GestureDetector( + onTap: () => context.pop(), + child: const Text( + '返回', + style: TextStyle( + fontSize: 16, + fontFamily: 'Inter', + height: 1.5, + color: Color(0xFFD4AF37), ), ), - ], - ), + ), + const SizedBox(width: 42), + const Expanded( + child: Text( + '发起转让', + style: TextStyle( + fontSize: 18, + fontFamily: 'Inter', + fontWeight: FontWeight.w700, + height: 1.25, + letterSpacing: -0.27, + color: Color(0xFF5D4037), + ), + ), + ), + ], ), ); } @@ -270,9 +385,11 @@ class _TransferInitiatePageState extends ConsumerState { return Text( text, style: const TextStyle( - fontSize: 14, + fontSize: 16, + fontFamily: 'Inter', fontWeight: FontWeight.w500, - color: Colors.white, + height: 1.5, + color: Color(0xFF5D4037), ), ); } @@ -289,23 +406,23 @@ class _TransferInitiatePageState extends ConsumerState { keyboardType: keyboardType, inputFormatters: inputFormatters, onChanged: onChanged, - style: const TextStyle(color: Colors.white, fontSize: 15, fontFamily: 'Inter'), + style: const TextStyle(color: Color(0xFF5D4037), fontSize: 15, fontFamily: 'Inter'), decoration: InputDecoration( hintText: hintText, - hintStyle: TextStyle(color: Colors.white.withValues(alpha: 0.3)), + hintStyle: const TextStyle(color: Color(0xFF745D43)), filled: true, - fillColor: const Color(0xFF16213E), + fillColor: const Color(0x99FFFFFF), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), - borderSide: BorderSide(color: Colors.white.withValues(alpha: 0.1)), + borderSide: const BorderSide(color: Color(0xFFEAE0C8)), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), - borderSide: BorderSide(color: Colors.white.withValues(alpha: 0.1)), + borderSide: const BorderSide(color: Color(0xFFEAE0C8)), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), - borderSide: const BorderSide(color: Color(0xFFD4AF37)), + borderSide: const BorderSide(color: Color(0xFFD4AF37), width: 2), ), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), ), @@ -318,13 +435,13 @@ class _TransferInitiatePageState extends ConsumerState { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(label, style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: 0.5))), + Text(label, style: const TextStyle(fontSize: 13, color: Color(0xFF745D43))), Text( value, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: valueColor ?? Colors.white, + color: valueColor ?? const Color(0xFF5D4037), fontFamily: 'Inter', ), ), diff --git a/frontend/mobile-app/lib/features/transfer/presentation/pages/transfer_list_page.dart b/frontend/mobile-app/lib/features/transfer/presentation/pages/transfer_list_page.dart index b621e603..050a6a10 100644 --- a/frontend/mobile-app/lib/features/transfer/presentation/pages/transfer_list_page.dart +++ b/frontend/mobile-app/lib/features/transfer/presentation/pages/transfer_list_page.dart @@ -84,31 +84,112 @@ class _TransferListPageState extends ConsumerState @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: const Color(0xFF1A1A2E), - appBar: AppBar( - backgroundColor: const Color(0xFF1A1A2E), - foregroundColor: Colors.white, - title: const Text( - '转让记录', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - color: Colors.white, + body: Container( + width: double.infinity, + height: double.infinity, + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xFFFFF7E6), + Color(0xFFEAE0C8), + ], ), ), - bottom: TabBar( - controller: _tabController, - indicatorColor: const Color(0xFFD4AF37), - labelColor: const Color(0xFFD4AF37), - unselectedLabelColor: Colors.white54, - tabs: const [ - Tab(text: '全部'), - Tab(text: '转出'), - Tab(text: '转入'), - ], + child: SafeArea( + child: Column( + children: [ + _buildHeader(), + _buildTabBar(), + Expanded(child: _buildBody()), + ], + ), ), ), - body: _buildBody(), + ); + } + + Widget _buildHeader() { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Row( + children: [ + GestureDetector( + onTap: () => context.pop(), + child: Container( + width: 32, + height: 32, + alignment: Alignment.center, + child: const Icon( + Icons.arrow_back_ios, + color: Color(0xFFD4AF37), + size: 20, + ), + ), + ), + const SizedBox(width: 4), + GestureDetector( + onTap: () => context.pop(), + child: const Text( + '返回', + style: TextStyle( + fontSize: 16, + fontFamily: 'Inter', + height: 1.5, + color: Color(0xFFD4AF37), + ), + ), + ), + const SizedBox(width: 42), + const Expanded( + child: Text( + '转让记录', + style: TextStyle( + fontSize: 18, + fontFamily: 'Inter', + fontWeight: FontWeight.w700, + height: 1.25, + letterSpacing: -0.27, + color: Color(0xFF5D4037), + ), + ), + ), + ], + ), + ); + } + + Widget _buildTabBar() { + return Container( + margin: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration( + color: const Color(0x99FFFFFF), + borderRadius: BorderRadius.circular(12), + ), + child: TabBar( + controller: _tabController, + indicatorColor: const Color(0xFFD4AF37), + indicatorSize: TabBarIndicatorSize.label, + labelColor: const Color(0xFFD4AF37), + unselectedLabelColor: const Color(0xFF745D43), + labelStyle: const TextStyle( + fontSize: 14, + fontFamily: 'Inter', + fontWeight: FontWeight.w600, + ), + unselectedLabelStyle: const TextStyle( + fontSize: 14, + fontFamily: 'Inter', + fontWeight: FontWeight.w400, + ), + dividerColor: Colors.transparent, + tabs: const [ + Tab(text: '全部'), + Tab(text: '转出'), + Tab(text: '转入'), + ], + ), ); } @@ -124,14 +205,23 @@ class _TransferListPageState extends ConsumerState child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ + const Icon(Icons.error_outline, color: Color(0xFFE65100), size: 32), + const SizedBox(height: 8), Text( '加载失败', - style: TextStyle(color: Colors.white.withValues(alpha: 0.7), fontSize: 16), + style: const TextStyle(color: Color(0xFF5D4037), fontSize: 16), ), const SizedBox(height: 8), - TextButton( - onPressed: _loadData, - child: const Text('重试', style: TextStyle(color: Color(0xFFD4AF37))), + GestureDetector( + onTap: _loadData, + child: const Text( + '点击重试', + style: TextStyle( + color: Color(0xFFD4AF37), + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), ), ], ), @@ -142,7 +232,7 @@ class _TransferListPageState extends ConsumerState return Center( child: Text( '暂无转让记录', - style: TextStyle(color: Colors.white.withValues(alpha: 0.5), fontSize: 16), + style: const TextStyle(color: Color(0xFF745D43), fontSize: 16), ), ); } @@ -169,12 +259,20 @@ class _TransferListPageState extends ConsumerState margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: const Color(0xFF16213E), + color: const Color(0x99FFFFFF), borderRadius: BorderRadius.circular(12), - border: Border.all( - color: Colors.white.withValues(alpha: 0.08), - width: 1, - ), + boxShadow: const [ + BoxShadow( + color: Color(0x1A000000), + blurRadius: 6, + offset: Offset(0, 4), + ), + BoxShadow( + color: Color(0x1A000000), + blurRadius: 4, + offset: Offset(0, 2), + ), + ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -189,7 +287,7 @@ class _TransferListPageState extends ConsumerState style: const TextStyle( fontSize: 13, fontFamily: 'Inter', - color: Colors.white70, + color: Color(0xFF745D43), ), overflow: TextOverflow.ellipsis, ), @@ -217,7 +315,7 @@ class _TransferListPageState extends ConsumerState children: [ Text( order.sellerAccountSequence, - style: const TextStyle(fontSize: 14, color: Colors.white, fontFamily: 'Inter'), + style: const TextStyle(fontSize: 14, color: Color(0xFF5D4037), fontFamily: 'Inter'), ), const Padding( padding: EdgeInsets.symmetric(horizontal: 8), @@ -225,7 +323,7 @@ class _TransferListPageState extends ConsumerState ), Text( order.buyerAccountSequence, - style: const TextStyle(fontSize: 14, color: Colors.white, fontFamily: 'Inter'), + style: const TextStyle(fontSize: 14, color: Color(0xFF5D4037), fontFamily: 'Inter'), ), ], ), @@ -236,7 +334,7 @@ class _TransferListPageState extends ConsumerState children: [ Text( '${order.treeCount} 棵', - style: const TextStyle(fontSize: 14, color: Colors.white70), + style: const TextStyle(fontSize: 14, color: Color(0xFF745D43)), ), Text( '${order.totalPrice.toStringAsFixed(0)} USDT', @@ -253,7 +351,7 @@ class _TransferListPageState extends ConsumerState // 时间 Text( _formatDateTime(order.createdAt), - style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: 0.4)), + style: const TextStyle(fontSize: 12, color: Color(0xFF745D43)), ), ], ),