vue实现购物车案例

news2025/2/25 21:33:34

要求

  1. 可以进行购物车水果删除
  2. 可以进行水果数量增减
  3. 可以进行总价计算、购物车商品计算
  4. 选中所有水果也会一同勾选全选框,全选框勾选也能选中所有水果
  5. 可以记录购物车状态,当页面关闭后重新打开可以看到原先的购物车数据
    在这里插入图片描述

功能代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link rel="stylesheet" href="./css/inputnumber.css" />
  <link rel="stylesheet" href="./css/index.css" />
  <title>购物车</title>
</head>

<body>
  <div class="app-container" id="app">
    <!-- 顶部banner -->
    <div class="banner-box"><img src="http://autumnfish.cn/static/fruit.jpg" 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,index) in fruitList" :key="item.id">
            <div class="td"><input type="checkbox" v-model="item.isChecked" /></div>
            <div class="td"><img :src="item.icon" alt="" /></div>
            <div class="td">{{item.price}}</div>
            <div class="td">
              <div class="my-input-number">
                <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>
            <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/dist/vue.js"></script>
  <script>
    const defaultArr = [
      {
        id: 1,
        icon: './img/火龙果.png',
        isChecked: true,
        num: 2,
        price: 6,
      },
      {
        id: 2,
        icon: './img/荔枝.png',
        isChecked: false,
        num: 7,
        price: 20,
      },
      {
        id: 3,
        icon: './img/榴莲.png',
        isChecked: false,
        num: 3,
        price: 40,
      },
      {
        id: 4,
        icon: './img/鸭梨.png',
        isChecked: true,
        num: 10,
        price: 3,
      },
      {
        id: 5,
        icon: './img/樱桃.png',
        isChecked: false,
        num: 20,
        price: 34,
      },
    ]
    const app = new Vue({
      el: '#app',
      data: {
        // 水果列表
        fruitList: JSON.parse(localStorage.getItem('list')) || defaultArr
      },
      methods: {
        del(id) {
          this.fruitList = this.fruitList.filter(item => item.id != id)
        },
        add(id) {
          const fruit = this.fruitList.find(item => item.id === id)
          fruit.num++;
        },
        sub(id) {
          const fruit = this.fruitList.find(item => item.id === id)
          fruit.num--;
        }
      },
      computed: {
        isAll: {
          get() {
            return this.fruitList.every(item => item.isChecked)
          },
          set(value) {
            this.fruitList.forEach(item => item.isChecked = value)
          }
        },
        totalPrice() {
          return this.fruitList.reduce((sum, item) => {
            if (item.isChecked) {
              return sum += item.price * item.num
            } else {
              return sum
            }
          }, 0)
        },
        totalNum() {
          return this.fruitList.reduce((sum, item) => {
            if (item.isChecked) {
              return sum += item.num
            } else {
              return sum
            }
          }, 0)
        }
      },
      watch: {
        fruitList: {
          deep: true,
          handler(newValue) {
            localStorage.setItem("list", JSON.stringify(newValue))
          }
        }
      }
    })
  </script>
</body>

</html>

css代码

index.css

.app-container {
  padding-bottom: 300px;
  width: 800px;
  margin: 0 auto;
}
@media screen and (max-width: 800px) {
  .app-container {
    width: 600px;
  }
}
.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;
}
.th {
  color: rgba(0, 0, 0, 0.85);
  font-weight: 500;
  text-align: left;
  background: #fafafa;
  border-bottom: 1px solid #f0f0f0;
  transition: background 0.3s ease;
}
.th.num-th {
  flex: 1.5;
}
.th {
  text-align: center;
}
.th:nth-child(4),
.th:nth-child(5),
.th:nth-child(6),
.th:nth-child(7) {
  text-align: center;
}
.th.th-pic {
  flex: 1.3;
}
.th:nth-child(6) {
  flex: 1.3;
}

.th,
.td {
  position: relative;
  padding: 16px 16px;
  overflow-wrap: break-word;
  flex: 1;
}
.pick-td {
  font-size: 14px;
}
.main,
.empty {
  border: 1px solid #f0f0f0;
  margin-top: 10px;
}
.tr {
  display: flex;
  cursor: pointer;
  border-bottom: 1px solid #ebeef5;
}
.tr.active {
  background-color: #f5f7fa;
}
.td {
  display: flex;
  justify-content: center;
  align-items: center;
}

.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;
}
.check-all {
  cursor: pointer;
}
.price {
  color: hotpink;
  font-size: 30px;
  font-weight: 700;
}
.price-box {
  display: flex;
  align-items: center;
}
.empty {
  padding: 20px;
  text-align: center;
  font-size: 30px;
  color: #909399;
}
.my-input-number {
  display: flex;
}
.my-input-number button {
  height: 40px;
  color: #333;
  border: 1px solid #dcdfe6;
  background-color: #f5f7fa;
}
.my-input-number button:disabled {
  cursor: not-allowed!important;
}
.my-input-number .my-input__inner {
  height: 40px;
  width: 50px;
  padding: 0;
  border: none;
  border-top: 1px solid #dcdfe6;
  border-bottom: 1px solid #dcdfe6;
}

