【flutter滑动拼图验证码】

news2024/11/25 8:19:03

在这里插入图片描述
Java后台使用aj_captcha插件,提供/captcha/get(获取captcha底图和拼块图片)、/captcha/check(验证拼图偏移量)这两个接口。并且这个插件在GitHub上有源码。
1.先准备好aj_captcha的工具类:

import 'dart:convert';

import 'package:steel_crypt/steel_crypt.dart';
//import 'package:encrypt/encrypt.dart';

class EncryptUtil {
  ///aes加密
  /// [key]AesCrypt加密key
  /// [content] 需要加密的内容字符串
  static String aesEncode({String key, String content}) {
    // var aesEncrypter = AesCrypt(key, 'ecb', 'pkcs7');

    var encodeKey = base64UrlEncode(utf8.encode(key));
    var aesEncrypter = AesCrypt(padding: PaddingAES.pkcs7, key: encodeKey);
    return aesEncrypter.ecb.encrypt(inp: content);
  }

  ///aes解密
  /// [key]aes解密key
  /// [content] 需要加密的内容字符串
  static String aesDecode({String key, String content}) {
    // var aesEncrypter = AesCrypt(key, 'ecb', 'pkcs7');
    var encodeKey = base64UrlEncode(utf8.encode(key));
    var aesEncrypter = AesCrypt(key: encodeKey, padding: PaddingAES.pkcs7);
    // return aesEncrypter.decrypt(content);
    return aesEncrypter.ecb.decrypt(enc: content);
  }
}

import 'dart:convert';

class ObjectUtils {
  /// 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);
  }
}

import 'dart:async';
import 'package:flutter/widgets.dart';
import 'object_utils.dart';

