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

Laravel9+FullCalendar+Livewire:模态框增改日程数据求助

Laravel 9 + Livewire + FullCalendar 5 实现日程新增与修改功能

我已经在Laravel 9项目中完成了FullCalendar 5与Livewire组件的集成,实现了以下功能:

  • 点击日历空白日期触发select函数弹出模态框
  • 点击已有日程触发eventClick函数弹出同一模态框,且能将数据库中的日程数据填充到模态框输入框内

现在需要实现模态框中保存按钮的逻辑,分别通过updateEvent和addEvent函数完成日程的修改与新增操作。


一、统一模态框(livewire/eventmodal.blade.php)

将原有的两个模态框合并为一个,新增隐藏的event_id字段用于区分新增/修改操作:

<div class="modal fade" id="eventModal" tabindex="-1" aria-labelledby="eventModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="eventModalLabel">@if($event_id) 修改日程 @else 新增日程 @endif</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <form wire:submit.prevent="@if($event_id) updateEvent @else saveEvent @endif">
                <div class="modal-body">
                    <input type="hidden" wire:model="event_id">
                    <div class="mb-3">
                        <label for="title" class="form-label">标题</label>
                        <input type="text" class="form-control" id="title" wire:model="title">
                        @error('title') <span class="text-danger">{{ $message }}</span> @enderror
                    </div>
                    <div class="mb-3">
                        <label for="acronym" class="form-label">缩写</label>
                        <input type="text" class="form-control" id="acronym" wire:model="acronym">
                    </div>
                    <div class="mb-3">
                        <label for="city" class="form-label">城市</label>
                        <input type="text" class="form-control" id="city" wire:model="city">
                    </div>
                    <div class="mb-3">
                        <label for="venue" class="form-label">地点</label>
                        <input type="text" class="form-control" id="venue" wire:model="venue">
                    </div>
                    <div class="mb-3">
                        <label for="value" class="form-label">值</label>
                        <input type="text" class="form-control" id="value" wire:model="value">
                    </div>
                    <div class="mb-3">
                        <label for="start" class="form-label">开始时间</label>
                        <input type="datetime-local" class="form-control" id="start" wire:model="start">
                        @error('start') <span class="text-danger">{{ $message }}</span> @enderror
                    </div>
                    <div class="mb-3">
                        <label for="end" class="form-label">结束时间</label>
                        <input type="datetime-local" class="form-control" id="end" wire:model="end">
                    </div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
                    <button type="submit" class="btn btn-primary">保存</button>
                </div>
            </form>
        </div>
    </div>
</div>

二、修改Livewire Calendar组件

添加必要属性、验证规则、updateEvent方法,完善日期处理逻辑:

<?php

namespace App\Http\Livewire;

use Livewire\Component;
use App\Models\Event;
use App\Models\Resource;
use Illuminate\Support\Arr;
use Carbon\Carbon;

class Calendar extends Component
{
    public $events, $resources;
    public $event_id, $title, $acronym, $city, $venue, $value, $start, $end;

    // 验证规则
    protected $rules = [
        'title' => 'required|string|max:255',
        'start' => 'required|date',
    ];

    public function resetInput()
    {
        $this->event_id = null;
        $this->title = '';
        $this->acronym = '';
        $this->city = '';
        $this->venue = '';
        $this->value = '';
        $this->start = '';
        $this->end = '';
    }

    public function saveEvent()
    {
        $this->validate();

        $eventData = [
            'title' => $this->title,
            'start' => Carbon::parse($this->start)->toIso8601String(),
            'extendedProps' => [
                'acronym' => $this->acronym,
                'city' => $this->city,
                'venue' => $this->venue,
                'value' => $this->value
            ]
        ];

        if ($this->end) {
            $eventData['end'] = Carbon::parse($this->end)->toIso8601String();
        }

        Event::create($eventData);

        session()->flash('message', '日程新增成功');
        $this->resetInput();
        $this->dispatchBrowserEvent('close-modal');
    }

    public function updateEvent()
    {
        $this->validate();

        $event = Event::find($this->event_id);
        if (!$event) {
            session()->flash('error', '日程不存在');
            return;
        }

        $eventData = [
            'title' => $this->title,
            'start' => Carbon::parse($this->start)->toIso8601String(),
            'extendedProps' => [
                'acronym' => $this->acronym,
                'city' => $this->city,
                'venue' => $this->venue,
                'value' => $this->value
            ]
        ];

        if ($this->end) {
            $eventData['end'] = Carbon::parse($this->end)->toIso8601String();
        }

        $event->update($eventData);

        session()->flash('message', '日程修改成功');
        $this->resetInput();
        $this->dispatchBrowserEvent('close-modal');
        $this->events = json_encode(Event::all());
    }

