Commit 7bce65f2 by Administrator

Merge branch 'feature-2511-opt-update-test' of http://120.24.213.56/ethan/appfra…

…me into feature-2511-opt-update-test
2 parents 401282c4 1932b376
import 'package:appframe/config/constant.dart';
import 'package:appframe/config/locator.dart'; import 'package:appframe/config/locator.dart';
import 'package:appframe/config/routes.dart'; import 'package:appframe/config/routes.dart';
import 'package:appframe/data/repositories/wechat_auth_repository.dart'; import 'package:appframe/data/repositories/wechat_auth_repository.dart';
...@@ -45,7 +44,7 @@ class LoginMainCubit extends Cubit<LoginMainState> { ...@@ -45,7 +44,7 @@ class LoginMainCubit extends Cubit<LoginMainState> {
LoginMainCubit(super.initialState) { LoginMainCubit(super.initialState) {
_fluwx = getIt.get<Fluwx>(); _fluwx = getIt.get<Fluwx>();
_fluwx.addSubscriber(_responseListener); _fluwx.addSubscriber(_responseListener);
_wechatAuthRepository = getIt<WechatAuthRepository>(); _wechatAuthRepository = getIt.get<WechatAuthRepository>();
} }
void toggleAgreed(bool value) { void toggleAgreed(bool value) {
...@@ -83,6 +82,10 @@ class LoginMainCubit extends Cubit<LoginMainState> { ...@@ -83,6 +82,10 @@ class LoginMainCubit extends Cubit<LoginMainState> {
router.go('/loginPhone'); router.go('/loginPhone');
} }
void goLoginQr() {
router.go('/loginQr');
}
void _responseListener(WeChatResponse response) async { void _responseListener(WeChatResponse response) async {
if (response is WeChatAuthResponse) { if (response is WeChatAuthResponse) {
if (response.code == null || response.code == '') { if (response.code == null || response.code == '') {
...@@ -91,6 +94,10 @@ class LoginMainCubit extends Cubit<LoginMainState> { ...@@ -91,6 +94,10 @@ class LoginMainCubit extends Cubit<LoginMainState> {
} }
dynamic resultData = await _wechatAuthRepository.codeToSk(response.code!); dynamic resultData = await _wechatAuthRepository.codeToSk(response.code!);
// 后续添加错误处理
if (resultData['resultCode'] != '001') {
return;
}
var data = resultData['data']; var data = resultData['data'];
var role = data['roles'][0]; var role = data['roles'][0];
...@@ -123,7 +130,7 @@ class LoginMainCubit extends Cubit<LoginMainState> { ...@@ -123,7 +130,7 @@ class LoginMainCubit extends Cubit<LoginMainState> {
@override @override
Future<void> close() { Future<void> close() {
_fluwx.removeSubscriber(_responseListener); _fluwx.clearSubscribers();
return super.close(); return super.close();
} }
} }
import 'dart:convert';
import 'dart:typed_data';
import 'package:appframe/config/locator.dart';
import 'package:appframe/config/routes.dart';
import 'package:appframe/data/repositories/wechat_auth_repository.dart';
import 'package:crypto/crypto.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:fluwx/fluwx.dart';
import 'package:shared_preferences/shared_preferences.dart';
class LoginQrState extends Equatable {
final int status;
final Uint8List? image;
final String tip;
const LoginQrState({
this.status = 0,
this.image,
this.tip = '',
});
@override
List<Object?> get props => [
status,
image,
tip,
];
LoginQrState copyWith({
int? status,
Uint8List? image,
String? tip,
}) {
return LoginQrState(
status: status ?? this.status,
image: image ?? this.image,
tip: tip ?? this.tip,
);
}
}
class LoginQrCubit extends Cubit<LoginQrState> {
late final Fluwx _fluwx;
late final WechatAuthRepository _wechatAuthRepository;
LoginQrCubit(super.initialState) {
_fluwx = getIt.get<Fluwx>();
_wechatAuthRepository = getIt.get<WechatAuthRepository>();
init();
}
Future<void> init() async {
// sdk_ticket
var result = await _wechatAuthRepository.getTicket();
// 后续添加错误处理
if (result['resultCode'] != '001') {
print("获取 sdk_ticket 失败");
return;
}
print('获取 sdk_ticket 成功');
var sdkTicket = result['data'];
// 当前时间戳
var timestamp = DateTime.now().millisecondsSinceEpoch;
var signature = _sig('wx8c32ea248f0c7765', '$timestamp', sdkTicket, '$timestamp');
print('开始处理二维码登录');
_fluwx.addSubscriber(_responseListener);
var authResult = await _fluwx.authBy(
which: QRCode(
appId: 'wx8c32ea248f0c7765',
scope: 'snsapi_userinfo',
nonceStr: '$timestamp',
timestamp: '$timestamp',
signature: signature,
),
);
print('AuthResult $authResult');
print('结束处理二维码');
}
void _responseListener(WeChatResponse response) async {
print('回调。。。。。。。。。');
if (response is WeChatAuthGotQRCodeResponse) {
print('收到二维码。。。');
int? errCode = response.errCode;
if (errCode != null && errCode == 0) {
emit(state.copyWith(status: 1, image: response.qrCode, tip: '打开微信,扫描二维码登录'));
} else {
emit(state.copyWith(tip: '错误 $errCode'));
}
} else if (response is WeChatQRCodeScannedResponse) {
print('已扫描二维码。。。');
int? errCode = response.errCode;
if (errCode != null && errCode == 0) {
emit(state.copyWith(status: 2, tip: '在微信中轻触允许即可登录'));
} else {
emit(state.copyWith(tip: '错误 $errCode'));
}
} else if (response is WeChatAuthByQRCodeFinishedResponse) {
print('确认二维码。。。');
int? errCode = response.errCode;
if (errCode != null && errCode == 0) {
_doLogin(response.authCode!);
} else {
emit(state.copyWith(status: 3, tip: '你已取消此次登录'));
}
}
}
String _sig(String appId, String nonceStr, String sdkTicket, String timestamp) {
String str = 'appid=$appId&noncestr=$nonceStr&sdk_ticket=$sdkTicket&timestamp=$timestamp';
return sha1.convert(utf8.encode(str)).toString();
}
Future<void> _doLogin(String code) async {
dynamic resultData = await _wechatAuthRepository.codeToSk(code);
// 后续添加错误处理
if (resultData['resultCode'] != '001') {
return;
}
var data = resultData['data'];
var role = data['roles'][0];
final sessionCode = data['sessionCode'];
final userCode = data['userCode'];
final classCode = role['classCode'];
final userType = role['userType'];
final stuId = role['stuId'];
var sharedPreferences = getIt.get<SharedPreferences>();
sharedPreferences.setString('auth_sessionCode', sessionCode);
sharedPreferences.setString('auth_userCode', userCode);
sharedPreferences.setString('auth_classCode', classCode);
sharedPreferences.setInt('auth_userType', userType);
sharedPreferences.setString('auth_stuId', stuId ?? '');
router.go(
'/web',
extra: {
'sessionCode': sessionCode,
'userCode': userCode,
'classCode': classCode,
'userType': userType,
'stuId': stuId,
},
);
}
void goLoginMain() {
router.go('/loginMain');
}
@override
Future<void> close() {
_fluwx.stopAuthByQRCode();
_fluwx.clearSubscribers();
return super.close();
}
}
...@@ -12,7 +12,6 @@ import 'package:appframe/services/im_service.dart'; ...@@ -12,7 +12,6 @@ import 'package:appframe/services/im_service.dart';
import 'package:appframe/services/local_server_service.dart'; import 'package:appframe/services/local_server_service.dart';
import 'package:appframe/services/player_service.dart'; import 'package:appframe/services/player_service.dart';
import 'package:appframe/services/recorder_service.dart'; import 'package:appframe/services/recorder_service.dart';
import 'package:appframe/utils/zip_util.dart';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
...@@ -87,8 +86,8 @@ class WebState extends Equatable { ...@@ -87,8 +86,8 @@ class WebState extends Equatable {
this.chooseImageCmdMessage = '', this.chooseImageCmdMessage = '',
this.chooseVideoCmdFlag = false, this.chooseVideoCmdFlag = false,
this.chooseVideoCmdMessage = '', this.chooseVideoCmdMessage = '',
String? h5Version, this.h5Version = '',
}) : this.h5Version = h5Version ?? getIt.get<SharedPreferences>().getString("h5_version") ?? Constant.h5Version; });
WebState copyWith({ WebState copyWith({
int? selectedIndex, int? selectedIndex,
...@@ -193,35 +192,39 @@ class WebCubit extends Cubit<WebState> { ...@@ -193,35 +192,39 @@ class WebCubit extends Cubit<WebState> {
} }
Future<void> _init() async { Future<void> _init() async {
// 当前使用的H5版本
var curVersion = getIt.get<SharedPreferences>().getString(Constant.h5VersionKey) ?? Constant.h5Version;
try { try {
// 获取版本信息 // 获取版本信息
var versionConfig = await _getVersionConfig(); var versionConfig = await _getVersionConfig();
var correctVersion = versionConfig['version'] as String; var configVersion = versionConfig['version'] as String;
var downloadUrl = versionConfig['zip'] as String; var downloadUrl = versionConfig['zip'] as String;
var force = versionConfig['force'] as String; var force = versionConfig['force'] as String;
// 当前使用的H5版本
var curVersion = getIt.get<SharedPreferences>().getString('h5_version') ?? Constant.h5Version;
// 版本不一致则需要升级 // 版本不一致则需要升级
if (curVersion != correctVersion) { // 需要强制升级时,一直等待下载完成
// 不需要强制升级时,异步下载,下载完后弹框提示用户进行确认操作
if (curVersion != configVersion) {
if (force == "1") { if (force == "1") {
// 一直等待升级完成 // 一直等待升级完成
// 遮罩界面 // 遮罩界面
emit(state.copyWith(isUpgrading: true)); emit(state.copyWith(isUpgrading: true));
await _upgrade(correctVersion, downloadUrl); await _downloadH5Zip(configVersion, downloadUrl);
// 升级完成后取消遮罩,继续初始化其它数据 _setH5Version(configVersion);
// 下载完成后取消遮罩,继续初始化其它数据
emit(state.copyWith(isUpgrading: false)); emit(state.copyWith(isUpgrading: false));
} else { } else {
// 后台下载,完成后提示用户 // 后台下载,完成后提示用户
_upgrade(correctVersion, downloadUrl).then( _downloadH5Zip(configVersion, downloadUrl).then(
(value) { (value) {
_setH5Version(configVersion);
emit(state.copyWith(suggestUpgrade: true)); emit(state.copyWith(suggestUpgrade: true));
}, },
); );
} }
} }
} catch (e) { } catch (e) {
emit(state.copyWith(isUpgrading: false));
print('升级检测处理失败'); print('升级检测处理失败');
print(e); print(e);
} }
...@@ -238,6 +241,9 @@ class WebCubit extends Cubit<WebState> { ...@@ -238,6 +241,9 @@ class WebCubit extends Cubit<WebState> {
// 加载H5页面 // 加载H5页面
_loadHtml(); _loadHtml();
// 读取 h5 版本号
_readH5ShowVersion();
// 登录IM // 登录IM
_loginIM(); _loginIM();
...@@ -264,6 +270,7 @@ class WebCubit extends Cubit<WebState> { ...@@ -264,6 +270,7 @@ class WebCubit extends Cubit<WebState> {
String zip = response.data['zip'] as String; String zip = response.data['zip'] as String;
return { return {
'version': version, 'version': version,
// 'force': "0",
'force': force, 'force': force,
// 'zip': 'http://192.168.2.177/1.0.0.zip', // 'zip': 'http://192.168.2.177/1.0.0.zip',
'zip': '$zip$version.zip', 'zip': '$zip$version.zip',
...@@ -273,7 +280,7 @@ class WebCubit extends Cubit<WebState> { ...@@ -273,7 +280,7 @@ class WebCubit extends Cubit<WebState> {
} }
} }
Future<void> _upgrade(String version, String zipUrl) async { Future<void> _downloadH5Zip(String version, String zipUrl) async {
Dio dio = Dio(); Dio dio = Dio();
try { try {
// 下载zip文件 // 下载zip文件
...@@ -301,20 +308,15 @@ class WebCubit extends Cubit<WebState> { ...@@ -301,20 +308,15 @@ class WebCubit extends Cubit<WebState> {
// 删除临时文件 // 删除临时文件
await tempZipFile.delete(); await tempZipFile.delete();
// 解压zip文件
String targetDir = '$httpDirPath/$version';
var result = await ZipUtil.extractZipFile(saveZipFilePath, targetDir);
if (!result) {
throw Exception('文件解压失败');
}
var sharedPreferences = await SharedPreferences.getInstance();
await sharedPreferences.setString('h5_version', version);
} finally { } finally {
dio.close(force: true); dio.close(force: true);
} }
} }
void _setH5Version(String version) {
getIt.get<SharedPreferences>().setString(Constant.h5VersionKey, version);
}
Future<void> _startLocalServer() async { Future<void> _startLocalServer() async {
// 启动本地服务器 // 启动本地服务器
_server = await getIt.get<LocalServerService>().startLocalServer(); _server = await getIt.get<LocalServerService>().startLocalServer();
...@@ -359,6 +361,11 @@ class WebCubit extends Cubit<WebState> { ...@@ -359,6 +361,11 @@ class WebCubit extends Cubit<WebState> {
_controller.loadRequest(Uri.parse(serverUrl)); _controller.loadRequest(Uri.parse(serverUrl));
} }
void _readH5ShowVersion() {
var h5Version = getIt.get<SharedPreferences>().getString(Constant.h5ShowVersionKey) ?? 'unknown';
emit(state.copyWith(h5Version: h5Version));
}
Future<void> _loginIM() async { Future<void> _loginIM() async {
if (Constant.needIM) { if (Constant.needIM) {
var imService = getIt.get<ImService>(); var imService = getIt.get<ImService>();
...@@ -485,7 +492,7 @@ class WebCubit extends Cubit<WebState> { ...@@ -485,7 +492,7 @@ class WebCubit extends Cubit<WebState> {
// 1 清理非 h5_version 的缓存 // 1 清理非 h5_version 的缓存
var sharedPreferences = getIt.get<SharedPreferences>(); var sharedPreferences = getIt.get<SharedPreferences>();
sharedPreferences.getKeys().forEach((key) async { sharedPreferences.getKeys().forEach((key) async {
if (!key.startsWith('h5_version')) { if (!key.startsWith('h5')) {
await sharedPreferences.remove(key); await sharedPreferences.remove(key);
} }
}); });
...@@ -494,7 +501,7 @@ class WebCubit extends Cubit<WebState> { ...@@ -494,7 +501,7 @@ class WebCubit extends Cubit<WebState> {
var dir = await getApplicationSupportDirectory(); var dir = await getApplicationSupportDirectory();
var httpDir = Directory('${dir.path}/${Constant.h5DistDir}'); var httpDir = Directory('${dir.path}/${Constant.h5DistDir}');
if (httpDir.existsSync()) { if (httpDir.existsSync()) {
var version = sharedPreferences.getString('h5_version') ?? Constant.h5Version; var version = sharedPreferences.getString(Constant.h5VersionKey) ?? Constant.h5Version;
await for (final FileSystemEntity entity in httpDir.list()) { await for (final FileSystemEntity entity in httpDir.list()) {
if (entity is Directory) { if (entity is Directory) {
...@@ -554,9 +561,9 @@ class WebCubit extends Cubit<WebState> { ...@@ -554,9 +561,9 @@ class WebCubit extends Cubit<WebState> {
/// ///
/// 升级提示 /// 升级提示
/// ///
void suggestUpgrade(BuildContext context) { void suggestUpgrade(BuildContext ctx) {
showDialog( showDialog(
context: context, context: ctx,
barrierDismissible: false, barrierDismissible: false,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
...@@ -566,18 +573,16 @@ class WebCubit extends Cubit<WebState> { ...@@ -566,18 +573,16 @@ class WebCubit extends Cubit<WebState> {
TextButton( TextButton(
child: Text('取消'), child: Text('取消'),
onPressed: () { onPressed: () {
emit(state.copyWith(suggestUpgrade: false));
Navigator.of(context).pop(); Navigator.of(context).pop();
emit(state.copyWith(suggestUpgrade: false));
}, },
), ),
TextButton( TextButton(
child: Text('确定'), child: Text('确定'),
onPressed: () { onPressed: () {
emit(state.copyWith(suggestUpgrade: false));
getIt.get<LocalServerService>().resetHttpDirectory();
_controller.reload();
// _loadHtml();
Navigator.of(context).pop(); Navigator.of(context).pop();
emit(state.copyWith(suggestUpgrade: false));
router.go('/reload');
}, },
), ),
], ],
...@@ -628,6 +633,14 @@ class WebCubit extends Cubit<WebState> { ...@@ -628,6 +633,14 @@ class WebCubit extends Cubit<WebState> {
} }
void _chooseImageFromAlbum(BuildContext context, int count, String unique, String cmd) async { void _chooseImageFromAlbum(BuildContext context, int count, String unique, String cmd) async {
// 检查是否已被永久拒绝,此时需要对用户进行引导
if (await _checkGalleryPermanentlyDenied()) {
var resp = {'unique': unique, 'cmd': cmd, 'data': null, 'errMsg': 'no auth'};
_sendResponse(resp);
_permissionLead(context, '相册权限');
return;
}
final List<AssetEntity>? result; final List<AssetEntity>? result;
try { try {
result = await AssetPicker.pickAssets( result = await AssetPicker.pickAssets(
...@@ -643,12 +656,6 @@ class WebCubit extends Cubit<WebState> { ...@@ -643,12 +656,6 @@ class WebCubit extends Cubit<WebState> {
} catch (e) { } catch (e) {
var resp = {'unique': unique, 'cmd': cmd, 'data': null, 'errMsg': 'no auth'}; var resp = {'unique': unique, 'cmd': cmd, 'data': null, 'errMsg': 'no auth'};
_sendResponse(resp); _sendResponse(resp);
// 权限异常之后,检查是否已被永久拒绝,此时需要对用户进行引导
if (await _checkGalleryPermanentlyDenied()) {
_permissionLead(context, '相册权限');
}
return; return;
} }
...@@ -675,17 +682,20 @@ class WebCubit extends Cubit<WebState> { ...@@ -675,17 +682,20 @@ class WebCubit extends Cubit<WebState> {
} }
void _chooseImageFromCamera(BuildContext context, String unique, String cmd) async { void _chooseImageFromCamera(BuildContext context, String unique, String cmd) async {
// 检查是否已被永久拒绝,此时需要对用户进行引导
if (await _checkCameraPermanentlyDenied()) {
var resp = {'unique': unique, 'cmd': cmd, 'data': null, 'errMsg': 'no auth'};
_sendResponse(resp);
_permissionLead(context, '相机权限');
return;
}
AssetEntity? asset; AssetEntity? asset;
try { try {
asset = await CameraPicker.pickFromCamera(context, pickerConfig: const CameraPickerConfig()); asset = await CameraPicker.pickFromCamera(context, pickerConfig: const CameraPickerConfig());
} catch (e) { } catch (e) {
var resp = {'unique': unique, 'cmd': cmd, 'data': null, 'errMsg': 'no auth'}; var resp = {'unique': unique, 'cmd': cmd, 'data': null, 'errMsg': 'no auth'};
_sendResponse(resp); _sendResponse(resp);
if (await _checkCameraPermanentlyDenied()) {
_permissionLead(context, '相机权限');
}
return; return;
} }
...@@ -831,8 +841,15 @@ class WebCubit extends Cubit<WebState> { ...@@ -831,8 +841,15 @@ class WebCubit extends Cubit<WebState> {
} }
void _chooseVideoFromAlbum(BuildContext context, int count, String unique, String cmd) async { void _chooseVideoFromAlbum(BuildContext context, int count, String unique, String cmd) async {
List<AssetEntity>? result; // 检查是否已被永久拒绝,此时需要对用户进行引导
if (await _checkGalleryPermanentlyDenied()) {
var resp = {'unique': unique, 'cmd': cmd, 'data': null, 'errMsg': 'no auth'};
_sendResponse(resp);
_permissionLead(context, '相册权限');
return;
}
List<AssetEntity>? result;
try { try {
result = await AssetPicker.pickAssets( result = await AssetPicker.pickAssets(
context, context,
...@@ -845,12 +862,6 @@ class WebCubit extends Cubit<WebState> { ...@@ -845,12 +862,6 @@ class WebCubit extends Cubit<WebState> {
} catch (e) { } catch (e) {
var resp = {'unique': unique, 'cmd': cmd, 'data': null, 'errMsg': 'no auth'}; var resp = {'unique': unique, 'cmd': cmd, 'data': null, 'errMsg': 'no auth'};
_sendResponse(resp); _sendResponse(resp);
// 权限异常之后,检查是否已被永久拒绝,此时需要对用户进行引导
if (await _checkGalleryPermanentlyDenied()) {
_permissionLead(context, '相册权限');
}
return; return;
} }
...@@ -877,6 +888,14 @@ class WebCubit extends Cubit<WebState> { ...@@ -877,6 +888,14 @@ class WebCubit extends Cubit<WebState> {
} }
void _chooseVideoFromCamera(BuildContext context, int maxDuration, String unique, String cmd) async { void _chooseVideoFromCamera(BuildContext context, int maxDuration, String unique, String cmd) async {
// 检查是否已被永久拒绝,此时需要对用户进行引导
if (await _checkCameraPermanentlyDenied()) {
var resp = {'unique': unique, 'cmd': cmd, 'data': null, 'errMsg': 'no auth'};
_sendResponse(resp);
_permissionLead(context, '相机权限');
return;
}
AssetEntity? asset; AssetEntity? asset;
try { try {
asset = await CameraPicker.pickFromCamera( asset = await CameraPicker.pickFromCamera(
...@@ -891,11 +910,6 @@ class WebCubit extends Cubit<WebState> { ...@@ -891,11 +910,6 @@ class WebCubit extends Cubit<WebState> {
} catch (e) { } catch (e) {
var resp = {'unique': unique, 'cmd': cmd, 'data': null, 'errMsg': 'no auth'}; var resp = {'unique': unique, 'cmd': cmd, 'data': null, 'errMsg': 'no auth'};
_sendResponse(resp); _sendResponse(resp);
if (await _checkCameraPermanentlyDenied()) {
_permissionLead(context, '相机权限');
}
return; return;
} }
......
import 'package:appframe/config/evn_config.dart';
class Constant { class Constant {
/// 应用内部 http 服务 /// 应用内部 http 服务
static const int localServerPort = 35982; static const int localServerPort = 35982;
...@@ -19,11 +21,20 @@ class Constant { ...@@ -19,11 +21,20 @@ class Constant {
/// app 版本号规则 /// app 版本号规则
static const String appVersion = '1.0.2512031'; static const String appVersion = '1.0.2512031';
// h5的起始终最低版本号规则 /// H5的起始终最低版本号规则
static const String h5Version = '1.0.0'; static const String h5Version = '1.0.0';
/// H5的版本号存储的key
static const String h5VersionKey = 'h5_version';
/// 用于显示的H5版本号存储的key
static const String h5ShowVersionKey = 'h5_show_version';
/// H5版本号配置文件地址 /// H5版本号配置文件地址
static const String configUrl = 'https://bxe-obs.banxiaoer.com/conf/xeapp_conf_dev.json'; static const String configUrl = EnvConfig.env == 'dev'
? 'https://bxe-obs.banxiaoer.com/conf/xeapp_conf_dev.json'
// ? 'http://192.168.2.177/xeapp_conf_dev.json'
: 'https://bxe-obs.banxiaoer.com/conf/xeapp_conf_pro.json';
/// 内部 H5 dist 目录 /// 内部 H5 dist 目录
static const String h5DistDir = 'http_dist_assets'; static const String h5DistDir = 'http_dist_assets';
...@@ -33,5 +44,5 @@ class Constant { ...@@ -33,5 +44,5 @@ class Constant {
static const String imClientSecure = 'kM4yqbehB3io9UiLvH6eHvM7xAhfYxoyyaO1tLoHgKltcaI7MZXkUbpFaWdeQIqe'; static const String imClientSecure = 'kM4yqbehB3io9UiLvH6eHvM7xAhfYxoyyaO1tLoHgKltcaI7MZXkUbpFaWdeQIqe';
/// 测试阶段使用 /// 测试阶段使用
static const bool needIM = true; static const bool needIM = false;
} }
class EnvConfig {
static const String env = String.fromEnvironment('env', defaultValue: 'dev');
static bool isDev() {
return env == 'dev';
}
}
...@@ -3,6 +3,8 @@ import 'package:appframe/ui/pages/im_page.dart'; ...@@ -3,6 +3,8 @@ import 'package:appframe/ui/pages/im_page.dart';
import 'package:appframe/ui/pages/link_page.dart'; import 'package:appframe/ui/pages/link_page.dart';
import 'package:appframe/ui/pages/login_main_page.dart'; import 'package:appframe/ui/pages/login_main_page.dart';
import 'package:appframe/ui/pages/login_phone_page.dart'; import 'package:appframe/ui/pages/login_phone_page.dart';
import 'package:appframe/ui/pages/login_qr_page.dart';
import 'package:appframe/ui/pages/reload_page.dart';
import 'package:appframe/ui/pages/scan_code_page.dart'; import 'package:appframe/ui/pages/scan_code_page.dart';
import 'package:appframe/ui/pages/web_page.dart'; import 'package:appframe/ui/pages/web_page.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
...@@ -42,6 +44,12 @@ final GoRouter router = GoRouter( ...@@ -42,6 +44,12 @@ final GoRouter router = GoRouter(
}, },
), ),
GoRoute( GoRoute(
path: '/loginQr',
builder: (BuildContext context, GoRouterState state) {
return const LoginQrPage();
},
),
GoRoute(
path: '/adv', path: '/adv',
builder: (BuildContext context, GoRouterState state) { builder: (BuildContext context, GoRouterState state) {
return const AdvPage(); return const AdvPage();
...@@ -53,5 +61,11 @@ final GoRouter router = GoRouter( ...@@ -53,5 +61,11 @@ final GoRouter router = GoRouter(
return const ImPage(); return const ImPage();
}, },
), ),
GoRoute(
path: '/reload',
builder: (BuildContext context, GoRouterState state) {
return const ReloadPage();
},
),
], ],
); );
import 'package:appframe/config/constant.dart';
import 'package:appframe/config/locator.dart'; import 'package:appframe/config/locator.dart';
import 'package:appframe/services/dispatcher.dart'; import 'package:appframe/services/dispatcher.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
...@@ -59,7 +60,7 @@ class ClearStorageHandler extends MessageHandler { ...@@ -59,7 +60,7 @@ class ClearStorageHandler extends MessageHandler {
Future<dynamic> handleMessage(dynamic params) async { Future<dynamic> handleMessage(dynamic params) async {
var sharedPreferences = getIt.get<SharedPreferences>(); var sharedPreferences = getIt.get<SharedPreferences>();
sharedPreferences.getKeys().forEach((key) async { sharedPreferences.getKeys().forEach((key) async {
if (!key.startsWith('h5_version')) { if (!key.startsWith('h5_')) {
await sharedPreferences.remove(key); await sharedPreferences.remove(key);
} }
}); });
......
...@@ -22,4 +22,18 @@ class WechatAuthRepository { ...@@ -22,4 +22,18 @@ class WechatAuthRepository {
return resp.data; return resp.data;
} }
Future<dynamic> getTicket() async {
Response resp = await _apiService.post(
'/login/applet/wkbxe/getTicketByApp?version=1.0.0',
{
"appCode": "bxeapp",
"params": {},
},
);
print('获取ticket: $resp');
return resp.data;
}
} }
...@@ -44,10 +44,6 @@ class LocalServerService { ...@@ -44,10 +44,6 @@ class LocalServerService {
return server; return server;
} }
void resetHttpDirectory() {
_httpDirectory = null;
}
// 目录下的文件 // 目录下的文件
Future<void> _serveTempFile(HttpRequest request, String requestPath) async { Future<void> _serveTempFile(HttpRequest request, String requestPath) async {
try { try {
...@@ -149,7 +145,7 @@ class LocalServerService { ...@@ -149,7 +145,7 @@ class LocalServerService {
} }
Future<void> _initHttpDirectory() async { Future<void> _initHttpDirectory() async {
var version = getIt.get<SharedPreferences>().getString('h5_version') ?? Constant.h5Version; var version = getIt.get<SharedPreferences>().getString(Constant.h5VersionKey) ?? Constant.h5Version;
var direct = await getApplicationSupportDirectory(); var direct = await getApplicationSupportDirectory();
_httpDirectory = '${direct.path}/${Constant.h5DistDir}/$version'; _httpDirectory = '${direct.path}/${Constant.h5DistDir}/$version';
} }
...@@ -163,12 +159,26 @@ class LocalServerService { ...@@ -163,12 +159,26 @@ class LocalServerService {
// } // }
// 判断H5打包文件是否存在,不存在则从assets中解压 // 判断H5打包文件是否存在,不存在则从assets中解压
var version = getIt.get<SharedPreferences>().getString('h5_version') ?? Constant.h5Version; var version = getIt.get<SharedPreferences>().getString(Constant.h5VersionKey) ?? Constant.h5Version;
var dir = await getApplicationSupportDirectory(); var dir = await getApplicationSupportDirectory();
var distFilePath = '${dir.path}/${Constant.h5DistDir}/$version.zip'; var distFilePath = '${dir.path}/${Constant.h5DistDir}/$version.zip';
if (!File(distFilePath).existsSync()) { if (!File(distFilePath).existsSync()) {
distFilePath = 'assets/dist.zip'; distFilePath = 'assets/dist.zip';
} }
// 解压
await ZipUtil.extractZipFile(distFilePath, outputDirectory); await ZipUtil.extractZipFile(distFilePath, outputDirectory);
// 用于显示的版本号
await _getAndSetShowVersion(outputDirectory);
}
// 读取和设置用于显示的版本号
Future<void> _getAndSetShowVersion(String outputDirectory) async {
var versionFile = File('$outputDirectory/version.txt');
if (await versionFile.exists()) {
var content = (await versionFile.readAsString()).trim();
getIt.get<SharedPreferences>().setString(Constant.h5ShowVersionKey, content);
} else {
getIt.get<SharedPreferences>().setString(Constant.h5ShowVersionKey, 'undefined');
}
} }
} }
...@@ -23,6 +23,7 @@ class LoginMainPage extends StatelessWidget { ...@@ -23,6 +23,7 @@ class LoginMainPage extends StatelessWidget {
top: false, top: false,
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
LoginPageImageWidget(), LoginPageImageWidget(),
Transform.translate( Transform.translate(
...@@ -48,7 +49,8 @@ class LoginMainPage extends StatelessWidget { ...@@ -48,7 +49,8 @@ class LoginMainPage extends StatelessWidget {
), ),
), ),
], ],
)), ),
),
), ),
), ),
state.loading state.loading
...@@ -61,7 +63,6 @@ class LoginMainPage extends StatelessWidget { ...@@ -61,7 +63,6 @@ class LoginMainPage extends StatelessWidget {
children: [ children: [
CircularProgressIndicator( CircularProgressIndicator(
color: Color(0xFF7691FA), color: Color(0xFF7691FA),
), ),
], ],
), ),
...@@ -112,6 +113,32 @@ class LoginMainPage extends StatelessWidget { ...@@ -112,6 +113,32 @@ class LoginMainPage extends StatelessWidget {
height: 47, height: 47,
child: ElevatedButton( child: ElevatedButton(
onPressed: () { onPressed: () {
loginMainCubit.goLoginQr();
},
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF00CB60),
foregroundColor: Colors.white,
textStyle: TextStyle(fontSize: 19),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(23.5),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('assets/images/login/wechat_white_icon.png'),
SizedBox(width: 6),
Text('扫码登录'),
],
),
),
),
SizedBox(height: 15),
SizedBox(
width: double.infinity,
height: 47,
child: ElevatedButton(
onPressed: () {
loginMainCubit.goLoginPhone(); loginMainCubit.goLoginPhone();
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
......
...@@ -22,7 +22,9 @@ class LoginPhonePage extends StatelessWidget { ...@@ -22,7 +22,9 @@ class LoginPhonePage extends StatelessWidget {
body: SafeArea( body: SafeArea(
top: false, top: false,
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column(children: [ child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
LoginPageImageWidget(), LoginPageImageWidget(),
Transform.translate( Transform.translate(
offset: Offset(0, -40), // 向上移动40像素 offset: Offset(0, -40), // 向上移动40像素
...@@ -43,14 +45,16 @@ class LoginPhonePage extends StatelessWidget { ...@@ -43,14 +45,16 @@ class LoginPhonePage extends StatelessWidget {
SizedBox(height: 20), SizedBox(height: 20),
_buildLoginButton(), _buildLoginButton(),
SizedBox(height: 30), SizedBox(height: 30),
_buildWechatLogin(loginPhoneCubit),
SizedBox(height: 24.5),
_buildAgreement(context, loginPhoneCubit, state.agreed), _buildAgreement(context, loginPhoneCubit, state.agreed),
SizedBox(height: 24.5),
_buildWechatLogin(loginPhoneCubit),
], ],
), ),
), ),
), ),
])), ],
),
),
), ),
); );
}, },
...@@ -231,7 +235,7 @@ class LoginPhonePage extends StatelessWidget { ...@@ -231,7 +235,7 @@ class LoginPhonePage extends StatelessWidget {
Image.asset('assets/images/login/wechat.png'), Image.asset('assets/images/login/wechat.png'),
SizedBox(height: 4), SizedBox(height: 4),
Text( Text(
'微信登录', '返回微信登录',
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
color: Color(0xFF000000), color: Color(0xFF000000),
......
import 'package:appframe/bloc/login_qr_cubit.dart';
import 'package:appframe/ui/widgets/login/login_page_header_widget.dart';
import 'package:appframe/ui/widgets/login/login_page_image_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class LoginQrPage extends StatelessWidget {
const LoginQrPage({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (BuildContext context) => LoginQrCubit(LoginQrState()),
child: BlocConsumer<LoginQrCubit, LoginQrState>(
builder: (context, state) {
var loginQrCubit = context.read<LoginQrCubit>();
return Scaffold(
resizeToAvoidBottomInset: true,
backgroundColor: Colors.white,
body: SafeArea(
top: false,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
LoginPageImageWidget(),
Transform.translate(
offset: Offset(0, -40), // 向上移动40像素
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
color: Colors.white, // 设置背景色
),
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 30),
LoginPageHeaderWidget(),
SizedBox(height: 25),
Center(
child: _buildQrCode(loginQrCubit, state),
),
SizedBox(height: 30),
_buildWechatLogin(loginQrCubit),
],
),
),
),
],
),
),
),
);
},
listener: (context, state) {},
));
}
Column? _buildQrCode(LoginQrCubit loginQrCubit, LoginQrState state) {
if (state.status == 0) {
// 等待二维码数据
return Column(
children: [
Column(
children: [
SizedBox(height: 40),
CircularProgressIndicator(),
SizedBox(height: 12),
Text(
'正在生成二维码...',
),
SizedBox(height: 40),
],
),
SizedBox(height: 12),
Text(
state.tip,
),
],
);
} else if (state.status == 1) {
// 等待扫码
return Column(
children: [
Image.memory(
state.image!,
width: 200,
height: 200,
),
SizedBox(height: 12),
Text(
state.tip,
),
],
);
} else if (state.status == 2) {
// 已扫码,等待确认
return Column(
children: [
// Image.asset("assets/qr_suc.png"),
Text(
"已扫码",
),
SizedBox(height: 12),
Text(
state.tip,
),
],
);
} else if (state.status == 3) {
// 拒绝
return Column(
children: [
// Image.asset("assets/qr_fail.png"),
Text(
"拒绝登录",
),
SizedBox(height: 12),
Text(
state.tip,
),
],
);
}
return null;
}
Widget _buildWechatLogin(LoginQrCubit loginQrCubit) {
return SizedBox(
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () {
loginQrCubit.goLoginMain();
},
child: Column(
children: [
Image.asset('assets/images/login/wechat.png'),
SizedBox(height: 4),
Text(
'返回微信登录',
style: TextStyle(
fontSize: 14,
color: Color(0xFF000000),
),
),
],
),
),
],
),
);
}
}
import 'package:appframe/config/routes.dart';
import 'package:flutter/material.dart';
///
/// 用于重新加载的中间路由
///
class ReloadPage extends StatefulWidget {
const ReloadPage({super.key});
@override
State<ReloadPage> createState() => _ReloadPageState();
}
class _ReloadPageState extends State<ReloadPage> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
// 界面显示完成后执行的操作
_performPostDisplayOperations();
});
}
void _performPostDisplayOperations() {
router.go('/web');
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircularProgressIndicator(),
const SizedBox(height: 16),
Text('加载中...'),
],
),
),
);
}
}
...@@ -269,8 +269,9 @@ class WebPage extends StatelessWidget { ...@@ -269,8 +269,9 @@ class WebPage extends StatelessWidget {
child: Text('确认'), child: Text('确认'),
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
ctx.read<WebCubit>().clearStorage(); var webCubit = ctx.read<WebCubit>();
ctx.read<WebCubit>().goLogin(); webCubit.clearStorage();
webCubit.goLogin();
}, },
), ),
], ],
......
...@@ -26,6 +26,7 @@ dependencies: ...@@ -26,6 +26,7 @@ dependencies:
path_provider: ^2.1.5 path_provider: ^2.1.5
shared_preferences: ^2.5.3 shared_preferences: ^2.5.3
uuid: ^4.5.1 uuid: ^4.5.1
crypto: ^3.0.7
# --- 路由与权限 --- # --- 路由与权限 ---
go_router: ^16.2.1 go_router: ^16.2.1
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!