Fullcalendar常用功能介绍

JavaScript性能优化实战 10w+人浏览 200人参与

更多推荐阅读:
 
前端性能&异常分析排查流程-CSDN博客

SQL问数提示词模板与测试-CSDN博客

Dify架构分析_dify系统架构-CSDN博客

使用pycharm社区版调试DIFY后端python代码_dify后端代码怎么跑起来-CSDN博客


目录

基础概念

注意事项

官网地址

前期准备

安装依赖

简单demo

初始化配置介绍

配置

配置详解

plugin

部分API

获取日历视图的时间显示

日历操作相关API

根据视图模式切换日期

this.$refs.fullCalendarRef.getApi().changeView('dayGridWeek')单元格中的事件相关

event数据结构

event的相关方法

event的点击事件

自定义样式

方式1:CSS方式

方式2:在初始化视图的时候,使用自己定义的内容

方式3: 自定义视图plugin

定义一个plugin.js


基础概念

FullCalendar是一个JavaScript事件日历库,它可以帮助您轻松地创建交互式日历,简称日历视图。

FullCalendar有很多用法,以下是一些常见的用法:

  • 显示日历视图:FullCalendar可以显示日历视图,包括月视图、周视图和日视图等。
  • 显示事件:FullCalendar可以显示事件,包括单个事件和重复事件等。
  • 选择日期和时间:FullCalendar可以让用户选择日期和时间,包括单个日期和时间范围等。
  • 拖放事件:FullCalendar可以让用户拖放事件,包括移动事件和调整事件持续时间等。
  • 自定义样式:FullCalendar可以自定义样式,包括更改颜色、字体、大小等。

注意事项

由于他的一些步骤都是操作HTML,所以需要对原生js或jq有比较熟练的掌握。

官网地址

Documentation | FullCalendar

前期准备

安装依赖

以vue2为例子(vue3版本需要将@fullcalendar/vue 替换成@fullcalendar/vue3)

需要什么就引入什么

Plugin Index - Docs | FullCalendar

npm install @fullcalendar/core @fullcalendar/vue @fullcalendar/daygrid @fullcalendar/timegrid  @fullcalendar/list @fullcalendar/interaction

由于fullcalendar的每一个功能都是一个依赖,所以如果想使用对应功能,就需要将对应的依赖进行安装

简单demo

<template>
  <FullCalendar :options="calendarOptions" />
</template>
<script>
 	import FullCalendar from '@fullcalendar/vue'
  import dayGridPlugin from '@fullcalendar/daygrid'
  import interactionPlugin from '@fullcalendar/interaction'

  export default {
    components: {
      FullCalendar // make the <FullCalendar> tag available
    },
    data() {
      return {
        calendarOptions: {
          plugins: [ dayGridPlugin, interactionPlugin ],
          initialView: 'dayGridMonth'
        }
      }
    }
  }
</script>

初始化配置介绍

这里指的是calendarOptions里的配置,由于有太多配置项,主要举例一些常用的说明

