瀏覽代碼

Merge branch 'dev_1.1.0' of DeNet/de-net into master

v1.1.0 【NFT】
wenliming 2 年之前
父節點
當前提交
dd7bb61241
共有 78 個文件被更改,包括 5080 次插入1196 次删除
  1. 二進制
      src/assets/gif/box.gif
  2. 二進制
      src/assets/img/icon-arrow.png
  3. 二進制
      src/assets/img/icon-nft.png
  4. 二進制
      src/assets/img/icon-ntf-share.png
  5. 二進制
      src/assets/img/img-box1.png
  6. 二進制
      src/assets/img/img-box5.png
  7. 6 0
      src/assets/svg/icon-back-head-list.svg
  8. 1 1
      src/assets/svg/icon-currency-category-02.svg
  9. 1 0
      src/assets/svg/icon-default.svg
  10. 3 0
      src/assets/svg/icon-form-refresh-blue.svg
  11. 7 0
      src/assets/svg/icon-get-giveaways-s.svg
  12. 4 0
      src/assets/svg/icon-head-help.svg
  13. 6 0
      src/assets/svg/icon-home-list.svg
  14. 6 0
      src/assets/svg/icon-home-refresh.svg
  15. 6 0
      src/assets/svg/icon-loading-while.svg
  16. 3 0
      src/assets/svg/icon-nft-back-arrow.svg
  17. 8 0
      src/assets/svg/icon-send-giveaways-s.svg
  18. 1 0
      src/assets/svg/icon-tab-NFT-active.svg
  19. 1 0
      src/assets/svg/icon-tab-NFT.svg
  20. 3 0
      src/assets/svg/icon-tab-message-active.svg
  21. 3 0
      src/assets/svg/icon-tab-message.svg
  22. 3 0
      src/assets/svg/icon-tab-more-active.svg
  23. 3 0
      src/assets/svg/icon-tab-more.svg
  24. 3 0
      src/assets/svg/icon-tab-wallet-active.svg
  25. 3 0
      src/assets/svg/icon-tab-wallet.svg
  26. 1 1
      src/assets/svg/icon-telegram.svg
  27. 6 0
      src/assets/svg/icon-wallter-list-blind-box.svg
  28. 19 1
      src/entry/background.js
  29. 30 1
      src/entry/content.js
  30. 23 1
      src/entry/content_help.js
  31. 1 1
      src/http/configAPI.js
  32. 9 1
      src/http/discordApi.js
  33. 41 0
      src/http/nft.js
  34. 7 0
      src/http/pay.js
  35. 8 0
      src/http/publishApi.js
  36. 13 0
      src/iframe/buy-nft.js
  37. 6 0
      src/iframe/nft-card.js
  38. 24 0
      src/iframe/popup-page.js
  39. 9 1
      src/log-center/logEnum.js
  40. 18 1
      src/log-center/logger.js
  41. 98 3
      src/logic/background/twitter.js
  42. 58 20
      src/logic/content/ParseCard.js
  43. 329 24
      src/logic/content/twitter.js
  44. 5 2
      src/manifest.json
  45. 33 0
      src/router/buy-nft.js
  46. 45 5
      src/router/popup.js
  47. 29 1
      src/uilts/help.js
  48. 29 10
      src/view/components/currency-list.vue
  49. 88 44
      src/view/components/popup-transactions.vue
  50. 144 0
      src/view/components/preview-balance.vue
  51. 300 0
      src/view/iframe/buy-nft/buy/home.vue
  52. 142 0
      src/view/iframe/buy-nft/buy/open-box.vue
  53. 431 0
      src/view/iframe/buy-nft/buy/pay.vue
  54. 29 0
      src/view/iframe/buy-nft/components/btn-loading.vue
  55. 40 0
      src/view/iframe/buy-nft/index.vue
  56. 1 0
      src/view/iframe/home/home.vue
  57. 248 0
      src/view/iframe/nft/card.vue
  58. 30 0
      src/view/iframe/popup/index.vue
  59. 1 1
      src/view/iframe/publish/components/follow-input.vue
  60. 117 29
      src/view/iframe/publish/give-dialog.vue
  61. 59 5
      src/view/iframe/red-packet/red-packet.vue
  62. 34 4
      src/view/popup/components/head.vue
  63. 231 0
      src/view/popup/components/tabbar.vue
  64. 68 0
      src/view/popup/components/top-bar.vue
  65. 302 0
      src/view/popup/currency-detail.vue
  66. 8 0
      src/view/popup/index.vue
  67. 0 1028
      src/view/popup/popup.vue
  68. 163 0
      src/view/popup/tabbar-page/index.vue
  69. 667 0
      src/view/popup/tabbar-page/message/index.vue
  70. 115 0
      src/view/popup/tabbar-page/more/index.vue
  71. 424 0
      src/view/popup/tabbar-page/nft/detail.vue
  72. 138 0
      src/view/popup/tabbar-page/nft/index.vue
  73. 359 0
      src/view/popup/tabbar-page/wallter/popup.vue
  74. 5 4
      src/view/popup/top-up/home.vue
  75. 12 1
      src/view/popup/transactions.vue
  76. 5 4
      src/view/popup/withdraw/home.vue
  77. 6 1
      src/view/popup/withdraw/info.vue
  78. 1 1
      src/view/popup/withdraw/success.vue

二進制
src/assets/gif/box.gif


二進制
src/assets/img/icon-arrow.png


二進制
src/assets/img/icon-nft.png


二進制
src/assets/img/icon-ntf-share.png


二進制
src/assets/img/img-box1.png


二進制
src/assets/img/img-box5.png


File diff suppressed because it is too large
+ 6 - 0
src/assets/svg/icon-back-head-list.svg


+ 1 - 1
src/assets/svg/icon-currency-category-02.svg

@@ -1,3 +1,3 @@
 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M21 7L12 2L3 7V17L12 22L21 17V7ZM12 4.29L17.91 7.57L14.9 9.24C14.17 8.48 13.14 8 12 8C10.86 8 9.83 8.48 9.1 9.24L6.09 7.57L12 4.29ZM11 19.16L5 15.83V9.26L8.13 11C8.04 11.31 8 11.65 8 12C8 13.86 9.27 15.43 11 15.87V19.16ZM10 12C10 10.9 10.9 10 12 10C13.1 10 14 10.9 14 12C14 13.1 13.1 14 12 14C10.9 14 10 13.1 10 12ZM13 19.16V15.88C14.73 15.44 16 13.87 16 12.01C16 11.66 15.96 11.32 15.87 11L19 9.26V15.83L13 19.16Z" fill="#2CD2C8"/>
+<path d="M12.1243 5.01728L12.1 5.00382L12.0757 5.01728L6.75674 7.96928L6.67795 8.01301L6.75674 8.05672L9.46574 9.55972L9.4994 9.57839L9.52606 9.55064C10.1741 8.87593 11.0883 8.45 12.1 8.45C13.1117 8.45 14.0259 8.87593 14.6739 9.55064L14.7006 9.57839L14.7343 9.55972L17.4433 8.05672L17.5221 8.01301L17.4433 7.96928L12.1243 5.01728ZM11.1757 18.4877L11.25 18.5289V18.444V15.483V15.4441L11.2123 15.4345C9.67711 15.0441 8.55 13.6507 8.55 12C8.55 11.6886 8.5856 11.3875 8.66502 11.1139L8.67613 11.0757L8.64129 11.0563L5.82429 9.4903L5.75 9.449V9.534V15.447V15.4764L5.77574 15.4907L11.1757 18.4877ZM12.95 18.444V18.5289L13.0243 18.4877L18.4243 15.4907L18.45 15.4764V15.447V9.534V9.449L18.3757 9.4903L15.5587 11.0563L15.5242 11.0755L15.5349 11.1135C15.6144 11.3964 15.65 11.6977 15.65 12.009C15.65 13.6597 14.5229 15.0531 12.9877 15.4435L12.95 15.4531V15.492V18.444ZM12.1 3.0572L20.15 7.52942V16.4706L12.1 20.9428L4.05 16.4706V7.52942L12.1 3.0572ZM12.1 10.15C11.0824 10.15 10.25 10.9824 10.25 12C10.25 13.0176 11.0824 13.85 12.1 13.85C13.1176 13.85 13.95 13.0176 13.95 12C13.95 10.9824 13.1176 10.15 12.1 10.15Z" fill="#1D9BF0" stroke="#F7F7F7" stroke-width="0.1"/>
 </svg>

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


+ 3 - 0
src/assets/svg/icon-form-refresh-blue.svg

@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4.21664 9.31085C4.21664 9.31085 6.4009 5.93844 9.85712 4.12882C13.3133 2.31927 17.5714 2.84021 20.364 4.59492C23.1566 6.34973 24.0968 7.83024 24.0968 7.83024L26.5022 6.45938C26.5022 6.45938 27 6.21266 27 6.78841V15.3978C27 15.3978 27 16.1655 26.4193 15.8912C25.9308 15.6607 20.648 12.655 18.9833 11.7067C18.0691 11.2988 18.8723 10.9673 18.8723 10.9673L21.1935 9.63986C21.1935 9.63986 19.8695 7.98074 17.9315 7.10206C15.8571 6.02069 13.9151 5.89246 11.5362 6.79105C9.985 7.37687 8.15868 8.87707 6.84323 11.093L4.21664 9.31085ZM25.7834 20.6891C25.7834 20.6891 23.5991 24.0616 20.1429 25.8711C16.6866 27.6807 12.4286 27.1599 9.63599 25.4051C6.84326 23.6503 5.90327 22.1697 5.90327 22.1697L3.4977 23.5405C3.4977 23.5405 3 23.7874 3 23.2115V14.6022C3 14.6022 3 13.8346 3.58058 14.1087C4.06912 14.3394 9.35204 17.345 11.0167 18.2933C11.9309 18.7013 11.1278 19.0326 11.1278 19.0326L8.80649 20.3601C8.80649 20.3601 10.1306 22.0192 12.0685 22.8979C14.1428 23.9792 16.0848 24.1075 18.4638 23.2089C20.0149 22.623 21.8412 21.123 23.1566 18.9069L25.7834 20.6891Z" fill="#1D9BF0"/>
+</svg>

File diff suppressed because it is too large
+ 7 - 0
src/assets/svg/icon-get-giveaways-s.svg


+ 4 - 0
src/assets/svg/icon-head-help.svg

@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle opacity="0.4" cx="12" cy="12" r="12" fill="#4E4E4E"/>
+<path d="M12.4852 5.5C11.1784 5.5 10.1541 5.87086 9.41239 6.63024C8.67067 7.37196 8.2998 8.37859 8.2998 9.66777H10.1541C10.1541 8.89073 10.3131 8.29029 10.6309 7.86645C10.9841 7.3543 11.5669 7.10706 12.3616 7.10706C12.9974 7.10706 13.5095 7.28366 13.8627 7.63686C14.1983 7.99007 14.3749 8.46689 14.3749 9.08499C14.3749 9.54415 14.1983 9.96799 13.8804 10.3742L13.5802 10.7097C12.4852 11.681 11.8142 12.4051 11.5846 12.8996C11.3373 13.3587 11.2314 13.9238 11.2314 14.5773V14.8775H13.121V14.5773C13.121 14.1534 13.2093 13.7649 13.4036 13.4117C13.5625 13.0938 13.8097 12.7759 14.1453 12.4934C14.9753 11.7693 15.4698 11.3102 15.6287 11.1159C16.0702 10.5508 16.2998 9.82671 16.2998 8.96137C16.2998 7.90177 15.9466 7.05408 15.2402 6.43598C14.5338 5.80022 13.6155 5.5 12.4852 5.5ZM12.1674 16.096C11.8142 16.096 11.4963 16.202 11.249 16.4492C11.0018 16.6788 10.8782 16.979 10.8782 17.3499C10.8782 17.7031 11.0018 18.0033 11.249 18.2506C11.4963 18.4978 11.8142 18.6214 12.1674 18.6214C12.5206 18.6214 12.8384 18.4978 13.0857 18.2682C13.3329 18.021 13.4742 17.7208 13.4742 17.3499C13.4742 16.979 13.3506 16.6788 13.1033 16.4492C12.8561 16.202 12.5382 16.096 12.1674 16.096Z" fill="white"/>
+</svg>

File diff suppressed because it is too large
+ 6 - 0
src/assets/svg/icon-home-list.svg


File diff suppressed because it is too large
+ 6 - 0
src/assets/svg/icon-home-refresh.svg


+ 6 - 0
src/assets/svg/icon-loading-while.svg

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: rgb(255, 255, 255,0); display: block; shape-rendering: auto;" width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
+<circle cx="50" cy="50" fill="none" stroke="#fff" stroke-width="12" r="35" stroke-dasharray="164.93361431346415 56.97787143782138">
+  <animateTransform attributeName="transform" type="rotate" repeatCount="indefinite" dur="1s" values="0 50 50;360 50 50" keyTimes="0;1"></animateTransform>
+</circle>
+<!-- [ldio] generated by https://loading.io/ --></svg>

+ 3 - 0
src/assets/svg/icon-nft-back-arrow.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="M3.46967 11.4697C3.17678 11.7626 3.17678 12.2374 3.46967 12.5303L8.24264 17.3033C8.53553 17.5962 9.01041 17.5962 9.3033 17.3033C9.59619 17.0104 9.59619 16.5355 9.3033 16.2426L5.06066 12L9.3033 7.75736C9.59619 7.46447 9.59619 6.98959 9.3033 6.6967C9.01041 6.40381 8.53553 6.40381 8.24264 6.6967L3.46967 11.4697ZM20 11.25L4 11.25L4 12.75L20 12.75L20 11.25Z" fill="black"/>
+</svg>

File diff suppressed because it is too large
+ 8 - 0
src/assets/svg/icon-send-giveaways-s.svg


File diff suppressed because it is too large
+ 1 - 0
src/assets/svg/icon-tab-NFT-active.svg


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


+ 3 - 0
src/assets/svg/icon-tab-message-active.svg

@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M15 24.8203C13.8506 24.8203 12.7405 24.6728 11.6932 24.3981C11.0133 24.2198 9.14472 24.8397 6.0875 26.2578C6.90527 23.6109 7.09915 22.0898 6.66917 21.6945C4.70537 19.8894 3.5 17.4468 3.5 14.7578C3.5 9.20044 8.64872 4.69531 15 4.69531C21.3513 4.69531 26.5 9.20044 26.5 14.7578C26.5 20.3152 21.3513 24.8203 15 24.8203ZM10.4288 16.0803C11.1432 16.0803 11.7225 15.5011 11.7225 14.7866C11.7225 14.0721 11.1432 13.4928 10.4288 13.4928C9.71426 13.4928 9.135 14.0721 9.135 14.7866C9.135 15.5011 9.71426 16.0803 10.4288 16.0803ZM15.0288 16.0803C15.7432 16.0803 16.3225 15.5011 16.3225 14.7866C16.3225 14.0721 15.7432 13.4928 15.0288 13.4928C14.3143 13.4928 13.735 14.0721 13.735 14.7866C13.735 15.5011 14.3143 16.0803 15.0288 16.0803ZM19.6287 16.0803C20.3432 16.0803 20.9225 15.5011 20.9225 14.7866C20.9225 14.0721 20.3432 13.4928 19.6287 13.4928C18.9143 13.4928 18.335 14.0721 18.335 14.7866C18.335 15.5011 18.9143 16.0803 19.6287 16.0803Z" fill="#1D9BF0"/>
+</svg>

+ 3 - 0
src/assets/svg/icon-tab-message.svg

@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M15 24.8203C13.8506 24.8203 12.7405 24.6728 11.6932 24.3981C11.0133 24.2198 9.14472 24.8397 6.0875 26.2578C6.90527 23.6109 7.09915 22.0898 6.66917 21.6945C4.70537 19.8894 3.5 17.4468 3.5 14.7578C3.5 9.20044 8.64872 4.69531 15 4.69531C21.3513 4.69531 26.5 9.20044 26.5 14.7578C26.5 20.3152 21.3513 24.8203 15 24.8203ZM10.4288 16.0803C11.1432 16.0803 11.7225 15.5011 11.7225 14.7866C11.7225 14.0721 11.1432 13.4928 10.4288 13.4928C9.71426 13.4928 9.135 14.0721 9.135 14.7866C9.135 15.5011 9.71426 16.0803 10.4288 16.0803ZM15.0288 16.0803C15.7432 16.0803 16.3225 15.5011 16.3225 14.7866C16.3225 14.0721 15.7432 13.4928 15.0288 13.4928C14.3143 13.4928 13.735 14.0721 13.735 14.7866C13.735 15.5011 14.3143 16.0803 15.0288 16.0803ZM19.6287 16.0803C20.3432 16.0803 20.9225 15.5011 20.9225 14.7866C20.9225 14.0721 20.3432 13.4928 19.6287 13.4928C18.9143 13.4928 18.335 14.0721 18.335 14.7866C18.335 15.5011 18.9143 16.0803 19.6287 16.0803Z" fill="#C0C0C0"/>
+</svg>

+ 3 - 0
src/assets/svg/icon-tab-more-active.svg

@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M14.465 12.034C14.465 13.3735 13.379 14.4595 12.0396 14.4595H7.43108C6.09162 14.4595 5.00559 13.3734 5.00559 12.034V7.4255C5.00559 6.08604 6.09162 5 7.43108 5H12.0395C13.379 5 14.465 6.08604 14.465 7.4255V12.034ZM25.0056 22.5744C25.0056 23.914 23.9196 25 22.58 25H17.9717C16.6322 25 15.5461 23.914 15.5461 22.5744V17.9661C15.5461 16.6266 16.6322 15.5405 17.9717 15.5405H22.58C23.9196 15.5405 25.0056 16.6266 25.0056 17.9661V22.5744ZM14.4595 22.5744C14.4595 23.914 13.3734 25 12.034 25H7.4255C6.08595 25 5 23.914 5 22.5744V17.9661C5 16.6266 6.08604 15.5405 7.4255 15.5405H12.0339C13.3734 15.5405 14.4594 16.6266 14.4594 17.9661V22.5744H14.4595ZM22.3336 7.10775C22.4817 7.10791 22.6237 7.16681 22.7284 7.27152C22.8331 7.37624 22.8921 7.51821 22.8923 7.66631V11.7932C22.8923 12.1011 22.6416 12.3517 22.3336 12.3517H18.2068C18.0588 12.3515 17.9168 12.2926 17.812 12.1879C17.7073 12.0832 17.6484 11.9412 17.6482 11.7932V7.66631C17.6482 7.35838 17.8988 7.10775 18.2068 7.10775H22.3336ZM22.5744 5H17.9661C16.6266 5 15.5405 6.08604 15.5405 7.42559V12.0339C15.5405 13.3734 16.6266 14.4595 17.9661 14.4595H22.5744C23.914 14.4595 25 13.3734 25 12.0339V7.42559C25 6.08604 23.914 5 22.5744 5Z" fill="#1D9BF0"/>
+</svg>

+ 3 - 0
src/assets/svg/icon-tab-more.svg

@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M14.465 12.034C14.465 13.3735 13.379 14.4595 12.0396 14.4595H7.43108C6.09162 14.4595 5.00559 13.3734 5.00559 12.034V7.4255C5.00559 6.08604 6.09162 5 7.43108 5H12.0395C13.379 5 14.465 6.08604 14.465 7.4255V12.034ZM25.0056 22.5744C25.0056 23.914 23.9196 25 22.58 25H17.9717C16.6322 25 15.5461 23.914 15.5461 22.5744V17.9661C15.5461 16.6266 16.6322 15.5405 17.9717 15.5405H22.58C23.9196 15.5405 25.0056 16.6266 25.0056 17.9661V22.5744ZM14.4595 22.5744C14.4595 23.914 13.3734 25 12.034 25H7.4255C6.08595 25 5 23.914 5 22.5744V17.9661C5 16.6266 6.08604 15.5405 7.4255 15.5405H12.0339C13.3734 15.5405 14.4594 16.6266 14.4594 17.9661V22.5744H14.4595ZM22.3336 7.10775C22.4817 7.10791 22.6237 7.16681 22.7284 7.27152C22.8331 7.37624 22.8921 7.51821 22.8923 7.66631V11.7932C22.8923 12.1011 22.6416 12.3517 22.3336 12.3517H18.2068C18.0588 12.3515 17.9168 12.2926 17.812 12.1879C17.7073 12.0832 17.6484 11.9412 17.6482 11.7932V7.66631C17.6482 7.35838 17.8988 7.10775 18.2068 7.10775H22.3336ZM22.5744 5H17.9661C16.6266 5 15.5405 6.08604 15.5405 7.42559V12.0339C15.5405 13.3734 16.6266 14.4595 17.9661 14.4595H22.5744C23.914 14.4595 25 13.3734 25 12.0339V7.42559C25 6.08604 23.914 5 22.5744 5Z" fill="#C0C0C0"/>
+</svg>

+ 3 - 0
src/assets/svg/icon-tab-wallet-active.svg

@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M6.9981 6H23.0044C24.6547 6 26 7.34533 26 8.9981V11.738H18.3117C16.4179 11.738 14.8684 13.2874 14.8684 15.1812V16.0617C14.8684 17.9555 16.4179 19.505 18.3117 19.505H26V22.2448C26 23.9 24.6547 25.2429 23.0019 25.2429H6.9981C5.34287 25.2429 4 23.8976 4 22.2448V8.9981C4 7.34287 5.34533 6 6.9981 6ZM16.0967 15.1806C16.0967 14.5928 16.3279 14.0394 16.7485 13.6188C17.1666 13.1983 17.7224 12.9671 18.3103 12.9671H25.9986V18.2746H18.3103C17.7224 18.2746 17.1691 18.0434 16.7485 17.6229C16.3279 17.2023 16.0967 16.6489 16.0967 16.0611V15.1806ZM20.1991 15.6208C20.1991 15.0773 19.7589 14.6371 19.2153 14.6371C18.6718 14.6371 18.2316 15.0773 18.2316 15.6208C18.2316 16.1644 18.6718 16.6046 19.2153 16.6046C19.7589 16.6046 20.1991 16.1644 20.1991 15.6208Z" fill="#1D9BF0"/>
+</svg>

+ 3 - 0
src/assets/svg/icon-tab-wallet.svg

@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M6.9981 6H23.0044C24.6547 6 26 7.34533 26 8.9981V11.738H18.3117C16.4179 11.738 14.8684 13.2874 14.8684 15.1812V16.0617C14.8684 17.9555 16.4179 19.505 18.3117 19.505H26V22.2448C26 23.9 24.6547 25.2429 23.0019 25.2429H6.9981C5.34287 25.2429 4 23.8976 4 22.2448V8.9981C4 7.34287 5.34533 6 6.9981 6ZM16.0967 15.1806C16.0967 14.5928 16.3279 14.0394 16.7485 13.6188C17.1666 13.1983 17.7224 12.9671 18.3103 12.9671H25.9986V18.2746H18.3103C17.7224 18.2746 17.1691 18.0434 16.7485 17.6229C16.3279 17.2023 16.0967 16.6489 16.0967 16.0611V15.1806ZM20.1991 15.6208C20.1991 15.0773 19.7589 14.6371 19.2153 14.6371C18.6718 14.6371 18.2316 15.0773 18.2316 15.6208C18.2316 16.1644 18.6718 16.6046 19.2153 16.6046C19.7589 16.6046 20.1991 16.1644 20.1991 15.6208Z" fill="#C0C0C0"/>
+</svg>

+ 1 - 1
src/assets/svg/icon-telegram.svg

@@ -1,3 +1,3 @@
 <svg width="42" height="42" viewBox="0 0 42 42" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M32.2165 13.8207L28.5516 30.7927C28.2752 31.9905 27.5541 32.2887 26.5295 31.7244L20.9454 27.6837L18.251 30.2284C17.9528 30.5212 17.7034 30.7661 17.1288 30.7661L17.5299 25.1815L27.8794 15.9981C28.3294 15.6042 27.7818 15.3859 27.18 15.7799L14.3855 23.6909L8.87738 21.998C7.67925 21.6306 7.65757 20.8214 9.12677 20.2571L30.6714 12.1065C31.669 11.7392 32.5418 12.3248 32.2165 13.8207V13.8207Z" fill="#259EDA"/>
+<path d="M32.2165 13.8207L28.5516 30.7927C28.2752 31.9905 27.5541 32.2887 26.5295 31.7244L20.9454 27.6837L18.251 30.2284C17.9528 30.5212 17.7034 30.7661 17.1288 30.7661L17.5299 25.1815L27.8794 15.9981C28.3294 15.6042 27.7818 15.3859 27.18 15.7799L14.3855 23.6909L8.87738 21.998C7.67925 21.6306 7.65757 20.8214 9.12677 20.2571L30.6714 12.1065C31.669 11.7392 32.5418 12.3248 32.2165 13.8207Z" fill="#1D9BF0"/>
 </svg>

File diff suppressed because it is too large
+ 6 - 0
src/assets/svg/icon-wallter-list-blind-box.svg


+ 19 - 1
src/entry/background.js

@@ -14,7 +14,11 @@ import {
     discordLoginCode,
     saveDiscordAuthWindowId,
     onDisconnectHandler,
-    getMessageInfo
+    getMessageInfo,
+    injectExtensionPopup,
+    setPopupConfig,
+    windwoLoadSetPopupPage,
+    setActionPopup
 } from "@/logic/background/twitter";
 
 import { PingPong } from "@/logic/background/help";
@@ -55,6 +59,14 @@ chrome.alarms.onAlarm.addListener(function (alarm) {
     }
 });
 
+chrome.action.onClicked.addListener(function(tab) { 
+    injectExtensionPopup(tab);
+});
+
+chrome.tabs.onActivated.addListener(function(activeInfo) {
+    setPopupConfig(activeInfo);
+})
+
 function onInstalledMethod() {
     onInstalledCreateTab()
     onInstalledMid()
@@ -114,6 +126,12 @@ function onMessageMethod(req, sender, sendResponse) {
             case 'CONTENT_PONG':
                 console.log('CONTENT_PONG')
                 break
+            case 'CONTENT_WINDOW_LOADED_SET_POPUP_PAGE': 
+                // windwoLoadSetPopupPage(req, sender);
+                break;
+            case 'CONTENT_SET_POPUP_CONFIG':
+                setActionPopup(req, sender);
+                break;
         }
     }
 }

+ 30 - 1
src/entry/content.js

@@ -8,6 +8,7 @@ import {
     showIframeHandler,
     hideIframeHandler,
     showTwitterPublishDialogHandler,
+    twitterPublishHandler,
     showPinTips,
     init,
     initExecuteScript,
@@ -18,7 +19,12 @@ import {
     getTweetAuthorByDom,
     facebookReplyTweet,
     doTaskTwitterAPI,
-    onTweetReplyClick
+    onTweetReplyClick,
+    showNFTSale,
+    showBuyNFT,
+    hideBuyNFT,
+    showPopupPage,
+    setPopupConfByPopupPage
 } from "@/logic/content/twitter.js";
 
 import {
@@ -32,6 +38,10 @@ chrome.storage.onChanged.addListener(changes => {
 window.onload = () => {
     init();
     initFacebookContent();
+    chrome.runtime.sendMessage({ 
+        actionType: "CONTENT_WINDOW_LOADED_SET_POPUP_PAGE", 
+        data: { } 
+    }, () => { });
 };
 
 window.onmessage = (res) => {
@@ -91,6 +101,25 @@ chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
         case 'IFRAME_TWITTER_API_DO_TASK':
             doTaskTwitterAPI(req)
             break
+        case 'IFRAME_NFT_SHOW_SALE':
+            showNFTSale()
+            break
+        case "IFRAME_TWITTER_PUBLISH":
+            twitterPublishHandler(req.publishRes);
+            break;
+        case 'IFRAME_TWITTER_SHOW_POPUP_PAGE':
+            let {from = ''} = req.data || {};
+            showPopupPage({path: '/NFT', from}); 
+            break
+        case "IFRAME_TWITTER_SHOW_BUY_NFT":
+            showBuyNFT(req.data)
+            break
+        case 'IFRAME_TWITTER_HIDE_BUY_NFT':
+            hideBuyNFT()
+            break
+        case 'BG_SET_POPUP_CONFIG':
+            setPopupConfByPopupPage();
+            break
     }
 })
 

+ 23 - 1
src/entry/content_help.js

@@ -1,3 +1,22 @@
+import {
+    appendPopupPage,
+    tiggerInjectPopupPage
+} from "@/logic/content/twitter.js";
+
+window.onload = () => {
+    appendPopupPage();
+    chrome.runtime.sendMessage({ 
+        actionType: "CONTENT_WINDOW_LOADED_SET_POPUP_PAGE", 
+        data: { } 
+    }, () => { });
+};
+
+chrome.runtime.sendMessage({ 
+    actionType: "CONTENT_SET_POPUP_CONFIG", 
+    data: {
+        popup: 'popup.html'
+    } 
+}, () => { });
 
 chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
     sendResponse('')
@@ -6,5 +25,8 @@ chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
             console.log('BACK_PING')
             chrome.runtime.sendMessage({ actionType: "CONTENT_PONG", data: '1' }, (res) => { console.log(res) })
             break
+        case 'BG_INJECT_EXTENSION_POPUP':
+            tiggerInjectPopupPage();
+            break
     }
-})
+})

