您当前的位置: 首页 > 学无止境 > JS经典实例 网站首页JS经典实例
vue项目第五天购物车、父子传值-02
发布时间:2020-05-15 20:31:01编辑:雪饮阅读()
商品详情页的评论功能
因为之前在图集详情页做过图片评论组件的封装,所以这里只需要调用下即可,这里采用的是跳转一个新页面来调用并且为了区分商品评论和图片评论(我这里后端要用不同的模型来获取所属分类)所以稍有改动。
那么商品详情页商品评论处调用一个商品评论方法
<ul>
<li>
<mt-button type="primary" size="large" plain @click="goGoodsInfo">图文介绍</mt-button>
</li>
<li>
<mt-button type="danger" size="large" @click="goGoodsComment" plain>商品评论</mt-button>
</li>
</ul>
则该详情页中就必须有这个商品评论方法
methods: {
goGoodsInfo() {
// 直接进入到新闻详情
this.$router.push({
name:'NewsDetail',
params:{
id:this.$route.query.id
}
});
},
goGoodsComment() {
this.$router.push({
name:'GoodsComment',
query:{
id:this.$route.query.id
}
});
}
},
这个方法又进入了一个新的组件,所以同时也要建立这个新的组件,当然这个组件就很简单的版本了。
GoodsComment.vue
<template>
<div>
<nav-bar title="商品评论"/>
<comment :cid="$route.query.id" :type="2"/>
</div>
</template>
<script>
</script>
<style>
</style>
注意到这里传递了type=2是为了区分商品和图集
则在原来的评论组件处也就有所改变
<script>
export default {
name: 'comment',
props:['cid','type'],
data(){
return {
msgs:[],
total:0,
content:"",
page:1
}
},
created () {
this.commentList(1);
},
methods:{
sendMsg(){
this.$axios.post(`/api/postcomment/${this.cid}`,`content=${this.content}&type=${this.type}`)
.then(res=>{
//刷新评论列表
this.commentList(1,1,'refresh')
})
.catch(res=>{
console.log(res);
});
},
如此以来商品评论功能就完成了。
加入购物车
首先商品详情页中加入购物车按钮要调用一个方法来实现加入购物车功能
<li>
<mt-button type="primary">立即购买</mt-button>
<mt-button type="danger" @click="addShopcart">加入购物车</mt-button>
</li>
那么在methods中则有
addShopcart() {
this.isShow = true;
},
这里为什么不是具体的加入购物车方法,而只是设置了某个值?
因为这里加入购物车是有那个抛物线效果的,这里为了触发那个抛物线效果。
抛物线效果原理:
其实抛物线效果实现方法每个人有每个人的想法,这里用vue的transition组件结合translate3d实现来说明下。
那么首页界面中有一个抛物线上面的那个点
<li>
<mt-button type="primary">立即购买</mt-button>
<mt-button type="danger" @click="addShopcart">加入购物车</mt-button>
</li>
</ul>
</div>
<!-- 过度效果内置组件 -->
<transition name="ball" @after-enter="afterEnter">
<div class="ball" v-if="isShow"></div>
</transition>
这里after-enter指定了当该组件内部元素效果完成变化时所一并触发的事件(钩子),transition组件可以按不同的时机来区分不同的钩子而指定不同的事件。
既然用到了指定的事件,则methods中定义该事件如
afterEnter() {
this.isShow = false;
// 加入数据
MyBus.$emit('addShopcart',this.buyNum);
},
这里又用到了MyBus,则有MyBus.js
import Vue from 'vue';
export default new Vue();
当前商品详情页也就必须导入mybus
<script>
import MyBus from '@/MyBus';
这里借用mybus是通知底部tabbar中的购物车数量增加,则对应app.vue中有如
<mt-tab-item id="ShopCart">
<img slot="icon" src="../static/7.jpg">
购物车<mt-badge size="small" type="error">{{totalNum}}</mt-badge>
</mt-tab-item>
<mt-tab-item id="Search">
<img slot="icon" src="../static/7.jpg">
查找
</mt-tab-item>
</mt-tabbar>
</div>
</template>
<script>
import MyBus from './MyBus';
export default {
name: 'App',
data (){
return {
selected:'',
imgs:[{img:'../static/7.jpg'},{img:'../static/7.jpg'},{img:'../static/7.jpg'}],
totalNum:0
}
},
created() {
MyBus.$on('addShopcart',(num)=>{
this.totalNum += num;
});
MyBus.$on('shopcartNum',(num)=>{
this.totalNum = num;
});
},
App.vue中mt-badge也是mint-ui中的组件,用于实现类似购物车旁边的数量的那种小徽章图标。
至此,加入购物车的JavaScript任务都完成了。接下来就是抛物线过渡的css任务了。
有ccs如
<style scoped>
.ball {
border-radius: 50%;
width: 24px;
height: 24px;
position: absolute;
top: 440px;
left: 120px;
display: inline-block;
z-index: 9999;
background-color: red;
}
/*元素被移除前默认有一个透明度1的显示的*/
.ball-leave {
opacity: 0;
}
.ball-enter-active {
animation: bounce-in 1s;
}
@keyframes bounce-in {
0% {
transform: translate3d(0, 0, 0);
}
50% {
transform: translate3d(140px, -50px, 0);
}
75% {
transform: translate3d(160px, 0px, 0);
}
100% {
transform: translate3d(140px, 300px, 0);
}
}
这里第一组标红(left和top)就是抛物线上面那个点的初始位置的x坐标,后面是每个阶段时候的x坐标。所以这里阶段数量越多,则颗粒度越细,则抛物线模拟的越真实,所以这里的坐标要根据实际情况做下不同视窗大小的兼容性。
Object.keys与join
Object.keys可以将一个数组对象中的所有index下标获取并生成一个index下标形成的新数组。
Join函数可实现将一个数组中所有成员按指定字符内爆分割为一个字符串,类似与php中的implode函数。
如
这里undefined属于正常现象,因为控制台每次执行一个语句后都会打印下该执行的值,而这里只是定义一个变量所有并没有具体的return所以就会报undefined
购物车列表
Shopcart /ShopCart.vue
<template>
<div class="tmpl">
<nav-bar title="购物车"/>
<div class="pay-detail">
<ul>
<li class="p-list" v-for="(goods,index) in shopcart" :key="goods.id">
<mt-switch v-model="goods.isSelected"></mt-switch>
<img :src="goods.thumb_path">
<div class="pay-calc">
<p>{{goods.title}}</p>
<div class="calc">
<span>¥{{goods.sell_price}}</span>
<span @click="substract(goods)">-</span>
<span>{{goods.buyNum}}</span>
<span @click="add(goods,index)">+</span>
<a href="javascript:;" @click="del(goods)">删除</a>
</div>
</div>
</li>
</ul>
</div>
<div class="show-price" v-if="shopcart.length>0">
<div class="show-1">
<p>总计(不含运费):</p>
<span>已经选择商品{{payment.count}}件,总价¥{{payment.total}}元</span>
</div>
<div class="show-2">
<mt-button type="danger" size="large">去结算</mt-button>
</div>
</div>
</div>
</template>
<script>
import MyBus from '@/MyBus';
export default {
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
if(this.act===false) next();
else{
let result = confirm('确定要离开吗?');
if(result) {
// 保存用户的编辑数据
// 计算总数
let total = Object.values(test.goodsObj).reduce((prev, cur)=> prev + cur );
MyBus.$emit('shopcartNum',total);
next();
} else {
next(false);// 取消导航
}
}
},
data() {
return {
shopcart:[],
act:false
}
},
methods: {
del(goods,i) {
// 删除数组元素
this.shopcart.splice(i,1);
// 删除test.goodsObj的数据
delete test.goodsObj[goods.id];
this.act=true;
},
substract(goods){
goods.buyNum --;
test.goodsObj[goods.id]--;
this.act=true;
},
add(goods){
console.log('加了');
goods.buyNum ++;
test.goodsObj[goods.id]++;
this.act=true;
}
},
computed:{
payment() {
// 件数/金额
let count = 0;
let total = 0;
this.shopcart.forEach( goods => {
// 如果 勾选
if(goods.isSelected) {
count += goods.buyNum;
total += goods.buyNum * goods.sell_price;
}
});
return {
total,count
}
}
},
async created() {
// 生成URL
var ids = Object.keys(test.goodsObj).join(',');
if(ids=="") return false;
let url = '/api/goods/getshopcarlist/'+ids;
// Object.defineProperty(this,'shopcart',{
// set:function() { // 获知视图要更新
// 判断shopcart元素 是否还有属性,那这些对象的属性都要这么干
// }
// })
// 会将shopcart中对象的所有属性进行监视,完成属性的响应式(属性改,页面改)
this.shopcart = (await this.$axios.get(url)).data.message.data;
// 给数组的元素添加属性(使用await的时候使用ES6中的map/each/find/findIndex是没有效果)
for (let i = 0; i< this.shopcart.length ; i ++) {
let goods = this.shopcart[i];
let num = test.goodsObj[goods.id];
if(num) {
// 手动完成响应式
this.$set(goods,'buyNum',num);
this.$set(goods,'isSelected',true);
// goods.buyNum = num;
// goods.isSelected = true;
}
}
}
}
</script>
<style scoped>
.pay-detail ul li {
list-style: none;
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
margin-bottom: 3px;
}
.pay-detail ul {
padding-left: 5px;
margin-top: 5px;
}
.pay-detail ul li img {
width: 80px;
height: 80px;
display: inline-block;
vertical-align: top;
margin-top: 10px;
}
.pay-detail ul li >:nth-child(1),
.pay-detail ul li >:nth-child(3) {
display: inline-block;
}
.pay-calc p {
display: inline-block;
width: 250px;
overflow: hidden;
color: blue;
font-size: 15px;
margin-bottom: 10px;
}
.pay-detail ul li >:nth-child(1) {
line-height: 80px;
}
.calc:nth-child(1) {
color: red;
font-size: 20px;
}
.calc span:not(:nth-child(1)) {
border: 1px solid rgba(0, 0, 0, 0.3);
display: inline-block;
width: 20px;
text-align: center;
}
.calc a {
margin-left: 20px;
}
.show-1,
.show-2 {
display: inline-block;
}
.show-1,
.show-2 {
margin-left: 30px;
}
.show-price {
background-color: rgba(0, 0, 0, 0.2);
}
</style>
<style>
.tmpl{
margin-top:80px;
}
</style>
mt-switch:mint-ui的切换组件,类似状态开关之类。
beforeRouteLeave:vue中组件的路由生命周期-离开当前组件时
reduce:上面用到的reduce可以展开如:
arr.reduce(function(prev, cur){
return prev + cur
});
这里的prev是遍历过程中上次累积值,cur是本次遍历到的值。
$set:手动完成响应,如果只是修改对象内部某个属性,则ui是不更新的,这里$set是为了触发更新ui。
关键字词:transition,translate3d,after-enter,mt-badge,Object.keys,join,mt-switch,beforeRou
上一篇:vue项目第五天