一、登陆页面

<!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(), // 用于确保用户名唯一性的Set
notes: [],
},
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(); // 使用时间戳作为唯一ID
const 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();
}
});