自学微信小程序开发第六天- 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;
}

重要参考:

B站上找到的开发教程视频,Up主不知道叫啥……

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值