准备和女朋友去旅游?赶紧撸个旅行账本小程序!


作者 | 豪豪
责编 | 伍杏玲
本文经授权转载自云开发x(ID:tcb2tcb)
【程序人生 编者按】春天来了,大家开始酝酿大大小小的旅行计划。除了线路计划外,很多人有记录各项花费的习惯。作为程序员,这点小事当然顺手就给女朋友给开发了呀,于是有了这个旅行小账本,一起看看如何实现的?
IDE
微信开发者工具
VSCode
熟悉微信开发者工具的朋友们应该知道,它不支持Emmet缩写语法,并且WXML的属性值默认用单引号表示(强迫症表示很难受)。而VSCode很好的补足了微信开发者工具的不足之处,并且支持多元化插件开发,轻量好用。
所以这里推荐采用微信开发者工具+VSCode配合开发。微信开发者工具负责调试、模拟小程序运行情况,VSCode负责代码编辑工作。二者各司其职,会使开发更加的高效、便捷。后端采用云开发。

总体架构
该项目基于小程序云开发,使用的模板是云开发快速启动模板由于是个全栈项目,前端使用小程序所支持的 WXML?+ WXSS + JS 开发模式,命名采用BEM命名规范。后台则是借助云数据库+云储存进行数据管理。

逆向工程
在做该小程序之前,有必要进行项目的逆向工程,进一步解构每一个页面,从而深入了解这款小程序的交互细节。那么现在我假设自己是产品设计师,在绘制完界面原型后,撰写了相应的交互文档。以下是我绘制的界面原型:



接下来对每个页面的细节进行解构,并完成简单的WXML结构:









云开发
在做完逆向工程的解构,页面基础结构基本搭建完成。但页面依旧是静态的,需要数据来填充。所以第二步就是数据库的设计。而小程序的云控制台恰好提供了数据的操作功能,为数据驱动提供基石。

1.云数据库设计
云数据库是一种NoSQL数据库。每一张表是一个集合。值得注意的是在设计数据库时, _id 和 _openid这两个字段需要带上。 _id是表的主键,而 _openid是用户标识,每个用户都有不同的 _openid,可区分不同用户。
以下是项目中的数据表设计:
cover_photos?账本封面表??用于存储创建账本时需要的封面信息cover_photos?
-?_id
-?_openid
-?cover_index?封面索引
-?cover_url???封面url
-?isSelected??封面是否选中
accounts?账本表???用于存储用户创建的账本
-?_id
-?_openid
-?accountKey??账本唯一标识
-?coverUrl????账本封面
-?i???????????账本索引
-?inputValue??账本名字
-?now?????????账本创建时间
-?spend???????账本总花费
account_detail?支出类型表???用于存储消费类型
-?_id
-?_openid
-?detail???????类型细节
-?pic_index????消费类型索引
-?pic_url??????未点击时的图片
-?pic_url_act??点击后的图片
-?type?????????消费类型
account_income?收入类型表???用于存储收入类型
-?_id
-?_openid
-?pic_index????收入类型索引
-?pic_url??????未点击时的图片
-?pic_url_act??点击后的图片
-?type?????????收入类型
spend_items???消费明细表
-?_id
-?_openid
-?accountKey???账本唯一标识
-?address??????消费地点
-?desc?????????消费描述
-?fullDate?????消费时间
-?money????????消费金额
-?pic_type?????消费类型
-?pic_url??????消费类型图片
2.云储存管理
这是个非常实用的板块。类似于百度云盘,它提供了文件存储、上传与下载功能:

除此之外,它还会将你所上传的资源自动进行压缩操作,并生成一个地址供你引用。该项目中的一些图片资源就是存在于此,然后在云数据库的字段中引用这些资源地址即可,十分方便,不必在本地存储,占用小程序内存:

3.云函数设计
云函数简单来说就是在云后端(Node.js)运行的代码,本地看不到这些代码的执行过程,全封闭式只暴露接口供本地调用执行,本地只需等待云端代码执行完毕后返回结果。这也是面向接口编程的思想体现。
项目中的云函数设计:
//?getTime??获取当前时间并格式化为?yyyy-mm-dd
//?云函数入口文件
const?cloud?=?require('wx-server-sdk')
//?初始化云函数
cloud.init()
//?云函数入口函数
exports.main?=?async?(event,?context)?=>?{
????var?date?=?new?Date()
????var?seperator1?=?"-"
????var?year?=?date.getFullYear()
????var?month?=?date.getMonth()?+?1
????var?strDate?=?date.getDate()
????if?(month?>=?1?&&?month?< =?9)?{
???????month?=?"0"?+?month
????}
????if?(strDate?>=?0?&&?strDate?< =?9)?{
???????strDate?=?"0"?+?strDate
????}
????//?格式化当前时间
????var?currentdate?=?year?+?seperator1?+?month?+?seperator1?+?
??????????????????????strDate
????return?currentdate
}
//?deleteItems??批量删除,云数据库的批量删除只允许在云函数中执行
//?云函数入口文件
const?cloud?=?require('wx-server-sdk')
//?初始化云函数
cloud.init()
//?连接云数据库
const?db?=?cloud.database()
const?_?=?db.command
//?云函数入口函数
exports.main?=?async?(event,?context)?=>?{
?????try?{
??????????return?await?db.collection('spend_items')
?????????????.where({
???????????????accountKey:?event.accountKey
?????????????})
?????????????.remove()
??????}?catch?(e)?{
????????console.error(e)
????}
}


