Todo App 示例

Todo App 是支付宝小程序的一个经典案例,本章节我们将结合小程序客户端程序和 Basement 后端服务,完成一个小程序的开发,使其同时具备客户端和后端的能力,可以作为一个完成程序使用。


前提条件


创建小程序

  • 登录 蚂蚁金服开放平台,选择 开发者中心 > 小程序 > 创建
  • 填写 基本信息,点击 创建 按钮,创建应用名为 示例应用 小程序。

说明:一个账号最多可以创建 10 个小程序;未提交过审核的小程序可以删除,删除的小程序不在计数范围。


创建 Basement

  • 我的小程序 页面,选择已经创建的小程序,点击 查看,进入 开发管理 页面。
  • 点击左侧导航栏的 云服务(公测),在 云服务列表 页面点击 创建云服务 > 创建 Basement
  • 创建 Basement 页面,填入 应用名称描述 (选填),点击 创建

说明:每个账号可免费创建一个 Basement 服务;当前测试环境该方案免费提供,但若 连续 7 日小程序没有访问量,并且未部署过代码 ,环境资源会被自动回收。

image.png


准备开发环境

  • 打开小程序开发者工具,点击 新建项目
    image.png

  • 在新建项目向导中,选择 小程序,选择 Todo Basement 示例,点击 下一步
    image.png

  • 输入 项目名称,项目路径会自动填充,选择 Basement 为后端服务,点击 完成
    image.png

  • 在 IDE 的客户端依赖页面,安装 @ant-basement/miniprogram-sdk

说明:如果列表中找不到,可以在依赖框中输入 @ant-basement/miniprogram-sdk 完成添加。

image.png

  • 点击 云服务:请选择 > 选择此前已经创建好的 Basement 服务作为关联的云服务。
    image.png

部署云函数

点击 云服务 唤起菜单,点击 上传部署服务端代码 提交云函数部署。

说明:将 server 目录的代码作为云函数进行上传和部署。部署成功相当于完成云函数的代码发布,部署前请注意区分测试环境和生产环境的区别。

image.png


预览和上传

  • 点击 刷新图标 可以在 IDE 模拟器 中查看预览效果;还可以点击 预览 ,使用 支付宝扫描二维码 在手机上实现真机预览。
    image.png

  • 点击 上传 将小程序上传到开放平台,具体操作请参考 小程序提审、发布流程
    image.png


后端代码介绍

获取用户信息

小程序是运行在支付宝上的,首先需要获取支付宝用户的基础信息,用于完善用户头像、昵称等信息。我们需要创建一个云应用,通过支付宝 OpenAPI,当用户访问小程序的时候将对应信息获取到并显示在页面上。

  • 在 server 目录增加 getUserInfo/index.js 文件,用于编写和存放云函数的代码。
  1. 'use strict';
  2. module.exports = async (ctx) => {
  3. // 从支付宝 OpenAPI 获取用户信息
  4. const user = await ctx.basement.openapi.alipay.exec('alipay.user.info.share');
  5. // 返回用户信息
  6. return user;
  7. };
  • 在 server 目录修改 serverless.yml 文件,将 getUserInfo/index 作为一个 function 服务。在这里相当于定义云函数的访问别名和云函数路径的关系。
  1. functions:
  2. getUserInfo:
  3. handler: getUserInfo/index
  • 重新点击 上传部署服务端代码 提交云函数部署。

image.png

  • 在小程序首页,有当前 用户的头像和昵称 。通过调用云函数在支付宝获取用户信息,并显示到页面上。
    image.png

client/pages/todos/todos.js 文件中,修改 onLoad 方法 ,在页面初始化时获取用户信息并更新页面。

javascript // 页面初始化 onLoad() { basement.function.invoke('getUserInfo').then((res) => { if (res.success) { this.setData({ user: res.result }); // 更新页面的用户信息 } }).catch(console.error); }

读取 todo 列表

在小程序首页,需要从数据库将 todo 列表 读取出来。

image.png

client/pages/todos/todos.js 文件中,在 loadTodoList 方法 获取最新的 todo 列表并更新页面。

  1. // 获得数据并加载当前用户 todo 列表
  2. loadTodoList(){
  3. basement.db.collection('todos').find(
  4. { userId: this.data.user.userId }, // 获取当前用户的 todo 记录
  5. { sort: { createTime: -1 } }, // 按时间倒序排列
  6. ).then(({ result: todos }) => {
  7. this.setData({ todos }); // 页面页面的 todo 列表
  8. }).catch(console.error);
  9. }

