zhangwei пре 2 година
родитељ
комит
9730e1515a
78 измењених фајлова са 3100 додато и 128 уклоњено
  1. 5 1
      package.json
  2. BIN
      src/assets/img/icon-box2.png
  3. BIN
      src/assets/img/icon-card-cover-treasure-big.png
  4. BIN
      src/assets/img/icon-flash-active.png
  5. BIN
      src/assets/img/icon-gold-close-box.png
  6. BIN
      src/assets/img/icon-gold-open-box-big.png
  7. BIN
      src/assets/img/icon-gold-open-box.png
  8. BIN
      src/assets/img/icon-silver-close-box.png
  9. BIN
      src/assets/img/icon-silver-open-box-big.png
  10. BIN
      src/assets/img/icon-silver-open-box.png
  11. BIN
      src/assets/img/icon-treasure-box.png
  12. BIN
      src/assets/img/img-preview-treasure-link-bg.png
  13. 2 0
      src/assets/svg/icon-big-treasure.svg
  14. 2 0
      src/assets/svg/icon-btn-box.svg
  15. 2 0
      src/assets/svg/icon-card-cover-treasure-small.svg
  16. 1 0
      src/assets/svg/icon-card-cover-treasure-tasks.svg
  17. 1 0
      src/assets/svg/icon-create-nfts.svg
  18. 3 0
      src/assets/svg/icon-custom-cover-btn.svg
  19. 3 0
      src/assets/svg/icon-form-amount.svg
  20. 4 0
      src/assets/svg/icon-form-cost.svg
  21. 3 0
      src/assets/svg/icon-form-custom-cover.svg
  22. 1 0
      src/assets/svg/icon-form-follow.svg
  23. 3 0
      src/assets/svg/icon-form-task.svg
  24. 8 0
      src/assets/svg/icon-form-time.svg
  25. 1 0
      src/assets/svg/icon-form-winners.svg
  26. 4 0
      src/assets/svg/icon-green-yes.svg
  27. 3 0
      src/assets/svg/icon-invite-list.svg
  28. 1 0
      src/assets/svg/icon-invite.svg
  29. 3 0
      src/assets/svg/icon-post-edit-luck.svg
  30. 3 0
      src/assets/svg/icon-post-edit-open.svg
  31. 4 0
      src/assets/svg/icon-post-lock.svg
  32. 3 0
      src/assets/svg/icon-refresh-treasure.svg
  33. 5 0
      src/assets/svg/icon-silver-open-box.svg
  34. 1 0
      src/assets/svg/icon-small-treasure.svg
  35. 1 0
      src/assets/svg/icon-three-line.svg
  36. 3 0
      src/assets/svg/icon-tip-arrow.svg
  37. 5 0
      src/assets/svg/icon-transaction-s.svg
  38. 4 0
      src/assets/svg/icon-treasure-return.svg
  39. 3 0
      src/assets/svg/icon-while-user.svg
  40. 3 0
      src/assets/svg/icon-while-yes.svg
  41. 0 19
      src/assets/svg/img-A0.svg
  42. 0 19
      src/assets/svg/img-A1.svg
  43. 0 44
      src/assets/svg/img-B0.svg
  44. 0 44
      src/assets/svg/img-B1.svg
  45. 3 0
      src/assets/svg/img-LT0.svg
  46. 3 0
      src/assets/svg/img-LT1.svg
  47. 3 0
      src/assets/svg/img-P1.svg
  48. 3 0
      src/assets/svg/img-P2.svg
  49. 3 0
      src/assets/svg/img-P3.svg
  50. 2 0
      src/assets/svg/img-T0.svg
  51. 2 0
      src/assets/svg/img-T1.svg
  52. 11 0
      src/assets/svg/img-preview-treasure-01-bg.svg
  53. 77 0
      src/http/treasure.js
  54. 9 0
      src/http/treasureApi.js
  55. 31 0
      src/log-center/autoLog/click.js
  56. 23 0
      src/log-center/autoLog/index.js
  57. 50 0
      src/log-center/autoLog/show.js
  58. 9 0
      src/log-center/log.js
  59. 160 0
      src/log-center/logEnum.js
  60. 120 0
      src/log-center/logger.js
  61. 3 1
      src/main.js
  62. 151 0
      src/pages/treasure-hunt/components/btn.vue
  63. 74 0
      src/pages/treasure-hunt/components/dialog.vue
  64. 76 0
      src/pages/treasure-hunt/components/head.vue
  65. 57 0
      src/pages/treasure-hunt/components/hover-tip.vue
  66. 227 0
      src/pages/treasure-hunt/components/invite-friends.vue
  67. 214 0
      src/pages/treasure-hunt/components/invite-list.vue
  68. 156 0
      src/pages/treasure-hunt/components/open-box.vue
  69. 83 0
      src/pages/treasure-hunt/components/toast.vue
  70. 419 0
      src/pages/treasure-hunt/cover.vue
  71. 304 0
      src/pages/treasure-hunt/index.vue
  72. 412 0
      src/pages/treasure-hunt/invite.vue
  73. 168 0
      src/pages/treasure-hunt/result.vue
  74. 6 0
      src/router/index.js
  75. 50 0
      src/types/global.js
  76. 1 0
      src/types/index.js
  77. 1 0
      vue.config.js
  78. 104 0
      yarn.lock

+ 5 - 1
package.json

@@ -13,8 +13,12 @@
   "dependencies": {
     "@vue/composition-api": "^1.7.0",
     "axios": "^0.27.2",
+    "clipboard": "^2.0.11",
+    "@sentry/tracing": "^7.5.1",
+    "@sentry/vue": "^7.5.1",
     "core-js": "^3.8.3",
     "element-plus": "2.1.10",
+    "moment": "^2.29.4",
     "vue": "^3.2.13",
     "vue-router": "^4.1.2"
   },
@@ -51,4 +55,4 @@
     "not dead",
     "not ie 11"
   ]
-}
+}

BIN
src/assets/img/icon-box2.png


BIN
src/assets/img/icon-card-cover-treasure-big.png


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-big.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-big.png


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


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


BIN
src/assets/img/img-preview-treasure-link-bg.png


Разлика између датотеке није приказан због своје велике величине
+ 2 - 0
src/assets/svg/icon-big-treasure.svg


Разлика између датотеке није приказан због своје велике величине
+ 2 - 0
src/assets/svg/icon-btn-box.svg


Разлика између датотеке није приказан због своје велике величине
+ 2 - 0
src/assets/svg/icon-card-cover-treasure-small.svg


Разлика између датотеке није приказан због своје велике величине
+ 1 - 0
src/assets/svg/icon-card-cover-treasure-tasks.svg


Разлика између датотеке није приказан због своје велике величине
+ 1 - 0
src/assets/svg/icon-create-nfts.svg


+ 3 - 0
src/assets/svg/icon-custom-cover-btn.svg

@@ -0,0 +1,3 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M17.8749 11.7838L17.1198 12.5169L15.6097 11.0508L16.3648 10.3177C16.4403 10.2444 16.5158 10.1711 16.6668 10.1711C16.7423 10.1711 16.8933 10.2444 16.9688 10.3177L17.9504 11.2707C18.0259 11.4173 18.0259 11.6372 17.8749 11.7838ZM10.5507 15.8891V17.4286H12.1364L16.7423 12.9568L15.2321 11.4906L10.5507 15.8891ZM16.5913 4.96617C16.5913 4.15977 15.9117 3.5 15.0811 3.5H4.51014C3.67956 3.5 3 4.15977 3 4.96617V15.2293C3 16.0357 3.67956 16.6955 4.51014 16.6955H9.04057V15.3026L9.87115 14.4962H4.51014L7.15289 11.1974L9.04057 13.3966L11.6833 10.0977L12.8914 11.6372L16.5913 7.9718V4.96617Z" fill="#2F2F2F"/>
+</svg>

+ 3 - 0
src/assets/svg/icon-form-amount.svg

@@ -0,0 +1,3 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4 12.0058V13.5734C4 14.8694 6.6875 15.9231 10 15.9231C13.3125 15.9231 16 14.8694 16 13.5734V12.0058C14.7094 13.0741 12.35 13.5734 10 13.5734C7.65 13.5734 5.29062 13.0741 4 12.0058ZM4 8.15454V10.049C4 11.3449 6.6875 12.3986 10 12.3986C13.3125 12.3986 16 11.3449 16 10.049V8.15454C14.7094 9.4028 12.3469 10.049 10 10.049C7.65313 10.049 5.29062 9.4028 4 8.15454ZM10 3C6.6875 3 4 4.31434 4 5.93706C4 7.55979 6.6875 8.87413 10 8.87413C13.3125 8.87413 16 7.55979 16 5.93706C16 4.31434 13.3125 3 10 3Z" fill="#2F2F2F"/>
+</svg>

+ 4 - 0
src/assets/svg/icon-form-cost.svg

@@ -0,0 +1,4 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11 13.575V14.5455C11 15.3477 12.5677 16 14.5 16C16.4323 16 18 15.3477 18 14.5455V13.575C17.2471 14.2364 15.8708 14.5455 14.5 14.5455C13.1292 14.5455 11.7529 14.2364 11 13.575ZM11 11.1909V12.3636C11 13.1659 12.5677 13.8182 14.5 13.8182C16.4323 13.8182 18 13.1659 18 12.3636V11.1909C17.2471 11.9636 15.869 12.3636 14.5 12.3636C13.131 12.3636 11.7529 11.9636 11 11.1909ZM14.5 8C12.5677 8 11 8.81364 11 9.81818C11 10.8227 12.5677 11.6364 14.5 11.6364C16.4323 11.6364 18 10.8227 18 9.81818C18 8.81364 16.4323 8 14.5 8Z" fill="#2F2F2F"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M5.42061 15.1282H5.4046C5.36893 15.2284 5.31128 15.3204 5.23468 15.397C5.09904 15.5326 4.91508 15.6088 4.72326 15.6088C4.53144 15.6088 4.34747 15.5326 4.21184 15.397C4.0762 15.2614 4 15.0774 4 14.8856C4 14.8202 4.01049 14.7614 4.02325 14.7114C4.33797 12.5974 5.72065 10.833 7.60544 9.98625C6.73631 9.26192 6.18262 8.1708 6.18262 6.95024C6.18262 4.76854 7.95117 3 10.1329 3C11.9921 3 13.5513 4.28442 13.9716 6.01437C13.5574 6.03701 13.1526 6.08619 12.7643 6.16045C12.6313 5.7191 12.3901 5.32421 12.0716 5.00682C12.3898 5.32432 12.6308 5.71925 12.7636 6.16059C12.6833 6.17595 12.6037 6.19239 12.5248 6.20988C12.2083 5.18971 11.2556 4.44651 10.1329 4.44651C8.75252 4.44651 7.62914 5.5699 7.62914 6.95024C7.62914 7.92308 8.18723 8.76842 9 9.18266V11.0304C7.15844 11.4847 5.73735 13.02 5.45165 14.9216L5.42061 15.1282ZM4.24375 14.8867C4.24375 15.0139 4.29418 15.1359 4.38397 15.226L4.38341 15.2254C4.29328 15.1353 4.24264 15.013 4.24264 14.8856C4.24264 14.842 4.25033 14.8004 4.26132 14.7602C4.56633 12.6796 5.9484 10.9491 7.82382 10.1557C7.91323 10.1179 8.00376 10.0822 8.09535 10.0487L8.09682 10.0497C6.08391 10.7858 4.582 12.5814 4.26243 14.7613C4.25144 14.8016 4.24375 14.8432 4.24375 14.8867Z" fill="#2F2F2F"/>
+</svg>

+ 3 - 0
src/assets/svg/icon-form-custom-cover.svg

@@ -0,0 +1,3 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M17.8749 11.7838L17.1198 12.5169L15.6097 11.0508L16.3648 10.3177C16.4403 10.2444 16.5158 10.1711 16.6668 10.1711C16.7423 10.1711 16.8933 10.2444 16.9688 10.3177L17.9504 11.2707C18.0259 11.4173 18.0259 11.6372 17.8749 11.7838ZM10.5507 15.8891V17.4286H12.1364L16.7423 12.9568L15.2321 11.4906L10.5507 15.8891ZM16.5913 4.96617C16.5913 4.15977 15.9117 3.5 15.0811 3.5H4.51014C3.67956 3.5 3 4.15977 3 4.96617V15.2293C3 16.0357 3.67956 16.6955 4.51014 16.6955H9.04057V15.3026L9.87115 14.4962H4.51014L7.15289 11.1974L9.04057 13.3966L11.6833 10.0977L12.8914 11.6372L16.5913 7.9718V4.96617Z" fill="#2F2F2F"/>
+</svg>

Разлика између датотеке није приказан због своје велике величине
+ 1 - 0
src/assets/svg/icon-form-follow.svg


+ 3 - 0
src/assets/svg/icon-form-task.svg

@@ -0,0 +1,3 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M4.267 4.82609L4.27273 15.7875L15.7324 15.7826L15.7273 4.82122L13.8182 4.82609V5.4287C13.819 5.75195 13.6858 6.06233 13.4476 6.29171C13.2095 6.52109 12.886 6.65072 12.548 6.65217H7.452C6.75136 6.65217 6.18182 6.10313 6.18182 5.4287V4.82609H4.267ZM12.548 5.43478L12.5455 4.22348L7.452 4.21739L7.45455 4.22348V5.4287L12.548 5.43478ZM13.64 3.6087H15.7241C16.0613 3.60789 16.3851 3.73512 16.6243 3.96246C16.8635 4.1898 16.9987 4.49866 17 4.82122V15.7875C16.9995 16.1089 16.8658 16.417 16.6282 16.6443C16.3905 16.8716 16.0684 16.9995 15.7324 17H4.26764C3.56891 17 3 16.4558 3 15.7875V4.82122C3.00017 4.49979 3.13369 4.19157 3.37124 3.96423C3.6088 3.73689 3.93096 3.60902 4.267 3.6087H6.36C6.58082 3.24591 6.98491 3 7.452 3H12.548C13.0151 3 13.4198 3.24652 13.64 3.6087ZM9.17585 11.4432L11.9743 8.20321C12.0799 8.08088 12.2271 8.00801 12.3835 8.00062C12.5399 7.99324 12.6926 8.05195 12.8081 8.16383C12.9236 8.27572 12.9924 8.43162 12.9994 8.59723C13.0064 8.76285 12.951 8.92461 12.8453 9.04694L9.60606 12.7969C9.49393 12.9263 9.33577 13 9.17054 13H9.167C9.08447 12.9995 9.00296 12.9806 8.92774 12.9447C8.85252 12.9087 8.78526 12.8565 8.7303 12.7913L7.14992 10.9163C7.04568 10.7926 6.99209 10.6302 7.00095 10.4647C7.0098 10.2992 7.08037 10.1442 7.19713 10.0338C7.31389 9.92341 7.46727 9.86665 7.62354 9.87603C7.77981 9.88541 7.92616 9.96014 8.0304 10.0838L9.17585 11.4432Z" fill="#2F2F2F"/>
+</svg>

+ 8 - 0
src/assets/svg/icon-form-time.svg

@@ -0,0 +1,8 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<mask id="path-1-outside-1_24371_182246" maskUnits="userSpaceOnUse" x="2" y="2" width="16" height="16" fill="black">
+<rect fill="white" x="2" y="2" width="16" height="16"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M3 10C3 6.14 6.14 3 10 3C13.86 3 17 6.14 17 10C17 13.86 13.86 17 10 17C6.14 17 3 13.86 3 10ZM4.0045 10C4.0045 13.3055 6.694 15.9955 10 15.9955C13.3055 15.9955 15.9955 13.3055 15.9955 10C15.9955 6.694 13.3055 4.0045 10 4.0045C6.694 4.0045 4.0045 6.694 4.0045 10ZM10.001 9.99788H12.5005C12.778 9.99788 13.0025 10.2204 13.0025 10.4979C13.0025 10.7754 12.778 10.9979 12.5005 10.9979H9.50104C9.22404 10.9979 9.00104 10.7754 9.00104 10.4979V6.49887C9.00104 6.22187 9.22354 5.99688 9.50104 5.99688C9.77854 5.99688 10.001 6.22137 10.001 6.49887V9.99788Z"/>
+</mask>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M3 10C3 6.14 6.14 3 10 3C13.86 3 17 6.14 17 10C17 13.86 13.86 17 10 17C6.14 17 3 13.86 3 10ZM4.0045 10C4.0045 13.3055 6.694 15.9955 10 15.9955C13.3055 15.9955 15.9955 13.3055 15.9955 10C15.9955 6.694 13.3055 4.0045 10 4.0045C6.694 4.0045 4.0045 6.694 4.0045 10ZM10.001 9.99788H12.5005C12.778 9.99788 13.0025 10.2204 13.0025 10.4979C13.0025 10.7754 12.778 10.9979 12.5005 10.9979H9.50104C9.22404 10.9979 9.00104 10.7754 9.00104 10.4979V6.49887C9.00104 6.22187 9.22354 5.99688 9.50104 5.99688C9.77854 5.99688 10.001 6.22137 10.001 6.49887V9.99788Z" fill="#2F2F2F"/>
+<path d="M10.001 9.99788H9.80104V10.1979H10.001V9.99788ZM10 2.8C6.02954 2.8 2.8 6.02954 2.8 10H3.2C3.2 6.25046 6.25046 3.2 10 3.2V2.8ZM17.2 10C17.2 6.02954 13.9705 2.8 10 2.8V3.2C13.7495 3.2 16.8 6.25046 16.8 10H17.2ZM10 17.2C13.9705 17.2 17.2 13.9705 17.2 10H16.8C16.8 13.7495 13.7495 16.8 10 16.8V17.2ZM2.8 10C2.8 13.9705 6.02954 17.2 10 17.2V16.8C6.25046 16.8 3.2 13.7495 3.2 10H2.8ZM10 15.7955C6.80447 15.7955 4.2045 13.1951 4.2045 10H3.8045C3.8045 13.4159 6.58353 16.1955 10 16.1955V15.7955ZM15.7955 10C15.7955 13.195 13.195 15.7955 10 15.7955V16.1955C13.416 16.1955 16.1955 13.416 16.1955 10H15.7955ZM10 4.2045C13.1951 4.2045 15.7955 6.80447 15.7955 10H16.1955C16.1955 6.58353 13.4159 3.8045 10 3.8045V4.2045ZM4.2045 10C4.2045 6.80446 6.80446 4.2045 10 4.2045V3.8045C6.58354 3.8045 3.8045 6.58354 3.8045 10H4.2045ZM12.5005 9.79788H10.001V10.1979H12.5005V9.79788ZM13.2025 10.4979C13.2025 10.1089 12.8875 9.79788 12.5005 9.79788V10.1979C12.6686 10.1979 12.8025 10.3319 12.8025 10.4979H13.2025ZM12.5005 11.1979C12.8875 11.1979 13.2025 10.8869 13.2025 10.4979H12.8025C12.8025 10.6639 12.6686 10.7979 12.5005 10.7979V11.1979ZM9.50104 11.1979H12.5005V10.7979H9.50104V11.1979ZM8.80104 10.4979C8.80104 10.886 9.11372 11.1979 9.50104 11.1979V10.7979C9.33437 10.7979 9.20104 10.6648 9.20104 10.4979H8.80104ZM8.80104 6.49887V10.4979H9.20104V6.49887H8.80104ZM9.50104 5.79688C9.11192 5.79688 8.80104 6.11259 8.80104 6.49887H9.20104C9.20104 6.33116 9.33517 6.19688 9.50104 6.19688V5.79688ZM10.201 6.49887C10.201 6.11196 9.89004 5.79688 9.50104 5.79688V6.19688C9.66705 6.19688 9.80104 6.33079 9.80104 6.49887H10.201ZM10.201 9.99788V6.49887H9.80104V9.99788H10.201Z" fill="#2F2F2F" mask="url(#path-1-outside-1_24371_182246)"/>
+</svg>

Разлика између датотеке није приказан због своје велике величине
+ 1 - 0
src/assets/svg/icon-form-winners.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>

Разлика између датотеке није приказан због своје велике величине
+ 3 - 0
src/assets/svg/icon-invite-list.svg


Разлика између датотеке није приказан због своје велике величине
+ 1 - 0
src/assets/svg/icon-invite.svg


+ 3 - 0
src/assets/svg/icon-post-edit-luck.svg

@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M5.05497 9.83636V7.82727C5.05497 6.12727 6.29133 4.89091 7.99133 4.89091H9.2277C10.7732 4.89091 12.1641 6.12727 12.1641 7.82727V9.83636H13.555V7.82727C13.555 5.35455 11.5459 3.5 9.2277 3.5H7.99133C5.51861 3.5 3.66406 5.50909 3.66406 7.82727V9.83636H5.05497ZM14.3273 20.5003H2.89091C2.11818 20.5003 1.5 19.8821 1.5 19.1094V12.0003C1.5 11.2276 2.27273 10.6094 2.89091 10.6094H14.1727C15.1 10.6094 15.5636 11.2276 15.5636 12.0003V19.1094C15.7182 19.8821 15.1 20.5003 14.3273 20.5003ZM20.1177 20.5458L21.8908 18.7097L21.7607 11.2568C22.0825 11.0797 22.381 10.8534 22.6513 10.5734C23.3567 9.84292 23.743 8.86219 23.7253 7.84692C23.7076 6.83165 23.2873 5.865 22.5569 5.15962C21.8264 4.45425 20.8457 4.06793 19.8304 4.08565C18.8151 4.10337 17.8485 4.52368 17.1431 5.25412C16.4377 5.98455 16.0514 6.96528 16.0691 7.98056C16.0869 8.99583 16.5072 9.96248 17.2376 10.6679C17.5176 10.9382 17.8237 11.154 18.1516 11.3198L18.1871 13.359L19.5642 14.6888L18.2344 16.0658L19.6115 17.3957L18.2816 18.7727L20.1177 20.5458ZM19.8592 5.73542C20.1976 5.72951 20.5245 5.85828 20.768 6.09341C21.0115 6.32853 21.1516 6.65075 21.1575 6.98917C21.1634 7.3276 21.0346 7.65451 20.7995 7.89799C20.5644 8.14147 20.2422 8.28157 19.9038 8.28748C19.5653 8.29339 19.2384 8.16461 18.9949 7.92949C18.7515 7.69436 18.6114 7.37215 18.6055 7.03372C18.5995 6.6953 18.7283 6.36839 18.9634 6.12491C19.1986 5.88143 19.5208 5.74132 19.8592 5.73542Z" fill="#424B51"/>
+</svg>

+ 3 - 0
src/assets/svg/icon-post-edit-open.svg

@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11.5957 5.58382C12.4466 5.58382 13.2627 5.92184 13.8643 6.52352C14.466 7.1252 14.804 7.94125 14.804 8.79215C14.804 9.64306 14.466 10.4591 13.8643 11.0608C13.2627 11.6625 12.4466 12.0005 11.5957 12.0005C10.7448 12.0005 9.92875 11.6625 9.32707 11.0608C8.72539 10.4591 8.38737 9.64306 8.38737 8.79215C8.38737 7.94125 8.72539 7.1252 9.32707 6.52352C9.92875 5.92184 10.7448 5.58382 11.5957 5.58382ZM5.17904 8.00049C5.69237 8.00049 6.16904 8.13799 6.58154 8.38549C6.44404 9.69632 6.82904 10.998 7.61737 12.0155C7.15904 12.8955 6.24237 13.5005 5.17904 13.5005C4.44969 13.5005 3.75022 13.2108 3.23449 12.695C2.71877 12.1793 2.42904 11.4798 2.42904 10.7505C2.42904 10.0211 2.71877 9.32167 3.23449 8.80594C3.75022 8.29022 4.44969 8.00049 5.17904 8.00049ZM18.0124 7.85352C18.7417 7.85352 19.4412 8.14325 19.9569 8.65897C20.4726 9.1747 20.7624 9.87417 20.7624 10.6035C20.7624 11.3329 20.4726 12.0323 19.9569 12.5481C19.4412 13.0638 18.7417 13.3535 18.0124 13.3535C16.949 13.3535 16.0324 12.7485 15.574 11.8685C16.3624 10.851 16.7474 9.54935 16.6099 8.23852C17.0224 7.99102 17.499 7.85352 18.0124 7.85352ZM5.63737 16.791C5.63737 14.8935 8.30487 13.3535 11.5957 13.3535C14.8865 13.3535 17.554 14.8935 17.554 16.791V18.3952H5.63737V16.791ZM0.595703 18.3952V17.0202C0.595703 15.746 2.3282 14.6735 4.67487 14.3619C4.13404 14.9852 3.80404 15.8468 3.80404 16.791V18.3952H0.595703ZM22.5957 18.3952H19.3874V16.791C19.3874 15.8468 19.0574 14.9852 18.5165 14.3619C20.8632 14.6735 22.5957 15.746 22.5957 17.0202V18.3952Z" fill="#424B51"/>
+</svg>

