微信小程序项目wx-store代码详解
这篇文章会很长,非常长,特别长,无敌长。
真的是挤牙膏般的项目进度,差不多是8月底有开始这个项目的想法,时至今日都1个多月了,抛去频繁的加班时间,王者时间,羽毛球时间...见缝插针的写这个项目,我竟然写完了,我竟然没有半途放弃,可真把我自己感动坏了。
好吧,主要是这个小程序很简单,本文会讲解一下这个小程序的代码,所有代码几乎是我一个个敲出来的,所有逻辑也是自己构思梳理的,因此很多实现方式并不是很好,冗余代码很多,代码质量堪忧,但我也在学习中,随着技术提升,会不断来重构代码,如果大家有任何建议欢迎私信我哦,特别感谢。
该小程序采用的云开发,没有自己搭建后端,我心目中,只要没有后端的内容我就觉得很简单。但其实我还是想有朝一日能自己独立完成前后端所有工作,写一个更棒的作品。
之前有写过几篇文章,可以回顾一下。你可能需要的文章:
天啦噜,看了半天代码发现还挺多,一时间不晓得该从哪里开始。那还是按照tab页使用逻辑顺序来吧。
启动页
之前文章也有过如何写一个启动页面。至于为什么需要个启动页呢?
我觉得主要也就两点,一个是好看,还有个就是添加获取用户信息类型按钮,间接引导用户授权获取用户信息。
至于为啥要获取用户信息?
曾真有段时间,我纠结了好久,因为我自己这个程序中,除了展示,好像也没啥需要用到这个数据的地方。
但我还是写了。
当前登陆用户的用户名和头像图片,通过 open-data 标签可以无需授权直接获取,只要指定相应类型userNickName和userAvatarUrl即可,样式只需要用view容器包裹起来进行设置。button按钮指定open-type为getUserInfo,在点击事件中就可以拿到授权之后的用户公开信息数据。
拿到用户信息需要保存到数据库中,也只需要首次登录的用户在授权之后才需要入库,所以加个判断当前登陆用户是否是首次登录,判断条件是每个用户的唯一值openId。
通过这个逻辑,那需要处理的可以分为如下几个:
1、获取当前登录用户的openId。
getOpenID() {
let that = this;
wx.cloud.callFunction({
name: 'login',
complete: res => {
const openId = res.result.event.userInfo.openId;
that.setData({
openId
})
}
})
},
这个login云函数是项目构建时自动生成的,云函数写法:
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
return {
event,
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID,
env: wxContext.ENV,
}
}
2、获取数据库中所有用户数据。
getUsersList() {
const that = this;
db.collection('users').get({
success: res => {
that.setData({
usersList: res.data,
})
},
fail: console.error
})
},
3、在页面刚加载的时候调用上面两个方法,拿到openId和userList。在点击按钮时,检查当前登录用户是否已经存在数据库。
goToIndex(e) {
const that = this;
const auth = e.detail.errMsg;
wx.switchTab({
url: '/pages/index/index',
});
if(auth === "getUserInfo:ok") {
const avatarUrl = e.detail.userInfo.avatarUrl;
const nickName = e.detail.userInfo.nickName;
that.checkUser(nickName, avatarUrl);
}
},
这里获取到用户信息数据是这个样子滴:
4、检查当前登录用户是否已经存在数据库。
checkUser(name, url) {
const that = this;
const list = this.data.usersList;
const openId = this.data.openId
const ids = [];
list.forEach((item) => {
ids.push(item._openid);
})
if(ids.indexOf(openId) === -1) {
that.setUserInfo(name, url)
} else {
return;
}
},
5、如果不存在的话,将该用户信息存入数据库users中。管它有用没用,先存着呗。
setUserInfo(name, url) {
db.collection('users').add({
data: {
nickName: name,
avatarUrl: url,
}
})
},
数据库中会自动给这个字段生成一个id和openId。
首页
首页从上到下分为几块,轮播图,轮播告示,icon列表,推荐商品展示。
轮播图。
直接用自带的组件,swiper和swiper-item配合使用。
<swiper class="swiper-top" indicator-dots="true" indicator-active-color="#fff" autoplay circular>
<swiper-item wx:for="{{bannersList}}" wx:key="item">
<image mode="aspectFill" data-url="{{item}}" src="{{item}}" />
</swiper-item>
</swiper>
图片数据来自数据中定义好的。
getBannerList() {
db.collection('banners').get({
success: res=> {
this.setData({
bannersList: res.data[0].imgs,
})
},
fail: console.error
})
}
因为之前有小伙伴咨询过,如何云开发中数据库新建集合,这里用gif简单说明一下。
为啥要两个gif呢,因为超过300帧的它不给上传~
轮播告示。
和轮播图一样的,只是轮播方向不同,swiper中添加个参数 vertical。点击显示弹窗,引用的是WeUI库,咋用参考以往文章。
icon列表。
到这里就要用到本程序中最最最复杂的一个数据库集合了,几乎所有的商品数据都是存放在这个集合中的。
那icon列表就是获取goods集合中每个对象icon字段值,推荐商品列表就是每个对象中list数组中所有isHot为true的数据。
getIconList() {
const that = this;
const arr = [];
db.collection('goods').get({
success: res=> {
const list = res.data;
list.forEach((item) => {
item.list.forEach((d) => {
if(d.isHot) {
const param = {
...d,
categoryId: item._id
};
arr.push(param);
}
})
})
that.setData({
categories: list,
goodsRecommend: arr
})
},
fail: console.error
})
},
给每个icon图片上加一个跳转到分类页的点击事件,一般的跳转可以使用wx.navigateTo,而tab页的跳转只允许使用wx.switchTab,官方文档中指明这个方法是不可以后缀参数的。
而我这里肯定是需要点击不同的icon跳转到不同的分类栏目中的,那就需要在跳转时候携带该分类id,还是当前这个数组的下标。
通过定义全局参数,可以解决wx.switchTab无法携带参数的问题。
app.js中,在onLaunch里定义个全局对象。
this.globalData = {
categoryParamId: 0,
categoryParamIndex: 0,
}
商品分类页
在menu.js中,在最开始需要引入全局变量。
const app = getApp()
那上面定义的globalData可以直接通过app拿到。
分类页这儿主要的处理逻辑有三块内容。
1、区分管理员权限和普通用户权限。
管理员权限可以有新增商品和删除的功能,普通用户只可以查看。
权限这块的处理应该会有更好的方案。
我比较挫,想到的最简单的方法就是利用openId来做过滤。在页面初次加载的时候获取当前用户的openId,和启动页一样的方法,只是回调函数中不一样。在数据库中定义个管理员集合,你需要给那些用户设置成管理员,将他们的openId放在这个集合中。
我是在app.js中获取这个管理员集合的,可能是刚刚尝过全局变量的甜头吧。
wx.cloud.database().collection('adminList').get({
success: res => {
this.adminList = res.data[0].admin_openId;
},
})
那在menu.js中可以直接拿到这个adminList中数据,判断一下当前登录用户的openId在不在adminList中。
getOpenID() {
let that = this;
wx.cloud.callFunction({
name: 'login',
complete: res => {
const openId = res.result.event.userInfo.openId;
if(app.adminList.indexOf(openId) === -1) {
that.setData({
isAdmin: false
})
} else {
that.setData({
isAdmin: true
})
}
}
})
},
2、将设置成喜欢状态的商品数据存入本地缓存。
当时对于这个逻辑处理的考虑也是想了蛮久,这个小程序的制作出发点只是作为一个助手作用,方便用户查看店铺所有商品,是做一个商品分类展示的功能,不支付线上下单,主要也是因为显示下单这个功能太复杂,个人小程序没权限做。
那我就想着仅仅分类展示并不满足使用,加入个喜欢列表实用性更大。
商品的固定数据是可以存入云开发的数据库中,但是针对于每个用户不同的喜欢数据,最好的方案就是使用缓存。
localStorange的数据形式是key / value,一开始计划的是固定一个key,value中是个数组对象。
这一定是可行的,但我不会做......麻烦能实现的朋友私信我。
好的方案来不了可以来挫的嘛。我用商品的分类Id和当前商品Id拼接起来作为key,这就保证了key唯一性,那存入本地的数据是需要在喜欢列表展示的,我需要展示的数据有分类Id,id,商品名,是不是喜欢,封面缩略图,价格。明白了这几点要求,实现就很简单了。
在每个商品的爱心图标上加一个点击事件。
joinFavorites(e) {
const that = this;
const id = e.currentTarget.dataset.id;
const index = e.currentTarget.dataset.index;
const list = this.data.goodsList[this.data.curIndex].list;
const loveList = [];
list.forEach((item) => {
if (item.id === id) {
item.isLove = !item.isLove;
}
const param = {
categoryId: this.data.curNav,
id: item.id,
name: item.goodsName,
isLove: item.isLove,
thumbnail: item.imgs[0],
price: item.newPrice
};
loveList.push(param);
})
that.setData({
goodsList: this.data.goodsList,
}) // 缓存的key以分类id和服装id用-连接
const key = loveList[index].categoryId + "-" + loveList[index].id;
this.saveLocally(key, loveList[index]);
},
// 存入本地缓存
saveLocally(key, data) {
wx.setStorage({
key,
data,
})
},
现在看这个代码,我觉得还是可以再重构优化的更好的。
3、从本地缓存中获取喜欢列表详情
有些商品是已经加入喜欢列表的,商品上的喜欢图标已经是高亮状态,等到下次进入该分类页,就应该将之前设置喜欢状态的商品显示出来,不然每次进来都是初始的模样就毫无意义了。
首先是需要获取商品列表数据,再根据本地缓存数据,将喜欢的商品数据修改一下状态。
这样就是分三步走。
获取商品列表数据。
getGoodsList() {
const that = this;
db.collection('goods').get({
success: res => {
const list = res.data;
that.getDetails(that.data.storageData, list); that.setData({
goodsList: list,
})
}
})
},
读取缓存数据。
getLocally() {
const that = this;
wx.getStorageInfo({
success(res) {
if (res.currentSize > res.limitSize) {
that.setData({
isDialog: true
})
} else {
that.setData({
storageData: res.keys
})
} },
})
},
从本地缓存中获取喜欢列表详情。
getDetails(localArr, goodsList) {
const that = this;
localArr.forEach((localItem) => {
const itemPId = localItem.split("-")[0].toString();
const itemId = localItem.split("-")[1].toString();
goodsList.forEach((goodItem) => {
if (itemPId === goodItem._id) {
goodItem.list.forEach((item) => {
if (itemId === item.id.toString()) {
wx.getStorage({
key: localItem,
success(res) {
item.isLove = res.data.isLove
that.setData({
goodsList,
})
}
})
}
})
}
})
})
},
主要的处理逻辑就是以上这三块。还有些其他的交互方法,商品分类的切换,详情页跳转,商品删除......这些就不写了,可以去看代码,都很容易理解的。
商品详情页
点击跳转过来的时候,携带的参数只有分类id和商品id。根据这两个字段就可以在商品列表数据查询到具体所有数据。
在当前页面获取传参过来的数据。
onLoad: function (options) {
this.setData({
categoryId: options.categoryId,
id: options.id
});
}
新增商品页
按照之前数据库集合中定义的数据格式,这里就分两块。一个是相关数据的填写表单,一个就是上传的图片列表。
图片列表上传的实现,官方都给了相应的api方法。
选择图片:
wx.chooseImage({
sizeType: ["original", "compressed"], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ["album", "camera"], // 可以指定来源是相册还是相机,默认二者都有
success: function (res) {
// 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
var tempFilePaths = res.tempFilePaths;
var imgs = that.data.imgs;
for (var i = 0; i < tempFilePaths.length; i++) {
if (imgs.length >= 9) {
that.setData({
imgs: imgs,
});
return false;
} else {
const filePath = res.tempFilePaths[i];
var timestamp = Date.parse(new Date());
const cloudPath = `${timestamp}-${i}`;
const param = {
cloudPath,
filePath,
};
imgs.push(param);
}
}
that.setData({
imgs: imgs,
});
},
});
上传图片:
uploadImgs(list) {
const that = this;
const imgList = [];
list.forEach((item) => {
wx.cloud.uploadFile({
cloudPath: `uploadImgs/${item.cloudPath}`, // 存入uploadImgs文件夹中
filePath: item.filePath, // 文件路径
}).then((res) => {
if(res.errMsg === "cloud.uploadFile:ok") {
imgList.push(res.fileID)
}
that.setData({
imgList,
})
if(that.data.imgList.length === that.data.imgs.length) {
that.add()
}
})
.catch((error) => {
console.log(error);
});
});
},
最终把表单数据和图片列表数据到存入数据库集合中。
add() {
const that = this;
wx.cloud.callFunction({
name: 'addGoods',
data: {
categoryId: that.data.categoryId,
id: that.data.id,
goodsName: that.data.goodsName,
newPrice: that.data.newPrice,
oldPrice: that.data.oldPrice,
isHot: that.data.isHot,
imgs: that.data.imgList
}
}).then()
},
商品新增的云函数:
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database()
const _ = db.command
exports.main = async (event, context) => {
const goodsName = event.goodsName;
const categoryId = event.categoryId;
const id = event.id;
const newPrice = event.newPrice;
const oldPrice = event.oldPrice;
const isHot = event.isHot;
const imgs = event.imgs;
db.collection("goods").doc(categoryId).update({
data: {
list: _.push({
id,
goodsName,
newPrice,
oldPrice,
isHot,
imgs
})
}
}) return {
categoryId,
id,
goodsName,
newPrice,
oldPrice,
isHot,
imgs
}
}
喜欢列表页
最轻松的一个页面,读取本地缓存展示数据。这里还用到了WeUI的mp-slideview组件,修改这个组件的样式还是挺麻烦,高度样式没改成功,多少存在点瑕疵。
个人信息页
这个页面已经纯属和小程序主旨功能无关了,我就是无聊写着玩凑凑页面的。想写些什么都可以自由发挥,随便添加什么功能都可以,这里我就不介绍我随便写的东西了。
至此,该篇历经四天的文章终于结束(主要是周末玩了两天),目前正文字数4500+......
我废话可真多呀。
这个小程序会继续维护,有任何不明白的地方联系我哦~
关注我吧
微信小程序项目wx-store代码详解的更多相关文章
- 微信小程序支付及退款流程详解
微信小程序的支付和退款流程 近期在做微信小程序时,涉及到了小程序的支付和退款流程,所以也大概的将这方面的东西看了一个遍,就在这篇博客里总结一下. 首先说明一下,微信小程序支付的主要逻辑集中在后端,前端 ...
- 【微信小程序】template模板使用详解
WXML提供模板(template),可以在模板中定义代码片段,然后在不同的地方调用. 模板的作用域: 模板拥有自己的作用域,只能使用 data 传入的数据以及模板定义文件中定义的 <wxs / ...
- 【干货】小程序内嵌 H5 代码详解
自从微信小程序发布了 web-view 组件,使得之前的 H5 网站移植到小程序成为可能.现在,很多项目在迁移的过程中遇到了许多问题,本文通过实例代码,为你讲解迁移过程中的几个典型场景. 1.小程序和 ...
- 微信小程序 this.setData() 详解
1.定义 setData()函数用于将逻辑层数据发送到视图层,同时对应的改变this.data的值. 2.setData()参数格式 接受一个对象,以键(key)值(value)的方式改变值. 其中, ...
- 【微信小程序项目实践总结】30分钟从陌生到熟悉 web app 、native app、hybrid app比较 30分钟ES6从陌生到熟悉 【原创】浅谈内存泄露 HTML5 五子棋 - JS/Canvas 游戏 meta 详解,html5 meta 标签日常设置 C#中回滚TransactionScope的使用方法和原理
[微信小程序项目实践总结]30分钟从陌生到熟悉 前言 我们之前对小程序做了基本学习: 1. 微信小程序开发07-列表页面怎么做 2. 微信小程序开发06-一个业务页面的完成 3. 微信小程序开发05- ...
- 高仿Readhub小程序 微信小程序项目【原】
# News #### 项目介绍微信小程序项目涉及功能 https://gitee.com/richard1015/News https://github.com/richard1015/News 高 ...
- 微信小程序项目实战之天气预报
概述 微信小程序项目实战之天气预报 详细 代码下载:http://www.demodashi.com/demo/10634.html 一.准备工作 1.注册微信小程序 2.注册和风天气账号 3.注册百 ...
- 微信小程序项目实战之豆瓣天气
概述 微信小程序项目实战之豆瓣天气 详细 代码下载:http://www.demodashi.com/demo/10943.html 一.准备工作 1.注册微信小程序 2.在小程序设置中设置reque ...
- 微信小程序项目转换为uni-app项目
一.它是谁? [miniprogram-to-uniapp]转换微信小程序"项目为uni-app项目.原则上混淆过的项目,也可以进转换,因为关键字丢失,不一定会完美. 二.它的原理是什么? ...
- 微信小程序开发01 --- 微信小程序项目结构介绍
一.微信小程序简单介绍: 微信官方介绍微信小程序是一个不需要下载安装就可使用(呵呵,JS代码不用下载吗?展示的UI不用下载吗?)的应用,它实现了应用“触手可及”的梦想,用户扫一扫或搜一下即可打开应用. ...
随机推荐
- Java成神之路:第三帖----数据结构与算法之队列
数据结构与算法--队列 今天掉了两根头发,摸掉的,记得 别乱摸,很珍贵的!! 什么是队列? 1)队列是一个有序列表,可以用数组或是链表来实现 2)遵循 先入先出 的原则.即:先存入队列的数据,要先取出 ...
- 复习 | 彻底弄懂Flexbox之Demo篇
flexbox之前有接触,写项目时也用过,但也只是简单的,对其也是似懂非懂,所以今天下定决心把这个再学一遍,因为似懂非懂就是不懂 本文主要是的demo演示,想看flexbox语法 请移步flexbox ...
- 初学 React native | 环境搭建(在模拟器上运行)
我的电脑是windows 所以就以 windows上+Android 配置React native 环境 网上的安装教程非常多,我总结了一下,配置环境时出错原因主要是node java python ...
- C语言的运算符
操作数 :参与运算的变量 表达式:有意义有结果的语句 运算符的分类:1.按照功能运算符划分:算术运算符 关系运算符 逻辑运算符 位运算 2.按照参与运算的操作数个数划分:单目运算(只有一个操作数) 双 ...
- P4742 【[Wind Festival]Running In The Sky】
相信来做这道题的人肯定都学过\(Tarjan\)缩点吧,如果没有建议先去做P3387 [模板]缩点,如果你忘了,建议也去看看 满足上面要求后,你会惊奇发现,这两道题基本一样,唯一的差别就是这道题需要记 ...
- Spark Job-Stage-Task实例理解
Spark Job-Stage-Task实例理解 基于一个word count的简单例子理解Job.Stage.Task的关系,以及各自产生的方式和对并行.分区等的联系: 相关概念 Job:Job是由 ...
- 实验 3:Mininet 实验——测量路径的损耗率
实验目的 在实验 2 的基础上进一步熟悉 Mininet 自定义拓扑脚本,以及与损耗率相关的设 定:初步了解 Mininet 安装时自带的 POX 控制器脚本编写,测试路径损耗率. 实验任务 h0 向 ...
- SQL Server通过创建临时表遍历更新数据
前言: 前段时间新项目上线为了赶进度很多模块的功能都没有经过详细的测试导致了生成环境中的数据和实际数据对不上,因此需要自己手写一个数据库脚本来更新下之前的数据.(线上数据库用是SQL Server20 ...
- SSH 框架常见错误
新手学习SSH框架的时候总会遇到很多问题,一碰到404 或者500错误就不知道怎么解决. 404错误是很常见的一个错误,如果没有用框架基本上只可能是没有这个路径或者文件,但是用了框架之后404的原因就 ...
- Android Studio 自定义字体显示英文音标
android:fontFamily="serif" 网上查了很多自定义字体的方式,或多或少都有些麻烦,最后还是尝试着认为内置字体不应该实现不了英文音标问题,就一个一个字体试了一下 ...