JavaScript 系列七:第7課 ── 認識 Template Refs

by applelily

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript 系列七:第7課 ── 認識 Template Refs</title>
    <link rel="stylesheet" href="./07_js_vue_Template-Refs.css">
</head>
<body>
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
 
    
    <div id="app">
        <div class="input_content">
            <div class="input_group">
                <div class="label">標題</div>
                <input class="input_area" type="text" placeholder="標題" v-model="newNote.title">
            </div>
            <div class="input_group">
                <div class="label">記事</div>
                <!-- 在 textarea 元素上设置 ref 来引用它 -->
                <textarea
                    ref="autoResizeTextArea"
                    class="input_area"
                    v-model="newNote.content"
                    cols="50"
                    rows="3"
                    placeholder="新增記事">
                </textarea>
            </div>
            <div class="input_group">
                <div class="label">顏色</div>
                <select class="input_area" v-model="newNote.color">
                    <option value="red">紅色</option>
                    <option value="green">綠色</option>
                    <option value="blue">藍色</option>
                </select>
            </div>
            <div class="input_group">
                <button class="create_btn" @click="addNote">新增</button>
            </div>
        </div>
    
        <div class="wrap">
          <div v-for="(note, index) in notes" :key="index" class="note" :class="note.color">
              <h3 class="title">
                  {{ note.title }}
                  <button class="delete_btn" @click="deleteNote(index)">X</button>
              </h3>
              <p class="content">
                  {{ note.content }}
              </p>
          </div>
      </div>
    </div>
    <script...

CSS

* {
    padding: 0;
    margin: 0;
    list-style: none;
    box-sizing: border-box;
}

.input_content {
    width: 760px;
    border-radius: 5px;
    border: 1px solid #ccc;
    padding: 10px;
    margin-left: 10px;
}

.input_group {
    position: relative;
    margin-bottom: 20px; /* Adds space between input groups */
    display: flex; /* 將容器設置為 Flexbox 以便對齊子元素 */
}

.label {
    position: absolute;
    left: 10px; 
    top: -10px;
    font-size: 16px;
    font-weight: bold;
    color: #277eb0;
    background-color: #fff;
}

.input_area {
    font-size: 20px;
    line-height: 150%;
    box-sizing: border-box;
    padding: 10px;
    border: 1px solid #ccc;
}

.create_btn {
    margin-left: auto; /* 將按鈕推到右側 */
    padding: 8px 16px;
    font-size: 20px;
    border-radius: 20px;
    border: 1px solid #ccc;
    width: 100px;
    letter-spacing: 5px;
}

.create_btn:hover {
    cursor: pointer;
}

.create_btn:active {
    background-color: #e24d4d;
    color: #f5cdcd;
}

.wrap {
    display: flex;
    flex-wrap: wrap; /* 允許項目換行 */
    align-content: flex-start; /* 對齊方式,這裡是從上到下 */
}

.delete_btn {
    background: none; /* 去掉背景 */
    border: none; /* 去掉邊框 */
    color: red; /* X 的顏色 */
    font-weight: bold; /* 字體加粗 */
    cursor: pointer; /* 鼠標懸停時顯示為手型 */
    position: absolute; /* 絕對定位 */
    top: -6px; /* 距離上邊界的距離 */
    right: -4px; /* 距離右邊界的距離 */
}


.note {
    width: 240px;
    height: 100px;
    border: 1px solid #ccc;
    border-radius: 5px;
    margin: 10px;
    padding: 15px;
    position: relative; /* 新增這一行 */
}

.red {
    background-color: #f7d5d9;
}

.green {
    background-color: #D0EDDA;
}

.blue {
    background-color: #C9E4FD;
}

JavaScript

const { createApp } = Vue;

const app = createApp({
    data() {
        return {
            notes: [
                {
                    title: "春節行程安排",
                    content: "吃飽睡,睡飽吃",
                    color: "red",
                },
                {
                    title: "工作待辦事項",
                    content: "詢問各家廠商報價",
                    color: "green",
                },
                {
                    title: "運動健身計畫",
                    content: "每天早上六點去健身",
                    color: "blue",
                },
            ],
            newNote: {
                title: '',
                content: '',
                color: 'red' // 預設顏色
            }
        };
    },
    methods: {
        addNote() {
            if (this.newNote.title && this.newNote.content) {
                this.notes.push({ ...this.newNote }); // 將新筆記添加到 notes 陣列中, 這是淺拷貝
                this.newNote = { title: '', content: '', color: 'red' }; // 清空表單
            } else {
                alert("請填寫標題和內容"); // 確保標題和內容不為空
            }
        },
        deleteNote(index) {
            this.notes.splice(index, 1); // 刪除指定索引的筆記
        }
    },
    watch: {
        // 監聽 notes 陣列的變化
        notes: {
            handler(newNotes) {
                // 每次 notes 更新後,將 notes 存儲到 Local Storage
                localStorage.setItem('notes', JSON.stringify(newNotes));
            },
            deep: true // 監聽嵌套物件的變化
        }
    },
    mounted() {
        // 應用啟動時從 Local Storage 加載筆記
        const savedNotes = localStorage.getItem('notes');
        if (savedNotes) {
            this.notes = JSON.parse(savedNotes); // 如果有筆記資料,則加載它們
        }
        // 使用 ref 引用 textarea 并应用 autosize
        autosize(this.$refs.autoResizeTextArea);
    }
});

// 將 Vue 應用實例掛載到 #app
app.mount('#app');