京东优选小程序的实现代码示例


Posted in Javascript onFebruary 25, 2020

写在前面

过年有大把的时光,为何一直宅在家里不出家门看着电脑,这究竟是道德的沦丧还是人性的泯灭...这一切都还得从一只蝙蝠说起...

咳咳,好了不皮了,言归正传。微信推出的小程序可谓是轻量又强大,所以最近我也开始了小程序的学习,学了挺多也看了很多文档,但总觉得自己没学到什么,感觉很迷茫。正所谓实践出真知,所以我选择了从高仿别人的小程序开始,选来选去最后选择了京东优选这个小程序(绝对不是因为它的界面清爽!)。

开发工具

微信开发者工具

VS Code

效果速览

废话不多说,咱先来搞一波图片看看,点这里查看更多图片

京东优选小程序的实现代码示例

京东优选小程序的实现代码示例

项目结构

这个项目我使用的是普通的开发,把所有的数据都放在了json-server中模拟。
可能很多人会觉得很奇怪,但这是因为我发现easy mock的网站经常打不开请求失败非常的不方便,所以我暂时没有选择mock数据,后期有时间我会把数据挪到easy mock上。

|-jd_recommend 项目名
  |-api 模拟数据接口
    |-db.json 模拟的数据
  |-assets 资源文件
    |-icons  图标资源
    |-images 图片资源
  |-components 组件模块
    |-navigationBar 自定义导航栏
    |-toast     自定义toast
    |-stepper    有赞vant步进器组件
    |-...      其他小程序所需组件
  |-pages 项目页面
    |-about     关于页面
    |-account    我的订单页面
    |-afterMarket  售后类型页面
    |-appointment  我的预约页面
    |-buy      填写订单信息页面
    |-commentDetail 评论详情页面
    |-discount    优惠券页面
    |-explore    发现页面
    |-feedback    反馈页面
    |-fix      售后页面
    |-goodsDetail  值得买优惠详情页面
    |-index     首页
    |-jd       京东商品详情页面
    |-login     登录页面
    |-orderDetail  订单详情页面
    |-seller     客服页面
    |-service    退换/售后页面
    |-shopCart    购物车页面
    |-user      个人中心页面
  |-style 公共样式
    |-comment.wxss  评论区样式
    |-goodsCard.wxss 商品卡片样式
    |-nav.wxss    导航栏样式
    |-orderCard.wxss 订单卡片样式
    |-popright.wxss  筛选框样式
    |-popup.wxss   上拉菜单样式
  |-utils 公共模块
    |-util.js promise封装接口
  app.js     全局js
  app.json    全局json配置
  app.wxss    全局wxss

自定义组件

大部分人写小程序肯定要涉及修改navigationBar的title,微信小程序开发内置了这个组件,可以直接在app.json中配置。但是,自带的navigationBar的样子是固定的,你肯定见过长成下面这样的navigationBar:

京东优选小程序的实现代码示例

相比平时常见的navigationBar,它左上角多了一个返回主页的按钮,这对于有多级页面的小程序来说是非常必要的,不然访问的层级太深用户不知道怎么返回主页。然而,小程序开发自带并没有这个样子的,好在可以自定义,接下来我们就来自定义一个。

navigationBar

首先,我们构建一下页面的结构:

<!-- components/navigationBar/index.wxml -->
<view class='nav-wrap' style='height: {{height*2 + 20}}px;'>
  <!-- 导航栏 中间的标题 -->
  <view class='nav-title' style='line-height: {{height*2 + 44}}px;'>{{navbarData.title}}</view>
  <view style='display: flex; justify-content: space-around;flex-direction: column'>
    <!-- 导航栏 左上角的返回按钮和home按钮 -->
    <!-- 其中wx:if='{{navbarData.showCapsule}}' 是控制左上角按钮的显示隐藏,首页不显示 -->
    <view class='nav-capsule' style='height: {{height*2 + 44}}px;' wx:if='{{navbarData.showCapsule}}'>
      <!-- 左上角的返回按钮,wx:if='{{!share}}'空制返回按钮显示 -->
      <view bindtap='_navback'>
        <image src='../../assets/icons/back.png' mode='aspectFill' class='back-pre'></image>
      </view>
      <view class='navbar-v-line' wx:if='{{!share}}'></view>
      <view bindtap='_backhome'>
        <image src='../../assets/icons/back_home.png' mode='aspectFill' class='back-home'></image>
      </view>
    </view>
  </view>
