3
1

More than 1 year has passed since last update.

Vuetifyのv-calendarを使って献立カレンダーを作ってみた

Posted at

やりたいこと

  • 日々の献立表をカレンダーを使って表示させる
  • カレンダー表示時にAPIを実行し、献立を取得する

今回はVuetifyを使っていたので、v-calendarを使って実現しました。
Vuetifyの環境構築についてはこちらのページに記載しています。

ソースコードの構成

構成としてはsrc/views配下にカレンダー表示用ページを作り、
実際のカレンダー表示はsrc/componentにコンポーネント化し、viewに組み込みます。

カレンダーコンポーネント

まずは、カレンダー表示用コンポーネントを作成します。

src/component/CalendarComponent.vue
<template>
  <v-row justify="center" style="height: 600px">
    <v-col cols="10">
      <v-sheet tile height="6vh" color="grey lighten-3" class="d-flex align-center">
        <v-btn outlined small class="ma-4" @click="setToday">
          今日
        </v-btn>
        <v-btn icon @click="$refs.calendar.prev()">
          <v-icon>mdi-chevron-left</v-icon>
        </v-btn>
        <v-btn icon @click="$refs.calendar.next()">
          <v-icon>mdi-chevron-right</v-icon>
        </v-btn>
        <v-toolbar-title>{{ title }}</v-toolbar-title>
      </v-sheet>
      <v-calendar
        ref="calendar"
        v-model="value"
        locale="ja-jp"
        :events="events"
        :day-format="(timestamp) => new Date(timestamp.date).getDate()"
        :month-format="(timestamp) => (new Date(timestamp.date).getMonth() + 1) + ' /'"
        @click:event="clickEvent"
        @click:date="clickDay"
      ></v-calendar>
    </v-col>
  </v-row>
</template>

<script>
import { mapActions } from "vuex";
import moment from "moment";

export default {
  name: "CalendarComponent",
  data() {
    return {
      value: moment().format("yyyy-MM-DD"),
    };
  },
  props: {
    events: [],
  },
  methods: {
    ...mapActions("calendar-store", [
      "setSelectedDate",
    ]),
    setToday() {
      this.value = moment().format("yyyy-MM-DD");
    },
    clickEvent({ event }) {
      this.$emit("clickEvent");
      this.setSelectedDate(event.start);
    },
    clickDay({ date }) {
      this.$emit("clickDay");
      this.setSelectedDate(date);
    },
  },
  computed: {
    title() {
      return moment(this.value).format("yyyy年 M月");
    },
  },
};
</script>

以下で細かく説明します。

カレンダーの表示

<v-calendar
  :events="events"
></v-calendar>

カレンダーに表示するデータは:eventsにて渡します。
今回はページ側から受け渡しするために、popsに定義しました。

props: {
  events: [],
},

月の遷移ボタン

<v-btn outlined small class="ma-4" @click="setToday">
  今日
</v-btn>
<v-btn icon @click="$refs.calendar.prev()">
  <v-icon>mdi-chevron-left</v-icon>
</v-btn>
<v-btn icon @click="$refs.calendar.next()">
  <v-icon>mdi-chevron-right</v-icon>
</v-btn>
<v-toolbar-title>{{ title }}</v-toolbar-title>

上記はカレンダーのツールバーに「今日」「<」「>」の3つのボタンと「年月」を並べて表示させています。

<v-calendar
  v-model="value"
  :day-format="(timestamp) => new Date(timestamp.date).getDate()"
  :month-format="(timestamp) => (new Date(timestamp.date).getMonth() + 1) + ' /'"
></v-calendar>

v-modelに表示させる日付を渡します。
day-formatmonth-formatは日本語表記に変更しています。

data() {
  return {
    value: moment().format("yyyy-MM-DD"),
  };
},

v-modelに渡すvaluedata()に定義し、初期値は本日日付を設定しています。(momentを使用)

