合并代码

This commit is contained in:
chenshuainan
2026-09-09 16:15:05 +08:00
parent 592e576319
commit 7acf745295
24 changed files with 1864 additions and 0 deletions
@@ -0,0 +1,136 @@
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/version/app_version_service.dart';
/// ============================================================
/// 【AboutAppPage】关于APP页 —— 对齐 Vue pages/myPackage/aboutApp/index.vue
///
/// 菜单:隐私政策 / 用户协议 / 版本号(只读)
/// 底部:APP备案号
/// ============================================================
class AboutAppPage extends ConsumerStatefulWidget {
const AboutAppPage({super.key});
@override
ConsumerState<AboutAppPage> createState() => _AboutAppPageState();
}
class _AboutAppPageState extends ConsumerState<AboutAppPage> {
String _version = '';
static const _recordNumber = '京ICP备2026019420号-2A';
@override
void initState() {
super.initState();
_loadVersion();
}
Future<void> _loadVersion() async {
final service = AppVersionService.instance;
if (service.versionName.isEmpty) {
await service.init();
}
if (mounted) setState(() => _version = service.versionName);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF5F8FA),
appBar: AppBar(
title: const Text('关于APP'),
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: const Icon(
Icons.chevron_right,
size: 22,
color: Color(0xFF7FB0F2),
),
onTap: () => context.push('/privacy-policy'),
showBorder: true,
),
// 用户协议
_buildRow(
title: '用户协议',
trailing: const Icon(
Icons.chevron_right,
size: 22,
color: Color(0xFF7FB0F2),
),
onTap: () => context.push('/user-agreement'),
showBorder: true,
),
// 版本号(只读,无跳转)
_buildRow(
title: '版本号',
trailing: Text(
_version,
style: TextStyle(fontSize: 14.sp, color: const Color(0xFF687FAD)),
),
onTap: null,
showBorder: false,
),
SizedBox(height: 48.h),
// 备案号
Center(
child: Text(
'APP备案编号:$_recordNumber',
style: TextStyle(fontSize: 12.sp, color: const Color(0xFF687FAD)),
),
),
],
),
);
}
Widget _buildRow({
required String title,
required Widget trailing,
required VoidCallback? onTap,
required bool showBorder,
}) {
return InkWell(
onTap: onTap,
child: Column(
children: [
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,
],
),
),
if (showBorder)
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Container(height: 1, color: const Color(0xFFEDF3F7)),
),
],
),
);
}
}
+198
View File
@@ -0,0 +1,198 @@
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<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends ConsumerState<SettingsPage> {
String _version = '';
@override
void initState() {
super.initState();
_loadVersion();
}
Future<void> _loadVersion() async {
final service = AppVersionService.instance;
if (service.versionName.isEmpty) {
await service.init();
}
if (mounted) {
setState(() => _version = service.versionName);
}
}
Future<void> _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<void> _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,
],
),
),
);
}
}