|
|
@@ -0,0 +1,207 @@
|
|
|
+import 'package:flutter/material.dart';
|
|
|
+import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
+import 'package:go_router/go_router.dart';
|
|
|
+import 'package:tdesign_flutter/tdesign_flutter.dart';
|
|
|
+import '../../core/i18n/app_localizations.dart';
|
|
|
+import '../../core/i18n/locale_provider.dart';
|
|
|
+import '../../core/theme/app_colors.dart';
|
|
|
+import '../../shared/widgets/profile_menu_item.dart';
|
|
|
+import '../shell/nav_bar_config.dart';
|
|
|
+
|
|
|
+class ProfilePage extends ConsumerWidget {
|
|
|
+ const ProfilePage({super.key});
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context, WidgetRef ref) {
|
|
|
+ final l10n = AppLocalizations.of(context);
|
|
|
+ final location = GoRouterState.of(context).uri.toString();
|
|
|
+ final currentLocale = ref.watch(localeProvider);
|
|
|
+
|
|
|
+ if (location.startsWith('/profile')) {
|
|
|
+ ref
|
|
|
+ .read(navBarConfigProvider.notifier)
|
|
|
+ .update(
|
|
|
+ NavBarConfig(
|
|
|
+ title: l10n.get('tabProfile'),
|
|
|
+ showBack: true,
|
|
|
+ leadingIcon: Icons.close,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+ return SingleChildScrollView(
|
|
|
+ physics: const AlwaysScrollableScrollPhysics(),
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ const SizedBox(height: 16),
|
|
|
+ // 用户信息头部
|
|
|
+ _buildUserInfoCard(context, l10n),
|
|
|
+ const SizedBox(height: 16),
|
|
|
+ // 菜单列表
|
|
|
+ Padding(
|
|
|
+ padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ ProfileMenuItem(
|
|
|
+ icon: Icons.checklist,
|
|
|
+ label: l10n.get('myApprovals'),
|
|
|
+ onTap: () => context.push('/expense/list'),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 8),
|
|
|
+ ProfileMenuItem(
|
|
|
+ icon: Icons.description,
|
|
|
+ label: l10n.get('myApplications'),
|
|
|
+ onTap: () => context.push('/expense-apply/list'),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 8),
|
|
|
+ ProfileMenuItem(
|
|
|
+ icon: Icons.receipt_long,
|
|
|
+ label: l10n.get('myExpenses'),
|
|
|
+ onTap: () => context.push('/expense/list'),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 8),
|
|
|
+ ProfileMenuItem(
|
|
|
+ icon: Icons.edit_note,
|
|
|
+ label: l10n.get('outingLog'),
|
|
|
+ onTap: () => context.push('/outing-log/list'),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 8),
|
|
|
+ ProfileMenuItem(
|
|
|
+ icon: Icons.campaign,
|
|
|
+ label: l10n.get('announcements'),
|
|
|
+ onTap: () => context.push('/announcement/list'),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 8),
|
|
|
+ ProfileMenuItem(
|
|
|
+ icon: Icons.settings,
|
|
|
+ label: l10n.get('settings'),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 8),
|
|
|
+ ProfileMenuItem(
|
|
|
+ icon: Icons.language,
|
|
|
+ label: l10n.get('language'),
|
|
|
+ trailing: _localeName(currentLocale),
|
|
|
+ onTap: () => _showLanguageSheet(context, ref, currentLocale),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 8),
|
|
|
+ ProfileMenuItem(
|
|
|
+ icon: Icons.info_outline,
|
|
|
+ label: l10n.get('about'),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 8),
|
|
|
+ TDFooter(TDFooterType.text, text: l10n.get('version')),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildUserInfoCard(BuildContext context, AppLocalizations l10n) {
|
|
|
+ return Container(
|
|
|
+ margin: const EdgeInsets.symmetric(horizontal: 16),
|
|
|
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: AppColors.bgCard,
|
|
|
+ borderRadius: BorderRadius.circular(12),
|
|
|
+ ),
|
|
|
+ child: Row(
|
|
|
+ children: [
|
|
|
+ // 头像
|
|
|
+ Container(
|
|
|
+ width: 64,
|
|
|
+ height: 64,
|
|
|
+ decoration: const BoxDecoration(
|
|
|
+ color: AppColors.primaryLight,
|
|
|
+ shape: BoxShape.circle,
|
|
|
+ ),
|
|
|
+ child: Center(
|
|
|
+ child: Text(
|
|
|
+ '张',
|
|
|
+ style: const TextStyle(
|
|
|
+ fontSize: 28,
|
|
|
+ fontWeight: FontWeight.w700,
|
|
|
+ color: AppColors.primary,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(width: 16),
|
|
|
+ // 用户信息
|
|
|
+ Expanded(
|
|
|
+ child: Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ children: [
|
|
|
+ Text(
|
|
|
+ l10n.get('userName'),
|
|
|
+ style: const TextStyle(
|
|
|
+ fontSize: AppFontSizes.title,
|
|
|
+ fontWeight: FontWeight.w600,
|
|
|
+ color: AppColors.textPrimary,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: AppSpacing.xs),
|
|
|
+ Text(
|
|
|
+ l10n.get('salesDepartment'),
|
|
|
+ style: const TextStyle(
|
|
|
+ fontSize: AppFontSizes.body,
|
|
|
+ color: AppColors.textSecondary,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ // 箭头
|
|
|
+ const Icon(
|
|
|
+ Icons.chevron_right,
|
|
|
+ size: 16,
|
|
|
+ color: AppColors.textPlaceholder,
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ String _localeName(Locale locale) {
|
|
|
+ if (locale.languageCode == 'zh' && locale.countryCode == 'TW') {
|
|
|
+ return '繁體中文';
|
|
|
+ }
|
|
|
+ if (locale.languageCode == 'en') {
|
|
|
+ return 'English';
|
|
|
+ }
|
|
|
+ return '简体中文';
|
|
|
+ }
|
|
|
+
|
|
|
+ void _showLanguageSheet(
|
|
|
+ BuildContext context,
|
|
|
+ WidgetRef ref,
|
|
|
+ Locale currentLocale,
|
|
|
+ ) {
|
|
|
+ final languages = [
|
|
|
+ (label: '简体中文', locale: const Locale('zh', 'CN')),
|
|
|
+ (label: 'English', locale: const Locale('en')),
|
|
|
+ (label: '繁體中文', locale: const Locale('zh', 'TW')),
|
|
|
+ ];
|
|
|
+
|
|
|
+ TDActionSheet.showListActionSheet(
|
|
|
+ Navigator.of(context, rootNavigator: true).context,
|
|
|
+ showCancel: false,
|
|
|
+ useSafeArea: false,
|
|
|
+ items: languages.map((lang) {
|
|
|
+ final isSelected =
|
|
|
+ lang.locale.languageCode == currentLocale.languageCode &&
|
|
|
+ lang.locale.countryCode == currentLocale.countryCode;
|
|
|
+ return TDActionSheetItem(
|
|
|
+ label: isSelected ? '${lang.label} ✓' : lang.label,
|
|
|
+ textStyle: TextStyle(
|
|
|
+ color: isSelected ? AppColors.primary : AppColors.textPrimary,
|
|
|
+ fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }).toList(),
|
|
|
+ onSelected: (item, index) {
|
|
|
+ ref.read(localeProvider.notifier).setLocale(languages[index].locale);
|
|
|
+ },
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|