diff --git a/frontend/genex-mobile/lib/features/coupons/presentation/pages/home_page.dart b/frontend/genex-mobile/lib/features/coupons/presentation/pages/home_page.dart index c89546d..2a48e27 100644 --- a/frontend/genex-mobile/lib/features/coupons/presentation/pages/home_page.dart +++ b/frontend/genex-mobile/lib/features/coupons/presentation/pages/home_page.dart @@ -6,19 +6,12 @@ import '../../../../shared/widgets/coupon_card.dart'; import '../../../ai_agent/presentation/widgets/ai_fab.dart'; import '../widgets/receive_coupon_sheet.dart'; -/// 首页 - 券钱包 + 分类网格 + AI推荐 + 精选券 +/// 首页 - 轻量钱包卡 + 分类网格 + AI推荐 + 精选券 /// /// Tab导航:首页/交易/消息/我的 -class HomePage extends StatefulWidget { +class HomePage extends StatelessWidget { const HomePage({super.key}); - @override - State createState() => _HomePageState(); -} - -class _HomePageState extends State { - int _walletFilter = 0; // 0=全部, 1=可使用, 2=待核销, 3=已过期 - @override Widget build(BuildContext context) { return Scaffold( @@ -43,10 +36,10 @@ class _HomePageState extends State { ], ), - // Coupon Wallet (replaces Banner) - SliverToBoxAdapter(child: _buildCouponWallet(context)), + // Lightweight Wallet Card (replaces heavy wallet) + SliverToBoxAdapter(child: _buildWalletCard(context)), - // Category Grid (6 new categories) + // Category Grid (8 items, 4x2) SliverToBoxAdapter(child: _buildCategoryGrid()), // AI Smart Suggestions @@ -143,277 +136,79 @@ class _HomePageState extends State { } // ============================================================ - // Coupon Wallet Section (replaces Banner) + // Lightweight Wallet Card + // 点击非快捷入口区域打开完整钱包页面 // ============================================================ - Widget _buildCouponWallet(BuildContext context) { - return Container( - margin: const EdgeInsets.fromLTRB(20, 8, 20, 0), - decoration: BoxDecoration( - gradient: AppColors.cardGradient, - borderRadius: AppSpacing.borderRadiusLg, - boxShadow: AppSpacing.shadowPrimary, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Header: 我的钱包 + 接收 button - Padding( - padding: const EdgeInsets.fromLTRB(16, 16, 16, 0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - const Icon(Icons.account_balance_wallet_rounded, - size: 20, color: Colors.white), - const SizedBox(width: 8), - Text('我的钱包', - style: AppTypography.h3.copyWith(color: Colors.white)), - ], - ), - GestureDetector( - onTap: () => _showReceiveSheet(context), - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), - decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.2), - borderRadius: AppSpacing.borderRadiusFull, - border: Border.all( - color: Colors.white.withValues(alpha: 0.3), - width: 0.5, - ), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.qr_code_rounded, - size: 14, color: Colors.white), - const SizedBox(width: 4), - Text('接收', - style: AppTypography.labelSmall.copyWith( - color: Colors.white, - fontWeight: FontWeight.w500, - )), - ], - ), - ), - ), - ], - ), - ), - - // Stats row - Padding( - padding: const EdgeInsets.fromLTRB(16, 14, 16, 0), - child: Row( - children: [ - _buildWalletStat('可使用', '3', true), - const SizedBox(width: 20), - _buildWalletStat('待核销', '1', false), - const SizedBox(width: 20), - _buildWalletStat('已过期', '0', false), - ], - ), - ), - - // Filter tabs - Padding( - padding: const EdgeInsets.fromLTRB(16, 12, 16, 0), - child: Row( - children: [ - _buildWalletTab('全部', 0), - const SizedBox(width: 6), - _buildWalletTab('可使用', 1), - const SizedBox(width: 6), - _buildWalletTab('待核销', 2), - const SizedBox(width: 6), - _buildWalletTab('已过期', 3), - ], - ), - ), - - // Coupon mini-cards (horizontal scroll) - SizedBox( - height: 88, - child: _filteredWalletCoupons.isEmpty - ? Center( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - '暂无券,去交易市场看看吧', - style: AppTypography.bodySmall.copyWith( - color: Colors.white.withValues(alpha: 0.6), - ), - ), - ), - ) - : ListView.separated( - scrollDirection: Axis.horizontal, - padding: const EdgeInsets.fromLTRB(16, 10, 16, 14), - itemCount: _filteredWalletCoupons.length, - separatorBuilder: (_, __) => const SizedBox(width: 10), - itemBuilder: (context, index) { - final coupon = _filteredWalletCoupons[index]; - return _buildWalletCouponCard(context, coupon); - }, - ), - ), - - // Quick actions bar - Container( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), - decoration: BoxDecoration( - color: Colors.black.withValues(alpha: 0.1), - borderRadius: const BorderRadius.only( - bottomLeft: Radius.circular(16), - bottomRight: Radius.circular(16), - ), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - _buildWalletAction(Icons.qr_code_rounded, '接收', () { - _showReceiveSheet(context); - }), - _buildWalletAction(Icons.card_giftcard_rounded, '转赠', () { - Navigator.pushNamed(context, '/transfer'); - }), - _buildWalletAction(Icons.sell_rounded, '出售', () { - Navigator.pushNamed(context, '/sell'); - }), - _buildWalletAction(Icons.check_circle_outline_rounded, '核销', () { - Navigator.pushNamed(context, '/redeem'); - }), - ], - ), - ), - ], - ), - ); - } - - Widget _buildWalletStat(String label, String count, bool highlight) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - count, - style: AppTypography.h2.copyWith( - color: highlight ? Colors.white : Colors.white.withValues(alpha: 0.7), - fontWeight: FontWeight.w700, - ), - ), - const SizedBox(height: 2), - Text( - label, - style: AppTypography.caption.copyWith( - color: Colors.white.withValues(alpha: 0.6), - ), - ), - ], - ); - } - - Widget _buildWalletTab(String label, int index) { - final isSelected = _walletFilter == index; + Widget _buildWalletCard(BuildContext context) { return GestureDetector( - onTap: () => setState(() => _walletFilter = index), + onTap: () => Navigator.pushNamed(context, '/wallet/coupons'), child: Container( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + margin: const EdgeInsets.fromLTRB(20, 8, 20, 0), + padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: isSelected - ? Colors.white.withValues(alpha: 0.25) - : Colors.transparent, - borderRadius: AppSpacing.borderRadiusFull, - border: Border.all( - color: isSelected - ? Colors.white.withValues(alpha: 0.4) - : Colors.transparent, - width: 0.5, - ), - ), - child: Text( - label, - style: AppTypography.caption.copyWith( - color: isSelected - ? Colors.white - : Colors.white.withValues(alpha: 0.5), - fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400, - ), - ), - ), - ); - } - - Widget _buildWalletCouponCard(BuildContext context, _WalletCoupon coupon) { - return GestureDetector( - onTap: () => Navigator.pushNamed(context, '/coupon/mine/detail'), - child: Container( - width: 140, - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.15), - borderRadius: AppSpacing.borderRadiusMd, - border: Border.all( - color: Colors.white.withValues(alpha: 0.2), - width: 0.5, - ), + gradient: AppColors.cardGradient, + borderRadius: AppSpacing.borderRadiusLg, + boxShadow: AppSpacing.shadowPrimary, ), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + // Top row: wallet info + receive button Row( children: [ - Icon(Icons.confirmation_number_outlined, - size: 14, color: Colors.white.withValues(alpha: 0.7)), - const SizedBox(width: 4), - Expanded( - child: Text( - coupon.brandName, - style: AppTypography.caption.copyWith( + const Icon(Icons.account_balance_wallet_rounded, + size: 20, color: Colors.white), + const SizedBox(width: 8), + Text('我的钱包', + style: AppTypography.labelMedium.copyWith(color: Colors.white)), + const Spacer(), + // Summary + Text('持有 ', + style: AppTypography.bodySmall.copyWith( color: Colors.white.withValues(alpha: 0.7), - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), + )), + Text('4', + style: AppTypography.h3.copyWith( + color: Colors.white, + fontWeight: FontWeight.w700, + )), + Text(' 张券 总值 ', + style: AppTypography.bodySmall.copyWith( + color: Colors.white.withValues(alpha: 0.7), + )), + Text('\$235', + style: AppTypography.h3.copyWith( + color: Colors.white, + fontWeight: FontWeight.w700, + )), + const SizedBox(width: 4), + Icon(Icons.chevron_right_rounded, + size: 18, color: Colors.white.withValues(alpha: 0.7)), ], ), - Text( - coupon.name, - style: AppTypography.labelSmall.copyWith( - color: Colors.white, - fontWeight: FontWeight.w600, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), + + const SizedBox(height: 14), + + // Quick action entries Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - Text( - '\$${coupon.faceValue.toStringAsFixed(0)}', - style: AppTypography.priceSmall.copyWith( - color: Colors.white, - fontSize: 13, - ), - ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 1), - decoration: BoxDecoration( - color: coupon.statusColor.withValues(alpha: 0.3), - borderRadius: AppSpacing.borderRadiusFull, - ), - child: Text( - coupon.statusLabel, - style: AppTypography.caption.copyWith( - color: Colors.white, - fontSize: 9, - fontWeight: FontWeight.w500, - ), - ), - ), + _buildQuickAction(context, Icons.qr_code_rounded, '接收', () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (_) => const ReceiveCouponSheet(), + ); + }), + _buildQuickAction(context, Icons.card_giftcard_rounded, '转赠', () { + Navigator.pushNamed(context, '/transfer'); + }), + _buildQuickAction(context, Icons.sell_rounded, '出售', () { + Navigator.pushNamed(context, '/sell'); + }), + _buildQuickAction(context, Icons.check_circle_outline_rounded, '核销', () { + Navigator.pushNamed(context, '/redeem'); + }), ], ), ], @@ -422,18 +217,28 @@ class _HomePageState extends State { ); } - Widget _buildWalletAction(IconData icon, String label, VoidCallback onTap) { + Widget _buildQuickAction( + BuildContext context, IconData icon, String label, VoidCallback onTap) { return GestureDetector( onTap: onTap, + behavior: HitTestBehavior.opaque, child: Column( mainAxisSize: MainAxisSize.min, children: [ - Icon(icon, size: 20, color: Colors.white.withValues(alpha: 0.9)), - const SizedBox(height: 4), + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.15), + borderRadius: AppSpacing.borderRadiusMd, + ), + child: Icon(icon, size: 20, color: Colors.white), + ), + const SizedBox(height: 6), Text( label, style: AppTypography.caption.copyWith( - color: Colors.white.withValues(alpha: 0.8), + color: Colors.white.withValues(alpha: 0.9), fontWeight: FontWeight.w500, ), ), @@ -442,43 +247,19 @@ class _HomePageState extends State { ); } - void _showReceiveSheet(BuildContext context) { - showModalBottomSheet( - context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, - builder: (_) => const ReceiveCouponSheet(), - ); - } - - List<_WalletCoupon> get _filteredWalletCoupons { - if (_walletFilter == 0) return _mockWalletCoupons; - if (_walletFilter == 1) { - return _mockWalletCoupons - .where((c) => c.status == CouponStatus.active) - .toList(); - } - if (_walletFilter == 2) { - return _mockWalletCoupons - .where((c) => c.status == CouponStatus.pending) - .toList(); - } - return _mockWalletCoupons - .where((c) => c.status == CouponStatus.expired) - .toList(); - } - // ============================================================ - // Category Grid (6 new savings-focused categories) + // Category Grid (8 items, 4x2, A+C savings-focused) // ============================================================ Widget _buildCategoryGrid() { final categories = [ ('限时抢购', Icons.flash_on_rounded, AppColors.error), ('新券首发', Icons.fiber_new_rounded, AppColors.primary), ('折扣排行', Icons.trending_up_rounded, AppColors.couponEntertainment), - ('即将到期', Icons.timer_rounded, AppColors.info), + ('即将到期', Icons.timer_rounded, AppColors.warning), ('比价', Icons.compare_arrows_rounded, AppColors.couponShopping), - ('全部分类', Icons.grid_view_rounded, AppColors.textSecondary), + ('转让市场', Icons.swap_horiz_rounded, AppColors.info), + ('热门交易', Icons.local_fire_department_rounded, AppColors.couponFood), + ('全部', Icons.grid_view_rounded, AppColors.textSecondary), ]; return Padding( @@ -487,10 +268,10 @@ class _HomePageState extends State { shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - mainAxisSpacing: 8, - crossAxisSpacing: 8, - childAspectRatio: 1.1, + crossAxisCount: 4, + mainAxisSpacing: 4, + crossAxisSpacing: 4, + childAspectRatio: 0.9, ), itemCount: categories.length, itemBuilder: (context, index) { @@ -501,13 +282,13 @@ class _HomePageState extends State { mainAxisSize: MainAxisSize.min, children: [ Container( - width: 48, - height: 48, + width: 44, + height: 44, decoration: BoxDecoration( color: color.withValues(alpha: 0.1), borderRadius: AppSpacing.borderRadiusMd, ), - child: Icon(icon, color: color, size: 24), + child: Icon(icon, color: color, size: 22), ), const SizedBox(height: 6), Text(name, style: AppTypography.caption.copyWith( @@ -567,83 +348,7 @@ class _HomePageState extends State { } } -// ============================================================ -// Wallet Coupon Model -// ============================================================ -class _WalletCoupon { - final String brandName; - final String name; - final double faceValue; - final CouponStatus status; - final DateTime expiryDate; - - const _WalletCoupon({ - required this.brandName, - required this.name, - required this.faceValue, - required this.status, - required this.expiryDate, - }); - - String get statusLabel { - switch (status) { - case CouponStatus.active: - return '可使用'; - case CouponStatus.pending: - return '待核销'; - case CouponStatus.expired: - return '已过期'; - case CouponStatus.used: - return '已使用'; - } - } - - Color get statusColor { - switch (status) { - case CouponStatus.active: - return AppColors.success; - case CouponStatus.pending: - return AppColors.warning; - case CouponStatus.expired: - return AppColors.textTertiary; - case CouponStatus.used: - return AppColors.textDisabled; - } - } -} - // Mock data -final _mockWalletCoupons = [ - _WalletCoupon( - brandName: 'Starbucks', - name: '星巴克 \$25 礼品卡', - faceValue: 25.0, - status: CouponStatus.active, - expiryDate: DateTime.now().add(const Duration(days: 30)), - ), - _WalletCoupon( - brandName: 'Amazon', - name: 'Amazon \$100 购物券', - faceValue: 100.0, - status: CouponStatus.active, - expiryDate: DateTime.now().add(const Duration(days: 45)), - ), - _WalletCoupon( - brandName: 'Nike', - name: 'Nike \$80 运动券', - faceValue: 80.0, - status: CouponStatus.pending, - expiryDate: DateTime.now().add(const Duration(days: 15)), - ), - _WalletCoupon( - brandName: 'Target', - name: 'Target \$30 折扣券', - faceValue: 30.0, - status: CouponStatus.active, - expiryDate: DateTime.now().add(const Duration(days: 60)), - ), -]; - const _mockBrands = ['Starbucks', 'Amazon', 'Walmart', 'Target', 'Nike']; const _mockNames = ['星巴克 \$25 礼品卡', 'Amazon \$100 购物券', 'Walmart \$50 生活券', 'Target \$30 折扣券', 'Nike \$80 运动券']; const _mockFaceValues = [25.0, 100.0, 50.0, 30.0, 80.0]; diff --git a/frontend/genex-mobile/lib/features/coupons/presentation/pages/wallet_coupons_page.dart b/frontend/genex-mobile/lib/features/coupons/presentation/pages/wallet_coupons_page.dart new file mode 100644 index 0000000..205c927 --- /dev/null +++ b/frontend/genex-mobile/lib/features/coupons/presentation/pages/wallet_coupons_page.dart @@ -0,0 +1,393 @@ +import 'package:flutter/material.dart'; +import '../../../../app/theme/app_colors.dart'; +import '../../../../app/theme/app_typography.dart'; +import '../../../../app/theme/app_spacing.dart'; +import '../../../../shared/widgets/status_tag.dart'; +import '../../../../shared/widgets/empty_state.dart'; +import '../widgets/receive_coupon_sheet.dart'; + +/// 完整钱包页面 - 融合"我的券"所有功能 +/// +/// 顶部汇总卡片 + 4-Tab筛选(全部/可使用/待核销/已过期) +/// 券列表(品牌+面值+状态+到期+快捷操作) +/// AppBar含接收按钮 +class WalletCouponsPage extends StatefulWidget { + const WalletCouponsPage({super.key}); + + @override + State createState() => _WalletCouponsPageState(); +} + +class _WalletCouponsPageState extends State + with SingleTickerProviderStateMixin { + late TabController _tabController; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 4, vsync: this); + } + + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('我的钱包'), + actions: [ + // 接收券 + IconButton( + icon: const Icon(Icons.qr_code_rounded, size: 22), + onPressed: () => _showReceiveSheet(context), + tooltip: '接收券', + ), + // 排序 + IconButton( + icon: const Icon(Icons.sort_rounded, size: 22), + onPressed: () {}, + ), + ], + bottom: TabBar( + controller: _tabController, + tabs: const [ + Tab(text: '全部'), + Tab(text: '可使用'), + Tab(text: '待核销'), + Tab(text: '已过期'), + ], + ), + ), + body: Column( + children: [ + // 汇总卡片 + _buildSummaryCard(), + + // 券列表 + Expanded( + child: TabBarView( + controller: _tabController, + children: [ + _buildCouponList(null), + _buildCouponList(CouponStatus.active), + _buildCouponList(CouponStatus.pending), + _buildCouponList(CouponStatus.expired), + ], + ), + ), + ], + ), + ); + } + + /// 顶部汇总卡片:持有数量 + 总面值 + 快捷操作 + Widget _buildSummaryCard() { + return Container( + margin: const EdgeInsets.fromLTRB(16, 12, 16, 4), + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + gradient: AppColors.cardGradient, + borderRadius: AppSpacing.borderRadiusMd, + boxShadow: AppSpacing.shadowPrimary, + ), + child: Row( + children: [ + // 持有券数 + Expanded( + child: Column( + children: [ + Text('4', + style: AppTypography.display.copyWith( + color: Colors.white, + fontWeight: FontWeight.w700, + )), + const SizedBox(height: 2), + Text('持有券数', + style: AppTypography.caption.copyWith( + color: Colors.white.withValues(alpha: 0.7), + )), + ], + ), + ), + Container( + width: 1, + height: 36, + color: Colors.white.withValues(alpha: 0.2), + ), + // 总面值 + Expanded( + child: Column( + children: [ + Text('\$235', + style: AppTypography.display.copyWith( + color: Colors.white, + fontWeight: FontWeight.w700, + )), + const SizedBox(height: 2), + Text('总面值', + style: AppTypography.caption.copyWith( + color: Colors.white.withValues(alpha: 0.7), + )), + ], + ), + ), + Container( + width: 1, + height: 36, + color: Colors.white.withValues(alpha: 0.2), + ), + // 节省金额 + Expanded( + child: Column( + children: [ + Text('\$38', + style: AppTypography.display.copyWith( + color: Colors.white, + fontWeight: FontWeight.w700, + )), + const SizedBox(height: 2), + Text('已节省', + style: AppTypography.caption.copyWith( + color: Colors.white.withValues(alpha: 0.7), + )), + ], + ), + ), + ], + ), + ); + } + + Widget _buildCouponList(CouponStatus? filter) { + final coupons = _filterCoupons(filter); + + if (coupons.isEmpty) { + return EmptyState.noCoupons( + onBrowse: () => Navigator.pop(context), + ); + } + + return ListView.separated( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 100), + itemCount: coupons.length, + separatorBuilder: (_, __) => const SizedBox(height: 12), + itemBuilder: (context, index) { + final coupon = coupons[index]; + return _buildCouponCard(context, coupon); + }, + ); + } + + Widget _buildCouponCard(BuildContext context, _WalletCouponItem coupon) { + return GestureDetector( + onTap: () => Navigator.pushNamed(context, '/coupon/mine/detail'), + child: Container( + padding: AppSpacing.cardPadding, + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: AppSpacing.borderRadiusMd, + boxShadow: AppSpacing.shadowSm, + border: Border.all(color: AppColors.borderLight), + ), + child: Column( + children: [ + Row( + children: [ + // 券图片占位 + Container( + width: 56, + height: 56, + decoration: BoxDecoration( + color: AppColors.primarySurface, + borderRadius: AppSpacing.borderRadiusSm, + ), + child: Icon( + Icons.confirmation_number_outlined, + color: AppColors.primary.withValues(alpha: 0.4), + size: 24, + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(coupon.brandName, style: AppTypography.caption), + Text(coupon.name, style: AppTypography.labelMedium), + const SizedBox(height: 4), + Row( + children: [ + Text('面值 \$${coupon.faceValue.toStringAsFixed(0)}', + style: AppTypography.bodySmall), + const SizedBox(width: 8), + _statusWidget(coupon.status), + ], + ), + ], + ), + ), + const Icon(Icons.chevron_right_rounded, + color: AppColors.textTertiary, size: 20), + ], + ), + + // 过期时间 + 快捷操作 + const SizedBox(height: 12), + Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: AppColors.gray50, + borderRadius: AppSpacing.borderRadiusSm, + ), + child: Row( + children: [ + Icon(Icons.access_time_rounded, + size: 14, color: _expiryColor(coupon.expiryDate)), + const SizedBox(width: 4), + Text( + _expiryText(coupon.expiryDate), + style: AppTypography.caption.copyWith( + color: _expiryColor(coupon.expiryDate)), + ), + const Spacer(), + if (coupon.status == CouponStatus.active) ...[ + _quickAction('转赠', Icons.card_giftcard_rounded, () { + Navigator.pushNamed(context, '/transfer'); + }), + const SizedBox(width: 12), + _quickAction('出售', Icons.sell_rounded, () { + Navigator.pushNamed(context, '/sell'); + }), + ], + ], + ), + ), + ], + ), + ), + ); + } + + Widget _statusWidget(CouponStatus status) { + switch (status) { + case CouponStatus.active: + return StatusTags.active(); + case CouponStatus.pending: + return StatusTags.pending(); + case CouponStatus.expired: + return StatusTags.expired(); + case CouponStatus.used: + return StatusTags.used(); + } + } + + Widget _quickAction(String label, IconData icon, VoidCallback onTap) { + return GestureDetector( + onTap: onTap, + behavior: HitTestBehavior.opaque, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(icon, size: 14, color: AppColors.primary), + const SizedBox(width: 3), + Text(label, + style: AppTypography.caption.copyWith( + color: AppColors.primary, + fontWeight: FontWeight.w500, + )), + ], + ), + ); + } + + String _expiryText(DateTime expiryDate) { + final days = expiryDate.difference(DateTime.now()).inDays; + if (days < 0) return '已过期'; + if (days == 0) return '今天到期'; + if (days <= 7) return '$days天后到期'; + return '${expiryDate.year}/${expiryDate.month}/${expiryDate.day}到期'; + } + + Color _expiryColor(DateTime expiryDate) { + final days = expiryDate.difference(DateTime.now()).inDays; + if (days <= 3) return AppColors.error; + if (days <= 7) return AppColors.warning; + return AppColors.textTertiary; + } + + void _showReceiveSheet(BuildContext context) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (_) => const ReceiveCouponSheet(), + ); + } + + List<_WalletCouponItem> _filterCoupons(CouponStatus? filter) { + if (filter == null) return _mockCoupons; + return _mockCoupons.where((c) => c.status == filter).toList(); + } +} + +// ============================================================ +// Data Model +// ============================================================ +class _WalletCouponItem { + final String brandName; + final String name; + final double faceValue; + final CouponStatus status; + final DateTime expiryDate; + + const _WalletCouponItem({ + required this.brandName, + required this.name, + required this.faceValue, + required this.status, + required this.expiryDate, + }); +} + +// Mock data +final _mockCoupons = [ + _WalletCouponItem( + brandName: 'Starbucks', + name: '星巴克 \$25 礼品卡', + faceValue: 25.0, + status: CouponStatus.active, + expiryDate: DateTime.now().add(const Duration(days: 30)), + ), + _WalletCouponItem( + brandName: 'Amazon', + name: 'Amazon \$100 购物券', + faceValue: 100.0, + status: CouponStatus.active, + expiryDate: DateTime.now().add(const Duration(days: 45)), + ), + _WalletCouponItem( + brandName: 'Nike', + name: 'Nike \$80 运动券', + faceValue: 80.0, + status: CouponStatus.pending, + expiryDate: DateTime.now().add(const Duration(days: 15)), + ), + _WalletCouponItem( + brandName: 'Target', + name: 'Target \$30 折扣券', + faceValue: 30.0, + status: CouponStatus.active, + expiryDate: DateTime.now().add(const Duration(days: 60)), + ), + _WalletCouponItem( + brandName: 'Walmart', + name: 'Walmart \$50 生活券', + faceValue: 50.0, + status: CouponStatus.expired, + expiryDate: DateTime.now().subtract(const Duration(days: 5)), + ), +]; diff --git a/frontend/genex-mobile/lib/main.dart b/frontend/genex-mobile/lib/main.dart index 4973021..111e2af 100644 --- a/frontend/genex-mobile/lib/main.dart +++ b/frontend/genex-mobile/lib/main.dart @@ -28,6 +28,7 @@ import 'features/message/presentation/pages/message_detail_page.dart'; import 'features/issuer/presentation/pages/issuer_main_page.dart'; import 'features/merchant/presentation/pages/merchant_home_page.dart'; import 'features/trading/presentation/pages/trading_detail_page.dart'; +import 'features/coupons/presentation/pages/wallet_coupons_page.dart'; void main() { runApp(const GenexConsumerApp()); @@ -109,6 +110,8 @@ class GenexConsumerApp extends StatelessWidget { return MaterialPageRoute(builder: (_) => const MerchantHomePage()); case '/trading/detail': return MaterialPageRoute(builder: (_) => const TradingDetailPage()); + case '/wallet/coupons': + return MaterialPageRoute(builder: (_) => const WalletCouponsPage()); default: return MaterialPageRoute( builder: (_) => Scaffold(