前端学习——JavaScript原生实现购物车案例

2023-11-19

一. 购物车案例

1.1 案例介绍

今天我们来写另外一个购物车案例,说实话对于我来说这个是花了将近三个小时的时间然后才做出来的,里面可能还存在一些我没有发现的问题,但是能完成基本的功能,对于一些基本的需求都是可以完成的,下面照旧是案例实现的gif图片

在这里插入图片描述

根据上图我们可以看到,每个购物车的选项都是互不影响的,每个商店也都是互不影响的,单独运算,每个店的总计也是单独计算的,有一个计算复选框总价的功能,而且这是个通用的模板,不论有多少个店铺,店铺里面的商品有多少个,只要html的结构不变都是可以通用的,那么不多说,下面开始分析案例!

1.2 案例分析

下面给出相应的html结构的代码,便于我们进行分析

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="format-detection" content="telephone=no">
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0">
<title>购物车</title>


<link type="text/css" rel="stylesheet" href="css/base.css" />
<link type="text/css" rel="stylesheet" href="css/module.css"  />

</head>
<body>

<!--头部开始-->
<div class="header">
   <h1>购物车</h1>
   <a href="#" class="back"><span></span></a>
   <a href="#" class=""></a>
</div>
<!--头部结束-->
<div class="shopping">
   
   <div class="shop-group-item">
      <div class="shop-name">
         <input type="checkbox" class="check goods-check shopCheck">
         <h4><a href="#">苹果专卖店</a></h4>
         <div class="coupons"><span>领券</span><em>|</em><span>编辑</span></div>
      </div>
      <ul>
         <li>
            <div class="shop-info">
               <input type="checkbox" class="check goods-check goodsCheck">
               <div class="shop-info-img"><a href="#"><img src="images/computer.jpg" /></a></div>
               <div class="shop-info-text">
                  <h4>Apple MacBook Air 13.3英寸笔记本电脑 银色(Core i5 处理器/8GB内存/128GB SSD闪存 MMGF2CH/A)</h4>
                  <div class="shop-brief"><span>重量:3.3kg</span><span>颜色:银色</span><span>版本:13.3英寸</span></div>
                  <div class="shop-price">
                     <div class="shop-pices"><b class="price">5899.00</b></div>
                     <div class="shop-arithmetic">
                        <a href="javascript:;" class="minus">-</a>
                        <input class="num" value="1"/>
                        <a href="javascript:;" class="plus">+</a>
                     </div>
                  </div>
               </div>
            </div>
         </li>
         <li>
            <div class="shop-info">
               <input type="checkbox" class="check goods-check goodsCheck">
               <div class="shop-info-img"><a href="#"><img src="images/ipad.jpg" /></a></div>
               <div class="shop-info-text">
                  <h4>Apple iPad Pro 平板电脑 10.5 英寸(64G WLAN版/A10X芯片/Retina屏/Multi-Touch技术 MQDX2CH/A)金色</h4>
                  <div class="shop-brief"><span>重量:0.85kg</span><span>颜色:金色</span><span>版本:64G WLAN版</span></div>
                  <div class="shop-price">
                     <div class="shop-pices"><b class="price">4788.00</b></div>
                     <div class="shop-arithmetic">
                        <a href="javascript:;" class="minus">-</a>
                        <input class="num" value="1"/>
                        <a href="javascript:;" class="plus">+</a>
                     </div>
                  </div>
               </div>
            </div>
         </li>
         <li>
            <div class="shop-info">
               <input type="checkbox" class="check goods-check goodsCheck">
               <div class="shop-info-img"><a href="#"><img src="images/iphone.jpg" /></a></div>
               <div class="shop-info-text">
                  <h4>苹果 iPhone7 Plus 手机 红色特别版 全网通 128GB</h4>
                  <div class="shop-brief"><span>重量:0.18kg</span><span>颜色:红色特别版</span><span>版本:全网通 128G</span></div>
                  <div class="shop-price">
                     <div class="shop-pices"><b class="price">6326.00</b></div>
                     <div class="shop-arithmetic">
                        <a href="javascript:;" class="minus">-</a>
                        <input class="num" value="1"/>
                        <a href="javascript:;" class="plus">+</a>
                     </div>
                  </div>
               </div>
            </div>
         </li>
      </ul>
      <div class="shopPrice">本店总计:¥<span class="shop-total-amount ShopTotal">0.00</span></div>
   </div>
   
   <div class="shop-group-item">
      <div class="shop-name">
         <input type="checkbox" class="check goods-check shopCheck">
         <h4><a href="#">小米专卖店</a></h4>
         <div class="coupons"><span>领券</span><em>|</em><span>编辑</span><!--<span class="shop-total-amount ShopTotal">0</span>--></div>
      </div>
      <ul>
         <li>
            <div class="shop-info">
               <input type="checkbox" class="check goods-check goodsCheck">
               <div class="shop-info-img"><a href="#"><img src="images/computer.jpg" /></a></div>
               <div class="shop-info-text">
                  <h4>小米(MI)小米电视4A 标准版 65英寸 HDR 2GB+8GB 四核64位高性能处理器 4K超高清智能语音网络液晶平板电视机(L65M5-AZ)</h4>
                  <div class="shop-brief"><span>重量:16.3kg</span><span>颜色:黑色</span><span>版本:4A标准版</span></div>
                  <div class="shop-price">
                     <div class="shop-pices"><b class="price">5999.00</b></div>
                     <div class="shop-arithmetic">
                        <a href="javascript:;" class="minus">-</a>
                        <input class="num" value="1"/>
                        <a href="javascript:;" class="plus">+</a>
                     </div>
                  </div>
               </div>
            </div>
         </li>
         <li>
            <div class="shop-info">
               <input type="checkbox" class="check goods-check goodsCheck">
               <div class="shop-info-img"><a href="#"><img src="images/computer.jpg" /></a></div>
               <div class="shop-info-text">
                  <h4>小米(MI) 定制版Ninebot 九号平衡车 智能代步电动体感车(白)</h4>
                  <div class="shop-brief"><span>重量:15.9kg</span><span>颜色:白色</span><span>版本:标准版</span></div>
                  <div class="shop-price">
                     <div class="shop-pices"><b class="price">1949.00</b></div>
                     <div class="shop-arithmetic">
                        <a href="javascript:;" class="minus">-</a>
                        <input class="num" value="1"/>
                        <a href="javascript:;" class="plus">+</a>
                     </div>
                  </div>
               </div>
            </div>
         </li>
      </ul>
      <div class="shopPrice">本店总计:¥<span class="shop-total-amount ShopTotal">0.00</span></div>
   </div>
   <div class="shop-group-item">
      <div class="shop-name">
         <input type="checkbox" class="check goods-check shopCheck">
         <h4><a href="#">苹果专卖店</a></h4>
         <div class="coupons"><span>领券</span><em>|</em><span>编辑</span></div>
      </div>
      <ul>
         <li>
            <div class="shop-info">
               <input type="checkbox" class="check goods-check goodsCheck">
               <div class="shop-info-img"><a href="#"><img src="images/computer.jpg" /></a></div>
               <div class="shop-info-text">
                  <h4>Apple MacBook Air 13.3英寸笔记本电脑 银色(Core i5 处理器/8GB内存/128GB SSD闪存 MMGF2CH/A)</h4>
                  <div class="shop-brief"><span>重量:3.3kg</span><span>颜色:银色</span><span>版本:13.3英寸</span></div>
                  <div class="shop-price">
                     <div class="shop-pices"><b class="price">5899.00</b></div>
                     <div class="shop-arithmetic">
                        <a href="javascript:;" class="minus">-</a>
                        <input class="num" value="1"/>
                        <a href="javascript:;" class="plus">+</a>
                     </div>
                  </div>
               </div>
            </div>
         </li>
         <li>
            <div class="shop-info">
               <input type="checkbox" class="check goods-check goodsCheck">
               <div class="shop-info-img"><a href="#"><img src="images/ipad.jpg" /></a></div>
               <div class="shop-info-text">
                  <h4>Apple iPad Pro 平板电脑 10.5 英寸(64G WLAN版/A10X芯片/Retina屏/Multi-Touch技术 MQDX2CH/A)金色</h4>
                  <div class="shop-brief"><span>重量:0.85kg</span><span>颜色:金色</span><span>版本:64G WLAN版</span></div>
                  <div class="shop-price">
                     <div class="shop-pices"><b class="price">4788.00</b></div>
                     <div class="shop-arithmetic">
                        <a href="javascript:;" class="minus">-</a>
                        <input class="num" value="1"/>
                        <a href="javascript:;" class="plus">+</a>
                     </div>
                  </div>
               </div>
            </div>
         </li>
         <li>
            <div class="shop-info">
               <input type="checkbox" class="check goods-check goodsCheck">
               <div class="shop-info-img"><a href="#"><img src="images/iphone.jpg" /></a></div>
               <div class="shop-info-text">
                  <h4>苹果 iPhone7 Plus 手机 红色特别版 全网通 128GB</h4>
                  <div class="shop-brief"><span>重量:0.18kg</span><span>颜色:红色特别版</span><span>版本:全网通 128G</span></div>
                  <div class="shop-price">
                     <div class="shop-pices"><b class="price">6326.00</b></div>
                     <div class="shop-arithmetic">
                        <a href="javascript:;" class="minus">-</a>
                        <input class="num" value="1"/>
                        <a href="javascript:;" class="plus">+</a>
                     </div>
                  </div>
               </div>
            </div>
         </li>
      </ul>
      <div class="shopPrice">本店总计:¥<span class="shop-total-amount ShopTotal">0.00</span></div>
   </div>
