仿小米Lite微信小程序

5,956 阅读15分钟

微信小程序-小米Lite

事先声明,这是一个高仿小米Lite的微信小程序。

我呢现在是一个大三快大四的学生,这个小程序花了我很长时间,把能写的功能基本上都写了。我秉着分享开源的心理,尽量把我写的这个小程序怎么写的,为什么这样写,详细的告诉大家。为什么是尽量?这是因为,我不太会说,可能说的不是很清楚,所以只能尽量.

项目预览

ok实现的效果就是这样。

使用的工具

  1. easy-moke
  2. VSCode
  3. 微信小程序开发者工具
  4. (阿里巴巴矢量图标库)

文件目录

├<assets>
│  ├<images>
├<components>
│  ├<goodList>
│  ├<icon>
│  ├<tabbar>
│  ├<userList>
├<pages>
│  ├<cart>
│  ├<category>
│  ├<deleteGoods>
│  ├<find>
│  ├<goodDetails>
│  ├<index>
│  ├<selectGoods>
│  ├<user>
├<utils>
│  └util.js
├<weui>
│  └weui.wxss
├<wxapi>
│  ├Api.js
│  ├main.js
│  └mock.js


对于初学者来说,可能拿到设计图就立马写,其实这样很不好,写出来的代码会有很多重复的代码,这样不利于之后的维护。所以应该把一些公用的代码封装,之后直接调用就行了,之后维护起来也更加的方便。

API封装

我们前端想要获取页面的数据,就需要发送HTTP请求后端提供给我们的API接口,从API接口中获取我们想要的数据。在微信小程序中,微信官方给我们提供了一个方法wx.request来请求.

一个程序,需要的HTTP请求会很多,如果我们每个请求都去写一个wx.request,这样写出来的代码,看起来会很冗长,他人看我们的代码时也会很累,也不利于我们之后的修改。因此为了代码的整洁,和之后的修改方便。我就把所有的API请求请求封装在wxapi文件目录下。

// Api.js
const banners = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/banners'

const navdata = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/navdata'

const goodList = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/goodList'

const category = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/category'

const findData = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/findData'

const userData = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/userData'

const goodDetail = 'https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/goodDetail'

const QQ_MAP_KEY = 'NNFBZ-6DRCP-IRLDU-VEQ4F-TXLP2-PFBEN'

const MAPURL = 'https://apis.map.qq.com/ws/geocoder/v1/'

module.exports = {
  banners,
  navdata,
  goodList,
  category,
  findData,
  userData,
  goodDetail,
  QQ_MAP_KEY,
  MAPURL
}
import * as MOCK from "./mock"
import * as API from "./Api"
const request = (url,mock = true,data) => {
  let _url = url
  return new Promise((resolve, reject) => {
    if (mock) {
      let res = {
        statusCode: 200,
        data: MOCK[_url]
      }
      if (res && res.statusCode === 200 && res.data) {
        resolve(res.data)
      } else {
        reject(res)
      }
    } else {
      wx.request({
        url: url,
        data,
        success(request) {
          resolve(request.data)
        },
        fail(error) {
          reject(error)
        }
      })
    }
  });
}
// showLoading
const showLoading = () => {
  wx.showLoading({
    title: '数据加载中',
    mask: true,
  });  
}
// 获取地理位置
const geocoder = (lat, lon) => {
  return request(API.MAPURL,false,{
    location: `${lat},${lon}`,
    key: API.QQ_MAP_KEY,
    get_poi: 0
  })
}
module.exports = {
  getBanners: () => {
    // return request('banners')
    return request(API.banners,false)  //首页 banners 
  },
  getNavData: () => {
    // return request('navdata')
    return request(API.navdata,false) //首页 navdata 
  },
  getGoodList: () => {
    // return request('goodList')
    return request(API.goodList,false)  //首页 商品列表
  },
  getCategroy: () => {
    // return request('category')
    return request(API.category,false)  //分类页面
  },
  getFindData: () => {
    // return request('findData')
    return request(API.findData,false)  //发现 页面
  },
  getUserData: () => {
    // return request('userData')
    return request(API.userData,false)  // 我的 页面
  },
  getGoodDetail: () => {
    // return request('goodDetail')
    return request(API.goodDetail,false)  //商品详情
  },
  showLoading,
  geocoder
}

