Files
zhinian_manage/lib/pages/settings_page.dart
2026-05-15 14:41:37 +08:00

435 lines
16 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../providers/auth_provider.dart';
import '../providers/settings_provider.dart';
import '../theme.dart';
import '../l10n/app_localizations.dart';
class SettingsPage extends ConsumerWidget {
const SettingsPage({super.key});
void _showThemePicker(BuildContext context, WidgetRef ref) {
final current = ref.read(settingsProvider).themeMode;
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
builder: (ctx) {
final l10n = AppLocalizations.of(ctx)!;
return Container(
padding: const EdgeInsets.all(20),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
),
child: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(width: 40, height: 4, decoration: BoxDecoration(color: AppColors.divider, borderRadius: BorderRadius.circular(2))),
const SizedBox(height: 20),
Text(l10n.themeSettings, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: AppColors.textPrimary)),
const SizedBox(height: 20),
_themeOption(ctx, ref, AppThemeMode.light, l10n.lightMode, Icons.wb_sunny_outlined, current),
_themeOption(ctx, ref, AppThemeMode.dark, l10n.darkMode, Icons.nights_stay_outlined, current),
_themeOption(ctx, ref, AppThemeMode.system, l10n.followSystem, Icons.settings_suggest_outlined, current),
],
),
),
);
},
);
}
Widget _themeOption(BuildContext ctx, WidgetRef ref, AppThemeMode mode, String label, IconData icon, AppThemeMode current) {
final selected = current == mode;
return ListTile(
leading: Icon(icon, color: selected ? AppColors.primary : AppColors.textTertiary),
title: Text(label, style: TextStyle(fontWeight: selected ? FontWeight.w600 : FontWeight.w400, color: AppColors.textPrimary)),
trailing: selected ? const Icon(Icons.check_circle, color: AppColors.primary) : null,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
onTap: () {
ref.read(settingsProvider.notifier).setThemeMode(mode);
Navigator.of(ctx).pop();
},
);
}
void _showLanguagePicker(BuildContext context, WidgetRef ref) {
final current = ref.read(settingsProvider).locale;
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
builder: (ctx) {
final l10n = AppLocalizations.of(ctx)!;
return Container(
padding: const EdgeInsets.all(20),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
),
child: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(width: 40, height: 4, decoration: BoxDecoration(color: AppColors.divider, borderRadius: BorderRadius.circular(2))),
const SizedBox(height: 20),
Text(l10n.languageSettings, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: AppColors.textPrimary)),
const SizedBox(height: 20),
_languageOption(ctx, ref, const Locale('zh', 'CN'), l10n.simplifiedChinese, current),
_languageOption(ctx, ref, const Locale('en', 'US'), l10n.english, current),
_languageOption(ctx, ref, const Locale('th', 'TH'), l10n.thai, current),
],
),
),
);
},
);
}
Widget _languageOption(BuildContext ctx, WidgetRef ref, Locale locale, String label, Locale current) {
final selected = current.languageCode == locale.languageCode;
return ListTile(
title: Text(label, style: TextStyle(fontWeight: selected ? FontWeight.w600 : FontWeight.w400, color: AppColors.textPrimary)),
trailing: selected ? const Icon(Icons.check_circle, color: AppColors.primary) : null,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
onTap: () {
ref.read(settingsProvider.notifier).setLocale(locale);
Navigator.of(ctx).pop();
},
);
}
@override
Widget build(BuildContext context, WidgetRef ref) {
final l10n = AppLocalizations.of(context)!;
final authState = ref.watch(authProvider);
final settings = ref.watch(settingsProvider);
final user = authState.user;
final isBoss = user?.isBoss ?? false;
final themeLabel = switch (settings.themeMode) {
AppThemeMode.light => l10n.lightMode,
AppThemeMode.dark => l10n.darkMode,
AppThemeMode.system => l10n.followSystem,
};
final localeLabel = switch (settings.locale.languageCode) {
'zh' => l10n.simplifiedChinese,
'en' => l10n.english,
'th' => l10n.thai,
_ => l10n.simplifiedChinese,
};
return Scaffold(
backgroundColor: AppColors.background,
appBar: AppBar(title: Text(l10n.settings)),
body: ListView(
padding: const EdgeInsets.all(20),
children: [
_buildProfileCard(user, l10n),
const SizedBox(height: 24),
_buildSectionTitle(l10n.appConfiguration),
const SizedBox(height: 12),
_buildSettingsCard([
_SettingItem(
icon: Icons.notifications_outlined,
iconColor: const Color(0xFF8B5CF6),
title: l10n.pushNotification,
subtitle: settings.pushNotification ? l10n.enabled : l10n.disabled,
trailing: Switch(
value: settings.pushNotification,
onChanged: (v) => ref.read(settingsProvider.notifier).setPushNotification(v),
activeTrackColor: AppColors.primary,
),
onTap: () => ref.read(settingsProvider.notifier).setPushNotification(!settings.pushNotification),
),
_SettingItem(
icon: Icons.volume_up_outlined,
iconColor: const Color(0xFFEC4899),
title: l10n.soundNotification,
subtitle: settings.soundNotification ? l10n.enabled : l10n.disabled,
trailing: Switch(
value: settings.soundNotification,
onChanged: (v) => ref.read(settingsProvider.notifier).setSoundNotification(v),
activeTrackColor: AppColors.primary,
),
onTap: () => ref.read(settingsProvider.notifier).setSoundNotification(!settings.soundNotification),
),
_SettingItem(
icon: Icons.palette_outlined,
iconColor: const Color(0xFFEC4899),
title: l10n.themeSettings,
subtitle: themeLabel,
onTap: () => _showThemePicker(context, ref),
),
_SettingItem(
icon: Icons.language_outlined,
iconColor: const Color(0xFF3B82F6),
title: l10n.languageSettings,
subtitle: localeLabel,
onTap: () => _showLanguagePicker(context, ref),
),
]),
const SizedBox(height: 24),
_buildSectionTitle(l10n.businessExtension),
const SizedBox(height: 12),
_buildSettingsCard([
_SettingItem(
icon: Icons.store_outlined,
iconColor: const Color(0xFFF59E0B),
title: l10n.storeManagement,
subtitle: l10n.storeDesc,
onTap: () => context.push('/settings/store'),
),
_SettingItem(
icon: Icons.people_outline,
iconColor: const Color(0xFF10B981),
title: l10n.employeeManagement,
subtitle: l10n.employeeDesc,
showBadge: isBoss,
onTap: () => context.push('/settings/employees'),
),
_SettingItem(
icon: Icons.analytics_outlined,
iconColor: const Color(0xFFEF4444),
title: l10n.dataReport,
subtitle: l10n.reportDesc,
showBadge: isBoss,
onTap: () => context.push('/settings/report'),
),
_SettingItem(
icon: Icons.extension_outlined,
iconColor: const Color(0xFF6366F1),
title: l10n.appMarket,
subtitle: l10n.marketDesc,
onTap: () => context.push('/settings/app-market'),
),
]),
const SizedBox(height: 24),
_buildSectionTitle(l10n.system),
const SizedBox(height: 12),
_buildSettingsCard([
_SettingItem(
icon: Icons.help_outline,
iconColor: const Color(0xFF64748B),
title: l10n.helpCenter,
subtitle: l10n.helpDesc,
onTap: () => context.push('/settings/help'),
),
_SettingItem(
icon: Icons.info_outline,
iconColor: const Color(0xFF64748B),
title: l10n.aboutUs,
subtitle: '${l10n.version} 1.0.0',
onTap: () => context.push('/settings/about'),
),
]),
const SizedBox(height: 32),
SizedBox(
width: double.infinity,
height: 52,
child: ElevatedButton(
onPressed: () {
ref.read(authProvider.notifier).logout();
context.go('/login');
},
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.error.withValues(alpha: 0.1),
foregroundColor: AppColors.error,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
),
child: Text(
l10n.logout,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
),
),
const SizedBox(height: 32),
],
),
);
}
Widget _buildProfileCard(user, AppLocalizations l10n) {
return Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
gradient: AppGradients.primary,
borderRadius: BorderRadius.circular(20),
),
child: Row(
children: [
Container(
width: 64,
height: 64,
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.2),
shape: BoxShape.circle,
),
child: Center(
child: Text(
user?.name.substring(0, 1) ?? l10n.user,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
user?.name ?? l10n.user,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 4),
Text(
user?.phone ?? '',
style: TextStyle(
fontSize: 14,
color: Colors.white.withValues(alpha: 0.8),
),
),
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(20),
),
child: Text(
user?.isBoss == true ? l10n.boss : l10n.employee,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
),
],
),
),
Icon(
Icons.chevron_right,
color: Colors.white.withValues(alpha: 0.6),
),
],
),
);
}
Widget _buildSectionTitle(String title) {
return Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: AppColors.textSecondary,
),
);
}
Widget _buildSettingsCard(List<_SettingItem> items) {
return Container(
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.03),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
children: items.asMap().entries.map((entry) {
final item = entry.value;
final isLast = entry.key == items.length - 1;
return Column(
children: [
ListTile(
leading: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: item.iconColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(10),
),
child: Icon(item.icon, color: item.iconColor, size: 20),
),
title: Row(
children: [
Text(
item.title,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: AppColors.textPrimary,
),
),
if (item.showBadge) ...[
const SizedBox(width: 8),
Container(
width: 8,
height: 8,
decoration: const BoxDecoration(
color: AppColors.error,
shape: BoxShape.circle,
),
),
],
],
),
subtitle: Text(
item.subtitle,
style: const TextStyle(
fontSize: 13,
color: AppColors.textTertiary,
),
),
trailing: item.trailing ?? const Icon(Icons.chevron_right, color: AppColors.textTertiary, size: 20),
onTap: item.onTap,
),
if (!isLast)
const Divider(height: 1, indent: 72),
],
);
}).toList(),
),
);
}
}
class _SettingItem {
final IconData icon;
final Color iconColor;
final String title;
final String subtitle;
final VoidCallback onTap;
final bool showBadge;
final Widget? trailing;
const _SettingItem({
required this.icon,
required this.iconColor,
required this.title,
required this.subtitle,
required this.onTap,
this.showBadge = false,
this.trailing,
});
}