一、登陸頁面

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>記事本登錄注冊</title><link rel="stylesheet" href="../css/login.css">
</head><body><div id="app"><!-- 登錄頁面 --><div v-show="isLoginPage"><h2>登錄</h2><form><input v-model="loginUsername" placeholder="用戶名" /><input v-model="loginPassword" placeholder="密碼" /><button type="button" @click="login">登錄</button><button type="button" @click="showRegisterPage">注冊</button></form></div><!-- 注冊頁面 --><div v-show="isRegisterPage"><h2>注冊</h2><form><input v-model="registerUsername" placeholder="用戶名" /><input v-model="registerPassword" placeholder="密碼" type="password" /><button type="button" @click="register">注冊</button><button type="button" @click="showLoginPage">返回登錄</button></form></div></div><script src="https://cdn.jsdelivr.net/npm/vue@2"></script><script src="../js/login.js"></script>
</body></html>
body {font-family: Arial, sans-serif;display: flex;justify-content: center;align-items: center;min-height: 100vh;margin: 0;background-color: #f4f4f4;
}div {background-color: white;padding: 20px;border-radius: 5px;box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);text-align: center;
}h2 {text-align: center;
}input {width: 80%;padding: 10px;margin-bottom: 15px;border: 1px solid #ccc;border-radius: 3px;
}button {width: 20%;padding: 10px;background-color: #4CAF50;color: white;border: none;border-radius: 3px;cursor: pointer;margin: 30px;
}button:hover {background-color: #45a049;
}
new Vue({el: '#app',data: {isLoginPage: true,isRegisterPage: false,isLoggedIn: false,loginUsername: '',loginPassword: '',registerUsername: '',registerPassword: '',registeredUsers: [], // 存儲已注冊用戶信息的數組usernamesSet: new Set(), // 用于確保用戶名唯一性的Setnotes: [],},mounted() {const storedUsers = localStorage.getItem('registeredUsers');if (storedUsers) {this.registeredUsers = JSON.parse(storedUsers);this.registeredUsers.forEach(user => {this.usernamesSet.add(user.username);});}},methods: {login() {const inputUsername = this.loginUsername;const inputPassword = this.loginPassword;const user = this.registeredUsers.find(u => u.username === inputUsername && u.password === inputPassword);if (user) {console.log('登錄成功,跳轉到指定頁面');window.location.href = '../html/index.html';} else {alert('用戶名或密碼錯誤,請重試!');}},register() {const usernamePattern = /^[a-zA-Z0-9]+$/;const passwordPattern = /.{6,}/;if (!this.registerUsername.match(usernamePattern)) {alert('用戶名只能包含字母和數字');return;}if (!this.registerPassword.match(passwordPattern)) {alert('密碼長度至少為6位');return;}if (this.usernamesSet.has(this.registerUsername)) {alert('該用戶名已注冊,請更換用戶名');return;}const userId = Date.now(); // 使用時間戳作為唯一IDconst newUser = {id: userId,username: this.registerUsername,password: this.registerPassword};this.registeredUsers.push(newUser);this.usernamesSet.add(this.registerUsername);localStorage.setItem('registeredUsers', JSON.stringify(this.registeredUsers));alert('注冊成功');console.log('已注冊用戶數組:', this.registeredUsers);this.isRegisterPage = false;this.isLoginPage = true;},showRegisterPage() {this.isLoginPage = false;this.isRegisterPage = true;},showLoginPage() {this.isRegisterPage = false;this.isLoginPage = true;}}
});
二、記事本頁面

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>記事本</title><!-- 引入重置樣式表 --><link href="../css/normalize.css" /><link href="../css/reset.css" /><link href="../css/index.css" rel="stylesheet" />
</head><body><!-- 引入 Vue --><script src="https://cdn.jsdelivr.net/npm/vue@2"></script><div id="contain"><div id="box"><div id="header"><span>記事本</span></div><div id="main"><div id="mainTop"><!-- 輸入框綁定 message 數據,按下回車鍵或點擊按鈕觸發 add 方法 --><input id="text" v-model="message" placeholder="just to do it!" @keyup.enter="add" /><button id="add" v-on:click="add">新增筆記</button><!-- 輸入框綁定 message 數據,按下回車鍵或點擊按鈕觸發 add 方法 --><br /><input id="search" v-model="searchKeyword" placeholder="搜索筆記" /><button id="check" v-on:click="select">查詢筆記</button></div><div id="mainBottom"><!-- 使用 ul 和 v-for 渲染筆記列表 --><ul><li v-for="(item, index) in searchResult.length > 0? searchResult : filteredNotesList" :key="index"><!-- 筆記內容展示部分 --><!-- 根據 delMuch 狀態顯示復選框,并綁定 checked 屬性 --><input type="checkbox" v-if="delMuch" v-model="item.checked"><span id="order">{{index + 1}} <!-- 根據 isEditing 狀態顯示文本或輸入框 --><!--根據 item.isEditing 的狀態來決定顯示筆記文本還是輸入框。當 isEditing 為 false 時顯示筆記文本,為 true 時顯示輸入框,并且輸入框通過 v-model 綁定 tempEditValue,用于編輯筆記內容。--><label v-if="!item.isEditing" id="content">{{item.text}}</label><input id="newMessage" v-else v-model="tempEditValue" type="text"></span><span id="right"><!-- 根據 isEditing 狀態顯示不同按鈕 --><button v-if="!item.isEditing" id="edit" @click="edit(index)">編輯</button><!--現在根據 item.isEditing 的狀態切換顯示不同按鈕。當處于非編輯狀態時顯示 “編輯” 按鈕,點擊后進入編輯狀態;進入編輯狀態后顯示 “確定” 和 “取消” 按鈕,分別用于確認修改和取消修改。--><button v-else id="confirm" @click="confirmEdit(index)">確定</button><button v-else id="cancel" @click="cancelEdit(index)">取消</button><button id="del" @click="del(index)">刪除</button></span></span></li></ul></div></div><div id="footer" v-show="NotesList.length > 0"><!-- 顯示筆記數量 --><span id="count">共有{{count}}條筆記</span><!-- 點擊按鈕觸發批量刪除和清空操作 --><button id="delMany" v-on:click="delMany" class="delS">批量刪除</button><button id="clear" v-on:click="clear">清空記事本</button></div></div></div><!-- 引入優化后的 JS 文件 --><script src="../js/index.js"></script>
</body></html>
/* 全局設置,將 box-sizing 設置為 border-box,避免尺寸計算問題 */
* {box-sizing: border-box;
}body {background-image: url(../src/img/image.png);background-size: cover;background-repeat: no-repeat;
}#box {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);
}#header {background-color: rgb(177, 223, 79, .8);width: 830px;height: 80px;border: 1px solid black;font-size: 25px;text-align: center;line-height: 80px;
}#main {width: 830px;height: 360px;border-left: 1px solid black;border-right: 1px solid black;background-color: rgb(255, 255, 255, .8);overflow: hidden;
}#mainTop {height: 60px;border: 1px solid black;display: flex;align-items: center;
}#text,
#search {width: 60%;height: 60px;box-sizing: border-box;font-size: 20px;float: left;
}#add,
#check {width: 15%;height: 60px;font-size: 17px;box-sizing: border-box;float: left;
}#mainBottom {height: 300px;overflow: auto;border: 1px solid black;position: relative;
}#newMessage,
#content {display: inline-block;height: 40px;line-height: 40px;width: 620px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;font-size: 20px;position: relative;vertical-align: middle;
}#footer {width: 830px;height: 70px;border: 1px solid black;background-color: rgba(177, 223, 79, .8);text-align: center;line-height: 70px;display: flex;align-items: center;position: relative;
}#text::placeholder {font-size: 22px;color: rgba(177, 220, 100, 1);padding-left: 30px;
}#search::placeholder {padding-left: 30px;
}#count {position: absolute;left: 10px;
}ul {padding: 0;margin: 0;
}li {height: 40px;box-sizing: border-box;display: flex;align-items: center;line-height: 40px;
}li:nth-child(2n + 1) {background-color: rgba(128, 128, 150, .3);
}#delMany {background-color: white;height: 40px;position: absolute;width: 90px;right: 120px;
}#clear {background-color: white;height: 40px;position: absolute;width: 90px;right: 10px;
}#right {right: 15px;position: absolute;font-size: 18px;
}#confirm,
#edit {margin-right: 8px;width: 45px;height: 30px;
}#del {width: 45px;height: 30px;
}#order {padding-left: 20px;
}
var app = new Vue({el: "#contain",data: {NotesList: [],count: 0,message: "",delMuch: false,tempEditValue: '',selectedCategory: '',searchKeyword: '',priorities: ['高', '中', '低'],selectedPriority: '',searchResult: [],searchResultCount: 0},computed: {filteredNotesList() {let filtered = this.NotesList;if (this.selectedCategory) {filtered = filtered.filter(item => item.category === this.selectedCategory);}if (this.searchKeyword) {const keyword = this.searchKeyword.toLowerCase();filtered = filtered.filter(item => item.text.toLowerCase().includes(keyword));}if (this.selectedPriority) {filtered = filtered.filter(item => item.priority === this.selectedPriority);}return filtered;},highlightedSearchResult() {return this.searchResult.map(item => {const keyword = this.searchKeyword;if (keyword) {const regex = new RegExp(keyword, 'gi');item.highlightedText = item.text.replace(regex, '<mark>$&</mark>');} else {item.highlightedText = item.text;}return item;});}},watch: {searchKeyword: function (newKeyword) {this.select();}},methods: {clear: function () {const confirmation = confirm('你確定要清空記事本嗎');if (confirmation) {this.NotesList = [];this.count = 0;this.saveNotesToLocalStorage();alert('操作已執行!');} else {alert('操作已取消。');}},add: function () {const input = this.message.trim();if (input) {this.NotesList.unshift({text: input,isEditing: false,checked: false,category: '工作',priority: '中'});this.count++;this.message = "";if (this.searchKeyword) {this.select();}this.saveNotesToLocalStorage();} else {alert("請輸入有效內容");}},edit: function (index) {if (this.filteredNotesList[index]) {const realIndex = this.NotesList.indexOf(this.filteredNotesList[index]);this.NotesList[realIndex].isEditing = true;this.tempEditValue = this.NotesList[realIndex].text;}},confirmEdit: function (index) {const newText = this.tempEditValue.trim();if (newText && this.filteredNotesList[index]) {const realIndex = this.NotesList.indexOf(this.filteredNotesList[index]);this.NotesList[realIndex].text = newText;}if (this.filteredNotesList[index]) {const realIndex = this.NotesList.indexOf(this.filteredNotesList[index]);this.NotesList[realIndex].isEditing = false;}if (this.searchKeyword) {this.select();}this.saveNotesToLocalStorage();},cancelEdit: function (index) {if (this.filteredNotesList[index]) {const realIndex = this.NotesList.indexOf(this.filteredNotesList[index]);this.NotesList[realIndex].isEditing = false;this.tempEditValue = this.NotesList[realIndex].text;}},del: function (index) {if (this.filteredNotesList.length > 0 && index >= 0 && index < this.filteredNotesList.length) {const realIndex = this.NotesList.indexOf(this.filteredNotesList[index]);this.NotesList.splice(realIndex, 1);this.count--;if (this.searchKeyword) {this.select();}this.saveNotesToLocalStorage();}},delMany: function () {this.delMuch = true;const originalLength = this.NotesList.length;this.NotesList = this.NotesList.filter(item => !item.checked);const deletedCount = originalLength - this.NotesList.length;if (deletedCount > 0) {this.count = this.NotesList.length;this.delMuch = false;const isConfirmed = confirm('你確定要刪除選中的筆記嗎?');if (isConfirmed) {alert(`已成功刪除 ${deletedCount} 條筆記`);}}if (this.searchKeyword) {this.select();}this.saveNotesToLocalStorage();},select: function () {const keyword = this.searchKeyword.trim().toLowerCase();if (keyword) {this.searchResult = this.NotesList.filter(item => item.text.toLowerCase().includes(keyword));this.searchResultCount = this.searchResult.length;if (this.searchResultCount === 0) {alert('沒有查詢到相關筆記');} else {alert(`查詢到 ${this.searchResultCount} 條相關筆記`);}} else {this.searchResult = [];this.searchResultCount = 0;}},addOrSearch: function (event) {if (event.key === 'Enter') {if (event.ctrlKey) {this.select();} else {this.add();}}},saveNotesToLocalStorage() {localStorage.setItem('notes', JSON.stringify(this.NotesList));},loadNotesFromLocalStorage() {const notes = localStorage.getItem('notes');if (notes) {this.NotesList = JSON.parse(notes);this.count = this.NotesList.length;}}},mounted() {this.loadNotesFromLocalStorage();}
});