
html
<template>
<view class="wrap">
<!-- 顶部 -->
<view class="header">
<text class="title">简易记事本</text>
</view>
<!-- 搜索 -->
<view class="search-box">
<input v-model="keyword" placeholder="搜索笔记..." @input="doSearch" />
</view>
<!-- 列表 -->
<view class="list">
<view class="item" v-for="(item, idx) in showList" :key="idx">
<view class="content" @click="toEdit(item)">
<text class="txt">{{ item.title }}</text>
<text class="time">{{ item.time }}</text>
</view>
<view class="del" @click="delNote(idx)">删除</view>
</view>
<view class="empty" v-if="showList.length === 0">
暂无笔记,点击右下角新增
</view>
</view>
<!-- 悬浮新增按钮 -->
<view class="add-float-btn" @click="toEdit">
<text class="add-icon">+</text>
</view>
<!-- 编辑弹窗 -->
<view class="modal" v-show="showModal">
<view class="card">
<view class="modal-head">
<text>{{ currentItem.idx > -1 ? '编辑笔记' : '新增笔记' }}</text>
<view @click="closeModal">×</view>
</view>
<textarea v-model="form.title" placeholder="请输入内容" class="area"></textarea>
<view class="btns">
<button @click="closeModal">取消</button>
<button @click="saveNote">保存</button>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
noteList: [],
showList: [],
keyword: '',
showModal: false,
form: { title: '' },
currentItem: { idx: -1 }
}
},
onLoad() {
this.getList()
},
methods: {
// 读取本地
getList() {
let list = uni.getStorageSync('noteList') || []
this.noteList = list
this.showList = list
},
// 保存本地
setList(list) {
this.noteList = list
this.showList = list
uni.setStorageSync('noteList', list)
},
// 搜索
doSearch() {
let k = this.keyword.trim()
if (!k) {
this.showList = this.noteList
return
}
this.showList = this.noteList.filter(item => {
return item.title.includes(k)
})
},
// 打开编辑
toEdit(item) {
if (item) {
this.currentItem = item
this.form.title = item.title
} else {
this.currentItem = { idx: -1 }
this.form.title = ''
}
this.showModal = true
},
// 关闭
closeModal() {
this.showModal = false
this.form.title = ''
},
// 保存
saveNote() {
let title = this.form.title.trim()
if (!title) return uni.showToast({ title: '请输入内容', icon: 'none' })
let time = new Date().toLocaleString()
let list = JSON.parse(JSON.stringify(this.noteList))
if (this.currentItem.idx > -1) {
// 编辑
list[this.currentItem.idx] = { title, time }
} else {
// 新增
list.unshift({ title, time })
}
this.setList(list)
this.closeModal()
uni.showToast({ title: '保存成功' })
},
// 删除
delNote(idx) {
uni.showModal({
title: '提示',
content: '确定删除该笔记?',
success: res => {
if (res.confirm) {
let list = JSON.parse(JSON.stringify(this.noteList))
list.splice(idx, 1)
this.setList(list)
uni.showToast({ title: '删除成功' })
}
}
})
}
}
}
</script>
<style scoped>
.wrap {
padding: 20rpx;
background: #f7f8fa;
min-height: 100vh;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.title {
font-size: 38rpx;
font-weight: bold;
}
/* 悬浮圆形新增按钮 */
.add-float-btn {
position: fixed;
right: 40rpx;
bottom: 40rpx;
width: 100rpx;
height: 100rpx;
border-radius: 50%;
background-color: #42b983;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 16rpx rgba(66, 185, 131, 0.3);
z-index: 99;
}
.add-icon {
font-size: 50rpx;
color: #fff;
font-weight: bold;
line-height: 1;
}
.search-box {
background: #fff;
border-radius: 12rpx;
padding: 20rpx;
margin-bottom: 20rpx;
}
.search-box input {
font-size: 28rpx;
}
.list {
background: #fff;
border-radius: 12rpx;
overflow: hidden;
}
.item {
display: flex;
padding: 30rpx;
border-bottom: 1rpx solid #eee;
}
.content {
flex: 1;
}
.txt {
font-size: 30rpx;
display: block;
margin-bottom: 10rpx;
}
.time {
font-size: 24rpx;
color: #999;
}
.del {
color: #ff4d4f;
font-size: 26rpx;
align-self: center;
padding: 10rpx;
}
.empty {
padding: 60rpx;
text-align: center;
color: #999;
}
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
}
.card {
width: 80%;
background: #fff;
border-radius: 20rpx;
overflow: hidden;
}
.modal-head {
padding: 30rpx;
display: flex;
justify-content: space-between;
font-size: 32rpx;
border-bottom: 1rpx solid #eee;
}
.area {
padding: 30rpx;
min-height: 300rpx;
font-size: 30rpx;
}
.btns {
display: flex;
padding: 20rpx;
justify-content: flex-end;
gap: 20rpx;
}
.btns button {
padding: 15rpx 30rpx;
border-radius: 10rpx;
font-size: 28rpx;
}
.btns button:first-child {
background: #f5f5f5;
}
.btns button:last-child {
background: #42b983;
color: #fff;
}
</style>