+ 4 - 0
src/assets/svg/icon-post-lock.svg

@@ -0,0 +1,4 @@
+<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M72.0799 86.2749H27.4304C24.4135 86.2749 22 83.6649 22 80.4025V50.388C22 47.1256 25.0169 44.5156 27.4304 44.5156H71.4766C75.0968 44.5156 76.9069 47.1256 76.9069 50.388V80.4025C77.5103 83.6649 75.0968 86.2749 72.0799 86.2749Z" fill="#F3B14D"/>
+<path d="M35.1867 40.4444V33.8186C35.1867 26.8184 40.1651 21.7274 47.0105 21.7274H51.989C58.212 21.7274 63.8128 26.8184 63.8128 33.8186V40.4444H69.4135V33.8186C69.4135 23.6366 61.3236 16 51.989 16H47.0105C37.0536 16 29.5859 24.2729 29.5859 33.8186V40.4444H35.1867Z" fill="#AEB9C2"/>
+</svg>

+ 3 - 0
src/assets/svg/icon-refresh-treasure.svg

@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M17.65 6.35C16.2 4.9 14.21 4 12 4C9.87827 4 7.84344 4.84285 6.34315 6.34315C4.84285 7.84344 4 9.87827 4 12C4 14.1217 4.84285 16.1566 6.34315 17.6569C7.84344 19.1571 9.87827 20 12 20C15.73 20 18.84 17.45 19.73 14H17.65C16.83 16.33 14.61 18 12 18C10.4087 18 8.88258 17.3679 7.75736 16.2426C6.63214 15.1174 6 13.5913 6 12C6 10.4087 6.63214 8.88258 7.75736 7.75736C8.88258 6.63214 10.4087 6 12 6C13.66 6 15.14 6.69 16.22 7.78L13 11H20V4L17.65 6.35Z" fill="#636363"/>
+</svg>

Разлика између датотеке није приказан због своје велике величине
+ 5 - 0
src/assets/svg/icon-silver-open-box.svg


Разлика између датотеке није приказан због своје велике величине
+ 1 - 0
src/assets/svg/icon-small-treasure.svg


Разлика између датотеке није приказан због своје велике величине
+ 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>

+ 5 - 0
src/assets/svg/icon-transaction-s.svg

@@ -0,0 +1,5 @@
+<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="17" height="17" rx="8.5" transform="matrix(-1 0 0 1 17 0)" fill="#4CD6D6"/>
+<path d="M9.6611 5.16894L10.7911 4.5L12.6981 7.68105H4.5V6.37508H10.3846L9.6611 5.16688V5.16894Z" fill="white"/>
+<path d="M6.4148 12.4985L7.54684 11.8316L7.54581 11.8295L6.8213 10.6213H12.7079V9.31641H4.50781L6.4148 12.4985Z" fill="white"/>
+</svg>

+ 4 - 0
src/assets/svg/icon-treasure-return.svg

@@ -0,0 +1,4 @@
+<svg width="34" height="34" viewBox="0 0 34 34" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M0 17C0 7.61116 7.61116 0 17 0C26.3888 0 34 7.61116 34 17C34 26.3888 26.3888 34 17 34C7.61116 34 0 26.3888 0 17Z" fill="#FFB443"/>
+<path d="M14.7368 11.8V7L7 15.4L14.7368 23.8V18.88C20.2632 18.88 24.1316 20.8 26.8947 25C25.7895 19 22.4737 13 14.7368 11.8Z" fill="white"/>
+</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>

Разлика између датотеке није приказан због своје велике величине
+ 0 - 19
src/assets/svg/img-A0.svg


Разлика између датотеке није приказан због своје велике величине
+ 0 - 19
src/assets/svg/img-A1.svg


Разлика између датотеке није приказан због своје велике величине
+ 0 - 44
src/assets/svg/img-B0.svg


Разлика између датотеке није приказан због своје велике величине
+ 0 - 44
src/assets/svg/img-B1.svg


Разлика између датотеке није приказан због своје велике величине
+ 3 - 0
src/assets/svg/img-LT0.svg


Разлика између датотеке није приказан због своје велике величине
+ 3 - 0
src/assets/svg/img-LT1.svg


Разлика између датотеке није приказан због своје велике величине
+ 3 - 0
src/assets/svg/img-P1.svg


Разлика између датотеке није приказан због своје велике величине
+ 3 - 0
src/assets/svg/img-P2.svg


Разлика између датотеке није приказан због своје велике величине
+ 3 - 0
src/assets/svg/img-P3.svg


Разлика између датотеке није приказан због своје велике величине
+ 2 - 0
src/assets/svg/img-T0.svg


Разлика између датотеке није приказан због своје велике величине
+ 2 - 0
src/assets/svg/img-T1.svg


+ 11 - 0
src/assets/svg/img-preview-treasure-01-bg.svg

@@ -0,0 +1,11 @@
+<svg width="375" height="500" viewBox="0 0 375 500" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="375" height="500" fill="url(#paint0_linear_24395_274606)"/>
+<defs>
+<linearGradient id="paint0_linear_24395_274606" x1="187" y1="-7.83211e-09" x2="187.5" y2="500" gradientUnits="userSpaceOnUse">
+<stop offset="0.388068" stop-color="#25180D"/>
+<stop offset="0.554041" stop-color="#5E4025"/>
+<stop offset="0.616047" stop-color="#876635"/>
+<stop offset="0.716041" stop-color="#24180C"/>
+</linearGradient>
+</defs>
+</svg>

+ 77 - 0
src/http/treasure.js

@@ -0,0 +1,77 @@
+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,
+    });
+}
+
+
+export function bindRepost(params) {
+    return service({
+        url: `/post/treasure/invite/bindRepost`,
+        method: "post",
+        data: params,
+    });
+}

+ 9 - 0
src/http/treasureApi.js

@@ -0,0 +1,9 @@
+import { service } from "./request";
+
+export function upGainCalculate(params) {
+    return service({
+        url: `/post/treasure/upGainCalculate`,
+        method: 'post',
+        data: params
+    })
+}

+ 31 - 0
src/log-center/autoLog/click.js

@@ -0,0 +1,31 @@
+// 点击埋点自定义属性
+
+import { reportLog } from '../logger';
+import { getTargetElementWhenClick } from '@/uilts/help';
+import Report from "@/log-center/log";
+
+let clickDataMap = new Map();
+
+const clickHandle = (e) => { 
+    const target = getTargetElementWhenClick(e);
+    const { extParams, ...eventData } = clickDataMap.get(target?.denetClickLogkey);
+    return eventData && reportLog({
+        businessType: Report.businessType.buttonClick,
+        ...eventData
+    }, extParams)
+}
+
+const clickLog =  {
+    mounted: (el, binding) => { 
+        const { value } = binding;
+        el.denetClickLogkey = el.denetClickLogkey || Math.random().toString(36).slice(-6);
+        clickDataMap.set(el.denetClickLogkey, value);
+        el.addEventListener('click', clickHandle,true)
+    },
+    unmounted(el) { 
+        // remove EventListener
+        el.removeEventListener('click', clickHandle, true)
+    }
+}
+
+export default clickLog;

+ 23 - 0
src/log-center/autoLog/index.js

@@ -0,0 +1,23 @@
+// 埋点插件
+
+import { showReportDialog } from '@sentry/vue';
+import clickLog from './click';
+import ShowLogObserver from './show';
+
+const AutoLog = {};
+
+AutoLog.install = (app) => { 
+    app.directive('click-log', clickLog);
+    app.directive('show-log', {
+        mounted(el, binding) {
+            // 加载阶段设置随机key标记当前元素
+            el.denetShowLogkey = el.denetShowLogkey || Math.random().toString(36).slice(-6);
+            ShowLogObserver.add(el, binding);
+        },
+        unmounted(el) {
+            ShowLogObserver.remove(el);
+        },
+    });
+}
+
+export default AutoLog;

+ 50 - 0
src/log-center/autoLog/show.js

@@ -0,0 +1,50 @@
+import { reportLog } from '../logger';
+import { getActiveKeyAfterClick } from '@/uilts/help';
+import Report from "@/log-center/log"
+
+// 每个窗口共享一个Observer实例
+class ShowLogObserver { 
+    constructor() { 
+        this._observe = null;
+        this.showLogMap = new Map();
+        this.init();
+    }
+
+    init() {
+        this._observe = new IntersectionObserver((entries, observer) => {
+            entries.forEach((entry) => {
+                if (entry.intersectionRatio > 0.5) {
+                    this.report(entry);
+                    // show-log-once  ===  '1' &&  曝光之后取消观察
+                    if (entry?.target?.getAttribute('show-log-once') === '1') {
+                        this.remove(entry.target);
+                    }
+                }
+            })
+        }, {
+            root: null,
+            rootMargin: '0px',
+            threshold: 1
+        })
+    }
+
+    remove(el) { 
+        this._observe.unobserve(el);
+        this.showLogMap.delete(el.denetShowLogkey);
+    }
+
+    add(el, binding) { 
+        this._observe.observe(el);
+        this.showLogMap.set(el.denetShowLogkey, binding.value)
+    }
+
+    report(el) { 
+        const { extParams, ...eventData } = this.showLogMap.get(el?.target?.denetShowLogkey);
+        return eventData && reportLog({
+            businessType: Report.businessType.pageView,
+            ...eventData
+        }, extParams)
+    }
+}
+
+export default new ShowLogObserver();

+ 9 - 0
src/log-center/log.js

@@ -0,0 +1,9 @@
+import * as logger from './logger'
+import * as logEnum from './logEnum'
+
+
+
+export default {
+  ...logger,
+  ...logEnum
+}

+ 160 - 0
src/log-center/logEnum.js

