忘记密码

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

View File

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