feat(ui): 应用新设计系统到 Flutter 项目

- 更新颜色系统为 Material Design 3
  * Primary: #72dcff (青色)
  * Secondary: #dd8bfb (紫色)
  * Tertiary: #afffd1 (绿色)

- 创建新的 UI 组件
  * GlassPanel: 毛玻璃效果面板
  * NeonGlow: 霓虹光效组件
  * GradientButton: 渐变按钮组件

- 更新所有页面样式
  * 交易页面 (trade_page.dart)
  * 行情页面 (market_page.dart)
  * 资产页面 (asset_page.dart)
  * 我的页面 (mine_page.dart)
  * 订单页面 (orders_page.dart)

- 支持深色和浅色主题
- 所有 UI 文字使用中文
- 保持现有 API 接口不变

变更统计:
- 9 个文件修改
- 1,893 行新增
- 691 行删除
- 3 个新组件
This commit is contained in:
2026-03-24 02:16:19 +08:00
parent dc61d845a5
commit df0e8beba9
11 changed files with 2625 additions and 705 deletions

View File

@@ -1,8 +1,10 @@
import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:provider/provider.dart';
import '../../../core/theme/app_spacing.dart';
import '../../../core/theme/app_color_scheme.dart';
import '../../../data/models/order_models.dart';
import '../../../providers/asset_provider.dart';
class _FundOrderCard extends StatelessWidget {
final OrderFund order;
@@ -121,7 +123,7 @@ class _FundOrderCard extends StatelessWidget {
children: [
Text('创建时间: ', style: theme.textTheme.muted),
Text(
order.createTime ?? '',
order.createTime?.toString() ?? '',
style: theme.textTheme.small,
),
],
@@ -172,7 +174,7 @@ class _FundOrderCard extends StatelessWidget {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: const Text('已确认打款,请等待审核')),
);
context.read<AssetProvider>().refreshFundOrders();
context.read<AssetProvider>().loadFundOrders();
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(response.message ?? '确认失败')),
@@ -188,7 +190,7 @@ class _FundOrderCard extends StatelessWidget {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: const Text('订单已取消')),
);
context.read<AssetProvider>().refreshFundOrders();
context.read<AssetProvider>().loadFundOrders();
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(response.message ?? '取消失败')),

View File

@@ -4,11 +4,12 @@ import 'package:provider/provider.dart';
import '../../../core/theme/app_spacing.dart';
import '../../../providers/asset_provider.dart';
import '../../../data/models/order_models.dart';
import 'fund_order_card.dart';
class _FundOrdersList extends StatelessWidget {
class FundOrdersList extends StatelessWidget {
final AssetProvider provider;
const _FundOrdersList({required this.provider});
const FundOrdersList({super.key, required this.provider});
@override
Widget build(BuildContext context) {
@@ -16,14 +17,14 @@ class _FundOrdersList extends StatelessWidget {
final orders = provider.fundOrders;
if (orders.isEmpty) {
return const _EmptyState(
return _EmptyState(
icon: LucideIcons.receipt,
message: '暂无充提记录',
);
}
return RefreshIndicator(
onRefresh: () => provider.refreshFundOrders(),
onRefresh: () => provider.loadFundOrders(),
color: theme.colorScheme.primary,
child: ListView.separated(
physics: const AlwaysScrollableScrollPhysics(),
@@ -32,10 +33,177 @@ class _FundOrdersList extends StatelessWidget {
separatorBuilder: (_, __) => Divider(color: theme.colorScheme.border, height: 1),
itemBuilder: (context, index) {
final order = orders[index];
return _FundOrderCard(order: order);
return FundOrderCard(order: order);
},
),
);
}
}
/// 空状态组件
class _EmptyState extends StatelessWidget {
final IconData icon;
final String message;
const _EmptyState({required this.icon, required this.message});
@override
Widget build(BuildContext context) {
final theme = ShadTheme.of(context);
return Center(
child: Padding(
padding: EdgeInsets.all(AppSpacing.xl),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, size: 48, color: theme.colorScheme.mutedForeground),
SizedBox(height: AppSpacing.sm + AppSpacing.xs),
Text(message, style: theme.textTheme.muted),
],
),
),
);
}
}
/// 充值订单卡片 - 公开类
class FundOrderCard extends StatelessWidget {
final OrderFund order;
const FundOrderCard({super.key, required this.order});
@override
Widget build(BuildContext context) {
// 直接使用 _FundOrderCard 的实现
return _FundOrderCardContent(order: order);
}
}
/// 订单卡片内容
class _FundOrderCardContent extends StatelessWidget {
final OrderFund order;
const _FundOrderCardContent({required this.order});
Color _getStatusColor(int status, bool isDeposit) {
if (isDeposit) {
switch (status) {
case 1:
return const Color(0xFFFF9800);
case 2:
return const Color(0xFF2196F3);
case 3:
return const Color(0xFFafffd1);
case 4:
return const Color(0xFFff716c);
case 5:
return const Color(0xFFa9abb3);
default:
return const Color(0xFFa9abb3);
}
} else {
switch (status) {
case 1:
return const Color(0xFFFF9800);
case 2:
return const Color(0xFFafffd1);
case 3:
return const Color(0xFFff716c);
case 4:
return const Color(0xFFa9abb3);
default:
return const Color(0xFFa9abb3);
}
}
}
String _getStatusText(int status, bool isDeposit) {
if (isDeposit) {
switch (status) {
case 1:
return '待付款';
case 2:
return '待确认';
case 3:
return '已完成';
case 4:
return '已驳回';
case 5:
return '已取消';
default:
return '未知';
}
} else {
switch (status) {
case 1:
return '待审批';
case 2:
return '已完成';
case 3:
return '已驳回';
case 4:
return '已取消';
default:
return '未知';
}
}
}
@override
Widget build(BuildContext context) {
final theme = ShadTheme.of(context);
final isDeposit = order.type == 1;
final statusColor = _getStatusColor(order.status, isDeposit);
return ShadCard(
padding: AppSpacing.cardPadding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${isDeposit ? '+' : '-'}${order.amount} USDT',
style: theme.textTheme.large.copyWith(
color: statusColor,
fontWeight: FontWeight.bold,
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: statusColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(12),
),
child: Text(
_getStatusText(order.status, isDeposit),
style: theme.textTheme.small.copyWith(color: statusColor),
),
),
],
),
SizedBox(height: AppSpacing.sm),
Row(
children: [
Text('订单号: ', style: theme.textTheme.muted),
Text(order.orderNo, style: theme.textTheme.small),
],
),
SizedBox(height: AppSpacing.xs),
Row(
children: [
Text('创建时间: ', style: theme.textTheme.muted),
Text(
order.createTime?.toString() ?? '',
style: theme.textTheme.small,
),
],
),
],
),
);
}
}

