站点登录
This commit is contained in:
37
ln_jq_app/lib/common/login_util.dart
Normal file
37
ln_jq_app/lib/common/login_util.dart
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import 'package:encrypt/encrypt.dart';
|
||||||
|
|
||||||
|
class LoginUtil {
|
||||||
|
// 定义密钥
|
||||||
|
// 对于ECB模式,我们只需要Key,不需要IV (初始化向量)
|
||||||
|
static final _keyString = '915eae87951a448c86c47796e44c1fcf';
|
||||||
|
static final _key = Key.fromUtf8(_keyString);
|
||||||
|
|
||||||
|
// 【核心修改】创建使用 ECB 模式的加密器实例
|
||||||
|
// 1. mode: AESMode.ecb -> 使用ECB模式,它不需要IV。
|
||||||
|
// 2. padding: 'PKCS7' -> 在encrypt库中,PKCS5和PKCS7的填充方式是兼容的。
|
||||||
|
static final _encrypter = Encrypter(AES(_key, mode: AESMode.ecb, padding: 'PKCS7'));
|
||||||
|
|
||||||
|
/// AES 加密方法
|
||||||
|
static String encrypt(String plainText) {
|
||||||
|
if (plainText.isEmpty) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
// 【核心修改】调用 encrypt 方法时不再需要传递 iv
|
||||||
|
final encrypted = _encrypter.encrypt(plainText);
|
||||||
|
|
||||||
|
// 返回Base64编码的密文,这是网络传输的标准做法
|
||||||
|
return encrypted.base64;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AES 解密方法 (可选,如果需要解密的话)
|
||||||
|
static String decrypt(String encryptedText) {
|
||||||
|
if (encryptedText.isEmpty) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
final encrypted = Encrypted.fromBase64(encryptedText);
|
||||||
|
// 【核心修改】调用 decrypt 方法时不再需要传递 iv
|
||||||
|
final decrypted = _encrypter.decrypt(encrypted);
|
||||||
|
return decrypted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,19 +1,75 @@
|
|||||||
class BaseModel {
|
/// 通用的 API 响应基础模型
|
||||||
bool? success;
|
/// 使用泛型 <T> 来适应 'data' 字段中任何可能的数据类型
|
||||||
String? type;
|
class BaseModel<T> {
|
||||||
String? url;
|
final int code; // 状态码,0正常,其他异常
|
||||||
|
final bool status; // 状态布尔值,true正常,false异常
|
||||||
|
final String message; // 消息,例如 "success"
|
||||||
|
final T? data; // 核心数据,使用泛型 T,可以是任何类型
|
||||||
|
final int time; // 时间戳
|
||||||
|
final dynamic error; // 错误信息,可以是任何类型或 null
|
||||||
|
|
||||||
BaseModel({this.success, this.type, this.url});
|
BaseModel({
|
||||||
|
required this.code,
|
||||||
|
required this.status,
|
||||||
|
required this.message,
|
||||||
|
this.data, // data 可以为 null
|
||||||
|
required this.time,
|
||||||
|
this.error, // error 可以为 null
|
||||||
|
});
|
||||||
|
|
||||||
factory BaseModel.fromJson(Map<String, dynamic> json) => BaseModel(
|
/// fromJson 工厂构造函数(重构后)
|
||||||
success: json['success']?.toString().contains("true"),
|
factory BaseModel.fromJson(
|
||||||
type: json['type']?.toString(),
|
Map<String, dynamic> json, {
|
||||||
url: json['url']?.toString(),
|
T? Function(dynamic dataJson)? dataBuilder,
|
||||||
);
|
}) {
|
||||||
|
// 使用一个辅助函数来安全地转换类型,防止因类型不匹配(如 "0" vs 0)而崩溃
|
||||||
|
int _parseInt(dynamic value) {
|
||||||
|
if (value is int) return value;
|
||||||
|
if (value is String) return int.tryParse(value) ?? -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
T? finalData;
|
||||||
|
|
||||||
|
// 检查 'data' 字段是否存在
|
||||||
|
if (json.containsKey('data') && json['data'] != null) {
|
||||||
|
if (dataBuilder != null) {
|
||||||
|
// 如果提供了 dataBuilder,就用它来解析成具体的 T 类型对象
|
||||||
|
finalData = dataBuilder(json['data']);
|
||||||
|
} else {
|
||||||
|
// 如果没有提供 dataBuilder,但 T 不是 dynamic,我们假设 data 就是 T 类型
|
||||||
|
// 这在使用 BaseModel<Map<String, dynamic>> 时很有用
|
||||||
|
if (T != dynamic) {
|
||||||
|
try {
|
||||||
|
finalData = json['data'] as T?;
|
||||||
|
} catch(e) {
|
||||||
|
// 如果直接转换失败,保持为 null,避免崩溃
|
||||||
|
finalData = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果 T 是 dynamic,直接赋值
|
||||||
|
finalData = json['data'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return BaseModel<T>(
|
||||||
|
code: _parseInt(json['code']),
|
||||||
|
status: json['status'] as bool? ?? false,
|
||||||
|
message: json['message']?.toString() ?? 'No message',
|
||||||
|
time: _parseInt(json['time']),
|
||||||
|
data: finalData,
|
||||||
|
error: json['error'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// toJson 方法 (可选)
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
if (success != null) 'success': success,
|
'code': code,
|
||||||
if (type != null) 'type': type,
|
'status': status,
|
||||||
if (url != null) 'url': url,
|
'message': message,
|
||||||
};
|
'time': time,
|
||||||
|
'data': data,
|
||||||
|
'error': error,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ class AppTheme {
|
|||||||
|
|
||||||
static const Color themeColor = Color(0xFF0c83c3);
|
static const Color themeColor = Color(0xFF0c83c3);
|
||||||
|
|
||||||
|
|
||||||
|
static const String test_service_url = "http://beta-esg.api.lnh2e.com/";
|
||||||
|
static const String release_service_url = "";
|
||||||
|
|
||||||
static const Color secondaryColor = Colors.orange;
|
static const Color secondaryColor = Colors.orange;
|
||||||
|
|
||||||
static const Color darkThemeColor = Color(0xFF032896);
|
static const Color darkThemeColor = Color(0xFF032896);
|
||||||
|
|||||||
@@ -1,23 +1,26 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||||
|
import 'package:getx_scaffold/common/common.dart';
|
||||||
import 'package:getx_scaffold/getx_scaffold.dart';
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
import 'package:ln_jq_app/pages/b_page/base_widgets/view.dart';
|
import 'package:ln_jq_app/common/model/base_model.dart';
|
||||||
import 'package:ln_jq_app/pages/c_page/base_widgets/view.dart';
|
import 'package:ln_jq_app/storage_service.dart';
|
||||||
import 'package:ln_jq_app/pages/home/view.dart';
|
|
||||||
|
|
||||||
import 'common/styles/theme.dart';
|
import 'common/styles/theme.dart';
|
||||||
import 'pages/login/view.dart';
|
import 'pages/home/view.dart';
|
||||||
|
|
||||||
/// Main
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsBinding widgetsBinding = await init(isDebug: kDebugMode, logTag: '小羚羚');
|
WidgetsBinding widgetsBinding = await init(isDebug: true, logTag: '小羚羚');
|
||||||
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
||||||
|
|
||||||
|
await Get.putAsync(() => StorageService().init());
|
||||||
|
initHttpSet();
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
GetxApp(
|
GetxApp(
|
||||||
// 设计稿尺寸 单位:dp
|
// 设计稿尺寸 单位:dp
|
||||||
designSize: const Size(390, 844),
|
designSize: const Size(390, 844),
|
||||||
// Getx Log
|
// Getx Log
|
||||||
enableLog: kDebugMode,
|
enableLog: true,
|
||||||
// 默认的跳转动画
|
// 默认的跳转动画
|
||||||
defaultTransition: Transition.rightToLeft,
|
defaultTransition: Transition.rightToLeft,
|
||||||
// 主题模式
|
// 主题模式
|
||||||
@@ -30,6 +33,10 @@ void main() async {
|
|||||||
title: '小羚羚',
|
title: '小羚羚',
|
||||||
// 首页入口
|
// 首页入口
|
||||||
home: HomePage(),
|
home: HomePage(),
|
||||||
|
// 推荐使用命名路由,如果配置好了可以取消下面两行的注释
|
||||||
|
// initialRoute: AppPages.INITIAL,
|
||||||
|
// getPages: AppPages.routes,
|
||||||
|
|
||||||
// Builder
|
// Builder
|
||||||
builder: (context, widget) {
|
builder: (context, widget) {
|
||||||
// do something....
|
// do something....
|
||||||
@@ -39,4 +46,23 @@ void main() async {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void initHttpSet() {
|
||||||
|
// 设置基础 URL
|
||||||
|
HttpService.to.setBaseUrl(AppTheme.test_service_url);
|
||||||
|
|
||||||
|
// 设置全局响应处理器
|
||||||
|
HttpService.to.setOnResponseHandler((response) async {
|
||||||
|
try {
|
||||||
|
final baseModel = BaseModel<dynamic>.fromJson(response.data);
|
||||||
|
if (baseModel.code == 0) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return baseModel.message;
|
||||||
|
}
|
||||||
|
} on Exception catch (e) {
|
||||||
|
e.printInfo();
|
||||||
|
return '服务器异常';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import 'package:get/get.dart';
|
|||||||
import 'package:getx_scaffold/getx_scaffold.dart';
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
import 'package:ln_jq_app/pages/login/view.dart';
|
import 'package:ln_jq_app/pages/login/view.dart';
|
||||||
|
|
||||||
|
import '../../../storage_service.dart';
|
||||||
|
|
||||||
class ReservationController extends GetxController with BaseControllerMixin {
|
class ReservationController extends GetxController with BaseControllerMixin {
|
||||||
@override
|
@override
|
||||||
String get builderId => 'b_reservation'; // 确保ID与View中一致
|
String get builderId => 'b_reservation'; // 确保ID与View中一致
|
||||||
@@ -50,11 +52,12 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
Get.snackbar('提示', '保存成功!'); // 示例:显示一个成功的提示
|
Get.snackbar('提示', '保存成功!'); // 示例:显示一个成功的提示
|
||||||
}
|
}
|
||||||
|
|
||||||
void logout() {
|
void logout() async{
|
||||||
// TODO: 在这里执行退出登录的逻辑
|
// TODO: 在这里执行退出登录的逻辑
|
||||||
// 1. 清理本地缓存的用户信息
|
//清理本地缓存的用户信息 导航到登录页面
|
||||||
// 2. 调用退出登录接口
|
HttpService.to.clearAuthorization();
|
||||||
// 3. 导航到登录页面
|
await StorageService.to.clearLoginInfo();
|
||||||
|
|
||||||
Get.offAll(() => LoginPage());
|
Get.offAll(() => LoginPage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import 'package:ln_jq_app/pages/b_page/base_widgets/view.dart';
|
|||||||
import 'package:ln_jq_app/pages/c_page/base_widgets/view.dart';
|
import 'package:ln_jq_app/pages/c_page/base_widgets/view.dart';
|
||||||
import 'package:ln_jq_app/pages/login/view.dart';
|
import 'package:ln_jq_app/pages/login/view.dart';
|
||||||
|
|
||||||
|
import '../../storage_service.dart';
|
||||||
|
|
||||||
class HomeController extends GetxController with BaseControllerMixin {
|
class HomeController extends GetxController with BaseControllerMixin {
|
||||||
@override
|
@override
|
||||||
String get builderId => 'home';
|
String get builderId => 'home';
|
||||||
@@ -32,7 +34,7 @@ class HomeController extends GetxController with BaseControllerMixin {
|
|||||||
if (loginChannel == "b_login") {
|
if (loginChannel == "b_login") {
|
||||||
return BaseWidgetsPage(); // 渠道A进入 BaseWidgetsPage
|
return BaseWidgetsPage(); // 渠道A进入 BaseWidgetsPage
|
||||||
} else {
|
} else {
|
||||||
return B_BaseWidgetsPage(); // 渠道B进入 B_BaseWidgetsPage
|
return StorageService.to.isLoggedIn ? B_BaseWidgetsPage() : LoginPage(); // 渠道B进入 B_BaseWidgetsPage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:getx_scaffold/getx_scaffold.dart';
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
|
import 'package:ln_jq_app/common/login_util.dart';
|
||||||
|
import 'package:ln_jq_app/common/model/base_model.dart';
|
||||||
import 'package:ln_jq_app/common/styles/theme.dart';
|
import 'package:ln_jq_app/common/styles/theme.dart';
|
||||||
import 'package:ln_jq_app/pages/b_page/base_widgets/view.dart';
|
import 'package:ln_jq_app/pages/b_page/base_widgets/view.dart';
|
||||||
import 'package:ln_jq_app/pages/c_page/base_widgets/view.dart';
|
import 'package:ln_jq_app/pages/c_page/base_widgets/view.dart';
|
||||||
import 'package:ln_jq_app/pages/login/controller.dart';
|
import 'package:ln_jq_app/pages/login/controller.dart';
|
||||||
|
import 'package:ln_jq_app/storage_service.dart';
|
||||||
|
|
||||||
class LoginPage extends StatefulWidget {
|
class LoginPage extends StatefulWidget {
|
||||||
const LoginPage({super.key});
|
const LoginPage({super.key});
|
||||||
@@ -54,76 +57,77 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
|
|||||||
return Container(
|
return Container(
|
||||||
color: Color(0xFFEFF4F7),
|
color: Color(0xFFEFF4F7),
|
||||||
child: <Widget>[
|
child: <Widget>[
|
||||||
Icon(cLogin ? AntdIcon.car : AntdIcon.USB),
|
Icon(cLogin ? AntdIcon.car : AntdIcon.USB),
|
||||||
SizedBox(height: 5.h),
|
SizedBox(height: 5.h),
|
||||||
TextX.bodyLarge(cLogin ? '司机端' : "加氢站", weight: FontWeight.w700),
|
TextX.bodyLarge(cLogin ? '司机端' : "加氢站", weight: FontWeight.w700),
|
||||||
SizedBox(height: 5.h),
|
SizedBox(height: 5.h),
|
||||||
TextX.bodyLarge(cLogin ? '安全驾驶·智能服务' : "氢能服务·专业运营"),
|
TextX.bodyLarge(cLogin ? '安全驾驶·智能服务' : "氢能服务·专业运营"),
|
||||||
Card(
|
Card(
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(15), // 设置圆角弧度
|
borderRadius: BorderRadius.circular(15), // 设置圆角弧度
|
||||||
),
|
),
|
||||||
margin: EdgeInsets.all(15),
|
margin: EdgeInsets.all(15),
|
||||||
elevation: 4,
|
elevation: 4,
|
||||||
child: Container(
|
child: Container(
|
||||||
height: cLogin ? 260.h : 320.h,
|
height: cLogin ? 260.h : 320.h,
|
||||||
padding: EdgeInsets.all(15),
|
padding: EdgeInsets.all(15),
|
||||||
child: // TabBar切换
|
child: // TabBar切换
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
Card(
|
Card(
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(3),
|
padding: EdgeInsets.all(3),
|
||||||
child: TabBar(
|
child: TabBar(
|
||||||
controller: tabController,
|
controller: tabController,
|
||||||
onTap: (index) {
|
onTap: (index) {
|
||||||
//保证尺寸变化
|
//保证尺寸变化
|
||||||
delayed(300, () {
|
delayed(300, () {
|
||||||
switchTab(index);
|
switchTab(index);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// 修改TabBar的选中状态和未选中状态样式
|
// 修改TabBar的选中状态和未选中状态样式
|
||||||
labelColor: Colors.white,
|
labelColor: Colors.white,
|
||||||
// 选中时的文字颜色
|
// 选中时的文字颜色
|
||||||
unselectedLabelColor: Colors.black,
|
unselectedLabelColor: Colors.black,
|
||||||
// 未选中时的文字颜色
|
// 未选中时的文字颜色
|
||||||
indicator: BoxDecoration(
|
indicator: BoxDecoration(
|
||||||
color: AppTheme.themeColor, // 选中的Tab背景色(模拟卡片式效果)
|
color: AppTheme.themeColor, // 选中的Tab背景色(模拟卡片式效果)
|
||||||
borderRadius: BorderRadius.circular(12), // 卡片的圆角效果
|
borderRadius: BorderRadius.circular(12), // 卡片的圆角效果
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.blue.withOpacity(0.2),
|
color: Colors.blue.withOpacity(0.2),
|
||||||
spreadRadius: 1,
|
spreadRadius: 1,
|
||||||
blurRadius: 6,
|
blurRadius: 6,
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
tabs: [
|
||||||
|
Tab(text: '司机端登录'),
|
||||||
|
Tab(text: '加氢站登录'),
|
||||||
],
|
],
|
||||||
|
isScrollable: false,
|
||||||
),
|
),
|
||||||
tabs: [
|
|
||||||
Tab(text: '司机端登录'),
|
|
||||||
Tab(text: '加氢站登录'),
|
|
||||||
],
|
|
||||||
isScrollable: false,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
// 根据选择的Tab展示不同的输入框
|
||||||
// 根据选择的Tab展示不同的输入框
|
Flexible(
|
||||||
Flexible(
|
child: TabBarView(
|
||||||
child: TabBarView(
|
controller: tabController,
|
||||||
controller: tabController,
|
children: [
|
||||||
children: [
|
// 司机端登录
|
||||||
// 司机端登录
|
_driverLoginView(controller),
|
||||||
_driverLoginView(controller),
|
// 加氢站登录
|
||||||
// 加氢站登录
|
_stationLoginView(controller),
|
||||||
_stationLoginView(controller),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
].toColumn(mainAxisSize: MainAxisSize.min).center(),
|
||||||
].toColumn(mainAxisSize: MainAxisSize.min).center(),);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 司机端登录界面
|
// 司机端登录界面
|
||||||
@@ -151,14 +155,12 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
|
|||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// 司机端登录
|
// 司机端登录
|
||||||
Get.offAll(() => BaseWidgetsPage());
|
Get.offAll(() => BaseWidgetsPage());
|
||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: AppTheme.themeColor,
|
backgroundColor: AppTheme.themeColor,
|
||||||
minimumSize: Size(double.infinity, 50), // 设置按钮宽度占满,指定最小高度
|
minimumSize: Size(double.infinity, 50), // 设置按钮宽度占满,指定最小高度
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
child: Text('登录'),
|
child: Text('登录'),
|
||||||
),
|
),
|
||||||
@@ -215,13 +217,60 @@ 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(
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
// 加氢站登录逻辑
|
// 加氢站登录逻辑
|
||||||
Get.offAll(() => B_BaseWidgetsPage());
|
String account = controller.stationIdController.text;
|
||||||
|
String password = controller.passwordController.text;
|
||||||
|
|
||||||
|
//todo 删除
|
||||||
|
account = "000017";
|
||||||
|
password = "LnQn.314000";
|
||||||
|
|
||||||
|
if (account.isEmpty || password.isEmpty) {
|
||||||
|
showToast("请输入账号和密码");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showLoading('登录中...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 对密码进行AES加密
|
||||||
|
String encryptedPassword = LoginUtil.encrypt(password);
|
||||||
|
|
||||||
|
// 调用登录接口
|
||||||
|
var responseData = await HttpService.to.post(
|
||||||
|
'/login/password',
|
||||||
|
data: {
|
||||||
|
'account': account,
|
||||||
|
'password': encryptedPassword,
|
||||||
|
'loginType': "station",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (responseData == null && responseData!.data == null) {
|
||||||
|
dismissLoading();
|
||||||
|
showToast('登录失败:无法获取凭证');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final responseMap = responseData.data as Map<String, dynamic>;
|
||||||
|
|
||||||
|
//保存用户信息
|
||||||
|
String token = responseMap['token'] ?? '';
|
||||||
|
//hydrogenId
|
||||||
|
String userId = responseMap['userId'] ?? '';
|
||||||
|
await StorageService.to.saveLoginInfo(token: token, userId: userId);
|
||||||
|
|
||||||
|
dismissLoading();
|
||||||
|
showToast('登录成功,欢迎您');
|
||||||
|
HttpService.to.setAuthorization(token);
|
||||||
|
// 跳转到主页,并清除所有历史页面
|
||||||
|
Get.offAll(() => B_BaseWidgetsPage());
|
||||||
|
} catch (e) {
|
||||||
|
dismissLoading();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: Text('登录'),
|
child: Text('登录'),
|
||||||
),
|
),
|
||||||
|
|||||||
43
ln_jq_app/lib/storage_service.dart
Normal file
43
ln_jq_app/lib/storage_service.dart
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:get_storage/get_storage.dart';
|
||||||
|
|
||||||
|
class StorageService extends GetxService {
|
||||||
|
late final GetStorage _box;
|
||||||
|
|
||||||
|
// 定义存储时使用的键名(Key)
|
||||||
|
static const String _tokenKey = 'user_token';
|
||||||
|
static const String _userIdKey = 'user_id';
|
||||||
|
|
||||||
|
// 提供一个静态的 'to' 方法,方便全局访问
|
||||||
|
static StorageService get to => Get.find();
|
||||||
|
|
||||||
|
// Service 初始化
|
||||||
|
Future<StorageService> init() async {
|
||||||
|
_box = GetStorage();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 判断是否已登录 (通过检查 token 是否存在且不为空)
|
||||||
|
bool get isLoggedIn =>
|
||||||
|
_box
|
||||||
|
.read<String?>(_tokenKey)
|
||||||
|
?.isNotEmpty ?? false;
|
||||||
|
|
||||||
|
/// 获取 Token
|
||||||
|
String? get token => _box.read<String?>(_tokenKey);
|
||||||
|
|
||||||
|
/// 获取 UserId
|
||||||
|
String? get userId => _box.read<String?>(_userIdKey);
|
||||||
|
|
||||||
|
/// 保存用户信息
|
||||||
|
Future<void> saveLoginInfo({required String token, required String userId}) async {
|
||||||
|
await _box.write(_tokenKey, token);
|
||||||
|
await _box.write(_userIdKey, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 3. 删除用户信息 (用于退出登录)
|
||||||
|
Future<void> clearLoginInfo() async {
|
||||||
|
await _box.remove(_tokenKey);
|
||||||
|
await _box.remove(_userIdKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -178,7 +178,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
encrypt:
|
encrypt:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: encrypt
|
name: encrypt
|
||||||
sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2"
|
sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2"
|
||||||
@@ -304,6 +304,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.7.2"
|
version: "4.7.2"
|
||||||
|
get_storage:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: get_storage
|
||||||
|
sha256: "39db1fffe779d0c22b3a744376e86febe4ade43bf65e06eab5af707dc84185a2"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
getx_scaffold:
|
getx_scaffold:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ dependencies:
|
|||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
getx_scaffold: ^0.2.2
|
getx_scaffold: ^0.2.2
|
||||||
|
encrypt: ^5.0.3
|
||||||
|
get_storage: ^2.1.1
|
||||||
|
|
||||||
flutter_native_splash: ^2.4.7
|
flutter_native_splash: ^2.4.7
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user