react结合vant的Dialog实现签到弹框操作

news2024/9/25 21:28:23

1.需求

有时候在开发的时候,需要实现一个签到获取积分的功能,使用react怎么实现呢?

需求如下:

        1.当点击“签到”按钮时,弹出签到框

        2.展示签到信息:

                签到天数,

                对应天数签到能够获取的积分,

                对应天数是否签到效果展示,

                签到按钮,

                签到说明,

        3.点击“签到”按钮,实现签到功能

                (1).如果当天没有签到,按钮文案“请签到”,点击后,执行签到功能,签到成功后,提示签到获取,然后提示慢慢隐藏

                (2).如果已经签到,按钮文案“已签到”,不能触发点击事件

 图示如下:

2.实现 

要实现上面的功能,需要使用vant的Dialog弹出框组件,代码如下:

主页代码:

        点击“签到按钮”,设置Dialog签到弹出框visible属性:显示弹出框


import GetSign from "../components/GetSign";

const Index = () => {
  //签到弹出框显示设置
  const [signDiaLogVisible, setSignDiaLogVisible] = useState(false);

  return (
        //点击签到按钮,设置弹出框显示
        <div onClick={() => setVisible(true)}>签到按钮</div>
        //签到弹框组件引用
        <GetSign signDiaLogVisible={signDiaLogVisible} setSignDiaLogVisible={setSignDiaLogVisible} />
    );
}

export default Index;

 签到弹框组件js:

import React, { useEffect } from "react";
import { useHistory } from "react-router-dom";
import { Dialog } from "react-vant";
import { PersistContext } from "../../data/PersistProvider";
import { RootStateContext } from "../../data/RootStateProvider";
import { SendCMD } from "../../utils/HTTPRequest";
import style from "./style.less";

//todo: 签到数据(可根据自己项目需求自行构建结构):
//checked:是否已经签到,award:积分, 
//数组的key就是天数,key从0开始,所以需要加1才是真正的天数

const data = {  //前端构建的签到结构,这里一般是从服务端获取数据,具体字段名称根据需求情况自行确定
  check_days: [ //天数
    { // key: 0, 第一天
      checked: true, //是否已经签到
      award: 2000, //积分数
    },
    {  // key: 1, 第二天,下面依次类推
      checked: true,
      award: 3000,
    },
    {
      checked: true,
      award: 3000,
    },
    {
      checked: false,
      award: 4000,
    },
    {
      checked: false,
      award: 5000,
    },
    {
      checked: false,
      award: 5000,
    },
    {
      checked: false,
      award: 8000,
    },
  ],
  today_checkable: true,  //今天是否可以签到
};


