vue实现购物车功能

实现功能

CSS部分

   <style>
        .tr {
            display: flex;
        }

        .th {
            margin: 10px;
            width: 20%;
            height: 50%;
        }

        .td {
            display: flex;
            margin: 10px;
            width: 20%;
            height: 100px;
            align-items: center;
        }

        .app-container .banner-box {
            border-radius: 20px;
            overflow: hidden;
            margin-bottom: 10px;
        }

        .app-container .banner-box img {
            width: 100%;
        }

        .app-container .nav-box {
            background: #ddedec;
            height: 60px;
            border-radius: 10px;
            padding-left: 20px;
            display: flex;
            align-items: center;
        }

        .app-container .nav-box .my-nav {
            display: inline-block;
            background: #5fca71;
            border-radius: 5px;
            width: 90px;
            height: 35px;
            color: white;
            text-align: center;
            line-height: 35px;
            margin-right: 10px;
        }

        .breadcrumb {
            font-size: 16px;
            color: gray;
        }

        .table {
            width: 100%;
            text-align: left;
            border-radius: 2px 2px 0 0;
            border-collapse: separate;
            border-spacing: 0;
        }

        .table img {
            width: 100px;
            height: 100px;
        }

        button {
            outline: 0;
            box-shadow: none;
            color: #fff;
            background: #d9363e;
            border-color: #d9363e;
            color: #fff;
            background: #d9363e;
            border-color: #d9363e;
            line-height: 1.5715;
            position: relative;
            display: inline-block;
            font-weight: 400;
            white-space: nowrap;
            text-align: center;
            background-image: none;
            border: 1px solid transparent;
            box-shadow: 0 2px 0 rgb(0 0 0 / 2%);
            cursor: pointer;
            transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
            touch-action: manipulation;
            height: 32px;
            padding: 4px 15px;
            font-size: 14px;
            border-radius: 2px;
        }

        button.pay {
            background-color: #3f85ed;
            margin-left: 20px;
        }

        .bottom {
            height: 60px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding-right: 20px;
            border: 1px solid #f0f0f0;
            border-top: none;
            padding-left: 20px;
        }

        .right-box {
            display: flex;
            align-items: center;
        }

        .empty {
            text-align: center;
            font-size: 30px;
        }

        /* 选中时颜色是灰色 */
        .tr.active {
            background-color: #f5f7fa;
        }
    </style>

HTML

<body>
<div class="app-container" id="app">
    <!-- 顶部banner -->
    <div class="banner-box"><img src="./fruitPot/封面.png" alt=""/></div>
    <!-- 面包屑 -->
    <div class="breadcrumb">
        <span>🏠</span>
        <span>购物车</span>
    </div>
    <!-- 购物车主体 -->
    <div class="main" v-if="fruitlist.length>0">
        <div class="table">
            <!-- 头部 -->
            <div class="thead">
                <div class="tr">
                    <div class="th">选中</div>
                    <div class="th th-pic">图片</div>
                    <div class="th">单价</div>
                    <div class="th num-th">个数</div>
                    <div class="th">小计</div>
                    <div class="th">操作</div>
                </div>
            </div>
            <!-- 身体 -->
            <div class="tbody">
                <div class="tr" :class="{active:item.isChecked}" v-for="(item) in fruitlist" :key="item.id">
                    <div class="td"><input type="checkbox" v-model="item.isChecked"/></div>
                    <div class="td pot"><img :src="item.icon" alt=""/></div>
                    <div class="td">{{ item.price }}</div>
                    <div class="td">
                        <div class="my-input-number">
                            <!--disabled:禁用  -->
                            <button :disabled="item.num <=0 " class="decrease" @click="sub(item.id)"> -</button>
                            <span class="my-input__inner">{{ item.num }}</span>
                            <button class="increase" @click="add(item.id)"> +</button>
                        </div>
                    </div>
                    <div class="td">{{item.price * item.num}}</div>
                    <!-- 删除filter -->
                    <div class="td">
                        <button @click="del(item.id)">删除</button>
                    </div>
                </div>

            </div>
        </div>
        <!-- 底部 -->
        <div class="bottom">
            <!-- 全选 -->
            <label class="check-all">
                <input type="checkbox" v-model="isAll"/>全选
            </label>
            <div class="right-box">
                <!-- 所有商品总价 -->
                <span class="price-box">总价&nbsp;&nbsp;:&nbsp;&nbsp;¥&nbsp;
							<span class="price">{{totalPrice()}}</span>
						</span>
                <!-- 结算按钮 -->
                <button class="pay">结算( {{totalNum()}} )</button>
            </div>
        </div>
    </div>
    <!-- 空车 -->
    <div class="empty" v-else>🛒空空如也</div>