/// 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) {
      RenderBox box = context.findRenderObject();
      if (box != null && box.semanticBounds != 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) {
    RenderBox box = context.findRenderObject();
    return (box != null && box.semanticBounds != 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();
    return box == null ? Offset.zero : box.localToGlobal(Offset.zero);
  }

  /// get image width height,load 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 (ObjectUtils.isEmpty(image) &&
        ObjectUtils.isEmpty(url) &&
        ObjectUtils.isEmpty(localUrl)) {
      return Future.value(Rect.zero);
    }
    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(new ImageConfiguration())
        .addListener(new ImageStreamListener(
          (ImageInfo info, bool _) {
            completer.complete(Rect.fromLTWH(0, 0, info.image.width.toDouble(),
                info.image.height.toDouble()));
          },
          onError: (dynamic 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 (ObjectUtils.isEmpty(image) &&
        ObjectUtils.isEmpty(url) &&
        ObjectUtils.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(new ImageConfiguration())
        .addListener(new ImageStreamListener(
          (ImageInfo info, bool _) {
            completer.complete(Rect.fromLTWH(0, 0, info.image.width.toDouble(),
                info.image.height.toDouble()));
          },
          onError: (dynamic exception, StackTrace stackTrace) {
            completer.completeError(exception, stackTrace);
          },
        ));

    return completer.future;
  }
}

2.绘制验证弹窗

import 'dart:convert';
import 'package:test/constant.dart';
import 'package:test/generated/l10n.dart';
import 'package:test/http/DioManager.dart';
import 'package:tset/util/easy_loading_util.dart';
import 'package:test/util/encrypt_util.dart';
import 'package:test/util/object_utils.dart';
import 'package:test/util/widtet_util.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';



typedef VoidSuccessCallback = dynamic Function(String c);

class CaptchaPage extends StatefulWidget {
  final VoidSuccessCallback onSuccess; //拖放完成后验证成功回调
  final VoidCallback onFail; //拖放完成后验证失败回调

  CaptchaPage({this.onSuccess, this.onFail});

  
  _CaptchaPageState createState() => _CaptchaPageState();
}

class _CaptchaPageState extends State<CaptchaPage>
    with TickerProviderStateMixin {
  /// 是否启用
  bool enable = true;

//  String baseImageBase64 =
//      "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";
  String baseImageBase64 = "";
  String slideImageBase64 = "";
  String captchaToken = "";
  String secretKey = "";
  Size baseSize = Size.zero; //底部基类图片
  Size slideSize = Size.zero; //滑块图片

  var sliderColor = Colors.white; //滑块的背景色
  var sliderIcon = Icons.arrow_forward; //滑块的图标
  var movedXBorderColor = Colors.white; //滑块拖动时,左边已滑的区域边框颜色
  double sliderStartX = 0; //滑块未拖前的X坐标
  double sliderXMoved = 0;
  bool sliderMoveFinish = false; //滑块拖动结束
  bool checkResultAfterDrag = false; //拖动后的校验结果

  //-------------动画------------
  int _checkMilliseconds = 0; //滑动时间
  bool _showTimeLine = false; //是否显示动画部件
  bool _checkSuccess = false; //校验是否成功
  AnimationController controller;


  var _ratio = 3.0;
  var dialogWidth;
  GlobalKey _baseImageKey = new GlobalKey();
  //高度动画
  Animation<double> offsetAnimation;

  //------------动画------------

  //校验通过
  void checkSuccess(String content) {
    setState(() {
      checkResultAfterDrag = true;
      _checkSuccess = true;
      _showTimeLine = true;
    });
    _forwardAnimation();
    updateSliderColorIcon();

    //刷新验证码
    Future.delayed(Duration(milliseconds: 1000)).then((v) {
      _reverseAnimation().then((v) {
        setState(() {
          _showTimeLine = false;
        });
        //回调
        if (widget.onSuccess != null) {
          widget.onSuccess(content);
          // NavigatorUtil.pop(value: true);
        }
        Navigator.pop(context);
      });
    });
  }

  //校验失败
  void checkFail() {
    setState(() {
      _showTimeLine = true;
      _checkSuccess = false;
      checkResultAfterDrag = false;
    });
    _forwardAnimation();
    updateSliderColorIcon();

    //刷新验证码
    Future.delayed(Duration(milliseconds: 1000)).then((v) {
      _reverseAnimation().then((v) {
        setState(() {
          _showTimeLine = false;
        });
        loadCaptcha();
        //回调
        if (widget.onFail != null) {
          widget.onFail();
        }
      });
    });
  }

  //重设滑动颜色与图标
  void updateSliderColorIcon() {
    var _sliderColor = null; //滑块的背景色
    var _sliderIcon = null; //滑块的图标
    var _movedXBorderColor = null; //滑块拖动时,左边已滑的区域边框颜色

    //滑块的背景色
    if (sliderMoveFinish) {
      //拖动结束
      _sliderColor = checkResultAfterDrag ? Colors.green : Colors.red;
      _sliderIcon = checkResultAfterDrag ? Icons.check : Icons.close;
      _movedXBorderColor = checkResultAfterDrag ? Colors.green : Colors.red;
    } else {
      //拖动未开始或正在拖动中
      _sliderColor = sliderXMoved > 0 ? Color(0xffe63850) : Colors.white;
      _sliderIcon = Icons.arrow_forward;
      _movedXBorderColor = Color(0xff89F2D0);
    }

    sliderColor = _sliderColor;
    sliderIcon = _sliderIcon;
    movedXBorderColor = _movedXBorderColor;
    setState(() {});
  }

  //加载验证码
  void loadCaptcha() {
    setState(() {
      _showTimeLine = false;
      sliderMoveFinish = false;
      checkResultAfterDrag = false;
      sliderColor = Colors.white; //滑块的背景色
      sliderIcon = Icons.arrow_forward; //滑块的图标
      movedXBorderColor = Colors.white; //滑块拖动时,左边已滑的区域边框颜色
    });
    DioManager.getInstance()
        .post(Constant.baseUrl + '/captcha/get', {"captchaType": "blockPuzzle"},
            (res) async {
      if (res['repCode'] != '0000' || res['repData'] == null) {
        setState(() {
          secretKey = "";
        });
        if (res['repCode'] == '6202') {
          enable = false;
          esLoadingError('您失败的次数太多啦,请稍后试试吧!');
        }

        return;
      }

      Map<String, dynamic> repData = res['repData'];
      print("--------------");
      print(repData.keys);
      print("${repData["point"]}");

      sliderXMoved = 0;
      sliderStartX = 0;
      captchaToken = '';
      checkResultAfterDrag = false;

      baseImageBase64 = repData["originalImageBase64"];
      baseImageBase64 = baseImageBase64.replaceAll('\n', '');
      secretKey = repData['secretKey'] ?? "";
      slideImageBase64 = repData["jigsawImageBase64"];
      slideImageBase64 = slideImageBase64.replaceAll('\n', '');
      captchaToken = repData["token"];

      print(baseImageBase64);

      var baseR = await WidgetUtil.getImageWH(
          image: Image.memory(Base64Decoder().convert(baseImageBase64)));
      baseSize = baseR.size;

      var silderR = await WidgetUtil.getImageWH(
          image: Image.memory(Base64Decoder().convert(slideImageBase64)));
      slideSize = silderR.size;
      enable = true;
      setState(() {});
    }, (error) {
      print(error);
    });
  }

  //校验验证码
  void checkCaptcha(sliderXMoved, captchaToken, {BuildContext myContext}) {
    setState(() {
      sliderMoveFinish = true;
    });
    //滑动结束,改变滑块的图标及颜色
//    updateSliderColorIcon();

    //pointJson参数需要aes加密

//    MediaQueryData mediaQuery = MediaQuery.of(myContext);
    /*
    * ScreenUtil().setHeight(20)*/

    print('sliderXMoved= $sliderXMoved');
    print('_baseImageKeyWidth ${_baseImageKey.currentContext.size.width}');
    print('_baseImageKeyWidthRatio= ${_baseImageKey.currentContext.size.width / baseSize.width}');
    //由于不同屏幕分辨率或者屏幕设置放大后拖动从最右侧拖动到同一位置的偏移量是不同的(屏幕),根据底图在屏幕上的实际宽度和从接口获取的底图的宽度(baseSize.width)的百分比来计算接口偏移量参数

    var pointMap = {"x": sliderXMoved / (_baseImageKey.currentContext.size.width / baseSize.width), "y": 5};//x:拖动的偏移量  y:偏移量误差范围 
    var pointStr = json.encode(pointMap);
    var cryptedStr = pointStr;

    /// secretKey 不为空,进行as加密
    if (!ObjectUtils.isEmpty(secretKey)) {
      // var aesEncrypter = AesCrypt(secretKey, 'ecb', 'pkcs7');
      cryptedStr = EncryptUtil.aesEncode(key: secretKey, content: pointStr);
      var dcrypt = EncryptUtil.aesDecode(key: secretKey, content: cryptedStr);
      // Map _map = json.decode(dcrypt);
    }

    // print("dcrypt ---- ${_map}");
    DioManager.getInstance().post(Constant.baseUrl + '/captcha/check', {
      "pointJson": cryptedStr,
      "captchaType": "blockPuzzle",
      "token": captchaToken
    }, (res) {
      if (res['repCode'] != '0000' || res['repData'] == null) {
        checkFail();
        return;
      }

      Map<String, dynamic> repData = res['repData'];
      if (repData["result"] != null && repData["result"] == true) {
        // 如果不加密 将 token 和 坐标序列化 通过 --- 链接成字符串

        var captchaVerification = '$captchaToken---$pointStr';
        if (!ObjectUtils.isEmpty(secretKey)) {
          // 如果加密 将 token 和 坐标序列化通过 --- 链接成字符串进行加密 加密秘钥为 _clickWordCaptchaModel.secretKey
          captchaVerification = EncryptUtil.aesEncode(
              key: secretKey, content: captchaVerification);
        }

        checkSuccess(captchaVerification);
      } else {
        checkFail();
      }
    }, (error) {
      loadCaptcha();
      print(error);
    });
  }

  
  void initState() {
    super.initState();
    initAnimation();
    loadCaptcha();
  }

  
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  // 初始化动画
  void initAnimation() {
    controller =
        AnimationController(duration: Duration(milliseconds: 500), vsync: this);

    offsetAnimation = Tween<double>(begin: 0.5, end: 0)
        .animate(CurvedAnimation(parent: controller, curve: Curves.ease))
          ..addListener(() {
            this.setState(() {});
          });
  }

  // 反向执行动画
  _reverseAnimation() async {
    await controller.reverse();
  }

  // 正向执行动画
  _forwardAnimation() async {
    await controller.forward();
  }

  
  void didUpdateWidget(CaptchaPage oldWidget) {
    // TODO: implement didUpdateWidget
    super.didUpdateWidget(oldWidget);
  }

  
  Widget build(BuildContext context) {
    dialogWidth = 0.9 * MediaQuery.of(context).size.width;
   _ratio = MediaQuery.of(context).devicePixelRatio;
    return Scaffold(
      backgroundColor: Colors.transparent,
      body: MediaQuery(
        data: MediaQueryData(devicePixelRatio: _ratio),
        child: Center(
          child: UnconstrainedBox(
            child: Container(
              width: dialogWidth,
              color: Colors.white,
              child: Stack(
                children: <Widget>[
                  Column(
                    mainAxisAlignment: MainAxisAlignment.start,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: <Widget>[
                      //顶部,提示+关闭
                      Container(
                        height: 50,
                        padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
                        decoration: BoxDecoration(
                          border: Border(
                              bottom:
                              BorderSide(width: 1, color: Color(0xffe5e5e5))),
                        ),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: <Widget>[
                            Expanded(
                                child: Text(
                                  S.current.qingwanchenganquanyanzheng,
                                  maxLines: 1,
                                  overflow: TextOverflow.ellipsis,
                                  style: TextStyle(fontSize: 18),
                                  textScaleFactor: 1.0,
                                )),
                            IconButton(
                                padding: EdgeInsets.all(3),
                                icon: Icon(Icons.refresh),
                                iconSize: 30,
                                color: Colors.black54,
                                onPressed: () {
                                  //刷新
                                  loadCaptcha();
                                }),
                            IconButton(
                                padding: EdgeInsets.all(3),
                                icon: Icon(Icons.highlight_off),
                                iconSize: 30,
                                color: Colors.black54,
                                onPressed: () {
                                  //退出
                                  Navigator.pop(context);
                                }),
                          ],
                        ),
                      ),

                      //显示验证码
                      Container(
                        margin: EdgeInsets.all(10),
                        child: Stack(
                          children: <Widget>[
                            //底图 310*155
                            baseImageBase64.length > 0
                                ? Image.memory(
                            Base64Decoder().convert(baseImageBase64),
                          fit: BoxFit.fitWidth,
                          key: _baseImageKey,
                          height: 310.w,
                          gaplessPlayback: true,)
                                : Container(
                              width: dialogWidth - 20,
                              height: 310.w,
                              alignment: Alignment.center,
                              child: CircularProgressIndicator(),
                            ),

                            //滑块图
                            slideImageBase64.length > 0
                                ? Container(
                              margin: EdgeInsets.fromLTRB(sliderXMoved, 0, 0, 0),
                              child: Image.memory(
                                Base64Decoder().convert(slideImageBase64),
                               height: 310.w,
                                fit: BoxFit.fitHeight,
                                gaplessPlayback: true,
                              ),
                            )
                                : Container(),

                            Positioned(
                                bottom: 0,
                                left: -10,
                                right: -10,
                                child: Offstage(
                                  offstage: !_showTimeLine,
                                  child: FractionalTranslation(
                                    translation: Offset(0, offsetAnimation.value),
                                    child: Container(
                                      margin: EdgeInsets.only(left: 10, right: 10),
                                      padding: EdgeInsets.only(left: 10),
                                      height: 40,
                                      color: _checkSuccess
                                          ? Color(0x7F66BB6A)
                                          : Color.fromRGBO(200, 100, 100, 0.4),
                                      alignment: Alignment.centerLeft,
                                      child: Text(
                                        _checkSuccess
                                            ? "${(_checkMilliseconds / (60.0 * 12)).toStringAsFixed(2)}${S.current.yanzhengchenggong}"
                                            : S.current.yanzhengshibai,
                                        style: TextStyle(color: Colors.white),
                                      ),
                                    ),
                                  ),
                                )),
                            Positioned(
                                bottom: -20,
                                left: 0,
                                right: 0,
                                child: Offstage(
                                  offstage: !_showTimeLine,
                                  child: Container(
                                    margin: EdgeInsets.only(left: 10, right: 10),
                                    height: 20,
                                    color: Colors.white,
                                  ),
                                ))
                          ],
                        ),
                      ),

                      //底部,滑动区域
                      baseSize.width > 0
                          ? Container(
                          margin: EdgeInsets.all(10),
                          height: slideSize.width * (dialogWidth - 20) / baseSize.width,
                          width: baseSize.width * 2.w,
                          child: Stack(
                            alignment: AlignmentDirectional.centerStart,
                            children: <Widget>[
                              Container(
                                height: slideSize.width * (dialogWidth - 20) / baseSize.width,
                                decoration: BoxDecoration(
                                  border: Border.all(
                                    width: 1,
                                    color: Color(0xffe5e5e5),
                                  ),
                                  color: Color(0xffe1e1e1),
                                ),
                              ),
                              Container(
                                alignment: Alignment.center,
                                child: Text(
                                  S.current.xiangyouhuadong,
                                  style: TextStyle(fontSize: 16),
                                  textScaleFactor: 1.0,
                                ),
                              ),
                              Container(
                                width: sliderXMoved,
                                height: 58,
                                decoration: BoxDecoration(
                                  border: Border.all(
                                    width: sliderXMoved > 0 ? 1 : 0,
                                    color: movedXBorderColor,
                                  ),
                                  color: Color(0xff89F2D0),
                                ),
                              ),
                              GestureDetector(
                                onPanStart: (startDetails) {
                                  if (!enable) return;

                                  _checkMilliseconds =
                                      new DateTime.now().millisecondsSinceEpoch;
                                  print("startDetails");
                                  print(startDetails.globalPosition);

                                  sliderStartX = startDetails.globalPosition.dx;
                                  print(
                                      "startDetails --- sliderStartX ${sliderStartX} ");
                                },
                                onPanUpdate: (updateDetails) {
                                  if (!enable) return;

                                  print("updateDetails");
                                  print(updateDetails.globalPosition);

                                  double offset =
                                      updateDetails.globalPosition.dx  - sliderStartX;
                                  double _w1 = baseSize.width * 2.w - 100.w;
                                  if (offset < 0) {
                                    offset = 0;
                                  } else if ((offset > _w1)) {
                                    offset = _w1;
                                  }
                                  setState(() {
                                    sliderXMoved = offset;
                                  });
                                  //滑动过程,改变滑块左边框颜色
                                  updateSliderColorIcon();
                                },
                                onPanEnd: (endDetails) {
                                  if (!enable) return;
                                  checkCaptcha(sliderXMoved, captchaToken);
                                  int _nowTime =
                                      new DateTime.now().millisecondsSinceEpoch;
                                  _checkMilliseconds =
                                      _nowTime - _checkMilliseconds;

                                  //滑动结束
                                },
                                child: Container(
                                  width: slideSize.width * (dialogWidth - 20) / baseSize.width,
                                  height: slideSize.width * (dialogWidth - 20) / baseSize.width,
                                  margin: EdgeInsets.fromLTRB(
                                      sliderXMoved > 0 ? sliderXMoved : 1,
                                      0,
                                      0,
                                      0),
                                  decoration: BoxDecoration(
                                    border: Border(
                                      top: BorderSide(
                                        width: 1,
                                        color: Color(0xffe5e5e5),
                                      ),
                                      right: BorderSide(
                                        width: 1,
                                        color: Color(0xffe5e5e5),
                                      ),
                                      bottom: BorderSide(
                                        width: 1,
                                        color: Color(0xffe5e5e5),
                                      ),
                                    ),
                                    color: sliderColor,
                                  ),
                                  child: IconButton(
                                    icon: Icon(sliderIcon),
                                    iconSize: 20,
                                    color: Colors.black54,
                                  ),
                                ),
                              )
                            ],
                          ))
                          : Container(),
                    ],
                  ),
                ],
              ),
            ),
          )

        ),
      )
    );
  }
}

3.使用:

_sendPhoneCode(setBottomSheetState) {
    showDialog<Null>(
      context: context,
      barrierDismissible: true,
      builder: (BuildContext context) {
        return CaptchaPage(
          onSuccess: (value) async {
            Response response = await _dio.post(LoginApi.SEND_MESSAGE_URL,
                data: {
                  'ic': '+$areaCode',
                  'phone': phoneController.text,
                  'captchaVerification': value
                });
            String dataStr = json.encode(response.data);
            Map<String, dynamic> dataMap = json.decode(dataStr);
            if (dataMap != null && dataMap['code'] == 200) {
              if (mounted) {
                setBottomSheetState(() {
                  isButtonEnable = false; //按钮状态标记
                });
              }

              timer = new Timer.periodic(Duration(seconds: 1), (Timer timer) {
                if (mounted) {
                  setBottomSheetState(() {
                    count--;
                    if (count == 0) {
                      timer.cancel(); //倒计时结束取消定时器
                      isButtonEnable = true; //按钮可点击
                      count = 60; //重置时间
                      buttonText = S.current.fasongyanzhengma; //重置按钮文本
                    } else {
                      buttonText = '${count}S'; //更新文本内容
                    }
                  });
                }
              });
              esLoadingToast(S.current.fasongchenggong);
            } else {
              esLoadingError(S.current.fasongshibai);
            }
          },
          onFail: () {
            // esLoadingError('人机校验失败');
          },
        );
      },
    );
  }

滑块拼图验证码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/718947.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux指令与权限

本期我们来学习Linux的权限内容 目录 Linux权限 1.认识Linux下用户的分类 2.什么是权限 3.没有权限是什么现象 4.权限的修改问题 chmod chown chgrp umask 粘滞位 file指令 我们在使用xshell登录后&#xff0c;会有下面的东西 我们来介绍一下&#xff0c;以我的登录…

19. WebGPU —计算着色器(compute shader)

WebGPU 是即将推出的 Web API&#xff0c;可提供对GPU的底层控制并用于通用目的计算任务 。 我对计算机图形不是很有经验。我通过阅读有关如何使用 OpenGL 构建游戏引擎的教程了解了 WebGL 的点点滴滴&#xff0c;并通过观看 Inigo Quilez 在 ShaderToy 上仅使用着色器而不使用…

最长连续序列

题目链接 最长连续序列 题目描述 注意点 0 < nums.length < 100000不要求序列元素在原数组中连续 解答思路 要想实现时间复杂度为 O(n) 的算法解决此问题&#xff0c;关键是数字不能多次遍历&#xff0c;所以首先要对数组进行去重&#xff1b;然后为什么防止某个元素…

NSS [HNCTF 2022 WEEK2]ez_ssrf

NSS [HNCTF 2022 WEEK2]ez_ssrf 先拿dirsearch扫一下。 访问/flag.php 访问/index.php 应该是从index.php传参,ssrf然后访问到flag.php。 因此构造poc.php: <?php $out "GET /flag.php HTTP/1.1\r\n"; $out . "Host: 127.0.0.1\r\n"; $out . "…

网络关键设备和网络安全专用产品目录-2023年7月

2023年7月3日&#xff0c;网络关键设备和网络安全专用产品目录&#xff08;一级&#xff09;终于更新了&#xff0c;增加到38类&#xff0c;大家想了解每类产品对应哪家检测机构、以及涉及相关的标准、分解的二级产品目录&#xff0c;可以联系龙域认证客服。 一、网络关键设备…

01_面向对象的设计原则

面向对象的设计原则 参考资料&#xff1a; 视频书籍 《设计模式&#xff1a;可复用面向对象软件的基础》 简介 面对复杂问题如何解决&#xff1f; 分解&#xff1a;分而治之&#xff0c;大问题分解成小问题。抽象&#xff1a;忽视非本质的细节&#xff0c;处理泛化和理想化…

交换排序--冒泡排序和快速排序

交换&#xff0c;是指根据序列中两个元素关键字的比较结果来对换这两个记录在序列中的位置 一&#xff0c;冒泡排序 1.基本思想&#xff1a;从后往前&#xff08;或从前往后&#xff09;两两比较相邻元素的值&#xff0c;若为逆序&#xff08;A[i-1] > A[i]&#xff09;&a…

Vivado创建IP核步骤

0、创建一个LED IP核 1、打开Vivado&#xff0c;点击Tasks栏的Manage IP&#xff0c;在弹出的选项框中选中New IP Location... 2、在弹出的界面选择Next 3、在弹出的界面中进行IP核的属性配置&#xff0c;修改系所属的器件和保存路径&#xff0c;其他的保持默认就行&#xff0…

Java(117):读取properties配置文件中文乱码问题解决

1、Edit展示properties后缀文件时乱码 2、读取properties配置文件中文乱码问题解决 2.1、文件存储为UTF-8格式 2.2、读取时设置为UTF-8格式 String enconding "UTF-8"; BufferedReader br new BufferedReader(new InputStreamReader(new FileInputStream(fileP…

VMware安装win10系统(超详细)

目录 一、创建虚拟机 二、选择自定义安装 三、根据自己的主机选择虚拟机的配置 四、光盘映像可以选择稍后安装​编辑 五、 根据自己的光盘映像选择操作系统和版本&#xff0c;因为我的装的是win10 x64&#xff0c;所以安装如下图所示 六、选择存放路径 七、 选择BIOS&#x…

java阻塞队列/kafka/spring整合kafka

queue增加删除元素 增加元素 add方法在添加元素的时候&#xff0c;若超出了度列的长度会直接抛出异常&#xff1a;put方法&#xff0c;若向队尾添加元素的时候发现队列已经满了会发生阻塞一直等待空间&#xff0c;以加入元素offer方法在添加元素时&#xff0c;如果发现队列已满…

FPGA实验三:状态机的设计

目录 一、实验目的 二、实验要求 三、实验代码 1.design source文件部分代码 2.测试文件代码 四、实验结果及分析 1、引脚锁定 2、仿真波形及分析 &#xff08;1&#xff09;设计好序列检测器 &#xff08;2&#xff09;仿真波形&#xff08;检测11010&#xff09; 3…

Linux-vim与gdb与make/makefile

三个模式&#xff1a;命令模式 文本模式 底行模式 yum :instell 安装 remove 卸载 gcc -o执行后生成文件命名 gcc 1.c -o fst.out -E预编译 -S汇编 -c生成机器码 Linux 中 静态库&#xff1a;.a&#xff1b;动态库&#xff1a;.so Linux默认动态库&#xff0c;…

Redis的安装,启动,关闭

一&#xff0c;redis安装linux 1&#xff0c;安装gcc环境 yum -y install gcc-c2,上传压缩包到/usr/soft目录&#xff0c;并解压 cd /soft tar -xvf redis-3.2.11.tar.gz3&#xff0c;进入redis-5.0.7目录&#xff0c;使用make命令编译redis [rootlocalhost soft]# cd re…

【DBA课程-笔记】第1章:MongoDB数据库入门

一、MongoDB 概览及新特性 1. MongoDB 简介 目前最流行的NoSQL数据库&#xff08;NO.1&#xff09;MongoDB是一个基于分布式文件存储的数据库&#xff0c;由C语言编写&#xff0c;特点是高性能、易部署、易使用、存储数据非常方便&#xff0c;旨在为Web应用提供可扩展的高性能…

企业该如何防止数据泄漏问题

关键词&#xff1a;企业网盘、知识文档管理系统、群晖NAS、数据安全 根据Verizon《2022 数据泄露调查报告》显示&#xff0c;2022年数据泄露事件中82%的违规行为涉及人为因素&#xff0c;勒索软件泄露事件增加了13%&#xff0c;超过过去五年的总和&#xff0c;数据安全已变成关…

【JUC-7】ReentrantLock (可重入锁)基础

ReentrantLock (可重入锁) ReentrantLock实现了Lock接口, 内部通过继承AQS, 实现了一个同步器. 可以通过同步器来创建Condition条件变量, 可以用作容器, 存放不同条件的等待线程. 说明ReentrantLock与AQS的关系 类图: 相对于synchronized, 都支持可重入. 它还具备如下特点: …

【算法练习】24:凯撒密码

一、凯撒密码介绍&#xff1a; 采用替换的方式对英文字母进行处理&#xff0c;将每一个英文字符循环替换为字母表序列中该字符的后面的第三个字符&#xff0c;即循环右移3位。 明文字母表&#xff1a;ABCDEFGHIJKLMNOPQRSTUVWXYZ 密文字母表&#xff1a;DEFGHIJKLMNOPQRSTUV…

微信小程序如何读取本地云存储txt数据,避免乱码

第一步 找到你的txt文件&#xff0c;重命名为json文件 第二步 上传到云存储中&#xff0c;获取File ID 第三步 编写js代码 相关技术文档&#xff1a; https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.readFile.html onShow(){wx.cloud.d…

《Redis 核心技术与实战》课程学习笔记(三)

高性能 IO 模型&#xff1a;为什么单线程 Redis 能那么快&#xff1f; Redis 是单线程&#xff0c;主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的&#xff0c;这也是 Redis 对外提供键值存储服务的主要流程。但 Redis 的其他功能&#xff0c;比如持久化、异步删…