Search code examples
javascriptvue.jsvuejs2fullcalendar

I want to catch the moment the data of fullcalendar is added


I succeeded in putting data into fullcalendar. So I tried to put the api at the point where data is added, but I can't catch this moment....

Chat gpt tells me to put an api in handleDrop, but no console is displayed when dragging and dropping table data.

The weird spot is that when I draggable="true" and any other letter or element I don't specify @drop="handleDrop" the console fires. Conflict with initializeDraggable?.

There seems to be a lot of unnecessary parts in my code, but I don't know where to modify to send api the moment it is added to the calendar.

 <template>
  <div class="bom">
    <div class="bomTable-processList">
      <div>
        <table>
          <thead>
            <tr>
              <th colspan="6">BOM 관리</th>
            </tr>
            
            <tr v-if="bomDatas.length > 0">
              <th colspan="3">미완 <input type="radio" id="done" value="Undone" aria-label="Undone" name="bom" v-model="bomDatas[0].state" /></th>
              <th colspan="3">작성중 <input type="radio" id="editting" value="Editting" aria-label="Editting" name="bom" v-model="bomDatas[0].state" /></th>
              <th colspan="3">작성완료 <input type="radio" id="done" value="Done" aria-label="Done" name="bom" v-model="bomDatas[0].state" /></th>
              <th colspan=""><button>완료</button></th>
            </tr>
          </thead>
          <tbody id="external-events">
            <tr
              class="fc-event"
              v-for="(bomData, index) in getBomData"
              :key="index"
              draggable="true"
              @drop="handleDrop"
              @dragstart="handleDragStart($event, bomData)"
            >
              <td v-for="(process, pIndex) in bomData.process" :key="pIndex">{{ process }}</td>
              <td>{{ bomData.process_order }}</td>
              <td class="minusBtn">
                <button @click="deleteBom(bomDatas[index])">-</button>
              </td>
              <td class="upBtn">
                <button @click="moveRowUp(bomData, index)">▲</button>
              </td>
              <td class="downBtn">
                <button @click="moveRowDown(bomData, index)">▼</button>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
      <processList />
    </div>
    <equipmentList />
    <div class="full">
      <div id="external-events"></div>
      <div id="calendar-container" @dragover="handleDragOver">
        <FullCalendar
          class="demo-app-calendar"
          ref="fullCalendar"
          :options="calendarOptions"
          :editable="true"
          :events="calendarEvents"
          @eventClick="handleEventClick"
        />
      </div>
    </div>
  </div>
</template>

<script lang="js">
import processList from '@/components/detail/processList.vue'
import equipmentList from '@/components/detail/equipmentList.vue'

import { mapGetters } from 'vuex'

import FullCalendar from '@fullcalendar/vue'
import dayGridPlugin from '@fullcalendar/daygrid'
import interactionPlugin, { Draggable } from '@fullcalendar/interaction'
import resourceTimelinePlugin from '@fullcalendar/resource-timeline'

