Electron+Vue3 MAC 版日历开发记录(21)——解决 Events 不显示的Bug

2,316 阅读3分钟

这是我参与更文挑战的第21天,活动详情查看: 更文挑战

今天是周一,相信大家都很忙,晚上我才有时间坐下来想一想写写代码,在整理的过程中,我发现这几天一直没解决的一个问题:那就是没显示「事件」。

Events 不显示问题排查

我把 Events 参考官网 Demo 加上 Events 显示:

<template>
  <full-calendar
    ref="fullcalendar"
    :options="calendarOptions"
  >
    <template #eventContent="arg">
      <i>{{ arg.event.title }}</i>
    </template>
  </full-calendar>
</template>

但发现还是显示不了。我打了一个 log 看了看,有数据回调啊:

但就是无法显示 Events,我后来把 Prop Events 加入到 calendarOptions:

calendarOptions: {
    plugins: [dayGridPlugin, interactionPlugin],
    headerToolbar: {
      left: 'prev,next',
      center: 'title',
      right: '',
    },
    initialEvents: this.event, // 看这里
    selectable: true,
    select: this.dateClick,
    eventClick: this.eventClick,
    eventChange: this.updateView,
    editable: false,
    // height: Number(import.meta.env.VITE_APP_HEIGHT) - 10,
    aspectRatio: 1.31,
    views: this.dayCellNewContent(),
    locale: zhLocale,
} as CalendarOptions,

这时候是有时候显示,有时候不显示。

对于这个问题,其实马上就能定位出毛病在哪:由于获取的数据是异步的,所以新数据并没有被重新加载。

这让我很难受了,Vue 不就是因为 MVVM 模式而出名的吗?在我百思不得其解的时候,只能看看 FullCalendar 官网,找找问题所在了:

在官网上initialEvents还真有这么一段话来说明:

This is exactly like specifying event as an array except that if the supplied value changes, the calendar will NOT be updated to reflect. Only applicable to declarative front-end frameworks such as React, Vue, and Angular.

Useful for when you want to supply an initial set of events and then manipulate these events via the API. If you use initialEvents, when your component receives new props, your changes will not be overwritten with the original event array.

此时此刻,我只有一个念头,我想办法自己整 Calender,不用这个 FullCalender,完全不符合 Vue 数据同步更新 View 的理念嘛。

Events 显示方法

好在,还是有办法解决的,官网提供了两个接口:

先看看我们的 Events Prop:

props: {
  events: {
    type: Array,
    default() {
      return [];
    },
  },
  ...
}

因为我把 events 定义为 Array,提示类型不匹配的 typecheck 的错误:

所以需要加上一个 “类型”:

events: {
  type: Array,
  default: [] as EventInput[],
},

具体同步数据到 View 上,只需要监听 Events 数据变化,在 Watch 里增加逻辑:

第一种Calendar::addEvent 方法实现:

watch: {
  events(): void {
    const calendarArray = this.$refs['fullcalendar'] as any;
    const calendarApi = calendarArray.getApi();
    this.events?.forEach((event: any) => {
      return calendarApi.addEvent(event);
   })
},

看似能解决问题:

但这种方法不能保证是不是会重复 add Event,而且我觉得效果不一定好。

接着寻找,我们发现fullcalender 提供了第二种接口: calendarApi.addEventSource,而需要的 EventSourceInput 类型为:

declare type EventSourceInput = EventSourceInputObject | 
EventInput[] | EventSourceFunc | 
string;

这类型本身也包含了数组类型,所以,可以用一条语句代替上面的代码:

      // this.events?.forEach((event: any) => {
      //   return calendarApi.addEvent(event);
      // })
      // const eventsTemp: EventInput[] = this.events;
      calendarApi.addEventSource(this.events);

达到相同的目标:

小结

今天在重构部分主要把 FullCalendarMain 主界面的三个 NDrawerContent 放到对应的页面布局上,这样看起来显得统一一些,而且只留下了,每个布局的 NDrawer 头部:

<n-drawer
    v-model:show="visibleFullSetting"
    :width="settingDrawerWidth"
    placement="left"
  >
    <setting-sub
      v-model:changeShowWeather="changeShowWeather"
      v-model:changeShowFestivals="changeShowFestivals"
      v-model:location="location"
      @focusClick="focusClick"
    />
  </n-drawer>
  <n-drawer
    v-model:show="visibleFullDateView"
    :width="hlDrawerWidth"
    placement="left"
  >
    <date-view-sub
      v-model:date="date"
    />
  </n-drawer>
  <n-drawer
    v-model:show="visibleECSub"
    :width="ecDrawerWidth"
    placement="left"
  >
    <event-create-sub
      v-model:event="event"
      @addEventClick="addEventClick"
    />
  </n-drawer>

相信大家也能看出来这段代码还有值得继续重构:去除相同的代码,让代码保持简洁。

每天进步一点点,是我的目标,相信今晚发现问题到解决问题,能让自己处理 Bug 上有所记录和总结。

未完待续!

代码已同步到 github 上了:github.com/fanly/fanly…