</div>

JS

<script>
    <!--    初始化-->
    // const defaultArr =[ ]
    const app = new Vue({
        el: '#app',
        data: {
            // // 水果列表从本地读取
            // // JSON.parse将本地JSON格式转回去
            // 加||defaultArr是怕用户删除完,下次刷新后只剩下空空如也
            // fruitlist: JSON.parse(localStorage.getItem('list'))||defaultArr,
            fruitlist: [{
                id: 1,
                icon: './fruitPot/苹果.png',
                isChecked: true,
                num: 2,
                price: 6,
            },
                {
                    id: 2,
                    icon: './fruitPot/苹果.png',
                    isChecked: false,
                    num: 7,
                    price: 20,
                },
                {
                    id: 3,
                    icon: './fruitPot/鸭梨.png',
                    isChecked: true,
                    num: 2,
                    price: 6,
                },
            ],

            },

        // 计算属性
        computed: {
            isAll:{
                // isAll:  对象
                // get()   方法
                get(){
                    // 必须所有的小选框都选中,全选按钮才选中--every
                    // 此时的item与上面的item没关联,只是一个形参
                    return this.fruitlist.every(item => {return  item.isChecked===true})
                },
                // value是复选框的布尔值
               set(value){
               //console.log(value)  ture/false
               //基于拿到的布尔值,要让所有的小选框同步状态
                   this.fruitlist.forEach(item =>item.isChecked = value)
               }

            }
        },
        methods: {
            del(id) {
                this.fruitlist = this.fruitlist.filter(item => item.id !== id)
            },
            add(id) {
                // 1.根据id 找到数组中的对应项 --find
                const fruit = this.fruitlist.find(item => item.id === id)
                // 2.操作num数量
                fruit.num++
            },
            sub(id) {
                const fruit = this.fruitlist.find(item => item.id === id)
                fruit.num--
            },
        //     总数量  reduce
            totalNum(){
                return this.fruitlist.reduce((sum, item) => {
                    if (item.isChecked) {
                        // 选中 → 需要累加
                        return sum + item.num
                    } else {
                        // 没选中 → 不需要累加
                        return sum
                    }
                }, 0)
            },
            // 总价
            totalPrice(){
                return this.fruitlist.reduce((sum, item) => {
                    if (item.isChecked) {
                        return sum + item.num * item.price
                    } else {
                        return sum
                    }
                }, 0)
            },
        },
    //     监视数据变化
        watch:{
            fruitlist:{
                deep:true,
                handler(newValue){
                    console.log(newValue)
                //     需要将变化后的newValue存入本地(转JSON)
                    localStorage.setItem('list',JSON.stringify(newValue))
                }
            }
        }
    })
</script>

全部代码

<!-- 标签\watch\methods -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>水果购物车</title>
    <style>
        .tr {
            display: flex;
        }

        .th {
            margin: 10px;
            width: 20%;
            height: 50%;
        }

        .td {
            display: flex;
            margin: 10px;
            width: 20%;
            height: 100px;
            align-items: center;
        }

        .app-container .banner-box {
            border-radius: 20px;
            overflow: hidden;
            margin-bottom: 10px;
        }

        .app-container .banner-box img {
            width: 100%;
        }

        .app-container .nav-box {
            background: #ddedec;
            height: 60px;
            border-radius: 10px;
            padding-left: 20px;
            display: flex;
            align-items: center;
        }

        .app-container .nav-box .my-nav {
            display: inline-block;
            background: #5fca71;
            border-radius: 5px;
            width: 90px;
            height: 35px;
            color: white;
            text-align: center;
            line-height: 35px;
            margin-right: 10px;
        }

        .breadcrumb {
            font-size: 16px;
            color: gray;
        }

        .table {
            width: 100%;
            text-align: left;
            border-radius: 2px 2px 0 0;
            border-collapse: separate;
            border-spacing: 0;
        }

        .table img {
            width: 100px;
            height: 100px;
        }

        button {
            outline: 0;
            box-shadow: none;
            color: #fff;
            background: #d9363e;
            border-color: #d9363e;
            color: #fff;
            background: #d9363e;
            border-color: #d9363e;
            line-height: 1.5715;
            position: relative;
            display: inline-block;
            font-weight: 400;
            white-space: nowrap;
            text-align: center;
            background-image: none;
            border: 1px solid transparent;
            box-shadow: 0 2px 0 rgb(0 0 0 / 2%);
            cursor: pointer;
            transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
            touch-action: manipulation;
            height: 32px;
            padding: 4px 15px;
            font-size: 14px;
            border-radius: 2px;
        }

        button.pay {
            background-color: #3f85ed;
            margin-left: 20px;
        }

        .bottom {
            height: 60px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding-right: 20px;
            border: 1px solid #f0f0f0;
            border-top: none;
            padding-left: 20px;
        }

        .right-box {
            display: flex;
            align-items: center;
        }

        .empty {
            text-align: center;
            font-size: 30px;
        }

        /* 选中时颜色是灰色 */
        .tr.active {
            background-color: #f5f7fa;
        }
    </style>
