Authored by 邱骏

限时抢券

import limitTimeCoupon from './limit-time-coupon';
export default [
...limitTimeCoupon
];
... ...
// 限时抢券活动
export default [{
path: '/xianyu/activity/limitTimeCoupon/:code?',
name: 'LimitTimeCoupon',
component: () => import(/* webpackChunkName: "priceChange" */ './limit-time-coupon')
}];
... ...
<template>
<LayoutApp :title="title" >
<div class="activity-wrapper">
<div class="activity-container">
<ul id="page_ul">
<li>
<img :src="pageData.title">
</li>
<li>
<div class="tab-container">
<div v-for="(item, index) in pageData.couponData" :class="['tab-item', index === nowActiveIndex ? 'active' : '']" :data-index="index" @click="changeTab">
<p class="time-text">{{item.time + ':00'}}</p>
<p class="status-text">{{item.statusText}}</p>
</div>
</div>
</li>
<li>
<div class="coupon-container">
<div class="coupon-item" v-for="(item, index) in pageData.couponData" v-if="index === nowActiveIndex">
</div>
</div>
</li>
</ul>
</div>
</div>
</LayoutApp>
</template>
<script>
import LayoutApp from '../../../components/layout/layout-app';
import {createNamespacedHelpers} from 'vuex';
const {mapState, mapActions, mapMutations} = createNamespacedHelpers('activitys/limitTimeCoupon');
const STATUS_MAP = {
0: '已抢完',
1: '即将开始',
2: '立即抢券',
3: '明日开启'
};
export default {
name: 'limitTimeCoupon',
components: {LayoutApp},
asyncData({store, router}) {
},
activated() {
this.openTime = Date.now();
this.getServerTime().then(result => {
this.serverTime = result.data;
console.log('serverTime', result, (new Date(result.data)).toLocaleString());
this.initCouponData();
});
},
computed: {
// ...mapState(['serverTime'])
},
data() {
return {
title: 'UFO限时抢券',
serverTime: 0, // 服务器时间,后面会累加计时器时间
pageData: {
title: '//ad.yoho.cn/html5/2019/10/activity/004/base/title.jpg?imageslim',
repeat: '//ad.yoho.cn/html5/2019/10/activity/004/base/repeat_bg.jpg?imageslim',
couponBg: '//ad.yoho.cn/html5/2019/10/activity/004/base/coupon_bg.png?imageslim',
buttonEnd: '//ad.yoho.cn/html5/2019/10/activity/004/base/btn_end.png?imageslim', // 已抢完按钮
buttonGet: '//ad.yoho.cn/html5/2019/10/activity/004/base/btn_get.png?imageslim', // 立即抢券按钮
buttonReady: '//ad.yoho.cn/html5/2019/10/activity/004/base/btn_ready.png?imageslim', // 即将开始按钮
couponBaseData: {
'2019-10-28': [
{
time: 10,
tokens: [
{
image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/1.png',
token: '22cae1c5-8e8c-4f8a-9502-c59fa752974a'
},
{
image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/2.png',
token: '66ef584f-6925-466e-ac04-126993c316a1'
},
{
image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/3.png',
token: 'f7be232b-d548-4927-bc53-db0d10d9f5f1'
},
{
image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/4.png',
token: 'e30b346f-ec92-453c-8b4e-08f5da225eb4'
},
{
image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/5.png',
token: 'f3a0b83e-b2d5-450c-9e1d-c68aa7e1fd4e'
}
]
},
{
time: 16,
tokens: [
{
image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/1.png',
token: '91c38ce0-bd9e-4d11-9bf0-f53c633b60c3'
},
{
image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/2.png',
token: '6f0d6158-061f-44f7-aed8-789c12eb9dc8'
},
{
image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/3.png',
token: 'c002aeb5-a398-4113-bbe0-a60369d9242e'
},
{
image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/4.png',
token: 'f5dd41ca-2a4b-4374-9bc3-c2777da82a8b'
},
{
image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/5.png',
token: 'e8bc629c-3bda-4778-a863-ae70bc7a5ce7'
}
]
},
{
time: 22,
tokens: [
{
image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/1.png',
token: '8eb43f41-b35f-4469-b7d2-b076e6315a71'
},
{
image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/2.png',
token: 'f066e81a-6d27-493f-8a3a-9276bd620718'
},
{
image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/3.png',
token: '0234b183-834e-4c53-ad42-b1fda17eb122'
},
{
image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/4.png',
token: '9957ec2f-dec4-46d3-8ae8-55c82d375443'
},
{
image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/5.png',
token: '1eb7ef64-fb07-4564-b61c-53d4e473292a'
}
]
}
],
'2019-10-29': [
{
time: 10,
tokens: []
},
{
time: 16,
tokens: []
},
{
time: 22,
tokens: []
}
],
'2019-10-30': [
{
time: 10,
tokens: []
},
{
time: 16,
tokens: []
},
{
time: 22,
tokens: []
}
]
},
couponData: [],
},
nowActiveIndex: 0,
nextTime: 0, // 下一次抢券时间点
intervalTime: 10000, // 计时器时间
timeout: null, // 定时器
openTime: 0, // 用户打开页面的时间
};
},
beforeRouteLeave(from, to, next) {
console.log('routeLeave');
this.clearData();
next();
},
methods: {
...mapActions(['getServerTime', 'getCoupon']),
getDateString(time) { // 获取日期的字符串,如'2019-10-26'
let date = new Date(time);
let month = date.getMonth() + 1 > 9 ? (date.getMonth() + 1) : '0' + (date.getMonth() + 1);
let day = date.getDate() > 9 ? date.getDate() : '0' + date.getDate();
return {
str: [date.getFullYear(), month, day].join('-'),
safariStr: [date.getFullYear(), month, day].join('/'), // safari中只能使用这种格式
dateNum: time
};
},
clearData() {
clearTimeout(this.timeout);
this.timeout = null;
this.serverTime = 0;
this.nowActiveIndex = 0;
this.nextTime = 0;
this.intervalTime = 10000;
this.openTime = 0;
this.pageData.couponData = [];
},
getActiveIndex(time) { // 计算当前应该生效的tab
console.log('time=', time, new Date(time));
if (this.pageData.couponData.length > 0) {
for (let i = 0; i < this.pageData.couponData.length; i ++) {
let obj = this.pageData.couponData[i];
if ((time >= obj.date_time && time < obj.date_time + 3600000) || (time > obj.date_time - 3600000 && time < obj.date_time)) {
this.nowActiveIndex = i;
this.nextTime = obj.date_time;
break;
} else if (time < obj.date_time) {
this.nowActiveIndex = i - 1 > 0 ? (i - 1) : 0;
this.nextTime = obj.date_time;
break;
} else if (time > obj.date_time + 3600000) {
this.nowActiveIndex = i;
this.nextTime = obj.date_time;
}
}
if (this.nextTime) {
this.checkCouponTime();
}
}
},
initCouponData() { // 初始化今日需要使用到的券的信息
if (this.serverTime) {
this.pageData.couponData = [];
let today = this.getDateString(this.serverTime);
let tomorrow = this.getDateString(this.serverTime + 86400000);
// 此处把今天日期对应的券信息取出放入couponData
if (this.pageData.couponBaseData[today.str] && this.pageData.couponBaseData[today.str].length > 0) {
this.pageData.couponBaseData[today.str].forEach((obj) => {
obj.date = today.str;
// 计算领券开始时间,用于比较,这边用safariStr取时间,防止浏览器兼容问题
obj.date_time = new Date(today.safariStr + ' ' + obj.time + ':00:00').getTime();
obj.is_today = true;
if (today.dateNum < obj.date_time) {
obj.status = 1; // 即将开始
} else if (today.dateNum > obj.date_time && today.dateNum < obj.date_time + 3600000) {
obj.status = 2; // 进行中
} else if (today.dateNum > obj.date_time + 3600000) {
obj.status = 0; // 已抢完
}
obj.statusText = STATUS_MAP[obj.status];
this.pageData.couponData.push(obj);
});
}
// 把明日对应的首条券信息放入couponData
if (this.pageData.couponBaseData[tomorrow.str] && this.pageData.couponBaseData[tomorrow.str].length > 0) {
let obj = this.pageData.couponBaseData[tomorrow.str][0];
obj.date = tomorrow.str;
obj.date_time = new Date(tomorrow.safariStr + ' ' + obj.time + ':00:00').getTime();
obj.is_today = false;
obj.status = 3;
obj.statusText = STATUS_MAP[obj.status];
this.pageData.couponData.push(obj);
}
this.getActiveIndex(this.serverTime); // 获取生效的tab页
console.log(this.pageData.couponData);
}
},
checkCouponTime() { // 定时器用于判断是否到下一次抢券的时间
let interval = this.intervalTime;
let serverDate = new Date(this.serverTime);
if (serverDate.getDate() !== new Date(this.openTime).getDate()) {
this.openTime = Date.now();
console.log(serverDate.getDate(), new Date(this.openTime).getDate());
this.$router.replace({
name: 'LimitTimeCoupon',
params: {
code: Math.random().toString(16).substr(2,8)
}
});
return;
}
if (this.nextTime - this.serverTime < 10000 && this.serverTime < this.nextTime) {
interval = 1000; // 当下一次开始时间和当前时间比较小于10秒时,则1秒判断一次
}
this.serverTime += interval;
console.log('timeout=', new Date(this.serverTime));
if (this.serverTime >= this.nextTime && this.serverTime < this.nextTime + 1000) { // 在抢券时间内则初始化券信息
this.initCouponData();
} else if (this.serverTime - this.nextTime > 3600000 + interval) {
this.initCouponData();
} else {
this.timeout = setTimeout(this.getActiveIndex.bind(this), interval, this.serverTime);
}
},
changeTab(e) {
let index = e.currentTarget.dataset.index;
this.nowActiveIndex = parseInt(index, 10);
clearTimeout(this.timeout);
}
}
};
</script>
<style lang="scss" scoped>
.activity-wrapper {
position: relative;
width: 100%;
height: 100%;
overflow-x: hidden;
overflow-y: scroll;
background-color: #444;
}
.activity-container {
width: 100%;
height: 3000px;
background-image: url("//ad.yoho.cn/html5/2019/10/activity/004/base/repeat_bg.jpg?imageslim");
background-repeat: repeat-y;
background-size: 100% auto;
overflow: hidden;
#page_ul {
width: 100%;
margin: 0;
padding: 0;
list-style: none;
overflow: hidden;
li {
position: relative;
width: 100%;
overflow: hidden;
img {
width: 100%;
float: left;
}
}
.tab-container {
width: 100%;
height: 112px;
display: flex;
flex-direction: row;
.tab-item {
display: inline-block;
width: 100%;
height: 100%;
box-sizing: border-box;
background-color: #9ef43a;
&.active {
background-color: #fff;
}
p {
font-family: "Helvetica Neue", "Source Han Sans CN", "PingFang SC", Helvetica, sans-serif;
display: inline-block;
text-align: center;
width: 100%;
line-height: 40px;
font-weight: 600;
}
.time-text {
font-size: 40px;
margin-top: 20px;
}
.status-text {
font-size: 24px;
line-height: 32px;
}
}
}
.coupon-container {
position: relative;
background-image: url("//ad.yoho.cn/html5/2019/10/activity/004/base/coupon_bg.png?imageslim");
background-size: 100% auto;
background-repeat: repeat-y;
overflow: hidden;
margin-top: 40px;
.coupon-item {
position: relative;
margin: 0 auto 20px auto;
width: 530px;
height: 127px;
background-size: 100% 100%;
}
}
}
}
</style>
... ...
... ... @@ -7,6 +7,7 @@ import Passport from './passport';
import Address from './address';
import Notice from './notice';
import Category from './category';
import Activitys from './activitys';
export default [
{
... ... @@ -22,4 +23,5 @@ export default [
...Address,
...Notice,
...Category,
...Activitys
];
... ...
import limitTimeCoupon from './limit-time-coupon';
export default function() {
return {
namespaced: true,
modules: {
limitTimeCoupon: limitTimeCoupon()
}
};
}
... ...
export const Types = {
GET_COUPON_REQUEST: 'GET_COUPON_REQUEST',
GET_COUPON_SUCCESS: 'GET_COUPON_SUCCESS',
GET_COUPON_FAILED: 'GET_COUPON_FAILED',
GET_SERVER_TIME_SUCCESS: 'GET_SERVER_TIME_SUCCESS',
};
export default function() {
return {
namespaced: true,
state: {
fetchingCoupon: false,
serverTime: ''
},
mutations: {
[Types.GET_COUPON_REQUEST](state) {
state.fetchingCoupon = true;
},
[Types.GET_COUPON_SUCCESS](state) {
state.fetchingCoupon = false;
},
[Types.GET_COUPON_FAILED](state) {
state.fetchingCoupon = false;
},
[Types.GET_SERVER_TIME_SUCCESS](state, {time}) {
state.serverTime = time;
console.log('mutationServerTime', time);
}
},
actions: {
async getCoupon({commit}, {couponToken}) {
commit(Types.GET_COUPON_REQUEST);
const result = await this.$api.get('/api/ufo/coupons/send', {coupon_tokens: couponToken});
if (result && result.code === 200) {
commit(Types.GET_COUPON_SUCCESS);
} else {
commit(Types.GET_COUPON_FAILED);
}
return result || {};
},
async getServerTime({commit}) {
const result =await this.$api.get('/app/ufo/getServerTime');
return result;
}
}
};
};
... ...
... ... @@ -12,6 +12,7 @@ import storeHome from './home';
import storeAddress from './address';
import storeNotice from './notice';
import storeCategory from './category';
import storeActivitys from './activitys';
Vue.use(Vuex);
... ... @@ -32,6 +33,7 @@ export function createStore(context) {
category: storeCategory(),
// buyerOderList: buyerOderList(),
activitys: storeActivitys(),
},
strict: process.env.NODE_ENV !== 'production',
});
... ...
module.exports = {
// 有货领券
'api/coupons/send': {
auth: true,
accessLog: true,
checkSign: true,
ufo: false,
api: 'app.coupons.couponSend',
params: {
coupon_send_token: {type: String}
},
},
// ufo领券
'api/ufo/coupons/send': {
auth: true,
accessLog: true,
checkSign: true,
ufo: true,
api: 'ufo.coupons.send',
params: {
coupon_tokens: {type: String}
},
},
};
... ...
... ... @@ -9,6 +9,7 @@ const orderListApi = require('./order-api-map');
const categoryApi = require('./category-api-map');
const sellerAskApi = require('./sellerask-api-map');
const systemApi = require('./system-api-map');
const activitysApi = require('./activitys-api-map');
module.exports = {
...orderApi,
... ... @@ -22,4 +23,5 @@ module.exports = {
...categoryApi,
...sellerAskApi,
...systemApi,
...activitysApi
};
... ...
... ... @@ -55,6 +55,14 @@ exports.createApp = async(app) => {
next();
});
// 获取服务器时间
app.get('/xianyu/app/ufo/getServerTime', function(req, res) {
return res.json({
code: 200,
data: Date.now()
});
});
// 添加请求上下文
app.use(global.yoho.httpCtx());
... ...