MVVM
界面有了,数据有了。万事俱备,只欠东风!所以下一步就是MVVM的设计。小程序本质就是基于MVVM所设计的,在MVVM的世界里,数据是灵魂,一切都由数据来驱动。
1.【账本页】显示
账本页有两种显示的风格,左上角的按钮可以来回切换风格,下拉可刷新页面,显示accounts数据表中存储的账本信息。显示时有个小细节,需要根据创建的时间先后来显示,越晚创建的越先显示:
//?页面数据设计,?在wxml中使用{{}}符号引用数据,数据就动态显示到了页面上
data:?{
????isList:?false,?//?转换页面风格的标识?true为竖向风格?false为横向风格
????accounts:?[],??//?存储查询的账本数据
????now:?null,?????//?存储当日时间
????year:?null?????//?存储年份
}
//?转换显示风格
switchList()?{
//?设置页面风格样式
????let?isList?=?!this.data.isList
????this.setData({
??????isList
????})
????wx.setStorage({
??????key:?"isList",
??????data:?isList
???})
}
//?获取页面风格转换标识
var?isList?=?wx.getStorageSync('isList')
//?查询账本
db.collection('accounts')
.get({
????success:?res?=>?{
??????this.setData({
????????accounts:?res.data.reverse(),??//?反转数组,优先显示创建早的账本
????????isList
????})
??????wx.hideLoading()
???}
})
//?调用云函数接口?获取当前日期
wx.cloud.callFunction({
//?云函数接口名就是创建的云函数名字,这里是'getTime'
????name:?'getTime',
????success:?(res)?=>?{
????let?year?=?res.result.split('-')[0]
????this.setData({
??????now:?res.result,
??????year
???})
???},
????fail:?console.error
})
2.【账本页】增删改
账本页通过调用相应的云数据库API,可进行一系列的增删改操作。值得一提的是,修改时需要表单回显,删除时需要级联删除。因为一个账本中有许多收支情况,spenditems表就是进行收支记录,所以删除账本时需要级联删除对应的spenditems表中的收支信息。
3.【支出】与【收入】页面设计
因为收入与支出页面基本类似,所以使用自定义组件封装,可以复用,具体看源码。
4.【账本明细】页面

因为账本明细中需要显示每一天的消费信息,所以需要将数据表中的数据通过时间来分类,分成若干个数组,页面从而使用wx:for来遍历这些数组。在显示之前,首先需要判断有无收支信息:
//?通过时间分类算法??{}?=>?[?[{时间1}],?[{时间2}],?[{时间3}]?]
arr.forEach(item?=>?{
if?(!_this.isExist(item.fullDate,?dateArr))?{
????dateArr.push([item])
}?else?{
????dateArr.forEach(res?=>?{
????????if?(res[0].fullDate?==?item.fullDate)?{
????????????res.push(item)
????????}
???})
??}
})
//?使用map?方法构造?[{},?{},?{},?...]?类型数组
dateArr?=?dateArr.map((item)?=>?{
??let?spend?=?0
??let?income?=?0
??item.forEach(res?=>?{
??????if?(res.money?>?0)?{
??????????spend?+=?res.money
??????}?else?{
??????????income?+=?(-res.money)
?????}
??})
return?{
????item,
????spend,
????income
}
})
//?判断自身是否存在数组中
isExist(item,?arr)?{
????for?(let?i?=?0;?i?< ?arr.length;?i++)?{
????if?(item?==?arr[i][0].fullDate)
????return?true
}
??return?false
}
以上是小程序中比较复杂的逻辑实现。
源码传送门:
https://github.com/FightingHao/travelbook
正在学习小程序?
开发小程序遇到问题不知道问谁?
找不到小程序的组织?
欢迎加入我们的小程序开发者群!



?热 文?推 荐?
?找 Bug 赚了 6,700,000元!这位 00 后是怎么做到的?
?漫画:如何给女朋友解释为什么有些网站域名不以 WWW 开头
?以安全之名:2019年DevSecOps社区调研白皮书解读
?虎口夺食! 打破Facebook谷歌垄断, MIT大神和他的区块链数据库传奇! |人物志
System.out.println("点个在看吧!");
console.log("点个在看吧!");
print("点个在看吧!");
printf("点个在看吧!n");
cout?< "点个在看吧!"?<
Console.WriteLine("点个在看吧!");
Response.Write("点个在看吧!");
alert("点个在看吧!")
echo "点个在看吧!"

关注公众号:拾黑(shiheibook)了解更多
[广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
关注网络尖刀微信公众号随时掌握互联网精彩
- 1 习近平听取李家超述职报告 7904315
- 2 收入分配制度或迎重大改革 7808563
- 3 福建舰入列后首次通过台湾海峡 7712915
- 4 回顾山东舰硬核名场面 7615887
- 5 健美冠军王昆去世 曾获职业赛8连冠 7520116
- 6 女教师新婚坠亡一楼业主要求赔偿 7426616
- 7 日本遗孤:从小恨自己为何是日本人 7328178
- 8 95后幼儿园园长1年给老师发3次黄金 7236956
- 9 亚运冠军称举报领导后不敢再呆在家 7140780
- 10 感染甲流后该如何科学调养 7048288




![张颖冰 最近好爱头巾啊[HO加油] ](https://imgs.knowsafe.com:8087/img/aideep/2023/7/6/4a67357b167a1a82757969ca40c72f9a.jpg?w=250)



程序人生
