329 lines
12 KiB
Dart
329 lines
12 KiB
Dart
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';
|
||
import '../../../../core/telemetry/telemetry_service.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: Text(context.t('settings_confirm_logout')),
|
||
content: Text(context.t('settings_confirm_logout_desc')),
|
||
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');
|
||
}
|
||
|
||
// 遥测:退出登录时清除 userId 和 token
|
||
if (TelemetryService().isInitialized) {
|
||
TelemetryService().clearUserId();
|
||
TelemetryService().clearAccessToken();
|
||
}
|
||
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),
|
||
),
|
||
Text(context.t('settings_tier_benefits'), style: const 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);
|
||
}
|