配置

 {
   	// 插件,需要什么日历视图,就需要引入什么插件
    plugins: [dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin],
    // 高度 等同于css
    height: 'calc(100% - 53px)',
    // 按钮文本
    buttonText: {
      today: '今天',
      month: '月',
      week: '周',
      day: '日',
      list: '周列表',
    },
   // 自定义按钮
    customButtons: {
      createEventButton: {
        text: '创建日程',
        click: () => {
          // do something
        }
      }
    },
   	// 周数,第几周第几周
    weekNumbers: true,
    // 头部工具
    headerToolbar: false,
    // 当前的视图类型
    initialView: this.calendarMode,
    // 头部日期设置
    datesSet: (info) => {
     // do something
    },
    // 周数格式化
    weekNumberFormat: { week: 'numeric' },
   // 视图自定义
    views: {
      dayGrid: {
        dayCellContent: customize.dayCellContent,
        dayHeaderContent: customize.dayHeaderContent,
        weekNumberContent: customize.weekNumberContent,
        dayMaxEvents: 3,
        duration: { weeks: 2 },
      },
      timeGrid: {
        dayCellContent: customize.dayCellContent,
        allDayContent: customize.allDayContent,
        dayHeaderContent: customize.dayHeaderContent,
        dayMaxEventRows: 3,
        // eventMaxStack: 2,
        nowIndicator: true,
        nowIndicatorContent: customize.nowIndicatorContent,
        // slotLabelDidMount: customize.slotLabelDidMount,
        slotDuration: '01:00',
        slotEventOverlap: false,
        slotLabelContent: customize.slotLabelContent,
        slotLabelFormat: {
          hour12: false,
          hour: '2-digit',
          minute: '2-digit'
        }
      }
    },
  	// 是否可选择
    selectable: true,
  	// 
    unselectAuto: false,
    // 周
    weekends: true,
    // 文本本地化设置
    locale: 'zh-cn',
    // 日期点击回调
    dateClick: this.handleDateClick,
    // 日期选择回调
    select: this.handleDateSelect,
    // 用于在每个格子里的数据,简称事件数据
    events: this.eventList,
    // 事件数据的点击事件
    eventClick: (info) => {
      // do something
    },
    // 事件数据的数量
    eventContent: customize.eventContent,
    // 
    eventOrderStrict: true,
    // 
    eventOrder: (prev, next) => (prev.INDEX <= next.INDEX ? -1 : 1),
    // 事件数据的格式化
    eventTimeFormat: {
      hour12: false,
      hour: '2-digit',
      minute: '2-digit'
    },
   // 事件超出一定数量后的显示的更多,点击更多后的触发事件
    moreLinkClick: (info)=> {
      // do something
    },
    // 更多展示的样子
    moreLinkContent: customize.moreLinkContent,
}

配置详解

plugin

<template>
  <FullCalendar :options="calendarOptions" />
</template>
<script>
  import FullCalendar from '@fullcalendar/vue'
  import dayGridPlugin from '@fullcalendar/daygrid'
  import interactionPlugin from '@fullcalendar/interaction'
  import timeGridPlugin from '@fullcalendar/timegrid'
  import listPlugin from '@fullcalendar/list'

  export default {
    name: 'demo2',
    components: {
      FullCalendar
    },
    data() {
      return {
        calendarOptions: {
          // 日布局视图插件,时间线布局视图插件,列表视图插件, 点击相关的插件
          plugins: [dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin],
          // 当前选中的视图
          initialView: 'dayGridMonth',
          // 按钮文本
          buttonText: {
            today: '今天',
            month: '月',
            week: '周',
            day: '日',
            list: '周列表',
          },
          headerToolbar: {
            left: 'today prev title next',
            right: 'timeGridDay,timeGridWeek,dayGridMonth,listMonth createEventButton'
          },
          customButtons: {
            createEventButton: {
              text: '创建日程',
              click: () => {
                console.log('do something')
              }
            }
          },
        }
      }
    }
  }
</script>

timeGridDay视图

timeGridWeek视图

dayGridMonth视图

listMonth视图

没有数据的话,这个视图是空视图

从上面可以看出,我们的视图,是由对应视图插件,加上展示条件进行共同配置的

部分API

使用方式以当前的日历视图实例.getApi()去获取整个日历视图的相关数据,返回的是一个Calendar的类型

this.$refs.fullCalendarRef.getApi()

由于返回的是Calendar,所以可以直接使用Calendar下面的方法,如

this.$refs.fullCalendarRef.getApi().prev()

获取日历视图的时间显示

周视图

月视图

日视图

this.dateTitle = this.$refs.fullCalendarRef.getApi().view.title // 返回 2023年5月21日 – 6月3日关于view的参数说明

View Object - Docs | FullCalendar

日历操作相关API

日历相关的api目前用得多的还是关于calendar的

Date Navigation - Docs | FullCalendar

