gcx/frontend/admin-app/lib/features/settings/presentation/pages/settings_page.dart

323 lines
11 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:package_info_plus/package_info_plus.dart';
import '../../../../app/theme/app_colors.dart';
import '../../../../app/router.dart';
import '../../../../app/i18n/app_localizations.dart';
import '../../../../core/updater/update_service.dart';
import '../../../../core/services/issuer_service.dart';
import '../../../../core/services/auth_service.dart';
import '../../../../core/network/api_client.dart';
/// 发行方设置页面(我的)
///
/// 企业信息、门店管理、员工管理、专属客服、安全设置
class SettingsPage extends StatefulWidget {
const SettingsPage({super.key});
@override
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
String _appVersion = '';
bool _isLoadingProfile = true;
IssuerProfile? _profile;
bool _isLoggingOut = false;
final _issuerService = IssuerService();
final _authService = AuthService();
@override
void initState() {
super.initState();
_loadVersion();
_loadProfile();
}
Future<void> _loadVersion() async {
final info = await PackageInfo.fromPlatform();
if (mounted) {
setState(() => _appVersion = 'v${info.version}+${info.buildNumber}');
}
}
Future<void> _loadProfile() async {
try {
final profile = await _issuerService.getProfile();
if (!mounted) return;
setState(() {
_profile = profile;
_isLoadingProfile = false;
});
} catch (e) {
debugPrint('[SettingsPage] loadProfile error: $e');
if (!mounted) return;
setState(() => _isLoadingProfile = false);
}
}
Future<void> _handleLogout() async {
final confirmed = await showDialog<bool>(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('确认退出'),
content: const Text('确定要退出登录吗?'),
actions: [
TextButton(onPressed: () => Navigator.pop(ctx, false), child: Text(context.t('cancel'))),
ElevatedButton(
onPressed: () => Navigator.pop(ctx, true),
style: ElevatedButton.styleFrom(backgroundColor: AppColors.error),
child: Text(context.t('confirm')),
),
],
),
);
if (confirmed != true) return;
setState(() => _isLoggingOut = true);
try {
await _authService.logout();
} catch (e) {
debugPrint('[SettingsPage] logout error: $e');
}
ApiClient.instance.setToken(null);
if (!mounted) return;
Navigator.pushNamedAndRemoveUntil(context, AppRouter.login, (_) => false);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(context.t('settings_title'))),
body: SingleChildScrollView(
child: Column(
children: [
// Profile Card
_buildProfileCard(context),
// Tier & Benefits
_buildTierCard(context),
const SizedBox(height: 8),
// Menu Groups
_buildMenuGroup(context.t('settings_group_company'), [
_MenuItem(context.t('settings_company_info'), Icons.business_rounded, () {
// TODO: Navigate to company info page when available
}),
_MenuItem(context.t('settings_store_mgmt'), Icons.store_rounded, () {
Navigator.pushNamed(context, AppRouter.storeManagement);
}),
_MenuItem(context.t('settings_employee_mgmt'), Icons.people_rounded, () {
Navigator.pushNamed(context, AppRouter.storeManagement);
}),
_MenuItem(context.t('settings_permissions'), Icons.admin_panel_settings_rounded, () {
// TODO: Navigate to permissions page when available
}),
]),
_buildMenuGroup(context.t('settings_group_support'), [
_MenuItem(context.t('settings_ai_assistant'), Icons.auto_awesome_rounded, () {
Navigator.pushNamed(context, AppRouter.aiAgent);
}),
_MenuItem(context.t('settings_customer_service'), Icons.headset_mic_rounded, () {
// TODO: Navigate to customer service page when available
}),
_MenuItem(context.t('settings_help_center'), Icons.help_outline_rounded, () {
// TODO: Navigate to help center page when available
}),
_MenuItem(context.t('settings_feedback'), Icons.feedback_rounded, () {
// TODO: Navigate to feedback page when available
}),
]),
_buildMenuGroup(context.t('settings_group_security'), [
_MenuItem(context.t('settings_change_password'), Icons.lock_outline_rounded, () {
// TODO: Navigate to change password page when available
}),
_MenuItem(context.t('settings_operation_log'), Icons.history_rounded, () {
// TODO: Navigate to operation log page when available
}),
_MenuItem(
'${context.t('settings_about')}${_appVersion.isNotEmpty ? ' $_appVersion' : ''}',
Icons.info_outline_rounded,
() => UpdateService().manualCheckUpdate(context),
),
]),
// Logout
Padding(
padding: const EdgeInsets.all(20),
child: SizedBox(
width: double.infinity,
child: OutlinedButton(
onPressed: _isLoggingOut ? null : _handleLogout,
style: OutlinedButton.styleFrom(
foregroundColor: AppColors.error,
side: const BorderSide(color: AppColors.error),
),
child: _isLoggingOut
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: Text(context.t('settings_logout')),
),
),
),
],
),
),
);
}
Widget _buildProfileCard(BuildContext context) {
return Container(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 16),
color: AppColors.surface,
child: Row(
children: [
_profile?.logoUrl != null
? ClipRRect(
borderRadius: BorderRadius.circular(14),
child: Image.network(
_profile!.logoUrl!,
width: 56,
height: 56,
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => ClipRRect(
borderRadius: BorderRadius.circular(14),
child: Image.asset('assets/images/logo_icon.png', width: 56, height: 56),
),
),
)
: ClipRRect(
borderRadius: BorderRadius.circular(14),
child: Image.asset(
'assets/images/logo_icon.png',
width: 56,
height: 56,
),
),
const SizedBox(width: 14),
Expanded(
child: _isLoadingProfile
? const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 120,
height: 16,
child: LinearProgressIndicator(),
),
SizedBox(height: 8),
SizedBox(
width: 80,
height: 12,
child: LinearProgressIndicator(),
),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_profile?.companyName ?? '--',
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w700),
),
const SizedBox(height: 4),
Text(
_profile?.contactEmail != null
? '${context.t('settings_admin')}${_profile!.contactEmail}'
: _profile?.contactPhone != null
? '${context.t('settings_admin')}${_profile!.contactPhone}'
: context.t('settings_admin'),
style: const TextStyle(fontSize: 13, color: AppColors.textSecondary),
),
],
),
),
const Icon(Icons.chevron_right_rounded, color: AppColors.textTertiary),
],
),
);
}
Widget _buildTierCard(BuildContext context) {
return Container(
margin: const EdgeInsets.fromLTRB(20, 12, 20, 0),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFFFFF7E6), Color(0xFFFFECC7)],
),
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
const Icon(Icons.star_rounded, color: AppColors.tierGold, size: 28),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_profile?.creditRating != null
? '${_profile!.creditRating} ${context.t('settings_gold_issuer')}'
: context.t('settings_gold_issuer'),
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w700, color: AppColors.tierGold),
),
const Text('手续费率 1.2% · 高级数据分析', style: TextStyle(fontSize: 12, color: AppColors.textSecondary)),
],
),
),
TextButton(
onPressed: () {
Navigator.pushNamed(context, AppRouter.credit);
},
child: Text(context.t('settings_upgrade'), style: const TextStyle(color: AppColors.tierGold)),
),
],
),
);
}
Widget _buildMenuGroup(String title, List<_MenuItem> items) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 8),
child: Text(title, style: const TextStyle(fontSize: 13, color: AppColors.textTertiary, fontWeight: FontWeight.w500)),
),
Container(
color: AppColors.surface,
child: Column(
children: items.map((item) {
return ListTile(
leading: Icon(item.icon, color: AppColors.textSecondary, size: 22),
title: Text(item.title, style: const TextStyle(fontSize: 15)),
trailing: const Icon(Icons.chevron_right_rounded, size: 20, color: AppColors.textTertiary),
onTap: item.onTap,
);
}).toList(),
),
),
],
);
}
}
class _MenuItem {
final String title;
final IconData icon;
final VoidCallback onTap;
const _MenuItem(this.title, this.icon, this.onTap);
}