You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FullCalendar中如何按条件将拖拽事件重置回原始位置

FullCalendar拖拽确认+回退实现方案

你存储拖拽前后位置的思路可行,但FullCalendar本身提供了内置的位置回退能力,不需要手动计算、修改事件属性回退,稳定性更高,具体实现如下:

核心原理

FullCalendar的拖拽相关回调自带操作回滚方法:

  • 日历内部已有事件拖拽完成触发eventDrop回调时,回调参数自带revert()方法,调用后事件会直接回到拖拽前的原始位置,自动还原时间、所属资源等所有属性
  • 外部元素拖入日历生成新事件触发eventReceive回调时,拿到的临时事件对象自带remove()方法,调用后会直接删除刚拖入的未确认事件
  • 事件缩放调整大小触发eventResize回调时,参数同样自带revert()方法,回退逻辑和拖拽完全一致

具体实现步骤

  1. 新增ref存储拖拽临时数据和日历实例,避免无关重渲染
// 组件内新增
const calendarRef = useRef(null);
const draggedEventRef = useRef(null);

给FullCalendar组件绑定ref:

<FullCalendar
  ref={calendarRef}
  // 其余原有配置不变
/>
  1. 改造拖拽相关回调,暂存拖拽信息、触发确认弹窗
<FullCalendar
  // 原有配置不变
  // 拖拽开始时暂存原始事件信息(做额外校验时可以用)
  eventDragStart={(e) => {
    draggedEventRef.current = {
      id: e.event.id,
      oldStart: e.event.start,
      oldEnd: e.event.end,
      oldResourceId: e.event.getResources()[0]?.id
    }
  }}
  // 内部事件拖拽完成
  eventDrop={(drop) => {
    // 弹出确认框
    setShowModal(true);
    // 暂存回调和新位置信息,等用户操作
    draggedEventRef.current = {
      ...draggedEventRef.current,
      dropInfo: drop,
      newStart: drop.event.start,
      newEnd: drop.event.end,
      newResourceId: drop.event.getResources()[0]?.id,
      type: 'inner'
    }
  }}
  // 外部事件拖入日历
  eventReceive={(e) => {
    setShowModal(true);
    draggedEventRef.current = {
      event: e.event,
      type: 'external'
    }
  }}
  // 事件缩放调整大小
  eventResize={(resize) => {
    setShowModal(true);
    draggedEventRef.current = {
      resizeInfo: resize,
      type: 'resize'
    }
  }}
/>
  1. 编写弹窗确认/取消的处理逻辑
// 确认拖拽/缩放
const handleConfirmChange = () => {
  const current = draggedEventRef.current;
  if (current.type === 'inner') {
    // 内部事件移动:更新本地状态
    setTableState(prev => prev.map(item => {
      if (item.id === current.id) {
        return {
          ...item,
          start: current.newStart,
          end: current.newEnd,
          resourceId: current.newResourceId
        }
      }
      return item
    }))
    // 这里可调用后端接口同步更新
  } else if (current.type === 'external') {
    // 外部事件拖入:新增到本地状态
    const newEvent = {
      id: Date.now(), // 生成正式业务id
      title: current.event.title,
      start: current.event.start,
      end: current.event.end,
      resourceId: current.event.getResources()[0]?.id
    }
    setTableState(prev => [...prev, newEvent])
    // 移除临时渲染的外部事件
    current.event.remove();
    // 这里可调用后端接口新增事件
  } else if (current.type === 'resize') {
    // 缩放逻辑和内部拖拽类似,更新对应事件的start/end即可
    const resizeEvent = current.resizeInfo.event;
    setTableState(prev => prev.map(item => {
      if (item.id === resizeEvent.id) {
        return {
          ...item,
          start: resizeEvent.start,
          end: resizeEvent.end
        }
      }
      return item
    }))
  }
  setShowModal(false);
}

// 取消拖拽/缩放
const handleCancelChange = () => {
  const current = draggedEventRef.current;
  if (current.type === 'inner') {
    // 调用内置方法直接回退位置
    current.dropInfo.revert();
  } else if (current.type === 'external') {
    // 删除临时拖入的事件
    current.event.remove();
  } else if (current.type === 'resize') {
    // 缩放回退同样用内置revert
    current.resizeInfo.revert();
  }
  setShowModal(false);
}
  1. 将两个处理函数绑定到你的AlertBox组件的确认、取消按钮事件上即可。

注意事项

  • 不要在eventDrop/eventReceive/eventResize回调触发时立刻更新tableState,否则会和内置回退逻辑冲突导致位置错乱,等用户确认后再更新状态
  • 拖拽过程中的临时数据全部存在useRef中即可,不需要存到state,避免日历组件不必要的重渲染
  • 该方案兼容FullCalendar v5、v6版本,不需要额外引入插件

内容的提问来源于stack exchange,提问作者Bilal Ashraf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 03:33:08