看到这里,可能就会有一些疑问,为什么我在每个请求后面都加上了一个false?

这是因为,我在写这个微信小程序开始时,没有使用easy-mock来模拟http请求数据。我是把假数据都放在mock.js文件中。然后使用 **return request('banners')**这种方式就可以获取我想要的数据。

API封装完了,该怎么调用呢?我就以首页的banners数据为例

// index.js 
const WXAPI = require('../../wxapi/main')

onLoad: function (options) {
    WXAPI.showLoading()
    this.getBanners()
},
getBanners() {
    WXAPI
    .getBanners()
    .then(res => {
      wx.hideLoading()
      this.setData({
        banners: res.data
      })
    })
},

记住,如果想要发送HTTP请求数据的页面,都必须加上这一句const WXAPI = require('../../wxapi/main')


定义的组件

开始准备OK,现在开始写页面。第一步要写的是tabBar部分。

tabBar组件

看起来是不是有点奇怪,为什么有点透明的感觉?因为这个tabBar组件是我自己写的。

一般来将,直接在把tabBar组件写在app.json中,就可以了。

但是我觉得不是那么好看,所以就自己撸了一个tabBar组件出来。

写完之后查文档才发现,微信小程序官方提供了自定义tabBar组件的方法,有需要的可以查看微信小程序文档

写完这个组件后我总解了一下,需要注意的问题.

  • 选中了的当前页面,再次点击因该无效。

    所以我在app.js中存入了一个page属性,来存储当前页面,然后在点击事件goToPage()方法中加入判断去解决。

<!--components/tabbar/tabbar.wxml-->
<view class="tabbar">
  <!-- 首页 -->
  <view class="shouye {{on === 'index' ? 'on': ''}}" data-page="index" bindtap="goToPage">
    <icon type="shouye" size="42" class="icon" color="{{on === 'index' ? '#f96702': ''}}"/>
    <text >首页</text>
  </view>
  <!-- 分类 -->
  <view class="fenlei {{on === 'category' ? 'on': ''}}" data-page="category" bindtap="goToPage">
    <icon type="classify" size="42" class="icon" color="{{on === 'category' ? '#f96702': ''}}"/>
    <text >分类</text>
  </view>
  <!-- 发现 -->
  <view class="faxian {{on === 'find' ? 'on': ''}}" data-page="find" bindtap="goToPage">
    <icon type="faxian" size="42" class="icon" color="{{on === 'find' ? '#f96702': ''}}"/>
    <text >发现</text>
  </view>
  <!-- 购物车 -->
  <view class="gouwuche {{on === 'cart' ? 'on': ''}}" data-page="cart" bindtap="goToPage">
    <icon type="gouwuche" size="42" class="icon" color="{{on === 'cart' ? '#f96702': ''}}"/>
    <text >购物车</text>
  </view>
  <!-- 我的 -->
  <view class="wode {{on === 'user' ? 'on': ''}}" data-page="user" bindtap="goToPage">
    <icon type="wode" size="42" class="icon" color="{{on === 'user' ? '#f96702': ''}}"/>
    <text >我的</text>
  </view>
</view>
// components/tabbar/tabbar.js
// 全局里面存了一个page 表示当前 路由
const app =  getApp();
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    // 是否选中
    on:{
      type: String,
      value: ''
    }
  },

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

  /**
   * 组件的方法列表
   */
  methods: {
    // 跳转到相应的页面
    // 加了一个判断
    // 因为如果现在显示的是当前页面就不需要再跳转
    goToPage(e) {
      let page = e.currentTarget.dataset.page || 'user';
      if(app.globalData.page === page) {
        return ;
      }
      wx.redirectTo({
        url: `/pages/${page}/${page}`,
      });
      app.globalData.page = page;
    }
  }
})
/* components/tabbar/tabbar.wxss */
.tabbar {
  width: 100%;
  height: 100rpx;
  background-color: #ffffff;
  display: flex;
  position: fixed;
  bottom: 0;
  font-size: 26rpx;
  z-index: 99;
}
.shouye,.fenlei,.faxian,.gouwuche,.wode {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: center;
  opacity: 0.5;
}
.icon {
  height: 60rpx;
}
.on {
  color:#f96702;
}

