实现效果如下图:
月视图


周视图

日视图

官方文档地址:Vue Component - Docs | FullCalendar
1、安装与FullCalendar相关的依赖项
| npm install --save @fullcalendar/vue @fullcalendar/core @fullcalendar/daygrid @fullcalendar/timegrid @fullcalendar/list |
| npm install --save @fullcalendar/interaction |
| npm install --save @fullcalendar/core @fullcalendar/resource-timeline |
复制
2.使用日历的页面需要导入
| import FullCalendar from "@fullcalendar/vue"; |
| import dayGridPlugin from "@fullcalendar/daygrid"; |
| import timeGridPlugin from "@fullcalendar/timegrid"; |
| import listPlugin from "@fullcalendar/list"; |
| import interactionPlugin from "@fullcalendar/interaction"; |
| |
复制
完整代码
| <template> |
| <div> |
| <div class="fc-toolbar"> |
| <div class="fc-left"> |
| <el-button-group> |
| <el-button @click="month"> 月 </el-button> |
| <el-button @click="week"> 周 </el-button> |
| <el-button @click="today"> 今天 </el-button> |
| </el-button-group> |
| </div> |
| <div class="fc-center"> |
| <el-button icon="el-icon-arrow-left" @click="prev"/> |
| <p class="title"> |
| {{ title }} |
| </p> |
| <el-button icon="el-icon-arrow-right" @click="next" /> |
| </div> |
| <div> |
| <el-select v-model="type" style="margin-right: 20px" @change="handleType"> |
| <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" /> |
| </el-select> |
| <el-button class="search" style="margin-right: 20px" type="button" @click="addEvent"> |
| 新增 |
| </el-button> |
| <el-button class="search" type="button" @click="search"> |
| 查询 |
| </el-button> |
| </div> |
| </div> |
| <el-dialog title="添加日程" :visible.sync="dialogFormVisible" width="600px"> |
| <el-form :model="form"> |
| <el-form-item label="事件"> |
| <el-input v-model="form.content" autocomplete="off" placeholder="请输入事件"></el-input> |
| </el-form-item> |
| </el-form> |
| <div slot="footer" class="dialog-footer"> |
| <el-button @click="dialogFormVisible = false">取 消</el-button> |
| <el-button type="primary" @click="navConfirm">确 定</el-button> |
| </div> |
| </el-dialog> |
| <div v-if="showFullcalendar">加载数据中......</div> |
| <FullCalendar v-else |
| id="calendar" |
| ref="fullCalendar" |
| class="demo-app-calendar" |
| :options="calendarOptions" |
| > |
| <template v-slot:eventContent="arg"> |
| <el-popover placement="top-start" title="标题" width="200" :visible-arrow="false" trigger="click"> |
| <i class="title">{{ arg.event.title }}</i> |
| <el-button @click="more(arg.event)"> 更多 </el-button> |
| <div slot="reference" class="popper-content"> |
| <span>{{ arg.timeText }}</span> |
| <i>{{ arg.event.title }}</i> |
| </div> |
| </el-popover> |
| </template> |
| <template v-slot:dayCellContent="arg"> |
| {{ arg.dayNumberText }} |
| </template> |
| <template v-slot:resourceLabelContent="arg"> |
| {{ arg.resource.id }} |
| </template> |
| </FullCalendar> |
| </div> |
| </template> |
| <script> |
| import FullCalendar from "@fullcalendar/vue"; |
| import dayGridPlugin from "@fullcalendar/daygrid"; |
| import timeGridPlugin from "@fullcalendar/timegrid"; |
| import listPlugin from "@fullcalendar/list"; |
| import interactionPlugin from "@fullcalendar/interaction"; |
| |
| |
| let clickCount = 0; |
| let prev = ""; |
| export default { |
| components: { |
| FullCalendar, |
| }, |
| data() { |
| return { |
| showFullcalendar: true, |
| title: "", |
| currentView: {}, |
| options: [ |
| { value: "timeline", label: "resource-timeline" }, |
| { value: "dategrid", label: "agenda" }, |
| ], |
| type: "dategrid", |
| calendarOptions: { |
| locale: "zh", |
| timeZone: "UTC", |
| plugins: [ |
| dayGridPlugin, |
| timeGridPlugin, |
| listPlugin, |
| |
| interactionPlugin, |
| ], |
| buttonText: { |
| |
| today: "今天", |
| month: "月", |
| week: "周", |
| dayGrid: "天", |
| }, |
| initialView: "dayGridMonth", |
| resourceAreaWidth: 200, |
| contentHeight: 600, |
| slotMinWidth: 70, |
| resourceOrder: "number", |
| editable: true, |
| dayMaxEvents: true, |
| eventDurationEditable: true, |
| selectable: true, |
| nowIndicator: true, |
| eventDisplay: "block", |
| headerToolbar: false, |
| selectMirror: false, |
| displayEventEnd: true, |
| eventTimeFormat: { |
| |
| hour: "2-digit", |
| minute: "2-digit", |
| meridiem: false, |
| hour12: false, |
| }, |
| events: [], |
| eventColor: "#378006", |
| allDayText: "全天", |
| dateClick: this.handleDateClick, |
| eventClick: this.handleEventClick, |
| select:this.handleDateSelect, |
| eventDrop:this.handleEventDrop, |
| eventResize:this.handleEventResize, |
| resourceAreaHeaderContent: "Rooms", |
| resources: [ |
| { |
| id: "111", |
| title: "asas", |
| number: 1, |
| }, |
| ], |
| schedulerLicenseKey: "GPL-My-Project-Is-Open-Source", |
| resourceLabelContent(arg) { |
| return { |
| html: `<div>id: ${arg.resource.id}</div><div>title: ${arg.resource.title}</div>`, |
| }; |
| }, |
| views: { |
| customTimeLineWeek: { |
| type: "resourceTimeline", |
| duration: { weeks: 1 }, |
| slotDuration: { days: 1 }, |
| buttonText: "Custom Week", |
| slotLabelFormat: { |
| weekday: "long", |
| |
| |
| omitCommas: true, |
| }, |
| }, |
| customTimeLineMonth: { |
| type: "resourceTimeline", |
| duration: { month: 1 }, |
| slotLabelFormat: { |
| |
| day: "numeric", |
| |
| }, |
| }, |
| customGridWeek: { |
| type: "timeGridWeek", |
| dayHeaderFormat: { |
| weekday: "long", |
| }, |
| slotLabelFormat: { |
| |
| hour: "2-digit", |
| minute: "2-digit", |
| meridiem: "lowercase", |
| hour12: false, |
| }, |
| }, |
| }, |
| |
| datesSet() { }, |
| }, |
| calendarApi: null, |
| monthEvent: [ |
| { |
| id: "1", |
| resourceId: "1", |
| title: "待办", |
| start: "2024-01-04 09:00", |
| end: "2024-01-04 18:00", |
| color: "red", |
| }, |
| { |
| resourceId: "2", |
| id: "2", |
| title: "待办", |
| start: "2024-01-15", |
| color: "purple", |
| }, |
| { title: "待办", start: "2024-01-09" }, |
| { title: "待办", start: "2024-01-17" }, |
| { title: "待办", start: "2024-01-07" }, |
| { title: "待办", start: "2024-01-07", color: "pink" }, |
| { title: "待办", start: "2024-01-07" }, |
| { title: "待办", start: "2024-01-07" }, |
| { |
| id: "3", |
| resourceId: "number_3", |
| title: "待办", |
| start: "20240111", |
| end: "20240113", |
| color: "blue", |
| extendedProps: { |
| description: "测试测试测试测试", |
| }, |
| }, |
| { |
| id: 4, |
| title: "待办", |
| start: "2024-01-15", |
| extendedProps: { |
| description: "test test test test test", |
| }, |
| }, |
| ], |
| weekEvent: [ |
| { |
| id: "4", |
| resourceId: "4", |
| title: "周待办", |
| start: "2024-01-11", |
| color: "red", |
| }, |
| { |
| id: "5", |
| resourceId: "5", |
| title: "待办1", |
| start: "2024-01-04 10:00", |
| end: "2024-01-04 18:00", |
| color: "orange", |
| }, |
| ], |
| dayDate:'', |
| dialogFormVisible:false, |
| form:{ |
| content:'', |
| }, |
| selectInfo:{}, |
| }; |
| }, |
| mounted() { |
| setTimeout(() => { |
| this.showFullcalendar = false; |
| this.$nextTick(() => { |
| this.calendarApi = this.$refs.fullCalendar.getApi(); |
| this.title = this.calendarApi.view.title; |
| this.getDtata(); |
| }); |
| }, 1000); |
| }, |
| watch: { |
| |
| "calendarApi.view.type"(newVal) { |
| this.getDtata(); |
| }, |
| }, |
| methods: { |
| |
| handleDateSelect(selectInfo) { |
| console.log('selectInfo: ', selectInfo); |
| this.selectInfo = selectInfo; |
| this.form.content = ''; |
| |
| this.dialogFormVisible = true; |
| }, |
| |
| handleEventDrop(dropInfo) { |
| console.log('dropInfo: ', dropInfo); |
| const updatedEvent = { ...dropInfo.event }; |
| updatedEvent.start = dropInfo.revertDuration ? dropInfo.oldEvent.start : dropInfo.event.start; |
| updatedEvent.end = dropInfo.event.end; |
| |
| |
| |
| |
| |
| const index = this.events.findIndex(e => e.id === updatedEvent.id); |
| if (index !== -1) { |
| this.events.splice(index, 1, updatedEvent); |
| } |
| }, |
| |
| handleEventResize(resizeInfo) { |
| console.log('resizeInfo: ', resizeInfo); |
| const updatedEvent = { ...resizeInfo.event }; |
| updatedEvent.end = resizeInfo.event.end; |
| |
| |
| |
| |
| const index = this.events.findIndex(e => e.id === updatedEvent.id); |
| if (index !== -1) { |
| this.events.splice(index, 1, updatedEvent); |
| } |
| }, |
| getDtata() { |
| setTimeout(() => { |
| this.calendarOptions.events = |
| this.calendarApi.view.type === "dayGridMonth" |
| ? this.monthEvent |
| : this.weekEvent; |
| }, 200); |
| }, |
| |
| more(e) { |
| console.log('more ', e) |
| }, |
| |
| navConfirm(){ |
| this.dialogFormVisible = false; |
| if (this.form.content) { |
| this.calendarOptions.events.push({ |
| title: this.form.content, |
| start: this.selectInfo.startStr, |
| end: this.selectInfo.endStr, |
| }); |
| |
| this.$refs.fullCalendar.getApi().addEvent({ |
| title: this.form.content, |
| start: this.selectInfo.startStr, |
| end: this.selectInfo.endStr, |
| }); |
| } |
| }, |
| |
| addEvent() { |
| this.form.content = ''; |
| this.dialogFormVisible = true; |
| |
| }, |
| |
| handleEventClick(clickInfo) { |
| console.log('clickInfo:', clickInfo); |
| |
| const event = clickInfo.event; |
| console.log('Clicked on:', event.title); |
| |
| }, |
| |
| handleDateClick(e) { |
| this.dayDate = e.dateStr; |
| if (e.dateStr !== prev) { |
| clickCount = 0; |
| } |
| clickCount += 1; |
| prev = e.dateStr; |
| setTimeout(() => { |
| if (clickCount === 2) { |
| console.log("db click"); |
| } else if (clickCount === 1) { |
| console.log("one click"); |
| } |
| clickCount = 0; |
| }, 300); |
| }, |
| |
| prev() { |
| this.calendarApi.prev(); |
| this.title = this.calendarApi.view.title; |
| }, |
| next() { |
| this.calendarApi.next(); |
| this.title = this.calendarApi.view.title; |
| }, |
| |
| today(date, jsEvent, view) { |
| |
| |
| |
| |
| |
| this.calendarApi.today(); |
| this.title = this.calendarApi.view.title; |
| |
| this.calendarApi.changeView("timeGridDay"); |
| |
| |
| }, |
| |
| month() { |
| if (this.type === "timeline") { |
| this.calendarApi.changeView("customTimeLineMonth"); |
| } else { |
| this.calendarApi.changeView("dayGridMonth"); |
| } |
| this.calendarApi.today(); |
| this.title = this.calendarApi.view.title; |
| }, |
| |
| week() { |
| if (this.type === "timeline") { |
| this.calendarApi.changeView("customTimeLineWeek"); |
| } else { |
| this.calendarApi.changeView("customGridWeek"); |
| } |
| this.calendarApi.today(); |
| this.title = this.calendarApi.view.title; |
| }, |
| |
| day() { |
| this.calendarApi.today(); |
| this.title = this.calendarApi.view.title; |
| }, |
| |
| search() { |
| this.calendarApi.changeView("dayGrid", { |
| start: "2022-07-07", |
| end: "2022-07-09", |
| }); |
| }, |
| |
| handleType() { |
| if (this.type === "timeline") { |
| this.calendarApi.changeView("customTimeLineMonth"); |
| this.calendarOptions.slotLabelFormat = null; |
| } else { |
| this.calendarApi.changeView("dayGridMonth"); |
| } |
| }, |
| }, |
| }; |
| </script> |
| <style scoped> |
| .demo-app { |
| display: flex; |
| min-height: 100%; |
| font-family: Arial, Helvetica Neue, Helvetica, sans-serif; |
| font-size: 14px; |
| } |
| .demo-app-sidebar { |
| width: 300px; |
| line-height: 1.5; |
| background: #eaf9ff; |
| border-right: 1px solid #d3e2e8; |
| } |
| .demo-app-sidebar-section { |
| padding: 2em; |
| } |
| .demo-app-main { |
| flex-grow: 1; |
| padding: 3em; |
| } |
| .fc { |
| |
| max-width: 1100px; |
| margin: 0 auto; |
| } |
| .fc-toolbar { |
| width: 100%; |
| margin: 30px auto; |
| display: flex; |
| flex: 1; |
| justify-content: space-around; |
| align-content: center; |
| } |
| .fc-center { |
| |
| display: flex; |
| align-content: center; |
| } |
| .fc-center .title { |
| font-size: 16px; |
| padding: 0 15px; |
| font-weight: 700; |
| |
| |
| } |
| </style> |
| |
| |
复制