Authored by 邱骏

限时抢券

  1 +import limitTimeCoupon from './limit-time-coupon';
  2 +
  3 +export default [
  4 + ...limitTimeCoupon
  5 +];
  1 +// 限时抢券活动
  2 +
  3 +export default [{
  4 + path: '/xianyu/activity/limitTimeCoupon/:code?',
  5 + name: 'LimitTimeCoupon',
  6 + component: () => import(/* webpackChunkName: "priceChange" */ './limit-time-coupon')
  7 +}];
  1 +<template>
  2 + <LayoutApp :title="title" >
  3 + <div class="activity-wrapper">
  4 + <div class="activity-container">
  5 + <ul id="page_ul">
  6 + <li>
  7 + <img :src="pageData.title">
  8 + </li>
  9 + <li>
  10 + <div class="tab-container">
  11 + <div v-for="(item, index) in pageData.couponData" :class="['tab-item', index === nowActiveIndex ? 'active' : '']" :data-index="index" @click="changeTab">
  12 + <p class="time-text">{{item.time + ':00'}}</p>
  13 + <p class="status-text">{{item.statusText}}</p>
  14 + </div>
  15 + </div>
  16 + </li>
  17 + <li>
  18 + <div class="coupon-container">
  19 + <div class="coupon-item" v-for="(item, index) in pageData.couponData" v-if="index === nowActiveIndex">
  20 +
  21 + </div>
  22 + </div>
  23 + </li>
  24 + </ul>
  25 + </div>
  26 + </div>
  27 + </LayoutApp>
  28 +</template>
  29 +
  30 +<script>
  31 +import LayoutApp from '../../../components/layout/layout-app';
  32 +import {createNamespacedHelpers} from 'vuex';
  33 +
  34 +const {mapState, mapActions, mapMutations} = createNamespacedHelpers('activitys/limitTimeCoupon');
  35 +const STATUS_MAP = {
  36 + 0: '已抢完',
  37 + 1: '即将开始',
  38 + 2: '立即抢券',
  39 + 3: '明日开启'
  40 +};
  41 +
  42 +export default {
  43 + name: 'limitTimeCoupon',
  44 + components: {LayoutApp},
  45 + asyncData({store, router}) {
  46 + },
  47 + activated() {
  48 + this.openTime = Date.now();
  49 + this.getServerTime().then(result => {
  50 + this.serverTime = result.data;
  51 + console.log('serverTime', result, (new Date(result.data)).toLocaleString());
  52 + this.initCouponData();
  53 + });
  54 + },
  55 + computed: {
  56 + // ...mapState(['serverTime'])
  57 + },
  58 + data() {
  59 + return {
  60 + title: 'UFO限时抢券',
  61 + serverTime: 0, // 服务器时间,后面会累加计时器时间
  62 + pageData: {
  63 + title: '//ad.yoho.cn/html5/2019/10/activity/004/base/title.jpg?imageslim',
  64 + repeat: '//ad.yoho.cn/html5/2019/10/activity/004/base/repeat_bg.jpg?imageslim',
  65 + couponBg: '//ad.yoho.cn/html5/2019/10/activity/004/base/coupon_bg.png?imageslim',
  66 + buttonEnd: '//ad.yoho.cn/html5/2019/10/activity/004/base/btn_end.png?imageslim', // 已抢完按钮
  67 + buttonGet: '//ad.yoho.cn/html5/2019/10/activity/004/base/btn_get.png?imageslim', // 立即抢券按钮
  68 + buttonReady: '//ad.yoho.cn/html5/2019/10/activity/004/base/btn_ready.png?imageslim', // 即将开始按钮
  69 + couponBaseData: {
  70 + '2019-10-28': [
  71 + {
  72 + time: 10,
  73 + tokens: [
  74 + {
  75 + image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/1.png',
  76 + token: '22cae1c5-8e8c-4f8a-9502-c59fa752974a'
  77 + },
  78 + {
  79 + image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/2.png',
  80 + token: '66ef584f-6925-466e-ac04-126993c316a1'
  81 + },
  82 + {
  83 + image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/3.png',
  84 + token: 'f7be232b-d548-4927-bc53-db0d10d9f5f1'
  85 + },
  86 + {
  87 + image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/4.png',
  88 + token: 'e30b346f-ec92-453c-8b4e-08f5da225eb4'
  89 + },
  90 + {
  91 + image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/5.png',
  92 + token: 'f3a0b83e-b2d5-450c-9e1d-c68aa7e1fd4e'
  93 + }
  94 + ]
  95 + },
  96 + {
  97 + time: 16,
  98 + tokens: [
  99 + {
  100 + image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/1.png',
  101 + token: '91c38ce0-bd9e-4d11-9bf0-f53c633b60c3'
  102 + },
  103 + {
  104 + image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/2.png',
  105 + token: '6f0d6158-061f-44f7-aed8-789c12eb9dc8'
  106 + },
  107 + {
  108 + image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/3.png',
  109 + token: 'c002aeb5-a398-4113-bbe0-a60369d9242e'
  110 + },
  111 + {
  112 + image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/4.png',
  113 + token: 'f5dd41ca-2a4b-4374-9bc3-c2777da82a8b'
  114 + },
  115 + {
  116 + image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/5.png',
  117 + token: 'e8bc629c-3bda-4778-a863-ae70bc7a5ce7'
  118 + }
  119 + ]
  120 + },
  121 + {
  122 + time: 22,
  123 + tokens: [
  124 + {
  125 + image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/1.png',
  126 + token: '8eb43f41-b35f-4469-b7d2-b076e6315a71'
  127 + },
  128 + {
  129 + image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/2.png',
  130 + token: 'f066e81a-6d27-493f-8a3a-9276bd620718'
  131 + },
  132 + {
  133 + image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/3.png',
  134 + token: '0234b183-834e-4c53-ad42-b1fda17eb122'
  135 + },
  136 + {
  137 + image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/4.png',
  138 + token: '9957ec2f-dec4-46d3-8ae8-55c82d375443'
  139 + },
  140 + {
  141 + image: '//ad.yoho.cn/html5/2019/10/activity/004/coupon/1/5.png',
  142 + token: '1eb7ef64-fb07-4564-b61c-53d4e473292a'
  143 + }
  144 + ]
  145 + }
  146 + ],
  147 + '2019-10-29': [
  148 + {
  149 + time: 10,
  150 + tokens: []
  151 + },
  152 + {
  153 + time: 16,
  154 + tokens: []
  155 + },
  156 + {
  157 + time: 22,
  158 + tokens: []
  159 + }
  160 + ],
  161 + '2019-10-30': [
  162 + {
  163 + time: 10,
  164 + tokens: []
  165 + },
  166 + {
  167 + time: 16,
  168 + tokens: []
  169 + },
  170 + {
  171 + time: 22,
  172 + tokens: []
  173 + }
  174 + ]
  175 + },
  176 + couponData: [],
  177 + },
  178 + nowActiveIndex: 0,
  179 + nextTime: 0, // 下一次抢券时间点
  180 + intervalTime: 10000, // 计时器时间
  181 + timeout: null, // 定时器
  182 + openTime: 0, // 用户打开页面的时间
  183 + };
  184 + },
  185 + beforeRouteLeave(from, to, next) {
  186 + console.log('routeLeave');
  187 + this.clearData();
  188 + next();
  189 + },
  190 + methods: {
  191 + ...mapActions(['getServerTime', 'getCoupon']),
  192 + getDateString(time) { // 获取日期的字符串,如'2019-10-26'
  193 + let date = new Date(time);
  194 + let month = date.getMonth() + 1 > 9 ? (date.getMonth() + 1) : '0' + (date.getMonth() + 1);
  195 + let day = date.getDate() > 9 ? date.getDate() : '0' + date.getDate();
  196 +
  197 + return {
  198 + str: [date.getFullYear(), month, day].join('-'),
  199 + safariStr: [date.getFullYear(), month, day].join('/'), // safari中只能使用这种格式
  200 + dateNum: time
  201 + };
  202 + },
  203 + clearData() {
  204 + clearTimeout(this.timeout);
  205 + this.timeout = null;
  206 + this.serverTime = 0;
  207 + this.nowActiveIndex = 0;
  208 + this.nextTime = 0;
  209 + this.intervalTime = 10000;
  210 + this.openTime = 0;
  211 + this.pageData.couponData = [];
  212 + },
  213 +
  214 + getActiveIndex(time) { // 计算当前应该生效的tab
  215 + console.log('time=', time, new Date(time));
  216 + if (this.pageData.couponData.length > 0) {
  217 + for (let i = 0; i < this.pageData.couponData.length; i ++) {
  218 + let obj = this.pageData.couponData[i];
  219 +
  220 + if ((time >= obj.date_time && time < obj.date_time + 3600000) || (time > obj.date_time - 3600000 && time < obj.date_time)) {
  221 + this.nowActiveIndex = i;
  222 + this.nextTime = obj.date_time;
  223 + break;
  224 + } else if (time < obj.date_time) {
  225 + this.nowActiveIndex = i - 1 > 0 ? (i - 1) : 0;
  226 + this.nextTime = obj.date_time;
  227 + break;
  228 + } else if (time > obj.date_time + 3600000) {
  229 + this.nowActiveIndex = i;
  230 + this.nextTime = obj.date_time;
  231 + }
  232 + }
  233 +
  234 +
  235 + if (this.nextTime) {
  236 + this.checkCouponTime();
  237 + }
  238 +
  239 + }
  240 + },
  241 + initCouponData() { // 初始化今日需要使用到的券的信息
  242 + if (this.serverTime) {
  243 + this.pageData.couponData = [];
  244 + let today = this.getDateString(this.serverTime);
  245 +
  246 + let tomorrow = this.getDateString(this.serverTime + 86400000);
  247 +
  248 + // 此处把今天日期对应的券信息取出放入couponData
  249 + if (this.pageData.couponBaseData[today.str] && this.pageData.couponBaseData[today.str].length > 0) {
  250 + this.pageData.couponBaseData[today.str].forEach((obj) => {
  251 + obj.date = today.str;
  252 +
  253 + // 计算领券开始时间,用于比较,这边用safariStr取时间,防止浏览器兼容问题
  254 + obj.date_time = new Date(today.safariStr + ' ' + obj.time + ':00:00').getTime();
  255 + obj.is_today = true;
  256 +
  257 + if (today.dateNum < obj.date_time) {
  258 + obj.status = 1; // 即将开始
  259 + } else if (today.dateNum > obj.date_time && today.dateNum < obj.date_time + 3600000) {
  260 + obj.status = 2; // 进行中
  261 + } else if (today.dateNum > obj.date_time + 3600000) {
  262 + obj.status = 0; // 已抢完
  263 + }
  264 + obj.statusText = STATUS_MAP[obj.status];
  265 +
  266 + this.pageData.couponData.push(obj);
  267 + });
  268 + }
  269 +
  270 + // 把明日对应的首条券信息放入couponData
  271 + if (this.pageData.couponBaseData[tomorrow.str] && this.pageData.couponBaseData[tomorrow.str].length > 0) {
  272 + let obj = this.pageData.couponBaseData[tomorrow.str][0];
  273 +
  274 + obj.date = tomorrow.str;
  275 + obj.date_time = new Date(tomorrow.safariStr + ' ' + obj.time + ':00:00').getTime();
  276 + obj.is_today = false;
  277 + obj.status = 3;
  278 + obj.statusText = STATUS_MAP[obj.status];
  279 + this.pageData.couponData.push(obj);
  280 + }
  281 +
  282 + this.getActiveIndex(this.serverTime); // 获取生效的tab页
  283 +
  284 + console.log(this.pageData.couponData);
  285 + }
  286 + },
  287 + checkCouponTime() { // 定时器用于判断是否到下一次抢券的时间
  288 + let interval = this.intervalTime;
  289 + let serverDate = new Date(this.serverTime);
  290 +
  291 + if (serverDate.getDate() !== new Date(this.openTime).getDate()) {
  292 + this.openTime = Date.now();
  293 + console.log(serverDate.getDate(), new Date(this.openTime).getDate());
  294 + this.$router.replace({
  295 + name: 'LimitTimeCoupon',
  296 + params: {
  297 + code: Math.random().toString(16).substr(2,8)
  298 + }
  299 + });
  300 + return;
  301 + }
  302 +
  303 + if (this.nextTime - this.serverTime < 10000 && this.serverTime < this.nextTime) {
  304 + interval = 1000; // 当下一次开始时间和当前时间比较小于10秒时,则1秒判断一次
  305 + }
  306 + this.serverTime += interval;
  307 + console.log('timeout=', new Date(this.serverTime));
  308 +
  309 + if (this.serverTime >= this.nextTime && this.serverTime < this.nextTime + 1000) { // 在抢券时间内则初始化券信息
  310 + this.initCouponData();
  311 + } else if (this.serverTime - this.nextTime > 3600000 + interval) {
  312 + this.initCouponData();
  313 + } else {
  314 + this.timeout = setTimeout(this.getActiveIndex.bind(this), interval, this.serverTime);
  315 + }
  316 + },
  317 + changeTab(e) {
  318 + let index = e.currentTarget.dataset.index;
  319 +
  320 + this.nowActiveIndex = parseInt(index, 10);
  321 + clearTimeout(this.timeout);
  322 +
  323 + }
  324 + }
  325 +};
  326 +</script>
  327 +
  328 +<style lang="scss" scoped>
  329 + .activity-wrapper {
  330 + position: relative;
  331 + width: 100%;
  332 + height: 100%;
  333 + overflow-x: hidden;
  334 + overflow-y: scroll;
  335 + background-color: #444;
  336 + }
  337 +
  338 + .activity-container {
  339 + width: 100%;
  340 + height: 3000px;
  341 + background-image: url("//ad.yoho.cn/html5/2019/10/activity/004/base/repeat_bg.jpg?imageslim");
  342 + background-repeat: repeat-y;
  343 + background-size: 100% auto;
  344 + overflow: hidden;
  345 +
  346 + #page_ul {
  347 + width: 100%;
  348 + margin: 0;
  349 + padding: 0;
  350 + list-style: none;
  351 + overflow: hidden;
  352 +
  353 + li {
  354 + position: relative;
  355 + width: 100%;
  356 + overflow: hidden;
  357 +
  358 + img {
  359 + width: 100%;
  360 + float: left;
  361 + }
  362 + }
  363 +
  364 + .tab-container {
  365 + width: 100%;
  366 + height: 112px;
  367 + display: flex;
  368 + flex-direction: row;
  369 +
  370 + .tab-item {
  371 + display: inline-block;
  372 + width: 100%;
  373 + height: 100%;
  374 + box-sizing: border-box;
  375 + background-color: #9ef43a;
  376 +
  377 + &.active {
  378 + background-color: #fff;
  379 + }
  380 +
  381 + p {
  382 + font-family: "Helvetica Neue", "Source Han Sans CN", "PingFang SC", Helvetica, sans-serif;
  383 + display: inline-block;
  384 + text-align: center;
  385 + width: 100%;
  386 + line-height: 40px;
  387 + font-weight: 600;
  388 + }
  389 +
  390 + .time-text {
  391 + font-size: 40px;
  392 + margin-top: 20px;
  393 + }
  394 +
  395 + .status-text {
  396 + font-size: 24px;
  397 + line-height: 32px;
  398 + }
  399 + }
  400 + }
  401 +
  402 + .coupon-container {
  403 + position: relative;
  404 + background-image: url("//ad.yoho.cn/html5/2019/10/activity/004/base/coupon_bg.png?imageslim");
  405 + background-size: 100% auto;
  406 + background-repeat: repeat-y;
  407 + overflow: hidden;
  408 + margin-top: 40px;
  409 +
  410 + .coupon-item {
  411 + position: relative;
  412 + margin: 0 auto 20px auto;
  413 + width: 530px;
  414 + height: 127px;
  415 + background-size: 100% 100%;
  416 + }
  417 + }
  418 + }
  419 + }
  420 +</style>