@@ -0,0 +1,160 @@
+import { PlayType } from '@/types';
+
+export const logType = {
+    'denet': '150',//denet-event-log
+}
+
+export const redPacketType = {
+    nftSale: 2,
+    nftGroupSale: 3,
+    treasure: 4,
+    postEditor: 5,
+}
+
+export const businessType = {
+    buttonView: "buttonView",
+    buttonClick: "buttonClick",
+    // 页面曝光
+    pageView: "pageView",
+}
+
+export const objectType = {
+    buttonMain: "button-main",
+    buttonSecond: "button-second",
+    confirmButton: "confirm-button",
+    tweetPostBinded: "TweetPostBinded",
+    loginButton: "login-button",
+    withdrawButton: "withdraw-button",
+    topupButton: "topup-button",
+
+
+    getMoreGiveaway: "get-more-giveaway",
+    nextButton: "next-button",
+    openChestButton: "open-chest-button",
+    copyButton: "copy-button",
+    repostSuccess: "repostSuccess",
+    channelButton: 'channel-button',
+
+    //discord
+    getDiscordGuildNoData: 'get-discord-guild-no-data',
+    getDiscordGuildCatch: 'get-discord-guild-catch',
+    getDiscordGuildOpenApiNoData: 'get-discord-guild-openapi-no-data',
+    getDiscordGuildOpenApiCatch: 'get-discord-guild-openapi-catch',
+    saveDiscordGuildData: 'save-discord-guild-data',
+
+    // 按钮点击
+    open_button: 'open-button',
+    // 关注全部
+    follow_button: 'follow-button',
+    follow: 'follow',
+    retweet: 'retweet',
+    like: 'like',
+    comment_and_tag: 'comment-and-tag',
+    join_discord: 'discord',
+    share_facebook: 'share-facebook',
+
+    // 查看已领取红包列表
+    received_list: 'received-list',
+    // 点击检测任务
+    get_giveaway: 'get-giveaway',
+    // 成功领取到钱包
+    wallet_button: 'wallet-button',
+    // 卡片解析
+    parse_card_error: 'parse-card-error',
+    // 安装成功
+    chrome_extension_installed: 'chrome-extension-installed',
+    // 发送事件异常
+    chrome_extension_sendmessage_error: 'chrome-extension-sendmessage-error',
+    // background文件安装catch异常
+    background_function_catch: 'background-function-catch',
+    // background 文件chrome 函数 try
+    background_function_try:'background-function-try',
+    // create Nft
+    create_nfts_button: 'create-nfts-button',
+    confirm_transfer_button: 'confirm-transfer-button',
+    redeem_button: 'redeem-button',
+    buy_button: 'buy-button',
+    buy_nft_button: 'buy-nft-button',
+    custom_link_button: 'custom-link-button',
+    history_button: 'history-button',
+    app_button: 'app-button',
+    enter_url_button: 'enter-url-button',
+    top_right_button: 'top-right-button',
+    fullscreen_button: 'fullscreen-button',
+    encrypte_nft_button: 'encrypte-nft-button',
+}
+
+export const pageSource = {
+    mainPage: "main-page",
+    publisherDialog: "publisher-dialog",
+    currencySelectorPage: "currency-selector-page",
+    rechargePage: "recharge-page",
+    previewPage: "preview-page",
+    denetLogin: "denet-login",
+    denetHomePage: "denet-home-page",
+    denetWithdrawSelector: "denet-withdraw-selector",
+    denetWithdrawForm: "denet-withdraw-form",
+    denetWithdrawConfirm: "denet-withdraw-confirm",
+    denetTopupSelector: "denet-topup-selector",
+    denetMorePage: "denet-more-page",
+    denetSelector: "denet-selector",
+    nftShopPage: "nft-shop-page",
+    nftPreviewPage: "nft-preview-page",
+    denetNftTransferPage: "denet-nft-transfer-page",
+
+
+    newFansRewardPage: "new-fans-reward-page",
+    inviteFriendsPage: "invite-friends-page",
+    openTreasurePage: "open-treasure-page",
+    beenInvitedPage: "been-invited-page",
+    waitingLotteryPage: "waiting-lottery-page",
+    missingLotteryPage: "missing-lottery-page",
+    expiredPage: "expired-page",
+
+    // 待开红包页
+    pending_page: 'pending-page',
+    // 已领取任务页
+    task_page: 'task-page',
+    // 领取列表页
+    received_list_page: 'received-list-page',
+    // 红包过期
+    expired_page: 'expired-page',
+    // 红包被领完
+    been_claimed_page: 'been-claimed-page',
+    // 机器人检测未通过
+    robot_detection_failed_page: 'robot-detection-failed-page',
+    // 成功领取到钱包
+    received_success_page: 'received-success-page',
+    received_empty_rewards_page: 'received-empty-rewards-page',
+    pe_loading_page: 'pe-loading-page',
+    pe_display_page: 'pe-display-page',
+    nft_sales_window: 'nft-sales-window',
+    nft_post_page: 'nft-post-page',
+    main_page_dashboard: 'main-page-dashboard',
+    post_editor_guide_page_left: 'post-editor-guide-page-left',
+    post_editor_guide_page_right: 'post-editor-guide-page-right',
+    buy_posteditor_nft_dialog: 'buy-posteditor-nft-dialog',
+
+}
+
+export const extParams = {
+    success: 'success',
+    failure: 'failure'
+}
+
+export const bizType = {
+    Treasure: 0,
+    Lottery: 1,
+    RedPacket: 2,
+    ToolBox: 3,
+}
+
+export const getCurrentBizType = (type) => {
+  let obj = {};
+  obj[PlayType.common] = bizType.RedPacket;
+  obj[PlayType.lottery] = bizType.Lottery;
+  obj[PlayType.treasure] = bizType.Treasure;
+  obj[PlayType.postEditor] = bizType.ToolBox;
+
+  return obj[type];
+}

+ 120 - 0
src/log-center/logger.js

@@ -0,0 +1,120 @@
+import { logApi, reportFrontLogApi } from '@/http/logApi'
+import { getBrowser } from '@/uilts/help.js';
+import { logType } from './logEnum.js';
+import { getChromeStorage } from '@/uilts/chromeExtension'
+let userInfo = null;
+let mid = '';
+/**
+ * @eventData 以键值对存储,会在最终上报里解开的参数
+ * @extParams 最终上报到阿里云以json字符串存储的参数,如果extparams传入的不是obj会转换成obj
+ */
+export async function reportLog(eventData = {}, extParams = {}) {
+    // 过滤空值
+    let dataKey = Object.keys(eventData);
+    dataKey.forEach(key => {
+        if (eventData[key] === '') {
+            delete eventData[key]
+        }
+    })
+    // 2.reportLog 异常 存储到本地,再上报
+    try {
+        if (!userInfo) {
+            userInfo = await getChromeStorage('userInfo').catch((error) => {console.log(error) }) || null;
+        }
+        if (!mid) {
+            mid = await getChromeStorage('mid').catch((error) => {console.log(error) }) || '';
+        }
+        let isMobile = navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i);
+        let platform = isMobile ? `mobile` : `pc`;
+        let browser = getBrowser();
+        if (chrome && chrome.tabs) {
+            chrome.tabs.getCurrent((tab) => {
+                if (tab && tab.url) {
+                    let { url = '' } = tab;
+                    let extData = {
+                        url,
+                        platform,
+                        browser,
+                        twitterId: userInfo && userInfo.nickName || '',
+                        ...eventData,
+                    }
+                    paramsPretreatmentAndRequest(logType.denet, extData, extParams)
+                } else {
+                    let extData = {
+                        platform,
+                        browser,
+                        twitterId: userInfo && userInfo.nickName || '',
+                        ...eventData,
+                    }
+                    paramsPretreatmentAndRequest(logType.denet, extData, extParams)
+                }
+            })
+        } else {
+            paramsPretreatmentAndRequest(logType.denet, eventData, extParams)
+        }
+    } catch (error) {
+        reportFrontLogApi({
+            logData: JSON.stringify({
+                funcName: 'reportLog',
+                errmsg: error.message
+            })
+        })
+    }
+}
+
+function paramsPretreatmentAndRequest(logType, eventData, extParams) {
+    extParams = wrapObject(extParams)
+    let obj = {};
+    let pageSource = eventData.pageSource;
+    if (eventData.hasOwnProperty('pageSource')) {
+        delete eventData.pageSource;
+    }
+    obj.logType = logType;
+    obj.eventData = JSON.stringify(eventData)
+    obj.extParams = JSON.stringify(extParams)
+    logApi({
+        params: {
+            pageSource,
+            ...obj
+        }
+    }).then(res => {
+    }).catch(err => {
+        reportFrontLogApi({
+            logData: JSON.stringify(err)
+        })
+    })
+}
+
+function wrapObject(extParams) {
+    if (typeDecide(extParams, 'Object')) {
+        return extParams
+    }
+    return { 'defaultExt': extParams }
+}
+
+/**
+ * 检测对象类型
+ */
+function typeDecide(o, type) {
+    return Object.prototype.toString.call(o) === `[object ${type}]`;
+}
+
+export async function getReportCommonParams () {
+  let commonParams = {};
+  if (!userInfo) {
+      userInfo = await getChromeStorage('userInfo') || null;
+  }
+  let isMobile = navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i);
+  let platform = isMobile ? `mobile` : `pc`;
+
+  if (chrome && chrome.tabs) {
+      let tab = await chrome.tabs.getCurrent();
+      commonParams = {
+          url: tab && tab.url ? tab.url : '',
+          platform,
+          browser: getBrowser(),
+          twitterId: userInfo && userInfo.nickName || '',
+      }
+  }
+  return commonParams;
+}

+ 3 - 1
src/main.js

@@ -1,7 +1,9 @@
 import { createApp } from 'vue';
 import App from './App.vue';
 import router from './router';
-
+import AutoLog from '@/log-center/autoLog';
 const app = createApp(App);
+
+app.use(AutoLog)
 app.use(router);
 app.mount('#app')

+ 151 - 0
src/pages/treasure-hunt/components/btn.vue

@@ -0,0 +1,151 @@
+<template>
+    <div :class="{ 'area-btn': disabled }">
+        <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, 'font-weight': fontWeight }">{{ txt }}</span>
+        </div>
+        <div class="refresh" v-if="disabled">
+            <img :src="require('@/assets/svg/icon-refresh-treasure.svg')"
+                :class="{ 'icon-refresh-rotate': refreshRotate }" alt="" @click="refresh" />
+        </div>
+    </div>
+</template>
+<script setup>
+import { inject, defineProps, defineEmits, ref } from 'vue'
+let refreshRotate = ref(false);
+let state = inject('state')
+let props = defineProps({
+    txt: {
+        type: String,
+        default: ''
+    },
+    loading: {
+        type: Boolean,
+        default: false
+    },
+    fontSize: {
+        type: String,
+        default: '20px'
+    },
+    fontWeight: {
+        type: String,
+        default: '800'
+    },
+    icon: {
+        type: Boolean,
+        default: true
+    },
+    disabled: {
+        type: Boolean,
+        default: false
+    },
+    txtCorlor: {
+        type: String
+    }
+})
+const emit = defineEmits(['on-click'])
+
+const clickBtn = () => {
+    if (!props.disabled) {
+        emit('on-click')
+    }
+}
+
+const refresh = () => {
+    if (!refreshRotate.value) {
+        refreshRotate.value = true;
+        setTimeout(() => {
+            refreshRotate.value = false;
+        }, 1000)
+
+        state.init(() => {
+            state.inviteInit()
+            state.inviteList()
+        })
+    }
+}
+</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;
+    }
+}
+
+.area-btn {
+    display: flex;
+
+    .refresh {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+
+        img {
+            cursor: pointer;
+            margin-left: 12px;
+        }
+
+        .icon-refresh-rotate {
+            transform: rotate(360deg);
+            transition-duration: 1s;
+        }
+    }
+
+    .no {
+        cursor: no-drop;
+    }
+
+    .disabled {
+        cursor: no-drop;
+        background: rgba(56, 154, 255, 0.2);
+        color: #FFFFFF;
+        width: 305px;
+        font-weight: 600;
+        margin: 0;
+
+
+        span {
+            font-weight: 600;
+            color: #FFFFFF;
+        }
+    }
+
+    @keyframes loading {
+        from {
+            transform: rotate(0deg);
+        }
+
+        to {
+            transform: rotate(360deg);
+        }
+    }
+}
+</style>

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

@@ -0,0 +1,74 @@
+<template>
+    <div class="content">
+        <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 } from 'vue'
+
+let state = inject('state')
+defineProps({
+    txt: {
+        type: String,
+        default: 'All treasures are hunted, good luck next time!'
+    }
+})
+const clickBtn = () => {
+    state.dialog.show = false
+}
+</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>

+ 76 - 0
src/pages/treasure-hunt/components/head.vue

