先上效果图
这边是根据日历从当天开始,生成7个Calendar实例,并且根据数据数据进行回显,并且禁用当前时间段之前的操作,和在拖动后进行鼠标附近的modal展示,并且按照第几周来进行筛选和获取数据,直接上关键代码⬇️
import { Calendar } from '@fullcalendar/core';
import resourceTimelinePlugin from '@fullcalendar/resource-timeline';
import interactionPlugin from '@fullcalendar/interaction';
import ProFormDatePickerWeek from "@ant-design/pro-form/es/components/DatePicker/WeekPicker"
import dayjs from "dayjs"
const [modalPosition, setModalPosition] = useState({ top: 0, left: 0 });
const [modalVisible, setModalVisible] = useState(false);
const [filterTimeValue,setFilterTimeValue]=useState(dayjs())
const [currentWeek, setCurrentWeek] = useState(dayjs().weekday(1));
const [condition, setCondition] = useState([{
condition: {
field: MeetConfig.f_1744783951543,
op: "between",
value:[dayjs().startOf('week').valueOf(),dayjs().endOf('week').valueOf()],
},
nextop: "and",
},
{
condition: {
field: MeetConfig.f_1744783955010,
op: "between",
value:[dayjs().startOf('week').valueOf(),dayjs().endOf('week').valueOf()],
},
nextop: "and",
},])
const modalRef =useRef()
const getWeekDays = () => {
// 确保始终从周一开始计算
let startOfWeek = currentWeek;
if(startOfWeek.day()!==1){
startOfWeek= startOfWeek.day(1)
}
console.log(startOfWeek.format('YYYY-MM-DD'),startOfWeek.day(),'currentWeek');
// 验证是否为周一
// console.assert(startOfWeek.day() === 1, 'Start day should be Monday');
const days = [];
for (let i = 0; i < 7; i++) {
// if (isWorkday(startOfWeek.add(i, 'day'))) {
console.log(startOfWeek.add(i, 'day'),'startOfWeek.add');
days.push(startOfWeek.add(i, 'day'));
// }
}
return days;
};
// 导航函数
const prevWeek = () => {
const weekStart = dayjs(currentWeek.subtract(1, 'week')).startOf('week'); // 强制转为周一
const weekEnd = weekStart.add(6, 'day').endOf('day');
let submitCondition = condition.filter((item)=>item.condition.field!==MeetConfig.f_1744783951543&&item.condition.field!==MeetConfig.f_1744783955010)
.concat(
{
condition: {
field: MeetConfig.f_1744783951543,
op: "between",
value: [weekStart.valueOf(), weekEnd.valueOf()]
},
nextop: "and",
},
)
.concat(
{
condition: {
field: MeetConfig.f_1744783955010,
op: "between",
value: [weekStart.valueOf(), weekEnd.valueOf()]
},
nextop: "and",
},
)
.filter((item2) => !isEmptyValues(item2?.condition?.value))
setCondition(submitCondition)
setFilterTimeValue(currentWeek.subtract(1, 'week'))
setCurrentWeek(prev => prev.subtract(1, 'week'));
};
const nextWeek = () => {
console.log(dayjs(currentWeek.add(1, 'week')).format('YYYY-MM-DD'),'lkkl');
const weekStart = dayjs(currentWeek.add(1, 'week')).startOf('week'); // 强制转为周一
const weekEnd = weekStart.add(6, 'day').endOf('day');
let submitCondition = condition.filter((item)=>item.condition.field!==MeetConfig.f_1744783951543&&item.condition.field!==MeetConfig.f_1744783955010)
.concat(
{
condition: {
field: MeetConfig.f_1744783951543,
op: "between",
value: [weekStart.valueOf(), weekEnd.valueOf()]
},
nextop: "and",
},
)
.concat(
{
condition: {
field: MeetConfig.f_1744783955010,
op: "between",
value: [weekStart.valueOf(), weekEnd.valueOf()]
},
nextop: "and",
},
)
.filter((item2) => !isEmptyValues(item2?.condition?.value))
setCondition(submitCondition)
setFilterTimeValue(currentWeek.add(1, 'week'))
setCurrentWeek(prev => prev.add(1, 'week'));
};
const goToToday = () => {
const weekStart = dayjs().startOf('week'); // 强制转为周一
const weekEnd = weekStart.add(6, 'day').endOf('day');
let submitCondition = condition.filter((item)=>item.condition.field!==MeetConfig.f_1744783951543&&item.condition.field!==MeetConfig.f_1744783955010)
.concat(
{
condition: {
field: MeetConfig.f_1744783951543,
op: "between",
value: [weekStart.valueOf(), weekEnd.valueOf()]
},
nextop: "and",
},
)
.concat(
{
condition: {
field: MeetConfig.f_1744783955010,
op: "between",
value: [weekStart.valueOf(), weekEnd.valueOf()]
},
nextop: "and",
},
)
.filter((item2) => !isEmptyValues(item2?.condition?.value))
setCondition(submitCondition)
setCurrentWeek(dayjs());
setFilterTimeValue(dayjs().startOf('week'));
};
useEffect(() => {
function handleClickOutside(event) {
if (modalRef.current && !modalRef.current.contains(event.target)) {
setModalVisible(false)
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
useEffect(() => {
// 清空之前的日历实例
calendarApis.current.forEach(api => api && api.destroy());
calendarApis.current = [];
// 为每一天初始化日历
getWeekDays().forEach((day, index) => {
if (!calendarRefs.current[index]) return;
const calendar = new Calendar(calendarRefs.current[index], {
plugins: [resourceTimelinePlugin, interactionPlugin],
initialView: 'resourceTimelineDay',
initialDate: day.toDate(),
headerToolbar: false,
resourceAreaWidth: '0',
selectAllow: (selectInfo) => {
console.log(selectInfo,'selet');
const now = new Date(); // 当前时间
return selectInfo.start >= now; // 只允许选择当前或未来的时间
},
slotLabelFormat: {
hour: '2-digit',
minute: '2-digit',
hour12: false
},
timeline: {
slotHeight: 30 // 设置与CSS一致的行高
},
headerToolbar:false,
//设置时间间隔
slotDuration: '00:30:00',
slotMinTime: '08:00:00',
slotMaxTime: '18:00:00',
height: 'auto',
editable: false,
selectable: true,
selectMirror: true,
resources: resources,
events: events.filter(event =>dayjs(event.start).isSame(day, 'day')),
select:(selectInfo) => {
const newEvent = {
id: String(Math.random()),
start: selectInfo.start,
end: selectInfo.end,
resourceId: selectInfo.resource.id
};
if (hasTimeConflict(newEvent, events)) {
message.warning('该时间段已被占用');
calendar.unselect();
return;
}
// 计算点击位置(相对于日历容器)
const position = {
top: selectInfo.jsEvent.clientY - 50,
left: selectInfo.jsEvent.clientX>1600? selectInfo.jsEvent.clientX-500:selectInfo.jsEvent.clientX-200
};
setModalPosition(position);
setConfirmInfo(newEvent);
setModalVisible(true);
},
eventClick: (info) => {
setRecord({
[MeetConfig.f_1744783951543]: dayjs(info.event.start).valueOf(),
[MeetConfig.f_1744783955010]: dayjs(info.event.end).valueOf(),
[MeetConfig.f_1745485256233]: info.event.title,
[MeetConfig.f_1745485270386]: initialState?.currentUser?.name,
id: info.event.id
});
setOpen(true);
},
dateClick: (selectInfo) => {
console.log(selectInfo,22,'kkk');
const calendarEl = calendarRef.current;
if (dayjs(selectInfo?.date).valueOf()<dayjs().valueOf()) {
message.warning('该时间段已禁用')
return;
}
// 计算点击位置(相对于日历容器)
const position = {
top: selectInfo.jsEvent.clientY - 50,
left: selectInfo.jsEvent.clientX>1600? selectInfo.jsEvent.clientX-500:selectInfo.jsEvent.clientX-200
};
const newEvent = {
id: String(Math.random()),
start: selectInfo.date,
end:dayjs(selectInfo.date).add(30, 'minute'),
resourceId: selectInfo.resource.id
};
setConfirmInfo(newEvent)
setModalPosition(position);
setModalVisible(true);
},
});
calendar.render();
calendarApis.current[index] = calendar;
});
return () => {
calendarApis.current.forEach(api => api && api.destroy());
};
}, [events, resources, currentWeek]);
// 获取数据
const getList = async () => {
const res = await getFormData({
constant: MeetConfig,
filter_cond: { conditions: [
...condition,
] },
page: 1,
page_size: 999,
})
console.log("获取数据", res)
if (res.ret === 0) {
console.log(res?.msg?.data?.map((e)=>{
return {
...e,
id:e.id,
resourceId: 'room',
title: e?.[MeetConfig.f_1745485256233],
start: new Date(e?.['f_1744783951543']),
end: new Date(e?.['f_1744783955010']),
backgroundColor: '#3788d8'
}
}),'123');
setEvents(res?.msg?.data?.map((e)=>{
return {
...e,
id:e.id,
resourceId: 'room',
title: e?.[MeetConfig.f_1745485256233],
start: new Date(e?.['f_1744783951543']),
end: new Date(e?.['f_1744783955010']),
backgroundColor: '#3788d8'
}
}))
}
}
useEffect(() => {
getList()
}, [condition])
//切换日期和筛选
<ProForm
title={null}
layout="horizontal"
onReset={() => {
setCondition([])
}}
submitter={{
render: (props, doms) => {
return [
]
},
}}
onFinish={(values) => {
console.log(values,'123');
let submitCondition = condition.filter((e)=>e.condition.field!==MeetConfig.f_1744783951543&&e.condition.field!==MeetConfig.f_1744783955010)
.concat(
{
condition: {
field: MeetConfig.f_1744783951543,
op: "between",
value:[dayjs( values['time']).startOf('day').valueOf(),dayjs( values['time']).endOf('day').valueOf()],
},
nextop: "and",
},
)
.concat(
{
condition: {
field: MeetConfig.f_1744783955010,
op: "between",
value:[dayjs( values['time']).startOf('day').valueOf(),dayjs( values['time']).endOf('day').valueOf()],
},
nextop: "and",
},
)
.filter((item) => !isEmptyValues(item?.condition?.value))
setPage(1)
setCondition(submitCondition)
}}
>
<ProFormGroup size={8}>
<ProFormDatePickerWeek
name="time"
fieldProps={{
disabledDate: (current) => current && current < dayjs().startOf('day'),
// 显式设置周一开始(兼容不同地区设置)
value: filterTimeValue,
onChange: (e) => {
let date
if(e){
date = e
}else{
date = dayjs()
}
// 统一按周一开始计算(ISO标准周)
const weekStart = dayjs(date).startOf('week'); // 强制转为周一
const weekEnd = weekStart.add(6, 'day').endOf('day');
// 更新状态(保持同步)
setCurrentWeek(dayjs(date).startOf('week'));
setFilterTimeValue(date.startOf('week'));
let submitCondition = condition.filter((item)=>item.condition.field!==MeetConfig.f_1744783951543&&item.condition.field!==MeetConfig.f_1744783955010)
.concat(
{
condition: {
field: MeetConfig.f_1744783951543,
op: "between",
value: [weekStart.valueOf(), weekEnd.valueOf()]
},
nextop: "and",
},
)
.concat(
{
condition: {
field: MeetConfig.f_1744783955010,
op: "between",
value: [weekStart.valueOf(), weekEnd.valueOf()]
},
nextop: "and",
},
)
.filter((item2) => !isEmptyValues(item2?.condition?.value))
setCondition(submitCondition)
}
}}
allowClear
// 清除时重置逻辑
onClear={() => {
const now = dayjs();
setCurrentWeek(now.startOf('week').add(1, 'day'));
setFilterTimeValue(now);
setCondition([]);
}}
/>
<Col>
<Button.Group>
<Button icon={<LeftOutlined />} onClick={prevWeek} disabled={filterTimeValue<=dayjs()}/>
<Button onClick={goToToday}>本周</Button>
<Button icon={<RightOutlined />} onClick={nextWeek} />
</Button.Group>
</Col>
<Col>
</Col>
</ProFormGroup>
</ProForm>
{getWeekDays().map((day, index) => (
<div key={day.format('YYYY-MM-DD')}>
<div style={{marginBottom:2,marginTop:8,fontSize:14,fontWeight:700,display:'flex'}}>
<div style={{marginRight:10}}>{dayjs(day).format('YYYY-MM-DD')}</div>
<div>{dayjs(day).format('dddd')=='Monday'?'周一':dayjs(day).format('dddd')=='Tuesday'?'周二':dayjs(day).format('dddd')=='Wednesday'?'周三':dayjs(day).format('dddd')=='Thursday'?'周四':dayjs(day).format('dddd')=='Friday'?'周五':dayjs(day).format('dddd')=='Saturday'?'周六':dayjs(day).format('dddd')=='Sunday'?'周日':dayjs(day).format('dddd')||'周日'}</div>
</div>
<div
ref={el => calendarRefs.current[index] = el}
style={{ height: '500px' }}
/>
</div>
))}
{modalVisible && (
<div
ref={modalRef}
style={{
position: 'absolute',
top: `${modalPosition.top}px`,
left: `${modalPosition.left}px`,
zIndex: 1001,
background: 'white',
padding: '16px',
borderRadius: '4px',
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
minWidth: '300px'
}}
>
{/* <h4>选择时间段</h4>
<p>{clickedTime.date.toLocaleString()}</p>
<p>资源: {clickedTime.resourceId || '无'}</p> */}
<div style={{fontSize:20,fontWeight:700}}>会议室申请</div>
<div style={{display:'flex',fontSize:16,fontWeight:500}}>
<div style={{marginRight:10}}> {dayjs(confirmInfo?.start).format('YYYY-MM-DD')}</div>
<div style={{marginRight:10}}> {dayjs(confirmInfo?.start).format('dddd')}</div>
<div>{dayjs(confirmInfo?.start).format('HH:mm')+'-'+dayjs(confirmInfo?.end).format('HH:mm')}</div>
</div>
<div style={{ marginTop: '16px', display: 'flex', gap: '8px'}}>
<Button
type="primary"
style={{width:'100%'}}
onClick={() => {
// 创建新事件逻辑
}}
>
预定
</Button>
{/* <Button onClick={() => setModalVisible(false)}>
取消
</Button> */}
</div>
</div>
)}
如果想只创建一个实例代码,并且按照天筛选
//修改创建实例代码
useEffect(() => {
const calendar = new Calendar(calendarRef.current, {
....同上
}, [events, resources,condition]);
<div ref={calendarRef} />