簡體   English   中英

在 Laravel 9 FullCalendar 項目中使用 Livewire 模態保存和更新事件數據

[英]Saving and updating event data with a Livewire modal in Laravel 9 FullCalendar project

我設法構建了一個 Laravel 9 項目,其中 FullCalendar 5 作為 Livewire 組件。 我有一個構造的單一模態,當單擊日歷上的開放日期(觸發“選擇”功能)和單擊現有事件(觸發 eventClick 函數)時,它會被觸發。 eventClick function 在模態的輸入字段中填充來自數據庫的事件數據。

我需要幫助獲取模式中 updateEvent 和 addEvent 函數上的“保存”按鈕,以分別在數據庫中創建或編輯信息。

這是 fullCalendar 刀片格式的完整視圖:

<style>
  #calendar-container {
        display: grid;
        grid-template-columns: 200px 1fr;
        padding: 20px;
    }
    #events {
        grid-column: 1;
    }
    #calendar {
        grid-column: 2;
        height: 700px;
    }
    .dropEvent {
        background-color: DodgerBlue;
        color: white;
        padding: 5px 16px;
        margin-bottom: 10px;
        text-align: center;
        display: inline-block;
        font-size: 16px;
        border-radius: 4px;
        cursor:pointer;
    }
</style>
<div>
  @include('livewire.eventmodal')
<div>
  <!-- sidebar -->
    <div id="calendar-container" wire:ignore>
        <div id="events">
            <div data-event='{"title":"Evénement A"}' class="dropEvent">Event One Drag</div>
            <div data-event='{"title":"Evénement B"}' class="dropEvent">Event Two Draggable</div>
        </div>
        <div id="calendar"></div>
    </div>
</div>
</div>

@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: 'personnel'
        }
      },
      customButtons: {
        promptResource: {
          text: "+ personnel",
      click: function() {
        var title = prompt("Name");
        if (title) {
          calendar.addResource({
            title: title
          });
          fetch("add_resources.php", {
            method: "POST",
            headers: {
              Accept: "application/json"
            },
            body: encodeFormData({ title: title })
          })
            .then(response => console.log(response))
            .catch(error => console.log(error));
        }
      }
    }
  },
    initialView: 'resourceTimelineMonth',
    locale: '{{ config('app.locale') }}',
    events: JSON.parse(@this.events),
    resourceAreaHeaderContent: 'Personnel',
    resources: JSON.parse(@this.resources),
    //'https://fullcalendar.io/api/demo-feeds/resources.json?with-nesting&with-colors',
    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) {
          $('#eventModal').modal('show');
          },

    eventClick: function(info, event) {
          // Display the modal and set the values to the event values.
          $('#updateEventModal').modal('show');
          $('#updateEventModal').find('#title').val(info.event.title);
          $('#updateEventModal').find('#acronym').val(info.event.extendedProps.acronym);
          $('#updateEventModal').find('#city').val(info.event.extendedProps.city);
          $('#updateEventModal').find('#venue').val(info.event.extendedProps.venue);
          $('#updateEventModal').find('#value').val(info.event.extendedProps.value);
          $('#updateEventModal').find('#start').val(info.event.start);
          $('#updateEventModal').find('#end').val(info.event.end);
          $('#calendar').calendar ('updateEvent', event);
          },
});
calendar.render();
});
</script>
<link href='https://cdn.jsdelivr.net/npm/fullcalendar-scheduler@5.11.3/main.min.css' rel='stylesheet' />
@endpush

這是日歷組件:

<?php

namespace App\Http\Livewire;

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

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


    public function eventChange ( $event )
    {
    $e=Event::find($event['id']) ;
    $e->start=$event['start'] ;
    if(Arr::exists($event,'end')) {
        $e->end=$event['end'];
    }
    $e->save();
    }

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

            Event::create($validatedData);
            session()->flash('message','Event Added Successfully');
            $this->resetInput();
            $this->dispatchBrowserEvent('close-modal');
        }


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

我設法構建了 2 個與 livewire 組件內聯的模態。 一種用於添加新事件,另一種用於編輯現有事件。 兩個問題:

  1. 添加事件的保存操作未使用 Livewire,而是 ajax 和 jquery。
  2. 在編輯模式上提交 function 復制事件而不是編輯現有事件。

這是我的觀點:

<style>
  #calendar-container {
        display: grid;
        grid-template-columns: 200px 1fr;
        padding: 20px;
    }
    #events {
        grid-column: 1;
    }
    #calendar {
        grid-column: 2;
        height: 700px;
    }
    .dropEvent {
        background-color: DodgerBlue;
        color: white;
        padding: 5px 16px;
        margin-bottom: 10px;
        text-align: center;
        display: inline-block;
        font-size: 16px;
        border-radius: 4px;
        cursor:pointer;
    }
