소스 검색

新增红包活动页

gongyan 4 년 전
부모
커밋
a1f942d085
48개의 변경된 파일3169개의 추가작업 그리고 18개의 파일을 삭제
  1. 7 0
      README.md
  2. 1 1
      config/dev.env.js
  3. 1 2
      config/prod.env.js
  4. 1 1
      config/test.env.js
  5. 6 2
      src/App.vue
  6. 8 0
      src/api/base.js
  7. 39 1
      src/api/index.js
  8. 6 0
      src/assets/css/common.css
  9. 27 3
      src/assets/font/iconfont.css
  10. 0 0
      src/assets/font/iconfont.js
  11. 42 0
      src/assets/font/iconfont.json
  12. BIN
      src/assets/font/iconfont.ttf
  13. BIN
      src/assets/font/iconfont.woff
  14. BIN
      src/assets/font/iconfont.woff2
  15. BIN
      src/assets/image/hb/active_bg.png
  16. BIN
      src/assets/image/hb/draw_award_bg_small.png
  17. BIN
      src/assets/image/hb/draw_button.png
  18. BIN
      src/assets/image/hb/hb_bg.png
  19. BIN
      src/assets/image/hb/icon_hb.png
  20. BIN
      src/assets/image/hb/light.gif
  21. BIN
      src/assets/image/hb/luckDraw/0.png
  22. BIN
      src/assets/image/hb/luckDraw/1.png
  23. BIN
      src/assets/image/hb/luckDraw/2.png
  24. BIN
      src/assets/image/hb/luckDraw/3.png
  25. BIN
      src/assets/image/hb/luckDraw/4.png
  26. BIN
      src/assets/image/hb/luckDraw/awardBgSmall.png
  27. BIN
      src/assets/image/hb/luckDraw/awardBtn.png
  28. BIN
      src/assets/image/hb/luckDraw/button.png
  29. BIN
      src/assets/image/hb/luckDraw/close.png
  30. BIN
      src/assets/image/hb/normal_bg.gif
  31. BIN
      src/assets/image/hb/point.gif
  32. BIN
      src/assets/image/rxzr.png
  33. BIN
      src/assets/image/wcfml.png
  34. 103 0
      src/components/Active/ActiveTitle/ActiveTitle.vue
  35. 425 0
      src/components/Active/LuckDraw/LuckDraw.vue
  36. 227 0
      src/components/Active/StartPage/StartPage.vue
  37. 136 0
      src/components/DropdownList/DropdownList.vue
  38. 8 1
      src/components/Menu/MenuService.vue
  39. 1 1
      src/config.js
  40. 16 4
      src/router/index.js
  41. 76 0
      src/store/index.js
  42. 168 0
      src/store/modules/hb.js
  43. 49 2
      src/utils/utils.js
  44. 82 0
      src/views/Active/Hb/Hb.vue
  45. 627 0
      src/views/Active/Hb/HbDialog/HbDialog.vue
  46. 555 0
      src/views/Active/Hb/HbTask/HbTask.vue
  47. 484 0
      src/views/Active/Hb/HbTask/HbTaskList/HbTaskList.vue
  48. 74 0
      src/views/Active/Hb/HbTask/HbTaskTips/HbTaskTips.vue

+ 7 - 0
README.md

@@ -235,3 +235,10 @@ appid
 ## 8.18
 
 -   [x] 鸿币可输入
+-   [ ] 新增鸿币充值页面
+-   [ ] 登录之后跳转活动页, 活动页之后再进入游戏
+-   [ ] 活动页转盘奖品 去领取 跳转鸿币页面 去领取(报错)
+
+## 8.19
+
+-   [x] 活动页新增 选择角色

+ 1 - 1
config/dev.env.js

@@ -6,5 +6,5 @@ module.exports = merge(prodEnv, {
     NODE_ENV: '"production"',
     API_ROOT: '"https://api.jhfly.cn/test"', // 测试环境地址(wcfml 测试)
     // API_ROOT: '"http://42.193.163.133:30000/test"', // 测试环境地址
-    wcfmlLogin: '"jhfly://localhost/login"', // 忘川伏魔录登录地址
+    API_HB: '"https://api.jhfly.cn"', // 正式环境 红包活动
 })

+ 1 - 2
config/prod.env.js

@@ -2,6 +2,5 @@
 module.exports = {
     NODE_ENV: '"production"',
     API_ROOT: '"https://api.jhfly.cn"', // 正式环境地址
-    wcfmlLogin: '"jhfly://localhost/login"', // 忘川伏魔录登录地址
-
+    API_HB: '"https://api.jhfly.cn"', // 正式环境 红包活动
 }

+ 1 - 1
config/test.env.js

@@ -5,5 +5,5 @@ const prodEnv = require('./prod.env')
 module.exports = merge(prodEnv, {
     NODE_ENV: '"development"',
     API_ROOT: '"http://42.193.163.133:30000"', // 本地开发测试环境地址
-    wcfmlLogin: '"jhfly://localhost/login"', // 忘川伏魔录登录地址
+    API_HB: '"https://api.jhfly.cn"', // 正式环境 红包活动
 })

+ 6 - 2
src/App.vue

@@ -1,11 +1,13 @@
 <template>
   <div id="app">
+    <MenuService v-if="showServiceInline" />
     <router-view />
   </div>
 </template>
 
 <script>
 import { mapState, mapActions } from "vuex";
+import MenuService from "@/components/Menu/MenuService"; // 联系客服
 export default {
   name: "App",
   // 向子组件提供变量/方法, 子组件通过inject 注入变量
@@ -31,14 +33,16 @@ export default {
       onRecharge: this.onRecharge,
     };
   },
-  components: {},
+  components: {
+    MenuService,
+  },
   data() {
     return {
       wechatAppid: "wx2c4f9f89c572aeb5", // 公众号appid
     };
   },
   computed: {
-    ...mapState(["query", "appid", "CONFIG", "userInfo"]),
+    ...mapState(["query", "appid", "CONFIG", "userInfo", "showServiceInline"]),
   },
   watch: {
     // 监听路由变化 初始化路由

+ 8 - 0
src/api/base.js

@@ -16,6 +16,14 @@ const base = {
     finance: "/api/pay/finance", // 鸿币余额
     recharge: "/api/pay/recharge", // 鸿币充值
     wechatOpenId: "/api/pay/wechatOpenId", // code换openid
+
+    /***** 红包 ******/
+    baseUrlHB: process.env.API_HB, // 红包接口api
+    hbInfo: "/activity/hb/info", // 玩家信息 get
+    hbTasks: "/activity/hb/tasks", // 任务列表 get
+    hbCompleteTask: "/activity/hb/completeTask", // 完成任务 post
+    hbDrawCoupon: '/activity/hb/drawCoupon', // 领取优惠券
+    hbCouponList: '/activity/hb/couponList', // 优惠券列表
 }
 
 export default base;

+ 39 - 1
src/api/index.js

@@ -132,7 +132,45 @@ const api = {
      */
     wechatOpenId(params) {
         return axios.post(base.baseUrl + base.wechatOpenId, params)
-    }
+    },
+
+    /* ************ 红包 ************ */
+    /**
+     * 玩家信息
+     */
+    hbInfo(params) {
+        return axios.get(base.baseUrlHB + base.hbInfo, { params })
+    },
+
+    /**
+     * 任务列表
+     */
+    hbTasks(params) {
+        return axios.get(base.baseUrlHB + base.hbTasks, { params })
+    },
+
+    /**
+     * 任务列表
+     */
+    hbCompleteTask(params) {
+        return axios.post(base.baseUrlHB + base.hbCompleteTask, params)
+    },
+
+
+    /**
+     * 领取优惠券
+     */
+    hbDrawCoupon(params) {
+        return axios.post(base.baseUrlHB + base.hbDrawCoupon, params)
+    },
+
+
+    /**
+     * 优惠券列表
+     */
+    hbCouponList(params) {
+        return axios.get(base.baseUrlHB + base.hbCouponList, { params })
+    },
 };
 
 export default api;

+ 6 - 0
src/assets/css/common.css

@@ -67,4 +67,10 @@ input {
     right: 0;
     bottom: 0;
     background-color: rgba(0, 0, 0, 0.6);
+}
+
+.text_overflow {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
 }

+ 27 - 3
src/assets/font/iconfont.css

