开学季,云开发,让我们一起在京东小程序中快乐成长

1,046 阅读19分钟

前言

hello,相信大家经过一个暑假的休息,又回到了梦开始的地方——学校,总是感触良多,想想自己又大了一岁,也要开始学习了,自己也要成为一个大佬,把暑假没学到的东西都补回来,那么刚刚好,下面是我最近学习小程序过程中自己编写的一个仿京东购物的小程序。下面是我开发的流程以及遇到的一些问题。本着学习和分享的原则,希望对您的小程序学习之旅带来一些帮助,期待和大家一起学习进步 fighting 。

开发前准备

项目展示

动图展示

下面的动图都是使用80年的诺基亚录的视频,画质不是很好,希望大家不要介意。

home.gif

静态图片展示

总体架构

该项目是基于小程序云开发设计的,在创建的时候使用的是云开发模块,基于mvvm模式设计的一个小程序,以数据驱动界面为主,进行组件化,模块化的设计,然后结合小程序自带的NoSql数据库存储数据进行系统性的开发。由于一开始对模块化的了解没有那么清晰,前面的部分数据都直接定义在界面上面,在后面进行整体修改的时候会有些没有整改到位的地方,请见谅,我会将我认为比较重要的东西作为目录结构,方便大家的查阅。

项目分析

1.对总体结构的分析

首先一开始对我要编写的页面进行pages界面目录的搭建:但是我主要编写的就是home(主界面),shop_cart(购物车界面),detail(商品详情页)

|-JINDONG_APP 项目名 
    |-cloudfunctions 云函数模块 
        |-getdatabase 自定义的一个获取数据库数据的云函数
    |-miniprogram 项目模块 
        |-components 自定义组件 
            |-Function-display 分类展示
            |-recommend-list 商品列表
        |-data 自定义数据 
            |-data.js 相关不怎么变换数据放置处
        |-images
            |-config tabBar的图片
        |-miniprogram_npm 构建有赞组件库 
        |-pages 页面 
            |-home 京东购物首页
            |-detail 商品详情页 
            |-shopcart 购物车界面 
        |-app.js 全局js 
        |-app.json 全局json配置 
        |-app.wxss 全局wxss

我对我要实现的京东小程序的界面进行下面tabBar的编写,由于这个效果小程序自带的tabBar就可以满足我的需求了,我就没有进行自定义tabBar组件了,下面是我的界面的tabBar如果有需要自定义tabBar需求的小伙伴可以参考下面的链接进行了解自定义tabBar

"tabBar": {
        "color": "#424242",
        "selectedColor": "#57be6a",
        "borderStyle": "white",
        "backgroundColor": "#f2f0f0",
        "list": [
            {
                "text": "首页",
                "pagePath": "pages/home/home",
                "iconPath": "images/config/home.png",
                "selectedIconPath": "images/config/home_act.png"
            },
            {
                "text": "分类",
                "pagePath": "pages/classify/classify",
                "iconPath": "images/config/classify.png",
                "selectedIconPath": "images/config/classify_act.png"
            },
            {
                "text": "购物车",
                "pagePath": "pages/shop_cart/shop_cart",
                "iconPath": "images/config/shop_cart.png",
                "selectedIconPath": "images/config/shop_cart_act.png"
            },
            {
                "text": "我的",
                "pagePath": "pages/my/my",
                "iconPath": "images/config/my.png",
                "selectedIconPath": "images/config/my_act.png"
            }
        ]
    },

2.数据库设计

数据库我使用的是小程序自带的云数据库,也是属于NoSql数据库,和传统的MySql数据库的区别我小小的总结了一下:

  • 1、MySQL是一个基于表格设计的关系数据库,而NoSQL本质上是非关系型的基于文档的设计。

  • 2、MySQL数据库,覆盖了巨大的IT市场;具有固定市场的MySQL数据库包含一个庞大的社区。而NoSQL数据库是最新的到来,与MySQL相比,社区正在慢慢发展。

  • 3、MySQL的严格模式限制并不容易扩展,而NoSQL可以通过动态模式特性轻松扩展。

  • 4、MySQL中创建数据库之前需要详细的数据库模型,而在NoSQL数据库类型的情况下不需要详细的建模。

  • 5、MySQL提供了大量的报告工具,可以帮助应用程序有效,而NoSQL数据库缺少用于分析和性能测试的报告工具。

  • 6、MySQL是一个关系数据库,其设计约束灵活性较低;而NoSQL本质上是非关系型的,与MySQL相比,它提供了更灵活的设计。

  • 7、MySQL中使用的标准语言是SQL;而NoSQL中缺乏标准的查询语言。