@@ -7,6 +7,7 @@ import Passport from './passport'; @@ -7,6 +7,7 @@ import Passport from './passport';
7 import Address from './address'; 7 import Address from './address';
8 import Notice from './notice'; 8 import Notice from './notice';
9 import Category from './category'; 9 import Category from './category';
  10 +import Activitys from './activitys';
10 11
11 export default [ 12 export default [
12 { 13 {
@@ -22,4 +23,5 @@ export default [ @@ -22,4 +23,5 @@ export default [
22 ...Address, 23 ...Address,
23 ...Notice, 24 ...Notice,
24 ...Category, 25 ...Category,
  26 + ...Activitys
25 ]; 27 ];
  1 +import limitTimeCoupon from './limit-time-coupon';
  2 +
  3 +export default function() {
  4 + return {
  5 + namespaced: true,
  6 + modules: {
  7 + limitTimeCoupon: limitTimeCoupon()
  8 + }
  9 + };
  10 +}
  1 +
  2 +export const Types = {
  3 + GET_COUPON_REQUEST: 'GET_COUPON_REQUEST',
  4 + GET_COUPON_SUCCESS: 'GET_COUPON_SUCCESS',
  5 + GET_COUPON_FAILED: 'GET_COUPON_FAILED',
  6 + GET_SERVER_TIME_SUCCESS: 'GET_SERVER_TIME_SUCCESS',
  7 +};
  8 +
  9 +export default function() {
  10 + return {
  11 + namespaced: true,
  12 + state: {
  13 + fetchingCoupon: false,
  14 + serverTime: ''
  15 + },
  16 + mutations: {
  17 + [Types.GET_COUPON_REQUEST](state) {
  18 + state.fetchingCoupon = true;
  19 + },
  20 + [Types.GET_COUPON_SUCCESS](state) {
  21 + state.fetchingCoupon = false;
  22 + },
  23 + [Types.GET_COUPON_FAILED](state) {
  24 + state.fetchingCoupon = false;
  25 + },
  26 + [Types.GET_SERVER_TIME_SUCCESS](state, {time}) {
  27 + state.serverTime = time;
  28 + console.log('mutationServerTime', time);
  29 + }
  30 + },
  31 + actions: {
  32 + async getCoupon({commit}, {couponToken}) {
  33 + commit(Types.GET_COUPON_REQUEST);
  34 +
  35 + const result = await this.$api.get('/api/ufo/coupons/send', {coupon_tokens: couponToken});
  36 +
  37 + if (result && result.code === 200) {
  38 + commit(Types.GET_COUPON_SUCCESS);
  39 + } else {
  40 + commit(Types.GET_COUPON_FAILED);
  41 + }
  42 +
  43 + return result || {};
  44 + },
  45 +
  46 + async getServerTime({commit}) {
  47 + const result =await this.$api.get('/app/ufo/getServerTime');
  48 +
  49 + return result;
  50 + }
  51 + }
  52 + };
  53 +};
@@ -12,6 +12,7 @@ import storeHome from './home'; @@ -12,6 +12,7 @@ import storeHome from './home';
12 import storeAddress from './address'; 12 import storeAddress from './address';
13 import storeNotice from './notice'; 13 import storeNotice from './notice';
14 import storeCategory from './category'; 14 import storeCategory from './category';
  15 +import storeActivitys from './activitys';
15 16
16 Vue.use(Vuex); 17 Vue.use(Vuex);
17 18
@@ -32,6 +33,7 @@ export function createStore(context) { @@ -32,6 +33,7 @@ export function createStore(context) {
32 33
33 category: storeCategory(), 34 category: storeCategory(),
34 // buyerOderList: buyerOderList(), 35 // buyerOderList: buyerOderList(),
  36 + activitys: storeActivitys(),
35 }, 37 },
36 strict: process.env.NODE_ENV !== 'production', 38 strict: process.env.NODE_ENV !== 'production',
37 }); 39 });
  1 +module.exports = {
  2 + // 有货领券
  3 + 'api/coupons/send': {
  4 + auth: true,
  5 + accessLog: true,
  6 + checkSign: true,
  7 + ufo: false,
  8 + api: 'app.coupons.couponSend',
  9 + params: {
  10 + coupon_send_token: {type: String}
  11 + },
  12 + },
  13 +
  14 + // ufo领券
  15 + 'api/ufo/coupons/send': {
  16 + auth: true,
  17 + accessLog: true,
  18 + checkSign: true,
  19 + ufo: true,
  20 + api: 'ufo.coupons.send',
  21 + params: {
  22 + coupon_tokens: {type: String}
  23 + },
  24 + },
  25 +};
