在昨天的實作中,我們透過 Vue 3 的 watch 深度監聽與 localStorage,成功讓掉進玻璃瓶裡的微光可以在網頁重新整理後依然保存下來。
但隨著系統功能越做越多,新的使用情境問題浮現了出來:
所以今天的目標就是:替每顆微光打上「日期印記」,並實作「當日可修訂/撤回,過期後不可修改」的規則!
在以前的實作中,每顆光珠的物件結構只有 id、color、note 以及隨機偏移座標:
// 過去的光珠資料結構
{
id: Date.now(),
color: '#ffd166',
note: '今天買到了好吃的蛋糕',
offsetX: 2,
offsetY: -1
}
為了追蹤這顆光珠到底屬於哪一天,我們需要為它新增一個 date 欄位(格式為標準的 YYYY-MM-DD)。
在 <script setup> 中,我們先定義今天的日期字串:
// 取得當前日期的標準字串 (例如 "2026-09-29")
const todayStr = new Date().toISOString().split('T')[0]
💡為什麼是用 'T' 來做分隔?
初次看到 new Date().toISOString().split('T')[0] 時,可能會好奇中間為何憑空出現英文字母 T。
這是源自於國際標準組織制定的時間格式 ISO 8601。當我們執行 toISOString() 時,會得到一段標準字串,例如:
"2026-09-29T14:15:30.123Z"
在這串標準格式中,字母 T 代表「Time」,專門用來作為「前半段日期(Date)」與「後半段時間(Time)」的明確分界線。
因此,使用 .split('T') 就像拿剪刀從 T 的位置剪開,將字串切成 ["2026-09-29", "14:15:30.123Z"] 兩個元素的陣列;最後加上 [0] 取第 1 個元素,便能以最簡潔的一行語法取得標準的 YYYY-MM-DD 日期格式。
註:toISOString() 預設採用 UTC 世界協調時間,若在接近午夜或清晨(特別是台灣時區 UTC+8)執行,可能會因時區換算而產生日期位移。若是對時區要求極為嚴格的情境,使用本機的 getFullYear()、getMonth()、getDate() 自行組裝會更穩定!
接著,修改「放入微光」的 addGem() 函式,在建立新光珠時把 date: todayStr 寫入:
function addGem() {
if (todayNote.value.trim() === '') {
alert('請先寫下一件今天的美好小事喔!')
return
}
const randomOffsetX = Math.floor(Math.random() * 9) - 4
const randomOffsetY = Math.floor(Math.random() * 7) - 3
const newGem = {
id: Date.now(),
date: todayStr, // 新增日期標籤
color: selectedColor.value,
note: todayNote.value,
offsetX: randomOffsetX,
offsetY: randomOffsetY
}
當使用者點開瓶身中的某顆光珠時,會彈出卡片顯示內容。
我們在卡片中提供兩個限當日可執行的操作:
prompt 彈窗直接編輯文字。filter 移除該光珠。在 <script setup> 中補上這兩個函式:
// 修改當日文字
function editGem() {
const newNote = prompt('修改你的美好小事:', activeGem.value.note)
if (newNote && newNote.trim() !== '') {
activeGem.value.note = newNote.trim()
}
}
// 撤回當日微光
function deleteGem() {
if (confirm('確定要撤回這道微光嗎?')) {
// 透過 filter 排除當前選中的光珠 ID
gems.value = gems.value.filter(g => g.id !== activeGem.value.id)
closeGem() // 關閉卡片
}
}
💡這裡有個 Vue 3 的隱藏亮點:
注意看!我們在 editGem() 與 deleteGem() 中,完全沒有手動寫 localStorage.setItem!
因為昨天我們已經建立了深度監聽:
watch(gems, (newVal) => {
localStorage.setItem('store_gems', JSON.stringify(newVal))
}, { deep: true })
只要我們修改了物件裡面的屬性(activeGem.value.note = ...)或是改變了陣列內容(gems.value = ...),Vue 的響應式系統就會自動捕捉變化,並在背景自動完成同步!
打開原本的彈出預覽卡片,我們利用 v-if 與 v-else 來做狀態劃分:
activeGem.date === todayStr,顯示「編輯」與「撤回」按鈕。<!-- 彈出預覽卡片 -->
<div v-if="activeGem" class="modal-backdrop" @click="closeGem">
<div class="gem-card" :style="{ borderColor: activeGem.color }" @click.stop>
<!-- 光點顏色標籤 -->
<div class="gem-badge" :style="{ backgroundColor: activeGem.color }"></div>
<!-- 顯示採集日期 -->
<div class="gem-date">{{ activeGem.date || '未知時間' }}</div>
<!-- 記錄的內文 -->
<p class="gem-text">{{ activeGem.note }}</p>
<!-- 情況 A:如果是今天採集的光珠,允許編輯與撤回 -->
<div v-if="activeGem.date === todayStr" class="gem-actions">
<button class="btn-sub edit" @click="editGem">編輯</button>
<button class="btn-sub delete" @click="deleteGem">撤回</button>
</div>
<!-- 情況 B:過期光珠,無法撤回 -->
<div v-else class="locked-hint">
昔日回憶已無法撤回
</div>
<button class="btn-close" @click="closeGem">收起微光</button>
</div>
</div>
在 <style scoped> 尾部補上幾行CSS:
/* 日期標籤文字 */
.gem-date {
font-size: 0.75rem;
color: #8b949e;
margin-bottom: 8px;
letter-spacing: 1px;
}
/* 鎖定提示文字 */
.locked-hint {
font-size: 0.75rem;
color: #6e7681;
margin-bottom: 16px;
font-style: italic;
}
/* 操作按鈕群組 */
.gem-actions {
display: flex;
justify-content: center;
gap: 10px;
margin-bottom: 16px;
}
.btn-sub {
border: none;
border-radius: 12px;
padding: 4px 12px;
font-size: 0.75rem;
cursor: pointer;
transition: opacity 0.2s;
}
.btn-sub.edit {
background: rgba(255, 255, 255, 0.15);
color: #c9d1d9;
}
.btn-sub.delete {
background: rgba(248, 81, 73, 0.2);
color: #f85149;
}
.btn-sub:hover {
opacity: 0.8;
}
💡開發者測試技巧:如何重置或偽造「歷史光珠」?
在導入了日期過期鎖定後,我們可能會遇到兩個測試上的需求:
這時瀏覽器的開發人員工具就是最好的工具:
清空所有光珠:
按下 F12 打開開發者工具,切換至 Application 分頁 -> 左側展開 Local Storage -> 點擊本機網址。找到 store_gems 項目,按右鍵點擊 Delete,再重新整理網頁,就能得到一個空瓶子。
偽造一顆「昨天」的微光來測試鎖定:
直接在 Local Storage 的 store_gems 內容點兩下進行編輯,找到其中一顆光珠,手動將其 "date" 改成昨天的日期(例如 "2026-09-28"),按 Enter 儲存並重新整理網頁。
此時回到畫面點開該顆光珠,你就會看見編輯與撤回按鈕消失,替換成 昔日回憶已無法撤回,用來驗證時光鎖定邏輯!
今天我們完成了採集瓶從純粹的圖形動態,邁向具備真實時間序列的日記應用:
以現實生活來說,現代人的作息真的會剛好在半夜 12:00(00:00)準時結束一天嗎?
明天(Day 16),我們將更改系統的 0 點分界,打造更符合現代人作息的時間判定。