inputNumber.css

  position: relative;
  display: inline-block;
  width: 140px;
  line-height: 38px;
}
.my-input-number span {
  -moz-user-select: none;
  -webkit-user-select: none;
  -ms-user-select: none;
}
.my-input-number .my-input {
  display: block;
  position: relative;
  font-size: 14px;
  width: 100%;
}
.my-input-number .my-input__inner {
  -webkit-appearance: none;
  background-color: #fff;
  background-image: none;
  border-radius: 4px;
  border: 1px solid #dcdfe6;
  box-sizing: border-box;
  color: #606266;
  display: inline-block;
  font-size: inherit;
  height: 40px;
  line-height: 40px;
  outline: none;
  padding: 0 15px;
  transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
  width: 100%;
  padding-left: 50px;
  padding-right: 50px;
  text-align: center;
}
.my-input-number .my-input-number__decrease,
.my-input-number .my-input-number__increase {
  position: absolute;
  z-index: 1;
  top: 1px;
  width: 40px;
  height: auto;
  text-align: center;
  background: #f5f7fa;
  color: #606266;
  cursor: pointer;
  font-size: 13px;
}
.my-input-number .my-input-number__decrease {
  left: 1px;
  border-radius: 4px 0 0 4px;
  border-right: 1px solid #dcdfe6;
}
.my-input-number .my-input-number__increase {
  right: 1px;
  border-radius: 0 4px 4px 0;
  border-left: 1px solid #dcdfe6;
}
.my-input-number .my-input-number__decrease.is-disabled,
.my-input-number .my-input-number__increase.is-disabled {
  color: #c0c4cc;
  cursor: not-allowed;
}

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

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

相关文章

【ELFK】之Filebeat

一、Filebeat介绍 1、Filebeat是什么&#xff1f; Filebeat适用于转发和集中数据的轻量级传送工具&#xff0c;Filebeat监视了指定的日志文件或位置&#xff0c;收集日志事件&#xff0c;并将他们转发到Elasticsearch或Logstash进行索引。 **Filebeat的工作方式&#xff1a;*…

【Java数据结构重点知识】第三节:认识包装类和泛型

目录 一&#xff1a;包装类 1.什么是包装类 2.装箱和拆箱 3.一个经典面试题 二&#xff1a;泛型 1.什么是泛型 2.语法 3.使用 4.泛型是如何编译的 5.泛型的上界 一&#xff1a;包装类 1.什么是包装类 &#xff08;1&#xff09;概念&#xff1a;在Java中&#xff0…

Leetcode刷题---多数元素(计数、哈希表、摩尔投票)

题目描述&#xff1a; 思路一&#xff1a; 可以使用暴力破解来解决此问题&#xff0c;但是暴力破解的时间复杂度很高&#xff0c;在leetcode测试的时候爆出来超出时间限制的问题。 代码&#xff1a; class Solution {public int majorityElement(int[] nums) {int nnums.leng…

野指针成因与避免方法

野指针 定义&#xff1a;指针指向的位置是不可知的&#xff08;随机的&#xff0c;不正确的&#xff0c;没有明确限制的&#xff09; 一、野指针的成因 1、指针未初始化 #include <stdio.h>int main(void) {// 定义一个未初始化的指针并赋值int* p; *p 5;// 打印输出指…

compileSdkVersion, minSdkVersion,targetSdkVersion的区别

minSdkVersion : 主要用于安卓系统环境校验。开发人员觉得满足app最佳正常运行的最低安卓环境版本&#xff0c;如果安卓系统低于设置的minSdkVersion将无法安装。 targetSdkVersion: 主要用于安卓系统环境校验。开发人员觉得满足app最佳正常运行的最高安卓环境版本&#xff0c…

SwiftUI 如何为文件和图片加入便捷的快速预览功能

功能需求 在 SwiftUI 开发的 App 中,我们有时需要以快速预览(QuickLook)的方式向用户展示文件内容,自己动手去处理上百种文件类型的预览是不现实的。 如上图所示:我们在 SwiftUI 中实现了任意文件的快速预览,这是如何完成的呢? 在本篇博文中您将学到如下内容: 功能需…

资源管理命令-陈述式

资源管理介绍 资源管理概念 在kubernetes中&#xff0c;所有的内容都抽象为资源&#xff0c;用户需要通过操作资源来管理kubernetes。 //kubernetes的本质就是一个集群系统&#xff0c;用户可以在集群中部署各种服务&#xff0c;起始就是在kubernetes集群中运行一个个容器&am…

网络爬虫——urllib(5)