methods: {
  setToday() {
    this.value = moment().format("yyyy-MM-DD");
  },

「今日」ボタンを押した場合はvalue値に本日日付が設定され、今日のカレンダーが表示されます。
v-btn$refs.calendar.prev()および@click="$refs.calendar.next()
valueにそれぞれ前月末日、翌月初日が設定され、カレンダーが遷移します。

computed: {
  title() {
    return moment(this.value).format("yyyy年 M月");
  },
},

ツールバーの日付部分に表示されている年月を算出しています。

イベント処理

<v-calendar
  @click:event="clickEvent"
  @click:date="clickDay"
></v-calendar>

eventはカレンダーのイベントをクリックした際に実行されるメソッドです。
dateはカレンダーの日付をクリックした際に実行されるメソッドです。

method: {
  clickEvent({ event }) {
    this.$emit("clickEvent");
    this.setSelectedDate(event.start);
  },
  clickDay({ date }) {
    this.$emit("clickDay");
    this.setSelectedDate(date);
  },
}

どちらも親コンポーネントのメソッドをemitするようにしました。
this.setSelectedDate();は日付をストアに保持しています。
(今回はストアの説明は省略します)

カレンダービュー

次に、コンポーネントの親である、カレンダー表示用のviewを作成します。

src/views/Calendar.vue
<template>
  <div id="calendar">
    <v-container>
      <CalendarComponent
        v-on:clickDay="onCategory"
        v-on:clickEvent="onMenuOfDay"
        :events="schedule"
      ></CalendarComponent>
    </v-container>
  </div>
</template>

<script>
import CalendarComponent from "@/components/CalendarComponent";
import MenuOfDayDialogComponent from "@/components/MenuOfDayDialogComponent";

import { mapActions, mapGetters } from "vuex";

export default {
  name: "Calendar",
  components: {
    CalendarComponent,
    MenuOfDayDialogComponent,
  },
  data() {
    return {
      isShowMenuOfDay: false,
      schedule: [],
    };
  },
  async created() {
    Promise.all([
      this.getSchedule().then((response) => {
        this.schedule = response.data;
        this.setScheduleList(response.data);
      }),
    ]);
  },
  methods: {
    ...mapActions("calendar-store", [
      "getSchedule",
      "setScheduleList",
    ]),
    onMenuOfDay() {
      this.isShowMenuOfDay = true;
    },
    onMenuOfDayClose() {
      this.isShowMenuOfDay = false;
    },
  },
};
</script>

以下で細かく説明します。

カレンダーコンポーネント

<template>
  <div id="calendar">
    <v-container>
      <CalendarComponent
        v-on:clickDay="onCategory"
        v-on:clickEvent="onMenuOfDay"
        :events="schedule"
      ></CalendarComponent>
    </v-container>
  </div>
</template>

カレンダーコンポーネントを使用しています。
v-onにてカレンダーの日付およびイベントがクリックされた際に、emitされるメソッドを定義しています。
カレンダーコンポーネントのeventsscheduleを渡しています。

初期表示

async created() {
  Promise.all([
    this.getSchedule().then((response) => {
      this.schedule = response.data;
      this.setScheduleList(response.data);
    }),
    this.getMenu().then((response) => {
      this.setMenuList(response.data);
    }),
  ]);
},

カレンダービューファイルが構成された際に、APIにて献立表およびメニューリストを取得しています。
APIはストア経由で取得されるようにしています。(ストアの説明は省略します)
献立表はscheduleに保持されます。

なお、APIで返却される情報は以下のようになっています。

[
    {
        "name": "玉子雑炊",
        "start": "2022-05-01",
        "end": "2022-05-01",
        "color": "red",
    },
    {
        "name": "麻婆春雨",
        "start": "2022-05-09",
        "end": "2022-05-09",
        "color": "blue",
    },
    {
        "name": "小松菜ときのこのお浸し",
        "start": "2022-05-10",
        "end": "2022-05-10",
        "color": "green",
    },
]
  • name: イベントに表示される名前
  • start: イベントの開始日
  • end: イベントの終了日
  • color: イベントの背景色

通常のカレンダーの使い方としてはイベントの開始日と終了日をstartとendにしますが、
今回は同日を設定することで、その日のみにメニューが表示されるようにしました。

イベント処理

onMenuOfDay() {
  this.isShowMenuOfDay = true;
},
onMenuOfDayClose() {
  this.isShowMenuOfDay = false;
  });
},

イベントがクリックされた際のメソッドの挙動です。
カレンダー機能はイベントが多いと2 moreのように省略されるため、
今回はダイアログでその日のメニュー一覧が表示されるようにしました。
MenuOfDayDialogComponentがダイアログで表示されるようになっていますが、割愛します。

完成

下記のようなカレンダーが表示されます。
スクリーンショット 2022-06-03 23.47.02.png

今回は省略しましたが、イベントをクリックするとその日のメニューが表示されます。
スクリーンショット 2022-06-03 23.55.23.png

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1