</head>
<body>
<div class="app-container" id="app">
    <!-- 顶部banner -->
    <div class="banner-box"><img src="./fruitPot/封面.png" alt=""/></div>
    <!-- 面包屑 -->
    <div class="breadcrumb">
        <span>🏠</span>
        <span>购物车</span>
    </div>
    <!-- 购物车主体 -->
    <div class="main" v-if="fruitlist.length>0">
        <div class="table">
            <!-- 头部 -->
            <div class="thead">
                <div class="tr">
                    <div class="th">选中</div>
                    <div class="th th-pic">图片</div>
                    <div class="th">单价</div>
                    <div class="th num-th">个数</div>
                    <div class="th">小计</div>
                    <div class="th">操作</div>
                </div>
            </div>
            <!-- 身体 -->
            <div class="tbody">
                <div class="tr" :class="{active:item.isChecked}" v-for="(item) in fruitlist" :key="item.id">
                    <div class="td"><input type="checkbox" v-model="item.isChecked"/></div>
                    <div class="td pot"><img :src="item.icon" alt=""/></div>
                    <div class="td">{{ item.price }}</div>
                    <div class="td">
                        <div class="my-input-number">
                            <!--disabled:禁用  -->
                            <button :disabled="item.num <=0 " class="decrease" @click="sub(item.id)"> -</button>
                            <span class="my-input__inner">{{ item.num }}</span>
                            <button class="increase" @click="add(item.id)"> +</button>
                        </div>
                    </div>
                    <div class="td">{{item.price * item.num}}</div>
                    <!-- 删除filter -->
                    <div class="td">
                        <button @click="del(item.id)">删除</button>
                    </div>
                </div>

            </div>
        </div>
        <!-- 底部 -->
        <div class="bottom">
            <!-- 全选 -->
            <label class="check-all">
                <input type="checkbox" v-model="isAll"/>全选
            </label>
            <div class="right-box">
                <!-- 所有商品总价 -->
                <span class="price-box">总价&nbsp;&nbsp;:&nbsp;&nbsp;¥&nbsp;
							<span class="price">{{totalPrice()}}</span>
						</span>
                <!-- 结算按钮 -->
                <button class="pay">结算( {{totalNum()}} )</button>
            </div>
        </div>
    </div>
    <!-- 空车 -->
    <div class="empty" v-else>🛒空空如也</div>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>

