自学微信小程序开发第六天- TODOS案例
TODOS是一个待办事项的小程序,用这个来测试我们之前学到的内容。
页面呈现
准备达成的目标界面如图所示,按照这个图来设计页面。
界面结构
首先,最上方标题文字更改和最下方的 tagbar 在 app.json 里更改。
{
"pages": [
"pages/index/index",
"pages/logs/logs"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "TODOS",
"navigationBarTextStyle": "black"
},
"style": "v2",
"sitemapLocation": "sitemap.json",
"tabBar": {
"list": [{
"pagePath": "pages/index/index",
"text": "todos",
"iconPath": "images/todos.png",
"selectedIconPath": "images/todos-active.png"
},{
"pagePath": "pages/logs/logs",
"text": "日志",
"selectedIconPath": "images/logs-active.png",
"iconPath": "images/logs.png"
}]
}
}
再就是页面布局结构,这里很多应该是根据后台呈现的内容,先不管,把整体结构先规划出来,之后再进行更改。
<!--index.wxml-->
<view class="container">
<!-- 增加项目 -->
<view class="add">
<image src="../../images/plus.png"></image>
<input type="text" placeholder="准备做的事" />
</view>
<!-- 需要做的事 -->
<view class="todos">
<view class="item">
<icon type="circle"></icon>
<text>做界面布局</text>
<icon type="clear"></icon>
</view>
<view class="item">
<icon type="success"></icon>
<text>抽象业务数据</text>
<icon type="clear"></icon>
</view>
<view class="item">
<icon type="circle"></icon>
<text>等等</text>
<icon type="clear"></icon>
</view>
</view>
<!-- 页脚 -->
<view class="footer">
<text>切换全部</text>
<text>剩余2件事</text>
<text>清除完成项</text>
</view>
</view>
添加界面样式
界面样式主要是在 .wxss 里完成,不过页面中使用了 <icon> 标签,这个标签的大小使用标签的 size 来调整。其他的均使用样式表。
<icon type="clear" size="32rpx"></icon>
/**index.wxss**/
/* 页面容器 */
.container{
border-top: 1rpx solid #e0e0e0; /* 最上方的横线,以上边框的形式呈现 */
}
/* 增加项目栏 */
.add{
border: 1rpx solid #e0e0e0; /* 边框 */
border-radius: 10rpx; /* 边框圆角 */
box-shadow: 0 0 10rpx #e0e0e0; /* 阴影 */
align-items: center; /* 侧轴对齐 */
display: flex; /* 设置为flex盒子 */
padding: 20rpx; /* 内边距 */
margin: 20rpx; /* 外边距 */
}
/* 增加项目栏下的图片 */
.add image{
width: 40rpx; /* 宽 */
height: 40rpx; /* 高 */
margin-right: 20rpx; /* 右侧外边距 */
}
/* 需要做到事项区域 */
.todos{
border: 1rpx solid #e0e0e0; /* 边框 */
border-radius: 10rpx; /* 边框圆角 */
box-shadow: 0 0 10rpx #e0e0e0; /* 阴影 */
align-items: center; /* 子元素侧轴对齐 */
margin: 20rpx; /* 外边距 */
}
/* 任务项 */
.todos .item{
padding: 20rpx; /* 内边距 */
border-bottom: 1rpx solid #e0e0e0; /* 底部边框 */
display: flex; /* 设置为flex盒子 */
align-items: center; /* 子元素侧轴对齐 */
}
/* 最后一个任务项,不需要底部边框 */
.todos .item:last-child{
padding: 20rpx; /* 内边距 */
display: flex; /* 设置为flex盒子 */
align-items: center; /* 子元素侧轴对齐 */
}
/* 任务项里的文本 */
.todos .item text{
flex: 1; /* 获取全部剩余空间 */
font-size: 30rpx; /* 字体大小 */
color: #444; /* 字体颜色 */
margin-left: 20rpx; /* 左侧外边距 */
}
/* 页脚 */
.footer{
font-size: 30rpx; /* 字体大小 */
margin: 20rpx; /* 外边距 */
display: flex; /* 设置为flex盒子 */
justify-content: space-between; /* 子元素主轴对齐 */
color: #333; /* 字体颜色 */
}
界面交互
抽象数据模型
页面设计好了,就要抽象数据模型了。抽象数据模型就是根据页面情况,确定添加哪些数据成员。即,确定哪个数据在程序使用过程中会改变,将其设计成变量。
// index.js
Page({
data: {
// 文本框数据
add: '',
// 任务列表
todos: [
{ name: '', completed: false }
],
// 剩余数量
leftCount: 0
}
})
界面数据绑定
数据模型建立好了,则要和相应的元素进行绑定。这里有个需注意的地方:需要做的事中具体项目的图标类型和文本样式会改变,使用三元表达式进行判断,然后随之进行改变。
<!--index.wxml-->
<view class="container">
<!-- 增加项目 -->
<view class="add">
<image src="../../images/plus.png"></image>
<input type="text" placeholder="准备做的事" value="{{add}}" />
</view>
<!-- 需要做的事 -->
<view class="todos">
<view class="item{{item.completed ? ' completed' : ''}}" wx:for="{{todos}}" >
<icon type="{{item.completed ? 'success' : 'circle'}}"></icon>
<text>{{item.name}}</text>
<icon type="clear" size="32rpx"></icon>
</view>
</view>
<!-- 页脚 -->
<view class="footer">
<text>切换全部</text>
<text>剩余{{leftCount}}件事</text>
<text>清除完成项</text>
</view>
</view>
新增样式,已完成任务的文本样式,改变字体颜色,增加删除线。
/* 任务项里已完成任务的文本 */
.todos .item.completed text{
flex: 1; /* 获取全部剩余空间 */
font-size: 30rpx; /* 字体大小 */
color: #888; /* 字体颜色 */
margin-left: 20rpx; /* 左侧外边距 */
text-decoration: line-through; /* 中划线 */
}
界面交互的操作
文本框输入数据,添加至列表
因为小程序的数据流是单向的,所以要绑定事件,来获取文本框输入的内容。点击 “+” 也要绑定事件,来向列表数组添加数据。
<!-- 增加项目 -->
<view class="add">
<image src="../../images/plus.png" bindtap="addTodo"></image>
<input type="text" placeholder="准备做的事" value="{{add}}" bindinput="textChange" bindconfirm="addTodo" focus />
</view>
// 将文本框的数据添加至任务列表数组
addTodo: function () {
if (!this.data.add) return // 如果文本框是空的,则终止执行
var todos = this.data.todos
todos.push({
name: this.data.add,
completed: false
})
this.setData({ todos: todos, add: '' })
},
// 文本有改变,改变相应数据成员
textChange: function (e) {
this.setData({ add: e.detail.value })
}
切换任务完成状态
切换任务状态需要给每个 item 项绑定个点击事件,且绑定个 data 数据,以便获取点击的项的索引号进行操作。
<!-- 需要做的事 -->
<view class="todos">
<view class="item{{item.completed ? ' completed' : ''}}" wx:for="{{todos}}" bindtap="toggleState" data-index="{{index}}">
<icon type="{{item.completed ? 'success' : 'circle'}}"></icon>
<text>{{item.name}}</text>
<icon type="clear" size="32rpx"></icon>
</view>
</view>
// 切换单项任务状态
toggleState: function (e) {
var item = this.data.todos[e.currentTarget.dataset.index] // 获取当前点击元素的索引号,确定具体 item 项
item.completed = !item.completed // 单项的完成情况取非
this.setData({ todos: this.data.todos }) //刷新数据绑定
}
剩余数量的实时改变
剩余数量绑定了数据成员 leftCount ,即需要在新增项、删除、切换等操作时维护此数据成员。已经完成了新增、切换的操作,所以先在这两个操作中进行维护。另在呈现的时候,如果 leftCount 为 0 ,则剩余的文本不显示,可以使用三元表达式进行判断。
<!-- 页脚 -->
<view class="footer">
<text>切换全部</text>
<text hidden="{{leftCount == 0 ? true : false}}">剩余{{leftCount}}件事</text>
<text>清除完成项</text>
</view>
// 将文本框的数据添加至任务列表数组
addTodo: function () {
if (!this.data.add) return
var todos = this.data.todos
todos.push({
name: this.data.add,
completed: false
})
this.setData({
todos: todos,
add: '',
leftCount: this.data.leftCount + 1
})
},
// 切换单项任务状态
toggleState: function (e) {
// 根据参数获得的索引号,确定具体项目
var item = this.data.todos[e.currentTarget.dataset.index]
item.completed = !item.completed
// 根据当前任务状态决定增加还是减少
var leftCount = this.data.leftCount + (item.completed ? -1 : 1)
this.setData({ todos: this.data.todos, leftCount: leftCount })
}
删除任务
删除任务是点任务列表里单项文本后面的叉号进行删除,除了需要绑定事件,还需要操作数组,维护数据成员。另,叉号的事件必须阻止冒泡。
<!-- 需要做的事 -->
<view class="todos">
<view class="item{{item.completed ? ' completed' : ''}}" wx:for="{{todos}}" wx:key="name" bindtap="toggleState" data-index="{{index}}">
<icon type="{{item.completed ? 'success' : 'circle'}}"></icon>
<text>{{item.name}}</text>
<icon type="clear" size="32rpx" catchtap="deleteItem" data-index="{{index}}"></icon>
</view>
</view>
// 删除单项任务
deleteItem: function (e) {
var todos = this.data.todos
// 根据索引号查找是否完成,决定leftCount是否改变
var item = this.data.todos[e.currentTarget.dataset.index]
var leftCount = this.data.leftCount + (item.completed ? 0 : -1)
// 根据索引号删除数组中的元素
todos.splice(e.currentTarget.dataset.index, 1)
this.setData({ todos: todos, leftCount: leftCount })
}
切换全部
点击后是切换成完成还是未完成,需要设置个标记变量来判断。
<!-- 页脚 -->
<view class="footer">
<text bindtap="toggleAll">切换全部</text>
<text hidden="{{leftCount == 0 ? true : false}}"> 剩余{{leftCount}}件事 </text>
<text>清除完成项</text>
</view>
data: {
// 文本框数据
add: '',
// 任务列表
todos: [],
// 剩余数量
leftCount: 0,
allCompleted: false
},
// 切换全部任务状态
toggleAll: function () {
// 切换全部任务的状态为
this.data.allCompleted = !this.data.allCompleted
var todos = this.data.todos
// this在函数内部使用出错,不是指page对象
var that = this
// 历遍todos更改completed状态
todos.forEach(function (item) {
item.completed = that.data.allCompleted
})
// 更改剩余数量
var leftCount = (this.data.allCompleted ? 0 : todos.length)
// 刷新值
this.setData({ todos: todos, leftCount: leftCount })
}
另外切换状态也涉及了 allCompleted ,所以也需要增加判断。
// 切换单项任务状态
toggleState: function (e) {
// 根据参数获得的索引号,确定具体项目
var item = this.data.todos[e.currentTarget.dataset.index]
// 切换状态
item.completed = !item.completed
// 根据当前任务状态决定剩余数增加还是减少
var leftCount = this.data.leftCount + (item.completed ? -1 : 1)
// 判断是否全部完成
var todos = this.data.todos
var completed = item.completed
// 如果切换后状态为true,则历遍todos,发现false则completed赋值false
if (completed) {
for (var i = 0; i < todos.length; i++) {
if (!todos[i].completed) {
completed = false
break
}
}
}
this.data.allCompleted = completed
// 刷新值
this.setData({ todos: this.data.todos, leftCount: leftCount })
}
清除完成项
清除完成项的思路很简单,历遍数组,将所有 completed 的值为 true 的项删掉。或者新建个空数组,将所有 completed 为 false 的项添加至新数组。因为前一种方法在历遍时还需要考虑到删除时索引号的改变,所以使用后一种方法较为简单明了。
<!-- 页脚 -->
<view class="footer">
<text bindtap="toggleAll">切换全部</text>
<text hidden="{{leftCount == 0 ? true : false}}">剩余{{leftCount}}件事</text>
<text bindtap="clearCompleted">清除完成项</text>
</view>
即使1种思路,实现方法也有多个,这里写2个。
//清除完成项
clearCompleted: function () {
// 创建新列表,将所有老列表completed为false的项添加到新列表
// 方法1:使用forEach历遍
// var todos=[]
// this.data.todos.forEach(function(item){
// if(!item.completed){
// todos.push(item)
// }
// })
// 方法2:使用filter过滤,根据条件(函数)过滤,返回值为true则添加到新数组
var todos = this.data.todos.filter(function (item) {
return !item.completed
})
// 刷新值
this.setData({ todos: todos })
}
再就是考虑到,没有完成项的时候,就不显示清除完成项了。思路是比对todos.length 和 leftCount 。
<!-- 页脚 -->
<view class="footer">
<text bindtap="toggleAll">切换全部</text>
<text hidden="{{leftCount == 0 ? true : false}}">剩余{{leftCount}}件事</text>
<text bindtap="clearCompleted" hidden="{{todos.length == leftCount ? true : false}}" >清除完成项</text>
</view>
当没有待办项时隐藏列表和页脚
主要思路就是判断 todos.length ,当值为 0 时不显示,或显示其他提示。
<!-- 判断是否需要显示列表和页脚 -->
<block wx:if="{{todos.length}}">
<!-- 需要做的事 -->
<view class="todos">
</view>
<!-- 页脚 -->
<view class="footer">
</view>
</block>
<!-- 不显示列表和页脚 -->
<block wx:else>
<view class="empty">
<text class="title">恭喜你</text>
<text class="content">没有剩余的工作了</text>
</view>
</block>
/* 列表空 */
.empty {
display: flex;
/* 设置为flex盒子 */
flex-direction: column;
/* 主轴设置为纵轴 */
align-items: center;
/* 子元素侧轴对齐方式 */
justify-content: center;
/* 子元素主轴对齐方式 */
}
/* 列表空时显示的标题 */
.empty .title {
font-size: 120rpx;
margin: 200rpx 50rpx 50rpx;
color: #444;
}
/* 列表空时显示的内容 */
.empty .content {
font-size: 40rpx;
color: #666;
text-align: center;
}
数据保存
到这里结构、界面就都完成了,作为一个应用程序,还有一道工具就是数据处理,即后台工作。对于 todos 这个程序来说,大部分的后台工作其实就是界面交互的控制,剩余的后台工作就只剩下数据保存了。
我们的小程序测试时发现目前无法保存数据,每次重新加载时数据是重置的,所以就需要进行数据保存。
在小程序中,我们可以使用API函数 wx.setStorageSync
以键值对的形式保存数据到缓存中,使用 wx.getStorageSync
函数来进行读取。在 onLoad 事件中进行读取,在数据有变动时进行保存,即达到了存储数据的目的。
// 保存数据
save: function () {
wx.setStorageSync('todo_list', this.data.todos)
},
// 读取数据
load: function () {
var todos = wx.getStorageSync('todo_list')
// 判断是否获得数据,并根据获得数据重新计算剩余数量
if (todos) {
var leftCount = todos.filter(function (item) {
return !item.completed
}).length
if (leftCount == 0) this.data.allCompleted = true
// 刷新值
this.setData({ todos: todos, leftCount: leftCount })
}
},
// 页面加载事件
onLoad: function () {
this.load()
}
最后就是每个设计数据变动的函数最后都增加 this.save()
日志
记录日志信息
在希望达成目标的界面中,还有个日志页面。这就需要在每次数据有改动时记录日志,日志内容初步设计为记录操作行为(增加、删除、改变状态、全部改变状态、删除已完成)、操作时间及操作项目。同时需要保存日志到缓存中。(这里只写相关代码)
//读取数据时
var logs = wx.getStorageSync('todo_logs') // 读取日志
if (logs) this.setData({ logs: logs }) // 如果读取到日志,刷新值
// 保存数据
save: function () {
wx.setStorageSync('todo_list', this.data.todos)
wx.setStorageSync('todo_logs', this.data.logs)
},
//清除完成项
var logs = this.data.logs // 写日志
logs.push({
timestamp: new Date(), action: '清除', item: '所有完成项'
})
// 切换全部任务状态
var logs = this.data.logs // 写日志
logs.push({
timestamp: new Date(), action: this.data.allCompleted ? '完成' : '重启', item: '所有列表项'
})
// 删除单项任务
var logs = this.data.logs // 写日志
logs.push({
timestamp: new Date(), action: '删除', item: item.name
})
// 切换单项任务状态
var logs = this.data.logs // 写日志
logs.push({
timestamp: new Date(), action: item.completed ? '完成' : '重启', item: item.name
})
// 新增列表项
var logs = this.data.logs // 写日志
logs.push({
timestamp: new Date(), action: '新增', item: this.data.add
})
日志页面
日志页面比较简单,就是一个列表框,显示所有列表项。日志列表只有3个元素:内容、行为、时间。
<!--pages/logs/logs.wxml-->
<!-- 页面容器 -->
<view class="container" >
<!-- 日志列表 -->
<view class="logs" wx:if="{{logs}}">
<!-- 列表项目 -->
<view class="item" wx:for="{{logs}}" wx:key="{{index}}">
<text class="content">{{item.item}}</text>
<text class="action">{{item.action}}</text>
<text class="timestamp">{{item.timestamp}}</text>
</view>
</view>
</view>
/* pages/logs/logs.wxss */
/* 日志列表 */
.logs{
border: 1rpx solid #e0e0e0;
border-radius: 10rpx;
box-shadow: 0 0 10rpx #e0e0e0;
}
.logs .item{
border-bottom: 1rpx solid #e0e0e0;
padding: 30rpx;
font-size: 30rpx;
}
.logs .item .content{
width: 70%;
color: #aa0;
display: inline-block;
}
.logs .item .action{
display: inline-block;
text-align: right;
color: #f40;
width: 30%;
}
.logs .item .timestamp{
display: block;
color: #888;
margin-top: 20rpx;
}
日志的数据绑定
日志页面只是呈现日志列表,不需要复杂的交互。只是需注意的是,加载缓存需要在页面呈现事件中,而不能在页面加载事件中。
// pages/logs/logs.js
Page({
/**
* 页面的初始数据
*/
data: {
logs: []
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
this.load()
},
load: function () {
// 读取日志
var logs = wx.getStorageSync('todo_logs')
// 如果读取到日志,刷新值(因为数组是按时间最早的在最下方,所以呈现时候需逆序)
if (logs) this.setData({ logs: logs.reverse() })
}
})
整个项目代码
/**app.wxss**/
/* 页面容器 */
.container {
border-top: 1rpx solid #e0e0e0;
/* 最上方的横线,以上边框的形式呈现 */
padding: 20rpx;
}
app.json
{
"pages": [
"pages/index/index",
"pages/logs/logs"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "TODOS",
"navigationBarTextStyle": "black"
},
"style": "v2",
"sitemapLocation": "sitemap.json",
"tabBar": {
"list": [{
"pagePath": "pages/index/index",
"text": "todos",
"iconPath": "images/todos.png",
"selectedIconPath": "images/todos-active.png"
},{
"pagePath": "pages/logs/logs",
"text": "日志",
"selectedIconPath": "images/logs-active.png",
"iconPath": "images/logs.png"
}]
}
}
// app.js
App({})
// index.js
Page({
data: {
// 文本框数据
add: '',
// 任务列表
todos: [],
// 剩余数量
leftCount: 0,
// 是否全部完成
allCompleted: false,
// 日志
logs: []
},
// 新增列表项
addTodo: function () {
// 如果文本框为空
if (!this.data.add) return
// 增加到数组最后
var todos = this.data.todos
todos.push({
name: this.data.add,
completed: false
})
// 写日志
var logs = this.data.logs
logs.push({
timestamp: new Date(), action: '新增', item: this.data.add
})
// 刷新值
this.setData({
todos: todos,
add: '',
leftCount: this.data.leftCount + 1,
logs: logs
})
this.save()
},
// 文本有改变,改变相应数据成员
textChange: function (e) {
this.setData({ add: e.detail.value })
},
// 切换单项任务状态
toggleState: function (e) {
// 根据参数获得的索引号,确定具体项目
var item = this.data.todos[e.currentTarget.dataset.index]
// 切换状态
item.completed = !item.completed
// 根据当前任务状态决定剩余数增加还是减少
var leftCount = this.data.leftCount + (item.completed ? -1 : 1)
// 判断是否全部完成
if (leftCount == 0) this.data.allCompleted = true
// 写日志
var logs = this.data.logs
logs.push({
timestamp: new Date(), action: item.completed ? '完成' : '重启', item: item.name
})
// 刷新值
this.setData({ todos: this.data.todos, leftCount: leftCount, logs: logs })
this.save()
},
// 删除单项任务
deleteItem: function (e) {
var todos = this.data.todos
// 根据索引号删除数组中的元素,返回值为删除的元素数组
var item = todos.splice(e.currentTarget.dataset.index, 1)[0]
// 根据删除元素的完成状态,决定leftCount是否改变
var leftCount = this.data.leftCount + (item.completed ? 0 : -1)
// 判断是否全部完成
if (leftCount == 0) this.data.allCompleted = true
// 写日志
var logs = this.data.logs
logs.push({
timestamp: new Date(), action: '删除', item: item.name
})
// 刷新值
this.setData({ todos: todos, leftCount: leftCount, logs: logs })
this.save()
},
// 切换全部任务状态
toggleAll: function () {
// 切换全部任务的状态为
this.data.allCompleted = !this.data.allCompleted
var todos = this.data.todos
// this在函数内部使用出错,不是指page对象
var that = this
// 历遍todos更改completed状态
todos.forEach(function (item) {
item.completed = that.data.allCompleted
})
// 更改剩余数量
var leftCount = this.data.allCompleted ? 0 : todos.length
// 写日志
var logs = this.data.logs
logs.push({
timestamp: new Date(), action: this.data.allCompleted ? '完成' : '重启', item: '所有列表项'
})
// 刷新值
this.setData({ todos: todos, leftCount: leftCount, logs: logs })
this.save()
},
//清除完成项
clearCompleted: function () {
// 创建新列表,历遍老列表,将completed为false项添加到新列表
// 使用forEach历遍
// var todos=[]
// this.data.todos.forEach(function(item){
// if(!item.completed){
// todos.push(item)
// }
// })
// 使用filter过滤,根据条件(函数)过滤,返回值为true则添加到新数组
var todos = this.data.todos.filter(function (item) {
return !item.completed
})
// 写日志
var logs = this.data.logs
logs.push({
timestamp: new Date(), action: '清除', item: '所有完成项'
})
// 刷新值
this.setData({ todos: todos, logs: logs })
this.save()
},
// 保存数据
save: function () {
wx.setStorageSync('todo_list', this.data.todos)
wx.setStorageSync('todo_logs', this.data.logs)
},
// 读取数据
load: function () {
var todos = wx.getStorageSync('todo_list')
// 判断是否获得列表数据,并根据获得数据重新计算剩余数量
if (todos) {
var leftCount = todos.filter(function (item) {
return !item.completed
}).length
if (leftCount == 0) this.data.allCompleted = true
// 刷新值
this.setData({ todos: todos, leftCount: leftCount })
}
// 读取日志
var logs = wx.getStorageSync('todo_logs')
// 如果读取到日志,刷新值
if (logs) this.setData({ logs: logs })
},
// 页面加载事件
onLoad: function () {
this.load()
}
})
<!--index.wxml-->
<view class="container">
<!-- 增加项目 -->
<view class="add">
<image src="../../images/plus.png" bindtap="addTodo"></image>
<input type="text" placeholder="准备做的事" value="{{add}}" bindinput="textChange" bindconfirm="addTodo" />
</view>
<!-- 判断是否需要显示列表和页脚 -->
<block wx:if="{{todos.length}}">
<!-- 需要做的事 -->
<view class="todos">
<view class="item{{item.completed ? ' completed' : ''}}" wx:for="{{todos}}" wx:key="name" bindtap="toggleState" data-index="{{index}}">
<icon type="{{item.completed ? 'success' : 'circle'}}"></icon>
<text>{{item.name}}</text>
<icon type="clear" size="32rpx" catchtap="deleteItem" data-index="{{index}}"></icon>
</view>
</view>
<!-- 页脚 -->
<view class="footer">
<text bindtap="toggleAll">切换全部</text>
<text hidden="{{leftCount == 0 ? true : false}}">剩余{{leftCount}}件事</text>
<text bindtap="clearCompleted" hidden="{{todos.length == leftCount ? true : false}}">清除完成项</text>
</view>
</block>
<!-- 不显示列表和页脚 -->
<block wx:else>
<view class="empty">
<text class="title">恭喜你</text>
<text class="content">没有剩余的工作了</text>
</view>
</block>
</view>
/**index.wxss**/
/* 增加项目栏 */
.add {
border: 1rpx solid #e0e0e0;
/* 边框 */
border-radius: 10rpx;
/* 边框圆角 */
box-shadow: 0 0 10rpx #e0e0e0;
/* 阴影 */
align-items: center;
/* 侧轴对齐 */
display: flex;
/* 设置为flex盒子 */
padding: 20rpx;
/* 内边距 */
}
/* 增加项目栏下的图片 */
.add image {
width: 40rpx;
/* 宽 */
height: 40rpx;
/* 高 */
margin-right: 20rpx;
/* 右侧外边距 */
}
/* 需要做到事项区域 */
.todos {
border: 1rpx solid #e0e0e0;
/* 边框 */
border-radius: 10rpx;
/* 边框圆角 */
box-shadow: 0 0 10rpx #e0e0e0;
/* 阴影 */
align-items: center;
/* 子元素侧轴对齐 */
}
/* 任务项 */
.todos .item {
padding: 20rpx;
/* 内边距 */
border-bottom: 1rpx solid #e0e0e0;
/* 底部边框 */
display: flex;
/* 设置为flex盒子 */
align-items: center;
/* 子元素侧轴对齐 */
}
/* 最后一个任务项,不需要底部边框 */
.todos .item:last-child {
padding: 20rpx;
/* 内边距 */
display: flex;
/* 设置为flex盒子 */
align-items: center;
/* 子元素侧轴对齐 */
}
/* 任务项里的文本 */
.todos .item text {
flex: 1;
/* 获取全部剩余空间 */
font-size: 30rpx;
/* 字体大小 */
color: #444;
/* 字体颜色 */
margin-left: 20rpx;
/* 左侧外边距 */
}
/* 任务项里已完成任务的文本 */
.todos .item.completed text {
flex: 1;
/* 获取全部剩余空间 */
font-size: 30rpx;
/* 字体大小 */
color: #888;
/* 字体颜色 */
margin-left: 20rpx;
/* 左侧外边距 */
text-decoration: line-through;
/* 中划线 */
}
/* 页脚 */
.footer {
font-size: 30rpx;
/* 字体大小 */
margin: 10rpx;
/* 外边距 */
display: flex;
/* 设置为flex盒子 */
justify-content: space-between;
/* 子元素主轴对齐 */
color: #333;
/* 字体颜色 */
}
/* 列表空 */
.empty {
display: flex;
/* 设置为flex盒子 */
flex-direction: column;
/* 主轴设置为纵轴 */
align-items: center;
/* 子元素侧轴对齐方式 */
justify-content: center;
/* 子元素主轴对齐方式 */
}
/* 列表空时显示的标题 */
.empty .title {
font-size: 120rpx;
margin: 200rpx 50rpx 50rpx;
color: #444;
}
/* 列表空时显示的内容 */
.empty .content {
font-size: 40rpx;
color: #666;
text-align: center;
}
// pages/logs/logs.js
Page({
/**
* 页面的初始数据
*/
data: {
logs: []
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
this.load()
},
load: function () {
// 读取日志
var logs = wx.getStorageSync('todo_logs')
// 如果读取到日志,刷新值(因为数组是按时间最早的在最下方,所以呈现时候需逆序)
if (logs) this.setData({ logs: logs.reverse() })
}
})
<!--pages/logs/logs.wxml-->
<!-- 页面容器 -->
<view class="container" >
<!-- 日志列表 -->
<view class="logs" wx:if="{{logs}}">
<!-- 列表项目 -->
<view class="item" wx:for="{{logs}}" wx:key="{{index}}">
<text class="content">{{item.item}}</text>
<text class="action">{{item.action}}</text>
<text class="timestamp">{{item.timestamp}}</text>
</view>
</view>
</view>
/* pages/logs/logs.wxss */
/* 日志列表 */
.logs{
border: 1rpx solid #e0e0e0;
border-radius: 10rpx;
box-shadow: 0 0 10rpx #e0e0e0;
}
.logs .item{
border-bottom: 1rpx solid #e0e0e0;
padding: 30rpx;
font-size: 30rpx;
}
.logs .item .content{
width: 70%;
color: #aa0;
display: inline-block;
}
.logs .item .action{
display: inline-block;
text-align: right;
color: #f40;
width: 30%;
}
.logs .item .timestamp{
display: block;
color: #888;
margin-top: 20rpx;
}
重要参考: