Files
chat/client/flutter/lib/pages/home_page.dart
2026-04-25 16:36:34 +08:00

447 lines
13 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:sales_chat/pages/chat_page.dart';
import 'package:sales_chat/pages/invite_page.dart';
import 'package:sales_chat/pages/dashboard_page.dart';
import 'package:sales_chat/pages/admin_page.dart';
import 'package:sales_chat/providers/auth_provider.dart';
import 'package:sales_chat/providers/chat_provider.dart';
import 'package:sales_chat/widgets/user_avatar.dart';
import 'package:sales_chat/theme/app_theme.dart';
/// 首页 —— 底部导航容器 + 销售工作台Twitter 风格)
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _currentIndex = 0;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => _loadData());
}
Future<void> _loadData() async {
final chatProvider = context.read<ChatProvider>();
await chatProvider.loadGroups();
}
@override
Widget build(BuildContext context) {
final authProvider = context.watch<AuthProvider>();
final user = authProvider.user;
final pages = [
_buildWorkbenchPage(),
const ChatPage(),
const InvitePage(),
const DashboardPage(),
if (user?.isAdmin == true) const AdminPage(),
];
return Scaffold(
body: pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
items: [
const BottomNavigationBarItem(
icon: Icon(Icons.home_outlined),
activeIcon: Icon(Icons.home),
label: '工作台',
),
const BottomNavigationBarItem(
icon: Icon(Icons.chat_outlined),
activeIcon: Icon(Icons.chat),
label: '群聊',
),
const BottomNavigationBarItem(
icon: Icon(Icons.card_giftcard_outlined),
activeIcon: Icon(Icons.card_giftcard),
label: '邀请',
),
const BottomNavigationBarItem(
icon: Icon(Icons.bar_chart_outlined),
activeIcon: Icon(Icons.bar_chart),
label: '看板',
),
if (user?.isAdmin == true)
const BottomNavigationBarItem(
icon: Icon(Icons.admin_panel_settings_outlined),
activeIcon: Icon(Icons.admin_panel_settings),
label: '管理',
),
],
),
);
}
/// 构建工作台页面 —— Twitter 风格
Widget _buildWorkbenchPage() {
return Scaffold(
appBar: AppBar(
title: const Text('销售工作台'),
actions: [
IconButton(
icon: const Icon(Icons.notifications_outlined),
onPressed: () {
// TODO: 消息通知
},
),
PopupMenuButton<String>(
icon: const Icon(Icons.account_circle),
onSelected: (value) {
if (value == 'logout') {
_handleLogout();
}
},
itemBuilder: (context) => [
const PopupMenuItem(
value: 'profile',
child: ListTile(
leading: Icon(Icons.person_outline),
title: Text('个人信息'),
),
),
const PopupMenuItem(
value: 'logout',
child: ListTile(
leading: Icon(Icons.logout),
title: Text('退出登录'),
),
),
],
),
],
),
body: RefreshIndicator(
onRefresh: _loadData,
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 欢迎区域 —— 卡片容器
_buildWelcomeSection(),
const SizedBox(height: 8),
// 快捷操作 —— 圆形图标按钮
_buildQuickActionsSection(),
const SizedBox(height: 8),
// 最近群聊 —— 卡片区块列表
_buildRecentGroupsSection(),
],
),
),
),
);
}
/// 欢迎区域 —— 简洁卡片,头像 + 问候语
Widget _buildWelcomeSection() {
return Consumer<AuthProvider>(
builder: (context, auth, _) {
return Container(
width: double.infinity,
color: AppTheme.cardBackground,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: Row(
children: [
UserAvatar(
displayName: auth.user?.displayName ?? 'U',
radius: 24,
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'你好,${auth.user?.displayName ?? '用户'}',
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
color: AppTheme.textPrimary,
),
),
const SizedBox(height: 4),
const Text(
'今天也要加油哦!',
style: TextStyle(
fontSize: 14,
color: AppTheme.textSecondary,
),
),
],
),
),
],
),
);
},
);
}
/// 快捷操作 —— 3个圆形图标按钮横排
Widget _buildQuickActionsSection() {
return Container(
width: double.infinity,
color: AppTheme.cardBackground,
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_QuickActionButton(
icon: Icons.add_circle_outline,
label: '创建邀请',
color: AppTheme.primaryColor,
onTap: () {
setState(() {
_currentIndex = 2;
});
},
),
_QuickActionButton(
icon: Icons.chat_outlined,
label: '进入群聊',
color: AppTheme.infoColor,
onTap: () {
setState(() {
_currentIndex = 1;
});
},
),
_QuickActionButton(
icon: Icons.bar_chart_outlined,
label: '查看报表',
color: AppTheme.warningColor,
onTap: () {
setState(() {
_currentIndex = 3;
});
},
),
],
),
);
}
/// 最近群聊 —— 卡片区块,扁平列表项
Widget _buildRecentGroupsSection() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 区块标题
Container(
width: double.infinity,
color: AppTheme.cardBackground,
padding: const EdgeInsets.fromLTRB(16, 16, 16, 0),
child: const Text(
'最近群聊',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: AppTheme.textPrimary,
),
),
),
// 群聊列表
Consumer<ChatProvider>(
builder: (context, chat, _) {
if (chat.isLoading) {
return Container(
color: AppTheme.cardBackground,
padding: const EdgeInsets.all(24),
child: const Center(
child: CircularProgressIndicator(),
),
);
}
if (chat.groups.isEmpty) {
return Container(
color: AppTheme.cardBackground,
padding: const EdgeInsets.all(24),
child: Center(
child: Text(
'暂无群聊',
style: TextStyle(color: AppTheme.textSecondary, fontSize: 14),
),
),
);
}
final groups = chat.groups.take(5).toList();
return Container(
color: AppTheme.cardBackground,
child: Column(
children: [
for (int i = 0; i < groups.length; i++) ...[
_GroupListTile(
group: groups[i],
onTap: () {
chat.selectGroup(groups[i]);
setState(() {
_currentIndex = 1;
});
},
),
if (i < groups.length - 1)
const Padding(
padding: EdgeInsets.only(left: 62),
child: Divider(height: 0.5, thickness: 0.5),
),
],
],
),
);
},
),
],
);
}
/// 退出登录确认
Future<void> _handleLogout() async {
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('退出登录'),
content: const Text('确定要退出登录吗?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('取消'),
),
TextButton(
onPressed: () => Navigator.pop(context, true),
child: Text('退出', style: TextStyle(color: AppTheme.errorColor)),
),
],
),
);
if (confirmed == true && mounted) {
final authProvider = context.read<AuthProvider>();
await authProvider.logout();
if (mounted) {
Navigator.of(context).pushReplacementNamed('/login');
}
}
}
}
/// 快捷操作按钮 —— 圆形图标 + 文字标签(卡片背景色)
class _QuickActionButton extends StatelessWidget {
final IconData icon;
final String label;
final Color color;
final VoidCallback onTap;
const _QuickActionButton({
required this.icon,
required this.label,
required this.color,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
behavior: HitTestBehavior.opaque,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: color.withValues(alpha: 0.1),
shape: BoxShape.circle,
),
child: Icon(icon, color: color, size: 26),
),
const SizedBox(height: 8),
Text(
label,
style: const TextStyle(
fontSize: 12,
color: AppTheme.textSecondary,
),
),
],
),
);
}
}
/// 群聊列表项 —— 卡片背景行,头像 + 群名 + 成员数 + 箭头
class _GroupListTile extends StatelessWidget {
final dynamic group;
final VoidCallback onTap;
const _GroupListTile({
required this.group,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
GroupAvatar(
groupName: group.name,
radius: 22,
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
group.name,
style: const TextStyle(
fontSize: 16,
color: AppTheme.textPrimary,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 3),
Text(
'${group.memberCount} 成员',
style: const TextStyle(
fontSize: 13,
color: AppTheme.textHint,
),
),
],
),
),
const Icon(
Icons.chevron_right,
color: AppTheme.textHint,
size: 20,
),
],
),
),
);
}
}