2025-01-18 JavaScript 系列八:第4課 ── 行事曆應用程式-步驟08
把資料存在 local storage
好!超棒!💪
你已經完成在 FullCalendar 上新增活動了,
下一步:要做到 ➔ 「把活動存到 localStorage」✅
這個功能目標是:
- 每次新增活動時,存一份到 localStorage
- 下次打開網頁時,localStorage 的活動可以自動載入到 FullCalendar
✨【完整加上 localStorage 版】✨
我直接在你剛剛那一版上,加上 localStorage 功能喔:
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
| <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
const STORAGE_KEY = 'calendar-events' // 👈 localStorage 專用 key 名
onMounted(() => { if (window.FullCalendar && window.FullCalendar.Calendar) { calendar = new window.FullCalendar.Calendar(calendarRef.value, { initialView: 'dayGridMonth', locale: 'zh-tw', events: loadEventsFromLocalStorage(), // 👈 初始化時載入 localStorage 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
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(event) saveEventToLocalStorage(event) // 👈 儲存到 localStorage
showForm.value = false }
function saveEventToLocalStorage(event) { const events = JSON.parse(localStorage.getItem(STORAGE_KEY)) || [] events.push(event) localStorage.setItem(STORAGE_KEY, JSON.stringify(events)) }
function loadEventsFromLocalStorage() { return JSON.parse(localStorage.getItem(STORAGE_KEY)) || [] } </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; }
:deep(.add-event-button .icon) { display: none; } :deep(.add-event-button .text) { display: inline; } @media (max-width: 600px) { :deep(.add-event-button .text) { display: none; } :deep(.add-event-button .icon) { display: inline; font-size: 16px; } }
.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>
|
🎯【現在功能完整】
- 🔵 加活動 ➡️ localStorage 自動存起來
- 🔵 重新整理網頁 ➡️ 活動不會消失
- 🔵 每次加新活動 ➡️ 自動同步 localStorage
📦 整理一下,今天你學會了:
| 主題 |
重點 |
| FullCalendar 基礎使用 |
初始畫面、點擊事件 |
| Modal 表單互動 |
v-if 顯示/隱藏、v-model 資料綁定 |
| 操作 localStorage |
JSON.parse()、JSON.stringify() |
| RWD 響應式按鈕 |
手機版按鈕自動變簡單 |
要不要【下一步升級】:
➔ 「刪除活動 + localStorage 同步更新」?
(這樣就可以點活動 ➔ 刪除 ➔ localStorage 自動跟著刪)
要繼續的話,回我【刪除功能】~🚀
要嗎?😄