微信小程序(黑马优购:购物车页面)

news2024/11/23 22:05:33

 1.渲染商品页面

<template>
  <view>
    <!-- 商品列表的标题区域 -->
    <view class="cart-title">
        <!-- 左侧的图标 -->
        <uni-icons type="shop" size="18"></uni-icons>
        <!-- 右侧的文本 -->
        <text class="cart-title-text">购物车</text>
    </view>
    
    <!-- 循环渲染购物车中的商品信息 -->
    <block v-for="(goods,i) in cart" :key="i">
      <my-goods :goods="goods"></my-goods>
    </block>

  </view>
</template>

<script>
  
  import badgeMix from '@/mixins/tabbar-badge.js'
  import { mapState } from 'vuex'  
  
  export default {
    mixins: [badgeMix],
    computed: {
        ...mapState('m_cart',['cart'])
    },
    data() {
      return {

      }
    }

  }
</script>

2.展示购物车选中状态

mygoods.vue中配置
 

  <view class="goods-item-left">
      <radio checked color="#C00000" v-if="showRadio"></radio>
      <image :src="goods.goods_small_logo || defaultPic" class="goods-pic"></image>
    </view>

引入showRadio组件,判断是否选中

 props: {
      goods: {
        type: Object,
        default: {}
      },
      showRadio: {
        type: Boolean,
        //默认情况下,不会展示radio组件
        default: false
      }
    },

1)动态修改
 

 <radio :checked="goods.goods_state" color="#C00000" v-if="showRadio"></radio>

2)修改购物车商品的勾选状态

  <!-- 循环渲染购物车中的商品信息 -->
    <block v-for="(goods,i) in cart" :key="i">
      <my-goods :goods="goods" :show-radio="true" @radio-change="radioChangeHandler"></my-goods>
    </block>

  <view class="goods-item-left">
      <radio :checked="goods.goods_state" color="#C00000" v-if="showRadio" @click="radioClickHandler"></radio>
      <image :src="goods.goods_small_logo || defaultPic" class="goods-pic"></image>
    </view>
methods:{
      //这是radio组件的点击事件处理函数
      radioClickHandler(){
        this.$emit('radio-change',{
           goods_id: this.goods.goods_id,
           goods_state: !this.goods.goods_state
        })
      }
    }

cart.js中配置

export default{
  namespaced: true,
  
  state: ()=> ({
    //购物车的数组,用来存储购物车中每个商品的信息对象
    //每个商品的信息对象,都包含如下6个属性
    //{goods_id,goods_name,goods_price,goods_count,goods_small_logo,goods_state}
    cart: JSON.parse(uni.getStorageSync('cart') || '[]')
  }),
  
  mutations: {
    addToCart(state,goods){
      //根据提交的商品的id,查询购物车中是否存在这件商品
      //如果不存在,则findResult为undefined;否则,为查找到的商品信息对象
      const findResult = state.cart.find(x => x.goods_id === goods.goods_id)
      
      if(!findResult){
        //如果购物车中没有这件商品,则直接push
        state.cart.push(goods)
      }else{
        //如果购物车中有这件商品,则只更新数量即可
        findResult.goods_count++
      }
      //通过commit方法,调用m_cart命名空间的saveToStorage方法
      this.commit('m_cart/saveToStorage')
      
    },
    
    saveToStorage(state){
        uni.setStorageSync('cart',JSON.stringify(state.cart))
    },
    
    //更新购物车中商品的勾选状态
    updateGoodsState(state,goods){
      const findResult = state.cart.find(x => x.goods_id === goods.goods_id)
      
      if(findResult){
        findResult.goods_state = goods.goods_state
        this.commit('m_cart/saveToStorage')
      }
      
      
    }
    
    
    
  },

  getters: {
    total(state){
      let c = 0
      state.cart.forEach(x => c += x.goods_count)
      return c
    }
  }
}

cart.vue中配置

import badgeMix from '@/mixins/tabbar-badge.js'
  import { mapState, mapMutations } from 'vuex'  
  
  export default {
    mixins: [badgeMix],
    computed: {
        ...mapState('m_cart',['cart'])
    },
    data() {
      return {
        
      }
    },
    methods:{
      ...mapMutations('m_cart',['updateGoodsState']),
      radioChangeHandler(e){
        this.updateGoodsState(e)
      }
    }

3)渲染数字选择框

  <view class="goods-item-right">
      <!-- 商品的名字 -->
      <view class="goods-name">{{goods.goods_name}}</view>
      <view class="goods-info-box">
        <view class="goods-price">¥{{goods.goods_price | tofixed}}</view>
        <!-- 数字选择框 -->
        <uni-number-box :min="1" :value="goods.goods_count"></uni-number-box>
      </view>
    </view>

