完了したタスクをカレンダーに記録するため
週ごとのカレンダーを作成したい
そのためvueを使用して作っていく
ステップ1:カレンダーの基本的な表示

まずはカレンダーの基本的な表示を作成しましょう。画像のようなシンプルな月表示を目指します。
- Vue.jsプロジェクトの準備: もし既存のVue.jsプロジェクトがない場合は、以下のコマンドで新しいプロジェクトを作成します。 Bash
npm install -g @vue/cli vue create calendar-app
プロジェクト作成時にいくつかの質問が出ますが、Vue 3を選択しておけば問題ありません。 - カレンダーコンポーネントの作成:
src/components
ディレクトリにCalendar.vue
という新しいファイルを作成します。 Calendar.vue
の記述: 以下は基本的なカレンダー表示のテンプレートと簡単なロジックです。まずはこれをCalendar.vue
に記述してみてください。
<template>
<div>
<h2>{{ year }}年 {{ month }}月</h2>
<table>
<thead>
<tr>
<th v-for="day in daysOfWeek" :key="day">{{ day }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(week, index) in calendarGrid" :key="index">
<td v-for="(date, i) in week" :key="i" :class="{ 'today': isToday(date), 'empty': !date }">
{{ date ? date.getDate() : '' }}
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
name: 'PomodoroCalendar',
data() {
const today = new Date();
const currentYear = today.getFullYear();
const currentMonth = today.getMonth() + 1; // JavaScriptのgetMonth()は0から始まるため+1
return {
year: currentYear,
month: currentMonth,
daysOfWeek: ['日', '月', '火', '水', '木', '金', '土'],
todayDate: today.getDate(),
todayMonth: currentMonth,
todayYear: currentYear,
};
},
computed: {
daysInMonth() {
return new Date(this.year, this.month, 0).getDate();
},
firstDayOfMonth() {
return new Date(this.year, this.month - 1, 1).getDay(); // 0:日, 1:月,...
},
calendarGrid() {
const grid = [];
let week = [];
let dayCounter = 1;
// 月の初めまでの空白を追加
for (let i = 0; i < this.firstDayOfMonth; i++) {
week.push(null);
}
while (dayCounter <= this.daysInMonth) {
week.push(new Date(this.year, this.month - 1, dayCounter));
if (week.length === 7) {
grid.push(week);
week = [];
}
dayCounter++;
}
// 月の終わりまでの空白を追加
while (week.length < 7 && week.length > 0) {
week.push(null);
}
if (week.length > 0) {
grid.push(week);
}
return grid;
},
},
methods: {
isToday(date) {
return date && date.getDate() === this.todayDate && date.getMonth() + 1 === this.todayMonth && date.getFullYear() === this.todayYear;
},
},
};
</script>
<style scoped>
table {
width: 100%;
border-collapse: collapse;
}
th, td {
border: 1px solid #ccc;
padding: 8px;
text-align: center;
}
.today {
background-color: lightblue;
}
.empty {
background-color: #eee;
}
</style>
App.vue
でCalendar
コンポーネントを使用:src/App.vue
を以下のように修正して、作成したCalendar
コンポーネントを表示します。
<template>
<img alt="Vue logo" src="./assets/logo.png">
<PomodoroCalendar />
</template>
<script>
import PomodoroCalendar from './components/PomodoroCalendar.vue';
export default {
name: 'App',
components: {
PomodoroCalendar
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
ステップ2:カレンダーに作業記録を表示する準備
次に、カレンダーの各日付に作業記録を表示するための準備をします。
PomodoroCalendar.vue
に作業記録データを保持する:PomodoroCalendar.vue
のdata
オプションに、この作業記録データを初期値として設定します
export default {
data() {
// ... 既存のデータ
return {
// ... 既存のデータ
workLogs: [], // 作業記録を保存する配列
};
},
// ... 既存の computed と methods
};
- カレンダーの日付に紐付けるメソッドの準備: カレンダーの各セルに作業記録を表示するために、日付に対応する作業記録を抽出するメソッドを
Calendar.vue
に追加します。
export default {
// ... data, computed
methods: {
// ... 既存のメソッド
getWorkLogsForDate(date) {
if (!date) {
return [];
}
const formattedDate = `<span class="math-inline">\{date\.getFullYear\(\)\}\-</span>{(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
return this.workLogs.filter(log => log.date === formattedDate);
},
},
};
- テンプレートの修正: カレンダーの
<td>
要素内でgetWorkLogsForDate
メソッドを呼び出し、該当する作業記録を表示するようにテンプレートを修正します。簡単な表示として、タスク名を表示してみましょう。 この時点ではworkLogs
は空なので、まだ何も表示されません。
<template>
<div>
<h2>{{ year }}年 {{ month }}月</h2>
<table>
<thead>
<tr>
<th v-for="day in daysOfWeek" :key="day">{{ day }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(week, index) in calendarGrid" :key="index">
<td
v-for="(date, i) in week"
:key="i"
:class="{ 'today': isToday(date), 'empty': !date }"
>
{{ date ? date.getDate() : '' }}
<div v-if="date">
<div v-for="log in getWorkLogsForDate(date)" :key="log.startTime">
- {{ log.task }} ({{ log.startTime }} - {{ log.endTime }})
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</template>
カレンダーとタイマーの紐付け
上記のコードをPomodoroTimerプロジェクトのcomponentsに追加して、紐付けを行う
まず、PomodoroCalendar.vue
の <script>
タグ内の recordWorkTime
メソッドを修正し、ポモドーロタイマーから渡されるイベントデータを適切に処理するようにします。
PomodoroCalendar.vue
の <script>
の methods
部分の修正:
methods: {
isToday(date) {
return (
date &&
date.getDate() === this.todayDate &&
date.getMonth() + 1 === this.todayMonth &&
date.getFullYear() === this.todayYear
);
},
getWorkLogsForDate(date) {
if (!date) {
return [];
}
const formattedDate = `${date.getFullYear()}-${(date.getMonth() + 1)
.toString()
.padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
return this.workLogs.filter((log) => log.date === formattedDate);
},
recordWorkTime(logData) {
const logEntry = {
date: logData.date,
task: logData.task || '集中作業', // タスク名がない場合はデフォルト値を設定
startTime: logData.startTime,
endTime: logData.endTime,
};
this.workLogs.push(logEntry);
console.log("PomodoroCalendar で作業ログを記録:", logEntry);
},
},
解説:
recordWorkTime
メソッドは、ポモドーロタイマーから$emit
されるwork-finished
イベントで渡されるlogData
オブジェクトを受け取るように変更しました。logData
にtask
プロパティが含まれていない場合に備えて、デフォルト値として'集中作業'
を設定するようにしました。- 日付の形式は、ポモドーロタイマーから送信される形式 (
YYYY-MM-DD
) をそのまま使用します。
1. startWorkTimerInternal()
メソッド内の作業開始時刻の記録:
JavaScript
startWorkTimerInternal() {
this.isWorkTimerActive = true;
this.isWorkTimerPaused = false;
this.currentTimerLabel = "作業時間";
this.remainingWorkSeconds = this.minutes * 60 + this.seconds;
this.workStartTime = new Date().toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
}); // 作業開始時刻を記録
// ... (タイマーのインターバル処理)
},
this.workStartTime = new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
:new Date()
: 現在の日時オブジェクトを作成します。.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })
: 現在の時刻を、時と分を2桁で表示する形式の文字列に変換します(例: "13:09")。- この開始時刻の文字列をコンポーネントの
data
プロパティworkStartTime
に保存します。
2. startWorkTimerInternal()
メソッド内の作業終了時のイベント発行:
JavaScript
// ... (タイマーのインターバル処理)
} else {
this.stopTimerInterval();
this.isWorkTimerActive = false;
this.isWorkTimerPaused = false;
this.sendTimerEndStatus("work");
const workEndTime = new Date().toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
});
this.$emit("work-finished", {
date: new Date().toISOString().slice(0, 10),
startTime: this.workStartTime,
endTime: workEndTime,
task: "集中作業", // ここは必要に応じて変更可能
});
alert("作業時間終了!休憩を開始してください。");
this.breakTimerStartButton = true;
}
// ... (タイマーのインターバル処理の続き)
const workEndTime = new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
:- 作業時間が終了した時点の時刻を、開始時刻と同様の形式で取得し、
workEndTime
変数に保存します。
- 作業時間が終了した時点の時刻を、開始時刻と同様の形式で取得し、
this.$emit("work-finished", { ... });
:this.$emit()
: このコンポーネントから親コンポーネント(ここではPomodoroCalendar
)へイベントを発行します。"work-finished"
: 発行するイベントの名前です。親コンポーネントはこの名前のイベントをリッスンします。{ ... }
: イベントと共に送信するデータオブジェクトです。date: new Date().toISOString().slice(0, 10)
: 現在の日付をYYYY-MM-DD
形式の文字列で送信します。startTime: this.workStartTime
: 作業開始時に記録した時刻を送信します。endTime: workEndTime
: 作業終了時に取得した時刻を送信します。task: "集中作業"
: 現在は固定で "集中作業" というタスク名を送信しています。必要に応じて、ユーザーがタスクを入力できるようなUIを追加し、その入力値を送信するように変更できます。
3. resumeTimer("work")
メソッド内の開始時刻の記録(再開時):
JavaScript
resumeTimer(timerType) {
if (!this.timerInterval) {
if (
timerType === "work" &&
this.isWorkTimerPaused &&
this.isWorkTimerActive
) {
this.isWorkTimerPaused = false;
this.workStartTime =
this.workStartTime ||
new Date().toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
}); // 再開時に開始時刻が未設定なら記録
// ... (タイマーのインターバル処理)
}
// ... (休憩タイマーの再開処理)
}
},
this.workStartTime = this.workStartTime || new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
:- タイマーが一時停止から再開されたときに、
this.workStartTime
がまだ設定されていない(例えば、開始直後に一時停止した場合など)場合に、現在の時刻を開始時刻として記録します。 ||
は「左側の値が falsy (null, undefined, 0, false, '') ならば右側の値を評価する」というJavaScriptの論理OR演算子を利用したイディオムです。
- タイマーが一時停止から再開されたときに、
これらの変更により、ポモドーロタイマーが作業時間の開始と終了を正確に把握し、終了時にその情報を work-finished
イベントを通じて親コンポーネントである PomodoroCalendar
に送信できるようになりました。PomodoroCalendar
はこのイベントを受け取り、作業ログを記録してカレンダーに表示します。
上記の変更をしたことでカレンダーに作業時間が追加されました!
