vue项目使用fullendar v5事件日历的分享

学习新技术是不可能的,这辈子都不可能,要不是需求有这个,我肯定不学,好了吐槽完毕,开始进行事件日历fullendar v5版本的分享

效果图

首先看一哈效果图,这是总览图
在这里插入图片描述
这个是添加事件预览图
在这里插入图片描述
这个是点击单元格快捷添加,最大的区别就是单元格点击事件日期已经确定
在这里插入图片描述
另外一个就是点击事件时,出现对事件的再次编辑,相当于预览加编辑二合一
在这里插入图片描述
另外针对事件的删除功能,做成了事件长按删除
在这里插入图片描述
此外还能拖拽事件到另外一个日期,扩大一个事件的日期范围等功能。

如何使用

企鹅自己使用的vue项目,所以保证你的项目是vue2.x版本。
首先是安装
yarn add @fullcalendar/daygrid @fullcalendar/interaction @fullcalendar/list @fullcalendar/vue
基本上这四个插件足够使用了,这几个插件是视图插件,拖拽插件,日程插件以及针对vue使用的插件。而且这种是属于按需使用,避免项目过大的体积。
这里在推荐一个插件,处理日期的dayjs,这个日期插件几乎和moment相同的用法,而且更加轻量,且由于moment不再维护,所以还是转向dayjs怀抱吧。

在组件内部,引入以下依赖。

import FullCalendar from '@fullcalendar/vue';
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import listPlugin from '@fullcalendar/list';
import interactionPlugin from '@fullcalendar/interaction';
import { addLongPress } from '@/utils/assits';
import dayjs from 'dayjs';

同时,FullCalendar是暴露出来的组件,所以模版中需要写上

<FullCalendar ref="calendar" :options="options"></FullCalendar>

记得FullCalendar需要在组件局部注册下,像这样

components: {
  FullCalendar
 },

然后就是FullCalendaroptions定义了,我们在data中这样定义

data() {
	options: {
        // 自定义渲染视图
        views: {
          dayGridMonth: {
            // https://fullcalendar.io/docs/date-formatting
            titleFormat: { year: 'numeric', month: '2-digit', day: '2-digit' }
          }
        },
        plugins: [dayGridPlugin, timeGridPlugin, interactionPlugin, listPlugin],
        // 自定义渲染按钮
        customButtons: {},
        // 自定义渲染头部
        headerToolbar: {
          start: 'prevYear prev next nextYear today',
          center: 'title',
          end: 'dayGridMonth timeGridWeek timeGridDay listMonth'
        },
        // 是否可以拖拽
        editable: true,
        // 是否可添加
        selectable: true,
        // 自定义文本
        buttonText: {
          today: '今天',
          month: '月',
          week: '周',
          day: '天',
          list: '日程'
        },
        // 语言
        locale: 'zh-cn',
        // 设置周几为第一天
        firstDay: 1,
        // 初始化视图
        initialView: 'dayGridMonth',
        // 是否显示周末
        weekends: true,
        // 当事件过多时是否隐藏
        dayMaxEvents: true,
        // 事件存储
        events: [],
        // 事件渲染完加载这个,类似mounted
        eventDidMount: info => {
          addLongPress(info, this.clickEvent, this.longPressEvent);
        },
        // 点击事件
        // eventClick: info => {
        //   console.log(info);
        // },
        // 缩放事件
        eventResize: info => {
          console.log(info);
        },
        // 拖拽事件
        eventDrop: info => {
          console.log(info);
        },
        // 重新渲染
        handleDatesRender: () => {},
        // 日期选中
        select: () => {
          this.addModal = true;
          this.isRapid = true;
        }
      }
}

基本上用到的api都写上了注释,然后就是自己一些🌰,比如自定义增加事件,显示事件信息,删除事件等等。
不对针对事件删除设想的是和编辑事件放一起,但是最后还是拿出去,做成了长按删除。主要逻辑在这,鼠标长按事件也好做,大致思想是同时监听click事件和mousedown事件,mousedown事件时开启一个定时器,进行长按计时,如果满足则向外回调一个长按callBack,如果不满足,则定义一个开关阻止掉click事件,同样在鼠标移出时,进行复位。这样一个点击和长按的功能就做好了,而且我们还对外传递了两个参数,点击的回调和长按的回调。

export function addLongPress(info, clickCb, longPressCb) {
  let timer = null;
  let onOff = false;
  const { el } = info;
  const start = e => {
  	// 这里的button的意思是鼠标点击可能不是左键点击可能是右键或者中键点击,所以这里需要判断下
    if (e.type === 'click' || e.button !== 0) return;

    if (timer === null) {
      timer = setTimeout(() => {
        onOff = true;
        longPressCb && longPressCb(info);
      }, 1000);
    }
  };

  const cancel = e => {
    if (e.type === 'click' && e.button === 0 && !onOff) {
      clickCb && clickCb(info);
    }
    if (timer !== null) {
      clearTimeout(timer);
      timer = null;
      onOff = false;
    }
  };
  el.addEventListener('mousedown', start);
  el.addEventListener('click', cancel);
  el.addEventListener('mouseout', cancel);
}

