IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> vue3 父向子组件、子向父组件和兄弟组件之间通信的购物车案例 -> 正文阅读

[JavaScript知识库]vue3 父向子组件、子向父组件和兄弟组件之间通信的购物车案例


一、需要的工具

二、项目目录

在这里插入图片描述

三、使用步骤

  • 在终端定位到需要创建项目的目录输入npm init vite-app demo-cart
  • npm install less -D安装less
  • 导入bootstrap
  • npm install axios安装axios
  • npm install mitt安装mitt

四、项目源码

  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <link rel="icon" href="/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vite App</title>
</head>
<body>
  <div id="app"></div>
  <script type="module" src="/src/main.js"></script>
</body>
</html>

  • main.js
import { createApp } from 'vue'
import App from './App.vue'
// 引入bootstrap
import './assets/css/bootstrap.min.css'
import './index.css'
//引入axios
import axios from 'axios'
// 导入封装的函数
import getTotall from './utils/compute.js'
axios.defaults.baseURL = 'https://applet-base-api-t.itheima.net/'
const app = createApp(App)
// 声明axios为全局属性
app.config.globalProperties.$http = axios
app.config.globalProperties.$getTotall = getTotall
app.mount('#app')

- index.css

```css
:root{
 font-size: 14px;
}
/*复选框的样式 */
.custom-checkbox .custom-control-label::before {
  border-radius: 10px;
} 
  • App.vue
<template>
  <div class="app-container">
    <Header title="我的购物车案例"></Header>
    <Goods :goodsList="goodsList" @getNewCount="getNewCount"></Goods>
    <Footer :goodsList="goodsList" @getFullSelect="fullSelect"></Footer>
  </div>
</template>

<script>
import Header from './components/EsHeader/Header.vue'
import Footer from './components/EsFooter/Footer.vue'
import Goods from './components/EsGoods/Goods.vue'

export default {
  name: 'App',
  data() {
    return { goodsList: [] }
  },
  created() {
    //  在创建阶段调用getGoodsList方法获取后台数据
    this.getGoodsList()
  },
  methods: {
    async getGoodsList() {
      // 使用全局挂载的属性$http发送ajax请求
      // 解构出data并赋值给res
      const { data: res } = await this.$http.get('api/cart')
      // 判断请求是否成功,并赋值给goodList
      if (res.status == 200) {
        console.log(res.message)
        this.goodsList = res.list
        // 初始化商品总价和商品总数
        this.$getTotall(this.goodsList)
      } else return console.log('获取购物车的数据失败!')
    },
    //控制全选
    fullSelect(list) {
      this.goodsList = list
    },
    //根据商品Id更新商品总价和总数
    getNewCount(e) {
      const findResult = this.goodsList.find(x => x.goods_id === e.goodsId)
      if (findResult) {
        findResult.goods_count = e.newCount
        this.$getTotall(this.goodsList)
      }
    }
  },
  components: {
    Header,
    Footer,
    Goods
  }
}
</script>
<style lang="less" scoped>
.app-container {
  margin-top: 50px;
}
</style>

  • Count.vue
<template>
  <div>
    <button @click="countSub">-</button>
    <input type="text" v-model.lazy="myCount" />
    <button @click="countAdd">+</button>
  </div>
</template>

<script>
export default {
  name: 'MyCount',
  props: {
    count: { type: Number, default: 0 },
    id: { type: Number, default: 0 }
  },
  //自定义事件
  emits: ['getNewCount'],
  data() {
    return { myCount: this.count, myId: this.id }
  },
  methods: {
    // 减少商品的数量
    countSub() {
      if (this.myCount <= 1) return 0
      this.myCount--
      // 获取最新的count和商品的id
      this.$emit('getNewCount', { newCount: this.myCount, goodsId: this.myId })
    },
    // 增加商品的数量
    countAdd() {
      this.myCount++
      this.$emit('getNewCount', { newCount: this.myCount, goodsId: this.myId })
    }
  },
  watch: {
    // 添加监听器监听输入myCount数值的变化
    myCount(newVal) {
      const parseResult = parseInt(newVal)
      // 当数值小于1或是个非数值时
      if (isNaN(newVal) || newVal < 1) {
        this.myCount = 1
      }
      // 当数值是小数时
      else if (String(newVal).indexOf('.') !== -1) {
        this.myCount = parseResult
      }
      this.$emit('getNewCount', { newCount: this.myCount, goodsId: this.myId })
    }
  }
}
</script>

<style lang="less" scoped>
input {
  width: 25px;
  height: 20px;
  text-align: center;
  line-height: 30px;
  outline: none;
  margin: 0 5px;
}
button {
  border: 0px;
}
</style>

  • Footer.vue
<template>
  <div class="footer-container">
    <div class="custom-control custom-checkbox">
      <input type="checkbox" class="custom-control-input" id="fullSelect" v-model="isFull" @change="isFullSelect" />
      <label class="custom-control-label" for="fullSelect">全选</label>
    </div>
    <div>
      合计<span class="totalNum">¥{{ totalPrice }}</span>
    </div>
    <div class="btn">
      <button :disabled="totalNum > 0 ? false : true">结算({{ totalNum }})</button>
    </div>
  </div>
</template>

<script>
import bus from '../../EventBus/eventBus.js'
export default {
  name: 'MyFooter',
  props: {
    goodsList: {
      type: Array,
      default: []
    }
  },
  data() {
    return {
      // 合计
      totalPrice: 0,
      // 结算
      totalNum: 0,
      // 控制全选的状态
      isFull: false
    }
  },
  // 自定义事件
  emits: ['getFullSelect'],
  methods: {
    // 全选
    isFullSelect(e) {
      this.goodsList.forEach(x => (x.goods_state = e.target.checked))
      this.$emit('getFullSelect', this.goodsList)
      this.$getTotall(this.goodsList)
    }
  },
  created() {
    // 接受来自EventBus的自定义事件
    bus.on('isfull', val => (this.isFull = val))
    bus.on('getTotalCount', val => (this.totalPrice = val))
    bus.on('getSum', val => (this.totalNum = val))
  }
}
</script>

<style lang="less" scoped>
.footer-container {
  width: 100%;
  height: 60px;
  padding: 10px;
  background: #fff;
  position: fixed;
  bottom: 0;
  left: 0;
  z-index: 999;
  border-top: 1px solid #efefef;
  display: flex;
  align-items: center;
  justify-content: space-between;
  .totalNum {
    color: #f00;
  }
  button {
    background-color: #00f;
    color: #fff;
    width: 120px;
    height: 40px;
    border-radius: 40px;
    border: 0px;
  }
}
</style>

  • Goods.vue
<template>
  <div class="goods-container" v-for="item in goodsList" :key="item.goods_id">
    <div class="container-left">
      <div class="img">
        <div class="custom-control custom-checkbox">
          <input type="checkbox" class="custom-control-input" :id="item.goods_id" v-model="item.goods_state" @change="checkSelect" />
          <label class="custom-control-label" :for="item.goods_id"><img :src="item.goods_img" alt="!" /></label>
        </div>
      </div>
    </div>
    <div class="container-right">
      <div class="title right-top">
        <h5>{{ item.goods_name }}</h5>
      </div>
      <div class="right-bottom">
        <div class="price">¥{{ item.goods_price }}</div>
        <div class="count">
          <count :count="item.goods_count" :id="item.goods_id" @getNewCount="getCount"></count>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import count from '../EsCount/Count.vue'
import bus from '../../EventBus/eventBus.js'
export default {
  name: 'MyGoods',
  props: { goodsList: { type: Array, default: [] } },
  emits: ['getNewCount'],
  components: {
    count
  },
  methods: {
    // 控制全选
    checkSelect() {
      this.$getTotall(this.goodsList)
      if (this.goodsList.every(x => x.goods_state === true)) {
        bus.emit('isfull', true)
      } else {
        bus.emit('isfull', false)
      }
    },
    getCount(val) {
      // 向App组件发送最新的count值
      this.$emit('getNewCount', val)
    }
  }
}
</script>

<style lang="less" scoped>
.goods-container {
  display: flex;
  padding: 10px;
  border-bottom: 1px solid #efefef;
  img {
    display: block;
    width: 100px;
    height: 100px;
  }
  .container-left {
    margin-right: 10px;
  }

  .container-right {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    flex: 1;
  }
  .right-bottom {
    display: flex;
    justify-content: space-between;
    align-items: center;
    .price {
      color: #f00;
    }
  }
  .custom-control-label::before,
  .custom-control-label::after {
    top: 3.25rem;
  }
}
</style>

  • Header.vue
<template>
  <!-- 自定义标题格式 -->
  <div class="header-container" :style="{ color: fontColor, backgroundColor: bgColor, fontSize: fSize + 'px' }">
    <span>{{ title }}</span>
  </div>
</template>

<script>
export default {
  name: 'MyHeader',
  props: {
    title: {
      type: String,
      require: true,
      default: '购物车案例'
    },
    fontColor: {
      type: String,
      require: true,
      default: '#fff'
    },
    bgColor: {
      type: String,
      require: true,
      default: '#00f'
    },
    fSize: {
      type: Number,
      require: true,
      default: 16
    }
  }
}
</script>

<style lang="less" scoped>
.header-container {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 50px;
  line-height: 50px;
  text-align: center;
  z-index: 999;
}
</style>

  • eventBus.js
// 导入mitt
import mitt from 'mitt'
// 创建bus对象
const bus = mitt()
export default bus

  • compute.js
// 封装发送总数和总价的函数
import bus from '../EventBus/eventBus.js'
export default function getTotall(arr) {
  // 已选中的商品总价
  let totalCount = 0
  // 已选中的商品总和
  let sum = 0
  arr.filter(x => x.goods_state === true).forEach(x => (totalCount += x.goods_price * x.goods_count))
  arr.filter(x => x.goods_state === true).forEach(x => (sum += x.goods_count))
  // 发送商品总价
  bus.emit('getTotalCount', totalCount)
  // 发送商品总和
  bus.emit('getSum', sum)
}


五、项目地址

demo-cart

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-21 21:27:19  更:2022-07-21 21:29:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/11 12:31:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码