学习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的知识了呢?路漫漫其修远兮,吾将上下而求索,一起加油吧!