写这样一个组件的初衷

项目用的是antd的组件,一开始用antd上面的日历组件本来没什么问题,感觉挺好用的,可是某一天需求人员说要求日历只展示一周或半个月,然后我就懵逼了,antd上面的日历组件根本就没有周模式的,只有月和年模式,再说,感觉根本没必要啊,但是用户强烈要求有这个功能,没办法,只能自己写一个了。

废话不多说,直接上代码:

首先创建Calendar.js文件

import React from 'react';
import {Select, Col, Row, Radio} from 'antd';
/**
*Calendar 日历组件
*@param currentDate {Date} 展示日历,默认当天
*@param onChange {function(date: Date)} 日期变化的回调
*@param dateCellRender{function(date: Date): ReactNode} 自定义渲染日期单元格,返回内容会被追加到单元格
*/
const { Option } = Select;
const { Group, Button } = Radio;//周名称
const WEEK_NAMES = ['一', '二', '三', '四', '五', '六', '日'];
//月份
const MONTH_NAMES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
// 日历行数
let LINES = [1, 2, 3, 4, 5, 6];export default class Calendar extends React.Component {state = {year: 0,month: 0,day: 0.mode: 'month', // 模式:周、月、年yearRange: [] // 年份范围}// 更新日期componentWillMount(){this.setCurrentYearMonth(this.props.currentDate);}componentDidUpdate(prevProps){let prevDate = prevProps.currentDate;let prevDay = Calendar.getDate(prevDate);let prevMonth = Calendar.getMonth(prevDate);let prevYear = Calendar.getFullYear(prevDate);let currentDate = this.props.currentDate;let currentDay = Calendar.getDate(currentDate);let currentMonth = Calendar.getMonth(currentDate);let currentYear = Calendar.getFullYear(currentDate);// 如果日期发生了变化就更新日期if (prevDay !== currentDay || prevMonth !== currentMonth || prevYear !== currentYear) {this.setCurrentYearMonth(currentDate);}}//  设置当前组件的年月setCurrentYearMonth(date){let day = Calendar.getDate(date);let month = Calendar.getMonth(date);let year = Calendar.getFullYear(date);let yearRange = Calendar.getYearRange(year);Calendar.lineNumbers(year, month, day, this.state.mode);this.setState({day,month,year,yearRange})}// 获取当前日static getDate(date) {return date.getDate();}// 获取当前月份static getMonth(date) {return date.getMonth();}// 获取当前年份static getFullYear(date) {return date.getFullYear();}// 获取年份范围 以当前年份前后推10年static getYearRange(year) {let yearRange = [];let startYear = year - 10;let endYear = year + 10;for(let i = startYear; i <= endYear; i++) {yearRange.push(i);}return yearRange;}// 根据年份和月份获取对应月份的总天数static getCurrentMonthDays(year, month) {const curMonthDays = new Date(year, month + 1, 0).getDate();return curMonthDays;}// 这里获得每个月的第一天是周几static getWeeksByFirstDay(year, month) {let date = Calendar.getDateByYearMonth(year, month);return date.getDay();}// 获取当天是周几static getWeeksByDay(year, month, day) {let date = Calendar.getDateByYearMonth(year, month, day);return date.getDay();}// 设置日期行数static lineNumbers(year, month, day, mode) {if (mode === 'month') {// 当前月份的总天数let monthDays = Calenday.getCurrentMonthDays(year, month);// 当前月份的第一天是周几let weekDay = Calenday.getWeeksByFirstDay(year, month);if (monthDays === 28 && weekDay === 1) {LINES = [1,2,3,4];} else if ((monthDays === 30 && weekDay === 0) || (monthDays === 31 && (weekDay === 0 || weekDay === 6))) {LINES = [1,2,3,4,5,6];} else {LINES = [1,2,3,4,5];}} else if (mode === 'week') {let weekDay = Calenday.getWeeksByDay(year, month, day);LINES = weekDay === 1 ? [1] : [1,2];}}// 日期展示文本getDayText(line, weekIndex, weekDay, monthDays, mode) {let year = this.state.year;let month = this.state.month;//                     一 二 三 四 五 六 日// getDay()对应的值: 1  2  3  4  5  6  0let _weekDay = weekDay;// 如果月份的第一天为周日,那么调整该值if (weekDay === 0) {_weekDay = 7;}let number;// 月模式if (mode === 'month') {number = line * 7 + weekIndex - _weekDay + 2;if (number <= 0 ) { // 上一个月// 如果是一月份, 那么上个月就是去年的12月份if (month === 0) {year = year - 1;month = 11;} else {month = month - 1;}// 获取上一个月的总天数let preMonthDays = Calendar.getCurrentMonthDays(year, month);number = preMonthDays + number;} else if (number > monthDays) { // 下个月if (month === 11) {year = year + 1;month = 0;} else {month = month + 1;}number = number + monthDays;}} else { // 周模式const day = this.state.day;number = line * 7 + weekIndex - _weekDay + 1 + day;if (number > monthDays && number <= (day + 6)) {if (month === 11) {year = year + 1;month = 0;} else {month = month + 1;}number = number - monthDays;} else if (number < day || number > (day + 6)) {return '';}}return year + '-' month + '-' + number;}// 获取当前月份某一天的日期,默认第一天static getDateByYearMonth(year, month, day = 1) {let _day = day;// 获取当前月份的总天数let curMonthDays = this.getCurrentMonthDays(year, month);// 如果 day大于当天月份的总天数,则day的值为当天月份的总天数,例如,5月31日切换到4月份则为4月30日, 因为4月没有31日:5.31 => 4.30if (_day > curMonthDays) {_day = curMonthDays;}let date = new Date();date.setFullYear(year);date.setMonth(month, _day);return date;}// 切换模式:周、月、年onModeChange = value => {const {year, month, day} = this.state;Calendar.lineNumbers(year, month, day, value);this.setState({mode: value});}// 选中日期onClickDay = (dayText, mode) {let dayTextList = dayText.split('-');let year = dayTextList[0] - 0;let month = dayTextList[1] - 0;let day = dayTextList[2] - 0;let currentDate = new Date(year, month, day);Calendar.lineNumbers(year, month, day, mode);this.props.onChange(currentDate );}// 年份下拉框选择selectYear = year => {const {month, day} = this.state;let currentDate = new Date(year, month, day);this.props.onChange(currentDate);}// 月份选择selectMonth = month => {const {year, day} = this.state;let currentDate = new Date(year, month, day);this.props.onChange(currentDate);}monthDom = month => {return (<tdonClick={() => {this.selectMonth(month)}}style={{backgroundColor: this.state.month === month ? '#E0FFFF' : 'white',color: this.state.month === month ? '#33CCFF' : '#000',cursor: 'pointer'}}>{`${month + 1}月`}</td>)}// 将年月日转换成对应的日期类型getDateType = (year, month, dayText) => {if (typeof dayText === 'number') {return new Date(year, month, dayText);}}render() {let { day, year, month, mode, yearRange } = this.state;// 当前月份的总天数let monthDays = Calendar.getCurrentMonthDays(year, month);// 周模式let weekDay = mode === 'week' ? Calendar.getWeeksByDay(year, month, day) : Calendar.getWeeksByFirstDay(year, month);const { style, dateCellRender} = this.props;return (<div><table cellPadding={0} cellSpacing={0} className="table" style={style}><caption><div style={{padding: 10}}><Row type="flex" justify="end"><Col span={4}><SelectdropdownMatchSelectWidth={false}className="my-year-select"onChange={this.selectYear}value={year}>{yearRange.map(item => <Option key={item} value={item}>{`${item}年`}</Option>)}</Select></Col><Col span={4}><SelectdropdownMatchSelectWidth={false}onChange={this.selectMonth}value={month}>{MONTH_NAMES.map(item => <Option key={item} value={item}>{`${item + 1}月`}</Option>)}</Select></Col><Col span={7}><GrouponChange={e => this.onModeChange(e.target.value)}value={mode}><Button value='week'>周</Button><Button value='month'>月</Button><Button value='year'>年</Button></Group></Col></Row></div></caption><thead>{mode !== 'year' ? (<tr>{WEEK_NAMES.map((week, key) => {return <td key={key}>{week}</td>})}</tr>) : null}</thead>{(mode === 'month' || mode === 'week') ? (// 周和月模式<tbody>{LINES.map((item, key) => {return <tr key={key} className='mwTr'>{WEEK_NAMES.map((week, index) => {let dayText = this.getDayText(key, index, weekDay, monthDays, mode);let year_ = year, month_ = month, day_, dayTextList;if (dayText.indexOf('-') > -1) {dayTextList = dayText .split('-');year_ = dayTextList[0] - 0;month_ = dayTextList[1] - 0;day_ = dayTextList[2] - 0;} else {day_ = <span>&nbsp;</span>}let isToday = day_ === day && month_ === month; // 判断是否是当天日期或者是选中的日期let isCurrentMonth = month_ === month; // 判断是否是当前月的日期;let hoverStyle = typeof day_ === 'number' ? 'hoverStyle' : 'noHover';let todayStyle = isToday ? 'isToday' : 'noToday';return (<tdkey={index}onClick={() => { this.onClickDay(dayText, mode) }}title={typeof day_ === 'number' ? `${year_}年${month_ + 1}月${day_}日` : null}className={`${hoverStyle} ${todayStyle}`}><div className={`day ${isCurrentMonth ? '' : 'grayColor'}`}>{day_ < 10 ? '0' + day_ : day_}</div><div className='context'>{typeof dateCellRender === 'function' && dateCellRender(this.getDateType(year_, month_, day_))}</div></td>)})}</tr>})}</tbody>) : (// 年模式<tbody><tr className='yearTr'>{this.monthDom(0)}{this.monthDom(1)}{this.monthDom(2)}</tr><tr className='yearTr'>{this.monthDom(3)}{this.monthDom(4)}{this.monthDom(5)}</tr><tr className='yearTr'>{this.monthDom(6)}{this.monthDom(7)}{this.monthDom(8)}</tr><tr className='yearTr'>{this.monthDom(9)}{this.monthDom(10)}{this.monthDom(11)}</tr></tbody>)}</table></div>)}
}

然后是样式 calendar.css:

@import '~antd/dist/antd.css';* {margin: 0;padding: 0;
}
.table {border-collapse:collapse;border-spacing: 0;
}
.table td {border: 1px solid #ddd;padding: 10px;min-width: 60px;
}
.table caption {border-top: 1px solid #ddd;border-right: 1px solid #ddd;border-left: 1px solid #ddd;padding: 10px;display: flex;justify-content: space-between;
}
.table caption .caption-header .arrow {cursor: pointer;font-family: "宋体";transition: all 0.3s;
}
.table caption .caption-header .arrow:hover {opacity:0.7;
}
.table tbody .mwTr td {cursor: pointer;position: relative;
}
.table tbody .mwTr .isToday {background-color: #E0FFFF;color: #33CCFF;
}
.table tbody .mwTr .noToday {background-color: white;color: #000;
}
.table tbody .mwTr .hoverStyle:hover{background-color: #E0FFFF;
}
.table tbody .mwTr .day{position: absolute;top: 0;right: 3px;
}
.table tbody .mwTr .grayColor{color: #ddd;
}
.table tbody .mwTr .context{margin-top: 5px;max-height: 100px;overflow-y: auto;
}
.table tbody .yearTr td {width: 33%;
}

现在来测试一下日历组件:

import React from 'react';
import Calendar from './Calendar';
class Test extends React.PureComponent {state = {currentDate: new Date()}onChangeDate = value => {this.setState({currentDate: value});}getChangeDate = value => {if (value) {let year = value.getFullYear();let month = value.getMonth() + 1;let day = value.getDate();return year + '年' + month + '月' + day + '日'}}dateCellRender = value => {return <div>this.getChangeDate(value)</div>}render(){const { currentDate } = this.state;return (<Calendar style={{width: '50%'}} currentDate={currentDate} onChange={this.onChangeDate} dateCellRender={dateCellRender} />)}
}

自己写一个含有周、月、年模式的日历组件相关推荐

  1. 教你如何写一个符合自己需求的小程序日历组件

    1|0 前言 很多时候,我们生活中会有各种打卡的情况,比如 keep 的运动打卡.单词的学习打卡和各种签到打卡或者酒店的入住时间选择,这时候就需要我们书写一个日历组件来处理我们这种需求. 但是更多时候 ...

  2. mysql查询最近一个自然周_自然周与自然月的Hive统计SQL

    按照周或者月统计活跃数: 周: SELECT week, COUNT(DISTINCT pin), business_type FROM ( SELECT DISTINCT user_log_acct ...

  3. 面试官让我手写一个生产者消费者模式?

    不知道你是否遇到过面试官让你手写生产者消费者代码.别说,前段时间有小伙伴还真的遇到了这种情况,当时是一脸懵逼. 但是,俗话说,从哪里跌倒就要从哪里爬起来.既然这次被问到了,那就回去好好研究一下,争取下 ...

  4. 经典笔试题:用C写一个函数测试当前机器大小端模式

    "用C语言写一个函数测试当前机器的大小端模式"是一个经典的笔试题,如下使用两种方式进行解答: 1. 用union来测试机器的大小端 1 #include <stdio.h&g ...

  5. 【scala】获取当前时间的上一个自然周以及自然周集合;获取当前时间的上一个自然月以及自然月的第一天与最后一天

    获取当前时间的上一个自然周的开始时间: import java.text.SimpleDateFormat import java.util.{Calendar, Date}def getLastWe ...

  6. 试写一个算法,识别依次读入的一个以“@”为结束符的字符序列是否为形如“序列1序列2”模式的字符序列。其中序列1和序列2都不含字符“”,且序列2是序列1的逆序列。例如,“a+bb+a”是属该模式

    由于题目要求,此篇文章用的是纯c写的- 已经一年半没有写c的我一口老血喷出来- [问题描述] 试写一个算法,识别依次读入的一个以"@"为结束符的字符序列是否为形如"序列1 ...

  7. 定义一个结构体变量(包括年、月、日)。计算该日在本年中是第几天,注意闰年 问题。 写一个函数days,实现上述计算。由主函数将年、月、日传递给days函数,计算后将 日子数传回主函数输出

    /*定义一个结构体变量(包括年.月.日).计算该日在本年中是第几天,注意闰年 问题. 写一个函数days,实现上述计算.由主函数将年.月.日传递给days函数,计算后将 日子数传回主函数输出*/#in ...

  8. 用装饰者模式 动手写一个导弹系统组装功能(滑稽+1)

    话接上文,敌人导弹来袭,我们的雷达做出了及时反应,通过观察者模式通知了五大战区做好防御, <用观察者模式 动手写一个导弹预警系统(滑稽)> 现在轮到我们反击了. 目前国防部发布了指示要求我 ...

  9. vue移动端项目日历组件,月周切换,点击进入上/下一个月

    项目场景: Vue移动端项目的日历组件,移动端如果没有别的特别要求,一般用vant中的日历组件就OK,这里用的另一个.组件是网上找的,原网址:vue-hash-calendar,需要的请自行去看. 我 ...

最新文章

  1. Selenium3自动化测试——17.控制滑动解锁
  2. java中什么表示菜单项_下列类型中,表示菜单项的是( )。_学小易找答案
  3. 【读书笔记】iOS-NSString的length
  4. Arcgis for Javascript实现两个地图的联动
  5. Java SpringMVC实现PC端网页微信扫码支付完整版
  6. 什么!卷积要旋转180度?!
  7. 微软面试题1、把二元查找树转变成排序的双向链表
  8. APP签名MD5获取
  9. mysql配置命令大全_MySQL常用命令汇总
  10. 基于matlab的车牌识别系统设计错误,基于MATLAB的车牌识别系统设计
  11. 70部MAYA灯光材质渲染教程合集
  12. 【分享】外卖优惠券怎么弄微信返利公众号系统的流程和方法
  13. Android发短信功能
  14. 软件需求的薛定谔之猫
  15. 【Python】可视化台风路径轨迹图
  16. read函数和write函数
  17. Android手机导出的已安装的APK到电脑
  18. 移动客户端与服务器通信方式一
  19. 六十星系之01紫微独坐子午
  20. 一区WR | 河海大学李轶课题组利用环境兼容的载氧生物炭修复缺氧淡水生物机制

热门文章

  1. 小米品牌:图腾化的胜利
  2. 使用脚本启动java程序
  3. 校验字符串是否为null或者空白/空格
  4. 全新的移动互联网,互联网营销
  5. 最嚣张的骗子跑路公告。
  6. 2020.7.4-参加YMO数学竞赛复赛
  7. 学习记录:图形界面系列教材
  8. oracle19c_rac远程连接pdb配置
  9. 理解一维数组中 buf、buf[0]、buf[0]、buf 四个符号的含义
  10. html5 source标签,HTML 5 source 标签 - HTML 参考手册