当前位置: 首页 > article >正文

学习Vue之商城案例(代码+详解)

 目前,我们学习Vue的一些基础的知识,那么就让我们做一个像下图这样简单的商城案例吧。


目录

通过脚手架创建项目

安装axios和bootstrap组件

安装axios和bootstrap

在保存的时候不进行格式化校验

初步定义App.vue文件

初步渲染组件页面

根据接口渲染商品

根据结构数据渲染商品具体信息

对商品数量组件进行渲染与操作

实现数量组件的增加与减少

实现全选选项

实现价格合计

实现结算后数量的变化


通过脚手架创建项目

本篇通过@vue/cli脚手架来创建形目,具体的如何导入vue、如何创建就不过多赘述啦~

安装axios和bootstrap组件

npm i axios
npm i bootstrap

安装axios和bootstrap

在main.js中配置axios和bootstrap

配置axios

import axios from 'axios'

引入bootstrap

import 'bootstrap/dist/css/bootstrap.min.css'

import 'bootstrap/dist/js/bootstrap.min.js'

把axios放到vue对象的属性上

Vue.prototype.axios = axios

在保存的时候不进行格式化校验

在vue.config.js中输入,防止系统因格式太多报错

lintOnSave: false

初步定义App.vue文件

在script中引入所有组件

import (组件名) from '(组件路径)';

本文件中引入:

 import shopHeader from './components/shopHeader.vue';
 import good from './components/good.vue';
 import shopFoot from './components/shopFoot.vue';
 import count from './components/count.vue';

注册组件

在export default中注册

components:{``(组件名称)``}

本文件中引入:

 components:{
      shopHeader,good,shopFoot,count
 }

利用组件标签初步渲染页面

在template中使用自定义组件标签

 <div>
     <shop-Header></shop-Header>
     <div style="margin-top: 40px;"></div>
     <good></good>
     <shopFoot></shopFoot>
 </div>

初步渲染组件页面

在这里,我们将初步的格式给到大家~

shopHeader.vue

 <template>
     <div class="my-header">购物车案例</div>
 </template>
 ​
 <script>
     export default {
 ​
     }
 </script>
 ​
 <style lang="less" scoped>
     .my-header {
         height: 45px;
         line-height: 45px;
         text-align: center;
         background-color: #1d7bff;
         color: #fff;
         position: fixed;
         top: 0;
         left: 0;
         width: 100%;
         z-index: 2;
     }
 </style>

goods.vue

 <template>
     <div class="my-goods-item">
         <div class="left">
             <div class="custom-control custom-checkbox">
                 <input type="checkbox" class="custom-control-input" id="input">
                 <label class="custom-control-label" for="input">
                     <img src="http://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg" alt="">
                 </label>
             </div>
         </div>
         <div class="right">
             <div class="top">商品名字</div>
                 <div class="bottom">
                     <span class="price">¥ 100</span>
                     <span>数量组件</span>
                 </div>
             </div>
         </div>
     </template>
 ​
 <script>
     export default {
 ​
     }
 ​
 </script>
 ​
 <style lang="less" scoped>
     .my-goods-item {
         display: flex;
         padding: 10px;
         border-bottom: 1px solid #ccc;
         .left {
             img {
                 width: 120px;
                 height: 120px;
                 margin-right: 8px;
                 border-radius: 10px;
             }
             .custom-control-label::before,
             .custom-control-label::after {
                 top: 50px;
             }
         }
         .right {
             flex: 1;
             display: flex;
             flex-direction: column;
             justify-content: space-between;
             .top{
                 font-size: 14px;
                 font-weight: 700;
             }
             .bottom {
                 display: flex;
                 justify-content: space-between;
                 padding: 5px 0;
                 align-items: center;
                 .price {
                     color: red;
                     font-weight: bold;
                 }
             }
         }
     }
 </style>

Count.vue

<template>
     <div class="my-counter">
         <button type="button" class="btn btn-light" >-</button>
         <input type="number" class="form-control inp" >
         <button type="button" class="btn btn-light">+</button>
     </div>
 </template>
 ​
 <script>
     export default {
 ​
     }
 </script>
 ​
 <style lang="less" scoped>
 .my-counter {
     display: flex;
     .inp {
         width: 45px;
         text-align: center;
         margin: 0 10px;
     }
     .btn, .inp{
         transform: scale(0.9);
     }
 }
 </style>

shopFooter.vue