根据视图模式切换日期

// 往前 this.$refs.fullCalendarRef.getApi().prev() // 往后 this.$refs.fullCalendarRef.getApi().next()

切换当前视图

切换名称参考配置详解

this.$refs.fullCalendarRef.getApi().changeView('dayGridWeek')单元格中的事件相关

单元格中一条条的事项,在fullCalendar中,称为Event

Event也是有对应操作属性的

event数据结构

const eventList = [
  {
    // 是否是整天,如果在时间视图中,会从00:00拉到23:59
    allDay: false,
    // 内容的标题,显示的文本内容
    title: '我的任务',
    // 开始时间
    start: 1684080000000,
    // 结束时间
    end: 1684080000000,
    // 可以定义你自己的参数,点击时可以拿到该属性
    extendedProps: {
      a: 1,
      b: 2
    },
    // 颜色,包含backgroundColor/borderColor,可传#f00, #ff0000, rgb(255,0,0), or red
    color: 'red',
    backgroundColor: 'red'.
    borderColor: 'red',
    textColor: 'red',
    // 是否可编辑
    editable: false
    // 其他属性,暂时没有用到过,详情请看文档
    ...
  }
]

详情请看文档

Event Parsing - Docs | FullCalendar

event的相关方法

除了curd

可看文档,这里不做赘述

event的点击事件

// 初始化
  init() {
    this.calendarOptions = {
      //...
      // event数组,用于存所有event
      events: this.eventList,
      // event的点击事件
      eventClick: (info) => {
        console.log(info.event.extendedProps)
        // do something
      },
      // 自定义event样式
      eventContent: null,
      // 使用event的排序
      eventOrderStrict: true,
      // event的排序序号
      eventOrder: (prev, next) => (prev.INDEX <= next.INDEX ? -1 : 1),
      // event的时间格式化
      eventTimeFormat: {
        hour12: false,
        hour: '2-digit',
        minute: '2-digit'
      },
    
    }
  },

自定义样式

原始demo样子

方式1:CSS方式

通过复写原始demo中的样式,达到修改的目的

方式2:在初始化视图的时候,使用自己定义的内容

对应得content进行重写其html,即可达到自定义内容的形式