在事件日历初始化完毕时,给每个事件进行监听

eventDidMount: info => {
  addLongPress(info, this.clickEvent, this.longPressEvent);
},
methods: {
	clickEvent() {},
	longPressEvent() {}
}

记得要关闭掉原来事件日历提供的点击事件。
针对添加和删除已经很好做了,每个事件都有对应的id,新增和删除直接找到事件id,进行事件数组的操作就行。
针对拖拽事件,扩大事件日期范围的事件,id固定,只是日期改变,改动以下日期即可,企鹅这里未做更多演示。
下面贴上完整的代码

<template>
  <div :class="`${prefixCls}-wrap`">
    <Button type="primary" @click="(addModal = true), (isRapid = false)">
      添加事件
    </Button>
    <FullCalendar ref="calendar" :options="options"></FullCalendar>
    <a-modal
      title="添加事件"
      v-model="addModal"
      @ok="handleOk"
      @cancel="handleCancel"
    >
      <a-form :label-col="{ span: 5 }" :wrapper-col="{ span: 19 }">
        <a-form-item label="事件标题">
          <a-input v-model="form.title"></a-input>
        </a-form-item>
        <a-form-item label="事件描述">
          <a-textarea v-model="form.description"></a-textarea>
        </a-form-item>
        <a-form-item label="事件开始日期" v-if="!isRapid">
          <a-date-picker v-model="form.startDay" valueFormat="YYYY-MM-DD" />
          <a-time-picker
            v-model="form.startTime"
            valueFormat="HH:mm:ss"
            style="margin-left: 15px"
          />
        </a-form-item>
        <a-form-item label="事件结束日期" v-if="!isRapid">
          <a-date-picker v-model="form.endDay" valueFormat="YYYY-MM-DD" />
          <a-time-picker
            v-model="form.endTime"
            valueFormat="HH:mm:ss"
            style="margin-left: 15px"
          />
        </a-form-item>
        <a-form-item label="事件文本颜色">
          <colorPicker v-model="form.textColor" class="color" />
        </a-form-item>
        <a-form-item label="事件背景颜色">
          <colorPicker v-model="form.backgroundColor" class="color" />
        </a-form-item>
        <a-form-item label="事件边框颜色">
          <colorPicker v-model="form.borderColor" class="color" />
        </a-form-item>
      </a-form>
    </a-modal>
    <a-modal title="预览事件" v-model="infoModal">
      <a-row type="flex">
        <a-col>标题:</a-col>
        <a-col push="1">{{ info && info.title }}</a-col>
      </a-row>
      <a-row type="flex">
        <a-col>描述:</a-col>
        <a-col push="1">
          {{ info && info.extendedProps && info.extendedProps.description }}
        </a-col>
      </a-row>
      <a-row type="flex">
        <a-col>时间:</a-col>
        <a-col push="1">
          <span>{{ info && info.startStr | dateFilter }}</span>
          <span v-if="info && info.endStr">
            ~ {{ info && info.endStr | dateFilter }}
          </span>
        </a-col>
      </a-row>
    </a-modal>
  </div>
</template>

<script>
import FullCalendar from '@fullcalendar/vue';
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import listPlugin from '@fullcalendar/list';
import interactionPlugin from '@fullcalendar/interaction';
import { addLongPress } from '@/utils/assits';
import dayjs from 'dayjs';

const prefixCls = 'ezb-calendar';

