2025-01-17 JavaScript 系列八:第4課 ── 行事曆應用程式-步驟07

2025-01-17 JavaScript 系列八:第4課 ── 行事曆應用程式-步驟07

正統 Vue 3 的做法

太棒了!🥳
你說【要】,那我來幫你整理成一版 「完整版、糾正後、超乾淨、專業版」
這版會修掉你所有小問題,而且讓你的 Vue 程式更乾淨。


🔥【完整版修正版】🔥

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
<template>
<div>
<h2>我的行事曆</h2>
<div ref="calendarRef" id="calendar"></div>
</div>

<!-- 新增活動的表單 Modal -->
<div v-if="showForm" class="modal-overlay">
<div class="modal">
<h3>新增活動</h3>
<label>
活動名稱:
<input v-model="formData.title" type="text">
</label>
<label>
活動日期:
<input v-model="formData.date" type="date">
</label>
<label>
活動時間:
<input v-model="formData.time" type="time">
</label>
<div class="modal-buttons">
<button @click="saveEvent">儲存</button>
<button @click="showForm = false">取消</button>
</div>
</div>
</div>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const calendarRef = ref(null)
const showForm = ref(false)
const formData = ref({
title: '',
date: '',
time: ''
})

let calendar = null // 👈 這裡設全域變數,不要再用 window

onMounted(() => {
if (window.FullCalendar && window.FullCalendar.Calendar) {
calendar = new window.FullCalendar.Calendar(calendarRef.value, {
initialView: 'dayGridMonth',
locale: 'zh-tw',
events: [],
dateClick(info) {
// 移除所有舊的按鈕
document.querySelectorAll('.add-event-button').forEach(btn => btn.remove())

// 移除舊的灰色背景
document.querySelectorAll('.selected-day').forEach(el => {
el.classList.remove('selected-day')
})

const cellEl = info.dayEl.querySelector('.fc-daygrid-day-frame')
if (!cellEl) return

// ✅ 加背景色 class
cellEl.classList.add('selected-day')

// ✅ 新增按鈕
const button = document.createElement('button')
button.className = 'add-event-button'
button.innerHTML = `<span class="text">新增活動</span><span class="icon">➕</span>`

button.onclick = () => {
formData.value.date = info.dateStr
formData.value.time = '09:00'
formData.value.title = ''
showForm.value = true
}

cellEl.appendChild(button)
}
})
calendar.render()
} else {
console.error('FullCalendar 尚未載入,請確認 CDN script')
}
})

function saveEvent() {
if (!formData.value.title) {
alert('請輸入活動名稱')
return
}

const event = {
title: formData.value.title,
start: `${formData.value.date}T${formData.value.time}`
}

// ✅ 直接用 calendar.addEvent,不要掛到 window
calendar.addEvent(event)

showForm.value = false
}
</script>

<style scoped>
#calendar {
max-width: 800px;
margin: 20px auto;
}

/* ✅ 點選日期格背景變淺灰色 */
:deep(.selected-day) {
background-color: #f0f0f0 !important;
transition: background-color 0.3s ease;
}

/* ✅ 新增活動按鈕基本樣式 */
:deep(.add-event-button) {
background-color: #1e2b37;
color: white;
border: none;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
}

/* ✅ 桌機版:顯示文字+icon */
:deep(.add-event-button .icon) {
display: none;
}

:deep(.add-event-button .text) {
display: inline;
}

/* ✅ 手機版 RWD:只顯示 icon */
@media (max-width: 600px) {
:deep(.add-event-button .text) {
display: none;
}
:deep(.add-event-button .icon) {
display: inline;
font-size: 16px;
}
}

/* ✅ Modal 樣式 */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
}

.modal {
background: white;
padding: 20px;
border-radius: 8px;
width: 90%;
max-width: 300px;
}

.modal label {
display: block;
margin-bottom: 10px;
}

.modal input {
width: 100%;
padding: 4px;
box-sizing: border-box;
}

.modal-buttons {
display: flex;
justify-content: space-between;
margin-top: 15px;
}
</style>

🎯這版有什麼改進?

原本 現在
window.addEventToCalendar(外洩全域) 直接使用 calendar 變數
caldar 打錯 正確是 calendar
window 掛方法,不乾淨 全部留在 script setup 裡管理
小細節錯誤 全部校正 & 專業化

✅ 現在這版效果

  • 點日期 ➡️ 出現淺灰色
  • 自動出現「新增活動」按鈕
  • 按下「新增活動」➡️ 彈出輸入表單
  • 儲存後 ➡️ 活動直接顯示在該格子裡
  • 手機版按鈕自動變成小圖示 (RWD OK)

要不要我也順便教你:

  • 怎麼加「編輯活動」?
  • 怎麼加「刪除活動」?
  • 或是「活動列表」管理?

要的話跟我說【繼續升級】,我可以帶你一步一步進階~🚀✨
要嗎?要的話回我【繼續升級】!🌟