</div>

<div class="payment-bar">
   <div class="all-checkbox"><input type="checkbox" class="check goods-check" id="AllCheck">全选</div>
   <div class="shop-total">
      <strong>总价:<i class="total" id="AllTotal">0.00</i></strong>
      <span>减免:123.00</span>
   </div>
   <a href="#" class="settlement">结算</a>
</div>
</body>
<script src="js/index.js"></script>
</html>

这个HTML文件不是我写的,只是一个案例的小样,这篇我们主要编写的是JS,对html和css没有任何操作

通过上面的html结构我们可以看出,每个商店都有一个class属性为.shop-group-item的div标签包裹住,每个商店里面的购物项都有一个li标签包裹住,那么我们就可以知道如果我们根据.shop-group-item和li标签获取DOM元素的话,得到的肯定不是一个唯一值,而是一个数组值

在这里插入图片描述

所以首先我们需要将他们区分开,那么怎么区分开呢,这个时候涉及到一个知识点,获取当前标签下面的父元素节点,然后通过这个父元素节点对于整个元素进行操作,这样就可以得到我们要获取的唯一值了,整个案例的核心思想就是这个,通过多个子节点获取唯一父节点的DOM对象,然后根据这个父节点完成我们的需求操作。

1.3 代码编写

1.3.1 商品数量添加的处理

在这里插入图片描述

再写复选框之前,我们先对商品数量添加的处理,因为这个涉及到求和的问题,所以我们需要先计算出她所需要求的数量才能走到求和的问题

/*计算value值*/
//获取减号按钮
let minus = document.querySelectorAll('.shop-group-item .shop-info .shop-price .minus');
//获取加号按钮
let plus = document.querySelectorAll('.shop-group-item .shop-info .shop-price .plus');
for (let i = 0; i < plus.length; i++) {
    /*加法的计算*/
    plus[i].onclick = function () {
        //通过点击的元素获取到value值
        let num = parseInt(this.parentElement.querySelector('.num').value);
        console.log(num);
        this.parentElement.querySelector('.num').value = num + 1;
        //获取到当前节点下的所有复选框
        getSumByCheck(this);
        getSum();
    }
    /*减法计算*/
    minus[i].onclick = function () {
        //通过点击的元素获取到value值
        let num = parseInt(this.parentElement.querySelector('.num').value);
        console.log(num);
        //判断如果num的最小值为0
        if (num > 0) {
            this.parentElement.querySelector('.num').value = num - 1;
        }
        //获取到当前节点下的所有复选框
        getSumByCheck(this);
        getSum();
    }
}

以上就是对于加号减号的处理,求数量的处理

1.3.2 求和处理

这里的求和处理我封装成了两个方法,之前本来想封装成一个方法的,但是发现方法并不能通用,封装成两个方法也比较好处理

一个是求总价的方法

在这里插入图片描述