export default {
  name: 'Calendar',
  components: {
    FullCalendar
  },
  data() {
    return {
      prefixCls,
      isRapid: false,
      addModal: false,
      infoModal: false,
      info: null,
      form: {
        id: '',
        title: '',
        description: '',
        startDay: '',
        startTime: '',
        endDay: '',
        endTime: '',
        textColor: '#000',
        backgroundColor: '#3788d8',
        borderColor: '#3788d8'
      },
      options: {
        // 自定义渲染视图
        views: {
          dayGridMonth: {
            // https://fullcalendar.io/docs/date-formatting
            titleFormat: { year: 'numeric', month: '2-digit', day: '2-digit' }
          }
        },
        plugins: [dayGridPlugin, timeGridPlugin, interactionPlugin, listPlugin],
        // 自定义渲染头部
        headerToolbar: {
          start: 'prevYear prev next nextYear today',
          center: 'title',
          end: 'dayGridMonth timeGridWeek timeGridDay listMonth'
        },
        // 是否可以拖拽
        editable: true,
        // 是否可添加
        selectable: true,
        // 自定义文本
        buttonText: {
          today: '今天',
          month: '月',
          week: '周',
          day: '天',
          list: '日程'
        },
        // 语言
        locale: 'zh-cn',
        // 设置周几为第一天
        firstDay: 1,
        // 初始化视图
        initialView: 'dayGridMonth',
        // 是否显示周末
        weekends: true,
        // 当事件过多时是否隐藏
        dayMaxEvents: true,
        events: [],
        eventDidMount: info => {
          addLongPress(info, this.clickEvent, this.longPressEvent);
        },
        // 点击事件
        // eventClick: info => {
        //   console.log(info);
        // },
        // 缩放事件
        eventResize: info => {
          console.log(info);
        },
        // 拖拽事件
        eventDrop: info => {
          console.log(info);
        },
        // 重新渲染
        handleDatesRender: () => {},
        // 日期选中
        select: () => {
          this.addModal = true;
          this.isRapid = true;
        }
      }
    };
  },
  filters: {
    dateFilter(date) {
      return dayjs(date).format('YYYY-MM-DD HH:mm:ss');
    }
  },
  created() {
  	// 模拟异步数据
    setTimeout(() => {
      this.options.events = [
        {
          id: '1',
          title: 'event 1',
          extendedProps: {},
          description: '这是一个事件',
          start: '2021-01-01',
          end: '2021-01-07',
          textColor: '#000',
          backgroundColor: 'red',
          borderColor: 'red'
        },
        { id: '2', title: 'event 2', start: '2021-01-01' },
        { id: '3', title: 'event 1', start: '2021-01-01' },
        { id: '4', title: 'event 2', start: '2021-01-01' },
        { id: '5', title: 'event 3', start: '2021-01-01' },
        { id: '6', title: 'event 4', start: '2021-01-01' },
        {
          id: '7',
          title: 'event 2',
          start: '2021-01-07 07:00:00',
          textColor: 'yellow',
          backgroundColor: 'red',
          className: ['red']
        },
        {
          id: '8',
          title: 'event 5',
          start: '2021-01-08 12:00:00',
          end: '2021-01-08 14:00:00',
          color: 'yellow'
          // textColor: 'red',
          // backgroundColor: 'red',
          // borderColor: 'yellow'
        },
        {
          id: '9',
          title: 'event 5',
          start: '2021-01-09'
          // textColor: 'yellow'
          // borderColor: 'yellow',
          // backgroundColor: 'red'
        }
      ];
    }, 2000);
  },
  methods: {
    addEvents() {
      const params = {};
      params.id = this.form.id ? this.form.id : dayjs().unix();
      params.title = this.form.title;
      params.description = this.form.description;
      params.start = this.filterDate(this.form.startDay, this.form.startTime);
      params.end = this.filterDate(this.form.endDay, this.form.endTime);
      params.textColor = this.form.textColor;
      params.backgroundColor = this.form.backgroundColor;
      params.borderColor = this.form.borderColor;

      this.addModal = false;
      this.options.events.push(params);
    },
    clickEvent(info) {
      this.addModal = true;
      const event = info.event;
      this.form.id = event.id;
      this.form.title = event.title;
      this.form.description = event.extendedProps.description;
      this.form.startDay = event.startStr;
      this.form.endDay = event.endStr;
      if (event.startStr.includes(' ')) {
        this.form.startDay = event.startStr.split(' ')[0];
        this.form.startTime = event.startStr.split(' ')[1];
      }
      if (event.endStr.includes(' ')) {
        this.form.endDay = event.endStr.split(' ')[0];
        this.form.endTime = event.endStr.split(' ')[1];
      }
      this.form.textColor = event.textColor;
      this.form.backgroundColor = event.backgroundColor;
      this.form.borderColor = event.borderColor;
      this.info = info.event;
    },
    longPressEvent(info) {
      this.$confirm({
        title: '温馨提示',
        content: '是否确认删除',
        onOk: () => {
          this.options.events = this.options.events.filter(
            event => event.id !== info.event.id
          );
        },
        onCancel: () => {}
      });
    },
    filterDate(day, time) {
      return day + (time ? ' ' : '') + time;
    },
    handleOk() {
      this.addEvents();
    },
    handleCancel() {}
  }
};
</script>

<style lang="scss" scoped>
/deep/ a {
  color: #000;
}
.color {
  position: relative;
  left: 0px;
  top: 2px;
}
</style>

因为下一个项目用到这项技术,因此提前做了项目预研,ui样式以及功能都未涉及,敬请谅解。
如果有其它不对或者其它见解,还请指正,一同进步。

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值