This commit is contained in:
huangjinming 2023-04-26 16:10:34 +08:00
parent 740e3694b4
commit c2a22d32ca
12 changed files with 4606 additions and 2784 deletions

451
README.md
View File

@ -1,3 +1,454 @@
运行代码指令 运行代码指令
npm run dev npm run dev
yarn run dev yarn run dev
const ERC20ABI = [
{
inputs: [
{
internalType: "address",
name: "_nftTarget",
type: "address"
},
{
internalType: "address[]",
name: "_manageAddress",
type: "address[]"
}
],
stateMutability: "nonpayable",
type: "constructor"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "user",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "nft",
type: "address"
},
{
indexed: false,
internalType: "uint256[]",
name: "nftSIds",
type: "uint256[]"
},
{
indexed: false,
internalType: "uint256[]",
name: "nftTIds",
type: "uint256[]"
}
],
name: "Minted",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "role",
type: "bytes32"
},
{
indexed: true,
internalType: "bytes32",
name: "previousAdminRole",
type: "bytes32"
},
{
indexed: true,
internalType: "bytes32",
name: "newAdminRole",
type: "bytes32"
}
],
name: "RoleAdminChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "role",
type: "bytes32"
},
{
indexed: true,
internalType: "address",
name: "account",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "sender",
type: "address"
}
],
name: "RoleGranted",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "role",
type: "bytes32"
},
{
indexed: true,
internalType: "address",
name: "account",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "sender",
type: "address"
}
],
name: "RoleRevoked",
type: "event"
},
{
inputs: [],
name: "DEFAULT_ADMIN_ROLE",
outputs: [
{
internalType: "bytes32",
name: "",
type: "bytes32"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [],
name: "MANAGE_ROLE",
outputs: [
{
internalType: "bytes32",
name: "",
type: "bytes32"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [
{
internalType: "bytes32",
name: "role",
type: "bytes32"
}
],
name: "getRoleAdmin",
outputs: [
{
internalType: "bytes32",
name: "",
type: "bytes32"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [
{
internalType: "bytes32",
name: "role",
type: "bytes32"
},
{
internalType: "uint256",
name: "index",
type: "uint256"
}
],
name: "getRoleMember",
outputs: [
{
internalType: "address",
name: "",
type: "address"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [
{
internalType: "bytes32",
name: "role",
type: "bytes32"
}
],
name: "getRoleMemberCount",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [
{
internalType: "bytes32",
name: "role",
type: "bytes32"
},
{
internalType: "address",
name: "account",
type: "address"
}
],
name: "grantRole",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "role",
type: "bytes32"
},
{
internalType: "address",
name: "account",
type: "address"
}
],
name: "hasRole",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [],
name: "nft",
outputs: [
{
internalType: "contract IBEERC721",
name: "",
type: "address"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
name: "nftMinted",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [
{
internalType: "address",
name: "",
type: "address"
},
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
name: "ownerToNFTs",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [
{
internalType: "bytes32",
name: "role",
type: "bytes32"
},
{
internalType: "address",
name: "account",
type: "address"
}
],
name: "renounceRole",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "role",
type: "bytes32"
},
{
internalType: "address",
name: "account",
type: "address"
}
],
name: "revokeRole",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes4",
name: "interfaceId",
type: "bytes4"
}
],
name: "supportsInterface",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [
{
internalType: "address",
name: "_user",
type: "address"
},
{
internalType: "uint256",
name: "count",
type: "uint256"
}
],
name: "mintToUser",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "_user",
type: "address"
},
{
internalType: "uint256[]",
name: "_nftIds",
type: "uint256[]"
}
],
name: "addNFTData",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "_user",
type: "address"
}
],
name: "getMintableCount",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [
{
internalType: "address",
name: "_user",
type: "address"
}
],
name: "getMintableNftIds",
outputs: [
{
internalType: "uint256[]",
name: "",
type: "uint256[]"
}
],
stateMutability: "view",
type: "function",
constant: true
}
]

View File