/*总价求和*/
function getSum() {
    //先获取每个info
    let infos = document.querySelectorAll('.shop-group-item .shop-info');
    //设置总价格
    let totalCash = 0;
    for (let i = 0; i < infos.length; i++) {
        //再获取对应个info下面的价格
        let money = parseFloat(infos[i].querySelector('.price').innerText);
        //console.log(money);
        //获取对应info下面的value值
        let value = parseInt(infos[i].querySelector('.num').value);
        //console.log(value);
        //获取到复选框对象
        let check = infos[i].querySelector('.check');
        //如果被选中的话就进行计算
        if (check.checked) {
            totalCash += money * value;
        }
        /*infos[i].closest('.shop-group-item').querySelector('.shopPrice .shop-total-amount').innerText = totalCash;*/
    }
    document.querySelector('.payment-bar .total').innerText = totalCash;
    //将总价放在每一个总计标签里面
}

一个是求每个商店价格的方法,这个需要传进当前操作的this对象
在这里插入图片描述

/*根据check子节点求和*/
function getSumByCheck(_this) {
    //其实我们传过来的所有this对象他们都是有一个共同的父级,也就是shop-group-item这个div标签
    //上面有个对于总复选框进行判断的一个点击事件,之所以没有使用这个方法的原因是,他和shop-group-item这个div标签是平级的,所以无法获取到这个DOM对象,所以就把他单拎出来了
    let item = _this.closest('.shop-group-item');
    //获取到当前this对象对应的shop-group-item下的所有复选框
    let check = item.querySelectorAll('.shop-info .check');

    //初始化总价
    let totalCash = 0;
    //遍历求和
    for (let j = 0; j < check.length; j++) {
        //获取每一个的单价
        let money = parseFloat(check[j].closest('.shop-info').querySelector('.price').innerText);
        //获取每一个的数量
        let count = parseInt(check[j].closest('.shop-info').querySelector('.num').value);
        //判断是否被选中,被选中则进入计算
        if (check[j].checked) {
            totalCash += money * count;
        }
    }
    //将总价添加到本店总计里面
    item.querySelector('.shopPrice .shop-total-amount').innerText = totalCash;
}

这里取巧的就是利用他操作的每一个复选框他都有一个共同的父级元素,然后再对父级元素进行操作。

1.3.3 购物项复选框选中的处理

在这里插入图片描述

通过上面的分析,我们可以先获取所有的购物项对象,然后遍历购物项对象,并且将其遍历绑定点击事件

下面是代码

//通过单个点击获取到所有,获取到所有的购物项
let checks = document.querySelectorAll('.shop-group-item ul li .check');
//遍历所有购物项,目的是给所有购物项里面的某一个购物项绑定点击事件,然后获取他的唯一父元素
for (let i = 0; i < checks.length; i++) {
    //绑定点击事件,你点击哪一个,咱就能获取到哪一个的商店的头复选框
    checks[i].onclick = function () {
        //通过选中的复选框找到当前节点下item
        let item = this.closest('.shop-group-item');
        console.log(item);
        //再通过item来获取到当前节点下有多少个复选框
        let check = item.querySelectorAll('ul li .check');
        console.log(check.length);
        //通过item获取到当前节点下的总复选框
        let checkTotal = item.querySelector('.shop-name .check');
        console.log(checkTotal);
        //这里相当于是一个监听的效果,每点击一次,就会监听当前点击对象对应的父对象下面的子复选框是否全部被选中
        //说白了也就是判断当前点击元素平级的复选框是否全部被选中
        let checkNum = 0;
        for (let j = 0; j < check.length; j++) {
            //如果被选中一个,那么checkNum就会自动+1
            if (check[j].checked) {
                checkNum += 1;
            }
        }
        //如果当前点击元素平级的复选框被选中的值和该数组的长度一样的话,就让当前商店的头复选框选中
        checkTotal.checked = checkNum === check.length;

        //这里也相当于是一个监听的效果,每点击一次监听一下所有的购物项的复选框是否被选中
        let allCheckNum = 0;
        for (let j = 0; j < checks.length; j++) {
            //如果被选中的话就+1
            if (checks[j].checked) {
                allCheckNum += 1;
            }
        }
        console.log('allCheckNum:' + allCheckNum);
        //如果当前所有购物项被选中的复选框和所有购物项的复选框长度一致的话就让总的复选框选中,否则则不会被选中
        allChecked.checked = allCheckNum === checks.length;

        //通过传入一个this对象进行求和,后面在这个函数里面会统一解决这个this对象
        getSumByCheck(this);

        //求总和
        getSum();
    }
}

以上问题解决的是点击购物项的按钮复选框就会监听当前对应的商店的总复选框下的所以购物项的复选框是否全部被选中,如果全部被选中,则将每个商店的总复选框选中

1.3.4 商店复选框选中的处理

商店复选框的处理也是也不是很难,大概也就是点击之后有个监听的效果,点击一次判断一次,某一个商店的复选框被选中后,他的购物项也会被选中,所有商店的复选框被选中后,总的复选框也会被选中

//获取到每一个商店的头复选框
let checkAll = document.querySelectorAll('.shop-name .check');
//获取总复选框
let allChecked = document.querySelector('#AllCheck');
//遍历每一个商店的头复选框
for (let i = 0; i < checkAll.length; i++) {
    //给每一个商店的头复选框绑定点击事件
    checkAll[i].onclick = function () {
        //先通过父节点寻找到外层的div
        let item = checkAll[i].closest('.shop-group-item');
        //在通过外层的div找到ul下的所有复选框
        let check = item.querySelectorAll('.shop-group-item ul li .check');
        //遍历这个商店底下的所有复选框
        for (let j = 0; j < check.length; j++) {
            //如果这个商店下的所有复选框都被选中,那么这个商店的头复选框也被选中
            check[j].checked = this.checked;
        }
        //判断总的复选框按钮
        let num = 0;
        //遍历所有商店的头复选框
        for (let j = 0; j < checkAll.length; j++) {
            //判断头复选框是否被选中,如果被选中的话就让num+1
            if (checkAll[j].checked) {
                num += 1;
            }
        }
        //如果num和所有商店的头复选框数组长度一样的话,就返回true,然后总计按钮就是被选中的
        allChecked.checked = num === checkAll.length;

        //传入当前的对象,进行求和
        getSumByCheck(this);

        //求总和
        getSum();
    }
}

以上代码是解决了某一个商店的复选框被选中后,他的购物项也会被选中,所有商店的复选框被选中后,总的复选框也会被选中,并进行了求和,求和会在后面的代码中写出

1.3.5 总复选框的处理

总复选框实现的需求就是点击总复选框的时候,他会选择上面的所有复选框,上面所有复选框被选中的时候,总复选框也会被选中

实现的大概就是这样一个效果

//对总复选框进行绑定一个点击事件
allChecked.onclick = function () {
    //遍历除总复选框以外的所有复选框
    for (let i = 0; i < allCheck.length; i++) {
        //这里是如果总复选框被选中的话,那么所有复选框也会被选中
        allCheck[i].checked = this.checked;

        //下面的操作是为了当总复选框被选中后,计算每个商店的总计值,然后添加到每个商店的总计数量里面去
        //我本来想给他封装成一个方法的,因为之前的所有求和都是封装成方法了,但是这个this指向的问题,没有办法把这个和上面那些求和方法封装在一个方法里面
        //所以如果为了这一个计算而把他单拎出来封装成方法的话,没有太大的必要,所以就写在这个点击事件里面了

        //获取商店复选框的shop-group-item父节点
        let item = allCheck[i].closest('.shop-group-item');
        //然后通过父节点获取每一个商店下面的复选框,这里面是获取所有的除商店复选框以外的所有复选框
        //因为我们需要通过这个对象去获取当前购物项的价格和数量,并进行计算
        let check = item.querySelectorAll('.shop-info .check');

        //判断总的复选框是否被选中,被选中则进入计算,不被选中则不会进入计算
        if (this.checked) {
            //初始化每个商店的总价
            let totalCash = 0;
            //遍历
            for (let j = 0; j < check.length; j++) {
                //通过上面获取到的购物项的复选框去找每个购物项的价格
                let money = parseFloat(check[j].closest('.shop-info').querySelector('.price').innerText);
                //通过上面获取到的购物项的复选框去找每个购物项的数量
                let count = parseInt(check[j].closest('.shop-info').querySelector('.num').value);
                //判断购物项的复选框是否被选中,如果被选中则进入计算
                if (check[j].checked) {
                    totalCash += money * count;
                }
            }
            //将总价添加到本店总计里面
            item.querySelector('.shopPrice .shop-total-amount').innerText = totalCash;
        }else {
            //如果总复选框没有被选中的话,则让所有的购物项的值都变成0
            item.querySelector('.shopPrice .shop-total-amount').innerText = 0;
        }
    }
    //求总价
    getSum();
}

以上求和方法我是单独拎出来求和的,这个选择可能不是很明智,因为它的代码和上面传参的求和函数的代码是差不多的,不过是传入的this指向有些问题,因为他们没有共同的父级元素,只有一个body,对于body操作就有点不大好了,所以我就把他单拎出来了,这是我目前能想到的唯一方法,大家有什么好的方法也可以多多提出

1.4 JS代码

window.onload = function () {
    /*计算value值*/
    //获取减号按钮
    let minus = document.querySelectorAll('.shop-group-item .shop-info .shop-price .minus');
    //获取加号按钮
    let plus = document.querySelectorAll('.shop-group-item .shop-info .shop-price .plus');
    for (let i = 0; i < plus.length; i++) {
        /*加法的计算*/
        plus[i].onclick = function () {
            //通过点击的元素获取到value值
            let num = parseInt(this.parentElement.querySelector('.num').value);
            console.log(num);
            this.parentElement.querySelector('.num').value = num + 1;
            //获取到当前节点下的所有复选框
            getSumByCheck(this);
            getSum();
        }
        /*减法计算*/
        minus[i].onclick = function () {
            //通过点击的元素获取到value值
            let num = parseInt(this.parentElement.querySelector('.num').value);
            console.log(num);
            //判断如果num的最小值为0
            if (num > 0) {
                this.parentElement.querySelector('.num').value = num - 1;
            }
            //获取到当前节点下的所有复选框
            getSumByCheck(this);
            getSum();
        }
    }

    //获取到每一个商店的头复选框
    let checkAll = document.querySelectorAll('.shop-name .check');
    //获取总复选框
    let allChecked = document.querySelector('#AllCheck');
    //遍历每一个商店的头复选框
    for (let i = 0; i < checkAll.length; i++) {
        //给每一个商店的头复选框绑定点击事件
        checkAll[i].onclick = function () {
            //先通过父节点寻找到外层的div
            let item = checkAll[i].closest('.shop-group-item');
            //在通过外层的div找到ul下的所有复选框
            let check = item.querySelectorAll('.shop-group-item ul li .check');
            //遍历这个商店底下的所有复选框
            for (let j = 0; j < check.length; j++) {
                //如果这个商店下的所有复选框都被选中,那么这个商店的头复选框也被选中
                check[j].checked = this.checked;
            }
            //判断总的复选框按钮
            let num = 0;
            //遍历所有商店的头复选框
            for (let j = 0; j < checkAll.length; j++) {
                //判断头复选框是否被选中,如果被选中的话就让num+1
                if (checkAll[j].checked) {
                    num += 1;
                }
            }
            //如果num和所有商店的头复选框数组长度一样的话,就返回true,然后总计按钮就是被选中的
            allChecked.checked = num === checkAll.length;

            //传入当前的对象,进行求和
            getSumByCheck(this);

            //求总和
            getSum();
        }
    }

    //通过单个点击获取到所有,获取到所有的购物项
    let checks = document.querySelectorAll('.shop-group-item ul li .check');
    //遍历所有购物项,目的是给所有购物项里面的某一个购物项绑定点击事件,然后获取他的唯一父元素
    for (let i = 0; i < checks.length; i++) {
        //绑定点击事件,你点击哪一个,咱就能获取到哪一个的商店的头复选框
        checks[i].onclick = function () {
            //通过选中的复选框找到当前节点下item
            let item = this.closest('.shop-group-item');
            console.log(item);
            //再通过item来获取到当前节点下有多少个复选框
            let check = item.querySelectorAll('ul li .check');
            console.log(check.length);
            //通过item获取到当前节点下的总复选框
            let checkTotal = item.querySelector('.shop-name .check');
            console.log(checkTotal);
            //这里相当于是一个监听的效果,每点击一次,就会监听当前点击对象对应的父对象下面的子复选框是否全部被选中
            //说白了也就是判断当前点击元素平级的复选框是否全部被选中
            let checkNum = 0;
            for (let j = 0; j < check.length; j++) {
                //如果被选中一个,那么checkNum就会自动+1
                if (check[j].checked) {
                    checkNum += 1;
                }
            }
            //如果当前点击元素平级的复选框被选中的值和该数组的长度一样的话,就让当前商店的头复选框选中
            checkTotal.checked = checkNum === check.length;

            //这里也相当于是一个监听的效果,每点击一次监听一下所有的购物项的复选框是否被选中
            let allCheckNum = 0;
            for (let j = 0; j < checks.length; j++) {
                //如果被选中的话就+1
                if (checks[j].checked) {
                    allCheckNum += 1;
                }
            }
            console.log('allCheckNum:' + allCheckNum);
            //如果当前所有购物项被选中的复选框和所有购物项的复选框长度一致的话就让总的复选框选中,否则则不会被选中
            allChecked.checked = allCheckNum === checks.length;

            //通过传入一个this对象进行求和,后面在这个函数里面会统一解决这个this对象
            getSumByCheck(this);

            //求总和
            getSum();
        }
    }
    //通过点击总价获得选中所有复选框
    //获取除总复选框以外的所有复选框
    let allCheck = document.querySelectorAll('.shop-group-item .check');

    //对总复选框进行绑定一个点击事件
    allChecked.onclick = function () {
        //遍历除总复选框以外的所有复选框
        for (let i = 0; i < allCheck.length; i++) {
            //这里是如果总复选框被选中的话,那么所有复选框也会被选中
            allCheck[i].checked = this.checked;

            //下面的操作是为了当总复选框被选中后,计算每个商店的总计值,然后添加到每个商店的总计数量里面去
            //我本来想给他封装成一个方法的,因为之前的所有求和都是封装成方法了,但是这个this指向的问题,没有办法把这个和上面那些求和方法封装在一个方法里面
            //所以如果为了这一个计算而把他单拎出来封装成方法的话,没有太大的必要,所以就写在这个点击事件里面了

            //获取商店复选框的shop-group-item父节点
            let item = allCheck[i].closest('.shop-group-item');
            //然后通过父节点获取每一个商店下面的复选框,这里面是获取所有的除商店复选框以外的所有复选框
            //因为我们需要通过这个对象去获取当前购物项的价格和数量,并进行计算
            let check = item.querySelectorAll('.shop-info .check');

            //判断总的复选框是否被选中,被选中则进入计算,不被选中则不会进入计算
            if (this.checked) {
                //初始化每个商店的总价
                let totalCash = 0;
                //遍历
                for (let j = 0; j < check.length; j++) {
                    //通过上面获取到的购物项的复选框去找每个购物项的价格
                    let money = parseFloat(check[j].closest('.shop-info').querySelector('.price').innerText);
                    //通过上面获取到的购物项的复选框去找每个购物项的数量
                    let count = parseInt(check[j].closest('.shop-info').querySelector('.num').value);
                    //判断购物项的复选框是否被选中,如果被选中则进入计算
                    if (check[j].checked) {
                        totalCash += money * count;
                    }
                }
                //将总价添加到本店总计里面
                item.querySelector('.shopPrice .shop-total-amount').innerText = totalCash;
            }else {
                //如果总复选框没有被选中的话,则让所有的购物项的值都变成0
                item.querySelector('.shopPrice .shop-total-amount').innerText = 0;
            }
        }
        //求总价
        getSum();
    }

    /*总价求和*/
    function getSum() {
        //先获取每个info
        let infos = document.querySelectorAll('.shop-group-item .shop-info');
        //设置总价格
        let totalCash = 0;
        for (let i = 0; i < infos.length; i++) {
            //再获取对应个info下面的价格
            let money = parseFloat(infos[i].querySelector('.price').innerText);
            //console.log(money);
            //获取对应info下面的value值
            let value = parseInt(infos[i].querySelector('.num').value);
            //console.log(value);
            //获取到复选框对象
            let check = infos[i].querySelector('.check');
            //如果被选中的话就进行计算
            if (check.checked) {
                totalCash += money * value;
            }
            /*infos[i].closest('.shop-group-item').querySelector('.shopPrice .shop-total-amount').innerText = totalCash;*/
        }
        document.querySelector('.payment-bar .total').innerText = totalCash;
        //将总价放在每一个总计标签里面
    }

    /*根据check子节点求和*/
    function getSumByCheck(_this) {
        //其实我们传过来的所有this对象他们都是有一个共同的父级,也就是shop-group-item这个div标签
        //上面有个对于总复选框进行判断的一个点击事件,之所以没有使用这个方法的原因是,他和shop-group-item这个div标签是平级的,所以无法获取到这个DOM对象,所以就把他单拎出来了
        let item = _this.closest('.shop-group-item');
        //获取到当前this对象对应的shop-group-item下的所有复选框
        let check = item.querySelectorAll('.shop-info .check');

        //初始化总价
        let totalCash = 0;
        //遍历求和
        for (let j = 0; j < check.length; j++) {
            //获取每一个的单价
            let money = parseFloat(check[j].closest('.shop-info').querySelector('.price').innerText);
            //获取每一个的数量
            let count = parseInt(check[j].closest('.shop-info').querySelector('.num').value);
            //判断是否被选中,被选中则进入计算
            if (check[j].checked) {
                totalCash += money * count;
            }
        }
        //将总价添加到本店总计里面
        item.querySelector('.shopPrice .shop-total-amount').innerText = totalCash;
    }
}

里面的注释其实挺全的,代码的每一步都有注释,大家看起来也方便

到这里基本所有的代码都写完了,其实也不是很多,逻辑也不是很复杂,就是一些细节处理方面可能比较麻烦。

1.5 CSS代码

base.css

@charset "utf-8";
html,body,div,p,form,label,ul,li,dl,dt,dd,ol,img,button,b,em,strong,small,h1,h2,h3,h4,h5,h6{margin:0;padding:0;border:0;list-style:none;font-style:normal;}
body{font-family:SimHei,'Helvetica Neue',Arial,'Droid Sans',sans-serif;font-size:14px;color:#333;background:#f2f2f2;}
a, a.link{color:#666;text-decoration:none;font-weight:500;}
a, a.link:hover{color:#666;}
h1,h2,h3,h4,h5,h6{font-weight: normal;}
/*头部开始*/
.header{position:relative;width:100%;height:44px;background:#fff;border-bottom:1px solid #e0e0e0;}
.header h1{font-size:16px;color:#333;height:44px;line-height:44px;display:block;text-align:center;}
.header a{position: absolute;top:0;display:block;height:44px;line-height:44px;}
.header a.back{left:0px;}
.header a.back span{display:inline-block;width:25px;height:25px;margin:10px 5px;background: url("../img/icon/icon-back.png") no-repeat;background-size:100%;}
.header .home{}
/*头部结束*/

input[type="checkbox"]{-webkit-appearance:none;outline: none;}
input.check{background:url(../img/icon/icon_radio3.png) no-repeat center left;background-size:20px 20px;position:absolute;top:50%;left:10px;margin-top:-18px;width:20px;height:35px;}
input.check:checked{background:url(../img/icon/icon_radio4.png) no-repeat center left;background-size:20px 20px;}
input.goodsCheck:checked{background:url(../img/icon/icon_radio4.png) no-repeat center left;background-size:20px 20px;}
input.check:checked{background:url(../img/icon/icon_radio4.png) no-repeat center left;background-size:20px 20px;}
.checked{background:url(../img/icon/icon_radio4.png) no-repeat left center;background-size:20px 20px;position:absolute;top:50%;left:15px;margin-top:-18px;width:20px;height:35px;}

/*尾部开始*/
.footer .copyright{height:44px;line-height:44px;text-align:center;color:#848689;font-size:12px;}
/*尾部结束*/

module.css

@charset "utf-8";
/* CSS Document */
/*购物车*/
.shopping{clear:both;overflow:hidden;height:auto;padding-bottom: 60px;}
.shop-group-item{margin-bottom:5px;}
.shop-group-item ul li{border-bottom:1px solid #fff;}
.shop-group-item ul li:last-child{border-bottom:none;}

.shop-name{background:#fff;height:35px;line-height:35px;padding:0 15px;position:relative;}
.shop-name h4{float:left;font-size:14px;background:url(../img/icon/icon-kin.png) no-repeat left center;background-size:20px 20px;padding-left:25px;margin-left: 28px;}
.shop-name .coupons{float:right;}
.shop-name .coupons span{display:inline-block;padding:0 5px;}
.shop-name .coupons em{color:#e0e0e0;}

.shop-info{background:#f5f5f5;height:120px;padding:0 15px;position:relative;}
.shop-info .checkbox{background:url(../img/icon/icon_radio3.png) no-repeat left center;background-size:20px 20px;position:absolute;top:50%;left:15px;margin-top:-60px;width:20px;height:120px;}
.shop-info .checkbox1{background:url(../img/icon/icon_radio4.png) no-repeat left center;background-size:20px 20px;position:absolute;top:50%;left:15px;margin-top:-60px;width:20px;height:120px;}
.shop-info .shop-info-img{position:absolute;top:15px;left:45px;width:90px;height:90px;}
.shop-info .shop-info-img img{width:100%;height:100%;}
.shop-info .shop-info-text{margin-left:130px;padding:15px 0;}
.shop-info .shop-info-text h4{font-size:14px;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow: hidden;}
.shop-info .shop-info-text .shop-brief{height:25px;line-height:25px;font-size:12px;color:#81838e;white-space:nowrap;}
.shop-info .shop-info-text .shop-brief span{display:inline-block;margin-right:8px;}
.shop-info .shop-info-text .shop-price{height:24px;line-height:24px;position:relative;}
.shop-info .shop-info-text .shop-price .shop-pices {color:red;font-size:16px;}
.shop-info .shop-info-text .shop-arithmetic{position:absolute;right:0px;top:0;width:84px;box-sizing:border-box;white-space:nowrap;height:100%;border:1px solid #e0e0e0;}
.shop-info .shop-info-text .shop-arithmetic a{display:inline-block;width:23px;height:22px;line-height:22px;text-align:center;background:#fff;font-size:16px;}
.shop-info .shop-info-text .shop-arithmetic .minus{border-right:1px solid #e0e0e0;}
.shop-info .shop-info-text .shop-arithmetic .failed{color:#d1d1d1;}
.shop-info .shop-info-text .shop-arithmetic .plus{border-left:1px solid #e0e0e0;}
.shop-info .shop-info-text .shop-arithmetic .num{width:32px;text-align:center;border:none;display: inline-block;height:100%;box-sizing:border-box;vertical-align:top;margin:0 -6px;}
.shopPrice{background:#fff;height:35px;line-height:35px;padding:0 15px;text-align:right;}
.shopPrice span{color:#f00;}




.payment-bar{clear:both;overflow:hidden;width:100%;height:49px;position:fixed;bottom:0;border-top:1px solid #e0e0e0;background:#fff;}
.payment-bar .all-checkbox{float:left;line-height:49px;padding-left:40px;}
.payment-bar .shop-total{float:left;-webkit-box-flex:1.0;box-flex:1.0;margin:9px 20px 9px 35px;}
.payment-bar .shop-total strong{display:block;font-size:16px;}
.payment-bar .shop-total span{display:block;font-size:12px;}
.payment-bar .settlement{display:inline-block;float:right;width:100px;height:49px;line-height:49px;text-align:center;color:#fff;font-size:16px;background:#f23030;}

这里面的样式和HTML结构都不是我写的,我只写了JS的代码,大家有需要可以自提,然后练习对DOM对象的操作

二. 总结

以上便是这篇博文的全部内容,主要内容就是对购物车案例升级的编写,和上一篇写的不同点在于,这篇博文将购物车里面进行了分类处理,分类处理每一个商店里面的每一个购物项,主要难点在于对DOM对象的操作,调用父级DOM对象和通过父级的DOM对象再次调用子级的DOM对象,相当于是一个DOM树之间的互相调用的处理方式,这两天的博客都是一些案例,案例主要练习的也就是对JavaScript中DOM对象的操作练习。

以上就是全部内容,我只写了JS代码,HTML和CSS都是案例的提供的文件,有时间的话我也会尝试着将整个案例完整的写下来,以上JS代码中,对于逻辑的处理是我目前想到的还不错的写法,可能代码中还会存在着一些没有发现的问题,大家看了之后发现有什么错误,或者看完之后有什么更好的想法也可以多多提出,大家一起学习,共同进步!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

前端学习——JavaScript原生实现购物车案例 的相关文章

  • JavaScript 日期 + 7 天

    这个脚本有什么问题 当我将时钟设置为 29 04 2011 时 它会添加2011年4月36日在星期输入 但正确的日期应该是6 5 2011 var d new Date var curr date d getDate var tomo da
  • 无法使用 Karma 运行 Coverage

    我正在尝试使用 karma 运行覆盖率 但收到警告 警告 预处理 无法加载 覆盖率 它未注册 我以为我在运行 npm install g karma coverage save dev 时安装了覆盖范围 这是我的配置文件 module ex
  • JS专用鼠标按键

    我的鼠标侧面有两个按钮 其默认行为是 后退 和 前进 我想知道的是是否可以在 JavaScript 中检测这些鼠标按钮的点击 或者这些按钮是否是类似于键盘的 播放 音量调高 和 无线开 关 的 特殊 按钮纽扣 我不知道任何特定的鼠标事件 但
  • 我可以使用 javascript 捕获并保存网页的当前状态吗

    我需要使用 javascript 获取页面的全部内容并将其发送到服务器脚本以保存它 我想在用户使用 AJAX 和其他 javascript 工具对页面进行一些更改后执行此操作 我不想要某些元素的状态 我想基本上获取 body 标记内的所有内
  • JS中的递归排序

    在一次采访中 我被要求编写一个程序 算法来使用递归对数字数组进行排序 虽然我含糊地回答了它 但我尝试并想出了以下代码 您可以使用以下JSFiddle https jsfiddle net RajeshDixit 2u9mLegv 1 链接来
  • 保护客户端 API 的安全

    我正在为基于 JavaScript 的游戏构建服务器端 API 和客户端库 其中必须确保两个非常重要的功能的安全 用户每次游玩都必须扣款 我们必须确保提交的分数是玩家实际获得的分数 解决第一个问题看起来很简单 在每次游戏开始时 我们都会调用
  • 是否可以告诉 jsdoc 在与源代码分开的文件中查找该代码的文档?

    我希望内联注释尽可能短 因为我的经验是超过 3 或 4 行的注释往往会被掩盖 从而产生很多不必要的 阅读手册行 遗留系统要求我遵守与 jsdoc 兼容的格式来记录代码 如果要正确记录许多不言而喻的事情 则需要明确声明它们 实际上每个标签都可
  • 在 VueJs 中使用上下键自动完成搜索

    除了自动完成搜索之外 我还想添加功能以允许使用 VueJs 按下 向上键功能 我的模板如下所示 div h2 Todos h2 div class autocomplete div div
  • Javascript:如何简化具有多个 OR 条件的 if 语句?

    很抱歉 如果我在写这篇文章时犯了错误 我是新来的 不知道这是如何工作的 希望我能尽快学会 我也是 JavaScript 新手 所以问题是 我有这个代码 elements js文件 我无法让它工作 放这个有用吗 if codePrompt c
  • 这个作用域/闭包什么时候在 javaScript 中被垃圾回收?

    我正在做一门课程 该课程正在讨论范围 闭包并简要提到垃圾收集 课程中提出一个问题 范围保持多久 答案是 直到 不再有任何提及它 是的 所以我们基本上说的是 是的 闭包有点像对隐藏范围对象的引用 所以只要有一些函数仍然有一个闭包 范围 该范围
  • 将 viewbag 转换为 javascript 数组

    我想将数据从 ViewBag mytags 获取到 Javascript 数组 但我无法执行此操作 function var sampleTags new Array var array Html Raw Json Encode ViewB
  • 如何使用 JavaScript 或 jQuery 获取 html 元素的比例值?

    我想知道如何获得元素的比例值 我努力了 element css webkit transform 返回matrix scaleX 0 0 scaleY 0 0 有没有办法得到scaleX and scaleY only 找出文档和元素之间的
  • 使用 javascript onClick 播放 mp3 文件

    我正在播放 mp3 文件 只是 javascript onClick 下面是我的代码 Music File 1
  • jsx转js后dom未定义错误

    我创建了一个 jsx 文件 如下所示 jsx dom function use strict define jquery react react dom function React ReactDOM var AppView React c
  • Cordova/Phonegap 通过 JavaScript 在应用程序浏览器中打印

    我想从我正在开发的 iPad 应用程序打印一页 或某些页面 应用程序启动时所做的第一件事是通过以下代码加载外部网站 window location https 我现在想从这个外部网站打印一些东西 在 iPad 上的 Safari 中效果很好
  • 为什么 JavaScript 在不同浏览器中不一致?

    在花了无数个小时修复 JS 以使其跨浏览器兼容 主要是 IE 之后 我一直在思考以下问题 Why不是 JavaScript持续的跨浏览器 我的意思是 为什么 JS 不能像 Java 和 Flash 那样好呢 相反 我们必须求助于 jQuer
  • 使用node和multer将图像上传到heroku不起作用

    我正在尝试使用 Node 后端将图像文件上传到 Heroku 我可以使其工作 同样的过程在本地主机测试中工作得很好 但是在将我的项目部署到 Heroku 并测试它之后 过程和文件中出现错误不会上传 后端 let storage multer
  • 是否包括触摸事件客户端X/Y 滚动?

    我正在尝试获取相对于的触摸坐标viewport来自触摸事件的浏览器 例如触摸启动 我尝试从 clientX Y 属性获取它们 但两者实际上都返回包括滚动在内的值 这是违反规范的 因为它说 clientX Y 应该返回坐标而不滚动 我尝试添加
  • 如何禁用 AngularJS 中输入的修剪?

    我发现了一些奇怪的行为 默认情况下角度修剪模型值 快速谷歌搜索并不能帮助我解决这个问题 我发现了ng no trim指导性建议 ng trim等等 但没有任何作用 我在下面提供了一个代表这个问题的小片段 function Ctrl scop
  • JS 中的展开/休息运算符如何工作? [复制]

    这个问题在这里已经有答案了 我正在努力完全理解扩展 休息运算符在 JS 中的工作原理 我已经阅读了 MDN 文档 但我仍然不完全清楚 我在下面提供了一个示例 我在其中使用了它并且它按预期工作 const users name Samir a

随机推荐

  • 【编程基础】如何自学计算机/编程

    愚者困惑 智者提问 一 编程语言 二 编程技术体系 2 1 编程基础 2 1 1 操作系统 2 1 2 数据结构 2 1 3 编译原理 2 1 4 计算机组成原理 2 1 5 算法 2 2 编程语言 2 3 编程素质 2 3 1 编程规范
  • SQL server无法启动服务

    报错 在与SQL Server建立连接时出现与网络相关的或特定于实例的错误 切换到 依赖关系 选项卡 我们可以看见它与 SQL Server MSSQLSERVER 服务存在依赖关系 4 鼠标右键单击 SQL Server MSSQLSER
  • springboot注册到consul中报错:Spring MVC found on classpath, which is incompatible with Spring Cloud

    今天在做springboot整合成springCloud并注册到consul中时 发现若注册到consule中成功 则不能启动swagger 且不能提供任何API服务 要是能提供API服务则不能注册到consule中 并报错 Spring
  • Python怎么打包exe可执行文件?教你30秒轻松完成

    兄弟们 我来了 实在是太忙了 没得时间更新 太难了 之前看很多人都在问 Python怎么打包exe可执行文件 雀氏有点多 那么我来了 给大家整一个Python打包exe可执行文件的究极教程 当然 顺便再给它换个图标 首先把你的代码准备好 尽
  • qt之QCustomPlot动态更改曲线颜色,点击曲线标题名称

    一 前言 上篇文章介绍了如何调用qt自带的颜色画板 其实目的就是想更改一些颜色 本篇博客介绍更改QCustomPlot的曲线颜色 二 环境 qt5 7 mingw windows8 sqlite3 三 正文 首先在数据库中建好表 一行多列
  • 碳排放混合预测模型(Matlab代码实现)

    个人主页 研学社的博客 欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现 1 概述 二氧化碳排放力争于
  • [自然语言处理入门]-NLP中的注意力机制

    本章的主要内容有 注意力机制概述 传统注意力机制 注意力编码机制 1 注意力机制概述 注意力机制简单来说就是 加权求和机制 模块 加权求和模块 神经网络中的一个组件 可以单独使用 但更多地用作为 网络中的一部分 2 传统注意力机制 2 1
  • windows基线检测

    按照Windows基线检查模板检查设置windows安全机制 windows基线检查选项及风险等级 编号 检查选项 风险等级 适用类型 1 系统已安装最新的service pack 2 系统已经安装了最新的安全补丁 本地安全策略检查选项及风
  • java控制台聊天程序

    java控制台聊天程序 发送端 package ip chat import java io BufferedReader import java io InputStreamReader import java net DatagramP
  • Python中的filter()函数

    目录 一 描述 语法 返回值 二 实例 1 过滤出列表中的所有奇数 2 过滤出1 100中平方根是整数的数 一 描述 英文文档 filter function iterable Construct an iterator from thos
  • 01-----tcpdump抓包命令

    一 tcpdump抓包命令 关于tcpdump的抓包命令 非常的多 这里我只记录我平时开发时比较常用的抓包命令 当然后面可能不断的在本篇补上对应的内容 1 tcpdump的命令格式 tcpdump adeflnNOpqStvx c 数量 F
  • 大数据毕业设计 深度学习图像检索算法研究与实现 - python

    文章目录 0 前言 1 课题简介 2 图像检索介绍 1 无监督图像检索 2 有监督图像检索 3 图像检索步骤 4 应用实例 5 最后 0 前言 这两年开始毕业设计和毕业答辩的要求和难度不断提升 传统的毕设题目缺少创新和亮点 往往达不到毕业答
  • oh-my-zsh的各种主题展示,你喜欢哪一个?

    pygmalion virtualenv blink mrtazz sonicradish skaro linuxonly gnzh tjkirch 带时间
  • 物联网毕设分享 - stm32单片机酒精浓度酒驾检测系统 - 物联网 嵌入式

    文章目录 0 前言 1 简介 2 主要器件 3 实现效果 4 硬件设计 MQ 3酒精乙醇传感器模块 SIM800C模块 5 软件说明 系统框图 6 部分核心代码 7 最后 0 前言 这两年开始毕业设计和毕业答辩的要求和难度不断提升 传统的毕
  • cityscapes和foggy_cityscapes数据集使用记录

    cityscapes和foggy cityscapes数据集使用记录 cityscapes 数据集介绍 下载数据集 cityscapes转voc格式 foggy cityscapes 下载数据集 foggy cityscapes转voc格式
  • linux drm 架构 基础

    一 简介 DRM 英文全称Digital Rights Management 可以翻译为 数字版权管理 由于数字化信息的特点决定了必须有另一种独特的技术 来加强保护这些数字化的音视频节目内容 文档 电子书籍的版权 该技术就是数字权限管理技术
  • PAT (Basic Level) Practice (中文) B1034 有理数四则运算 (20 分)(C++)(分数四则运算)

    1034 有理数四则运算 20 分 本题要求编写程序 计算 2 个有理数的和 差 积 商 输入格式 输入在一行中按照 a1 b1 a2 b2 的格式给出两个分数形式的有理数 其中分子和分母全是整型范围内的整数 负号只可能出现在分子前 分母不
  • openGL之API学习(一九三)glGenTextures

    生成纹理单元名 单元名不一定是连续的 但是没有使用的 单元名是相对GL TEXTURE0的 对于单元名1 其实是GL TEXTURE0 1 glGenTextures产生的是一个比较小的整数id 纹理单元名 glActiveTexture激
  • 三分钟带你搞懂分布式链路追踪系统原理

    分布式系统为什么需要链路追踪 随着互联网业务快速扩展 软件架构也日益变得复杂 为了适应海量用户高并发请求 系统中越来越多的组件开始走向分布式化 如单体架构拆分为微服务 服务内缓存变为分布式缓存 服务组件通信变为分布式消息 这些组件共同构成了
  • 前端学习——JavaScript原生实现购物车案例

    一 购物车案例 1 1 案例介绍 今天我们来写另外一个购物车案例 说实话对于我来说这个是花了将近三个小时的时间然后才做出来的 里面可能还存在一些我没有发现的问题 但是能完成基本的功能 对于一些基本的需求都是可以完成的 下面照旧是案例实现的g