home.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. <template>
  2. <div class="dialog" :style="{'height': dialogStyle.height + 'px'}">
  3. <!-- home -->
  4. <div class="area-title">
  5. <img :src="require('@/assets/svg/icon-close.svg')" @click="clickClose" />
  6. <div class="title">NFT Mystery box</div>
  7. </div>
  8. <!-- 内容 -->
  9. <div class="area-content">
  10. <img :src="state.data.mysteryBoxImagePath" class="box" v-show="state.data.mysteryBoxImagePath" />
  11. <img :src="require('@/assets/svg/icon-default.svg')" class="box" v-show="!state.data.mysteryBoxImagePath" />
  12. </div>
  13. <!-- 底部 -->
  14. <div class="footer" v-show="state.data.mysteryBoxImagePath">
  15. <!-- 首页 -->
  16. <div class="mark">
  17. <div class="sold">SOLD: {{ state.data.itemSoldCount || 0 }}/{{ state.data.itemTotalCount || 0 }} </div>
  18. <div class="limit" v-if="showDesc">Buy Limit: {{ state.data.userBuyCount || 0 }}/{{ state.data.perUserBuyLimit || 0 }}</div>
  19. </div>
  20. <div class="btn-area">
  21. <!-- 兑换码 -->
  22. <template v-if="(state.data.perUserBuyLimit - state.data.userBuyCount) >= 1 && (state.data.itemTotalCount - state.data.itemSoldCount) >= 1">
  23. <div class="redeem" @click="showRedeemLayer">Redeem</div>
  24. </template>
  25. <template v-else>
  26. <div class="redeem grey">Redeem</div>
  27. </template>
  28. <template v-for="item in state.data.salePlans.slice(0, 2).reverse()">
  29. <div class="buy1" @click="clickJump(item)" v-if="item.itemCount == 1 && (state.data.perUserBuyLimit - state.data.userBuyCount) >= 1
  30. && (state.data.itemTotalCount - state.data.itemSoldCount) >= 1">
  31. <template v-if="(item.price.length + item.currencyInfo.tokenSymbol.length) > 30">
  32. <div class="left">Buy 1</div>
  33. <div class="right">
  34. <p>{{ item.price }}</p>
  35. <p>{{ item.currencyInfo.tokenSymbol }}</p>
  36. </div>
  37. </template>
  38. <template v-else>
  39. <div class="left">Buy 1</div>
  40. <div class="right">
  41. {{ item.price }}
  42. {{ item.currencyInfo.tokenSymbol }}
  43. </div>
  44. </template>
  45. </div>
  46. <div class="buy1 grey" v-if="item.itemCount == 1 && ((state.data.perUserBuyLimit - state.data.userBuyCount) <= 0
  47. || (state.data.itemTotalCount - state.data.itemSoldCount) <= 0)">
  48. <template v-if="(item.price.length + item.currencyInfo.tokenSymbol.length) > 30">
  49. <div class="left">Buy 1</div>
  50. <div class="right">
  51. <p>{{ item.price }}</p>
  52. <p>{{ item.currencyInfo.tokenSymbol }}</p>
  53. </div>
  54. </template>
  55. <template v-else>
  56. <div class="left">Buy 1</div>
  57. <div class="right">
  58. {{ item.price }}
  59. {{ item.currencyInfo.tokenSymbol }}
  60. </div>
  61. </template>
  62. </div>
  63. <div class="buy5" v-if="item.itemCount == 5 && (state.data.perUserBuyLimit - state.data.userBuyCount) >= 5 &&
  64. (state.data.itemTotalCount - state.data.itemSoldCount) >= 5" @click="clickJump(item)">
  65. <div class="left">Buy {{ item.itemCount }}</div>
  66. <div class="right" v-if="(item.price.length + item.currencyInfo.tokenSymbol.length) > 30">
  67. <div class="usdt">
  68. <p>{{ item.price }}</p>
  69. <p>{{ item.currencyInfo.tokenSymbol }}</p>
  70. </div>
  71. <div class="off">
  72. <p>{{ item.discount }}</p>
  73. </div>
  74. </div>
  75. <div class="right" v-else>
  76. <div class="usdt">{{ item.price }} {{ item.currencyInfo.tokenSymbol }}</div>
  77. <div class="off">{{ item.discount }}</div>
  78. </div>
  79. </div>
  80. </template>
  81. </div>
  82. </div>
  83. </div>
  84. <!-- 兑换码弹出层 -->
  85. <template v-if="showRedeem">
  86. <div class="redeemLayer">
  87. <div class="header">
  88. <img :src="require('@/assets/svg/icon-close.svg')" @click="hideRedeemLayer" />
  89. </div>
  90. <div class="footer">
  91. <div class="tips">Enter Redemption Code</div>
  92. <div class="input"><input ref="refInput" type="text" v-model="redeemStr" /></div>
  93. <div class="confirm">
  94. <button class="btn" @click="redeemPost" v-if="redeemNext">Confirm</button>
  95. <button class="btn grey" v-else>Confirm</button>
  96. </div>
  97. </div>
  98. </div>
  99. <div class="redeemMask" @click="hideRedeemLayer"></div>
  100. </template>
  101. </template>
  102. <script setup>
  103. import { useRouter } from 'vue-router'
  104. import { ElMessage } from 'element-plus'
  105. import { onMounted, reactive, inject, ref, nextTick, watchEffect } from "vue";
  106. import { getNftMysteryBoxSaleInfo, redeemNft } from "@/http/nft";
  107. import BtnLoading from '../components/btn-loading.vue'
  108. import Report from "@/log-center/log"
  109. import { getQueryString } from "@/uilts/help";
  110. import { sendChromeTabMessage } from '@/uilts/chromeExtension.js';
  111. let pay_info = inject('pay_info');
  112. let router = useRouter()
  113. let showDesc = ref(true)
  114. let showRedeem = ref(false)
  115. let redeemNext = ref(false)
  116. let redeemStr = ref('')
  117. let refInput = ref('')
  118. let groupId = ref('')
  119. let projectId = ref('')
  120. let dialogStyle = reactive({
  121. height: '800'
  122. })
  123. let state = reactive({
  124. data: {
  125. salePlans: [
  126. {
  127. currencyCode: 'BSC_TESTNET_BF_6X',
  128. discount: '20$',
  129. itemCount: 5,
  130. price: 23,
  131. salePlanId: '2'
  132. },
  133. {
  134. currencyCode: 'BSC_TESTNET_BF_6X',
  135. discount: '20$',
  136. itemCount: 1,
  137. price: 123,
  138. salePlanId: '123'
  139. }
  140. ]
  141. }
  142. })
  143. const clickClose = () => {
  144. chrome.tabs.getCurrent((tab) => {
  145. chrome.tabs.sendMessage(tab.id, {
  146. actionType: "IFRAME_TWITTER_HIDE_BUY_NFT",
  147. }, (res) => { });
  148. })
  149. }
  150. const clickJump = (item) => {
  151. pay_info.home.sale_plan = item
  152. router.push({ path: '/pay' });
  153. }
  154. const setDialogStyle = () => {
  155. let clientHeight = window.innerHeight;
  156. if(clientHeight >= 840) {
  157. dialogStyle.height = 800;
  158. } else {
  159. dialogStyle.height = clientHeight - 40;
  160. }
  161. }
  162. const redeemPost = () => {
  163. let params = {
  164. redeemCode: redeemStr.value
  165. }
  166. if (projectId.value) {
  167. params['nftProjectId'] = projectId.value
  168. }
  169. if (groupId.value) {
  170. params['nftGroupId'] = groupId.value
  171. }
  172. // disabled btn
  173. redeemNext.value = false
  174. redeemNft({ params }).then(res => {
  175. let { code, data } = res;
  176. if (code == 0 && data) {
  177. pay_info.buy_items = data
  178. sendChromeTabMessage({ actionType: "FINISH_GROUP_BANNNER" }, () => { })
  179. router.push({ path: '/open_box' });
  180. // report
  181. Report.reportLog({
  182. pageSource: Report.pageSource.nftShopPage,
  183. businessType: Report.businessType.buttonClick,
  184. objectType: Report.objectType.redeem_button,
  185. nftProjectId: projectId.value,
  186. }, {
  187. result: 'success'
  188. })
  189. } else {
  190. let msg = ''
  191. switch (res.code.toString()) {
  192. case '5001':
  193. msg = 'nft project not exist'
  194. break;
  195. case '5002':
  196. msg = 'nft project not available'
  197. break
  198. case '5101':
  199. msg = 'nft sale plan not exist'
  200. break
  201. case '5102':
  202. msg = 'nft sold out'
  203. break
  204. case '5103':
  205. msg = 'Purchase limit reached'
  206. break
  207. case '5104':
  208. case '5105':
  209. case '5106':
  210. msg = 'Invalid redemption code'
  211. break;
  212. default:
  213. msg = 'Invalid redemption code, please try again'
  214. console.log(res.msg)
  215. }
  216. ElMessage({
  217. message: msg,
  218. type: 'warning'
  219. })
  220. redeemNext.value = true;
  221. // report
  222. Report.reportLog({
  223. pageSource: Report.pageSource.nftShopPage,
  224. businessType: Report.businessType.buttonClick,
  225. objectType: Report.objectType.redeem_button,
  226. nftProjectId: projectId.value,
  227. }, {
  228. result: 'fail'
  229. })
  230. }
  231. }).catch(() => {
  232. ElMessage({
  233. message: 'Invalid redemption code, please try again',
  234. type: 'warning'
  235. })
  236. redeemNext.value = true;
  237. })
  238. }
  239. const showRedeemLayer = () => {
  240. showRedeem.value = true;
  241. nextTick(() => {
  242. refInput.value.focus()
  243. })
  244. // report
  245. Report.reportLog({
  246. pageSource: Report.pageSource.nftShopPage,
  247. businessType: Report.businessType.buttonClick,
  248. objectType: Report.objectType.redeem_button,
  249. nftProjectId: projectId.value,
  250. })
  251. }
  252. const hideRedeemLayer = () => {
  253. showRedeem.value = false;
  254. redeemStr.value = '';
  255. }
  256. watchEffect(() => {
  257. let len = 16
  258. let str = redeemStr.value.replace(/[^a-zA-Z0-9]/g, '')
  259. str = str.toUpperCase();
  260. str = str.slice(0, len);
  261. // set
  262. redeemStr.value = str;
  263. redeemNext.value = str !== '' && str.length === len;
  264. })
  265. onMounted(() => {
  266. setDialogStyle();
  267. let nft_project_Id = router.currentRoute.value.query.nftProjectId
  268. let nft_group_Id = router.currentRoute.value.query.nft_group_Id
  269. if(nft_group_Id){
  270. pay_info.nft_group_Id = nft_group_Id
  271. }
  272. if (!nft_project_Id) {
  273. return
  274. }
  275. // 作用域外用
  276. groupId.value = nft_group_Id
  277. projectId.value = nft_project_Id
  278. getNftMysteryBoxSaleInfo({
  279. params: {
  280. nftProjectId: nft_project_Id
  281. }
  282. }).then((res) => {
  283. if (res.code == 0) {
  284. state.data = res.data
  285. pay_info.home = res.data
  286. let { perUserBuyLimit, itemTotalCount } = res.data;
  287. // 不限购
  288. if (perUserBuyLimit && itemTotalCount && perUserBuyLimit >= itemTotalCount) {
  289. showDesc.value = false;
  290. }
  291. } else {
  292. }
  293. }).catch(() => {
  294. })
  295. // report
  296. Report.reportLog({
  297. pageSource: Report.pageSource.nftShopPage,
  298. businessType: Report.businessType.pageView,
  299. nftProjectId: nft_project_Id
  300. })
  301. })
  302. </script>
  303. <style lang="scss" scoped>
  304. .dialog {
  305. background: #fff;
  306. border-radius: 25px;
  307. max-width: 1000px;
  308. min-width: 1000px;
  309. max-height: 90%;
  310. z-index: 23;
  311. display: flex;
  312. flex-direction: column;
  313. .area-title {
  314. width: 100%;
  315. min-height: 48px;
  316. display: flex;
  317. align-items: center;
  318. border-bottom: 1px solid #D9D9D9;
  319. font-weight: 500;
  320. font-size: 16px;
  321. letter-spacing: 0.3px;
  322. color: #000000;
  323. img {
  324. width: 24p;
  325. height: 24px;
  326. margin-left: 14px;
  327. margin-right: 12px;
  328. cursor: pointer;
  329. }
  330. }
  331. .area-content {
  332. flex: 1;
  333. overflow-y: auto;
  334. img {
  335. width: 100%;
  336. object-fit: contain;
  337. }
  338. }
  339. .footer {
  340. border-top: 1px solid #D9D9D9;
  341. width: 100%;
  342. display: flex;
  343. align-items: center;
  344. justify-content: space-between;
  345. .loading {
  346. width: 24px;
  347. }
  348. .mark {
  349. margin-left: 20px;
  350. .sold {
  351. margin-bottom: 7px;
  352. }
  353. .limit {
  354. color: #AF934E;
  355. margin-right: 25px;
  356. }
  357. }
  358. .btn-area {
  359. height: 100%;
  360. display: flex;
  361. padding: 15px 0;
  362. min-height: 50px;
  363. box-sizing: border-box;
  364. .buy5 {
  365. border: 1px solid #1D9BF0;
  366. background: rgba(29, 155, 240, 0.01);
  367. border-radius: 100px;
  368. color: #1D9BF0;
  369. min-width: 217px;
  370. display: flex;
  371. justify-content: space-between;
  372. align-items: center;
  373. padding: 10px 15px 10px 20px;
  374. font-weight: 700;
  375. font-size: 14px;
  376. cursor: pointer;
  377. margin-right: 12px;
  378. box-sizing: border-box;
  379. .left {
  380. margin-right: 20px;
  381. }
  382. .right {
  383. text-align: right;
  384. line-height: 17px;
  385. p {
  386. margin: 0;
  387. padding: 0;
  388. line-height: 17px;
  389. }
  390. }
  391. .off {
  392. color: #AF934E;
  393. font-weight: 700;
  394. font-size: 12px;
  395. letter-spacing: 0.3px;
  396. }
  397. .usdt {
  398. color: #1D9BF0;
  399. font-size: 14px;
  400. font-weight: 700;
  401. }
  402. }
  403. .buy1 {
  404. cursor: pointer;
  405. background: #1D9BF0;
  406. color: #fff;
  407. border-radius: 100px;
  408. min-width: 217px;
  409. display: flex;
  410. align-items: center;
  411. font-size: 14px;
  412. font-weight: 700;
  413. justify-content: space-between;
  414. padding: 0 15px 0 20px;
  415. margin-right: 25px;
  416. box-sizing: border-box;
  417. min-height: 50px;
  418. .left {
  419. margin-right: 20px;
  420. }
  421. .right {
  422. line-height: 17px;
  423. text-align: right;
  424. p {
  425. margin: 0;
  426. padding: 0;
  427. line-height: 17px;
  428. }
  429. }
  430. }
  431. .grey {
  432. background: #CDCDCD;
  433. cursor: not-allowed;
  434. }
  435. .redeem {
  436. border: 1px solid #1D9BF0;
  437. background: rgba(29, 155, 240, 0.01);
  438. border-radius: 100px;
  439. color: #1D9BF0;
  440. min-width: 110px;
  441. display: flex;
  442. justify-content: center;
  443. align-items: center;
  444. font-weight: 700;
  445. font-size: 16px;
  446. cursor: pointer;
  447. margin-right: 12px;
  448. box-sizing: border-box;
  449. &.grey {
  450. background: #CDCDCD;
  451. cursor: not-allowed;
  452. color: #fff;
  453. border: unset;
  454. }
  455. }
  456. }
  457. }
  458. }
  459. .redeemLayer {
  460. position: absolute;
  461. z-index: 25;
  462. top: 50%;
  463. left: 50%;
  464. width: 630px;
  465. height: 270px;
  466. border-radius: 20px;
  467. background: #FFFFFF;
  468. transform: translate(-50%, -50%);
  469. .header {
  470. display: flex;
  471. align-items: center;
  472. height: 48px;
  473. box-shadow: 0px 0.5px 0px #D1D9DD;
  474. img {
  475. width: 24px;
  476. height: 24px;
  477. margin-left: 14px;
  478. cursor: pointer;
  479. }
  480. }
  481. .footer {
  482. padding: 20px;
  483. .tips {
  484. font-size: 16px;
  485. font-weight: 500;
  486. line-height: 19px;
  487. letter-spacing: 0.3px;
  488. margin-bottom: 18px;
  489. }
  490. .input {
  491. display: flex;
  492. align-items: center;
  493. justify-content: center;
  494. height: 49px;
  495. border-radius: 100px;
  496. background: #FFFFFF;
  497. border: 1px solid #DDDDDD;
  498. input {
  499. width: calc(100% - 40px);
  500. border: 0;
  501. outline: 0;
  502. font-size: 16px;
  503. font-weight: 500;
  504. line-height: 19px;
  505. }
  506. }
  507. .confirm {
  508. margin-top: 45px;
  509. text-align: right;
  510. .btn {
  511. border: 0;
  512. width: 170px;
  513. height: 51px;
  514. cursor: pointer;
  515. color: #FFFFFF;
  516. font-size: 16px;
  517. font-weight: 700;
  518. border-radius: 100px;
  519. background: #1D9BF0;
  520. &.grey {
  521. background: #CDCDCD;
  522. cursor: not-allowed;
  523. color: #fff;
  524. border: unset;
  525. }
  526. }
  527. }
  528. }
  529. }
  530. .redeemMask {
  531. position: absolute;
  532. z-index: 24;
  533. top: 0;
  534. left: 0;
  535. width: 100%;
  536. height: 100%;
  537. background-color: rgba($color: #000000, $alpha: .7);
  538. }
  539. </style>