• 【vue基础】黑马vue视频笔记(五)


    用vue做一个购物车案例

    涉及知识点

    1. 组件基本使用
    2. vue指令(v-text、v-bind、v-on、v-for)
    3. 计算属性
    4. 过滤器函数
    5. 生命周期函数
    6. axios
    7. 父子组件,兄弟组件相互传信息(自定义属性,自定义事件,eventBus)

    在这里插入图片描述

    //APP.vue
    <template>
      <div class="app-container">
        <Header title="购物车案例">Header>
        
        <Goods
          v-for="item in list"
          :key="item.id"
          :id="item.id"
          :title="item.goods_name"
          :pic="item.goods_img"
          :price="item.goods_price"
          :state="item.goods_state"
          :count="item.goods_count"
          @state-change="getNewState"
        >Goods>
        
        <Footer
          :isAllchecked="isAllstate"
          @all-change="getAllState"
          :amount="amt"
          :all="total"
        >Footer>
      div>
    template>
    
    <script>
    // 1.导入eventBus.js
    import bus from '@/components/eventBus.js'
    // 1.1安装npm i -S axios 导入
    import axios from 'axios'
    import Header from '@/components/Header/Header.vue'
    import Footer from '@/components/Footer/Footer.vue'
    import Goods from '@/components/Goods/Goods.vue'
    export default {
      data() {
        return {
          // 存放商品信息。默认为空
          list: [],
        }
      },
      computed: {
        isAllstate() {
          return this.list.every((item) => item.goods_state)
        },
        // 已经勾选商品的总价格
        // 1.先filter过滤
        // 2.再reduce累加
        amt() {
          return this.list
            .filter((item) => item.goods_state)
            .reduce((t, item) => (t += item.goods_price * item.goods_count), 0)
        },
        // 已经勾选商品的总数量
        total() {
          return this.list
            .filter((item) => item.goods_state)
            .reduce((t, item) => (t += item.goods_count), 0)
        },
      },
      components: {
        Header,
        Footer,
        Goods,
      },
      methods: {
        // 1.2封装请求列表数据的方法
        async initCartList() {
          const { data: res } = await axios.get('https://www.escook.cn/api/cart')
          if (res.status === 200) {
            this.list = res.list
            // console.log(res.list)
          }
        },
        // 接收子组件传来单个选中信息
        // e的格式为{id,value}
        getNewState(e) {
          this.list.some((item) => {
            // 查找修改state的id是哪一个
            if (item.id === e.id) {
              item.goods_state = e.value
              // 终止后续循环
              return true
            }
          })
        },
        // 接收子组件传来的全选状态
        getAllState(val) {
          this.list.forEach((item) => (item.goods_state = val))
        },
      },
      //  1.3 在created调用请求数据的方法
      created() {
        // 1.3 调用请求数据的方法,而且如果渲染需要使用,要保存到data
        this.initCartList()
        //
        bus.$on('numShare', (obj) => {
          this.list.some((item) => {
            if (item.id === obj.id) {
              item.goods_count = obj.value
              return true
            }
          })
        })
      },
    }
    script>
    
    <style lang="less" scoped>
    .app-container {
      padding-top: 45px;
      padding-bottom: 50px;
    }
    style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    //  Header组件
    <template>
      <div class="header-container">{{ title }}div>
    template>
    
    <script>
    export default {
      props: ['title'],
      data() {
        return {}
      },
      methods: {},
    }
    script>
    
    <style lang="less" scoped>
    .header-container {
      font-size: 12px;
      height: 45px;
      width: 100%;
      background-color: #1d7bff;
      display: flex;
      justify-content: center;
      align-items: center;
      color: #fff;
      position: fixed;
      top: 0;
      z-index: 999;
    }
    style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    // Goods组件
    <template>
      <div class="goods-container">
        
        <div class="thumb">
          <div class="custom-control custom-checkbox">
            
            <input
              type="checkbox"
              class="custom-control-input"
              :id="'cd' + id"
              :checked="state"
              @change="stateChange"
            />
            
            <label class="custom-control-label" :for="'cd' + id">
              
              <img :src="`${pic}`" alt="" />
            label>
          div>
        div>
        
        <div class="goods-info">
          
          <h6 class="goods-title">{{ title }}h6>
          <div class="goods-info-bottom">
            
            <span class="goods-price">¥{{ price }}span>
            
            <Counter :num="count" :id="id">Counter>
          div>
        div>
      div>
    template>
    
    <script>
    import Counter from '@/components/Counter/Counter.vue'
    export default {
      props: {
        // 商品的 id
        // 为啥在这里要封装一个 id 属性呢?
        // 原因:将来,子组件中商品的勾选状态变化之后, 需要通过子 -> 父的形式,
        // 通知父组件根据 id 修改对应商品的勾选状态。
        id: {
          required: true,
          type: Number,
        },
        // 要渲染的商品的标题
        title: {
          default: '',
          type: String,
        },
        // 要渲染的商品的图片
        pic: {
          default: '',
          type: String,
        },
        // 商品的单价
        price: {
          default: 0,
          type: Number,
        },
        // 商品的勾选状态
        state: {
          default: true,
          type: Boolean,
        },
        // 商品的购买数量
        count: {
          type: Number,
          default: 1,
        },
      },
      components: {
        Counter,
      },
      methods: {
        // 复选框选中状态发生了变化,就调用函数
        stateChange(e) {
          // this.state = !this.state
          // console.log(this.state)
          // props值 不可修改
          // target 属性规定哪个 DOM 元素触发了该事件。不会冒泡
          const newState = e.target.checked
          // 用自定义事件方法 向父组件传递信息
          this.$emit('state-change', { id: this.id, value: newState })
        },
      },
    }
    script>
    
    <style lang="less" scoped>
    .goods-container {
      + .goods-container {
        border-top: 1px solid #efefef;
      }
      padding: 10px;
      display: flex;
      .thumb {
        display: flex;
        align-items: center;
        img {
          width: 100px;
          height: 100px;
          margin: 0 10px;
        }
      }
    
      .goods-info {
        display: flex;
        flex-direction: column;
        justify-content: space-between;
        flex: 1;
        .goods-title {
          font-weight: bold;
          font-size: 12px;
        }
        .goods-info-bottom {
          display: flex;
          justify-content: space-between;
          .goods-price {
            font-weight: bold;
            color: red;
            font-size: 13px;
          }
        }
      }
    }
    style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    //counter
    <template>
      <div
        class="number-container d-flex justify-content-center align-items-center"
      >
        
        <button
          type="button"
          class="btn btn-light btn-sm"
          @click="sub"
          :disabled="isOk"
        >
          -
        button>
        
        <span class="number-box">{{ num }}span>
        
        <button type="button" class="btn btn-light btn-sm" @click="add">+button>
      div>
    template>
    
    <script>
    // 1.导入eventBus.js
    import bus from '@/components/eventBus.js'
    export default {
      props: {
        id: {
          type: Number,
          require: 1,
        },
        num: {
          type: Number,
          default: 1,
        },
      },
      data() {
        return {
          isOk: false,
        }
      },
      methods: {
        sub() {
          if (this.num <= 1) {
            this.isOk = true
            return
          }
          const obj = {
            id: this.id,
            value: this.num - 1,
          }
          bus.$emit('numShare', obj)
        },
        // 点击按钮,数量加一
        add() {
          // 将更新之后的数据传送给app组件(异性兄弟节点),格式{id,value},用eventBus方法
          const obj = {
            id: this.id,
            value: this.num + 1,
          }
          this.isOk = false
          bus.$emit('numShare', obj)
        },
      },
      created() {
        if (this.num === 1) this.isOk = true
      },
    }
    script>
    
    <style lang="less" scoped>
    .number-box {
      min-width: 30px;
      text-align: center;
      margin: 0 5px;
      font-size: 12px;
    }
    
    .btn-sm {
      width: 30px;
    }
    style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    // Footer组件
    <template>
      <div class="footer-container">
        
        <div class="custom-control custom-checkbox">
          <input
            type="checkbox"
            class="custom-control-input"
            id="cbFull"
            :checked="isAllchecked"
            @change="allChange"
          />
          <label class="custom-control-label" for="cbFull">全选label>
        div>
    
        
        <div>
          <span>合计:span>
          <span class="total-price">¥{{ amount }}span>
        div>
    
        
        <button type="button" class="btn btn-primary btn-settle">
          结算({{ all }})
        button>
      div>
    template>
    
    <script>
    export default {
      props: {
        isAllchecked: {
          default: true,
          type: Boolean,
        },
        amount: {
          default: 0,
          type: Number,
        },
        all: {
          default: 0,
          type: Number,
        },
      },
      methods: {
        // 利用自定义事件向父组件传递全选变化状态
        allChange(e) {
          this.$emit('all-change', e.target.checked)
        },
      },
    }
    script>
    
    <style lang="less" scoped>
    .footer-container {
      font-size: 12px;
      height: 50px;
      width: 100%;
      border-top: 1px solid #efefef;
      position: fixed;
      bottom: 0;
      background-color: #fff;
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 0 10px;
    }
    
    .custom-checkbox {
      display: flex;
      align-items: center;
    }
    
    #cbFull {
      margin-right: 5px;
    }
    
    .btn-settle {
      height: 80%;
      min-width: 110px;
      border-radius: 25px;
      font-size: 12px;
    }
    
    .total-price {
      font-weight: bold;
      font-size: 14px;
      color: red;
    }
    style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
  • 相关阅读:
    腾讯内部疯传MyBatis实践指南,让味同嚼蜡的知识生动有趣
    vector 模拟与用法
    可视化全链路日志追踪
    怎么处理zk或redis脑裂
    Python 全栈系列211 自建容器仓库
    RabbitMQ(五)【入门案例】
    阿里云 CLI相关使用笔记
    数据挖掘技术-转换字符串时间为标准时间
    KNN算法推理与实现
    Redis入门完整教程:客户端案例分析
  • 原文地址:https://blog.csdn.net/weixin_51233575/article/details/125894519