@@ -0,0 +1,76 @@
+<template>
+    <div class="head">
+        <div class="left" v-if="leftData" @click="clickItem">
+            <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'
+let props = defineProps({
+    leftData: {
+        type: Object,
+        default: null
+    },
+    rightData: {
+        type: String,
+        default: ''
+    }
+})
+const clickItem = () => {
+    window.open(`https://twitter.com/${props.leftData.nickName}`)
+}
+</script>
+<style lang="scss" scoped>
+.head {
+    padding: 10px;
+    display: flex;
+    justify-content: space-between;
+
+    .left {
+        cursor: pointer;
+        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/pages/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>

+ 227 - 0
src/pages/treasure-hunt/components/invite-friends.vue

@@ -0,0 +1,227 @@
+<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.detail.inviteUrl }}</div>
+            <div class="btn copy-btn" @click="clickCopy" v-click-log="state.log_invite_copy_btn_click"
+                :data-clipboard-text="state.detail.inviteCopyUrl">
+                Copy
+            </div>
+        </div>
+        <div class="share-list">
+            <img :src="item.iconPath" alt="" v-for="item in state.share_list" :data-clipboard-text="item.inviteContent"
+                @click="clickShare(item)" class="share-item" />
+        </div>
+        <v-btn :txt="state.open_btn.txt" :font-size="'17px'" class="btn" :icon="false"
+            :disabled="state.open_btn.disabled" v-show-log="state.log_invite_btn_show" :loading="state.btn_loading"
+            v-click-log="state.log_invite_btn_click" @onClick="clickBtn" font-weight="600"></v-btn>
+
+    </div>
+</template>
+<script setup>
+import VBtn from '@/pages/treasure-hunt/components/btn.vue'
+import { inviteChannel } from '@/http/treasure'
+import { inject, onMounted } from 'vue'
+import Report from "@/log-center/log"
+
+let ClipboardJS = require('clipboard');
+
+let state = inject('state')
+
+state.log_invite_btn_show = {
+    businessType: Report.businessType.buttonView,
+    pageSource: Report.pageSource.inviteFriendsPage,
+    objectType: Report.objectType.openChestButton,
+    redPacketType: Report.redPacketType.treasure,
+    shareLinkId: state.invite_code,
+    myShareLinkId: state.detail.inviteCopyUrl,
+    currentInvitedNum: state.detail.inviteCount,
+    postId: state.postId
+}
+
+state.log_invite_btn_click = {
+    businessType: Report.businessType.buttonClick,
+    pageSource: Report.pageSource.inviteFriendsPage,
+    objectType: Report.objectType.openChestButton,
+    redPacketType: Report.redPacketType.treasure,
+    shareLinkId: state.invite_code,
+    myShareLinkId: state.detail.inviteCopyUrl,
+    currentInvitedNum: state.detail.inviteCount,
+    postId: state.postId
+}
+state.log_invite_copy_btn_click = {
+    businessType: Report.businessType.buttonClick,
+    pageSource: Report.pageSource.inviteFriendsPage,
+    objectType: Report.objectType.copyButton,
+    redPacketType: Report.redPacketType.treasure,
+    shareLinkId: state.invite_code,
+    myShareLinkId: state.detail.inviteCopyUrl,
+    currentInvitedNum: state.detail.inviteCount,
+    postId: state.postId
+}
+onMounted(() => {
+    state.btn_loading = false
+    try {
+        chrome.management.get('ophjlpahpchlmihnnnihgmmeilfjmjjc', (res) => {
+            let linePluginInstalled = 0
+            if (res) {
+                linePluginInstalled = 1
+            }
+            inviteChannel({
+                params: {
+                    linePluginInstalled,
+                    postId: state.postId
+                }
+            }).then((res) => {
+                if (res.code == 0) {
+                    state.share_list = res.data
+                }
+            })
+        })
+    } catch (error) {
+        console.error(error)
+    }
+
+})
+
+async function clickBtn() {
+    let _userInfo = await state.checkIsLogin()
+    if (!_userInfo) {
+        return
+    }
+    state.btn_loading = true
+    state.treasureOpen()
+}
+
+const clickShare = (item) => {
+    var clipboard = new ClipboardJS('.share-item');
+    clipboard.on('success', function (e) {
+        state.toast.txt = 'Copy Successfully'
+        state.toast.has_icon = true
+        state.toast.show = true
+        setTimeout(() => {
+            state.toast.show = false
+        }, 2000)
+        e.clearSelection();
+    })
+    chrome.tabs.create({
+        url: item.redirectPath
+    });
+    let strArr = item.treasureInviteUrl.split('/');
+    let channelName = window.atob(strArr[strArr.length-1]);
+    Report.reportLog({
+      businessType: Report.businessType.buttonClick,
+      pageSource: Report.pageSource.inviteFriendsPage,
+      objectType: Report.objectType.channelButton,
+      shareLinkId: state.invite_code,
+      myShareLinkId: state.detail.inviteCopyUrl,
+      currentInvitedNum: state.detail.inviteCount,
+    }, {
+      'channel-name': channelName
+    });
+}
+
+
+const clickCopy = () => {
+    var clipboard = new ClipboardJS('.copy-btn');
+    clipboard.on('success', function (e) {
+        state.toast.txt = 'Copy Successfully'
+        state.toast.has_icon = true
+        state.toast.show = true
+        setTimeout(() => {
+            state.toast.show = false
+        }, 2000)
+        e.clearSelection();
+    })
+
+    clipboard.on('error', function (e) {
+        state.toast.txt = 'Copy Error'
+        state.toast.has_icon = false
+        state.toast.show = true
+        setTimeout(() => {
+            state.toast.show = false
+        }, 2000)
+    })
+}
+</script>
+<style lang="scss" scoped>
+.invite-friends {
+    padding: 18px 16px 25px 16px;
+    background: #fff;
+
+    .txt {
+        font-style: normal;
+        font-weight: 500;
+        font-size: 13px;
+        line-height: 18px;
+        /* or 129% */
+        margin-bottom: 18.5px;
+
+        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: 14px;
+
+        img {
+            user-select: none;
+            cursor: pointer;
+            width: 33px;
+            height: 33px;
+            margin-right: 14px;
+            border-radius: 100px;
+        }
+    }
+
+
+}
+</style>

+ 214 - 0
src/pages/treasure-hunt/components/invite-list.vue

