杰瑞科技汇

FullCalendar教程,如何快速上手?

Fullcalendar 全方位教程

目录

  1. 第一部分:入门指南

    FullCalendar教程,如何快速上手?-图1
    (图片来源网络,侵删)
    • 什么是 FullCalendar?
    • 为什么选择 FullCalendar?
    • 环境准备:安装与引入
    • 创建你的第一个日历(Hello World)
  2. 第二部分:核心配置与基本功能

    • 初始化选项
    • 、视图和按钮
    • 事件的基本使用
    • 事件对象详解
  3. 第三部分:事件交互与数据源

    • 事件操作:点击、拖拽、调整大小
    • 使用外部数据源(AJAX)
    • 事件渲染函数:eventContent
  4. 第四部分:高级功能与自定义

    • 自定义视图
    • 日期点击与范围选择
    • 插件系统
    • 国际化
    • 主题与样式定制
  5. 第五部分:实战案例:构建一个任务管理系统

    FullCalendar教程,如何快速上手?-图2
    (图片来源网络,侵删)
    • 需求分析
    • 项目结构
    • 实现步骤
    • 完整代码示例
  6. 第六部分:常见问题与最佳实践

    • 常见问题解答
    • 性能优化建议

第一部分:入门指南

什么是 FullCalendar?

FullCalendar 是一个功能强大、高度可定制的 JavaScript 日历库,它可以轻松地在你的网页中嵌入交互式的事件日历,它支持多种视图(月、周、日、时间线等),并内置了丰富的事件处理功能。

为什么选择 FullCalendar?

  • 功能全面:支持拖拽、调整大小、点击、外部数据源等。
  • 文档完善:官方文档非常详尽,社区活跃。
  • 主题丰富:提供多个官方主题(如 Bootstrap 5, Material, Tailwind CSS),并支持自定义样式。
  • 视图多样:除了标准的月/周/日视图,还支持时间线视图(Timeline)等高级视图。
  • 商业支持:提供免费版和功能更强大的付费商业版。

环境准备:安装与引入

FullCalendar 提供了多种安装方式,这里介绍最常用的两种。

通过 CDN 引入(最简单,适合快速上手)

直接在 HTML 文件中引入 CSS 和 JavaScript 文件,这种方式无需构建工具。

<!-- 1. 引入 FullCalendar 的 CSS -->
<link href='https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/index.global.min.css' rel='stylesheet' />
<!-- 2. 引入 FullCalendar 的 JS -->
<script src='https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/index.global.min.js'></script>

通过 npm/yarn 安装(推荐用于生产环境)

如果你使用的是现代前端框架(如 React, Vue, Angular)或构建工具(如 Vite, Webpack),推荐使用 npm 或 yarn 安装。

# 使用 npm
npm install fullcalendar --save
# 使用 yarn
yarn add fullcalendar

安装后,你需要根据你使用的框架来导入相应的模块。

创建你的第一个日历

我们使用最简单的 CDN 方式来创建一个基础日历。

步骤:

  1. 创建一个 HTML 文件(index.html)。
  2. <body> 中创建一个 <div> 作为日历的容器,并给它一个 id
  3. <body> 的底部,编写 JavaScript 代码来初始化日历。

index.html 代码:

<!DOCTYPE html>
<html>
<head>
  <meta charset='utf-8' />FullCalendar 示例</title>
  <!-- 1. 引入 FullCalendar 的 CSS -->
  <link href='https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/index.global.min.css' rel='stylesheet' />
  <style>
    /* 给日历容器设置一个高度,否则日历将不可见 */
    #calendar-container {
      max-width: 900px;
      margin: 40px auto;
    }
  </style>
</head>
<body>
  <!-- 2. 创建日历容器 -->
  <div id='calendar-container'></div>
  <!-- 3. 引入 FullCalendar 的 JS 并初始化 -->
  <script src='https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/index.global.min.js'></script>
  <script>
    document.addEventListener('DOMContentLoaded', function() {
      const calendarEl = document.getElementById('calendar-container'); // 获取容器元素
      // 初始化日历
      const calendar = new FullCalendar.Calendar(calendarEl, {
        // 初始化选项
        initialView: 'dayGridMonth', // 初始视图为月视图
        locale: 'zh-cn' // 设置语言为中文
      });
      calendar.render(); // 渲染日历
    });
  </script>
</body>
</html>