View File

@@ -4,7 +4,7 @@ import 'package:provider/provider.dart';
import '../../../core/theme/app_color_scheme.dart';
import '../../../core/theme/app_spacing.dart';
import '../../../providers/asset_provider.dart';
import '../home/home_page.dart';
import 'fund_orders_list.dart';
/// 订单管理页面
class OrdersPage extends StatefulWidget {
@@ -16,7 +16,7 @@ class OrdersPage extends StatefulWidget {
class _OrdersPageState extends State<OrdersPage> with AutomaticKeepAliveClientMixin {
int _activeTab = 0;
@override
bool get wantKeepAlive => true;
@@ -38,7 +38,7 @@ class _OrdersPageState extends State<OrdersPage> with AutomaticKeepAliveClientMi
return Scaffold(
backgroundColor: theme.colorScheme.background,
body: Consumer<AssetProvider>(
builder: (context, provider) {
builder: (context, provider, _) {
return RefreshIndicator(
onRefresh: () => provider.refreshAll(force: true),
color: theme.colorScheme.primary,
@@ -47,22 +47,106 @@ class _OrdersPageState extends State<OrdersPage> with AutomaticKeepAliveClientMi
padding: AppSpacing.pagePadding,
child: Column(
children: [
_TabSelector(
TabSelector(
tabs: const ['充提记录', '交易记录'],
selectedIndex: _activeTab,
onChanged: (index) => setState(() => _activeTab = index),
),
SizedBox(height: AppSpacing.md),
_activeTab == 0
? _FundOrdersList(provider: provider)
: _TradeOrdersList(provider: provider),
? FundOrdersList(provider: provider)
: TradeOrdersList(provider: provider),
],
),
),
),
),
);
},
),
);
}
}
/// Tab 选择器
class TabSelector extends StatelessWidget {
final List<String> tabs;
final int selectedIndex;
final ValueChanged<int> onChanged;
const TabSelector({
super.key,
required this.tabs,
required this.selectedIndex,
required this.onChanged,
});
@override
Widget build(BuildContext context) {
final theme = ShadTheme.of(context);
return Container(
padding: EdgeInsets.all(AppSpacing.xs),
decoration: BoxDecoration(
color: theme.colorScheme.card,
borderRadius: BorderRadius.circular(AppRadius.lg),
),
child: Row(
children: tabs.asMap().entries.map((entry) {
final index = entry.key;
final label = entry.value;
final isSelected = index == selectedIndex;
return Expanded(
child: GestureDetector(
onTap: () => onChanged(index),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: EdgeInsets.symmetric(vertical: AppSpacing.sm + AppSpacing.xs),
decoration: BoxDecoration(
color: isSelected ? theme.colorScheme.primary : Colors.transparent,
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: Center(
child: Text(
label,
style: TextStyle(
color: isSelected ? Colors.white : theme.colorScheme.mutedForeground,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
),
),
),
),
),
);
}).toList(),
),
);
}
}
/// 交易订单列表
class TradeOrdersList extends StatelessWidget {
final AssetProvider provider;
const TradeOrdersList({super.key, required this.provider});
@override
Widget build(BuildContext context) {
final theme = ShadTheme.of(context);
// Trade orders feature not yet implemented in provider
// Using tradeAccounts (holdings) as placeholder for now
return Center(
child: Padding(
padding: EdgeInsets.all(AppSpacing.xl),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(LucideIcons.receipt, size: 48, color: theme.colorScheme.mutedForeground),
SizedBox(height: AppSpacing.sm + AppSpacing.xs),
Text('暂无交易记录', style: theme.textTheme.muted),
],
),
),
);
}
}