Vue日历,FullCalendar 使用及兼容IE10+

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>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 229,327评论 6 537
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 98,996评论 3 423
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 177,316评论 0 382
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,406评论 1 316
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,128评论 6 410
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,524评论 1 324
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,576评论 3 444
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,759评论 0 289
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,310评论 1 335
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,065评论 3 356
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,249评论 1 371
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,821评论 5 362
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,479评论 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,909评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,140评论 1 290
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,984评论 3 395
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,228评论 2 375