vue实现购物车功能

news2024/9/28 11:24:06

实现功能

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>

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

图论(三)之最小生成树(kurskal/Prim)

Minimum Spanning Tree 两大算法&#xff1a;Kruskal 与 Prim 树的含义&#xff1a; 结构中不能形成环 必须连接图结构中的全部顶带&#xff0c;任意两个顶点都是互通的 不同的生成树有不同的权值和&#xff0c;而最小生成树即为最小的那个树 如何构造最小生成树 **目标&…

OB_GINS学习

OB_GINS学习 组合导航中的杆臂测量加速度计的零偏单位转换受到经纬度以及高程影响的正常重力位的计算公式大地坐标系&#xff08;LBH&#xff09;向空间直角坐标系&#xff08;XYZ&#xff09;的转换及其逆转换导航坐标系&#xff08;n系&#xff09;到地心地固坐标系&#xff…

Error while Deploying HAP

第一个程序就遇到这么恶心的bug&#xff0c;也查了很多类似的问题是什么情况&#xff0c;后来无意中菜解决了这个bug&#xff0c;确实也是devicps下面加一个参数&#xff0c;但是找了半天 这是我遇到这个问题的解决办法。其他解决办法如下&#xff1a; https://blog.51cto.com…

STM32中断和外部中断

NVIC&#xff1a;嵌套中断向量控制器&#xff1a;用于统一分配中断优先级和管理中断 响应式优先级&#xff1a;也可以称为插队式优先级哪个优先级高优先处理哪个 抢占式优先级&#xff1a;优先级高的可以优先被处理&#xff0c;相当于CPU可以暂时中断当前处理的程序&#xff0c…

【实验报告】C语言实现猜单词的小游戏

之前帮别人写的一个简单的报告&#xff0c;无偿分享给大家~代码在后面&#xff0c;有一些图片出于懒惰没有上传。比较简单&#xff0c;喜欢的话关注我~&#xff0c;请勿商用~ 1 系统功能模块结构图 该程序主要思路&#xff1a; 头文件设计&#xff0c;存储结构设计&#xff0…

深度学习预备知识(线性代数)

介绍&#xff1a; 深度学习是一种机器学习的方法&#xff0c;涉及到大量的线性代数运算。线性代数是研究向量空间和线性映射的数学学科。在深度学习中&#xff0c;线性代数常用于表示和处理输入数据和模型参数。下面是一些深度学习中常见的线性代数概念和运算&#xff1a; 1. …

Java学习笔记之IDEA的安装与下载以及相关配置

1 IDEA概述 ​IDEA全称IntelliJ IDEA&#xff0c;是用于Java语言开发的集成环境&#xff0c;它是业界公认的目前用于Java程序开发最好的工具。 集成环境&#xff1a; ​把代码编写&#xff0c;编译&#xff0c;执行&#xff0c;调试等多种功能综合到一起的开发工具。 2 IDEA…

新IDEA电脑环境设置

1.设置UTF-8 2.Maven 3.JRE选对

three.js 包围盒

效果&#xff1a; 想要显示包围盒的样子&#xff1b;需要借助 Box3Helper 辅助显示&#xff1b; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs"></div></div><…

【大厂AI课学习笔记NO.68】开源和开源发展情况

开源即源代码公开&#xff0c;任何人能获取源代码&#xff0c;查看、修改、分发他们认为合适的代码。 依托同行评审和社区生成&#xff0c;旨在以分散、协作的方式开发。 我们曾经很详细的讨论过开源协议的问题&#xff0c;详细可以参考我的文章&#xff1a; https://giszz.…

块设备驱动(1)-什么是块设备驱动?块设备驱动概念总结

1.块设备驱动概念 块设备驱动是针对存储设备&#xff0c;例如SD卡、EMMC、NAND FLASH、NOR FLSASH。 块设备驱动以块为单位进行访问、最小寻址单位是扇区、一个块中包含多个扇区、支持随机访问、带缓冲区&#xff0c;&#xff0c;当发生写入操作时&#xff0c;并不会立马操作硬…