// 初始化
init() {
  //...
  this.calendarOptions = {
    // 对对应的视图进行配置,对其content进行重写,即可达到自定义内容的形式
    views: {
      dayGrid: {
        // 每天单元格的内容
        dayCellContent: (arg) => {
          const { date, isToday } = arg
          const year = date.getFullYear()
          const month = date.getMonth()
          const day = date.getDate()
          const week = date.getDay()
          const isFirstDayOfMonth = day === 1
          const cellEl = document.createElement('div')
          cellEl.className = 'cell_text'
          const solarText = document.createElement('span')
          const lunarText = document.createElement('span')
          const weekText = document.createElement('span')
          solarText.className = isFirstDayOfMonth ? 'solar_text solar_text-month' : 'solar_text'
          lunarText.className = 'lunar_text'
          weekText.className = 'week_text'
          if (isToday) {
            solarText.innerText = '今'
          } else {
            solarText.innerText = isFirstDayOfMonth ? `${month + 1}月` : `${day}`
          }
          lunarText.innerText = getLunarText(year, month, day)
          weekText.innerText = `周${weekMap[week]}`
          cellEl.appendChild(solarText)
          cellEl.appendChild(lunarText)
          cellEl.appendChild(weekText)
          return {
            domNodes: [cellEl]
          }
        },
        // 每天的单元格头部内容
        dayHeaderContent: (arg) => {
          const { date, isToday, text } = arg
          const year = date.getFullYear()
          const month = date.getMonth()
          const day = date.getDate()
          const cellEl = document.createElement('div')
          const weekEl = document.createElement('div')
          const dateEl = document.createElement('div')
          const solarText = document.createElement('span')
          const lunarText = document.createElement('span')
          cellEl.className = 'cell_text'
          weekEl.className = 'week_text'
          solarText.className = 'solar_text'
          lunarText.className = 'lunar_text'
          weekEl.innerText = text.substr(-2, 3)
          solarText.innerText = isToday ? '今' : day
          lunarText.innerText = getLunarText(year, month, day)
          dateEl.appendChild(solarText)
          dateEl.appendChild(lunarText)
          cellEl.appendChild(dateEl)
          return {
            domNodes: [weekEl, cellEl]
          }
        },
        // 周数的内容
        weekNumberContent: (arg) => {
          const weekDom = document.createElement('span')
          weekDom.className = "weekNumber_text"
          weekDom.innerHTML = `第${arg.text}周`
          return {
            domNodes: [weekDom]
          }
        },
        // 最大显示event的条数,超出会显示more
        dayMaxEvents: 10,
        // 可配置时间区间的跨度,这里是显示两周
        duration: { weeks: 2 },
      }
    },
    // 自定义event样式
    eventContent: (arg) => {
      let { timeText, event, view } = arg
      timeText = timeText || '全天'
      const { allDay: isAllDay, title, extendedProps } = event
      const isPast = new Date().getTime() > extendedProps.endTime
      const isTimeGridWeek = view.type === 'timeGridWeek'
      const duration = extendedProps.endTime - extendedProps.startTime
      let isPlaceTimetext = false
      let weekGridItem = `<p>${title}</p>`
      if (duration >= 45 * 60 * 1000) {
        if (extendedProps.place) {
          weekGridItem += `<p>${extendedProps.place}</p>`
          isPlaceTimetext = false
        } else {
          weekGridItem += `<p>${timeText}</p>`
          isPlaceTimetext = true
        }
  
        if (duration >= 60 * 60 * 1000 && !isPlaceTimetext) {
          weekGridItem += `<p>${timeText || '00:00-24:00'}</p>`
        }
      }
      
      const isWholeDay = isAllDay || duration >= 24 * 60 * 60 * 1000
      let opacity = isPast || extendedProps.status === -1 ? 0.3 : 1
      const style = isTimeGridWeek ? `border-left-color: rgba(${extendedProps.color}, ${opacity})` : `background-color: rgba(${extendedProps.color}, 1)`
      let withDot = isTimeGridWeek ? '' : `<span class="dot" style="background-color: rgba(${extendedProps.color}, ${opacity})"></span>`
      const backgroundColor = isWholeDay ? `<div class="custom_event_background" style="${style}"></div>` : ''
      let content = isTimeGridWeek ? weekGridItem : `${backgroundColor}${withDot}<span class="width_32">${timeText}</span><span class="center">${title}</span>`
      let className = isTimeGridWeek ? 'in_week' : 'in_month with_dot'
      const timeRangeClass = isWholeDay ? 'custom_event_content cec_long' : 'custom_event_content cec_short'
      if (isPast) {
        className += ' in_past'
      }
      if (extendedProps.status === -1) {
        className += ' is_reject'
      }
      if (isAllDay) {
        content = `<div class="custom_event_background" style="${style}"></div>${withDot}<span class="width_32">全天</span><span class="center">${title}</span>`
      }
      return {
        html: `<div class="${timeRangeClass} ${className} calendar_ellipsis" style="cursor: pointer;${isTimeGridWeek ? style : ''}" title="${title}">${content}</div>`
      }
    },
    // 更多点击的样式
    moreLinkContent: (arg) => {
      const { num } = arg
      return `还有${num}个日程或任务`
    },
  }
},

结果

方式3: 自定义视图plugin

注意:除非确认所有视图都不能使用,才使用这种视图,文档上描述不是很清晰,并不能让我们知道自定义后事件是如何触发的,很多东西都要重新写过,事件触发还得自己搞

如实现以下这种视图

官方文档

Custom Views via JS - Docs | FullCalendar

定义一个plugin.js

他需要以

createPlugin({

views: {

// 自定义视图的名称,切换视图时使用的就是该视图名称

customWeek: customWeekViewConfig

}

})

的形式导出

import { sliceEvents, createPlugin } from '@fullcalendar/core';
import dayjs from 'dayjs';
import $ from 'jquery'
const weekCn = ['日','一','二','三','四','五','六']

// 获取日期区间
function getDateList({start}) {
  if(!start) {
    return []
  }

  const arr = [];
  for (let i = 0; i < 7; i++) {

    const t = new Date(start);
    t.setDate(start.getDate() + i);
    arr.push({
      year: t.getFullYear(),
      month: t.getMonth() + 1,
      date:  t.getDate()
    });
  }

  return arr
}
// 获取事件列表
function getEventMap(events) {
  const eventMap = {}
  events.forEach(item => {
    const date = item.range.start.getDate()
    if(!eventMap[date]) {
      eventMap[date] = []
    }
    eventMap[date].push(item)
  })
  return eventMap
}

// 获取每一天的事件
function getEventToHTML(event, date) {
  if(!event) {
    return ''
  }
  let html = ''
  for(let i=0; i<event.length;i++) {
    const item = event[i]
    const startTime = dayjs(item.def.extendedProps.startTime)
    const endTime = dayjs(item.def.extendedProps.endTime)
    const isAllDay = item.def.extendedProps.isAllDay || !startTime.isSame(endTime, 'day') || endTime.isAfter(startTime, 'day')
    const isMorning = Number(startTime.format('H')) < 12 && Number(endTime.format('H')) < 12
    html += `
      <div class="eventItem${isAllDay? ' isAllDay': ''}${!isAllDay && !isMorning? ' isAfternoon': ''}" data-id="${item.def.extendedProps.id}" data-date="${date}">
        ${startTime.format(isAllDay ? 'MM-DD HH:mm' : 'HH:mm:ss')}${isAllDay?'至':'-'}${endTime.isValid() ? endTime.format(isAllDay ? 'MM-DD HH:mm' : 'HH:mm:ss'):'--:--'} ${item.def.title}
      </div>
    `
    if(i===2 && event.length - 3 > 0) {
      html += `<div class="eventItem-more" data-id="${item.def.extendedProps.id}" data-date="${startTime.format('YYYY-MM-DD')}">还有${event.length - 3}个日程或任务</div>`
      break;
    }
  }
  return html
}

// 获取一周HTML
function getWeekListHTML(currentRange, events) {
  window.dateList = getDateList(currentRange)
  let listString = ''
  const nowDay = new Date().getDate()
  const eventMap = getEventMap(events)

  weekCn.forEach((item,index) => {
    const date = window.dateList[index].date
    listString+=`
      <div class="weekItem">
        <div 
          class="weekDate"
          ${nowDay === date ? 'style="color:#0085FF"': ""}>
          <span>${date}</span>
          <br/>
          <span>周${item}</span>
        </div>
        <div class="weekNote view-events">
          ${getEventToHTML(eventMap[date], date)}
        </div>
      </div>`
  })
  return listString
}

// 自定义周视图配置
const customWeekViewConfig = {

  classNames: [ 'custom-view' ],
  content: function(props) {
    // 事件
    let segs = sliceEvents(props, true); // allDay=true
    const segsMap = {}
    segs.forEach(item => {
      const id = item.def.extendedProps.id
      segsMap[id] = item.def
    })
    let html =`
      <div class="customWeekView">
        <div class="header view-title">
          <div>上午</div>
          <div>下午</div>
        </div>
        <div class="weekList">
          ${getWeekListHTML(props.dateProfile.currentRange, segs)}
        </div>
      </div>
    `
    // 监听事件
    $(document).off('click', '.eventItem');
    $(document).on('click', '.eventItem', function() {
      const id = $(this).data('id')
      console.log(segsMap[id])
      if(id) {
        // 调用日历视图实例上的event点击事件
        document.querySelector('.pc_calendar_content').__vue__._data.calendarOptions.eventClick({event: segsMap[id]})
      }
    })
    $(document).off('click', '.eventItem-more');
    $(document).on('click', '.eventItem-more', function() {
      const date = $(this).data('date')
      if(date) {
        // 调用日历视图实例上的更多点击事件
        document.querySelector('.pc_calendar_content').__vue__.showMoreFunction({date})
      }
    })
    return { html }
  },
}


