QinGang_interested/lib/pages/mine/mine_page.dart

498 lines
15 KiB
Dart
Raw Normal View History

2026-03-12 10:23:21 +08:00
// file: mine_page.dart
2025-12-12 09:11:30 +08:00
import 'dart:convert';
2026-03-12 10:23:21 +08:00
import 'package:flutter/foundation.dart';
2025-12-12 09:11:30 +08:00
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
import 'package:qhd_prevention/customWidget/custom_button.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
2025-12-24 16:07:53 +08:00
import 'package:qhd_prevention/http/ApiService.dart';
2025-12-12 09:11:30 +08:00
import 'package:qhd_prevention/pages/home/scan_page.dart';
import 'package:qhd_prevention/pages/home/userinfo_page.dart';
import 'package:qhd_prevention/pages/mine/face_ecognition_page.dart';
2025-12-24 16:07:53 +08:00
import 'package:qhd_prevention/pages/mine/mine_change_firm_page.dart';
2025-12-12 09:11:30 +08:00
import 'package:qhd_prevention/pages/mine/mine_feedback_page.dart';
import 'package:qhd_prevention/pages/mine/mine_set_pwd_page.dart';
import 'package:qhd_prevention/pages/mine/onboarding_full_page.dart';
import 'package:qhd_prevention/pages/user/full_userinfo_page.dart';
import 'package:qhd_prevention/pages/user/login_page.dart';
import 'package:qhd_prevention/services/SessionService.dart';
import 'package:qhd_prevention/tools/tools.dart';
import 'package:shared_preferences/shared_preferences.dart';
2026-02-28 14:38:07 +08:00
import 'certificate/certificate_list_page.dart';
2026-03-12 10:23:21 +08:00
import 'package:qhd_prevention/common/route_service.dart';
import 'package:qhd_prevention/common/route_model.dart';
2026-02-28 14:38:07 +08:00
2025-12-12 09:11:30 +08:00
class MinePage extends StatefulWidget {
2026-01-14 09:55:23 +08:00
const MinePage({super.key, required this.isChooseFirm});
2026-03-12 10:23:21 +08:00
final bool isChooseFirm;
2025-12-12 09:11:30 +08:00
@override
State<MinePage> createState() => MinePageState();
}
class MinePageState extends State<MinePage> {
// 设置项状态
bool notificationsEnabled = false;
bool passwordChanged = false;
bool updateAvailable = false;
bool logoutSelected = false;
bool faceAuthentication = false;
bool scanAuthentication = false;
String name = '登录/注册';
String phone = '';
2026-03-12 10:23:21 +08:00
/// 配置UI 顺序/图标/对应后端 menuPerm(s)
/// perm 列表为空表示不受后端配置控制(始终显示)
final List<Map<String, dynamic>> _allSettings = [
{
'title': '我的信息',
'icon': 'assets/images/ico9.png',
'perms': ['my-center-My-Information'],
'action': 'userinfo',
},
{
'title': '修改密码',
'icon': 'assets/images/ico16.png',
'perms': ['my-center-Change-Password'],
'action': 'changePwd',
},
{
'title': '扫码入职',
'icon': 'assets/images/ico10.png',
// 如果你希望扫码入职也受后端控制,可以保留下面 perm否则置空 []
'perms': ['dashboard-scan'],
'action': 'scanOnboarding',
},
{
'title': '人脸认证',
'icon': 'assets/images/ico11.png',
'perms': ['my-center-Face-Authentication'],
'action': 'face',
},
{
'title': '证书信息',
'icon': 'assets/images/ico12.png',
'perms': ['my-center-Certificate-Information'],
'action': 'certificate',
},
// {
// 'title': '版本更新',
// 'icon': 'assets/images/ico14.png',
// 'perms': ['my-center-Version-Update'],
// 'action': 'version',
// },
// {
// 'title': '关于我们',
// 'icon': 'assets/images/ico15.png',
// 'perms': ['my-center-About-Us'],
// 'action': 'about',
// },
{
'title': '切换账户',
'icon': 'assets/images/ico_switch.png',
// 切换账户仍只在 widget.isChooseFirm 为 true 时显示(参见构建函数)
'perms': ['my-center-Switch-Account'],
'action': 'changeAccount',
},
{
'title': '账户注销',
'icon': 'assets/images/ico_quit.png',
// 后端可能有多种命名,两者任一存在即显示
'perms': ['my-center-User-Logout', 'my-center-Logout'],
'action': 'logout',
},
];
// 当前可见项(与 _allSettings 顺序对应)
late List<bool> _visible;
2025-12-12 09:11:30 +08:00
void onRouteConfigLoaded() {
if (mounted) {
setState(() {
2026-03-12 10:23:21 +08:00
// 这里不直接修改业务逻辑仅触发重建update 会在 listener 中执行)
2025-12-12 09:11:30 +08:00
});
}
}
@override
void initState() {
super.initState();
2026-02-28 14:38:07 +08:00
_getUserInfo();
2026-03-12 10:23:21 +08:00
// 初始默认都显示,避免短时白屏/闪烁
_visible = List<bool>.filled(_allSettings.length, true);
// 监听路由变化来决定哪些设置项应显示
RouteService().addListener(_onRouteServiceUpdated);
// 尝试立即应用(如果 route 已加载)
WidgetsBinding.instance.addPostFrameCallback((_) => _updateVisibilityFromRoute());
}
@override
void dispose() {
try {
RouteService().removeListener(_onRouteServiceUpdated);
} catch (_) {}
super.dispose();
}
void _onRouteServiceUpdated() {
_updateVisibilityFromRoute();
}
/// 根据 RouteService 的路由配置计算每个条目的可见性
void _updateVisibilityFromRoute() {
final rs = RouteService();
// 如果 mainTabs 为空,说明路由还没加载;保持当前 visible避免闪烁
if (rs.mainTabs.isEmpty) {
return;
}
final List<bool> next = List<bool>.filled(_allSettings.length, false);
for (int i = 0; i < _allSettings.length; i++) {
final perms = (_allSettings[i]['perms'] ?? []) as List<String>;
if (perms.isEmpty) {
// 无后端控制的项默认显示(比如你希望用户协议/隐私总是能见到)
next[i] = true;
continue;
}
// 只要找到任一 perm 在路由树中可见,即认为该项可见
bool anyFound = false;
for (final p in perms) {
final node = rs.findRouteByPerm(p);
if (node != null) {
// rs.findRouteByPerm 已经会考虑父节点 visible 的情况(按照 RouteService 实现)
// 这里再用 showFlag==1 做额外判断:当后端返回节点但 showFlag==0 (不显示) 则视为不可见
if (node.showFlag == 1 || node.visible) {
anyFound = true;
break;
}
}
}
next[i] = anyFound;
}
// 与当前 _visible 比较,只有变更时才 setState
if (!listEquals(next, _visible)) {
setState(() {
_visible = next;
});
}
2026-02-28 14:38:07 +08:00
}
2026-03-12 10:23:21 +08:00
// 获取用户信息(保持原逻辑)
2026-02-28 14:38:07 +08:00
Future<void> _getUserInfo() async {
final res = await BasicInfoApi.getUserMessage(
'${SessionService.instance.accountId}',
);
if (res['success'] == true) {
final data = res['data'] as Map<String, dynamic>;
SessionService.instance.updateFromApiResponse(data);
await SessionService.instance.saveToPrefs();
setState(() {
name = SessionService.instance.userData?.name ?? "登录/注册";
phone = SessionService.instance.userData?.phone ?? "";
});
}
}
2025-12-12 09:11:30 +08:00
2025-12-24 16:07:53 +08:00
Future<void> _logout() async {
LoadingDialogHelper.show();
/// 获取用户在职列表
final firmData = await BasicInfoApi.getJoinFirmList();
if (firmData['success'] == true) {
final firmList = firmData['data'];
LoadingDialogHelper.dismiss();
if (firmList.isNotEmpty) {
CustomAlertDialog.showAlert(
context,
title: '温馨提示',
content: '您目前还有入职信息无法直接注销。\n请先在“就职单位”页面中离职。',
);
} else {
2026-01-14 09:55:23 +08:00
final result = await CustomAlertDialog.showConfirm(
2025-12-24 16:07:53 +08:00
context,
title: '温馨提示',
content: '注销后您的所有信息将会被删除\n请确认是否注销。 ',
2026-03-12 10:23:21 +08:00
onConfirm: () {},
2026-01-14 09:55:23 +08:00
);
if (result) {
CustomAlertDialog.showInputWithCode(
2025-12-24 16:07:53 +08:00
context,
title: '手机号:${SessionService.instance.phone}',
onGetCode: () async {
LoadingDialogHelper.show();
final res = await BasicInfoApi.sendRegisterSms({
'phone': phone,
});
LoadingDialogHelper.dismiss();
return true;
},
onConfirm: (code) async {
2026-01-14 09:55:23 +08:00
_quit(code);
2026-03-12 10:23:21 +08:00
});
2026-01-14 09:55:23 +08:00
}
2025-12-24 16:07:53 +08:00
}
} else {
LoadingDialogHelper.dismiss();
ToastUtil.showNormal(context, firmData['errMessage'] ?? '');
}
}
2026-01-14 09:55:23 +08:00
// 离职
Future<void> _quit(String code) async {
LoadingDialogHelper.show();
Map data = {
2026-03-12 10:23:21 +08:00
'id': SessionService.instance.accountId,
'phoneCode': code,
2026-01-14 09:55:23 +08:00
};
await BasicInfoApi.logout(data).then((res) async {
LoadingDialogHelper.dismiss();
if (res['success'] == true) {
ToastUtil.showNormal(context, '账号已注销');
await SessionService.instance.clear(clearPrefs: true);
Navigator.pushReplacement(
context,
2026-03-12 10:23:21 +08:00
MaterialPageRoute(builder: (_) => const LoginPage()),
2026-01-14 09:55:23 +08:00
);
} else {
ToastUtil.showNormal(context, res['errMessage'] ?? '');
}
});
}
2025-12-12 09:11:30 +08:00
Widget _buildSloganSection() {
2026-02-28 14:38:07 +08:00
final headerUrl = SessionService.instance.userData?.userAvatarUrl ?? '';
2025-12-12 09:11:30 +08:00
return Container(
2026-03-12 10:23:21 +08:00
margin: const EdgeInsets.fromLTRB(0, 100, 0, 0),
2025-12-12 09:11:30 +08:00
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 20),
child: Row(
2026-03-12 10:23:21 +08:00
mainAxisAlignment: MainAxisAlignment.spaceBetween,
2025-12-12 09:11:30 +08:00
children: [
Row(
children: [
2026-03-12 10:23:21 +08:00
const CircleAvatar(
backgroundImage: AssetImage("assets/images/yingyong11.png"),
radius: 30,
),
2025-12-12 09:11:30 +08:00
const SizedBox(width: 16),
Text(
name,
2026-03-12 10:23:21 +08:00
style: const TextStyle(
2025-12-12 09:11:30 +08:00
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
],
),
const SizedBox(width: 16),
],
),
);
}
2025-12-24 16:07:53 +08:00
2025-12-12 09:11:30 +08:00
Widget _buildSettingItem({
required String title,
required String icon,
required bool value,
required ValueChanged<bool?> onChanged,
}) {
return GestureDetector(
onTap: () async {
2025-12-24 16:07:53 +08:00
onChanged(value);
2025-12-12 09:11:30 +08:00
},
child: ListTile(
leading: Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
),
child: Image.asset(icon, fit: BoxFit.cover),
),
title: Text(
title,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
),
trailing: Transform.scale(
scale: 1.2,
2026-03-12 10:23:21 +08:00
child: const Icon(Icons.chevron_right),
2025-12-12 09:11:30 +08:00
),
),
);
}
Future<void> _clearUserSession() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('isLoggedIn'); // 清除登录状态
}
2026-03-12 10:23:21 +08:00
void _onSettingTapAction(String action) async {
switch (action) {
case 'userinfo':
await pushPage(FullUserinfoPage(isEidt: false, isChooseFirm: true), context);
break;
case 'changePwd':
await pushPage(MineSetPwdPage('0'), context);
break;
case 'scanOnboarding':
final result = await pushPage(ScanPage(type: ScanType.Onboarding), context);
if (result == null) return;
pushPage(OnboardingFullPage(scanData: result), context);
break;
case 'face':
pushPage(const FaceRecognitionPage(studentId: '', data: {}, mode: FaceMode.setUpdata), context);
break;
case 'certificate':
pushPage(const CertificateListPage(), context);
break;
case 'version':
// 版本检查逻辑(占位)
ToastUtil.showNormal(context, '功能开发中...');
break;
case 'about':
// 关于我们(占位)
ToastUtil.showNormal(context, '功能开发中...');
break;
case 'changeAccount':
pushPage(MineChangeFirmPage(), context);
break;
case 'logout':
_logout();
break;
default:
break;
}
}
Widget _buildSettingsList() {
final children = <Widget>[];
for (int i = 0; i < _allSettings.length; i++) {
// 保证索引范围并且按照 _visible 判定
if (i >= _visible.length) continue;
if (!_visible[i]) continue;
final item = _allSettings[i];
final title = item['title'] as String;
2026-03-13 15:19:09 +08:00
// 只在 isChooseFirm 为 true 时显示
2026-03-12 10:23:21 +08:00
if (title == '切换账户' && !widget.isChooseFirm) continue;
children.add(_buildSettingItem(
title: title,
icon: item['icon'] as String,
value: false,
onChanged: (_) => _onSettingTapAction(item['action'] as String),
));
}
return Container(
margin: const EdgeInsets.fromLTRB(20, 0, 20, 0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 2,
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Column(
children: children,
),
);
}
@override
Widget build(BuildContext context) {
final double headerHeight = 300.0;
final double overlap = 100.0;
return SizedBox(
height: MediaQuery.of(context).size.height,
child: Stack(
children: [
Positioned(top: 0, left: 0, right: 0, height: headerHeight, child: _buildHeaderSection()),
Positioned.fill(
child: NotificationListener<OverscrollIndicatorNotification>(
onNotification: (overscroll) {
overscroll.disallowIndicator();
return false;
},
child: ListView(
padding: EdgeInsets.only(top: headerHeight - overlap, bottom: 24, left: 0, right: 0),
children: [
_buildSettingsList(),
const SizedBox(height: 15),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: CustomButton(
text: '退出登录',
textStyle: const TextStyle(fontSize: 16),
backgroundColor: Colors.blue,
onPressed: () {
CustomAlertDialog.showConfirm(
context,
title: '确认退出',
content: '确定要退出当前账号吗',
onConfirm: () async {
await _clearUserSession();
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => const LoginPage()),
(Route<dynamic> route) => false,
);
},
);
},
),
),
],
),
),
),
],
),
);
}
Widget _buildHeaderSection() {
return Stack(
alignment: const FractionalOffset(0.5, 0),
children: [
Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 10),
child: Image.asset(
"assets/images/my_bg.png",
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover,
),
),
const Positioned(
top: 51,
child: Text(
"我的",
style: TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold),
),
),
_buildSloganSection(),
],
);
}
}