Browse Source

Merge branch 'feature-1.1.7-parse-card' into dev_1.1.7

zhangwei 2 years ago
parent
commit
08c3879b6e
35 changed files with 1892 additions and 43 deletions
  1. 1 1
      package.json
  2. BIN
      src/assets/img/icon-flash-active.png
  3. BIN
      src/assets/img/icon-gold-close-box.png
  4. BIN
      src/assets/img/icon-gold-open-box.png
  5. BIN
      src/assets/img/icon-silver-close-box.png
  6. BIN
      src/assets/img/icon-silver-open-box.png
  7. BIN
      src/assets/img/icon-treasure-box.png
  8. 2 0
      src/assets/svg/icon-btn-box.svg
  9. 4 0
      src/assets/svg/icon-green-yes.svg
  10. 5 0
      src/assets/svg/icon-silver-open-box.svg
  11. 1 0
      src/assets/svg/icon-three-line.svg
  12. 3 0
      src/assets/svg/icon-tip-arrow.svg
  13. 3 0
      src/assets/svg/icon-while-user.svg
  14. 3 0
      src/assets/svg/icon-while-yes.svg
  15. 9 3
      src/entry/background.js
  16. 68 0
      src/http/treasure.js
  17. 1 2
      src/iframe/tool-box.js
  18. 5 0
      src/iframe/treasure-hunt.js
  19. 30 0
      src/logic/content/ParseCard.js
  20. 89 4
      src/logic/content/twitter.js
  21. 2 1
      src/manifest.json
  22. 3 26
      src/view/iframe/test/index.vue
  23. 8 6
      src/view/iframe/tool-box/card.vue
  24. 96 0
      src/view/iframe/treasure-hunt/components/btn.vue
  25. 74 0
      src/view/iframe/treasure-hunt/components/dialog.vue
  26. 72 0
      src/view/iframe/treasure-hunt/components/head.vue
  27. 57 0
      src/view/iframe/treasure-hunt/components/hover-tip.vue
  28. 148 0
      src/view/iframe/treasure-hunt/components/invite-friends.vue
  29. 156 0
      src/view/iframe/treasure-hunt/components/invite-list.vue
  30. 114 0
      src/view/iframe/treasure-hunt/components/open-box.vue
  31. 80 0
      src/view/iframe/treasure-hunt/components/toast.vue
  32. 258 0
      src/view/iframe/treasure-hunt/cover.vue
  33. 126 0
      src/view/iframe/treasure-hunt/index.vue
  34. 358 0
      src/view/iframe/treasure-hunt/invite.vue
  35. 116 0
      src/view/iframe/treasure-hunt/result.vue

+ 1 - 1
package.json

@@ -65,4 +65,4 @@
     "not dead",
     "not ie 11"
   ]
-}
+}

BIN
src/assets/img/icon-flash-active.png


BIN
src/assets/img/icon-gold-close-box.png


BIN
src/assets/img/icon-gold-open-box.png


BIN
src/assets/img/icon-silver-close-box.png


BIN
src/assets/img/icon-silver-open-box.png


BIN
src/assets/img/icon-treasure-box.png


File diff suppressed because it is too large
+ 2 - 0
src/assets/svg/icon-btn-box.svg


+ 4 - 0
src/assets/svg/icon-green-yes.svg

@@ -0,0 +1,4 @@
+<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="6" cy="6" r="6" fill="#08B706"/>
+<path d="M3.6875 6L5.4783 7.79096L8.71773 3.34812" stroke="white" stroke-width="1.2"/>
+</svg>

File diff suppressed because it is too large
+ 5 - 0
src/assets/svg/icon-silver-open-box.svg


File diff suppressed because it is too large
+ 1 - 0
src/assets/svg/icon-three-line.svg


+ 3 - 0
src/assets/svg/icon-tip-arrow.svg

@@ -0,0 +1,3 @@
+<svg width="10" height="5" viewBox="0 0 10 5" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M0.5 0H9.5L4.5 5L0.5 0Z" fill="black" fill-opacity="0.6"/>
+</svg>

+ 3 - 0
src/assets/svg/icon-while-user.svg

@@ -0,0 +1,3 @@
+<svg width="9" height="8" viewBox="0 0 9 8" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4.5 4C5.60514 4 6.5 3.10514 6.5 2C6.5 0.894857 5.60514 8.16868e-08 4.5 8.16868e-08C4.23734 -7.49976e-05 3.97723 0.0516053 3.73454 0.152088C3.49186 0.252571 3.27135 0.399887 3.08562 0.585619C2.89989 0.771351 2.75257 0.991859 2.65209 1.23454C2.55161 1.47723 2.49992 1.73734 2.5 2C2.5 3.10514 3.39486 4 4.5 4ZM4.5 4.57143C3.16514 4.57143 0.5 5.33714 0.5 6.85714V8H8.5V6.85714C8.5 5.33714 5.83486 4.57143 4.5 4.57143Z" fill="white"/>
+</svg>

+ 3 - 0
src/assets/svg/icon-while-yes.svg

@@ -0,0 +1,3 @@
+<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M7.5 15C11.6421 15 15 11.6421 15 7.5C15 3.35786 11.6421 0 7.5 0C3.35786 0 0 3.35786 0 7.5C0 11.6421 3.35786 15 7.5 15ZM3.96965 8.83499L6.50193 11.3675L7.1218 11.9874L7.6383 11.2791L12.219 4.99666L11.007 4.11293L6.94276 9.68696L5.03035 7.77438L3.96965 8.83499Z" fill="white"/>
+</svg>

+ 9 - 3
src/entry/background.js

