
01、逻辑实现
1 录入新事项
首先在index.js文件顶部声明与data属性中同名的数组todoList和completeList方便后续使用。index.js代码更新如下:
1. // index.js
2. // 共享数据
3. let todoList = []
4. let completeList = []
5.
6. Page({
7. ...8. })
在index.js中封装一个函数updateAllData()用于批量更新所有数据,后面会用到。
index.js代码更新如下:
1. // index.js
2. ...
3. Page({
4. ...,
5. /**
6. * 自定义函数--更新页面和本地数据
7. */
8. updateAllData: function () {
9. // 更新页面显示
10. this.setData({
11. todoList: todoList,
12. completeList: completeList,
13. isChecked:true
14. })
15. // 更新本地缓存
16. wx.setStorageSync('todoList', todoList)
17. wx.setStorageSync('completeList', completeList)
18. },
19. ...20. })
该函数在每次被调用时可以更新页面显示和本地缓存的数据。
修改index.wxml代码,为浮动按钮添加自定义名称的点击事件addData。
index.wxml代码修改如下:
1. <!-- 1 代办区域(...内容略...)-->
2. <!-- 2 已完成区域(...内容略...)-->
3.
4. <!-- 3 浮动按钮 -->
5. <view class="addBtn" bindtap="addData">+</view>
index.js代码新增addData()函数如下:
1. // index.js
2. Page({
3. ...,
4. /**
5. * 自定义函数--添加数据
6. */
7. addData: function () {
8. // 弹出对话框
9. wx.showModal({
10. title: '请输入您的代办内容',
11. editable: true,
12. success: res => {
13. // 是否点击了确认按钮
14. if (res.confirm) {
15. // 获取文本内容
16. let content = res.content
17. // 获取代办数据并追加最新一条代办
18. todoList = [content].concat(todoList)
19. // 更新所有数据
20. this.updateAllData()
21. }
22. }
23. })
24. },
25. ...26. })
此时就可以通过点击按钮弹出对话框来更新代办区域的数据了,为了不让之前data属性中的测试数据对实际添加的事项造成影响,可以将它们清空。
index.js的data属性修改如下:
1. Page({
2. /**
3. * 页面的初始数据
4. */
5. data: {
6. todoList: [ ],
7. completeList: [ ],
8. isChecked: true
9. },
10. ...11. })