// 最终输出给calendar的配置使用
const plugin = createPlugin({
  views: {
    // 自定义视图的名称
    customWeek: customWeekViewConfig
  }
});


export default  plugin

plugin在calendar的初始化配置里使用

<template>
  <div class="pc_calendar_content">
    <div class="pc_calendar">
      <FullCalendar
        v-loading="isLoading"
        ref="fullCalendarRef" :options="calendarOptions" >
        <template #eventContent="{ event }">
          <span>{{ event.allDay ? '全天' : event.start }}</span>
          <span>{{ event.title }}</span>
        </template>
      </FullCalendar>
    </div>
    <van-overlay :show="showMore" @click="showMore = false">
      <div class="calendar_wrapper">
        <div class="more_calendar_content" @click.stop>
          <div class="more_title">{{showMoreDate}}</div>
          <div class="more_list">
            <div
              class="calendar_search_item_content"  
              v-for="item2 in showMoreEvents" :key="item2.id"
              @click="handleClickEvent(item2)">
              <div class="calendar_search_item_title">
                <div style="width: 160px">{{ item2.calendarName }}</div>
                <div>{{ item2.startTime }}</div>
              </div>
              <div class="calendar_search_item_footer">
                <div style="width: 160px">组织人:{{ item2.organizer || '-' }}</div>
                <div>{{ item2.endTime }}</div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </van-overlay>
  </div>
</template>
<script>
  import FullCalendar from '@fullcalendar/vue'
  import customWeekPlugin from './utils/customWeekViewPlugin'

  export default {
     data() {
      return {
        showMore: false,
        showMoreEvents: [],
        showMoreDate: ''
      }
    },
    methods: {
      // 初始化
      init() {
        this.calendarOptions = {
          // 使用自定义视图
          plugins: [customWeekPlugin],
          // 切换到该视图对应的名称,与视图plugin中的视图名称一致
          initialView: 'customWeek',
          eventClick: ()=>{},
          views: {
            // 视图的配置
            customWeek: {
              duration: { weeks: 1 }
            }
          }
        }
      },
    	// 自己定义的点击打开更多
      showMoreFunction(data) {
        this.showMore = true
        this.showMoreEvents  = []
        this.showMoreDate = `${dayjs(data.date).format('M月D日')} 星期${weekMap[dayjs(data.date).day()]}`
        this.eventList.forEach(dItem => {
          const dateItem = JSON.parse(JSON.stringify(dItem.extendedProps))
          if(dayjs(dateItem.startTime).format('YYYY-MM-DD') === data.date){
            const {
              startTime,
              endTime,
              isAllDay
            } = dateItem
            const start = dayjs(startTime)
            const end = dayjs(endTime)
            const allDay = isAllDay || !start.isSame(end, 'day') || end.isAfter(start, 'day')
            dateItem.startTime = startTime ? `${start.format(allDay ? 'MM月DD日 HH:mm:ss' : 'HH:mm:ss')}` : '-'
            dateItem.endTime = endTime ? `${end.format(allDay ? 'MM月DD日 HH:mm:ss' : 'HH:mm:ss')}` : '-'
            
            this.showMoreEvents.push(dateItem)
          }
        })
      }
    }
  }
</script>

这样一个自定义plugin就完成了


七巧低代码是以业务应用搭建为核心的aPaaS低代码应用平台,为客户提供aPaaS+iPaaS的全民数智化解决方案。技术资料获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

道一云黑板报

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值