Basement 版
Todo App 是支付宝小程序的一个经典案例,本章节我们将结合小程序客户端程序和 Basement 后端服务,完成一个小程序的开发,使其同时具备客户端和后端的能力,可以作为一个完成程序使用。
前提条件
创建小程序
- 登录 蚂蚁金服开放平台,选择 开发者中心 > 小程序 > 创建 。
- 填写 基本信息,点击 创建 按钮,创建应用名为 示例应用 小程序。
说明:一个账号最多可以创建 10 个小程序;未提交过审核的小程序可以删除,删除的小程序不在计数范围。
创建 Basement
- 在 我的小程序 页面,选择已经创建的小程序,点击 查看,进入 开发管理 页面。
- 点击左侧导航栏的 云服务(公测),在 云服务列表 页面点击 创建云服务 > 创建 Basement 。
- 在 创建 Basement 页面,填入 应用名称 和 描述 (选填),点击 创建。
说明:每个账号可免费创建一个 Basement 服务;当前测试环境该方案免费提供,但若 连续 7 日小程序没有访问量,并且未部署过代码 ,环境资源会被自动回收。
准备开发环境
打开小程序开发者工具,点击 新建项目。
在新建项目向导中,选择 小程序,选择 Todo Basement 示例,点击 下一步。
输入 项目名称,项目路径会自动填充,选择 Basement 为后端服务,点击 完成。
在 IDE 的客户端依赖页面,安装 @ant-basement/miniprogram-sdk。
说明:如果列表中找不到,可以在依赖框中输入 @ant-basement/miniprogram-sdk 完成添加。
- 点击 云服务:请选择 > 选择此前已经创建好的 Basement 服务作为关联的云服务。
部署云函数
点击 云服务 唤起菜单,点击 上传部署服务端代码 提交云函数部署。
说明:将 server 目录的代码作为云函数进行上传和部署。部署成功相当于完成云函数的代码发布,部署前请注意区分测试环境和生产环境的区别。
预览和上传
点击 刷新图标 可以在 IDE 模拟器 中查看预览效果;还可以点击 预览 ,使用 支付宝扫描二维码 在手机上实现真机预览。
点击 上传 将小程序上传到开放平台,具体操作请参考 小程序提审、发布流程。
后端代码介绍
获取用户信息
小程序是运行在支付宝上的,首先需要获取支付宝用户的基础信息,用于完善用户头像、昵称等信息。我们需要创建一个云应用,通过支付宝 OpenAPI,当用户访问小程序的时候将对应信息获取到并显示在页面上。
- 在 server 目录增加 getUserInfo/index.js 文件,用于编写和存放云函数的代码。
'use strict';
module.exports = async (ctx) => {
// 从支付宝 OpenAPI 获取用户信息
const user = await ctx.basement.openapi.alipay.exec('alipay.user.info.share');
// 返回用户信息
return user;
};
- 在 server 目录修改 serverless.yml 文件,将 getUserInfo/index 作为一个 function 服务。在这里相当于定义云函数的访问别名和云函数路径的关系。
functions:
getUserInfo:
handler: getUserInfo/index
- 重新点击 上传部署服务端代码 提交云函数部署。
- 在小程序首页,有当前 用户的头像和昵称 。通过调用云函数在支付宝获取用户信息,并显示到页面上。
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 列表 读取出来。
client/pages/todos/todos.js 文件中,在 loadTodoList 方法 获取最新的 todo 列表并更新页面。
// 获得数据并加载当前用户 todo 列表
loadTodoList(){
basement.db.collection('todos').find(
{ userId: this.data.user.userId }, // 获取当前用户的 todo 记录
{ sort: { createTime: -1 } }, // 按时间倒序排列
).then(({ result: todos }) => {
this.setData({ todos }); // 页面页面的 todo 列表
}).catch(console.error);
}
更新 todo 记录
当用户点击 todo 前方的 checkbox 可以更改 todo 的状态(是否已完成),此时需要更新 todo 的数据,调整其状态。
client/pages/todos/todos.js 文件中,在 changeComplate 方法 根据 _id 更新某个 todo 的状态。
// 根据 id 改变当前 todo 状态
changeComplate(_id, completed){
return new Promise(function (resolve, reject) {
basement.db.collection('todos').updateOne(
{ _id }, // 更新的条件
{
$set: { // 更新 todo 的状态和最后完成时间
completed,
completeTime: completed ? new Date() : false,
}
}
).then(() => {
resolve({ success: true });
}).catch(err => {
console.error(err);
reject({ success: false });
});
});
}
删除 todo 记录
当用户点击 todo 后方的删除按钮 可以删除 todo 记录,此时需要删除 todo 的数据项。
client/pages/todos/todos.js 文件中,在 deleteById 方法 根据 _id 删除某个 todo。
// 删除当前的列表
deleteById(_id){
const that = this;
return new Promise(function (resolve, reject) {
// 确认和删除图片
my.confirm({
title: '删除 todo',
content: '是否确认删除?',
confirmButtonText: '删除',
cancelButtonText: '取消',
success: (result) => {
if (result.confirm) {
basement.db.collection('todos').deleteOne({ // 删除特定用户特定 _id 的 todo 项
_id,
userId: that.data.user.userId,
}).then(() => {
resolve({ success: true });
}).catch(err => {
console.error(err);
reject({ success: false });
});
}
},
});
});
}
插入 todo 记录
当用户点击 + Add Todo 进入新建页面,在这里填写 todo 名称和图标 后,可以插入一条新的 todo 记录。
- 上传图标:client/pages/add-todo/add-todos.js 文件中,在 uploadImg 方法 完成图片上传和存储。
// 选择图片并上传
uploadImg() {
my.chooseImage({
chooseImage: 1, // 只上传一张图片
success: res => {
const path = res.apFilePaths[0];
const options = {
filePath: path,
headers: {
contentDisposition: 'attachment',
},
};
basement.file.uploadFile(options).then((image) => {
this.setData({
iconUrl: image.fileUrl, // 暂存图片地址
});
}).catch(console.error);
},
});
}
- 插入 todo:client/pages/add-todo/add-todos.js 文件中,在 addTodo 方法 完成新的 todo 插入。
// 写入数据库 obj,当前用户增加一条 todo
addTodo(){
const that = this;
return new Promise(function (resolve, reject) {
basement.function.invoke('getUserInfo').then((res) => {
if (res.success) {
const user = res.result;
basement.db.collection('todos').insertOne({
text: that.data.inputValue, // todo 标题
iconUrl: that.data.iconUrl ? that.data.iconUrl : false, // 图标是可选的,如果有图标则一同保存图标
userId: user.userId, // 操作人
completed: false, // 状态为未完成
createTime: new Date(), // 创建时间是当前服务器时间
completeTime: false, // 因为未完成,暂不存在完成时间
}).then(() => {
resolve({ success: true });
}).catch(err => {
console.error(err);
reject({ success: false });
});
}
}).catch(err => {
console.error(err);
reject({ success: false });
});
});
}