设计需求
因为设计稿是这样的
像日历又不完全是日历,所以本来想用antd的日历组件也用不了
还好Fullcalendar插件可以满足
安装
npm install --save @fullcalendar/react @fullcalendar/daygrid @fullcalendar/timegrid @fullcalendar/interaction
不说太多上代码
具体看注释
import React, { useState, useRef, useMemo } from 'react';
import { Space, Row, Divider, message } from 'antd';
import FullCalendar from '@fullcalendar/react'; // must go before plugins
import dayGridPlugin from '@fullcalendar/daygrid'; // a plugin!
import timeGridPlugin from '@fullcalendar/timegrid';
import interactionPlugin from '@fullcalendar/interaction'; // needed for dayClick
import dayjs from 'dayjs';
import moment from 'moment';
import { uniqueId } from 'lodash';
import '@fullcalendar/daygrid/main.css';
import '@fullcalendar/timegrid/main.css';
import '../styles/dialTimeRange.scss';
const week = [ '周日', '周一', '周二', '周三', '周四', '周五', '周六' ];
// 拨打时间范围弹窗
const DialTimeRange = ({ timeList = [], ...props }) => {
const [ eventList, setEventList ] = useState(useMemo(() => timeList, [timeList]));
const calendarComponentRef = useRef();
// render header
// 每列的头部渲染
const columnHeaderHtml = (date) => {
const day = dayjs(date.date).day(); // 获取周几
return <span>{week[day]}</span>;
};
// 判断时间是否重复
const getRepeatDates = (start, end, eventsList) => {
let repeats =
eventsList &&
eventsList.filter((x) => {
return (
(x.start < start && x.end > start) ||
(x.start > start && x.end <= end) ||
(x.start < end && x.end >= end)
);
});
return repeats;
};
const weekList = () => {
const weekTimeList = [ {} ];
for (let i = 0; i < 7; i++) {
weekTimeList.push({
start: dayjs(`2021-08-${16 + i} 09:00:00`).format(),
end: dayjs(`2021-08-${16 + i} 20:00:00`).format()
});
}
return weekTimeList;
};
// 将跨天的事件转化成单天的事件
const handleTransformEventList = (eStart, eEnd, startWeek, endWeek) => {
const weeklist = weekList();
// 开始时间不是09:00
eventList.push({
...weeklist[startWeek],
start: eStart,
id: uniqueId(),
weekRange: startWeek,
startTimeStr: dayjs(eStart).format('HH:mm'),
endTimeStr: dayjs(weeklist[startWeek]['end']).format('HH:mm')
});
if (endWeek - startWeek >= 2) {
// 比如框选了周一某点到周三某点
// 这是中间肯定被选了整天,所以用09:00~20:00 即可
for (let i = startWeek + 1; i <= endWeek - startWeek; i++) {
eventList.push({
...weeklist[i],
id: uniqueId(),
weekRange: i,
startTimeStr: dayjs(weeklist[startWeek]['start']).format('HH:mm'),
endTimeStr: dayjs(weeklist[startWeek]['end']).format('HH:mm')
});
}
}
// 结束时间不是20:00
eventList.push({
...weeklist[endWeek],
end: eEnd,
id: uniqueId(),
weekRange: endWeek,
startTimeStr: dayjs(weeklist[startWeek]['start']).format('HH:mm'),
endTimeStr: dayjs(eEnd).format('HH:mm')
});
return eventList;
};
// 选择
const handleSelect = (event) => {
const eStart = event.start;
const eEnd = event.end;
// 校验时间是否重叠
let repeats = getRepeatDates(eStart, eEnd, eventList);
if (repeats.length > 0) {
calendarComponentRef.current._calendarApi.unselect(); // 清除选择的重复时间段
message.error('选择的时间段中已存在重复时间段');
return;
}
const startWeek = dayjs(eStart).day();
const endWeek = dayjs(eEnd).day() || 7; // 后面都是把7当做周日,而不是0
if (startWeek === endWeek) {
// 说明选的是同一天的
eventList.push({
id: uniqueId(),
start: eStart,
end: eEnd,
weekRange: startWeek,
startWeek,
endWeek,
startTimeStr: dayjs(eStart).format('HH:mm'),
endTimeStr: dayjs(eEnd).format('HH:mm')
// display: 'background', // 添加了这个就不能获取事件点击事件
// color: '#466DFF'
});
} else {
// 跨天了
handleTransformEventList(eStart, eEnd, startWeek, endWeek);
}
// }
// console.log('handleSelect', eventList);
setEventList([...eventList]);
};
// 事件点击
const handleventClick = (arg) => {
let id = arg.event.id;
// let data = arg.event.extendedProps; // 可以查看自定义数据
const idIndex = eventList.findIndex((ev) => ev.id === id);
eventList.splice(idIndex, 1); //删除已选择的事件
setEventList([ ...eventList ]);
// console.log('handleventClick', arg)
};
const handleUnSelect = (event) => {
console.log({ event });
};
// console.log({ eventList, setEventList });
return (
<FullCalendar
ref={calendarComponentRef}
plugins={[ dayGridPlugin, timeGridPlugin, interactionPlugin ]}
initialView="timeGridWeek" // 默认为哪个视图(月:dayGridMonth,周:timeGridWeek,日:timeGridDay)
locale="zh-cn"
firstDay={1} // 设置周一为第一天
headerToolbar={false} // 设置日历头部按钮
// editable={true}
slotLabelFormat={{
hour: '2-digit',
minute: '2-digit',
meridiem: true,
hour12: false // 设置时间为24小时
}}
initialDate={moment('2021-08-16').format('YYYY-MM-DD')} // 自定义设置背景颜色时一定要初始化日期时间
selectable={true} // 是否可选择
allDaySlot={false} // 周,日视图时,all-day 不显示
aspectRatio={1.6} // 设置日历单元格宽度与高度的比例。
select={handleSelect} // 选中日历格事件
unselect={handleUnSelect} // 不选触发的事件
eventClick={handleventClick} // 点击日历日程事件
events={eventList} // 事件数据
dayHeaderContent={columnHeaderHtml} // 每天的头部内容
slotMinTime="09:00:00" // 左边的时间最小从几点展示
slotMaxTime="20:00:00" // 左边的时间最大展示到几点
// eventContent={eventRender} // 时间的内容渲染
/>
);
};
export default DialTimeRange;