From 3a94281310ecf86ccdd0a5d21853b9ed38dceff6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 03:14:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20web=E7=AB=AF=E6=89=AB=E7=A0=81=E7=99=BB?= =?UTF-8?q?=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 12 +++- lib/http/login.dart | 27 +++++++ lib/pages/login/controller.dart | 35 +++++++++ lib/pages/login/view.dart | 121 ++++++++++++++++++++++++++------ pubspec.lock | 16 +++++ pubspec.yaml | 2 + 6 files changed, 190 insertions(+), 23 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index e0b92c02..b0fc0556 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -405,11 +405,19 @@ class Api { // web端验证码登录 static const String webSmsLogin = - "${HttpString.passBaseUrl}/x/passport-login/web/login/sms"; + '${HttpString.passBaseUrl}/x/passport-login/web/login/sms'; // web端密码登录 static const String loginInByWebPwd = - "${HttpString.passBaseUrl}/x/passport-login/web/login"; + '${HttpString.passBaseUrl}/x/passport-login/web/login'; + + // web端二维码 + static const String qrCodeApi = + '${HttpString.passBaseUrl}/x/passport-login/web/qrcode/generate'; + + // 扫码登录 + static const String loginInByQrcode = + '${HttpString.passBaseUrl}/x/passport-login/web/qrcode/poll'; // app端短信验证码 static const String appSmsCode = diff --git a/lib/http/login.dart b/lib/http/login.dart index a97360c7..2437b72a 100644 --- a/lib/http/login.dart +++ b/lib/http/login.dart @@ -252,4 +252,31 @@ class LoginHttp { return {'status': false, 'data': [], 'msg': res.data['message']}; } } + + // web端登录二维码 + static Future getWebQrcode() async { + var res = await Request().get(Api.qrCodeApi); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } + } + + // web端二维码轮询登录状态 + static Future queryWebQrcodeStatus(String qrcodeKey) async { + var res = await Request() + .get(Api.loginInByQrcode, data: {'qrcode_key': qrcodeKey}); + if (res.data['data']['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } + } } diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index 354076f9..b5ddba16 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -42,6 +42,10 @@ class LoginPageController extends GetxController { late int tel; late int webSmsCode; + RxInt validSeconds = 180.obs; + late Timer validTimer; + late String qrcodeKey; + // 监听pageView切换 void onPageChange(int index) { currentIndex.value = index; @@ -298,4 +302,35 @@ class LoginPageController extends GetxController { } }); } + + // 获取登录二维码 + Future getWebQrcode() async { + var res = await LoginHttp.getWebQrcode(); + validSeconds.value = 180; + if (res['status']) { + qrcodeKey = res['data']['qrcode_key']; + validTimer = Timer.periodic(const Duration(seconds: 1), (validTimer) { + if (validSeconds.value > 0) { + validSeconds.value--; + queryWebQrcodeStatus(); + } else { + getWebQrcode(); + validTimer.cancel(); + } + }); + return res; + } else { + SmartDialog.showToast(res['msg']); + } + } + + // 轮询二维码登录状态 + Future queryWebQrcodeStatus() async { + var res = await LoginHttp.queryWebQrcodeStatus(qrcodeKey); + if (res['status']) { + await LoginUtils.confirmLogin('', null); + validTimer.cancel(); + Get.back(); + } + } } diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index 2f406706..cd91ea26 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:qr_flutter/qr_flutter.dart'; import 'controller.dart'; @@ -37,6 +38,105 @@ class _LoginPageState extends State { icon: const Icon(Icons.arrow_back), ), ), + actions: [ + IconButton( + tooltip: '浏览器打开', + onPressed: () { + Get.offNamed( + '/webview', + parameters: { + 'url': 'https://passport.bilibili.com/h5-app/passport/login', + 'type': 'login', + 'pageTitle': '登录bilibili', + }, + ); + }, + icon: const Icon(Icons.language), + ), + IconButton( + tooltip: '二维码登录', + onPressed: () { + showDialog( + context: context, + builder: (context) { + return StatefulBuilder( + builder: (context, StateSetter setState) { + return AlertDialog( + title: Row( + children: [ + const Text('扫码登录'), + IconButton( + onPressed: () { + setState(() {}); + }, + icon: const Icon(Icons.refresh), + ), + ], + ), + contentPadding: const EdgeInsets.fromLTRB(0, 0, 0, 4), + content: AspectRatio( + aspectRatio: 1, + child: Container( + width: 200, + padding: const EdgeInsets.all(12), + child: FutureBuilder( + future: _loginPageCtr.getWebQrcode(), + builder: (context, snapshot) { + if (snapshot.connectionState == + ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + Map data = snapshot.data as Map; + return QrImageView( + data: data['data']['url'], + backgroundColor: Colors.transparent, + ); + } else { + return const Center( + child: SizedBox( + width: 40, + height: 40, + child: CircularProgressIndicator(), + ), + ); + } + }, + ), + ), + ), + actions: [ + TextButton( + onPressed: () {}, + child: Obx(() { + return Text( + '有效期: ${_loginPageCtr.validSeconds.value}s', + style: Theme.of(context).textTheme.titleMedium, + ); + }), + ), + TextButton( + onPressed: () {}, + child: Text( + '检查登录状态', + style: TextStyle( + fontSize: Theme.of(context) + .textTheme + .titleMedium! + .fontSize, + ), + ), + ) + ], + ); + }); + }, + ); + }, + icon: const Icon(Icons.qr_code), + ), + const SizedBox(width: 22), + ], ), body: PageView( physics: const NeverScrollableScrollPhysics(), @@ -99,27 +199,6 @@ class _LoginPageState extends State { }, ), ), - GestureDetector( - onTap: () { - Get.offNamed( - '/webview', - parameters: { - 'url': - 'https://passport.bilibili.com/h5-app/passport/login', - 'type': 'login', - 'pageTitle': '登录bilibili', - }, - ); - }, - child: Padding( - padding: const EdgeInsets.only(left: 2), - child: Text( - '使用网页端登录', - style: TextStyle( - color: Theme.of(context).colorScheme.primary), - ), - ), - ), const Spacer(), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/pubspec.lock b/pubspec.lock index 406fbca4..5c6f2b43 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1218,6 +1218,22 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "3.1.0" + qr: + dependency: transitive + description: + name: qr + sha256: "64957a3930367bf97cc211a5af99551d630f2f4625e38af10edd6b19131b64b3" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.1" + qr_flutter: + dependency: "direct main" + description: + name: qr_flutter + sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097" + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.1.0" rxdart: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index f02c2ead..66187abf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -145,6 +145,8 @@ dependencies: # 投屏 dlna_dart: ^0.0.8 lottie: ^3.1.2 + # 二维码 + qr_flutter: ^4.1.0 dev_dependencies: flutter_test: