home.vue 18 KB

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