下面是我在这个小程序里面的数据库表格设计:

在这个小程序中我主要创建了两个表格,都是在excel里面写好的,然后使用 .csv 文件上传的,这里要注意在保存 .csv 文件的时候要使用 utf-8编码的csv文件格式,不然的话导入会出现乱码的现象。

shop商品表
    |-type                 类型用于不同界面的数据驱动效果
    |-S_id                 商品id,
    |-S_label              一个标志
    |-S_introduce          商品介绍
    |-S-price              商品价格
    |-S-img                商品图片
shopclassify商品类别表
    |-S_id                 商品id,和shop表进行关联
    |-C_id                 类别id,用于遍历
    |-C_img                类别图片
    |-C_number             类别数量
    |-C_price              类别价格
    |-C_classify           类别描述
shopcart购物车商品表(是根据前面两个表,在添加了一些标识组成的,是在对商品进行加入购物车的操作过后,添加到数据库里面的,为了在购物车界面进行遍历。)
    |-C_Number             商品选择的数量
    |-C_classify           商品类别
    |-C_id                 类别id
    |-C_img                图片
    |-C_introduce          介绍
    |-C_price              价格
    |-S_id                 商品id
    |-selected             就是是否在被选择状态
    

对于数据库的使用在小程序中有两中不同的使用方式:

1.直接使用以下命令的形式读取(但是要注意更改用户权限)

image.png

const db = wx.cloud.database();
// shop 是数据库集合的名字
const shopCollection = db.collection("shop")

详细的说明同样可以参考这的连接

2.使用云函数读取(比较的难说明这里我就不加以解释了有需要的小伙伴们可以去看一下下面的连接)

云函数

上面的两种方式我也简单的说一下他们的优劣

  • 直接读取的方式较为简单,但是一次只能读取20条数据,适合小数据量的读取,如果要读取更多数据,就只能多次读取。加起来还要记得在访问的使用要保证权限是所有用户可读取,所以当没拿到数据的时候不用慌张,看一下权限设置。
  • 使用云函数访问的时候,步骤比较繁琐,一次能够读取100条数据。适合多数据量的读取。加起来默认就是以管理员权限对数据访问所以就不用设置权限。

3.界面样式解析

下面就是简单的对我做的几个界面样式的介绍。

主界面样式

1.自定义顶部导航栏就是在当前界面的 .json文件里面配置 "navigationStyle":"custom",这样的话小程序自带的导航栏就被消除了。

image.png

2.vant组件的使用可以参考官网。这里我使用的是search组件 search搜索

<view class="search">
            <van-search background="" class="search__input" show-action  shape="round" placeholder="请输入搜索关键字">
            </van-search>
        </view>

下面是对vant组件的配置(这里我是在全局进行配置的)当然也可以在当前界面的 .json文件下面进行配置,但是为了复用,建议在全局当中配置:

// 在app.json里面进行的全局配置,
"usingComponents": {
        "van-search": "./miniprogram_npm/@vant/weapp/search/index",
        "van-goods-action":"./miniprogram_npm/@vant/weapp/goods-action",
        "van-goods-action-icon": "./miniprogram_npm/@vant/weapp/goods-action-icon/index",
        "van-goods-action-button": "./miniprogram_npm/@vant/weapp/goods-action-button/index",
        "van-stepper": "./miniprogram_npm/@vant/weapp/stepper/index",
        "van-checkbox-group":"./miniprogram_npm/@vant/weapp/checkbox-group/index",
        "van-checkbox":"./miniprogram_npm/@vant/weapp/checkbox/index",
        "van-popup":"./miniprogram_npm/@vant/weapp/popup/index",
        "van-cell":"./miniprogram_npm/@vant/weapp/cell/index",
        "van-swipe-cell":"./miniprogram_npm/@vant/weapp/swipe-cell/index"
    },

在微信小程序里面使用vant组件的配置可以看一下下面的链接:但是要注意的一点就是如果要在全局配置vant组件的话,一定要记得将 app.json 的"style": "v2"配置删除掉,所以当你看到组件的效果跟介绍的有点不一样,不要着急,请查看一下自己的配置文件。 vant配置地址