前言&#x1f36d; ❤️❤️❤️网络爬虫专栏更新中&#xff0c;各位大佬觉得写得不错&#xff0c;支持一下&#xff0c;感谢了&#xff01;❤️❤️❤️ Python网络爬虫_热爱编程的林兮的博客-CSDN博客 上一篇我们讲解有关ajax的相关案例&#xff0c;下面我们来学习新的关于ur…

开发小程序需要多少钱?

随着移动互联网的快速发展&#xff0c;小程序已经成为了企业、个人创业者获取用户、提升品牌影响力的重要工具。然而&#xff0c;对于许多初次接触小程序的人来说&#xff0c;开发小程序需要多少钱&#xff0c;是他们最关心的问题。 首先我们需要明确的是&#xff0c;开发小程…

AI技术发展:防范AI诈骗,守护数字安全

随着AI技术的迅猛发展&#xff0c;人工智能赋予了计算机更多的能力&#xff0c;包括自然语言处理、图像生成、声音合成等。这些领域的突破为人们提供了全新的体验和便捷&#xff0c;但同时也催生了一些潜在的安全风险&#xff0c;其中最突出的就是AI诈骗。本文将探讨如何防范AI…

echarts 地图迁徙与地图下钻

echarts 地图迁徙与地图下钻 一、迁徙 1、引入中国地图数据及echarts const china require("./echarts-mapJson-master/china.json") import * as echarts from echarts2、数据准备 // 点数据 需要hover或点击获取数据可以添加额外的参数&#xff0c; // 如{ val…

前端开发必知:HTML、Vue和React中的跨域页面跳转解决方案

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

万物皆可“云” 从杭州云栖大会看数智生活的未来

文章目录 前言一、云栖渐进&#xff1a;一个科技论坛的变迁与互联网历史互联网创新创业飞天进化飞天智能驱动数字中国 二、2023云栖大会&#xff1a;云计算人工智能 玩出科技跨界新花样大会亮点重磅嘉宾热门展览算力馆人工智能馆产业创新馆 总结 前言 10月31日&#xff0c;202…

查询平均提速 700%,奇安信基于 Apache Doris 升级日志安全分析系统

本文导读&#xff1a; 数智时代的到来使网络安全成为了不可忽视的重要领域。奇安信作为一家领先的网络安全解决方案领军者&#xff0c;致力于为企业提供先进全面的网络安全保护&#xff0c;其日志分析系统在网络安全中发挥着关键作用&#xff0c;通过对运行日志数据的深入分析…

做好团队沟通和协调 不可忽略的5个重点

做好团队的沟通和协调&#xff0c;可以确保成员之间及时交流、分享信息和解决问题。这有助于避免信息滞后和工作冲突&#xff0c;提高工作效率。如果沟通不顺畅&#xff0c;往往导致成员间信息传递不及时或遗漏&#xff0c;容易产生误解和冲突&#xff0c;影响项目进度地顺利开…

【LVS实战】02 搭建一个LVS-NAT模式实验

一、网络结构 用虚拟机搭建如下的几台机器&#xff0c;并配置如下的ip 关于虚拟机网卡和网络的配置&#xff0c;可以参考 iptables章节&#xff0c;05节&#xff1a;网络转发实验 主机A模拟外网的机器 B为负载均衡的机器 C和D为 RealServer 二、C和D主机的网关设置 C和D机…

专业医学病例翻译公司推荐

我们知道&#xff0c;医学病例翻译在涉外看病的患者中具有广泛的应用&#xff0c;它可以帮助医生快速了解患者的病情&#xff0c;为治疗和药物处方提供关键信息。因此&#xff0c;对于出国看病的患者&#xff0c;医学病例翻译便成了不可或缺的重要工具。 翻译医学病例不仅要求译…

使用 Node.js 搭建 API 网关

您好&#xff0c; 如果喜欢我的文章或者想上岸大厂&#xff0c;可以关注公众号「量子前端」&#xff0c;将不定期关注推送前端好文、分享就业资料秘籍&#xff0c;也希望有机会一对一帮助你实现梦想 外部客户端访问微服务架构中的服务时&#xff0c;服务端会对认证和传输有一些…

如何将微信视频号的视频保存到手机相册?

微信视频号是近年来微信推出的一项功能&#xff0c;它允许用户通过手机拍摄和分享短视频内容。然而&#xff0c;许多人纷纷表示他们想要保存微信视频号上的视频&#xff0c;以便在离线时观看。但一直以来这个需求腾讯一直没有开放随后也取消了复制视频链接功能&#xff0c;为此…

最全的操作系统原版镜像,镜像winxp、win7、8、10、11,win server2008、2012、2016、2019、2022各版本系统下载 址

1、 MSDN原版系统(我告诉你&#xff09; [软件、镜像下载地址](https://www.imsdn.cn/)2、 HelloWindows.cn 精校 完整 极致 Windows系统下载仓储站 镜像、软件下载地址 3、系统库 镜像下载地址 4、 山己几子木 镜像下载地址