vue--实现简易版的购物车

2022-10-04 12:09:18

目录

1.先搭好页面的基本布局:

2.渲染表格里面的内容

 3.实现全选功能

4.实现数量的加减功能

5.实现点击删除功能

6. 显示总价


在这个案例里我们把表格里面的数据也就是 tr进行封装成一个组件,然后通过 v-for 循环进行重复利用

大致步骤如下:

1.先搭好页面的基本布局:

将头部和尾部部分在 App.vue 里进行基本的搭建

需要单独封装处理的 tr 封装成 MyTr.vue 组件,然后在 App.vue 里进行注册和引入,这里需要注意的是:DOM模板限制, tbody里必须是tr标签, 所以使用组件就得用is属性执行组件名字,因此不能直接像 <MyTr></MyTr> 这样使用

 App.vue,

<template>
  <div>
    <table border="1"
           width="700"
           style="border-collapse: collapse">
      <thead>
        <tr>
          <th>
            <input type="checkbox" />
            <span>全选</span>
          </th>
          <th>名称</th>
          <th>价格</th>
          <th>数量</th>
          <th>总价</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody>
        <!-- <MyTr></MyTr> -->
        <!-- DOM模板限制, tbody>必须是tr标签, 所以使用组件就得用is属性执行组件名字 -->
        <tr is="MyTr"></tr>
      </tbody>
      <tfoot>
        <tr>
          <td>合计:</td>
          <td colspan="5"></td>
        </tr>
      </tfoot>
    </table>
  </div>
</template>

<script>
import MyTr from './components/MyTr'
export default {
  name: 'App',
  components: {
    MyTr
  },
  data () {
    return {
      goodList: [
        {
          name: "诸葛亮",
          price: 1000,
          num: 1,
          checked: false,
        },
        {
          name: "蔡文姬",
          price: 1500,
          num: 1,
          checked: false,
        },
        {
          name: "妲己",
          price: 2000,
          num: 1,
          checked: false,
        },
        {
          name: "鲁班",
          price: 2200,
          num: 1,
          checked: false,
        },
      ],
    }
  }
}
</script>

MyTr.vue,

<template>
    <tr>
    <td>
      <input type="checkbox" />
    </td>
    <td></td>
    <td></td>
    <td>
      <button>-</button>
      <span></span>
      <button>+</button>
    </td>
    <td></td>
    <td>
      <button>删除</button>
    </td>
  </tr>
</template>

<script>
export default {
  name: 'MyTr'
}
</script>

最后大致的效果如下:

2.渲染表格里面的内容

在 App.vue 里的 tbody 标签的 tr 里通过 v-for 循环,然后需要将对象传给 tr 组件内部也就是 MyTr.vue 里

这里就需要用到组件间的传值--父传子,子类通过 props 进行接收

在提前准备好的 App.vue 里的数据 goodList 里面的 checked 就是用于记录当前的复选框是否处于勾选状态,因此在 MyTr.vue 里的 input 标签里我们需要利用 v-model 进行双向绑定

App.vue

 MyTr.vue

<template>
    <tr>
    <td>
      <input type="checkbox" v-model="obj.checked" />
    </td>
    <td>{{obj.name}}</td>
    <td>{{obj.price}}</td>
    <td>
      <button>-</button>
      <span>{{obj.num}}</span>
      <button>+</button>
    </td>
    <td>{{obj.price * obj.num}}元</td>
    <td>
      <button>删除</button>
    </td>
  </tr>
</template>

<script>
export default {
  name: 'MyTr',
  props: ["obj"],
}
</script>

最后效果如下:

 3.实现全选功能

这里 全选 按钮应该是一个计算属性,因为下面表格里的 小选框 们的状态会直接影响 全选 框

再者当我们选中 全选框 时,页面会把选中的  true/false 状态赋予给 isAll 这个变量,因此这里的 isAll 既要设置又要取值,就需要用到计算属性的完整写法,通过 set() 和 get()

当在页面点击 全选 复选框后,会把选中状态 true/false 赋予给 v-model 的变量 isAll,这里同时也会影响表格里的 小选框 的状态,而小选框里的状态都在 goodList 数组里的 checked 属性上存着的,因此我们需要做的就是当我们改变 全选框 的状态时就需要更新 goodList 数组

同理当前的这个 isAll 还要来源于我们 goodList 数组里的小选框最后的统计效果

App.vue

  computed: {
    isAll: {
      set(val){ 
        // 页面点击 全选 复选框后,会把选中状态 true/false 赋予给 v-model 的变量
        // 这里的 val 即 isAll,值为 true/false
        this.goodList.forEach(obj => { // 将 true/false 同步给所有小选框的状态值
          obj.checked = val
        });
      },
      get(){
        // every() 该函数所有一项返回true,则返回true。一旦有一项不满足则返回flase
        // 统计小选框的选中状态
        return this.goodList.every(obj => obj.checked == true)
      }
    }
  }

这里需要注意:计算属性里的变量名不能与data里的变量名重名

最后效果如下:

4.实现数量的加减功能

在 MyTr.vue 里给当前的 加减 按钮分别绑定点击事件

MyTr.vue

      <button @click="sub" :disabled="obj.num === 0">-</button>
      
      <button @click="add">+</button>
   
     add(){
      this.obj.num++
    },
    sub(){
      this.obj.num--
    }

最后效果如下:

5.实现点击删除功能

