diff --git a/lib/base/http/token_interceptor.dart b/lib/base/http/token_interceptor.dart index e174885..3daffaa 100644 --- a/lib/base/http/token_interceptor.dart +++ b/lib/base/http/token_interceptor.dart @@ -7,18 +7,22 @@ import '../userinfo_viewmodel.dart'; class TokenInterceptor extends Interceptor { @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) { - if (getIt().token != null && getIt().token!.isNotEmpty) { - options.headers["Authorization"] = "Bearer " + getIt().token!; - } - options.headers["User-Agent"] = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1"; options.headers["Content-Type"] = "application/json;charset=UTF-8"; - if (!Url.inWhiteList(options.path)) { - options.queryParameters["t"] = (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(); + options.queryParameters["t"] = + (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(); + } + + if (!Url.inLoginList(options.path)) { + if (getIt().token != null && + getIt().token!.isNotEmpty) { + options.headers["Authorization"] = + "Bearer " + getIt().token!; + } } return handler.next(options); } diff --git a/lib/base/http/url.dart b/lib/base/http/url.dart index 6e919ab..26e2025 100644 --- a/lib/base/http/url.dart +++ b/lib/base/http/url.dart @@ -12,70 +12,131 @@ class Url { static const updatePassword = "/api/user"; - static get tasks => getIt().useSecretLogined ? "/open/crons" : "/api/crons"; + static get tasks => getIt().useSecretLogined + ? "/open/crons" + : "/api/crons"; - static get runTasks => getIt().useSecretLogined ? "/open/crons/run" : "/api/crons/run"; + static get runTasks => getIt().useSecretLogined + ? "/open/crons/run" + : "/api/crons/run"; - static get stopTasks => getIt().useSecretLogined ? "/open/crons/stop" : "/api/crons/stop"; + static get stopTasks => getIt().useSecretLogined + ? "/open/crons/stop" + : "/api/crons/stop"; - static get taskDetail => getIt().useSecretLogined ? "/open/crons/" : "/api/crons/"; + static get taskDetail => getIt().useSecretLogined + ? "/open/crons/" + : "/api/crons/"; - static get addTask => getIt().useSecretLogined ? "/open/crons" : "/api/crons"; + static get addTask => getIt().useSecretLogined + ? "/open/crons" + : "/api/crons"; - static get pinTask => getIt().useSecretLogined ? "/open/crons/pin" : "/api/crons/pin"; + static get pinTask => getIt().useSecretLogined + ? "/open/crons/pin" + : "/api/crons/pin"; - static get unpinTask => getIt().useSecretLogined ? "/open/crons/unpin" : "/api/crons/unpin"; + static get unpinTask => getIt().useSecretLogined + ? "/open/crons/unpin" + : "/api/crons/unpin"; - static get enableTask => getIt().useSecretLogined ? "/open/crons/enable" : "/api/crons/enable"; + static get enableTask => getIt().useSecretLogined + ? "/open/crons/enable" + : "/api/crons/enable"; - static get disableTask => getIt().useSecretLogined ? "/open/crons/disable" : "/api/crons/disable"; + static get disableTask => getIt().useSecretLogined + ? "/open/crons/disable" + : "/api/crons/disable"; - static get files => getIt().useSecretLogined ? "/open/configs/files" : "/api/configs/files"; + static get files => getIt().useSecretLogined + ? "/open/configs/files" + : "/api/configs/files"; - static get configContent => getIt().useSecretLogined ? "/open/configs/" : "/api/configs/"; + static get configContent => getIt().useSecretLogined + ? "/open/configs/" + : "/api/configs/"; - static get saveFile => getIt().useSecretLogined ? "/open/configs/save" : "/api/configs/save"; + static get saveFile => getIt().useSecretLogined + ? "/open/configs/save" + : "/api/configs/save"; - static get envs => getIt().useSecretLogined ? "/open/envs" : "/api/envs"; + static get envs => + getIt().useSecretLogined ? "/open/envs" : "/api/envs"; - static get addEnv => getIt().useSecretLogined ? "/open/envs" : "/api/envs"; + static get addEnv => + getIt().useSecretLogined ? "/open/envs" : "/api/envs"; - static get delEnv => getIt().useSecretLogined ? "/open/envs" : "/api/envs"; + static get delEnv => + getIt().useSecretLogined ? "/open/envs" : "/api/envs"; - static get disableEnvs => getIt().useSecretLogined ? "/open/envs/disable" : "/api/envs/disable"; + static get disableEnvs => getIt().useSecretLogined + ? "/open/envs/disable" + : "/api/envs/disable"; - static get enableEnvs => getIt().useSecretLogined ? "/open/envs/enable" : "/api/envs/enable"; + static get enableEnvs => getIt().useSecretLogined + ? "/open/envs/enable" + : "/api/envs/enable"; - static get loginLog => getIt().useSecretLogined ? "/open/user/login-log" : "/api/user/login-log"; + static get loginLog => getIt().useSecretLogined + ? "/open/user/login-log" + : "/api/user/login-log"; - static get taskLog => getIt().useSecretLogined ? "/open/logs" : "/api/logs"; + static get taskLog => + getIt().useSecretLogined ? "/open/logs" : "/api/logs"; - static get taskLogDetail => getIt().useSecretLogined ? "/open/logs/" : "/api/logs/"; + static get taskLogDetail => getIt().useSecretLogined + ? "/open/logs/" + : "/api/logs/"; - static get scripts => getIt().useSecretLogined ? "/open/scripts/files" : "/api/scripts/files"; + static get scripts => getIt().useSecretLogined + ? "/open/scripts/files" + : "/api/scripts/files"; - static get scriptUpdate => getIt().useSecretLogined ? "/open/scripts" : "/api/scripts"; + static get scriptUpdate => getIt().useSecretLogined + ? "/open/scripts" + : "/api/scripts"; - static get scriptDetail => getIt().useSecretLogined ? "/open/scripts/" : "/api/scripts/"; + static get scriptDetail => getIt().useSecretLogined + ? "/open/scripts/" + : "/api/scripts/"; - static get dependencies => getIt().useSecretLogined ? "/open/dependencies" : "/api/dependencies"; + static get dependencies => getIt().useSecretLogined + ? "/open/dependencies" + : "/api/dependencies"; - static get dependencyReinstall => getIt().useSecretLogined ? "/open/dependencies/reinstall" : "/api/dependencies/reinstall"; + static get dependencyReinstall => getIt().useSecretLogined + ? "/open/dependencies/reinstall" + : "/api/dependencies/reinstall"; static intimeLog(String cronId) { - return getIt().useSecretLogined ? "/open/crons/$cronId/log" : "/api/crons/$cronId/log"; + return getIt().useSecretLogined + ? "/open/crons/$cronId/log" + : "/api/crons/$cronId/log"; } static envMove(String envId) { - return getIt().useSecretLogined ? "/open/envs/$envId/move" : "/api/envs/$envId/move"; + return getIt().useSecretLogined + ? "/open/envs/$envId/move" + : "/api/envs/$envId/move"; } static bool inWhiteList(String path) { - if (path == login || path == loginByClientId || path == loginTwo || path == loginOld) { + if (path == login || + path == loginByClientId || + path == loginTwo || + path == loginOld) { return true; } return false; } - static String checkUpdateUrl = "https://raw.githubusercontent.com/qinglong-app/qinglong_app/main/version"; + static bool inLoginList(String path) { + if (path == login || path == loginByClientId || path == loginOld) { + return true; + } + return false; + } + + static String checkUpdateUrl = + "https://raw.githubusercontent.com/qinglong-app/qinglong_app/main/version"; } diff --git a/lib/base/routes.dart b/lib/base/routes.dart index caafac2..7023f3a 100644 --- a/lib/base/routes.dart +++ b/lib/base/routes.dart @@ -7,6 +7,7 @@ import 'package:qinglong_app/module/env/env_detail_page.dart'; import 'package:qinglong_app/module/home/home_page.dart'; import 'package:qinglong_app/module/login/login_page.dart'; import 'package:qinglong_app/module/others/about_page.dart'; +import 'package:qinglong_app/module/others/change_account.dart'; import 'package:qinglong_app/module/others/dependencies/add_dependency_page.dart'; import 'package:qinglong_app/module/others/dependencies/dependency_page.dart'; import 'package:qinglong_app/module/others/login_log/login_log_page.dart'; @@ -41,13 +42,22 @@ class Routes { static const String routeUpdatePassword = "/updatePassword"; static const String routeAbout = "/about"; static const String routeTheme = "/theme"; + static const String routeChangeAccount = "/changeAccount"; static Route? generateRoute(RouteSettings settings) { switch (settings.name) { case routeHomePage: return MaterialPageRoute(builder: (context) => const HomePage()); case routeLogin: - return MaterialPageRoute(builder: (context) => const LoginPage()); + if (settings.arguments != null) { + return MaterialPageRoute( + builder: (context) => const LoginPage( + doNotLoadLocalData: true, + )); + } else { + return MaterialPageRoute(builder: (context) => const LoginPage()); + } + case routeAddTask: if (settings.arguments != null) { return CupertinoPageRoute( @@ -129,6 +139,10 @@ class Routes { return CupertinoPageRoute( builder: (context) => const ThemePage(), ); + case routeChangeAccount: + return CupertinoPageRoute( + builder: (context) => const ChangeAccountPage(), + ); case routeScriptUpdate: return CupertinoPageRoute( builder: (context) => ScriptEditPage( diff --git a/lib/base/sp_const.dart b/lib/base/sp_const.dart index b4ae7b4..1fd2dce 100644 --- a/lib/base/sp_const.dart +++ b/lib/base/sp_const.dart @@ -5,3 +5,4 @@ String spPassWord = "password"; String spTheme = "dart_mode"; String spSecretLogined = "secret_logined"; String spCustomColor = "customColor"; +String spLoginHistory = "loginHistory"; diff --git a/lib/base/userinfo_viewmodel.dart b/lib/base/userinfo_viewmodel.dart index 97c7c7b..f263895 100644 --- a/lib/base/userinfo_viewmodel.dart +++ b/lib/base/userinfo_viewmodel.dart @@ -1,18 +1,22 @@ +import 'dart:convert'; + import 'package:qinglong_app/utils/sp_utils.dart'; import 'sp_const.dart'; import 'theme.dart'; class UserInfoViewModel { + int _primaryColor = commonColor.value; String? _token; String? _host = ""; String? _userName; String? _passWord; bool _useSecertLogined = false; - int _primaryColor = commonColor.value; + + List historyAccounts = []; UserInfoViewModel() { - String userInfoJson = SpUtil.getString(spUserInfo); + _token = SpUtil.getString(spUserInfo); _userName = SpUtil.getString(spUserName); _passWord = SpUtil.getString(spPassWord); _primaryColor = SpUtil.getInt(spCustomColor, defValue: commonColor.value); @@ -20,8 +24,13 @@ class UserInfoViewModel { _useSecertLogined = SpUtil.getBool(spSecretLogined, defValue: false); _host = SpUtil.getString(spHost, defValue: ''); - if (userInfoJson.isNotEmpty) { - _token = userInfoJson; + List? tempList = + jsonDecode(SpUtil.getString(spLoginHistory, defValue: '[]')); + + if (tempList != null && tempList.isNotEmpty) { + for (Map value in tempList) { + historyAccounts.add(UserInfoBean.fromJson(value)); + } } } @@ -30,14 +39,19 @@ class UserInfoViewModel { SpUtil.putString(spUserInfo, token); } - void updateUserName(String userName, String password) { + void updateUserName( + String host, String userName, String password, bool secretLogin) { + updateHost(host); + _useSecretLogin(secretLogin); _userName = userName; _passWord = password; SpUtil.putString(spUserName, userName); SpUtil.putString(spPassWord, password); + + save2HistoryAccount(); } - void useSecretLogin(bool use) { + void _useSecretLogin(bool use) { _useSecertLogined = use; SpUtil.putBool(spSecretLogined, _useSecertLogined); } @@ -67,4 +81,60 @@ class UserInfoViewModel { bool isLogined() { return token != null && token!.isNotEmpty; } + + void save2HistoryAccount() { + if (_host == null || _host!.isEmpty) return; + if (_userName == null || _userName!.isEmpty) return; + if (_passWord == null || _passWord!.isEmpty) return; + + //如果已经存在host,那就更新 + + print("login success $_host $userName $passWord"); + + historyAccounts.removeWhere((element) => element.host == _host); + + historyAccounts.insert( + 0, + UserInfoBean( + userName: _userName, + password: _passWord, + useSecretLogined: _useSecertLogined, + host: _host)); + + SpUtil.putString(spLoginHistory, jsonEncode(historyAccounts)); + } + + void removeHistoryAccount(String? host) { + if (host == null || host.isEmpty) return; + + historyAccounts.removeWhere((element) => element.host == host); + + SpUtil.putString(spLoginHistory, jsonEncode(historyAccounts)); + } +} + +class UserInfoBean { + String? userName; + String? password; + bool useSecretLogined = false; + String? host; + + UserInfoBean( + {this.userName, this.password, this.useSecretLogined = false, this.host}); + + UserInfoBean.fromJson(Map json) { + userName = json['userName']; + password = json['password']; + useSecretLogined = json['useSecretLogined'] ?? false; + host = json['host']; + } + + Map toJson() { + final Map data = new Map(); + data['userName'] = this.userName; + data['password'] = this.password; + data['useSecretLogined'] = this.useSecretLogined; + data['host'] = this.host; + return data; + } } diff --git a/lib/main.dart b/lib/main.dart index 9b12a29..2ace2c5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -34,7 +34,8 @@ void main() async { ), ); if (Platform.isAndroid) { - SystemUiOverlayStyle style = const SystemUiOverlayStyle(statusBarColor: Colors.transparent); + SystemUiOverlayStyle style = + const SystemUiOverlayStyle(statusBarColor: Colors.transparent); SystemChrome.setSystemUIOverlayStyle(style); } } @@ -55,7 +56,10 @@ class QlAppState extends ConsumerState { FocusScope.of(context).requestFocus(FocusNode()); }, child: MediaQuery( - data: MediaQueryData.fromWindow(WidgetsBinding.instance!.window).copyWith(textScaleFactor: 1,), + data: + MediaQueryData.fromWindow(WidgetsBinding.instance!.window).copyWith( + textScaleFactor: 1, + ), child: MaterialApp( title: "青龙", locale: const Locale('zh', 'CN'), @@ -66,7 +70,12 @@ class QlAppState extends ConsumerState { }, home: Builder( builder: (context) { - return getIt().isLogined() ? const HomePage() : const LoginPage(); + if (!kReleaseMode) { + showDebugBtn(context); + } + return getIt().isLogined() + ? const HomePage() + : const LoginPage(); }, ), // home: LoginPage(), diff --git a/lib/module/config/config_page.dart b/lib/module/config/config_page.dart index de4b25d..72c6896 100644 --- a/lib/module/config/config_page.dart +++ b/lib/module/config/config_page.dart @@ -19,7 +19,8 @@ class ConfigPage extends StatefulWidget { class ConfigPageState extends State with SingleTickerProviderStateMixin { - TabController? _tabController; + int _initIndex = 0; + BuildContext? childContext; @override void initState() { @@ -33,51 +34,57 @@ class ConfigPageState extends State if (model.list.isEmpty) { return const EmptyWidget(); } - _tabController ??= - TabController(length: model.list.length, vsync: this); - return Column( - children: [ - TabBar( - controller: _tabController, - tabs: model.list - .map((e) => Tab( - text: e.title, - )) - .toList(), - isScrollable: true, - indicator: AbsUnderlineTabIndicator( - wantWidth: 20, - borderSide: BorderSide( - color: Theme.of(context).primaryColor, - width: 2, + return DefaultTabController( + length: model.list.length, + initialIndex: _initIndex, + child: Builder(builder: (context) { + childContext = context; + return Column( + children: [ + TabBar( + tabs: model.list + .map((e) => Tab( + text: e.title, + )) + .toList(), + isScrollable: true, + indicator: AbsUnderlineTabIndicator( + wantWidth: 20, + borderSide: BorderSide( + color: Theme.of(context).primaryColor, + width: 2, + ), + ), + onTap: (index) { + print(".....$index"); + }, ), - ), - ), - Expanded( - child: TabBarView( - controller: _tabController, - children: model.list - .map( - (e) => SingleChildScrollView( - child: HighlightView( - model.content[e.title] ?? "", - language: "sh", - padding: const EdgeInsets.symmetric( - horizontal: 15, + Expanded( + child: TabBarView( + children: model.list + .map( + (e) => SingleChildScrollView( + child: HighlightView( + model.content[e.title] ?? "", + language: "sh", + padding: const EdgeInsets.symmetric( + horizontal: 15, + ), + theme: ref + .watch(themeProvider) + .themeColor + .codeEditorTheme(), + tabSize: 14, + ), ), - theme: ref - .watch(themeProvider) - .themeColor - .codeEditorTheme(), - tabSize: 14, - ), - ), - ) - .toList(), - ), - ), - ], + ) + .toList(), + ), + ), + ], + ); + }), ); }, model: configProvider, @@ -88,11 +95,16 @@ class ConfigPageState extends State } void editMe(WidgetRef ref) { - if (_tabController == null || _tabController!.length == 0) return; + if (childContext == null) return; navigatorState.currentState?.pushNamed(Routes.routeConfigEdit, arguments: { - "title": ref.read(configProvider).list[_tabController?.index ?? 0].title, - "content": ref.read(configProvider).content[ - ref.read(configProvider).list[_tabController?.index ?? 0].title] + "title": ref + .read(configProvider) + .list[DefaultTabController.of(childContext!)?.index ?? 0] + .title, + "content": ref.read(configProvider).content[ref + .read(configProvider) + .list[DefaultTabController.of(childContext!)?.index ?? 0] + .title] }).then((value) async { if (value != null && (value as String).isNotEmpty) { await ref.read(configProvider).loadContent(value); diff --git a/lib/module/login/login_page.dart b/lib/module/login/login_page.dart index a638c1d..e1704c8 100644 --- a/lib/module/login/login_page.dart +++ b/lib/module/login/login_page.dart @@ -10,22 +10,25 @@ import 'package:qinglong_app/base/userinfo_viewmodel.dart'; import 'package:qinglong_app/main.dart'; import 'package:qinglong_app/module/login/user_bean.dart'; import 'package:qinglong_app/utils/extension.dart'; +import 'package:qinglong_app/utils/login_helper.dart'; import 'package:qinglong_app/utils/update_utils.dart'; import 'package:qinglong_app/utils/utils.dart'; import 'package:flip_card/flip_card.dart'; -import 'login_bean.dart'; - class LoginPage extends ConsumerStatefulWidget { - const LoginPage({Key? key}) : super(key: key); + final bool doNotLoadLocalData; + + const LoginPage({ + Key? key, + this.doNotLoadLocalData = false, + }) : super(key: key); @override _LoginPageState createState() => _LoginPageState(); } class _LoginPageState extends ConsumerState { - final TextEditingController _hostController = - TextEditingController(text: getIt().host); + final TextEditingController _hostController = TextEditingController(); final TextEditingController _userNameController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); final TextEditingController _cIdController = TextEditingController(); @@ -39,25 +42,27 @@ class _LoginPageState extends ConsumerState { @override void initState() { super.initState(); - useSecretLogin = getIt().useSecretLogined; - - if (getIt().userName != null && - getIt().userName!.isNotEmpty) { - if (getIt().useSecretLogined) { - _cIdController.text = getIt().userName!; + if (!widget.doNotLoadLocalData) { + _hostController.text = getIt().host ?? ""; + useSecretLogin = getIt().useSecretLogined; + if (getIt().userName != null && + getIt().userName!.isNotEmpty) { + if (getIt().useSecretLogined) { + _cIdController.text = getIt().userName!; + } else { + _userNameController.text = getIt().userName!; + } + rememberPassword = true; } else { - _userNameController.text = getIt().userName!; + rememberPassword = false; } - rememberPassword = true; - } else { - rememberPassword = false; - } - if (getIt().passWord != null && - getIt().passWord!.isNotEmpty) { - if (getIt().useSecretLogined) { - _cSecretController.text = getIt().passWord!; - } else { - _passwordController.text = getIt().passWord!; + if (getIt().passWord != null && + getIt().passWord!.isNotEmpty) { + if (getIt().useSecretLogined) { + _cSecretController.text = getIt().passWord!; + } else { + _passwordController.text = getIt().passWord!; + } } } getIt().updateToken(""); @@ -361,9 +366,6 @@ class _LoginPageState extends ConsumerState { onPressed: () { Http.pushedLoginPage = false; Utils.hideKeyBoard(context); - getIt() - .updateHost(_hostController.text); - Http.clear(); if (loginByUserName()) { login(_userNameController.text, _passwordController.text); @@ -392,64 +394,33 @@ class _LoginPageState extends ConsumerState { return !useSecretLogin; } + LoginHelper? helper; + Future login(String userName, String password) async { isLoading = true; setState(() {}); - HttpResponse response; - if (loginByUserName()) { - response = await Api.login(userName, password); + helper = LoginHelper(useSecretLogin, _hostController.text, userName, + password, rememberPassword); + var response = await helper!.login(); + dealLoginResponse(response); + } + + void dealLoginResponse(int response) { + if (response == LoginHelper.success) { + Navigator.of(context).pushReplacementNamed(Routes.routeHomePage); + } else if (response == LoginHelper.failed) { + loginFailed(); } else { - response = await Api.loginByClientId(userName, password); - } - if (response.success) { - loginSuccess(response, userName, password); - } else if (loginByUserName() && response.code == 401) { - //可能用户使用的是老版本qinglong - HttpResponse oldResponse = - await Api.loginOld(userName, password); - if (oldResponse.success) { - loginSuccess(oldResponse, userName, password); - } else { - (oldResponse.message ?? "请检查网络情况").toast(); - if (oldResponse.code == 420) { - twoFact(userName, password); - } else { - isLoading = false; - setState(() {}); - } - } - } else { - print(response.code); - (response.message ?? "请检查网络情况").toast(); - //420代表需要2步验证 - if (response.code == 420) { - twoFact(userName, password); - } else { - isLoading = false; - setState(() {}); - } + twoFact(); } } - void loginFailed(HttpResponse response) { - (response.message ?? "请检查网络情况").toast(); + void loginFailed() { isLoading = false; setState(() {}); } - void loginSuccess( - HttpResponse response, String userName, String password) { - getIt().updateToken(response.bean?.token ?? ""); - getIt().useSecretLogin(!loginByUserName()); - if (rememberPassword) { - getIt().updateUserName(userName, password); - } else { - getIt().updateUserName("", ""); - } - Navigator.of(context).pushReplacementNamed(Routes.routeHomePage); - } - bool canClickLoginBtn() { if (isLoading) return false; @@ -463,7 +434,7 @@ class _LoginPageState extends ConsumerState { } } - void twoFact(String userName, String password) { + void twoFact() { String twoFact = ""; showCupertinoDialog( context: context, @@ -510,12 +481,11 @@ class _LoginPageState extends ConsumerState { ), onPressed: () async { Navigator.of(context).pop(true); - HttpResponse response = - await Api.loginTwo(userName, password, twoFact); - if (response.success) { - loginSuccess(response, userName, password); + if (helper != null) { + var response = await helper!.loginTwice(twoFact); + dealLoginResponse(response); } else { - loginFailed(response); + "状态异常,请重新点登录按钮".toast(); } }, ), diff --git a/lib/module/others/change_account.dart b/lib/module/others/change_account.dart new file mode 100644 index 0000000..27a1c52 --- /dev/null +++ b/lib/module/others/change_account.dart @@ -0,0 +1,320 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:qinglong_app/base/http/api.dart'; +import 'package:qinglong_app/base/http/http.dart'; +import 'package:qinglong_app/base/ql_app_bar.dart'; +import 'package:qinglong_app/base/routes.dart'; +import 'package:qinglong_app/base/theme.dart'; +import 'package:qinglong_app/base/userinfo_viewmodel.dart'; +import 'package:qinglong_app/utils/extension.dart'; +import 'package:qinglong_app/utils/login_helper.dart'; + +import '../../main.dart'; + +class ChangeAccountPage extends ConsumerStatefulWidget { + const ChangeAccountPage({Key? key}) : super(key: key); + + @override + _ChangeAccountPageState createState() => _ChangeAccountPageState(); +} + +class _ChangeAccountPageState extends ConsumerState { + String isLoginingHost = ""; + String preHost = ""; + + @override + void initState() { + super.initState(); + preHost = getIt().historyAccounts.first.host ?? ""; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: QlAppBar( + canBack: true, + title: "切换账号", + ), + body: SingleChildScrollView( + child: SizedBox( + width: MediaQuery.of(context).size.width, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: MediaQuery.of(context).size.height / 10, + ), + Text( + "轻触账号以切换登录", + style: TextStyle( + color: ref.watch(themeProvider).themeColor.titleColor(), + fontSize: 25, + ), + ), + const SizedBox( + height: 30, + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 15, + ), + child: ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + if (index == + getIt().historyAccounts.length) { + return addAccount(); + } + return ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Container( + color: Colors.white, + child: buildCell(index), + ), + ); + }, + separatorBuilder: (_, __) { + return const SizedBox( + height: 10, + ); + }, + itemCount: + getIt().historyAccounts.length + 1), + ), + ], + ), + ), + ), + ); + } + + Widget buildCell(int index) { + Widget child = ListTile( + title: Text( + getIt().historyAccounts[index].host ?? "", + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 15, + ), + minVerticalPadding: 10, + subtitle: Padding( + padding: const EdgeInsets.only(top: 10.0), + child: Text( + getIt().historyAccounts[index].userName ?? "", + ), + ), + trailing: index == 0 + ? Container( + padding: const EdgeInsets.symmetric( + horizontal: 5, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + border: Border.all( + color: ref.watch(themeProvider).primaryColor, width: 1), + ), + child: Text( + "已登录", + style: TextStyle( + color: ref.watch(themeProvider).primaryColor, fontSize: 12), + ), + ) + : (isLoginingHost.isNotEmpty + ? SizedBox( + width: 15, + height: 15, + child: CircularProgressIndicator( + strokeWidth: 2, + color: ref.watch(themeProvider).primaryColor, + ), + ) + : const SizedBox.shrink()), + ); + + if (getIt().historyAccounts[index].host == + getIt().host) { + return child; + } + + return Slidable( + key: ValueKey(index), + endActionPane: ActionPane( + motion: const ScrollMotion(), + extentRatio: 0.15, + children: [ + SlidableAction( + backgroundColor: Colors.red, + flex: 1, + onPressed: (_) { + getIt().removeHistoryAccount( + getIt().historyAccounts[index].host); + setState(() {}); + }, + foregroundColor: Colors.white, + icon: CupertinoIcons.delete, + ), + ], + ), + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + loginNewByBean(getIt().historyAccounts[index]); + }, + child: child), + ); + } + + LoginHelper? helper; + + void loginNewByBean(UserInfoBean historyAccount) async { + isLoginingHost = historyAccount.host ?? ""; + setState(() {}); + helper = LoginHelper( + historyAccount.useSecretLogined, + historyAccount.host ?? "", + historyAccount.userName ?? "", + historyAccount.password ?? "", + true, + ); + var response = await helper!.login(); + dealLoginResponse(response); + } + + void dealLoginResponse(int response) { + if (response == LoginHelper.success) { + Navigator.of(context) + .pushNamedAndRemoveUntil(Routes.routeHomePage, (_) => false); + } else if (response == LoginHelper.failed) { + loginFailed(); + } else { + twoFact(); + } + } + + void loginFailed() { + isLoginingHost = ""; + Http.clear(); + getIt().updateHost(preHost); + setState(() {}); + } + + void twoFact() { + String twoFact = ""; + showCupertinoDialog( + context: context, + builder: (_) => CupertinoAlertDialog( + title: const Text("两步验证"), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Material( + color: Colors.transparent, + child: TextField( + onChanged: (value) { + twoFact = value; + }, + maxLines: 1, + keyboardType: TextInputType.number, + decoration: const InputDecoration( + contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 5), + hintText: "请输入code", + ), + autofocus: true, + ), + ), + ], + ), + actions: [ + CupertinoDialogAction( + child: const Text( + "取消", + style: TextStyle( + color: Color(0xff999999), + ), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + CupertinoDialogAction( + child: Text( + "确定", + style: TextStyle( + color: ref.watch(themeProvider).primaryColor, + ), + ), + onPressed: () async { + Navigator.of(context).pop(true); + if (helper != null) { + var response = await helper!.loginTwice(twoFact); + dealLoginResponse(response); + } else { + "状态异常".toast(); + } + }, + ), + ], + )).then((value) { + if (value == null) { + isLoginingHost = ""; + setState(() {}); + } + }); + } + + Widget addAccount() { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + Navigator.of(context).pushNamedAndRemoveUntil( + Routes.routeLogin, + (_) => false, + arguments: true, + ); + }, + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 15, + horizontal: 15, + ), + color: Colors.white, + child: Row( + children: [ + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + border: Border.all( + color: ref.watch(themeProvider).themeColor.descColor(), + ), + ), + child: Icon( + CupertinoIcons.add, + color: ref.watch(themeProvider).themeColor.descColor(), + ), + ), + const SizedBox( + width: 15, + ), + Text( + "添加账号", + style: TextStyle( + color: ref.watch(themeProvider).themeColor.titleColor(), + fontSize: 18, + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/module/others/other_page.dart b/lib/module/others/other_page.dart index f85f3ca..7244b8f 100644 --- a/lib/module/others/other_page.dart +++ b/lib/module/others/other_page.dart @@ -379,6 +379,29 @@ class _OtherPageState extends ConsumerState { const SizedBox( height: 30, ), + Center( + child: SizedBox( + width: MediaQuery.of(context).size.width - 40, + child: CupertinoButton( + padding: const EdgeInsets.symmetric( + vertical: 5, + ), + color: ref.watch(themeProvider).themeColor.buttonBgColor(), + child: const Text( + "切换账号", + style: TextStyle( + fontSize: 16, + ), + ), + onPressed: () { + Navigator.of(context).pushNamed(Routes.routeChangeAccount); + }, + ), + ), + ), + const SizedBox( + height: 30, + ), Center( child: SizedBox( width: MediaQuery.of(context).size.width - 40, diff --git a/lib/module/others/update_password_page.dart b/lib/module/others/update_password_page.dart index 12716ac..f4512ea 100644 --- a/lib/module/others/update_password_page.dart +++ b/lib/module/others/update_password_page.dart @@ -23,7 +23,8 @@ class UpdatePasswordPage extends ConsumerStatefulWidget { class _UpdatePasswordPageState extends ConsumerState { final TextEditingController _nameController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); - final TextEditingController _passwordAgainController = TextEditingController(); + final TextEditingController _passwordAgainController = + TextEditingController(); FocusNode focusNode = FocusNode(); @@ -175,7 +176,8 @@ class _UpdatePasswordPageState extends ConsumerState { "用户名不能为空".toast(); return; } - if (_passwordController.text.isEmpty || _passwordAgainController.text.isEmpty) { + if (_passwordController.text.isEmpty || + _passwordAgainController.text.isEmpty) { "密码不能为空".toast(); return; } @@ -191,15 +193,18 @@ class _UpdatePasswordPageState extends ConsumerState { void commitReal() async { String name = _nameController.text; String password = _passwordController.text; - HttpResponse response = await Api.updatePassword(name, password); + HttpResponse response = + await Api.updatePassword(name, password); if (response.success) { "更新成功".toast(); if (!getIt().useSecretLogined) { - getIt().updateUserName(name, password); + getIt().updateUserName( + getIt().host ?? "", name, password, false); } - Navigator.of(context).pushNamedAndRemoveUntil(Routes.routeLogin, (route) => false); + Navigator.of(context) + .pushNamedAndRemoveUntil(Routes.routeLogin, (route) => false); } else { response.message.toast(); } diff --git a/lib/utils/login_helper.dart b/lib/utils/login_helper.dart new file mode 100644 index 0000000..7a05d18 --- /dev/null +++ b/lib/utils/login_helper.dart @@ -0,0 +1,89 @@ +import 'package:qinglong_app/base/http/api.dart'; +import 'package:qinglong_app/base/http/http.dart'; +import 'package:qinglong_app/base/userinfo_viewmodel.dart'; +import 'package:qinglong_app/module/login/login_bean.dart'; +import 'package:qinglong_app/utils/extension.dart'; + +import '../main.dart'; + +class LoginHelper { + static int success = 0; + static int failed = 1; + static int twiceLogin = 2; + bool useSecretLogin = false; + + final String host; + final String userName; + final String password; + final bool rememberPassword; + + LoginHelper(this.useSecretLogin, this.host, this.userName, this.password, + this.rememberPassword); + + Future login() async { + Http.clear(); + getIt().updateHost(host); + HttpResponse response; + + if (loginByUserName()) { + response = await Api.login(userName, password); + } else { + response = await Api.loginByClientId(userName, password); + } + if (response.success) { + loginSuccess(response, userName, password); + return success; + } else if (loginByUserName() && response.code == 401) { + //可能用户使用的是老版本qinglong + HttpResponse oldResponse = + await Api.loginOld(userName, password); + if (oldResponse.success) { + loginSuccess(oldResponse, userName, password); + return success; + } else { + (oldResponse.message ?? "请检查网络情况").toast(); + if (oldResponse.code == 420) { + return twiceLogin; + } else { + return failed; + } + } + } else { + (response.message ?? "请检查网络情况").toast(); + //420代表需要2步验证 + if (response.code == 420) { + return twiceLogin; + } else { + return failed; + } + } + } + + Future loginTwice(String code) async { + HttpResponse response = + await Api.loginTwo(userName, password, code); + if (response.success) { + loginSuccess(response, userName, password); + return success; + } else { + (response.message ?? "请检查网络情况").toast(); + return failed; + } + } + + void loginSuccess( + HttpResponse response, String userName, String password) { + getIt().updateToken(response.bean?.token ?? ""); + if (rememberPassword) { + getIt() + .updateUserName(host, userName, password, !loginByUserName()); + } else { + getIt() + .updateUserName(host, "", "", !loginByUserName()); + } + } + + bool loginByUserName() { + return !useSecretLogin; + } +}