</view>

这就是一个很普通的页面结构,值得注意的是,它的高度是根据获取的设备的高度来确定的。
接下来又到了切图仔上线的时候了(误):

/* components/navigationBar/index.wxss */
/* 顶部要固定定位  标题要居中  自定义按钮和标题要和右边微信原生的胶囊上下对齐 */
.nav-wrap {
  position: fixed;
  width: 100%;
  top: 0;
  background: #fff;
  color: #000;
  z-index: 9999999;
  border-bottom: 1rpx solid #EFEFF4;
}
/* 标题要居中 */
.nav-title {
  position: absolute;
  text-align: center;
  max-width: 400rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: auto;
  font-size: 36rpx;
  color: #2c2b2b;
  /* font-weight: 600; */
}
.nav-capsule {
  display: flex;
  align-items: center;
  margin-left: 30rpx;
  width: 140rpx;
  justify-content: space-between;
  height: 100%;
}
.navbar-v-line {
  width: 1px;
  height: 32rpx;
  background-color: #e5e5e5;
}
.back-pre, .back-home {
  width: 32rpx;
  height: 36rpx;
  margin-top: 4rpx;
  padding: 10rpx;
}
.nav-capsule .back-home {
  width: 36rpx;
  height: 40rpx;
  margin-top: 3rpx;
}
// components/navigationBar/index.js
const app = getApp()
Component({
 properties: {
  navbarData: {  //navbarData  由父页面传递的数据,变量名字自命名
   type: Object,
   value: {},
   // observer: function (newVal, oldVal) { }
  }
 },
 data: {
  height: '',
  //默认值 默认显示左上角
  navbarData: {
   showCapsule: 1
  }
 },
 attached: function () {
  // 定义导航栏的高度  方便对齐
  this.setData({
   height: app.globalData.height
  })
 },
 methods: {
  // 返回上一页面
  _navback() {
   wx.navigateBack()
  },
  //返回到首页
  _backhome() {
   wx.switchTab({
    url: '/pages/index/index',
   })
  }
 }
})

京东优选小程序这里的两个按钮都是返回首页,我在开发的时候觉得不对劲,所以我改过来了。
在这里还去取了一下全局定义的变量,也就是获取的设备顶部窗口的高度(不同设备窗口高度不一样,根据这个来设置自定义导航栏的高度),在app.js中要定义一下:

app.js
App({
 onLaunch: function () {
  ......
  wx.getSystemInfo({
   success: (res) => {
    this.globalData.height = res.statusBarHeight
   }
  })
 },
 globalData: {
  ...
  height: 0
 }
})

记得自定组件的时候一定要在json中写成自定义组件

// components/navigationBar/index.json
{
 "component": true
}

接下来就是调用该组件了

<navigationBar navbar-data='{{navbarData}}'></navigationBar>

别忘了在要引用页面的json中引入该组件

"usingComponents": {
  "navigationBar": "../../components/navigationBar/index"
 }

Toast

Toast同样也是小程序开发已经做好给你用的了,虽然它可以支持替换里面的图标,但是你会发现很鸡肋的一点是,如果你想显示两行文字你就没办法做到了。我在开发过程中也搜索过相关的实现方法,找到了大部分是说在要换行的文字后背加上rn就能实现了,但是我自己亲测无效,所以实在忍不住也自己做了一个。

京东优选小程序的实现代码示例

<!-- components/toast/index.wxml -->
<!-- 距离顶部高度由业务需要动态确定 -->
<view class='mask' hidden="{{hide}}" style='top: {{toastData.top}}'>
  <image class="image" src='../../assets/icons/{{toastData.icon}}.png' mode='aspectFit'></image>
  <view class="info">
    <view class='info1' wx:if="{{toastData.info1 != ''}}">{{toastData.info1}}</view>
    <view class="info2" wx:if="{{toastData.info2 != ''}}">{{toastData.info2}}</view>
  </view>
