需求点:
一句话概括 —— 为营造活动氛围,在主要页面添加订单成功提示,详情参考拼多多;
细分需求 ——
1. 在首页,商情页,购物车,搜索页和列表页等只要页面添加气氛提醒;
2. 请求接口拿到对应当前时段的用户下单数据,根据接口记录下次接口的请求参数;
3. 数据在每个页面均连续展示,一个接口最多五十条数据;
4. 页面滞留时间超出全部数据弹出完毕也不发送接口,到其他页面之后(或者刷新页面)发送请求;
5. 添加气泡出现动画.
思路细分 ——
1.气泡的展示是连续且多页面的,所以制作成组件;
2.接口数据返回的是多条,但是每一条数据是独立存在的;
3.需要记录接口返回的数据,当前进行到第几条;
4.动画需求.
代码展示
<template>
<div class="bubbleTips">
<!-- <transition name="fade" mode="out-in"> -->
<div :class="['order_info','item'+item.payTime+Math.floor(Math.random()*100)]" v-for="item in currentArr" :key="item.payTime+Math.floor(Math.random()*100)">
<img class="user_img" :src="item.headimgurl!='' ? item.headimgurl:'https://oss-image.dfs168.com/market/315/normal.png'"
alt="" />
<p v-if="item.text!=''" class="copy_writing">{{item.text}}</p>
<p v-if="item.text==''" class="nickname">{{item.nickname==""?'****':item.nickname}}</p>
<p class="copy_writing" v-if="item.text==''&&item.goods_name==''">{{item.payDate}}订单支付成功</p>
<p class="copy_writing" v-if="item.text==''&&item.goods_name!=''">{{item.payDate}}购买了{{item.goods_name|cutOutStr}}</p>
</div>
<!-- </transition> -->
</div>
</template>
<script>
import Api from "@/api/server";
export default {
props: {
discharge: {
type: Boolean,
default: false
}
},
data() {
return {
index: 0,
arr: JSON.parse(localStorage.getItem("bubbleList")) || [],
currentArr: [],
timer: null
};
},
methods: {
getBubbleList(time) {
var params = {
'payings[time]': time
}
Api.common.getBubbleList(params).then(res => {
if (res.state == 1) {
localStorage.setItem("payingsTime", res.data.payings.time);
if (res.data.payings.list) {
this.arr = res.data.payings.list;
} else {
this.arr = [];
}
localStorage.setItem("bubbleList", JSON.stringify(this.arr));
}
})
},
goFadeBubble() {
var _this = this;
this.index = localStorage.getItem("currentInex") || 0;
this.timer = setInterval(function() {
_this.currentArr = _this.arr.slice(
_this.index,
_this.index * 1 + 1 * 1
);
localStorage.setItem("currentInex", _this.index);
_this.index++;
}, 4000);
}
},
created() {
var index = localStorage.getItem("currentInex"),
payingsTime = localStorage.getItem("payingsTime"),
bubbleList = JSON.parse(localStorage.getItem("bubbleList")) || [];
if (!payingsTime && bubbleList.length == 0) {
this.getBubbleList(0);
localStorage.setItem("currentInex", 0);
} else if (payingsTime && bubbleList.length - index <= 0) {
this.getBubbleList(localStorage.getItem("payingsTime"));
localStorage.setItem("currentInex", 0);
}
this.goFadeBubble();
},
mounted() {
this.$emit("getTimer", this.timer);
},
watch: {
index() {
if (this.index - this.arr.length > 0) {
clearInterval(this.timer);
this.timer = null;
return false;
}
},
discharge() {
if (this.$props.discharge) {
localStorage.setItem("currentInex", this.index);
clearInterval(this.timer);
this.timer = null;
return false;
} else {
var currentInex = localStorage.getItem("currentInex") || this.arr.length;
if (currentInex - this.arr.length < 0) {
this.goFadeBubble();
}
}
}
},
filters:{
cutOutStr(value){
if(value.length>6){
return value.substring(0, 6);
}else{
return value;
}
}
}
};
</script>
<style scoped="scoped">
.bubbleTips .order_info {
position: absolute;
top: 0;
left: 0;
padding: 0.04rem 0.32rem 0.04rem 0.04rem;
box-sizing: border-box;
display: flex;
align-items: center;
border-radius: 0.32rem;
background: rgba(0, 0, 0, .7);
box-shadow: 0px 6px 24px 0px rgba(0, 0, 0, 0.32);
animation: fade_in 2s ease;
}
.bubbleTips .order_info .user_img {
width: 0.56rem;
height: 0.56rem;
border-radius: 50%;
margin-right: .13rem;
}
.bubbleTips .order_info p {
line-height: .3rem;
font-size: 0.24rem;
font-weight: bold;
color: rgba(255, 255, 255, 1);
white-space: nowrap;
}
.bubbleTips .order_info .nickname {
max-width: 1.22rem;
overflow: hidden;
text-overflow: ellipsis;
margin-right: .07rem;
}
@keyframes fade_in {
0% {
opacity: 0;
}
80% {
opacity: 0;
}
100% {
opacity: 1;
}
}
</style>
调用代码
<!-- 导入 -->
<bubbleTips id="Bubble" v-if="!hasBubble" @getTimer="closeInterval" />
<script>
<!-- 页面保持状态(使用keep-alive) -->
import bubbleTips from '../../components/bubbleTips';
export default {
data () {
return {
hasBubble: false
}
},
methods:{
closeInterval(timer) {
this.timer = timer;
}
},
activated: function(){
this.hasBubble = false;
},
deactivated() {
this.hasBubble = true;
clearInterval(this.timer);
}
}
</script>
<bubbleTips id="Bubble" @getTimer="closeInterval" />
<script>
<!-- 未使用keep-alive -->
import bubbleTips from '../../components/bubbleTips';
export default {
data () {
return {
}
},
methods:{
closeInterval(timer) {
this.timer = timer;
}
},
beforeDestroy(){
clearInterval(this.timer);
},
}
</script>
要点介绍
1.由于商场的特殊性,某些页面(例如首页)需要保持状态,使用到keep-alive,有些页面又没有使用到,所以在处理需求中的连续时,及在处理需求时,对特殊页面要进行特殊处理,这里需要强调的是,生命钩子的秩序顺序在第一次进入页面时舒服顺序和一般页面一样,之后再进入时触发activated,离开时触发deactivated(注:keep-alive的页面,组件离开时需要卸载).
2.定时器是全局存在的,在清除数据的同时也要清除定时器,这里使用了子传父自定义事件的方式,把定时器的id传递给调用组件的父组件,在离开页面时清除定时器.
3.v-for和v-if的冲突性,在使用时一般是算好满足条件的数据进行for循环.
4.动画效果是每一个独立的信息在进入时有淡入淡出动画,vue在for循环时需要标注key值(特定且唯一),使生成的每一条数据与其他数据不用,这样在下一个数据出现时,内核处理为认为是两个不同且独立的数据,重新进行进入动画.