关于如何使用的问题,只需要在,需要tabBar功能的页面底部加上就可以.

比如index页面

<tabbar on="index"></tabbar>

记得在json中引入组件

{
  "usingComponents": {
    "goodList": "../../components/goodList/goodList",
    "tabbar": "../../components/tabbar/tabbar"
  }
}

icon组件

tabBar组件中需要图标,我使用的是阿里的图标库

如何使用?

  1. 先去图标库中找到你所需要的图标,加入购物车。
  2. 然后添加至项目,自己新建一个就可以了。
  3. 在然后下载之本地,把其中的CSS文件里面所有的内容都复制到wxss文件中就行了.

这个组件很简单,定义出来就可以直接使用,并且修改颜色和大小.

<!--components/icon/icon.wxml-->
<text class='iconfont icon-{{type}}' style='color:{{color}}; font-size:{{size}}rpx;'></text>
properties: {
    type: {
      type: String,
      value: ''
    },
    color: {
      type: String,
      value: '#000000'
    },
    size: {
      type: Number,
      value: '45'
    }
  },

goodList组件

在首页中存在很多这样的商品列表,一个一个写,这样写出来的代码会导致首页的代码显得很多,并且不好维护,所以我就封装成了一个组件.

<!--components/goodList/goodList.wxml-->
<view class="goodList-good">
  <!-- 商品的图片 -->
  <view class="goodList-good-img">
    <image src="{{url}}" mode="aspectFill" />
  </view>
  <!-- 商品详细的信息 -->
  <view class="goodList-good_detail">
    <!-- 名称 -->
    <view class="good_detail_name">
      {{name}}
    </view>
    <!-- 信息 -->
    <view class="good_detail_brief">
      {{brief}}
    </view>
    <!-- 价格 -->
    <view class="good_detail_price">
      <text class="price" >¥{{price}}</text>
      <text class="oldPrice" style="display:{{oldPrice == '' ? 'none': 'inline'}};">¥{{oldPrice}}</text>
    </view>
  </view>
</view>
properties: {
    // 图片链接
    url: {  
      type: String,
      value: ''
    },
    // 名称
    name: {
      type: String,
      value: ''
    },
    // 信息
    brief: {
      type: String,
      value: ''
    },
    // 新的价格
    price: {
      type: String,
      value: ''
    },
    // 旧的价格
    oldPrice: {
      type: String,
      value: ''
    }
  },
/* components/goodList/goodList.wxss */

.goodList-good {
  position: relative;
  width: 100%;
  height: 100%;
}
.goodList-good-img {
  width: 100%;
  height: 312rpx;
  position: relative;
}
.goodList-good-img image {
  width: 100%;
  height: 100%;
}
.goodList-good_detail {
  padding: 26rpx 23rpx;
}
.good_detail_name {
  width:100%;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
}
.good_detail_brief {
  width:100%;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  font-size: 25rpx;
  color: #8c8c8c;
}
.good_detail_price {
  display: flex;
  justify-content: flex-start;
  align-items: flex-end;
}
.good_detail_price .price {
  color: #a36d4a;
  font-size: 28rpx;
  padding-right: 16rpx;
}
.good_detail_price .oldPrice {
  font-size: 24rpx;
  color: #8c8c8c;
  text-decoration: line-through;
}

你们会发现,我的边框为什么那么细。


0.5px边框如何画

初学者,可能会说,0.5px边框,不就是border: 0.5px吗,其实这是错的。

浏览器会把任何小于1px的边框都解析成1px,所以你写0.5px其实浏览器会解析成1px,这样就实现不了效果。

其实也很简单,使用伪类去画。

例如,goodList组件的右部边框.

在index页面的html中,我在包裹goodList的view标签的class中加上了rightBorder这个类来表示画出上边框。

<view class="item topBorder rightBorder" data-id="{{item.id}}" bindtap="goDetails">
  <goodList url="{{item.url}}"
    name="{{item.name}}"
    brief="{{item.brief}}"
    price="{{item.price}}"
    oldPrice="{{item.oldPrice}}" ></goodList>
</view>
.rightBorder::after {
  content: '';
  position: absolute;
  height: 200%;
  width: 1rpx;
  right: -1rpx;
  top: 0;
  transform-origin: 0 0;
  border-right: 1rpx solid#e0e0e0;
  transform: scale(.5);
  z-index: 1;
}