@@ -1,8 +1,8 @@
 @font-face {
   font-family: "iconfont"; /* Project id 2554785 */
-  src: url('iconfont.woff2?t=1625988955157') format('woff2'),
-       url('iconfont.woff?t=1625988955157') format('woff'),
-       url('iconfont.ttf?t=1625988955157') format('truetype');
+  src: url('iconfont.woff2?t=1629335336831') format('woff2'),
+       url('iconfont.woff?t=1629335336831') format('woff'),
+       url('iconfont.ttf?t=1629335336831') format('truetype');
 }
 
 .iconfont {
@@ -13,6 +13,30 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-zhuanhuan:before {
+  content: "\e726";
+}
+
+.icon-notice:before {
+  content: "\e628";
+}
+
+.icon-message:before {
+  content: "\e622";
+}
+
+.icon-wenjianbao:before {
+  content: "\e64d";
+}
+
+.icon-qq:before {
+  content: "\e604";
+}
+
+.icon-zaixiankefu:before {
+  content: "\e65a";
+}
+
 .icon-shandian:before {
   content: "\e638";
 }

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
src/assets/font/iconfont.js


+ 42 - 0
src/assets/font/iconfont.json

@@ -5,6 +5,48 @@
   "css_prefix_text": "icon-",
   "description": "",
   "glyphs": [
+    {
+      "icon_id": "22779326",
+      "name": "转换",
+      "font_class": "zhuanhuan",
+      "unicode": "e726",
+      "unicode_decimal": 59174
+    },
+    {
+      "icon_id": "850713",
+      "name": "notice",
+      "font_class": "notice",
+      "unicode": "e628",
+      "unicode_decimal": 58920
+    },
+    {
+      "icon_id": "4682611",
+      "name": "message",
+      "font_class": "message",
+      "unicode": "e622",
+      "unicode_decimal": 58914
+    },
+    {
+      "icon_id": "1271950",
+      "name": "icon_package",
+      "font_class": "wenjianbao",
+      "unicode": "e64d",
+      "unicode_decimal": 58957
+    },
+    {
+      "icon_id": "455048",
+      "name": "qq",
+      "font_class": "qq",
+      "unicode": "e604",
+      "unicode_decimal": 58884
+    },
+    {
+      "icon_id": "22236035",
+      "name": "在线客服",
+      "font_class": "zaixiankefu",
+      "unicode": "e65a",
+      "unicode_decimal": 58970
+    },
     {
       "icon_id": "18663433",
       "name": "闪电",

BIN
src/assets/font/iconfont.ttf


BIN
src/assets/font/iconfont.woff


BIN
src/assets/font/iconfont.woff2


BIN
src/assets/image/hb/active_bg.png


BIN
src/assets/image/hb/draw_award_bg_small.png


BIN
src/assets/image/hb/draw_button.png


BIN
src/assets/image/hb/hb_bg.png


BIN
src/assets/image/hb/icon_hb.png


BIN
src/assets/image/hb/light.gif


BIN
src/assets/image/hb/luckDraw/0.png


BIN
src/assets/image/hb/luckDraw/1.png


BIN
src/assets/image/hb/luckDraw/2.png


BIN
src/assets/image/hb/luckDraw/3.png


BIN
src/assets/image/hb/luckDraw/4.png


BIN
src/assets/image/hb/luckDraw/awardBgSmall.png


BIN
src/assets/image/hb/luckDraw/awardBtn.png


BIN
src/assets/image/hb/luckDraw/button.png


BIN
src/assets/image/hb/luckDraw/close.png


BIN
src/assets/image/hb/normal_bg.gif


BIN
src/assets/image/hb/point.gif


BIN
src/assets/image/rxzr.png


BIN
src/assets/image/wcfml.png


+ 103 - 0
src/components/Active/ActiveTitle/ActiveTitle.vue

@@ -0,0 +1,103 @@
+<template>
+  <div class="active_title">
+    <!-- 红包 -->
+    <div v-if="active === 'hb'" class="active_title_hb">{{ acitveTitle }}</div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "",
+  components: {},
+  data() {
+    return {};
+  },
+  props: {
+    // 活动代号
+    active: {
+      type: String,
+      default: "",
+    },
+    // 活动标题
+    acitveTitle: {
+      type: String,
+      default: "",
+    },
+  },
+  computed: {},
+  watch: {},
+  created() {},
+  mounted() {},
+  methods: {},
+};
+</script>
+
+<style lang='less'>
+.active_title {
+  width: 250 / @rem;
+  margin: -60 / @rem auto 0;
+
+  // 红包
+  .active_title_hb {
+    position: relative;
+    width: 250 / @rem;
+    height: 100 / @rem;
+    line-height: 100 / @rem;
+    border-radius: 0 0 20px 20px;
+    color: #fff;
+    background-image: linear-gradient(#fbd6bf, #ff0a2f);
+
+    &::after {
+      position: absolute;
+      left: -40 / @rem;
+      top: 0;
+      content: "";
+      display: block;
+      border: 20 / @rem solid transparent;
+      border-right: 20 / @rem solid #c50b00;
+      border-bottom: 20 / @rem solid #c50b00;
+    }
+
+    &::before {
+      position: absolute;
+      left: 250 / @rem;
+      top: 0;
+      content: "";
+      display: block;
+      border: 20 / @rem solid transparent;
+      border-left: 20 / @rem solid #c50b00;
+      border-bottom: 20 / @rem solid #c50b00;
+    }
+  }
+}
+
+// 正常横屏样式
+@media all and (orientation: landscape),
+  /** 伪竖屏*/all and (orientation: portrait) and (min-width: 600px) and (min-height: 800px) {
+  .active_title {
+    width: 125 / @rem;
+    margin: -30 / @rem auto 0;
+
+    // 红包
+    .active_title_hb {
+      width: 125 / @rem;
+      height: 50 / @rem;
+      line-height: 50 / @rem;
+
+      &::after {
+        left: -20 / @rem;
+        border: 10 / @rem solid transparent;
+        border-right: 10 / @rem solid #c50b00;
+        border-bottom: 10 / @rem solid #c50b00;
+      }
+
+      &::before {
+        left: 125 / @rem;
+        border: 10 / @rem solid transparent;
+        border-left: 10 / @rem solid #c50b00;
+        border-bottom: 10 / @rem solid #c50b00;
+      }
+    }
+  }
+}
+</style>

+ 425 - 0
src/components/Active/LuckDraw/LuckDraw.vue

@@ -0,0 +1,425 @@
+<template>
+  <div class="luck_draw">
+    <!-- 十二宫格奖品框 -->
+    <div class="first">
+      <div class="second">
+        <div class="third">
+          <!-- 奖品列表 -->
+          <ul class="award_list" v-if="awardList.length > 0">
+            <li
+              v-for="(item, index) in awardList"
+              :key="index"
+              :class="{ active: currentIndex == index, isbig: !item.name }"
+            >
+              <div class="award_center">
+                <img :src="item.img" alt="" />
+                <span>{{ item.name }}</span>
+              </div>
+            </li>
+            <!-- 立即抽奖 -->
+            <li class="btn_box" @click="toDraw">
+              <img
+                src="@/assets/image/hb/luckDraw/button.png"
+                class="button_big"
+                alt=""
+              />
+              <!-- <div class="btn">
+                <div @click="toOne">单抽</div>
+                <div @click="toTen">十连</div>
+              </div> -->
+            </li>
+            <!-- 立即抽奖阴影 -->
+            <li class="border"></li>
+          </ul>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "LuckDraw",
+  components: {},
+  data() {
+    return {
+      currentIndex: -1, // 当前奖品(动效)
+      drawTimer: null, // 定时器 快速
+      canDraw: true, // 是否可以抽奖(防抖)
+      timerSpeed: 100, // 定时器速度
+      randomIndex: null, // 随机下标开始减速
+      loop: 0, // 循环次数
+    };
+  },
+  props: {
+    // 十二宫格奖品列表
+    awardList: {
+      type: Array,
+      default: function () {
+        return [];
+      },
+    },
+  },
+  computed: {
+    // 获取到的折扣券
+    myAwardList() {
+      return this.$store.state.hb.myAwardList;
+    },
+  },
+  watch: {
+    myAwardList: {
+      handler: function (newValue) {},
+      deep: true,
+      immediate: true,
+    },
+  },
+  created() {},
+  mounted() {},
+  beforeDestroy() {
+    const { drawTimer } = this;
+    clearTimeout(drawTimer);
+  },
+  methods: {
+    // 单抽动画
+    toDraw() {
+      if (!this.canDraw) {
+        return;
+      }
+      this.canDraw = false;
+      // 1. 取数据
+      this.$emit("toDraw");
+      // 2. 动效
+      this.animationDraw();
+    },
+
+    // 立即抽奖动效(中奖前的动效)
+    animationDraw() {
+      // 1. 快速过一圈
+      this.drawTimer = setTimeout(() => {
+        this.currentIndex = this.currentIndex + 1;
+        // 1.1 重复圈数
+        if (this.currentIndex >= this.awardList.length) {
+          this.currentIndex = 0;
+          this.loop = this.loop + 1;
+        }
+        // 1.3 拿到奖品
+        if (this.myAwardList && this.myAwardList.length) {
+          let index = null;
+          // 1.3.1 找到奖品在页面上显示的下标
+          this.awardList.map((awardListEle, awardListIndex) => {
+            const { myAwardList, currentAward } = this;
+            if (awardListEle.id == myAwardList[0].id) {
+              index = awardListIndex;
+              return;
+            }
+          });
+
+          // 1.3.2 如果下标在 index的前 5-3 位, 开始减速
+          if (this.randomIndex == null) {
+            this.randomIndex = parseInt(Math.random() * (5 - 3 + 1) + 3);
+          }
+          const len = this.awardList.length - 1;
+          const _index =
+            index - this.randomIndex < 0
+              ? len - (this.randomIndex - index)
+              : index - this.randomIndex;
+
+          // 1.3.4 开始最小速度
+          if (this.currentIndex === _index && this.loop >= 1) {
+            this.timerSpeed = this.timerSpeed + 500;
+          }
+
+          // 1.3.5如果下标相同, 停止
+          if (
+            this.currentIndex == index &&
+            this.loop >= 1 &&
+            this.timerSpeed > 100
+          ) {
+            this.loop = 0;
+            this.timerSpeed = 100;
+            clearTimeout(this.drawTimer);
+            setTimeout(() => {
+              this.$emit("onShowDialog", "luckDrawAward");
+            }, 1000);
+            return;
+          }
+        }
+
+        // 2. 再次启动定时器
+        this.animationDraw();
+      }, this.timerSpeed);
+    },
+  },
+};
+</script>
+
+<style lang='less'>
+.luck_draw {
+  width: 100%;
+  height: 100%;
+  color: #fff;
+  font-size: 26 / @rem;
+  position: relative;
+
+  .first {
+    width: 100%;
+    height: 100%;
+    padding: 4%;
+    box-sizing: border-box;
+    border-radius: 6%;
+    background-color: #fe777d;
+  }
+  .second {
+    width: 100%;
+    height: 100%;
+    padding: 1.5%;
+    box-sizing: border-box;
+    border-radius: 5%;
+    background-color: #ff4a4c;
+  }
+  .third {
+    width: 100%;
+    height: 100%;
+    padding-top: 1.2%;
+    padding-left: 1.2%;
+    padding-right: 1%;
+    padding-bottom: 0.5%;
+    box-sizing: border-box;
+    border-radius: 5%;
+    background-color: #ff5454;
+    // background-color: #ff625b;
+  }
+  // 奖品列表
+  .award_list {
+    width: 100%;
+    height: 100%;
+    margin: 0 auto;
+    padding-left: 0.5%;
+    position: relative;
+
+    li {
+      width: 24%;
+      height: 24%;
+      position: absolute;
+      border-radius: 20%;
+      background-color: #fff;
+      color: #e25b63;
+
+      &.active {
+        background-image: linear-gradient(#ffece0, #ff768a);
+        // background-image: linear-gradient(160deg, #fedbb6 0%, #fdc68a 75%);
+      }
+
+      img {
+        width: 50%;
+        display: block;
+        margin: 10% auto 1%;
+      }
+
+      // 左->右
+      &:nth-child(1) {
+        left: 0;
+      }
+      &:nth-child(2) {
+        left: 25%;
+      }
+      &:nth-child(3) {
+        left: 50%;
+      }
+      &:nth-child(4) {
+        left: 75%;
+      }
+      // 右->下
+      &:nth-child(5) {
+        top: 25%;
+        left: 75%;
+      }
+      &:nth-child(6) {
+        top: 50%;
+        left: 75%;
+      }
+      &:nth-child(7) {
+        top: 75%;
+        left: 75%;
+      }
+      // 右下->左下
+      &:nth-child(8) {
+        top: 75%;
+        left: 50%;
+      }
+      &:nth-child(9) {
+        top: 75%;
+        left: 25%;
+      }
+      &:nth-child(10) {
+        top: 75%;
+        left: 0%;
+      }
+      // 左下->左上
+      &:nth-child(11) {
+        top: 50%;
+        left: 0%;
+      }
+      &:nth-child(12) {
+        top: 25%;
+        left: 0%;
+      }
+
+      //   &.isbig {
+      //     img {
+      //       width: 110%;
+      //       margin-top: -22%;
+      //       margin-left: -5%;
+      //     }
+      //   }
+    }
+
+    // 立即抽奖
+    .btn_box {
+      width: 48%;
+      height: 46%;
+      top: 25.5%;
+      left: 25.5%;
+      border-radius: 10%;
+      z-index: 2;
+      background-image: linear-gradient(160deg, #ffa3a7 0%, #ff2848 135%);
+
+      img {
+        width: 65%;
+        margin-top: 22%;
+      }
+
+      .btn {
+        display: flex;
+
+        div {
+          width: 40%;
+          height: 50 / @rem;
+          line-height: 50 / @rem;
+          margin: 20 / @rem 10 / @rem;
+          float: left;
+          flex-grow: 1;
+          font-size: 28 / @rem;
+          border-radius: 25 / @rem;
+          background-color: #ff3a50;
+          color: #fff;
+        }
+      }
+    }
+    // 立即抽奖阴影
+    .border {
+      width: 48%;
+      height: 48%;
+      top: 25.5%;
+      left: 25.5%;
+      border-radius: 8%;
+      background-color: #fc3347;
+      z-index: 1;
+    }
+  }
+
+  // 单抽和十连确认框
+  .confirm_box {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background-color: rgba(0, 0, 0, 0.6);
+    border-radius: 6%;
+    z-index: 3;
+    color: #e66f51;
+
+    .confirm_box_content {
+      width: 70%;
+      height: 70%;
+      margin: 15%;
+      padding: 20 / @rem;
+      box-sizing: border-box;
+      //   background-color: #fff;
+      background-image: linear-gradient(160deg, #fedbb6 0%, #fdc68a 75%);
+      border-radius: 6%;
+
+      .title {
+        font-size: 36 / @rem;
+        font-weight: bold;
+        margin-bottom: 100 / @rem;
+      }
+
+      span {
+        color: #ff3a50;
+      }
+
+      .tips {
+        height: 50 / @rem;
+        line-height: 50 / @rem;
+      }
+
+      .btn_box {
+        display: flex;
+
+        .left,
+        .right {
+          height: 60 / @rem;
+          line-height: 60 / @rem;
+          margin: 120 / @rem 20 / @rem;
+          border-radius: 25 / @rem;
+          flex-grow: 1;
+          background-color: #fff;
+        }
+      }
+    }
+  }
+}
+
+/**横屏 */
+@media screen and (orientation: landscape),
+  only screen and (min-device-width: 768px) and (min-device-height: 768px) and (orientation: portrait) {
+  .luck_draw {
+    font-size: 13 / @rem;
+
+    // 奖品列表
+    .award_list {
+      // 立即抽奖
+      .btn_box {
+        .btn {
+          div {
+            height: 25 / @rem;
+            line-height: 25 / @rem;
+            margin: 10 / @rem 5 / @rem;
+            font-size: 14 / @rem;
+            border-radius: 12.5 / @rem;
+          }
+        }
+      }
+    }
+
+    // 单抽和十连确认框
+    .confirm_box {
+      .confirm_box_content {
+        padding: 10 / @rem;
+
+        .title {
+          font-size: 18 / @rem;
+          margin-bottom: 50 / @rem;
+        }
+
+        .tips {
+          height: 25 / @rem;
+          line-height: 25 / @rem;
+        }
+
+        .btn_box {
+          .left,
+          .right {
+            height: 30 / @rem;
+            line-height: 30 / @rem;
+            margin: 60 / @rem 10 / @rem;
+            border-radius: 15 / @rem;
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 227 - 0
src/components/Active/StartPage/StartPage.vue

@@ -0,0 +1,227 @@
+<template>
+  <!-- 开屏 -> 红包 / 开屏 动效 -> 从页面位置移动到sdk位置 -->
+  <div
+    class="start_page"
+    :class="{ start_page_animate: startPageAnimate }"
+    ref="startPage"
+  >
+    <div class="img_box">
+      <i class="iconfont icon-baseline-close-px" @click="back"></i>
+      <img ref="normalImg" :src="normalImg" alt="" @click="start" />
+      <img
+        ref="activeImg"
+        v-if="activeImg && showActiveImg"
+        :src="activeImg"
+        alt=""
+        class="active_img"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "StartPage",
+  components: {},
+  data() {
+    return {
+      startPageAnimate: false, // 是否开始动画
+      showActiveImg: false, // 显示动效gif
+      animateTime: 3000, // 动效时间 (同时需要修改css)
+    };
+  },
+  props: {
+    // 默认展示图片
+    normalImg: {
+      type: String,
+      default: "",
+    },
+    // 点击开启的动图
+    activeImg: {
+      type: String,
+      default: "",
+    },
+    // 点击开始延迟关闭时间
+    delayTime: {
+      type: Number,
+      default: 0,
+    },
+  },
+  computed: {},
+  watch: {},
+  created() {},
+  mounted() {},
+  methods: {
+    // 关闭
+    back() {
+      this.$emit("onCloseActive");
+
+      // 1. 获取到sdk-icon的位置
+      //   const floatLocation = this.getSdkIconLocation();
+      // 2.1. 获取到normalImg的高度(动效终点 startPage居中)
+      //   const normalImgHeight = this.$refs.normalImg.clientHeight;
+      //   // 2.2 执行动效 -> 移动到sdk位置
+      //   this.hbKeyframe(floatLocation, normalImgHeight);
+
+      // 3. 不显示hb  动效时间
+      //   setTimeout(() => {
+      //     this.$emit("onCloseActive");
+      //   }, this.animateTime);
+    },
+
+    // 获取sdk-icon的位置
+    getSdkIconLocation() {
+      const float = document.querySelector(".float");
+      const floatTop = float && float.getClientRects()[0].top;
+      const floatLeft = float && float.getClientRects()[0].left;
+      const floatHeight = float && float.getClientRects()[0].height;
+      return { top: floatTop, left: floatLeft, height: floatHeight };
+    },
+
+    // keyframe 动效 -> 移动到sdk位置
+    hbKeyframe(floatLocation, imgHeight) {
+      // 获取本身高度
+      const scale = 0.2;
+
+      // 设置keyframes
+      const style = document.createElement("style");
+      style.setAttribute("type", "text/css");
+      document.head.appendChild(style);
+      let sheet = style.sheet;
+      sheet.insertRule(
+        `
+         @keyframes moveToIcon {
+             100% {
+                width: 1%;
+                top: ${
+                  floatLocation.top +
+                  floatLocation.height / 2 -
+                  (imgHeight * scale) / 10 / 2
+                }px;
+                margin-left:0;
+                margin-top:0;
+                left: ${floatLocation.left}px;
+            }
+        }
+          `
+      );
+      this.startPageAnimate = true;
+    },
+
+    // 点击开启
+    start() {
+      // 显示active
+      this.showActiveImg = true;
+      this.$emit("onOpenActivePage");
+
+      //  gif播放完执行动效  动效时间
+      //   setTimeout(() => {
+      // 展示
+      // this.$emit("onOpenActivePage");
+      //     // 1. 获取到sdk-icon的位置
+      //     const floatLocation = this.getSdkIconLocation();
+      //     // 2 获取到normalImg的高度(动效终点 startPage居中)
+      //     const normalImgHeight = this.$refs.normalImg.clientHeight;
+      //     const activeImgHeight =
+      //       this.$refs.activeImg && this.$refs.activeImg.clientHeight;
+      //     // 3.1 执行动效 -> 移动到sdk位置 (如果active存在, 取activImg 不存在, 取normalImg)
+      //     this.hbKeyframe(floatLocation, activeImgHeight || normalImgHeight);
+      //     // 3.2 不显示hb
+      //     setTimeout(() => {
+      //       this.$emit("onCloseActive");
+      //     }, this.animateTime);
+      //   }, this.delayTime);
+    },
+  },
+};
+</script>
+
+<style lang='less' scoped>
+.start_page {
+  width: 600 / @rem;
+  height: 824 / @rem;
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  margin-top: -412 / @rem;
+  margin-left: -300 / @rem;
+  transition: all 1s;
+
+  .img_box {
+    width: 100%;
+
+    .icon-baseline-close-px {
+      margin-right: 10 / @rem;
+      position: absolute;
+      top: 10 / @rem;
+      right: 10 / @rem;
+      font-size: 50 / @rem;
+      color: #fff;
+      cursor: pointer;
+      z-index: 3;
+    }
+
+    img {
+      width: 100%;
+      position: absolute;
+      left: 0;
+
+      &.active_img {
+        // transform: rotateY(180deg);
+      }
+    }
+  }
+}
+
+.start_page_animate {
+  animation: moveToIcon 3s 1 forwards;
+
+  .img_box {
+    .icon-baseline-close-px {
+      display: none;
+    }
+  }
+}
+
+// 正常横屏样式
+@media all and (orientation: landscape),
+  /** 伪竖屏*/all and (orientation: portrait) and (min-width: 600px) and (min-height: 800px) {
+  .start_page {
+    width: 250 / @rem;
+    height: 350 / @rem;
+    margin-top: -175 / @rem;
+    margin-left: -125.5 / @rem;
+
+    .img_box {
+      .icon-baseline-close-px {
+        margin-right: 5 / @rem;
+        top: 5 / @rem;
+        right: 5 / @rem;
+        font-size: 25 / @rem;
+      }
+    }
+  }
+}
+
+// ipad 横屏
+@media /** 伪竖屏*/all and (orientation: portrait) and (min-width: 600px) and (min-height: 800px),
+  /**ipad伪横屏 */ all and (orientation: landscape) and (min-width: 800px) and (min-height: 600px),
+  all and (min-device-aspect-ratio: 3/4) and (max-device-aspect-ratio: 4/3),
+  all and (device-aspect-ratio: 4/3) {
+  .start_page {
+    width: 300 / @rem;
+    height: 425 / @rem;
+    margin-top: -212.5 / @rem;
+    margin-left: -150 / @rem;
+
+    .img_box {
+      .icon-baseline-close-px {
+        margin-right: 5 / @rem;
+        top: 5 / @rem;
+        right: 5 / @rem;
+        font-size: 18 / @rem;
+      }
+    }
+  }
+}
+</style>

+ 136 - 0
src/components/DropdownList/DropdownList.vue

@@ -0,0 +1,136 @@
+<template>
+  <div class="dropdown_list">
+    <div v-show="showList" class="bg" @click="onControl(false)"></div>
+    <div class="show_value text_overflow" @click="onControl(!showList)">
+      {{ currentItem.name }}
+    </div>
+    <ul v-show="showList" v-if="listData.length">
+      <li
+        @click="onSelectChange(item)"
+        v-for="(item, index) in listData"
+        :key="index"
+        :class="{ active: currentItem.name == item.name }"
+        class="text_overflow"
+      >
+        {{ item.name }}
+      </li>
+    </ul>
+    <ul v-show="showList" v-else @click="onControl(!showList)">
+      <li>暂无</li>
+    </ul>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "DropdownList",
+  components: {},
+  data() {
+    return {
+      showList: false, // 是否展示下拉列表
+    };
+  },
+  props: {
+    // 列表
+    listData: {
+      type: Array,
+      default: function () {
+        return [];
+      },
+    },
+    // 当前选中的值
+    currentItem: {
+      type: Object,
+      default: function () {
+        return {};
+      },
+    },
+  },
+  computed: {},
+  watch: {},
+  created() {},
+  mounted() {},
+  methods: {
+    // 搜索框内容发生改变的时候执行的函数
+    onSelectChange(options) {
+      this.$emit("onSelectChange", options);
+      this.onControl(false);
+    },
+
+    // 关闭显示下拉列表
+    onControl(showList) {
+      this.showList = showList;
+    },
+  },
+};
+</script>
+
+<style lang="less">
+.dropdown_list {
+  width: 100%;
+  position: relative;
+  text-align: center;
+  cursor: pointer;
+
+  .bg {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+  }
+
+  .show_value {
+    height: 70 / @rem;
+    line-height: 68 / @rem;
+    border: 2px solid #e2e2e2;
+    border-radius: 8 / @rem;
+    box-sizing: border-box;
+  }
+
+  ul {
+    width: 100%;
+    max-height: 350 / @rem;
+    line-height: 70 / @rem;
+    position: absolute;
+    top: 70 / @rem;
+    background-color: #fff;
+    border: 2px solid #e6e6e6;
+    border-bottom: 0;
+    border-radius: 8 / @rem;
+    box-sizing: border-box;
+    overflow-y: auto;
+
+    li {
+      border-bottom: 1 / @rem solid #e6e6e6;
+
+      &:last-child {
+        border-bottom: 0;
+      }
+
+      &.active {
+        background-color: #e6e6e6;
+      }
+    }
+  }
+}
+
+// 正常横屏样式
+@media all and (orientation: landscape),
+  /** 伪竖屏*/only screen and (min-device-width: 768px) and (min-device-height: 768px) and (orientation: portrait) {
+  .dropdown_list {
+    .show_value {
+      height: 35 / @rem;
+      line-height: 34 / @rem;
+      border-radius: 4 / @rem;
+    }
+
+    ul {
+      max-height: 230 / @rem;
+      line-height: 35 / @rem;
+      top: 35 / @rem;
+      border-radius: 4 / @rem;
+    }
+  }
+}
+</style>

+ 8 - 1
src/components/Menu/MenuService.vue

@@ -17,7 +17,7 @@
 </template>
 
 <script>
-import { mapState } from "vuex";
+import { mapActions, mapState } from "vuex";
 export default {
   name: "MenuService",
   components: {},
@@ -40,9 +40,12 @@ export default {
     this.onResize();
   },
   methods: {
+    ...mapActions(["showServiceInlineAction"]),
+
     // 返回
     back() {
       this.showInnerControl("");
+      this.showServiceInlineAction(false);
     },
 
     // 显示哪个组件(工具函数)
@@ -98,6 +101,9 @@ export default {
 <style lang='less' scoped>
 .menu_service {
   display: flex;
+  position: fixed;
+  z-index: 10;
+  background-color: #f5f5f5;
 
   i {
     float: right;
@@ -110,6 +116,7 @@ export default {
   }
 
   .icon-baseline-close-px {
+    z-index: 11;
     margin-right: 10 / @rem;
     font-size: 50 / @rem;
   }

+ 1 - 1
src/config.js

@@ -1,7 +1,7 @@
 import wechatImg from "./assets/image/jh.jpg"; // 公众号二维码
 import logoIcon from "./assets/image/logoIcon.png"; // logo icon
 import floatImg from "./assets/image/floatImg.png"; //  悬浮缩略图
-import icon from "./assets/image/rxzr.png"; // 图标
+import icon from "./assets/image/wcfml.png"; // 图标
 import wechatImgTips from "./assets/image/wxtips.jpg"; // 微信公众号福利
 
 /* 所有配置 */

+ 16 - 4
src/router/index.js

@@ -11,9 +11,9 @@ const router = new Router({
             name: 'Home',
             component: () =>
                 import ('../views/Home/Home'),
-            // meta: {
-            //     isLogin: true
-            // }
+            meta: {
+                isLogin: true
+            }
         },
         // 登录
         {
@@ -65,7 +65,19 @@ const router = new Router({
             // meta: {
             //     isLogin: true
             // }
-        }
+        },
+
+        // 红包活动
+        {
+            path: '/activity_hb',
+            name: 'ActivityHb',
+            component: () =>
+                import ("@/views/Active/Hb/Hb"),
+            // meta: {
+            //     isLogin: true
+            // }
+        },
+
     ]
 });
 // 全局前置守卫

+ 76 - 0
src/store/index.js

@@ -1,6 +1,9 @@
 import Vue from 'vue'
 import 'es6-promise'
 import Vuex from 'vuex'
+import hb from '@/store/modules/hb'
+import api from "@/api/index.js"
+
 Vue.use(Vuex)
 export default new Vuex.Store({
     state: {
@@ -22,6 +25,33 @@ export default new Vuex.Store({
          * 忘川伏魔录: wcfml
          */
         platform: "",
+        showServiceInline: false, // 是否显示联系在线客服
+        couponList: [], // 用户 优惠券列表
+        gameRoleInfo: null, // 游戏内角色信息 
+        // 所有角色列表
+        gameRoleList: [],
+        // gameRoleList: [{
+        //     server_id: 880416,
+        //     server_name: "惊鸿专服",
+        //     role_id: 416,
+        //     role_name: "麻辣火锅生煎包",
+        //     name: '惊鸿专服 麻辣火锅生煎包'
+        // }, {
+        //     server_id: 880416,
+        //     server_name: "惊鸿专服",
+        //     role_id: 4160,
+        //     role_name: "小树小树向前冲",
+        //     name: '惊鸿专服 小树小树向前冲'
+        // }, {
+        //     server_id: 880416,
+        //     server_name: "惊鸿专服",
+        //     role_id: 4161,
+        //     role_name: "小树最棒啦",
+        //     name: '惊鸿专服 小树最棒啦'
+        // }],
+    },
+    modules: {
+        hb
     },
     getters: {
         // 获取当前支付平台的中文名
@@ -76,6 +106,36 @@ export default new Vuex.Store({
         platformAction(context, payload) {
             context.commit("platformMutation", payload)
         },
+        // 是否显示在线客服
+        showServiceInlineAction(context, payload) {
+            context.commit("showServiceInlineMutation", payload)
+        },
+        // 用户红包列表
+        couponListAction(context, payload) {
+            const { server_id, role_id, appid } = payload;
+            api.hbCouponList({
+                    app_id: appid,
+                    server_id,
+                    role_id,
+                })
+                .then((res) => {
+                    payload.checkCode(res);
+                    const { code, data, msg } = res.data;
+                    // 错误
+                    if (code) {
+                        return;
+                    }
+                    context.commit("couponListMutation", data)
+                });
+        },
+        // 游戏内角色信息
+        gameRoleInfoAction(context, payload) {
+            context.commit("gameRoleInfoMutation", payload)
+        },
+        // 游戏内所有角色列表
+        gameRoleListAction(context, payload) {
+            context.commit("gameRoleListMutation", payload)
+        },
     },
     mutations: {
         // 页面配置
@@ -106,5 +166,21 @@ export default new Vuex.Store({
         platformMutation(state, payload) {
             state.platform = payload
         },
+        // 是否显示在线客服
+        showServiceInlineMutation(state, payload) {
+            state.showServiceInline = payload
+        },
+        // 用户折扣列表
+        couponListMutation(state, payload) {
+            state.couponList = payload
+        },
+        // 游戏内角色信息
+        gameRoleInfoMutation(state, payload) {
+            state.gameRoleInfo = payload
+        },
+        // 游戏内角色所有角色列表
+        gameRoleListMutation(state, payload) {
+            state.gameRoleList = payload
+        },
     }
 })

+ 168 - 0
src/store/modules/hb.js

@@ -0,0 +1,168 @@
+import api from "@/api/index.js"
+import utils from '@/utils/utils'
+const hb = {
+    state: () => ({
+        userHbInfo: null, // 红包活动, 游戏角色信息
+        taskInfo: [{
+                amount: 500,
+                end_time: "2021-08-20 20:02:00",
+                reward_name: "5元现金",
+                task_id: 43,
+                task_name: "完成创建角色",
+                task_status: "completed",
+            }, {
+                amount: 500,
+                end_time: "2021-08-20 20:02:00",
+                reward_name: "5元现金",
+                task_id: 44,
+                task_name: "达到30级",
+                task_status: "uncomplete",
+            },
+            {
+                amount: 1000,
+                end_time: "2021-08-20 20:02:00",
+                reward_name: "10元现金",
+                task_id: 45,
+                task_name: "达到50级",
+                task_status: "uncomplete",
+            },
+            {
+                amount: 1000,
+                end_time: "2021-08-21 20:02:00",
+                reward_name: "10元现金",
+                task_id: 46,
+                task_name: "达到70级",
+                task_status: "uncomplete",
+            }
+        ], // 红包活动 任务列表
+        myAwardList: [], // 提现失败 获取更多 -> 抽奖 抽中的奖品
+    }),
+    actions: {
+        // 红包活动, 游戏角色信息
+        userHbInfoAction(context, payload) {
+            const userHbInfo = {
+                amount: 500,
+                red_point: 0,
+                role_id: "1872_380003",
+                role_level: "0",
+                role_name: "冷月殇璃",
+                server_id: "380003",
+                server_name: "圣者3服",
+                start_time: "2021-08-10 20:02:00",
+            }
+            context.commit("userHbInfoMutation", userHbInfo)
+            return
+            const { server_id, role_id } = payload.arg;
+            const { appid } = payload
+            api.hbInfo({ server_id, role_id, app_id: appid }).then((res) => {
+                payload.checkCode(res, true)
+                const { data, code, msg } = res.data;
+                // 错误
+                if (code) {
+                    return;
+                }
+                // 成功
+                data.amount = data.amount / 100;
+                data.role_level = utils.getRoleLevel(data.role_level);
+
+                context.commit("userHbInfoMutation", data)
+            });
+        },
+        // 红包活动 任务列表
+        taskInfoAction(context, payload) {
+            const taskInfo = [{
+                    amount: 500,
+                    end_time: "2021-08-20 20:02:00",
+                    reward_name: "5元现金",
+                    task_id: 43,
+                    task_name: "完成创建角色",
+                    task_status: "completed",
+                }, {
+                    amount: 500,
+                    end_time: "2021-08-20 20:02:00",
+                    reward_name: "5元现金",
+                    task_id: 44,
+                    task_name: "达到30级",
+                    task_status: "uncomplete",
+                },
+                {
+                    amount: 1000,
+                    end_time: "2021-08-20 20:02:00",
+                    reward_name: "10元现金",
+                    task_id: 45,
+                    task_name: "达到50级",
+                    task_status: "uncomplete",
+                },
+                {
+                    amount: 1000,
+                    end_time: "2021-08-21 20:02:00",
+                    reward_name: "10元现金",
+                    task_id: 46,
+                    task_name: "达到70级",
+                    task_status: "uncomplete",
+                }
+            ];
+            context.commit("taskInfoMutation", taskInfo)
+            return
+
+            const { server_id, role_id } = payload.arg;
+            const { appid } = payload
+            api.hbTasks({ server_id, role_id, app_id: appid }).then((res) => {
+                payload.checkCode(res, true);
+                const { data, code } = res.data;
+                // 错误
+                if (code) {
+                    return;
+                }
+
+                // 如果用户列表为空 不显示入口
+                if (!data.length) {
+                    context.commit('showHbStartPageMutation', false)
+                    context.commit('showHbTaskMutation', false)
+                    return
+                }
+
+                // 查询第一个 去完成 用来显示小手
+                let isFirstCompleteable = false;
+                data.map(ele => {
+                    if (isFirstCompleteable) {
+                        return
+                    }
+
+                    // 第一个去完成 并且 任务时间未到期
+                    const endTime = new Date(ele.end_time.replace(/\-/g, "/")).getTime()
+                    const nowTime = new Date().getTime()
+                    if (ele.task_status === 'uncomplete' && endTime > nowTime) {
+                        ele.is_first = true;
+                        isFirstCompleteable = true
+                        return
+                    }
+                })
+
+                // 成功
+                context.commit("taskInfoMutation", data)
+            });
+        },
+        // 提现失败 获取更多 -> 抽奖 抽中的奖品
+        myAwardListAction(context, payload) {
+            context.commit("myAwardListMutation", payload)
+        }
+    },
+    mutations: {
+        // 红包活动, 游戏角色信息
+        userHbInfoMutation(state, payload) {
+            state.userHbInfo = payload
+        },
+        // 红包活动 任务列表
+        taskInfoMutation(state, payload) {
+            state.taskInfo = payload
+        },
+        // 提现失败 获取更多 -> 抽奖 抽中的奖品
+        myAwardListMutation(state, payload) {
+            state.myAwardList = payload
+        },
+    }
+
+}
+
+export default hb

+ 49 - 2
src/utils/utils.js

@@ -315,6 +315,18 @@ const utils = {
         }
     },
 
+    /**
+     * 判断是否pc
+     * @returns 是否pc端
+     */
+    IsPC() {
+        if (!navigator.userAgent.match(/AppleWebKit.*Mobile.*/)) {
+            // pc
+            return true
+        }
+        return false
+    },
+
     // 销毁数据(bookName)
     destoryStorage(bookName) {
         // 忘川伏魔录特制版
@@ -357,12 +369,47 @@ const utils = {
 
     // 联系qq客服
     onQQService() {
-        const { kfQQ } = _this.$CONFIG;
+        const { kfQQ } = $CONFIG;
+        const isPc = this.IsPC()
         const urlMobil = `mqqwpa://im/chat?chat_type=wpa&uin=${kfQQ}&version=1&src_type=web&web_src=http:://wpa.b.qq.com`;
         const urlPc = `http://wpa.qq.com/msgrd?v=3&uin=${kfQQ}&site=qq&menu=yes`;
         const url = isPc ? urlPc : urlMobil;
         window.open(url, "_brank");
-    }
+    },
 
+    // wcfml 游戏内登录工具函数
+    wcfmlLoginUtils(data) {
+        // 记住密码 todo
+        const savedata = decodeURIComponent(this.getQueryString("data"));
+        // 跳转对应登录链接
+        const query = this.queryStringUtil({
+            uid: data.uid,
+            data: encodeURIComponent(savedata),
+        });
+        window.location.href = `${$CONFIG.wcfmlLoginUrl}?${query}`;
+    },
+
+    // 倒计时
+    countDown(startTime, endTime) {
+        // 两个日期对象,相差的秒数
+        let interval = endTime - startTime;
+        interval /= 1000
+            // 求 相差的天数/小时数/分钟数/秒数
+        let hour, minute, second;
+
+        hour = Math.floor(interval / 60 / 60);
+        minute = Math.floor(interval / 60 - hour * 60);
+        second = Math.floor(interval - hour * 60 * 60 - minute * 60);
+
+        hour = hour < 10 ? `0${hour}` : hour
+        minute = minute < 10 ? `0${minute}` : minute
+        second = second < 10 ? `0${second}` : second
+
+        return {
+            hour,
+            minute,
+            second
+        }
+    },
 };
 export default utils;

+ 82 - 0
src/views/Active/Hb/Hb.vue

@@ -0,0 +1,82 @@
+<template>
+  <!-- 红包 -> 开屏红包  玩游戏赚红包 -> 一天展示一次 -->
+  <div class="hb">
+    <!-- 开屏红包界面 未打开时图片 normalImg, 打开图片 activeImg active存在, 延迟关闭 -->
+    <StartPage
+      v-if="showActiveHbStart"
+      :delayTime="0"
+      :normalImg="normalImg"
+      @onCloseActive="onCloseActive"
+      @onOpenActivePage="onOpenActivePage"
+    />
+
+    <!-- 玩游戏 赚红包 -->
+    <HbTask
+      v-if="showActiveHbTask"
+      v-show="showActiveHbTask"
+      @onCloseActive="onCloseActive"
+    />
+  </div>
+</template>
+
+<script>
+import StartPage from "@/components/Active/StartPage/StartPage"; // 开屏
+import HbTask from "@/views/Active/Hb/HbTask/HbTask"; // 玩游戏, 赚红包
+import MenuService from "@/components/Menu/MenuService"; // 联系客服
+import { mapState } from "vuex";
+export default {
+  name: "HB",
+  components: {
+    StartPage,
+    HbTask,
+    MenuService,
+  },
+  data() {
+    return {
+      normalImg: require("@/assets/image/hb/normal_bg.gif"), // 默认展示图片 路径
+      //   activeImg: require("@/assets/image/hb/active_bg.png"), // 点击开启  路径
+      showMenuService: false, // 是否显示 玩游戏 赚红包
+      showActiveHbStart: true, // 显示首次打开的大红包
+      showActiveHbTask: false, // 是否显示具体任务
+    };
+  },
+  computed: {
+    ...mapState(["hb", "isLogin", "userInfo"]),
+  },
+  watch: {},
+  created() {},
+  mounted() {},
+  methods: {
+    // 开屏显示的图片的关闭按钮 关闭 进入游戏
+    onCloseActive() {
+      this.$utils.wcfmlLoginUtils(this.userInfo);
+    },
+
+    // 立即开启(开启活动)
+    onOpenActivePage() {
+      this.showActiveHbStart = false;
+      this.showActiveHbTask = true;
+    },
+  },
+};
+</script>
+
+<style lang='less' scoped>
+.hb {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  z-index: 5;
+  overflow-y: auto;
+  background-color: rgba(0, 0, 0, 0.6);
+}
+
+// 正常横屏样式
+@media all and (orientation: landscape),
+  /** 伪竖屏*/all and (orientation: portrait) and (min-width: 600px) and (min-height: 800px) {
+  .hb {
+  }
+}
+</style>

+ 627 - 0
src/views/Active/Hb/HbDialog/HbDialog.vue

@@ -0,0 +1,627 @@
+<template>
+  <!-- 红包弹窗 -->
+  <div class="hb_dialog">
+    <!-- 提现失败 时间 -->
+    <div class="dialog_box" v-if="dialog === 'cashFailedTime'">
+      <div class="title">提现失败</div>
+      <div class="content">活动结束后24小时内领取</div>
+      <div class="btn" @click="onCloseDialog">确定</div>
+    </div>
+
+    <!-- 提现失败 活动结束 金额不足 -->
+    <div class="dialog_box" v-if="dialog === 'cashFailedAmount'">
+      <div class="title">提现失败</div>
+      <div class="content">红包提现需满1000元才可领取</div>
+      <div class="btn" @click="onShowDraw">获取更多</div>
+    </div>
+
+    <!-- 提现成功 联系客服 -->
+    <div class="dialog_box" v-if="dialog === 'cashSuccess'">
+      <div class="title">提现成功</div>
+      <div class="content">请联系客服进行提现</div>
+      <div class="btn" @click="onService">确定</div>
+    </div>
+
+    <!-- 提现成功 测试 -->
+    <!-- <div class="dialog_box dialog_title_none" v-if="dialog === 'cashSuccess'">
+      <div class="title"></div>
+      <div class="content">提现成功</div>
+      <div class="btn" @click="onCloseDialog">确定</div>
+    </div> -->
+
+    <!-- 领取红包 领取成功 -->
+    <div
+      v-if="dialog === 'receiveSuccess'"
+      class="dialog_box dialog_title_none receive_success"
+    >
+      <div class="title"></div>
+      <div class="content">领取成功</div>
+      <div class="btn" @click="onCloseDialog">确定</div>
+    </div>
+
+    <!-- 提现失败 获取更多-> 十二宫格转盘 -->
+    <div class="luck_dialog" v-if="dialog === 'luckDraw'">
+      <LuckDraw :awardList="awardList" v-on="$listeners" />
+      <div class="close" @click="onCloseDialog"></div>
+    </div>
+
+    <!-- 提现失败 活动过期 -->
+    <div class="dialog_box" v-if="dialog === 'cashFailedTimeOut'">
+      <div class="title">提现失败</div>
+      <div class="content">活动已结束</div>
+      <div class="btn" @click="onCloseDialog">确定</div>
+    </div>
+
+    <!-- 抽奖成功弹窗 -->
+    <div class="award_small_box" v-if="dialog === 'luckDrawAward'">
+      <div class="award_small">
+        <div class="title">恭喜你</div>
+        <div class="award_name">获得</div>
+        <div class="award_img">
+          <img :src="myAwardList[0].img" alt="" />
+        </div>
+        <!-- 单抽 -->
+        <div class="to_bag" @click="toUse">去领取</div>
+
+        <!-- 单抽才有关闭 -->
+        <div class="close" @click="onCloseDialog"></div>
+      </div>
+    </div>
+
+    <!-- 绑定角色 -->
+    <div class="bind_role" v-if="dialog === 'bindRole'">
+      <div class="bind_role_content">
+        <ActiveTitle active="hb" acitveTitle="绑定角色" />
+        <!-- 选择角色 -->
+        <div class="select_role_box clear">
+          <!-- <div class="left">请选择角色</div> -->
+          <div class="right">
+            <DropdownList
+              :listData="gameRoleList"
+              :currentItem="currentRoleInfo"
+              @onSelectChange="onSelectChange"
+            />
+          </div>
+
+          <div>当前选择</div>
+          <div class="server_name">
+            服务器:{{
+              (currentRoleInfo && currentRoleInfo.server_name) || "暂无"
+            }}
+          </div>
+          <div class="role_name">
+            角色:{{ (currentRoleInfo && currentRoleInfo.role_name) || "暂无" }}
+          </div>
+        </div>
+        <!-- 确认 取消 -->
+        <div class="btn_box clear">
+          <div class="confirm" @click="onSelectRole">确定</div>
+          <div class="cancel" @click="onCloseDialog">取消</div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import LuckDraw from "@/components/Active/LuckDraw/LuckDraw"; // 大转盘
+import ActiveTitle from "@/components/Active/ActiveTitle/ActiveTitle"; // 标题
+import DropdownList from "@/components/DropdownList/DropdownList"; // 下拉列表
+import { mapState } from "vuex";
+export default {
+  name: "HbDialog",
+  components: {
+    LuckDraw,
+    ActiveTitle,
+    DropdownList,
+  },
+  data() {
+    return {
+      currentRoleInfo: {
+        name: "请选择角色",
+      },
+    };
+  },
+  props: {
+    // 弹窗组件
+    dialog: {
+      type: String,
+      default: "",
+    },
+    // 奖品列表
+    awardList: {
+      type: Array,
+      default: function () {
+        return [];
+      },
+    },
+  },
+  computed: {
+    ...mapState(["gameRoleList"]),
+    // 抽中的奖品
+    myAwardList() {
+      return this.$store.state.hb.myAwardList;
+    },
+    // 用户活动信息
+    userHbInfo() {
+      return this.$store.state.hb.userHbInfo;
+    },
+    // 当前选择的用户角色
+    gameRoleInfo() {
+      return this.$store.state.gameRoleInfo;
+    },
+  },
+  watch: {
+    // 抽中的奖品
+    myAwardList: {
+      handler: function () {},
+      deep: true,
+      immediate: true,
+    },
+    // 用户活动信息
+    userHbInfo: {
+      handler: function () {},
+      deep: true,
+      immediate: true,
+    },
+    // 所有角色列表
+    gameRoleList: {
+      handler: function () {},
+      deep: true,
+      immediate: true,
+    },
+    // 当前选择的用户角色
+    gameRoleInfo: {
+      handler: function () {},
+      deep: true,
+      immediate: true,
+    },
+  },
+  created() {},
+  mounted() {
+    const currentItem = {
+      name: "请选择角色",
+    };
+    this.currentRoleInfo = this.gameRoleInfo || currentItem;
+  },
+  methods: {
+    // 确定 关闭当前弹窗
+    onCloseDialog() {
+      this.$emit("onShowDialog", "");
+    },
+
+    // 显示折扣转盘
+    onShowDraw() {
+      this.$emit("onShowDialog", "luckDraw");
+    },
+
+    // 联系客服
+    onService() {
+      this.$emit("onShowDialog", "");
+      this.$emit("onService");
+    },
+
+    // 去领取
+    toUse() {
+      this.$emit("onUseCash");
+    },
+
+    // 改变选择的角色
+    onSelectChange(options) {
+      this.currentRoleInfo = options;
+    },
+
+    // 选择角色
+    onSelectRole() {
+      if (!this.currentRoleInfo.server_id) {
+        this.$toast.text("请选择角色!");
+        return;
+      }
+      this.$emit("onSelectRole", this.currentRoleInfo);
+    },
+  },
+};
+</script>
+
+<style lang='less'>
+.hb_dialog {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  z-index: 6;
+  background-color: rgba(0, 0, 0, 0.6);
+  overflow-y: auto;
+
+  .dialog_box {
+    width: 500 / @rem;
+    height: 350 / @rem;
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    margin-top: -175 / @rem;
+    margin-left: -250 / @rem;
+    background-color: #fff;
+    border-radius: 20px;
+
+    .title {
+      height: 60 / @rem;
+      line-height: 60 / @rem;
+      margin: 30 / @rem auto;
+    }
+
+    .content {
+      width: 80%;
+      line-height: 60 / @rem;
+      margin: 0 auto;
+    }
+
+    .btn {
+      width: 180 / @rem;
+      height: 70 / @rem;
+      line-height: 70 / @rem;
+      margin: 50 / @rem auto 0;
+      border-radius: 35 / @rem;
+      background-image: linear-gradient(#fbd8c0, #ff092e);
+      color: #fff;
+      cursor: pointer;
+    }
+  }
+
+  .dialog_title_none {
+    .title {
+      margin: 20 / @rem auto;
+    }
+  }
+
+  // 大转盘
+  .luck_dialog {
+    width: 100%;
+    height: 100%;
+    position: relative;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    overflow-y: auto;
+
+    .luck_draw {
+      width: 620 / @rem;
+      height: 620 / @rem;
+      position: relative;
+      top: 50%;
+      left: 50%;
+      margin-left: -310 / @rem;
+      margin-top: -370 / @rem;
+
+      .award_list .award_center img {
+        width: 80%;
+        margin-top: 18%;
+      }
+    }
+
+    .close {
+      width: 70 / @rem;
+      height: 70 / @rem;
+      position: absolute;
+      top: 950 / @rem;
+      left: 50%;
+      margin-left: -35 / @rem;
+      background: url("~@/assets/image/hb/luckDraw/close.png") 100%;
+      background-size: cover;
+    }
+  }
+
+  // 大转盘奖品
+  .award_small_box {
+    color: #f4434e;
+
+    .award_small {
+      width: 510 / @rem;
+      height: 630 / @rem;
+      margin: -380 / @rem -255 / @rem 0;
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      background: url("~@/assets/image/hb/luckDraw/awardBgSmall.png") 100%;
+      background-size: cover;
+
+      .title {
+        margin-top: 80 / @rem;
+        height: 70 / @rem;
+        line-height: 70 / @rem;
+        font-size: 34 / @rem;
+      }
+      .award_name {
+        margin-top: 20 / @rem;
+      }
+      .award_img {
+        width: 40%;
+        margin: 40 / @rem auto;
+        img {
+          width: 100%;
+        }
+      }
+
+      .to_bag {
+        width: 320 / @rem;
+        height: 70 / @rem;
+        line-height: 70 / @rem;
+        color: #fff;
+        margin: 0 auto;
+        background-image: linear-gradient(#fbd8c0, #ff092e);
+        border-radius: 35 / @rem;
+        cursor: pointer;
+      }
+
+      .next_box {
+        .to_bag {
+          margin-top: -10 / @rem;
+        }
+      }
+
+      .jump {
+        float: right;
+        margin-top: 10 / @rem;
+        margin-right: 30 / @rem;
+        font-size: 28 / @rem;
+      }
+    }
+
+    .close {
+      width: 70 / @rem;
+      height: 70 / @rem;
+      margin: 150 / @rem auto;
+      background: url("~@/assets/image/hb/luckDraw/close.png") 100%;
+      background-size: cover;
+    }
+  }
+
+  // 绑定角色
+  .bind_role {
+    .bind_role_content {
+      width: 500 / @rem;
+      height: 600 / @rem;
+      background-color: #fff;
+      border-radius: 5px;
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      margin-left: -250 / @rem;
+      margin-top: -300 / @rem;
+
+      // 标题
+      .active_title {
+        width: 200 / @rem;
+        margin: -40 / @rem auto 0;
+
+        .active_title_hb {
+          width: 100%;
+          height: 80 / @rem;
+          line-height: 80 / @rem;
+
+          &::before {
+            left: 200 / @rem;
+          }
+        }
+      }
+
+      // 选择角色
+      .select_role_box {
+        width: 90%;
+        line-height: 80 / @rem;
+        margin: 40 / @rem auto;
+
+        .dropdown_list {
+          .show_value,
+          ul {
+            border: 2px solid #ffcac6;
+          }
+
+          ul {
+            margin-top: -2px;
+
+            li {
+              &.active {
+                background-color: #ffcac6;
+              }
+            }
+          }
+        }
+      }
+
+      // 按钮
+      .btn_box {
+        height: 80 / @rem;
+        line-height: 80 / @rem;
+
+        .confirm,
+        .cancel {
+          width: 120 / @rem;
+          height: 60 / @rem;
+          line-height: 60 / @rem;
+          margin-left: 85 / @rem;
+          float: left;
+          border-radius: 30 / @rem;
+          color: #fff;
+          cursor: pointer;
+        }
+
+        .confirm {
+          background-image: linear-gradient(#fbd8c0, #ff092e);
+        }
+        .cancel {
+          background-image: linear-gradient(#dfdfdf, #b3b3b3);
+        }
+      }
+    }
+  }
+}
+
+// 正常横屏样式
+@media all and (orientation: landscape),
+  /** 伪竖屏*/all and (orientation: portrait) and (min-width: 600px) and (min-height: 800px) {
+  .hb_dialog {
+    .dialog_box {
+      width: 250 / @rem;
+      height: 175 / @rem;
+      margin-top: -87.5 / @rem;
+      margin-left: -125 / @rem;
+
+      .title {
+        height: 30 / @rem;
+        line-height: 30 / @rem;
+        margin: 15 / @rem auto;
+      }
+
+      .content {
+        width: 90%;
+        line-height: 30 / @rem;
+      }
+
+      .btn {
+        width: 90 / @rem;
+        height: 40 / @rem;
+        line-height: 40 / @rem;
+        margin: 25 / @rem auto 0;
+        border-radius: 20 / @rem;
+      }
+    }
+
+    .dialog_title_none {
+      .title {
+        margin: 10 / @rem auto;
+      }
+    }
+
+    .luck_dialog {
+      .luck_draw {
+        width: 300 / @rem;
+        height: 300 / @rem;
+        margin-left: -150 / @rem;
+        margin-top: -150 / @rem;
+      }
+
+      .close {
+        width: 35 / @rem;
+        height: 35 / @rem;
+        margin-left: 180 / @rem;
+        top: 195 / @rem;
+      }
+    }
+
+    // 大转盘奖品
+    .award_small_box {
+      .award_small {
+        width: 250 / @rem;
+        height: 306 / @rem;
+        margin: -190 / @rem -127.5 / @rem 0;
+
+        .title {
+          margin-top: 40 / @rem;
+          height: 35 / @rem;
+          line-height: 35 / @rem;
+          font-size: 17 / @rem;
+        }
+        .award_name {
+          margin-top: 10 / @rem;
+        }
+        .award_img {
+          margin: 20 / @rem auto;
+        }
+
+        .to_bag {
+          width: 160 / @rem;
+          height: 35 / @rem;
+          line-height: 35 / @rem;
+          border-radius: 17.5 / @rem;
+        }
+
+        .next_box {
+          .to_bag {
+            margin-top: -5 / @rem;
+          }
+        }
+
+        .jump {
+          margin-top: 5 / @rem;
+          margin-right: 15 / @rem;
+          font-size: 14 / @rem;
+        }
+      }
+
+      .close {
+        width: 35 / @rem;
+        height: 35 / @rem;
+        margin: 50 / @rem auto;
+      }
+    }
+
+    // 绑定角色
+    .bind_role {
+      .bind_role_content {
+        width: 250 / @rem;
+        height: 300 / @rem;
+        margin-left: -125 / @rem;
+        margin-top: -150 / @rem;
+
+        // 标题
+        .active_title {
+          width: 100 / @rem;
+          margin: -20 / @rem auto 0;
+
+          .active_title_hb {
+            height: 40 / @rem;
+            line-height: 40 / @rem;
+
+            &::before {
+              left: 100 / @rem;
+            }
+          }
+        }
+
+        // 选择角色
+        .select_role_box {
+          width: 90%;
+          line-height: 40 / @rem;
+          margin: 20 / @rem auto;
+        }
+
+        // 按钮
+        .btn_box {
+          height: 40 / @rem;
+          line-height: 40 / @rem;
+
+          .confirm,
+          .cancel {
+            width: 60 / @rem;
+            height: 30 / @rem;
+            line-height: 30 / @rem;
+            margin-left: 42.5 / @rem;
+            border-radius: 15 / @rem;
+          }
+
+        }
+      }
+    }
+  }
+}
+
+// ipad 横屏
+@media /** 伪竖屏*/all and (orientation: portrait) and (min-width: 600px) and (min-height: 800px),
+  /**ipad伪横屏 */ all and (orientation: landscape) and (min-width: 800px) and (min-height: 600px),
+  all and (min-device-aspect-ratio: 3/4) and (max-device-aspect-ratio: 4/3),
+  all and (device-aspect-ratio: 4/3) {
+  .hb_dialog {
+    .luck_dialog {
+      .close {
+        width: 35 / @rem;
+        height: 35 / @rem;
+        top: 520 / @rem;
+        left: 70 / @rem;
+      }
+    }
+  }
+}
+</style>

+ 555 - 0
src/views/Active/Hb/HbTask/HbTask.vue

@@ -0,0 +1,555 @@
+<template>
+  <!-- 玩游戏赚红包 -->
+  <div class="hb_task">
+    <div class="task_list_box">
+      <!-- 背景 -->
+      <i class="iconfont icon-baseline-close-px" @click="onCloseActive"></i>
+      <img src="@/assets/image/hb/hb_bg.png" alt="" class="hb_bg" />
+
+      <div class="task_box">
+        <!-- 活动内容 -->
+        <HbTaskList
+          :taskInfo="taskInfo"
+          :endTimeArr="endTimeArr"
+          :userHbInfo="userHbInfo"
+          @onGame="onGame"
+          @onService="onService"
+          @onReceive="onReceive"
+          @onCash="onCash"
+          @onShowDialog="onShowDialog"
+        />
+      </div>
+
+      <!-- 活动说明 -->
+      <div class="task_box tips">
+        <HbTaskTips @onService="onService" />
+      </div>
+    </div>
+
+    <!-- 活动弹窗 -->
+    <HbDialog
+      v-if="dialog"
+      :dialog="dialog"
+      :awardList="awardList"
+      v-on="$listeners"
+      @onShowDialog="onShowDialog"
+      @onService="onService"
+      @toDraw="toDraw"
+      @onUseCash="onUseCash"
+      @onSelectRole="onSelectRole"
+    />
+  </div>
+</template>
+
+<script>
+import HbTaskList from "@/views/Active/Hb/HbTask/HbTaskList/HbTaskList"; // 活动列表
+import HbTaskTips from "@/views/Active/Hb/HbTask/HbTaskTips/HbTaskTips"; // 活动说明
+import HbDialog from "@/views/Active/Hb/HbDialog/HbDialog"; // 活动弹窗
+import { mapActions, mapState } from "vuex";
+export default {
+  name: "HbTask",
+  components: {
+    HbTaskList,
+    HbTaskTips,
+    HbDialog,
+  },
+  inject: ["checkCode"],
+  data() {
+    return {
+      /**
+       * 弹窗组件
+       * cashFailedTime: 提现失败-> 活动结束后24小时内领取
+       * cashFailedAmount: 提现失败-> 红包提现需满1000元才可领取
+       * cashSuccess: 提现成功 -> 请联系客服进行提现
+       * receiveSuccess: 领取成功
+       * luckDraw: 大转盘
+       * cashFailedTimeOut: 活动过期
+       * luckDrawAward:抽奖成功弹窗
+       * bindRole: 绑定角色
+       */
+      dialog: "",
+      showMenuService: false, // 是否显示联系客服
+      endTimeArr: [], // 倒计时列表
+      countDownTimer: null, // 倒计时定时器
+      // 抽奖
+      awardList: [
+        {
+          id: 1,
+          name: "",
+          img: require("@/assets/image/hb/luckDraw/2.png"),
+        },
+        {
+          id: 2,
+          name: "",
+          img: require("@/assets/image/hb/luckDraw/4.png"),
+        },
+        {
+          id: 3,
+          name: "",
+          img: require("@/assets/image/hb/luckDraw/1.png"),
+        },
+        {
+          id: 4,
+          name: "",
+          img: require("@/assets/image/hb/luckDraw/3.png"),
+        },
+
+        {
+          id: 5,
+          name: "",
+          img: require("@/assets/image/hb/luckDraw/0.png"),
+        },
+        {
+          id: 6,
+          name: "",
+          img: require("@/assets/image/hb/luckDraw/1.png"),
+        },
+        {
+          id: 7,
+          name: "",
+          img: require("@/assets/image/hb/luckDraw/4.png"),
+        },
+        {
+          id: 8,
+          name: "",
+          img: require("@/assets/image/hb/luckDraw/1.png"),
+        },
+        {
+          id: 9,
+          name: "",
+          img: require("@/assets/image/hb/luckDraw/0.png"),
+        },
+        {
+          id: 10,
+          name: "",
+          img: require("@/assets/image/hb/luckDraw/3.png"),
+        },
+        {
+          id: 11,
+          name: "",
+          img: require("@/assets/image/hb/luckDraw/2.png"),
+        },
+        {
+          id: 12,
+          name: "",
+          img: require("@/assets/image/hb/luckDraw/1.png"),
+        },
+      ],
+      canCash: true, // 是否可领取优惠券
+    };
+  },
+  props: {},
+  computed: {
+    ...mapState(["gameRoleInfo", "appid", "hb", "couponList"]),
+    // 红包活动 用户 游戏角色信息
+    userHbInfo() {
+      return this.$store.state.hb.userHbInfo;
+    },
+    // 红包活动 任务列表
+    taskInfo() {
+      return this.$store.state.hb.taskInfo;
+    },
+  },
+  watch: {
+    // 红包活动 用户 游戏角色信息 监听
+    userHbInfo: {
+      handler(newValue) {
+        if (newValue && newValue.role_id) {
+          this.onShowDialog("");
+        }
+      },
+      deep: true,
+      immediate: true,
+    },
+    // 红包活动 任务列表 监听
+    taskInfo: {
+      handler(newValue) {
+        // 更新倒计时数组
+        const endTimeArr = this.onGetTime(newValue);
+        // 倒计时
+        this.countDown(endTimeArr);
+      },
+      deep: true,
+      immediate: true,
+    },
+    // 倒计时
+    endTimeArr: {
+      handler(newValue) {},
+      deep: true,
+      immediate: true,
+    },
+    // 折扣券列表
+    couponList: {
+      handler(newValue) {},
+      deep: true,
+      immediate: true,
+    },
+  },
+  created() {},
+  mounted() {
+    // 倒计时数组
+    const endTimeArr = this.onGetTime(this.taskInfo);
+    // 倒计时
+    this.countDown(endTimeArr);
+  },
+  beforeDestroy() {
+    clearTimeout(this.countDownTimer);
+  },
+  methods: {
+    ...mapActions([
+      "userHbInfoAction",
+      "taskInfoAction",
+      "myAwardListAction",
+      "couponListAction",
+      "showServiceInlineAction",
+      "gameRoleInfoAction",
+    ]),
+
+    // 弹窗控制
+    onShowDialog(dialog) {
+      this.dialog = dialog;
+    },
+
+    // 关闭当前
+    onCloseActive() {
+      this.$emit("onCloseActive");
+    },
+
+    // 联系客服
+    onService() {
+      //   this.showMenuService = true;
+      //   this.$utils.onQQService(this);
+      this.showServiceInlineAction(true);
+    },
+
+    // 联系客服
+    onShowControl(onShowControl) {
+      const { showMenuBox } = onShowControl;
+      this.showMenuService = !showMenuBox;
+    },
+
+    // 提现
+    onCash() {
+      const nowDate = new Date().getTime();
+      const { start_time } = this.hb.userHbInfo;
+      //   this.onShowDialog("cashFailedAmount");
+      //   return;
+
+      //  活动结束后 24小时内领取 活动七天
+      if (
+        nowDate <
+        new Date(start_time.replace(/\-/g, "/")).getTime() +
+          7 * 24 * 60 * 60 * 1000
+      ) {
+        this.onShowDialog("cashFailedTime");
+        return;
+      }
+
+      // 活动结束未满 1000 元
+      if (this.userHbInfo.amount < 1000) {
+        this.onShowDialog("cashFailedAmount");
+        return;
+      }
+
+      // 活动结束 展示活动结束
+      if (
+        nowDate <
+        new Date(start_time.replace(/\-/g, "/")).getTime() +
+          8 * 24 * 60 * 60 * 1000
+      ) {
+        this.onShowDialog("cashFailedTimeOut");
+        return;
+      }
+
+      // 活动结束 满 1000 元
+      this.onShowDialog("cashSuccess");
+    },
+
+    // 去完成
+    onGame() {
+      this.onCloseActive();
+    },
+
+    // 领取红包按钮
+    onReceive(task) {
+      // 请求接口 完成任务
+      const { appid } = this;
+      const { task_id } = task;
+      const { server_id, role_id } = this.gameRoleInfo;
+      this.$api
+        .hbCompleteTask({ app_id: appid, server_id, role_id, task_id })
+        .then((res) => {
+          // 完成任务之后, 请求任务列表数据 请求用户游戏角色信息数据
+          this.checkCode(res);
+          const { code, data, msg } = res.data;
+          // 错误
+          if (code) {
+            return;
+          }
+          // 成功
+          this.onShowDialog("receiveSuccess");
+          this.getUserHbInfo();
+          this.getHbTask();
+        });
+    },
+
+    // 获取hb活动 游戏内玩家信息
+    getUserHbInfo() {
+      this.userHbInfoAction({
+        arg: this.gameRoleInfo,
+        checkCode: this.checkCode,
+        appid: this.appid,
+      });
+    },
+
+    // 获取 红包 任务列表
+    getHbTask() {
+      this.taskInfoAction({
+        arg: this.gameRoleInfo,
+        checkCode: this.checkCode,
+        appid: this.appid,
+      });
+    },
+
+    // 获取任务倒计时
+    onGetTime(taskInfo) {
+      //  获取所有的任务结束时间 数组
+      const endTimeArr = [];
+      taskInfo.map((ele, index) => {
+        const endTime = new Date(ele.end_time.replace(/\-/g, "/")).getTime();
+        endTimeArr.push(endTime);
+      });
+      return endTimeArr;
+    },
+
+    // 倒计时
+    countDown(timeArr) {
+      const endTimeArr = [];
+      clearTimeout(this.countDownTimer);
+      this.countDownTimer = setTimeout(() => {
+        timeArr.map((ele, index) => {
+          // 1. 获取现在时间
+          const nowDate = new Date().getTime();
+          // 2.1 比较 如果活动结束 return
+          if (ele < nowDate) {
+            endTimeArr[index] = "00:00:00";
+            return;
+          }
+
+          // 2.2  活动没结束 倒计时
+          const timeDetail = this.$utils.countDown(nowDate, ele);
+          const { hour, minute, second } = timeDetail;
+          endTimeArr[index] = `${hour}:${minute}:${second}`;
+        });
+        this.endTimeArr = endTimeArr;
+        // 再次执行
+        this.countDown(timeArr);
+      }, 1000);
+    },
+
+    // 抽奖
+    toDraw() {
+      setTimeout(() => {
+        const awardList = [
+          {
+            id: 5,
+            name: "",
+            img: require("@/assets/image/hb/luckDraw/0.png"),
+          },
+        ];
+        this.myAwardListAction(awardList);
+      }, 2000);
+    },
+
+    // 领取折扣券
+    onUseCash() {
+      if (this.couponList.length) {
+        this.$toast.text("您已领取该优惠券");
+        // 关闭当前 打开鸿币
+        this.onShowDialog("");
+        // 打开鸿币
+        this.$emit("onShowControl", { showMenuPay: true });
+        return;
+      }
+      // 防抖
+      if (!this.canCash) {
+        return;
+      }
+      const { appid } = this;
+      const { server_id, role_id } = this.gameRoleInfo;
+      this.canCash = false;
+      // 领取折扣券接口
+      this.$api
+        .hbDrawCoupon({
+          app_id: appid,
+          server_id,
+          role_id,
+        })
+        .then((res) => {
+          this.canCash = true;
+          this.checkCode(res);
+          const { code, data, msg } = res.data;
+          // 错误
+          if (code) {
+            return;
+          }
+
+          // 关闭当前 打开鸿币
+          this.onShowDialog("");
+          // 打开鸿币
+          this.$emit("onShowControl", { showMenuPay: true });
+          // 更新优惠券列表
+          this.onGetCouponList();
+        });
+    },
+
+    // 获取优惠券列表
+    onGetCouponList() {
+      const { appid, checkCode } = this;
+      const { server_id, role_id } = this.gameRoleInfo;
+      this.couponListAction({ appid, server_id, role_id, checkCode });
+    },
+
+    // 确认绑定角色
+    onSelectRole(roleInfo) {
+      const { appid } = this;
+      // 更新当前角色
+      this.gameRoleInfoAction(roleInfo);
+      // 2. 更新任务列表 用户任务信息
+      this.userHbInfoAction({
+        arg: { ...roleInfo },
+        appid,
+        checkCode: this.checkCode,
+      });
+      this.taskInfoAction({ ...roleInfo, appid });
+      // 3. 活动的绑定按钮隐藏 展示角色信息 todo 在userHbInfo由内容之后更新
+    },
+  },
+};
+</script>
+
+<style lang='less' scoped>
+.hb_task {
+  .icon-baseline-close-px {
+    margin-right: 10 / @rem;
+    position: fixed;
+    top: 10 / @rem;
+    right: 10 / @rem;
+    font-size: 50 / @rem;
+    color: #fff;
+    cursor: pointer;
+  }
+
+  .task_list_box {
+    background-color: #df354f;
+    height: 100%;
+
+    img {
+      width: 100%;
+    }
+
+    .task_box {
+      width: 90%;
+      margin: 0 auto;
+      padding: 20 / @rem;
+      position: relative;
+      top: -50 / @rem;
+      z-index: 5;
+      border-radius: 20px;
+      background-color: #fff;
+
+      &.tips {
+        margin-top: 100 / @rem;
+      }
+    }
+  }
+
+  .menu_service {
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 6;
+  }
+}
+
+// 正常横屏样式
+@media all and (orientation: landscape),
+  /** 伪竖屏*/all and (orientation: portrait) and (min-width: 600px) and (min-height: 800px) {
+  .hb_task {
+    .icon-baseline-close-px {
+      margin-right: 5 / @rem;
+      top: 5 / @rem;
+      right: 5 / @rem;
+      font-size: 25 / @rem;
+    }
+
+    .task_list_box {
+      //   width: 52%;
+      margin: 0 auto;
+
+      img {
+        width: 100%;
+      }
+
+      .task_box {
+        width: 60%;
+        padding: 10 / @rem;
+        top: -75 / @rem;
+
+        &.tips {
+          margin-top: 50 / @rem;
+        }
+      }
+    }
+  }
+}
+
+// ipad 横屏
+@media /** 伪竖屏*/all and (orientation: portrait) and (min-width: 600px) and (min-height: 800px),
+  /**ipad伪横屏 */ all and (orientation: landscape) and (min-width: 800px) and (min-height: 600px),
+  all and (min-device-aspect-ratio: 3/4) and (max-device-aspect-ratio: 4/3),
+  all and (device-aspect-ratio: 4/3) {
+  .hb_task {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background-color: rgba(0, 0, 0, 0.6);
+    font-size: 12 / @rem;
+
+    .icon-baseline-close-px {
+      position: absolute;
+      top: 5 / @rem;
+      right: 5 / @rem;
+      font-size: 25 / @rem;
+    }
+
+    .task_list_box {
+      width: 400 / @rem;
+      height: 600 / @rem;
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      margin-top: -300 / @rem;
+      margin-left: -200 / @rem;
+      border-radius: 20px;
+      overflow-y: auto;
+
+      img {
+        width: 100%;
+      }
+
+      .task_box {
+        width: 90%;
+        padding: 10 / @rem;
+        top: -25 / @rem;
+
+        &.tips {
+          margin-top: 50 / @rem;
+        }
+      }
+    }
+  }
+}
+</style>

+ 484 - 0
src/views/Active/Hb/HbTask/HbTaskList/HbTaskList.vue

@@ -0,0 +1,484 @@
+<template>
+  <!-- 活动列表 ui -->
+  <div class="hb_task_list">
+    <!-- 标题 -->
+    <ActiveTitle active="hb" acitveTitle="玩游戏 赚红包" />
+
+    <!-- 信息 start -->
+    <div class="top_info clear">
+      <div class="task_service" @click="onService">联系客服</div>
+
+      <!-- 玩家信息 换绑按钮 -->
+      <div v-if="userHbInfo && userHbInfo.role_id" class="center_box">
+        <div class="user_info">
+          <div class="role text_overflow">
+            {{ userHbInfo.server_name }}
+          </div>
+          <div class="role text_overflow">
+            {{ userHbInfo.role_name }}
+          </div>
+          <div class="level">{{ userHbInfo.role_level }}</div>
+        </div>
+
+        <div class="exchange" @click="onBindRole">
+          <i class="iconfont icon-zhuanhuan"></i>
+        </div>
+      </div>
+
+      <!-- 绑定按钮 -->
+      <div v-else class="bind" @click="onBindRole">绑定角色</div>
+
+      <!-- 余额 -->
+      <div class="money">
+        <span>余额: </span>
+        <span class="amount">
+          {{ (userHbInfo && userHbInfo.amount) || 0 }}元
+        </span>
+      </div>
+      <!-- 提现 -->
+      <div class="cash" @click="onCash">提现</div>
+    </div>
+    <!-- 信息 end -->
+
+    <!-- 活动列表 start -->
+    <ul v-if="taskInfo && taskInfo.length && endTimeArr.length" class="list">
+      <li v-for="(item, index) in taskInfo" :key="index" class="clear">
+        <div class="task_content">
+          <div class="text_overflow">
+            任务:<span>{{ item.task_name }}</span>
+          </div>
+          <div>
+            奖励:<span>{{ item.reward_name }}</span>
+          </div>
+        </div>
+        <div class="time">{{ endTimeArr[index] }}</div>
+        <!-- 去完成 -->
+        <div
+          v-if="
+            item.task_status == 'uncomplete' && endTimeArr[index] !== '00:00:00'
+          "
+          class="btn"
+          @click="onGame(item)"
+        >
+          <span>去完成</span>
+          <img
+            v-if="item.is_first && firstPage"
+            class="to_complete"
+            src="@/assets/image/hb/point.gif"
+            alt=""
+          />
+        </div>
+        <!-- 领取 -->
+        <div
+          v-else-if="
+            item.task_status == 'completeable' &&
+            endTimeArr[index] !== '00:00:00'
+          "
+          class="btn"
+          @click="onReceive(item)"
+        >
+          领取
+        </div>
+        <!-- 已领取 -->
+        <div v-else-if="item.task_status == 'completed'" class="btn already">
+          已领取
+        </div>
+        <!-- 条件不足 -->
+        <div v-else class="btn already">未达标</div>
+      </li>
+    </ul>
+    <!-- 活动列表 end -->
+
+    <!-- test -->
+    <!-- <ul class="list">
+      <li class="clear">
+        <div class="task_content">
+          <div>任务:<span>test </span></div>
+          <div>奖励:<span>5元现金</span></div>
+        </div>
+        <div class="time">168:16:16</div>
+        <div class="btn" @click="onGame()">
+          <span>去完成</span>
+          <img class="to_complete" src="@/assets/image/hb/point.gif" alt="" />
+        </div>
+      </li>
+    </ul> -->
+    <!-- test -->
+  </div>
+</template>
+
+<script>
+import ActiveTitle from "@/components/Active/ActiveTitle/ActiveTitle"; // 活动标题
+export default {
+  name: "HbTaskList",
+  components: {
+    ActiveTitle,
+  },
+  data() {
+    return {};
+  },
+  props: {
+    // 红包活动 用户 游戏角色信息
+    userHbInfo: {
+      type: Object,
+      default: function () {
+        return {};
+      },
+    },
+    // 红包活动 任务列表
+    taskInfo: {
+      type: Array,
+      default: function () {
+        return [];
+      },
+    },
+    // 倒计时
+    endTimeArr: {
+      type: Array,
+      default: function () {
+        return [];
+      },
+    },
+    // 每日首次展示
+    firstPage: {
+      type: [Boolean, String],
+      default: false,
+    },
+  },
+  computed: {},
+  watch: {},
+  created() {},
+  mounted() {},
+  methods: {
+    // 联系客服
+    onService() {
+      this.$emit("onService");
+    },
+
+    // 提现
+    onCash() {
+      this.$emit("onCash");
+    },
+
+    // 领取
+    onReceive(task) {
+      this.$emit("onReceive", task);
+    },
+
+    // 去完成
+    onGame() {
+      this.$emit("onGame");
+    },
+
+    // 绑定角色
+    onBindRole() {
+      this.$emit("onShowDialog", "bindRole");
+    },
+  },
+};
+</script>
+
+<style lang='less' scoped>
+.hb_task_list {
+  // 活动标题
+  .active_title {
+    margin-bottom: 20 / @rem;
+  }
+
+  .top_info {
+    position: relative;
+
+    .task_service {
+      width: 170 / @rem;
+      height: 80 / @rem;
+      line-height: 80 / @rem;
+      margin-right: 10 / @rem;
+      border-radius: 40 / @rem;
+      float: left;
+      color: #fff;
+      background-image: linear-gradient(#fbd8c0, #ff092e);
+      //   background-image: linear-gradient(#fed5a4, #ff9d25);
+      cursor: pointer;
+    }
+
+    .user_info,
+    .money,
+    .bind,
+    .exchange {
+      width: 170 / @rem;
+      float: left;
+    }
+
+    .bind {
+      height: 80 / @rem;
+      line-height: 80 / @rem;
+      border-radius: 40 / @rem;
+      margin-left: 70 / @rem;
+      color: #fff;
+      background-image: linear-gradient(#fbd8c0, #ff092e);
+      cursor: pointer;
+    }
+
+    .exchange {
+      width: 90 / @rem;
+      line-height: 80 / @rem;
+      cursor: pointer;
+
+      i {
+        font-size: 45 / @rem;
+      }
+    }
+
+    .user_info {
+      width: 210 / @rem;
+      margin-top: -20 / @rem;
+    }
+
+    .money {
+      position: absolute;
+      line-height: 80 / @rem;
+      top: -80 / @rem;
+      right: 0 / @rem;
+      font-size: 30 / @rem;
+    }
+
+    .cash {
+      width: 170 / @rem;
+      height: 80 / @rem;
+      line-height: 80 / @rem;
+      border-radius: 40 / @rem;
+      float: right;
+      color: #fff;
+      background-image: linear-gradient(#fbd8c0, #ff092e);
+      //   background-image: linear-gradient(#fed5a4, #ff9d25);
+      cursor: pointer;
+    }
+  }
+
+  .list {
+    margin-top: 20 / @rem;
+
+    li {
+      height: 140 / @rem;
+      padding: 0 10 / @rem;
+      margin-top: 20 / @rem;
+      line-height: 70 / @rem;
+      border-radius: 5px;
+      background-color: #eee;
+      box-sizing: border-box;
+
+      .task_content,
+      .time,
+      .btn {
+        width: 44%;
+        float: left;
+      }
+
+      .time,
+      .btn {
+        width: 28%;
+        margin-top: 35 / @rem;
+      }
+
+      .task_content {
+        text-align: left;
+      }
+
+      .btn {
+        float: right;
+        border-radius: 36 / @rem;
+        color: #fff;
+        position: relative;
+        background-image: linear-gradient(#fbd8c0, #ff092e);
+        cursor: pointer;
+
+        &.already {
+          background-image: linear-gradient(#ffece0, #ff768a);
+          cursor: not-allowed;
+        }
+
+        .to_complete {
+          width: 100%;
+          position: absolute;
+          top: 0;
+          left: 0;
+          transform: rotate(90deg);
+        }
+      }
+    }
+  }
+}
+
+// 正常横屏样式
+@media all and (orientation: landscape),
+  /** 伪竖屏*/all and (orientation: portrait) and (min-width: 600px) and (min-height: 800px) {
+  .hb_task_list {
+    // 活动标题
+    .active_title {
+      margin-bottom: 10 / @rem;
+    }
+
+    .top_info {
+      .task_service {
+        width: 90 / @rem;
+        height: 40 / @rem;
+        line-height: 40 / @rem;
+        margin-right: 5 / @rem;
+        border-radius: 20 / @rem;
+      }
+
+      .user_info,
+      .money,
+      .bind,
+      .exchange {
+        width: 85 / @rem;
+      }
+
+      .bind {
+        height: 40 / @rem;
+        line-height: 40 / @rem;
+        border-radius: 20 / @rem;
+        margin-left: 90 / @rem;
+      }
+
+      .exchange {
+        width: 45 / @rem;
+        line-height: 40 / @rem;
+
+        i {
+          font-size: 23 / @rem;
+        }
+      }
+
+      .user_info {
+        width: 180 / @rem;
+        margin-top: -10 / @rem;
+      }
+
+      .money {
+        line-height: 40 / @rem;
+        top: -40 / @rem;
+        font-size: 15 / @rem;
+      }
+
+      .cash {
+        width: 85 / @rem;
+        height: 40 / @rem;
+        line-height: 40 / @rem;
+        margin-left: 12.5 / @rem;
+        border-radius: 20 / @rem;
+      }
+    }
+
+    .list {
+      margin-top: 10 / @rem;
+
+      li {
+        height: 70 / @rem;
+        padding: 0 5 / @rem;
+        margin-top: 10 / @rem;
+        line-height: 35 / @rem;
+
+        .time,
+        .btn {
+          margin-top: 17.5 / @rem;
+        }
+
+        .btn {
+          border-radius: 18 / @rem;
+
+          .to_complete {
+            width: 80%;
+            left: 10%;
+          }
+        }
+      }
+    }
+  }
+}
+
+// ipad 横屏
+@media /** 伪竖屏*/all and (orientation: portrait) and (min-width: 600px) and (min-height: 800px),
+  /**ipad伪横屏 */ all and (orientation: landscape) and (min-width: 800px) and (min-height: 600px),
+  all and (min-device-aspect-ratio: 3/4) and (max-device-aspect-ratio: 4/3),
+  all and (device-aspect-ratio: 4/3) {
+  .hb_task_list {
+    // 活动标题
+    .active_title {
+      margin-bottom: 10 / @rem;
+    }
+
+    .top_info {
+      .task_service {
+        width: 85 / @rem;
+        height: 40 / @rem;
+        line-height: 40 / @rem;
+        margin-right: 5 / @rem;
+        border-radius: 20 / @rem;
+      }
+
+      .user_info,
+      .money,
+      .bind,
+      .exchange {
+        width: 85 / @rem;
+      }
+
+      .money {
+        line-height: 40 / @rem;
+        top: -40 / @rem;
+        font-size: 15 / @rem;
+      }
+
+      .bind {
+        height: 40 / @rem;
+        line-height: 40 / @rem;
+        border-radius: 20 / @rem;
+        margin-left: 42.5 / @rem;
+      }
+
+      .user_info {
+        width: 110 / @rem;
+      }
+
+      .exchange {
+        width: 45 / @rem;
+        line-height: 40 / @rem;
+
+        i {
+          font-size: 23 / @rem;
+        }
+      }
+
+      .cash {
+        width: 85 / @rem;
+        height: 40 / @rem;
+        line-height: 40 / @rem;
+        margin-left: 12.5 / @rem;
+        border-radius: 20 / @rem;
+      }
+    }
+
+    .list {
+      margin-top: 10 / @rem;
+      font-size: 12 / @rem;
+
+      li {
+        height: 70 / @rem;
+        padding: 0 5 / @rem;
+        margin-top: 10 / @rem;
+        line-height: 35 / @rem;
+
+        .time,
+        .btn {
+          margin-top: 17.5 / @rem;
+        }
+
+        .btn {
+          border-radius: 18 / @rem;
+        }
+      }
+    }
+  }
+}
+</style>

+ 74 - 0
src/views/Active/Hb/HbTask/HbTaskTips/HbTaskTips.vue

@@ -0,0 +1,74 @@
+<template>
+  <!-- 活动说明 -->
+  <div class="task_tips_box">
+    <!-- 标题 -->
+    <ActiveTitle active="hb" acitveTitle="活动说明" />
+
+    <ul class="hb_task_tips">
+      <li>1.活动从角色创建以后开启,7天后结束;</li>
+      <li>2.每完成对应任务,即可领取对应红包奖励;</li>
+      <li>
+        3.红包奖励要在规定时间内完成并领取,有效时间内完成但未领取,视为自动放弃;
+      </li>
+      <li>4.红包提现需要达到提现额度方可提现;</li>
+      <li>
+        5.红包提现要在 “任务:10转150级” 时间结束后进行提现操作,24小时内如不提现视为自动放弃;
+      </li>
+      <li>
+        6.最终解释权归平台所有,活动期间任何问题都可以咨询<span
+          @click="onService"
+          >客服</span
+        >。
+      </li>
+    </ul>
+  </div>
+</template>
+
+<script>
+import ActiveTitle from "@/components/Active/ActiveTitle/ActiveTitle"; // 标题
+export default {
+  name: "HbTaskTips",
+  components: {
+    ActiveTitle,
+  },
+  data() {
+    return {};
+  },
+  computed: {},
+  watch: {},
+  created() {},
+  mounted() {},
+  methods: {
+    // 联系客服
+    onService() {
+      this.$emit("onService");
+    },
+  },
+};
+</script>
+
+<style lang='less' scoped>
+.hb_task_tips {
+  line-height: 50 / @rem;
+  margin-top: 20 / @rem;
+  text-align: left;
+
+  li {
+    span {
+      margin: 0 5 / @rem;
+      color: #fe777d;
+      border-bottom: 1px solid #fe777d;
+      cursor: pointer;
+    }
+  }
+}
+
+// 正常横屏样式
+@media all and (orientation: landscape),
+  /** 伪竖屏*/all and (orientation: portrait) and (min-width: 600px) and (min-height: 800px) {
+  .hb_task_tips {
+    line-height: 30 / @rem;
+    margin-top: 10 / @rem;
+  }
+}
+</style>

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.