</view>
/* components/toast/index.wxss */
.mask {
  width: 440rpx;
  height: auto;
  border-radius: 20rpx;
  position: fixed;
  left: 155rpx;
  z-index: 1000;
  background: rgba(0, 0, 0, 0.6);
  text-align: center;
  padding-bottom: 30rpx;
}
.image {
  z-index: 1000;
  width: 80rpx;
  height: 80rpx;
  padding-top: 30rpx;
  padding-bottom: 20rpx;
}
.info1, .info2 {
  color: #ffffff;
  font-size: 32rpx;
}
.info {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
// components/toast/index.js
Component({
 properties: {       //定义组件属性
  toastData: {      //用来显示提示信息
   type: Object,     // 类型(必填),目前接受的类型包括:String, Number, Boolean, Object, Array, null(表示任意类型)
   value: {
    icon: 'success'
   }   // 属性初始值(可选),如果未指定则会根据类型选择一个
  },
 },
 data: {
  hide: true
 },
 methods: {
  showToast: function () {
   let that = this;
   that.setData({
    hide: false
   });
  },
  hideToast: function (e) {
   let that = this;
   setTimeout(function () {
    that.setData({
     hide: true
    });
   }, 2000);
  }
 }
})

这里给组件定义了两个方法,是用来显示和隐藏Toast的。这里要注意一下,调用给自定义组件定义方法要先在页面上获取该组件

<toast id="toast" toast-data="{{toastData}}"></toast>
Page({
  data: {
  toastData: { // toast需要的参数
   icon: "success",
   info1: "加入购物车成功",
   top: "50%"
  }
 },
 onReady() {
  this.toast = this.selectComponent("#toast");
 }
})

然后在需要触发Toast的事件中写上这两句:

this.toast.showToast()
this.toast.hideToast()

功能实现

导航

所谓导航,也是很常见了,就是根据选择栏目的不同,显示不同的类别内容。例如:

京东优选小程序的实现代码示例

功能要求:

  1. 点击导航栏目,显示对应的栏目数据。
  2. 如果栏目中没有东西,要显示对应的提示信息。

实现它的功能并不难,直接sroll-view往上怼。个人觉得,京东优选在这里有一点不足的地方就是,如果点击了偏右侧的导航栏目的话,导航条不会跟着右移显示后面的项目,可能它的开发者有不一样的想法吧。

<view class="navigator">
 <scroll-view scroll-x="true" class="nav" scroll-left="{{navScrollLeft}}" scroll-with-animation="{{true}}">
  <block wx:for="{{navData}}" wx:for-index="id" wx:for-item="navItem" wx:key="id">
   <view class="nav-item {{currentTab == id?'active':''}}" data-name="{{navItem.name}}" data-current="{{id}}" bindtap="switchNav">
    {{navItem.name}}
   </view>
  </block>
 </scroll-view>
</view>

通过js可以实现动态的填放数据,这里设置的current就是当前选择的栏目,可以根据这个改变样式等。

switchNav(e) {
    const cur = e.currentTarget.dataset.current; // Number
    let currData = []
    // console.log(cur.toString());
    if (cur === 0) {
      currData = this.data.goods
    } else {
      this.data.goods.forEach(val => {
        if (val.category === cur.toString()) {
          currData.push(val)
        }
      })
    }
    this.setData({
      currentTab: cur,
      category: cur,
      currData
    });
}

如果是要实现点击之后自动向点击的方法滑出显示更多的内容,可以通过动态改变navScrollLeft的值去实现,这里我就不细说了,不过我在实现的时候还是花了一番功夫,实现的不是很好所以就没有放在代码里,如果你以后想做出这种效果的导航栏建议去网上搜一搜demo看懂了之后借过来用一用,毕竟传说程序猿最高的境界是复制粘贴,狗头(误)

上拉菜单和筛选框

这两个比较相似,只是拉出的位置不一样,这里我就举一个筛选框的例子,我们先看看它长啥样:

京东优选小程序的实现代码示例

我们先看看结构,这里我省略了中间的一些内容:

<!-- 点击筛选弹出的选择菜单 -->
<view class="float {{isRuleTrue?'isRuleShow':'isRuleHide'}}">
  <view class="animation-element" animation="{{animation}}">
    ...中间自己放的具体内容...
    <!-- 底部的两个按钮 -->
    <view class='bottom'>
      <view class="animation-reset" bindtap="reset">重置</view>
      <view class="animation-button" bindtap="success">确定</view>
    </view>
  </view>
</view>
/* 筛选弹框 */
/* 弹框的布局 */
.isRuleShow {
  display: block;
}
.isRuleHide {
  display: none;
}
.float {
  height: 100%;
  width: 100%;
  position: fixed;
  z-index: 999;
  top: 0;
  left: 0;
  /* 弹出后背景的颜色 */
  background-color: rgba(0, 0, 0, 0.5);
  padding-left: 30rpx;
  padding-left: 30rpx;
  /* margin-top:80rpx; */
}
.animation-element {
  width: 600rpx;
  height: 100%;
  padding-left: 30rpx;
  padding-right: 30rpx;
  background-color: #ffffff;
  border: 1px solid #f3f0f0;
  position: absolute;
  right: -550rpx;
  box-sizing: border-box;
}
.bottom {
  width: 600rpx;
  height: 110rpx;
  font-size: 32rpx;
  padding-top: 55rpx;
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  display: flex;
}
.animation-reset {
  width: 50%;
  height: 100%;
  line-height: 50%;
  text-align: center;
  padding-top: 55rpx;
  border-top: 1px solid #EFEFF4;
}
.animation-button {
  width: 50%;
  height: 100%;
  line-height: 50%;
  color: #fff;
  text-align: center;
  background-color: #ED7358;
  padding-top: 55rpx;
}

重点是它的显示和隐藏事件,需要用到animation,如果有不熟悉animation,可以去参考一些资料,或者是官方文档。同样,我也去掉了我实现其他业务的一些内容。

showSelect() { // 显示选择菜单
  this.setData({
   isRuleTrue: true
  })
  // 左偏移245 step表示一个动作的开始
  this.animation.translate(-245, 0).step()
  this.setData({ animation: this.animation.export() })
 },
 success: function () { // 关闭选择菜单
  this.setData({
   isRuleTrue: false,
   selected: true
  })
  this.animation.translate(0, 0).step()
  this.setData({ animation: this.animation.export() })
 },

购物车逻辑

京东优选小程序的实现代码示例

要实现这样的效果并不困难,需要自己思路清晰,不能被绕进去了。实现加入购物车并不难,细节是购物车图标右上角的数字要根据加入购物车的数量进行动态的改变,还要注意如果是同一件商品就不需要添加新的,只需要修改原来的数量。

在这里我使用的是小程序的wx.setStorage()实现的:

<view class='bottom'>
  <view class="animation-reset" bindtap="addCart">加入购物车</view>
  <view class="animation-button" bindtap="buy">立即购买</view>
</view>
addCart() { // 加入购物车
  this.setData({
   toastData: { // toast需要的参数
    icon: "success",
    info1: "加入购物车成功",
    top: "50%"
   }
  })
  this.toast.showToast()
  this.toast.hideToast()
  this.hideModal()
  // 真正实现添加购物车的部分
  let cartData = wx.getStorageSync('cart') || [];
  let count = 0
  cartData.map(val => {
   if (val.title === this.data.currData[0].title && val.type === this.data.choose_value) {
    val.num += this.data.num
    count++ // 标记是否有找到相同的商品
   }
  })
  if (count === 0) { // 没找到 添加新的商品信息进购物车
   let data = {
    id: this.data.currData[0]._id,
    title: this.data.currData[0].title,
    weight: "0.78kg",
    type: this.data.choose_value,
    num: this.data.num,
    price: this.data.currData[0].plain_price,
    img: this.data.currData[0].thumb,
    discount: 20,
    select: true // 是否选中,方便后续计算总价
   }
   cartData.push(data)
  }
  // 刷新购物车图标上的数量
  let allNum = 0
  cartData.forEach(val => {
   allNum += val.num
  });
  this.setData({
   allNum
  })
  wx.setStorage({
   key: 'cart',
   data: cartData
  })
 },

这里你可以根据自己的开发来决定方式,如果你使用的是云开发的话,可以选择把数据存进云数据库里。

回到顶部

京东优选小程序的实现代码示例

这也是一个老生常谈的功能,当你滑到页面比较后的位置的时候需要快速回顶。这里要记住,用swiper实现。首先是在页面上撸一个回到顶部的图标出来:

<!-- 滑动一段距离后显示返回顶部的按钮 -->
<scroll-view class="bigWrap" 
  scroll-y="true" 
  scroll-top="{{scrollTop}}" 
  bindscroll="scroll" 
  style="position: absolute; left: 0; top:0; bottom: 0; right: -999rpx;">
<view class="goTop" bindtap="goTop" wx:if="{{&& floorstatus}}">
  <image class="icon_goTop" src="../../assets/icons/back_to_top.png"></image>
</view>
</scroll-view>

{{scrollTop}}用来表示滑动的时候距离顶部的位置。它的样式也很简单,使用固定定位把它定在屏幕上,这里一定要注意页面的层级,不然它可能会被其他组件给遮挡掉!

/* 回到顶部 */
.goTop {
  position: fixed;
  bottom: 200rpx;
  right: 20rpx;
  width: 65rpx;
  height: 65rpx;
  border: 1px solid #DDDDDD;
  border-radius: 50%;
  background-color: #fff;
  text-align: center;
}
.icon_goTop {
  width: 40rpx;
  height: 40rpx;
  padding-top: 12rpx;
  padding-left: 2rpx;
}
goTop(e) { // 回到顶部
  this.setData({
   scrollTop: 0
  })
 }

你肯定也注意到了,当滑到了一定距离的时候它才显示出来,这就要靠swiper绑定的滚动事件了:

scroll(e) { // 滚动事件
  // 容器滚动时将此时的滚动距离赋值给 this.data.scrollTop
  let floorstatus = false
  if (e.detail.scrollTop > 300) {
   floorstatus = true
  }
  this.setData({
   floorstatus
  })
}

功能大致先说这么一点,可能在大牛看起来都是些很容易不起眼的功能,但是对应我这个初学者来说还是有点困难的,希望如果有大牛看了我的一些功能的实现之后我不会被骂死。

值得注意的一点

做过小程序开发或者是vue等开发的人一定听过事件冒泡这个名词:子元素的事件触发了父元素的事件,例如点击事件。我就是那个幸运鹅,我在开发的时候就遇到了这个情况。
在购物车中点击商品可以跳转商品详情,但是我一开始把跳转事件绑定在了每个商品卡片上,这样就导致了点击修改商品数量的时候修改了数字但是也会直接跳转商品详情,比如下面这样...

京东优选小程序的实现代码示例

这就很不友好了,用户体验很差,关于事件冒泡,微信小程序的解决方法是把bindtap替换成catchtap,这样可以阻止子元素事件向上冒泡。

然而巧的是,我就是那个最幸运的鹅,步进器我用的是有赞Vant Weapp组件库里的,我搜索了很多资料都没有找到有效的解决方案,差点就放弃使用组件库了,好在最后发现京东优选小程序购物车绑定的跳转事件是在商品的图片和标题上。

这一点还是比较重要的,所以大家在开发的时候一定要考虑事件的冒泡,这也是我把它放在最后来写的原因。

写在后面

最后,我想说的是小程序开发真的不容易,开发一个好的小程序更是需要考虑性能和用户体验的方方面面。当我觉得自己第一个小程序差不多要完工的时候真的要跳起来唱joyful了(误)。作为一个程序猿真的不容易,难怪是个容易掉发的群体。但好在愿意分享技术的人很多,在这次开发的过程中我也查阅了很多的资料、社区和文档。小程序的学习我也不会停下脚步,这个项目还有非常多做的不好的地方,我发出来也是希望大家和我进行交流分享,后期我也会继续完善优化这个小程序项目。希望我的作品可以对那些初学小程序的人有所帮助。

最后附上我的github项目地址:https://github.com/tearill/jd_recommend
如果你觉得这个项目还不错或者是对你有所帮助的话欢迎star,你点亮的每一个star将都是我前进的动力!

到此这篇关于京东优选小程序的实现代码示例的文章就介绍到这了,更多相关京东优选小程序内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
发布一个基于javascript的动画类 Fx.js
Nov 05 Javascript
表单类各种类型(文本框)失去焦点效果jquery代码
Apr 26 Javascript
把input初始值不写value的具体实现方法
Jul 04 Javascript
如何让DIV可编辑、可拖动示例代码
Sep 18 Javascript
jQuery禁用键盘后退屏蔽F5刷新及禁用右键单击
Jan 22 Javascript
20分钟成功编写bootstrap响应式页面 就这么简单
May 12 Javascript
省市二级联动小案例讲解
Jul 24 Javascript
Vue.js系列之vue-router(上)(3)
Jan 03 Javascript
详解ElementUI之表单验证、数据绑定、路由跳转
Jun 21 Javascript
小程序视频列表中视频的播放与停止的示例代码
Jul 20 Javascript
详解javascript 变量提升(Hoisting)
Mar 12 Javascript
JS使用iView的Dropdown实现一个右键菜单
May 06 Javascript
手把手带你入门微信小程序新框架Kbone的使用
Feb 25 #Javascript
vue 路由守卫(导航守卫)及其具体使用
Feb 25 #Javascript
Javascript地址引用代码实例解析
Feb 25 #Javascript
Javascript如何实现双指控制图片功能
Feb 25 #Javascript
vue-video-player 解决微信自动全屏播放问题(横竖屏导致样式错乱问题)
Feb 25 #Javascript
通过js实现压缩图片上传功能
Feb 25 #Javascript
Vue实现简单计算器案例
Feb 25 #Javascript
You might like
编译问题
2006/10/09 PHP
php更改目录及子目录下所有的文件后缀的代码
2010/09/24 PHP
PHP中抽象类,接口功能、定义方法示例
2019/02/26 PHP
laravel框架邮箱认证实现方法详解
2019/11/22 PHP
PHP如何解决微信文章图片防盗链
2020/12/09 PHP
控制打印时页眉角的代码
2007/02/08 Javascript
javascript中input中readonly和disabled区别介绍
2012/10/23 Javascript
JavaScript判断textarea值是否为空并给出相应提示
2014/09/04 Javascript
jQuery超精致图片轮播幻灯片特效代码分享
2015/09/10 Javascript
深入分析javascript中的错误处理机制
2016/07/17 Javascript
js实现拖拽功能
2017/03/01 Javascript
VUE开发一个图片轮播的组件示例代码
2017/03/06 Javascript
javascript 动态生成css代码的两种方法
2017/03/17 Javascript
AngularJS 表单验证手机号的实例(非必填)
2017/11/12 Javascript
解决jquery的ajax调取后端数据成功却渲染失败的问题
2018/08/08 jQuery
VsCode与Node.js知识点详解
2019/09/05 Javascript
Vue插件之滑动验证码
2019/09/21 Javascript
vue项目查看vue版本及cli版本的实现方式
2020/10/24 Javascript
[09:37]DOTA2卡尔工作室 英雄介绍圣堂刺客篇
2013/06/13 DOTA
python实现在目录中查找指定文件的方法
2014/11/11 Python
Mac下Anaconda的安装和使用教程
2018/11/29 Python
python如何实现代码检查
2019/06/28 Python
python3中关于excel追加写入格式被覆盖问题(实例代码)
2020/01/10 Python
完美解决pycharm导入自己写的py文件爆红问题
2020/02/12 Python
PyQt5+python3+pycharm开发环境配置教程
2020/03/24 Python
Python常驻任务实现接收外界参数代码解析
2020/07/21 Python
java关于string最常出现的面试题整理
2021/01/18 Python
马德里竞技官方网上商店:Atletico Madrid Shop
2019/03/31 全球购物
法国包包和行李箱销售网站:Bagage24.fr
2020/03/24 全球购物
什么是GWT的Module
2013/01/20 面试题
护理毕业生自我鉴定
2014/02/11 职场文书
小学班主任评语
2014/12/29 职场文书
餐厅保洁员岗位职责
2015/04/10 职场文书
储备店长岗位职责
2015/04/14 职场文书
导游词之京东大峡谷旅游区
2019/10/29 职场文书
vue-cli3.x配置全局的scss的时候报错问题及解决
2022/04/30 Vue.js