</style>
<div>
  @include('livewire.eventmodal')
<div>
  <!-- sidebar -->
    <div id="calendar-container" wire:ignore>
        <div id="events">
            <div data-event='{"title":"Evénement A"}' class="dropEvent">Event One Drag</div>
            <div data-event='{"title":"Evénement B"}' class="dropEvent">Event Two Draggable</div>
        </div>
        <div id="calendar"></div>
    </div>
</div>
<!-- start add event modal -->
<div id="AddEventModal" class="modal fade">
  <form action="/events" method="POST">
    {{csrf_field()}}
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h4>Add Event</h4> &nbsp;
                <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span> <span class="sr-only">close</span></button>
            </div>
            <!-- <form wire:submit.prevent="saveEvent"> -->
            <div id="modalBody" class="modal-body">
              <div class="bg-white sm:p-6">
                  <label for="title" class="block font-medium text-sm text-gray-700">Title</label>
                  <input type="text" name="title" id="title" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
                         value="{{ old('title', '') }}" />
                  @error('title')
                      <p class="text-sm text-red-600">{{ $message }}</p>
                  @enderror
              </div>
              <div class="bg-white sm:p-6">
                  <label for="acronym" class="block font-medium text-sm text-gray-700">Acronym</label>
                  <input type="text" name="acronym" id="acronym" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
                         value="{{ old('acronym', '') }}" />
                  @error('acronym')
                      <p class="text-sm text-red-600">{{ $message }}</p>
                  @enderror
              </div>
              <div class="bg-white sm:p-6">
                  <label for="city" class="block font-medium text-sm text-gray-700">City</label>
                  <input type="text" name="city" id="city" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
                         value="{{ old('city', '') }}" />
                  @error('city')
                      <p class="text-sm text-red-600">{{ $message }}</p>
                  @enderror
              </div>
              <div class="bg-white sm:p-6">
                  <label for="venue" class="block font-medium text-sm text-gray-700">Venue</label>
                  <input type="text" name="venue" id="venue" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
                         value="{{ old('venue', '') }}" />
                  @error('venue')
                      <p class="text-sm text-red-600">{{ $message }}</p>
                  @enderror
              </div>
              <div class="bg-white sm:p-6">
                  <label for="value" class="block font-medium text-sm text-gray-700">Value</label>
                  <input type="text" name="value" id="value" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
                         value="{{ old('value', '') }}" />
                  @error('value')
                      <p class="text-sm text-red-600">{{ $message }}</p>
                  @enderror
              </div>
              <div class="bg-white sm:p-6">
                  <label for="start" class="block font-medium text-sm text-gray-700">Start Date</label>
                  <input type="text" name="start" id="start" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
                         value="{{ old('start', '') }}" />
                  @error('start')
                      <p class="text-sm text-red-600">{{ $message }}</p>
                  @enderror
              </div>
              <div class="bg-white sm:p-6">
                  <label for="end" class="block font-medium text-sm text-gray-700">End Date</label>
                  <input type="text" name="end" id="end" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
                         value="{{ old('end', '') }}" />
                  @error('end')
                      <p class="text-sm text-red-600">{{ $message }}</p>
                  @enderror
              </div>
            </div>
            <div class="modal-footer">
                <button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>
                <button type="submit" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#AddEventModal" id="submit">Save</button>
            </div>
          </form>
        </div>
    </div>
</div>
<!--end add event modal -->