<template>
     <!-- 底部 -->
     <div class="my-footer">
         <!-- 全选 -->
         <div class="custom-control custom-checkbox">
             <input type="checkbox" class="custom-control-input" id="footerCheck">
             <label class="custom-control-label" for="footerCheck">全选</label>
         </div>
         <!-- 合计 -->
         <div>
             <span>合计:</span>
             <span class="price">¥ 0</span>
         </div>
         <!-- 按钮 -->
         <button type="button" class="footer-btn btn btn-primary">结算 ( 0 )</button>
     </div>
 </template>
 ​
 <script>
     export default {
 ​
     }
 </script>
 ​
 <style lang="less" scoped>
     .my-footer {
             position: fixed;
             z-index: 2;
             bottom: 0;
             width: 100%;
             height: 50px;
             border-top: 1px solid #ccc;
             display: flex;
             justify-content: space-between;
             align-items: center;
             padding: 0 10px;
             background: #fff;
         .price {
             color: red;
             font-weight: bold;
             font-size: 15px;
         }
         .footer-btn {
             min-width: 80px;
             height: 30px;
             line-height: 30px;
             border-radius: 25px;
             padding: 0;
         }
     }
 </style>

根据接口渲染商品

调用created钩子函数

created是一个生命周期钩子函数,用于在Vue实例被创建后立即被调用。

 created(){
         this.loadData()
 },

用数据定义一个空的 goods 数组,用于存储商品数据

data(){
   return {
     goods:[]
   }
}

给上述自定义组件标签设定一个循环输出属性

是一个循环,它遍历 goods 数组,并为每个数组项渲染一个 <good> 组件

在goods数组中循环item数据,标识item.id是唯一标识,并把item传递给good组件

<good v-for="item in goods" :key="item.id" :good="item" ></good>

设计loadData函数来获取接口数据

方法使用 axios 库发送一个 GET 请求到 h ttps://applet-base-api-t.itheima.net/api/goods 来获取商品数据