解释:

  • DOMContentLoaded:确保 DOM 完全加载后再执行 JavaScript 代码。
  • document.getElementById('calendar-container'):获取我们创建的日历容器。
  • new FullCalendar.Calendar(calendarEl, { ... }):这是 FullCalendar 的核心构造函数,它接收两个参数:容器元素和配置对象。
  • initialView: 'dayGridMonth':设置日历的初始显示模式为“月视图”。
  • locale: 'zh-cn':设置日历的语言为简体中文,你需要引入相应的语言包(CDN 方式会自动处理)。
  • calendar.render():执行此方法后,日历才会被真正渲染到页面上。

用浏览器打开这个 index.html 文件,你就能看到一个简单的中文月历了!


第二部分:核心配置与基本功能

初始化选项

初始化选项是一个 JavaScript 对象,用于配置日历的方方面面。

const calendar = new FullCalendar.Calendar(calendarEl, {
  // ... 其他选项
  initialView: 'dayGridWeek', // 初始视图为周视图
  headerToolbar: { // 自定义顶部工具栏
    left: 'prev,next today',
    center: 'title',
    right: 'dayGridMonth,timeGridWeek,timeGridDay'
  },
  height: 'auto', // 设置日历高度
  editable: true, // 允许事件拖拽和调整大小
  selectable: true, // 允许选择日期范围
  nowIndicator: true // 显示当前时间指示线
});

、视图和按钮

通过 headerToolbar 选项可以高度自定义顶部导航栏。

headerToolbar: {
  left: 'prev,next today myCustomButton', // 左侧按钮
  center: 'title', // 中间显示当前月份/周/日
  right: 'dayGridMonth,timeGridWeek,timeGridDay' // 右侧视图切换按钮
}
  • prev, next, today:内置的上一页、下一页、今天按钮。:显示当前视图的标题(如 "2025年10月")。
  • dayGridMonth, timeGridWeek, timeGridDay:视图切换按钮,按钮文本与视图名称相同。
  • 你也可以自定义按钮,myCustomButton,然后通过 customButtons 选项来定义它的行为。

事件的基本使用

事件是日历的核心数据,最简单的方式是在初始化时直接提供一个事件数组。

const calendar = new FullCalendar.Calendar(calendarEl, {
  initialView: 'dayGridMonth',
  events: [ // 事件数组
    {
      title: '团队会议',
      start: '2025-10-10T10:00:00',
      end: '2025-10-10T12:00:00'
    },
    {
      title: '项目截止日期',
      start: '2025-10-15',
      color: 'red' // 设置事件背景色
    },
    {
      title: '午餐约会',
      start: '2025-10-12',
      allDay: false // 表示这是一个全天事件(默认为 true)
    }
  ]
});

事件对象详解

一个事件对象可以包含很多属性,以下是常用属性:

| 属性 | 类型 | 描述 | | :--- | :--- | :--- | | string | 事件的标题(显示在日历上)。 | | start | Datestring | 事件的开始时间,可以是 Date 对象或 ISO8601 格式的字符串(如 '2025-10-10T10:00:00')。 | | end | Datestring | 事件的结束时间,如果未提供,则默认与 start 相同。 | | allDay | boolean | 是否为全天事件,默认为 true,如果为 false,则会显示在时间视图中。 | | color / backgroundColor | string | 事件的背景颜色,可以是颜色名('red')、十六进制('#ff0000')或 RGB。 | | textColor | string | 事件文本的颜色。 | | url | string | 点击事件时跳转的链接。 | | editable | boolean | 是否可以拖拽或调整此事件的大小,默认为 true(如果日历的 editabletrue)。 | | extendedProps | object | 一个自定义对象,可以存放任何你需要的额外数据。 |


第三部分:事件交互与数据源

事件操作:点击、拖拽、调整大小

要启用这些交互功能,只需在初始化选项中设置相应的标志。

const calendar = new FullCalendar.Calendar(calendarEl, {
  editable: true, // 允许事件拖拽和调整大小
  selectable: true, // 允许在日历上拖动鼠标来选择日期范围
  selectMirror: true, // 选择时显示一个镜像事件
  dayMaxEvents: true, // 当某天的事件过多时,显示 "+x more" 按钮
  // ... 其他选项
});

监听事件:

FullCalendar 提供了丰富的事件回调函数,让你可以在用户操作时执行自定义逻辑。

const calendar = new FullCalendar.Calendar(calendarEl, {
  // ... 其他选项
  dateClick: function(info) { // 监听日期点击
    alert('点击的日期是: ' + info.dateStr);
    console.log(info); // info 对象包含丰富的信息,如点击的日期、日期对象、JS 事件等
  },
  eventClick: function(info) { // 监听事件点击
    if (confirm(`你确定要删除事件 "${info.event.title}" 吗?`)) {
      info.event.remove(); // 删除事件
    }
  },
  eventDrop: function(info) { // 监听事件拖拽
    alert(`事件 "${info.event.title}" 被移动到了 ${info.event.start.toISOString()}`);
    // 在这里发送 AJAX 请求到后端,更新数据库
  },
  eventResize: function(info) { // 监听事件调整大小
    alert(`事件 "${info.event.title}" 的时间范围被调整为 ${info.event.start.toISOString()} - ${info.event.end.toISOString()}`);
    // 在这里发送 AJAX 请求到后端,更新数据库
  }
});

使用外部数据源

在实际应用中,事件数据通常来自服务器,FullCalendar 通过 events 选项可以轻松实现。

events 选项可以接受一个 URL 字符串,FullCalendar 会自动向该 URL 发送 GET 请求来获取事件数据。

后端 API 示例 (Node.js/Express):

// server.js
const express = require('express');
const app = express();
const port = 3000;
app.get('/api/events', (req, res) => {
  // 模拟从数据库获取数据
  const events = [
    { id: 1, title: '远程会议', start: '2025-10-11T09:00:00', end: '2025-10-11T10:00:00' },
    { id: 2, title: '代码审查', start: '2025-10-12T14:00:00', end: '2025-10-12T15:30:00' },
  ];
  res.json(events);
});
app.listen(port, () => {
  console.log(`Server listening at http://localhost:${port}`);
});

前端代码:

const calendar = new FullCalendar.Calendar(calendarEl, {
  initialView: 'dayGridWeek',
  events: '/api/events' // 直接指向你的 API 端点
});

数据格式要求:

FullCalendar 期望从 API 获取的是一个 JSON 数组,数组中的每个对象都遵循我们之前提到的事件对象格式

事件渲染函数:eventContent

如果你想对事件进行更复杂的自定义渲染,而不仅仅是改变颜色,可以使用 eventContent 函数。

const calendar = new FullCalendar.Calendar(calendarEl, {
  // ... 其他选项
  eventContent: function(arg) { // arg 是事件对象
    let eventTitle = arg.event.title;
    // 根据事件标题返回不同的内容
    if (eventTitle.includes('重要')) {
      return {
        html: `<div class="important-event">
                 <i class="fas fa-exclamation-circle"></i>
                 <b>${eventTitle}</b>
               </div>`
      };
    } else {
      return {
        html: `<div class="normal-event">${eventTitle}</div>`
      };
    }
  }
});

你需要在 HTML 中引入 Font Awesome 等图标库来使用 i 标签。eventContent 函数必须返回一个包含 htmldomNodes 属性的对象。


第四部分:高级功能与自定义

自定义视图

除了内置的视图,你还可以组合视图来创建自定义视图,创建一个左侧为月视图,右侧为周视图的布局。

import { Calendar } from '@fullcalendar/react'; // 如果你使用 React
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import interactionPlugin from '@fullcalendar/interaction'; // 包含拖拽等交互功能
// 在初始化时传入插件
const calendar = new FullCalendar.Calendar(calendarEl, {
  plugins: [dayGridPlugin, timeGridPlugin, interactionPlugin],
  initialView: 'dayGridMonth', // 默认视图
  views: {
    customTwoDayView: { // 自定义视图名称
      type: 'dayGrid', // 基于哪个视图类型
      duration: { days: 2 } // 显示两天
    },
    customWeekSideBar: { // 一个更复杂的例子
      type: 'dayGridMonth', // 基于月视图
      sideContent: { // 在日历右侧添加内容
        html: '<div>这里是侧边栏内容</div>'
      },
      sideContentOverlap: false // 确保侧边栏不覆盖日历
    }
  }
});

日期点击与范围选择

  • selectAllow: 一个函数,用于判断用户是否可以选择某个日期范围。
  • select: 当用户选择日期范围后触发的回调。
const calendar = new FullCalendar.Calendar(calendarEl, {
  selectable: true,
  selectMirror: true,
  select: function(info) { // info 包含 start, end, allDay 等
    const title = prompt('请输入新事件的标题:');
    if (title) {
      calendar.addEvent({ title: title, start: info.start, end: info.end, allDay: info.allDay });
    }
    calendar.unselect(); // 清除选择状态
  },
  selectAllow: function(selectInfo) {
    // 不允许选择过去的日期
    return selectInfo.start >= new Date();
  }
});

插件系统

FullCalendar 的许多高级功能(如时间线视图、Google Calendar 集成)都是以插件形式存在的,使用插件前必须先安装并导入。

示例:使用 @fullcalendar/timegrid 插件

npm install @fullcalendar/timegrid
import { Calendar } from '@fullcalendar/react';
import timeGridPlugin from '@fullcalendar/timegrid';
import dayGridPlugin from '@fullcalendar/daygrid';
// 在组件或初始化代码中
<Calendar
  plugins={[ dayGridPlugin, timeGridPlugin ]}
  initialView="timeGridWeek"
/>

国际化

FullCalendar 支持多种语言,你需要引入对应的语言包。

CDN 方式:

<script src='https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/locales/zh-cn.global.min.js'></script>

npm 方式:

import { zh-cn } from '@fullcalendar/locales';
// ...
new FullCalendar.Calendar(calendarEl, {
  locale: zh-cn, // 直接导入的语言对象
  // ...
});

主题与样式定制

FullCalendar 提供了开箱即用的主题,可以让你轻松匹配 UI 框架。

  • Bootstrap 5 主题@fullcalendar/bootstrap5
  • Material (MUI) 主题@fullcalendar/material
  • Tailwind CSS 主题@fullcalendar/tailwind

示例:使用 Tailwind CSS 主题

npm install @fullcalendar/tailwind
import { Calendar } from '@fullcalendar/react';
import tailwindTheme from '@fullcalendar/tailwind';
import dayGridPlugin from '@fullcalendar/daygrid';
// ...
<Calendar
  plugins={[ dayGridPlugin, tailwindTheme ]}
  initialView="dayGridMonth"
/>

你还需要确保你的项目已经正确配置了 Tailwind CSS。


第五部分:实战案例:构建一个任务管理系统

我们将创建一个简单的任务管理系统,用户可以在日历上添加、查看和删除任务。

需求:

  1. 初始显示月视图。
  2. 点击任意一天,弹窗输入任务标题并添加。
  3. 任务以不同颜色显示。
  4. 点击任务可以删除它。
  5. 任务数据存储在浏览器的 localStorage 中。

实现步骤:

  1. HTML 结构 (index.html):

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <title>FullCalendar 任务管理</title>
      <link href='https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/index.global.min.css' rel='stylesheet' />
      <style>
        body { font-family: Arial, sans-serif; }
        #calendar { max-width: 900px; margin: 40px auto; }
        .modal {
          display: none;
          position: fixed;
          z-index: 1;
          left: 0;
          top: 0;
          width: 100%;
          height: 100%;
          background-color: rgba(0,0,0,0.4);
        }
        .modal-content {
          background-color: #fefefe;
          margin: 15% auto;
          padding: 20px;
          border: 1px solid #888;
          width: 80%;
          max-width: 300px;
        }
      </style>
    </head>
    <body>
      <div id='calendar'></div>
      <!-- 添加任务的模态框 -->
      <div id="taskModal" class="modal">
        <div class="modal-content">
          <h2>添加新任务</h2>
          <input type="text" id="taskTitleInput" placeholder="输入任务标题">
          <button id="addTaskBtn">添加</button>
          <button id="cancelBtn">取消</button>
        </div>
      </div>
      <script src='https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/index.global.min.js'></script>
      <script src="app.js"></script>
    </body>
    </html>
  2. JavaScript 逻辑 (app.js):

    document.addEventListener('DOMContentLoaded', function() {
      const calendarEl = document.getElementById('calendar');
      const modal = document.getElementById('taskModal');
      const taskInput = document.getElementById('taskTitleInput');
      const addTaskBtn = document.getElementById('addTaskBtn');
      const cancelBtn = document.getElementById('cancelBtn');
      let selectedDate = null;
      // 从 localStorage 加载事件
      function loadEvents() {
        const storedEvents = localStorage.getItem('calendarEvents');
        return storedEvents ? JSON.parse(storedEvents) : [];
      }
      // 保存事件到 localStorage
      function saveEvents(events) {
        localStorage.setItem('calendarEvents', JSON.stringify(events));
      }
      const calendar = new FullCalendar.Calendar(calendarEl, {
        initialView: 'dayGridMonth',
        locale: 'zh-cn',
        editable: true,
        selectable: true,
        events: loadEvents(), // 初始加载事件
        // 点击日期时,打开模态框
        dateClick: function(info) {
          selectedDate = info.dateStr;
          modal.style.display = 'block';
          taskInput.value = '';
          taskInput.focus();
        },
        // 点击事件时,删除任务
        eventClick: function(info) {
          if (confirm(`确定要删除任务 "${info.event.title}" 吗?`)) {
            const events = calendar.getEvents();
            const eventToRemove = events.find(e => e.id === info.event.id);
            if (eventToRemove) {
              eventToRemove.remove();
              saveEvents(calendar.getEvents().map(e => ({
                id: e.id,
                title: e.title,
                start: e.start.toISOString().split('T')[0],
                color: e.backgroundColor
              })));
            }
          }
        },
        // 事件被拖拽或调整大小后,更新 localStorage
        eventChange: function(info) {
          saveEvents(calendar.getEvents().map(e => ({
            id: e.id,
            title: e.title,
            start: e.start.toISOString().split('T')[0],
            color: e.backgroundColor
          })));
        }
      });
      calendar.render();
      // 添加任务按钮点击事件
      addTaskBtn.addEventListener('click', function() {
        const title = taskInput.value.trim();
        if (title) {
          calendar.addEvent({
            id: String(Date.now()), // 简单生成唯一 ID
            title: title,
            start: selectedDate,
            color: '#' + Math.floor(Math.random()*16777215).toString(16) // 随机颜色
          });
          saveEvents(calendar.getEvents().map(e => ({
            id: e.id,
            title: e.title,
            start: e.start.toISOString().split('T')[0],
            color: e.backgroundColor
          })));
          modal.style.display = 'none';
        }
      });
      // 取消按钮点击事件
      cancelBtn.addEventListener('click', function() {
        modal.style.display = 'none';
      });
      // 点击模态框外部关闭
      window.addEventListener('click', function(event) {
        if (event.target == modal) {
          modal.style.display = 'none';
        }
      });
    });

这个案例涵盖了日历的核心交互,并与本地存储结合,形成了一个可用的迷你应用。


第六部分:常见问题与最佳实践

常见问题解答

Q: 为什么我的日历显示不出来? A: 最常见的原因是没有给容器设置高度,日历需要一个明确的高度才能渲染,请确保你的 CSS 中 #calendar-containerheightmax-height 属性。

Q: 如何在 React/Vue 中使用 FullCalendar? A: FullCalendar 官方为这些框架提供了专门的封装包(@fullcalendar/react, @fullcalendar/vue),使用它们可以更方便地管理日历的生命周期和状态,请查阅官方文档获取相应框架的集成指南。

Q: 如何处理海量事件数据? A: 如果事件数量非常大(例如超过 1000 条),一次性渲染所有事件会导致性能问题,解决方案是:

  1. 分页/懒加载:根据当前视图的范围(如当前月)向后端请求该范围内的事件。
  2. 使用 eventSources:将 events 替换为 eventSources,它是一个数组,可以包含多个数据源,每个数据源都可以是一个返回 Promise 的函数,实现按需加载。

Q: 如何实现事件的“拖拽到外部”? A: 这是一个高级功能,你需要监听 eventDragStop 事件,然后判断事件的 DOM 元素是否已经不在日历容器内,如果是,则执行删除或移动操作。

性能优化建议

  1. 按需加载视图和插件:只引入你需要的视图(如 dayGridPlugin)和插件,不要全部引入。
  2. 使用事件渲染函数:对于有大量事件且需要自定义样式的场景,使用 eventContenteventDidMount/eventWillUnmount 钩子比操作 DOM 更高效。
  3. 优化数据请求:确保从服务器获取的事件数据是精简的,只包含必要的字段,避免在每次渲染时都重新请求数据。
  4. 避免频繁的状态更新:在使用 React/Vue 等框架时,确保日历组件的 key 属性稳定,避免不必要的重新渲染。
分享:
扫描分享到社交APP
上一篇
下一篇