const GetSign = (props) => {
  let history = useHistory();
  //persist:保存到浏览器的临时缓存数据
  const { getPersist} = React.useContext(PersistContext);
   //用户信息
  const userInfo = getPersist("user_info");

  //是否展示签到所得
  const [visible, setVisible] = React.useState(-1);

  //弹框数据初始化
  const [signData, setSignData] = React.useState(data);

  //获取签到数据
  const sign_data = () => {  //从服务端api获取签到数据
    SendCMD("getCheckIn", { token: userInfo.token }).then((res) => {
      if (res.check_in_data) {
        setSignData(res.check_in_data);  //数据应该和上面data中的保持一致
      }
    });
  };

  //签到请求
  const sign_cmd = () => {
    SendCMD("doCheckIn", { token: userInfo.token }).then((res) => {
      if (res[0].check_success && res[0].check_in_data) { //判断是否签到成功,并更新签到信息
        setSignData(res[0].check_in_data);        
      }

      //判断最后一次签到的index
      let activeIndex = -1;
      res[0].check_in_data.check_days.map((item, index) => { //循环,设置最后一次签到的index
        if (item.checked) {
          activeIndex = index;
        }
      });
      //设置显示的log index
      setVisible(activeIndex);
    });
  };

  //初始化签到数据
  useEffect(() => {
    if (userInfo) {
      sign_data();
    }
  }, [userInfo]);

  //点击签到按钮事件
  const onSignClick = () => {
    //判断是否登录
    if (!userInfo) {
      //跳转到注册页面
      history.push("./login");
      return;
    }

    //todo 判断是否可以点击
    if (!signData["today_checkable"]) {
        
      //已经签到,弹框提示
      alert("今天已签到");
      return;
    }
    //请求接口签到,并根据返回结果响应改变展示效果
    sign_cmd();
  };

  return (
    <Dialog
      closeable={true}
      closeOnClickOverlay={true}
      width={"85%"}
      // closeIcon={<Close />}
      visible={props.visible != 0}
      showCancelButton={false}
      className={style.signDialog}
      showConfirmButton={false}
      onClose={() => {
        props.setVisible(false);
      }}
    >
      <div class={style.firstSignDialog}>
        <div style={{ width: "100%", height: "4rem" }}>
          <img style={{ width: "43%", marginTop: "1.2rem" }} src="../images/sign_title.png" />
        </div>
        <div class={style.firstSignSteps}>
          <div class={style.btntop}>
            {signData.check_days
              ? signData.check_days.map((item, index) => {
                  return (
                    <div key={index} class={style.checkactivebox} id={"index" + index}>
                      <div class={style.c_l}>
                        <div class={style.c_l_text}>
                          <span>+{item.award / 100}</span>
                        </div>
                        <div class={item.checked ? style.yuanbox_yes : style.yuanbox}>
                          {item.checked ? (
                            <sapn className={style.yuanbox_num}>
                              <img src={"../images/sign_yes.png"} style={{ width: "1rem" }} />
                            </sapn>
                          ) : (
                            <sapn className={style.yuanbox_num}>{index + 1}</sapn>
                          )}
                        </div>
                        <div class={style.tian}>Day {index + 1}</div>
                      </div>
                      <div>
                        {index === 3 || index === 6 ? (
                          <img alt="" />
                        ) : signData.check_days[index + 1].checked ? (
                          <img className={style.img_2} src="../images/sign_line2.png" alt="" />
                        ) : (
                          <img className={style.img_2} src="../images/sign_line.png" alt="" />
                        )}
                      </div>
                      <div class={`${style.p_log} ${pLogVisible == index ? style.p_log_active : ""}`}>
                        {" "}
                        Coins +{item.award / 100}
                      </div>
                    </div>
                  );
                })
              : null}
          </div>
        </div>
        <div class={style.firstSignBtn} onClick={onSignClick}>
          <div class={style.firstSignDialogBtnText}>
            {!userInfo ? (
              <span>注册</span>
            ) : !signData.today_checkable ? (
              <span>已签到</span>
            ) : (
              <span>签到</span>
            )}
          </div>
        </div>
        <div class={style.signBottom}>
          <div class={style.signBottom_title}>签到说明</div>
          <div class={style.signBottom_content}>
            <span>.说明1</span>
            <br />
            <span>.说明2</span>
            <br />
            <span>.说明3</span>
          </div>
        </div>
      </div>
    </Dialog>
  );
};

export default GetSign;

签到弹出框css:


.signDialog {
  --rv-dialog-background-color: rgba(0, 0, 0, 0);
}

.firstSignDialog {
  text-align: center;
  background-size: 100% 100%;
  background-repeat: no-repeat;
  background-image: url("../assets/images/sign.png");
}

.btntop {
  margin: 0 0.53333rem;
  display: flex;
  flex-wrap: wrap;
  font-weight: 700;
}

.c_l_text {
  color: #CAE7DC;
  font-weight: bold;
}

.img_1 {
  width: 0.66667rem;
}

.tian {
  color: #CAE7DC;
  text-align: center;
  font-weight: bold;
}

.checkactivebox {
  width: 25%;
  position: relative;
}

 .img_1 {
  width: 0.66667rem;
}

.yuanbox_yes {
  width: 2rem;
  height: 2rem;
  z-index: 2;
  border-radius: 99rem;
  background-color:  #00FFCC;
}

 .yuanbox {
  width: 2rem;
  height: 2rem;
   z-index: 2;
  border-radius: 99rem;
  background-color:#CAE7DC;
}

.yuanbox_num {
  color: #035d3f;
  font-weight: 700;
  font-size: 1.2rem;
  line-height: 2rem;
  text-align: center;
}