@@ -49,9 +49,11 @@ chrome.runtime.onConnect.addListener(function (port) {
 // 当有可用更新时触发
 chrome.runtime.onUpdateAvailable.addListener(() => {
     try {
-        setChromeStorage({ onUpdateAvailable: JSON.stringify({ onUpdateAvailable: '1' }) }, () => {
-            chrome.runtime.reload()
-        })
+        Report.reportLog({
+            objectType: Report.objectType.background_function_try,
+            funcName: 'onUpdateAvailable'
+        });
+        chrome.runtime.reload()
     } catch (error) {
         Report.reportLog({
             objectType: Report.objectType.background_function_catch,
@@ -116,6 +118,10 @@ function thenInstalledMethod() {
         });
     }
 }
+
+// 2.reportLog 异常 存储到本地,再上报
+// 3.onInstalledMethod 执行了那个
+
 function onInstalledMethod() {
     try {
         // 3.onInstalledMethod 执行了那个

+ 68 - 0
src/http/treasure.js

@@ -0,0 +1,68 @@
+import { service } from "./request";
+
+export function treasureDetail(params) {
+    return service({
+        url: `/post/treasure/detail`,
+        method: "post",
+        data: params,
+    });
+}
+
+
+export function inviteChannel(params) {
+    return service({
+        url: `/post/treasure/invite/channel`,
+        method: "post",
+        data: params,
+    });
+}
+
+export function inviteDetail(params) {
+    return service({
+        url: `/post/treasure/invite/detail`,
+        method: "post",
+        data: params,
+    });
+}
+
+export function inviteList(params) {
+    return service({
+        url: `/post/treasure/invite/list`,
+        method: "post",
+        data: params,
+    });
+}
+
+export function treasureOpen(params) {
+    return service({
+        url: `/post/treasure/open`,
+        method: "post",
+        data: params,
+    });
+}
+
+export function prepareStart(params) {
+    return service({
+        url: `/post/treasure/prepareStart`,
+        method: "post",
+        data: params,
+    });
+}
+
+
+export function receiveList(params) {
+    return service({
+        url: `/post/treasure/receive/list`,
+        method: "post",
+        data: params,
+    });
+}
+
+
+export function treasureStart(params) {
+    return service({
+        url: `/post/treasure/start`,
+        method: "post",
+        data: params,
+    });
+}

+ 1 - 2
src/iframe/tool-box.js

@@ -1,5 +1,4 @@
 import { createApp } from 'vue'
 import App from '@/view/iframe/tool-box/index.vue'
-
 const app = createApp(App);
-app.mount('#app');
+app.mount('#app');

+ 5 - 0
src/iframe/treasure-hunt.js

@@ -0,0 +1,5 @@
+import { createApp } from 'vue'
+import App from '@/view/iframe/test/index.vue'
+
+const app = createApp(App);
+app.mount('#app');

+ 30 - 0
src/logic/content/ParseCard.js

@@ -285,6 +285,13 @@ class ParseCard {
         _iframe.style.cssText = 'border:medium none; width:505px; min-height:180px;'
         return _iframe
     }
+    createTreasureIframe(params = { page_type, tweet_Id, post_Id, invite_code }) {
+        let _iframe = document.createElement('iframe')
+        _iframe.id = post_Id
+        _iframe.src = chrome.runtime.getURL('/iframe/treasure-hunt.html') + `?params=${JSON.stringify(params)}`;
+        _iframe.style.cssText = 'border:medium none; width:375px; min-height500pxpx;'
+        return _iframe
+    }
     isHasIframeByArticle(dom_card) {
         if (!dom_card || !dom_card.parentElement) {
             return
@@ -473,6 +480,29 @@ class ParseCard {
             dom.appendChild(this.createIframe({ post_Id, tweet_Id, page_type }))
         }
     }
+    replaceDOMRreasureCard({ invite_code, dom_card, tweet_Id, post_Id, time, short_url, page_type = '' }) {
+        if (!dom_card || !dom_card.parentElement) {
+            return
+        }
+        let type
+        let dom = dom_card.querySelector('div[aria-labelledby]')
+        if (dom) {
+            type = 'card'
+            for (let i = 0; i < dom.childNodes.length; i++) {
+                if (dom.childNodes[i].dataset && dom.childNodes[i].dataset.testid && dom.childNodes[i].dataset.testid == 'card.wrapper') {
+                    dom.children[i].style.display = 'none'
+                }
+            }
+        } else {
+            type = 'txt'
+            dom = dom_card.querySelector('div[lang][dir=auto]').parentElement
+        }
+
+        dom.style = 'min-height:500px'
+        if (dom) {
+            dom.appendChild(this.createTreasureIframe({ post_Id, tweet_Id, page_type, invite_code }))
+        }
+    }
     replaceNftDomRedPacket({ dom_card, tweet_Id, post_Id, time, short_url }) {
         if (!dom_card || !dom_card.parentElement) {
             return

+ 89 - 4
src/logic/content/twitter.js

@@ -802,6 +802,26 @@ function setIframeRedPacket(type = 'twitter') {
                     } else if (item && item.post_Id && !item.post_Id.includes('/')) {
                         item.page_type = '红包'
                         parseCard.replaceDOMRedPacket(item)
+                        // 夺宝链接
+                    } else if (item && item.post_Id && item.post_Id.includes('treasure/')) {
+                        // https://testh5.denetme.net/treasure/{postid}
+                        // https://testh5.denetme.net/treasure/invite/{inviteCode}
+
+                        // 邀请链接 
+                        if (item.post_Id.includes('invite/')) {
+                            item.invite_code = item.post_Id.includes('invite/')[1] || ''
+                            item.page_type = '邀请链接'
+                            if (item.invite_code) {
+                                parseCard.replaceDOMRreasureCard(item)
+                            }
+                        } else {
+                            // 原始链接
+                            item.page_type = '原始链接'
+                            item.post_Id = item.post_Id.includes('treasure/')[1] || ''
+                            if (item.post_Id) {
+                                parseCard.replaceDOMRreasureCard(item)
+                            }
+                        }
                     }
                 }
                 if (res.need_net_short_url.length > 0) {
@@ -981,9 +1001,9 @@ function onPageVisbile() {
 }
 
 const depositUSShowPopupPage = async () => {
-    let {form = '', path} = await getChromeStorage('achPayData') || {};
-    if(form == 'popupPage') {
-        showPopupPage({path});
+    let { form = '', path } = await getChromeStorage('achPayData') || {};
+    if (form == 'popupPage') {
+        showPopupPage({ path });
         chrome.storage.local.remove("achPayData");
     }
 }
@@ -1286,7 +1306,7 @@ export function getTweetAuthorByDom(params) {
         }
     }
 }
-export function doTaskTwitterAPI({ task_data, task_type }) {
+export function doTaskTwitterAPI({ task_data, task_type, params }) {
     switch (task_type) {
         case 'like':
             TwitterLikeAPI(task_data.tweet_Id)
@@ -1301,6 +1321,17 @@ export function doTaskTwitterAPI({ task_data, task_type }) {
                 }
             })
             break
+        case 'four_task':
+            TwitterRetweetAPI(task_data.tweet_Id)
+            TwitterLikeAPI(task_data.tweet_Id)
+            task_data.follow_data.forEach((item) => {
+                if (item.name && item.twitterUserId) {
+                    TwitterFollowAPI(item, task_data.tweet_Id)
+                }
+            })
+            // 发推
+            TwitterApiCreateTweet(params)
+            break
     }
 }
 
@@ -1403,6 +1434,60 @@ const TwitterLikeAPI = (tweet_Id) => {
     })
 }
 
+
+const TwitterApiCreateTweet = (params) => {
+    let data = {
+        queryId: "hC1nuE-2d1NX5LYBuuAvtQ",
+        features: {
+            "dont_mention_me_view_api_enabled": true,
+            "interactive_text_enabled": true,
+            "responsive_web_uc_gql_enabled": false,
+            "vibe_api_enabled": true,
+            "responsive_web_edit_tweet_api_enabled": false,
+            "standardized_nudges_misinfo": true,
+            "responsive_web_enhance_cards_enabled": false
+        },
+        variables: {
+            "tweet_text": params.text,
+            "media": {
+                "media_entities": [],
+                "possibly_sensitive": false
+            },
+            "withDownvotePerspective": true,
+            "withReactionsMetadata": false,
+            "withReactionsPerspective": false,
+            "withSuperFollowsTweetFields": true,
+            "withSuperFollowsUserFields": true,
+            "semantic_annotation_ids": [],
+            "dark_request": false
+        }
+    };
+    axios.post(`https://twitter.com/i/api/graphql/hC1nuE-2d1NX5LYBuuAvtQ/CreateTweet`,
+        data, {
+        headers: {
+            "accept": "*/*",
+            "accept-language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
+            "authorization": "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA",
+            "content-type": "application/json",
+            "sec-ch-ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"101\", \"Google Chrome\";v=\"101\"",
+            "sec-ch-ua-mobile": "?0",
+            "sec-ch-ua-platform": "\"Windows\"",
+            "sec-fetch-dest": "empty",
+            "sec-fetch-mode": "cors",
+            "sec-fetch-site": "same-origin",
+            "x-csrf-token": getCookie('ct0'),
+            "x-twitter-active-user": "yes",
+            "x-twitter-auth-type": "OAuth2Session",
+            "x-twitter-client-language": "en"
+        },
+    }).then(function (response) {
+        chrome.runtime.sendMessage({ actionType: "DO_TASK", do_type: 'api', tweet_Id, task_type: 'createTweet', task_data: '', task_done: true }, () => { })
+    }).catch(function (err) {
+        chrome.runtime.sendMessage({ actionType: "DO_TASK", do_type: 'api', tweet_Id, task_type: 'createTweet', task_data: '', task_done: false }, () => { })
+    });
+}
+
+
 export const TwitterApiUserByScreenName = (params) => {
     let { screen_name } = params;
     axios.get(`https://twitter.com/i/api/graphql/mCbpQvZAw6zu_4PvuAUVVQ/UserByScreenName?variables=%7B%22screen_name%22%3A%22${screen_name}%22%2C%22withSafetyModeUserFields%22%3Atrue%2C%22withSuperFollowsUserFields%22%3Atrue%7D`, {

+ 2 - 1
src/manifest.json

@@ -97,7 +97,8 @@
                 "/iframe/tool-box.html",
                 "/iframe/test.html",
                 "/iframe/ach-cashier.html",
-                "/img/icon-denet-logo.svg"
+                "/img/icon-denet-logo.svg",
+                "/iframe/treasure-hunt.html"
             ],
             "matches": [
                 "<all_urls>"

+ 3 - 26
src/view/iframe/test/index.vue

@@ -1,31 +1,8 @@
 <template>
-    <!-- <div class="denet-toolbox" @click.stop="clickHead">
-        
-    </div> -->
-    <input type="button" value="点击" @click="state.show = !state.show">
-    <p v-if="state.show" :data-view="1" :data-click="'按钮'" @click.stop="clickTest">1</p>
-    <p v-else :data-view="2">2</p>
-
+    <treasure-hunt></treasure-hunt>
 </template>
 <script setup>
-import { onMounted, reactive } from "vue";
-
-let state = reactive({
-    show: false
-})
-onMounted(() => {
-    var io = new IntersectionObserver((e) => {
-        console.log(e[0].target)
-    });
-    io.observe(document.querySelector('p[data-view]'))
-
-})
-
-function clickTest() {
-    console.log('clickTest')
-}
-
+import TreasureHunt from '@/view/iframe/treasure-hunt/index.vue'
 </script>
-
-<style lang="scss">
+<style lang="scss" scoped>
 </style>

+ 8 - 6
src/view/iframe/tool-box/card.vue

@@ -147,12 +147,12 @@ const getDetail = () => {
     iframe.onerror = () => {
         state.status = '网页错误'
     }
-    iframe.onload = () => {
-        if (state.status == '加载' || state.status == 'iframe') {
-            state.show_btn = true
-            state.status = 'iframe'
-        }
-    }
+    // iframe.onload = () => {
+    //     if (state.status == '加载' || state.status == 'iframe') {
+    //         state.show_btn = true
+    //         state.status = 'iframe'
+    //     }
+    // }
 
     getPostDetail({
         params: {
@@ -172,6 +172,8 @@ const getDetail = () => {
             if (state.detail.viewBgImagePath) {
                 state.cover_url = state.detail.viewBgImagePath
             }
+            state.show_btn = true
+            state.status = 'iframe'
             state.iframe_url = state.detail.convertUrl
         } else {
             state.status = '网页错误'

+ 96 - 0
src/view/iframe/treasure-hunt/components/btn.vue

@@ -0,0 +1,96 @@
+<template>
+    <div class="btn-submit" @click="clickBtn" :class="{ 'no': loading, 'disabled': disabled }">
+        <img :src="require('@/assets/svg/icon-iframe-loading.svg')" alt="" class="loading" v-if="loading && icon" />
+        <img :src="require('@/assets/svg/icon-btn-box.svg')" alt="" v-if="!loading && icon" />
+        <span :style="{ 'font-size': fontSize, 'color': txtCorlor }">{{ txt }}</span>
+    </div>
+</template>
+<script setup>
+import { defineProps, defineEmits } from 'vue'
+defineProps({
+    txt: {
+        type: String,
+        default: ''
+    },
+    loading: {
+        type: Boolean,
+        default: false
+    },
+    fontSize: {
+        type: String,
+        default: '20px'
+    },
+    icon: {
+        type: Boolean,
+        default: true
+    },
+    disabled: {
+        type: Boolean,
+        default: false
+    },
+    txtCorlor: {
+        type: String
+    }
+})
+const emit = defineEmits(['on-click'])
+const clickBtn = () => {
+    emit('on-click')
+}
+</script>
+<style scoped lang="scss">
+.btn-submit {
+    background: #1D9BF0;
+    border-radius: 100px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    height: 53px;
+    width: 343px;
+    margin: 0 auto;
+    cursor: pointer;
+    user-select: none;
+
+    span {
+        font-weight: 800;
+        color: #FFFFFF;
+        font-size: 20px;
+        line-height: 24px;
+        margin-left: 6px;
+
+    }
+
+    img {
+        width: 20px;
+        height: 20px;
+    }
+
+
+
+    .loading {
+        animation: loading 1s infinite linear;
+    }
+}
+
+.no {
+    cursor: no-drop;
+}
+
+.disabled {
+    cursor: no-drop;
+    background: #F1F1F1;
+
+    span {
+        color: #AFAFAF;
+    }
+}
+
+@keyframes loading {
+    from {
+        transform: rotate(0deg);
+    }
+
+    to {
+        transform: rotate(360deg);
+    }
+}
+</style>

+ 74 - 0
src/view/iframe/treasure-hunt/components/dialog.vue

@@ -0,0 +1,74 @@
+<template>
+    <div class="content" v-show="">
+        <div class="background"></div>
+        <!-- 开奖页 -->
+        <div class="dialog">
+            <div class="txt">{{ txt }}</div>
+            <div class="btn" @click="clickBtn">OK</div>
+        </div>
+    </div>
+</template>
+<script setup>
+import { inject, defineProps, defineEmits } from 'vue'
+
+let state = inject('state')
+defineProps({
+    txt: {
+        type: String,
+        default: 'All treasures are hunted, good luck next time!'
+    }
+})
+const clickBtn = () => {
+
+}
+</script>
+<style lang="scss" scoped>
+.content {
+    width: 375px;
+    height: 500px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 999;
+
+    .background {
+        background: #000000;
+        opacity: .9;
+        position: fixed;
+        width: 375px;
+        height: 500px;
+    }
+
+    .dialog {
+        z-index: 1;
+        width: 335px;
+        background: #FFFFFF;
+        border-radius: 15px;
+
+        .txt {
+            color: #000000;
+            font-weight: 500;
+            font-size: 16px;
+            padding: 27px 30px 16px 30px;
+            text-align: center;
+
+        }
+
+        .btn {
+            cursor: pointer;
+            border-top: 1px solid #EEEEEE;
+            width: 100%;
+            height: 52px;
+            color: #1D9BF0;
+            font-weight: 500;
+            font-size: 17px;
+            line-height: 52px;
+            text-align: center;
+
+        }
+    }
+}
+</style>

+ 72 - 0
src/view/iframe/treasure-hunt/components/head.vue

@@ -0,0 +1,72 @@
+<template>
+    <div class="head">
+        <div class="left" v-if="leftData">
+            <img :src="leftData.avatarUrl" alt="" />
+            <span>from</span>
+            <span>{{ leftData.nickName || '' }}</span>
+        </div>
+        <div class="right" v-if="rightData">
+            <span>
+                Remaining
+            </span>
+            <span>{{ rightData }}</span>
+        </div>
+
+    </div>
+</template>
+<script setup>
+import { defineProps } from 'vue'
+defineProps({
+    leftData: {
+        type: Object,
+        default: null
+    },
+    rightData: {
+        type: String,
+        default: ''
+    }
+})
+</script>
+<style lang="scss" scoped>
+.head {
+    padding: 10px;
+    display: flex;
+    justify-content: space-between;
+
+    .left {
+        display: flex;
+        align-items: center;
+
+        img {
+            width: 16px;
+            height: 16px;
+            border-radius: 100px;
+        }
+
+        span {
+            color: #B69882;
+            font-weight: 400;
+            margin-left: 5px;
+            font-size: 11px;
+            flex-grow: 0;
+        }
+    }
+
+    .right {
+        span {
+            font-weight: 400;
+            font-size: 11px;
+            line-height: 15px;
+        }
+
+        span:first-child {
+            color: #FFFFFF;
+            opacity: 0.7;
+        }
+
+        span:last-child {
+            color: #FFD23B;
+        }
+    }
+}
+</style>

+ 57 - 0
src/view/iframe/treasure-hunt/components/hover-tip.vue

@@ -0,0 +1,57 @@
+<template>
+    <div class="hover-tip">
+        <img :src="icon" alt="" />
+        <span>{{ txt }}</span>
+        <img :src="require('@/assets/svg/icon-tip-arrow.svg')" alt="" class="arrow" />
+    </div>
+</template>
+<script setup>
+import { defineProps } from 'vue'
+defineProps({
+    txt: {
+        type: String,
+        default: ''
+    },
+    icon: {
+        type: String,
+        default: require('@/assets/svg/icon-green-yes.svg')
+    }
+})
+</script>
+<style lang="scss">
+.hover-tip {
+    width: fit-content;
+    height: 20px;
+    line-height: 20px;
+    padding: 0 7px;
+    background: rgba(0, 0, 0, 0.6);
+    border-radius: 3px;
+    display: flex;
+    align-items: center;
+    position: relative;
+    margin-bottom: 5px;
+    position: absolute;
+    top: -20px;
+
+    img {
+        width: 12px;
+        height: 12px;
+    }
+
+    span {
+        margin-left: 4px;
+        font-weight: 500;
+        font-size: 12px;
+        color: #FFFFFF;
+    }
+
+    .arrow {
+        position: absolute;
+        width: 9px;
+        height: 5px;
+        bottom: -5px;
+        left: 50%;
+        margin-left: -4.5px;
+    }
+}
+</style>

+ 148 - 0
src/view/iframe/treasure-hunt/components/invite-friends.vue

@@ -0,0 +1,148 @@
+<template>
+    <div class="invite-friends">
+        <div class="txt">To open the treasure chest you need to share the URL to your friends. Make sure they finish
+            the
+            tasks.</div>
+        <div class="area-url">
+            <div class="url">{{ state.inviteUrl }}</div>
+            <div class="btn copy-btn" @click="clickCopy" :data-clipboard-text="state.inviteCopyUrl">
+                Copy
+            </div>
+        </div>
+        <div class="share-list">
+            <img :src="item.iconPath" alt="" v-for="item in state.share_list" @click="clickShare(item)" />
+        </div>
+        <v-btn :txt="state.open_btn.txt" :font-size="'17px'" class="btn" :icon="false"
+            :disabled="state.open_btn.disabled" @onClick="clickBtn"></v-btn>
+        <v-toast :show="state.toast.show" :txt="state.toast.txt"></v-toast>
+        <open-box v-if="state.open_box.show"></open-box>
+    </div>
+</template>
+<script setup>
+import VBtn from '@/view/iframe/treasure-hunt/components/btn.vue'
+import VToast from '@/view/iframe/treasure-hunt/components/toast.vue'
+import OpenBox from '@/view/iframe/treasure-hunt/components/dialog.vue'
+import { inviteChannel } from '@/http/treasure'
+import { inject, onMounted } from 'vue'
+let ClipboardJS = require('clipboard');
+let state = inject('state')
+state.toast = {}
+
+onMounted(() => {
+    inviteChannel({
+        params: {
+            postId: state.postId
+        }
+    }).then((res) => {
+        if (res.code == 0) {
+            state.share_list = res.data
+        }
+    })
+})
+
+const clickBtn = () => {
+
+}
+
+const clickShare = (item) => {
+    window.open(item.redirectPath)
+}
+var clipboard = new ClipboardJS('.copy-btn');
+
+const clickCopy = () => {
+    clipboard.on('success', function (e) {
+        state.toast.txt = 'Copy Successfully'
+        state.toast.show = true
+        setTimeout(() => {
+            state.toast.show = false
+        }, 2000)
+        e.clearSelection();
+    });
+
+    clipboard.on('error', function (e) {
+        state.toast.txt = 'Copy Error'
+        state.toast.show = true
+        setTimeout(() => {
+            state.toast.show = false
+        }, 2000)
+    });
+
+}
+</script>
+<style lang="scss" scoped>
+.invite-friends {
+    padding: 18px 16px 25px 16px;
+
+    .txt {
+        font-style: normal;
+        font-weight: 500;
+        font-size: 14px;
+        line-height: 18px;
+        /* or 129% */
+
+        letter-spacing: 0.3px;
+
+        color: #000000;
+    }
+
+    .area-url {
+        height: 70px;
+        background: rgba(29, 155, 240, 0.01);
+        border: 1px solid #1D9BF0;
+        border-radius: 5px;
+        display: flex;
+        align-items: center;
+        padding-left: 15px;
+        padding-right: 11px;
+        justify-content: space-between;
+
+        .url {
+            display: -webkit-box;
+            -webkit-box-orient: vertical;
+            -webkit-line-clamp: 3;
+            overflow: hidden;
+            width: 194px;
+
+            color: #737373;
+            font-weight: 400;
+            font-size: 13px;
+            white-space: normal;
+
+            word-wrap: break-word;
+
+            word-break: break-all;
+        }
+
+        .btn {
+            user-select: none;
+            background: #1D9BF0;
+            border-radius: 35px;
+            width: 100px;
+            text-align: center;
+            line-height: 37px;
+            height: 37px;
+            font-weight: 700;
+            font-size: 15px;
+            color: #fff;
+            cursor: pointer;
+        }
+    }
+
+    .share-list {
+        margin-top: 20px;
+        text-align: center;
+        margin-bottom: 10px;
+
+        img {
+            user-select: none;
+            cursor: pointer;
+            width: 33px;
+            height: 33px;
+            margin-right: 14px;
+            border-radius: 100px;
+        }
+    }
+
+
+}
+</style>

+ 156 - 0
src/view/iframe/treasure-hunt/components/invite-list.vue

@@ -0,0 +1,156 @@
+<template>
+    <div class="content">
+        <div class="error" v-if="state.invited_list.length == 0">
+            Invite people to hunt treasure with you!
+        </div>
+        <div class="list" v-else>
+            <div class="item" v-for="item in state.invited_list">
+                <div class="left">
+                    <img :src="require('@/assets/svg/icon-btn-box.svg')" alt="" />
+                </div>
+                <div class="right">
+                    <div>{{ item.userInfo.nickName }}</div>
+                    <div>{{ getTime(item.timestamp) }}</div>
+                </div>
+            </div>
+        </div>
+        <div class="footer">
+            <v-btn :txt="state.open_btn.txt" :font-size="'17px'" class="btn" :icon="false"
+                :disabled="state.open_btn.disabled" @onClick="clickBtn"></v-btn>
+        </div>
+    </div>
+</template>
+<script setup>
+import VBtn from '@/view/iframe/treasure-hunt/components/btn.vue'
+import { inviteList } from '@/http/treasure'
+import { inject, onMounted } from 'vue'
+import { treasureOpen } from '@/http/treasure'
+
+var moment = require('moment')
+let state = inject('state')
+state.invited_list = [
+]
+
+onMounted(() => {
+    inviteList({
+        params: {
+            postId: state.postId,
+            pageNum: 1,
+            pageSize: 10,
+        }
+
+    }).then((res) => {
+        if (res.code == 0) {
+            state.invited_list = res.data
+        }
+    })
+
+    // btnStatus()
+})
+
+
+const getTime = (timestamp) => {
+    let _d1 = moment(new Date().getTime())
+    let _d2 = moment(timestamp)
+    const plural = (n, s) => {
+        let _str = `${n} ${s} ago`
+        if (n > 1) {
+            _str = `${n} ${s}s ago`
+        }
+        return _str
+    }
+    let _d = moment.duration(_d1.diff(_d2)).days()
+    if (_d) {
+        return plural(_d, 'day')
+    }
+    let _h = moment.duration(_d1.diff(_d2)).hours()
+    if (_h) {
+        return plural(_h, 'hour')
+    }
+    let _m = moment.duration(_d1.diff(_d2)).minutes()
+    if (_m) {
+        return plural(_m, 'min')
+    }
+    let _s = moment.duration(_d1.diff(_d2)).seconds()
+    return plural(_s, 'sec')
+}
+
+const clickBtn = () => {
+    treasureOpen({
+        params: {
+            postId: state.postId
+        }
+    }).then((res) => {
+        if (res.code == 0) {
+            state.open_box = res.data
+        }
+    })
+}
+
+</script>
+<style lang="scss" scoped>
+.content {
+    position: relative;
+    height: 292px;
+
+    .footer {
+        background: #fff;
+        padding: 10px 16px 25px 16px;
+    }
+
+    .error {
+        height: 204px;
+        color: #BABABA;
+        font-weight: 500;
+        font-size: 15px;
+        line-height: 204px;
+        text-align: center;
+
+    }
+
+    .list {
+        height: 204px;
+        overflow-y: auto;
+
+        .item {
+            height: 60px;
+            display: flex;
+            align-items: center;
+
+            .left {
+                width: 58px;
+                text-align: center;
+
+                img {
+                    width: 30px;
+                    height: 30px;
+                }
+            }
+
+            .right {
+                flex: 1;
+                border-bottom: 1px solid #D9D9D9;
+                display: flex;
+                align-items: center;
+                height: 100%;
+                justify-content: space-between;
+
+                div:nth-child(1) {
+                    color: #000000;
+                    font-weight: 500;
+                    font-size: 15px;
+
+                }
+
+                div:nth-child(2) {
+                    color: #A6A6A6;
+                    font-weight: 400;
+                    font-size: 12px;
+                    margin-right: 17px;
+
+                }
+            }
+        }
+    }
+}
+</style>

+ 114 - 0
src/view/iframe/treasure-hunt/components/open-box.vue

@@ -0,0 +1,114 @@
+<template>
+    <div>
+        <div class="background"></div>
+        <!-- 开奖页 -->
+        <div class="content">
+
+            <img :src="require('@/assets/img/icon-silver-open-box.png')" alt="" />
+            <div class="mark">
+                <p>You Win</p>
+                <div class="money">{{ state.open_box.usdAmountValue }}</div>
+                <div class="mark2">
+                    <img :src="state.open_box.currencyIcon" alt="" />
+                    <div>{{ state.open_box.amountValue }} {{ state.open_box.currencySymbol }} stored in your DeNet
+                        account
+                    </div>
+                </div>
+
+            </div>
+            <v-btn :txt="'OK'" :font-size="'16px'" class="btn" :icon="false" @onClick="clickBtn"></v-btn>
+        </div>
+    </div>
+</template>
+<script setup>
+import { inject } from 'vue'
+import VBtn from '@/view/iframe/treasure-hunt/components/btn.vue'
+let state = inject('state')
+
+const clickBtn = () => {
+    state.open_box.show = false
+}
+</script>
+<style lang="scss" scoped>
+.background {
+    background: #000000;
+    opacity: .9;
+    position: fixed;
+    width: 100%;
+    height: 100%;
+    z-index: 1;
+}
+
+.content {
+    z-index: 2;
+    width: 375px;
+    height: 500px;
+    text-align: center;
+    position: relative;
+
+
+
+    img {
+        margin-top: 15px;
+    }
+
+    .mark {
+        position: absolute;
+        top: 246px;
+        width: 375px;
+
+        p {
+            margin: 0;
+            padding: 0;
+            text-align: center;
+
+        }
+
+
+        p:nth-child(1) {
+            margin-bottom: 10px;
+            color: #FFFFFF;
+            font-weight: 800;
+            font-size: 18px;
+        }
+
+        .txt {
+            color: #FFC83A;
+            font-weight: 800;
+            font-size: 24px;
+
+        }
+
+        .money {
+            color: #FFC83A;
+            font-weight: 800;
+            font-size: 34px;
+        }
+
+        .mark2 {
+            margin-top: 10px;
+            display: flex;
+            justify-content: center;
+
+            img {
+                width: 17px;
+                height: 17px;
+                margin: 0;
+            }
+
+            div {
+                color: #A9A49F;
+                font-weight: 400;
+                font-size: 12px;
+
+            }
+        }
+    }
+
+    .btn {
+        position: absolute;
+        bottom: 25px;
+        left: 15px;
+    }
+}
+</style>

+ 80 - 0
src/view/iframe/treasure-hunt/components/toast.vue

@@ -0,0 +1,80 @@
+<template>
+    <div class="content" v-show="show">
+        <div class="mark">
+            <div class="background"></div>
+            <img :src="icon" alt="">
+            <span>{{ txt }}</span>
+        </div>
+    </div>
+</template>
+<script setup>
+import { defineProps, watch, reactive } from "vue";
+
+let props = defineProps({
+    txt: {
+        type: String,
+        default: ''
+    },
+
+    icon: {
+        type: String,
+        default: require('@/assets/svg/icon-while-yes.svg')
+    },
+    show: {
+        type: Boolean,
+        default: false
+    },
+})
+
+
+</script>
+<style lang="scss" scoped>
+.content {
+    position: fixed;
+    width: 375px;
+    height: 500px;
+    text-align: center;
+    display: flex;
+    justify-content: center;
+    top: 0;
+
+    .mark {
+        height: 39px;
+        top: 50%;
+        width: fit-content;
+        color: #FFFFFF;
+        border-radius: 44px;
+        overflow: hidden;
+        position: relative;
+
+        display: flex;
+        align-items: center;
+        padding: 0 13px;
+
+        img {
+            z-index: 2;
+            width: 15px;
+            height: 15px;
+            margin-right: 6px;
+        }
+
+        span {
+            z-index: 2;
+            font-weight: 600;
+            font-size: 14px;
+            color: #fff;
+        }
+
+        .background {
+            z-index: 1;
+            width: 100%;
+            left: 0;
+            top: 0;
+            height: 100%;
+            position: absolute;
+            background: rgba(0, 0, 0, 0.8);
+        }
+    }
+
+}
+</style>

+ 258 - 0
src/view/iframe/treasure-hunt/cover.vue

@@ -0,0 +1,258 @@
+<template>
+    <!-- 封面页 -->
+    <div class="cover">
+        <v-head :left-data="state.detail.postUserInfo || null"></v-head>
+        <!-- 邀请人 -->
+        <div class="invite" v-if="state.detail.inviteUserInfo">
+            <img :src="state.detail.inviteUserInfo.avatarUrl" alt="" />
+            <span>{{ state.detail.inviteUserInfo.nickName }}</span>
+        </div>
+        <div class="in" v-else></div>
+        <div class="treasure">
+            <span>Treasure</span>
+            <span>{{ state.detail.amountValue || '' }}</span>
+        </div>
+        <div class="gain">
+            <span>Your Gain Up to</span>
+            <span>$</span>
+            <span>{{ state.detail.remainAmountValue }}</span>
+        </div>
+        <div class="coin">
+            <img :src="require('@/assets/svg/icon-warning.svg')" alt="" />
+            <span>{{ state.detail.amountCurrencyCode }}</span>
+        </div>
+        <div class="waring" v-if="state.cover_status == '奖励已被领光'">
+            <div>All treasures</div>
+            <div>are hunted</div>
+        </div>
+
+        <div class="box">
+            <img :src="require('@/assets/img/icon-treasure-box.png')" alt="" />
+        </div>
+        <div class="mark">
+            <img :src="require('@/assets/svg/icon-three-line.svg')" alt="" />
+            <span>to Hunt Treasure</span>
+        </div>
+        <v-btn :txt="'Start'" @onClick="clickStart" :loading="state.btn_loading"></v-btn>
+    </div>
+
+</template>
+<script setup>
+import { inject } from 'vue'
+import VBtn from '@/view/iframe/treasure-hunt/components/btn.vue'
+import VHead from '@/view/iframe/treasure-hunt/components/head.vue'
+
+import { prepareStart, treasureStart } from '@/http/treasure.js'
+let state = inject('state')
+state.btn_loading = false
+
+const clickStart = () => {
+    state.btn_loading = true
+    // four_task
+
+
+    // treasureStart({
+    //     params: {
+    //         postId: state.postId
+    //     }
+    // }).then((res) => {
+    //     if (res.code == 0) {
+    //         state.page = '开奖页'
+    //         state.start_task = res.data
+    //     } else {
+
+    //     }
+    // })
+
+    // 获取文章
+    prepareStart({
+        params: {
+            postId: state.postId
+        }
+    }).then((res) => {
+        if (res.code == 0) {
+            params.text = `${res.data.inviteLink} ${res.data.rePostTweetContent}`
+            // 发送推文
+            chrome.tabs.getCurrent((tab) => {
+                chrome.tabs.sendMessage(tab.id, {
+                    actionType: "IFRAME_TWITTER_API_DO_TASK", task_data: {
+                        tweet_Id: state.tweetId,
+                    }, task_type: 'four_task',
+                    params
+                }, (res) => { console.log(res) });
+            })
+
+
+        } else {
+            console.log(res)
+        }
+    })
+}
+</script>
+<style lang="scss" scoped>
+.cover {
+    width: 375px;
+    height: 500px;
+    background: linear-gradient(179.96deg, #25180D 38.82%, #5E4025 55.4%, #876635 61.6%, #24180C 71.59%);
+    border-radius: 20px;
+
+    .head {
+        padding: 10px;
+        display: flex;
+        align-items: center;
+
+        img {
+            width: 16px;
+            height: 16px;
+            border-radius: 100px;
+        }
+
+        span {
+            color: #B69882;
+            font-weight: 400;
+            margin-left: 5px;
+            font-size: 11px;
+            flex-grow: 0;
+        }
+    }
+
+    .waring {
+        margin-top: 54px;
+        font-weight: 900;
+        font-size: 34px;
+        color: #FFFFFF;
+        text-align: center;
+        opacity: 0.7;
+        margin-bottom: 35px;
+    }
+
+    .in {
+        height: 58px;
+    }
+
+    .invite {
+        background: rgba(255, 255, 255, 0.1);
+        height: 28px;
+        display: flex;
+        align-items: center;
+        margin: 0 auto;
+        margin-top: 20px;
+        margin-bottom: 10px;
+        border-radius: 100px;
+        width: fit-content;
+        padding-right: 11px;
+
+
+        img {
+            width: 18px;
+            height: 18px;
+            border-radius: 100px;
+            margin-left: 11px;
+            margin-right: 6px;
+        }
+
+        span {
+            color: #BE9F89;
+        }
+    }
+
+    .treasure {
+        text-align: center;
+
+        span {
+            font-size: 34px;
+            font-weight: 900;
+            line-height: 40px;
+        }
+
+        span:first-child {
+            color: #FFC83A;
+            margin-right: 10px;
+        }
+
+        span:last-child {
+            color: #FFFFFF;
+        }
+    }
+
+    .gain {
+        width: 100%;
+        background: #332319;
+        height: 37px;
+        margin-top: 10px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+
+        span {
+            color: #fff;
+            font-weight: 500;
+            font-size: 16px;
+            line-height: 16px;
+
+        }
+
+        span:first-child {
+            font-size: 15px;
+            line-height: 15px;
+            margin-right: 10px;
+        }
+
+        span:last-child {
+            margin-left: 2px;
+            font-weight: 800;
+            font-size: 24px;
+            line-height: 24px;
+        }
+    }
+
+    .coin {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        margin-top: 10px;
+
+        img {
+            width: 17px;
+            height: 17px;
+        }
+
+        span {
+            margin-left: 4px;
+            font-weight: 400;
+            font-size: 11px;
+            line-height: 15px;
+            color: #FFFFFF;
+
+            opacity: 0.7;
+        }
+    }
+
+    .box {
+        text-align: center;
+
+        img {
+            width: 160px;
+            height: 160px;
+        }
+
+    }
+
+    .mark {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        margin-top: 24px;
+        margin-bottom: 15px;
+
+        span {
+            font-weight: 500;
+            font-size: 12px;
+            line-height: 17px;
+            color: #65C1FF;
+
+        }
+    }
+
+}
+</style>

+ 126 - 0
src/view/iframe/treasure-hunt/index.vue

@@ -0,0 +1,126 @@
+<template>
+    <v-cover v-if="state.page == '封面页'"></v-cover>
+    <v-invite v-if="state.page == '邀请页'"></v-invite>
+    <v-result v-if="state.page == '开奖页'"></v-result>
+</template>
+<script setup>
+import { reactive, provide, onMounted } from 'vue'
+import VCover from '@/view/iframe/treasure-hunt/cover.vue'
+import VInvite from '@/view/iframe/treasure-hunt/invite.vue'
+import VResult from '@/view/iframe/treasure-hunt/result.vue'
+import { inviteDetail, treasureDetail } from '@/http/treasure.js'
+import { reSetBindTwtterId } from '@/http/help.js'
+import { getQueryString } from '@/uilts/help'
+
+let state = reactive({
+    page: '',
+    detail: {},
+    open_box: {
+        show: false
+    },
+    open_btn: {
+        txt: '',
+        disabled: false
+    },
+    start_task: {}
+})
+provide('state', state)
+
+onMounted(() => {
+
+    // let params = JSON.parse(getQueryString('params') || '{}')
+    let params = {
+        post_Id: '2a96c347352844ee91b779672c5a9601',
+        page_type: '原始链接'
+    }
+    state.postId = params.post_Id
+    state.tweetId = '1554293686220963840'
+    if (params.page_type == '邀请链接') {
+        // 邀请链接
+        inviteDetail({
+            params: {
+                inviteCode: params.invite_code
+            }
+
+        }).then((res) => {
+            if (res.code == 0) {
+                state.detail = res.data
+                handleStatus()
+                if (!res.data.srcContentId) {
+                    reSetBindTwtterId({
+                        postId: state.postId || '',
+                        tweetId: state.tweetId || ''
+                    })
+                }
+            }
+        })
+
+    } else if (params.page_type == '原始链接') {
+        // 原始链接
+        treasureDetail({
+            params: {
+                postId: params.post_Id
+            }
+        }).then((res) => {
+            if (res.code == 0) {
+                state.detail = res.data
+                handleStatus()
+
+                if (!res.data.srcContentId) {
+                    reSetBindTwtterId({
+                        postId: state.postId || '',
+                        tweetId: state.tweetId || ''
+                    })
+                }
+            }
+        })
+    }
+
+})
+
+const handleStatus = () => {
+    // 如果 夺宝状态 = 未开始
+    //   显示未开始页面
+    // 如果 夺宝状态 = 进行中
+    //   如果 夺宝参与状态 = 未参与夺宝
+    //      显示封面页
+    //   如果 夺宝参与状态 = 已参与夺宝
+    //      显示邀请页
+    // 如果 夺宝状态 = 已结束
+    //  显示结束页面
+
+    let { status, joinStatus } = state.detail || {}
+
+    // 如果 夺宝状态 = 未开始
+    if (status == 0) {
+        state.page = '封面页'
+    }
+    // 如果 夺宝状态 = 进行中
+    else if (status == 1) {
+        // 如果 夺宝参与状态 = 未参与夺宝
+        if (joinStatus == 0) {
+            // 显示封面页
+            state.page = '封面页'
+            state.cover_status = '有邀请人'
+            // state.cover_status = '无邀请人'
+            // state.cover_status = '奖励已被领光'
+        }
+        // 如果 夺宝参与状态 = 已参与夺宝
+        else if (joinStatus == 1) {
+            state.page = '邀请页'
+        }
+    }
+    // 如果 夺宝状态 = 已结束
+    else {
+        state.page = '封面页'
+        state.cover_status = '奖励已被领光'
+    }
+}
+</script>
+<style lang="scss" >
+html,
+body {
+    margin: 0;
+    padding: 0;
+}
+</style>

+ 358 - 0
src/view/iframe/treasure-hunt/invite.vue

@@ -0,0 +1,358 @@
+<template>
+    <!-- 邀请页 -->
+    <div class="area-process">
+        <v-head :left-data="state.detail.postUserInfo || null" :rightData="state.detail.remainAmountValue"></v-head>
+        <div class="box-process">
+            <div class="item" v-for="item, i in state.boxs">
+                <hover-tip :txt="item.txt" v-show="item.show || item.openStatus" :icon="item.hover_icon"></hover-tip>
+                <img :src="item.icon" alt="" @mouseenter="mouseItem(i)" @mouseleave="mouseLeaveItem(i)" />
+                <img :src="require('@/assets/img/icon-flash-active.png')" alt="" class="flash"
+                    v-if="item.openStatus == 0 && item.taskFinishStatus == 1" />
+            </div>
+            <div class="line">
+                <div class="full" ref="line_full"></div>
+            </div>
+        </div>
+        <div class="area-success-message" @mouseover="mouseOver" @mouseleave="mouseLeave">
+            <div class="content-success-message" ref="content_success_message">
+                <div class="success-message" v-for="item, index in state.success_message_list" :key="index">
+                    <img :src="item.userInfo.avatarUrl" alt="" />
+                    <span>{{ item.userInfo.nickName }} </span> &nbsp;
+                    <span>Opened Treasure Chest</span>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="area-nav">
+        <div class="item" :class="{ active: state.tab_index == i }" @click="state.tab_index = i"
+            v-for="item, i in state.tabs">
+            {{ item.txt }}
+        </div>
+    </div>
+    <div class="area-info">
+        <invite-friends v-show="state.tab_index == 0"></invite-friends>
+        <invite-list v-show="state.tab_index == 1"></invite-list>
+    </div>
+
+</template>
+<script setup>
+import { reactive, ref, onMounted, watch, inject } from 'vue'
+import { receiveList } from '@/http/treasure.js'
+import VHead from '@/view/iframe/treasure-hunt/components/head.vue'
+import InviteList from '@/view/iframe/treasure-hunt/components/invite-list.vue'
+import HoverTip from '@/view/iframe/treasure-hunt/components/hover-tip.vue'
+import InviteFriends from '@/view/iframe/treasure-hunt/components/invite-friends.vue'
+let content_success_message = ref(null)
+let state = inject('state')
+
+// ---- 走马灯
+state.success_message_list = []
+
+
+// ---- box 区域
+let silver_close_box = require('@/assets/img/icon-silver-close-box.png')
+let silver_open_box = require('@/assets/img/icon-silver-open-box.png')
+let gold_open_box = require('@/assets/img/icon-gold-open-box.png')
+let gold_close_box = require('@/assets/img/icon-gold-close-box.png')
+
+
+// ---- tab区域 ----
+state.tab_index = 0
+state.tabs = [{
+    txt: 'invite friends'
+}, {
+    txt: 'invited'
+}]
+
+
+watch(state, () => {
+    if (content_success_message && content_success_message.value) {
+        let dom = content_success_message.value
+        let width = dom.clientWidth
+        let s = parseInt(width / 80)
+        dom.style.animationDuration = s + 's'
+    }
+})
+
+state.boxs = []
+
+let line_full = ref(null)
+onMounted(() => {
+    state.tabs[1].txt = `invited(${state.detail.inviteCount})`
+
+    // line_full
+    state.detail.treasureRecords.forEach((item, index) => {
+        if (item.openStatus == 0) {
+            item.hover_icon = require('@/assets/svg/icon-user.svg')
+            // 最后一条
+            if ((index + 1) == state.detail.treasureRecords.length) {
+                item.icon = gold_close_box
+            } else {
+                item.icon = silver_close_box
+            }
+        } else {
+            item.icon = silver_open_box
+            item.hover_icon = require('@/assets/svg/icon-green-yes.svg')
+            // 最后一条
+            if ((index + 1) == state.detail.treasureRecords.length) {
+                item.icon = gold_open_box
+            } else {
+                item.icon = silver_open_box
+            }
+        }
+        item.txt = item.inviteProgress
+        state.boxs.push(item)
+    })
+
+    receiveList({
+        params: {
+            postId: state.postId,
+            pageNum: 1,
+            pageSize: 10,
+        }
+    }).then((res) => {
+        if (res.code == 0) {
+            state.success_message_list = res.data
+        }
+    })
+    btnStatus()
+})
+
+const setLineFull = (n) => {
+    if (n == 0) {
+        line_full.value.style.width = '0px'
+    } else if (n == 1) {
+        line_full.value.style.width = '80px'
+    }
+}
+
+const btnStatus = () => {
+    // 没有邀请的时候
+    if (state.boxs[0].taskFinishStatus == 0) {
+        state.open_btn.txt = 'Invite a friend to open the chest'
+        state.open_btn.disabled = true
+        setLineFull(0)
+    }
+    // 第二个箱子未完成 
+    if (state.boxs[1].taskFinishStatus == 0) {
+        state.open_btn.txt = 'Invite 3 friends to open the chest'
+        state.open_btn.disabled = true
+    }
+    // 第三个箱子未完成 
+    if (state.boxs[1].taskFinishStatus == 0) {
+        state.open_btn.txt = 'Invite 6 friends to open the chest'
+        state.open_btn.disabled = true
+    }
+
+    let open_num = 0
+    // 有打开的箱子 Open the chest 
+    state.boxs.forEach(item => {
+        if (item.taskFinishStatus == 1 && item.openStatus == 0) {
+            state.open_btn.txt = 'Open the Chest'
+            state.open_btn.disabled = false
+        }
+
+        // 三个箱子全部打开了 
+        if (item.openStatus == 1) {
+            open_num++
+        }
+        if (open_num == state.boxs.length) {
+            state.open_btn.txt = 'All Chests are Open'
+            state.open_btn.disabled = true
+        }
+    })
+}
+
+const mouseItem = (i) => {
+    state.boxs[i].show = true
+}
+const mouseLeaveItem = (i) => {
+    state.boxs[i].show = false
+}
+
+const mouseOver = () => {
+    if (content_success_message && content_success_message.value && content_success_message.value.style) {
+        content_success_message.value.style.animationPlayState = 'paused'
+    }
+}
+const mouseLeave = () => {
+    if (content_success_message && content_success_message.value && content_success_message.value.style) {
+        content_success_message.value.style.animationPlayState = 'running'
+    }
+}
+
+</script>
+<style lang="scss"  scoped>
+.area-process {
+    width: 375px;
+    height: 170px;
+    background: linear-gradient(179.96deg, #735931 0.04%, #0E0803 53.64%);
+    position: relative;
+
+    .box-process {
+        width: 350px;
+        height: 90px;
+        margin: 0 auto;
+        display: flex;
+        align-items: center;
+        position: absolute;
+        top: 32px;
+        left: 13px;
+
+        img {
+            width: 60px;
+            height: 60px;
+            z-index: 2;
+        }
+
+        .item {
+            z-index: 2;
+            display: flex;
+            justify-content: center;
+            position: relative;
+
+            .flash {
+                position: absolute;
+                top: 0;
+                left: 0;
+                z-index: 0;
+                width: 100%;
+                height: 100%;
+            }
+        }
+
+        .item:nth-child(1) {
+
+            margin-left: 56px;
+        }
+
+        .item:nth-child(2) {
+            width: 60px;
+            height: 60px;
+            margin-left: 40px;
+        }
+
+        .item:nth-child(3) {
+            img {
+                width: 90px;
+                height: 90px;
+            }
+
+            margin-left: 40px;
+        }
+
+        .line {
+            width: 300px;
+            height: 4px;
+            background: rgba(255, 210, 59, 0.2);
+            position: absolute;
+            border-radius: 100px;
+            overflow: hidden;
+            left: 13px;
+            top: 45px;
+
+            .full {
+                position: absolute;
+                left: 0;
+                top: 0;
+                height: 4px;
+                width: 80px;
+                background: #FFD23B;
+            }
+        }
+
+    }
+
+    .area-success-message {
+        height: 30px;
+        width: 100%;
+        position: absolute;
+        bottom: 13px;
+        overflow: hidden;
+
+        .content-success-message {
+
+            width: fit-content;
+            display: flex;
+            animation: rolling 18s linear infinite;
+
+            .success-message {
+                cursor: pointer;
+                width: fit-content;
+                height: 30px;
+                padding: 0 9px;
+                border-radius: 100px;
+                background: rgba(255, 255, 255, 0.1);
+                display: flex;
+                align-items: center;
+                overflow: hidden;
+                margin-right: 15px;
+
+                img {
+                    width: 20px;
+                    height: 20px;
+                    border-radius: 100px;
+                    margin-right: 8px;
+
+                }
+
+                span {
+                    font-style: normal;
+                    font-weight: 500;
+                    font-size: 12px;
+                    line-height: 14px;
+                    white-space: nowrap;
+                }
+
+                span:nth-child(2) {
+                    color: #1D9BF0;
+                }
+
+                span:nth-child(3) {
+                    color: #A8A8A8;
+                }
+            }
+        }
+    }
+}
+
+@keyframes rolling {
+    from {
+        transform: translateX(0);
+    }
+
+    to {
+        transform: translateX(-50%);
+    }
+}
+
+.area-nav {
+    width: 375px;
+    height: 38px;
+    display: flex;
+
+    .item {
+        user-select: none;
+        color: #757575;
+        background: #F0F0F0;
+        text-align: center;
+        width: 50%;
+        font-weight: 500;
+        font-size: 14px;
+        line-height: 38px;
+        cursor: pointer;
+    }
+
+    .active {
+        background: #FFFFFF;
+        color: #000000;
+    }
+}
+
+.area-info {
+    width: 375px;
+
+
+}
+</style>

+ 116 - 0
src/view/iframe/treasure-hunt/result.vue

@@ -0,0 +1,116 @@
+<template>
+    <!-- 开奖页 -->
+    <div class="content">
+        <img :src="require('@/assets/img/icon-silver-open-box.png')" alt="" />
+        <div class="mark">
+            <p>You already followed Younan111 </p>
+            <!-- 新粉 -->
+            <template v-if="state.start_task.amountValue > 0">
+                <p>You Win</p>
+                <div class="money">${{ state.start_task.usdAmountValue }}</div>
+                <div class="mark2">
+                    <img :src="state.start_task.currencyIcon" alt="" />
+                    <div>{{ state.start_task.amountValue }} {{ state.start_task.currencySymbol }} stored in your DeNet
+                        account</div>
+                </div>
+            </template>
+            <!-- 老粉不给钱 -->
+            <template v-else>
+                <p>Only new followers open silver chest</p>
+                <p class="txt">Invite people to</p>
+                <p class="txt">open golden chest!</p>
+            </template>
+        </div>
+        <v-btn :txt="'Invite friends for more treasures'" :font-size="'16px'" class="btn"
+            v-if="state.start_task.amountValue > 0" @onClick="clickBtn"></v-btn>
+        <v-btn :txt="'Invite'" :font-size="'16px'" class="btn" v-else @onClick="clickBtn"></v-btn>
+    </div>
+</template>
+<script setup>
+import { inject } from 'vue'
+import VBtn from '@/view/iframe/treasure-hunt/components/btn.vue'
+let state = inject('state')
+
+const clickBtn = () => {
+    state.page = '邀请页'
+}
+
+</script>
+<style lang="scss" scoped>
+.content {
+    width: 375px;
+    height: 500px;
+    background: linear-gradient(179.96deg, #876635 20.15%, #31251A 44.61%, #24180C 78.18%);
+    text-align: center;
+    position: relative;
+
+    img {
+        margin-top: 15px;
+    }
+
+    .mark {
+        position: absolute;
+        top: 246px;
+        width: 375px;
+
+        p {
+            margin: 0;
+            padding: 0;
+            text-align: center;
+
+        }
+
+        p:nth-child(1) {
+            color: #A9A49F;
+            font-weight: 400;
+            font-size: 12px;
+            margin-bottom: 10px;
+        }
+
+        p:nth-child(2) {
+            margin-bottom: 10px;
+            color: #FFFFFF;
+            font-weight: 800;
+            font-size: 18px;
+        }
+
+        .txt {
+            color: #FFC83A;
+            font-weight: 800;
+            font-size: 24px;
+
+        }
+
+        .money {
+            color: #FFC83A;
+            font-weight: 800;
+            font-size: 34px;
+        }
+
+        .mark2 {
+            margin-top: 10px;
+            display: flex;
+            justify-content: center;
+
+            img {
+                width: 17px;
+                height: 17px;
+                margin: 0;
+            }
+
+            div {
+                color: #A9A49F;
+                font-weight: 400;
+                font-size: 12px;
+
+            }
+        }
+    }
+
+    .btn {
+        position: absolute;
+        bottom: 25px;
+        left: 15px;
+    }
+}
+</style>

Some files were not shown because too many files changed in this diff