Vue实现购物车页面

news2025/1/10 20:54:46

包括全选反选、数据的统计和本地持久化的处理。

html

<!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="img/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" v-for="(item,index) in fruitList" :key="item.id">
            <div class="tr" :class="{active:item.isChecked}">
              <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 class="decrease" @click="item.num-=1" :disabled="item.num<=0"> - </button>
                  <span class="my-input__inner">{{item.num}}</span>
                  <button class="increase" @click="add(item.id)"> + </button>
                </div>
              </div>
              <div class="td">{{item.num*item.price}}</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">{{allMoney}}</span></span>
            <!-- 结算按钮 -->
            <button class="pay">结算( {{allNum}} )</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 += 1
        }
      },

      computed:{
        isAll:{
          get(){
            return this.fruitList.every(item => item.isChecked)
          },
          set(value){
            this.fruitList.forEach(item => {
              item.isChecked = value
            });
          }
        },
        allMoney(){
            return this.fruitList.reduce((sum,item) => {
              if(item.isChecked){
                return sum+item.num*item.price
              }
              else{
                return sum
              }
            },0)
        },
        allNum(){
          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

.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

.my-input-number {
  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/1109453.html

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

相关文章

289. 生命游戏 Python

文章目录 一、题目描述示例 1示例 2 二、代码三、解题思路 一、题目描述 根据 百度百科 &#xff0c; 生命游戏 &#xff0c;简称为 生命 &#xff0c;是英国数学家约翰何顿康威在 1970 年发明的细胞自动机。 给定一个包含 m n 个格子的面板&#xff0c;每一个格子都可以看成…

以“大数据+云服务”为核心的SaaS云平台 智慧校园管理平台 教师端、家长端、学生端全套源码

智慧校园全套源码 电子班牌系统源码 家校互联小程序源码 智慧校园以互联网为基础&#xff0c;以“大数据云服务”为核心&#xff0c;融合校园教学、管理、生活软硬件平台&#xff0c;定义智慧校园新生活。智慧校园管理平台管理者、教师、学生、家长提供一站式智慧校园解决方案…

基于Java+SpringBoot+Vue前后端分离的宠物领养系统

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 近年来&#xff0c;随…

和鲸ModelWhale与中科可控X系列异构加速服务器完成适配认证,搭载海光芯片,构筑AI算力底座

AIGC 时代&#xff0c;算力作为新型生产力&#xff0c;是国家和企业构建竞争优势的关键。而随着传统计算方式无法满足新时代激增的算力需求&#xff0c;计算场景的多元化和计算应用的复杂化推动了 CPUGPU 异构平台的加速组建。在此全球激烈角逐的大趋势下&#xff0c;我国信创产…

再有国产手机宣布自研操作系统,去美国化成潮流,谷歌自作自受

在小米正在大举宣传自研操作系统之后&#xff0c;日前vivo也宣布将自研操作系统&#xff0c;它们未来的目标都是从兼容安卓到实现真正的独立自主&#xff0c;摆脱对美国谷歌的依赖&#xff0c;实现类似于苹果的封闭系统。 国产手机自研操作系统&#xff0c;首先是考虑到系统的供…

事件知识图谱综述10.17+10.18 弃

事件知识图谱综述 摘要介绍2 什么是事件知识图谱&#xff1a;历史视角2.1 EKG的简要历史2.2 EKG的定义 什么是EKG&#xff1a;本体视角3.1 事件架构归纳 摘要 除了以实体为中心的知识&#xff0c;通常以知识图谱&#xff08;KG&#xff09;的形式组织外&#xff0c;事件也是世…

单身狗1和单身狗2(C语言版)

目录 1. 单身狗1 2. 单身狗2 1. 单身狗1 题目&#xff1a; 一个数组中只有一个数字是出现一次&#xff0c;其他所有数字都出现了两次。 编写一个函数找出这一个只出现一次的数字。 例如&#xff1a;有数组的元素是&#xff1a;1&#xff0c;2&#xff0c;3&#xff0c;4&a…

输入/输出的实用性-SOLIDWORKS 2024新功能

导出为 Extended Reality 您可以将 SOLIDWORKS CAD 文件导出为 .glb 或 .gltf 文件格式。 文件包含以下信息&#xff0c;例如几何体、外观、纹理、动画、运动算例、配置、显示状态、爆炸视图、光源 和元数据。对于大文件&#xff0c;导出支持 Draco&#xff0c;这是 .glb 和 .…

ChatGPT对于留学生论文写作有哪些帮助?

2022年11月&#xff0c;OpenAI公司的智能聊天产品ChatGPT横空出世&#xff0c;并两个月之内吸引了超过1亿用户&#xff0c;打破了TikTok&#xff08;抖音国际版&#xff09;9个月用户破亿的纪录。 划时代的浪潮 ChatGPT的火爆立即引起了全球关注并成为热门话题&#xff0c;它…

Mysql 内外链接,索引,事务,用户管理以及用C语言链接Mysql

文章目录 内外链接索引索引的相关操作全文索引 事务事务的操作事务的隔离级别隔离级别3个记录隐藏列字段 用户管理权限修改 使用C语言链接数据库 内外链接 两张表直接做笛卡尔积为内连接&#xff0c;之前使用的都是内连接 两张表&#xff1a;stu和exam 将两张表进行连接&…

Arya碎碎念 | 我的创作纪念日——写在成为创作者的第1095天

前言 打开博客创作者页面&#xff0c;发现CSDN发送过来一条私信&#xff0c;提醒我3年前发了第一篇博客。3年的时间里&#xff0c;大家都经历了很多事情&#xff0c;疫情 . . . . . 本篇博客是一些碎碎念&#xff0c;作为一个成为1095天创作者的纪念博客。 一、个人优质博客汇…

I2C的硬件实现

因为I2C是同步的&#xff0c;所以相对来说I2C更好用软件来实现&#xff0c;硬件却相对来说没这么好&#xff0c;但是硬件I2C通信也是有其优点的 我们是通过软件写入控制寄存器CR和数据寄存器DR&#xff0c;读取状态寄存器SR来了解外设电路当前处于什么状态&#xff0c;来实现I…

jvm实现的锁优化

目录 轻量级锁 轻量级锁的工作流程 轻量级锁的解锁 偏向锁 偏向锁的流程&#xff1a; 偏向锁和轻量级锁机区别&#xff1a; 其他优化 自旋锁和自适应自旋锁 锁消除 锁粗化 轻量级锁 “轻量级” 是相对于使用操作系统互斥量来实现的传统锁而言的&#xff0c;因此传统的…

Java学习笔记(四)——程序控制结构

一、顺序控制 二、分支控制 &#xff08;一&#xff09;单分支 &#xff08;二&#xff09;双分支 &#xff08;三&#xff09;多分支 &#xff08;四&#xff09;嵌套分支 &#xff08;五&#xff09;switch分支结构 &#xff08;六&#xff09;if和switch的选择 三、循…

Android Studio快速实现Flutter应用的国际化和多语言支持

文章目录 Flutter实现国际化和多语言支持添加依赖库Android Studio 安装flutter Intl插件项目初始化增加语言app中使用国际化在应用中切换语言&#xff1a;运行应用 总结easy_localization 插件intl 包Flutter GetX 包flutter_i18n 插件JSON 文件 Flutter实现国际化和多语言支持…

利用ArcGIS获取每一个冰川的中心位置经纬度坐标:要素转点和要素折点转点的区别

问题概述&#xff1a;下图是天山地区的冰川的分布&#xff0c;我们可以看到每一条冰川是一个面要素&#xff0c;要求得到每一个冰川&#xff08;面要素&#xff09;的中心经纬度坐标。 1.采用要素转点功能 选择工具箱的【数据管理工具】-【要素】-【要素转点】。完成之后再采用…

疯狂堆料!技嘉钛雕Z790 AORUS PRO X主板图赏

技嘉推出了钛雕Z790 AORUS PRO X主板。 现在这款新品已经来到了我们评测室&#xff0c;下面为大家带来图赏。 技嘉钛雕Z790 AORUS PRO X主板采用新一代超耐久显卡插槽&#xff0c;约58KG承重能力、内衬保护显卡PCB。 其采用1812相供电设计&#xff0c;4根双通道DDR5内存插槽&am…

药物滥用第四篇介绍

OXY&#xff1a; 羟考酮&#xff08;Oxycodone&#xff0c;OXY&#xff09;&#xff0c;分子式为C18H21NO4&#xff0c;是一种半合成的蒂巴因衍生物。羟考酮为半合成的纯阿片受体激动药&#xff0c;其作用机制与吗啡相似&#xff0c;主要通过激动中枢神经系统内的阿片受体而起镇…

FastAdmin框架实现数据表的增删改查

目录 简介 增加数据 修改数据 控制器&#xff08;controller&#xff09;代码&#xff1a; 查询数据 控制器&#xff08;controller&#xff09;代码&#xff1a; 模型&#xff08;model&#xff09;代码&#xff1a; 删除数据 控制器&#xff08;controller&#xff0…

2023年农村市场风口新商机:互联网+认养模式商业模式解析

背景&#xff1a;随着城市化进程的不断加快&#xff0c;人们对物质生活的要求和品质日益增高&#xff0c;特别是在疫情过亲身经历过病痛的折磨后&#xff0c;大家对自己的更加爱惜了&#xff0c;今天&#xff0c;微三云营销总监胡佳东发现一套2023年创业新项目新商机&#xff1…