+ 1 - 1
src/http/configAPI.js

@@ -1,4 +1,4 @@
-export const appVersionCode = 7
+export const appVersionCode = 10
 
 const api = {
 	production: 'https://api.denetme.net',

+ 9 - 1
src/http/discordApi.js

@@ -15,11 +15,19 @@ export function discordAuthUrl(params) {
  */
 export function getInviteGuildInfo(params) {
     return service({
-        url: `https://discord.com/api/invites/${params.inviteCode}`,
+        url: `https://discord.com/api/v9/invites/${params.inviteCode}?with_counts=true&with_expiration=true`,
         method: "get",
     });
 }
 
+export function getInviteGuildInfoByOpenApi(params) {
+    return service({
+        url: `https://discord.com/api/invites/${params.inviteCode}?with_counts=true&with_expiration=true`,
+        method: "get",
+    });
+}
+
+
 export function checkGuildJoined(params) {
     return service({
         url: `/openapi/discord/checkGuildJoined`,

+ 41 - 0
src/http/nft.js

@@ -0,0 +1,41 @@
+import { service } from "./request";
+
+export function getTwitterSaleNftProjectInfo(params) {
+    return service({
+        url: `/nft/project/getTwitterSaleNftProjectInfo`,
+        method: 'post',
+        data: params
+    })
+}
+
+export function nftListMine(params) {
+    return service({
+        url: `/nft/item/listMine`,
+        method: 'post',
+        data: params
+    })
+}
+
+export function getNFTDetail(params) {
+    return service({
+        url: `/nft/item/getDetail`,
+        method: 'post',
+        data: params
+    })
+}
+
+export function getNftMysteryBoxSaleInfo(params) {
+    return service({
+        url: `/nft/project/getNftMysteryBoxSaleInfo`,
+        method: 'post',
+        data: params
+    })
+}
+
+export function getNftProjectInfo(params) {
+    return service({
+        url: `/nft/project/getNftProjectInfo`,
+        method: 'post',
+        data: params
+    })
+}

+ 7 - 0
src/http/pay.js

@@ -33,5 +33,12 @@ export function getTokenRechargeAddress(params) {
     });
 }
 
+export function payNftMysteryBoxWithBalance(params) {
+    return service({
+        url: `/wallet/pay/payNftMysteryBoxWithBalance`,
+        method: "post",
+        data: params,
+    });
+}
 
 // ---- 提现 ----

+ 8 - 0
src/http/publishApi.js

@@ -75,6 +75,14 @@ export function searchCurrencyInfo(params) {
   })
 }
 
+export function getCurrencyInfoBySymbol(params) {
+  return service({
+    url: `/currency/v2/getCurrencyInfoBySymbol`,
+    method: 'post',
+    data: params
+  })
+}
+
 export function syncChainTokenRechargeRecord(params) {
   return service({
     url: `/wallet/recharge/syncChainTokenRechargeRecord`,

+ 13 - 0
src/iframe/buy-nft.js

@@ -0,0 +1,13 @@
+import { createApp } from 'vue'
+import App from '@/view/iframe/buy-nft/index.vue'
+import ElementPlus from 'element-plus'
+import 'element-plus/dist/index.css'
+
+import router from '@/router/buy-nft.js'
+
+const app = createApp(App);
+
+
+app.use(router)
+app.use(ElementPlus);
+app.mount('#app');

+ 6 - 0
src/iframe/nft-card.js

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

+ 24 - 0
src/iframe/popup-page.js

@@ -0,0 +1,24 @@
+import { createApp } from 'vue'
+import App from '@/view/iframe/popup/index.vue'
+
+const app = createApp(App)
+
+// 引入路由对象实例
+import router from '@/router/popup.js'
+import "ant-design-vue/dist/antd.css"; // or 'ant-design-vue/dist/antd.less'
+import { Button, message, Tooltip } from "ant-design-vue";
+
+message.config({
+    top: `10px`,
+    duration: 3,
+    maxCount: 1,
+});
+app.use(Tooltip);
+app.use(Button);
+app.use(message);
+app.use(router)
+app.mount('#app')
+
+window.onload= () => {
+    document.title = 'DeNet'
+}

+ 9 - 1
src/log-center/logEnum.js

@@ -17,6 +17,14 @@ export const objectType = {
     loginButton: "login-button",
     withdrawButton: "withdraw-button",
     topupButton: "topup-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',
     // 关注全部
@@ -29,7 +37,7 @@ export const objectType = {
     // 点击检测任务
     get_giveaway: 'get-giveaway',
     // 成功领取到钱包
-    wallet_button: 'wallet-button'
+    wallet_button: 'wallet-button',
 }
 
 export const pageSource = {

+ 18 - 1
src/log-center/logger.js

@@ -1,4 +1,5 @@
 import {logApi} from '@/http/logApi'
+import { getBrowser } from '@/uilts/help.js';
 import { logType } from './logEnum.js';
 
 
@@ -7,7 +8,23 @@ import { logType } from './logEnum.js';
  * @extParams 最终上报到阿里云以json字符串存储的参数,如果extparams传入的不是obj会转换成obj
  */
 export function reportLog(eventData = {}, extParams = {}) {
-    paramsPretreatmentAndRequest(logType.denet, eventData, extParams)
+    if (chrome && chrome.tabs) {
+        chrome.tabs.getCurrent((tab) => {
+            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 { url = '' } = tab;
+            let platform = isMobile ? `mobile` : `pc`;
+            let browser = getBrowser();
+            let extData = {
+                url,
+                platform,
+                browser,
+                ...extParams,
+            }
+            paramsPretreatmentAndRequest(logType.denet, eventData, extData)
+        })
+    } else {
+        paramsPretreatmentAndRequest(logType.denet, eventData, extParams)
+    }
 }
 
 function paramsPretreatmentAndRequest(logType, eventData, extParams) {

+ 98 - 3
src/logic/background/twitter.js

@@ -87,6 +87,11 @@ export function twitterPinLoginCode(sender, code) {
         fetchTwitterLogin(authToken, consumerKey, code, receivedIds).then(res => {
             if (res.code == 0) {
                 setChromeStorage({ userInfo: JSON.stringify(res.data) })
+
+                sendActivetabMessage({
+                    actionType: 'BG_LOGIN_SET_USERINFO_CB'
+                });
+
                 // 获取全局消息数据
                 setMessageCount()
                 chrome.cookies.remove(LANDING_PAGE)
@@ -256,9 +261,24 @@ export function onInstalledCreateTab() {
             });
             removeChromeCookie(cookiesParams)
         } else {
-            chrome.tabs.create({
-                url: "https://twitter.com",
-            });
+            let nftParams = {
+                name: 'nft_info',
+                url: pageUrl
+            }
+            getChromeCookie(nftParams, (res) => {
+                let { twitterAccount, nftProjectId } = res;
+                if (res && twitterAccount && nftProjectId) {
+                    let url = `https://twitter.com/${twitterAccount}`
+                    chrome.tabs.create({
+                        url
+                    });
+                    removeChromeCookie(nftParams)
+                } else {
+                    chrome.tabs.create({
+                        url: "https://twitter.com",
+                    });
+                }
+            })
         }
     })
 }
@@ -345,4 +365,79 @@ export const onDisconnectHandler = (port) => {
             getMessageInfo();
         })
     }
+}
+
+export const injectExtensionPopup = (tab) => {
+    sendActivetabMessage({
+        actionType: 'BG_INJECT_EXTENSION_POPUP'
+    });
+}
+
+export const setPopupConfig = (activeInfo) => {
+    chrome.tabs.query({
+        active: true,
+        currentWindow: true
+    }, (tabs) => {
+        if(tabs.length) {
+            let {pendingUrl = '', url = ''} = tabs[0];
+            if(pendingUrl.startsWith('https://twitter.com') || url.startsWith('https://twitter.com')) {
+                sendActivetabMessage({
+                    actionType: 'BG_SET_POPUP_CONFIG'
+                });
+            } else {
+                chrome.action.setPopup({
+                    popup: 'popup.html',
+                },function() {
+                });
+            }
+
+            // if(pendingUrl.startsWith('chrome://') || url.startsWith('chrome://') || pendingUrl.startsWith('https://chrome.google.com') || url.startsWith('https://chrome.google.com')) {
+            //     chrome.action.setPopup({
+            //         popup: 'popup.html',
+            //     },function() {
+            //     });
+            // } else {
+            //     chrome.action.setPopup({
+            //         popup: '',
+            //     },function() {
+            //     });
+            // }
+        }
+    })
+}
+
+export const windwoLoadSetPopupPage = (data, sender) => {
+    let {url = ''} = sender.tab;
+    if(url.startsWith('chrome://')) {
+        chrome.action.setPopup({
+            popup: 'popup.html',
+        },function() {
+        });
+    } else {
+        chrome.action.setPopup({
+            popup: '',
+        },function() {
+        });
+    }
+}
+
+export const setActionPopup = (data) => {
+    let {popup} = data.data || {};
+    if(popup) {
+        chrome.action.getPopup(
+            {},
+            function(result){
+                if(!result) {
+                    chrome.action.setPopup({
+                        popup,
+                    },function() {
+                    });
+                }
+        });
+    } else {
+        chrome.action.setPopup({
+            popup: '',
+        },function() {
+        });
+    }
 }

+ 58 - 20
src/logic/content/ParseCard.js

@@ -41,12 +41,12 @@ class ParseCard {
             let item
             for (let i in arr_shadow) {
                 item = arr_shadow[i].childNodes
-                if(has_denet){
+                if (has_denet) {
                     break
                 }
                 if (item) {
                     for (let j in item) {
-                        if (item[j].innerText && item[j].innerText.includes('#DeNet')) {
+                        if (item[j].innerText && (item[j].innerText.includes('#DeNet') || item[j].innerText.includes('#DNFT'))) {
                             has_denet = true
                             break
                         }
@@ -64,8 +64,8 @@ class ParseCard {
             let arr_article = document.querySelectorAll('article') || []
             let _txt
             for (let i in arr_article) {
-                _txt = arr_article[i].innerText || ''                
-                if ((_txt.includes('#DeNet') || this.compatibleMask(arr_article[i])) && !this.isHasIframeByArticle(arr_article[i])) {
+                _txt = arr_article[i].innerText || ''
+                if ((_txt.includes('#DeNet') || _txt.includes('#DNFT') || this.compatibleMask(arr_article[i])) && !this.isHasIframeByArticle(arr_article[i])) {
                     de_net_card.push({
                         time: new Date().getTime(),
                         dom: arr_article[i]
@@ -104,7 +104,7 @@ class ParseCard {
         a_arr = Array.from(a_arr).reverse()
         for (let i in a_arr) {
             // 获取推特id
-            if (a_arr[i].href.indexOf('facebook.com') > 0 && a_arr[i].href.indexOf('denetme') > 0) {
+            if (a_arr[i].href.indexOf('facebook.com') > 0 && a_arr[i].href.indexOf('denetme') > 0 && a_arr[i].href.indexOf('nft') == -1) {
                 let faceUrl = new URL(a_arr[i].href);
                 let faceSearch = new URLSearchParams(faceUrl.search);
                 let faceJumpUrl = decodeURIComponent(faceSearch.get('u'));
@@ -124,7 +124,7 @@ class ParseCard {
                     }
                 }
             } else {
-                if (a_arr[i].href.indexOf('denetme.net') > 0) {
+                if (a_arr[i].href.indexOf('denetme.net') > 0 && a_arr[i].href.indexOf('nft') == -1) {
                     let urlArr = new URL(a_arr[i].href);
                     let searchArr = new URLSearchParams(urlArr.search);
                     let deUrlParams = searchArr.get('deUrlParams') || '{}';
@@ -230,18 +230,33 @@ class ParseCard {
 
     createIframe({ post_Id = '', tweet_Id = '', tweet_author = '' }, if_center = false) {
         let _iframe = document.createElement('iframe')
-        let tweet_str = '';
+        let _iframe_url = ''
+        let tweet_str = ''
         if (tweet_Id) {
-            tweet_str = `&tweetId=${tweet_Id}`;
+            tweet_str = `&tweetId=${tweet_Id}`
         }
         _iframe.id = post_Id
-        _iframe.src = chrome.runtime.getURL('/iframe/red-packet.html') + `?postId=${post_Id}${tweet_str}&tweet_author=${tweet_author}&window_origin=${window.location.origin}`;
+        _iframe_url =  chrome.runtime.getURL('/iframe/red-packet.html') + `?postId=${post_Id}${tweet_str}&tweet_author=${tweet_author}&window_origin=${window.location.origin}`;
+        // debugger mode
+        if(window.location.href.includes('denet_debugger')){
+            _iframe_url = _iframe_url + '&denet_debugger=1'
+        }
+        _iframe.src = _iframe_url
         _iframe.style.cssText = 'border:medium none; width:375px; min-height:500px;'
         if (if_center) {
             _iframe.style.cssText = 'border:medium none; width:375px; min-height:500px; display:block; margin:auto;'
         }
         return _iframe
     }
+    createNftIframe({ project_Id }) {
+        let project_arr = project_Id.split('/');
+        let projectId = project_arr[0]
+        let _iframe = document.createElement('iframe')
+        _iframe.id = project_Id
+        _iframe.src = chrome.runtime.getURL('/iframe/nft-card.html') + `?projectId=${projectId}&twitterAccount=${project_arr[1]}`;
+        _iframe.style.cssText = 'border:medium none; width:375px; min-height:300px;'
+        return _iframe
+    }
     isHasIframeByArticle(dom_card) {
         if (!dom_card || !dom_card.parentElement) {
             return
@@ -328,26 +343,49 @@ class ParseCard {
 
         dom.style = 'min-height:500px'
         if (dom) {
-            // let div = document.createElement('div')
-            // div.style.color = 'red'
-            // div.innerText = `
-            // tweet_Id:${tweet_Id} , 
-            // post_Id:${post_Id}
-            // 获取dom时间:${time}
-            // 短链接:${short_url}
-            // 渲染时长:${(new Date().getTime() - time) / 1000}s
-            // `
-            // dom.parentElement.appendChild(div)
+            // debugger mode
+            if (window.location.href.includes('denet_debugger')) {
+                let div = document.createElement('div')
+                div.style.color = 'red'
+                div.innerText = `
+                tweet_Id:${tweet_Id} , 
+                post_Id:${post_Id}
+                获取dom时间:${time}
+                短链接:${short_url}
+                渲染时长:${(new Date().getTime() - time) / 1000}s
+                `
+                dom.parentElement.appendChild(div)
+            }
             dom.appendChild(this.createIframe({ post_Id, tweet_Id }))
         }
+    }
+    replaceNftDomRedPacket({ dom_card, tweet_Id, post_Id, time, short_url }) {
+        if (!dom_card || !dom_card.parentElement) {
+            return
+        }
+        let dom = dom_card.querySelector('div[aria-labelledby]')
+        if (dom) {
+            for (let i = 0; i < dom.childNodes.length; i++) {
+                if (dom.children[i].tagName.toLowerCase() != 'iframe') {
+                    dom.children[i].style.display = 'none'
+                }
+            }
+        } else {
+            dom = dom_card.querySelector('div[lang][dir=auto]').parentElement
+        }
 
+        dom.style = 'min-height:300px'
+        if (dom) {
+            let project_Id = post_Id.replace('nft/', '');
+            dom.appendChild(this.createNftIframe({ project_Id }))
+        }
     }
     replaceFacebookPacket({ dom_card, tweet_Id, short_url, tweet_author }) {
         if (!dom_card || !dom_card.parentElement) {
             return
         }
         let dom = dom_card.querySelector('div[id^=jsc_c_]').parentElement
-            dom.style = 'min-height:500px'
+        dom.style = 'min-height:500px'
         if (dom) {
             for (let i = 0; i < dom.childNodes.length; i++) {
                 if (dom.children[i].tagName.toLowerCase() != 'iframe' && (i !== 0)) {

+ 329 - 24
src/logic/content/twitter.js

@@ -1,5 +1,5 @@
 import { getChromeStorage, setChromeStorage } from '@/uilts/chromeExtension.js'
-import { throttle, getQueryString, getCookie } from '@/uilts/help'
+import { throttle, getQueryString, getCookie, nextTick } from '@/uilts/help'
 import { discordAuthRedirectUri } from '@/http/configAPI'
 import { reportSrcPublishEvent } from '@/http/publishApi'
 import Report from "@/log-center/log"
@@ -95,7 +95,25 @@ export function showTwitterPublishDialogHandler(publishRes) {
     _publishTweetEvent(publishRes, bindTwitterArtMethod);
 }
 
+export function twitterPublishHandler(res) {
+    let bigBtn = document.querySelector('a[data-testid="SideNav_NewTweet_Button"]');
+    if (bigBtn) {
+        bigBtn.click();
+    } else {
+        let smallBtn = document.querySelector('a[href="/compose/tweet"]')
+        smallBtn && smallBtn.click();
+    }
+
+    nextTick(() => {
+        document.execCommand('selectAll');
+    }, 100).then(() => {
+        _setPublishContent(res.srcContent, 500);
+    })
+}
+
 export function showPinTips() {
+    hidePopupPage();
+    hideNoticeBindTweet();
     getChromeStorage('pinData', (res) => {
         if (!res || res.show) {
             let domPop = document.getElementById('de-pin-pop');
@@ -104,6 +122,13 @@ export function showPinTips() {
     })
 }
 
+export function hidePinTips() {
+    let pop = document.querySelector('#de-pin-pop');
+    if (pop) {
+        pop.style.display = 'none';
+    }
+}
+
 function addPinedPop() {
     let domPop = document.getElementById('de-pin-pop');
 
@@ -125,12 +150,7 @@ function addPinedPop() {
     popWrapper.appendChild(img);
     popWrapper.appendChild(contentDom);
 
-    let layersDom = document.getElementById('layers');
-    if (layersDom) {
-        layersDom.appendChild(popWrapper);
-    } else {
-        document.querySelector('body').appendChild(popWrapper);
-    }
+    document.querySelector('body').appendChild(popWrapper);
 
     let deCheck = document.querySelector('#de-check');
     let deRemind = document.querySelector('#de-remind');
@@ -237,7 +257,10 @@ function _addDeNetEditBtn(parent, dom, isClick = false) {
                 businessType: Report.businessType.buttonView,
                 objectType: Report.objectType.buttonSecond
             });
-            parent.parentNode.insertBefore(dom, parent.nextElementSibling);
+            let innerDeIcon = document.getElementById('de-btn1');
+            if (!innerDeIcon) {
+                parent.parentNode.insertBefore(dom, parent.nextElementSibling);
+            }
         } else {
             setTimeout(() => {
                 parent = _getScheduleDom(isClick);
@@ -330,13 +353,7 @@ function _addIframe() {
     dom.iframe = iframe;
     let iframeContent = document.getElementById('iframe-content');
     if (!iframeContent) {
-        let layersDom = document.getElementById('layers');
-        if (layersDom) {
-            layersDom.appendChild(iframe);
-        } else {
-            document.querySelector('body').appendChild(iframe);
-        }
-
+        document.querySelector('body').appendChild(iframe);
     }
 }
 
@@ -361,6 +378,8 @@ function addPublishTipsIframe(params = {}) {
 }
 
 export function noticeBindTweet(params) {
+    hidePinTips();
+    hidePopupPage();
     let iframe = document.createElement('iframe');
     iframe.id = 'de-notice-bind-tweet';
     iframe.src = chrome.runtime.getURL('/iframe/bind-tweet.html') + `?params=${JSON.stringify(params)}`;
@@ -417,7 +436,7 @@ function _deNetBtnClick() {
  * @private
  */
 let isSetContent = false;
-const _setPublishContent = throttle(function (content) {
+const _setPublishContent = throttle(function (content, time = 1000) {
     if (!isSetContent) {
         isSetContent = true;
         let inputEle = document.querySelector('div[contenteditable="true"]');
@@ -429,7 +448,7 @@ const _setPublishContent = throttle(function (content) {
             setTimeout(() => {
                 isSetContent = false;
             }, 2000)
-        }, 1000);
+        }, time);
     }
 }, 800);
 
@@ -674,7 +693,12 @@ function setIframeRedPacket(type = 'twitter') {
             // 过滤出可以请求的短链接
             parseCard.getCardParmas(card_json_data).then((res) => {
                 for (let i in res.has_post_Id_card_data) {
-                    parseCard.replaceDOMRedPacket(res.has_post_Id_card_data[i])
+                    let item = res.has_post_Id_card_data[i];
+                    if (item && item.post_Id && item.post_Id.indexOf('nft/') >= 0) {
+                        parseCard.replaceNftDomRedPacket(item)
+                    } else {
+                        parseCard.replaceDOMRedPacket(item)
+                    }
                 }
                 if (res.need_net_short_url.length > 0) {
                     // 请求短链接
@@ -708,13 +732,15 @@ export function initExecuteScript(changes) {
     }
 }
 
-const createIframe = ({ url, tweet_Id }, callback) => {
+const createNFTIframe = ({ url, id }, callback) => {
     let iframe = document.createElement('iframe')
-    iframe.id = `tweetId${tweet_Id}`
+    iframe.id = id
     iframe.src = url
+    iframe.style.cssText = 'border:medium none; width:100%; height:100%; z-index:100; position: fixed; top:0;left:0; display:none;';
     iframe.onload = () => {
-        callback()
+        callback && callback()
     }
+    document.body.appendChild(iframe);
 }
 
 
@@ -738,6 +764,7 @@ function initParseCard() {
                 checkHasDeBtn()
                 checkHasSliderDeBtn();
                 changeQueueNum(-1)
+                showNFTCard()
             }, 1000)
         } else if (inFacebook && inFacebookNode) {
             clearInterval(timer)
@@ -758,13 +785,22 @@ function initParseCard() {
 let inited = false
 // 初始化
 export function init() {
-    if(inited){
-        return 
+    if (inited) {
+        return
     }
     inited = true
     console.log('init')
 
     getDiscordAuthCode();
+    appendPopupPage();
+
+    chrome.runtime.sendMessage({ 
+        actionType: "CONTENT_SET_POPUP_CONFIG", 
+        data: {
+            popup: 'popup.html'
+        } 
+    }, () => { });
+
     let where = window.location.href.indexOf('twitter.com') < 0 && window.location.href.indexOf('facebook.com') < 0;
     if (where) {
         return
@@ -772,10 +808,13 @@ export function init() {
     twitterPinLogin();
     // 渲染dom
     initParseCard()
-
+    showNFTCard()
     renderDom();
     checkTwitterTaskState();
 
+    onBodyClick();
+    initBuyNFT();
+
     getChromeStorage("popupShowPublishDialog", (res) => {
         console.log("popupShowPublishDialog", res);
         if (res && res.show) {
@@ -840,6 +879,13 @@ export function replyHandle(params) {
         let tweetReply = document.querySelector('div[data-testid="tweetButtonInline"]');
         if (tweetReply) {
             tweetReply.addEventListener('click', function () {
+                // 详情页回复按钮点击
+                Report.reportLog({
+                    pageSource: Report.pageSource.mainPage,
+                    businessType: Report.businessType.buttonClick,
+                    objectType: Report.objectType.replyClickByDetailPage
+                });
+
                 let eleList = tweetReply.parentNode.parentNode.parentNode.parentNode.parentNode.querySelectorAll('span[data-text="true"]');
                 reportReplyResult(eleList, params, () => {
                     // iframe.contentWindow.postMessage({ actionType: 'CONTENT_RED_PACKET_REPLY_RASK_FINSH', data: {} }, '*');
@@ -889,6 +935,13 @@ function onReplyDialogOpen(params, iframe) {
 
         if (dialog && replyBtn) {
             replyBtn.addEventListener('click', function () {
+                // 推文页回复按钮点击
+                Report.reportLog({
+                    pageSource: Report.pageSource.mainPage,
+                    businessType: Report.businessType.buttonClick,
+                    objectType: Report.objectType.replyClickByTwitterList
+                });
+
                 let eleList = dialog.querySelector('div[contenteditable="true"]').querySelectorAll('span[data-text="true"]');
                 reportReplyResult(eleList, params, () => {
                     // 上報完成
@@ -901,6 +954,15 @@ function onReplyDialogOpen(params, iframe) {
 }
 
 const reportReplyResult = throttle(function (eleList, params, cb) {
+    //未过滤的回复文本
+    Report.reportLog({
+        pageSource: Report.pageSource.mainPage,
+        businessType: Report.businessType.buttonClick,
+        objectType: Report.objectType.replyInputTextGet
+    }, {
+        replyStr: eleList
+    });
+
     if (eleList && eleList.length) {
         let atList = [];
         for (let i = 0; i < eleList.length; i++) {
@@ -913,9 +975,27 @@ const reportReplyResult = throttle(function (eleList, params, cb) {
             }
         }
 
+        // 去重过滤文本
         atList = Array.from(new Set(atList));
 
+        Report.reportLog({
+            pageSource: Report.pageSource.mainPage,
+            businessType: Report.businessType.buttonClick,
+            objectType: Report.objectType.replyFilterTextGet
+        }, {
+            replyStr: atList
+        });
+
         if (atList.length >= 3) {
+            //真实上报
+            Report.reportLog({
+                pageSource: Report.pageSource.mainPage,
+                businessType: Report.businessType.buttonClick,
+                objectType: Report.objectType.replyReport
+            }, {
+                replyStr: atList
+            });
+
             fetchAddFinishEvent({
                 eventType: params.type,
                 luckdropId: params.taskLuckdropId
@@ -1166,4 +1246,229 @@ const TwitterLikeAPI = (tweet_Id) => {
     }).catch(() => {
         chrome.runtime.sendMessage({ actionType: "DO_TASK", do_type: 'api', tweet_Id, task_type: 'like', task_data: '', task_done: false }, () => { })
     })
+}
+
+export const hideBuyNFT = () => {
+    let iframe = document.querySelector('#nftProjectId')
+    iframe.style.display = 'none'
+    iframe.src = ''
+}
+
+export const showBuyNFT = ({ nft_project_Id }) => {
+    if (!nft_project_Id) {
+        return
+    }
+    let iframe = document.querySelector('#nftProjectId')
+    iframe.style.display = 'block'
+    iframe.src = chrome.runtime.getURL(`/iframe/buy-nft.html?nftProjectId=${nft_project_Id}`)
+}
+
+const initBuyNFT = () => {
+    let url = chrome.runtime.getURL(`/iframe/buy-nft.html`)
+    let id = `nftProjectId`
+    createNFTIframe({ url, id })
+}
+
+export const showNFTCard = () => {
+    let urlInfo = new URL(window.location.href)
+    let isTwitter = urlInfo.hostname === 'twitter.com'
+    let userElem = document.querySelector('div[data-testid="UserName"]');
+    let sideElem = document.querySelector('div[data-testid="sidebarColumn"]')
+    let tabIndex = sideElem && sideElem.querySelector('div[tabindex="0"]');
+    let isAppend = document.querySelector('div[id="de-nft-node"]');
+    let where = isTwitter && userElem && tabIndex;
+    if (where) {
+        let iframe = document.createElement('iframe');
+        iframe.src = chrome.runtime.getURL(`/iframe/nft-card.html`)
+        iframe.style.cssText = 'border:medium none; width:100%; height:290px;';
+        let nftElement = document.createElement('div');
+        nftElement.id = 'de-nft-node';
+        nftElement.innerHTML = `
+                ${iframe.outerHTML}
+                <style>
+                    #de-nft-node {height:290px; margin-bottom:17px; display:none;}
+                </style>
+            `;
+
+        if (tabIndex && tabIndex.firstChild && tabIndex.firstChild.childNodes && !isAppend) {
+            tabIndex.firstChild.insertBefore(nftElement, tabIndex.firstChild.childNodes[2]);
+        }
+    }
+}
+
+export const showNFTSale = () => {
+    document.querySelector('div[id="de-nft-node"]').style.display = 'block';
+}
+
+export const appendPopupPage = (params = {}) => {
+    let { path = '' } = params;
+
+    let iframe = document.createElement('iframe');
+        iframe.id = 'de-popup-page';
+        iframe.src = chrome.runtime.getURL('/iframe/popup-page.html') + `#${path}`;
+        iframe.style.cssText = `border: medium none; width: 375px;
+        height: 650px;position: fixed; right: 16px; top: 16px;background: #FFFFFF;border: 0.5px solid #919191;box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.2);box-sizing: border-box;z-index: 90000;
+        animation-duration: 0.5s !important;
+        animation-timing-function: ease-in-out !important;
+        animation-fill-mode: forwards !important;
+        transition: all 1s ease 0s !important;right: -385px;transform: translateX(385px);`
+    let iframeContent = document.getElementById('de-popup-page');
+
+    let overlayDom = document.createElement('div');
+        overlayDom.id = 'de-popup-overlay';
+        overlayDom.style.cssText = `position: fixed;z-index: 88888;top: 0;
+        left: 0;width: 100%;height: 100%;opacity: 0;display: none`;
+
+    let overlay = document.getElementById('de-popup-overlay');
+    let body = document.querySelector('body');
+
+    if (!iframeContent && body) {
+        document.querySelector('body').appendChild(iframe);
+        if(!overlay) {
+            document.querySelector('body').appendChild(overlayDom);
+
+            overlayDom.addEventListener('click', function() {
+                hidePopupPage();
+            })
+        } else {
+            overlay.addEventListener('click', function() {
+                hidePopupPage();
+            })
+        }
+    }
+}
+
+let showPopupPageFrom = '';
+export const showPopupPage = (params = {}) => {
+    let { path = '', from } = params;
+    showPopupPageFrom = from;
+    hidePinTips();
+    hideNoticeBindTweet();
+
+    let iframe = document.getElementById('de-popup-page');
+    if (!iframe) {
+        appendPopupPage();
+        iframe = document.getElementById('de-popup-page');
+    }
+    if(iframe) {
+        if(path) {
+            iframe.src = chrome.runtime.getURL('/iframe/popup-page.html') + `#${path}`;
+        }
+        iframe.style.transform = 'translateX(-' + 395 + 'px)';
+    
+        chrome.runtime.sendMessage({ 
+            actionType: "CONTENT_POPUP_PAGE_SHOW", 
+            data: { } 
+        }, () => { });
+
+        chrome.runtime.sendMessage({ 
+            actionType: "CONTENT_SET_POPUP_CONFIG", 
+            data: {
+                popup: ''
+            } 
+        }, () => { });
+    
+        let overlay = document.getElementById('de-popup-overlay');
+        overlay.style.display = 'block';
+    
+        let htmlDom = document.querySelector('html');
+        if(htmlDom) {
+            htmlDom.style.overflowY = 'hidden';
+        }
+    }
+}
+
+export const hidePopupPage = () => {
+    let iframe = document.getElementById('de-popup-page');
+    if (iframe) {
+        iframe.style.transform = 'translateX(' + 385 + 'px)';
+
+        let overlay = document.getElementById('de-popup-overlay');
+        overlay.style.display = 'none';
+
+        let htmlDom = document.querySelector('html');
+        if(htmlDom) {
+            htmlDom.style.overflowY = 'auto';
+        }
+
+        chrome.runtime.sendMessage({ 
+            actionType: "CONTENT_SET_POPUP_CONFIG", 
+            data: {
+                popup: 'popup.html'
+            } 
+        }, () => { });
+
+        if(showPopupPageFrom == 'BUY_NFT_FINISH') {
+            chrome.runtime.sendMessage({ 
+                actionType: "CONTENT_GET_PINED", 
+                data: {} 
+            }, () => { });
+            showPopupPageFrom = '';
+        }
+    }
+}
+
+export const tiggerInjectPopupPage = () => {
+    let iframeContent = document.getElementById('de-popup-page');
+    if (iframeContent) {
+        hidePinTips();
+        hideNoticeBindTweet();
+        let {transform = ''} = iframeContent.style;
+        if(transform == 'translateX(385px)' || !transform) {
+            showPopupPage();
+        } else {
+            hidePopupPage();
+        }
+    } else {
+        appendPopupPage();
+        setTimeout(() => {
+            let iframe = document.getElementById('de-popup-page');
+            let {transform = ''} = iframe.style;
+            if(transform == 'translateX(385px)' || !transform) {
+                showPopupPage();
+            } else {
+                hidePopupPage();
+            }
+        }, 300)
+    }
+}
+
+const onBodyClick = () => {
+    if(window.location.href.indexOf('api.twitter.com') < 0) {
+        document.querySelector('body').addEventListener('click', function () {
+            console.log('click')
+            // hidePopupPage();
+        })
+    } 
+}
+
+
+export const setPopupConfByPopupPage = () => {
+    let iframe = document.getElementById('de-popup-page');
+    if (iframe) {
+        let {transform = ''} = iframe.style;
+
+        if(transform && transform == 'translateX(-395px)') {
+            chrome.runtime.sendMessage({ 
+                actionType: "CONTENT_SET_POPUP_CONFIG", 
+                data: {
+                    popup: ''
+                } 
+            }, () => { });
+        } else {
+            chrome.runtime.sendMessage({ 
+                actionType: "CONTENT_SET_POPUP_CONFIG", 
+                data: {
+                    popup: 'popup.html'
+                } 
+            }, () => { });
+        }
+    } else {
+        chrome.runtime.sendMessage({ 
+            actionType: "CONTENT_SET_POPUP_CONFIG", 
+            data: {
+                popup: 'popup.html'
+            } 
+        }, () => { });
+    }
 }

+ 5 - 2
src/manifest.json

@@ -2,7 +2,7 @@
     "manifest_version": 3,
     "name": "DeNet",
     "description": "Growing more twitter followers with Denet",
-    "version": "1.0.7",
+    "version": "1.1.0",
     "background": {
         "service_worker": "/js/background.js"
     },
@@ -66,7 +66,10 @@
                 "/iframe/red-packet.html",
                 "/iframe/home.html",
                 "/iframe/publish-tips.html",
-                "/iframe/bind-tweet.html"
+                "/iframe/bind-tweet.html",
+                "/iframe/nft-card.html",
+                "/iframe/buy-nft.html",
+                "/iframe/popup-page.html"
             ],
             "matches": [
                 "<all_urls>"

+ 33 - 0
src/router/buy-nft.js

@@ -0,0 +1,33 @@
+import { createRouter, createWebHashHistory, createWebHistory } from "vue-router"
+import Home from '@/view/iframe/buy-nft/buy/home.vue'
+import Pay from '@/view/iframe/buy-nft/buy/pay.vue'
+import OpenBox from '@/view/iframe/buy-nft/buy/open-box.vue'
+
+let routes = [
+    {
+        path: '/',
+        name: 'Home',
+        component: Home,
+    },
+    {
+        path: "/pay",
+        name: 'Pay',
+        component: Pay
+    }, {
+        path: "/open_box",
+        name: 'OpenBox',
+        component: OpenBox
+    }
+]
+// 3. 创建路由实例
+const router = createRouter({
+
+    // 4. 采用hash 模式
+    history: createWebHashHistory('buy-nft.html'),
+    // 采用 history 模式
+    // history: createWebHistory('popup.html'),
+    routes, // short for `routes: routes`
+});
+
+export default router
+

+ 45 - 5
src/router/popup.js

@@ -1,6 +1,13 @@
 /* eslint-disable */
 import { createRouter, createWebHashHistory, createWebHistory } from "vue-router"
-import Home from '@/view/popup/popup.vue'
+import Tabcontent from '@/view/popup/tabbar-page/index.vue'
+import Home from '@/view/popup/tabbar-page/wallter/popup.vue'
+import NFT  from '@/view/popup/tabbar-page/nft/index.vue'
+import Message from '@/view/popup/tabbar-page/message/index.vue'
+import More from '@/view/popup/tabbar-page/more/index.vue'
+
+import NFTDetail  from '@/view/popup/tabbar-page/nft/detail.vue'
+
 import Withdraw from '@/view/popup/withdraw/index.vue'
 import WithdrawInfo from '@/view/popup/withdraw/info.vue'
 import WithdrawConfirm from '@/view/popup/withdraw/confirm.vue'
@@ -14,12 +21,36 @@ import TopUp from '@/view/popup/top-up/index.vue'
 import TopUpInfo from '@/view/popup/top-up/info.vue'
 import TopUpHome from '@/view/popup/top-up/home.vue'
 
+import CurrencyDetail from '@/view/popup/currency-detail.vue'
+
 // 2. 定义路由配置
 const routes = [
     {
-        path: "/",
-        name: 'home',
-        component: Home
+        path: '/',
+        name: 'tabcontent',
+        component: Tabcontent,
+        children: [
+            {
+                path: "",
+                name: 'home',
+                component: Home
+            },
+            {
+                path: "NFT",
+                name: 'NFT',
+                component: NFT
+            },
+            {
+                path: "message",
+                name: 'message',
+                component: Message
+            },
+            {
+                path: "more",
+                name: 'more',
+                component: More
+            },
+        ]
     },
     {
         path: '/withdraw',
@@ -81,8 +112,17 @@ const routes = [
         path: '/transactions',
         name: 'Transactions',
         component: Transactions
+    },
+    {
+        path: '/NFTDetail',
+        name: 'NFTDetail',
+        component: NFTDetail
+    },
+    {
+        path: '/currencyDetail',
+        name: 'currencyDetail',
+        component: CurrencyDetail
     }
-
 ]
 
 

+ 29 - 1
src/uilts/help.js

@@ -100,4 +100,32 @@ export function getCookie(name){
       }
   }
   return "";
-}
+}
+
+export function nextTick(fn, time = 50) {
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            if (fn) fn();
+            resolve();
+        }, time)
+    })
+}
+
+export function getBrowser() {
+    let browser;
+    let UserAgent = navigator.userAgent.toLowerCase();
+    if (UserAgent.indexOf('chrome') > -1 && UserAgent.indexOf('safari') > -1) {
+        browser = `Chrome`
+    } else if (UserAgent.indexOf('firefox') > -1) {
+        browser = `Firefox`
+    } else if (UserAgent.indexOf('opera') > -1) {
+        browser = `Opera`
+    } else if (UserAgent.indexOf('safari') > -1 && UserAgent.indexOf('chrome') == -1) {
+        browser = `Safari`
+    } else if (UserAgent.indexOf('edge') > -1) {
+        browser = `Edge`
+    } else {
+        browser = `Other`
+    }
+    return browser;
+}

+ 29 - 10
src/view/components/currency-list.vue

@@ -2,14 +2,23 @@
 <template>
     <div class="currency-list-wrapper">
         <div class="search-input-wrapper">
-            <input class="input" v-model="keywords" @input="onInput" placeholder="Search name" />
+            <input class="input" 
+                :style="{width: !showRefresh ? '100%' : '88%'}"
+                v-model="keywords" 
+                placeholder="Search name"
+                @input="onInput" />
+
             <img :src="require('../../assets/svg/icon-form-refresh.svg')" 
+                v-if="showRefresh"
                 class="icon"
                 :class="{ 'icon-refresh-rotate': refreshRotate }"
                 @click="refresh">
-            <img :src="require('../../assets/svg/icon-clear-search.svg')" class="icon-clear"
-            v-if="keywords"
-            @click="clearIpt" >
+
+            <img :src="require('../../assets/svg/icon-clear-search.svg')" 
+                class="icon-clear"
+                v-if="keywords"
+                :style="{right: !showRefresh ? '6%' : '16%'}"
+                @click="clearIpt" >
         </div>
         <div class="list-wrapper" ref="listWrapperDom" @scroll="listScroll">
             <div class="page-list" ref="listContentDom" v-if="!showSearch">
@@ -22,7 +31,7 @@
                             {{ item.type == 1 ? 'Cash' : 'Crypto' }}
                         </div>
                         <div class="item-detail" v-for="(data, idx) in item.data" :key="idx"
-                            @click="selectCurrency(data.currencies)">
+                            @click="selectCurrency(data)">
                             <div class="left">
                                 <img class="icon-currency" :src="data.currencies[0].iconPath" />
                                 <div class="currency-info">
@@ -47,13 +56,13 @@
                     </template>
                 </div>
                 <div class="no-data" v-if="show_empty">
-                    {{page != 'top-up' ? 'Not found' : 'No balance'}}
+                    Not found
                 </div>
 
             </div>
             <!-- 显示搜索结果列表 -->
             <div class="search-list" v-else>
-                <div class="item-detail" v-for="(data, idx) in searchList" :key="idx" @click="selectCurrency(data.currencies)">
+                <div class="item-detail" v-for="(data, idx) in searchList" :key="idx" @click="selectCurrency(data)">
                     <div class="left">
                         <img class="icon-currency" :src="data.currencies[0].iconPath" />
                         <div class="currency-info">
@@ -75,7 +84,7 @@
                     </div>
                 </div>
                 <div class="no-data" v-if="!searchList.length">
-                    {{page != 'top-up' ? 'Not found' : 'No balance'}}
+                    Not found
                 </div>
             </div>
         </div>
@@ -96,6 +105,10 @@ const props = defineProps({
     filterEmptyBalance: {
         type: Boolean,
         default: false
+    },
+    showRefresh: {
+        type: Boolean,
+        default: true
     }
 })
 let keywords = ref('');
@@ -233,7 +246,7 @@ const listScroll = (e) => {
     let scrollTop = e.target.scrollTop || 0;
     if (
         listReqParams.loadMore === false &&
-        wrapperHeight + scrollTop >= listContentHeight
+        wrapperHeight + scrollTop >= (listContentHeight - 50)
     ) {
         listReqParams.loadMore = true;
         listReqParams.params.pageNum++;
@@ -276,6 +289,11 @@ defineExpose({
             border: none;
             outline: none;
             padding: 10px 80px 10px 22px;
+            color: #adadad;
+        }
+
+        input::placeholder {
+            color: #adadad;
         }
 
         .icon {
@@ -308,7 +326,6 @@ defineExpose({
                 padding: 0 10px;
                 box-sizing: border-box;
                 background: #f7f7f7;
-                font-weight: 500;
                 font-size: 14px;
                 color: #a2a2a2;
 
@@ -335,6 +352,7 @@ defineExpose({
                     width: 24px;
                     height: 24px;
                     margin-right: 12px;
+                    margin-top: 4px;
                 }
 
                 .currency-info {
@@ -342,6 +360,7 @@ defineExpose({
                         font-weight: 500;
                         font-size: 15px;
                         margin-bottom: 5px;
+                        word-break: break-all;
                     }
 
                     .desc {

+ 88 - 44
src/view/components/popup-transactions.vue

@@ -22,7 +22,7 @@
                         <template v-if="item.bizType == 1">
                             <img class="icon-avatar" :src="item.bizData.avatarUrl" />
                             <img class="icon-give" :src="
-                                require('@/assets/svg/icon-giveaways.svg')
+                                require('@/assets/svg/icon-get-giveaways-s.svg')
                             " />
                         </template>
                         <!-- 收入- 任务红包结余退款, -->
@@ -55,6 +55,12 @@
                                         require('@/assets/svg/icon-big-give.svg')
                             " />
                         </template>
+                        <!-- 支出-买盲盒 -->
+                        <template v-else-if="item.bizType == -3">
+                            <img style="margin-left:-4px" :src="
+                                        require('@/assets/svg/icon-wallter-list-blind-box.svg')
+                            " />
+                        </template>
                     </div>
                     <div class="info-wrapper">
                         <div class="left">
@@ -77,24 +83,29 @@
                                 <template v-else-if="item.bizType == -2">
                                     Giveaways
                                 </template>
+                                <template v-else-if="item.bizType == -3">
+                                    Mystery box*{{(item.bizData && item.bizData.nftItemCount || '')}}  Sold
+                                </template>
                             </div>
                             <div class="time">{{ moment(item.createTimestamp).format('MM-DD HH:mm:ss') }}</div>
                         </div>
                         <div class="right">
                             <div class="msg">
-                                <!-- 支出--提现 -->
                                 <template v-if="item.bizType == -1">
-                                    <!-- 提现状态(0:已申请,1:支付中,2:提现成功,3:提现失败) -->
+                                    <!-- 提现支出-状态(0:已申请,1:支付中,2:提现成功,3:提现失败) -->
                                     <template v-if="item.bizData.withdrawStatus == 0 || item.bizData.withdrawStatus == 1">
                                         <div>
-                                            <div class="balance">
+                                            <div class="balance"
+                                                :class="{'balance-direction': item.trxAmountCurrencyInfo.tokenSymbol.length + ('' + item.trxAmountValue).length > 12}">
                                                 <span class="amount">
                                                     <a-tooltip :title="'-'+item.trxAmountValue">
                                                         -{{ getBit(item.trxAmountValue) || 0 }}
                                                     </a-tooltip>
                                                 </span>
-                                                <span class="name">{{ item.trxAmountCurrencyInfo.tokenSymbol }}</span>
-                                                <img :src="item.trxAmountCurrencyInfo.iconPath" alt="">
+                                                <div class="trx-amount-currency-info">
+                                                    <span class="name">{{ item.trxAmountCurrencyInfo.tokenSymbol }}</span>
+                                                    <img :src="item.trxAmountCurrencyInfo.iconPath" alt="">
+                                                </div>
                                             </div>
                                             <div class="desc">
                                                 in progress
@@ -102,26 +113,32 @@
                                         </div>
                                     </template>
                                     <template v-else-if="item.bizData.withdrawStatus == 2">
-                                        <div class="balance">
+                                        <div class="balance"
+                                            :class="{'balance-direction': item.trxAmountCurrencyInfo.tokenSymbol.length + ('' + item.trxAmountValue).length > 12}">
                                             <span class="amount">
                                                 <a-tooltip :title="'-'+item.trxAmountValue">
                                                     -{{ getBit(item.trxAmountValue) || 0 }}
                                                 </a-tooltip>
                                             </span>
-                                            <span class="name">{{ item.trxAmountCurrencyInfo.tokenSymbol }}</span>
-                                            <img :src="item.trxAmountCurrencyInfo.iconPath" alt="">
+                                            <div class="trx-amount-currency-info">
+                                                <span class="name">{{ item.trxAmountCurrencyInfo.tokenSymbol }}</span>
+                                                <img :src="item.trxAmountCurrencyInfo.iconPath" alt="">
+                                            </div>
                                         </div>
                                     </template>
                                     <template v-else-if="item.bizData.withdrawStatus == 3">
                                         <div>
-                                            <div class="balance">
+                                            <div class="balance"
+                                                :class="{'balance-direction': item.trxAmountCurrencyInfo.tokenSymbol.length + ('' + item.trxAmountValue).length > 12}">
                                                 <span class="amount"> 
                                                     <a-tooltip :title="'-'+item.trxAmountValue">
                                                         -{{ getBit(item.trxAmountValue) || 0 }}
                                                     </a-tooltip>
                                                 </span>
-                                                <span class="name">{{ item.trxAmountCurrencyInfo.tokenSymbol }}</span>
-                                                <img :src="item.trxAmountCurrencyInfo.iconPath" alt="">
+                                                <div class="trx-amount-currency-info">
+                                                    <span class="name">{{ item.trxAmountCurrencyInfo.tokenSymbol }}</span>
+                                                    <img :src="item.trxAmountCurrencyInfo.iconPath" alt="">
+                                                </div>
                                             </div>
                                             <div class="desc">
                                                 Withdrawal failed
@@ -129,25 +146,44 @@
                                         </div>
                                     </template>
                                     <template v-else>
-                                        <span class="amount">
-                                            <a-tooltip :title="'-'+item.trxAmountValue">
-                                                -{{ getBit(item.trxAmountValue) || 0 }}
-                                            </a-tooltip>
-                                        </span>
-                                        <span class="name">{{ item.trxAmountCurrencyInfo.tokenSymbol }}</span>
-                                        <img :src="item.trxAmountCurrencyInfo.iconPath" alt="">
+                                        <div class="balance"
+                                            :class="{'balance-direction': item.trxAmountCurrencyInfo.tokenSymbol.length + ('' + item.trxAmountValue).length > 12}">
+                                            <span class="amount">
+                                                <a-tooltip :title="'-'+item.trxAmountValue">
+                                                    -{{ getBit(item.trxAmountValue) || 0 }}
+                                                </a-tooltip>
+                                            </span>
+                                            <div class="trx-amount-currency-info">
+                                                <span class="name">{{ item.trxAmountCurrencyInfo.tokenSymbol }}</span>
+                                                <img :src="item.trxAmountCurrencyInfo.iconPath" alt="">
+                                            </div>
+                                        </div>
                                     </template>
                                 </template>
-                                <!-- 收入 -->
+
                                 <template v-else>
-                                    <span class="amount">
-                                        <template v-if="item.bizType == -2">-</template>
-                                        <a-tooltip :title="item.bizType == -2 ? '-' + item.trxAmountValue : item.trxAmountValue">
-                                            {{ getBit(item.trxAmountValue) || 0 }}
-                                        </a-tooltip>
-                                    </span>
-                                    <span class="name">{{ item.trxAmountCurrencyInfo.tokenSymbol }}</span>
-                                    <img :src="item.trxAmountCurrencyInfo.iconPath" alt="">
+                                    <div class="balance"
+                                        :class="{'balance-direction': item.trxAmountCurrencyInfo.tokenSymbol.length + ('' + item.trxAmountValue).length > 12}">
+
+                                        <!--支出—— -2:零钱余额支付 、-3: NFT盲盒余额支付 -->
+                                        <span class="amount" v-if="item.bizType == -2 || item.bizType == -3">
+                                            -
+                                            <a-tooltip :title="'-' + item.trxAmountValue">
+                                                {{ getBit(item.trxAmountValue) || 0 }}
+                                            </a-tooltip>
+                                        </span>
+                                        <!-- 收入—— bizType:1、2、3、4 -->
+                                        <span class="amount" v-else>
+                                            <a-tooltip :title="item.trxAmountValue">
+                                                {{ getBit(item.trxAmountValue) || 0 }}
+                                            </a-tooltip>
+                                        </span>
+
+                                        <div class="trx-amount-currency-info">
+                                            <span class="name">{{ item.trxAmountCurrencyInfo.tokenSymbol }}</span>
+                                            <img :src="item.trxAmountCurrencyInfo.iconPath" alt="">
+                                        </div>
+                                    </div>
                                 </template>
                             </div>
                         </div>
@@ -160,7 +196,7 @@
 
 <script setup>
 /* eslint-disable */
-import { onMounted, ref, defineProps, defineEmits, onBeforeUnmount } from "vue";
+import { onMounted, ref, defineProps, defineEmits } from "vue";
 
 import redDot from "@/view/components/red-dot.vue";
 
@@ -178,7 +214,7 @@ let listContent = ref(null);
 let listReqParams = {
     params: {
         pageNum: 1,
-        pageSize: 10,
+        pageSize: 20,
     },
     loadMore: false,
 };
@@ -212,13 +248,6 @@ const getTransactionsList = () => {
 
 onMounted(() => {
     chrome.runtime.connect({ name: "popup_transactions" });
-    readAllMsgByType({
-        params: {
-            msgType: 1
-        }
-    }).then(res => {
-        setMessageCount();
-    });
     setTimeout(() => {
         isReadMsg.value = false;
         readAllMsgByType({
@@ -305,7 +334,6 @@ const listScroll = (e) => {
         .cell {
             display: flex;
             justify-content: space-between;
-            align-items: center;
             min-height: 66px;
             box-sizing: border-box;
             padding-left: 20px;
@@ -321,17 +349,19 @@ const listScroll = (e) => {
                 position: relative;
                 margin-right: 16px;
                 box-sizing: border-box;
+                margin-top: 11px;
 
                 .icon-avatar {
                     width: 34px;
                     height: 34px;
                     border-radius: 50%;
+                    margin-right: 4px;
                 }
 
                 .icon-give {
                     position: absolute;
-                    right: -4px;
-                    bottom: 2px;
+                    right: -2px;
+                    top: 19px;
                 }
             }
 
@@ -341,7 +371,6 @@ const listScroll = (e) => {
                 display: flex;
                 justify-content: space-between;
                 align-items: center;
-                border-bottom: 1px solid #ECECEC;
                 box-sizing: border-box;
                 padding: 10px 12px 10px 0;
 
@@ -351,7 +380,7 @@ const listScroll = (e) => {
                         font-size: 14px;
                         margin-bottom: 5px;
                         word-break: break-all;
-                        max-width: 120px;
+                        max-width: 140px;
                     }
 
                     .time {
@@ -373,6 +402,7 @@ const listScroll = (e) => {
 
                         .amount {
                             max-width: 110px;
+                            min-width: 20px;
                             display: inline-block;
                             word-break: break-all;
                             text-align: right;
@@ -380,7 +410,11 @@ const listScroll = (e) => {
 
                         .name {
                             margin-left: 3px;
-                            color: #E29A2E;
+                            color: #E86F00;
+                            max-width: 130px;
+                            line-height: 14px;
+                            font-size: 13px;
+                            word-break: break-all;
                         }
 
                         img {
@@ -391,9 +425,18 @@ const listScroll = (e) => {
 
                         .balance {
                             text-align: right;
-                            margin-bottom: 4px;
                             display: flex;
                             align-items: center;
+
+                            .trx-amount-currency-info {
+                                display: flex;
+                                align-items: center;
+                            }
+                        }
+
+                        .balance-direction {
+                            flex-direction: column;
+                            align-items: flex-end;
                         }
 
                         .desc {
@@ -401,6 +444,7 @@ const listScroll = (e) => {
                             font-weight: 400;
                             font-size: 12px;
                             color: #797979;
+                            margin-top: 4px;
                         }
                     }
 

+ 144 - 0
src/view/components/preview-balance.vue

@@ -0,0 +1,144 @@
+<template>
+    <div class="card-amount">
+        <img class="icon" src="@/assets/subject/icon-balance.png" />
+        <div class="con">
+            <div class="desc">Balance</div>
+            <div class="price">{{ currentCurrencyInfo.balance }} {{ props.tokenSymbol }}</div>
+        </div>
+        <img class="refresh" :class="{ 'icon-refresh-rotate': refreshRotate }" @click="updateCurrencyBanlce"
+            :src="require('@/assets/svg/icon-form-refresh-blue.svg')" />
+    </div>
+</template>
+<script setup>
+import { ref, onMounted, reactive, defineProps, defineEmits } from "vue";
+import { syncChainTokenRechargeRecord } from "@/http/publishApi";
+const emits = defineEmits(["updateData"]);
+const props = defineProps({
+    dialogVisible: {
+        type: Object,
+        default: {},
+    },
+    currencyCode: {
+        type: String,
+        default: ''
+    },
+    tokenSymbol: {
+        type: String,
+        default: ''
+    }
+});
+
+/**
+ * 同步链上交易
+ */
+const asyncTokenRechRecord = () => {
+    syncChainTokenRechargeRecord({
+        params: {
+            currencyCode: props.currencyCode
+        }
+    }).then(res => {
+        if (res.code == 0) {
+            let data = res.data || []
+            if (data.length > 0) {
+                let currencyInfo = data[0];
+                if (currencyInfo.currencyCode == props.currencyCode) {
+                    currentCurrencyInfo.balance = currencyInfo.balance;
+                    emits('updateData', currentCurrencyInfo)
+                }
+            } else {
+                currentCurrencyInfo.balance = 0
+                emits('updateData', currentCurrencyInfo)
+            }
+        }
+    })
+}
+// 刷新按钮旋转
+let refreshRotate = ref(false);
+
+// 当前选择的货币信息
+let currentCurrencyInfo = reactive({
+    currencyCode: "",
+    currencyName: "",
+    balance: "",
+    currencyType: "",
+    iconPath: "",
+    minAmount: "",
+    tokenChain: "",
+    tokenSymbol: "",
+    usdEstimateBalance: ""
+});
+
+/**
+ * 更新货币余额
+ */
+const updateCurrencyBanlce = () => {
+    if (!refreshRotate.value) {
+        refreshRotate.value = true;
+        setTimeout(() => {
+            refreshRotate.value = false;
+        }, 1000)
+    }
+    asyncTokenRechRecord()
+}
+
+
+onMounted(() => {
+    console.log(props.currencyCode)
+    if (!props.currencyCode) {
+        return
+    }
+    asyncTokenRechRecord()
+    setInterval(() => {
+        asyncTokenRechRecord()
+    }, 10000)
+})
+
+</script>
+
+<style lang="scss" scoped>
+.card-amount {
+    overflow: hidden;
+    display: flex;
+    align-items: center;
+    padding: 20px;
+    border-radius: 20px;
+    border: 1px solid #E6E6E6;
+
+    .icon {
+        width: 40px;
+        height: 40px;
+    }
+
+    .con {
+        flex: 1;
+        padding: 0 10px;
+
+        .desc {
+            color: rgba($color: #000000, $alpha: 0.5);
+            font-size: 12px;
+            margin-bottom: 4px;
+            line-height: 14px;
+        }
+
+        .price {
+            font-size: 16px;
+            font-weight: bold;
+            line-height: 19px;
+        }
+    }
+
+    .refresh {
+        cursor: pointer;
+        width: 30px;
+        height: 30px;
+        margin-top: -5px;
+    }
+
+
+}
+
+.icon-refresh-rotate {
+    transform: rotate(360deg);
+    transition-duration: 1s;
+}
+</style>

+ 300 - 0
src/view/iframe/buy-nft/buy/home.vue

@@ -0,0 +1,300 @@
+<template>
+    <div class="dialog">
+        <!-- home -->
+        <div class="area-title">
+            <img :src="require('@/assets/svg/icon-close.svg')" @click="clickClose" />
+            <div class="title">NFT Mystery box</div>
+        </div>
+        <!-- 内容 -->
+        <div class="area-content">
+            <img :src="state.data.mysteryBoxImagePath" class="box" v-show="state.data.mysteryBoxImagePath" />
+            <img :src="require('@/assets/svg/icon-default.svg')" class="box" v-show="!state.data.mysteryBoxImagePath" />
+        </div>
+
+        <!-- 底部 -->
+        <div class="footer" v-show="state.data.mysteryBoxImagePath">
+            <!-- 首页 -->
+            <div class="mark">
+                <div class="sold">SOLD: {{ state.data.itemSoldCount || 0 }}/{{ state.data.itemTotalCount || 0 }} </div>
+                <div class="limit">Buy Limit: {{ state.data.userBuyCount || 0 }}/{{ state.data.perUserBuyLimit || 0 }}
+                </div>
+            </div>
+            <div class="btn-area">
+                <template v-for="item in state.data.salePlans.splice(0, 2).reverse()">
+                    <div class="buy1" @click="clickJump(item)" v-if="item.itemCount == 1 && (state.data.perUserBuyLimit - state.data.userBuyCount) >= 1
+                    && (state.data.itemTotalCount - state.data.itemSoldCount) >= 1">
+                        <template v-if="(item.price.length + item.currencyInfo.tokenSymbol.length) > 30">
+                            <div class="left">Buy 1</div>
+                            <div class="right">
+                                <p>{{ item.price }}</p>
+                                <p>{{ item.currencyInfo.tokenSymbol }}</p>
+                            </div>
+                        </template>
+                        <template v-else>
+                            <div class="left">Buy 1</div>
+                            <div class="right">
+                                {{ item.price }}
+                                {{ item.currencyInfo.tokenSymbol }}
+                            </div>
+                        </template>
+                    </div>
+                    <div class="buy1 grey" v-if="item.itemCount == 1 && ((state.data.perUserBuyLimit - state.data.userBuyCount) <= 0
+                    || (state.data.itemTotalCount - state.data.itemSoldCount) <= 0)">
+                        <template v-if="(item.price.length + item.currencyInfo.tokenSymbol.length) > 30">
+                            <div class="left">Buy 1</div>
+                            <div class="right">
+                                <p>{{ item.price }}</p>
+                                <p>{{ item.currencyInfo.tokenSymbol }}</p>
+                            </div>
+                        </template>
+                        <template v-else>
+                            <div class="left">Buy 1</div>
+                            <div class="right">
+                                {{ item.price }}
+                                {{ item.currencyInfo.tokenSymbol }}
+                            </div>
+                        </template>
+                    </div>
+
+                    <div class="buy5" v-if="item.itemCount == 5 && (state.data.perUserBuyLimit - state.data.userBuyCount) >= 5 &&
+                    (state.data.itemTotalCount - state.data.itemSoldCount) >= 5" @click="clickJump(item)">
+                        <div class="left">Buy {{ item.itemCount }}</div>
+
+                        <div class="right" v-if="(item.price.length + item.currencyInfo.tokenSymbol.length) > 30">
+                            <div class="usdt">
+                                <p>{{ item.price }}</p>
+                                <p>{{ item.currencyInfo.tokenSymbol }}</p>
+                            </div>
+                            <div class="off">
+                                <p>{{ item.discount }}</p>
+                            </div>
+                        </div>
+                        <div class="right" v-else>
+                            <div class="usdt">{{ item.price }} {{ item.currencyInfo.tokenSymbol }}</div>
+                            <div class="off">{{ item.discount }}</div>
+                        </div>
+                    </div>
+                </template>
+            </div>
+        </div>
+    </div>
+</template>
+<script setup>
+import router from "@/router/buy-nft.js";
+import { onMounted, reactive, inject, } from "vue";
+import { getNftMysteryBoxSaleInfo } from "@/http/nft";
+import BtnLoading from '../components/btn-loading.vue'
+import { getQueryString } from "@/uilts/help";
+let pay_info = inject('pay_info');
+let state = reactive({
+    data: {
+        salePlans: [
+            {
+                currencyCode: 'BSC_TESTNET_BF_6X',
+                discount: '20$',
+                itemCount: 5,
+                price: 23,
+                salePlanId: '2'
+            },
+            {
+                currencyCode: 'BSC_TESTNET_BF_6X',
+                discount: '20$',
+                itemCount: 1,
+                price: 123,
+                salePlanId: '123'
+            }
+        ]
+    }
+})
+const clickClose = () => {
+    chrome.tabs.getCurrent((tab) => {
+        chrome.tabs.sendMessage(tab.id, {
+            actionType: "IFRAME_TWITTER_HIDE_BUY_NFT",
+        }, (res) => { });
+    })
+}
+const clickJump = (item) => {
+    pay_info.home.sale_plan = item
+    router.push({ path: '/pay' });
+}
+onMounted(() => {
+    let nft_project_Id = getQueryString('nftProjectId') || ''
+    if (!nft_project_Id) {
+        return
+    }
+    getNftMysteryBoxSaleInfo({
+        params: {
+            nftProjectId: nft_project_Id
+        }
+    }).then((res) => {
+        if (res.code == 0) {
+            state.data = res.data
+            pay_info.home = res.data
+        } else {
+
+        }
+    }).catch(() => {
+
+    })
+})
+</script>
+
+<style lang="scss" scoped>
+.dialog {
+    background: #fff;
+    border-radius: 25px;
+    max-width: 1000px;
+    min-width: 800px;
+    max-height: 90%;
+    min-height: 90%;
+    z-index: 23;
+    display: flex;
+    flex-direction: column;
+
+
+    .area-title {
+        width: 100%;
+        min-height: 48px;
+        display: flex;
+        align-items: center;
+        border-bottom: 1px solid #D9D9D9;
+        font-weight: 500;
+        font-size: 16px;
+        letter-spacing: 0.3px;
+        color: #000000;
+
+        img {
+            width: 24p;
+            height: 24px;
+            margin-left: 14px;
+            margin-right: 12px;
+            cursor: pointer;
+        }
+
+    }
+
+    .area-content {
+        flex: 1;
+        overflow-y: auto;
+
+        img {
+            width: 100%;
+            height: 100%;
+        }
+    }
+
+    .footer {
+        border-top: 1px solid #D9D9D9;
+        width: 100%;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+
+        .loading {
+            width: 24px;
+        }
+
+        .mark {
+            margin-left: 20px;
+
+            .sold {}
+
+            .limit {
+                color: #AF934E;
+                margin-right: 25px;
+            }
+        }
+
+        .btn-area {
+            height: 100%;
+            display: flex;
+            padding: 15px 0;
+            min-height: 50px;
+
+            .buy5 {
+                border: 1px solid #1D9BF0;
+                background: rgba(29, 155, 240, 0.01);
+                border-radius: 100px;
+                color: #1D9BF0;
+                min-width: 217px;
+                display: flex;
+
+                justify-content: space-between;
+                align-items: center;
+                padding: 10px 15px 10px 20px;
+                font-weight: 700;
+                font-size: 14px;
+                cursor: pointer;
+                margin-right: 12px;
+
+                .left {
+                    margin-right: 20px;
+                }
+
+                .right {
+                    text-align: right;
+                    line-height: 17px;
+
+                    p {
+                        margin: 0;
+                        padding: 0;
+                        line-height: 17px;
+                    }
+                }
+
+
+                .off {
+                    color: #AF934E;
+                    font-weight: 700;
+                    font-size: 14px;
+
+                    letter-spacing: 0.3px;
+                }
+
+                .usdt {
+                    color: #1D9BF0;
+                    font-size: 14px;
+                    font-weight: 700;
+
+                }
+            }
+
+            .buy1 {
+                cursor: pointer;
+                background: #1D9BF0;
+                color: #fff;
+                border-radius: 100px;
+                min-width: 217px;
+                display: flex;
+                align-items: center;
+                font-size: 14px;
+                font-weight: 700;
+                justify-content: space-between;
+                padding: 0 15px 0 20px;
+                margin-right: 25px;
+
+                .left {
+                    margin-right: 20px;
+                }
+
+                .right {
+                    line-height: 17px;
+                    text-align: right;
+
+                    p {
+                        margin: 0;
+                        padding: 0;
+                        line-height: 17px;
+                    }
+                }
+
+            }
+
+            .grey {
+                background: #CDCDCD;
+                cursor: not-allowed;
+            }
+        }
+    }
+}
+</style>

+ 142 - 0
src/view/iframe/buy-nft/buy/open-box.vue

@@ -0,0 +1,142 @@
+<template>
+    <!-- gif -->
+    <div class="box_content">
+        <img :src="require('@/assets/gif/box.gif')" class="box" v-show="state.box.show" />
+        <div class="nft" v-for="item in state.nft.data" v-show="item.show">
+            <div class="detail">
+                <img :src="item.imagePath" alt="" />
+            </div>
+            <p>{{ item.nftItemName }}</p>
+        </div>
+    </div>
+</template>
+<script setup>
+import { reactive, onMounted, inject } from 'vue'
+import router from "@/router/buy-nft.js";
+let pay_info = inject('pay_info');
+let state = reactive({
+    box: {
+        show: true
+    },
+    nft: {
+        data: []
+    }
+})
+
+const setAllNoShow = () => {
+    state.nft.data.forEach((item) => {
+        item.show = false
+    })
+}
+
+const showNFTs = () => {
+    let len = state.nft.data.length
+    if (len == 0) {
+        return
+    }
+    let i = 0
+    setAllNoShow()
+    state.nft.data[i].show = true
+    i++
+    let timer = setInterval(() => {
+        if (len > i) {
+            setAllNoShow()
+            state.nft.data[i].show = true
+        } else {
+            clearInterval(timer)
+            chrome.tabs.getCurrent((tab) => {
+                chrome.tabs.sendMessage(tab.id, {
+                    actionType: "IFRAME_TWITTER_HIDE_BUY_NFT",
+                }, (res) => { });
+                chrome.tabs.sendMessage(tab.id, {
+                    actionType: "IFRAME_TWITTER_SHOW_POPUP_PAGE",
+                    data: {
+                        from: 'BUY_NFT_FINISH'
+                    }
+                }, (res) => { });
+                router.replace('/')
+            })
+        }
+        i++
+    }, 2000)
+}
+
+onMounted(() => {
+    state.nft.data = pay_info.buy_items || []
+    setTimeout(() => {
+        state.box.show = false
+        showNFTs()
+    }, 2000)
+})
+</script>
+<style lang="scss" scoped>
+.box_content {
+    position: fixed;
+    text-align: center;
+    display: flex;
+    justify-content: center;
+    z-index: 2;
+
+    .box {
+        width: 200px;
+        position: absolute;
+        top: 40%;
+        left: 50%;
+        margin-top: -100px;
+        margin-left: -100px;
+    }
+
+    .nft {
+        position: absolute;
+        top: 40%;
+        margin-top: -200px;
+        width: 400px;
+        height: 450px;
+        animation: myfirst 0.5s;
+
+        display: flex;
+        flex-direction: column;
+
+        .detail {
+            flex: 1;
+            text-align: center;
+
+            img {
+                width: 100%;
+                height: 100%;
+                border: 3px solid white;
+                border-radius: 10px;
+            }
+        }
+
+        p {
+            margin: 0;
+            padding: 0;
+            margin-top: 20px;
+            color: #FFFFFF;
+            font-size: 16px;
+            font-weight: 700;
+        }
+
+    }
+}
+
+@keyframes myfirst {
+    0% {
+        width: 300px;
+        height: 300px;
+    }
+
+    50% {
+        width: 450px;
+        height: 500px;
+        margin-top: -210px;
+    }
+
+    100% {
+        margin-top: -200px;
+        width: 400px;
+        height: 450px;
+    }
+}
+</style>

+ 431 - 0
src/view/iframe/buy-nft/buy/pay.vue

@@ -0,0 +1,431 @@
+<template>
+    <div class="dialog">
+        <!-- home -->
+        <div class="area-title">
+            <img :src="require('@/assets/svg/icon-back.svg')" @click="clickBack" />
+            <div class="title">Payment</div>
+        </div>
+        <!-- 内容 -->
+        <div class="area-content">
+            <div class="left">
+                <img :src="require('@/assets/img/img-box5.png')" v-show="pay_info.home.sale_plan.itemCount == 5"
+                    alt="" />
+                <img :src="require('@/assets/img/img-box1.png')" v-show="pay_info.home.sale_plan.itemCount == 1"
+                    alt="" />
+                <div class="tip">
+                    <span>Mystery box*{{ pay_info.home.sale_plan.itemCount }}</span>
+                    <div>
+                        <img :src="pay_info.home.sale_plan.currencyInfo.iconPath" alt="" />
+                        <span>{{ pay_info.home.sale_plan.price }}</span>
+                    </div>
+                </div>
+            </div>
+            <div class="right">
+                <div class="card-content">
+                    <template v-if="tempCurrentCurrencyInfo.currencyCode">
+                        <div class="card-title">
+                            <img class="img" :src="require('@/assets/subject/top-01.svg')" />
+                            <div class="font">Deposit to Send Giveaway</div>
+                        </div>
+                        <top-up2 v-if="tempCurrentCurrencyInfo.currencyCode" :asyncIng="asyncIng"
+                            :currentCurrencyInfo="tempCurrentCurrencyInfo" @topUpDone="topUpDone">
+                        </top-up2>
+
+                        <div class="card-title">
+                            <img class="img" :src="require('@/assets/subject/top-02.svg')" />
+                            <div class="font">Wait for the amount to arrive</div>
+                        </div>
+                        <preview-balance v-if="tempCurrentCurrencyInfo.currencyCode"
+                            :currencyCode="tempCurrentCurrencyInfo.currencyCode"
+                            :tokenSymbol="tempCurrentCurrencyInfo.tokenSymbol" @updateData="updateData">
+                        </preview-balance>
+                    </template>
+                </div>
+            </div>
+        </div>
+
+        <!-- 底部 -->
+        <div class="footer">
+            <div class="buy1" @click="clickPlay" v-if="state.is_btn_grey == false">
+                <btn-loading :color="'while'" v-if="state.loading.show"></btn-loading>
+                <template
+                    v-else-if="(pay_info.home.sale_plan.price.length + pay_info.home.sale_plan.currencyInfo.tokenSymbol.length) > 30">
+                    <div class="left">Pay</div>
+                    <div class="right">
+                        <p>{{ pay_info.home.sale_plan.price }}</p>
+                        <p>{{ pay_info.home.sale_plan.currencyInfo.tokenSymbol }}</p>
+                    </div>
+                </template>
+                <template v-else>
+                    <div class="left">Pay</div>
+                    <div class="right">
+                        {{ pay_info.home.sale_plan.price }}
+                        {{ pay_info.home.sale_plan.currencyInfo.tokenSymbol }}
+                    </div>
+                </template>
+            </div>
+            <div class="buy1 grey" v-else>
+                <template
+                    v-if="(pay_info.home.sale_plan.price.length + pay_info.home.sale_plan.currencyInfo.tokenSymbol.length) > 30">
+                    <div class="left">Pay</div>
+                    <div class="right">
+                        <p>{{ pay_info.home.sale_plan.price }}</p>
+                        <p>{{ pay_info.home.sale_plan.currencyInfo.tokenSymbol }}</p>
+                    </div>
+                </template>
+                <template v-else>
+                    <div class="left">Pay</div>
+                    <div class="right">
+                        {{ pay_info.home.sale_plan.price }}
+                        {{ pay_info.home.sale_plan.currencyInfo.tokenSymbol }}
+                    </div>
+                </template>
+            </div>
+        </div>
+    </div>
+</template>
+<script setup >
+import router from "@/router/buy-nft.js";
+import { ref, onMounted, inject, reactive } from 'vue'
+import topUp2 from "@/view/iframe/publish/components/top-up2.vue";
+import { getCurrencyInfoByCode } from "@/http/publishApi";
+import PreviewBalance from "@/view/components/preview-balance.vue";
+import BtnLoading from '../components/btn-loading.vue'
+import { payNftMysteryBoxWithBalance } from "@/http/pay";
+import { getChromeStorage } from "@/uilts/chromeExtension"
+import { ElMessage } from 'element-plus'
+let pay_info = inject('pay_info');
+let state = reactive({
+    loading: {
+        show: false
+    },
+    is_btn_grey: true
+})
+
+let currentCurrencyInfo = reactive({
+    currencyCode: "",
+    currencyName: "",
+    balance: "",
+});
+
+const updateData = (obj_data) => {
+    if (Number(obj_data.balance) >= Number(pay_info.home.sale_plan.price)) {
+        state.is_btn_grey = false
+    }
+}
+
+const clickBack = () => {
+    router.back()
+}
+const clickPlay = () => {
+    state.loading.show = true
+    payNftMysteryBoxWithBalance({
+        params: {
+            nftProjectId: pay_info.home.nftProjectId,
+            salePlanId: pay_info.home.sale_plan.salePlanId
+        }
+    }).then((res) => {
+        state.loading.show = false
+        if (res.code == 0) {
+            pay_info.buy_items = res.data.buyItems
+            router.push({ path: '/open_box' });
+        } else {
+            let msg = ''
+            switch (res.code.toString()) {
+                case '5001':
+                    msg = 'nft project not exist'
+                    break;
+                case '5002':
+                    msg = 'nft project not available'
+                    break
+                case '5101':
+                    msg = 'nft sale plan not exist'
+                    break
+                case '5102':
+                    msg = 'nft sold out'
+                    break
+                case '5103':
+                    msg = 'Purchase limit reached'
+                    break
+                default:
+                    console.log(res.msg)
+            }
+            ElMessage({
+                message: msg,
+                grouping: true,
+                type: 'warning',
+                offset: -16,
+                appendTo: document.body
+            })
+        }
+    }).catch(() => {
+        state.loading.show = false
+    })
+}
+// 余额是否同步中
+let asyncIng = ref(false);
+
+
+//临时货币信息
+let tempCurrentCurrencyInfo = ref({});
+
+
+const getLocalCurrencyInfoByCode = () => {
+    if (currentCurrencyInfo.currencyCode) {
+        getCurrencyInfo();
+    }
+}
+const getCurrencyInfo = async () => {
+    let { accessToken = '' } = await getChromeStorage('userInfo') || {};
+    if (accessToken) {
+        getCurrencyInfoByCode({
+            params: {
+                currencyCode: currentCurrencyInfo.currencyCode
+            }
+        }).then(res => {
+            if (res.code == 0 && res.data) {
+                currentCurrencyInfo = res.data;
+                tempCurrentCurrencyInfo.value = res.data;
+            }
+        });
+    }
+}
+
+onMounted(() => {
+    currentCurrencyInfo.currencyCode = pay_info.home.sale_plan.currencyCode
+    getLocalCurrencyInfoByCode()
+})
+
+
+</script>
+<style lang="scss" scoped>
+.dialog {
+    background: #fff;
+    border-radius: 25px;
+    max-width: 1000px;
+    min-width: 800px;
+    height: 90%;
+    z-index: 23;
+    display: flex;
+    flex-direction: column;
+
+
+    .area-title {
+        width: 100%;
+        height: 48px;
+        display: flex;
+        align-items: center;
+        border-bottom: 1px solid #D9D9D9;
+        font-weight: 500;
+        font-size: 16px;
+        letter-spacing: 0.3px;
+        color: #000000;
+
+        img {
+            width: 24p;
+            height: 24px;
+            margin-left: 14px;
+            margin-right: 12px;
+            cursor: pointer;
+        }
+
+    }
+
+    .area-content {
+        display: flex;
+        overflow-y: auto;
+        flex: 1;
+
+        .left {
+            width: 400px;
+            margin: 40px 56px 0 56px;
+
+            img {
+                max-width: 400px;
+                max-height: 400px;
+                width: 100%;
+                height: auto;
+            }
+
+            p {
+                margin: 0;
+                padding: 0;
+            }
+
+            .tip {
+                margin-top: 15px;
+                display: flex;
+                justify-content: space-between;
+                div{
+                    display: flex;
+                        align-items: center;
+                }
+
+                img {
+                    margin-right: 4px;
+                    width: 14px;
+                    height: 14px;
+                }
+            }
+        }
+
+        .right {
+            margin: 30px 56px 0px 30px;
+
+            .card-content {
+                float: right;
+                width: 430px;
+                padding-bottom: 22px;
+
+                .card-title {
+
+                    margin-bottom: 12px;
+                    display: flex;
+                    align-items: center;
+
+                    .img {
+                        width: 20px;
+                        height: 20px;
+                        margin-right: 8px;
+                    }
+
+                    .font {
+
+                        font-size: 17px;
+                        font-weight: 500;
+
+                        span {
+                            color: #0091e9;
+                        }
+                    }
+                }
+
+
+
+                .card-list {
+                    padding: 20px;
+                    border-radius: 20px;
+                    border: 1px solid #E6E6E6;
+
+                    .item {
+                        display: flex;
+                        justify-content: space-between;
+                        align-items: center;
+                        height: 47px;
+                        font-size: 14px;
+                        font-weight: 500;
+                        box-shadow: inset 0px -1px 0px #EAEAEA;
+                    }
+                }
+            }
+        }
+    }
+
+    .pay {
+        width: 200px;
+        height: 50px;
+        font-weight: 700;
+        font-size: 18px;
+        margin-right: 30px;
+        color: #FFFFFF;
+        background: #1D9BF0;
+        border-radius: 10000px;
+        text-align: center;
+        line-height: 50px;
+        cursor: pointer;
+
+        p {
+            margin: 0;
+            padding: 0;
+        }
+    }
+
+    .footer {
+        border-top: 1px solid #D9D9D9;
+        height: 80px;
+        width: 100%;
+        display: flex;
+        align-items: center;
+        justify-content: flex-end;
+        position: relative;
+
+        .sold {
+            position: absolute;
+            left: 20px;
+        }
+
+        .limit {
+            color: #AF934E;
+            margin-right: 25px;
+        }
+
+        .buy5 {
+            border: 1px solid #1D9BF0;
+            background: rgba(29, 155, 240, 0.01);
+            border-radius: 100px;
+            color: #1D9BF0;
+            width: 217px;
+            height: 50px;
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 0 15px 0 20px;
+            font-weight: 700;
+            font-size: 18px;
+            cursor: pointer;
+            margin-right: 12px;
+
+            .off {
+                color: #AF934E;
+                font-weight: 700;
+                font-size: 14px;
+
+                letter-spacing: 0.3px;
+            }
+
+            .usdt {
+                color: #1D9BF0;
+                font-size: 14px;
+                font-weight: 700;
+
+            }
+        }
+
+        .buy1 {
+            cursor: pointer;
+            background: #1D9BF0;
+            color: #fff;
+            border-radius: 100px;
+            min-width: 217px;
+            min-height: 50px;
+            display: flex;
+            align-items: center;
+            font-size: 14px;
+            font-weight: 700;
+            justify-content: space-between;
+            padding: 0 15px 0 20px;
+            margin-right: 25px;
+
+            .left {
+                margin-right: 20px;
+            }
+
+            .right {
+                text-align: right;
+
+                p {
+                    margin: 0;
+                    padding: 0;
+                    line-height: 17px;
+                }
+            }
+
+        }
+
+        .grey {
+            background: #CDCDCD;
+            cursor: not-allowed;
+        }
+    }
+}
+</style>

+ 29 - 0
src/view/iframe/buy-nft/components/btn-loading.vue

@@ -0,0 +1,29 @@
+<template>
+    <div class="btn-loading">
+        <img :src="require('@/assets/svg/icon-loading.svg')" class="loading" v-if="props.color == 'blue'"/>
+        <img :src="require('@/assets/svg/icon-loading-while.svg')" class="loading" v-else-if="props.color == 'while'"/>
+    </div>
+</template>
+<script setup>
+import {  defineProps } from 'vue'
+const props = defineProps({
+    color: {
+        type: String,
+        default: 'blue',
+    },
+})
+</script>
+<style lang="scss" scoped>
+.btn-loading{
+    width: 100%;
+    height: 100%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    cursor: auto;
+    img{
+        width:24px;
+        height: 24px;
+    }
+}
+</style>

+ 40 - 0
src/view/iframe/buy-nft/index.vue

@@ -0,0 +1,40 @@
+<template>
+    <div class="content">
+        <router-view></router-view>
+    </div>
+</template>
+<script setup>
+import { reactive, provide } from 'vue'
+let pay_info = reactive({})
+provide('pay_info', pay_info)
+let state = reactive({
+    // 
+    show: 'dialog-home'
+})
+
+
+
+</script>
+<style lang="scss" >
+html,
+body,
+#app {
+    width: 100%;
+    height: 100%;
+    margin: 0;
+    padding: 0;
+}
+
+body {
+
+    background-color: rgba(0, 0, 0, 0.5);
+}
+
+.content {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+</style>

+ 1 - 0
src/view/iframe/home/home.vue

@@ -39,6 +39,7 @@ onMounted(() => {
         box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.2);
         border-radius: 15px;
         position: relative;
+        overflow-y: auto;
     }
 }
 /deep/ .page-wrapper {

+ 248 - 0
src/view/iframe/nft/card.vue

@@ -0,0 +1,248 @@
+<template>
+    <div class="nft" :class="{ border: isShare }">
+        <template v-if="!isLoading">
+            <div class="title">
+                <div class="tag">
+                    <img class="logo" :src="saleData.nftProjectAvatar" />
+                    <font class="text">{{saleData.nftProjectName}}</font>
+                    <img class="tagImg" :src=" require('@/assets/img/icon-nft.png') " />
+                </div>
+                <div class="share" v-if="!isShare" @click="share">
+                    <img :src=" require('@/assets/img/icon-ntf-share.png') " />
+                </div>
+            </div>
+            <div class="content">
+                <img :src="saleData.windowImagePath" />
+            </div>
+            <div class="buy" @click="buy">
+                <img class="guide" v-if="isShowGuide" :src=" require('@/assets/img/icon-arrow.png') " />
+                <button>Buy NFT</button>
+            </div>
+        </template>
+    </div>
+</template>
+
+<script setup>
+import { onBeforeMount, ref } from 'vue'
+import { getTwitterSaleNftProjectInfo, getNftProjectInfo } from '@/http/nft'
+import { pageUrl } from "@/http/configAPI.js"
+import { getChromeStorage, setChromeStorage } from '@/uilts/chromeExtension.js'
+const saleData = ref({});
+const isShare = ref(false);
+const isLoading = ref(true);
+const isShowGuide = ref(false);
+
+const getSaleInfo = () => {
+    chrome.tabs.getCurrent((tab) => {
+        let url = new URL(tab.url);
+        let pathname = url.pathname;
+        let pathArr, account;
+        if (pathname) {
+            pathname = decodeURIComponent(pathname);
+            pathname = pathname.slice(1);
+            pathArr = pathname.split('/');
+            account = pathArr[0];
+            getSaleProjectInfo(account);
+        }
+    })
+}
+
+const getSaleData = (projectId) => {
+    getNftProjectInfo({
+        params: {
+            nftProjectId: projectId
+        }
+    }).then(res => {
+        let { data } = res;
+        if (data !== null) {
+            // setData
+            saleData.value = data;
+            isLoading.value = false;
+        }
+    })
+}
+
+const getSaleProjectInfo = (account) => {
+    getTwitterSaleNftProjectInfo({
+        params: {
+            twitterAccount: account
+        }
+    }).then(res => {
+        let { data } = res;
+        if (data !== null) {
+            // setData
+            saleData.value = data;
+            isLoading.value = false;
+            // postMessage
+            chrome.tabs.getCurrent((tab) => {
+                chrome.tabs.sendMessage(tab.id, { actionType: "IFRAME_NFT_SHOW_SALE" });
+            })
+            getChromeStorage('nft_guide', (info) => {
+                if (!info) {
+                    isShowGuide.value = true
+                    setTimeout(() => {
+                        setChromeStorage({ nft_guide: Date.now() })
+                    }, 2000)
+                }
+            })
+        }
+    })
+}
+
+const share = () => {
+    chrome.tabs.getCurrent((tab) => {
+        let tagUrl = new URL(tab.url);
+        let tagPathName = tagUrl.pathname.slice(1);
+        let tagSearch = ``;
+        if (tagPathName) {
+            let tagArr = tagPathName.split('/');
+            tagSearch = `${btoa(tagArr[0])}`
+        }
+        let url = pageUrl + `/nft/${saleData.value.nftProjectId}/${tagSearch}`
+        let content = `#DNFT\r\r${url}`
+        chrome.tabs.getCurrent((tab) => {
+            chrome.tabs.sendMessage(tab.id, { actionType: "IFRAME_TWITTER_PUBLISH", publishRes: { srcContent: content }  });
+        });
+    });
+}
+
+const buy = () => {
+    getChromeStorage('userInfo', (_userInfo) => {
+        if (!_userInfo) {
+            chrome.runtime.sendMessage(
+                { actionType: "POPUP_LOGIN", data: "" },
+                (response) => {
+                    console.log("res", response);
+                }
+            )
+        } else {
+            chrome.tabs.getCurrent((tab) => {
+                chrome.tabs.sendMessage(tab.id, {
+                    actionType: "IFRAME_TWITTER_SHOW_BUY_NFT",
+                    data: {
+                        nft_project_Id: saleData.value.nftProjectId
+                    }
+                }, (res) => { });
+            })
+        }
+    })
+}
+
+onBeforeMount(() => {
+    let urlParams = new URL(window.location.href);
+    let searchParmas = new URLSearchParams(urlParams.search);
+    let projectId = searchParmas.get('projectId') || '';
+    if (projectId) {
+        isShare.value = true;
+        getSaleData(projectId)
+    } else {
+        getSaleInfo()
+    }
+})
+</script>
+
+<style lang='scss'>
+body {
+    margin: 0;
+    padding: 0;
+}
+
+.nft {
+    width: 100%;
+    height: 290px;
+    user-select:none;
+    border-radius:20px;
+    background:#F7F9F9;
+    &.border {
+        box-sizing: border-box;
+        border: solid 1px #DCDCDC;
+    }
+    .title {
+        height: 46px;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+
+        .tag {
+            display: flex;
+            align-items: center;
+            padding-left: 15px;
+
+            .logo {
+                overflow: hidden;
+                width: 20px;
+                height: 20px;
+                border-radius: 50%;
+                background-color: #eee;
+            }
+
+            .text {
+                font-size: 18px;
+                font-weight: bold;
+                margin: 0 7px;
+            }
+
+            .tagImg {
+                width: 37px;
+                height: 22px;
+            }
+        }
+
+        .share {
+            cursor: pointer;
+            padding-right: 15px;
+
+            img {
+                width: 19px;
+                height: 18px;
+            }
+        }
+    }
+
+    .content {
+        height: 190px;
+
+        img {
+            width: 100%;
+            height: 100%;
+        }
+    }
+
+    .buy {
+        position: relative;
+        height: 54px;
+        display: flex;
+        justify-content: center;
+        .guide {
+            position: absolute;
+            top: 6px;
+            right: 30%;
+            width: 26px;
+            animation: fade 1s infinite;
+        }
+        button {
+            width: 100%;
+            height: 34px;
+            margin: 0 20px;
+            cursor: pointer;
+            color: #ffffff;
+            font-size: 15px;
+            font-weight: bold;
+            background: #000;
+            border: 0;
+            border-radius: 44px;
+        }
+    }
+}
+
+@keyframes fade {
+    0%, 100% {
+        opacity: .5;
+        transform: scale(1);
+    }
+    50% {
+        opacity: 1;
+        transform: scale(1.4);
+    }
+}
+</style>

+ 30 - 0
src/view/iframe/popup/index.vue

@@ -0,0 +1,30 @@
+<template>
+    <div class="popup_page_main">
+        <popup></popup>
+    </div>
+</template>
+
+
+<script setup>
+import { onMounted, ref } from "vue";
+import popup from '@/view/popup/index.vue'
+
+onMounted(() => {
+});
+
+</script>
+
+<style lang='scss'>
+#app {
+    height: 100%;
+}
+
+.popup_page_main {
+    height: 100%;
+    overflow: hidden;
+}
+
+.popup_content{
+    height: 650px !important;
+}
+</style>

+ 1 - 1
src/view/iframe/publish/components/follow-input.vue

@@ -310,7 +310,7 @@ const onUserMouseLeave = (params, index) => {
                 max-height: 430px;
                 position: absolute;
                 box-shadow: 0px 4px 20px 0px #0000004D;
-                overflow-y: scroll;
+                overflow-y: auto;
                 background-color: #fff; 
                 top: 30px;
                 left: -150px;

+ 117 - 29
src/view/iframe/publish/give-dialog.vue

@@ -388,7 +388,7 @@
                                                 class="refresh"
                                                 :class="{ 'icon-refresh-rotate': refreshRotate }"
                                                 @click="updateCurrencyBanlce"
-                                                :src=" require('@/assets/svg/icon-form-refresh.svg') "
+                                                :src=" require('@/assets/svg/icon-form-refresh-blue.svg') "
                                             />
                                         </div>
                                     </template>
@@ -446,7 +446,7 @@
 <script setup>
 import { ref, watch, reactive, defineProps, defineEmits, onMounted, nextTick, provide } from "vue";
 import { postPublish, verifyPaypalResult, syncChainTokenRechargeRecord, getCurrencyInfoByCode } from "@/http/publishApi";
-import { getInviteGuildInfo, saveInviteGuildInfo } from "@/http/discordApi";
+import { getInviteGuildInfo, getInviteGuildInfoByOpenApi, saveInviteGuildInfo } from "@/http/discordApi";
 import { payCalcFee, getPayConfig } from "@/http/pay";
 import { getFrontConfig } from "@/http/account";
 import {setChromeStorage, getChromeStorage} from "@/uilts/chromeExtension"
@@ -668,7 +668,7 @@ watch(
             // 更新余额
             clearInterval(timer.value);
             timer.value = setInterval(() => {
-                getCurrencyInfo();
+                getCurrencyInfo({loop: true});
             }, 10000)
         } else {
             clearInterval(timer.value);
@@ -744,6 +744,15 @@ const getPayAmount = async (amountValue) => {
 
 const saveDiscordGuildInfo = () => {
     let {guildId, inviteCode, inviteUrl} = discordInviteInfo.value;
+    //保存服务器信息 
+    Report.reportLog({
+        pageSource: Report.pageSource.publisherDialog,
+        businessType: Report.businessType.buttonClick,
+        objectType: Report.objectType.saveDiscordGuildData
+    }, {
+        reportData: discordInviteInfo.value
+    });
+
     if(guildId && inviteCode && inviteUrl) {
         saveInviteGuildInfo({
             params: {
@@ -776,13 +785,14 @@ const confirm = () => {
  * 货币列表-选中货币
  */
 const selectCurrency = (params) => {
-    tempCurrentCurrencyList.value = params;
+    let { currencies } = params;
+    tempCurrentCurrencyList.value = currencies;
     depositGuide.value = false;
-    if (params.length > 1) {
+    if (currencies.length > 1) {
         showCurrencyPop.value = false;
         showCurrencySelect.value = true;
     } else {
-        selectCurrencyAfter(params[0])
+        selectCurrencyAfter(currencies[0])
     }
 };
 
@@ -1402,27 +1412,72 @@ const getDiscordInviteInfo = ({inviteUrl, getDuildId}, cb) => {
     getInviteGuildInfo({
         inviteCode
     }).then(res => {
-        if(!res) {
-            res = {};
+        let resData = inviteGuildResHandler(res, {inviteCode, inviteUrl});
+        if(resData.data.guildId) {
+            cb && cb(resData);
+        } else {
+            //DISCORD 接口未获取到服务器信息 resdata
+            Report.reportLog({
+                pageSource: Report.pageSource.publisherDialog,
+                businessType: Report.businessType.buttonClick,
+                objectType: Report.objectType.getDiscordGuildNoData
+            }, {
+                resData: resData
+            });
+            getGuildInfoByOpenApi({inviteCode}, cb);
         }
-        let {name, icon, id} = res.guild || {};
+    }).catch((err) => {
+        //DISCORD 接口 catch 
+        Report.reportLog({
+            pageSource: Report.pageSource.publisherDialog,
+            businessType: Report.businessType.buttonClick,
+            objectType: Report.objectType.getDiscordGuildCatch
+        }, {
+            err: err
+        });
 
-        icon = icon && id ? `https://cdn.discordapp.com/icons/${id}/${icon}.png` : '';
-        let resData = {
-            inviteCode,
-            data: {
-                code: res.code,
-                guildId: id,
-                inviteUrl,
-                inviteCode,
-                expires: res.expires_at,
-                name,
-                icon,
-            }
+        let errMsg = err;
+        if(typeof errMsg == 'object') {
+            errMsg = JSON.stringify(err);
+        }
+        if(errMsg.indexOf('code 404') > -1) {
+            discordInviteInfo.value = {};
+            iptErrMsgTxt.value = discordIptErrTxt;
+        } else {
+            getGuildInfoByOpenApi({inviteCode}, cb);
+
+        }
+    });
+}
+
+const getGuildInfoByOpenApi = (params, cb) => {
+    let { inviteCode } = params;
+
+    getInviteGuildInfoByOpenApi({
+        inviteCode
+    }).then(res => {
+        let resData = inviteGuildResHandler(res, {inviteCode, inviteUrl});
+        if(resData.data.guildId) {
+            cb && cb(resData);
+        } else {
+            //OPENAPI 接口未获取到服务器信息 resdata
+            Report.reportLog({
+                pageSource: Report.pageSource.publisherDialog,
+                businessType: Report.businessType.buttonClick,
+                objectType: Report.objectType.getDiscordGuildOpenApiNoData
+            }, {
+                resData: resData
+            });
         }
-        discordInviteInfo.value = resData.data;
-        cb && cb(resData);
     }).catch((err) => {
+        // OPENAPI 接口 catch 限频
+        Report.reportLog({
+            pageSource: Report.pageSource.publisherDialog,
+            businessType: Report.businessType.buttonClick,
+            objectType: Report.objectType.getDiscordGuildOpenApiCatch
+        }, {
+            err: err
+        });
         if(iptErrMsgTxt.value && iptErrType == 'discord') {
             iptErrMsgTxt.value = '';
             iptErrType = '';
@@ -1430,6 +1485,35 @@ const getDiscordInviteInfo = ({inviteUrl, getDuildId}, cb) => {
     });
 }
 
+const inviteGuildResHandler = (res, params) => {
+    let {inviteCode, inviteUrl} = params;
+    if(!res) {
+        res = {};
+    }
+    let {name, icon, id} = res.guild || {};
+
+    icon = icon && id ? `https://cdn.discordapp.com/icons/${id}/${icon}.png` : '';
+    let resData = {
+        inviteCode,
+        data: {
+            code: res.code,
+            guildId: id,
+            inviteUrl,
+            inviteCode,
+            expires: res.expires_at,
+            name,
+            icon,
+        }
+    }
+    if(res.code == inviteCode) {
+         discordInviteInfo.value = resData.data;
+    } else {
+        discordInviteInfo.value = {};
+        iptErrMsgTxt.value = discordIptErrTxt;
+    }
+    return resData;
+};
+
 /**
  * 获取支付配置(paypalClientId)
  */
@@ -1471,7 +1555,8 @@ const getLocalCurrencyInfoByCode = () => {
     }
 }
 
-const getCurrencyInfo = async () => {
+const getCurrencyInfo = async (_params) => {
+    let { loop = false} = _params || {};
     let {accessToken = ''} = await getChromeStorage('userInfo') || {};
     if (accessToken) {
         getChromeStorage('selectCurrencyInfo', (res) => {
@@ -1484,7 +1569,9 @@ const getCurrencyInfo = async () => {
                     if(res.code == 0 && res.data) {
                         currentCurrencyInfo.value = res.data;
                         tempCurrentCurrencyInfo.value = res.data;
-                        onIptSetErrorTxt();
+                        if(!loop) {
+                            onIptSetErrorTxt();
+                        }
                     }
                 });
             }
@@ -1658,7 +1745,7 @@ onMounted(() => {
                 box-shadow: 0px 4px 30px rgba(0, 0, 0, 0.3);
                 background-color: #fff;
                 border-radius: 20px;
-                overflow-y: scroll;
+                overflow-y: auto;
             }
             
             .currency-pop-select {
@@ -1712,7 +1799,7 @@ onMounted(() => {
                 .form-wrapper {
                     padding: 0px 18px 18px 18px;
                     height: calc(100% - 80px);
-                    overflow-y: scroll;
+                    overflow-y: auto;
                     overflow-x: hidden;
                     box-sizing: border-box;
 
@@ -2138,6 +2225,7 @@ onMounted(() => {
         overflow: hidden;
         display: flex;
         height: 80px;
+        align-items: center;
         padding: 20px;
         border-radius: 20px;
         border: 1px solid #E6E6E6;
@@ -2160,8 +2248,8 @@ onMounted(() => {
         }
         .refresh {
             cursor: pointer;
-            width: 50px;
-            height: 50px;
+            width: 30px;
+            height: 30px;
             margin-top: -5px;
         }
     }

+ 59 - 5
src/view/iframe/red-packet/red-packet.vue

@@ -395,6 +395,7 @@ let facebookAppConfig = {
 
 let state = reactive({
   status: '',
+  userId: '',
   loading_show: false,
   loading_redbag: true,
   detail: {},
@@ -480,6 +481,10 @@ async function clickLikeBtn() {
     objectType: Report.objectType.like,
     pageSource: Report.pageSource.task_page,
     businessType: Report.businessType.buttonClick
+  }, {
+    postId: state.postId,
+    srcContentId: state.tweetId,
+    senderId: state.userId,
   });
 }
 function clickDone() {
@@ -489,6 +494,10 @@ function clickDone() {
     objectType: Report.objectType.wallet_button,
     pageSource: Report.pageSource.received_success_page,
     businessType: Report.businessType.buttonClick
+  }, {
+    postId: state.postId,
+    srcContentId: state.tweetId,
+    senderId: state.userId,
   });
 }
 function handleScroll(e) {
@@ -586,6 +595,10 @@ async function clickRetweetBtn() {
     objectType: Report.objectType.retweet,
     pageSource: Report.pageSource.task_page,
     businessType: Report.businessType.buttonClick
+  }, {
+    postId: state.postId,
+    srcContentId: state.tweetId,
+    senderId: state.userId,
   });
 }
 
@@ -817,7 +830,11 @@ async function clickFollowAll(item, is_all) {
   if (is_all) {
     _log_obj.objectType = Report.objectType.follow_button
   }
-  Report.reportLog(_log_obj);
+  Report.reportLog(_log_obj, {
+    postId: state.postId,
+    srcContentId: state.tweetId,
+    senderId: state.userId,
+  });
 }
 
 
@@ -853,6 +870,10 @@ const showCloseEndTimePageReport = () => {
   Report.reportLog({
     pageSource: Report.pageSource.expired_page,
     businessType: Report.businessType.pageView,
+  }, {
+    postId: state.postId,
+    srcContentId: state.tweetId,
+    senderId: state.userId,
   });
 }
 
@@ -862,6 +883,10 @@ const showSuccessPage = () => {
   Report.reportLog({
     pageSource: Report.pageSource.received_success_page,
     businessType: Report.businessType.pageView,
+  }, {
+    postId: state.postId,
+    srcContentId: state.tweetId,
+    senderId: state.userId,
   });
 }
 const showNotOpenPage = () => {
@@ -869,6 +894,10 @@ const showNotOpenPage = () => {
   Report.reportLog({
     pageSource: Report.pageSource.pending_page,
     businessType: Report.businessType.pageView
+  }, {
+    postId: state.postId,
+    srcContentId: state.tweetId,
+    senderId: state.userId,
   });
 }
 const showOpenedPage = () => {
@@ -881,6 +910,10 @@ const showOpenedPageReport = () => {
   Report.reportLog({
     pageSource: Report.pageSource.task_page,
     businessType: Report.businessType.pageView,
+  }, {
+    postId: state.postId,
+    srcContentId: state.tweetId,
+    senderId: state.userId,
   });
 }
 
@@ -894,6 +927,10 @@ const showRabbitPageReport = () => {
   Report.reportLog({
     pageSource: Report.pageSource.received_empty_rewards_page,
     businessType: Report.businessType.pageView,
+  }, {
+    postId: state.postId,
+    srcContentId: state.tweetId,
+    senderId: state.userId,
   });
 }
 
@@ -1035,6 +1072,7 @@ function init(initParams) {
       state.detail = JSON.parse(res.data.postBizData)
       state.detail.taskCondition = state.detail.taskCondition || []
       state.tweetId = state.srcContentId;
+      state.userId = res.data.srcUserId;
       state.tweet_author = state.detail.postUserInfo && state.detail.postUserInfo.nickName || '';
       // 不要删除这个console
       console.log('postBizData',state.detail)
@@ -1232,6 +1270,10 @@ function handleRedPacket() {
     pageSource: Report.pageSource.pending_page,
     businessType: Report.businessType.buttonClick,
     objectType: Report.objectType.open_button
+  }, {
+    postId: state.postId,
+    srcContentId: state.tweetId,
+    senderId: state.userId,
   });
 }
 
@@ -1297,7 +1339,10 @@ function handleFinishRedPacket() {
           businessType: Report.businessType.buttonClick,
           objectType: Report.objectType.get_giveaway
         }, {
-          get_giveaway_result: Report.extParams.success
+          get_giveaway_result: Report.extParams.success,
+          postId: state.postId,
+          srcContentId: state.tweetId,
+          senderId: state.userId,
         });
       } else {
         let _data = res.data.conditionResult
@@ -1373,7 +1418,10 @@ function handleFinishRedPacket() {
           businessType: Report.businessType.buttonClick,
           objectType: Report.objectType.get_giveaway
         }, {
-          get_giveaway_result: Report.extParams.failure
+          get_giveaway_result: Report.extParams.failure,
+          postId: state.postId,
+          srcContentId: state.tweetId,
+          senderId: state.userId,
         });
         if (discordAuthorizeRequired) {
           discordAuth('reAuth');
@@ -1386,7 +1434,10 @@ function handleFinishRedPacket() {
         businessType: Report.businessType.buttonClick,
         objectType: Report.objectType.get_giveaway
       }, {
-        get_giveaway_result: Report.extParams.failure
+        get_giveaway_result: Report.extParams.failure,
+        postId: state.postId,
+        srcContentId: state.tweetId,
+        senderId: state.userId,
       });
       handleErrorCode(res)
     }
@@ -1465,6 +1516,10 @@ function handleErrorCode(res) {
       Report.reportLog({
         pageSource: Report.pageSource.robot_detection_failed_page,
         businessType: Report.businessType.pageView,
+      }, {
+        postId: state.postId,
+        srcContentId: state.tweetId,
+        senderId: state.userId,
       });
       break
     // 无法校验用户Twitter信息
@@ -1565,7 +1620,6 @@ function onWindowMessage() {
 function onPageVisbile() {
   document.addEventListener('visibilitychange', function () {
     let isHidden = document.hidden;
-    console.log('joinDiscordActionState', joinDiscordActionState);
     if (!isHidden) {
       checkJoinDiscord();
     }

+ 34 - 4
src/view/popup/components/head.vue

@@ -12,9 +12,13 @@
         <template v-else>
             <img :src="require('@/assets/svg/icon-back.svg')" alt="" class="back" @click="clickBack">
             <div class="title">{{ props.title }}</div>
+            <img :src="require('@/assets/svg/icon-back-head-list.svg')"
+                v-if="show_list" 
+                class="list" 
+                @click="clickList" />
             <img :src="require('@/assets/svg/icon-refresh.svg')" alt="" class="refresh" v-if="show_refresh"
                 @click="clickRefresh" :class="{ transform_rotate: state.rotate }">
-            <img :src="require('@/assets/svg/icon-withdraw-help.svg')" alt="" class="help" v-if="props.show_help"
+            <img :src="require('@/assets/svg/icon-head-help.svg')" alt="" class="help" v-if="props.show_help"
                 @click="clickHelp">
             <img :src="require('@/assets/svg/icon-more-l.svg')" alt="" class="more" v-if="props.show_more"
                 @click="state.show_option = true">
@@ -43,7 +47,9 @@ let props = defineProps({
     show_more: Boolean,
     show_help: Boolean,
     back_url: String,
-    user_info: Object
+    user_info: Object,
+    show_list: Boolean,
+    transactionsRouterParams: Object,
 })
 
 let state = reactive({
@@ -74,13 +80,30 @@ function clickRefresh() {
 }
 
 function clickItem(path) {
-    router.push(path)
+    let params = props.transactionsRouterParams || {};
+    router.push({
+        path: path,
+        query: {
+            params: JSON.stringify(params)
+        }
+    })
 }
 
 function clickHelp() {
     window.open(`https://aboard-cattle-610.notion.site/How-to-withdraw-assets-from-DeNet-to-MetaMask-01c679bb9ff441429e31e8f7c1f67411`)
 }
 
+function clickList() {
+    let params = props.transactionsRouterParams || {};
+    console.log('transactionsRouterParams',params);
+    router.push({
+        path: '/transactions',
+        query: {
+            params: JSON.stringify(params)
+        }
+    })
+}
+
 </script>
 <style lang="scss" scoped>
 .border {
@@ -94,6 +117,7 @@ function clickHelp() {
     justify-content: space-between;
     align-items: center;
     padding: 0 12px;
+    overflow: hidden;
 
     &.home {
         height: 64px;
@@ -185,11 +209,16 @@ function clickHelp() {
         height: 24px;
     }
 
+    .list {
+        margin-right: 12px;
+    }
+
     .refresh {
-        margin-right: 20px;
+        
     }
 
     .help {
+        margin-left: 20px;
         margin-right: 12px;
     }
 
@@ -199,6 +228,7 @@ function clickHelp() {
         color: #000000;
         font-size: 16px;
         font-weight: 500;
+        word-break: break-all;
     }
 }
 </style>

+ 231 - 0
src/view/popup/components/tabbar.vue

@@ -0,0 +1,231 @@
+<template>
+    <div class="tab-bar-wrappeer">
+        <template
+            v-for="(item, index) in tabbarData"
+            :key="index">
+            <div class="tab-item" 
+                 v-if="item.path != '/NFT' || item.path == '/NFT' && showNFTTab"
+                 @click="tabbarHandler(item, index)">
+                    <red-dot class="red-dots"
+                        v-if="unReadCountTask > 0 && item.path == '/message' && currentTab.path != '/message'"
+                        ></red-dot>
+                    <img
+                        :src="
+                            index == currentTab.index
+                                ? item.iconActive
+                                : item.iconInActive
+                        "
+                    />
+                    <div
+                        class="text"
+                        :class="{ 'active-tab': index == currentTab.index }"
+                    >
+                        {{ item.name }}
+                    </div>
+            </div>
+        </template>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, defineProps, defineEmits, nextTick, watch, onBeforeUnmount } from "vue";
+
+import redDot from "@/view/components/red-dot.vue";
+import router from "@/router/popup.js";
+import { setBadgeInfo, hideBadge } from "@/logic/background/twitter";
+
+
+import { nftListMine } from "@/http/nft.js";
+import { getAllMessageInfo } from "@/http/messageApi"
+
+
+const props = defineProps({
+    userInfo: {
+        type: Object,
+        default: () => {
+            return {
+                accessToken: undefined,
+            };
+        }
+    },
+});
+
+watch(
+    () => props.userInfo,
+    (newVal) => {
+        getNFTListMine();
+        setMessageCount();
+    },
+    {
+        deep: true
+    }
+);
+
+
+let currentTab = ref({
+    index: 0,
+    path: '/'
+});
+
+let unReadCountTask = ref(0);
+let showNFTTab = ref(true);
+
+let tabbarData = ref([
+    {
+        name: "Wallet",
+        path: "/",
+        iconActive: require("@/assets/svg/icon-tab-wallet-active.svg"),
+        iconInActive: require("@/assets/svg/icon-tab-wallet.svg"),
+    },
+    {
+        name: "NFTs",
+        path: "/NFT",
+        iconActive: require("@/assets/svg/icon-tab-NFT-active.svg"),
+        iconInActive: require("@/assets/svg/icon-tab-NFT.svg"),
+    },
+    {
+        name: "Message",
+        path: "/message",
+        iconActive: require("@/assets/svg/icon-tab-message-active.svg"),
+        iconInActive: require("@/assets/svg/icon-tab-message.svg"),
+    },
+    {
+        name: "More",
+        path: "/more",
+        iconActive: require("@/assets/svg/icon-tab-more-active.svg"),
+        iconInActive: require("@/assets/svg/icon-tab-more.svg"),
+    },
+]);
+
+let NFTReqParams = {
+    params: {
+        pageNum: 1,
+        pageSize: 20,
+    },
+};
+
+const emits = defineEmits(["tabbarClick"]);
+
+const tabbarHandler = (params, index) => {
+    unReadCountTask.value = 0;
+    setMessageCount();
+    currentTab.value.index = index;
+    currentTab.value.path = params.path;
+    router.push(params.path);
+    emits("tabbarClick", params);
+};
+
+const setActiveTab = () => {
+    nextTick(() => {
+        let path = router.currentRoute.value.path;
+        let list = tabbarData.value;
+        for (let i = 0; i < list.length; i++) {
+            if (path == list[i].path) {
+                currentTab.value.index = i;
+                currentTab.value.path = path;
+                break;
+            }
+        }
+    })
+};
+
+const getNFTListMine = () => {
+    if(!props.userInfo.accessToken) return;
+    nftListMine({
+        params: NFTReqParams.params,
+    }).then((res) => {
+        if (res.data && res.data.length) {
+            showNFTTab.value = true;
+        } else {
+            showNFTTab.value = false;
+        }
+    });
+};
+
+
+const setMessageCount = () => {
+    if(!props.userInfo.accessToken) return;
+    getAllMessageInfo({params: {
+    }}).then(res => {
+        if(res.code == 0) {
+            let {unReadCountTotal = 0, unReadCountWalletDetail = 0, unReadCountTaskLuckdrop = 0} = res.data;
+            unReadCountTask.value = unReadCountTaskLuckdrop;
+            if(unReadCountTotal > 0) {
+                let text = unReadCountTotal > 99 ? '99+' : unReadCountTotal+'';
+                setBadgeInfo({data: {text}});
+            } else {
+                hideBadge();
+            }
+        }
+    });
+}
+
+
+const onMessage = () => {
+    chrome.runtime.onMessage.addListener(msgListener)
+}
+
+const msgListener = (req, sender, sendResponse) => {
+    sendResponse('');
+    switch (req.actionType) {
+        case 'CONTENT_POPUP_PAGE_SHOW':
+            init();
+            break;
+    }
+}
+
+const init = () => {
+    setActiveTab();
+    setMessageCount();
+}
+
+onMounted(() => {
+    onMessage();
+    init();
+});
+
+onBeforeUnmount(() => {
+    chrome.runtime.onMessage.removeListener(msgListener);
+})
+</script>
+
+<style scoped lang="scss">
+.tab-bar-wrappeer {
+    background: #ffffff;
+    box-shadow: inset 0px 1px 0px #ececec;
+    width: 100%;
+    height: 70px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    position: absolute;
+    z-index: 1000;
+    bottom: 0px;
+
+    cursor: pointer;
+
+        .tab-item {
+            flex: 1;
+            text-align: center;
+            position: relative;
+
+            .text {
+                font-size: 12px;
+                color: #c0c0c0;
+            }
+
+            .active-tab {
+                color: #1d9bf0 !important;
+                font-weight: 500;
+            }
+
+            .red-dots {
+                position: absolute;
+                right: 32%;
+                top: 0px;
+            }
+        }
+    
+}
+</style>

+ 68 - 0
src/view/popup/components/top-bar.vue

@@ -0,0 +1,68 @@
+<template>
+    <div class="top-bar-wrapper" :style="{background: bgColor, boxShadow: boxShadow}">
+        <div class="left" @click="clickUserInfo">
+            <img :src="userInfo.avatarUrl" class="icon-avatar">
+            <span class="nick-name" :style="{color: color}">{{userInfo.nickName}}</span>
+        </div>
+        <!-- <div>
+            <img :src="require('@/assets/svg/icon-denet-logo.svg')" />
+        </div> -->
+    </div>
+</template>
+
+<script setup>
+import {defineProps} from 'vue';
+
+const props = defineProps({
+    userInfo: {
+        type: Object,
+        default: () => {
+            return {};
+        }
+    },
+    bgColor: {
+        type: String,
+        default: '#fff'
+    },
+    boxShadow: {
+        type: String,
+        default: 'none'
+    },
+    color: {
+        type: String,
+        default: '#fff'
+    }
+})
+
+function clickUserInfo() {
+    window.open(`https://twitter.com/${props.userInfo.nickName}`);
+}
+</script>
+
+<style scoped lang='scss'>
+.top-bar-wrapper {
+    width: 100%;
+    height: 48px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 12px 16px;
+    box-sizing: border-box;
+
+    .left {
+        display: flex;
+        align-items: center;
+        cursor: pointer;
+        .icon-avatar {
+            width: 24px;
+            height: 24px;
+            margin-right: 8px;
+            border-radius: 50%;
+        }
+        .nick-name {
+            font-weight: 500;
+            font-size: 14px;
+        }
+    }
+}
+</style>

+ 302 - 0
src/view/popup/currency-detail.vue

@@ -0,0 +1,302 @@
+<template>
+  <div class="currency-detail-page">
+    <v-head :title="currencyInfo.tokenSymbol" 
+            :show_more="false"
+            :show_refresh="true"
+            :show_list="true"
+            :transactionsRouterParams="{
+              backUrl: 'back'
+            }"
+            @on-refresh="onRefresh" />
+    <div class="top">
+      <img
+        class="icon-currency"
+        :src="currencyInfo.iconPath"/>
+      <div class="amount">
+        <div class="balance"
+             :class="{'direction-column': (currencyInfo.totalBalance.length + currencyInfo.tokenSymbol.length) > 15}">
+          <a-tooltip :title="currencyInfo.totalBalance">
+              {{ getBit(currencyInfo.totalBalance) }}
+          </a-tooltip> 
+          <template v-if="currencyInfo.totalBalance.length + currencyInfo.tokenSymbol.length < 16">
+            &nbsp;&nbsp;
+          </template>
+          <span class="symbol">
+             {{currencyInfo.tokenSymbol}}
+          </span>
+        </div>
+        <div class="final">
+          <a-tooltip :title="'$'+currencyInfo.totalUsdEstimateBalance">
+              ${{ getBit(currencyInfo.totalUsdEstimateBalance) }}
+          </a-tooltip>
+        </div>
+      </div>
+    </div>
+    <div class="bottom">
+      <div class="btn deposit-btn"
+          v-if="currencyInfo.currencyCode != 'USD'"
+          @click="clickDeposit">Deposit</div>
+      <div class="btn withdrawal-btn" @click="clickWithdraw">Withdrawal</div>
+    </div>
+
+    <template v-if="showCurrencySelect">
+        <div class="selectDiv">
+            <currency-select 
+                ref="currencySelectDom"
+                :list="currenciesData"
+                @selectCurrency="selectCurrency">
+            </currency-select>
+        </div>
+        <div class="selectBg" @click="showCurrencySelect = false"></div>
+    </template>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, inject, reactive } from "vue";
+import router from "@/router/popup.js";
+import Report from "@/log-center/log";
+import { getStorage } from "@/uilts/help";
+
+import { getCurrencyInfoBySymbol } from "@/http/publishApi";
+
+import VHead from '@/view/popup/components/head.vue'
+import currencySelect from "@/view/components/currency-select.vue";
+import { getBit } from "@/uilts/help";
+
+let currenciesData = ref([]);
+let currencyInfo = ref({
+  totalBalance: '',
+  tokenSymbol: ''
+});
+let showCurrencySelect = ref(false);
+
+let currencyOpertionType = '';
+
+
+
+const selectCurrency = (params) => {
+    showCurrencySelect.value = false;
+    if(currencyOpertionType == 'WITHDRAW') {
+      withdrawHandle(params);
+    } else if(currencyOpertionType == 'DEPOSIT') {
+      depositHandle(params);
+    }
+}
+
+let withdraw_info = inject('withdraw_info')
+// 点击提现
+const clickWithdraw = () => {
+    Report.reportLog({
+        pageSource: Report.pageSource.denetHomePage,
+        businessType: Report.businessType.buttonClick,
+        objectType: Report.objectType.withdrawButton
+    });
+
+    if(currenciesData.value.length > 1) {
+      showCurrencySelect.value = true;
+      currencyOpertionType = "WITHDRAW";
+    } else if(currenciesData.value.length == 1){
+      withdrawHandle(currenciesData.value[0]);
+    }
+}
+
+const withdrawHandle = (_params) => {
+  withdraw_info.chainInfo = _params.chainInfo;
+  if (_params.currencyCode == 'USD') {
+      withdraw_info.currency_code = _params.currencyCode
+      router.push('/withdraw/paypal')
+  } else {
+      withdraw_info.source = 'home'
+      withdraw_info.balance = _params.balance
+      withdraw_info.token_symbol = _params.tokenSymbol || ''
+      withdraw_info.currency_name = _params.currencyName || ''
+      withdraw_info.token_chain = _params.tokenChain || 'BNB Chain'
+      withdraw_info.currency_code = _params.currencyCode
+      withdraw_info.icon_token = _params.iconPath || ''
+      withdraw_info.icon_net = require('@/assets/svg/icon-BNB.svg')
+      console.log(withdraw_info.chainInfo.iconPath)
+      router.push('/withdraw/info')
+  }
+}
+
+
+
+let top_up_info = inject('top_up_info');
+
+const clickDeposit = () => {
+    Report.reportLog({
+        pageSource: Report.pageSource.denetHomePage,
+        businessType: Report.businessType.buttonClick,
+        objectType: Report.objectType.topupButton
+    });
+
+    if(currenciesData.value.length > 1) {
+      showCurrencySelect.value = true;
+      currencyOpertionType = "DEPOSIT";
+    } else if(currenciesData.value.length == 1){
+      depositHandle(currenciesData.value[0]);
+    }
+}
+
+const depositHandle = (_params) => {
+  top_up_info.token = _params.currencyName || ''
+  top_up_info.token_chain = _params.tokenChain 
+  top_up_info.token_symbol = _params.tokenSymbol || ''
+  top_up_info.currency_code = _params.currencyCode
+  top_up_info.icon_token = _params.iconPath || ''
+  top_up_info.icon_net = require('@/assets/svg/icon-BNB.svg')
+  top_up_info.chainInfo = {
+    ..._params.chainInfo
+  };
+  
+  router.push('/top-up/info');
+};
+
+const onRefresh = () => {
+  getCurrencyInfoBySymbol({
+    params: {
+      symbol: currencyInfo.value.tokenSymbol
+    }
+  }).then(res => {
+    if(res.code == 0) {
+      if(res.data && res.data.currencyCategories && res.data.currencyCategories.length) {
+        let data = res.data.currencyCategories[0].data;
+        if(data.length) {
+          let {totalBalance = '', totalUsdEstimateBalance = ''} = data[0] || {};
+          currencyInfo.value.totalBalance = totalBalance;
+          currencyInfo.value.totalUsdEstimateBalance = totalUsdEstimateBalance;
+        }
+      }
+    }
+  })
+};
+
+onMounted(() => {
+    let {params = '{}'} = router.currentRoute.value.query;
+
+    let {currencies = [], totalBalance = 0, totalUsdEstimateBalance = 0} =  JSON.parse(params);
+
+    currenciesData.value = currencies;
+
+    if(currencies.length) {
+      currencyInfo.value = {
+        ...currencies[0],
+        totalBalance,
+        totalUsdEstimateBalance
+      };
+      console.log(currencyInfo.value )
+    }
+})
+</script>
+
+
+<style lang='scss' scoped>
+.currency-detail-page {
+  width: 100%;
+  height: 100%;
+  position: relative;
+
+  .top {
+    height: calc(100% - 212px);
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+
+    .icon-currency {
+      width: 100px;
+      margin-bottom: 30px;
+    }
+
+    .amount {
+      font-weight: 700;
+      font-size: 28px;
+      text-align: center;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      flex-direction: column;
+      width: 100%;
+      .balance {
+        padding: 0 18px;
+        box-sizing: border-box;
+        width: 100%;
+        word-break: break-word;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+
+        .symbol{
+          word-break: break-all;
+        }
+      }
+
+      .direction-column {
+        flex-direction: column;
+      }
+
+      .final {
+        margin-top: 9px;
+        font-weight: 500;
+        font-size: 22px;
+        color: #a2a2a2;
+        text-align: center;
+      }
+    }
+  }
+  .bottom {
+    height: 162px;
+    padding: 0 20px;
+    box-sizing: border-box;
+
+    .btn {
+      width: 100%;
+      height: 57px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      font-weight: 500;
+      font-size: 17px;
+      border-radius: 100px;
+      cursor: pointer;
+    }
+
+    .deposit-btn {
+      margin-bottom: 18px;
+      background: #1d9bf0;
+      color: #fff;
+    }
+
+    .withdrawal-btn {
+      background: rgba(244, 244, 244, 0.01);
+      border: 1px solid #d7e8f4;
+      box-sizing: border-box;
+      color: #1d9bf0;
+    }
+  }
+
+
+  .selectDiv {
+      position: absolute;
+      z-index: 1000;
+      width: 100%;
+      max-height: 480px;
+      padding-bottom: 30px;
+      left: 0;
+      bottom: 0;
+      background-color: #fff;
+      border-radius: 20px 20px 0 0;
+      overflow-y: auto;
+  }
+  .selectBg {
+      position: absolute;
+      z-index: 999;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      background: rgba($color: #000000, $alpha: 0.6);
+  }
+}
+</style>

+ 8 - 0
src/view/popup/index.vue

@@ -15,6 +15,14 @@ watch(withdraw_info, (newVal) => {
 
 provide('withdraw_info', withdraw_info)
 
+let top_up_info = reactive(getStorage("top_up_info") || {});
+
+watch(top_up_info, (newVal) => {
+  setStorage("top_up_info", newVal);
+});
+
+provide("top_up_info", top_up_info);
+
 let state = reactive({
   popup: true
 })

+ 0 - 1028
src/view/popup/popup.vue

@@ -1,1028 +0,0 @@
-<template>
-    <global-tip :type="'3'"></global-tip>
-    <div class="page-wrapper" ref="pageWrapperDom" @scroll="pageScroll">
-        <template v-if="isLogin && homeVisibility">
-            <v-head :show_more="true" :show_state="'home'" :user_info="userInfo"></v-head>
-            <div class="content">
-                <div class="balance">
-                    <div class="wallet">
-                        <img :src="require('@/assets/svg/icon-home-wallet.svg')" />
-                        <font>Balance Valuation</font>
-                    </div>
-                    <div class="bill" @click="showTransactions">
-                        <red-dot class="red-dot" v-if="unReadCountWallet > 0"></red-dot>
-                        <img :src="require('@/assets/svg/icon-home-bill.svg')" />
-                    </div>
-                </div>
-                <div class="amount">
-                    <a-tooltip :title="'$'+canWithdrawBalance">
-                        ${{ getBit(canWithdrawBalance) }}
-                    </a-tooltip>
-                </div>
-                <div class="area-btn">
-                    <div class="withdraw-btn" @click="clickWithdraw">Withdraw</div>
-                    <div class="top-up-btn" @click="clickTopUp">Deposit</div>
-                </div>
-            </div>
-
-            <div class="tab-bar">
-                <div class="tab-item" 
-                    :class="{ active: currentTabIndex == index }" 
-                    v-for="(item, index) in tabList"
-                    :key="index" 
-                    @click="clickTab(item, index)">
-                    <img :src="item.icon" class="icon" />
-                    {{ item.label }}
-                </div>
-            </div>
-            <div class="list-wrapper" ref="pageGiveListDom">
-                <div class="give-list" v-if="currentTabIndex == 0">
-                    <template v-if="giveList.length">
-                        <div class="cell" 
-                            :class="{'cell-center': item.type == 1}"
-                            v-for="(item, index) in giveList" 
-                            :key="index"
-                            @click="clickListItem(item, index)">
-
-                            <red-dot class="red-dots"
-                                v-if="item.unReadMsgCount > 0 && isReadMsg"></red-dot>
-
-                            <div class="img-wrapper">
-                                <!-- 收到红包 -->
-                                <template v-if="item.type == 1">
-                                    <img class="icon-avatar" :src="item.userInfo.avatarUrl" />
-                                    <img class="icon-give" :src="
-                                        require('@/assets/svg/icon-giveaways.svg')
-                                    " />
-                                </template>
-                                <!-- 发出去红包 -->
-                                <template v-else-if="2">
-                                    <img 
-                                        class="icon-big-give"
-                                        :src="
-                                        require('@/assets/svg/icon-list-big-give.svg')
-                                    " />
-                                </template>
-                            </div>
-                            <div class="info-wrapper"
-                                :class="{'info-center': item.type == 1}">
-                                <div class="left">
-                                    <div class="nickname">
-                                        {{
-                                                item.type == 1
-                                                    ? item.userInfo.nickName
-                                                    : "Giveaways"
-                                        }}
-                                    </div>
-                                    <div class="time">
-                                        {{
-                                                moment(item.timestamp).format(
-                                                    "MM-DD HH:mm:ss"
-                                                )
-                                        }}
-                                    </div>
-                                </div>
-                                <div class="right">
-                                    <div class="msg">
-                                        <div class="bold" 
-                                            :class="{'align-content': (item.type == 2 || (item.type == 1 && item.status == 1)) && 
-                                                                        item.amount.length + item.currencySymbol.length > 12 }">
-                                            <!-- 收到的 -->
-                                            <template v-if="item.type == 1">
-                                                <!-- 进行中-->
-                                                <template v-if="item.status == 0">
-                                                    in progress
-                                                </template>
-                                                <!-- 已完成 -->
-                                                <template v-else-if="item.status == 1">
-                                                    <span class="blance">
-                                                        <a-tooltip :title="item.amount">
-                                                            {{ getBit(item.amount) }}</a-tooltip>
-                                                    </span>
-                                                    <div class="coin-type-wrapper">
-                                                        <span class="coin-type">{{ item.currencySymbol || '' }}</span>
-                                                        <img :src="item.currencyIconPath" alt="">
-                                                    </div>
-                                                </template>
-                                                <!-- 已过期 -->
-                                                <template v-else-if="item.status == 2">
-                                                    Timeout
-                                                </template>
-                                            </template>
-                                            <!-- 发出去的 -->
-                                            <template v-else-if="item.type == 2">
-                                                <span class="blance">
-                                                    <a-tooltip :title="'-' + item.amount">
-                                                        -{{ getBit(item.amount) }}
-                                                    </a-tooltip>
-                                                </span>
-                                                <div class="coin-type-wrapper">
-                                                    <span class="coin-type">{{ item.currencySymbol || '' }}</span>
-                                                    <img :src="item.currencyIconPath" alt="">   
-                                                </div>
-                                            </template>
-                                        </div>
-                                        <!-- 发出的红包显示 -->
-                                        <div class="desc" v-if="item.type == 2">
-                                            <!-- 未发送-->
-                                            <template v-if="item.postTaskLuckdrop.reSendAvailable">
-                                                Unpublished
-                                            </template>
-                                            <!-- 进行中 -->
-                                            <template v-else-if="item.status == 1">
-                                                {{item.postTaskLuckdrop.receivedCount}}/{{item.postTaskLuckdrop.totalCount}}
-                                            </template>
-                                            <!-- 2:已结束; 3:提前终止-->
-                                            <template v-else-if="item.status == 2 || item.status == 3">
-                                                ({{item.status == 2 ? 'Time expired' : 'Termination'}})
-                                                {{
-                                                        item.postTaskLuckdrop
-                                                            .receivedCount
-                                                }}/{{
-                                                        item.postTaskLuckdrop
-                                                            .totalCount
-                                                }}
-                                            </template>
-                                            <!-- 红包提前终止/退款(进行中)显示-->
-                                            <template v-if="item.status == 4">
-                                                Terminating
-                                            </template>
-
-                                            <!-- 进行中或者未发送成功时显示 
-                                                v-if="item.status == 1 || item.postTaskLuckdrop.reSendAvailable"-->
-                                            <div  class="desc-bottom-bar">
-                                                <!-- 没有终止红包时显示 -->
-                                                <div v-if="item.postTaskLuckdrop.terminatedAvailable"
-                                                    class="btn"
-                                                    @click.stop="terminaHandler(item, index)">
-                                                    Termination
-                                                </div>
-                                                
-                                                <!-- 红包未发出显示 -->
-                                                <div class="btn send-btn"
-                                                    v-if="item.postTaskLuckdrop.reSendAvailable"       
-                                                    @click.stop="sendTwitter(item)">
-                                                    Send
-                                                </div>
-                                                <div v-else-if="item.srcContentId"
-                                                    class="btn detail-btn"
-                                                    @click.stop="clickListItem(item, index)">details</div>
-                                            </div>
-                                        </div>
-                                    </div>
-                                    <!-- 发红包—— 未发出、进行中 隐藏 -->
-                                    <img v-if="item.type != 2" 
-                                        class="icon" 
-                                        :src="require('@/assets/svg/icon-cell-arrow-right.svg')" />
-                                </div>
-                            </div>
-                        </div>
-                    </template>
-                    <template v-else>
-                        <img class="icon-empty" :src="require('@/assets/svg/icon-empty-list.svg')" />
-                    </template>
-                </div>
-                <div class="more-list" v-else>
-                    <div class="cell" 
-                    v-for="(item, index) in moreTabList" 
-                    :key="index"
-                    @click="moreItemHandle(item)">
-                        <img class="icon" :src="item.icon" />
-                        <div class="info-wrapper">
-                            <div class="left">
-                                {{ item.label }}
-                            </div>
-                            <div class="right">
-                                <img class="icon" :src="
-                                    require('@/assets/svg/icon-cell-arrow-right.svg')
-                                " />
-                            </div>
-                        </div>
-                    </div>
-                </div>
-            </div>
-        </template>
-        <!-- login -->
-        <popup-login v-if="!isLogin" @loginAction="loginAction" />
-        <template v-if="isLogin && !homeVisibility">
-            <!-- 交易列表 -->
-            <popup-transactions v-if="transactionsVisibility" @back="transactionsBack" />
-            <!-- 提现页 -->
-            <popup-withdraw :amountValue="canWithdrawBalance" :walletWithdrawConfig="walletWithdrawConfig"
-                v-if="withdrawVisibility" @back="withdrawBack" />
-        </template>
-        <modal :visible="modalVisible"   
-            title="Early termination of Giveaway?"
-            content="The remaining amount will be returned to your wallet within 1 day."
-            cancelText="Termination"
-            confirmText="Cancel"
-            @cancel="modalCancel"
-            @confirm="modalConfirm" />
-    </div>
-</template>
-
-<script setup>
-import { ref, onMounted, inject } from "vue";
-
-import popupLogin from "@/view/popup/components/login.vue";
-import popupTransactions from "@/view/components/popup-transactions";
-import popupWithdraw from "@/view/components/popup-withdraw.vue";
-import redDot from "@/view/components/red-dot.vue";
-import modal from "@/view/popup/components/modal.vue";
-
-import {
-    getChromeStorage,
-} from "@/uilts/chromeExtension";
-import { getBalance, getMineLuckdropRecords } from "@/http/account";
-import { readAllMsgByType, getAllMessageInfo } from "@/http/messageApi"
-import { terminatedLuckdrop } from "@/http/redPacket";
-import { setBadgeInfo, hideBadge } from "@/logic/background/twitter";
-import Report from "@/log-center/log";
-import router from "@/router/popup.js";
-import { getBit } from "@/uilts/help";
-import  GlobalTip  from '@/view/components/global-tip.vue'
-
-import VHead from '@/view/popup/components/head.vue';
-
-
-let withdraw_info = inject('withdraw_info')
-withdraw_info.paypal = {}
-
-var moment = require("moment");
-
-let pageWrapperDom = ref(null);
-let pageGiveListDom = ref(null);
-
-let isLogin = ref(false);
-let homeVisibility = ref(false);
-let transactionsVisibility = ref(false);
-let withdrawVisibility = ref(false);
-let modalVisible = ref(false);
-
-let terminaTask = {};
-
-let userInfo = ref({});
-let canWithdrawBalance = ref(0);
-withdraw_info.paypal.amount_value = canWithdrawBalance
-withdraw_info.balance = 0
-
-let isRequestWithdrawBalance = ref(false);
-
-let currentTabIndex = ref(0);
-let giveList = ref([]);
-
-// 钱包未读数
-let unReadCountWallet = ref(0);
-let isReadMsg = ref(true);
-
-let giveReqParams = {
-    params: {
-        pageNum: 1,
-        pageSize: 10,
-    },
-    loadMore: false,
-};
-
-
-let walletWithdrawConfig = ref({
-    withdrawUSDPaypalFee: 0,
-    withdrawUSDPreMinAmount: 100,
-    withdrawUSDSwitch: "",
-    withdrawUSDPaypalFeeDesc: ''
-});
-withdraw_info.paypal.wallet_withdraw_config = walletWithdrawConfig
-
-
-let moreTabList = ref([
-    {
-        icon: require("@/assets/svg/icon-website.svg"),
-        label: "Website",
-        href: 'https://denet.me'
-    },
-    {
-        icon: require("@/assets/svg/icon-twitter.svg"),
-        label: "Twitter",
-        href: 'https://twitter.com/denet2022'
-    },
-    {
-        icon: require("@/assets/svg/icon-discord.svg"),
-        label: "Discord",
-        href: 'https://discord.gg/wZSz9p8ddG'
-    }
-    // , {
-    //     icon: require("@/assets/svg/icon-telegram.svg"),
-    //     label: "Telegram",
-    //     href: 'https://t.me/denetpro'
-    // }
-]);
-
-let tabList = ref([
-    {
-        icon: require("@/assets/svg/icon-giveaways.svg"),
-        label: "Giveaways",
-    },
-    {
-        icon: require("@/assets/svg/icon-more.svg"),
-        label: "More",
-    },
-]);
-
-onMounted(() => {
-    checkLoginState(() => {
-        if (isLogin.value) {
-            getAccountBalance();
-            getLuckdropRecordsList();
-            chrome.runtime.connect({ name: "popup" });
-            Report.reportLog({
-                pageSource: Report.pageSource.denetHomePage,
-                businessType: Report.businessType.pageView,
-            },{
-                type: window.location.href.indexOf('home.html') > -1 ? 'web' : 'extensions'
-            });
-            setMessageCount();
-            setTimeout(() => {
-                isReadMsg.value = false;
-                readAllMsg({msgType: 1}, () => {
-                    setMessageCount();
-                });
-            }, 2000);
-        } else {
-            Report.reportLog({
-                pageSource: Report.pageSource.denetLogin,
-                businessType: Report.businessType.pageView,
-            });
-        }
-    });
-});
-
-const readAllMsg = ({msgType}, cb) => {
-    readAllMsgByType({
-        params: {
-            msgType
-        }
-    }).then(res => {
-        cb && cb();
-    })
-};
-
-const setMessageCount = () => {
-    getAllMessageInfo({params: {
-    }}).then(res => {
-        if(res.code == 0) {
-            let {unReadCountTotal = 0, unReadCountWalletDetail = 0, unReadCountTaskLuckdrop = 0} = res.data;
-            unReadCountWallet.value = unReadCountWalletDetail;
-            if(unReadCountTotal > 0) {
-                let text = unReadCountTotal > 99 ? '99+' : unReadCountTotal+'';
-                setBadgeInfo({data: {text}});
-            } else {
-                hideBadge();
-            }
-        }
-    });
-}
-
-const moreItemHandle = (params) => {
-    window.open(params.href);
-}
-
-
-/**
- * 获取账户余额
- */
-const getAccountBalance = () => {
-    isRequestWithdrawBalance.value = false;
-    getBalance({}).then((res) => {
-        isRequestWithdrawBalance.value = true;
-        if (res.code == 0) {
-            if (res.data) {
-                canWithdrawBalance.value = res.data.allAssetValuationUSD;
-                withdraw_info.balance = res.data.allAssetValuationUSD || 0
-            }
-        }
-    });
-};
-
-const getUserInfo = (cb) => {
-    getChromeStorage("userInfo", (res) => {
-        cb && cb(res);
-    });
-};
-
-/**
- * 检查登录状态
- */
-const checkLoginState = (cb) => {
-    getUserInfo((res) => {
-        if (res && res.accessToken) {
-            userInfo.value = res;
-            isLogin.value = true;
-            homeVisibility.value = true;
-        } else {
-            userInfo.value = {};
-            isLogin.value = false;
-        }
-        cb && cb();
-    });
-};
-
-const pageScroll = (e) => {
-    let wrapperHeight = pageWrapperDom.value.offsetHeight;
-    let pageGiveListHeight = pageGiveListDom.value.offsetHeight;
-    let scrollTop = e.target.scrollTop || 0;
-    if (currentTabIndex.value != 0) {
-        return;
-    }
-    if (
-        giveReqParams.loadMore === false &&
-        wrapperHeight + scrollTop >= pageGiveListHeight
-    ) {
-        giveReqParams.loadMore = true;
-        giveReqParams.params.pageNum++;
-        getLuckdropRecordsList();
-    }
-};
-
-/**
- * 获取红包列表
- */
-const getLuckdropRecordsList = () => {
-    getMineLuckdropRecords({
-        params: giveReqParams.params,
-    }).then((res) => {
-        if (res.data && res.data.length) {
-            if (giveReqParams.params.pageNum < 2) {
-                giveList.value = res.data;
-            } else {
-                let data = giveList.value;
-                data = data.concat(res.data);
-                giveList.value = data;
-            }
-            giveReqParams.loadMore = false;
-        }
-    });
-};
-
-const clickTab = (params, index) => {
-    currentTabIndex.value = index;
-    console.log(params, index);
-};
-
-/**
- * 点击列表跳转到推文
- */
-const clickListItem = (params) => {
-    if (!params.srcContentId) {
-        return;
-    }
-    let twitterUrl = "https://twitter.com/";
-    let nickName = "";
-    if (params.type == 1) {
-        nickName = params.userInfo.nickName;
-    } else if (params.type == 2) {
-        nickName = userInfo.value.nickName;
-    }
-    let url = twitterUrl + nickName + "/status/" + params.srcContentId;
-
-    chrome.tabs.create({
-        url,
-    });
-};
-
-/**
- * 交易列表返回
- */
-const transactionsBack = () => {
-    if (!homeVisibility.value) {
-        if (transactionsVisibility.value) {
-            transactionsVisibility.value = false;
-        }
-        homeVisibility.value = true;
-    }
-};
-
-/**
- * 提现返回
- */
-const withdrawBack = () => {
-    if (!homeVisibility.value) {
-        if (withdrawVisibility.value) {
-            withdrawVisibility.value = false;
-        }
-        homeVisibility.value = true;
-        getAccountBalance();
-        giveReqParams.params.pageNum = 1;
-        getLuckdropRecordsList();
-    }
-};
-
-
-const showTransactions = () => {
-//     // homeVisibility.value = false;
-//     // transactionsVisibility.value = true;
-
-    readAllMsg({msgType: 1});
-    router.push('/transactions')
-};
-
-// const clickWithdraw = () => {
-//     if (isRequestWithdrawBalance.value) {
-//         homeVisibility.value = false;
-//         withdrawVisibility.value = true;
-//     }
-// };
-
-const loginAction = () => {
-    Report.reportLog({
-        pageSource: Report.pageSource.denetLogin,
-        businessType: Report.businessType.buttonClick,
-        objectType: Report.objectType.loginButton
-    });
-    login();
-};
-
-const login = () => {
-    callEventPageMethod("POPUP_LOGIN", "", function (response) {
-        console.log("res", response);
-    });
-};
-
-/**
- * sendMessage
- */
-const callEventPageMethod = (actionType, data, callback) => {
-    chrome.runtime.sendMessage(
-        { 
-            actionType: actionType, 
-            data: data 
-        },
-        function (response) {
-            if (typeof callback === "function") callback(response);
-        }
-    );
-};
-
-/**
- * 点击发送,去发推
- */
-const sendTwitter = (params) => {
-    console.log(params)
-    callEventPageMethod(
-        "POPUP_PUBLISH_TWITTER_RED_PACK",
-        { 
-            srcContent: params.postTaskLuckdrop.srcContent, 
-            postId: params.postTaskLuckdrop.postId 
-        },
-        function (response) {
-            console.log("res", response);
-        }
-    );
-};
-// 点击提现
-const clickWithdraw = () => {
-    Report.reportLog({
-        pageSource: Report.pageSource.denetHomePage,
-        businessType: Report.businessType.buttonClick,
-        objectType: Report.objectType.withdrawButton
-    });
-    router.push('/withdraw/home');
-}
-const clickTopUp = () => {
-    Report.reportLog({
-        pageSource: Report.pageSource.denetHomePage,
-        businessType: Report.businessType.buttonClick,
-        objectType: Report.objectType.topupButton
-    });
-    router.push('/top-up/home');
-}
-
-const terminaHandler = (params, index) => {
-    terminaTask = params;
-    terminaTask.index = index;
-    modalVisible.value = true;
-}
-
-const modalCancel = () => {
-    //请求终止接口 id terminaTask.id 、 刷新当前列表、 关闭 
-    modalVisible.value = false;
-    let index = terminaTask.index;
-    terminatedLuckdrop({
-        params: {
-            luckdropId: terminaTask.id
-        }
-    }).then(res => {
-        if(res.code == 0) {
-            giveList.value[index]['status'] = res.data.status;
-            giveList.value[index]['postTaskLuckdrop']['reSendAvailable'] = false;
-            giveList.value[index]['postTaskLuckdrop']['terminatedAvailable'] = false;
-        }
-    });
-    terminaTask = {};
-}
-
-const modalConfirm = () => {
-    modalVisible.value = false;
-    terminaTask = {};
-}
-
-</script>
-
-<style lang="scss" scoped>
-html,
-body {
-    padding: 0 !important;
-    margin: 0 !important;
-}
-
-.page-wrapper {
-    width: 375px;
-    height: 600px;
-    box-sizing: border-box;
-    overflow-y: scroll;
-
-    .nav-bar {
-        padding: 14px;
-        box-sizing: border-box;
-        display: flex;
-        align-items: center;
-        justify-content: space-between;
-
-        .item {
-            display: flex;
-            align-items: center;
-            font-size: 13px;
-            cursor: pointer;
-
-            img {
-                width: 16px;
-                height: 16px;
-                margin-right: 4px;
-            }
-        }
-
-        .left {
-            font-weight: 500;
-        }
-
-        .right {
-            color: #b6b6b6;
-        }
-    }
-
-    .content {
-        padding: 20px;
-        background-color: #F0F8FE;
-
-        .icon-money {
-            width: 70px;
-            height: 70px;
-        }
-
-        .balance {
-            display: flex;
-            justify-content: space-between;
-            .wallet {
-                img {
-                    width: 24px;
-                    height: 24px;
-                    margin-right: 6px;
-                    vertical-align: middle;
-                }
-                font {
-                    color: #000;
-                    font-size: 14px;
-                    font-weight: bold;
-                }
-            }
-            .bill {
-                position: relative;
-                img {
-                    width: 24px;
-                    height: 24px;
-                    cursor: pointer;
-                }
-                .red-dot {
-                    position: absolute;
-                    right: 0px;
-                    top: -1px;
-                }
-            }
-        }
-
-        .amount {
-            margin-top: 20px;
-            margin-bottom: 20px;
-            font-weight: 700;
-            font-size: 36px;
-            line-height: 43px;
-        }
-
-        .area-btn {
-            display: flex;
-            justify-content: space-between;
-            font-weight: 600;
-            font-size: 15px;
-
-            .top-up-btn {
-                cursor: pointer;
-                border: 1px solid #1D9BF0;
-                color: #fff;
-                background: #1D9BF0;
-                border-radius: 100px;
-                width: 165px;
-                height: 38px;
-                text-align: center;
-                margin-left: 8px;
-                line-height: 36px;
-            }
-
-            .withdraw-btn {
-                background: rgba(56, 154, 255, 0.01);
-                border: 1px solid #1D9BF0;
-                box-sizing: border-box;
-                width: 165px;
-                font-size: 15px;
-                height: 38px;
-                text-align: center;
-                line-height: 36px;
-                border-radius: 100px;
-                color: #1D9BF0;
-                display: inline-block;
-                cursor: pointer;
-            }
-        }
-
-        .msg {
-            margin-top: 10px;
-            font-size: 13px;
-            color: #b6b6b6;
-        }
-    }
-
-    .tab-bar {
-        display: flex;
-        align-items: center;
-        position: sticky;
-        position: -webkit-sticky;
-        top: 0px;
-        z-index: 1000;
-        background-color: #fff;
-
-        .tab-item {
-            flex: 1;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            padding: 17px 0;
-            box-sizing: border-box;
-            border-bottom: 1px solid #ECECEC;
-            cursor: pointer;
-
-            .icon {
-                width: 16px;
-                height: 16px;
-                margin-right: 5px;
-                font-weight: 500;
-                font-size: 16px;
-            }
-        }
-
-        .active {
-            border-bottom: 2px solid #000;
-        }
-    }
-
-    .list-wrapper {
-        min-height: 202px;
-
-        .give-list {
-            min-height: 202px;
-            position: relative;
-
-            .cell {
-                display: flex;
-                justify-content: space-between;
-                min-height: 66px;
-                box-sizing: border-box;
-                padding-left: 14px;
-                position: relative;
-                cursor: pointer;
-
-                .red-dots {
-                    position: absolute; 
-                    right: 4px; 
-                    top: 4px;
-                }
-
-                .img-wrapper {
-                    position: relative;
-                    margin-right: 16px;
-                    box-sizing: border-box;
-
-                    .icon-avatar {
-                        width: 34px;
-                        height: 34px;
-                        border-radius: 50%;
-                    }
-
-                    .icon-give {
-                        position: absolute;
-                        right: -4px;
-                        bottom: 2px;
-                    }
-
-                    .icon-big-give {
-                        margin-top: 14px;
-                    }
-                }
-
-                .info-wrapper {
-                    flex: 1;
-                    height: 100%;
-                    display: flex;
-                    justify-content: space-between;
-                    border-bottom: 1px solid #ECECEC;
-                    box-sizing: border-box;
-                    padding: 10px 14px 10px 0;
-
-                    .left {
-                        .nickname {
-                            font-weight: 500;
-                            font-size: 14px;
-                            margin-bottom: 5px;
-                            max-width: 132px;
-                            word-break: break-all;
-                        }
-
-                        .time {
-                            font-size: 12px;
-                            color: #B0B0B0;
-                        }
-                    }
-
-                    .right {
-                        display: flex;
-                        align-items: center;
-                        cursor: pointer;
-
-                        .msg {
-                            .bold {
-                                font-weight: 500;
-                                font-size: 14px;
-                                text-align: right;
-                                display: flex;
-                                justify-content: flex-end;
-                                align-items: center;
-
-                                .blance {
-                                    margin-left: 3px;
-                                    display: inline-block;
-                                    max-width: 80px;
-                                    word-break: break-all;
-                                    line-height: 18px;
-                                    color: #E29A2E;
-                                }
-
-                                .coin-type-wrapper {
-                                    display: flex;
-                                    align-items: center;
-                                }
-
-                                .coin-type {
-                                    margin-left: 3px;
-                                }
-
-                                img {
-                                    margin-left: 4px;
-                                    width: 14px;
-                                    height: 14px;
-                                }
-                            }
-
-                            .align-content {
-                                flex-direction: column;
-                                align-items: flex-end;
-
-                                .blance {
-                                    max-width: 130px;
-                                }
-                            }
-
-                            .desc {
-                                font-size: 12px;
-                                color: #b6b6b6;
-                                margin-top: 5px;
-                                text-align: right;
-
-                                .desc-bottom-bar {
-                                    display: flex;
-                                    align-items: center;
-                                    justify-content: end;
-                                    margin-top: 10px;
-
-                                    .btn {
-                                        min-width: 80px;
-                                        height: 29px;
-                                        padding: 0 8px;
-                                        box-sizing: border-box;
-                                        font-weight: 400;
-                                        font-size: 14px;
-                                        cursor: pointer;
-                                        text-align: center;
-                                        border-radius: 100px;
-                                        color: #5E5E5E;
-                                        border: 1px solid #DFDFDF;
-                                        display: flex;
-                                        align-items: center;
-                                        justify-content: center;
-                                    }
-
-                                    .send-btn {
-                                        border: 1px solid #1D9BF0;
-                                        color: #1D9BF0;
-                                    }
-
-                                    .detail-btn, .send-btn {
-                                        margin-left: 8px;
-                                    }
-                                }
-                            }
-                        }
-
-                        .icon {
-                            width: 18px;
-                            height: 24px;
-                            margin-left: 4px;
-                            margin-right: -5px;
-                        }
-                    }
-                }
-                .info-center {
-                    align-items: center;
-                }
-            }
-            .cell-center {
-                align-items: center;
-            }
-
-            .icon-empty {
-                position: absolute;
-                left: 50%;
-                top: 50%;
-                transform: translate(-50%, -50%);
-            }
-        }
-
-        .more-list {
-            .cell {
-                cursor: pointer;
-                display: flex;
-                justify-content: space-between;
-                align-items: center;
-                height: 66px;
-                box-sizing: border-box;
-                padding-left: 20px;
-
-                .icon {
-                    width: 42px;
-                    height: 42px;
-                    border-radius: 50%;
-                }
-
-                .info-wrapper {
-                    flex: 1;
-                    height: 100%;
-                    display: flex;
-                    justify-content: space-between;
-                    align-items: center;
-                    border-bottom: 1px solid #ECECEC;
-                    box-sizing: border-box;
-                    padding-right: 16px;
-
-                    .left {
-                        font-weight: 500;
-                        font-size: 16px;
-                        .time {
-                            color: #B0B0B0;
-                        }
-                    }
-
-                    .right {
-                        display: flex;
-                        align-items: center;
-                        cursor: pointer;
-
-                        .icon {
-                            width: 18px;
-                            height: 24px;
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
-.page-wrapper::-webkit-scrollbar {
-    display: none;
-}
-</style>

+ 163 - 0
src/view/popup/tabbar-page/index.vue

@@ -0,0 +1,163 @@
+<template>
+  <div class="tabbar-page-wrapper">
+    <global-tip :type="'3'"></global-tip>
+    <!-- login -->
+    <popup-login v-if="!loginStatus" @loginAction="loginAction" />
+    <template v-else>
+      <top-bar  :userInfo="userInfo" 
+                :bgColor="bgColor"
+                :color="color"
+                :boxShadow="boxShadow"></top-bar>
+      <div class="tabbar-content">
+        <router-view></router-view>
+      </div>
+      <tabbar :userInfo="userInfo"  
+              @tabbarClick = "onTabbarHandler"></tabbar>
+    </template>
+  </div>
+</template>
+
+<script setup>
+import { onMounted, onBeforeUnmount, ref, nextTick } from "vue";
+import { getChromeStorage } from "@/uilts/chromeExtension";
+import { setStorage, getStorage } from "@/uilts/help";
+import Report from "@/log-center/log";
+import router from "@/router/popup.js";
+
+import TopBar from "@/view/popup/components/top-bar.vue";
+import Tabbar from "@/view/popup/components/tabbar.vue";
+import  GlobalTip  from '@/view/components/global-tip.vue';
+import popupLogin from "@/view/popup/components/login.vue";
+
+let userInfo = ref({});
+let bgColor = ref('#1b92e2');
+let color =  ref('#fff');
+let boxShadow = ref('none');
+
+let loginStatus = ref('default'); 
+
+const getUserInfo = (cb) => {
+  getChromeStorage("userInfo", (res) => {
+    if (res && res.accessToken) {
+      userInfo.value = res;
+      loginStatus.value = res;
+    } else {
+      loginStatus.value = '';
+      userInfo.value = {};
+    }
+    cb && cb(res);
+  });
+};
+
+const onTabbarHandler = (params) => {
+  if(params.path == '/') {
+    bgColor.value = '#1b92e2';
+    boxShadow.value = 'none';
+    color.value = '#fff'
+  } else {
+    bgColor.value = '#fff';
+    color.value = '#000';
+
+    if(params.path != '/more') {
+      boxShadow.value = '0px 0.5px 0px 0px #D1D9DD';
+    } else {
+      boxShadow.value = 'none';
+    }
+  }
+}
+
+const loginAction = () => {
+    Report.reportLog({
+        pageSource: Report.pageSource.denetLogin,
+        businessType: Report.businessType.buttonClick,
+        objectType: Report.objectType.loginButton
+    });
+    login();
+};
+
+const login = () => {
+    callEventPageMethod("POPUP_LOGIN", "", function (response) {
+        console.log("res", response);
+    });
+}
+
+
+/**
+ * sendMessage
+ */
+const callEventPageMethod = (actionType, data, callback) => {
+    chrome.runtime.sendMessage(
+        { 
+            actionType: actionType, 
+            data: data 
+        },
+        function (response) {
+            if (typeof callback === "function") callback(response);
+        }
+    );
+};
+
+const onPageVisbile = () => {
+  document.addEventListener('visibilitychange', function () {
+    let isHidden = document.hidden;
+    if (!isHidden) {
+      if(!userInfo.value.accessToken) {
+        setTimeout(() => {
+          getUserInfo();
+        }, 500)
+      }
+    }
+  });
+}
+
+const onRuntimeMsg = () => {
+  chrome.runtime.onMessage.addListener(msgListener)
+}
+
+const msgListener = (req, sender, sendResponse) => {
+  sendResponse('');
+  switch (req.actionType) {
+    case 'BG_LOGIN_SET_USERINFO_CB':
+      if(!userInfo.value.accessToken) {
+        setTimeout(() => {
+          getUserInfo();
+        }, 800)
+      }
+      break;
+    case 'CONTENT_POPUP_PAGE_SHOW':
+        init();
+        break;
+  }
+}
+
+const init = () => {
+  nextTick(() => {
+    onTabbarHandler(router.currentRoute.value);
+  })
+  getUserInfo();
+}
+
+onMounted(() => {
+  onPageVisbile();
+  onRuntimeMsg();
+  init();
+});
+
+onBeforeUnmount(() => {
+  chrome.runtime.onMessage.removeListener(msgListener);
+})
+</script>
+
+
+<style lang='scss'>
+.tabbar-page-wrapper {
+  width: 100%;
+  height: 100%;
+  position: relative;
+
+  .tabbar-content {
+    width: 100%;
+    height: calc(100% - 120px);
+  }
+}
+</style>

+ 667 - 0
src/view/popup/tabbar-page/message/index.vue

@@ -0,0 +1,667 @@
+<template>
+  <div class="message-wrapper">
+    <!-- <div class="tab-bar">
+      <div
+        class="tab-item"
+        :class="{ active: currentTabIndex == index }"
+        v-for="(item, index) in tabList"
+        :key="index"
+        @click="clickTab(item, index)"
+      >
+        {{ item.label }}
+      </div>
+    </div> -->
+    <div class="tab-content" ref="pageWrapperDom" @scroll="pageScroll">
+      <div class="list-wrapper" ref="pageGiveListDom">
+        <div class="give-list" v-if="currentTabIndex == 0">
+          <template v-if="giveList.length">
+            <div
+              class="cell"
+              :class="{ 'cell-center': item.type == 1 }"
+              v-for="(item, index) in giveList"
+              :key="index"
+              @click="clickListItem(item, index)"
+            >
+              <red-dot
+                class="red-dots"
+                v-if="item.unReadMsgCount > 0 && isReadMsg"
+              ></red-dot>
+
+              <div class="img-wrapper">
+                <!-- 收到红包 -->
+                <template v-if="item.type == 1">
+                  <img class="icon-avatar" :src="item.userInfo.avatarUrl" />
+                  <img
+                    class="icon-give"
+                    :src="require('@/assets/svg/icon-get-giveaways-s.svg')"
+                  />
+                </template>
+                <!-- 发出去红包 -->
+                <template v-else-if="2">
+                  <img
+                    class="icon-big-give"
+                    :src="require('@/assets/svg/icon-send-giveaways-s.svg')"
+                  />
+                </template>
+              </div>
+              <div
+                class="info-wrapper"
+                :class="{ 'info-center': item.type == 1 }"
+              >
+                <div class="left">
+                  <div class="nickname">
+                    {{ item.type == 1 ? "Get Giveaway" : "Send Giveaway" }}
+                  </div>
+                  <div class="time">
+                    {{ moment(item.timestamp).format("MM-DD HH:mm:ss") }}
+                  </div>
+                </div>
+                <div class="right">
+                  <div class="msg">
+                    <div
+                      class="bold"
+                      :class="{
+                        'align-content':
+                          (item.type == 2 ||
+                            (item.type == 1 && item.status == 1)) &&
+                          item.amount.length + item.currencySymbol.length > 12,
+                      }"
+                    >
+                      <!-- 收到的 -->
+                      <template v-if="item.type == 1">
+                        <!-- 进行中-->
+                        <template v-if="item.status == 0">
+                          in progress
+                        </template>
+                        <!-- 已完成 -->
+                        <template v-else-if="item.status == 1">
+                          <span class="blance">
+                            <a-tooltip :title="item.amount">
+                              {{ getBit(item.amount) }}</a-tooltip
+                            >
+                          </span>
+                          <div class="coin-type-wrapper">
+                            <span class="coin-type">{{
+                              item.currencySymbol || ""
+                            }}</span>
+                            <img :src="item.currencyIconPath" alt="" />
+                          </div>
+                        </template>
+                        <!-- 已过期 -->
+                        <template v-else-if="item.status == 2">
+                          Timeout
+                        </template>
+                      </template>
+                      <!-- 发出去的 -->
+                      <template v-else-if="item.type == 2">
+                        <span class="blance">
+                          <a-tooltip :title="'-' + item.amount">
+                            -{{ getBit(item.amount) }}
+                          </a-tooltip>
+                        </span>
+                        <div class="coin-type-wrapper">
+                          <span class="coin-type">{{
+                            item.currencySymbol || ""
+                          }}</span>
+                          <img :src="item.currencyIconPath" alt="" />
+                        </div>
+                      </template>
+                    </div>
+                    <!-- 发出的红包显示 -->
+                    <div class="desc" v-if="item.type == 2">
+                      <!-- 未发送-->
+                      <template v-if="item.postTaskLuckdrop.reSendAvailable">
+                        Unpublished
+                      </template>
+                      <!-- 进行中 -->
+                      <template v-else-if="item.status == 1">
+                        {{ item.postTaskLuckdrop.receivedCount }}/{{
+                          item.postTaskLuckdrop.totalCount
+                        }}
+                      </template>
+                      <!-- 2:已结束; 3:提前终止-->
+                      <template
+                        v-else-if="item.status == 2 || item.status == 3"
+                      >
+                        ({{
+                          item.status == 2 ? "Time expired" : "Termination"
+                        }}) {{ item.postTaskLuckdrop.receivedCount }}/{{
+                          item.postTaskLuckdrop.totalCount
+                        }}
+                      </template>
+                      <!-- 红包提前终止/退款(进行中)显示-->
+                      <template v-if="item.status == 4"> Terminating </template>
+
+                      <!-- 进行中或者未发送成功时显示 
+                                                v-if="item.status == 1 || item.postTaskLuckdrop.reSendAvailable"-->
+                      <div class="desc-bottom-bar">
+                        <!-- 没有终止红包时显示 -->
+                        <div
+                          v-if="item.postTaskLuckdrop.terminatedAvailable"
+                          class="btn"
+                          @click.stop="terminaHandler(item, index)"
+                        >
+                          Termination
+                        </div>
+
+                        <!-- 红包未发出显示 -->
+                        <div
+                          class="btn send-btn"
+                          v-if="item.postTaskLuckdrop.reSendAvailable"
+                          @click.stop="sendTwitter(item)"
+                        >
+                          Send
+                        </div>
+                        <div
+                          v-else-if="item.srcContentId"
+                          class="btn detail-btn"
+                          @click.stop="clickListItem(item, index)"
+                        >
+                          details
+                        </div>
+                      </div>
+                    </div>
+                  </div>
+                  <!-- 发红包—— 未发出、进行中 隐藏 -->
+                  <img
+                    v-if="item.type != 2"
+                    class="icon"
+                    :src="require('@/assets/svg/icon-cell-arrow-right.svg')"
+                  />
+                </div>
+              </div>
+            </div>
+          </template>
+          <template v-else>
+            <img
+              class="icon-empty"
+              :src="require('@/assets/svg/icon-empty-list.svg')"
+            />
+          </template>
+        </div>
+      </div>
+    </div>
+    <modal
+      :visible="modalVisible"
+      title="Early termination of Giveaway?"
+      content="The remaining amount will be returned to your wallet within 1 day."
+      cancelText="Termination"
+      confirmText="Cancel"
+      @cancel="modalCancel"
+      @confirm="modalConfirm"
+    />
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, inject, onBeforeUnmount } from "vue";
+
+import modal from "@/view/popup/components/modal.vue";
+import redDot from "@/view/components/red-dot.vue";
+
+import { getBit } from "@/uilts/help";
+import { getMineLuckdropRecords } from "@/http/account";
+import { terminatedLuckdrop } from "@/http/redPacket";
+import { readAllMsgByType, getAllMessageInfo } from "@/http/messageApi"
+import { setBadgeInfo, hideBadge } from "@/logic/background/twitter";
+import { getChromeStorage } from "@/uilts/chromeExtension";
+
+var moment = require("moment");
+
+let currentTabIndex = ref(0);
+let userInfo = ref({});
+
+let tabList = ref([
+  {
+    label: "ALL",
+  },
+  {
+    label: "Giveaway",
+  },
+  {
+    label: "NFT",
+  },
+]);
+
+let pageWrapperDom = ref(null);
+let pageGiveListDom = ref(null);
+
+let modalVisible = ref(false);
+let terminaTask = {};
+
+let giveList = ref([]);
+let giveReqParams = {
+  params: {
+    pageNum: 1,
+    pageSize: 20,
+  },
+  loadMore: false,
+};
+
+
+let isReadMsg = ref(true);
+
+const clickTab = (params, index) => {
+  currentTabIndex.value = index;
+};
+
+/**
+ * 获取红包列表
+ */
+const getLuckdropRecordsList = () => {
+  getMineLuckdropRecords({
+    params: giveReqParams.params,
+  }).then((res) => {
+    chrome.runtime.connect({ name: "popup" });
+    if (res.data && res.data.length) {
+      if (giveReqParams.params.pageNum < 2) {
+        giveList.value = res.data;
+      } else {
+        let data = giveList.value;
+        data = data.concat(res.data);
+        giveList.value = data;
+      }
+      giveReqParams.loadMore = false;
+    }
+  });
+};
+
+/**
+ * 点击列表跳转到推文
+ */
+const clickListItem = (params) => {
+  if (!params.srcContentId) {
+    return;
+  }
+  let twitterUrl = "https://twitter.com/";
+  let nickName = "";
+  if (params.type == 1) {
+    nickName = params.userInfo.nickName;
+  } else if (params.type == 2) {
+    nickName = userInfo.value.nickName;
+  }
+  let url = twitterUrl + nickName + "/status/" + params.srcContentId;
+
+  chrome.tabs.create({
+    url,
+  });
+};
+
+const pageScroll = (e) => {
+  let wrapperHeight = pageWrapperDom.value.offsetHeight;
+  let pageGiveListHeight = pageGiveListDom.value.offsetHeight;
+  let scrollTop = e.target.scrollTop || 0;
+  if (currentTabIndex.value != 0) {
+    return;
+  }
+  if (
+    giveReqParams.loadMore === false &&
+    wrapperHeight + scrollTop >= pageGiveListHeight - 60
+  ) {
+    giveReqParams.loadMore = true;
+    giveReqParams.params.pageNum++;
+    getLuckdropRecordsList();
+  }
+};
+
+/**
+ * 点击发送,去发推
+ */
+const sendTwitter = (params) => {
+  console.log(params);
+  callEventPageMethod(
+    "POPUP_PUBLISH_TWITTER_RED_PACK",
+    {
+      srcContent: params.postTaskLuckdrop.srcContent,
+      postId: params.postTaskLuckdrop.postId,
+    },
+    function (response) {
+      console.log("res", response);
+    }
+  );
+};
+
+/**
+ * sendMessage
+ */
+const callEventPageMethod = (actionType, data, callback) => {
+  chrome.runtime.sendMessage(
+    {
+      actionType: actionType,
+      data: data,
+    },
+    function (response) {
+      if (typeof callback === "function") callback(response);
+    }
+  );
+};
+
+const terminaHandler = (params, index) => {
+  terminaTask = params;
+  terminaTask.index = index;
+  modalVisible.value = true;
+};
+
+const modalCancel = () => {
+  //请求终止接口 id terminaTask.id 、 刷新当前列表、 关闭
+  modalVisible.value = false;
+  let index = terminaTask.index;
+  terminatedLuckdrop({
+    params: {
+      luckdropId: terminaTask.id,
+    },
+  }).then((res) => {
+    if (res.code == 0) {
+      giveList.value[index]["status"] = res.data.status;
+      giveList.value[index]["postTaskLuckdrop"]["reSendAvailable"] = false;
+      giveList.value[index]["postTaskLuckdrop"]["terminatedAvailable"] = false;
+    }
+  });
+  terminaTask = {};
+};
+
+const modalConfirm = () => {
+  modalVisible.value = false;
+  terminaTask = {};
+};
+
+const readAllMsg = ({msgType}, cb) => {
+    readAllMsgByType({
+        params: {
+            msgType
+        }
+    }).then(res => {
+        cb && cb();
+    })
+};
+
+const setMessageCount = () => {
+    getAllMessageInfo({params: {
+    }}).then(res => {
+        if(res.code == 0) {
+            let {unReadCountTotal = 0, unReadCountWalletDetail = 0, unReadCountTaskLuckdrop = 0} = res.data;
+            if(unReadCountTotal > 0) {
+                let text = unReadCountTotal > 99 ? '99+' : unReadCountTotal+'';
+                setBadgeInfo({data: {text}});
+            } else {
+                hideBadge();
+            }
+        }
+    });
+}
+
+
+const getUserInfo = (cb) => {
+  getChromeStorage("userInfo", (res) => {
+    if (res && res.accessToken) {
+      userInfo.value = res;
+    } else {
+      userInfo.value = {};
+    }
+    cb && cb(res);
+  });
+};
+
+const init = () => {
+  getUserInfo();
+  getLuckdropRecordsList();
+
+  setMessageCount();
+  setTimeout(() => {
+      isReadMsg.value = false;
+      readAllMsg({msgType: 1}, () => {
+          setMessageCount();
+      });
+  }, 2000);
+}
+
+const onMessage = () => {
+    chrome.runtime.onMessage.addListener(msgListener)
+}
+
+const msgListener = (req, sender, sendResponse) => {
+  sendResponse('');
+  switch (req.actionType) {
+      case 'CONTENT_POPUP_PAGE_SHOW':
+          init();
+          break;
+  }
+}
+
+onMounted(() => {
+  onMessage();
+  init();
+});
+
+onBeforeUnmount(() => {
+  chrome.runtime.onMessage.removeListener(msgListener);
+})
+</script>
+
+
+<style scoped lang="scss">
+.message-wrapper {
+  width: 100%;
+  height: 100%;
+  margin-top: 1px;
+  .tab-bar {
+    display: flex;
+    align-items: center;
+    background-color: #fff;
+    box-shadow: 0px 0.5px 0px #d1d9dd;
+
+    .tab-item {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      padding: 14px 0px;
+      box-sizing: border-box;
+      border-bottom: 2px solid #fff;
+      margin: 0 12px;
+      font-size: 14px;
+      color: #949494;
+      cursor: pointer;
+    }
+
+    .active {
+      border-bottom: 2px solid #1d9bf0;
+      font-weight: 500;
+      color: #000;
+    }
+  }
+
+  .tab-content {
+    height: 100%;
+    overflow-y: auto;
+    .list-wrapper {
+      min-height: 202px;
+
+      .give-list {
+        min-height: 202px;
+        position: relative;
+
+        .cell {
+          display: flex;
+          justify-content: space-between;
+          min-height: 76px;
+          box-sizing: border-box;
+          padding-left: 14px;
+          position: relative;
+          cursor: pointer;
+
+          .red-dots {
+            position: absolute;
+            right: 4px;
+            top: 4px;
+          }
+
+          .img-wrapper {
+            position: relative;
+            margin-right: 16px;
+            box-sizing: border-box;
+
+            .icon-avatar {
+              width: 34px;
+              height: 34px;
+              border-radius: 50%;
+              margin-right: 8px;
+            }
+
+            .icon-give {
+              position: absolute;
+              right: 1px;
+              bottom: -1px;
+              width: 18px;
+              height: 18px;
+            }
+
+            .icon-big-give {
+              margin-top: 12px;
+            }
+          }
+
+          .info-wrapper {
+            flex: 1;
+            height: 100%;
+            display: flex;
+            justify-content: space-between;
+            box-sizing: border-box;
+            padding: 10px 14px 10px 0;
+
+            .left {
+              .nickname {
+                font-weight: 500;
+                font-size: 13px;
+                margin-bottom: 5px;
+                max-width: 132px;
+                word-break: break-all;
+              }
+
+              .time {
+                font-size: 12px;
+                color: #b0b0b0;
+              }
+            }
+
+            .right {
+              display: flex;
+              align-items: center;
+              cursor: pointer;
+
+              .msg {
+                display: flex;
+                align-items: end;
+                flex-direction: column;
+                .bold {
+                  font-weight: 500;
+                  font-size: 13px;
+                  text-align: right;
+                  display: flex;
+                  justify-content: flex-end;
+                  align-items: center;
+                  max-width: 140px;
+
+                  .blance {
+                    margin-left: 3px;
+                    display: inline-block;
+                    max-width: 80px;
+                    word-break: break-all;
+                    line-height: 18px;
+                    color: #e86f00;
+                  }
+
+                  .coin-type-wrapper {
+                    display: flex;
+                    align-items: center;
+                  }
+
+                  .coin-type {
+                    margin-left: 3px;
+                    word-break: break-all;
+                  }
+
+                  img {
+                    margin-left: 4px;
+                    width: 14px;
+                    height: 14px;
+                  }
+                }
+
+                .align-content {
+                  flex-direction: column;
+                  align-items: flex-end;
+
+                  .blance {
+                    max-width: 130px;
+                  }
+                }
+
+                .desc {
+                  font-size: 12px;
+                  color: #b6b6b6;
+                  margin-top: 5px;
+                  text-align: right;
+
+                  .desc-bottom-bar {
+                    display: flex;
+                    align-items: center;
+                    justify-content: end;
+                    margin-top: 10px;
+
+                    .btn {
+                      min-width: 80px;
+                      height: 29px;
+                      padding: 0 8px;
+                      box-sizing: border-box;
+                      font-weight: 400;
+                      font-size: 14px;
+                      cursor: pointer;
+                      text-align: center;
+                      border-radius: 100px;
+                      color: #5e5e5e;
+                      border: 1px solid #dfdfdf;
+                      display: flex;
+                      align-items: center;
+                      justify-content: center;
+                    }
+
+                    .send-btn {
+                      border: 1px solid #1d9bf0;
+                      color: #1d9bf0;
+                    }
+
+                    .detail-btn,
+                    .send-btn {
+                      margin-left: 8px;
+                    }
+                  }
+                }
+              }
+
+              .icon {
+                width: 18px;
+                height: 24px;
+                margin-left: 4px;
+                margin-right: -5px;
+              }
+            }
+          }
+          .info-center {
+            align-items: center;
+          }
+        }
+        .cell-center {
+          align-items: center;
+        }
+
+        .icon-empty {
+          position: absolute;
+          left: 50%;
+          top: 50%;
+          transform: translate(-50%, -50%);
+        }
+      }
+    }
+  }
+}
+</style>

+ 115 - 0
src/view/popup/tabbar-page/more/index.vue

@@ -0,0 +1,115 @@
+<template>
+  <div class="more-page">
+    <div class="more-list">
+      <div
+        class="cell"
+        v-for="(item, index) in moreTabList"
+        :key="index"
+        @click="moreItemHandle(item)"
+      >
+        <img class="icon" :src="item.icon" />
+        <div class="info-wrapper">
+          <div class="left">
+            {{ item.label }}
+          </div>
+          <div class="right">
+            <img
+              class="icon"
+              :src="require('@/assets/svg/icon-cell-arrow-right.svg')"
+            />
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from "vue";
+
+let moreTabList = ref([
+  {
+    icon: require("@/assets/svg/icon-website.svg"),
+    label: "Official Website",
+    href: "https://denet.me",
+  },
+  {
+    icon: require("@/assets/svg/icon-twitter.svg"),
+    label: "Twitter",
+    href: "https://twitter.com/denet2022",
+  },
+  {
+    icon: require("@/assets/svg/icon-discord.svg"),
+    label: "Discord",
+    href: "https://discord.gg/wZSz9p8ddG",
+  },
+  {
+      icon: require("@/assets/svg/icon-telegram.svg"),
+      label: "Telegram",
+      href: 'https://t.me/denetpro'
+  }
+]);
+
+const moreItemHandle = (params) => {
+  window.open(params.href);
+};
+</script>
+
+<style scoped lang="scss">
+.more-page {
+  background: #f6f6f6;
+  width: 100%;
+  height: 100%;
+  overflow: auto;
+  margin-top: 1px;
+
+  .more-list {
+    background: #fff;
+    margin-top: 10px;
+
+    .cell {
+      cursor: pointer;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      height: 56px;
+      box-sizing: border-box;
+      padding: 0 16px;
+
+      .icon {
+        width: 40px;
+        height: 40px;
+        border-radius: 50%;
+      }
+
+      .info-wrapper {
+        flex: 1;
+        height: 100%;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        box-sizing: border-box;
+        margin-left: 13px;
+
+        .left {
+          font-size: 14px;
+          .time {
+            color: #b0b0b0;
+          }
+        }
+
+        .right {
+          display: flex;
+          align-items: center;
+          cursor: pointer;
+
+          .icon {
+            width: 18px;
+            height: 24px;
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 424 - 0
src/view/popup/tabbar-page/nft/detail.vue

@@ -0,0 +1,424 @@
+<template>
+    <div class="nft-detail-wrapper">
+        <div class="back-bar">
+            <img
+                :src="require('@/assets/svg/icon-nft-back-arrow.svg')"
+                class="icon-arrow"
+                @click="back"
+            />
+            {{NFTInfo.nftItemName}}
+        </div>
+        <div class="content">
+            <div class="nft-img">
+                <img
+                    class="img"
+                    :src="NFTInfo.imagePath"
+                    @click="clickNFTImg"
+                />
+            </div>
+            <div class="desc item" v-if="nftMetaData.description">
+                <div class="title">Description</div>
+                <div class="desc-content" v-html="nftMetaData.description"></div>
+            </div>
+            <div class="prop item" v-if="nftMetaData.properties && nftMetaData.properties.length">
+                <div class="title">Properties</div>
+                <div class="prop-content">
+                    <div
+                        class="prop-item"
+                        v-for="(filedValueItem, filedValueIndex) in nftMetaData.properties"
+                        :key="filedValueIndex"
+                    >
+                        {{ filedValueItem.name }}
+                        <div class="prop-name">
+                            {{ filedValueItem.value }}
+                        </div>
+                        {{ filedValueItem.description }}
+                    </div>
+                </div>
+            </div>
+
+            <div class="about item" v-if="nftMetaData.about">
+                <div class="title">About</div>
+                <div class="about-content" v-html="nftMetaData.about"></div>
+            </div>
+            <div class="detail item"  v-if="nftDetailData.details">
+                    <div class="title">Details</div>
+                    <div class="detail-content">
+                        <div class="detail-item">
+                            <div class="left">Contract Address</div>
+                            <div class="right address"  
+                                @click="clickAddress">
+                                    <span>{{nftDetailData.details.contractAddress}}</span>
+                                    <span>{{nftDetailData.details.contractAddress}}</span>
+                            </div>
+                        </div>
+                        <div class="detail-item">
+                            <div class="left">Token ID</div>
+                            <div class="right token"
+                                @click="clickToken">
+                                {{nftDetailData.details.tokenId}}
+                            </div>
+                        </div>
+                        <div class="detail-item">
+                            <div class="left">Token Standard</div>
+                            <div class="right" >
+                                {{nftDetailData.details.tokenStandard}}
+                            </div>
+                        </div>
+                        <div class="detail-item">
+                            <div class="left">Blockchain</div>
+                            <div class="right" >
+                                {{nftDetailData.details.blockChain}}
+                            </div>
+                        </div>
+                        <div class="detail-item">
+                            <div class="left">Creator Fees</div>
+                            <div class="right" >
+                                {{nftDetailData.details.creatorFees}}
+                            </div>
+                        </div>
+                        <div class="detail-item">
+                            <div class="left">Transaction Royalties</div>
+                            <div class="right" >
+                                {{nftDetailData.details.transactionRoyalties}}
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="date item"  v-if="nftDetailData.dateOfPossession">
+                    <div class="title">Date of possession</div>
+                    <div class="date-content">{{nftDetailData.dateOfPossession}}</div>
+                </div>
+
+                <div class="price item"  v-if="nftDetailData.purchasePrice">
+                    <div class="title">Purchase price</div>
+                    <div class="price-content">{{nftDetailData.purchasePrice}}</div>
+                </div>
+        </div>
+        <div class="bottom-bar">
+            <div class="default">NFT Sale function, coming soon</div>
+            <!-- <div class="sell">
+                    <div class="sell-btn">
+                        Sell
+                    </div>
+                </div> 
+                <div class="cancel-sale">
+                    <div class="left">
+                        233 USDT
+                        <div class="final">
+                            (Final 203.5 USDT)
+                        </div>
+                    </div>
+                    <div class="cancel-btn">
+                        Cancel sale
+                    </div>
+                </div> -->
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from "vue";
+import router from "@/router/popup.js";
+
+import {getNFTDetail} from "@/http/nft.js";
+
+let nftMetaData = ref({});
+let nftDetailData = ref({});
+
+let NFTInfo = ref({
+    imagePath: '',
+    nftItemName: ''
+});
+
+const back = () => {
+    router.back();
+};
+
+const clickAddress = () => {
+    let {contractAddressUrl = ''} = nftDetailData.value.details;
+    if(contractAddressUrl) {
+        window.open(contractAddressUrl);
+    }
+}
+
+const clickToken = () => {
+    let {tokenIdUrl = ''} = nftDetailData.value.details;
+    if(tokenIdUrl) {
+        window.open(tokenIdUrl);
+    }
+}
+
+const clickNFTImg = () => {
+    // window.open(NFTInfo.value.imagePath);
+};
+
+const getDetail = () => {
+    getNFTDetail({
+        params: {
+            nftItemId: NFTInfo.value.nftItemId
+        }
+    }).then(res => {
+        if(res.code == 0) {
+            console.log(res)
+            let { metadata = '{}'} = res.data || {};
+            nftDetailData.value = res.data;
+            nftMetaData.value = JSON.parse(metadata);
+        }
+    }).catch((err)=>{
+
+    })
+}
+
+onMounted(() => {
+    let {params = '{}'} = router.currentRoute.value.query;
+    NFTInfo.value = JSON.parse(params);
+    getDetail();
+})
+
+
+</script>
+
+<style scoped lang="scss">
+.nft-detail-wrapper {
+    width: 100%;
+    height: 100%;
+
+    .back-bar {
+        height: 48px;
+        background: #ffffff;
+        box-shadow: 0px 0.5px 0px #d1d9dd;
+        box-sizing: border-box;
+        padding: 14px;
+        font-weight: 500;
+        font-size: 16px;
+        display: flex;
+        align-items: center;
+
+        .icon-arrow {
+            width: 24px;
+            margin-right: 12px;
+            cursor: pointer;
+        }
+    }
+
+    .content {
+        width: 100%;
+        height: calc(100% - 120px);
+        padding: 0 16px;
+        box-sizing: border-box;
+        overflow-y: auto;
+
+        .nft-img {
+            margin-top: 23px;
+            margin-bottom: 20px;
+            text-align: center;
+            cursor: pointer;            
+
+            .img {
+                width: 280px;
+                border-radius: 26px;
+            }
+        }
+
+        .item {
+            border: 1px solid #e3e3e3;
+            border-radius: 10px;
+            padding: 14px;
+            box-sizing: border-box;
+            margin-bottom: 12px;
+
+            .title {
+                font-weight: 600;
+                font-size: 14px;
+            }
+        }
+
+        .desc {
+            margin-top: 10px;
+            .desc-content {
+                font-weight: 500;
+                font-size: 14px;
+                color: #929292;
+
+                span {
+                    color: #1d9bf0;
+                }
+            }
+        }
+
+        .prop {
+            .prop-content {
+                display: flex;
+                flex-wrap: wrap;
+                margin-top: 12px;
+
+                .prop-item {
+                    width: 48%;
+                    height: 88px;
+                    background: #f8f8f8;
+                    border-radius: 10px;
+                    display: flex;
+                    flex-direction: column;
+                    justify-content: center;
+                    padding: 8px;
+                    box-sizing: border-box;
+                    align-items: center;
+                    font-weight: 500;
+                    font-size: 12px;
+                    color: #929292;
+                    margin-bottom: 10px;
+
+                    .prop-name {
+                        font-weight: 700;
+                        font-size: 17px;
+                        margin-top: 6px;
+                        margin-bottom: 8px;
+                        color: #000;
+                    }
+                }
+                .prop-item:nth-child(odd) {
+                    margin-right: 8px;
+                }
+            }
+        }
+
+        .about-content {
+            margin-top: 22px;
+            .section {
+                font-weight: 400;
+                font-size: 14px;
+                margin-bottom: 20px;
+            }
+        }
+        .section {
+            font-weight: 400;
+            font-size: 14px;
+            margin-bottom: 10px;
+        }
+
+        .detail-content {
+            margin-top: 15px;
+
+            .detail-item {
+                display: flex;
+                align-items: center;
+                justify-content: space-between;
+                height: 24px;
+                font-weight: 400;
+                font-size: 14px;
+
+                .right {
+                    color: #929292;
+                }
+
+                .token {
+                    color: #1d9bf0 !important;
+                    cursor: pointer;
+                }
+
+                .address {
+                    width: 100px;
+                    white-space: nowrap;
+                    color: #1d9bf0 !important;
+                    cursor: pointer;
+
+                    > span {
+                        display: inline-block;
+                        overflow: hidden;
+                        text-overflow: ellipsis;
+                        width: 50%;
+                        + span {
+                            width: calc(50% + 10px);
+                            direction: rtl;
+                            margin-left: -11px;
+                        }
+                    }
+                }
+            }
+        }
+
+        .date-content,
+        .price-content {
+            margin-top: 10px;
+            font-weight: 500;
+            font-size: 14px;
+            color: #929292;
+        }
+    }
+
+    .bottom-bar {
+        background: #ffffff;
+        box-shadow: inset 0px 1px 0px #ececec;
+        height: 70px;
+        padding: 15px 16px;
+        box-sizing: border-box;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+
+        .default {
+            font-weight: 500;
+            font-size: 16px;
+            color: #a8a8a8;
+            text-align: center;
+        }
+
+        .sell {
+            width: 100%;
+            height: 100%;
+
+            .sell-btn {
+                width: 120px;
+                height: 40px;
+                box-sizing: border-box;
+                border: 1px solid #e9e9e9;
+                border-radius: 100px;
+                font-weight: 500;
+                font-size: 16px;
+                color: #1d9bf0;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                position: absolute;
+                right: 16px;
+                cursor: pointer;
+            }
+        }
+
+        .cancel-sale {
+            width: 100%;
+            height: 100%;
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+
+            .left {
+                font-weight: 500;
+                font-size: 15px;
+                .final {
+                    font-weight: 500;
+                    font-size: 12px;
+                    color: #929292;
+                    margin-top: 6px;
+                }
+            }
+
+            .cancel-btn {
+                width: 120px;
+                height: 40px;
+                box-sizing: border-box;
+                border: 1px solid #e9e9e9;
+                border-radius: 100px;
+                font-weight: 500;
+                font-size: 16px;
+                color: #ff0000;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                cursor: pointer;
+            }
+        }
+    }
+}
+</style>

+ 138 - 0
src/view/popup/tabbar-page/nft/index.vue

@@ -0,0 +1,138 @@
+<template>
+  <div class="nft-page-wrapper"
+    ref="pageWrapperDom"
+    @scroll="pageScroll" >
+    <div class="content" ref="pageListDom">
+      <div class="item" 
+          v-for="(item, index) in listData" 
+          :key="index" 
+          @click="clickNFT(item)">
+        <img :src="item.imagePath" class="img">
+        <div class="name">{{item.nftItemName}}</div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onBeforeUnmount } from "vue";
+import router from "@/router/popup.js";
+
+import {nftListMine} from "@/http/nft.js";
+
+let listData = ref([]);
+
+let NFTReqParams = {
+  params: {
+    pageNum: 1,
+    pageSize: 30,
+  },
+  loadMore: false,
+};
+
+let pageWrapperDom = ref(null);
+let pageListDom = ref(null);
+
+const clickNFT = (params) => {
+  router.push({
+    path: '/NFTDetail',
+    query: {
+      params: JSON.stringify(params)
+    }
+  })
+}
+
+const getNFTListMine = () => {
+  nftListMine({
+    params: NFTReqParams.params,
+  }).then((res) => {
+    if (res.data && res.data.length) {
+      if (NFTReqParams.params.pageNum < 2) {
+        listData.value = res.data;
+      } else {
+        let data = listData.value;
+        data = data.concat(res.data);
+        listData.value = data;
+      }
+      NFTReqParams.loadMore = false;
+    }
+  });
+}
+
+
+
+const pageScroll = (e) => {
+  let wrapperHeight = pageWrapperDom.value.offsetHeight;
+  let pageListHeight = pageListDom.value.offsetHeight;
+  let scrollTop = e.target.scrollTop || 0;
+  if (
+    NFTReqParams.loadMore === false &&
+    wrapperHeight + scrollTop >= pageListHeight - 60
+  ) {
+    NFTReqParams.loadMore = true;
+    NFTReqParams.params.pageNum++;
+    getNFTListMine();
+  }
+};
+
+
+const onMessage = () => {
+    chrome.runtime.onMessage.addListener(msgListener);
+}
+
+const msgListener = (req, sender, sendResponse) => {
+    sendResponse('');
+    switch (req.actionType) {
+        case 'CONTENT_POPUP_PAGE_SHOW':
+            getNFTListMine();
+            break;
+    }
+}
+
+onMounted(() => {
+  onMessage();
+  getNFTListMine();
+})
+
+onBeforeUnmount(() => {
+    chrome.runtime.onMessage.removeListener(msgListener);
+})
+</script>
+
+
+<style scoped lang="scss">
+    .nft-page-wrapper {
+      width: 100%;
+      height: 100%;
+      overflow-y: auto;
+
+      .content {
+        width: 100%;
+        display: flex;
+        flex-wrap: wrap;
+        padding: 5px 2px 0 16px;
+        box-sizing: border-box;
+
+        .item {
+          width: 33%;
+          box-sizing: border-box;
+          padding-right: 14px;
+          margin-top: 15px;
+          cursor: pointer;
+
+          .img {
+            width: 100%;
+            border-radius: 5px;
+            height: 104px;
+            object-fit: cover;
+          }
+
+          .name {
+            font-weight: 400;
+            font-size: 12px;
+            margin-top: 6px;
+          }
+        }
+      }
+    }
+</style>

+ 359 - 0
src/view/popup/tabbar-page/wallter/popup.vue

@@ -0,0 +1,359 @@
+<template>
+    <div class="page-wrapper" ref="pageWrapperDom">
+        <div class="content">
+            <div class="balance">
+                <div class="wallet">
+                    <font>Balance Valuation</font>
+                </div>
+            </div>
+            <div class="amount-wrapper">
+                <div class="amount">
+                    <a-tooltip :title="'$'+canWithdrawBalance">
+                        ${{ getBit(canWithdrawBalance) }}
+                    </a-tooltip>
+                </div>
+
+                <div class="right">
+                    <div class="bill" @click="showTransactions">
+                        <red-dot class="red-dot" v-if="unReadCountWallet > 0"></red-dot>
+                        <img :src="require('@/assets/svg/icon-home-list.svg')" />
+                    </div>
+
+                    <img :src="require('@/assets/svg/icon-home-refresh.svg')" 
+                        class="icon"
+                        :class="{ transform_rotate: iconRotate }"
+                        @click="refreshList"  />
+                </div>
+            </div>
+        </div>
+
+        <currency-list 
+            v-if="userInfo.accessToken"
+            style="height: calc(100% - 103px);"
+            ref="currencyListDom"
+            :showRefresh="false"
+            :page="'top-up'"
+            @selectCurrency="selectCurrency"></currency-list>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onBeforeUnmount, inject } from "vue";
+
+import redDot from "@/view/components/red-dot.vue";
+import CurrencyList from "@/view/components/currency-list.vue";
+
+import {
+    getChromeStorage,
+} from "@/uilts/chromeExtension";
+import { getBalance } from "@/http/account";
+import { getAllMessageInfo } from "@/http/messageApi"
+import { setBadgeInfo, hideBadge } from "@/logic/background/twitter";
+import Report from "@/log-center/log";
+import router from "@/router/popup.js";
+import { getBit } from "@/uilts/help";
+
+let withdraw_info = inject('withdraw_info')
+withdraw_info.paypal = {}
+
+var moment = require("moment");
+
+let userInfo = ref({});
+let canWithdrawBalance = ref(0);
+withdraw_info.paypal.amount_value = canWithdrawBalance
+withdraw_info.balance = 0
+
+let isRequestWithdrawBalance = ref(false);
+
+let currencyListDom = ref('');
+let iconRotate = ref(false)
+
+// 钱包未读数
+let unReadCountWallet = ref(0);
+
+let walletWithdrawConfig = ref({
+    withdrawUSDPaypalFee: 0,
+    withdrawUSDPreMinAmount: 100,
+    withdrawUSDSwitch: "",
+    withdrawUSDPaypalFeeDesc: ''
+});
+withdraw_info.paypal.wallet_withdraw_config = walletWithdrawConfig
+
+
+function selectCurrency(_params) {
+    router.push({ 
+        path: 'currencyDetail',
+        query: {
+            params: JSON.stringify(_params)
+        }
+    });
+}
+
+const init = () => {
+    checkLoginState((res) => {
+        if (res) {
+            getAccountBalance();
+            Report.reportLog({
+                pageSource: Report.pageSource.denetHomePage,
+                businessType: Report.businessType.pageView,
+            },{
+                type: window.location.href.indexOf('home.html') > -1 ? 'web' : 'extensions'
+            });
+            setMessageCount();
+        } else {
+            Report.reportLog({
+                pageSource: Report.pageSource.denetLogin,
+                businessType: Report.businessType.pageView,
+            });
+        }
+    });
+}
+
+const setMessageCount = () => {
+    getAllMessageInfo({params: {
+    }}).then(res => {
+        if(res.code == 0) {
+            let {unReadCountTotal = 0, unReadCountWalletDetail = 0, unReadCountTaskLuckdrop = 0} = res.data;
+            unReadCountWallet.value = unReadCountWalletDetail;
+            if(unReadCountTotal > 0) {
+                let text = unReadCountTotal > 99 ? '99+' : unReadCountTotal+'';
+                setBadgeInfo({data: {text}});
+            } else {
+                hideBadge();
+            }
+        }
+    });
+}
+
+
+/**
+ * 获取账户余额
+ */
+const getAccountBalance = () => {
+    isRequestWithdrawBalance.value = false;
+    getBalance({}).then((res) => {
+        isRequestWithdrawBalance.value = true;
+        if (res.code == 0) {
+            if (res.data) {
+                canWithdrawBalance.value = res.data.allAssetValuationUSD;
+                withdraw_info.balance = res.data.allAssetValuationUSD || 0
+            }
+        }
+    });
+};
+
+const getUserInfo = (cb) => {
+    getChromeStorage("userInfo", (res) => {
+        cb && cb(res);
+    });
+};
+
+/**
+ * 检查登录状态
+ */
+const checkLoginState = (cb) => {
+    getUserInfo((res) => {
+        if (res && res.accessToken) {
+            userInfo.value = res;
+        } else {
+            userInfo.value = {};
+        }
+        cb && cb(res);
+    });
+};
+
+const showTransactions = () => {
+    router.push('/transactions')
+};
+
+// 点击提现
+const clickWithdraw = () => {
+    Report.reportLog({
+        pageSource: Report.pageSource.denetHomePage,
+        businessType: Report.businessType.buttonClick,
+        objectType: Report.objectType.withdrawButton
+    });
+    router.push('/withdraw/home');
+}
+
+const clickTopUp = () => {
+    Report.reportLog({
+        pageSource: Report.pageSource.denetHomePage,
+        businessType: Report.businessType.buttonClick,
+        objectType: Report.objectType.topupButton
+    });
+    router.push('/top-up/home');
+}
+
+const refreshList = () => {
+    if (iconRotate.value) {
+        return
+    }
+    iconRotate.value = true
+    setTimeout(() => {
+        iconRotate.value = false
+    }, 1000)
+
+    getAccountBalance();
+    if(currencyListDom.value) {
+        currencyListDom.value.getCurrencyInfoList && currencyListDom.value.getCurrencyInfoList();
+    }
+}
+
+const onMessage = () => {
+    chrome.runtime.onMessage.addListener(msgListener)
+}
+
+const msgListener = (req, sender, sendResponse) => {
+    sendResponse('');
+    switch (req.actionType) {
+        case 'CONTENT_POPUP_PAGE_SHOW':
+            init();
+            if(currencyListDom.value) {
+                currencyListDom.value.getCurrencyInfoList && currencyListDom.value.getCurrencyInfoList();
+            }
+            break;
+    }
+}
+
+
+onMounted(() => {
+    onMessage();
+    init();
+});
+
+onBeforeUnmount(() => {
+    chrome.runtime.onMessage.removeListener(msgListener);
+})
+
+</script>
+
+<style lang="scss" scoped>
+html,
+body {
+    padding: 0 !important;
+    margin: 0 !important;
+}
+
+.page-wrapper {
+    width: 375px;
+    height: 100%;
+    box-sizing: border-box;
+    overflow-y: auto;
+
+    .nav-bar {
+        padding: 14px;
+        box-sizing: border-box;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+
+        .item {
+            display: flex;
+            align-items: center;
+            font-size: 13px;
+            cursor: pointer;
+
+            img {
+                width: 16px;
+                height: 16px;
+                margin-right: 4px;
+            }
+        }
+
+        .left {
+            font-weight: 500;
+        }
+
+        .right {
+            color: #b6b6b6;
+        }
+    }
+
+    .content {
+        padding: 12px 16px 10px 16px;
+        background: #1D9BF0;
+        box-sizing: border-box;
+
+        .icon-money {
+            width: 70px;
+            height: 70px;
+        }
+
+        .balance {
+            display: flex;
+            justify-content: space-between;
+            .wallet {
+                font {
+                    font-size: 13px;
+                    color: #fff;
+                    opacity: 0.7;
+                }
+            }
+        }
+
+        .amount-wrapper {
+            margin-top: 2px;
+            font-weight: 700;
+            font-size: 36px;
+            color: #fff;
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+
+            .amount {
+                display: inline-block;
+            }
+
+            .right {
+                display: flex;
+                align-items: center;
+
+                .bill {
+                    height: 24px;
+                    width: 24px;
+                    position: relative;
+
+                    img {
+                        width: 24px;
+                        height: 24px;
+                        cursor: pointer;
+                        position: absolute;
+                        left: 0;
+                        top: 0;
+                    }
+
+                    .red-dot {
+                        position: absolute;
+                        right: 0px;
+                        top: -1px;
+                        z-index: 100;
+                    }
+                }
+
+                .icon {
+                    margin-left: 22px;
+                    cursor: pointer;
+                }
+                .transform_rotate {
+                    transform: rotate(360deg);
+                    transition-duration: 1s;
+                }
+            }
+        }
+
+        .msg {
+            margin-top: 10px;
+            font-size: 13px;
+            color: #b6b6b6;
+        }
+    }
+
+
+
+}
+
+.page-wrapper::-webkit-scrollbar {
+    display: none;
+}
+</style>

+ 5 - 4
src/view/popup/top-up/home.vue

@@ -30,11 +30,12 @@ let showCurrencySelect = ref(false)
 let tempCurrentCurrencyList = ref([])
 
 function selectCurrency(params) {
-    tempCurrentCurrencyList.value = params;
-    if (params.length > 1) {
+    let { currencies } = params;
+    tempCurrentCurrencyList.value = currencies;
+    if (currencies.length > 1) {
         showCurrencySelect.value = true;
     } else {
-        selectCurrencyAfter(params[0])
+        selectCurrencyAfter(currencies[0])
     }
 }
 function selectCurrencyAfter(_params) {
@@ -74,7 +75,7 @@ onMounted(() => {
     bottom: 0;
     background-color: #fff;
     border-radius: 20px 20px 0 0;
-    overflow-y: scroll;
+    overflow-y: auto;
 }
 .selectBg {
     position: absolute;

+ 12 - 1
src/view/popup/transactions.vue

@@ -1,7 +1,7 @@
 <template>
     <!-- 公共组件 -->
     <div class="info">
-        <v-head :title="'Transactions'" :show_more="false" :back_url="'/'" @onBack="clickBack" ></v-head>
+        <v-head :title="'Transactions'" :show_more="false" :back_url="back_url" @onBack="clickBack" ></v-head>
         <popup-transactions style="height: calc(100% - 48px);"></popup-transactions>
     </div>
 
@@ -10,8 +10,19 @@
 <script setup>
 import VHead from '@/view/popup/components/head.vue'
 import PopupTransactions from "@/view/components/popup-transactions";
+import {ref, onMounted} from 'vue';
+import router from "@/router/popup.js";
 
+let back_url = ref('/');
 
+onMounted(() => {
+    let {params = '{}'} = router.currentRoute.value.query;
+    let {backUrl = '/'} =  JSON.parse(params);
+    
+    if(backUrl == 'back') {
+        back_url.value = '';
+    }
+})
 </script>
 
 

+ 5 - 4
src/view/popup/withdraw/home.vue

@@ -30,11 +30,12 @@ let showCurrencySelect = ref(false)
 let tempCurrentCurrencyList = ref([])
 
 function selectCurrency(params) {
-    tempCurrentCurrencyList.value = params;
-    if (params.length > 1) {
+    let { currencies } = params;
+    tempCurrentCurrencyList.value = currencies;
+    if (currencies.length > 1) {
         showCurrencySelect.value = true;
     } else {
-        selectCurrencyAfter(params[0])
+        selectCurrencyAfter(currencies[0])
     }
 }
 
@@ -84,7 +85,7 @@ onMounted(() => {
     bottom: 0;
     background-color: #fff;
     border-radius: 20px 20px 0 0;
-    overflow-y: scroll;
+    overflow-y: auto;
 }
 .selectBg {
     position: absolute;

+ 6 - 1
src/view/popup/withdraw/info.vue

@@ -1,7 +1,12 @@
 <template>
   <!-- 公共组件 -->
   <div class="info">
-    <v-head :title="'Withdraw'" :show_more="true"></v-head>
+    <v-head :title="'Withdraw'" 
+            :show_more="true" 
+            :show_help="true"  
+            :transactionsRouterParams="{
+              backUrl: 'back'
+            }"></v-head>
     <!-- 内容 -->
     <div class="content">
       <div class="area-input-1">

+ 1 - 1
src/view/popup/withdraw/success.vue

@@ -17,7 +17,7 @@ let withdraw_info = inject('withdraw_info')
 
 let router = useRouter()
 const onDone = () => {
-    router.replace('/withdraw/home')
+    router.replace('/')
 }
 
 </script>

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