其实画0.5px边框就记住。

  1. 使用伪类,并且 position: absolute;
  2. 如果想画上下的边框,就把width设置成200%,,左右就把height设置成200%
  3. 然后使用transform: scale(.5); 宽高都缩小0.5倍就成了0.5px边框.

其它还有一些 细节问题就按照所需写就行。


userList组件

只是我的页面中的一个列表信息,这个很简单

<!--components/userList/userList.wxml-->
<view class="main">
  <image src="{{List.img}}" />
  <view class="text" >
    <text >{{List.text}}</text>
  </view>
  <view class="state" wx:if="{{List.state !== ''}}">
    <text >({{List.state}})</text>
  </view>
</view>
/* components/userList/userList.wxss */
.main {
  width: 100%;
  height: 120rpx;
  display: flex;
  align-items: center;
  background-color: #fff;
  padding: 40rpx;
  box-sizing: border-box;
}
.main image {
  width: 80rpx;
  height: 80rpx;
}
.main .text {
  font-size: 32rpx;
  padding: 0 10rpx 0 5rpx;
}
.main .state {
  font-size: 26rpx;
  color: #8c8c8c;
}
properties: {
    List: {
      type: Object
    }
  },

页面和功能介绍

首页搜索部分

我这里引入的是weui组件的搜索样式。如何用?

  1. 下载WEUI小程序版,可以在微信开发者工具中打开。
  2. (推荐)把styly文件夹下的weui.wxss 放到自己项目中weui文件夹下
  3. 在app.wxss中@import "./weui/weui.wxss".就可以使用了

首页轮播部分

这是微信小程序提供的一个组件swiper,微信小程序开发者文档

swiper和swiper-item组合起来就可以实现,一些配置信息,请查看官方文档

具体代码

<swiper indicator-dots="{{indicatorDots}}" indicator-active-color="#ffffff" autoplay="{{autoPlay}}" interval="{{interval}}" duration="{{duration}}">
    <block wx:for="{{banners}}" wx:key="index">
      <swiper-item>
        <image src="{{item.imgurl}}" mode="aspectFill" class="banner-image" />
      </swiper-item>
    </block>
  </swiper>
 data: {
    banners: [],
    indicatorDots: true,
    autoPlay: true,
    interval: 3000,
    duration: 1000,
    navdata: [],
    goodList: [],
    goodListOne: {},
    name:'',
  },

一个常用的功能

在商城小程序中经常要做一个这样的功能.例如:

功能要求:

  1. 点击左边的商品列表,右边的商品信息会滑动到对应位置.
  2. 滑动右边的商品信息,左边的商品列表显示的高亮会对应发生变化.

功能要求并不难,但是对于初学者而言,可能会有些问题。我就直接说功能该怎么做

首先:分析一下,页面结构是左右布局。并且两边都可以滑动.所以可以使用微信给我们提供的

scroll-view组件.两个组件就可以采用float,分布在左右两边.

<!-- miniprogram/pages/category/category.wxml -->
<view class="container">
  <!-- 左边商品的标签信息 -->
  <scroll-view scroll-y scroll-with-animation="{{true}}" class="categroy-left">
    <view wx:for="{{categroy}}" wx:key="{{index}}" data-index="{{index}}" bindtap="switchTab" class="cate-list {{curIndex === index ? 'on': ''}}">
      {{item[0].name}}
    </view>
  </scroll-view>
  <!-- 右边 标签对应的商品信息 -->
  <scroll-view scroll-y scroll-into-view="{{toView}}" scroll-with-animation="true" bindscroll="scrollContent" bindscrolltolower="scrollEnd" class="categroy-right">
    <block wx:for="{{categroy}}" wx:key="inedx">
      <view id="right-list" class="right-list" id="{{index}}">
        <view class="right-title">
          <text>{{item[0].name}}</text>
        </view>
        <view class="right-content">
          <block wx:for="{{item}}" wx:key="idex" wx:for-item="product" wx:for-index="idex">
            <view class="list-detail" wx:if="{{idex > 0}}">
              <image src="{{product.picture}}" />
              <view class="detail-name">
                <text>{{product.desc}}</text>
              </view>
            </view>
          </block>
        </view>
      </view>
    </block>
  </scroll-view>