3.scroll view 的使用

        <view  class="navigation"  >
            <scroll-view class="navigation__src" scroll-x style="white-space:nowrap;width:100%" scroll-left="{{navScrollLeft}}" scroll-with-animation="{{true}}">
                <view class=" {{item.id == navigationindex  ? 'navigation__title':'navigation__big'}}"  wx:for="{{navigations}}" wx:key="id" scroll-x bindtap="changeType1" data-id="{{item.id}}">
                    {{item.title}}
                </view>
            </scroll-view>
        </view>

4.展开框这里我是使用一张图片的旋转来表示点击前后箭头的指向,然后利用优先级特点展开展示框,并没有使用vant提供的弹窗组件,但是在后面的使用了一下,因为一开始对组件不是很了解,加起来感觉太依赖组件就会失去自己的一点思考,所以就自己编写了,效果图在上面有,这里我也展示一下吧,下面是我的代码:

image.png

    // 我让一开始界面隐藏的通过 点击事件改变值并使用三元运算符来进行 选择器的选择
    <view class="{{logoIndex == 1 ? 'container__shadow':''}}"> 
        <view class="shadowNav__all">{{navigation2s[0]}}</view>
        <view  wx:for="{{navigation1s}}" wx:key="id" data-id="{{item.id}}" bindtap="changeType2">
            <view class="{{item.id == navigationindex ? 'shadowNav__list' : 'shadowNav__border' }}" >{{item.title}}</view>
        </view>
    </view>

详情界面样式

image.png

1.有关css里面怎么选择第一个元素(使用first-letter):

//wxml
<view class="shop__detail__price">¥{{item.S_price}}</view>
// wxss
.shop__detail__price{
    display: inline;
    color: red;
    font-size: 40rpx;
    position: absolute;
    top: 120rpx;
}
.shop__detail__price::first-letter{
    font-size: 20rpx;
    color: red;
}

2.这里我使用的是 vant组件库里面的 Popup弹出层,怎么引入vant组件的话,上面我在一开始就进行了讲解,不怎么懂的同学可以到上面看一下。下面是我的代码:

<van-popup
        show="{{ show }}"
        closeable
        position="bottom"
        custom-style="height: 80%"
        bind:close="onClose"
      >
      <view  class="shop__cartimage">
          <view class="shop__detail" wx:for="{{shopinformation}}" wx:key="S_id">
            <image class="shop__detail__image" src="{{item.S_img}}"></image>
            <view class="shop__detail__vert">
              <view class="shop__detail__price">¥{{item.S_price}}</view>
              <view class="shop__detail__level">
                <span class="shop__detail__choice">已选</span>
                <span class="shop__detail__list">{{choose}},</span>
                <span class="shop__detail__num">{{choiceNumber}}个</span>
              </view>
            </view>
          </view>
          <view class="shop__classify__color">颜色</view>
          <view class="shop__classify__desc">
            <view class="{{item.C_id == classifyid ?'shop__classify__onclick':'shop__classify__click'}}" wx:for="{{shopclassify}}" wx:key="id" data-id="{{item.C_id}}" bindtap="choiceclassify">
            {{item.C_classify}}           
            </view>
          </view>
          <view class="shop__number__list">
          <view class="shop__number__name">数量</view>
            <view class="shop__number__mod">
                <van-stepper value="{{item.C_number}}" bind:change="onChange" />
            </view>
          </view>
          
          <view class="bottom">
              <view  class="button-add" bindtap="addToCart" > 加入购物车</view>
              <view  class="button-buy" bindtap="buyImmediately" > 立即购买</view>
          </view>
      </view>
      </van-popup>

弹出层的效果我在这里展示一下:(这里面的样式我就不加一讲解了,相信大家都是大佬,这个就直接pass

image.png

3.最后底部的vant组件 GoodsAction 商品导航

// 这里的 total 就是购物车商品的数量,在下文有介绍
<van-goods-action>
  <van-goods-action-icon icon="shop-o" text="店铺" />
  <van-goods-action-icon icon="chat-o" text="客服" dot />
  <van-goods-action-icon icon="cart-o" text="购物车" info="{{total}}" bindtap="gotocart" />
  <van-goods-action-button text="加入购物车" type="warning" />
  <van-goods-action-button text="立即购买" />
</van-goods-action>

购物车界面

image.png

界面设计就不加以介绍了,相信大家都懂,就是简单的使用弹性布局ZB1$PB%IP}1K7CF}O4TO9FC.png 下面就介绍一下购物车实现,单选,多选,全选,还有删除

