Vue里面的多选反选单选问题

题目描述

完成购物车,完成对应商品的选择和取消的功能

题目来源及自己的思路

《Vue.js实战》第五章

相关代码

// 请把代码文本粘贴到下方(请勿用图片代替代码)

<!DOCTYPE html>
<html lang="cmn-hans">

<head>
    <meta charset="utf-8">
    <meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1">
    <!--PC端必选   强制360浏览器使用webkit渲染 -->
    <meta name="renderer" content="webkit">
    <!--必选  for PC -->
    <link rel="stylesheet" type="text/css"  href="css.css">
    <title>购物车</title>
</head>

<body>
    <div>
        <p>购物车需求</p>
        <ol>
            <li>展示已经加入购物车的商品列表</li>
            <li>商品列表包含商品名称,商品单价,购买数量和操作能力</li>
            <li>能够实时显示所购买的总价</li>
            <li>商品数量可以增加,减少或者直接移除</li>
        </ol>

    </div>
    <hr>
    <!-- 页面挂载点 -->
    <div id="app" v-cloak>
        <template v-if="goods.length">
            <table>
                <thead>
                    <tr>
                        <td>表头</td>
                        <td>商品名称</td>
                        <td>商品单价</td>
                        <td>购买数量</td>
                        <td>操作</td>
         <td>选择 | 全选<input type="checkbox" @click="selectAll" id="selectAll" :checked="isCheckedAll"></td>
                    </tr>
                </thead>
                <tbody>
                    <tr v-for="good,index in goods">
                        <td>{{index+1}}</td>
                        <td>{{good.name}}</td>
                        <td>{{good.price}}</td>
                        <!-- 数量调整 -->
                        <td>
                            <button @click="reduce(index)" :disabled="good.count === 1">-</button>
                            {{good.count}}
                            <button @click="add(index)">+</button>
                        </td>
                        <!-- 操作 -->
                        <td>
                            <button @click="remove(index)">移除</button>
                        </td>
                        <td>
             <input type="checkbox" @click="select(index,$event)" class="select">
                        </td>
                    </tr>
                </tbody>
            </table>
            <div><p> 全部总价:¥{{totalPrice}}</p>

                    <p>选中总价: ¥{{selectPrice}}</p>

            </div>
        </template>
        <div v-else>购物车是空的</div>
    </div>
    <script src="https://unpkg.com/vue/dist/vue.min.js"></script>
    <script src="trolley.js"></script>
</body>

</html>

css.css:

table {
    border: 1px solid #ccc;
}

thead {
    background-color: #ddd;
}

td {
    border: 1px solid #ccc;
}

trolley.js:

var app = new Vue({
    el: '#app',
    data: {
        //商品数据
        goods: [
            { id: 1, name: 'iphone 7', price: 6188, count: 1 },
            { id: 2, name: 'ipad Pro', price: 5888, count: 1 },
            { id: 3, name: 'MacBook Pro', price: 21488, count: 1 }
        ],
        selectPrice: 0,
        isCheckedAll: false ,//初始化没有全选择
        isChecked:false
    },
    computed: {
        totalPrice: function() {
            var total = 0;
            for (var i = 0; i < this.goods.length; i++) {
                total += this.goods[i].price * this.goods[i].count;
            }
            return total;
        }
    },
    methods: {
        reduce: function(index) {
            //再次判断 reduce 减法的可靠性
            if (this.goods[index].count === 1) return;
            this.goods[index].count--;
        },
        add: function(index) {
            this.goods[index].count++;
        },
        remove: function(index) {
            //删除一条数组数据
            this.goods.splice(index, 1);
        },
        selectAll: function() {
            if (this.isCheckedAll) {

                for (let i = 0; i < this.goods.length; i++) {
                    document.querySelectorAll(".select")[i].checked = false;
                }
            } else {

                for (let i = 0; i < this.goods.length; i++) {
                    document.querySelectorAll(".select")[i].checked = true;
                }
            }
            this.isCheckedAll = !this.isCheckedAll;
        },
        select: function(index, event) {

            if (document.querySelectorAll(".select")[index].checked) {
                document.querySelectorAll(".select")[index].checked = true;

                //逐步全部选中及后续






            } else {
                document.querySelectorAll(".select")[index].checked = false;


                      //逐步全部不选中及后续

            }
        }
    },
});

你期待的结果是什么?实际看到的错误信息又是什么?

目前完成了单选和全选功能,但是实际在操作过程中应该在逐个选中之后,全选按钮选中,和逐个取消选中之后,全选按钮最后也取消选中状态,没有完成。
我觉得思路应该是每次单选/取消单选的时候都要循环判断全部的checkbox是全部选中/全部不选中,然后再设置全选按钮的状态。但是不知道应该怎么写。

阅读 5.6k
5 个回答

其他答案提到,需要使用computed,这是正确的,因为是否isCheckedAll是需要“实时”查看各个check的值的变化的,故使用computed比较适合。
顺便简化了一下你的代码,第一可以将每个物品的选中与否设计成goods的字段,这样逻辑清楚些;第二 select和selectAll方法重写了下,以供参考。
代码如下:

<!DOCTYPE html>
<html lang="cmn-hans">

<head>
  <meta charset="utf-8">
  <meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1">
  <!--PC端必选   强制360浏览器使用webkit渲染 -->
  <meta name="renderer" content="webkit">
  <title>购物车</title>
  <style>
    table {
      border: 1px solid #ccc;
    }

    thead {
      background-color: #ddd;
    }

    td {
      border: 1px solid #ccc;
    }

  </style>
</head>

<body>
<div>
  <p>购物车需求</p>
  <ol>
    <li>展示已经加入购物车的商品列表</li>
    <li>商品列表包含商品名称,商品单价,购买数量和操作能力</li>
    <li>能够实时显示所购买的总价</li>
    <li>商品数量可以增加,减少或者直接移除</li>
  </ol>

</div>
<hr>
<!-- 页面挂载点 -->
<div id="app" v-cloak>
  <template v-if="goods.length">
    <table>
      <thead>
      <tr>
        <td>表头</td>
        <td>商品名称</td>
        <td>商品单价</td>
        <td>购买数量</td>
        <td>操作</td>
        <td>选择 | 全选<input type="checkbox" @click="selectAll" id="selectAll" :checked="isCheckedAll"></td>
      </tr>
      </thead>
      <tbody>
      <tr v-for="good,index in goods">
        <td>{{index+1}}</td>
        <td>{{good.name}}</td>
        <td>{{good.price}}</td>
        <!-- 数量调整 -->
        <td>
          <button @click="reduce(index)" :disabled="good.count === 1">-</button>
          {{good.count}}
          <button @click="add(index)">+</button>
        </td>
        <!-- 操作 -->
        <td>
          <button @click="remove(index)">移除</button>
        </td>
        <td>
          <input type="checkbox" @click="select(index)" class="select" :checked="good.isChecked">
        </td>
      </tr>
      </tbody>
    </table>
    <div><p> 全部总价:¥{{totalPrice}}</p>

      <p>选中总价: ¥{{selectPrice}}</p>

    </div>
  </template>
  <div v-else>购物车是空的</div>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script>
  var app = new Vue({
    el: '#app',
    data: {
      //商品数据
      goods: [
        { id: 1, name: 'iphone 7', price: 6188, count: 1, isChecked: false },
        { id: 2, name: 'ipad Pro', price: 5888, count: 1, isChecked: false },
        { id: 3, name: 'MacBook Pro', price: 21488, count: 1, isChecked: false }
      ],
      selectPrice: 0,
      isChecked:false
    },
    computed: {
      totalPrice: function() {
        var total = 0;
        for (var i = 0; i < this.goods.length; i++) {
          total += this.goods[i].price * this.goods[i].count;
        }
        return total;
      },
      // 是否全选
      isCheckedAll: function () {
        var flag = true;
        this.goods.forEach(function (item, index) {
          if (!item.isChecked) flag = false;
        })
        return flag;
      }
    },
    methods: {
      reduce: function(index) {
        //再次判断 reduce 减法的可靠性
        if (this.goods[index].count === 1) return;
        this.goods[index].count--;
      },
      add: function(index) {
        this.goods[index].count++;
      },
      remove: function(index) {
        //删除一条数组数据
        this.goods.splice(index, 1);
      },
      selectAll: function() {
        this.goods.forEach(function (item, index) {
          item.isChecked = !item.isChecked;
        });
      },
      select: function(index) {
        this.goods[index].isChecked = !this.goods[index].isChecked;
      }
    },
  });
</script>
</body>

</html>

用watch或者computed

给你点个赞,问题步骤很清晰。
你可以使用watch或者computed监听单选按钮的state,从而设置全选按钮的选中与否。

var app = new Vue({
    el: '#app',
    data: {
        //商品数据
        goods: [
            { id: 1, name: 'iphone 7', price: 6188, count: 1 },
            { id: 2, name: 'ipad Pro', price: 5888, count: 1 },
            { id: 3, name: 'MacBook Pro', price: 21488, count: 1 }
        ],
        selectPrice: 0,
        isChecked:false,
        selectedList: []
    },
    computed: {
        totalPrice: function() {
            var total = 0;
            for (var i = 0; i < this.goods.length; i++) {
                total += this.goods[i].price * this.goods[i].count;
            }
            return total;
        },
        isCheckedAll(){
            return this.selectedList.length === this.goods.length
        }
    },
    methods: {
        reduce: function(index) {
            //再次判断 reduce 减法的可靠性
            if (this.goods[index].count === 1) return;
            this.goods[index].count--;
        },
        add: function(index) {
            this.goods[index].count++;
        },
        remove: function(index) {
            //删除一条数组数据
            this.goods.splice(index, 1);
        },
        selectAll: function() {
            if (this.isCheckedAll) {

                for (let i = 0; i < this.goods.length; i++) {
                    document.querySelectorAll(".select")[i].checked = false;
                }
                this.selectedList = []
            } else {
                                this.selectedList = []
                for (let i = 0; i < this.goods.length; i++) {
                    document.querySelectorAll(".select")[i].checked = true;
                    this.selectedList.push(i)
                }
            }
        },
        select: function(index, event) {
            let isSelected = document.querySelectorAll(".select")[index].checked
          let selectedIndex = this.selectedList.indexOf(index)
          
          if (isSelected && selectedIndex <= -1) {
                this.selectedList.push(index)
          } else if (!isSelected && selectedIndex > -1){
                this.selectedList.splice(selectedIndex, 1)
          }
        }
    },
});

之前回答别人的问题,Vue 全选 单选问题

既然用Vue了就操作数据,尽量不操作DOM
写了个小demo Vue 全选单选问题,右击查看源码,可供参考。
<template>
    <div id="app">
        <a href="https://segmentfault.com/q/1010000012675796?_ea=3106401" target="_blank">SF上的Vue 全选 单选问题</a>
        <ul>
            <li class="checked-item" @click="singleSelect(row, index)" v-for="(row, index) in goods">
                <input type="checkbox" :checked="row.checked"/>{{row.name}}
            </li>
        </ul>
        <div>
            <span @click="allSelect">
                <input type="checkbox" :checked="isAllSelected"/>
                <button>全选</button>
            </span>
            <span class="checked-count" v-if="checkedGoodIds.length > 0">已选择:{{checkedGoodIds.length}}项</span>
        </div>
    </div>
</template>
<script>
    new Vue({
        data: {
            goods: [{
                id: 1,
                name: '选项1-xuanyuan1',
                checked: false
            }, {
                id: 2,
                name: '选项2-xuanyuan2',
                checked: false
            }, {
                id: 3,
                name: '选项3-xuanyuan3',
                checked: false
            }, {
                id: 4,
                name: '选项4-xuanyuan4',
                checked: false
            }, {
                id: 5,
                name: '选项5-xuanyuan5',
                checked: false
            }, {
                id: 6,
                name: '选项6-xuanyuan6',
                checked: false
            }],
        },
        computed:{
            // 是否全选
            isAllSelected(){
                return this.goods.every((el) => {
                    return el.checked;
                })
            },
            // 选中商品的id
            checkedGoodIds(){
                let filterArr = this.goods.filter((el) => {
                    return el.checked;
                });
                return filterArr.map((el) => {
                    return el.id;
                })
            }
        },
        methods: {
            // 全选、全不选
            allSelect() {
                let checked = true;
                // 全选
                if(this.isAllSelected){
                    checked = false;
                }
                this.goods = this.goods.map(el => {
                    el.checked = checked;
                    return el;
                })
            },
            // 单选
            singleSelect(row, index) {
                row.checked = !row.checked;
                this.goods.splice(index, 1, row);
            }
        }
    }).$mount('#app')
</script>
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题