FullCalendar v4.1.0版本
效果图(周)
以下是部分代码,看懂逻辑即可:
// package.json 引入
"dependencies": {
"@fullcalendar/core": "^4.1.0",
"@fullcalendar/daygrid": "^4.1.0",
"@fullcalendar/interaction": "^4.1.0",
"@fullcalendar/timegrid": "^4.1.0",
"@fullcalendar/vue": "^4.1.0"
}
IE10需要单独引入此方法:
Intl.complete.js 来源: https://github.com/andyearnshaw/Intl.js/blob/master/dist/Intl.complete.js
<!-- 日历组件支持IE10 -->
<script type="text/javascript" src="./vendor/Intl.complete.js"></script>
<script>window.Intl = window.IntlPolyfill</script>
<!-- popper 弹窗库 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/jquery.webui-popover/1.2.18/jquery.webui-popover.min.css">
<script src="//cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/jquery.webui-popover/1.2.18/jquery.webui-popover.min.js"></script>
对应页面的初始化代码。
<template>
<div class="calendar-container">
<FullCalendar
ref="calendar"
v-loading="loading"
class="calendar"
:style="{ width: calendarOptions.width + 'px' }"
:locale="calendarOptions.locale"
:default-view="calendarOptions.initialView"
:buttonText="calendarOptions.buttonText"
:header="calendarOptions.headerToolbar"
:plugins="calendarOptions.plugins"
:all-day-slot="calendarOptions.allDaySlot"
:height="calendarOptions.height"
:custom-buttons="calendarOptions.customButtons"
:slot-duration="calendarOptions.slotDuration"
:slot-label-interval="calendarOptions.slotLabelInterval"
:slot-min-time="calendarOptions.slotMinTime"
:slot-max-time="calendarOptions.slotMaxTime"
:scroll-time="calendarOptions.scrollTime"
:slot-label-format="calendarOptions.slotLabelFormat"
:event-time-format="calendarOptions.eventTimeFormat"
:events="getCalendarEvents"
@datesRender="datesRender"
@eventMouseEnter="eventMouseEnter"
/>
<popper v-show="false" :data-item="popperData"></popper>
</div>
</template>
<script>
// See https://fullcalendar.io/docs#toc version: v4!
import FullCalendar from '@fullcalendar/vue'
import dayGridPlugin from '@fullcalendar/daygrid'
import timeGridPlugin from '@fullcalendar/timegrid'
import interactionPlugin from '@fullcalendar/interaction'
import cnLocale from '@fullcalendar/core/locales/zh-cn'
import Popper from './Popper.vue'
import { mapGetters } from 'vuex'
import defaultSettings from '@/settings.js'
export default {
name: 'Calendar',
components: {
FullCalendar,
Popper
},
props: {
loading: {
type: Boolean,
required: true,
default: () => false
}
},
computed: {
...mapGetters({
calendarData: 'teacher/calendarData',
})
},
watch: {
calendarData: {
immediate: true,
handler(newVal) {
this.configCalendarData(newVal)
}
}
},
data() {
const _this = this
return {
calendarOptions: {
locale: cnLocale,
plugins: [dayGridPlugin, timeGridPlugin, interactionPlugin],
headerToolbar: { // 配置Header的操作按钮和位置
left: 'timeGridWeek,dayGridMonth',
center: 'prev title next today',
right: 'allCourseButton'
},
buttonText: {
today: '本周'
},
customButtons: {
allCourseButton: {
text: '查看所有课程',
click: function() {
_this.$router.push('/teacher/list')
}
}
},
height: 795, // 日历高度
slotLabelInterval: '00:15', // 时间间隔
slotDuration: '00:15:00', // 时间间隔
slotMinTime: '00:00',
slotMaxTime: '24:00',
scrollTime: '08:00', // 默认展示的时间
slotLabelFormat: { // 周时间的左侧时间轴配置
hour12: false,
hour: '2-digit',
minute: '2-digit'
},
eventTimeFormat: {
hour12: false,
hour: '2-digit',
minute: '2-digit'
},
allDaySlot: false,
events: this.getCalendarEvents,
eventMouseEnter: this.eventMouseEnter,
initialView: 'timeGridWeek' // 初始化时展示的类型
},
calendarEvents: [],
calendarApi: '', // 日历实例
popperData: {}, // 当前弹窗的数据
currentPopper: false, // 当前弹窗的实例
eventControl: false, // 用于事件节流
containerWidth: 800 // 用于判断弹出窗口朝左还是朝右
}
},
mounted() {
// 日历的实例
this.calendarApi = this.$refs.calendar.getApi()
// 获取页面宽度高度, 需要用到
const container = document.querySelector('.teacher-schedule-container')
this.calendarOptions.height = container.clientHeight
this.containerWidth = container.clientWidth
},
methods: {
datesRender(info) {
// 日历类型改变回调
if (info.view.type === 'dayGridMonth') {
$('.fc-today-button').text('本月')
} else if (info.view.type === 'timeGridWeek') {
$('.fc-today-button').text('本周')
}
},
eventMouseEnter(event) {
// 设置弹窗数据
this.$set(this, 'popperData', event.event.extendedProps)
const el = $(event.el)
const offset = event.el.getBoundingClientRect()
const position = offset.right
this.$nextTick(() => {
// 根据位置弹出汽泡
// See: https://github.com/sandywalker/webui-popover
el.webuiPopover({
// 如果位置太靠右就在左侧显示
placement: position + 300 > this.containerWidth ? 'left' : 'right',
container: document.body,
trigger: 'hover',
url: '#popper',
padding: false,
animation: 'pop',
cache: false,
onShow: () => {
// 隐藏原有Dom上的元素
$('.calendar-container .popper-container').hide()
try {
// 添加点击事件, 原Dom上的加不了, 只能这样加
$('body .popper-container .live-button').last().click(() => {
// 弹窗上的按钮点击了
})
} catch (e) {
// eslint-disable-next-line
}
},
onHide: () => {
el.webuiPopover('destroy')
}
})
el.webuiPopover('show')
})
},
getCalendarEvents(info, cbk) {
// 返回日历的事件集合
cbk(this.calendarEvents)
},
configCalendarData(datas) {
// 配置日历数据源
let events = []
for (let i = 0; i < datas.length; i++) {
const item = datas[i]
const color = item.Status === '已结束' ? '#adadad' : '#ff9c5a'
events.push({
id: item.HourGuid,
backgroundColor: color,
borderColor: color,
title: item.HourName,
extendedProps: item,
start: new Date(item.HourStart.replace(/-/g, '/')), // IE不支持 2019-01-01, 支持2019/01/01
end: new Date(item.HourEnd.replace(/-/g, '/'))
})
}
this.calendarEvents = events
this.calendarApi.refetchEvents()
}
}
}
</script>
<style type="text/css">
/* 气泡弹窗UI */
.webui-popover {
border: 0 !important;
box-shadow: 0 1px 10px rgba(0,0,0,.2) !important;
}
.webui-popover .webui-arrow {
border-right-color: #f0f0f0 !important;
border-left-color: #f0f0f0 !important;
}
</style>
<style lang="scss" scoped>
@import '~@fullcalendar/core/main.css';
@import '~@fullcalendar/daygrid/main.css';
@import '~@fullcalendar/timegrid/main.css';
.calendar-container {
width: calc(100vw - 80px);
min-width: 1120px;
// 头部操作按钮相关CSS
::v-deep .fc-header-toolbar {
display: flex;
.fc-button {
background-color: #fff;
border: 1px solid #dcdfe6;
color: #418aec;
font-size: 14px;
}
.fc-center {
display: flex;
}
.fc-button-active {
background-color: #418aec;
border: 1px solid #418aec;
color: #fff;
}
.fc-button:focus {
box-shadow: none;
}
.fc-timeGridWeek-button, .fc-dayGridMonth-button {
padding: 8px 30px;
}
.fc-allCourseButton-button {
border: 0;
background-color: #418aec;
color: #fff;
// font-size: 13px;
padding: 8px 18px;
}
.fc-today-button {
font-weight: bold;
border: 0;
padding-left: 20px;
}
}
// 周日历事件相关CSS
::v-deep .fc-time-grid {
.fc-title {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.fc-time-grid-event {
padding: 5px;
font-weight: bold;
}
}
::v-deep .fc-day-grid-container .fc-title {
position: absolute;
left: 45px;
right: 0;
text-overflow: ellipsis;
overflow: hidden;
}
// 修改非当月日期背景色为灰色
::v-deep .fc-other-month {
background-color: #f0f0f0;
}
// 修改当天在日历中的背景颜色
::v-deep .fc-today {
background-color: #fff;
}
}
</style>
Popper 组件内容:
<template>
<div id="popper" ref="popper" class="popper-container">
<div class="content">名称: {{ dataItem.Name }}</div>
<!-- 省略其他字段 -->
<el-button round class="live-button">可点击按钮</el-button>
</div>
</template>
<script>
export default {
name: 'Popper',
props: {
dataItem: {
type: Object,
required: false,
default: () => {}
}
}
}
</script>
<style lang="scss" scoped>
.popper-container {
background: #fff;
padding: 15px 20px;
font-size: 15px;
border-radius: 4px;
z-index: -1;
width: 200px !important;
text-align: center;
max-height: 300px !important;
overflow-y: auto;
display: none;
.content {
font-size: 14px;
line-height: 20px;
padding-bottom: 10px;
text-align: left;
}
.live-button {
border-color: #418aec;
background-color: #418aec;
margin-top: 10px;
margin-bottom: 5px;
width: 160px;
}
}
</style>
网友评论