先描述一下界面效果的实现吧,也就是 icon:

// 都是使用icon图标自带的效果,使用 wx:if 进行判断selected 选择是否
// 单选icon
<icon wx:if="{{item.selected}}" class='cart-icon' type="success" color="red" />
<icon wx:else type="circle" class='cart-icon'  />   

// 全选icon
<icon wx:if="{{selectAllStatus}}"  type="success" color="red"  />
<icon wx:else type="circle" color="gray"  />

组件的设计

这个也是简单的使用弹性布局,然后进行遍历,因为这个在很多个界面都要使用,我将它封装成一个组件 recommend-list 的形式,方便后面界面的复用,要注意的点是在对图片进行遍历的时候记得加上 lazy-load 懒加载,会让你的界面体验感更加好,不懂的小伙们,我会在后面进行讲解,下面这个组件的部分代码,其实和普通界面编写的方式都是一样的,就是放的地方不一样,下面会有注释:

// recommend-list组件里面的 wxml 
<view class="recommend">
    <view class="recommend__title">
        <image class="recommend__title__logo" src="https://636c-cloud1-0gtw4ft6b96fe5ff-1306520646.tcb.qcloud.la/love.png?sign=e1279d83a1acb227e8b4273b22dbcc3d&t=1628339354"></image>
        <view class="recommend__title__name">为你精选</view>
    </view>
    <view class="recommend__alllist">
        <view class="recommend__list" wx:for="{{shopinformation}}" wx:key="S_id" data-id="{{item.S_id}}" >
            <image lazy-load class="recommend__list__img" src="{{item.S_img}}"></image>
            <view class="recommend__list__desc">{{item.S_introduce}}</view>
            <view class="recommend__list__price">¥{{item.S_price}}</view>
        </view>
    </view>
</view>
// recommend-list组件里面的 wxss
.recommend{
    width: 100%;
    background: #f4f4f4
}

.recommend__title{
    width: 100%;
    display: flex;
    text-align: center;
    justify-content: center;
    height: 50rpx;
    padding-bottom: 10rpx;
}

.recommend__title__logo{
    width: 50rpx;
    height: 50rpx;
}

.recommend__title__name{
    font-size: 30rpx;
    line-height: 50rpx;
}

.recommend__alllist{
    display: flex;
    flex-wrap: wrap;
    
}

.recommend__list{
    margin-left: 25rpx;
    width: 340rpx;
    height: 480rpx;
    margin-bottom: 20rpx;
    background-color: white;
}

.recommend__list__img{
    width: 340rpx;
    height: 340rpx;
    border-radius: 20rpx;
}

.recommend__list__desc{
    display: -webkit-box;
    overflow: hidden;
    -webkit-line-clamp: 2;
    text-overflow: ellipsis;
    -webkit-box-orient: vertical;
    font-size: 30rpx;
    background-color: white;
}

.recommend__list__price{
    color: red;
}
// recommend-list组件里面的 js 
Component({
    /**
     * 组件的属性列表
     */
    properties: {
        shopinformation:Array
    },

    /**
     * 组件的初始数据
     */
    data: {

    },

    /**
     * 组件的方法列表
     */
    methods: {

    }
})


// home界面的使用,还有传参,也就是在调用的时候将要传进去的数据按照shopinformation="{{shopinformation}}" 的形式传入。
<view class="modified2"  bindtap="gotodetial"> 
    <recommend-list shopinformation="{{shopinformation}}"></recommend-list>
</view>

在上面的界面中介绍了其中对商品遍历的组件(recommend-list), 然后这里还是用了一个组件(Function-display):这部分就不加以介绍了。

感觉我讲的不是很清晰的同学,可以参考以下的链接:

自定义组件

4.界面业务逻辑

下面是对界面中涉及到的业务逻辑层面做了一个简单的介绍

主界面的数据驱动界面

就是当你点击导航栏上面不同的按钮的时候,界面的数据会发生变化,使用的就是数据驱动界面的效果(中间那一部分我没有进行改变):

Screenrecorder-2021-08-30-23-08-59-469-0-14.gif

下面对于怎么实现这个效果的,重点重点

首先对于数据的设计,这个是导航栏数据设计,让每一个数据都和一个id进行匹配。然后对在界面上面要显示数据的设计(只取一条数据进行分析)