    public function eventChange($event)
    {
        $e = Event::find($event['id']);
        if (!$e) return;

        $e->start = $event['start'];
        if (Arr::exists($event, 'end')) {
            $e->end = $event['end'];
        }
        $e->save();
    }

    public function eventAdd($event)
    {
        Event::create($event);
    }

    public function eventRemove($id)
    {
        Event::destroy($id);
    }

    public function resourceAdd($resources)
    {
        Resource::create($resources);
    }

    public function resourceRemove($id)
    {
        Resource::destroy($id);
    }

    public function render()
    {
        $this->events = json_encode(Event::all());
        $this->resources = json_encode(Resource::all());
        return view('livewire.calendar');
    }
}

三、修改FullCalendar的JS逻辑

统一模态框调用逻辑,实现Livewire数据同步,添加日历刷新逻辑:

@push('scripts')
<script src='https://cdn.jsdelivr.net/npm/fullcalendar-scheduler@5.10.1/main.min.js'></script>
<script>
create_UUID = () => {
let dt = new Date().getTime();
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
    let r = (dt + Math.random() * 16) % 16 | 0;
    dt = Math.floor(dt / 16);
    return (c == 'x' ? r :(r&0x3|0x8)).toString(16);
});
return uuid;
}
document.addEventListener('livewire:load', function () {
const Calendar = FullCalendar.Calendar;
const calendarEl = document.getElementById('calendar');
const Draggable = FullCalendar.Draggable;
    new Draggable(document.getElementById('events'), {
        itemSelector: '.dropEvent'
    });
const calendar = new Calendar(calendarEl, {
  headerToolbar: {
        left: 'promptResource prev,next today',
        center: 'title',
        right: 'resourceMonth,dayGridMonth,timeGridWeek,timeGridDay,listMonth'
      },
      views: {
        resourceMonth: {
        type: 'resourceTimelineMonth',
        buttonText: '人员'
        }
      },
      customButtons: {
        promptResource: {
          text: "+ 人员",
      click: function() {
        var title = prompt("姓名");
        if (title) {
          calendar.addResource({
            title: title
          });
          fetch("add_resources.php", {
            method: "POST",
            headers: {
              Accept: "application/json"
            },
            body: new URLSearchParams({ title: title })
          })
            .then(response => console.log(response))
            .catch(error => console.log(error));
        }
      }
    }
  },
    initialView: 'resourceTimelineMonth',
    locale: '{{ config('app.locale') }}',
    events: JSON.parse(@this.events),
    resourceAreaHeaderContent: '人员',
    resources: JSON.parse(@this.resources),
    editable: true,
    eventResize: info => @this.eventChange(info.event),
    eventDrop: info => @this.eventChange(info.event),
    eventReceive: info => {
        const id = create_UUID();
          info.event.setProp('id', id);
          @this.eventAdd(info.event);
    },
    selectable: true,
    select: function(info) {
          @this.resetInput();
          @this.set('start', info.startStr);
          if (info.endStr) {
              @this.set('end', info.endStr);
          }
          $('#eventModal').modal('show');
    },

    eventClick: function(info) {
          @this.set('event_id', info.event.id);
          @this.set('title', info.event.title);
          @this.set('acronym', info.event.extendedProps.acronym);
          @this.set('city', info.event.extendedProps.city);
          @this.set('venue', info.event.extendedProps.venue);
          @this.set('value', info.event.extendedProps.value);
          @this.set('start', info.event.startStr);
          if (info.event.endStr) {
              @this.set('end', info.event.endStr);
          }
          $('#eventModal').modal('show');
    },
});
calendar.render();

document.addEventListener('close-modal', function() {
    $('#eventModal').modal('hide');
    calendar.refetchEvents();
});
});
</script>
<link href='https://cdn.jsdelivr.net/npm/fullcalendar-scheduler@5.11.3/main.min.css' rel='stylesheet' />
@endpush

功能说明

  1. 模态框区分操作:通过event_id字段自动判断调用新增/修改方法
  2. 数据双向同步:使用wire:model绑定表单字段,实现Livewire与视图的数据同步
  3. 日期格式兼容:将用户输入的日期转换为ISO8601格式,确保FullCalendar正确解析
  4. 实时刷新日历:保存完成后通过refetchEvents()刷新日历,展示最新日程

内容的提问来源于stack exchange,提问作者Steve Adams

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 18:51:20