更新 todo 记录

当用户点击 todo 前方的 checkbox 可以更改 todo 的状态(是否已完成),此时需要更新 todo 的数据,调整其状态。

image.png

client/pages/todos/todos.js 文件中,在 changeComplate 方法 根据 _id 更新某个 todo 的状态。

  1. // 根据 id 改变当前 todo 状态
  2. changeComplate(_id, completed){
  3. return new Promise(function (resolve, reject) {
  4. basement.db.collection('todos').updateOne(
  5. { _id }, // 更新的条件
  6. {
  7. $set: { // 更新 todo 的状态和最后完成时间
  8. completed,
  9. completeTime: completed ? new Date() : false,
  10. }
  11. }
  12. ).then(() => {
  13. resolve({ success: true });
  14. }).catch(err => {
  15. console.error(err);
  16. reject({ success: false });
  17. });
  18. });
  19. }

删除 todo 记录

当用户点击 todo 后方的删除按钮 可以删除 todo 记录,此时需要删除 todo 的数据项。

image.png

client/pages/todos/todos.js 文件中,在 deleteById 方法 根据 _id 删除某个 todo。

  1. // 删除当前的列表
  2. deleteById(_id){
  3. const that = this;
  4. return new Promise(function (resolve, reject) {
  5. // 确认和删除图片
  6. my.confirm({
  7. title: '删除 todo',
  8. content: '是否确认删除?',
  9. confirmButtonText: '删除',
  10. cancelButtonText: '取消',
  11. success: (result) => {
  12. if (result.confirm) {
  13. basement.db.collection('todos').deleteOne({ // 删除特定用户特定 _id 的 todo 项
  14. _id,
  15. userId: that.data.user.userId,
  16. }).then(() => {
  17. resolve({ success: true });
  18. }).catch(err => {
  19. console.error(err);
  20. reject({ success: false });
  21. });
  22. }
  23. },
  24. });
  25. });
  26. }

插入 todo 记录

当用户点击 + Add Todo 进入新建页面,在这里填写 todo 名称和图标 后,可以插入一条新的 todo 记录。

image.png

  • 上传图标:client/pages/add-todo/add-todos.js 文件中,在 uploadImg 方法 完成图片上传和存储。
  1. // 选择图片并上传
  2. uploadImg() {
  3. my.chooseImage({
  4. chooseImage: 1, // 只上传一张图片
  5. success: res => {
  6. const path = res.apFilePaths[0];
  7. const options = {
  8. filePath: path,
  9. headers: {
  10. contentDisposition: 'attachment',
  11. },
  12. };
  13. basement.file.uploadFile(options).then((image) => {
  14. this.setData({
  15. iconUrl: image.fileUrl, // 暂存图片地址
  16. });
  17. }).catch(console.error);
  18. },
  19. });
  20. }
  • 插入 todo:client/pages/add-todo/add-todos.js 文件中,在 addTodo 方法 完成新的 todo 插入。
  1. // 写入数据库 obj,当前用户增加一条 todo
  2. addTodo(){
  3. const that = this;
  4. return new Promise(function (resolve, reject) {
  5. basement.function.invoke('getUserInfo').then((res) => {
  6. if (res.success) {
  7. const user = res.result;
  8. basement.db.collection('todos').insertOne({
  9. text: that.data.inputValue, // todo 标题
  10. iconUrl: that.data.iconUrl ? that.data.iconUrl : false, // 图标是可选的,如果有图标则一同保存图标
  11. userId: user.userId, // 操作人
  12. completed: false, // 状态为未完成
  13. createTime: new Date(), // 创建时间是当前服务器时间
  14. completeTime: false, // 因为未完成,暂不存在完成时间
  15. }).then(() => {
  16. resolve({ success: true });
  17. }).catch(err => {
  18. console.error(err);
  19. reject({ success: false });
  20. });
  21. }
  22. }).catch(err => {
  23. console.error(err);
  24. reject({ success: false });
  25. });
  26. });
  27. }

原文: https://docs.alipay.com/mini/cloud-service/xyd2pl