//这个是导航栏数据设计,让每一个数据都和一个id进行匹配
const navigations = [
  {
    id:"0",
    title:"首页"
  },
  {
    id:"1",
    title:"电脑办公"
  },
  {
    id:"2",
    title:"大家电"
  },
];
// 这个是要显示在不同界面的数据,这里的 主要观察的是 type 
const information = [
  {
    id:"1",
    type:"1",
    name:"显示器",
    icon:"",
  },
  {
    id:"11",
    type:"2",
    name:"空调",
    icon:""
  },
],

对点击事件的js进行编写:主要思想就是(相信以大家的水平,随便理解)

当你点击navigations导航栏上面的按钮的时候,你就会获取到按钮数据的id值,然后和information数据的type值进行匹配。只要将 type==id 的数据渲染到界面相关的位置就可以了,这里只需要将数据遍历进行匹配就可以获取到相关对应的数据。这里要注意的是,由于首页的数据和其他界面不一样,所以要进行判断,当id=0,的时候就是首页的界面,其它的 id==type 就显示出相同界面格式的不同数据。

下面的代码会有一点点乱,特别是在 this.setdata 里面,建议自动屏蔽。

//wxml 这里的 data-id="{{item.id}}",会将navigations数据的id传给changeType1这个方法。
<view 
wx:for="{{navigations}}" 
wx:key="id" 
scroll-x 
bindtap="changeType1" 
data-id="{{item.id}}">
{{item.title}}
</view>
//js
 async changeType1(e){
    var singleNavWidth = this.data.windowWidth / 7;
    // 获取到id的值
    let tid =  e.currentTarget.dataset.id;
    let {data} = await shopCollection
    .where({Type:_.eq(Number(tid))})
    .orderBy("S_price","desc")
    .get()
    this.setData({
    shopinformation:data,
    navigationindex:tid,
    Banners:0,
    categories:0,
    shoop:0,
    logo,
    navigations,
    navigations1s:"",
    navigations2s:"",
    information,
    simple1,
    simple2:0
    })
    // 对是否是首页进行判断
    if(tid ==0){
      this.setData({
      navScrollLeft: (tid-2) * singleNavWidth,
      navigationindex:tid,
      logoIndex:0,
      a:0,
      Banners,
      categories,
      shoop,
      navigation1s:"",
      navigation2s:"",
      information,
      simple1,
      simple2
      })
    }
    // 其它界面
    else{
    let lists = [];
    this.data.message.forEach(list=>{
      if(list.type == tid ){
        lists.push(list)  
      }
    })
    this.data.message.filter(list=>{
      list.type == tid
    })
    this.setData({
      navScrollLeft: (tid-2) * singleNavWidth,
      logoIndex:0,
      navigation1s:"",
      navigation2s:"",
      navigationindex:tid,
      information:lists,
      a:1,
      simple2:0,
      shoop
    })
     }
  },

主界面商品跳转到详情页面的时候,一一对应

Screenrecorder-2021-08-30-23-14-06-388-0-17.gif

就是当你主界面点击某一张图片的时候,进行页面跳转到详情页面的时候就会携带一个匹配的 id

// wxml
<view class="recommend__list" wx:for="{{shopinformation}}" wx:key="S_id" data-id="{{item.S_id}}"   bindtap="gotodetial"></view>

//js
gotodetial(event){
    let {id} = event.currentTarget.dataset;
    // 就是在这里 携带 id 过去的。
    wx.navigateTo({
      url:"../detail/detail?id="+id
    })
  },

然后详情界面进行加载的时候就可以拿到这个 id,然后和数据库数据进行匹配就可以拿到与之对应的数据了。这里要注意类型转换,用为一般获取的id是字符串类型的,可以使用 强制类型转换 ,Number ,parseInt来进行转换,如果没有的话就忽略。

async onLoad(options) {
        // 携带过来的 id 就储存在 options 里面,可以先打印options ,然后进行解析出 id,
        // 这里我直接通过 es6 的语法直接 将 id 解析出来了
        let { id } = options;
        console.log(id,"-----------");
        // 和数据库进行匹配, data 就是对应相关商品的数据。
        let {data} = await shopCollection
        .where({S_id:_.eq(Number(id))})
        .orderBy("S_price","desc")
        .get()
}

类型选择驱动界面变化

Screenrecorder-2021-08-30-23-22-12-693-0-14.gif