</view>
<tabbar on="category"></tabbar>

/* miniprogram/pages/category/category.wxss */
/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
::-webkit-scrollbar
{
width: 0px;
height: 0px;
background-color: pink;
}
.categroy-left {
  height: 100%;
  width: 150rpx;
  float: left;
  border-right: 1px solid #ebebeb;
  box-sizing: border-box;
  position: fixed;
  font-size: 30rpx;
  padding-bottom: 100rpx;
  box-sizing: border-box;
}
.categroy-left .cate-list {
  height: 90rpx;
  line-height: 90rpx;
  text-align: center;
  border: 2px solid #fff;
}
.categroy-left .cate-list.on {
  color: #ff4800;
  font-size: 34rpx;
}
/* 右边的列表 */
.categroy-right {
  width: 600rpx;
  float: right; 
  height: 1334rpx;
  /* height: 100%; */
  padding-bottom: 100rpx;
  box-sizing: border-box;
  overflow: hidden;
}
.right-title {
  width: 100%;
  text-align: center;
  position: relative;  
  padding-top: 30rpx;
  /* font-size: 30rpx; */
  padding-bottom: 30rpx;
}
 .right-title text::before, .right-title text::after {
  content: '';
  position: absolute;
  width: 60rpx;
  /* height: 1px; */
  top: 50%;
  border-top: 1px solid #e0e0e0;
  /* transform: scale(.5); */
}
.right-title text::before { 
  left: 30%;
}
.right-title text::after {
  right: 30%;
}
.right-list {
  /* height: 100%; */
  background-color: #fff;
}
.right-content {
  width: 100%;
  height: 100%;
  display: flex;
  flex-wrap: wrap;
}
.right-content .list-detail {
  flex-shrink: 0;
  width: 33.3%;
  height: 100%;
  font-size: 26rpx;
  text-align: center;
  
}
.right-content .list-detail image {
  width: 120rpx;
  height: 120rpx;
  padding: 10rpx;
  /* background-color: pink; */
}

这个功能的难点就在于js逻辑

先贴出data中需要的数据

data: {
    categroy:[],    //商品信息
    curIndex: 'A',  //当前的选中的标签
    toView: 'A',    //去到的标签
    // 存入每个list的高度叠加
    heightArr: [],
    // 最后一个list,就是最后一个标签的id
    endActive: 'A'
  },

点击左边右边滑动

这一功能很简单就能实现

只需要在右边scroll-view组件中添加事件scroll-into-view="{{toView}}",toView就是商品显示的id

注意,右边每个商品信息,页面渲染时必须加上id属性

然后左边的scroll-view组件只需添加一个点击事件去修改toView的值就行了

// 点击左边标签要修改的信息
  switchTab(e) {
    this.setData({
      curIndex: e.target.dataset.index,
      toView: e.target.dataset.index
    })
  },

滑动右边左边高亮对应改变

  1. 首先需要计算出 右边商品每一块占据的高度,并且存入数组中,可以放入onReady生命周期中

    // 计算出右边每个商品占据的高度
      getPageMessage() {
        // console.log(4)
        let self = this
        let heightArr = []
        let h = 0
        const query = wx.createSelectorQuery()
        query.selectAll('.right-list').boundingClientRect()
        query.exec( res => {
          res[0].forEach( item => {
            h += item.height
            heightArr.push(h)
          })
          self.setData({
            heightArr: heightArr
          })
        })
      },
    
  2. 在右边的scroll-view组件中加上事件。bindscroll="scrollContent,这是scroll-view提供的事件,在滑动时就会触发.

    // 页面滑动时触发
      scrollContent(e) {
        const scrollTop = e.detail.scrollTop
        const scrollArr = this.data.heightArr
        const length = scrollArr.length - 1
        let endChar = String.fromCharCode(65 + length)
        let curChar = this.getCurrentIndex(scrollTop)
        if(this.data.endActive != endChar) {
          this.setData({
            curIndex: curChar
          })
        } else {
          this.setData({
            endActive: 'A'
          })
        }
      },
    
    // 判断curIndex应该是那个
      getCurrentIndex(scrollTop) {
        const scrollArr = this.data.heightArr
        let find = scrollArr.findIndex(item => {
          // 提前10rpx触发效果
          return scrollTop < item - 10
        })
        let curChar = String.fromCharCode(65 + find)
        return curChar
      },
    

    以上就可以实现所有的功能要求了。

    但是这样的滑动并不是很完美,右边滑动到最下面,左边高亮却不是最后一个

    相对完美效果就是这样

