JSFiddle - React, Tailwind, and code Playground

HTML

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/autosize.js/3.0.20/autosize.min.js"></script>
<h1 style="text-align: center;">JavaScript 系列七:第7課 ── 認識 Template Refs</h1>

<div id="app">
  <div class="control">
    <input type="text" v-model="newTitle" placeholder="標題">
    <!-- 為方便觀察autosize第三方套件,故將rows設為3 -->
    <textarea cols="30" rows="3" v-model="newContent" placeholder="內容" ref="textarea"></textarea>
    <select v-model="newColor">
      <option value="" disabled selected>選擇顏色</option>
      <option value="red">紅色</option>
      <option value="green">綠色</option>
      <option value="blue">藍色</option>
    </select>
    <button @click="addNote">新增</button>
  </div>

  <div class="result">
    <div class="note" v-for="(note, index) of notes" :style="{ color: note.color ,'border-color': note.color }">
      <h3 class="title">{{ note.title }}</h3>
      <p class="content">{{ note.content }}</p>
      <button @click="deleteNote(note, index)" :style="{ color: note.color}">X</button>
    </div>
  </div>
</div>

CSS

* {
  padding: 0;
  margin: 0;
}

#app {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.control {
  display: flex;
  flex-direction: column;
  width: 50%;
}

.control input,
.control textarea,
.control section,
.control button {
  margin: 10px 0;
  padding: 5px;
  width: 100%;
  box-sizing: border-box;
}

.control textarea {
  text-align: justify;
  line-height: 1.5;
}

.result {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 75%;
}

.note {
  margin: 20px 0;
  border: 1px solid #ccc;
  width: 100%;
  padding: 20px;
  box-sizing: border-box;
  text-align: center;
  position: relative;
}

.result button {
  position: absolute;
  top: 5px;
  right: 5px;
  background-color: transparent;
  border: none;
  cursor: pointer;
  padding: 5px;
}

JavaScript

const { createApp } = Vue;

createApp({
  data() {
    return {
      newTitle: "",
      newContent: "",
      newColor: "",
      notes: [
        {
          title: "春節行程安排",
          content: "吃飽睡,睡飽吃",
          color: "red"
        },
        {
          title: "工作待辦事項",
          content: "詢問各家廠商報價",
          color: "green"
        },
        {
          title: "運動健身計畫",
          content: "每天早上六點去健身",
          color: "blue"
        }
      ]
    };
  },
  watch: {
    notes: {
      handler(newValue, oldValue) {
        localStorage.setItem("notes", JSON.stringify(newValue));
      },
      deep: true
    }
  },
  methods: {
    addNote() {
      if (
        this.newTitle !== "" &&
        this.newContent !== "" &&
        this.newColor !== ""
      ) {
        let newNote = {
          title: this.newTitle,
          content: this.newContent,
          color: this.newColor
        };
        this.notes.push(newNote);
        this.newTitle = "";
        this.newContent = "";
        this.newColor = "";
      } else {
        alert("請輸入所有資料");
      }
    },
    deleteNote(note, index) {
      this.notes.splice(index, 1);
    }
  },
  mounted() {
    this.notes[0].content = "多出門、到處走走、也要多運動";

    if (localStorage.getItem("notes")) {
      this.notes = JSON.parse(localStorage.getItem("notes"));
    }

    // autosize
    autosize(this.$refs.textarea);
  }
}).mount("#app");