react+antd+dayjs封装自定义日期组件

3,507 阅读6分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

hello,小伙伴们好,今天给大家分享一个自定义日期组件实战,涉及了reacthooks,css in js的知识。难度不大,但实用性很强,如果你的项目也频繁做日期相关的操作,那就可以来看看。

1.什么样的效果?

  • 如图,这是最终实现的效果:

微信图片_20220113143746.png

  • 主页面就只有三个东西,显示‘今天’的div,显示日期的div和显示蓝色按钮的悬浮框。
  • 当鼠标点击“今天”时,会弹出蓝色按钮选择浮层,共有十几种日期类型。
  • 鼠标移动到蓝色button上,有阴影效果,当前选中的日期类型会保持阴影效果。
  • 点击蓝色按钮,更新显示器文本,并通过dayjs计算出对应的时间范围,显示在下面。

难点分析

  • 这个日期组件的难点主要在于日期的转换,比如点击了上周,上月,最近三十天等按钮,我们如何计算出具体的时间范围。
  • 然后其他就没什么难点了,我们接下来看看这个日期组件如何封装。

2.技术栈介绍

  • css in js 我这里写样式主要用css in js的方式,所以用了styled-components库,github上35k的点赞数,还没有入手过的小伙伴可以去看看,用起来非常爽。我这里介绍下简单的用法:
import styled from 'styled-components';

const width=100;
const Button = styled.div`
  background: palevioletred;
  border-radius: 3px;
  border: none;
  color: white;
  width:${width}px;
`
const App=()=>{
  return <Button>这是一个自定义button</Button>
}
  • 它的写法就是写组件一样的形式写样式,可以定义变量之类的直接使用。

  • 第二个库 dayjs ,一个专门操作时间的库。github 37k点赞,也是个很棒的库。我简单介绍下用法:

import * as dayjs from "dayjs";

const format = "YYYY-MM-DD HH:mm:ss";//定义时间格式

dayjs(new Date().getTime()).format(format);
//输出: 2022-01-13 16:12:57,这是我电脑当前的时间
dayjs(new Date()).format(format);
//输出: 2022-01-13 16:12:57
dayjs().startOf("day").format(format)
//输出: 2022-01-13 00:00:00 获取今天开始的时间
dayjs().subtract(1,'day').endOf('day').format(format)
//输出: 2022-01-12 23:59:59 //获取昨天结束的时间,subtract代表减去,这里1和'day'代表减去一天
  • 用到的库就是这两个,antd就不用介绍了,大家应该都了解。接下来我们开始封装组件

3.组件封装第一步:常量和时间获取方法的编写

  • 定义时间类型常量,方便我们之后再增加别的时间类型,如下:
const TIME_TXT = {
  TODAY: "今天",
  YESTERDAY: "昨天",
  TOMORROW: "明天",
  LAST_WEEK: "上周",
  THIS_WEEK: "这个周",
  THIS_MONTH: "这个月",
  LAST_MONTH: "上个月",
  THIS_YEAR: "今年",
  LAST_YEAR: "去年",
  LAST_THIRTY_DAY: "最近三十天",
  LAST_HOUR: "最近一小时",
  LAST_HALF_AN_HOUR: "最近半小时",
};
  • 定义时间获取方法:
const format = "YYYY-MM-DD HH:mm:ss";

const getTime = (type) => {
  switch (type) {
    case TIME_TXT.TODAY://获取今天的开始和结束时间
      return {
        begin: dayjs().startOf("day").format(format),
        end: dayjs().endOf("day").format(format),
      };
    case TIME_TXT.YESTERDAY://获取昨天的开始和结束时间
      return {
        begin: dayjs().startOf("day").subtract(1, "day").format(format),
        end: dayjs().endOf("day").subtract(1, "day").format(format),
      };
    case TIME_TXT.TOMORROW://获取明天的开始和结束时间
      return {
        begin: dayjs().startOf("day").add(1, "day").format(format),
        end: dayjs().endOf("day").add(1, "day").format(format),
      };
    case TIME_TXT.LAST_WEEK://获取上周的开始和结束时间
      return {
        begin: dayjs().day(1).startOf("day").subtract(7, "day").format(format),
        end: dayjs().day(0).endOf("day").format(format),
      };
    case TIME_TXT.THIS_WEEK://获取这周的开始和结束时间
      return {
        begin: dayjs().day(1).startOf("day").format(format),
        end: dayjs().day(7).endOf("day").format(format),
      };
    case TIME_TXT.THIS_MONTH://获取这个月的开始和结束时间
      return {
        begin: dayjs().startOf("month").format(format),
        end: dayjs().endOf("month").format(format),
      };
    case TIME_TXT.LAST_MONTH://获取上个月的开始和结束时间
      return {
        begin: dayjs().startOf("month").subtract(1, "month").format(format),
        end: dayjs().endOf("month").subtract(1, "month").format(format),
      };
    case TIME_TXT.THIS_YEAR://获取今年的开始和结束时间
      return {
        begin: dayjs().startOf("year").format(format),
        end: dayjs().endOf("year").format(format),
      };
    case TIME_TXT.LAST_YEAR://获取上一年的开始和结束时间
      return {
        begin: dayjs().startOf("year").subtract(1, "year").format(format),
        end: dayjs().endOf("year").subtract(1, "year").format(format),
      };
    case TIME_TXT.LAST_THIRTY_DAY://获取最近三十天的开始和结束时间
      return {
        begin: dayjs().subtract(30, "day").format(format),
        end: dayjs().format(format),
      };
    case TIME_TXT.LAST_HOUR://获取最近一小时的开始和结束时间
      return {
        begin: dayjs().subtract(1, "hour").format(format),
        end: dayjs().format(format),
      };
    case TIME_TXT.LAST_HALF_AN_HOUR://获取最近半小时的开始和结束时间
      return {
        begin: dayjs().subtract(30, "minute").format(format),
        end: dayjs().format(format),
      };
  }
};
  • 方法内容很多,但难度不大,我这里主要讲两个时间的获取,这个周这个月的时间如何获取。
  • dayjs() 代表当前时间。dayjs().day(1) 代表设置时间为星期一,dayjs().day(1).startOf() 代表获取星期一的开始时间。 dayjs().day(7) 代表设置时间为星期日。dayjs().day(7).endOf() 代表获取时间为星期日的结束时间。
  • dayjs().startOf("month").format(format) 就是获取这个月开始时间。
  • subtract(1, "month") 代表当前时间减去一个月。
  • dayjs().startOf("month").subtract(1, "month").format(format) 就是获取上个月开始时间。
  • 怎么样dayjs是不是很强大,用起来十分方便。