.sign_toast {
  margin-top: -7rem;
  background-color: #f00 !important;
  color: #006a2d !important;
}

.c_l {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  font-size: 1rem;
}

.img_2 {
  position: absolute;
  top: 1.9rem;
  z-index: 1;
  width: 100%;
}

.p_log {
  position: absolute;
  top: -0.53333rem;
  left: 0.47333rem;
  line-height: 1.43333rem;
  text-align: center;
  background-color: #035d3f;
  font-size: 0.5rem;
  color: #ffffff;
  border-radius: 0.5rem;
  padding-left: 0.15rem;
  padding-right: 0.15rem;
  z-index: 100;
  opacity: 0;
}

@keyframes fadenum{
  0%{opacity: 1;}
  100%{opacity: 0;}
}

.p_log_active {
  animation:fadenum 5s 1;
}

.firstCoinsSteps {
  margin-top: 3%;
}


.firstSignBtn {
  display: inline-block;
  margin-bottom: 1.5rem;
  background: url("../assets/home/sign_btn.png") no-repeat 100%;
  background-size: 100%;
  width: 80%;
  border-radius: 2rem;
  height: 3rem;
}

.firstSignDialogBtnText {
  margin-top: 1rem;
  transform: scale(1, 1.5);
}

.firstSignDialogBtnText span {
  text-align: center;
  font-weight: 700;
  font-size: 1rem;
  background-color: #000000;
  color: #035d3f;
  text-shadow: 0px 0px 1px rgba(255, 255, 255, 0.925);
  -webkit-background-clip: text;
  -moz-background-clip: text;
  background-clip: text;
}

.signBottom {
  margin-top: 2%;
  color: #969696;
  font-weight: 700;
}

.signBottom_title {
  font-size: 1.2rem;
}

.signBottom_content {
  margin-top: 0.3rem;
}

.signBottom_content span {
  text-align: left;
  margin-left: 0.5rem;
  width: 96%;
  font-size: 0.8rem;
  font-weight: 700;
  display: inline-block;
}

上面要引入的setPersist组件:       

import { createContext, useState, useEffect } from "react";
import { SendCMD } from "../utils/HTTPRequest";
import { DeepEqual, GetOS, GetBrowser } from "../utils/JSTool";

// 创建一个 Context
export const PersistContext = createContext(defaultPersist);

let untrackedData = defaultPersist;

// 创建一个 DataProvider 组件
export const PersistProvider = ({ children }) => {
  const [data, setData] = useState(untrackedData);
  const [expires, setExpires] = useState({});

  useEffect(() => {
    loadPersistDataOnce();
  }, []);

  const loadPersistDataOnce = () => {
    if (untrackedData.dataLoaded) {
      return;
    }
    let persistStr = localStorage.getItem("persist");
    if (persistStr) {
      try {
        let persistData = JSON.parse(persistStr);
        for (let key in persistData) {
          if (typeof persistData[key] == "undefined" || persistData[key] == null) {
            delete persistData[key];
          }
        }
        untrackedData = Object.assign({}, untrackedData, persistData);
      } catch (e) {
        console.error(e);
      }
    }
    let expiresStr = localStorage.getItem("expires");
    if (expiresStr) {
      try {
        let expires = JSON.parse(expiresStr);
        setExpires(expires);
      } catch (e) {
        console.error(e);
      }
    }
    untrackedData.dataLoaded = true;
    setData(Object.assign({}, untrackedData));
  };

  const setPersist = (key, value, expireSeconds) => {
    loadPersistDataOnce();
    if (DeepEqual(untrackedData[key], value)) return;
    untrackedData[key] = value;
    if (expireSeconds && expireSeconds > 0) {
      let expireData = { ...expires };
      expireData[key] = Date.now() + expireSeconds * 1000;
      setExpires(expireData);
      localStorage.setItem("expires", JSON.stringify(expireData));
    }
    setData(() => Object.assign({}, untrackedData));
    localStorage.setItem("persist", JSON.stringify(untrackedData));
  };

  const getPersist = (key) => {
    loadPersistDataOnce();
    let value = data[key];
    if (typeof value == "undefined") return null;
    let keyExpire = expires[key];
    if (keyExpire && keyExpire < Date.now()) {
      let dataTmp = { ...data };
      delete dataTmp[key];
      delete untrackedData[key];
      let expiresTmp = { ...expires };
      delete expiresTmp[key];
      localStorage.setItem("persist", JSON.stringify(dataTmp));
      localStorage.setItem("expires", JSON.stringify(expiresTmp));
      return null;
    }
    return value;
  };

  return (
    <PersistContext.Provider value={{ getPersist, setPersist, reloadUserInfo }}>{children}</PersistContext.Provider>
  );
};

