使用步骤请参考:react使用Fullcalendar
卡片式的日历:
需求图:
卡片式的日历,其实我是推荐 antd的,我两个都写了一下都能实现。
antd 的代码:
antd的我直接用的官网示例:antd 日历示例
import React, { useEffect, useState, useRef } from 'react';
import { Calendar, Col, Radio, Row, Select, Typography } from 'antd';
import "./index.less"
import moment from 'moment';
const ProductFullcalendar = () => {
const [currentDate, setCurrentDate] = useState(moment()); //当前日期
const onPanelChange = (value, mode) => {
console.log(value.format('YYYY-MM-DD'), mode);
};
return (
<div className='vv'>
产品日历
<Calendar
fullscreen={false}
onPanelChange={onPanelChange}
dateFullCellRender={(current) => {
let currentDate1 = moment(current).format('YYYY-MM-DD');
let selectDate = currentDate.format('YYYY-MM-DD');
if (currentDate1 === selectDate) {
return <div className='dateCell selected'>
<div className='date_select'>{moment(current).format('DD')}</div>
<div className='event_select'></div>
</div>
} else {
return <div className='dateCell'>
<div className='date'>{moment(current).format('DD')}</div>
</div>
}
}}
onSelect={(date) => {
setCurrentDate(date);
}}
/>
</div>
)
};
export default ProductFullcalendar;
less:
.vv {
.ant-picker-cell {
color: rgba(0, 0, 0, 0.3);
}
.ant-picker-cell-in-view {
color: rgba(0, 0, 0, 0.87);
}
.selected {
background: #3D57B1;
box-shadow: 0px 2px 6px 0px rgba(33, 77, 208, 0.36);
border-radius: 8px;
.date_select {
font-family: Avenir, Avenir;
font-weight: 500;
font-size: 20px;
color: #FFFFFF;
}
.event_select{
width: 6px;
height: 6px;
background: #FFFFFF;
border-radius: 50%;
}
}
.dateCell {
width: 48px;
height: 55px;
border-radius: 8px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
.date {
font-family: Avenir, Avenir;
font-weight: 500;
font-size: 20px;
}
.event{
width: 6px;
height: 6px;
background: red;
border-radius: 50%;
}
}
}
@fullcalendar/react的代码:
它需要处理的东西很多,点击上个月的日期时,需要自己跳到上一个月。
import React, { useEffect, useState, useRef } from 'react';
import FullCalendar from '@fullcalendar/react'
import dayGridPlugin from '@fullcalendar/daygrid'
import interactionPlugin from "@fullcalendar/interaction"
import "./index.less"
const ProductFullcalendar = () => {
const [currentDate, setCurrentDate] = useState(); //当前日期
// 创建一个 ref 来存储 FullCalendar 的实例
const calendarRef = useRef(null);
const renderEventContent = (eventInfo) => {
return <div className='kk'></div>
}
const events = [
{ title: '', start: new Date(2024, 10, 1) }
]
const dayCellContent = (data) => {
console.log(data, currentDate, "908777");
let dayDate = data.dayNumberText.replace("日", "");
let isToday = data.isToday;
//是不是当月的日期
let isOther = data.isOther;
return <div className='dayCellContent'>
<div className={'dayDate'}>{dayDate}</div>
</div>
}
return (
<div className='vv'>
产品日历
<FullCalendar
ref={calendarRef}
plugins={[dayGridPlugin, interactionPlugin]}
initialView='dayGridMonth'
events={events}
eventContent={renderEventContent}
dayCellClassNames={"dayCell"}
locale='zh-cn'// 设置语言
selectable={true}
dateClick={(info) => {
document.querySelectorAll('.fc-day.selected').forEach(function (el) {
el.classList.remove('selected');
});
const clickedDate = info.date;
//console.log(calendarRef.current.getApi()?.getDate(),calendarRef,"987")
const currentViewDate = calendarRef?.current?.getApi()?.getDate(); // 获取当前视图的日期
// 判断是否为同一月份
if (clickedDate.getMonth() !== currentViewDate.getMonth() ||
clickedDate.getFullYear() !== currentViewDate.getFullYear()) {
// 如果不是当月日期,则切换到点击的月份
calendarRef?.current?.getApi()?.gotoDate(clickedDate);
}
// calendarRef?.current?.getApi()?.refetchEvents(); // 刷新事件
setTimeout(() => {
info.dayEl.classList.add('selected');
})
}}
dayHeaderClassNames={"dayHeader"}
dayHeaderContent={(arg) => {
// 自定义星期内容
const days = ['日', '一', '二', '三', '四', '五', '六']; // 星期的中文表示
return days[arg.date.getDay()]; // 获取对应星期的中文名称
}}
dayCellContent={(data) => {
return dayCellContent;
}}
/>
</div>
)
};
export default ProductFullcalendar;
less代码:
.vv {
--fc-border-color: none;
--fc-highlight-color: none;
--fc-today-bg-color: none;
//日历总高度 包括 toolBar 和日历内容
--fc-daygrid-height: 390px;
// 单元格内容宽度 我自定义的
// 单元格内容宽度 我自定义的
--fc-daygrid-day-frame-width: 48px;
// 单元格内容高度 我自定义的
--fc-daygrid-day-frame-height: 55px;
.fc-media-screen {
height: var(--fc-daygrid-height);
}
.fc-header-toolbar {
margin-bottom: 12px;
}
a {
color: initial;
}
.fc-daygrid-day-events {
min-height: 0 !important;
}
.dayCell {
--fc-border-color: none;
width: var(--fc-daygrid-day-frame-width);
.fc-daygrid-day-frame {
width: var(--fc-daygrid-day-frame-width);
height: var(--fc-daygrid-day-frame-height);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
margin: 0 auto;
}
}
.fc-theme-standard th {
border-bottom: 1px solid #F0F0F0;
}
}
.dayHeader {
font-weight: 400;
font-size: 14px;
color: rgba(0, 0, 0, 0.54) !important;
width: var(--fc-daygrid-day-frame-width);
}
.kk {
width: 6px;
height: 6px;
background: #FF7D7D;
border-radius: 50%;
}
.fc-day-other {
color: rgba(0, 0, 0, 0.3);
}
.fc-daygrid-day {
color: rgba(0, 0, 0, 0.87);
}
.dayCellContent {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
line-height: 20px;
.dayDate {
font-family: Avenir, Avenir;
font-weight: 800;
font-size: 20px;
}
.dayEvent {
margin-top: 2px;
.dayEventItem {
width: 6px;
height: 6px;
background: #4982F3;
border-radius: 50%;
}
}
}
.selected {
width: var(--fc-daygrid-day-frame-width);
.fc-daygrid-day-frame {
width: var(--fc-daygrid-day-frame-width);
height: var(--fc-daygrid-day-frame-height);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
margin: 0 auto;
background: #3D57B1;
box-shadow: 0px 2px 6px 0px rgba(33, 77, 208, 0.36);
.fc-daygrid-day-events {
.kk {
width: 6px;
height: 6px;
background: #FFFFFF;
border-radius: 50%;
}
}
.dayCellContent {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
.dayDate {
font-family: Avenir, Avenir;
font-weight: 800;
font-size: 20px;
color: #FFFFFF;
}
.dayEvent {
margin-top: 2px;
}
}
}
}
antd官网代码:
import { Calendar, Col, Radio, Row, Select, Typography } from 'antd';
import React from 'react';
const App = () => {
const onPanelChange = (value, mode) => {
console.log(value.format('YYYY-MM-DD'), mode);
};
return (
<div className="site-calendar-customize-header-wrapper">
<Calendar
fullscreen={false}
headerRender={({ value, type, onChange, onTypeChange }) => {
const start = 0;
const end = 12;
const monthOptions = [];
const current = value.clone();
const localeData = value.localeData();
const months = [];
for (let i = 0; i < 12; i++) {
current.month(i);
months.push(localeData.monthsShort(current));
}
for (let i = start; i < end; i++) {
monthOptions.push(
<Select.Option key={i} value={i} className="month-item">
{months[i]}
</Select.Option>,
);
}
const year = value.year();
const month = value.month();
const options = [];
for (let i = year - 10; i < year + 10; i += 1) {
options.push(
<Select.Option key={i} value={i} className="year-item">
{i}
</Select.Option>,
);
}
return (
<div
style={{
padding: 8,
}}
>
<Typography.Title level={4}>Custom header</Typography.Title>
<Row gutter={8}>
<Col>
<Radio.Group
size="small"
onChange={(e) => onTypeChange(e.target.value)}
value={type}
>
<Radio.Button value="month">Month</Radio.Button>
<Radio.Button value="year">Year</Radio.Button>
</Radio.Group>
</Col>
<Col>
<Select
size="small"
dropdownMatchSelectWidth={false}
className="my-year-select"
value={year}
onChange={(newYear) => {
const now = value.clone().year(newYear);
onChange(now);
}}
>
{options}
</Select>
</Col>
<Col>
<Select
size="small"
dropdownMatchSelectWidth={false}
value={month}
onChange={(newMonth) => {
const now = value.clone().month(newMonth);
onChange(now);
}}
>
{monthOptions}
</Select>
</Col>
</Row>
</div>
);
}}
onPanelChange={onPanelChange}
/>
</div>
);
};
export default App;
正常日历 antd 和 Fullcalendar 都行:
我的需求是:
我直接用的@fullcalendar/react 因为我的和它基本功能完全一致。具体怎么选看自己。
官网的demo效果: