导读:本期聚焦于缅甸程序员创作的《如何使用jQuery FullCalendar插件实现实现日程拖拽与事件持久化存储》,敬请观看详情。为什么把日程拖到另一天后,刷新页面事件又回到了原位?根本原因在于拖拽只改变了前端视图,变更没有同步到后端数据库。FullCalendar提供了eventDrop和eventResize等回调接口,可以在用户拖动或缩放事件时拿到事件ID和新的起止时间,再通过Ajax提交给服务器写入存储,才能实现真正的持久化。本文围绕jQuery环境下FullCalendar的集成展开,先介绍插件的引入与初始化配置,重点分析editable、eventDrop、eventResize、dayClick等关键参数的用法,随后给出后端更新接口的设计思路与数据库字段建议,最后整理拖拽不生效、时区偏移、保存失败回滚等常见问题的排查办法,帮你搭建一套完整可用的日程管理系统。

FullCalendar是jQuery生态里最经典的日历组件之一,它不仅渲染效果好,还内置了事件拖拽、事件缩放、日期点击等交互能力,非常适合用来做会议室预订、个人日程、排班系统这类应用。不过很多初学者会发现一个现象:把日程从一个日期拖到另一个日期,页面上的显示确实变了,可是一刷新,事件又回到了原来的位置。这是因为拖拽只修改了前端数据,服务器根本不知道这次变更。本文就围绕这个核心问题,讲清楚如何在jQuery项目中接入FullCalendar,并把拖拽产生的变更可靠地持久化到后端。

如何使用jQuery FullCalendar插件实现实现日程拖拽与事件持久化存储

一、引入插件并完成基础初始化

FullCalendar的引入方式有两种,一种是通过CDN直接引用css和js文件,另一种是npm安装后打包引入。对于传统的jQuery项目,CDN方式最省事。需要注意的是,FullCalendar从v3升级到v5之后API变化很大,v5改用ES模块导出,全局初始化写法变成了FullCalendar.Calendar,而不再支持$('#calendar').fullCalendar()这种链式调用。如果你的项目是老系统改造,建议直接使用v3.10版本,它的jQuery风格API与大量网上资料一致,踩坑最少。

以v3为例,引入资源后,先准备一个容器div,再在document ready里初始化。最简配置包含defaultView(视图类型)、events(事件数据源)和header(头部按钮),代码如下:

<link href="https://cdn.jsdelivr.net/npm/fullcalendar@3.10.5/dist/fullcalendar.min.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@3.10.5/dist/fullcalendar.min.js"></script>

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

<script>
$(function () {
    $('#calendar').fullCalendar({
        defaultView: 'month',
        header: {
            left: 'prev,next today',
            center: 'title',
            right: 'month,agendaWeek,agendaDay'
        },
        // 事件数据从后端接口加载
        events: '/api/events',
        editable: true
    });
});
</script>
</script>

这里的events参数很灵活,可以直接传数组、JSON接口地址,也可以传一个函数自己控制Ajax请求。对于日程管理系统,推荐传接口地址,让后端返回标准的JSON数组,每个事件至少包含id、title、start三个字段,如果有结束时间再加end。后端返回的时间格式建议统一为YYYY-MM-DD HH:mm:ss,FullCalendar可以正常解析。

二、开启拖拽并捕获eventDrop与eventResize回调

拖拽能力的总开关是editable: true,不设置这个参数,事件在页面上是拖不动的。开启之后还要注意两个细分的开关:eventStartEditable控制事件能否被拖动到别的日期,eventDurationEditable控制事件能否被拉伸改变时长。有些系统希望允许移动但不允许改时长,就可以单独把eventDurationEditable设为false,这种精细控制在排班场景里很实用。

用户把事件拖到新位置后,组件会触发eventDrop回调;拉伸事件的边缘改变起止时间,则触发eventResize。这两个回调都会把事件对象、新的起止时间以及revertFunc回滚函数交给你。持久化的关键就在这两个回调里:先通过Ajax把新时间提交给后端,如果后端返回失败,就调用revertFunc()把事件弹回原位,这样用户能立刻感知保存失败,而不是等刷新后才发现数据丢了。

$('#calendar').fullCalendar({
    defaultView: 'month',
    events: '/api/events',
    editable: true,

    // 拖动事件到新的日期或时间
    eventDrop: function (event, delta, revertFunc) {
        $.ajax({
            url: '/api/events/update',
            type: 'POST',
            dataType: 'json',
            data: {
                id: event.id,
                // moment格式化,保证后端拿到标准时间字符串
                start: event.start.format('YYYY-MM-DD HH:mm:ss'),
                end: event.end ? event.end.format('YYYY-MM-DD HH:mm:ss') : null
            },
            success: function (res) {
                if (res.code !== 0) {
                    revertFunc(); // 保存失败,把事件还原到拖拽前的位置
                    alert(res.msg || '保存失败,请重试');
                }
            },
            error: function () {
                revertFunc();
                alert('网络异常,变更未保存');
            }
        });
    },

    // 拉伸边缘改变事件时长
    eventResize: function (event, delta, revertFunc) {
        $.ajax({
            url: '/api/events/update',
            type: 'POST',
            data: {
                id: event.id,
                start: event.start.format('YYYY-MM-DD HH:mm:ss'),
                end: event.end.format('YYYY-MM-DD HH:mm:ss')
            },
            success: function (res) {
                if (res.code !== 0) {
                    revertFunc();
                }
            },
            error: function () {
                revertFunc();
            }
        });
    }
});

除了拖拽已有事件,新建日程一般通过dayClickeventClick实现:前者在用户点击空白日期格子时弹出表单,把选中的日期作为默认开始时间;后者在点击事件时打开编辑弹窗。两个回调配合Bootstrap的模态框或者Layer弹层,就能完成日程的增删改闭环。要注意dayClick回调里的date参数是一个moment对象,直接date.format()即可得到用户点击的时间点。

三、后端接口设计与数据持久化

前端的持久化只是把数据发出去,真正落库还得靠后端。事件表的设计建议包含以下字段:id(主键)、title(标题)、start_time(开始时间)、end_time(结束时间)、all_day(是否全天事件)、color(自定义颜色)、user_id(归属人)以及created_at、updated_at两个时间戳。其中all_day字段不要省略,FullCalendar在月视图下把事件拖成全天事件时,event.allDay会变为true,此时end的语义是排他的,后端存储时要处理好这种差异。

更新接口的逻辑很简单:接收id和新的起止时间,更新对应记录。以PHP配合MySQL为例,接口代码可以这样写:

<?php
// /api/events/update
$data = json_decode(file_get_contents('php://input'), true);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $data = $_POST;
}

$id    = isset($data['id']) ? intval($data['id']) : 0;
$start = isset($data['start']) ? $data['start'] : '';
$end   = isset($data['end']) ? $data['end'] : null;

if ($id <= 0 || $start === '') {
    echo json_encode(['code' => 1, 'msg' => '参数错误']);
    exit;
}

// 使用预处理语句防止SQL注入
$stmt = $pdo->prepare("UPDATE calendar_events SET start_time = ?, end_time = ? WHERE id = ?");
$ok = $stmt->execute([$start, $end, $id]);

echo json_encode($ok ? ['code' => 0] : ['code' => 1, 'msg' => '更新失败']);

有一点经常被忽略:更新接口一定要校验当前用户是否有权修改这条事件。如果只凭前端传来的id就更新,任何登录用户都可以改别人的日程,属于典型的越权漏洞。正确做法是在WHERE条件里加上user_id,或者先查询记录归属再判断权限。另外建议给接口加乐观锁或至少记录updated_at,便于后续做冲突检测。

列表接口返回的JSON要注意格式规范,字段名与FullCalendar对应,返回前把数据库里的时间字段格式化好,前端就无需二次处理。示例如下:

$rows = $pdo->query("SELECT id, title, start_time, end_time, color FROM calendar_events WHERE user_id = {$uid}")->fetchAll(PDO::FETCH_ASSOC);

$events = array_map(function ($row) {
    return [
        'id'    => $row['id'],
        'title' => $row['title'],
        'start' => $row['start_time'],
        'end'   => $row['end_time'],
        'color' => $row['color']
    ];
}, $rows);

header('Content-Type: application/json');
echo json_encode($events);

四、常见问题排查:拖拽不生效、时区偏移与保存回滚

拖拽不生效排第一位的原因永远是忘了设置editable: true。其次是事件数据里缺少id,没有id的事件FullCalendar视为临时对象,某些版本下无法正确响应拖拽后的更新。还有一种情况是容器外层CSS设置了pointer-events: none或者被透明遮罩层盖住,导致事件元素收不到鼠标事件,用浏览器开发者工具检查元素层级即可定位。

时区问题也相当常见。典型症状是拖拽保存后刷新,事件偏移了8小时,这基本就是浏览器本地时间与服务器时区不一致导致的。解决办法有三种:初始化时设置timezone: 'local'并让后端统一存储不带时区的时间字符串;或者后端全部使用UTC存储,前端通过moment的timezone插件做转换;再或者最简单粗暴,前后端都固定用同一个时区(比如统一东八区)存取,不引入任何时区换算。对小规模内部系统,第三种方案最省心。

关于保存回滚,再补充两个实践建议。第一,Ajax请求尽量用POST并带上CSRF令牌,避免接口被跨站伪造请求调用。第二,如果业务上存在并发修改的可能,比如两个运营同时调整同一个会议的时间,可以在事件表加一个version字段,更新时校验版本号,版本不一致就返回失败并触发前端的revertFunc,同时提示用户刷新后重试。这样配合FullCalendar自带的回滚机制,就能保证界面显示与数据库状态始终一致,用户体验和数据准确性都得到了保障。

(修正:正文见上,直接输出HTML。)

FullCalendarjQuery日程管理事件拖拽修改时间:2026-09-04 15:20:56

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260904/50315.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。