@ -1,3 +1,4 @@
import request from "@/utils/request";
/** /**
* begin claim badge * begin claim badge
* @param {*} data: {id: string} * @param {*} data: {id: string}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -11,6 +11,7 @@ import {
ACTIVATE_PROXY_ABI, ACTIVATE_PROXY_ABI,
MYSTERY_BOX_ABI, MYSTERY_BOX_ABI,
MYSTERY_PROXY_ABI, MYSTERY_PROXY_ABI,
MEDAL_REWARDS_ABI,
} from "@/configs/contracts"; } from "@/configs/contracts";
const AppModule = useAppStore(pinia); const AppModule = useAppStore(pinia);
@ -116,6 +117,7 @@ export default class ChainManager {
return balance; return balance;
} }
/** /**
* get amount of mystery boxes * get amount of mystery boxes
* this method can get amount of general erc721 also * this method can get amount of general erc721 also

View File

@ -6,9 +6,10 @@ import { useUserStore } from "@/store/user";
import { isMobile } from "@/utils/resize"; import { isMobile } from "@/utils/resize";
import { hasMetamask, toHexChainId } from "@/utils/chain.util"; import { hasMetamask, toHexChainId } from "@/utils/chain.util";
import { AllChains } from "@/configs/allchain"; import { AllChains } from "@/configs/allchain";
import { ERC20ABI } from "@/configs/contracts"; import { ERC20ABI, LIMIT_ABI, MEDAL_REWARDS_ABI } from "@/configs/contracts";
import { TransactionReceipt } from "web3-core"; import { TransactionReceipt } from "web3-core";
import { useEventBus } from "@vueuse/core"; import { useEventBus } from "@vueuse/core";
import { Global } from "@/configs/global";
import * as bus_event from "@/bus/event"; import * as bus_event from "@/bus/event";
import pinia from "@/store"; import pinia from "@/store";
@ -95,6 +96,7 @@ export class Blockchain {
return; return;
} }
this.web3 = new Web3(this.provider); this.web3 = new Web3(this.provider);
const chainId = await this.web3.eth.getChainId(); const chainId = await this.web3.eth.getChainId();
await this.checkChain(chainId); await this.checkChain(chainId);
@ -126,7 +128,7 @@ export class Blockchain {
* @private * @private
*/ */
private async checkChain(chainId: number) { private async checkChain(chainId: number) {
if (!this.chainMap.has(chainId)) { if (chainId !== AVAILABLE_CHAINS[0]) {
// if (this.walletType === 1) { // if (this.walletType === 1) {
try { try {
await this.selectChain(); await this.selectChain();
@ -195,19 +197,12 @@ export class Blockchain {
*/ */
private selectChain(): Promise<number> { private selectChain(): Promise<number> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
busNeedChangeChain.emit({ this.switchEthereumChain(AVAILABLE_CHAINS[0], (res: any)=>{
confirm: async (id: number) => { if (res.err) {
console.log("select chain: ", id); reject && reject(res.err)
this.currentChain = id; return
if (this.provider) {
await this.switchEthereumChain();
} }
resolve && resolve(id); resolve && resolve(res.chain)
},
cancel: (reason: any) => {
console.log("cancel select chain: ", reason);
reject && reject(reason);
},
}); });
}); });
} }
@ -256,6 +251,11 @@ export class Blockchain {
} }
public async getContractInstance(address: string, abi: any = ERC20ABI) { public async getContractInstance(address: string, abi: any = ERC20ABI) {
if (!this.web3) {
throw new Error(
"Web3 instance is not initialized. Please call `initWeb3` method after user login."
);
}
if (!this.instanceCacheMap.has(address)) { if (!this.instanceCacheMap.has(address)) {
const instance = new this.web3.eth.Contract(abi, address, { const instance = new this.web3.eth.Contract(abi, address, {
from: AppModule.accountId, from: AppModule.accountId,
@ -344,24 +344,15 @@ export class Blockchain {
* @param {() => void} cb * @param {() => void} cb
* @return {Promise<void>} * @return {Promise<void>}
*/ */
async switchEthereumChain(chainId?: number, cb?: () => void) { async switchEthereumChain(chainId?: number, cb?: (res: any) => void) {
chainId = chainId || this.currentChain; chainId = chainId || AVAILABLE_CHAINS[0];
const hexChainId = toHexChainId(chainId); const hexChainId = toHexChainId(chainId);
const onChainChange = (chainId: string) => { const onChainChange = (chainId: string) => {
console.log("switchEthereumChain: ", chainId); console.log("switchEthereumChain: ", chainId);
this.provider.removeListener("chainChanged", onChainChange); this.provider.removeListener("chainChanged", onChainChange);
cb && cb(); cb && cb({chain: chainId});
}; };
this.provider.on("chainChanged", onChainChange); this.provider.on("chainChanged", onChainChange);
try {
await this.provider.request({
method: "wallet_switchEthereumChain",
params: [{ chainId: hexChainId }],
});
console.log("switch chain success");
} catch (e: any) {
console.log("switch chain error: ", e);
if (e.code === 4902 || e.message.indexOf("Unrecognized chain ID") >= 0) {
try { try {
const data = this.chainMap.get(chainId)!; const data = this.chainMap.get(chainId)!;
await this.provider.request({ await this.provider.request({
@ -384,9 +375,7 @@ export class Blockchain {
} catch (addError) { } catch (addError) {
console.error("add chain error: ", addError); console.error("add chain error: ", addError);
this.provider.removeListener("chainChanged", onChainChange); this.provider.removeListener("chainChanged", onChainChange);
} cb && cb({err: addError})
}
// console.error(e)
} }
} }
@ -455,6 +444,38 @@ export class Blockchain {
console.log("increaseAllowance: ", res); console.log("increaseAllowance: ", res);
} }
public async totalSupply(address: string) {
const coinInstance: any = await this.getContractInstance(
Global.LIMIT_ABI_Address,
LIMIT_ABI
);
const res = await coinInstance.methods.totalSupply().call({ gas: 1000000 });
console.log("totalSupply: ", res);
return res
}
// 获取当前用户可mint的数量
public async getMintableCount(address?: string) {
// address = address || //TODO: current address
const coinInstance: any = await this.getContractInstance(
Global.MEDAL_REWARDS_Address,
MEDAL_REWARDS_ABI
);
const res = await coinInstance.methods
.getMintableCount(AppModule.accountId)
.call({ gas: 1000000 });
console.log("getMintableCount: ", res);
return res
}
// 获取当前用户的mint总数
public async supplyLimit(address: string) {
const coinInstance: any = await this.getContractInstance(
Global.LIMIT_ABI_Address,
LIMIT_ABI
);
const res = await coinInstance.methods.supplyLimit().call({ gas: 1000000 });
console.log("supplyLimit: ", res);
return res
}
/** /**
* @param {string} address * @param {string} address
* @param {string | null} account * @param {string | null} account

View File

@ -47,7 +47,7 @@ import { useCopyToClipboard } from "./../../hooks/useCopyToClipboard";
const AppModule = useAppStore(); const AppModule = useAppStore();
const chain = useChainStore(); const chain = useChainStore();
const app = useAppStore(); const app = useAppStore();
const emit = defineEmits(['login-success'])
const { copied, copyToClipboard } = useCopyToClipboard(AppModule.accountId); const { copied, copyToClipboard } = useCopyToClipboard(AppModule.accountId);
const message = copied.value const message = copied.value
@ -64,14 +64,16 @@ const formatAddress = computed(() => {
async function login(event) { async function login(event) {
if (!chain.logined) { if (!chain.logined) {
await chain.chainManager.login(); await chain.chainManager.login();
chain.logined = chain.chainManager.isLogined; chain.logined = chain.chainManager.isLogined;
emit('login-success');
} }
} }
const logout = async () => { const logout = async () => {
await chain.chainManager.logout(); await chain.chainManager.logout();
chain.logined = chain.chainManager.isLogined; chain.logined = chain.chainManager.isLogined;
}; };
</script> </script>

View File

@ -15,7 +15,7 @@ export const ALL_PROVIDERS = [
}, },
]; ];
export const AVAILABLE_CHAINS = env === "production" ? [321] : [322]; export const AVAILABLE_CHAINS = env === "production" ? [137] : [80001];
export const OFFICE_ACCOUNT = export const OFFICE_ACCOUNT =
env === "production" env === "production"

File diff suppressed because it is too large Load Diff

4
src/configs/global.ts Normal file
View File

@ -0,0 +1,4 @@
export namespace Global {
export const MEDAL_REWARDS_Address: string = "0xC0DA6D9d8AaDCFf7BD6d69B8b46948dD40BF0dec"; // 分发地址
export const LIMIT_ABI_Address: string = "0x5b36329D0DA1F56eD60F3C5DE1855c8dE0440140"; // 徽章地址
}

View File

@ -67,7 +67,7 @@ export const useUserStore = defineStore("user", () => {
if (!res.errcode && res.data?.token) { if (!res.errcode && res.data?.token) {
accountId.value = account; accountId.value = account;
setToken(res.data.token); setToken(res.data.token);
glodata.token = getToken();
setAccountId(account); setAccountId(account);
AppModule.updateAccount(account); AppModule.updateAccount(account);
AppModule.updateToken(res.data.token); AppModule.updateToken(res.data.token);
@ -83,6 +83,9 @@ export const useUserStore = defineStore("user", () => {
removeToken(); removeToken();
token.value = ""; token.value = "";
accountId.value = ""; accountId.value = "";
glodata.token = '';
glodata.accountId=''
AppModule.updateToken("");
AppModule.updateAccount(""); AppModule.updateAccount("");
} }

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="my-container"> <div class="my-container">
<NavBar></NavBar> <NavBar @login-success="fetchData"></NavBar>
<div class="content-top"> <div class="content-top">
<div class="text-left"> <div class="text-left">
<img src="../assets/img/badge/left-text.png" alt="" /> <img src="../assets/img/badge/left-text.png" alt="" />
@ -20,7 +20,7 @@
<div class="card shadow-md rounded cards-container"> <div class="card shadow-md rounded cards-container">
<div></div> <div></div>
<div <div
class="card-title text-xl mb-8 text-yellow-color font-poppins-semiBoldItalic cards-container" class="card-title text-xl text-yellow-color font-poppins-semiBoldItalic cards-container"
> >
<div>GENESIS</div> <div>GENESIS</div>
</div> </div>
@ -41,7 +41,7 @@
</div> </div>
</div> </div>
<div class="genesicard"> <div class="genesicard">
<div class="genesis-title pt-24 font-firaSans-blackItalic"> <div class="genesis-title font-firaSans-blackItalic">
Genesis NFT <br /> Genesis NFT <br />
holder Only holder Only
</div> </div>
@ -61,20 +61,29 @@
</div> </div>
<div class="ml-3">December 1 (st), 2022</div> <div class="ml-3">December 1 (st), 2022</div>
</div> </div>
<div class="december-number">2000 / 4000</div> <div class="december-number">
{{ totalSupply || "0" }} / {{ supplyLimit || "0" }}
</div>
<div <div
:class="isbtn === true ? 'disabled-claim' : 'claim'" :class="mintableCount == 0 ? 'disabled-claim' : 'claim'"
@click="handClaimBadge" @click="handClaimBadge"
> >
<div class="claim-item">Claim your Airdorp</div> <div class="claim-item">Claim your Airdorp</div>
</div> </div>
<div class="dis-msg"> <div class="dis-msg">
<div class="msg">Youre not eligible for this stage.<img class="rabbit" src="../assets/img/badge/rabbit.png" alt=""></div> <div class="msg">
{{ message
}}<img
class="rabbit"
src="../assets/img/badge/rabbit.png"
alt=""
/>
</div>
</div> </div>
</div> </div>
<div class="card shadow-md rounded cards-container"> <div class="card shadow-md rounded cards-container">
<div <div
class="text-xl font-bold font-poppins-semiBoldItalic card-title-right mb-4 p-3 text-black-color-300 cards-container" class="text-xl font-bold font-poppins-semiBoldItalic card-title-right text-black-color-300 cards-container"
> >
EXPLORER EXPLORER
</div> </div>
@ -91,7 +100,7 @@
</div> </div>
</div> </div>
<div class="timeline"> <div class="timeline">
<div class="timeline-boder flex justify-center pt-12"> <div class="timeline-boder flex justify-center pt-6">
<img src="../assets/img/badge/timeline.png" alt="" /> <img src="../assets/img/badge/timeline.png" alt="" />
</div> </div>
<div class="pt-2"> <div class="pt-2">
@ -103,6 +112,7 @@
<li>Beta Test I</li> <li>Beta Test I</li>
<li>Beta Test II</li> <li>Beta Test II</li>
</ul> </ul>
<div class="coming">Coming Soon</div>
</div> </div>
</div> </div>
</div> </div>
@ -127,19 +137,25 @@ import {
import { useAppStore } from "@/store/app"; import { useAppStore } from "@/store/app";
import { useRouter, useRoute } from "vue-router"; import { useRouter, useRoute } from "vue-router";
import { useMouse, useRafFn } from "@vueuse/core"; import { useMouse, useRafFn } from "@vueuse/core";
import { beginClaim, checkClaimStatus } from "../api/badge";
import { useMouseRotation } from "../hooks/useMouseRotation"; import { useMouseRotation } from "../hooks/useMouseRotation";
import NavBar from "@/components/layout/NavBar.vue"; import NavBar from "@/components/layout/NavBar.vue";
import ImageTextModal from "../components/global/ImageTextModal.vue"; import ImageTextModal from "../components/global/ImageTextModal.vue";
import { useChainStore } from "@/store/chain";
const AppModule = useAppStore();
const scrollOffset = ref("0%"); const scrollOffset = ref("0%");
const parallaxFactor = 0.135; // const parallaxFactor = 0.135; //
const visible = ref(false); const visible = ref(false);
const isbtn = ref(false); const canClaim = ref(false);
const chain = useChainStore();
const totalSupply = ref(0);
const mintableCount = ref(0);
const supplyLimit = ref(0);
const message = ref("");
const handlHero = () => { const handlHero = () => {
visible.value = true; visible.value = true;
}; };
const handleScroll = () => { const handleScroll = () => {
const currentScrollTop = const currentScrollTop =
window.pageYOffset || document.documentElement.scrollTop; window.pageYOffset || document.documentElement.scrollTop;
@ -165,8 +181,62 @@ const {
stopTracking: stopTracking2, stopTracking: stopTracking2,
} = useMouseRotation(); } = useMouseRotation();
const handClaimBadge = () => { const handClaimBadge = async () => {
isbtn.value = true; if (mintableCount.value == 0) {
return false;
}
try {
if (!chain.logined) {
await chain.chainManager.login();
chain.logined = chain.chainManager.isLogined;
}
} catch (error) {
message.error("登录失败,请稍后重试");
console.error(error);
return;
}
try {
// beginClaim API
const res = await beginClaim(1);
// message.loading("...", 0);
console.log("领取失败,请稍后重试!", res.data.taskId);
// claim
let status = 0;
let timer;
while (status !== 9 && status !== 10) {
await new Promise((resolve) => {
timer = setTimeout(resolve, 5000); // 5
});
const resStatus = await checkClaimStatus(res.data.taskId);
status = resStatus.data.status;
console.log("status", resStatus.data.status);
}
// message.destroy(); // loading message
if (status === 9) {
console.log("领取成功!");
message.value = "claim success";
clearTimeout(timer); //
// message.success("");
} else if (status === 10) {
console.log("领取失败,请稍后重试!");
message.value = "claim failure";
clearTimeout(timer); //
// message.error("");
}
if (status === 0) {
message.value = "claiming !";
}
} catch (error) {
// message.destroy();
// message.error("");
console.log("领取过程中发生错误,请稍后重试!");
// clearTimeout(timer); //
console.error(error);
}
}; };
const bgTranslateX = ref(0); const bgTranslateX = ref(0);
@ -178,12 +248,34 @@ const handleScrolls = () => {
bgTranslateX.value -= scrollDelta / 3; // bgTranslateX.value -= scrollDelta / 3; //
scrollPos.value = newScrollPos; scrollPos.value = newScrollPos;
}; };
const fetchData = async () => {
try {
//
totalSupply.value = await chain.chainManager.bc.totalSupply();
onMounted(() => { //mint
mintableCount.value = await chain.chainManager.bc.getMintableCount();
mintableCount.value == 0
? (message.value = "Youre not eligible for this stage.")
: "";
//mint
supplyLimit.value = await chain.chainManager.bc.supplyLimit();
} catch (error) {
console.error("Error fetching data:", error);
}
console.log("totalSupply.value:", totalSupply.value);
console.log("mintableCount.value:", mintableCount.value);
console.log("supplyLimit.value:", supplyLimit.value);
};
//
onMounted(async () => {
if (AppModule.accountId) {
await fetchData();
}
window.addEventListener("scroll", handleScroll); window.addEventListener("scroll", handleScroll);
window.addEventListener("scroll", handleScrolls); window.addEventListener("scroll", handleScrolls);
}); });
onUnmounted(() => { onUnmounted(() => {
window.removeEventListener("scroll", handleScroll); window.removeEventListener("scroll", handleScroll);
window.removeEventListener("scroll", handleScrolls); window.removeEventListener("scroll", handleScrolls);
@ -207,7 +299,7 @@ onUnmounted(() => {
.star-bg { .star-bg {
position: absolute; position: absolute;
left: 30px; left: 30px;
top:30px; top: 30px;
bottom: 0; bottom: 0;
right: 20px; right: 20px;
z-index: -1; z-index: -1;
@ -220,7 +312,7 @@ onUnmounted(() => {
.bg { .bg {
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: -10px;
bottom: 0; bottom: 0;
right: 0; right: 0;
z-index: -2; z-index: -2;
@ -233,7 +325,7 @@ onUnmounted(() => {
.text-left { .text-left {
position: absolute; position: absolute;
left: 85px; left: 85px;
bottom: 30px; bottom: 10px;
animation: slide-up 1s ease-out; animation: slide-up 1s ease-out;
} }
.text-right { .text-right {
@ -250,8 +342,8 @@ onUnmounted(() => {
font-weight: 500; font-weight: 500;
color: #ffb900; color: #ffb900;
} }
.timeline{ .timeline {
width:1188px; width: 1188px;
margin-left: 5px; margin-left: 5px;
height: 38px; height: 38px;
} }
@ -263,16 +355,32 @@ onUnmounted(() => {
} }
.card-title { .card-title {
font-size: 80px; font-size: 80px;
margin-bottom: 19px;
// font-family: "Poppins"; // font-family: "Poppins";
font-weight: normal; font-weight: normal;
color: #ffb900; color: #ffb900;
position: relative;
line-height: 60px; line-height: 60px;
background: linear-gradient(180deg, #f9fe1d 0%, #ffbb17 100%); background: linear-gradient(180deg, #f9fe1d 0%, #ffbb17 100%);
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
} }
.card-title::after {
content: "";
display: inline-block;
position: absolute;
right: 125px;
bottom: 3px;
width: 5px;
height: 5px;
background: #fcde1a;
margin-left: 5px;
vertical-align: middle;
}
.card-title-right { .card-title-right {
font-size: 40px; font-size: 40px;
margin-top: 16px;
text-align: center;
// font-family: "Fira Sans"; // font-family: "Fira Sans";
font-weight: normal; font-weight: normal;
} }
@ -307,7 +415,7 @@ p {
text-align: center; text-align: center;
line-height: 28px; line-height: 28px;
&:hover { &:hover {
background: url("../assets/img/badge/more.png"); background: url("../assets/img/badge/more-hover.png");
} }
} }
.btn-more-right { .btn-more-right {
@ -324,6 +432,15 @@ p {
color: #a7a7a7; color: #a7a7a7;
text-align: center; text-align: center;
line-height: 28px; line-height: 28px;
.coming {
position: absolute;
top: -90px;
left: 20px;
font-size: 16px;
font-family: "Poppins-Regular";
font-weight: 400;
color: #a7a7a7;
}
} }
.btn-more-right:hover .dropdown-menu { .btn-more-right:hover .dropdown-menu {
display: block; display: block;
@ -336,6 +453,7 @@ p {
left: 0; left: 0;
// background-color: white; // background-color: white;
background: url("../assets/img/badge/dropdown-menu-bg.png") no-repeat; background: url("../assets/img/badge/dropdown-menu-bg.png") no-repeat;
background-size: 100% 100%;
min-width: 145px; min-width: 145px;
// box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2); // box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
list-style-type: none; list-style-type: none;
@ -345,12 +463,20 @@ p {
} }
.dropdown-menu li { .dropdown-menu li {
padding: 4px 10px; padding: 2px 10px;
}
.dropdown-menu li::after {
content: "";
display: block;
border-bottom: 1px solid #838383;
margin-top: -1px; /* 调整下划线与文字之间的间距 */
} }
.dropdown-menu li:hover { .dropdown-menu li:hover {
// background-color: #f1f1f1; // background-color: #f1f1f1;
} }
.dropdown-menu li:last-child::after {
border-bottom: none;
}
.card-top { .card-top {
width: 450px; width: 450px;
height: 450px; height: 450px;
@ -443,6 +569,7 @@ p {
.genesis-title { .genesis-title {
font-size: 40px; font-size: 40px;
letter-spacing: 3px; letter-spacing: 3px;
margin-top: 75px;
// font-family: "Fira Sans"; // font-family: "Fira Sans";
font-weight: normal; font-weight: normal;
text-transform: uppercase; text-transform: uppercase;
@ -452,7 +579,7 @@ p {
position: relative; /* 添加相对定位以便伪元素定位 */ position: relative; /* 添加相对定位以便伪元素定位 */
width: 302px; width: 302px;
margin-left: 39px; margin-left: 39px;
margin-right: 167px; margin-right: 162px;
} }
.genesicard::before { .genesicard::before {
@ -489,7 +616,7 @@ p {
font-family: Arial; font-family: Arial;
font-weight: 400; font-weight: 400;
color: #ffffff; color: #ffffff;
margin-bottom: 30px; margin-bottom: 26px;
// line-height: 60px; // line-height: 60px;
} }
.disabled-claim { .disabled-claim {
@ -589,7 +716,6 @@ p {
height: 50px; height: 50px;
margin-top: 41px; margin-top: 41px;
line-height: 50px; line-height: 50px;
font-size: 12px; font-size: 12px;
font-family: "Poppins"; font-family: "Poppins";
font-weight: 400; font-weight: 400;
@ -597,12 +723,12 @@ p {
background: rgba(255, 255, 255, 0.04); background: rgba(255, 255, 255, 0.04);
border: 1px solid #353535; border: 1px solid #353535;
border-radius: 12px 0px 12px 0px; border-radius: 12px 0px 12px 0px;
.msg{ .msg {
margin-left: 21px; margin-left: 21px;
position: relative; position: relative;
.rabbit{ .rabbit {
position: absolute; position: absolute;
right: 0; right: -6px;
top: -24px; top: -24px;
} }
} }

View File

@ -1,55 +1,57 @@
<!-- MedalApp.vue -->
<template> <template>
<div> <div>
<h1>可铸造的 NFT 数量{{ mintableCount }}</h1> <h1>可领取的勋章数量{{ mintableCount }}</h1>
<button @click="mintNFTs">领取勋章</button> <button @click="mintMedals">领取勋章</button>
</div> </div>
</template> </template>
<script> <script>
import { ref } from 'vue'; import { ref } from 'vue';
import { ethers } from 'ethers'; import { ethers } from 'ethers';
import NftDistributorABI from './NftDistributorABI.json'; // ABI import MEDAL_REWARDS_ABI from './MEDAL_REWARDS_ABI.json'; // ABI
export default { export default {
setup() { setup() {
const mintableCount = ref(0); const mintableCount = ref(0);
const nftDistributorAddress = '0x123...'; // const medalRewardsAddress = '0x123...'; //
const provider = new ethers.providers.Web3Provider(window.ethereum); // 使 MetaMask provider const provider = new ethers.providers.Web3Provider(window.ethereum); // 使 MetaMask provider
const nftDistributorContract = new ethers.Contract(nftDistributorAddress, NftDistributorABI, provider); const medalRewardsContract = new ethers.Contract(medalRewardsAddress, MEDAL_REWARDS_ABI, provider);
const loadMintableCount = async () => { const loadMintableCount = async () => {
try { try {
const userAddress = await provider.getSigner().getAddress(); // const userAddress = await provider.getSigner().getAddress(); //
const count = await nftDistributorContract.getMintableCount(userAddress); const count = await medalRewardsContract.getMintableCount(userAddress);
mintableCount.value = count.toNumber(); mintableCount.value = count.toNumber();
} catch (error) { } catch (error) {
console.error('Error fetching mintable count:', error); console.error('Error fetching mintable count:', error);
} }
}; };
const mintNFTs = async () => { const mintMedals = async () => {
try { try {
const signer = provider.getSigner(); // const signer = provider.getSigner(); //
const userAddress = await signer.getAddress(); // const userAddress = await signer.getAddress(); //
const contractWithSigner = nftDistributorContract.connect(signer); const contractWithSigner = medalRewardsContract.connect(signer);
const tx = await contractWithSigner.mintToUser(userAddress, mintableCount.value); const tx = await contractWithSigner.mintToUser(userAddress, mintableCount.value);
await tx.wait(); // await tx.wait(); //
// NFT NFT //
await loadMintableCount(); await loadMintableCount();
} catch (error) { } catch (error) {
console.error('Error minting NFTs:', error); console.error('Error minting medals:', error);
} }
}; };
// NFT //
loadMintableCount(); loadMintableCount();
return { return {
mintableCount, mintableCount,
mintNFTs, mintMedals,
}; };
}, },
}; };
</script> </script>