FullCalendar Timeline View(v4)
The Scheduler add-on provides a new view called “timeline view” with a customizable horizontal time-axis and resources as rows.
1. 先安装fullcalendar和用到的插件
npm install --save @fullcalendar/core @fullcalendar/resource-timeline @fullcalendar/interaction
2.在使用时导入
import {Calendar} from '@fullcalendar/core'; import resourceTimelinePlugin from '@fullcalendar/resource-timeline'; import interactionPlugin from '@fullcalendar/interaction'; import '@fullcalendar/core/main.css'; import '@fullcalendar/timeline/main.css'; import '@fullcalendar/resource-timeline/main.css';
3. 初始化Calendar时,添加使用的插件
plugins: [interactionPlugin, resourceTimelinePlugin],
4.最终实现资源/事件的添加删除.
上代码.
1 import {Calendar} from '@fullcalendar/core'; 2 import resourceTimelinePlugin from '@fullcalendar/resource-timeline'; 3 import interactionPlugin from '@fullcalendar/interaction'; 4 import '@fullcalendar/core/main.css'; 5 import '@fullcalendar/timeline/main.css'; 6 import '@fullcalendar/resource-timeline/main.css'; 7 8 // import zh_cn from '@fullcalendar/core/locales/zh-cn'; 9 let option = { 10 schedulerLicenseKey: 'GPL-My-Project-Is-Open-Source', 11 plugins: [interactionPlugin, resourceTimelinePlugin], 12 defaultView: 'resourceTimeline', 13 now: '2019-03-07', 14 // locale: zh_cn, 15 selectable: true, 16 selectHelper: true, 17 editable: true, // enable draggable events 18 eventResourceEditable: false, 19 aspectRatio: 1, 20 // height: 440, 21 contentHeight: 440, 22 resourceAreaWidth: '120px', 23 eventOverlap: false, 24 selectOverlap: false, 25 eventTextColor: '#fff', 26 displayEventTime: true, 27 displayEventEnd: true, 28 slotLabelFormat: { 29 hour: 'numeric', 30 minute: '2-digit', 31 omitZeroMinute: true, 32 meridiem: 'short', 33 hour12: false, 34 }, 35 eventTimeFormat: { 36 hour: 'numeric', 37 minute: '2-digit', 38 meridiem: 'narrow', 39 hour12: false, 40 }, 41 header: { 42 left: '', 43 center: '', 44 right: '', 45 }, 46 resourceLabelText: '姓名', 47 resources: [], 48 events: [], 49 }; 50 /** 51 * {Object} option , onSelect: function onEventClick: function , 52 */ 53 54 class Timeline { 55 constructor(el, opt = {}, callBack = () => {}) { 56 this.callBack = callBack; 57 console.log('timeline -init'); 58 this._option = Object.assign( 59 { 60 select: info => this.select(info), 61 dateClick: info => this.dateClick(info), 62 eventClick: info => this.eventClick(info), 63 eventMouseEnter: info => this.eventMouseEnter(info), 64 eventMouseLeave: info => this.eventMouseLeave(info), 65 eventResize: info => this.eventResize(info), 66 eventDrop: info => this.eventDrop(info), 67 resourceRender: info => this.resourceRender(info), 68 eventRender: info => this.eventRender(info), 69 eventDestroy: info => this.eventDestroy(info), 70 }, 71 option, 72 opt 73 ); 74 console.log('timeline-option==>>', this._option); 75 this.calendar = new Calendar(el, this._option); 76 this.render(); 77 78 let currentDate = new Date(this._option.now); 79 let end = new Date().setDate(currentDate.getDate()); 80 if (currentDate.getHours() !== 0) { 81 end = new Date().setDate(currentDate.getDate() + 1); 82 } 83 console.table('start, end', currentDate, new Date(end)); 84 this.setOption('visibleRange', { 85 start: currentDate, 86 end: end, 87 }); 88 } 89 90 /** 91 * @param {Object} value 92 */ 93 setOption(key, value) { 94 this.calendar.setOption(key, value); 95 this._option[key] = value; 96 } 97 98 // methods 99 render() { 100 this.calendar.render(); 101 } 102 addResource(resource) { 103 if (!resource) { 104 return; 105 } 106 this.calendar.addResource(resource); 107 } 108 removeResource(resource, e) { 109 if (!this._option.editable) { 110 return; 111 } 112 this._option.onRemoveResource && this._option.onRemoveResource(resource); 113 let events = resource.getEvents(); 114 events.forEach(event => { 115 event.remove(); 116 }); 117 resource.remove(); 118 this.getResult(); 119 120 e.target.removeEventListener('click', this.removeResource); 121 } 122 addEvent(event) { 123 if (!event) { 124 return; 125 } 126 let tmp = this.calendar.getEventById(event.id); 127 if (tmp) { 128 for (let key in event) { 129 if (tmp.extendedProps[key]) { 130 tmp.setExtendedProp(key, event[key]); 131 continue; 132 } 133 if (tmp[key]) { 134 tmp.setProp(key, event[key]); 135 } 136 } 137 } else { 138 this.calendar.addEvent(event); 139 console.log('addd', event); 140 } 141 } 142 removeEvent(eventId) { 143 let event = this.calendar.getEventById(eventId); 144 if (event) { 145 event.remove(); 146 this.getResult(); 147 } 148 } 149 150 destroy() { 151 this.calendar.destroy(); 152 console.log('timeline destroy >>>>>>>'); 153 } 154 getResult() { 155 let resources = this.calendar.getResources(); 156 let result = []; 157 resources.map(item => { 158 let tmp = { 159 resource: item, 160 events: item.getEvents(), 161 }; 162 result.push(tmp); 163 }); 164 165 this.callBack && this.callBack(result); 166 } 167 isValid(event) { 168 let now = this._option.now; 169 let start = new Date(event.start).getTime(); 170 let end = new Date(event.end).getTime(); 171 let startH = new Date(now).getHours(); 172 let startD = new Date(now).getDate(); 173 let crossDate = new Date(now); 174 crossDate.setDate(startD); 175 crossDate.setHours(23); 176 let endPoint = crossDate.getTime(); 177 if (startH !== 0) { 178 crossDate.setDate(startD + 1); 179 crossDate.setHours(startH); 180 endPoint = crossDate.getTime(); 181 } 182 if (start < now || end < now || start > endPoint || end > endPoint) { 183 return false; 184 } 185 return true; 186 } 187 /** 188 callbacks 189 */ 190 select(info) { 191 if (!this.isValid({start: info.start, end: info.end})) { 192 // info.revert(); 193 return; 194 } 195 this._option.onSelect && this._option.onSelect(info); 196 } 197 dateClick(arg) { 198 console.log('dateClick', arg.date, arg.resource ? arg.resource.id : '(no resource)'); 199 } 200 eventClick(info) { 201 this._option.onEventClick && this._option.onEventClick(info); 202 } 203 eventMouseEnter(info) { 204 this._option.onEventMouseEnter && this._option.onEventMouseEnter(info); 205 } 206 eventMouseLeave(info) { 207 this._option.onEventMouseLeave && this._option.onEventMouseLeave(info); 208 } 209 eventResize(info) { 210 if (!this.isValid(info.event)) { 211 info.revert(); 212 } 213 // this.getResult(); 214 } 215 eventDrop(info) { 216 if (!this.isValid(info.event)) { 217 info.revert(); 218 } 219 // this.getResult(); 220 } 221 resourceRender(info) { 222 let dom = info.el; 223 dom.style = dom.style + ';position:relative;'; 224 let close = document.createElement('i'); 225 close.classList.add('iconfont', 'icon-c'); 226 close.style = 'position:absolute;right:10px;top:50%;transform:translateY(-50%);font-size: 10px;'; 227 close.addEventListener('click', e => this.removeResource(info.resource, e)); 228 dom.appendChild(close); 229 } 230 eventRender(info) { 231 this.getResult(); 232 } 233 234 eventDestroy(info) { 235 // this.getResult(); 236 // console.log('eventDestroy', info); 237 } 238 } 239 240 export default Timeline;
使用(示例)
let timelineView = new Timeline( document.querySelector('#time-line-day'), { now: new Date(), onSelect: info => { let event = { id: this.eventId, title: this.eventId, resourceId: info.resource.id, start: info.start, end: info.end, }; timelineView.addEvent(event); }, onRemoveResource: info => { }, onEventClick: info => { let event = { id: info.event.id, title: info.event.title, resourceId: info.event.getResources()[0].id, start: info.event.start, end: info.event.end, }; let resourceId = info.event.getResources()[0].id; }, }, result => { } );
FullCalendar插件 plug-index