好了,签到获取积分操作ok

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

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

相关文章

封装时间轴组件 timeline

要求时间轴的点展示进度百分比&#xff0c;线也根据进度不同展示不同长度的颜色 实现效果&#xff1a; 使用的组件库是vant的circle 子组件&#xff1a; <template><div class"m-timeline-area" :style"width: ${width}px"><div class&qu…

XXL-Job详解(五):动态添加、启动任务

目录 前言XXL-Job API接口添加任务API动态添加任务动态启动任务 前言 看该文章之前&#xff0c;最好看一下之前的文章&#xff0c;比较方便我们理解 XXL-Job详解&#xff08;一&#xff09;&#xff1a;组件架构 XXL-Job详解&#xff08;二&#xff09;&#xff1a;安装部署 X…

SQLserver通过字符串中间截取然后分组

当我们存的数据是json的时候可以全部取出在模糊查询但是有多个重复数据的时候就没办法准确的模糊出来这个时候我们就需要用的字符串截取 --创建函数create FUNCTION [dbo].[Fmax] (str varchar(50),start VARCHAR(50),length VARCHAR(50)) RETURNS varchar(max) AS BEGINDEC…

如何使用Cloudreve搭建本地云盘系统并实现随时远程访问

文章目录 1、前言2、本地网站搭建2.1 环境使用2.2 支持组件选择2.3 网页安装2.4 测试和使用2.5 问题解决 3、本地网页发布3.1 cpolar云端设置3.2 cpolar本地设置 4、公网访问测试5、结语 1、前言 自云存储概念兴起已经有段时间了&#xff0c;各互联网大厂也纷纷加入战局&#…

nginx部署和安装-后端程序多端口访问-后端代理设置

部分补充 查看nginx是否安装http_ssl_module模块 ./nginx -V 看到有 configure arguments: --with-http_ssl_module, 则已安装。 如果没有安装&#xff1a;参考文档 nginx官网地址&#xff1a;nginx: download 这里下载nginx-1.18.0稳定版tar.gz 下载后&#xff0c;利用…

失落的艺术:无着色器3D渲染

假设你想创建一个甜蜜的弹跳立方体&#xff0c;如下所示&#xff1a; 一个弹跳的立方体 你可以使用 3D 框架&#xff0c;例如 OpenGL 或 Metal。 这涉及编写一个或多个顶点着色器来变换 3D 对象&#xff0c;以及编写一个或多个片段着色器来在屏幕上绘制这些变换后的对象。 然…

Docker Image(镜像)——5

目录&#xff1a; Docker 镜像是什么镜像生活案例镜像分层生活案例为什么需要镜像镜像命令详解 镜像命令清单docker imagesdocker tagdocker pulldocker pushdocker rmidocker savedocker loaddocker historydocker importdocker image prunedocker build镜像操作案例 查找镜像…

为什么Nginx被称为反向代理

下图显示了 &#x1d41f;&#x1d428;&#x1d42b;&#x1d430;&#x1d41a;&#x1d42b;&#x1d41d; &#x1d429;&#x1d42b;&#x1d428;&#x1d431;&#x1d432; 和 &#x1d42b;&#x1d41e;&#x1d42f;&#x1d41e;&#x1d42b;&#x1d42c;&#…

如何制定公司网络安全战略

网络安全可以保护公司的重要信息免受恶意软件和数据泄露等威胁。网络安全策略列出了您公司的 IT 系统当前面临的风险、您计划如何预防这些风险&#xff0c;以及如果发生这些风险该怎么办。 让本文成为您制定有效网络安全策略的一站式指南。我们将讨论网络安全风险评估以及策略…

