首页 > 其他分享 >商品列表、商品详情、添加购物车、tabbar徽标

商品列表、商品详情、添加购物车、tabbar徽标

时间:2023-01-31 11:27:26浏览次数:55  
标签:goods 数组 购物车 商品 徽标 git tabbar uni

让小程序跟h5样式一样

  • 问题:小程序里搜索出来的结果没有一行显示,超出带省略号
  • 原因:我们之前用的是标签选择器span,而在小程序中是没有span这个标签的
  • 解决办法:直接不要用标签选择器,就是把之前span去掉变成
::v-deep .uni-list-item__content-title {

  display: inline-block;
  width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
  • 推荐以后尽量要用类选择器

清空历史记录

  • 就是给删除的图标 加点击事件
<uni-icons @click="del" type="trash" size="18"></uni-icons>
  • 点击事件里清空本地存储以及数组即可
// 删除图标的点击事件
del() {
  // 光删本地存储界面不会变
  uni.removeStorageSync('history')
  // 还要把data里的数组清空
  this.historyList = []
},

点击历史记录跳转到商品列表页

  • 无非就是给每个历史记录的tag加点击事件,传入被点的历史,然后跳转到商品列表页,并携带被点的历史名作为参数
<uni-tag class="tag" v-for="item in historyList" :text="item" type="success" @click="toList(item)" />
toList(item) {
  uni.navigateTo({
    url: "/subpkg/goods_list/goods_list?query=" + item
  })
},

作业:搜索历史如果存在就提前

  • 思路:
    • 先拿到输入的内容在数组中的下标,判断这个下标是否不等于-1,不等于-1代表要搜索的内容在里面,在里面我就先把以前的删掉,再加到最前面。就相当于把这个提前了
  • 代码:
// 发请求之前我先把历史记录存起来
// 拿到下标
const index = this.historyList.indexOf(this.key)
          // 判断在里面
if (index !== -1) {
  this.historyList.splice(index, 1)
}
// 先把输入的内容加到数组里
this.historyList.unshift(this.key)
// 把数组存到本地
uni.setStorageSync('history', this.historyList)

search - 分支处理

# 把改动添加到暂存区
git add .
# 提交成一条记录
git commit -m'search完成'
# 切换到dev
git checkout dev
# 合并到dev来
git merge search
# 推送dev
git push
# 可选择---删除本地的分支
git branch -d search

goodslist 分支准备

运行如下的命令,基于 master 分支在本地创建 goodslist 子分支,用来开发商品列表相关的功能:

git checkout -b goodslist

商品列表 - 准备商品列表的结构和样式

image-20220706092332497

  • 如上图分析,每个商品应该是一个大vie,大view里分为一左一右,左边是图片,右边又是一个view,右边view里放两个text,到时候一上一下
<view class="goods-item">
  <!-- 左边图片 -->
  <image class="pic"
    src="http://image3.suning.cn/uimg/b2c/newcatentries/0000000000-000000000614133369_1_400x400.jpg">
  </image>
  <!-- 右边的盒子 -->
  <view class="info-box">
    <text class="title">我是标题</text>
    <text class="price">¥ 79.00</text>
  </view>
</view>
  • 样式:大盒子用弹性布局,图片给宽高。右边的view也给弹性布局,只不过要改方向为一上一下、并且排列方式为一个起点,一个终点,最后就是价格颜色变红
.goods-item {
  display: flex;
  margin: 20rpx;

  .pic {
    width: 300rpx;
    height: 300rpx;
  }

  .info-box {
    display: flex;
    flex-direction: column;
    justify-content: space-between;

    .price {
      color: #c00;
    }
  }
}

商品列表 - 发请求获取列表数据

  • 因为这个接口要传递参数,所以先声明一个参数对象,用来准备参数
data () {
    return {
        params:{
            query: '',
            cid: '',
            pagenum: 1,
            pagesize:10
        }
    }
}
  • 来到商品列表一般都会携带一个参数,代表要查询的内容,所以得接收这个参数,所以写了onLoad来接收
onLoad (options) {
    // 把传递过来的参数存起来
  this.params.query = options.query
  this.params.cid = options.cid
}
  • 然后封装一个发请求的方法,在这之前可以在data中声明一个数组用来保存请求到的结果
data () {
    return {
        ......
        goodsList: []
    }
}
methods: {
  async getGoodsList() {
      const res = await uni.$http.get('goods/search', this.params)
      this.goodsList = res.data.message.goods
  }
}
  • 最后去onLoad里调用
onLoad (options) {
    .........
    this.getGoodsList()
}

商品列表 - 渲染数据

  • 对着之前写好的结构v-for把对应的属性渲染到对应的地方
<template>
  <view>
    <view class="goods-item" v-for="item in goodsList" :key="item.goods_id">
      <!-- 左边图片 -->
      <image class="pic" :src="item.goods_small_logo">
      </image>
      <!-- 右边的盒子 -->
      <view class="info-box">
        <text class="title">{{ item.goods_name }}</text>
        <text class="price">¥ {{ item.goods_price }}</text>
      </view>
    </view>
  </view>
</template>
  • 染后发现右侧文字把左边图片挤压了,原因是右侧是纯文字,不给宽度会拉伸盒子,所以给右侧盒子加flex:1相当于给个宽度
.info-box {
  flex: 1;
    ......
}

商品列表 - 设置默认图片以及处理价格小数点

<image class="pic"
        :src="item.goods_small_logo || 'https://img3.doubanio.com/f/movie/8dd0c794499fe925ae2ae89ee30cd225750457b4/pics/movie/celebrity-default-medium.png'">
      </image>
  • 最后让价格保留两位小数即可
<text class="price">¥ {{ item.goods_price.toFixed(2) }}</text>

商品列表 - 上拉加载更多

  • 一般app也不会让你拉到底才去加载,所以要设置触底距离,稍微大一点
  • 来到 pages.json(这个文件既有全局配置,也有局部的页面配置),找到 分包里的goods_list,设置上拉触底距离
{
  "path": "goods_list/goods_list",
   "style": {
      "enablePullDownRefresh": false,
      "onReachBottomDistance": 230
   }
},
- 来到`goods_list`写上拉触底的回调:先让页码+1,再调用请求
// 触底触发的钩子
    onReachBottom() {
      
      // 让页码+1
      this.params.pagenum++
      // 发请求加载数据
      this.getGoodsList()
    },
- 因为之前是对数组进行赋值,那么会导致,每次加载新的都会前面的给覆盖,所以修改 `getGoodsList`里的方法,改成push添加
- 并且因为需要判断是否加载完成,所以data里先声明一个变量用来记录有没完成
data () {
    return {
        finished: false
    }
}
- 修改`getGoodsList`方法
// 加载数据的方法
async getGoodsList() {
  const res = await uni.$http.get('goods/search', this.params)
  // 每次追加
  this.goodsList.push(...res.data.message.goods)
  // 加载完数据都要判断是否加载完毕
  this.finished = this.goodsList.length >= res.data.message.total
}
- 然后在上拉触底触发的钩子里要判断如果完成了,就不往下执行,且弹出提示
// 触底触发的钩子
onReachBottom() {
  // 判断是否完成
  if (this.finished) return uni.showToast({
    title: '数据加载完毕',
    icon: 'none'
  })
  // 让页码+1
  this.params.pagenum++
  // 发请求加载数据
  this.getGoodsList()
},

商品列表 - 上拉加载更多-节流阀

  • 先data里声明一个节流阀变量
data () {
    return {
        isLoading: false
    }
}
  • 发请求前变true,发完请求变false
// 加载数据的方法
async getGoodsList() {
  this.isLoading = true
  .......
          
  // 发完请求就变false
  this.isLoading = false
  // 加载完数据都要判断是否加载完毕
  this.finished = this.goodsList.length >= res.data.message.total
}
- 然后上拉钩子里做判断,当前如果正在发请求则return
// 触底触发的钩子
onReachBottom() {
  if (this.isLoading) return
  // 判断是否完成
  if (this.finished) return uni.showToast({
    title: '数据加载完毕',
    icon: 'none'
  })
  // 让页码+1
  this.params.pagenum++
  // 发请求加载数据
  this.getGoodsList()
},

小程序里发请求参数的说明

  • 在网页中,如果参数写undefined,浏览器会自动优化成空参数,而小程序没有这个优化,所以导致如果是undefined,那么也给你传递到接口里了
  • 而我们接口的要求的是,要么就传值,要么就给空。而你给个undefined它表示找不到商品,所以小程序里返回空数组
  • 如何处理?
    • 不管有没有优化成空,我就干脆自己手动判断如果是undefined,就改空字符串
    • 来到 onl oad
// onl oad有形参,形参里就有传递过来的数据
    onl oad(options) {
      // 把传递过来的参数存起来
      this.params.query = options.query || ''
      this.params.cid = options.cid || ''

      // 调用请求方法
      this.getGoodsList()
    },

商品列表 - 下拉刷新

  • 下拉刷新需要先改配置开启,所以来到 pages.json,找到 goods_list开启
{
        "path": "goods_list/goods_list",
        "style": {
          "enablePullDownRefresh": true,
          "onReachBottomDistance": 230
}
  • 然后回到 goods_list页面,监听下拉,在下拉里把数据恢复并重新请求
// 监听下拉刷新的钩子
onPullDownRefresh() {
  // 重新加载数据
  this.params.pagenum = 1
  // 数组要清空
  this.goodsList = []
  // 加载状态都变成完成了,所以要恢复
  this.finished = false
  // 发请求加载数据
  this.getGoodsList()
},
  • 最后在请求完成后,手动停止下拉状态
async getGoodsList() {
        ...................
                ...................
                ...................
        // 停止下拉状态
        uni.stopPullDownRefresh()
      }

商品列表 - 点击商品跳转到详情页

  • 来到 goods_list,给每个商品盒子加点击事件
<view .....  @click="toDetail(item)">
  • 点击事件里跳转到商品详情并携带id
// 每个商品的点击事件
      toDetail(item) {
        uni.navigateTo({
          url: '/subpkg/goods_detail/goods_detail?goods_id=' + item.goods_id
        })
      },

商品列表 - 分支合并与提交

  1. goodslist 分支进行本地提交:
git add .
git commit -m "完成了商品列表页面的开发"
  1. 将本地的 goodslist 分支推送到码云:
git push -u origin goodslist
  1. 将本地 goodslist 分支中的代码合并到 master 分支:
git checkout dev
git merge goodslist
git push
  1. 删除本地的 goodslist 分支:
git branch -d goodslist

商品详情 - 创建分支

运行如下的命令,基于 master 分支在本地创建 goodsdetail 子分支,用来开发商品详情页相关的功能:

git checkout -b goodsdetail

商品详情 - 获取详情数据

  • 建议添加编译命令:goods_id=47866
  • 来到 goods_detail页面,声明变量接收传递过来的id和声明变量用来保存获取到的详情数据
data() {
    return {
      detail: {}
    };
  },
  • onLoad里拿到传递过来的id,并调用发请求的方法
async onl oad(options) {
  const res = await uni.$http.get('goods/detail', {
    goods_id: options.goods_id
  })
  this.detail = res.data.message
},

商品详情 - 渲染轮播图

<!-- 轮播图区域--快捷键:uswiper -->
<swiper class="swiper" :indicator-dots="true" :autoplay="true" :interval="3000" :duration="1000">
  <swiper-item v-for="item in detail.pics">
    <image class="swiper-pic" :src="item.pics_sma"></image>
  </swiper-item>
</swiper>
.swiper {
  height: 500rpx;

  .swiper-pic {
    width: 100%;
    height: 100%;
  }
}

商品详情 - 预览效果

文档-图片

  • 给轮播图的image加点击事件,需要传入当前的下标
<swiper-item v-for="(item, index) in detail.pics" @click="showPreview(index)">
        <image class="swiper-pic" :src="item.pics_sma"></image>
      </swiper-item>
  • 点击事件里,调用预览的方法即可(提取出pics里所有的大图产生一个新的数组给urls)
// 预览的点击事件
showPreview (index) {
  // 图片预览
  uni.previewImage({
    current:index,
    urls: this.detail.pics.map(v => v.pics_big)
  })
},

商品详情 - 渲染商品信息区域

image-20220706145044016

  • 如上图布局即可
<!-- 信息区域 -->
    <view class="info-box">
      <view class="price">
        ¥ 89
      </view>
      <view class="title-box">
        <view class="title">
          韵语孕期全包裹托腹带 韵语孕妇托腹腰带 孕产妇服饰舒适透气薄款托腹带
        </view>
        <view class="icon-box">
          <uni-icons size="25" type="star"></uni-icons>
          <view>
            收藏
          </view>
        </view>
      </view>
      <view class="yf">
        快递:免运费
      </view>
    </view>
  • 样式如下
.info-box {
  margin: 10px;

  .price {
    font-size: 18px;
    font-weight: 700;
    color: #c00;
  }

  .title-box {
    margin: 10px 0;
    display: flex;

    .icon-box {
      width: 240rpx;
      text-align: center;
    }
  }

  .yf {
    color: #ccc;
    font-size: 13px;
  }
}

商品详情 - 渲染商品详细内容

  • 发现商品详情其实就是服务器返回的一段富文本,所以直接用即可
<rich-text :nodes="detail.goods_introduce"></rich-text>

商品详情 - 解决图片多出空格问题

  • 多出空格的原因:图片默认是基线对齐,所以会空出大约4px的高度
  • 解决办法1:设置对齐方式为top、middle等等都行,只要不是baseline
  • 解决办法2:直接把图片转成块级元素
  • 因此我们需要发请求拿到数据后,先对数据做改良,然后再赋值即可
// 在渲染之前把所有的<img替换成 <img style="display:block"
    res.message.goods_introduce = res.message.goods_introduce.replaceAll('<img', '<img style="display:block" ')
    this.detail= res.message

商品详情 - 渲染底部购物车区域

uni-ui 商品导航-文档

  • 其实主要就是用 uni-ui提供的 商品导航
  • 来到详情页,在最后加一个view,view里包一个商品导航
    • options是用来设置左侧的图标
    • button-group是用来设置右侧的按钮
<view class="nav-bottom">
      <!-- options绑定左侧图标的 -->
      <uni-goods-nav :fill="true" :options="options" :button-group="customButtonGroup" />
    </view>
options: [{
          icon: 'shop',
          text: '店铺'
        }, {
          icon: 'cart',
          text: '购物车',
          info: 2
        }],

        customButtonGroup: [{
            text: '加入购物车',
            backgroundColor: '#f00',
            color: '#fff'
          },
          {
            text: '立即购买',
            backgroundColor: '#ffa200',
            color: '#fff'
          }
        ],
      
  • 设置它为固定定位到底部
.nav-bottom {
  position: fixed;
  left: 0;
  bottom:0;
  width:100%;
}
  • 此时底部购物车区域会挡住一部分内容,所以需要给上面整体的内容加一个大盒子包起来,然后让大盒子加一个 padding-bottom:50px即可
<view style="padding-bottom:50px;">
    <!-- 这里包内容 -->
</view>

商品详情 - 点击跳转到购物车页面

  • 经过查阅文档,发现上面的 商品导航组件有两个事件,一个叫 click,代表点击左侧任意图标触发,还有一个事件叫 buttonClick,点击右侧任何按钮触发

  • 通过事件调用的函数里的参数可以区分是谁被点

    image-20220706155456841

ge

  • 给购物车加click事件(上面已经写好了),然后写一个函数即可
onClick(e) {
        // 通过e可以区分点的是哪一个
        if (e.index === 1) {
          // 跳转到购物车页面
          uni.switchTab({
            url: '/pages/cart/cart'
          })
        }
      },

商品详情 - 分支与合并

  1. goodsdetail 分支进行本地提交:
git add .
git commit -m "完成了商品详情页面的开发"
  1. 将本地的 goodsdetail 分支推送到码云:
git push -u origin goodsdetail
  1. 将本地 goodsdetail 分支中的代码合并到 master 分支:
git checkout master
git merge goodsdetail
git push
  1. 删除本地的 goodsdetail 分支:
git branch -d goodsdetail

购物车 - 分支准备

运行如下的命令,基于 master 分支在本地创建 cart 子分支,用来开发购物车相关的功能:

git checkout -b cart

购物车 - 准备vuex管理购物车数据

uni网站 - vuex介绍

  • 来到项目根目录新建 store文件夹,里面放一个index.js,代码如下
// 导入Vue
import Vue from 'vue'
// 导入Vuex
import Vuex from 'vuex'
// 安装vuex
Vue.use(Vuex)

// 实例化store对象
const store = new Vuex.Store({
  // 注册子模块的地方
  modules: {
    
  }
})

// 把store对象暴露出去
export default store
  • 来到 main.js 导入上面的store,并挂在到vue的根实例
import store from './store'
const app = new Vue({
  store,
  ...App
})
  • 来到 store文件夹新建 modules文件夹,里面放一个 cart.js,代码如下
export default {
  // 开启命名空间
  namespaced: true,
  state() {
    return {
      // 购物车是一个数组,数组里每个元素是一个对象
      // 对象里至少要存:商品id、商品名字、商品价格、商品数量、商品图片、商品是否选中
      cartList: []
    }
  },
  mutations: {
  }
}

  • 来到 index.js 做导入和注册
import cart from './modules/cart.js'

// 实例化store对象
const store = new Vuex.Store({
  // 注册子模块的地方
  modules: {
    cart
  }
})

购物车 - 实现添加购物车功能

  • 因为要修改到vuex里的数据,所以就要提供一个mutations,这个mutations的方法,只是实现添加购物车,也就是把一个商品加到购物车里,那相当于只是要让数组多一个元素,所以没必要替换,而是只是传一个商品,加到数组里
mutations: {
    addGoods (state, goods) {
        state.cartList.push(goods)
    }
}
  • 回到 goods_detail页,给添加购物车加点击事件(之前有绑定,但是还没写方法,现在只要写方法)
buttonClick(e) {
          // 代表是加入购物车
        if (e.index === 0) {
          // 如果点的是添加购物车,就创建一个对象,给vuex里的方法
          this.$store.commit('cart/addGoods', {
            goods_id: this.detail.goods_id,
            goods_name: this.detail.goods_name,
            goods_price: this.detail.goods_price,
            goods_small_logo: this.detail.goods_small_logo,
                        // 写死true代表默认选中
            goods_checked: true,
                        // 代表默认只有1个数量
            goods_count: 1
          })
        }
      },
  • 此时发现,添加购物车确实能添加进去,但是如果我对同一个商品点多次添加,它会重复添加多个到数组里,而我们要的逻辑是: 如果在数组里,只要让数量+1,不在数组里才加到数组里,所以修改mutations里的逻辑
// 把要添加的商品传进来
    addGoods(state, goods) {
      // 判断当前商品在不在数组里
      // 左边的v代表当前数组里的每一项,右边的代表当前要添加的商品的id
      // 通过id来找,如果找到了会得到这个元素,没找到会得到undefined
      const item = state.cartList.find(v => v.goods_id === goods.goods_id)
      if (item) { // 只有0、空字符串、null、undefined、NaN会得到false,其他都是true
        // 如果在就只要让数量+1
        item.goods_count++
      } else {
        // 如果不在就是加到数组里
        state.cartList.push(goods)
      }
    }

购物车 - 使用getters动态统计购物车商品总数量

  • 来到 modules/cart.js封装一个getters统计总数量
getters: {
  total(state) {
    // 统计出总数量--要统计出选中的数量
    let count = 0
    state.cartList.forEach(v => {
      if (v.goods_checked) {
        count += v.goods_count
      }
    })
    // 把统计的数量返回出去
    return count
  }
},

购物车 - 使用计算属性把总数量赋值到界面

  • 先导入 mapGetters 然后做计算属性的映射
import { mapGetters } from 'vuex'
export default {
  computed: {
    ...mapGetters('cart', ['total'])
  },
  }
  • 侦听total的改变,一旦改变就赋值给 info
// 侦听器
watch:{
  
  total () {
    // 把购物车总数量赋值给徽标
    this.options[1].info = this.total
  }
}

购物车 - 持久化购物车数据

  • 也就是来到 store/modules/cart.js,找到 addGoods方法,在操作完数据后,立即缓存到本地
addGoods(state, goods) {

      ......
      
      // 把当前购物车缓存到本地
      uni.setStorageSync('cart', state.cartList)
    }
  • 然后给state默认值
state() {
  return {
    // 购物车是一个数组,数组里每个元素是一个对象
    // 对象里至少要存:商品id、商品名字、商品价格、商品数量、商品图片、商品是否选中
    cartList: uni.getStorageSync('cart') || []
  }
},
  • 解决页面一打开购物车不显示徽标的问题
    • 问题原因: 只有当total发生改变,才会对徽标进行赋值,而我们需要页面一打开就自动执行
    • 所以就只要把侦听器改成完整版,让他页面一打开就默认执行
watch:{
      total: {
        handler (newVal) {
          this.options[1].info = newVal
        },
        // 开启页面一打开就默认执行一次
        immediate: true
      }
}

标签:goods,数组,购物车,商品,徽标,git,tabbar,uni
From: https://www.cnblogs.com/strundent/p/17078365.html

相关文章