想要完美一点就要在,右边scroll-view添加一个事件:bindscrolltolower="scrollEnd"

// 页面滑动到底部触发
  scrollEnd() {
    const scrollArr = this.data.heightArr
    const length = scrollArr.length - 1
    let endChar = String.fromCharCode(65 + length)
    this.setData({
      curIndex: endChar,
      endActive: endChar
    })
  },

购物逻辑

想要实现这样的效果并不困难。

逻辑顺序: 首页点击商品信息 -> 商品详情页面显示对应的商品详情信息 -> 购物车页面显示商品购买的商品信息. -> 修改之后,商品详情页面显示修改的信息。

想要实现这样的功能,必须有一个 id 在页面跳转时,传入给跳转的页面,跳转的页面再通过id值获取页面所需的数据

例如:首页 -> 商品详情页

这是一条商品的列表的信息,通过点击事件bindtap="goDetails",跳到对应的页面.

<view class="list">
      <block wx:for="{{goodList}}" wx:key="{{item.id}}">
        <view class="item topBorder rightBorder" data-id="{{item.id}}" bindtap="goDetails">
          <goodList url="{{item.url}}"
            name="{{item.name}}"
            brief="{{item.brief}}"
            price="{{item.price}}"
            oldPrice="{{item.oldPrice}}" ></goodList>
        </view>  
      </block>
    </view>
goDetails(e) {
    const id = e.currentTarget.dataset.id
    wx.navigateTo({
      url: `/pages/goodDetails/goodDetails?id=${id}`,
    });
  },

商品详情页:

传入的id值可以再onLoad生命周期的options参数上获取

onLoad: function (options) {
    WXAPI.showLoading()
    // 获取用户的地址信息
    const address = wx.getStorageSync('Address');
    this.setData({
      id: options.id,
      address
    })
    this.getGoodDetail()
  },

数据的存储逻辑

我是把数据直接存在本地缓存中(也可以直接存入到云数据库中),使用的是 wx.setStorage()

在本地存入了两个数据,一个是所有购买的商品信息,一个是总的商品购买数量

// 添加到购物车
  toAddCart() {
    let cartData = wx.getStorageSync('goods') || [];    
    let data = {
      id: this.data.id,
      name: this.data.goodData.name,
      memory: this.data.memory,
      color: this.data.color,
      price: this.data.price,
      num: this.data.selectNum,
      img: this.data.imgSrc,
      select: true
    }
    // wx.removeStorageSync('goods');
    cartData.push(data)
    const allNum =this.getAllNum(cartData)
    wx.setStorage({
      key: 'goods',
      data: cartData,
      success: (res) => {
        console.log(res)
        let pageIndex = getCurrentPages()
        let backIndex = pageIndex.length - 2
        wx.navigateBack({
          delta: backIndex
        })
      },
      fail: () => {},
      complete: () => {}
    });
    // 存储数量到storage
    wx.setStorageSync('allNum', allNum);
    // 写到外面就可以让showToast 显示在前一个页面
    setTimeout(()=>{
      wx.showToast({
        title: '已加入购物车',
        icon: 'success',
        duration: 2000
      });
   },500)
  },
  // 获取所有的数量
  getAllNum(cartData) {
    return cartData.reduce((sum, item) => {
      return sum + (+item.num)
    },0)
  },

概述的参数切换

实现这个功能只需要加一个状态,点击时就修改状态的值,并且修改相关渲染的数据就行。

data: {
 state: 'details_img', //判断概述 参数
}
<view class="summarize-parameter">
    <view class="title">
      <view class="summarize" bindtap="changeState">
        <text class="{{state === 'details_img'? 'on' : ''}}">概述</text>
      </view>
      <view class="parameter" bindtap="changeState">
        <text class="{{state === 'param_img'? 'on' : ''}}">参数</text>
      </view>
    </view>
    <view class="state">
      <block wx:for="{{state === 'details_img'? details_img : param_img}}" wx:key="index">
          <image src="{{item}}" mode="widthFix"/>
      </block>
    </view>
  </view>