■ 图8-18 录入新事项功能实现
注:最新录入的事项会出现在最上方,因此图中这首诗实际上是倒着录入的。
2 已完成事项
点击代办区域的多选框或文字描述希望可以将此条记录挪到已完成事项中。
修改index.wxml代码,为代办区域中的单行事项区域添加自定义名称的点击事件done,并携带参数idx取值为数组下标。index.wxml代码更新如下:
1. <!-- 1 代办区域 -->
2. <view id="todoBox" class="flexV" wx:if="{{todoList.length>0}}">
3. <!-- 1-1 单行事项区域 -->
4. <view class="bar flexH alignCenter" wx:for="{{todoList}}" wx:key="index" bindtap="done" data-idx="{{index}}">
5. <!-- 1-1-1 多选框组件(...内容略...) -->
6. <!-- 1-1-2 事项文字(...内容略...) -->
7. </view>
8. </view>
9.
10. <!-- 2 已完成区域(...内容略...)-->
11. <!-- 3 浮动按钮(...内容略...) -->
index.js相关代码如下:
1. // index.js
2. ...
3. Page({
4. ...,
5. /**
6. * 自定义函数--完成代办
7. */
8. done: function (e) {
9. console.log(e.currentTarget.dataset.idx)
10. // 获取被点击的代办编号(从0开始)
11. let i = e.currentTarget.dataset.idx
12. // 取出指定数据
13. let content = todoList[i]
14. // 从代办数据中删除
15. todoList.splice(i, 1)
16. // 追加到完成数据中
17. completeList = [content].concat(completeList)
18. // 更新所有数据
19. this.updateAllData()
20. },
21. ...22. }
运行效果如图8-19所示。

■ 图8-19 代办事项转已完成功能实现
3 撤销已完成
如果不小心点错了导致代办任务转成已完成状态了,希望可以在已完成清单中再次点击使其返回代办列表。
修改index.wxml代码,为已完成区域中单行事项区域添加自定义名称的点击事件undo,并携带参数idx取值为数组下标。index.wxml代码更新如下:
1. <!-- 1 代办区域(...内容略...) -->
2.
3. <!-- 2 已完成区域 -->
4. <view id="completeBox" class="box flexV" wx:if="{{completeList.length>0}}">
5. <!-- 2-1 标题区域(...内容略...) -->
6.
7. <!-- 2-2 单行事项区域 -->
8. <view class="bar flexH alignCenter" wx:for="{{completeList}}" wx:key="index" bindtap="undo" data-idx="{{index}}">
9. <!-- 2-2-1 多选框组件(...内容略...) -->
10. <!-- 2-2-2 事项文字(...内容略...) -->
11. </view>
12. </view>
13.
14. <!-- 3 浮动按钮(...内容略...) -->
index.js相关代码如下:
23. // index.js
24. ...
25. Page({
26. ...,
27. /**
28. * 自定义函数--撤销代办
29. */
30. undo: function (e) {
31. // 获取被点击的代办编号(从0开始)
32. let i = e.currentTarget.dataset.idx
33. // 取出指定数据
34. let content = completeList[i]
35. // 从已完成数据中删除
36. completeList.splice(i, 1)
37. // 追加到代办数据中
38. todoList = [content].concat(todoList)
39. // 更新所有数据
40. this.updateAllData()
41. },
42. ...43. }
运行效果如图8-20所示。

■ 图8-20 已完成事项转回代办功能实现
4 长按删除事项
如果有不需要记录的事项也可以长按进行删除,代办区域和已完成区域的清单都应该需要此功能。
修改index.wxml代码,为代办区域和已完成区域中单行事项区域都添加自定义名称的长按点击事件delData,并携带参数type取值为区域名称todo或complete。index.wxml代码更新如下:
1. <!-- 1 代办区域 -->
2. <view id="todoBox" class="flexV" wx:if="{{todoList.length>0}}">
3. <!-- 1-1 单行事项区域 -->
4. <view class="bar flexH alignCenter" wx:for="{{todoList}}" wx:key="index" bindtap="done" data-idx="{{index}}" bindlongpress="delData" data-type="todo">
5. <!-- 1-1-1 多选框组件(...内容略...) -->
6. <!-- 1-1-2 事项文字(...内容略...) -->
7. </view>
8. </view>
9.
10. <!-- 2 已完成区域 -->
11. <view id="completeBox" class="box flexV" wx:if="{{completeList.length>0}}">
12. <!-- 2-1 标题区域(...内容略...) -->
13.
14. <!-- 2-2 单行事项区域 -->
15. <view class="bar flexH alignCenter" wx:for="{{completeList}}" wx:key="index" bindtap="undo" data-idx="{{index}}" bindlongpress="delData" data-type="complete">
16. <!-- 2-2-1 多选框组件(...内容略...) -->
17. <!-- 2-2-2 事项文字(...内容略...) -->
18. </view>
19. </view>
20.
21. <!-- 3 浮动按钮(...内容略...) -->
index.js中新增delData函数内容如下:
1. // index.js
2. Page({
3. ...,
4. /**
5. * 自定义函数--长按删除数据
6. */
7. delData: function (e) {
8. // 弹出对话框
9. wx.showModal({
10. title: '您确定要删除吗?',
11. success: res => {
12. // 是否点击了确认按钮
13. if (res.confirm) {
14. // 获取数据类型
15. let type = e.currentTarget.dataset.type
16. // 获取数据编号(从0开始)
17. let i = e.currentTarget.dataset.idx
18. // 处理代办数据
19. if (type == 'todo') {
20. todoList.splice(i, 1)
21. }
22. // 处理完成数据
23. else {
24. completeList.splice(i, 1)
25. }
26. // 更新所有数据
27. this.updateAllData()
28. }
29. }
30. })
31. },
32. ...33. }
此时长按就会弹出删除确认对话框,确认后即可删除不需要的事项。
这里以代办事项为例,长按删除效果如图8-21所示。

■ 图8-21 长按删除代办事项功能实现
5 读取数据缓存
当已经录入了一些数据后,希望下次打开小程序时还可以读到。
这就需要在触发index.js中的生命周期函数onLoad(options)时可以提前读取一下数据缓存中的数组信息。
index.js中新增getStorageData()函数并在onLoad(options)中调用,代码如下:
1. // index.js
2. Page({
3. ...,
4. /**
5. * 自定义函数--获取本地缓存数据
6. */
7. getStorageData: function () {
8. // 获取代办数据
9. todoList = wx.getStorageSync('todoList') || []
10. // 获取已完成数据
11. completeList = wx.getStorageSync('completeList') || []
12. // 更新页面显示
13. this.setData({
14. todoList: todoList,
15. completeList: completeList,
16. isChecked:true
17. })
18. },
19. ...,
20. /**
21. * 生命周期函数--监听页面加载
22. */
23. onLoad: function (options) {
24. // 从数据缓存中读取数据
25. this.getStorageData()
26. },
27. ...28. }
最后记得在index.js自带的生命周期函数onUnload()里面清空共享数据todoList和completeList以防下次打开会有干扰。
index.js相关代码修改如下:
1. // index.js
2. Page({
3. ...,
4. /**
5. * 生命周期函数--监听页面卸载
6. */
7. onUnload: function () {
8. // 清空共享数据
9. todoList = []
10. completeList = []
11. }12. }
至此整个阶段案例就完成了,如果录入了太多的缓存数据不想要了也可以直接使用微信开发者工具顶部"清缓存"按钮清除全部数据。
完整运行效果如图8-22所示。

■图8-22 第8章阶段案例最终效果图
本案例综合应用了小程序数据缓存API中的数据存储和读取功能,开发者还需要了解bindtap和bindlongpress的区别和应用,本案例的难点是如何为多选框设置彩色背景样式。