this.axios.get('https://applet-base-api-t.itheima.net/api/goods').then(res=>{

}

了解接口数据属性

数据中有goods_name(商品名称),goods_price(商品价格),id(商品id),inputValue(商品购买数量),inputVisible(商品是否被勾选属性)为我们能用到的

判断有没有存入成功

如果请求成功,它将更新 goods 数组,并为每个商品设置一个初始的 inputValue

// 请求成功
if (res.data.status === 0){
         this.goods = res.data.data;
}

若未请求成功还要做出未反馈响应

else{
         alert('网络出小差了~')
}

根据结构数据渲染商品具体信息

在good.vue中进行修改

接收父组件传递的信息

props:['good']

利用插值表达式修改商品名称与价格

{{good.goods_name}} 
{{good.goods_price}}

对商品数量组件进行渲染与操作

对商品数量组件基本渲染

在good.vue中引入count组件并注册

import count from '@/components/count.vue';
components:{count},

用count组件自定义标签替换span标签中的数量组件文字

:good="good"用来接收good数据

<count :good="good"></count>

将数量组件设置默认为1

在上述请求数据的if语句中直接设定默认值

利用三元运算符来输入,item.inputValue在输入时就为空,所以当请求到的值为空时,默认输入为1,否则,把item.inputValue的数据取整

this.goods.forEach(item=>{
           item.inputValue = item.inputValue === "" ? 1 : parseInt(item.inputValue);
})

在count组件中渲染此数据

props:['good'],

将count组件中的input标签加上用来渲染

v-model="good.inputValue"

实现数量组件的增加与减少

首先,要在count上给“+”“-”按钮添加点击事件

@click="sub"
@click="add"

因为在组件内不能单独的进行运算,所以我们要把点击事件传递到父元素上去,在本实例中,我们要传递到父元素的父元素上

methods:{
     add(){
          this.$emit('add',this.good.id)
     },
     sub(){
          this.$emit('sub',this.good.id)
     }
}

在count中父元素为good组件,所以我们还要从good组件传递到app.vue中,

在good组件中,引用的count标签内接收一下

@add="add" @sub="sub"

并再次传递到App.vue上

methods:{
     add(id){
          this.$emit('add',id)
     },
     sub(id){
          this.$emit('sub',id)
     }
}

在App.vue的good标签中也接收一下

@add="add" @sub="sub"

并在定义增加与减少的语法

增加语法:方法用于增加商品的数量,它遍历 goods 数组,找到对应 id 的商品,并将其 inputValue 加一

add(id){
            this.goods.forEach(item=>{
                if (item.id === id){
                    item.inputValue++;
                }
            })
        }

减少语法:方法用于减少商品的数量,它遍历 goods 数组,找到对应 id 的商品,并将其 inputValue 减一。如果 inputValue 已经为一,它将从 goods 数组中移除该商品

sub(id){
            this.goods.forEach(item=>{
                if (item.id === id){
                    if(item.inputValue > 1){
                        item.inputValue--;
                    }else{
                        this.goods.splice(item,1);
                    } 
                }
            })
        }

实现全选选项

找到shopFoot.vue组件

首先引入商品组件,根据依赖的数据(即 goods 数组)自动更新

props:['goods']

在App.vue的shopFoot标签中也要引入goods

:goods="goods"

在全选input框中添加checkAll自定义计算属性

v-model="checkAll"

checkAll计算属性方法

get:every 方法会遍历数组中的每个元素,并对每个元素执行一个回调函数。如果所有商品的 inputVisible 属性都为 true,则 every 方法返回 true,表示所有商品都被选中;否则,every方法返回 false,表示至少有一个商品没有被选中

set:接收一个参数 val,这个参数表示复选框的选中状态(true 或 false)。然后,它遍历 goods 数组中的每个商品,并将每个商品的 inputVisible属性设置为 val。这样,所有商品的选中状态就会被更新为与复选框的选中状态一致。

checkAll:{
                get:function(){
                    return this.goods.every(item=>item.inputVisible === true)
                },
                set:function(val){
                    this.goods.forEach(item=>{
                    item.inputVisible = val;
                    })
                }
            }

在good.vue中将复选框的 v-model 绑定到 good.inputVisible 属性,用于控制复选框的选中状态。

v-model="good.inputVisible"

实现价格合计

在上述引入good组件的基础下

定义一个totalPrice计算属性并用插值表达式替代0

{{totalPrice}}

totalPrice计算属性语法:

声明一个局部变量 total,初始值为 0,用于累加商品的总价。

this.goods.forEach(item=>{...}):使用 forEach 方法遍goods 数组中的每个商品。

if(item.inputVisible ===true){...}:检查当前商品的inputVisibl属性是否为true,即该商品是否被选中。

total += item.goods_price * item.inputValue;:如果商品被选中,将其价格(goods_pric)乘以数量(inputValu),并累加到 total变量中。

return total;:方法结束时返回 total 变量的值,即购物车中已选中商品的总价。

totalPrice(){
                let total = 0;
                this.goods.forEach(item=>{
                    if(item.inputVisible ===true){
                        total += item.goods_price * item.inputValue;
                    }
                })
                return total;
            },

实现结算后数量的变化

在上述引入good组件的基础下

定义一个totalnum计算属性并用插值表达式替代0

{{ totalnum }}

totalnum计算属性语法:定义了一个名为 totalnum 的计算属性,它用于计算购物车中已选中商品的数量。这个方法通过遍历 goods数组,检查每个商品的 inputVisible属性是否为 true,如果是,则将计数器 num 加一。最终,方法返回计数器的值,即购物车中已选中商品的数量

 totalnum(){
                 let num = 0;
                 this.goods.forEach(item=>{
                     if(item.inputVisible ===true){
                         num++
                     }
                 })
                 return num;
             }

到这里,商城的Vue案例就结束了,大家是否成功的跟上并且完成了呢,是否根据这个小案例更深入学习到了Vue的知识了呢?路漫漫其修远兮,吾将上下而求索,一起加油吧!


http://www.kler.cn/a/383232.html

相关文章:

  • pytest自动化测试数据驱动yaml/excel/csv/json
  • golang , chan学习
  • Datawhale AI 冬令营学习笔记-零编程基础制作井字棋小游戏
  • WebRTC服务质量(08)- 重传机制(05) RTX机制
  • 某集团GIF动态验证码识别
  • C#(委托)
  • 视频——教学篇——拍摄和剪辑
  • 1.1 关于游戏编程
  • spp/ble蓝牙模块在地震仪上的创新应用方案
  • 【docker】5. 背景知识(了解)
  • [大模型]视频生成-Sora简析
  • 基于 JAVASSM(Java + Spring + Spring MVC + MyBatis)框架开发一个九宫格日志系统
  • Kotlin函数由易到难
  • sqlserver使用bak文件恢复数据库
  • 解密 C# 中的迭代器与 yield:高效管理序列的艺术
  • 阿里云文本内容安全处理
  • Vue3中实现原生CSS完成圆形按钮点击粒子效果和定点旋转动画
  • 云联网:打造多云互联新生态,助力企业数字化转型
  • DICOM标准:重要概念——多种传输语法、私有数据元素标签、唯一标识符(UID)等详解
  • 梧桐数据库与GBase建表方式比较
  • 【机器学习】连续属性离散化与sklearn.preprocessing.KBinsDiscretizer
  • 非[I,P]结构的生成矩阵如何巧妙计算校验矩阵
  • 题目练习之二叉树那些事儿(续集)
  • Linux入门之vim
  • 深度学习常用开源数据集介绍【持续更新】
  • 《华为工作法》读书摘记