// 改变概述和参数
  changeState() {
    let state = this.data.state
    if(state === 'details_img') {
      state = 'param_img'
    } else {
      state = 'details_img'
    }
    this.setData({
      state
    })
  },

购物数据改变,商品详情数据修改

对比一下上面两张图的区别.

在购物页面中选的商品数量和具体的商品信息,之后跳转回商品详情页面中,对应的数据会修改

<view class="sales" bindtap="goSelectGoods">
      <text class="describe">已选</text>
      <view class="detail detail-change">
        {{default_change.name}}
        {{default_change.memory}}
        {{default_change.color}}
        <text >× {{default_change.num}}</text>
      </view>
      <view class="right"></view>
    </view>
<view class="shopping-img" bindtap="goCart">
    <icon type="gouwuche" color="#e0e0e0" size="40"/>
    <text wx:if="{{allNum != 0}}">{{allNum}}</text>
  </view>

上面时两块修改的html结构

在购物页面点击确认之后,我默认就把商品添加到购物车中,并且位于数据的最后一条

返回商品详情页面时,会重新触发onShow生命周期的函数。

所以我只需要,在onShow中触发修改方法就行.

// 改变默认的版本数据 default_change
  changeDefauleChange() {
    const goods = wx.getStorageSync('goods') || [];
    if(goods.length === 0) {
      return
    }
    const id = this.data.id
    const default_change = goods[goods.length - 1]
    let memory = default_change.memory.toString()
    memory = memory.substring(0,memory.length - 4)
    default_change.memory = memory
    this.setData({
      default_change
    })
  },

画一个三角形

这一个三角形是使用CSS画出来的,并不是图标。

使用CSS画出一个三角形,也不是那么困难。使用的是伪类和border属性

.right:before,
.right:after {
  content: '';
  position: absolute;
  top: 35%;
  right: 0;
  border-width: 8px;
  /* transform: translateY(10000rpx); */
  border-color: transparent transparent transparent transparent;
  border-style: solid;
  transform: rotate(90deg);
}

.right:before {
  border-bottom: 8px #aaaaaa solid;
}

.right:after {
  right: 1px;
  /*覆盖并错开1px*/
  border-bottom: 8px #fff solid;
}

修改商品数量

可以直接使用微信小程序提供的picker组件,具体配置请查看文档

使用腾讯地图获取地理位置

先搜索腾讯地图,并且注册开发者信息,申请一个密钥key.

// 获取地理位置
const geocoder = (lat, lon) => {
  return request(API.MAPURL,false,{
    location: `${lat},${lon}`,
    key: API.QQ_MAP_KEY,
    get_poi: 0
  })
}

然后把密钥复制,因为我封装了所有的API接口。所以使用了 API.QQ_MAP_KEY代替,这里就填写申请的密钥就行.

想要获取用户的经纬度信息,可以使用wx.getLocation(),就可以获取用户的经纬度信息了.

getLocation() {
    wx.getLocation({
      type: 'gcj02',
      success: this.getAddress,
      fail: () => {
        this.openLocation()
      }
    })
  },
  getAddress(res) {
    let { latitude: lat, longitude: lon} = res
    WXAPI.geocoder(lat, lon)
    .then(res => {
      if(res.status !== 0 || !res.result) {
        return
      } 
      let {address_component
      } = res.result
      const Address = {
        city: address_component.city,
        district: address_component.district
      }
      wx.setStorageSync("Address", Address);
    })
  },

因为我未让用户授权,所以直接把获取的地理位置,保存在本地storage中.

获取的地理位置,就可以在商品详情页面的送至显示出来

结语

做这个项目的过程来说是快乐的,没有使用云函数(页面数据并不多,我觉得不需要就可以写出来了),所以总共加起来写的时间也很短,不到一个星期就写完了。写完之后的那一刻的成就感也很好。如果你觉得这篇文章有帮到你的地方,不妨给个赞吧!同时也非常希望在下方看到给出的建议!最后奉上源码.如果有需要就自取吧!

最后,说点题外话,因为我是2020届的毕业生,现在面临实习压力。因为需要话时间去看面试题,所以后面写的一段文章,我只是简要的把重要的功能逻辑写了出来,如果写的不清楚,请见谅。