import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:go_router/go_router.dart'; import 'package:new_learn/core/api/auth/api.dart'; import 'package:new_learn/core/components/app_update_dialog.dart'; import 'package:new_learn/core/components/confirm_box.dart'; import 'package:new_learn/core/network/dio_client.dart'; import 'package:new_learn/core/providers/user_provider.dart'; import 'package:new_learn/core/storage/user_storage.dart'; import 'package:new_learn/core/version/app_version_service.dart'; /// ============================================================ /// 【SettingsPage】设置页 —— 对齐 Vue pages/myPackage/settings/index.vue /// /// 三栏菜单: /// 检查更新(右侧显示当前版本号,手动触发版本比对弹窗) /// 关于APP (TODO: 跳转关于页) /// 注销账号 (Vue 端受 isAuditMode 控制,Flutter 暂先显示) /// /// 样式:白色圆角卡片 + 分隔线,和 my_page 的 _MenuCard 风格一致 /// ============================================================ class SettingsPage extends ConsumerStatefulWidget { const SettingsPage({super.key}); @override ConsumerState createState() => _SettingsPageState(); } class _SettingsPageState extends ConsumerState { String _version = ''; @override void initState() { super.initState(); _loadVersion(); } Future _loadVersion() async { final service = AppVersionService.instance; if (service.versionName.isEmpty) { await service.init(); } if (mounted) { setState(() => _version = service.versionName); } } Future _handleCheckUpdate() async { // 手动检查更新:复用 AppVersionService + AppUpdateDialog try { final service = AppVersionService.instance; await service.init(); final update = await service.checkForUpdate(); if (!mounted) return; if (update == null) { ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('已是最新版本'))); return; } await AppUpdateDialog.show( context, update, isForce: service.isForceUpdate(), ); } catch (e) { if (!mounted) return; ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('检查更新失败:$e'))); } } Future _handleCloseAccount() async { final ok = await showConfirmBox( context, title: '注销提醒', content: '账号注销后,将会删除账号的全部数据,不可恢复。请确认是否注销账号?', confirmText: '确定注销', cancelText: '我再想想', ); if (!ok) return; try { await UserApi.closeAccount(); } catch (e) { if (!mounted) return; ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('注销失败:$e'))); return; } // 清理本地登录态 → 路由守卫自动踢回登录页 DioClient.instance.accessToken = null; await UserStorage.instance.clearAll(); if (!mounted) return; ref.read(currentUserProvider.notifier).set(null); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF5F8FA), appBar: AppBar( title: const Text('设置'), backgroundColor: Colors.white, surfaceTintColor: Colors.transparent, leading: IconButton( icon: const Icon(Icons.arrow_back), onPressed: () => context.canPop() ? context.pop() : context.go('/my'), ), ), body: ListView( padding: EdgeInsets.all(12.w), children: [ // 检查更新 _buildRow( title: '检查更新', trailing: Row( mainAxisSize: MainAxisSize.min, children: [ if (_version.isNotEmpty) Text( 'v$_version', style: TextStyle( fontSize: 12.sp, color: const Color(0xFF687FAD), ), ), SizedBox(width: 4.w), const Icon( Icons.chevron_right, size: 22, color: Color(0xFF7FB0F2), ), ], ), onTap: _handleCheckUpdate, ), _buildDivider(), // 关于APP _buildRow( title: '关于APP', trailing: const Icon( Icons.chevron_right, size: 22, color: Color(0xFF7FB0F2), ), onTap: () => context.push('/about-app'), ), _buildDivider(), // 注销账号 _buildRow( title: '注销账号', trailing: const Icon( Icons.chevron_right, size: 22, color: Color(0xFF7FB0F2), ), onTap: _handleCloseAccount, ), ], ), ); } Widget _buildDivider() { return Padding( padding: EdgeInsets.symmetric(horizontal: 16.w), child: Container(height: 1, color: const Color(0xFFEDF3F7)), ); } Widget _buildRow({ required String title, required Widget trailing, required VoidCallback onTap, }) { return InkWell( onTap: onTap, child: Container( height: 52.h, padding: EdgeInsets.symmetric(horizontal: 16.w), color: Colors.white, child: Row( children: [ Text( title, style: TextStyle(fontSize: 16.sp, color: const Color(0xFF1D2B47)), ), const Spacer(), trailing, ], ), ), ); } }