diff --git a/android/app/build.gradle b/android/app/build.gradle index e2d75a9..8dd901f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -93,5 +93,5 @@ dependencies { implementation 'com.geyifeng.immersionbar:immersionbar:3.2.2' // kotlin扩展(可选) implementation 'com.geyifeng.immersionbar:immersionbar-ktx:3.2.2' - implementation 'io.keyss.android.library:bjgame:1.0.2' + implementation 'io.keyss.android.library:bjgame:1.0.3' } diff --git a/lib/app/splash_page.dart b/lib/app/splash_page.dart index 3cf91be..0aa8159 100644 --- a/lib/app/splash_page.dart +++ b/lib/app/splash_page.dart @@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:limiting_direction_csx/limiting_direction_csx.dart'; +import 'package:wow_english/common/core/app_config_helper.dart'; import 'package:wow_english/common/core/user_util.dart'; import 'package:wow_english/common/extension/string_extension.dart'; import 'package:wow_english/common/request/config.dart'; @@ -44,9 +45,11 @@ class _TransitionViewState extends State { //Log.d('Splash读当前页面:$currentPageName'); Log.d('Splash读本地, userEntity: $userEntity'); int apartInMilliseconds = 0; + // 阻塞获取系统配置信息 + await AppConfigHelper.getAppConfig(); // 调一下接口判断一下有效性再往下 - if (userEntity != null) { - String token = userEntity.token; + if (userEntity != null && userEntity.token != null) { + String token = userEntity.token!; DateTime startTime = DateTime.now(); try { userEntity = await UserDao.getUserInfo(); diff --git a/lib/common/core/app_config_helper.dart b/lib/common/core/app_config_helper.dart new file mode 100644 index 0000000..4933c89 --- /dev/null +++ b/lib/common/core/app_config_helper.dart @@ -0,0 +1,41 @@ +import 'dart:ffi'; +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:wow_english/common/core/user_util.dart'; + +import '../../models/app_config_entity.dart'; +import '../request/dao/system_dao.dart'; + +class AppConfigHelper { + + static AppConfigEntityEntity? configEntityEntity; + + static String versionCode = ''; + + /// 获取用户信息 + static Future getAppConfig() async { + configEntityEntity = await SystemDao.getAppConfig(); + return null; + } + + // 是否需要隐藏... + static bool shouldHidePay() { + return configEntityEntity?.isAppReviewing() == true || UserUtil.getUser()?.phoneNum == "17730280759"; + } + + // 获取app版本号 + static Future getAppVersion() async { + if (versionCode.isNotEmpty) { + return versionCode; + } + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + String version = packageInfo.version; // 版本号 + String buildNumber = packageInfo.buildNumber; // 构建号 + versionCode = version; + + debugPrint('versionCode=$versionCode platForm=${Platform.operatingSystem}'); + return versionCode; + } +} diff --git a/lib/common/core/user_util.dart b/lib/common/core/user_util.dart index eb206d7..3875d57 100644 --- a/lib/common/core/user_util.dart +++ b/lib/common/core/user_util.dart @@ -11,8 +11,6 @@ import 'package:wow_english/utils/sp_util.dart'; class UserUtil { static UserEntity? _userEntity; - static String get token => _userEntity?.token ?? ''; - static void saveUser(UserEntity? user) { if (user == null) { _clearUserData(); @@ -54,7 +52,7 @@ class UserUtil { SpUtil.getInstance().remove(SpConst.prefsKeyUserInfo); } - static void logout() { + static void logout([bool showPasswordLoginPage = false]) { _clearUserData(); // 判断下不在Splash页就跳转 /*var currentPageName = ModalRoute.of(AppRouter.context)?.settings.name; @@ -62,6 +60,25 @@ class UserUtil { if (currentPageName != AppRouteName.splash) { Navigator.of(AppRouter.context).pushNamedAndRemoveUntil(AppRouteName.login, (route) => false); }*/ - Navigator.of(AppRouter.context).pushNamedAndRemoveUntil(AppRouteName.login, (route) => false); + Navigator.of(AppRouter.context).pushNamedAndRemoveUntil(AppRouteName.login, (route) => false, arguments: {'showPasswordPage': showPasswordLoginPage}); + } + + // 是否有游戏权限 + static bool hasGamePermission() { + return _userEntity?.valid ?? false; + } + + // 是否登录(token是否有效) + static bool isLogined() { + return getUserToken().isNotEmpty; + } + + static String getUserToken() { + return _userEntity?.token ?? ''; + } + + // (vip)剩余有效期 + static int getRemainingValidity() { + return _userEntity?.validDay ?? 0; } } diff --git a/lib/common/request/dao/system_dao.dart b/lib/common/request/dao/system_dao.dart new file mode 100644 index 0000000..20155ac --- /dev/null +++ b/lib/common/request/dao/system_dao.dart @@ -0,0 +1,10 @@ +import '../../../models/app_config_entity.dart'; +import '../request_client.dart'; + +class SystemDao { + + /// 获取配置信息 + static Future getAppConfig() async { + return await requestClient.get(Apis.appConfig); + } +} diff --git a/lib/common/request/dao/user_dao.dart b/lib/common/request/dao/user_dao.dart index 6894f65..18bae66 100644 --- a/lib/common/request/dao/user_dao.dart +++ b/lib/common/request/dao/user_dao.dart @@ -16,6 +16,12 @@ class UserDao { ); if (data != null) { UserUtil.saveUser(data); + // 由于userInfo接口不会返回token,所以这里需要再次保存一下token + final token = data.token; + //登录成功后zip一下getUserInfo,因为进入首页需要的信息在userinfo里,保证进入首页数据是最新的 + data = await getUserInfo(); + data?.token = token; + UserUtil.saveUser(data); } return data; } diff --git a/lib/common/request/token_interceptor.dart b/lib/common/request/token_interceptor.dart index 95f24fa..165af84 100644 --- a/lib/common/request/token_interceptor.dart +++ b/lib/common/request/token_interceptor.dart @@ -1,14 +1,18 @@ import 'package:dio/dio.dart'; import 'package:wow_english/common/core/user_util.dart'; +import '../core/app_config_helper.dart'; + class TokenInterceptor extends Interceptor { @override - void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + void onRequest(RequestOptions options, RequestInterceptorHandler handler) async { // 判断token不为空插入, todo token的取法应该跟user在一起,这里取不到user - if (UserUtil.token.isNotEmpty) { - options.headers["Auth-token"] = UserUtil.token; + if (UserUtil.isLogined()) { + options.headers["Auth-token"] = UserUtil.getUserToken(); } - options.headers["version"] = '1.0.0'; + // 在发送请求之前获取版本号 + String version = await AppConfigHelper.getAppVersion(); + options.headers["version"] = version; super.onRequest(options, handler); } } diff --git a/lib/generated/json/app_config_entity.g.dart b/lib/generated/json/app_config_entity.g.dart new file mode 100644 index 0000000..e917a9e --- /dev/null +++ b/lib/generated/json/app_config_entity.g.dart @@ -0,0 +1,93 @@ +import 'package:wow_english/generated/json/base/json_convert_content.dart'; +import 'package:wow_english/models/app_config_entity.dart'; + +AppConfigEntityEntity $AppConfigEntityEntityFromJson( + Map json) { + final AppConfigEntityEntity appConfigEntityEntity = AppConfigEntityEntity(); + final bool? androidForceUpdate = jsonConvert.convert( + json['androidForceUpdate']); + if (androidForceUpdate != null) { + appConfigEntityEntity.androidForceUpdate = androidForceUpdate; + } + final bool? androidRecommendUpdate = jsonConvert.convert( + json['androidRecommendUpdate']); + if (androidRecommendUpdate != null) { + appConfigEntityEntity.androidRecommendUpdate = androidRecommendUpdate; + } + final String? androidUpdatePackageUrl = jsonConvert.convert( + json['androidUpdatePackageUrl']); + if (androidUpdatePackageUrl != null) { + appConfigEntityEntity.androidUpdatePackageUrl = androidUpdatePackageUrl; + } + final int? androidVersion = jsonConvert.convert(json['androidVersion']); + if (androidVersion != null) { + appConfigEntityEntity.androidVersion = androidVersion; + } + final bool? iosForceUpdate = jsonConvert.convert( + json['iosForceUpdate']); + if (iosForceUpdate != null) { + appConfigEntityEntity.iosForceUpdate = iosForceUpdate; + } + final bool? iosRecommendUpdate = jsonConvert.convert( + json['iosRecommendUpdate']); + if (iosRecommendUpdate != null) { + appConfigEntityEntity.iosRecommendUpdate = iosRecommendUpdate; + } + final int? iosVersion = jsonConvert.convert(json['iosVersion']); + if (iosVersion != null) { + appConfigEntityEntity.iosVersion = iosVersion; + } + final String? noticeBeforePurchaseUrl = jsonConvert.convert( + json['noticeBeforePurchaseUrl']); + if (noticeBeforePurchaseUrl != null) { + appConfigEntityEntity.noticeBeforePurchaseUrl = noticeBeforePurchaseUrl; + } + final String? safe = jsonConvert.convert(json['safe']); + if (safe != null) { + appConfigEntityEntity.safe = safe; + } + return appConfigEntityEntity; +} + +Map $AppConfigEntityEntityToJson( + AppConfigEntityEntity entity) { + final Map data = {}; + data['androidForceUpdate'] = entity.androidForceUpdate; + data['androidRecommendUpdate'] = entity.androidRecommendUpdate; + data['androidUpdatePackageUrl'] = entity.androidUpdatePackageUrl; + data['androidVersion'] = entity.androidVersion; + data['iosForceUpdate'] = entity.iosForceUpdate; + data['iosRecommendUpdate'] = entity.iosRecommendUpdate; + data['iosVersion'] = entity.iosVersion; + data['noticeBeforePurchaseUrl'] = entity.noticeBeforePurchaseUrl; + data['safe'] = entity.safe; + return data; +} + +extension AppConfigEntityEntityExtension on AppConfigEntityEntity { + AppConfigEntityEntity copyWith({ + bool? androidForceUpdate, + bool? androidRecommendUpdate, + String? androidUpdatePackageUrl, + int? androidVersion, + bool? iosForceUpdate, + bool? iosRecommendUpdate, + int? iosVersion, + String? noticeBeforePurchaseUrl, + String? safe, + }) { + return AppConfigEntityEntity() + ..androidForceUpdate = androidForceUpdate ?? this.androidForceUpdate + ..androidRecommendUpdate = androidRecommendUpdate ?? + this.androidRecommendUpdate + ..androidUpdatePackageUrl = androidUpdatePackageUrl ?? + this.androidUpdatePackageUrl + ..androidVersion = androidVersion ?? this.androidVersion + ..iosForceUpdate = iosForceUpdate ?? this.iosForceUpdate + ..iosRecommendUpdate = iosRecommendUpdate ?? this.iosRecommendUpdate + ..iosVersion = iosVersion ?? this.iosVersion + ..noticeBeforePurchaseUrl = noticeBeforePurchaseUrl ?? + this.noticeBeforePurchaseUrl + ..safe = safe ?? this.safe; + } +} \ No newline at end of file diff --git a/lib/generated/json/base/json_convert_content.dart b/lib/generated/json/base/json_convert_content.dart index bac27fe..15812c8 100644 --- a/lib/generated/json/base/json_convert_content.dart +++ b/lib/generated/json/base/json_convert_content.dart @@ -5,6 +5,7 @@ // This file is automatically generated. DO NOT EDIT, all your changes would be lost. import 'package:flutter/material.dart' show debugPrint; import 'package:wow_english/models/aliyun_oss_upload_sts_entity.dart'; +import 'package:wow_english/models/app_config_entity.dart'; import 'package:wow_english/models/course_entity.dart'; import 'package:wow_english/models/course_module_entity.dart'; import 'package:wow_english/models/course_process_entity.dart'; @@ -154,6 +155,10 @@ class JsonConvert { Map e) => AliyunOssUploadStsCallbackParam.fromJson(e)).toList() as M; } + if ([] is M) { + return data.map((Map e) => + AppConfigEntityEntity.fromJson(e)).toList() as M; + } if ([] is M) { return data.map((Map e) => CourseEntity.fromJson(e)).toList() as M; @@ -231,6 +236,7 @@ class JsonConvertClassCollection { (AliyunOssUploadStsEntity).toString(): AliyunOssUploadStsEntity.fromJson, (AliyunOssUploadStsCallbackParam) .toString(): AliyunOssUploadStsCallbackParam.fromJson, + (AppConfigEntityEntity).toString(): AppConfigEntityEntity.fromJson, (CourseEntity).toString(): CourseEntity.fromJson, (CourseCourseLessons).toString(): CourseCourseLessons.fromJson, (CourseModuleEntity).toString(): CourseModuleEntity.fromJson, diff --git a/lib/generated/json/user_entity.g.dart b/lib/generated/json/user_entity.g.dart index 730c6dc..ef0c8db 100644 --- a/lib/generated/json/user_entity.g.dart +++ b/lib/generated/json/user_entity.g.dart @@ -45,6 +45,14 @@ UserEntity $UserEntityFromJson(Map json) { if (effectiveDate != null) { userEntity.effectiveDate = effectiveDate; } + final int? validDay = jsonConvert.convert(json['validDay']); + if (validDay != null) { + userEntity.validDay = validDay; + } + final bool? valid = jsonConvert.convert(json['valid']); + if (valid != null) { + userEntity.valid = valid; + } return userEntity; } @@ -60,6 +68,8 @@ Map $UserEntityToJson(UserEntity entity) { data['fillUserInfo'] = entity.fillUserInfo; data['nowCourseModuleId'] = entity.nowCourseModuleId; data['effectiveDate'] = entity.effectiveDate; + data['validDay'] = entity.validDay; + data['valid'] = entity.valid; return data; } @@ -75,6 +85,8 @@ extension UserEntityExtension on UserEntity { int? fillUserInfo, int? nowCourseModuleId, String? effectiveDate, + int? validDay, + bool? valid, }) { return UserEntity() ..id = id ?? this.id @@ -86,6 +98,8 @@ extension UserEntityExtension on UserEntity { ..phoneNum = phoneNum ?? this.phoneNum ..fillUserInfo = fillUserInfo ?? this.fillUserInfo ..nowCourseModuleId = nowCourseModuleId ?? this.nowCourseModuleId - ..effectiveDate = effectiveDate ?? this.effectiveDate; + ..effectiveDate = effectiveDate ?? this.effectiveDate + ..validDay = validDay ?? this.validDay + ..valid = valid ?? this.valid; } } \ No newline at end of file diff --git a/lib/models/app_config_entity.dart b/lib/models/app_config_entity.dart new file mode 100644 index 0000000..28873cc --- /dev/null +++ b/lib/models/app_config_entity.dart @@ -0,0 +1,51 @@ +import 'package:wow_english/generated/json/base/json_field.dart'; +import 'dart:convert'; + +import '../generated/json/app_config_entity.g.dart'; + + +@JsonSerializable() +class AppConfigEntityEntity { + + // 安卓是否强制更新 + bool? androidForceUpdate; + + // 安卓是否推荐更新 + bool? androidRecommendUpdate; + + // 安卓更新包地址 + String? androidUpdatePackageUrl; + + // 安卓当前版本号 + int? androidVersion; + + bool? iosForceUpdate; + + bool? iosRecommendUpdate; + + // ios版本 + int? iosVersion; + + // 购前须知图片 + String? noticeBeforePurchaseUrl; + + // 当前是否安全,safe-安全 otherwise-隐藏pay + String? safe; + + + AppConfigEntityEntity(); + + factory AppConfigEntityEntity.fromJson(Map json) => $AppConfigEntityEntityFromJson(json); + + Map toJson() => $AppConfigEntityEntityToJson(this); + + @override + String toString() { + return jsonEncode(this); + } + + // 是否审核中(null或者非"safe"即不安全) + bool isAppReviewing() { + return safe != "safe"; + } +} \ No newline at end of file diff --git a/lib/models/user_entity.dart b/lib/models/user_entity.dart index 4c9d1c1..44a68c1 100644 --- a/lib/models/user_entity.dart +++ b/lib/models/user_entity.dart @@ -9,7 +9,7 @@ class UserEntity { late String name; /// 一定有也必须要有 - late String token; + String? token; //late int expireTime; @@ -29,6 +29,12 @@ class UserEntity { /// 有效时间,VIP,为null没有 String? effectiveDate; + /// 有效天数 + int? validDay; + + /// 游戏权限 + bool? valid; + UserEntity(); factory UserEntity.fromJson(Map json) => $UserEntityFromJson(json); diff --git a/lib/pages/home/home_page.dart b/lib/pages/home/home_page.dart index 7a1629a..c65d121 100644 --- a/lib/pages/home/home_page.dart +++ b/lib/pages/home/home_page.dart @@ -23,33 +23,30 @@ class HomePage extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (context) => HomeBloc(moduleId)..add(RequestDataEvent()), - child: _HomePageView(), + child: _HomePageView(context), ); } } class _HomePageView extends StatelessWidget { + + const _HomePageView(this.context); + + final BuildContext context; + void _headerActionEvent(HeaderActionType type) { if (type == HeaderActionType.video) { pushNamed(AppRouteName.reAfter); } else if (type == HeaderActionType.phase) { - if(UserUtil.token.isEmpty) { - pushNamed(AppRouteName.login); - } else { - pushNamed(AppRouteName.lesson); - } + pushNamed(AppRouteName.lesson); } else if (type == HeaderActionType.listen) { pushNamed(AppRouteName.listen); } else if (type == HeaderActionType.shop) { pushNamed(AppRouteName.shop); } else if (type == HeaderActionType.user) { - if(UserUtil.token.isEmpty) { - pushNamed(AppRouteName.login); - } else { - pushNamed(AppRouteName.user); - } - } else { - + pushNamed(AppRouteName.user); + } else if (type == HeaderActionType.home) { + Navigator.pop(context); } } @@ -155,7 +152,7 @@ class _HomePageView extends StatelessWidget { //彩蛋 return GestureDetector( onTap: () { - if(UserUtil.token.isEmpty) { + if (!UserUtil.isLogined()) { pushNamed(AppRouteName.login); return; } @@ -173,7 +170,7 @@ class _HomePageView extends StatelessWidget { } else { return GestureDetector( onTap: () { - if(UserUtil.token.isEmpty) { + if (!UserUtil.isLogined()) { pushNamed(AppRouteName.login); return; } diff --git a/lib/pages/home/widgets/home_tab_header_widget.dart b/lib/pages/home/widgets/home_tab_header_widget.dart index d77785a..cc00212 100644 --- a/lib/pages/home/widgets/home_tab_header_widget.dart +++ b/lib/pages/home/widgets/home_tab_header_widget.dart @@ -1,11 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:wow_english/common/core/user_util.dart'; import 'package:wow_english/common/extension/string_extension.dart'; import 'package:wow_english/pages/user/bloc/user_bloc.dart'; -import 'package:wow_english/utils/image_util.dart'; +import '../../../common/core/app_config_helper.dart'; import '../../../models/course_entity.dart'; import '../courese_module_model.dart'; @@ -19,70 +18,89 @@ enum HeaderActionType { //购买 shop, //个人信息 - user + user, + //返回到(模块选择)首页 + home, } class HomeTabHeaderWidget extends StatelessWidget { - const HomeTabHeaderWidget({super.key,this.entity, this.actionTap}); + const HomeTabHeaderWidget({super.key, this.entity, this.actionTap}); final CourseEntity? entity; final Function(HeaderActionType type)? actionTap; @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context,state) { + return BlocBuilder( + builder: (context, state) { return Container( height: 45, width: double.infinity, - color: CourseModuleModel(entity?.courseModuleCode??'Phase-1').color, + color: + CourseModuleModel(entity?.courseModuleCode ?? 'Phase-1').color, padding: EdgeInsets.symmetric(horizontal: 9.5.w), child: Row( children: [ ScreenUtil().bottomBarHeight.horizontalSpace, GestureDetector( - onTap: () => actionTap?.call(HeaderActionType.user), - child: Container( - decoration: BoxDecoration( - border: Border.all( - width: 1.0, - color: const Color(0xFF140C10), - ), - borderRadius: BorderRadius.circular(21), - ), - child: CircleAvatar( - radius: 21, - backgroundImage: ImageUtil.getImageProviderOnDefault(UserUtil.getUser()?.avatarUrl), - ), - ), - ), - GestureDetector( onTap: () { if (actionTap != null) { - actionTap!(HeaderActionType.user); + actionTap!(HeaderActionType.home); } }, child: Container( - margin: const EdgeInsets.only(left: 7), - padding: const EdgeInsets.all(4.0), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(2), - border: Border.all(width: 1.0, color: const Color(0xFF140C10), style: BorderStyle.solid), - ), - child: Text( - UserUtil.getUser()?.name ?? '未登录', - style: TextStyle(color: const Color(0xFF333333), fontSize: 16.sp), + alignment: Alignment.center, + child: Image.asset( + 'back_around'.assetPng, + height: 40.h, + width: 40.w, ), ), ), + // GestureDetector( + // onTap: () => actionTap?.call(HeaderActionType.user), + // child: Container( + // decoration: BoxDecoration( + // border: Border.all( + // width: 1.0, + // color: const Color(0xFF140C10), + // ), + // borderRadius: BorderRadius.circular(21), + // ), + // child: CircleAvatar( + // radius: 21, + // backgroundImage: ImageUtil.getImageProviderOnDefault(UserUtil.getUser()?.avatarUrl), + // ), + // ), + // ), + // GestureDetector( + // onTap: () { + // if (actionTap != null) { + // actionTap!(HeaderActionType.user); + // } + // }, + // child: Container( + // margin: const EdgeInsets.only(left: 7), + // padding: const EdgeInsets.all(4.0), + // decoration: BoxDecoration( + // color: Colors.white, + // borderRadius: BorderRadius.circular(2), + // border: Border.all(width: 1.0, color: const Color(0xFF140C10), style: BorderStyle.solid), + // ), + // child: Text( + // UserUtil.getUser()?.name ?? '未登录', + // style: TextStyle(color: const Color(0xFF333333), fontSize: 16.sp), + // ), + // ), + // ), 20.horizontalSpace, Expanded( child: Text( - CourseModuleModel(entity?.courseModuleCode??'Phase-1').courseModuleTitle, - textAlign: TextAlign.left, - style: const TextStyle(color: Colors.white, fontSize: 30.0), - )), + CourseModuleModel(entity?.courseModuleCode ?? 'Phase-1') + .courseModuleTitle, + textAlign: TextAlign.left, + style: const TextStyle(color: Colors.white, fontSize: 30.0), + )), // IconButton( // onPressed: () { // if (actionTap != null) { @@ -104,18 +122,19 @@ class HomeTabHeaderWidget extends StatelessWidget { } }, icon: Image.asset('listen'.assetPng)), - IconButton( - onPressed: (){ - if(actionTap != null) { - actionTap!(HeaderActionType.shop); - } - }, - icon: Image.asset('shop'.assetPng) + Visibility( + visible: !AppConfigHelper.shouldHidePay(), + child: IconButton( + onPressed: () { + if (actionTap != null) { + actionTap!(HeaderActionType.shop); + } + }, + icon: Image.asset('shop'.assetPng)), ), ScreenUtil().bottomBarHeight.horizontalSpace, ], - ) - ); + )); }, ); } diff --git a/lib/pages/login/loginpage/bloc/login_bloc.dart b/lib/pages/login/loginpage/bloc/login_bloc.dart index 0ffe1fc..b7afc66 100644 --- a/lib/pages/login/loginpage/bloc/login_bloc.dart +++ b/lib/pages/login/loginpage/bloc/login_bloc.dart @@ -45,7 +45,8 @@ class LoginBloc extends Bloc { bool get canSendSms => _canSendSms; - LoginBloc() : super(LoginInitial()) { + LoginBloc(bool preferencesPasswordLogin) : super(LoginInitial()) { + _isSmsLoginType = !preferencesPasswordLogin; on(_requestLoginApi); on(_changeLoginType); on(_changePhoneNumber); diff --git a/lib/pages/login/loginpage/login_page.dart b/lib/pages/login/loginpage/login_page.dart index 6af7d9e..5ae615d 100644 --- a/lib/pages/login/loginpage/login_page.dart +++ b/lib/pages/login/loginpage/login_page.dart @@ -11,12 +11,16 @@ import 'package:wow_english/route/route.dart'; import 'bloc/login_bloc.dart'; class LoginPage extends StatelessWidget { - const LoginPage({super.key}); + + // 优先展示密码登录 + final bool preferencesPasswordLogin; + + const LoginPage({super.key, this.preferencesPasswordLogin = false}); @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => LoginBloc(), + create: (context) => LoginBloc(preferencesPasswordLogin), child: _LoginPageView(), ); } @@ -30,7 +34,7 @@ class _LoginPageView extends StatelessWidget { if (state is LoginResultChangeState) { // 调试用 // Navigator.of(context).pushNamed(AppRouteName.home); - pushNamedAndRemoveUntil(AppRouteName.home, (route) => false); + pushNamedAndRemoveUntil(AppRouteName.moduleSelect, (route) => false); } }, child: _buildLoginViewWidget(), diff --git a/lib/pages/login/setpwd/set_pwd_page.dart b/lib/pages/login/setpwd/set_pwd_page.dart index 0ed15e7..2c2874f 100644 --- a/lib/pages/login/setpwd/set_pwd_page.dart +++ b/lib/pages/login/setpwd/set_pwd_page.dart @@ -7,6 +7,7 @@ import 'package:wow_english/common/widgets/textfield_customer_widget.dart'; import 'package:wow_english/route/route.dart'; import 'package:wow_english/utils/toast_util.dart'; +import '../../../common/core/user_util.dart'; import 'bloc/set_pwd_bloc.dart'; enum SetPwdPageType { @@ -59,9 +60,9 @@ class _SetPassWordPageView extends StatelessWidget { if (bloc.pageType == SetPwdPageType.initPwd) { showToast('密码设置成功'); } else { - showToast('密码修改成功'); + showToast('密码修改成功,请重新登录'); } - pushNamedAndRemoveUntil(AppRouteName.home, (route) => false); + UserUtil.logout(true); } else if (state is PasswordSetFailedState) { state.message.toast(); } diff --git a/lib/pages/moduleSelect/view.dart b/lib/pages/moduleSelect/view.dart index 463fbd5..c8b67ac 100644 --- a/lib/pages/moduleSelect/view.dart +++ b/lib/pages/moduleSelect/view.dart @@ -1,9 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:wow_english/common/core/app_config_helper.dart'; import 'package:wow_english/common/extension/string_extension.dart'; import 'package:wow_english/pages/moduleSelect/state.dart'; import 'package:wow_english/pages/moduleSelect/widgets/BaseHomeHeaderWidget.dart'; +import '../../common/core/user_util.dart'; +import '../../common/dialogs/show_dialog.dart'; import 'bloc.dart'; import 'event.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -47,7 +50,11 @@ class _HomePageView extends StatelessWidget { Expanded( child: GestureDetector( onTap: () { - pushNamed(AppRouteName.home); + if (UserUtil.isLogined()) { + pushNamed(AppRouteName.home); + } else { + pushNamed(AppRouteName.login); + } }, child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -74,7 +81,27 @@ class _HomePageView extends StatelessWidget { Expanded( child: GestureDetector( onTap: () { - pushNamed(AppRouteName.games); + //如果没登录先登录 + if (UserUtil.isLogined()) { + if (AppConfigHelper.shouldHidePay()) { + pushNamed(AppRouteName.games); + } else { + if (UserUtil.hasGamePermission()) { + pushNamed(AppRouteName.games); + } else { + showTwoActionDialog( + '提示', '忽略', '去续费', + '您的课程已到期,请快快续费继续学习吧!', leftTap: () { + popPage(); + }, rightTap: () { + popPage(); + pushNamed(AppRouteName.shop); + }); + } + } + } else { + pushNamed(AppRouteName.login); + } }, child: Column( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/pages/moduleSelect/widgets/BaseHomeHeaderWidget.dart b/lib/pages/moduleSelect/widgets/BaseHomeHeaderWidget.dart index 5560345..f9f924b 100644 --- a/lib/pages/moduleSelect/widgets/BaseHomeHeaderWidget.dart +++ b/lib/pages/moduleSelect/widgets/BaseHomeHeaderWidget.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:wow_english/common/core/app_config_helper.dart'; import 'package:wow_english/common/extension/string_extension.dart'; import '../../../common/core/user_util.dart'; @@ -77,16 +78,19 @@ class BaseHomeHeaderWidget extends StatelessWidget { textAlign: TextAlign.left, style: TextStyle(color: Colors.white, fontSize: 30.0), )), - Row(children: [ - Image( - width: 20.0.w, - height: 20.0.h, - image: AssetImage('ic_countdown'.assetPng)), - // 替换为你的图片资源路径 - const SizedBox(width: 10.0), - // 图片和文本之间的间隔 - const Text('还剩29天'), - ]), + Visibility( + visible: !AppConfigHelper.shouldHidePay(), + child: Row(children: [ + Image( + width: 20.0.w, + height: 20.0.h, + image: AssetImage('ic_countdown'.assetPng)), + // 替换为你的图片资源路径 + const SizedBox(width: 10.0), + // 图片和文本之间的间隔 + Text('还剩${UserUtil.getRemainingValidity()}天'), + ]), + ), ScreenUtil().bottomBarHeight.horizontalSpace, ], )); @@ -96,9 +100,9 @@ class BaseHomeHeaderWidget extends StatelessWidget { } void onUserClick() { - if (UserUtil.token.isEmpty) { - pushNamed(AppRouteName.login); - } else { + if (UserUtil.isLogined()) { pushNamed(AppRouteName.user); + } else { + pushNamed(AppRouteName.login); } } diff --git a/lib/pages/shop/home/widgets/product_item.dart b/lib/pages/shop/home/widgets/product_item.dart index cab0371..17c1f29 100644 --- a/lib/pages/shop/home/widgets/product_item.dart +++ b/lib/pages/shop/home/widgets/product_item.dart @@ -4,8 +4,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:wow_english/models/product_entity.dart'; class ProductItem extends StatelessWidget { - const ProductItem({super.key, - required this.onTap, this.entity}); + const ProductItem({super.key, required this.onTap, this.entity}); final ProductEntity? entity; @@ -15,52 +14,27 @@ class ProductItem extends StatelessWidget { Widget build(BuildContext context) { return Container( decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10.r), - border: Border.all( - width: 1.0, - color: Colors.black - ) - // image: DecorationImage( - // image: AssetImage( - // ''.assetPng, - // ), - // fit: BoxFit.fill - // ) - ), - padding: EdgeInsets.symmetric(horizontal: 16.w,vertical: 16.h), + borderRadius: BorderRadius.circular(10.r), + border: Border.all(width: 1.0, color: Colors.black)), + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Container( - width: 124.w, + width: 124.w, // 图片宽度 + height: 124.h, // 图片高度 decoration: BoxDecoration( - border: Border.all( - width: 1.0, - color: const Color(0xFF333333), - ), - image: DecorationImage( - image: NetworkImage(entity?.picUrl ?? ''), - ) + borderRadius: BorderRadius.circular(5), + // 圆角为5 + border: Border.all(width: 1.w, color: const Color(0xFF333333)), + // 边框宽度为1 + // 使用ClipRRect圆角会有间隙,ClipRRect在裁剪时可能会导致圆角部分的边框显示不完整。 + image: DecorationImage( + image: NetworkImage(entity?.picUrl ?? ''), // 图片地址 + fit: BoxFit.cover, // 图片填充方式 + ), ), ), - // CachedNetworkImage( - // imageUrl: entity?.picUrl ?? '', - // fit: BoxFit.fill, - // imageBuilder: (context, imageProvider) => - // Container( - // decoration: BoxDecoration( - // border: Border.all( - // width: 1.0, - // color: const Color(0xFF333333), - // ), - // borderRadius: BorderRadius.circular(5.0), - // ), - // ), - // placeholder: (context, url) => const CircularProgressIndicator(), - // // errorWidget: (context, url, error) => const Icon(Icons.error), - // height: 124.h, - // width: 124.w, - // ), 21.5.horizontalSpace, Expanded( child: Column( @@ -72,29 +46,24 @@ class ProductItem extends StatelessWidget { softWrap: true, textAlign: TextAlign.left, style: TextStyle( - fontSize: 12.sp, - color: const Color(0xFF333333) - ), + fontSize: 16.sp, color: const Color(0xFF333333)), ), RichText( - text: TextSpan( - children:[ - TextSpan( - text: '¥', - style: TextStyle( - fontSize: 21.sp, - color: const Color(0xFFF51A1A), - ) - ), - TextSpan( - text: entity?.price?.toString() ?? '', - style: TextStyle( - fontSize: 40.sp, - color: const Color(0xFFF51A1A), - ), - ) - ] - ), + text: TextSpan(children: [ + TextSpan( + text: '¥', + style: TextStyle( + fontSize: 21.sp, + color: const Color(0xFFF51A1A), + )), + TextSpan( + text: entity?.price?.toString() ?? '', + style: TextStyle( + fontSize: 40.sp, + color: const Color(0xFFF51A1A), + ), + ) + ]), ), GestureDetector( onTap: () { @@ -107,8 +76,7 @@ class ProductItem extends StatelessWidget { border: Border.all( color: const Color(0xFF333333), width: 1.0, - ) - ), + )), padding: EdgeInsets.symmetric( vertical: 1.h, horizontal: 26.5.w, @@ -116,9 +84,7 @@ class ProductItem extends StatelessWidget { child: Text( '立即购买', style: TextStyle( - fontSize: 10.sp, - color: const Color(0xFF333333) - ), + fontSize: 10.sp, color: const Color(0xFF333333)), ), ), ) @@ -129,4 +95,4 @@ class ProductItem extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/pages/shopping/view.dart b/lib/pages/shopping/view.dart index 2ad5add..c5757bc 100644 --- a/lib/pages/shopping/view.dart +++ b/lib/pages/shopping/view.dart @@ -19,33 +19,31 @@ Widget buildRadioOption({ required bool isSelected, required ValueChanged onSelect, }) { - return - GestureDetector( - onTap: () { - onSelect(value); - }, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Image(image: icon, width: 25.0.w, height: 25.0.h, fit: BoxFit.contain), - const SizedBox(width: 10.0), - Expanded( - child: Text( - text, - style: TextStyle(color: const Color(0xFF333333), fontSize: 12.5.sp), - ), + return GestureDetector( + onTap: () { + onSelect(value); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image(image: icon, width: 25.0.w, height: 25.0.h, fit: BoxFit.contain), + const SizedBox(width: 10.0), + Expanded( + child: Text( + text, + style: TextStyle(color: const Color(0xFF333333), fontSize: 12.5.sp), ), - Image( - image: isSelected - ? AssetImage('checked'.assetPng) - : AssetImage('unchecked'.assetPng), - width: 22.0.w, - height: 22.0.h, - ), - ], - ), - ); - + ), + Image( + image: isSelected + ? AssetImage('checked'.assetPng) + : AssetImage('unchecked'.assetPng), + width: 22.0.w, + height: 22.0.h, + ), + ], + ), + ); } class ShoppingPage extends StatelessWidget { @@ -83,22 +81,22 @@ class _ShoppingView extends StatelessWidget { child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - CachedNetworkImage( - imageUrl: bloc.productData?.detailPicUrl ?? '', - imageBuilder: (context, imageProvider) => Container( - decoration: BoxDecoration( - image: DecorationImage( - image: imageProvider, - fit: BoxFit.cover, - ), - borderRadius: BorderRadius.circular(5.0), + Container( + width: 210.w, // 图片宽度 + height: 210.h, // 图片高度 + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5.w), + // 圆角为5 + border: + Border.all(width: 1.w, color: const Color(0xFF333333)), + // 边框宽度为1 + // 使用ClipRRect圆角会有间隙,ClipRRect在裁剪时可能会导致圆角部分的边框显示不完整。 + image: DecorationImage( + image: NetworkImage(bloc.productData?.detailPicUrl ?? ''), + // 图片地址 + fit: BoxFit.cover, // 图片填充方式 ), ), - placeholder: (context, url) => - const CircularProgressIndicator(), - // errorWidget: (context, url, error) => const Icon(Icons.error), - height: 210.0.h, - width: 210.0.w, ), const SizedBox(width: 35.5), Expanded(child: _paymentWidget()) @@ -117,26 +115,21 @@ Widget _paymentWidget() => BlocBuilder( crossAxisAlignment: CrossAxisAlignment.start, children: [ RichText( - text: TextSpan( - children: [ - TextSpan( - text: '套餐价格:', - style: - TextStyle(color: const Color(0xFF333333), fontSize: 16.5.sp) - ), - TextSpan( - text: '¥${bloc.productData?.price ?? ''}', - style: - TextStyle(color: const Color(0xFFE5262A), fontSize: 16.5.sp) - ), - ] - ), + text: TextSpan(children: [ + TextSpan( + text: '套餐价格:', + style: TextStyle( + color: const Color(0xFF333333), fontSize: 16.5.sp)), + TextSpan( + text: '¥${bloc.productData?.price ?? ''}', + style: TextStyle( + color: const Color(0xFFE5262A), fontSize: 16.5.sp)), + ]), ), const SizedBox(height: 14.5), Text( '套餐名称:${bloc.productData?.name}', - style: - TextStyle(color: const Color(0xFF333333), fontSize: 16.sp), + style: TextStyle(color: const Color(0xFF333333), fontSize: 16.sp), maxLines: 2, ), const SizedBox(height: 14.5), @@ -158,7 +151,8 @@ Widget _paymentWidget() => BlocBuilder( value: PaymentChannel.wechatPay.payChannelType, icon: AssetImage('weixin'.assetPng), text: PaymentChannel.wechatPay.payChannelName, - isSelected: bloc.curPaymentChannel.payChannelType == PaymentChannel.wechatPay.payChannelType, + isSelected: bloc.curPaymentChannel.payChannelType == + PaymentChannel.wechatPay.payChannelType, onSelect: (newValue) { bloc.add(ChangePaymentChannelEvent(PaymentChannel.wechatPay)); }, @@ -168,7 +162,8 @@ Widget _paymentWidget() => BlocBuilder( value: PaymentChannel.aliPay.payChannelType, icon: AssetImage('zhifubao'.assetPng), text: PaymentChannel.aliPay.payChannelName, - isSelected: bloc.curPaymentChannel.payChannelType == PaymentChannel.aliPay.payChannelType, + isSelected: bloc.curPaymentChannel.payChannelType == + PaymentChannel.aliPay.payChannelType, onSelect: (newValue) { bloc.add(ChangePaymentChannelEvent(PaymentChannel.aliPay)); }, @@ -178,25 +173,26 @@ Widget _paymentWidget() => BlocBuilder( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ RichText( - text: TextSpan( - children: [ - const TextSpan( - text: '需支付:', - style: - TextStyle(color: Color(0xFF333333), fontSize: 12.5, fontFamily: 'PingFangSC-Regular') - ), - TextSpan( - text: '¥${bloc.productData?.price ?? ''}', - style: - TextStyle(color: const Color(0xFFE7383B), fontSize: 41.sp, fontFamily: 'PingFangSC-Regular') - ), - ] - ), + text: TextSpan(children: [ + const TextSpan( + text: '需支付:', + style: TextStyle( + color: Color(0xFF333333), + fontSize: 12.5, + fontFamily: 'PingFangSC-Regular')), + TextSpan( + text: '¥${bloc.productData?.price ?? ''}', + style: TextStyle( + color: const Color(0xFFE7383B), + fontSize: 41.sp, + fontFamily: 'PingFangSC-Regular')), + ]), ), // 确认支付按钮 GestureDetector( onTap: () { - bloc.add(DoPayEvent(bloc.productData, bloc.curPaymentChannel)); + bloc.add( + DoPayEvent(bloc.productData, bloc.curPaymentChannel)); }, child: Image( width: 105.w, diff --git a/lib/pages/user/modify/modify_user_information_page.dart b/lib/pages/user/modify/modify_user_information_page.dart index bbeae11..3c17024 100644 --- a/lib/pages/user/modify/modify_user_information_page.dart +++ b/lib/pages/user/modify/modify_user_information_page.dart @@ -66,7 +66,6 @@ class ModifyUserInformationPage extends StatelessWidget { if (bloc.isChangeInfo) { showTwoActionDialog('提示', '确定离开', '继续修改', '您的信息尚未保存',leftTap: (){ popPage(); - popPage(); },rightTap: (){ popPage(); }); diff --git a/lib/route/route.dart b/lib/route/route.dart index e6b5a20..efe888b 100644 --- a/lib/route/route.dart +++ b/lib/route/route.dart @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:wow_english/app/splash_page.dart'; import 'package:wow_english/common/pages/wow_web_page.dart'; +import 'package:wow_english/generated/json/base/json_convert_content.dart'; import 'package:wow_english/models/product_entity.dart'; import 'package:wow_english/pages/games/view.dart'; import 'package:wow_english/pages/home/home_page.dart'; @@ -91,7 +92,9 @@ class AppRouter { transitionDuration: Duration.zero, transitionsBuilder: (_, __, ___, child) => child); case AppRouteName.login: - return CupertinoPageRoute(builder: (_) => const LoginPage()); + // 是否默认密码登录,修改密码后跳登录页,优先密码登录体验好点 + final bool showPasswordPage = (settings.arguments as Map?)?.getOrNull('showPasswordPage') as bool? ?? false; + return CupertinoPageRoute(builder: (_) => LoginPage(preferencesPasswordLogin: showPasswordPage)); case AppRouteName.moduleSelect: return CupertinoPageRoute(builder: (_) => const ModuleSelectPage()); case AppRouteName.games: diff --git a/pubspec.yaml b/pubspec.yaml index 35040bb..6966f1c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -104,7 +104,7 @@ dependencies: # 阿里云oss https://pub.dev/packages/flutter_oss_aliyun flutter_oss_aliyun: ^6.2.7 # App信息 https://pub.dev/packages/package_info_plus - package_info_plus: ^4.0.2 + package_info_plus: ^4.2.0 dev_dependencies: build_runner: ^2.4.4