项目中又遇到了自定义日历,记录下来,方便以后抄代码~~0_0~
自定义日历最重要的三步:
- 找到当前月有多少天,以及第一天是星期几--灰常重要!
- 根据第一步找到需要填充的上月的日期,因为第一步中获取的到下标刚好是需要显示的上个月的天数
- 根据第一步,第二步获取需要填充的下个月的日期
这里是基于moment实现的~
先介绍几个moment方法:
一 :moment.weekdaysMin()
获取缩写的星期
二: moment().daysInMonth()
获取当前月的天数
三: moment().startOf('month').weekday()
获取当前月第一天是星期几
四: moment().subtract(1, 'month')
获取上个月月份
四: moment().add(1, 'month')
获取下个月月份
上代码:
index.jsx
import React, { useState } from 'react';
import classnames from 'classnames';
import { initCalendar } from '../utils';
import { Typography } from 'antd';
import Header from '../header';
import './index.less';
export default function Content({ className }) {
const [days, setDays] = useState(initCalendar());
const [active, setActive] = useState(0);
return (
<div className={classnames('content_container', className)}>
<div className={classnames('toolbar_container', className)}>
<Space>
<Button icon={<LeftOutlined />} type="text" onClick={handleUp} />
{currentMonth.format(YEAER_MONTH_FORMAT_ZH)}
<Button icon={<RightOutlined />} type="text" onClick={handleDown} />
</Space>
</div>
<div className="content_wrapper">
{days?.map((day, index) => (
<div
onClick={() => setActive(index)}
className={classnames('day_container', {
active: index === active,
})}
key={index}
>
<Typography.Text>{day}</Typography.Text>
</div>
))}
</div>
</div>
);
}
index.less
.content_container {
width: 100%;
height: 100%;
.toolbar_container {
display: flex;
justify-content: center;
border: 1px solid rgb(215 215 215);
background: white;
}
.calendar_content_wrapper {
width: 100%;
height: 100%;
display: grid;
border: 1px solid rgb(215 215 215);
grid-template-columns: repeat(7, 1fr);
background: white;
.day_container {
display: flex;
justify-content: center;
height: 100px;
box-sizing: content-box;
border-right: 1px solid rgb(215 215 215);
border-bottom: 1px solid rgb(215 215 215);
cursor: pointer;
position: relative;
.more {
position: relative;
width: 23px;
height: 23px;
border: 1px solid #000;
left: 0;
bottom: 0;
}
}
.date_title {
display: inline-block;
width: 24px;
height: 20px;
line-height: 20px;
margin-left: 4px;
text-align: center;
font-size: 16px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: rgb(103 113 122);
}
.active {
box-shadow: inset 0px 0px 5px 1px lightgreen;
}
.isToday {
background: lightgreen;
}
}
}
utils.js
import moment from 'moment';
import { YEAER_MONTH_FORMAT, INIT_DAYS } from './const';
/**
* 获取当前月的天数
*/
console.log(moment().format('YYYY-MM-DD'), 'moment()');
export function getMonthDays() {
return moment().daysInMonth();
}
/**
* 获取当前月第一天是星期几
*/
export function getWeekDays() {
return moment().startOf('month').weekday();
}
/**
* 获取上个月份
*/
export function getLastMonth() {
return moment().subtract(1, 'month').format(YEAER_MONTH_FORMAT);
}
/**
* 获取下个月份
*/
export function getNextMonth() {
return moment().add(1, 'month').format(YEAER_MONTH_FORMAT);
}
/**
* 初始化日历
*/
export function initCalendar() {
// 当前月天数
const totalDaysInMonth = getMonthDays();
// 上个月天数
let totalDaysInLastMonth = getMonthDays(getLastMonth());
// 下个月开始日期
let nextFirstDate = 1;
const lastDays = [],
currentDays = [],
nextDays = [];
// 当前月第一天是星期几(索引值)
/**
* 这里的索引值刚刚好是需要填充的上月的天数
*/
const currentWeekDay = getWeekDays();
for (let i = 0, len = 42; i < len; i++) {
// 填充上个月的天数
if (i < currentWeekDay) {
lastDays.push(totalDaysInLastMonth);
totalDaysInLastMonth--;
}
// 填充下个月的天数
else if (i >= totalDaysInMonth + currentWeekDay) {
nextDays.push(nextFirstDate);
nextFirstDate++;
}
// 填充当前月天数
else {
currentDays.push(i - currentWeekDay + 1);
}
}
// 上个月需要倒序显示
return [...lastDays.reverse(), ...currentDays, ...nextDays];
}
const.js
import moment from 'moment';
export const YEAER_MONTH_FORMAT_ZH = 'YYYY年MM月';
export const YEAER_MONTH_FORMAT_EN = 'YYYY-MM';
export const YEAER_MONTH_FORMAT_FULL = 'YYYY-MM-DD';
// 获取星期
const [_, ...rest] = moment.weekdaysMin();
// 将星期天放置数组最后
export const WEEk_HEADER = [...rest, _];
// 日历布局为6 * 7单元格
export const TOTAL_LENGTH = 6 * 7;
// 初始化一下下
export const INIT_DAYS = Array(TOTAL_LENGTH)
.fill(0)
.map((_, index) => +index + 1);