Browse Source

Merge branch 'wu_2023_map' into wr_2023_business

# Conflicts:
#	pubspec.lock
wr_2023_business
huixiang_app 1 year ago
parent
commit
e408f8f017
  1. 551
      lib/login/captcha/block_puzzle_captcha.dart
  2. 367
      lib/login/captcha/click_word_captcha.dart
  3. 219
      lib/login/login_page.dart
  4. 72
      lib/login/new_login_page.dart
  5. 4
      lib/retrofit/min_api.dart
  6. 25
      lib/retrofit/retrofit_api.dart
  7. 64
      lib/retrofit/retrofit_api.g.dart
  8. 61
      lib/setting/binding_phone_page.dart
  9. 42
      lib/setting/logout_ing.dart
  10. 23
      lib/setting/platform_code_page.dart
  11. 86
      lib/utils/captcha_util.dart
  12. 133
      lib/utils/widget_util.dart
  13. 42
      pubspec.lock
  14. 3
      pubspec.yaml

551
lib/login/captcha/block_puzzle_captcha.dart

File diff suppressed because one or more lines are too long

367
lib/login/captcha/click_word_captcha.dart

@ -0,0 +1,367 @@
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../retrofit/retrofit_api.dart';
import '../../utils/captcha_util.dart';
import '../../utils/widget_util.dart';
typedef VoidSuccessCallback = dynamic Function(String v);
class ClickWordCaptcha extends StatefulWidget {
final VoidSuccessCallback onSuccess; //
final VoidCallback onFail; //
const ClickWordCaptcha({Key key, this.onSuccess, this.onFail})
: super(key: key);
@override
_ClickWordCaptchaState createState() => _ClickWordCaptchaState();
}
class _ClickWordCaptchaState extends State<ClickWordCaptcha> {
ClickWordCaptchaState _clickWordCaptchaState = ClickWordCaptchaState.none;
List<Offset> _tapOffsetList = [];
ClickWordCaptchaModel _clickWordCaptchaModel = ClickWordCaptchaModel();
Color titleColor = Colors.black;
Color borderColor = Color(0xffdddddd);
String bottomTitle = "";
Size baseSize = Size(310.0, 155.0);
//
_changeResultState() {
switch (_clickWordCaptchaState) {
case ClickWordCaptchaState.normal:
titleColor = Colors.black;
borderColor = Color(0xffdddddd);
break;
case ClickWordCaptchaState.success:
_tapOffsetList = [];
titleColor = Colors.green;
borderColor = Colors.green;
bottomTitle = "验证成功";
break;
case ClickWordCaptchaState.fail:
_tapOffsetList = [];
titleColor = Colors.red;
borderColor = Colors.red;
bottomTitle = "验证失败";
break;
default:
titleColor = Colors.black;
borderColor = Color(0xffdddddd);
bottomTitle = "数据加载中……";
break;
}
setState(() {});
}
@override
void initState() {
super.initState();
_loadCaptcha();
}
//
_loadCaptcha() async {
_tapOffsetList = [];
_clickWordCaptchaState = ClickWordCaptchaState.none;
_changeResultState();
ApiService apiIpService = ApiService(Dio(), context: context);
ClickWordCaptchaModel baseData = await apiIpService.captchaGet({"captchaType": "clickWord"}).catchError((onError) {});
if (baseData == null) {
_clickWordCaptchaModel.secretKey = "";
bottomTitle = "加载失败,请刷新";
_clickWordCaptchaState = ClickWordCaptchaState.normal;
_changeResultState();
return;
}
else {
_clickWordCaptchaModel = baseData;
var baseR = await WidgetUtil.getImageWH(
image: Image.memory(
Base64Decoder().convert(_clickWordCaptchaModel.imgStr)));
baseSize = baseR.size;
bottomTitle = "请依次点击【${_clickWordCaptchaModel.wordStr}";
}
_clickWordCaptchaState = ClickWordCaptchaState.normal;
_changeResultState();
}
//
_checkCaptcha() async {
List<Map<String, dynamic>> mousePos = [];
_tapOffsetList.map((size) {
mousePos
.add({"x": size.dx.roundToDouble(), "y": size.dy.roundToDouble()});
}).toList();
var pointStr = json.encode(mousePos);
var cryptedStr = pointStr;
// secretKey as加密
if (!CaptchaUtil.isEmpty(_clickWordCaptchaModel.secretKey)) {
cryptedStr = CaptchaUtil.aesEncode(
key: _clickWordCaptchaModel.secretKey, content: pointStr);
// var dcrypt = CaptchaUtil.aesDecode(
// key: _clickWordCaptchaModel.secretKey, content: cryptedStr);
}
// Map _map = json.decode(dcrypt);
ApiService apiIpService = ApiService(Dio(), context: context);
bool baseData = await apiIpService.captchaCheck({
"pointJson": cryptedStr,
"captchaType": "clickWord",
"token": _clickWordCaptchaModel.token
}).catchError((onError) {});
if (baseData) {
_checkFail();
return;
}
// token ---
var captchaVerification = "${_clickWordCaptchaModel.token}---$pointStr";
if (!CaptchaUtil.isEmpty(_clickWordCaptchaModel.secretKey)) {
// token --- _clickWordCaptchaModel.secretKey
captchaVerification = CaptchaUtil.aesEncode(
key: _clickWordCaptchaModel.secretKey,
content: captchaVerification);
}
_checkSuccess(captchaVerification);
}
//
_checkFail() async {
_clickWordCaptchaState = ClickWordCaptchaState.fail;
_changeResultState();
await Future.delayed(Duration(milliseconds: 1000));
_loadCaptcha();
//
if (widget.onFail != null) {
widget.onFail();
}
}
//
_checkSuccess(String pointJson) async {
_clickWordCaptchaState = ClickWordCaptchaState.success;
_changeResultState();
await Future.delayed(Duration(milliseconds: 1000));
var cryptedStr = CaptchaUtil.aesEncode(key: 'BGxdEUOZkXka4HSj', content: pointJson);
print(cryptedStr);
// pointJson es加密之后的信息
if (widget.onSuccess != null) {
widget.onSuccess(cryptedStr);
}
//
Navigator.pop(context);
}
@override
Widget build(BuildContext context) {
var data = MediaQuery.of(context);
var dialogWidth = 0.9 * data.size.width;
var isRatioCross = false;
if (dialogWidth < 320.0) {
dialogWidth = data.size.width;
isRatioCross = true;
}
return Scaffold(
backgroundColor: Colors.transparent,
body: Center(
child: Container(
width: dialogWidth,
height: 320.h,
color: Colors.white,
child: Column(
children: <Widget>[
_topConttainer(),
_captchaContainer(),
_bottomContainer()
],
),
),
),
);
}
//
_captchaContainer() {
List<Widget> _widgetList = [];
if (!CaptchaUtil.isEmpty(_clickWordCaptchaModel.imgStr)) {
_widgetList.add(Image(
width: baseSize.width,
height: baseSize.height,
gaplessPlayback: true,
image: MemoryImage(
Base64Decoder().convert(_clickWordCaptchaModel.imgStr))));
}
double _widgetW = 20;
for (int i = 0; i < _tapOffsetList.length; i++) {
Offset offset = _tapOffsetList[i];
_widgetList.add(Positioned(
left: offset.dx - _widgetW * 0.5,
top: offset.dy - _widgetW * 0.5,
child: Container(
alignment: Alignment.center,
width: _widgetW,
height: _widgetW,
decoration: BoxDecoration(
color: Color(0xCC43A047),
borderRadius: BorderRadius.all(Radius.circular(_widgetW))),
child: Text(
"${i + 1}",
style: TextStyle(color: Colors.white, fontSize: 15),
),
)));
}
_widgetList.add(//
Positioned(
top: 0,
right: 0,
child: IconButton(
icon: Icon(Icons.refresh),
iconSize: 30,
color: Colors.deepOrangeAccent,
onPressed: () {
//
_loadCaptcha();
}),
));
return GestureDetector(
onTapDown: (TapDownDetails details) {
debugPrint(
"onTapDown globalPosition全局坐标系位置: ${details.globalPosition} localPosition组件坐标系位置: ${details.localPosition} ");
if (!CaptchaUtil.isListEmpty(_clickWordCaptchaModel.wordList) &&
_tapOffsetList.length < _clickWordCaptchaModel.wordList.length) {
_tapOffsetList.add(
Offset(details.localPosition.dx, details.localPosition.dy));
}
setState(() {});
if (!CaptchaUtil.isListEmpty(_clickWordCaptchaModel.wordList) &&
_tapOffsetList.length == _clickWordCaptchaModel.wordList.length) {
_checkCaptcha();
}
},
child: Container(
width: baseSize.width,
height: baseSize.height,
child: Stack(
children: _widgetList,
),
));
}
//
_bottomContainer() {
return Container(
height: 50.h,
margin: EdgeInsets.only(top: 10),
alignment: Alignment.center,
width: baseSize.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(4)),
border: Border.all(color: borderColor)),
child:
Text(bottomTitle, style: TextStyle(fontSize: 18, color: titleColor)),
);
}
//+
_topConttainer() {
return Container(
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
margin: EdgeInsets.only(bottom: 20, top: 5),
decoration: BoxDecoration(
border: Border(bottom: BorderSide(width: 1, color: Color(0xffe5e5e5))),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'请完成安全验证',
style: TextStyle(fontSize: 18),
),
IconButton(
icon: Icon(Icons.highlight_off),
iconSize: 35,
color: Colors.black54,
onPressed: () {
//退
Navigator.pop(context);
}),
],
),
);
}
}
//
enum ClickWordCaptchaState {
normal, //
success, //
fail, //
none, // 使
}
//
class ClickWordCaptchaModel {
String imgStr; //url base64 data
String jigsawImageBase64; //url base64 data
String token; // token
List wordList; //
String wordStr; //
String secretKey; //key
ClickWordCaptchaModel(
{this.imgStr = "",
this.jigsawImageBase64 = "",
this.token = "",
this.secretKey = "",
this.wordList = const [],
this.wordStr = ""});
//
static ClickWordCaptchaModel fromMap(Map<String, dynamic> map) {
ClickWordCaptchaModel captchaModel = ClickWordCaptchaModel();
captchaModel.imgStr = map["originalImageBase64"] ?? "";
captchaModel.jigsawImageBase64 = map["jigsawImageBase64"] ?? "";
captchaModel.token = map["token"] ?? "";
captchaModel.secretKey = map["secretKey"] ?? "";
captchaModel.wordList = map["wordList"] ?? [];
if (!CaptchaUtil.isListEmpty(captchaModel.wordList)) {
captchaModel.wordStr = captchaModel.wordList.join(",");
}
return captchaModel;
}
//
Map<String, dynamic> toJson() {
var map = new Map<String, dynamic>();
map['imgStr'] = imgStr;
map['jigsawImageBase64'] = jigsawImageBase64;
map['token'] = token;
map['secretKey'] = token;
map['wordList'] = wordList;
map['wordStr'] = wordStr;
return map;
}
@override
String toString() {
// TODO: implement toString
return JsonEncoder.withIndent(' ').convert(toJson());
}
}

