仿鸿星尔克微信小程序
前言
最近鸿星尔克直接刷屏朋友圈,前阵子也学过一段时间的微信小程序开发,为了巩固所学的知识和提高实战经验,并且支持一波鸿星尔克,所以用微信小程序云开发来着手制作了一个简易版的仿鸿星尔克小程序。在此次开发过程中,着重于功能方面的实现,实现了网上购物的基础功能,并且在开发过程中碰到过学习中没有遇到过的坑,我也会在文章中分享我开发中小程序的痛点难点,并且分享我所遇到的坑给和我一样的初学小程序的人带来一些帮助。
项目部分展示:
开发工具准备
云数据库设计
在本次小程序开发中,我运用了云数据库来帮助我完成这次项目。本次使用了俩个数据库表,一个是存放所有商品的products的表,下面是products表的截图,并且简要介绍。
另外一个是shopcar购物车表,这个表一开始是为空的,用户在进入详情页后,点击加入购物车,便会在shopcar购物车表中添加一个数据,下面是shopcar表的截图,并附带简要介绍。
首页page/home的设计
home.wxml中大概代码框架如下:
有一个hx__hd设置为上面展示的一些导航信息,并且固定在这里,然后是一个swiper标签,里面有五个swiper-item分别代表着首页/推荐/商品/上新/促销的页面
这里运用了竖直方向的弹性布局,先设置整个页面高100vh,然后设置首页导航的宽度,然后设置view标签中的class=hx_swiper_list为1,运用弹性布局,将除首页导航的高度外,其余的高度都分配它。最后设置其子代高度为100%,来继承hx_swiper_list得来的高度。完成本次布局。
.hx_swiper_list{
flex: 1;
}
.hx_swiper_list_swiper,
.hx_swiper_list__item{
height: 100%;
}
自定义tabBar
"tabBar": {
"selectedColor": "#fece00", //选中的颜色
"borderStyle": "white",
"backgroundColor": "#ededed",//背景颜色
"list": [ //开始写导航栏了
{
"text": "首页", // 下面的字
"pagePath": "pages/home/home", //对应的页面
"iconPath": "assets/images/tabs/home.png", //图片
"selectedIconPath": "assets/images/tabs/home-active.png" //选中时的图片
},
{
"text": "分类",
"pagePath": "pages/sort/sort",
"iconPath": "assets/images/tabs/sort.png",
"selectedIconPath": "assets/images/tabs/sort-active.png"
},
{
"text": "购物车",
"pagePath": "pages/shopcar/shopcar",
"iconPath": "assets/images/tabs/shopcar.png",
"selectedIconPath": "assets/images/tabs/shopcar-active.png"
},
{
"text": "我的",
"pagePath": "pages/my/my",
"iconPath": "assets/images/tabs/my.png",
"selectedIconPath": "assets/images/tabs/my-active.png"
}
]
},
在app.json中定义"tabBar": {} 来自定义导航栏,并且可以定义选中后的字体颜色,和选中后不同样式的图片。使得这个导航栏可以表示并体现出此时选中的到底是哪些导航标签。
完成点击首页/推荐/商品/上新/促销,与手指水平滑动切换数据联动
data:{
activeSwiperIndex:0, // activeSwiperIndex,activeTabIndex 绑定联动 是首页 推荐等
activeTabIndex:0, // 让滑动换面与点击 首页和推荐等绑定
}
在home.js中的data数据中:
<view>
<view bindtap="switchTab" data-index="0"
class="hd__tabs_item {{activeTabIndex == 0?\"hd_tabs__item_on\":\"\"}}" >首页</view>
<view bindtap="switchTab" data-index="1"
class="hd__tabs_item {{activeTabIndex == 1?\"hd_tabs__item_on\":\"\"}}" >推荐</view>
<view bindtap="switchTabproduct" data-index="2"
class="hd__tabs_item {{activeTabIndex == 2?\"hd_tabs__item_on\":\"\"}}" >商品</view>
<view bindtap="switchTabNew" data-index="3"
class="hd__tabs_item {{activeTabIndex == 3?\"hd_tabs__item_on\":\"\"}}" >上新</view>
<view bindtap="switchTab" data-index="4"
class="hd__tabs_item {{activeTabIndex == 4?\"hd_tabs__item_on\":\"\"}}" >促销</view>
</view>
然后在home.wxml中对这些按钮设置点击事件switchTab,并传入一个index值,并且设置一个三元运算符,如果现在点击的对应的额是activeTabIndex的值,便会在其下方加重下划线,表示选中的意思。
switchTab(e){
let index=e.currentTarget.dataset.index;
if(index == this.data.activeTabIndex){
return ;
}
this.setData({
activeTabIndex:index,
})
},
现在是switchTab事件,让传入的date-index的值赋值给index的变量,随后进行判断,若index等于activeTabIndex,直接返回,若不相等则将index的值赋给activeTabIndex。
<swiper class="hx_swiper_list_swiper"
current="{{activeTabIndex}}"
bindchange="doListChange">
随后在swiper标签中传入一个current值为{{activeTabIndex}},用bindchange事件来实现滑动与点击按钮联动。
doListChange(e){
let index=e.detail.current;
this.setData({
activeTabIndex:index,
})
},
随后重新将传入的值赋给activeTabIndex的值,这样就实现了滑动与点击联动,只要点击一个,也会影响另外一个。
循环输出数据库中的products表单,并且能够根据products表单的属性来出现在指定的页面。
预加载拿到初始最初显示数据
const limit = 10;
Page({
data:{
productlike:[], //存放首页中猜你喜欢的最初的10条数据
productRec:[], //存放推荐页面中的最初10条数据
productNew:[],
}
async onLoad(){
let { data } = await productsCollection
.limit(limit).get();
let {data:productnew} = await productsCollection.where({
isNew:true
}).limit(limit).get();
let{data:productlike} = await productsCollection.where({
isTitle:true
}).limit(limit).get()
let{data:productrec} = await productsCollection.where({
isRec:true
}).limit(limit).get()
let {total} = await productsCollection.count()
this.setData({
productlike:productlike,
product:data,
productRec:productrec,
productNew:productnew,
total:total,
})
}
)}
首先在home页面进行初始化数据,其中limit是该小程序滑动到底部加载一次性加载多少个数组,因为我这里设置了limit=10,所以当滑动到该商品时候,若还有数据,则一次性加载10张出来。
坑点提示: 在小程序中,从数据库中取出的数据不设限制的话默认一次性拿去20条,这也是一次性拿到数据的最大值,所以我们设置limit值也只能在1-20条之间,超出也没有效果。
scroll-view中的bindscrolltoupper事件来加载更多数据
在文的坑点提示中说过,微信小程序从数据库取数据,最大值便为20条,倘若有20条以上的数据,那到底该如何加载更多的数据呢?这里就需要使用云数据库的.skip()方法来跳到你想取得数据位置。接下来就由我来带领大家来体验一下。
首先需要在上文中提到的 <swiper-item class="hx_swiper_list__item item1 ">
后面添加一个sroll-view标签,具体代码如下:
<swiper-item class="hx_swiper_list__item item1 ">
<scroll-view scroll-y="true"
bindscrolltolower="addMore_product"
lower-threshold="100">
这里代码指的是滑动到距离底部100的位置的时候,会自动触发addMore_product事件, 这个事件是加载所有商品的事件,加载上新的事件也差不多一样,只不过需要加.where来限制筛选。接下来就由我来介绍一下addMore_product事件,其他差不多的加载上新的事件欢迎大家去gitee 观看源码,在文章最后会给出链接。
data:{
page:1, //在onload里面已经预加载了page,所以从1开始
product:[], //储存获得的数据的product数组
}
async addMore(){
wx.showLoading({
title: '正在加载中....'
})
let { data } = await productsCollection
.skip(this.data.page * limit)
.limit(limit)
.get();
wx.hideLoading();
this.setData({
product:[...this.data.product,...data],
page:++this.data.page
})
if(this.data.product.length >= this.data.total){
wx.showToast({
title: '没有更多商品了....',
})
return;
}
}
首先,因为onLoad预加载了,所以page页要从1开始,然后当下滑到页面底部150距离的时候触发addMore事件,因为鸿星尔克app是为10个数据一加载,将limit限制为10,随后运用skip跳转得到下一页的数据,随后将page自增+1,这样每次就能拿到不同的数据。但是如何将这些数据扩容加入到js中data里的product数据项呢?
坑点提示:这里需要运用...张开语法,将this.data.product和新增的data数据展开,然后一起放进一个新的数组,随后将数组数据给到data中的product,从而实现将新数据放进product中,并且保存原来的product数据。
.orderby()实现价格升序排序
<view bindtap="switchProductTabprice" data-index="3"
class="products_tabar__desc
{{activeProductTabIndex == 3?\"products_tabar__desc_on\":\"\"}}">价格</view>
在这里对点击价格设立个switchProductTabprice事件,在js中代码如下:
async switchProductTabprice(e){
let index=e.currentTarget.dataset.index;
if(index == this.data.activeProductTabIndex){
return ;
}
let {data} =await productsCollection
.orderBy("price","asc")
.skip(0)
.limit(limit)
.get()
this.setData({
activeProductTabIndex:index,
pageprice:1,
product:data
})
},
}
这里需要判断,若这里的index索引就是传入的索引,即在价格页面点击价格,就直接结束。如果不是的话就在数据库搜索,.orderBy("price","asc")根据价格进行升序排序,搜索随后跟其他数据一样,进行page自增数组增加。
将在js中data里的数据,用wx:for循环展示:
<view id="changes" class="product__item"
wx:for="{{productlike}}" wx:key="index"
bindtap="goToDetail" data-item="{{item}}">
<view class="pr__hd">
<image src="{{item.pic}}" style="height:350rpx;width:350rpx;"></image>
</view>
<view class="item__des">{{item.title}}</view>
<view class="item__price">¥{{item.price}}.00</view>
</view>
例如这里这里循环productlike数据,就将products数据库中isTitle为true的都选出来。 其他的上新(isNew),推荐(isRec)都是同理,保存到js中的data数据中,并且wx:for循环出来就可以实现了。
进入详情页JS
效果展示:
进入详情页后:
此时的颜色选项是通过数据库查找得到的。下面是实现的操作。
在点击进入的页面传输id值
<view id="changes" class="product__item" wx:for="{{productlike}}"
wx:key="index" bindtap="goToDetail" data-item="{{item}}">
<view class="pr__hd">
<image src="{{item.pic}}" style="height:350rpx;width:350rpx;"/>
</view>
<view class="item__des">{{item.title}}</view>
<view class="item__price">¥{{item.price}}.00</view>
</view>
在每个wx:for循环的数据中加入一个进入详情页的点击事件,并且将循环的数据的值传给goToDetail的js中。
goToDetail(e){
if(e.currentTarget.dataset.item.isShoes){
wx.navigateTo({
url: `../detail/detail?id=${e.currentTarget.dataset.item.id}`
})
}
else{
wx.navigateTo({
url: `../detail2/detail2?id=${e.currentTarget.dataset.item.id}`,
})
}
},
这里设计了俩个detail和detail2页面,一个对应鞋子一个对应衣服,因为数据库数量较大,如果只设计一个detail的话,还得将详情页的尺码设计存入到一个数组中,跟颜色数组一样。
但是因为数据太多,所以我就将尺码设计成固定尺寸,只设计俩个detail界面,通过传入的数据是否是鞋子来判断。并且通过字符串模板../detail2/detail2?id=${e.currentTarget.dataset.item.id}
来将数据库中的数据项id跟着跳转一起传入到下个页面中。
在详情页中获取传入的id值并在products数据库查找拿到
async onLoad(options) {
// console.log(typeof options.id); String类型
const id = parseInt(options.id);
// console.log(id);
wx.setNavigationBarTitle({
title: '商品详情',
})
let{total} = await productsCollection.count()
this.setData({
total:total,
page:1,
})
let {data} = await productsCollection
.where({
id: db.command.eq(id)
})
.get();
this.setData({
productinfo:data
})
},
在detail页面中的onload预加载时,拿到传入的id值。随后用id值进行查找,这里使用了es6新出的async await异步操作,能将异步操作变为同步操作。通过.where搜索products数据库中满足id等于传入的id的值并取得。并用this.setData设置将取得的值传入detail界面js中的data中的productinfo中。
大坑提示:这里必须得注意,虽然我数据库products中设计的id值是number类型,但是通过字符串模板传入的永远都是字符串类型,如果拿着字符串类型的id,用.where查找会找不到,得到的data数据为空,所以得先用parseInt进行强制类型转化再进行比较。
最后通过得到的data中的productinfo数据,通过wx:for进行循环,虽然每个productinfo只有一个products数据库中的数据,但是能够实现数据与界面的分离,使得每个详情页都不相同。
总结
本次代码只是程序的大致框架,在本周六周日,我会继续更新鸿星尔克微信小程序关于使用云数据库完成购物车的开发,能够从详情页中将商品添加进入购物车(虽然不知道手机预览没法添加,但是在电脑调试操作是成功的)。若本篇文章对您有帮助,请继续支持我。
下面是gitee源码:gitee.com/jhqaq/hon-x… 。不过需要自行添加粘贴app.json和project.config.json并替换appid为自己的微信小程序id。
app.json:
{
"pages": [
"pages/home/home",
"pages/test/test",
"pages/sort/sort",
"pages/home1/home1",
"pages/home2/home2",
"pages/shopcar/shopcar",
"pages/my/my",
"pages/search/search",
"pages/detail/detail",
"pages/detail2/detail2"
],
"window": {
"backgroundColor": "#F6F6F6",
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#F6F6F6",
"navigationBarTitleText": "鸿星尔克官方旗舰店",
"navigationBarTextStyle": "black"
},
"tabBar": {
"selectedColor": "#fece00",
"borderStyle": "white",
"backgroundColor": "#ededed",
"list": [
{
"text": "首页",
"pagePath": "pages/home/home",
"iconPath": "assets/images/tabs/home.png",
"selectedIconPath": "assets/images/tabs/home-active.png"
},
{
"text": "分类",
"pagePath": "pages/sort/sort",
"iconPath": "assets/images/tabs/sort.png",
"selectedIconPath": "assets/images/tabs/sort-active.png"
},
{
"text": "购物车",
"pagePath": "pages/shopcar/shopcar",
"iconPath": "assets/images/tabs/shopcar.png",
"selectedIconPath": "assets/images/tabs/shopcar-active.png"
},
{
"text": "我的",
"pagePath": "pages/my/my",
"iconPath": "assets/images/tabs/my.png",
"selectedIconPath": "assets/images/tabs/my-active.png"
}
]
},
"usingComponents": {
"van-search": "./miniprogram_npm/@vant/weapp/search/index",
"van-cell": "@vant/weapp/cell/index",
"van-cell-group": "@vant/weapp/cell-group/index",
"van-grid": "@vant/weapp/grid/index",
"van-grid-item": "@vant/weapp/grid-item/index"
},
"sitemapLocation": "sitemap.json",
"style": "v2"
}
project.config.json
{
"miniprogramRoot": "miniprogram/",
"cloudfunctionRoot": "cloudfunctions/",
"setting": {
"urlCheck": true,
"es6": false,
"enhance": true,
"postcss": true,
"preloadBackgroundData": false,
"minified": true,
"newFeature": false,
"coverView": true,
"nodeModules": true,
"autoAudits": false,
"showShadowRootInWxmlPanel": true,
"scopeDataCheck": false,
"uglifyFileName": false,
"checkInvalidKey": true,
"checkSiteMap": true,
"uploadWithSourceMap": true,
"compileHotReLoad": false,
"lazyloadPlaceholderEnable": false,
"useMultiFrameRuntime": true,
"useApiHook": true,
"useApiHostProcess": true,
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
},
"enableEngineNative": false,
"useIsolateContext": true,
"userConfirmedBundleSwitch": false,
"packNpmManually": false,
"packNpmRelationList": [],
"minifyWXSS": true,
"showES6CompileOption": false
},
"projectname": "weApp",
"libVersion": "2.14.1",
"cloudfunctionTemplateRoot": "cloudfunctionTemplate",
"appid": "wxb4a42d4b7e97a817", //这里需要更换成自己的id
"condition": {
"search": {
"list": []
},
"conversation": {
"list": []
},
"plugin": {
"list": []
},
"game": {
"list": []
},
"miniprogram": {
"list": [
{
"id": -1,
"name": "db guide",
"pathName": "pages/databaseGuide/databaseGuide"
}
]
}
}
}
最后在云数据库中塞入一些数据就能看到效果了。