需求
我们需要还原UI给我们的设计图里面的日历样式, 找到了一款第三方日历库,我们如何进行魔改呢?
这是react-calendar
库官方示例中的代码,我们导入使用默认样式就是这个样子
我们需要做成下面的这个样子
咋一看,确实感觉没有什么思路, 不过跟着步伐来,你会发现其实不复杂.
因为接到这样的一个需求, 我大概了看了一下UI设计图,然后第一反应就是去掘金,GITHUB去找有没有对应的轮子库, 但找了一圈,没有找到像这种个性化定义的. 但是要是自己去写吧,自己不一定能写的出来, 而且耗时耗力. 所以也没多想就直接找了一个react用的较多的日历库react-calendar
.
方案选择
下面是关于这个库的一些介绍:
React Calendar 是一个用于 React 的灵活且易于使用的日历组件。它允许开发人员在他们的 React 应用程序中轻松集成日期选择功能。以下是对 React Calendar 的详细介绍:
- 简单易用
- React Calendar 提供了简单直观的 API,方便开发人员快速上手并集成到项目中。
- 高度可定制
- 组件提供了多种配置选项,允许开发人员根据需要自定义日历的外观和行为。例如,可以设置日期格式、最小和最大日期、禁用特定日期等。
- 支持多种视图
- React Calendar 支持多种视图模式,包括月视图、年视图等,用户可以根据需求切换视图。
- 事件处理
- 组件提供了丰富的事件处理函数,如日期选择、视图切换等,方便开发人员在不同的交互事件中执行自定义逻辑。
- 国际化支持
- React Calendar 支持多种语言和区域设置,可以轻松实现多语言的日期显示和选择功能。
二话不说,我们直接开始编写.
定义组件,导入库,初始化日历
我们定义一个组件 ClockInCalendar.tsx
. 然后将官网的示例直接填写进去.
tsx
import { useState } from 'react'; import Calendar from 'react-calendar'; type ValuePiece = Date | null; type Value = ValuePiece | [ValuePiece, ValuePiece]; function ClockInCalendar() { const [value, onChange] = useState<Value>(new Date()); return ( <div> <Calendar onChange={onChange} value={value} /> </div> ); } export default ClockInCalendar
然后在其他组件进行导入即可
app.tsx
tsx
import ClockInCalendar from './ClockInCalendar' ....... <ClockInCalendar></ClockInCalendar>
此时我们的页面就是这样的
我们需要修改哪些东西呢,观察一开始的那个成品就会发现:
- 顶部全部进行修改 [
改写成我们的头部样式
] - 周一, 周二, 周三, 转换为
一, 二, 三
- 日期的话只需要
数字即可
- 数字下方需要显示
打卡状态
, [绿色:已打卡
] , [黄色:请假
], [红色:未打卡
] - 当天日期的背景颜色需要
高亮显示
- 日历可以进行一个
展开\折叠的效果
- ....
还有好多小细节需要处理, 不要担心, 跟着我的步伐一步步来, 不难实现!
头部魔改
我们打开F12 就会看到这个, 我们的思路是 将这个进行隐藏display:none
, 然后编写自己的DOM结构 + CSS样式
.
首先创建一个自定义的css文件, 专门用来覆盖
组件的内部样式的
css
.react-calendar__navigation{ display: none; }
然后在_app.tsx [NEXT项目]里面进行一个全局导入
tsx
import '../styles/customCalendar.css'
此时我们打开页面, 就会发现日历的头部没有了
然后我们就可以编写头部的结构和样式了,
这里就不放代码, 大概就是左边一部分, 右边一部分, 其中左边又可以分为日历icon + 年月份 + 打卡数量, 右边则是上个月和下个月的button.
日历的周字去除
formatShortWeekday
是 react-calendar
库中的一个方法,用于格式化一周中每一天的显示名称。这个方法主要用于显示日历组件中的星期几的缩写形式。
locale
: 当前的区域设置(例如en-US
、zh-CN
等),决定了日期格式的语言和地区规则。date
: 当前的日期对象,代表一周中的某一天。
tsx
<Calendar onChange={onChange} value={date} locale="zh-CN" formatShortWeekday={formatShortWeekday} />
对应的方法编写
tsx
const formatShortWeekday = (locale: any, date: Date) => { const weekdays = ['日', '一', '二', '三', '四', '五', '六']; return weekdays[date.getDay()]; };
date.getDay()
是Date
对象的一个方法,用于获取一周中某一天的索引。这个方法返回的值是一个整数,代表一周中的某一天。具体来说,返回值是一个从0
到6
的整数,分别对应一周的七天。
自定义日期单元格中的内容(状态指示+日期显示格式)
tileContent
是一个非常有用的属性,允许你自定义日历每个日期单元格中的内容。这个属性接收一个函数作为参数,你可以通过这个函数提供自定义的渲染逻辑来展示日期信息、事件、标记等内容。
tsx
<Calendar key={date.toString()} onChange={onChange} value={date} locale="zh-CN" tileContent={tileContent} formatShortWeekday={formatShortWeekday} />
这个就是上面函数的编写, 可以看下注释.
大概就是做了
- 格式化日期
- 比对MocK的数据日期的状态,
- 如果是completed, 就设置指示状态的背景颜色为
绿色
- 如果是missed, 就设置指示状态的背景颜色为
红色
- 如果是leave, 就设置指示状态的背景颜色为
黄色
- 比对当天的日期, 对当天的日期进行一个
背景颜色的高亮
最后将这些上面格式化之后的数据进行一个数据填入, 最后将这个dom结构进行return 返回出去
tsx
/** * 根据日期和视图类型为日历的每个瓷砖设置内容。 * * 这个函数在 `month` 视图中为每个日期的瓷砖返回自定义内容,包括日期数字和状态指示点。 * * @function * @param {{ date: Date, view: 'month' | 'year' | 'decade' | 'century' }} tileInfo - 包含日期和视图类型的信息对象。 * @returns {JSX.Element | null} 返回一个包含日期数字和状态指示点的 JSX 元素,或者在其他视图类型中返回 `null`。 * @example * // 在组件中使用示例 * const content = tileContent({ date, view }); * return <div>{content}</div>; */ const tileContent = ({ date, view }: { date: Date, view: string }): JSX.Element | null => { if (view === 'month') { const formattedDate = format(date, 'd', { locale: zhCN }); const dateString = format(date, 'yyyy-MM-dd'); let dotStyle = styles.transparentDot; // 进行日期状态的判断,然后分别给状态指示添加不同的css的背景颜色. if (data[dateString] === 'completed') { dotStyle = { ...styles.dot, backgroundColor: '#00ee00' }; } else if (data[dateString] === 'missed') { dotStyle = { ...styles.dot, backgroundColor: '#FF4500' }; } else if (data[dateString] === 'leave') { dotStyle = { ...styles.dot, backgroundColor: ' #FFD700' }; } // 当前日期的背景颜色进行一个高亮显示 // 判断是否为当天 // 获取当天 const currentDate = new Date(); const tileStyle = isSameDay(date, currentDate) ? { ...styles.customCalendarTile, ...styles.highlightedTile } : styles.customCalendarTile; return ( // 日期数字 <div className='flex flex-col items-center'> <div style={tileStyle} className='hover:bg-slate-100' > {formattedDate} </div> {/* 状态标识 */} <div style={dotStyle}></div> </div> ); } return null; };
日历的折叠/展开
这里先说下思路
通过在日历组件
外面套一侧DIV
, 分别为它创建两个类名
- 一个设置高为
80px
[正好显示一行的高度]- 一个设置高为
500px
[全部显示]通过点击动态添加类名,即可Ok
tsx
const styles: { [key: string]: React.CSSProperties } = { calendarContainer: { overflow: 'hidden', transition: 'max-height 0.3s ease-out', }, calendarContainerExpanded: { maxHeight: '500px', }, calendarContainerCollapsed: { maxHeight: '70px', // 只显示头部和第一排日期的高度 } };
tsx
const [collapsed, setCollapsed] = useState<boolean>(false); const toggleCollapse = () => { setCollapsed(!collapsed); }; <div style={{ ...styles.calendarContainer, ...(collapsed ? styles.calendarContainerCollapsed : styles.calendarContainerExpanded) }}> <Calendar key={date.toString()} onChange={onChange} value={date} className={`w-full h-auto ${ClockInCalendarStyle.customCalendar}`} // 添加自定义样式类 locale="zh-CN" tileContent={tileContent} formatShortWeekday={formatShortWeekday} /> </div> <button onClick={toggleCollapse} style={collapsed ? { ...styles.collapseButton, ...styles.collapseButtonHover } : styles.collapseButton} > {collapsed ? '⬆️ 展开' : '⬇️ 收起'} </button>
结语
以上就是我的方法,如果能对您有些帮助,希望可以点个赞,有任何问题,也欢迎进行交流!!!