<!--start edit event modal -->
  <div id="EditEventModal" class="modal fade" role="dialog">
    <form action="/events" method="POST">
      {{csrf_field()}}
      <div class="modal-dialog">
          <div class="modal-content">
              <div class="modal-header">
                  <h4>Add Event</h4> &nbsp;
                  <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span> <span class="sr-only">close</span></button>
              </div>
              <!-- <form wire:submit.prevent="saveEvent"> -->
              <div id="modalBody" class="modal-body">
                <div class="bg-white sm:p-6">
                    <label for="title" class="block font-medium text-sm text-gray-700">Title</label>
                    <input type="text" name="title" id="title" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
                           value="{{ old('title', '') }}" />
                    @error('title')
                        <p class="text-sm text-red-600">{{ $message }}</p>
                    @enderror
                </div>
                <div class="bg-white sm:p-6">
                    <label for="acronym" class="block font-medium text-sm text-gray-700">Acronym</label>
                    <input type="text" name="acronym" id="acronym" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
                           value="{{ old('acronym', '') }}" />
                    @error('acronym')
                        <p class="text-sm text-red-600">{{ $message }}</p>
                    @enderror
                </div>
                <div class="bg-white sm:p-6">
                    <label for="city" class="block font-medium text-sm text-gray-700">City</label>
                    <input type="text" name="city" id="city" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
                           value="{{ old('city', '') }}" />
                    @error('city')
                        <p class="text-sm text-red-600">{{ $message }}</p>
                    @enderror
                </div>
                <div class="bg-white sm:p-6">
                    <label for="venue" class="block font-medium text-sm text-gray-700">Venue</label>
                    <input type="text" name="venue" id="venue" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
                           value="{{ old('venue', '') }}" />
                    @error('venue')
                        <p class="text-sm text-red-600">{{ $message }}</p>
                    @enderror
                </div>
                <div class="bg-white sm:p-6">
                    <label for="value" class="block font-medium text-sm text-gray-700">Value</label>
                    <input type="text" name="value" id="value" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
                           value="{{ old('value', '') }}" />
                    @error('value')
                        <p class="text-sm text-red-600">{{ $message }}</p>
                    @enderror
                </div>
                <div class="bg-white sm:p-6">
                    <label for="start" class="block font-medium text-sm text-gray-700">Start Date</label>
                    <input type="text" name="start" id="start" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
                           value="{{ old('start', '') }}" />
                    @error('start')
                        <p class="text-sm text-red-600">{{ $message }}</p>
                    @enderror
                </div>
                <div class="bg-white sm:p-6">
                    <label for="end" class="block font-medium text-sm text-gray-700">End Date</label>
                    <input type="text" name="end" id="end" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
                           value="{{ old('end', '') }}" />
                    @error('end')
                        <p class="text-sm text-red-600">{{ $message }}</p>
                    @enderror
                </div>
              </div>
              <div class="modal-footer">
                  <button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>
                  <button type="submit" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#EditEventModal" id="updateBtn">Save</button>
              </div>
            </form>
      </div>
  </div>

</div>

@push('scripts')
<script src='https://cdn.jsdelivr.net/npm/moment@2.27.0/min/moment.min.js'></script>
<script src='https://cdn.jsdelivr.net/npm/fullcalendar-scheduler@5.11.3/main.min.js'></script>
<script src='https://cdn.jsdelivr.net/npm/@fullcalendar/moment@5.11.3/main.global.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, {
  titleFormat: function(date) {
    return date.toString() + '!!!';
  },
  headerToolbar: {
        left: 'promptResource prev,next today',
        center: 'title',
        right: 'resourceMonth,dayGridMonth,timeGridWeek,timeGridDay,listMonth'
      },
      views: {
        resourceMonth: {
        type: 'resourceTimelineMonth',
        buttonText: 'personnel'
        }
      },
      customButtons: {
        promptResource: {
          text: "+ personnel",
      click: function() {
        var title = prompt("Name");
        if (title) {
          calendar.addResource({
            title: title
          });
          fetch("add_resources.php", {
            method: "POST",
            headers: {
              Accept: "application/json"
            },
            body: encodeFormData({ title: title })
          })
            .then(response => console.log(response))
            .catch(error => console.log(error));
        }
      }
    }
  },
    initialView: 'resourceTimelineMonth',
    locale: '{{ config('app.locale') }}',
    events: JSON.parse(@this.events),
    resourceAreaHeaderContent: 'Personnel',
    resources: JSON.parse(@this.resources),
    //'https://fullcalendar.io/api/demo-feeds/resources.json?with-nesting&with-colors',
    editable: true,
    //eventLimit: 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(selectionInfo) {
        $('#AddEventModal').find('#start').val(selectionInfo.start);
        $('#AddEventModal').find('#end').val(selectionInfo.end);
        //alert('selected ' + info.startStr + ' to ' + info.endStr);
        $('#AddEventModal').modal('show');

    $('#saveBtn').click(function() {
        var title = $('#title').val();
        var start = moment(start).format('YYYY-MM-DD');
        var end = moment(end).format('YYYY-MM-DD');

        $.ajax({
            url:"{{ route('event.store') }}",
            type:"POST",
            dataType:'json',
            data:{ title, acronym, city, venue, value, start, end, color  },
            success:function(response)
            {
                $('#AddEventModal').modal('hide')
                $('#calendar').fullCalendar('renderEvent', {
                    'title': response.title,
                    'acronym': response.acronym,
                    'city': response.city,
                    'venue': response.venue,
                    'value': response.value,
                    'start' : response.start,
                    'end'  : response.end,
                    'color' : response.color
                });

            },
            error:function(error)
            {
                if(error.responseJSON.errors) {
                    $('#titleError').html(error.responseJSON.errors.title);
                }
            },
        });
    });
  },
        

    eventClick: function(info) {
                  //  var start = moment(event.start).format('YYYY-MM-DD');
                  //  var end = moment(event.end).format('YYYY-MM-DD');
                  $('#EditEventModal').modal('show');
                  $('#EditEventModal').find('#id').val(info.event.id);
                  $('#EditEventModal').find('#title').val(info.event.title);
                  $('#EditEventModal').find('#acronym').val(info.event.extendedProps.acronym);
                  $('#EditEventModal').find('#city').val(info.event.extendedProps.city);
                  $('#EditEventModal').find('#venue').val(info.event.extendedProps.venue);
                  $('#EditEventModal').find('#value').val(info.event.extendedProps.value);
                  $('#EditEventModal').find('#start').val(info.event.start);
                  $('#EditEventModal').find('#end').val(info.event.end);
                  $('#EditEventModal').modal('show');

                    $.ajax({
                            url:"{{ route('calendar.update', '') }}" +'/'+ id,
                            type:"PATCH",
                            dataType:'json',
                            data:{ start, end  },
                            success:function(response)
                            {
                                swal("Good job!", "Event Updated!", "success");
                            },
                            error:function(error)
                            {
                                console.log(error)
                            },
                        });
                },

});
calendar.render();


            $("#AddEventModal").on("hidden.bs.modal", function () {
                $('#saveBtn').unbind();
            });

            $("#EditEventModal").on("hidden.bs.modal", function () {
                $('#updateBtn').unbind();
            });

            $('.fc-event').css('font-size', '13px');
            $('.fc-event').css('width', '20px');
            $('.fc-event').css('border-radius', '50%');


});
</script>
<link href='https://cdn.jsdelivr.net/npm/fullcalendar-scheduler@5.11.3/main.min.css' rel='stylesheet' />
@endpush

