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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JIRA 重建索引

JIRA为了增快搜索速度&#xff0c;为所有的问题的字段生成一个索引文件。这个索引文件存在磁盘的一个文件里面&#xff0c; 并且会实时更新。但是有时候某些操作后&#xff08;例如增加自定义字段&#xff09;&#xff0c;需要重新建索引。 详情请见 Re-indexing after major c…

UDS 诊断报文格式

文章目录 网络层目的N_PDU 格式诊断报文的分类&#xff1a;单帧、多帧 网络层目的 N_PDU(network protocol data unit)&#xff0c;即网络层协议数据单元 网络层最重要的目的就是把数据转换成符合标准的单一数据帧&#xff08;符合can总线规范的&#xff09;&#xff0c;从而…

Spring Initial 脚手架国内镜像地址

官方的脚手架下载太慢了&#xff0c;并且现在没有了Java8的选项&#xff0c;所以找到国内的脚手架镜像地址&#xff0c;推荐给大家。 首先说官方的脚手架 官方的脚手架地址为&#xff1a; https://start.spring.io/ 但是可以看到&#xff0c;并没有了Java8的选项。 所以推荐…

手把手带你离线部署Walrus,体验极简应用交付

Walrus 0.4 已于近日发布&#xff0c;新版本中采用的应用模型可以让运维团队仅需配置1次&#xff0c;即可在多模态的基础设施及环境中运行包括应用服务及周边依赖资源在内的全套应用系统。这极大减少了运维人员的工作量&#xff0c;同时为研发人员屏蔽了底层基础设施的复杂度. …

Web漏洞分析-SQL注入XXE注入(上)

随着互联网的不断普及和Web应用的广泛应用&#xff0c;网络安全问题愈发引起广泛关注。在网络安全领域中&#xff0c;SQL注入和XXE注入是两个备受关注的话题&#xff0c;也是导致许多安全漏洞的主要原因之一。本博客将深入研究这两种常见的Web漏洞&#xff0c;带您探寻背后的原…

十二月四日多继承

#include <iostream>using namespace std; //定义沙发类 class Sofa { private:string *sitting; public:Sofa(){}//无参构造函数Sofa(string sitting):sitting(new string (sitting))//有参构造函数{}~Sofa() //析构函数{delete sitting;}Sofa &op…

DeepStream--测试PCB-Defect-Detection

GitHub - clintonoduor/PCB-Defect-Detection-using-Deepstream: PCB defect detection using deepstream & YoloV5我参考了了这个代码&#xff0c;作者基于YoloV5&#xff0c;训练一个电路板检测的模型&#xff0c;训练数据集来自https://robotics.pkusz.edu.cn/resources…

4K-Resolution Photo Exposure Correction at 125 FPS with ~8K Parameters

MSLTNet开源 | 4K分辨率125FPS8K的参数量&#xff0c;怎养才可以拒绝这样的模型呢&#xff1f; 错误的曝光照片的校正已经被广泛使用深度卷积神经网络或Transformer进行广泛修正。尽管这些方法具有令人鼓舞的表现&#xff0c;但它们通常在高分辨率照片上具有大量的参数数量和沉…

GitHub项目推荐-Deoldify

有小伙伴推荐了一个老照片上色的GitHub项目&#xff0c;看了简介&#xff0c;还不错&#xff0c;推荐给大家。 项目地址 GitHub - SpenserCai/sd-webui-deoldify: DeOldify for Stable Diffusion WebUI&#xff1a;This is an extension for StableDiffusions AUTOMATIC1111 w…

Stm32 CubeIDE对RTC的日期、时间读写,后备存储的读写

Stm32 CubeIDE对RTC的日期、时间读写&#xff0c;后备存储的读写&#xff0c;一折腾又是好多的问题&#xff0c;现在梳理一下&#xff0c;后面的不要过多踩坑了。 用STM32CubeIDE生成代码 这里有时间和日期的设置&#xff0c;在代码中也会生成相应的代码&#xff0c;首次设置后…

密集书库是什么意思?图书馆密集书库的书可以借出吗

