Skip to content

日历组件

注意

  1. 日历组件我在这里用的就是Fullcalendar

  2. 个人觉得他是前端最强的日历组件,没有之一

基本使用

官方文档

安装

  • Vue2
js
npm install --save @fullcalendar/vue
//下面包是日历的周视图、日视图等插件包:
npm install --save @fullcalendar/core @fullcalendar/daygrid @fullcalendar/interaction @fullcalendar/list @fullcalendar/timegrid
//安装后的fullcalendar源码和其它插件都会在@fullcalendar
npm install --save dayjs
//安装dayjs用于格式化时间格式
  • Vue3
bash

npm install --save @fullcalendar/vue3
//下面包是日历的周视图、日视图等插件包:
npm install --save @fullcalendar/core @fullcalendar/daygrid @fullcalendar/interaction @fullcalendar/list @fullcalendar/timegrid
//安装后的fullcalendar源码和其它插件都会在@fullcalendar
npm install --save dayjs
//安装dayjs用于格式化时间格式

架构

bash
├── src
   ├── components
   ├── chooseCalendar
      ├── lib (引入的第三方库)
    └── xxx.json
      ├── src (源码)
   ├── index.vue
      ├── index.ts

index.ts

ts
import { type App } from "vue";
import chooseCalendar from "./src/index.vue";

// 让这个组件可以通过use的形式使用
export default {
  install(app: App) {
    app.component("YJ-choose-calendar", chooseCalendar);
  },
};

interface/chooseCalendar/type.ts

ts
export interface EventItem {
  // 事件标题
  title: string;
  // 开始时间
  start: string;
  // 结束时间
  end: string;
  // 是否可拖动编辑
  editable?: boolean;
}

components/chooseCalendar/src/index.vue

vue
<template>
  <div id="calendar"></div>
</template>

<script lang="ts" setup>
// 在vite里面使用fullcalendar 需要引入一个包
// import '@fullcalendar/core/vdom'
import {
  Calendar,
  type EventClickArg,
  type EventContentArg,
} from "@fullcalendar/core";
import daygridPlugin from "@fullcalendar/daygrid";
import timeGridPlugin from "@fullcalendar/timegrid";
import interactionPlugin, {
  type DateClickArg,
} from "@fullcalendar/interaction";
import { ref, onMounted, type PropType, watch } from "vue";
import { type EventItem } from "@/interfaces/chooseCalendar/type";

let props = defineProps({
  // 日历显示的语言
  local: {
    type: String,
    default: "zh-cn",
  },
  // 视图模式
  initialView: {
    type: String,
    default: "dayGridMonth",
  },
  // 按钮文字
  buttonText: {
    type: Object,
    default: () => {
      return {
        today: "今天",
        month: "月",
        week: "周",
        day: "日",
      };
    },
  },
  // 头部工具栏
  headerToolbar: {
    type: Object,
    default: () => {
      return {
        left: "prev,next, today",
        center: "title",
        right: "dayGridMonth,timeGridWeek,timeGridDay",
      };
    },
  },
  // 底部工具栏
  footerToolbar: {
    type: Object,
  },
  // 日历事件
  events: {
    type: Array as PropType<EventItem[]>,
    default: () => [],
  },
  // 自定义渲染内容方法
  eventContent: {
    type: Function,
  },
});

let emits = defineEmits(["date-click", "event-click"]);

// 日历对象
let calendar = ref<Calendar>();

// 生成日历的方法
let renderCalendar = () => {
  let el = document.getElementById("calendar");
  if (el) {
    // 日历的配置选项
    calendar.value = new Calendar(el, {
      // 使用到的插件
      plugins: [daygridPlugin, interactionPlugin, timeGridPlugin],
      // 视图模式
      initialView: props.initialView,
      // 语言
      locale: props.local,
      // 按钮文字
      buttonText: props.buttonText,
      // 头部工具栏
      headerToolbar: props.headerToolbar,
      // 底部工具栏
      footerToolbar: props.footerToolbar,
      // 显示周末
      weekends: true,
      // 事件源
      eventSources: [
        {
          // 生成事件
          events(e, callback) {
            if (props.events.length) callback(props.events);
            else callback([]);
          },
        },
      ],
      // 点击日历的某一天
      dateClick(info: DateClickArg) {
        // console.log(info)
        emits("date-click", info);
      },
      // 点击日历上的时间
      eventClick(info: EventClickArg) {
        // console.log('eventClick', info)
        emits("event-click", info);
      },
      // 显示事件的结束时间
      displayEventEnd: true,
      // 自定义渲染内容
      eventContent: props.eventContent,
      // 拖拽合集开始
      editable: true, // 启用事件拖拽和调整大小
      droppable: true, // 启用外部元素拖拽到日历
      eventDrop: function (info) {
        console.log("Event dropped: " + info.event.title);
        console.log(info);
        console.log("开始时间:" + info.event.startStr);
        console.log("结束时间:" + info.event.endStr);
        console.log("结束时间:" + info.event.title);
      },
      eventResize: function (info) {
        console.log("Event resized: " + info.event.title);
        console.log(info);
      },
      drop: function (info) {
        console.log("Dropped");
        console.log(info);
        console.log(info.view.calendar);
      },
      // 拖拽合集结束
    });
    calendar.value.render();
  }
};

// 监听事件源的变化
watch(
  () => props.events,
  () => {
    renderCalendar();
  },
  { deep: true },
);
onMounted(() => {
  renderCalendar();
});
</script>

<style lang="scss" scoped>
svg {
  width: 1em;
  height: 1em;
}
</style>

父元素调用

vue
<template>
  <div>
    <YJ-choose-calendar
      :events="events"
      :eventContent="eventContent"
      @date-click="dateClick"
    ></YJ-choose-calendar>
  </div>
</template>

<script setup lang="ts">
import { type DateClickArg } from "@fullcalendar/interaction";
import { type EventItem } from "@/interfaces/chooseCalendar/type";
import { ref } from "vue";
import { type EventContentArg } from "@fullcalendar/core";
import dayjs from "dayjs";
let events = ref<EventItem[]>([
  {
    title: "购物",
    start: "2025-02-04 10:00:00",
    end: "2025-02-06 12:00:00",
    editable: true,
  },
  {
    title: "学习",
    start: "2025-02-05 08:00:00",
    end: "2025-02-05 16:00:00",
  },
]);
let dateClick = (info: DateClickArg) => {
  let event = {
    start: `${info.dateStr} 12:00:00`,
    end: `${info.dateStr} 13:00:00`,
    title: "吃饭",
  };
  events.value.push(event);
  console.log(info);
};

let eventContent = (arg: EventContentArg) => {
  let el = document.createElement("div");
  let start = `${dayjs(arg.event.start).format("YYYY-MM-DD HH:mm:ss")}时开始`;
  let end = `${dayjs(arg.event.end).format("YYYY-MM-DD HH:mm:ss")}时结束`;
  el.innerHTML = `
        <img src="https://jsopy.oss-cn-beijing.aliyuncs.com/IMAGES/avatar.jpg" style="width:20px;height:20px;">
         <div>开始时间: ${start}</div>
         <div>结束时间: ${end}</div>
         <div>标题: ${arg.event.title}</div>
        `;
  return {
    domNodes: [el],
  };
};
</script>

<style scoped></style>