760 lines
22 KiB
Dart
760 lines
22 KiB
Dart
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 '../../providers/user_providers.dart';
|
|
import '../../providers/profile_providers.dart';
|
|
import '../../widgets/shimmer_loading.dart';
|
|
|
|
class ProfilePage extends ConsumerWidget {
|
|
const ProfilePage({super.key});
|
|
|
|
// 设计色彩
|
|
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 _lightGray = Color(0xFF9CA3AF);
|
|
static const Color _bgGray = Color(0xFFF3F4F6);
|
|
static const Color _red = Color(0xFFEF4444);
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final user = ref.watch(userNotifierProvider);
|
|
final statsAsync = ref.watch(userStatsProvider);
|
|
final invitationCode = ref.watch(invitationCodeProvider);
|
|
|
|
final isStatsLoading = statsAsync.isLoading;
|
|
final stats = statsAsync.valueOrNull;
|
|
|
|
return Scaffold(
|
|
backgroundColor: _bgGray,
|
|
body: SafeArea(
|
|
bottom: false,
|
|
child: RefreshIndicator(
|
|
onRefresh: () async {
|
|
ref.invalidate(userStatsProvider);
|
|
await ref.read(userNotifierProvider.notifier).fetchProfile();
|
|
},
|
|
child: SingleChildScrollView(
|
|
physics: const AlwaysScrollableScrollPhysics(),
|
|
child: Column(
|
|
children: [
|
|
// 用户头部信息
|
|
_buildUserHeader(context, user),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
// 统计数据行
|
|
_buildStatsRow(stats, isStatsLoading),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
// 邀请码卡片
|
|
_buildInvitationCard(context, invitationCode),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
// 账户设置
|
|
_buildAccountSettings(context, user),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
// 记录入口
|
|
_buildRecordsSection(context),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
// 团队与收益
|
|
_buildTeamEarningsSection(context),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
// 其他设置
|
|
_buildOtherSettings(context),
|
|
|
|
const SizedBox(height: 24),
|
|
|
|
// 退出登录
|
|
_buildLogoutButton(context, ref),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
// 版本信息
|
|
const Text(
|
|
'Version 1.0.0',
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: _lightGray,
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 24),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildUserHeader(BuildContext context, UserState user) {
|
|
return Container(
|
|
padding: const EdgeInsets.all(20),
|
|
color: Colors.white,
|
|
child: Row(
|
|
children: [
|
|
// 头像
|
|
Container(
|
|
width: 80,
|
|
height: 80,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
gradient: LinearGradient(
|
|
colors: [_orange.withValues(alpha: 0.8), _orange],
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
),
|
|
),
|
|
child: Center(
|
|
child: Text(
|
|
user.nickname?.isNotEmpty == true
|
|
? user.nickname!.substring(0, 1).toUpperCase()
|
|
: (user.realName?.isNotEmpty == true
|
|
? user.realName!.substring(0, 1).toUpperCase()
|
|
: 'U'),
|
|
style: const TextStyle(
|
|
fontSize: 36,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
const SizedBox(width: 16),
|
|
|
|
// 用户信息
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Text(
|
|
user.realName ?? user.nickname ?? '榴莲用户',
|
|
style: const TextStyle(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
color: _darkText,
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
// 实名认证徽章
|
|
if (user.isKycVerified)
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 8,
|
|
vertical: 2,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: _green.withValues(alpha: 0.1),
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: const Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(Icons.verified, size: 12, color: _green),
|
|
SizedBox(width: 2),
|
|
Text(
|
|
'已实名',
|
|
style: TextStyle(
|
|
fontSize: 10,
|
|
fontWeight: FontWeight.w500,
|
|
color: _green,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 8),
|
|
Row(
|
|
children: [
|
|
Text(
|
|
'ID: ${user.accountSequence ?? '--------'}',
|
|
style: const TextStyle(
|
|
fontSize: 14,
|
|
color: _grayText,
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
GestureDetector(
|
|
onTap: () {
|
|
if (user.accountSequence != null) {
|
|
Clipboard.setData(
|
|
ClipboardData(text: user.accountSequence!),
|
|
);
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('ID已复制'),
|
|
duration: Duration(seconds: 1),
|
|
),
|
|
);
|
|
}
|
|
},
|
|
child: const Icon(
|
|
Icons.copy,
|
|
size: 16,
|
|
color: _grayText,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
// 手机号
|
|
if (user.phone != null)
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 4),
|
|
child: Text(
|
|
'手机: ${user.phone}',
|
|
style: const TextStyle(
|
|
fontSize: 12,
|
|
color: _lightGray,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
// 编辑按钮
|
|
IconButton(
|
|
onPressed: () {
|
|
// TODO: 编辑个人资料
|
|
},
|
|
icon: const Icon(
|
|
Icons.edit_outlined,
|
|
color: _grayText,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildStatsRow(UserStats? stats, bool isLoading) {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
|
color: Colors.white,
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
children: [
|
|
_buildStatItem(
|
|
'认种状态',
|
|
stats?.hasAdopted == true ? '已认种' : '未认种',
|
|
isLoading,
|
|
),
|
|
_buildDivider(),
|
|
_buildStatItem(
|
|
'直推人数',
|
|
stats?.directReferralAdoptedCount.toString() ?? '0',
|
|
isLoading,
|
|
),
|
|
_buildDivider(),
|
|
_buildStatItem(
|
|
'团队层级',
|
|
stats?.unlockedLevelDepth.toString() ?? '0',
|
|
isLoading,
|
|
),
|
|
_buildDivider(),
|
|
_buildStatItem(
|
|
'VIP等级',
|
|
stats?.vipLevel ?? '-',
|
|
isLoading,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildStatItem(String label, String value, bool isLoading) {
|
|
return Column(
|
|
children: [
|
|
DataText(
|
|
data: isLoading ? null : value,
|
|
isLoading: isLoading,
|
|
placeholder: '--',
|
|
style: const TextStyle(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
color: _darkText,
|
|
),
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
label,
|
|
style: const TextStyle(
|
|
fontSize: 12,
|
|
color: _grayText,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildDivider() {
|
|
return Container(
|
|
width: 1,
|
|
height: 30,
|
|
color: _bgGray,
|
|
);
|
|
}
|
|
|
|
Widget _buildInvitationCard(BuildContext context, String invitationCode) {
|
|
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: [
|
|
const Text(
|
|
'我的邀请码',
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: _grayText,
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 16,
|
|
vertical: 12,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: _bgGray,
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Text(
|
|
invitationCode,
|
|
style: const TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
color: _darkText,
|
|
letterSpacing: 2,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
_buildActionButton(
|
|
icon: Icons.copy,
|
|
label: '复制',
|
|
onTap: () {
|
|
Clipboard.setData(ClipboardData(text: invitationCode));
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('邀请码已复制'),
|
|
duration: Duration(seconds: 1),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
const SizedBox(width: 8),
|
|
_buildActionButton(
|
|
icon: Icons.share,
|
|
label: '分享',
|
|
onTap: () {
|
|
// TODO: 分享功能
|
|
},
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildActionButton({
|
|
required IconData icon,
|
|
required String label,
|
|
required VoidCallback onTap,
|
|
}) {
|
|
return GestureDetector(
|
|
onTap: onTap,
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
decoration: BoxDecoration(
|
|
color: _orange,
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(icon, size: 16, color: Colors.white),
|
|
const SizedBox(width: 4),
|
|
Text(
|
|
label,
|
|
style: const TextStyle(
|
|
fontSize: 12,
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildAccountSettings(BuildContext context, UserState user) {
|
|
return Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: 16),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const Padding(
|
|
padding: EdgeInsets.fromLTRB(16, 16, 16, 8),
|
|
child: Text(
|
|
'账户设置',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
color: _darkText,
|
|
),
|
|
),
|
|
),
|
|
_buildSettingItem(
|
|
icon: Icons.security,
|
|
label: '账户安全',
|
|
onTap: () => context.push(Routes.changePassword),
|
|
),
|
|
_buildSettingItem(
|
|
icon: Icons.verified_user,
|
|
label: '实名认证',
|
|
trailing: Text(
|
|
user.isKycVerified ? '已认证' : '未认证',
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: user.isKycVerified ? _green : _lightGray,
|
|
),
|
|
),
|
|
onTap: () {},
|
|
),
|
|
_buildSettingItem(
|
|
icon: Icons.lock_outline,
|
|
label: '支付密码',
|
|
trailing: const Text(
|
|
'已设置',
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: _green,
|
|
),
|
|
),
|
|
onTap: () {},
|
|
showDivider: false,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSettingItem({
|
|
required IconData icon,
|
|
required String label,
|
|
Widget? trailing,
|
|
required VoidCallback onTap,
|
|
bool showDivider = true,
|
|
}) {
|
|
return Column(
|
|
children: [
|
|
ListTile(
|
|
leading: Icon(icon, color: _grayText, size: 22),
|
|
title: Text(
|
|
label,
|
|
style: const TextStyle(
|
|
fontSize: 14,
|
|
color: _darkText,
|
|
),
|
|
),
|
|
trailing: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
if (trailing != null) trailing,
|
|
if (trailing != null) const SizedBox(width: 8),
|
|
const Icon(Icons.chevron_right, color: _lightGray, size: 20),
|
|
],
|
|
),
|
|
onTap: onTap,
|
|
),
|
|
if (showDivider)
|
|
const Divider(height: 1, indent: 56, endIndent: 16),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildRecordsSection(BuildContext context) {
|
|
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: [
|
|
const Text(
|
|
'我的记录',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
color: _darkText,
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
children: [
|
|
_buildRecordIcon(
|
|
icon: Icons.eco,
|
|
label: '认种记录',
|
|
onTap: () {},
|
|
),
|
|
_buildRecordIcon(
|
|
icon: Icons.assignment,
|
|
label: '分配记录',
|
|
onTap: () {},
|
|
),
|
|
_buildRecordIcon(
|
|
icon: Icons.receipt_long,
|
|
label: '交易记录',
|
|
onTap: () {},
|
|
),
|
|
_buildRecordIcon(
|
|
icon: Icons.account_balance_wallet,
|
|
label: '提现记录',
|
|
onTap: () {},
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildRecordIcon({
|
|
required IconData icon,
|
|
required String label,
|
|
required VoidCallback onTap,
|
|
}) {
|
|
return GestureDetector(
|
|
onTap: onTap,
|
|
behavior: HitTestBehavior.opaque,
|
|
child: Column(
|
|
children: [
|
|
Container(
|
|
width: 48,
|
|
height: 48,
|
|
decoration: BoxDecoration(
|
|
color: _orange.withValues(alpha: 0.1),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Icon(icon, color: _orange, size: 24),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
label,
|
|
style: const TextStyle(
|
|
fontSize: 12,
|
|
color: _grayText,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildTeamEarningsSection(BuildContext context) {
|
|
return Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: 16),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const Padding(
|
|
padding: EdgeInsets.fromLTRB(16, 16, 16, 8),
|
|
child: Text(
|
|
'团队与收益',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
color: _darkText,
|
|
),
|
|
),
|
|
),
|
|
_buildSettingItem(
|
|
icon: Icons.people,
|
|
label: '我的团队',
|
|
onTap: () {},
|
|
),
|
|
_buildSettingItem(
|
|
icon: Icons.trending_up,
|
|
label: '收益明细',
|
|
onTap: () {},
|
|
),
|
|
_buildSettingItem(
|
|
icon: Icons.card_giftcard,
|
|
label: '推广奖励',
|
|
onTap: () {},
|
|
showDivider: false,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildOtherSettings(BuildContext context) {
|
|
return Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: 16),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const Padding(
|
|
padding: EdgeInsets.fromLTRB(16, 16, 16, 8),
|
|
child: Text(
|
|
'其他设置',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
color: _darkText,
|
|
),
|
|
),
|
|
),
|
|
_buildSwitchItem(
|
|
icon: Icons.notifications_outlined,
|
|
label: '消息通知',
|
|
value: true,
|
|
onChanged: (value) {},
|
|
),
|
|
_buildSwitchItem(
|
|
icon: Icons.dark_mode_outlined,
|
|
label: '深色模式',
|
|
value: false,
|
|
onChanged: (value) {},
|
|
),
|
|
_buildSettingItem(
|
|
icon: Icons.help_outline,
|
|
label: '帮助中心',
|
|
onTap: () {},
|
|
),
|
|
_buildSettingItem(
|
|
icon: Icons.info_outline,
|
|
label: '关于我们',
|
|
onTap: () {},
|
|
showDivider: false,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSwitchItem({
|
|
required IconData icon,
|
|
required String label,
|
|
required bool value,
|
|
required ValueChanged<bool> onChanged,
|
|
}) {
|
|
return Column(
|
|
children: [
|
|
ListTile(
|
|
leading: Icon(icon, color: _grayText, size: 22),
|
|
title: Text(
|
|
label,
|
|
style: const TextStyle(
|
|
fontSize: 14,
|
|
color: _darkText,
|
|
),
|
|
),
|
|
trailing: Switch(
|
|
value: value,
|
|
onChanged: onChanged,
|
|
activeTrackColor: _orange,
|
|
activeThumbColor: Colors.white,
|
|
),
|
|
),
|
|
const Divider(height: 1, indent: 56, endIndent: 16),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildLogoutButton(BuildContext context, WidgetRef ref) {
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
child: SizedBox(
|
|
width: double.infinity,
|
|
child: TextButton(
|
|
onPressed: () {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: const Text('退出登录'),
|
|
content: const Text('确定要退出登录吗?'),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: const Text('取消'),
|
|
),
|
|
TextButton(
|
|
onPressed: () {
|
|
Navigator.pop(context);
|
|
ref.read(userNotifierProvider.notifier).logout();
|
|
},
|
|
child: const Text(
|
|
'确定',
|
|
style: TextStyle(color: _red),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
style: TextButton.styleFrom(
|
|
backgroundColor: Colors.white,
|
|
padding: const EdgeInsets.symmetric(vertical: 14),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
),
|
|
child: const Text(
|
|
'退出登录',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
color: _red,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|