密集书库是一种用于存储大量书籍和资料的高密度储存设施。它通常包括一系列钢制书架和可移动的储存架&#xff0c;使得书籍可以被紧密地排列和存储&#xff0c;以最大程度地利用存储空间。同时&#xff0c;密集书库还有各种自动化系统&#xff0c;如自动化取书系统、气候控制系…

微信小程序引入node_modules依赖

微信小程序不支持直接读取node_modules 首先在目录文件夹下cmd输入npm init命令 D:\小程序\project\calendar\calendar_1>npm init This utility will walk you through creating a package.json file. It only covers the most common items, and tries to guess sensible…

甘草书店记:6# 2023年10月31日 星期二 「梦想从来不是一夜之间实现的」

甘草书店 今天收到甘草书店第二版装修设计平面图&#xff0c;与理想空间越来越近。 于我而言&#xff0c;每一次世俗意义上所谓的成功都不如文艺作品中表现的那样让人欢腾雀跃。当你用尽120分努力&#xff0c;达到了冲刺满分的实力时&#xff0c;得个优秀的成绩也并不意外。 …

Pycharm配置jupyter使用notebook详细指南(可换行conda环节)

本教程为事后记录&#xff0c;部分图片非实操图片。 详细记录了pycharm配置jupyter的方法&#xff0c;jupyter添加其他conda环境的方法&#xff0c;远程密码调用jupyter的方法&#xff0c;修改jupyter工作目录的方法。 文章目录 一、入门级配置1. Pycharm配置Conda自带的jupyt…

案例研究|作为一家BI厂商,飞致云是如何人人使用DataEase的?

杭州飞致云信息科技有限公司&#xff08;以下简称为飞致云&#xff09;长期秉持“软件用起来才有价值&#xff0c;才有改进的机会”的核心价值观&#xff0c;以“为数字经济时代创造好软件”为使命&#xff0c;致力于成为中国数字化团队首选的通用工具软件提供商。在软件产品不…

容器与集群——通过deployment 创建pod以及Java Web应用的容器化发布

## 一、通过deployment 创建pod 1.1 编写yaml文件 1.2 安装pod 创建kubectl create -f dp-nginx.yaml 查看Deployment信息 1.3 查看相关信息 查看pod信息kubecel get pods 查看rs信息 二、Java Web应用的容器化发布 1. 环境准备 部署K8s集群并启动。 为了与其他pod…

基恩士软件的基本操作(六,KV脚本的使用)

目录 什么是KV脚本&#xff1f; KV脚本有什么用&#xff1f; 怎么使用KV脚本&#xff08;脚本不能与梯形图并联使用&#xff09;&#xff1f; 插入框脚本&#xff08;CtrlB&#xff09; 插入域脚本&#xff08;CtrlR&#xff09; 区别 脚本语句&#xff08;.T是字符串类…

PTA 一维数组7-3出生年(本题请你根据要求,自动填充“我出生于y年,直到x岁才遇到n个数字都不相同的年份”这句话)

以上是新浪微博中一奇葩贴&#xff1a;“我出生于1988年&#xff0c;直到25岁才遇到4个数字都不相同的年份。”也就是说&#xff0c;直到2013年才达到“4个数字都不相同”的要求。本题请你根据要求&#xff0c;自动填充“我出生于y年&#xff0c;直到x岁才遇到n个数字都不相同的…

二极管:二极管的基本原理

一、认识导体、绝缘体、半导体 什么是导体&#xff1f; 导体 conductor &#xff0c;是指电阻率很小&#xff0c;且容易传导电流的物质。导体中存在大量可自由移动的带电粒子&#xff0c;也称为载流子。在外电场的作用下&#xff0c;载流子作定向运动&#xff0c;形成电流。 …

沐足采耳店服务预约管理系统会员小程序效果如何

沐足采耳店的很多服务都有较高的市场需求度&#xff0c;如贵妃采耳、太极足疗等&#xff0c;很多城市中都有多个品牌店铺&#xff0c;在如今互联网趋势下&#xff0c;商家们开始借势线上解决传统经营痛点和促进生意发展。 那么通过【雨科】平台搭建沐足采耳店管理小程序能实现…