开发环境: vue2.6.11 + vue-cli4.5.13
使用工具:vscode,vue脚手架
使用技术:vue基础,axios
案例描述:使用axios渲染数据,实现计算勾选商品的金额,勾选的数量,全选控制小选,小选控制全选,商品数量加减,手动输入商品数量
步骤如下:
项目初始化:创建项目名称,安装第三方模块
npm i bootstrap less less-loader@5.0.0 -D
-
按照需求, 把项目页面拆分成几个组件, 在components下创建
-
MyHeader组件 - 头部
-
MyFooter组件 - 底部
-
MyGoods组件 - 商品
-
MyCount组件 - 商品数量
-
然后引入到App.vue上注册
-
在main.js中引入bootStrap库 和 全部 配置
import "bootstrap/dist/css/bootstrap.css" // 引入第三方包里的某个css文件
import Vue from 'vue'
import App from './App.vue'
import "bootstrap/dist/css/bootstrap.css"
import axios from 'axios'
axios.defaults.baseURL = "https://www.escook.cn"
Vue.prototype.$axios = axios
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
以下是代码部分:
App.vue 引入头部组件、商品组件、底部组件
<template>
<div>
<MyHeader colorStr="white" content="购物车案例"></MyHeader>
<div style="padding-top: 45px; padding-bottom: 50px">
<MyGoods v-for="obj in arr" :key="obj.id" :goodObj="obj"></MyGoods>
</div>
<MyFooter :goodsObj="arr" @changeAllEvent="changeAllEventFn"></MyFooter>
</div>
</template>
<script>
import MyHeader from "./components/MyHeader.vue";
import MyGoods from "./components/MyGoods.vue";
import MyFooter from "./components/MyFooter.vue";
export default {
data() {
return {
arr: [],
};
},
methods: {
changeAllEventFn(bool) {
this.arr.forEach((obj) => {
obj.goods_state = bool;
});
},
},
created() {
this.$axios({
url: "/api/cart",
}).then((res) => {
// console.log(res.data.list);
this.arr = res.data.list;
});
},
components: {
MyHeader,
MyGoods,
MyFooter,
},
};
</script>
<style>
</style>
MyHeader.vue组件
<template>
<div class="my-header" :style="{ backgroundColor: bgc, color: colorStr }">
{{ content }}
</div>
</template>
<script>
export default {
//格式:
// 变量名:类型
// 变量名:{
// type:类型,
// default:默认值,
// required:true 此变量必须传入
// }
props: {
bgc: String, //背景颜色
colorStr: {
//文字颜色
type: String,
default: "black",
},
content: {
//标题文字
type: String,
required: true,
},
},
};
</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>
MyGoods.vue组件
<template>
<div class="my-goods-item">
<div class="left">
<div class="custom-control custom-checkbox">
<input
type="checkbox"
class="custom-control-input"
:id="goodObj.id"
v-model="goodObj.goods_state"
/>
<label class="custom-control-label" :for="goodObj.id">
<img :src="goodObj.goods_img" alt="" />
</label>
</div>
</div>
<div class="right">
<div class="top">{{ goodObj.goods_name }}</div>
<div class="bottom">
<span class="price">¥ {{ goodObj.goods_price }}</span>
<span>
<MyCount
:count="goodObj.goods_count"
@changeNum="changeNumFn"
></MyCount>
</span>
</div>
</div>
</div>
</template>
<script>
import MyCount from "./MyCount.vue";
export default {
props: {
goodObj: Object,
},
methods: {
changeNumFn(num) {
this.goodObj.goods_count = num;
},
},
components: {
MyCount,
},
};
</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>
MyCount.vue组件
<template>
<div class="my-counter">
<button type="button" class="btn btn-light" @click="subFn">-</button>
<input type="number" class="form-control inp" v-model.number="val" />
<button type="button" class="btn btn-light" @click="addFn">+</button>
</div>
</template>
<script>
export default {
data() {
return {
val: 0,
};
},
methods: {
addFn() {
this.val++;
},
subFn() {
if (this.val < 2) return;
this.val--;
},
},
watch: {
val(newVal) {
if (newVal < 1) {
this.val = 1;
}
this.$emit("changeNum", this.val); //传给父组件MyGoods
},
},
props: {
count: Number,
},
created() {
this.val = this.count;
},
};
</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>
MyFooter.vue组件
<template>
<!-- 底部 -->
<div class="my-footer">
<!-- 全选 -->
<div class="custom-control custom-checkbox">
<!-- v-model="isAll" -->
<input
v-model="isAll"
type="checkbox"
class="custom-control-input"
id="footerCheck"
/>
<label class="custom-control-label" for="footerCheck">全选</label>
</div>
<!-- 合计 -->
<div>
<span>合计:</span>
<span class="price">¥ {{ allSprice }}</span>
</div>
<!-- 按钮 -->
<button type="button" class="footer-btn btn btn-primary">
结算 ( {{ allNumber }} )
</button>
</div>
</template>
<script>
export default {
props: {
goodsObj: Array,
},
computed: {
isAll: {
set(val) {
this.$emit("changeAllEvent", val);
},
get() {
return this.goodsObj.every((obj) => obj.goods_state === true);
},
},
allSprice() {
return this.goodsObj.reduce((sum, obj) => {
// console.log(111);
if (obj.goods_state === true) {
sum += obj.goods_price * obj.goods_count;
}
return sum;
}, 0);
},
allNumber() {
return this.goodsObj.reduce((sum, obj) => {
// console.log(111);
if (obj.goods_state === true) {
sum += obj.goods_count;
}
return sum;
}, 0);
},
// isAll() {
// return this.goodsObj.every((obj) => obj.goods_state === true);
// },
},
};
</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>