删除 按钮在 MyTr.vue 组件里,当点击 删除 时其实是删除数组里的某个元素,在 MyTr.vue 组件里实现不了,因此当点击 删除 时需要通过 子向父 传值,来通知 App.vue goodList 数组里面将对应下角标对应的对象删除

但是在 MyTr.vue 并没有 goodList 数组里的 索引,实现不了删除,因此需要在 App.vue 里将 index 索引传递给 MyTr.vue ,在 MyTr.vue 通过 props 接收

  • 父: @自定义事件名="父methods函数"

  • 子: this.$emit("自定义事件名", 传值) - 执行父methods里函数代码

App.vue

MyTr.vue

 最后效果如下:

6. 显示总价

统计 goodList 数组里的单价和数量相累计即可得到,因此总价也应该为计算属性

App.vue

  computed: {
        allPrice(){
      /**reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值
       * array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
       * total 	必需。初始值, 或者计算结束后的返回值。
         currentValue 	必需。当前元素
         currentIndex 	可选。当前元素的索引
         arr 	可选。当前元素所属的数组对象。
         initialValue 	可选。传递给函数的初始值
       */
      /**
       * 这里的 0 其实就是第一个参数 sum 的初始值
       * obj 遍历数组里面的每个对象
       */
      return this.goodList.reduce((sum, obj) => {
        if(obj.checked === true){
          sum += obj.price * obj.num
        }
        return sum
      }, 0)
    }
  },

 最后效果如下:

源码:

App.vue

<template>
  <div>
    <table border="1"
           width="700"
           style="border-collapse: collapse">
      <thead>
        <tr>
          <th>
            <input type="checkbox" v-model="isAll" />
            <span>全选</span>
          </th>
          <th>名称</th>
          <th>价格</th>
          <th>数量</th>
          <th>总价</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody>
        <!-- <MyTr></MyTr> -->
        <!-- DOM模板限制, tbody>必须是tr标签, 所以使用组件就得用is属性执行组件名字 -->
        <tr is="MyTr" v-for="(obj, index) in goodList" :key="index" :obj="obj" :index="index" @sub="subFn"></tr>
      </tbody>
      <tfoot>
        <tr>
          <td>合计:</td>
          <td colspan="5">
            {{allPrice}}
          </td>
        </tr>
      </tfoot>
    </table>
  </div>
</template>

<script>
import MyTr from './components/MyTr'
export default {
  name: 'App',
  components: {
    MyTr
  },
  data () {
    return {
      goodList: [
        {
          name: "诸葛亮",
          price: 1000,
          num: 1,
          checked: false,
        },
        {
          name: "蔡文姬",
          price: 1500,
          num: 1,
          checked: false,
        },
        {
          name: "妲己",
          price: 2000,
          num: 1,
          checked: false,
        },
        {
          name: "鲁班",
          price: 2200,
          num: 1,
          checked: false,
        },
      ],
    }
  },
  computed: {
    isAll: {
      set(val){ 
        // 页面点击 全选 复选框后,会把选中状态 true/false 赋予给 v-model 的变量
        // 这里的 val 即 isAll,值为 true/false
        this.goodList.forEach(obj => { // 将 true/false 同步给所有小选框的状态值
          obj.checked = val
        });
      },
      get(){
        // every() 该函数所有一项返回true,则返回true。一旦有一项不满足则返回flase
        // 统计小选框的选中状态
        return this.goodList.every(obj => obj.checked == true)
      }
    },
    allPrice(){
      /**reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值
       * array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
       * total 	必需。初始值, 或者计算结束后的返回值。
         currentValue 	必需。当前元素
         currentIndex 	可选。当前元素的索引
         arr 	可选。当前元素所属的数组对象。
         initialValue 	可选。传递给函数的初始值
       */
      /**
       * 这里的 0 其实就是第一个参数 sum 的初始值
       * obj 遍历数组里面的每个对象
       */
      return this.goodList.reduce((sum, obj) => {
        if(obj.checked === true){
          sum += obj.price * obj.num
        }
        return sum
      }, 0)
    }
  },
  methods: {
    subFn(index){ // 通过子组件传入 index 删除对应的数据
      this.goodList.splice(index, 1)
    }
  }
}
</script>

MyTr.vue

<template>
    <tr>
    <td>
      <input type="checkbox" v-model="obj.checked" />
    </td>
    <td>{{obj.name}}</td>
    <td>{{obj.price}}</td>
    <td>
      <button @click="sub" :disabled="obj.num === 0">-</button>
      <span>{{obj.num}}</span>
      <button @click="add">+</button>
    </td>
    <td>{{obj.price * obj.num}}元</td>
    <td>
      <button @click="delFn">删除</button>
    </td>
  </tr>
</template>

<script>
export default {
  name: 'MyTr',
  /**
   * obj变量本身是只读的, this.obj = {} 不行的, 
   * 但是你改变obj里的属性值是ok的
   * (而且对象是引用关系, 间接的 影响了外部数组里的对象)
   */
  props: ["obj", "index"],
  methods: {
    add(){
      this.obj.num++
    },
    sub(){
      this.obj.num--
    },
    delFn(){
      this.$emit('sub', this.index)
    }
  }
}
</script>
  • 作者:小白小白从不日白
  • 原文链接:https://blog.csdn.net/weixin_43285360/article/details/121866150
    更新时间:2022-10-04 12:09:18