fullcalendar_会议预约
项目中已经包含 ant-desing-vue 组件库和css预处理库scss配置
安装依赖到Vue项目中
yarn add @fullcalendar/vue
yarn add @fullcalendar/core @fullcalendar/interaction @fullcalendar/timegrid
yarn add tippy.js
应用组件
<template>
<div class="meeting">
<a-spin :spinning="spinningLoading">
<FullCalendar ref="myCalendar" :options="calendarOptions" />
</a-spin>
</div>
</template>
<script>
import FullCalendar from '@fullcalendar/vue'
import timeGridPlugin from '@fullcalendar/timegrid'
import interactionPlugin from '@fullcalendar/interaction'
import moment from 'moment'
import tippy from 'tippy.js'
import 'tippy.js/dist/tippy.css'
import 'tippy.js/themes/light.css'
function getWeekDate() {
const start_week = moment().startOf('week')
const end_week = moment().endOf('week')
let start = moment(start_week.add(1, 'days')).format('YYYY-MM-DD')
let end = moment(end_week.add(1, 'days')).format('YYYY-MM-DD')
return { start, end }
}
function formatDate(params) {
if (!params) return '格式化日期不能为空'
let date = moment(params).format('YYYY-MM-DD')
date = date.replace('星期', '周')
let vNowDate = moment(new moment(date).format('YYYY-MM-DD'))
let vWeekOfDay = moment(vNowDate).format('E') //算出这周的周几
let weekList = ['', '周一', '周二', '周三', '周四', '周五', '周六', '周日']
let result = `${date}(${weekList[vWeekOfDay]})`
return result
}
export default {
name: 'CalendarDemo',
components: {
FullCalendar
// addEdit
},
data() {
return {
spinningLoading: false,
calendarOptions: {
// listPlugin
// 引入的插件,比如fullcalendar/daygrid,fullcalendar/timegrid引入后才可显示月,周,日
plugins: [interactionPlugin, timeGridPlugin],
initialView: 'timeGridWeek', // 默认为那个视图(月:dayGridMonth 周:timeGridWeek 日:timeGridDay)
firstDay: 1, // 设置一周中显示的第一天是哪天,周日是0,周一是1,类推
locale: 'zh-cn', // 切换语言,当前为中文
eventColor: '#3BB2E3', // 全部日历日程背景色
themeSystem: 'bootstrap', // 主题色(本地测试未能生效)
initialDate: moment().format('YYYY-MM-DD'), // 自定义设置背景颜色时一定要初始化日期时间
timeGridEventMinHeight: '20', // 设置事件的最小高度
// height: 800, //设置日历的高度,包括header日历头部,默认未设置,高度根据aspectRatio值自适应。
// contentHeight: 820, //设置日历主体内容的高度,不包括header部分,默认未设置,高度根据aspectRatio值自适应。
aspectRatio: 2.2, //设置日历单元格宽度与高度的比例。
// displayEventTime: false, // 是否显示时间
allDaySlot: false, // 周,日视图时,all-day 不显示
eventLimit: true, // 设置月日程,与all-day slot的最大显示数量,超过的通过弹窗显示
handleWindowResize: true, //是否随浏览器窗口大小变化而自动变化
nowIndicator: false, // 是否显示当前时间轴
headerToolbar: {
// 日历头部按钮位置
left: '',
// center: 'prevYear,prev title next,nextYear',
right: ''
// right: 'today dayGridMonth,timeGridWeek,timeGridDay'
},
buttonText: {
today: '今天',
month: '月',
week: '周',
day: '日'
},
slotLabelFormat: {
hour: 'numeric',
minute: '2-digit',
meridiem: false,
hour12: false // 设置时间为24小时
},
// 不可预约背幕设置
// businessHours: [
// {
// daysOfWeek: [1],
// startTime: '12:00', // 可接受字符串或处理函数
// endTime: '13:00'
// }
// ],
eventLimitNum: {
// 事件显示数量限制(本地测试未能生效)
dayGrid: {
eventLimit: 5
},
timeGrid: {
eventLimit: 2
}
},
//视图数据加载中、加载完成触发
loading: this.loadingHandle,
views: {
//对应周视图调整
timeGridWeek: {
slotMinTime: '08:00', //周视图开始时间
slotMaxTime: '21:00', //周视图结束时间
displayEventTime: false, //是否显示时间
dayHeaderContent(item) {
let _date = formatDate(item.date)
return {
html: `<div>${_date}</div>`
}
}
}
},
// 日程事件 json
events: [
{
title: 'xxx会议1',
date: '2023-03-16 12:00',
date: '2023-03-16 13:00',
color: '#05818A',
borderColor: '#05818A',
textColor: '#ffffff',
id: '2c69cbc273ca263119714a85320e75c9',
extendedProps: {
date: '2023-03-17',
startTime: '12:00',
endTime: '13:00'
}
},
{
title: 'xxxx会议',
start: '2023-03-16 08:23',
end: '2023-03-16 09:10',
editable: false,
id: 'hjhgkjgp[agapoigadgidfihdi',
color: '#05818A',
borderColor: '#05818A',
textColor: '#ffffff',
extendedProps: {
date: '2023-03-15',
startTime: '08:23',
endTime: '09:10'
}
}
],
// 事件
dateClick: this.handleDateClick, // 点击日历空白处
eventClick: this.handleEventClick, // 点击日历日程事件
eventMouseEnter: this.handleEventMouseEnter, // 鼠标移入
eventMouseLeave: this.handleEventMouseLeave, // 鼠标移出
allowContextMenu: false,
editable: true, // 是否可以进行(拖动、缩放)修改
eventStartEditable: true, // Event日程开始时间可以改变,默认true,如果是false其实就是指日程块不能随意拖动,只能上下拉伸改变他的endTime
eventDurationEditable: true, // Event日程的开始结束时间距离是否可以改变,默认true,如果是false则表示开始结束时间范围不能拉伸,只能拖拽
selectable: false, // 是否可以选中日历格
selectMirror: true,
selectMinDistance: 0, // 选中日历格的最小距离
dayMaxEvents: true,
weekends: true,
navLinks: false, // 表头下钻链接
selectHelper: false,
slotEventOverlap: false, // 相同时间段的多个日程视觉上是否允许重叠,默认true允许
eventContent: this.renderEverntContent
}
}
},
mounted() {
console.log('getWeekDate() :>> ', getWeekDate())
},
methods: {
// 获取预约会议内容
loadDataHandle() {
let params = {
...getWeekDate()
}
this.spinningLoading = true
getAction('', params).then((res) => {
if (res.success) {
this.calendarOptions.events = res.result
this.spinningLoading = false
}
})
},
// 点击已预约会议
handleEventClick(eventSource) {
let data = eventSource.event._def
let id = data.publicId
this.$refs.addEdit.edit({ id: id })
},
// 空白的日期区,单击时触发
handleDateClick(info) {
let calendarApi = this.$refs['myCalendar'].getApi()
console.log('calendarApi :>> ', calendarApi)
let date = moment(info.dateStr).format('YYYY-MM-DD')
// TODO:实现新增预约会议弹框
},
// 自定义日程渲染函数
renderEverntContent(arg) {
let italicEl = document.createElement('div')
let obj = arg.event._def
if (obj) {
let html = `
<div style="padding: 1px 6px">
<h4 style="padding:0;margin:0">${obj.title}</h4>
</div>
`
italicEl.innerHTML = html
}
let arrayOfDomNodes = [italicEl]
return { domNodes: arrayOfDomNodes }
},
// 鼠标移入
handleEventMouseEnter(mouseEnter) {
let { event, el, jsEvent, view } = mouseEnter
let obj = event._def
let htmlTemplate = `<div style='padding: 2px 6px;min-width: 260px'>
<h4 style='padding: 0;margin: 0;'>${obj.title}</h4>
<div>
<label>会议时间:</label>
<span>${obj.extendedProps.date} ${obj.extendedProps.startTime}~${obj.extendedProps.endTime}</span>
</div>
<div>
<label>会议地点:</label>
<span>
xxxxxxxx办公楼1楼105室
</span>
</div>
`
let options = {
theme: 'light', //主题选取
arrow: true,
interactive: true, //可交互的
placement: 'right', //悬浮框位置
allowHTML: true, //是否允许html文本
zIndex: 99999,
content: htmlTemplate
}
tippy(el, options)
},
// 鼠标移出
handleEventMouseLeave(mouseLeave) {
// let { event, el, jsEvent, view } = mouseLeave
// tippy(el, {
// // onCreate(instance) {
// // console.log('instance :>> ', instance)
// // instance.unmount()
// // },
// // onDestroy(instance) {
// // console.log('instance -=1:>> ', instance)
// // instance.destroy()
// // // instance.unmount()
// // }
// })
},
// 新增测试按钮
addTestHandle() {
let copyData = JSON.parse(JSON.stringify(this.calendarOptions.events))
let addData = [
{
title: '讨论会议',
editable: false,
start: moment().format('YYYY-MM-DD') + ' 08:23',
end: moment().format('YYYY-MM-DD') + ' 12:10',
id: 'gvashpksahkjo-ajhps]ajh]opjhe',
color: '#05818A',
borderColor: '#05818A',
textColor: '#ffffff',
extendedProps: {
date: '2023-03-13',
startTime: '08:23',
endTime: '12:10'
}
}
]
this.calendarOptions.events = [...copyData, ...addData]
},
loadingHandle(isLoading, view) {
if (isLoading == true) {
this.spinningLoading = true
} else if (isLoading == false) {
this.spinningLoading = false
} else {
this.spinningLoading = false
this.$message.warning('加载异常')
}
}
}
}
</script>
<style scoped lang="scss">
.meeting {
padding: 35px;
button {
margin-bottom: 16px;
position: relative;
display: inline-block;
font-weight: 400;
white-space: nowrap;
text-align: center;
background-image: none;
border: 1px solid transparent;
box-shadow: 0 2px 0 rgb(0 0 0 / 2%);
cursor: pointer;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
user-select: none;
touch-action: manipulation;
height: 32px;
padding: 0 15px;
font-size: 14px;
border-radius: 1px;
}
.meeting-hander {
display: flex;
margin-bottom: 8px;
.meeting-hander-select {
margin-right: 16px;
}
.meeting-hander-info {
display: flex;
flex: 1;
justify-content: flex-start;
align-items: flex-end;
padding-bottom: 4px;
&-item {
span {
font-weight: 700;
}
}
&-item:not(:first-child) {
margin-left: 18px;
}
}
.meeting-hander-legend {
margin-left: 10px;
display: flex;
align-items: flex-end;
&-item {
width: 26px;
height: 26px;
border: 1px solid #057f88;
background: #057f88;
}
&-item:last-child {
border: 1px solid #d8d8d8;
background-color: #d8d8d8;
margin-left: 10px;
}
}
}
:deep(.fc-toolbar) {
display: none;
}
:deep(.fc-col-header) {
height: 39px;
line-height: 39px;
background-color: #c6e9ed;
}
:deep(.fc-theme-standard td, .fc-theme-standard th) {
border: 1px solid #e9e9e9;
}
:deep(.fc-timegrid-slot) {
height: 40px;
}
}
</style>