@@ -0,0 +1,214 @@
+<template>
+    <div class="content" v-show-log="state.log_invite_list_show">
+        <div class="error" v-if="state.invited_list.length == 0">
+            Invite people to hunt treasure with you!
+        </div>
+        <div class="list" v-else @scroll="handleScroll($event)">
+            <div class="item" v-for="item in state.invited_list">
+                <div class="left">
+                    <img :src="item.userInfo.avatarUrl" alt="" @click="clickItem(item)" />
+                </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" :loading="state.btn_loading"
+                :disabled="state.open_btn.disabled" v-click-log="state.log_invite_btn_click" @onClick="clickBtn"
+                font-weight="600"></v-btn>
+        </div>
+    </div>
+</template>
+<script setup>
+import VBtn from '@/pages/treasure-hunt/components/btn.vue'
+import { inviteList } from '@/http/treasure'
+import { inject, onMounted } from 'vue'
+import Report from "@/log-center/log"
+
+var moment = require('moment')
+let state = inject('state')
+state.invited_list = []
+let page_num = 1
+let page_size = 10
+let list_end = false
+
+state.log_invite_btn_click = {
+    businessType: Report.businessType.buttonClick,
+    pageSource: Report.pageSource.inviteFriendsPage,
+    objectType: Report.objectType.openChestButton,
+    redPacketType: Report.redPacketType.treasure,
+    shareLinkId: state.invite_code,
+    myShareLinkId: state.detail.inviteCopyUrl,
+    currentInvitedNum: state.detail.inviteCount,
+    postId: state.postId
+}
+
+state.log_invite_list_show = {
+    businessType: Report.businessType.pageView,
+    pageSource: Report.pageSource.beenInvitedPage,
+    redPacketType: Report.redPacketType.treasure,
+    shareLinkId: state.invite_code,
+    myShareLinkId: state.detail.inviteCopyUrl,
+    currentInvitedNum: state.detail.inviteCount,
+    postId: state.postId
+}
+
+onMounted(() => {
+    state.btn_loading = false
+    list()
+})
+
+const clickItem = (item) => {
+    window.open(`https://twitter.com/${item.userInfo.nickName}`)
+}
+
+function handleScroll(e) {
+    if (list_end) {
+        return
+    }
+    e = e.target
+    if ((e.clientHeight + e.scrollTop) / e.scrollHeight > .8) {
+        list_end = true
+        if (state.invited_list.length == page_num * page_size) {
+            page_num++
+        }
+        list()
+    }
+}
+
+const list = () => {
+    state.inviteList()
+}
+
+state.inviteList = () => {
+    inviteList({
+        params: {
+            inviteCode: state.invite_code,
+            postId: state.postId,
+            pageNum: page_num,
+            pageSize: page_size,
+        }
+
+    }).then((res) => {
+        if (res.code == 0) {
+            if (res.data.length > 0) {
+                res.data.forEach(item => {
+                    if (state.invited_list.filter((item2) => { return item2.userInfo.uid == item.userInfo.uid }).length == 0) {
+                        state.invited_list.push(item)
+                    }
+                })
+                list_end = false
+            } else {
+                list_end = false
+            }
+        }
+    })
+}
+
+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')
+}
+
+async function clickBtn() {
+    let _userInfo = await state.checkIsLogin()
+    if (!_userInfo) {
+        return
+    }
+    state.btn_loading = true
+    state.treasureOpen()
+}
+
+</script>
+<style lang="scss" scoped>
+.content {
+    position: relative;
+    height: 292px;
+
+    .footer {
+        background: #fff;
+        padding: 10px 16px 25px 16px;
+    }
+
+    .error {
+        height: 204px;
+        color: #BABABA;
+        background-color: #fff;
+        font-weight: 500;
+        font-size: 15px;
+        line-height: 204px;
+        text-align: center;
+
+    }
+
+    .list {
+        background: #fff;
+        height: 204px;
+        overflow-y: auto;
+
+        .item {
+            height: 60px;
+            display: flex;
+            align-items: center;
+
+            .left {
+                width: 58px;
+                text-align: center;
+
+                img {
+                    border-radius: 50px;
+                    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>

+ 156 - 0
src/pages/treasure-hunt/components/open-box.vue

@@ -0,0 +1,156 @@
+<template>
+    <div class="open-box">
+        <div class="background"></div>
+        <!-- 开奖页 -->
+        <div class="content">
+            <img :src="state.open_box.icon" alt="" class="popIn" />
+            <div class="mark">
+                <p>You Win</p>
+                <div class="money">${{ state.open_box.data.usdAmountValue }}</div>
+                <div class="mark2">
+                    <img :src="state.open_box.data.currencyIcon" alt="" />
+                    <div>{{ state.open_box.data.amountValue }} {{ state.open_box.data.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 '@/pages/treasure-hunt/components/btn.vue'
+
+let state = inject('state')
+
+const clickBtn = () => {
+    state.open_box.show = false
+    state.open_box.clicked = true
+    state.init()
+}
+</script>
+<style lang="scss" scoped>
+.open-box {
+    position: fixed;
+    left: 0;
+    top: 0;
+    z-index: 999;
+
+
+    .background {
+        background: #000000;
+        opacity: .9;
+        position: fixed;
+        width: 100%;
+        height: 100%;
+    }
+
+    .content {
+        z-index: 2;
+        width: 375px;
+        height: 500px;
+        text-align: center;
+        position: relative;
+
+        .popIn {
+            -webkit-animation: fadeleftIn .4s;
+            animation: fadeleftIn .4s;
+            -webkit-animation-name: popIn;
+            animation-name: popIn;
+        }
+
+        img {
+            margin-top: 15px;
+            width: 250px;
+            height: 250px;
+            animation: show 0.5s;
+        }
+
+        .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;
+                align-items: center;
+
+                img {
+                    width: 17px;
+                    height: 17px;
+                    margin: 0;
+                    margin-right: 5px;
+                }
+
+                div {
+                    color: #A9A49F;
+                    font-weight: 400;
+                    font-size: 12px;
+
+                }
+            }
+        }
+
+        .btn {
+            position: absolute;
+            bottom: 25px;
+            left: 15px;
+        }
+    }
+}
+
+
+
+@keyframes popIn {
+    0% {
+        -webkit-transform: scale3d(0, 0, 0);
+        transform: scale3d(0.5, 0.5, 0.5);
+        opacity: 0;
+    }
+
+    50% {
+        -webkit-animation-timing-function: cubic-bezier(0.47, 0, 0.745, 0.715);
+        animation-timing-function: cubic-bezier(0.47, 0, 0.745, 0.715);
+    }
+
+    100% {
+        -webkit-transform: scale3d(1, 1, 1);
+        transform: scale3d(1, 1, 1);
+        -webkit-animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
+        animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
+        opacity: 1;
+    }
+}
+</style>

+ 83 - 0
src/pages/treasure-hunt/components/toast.vue

@@ -0,0 +1,83 @@
+<template>
+    <div class="content" v-show="show">
+        <div class="mark">
+            <div class="background"></div>
+            <img :src="icon" alt="" v-if="has_icon" />
+            <span>{{ txt }}</span>
+        </div>
+    </div>
+</template>
+<script setup>
+import { defineProps } from "vue";
+
+defineProps({
+    txt: {
+        type: String,
+        default: ''
+    },
+    has_icon: {
+        type: Boolean,
+        default: true
+    },
+    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>

+ 419 - 0
src/pages/treasure-hunt/cover.vue

@@ -0,0 +1,419 @@
+<template>
+    <!-- 封面页 -->
+    <div class="cover" v-show-log="state.log_show">
+        <v-head :left-data="state.detail.postUserInfo || null"></v-head>
+        <div class="waring" v-if="state.cover_status == '奖励已被领光'">
+            <div>All treasures</div>
+            <div>are hunted</div>
+        </div>
+        <!-- 邀请人 -->
+        <template v-else>
+            <div class="invite"
+                v-if="state.detail.inviteUserInfo && state.detail.inviteUserInfo.nickName != state.detail.postUserInfo.nickName">
+                <img :src="state.detail.inviteUserInfo.avatarUrl" alt="" />
+                <span>@{{ state.detail.inviteUserInfo.nickName }} invites you</span>
+            </div>
+            <div class="in-invite" v-else></div>
+            <!-- <div class="treasure">
+                <component-zoom width="335" fontSize="34" style="margin:0 auto;">
+                    <span>Treasure</span>
+                    <span>${{ toLast(state.detail.amountUsdValue, 2) }}</span>
+                </component-zoom>
+            </div> -->
+            <div class="gain" v-if="Number(state.detail.upGainAmountValue) > 0">
+                <!-- <component-zoom width="335" fontSize="34" style="margin:0 auto;">
+                    <span>Your Gain Up to</span>
+                    <span>$</span>
+                    <span>{{ toLast(state.detail.upGainAmountValue, 3) }}</span>
+                </component-zoom> -->
+            </div>
+            <div class="coin" v-if="state.detail.currencySymbol != 'USD'">
+                <img :src="state.detail.currencyIconPath" alt="" />
+                <span> {{ state.detail.currencySymbol }} equivalent (Crypto)</span>
+            </div>
+            <div class="coin" v-else></div>
+        </template>
+
+        <div class="box">
+            <img :src="require('@/assets/img/icon-gold-close-box.png')" alt="" v-if="state.cover_status == '奖励已被领光'" />
+            <img :src="require('@/assets/img/icon-treasure-box.png')" alt="" v-else />
+        </div>
+        <div class="mark" :style="{ 'opacity': state.cover_status == '奖励已被领光' ? '0' : '1' }">
+            <img :src="require('@/assets/svg/icon-three-line.svg')" alt="" />
+            <span>to Hunt Treasure</span>
+        </div>
+        <v-btn :txt="state.open_btn.txt" :font-size="'17px'" :icon="true" :disabled="false" @onClick="clickBtn"
+            :loading="state.btn_loading"></v-btn>
+    </div>
+
+</template>
+<script setup>
+import { inject } from 'vue'
+import VBtn from '@/pages/treasure-hunt/components/btn.vue'
+import VHead from '@/pages/treasure-hunt/components/head.vue'
+// import ComponentZoom from "@/view/components/component-zoom.vue";
+import { pageUrl } from "@/http/configAPI.js"
+import Report from "@/log-center/log"
+import { prepareStart, treasureStart } from '@/http/treasure.js'
+import { getChromeCookie, removeChromeCookie, getChromeStorage, sendChromeTabMessage } from '@/uilts/chromeExtension.js'
+import { reSetBindRepost } from '@/http/help.js'
+
+let state = inject('state')
+
+state.log_show = {
+    businessType: Report.businessType.pageView,
+    pageSource: Report.pageSource.pending_page,
+    redPacketType: Report.redPacketType.treasure,
+    shareLinkId: state.invite_code,
+    postId: state.postId
+}
+
+// chrome.storage.onChanged.addListener(changes => {
+//     if (changes.userInfo) {
+//         // let item = JSON.parse(changes.userInfo.newValue)
+//         state.btn_loading = false
+//         state.init()
+//     }
+// })
+
+const toStart = (req) => {
+    treasureStart({
+        params: {
+            postId: state.postId || '',
+            inviteCode: state.invite_code || ''
+        }
+    }).then((res) => {
+        if (res.code == 0) {
+            state.page = '开奖页'
+            state.start_task = res.data
+            state.btn_loading = false
+            if (req.response) {
+                let repost_tweetId = req.response.data.data.create_tweet.tweet_results.result.rest_id
+                reSetBindRepost({
+                    inviteCode: res.data.inviteCode,
+                    tweetId: repost_tweetId
+                })
+            }
+        } else {
+            state.init()
+        }
+
+    }).catch((error) => {
+        console.error(error)
+    })
+}
+
+// chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
+//     switch (req.actionType) {
+//         case 'DO_TASK':
+//             if (!req.task_type || state.tweetId != req.tweet_Id) {
+//                 return
+//             }
+//             if (!req.task_done && req.task_type == 'createTweet') {
+//                 state.toast.txt = 'Seems something went wrong, please try again'
+//                 state.toast.show = true
+//                 state.toast.has_icon = false
+//                 setTimeout(() => {
+//                     state.toast.show = false
+//                 }, 2000)
+//             } else if (req.task_type == 'createTweet' && req.task_done) {
+//                 toStart(req);
+//                 getChromeStorage('userInfo', (_userInfo) => {
+//                     if (_userInfo) {
+//                         sendChromeTabMessage({
+//                             actionType: "IFRAME_API_GET_TWEET_USER_INFO_REQ",
+//                             data: {
+//                                 screen_name: _userInfo.nickName,
+//                                 tweetId: state.tweetId,
+//                                 objectType: Report.objectType.repostSuccess
+//                             }
+//                         })
+//                     }
+//                 })
+//             }
+//             break
+//         case 'CONTENT_API_GET_TWEET_USER_INFO_RES':
+//             let twitterFans = 0;
+//             let { user } = req.data || {};
+//             if (user && user.result && user.result.legacy) {
+//                 let legacy = user.result.legacy;
+//                 twitterFans = legacy ? legacy.followers_count : 0;
+//             }
+//             if (state.tweetId == req.tweetId && req.objectType == Report.objectType.repostSuccess) {
+//                 Report.reportLog({
+//                     objectType: Report.objectType.repostSuccess,
+//                     twitterFans: twitterFans,
+//                     redPacketType: Report.redPacketType.treasure,
+//                     postId: state.postId,
+//                     shareLinkId: state.invite_code,
+//                 });
+//             };
+//             break;
+//     }
+// })
+
+const toLast = (num, bit) => {
+    let str = 1
+    for (let i = 0; i < bit; i++) {
+        str = str + '0'
+    }
+    let _num = Number(str)
+    return Math.floor(Number(num) * _num) / _num
+}
+
+
+async function clickBtn() {
+    let _userInfo = await state.checkIsLogin()
+    if (!_userInfo) {
+        return
+    }
+    if (state.cover_status == '奖励已被领光') {
+        Report.reportLog({
+            pageSource: Report.pageSource.pending_page,
+            businessType: Report.businessType.buttonClick,
+            objectType: Report.objectType.getMoreGiveaway,
+            postId: state.postId
+        });
+        window.open('https://twitter.com/search?q=%23denet');
+        return
+    }
+
+
+    Report.reportLog({
+        businessType: Report.businessType.buttonClick,
+        objectType: Report.objectType.open_button,
+        pageSource: Report.pageSource.pending_page,
+        redPacketType: Report.redPacketType.treasure,
+        shareLinkId: state.invite_code,
+        postId: state.postId
+    });
+
+    state.btn_loading = true
+    setTimeout(() => {
+        if (state.btn_loading == true) {
+            state.btn_loading = false
+        }
+    }, 10000)
+
+    // 获取邀请码
+    getChromeCookie({
+        name: state.postId,
+        url: pageUrl,
+    }, (res) => {
+        if (res && res.inviteCode) {
+            state.invite_code = res.inviteCode
+        }
+        startBtn()
+    })
+}
+
+const startBtn = () => {
+    // 获取文本
+    prepareStart({
+        params: {
+            postId: state.postId || '',
+            inviteCode: state.invite_code || ''
+        }
+    }).then((res) => {
+        if (res.code == 0) {
+            removeChromeCookie({
+                name: state.postId,
+                url: pageUrl,
+            })
+
+            let text = res.data.rePostTweetContent
+            // 一键三连
+            chrome.tabs.getCurrent((tab) => {
+                chrome.tabs.sendMessage(tab.id, {
+                    actionType: "IFRAME_TWITTER_API_DO_TASK",
+                    task_data: {
+                        tweet_Id: state.tweetId,
+                        tweet_text: text
+                    },
+                    task_type: 'tasks',
+                    tasks: state.tasks,
+                });
+            })
+        } else {
+            state.init()
+        }
+    })
+}
+</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;
+    }
+
+    .in-invite {
+        height: 28px;
+        margin-top: 20px;
+        margin-bottom: 10px;
+    }
+
+    .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;
+        height: 37px;
+        display: flex;
+        align-items: 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 {
+        height: 17px;
+        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>

+ 304 - 0
src/pages/treasure-hunt/index.vue

@@ -0,0 +1,304 @@
+<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>
+    <open-box v-show="state.open_box.show"></open-box>
+    <v-toast :show="state.toast.show" :txt="state.toast.txt" :has_icon="state.toast.has_icon"></v-toast>
+</template>
+<script setup>
+import { reactive, provide, onMounted } from 'vue'
+import VCover from '@/pages/treasure-hunt/cover.vue'
+import VInvite from '@/pages/treasure-hunt/invite.vue'
+import VResult from '@/pages/treasure-hunt/result.vue'
+import { inviteDetail, treasureDetail, treasureOpen } from '@/http/treasure.js'
+import { reSetBindTwtterId, reSetBindRepost } from '@/http/help.js'
+import { getQueryString } from '@/uilts/help'
+import { getChromeStorage, sendChromeTabMessage } from '@/uilts/chromeExtension.js'
+import VToast from '@/pages/treasure-hunt/components/toast.vue'
+import OpenBox from '@/pages/treasure-hunt/components/open-box.vue'
+import Report from "@/log-center/log"
+
+let state = reactive({
+    page: '',
+    detail: {},
+    oldDetail: {},
+    btn_loading: false,
+    open_box: {
+        showed: false,
+        show: false,
+        clicked: false,
+        data: {}
+    },
+    open_btn: {
+        txt: '',
+        disabled: false
+    },
+    dialog: {
+        show: false,
+    },
+    start_task: {},
+    toast: {}
+})
+provide('state', state)
+
+let params = {}
+onMounted(() => {
+    params = JSON.parse(getQueryString('params') || '{}')
+    state.postId = params.post_Id || ''
+    state.tweetId = params.tweet_Id || ''
+    state.invite_code = params.invite_code || ''
+    state.init();
+    // onRuntimeMsg();
+})
+
+state.checkIsLogin = () => {
+    return new Promise((resolve) => {
+        getChromeStorage('userInfo', (_userInfo) => {
+            if (!_userInfo) {
+                state.btn_loading = true
+                setTimeout(() => {
+                    state.btn_loading = false
+                }, 3000)
+                chrome.runtime.sendMessage({ actionType: "POPUP_LOGIN", data: "" })
+                resolve(_userInfo)
+            } else {
+                resolve(_userInfo)
+            }
+        })
+    })
+}
+
+state.init = (callback) => {
+    if (params.page_type == '邀请链接') {
+        // 邀请链接
+        inviteDetail({
+            params: {
+                inviteCode: params.invite_code
+            }
+        }).then((res) => {
+            if (res.code == 0) {
+
+                handleCommon(res, callback)
+                // 绑定repostSrcContentId
+                if (!res.data.repostSrcContentId) {
+                    reSetBindRepost({
+                        inviteCode: state.invite_code,
+                        tweetId: state.tweetId
+                    })
+                }
+            }
+        })
+
+    } else if (params.page_type == '原始链接') {
+        // 原始链接
+        treasureDetail({
+            params: {
+                postId: params.post_Id
+            }
+        }).then((res) => {
+            if (res.code == 0) {
+                handleCommon(res, callback)
+            }
+        })
+    }
+}
+
+const reportOpenBoxLog = () => {
+    if (state.open_box.showed) {
+        Report.reportLog({
+            businessType: Report.businessType.pageView,
+            pageSource: Report.pageSource.openTreasurePage,
+            redPacketType: Report.redPacketType.treasure,
+            shareLinkId: state.invite_code,
+            myShareLinkId: state.detail.inviteCopyUrl,
+            currentInvitedNum: state.detail.inviteCount,
+            postId: state.postId
+        });
+        state.open_box.showed = false
+    }
+    if (state.open_box.clicked) {
+        Report.reportLog({
+            businessType: Report.businessType.buttonClick,
+            pageSource: Report.pageSource.openTreasurePage,
+            objectType: Report.objectType.nextButton,
+            redPacketType: Report.redPacketType.treasure,
+            shareLinkId: state.invite_code,
+            myShareLinkId: state.detail.inviteCopyUrl,
+            currentInvitedNum: state.detail.inviteCount,
+            postId: state.postId
+        });
+        state.open_box.clicked = false
+    }
+}
+
+const handleCommon = (res, callback) => {
+    state.detail = res.data
+    state.postId = state.detail.postId
+    reportOpenBoxLog()
+    try {
+        state.tasks = JSON.parse(state.detail.startCondition)
+        let follows = state.tasks.filter((item) => { return item.type == 1 })
+        if (follows.length) {
+            state.follows = follows[0].relatedUsers
+        }
+    }
+    catch (error) {
+        console.error('catch', error)
+    }
+    if (!res.data.srcContentId) {
+        reSetBindTwtterId({
+            postId: state.postId || '',
+            tweetId: state.tweetId || ''
+        }, () => {
+            sendChromeTabMessage({
+                actionType: "IFRAME_API_GET_TWEET_USER_INFO_REQ",
+                data: {
+                    screen_name: state.detail.postUserInfo.nickName,
+                    tweetId: state.tweetId,
+                    objectType: Report.objectType.tweetPostBinded
+                }
+            })
+        })
+    }
+    handleStatus(callback)
+}
+let silver_open_box_big = require('@/assets/img/icon-silver-open-box-big.png')
+let gold_open_box_big = require('@/assets/img/icon-gold-open-box-big.png')
+
+state.treasureOpen = () => {
+    treasureOpen({
+        params: {
+            postId: state.postId,
+            treasureId: state.treasureId,
+        }
+    }).then((res) => {
+        state.btn_loading = false
+        if (res.code == 0) {
+            // icon
+            for (let i in state.boxs) {
+                if (state.boxs[i].id == state.treasureId) {
+                    if (i > 0) {
+                        state.open_box.icon = gold_open_box_big
+                    } else {
+                        state.open_box.icon = silver_open_box_big
+                    }
+                    break
+                }
+            }
+            state.open_box.show = true
+            state.open_box.showed = true
+            state.open_box.data = res.data
+
+            state.init(() => {
+                state.inviteInit()
+                state.inviteList()
+            })
+        } else {
+            switch (String(res.code)) {
+                case '2037':
+                    state.dialog.show = true
+                    break;
+                case '2203':
+                    state.toast.txt = 'You have already opened the treasure chest'
+                    state.toast.show = true
+                    break
+                case '2208':
+                    state.toast.txt = 'No treasure chests to open'
+                    state.toast.show = true
+                    break
+                default:
+                    state.toast.txt = 'System Error'
+                    state.toast.show = true
+                    break
+            }
+            state.toast.has_icon = false
+            setTimeout(() => {
+                state.toast.show = false
+            }, 2000)
+
+            state.init(() => {
+                state.inviteInit()
+                state.inviteList()
+            })
+        }
+    }).catch(() => {
+        state.btn_loading = false
+    })
+}
+
+const handleStatus = (callback) => {
+    // 如果 夺宝状态 = 未开始
+    //   显示未开始页面
+    // 如果 夺宝状态 = 进行中
+    //   如果 夺宝参与状态 = 未参与夺宝
+    //      显示封面页
+    //   如果 夺宝参与状态 = 已参与夺宝
+    //      显示邀请页
+    // 如果 夺宝状态 = 已结束
+    //  显示结束页面
+    let { status, joinStatus } = state.detail || {}
+    state.open_btn.txt = 'Start'
+    // 如果 夺宝状态 = 未开始
+    if (status == 0) {
+        state.page = '封面页'
+
+    }
+    state.page = '开奖页'
+    // // 如果 夺宝状态 = 进行中
+    // else if (status == 1) {
+    //     // 如果 夺宝参与状态 = 未参与夺宝
+    //     if (joinStatus == 0) {
+    //         // 显示封面页
+    //         state.page = '封面页'
+    //         state.cover_status = '有邀请人'
+    //         state.open_btn.txt = 'Start'
+    //         // state.cover_status = '无邀请人'
+    //         // state.cover_status = '奖励已被领光'
+    //     }
+    //     // 如果 夺宝参与状态 = 已参与夺宝
+    //     else if (joinStatus == 1) {
+    //         state.page = '邀请页'
+    //     }
+    // }
+    // // 如果 夺宝状态 = 已结束
+    // else {
+    //     state.open_btn.txt = 'Look for more treasures'
+    //     state.page = '封面页'
+    //     state.cover_status = '奖励已被领光'
+    //     state.btn_loading = false
+    //     return
+    // }
+    callback && callback()
+}
+
+function onRuntimeMsg() {
+    chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
+        switch (req.actionType) {
+            case 'CONTENT_API_GET_TWEET_USER_INFO_RES':
+                let twitterFans = 0;
+                let { user } = req.data || {};
+                if (user && user.result && user.result.legacy) {
+                    let legacy = user.result.legacy;
+                    twitterFans = legacy ? legacy.followers_count : 0;
+                }
+                if (state.tweetId == req.tweetId && req.objectType == Report.objectType.tweetPostBinded) {
+                    Report.reportLog({
+                        objectType: Report.objectType.tweetPostBinded,
+                        twitterFans: twitterFans,
+                        redPacketType: Report.redPacketType.treasure,
+                        postId: state.postId
+                    });
+                }
+                break;
+        }
+        sendResponse && sendResponse();
+    })
+}
+</script>
+<style lang="scss" >
+html,
+body {
+    margin: 0;
+    padding: 0;
+}
+</style>

+ 412 - 0
src/pages/treasure-hunt/invite.vue

@@ -0,0 +1,412 @@
+<template>
+    <!-- 邀请页 -->
+    <div class="area-process" v-show-log="state.log_invite_show">
+        <v-head :left-data="state.detail.postUserInfo || null" :rightData="state.detail.remainAmountUsdValue"></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"
+                    @click="clickItem(item)">
+                    <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">
+            <img :src="require('@/assets/svg/icon-invite.svg')" alt=""
+                :style="{ opacity: state.tab_index == 0 ? '1' : '0.55' }" v-if="i == 0" />
+            <img :src="require('@/assets/svg/icon-invite-list.svg')" alt=""
+                :style="{ opacity: state.tab_index == 1 ? '1' : '0.55' }" v-if="i == 1" />
+            {{ 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>
+    <v-dialog v-show="state.dialog.show"></v-dialog>
+</template>
+<script setup>
+import { ref, onMounted, inject } from 'vue'
+import { receiveList } from '@/http/treasure.js'
+import VHead from '@/pages/treasure-hunt/components/head.vue'
+import InviteList from '@/pages/treasure-hunt/components/invite-list.vue'
+import HoverTip from '@/pages/treasure-hunt/components/hover-tip.vue'
+import InviteFriends from '@/pages/treasure-hunt/components/invite-friends.vue'
+import VDialog from '@/pages/treasure-hunt/components/dialog.vue'
+
+import Report from "@/log-center/log"
+
+let content_success_message = ref(null)
+let state = inject('state')
+
+state.log_invite_show = {
+    businessType: Report.businessType.pageView,
+    pageSource: Report.pageSource.inviteFriendsPage,
+    redPacketType: Report.redPacketType.treasure,
+    shareLinkId: state.invite_code,
+    myShareLinkId: state.detail.inviteCopyUrl,
+    currentInvitedNum: state.detail.inviteCount,
+    postId: state.postId
+}
+
+// ---- 走马灯
+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'
+}]
+
+state.boxs = []
+
+let line_full = ref(null)
+onMounted(() => {
+    state.inviteInit()
+    setInterval(() => {
+        state.init(() => {
+            state.inviteInit()
+            state.inviteList()
+        })
+    }, 30000)
+})
+
+const clickItem = (item) => {
+    window.open(`https://twitter.com/${item.userInfo.nickName}`)
+}
+state.inviteInit = () => {
+    if (state.detail.inviteCount > 0) {
+        state.tabs[1].txt = `invited(${state.detail.inviteCount})`
+    }
+    state.boxs = []
+    state.detail.treasureRecords.forEach((item, index) => {
+        if (item.openStatus == 0) {
+            item.hover_icon = require('@/assets/svg/icon-user.svg')
+            if (index > 0) {
+                item.icon = gold_close_box
+            } else {
+                item.icon = silver_close_box
+            }
+            item.txt = item.inviteProgress
+        } else {
+            item.icon = silver_open_box
+            item.hover_icon = require('@/assets/svg/icon-green-yes.svg')
+            // 最后一条
+            if (index > 0) {
+                item.icon = gold_open_box
+            } else {
+                item.icon = silver_open_box
+            }
+            item.txt = '$' + item.amountValue
+        }
+
+        state.boxs.push(item)
+    })
+
+    receiveList({
+        params: {
+            postId: state.postId,
+            pageNum: 1,
+            pageSize: 100,
+        }
+    }).then((res) => {
+        if (res.code == 0) {
+            state.success_message_list = res.data
+            state.success_message_list = state.success_message_list.concat(state.success_message_list)
+            state.success_message_list = state.success_message_list.concat(state.success_message_list)
+
+            if (content_success_message && content_success_message.value) {
+                let dom = content_success_message.value
+                let s = state.success_message_list.length * 4
+                dom.style.animationDuration = s + 's'
+            }
+        }
+    })
+    btnStatus()
+
+}
+let line_width = 0
+const setLineFull = (box_num = 0, finishNeedInviteCount = 0, successInviteCount = 0) => {
+    if (box_num == 0) {
+        // 第一个宝箱起点是0,终点是55
+        line_width = (successInviteCount / finishNeedInviteCount) * 55
+    } else if (box_num == 1) {
+        if (line_width == 55) {
+            // 第二个宝箱起点是92,终点是155
+            line_width = 92
+            line_width = (successInviteCount / finishNeedInviteCount) * (155 - line_width) + line_width
+        }
+    } else if (box_num == 2) {
+        if (line_width == 155) {
+            line_width = 192
+            // 第二个宝箱起点是192,终点是260
+            line_width = (successInviteCount / finishNeedInviteCount) * (260 - line_width) + line_width
+        }
+    }
+    line_full.value.style.width = line_width + 'px'
+}
+
+const btnStatus = () => {
+    for (let i in state.boxs) {
+        if (state.boxs[i].taskFinishStatus == 0) {
+            let num = state.boxs[i].finishNeedInviteCount - state.boxs[i].successInviteCount
+            if (num == 1) {
+                state.open_btn.txt = 'Invite 1 Friend to Open'
+            } else {
+                state.open_btn.txt = `Invite ${num} Friends to Open`
+            }
+            state.open_btn.disabled = true
+            break
+        }
+    }
+
+    state.treasureId = ''
+    let open_num = 0
+    // 有打开的箱子 Open the chest
+    state.boxs.forEach((item, index) => {
+        if (item.taskFinishStatus == 1 && item.openStatus == 0) {
+            state.open_btn.txt = 'Open the Treasure Chest'
+            state.open_btn.disabled = false
+            if (!state.treasureId) {
+                state.treasureId = item.id
+            }
+        }
+        setLineFull(index, item.finishNeedInviteCount, item.successInviteCount)
+        // 三个箱子全部打开了
+        if (item.openStatus == 1) {
+            open_num++
+        }
+        if (open_num == state.boxs.length) {
+            state.open_btn.txt = 'All Chests Have Been Opened'
+            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: 0px;
+                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;
+            animation-duration: 10s;
+            animation-play-state: running;
+
+            .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 {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        user-select: none;
+        color: #757575;
+        background: #F0F0F0;
+        text-align: center;
+        width: 50%;
+        font-weight: 500;
+        font-size: 14px;
+        line-height: 38px;
+        cursor: pointer;
+
+        img {
+            margin-right: 8px;
+            width: 20px;
+            height: 20px;
+        }
+    }
+
+    .active {
+        background: #FFFFFF;
+        color: #000000;
+    }
+}
+
+.area-info {
+    width: 375px;
+
+
+}
+</style>

+ 168 - 0
src/pages/treasure-hunt/result.vue

@@ -0,0 +1,168 @@
+<template>
+    <!-- 开奖页 -->
+    <div class="content" v-show-log="state.log_result_show">
+        <img :src="require('@/assets/img/icon-silver-open-box-big.png')" alt="" />
+        <div class="mark">
+
+            <!-- 新粉 -->
+            <template v-if="Number(state.start_task.amountValue) > 0">
+                <p>You are now following
+                    <template v-for="item, i in state.follows">
+                        <span v-if="i == 0">@{{ item.name }}</span>
+                        <span v-else>, @{{ item.name }}</span>
+                    </template>
+                </p>
+                <p>You Win</p>
+                <component-zoom width="335" fontSize="34" style="margin:0 auto;">
+                    <div class="money">${{ state.start_task.usdAmountValue }}</div>
+                </component-zoom>
+                <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>You already followed
+                    <template v-for="item, i in state.follows">
+                        <span v-if="i == 0">@{{ item.name }}</span>
+                        <span v-else>, @{{ item.name }}</span>
+                    </template>
+                </p>
+                <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="Number(state.start_task.amountValue) > 0" v-click-log="state.log_result_click" @onClick="clickBtn"
+            :disabled="false"></v-btn>
+        <v-btn :txt="'Invite'" :font-size="'16px'" class="btn" v-else @onClick="clickBtn" :disabled="false"
+            v-click-log="state.log_result_click"></v-btn>
+    </div>
+</template>
+<script setup>
+import { inject } from 'vue'
+import VBtn from '@/pages/treasure-hunt/components/btn.vue'
+import Report from "@/log-center/log"
+
+let state = inject('state')
+
+state.log_result_show = {
+    businessType: Report.businessType.pageView,
+    pageSource: Report.pageSource.newFansRewardPage,
+    redPacketType: Report.redPacketType.treasure,
+    shareLinkId: state.invite_code,
+    postId: state.postId,
+    extParams: {
+        isNewFans: Number(state.start_task.amountValue) > 0 ? true : false
+    }
+}
+
+state.log_result_click = {
+    businessType: Report.businessType.buttonClick,
+    pageSource: Report.pageSource.newFansRewardPage,
+    objectType: Report.objectType.nextButton,
+    redPacketType: Report.redPacketType.treasure,
+    shareLinkId: state.invite_code,
+    postId: state.postId,
+    extParams: {
+        isNewFans: Number(state.start_task.amountValue) > 0 ? true : false
+    }
+}
+
+async function clickBtn() {
+    let _userInfo = await state.checkIsLogin()
+    if (!_userInfo) {
+        return
+    }
+    state.init(() => {
+        state.page = '邀请页'
+    })
+}
+//JSON.parse('[{\"type\":2},{\"type\":10},{\"relatedUsers\":[{\"name\":\"Ice17619765\",\"twitterUserId\":\"1502254505236525056\"},{\"name\":\"IanDuddyUK\",\"twitterUserId\":\"556285604\"},{\"name\":\"ffvc\",\"twitterUserId\":\"285917234\"}],\"type\":1}]')
+
+</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;
+        width: 250px;
+        height: 250px;
+    }
+
+    .mark {
+        position: absolute;
+        top: 246px;
+        width: 375px;
+
+        p {
+            margin: 0;
+            padding: 0 16px;
+            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;
+            align-items: center;
+
+            img {
+                width: 17px;
+                height: 17px;
+                margin: 0;
+                margin-right: 5px;
+            }
+
+            div {
+                color: #A9A49F;
+                font-weight: 400;
+                font-size: 12px;
+
+            }
+        }
+    }
+
+    .btn {
+        position: absolute;
+        bottom: 25px;
+        left: 15px;
+    }
+}
+</style>

+ 6 - 0
src/router/index.js

@@ -1,5 +1,6 @@
 import { createRouter, createWebHistory } from "vue-router"
 import TabGroup from './../pages/tab-group';
+import TreasureHunt from '@/pages/treasure-hunt';
 
 // 2. 定义路由配置
 const routes = [
@@ -7,6 +8,11 @@ const routes = [
         path: '/tab-group',
         name: '',
         component: TabGroup
+    },
+    {
+        path: '/treasure-hunt',
+        name: '',
+        component: TreasureHunt
     }
 ]
 

+ 50 - 0
src/types/global.js

@@ -0,0 +1,50 @@
+/**
+ * 全局通用字段定义
+ */
+
+/**
+ * 玩法类型
+ * 普通任务:common=1;
+ * 抽奖:lottery=2
+ * 夺宝:treasure=3
+ */
+export const PlayType = {
+  common: 1,
+  lottery: 2,
+  treasure: 3,
+  postEditor: 4,
+};
+
+/**
+ * 奖品类型
+ * 货币:money=1;
+ * 自定义奖品:custom=2
+ */
+export const RewardType = {
+  money: 1,
+  custom: 2,
+};
+
+/**
+ * 任务类型
+ */
+export const TaskType = {
+  twitterFollow: 1,
+  twitterLikeTweet: 2,
+  twitterRetweet: 3,
+  joinDiscord: 7,
+  repostToFacebook: 8,
+  twitterCommentAndTag: 9,
+  twitterRePost: 10
+};
+
+/**
+ * 帖子类型
+ */
+
+export const PostType = {
+  giveaway: 1,
+  lottery: 2,
+  postEditor: 3,
+  treasure: 4
+}

+ 1 - 0
src/types/index.js

@@ -0,0 +1 @@
+export * from "@/types/global";

+ 1 - 0
vue.config.js

@@ -4,6 +4,7 @@ const path = require("path");
 console.log(process.env.NODE_ENV)
 
 module.exports = defineConfig({
+	lintOnSave: false,
 	transpileDependencies: true,
 	devServer: {
 		port: 3000, // 端口号

+ 104 - 0
yarn.lock

@@ -1112,6 +1112,69 @@
   resolved "https://registry.npmmirror.com/@popperjs/core/-/core-2.11.5.tgz#db5a11bf66bdab39569719555b0f76e138d7bd64"
   integrity sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==
 
+"@sentry/browser@7.10.0":
+  version "7.10.0"
+  resolved "https://registry.npmmirror.com/@sentry/browser/-/browser-7.10.0.tgz#54a831811b65e3b9e01785f5a96f2441167d01b3"
+  integrity sha512-RNOKCRonqzUOqyM/JcjxjCOMosfS0MRmzkBKWewfyXse9AqfqC9+y8BI5J7wFmpYCypQP72JROKPBaxi7rvOFw==
+  dependencies:
+    "@sentry/core" "7.10.0"
+    "@sentry/types" "7.10.0"
+    "@sentry/utils" "7.10.0"
+    tslib "^1.9.3"
+
+"@sentry/core@7.10.0":
+  version "7.10.0"
+  resolved "https://registry.npmmirror.com/@sentry/core/-/core-7.10.0.tgz#ebe1f7158954d3d29ba319bdfe745a06d3a43d08"
+  integrity sha512-uq6oUXPH+6cjsEL5/j/xSW91mVrJo7knTqax7E5MDiA5j98BPK4budGiBiPO7GEB856QhA7N+pOO0lccii5QYQ==
+  dependencies:
+    "@sentry/hub" "7.10.0"
+    "@sentry/types" "7.10.0"
+    "@sentry/utils" "7.10.0"
+    tslib "^1.9.3"
+
+"@sentry/hub@7.10.0":
+  version "7.10.0"
+  resolved "https://registry.npmmirror.com/@sentry/hub/-/hub-7.10.0.tgz#41a32c1e68efaedaebc1dca10b8e22d12937715f"
+  integrity sha512-9Appy7J87EU7Xu2BDY1cLK79nsuE72geeYmG71lgdttTD3XOMcQBOxET4/2sAI+d/ansurXnURx+DAQ9FOKT+w==
+  dependencies:
+    "@sentry/types" "7.10.0"
+    "@sentry/utils" "7.10.0"
+    tslib "^1.9.3"
+
+"@sentry/tracing@^7.5.1":
+  version "7.10.0"
+  resolved "https://registry.npmmirror.com/@sentry/tracing/-/tracing-7.10.0.tgz#084019c6ab841dff1722ed5eb397ee80fb36af51"
+  integrity sha512-ojuBYS1bL/IGWKt/ItY4HmC8NElJrYtTUvm73VbhylhIO4zcn5ICHmgMFj1lqL9gQ1nCnAlifKiWIjL9qUatTA==
+  dependencies:
+    "@sentry/hub" "7.10.0"
+    "@sentry/types" "7.10.0"
+    "@sentry/utils" "7.10.0"
+    tslib "^1.9.3"
+
+"@sentry/types@7.10.0":
+  version "7.10.0"
+  resolved "https://registry.npmmirror.com/@sentry/types/-/types-7.10.0.tgz#c91d634768336238ac30ed750fa918326c384cbb"
+  integrity sha512-1UBwdbS0xXzANzp63g4eNQly/qKIXp0swP5OTKWoADvKBtL4anroLUA/l8ADMtuwFZYtVANc8WRGxM2+YmaXtg==
+
+"@sentry/utils@7.10.0":
+  version "7.10.0"
+  resolved "https://registry.npmmirror.com/@sentry/utils/-/utils-7.10.0.tgz#237cfcfa300f65fad45ec703d4acb4f02f22093b"
+  integrity sha512-/aD2DnfyOhV0Wdbb6VF78vu4fQIZJyuReDpBI7MV/EqcEB6FxUKq2YjinfKZF/exHEPig6Ag/Yt+CRFgvtVFuw==
+  dependencies:
+    "@sentry/types" "7.10.0"
+    tslib "^1.9.3"
+
+"@sentry/vue@^7.5.1":
+  version "7.10.0"
+  resolved "https://registry.npmmirror.com/@sentry/vue/-/vue-7.10.0.tgz#395c703f283f2f3b4d5af927f81c443867c8c497"
+  integrity sha512-pIK1Nx0IHBJnkURqe7wsJF0l7SFihJlhXdpnWUvgzwnu7WoTiUzGdDYzYCN5PiebbixBoSX2f1J9jEPicIj/ww==
+  dependencies:
+    "@sentry/browser" "7.10.0"
+    "@sentry/core" "7.10.0"
+    "@sentry/types" "7.10.0"
+    "@sentry/utils" "7.10.0"
+    tslib "^1.9.3"
+
 "@sideway/address@^4.1.3":
   version "4.1.4"
   resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0"
@@ -2356,6 +2419,15 @@ cli-spinners@^2.5.0:
   resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a"
   integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==
 
+clipboard@^2.0.11:
+  version "2.0.11"
+  resolved "https://registry.npmmirror.com/clipboard/-/clipboard-2.0.11.tgz#62180360b97dd668b6b3a84ec226975762a70be5"
+  integrity sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==
+  dependencies:
+    good-listener "^1.2.2"
+    select "^1.1.2"
+    tiny-emitter "^2.0.0"
+
 clipboardy@^2.3.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-2.3.0.tgz#3c2903650c68e46a91b388985bc2774287dba290"
@@ -2774,6 +2846,11 @@ delayed-stream@~1.0.0:
   resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
   integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
 
+delegate@^3.1.2:
+  version "3.2.0"
+  resolved "https://registry.npmmirror.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166"
+  integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==
+
 depd@2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
@@ -3525,6 +3602,13 @@ globby@^11.0.2, globby@^11.0.3:
     merge2 "^1.4.1"
     slash "^3.0.0"
 
+good-listener@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.npmmirror.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
+  integrity sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==
+  dependencies:
+    delegate "^3.1.2"
+
 graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9:
   version "4.2.10"
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
@@ -4319,6 +4403,11 @@ module-alias@^2.2.2:
   resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0"
   integrity sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==
 
+moment@^2.29.4:
+  version "2.29.4"
+  resolved "https://registry.npmmirror.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
+  integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
+
 mrmime@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27"
@@ -5331,6 +5420,11 @@ select-hose@^2.0.0:
   resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
   integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==
 
+select@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.npmmirror.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
+  integrity sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==
+
 selfsigned@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.0.1.tgz#8b2df7fa56bf014d19b6007655fff209c0ef0a56"
@@ -5794,6 +5888,11 @@ thunky@^1.0.2:
   resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
   integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
 
+tiny-emitter@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.npmmirror.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
+  integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
+
 to-fast-properties@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
@@ -5821,6 +5920,11 @@ tr46@~0.0.3:
   resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
   integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
 
+tslib@^1.9.3:
+  version "1.14.1"
+  resolved "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
+  integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
+
 tslib@^2.0.3:
   version "2.4.0"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"

Неке датотеке нису приказане због велике количине промена