P1948 [USACO08JAN] Telephone Lines S

Here 典中之典&#xff01;&#xff01; 解题思路 可选k条边代价为0如何决策&#xff1f; 将到当前位置选择了几条代价为0的边放入状态&#xff0c;即若当前状态选的边数小于&#xff0c;则可以进行决策&#xff0c;是否选择当前边&#xff0c;若选&#xff0c;则&#xff0c…

[2024-03-09 19:55:01] [42000][1067] Invalid default value for ‘create_time‘【报错】

这个错误可能是因为你的 MySQL 数据库版本不支持 CURRENT_TIMESTAMP 作为默认值。在一些早期版本中&#xff0c;MySQL 对 TIMESTAMP 类型字段的默认值设置有限制&#xff0c;只允许使用特定的常量值&#xff08;如 0000-00-00 00:00:00 或 CURRENT_TIMESTAMP()&#xff09;。如…

选修-单片机作业第1/2次

第一次作业 第二次作业 1、51 系列单片机片内由哪几个部分组成&#xff1f;各个部件的最主要功能是什么&#xff1f; 51系列单片机的内部主要由以下几个部分组成&#xff0c;每个部件的主要功能如下&#xff1a; 1. **中央处理器&#xff08;CPU&#xff09;**&#xff1a;这是…

Cisco Packet Tracer 模拟器实现一些交换机的基本配置

1. 内容 应用Cisco Packet Tracer 5.3搭建网络 应用Cisco Packet Tracer 5.3配置网络 通过不同的命令实现交换机的基本配置&#xff0c;包括交换机的各种配置模式、交换机的基本配置、交换机的端口配置。 2. 过程 2.1 打开软件 安装模拟器后打开如下&#xff1a; 图1 安装并…

Linux 之九:CentOS 上 Tomcat 安装、SpringBoot 项目打包和部署

安装 Tomcat 下载 a. 方式一&#xff1a;可以在windows 真机上下载后&#xff0c;再上传到服务器 b. 方式二&#xff1a;可以在服务器端使用 wget 下载命令来下载 登录官网https://tomcat.apache.org/download-90.cgi&#xff0c;选择 linux 版本 右键&#xff0c;获取下载链接…

【EtherCAT实践篇】九、EtherCAT增加变量示例:增加浮点数输入变量

目的&#xff1a;在EtherCAT开发板上IO程序基础上进行修改&#xff0c;将原来的16位整数型数据Analog input改为32位浮点数&#xff0c;基于STM32F405底板。 1、XML配置修改 1.1 更改数据类型 ETG1020基础数据中包括浮点数 REAL&#xff0c;可以直接使用浮点数。 这里在xml…

STM32 | 库函数与寄存器开发区别及LED等和按键源码(第三天)

STM32 | STM32F407ZE(LED寄存器开发续第二天源码)STM32 第三天 一、 库函数与寄存器开发区别 1.1 寄存器 寄存器开发优点 直接操作寄存器,运行效率高。 寄存器开发缺点 1、开发难度大,开发周期长 2、代码可阅读性差,可移植差 3、后期维护难度高 1.2库函数 库函数开…

稀碎从零算法笔记Day14-LeetCode:同构字符串

题型&#xff1a;字符串、哈希表 链接&#xff1a;205. 同构字符串 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述 给定两个字符串 s 和 t &#xff0c;判断它们是否是同构的。 如果 s 中的字符可以按某种映射关系替换得到 t &#xff0c;那…

017集——圆弧(ARC)转多段线(lwpolyline)——cad vba 中按一定精度拟合加点实现

在国土资源管理项目中&#xff0c;我们经常会遇到CAD转gis数据实现入库&#xff0c;而cad中的arc圆弧转为gis数据只能转出弧的顶点坐标&#xff0c;导致图形变形失真。若一个一个对弧进行手工增加点转为多段线&#xff0c;耗时耗力&#xff0c;效率极其低下。这里给出解决方案&…