3控制选择框按需展示/隐藏

cart.vue

  <!-- 循环渲染购物车中的商品信息 -->
    <block v-for="(goods,i) in cart" :key="i">
      <my-goods :goods="goods" :show-radio="true" :show-num="true" @radio-change="radioChangeHandler"></my-goods>
    </block>

my-goods.vue

 <!-- 数字选择框 -->
        <uni-number-box :min="1" :value="goods.goods_count" v-if="showNum"></uni-number-box>

props: {
   
      showNum: {
        type: Boolean,
        default: false
      }

    },

引入NumberBox组件

my-goods.vue

 <!-- 数字选择框 -->
        <uni-number-box :min="1" :value="goods.goods_count" v-if="showNum" @change="numChangeHandler"></uni-number-box>

 //监听到了NumberBox组件数量变化的事件9
      numChangeHandler(val){
          this.$emit('num-change',{
            goods_id: this.goods.goods_id,
            goods_count: +val  ///或者val-0   (这是为了确保val是一个数值)
          })
      }

cart.vue

 <my-goods :goods="goods" :show-radio="true" :show-num="true" @radio-change="radioChangeHandler"
              @num-change="numberChangeHandler"
      ></my-goods>

numberChangeHandler(e){
        console.log(e);
      }

4.修改购物车商品数量

cart.js中新增函数

 //更新商品的数量
    updateGoodsCount(state,goods){
      const findResult = state.cart.find(x => x.goods_id === goods.goods_id)
      if(findResult){
        findResult.goods_count = goods.goods_count

//       持久化存储
        this.commit('m_cart/saveToStorage')
      }
    }

cart.vue中配置

 methods:{
      ...mapMutations('m_cart',['updateGoodsState','updateGoodsCount']),
      radioChangeHandler(e){
        this.updateGoodsState(e)
      },
      numberChangeHandler(e){
          this.updateGoodsCount(e)
      }
    }
 

5.购物车滑动删除UI效果

 

  <!-- 滑动删除效果 -->
    <uni-swipe-action>
      <block v-for="(goods,i) in cart" :key="i">
         <uni-swipe-action-item :right-options="options">
             <my-goods :goods="goods" :show-radio="true" :show-num="true" @radio-change="radioChangeHandler"
                      @num-change="numberChangeHandler"
              ></my-goods>
          </uni-swipe-action-item>
      </block>
    </uni-swipe-action>

  data() {
      return {
        options: [{
          text: '删除',
          style: {
            backgroundColor: '#C00000'
          }
        }]
      }
    },

6.商品列表实现滑动删除

cart.js配置

 //根据id删除对应的商品
    removeGoodsById(state,goods_id){
      state.cart =  state.cart.filter(x => x.goods_id !== goods_id)
      this.commit('m_cart/saveToStorage')
    }v

cart.vue

   swipeItemClickHandler(goods){
        this.removeGoodsById(goods.goods_id)
      }

7.收货地址

1)微信小程序中点击跳转到选择地址

manifest.json中配置

  /* 快应用特有相关 */
    "mp-weixin" : {
        /* 小程序特有相关 */
        "appid" : "",
        "setting" : {
          // 取消黄色警告
            "urlCheck" : true,
            "checkSiteMap": false
        },
        "usingComponents" : true,
         "permissions": {
            "chooseAddress": {
              "desc": "你的文字描述" // 这里是对权限的描述,可以自定义
            }

          },
        "requiredPrivateInfos":["chooseAddress"]
    },

  <view class="address-choose-box" v-if="JSON.stringify(address) === '{}'  ">
      <button type="primary" size="mini" class="btnChooseAddress" @click="chooseAddress">请选择收货地址+</button>
    </view>

2)渲染收货人信息

<!-- 渲染收货信息的盒子 -->
    <view class="address-info-box" v-else>
        <view class="row1">
          <view class="row1-left">
            <view class="username">收货人: {{address.userName}}</view>
          </view>
          <view class="row1-right">
            <view class="phone">电话: {{address.telNumber}}</view>
            <uni-icons type="arrowright" size="16"></uni-icons>
          </view>
        </view>
        <view class="row2">
          <view class="row2-left">收货地址:</view>
          <view class="row2-right">{{addstr}}</view>
        </view>
    </view>

 methods: {
       async chooseAddress() {
          
          //1调用小程序提供的chooseAddress()方法,即可使用选中收货地址的功能
          //返回值是一个数组:第1项为错误对象,第2项为成功之后的收货地址对象
          const [err, succ] = await uni.chooseAddress().catch(err => err)
          //2.用户成功的选择了收货地址
          if (err === null && succ.errMsg === 'chooseAddress:ok') {
            //为data里面 的收货地址对象赋值
            this.address = succ

            console.log(succ);
          }

        }
    },
    //计算属性
    computed:{
      addstr(){
        if(!this.address.provinceName) {
          return ''
        } 
        return this.address.provinceName + this.address.cityName + this.address.countyName + this.address.detailInfo
      }
    }