常量定义完毕,时间获取方法定义完毕,接下来,我们开始封装组件。

4.封装日期组件

  • 日期组件我拆为了两个部分,一个是时间显示层,一个是弹框。这里的弹框是基于antd的Popover做的。我来一一讲解,先看css in js的使用,我定义了几个样式组件,就是写了一些圆角样式和阴影:
import React, {  useState } from "react";
import Styled from "styled-components";
import { ClockCircleOutlined } from "@ant-design/icons";
import { Popover } from "antd";
import * as dayjs from "dayjs";


const Wrap = Styled.div`
  height:30px;
  display:flex;
  min-width:100px;
  align-items:center;
  box-shadow:0 0 3px 3px skyblue;
  padding:0 20px;
  border-radius:4px;
  cursor:pointer;
  
`;

const Time = Styled.div`
  box-shadow:0 0 3px 3px skyblue;
  padding:5px 20px;
  border-radius:4px;
  margin:10px;
`;

const SelectWrap = Styled.div`
   display: flex;
   flex-wrap:wrap;
  .txt-item {
    border-radius: 4px;
    padding: 0 20px;
    background:#1890ff;
    color:white;
    margin:5px;
    height:30px;
    display:flex;
    align-items:center;
    cursor:pointer;
  }
`;
  • 弹框内容组件的封装如下:
const TimeSelect = (props) => {
  const { selected, onClick } = props;
  const [hoverItem, setHoverItem] = useState(undefined);

  return (
    <SelectWrap className={"txt-wrap"}>
      {Object.values(TIME_TXT).map((item, index) => {
        return (
          <div
            key={index}
            className={"txt-item"}
            style={{
              boxShadow:
                hoverItem === index || item === selected
                  ? "0 0 3px 3px #333"
                  : "",
            }}
            onMouseOver={() => setHoverItem(index)}
            onMouseOut={() => setHoverItem("")}
            onClick={onClick.bind(this, item)}
          >
            {item}
          </div>
        );
      })}
    </SelectWrap>
  );
};
  • 这个组件就是渲染那一排蓝色的按钮,处理了鼠标移入移出事件,移入的时候给按钮增加一个黑色阴影。

  • 接收了父组件的selected和onClick,selected代表当前选中的哪个日期类型(蓝色按钮),被选中的做一个黑色阴影。onclick事件绑定在蓝色按钮上。

  • 最后看显示层组件的封装:

const CustomTime = () => {
  const [selected, setSelected] = useState(TIME_TXT.TODAY);

  const handleClick = (type) => {
    setSelected(type);
  };
  
 const time=getTime(selected);

  return (
    <div
      style={{ display: "flex", flexDirection: "column", alignItems: "center" }}
    >
      <Popover
        placement="top"
        title={null}
        content={<TimeSelect selected={selected} onClick={handleClick} />}
        trigger="click"
      >
        <Wrap>
          <div style={{ marginRight: 15, whiteSpace: "nowrap" }}>
            {selected}
          </div>
          <ClockCircleOutlined style={{ color: "#1890ff" }} />
        </Wrap>
      </Popover>
      <Time >
        {time?.begin} - {time?.end}
      </Time>
    </div>
  );
};
  • 定义了一个state,selected代表当前选中的日期类型。还定义了一个常量time代表这个日期类型对应的时间。定义了一个click方法用来处理那一排蓝色按钮的点击事件,点击了就重新赋值selected;

总结

  • 日期组件的封装就这样结束了,小伙伴们快去试一试dayjs,css in js的使用吧,结合react用起来是真的很爽哦!