LeetCode 1212 查询球队积分(PostgreSQL)

数据准备 Create table If Not Exists Teams (team_id int, team_name varchar(30)) Create table If Not Exists Matches (match_id int, host_team int, guest_team int, host_goals int, guest_goals int) Truncate table Teams insert into Teams (team_id, team_name) va…

创新领航 | 竹云参编《基层智治系统安全接入规范》团体标准正式发布!

近日&#xff0c;由杭州市委办公厅&#xff08;市密码管理局&#xff09;、杭州市基层治理综合指挥保障中心、杭州市拱墅区社会治理中心、杭州市拱墅区数据资源管理局、杭州竹云数字智能科技有限公司、杭州智诚质量标准技术评定中心共同参与编写的《基层智治系统安全接入规范》…

01、pytest:帮助你编写更好的程序

简介 ​pytest框架可以很容易地编写小型、可读的测试&#xff0c;并且可以扩展以支持应用程序和库的复杂功能测试。使用pytest至少需要安装Python3.7或PyPy3。PyPI包名称为pytest 一个快速的例子 content of test_sample.py def inc(x):return x1def test_ansewer():assert i…

nodejs+vue+微信小程序+python+PHP就业求职招聘信息平台的设计与实现-计算机毕业设计推荐

主要有前端和后端&#xff0c;前端显示整个网站的信息&#xff0c;后端主要对前端和网站的基本信息进行管理。用户端模块主要是系统中普通用户在注册、登录系统可以看到自己的基本信息&#xff0c;维护自己的信息&#xff1b;管理员端模块主要是管理员登录后对整个系统相关操作…

05、pytest断言确定的异常

官方用例 # content of test_sysexit.py import pytestdef f():raise SystemExit(1)def test_mytest():with pytest.raises(SystemExit):f()解读与实操 ​ 标准python raise函数可产生异常。pytest.raises可以断言某个异常会发现。异常发生了&#xff0c;用例执行成功&#x…

AF自动登录应用--实现无源码系统单点登录

在企业信息化的进程中&#xff0c;许多组织拥有一系列的老应用系统&#xff0c;这些系统在多年的运行中积累了大量的业务数据和流程。然而&#xff0c;这些老应用系统往往没有设计或实现单点登录&#xff08;SSO&#xff09;功能&#xff0c;用户需要在不同系统之间频繁输入账号…

【算法】算法题-20231205

这里写目录标题 一、LCS 01. 下载插件二、已知一个由数字组成的列表&#xff0c;请将列表中的所有0移到右侧三、实现一个trim()函数&#xff0c;去除字符串首尾的空格&#xff08;不能使用strip()方法&#xff09; 一、LCS 01. 下载插件 简单 小扣打算给自己的 VS code 安装使…

pycharm打断点调试

在PyCharm中使用断点调试可以帮助逐行执行代码并查看变量的值&#xff0c;以便更好地理解程序的执行过程。以下是在PyCharm中设置断点和使用调试功能的步骤和注意事项&#xff1a; 步骤&#xff1a; 打开PyCharm并打开要调试的项目。找到要设置断点的代码行。您可以在行号区…

如何使用Node.js快速创建本地HTTP服务器并实现异地远程访问

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation&#xff0…

在用户不安装 ImageMagick 的情况下使用

需要以编程的方式配置环境变量&#xff08;手工配置也是一样的效果&#xff09; 1、首先要配置 path 将 {ImageMagick目录} 配置到path中 2、配置 MAGICK_HOME 将 {ImageMagick目录} 配置到MAGICK_HOME中 3、配置MAGICK_CODER_MODULE_PATH 将 {ImageMagick目录}\modules\…

【动态规划】LeetCode-198/LCR089.打家劫舍

&#x1f388;算法那些事专栏说明&#xff1a;这是一个记录刷题日常的专栏&#xff0c;每个文章标题前都会写明这道题使用的算法。专栏每日计划至少更新1道题目&#xff0c;在这立下Flag&#x1f6a9; &#x1f3e0;个人主页&#xff1a;Jammingpro &#x1f4d5;专栏链接&…