涉及到选择什么款式,然后驱动上面的数据绑定,还有外面数据绑定都是通过:要记住只有经过 this.setData 数据才会渲染到界面上显示效果。所以在你操作一个要改变的数据时,最后一定要进行 this.setData。

// wxml
 <van-cell title="已选  {{choose}}, {{choiceNumber}}个, 可服务"  bind:click="showPopup" />
// 这里的class 就是为了实现点击之后,变换颜色。
 <view class="{{item.C_id == classifyid ?'shop__classify__onclick':'shop__classify__click'}}" wx:for="{{shopclassify}}" wx:key="id" data-id="{{item.C_id}}" bindtap="choiceclassify">
{{item.C_classify}}           
</view>

// js  一开始就是设置默认为 C_id = 1的类型 this.data.choiceid = 1 ,在进入这个界面进行默认第一个。
onLoad(){
    this.data.shopclassify.forEach(res=>{
            if(res.C_id == this.data.choiceid){
                this.setData({
                    choose:res.C_classify
                })
                // classify.push(res)
            }
        })
}
// 然后就是在你点击那个按钮,由 id 进行匹配驱动界面发生变化。
choiceclassify(e){
        let { id } = e.currentTarget.dataset;
        console.log(id);
        this.data.shopclassify.forEach(res=>{
            console.log(res.C_id);
            if(res.C_id == id){
                this.setData({
                    classifyid:id,
                    choose:res.C_classify
                })  
            }
        })
   }

加入购物车功能实现

Screenrecorder-2021-08-30-23-25-08-734-0-14.gif

我先说一下里面相关的业务逻辑吧:将选择的商品类别信息还有数量存储到数据库当中,(这里我选择使用数据库,而没有使用小程序自带的 Storage 存储数据),因为我感觉数据库能存的数据比较多一点,还有就是顺便熟悉一下数据库的相关操作。这里要注意的就是在将选择的商品信息保存到数据库当中,一个是数据库不能保存同一条数据,所以在添加的时候就要进行判断数据库里面有没有这条数据,如果有的话就对商品的数量进行更新操作,如果没有的话就直接添加相关信息。

// 加入购物车
 async addToCart(){
        let {data} = await shopclassifyCollection
                    .where(_.and([{C_id:_.eq(this.data.classifyid)},{S_id:_.eq(Number(this.data.Sid))}]))
                    .get()
        let  classifydata =  [...data]
        let {data:data1} = await shopcartCollection
        .where(_.and([{C_id:_.eq(this.data.classifyid)},{S_id:_.eq(Number(this.data.Sid))}]))
        .get()
        // 判断数据库有没有数据
        if(data1.length==0){
            await classifydata.forEach(res=>{
            // console.log(res);
            shopcartCollection.add({
            data:{...res,C_Number:this.data.choiceNumber,selected:false,order:order,C_introduce:this.data.shopinformation[0].S_introduce}
            })
            order = order +1;
            
        })
        }else{
            let {data:data2} = await shopcartCollection
            .where({C_id:_.eq(this.data.classifyid)})
            .get()
            await data2.forEach(res=>{
                this.setData({
                    databaseNumber:res.C_Number,
                    show:false
                })
                
            })
            // 对数量进行更新,也就是购物车头上的数量
            await shopcartCollection
            .where({C_id:_.eq(this.data.classifyid)})
            .update({
                 data:{
                    C_Number:this.data.choiceNumber+this.data.databaseNumber
                 },
                 success:res=>{
                     console.log(res);
                 }
             })
        }
        // 对数据库进行计数
        let {data:data2} = await shopcartCollection.get();
        let {total:total1} = await shopcartCollection.count()
                this.setData({
                     total:total1,
                     show:false
                })
        // 点击后出现成功弹窗
        wx.showToast({
            icon:"success",
            title: '加入购物车成功',
            duration: 4000
        })

    },

对购物车商品的操作

Screenrecorder-2021-08-30-23-28-20-368-0-21.gif

下面就是对js部分,也就是业务逻辑部分的编写了。分别是选择状态的转换,以及价格的计算:

// 一开始就是初始化界面效果,以及将数据从数据库拿出来,显示在界面上。这里我将数据库的拿出来变成一个对象,方便后面的使用
async onLoad(options) {
    let {data} = await shopcartCollection.get()
    this.setData({
      shopcart:data,
      totalPrice:0
    })
    shopcart = data
    console.log(shopcart);  
  },