export default {
  components: {
    FullCalendar,
    processList,
    equipmentList,
  },
  data() {
    return {
      draggedData: null,
      calendarOptions: {
        schedulerLicenseKey: 'CC-Attribution-NonCommercial-NoDerivatives',
        plugins: [dayGridPlugin, interactionPlugin, resourceTimelinePlugin],
        initialView: 'dayGrid1',
        slotDuration: '24:00:00',
        resources: [],
        events: [],
        height: 600,
        headerToolbar: {
          left: 'prev,next today',
          center: 'title',
          right: 'dayGrid1,dayGrid2',
        },
        views: {
          dayGrid1: {
            type: 'resourceTimelineDay',
            duration: { days: 30 },
            buttonText: '15',
          },
          dayGrid2: {
            type: 'resourceTimelineDay',
            duration: { days: 60 },
            buttonText: '30',
          },
        },
        dayHeaderFormat: {
          weekday: 'short',
          month: 'numeric',
          day: 'numeric',
          omitCommas: true,
        },
        editable: true,
        droppable: true,
        eventResizableFromStart: true,
        eventDidMount: this.handleEventMount,
      },
      removeAfterDrop: false,
      calendarEvents: [],
    }
  },
  watch: {
    facilityDatas: {
      deep: true,
      handler() {
        this.calendarOptions.resources = this.facilityDatas.map((facility, index) => ({
          id: index.toString(),
          title: facility.facility_name,
        }))
      },
    },
  },
  updated() {
    this.$nextTick(function () {
      this.initializeDraggable();
    });
  },
  computed: {
    ...mapGetters(['getBomData', 'getProductUnitData', 'getFacilityData', 'getProductAmount', 'processDataNull']),
    facilityDatas: {
      get() {
        return this.getFacilityData
      },
      set(value) {
        const updatedValue = value.map((item) => ({
          ...item,
        }))
        this.$store.commit('setFacilityData', updatedValue)
      },
    },
    bomDatas: {
      get() {
        return this.$store.getters.getBomData;
      },
      set(value) {
        const updatedValue = value.map((item) => ({
          ...item,
        }))
        this.$store.commit('setBomData', updatedValue)
      },
    },
    productUnit() {
      return this.getProductUnitData
    },
    processDataIsNull() {
      return this.$store.getters.processDataNull;
    },
  },
  async mounted() {
    const payload = {
      product_unit: this.productUnit,
    }
    await this.$store.dispatch('getBom', payload)
    this.bomDatas = this.getBomData ? [...this.getBomData] : []

    this.calendarOptions.resources = this.facilityDatas.map((facility, index) => ({
      id: index.toString(),
      title: facility.facility_name,
    }))

    this.fullCalendar = this.$refs.fullCalendar.$refs.calendar
  },
  methods: {
    async moveRowUp(bomData, index) {
      if (index > 0) {
        [this.bomDatas[index], this.bomDatas[index - 1]] = [
          this.bomDatas[index - 1],
          this.bomDatas[index],
        ];
        this.bomDatas.forEach((data, i) => {
          data.process_order = i + 1;
        });
        await this.updateBomData();
      }
    },

    async moveRowDown(bomData, index) {
      if (index < this.bomDatas.length - 1) {
        [this.bomDatas[index], this.bomDatas[index + 1]] = [
          this.bomDatas[index + 1],
          this.bomDatas[index],
        ];
        this.bomDatas.forEach((data, i) => {
          data.process_order = i + 1;
        });
        await this.updateBomData();
      }
    },

    async updateBomData() {
      const payload = {
        product_unit: this.productUnit,
        process_names: this.bomDatas.map((data) => data.process),
      }
      await this.$store.dispatch('putBom', payload);
    },

    async deleteBom(bomData) {
      console.log(bomData)
      if (window.confirm('정말로 삭제하시겠습니까?')) {
        await this.$store.dispatch('deleteBom', {
          product_unit: bomData.product_unit,
          process_order: bomData.process_order,
          productAmount: this.getProductAmount,
        });
        const payload = {
          product_unit: this.productUnit,
        }
        await this.$store.dispatch('getBom', payload)
      }
    },
    handleEventClick(info) {
      if (confirm('삭제하시겠습니까?')) {
        info.event.remove()
        const index = this.calendarEvents.findIndex((event) => event.id === info.event.id)
        if (index !== -1) {
          this.calendarEvents.splice(index, 1)
        }
      }
    },
    handleEventMount(info) {
      const eventEl = info.el
      eventEl.addEventListener('dblclick', () => {
        this.handleEventClick(info)
      })
    },

    handleDragStart(event, bomData) {
      console.log('handleDragStart')

      if (bomData) {
        this.draggedData = {
          product_unit: bomData.product_unit,
          process_name: bomData.process,
          productAmount: this.getProductAmount,
        }
      } else {
        console.error('bomData is undefined or null')
      }
    },

    async handleDrop(event) {
      console.log('handleDrop')

      const dragData = this.draggedData
      this.draggedData = null

      if (dragData) {
        console.log('drop')

        const dateClickedArg = {
          date: this.fullCalendar.getApi().getDate(),
          allDay: true,
        }

        const facilityName = event.currentTarget.dataset.resourceId;

        const newEvent = {
          title: `${dragData.product_unit} - ${dragData.process} - ${this.getProductAmount}`,
          start: dateClickedArg.date,
          allDay: dateClickedArg.allDay,
        }

        this.calendarEvents.push(newEvent)

        if (this.removeAfterDrop) {
          const index = this.bomDatas.findIndex((data) => data === dragData)
          if (index !== -1) {
            this.bomDatas.splice(index, 1)
          }
        }
        const payload = {
          title: `${dragData.product_unit} - ${dragData.process} - ${this.getProductAmount}`,
          start: dateClickedArg.date,
          end_date: dateClickedArg.date,
          facility_name: facilityName,
        }
        console.log(facilityName)
        await this.$store.dispatch('postBom', payload)
      }
    },
    initializeDraggable() {
      console.log('initializeDraggable')

      const containerEl = document.getElementById('external-events')
      if (containerEl) {
        const eventElements = containerEl.getElementsByClassName('fc-event')
        Array.from(eventElements).forEach((eventEl, index) => {
          if (eventEl.classList.contains('draggable-initialized')) {
            return;
          }
          let draggableInstance = null;

          const createDraggable = () => {
            if (draggableInstance) {
              draggableInstance.destroy();
            }

            draggableInstance = new Draggable(eventEl, {
              eventData: () => {
                const bomData = this.bomDatas[index]
                return {
                  title: `${bomData.product_unit} - ${bomData.process} -${this.getProductAmount}`,
                }
              },
            });

            eventEl.addEventListener('dragstart', (event) => {
              const bomData = this.bomDatas[index]
              const dragData = {
                product_unit: bomData.product_unit,
                process_name: bomData.process_name,
                productAmount: this.getProductAmount,
              }
              event.dataTransfer.setData('text', JSON.stringify(bomData))
              this.draggedData = dragData
            });

            eventEl.removeEventListener('dragstart', createDraggable);
            eventEl.removeEventListener('mouseover', createDraggable);
          };

          createDraggable();

          eventEl.addEventListener('mouseover', () => {
            createDraggable();
          });
          eventEl.classList.add('draggable-initialized');
        });

        const calendarContainerEl = document.getElementById('calendar-container')
        if (calendarContainerEl) {
          calendarContainerEl.addEventListener('drop', this.handleDrop)
          calendarContainerEl.addEventListener('dragover', this.handleDragOver)
        }
      }
    },
    handleDragOver(event) {
      console.log('handleDragOver')

      event.preventDefault()
    },
  },
}
</script>

Solution

  •       @drop="drop"
          @eventDrop="eventDrop"
    

    I solved it using these two.