<script>
    <!--    初始化-->
    // const defaultArr =[ ]
    const app = new Vue({
        el: '#app',
        data: {
            // // 水果列表从本地读取
            // // JSON.parse将本地JSON格式转回去
            // 加||defaultArr是怕用户删除完,下次刷新后只剩下空空如也
            // fruitlist: JSON.parse(localStorage.getItem('list'))||defaultArr,
            fruitlist: [{
                id: 1,
                icon: './fruitPot/苹果.png',
                isChecked: true,
                num: 2,
                price: 6,
            },
                {
                    id: 2,
                    icon: './fruitPot/苹果.png',
                    isChecked: false,
                    num: 7,
                    price: 20,
                },
                {
                    id: 3,
                    icon: './fruitPot/鸭梨.png',
                    isChecked: true,
                    num: 2,
                    price: 6,
                },
            ],

            },

        // 计算属性
        computed: {
            isAll:{
                // isAll:  对象
                // get()   方法
                get(){
                    // 必须所有的小选框都选中,全选按钮才选中--every
                    // 此时的item与上面的item没关联,只是一个形参
                    return this.fruitlist.every(item => {return  item.isChecked===true})
                },
                // value是复选框的布尔值
               set(value){
               //console.log(value)  ture/false
               //基于拿到的布尔值,要让所有的小选框同步状态
                   this.fruitlist.forEach(item =>item.isChecked = value)
               }

            }
        },
        methods: {
            del(id) {
                this.fruitlist = this.fruitlist.filter(item => item.id !== id)
            },
            add(id) {
                // 1.根据id 找到数组中的对应项 --find
                const fruit = this.fruitlist.find(item => item.id === id)
                // 2.操作num数量
                fruit.num++
            },
            sub(id) {
                const fruit = this.fruitlist.find(item => item.id === id)
                fruit.num--
            },
        //     总数量  reduce
            totalNum(){
                return this.fruitlist.reduce((sum, item) => {
                    if (item.isChecked) {
                        // 选中 → 需要累加
                        return sum + item.num
                    } else {
                        // 没选中 → 不需要累加
                        return sum
                    }
                }, 0)
            },
            // 总价
            totalPrice(){
                return this.fruitlist.reduce((sum, item) => {
                    if (item.isChecked) {
                        return sum + item.num * item.price
                    } else {
                        return sum
                    }
                }, 0)
            },
        },
    //     监视数据变化
        watch:{
            fruitlist:{
                deep:true,
                handler(newValue){
                    console.log(newValue)
                //     需要将变化后的newValue存入本地(转JSON)
                    localStorage.setItem('list',JSON.stringify(newValue))
                }
            }
        }
    })
</script>
</body>
</html>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

docker-swarm集群搭建

目录 一、docker swarm介绍 二、部署docker 三、搭建集群 3.1 工作模式 3.2 将当前主机作为leader 3.3 将第二个节点slave1加入到worker 3.4 将第三个节点slave2也加入到worker 3.5 将第四个节点(slave3)加入到manager 四、总结 一、docker swarm介绍 Docker Swarm…

CSS顶部与JS后写:网页渲染的奥秘

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

动态规划(算法竞赛、蓝桥杯)--数位DP度的数量

1、B站视频链接&#xff1a;E38 数位DP 度的数量_哔哩哔哩_bilibili #include <bits/stdc.h> using namespace std; const int N34; int a[N];//把B进制数的每一位抠出存入数组 int f[N][N];//f[i][j]表示在i个位置上&#xff0c;放置j个1的组合数 int K,B;void init(…

11.Node.js入门

一.什么是 Node.js Node.js 是一个独立的 JavaScript 运行环境&#xff0c;能独立执行 JS 代码&#xff0c;因为这个特点&#xff0c;它可以用来编写服务器后端的应用程序 Node.js 作用除了编写后端应用程序&#xff0c;也可以对前端代码进行压缩&#xff0c;转译&#xff0c;…

Java 数据结构之链表

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {if (headA null || headB null) return null;ListNode pA headA, pB headB;while (pA ! pB) {pA pA null ? headB : pA.next;pB pB null ? headA : pB.next;}return pA;} public ListNode rev…

2024.3.6每日一题

LeetCode 找出数组中的 K -or 值 题目链接&#xff1a;2917. 找出数组中的 K-or 值 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。 nums 中的 K-or 是一个满足以下条件的非负整数&#xff1a; 只有在 nums 中&…

【开源】SpringBoot框架开发教学资源共享平台

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 课程档案模块2.3 课程资源模块2.4 课程作业模块2.5 课程评价模块 三、系统设计3.1 用例设计3.2 类图设计3.3 数据库设计3.3.1 课程档案表3.3.2 课程资源表3.3.3 课程作业表3.3.4 课程评价表 四、系统展…

遗传算法GA求解机器人栅格地图最短路径规划,可以自定义地图及起始点(提供MATLAB代码)

一、原理介绍 遗传算法是一种基于生物进化原理的优化算法&#xff0c;常用于求解复杂问题。在机器人栅格地图最短路径规划中&#xff0c;遗传算法可以用来寻找最优路径。 遗传算法的求解过程包括以下几个步骤&#xff1a; 1. 初始化种群&#xff1a;随机生成一组初始解&…

先进电机技术 —— 高速电机与低速电机

