初始化
This commit is contained in:
104
src/App.vue
104
src/App.vue
@@ -1,11 +1,107 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<h1>App 根组件</h1>
|
||||
</div>
|
||||
<div class="app-container">
|
||||
<!-- Header 头部区域 -->
|
||||
<Header title="购物车案例"></Header>
|
||||
<!-- 循环渲染每一个商品的信息 -->
|
||||
<Goods
|
||||
v-for="item in list"
|
||||
:id="item.id"
|
||||
:key="item.id"
|
||||
:count="item.goods_count"
|
||||
:pic="item.goods_img"
|
||||
:price="item.goods_price"
|
||||
:state="item.goods_state"
|
||||
:title="item.goods_name"
|
||||
@state-change="getNewState"
|
||||
></Goods>
|
||||
|
||||
<!-- Footer 区域 -->
|
||||
<Footer :all="total" :amount="amt" :isfull="fullState" @full-change="getFullState"></Footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {}
|
||||
// 导入 axios 请求库
|
||||
import axios from 'axios'
|
||||
// 导入需要的组件
|
||||
import Header from '@/components/Header/Header.vue'
|
||||
import Goods from '@/components/Goods/Goods.vue'
|
||||
import Footer from '@/components/Footer/Footer.vue'
|
||||
|
||||
import bus from '@/components/eventBus.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 用来存储购物车的列表数据,默认为空数组
|
||||
list: []
|
||||
}
|
||||
},
|
||||
// 计算属性
|
||||
computed: {
|
||||
// 动态计算出全选的状态是 true 还是 false
|
||||
fullState() {
|
||||
return this.list.every(item => item.goods_state)
|
||||
},
|
||||
// 已勾选商品的总价格
|
||||
amt() {
|
||||
// 1. 先 filter 过滤
|
||||
// 2. 再 reduce 累加
|
||||
return this.list
|
||||
.filter(item => item.goods_state)
|
||||
.reduce((total, item) => (total += item.goods_price * item.goods_count), 0)
|
||||
},
|
||||
// 已勾选商品的总数量
|
||||
total() {
|
||||
return this.list.filter(item => item.goods_state).reduce((t, item) => (t += item.goods_count), 0)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 调用请求数据的方法
|
||||
this.initCartList()
|
||||
|
||||
bus.$on('share', val => {
|
||||
this.list.some(item => {
|
||||
if (item.id === val.id) {
|
||||
item.goods_count = val.value
|
||||
return true
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
// 封装请求列表数据的方法
|
||||
async initCartList() {
|
||||
// 调用 axios 的 get 方法,请求列表数据
|
||||
const {data: res} = await axios.get('http://127.0.0.1:8080/goods')
|
||||
// 只要请求回来的数据,在页面渲染期间要用到,则必须转存到 data 中
|
||||
if (res.flag === true) {
|
||||
this.list = res.data
|
||||
console.log(this.list)
|
||||
}
|
||||
},
|
||||
// 接收子组件传递过来的数据
|
||||
// e 的格式为 { id, value }
|
||||
getNewState(e) {
|
||||
this.list.some(item => {
|
||||
if (item.id === e.id) {
|
||||
item.goods_state = e.value
|
||||
// 终止后续的循环
|
||||
return true
|
||||
}
|
||||
})
|
||||
},
|
||||
// 接收 Footer 子组件传递过来的全选按钮的状态
|
||||
getFullState(val) {
|
||||
this.list.forEach(item => (item.goods_state = val))
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Header,
|
||||
Goods,
|
||||
Footer
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
@@ -1,16 +1,50 @@
|
||||
<template>
|
||||
<div class="number-container d-flex justify-content-center align-items-center">
|
||||
<!-- 减 1 的按钮 -->
|
||||
<button type="button" class="btn btn-light btn-sm">-</button>
|
||||
<!-- 购买的数量 -->
|
||||
<span class="number-box">1</span>
|
||||
<!-- 加 1 的按钮 -->
|
||||
<button type="button" class="btn btn-light btn-sm">+</button>
|
||||
</div>
|
||||
<div class="number-container d-flex justify-content-center align-items-center">
|
||||
<!-- 减 1 的按钮 -->
|
||||
<button class="btn btn-light btn-sm" type="button" @click="sub">-</button>
|
||||
<!-- 购买的数量 -->
|
||||
<span class="number-box">{{ num }}</span>
|
||||
<!-- 加 1 的按钮 -->
|
||||
<button class="btn btn-light btn-sm" type="button" @click="add">+</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {}
|
||||
import bus from '@/components/eventBus.js'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
// 接收商品的 id 值,将来,使用 EventBus 方案,
|
||||
// 把数量传递到 App.vue 的时候,需要通知 App 组件,更新哪个商品的数量
|
||||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
// 接收到的 num 数量值
|
||||
num: {
|
||||
type: Number,
|
||||
default: 1
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 点击按钮,数值 +1
|
||||
add() {
|
||||
// 要发送给 App 的数据格式为 { id, value }
|
||||
// 其中,id 是商品的 id; value 是商品最新的购买数量
|
||||
const obj = {id: this.id, value: this.num + 1}
|
||||
// 要做的事情:通过 EventBus 把 obj 对象,发送给 App.vue 组件
|
||||
bus.$emit('share', obj)
|
||||
},
|
||||
sub() {
|
||||
if (this.num - 1 === 0) return
|
||||
// 要发送给 App 的数据格式为 { id, value }
|
||||
// 其中,id 是商品的 id; value 是商品最新的购买数量
|
||||
const obj = {id: this.id, value: this.num - 1}
|
||||
// 要做的事情:通过 EventBus 把 obj 对象,发送给 App.vue 组件
|
||||
bus.$emit('share', obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
@@ -1,24 +1,48 @@
|
||||
<template>
|
||||
<div class="footer-container">
|
||||
<!-- 左侧的全选 -->
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="cbFull" :checked="true" />
|
||||
<label class="custom-control-label" for="cbFull">全选</label>
|
||||
</div>
|
||||
<div class="footer-container">
|
||||
<!-- 左侧的全选 -->
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input id="cbFull" :checked="isfull" class="custom-control-input" type="checkbox" @change="fullChange"/>
|
||||
<label class="custom-control-label" for="cbFull">全选</label>
|
||||
</div>
|
||||
|
||||
<!-- 中间的合计 -->
|
||||
<div>
|
||||
<span>合计:</span>
|
||||
<span class="total-price">¥{{ 0 }}</span>
|
||||
</div>
|
||||
<!-- 中间的合计 -->
|
||||
<div>
|
||||
<span>合计:</span>
|
||||
<span class="total-price">¥{{ amount.toFixed(2) }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 结算按钮 -->
|
||||
<button type="button" class="btn btn-primary btn-settle">结算({{ 0 }})</button>
|
||||
</div>
|
||||
<!-- 结算按钮 -->
|
||||
<button class="btn btn-primary btn-settle" type="button">结算({{ all }})</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {}
|
||||
export default {
|
||||
props: {
|
||||
// 全选的状态
|
||||
isfull: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 总价格
|
||||
amount: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 已勾选的商品的总数量
|
||||
all: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 监听到了全选的状态变化
|
||||
fullChange(e) {
|
||||
this.$emit('full-change', e.target.checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
@@ -1,31 +1,82 @@
|
||||
<template>
|
||||
<div class="goods-container">
|
||||
<!-- 左侧图片 -->
|
||||
<div class="thumb">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<!-- 复选框 -->
|
||||
<input type="checkbox" class="custom-control-input" id="cb1" :checked="true" />
|
||||
<label class="custom-control-label" for="cb1">
|
||||
<!-- 商品的缩略图 -->
|
||||
<img src="../../assets/logo.png" alt="" />
|
||||
</label>
|
||||
</div>
|
||||
<div class="goods-container">
|
||||
<!-- 左侧图片 -->
|
||||
<div class="thumb">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<!-- 复选框 -->
|
||||
<input :id="'cb' + id" :checked="state" class="custom-control-input" type="checkbox"
|
||||
@change="stateChange"/>
|
||||
<label :for="'cb' + id" class="custom-control-label">
|
||||
<!-- 商品的缩略图 -->
|
||||
<img :src="pic" alt=""/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 右侧信息区域 -->
|
||||
<div class="goods-info">
|
||||
<!-- 商品标题 -->
|
||||
<h6 class="goods-title">{{ title }}</h6>
|
||||
<div class="goods-info-bottom">
|
||||
<!-- 商品价格 -->
|
||||
<span class="goods-price">¥{{ price }}</span>
|
||||
<!-- 商品的数量 -->
|
||||
<Counter :id="id" :num="count"></Counter>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 右侧信息区域 -->
|
||||
<div class="goods-info">
|
||||
<!-- 商品标题 -->
|
||||
<h6 class="goods-title">商品名称商品名称商品名称商品名称</h6>
|
||||
<div class="goods-info-bottom">
|
||||
<!-- 商品价格 -->
|
||||
<span class="goods-price">¥0</span>
|
||||
<!-- 商品的数量 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {}
|
||||
import Counter from '@/components/Counter/Counter.vue'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
// 商品的 id
|
||||
// 为啥在这里要封装一个 id 属性呢?
|
||||
// 原因:将来,子组件中商品的勾选状态变化之后, 需要通过子 -> 父的形式,
|
||||
// 通知父组件根据 id 修改对应商品的勾选状态。
|
||||
id: {
|
||||
required: true,
|
||||
type: Number
|
||||
},
|
||||
// 要渲染的商品的标题
|
||||
title: {
|
||||
default: '',
|
||||
type: String
|
||||
},
|
||||
// 要渲染的商品的图片
|
||||
pic: {
|
||||
default: '',
|
||||
type: String
|
||||
},
|
||||
// 商品的单价
|
||||
price: {
|
||||
default: 0,
|
||||
type: Number
|
||||
},
|
||||
// 商品的勾选状态
|
||||
state: {
|
||||
default: true,
|
||||
type: Boolean
|
||||
},
|
||||
// 商品的购买数量
|
||||
count: {
|
||||
type: Number,
|
||||
default: 1
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 只要复选框的选中状态发生了变化,就会调用这个处理函数
|
||||
stateChange(e) {
|
||||
const newState = e.target.checked
|
||||
// 触发自定义事件
|
||||
this.$emit('state-change', {id: this.id, value: newState})
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Counter
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@@ -33,15 +84,18 @@ export default {}
|
||||
+ .goods-container {
|
||||
border-top: 1px solid #efefef;
|
||||
}
|
||||
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
|
||||
.thumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin-right: 10px;
|
||||
margin: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,13 +104,16 @@ export default {}
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
flex: 1;
|
||||
|
||||
.goods-title {
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.goods-info-bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.goods-price {
|
||||
font-weight: bold;
|
||||
color: red;
|
||||
|
@@ -1,9 +1,17 @@
|
||||
<template>
|
||||
<div class="header-container">标题</div>
|
||||
<div class="header-container">{{ title }}</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {}
|
||||
export default {
|
||||
props: {
|
||||
// 声明 title 自定义属性,允许使用者自定义标题的内容
|
||||
title: {
|
||||
default: '',
|
||||
type: String
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
3
src/components/eventBus.js
Normal file
3
src/components/eventBus.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import Vue from 'vue'
|
||||
|
||||
export default new Vue()
|
@@ -6,5 +6,5 @@ import 'bootstrap/dist/css/bootstrap.min.css'
|
||||
Vue.config.productionTip = false
|
||||
|
||||
new Vue({
|
||||
render: h => h(App)
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
|
Reference in New Issue
Block a user