profile_page.dart 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_riverpod/flutter_riverpod.dart';
  3. import 'package:go_router/go_router.dart';
  4. import 'package:tdesign_flutter/tdesign_flutter.dart';
  5. import '../../core/i18n/app_localizations.dart';
  6. import '../../core/i18n/locale_provider.dart';
  7. import '../../core/theme/app_colors.dart';
  8. import '../../shared/widgets/profile_menu_item.dart';
  9. import '../shell/nav_bar_config.dart';
  10. class ProfilePage extends ConsumerWidget {
  11. const ProfilePage({super.key});
  12. @override
  13. Widget build(BuildContext context, WidgetRef ref) {
  14. final l10n = AppLocalizations.of(context);
  15. final location = GoRouterState.of(context).uri.toString();
  16. final currentLocale = ref.watch(localeProvider);
  17. if (location.startsWith('/profile')) {
  18. ref
  19. .read(navBarConfigProvider.notifier)
  20. .update(
  21. NavBarConfig(
  22. title: l10n.get('tabProfile'),
  23. showBack: true,
  24. leadingIcon: Icons.close,
  25. ),
  26. );
  27. }
  28. return SingleChildScrollView(
  29. physics: const AlwaysScrollableScrollPhysics(),
  30. child: Column(
  31. children: [
  32. const SizedBox(height: 16),
  33. // 用户信息头部
  34. _buildUserInfoCard(context, l10n),
  35. const SizedBox(height: 16),
  36. // 菜单列表
  37. Padding(
  38. padding: const EdgeInsets.symmetric(horizontal: 16),
  39. child: Column(
  40. children: [
  41. ProfileMenuItem(
  42. icon: Icons.checklist,
  43. label: l10n.get('myApprovals'),
  44. onTap: () => context.push('/expense/list'),
  45. ),
  46. const SizedBox(height: 8),
  47. ProfileMenuItem(
  48. icon: Icons.description,
  49. label: l10n.get('myApplications'),
  50. onTap: () => context.push('/expense-apply/list'),
  51. ),
  52. const SizedBox(height: 8),
  53. ProfileMenuItem(
  54. icon: Icons.receipt_long,
  55. label: l10n.get('myExpenses'),
  56. onTap: () => context.push('/expense/list'),
  57. ),
  58. const SizedBox(height: 8),
  59. ProfileMenuItem(
  60. icon: Icons.edit_note,
  61. label: l10n.get('outingLog'),
  62. onTap: () => context.push('/outing-log/list'),
  63. ),
  64. const SizedBox(height: 8),
  65. ProfileMenuItem(
  66. icon: Icons.campaign,
  67. label: l10n.get('announcements'),
  68. onTap: () => context.push('/announcement/list'),
  69. ),
  70. const SizedBox(height: 8),
  71. ProfileMenuItem(
  72. icon: Icons.settings,
  73. label: l10n.get('settings'),
  74. ),
  75. const SizedBox(height: 8),
  76. ProfileMenuItem(
  77. icon: Icons.language,
  78. label: l10n.get('language'),
  79. trailing: _localeName(currentLocale),
  80. onTap: () => _showLanguageSheet(context, ref, currentLocale),
  81. ),
  82. const SizedBox(height: 8),
  83. ProfileMenuItem(
  84. icon: Icons.info_outline,
  85. label: l10n.get('about'),
  86. ),
  87. ],
  88. ),
  89. ),
  90. const SizedBox(height: 8),
  91. TDFooter(TDFooterType.text, text: l10n.get('version')),
  92. ],
  93. ),
  94. );
  95. }
  96. Widget _buildUserInfoCard(BuildContext context, AppLocalizations l10n) {
  97. return Container(
  98. margin: const EdgeInsets.symmetric(horizontal: 16),
  99. padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
  100. decoration: BoxDecoration(
  101. color: AppColors.bgCard,
  102. borderRadius: BorderRadius.circular(12),
  103. ),
  104. child: Row(
  105. children: [
  106. // 头像
  107. Container(
  108. width: 64,
  109. height: 64,
  110. decoration: const BoxDecoration(
  111. color: AppColors.primaryLight,
  112. shape: BoxShape.circle,
  113. ),
  114. child: Center(
  115. child: Text(
  116. '张',
  117. style: const TextStyle(
  118. fontSize: 28,
  119. fontWeight: FontWeight.w700,
  120. color: AppColors.primary,
  121. ),
  122. ),
  123. ),
  124. ),
  125. const SizedBox(width: 16),
  126. // 用户信息
  127. Expanded(
  128. child: Column(
  129. crossAxisAlignment: CrossAxisAlignment.start,
  130. children: [
  131. Text(
  132. l10n.get('userName'),
  133. style: const TextStyle(
  134. fontSize: AppFontSizes.title,
  135. fontWeight: FontWeight.w600,
  136. color: AppColors.textPrimary,
  137. ),
  138. ),
  139. const SizedBox(height: AppSpacing.xs),
  140. Text(
  141. l10n.get('salesDepartment'),
  142. style: const TextStyle(
  143. fontSize: AppFontSizes.body,
  144. color: AppColors.textSecondary,
  145. ),
  146. ),
  147. ],
  148. ),
  149. ),
  150. // 箭头
  151. const Icon(
  152. Icons.chevron_right,
  153. size: 16,
  154. color: AppColors.textPlaceholder,
  155. ),
  156. ],
  157. ),
  158. );
  159. }
  160. String _localeName(Locale locale) {
  161. if (locale.languageCode == 'zh' && locale.countryCode == 'TW') {
  162. return '繁體中文';
  163. }
  164. if (locale.languageCode == 'en') {
  165. return 'English';
  166. }
  167. return '简体中文';
  168. }
  169. void _showLanguageSheet(
  170. BuildContext context,
  171. WidgetRef ref,
  172. Locale currentLocale,
  173. ) {
  174. final languages = [
  175. (label: '简体中文', locale: const Locale('zh', 'CN')),
  176. (label: 'English', locale: const Locale('en')),
  177. (label: '繁體中文', locale: const Locale('zh', 'TW')),
  178. ];
  179. TDActionSheet.showListActionSheet(
  180. Navigator.of(context, rootNavigator: true).context,
  181. showCancel: false,
  182. useSafeArea: false,
  183. items: languages.map((lang) {
  184. final isSelected =
  185. lang.locale.languageCode == currentLocale.languageCode &&
  186. lang.locale.countryCode == currentLocale.countryCode;
  187. return TDActionSheetItem(
  188. label: isSelected ? '${lang.label} ✓' : lang.label,
  189. textStyle: TextStyle(
  190. color: isSelected ? AppColors.primary : AppColors.textPrimary,
  191. fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
  192. ),
  193. );
  194. }).toList(),
  195. onSelected: (item, index) {
  196. ref.read(localeProvider.notifier).setLocale(languages[index].locale);
  197. },
  198. );
  199. }
  200. }