CounterFireGames/src/views/DetailView.vue
2024-07-25 17:58:02 +08:00

1056 lines
33 KiB
Vue

<template>
<div class="detail">
<div class="bg-content">
<div class="detail-bg"></div>
<div v-if="detailData" class="detail-content">
<div class="content">
<div class="top-left">
<div class="top-left-img">
<ImgCard :nftData="detailData.nft" />
</div>
</div>
<div class="top-right">
<h2 v-if="detailData.nft.type == 13">Founder's Tag</h2>
<h2 v-else>{{ detailData.nft.name }}</h2>
<div class="top-right-owner">
<div>Owner:</div>
<div class="address">{{ detailData.nft.owner_address ? detailData.nft.owner_address : myAddress }}</div>
</div>
<div class="top-right-price" v-if="detailData.event">
<li>
<div>Price</div>
<div class="time">
<img src="@/assets/img/marketplace/time.png" alt="img">
<div>Time remaining: &nbsp;<StarTimer :getAddress="detailData.event.data.end_at" /></div>
</div>
</li>
<li>
<div class="price">
<span class="bold">{{ price }}&nbsp;</span>
<img :src="icon" alt="ICON">
<span>( $ {{ usd }} )</span>
</div>
</li>
</div>
<div class="back-time" v-if="FOUNDER_ADDRESS.toLowerCase() == detailData.nft.owner_address.toLowerCase() || LOCKER_ADDRESS.toLowerCase() == detailData.nft.owner_address.toLowerCase()">
<div>
<img src="@/assets/img/marketplace/time.png" alt="">
<span>Time remaining : &nbsp;
{{ timeStaking(detailData.nft.last_lock_time)[0] }}d:
{{ timeStaking(detailData.nft.last_lock_time)[1] }}h:
{{ timeStaking(detailData.nft.last_lock_time)[2] }}m
</span>
</div>
<div>
<span class="expected" v-if="detailData.nft.type == 1 || detailData.nft.type == 12">+{{contribution(detailData.nft.detail.quality, detailData.nft.type)}}/d</span>
<span class="expected" v-if="detailData.nft.type == 13">+{{contribution(detailData.nft.quality, detailData.nft.type)}}/d</span>
<img src="@/assets/img/marketplace/totalContribution.png" alt="">
</div>
</div>
<div class="top-right-btns">
<div v-if="FOUNDER_ADDRESS.toLowerCase() == detailData.nft.owner_address.toLowerCase() || LOCKER_ADDRESS.toLowerCase() == detailData.nft.owner_address.toLowerCase()">
<div class="cancel" v-if="detailData.nft?.status == 1">
<span>Loading</span>
</div>
<div class="cancel" v-else>
<span @click="localWalletStore.token == '' ? cardLogin() : backStaking()">Unstake</span>
</div>
</div>
<div v-if="(detailData.nft.type == 1 && !detailData.nft.contract_address) && LOCKER_ADDRESS.toLowerCase() != detailData.nft.owner_address.toLowerCase()">
<div class="mint" @click="mintHero">Mint</div>
</div>
<!--
1、添加购物车
2、移除购物车
3、购买
4、
-->
<div v-if="myAddress != detailData.nft.owner_address">
<div v-if="detailData.event != null" class="buy" @click="localWalletStore.token == '' ? cardLogin() : buyNow()">Buy Now</div>
<div class="add" v-if="detailData.event != null && detailData.in_shopcart == 0" @click="localWalletStore.token == '' ? cardLogin() : addCart()">
<span>Add to cart </span>
<img src="@/assets/img/marketplace/Add_shopping_cart.png" alt="">
</div>
<div class="remove" v-if="detailData.in_shopcart == 1" @click="localWalletStore.token == '' ? cardLogin() : clearCart()">
<span>Remove from cart</span>
<img src="@/assets/img/marketplace/Move_out.png" alt="">
</div>
</div>
<!--
1、上架
2、下架
3、使用
-->
<div v-if="myAddress == detailData.nft.owner_address && detailData.nft?.status != 1">
<div class="sell" @click="beginSell" v-if="detailData.nft.on_sale == 0 && detailData.nft.type != 13">List</div>
<div class="cancel" @click="cancelSell" v-if="detailData.nft.on_sale == 1">Cancel listing</div>
<div class="redeem" v-if="detailData.nft.on_sale == 0 && (detailData.nft.type == 1 || detailData.nft.type == 12)" @click="lockToGame('convert')">
<span>Stake</span>
</div>
<div class="redeem" v-else-if="detailData.nft.type == 13">
<span v-if="detailData.nft?.status == 1" @click="lockToGame('founder')">Loading</span>
<span v-else @click="lockToGame('founder')">Stake</span>
</div>
<!-- <div class="redeem" @click="lockToGame('redeem')" v-if="detailData.nft.on_sale == 0 && detailData.nft.type == 11">Redeem</div> -->
</div>
<!-- <div v-else-if="detailData.nft?.status == 1">
<div class="cancel">
<span>Loading</span>
</div>
</div> -->
</div>
<div class="info">
<h2>Info</h2>
<li>
<div>Contract address</div>
<div class="contract-address">
<div>{{ detailData.nft.contract_address}}</div>
<span v-if="detailData.nft.type != 13"><a :href="contractBlankUrl" target="_blank">{{ sliceAddress(detailData.nft.contract_address) }}</a></span>
<span v-else><a :href="founderBlankUrl" target="_blank">{{ sliceAddress(detailData.nft.contract_address) }}</a></span>
</div>
</li>
<li>
<div>Token ID</div>
<div>
<a :href="contractTokenUrl" target="_blank">{{ detailData.nft.token_id ? detailData.nft.token_id : '-' }}</a>
</div>
</li>
<li>
<div>Blockchain</div>
<div>{{ detailData.nft.token_id ? 'lmmutascan' : '-' }}</div>
</li>
<li>
<div>Metadata</div>
<div v-if="detailData.nft.type != 13"><a :href="detailData.nft.meta_url" target="_blank">{{ sliceAddress(detailData.nft.meta_url) }}</a></div>
<div v-else><a :href="founderBlankUrl" target="_blank">{{ sliceAddress(detailData.nft.meta_url) }}</a></div>
</li>
<li>
<div>Royalties</div>
<div>{{ detailData.nft.token_id ? '2%' : '-' }}</div>
</li>
</div>
</div>
</div>
<div class="content">
<div v-if="detailData.nft.type == 1 || detailData.nft.type == 12" class="btm-left">
<h2>Property</h2>
<div class="btm-detail">
<li v-for="(item, val, index) in nftAbilities" :key="index">
<div v-show="val == 'quality'">
<h5>Tier</h5>
<p>{{ item }}</p>
</div>
<div v-show="val == 'max_mining_days'">
<h5>Active Days</h5>
<p>{{ item }}</p>
</div>
<div v-show="val == 'wealth'">
<h5>Wealth Value</h5>
<p>{{ parseInt(item) }}</p>
</div>
<div v-show="val == 'lucky'">
<h5>Luck Value</h5>
<p>{{ parseInt(item) }}</p>
</div>
<div v-show="val == 'hp'">
<h5>HP</h5>
<p>{{ parseInt(item) }}</p>
</div>
<div v-show="val == 'atk'">
<h5>Attack</h5>
<p>{{ parseInt(item) }}</p>
</div>
<div v-show="val == 'def'">
<h5>Defense</h5>
<p>{{ Number(item).toFixed(2) }}%</p>
</div>
<div v-show="val == 'block'">
<h5>Block Rate</h5>
<p>{{ Number(item).toFixed(2) }}%</p>
</div>
<div v-show="val == 'crit'">
<h5>Crit Rate</h5>
<p>{{ Number(item).toFixed(2) }}%</p>
</div>
</li>
</div>
</div>
<div v-else-if="detailData.nft.item_id == 10017 || detailData.nft.item_id == 10018" class="btm-left">
<h2>Property</h2>
<div class="gold-content">
<div class="left">
<h4>Gold Amount</h4>
<p>{{ detailData.nft.detail.gold_coins }}</p>
</div>
<div class="right">
<div class="right-img">
<img src="@/assets/img/marketplace/Icon_!.png" alt="">
</div>
<div class="right-tips">
A Gold Card is equivalent to 100,000 Gold. Once redeemed, 100,000 Gold will be credited to your Counter Fire game account.
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<Footer />
<BuyDialog :buyDialogVisible="buyDialogVisible" :buyDataArr="buyDataArr" @handleClose="BuyHandleClose" />
<SellDialog v-if="detailData" :sellDialogVisible="sellDialogVisible" :floorPrice="floorPrice" :sellDataArr="detailData.nft" @handleClose="sellHandleClose" />
<LoadingDialog :loadingDialogVisible="loadingDialogVisible" />
</div>
</template>
<script setup>
import { ref, toRefs, toRaw, onMounted , inject} from "vue"
const message = inject('$message')
import { useRouter, useRoute } from "vue-router";
import BuyDialog from "@/components/Dialogs/buyDialog.vue"
import ConfirmDialog from "@/components/Dialogs/confirmDialog.vue"
import LoadingDialog from "@/components/Dialogs/loadingDialog.vue"
import ImgCard from "@/components/common/imgCard.vue"
import StarTimer from "@/components/common/starTimer.vue"
import SellDialog from "@/components/Dialogs/sellDialog.vue"
import Footer from "@/components/common/footer.vue"
import { nftDetail, apiGetPrice, apiTxHash } from "@/utils/marketplace"
import {priceCalculated} from "@/configs/priceCalculate.js"
import { BlockChain } from "@/components/chain/BlockChain"
import {walletStore} from "@/store/wallet";
import { useMarketplaceStore } from "@/store/marketplace"
import LazyLoadImg from "@/components/lazyloadimg"
import {formatPrice} from "@/components/chain/utils"
import {createModal} from "@/utils/model.util";
import { contribution, timeStaking } from "@/configs/priceCalculate"
const router = useRouter();
const route = useRoute();
const localWalletStore = walletStore()
const marketplaceList = useMarketplaceStore()
const props = defineProps({
address: String,
tokenid: String
})
import placeholderImg from '@/assets/img/marketplace/GenesisHeroes_NFT.png'
const detailData = ref(null)
const assetsDetailData = ref(null)
const myAddress = localWalletStore.address
const contractBlankUrl = ref()
const founderBlankUrl = ref()
const contractTokenUrl = ref()
const nftAbilities = ref()
const icon = ref('')
const usd = ref('')
const price = ref('')
const loadingDialogVisible = ref(false)
const LOCKER_ADDRESS = ref(import.meta.env.VUE_APP_LOCKER_ADDRESS)
const FOUNDER_ADDRESS = ref(import.meta.env.VUE_APP_FOUNDER_ADDRESS)
// 登录
const cardLogin = async () => {
await new BlockChain().connect()
}
// 购买
const buyDialogVisible = ref(false)
const buyDataArr = ref([])
const buyNow = async () => {
detailData.value['icon'] = icon.value
detailData.value['tokenAmount'] = price.value
buyDataArr.value = []
buyDataArr.value.push(detailData.value)
buyDialogVisible.value = true
return
const buyResult = await createModal(BuyDialog, {
buyDataArr: buyDataArr.value,
}).show()
console.log(buyResult.errcode)
if(buyResult.errcode == 0) {
message.success('buy success')
} else if(buyResult.errcode == 1) {
if(buyResult.err.message.indexOf() > -1) {
message.error('User rejected the request')
} else {
message.error('buy fail')
}
}
}
const BuyHandleClose = () => {
buyDialogVisible.value = false
}
// 添加购物车
const addCart = async () => {
const data = {
net_id: import.meta.env.VUE_APP_NET_ID,
tokens: [
{
token_id: detailData.value.nft.token_id,
contract_address: detailData.value.nft.contract_address,
}
]
}
try {
const { errcode, errmsg } = await marketplaceList.addCartListState(data)
// console.log(errcode, errmsg)
if(errcode == 0) {
message.success('success! Add from cart')
marketplaceList.getCartList = await marketplaceList.getCartListState()
getDetail()
}
} catch (e) {
message.error('fail! Add from cart')
}
}
// 移除购物车
const clearCart = async () => {
// TODO:
const data = {
net_id: import.meta.env.VUE_APP_NET_ID,
tokens: [
{
token_id: detailData.value.nft.token_id,
contract_address: detailData.value.nft.contract_address,
}
]
}
try {
const { errcode, errmsg } = await marketplaceList.delCartListState(data)
if(errcode == 0) {
message.success('success! Remove from cart')
marketplaceList.getCartList = await marketplaceList.getCartListState()
getDetail()
}
} catch (e) {
// console.log(e)
}
}
// 下链使用
const lockToGame = async(type) => {
if(type == 'founder') {
const confirmResult = await createModal(ConfirmDialog, {
title: '',
message: `Are you sure you want to stake?`
}).show()
if (confirmResult.errcode == 0) {
loadingDialogVisible.value = true
unlockMainConfirm()
}
} else if(type == 'convert') {
const confirmResult = await createModal(ConfirmDialog, {
title: '',
message: 'Are you sure you want to stake?'
}).show()
if (confirmResult.errcode == 0) {
loadingDialogVisible.value = true
lockToGameConfirm()
}
}
}
const lockToGameConfirm = async () => {
try {
await new BlockChain().locker.lock(detailData.value.nft.contract_address, [detailData.value.nft.token_id])
// message.success('lockToGame success')
detailData.value.nft['status'] = 1
const confirmResult = await createModal(ConfirmDialog, {
title: '',
noBtnTitle: '',
message: 'Staking is complete. This process may take 3-4 minutes.'
}).show()
loadingDialogVisible.value = false
if(res) {
try {
const { confirmed } = await apiTxHash(detailData.value.nft.net_id, res)
let timer = setInterval( async () => {
// message.success('Unstake success.')
if(confirmed == 1) {
detailData.value.nft['status'] = ''
router.go(-1)
clearInterval(timer);
}
}, 2000);
} catch (e) {
console.log(e)
}
}
} catch (e) {
if(e.message.indexOf('rejected') > -1) {
message.error('User rejected the request')
} else {
message.error('lockToGame fail.')
}
loadingDialogVisible.value = false
}
}
const unlockMainConfirm = async () => {
try {
const bc = new BlockChain()
let res = await bc.locker.lockMain(detailData.value.nft.contract_address, [detailData.value.nft.token_id])
detailData.value.nft['status'] = 1
const confirmResult = await createModal(ConfirmDialog, {
title: '',
noBtnTitle: '',
message: 'Staking is complete. This process may take 3-4 minutes.'
}).show()
loadingDialogVisible.value = false
if(res) {
try {
const { confirmed } = await apiTxHash(detailData.value.nft.net_id, res)
let timer = setInterval( async () => {
// message.success('Unstake success.')
if(confirmed == 1) {
detailData.value.nft['status'] = ''
router.go(-1)
clearInterval(timer);
}
}, 2000);
} catch (e) {
console.log(e)
}
}
} catch (e) {
if(e.message.indexOf('rejected') > -1) {
message.error('User rejected the request')
} else {
message.error('lockToGame fail.')
}
loadingDialogVisible.value = false
}
}
// 赎回
const backStaking = async () => {
if(marketplaceList.userGold < 0) {
const confirmResult = await createModal(ConfirmDialog, {
title: '',
message: 'Unstake Failed - Negative in-game gold balance. Please adjust your balance to continue.'
}).show()
} else {
if(detailData.value.nft.type == 13) {
founderLock()
} else if(detailData.value.nft.type == 1 || detailData.value.nft.type == 12) {
heroLock()
}
}
}
const heroLock = async () => {
loadingDialogVisible.value = true
try {
const bc = new BlockChain()
let res = await bc.locker.unlockOrMintGameNft(detailData.value.nft.contract_address,[detailData.value.nft.token_id])
detailData.value.nft['status'] = 1
const confirmResult = await createModal(ConfirmDialog, {
title: '',
noBtnTitle: '',
message: 'Unstake success. It is expected to take 3-4 minutes. Please log in to the original wallet address to check later.'
}).show()
loadingDialogVisible.value = false
if(res) {
try {
const { confirmed } = await apiTxHash(detailData.value.nft.net_id, res)
let timer = setInterval( async () => {
// message.success('Unstake success.')
if( confirmed) {
detailData.value.nft['status'] = ''
router.go(-1)
clearInterval(timer);
}
}, 2000);
} catch (e) {
console.log(e)
}
}
} catch (e) {
loadingDialogVisible.value = false
if(e.message.indexOf('rejected') > -1) {
message.error('User rejected the request')
} else if (e.message.indexOf('select wallet') > -1) {
message.error('user cancel select wallet')
} else {
message.error('Unstake fail')
}
}
}
const founderLock = async () => {
loadingDialogVisible.value = true
try {
const bc = new BlockChain()
let res = await bc.locker.unlockMain(detailData.value.nft.contract_address,[detailData.value.nft.token_id])
detailData.value.nft['status'] = 1
const confirmResult = await createModal(ConfirmDialog, {
title: '',
noBtnTitle: '',
message: 'Unstake success. It is expected to take 3-4 minutes. Please log in to the original wallet address to check later.'
}).show()
loadingDialogVisible.value = false
if(res) {
try {
const { confirmed } = await apiTxHash(detailData.value.nft.net_id, res)
let timer = setInterval( async () => {
// message.success('Unstake success.')
if( confirmed) {
detailData.value.nft['status'] = ''
router.go(-1)
clearInterval(timer);
}
}, 2000);
} catch (e) {
console.log(e)
}
}
} catch (e) {
loadingDialogVisible.value = false
if(e.message.indexOf('rejected') > -1) {
message.error('User rejected the request')
} else if (e.message.indexOf('select wallet') > -1) {
message.error('user cancel select wallet')
} else if (e.message.indexOf('eoa address changed') > -1) {
const confirmResult = await createModal(ConfirmDialog, {
title: '',
noBtnTitle: '',
message: "It looks like there's an issue with your MetaMask or OKX wallet address. Please check your wallet address and try again."
}).show()
} else {
message.error('Unstake fail')
}
}
}
// 售卖弹窗
const floorPrice = ref('0')
const sellDialogVisible = ref(false)
const beginSell = async() => {
if (detailData.value.event) return
floorPrice.value = await getFloorPrice()
sellDialogVisible.value = true
}
// 取消售卖
const cancelSell = async() => {
// console.log('cancelSell', detailData.value.event)
if (!detailData.value.event) return
try {
let res = await new BlockChain().market.cancelOrder([detailData.value.event.data.id])
if(res.result) {
let timer = setTimeout(() => {
getDetail()
message.success('Your item has been unlisted.')
clearTimeout(timer)
}, 2000);
}
} catch (e) {
// try {
// let res = await new BlockChain().market.cancelOrdersOnChain([detailData.value.event.data.id])
// if(res.result) {
// let timer = setTimeout(() => {
// getDetail()
// clearTimeout(timer)
// }, 2000);
// }
// } catch (e2) {
// // console.log('cancelSell fail', e.message)
// }
if(e.message.indexOf('rejected') > -1) {
message.error('User rejected the request')
} else {
message.error('Your item has failed to be unlisted.')
}
}
}
// 处理地址
const sliceAddress = (address) => {
if (!address) return "-";
if (address.length >= 10) {
return `${address.substring(0, 6)}......${address.slice(-4)}`;
}
return address;
}
// 获取地板价
const getFloorPrice = async () => {
const data = {
net_id: detailData.value.nft.net_id,
contract_address: detailData.value.nft.contract_address,
item_id: detailData.value.nft.item_id,
quality: detailData.value.nft.detail.quality,
}
let res = await apiGetPrice(data)
if(res.lowest_price_goods) {
floorPrice.value = res.lowest_price_goods.event.data.buy[0].amount
} else {
floorPrice.value = '0'
}
return floorPrice.value
}
// 关闭出售弹窗
const sellHandleClose = (val) => {
if(val) {
let timer = setTimeout(() => {
getDetail()
clearTimeout(timer)
}, 2000);
sellDialogVisible.value = false
} else {
sellDialogVisible.value = false
}
}
// mint上链
const mintHero = async () => {
if(marketplaceList.userGold < 0) {
const confirmResult = await createModal(ConfirmDialog, {
title: '',
noBtnTitle: '',
message: 'Mint Failed - Negative in-game gold balance. Please adjust your balance to continue.'
}).show()
} else {
try {
const res = await new BlockChain().locker.mintNft([detailData.value.nft.uniid])
message.success('Mint success.')
router.go(-1)
} catch (e) {
if(e.message.indexOf('rejected') > -1) {
message.error('User rejected the request')
} else if (e.message.indexOf('select wallet') > -1) {
message.error('user cancel select wallet')
} else {
message.error('Mint Failed.')
}
}
}
}
const getDetail = async () => {
let { errcode, errmsg, data} = await nftDetail(props.address, props.tokenid)
console.log(data)
contractBlankUrl.value = `${import.meta.env.VUE_APP_EXPLORER_URL}/address/${data.nft.contract_address}`
contractTokenUrl.value = `${import.meta.env.VUE_APP_EXPLORER_URL}/token/${data.nft.contract_address}/instance/${data.nft.token_id}`
if (errcode) {
// console.log(errmsg)
//TODO:: 提示错误信息
return
}
const nftData = data.nft
nftData.event = data.event
nftAbilities.value = data.nft.detail
detailData.value = data
if (data.event?.data) {
const _data = formatPrice(data.event?.data)
icon.value = _data.icon
usd.value = _data.usd
price.value = _data.tokenAmount
}
}
onMounted(() => {
if(location.pathname.length > 8) {
getDetail()
} else {
detailData.value = marketplaceList.detailData
founderBlankUrl.value = `https://etherscan.io/token/0xec23679653337d4c6390d0eeba682246a6067777?a=${detailData.value.nft.token_id}`
nftAbilities.value = marketplaceList.detailData.nft.detail
}
})
</script>
<style lang="scss" scoped>
.detail {
width: 100%;
box-sizing: border-box;
background: #000;
.bg-content {
width: 100%;
height: 100%;
background: url('@/assets/img/marketplace/BG01.png') no-repeat;
background-size: 100% 100%;
}
.detail-bg {
width: 100%;
height: 84px;
}
.detail-content {
width: 100%;
height: 100%;
.content {
width: 1266px;
margin: 0 auto;
padding-top: 76px;
display: flex;
justify-content: space-between;
color: #fff;
.top-left {
width: 453px;
.top-left-img {
height: 678px;
:deep(.card-img-common) {
.img-top {
width: 120px;
height: 35px;
top: 20px;
left: 20px;
font-size: 18px;
}
.img-btm {
bottom: 35px;
left: 65px;
>div {
width: 120px;
height: 43px;
font-size: 18px;
}
div:nth-child(2) {
margin-left: 15px;
}
}
}
}
}
.top-right {
width: 719px;
h2 {
font-size: 42px;
font-family: 'Poppins';
font-weight: bold;
}
.top-right-owner {
display: flex;
font-family: 'Poppins';
font-weight: bold;
font-size: 30px;
.address {
margin-left: 20px;
}
}
.top-right-price {
width: 719px;
height: 133px;
display: flex;
flex-direction: column;
justify-content: space-between;
background: #2d2738;
border-radius: 20px;
margin-top: 10px;
color: #BB7FFF;
padding: 15px 20px;
box-sizing: border-box;
li {
display: flex;
justify-content: space-between;
>div {
display: flex;
font-size: 28px;
color: #9950FD;
}
.time {
display: flex;
align-items: center;
justify-content: end;
font-size: 18px;
img {
width: 25px;
height: 28px;
margin-right: 5px;
}
div {
display: flex;
align-items: center;
}
}
.price {
display: flex;
align-items: center;
span {
font-family: 'Poppins';
font-weight: 400;
font-size: 24px;
color: #BB7FFF;
}
.bold {
font-weight: bold;
font-size: 48px;
}
img {
width: 30px;
height: 30px;
margin-right: 20px;
}
}
}
}
.back-time {
// width: 340px;
height: 40px;
line-height: 40px;
border-radius: 20px;
font-family: 'Poppins';
font-weight: 300;
font-size: 18px;
display: flex;
align-items: center;
div {
display: flex;
align-items: center;
background: #272130;
padding: 0 24px;
border-radius: 20px;
img {
width: 20px;
height: 25px;
margin-right: 6px;
// margin-left: 24px;
}
span {
display: inline-block;
height: 40px;
line-height: 40px;
color: #9A50FF;
}
&:nth-child(2) {
margin-left: 20px;
span {
font-family: 'Poppins';
font-weight: 600;
font-size: 20px;
color: #F3F0FF;
}
.expected {
color: #00DEFF;
}
img {
width: 25px;
margin-left: 10px;
}
}
}
}
.top-right-btns {
div {
display: flex;
justify-content: space-between;
align-items: center;
margin: 10px 0;
>div {
width: 348px;
height: 57px;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Poppins';
font-weight: bold;
font-size: 28px;
color: #FFFFFF;
border-radius: 12px;
cursor: pointer;
}
.buy {
background: #6336d7;
}
.add {
background: #1778f1;
span {
height: 100%;
line-height: 57px;
border-radius: 12px;
text-align: center;
display: inline-block;
background: #1778f1;
}
img {
width: 43px;
height: 44px;
margin-left: 20px;
}
}
.remove {
background: #1778f1;
span {
height: 100%;
line-height: 57px;
border-radius: 12px;
text-align: center;
display: inline-block;
}
img {
width: 43px;
height: 44px;
}
}
.sell {
width: 348px;
background: #6336d7;
}
.cancel {
width: 348px;
background: #ff6271;
}
.redeem {
width: 348px;
background: #1778f1;
}
.mint {
background: #6336d7;
}
}
}
.info {
color: #B3B5DA;
margin-top: 40px;
h2 {
font-family: 'Poppins';
font-weight: bold;
font-size: 30px;
color: #fff;
border-bottom: 2px solid #3A3B57;
}
li {
display: flex;
justify-content: space-between;
margin-top: 10px;
div {
width: 300px;
font-size: 22px;
text-align: left;
margin-top: 10px;
a {
color: #B3B5DA;
cursor: pointer;
}
}
.contract-address {
position: relative;
cursor: pointer;
>div {
width: 560px;
height: 40px;
border-radius: 10px;
line-height: 40px;
border: 1px solid #7e7686;
position: absolute;
top: -50px;
left: -50%;
text-align: center;
background: #282131;
color: #fff;
display: none;
}
&:hover {
>div {
display: block;
}
}
}
}
}
}
.btm-left {
padding-bottom: 90px;
h2 {
font-size: 30px;
font-family: 'Poppins';
font-weight: bold;
margin-bottom: 10px;
}
.btm-detail {
display: flex;
flex-wrap: wrap;
clear: both;
width: 489px;
li {
width: 154px;
height: 84px;
background: #2d2738;
border-radius: 20px;
margin-right: 9px;
margin-bottom: 10px;
div {
text-align: center;
font-size: 22px;
color: #B3B5DA;
font-family: "Poppins";
h5 {
font-weight: bold;
}
p {
font-weight: 400;
color: #fff;
}
}
}
}
.gold-content {
width: 455px;
height: 84px;
background: #2D2738;
border-radius: 20px;
display: flex;
justify-content: space-between;
.left {
padding: 10px 19px;
font-family: 'Poppins';
font-size: 22px;
h4 {
height: 18px;
font-weight: 400;
color: #B3B5DA;
}
p {
height: 20px;
font-weight: bold;
color: #FFFFFF;
margin-top: 18px;
}
}
.right {
position: relative;
cursor: pointer;
margin-top: 9px;
margin-right: 10px;
.right-img {
width: 34px;
height: 33px;
img {
width: 100%;
height: 100%;
}
}
.right-tips {
display: none;
position: absolute;
top: 45px;
left: -20px;
width: 313px;
height: 82px;
padding: 20px;
box-sizing: border-box;
background: #1A1821;
box-shadow: 0px 15px 28px 3px rgba(22,22,22,0.13);
border-radius: 10px;
border: 1px solid #B966FF;
}
&:hover {
.right-tips {
display: block;
}
}
}
}
}
&:nth-child(2) {
padding-top: 20px;
}
}
}
}
</style>