219
lib/login/login_page.dart

@ -75,7 +75,8 @@ class _MyLoginPageState extends State<LoginPage> with TickerProviderStateMixin {
sharedPreferences.getString("token") != null &&
sharedPreferences.getString("token") != "") {
Navigator.of(context).popAndPushNamed('/router/main_page');
} else {initController();
} else {
initController();
client = ApiService(Dio(), context: context);
isShowLogin = true;
@ -180,15 +181,16 @@ class _MyLoginPageState extends State<LoginPage> with TickerProviderStateMixin {
_controllerInviteCode.addListener(() {
print(_controllerInviteCode.text);
if (_controllerInviteCode.text != null && _controllerInviteCode.text != "") {
if (_controllerInviteCode.text.length == 6 ){
if (_controllerInviteCode.text != null &&
_controllerInviteCode.text != "") {
if (_controllerInviteCode.text.length == 6) {
statusInviteTextColor = Color(0xFF353535);
statusInviteLineColor = Color(0xFF32A060);
statusInviteVisible = false;
statusInviteLineColor = Color(0xFF32A060);
statusInviteVisible = false;
} else {
statusInviteTextColor = Color(0xFFF72626);
statusInviteLineColor = Color(0xFFF72626);
statusInviteVisible = true;
statusInviteTextColor = Color(0xFFF72626);
statusInviteLineColor = Color(0xFFF72626);
statusInviteVisible = true;
}
}
});
@ -206,7 +208,6 @@ class _MyLoginPageState extends State<LoginPage> with TickerProviderStateMixin {
Color statusInviteTextColor = Color(0xFF353535);
Color statusInviteLineColor = Color(0xFF32A060);
_sendCode() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
if (!sharedPreferences.containsKey("isShowPrivacyPolicy") ||
@ -228,7 +229,8 @@ class _MyLoginPageState extends State<LoginPage> with TickerProviderStateMixin {
}
if (_sendCodeStatus == 0) {
client
.sendVerify(areaCode,mobile)
.sendVerify(
{"areaCode": areaCode, "mobile": mobile, "verification": ""})
.then((value) => {
if (value.isSuccess)
{_sendCodeStatus = 1, countdown()}
@ -242,8 +244,8 @@ class _MyLoginPageState extends State<LoginPage> with TickerProviderStateMixin {
}
})
.catchError((error) {
SmartDialog.showToast("$error", alignment: Alignment.center);
});
SmartDialog.showToast("$error", alignment: Alignment.center);
});
}
}
@ -324,28 +326,35 @@ class _MyLoginPageState extends State<LoginPage> with TickerProviderStateMixin {
var param = {
"capcha": code,
"mobile": mobile,
"invite":invite,
"invite": invite,
};
EasyLoading.show(status: S.of(context).zhengzaijiazai,maskType: EasyLoadingMaskType.black);
BaseData<LoginInfo> value = await client.memberLogin(param).catchError((error) {
EasyLoading.show(
status: S.of(context).zhengzaijiazai,
maskType: EasyLoadingMaskType.black);
BaseData<LoginInfo> value =
await client.memberLogin(param).catchError((error) {
print(error.message);
SmartDialog.showToast(AppUtils.dioErrorTypeToString(error.type), alignment: Alignment.center);
SmartDialog.showToast(AppUtils.dioErrorTypeToString(error.type),
alignment: Alignment.center);
});
// EasyLoading.show(status: S.of(context).zhengzaijiazai);
Future.delayed(Duration(seconds:2), () {
if (value !=null && value.isSuccess) {
Future.delayed(Duration(seconds: 2), () {
if (value != null && value.isSuccess) {
saveUserJson(value.data.authInfo.toJson());
eventBus.fire(EventType(3));
Navigator.of(context).pushNamedAndRemoveUntil(
'/router/main_page',
(route) => false,arguments:{"invite":invite,"interviewCouponList":value.data.interviewCouponList,
"firstLoginCouponList":value.data.firstLoginCouponList});
'/router/main_page', (route) => false,
arguments: {
"invite": invite,
"interviewCouponList": value.data.interviewCouponList,
"firstLoginCouponList": value.data.firstLoginCouponList
});
EasyLoading.dismiss();
} else {
if(value?.msg !=null)
SmartDialog.showToast("${value?.msg ??""}", alignment: Alignment.center);
if (value?.msg != null)
SmartDialog.showToast("${value?.msg ?? ""}",
alignment: Alignment.center);
}
});
}
@ -515,7 +524,7 @@ class _MyLoginPageState extends State<LoginPage> with TickerProviderStateMixin {
height: MediaQuery.of(context).size.height * 0.78,
margin: EdgeInsets.only(top: 56.h),
alignment: Alignment.topCenter,
child:Image.asset(
child: Image.asset(
"assets/image/icon_login_logo.webp",
width: 91.w,
height: 91.h,
@ -708,7 +717,7 @@ class _MyLoginPageState extends State<LoginPage> with TickerProviderStateMixin {
),
),
Container(
height:30.h,
height: 30.h,
width: MediaQuery.of(context).size.width - 80.h,
// margin: EdgeInsets.only(top: 12.h),
child: TextField(
@ -754,7 +763,7 @@ class _MyLoginPageState extends State<LoginPage> with TickerProviderStateMixin {
color: statusPhoneLineColor,
),
SizedBox(
height:30.h,
height: 30.h,
child: Visibility(
visible: statusPhoneVisible,
child: Text(
@ -775,7 +784,7 @@ class _MyLoginPageState extends State<LoginPage> with TickerProviderStateMixin {
),
),
Container(
height:30.h,
height: 30.h,
width: MediaQuery.of(context).size.width - 80.h,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
@ -878,7 +887,7 @@ class _MyLoginPageState extends State<LoginPage> with TickerProviderStateMixin {
),
),
SizedBox(
height:25.h,
height: 25.h,
// child: Visibility(
// visible: statusPhoneVisible,
// child: Text(
@ -891,89 +900,95 @@ class _MyLoginPageState extends State<LoginPage> with TickerProviderStateMixin {
// ),
),
GestureDetector(
onTap: (){
onTap: () {
setState(() {
invitationCode = false;
});
},
child:
invitationCode ?
Container(child:
Column(children: [
Text(
S.of(context).woyouyaoqingma,
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
Container(
width: 56.w,
height: 0.5,
color: Colors.black,
),
],),):Container(child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
S.of(context).input_invite_code_hide,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
color: Colors.black,
),
child: invitationCode
? Container(
child: Column(
children: [
Text(
S.of(context).woyouyaoqingma,
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
Container(
width: 56.w,
height: 0.5,
color: Colors.black,
),
],
),
Container(
height:25.h,
width: MediaQuery.of(context).size.width - 80.h,
child: TextField(
style: TextStyle(
height: 1.h,
fontSize: 16.sp,
color: statusInviteTextColor,
)
: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
S.of(context).input_invite_code_hide,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
onChanged: (value){
setState(() {
});
},
controller: _controllerInviteCode,
keyboardType: TextInputType.text,
decoration: InputDecoration(
errorBorder: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
hintText: "",
// contentPadding: EdgeInsets.only(top: 12, bottom: 12, left: 12),
hintStyle: TextStyle(
fontSize: 10.sp,
color: Color(0xFFA29E9E),
Container(
height: 25.h,
width: MediaQuery.of(context).size.width - 80.h,
child: TextField(
style: TextStyle(
height: 1.h,
fontSize: 16.sp,
color: statusInviteTextColor,
),
onChanged: (value) {
setState(() {});
},
controller: _controllerInviteCode,
keyboardType: TextInputType.text,
decoration: InputDecoration(
errorBorder: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
hintText: "",
// contentPadding: EdgeInsets.only(top: 12, bottom: 12, left: 12),
hintStyle: TextStyle(
fontSize: 10.sp,
color: Color(0xFFA29E9E),
),
),
textInputAction: TextInputAction.next,
inputFormatters: [
LengthLimitingTextInputFormatter(6)
],
cursorColor: Colors.grey,
maxLines: 1,
),
),
textInputAction: TextInputAction.next,
inputFormatters: [LengthLimitingTextInputFormatter(6)],
cursorColor: Colors.grey,
maxLines: 1,
),
),
Container(
height: 1.h,
width: MediaQuery.of(context).size.width - 80.h,
color: statusInviteLineColor,
margin: EdgeInsets.only(bottom: 10.h),
),
Visibility(
visible: statusInviteVisible,
child: Text(
S.of(context).invite_code_error,
style: TextStyle(
color: Color(0xFFF72626),
fontSize: 12.sp,
Container(
height: 1.h,
width: MediaQuery.of(context).size.width - 80.h,
color: statusInviteLineColor,
margin: EdgeInsets.only(bottom: 10.h),
),
),
Visibility(
visible: statusInviteVisible,
child: Text(
S.of(context).invite_code_error,
style: TextStyle(
color: Color(0xFFF72626),
fontSize: 12.sp,
),
),
),
],
),
],
),),
),
),
Expanded(
flex: 1,

72
lib/login/new_login_page.dart

@ -26,6 +26,7 @@ import 'package:sharesdk_plugin/sharesdk_interface.dart';
import '../main.dart';
import '../retrofit/data/channels_list.dart';
import 'captcha/block_puzzle_captcha.dart';
class NewLoginPage extends StatefulWidget {
final Map<String, dynamic> arguments;
@ -56,6 +57,7 @@ class _NewLoginPage extends State<NewLoginPage> {
String area = "+86";
String channelName;
ChannelsList channelsList;
String mobile;
@override
void initState() {
@ -127,37 +129,22 @@ class _NewLoginPage extends State<NewLoginPage> {
showAlertDialog();
return;
}
if (!checkStatus) {
SmartDialog.showToast(S.of(context).gouxuanxieyi,
alignment: Alignment.center);
return;
}
var mobile = _controllerPhone.text;
mobile = _controllerPhone.text;
if (mobile == "") {
mobileStatus = 2;
mobileErrorText = S.of(context).qingshurushoujihao;
SmartDialog.showToast(S.of(context).qingshurushoujihao,
alignment: Alignment.center);
setState(() {});
return;
}
if (!checkStatus) {
SmartDialog.showToast(S.of(context).gouxuanxieyi,
alignment: Alignment.center);
return;
}
if (_sendCodeStatus == 0) {
apiService
.sendVerify(area, mobile)
.then((value) => {
if (value.isSuccess)
{_sendCodeStatus = 1, countdown()}
else
{
btnText = S.of(context).send_code,
_sendCodeStatus = 0,
SmartDialog.showToast("${value.msg}",
alignment: Alignment.center),
refresh()
}
})
.catchError((error) {
SmartDialog.showToast(AppUtils.dioErrorTypeToString(error.type),
alignment: Alignment.center);
});
loadingBlockPuzzle(context);
}
}
@ -964,4 +951,41 @@ class _NewLoginPage extends State<NewLoginPage> {
r'^((13[0-9])|(14[0-9])|(15[0-9])|(16[0-9])|(17[0-9])|(18[0-9])|(19[0-9]))\d{8}$');
return exp.hasMatch(mobile);
}
//
loadingBlockPuzzle(BuildContext context,
{barrierDismissible = true}) {
showDialog<Null>(
context: context,
barrierDismissible: barrierDismissible,
builder: (BuildContext context) {
return BlockPuzzleCaptchaPage(
onSuccess: (v) {
sendSms(v);
},
onFail: () {
print("onFail");
},
);
},
);
}
sendSms(v) async{
BaseData baseData = await apiService.sendVerify({"areaCode":area, "mobile": mobile, "verification": v}).catchError((onError) {
SmartDialog.showToast(AppUtils.dioErrorTypeToString(onError.type),
alignment: Alignment.center);});
if (baseData != null && baseData.isSuccess) {
_sendCodeStatus = 1;
countdown();
SmartDialog.showToast(baseData.data,
alignment: Alignment.center);
}else{
btnText = S.of(context).send_code;
_sendCodeStatus = 0;
SmartDialog.showToast("${baseData.msg}",
alignment: Alignment.center);
refresh();
}
}
}

4
lib/retrofit/min_api.dart

@ -27,8 +27,8 @@ import 'data/shopping_home_config.dart';
part 'min_api.g.dart';
// const localBaseUrl = "http://app-api.test.yixinhuixiang.com/app/";///
const localBaseUrl = "http://pos-test.api.lotus-wallet.com/app/";///
const localBaseUrl = "http://app-api.test.yixinhuixiang.com/app/";///
// const localBaseUrl = "http://pos-test.api.lotus-wallet.com/app/";///
const serviceBaseUrl = "https://pos.api.lotus-wallet.com/app/";///线

25
lib/retrofit/retrofit_api.dart

@ -18,6 +18,7 @@ import 'package:huixiang/retrofit/data/order_info.dart';
import 'package:huixiang/view_widget/login_tips_dialog.dart';
import 'package:retrofit/retrofit.dart';
import '../login/captcha/click_word_captcha.dart';
import '../utils/flutter_utils.dart';
import 'data/achievement_detail_list.dart';
import 'data/activity_pos.dart';
@ -65,8 +66,8 @@ import 'data/wx_pay.dart';
part 'retrofit_api.g.dart';
// const localBaseUrl = "http://platform-api.test.yixinhuixiang.com/app/";///
const localBaseUrl = "http://platform.test.api.lotus-wallet.com/app/";///
const localBaseUrl = "http://platform-api.test.yixinhuixiang.com/app/";///
// const localBaseUrl = "http://platform.test.api.lotus-wallet.com/app/";///
const serviceBaseUrl = "https://pos.platform.lotus-wallet.com/app/";
///线
@ -127,8 +128,7 @@ abstract class ApiService {
}
debugPrint("code = ${response.statusCode}");
if (response.request.path != "/creditGoods/list")
p(jsonEncode(response.data));
p(jsonEncode(response.data));
// debugPrint(jsonEncode(response.data), wrapWidth: response.data.toString().length * 10);
@ -165,6 +165,8 @@ abstract class ApiService {
static void p(String msg) {
//String的length是字符数量不是字节数量所以为了防止中文字符过多
// 4*1024MAX字节打印长度改为1000字符数
if(msg.length > 10000)
return;
int maxStrLength = 900;
//1000
while (msg.length > maxStrLength) {
@ -196,9 +198,8 @@ abstract class ApiService {
Future<BaseData<ChannelsList>> appChannels();
///
@GET("/auth/sendVerify/{areaCode}/{mobile}")
Future<BaseData> sendVerify(
@Path("areaCode") String areaCode, @Path("mobile") String mobile);
@POST("/auth/sendVerify")
Future<BaseData<dynamic>> sendVerify(@Body() Map<String, dynamic> param);
///
@POST("/creditGoods/list")
@ -368,6 +369,16 @@ abstract class ApiService {
Future<BaseData<PageInfo<Message>>> msgList(
@Body() Map<String, dynamic> param);
///
@POST("/captcha/get")
Future<ClickWordCaptchaModel> captchaGet(
@Body() Map<String, dynamic> param);
///
@POST("/captcha/check")
Future<bool> captchaCheck(
@Body() Map<String, dynamic> param);
///App消息详情
@GET("/app-msg/{id}")
Future<BaseData> queryMsg(@Path("id") String id);

64
lib/retrofit/retrofit_api.g.dart

@ -117,24 +117,23 @@ class _ApiService implements ApiService {
}
@override
Future<BaseData<dynamic>> sendVerify(areaCode, mobile) async {
ArgumentError.checkNotNull(mobile, 'mobile');
ArgumentError.checkNotNull(areaCode, 'areaCode');
Future<BaseData<dynamic>> sendVerify(param) async {
ArgumentError.checkNotNull(param, 'param');
const _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _data = <String, dynamic>{};
final _result = await _dio.request<Map<String, dynamic>>(
'/auth/sendVerify/$areaCode/$mobile',
_data.addAll(param ?? <String, dynamic>{});
final _result = await _dio.request<Map<String, dynamic>>('/auth/sendVerify',
queryParameters: queryParameters,
options: RequestOptions(
method: 'GET',
method: 'POST',
headers: <String, dynamic>{},
extra: _extra,
baseUrl: baseUrl),
data: _data);
final value = BaseData<dynamic>.fromJson(
_result.data,
(json) => json as dynamic,
(json) => json == null ? null :json as dynamic,
);
return value;
}
@ -1032,6 +1031,46 @@ class _ApiService implements ApiService {
return value;
}
@override
Future<ClickWordCaptchaModel> captchaGet(param) async {
ArgumentError.checkNotNull(param, 'param');
const _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _data = <String, dynamic>{};
_data.addAll(param ?? <String, dynamic>{});
final _result = await _dio.request<Map<String, dynamic>>('/captcha/get',
queryParameters: queryParameters,
options: RequestOptions(
method: 'POST',
headers: <String, dynamic>{},
extra: _extra,
baseUrl: baseUrl),
data: _data);
final value =
(_result.data['repCode'] != '0000' || _result.data['repData'] == null)
? null
: ClickWordCaptchaModel.fromMap(_result.data['repData']);
return value;
}
@override
Future<bool> captchaCheck(param) async {
ArgumentError.checkNotNull(param, 'param');
const _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _data = <String, dynamic>{};
_data.addAll(param ?? <String, dynamic>{});
final _result = await _dio.request<Map<String, dynamic>>('/captcha/check',
queryParameters: queryParameters,
options: RequestOptions(
method: 'POST',
headers: <String, dynamic>{},
extra: _extra,
baseUrl: baseUrl),
data: _data);
return (_result.data['repCode'] != '0000' || _result.data['repData'] == null || !_result.data['repData']['result']);
}
@override
Future<BaseData<dynamic>> queryMsg(id) async {
ArgumentError.checkNotNull(id, 'id');
@ -2119,12 +2158,15 @@ class _ApiService implements ApiService {
final _data = <String, dynamic>{};
final _result = await _dio.request<List<int>>('/ipJson.jsp',
queryParameters: queryParameters,
options: RequestOptions(method: 'GET', extra: _extra, baseUrl: baseUrl,responseType: ResponseType.bytes),
options: RequestOptions(
method: 'GET',
extra: _extra,
baseUrl: baseUrl,
responseType: ResponseType.bytes),
data: _data);
var ts = gbk.decode(_result.data);
final value = IpData.fromJson(jsonDecode(ts
.substring(ts.indexOf("{\"ip\":\""))
.replaceAll(");}", "")));
final value = IpData.fromJson(jsonDecode(
ts.substring(ts.indexOf("{\"ip\":\"")).replaceAll(");}", "")));
return value;
}

61
lib/setting/binding_phone_page.dart

@ -14,6 +14,7 @@ import 'package:huixiang/utils/font_weight.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../login/captcha/block_puzzle_captcha.dart';
import '../view_widget/border_text.dart';
class BindingPhonePage extends StatefulWidget {
@ -42,6 +43,8 @@ class _BindingPhonePage extends State<BindingPhonePage> {
Timer _timer;
UserInfo userInfo;
int phoneState = 1;
String mobile;
String newMobile;
@override
void initState() {
@ -114,16 +117,6 @@ class _BindingPhonePage extends State<BindingPhonePage> {
///
verificationCode() async {
var mobile = _controllerPhone.text;
if (mobile == "" && phoneState == 1) {
SmartDialog.showToast(S.of(context).qingshurushoujihao, alignment: Alignment.center);
return;
}
var newMobile = _controllerNewPhone.text;
if (newMobile == "" && phoneState == 2) {
SmartDialog.showToast(S.of(context).qingshurushoujihao, alignment: Alignment.center);
return;
}
if (apiService == null) {
SharedPreferences value = await SharedPreferences.getInstance();
apiService = ApiService(Dio(),
@ -466,7 +459,22 @@ class _BindingPhonePage extends State<BindingPhonePage> {
alignment: Alignment.bottomCenter,
child: InkWell(
onTap: (){
verificationCode();
mobile = _controllerPhone.text;
if (mobile == "" && phoneState == 1) {
SmartDialog.showToast(S.of(context).qingshurushoujihao, alignment: Alignment.center);
return;
}
newMobile = _controllerNewPhone.text;
if (newMobile == "" && phoneState == 2) {
SmartDialog.showToast(S.of(context).qingshurushoujihao, alignment: Alignment.center);
return;
}
if(mobile != (userInfo?.phone??"")){
SmartDialog.showToast("手机号码不正确",
alignment: Alignment.center);
return ;
}
loadingBlockPuzzle(context);
},
child: BorderText(
text: btnText,
@ -691,7 +699,17 @@ class _BindingPhonePage extends State<BindingPhonePage> {
alignment: Alignment.bottomCenter,
child: InkWell(
onTap:(){
verificationCode();
mobile = _controllerPhone.text;
if (mobile == "" && phoneState == 1) {
SmartDialog.showToast(S.of(context).qingshurushoujihao, alignment: Alignment.center);
return;
}
newMobile = _controllerNewPhone.text;
if (newMobile == "" && phoneState == 2) {
SmartDialog.showToast(S.of(context).qingshurushoujihao, alignment: Alignment.center);
return;
}
loadingBlockPuzzle(context);
},
child: BorderText(
text: btnText,
@ -803,4 +821,23 @@ class _BindingPhonePage extends State<BindingPhonePage> {
),
);
}
//
loadingBlockPuzzle(BuildContext context,
{barrierDismissible = true}) {
showDialog<Null>(
context: context,
barrierDismissible: barrierDismissible,
builder: (BuildContext context) {
return BlockPuzzleCaptchaPage(
onSuccess: (v) {
verificationCode();
},
onFail: () {
print("onFail");
},
);
},
);
}
}

42
lib/setting/logout_ing.dart

@ -17,6 +17,7 @@ import 'package:huixiang/view_widget/my_appbar.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../login/captcha/block_puzzle_captcha.dart';
import '../main.dart';
class LogoutIng extends StatefulWidget {
@ -73,16 +74,20 @@ class _LogoutIng extends State<LogoutIng> {
}
if (_sendCodeStatus != 0)
return;
if (apiService == null) {
SharedPreferences value = await SharedPreferences.getInstance();
apiService = ApiService(
Dio(),
context: context,
token: value.getString("token"),
showLoading: true
);
loadingBlockPuzzle(context);
}
BaseData baseData = await apiService.sendVerify("+86",mobile).catchError((onError) {});
sendProving(v) async{
if (apiService == null) {
SharedPreferences value = await SharedPreferences.getInstance();
apiService = ApiService(
Dio(),
context: context,
token: value.getString("token"),
showLoading: true
);
}
BaseData baseData = await apiService.sendVerify({"areaCode": "+86", "mobile": phoneController.text, "verification": v}).catchError((onError) {});
if (baseData != null && baseData.isSuccess) {
countdown();
SmartDialog.showToast(baseData.data,
@ -497,4 +502,23 @@ class _LogoutIng extends State<LogoutIng> {
// xgFlutterPlugin.stopXg();
// }
}
//
loadingBlockPuzzle(BuildContext context,
{barrierDismissible = true}) {
showDialog<Null>(
context: context,
barrierDismissible: barrierDismissible,
builder: (BuildContext context) {
return BlockPuzzleCaptchaPage(
onSuccess: (v) {
sendProving(v);
},
onFail: () {
print("onFail");
},
);
},
);
}
}

23
lib/setting/platform_code_page.dart

@ -15,6 +15,8 @@ import 'package:huixiang/view_widget/pay_selected_dialog.dart';
import 'package:pin_input_text_field/pin_input_text_field.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../login/captcha/block_puzzle_captcha.dart';
class PlatformCodePage extends StatefulWidget {
final Map<String, dynamic> arguments;
@ -189,7 +191,7 @@ class _PlatformCodePage extends State<PlatformCodePage> {
GestureDetector(
onTap: () {
setState(() {
sendCode();
loadingBlockPuzzle(context);
});
},
child: Container(
@ -308,4 +310,23 @@ class _PlatformCodePage extends State<PlatformCodePage> {
),
);
}
//
loadingBlockPuzzle(BuildContext context,
{barrierDismissible = true}) {
showDialog<Null>(
context: context,
barrierDismissible: barrierDismissible,
builder: (BuildContext context) {
return BlockPuzzleCaptchaPage(
onSuccess: (v) {
sendCode();
},
onFail: () {
print("onFail");
},
);
},
);
}
}

86
lib/utils/captcha_util.dart

@ -0,0 +1,86 @@
import 'dart:convert';
import 'package:steel_crypt/steel_crypt.dart';
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';
class CaptchaUtil{
///aes加密
/// [key]AesCrypt加密key
/// [content]
static String aesEncode({String key, String content}) {
var aesCrypt = AesCrypt(
key: base64UrlEncode(key.codeUnits), padding: PaddingAES.pkcs7);
return aesCrypt.ecb.encrypt(inp: content);
}
///aes解密
/// [key]aes解密key
/// [content]
static String aesDecode({String key, String content}) {
var aesCrypt = AesCrypt(
key: base64UrlEncode(key.codeUnits), padding: PaddingAES.pkcs7);
return aesCrypt.ecb.decrypt(enc: content);
}
/// isEmpty.
static bool isEmpty(Object value) {
if (value == null) return true;
if (value is String && value.isEmpty) {
return true;
}
return false;
}
//list length == 0 || list == null
static bool isListEmpty(Object value) {
if (value == null) return true;
if (value is List && value.length == 0) {
return true;
}
return false;
}
static String jsonFormat(Map<dynamic, dynamic> map) {
Map _map = Map<String, Object>.from(map);
JsonEncoder encoder = JsonEncoder.withIndent(' ');
return encoder.convert(_map);
}
static String generateMd5(String data){
var content = new Utf8Encoder().convert(data);
var digest = md5.convert(content);
return hex.encode(digest.bytes);
}
static signData(Object params, tokenStr) async {
var time = new DateTime.now().millisecondsSinceEpoch;
String token = tokenStr;
Map<String, dynamic> reqData = new Map();
Map<String, dynamic> paramsObj = new Map();
paramsObj = params as Map<String, dynamic>;
var arr = [];
//
paramsObj?.forEach((key, value) => arr.add(key));
//
Map cr = new Map();
cr['token'] = token;
cr['time'] = time.toString();
cr['reqData'] = json.encode(paramsObj);
var array = [];
cr.forEach((key, value) => array.add(key));
array.sort();
var str = '';
for (var i = 0; i < array.length; i++) {
var key = array[i];
var value = cr[key];
str += key + value;
}
reqData["time"] = time;
reqData["token"] = token;
reqData['reqData'] = params;
reqData['sign'] = generateMd5(str);
return reqData;
}
}

133
lib/utils/widget_util.dart

@ -0,0 +1,133 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'captcha_util.dart';
/**
* @Author: thl
* @GitHub: https://github.com/Sky24n
* @Email: 863764940@qq.com
* @Email: sky24no@gmail.com
* @Description: Widget Util.
* @Date: 2018/9/10
*/
/// Widget Util.
class WidgetUtil {
bool _hasMeasured = false;
double _width;
double _height;
/// Widget rendering listener.
/// Widget渲染监听.
/// context: Widget context.
/// isOnce: true,Continuous monitoring false,Listen only once.
/// onCallBack: Widget Rect CallBack.
void asyncPrepare(
BuildContext context, bool isOnce, ValueChanged<Rect> onCallBack) {
if (_hasMeasured) return;
WidgetsBinding.instance.addPostFrameCallback((Duration timeStamp) {
RenderObject box = context.findRenderObject();
if (box != null) {
if (isOnce) _hasMeasured = true;
double width = box.semanticBounds.width;
double height = box.semanticBounds.height;
if (_width != width || _height != height) {
_width = width;
_height = height;
if (onCallBack != null) onCallBack(box.semanticBounds);
}
}
});
}
/// Widget渲染监听.
void asyncPrepares(bool isOnce, ValueChanged<Rect> onCallBack) {
if (_hasMeasured) return;
WidgetsBinding.instance.addPostFrameCallback((Duration timeStamp) {
if (isOnce) _hasMeasured = true;
if (onCallBack != null) onCallBack(null);
});
}
///get Widget Bounds (width, height, left, top, right, bottom and so on).Widgets must be rendered completely.
///widget Rect
static Rect getWidgetBounds(BuildContext context) {
RenderObject box = context.findRenderObject();
return (box != null) ? box.semanticBounds : Rect.zero;
}
///Get the coordinates of the widget on the screen.Widgets must be rendered completely.
///widget在屏幕上的坐标,widget必须渲染完成
static Offset getWidgetLocalToGlobal(BuildContext context) {
RenderBox box = context.findRenderObject() as RenderBox;
return box == null ? Offset.zero : box.localToGlobal(Offset.zero);
}
/// get image width heightload error return Rect.zero.unit px
/// Rect.zero. px
/// image
/// url network
/// local url , package
static Future<Rect> getImageWH(
{Image image, String url, String localUrl, String package}) {
if (CaptchaUtil.isEmpty(image) &&
CaptchaUtil.isEmpty(url) &&
CaptchaUtil.isEmpty(localUrl)) {
return Future.value(Rect.zero);
}
Completer<Rect> completer = Completer<Rect>();
Image img = image ?? ((url != null && url.isNotEmpty)
? Image.network(url)
: Image.asset(localUrl, package: package));
img.image
.resolve(const ImageConfiguration())
.addListener(ImageStreamListener(
(ImageInfo info, bool _) {
completer.complete(Rect.fromLTWH(0, 0, info.image.width.toDouble(),
info.image.height.toDouble()));
},
onError: (Object exception, StackTrace stackTrace) {
completer.completeError(exception, stackTrace);
},
));
return completer.future;
}
/// get image width height, load error throw exception.unit px
/// . px
/// image
/// url network
/// local url (full path/example"assets/images/ali_connors.png"""assets/images/3.0x/ali_connors.png"" );
/// package
static Future<Rect> getImageWHE(
{Image image,
String url,
String localUrl,
String package}) {
if (CaptchaUtil.isEmpty(image) &&
CaptchaUtil.isEmpty(url) &&
CaptchaUtil.isEmpty(localUrl)) {
return Future.error("image is null.");
}
Completer<Rect> completer = Completer<Rect>();
Image img = image != null
? image
: ((url != null && url.isNotEmpty)
? Image.network(url)
: Image.asset(localUrl, package: package));
img.image
.resolve(const ImageConfiguration())
.addListener(ImageStreamListener(
(ImageInfo info, bool _) {
completer.complete(Rect.fromLTWH(0, 0, info.image.width.toDouble(),
info.image.height.toDouble()));
},
onError: (Object exception, StackTrace stackTrace) {
completer.completeError(exception, stackTrace);
},
));
return completer.future;
}
}

42
pubspec.lock

@ -15,6 +15,20 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.2"
args:
dependency: transitive
description:
name: args
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.1"
asn1lib:
dependency: transitive
description:
name: asn1lib
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.1"
async:
dependency: transitive
description:
@ -106,6 +120,13 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.16.0"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.0"
crypto:
dependency: transitive
description:
@ -141,6 +162,13 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.1"
encrypt:
dependency: "direct main"
description:
name: encrypt
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.0.1"
equatable:
dependency: transitive
description:
@ -616,6 +644,13 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.3"
pointycastle:
dependency: transitive
description:
name: pointycastle
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.7.3"
process:
dependency: transitive
description:
@ -775,6 +810,13 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.10.0"
steel_crypt:
dependency: "direct main"
description:
name: steel_crypt
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.0+1"
stream_channel:
dependency: transitive
description:

3
pubspec.yaml

@ -62,6 +62,9 @@ dependencies:
visibility_detector: ^0.3.3
steel_crypt: ^3.0.0+1
encrypt: ^5.0.1
event_bus: ^2.0.0
intl: ^0.17.0
shared_preferences: ^2.0.6

Loading…
Cancel
Save