忘记密码

This commit is contained in:
2025-11-18 17:08:46 +08:00
parent 959fe89bed
commit 6a78d186d0
2 changed files with 84 additions and 82 deletions

View File

@@ -18,25 +18,26 @@ class LoginPage extends StatefulWidget {
class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMixin { class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMixin {
late TabController tabController; late TabController tabController;
//默认司机端
bool cLogin = true; bool cLogin = true;
bool _obscureText = true; // 默认密码为隐藏状态 bool _obscureText = true;
// 用于管理“记住密码”的复选框状态
bool _rememberPassword = true;
// 用于确保凭证只在首次加载时回填一次
bool _credentialsLoaded = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// 初始化 TabController传入 vsync 参数
tabController = TabController(length: 2, vsync: this); tabController = TabController(length: 2, vsync: this);
tabController.addListener(_tabChangeListener); tabController.addListener(_tabChangeListener);
} }
void _tabChangeListener() { void _tabChangeListener() {
// 如果 TabController 不是正在被用户手动滑动(即是初始化或其他操作)
if (!tabController.indexIsChanging) { if (!tabController.indexIsChanging) {
switchTab(tabController.index); switchTab(tabController.index);
} }
} }
// 切换Tab
void switchTab(int index) { void switchTab(int index) {
setState(() { setState(() {
cLogin = (index == 0); cLogin = (index == 0);
@@ -45,14 +46,22 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
@override @override
void dispose() { void dispose() {
tabController.dispose(); // 销毁 TabController tabController.dispose();
super.dispose(); super.dispose();
} }
// 主视图 Widget _buildView(LoginController controller) {
Widget _buildView() { // 在视图构建时,检查并回填已保存的凭证
// 使用 Get.find 获取控制器 if (!_credentialsLoaded) {
final controller = Get.find<LoginController>(); final savedAccount = StorageService.to.stationAccount;
final savedPassword = StorageService.to.stationPassword;
if (savedAccount != null && savedPassword != null) {
controller.stationIdController.text = savedAccount;
controller.passwordController.text = savedPassword;
_rememberPassword = true; // 如果有保存的密码,则默认勾选
}
_credentialsLoaded = true; // 标记为已加载,防止重复执行
}
return Container( return Container(
color: Color(0xFFEFF4F7), color: Color(0xFFEFF4F7),
@@ -69,10 +78,9 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
margin: EdgeInsets.all(15), margin: EdgeInsets.all(15),
elevation: 4, elevation: 4,
child: Container( child: Container(
height: cLogin ? 285.h : 350.h, height: cLogin ? 285.h : 360.h,
padding: EdgeInsets.all(15), padding: EdgeInsets.all(15),
child: // TabBar切换 child: Column(
Column(
children: [ children: [
Card( Card(
elevation: 2, elevation: 2,
@@ -82,19 +90,15 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
child: TabBar( child: TabBar(
controller: tabController, controller: tabController,
onTap: (index) { onTap: (index) {
//保证尺寸变化
delayed(300, () { delayed(300, () {
switchTab(index); switchTab(index);
}); });
}, },
// 修改TabBar的选中状态和未选中状态样式
labelColor: Colors.white, labelColor: Colors.white,
// 选中时的文字颜色
unselectedLabelColor: Colors.black, unselectedLabelColor: Colors.black,
// 未选中时的文字颜色
indicator: BoxDecoration( indicator: BoxDecoration(
color: AppTheme.themeColor, // 选中的Tab背景色模拟卡片式效果 color: AppTheme.themeColor,
borderRadius: BorderRadius.circular(12), // 卡片的圆角效果 borderRadius: BorderRadius.circular(12),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.blue.withOpacity(0.2), color: Colors.blue.withOpacity(0.2),
@@ -111,14 +115,11 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
), ),
), ),
), ),
// 根据选择的Tab展示不同的输入框
Flexible( Flexible(
child: TabBarView( child: TabBarView(
controller: tabController, controller: tabController,
children: [ children: [
// 司机端登录
_driverLoginView(controller), _driverLoginView(controller),
// 加氢站登录
_stationLoginView(controller), _stationLoginView(controller),
], ],
), ),
@@ -131,7 +132,6 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
); );
} }
// 司机端登录界面
Widget _driverLoginView(LoginController controller) { Widget _driverLoginView(LoginController controller) {
return !cLogin return !cLogin
? SizedBox() ? SizedBox()
@@ -158,44 +158,28 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
SizedBox(height: 20.h), SizedBox(height: 20.h),
ElevatedButton( ElevatedButton(
onPressed: () async { onPressed: () async {
// 司机端登录
String password = controller.driverIdentityController.text; String password = controller.driverIdentityController.text;
if (password.isEmpty) { if (password.isEmpty) {
showToast("请输入密码"); showToast("请输入密码");
return; return;
} }
showLoading('登录中...'); showLoading('登录中...');
try { try {
// 调用登录接口
var responseData = await HttpService.to.post( var responseData = await HttpService.to.post(
'appointment/login/loginForDriver', 'appointment/login/loginForDriver',
data: {'idNo': password}, data: {'idNo': password},
); );
if (responseData == null && responseData!.data == null) { if (responseData == null && responseData!.data == null) {
dismissLoading(); dismissLoading();
showToast('登录失败:无法获取凭证'); showToast('登录失败:无法获取凭证');
return; return;
} }
try { try {
var result = BaseModel.fromJson(responseData.data); var result = BaseModel.fromJson(responseData.data);
//保存用户信息
String token = result.data['token'] ?? ''; String token = result.data['token'] ?? '';
String idCard = result.data['idCard'] ?? ''; String idCard = result.data['idCard'] ?? '';
String name = result.data['name'] ?? ''; String name = result.data['name'] ?? '';
String phone = result.data['phone'] ?? ''; String phone = result.data['phone'] ?? '';
/*{
"token": "a615cf38a066473ebf8e9a956fc51928",
"idCard": "330324200010241024",
"name": "王小龙",
"phone": "15888331828"
}*/
await StorageService.to.saveLoginInfo( await StorageService.to.saveLoginInfo(
token: token, token: token,
userId: "", userId: "",
@@ -204,14 +188,10 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
name: name, name: name,
phone: phone, phone: phone,
); );
dismissLoading(); dismissLoading();
showToast('登录成功,欢迎您'); showToast('登录成功,欢迎您');
// 跳转到主页,并清除所有历史页面
Get.offAll(() => BaseWidgetsPage()); Get.offAll(() => BaseWidgetsPage());
} catch (e) { } catch (e) {
// 如果解析 JSON 失败
dismissLoading(); dismissLoading();
showToast('登录失败:数据异常'); showToast('登录失败:数据异常');
} }
@@ -221,7 +201,7 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: AppTheme.themeColor, backgroundColor: AppTheme.themeColor,
minimumSize: Size(double.infinity, 50), // 设置按钮宽度占满,指定最小高度 minimumSize: Size(double.infinity, 50),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
), ),
child: Text('登录'), child: Text('登录'),
@@ -230,7 +210,6 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
); );
} }
// 加氢站登录界面
Widget _stationLoginView(LoginController controller) { Widget _stationLoginView(LoginController controller) {
return cLogin return cLogin
? SizedBox() ? SizedBox()
@@ -253,7 +232,7 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
), ),
), ),
), ),
SizedBox(height: 20), SizedBox(height: 10),
TextFormField( TextFormField(
controller: controller.passwordController, controller: controller.passwordController,
obscureText: _obscureText, obscureText: _obscureText,
@@ -266,7 +245,7 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
border: OutlineInputBorder(), border: OutlineInputBorder(),
suffixIcon: IconButton( suffixIcon: IconButton(
icon: Icon( icon: Icon(
_obscureText ? Icons.visibility_off : Icons.visibility, // 切换图标 _obscureText ? Icons.visibility_off : Icons.visibility,
), ),
onPressed: () { onPressed: () {
setState(() { setState(() {
@@ -280,22 +259,40 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
), ),
), ),
), ),
SizedBox(height: 20), SizedBox(height: 10),
//记住密码复选框 ---
Row(
children: <Widget>[
SizedBox(
height: 24,
width: 24,
child: Checkbox(
value: _rememberPassword,
activeColor: AppTheme.themeColor,
onChanged: (bool? value) {
setState(() {
_rememberPassword = value ?? true;
});
},
),
),
GestureDetector(
onTap: () => setState(() => _rememberPassword = !_rememberPassword),
child: const Text('记住密码', style: TextStyle(color: Colors.grey)),
)
],
),
SizedBox(height: 20), // 调整间距
ElevatedButton( ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: AppTheme.themeColor, backgroundColor: AppTheme.themeColor,
minimumSize: Size(double.infinity, 50), // 设置按钮宽度占满,指定最小高度 minimumSize: Size(double.infinity, 50),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
), ),
onPressed: () async { onPressed: () async {
// 加氢站登录逻辑
String account = controller.stationIdController.text; String account = controller.stationIdController.text;
String password = controller.passwordController.text; String password = controller.passwordController.text;
//todo 删除
account = "000017";
password = "LnQn.314000";
if (account.isEmpty || password.isEmpty) { if (account.isEmpty || password.isEmpty) {
showToast("请输入账号和密码"); showToast("请输入账号和密码");
return; return;
@@ -304,10 +301,7 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
showLoading('登录中...'); showLoading('登录中...');
try { try {
// 对密码进行AES加密
String encryptedPassword = LoginUtil.encrypt(password); String encryptedPassword = LoginUtil.encrypt(password);
// 调用登录接口
var responseData = await HttpService.to.post( var responseData = await HttpService.to.post(
'/login/password', '/login/password',
data: { data: {
@@ -325,10 +319,7 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
try { try {
var result = BaseModel.fromJson(responseData.data); var result = BaseModel.fromJson(responseData.data);
//保存用户信息
String token = result.data['token'] ?? ''; String token = result.data['token'] ?? '';
//hydrogenId
String userId = result.data['userId'] ?? ''; String userId = result.data['userId'] ?? '';
await StorageService.to.saveLoginInfo( await StorageService.to.saveLoginInfo(
@@ -337,13 +328,17 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
channel: "station", channel: "station",
); );
// 根据复选框状态保存或清除密码 ---
if (_rememberPassword) {
await StorageService.to.saveStationCredentials(account, password);
} else {
await StorageService.to.clearStationCredentials();
}
dismissLoading(); dismissLoading();
showToast('登录成功,欢迎您'); showToast('登录成功,欢迎您');
// 跳转到主页,并清除所有历史页面
Get.offAll(() => B_BaseWidgetsPage()); Get.offAll(() => B_BaseWidgetsPage());
} catch (e) { } catch (e) {
// 如果解析 JSON 失败
dismissLoading(); dismissLoading();
showToast('登录失败:数据异常'); showToast('登录失败:数据异常');
} }
@@ -362,8 +357,8 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
return GetBuilder<LoginController>( return GetBuilder<LoginController>(
init: LoginController(), init: LoginController(),
id: 'login', id: 'login',
builder: (_) { builder: (controller) {
return Scaffold(body: _buildView()); return Scaffold(body: _buildView(controller));
}, },
); );
} }

View File

@@ -19,41 +19,40 @@ class StorageService extends GetxService {
static const String _nameKey = 'user_name'; static const String _nameKey = 'user_name';
static const String _phoneKey = 'user_phone'; static const String _phoneKey = 'user_phone';
static const String _idCardKey = 'user_id_card'; static const String _idCardKey = 'user_id_card';
// 车辆信息的键名
static const String _vehicleInfoKey = 'vehicle_info'; static const String _vehicleInfoKey = 'vehicle_info';
// 加氢站登录凭证的键名
static const String _stationAccountKey = 'station_account';
static const String _stationPasswordKey = 'station_password';
// 提供一个静态的 'to' 方法,方便全局访问
static StorageService get to => Get.find(); static StorageService get to => Get.find();
// Service 初始化
Future<StorageService> init() async { Future<StorageService> init() async {
_box = GetStorage(); _box = GetStorage();
return this; return this;
} }
/// --- Getter 方法,用于读取状态 --- // --- Getters ---
bool get isLoggedIn => _box.read<String?>(_tokenKey)?.isNotEmpty ?? false; bool get isLoggedIn => _box.read<String?>(_tokenKey)?.isNotEmpty ?? false;
String? get token => _box.read<String?>(_tokenKey); String? get token => _box.read<String?>(_tokenKey);
String? get userId => _box.read<String?>(_userIdKey); String? get userId => _box.read<String?>(_userIdKey);
String? get name => _box.read<String?>(_nameKey); String? get name => _box.read<String?>(_nameKey);
String? get phone => _box.read<String?>(_phoneKey); String? get phone => _box.read<String?>(_phoneKey);
String? get idCard => _box.read<String?>(_idCardKey); String? get idCard => _box.read<String?>(_idCardKey);
/// 判断是否已有车辆信息缓存
bool get hasVehicleInfo => _box.hasData(_vehicleInfoKey); bool get hasVehicleInfo => _box.hasData(_vehicleInfoKey);
/// 获取已缓存的车辆信息 // 记住密码:获取加氢站账号和密码
String? get stationAccount => _box.read<String?>(_stationAccountKey);
String? get stationPassword => _box.read<String?>(_stationPasswordKey);
VehicleInfo? get vehicleInfo { VehicleInfo? get vehicleInfo {
final vehicleJson = _box.read<String?>(_vehicleInfoKey); final vehicleJson = _box.read<String?>(_vehicleInfoKey);
if (vehicleJson != null) { if (vehicleJson != null) {
// 使用我们之前创建的辅助函数来解析
return vehicleInfoFromJson(vehicleJson); return vehicleInfoFromJson(vehicleJson);
} }
return null; return null;
} }
///获取当前登录渠道
LoginChannel get loginChannel { LoginChannel get loginChannel {
final channelString = _box.read<String?>(_channelKey); final channelString = _box.read<String?>(_channelKey);
if (channelString == 'station') { if (channelString == 'station') {
@@ -64,7 +63,7 @@ class StorageService extends GetxService {
return LoginChannel.none; return LoginChannel.none;
} }
/// --- 操作方法 --- // --- Methods ---
Future<void> saveLoginInfo({ Future<void> saveLoginInfo({
required String token, required String token,
required String userId, required String userId,
@@ -81,18 +80,26 @@ class StorageService extends GetxService {
await _box.write(_idCardKey, idCard); await _box.write(_idCardKey, idCard);
} }
/// 保存车辆信息
Future<void> saveVehicleInfo(VehicleInfo data) async { Future<void> saveVehicleInfo(VehicleInfo data) async {
// 使用辅助函数将对象转换为 JSON 字符串并保存
await _box.write(_vehicleInfoKey, vehicleInfoToJson(data)); await _box.write(_vehicleInfoKey, vehicleInfoToJson(data));
} }
/// 清除车辆信息 // 保存加氢站登录凭证
Future<void> saveStationCredentials(String account, String password) async {
await _box.write(_stationAccountKey, account);
await _box.write(_stationPasswordKey, password);
}
Future<void> clearVehicleInfo() async { Future<void> clearVehicleInfo() async {
await _box.remove(_vehicleInfoKey); await _box.remove(_vehicleInfoKey);
} }
/// 删除用户信息 (登出) // 清除加氢站登录凭证
Future<void> clearStationCredentials() async {
await _box.remove(_stationAccountKey);
await _box.remove(_stationPasswordKey);
}
Future<void> clearLoginInfo() async { Future<void> clearLoginInfo() async {
await _box.remove(_tokenKey); await _box.remove(_tokenKey);
await _box.remove(_userIdKey); await _box.remove(_userIdKey);
@@ -100,7 +107,7 @@ class StorageService extends GetxService {
await _box.remove(_nameKey); await _box.remove(_nameKey);
await _box.remove(_phoneKey); await _box.remove(_phoneKey);
await _box.remove(_idCardKey); await _box.remove(_idCardKey);
// 登出时一并清除车辆信息
await clearVehicleInfo(); await clearVehicleInfo();
// 注意:登出时我们不清除“记住的密码”,以便下次用户登录时仍然可以回填
} }
} }