• FullCalendar日历插件在Vue3中的简单使用


    写在前面的话

    由于近日公司项目要升级使用Vue3,很多插件就需要去更新版本,首先想到的就是FullCalendar日历插件,于是便去对应的官网查看插件是否支持Vue3,结果 o(╥﹏╥)o 官方暂时还没更新Vue3.0版本,紧接着去github上查看大神们的解决方案,皇天不负有心人,找到了部分大神们的意见【文档地址一】【文档地址二】,可以前往查看,我在这里稍微整理了一下,使得FullCalendar插件可以在Vue3中正常运行

    1、Vue版本和FullCalendar版本

    "vue": "^3.0.0"  
    
    "@fullcalendar/core": "^5.6.0",
    
    "@fullcalendar/daygrid": "^5.6.0",
    
    "@fullcalendar/interaction": "^5.6.0",
    
    "@fullcalendar/timegrid": "^5.6.0",
    
    "@fullcalendar/vue": "^5.6.0",

    2、安装

    npm install --save @fullcalendar/vue @fullcalendar/core @fullcalendar/daygrid @fullcalendar/interaction @fullcalendar/timegrid

    3、修改custom-content-type.js文件

    找到安装之后的文件,文件路径为 ode_modules@fullcalendarvuedistcustom-content-type.js

    import { createPlugin } from '@fullcalendar/core'
    import { createApp } from 'vue'
    
    
    /*
    wrap it in an object with a `vue` key, which the custom content-type handler system will look for
    */
    export function wrapVDomGenerator(vDomGenerator) {
        return function (props) {
            return { vue: vDomGenerator(props) }
        }
    }
    
    
    export function createVueContentTypePlugin(parent){
       return createPlugin({
            contentTypeHandlers: {
                vue: function () { return buildVDomHandler(parent); },
            }
        });
    }
    
    
    function buildVDomHandler() {
        let currentEl
        let v // the Vue instance
    
        return function (el, vDomContent) { // the handler
    
            if (currentEl !== el) {
                if (currentEl && v) { // if changing elements, recreate the vue
                    v.$destroy()
                }
                currentEl = el
            }
    
            if (!v) {
                v = initVue(vDomContent)
    
                // vue's mount method *replaces* the given element. create an artificial inner el
                let innerEl = document.createElement('span')
                el.appendChild(innerEl)
                v.$mount(innerEl)
    
            } else {
                v.content = vDomContent
            }
        }
    }
    
    
    function initVue(initialContent) {
        return createApp({
            props: {
                content: Array
            },
            propsData: {
                content: initialContent
            },
            render(h) {
                let { content } = this
    
                // the slot result can be an array, but the returned value of a vue component's
                // render method must be a single node.
                if (content.length === 1) {
                    return content[0]
    
                } else {
                    return h('span', {}, content)
                }
            }
        })
    }

    4、修改FullCalendar.js文件

    找到安装之后的文件,文件路径为 ode_modules@fullcalendarvuedistFullCalendar.js

    import { __assign } from "tslib";
    import {defineComponent,h} from 'vue';
    import { Calendar } from '@fullcalendar/core';
    import { OPTION_IS_COMPLEX } from './options';
    import { shallowCopy, mapHash } from './utils';
    import { wrapVDomGenerator, createVueContentTypePlugin } from './custom-content-type';
    const FullCalendar={
        props: {
            options: Object
        },
        data: initData,
        render(){
            return h('div', {
                // when renderId is changed, Vue will trigger a real-DOM async rerender, calling beforeUpdate/updated
                attrs: { 'data-fc-render-id': this.renderId }
            });
        },
        mounted: function () {
            var internal = this.$options;
            internal.scopedSlotOptions = mapHash(this.$scopedSlots, wrapVDomGenerator); // needed for buildOptions
            var calendar = new Calendar(this.$el, this.buildOptions(this.options, this));
            internal.calendar = calendar;
            calendar.render();
        },
        methods: {
            getApi: getApi,
            buildOptions: buildOptions,
        },
        beforeUpdate: function () {
            this.getApi().resumeRendering(); // the watcher handlers paused it
        },
        beforeDestroy: function () {
            this.getApi().destroy();
        },
        watch: buildWatchers()
    };
    function initData() {
        return {
            renderId: 0
        };
    }
    function buildOptions(suppliedOptions, parent) {
        var internal = this.$options;
        suppliedOptions = suppliedOptions || {};
        // return __assign(__assign(__assign({}, internal.scopedSlotOptions), suppliedOptions), { plugins: (suppliedOptions.plugins || []).concat([
        //         createVueContentTypePlugin(parent)
        //     ]) });
         return {
        ...internal.scopedSlotOptions,
        ...suppliedOptions, // spread will pull out the values from the options getter functions
        plugins: (suppliedOptions.plugins || []).concat([
          createVueContentTypePlugin(parent)
        ])
      }
    }
    function getApi() {
        var internal = this.$options;
        return internal.calendar;
    }
    function buildWatchers() {
        var watchers = {
            // watches changes of ALL options and their nested objects,
            // but this is only a means to be notified of top-level non-complex options changes.
            options: {
                deep: true,
                handler: function (options) {
                    var calendar = this.getApi();
                    calendar.pauseRendering();
                    calendar.resetOptions(this.buildOptions(options, this));
                    this.renderId++; // will queue a rerender
                }
            }
        };
        var _loop_1 = function (complexOptionName) {
            // handlers called when nested objects change
            watchers["options." + complexOptionName] = {
                deep: true,
                handler: function (val) {
                    var _a;
                    // unfortunately the handler is called with undefined if new props were set, but the complex one wasn't ever set
                    if (val !== undefined) {
                        var calendar = this.getApi();
                        calendar.pauseRendering();
                        calendar.resetOptions((_a = {},
                            // the only reason we shallow-copy is to trick FC into knowing there's a nested change.
                            // TODO: future versions of FC will more gracefully handle event option-changes that are same-reference.
                            _a[complexOptionName] = shallowCopy(val),
                            _a), true);
                        this.renderId++; // will queue a rerender
                    }
                }
            };
        };
        for (var complexOptionName in OPTION_IS_COMPLEX) {
            _loop_1(complexOptionName);
        }
        return watchers;
    }
    export default defineComponent(FullCalendar);

    5、修改完上面两个文件之后,就可以进行测试了

    <script>
    import FullCalendar from "@fullcalendar/vue";
    import dayGridPlugin from "@fullcalendar/daygrid";
    import timeGridPlugin from "@fullcalendar/timegrid";
    import interactionPlugin from "@fullcalendar/interaction";
    
    export default {
      components: {
        FullCalendar, // make the <FullCalendar> tag available
      },
    
      data: function () {
        return {
          calendarOptions: {
            allDayText: "全天",
            buttonText: {
              today: "今天",
              month: "月视图",
              week: "周视图",
              day: "天视图",
              list: "列表",
            },
            locale: "zh-cn",
            weekMode: "variable",
            plugins: [
              dayGridPlugin,
              timeGridPlugin,
              interactionPlugin, // needed for dateClick
            ],
            headerToolbar: {
              left: "prev,next today",
              center: "title",
              right: "dayGridMonth,timeGridWeek,timeGridDay",
            },
            initialView: "dayGridMonth",
            initialEvents: [
              {
                id: this.createEventId(),
                title: "All-day event",
                start: new Date().toISOString().replace(/T.*$/, "") + "T12:00:00",
              },
              {
                id: this.createEventId(),
                title: "Timed event",
                start: new Date().toISOString().replace(/T.*$/, "") + "T12:00:00",
              },
            ], // alternatively, use the `events` setting to fetch from a feed
            editable: true,
            selectable: true,
            selectMirror: true,
            dayMaxEvents: true,
            weekends: true,
            select: this.handleDateSelect,
            eventClick: this.handleEventClick,
            eventsSet: this.handleEvents,
            /* you can update a remote database when these fire:
            eventAdd:
            eventChange:
            eventRemove:
            */
          },
          currentEvents: [],
          eventGuid: 0,
        };
      },
    
      methods: {
        handleWeekendsToggle() {
          this.calendarOptions.weekends = !this.calendarOptions.weekends; // update a property
        },
    
        handleDateSelect(selectInfo) {
          let title = prompt("Please enter a new title for your event");
          let calendarApi = selectInfo.view.calendar;
    
          calendarApi.unselect(); // clear date selection
    
          if (title) {
            calendarApi.addEvent({
              id: this.createEventId(),
              title,
              start: selectInfo.startStr,
              end: selectInfo.endStr,
              allDay: selectInfo.allDay,
            });
          }
        },
    
        handleEventClick(clickInfo) {
          if (
            confirm(
              `Are you sure you want to delete the event '${clickInfo.event.title}'`
            )
          ) {
            clickInfo.event.remove();
          }
        },
    
        handleEvents(events) {
          this.currentEvents = events;
        },
        createEventId() {
          return String(this.eventGuid++);
        },
      },
      mounted() {
        this.initialEvents = [
          {
            id: this.createEventId(),
            title: "All-day event",
            start: this.todayStr,
          },
          {
            id: this.createEventId(),
            title: "Timed event",
            start: this.todayStr + "T12:00:00",
          },
        ];
      },
    };
    </script>
    
    <template>
      <div class="demo-app">
        <div class="demo-app-sidebar">
          <div class="demo-app-sidebar-section">
            <h2>Instructions</h2>
            <ul>
              <li>Select dates and you will be prompted to create a new event</li>
              <li>Drag, drop, and resize events</li>
              <li>Click an event to delete it</li>
            </ul>
          </div>
          <div class="demo-app-sidebar-section">
            <label>
              <input
                type="checkbox"
                :checked="calendarOptions.weekends"
                @change="handleWeekendsToggle"
              />
              toggle weekends
            </label>
          </div>
          <div class="demo-app-sidebar-section">
            <h2>All Events ({{ currentEvents.length }})</h2>
            <ul>
              <li v-for="event in currentEvents" :key="event.id">
                <b>{{ event.startStr }}</b>
                <i>{{ event.title }}</i>
              </li>
            </ul>
          </div>
        </div>
        <div class="demo-app-main">
          <FullCalendar class="demo-app-calendar" :options="calendarOptions">
            <template v-slot:eventContent="arg">
              <b>{{ arg.timeText }}</b>
              <i>{{ arg.event.title }}</i>
            </template>
          </FullCalendar>
        </div>
      </div>
    </template>
    
    <style lang="css">
    h2 {
      margin: 0;
      font-size: 16px;
    }
    
    ul {
      margin: 0;
      padding: 0 0 0 1.5em;
    }
    
    li {
      margin: 1.5em 0;
      padding: 0;
    }
    
    b {
      /* used for event dates/times */
      margin-right: 3px;
    }
    
    .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 {
      /* the calendar root */
      max-width: 1100px;
      margin: 0 auto;
    }
    </style>

    6、运行界面

    由于每次重新安装FullCalendar插件之后都需要重新修改一遍文件,这里就不提供案例了

    平时积累,用于复习,如有问题,请留言,谢谢

  • 相关阅读:
    我们是在开发产品还是项目?
    创业期的软件开发管理(一)
    由“I”到“T”
    创业期的软件开发管理(二)
    平台架构用户系统
    产品的臃肿过程
    平台架构——体系结构
    狼群的架构暗示
    如何创建一个好的索引
    哈希索引
  • 原文地址:https://www.cnblogs.com/sas1231/p/14747902.html
Copyright © 2020-2023  润新知