// 单选,多选
 selectshop(e){
    let index = e.currentTarget.dataset.index
    const selected = shopcart[index].selected;
    // 每一次点击改变状态
    shopcart[index].selected = !selected
    // 渲染数据
    this.setData({
      shopcart:shopcart
    })
    this.getTotalPrice();
  },
  // 全选
  selectAll(){
    let selectAllStatus = this.data.selectAllStatus;
    selectAllStatus = !selectAllStatus;
    for(let i=0;i<shopcart.length;i++){
      shopcart[i].selected = selectAllStatus;
    }
    this.setData({
      selectAllStatus:selectAllStatus,
      shopcart:shopcart
    });
    this.getTotalPrice();
  },
  // 都会用到的一个方法计算价格,进行相应的封装
  getTotalPrice(){
    let total = 0;
    for(let i =0;i<shopcart.length;i++){
      if(shopcart[i].selected){
        total +=shopcart[i].C_Number *shopcart[i].C_price;
      }
    }
    this.setData({
      shopcart:shopcart,
      totalPrice:total.toFixed(2)
    })
  },
  // 删除商品
  deleteShop(e){
    // console.log(e);
    let index = e.currentTarget.dataset.id;
    console.log(index);
    shopcart.splice(index,1);
    console.log(shopcart);
    this.setData({
      shopcart:shopcart
    })
  }, 

文章细节点

  • Lazy-Load,翻译过来是“懒加载”。它是针对图片加载时机的优化:在一些图片量比较大的网站(比如电商网站首页,或者团购网站、小游戏首页等),如果我们尝试在用户打开页面的时候,就把所有的图片资源加载完毕,那么很可能会造成白屏、卡顿等现象,因为图片真的太多了,一口气处理这么多任务,浏览器做不到啊!但我们再想,用户真的需要这么多图片吗?不对,用户点开页面的瞬间,呈现给他的只有屏幕的一部分(我们称之为首屏)。只要我们可以在页面打开的时候把首屏的图片资源加载出来,用户就会认为页面是没问题的。至于下面的图片,我们完全可以等用户下拉的瞬间再即时去请求、即时呈现给他。这样一来,性能的压力小了,用户的体验却没有变差——这个延迟加载的过程,就是 Lazy-Load。

  • 对于数据的设计,都会设置一个唯一的标识符,这里统一就叫做 id

  • 对于数据库的使用,很关键的一个点,就是将他看作平常化,在把数据拿到之后,其实就和你平常的数据一样处理就可以了,减少在数据库层面直接操作,会影响界面的效果展示时间。因为在数据方面的操作是异步的,这里就涉及到一个 async 和 await 的知识点了。

  1. Async—声明一个异步函数(async function someName(){...})
  • 自动将常规函数转换成Promise,返回值也是一个Promise对象
  • 只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数
  • 异步函数内部可以使用await
  1. Await—暂停异步的功能执行(var result = await someAsyncCall();)
  • 放置在Promise调用之前,await强制其他代码等待,直到Promise完成并返回结果
  • 只能与Promise一起使用,不适用与回调
  • 只能在async函数内部使用
  1. 使用小贴士:async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。

建议

  • 一开始要对自己要编写的项目进行总体结构的编写和构思
  • 如果要用到数据库的话,对表格的创建一定要想好主键
  • 在开发过程中先看一下微信小程序开发者文档找寻一些自己要使用到的相关功能介绍
  • 在编写业务逻辑过程中,多使用 console.log() ,进行相关数据的打印,这样可以更直接的看出那个地方发生错误了
  • 遇到问题的时候可以多和队友讨论一下,不要在一个问题中花费太多的时间
  • 不要计划着完美,什么东西都会有不足之处,小程序也是如此
  • 先实现效果,然后在进行完善,这样你的积极性会更好,也会然你的界面更接近完美

源码

项目gitee源码地址

总结

这是第一次进行自己一个人进行一个小型项目的编写,也是在学习当中一步步推进的,很感谢在这过程中同学和老师给的帮助。文章讲解有什么不好的地方,还有里面有什么逻辑没有编写的很好,还有可以完善的地方,等等。在这当中的不足之处请见谅,当然我也希望您能指出我的不足的地方。同时希望我的文章能给你带来一些帮助,如果你感觉我文章写得还可以的话,或者对您有一些帮助的话,就麻烦您动一下手指帮我点个赞吧 谢谢。最后期待和你一起学习进步。

感谢你的支持