這是我的活動 controller:

<?php

namespace App\Http\Controllers;

use App\Http\Requests\StoreEventRequest;
use App\Http\Requests\UpdateEventRequest;
use App\Models\Event;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Symfony\Component\HttpFoundation\Response;

class EventsController extends Controller
{
    public function index()
    {
        abort_if(Gate::denies('event_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');

        $events = Event::all();

        return view('events.index', compact('events'));
    }

    public function create()
    {
        abort_if(Gate::denies('event_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');

        return view('events.create');
    }

    public function store(Request $request)
    {
        $request->validate([
            'title' => 'required|string'
        ]);

        $event = Event::create([
            'title' => $request->title,
            'acronym' => $request->acronym,
            'city' => $request->city,
            'venue' => $request->venue,
            'value' => $request->value,
            'start' => $request->start,
            'end' => $request->end,
        ]);

        $color = null;

        if($event->title == 'Test') {
            $color = '#924ACE';
        }

        return redirect('/dashboard')-> with('success', 'Event Added'); response()->json([
            'id' => $event->id,
            'title' => $event->title,
            'acronym' => $request->acronym,
            'city' => $request->city,
            'venue' => $request->venue,
            'value' => $request->value,
            'start' => $event->start,
            'end' => $event->end,
          //  'color' => $color ? $color: '',

        ]);
    }


    public function show(Event $event)
    {
        abort_if(Gate::denies('event_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');

        return view('events.show', compact('event'));
    }

    public function edit(Event $event)
    {
        abort_if(Gate::denies('event_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');

        return view('events.edit', compact('event'));
    }


    public function update(Request $request ,$id)
{
    $event = Event::find($id);
    if(! $event) {
        return response()->json([
            'error' => 'Unable to locate the event'
        ], 404);
    }
    $event->update([
        'start' => $request->start,
        'end' => $request->end,
    ]);
    return response()->json('Event updated');
}

    public function destroy(Event $event)
    {
        abort_if(Gate::denies('event_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');

        $event->delete();

        return redirect()->route('events.index');
    }
}

這是相關的路線:

Route::get('calendar/index', [CalendarController::class, 'index'])->name('calendar.index');
Route::post('calendar', [EventController::class, 'store'])->name('event.store');
Route::patch('calendar/update/{id}', [CalendarController::class, 'update'])->name('calendar.update');
Route::delete('calendar/destroy/{id}', [CalendarController::class, 'destroy'])->name('calendar.destroy');

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM