bemarket/src/components/main/NftSection.vue
2022-02-25 11:39:48 +08:00

385 lines
9.3 KiB
Vue

<template>
<section id="nft_section" data-anchor="nft">
<div class="info">
<div class="count-down">
<div class="time" v-if="presaleStatus === 2">{{timeStr}}</div>
<div class="title">{{presaleTitle}}</div>
</div>
<div class="text" v-html="hint">
</div>
</div>
<card-scroller :heros="heroDatas" :presale-status="presaleStatus" class="card-scroller"></card-scroller>
<div class="count-label">{{numberRest}}/{{numberTotal}}</div>
<div class="order-status" v-show="showOrderStatus">
<el-tooltip class="item" effect="light" content="Transaction in progress" placement="right">
<time-loader></time-loader>
</el-tooltip>
</div>
</section>
</template>
<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator'
import CardScroller from '@/components/main/CardScroller.vue'
import { queryOrder, queryPresaleStatus, searchBox } from '@/api/Mall'
import { ISpineData } from '@/utils/SpineRender'
import { AppModule } from '@/store/modules/app'
import { secs2str } from '@/utils/time.util'
import { EventBus, PRESALE_BEGIN, PRESALE_ERROR, PRESALE_ORDER_GET, PRESALE_SUCCESS } from '@/utils/event-bus'
import { Loading } from 'element-ui'
import { ElLoadingComponent } from 'element-ui/types/loading'
import TimeLoader from '@/components/main/TimeLoader.vue'
import { UserModule } from '@/store/modules/user'
@Component({
name: 'NftSection',
components: {
TimeLoader,
CardScroller
}
})
export default class extends Vue {
heroDatas: ISpineData[] = []
hint = ''
presaleTitle = ''
presaleStatus = 0
numberTotal = 0
numberRest = 0
buyed = true
timeStr = ''
countdown = 0
timer: any = null
orderTimer: any = null
loadingInstance: ElLoadingComponent
showOrderStatus = false
historyOrderId: string | null = ''
created() {
this.fetchData()
this.subscribeToEvents()
}
mounted() {
this.checkOrderHistory()
}
unmounted() {
this.removeEvents()
}
@Watch('isLogin')
private accountChange() {
if (this.accountId) {
this.getPresaleInfo()
this.checkOrderHistory()
}
}
@Watch('countdown')
private countDownChange() {
this.timeStr = secs2str(this.countdown)
}
get accountId() {
return AppModule.accountId
}
get isLogin() {
return !!UserModule.token && !!AppModule.step
}
async fetchData() {
await this.queryPresaleList()
await this.getPresaleInfo()
}
checkOrderHistory() {
const historyOrderId = localStorage.getItem('tmp_presale_order_id')
if (historyOrderId && this.accountId) {
this.beginTraceOrderStatus(historyOrderId)
}
}
subscribeToEvents() {
EventBus.$on(PRESALE_BEGIN, this.onPresaleBegin.bind(this))
EventBus.$on(PRESALE_SUCCESS, this.onPresaleSuccess.bind(this))
EventBus.$on(PRESALE_ERROR, this.onPresaleError.bind(this))
EventBus.$on(PRESALE_ORDER_GET, this.onOrderIDGeted.bind(this))
}
removeEvents() {
EventBus.$off(PRESALE_BEGIN, this.onPresaleBegin.bind(this))
EventBus.$off(PRESALE_SUCCESS, this.onPresaleSuccess)
EventBus.$off(PRESALE_ERROR, this.onPresaleError)
EventBus.$off(PRESALE_ORDER_GET, this.onOrderIDGeted.bind(this))
}
onPresaleBegin() {
this.loadingInstance = Loading.service({})
console.log('presale begin')
}
onOrderIDGeted(orderId: string) {
console.log('order id:', orderId)
this.$alert('Transaction in progress', 'We will notify you after confirmation', { type: 'info', confirmButtonText: 'OK' })
this.beginTraceOrderStatus(orderId)
}
onPresaleError(res: any) {
console.log('presale error: ', res)
this.showOrderStatus = false
this.loadingInstance?.close()
this.$alert('Some error when process presale', 'Buy Failed', { type: 'error', confirmButtonText: 'OK' })
}
async onPresaleSuccess(res: any) {
console.log('presale success', res)
this.resetTmpOrderId()
this.showOrderStatus = false
this.$alert('Congratulations', 'Buy Success', { type: 'success', confirmButtonText: 'OK' })
await this.getPresaleInfo()
}
beginTraceOrderStatus(orderId: string) {
this.showOrderStatus = true
this.loadingInstance?.close()
this.orderTimer = setInterval(() => {
this.getOrderStatus(orderId)
}, 1000)
}
resetTmpOrderId() {
if (this.orderTimer !== null) {
clearInterval(this.orderTimer)
this.orderTimer = null
}
localStorage.removeItem('tmp_presale_order_id')
this.loadingInstance?.close()
}
async getOrderStatus(orderId: string) {
try {
const res: any = await queryOrder({ account: this.accountId, order_id: orderId })
if (res.state === 1) {
EventBus.$emit(PRESALE_SUCCESS, {})
} else if (res.state === 3) {
EventBus.$emit(PRESALE_ERROR, {})
this.resetTmpOrderId()
} else if (res.state === 0) {
EventBus.$emit(PRESALE_ERROR, {})
this.resetTmpOrderId()
}
} catch (err) {
console.log('query order status error', err)
}
}
beginCountdown() {
this.clearTimer()
this.timer = setInterval(() => {
if (this.countdown <= 0) {
this.clearTimer()
} else {
this.countdown--
}
}, 1000)
}
clearTimer() {
clearInterval(this.timer)
this.timer = null
}
async getPresaleInfo() {
const res: any = await queryPresaleStatus({ account: this.accountId })
if (res.presale_info) {
this.numberTotal = res.presale_info.total_num || 0
this.numberRest = this.numberTotal - (res.presale_info.sold_num || 0)
this.numberRest = this.numberRest < 0 ? 0 : this.numberRest
this.hint = res.presale_info.hint.replace(/\\n/g, '<br/>')
this.buyed = !!res.presale_info.buyed
this.presaleStatus = res.presale_info.state || 0
this.presaleTitle = res.presale_info.title
this.countdown = res.presale_info.countdown
if (this.presaleStatus === 1 && this.countdown > 0) {
this.beginCountdown()
}
AppModule.updatePresaleStat(this.presaleStatus)
const buySet: Set<string> = new Set()
if (res.presale_info.buyable_list) {
for (const sub of res.presale_info.buyable_list) {
buySet.add(sub.box_id)
}
}
AppModule.updateCanBuy(this.presaleStatus === 2 &&
this.numberRest > 0 && buySet.size > 0
)
for (const data of this.heroDatas) {
Vue.set(data, 'stopBuy', !buySet.has(data.id!))
}
}
}
async queryPresaleList() {
const reqData = {
account: '',
page: 0
}
const res: any = await searchBox(reqData)
this.heroDatas.length = 0
for (const data of res.rows) {
const heroData: any = {
name: data.name,
class: (data.job + '').toLowerCase(),
recordId: data.box_id,
id: data.box_id,
skelName: `n_${data.name.toLowerCase()}`,
directBuy: true,
stopBuy: false,
showBuy: true
}
if (data.currency_list && data.currency_list.length > 0) {
const priceData: any = data.currency_list[0]
heroData.discount = priceData.discount_rate
heroData.price = priceData.original_price
heroData.decimals = priceData.decimals || 8
heroData.priceDiscount = priceData.discount_price
heroData.currency = priceData.name
heroData.coinAddress = priceData.contract_address
}
this.heroDatas.push(heroData)
}
}
}
</script>
<style lang="scss" scoped>
#nft_section {
background-image: url('../../assets/main/p3/bg_p3.png');
position: relative;
background-repeat: no-repeat;
background-size: cover;
}
.info {
position: absolute;
top: 100px;
left: 0;
right: 0;
display: flex;
flex-direction: column;
align-items: center;
color: white;
.count-down {
display: flex;
flex-direction: row;
color: #F9D41D;
font-family: 'title',serif;
height: 50px;
align-items: end;
.time {
font-size: 50px;
}
.title{
font-size: 30px;
margin-bottom: 7px;
margin-left: 20px;
}
}
.text{
font-family: "zitic",serif;
font-size: 20px;
margin-top: 20px;
white-space: pre-wrap;
}
}
.count-label {
font-family: 'title',serif;
font-size: 50px;
color: #21f730;
position: absolute;
left:0;
right: 0;
bottom: 76px;
margin: auto;
width: 200px;
text-align: center;
}
@media (max-width: 767px) {
.title img{
width: 281px;
top: 5vh;
}
.card-scroller {
height: 70vh;
font-size: 11px;
}
.title .text{
font-size: 14px;
}
}
@media (max-width: 415px) {
.card-scroller {
height: 70vh;
font-size: 10px;
bottom: 30px;
}
.count-label {
bottom: 10px;
}
.info {
top: 40px;
padding: 0 8px;
.text{
font-size: 15px;
}
.count-down{
.time {
font-size: 30px;
}
.title {
font-size: 20px;
}
}
}
}
@media (min-width: 321px) and (max-width: 375px) {
.info {
top: 40px;
padding: 0 8px;
.text{
font-size: 12px;
margin-top: 1px;
}
.count-down{
.time {
font-size: 30px;
}
.title {
font-size: 20px;
}
}
}
}
.order-status {
transform: scale(0.3);
width: 220px;
height: 220px;
border-radius: 110px;
background: white;
display: flex;
justify-content: center;
align-items: center;
transform-origin: top;
position: fixed;
left: 100px;
top: 100px;
}
</style>