【微信小程序项目实战】TodoList-项目主体搭设(2)
目录
- JS 部分
- 数据 data
- onShow
- 输入框双向绑定
- 保存与读取
- 添加新的待办事项
- 完成待办事项
- 删除待办事项
- WXML
- 顶部输入框
- 主体
- 回到顶部按钮
- 完整代码
- JS
- WXML
- WXSS
JS 部分
为便于分析各个组件的相互作用与原理,故先从 JS 入手,而后再完善 HTML 部分
以下所有代码(除了 import 导包语句)都写在 todo.js 的 Page({}) 对象内
数据 data
作为一款小而精的 todolist 小程序,我们仅需下方五个属性即可解决所有代码逻辑
inputValue双向绑定输入框内容todoList当前的 todo 表currentUndo当前未完成的待办事项数allComplete事项是否全部完成isFocus用于搜索框自动获取焦点
data: {inputValue: '',todoList: [],currentUndo: 0,allComplete: false,isFocus:false},
onShow
由于我们没有在 app.json 中设置总标题,故在每个页面打开后,最好都在 onShow 钩子里面显式指定当前页面标题
// 进入此页面时,自动设置好标题onShow: function () {wx.setNavigationBarTitle({title: 'Todo待办事项'})},
输入框双向绑定
由于微信小程序 功能太少太烂 ,特别是仅仅提供了单向绑定,而不像 vue 还有 v-model 执行双向绑定;
所有这里需要开发者自行实现双向绑定功能,具体流程即:监听输入框内容,内容发生改变触发回调函数修改对应的值,而通过单向绑定,实现值的动态变化
// 输入框双向绑定处理inputChange(e) {this.setData({// e.detail.value可视为一种固定写法,可以从input类型的组件获取其值inputValue: e.detail.value})},
保存与读取
代码不难理解,即我们需要在每次对 todo 进行增删后,都自带保存数据到本地,这样子下次开启小程序就可直接读取数据了
onLoad 回调保证页面加载时自动从内存读取 todolist 数据
// 保存todos到本地内存saveTodos() {wx.setStorageSync('todo_list', this.data.todoList)},// 加载todos列表loadTodos() {let todos = wx.getStorageSync('todo_list')if (todos) {let undo = todos.filter((item) => {return !item.complete}).lengththis.setData({todoList: todos,currentUndo: undo})}console.log(todos);},// 页面加载钩子调用loadTodosonLoad() {this.loadTodos()},
添加新的待办事项
添加新待办事项的代码逻辑
// 将新的待办事项添加到栏内addTodo() {// 如果编辑框文本为空或者仅有空格的话,不予添加if (!this.data.inputValue || !this.data.inputValue.trim()) return// 每次都需要单独取出todolist然后push新的内容进去,最后setdata,你可以将其当做固定套路let todos = this.data.todoListtodos.push({title: this.data.inputValue,complete: false})this.setData({inputValue: "",todoList: todos,currentUndo: this.data.currentUndo + 1, // 添加新待办,未完成数+1isFocus:true // 添加完一个待办后,自动令编辑框获得焦点,省去客户重复点击过程})// 每次修改完毕都必须保存一下!this.saveTodos()},
完成待办事项
请注意此处 complete 状态的设置:点击一次完成事项,再点击一次取消完成变为待办,此时就需要对 complete 做出判断,以动态增减 currentUndo 的数值量
// 点击完成单个事项toggleTodo(e) {let index = e.currentTarget.dataset.indexlet todos = this.data.todoListtodos[index].complete = !todos[index].completethis.setData({todoList: todos,currentUndo: this.data.currentUndo + (todos[index].complete ? -1 : 1),})this.saveTodos()},// 选中全部的待办事项toggleAllTodos() {this.data.allComplete = !this.data.allCompletelet todos = this.data.todoListtodos.forEach(i => {i.complete = this.data.allComplete})this.setData({todoList: todos,currentUndo: this.data.allComplete ? 0 : todos.length})this.saveTodos()},
删除待办事项
删除单个待办事项的方法为通过索引找到该 todo,并使用 splice 删去,之后更新数据即可
删除多个待办事项时需要配合 foreach 方法
// 删除单个待办事项removeTodo(e) {let index = e.currentTarget.dataset.indexlet todos = this.data.todoListlet remove = todos.splice(index, 1)[0]this.setData({todoList: todos,currentUndo: this.data.currentUndo - (remove.complete ? 0 : 1)})this.saveTodos()},// 删除选中项removeTodos(e) {let todos = this.data.todoListlet remain = []todos.forEach(i => {if (!i.complete) remain.push(i)})this.setData({todoList: remain})this.saveTodos()// wx自带的手机振动接口,vibrateShort表示振动15mswx.vibrateShort()},
WXML
顶部输入框
这里涉及到的 t-input 配置项可以自行前往官网查询 API,这里不做过多解释
bind:change、bind:blur、bind:enter 分别表示 内容改变、是否获取焦点、按下回车键 后的回调函数
对于 t-button,如果他在 t-input 内部,则需使用 slot="suffix" 指定插槽来安放该 button
<!-- 顶部添加栏 -->
<view class="todo-input"><t-inputvalue="{{inputValue}}"style="border-radius: 12rpx;"clearableplaceholder="请输入事项名称"confirm-type="done"bind:change="inputChange"bind:blur="isFocus=!isFocus"bind:enter="addTodo"focus="{{isFocus}}"><t-buttonwx:if="{{inputValue}}"slot="suffix"theme="light"size="small"bindtap="addTodo">添加</t-button></t-input>
</view>
主体
block 配以 wx:if,实现状态页显示:当待办事项列表为空时,动态判断并显示 404 页面
<!-- 当todo列表存在数据时,渲染该页面 -->
<block wx:if="{{todoList.length}}"><view class="todo-control"><image bindtap="toggleAllTodos" src="../../image/pages/all.png"></image><text wx:if="{{currentUndo}}">待完成任务 {{currentUndo}}</text><imagebindtap="removeTodos"src="../../image/pages/delete.png"wx:if="{{todoList.length>currentUndo}}"></image></view><view class="todo-itembox"><viewclass="todo-items {{item.complete?'comp':''}}"wx:for="{{todoList}}"wx:key="index"data-index="{{index}}"bindtap="toggleTodo"><iconclass="checkbox"type="{{ item.complete ? 'success' : 'circle' }}"></icon><text class="title">{{ item.title }}</text><iconclass="remove"type="clear"size="16"catchtap="removeTodo"data-index=" {{index}} "/></view></view>
</block><!-- 当todo列表为空时,渲染该页面 -->
<block wx:else><view class="todo-empty"><image src="../../image/state/no-data.png"></image><view style="color: gray;">当前还没有待办事项哦~</view></view>
</block>
回到顶部按钮
<!-- 回到顶部悬浮按钮 -->
<!-- 判断当且仅当列表项多于6个时,才会显示该悬浮按钮 -->
<t-fab wx:if="{{todoList.length>6}}" icon="arrow-up" bind:click="fabBack2Top" />
CSS 部分由于不方便表述,故留到文末以源码的形式展现给大家
完整代码
JS
import Message from "tdesign-miniprogram/message/index";// pages/todo/todo.js
Page({data: {inputValue: "",todoList: [],currentUndo: 0,allComplete: false,isFocus: false,},// 进入此页面时,自动设置好标题onShow: function () {wx.setNavigationBarTitle({title: "Todo待办事项",});},// 输入框双向绑定处理inputChange(e) {this.setData({inputValue: e.detail.value,});},// 保存todos到本地内存saveTodos() {wx.setStorageSync("todo_list", this.data.todoList);},// 加载todos列表loadTodos() {let todos = wx.getStorageSync("todo_list");if (todos) {let undo = todos.filter((item) => {return !item.complete;}).length;this.setData({todoList: todos,currentUndo: undo,});}console.log(todos);},// 页面加载钩子调用loadTodosonLoad() {this.loadTodos();},// 将新的待办事项添加到栏内addTodo() {if (!this.data.inputValue || !this.data.inputValue.trim()) return;let todos = this.data.todoList;todos.push({title: this.data.inputValue,complete: false,});this.setData({inputValue: "",todoList: todos,currentUndo: this.data.currentUndo + 1,isFocus: true,});this.saveTodos();},// 悬浮按钮回到顶部fabBack2Top() {wx.pageScrollTo({duration: 500,scrollTop: 0,});},// 点击完成单个事项toggleTodo(e) {let index = e.currentTarget.dataset.index;let todos = this.data.todoList;todos[index].complete = !todos[index].complete;this.setData({todoList: todos,currentUndo: this.data.currentUndo + (todos[index].complete ? -1 : 1),});this.saveTodos();},// 选中全部的待办事项toggleAllTodos() {this.data.allComplete = !this.data.allComplete;let todos = this.data.todoList;todos.forEach((i) => {i.complete = this.data.allComplete;});this.setData({todoList: todos,currentUndo: this.data.allComplete ? 0 : todos.length,});this.saveTodos();},// 删除单个待办事项removeTodo(e) {let index = e.currentTarget.dataset.index;let todos = this.data.todoList;let remove = todos.splice(index, 1)[0];this.setData({todoList: todos,currentUndo: this.data.currentUndo - (remove.complete ? 0 : 1),});this.saveTodos();},// 删除选中项removeTodos(e) {let todos = this.data.todoList;let remain = [];todos.forEach((i) => {if (!i.complete) remain.push(i);});this.setData({todoList: remain,});if (this.data.currentUndo === 0) {Message.success({context: this,content: "完成所有任务,休息一下吧!",duration: 2000,offset: [20, 32],closeBtn: true,});}this.saveTodos();wx.vibrateShort();},
});
WXML
<view class="todo-container"><!-- 顶部添加栏 --><view class="todo-input"><t-inputvalue="{{inputValue}}"style="border-radius: 12rpx;"clearableplaceholder="请输入事项名称"confirm-type="done"bind:change="inputChange"bind:blur="isFocus=!isFocus"bind:enter="addTodo"focus="{{isFocus}}"><t-buttonwx:if="{{inputValue}}"slot="suffix"theme="light"size="small"bindtap="addTodo">添加</t-button></t-input></view><!-- 当todo列表存在数据时,渲染该页面 --><block wx:if="{{todoList.length}}"><view class="todo-control"><image bindtap="toggleAllTodos" src="../../image/pages/all.png"></image><text wx:if="{{currentUndo}}">待完成任务 {{currentUndo}}</text><imagebindtap="removeTodos"src="../../image/pages/delete.png"wx:if="{{todoList.length>currentUndo}}"></image></view><view class="todo-itembox"><viewclass="todo-items {{item.complete?'comp':''}}"wx:for="{{todoList}}"wx:key="index"data-index="{{index}}"bindtap="toggleTodo"><iconclass="checkbox"type="{{ item.complete ? 'success' : 'circle' }}"></icon><text class="title">{{ item.title }}</text><iconclass="remove"type="clear"size="16"catchtap="removeTodo"data-index=" {{index}} "/></view></view></block><!-- 当todo列表为空时,渲染该页面 --><block wx:else><view class="todo-empty"><image src="../../image/state/no-data.png"></image><view style="color: gray;">当前还没有待办事项哦~</view></view></block><!-- 回到顶部悬浮按钮 --><!-- 判断当且仅当列表项多于6个时,才会显示该悬浮按钮 --><t-fabwx:if="{{todoList.length>6}}"icon="arrow-up"bind:click="fabBack2Top"/>
</view>
WXSS
.todo-container {margin: 0;padding: 0;width: 100vw;min-height: 100vh;background-color: #ededed;display: flex;flex-direction: column;position: relative;
}.todo-input {margin: 12rpx 32rpx;display: flex;flex-direction: row;justify-content: space-between;
}.todo-empty {display: flex;flex-direction: column;justify-content: center;align-items: center;height: 100%;
}
.todo-empty image {width: 450rpx;height: 450rpx;
}/* 悬浮按钮 */
.fab {position: absolute;right: 0;bottom: 0;
}/* 多选操纵栏 */
.todo-control {display: flex;flex-direction: row;justify-content: space-between;align-items: center;height: 80rpx;margin: 12rpx 32rpx;
}
.todo-control image {height: 70rpx;width: 70rpx;
}/* todo项目展示 */
.todo-items {border-radius: 8rpx;height: 120rpx;background-color: white;margin: 10rpx 32rpx;display: flex;flex-direction: row;justify-content: space-between;align-items: center;
}
.comp {background-color: lightgray;
}
.todo-items .checkbox {margin-left: 24rpx;
}
.todo-items .title {min-width: 70%;max-width: 70%;text-overflow: ellipsis;overflow: hidden;white-space: nowrap;
}
.todo-items .remove {margin-right: 24rpx;
}
相关文章:
【微信小程序项目实战】TodoList-项目主体搭设(2)
目录JS 部分数据 dataonShow输入框双向绑定保存与读取添加新的待办事项完成待办事项删除待办事项WXML顶部输入框主体回到顶部按钮完整代码JSWXMLWXSSJS 部分 为便于分析各个组件的相互作用与原理,故先从 JS 入手,而后再完善 HTML 部分 以下所有代码&…...
23种设计模式-迭代器模式(安卓应用场景介绍)
迭代器模式是一种行为型设计模式,它允许你在不暴露集合对象内部结构的情况下遍历集合中所有元素。在本文中,我们将介绍迭代器模式的概念和原理,提供一个基于Java的示例,并探讨在Android应用程序开发中的实际应用。 迭代器模式的概…...
面试 - 软件工程体系
今天是我人生中的第二次面试,第一次面试到技术问题。 面试公司:无锡信捷电气股份有限公司 面试时间:2023 年 3 月 6 日 15:30 面试地点:西安工程大学(临潼校区)D-188 在技术面中,我表现的不是很…...
05-CSS
今日目标能够说出 为什么要用定位能够说出 定位的 4 种分类能够说出 4 种定位各自的特点能够说出 为什么常用子绝父相布局能够写出 淘宝轮播图布局能够说出 显示隐藏的 3 种方式以及区别1. 定位(position) 介绍1.1 为什么使用定位我们先来看一个效果,同时思考一下用…...
华为OD机试题,用 Java 解【分奖金】问题
华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典使用说明 参加华为od机试,一定要注意不…...
Multisim 14.3 安装教程
1、首先解压Multisim 安装包。 2、解压完成后,双击点进去,找到setup这个文件,点进去。 3、找到setup文件里面的install.exe文件,并鼠标右键 “找到以管理员身份运行”。 4、选择“我接受上述许可协议”,点击下一步。 …...
06-Oracle表空间与用户管理(表空间,用户,备份与恢复,导入导出数据)
本讲主要内容: 1.表空间管理:表空间的作用,创建,修改,删除及管理; 2.用户管理:创建用户,修改用户,删除用户,修改密码,解锁; 3.用户…...
XSS攻击防御
XSS攻击防御XSS Filter过滤方法输入验证数据净化输出编码过滤方法Web安全编码规范XSS Filter XSS Filter的作用是通过正则的方式对用户(客户端)请求的参数做脚本的过滤,从而达到防范XSS攻击的效果。 XSS Filter作为防御跨站攻击的主要手段之…...
敏捷开发还需要PRD吗
一、PRD有什么用 prd提升与RD或者未来接手人的沟通效率 二、为什么会有PRD 首先来说说为什么会有PRD文档。 1、稍微大一点的团队产品经理未必能向每个人传达产品需求,这就需要有一个文档的形式来向项目的所有成员来传达需求,这就是文档的来源。 2、由…...
完整教程:使用Spring Boot实现大文件断点续传及文件校验
一、简介 随着互联网的快速发展,大文件的传输成为了互联网应用的重要组成部分。然而,由于网络不稳定等因素的影响,大文件的传输经常会出现中断的情况,这时需要重新传输,导致传输效率低下。 为了解决这个问题ÿ…...
数位dp-- 数字游戏
题目 思路 也是一道比较典型的数位dp的问题,关键的思想跟我上一篇博客很像, 首先把区间值变成[1,Y]-[1,X-1]的值,然后单独计算得到结果。 总的来说就是把这个数的每一位都单独拿出来,然后根据选0-an-1和选**an**两种方案单独计算&…...
Linux脚本 启动、重启、停止、授权
在jar包所在目录 vim start.sh | reload.sh | stop.sh输入以下命令 然后保存,进行授权 1.启动 nohup java -jar -Dfile.encodingutf-8 IntegrationFrame-sso-1.0.0-SNAPSHOT.jar & echo "started"2.重启 pid$(ps -ef|grep IntegrationFrame-sso-1.…...
Pytorch深度学习实战3-8:详解数据可视化组件TensorBoard安装与使用
目录1 什么是Tensorboard?2 Tensorboard安装3 Tensorboard可视化流程4 Tensorboard可视化实例4.1 常量可视化4.2 特征图可视化1 什么是Tensorboard? 在深度学习领域,网络内部如同黑箱,其中包含大量的连接参数,这给人工…...
华为OD机试 - 旋转骰子(C 语言解题)【独家】
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧文章目录 使用说明本期题目:旋转骰子…...
如何做SpringBoot单元测试?
前言单元测试(unit testing),是指对项目中的最⼩可测试单元进⾏检查和验证的过程就叫单元测试,对于Java来说或者是在SpringBoot项目中,最小的可测试单元就是一个方法。做单元测试就是为了证明某段代码的执⾏结果是否符…...
ZZULI训练: 数组和字符串专题
ZZULI训练:数组和字符串专题ZZULI训练: 数组和字符串专题ZZULI训练:数组和字符串专题 部分多实例没写循环多次是因为在main里面循环了, 你们写的时候要加上只提供大概思路和核心代码建议多尝试一下c, 并没有想象的那么难 7-1 个位数统计 可以开个数组来存一下每个数组出现的…...
ElasticSearch如何解决深分页问题?
文章目录 前言From/Size参数Query阶段Fetch阶段深度分页问题Scroll遍历数据基本使用遍历优缺点缺点:优点:」Scroll Scan基本使用Scroll Scan与Scroll的区别Sliced ScrollSearch After基本使用基本原理优缺点总结ES7版本变...
JDK8新特性宝典
JDK8新特性 Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机,Java 仍是企业和开发人员的首选开发平台 课程内容的介绍 了解Java发展史Lambda表达式…...
【C++】关于C++模板的分离编译问题
文章目录1.阐述模板的实例化和重复定义问题2.分离编译可能出现的问题3.解决方法将函数模板的定义放到头文件中模板定义的位置显式实例化模板总结1.阐述模板的实例化和重复定义问题 C模板是一种非常强大的工具,可以为我们提供通用的代码实现方式。然鹅,在…...
小应用记账本-第2章-数据库设计
小应用记账本-第2章-数据库设计 在上一章《小应用记账本-第1章-需求分析》已经罗列了我们需要的功能,因为很简单,所以这一章就来设计数据库吧。 Account表:账户表 字段名类型说明取值idint账户idaccount_namevarchar账户名称remaining_sumd…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...
GitHub 趋势日报 (2025年06月06日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