一、背景 高速电机是指转速远高于一般电机的电动机&#xff0c;通常其转速在每分钟几千转至上万转甚至几十万转以上。这类电机具有功率密度高、响应速度快、输出扭矩大等特点&#xff0c;在航空航天、精密仪器、机器人、电动汽车、高端装备制造等领域有着广泛的应用。 高速电…

【Pytorch】新手入门:基于sklearn实现鸢尾花数据集的加载

【Pytorch】新手入门&#xff1a;基于sklearn实现鸢尾花数据集的加载 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望…

学习和认知的四个阶段,以及学习方法分享

本文分享学习的四个不同的阶段&#xff0c;以及分享个人的一些学习方法。 一、学习认知的四个阶段 我们在学习的过程中&#xff0c;总会经历这几个阶段&#xff1a; 第一阶段&#xff1a;不知道自己不知道&#xff1b; 第二阶段&#xff1a;知道自己不知道&#xff1b; 第三…

模板不存在:./Application/Home/View/OnContact/Index.html 错误位置

模板不存在:./Application/Home/View/OnContact/Index.html 错误位置FILE: /home/huimingdedhpucixmaihndged5e/wwwroot/ThinkPHP123/Library/Think/View.class.php  LINE: 110 TRACE#0 /home/huimingdedhpucixmaihndged5e/wwwroot/ThinkPHP123/Library/Think/View.class.php(…

【REST2SQL】11 基于jwt-go生成token与验证

【REST2SQL】01RDB关系型数据库REST初设计 【REST2SQL】02 GO连接Oracle数据库 【REST2SQL】03 GO读取JSON文件 【REST2SQL】04 REST2SQL第一版Oracle版实现 【REST2SQL】05 GO 操作 达梦 数据库 【REST2SQL】06 GO 跨包接口重构代码 【REST2SQL】07 GO 操作 Mysql 数据库 【RE…

论文阅读:Iterative Denoiser and Noise Estimator for Self-Supervised Image Denoising

这篇论文是发表在 2023 ICCV 上的一篇工作&#xff0c;主要介绍利用自监督学习进行降噪的。 Abstract 随着深度学习工具的兴起&#xff0c;越来越多的图像降噪模型对降噪的效果变得更好。然而&#xff0c;这种效果的巨大进步都严重依赖大量的高质量的数据对&#xff0c;这种对…

在 Python 中 JSON 数据格式的使用

在 Python 中 JSON 数据格式的使用 JSON 简介 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式。它易于阅读和编写&#xff0c;并且与许多编程语言兼容。 Python 中的 JSON 模块 Python 标准库中包含一个 json 模块&#xff0c;用于处理…

【嵌入式——QT】MDI应用程序设计

MDI应用程序就是在主窗口里创建多个同类型的MDI子窗口&#xff0c;这些MDI子窗口在主窗口里显示&#xff0c;并享受主窗口上的工具栏和菜单等操作功能&#xff0c;主窗口上的操作都针对当前活动的MDI子窗口进行。 图示 代码示例 QWMainWindow.h #ifndef QWMAINWINDOW_H …

静态路由--添加路由表,实现非直连网段的通信

建立拓扑&#xff1a; 路由器**只有直连网段的路由表,而对非直连并不拥有,因此要在路由器的路由表中手动添加非直连网段的路由. ** 也就是说对于AR2来说&#xff0c;**网段192.168.10.0**和**网段192.168.40.0**是他的直连网段。进一步说这两个网端的设备可以相互通信而网段19…

flink 总结

flink 流式api checkpoint state 状态分类 Managed State 和 Raw State Managed State Flink 自己管理&#xff0c;支持多种数据结构 Raw State 用户自己管理&#xff0c; 只支持byte Managed Staste 分为 Keyed State 和 operator State Managed State 只能在Keyed Str…

浅谈Redis和分布式系统

浅谈Redis Redis用于存储数据&#xff0c;且在内存当中进行存储。 但是在日常编写代码中&#xff0c;定义一个变量也就属于在内存当中存储一个数据。 Redis主要会在分布式系统当中发挥重要作用&#xff0c;如果只是单机程序&#xff0c;直接通过变量存储数据的方式会比使用Re…

ubuntu安装开源汇编调试器NASM

安装 安装很简单&#xff0c;直接在终端输入以下命令即可 sudo apt-get install nasm 安装完成后&#xff0c;如果可以查看到nasm的版本号即可视为安装成功 nasm -version 测试 创建汇编文件 创建一个asm文件 vim hello.asm 文件内容如下 section .datahello: db …