本文已参与「新人创作礼」活动,一起开启掘金创作之路
hello,小伙伴们好,今天给大家分享一个自定义日期组件实战,涉及了reacthooks,css in js的知识。难度不大,但实用性很强,如果你的项目也频繁做日期相关的操作,那就可以来看看。
1.什么样的效果?
- 如图,这是最终实现的效果:
- 主页面就只有三个东西,显示‘今天’的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>
}
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用起来是真的很爽哦!