当前位置: 首页 > news >正文

根据fullcalendar实现企业微信的拖动式预约会议

先上效果图

这边是根据日历从当天开始,生成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])//切换日期和筛选<ProFormtitle={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}><ProFormDatePickerWeekname="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}  />

http://www.xdnf.cn/news/1439893.html

相关文章:

  • Linux 用户的 Windows 改造之旅
  • Web端最强中继器表格元件库来了!55页高保真交互案例,Axure 9/10/11通用
  • 使用langgraph创建工作流系列3:增加记忆
  • 100种高级数据结构 (速查表)
  • 【NVIDIA B200】1.alltoall_perf 单机性能深度分析:基于 alltoall_perf 测试数据
  • 如何评价2025年数学建模国赛?
  • Debezium系列之:Flink SQL消费Debezium数据,只消费新增数据,过滤掉更新、删除数据
  • 计算机毕业设计选题推荐:基于Python+Django的新能源汽车数据分析系统
  • AI随笔番外 · 猫猫狐狐的尾巴式技术分享
  • Networking Concepts
  • 超越马力欧:如何为经典2D平台游戏注入全新灵魂
  • vue 手动书写步骤条
  • 用Blender制作Rat Rod风格汽车
  • MySQL 8.0.40 主从复制完整实验总结(基础搭建 + 进阶延时同步与误操作恢复)
  • 智能电视小米电视浏览器兼容性踩坑电视黑屏或者电视白屏,Vue项目从Axios到Fetch的避坑指南
  • GitHub每日最火火火项目(9.3)
  • 演员-评论员算法有何优点?
  • 《探索C++11:现代语法的性能优化策略(中篇)》
  • 从公共形象到专属定制,井云交互数字人满足金融/政务多元需求
  • etcd对比redis
  • MySQL--CRUD
  • Oracle 10g 安装教程(详解,从exe安装到数据库配置,附安装包)​
  • 食物分类案例优化改进 (数据增强,最优模型保存和使用)
  • oracle 从一张表更新到另外一张表的方法(MERGE)
  • IO进程线程;进程,发送信号;进程,消息队列通信;0903
  • 如何利用SMS、RDS把服务从阿里云迁移到华为云
  • FastGPT社区版大语言模型知识库、Agent开源项目推荐
  • 矿山 6KV 不接地系统中的绝缘监测解决方案
  • 简述 Java 的异常体系结构。Error 和 Exception 有什么区别?
  • 小米fastboot不能被电脑识别但手机正常使用模式时能被电脑识别