@@ -9,6 +9,7 @@ const orderListApi = require('./order-api-map'); @@ -9,6 +9,7 @@ const orderListApi = require('./order-api-map');
9 const categoryApi = require('./category-api-map'); 9 const categoryApi = require('./category-api-map');
10 const sellerAskApi = require('./sellerask-api-map'); 10 const sellerAskApi = require('./sellerask-api-map');
11 const systemApi = require('./system-api-map'); 11 const systemApi = require('./system-api-map');
  12 +const activitysApi = require('./activitys-api-map');
12 13
13 module.exports = { 14 module.exports = {
14 ...orderApi, 15 ...orderApi,
@@ -22,4 +23,5 @@ module.exports = { @@ -22,4 +23,5 @@ module.exports = {
22 ...categoryApi, 23 ...categoryApi,
23 ...sellerAskApi, 24 ...sellerAskApi,
24 ...systemApi, 25 ...systemApi,
  26 + ...activitysApi
25 }; 27 };
@@ -55,6 +55,14 @@ exports.createApp = async(app) => { @@ -55,6 +55,14 @@ exports.createApp = async(app) => {
55 next(); 55 next();
56 }); 56 });
57 57
  58 + // 获取服务器时间
  59 + app.get('/xianyu/app/ufo/getServerTime', function(req, res) {
  60 + return res.json({
  61 + code: 200,
  62 + data: Date.now()
  63 + });
  64 + });
  65 +
58 // 添加请求上下文 66 // 添加请求上下文
59 app.use(global.yoho.httpCtx()); 67 app.use(global.yoho.httpCtx());
60 68