3)改造收货地址方法

创建user.js

export default{
  //开启命名空间
  namespaced: true,
  //数据
  state: () =>({
    address: {}
  }),
  //方法
  mutations: {
    //更改收货地址
    updateAddress(state,address){
      state.address = address
    }
  },
  getters: {}
}

挂载到store.js中


import moduleUser from '@/store/user.js'

Vue.use(Vuex)

const store = new Vuex.Store({
  modules:{
    //挂载购物车的vuex模块,模块内成员的访问路径被调整为m_cart,例如:
    //购物车模块中cart 数组的访问路径是 m_cart/cart
    'm_cart': moduleCart,
    'm_user': moduleUser
  }
})

export default store

在my-address.vue调用

  import { mapState, mapMutations, mapGetters } from 'vuex'

 ...mapMutations('m_user',['updateAddress']),
       async chooseAddress() {
          //1调用小程序提供的chooseAddress()方法,即可使用选中收货地址的功能
          //返回值是一个数组:第1项为错误对象,第2项为成功之后的收货地址对象
          const [err, succ] = await uni.chooseAddress().catch(err => err)
          //2.用户成功的选择了收货地址
          if (err === null && succ.errMsg === 'chooseAddress:ok') {
            //为data里面 的收货地址对象赋值
            // this.address = succ
            this.updateAddress(succ)
          }

        }

  //计算属性
    computed:{
      ...mapState('m_user',['address']),
      addstr(){
        if(!this.address.provinceName) {
          return ''
        } 
        return this.address.provinceName + this.address.cityName + this.address.countyName + this.address.detailInfo
      }
    }

4)将Store中的address持久化存储到本地

user.js中配置

export default{
  //开启命名空间
  namespaced: true,
  //数据
  state: () =>({
    // address: {}
    address: JSON.parse(uni.getStorageSync('address') || '{}')
  }),
  //方法
  mutations: {
    //更改收货地址
    updateAddress(state,address){
      state.address = address
      this.commit('m_user/saveAddressToStorage')
    },

    //持久化存储 address
    saveAddressToStorage(state){
      uni.setStorageSync('address',JSON.stringify(state.address))
    }

  },
  getters: {}
}

5)将addstr抽离为getters

user.js

 getters: {
    addstr(state){
      if(!state.address.provinceName) {
        return ''
      } 
      return state.address.provinceName + state.address.cityName + state.address.countyName + state.address.detailInfo
    }
  }

 my-address.vue

  import { mapState, mapMutations, mapGetters } from 'vuex'

//计算属性
    computed:{
      ...mapState('m_user',['address']),
      ...mapGetters('m_user',['addstr'])
    }
    

6)重新选择收货地址

<!-- 渲染收货信息的盒子 -->
    <view class="address-info-box" v-else  @click="chooseAddress">

8.结算组件

my-settle.vue

1)渲染

  <view class="my-settle-container">
    <!-- 全选 -->
    <label class="radio">
      <radio color="#C00000" /><text></text>
    </label>
    
    <!-- 合计 -->
    <view class="amount-box">
        合计:<text class="amount">¥123.00</text>
    </view>
    
    <!-- 结算按钮 -->
    <view class="btn-settle">结算(0)</view>
  </view>

2)动态渲染已勾选商品的总数量

cart.js中配置

 getters: {
    total(state){
      let c = 0
      state.cart.forEach(x => c += x.goods_count)
      return c
    },
    checkedCount(state){
      return  state.cart.filter(x => x.goods_state).reduce((total,item) =>  total += item.goods_count ,0)
    }

  }

my-settle.vue

<!-- 结算按钮 -->
    <view class="btn-settle">结算({{checkedCount}})</view>

import { mapGetters } from 'vuex'
  export default {
  
    computed: {
      ...mapGetters('m_cart',['checkedCount'])
    }
  }

3)动态渲染全选按钮的选中状态

my-settle.vue

 computed: {
      ...mapGetters('m_cart',['checkedCount','total']),
      isFullCheck(){
        return this.total === this.checkedCount
      }

4)使用数组的reduce方法改造total

cart.js

 //购物车中所有商品的总数量
    total(state){
      // let c = 0
      // state.cart.forEach(x => c += x.goods_count)
      // return c
     return state.cart.reduce((total,item) => total += item.goods_count,0)
    },

5)实现全选和反选功能

cart.js

//更新购物车中所有商品的勾选状态
    updateAllGoodsState(state,newState){
      state.cart.forEach(x => x.goods_state = newState)
      this.commit('m_cart/saveToStorage')
    }

my-settle.vue

<label class="radio" @click="changeAllState">

import { mapGetters,mapMutations } from 'vuex'

 methods:{
      ...mapMutations('m_cart',['updateAllGoodsState']),
      changeAllState(){
        this.updateAllGoodsState(!this.isFullCheck)
      }
    }

 6)动态渲染价格

cart.js

 //已勾选商品的总价格
    checkedGoodsAmount(state){
      return state.cart.filter(x => x.goods_state).reduce((total,item) => total += item.goods_count * item.goods_price,0).toFixed(2)
    }
    

my-settle.vue

    <!-- 合计 -->
    <view class="amount-box">
        合计:<text class="amount">¥{{checkedGoodsAmount}}</text>
    </view>

 computed: {
      ...mapGetters('m_cart',['checkedCount','total','checkedGoodsAmount']),
      isFullCheck(){
        return this.total === this.checkedCount
      }
    },

7)让购物车图标正确显示物品数量

tabbar-badge.js

 //计算属性
    computed: {
        ...mapGetters('m_cart',['total'])
    },
    watch:{
      total(){
        this.setBadge()
      }

    },

9.渲染购物车为空时的页面

 <view class="cart-container" v-if="cart.length !== 0">

<!-- 空白购物车的区域 -->
 <view class="empty-cart" v-else
      <image src="/static/cart_empty@2x.png" class="empty-img"></image>
      <text class="tip-text">空空如也~</text>
      
  </view>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1559719.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣 1143. 最长公共子序列

题目来源&#xff1a;https://leetcode.cn/problems/longest-common-subsequence/description/ C题解&#xff08;思路来源代码随想录&#xff09;&#xff1a;动态规划。 1. 确定dp数组&#xff08;dp table&#xff09;以及下标的含义 dp[i][j]&#xff1a;长度为[0, i - 1]…

Python之Opencv进阶教程(1):图片模糊

1、Opencv提供了多种模糊图片的方法 加载原始未经模糊处理的图片 import cv2 as cvimg cv.imread(../Resources/Photos/girl.jpg) cv.imshow(girl, img)1.1 平均值 关键代码 # Averaging 平均值 average cv.blur(img, (3, 3)) cv.imshow(Average Blur, average)实现效果 1.2…

备战蓝桥杯---贪心刷题1

话不多说&#xff0c;直接看题&#xff1a; 本质是一个数学题&#xff1a; 我们令xi<0表示反方向传递&#xff0c;易得我们就是求每一个xi的绝对值之和min,我们令平均值为a爸。 易得约束条件&#xff1a; x1-x2a1-a,x2-x3a2-a..... 解得x1x1-0,x2x1-((n-1)*a-a2-...an)。…

通过搜索引擎让大模型获取实时数据-实现类似 perplexity 的效果

文章目录 一、前言二、初衷三、实现方式四、总结 一、前言 汇报一下这周末的工作&#xff0c;主要是开发了一门课程&#xff1a;通过搜索引擎让大模型获取实时数据&#xff0c;第一次开发一门课程&#xff0c;难免会有很多不熟悉和做的不好的地方。 已经训练好的大模型有气数…

今天起,Windows可以一键召唤GPT-4了

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了人工智能中文站https://ai.weoknow.com 每天给大家更新可用的国内可用chatGPT资源 发布在https://it.weoknow.com 更多资源欢迎关注 微软 AI 大计的最后一块拼图完成了&#xff1f; 把 Copilot 按钮放在 Window…

【Linux】权限的基本概念

在本篇博客中&#xff0c;作者将会讲解在linux系统中&#xff0c;权限的基本概念。 一.什么是权限 通俗的讲&#xff0c;权限是用来约束人的。比如说&#xff1a;你买了某软件的vip会员&#xff0c;那么你就可以执行相对操作&#xff0c;如果你没买&#xff0c;则就会有权限约束…

Linux的中间件

我们先补充点关于awk的内容 awk的用法其实很广。 $0 表示整条记录 变量&#xff1a; NF 一行中有多少个字段&#xff08;表示字段数&#xff09; NR &#xff1a; 代表当前记录的序号&#xff0c;从1开始计数。每读取一条记录&#xff0c;NR的值就会自动增加1。&#xff08;…

基于ssm旅游资源网站(java项目+文档+源码)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的旅游资源网站。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 旅游资源网站的主要使用者分为管理…

稀碎从零算法笔记Day35-LeetCode:字典序的第K小数字

要考虑完结《稀碎从零》系列了哈哈哈 这道题和【LC.42 接雨水】&#xff0c;我愿称之为【笔试界的颜良&文丑】 题型&#xff1a;字典树、前缀获取、数组、树的先序遍历 链接&#xff1a;440. 字典序的第K小数字 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1…

Pytorch 下载失败原因

错误信息&#xff1a; ERROR: Could not find a version that satisfies the requirement torch (from versions: none) ERROR: No matching distribution found for torch 解决方案&#xff1a; 在官网看到&#xff0c;它需要python3.8-3.11的环境。过高和过低的版本都不…

番外篇 | 手把手教你如何用YOLOv8实现行人/车辆等过线统计

前言:Hello大家好,我是小哥谈。目标检测行人/车辆等过线统计是一种常见的视频分析任务,用于统计行人/车辆等在指定区域内过线的次数。这个任务通常需要使用目标检测算法来识别行人/车辆等,并使用计数器算法来统计过线的次数。🌈 目录 🚀1.本文介绍 🚀2.实现

LeetCode刷题【链表,图论,回溯】

目录 链表138. 随机链表的复制148. 排序链表146. LRU 缓存 图论200. 岛屿数量994. 腐烂的橘子207. 课程表 回溯 链表 138. 随机链表的复制 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节…

2024年泰迪杯数据挖掘B题详细思路代码文章教程

目前b题已全部更新包含详细的代码模型和文章&#xff0c;本文也给出了结果展示和使用模型说明。 同时文章最下方包含详细的视频教学获取方式&#xff0c;手把手保姆级&#xff0c;模型高精度&#xff0c;结果有保障&#xff01; 分析&#xff1a; 本题待解决问题 目标&#…

K8S之Secret的介绍和使用

Secret Secret的介绍Secret的使用通过环境变量引入Secret通过volume挂载Secret Secret的介绍 Secret是一种保护敏感数据的资源对象。例如&#xff1a;密码、token、秘钥等&#xff0c;而不需要把这些敏感数据暴露到镜像或者Pod Spec中。Secret可以以Volume或者环境变量的方式使…

Valkey是一个新兴的开源项目,旨在成为Redis的替代品,背后得到了AWS、Google、Oracle支持

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

金融汽车科技LLM

汇丰银行 众安保险 1. AIGC重塑保险价值链 小额高频 2.构建智能应用的技术方案演进 增加微服务 长记忆&#xff1a;向量库短记忆&#xff1a;对话历史&#xff0c;思考路径&#xff0c;执行历史 中台架构设计 蔚来汽车在大模型的应用实践 公司介绍 应用架构 应用实践 4.大…

每日面经分享(pytest入门)

1. pytest具有什么功能 a. 自动发现和执行测试用例&#xff1a;pytest可以自动发现项目中的测试文件和测试函数&#xff0c;无需手动编写测试套件或测试运行器。 b. 丰富的断言函数&#xff1a;pytest提供了丰富的断言函数&#xff0c;方便地验证测试结果是否符合预期。断言函…

SpringBoot + Vue3邮件验证码功能的实现

后端 SpringBootmavenmysqlIDEA 后端负责编写邮件发送的接口逻辑&#xff0c;具体流程如下: 引入相关依赖配置邮箱信息编写邮件发送服务接口OK 引入依赖 <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-mail --> <dependen…

论文笔记:GEOLLM: EXTRACTING GEOSPATIALKNOWLEDGE FROM LARGE LANGUAGE MODELS

ICLR 2024 reviewer 评分 35668 1 intro 1.1 地理空间预测 地理空间预测在各个领域都有广泛的应用 包括贫困估算&#xff0c;公共卫生&#xff0c;粮食安全&#xff0c;生物多样性保护&#xff0c;环境保护。。。这些预测中使用的变量包括地理坐标、遥感数据、卫星图像、人类…

手机无线投屏到windows11电脑

1 安装无线投影组件 2 电脑端打开允许其他设备投影的开关 3 手机找到投屏选项 4 手机搜索可用设备连接即可 这里的官方文档给的不太好,给了一些让人眼花撩乱的信息,以下是经过整合的有效信息