yyy9608
1 year ago
28 changed files with 876 additions and 130 deletions
-
30src/api/index.ts
-
13src/components/Modal.tsx
-
55src/components/ProductInfo.tsx
-
2src/components/ProductItem.tsx
-
1src/hooks/useConnectWallet.ts
-
2src/hooks/useLike.ts
-
85src/pages/detail/AuctionNFT.tsx
-
14src/pages/detail/BuyNft.tsx
-
101src/pages/detail/IssuanceRushNFT.tsx
-
142src/pages/detail/OnShelvesNFT.tsx
-
58src/pages/detail/RemoveMarketMyNFT.tsx
-
74src/pages/detail/index.tsx
-
52src/pages/home/index.tsx
-
104src/pages/no-share/index.tsx
-
5src/pages/personal/AccountAssetsCard.tsx
-
36src/pages/record/index.tsx
-
2src/pages/share/index.tsx
-
1src/pages/withdraw/index.tsx
-
20src/router/layout/Navbar.tsx
-
4src/router/layout/index.tsx
-
5src/router/routes.tsx
-
3src/store/index.ts
-
13src/styles/components.scss
-
93src/styles/detail.scss
-
8src/styles/global.scss
-
11src/styles/home.scss
-
6src/styles/share.scss
-
10src/types/store.d.ts
@ -0,0 +1,85 @@ |
|||
import { useMemo, useState } from "react" |
|||
import { Button, Toast } from "react-vant" |
|||
import { buy_nft } from "~/api" |
|||
import Modal from "~/components/Modal" |
|||
import store from "~/store" |
|||
import '~/styles/detail.scss' |
|||
import { MarketNFTData } from "~/types/store" |
|||
import { toFixed2, toThousands } from "~/utils" |
|||
|
|||
interface AuctionNFTProps { |
|||
data: MarketNFTData, |
|||
getData: Function |
|||
} |
|||
|
|||
const AuctionNFT = (props: AuctionNFTProps) => { |
|||
|
|||
const { data, getData } = props |
|||
const [visible, setVisible] = useState(false) |
|||
const [markupCount, setMarkupCount] = useState(0) |
|||
const currentPrice = useMemo(() => { |
|||
let price = (data.mark * markupCount / 100 * Number(data.auction_price)) + Number(data.price) |
|||
return toFixed2(`${price}`, 3) |
|||
}, [data, markupCount]) |
|||
|
|||
const openModal = () => { |
|||
if (!store.state.token) return store.setVisibleUnLogin(true) |
|||
const diffTime = data.end_Time - Date.now() / 1000 |
|||
if (diffTime <= 0) return Toast.fail("拍賣時間已過") |
|||
setVisible(true) |
|||
} |
|||
|
|||
const confirm = async () => { |
|||
setVisible(false) |
|||
const params = { |
|||
mark: markupCount * data.mark, |
|||
id: data.id, |
|||
type: data.type |
|||
} |
|||
const res: any = await buy_nft(params) |
|||
if (res && res.code === 0) { |
|||
getData() |
|||
store.getMarketNft("auctionNft") |
|||
store.getUserInfo() |
|||
Toast.success('出價成功') |
|||
} |
|||
} |
|||
|
|||
return ( |
|||
<div className='mt-4 buy-nft'> |
|||
<Button |
|||
className='button' |
|||
style={{ |
|||
background: 'linear-gradient(103deg, #1DD0DF -1%, #1DD0DF -1%, #1BEDFF -1%, #14BDEB 108%)', |
|||
borderRadius: 10, |
|||
}} |
|||
onClick={openModal} |
|||
> |
|||
<span className="fz-20 fz-wb-550">出價</span> |
|||
</Button> |
|||
<Modal |
|||
title={data.name} |
|||
visible={visible} |
|||
setVisible={setVisible} |
|||
showConfirmButton |
|||
width="90%" |
|||
> |
|||
<div> |
|||
<div className={`tac mt-2 fz-wb-550`}>當前出價 "<span className={`${data.symbol}-text`}>{toThousands(currentPrice)}</span>" {data.symbol}</div> |
|||
<div className={`tac mt-1 fz-wb-550`}>您的最低出價 +{data.mark}% <span className={`${data.symbol}-text`}>{toThousands((data.mark / 100 * Number(data.auction_price)) + Number(data.price))}</span> {data.symbol}</div> |
|||
<div className="row-center mt-3"> |
|||
<div className={`tag-mark ${data.symbol}-bg`} onClick={() => { |
|||
setMarkupCount(val => val + 1) |
|||
}}>+{data.mark}%</div> |
|||
<div className={`tag-mark ml-2 ${markupCount <= 0 ? 'mark-button-disable' : data.symbol + '-bg'}`} onClick={() => markupCount > 0 && setMarkupCount(val => val - 1)}>-{data.mark}%</div> |
|||
</div> |
|||
<div className="mt-3 row-center"> |
|||
<Button onClick={confirm} className={`mark-button ${data.symbol}-bg`} disabled={markupCount <= 0}>確定出價</Button> |
|||
</div> |
|||
</div> |
|||
</Modal> |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
export default AuctionNFT |
@ -0,0 +1,101 @@ |
|||
import { useMemo, useState } from "react" |
|||
import { Button, CountDown } from "react-vant" |
|||
import { buy_nft } from "~/api" |
|||
import Modal from "~/components/Modal" |
|||
import store from "~/store" |
|||
import '~/styles/detail.scss' |
|||
import { MarketNFTData } from "~/types/store" |
|||
import { getTime, toThousands } from "~/utils" |
|||
import NFTProperties from "./NFTProperties" |
|||
|
|||
interface IssuanceRushNFTProps { |
|||
data: MarketNFTData, |
|||
getData: Function |
|||
} |
|||
|
|||
const IssuanceRushNFT = (props: IssuanceRushNFTProps) => { |
|||
|
|||
const { data, getData } = props |
|||
const [isBuy, setIsBuy] = useState(false) |
|||
const [buyVisible, setBuyVisible] = useState(false) |
|||
const [successVisible, setSuccessVisible] = useState(false) |
|||
|
|||
const openModal = () => { |
|||
if (!isBuy) return |
|||
setBuyVisible(true) |
|||
} |
|||
|
|||
const buyNft = async () => { |
|||
setBuyVisible(false) |
|||
const params = { |
|||
id: data.id, |
|||
type: data.type |
|||
} |
|||
const res: any = await buy_nft(params) |
|||
if (res && res.code === 0) { |
|||
setSuccessVisible(true) |
|||
getData() |
|||
store.getMyNft("myNft") |
|||
store.getMarketNft("issueNft") |
|||
store.getUserInfo() |
|||
} |
|||
} |
|||
|
|||
return ( |
|||
<div className='mt-2 buy-nft'> |
|||
<div className="fz-wb-550">NFT説明:</div> |
|||
<div className="fz-14 mt-1 taj">{data.illustrate}</div> |
|||
<div className="mt-2"> |
|||
<NFTProperties data={data} /> |
|||
</div> |
|||
<Button |
|||
className={`button ${data.symbol}-bg mtb-3`} |
|||
style={{ |
|||
borderRadius: 10, |
|||
}} |
|||
onClick={openModal} |
|||
> |
|||
{ |
|||
isBuy ? ( |
|||
<span className="fz-20 fz-wb-550 white">购买</span> |
|||
) : ( |
|||
<CountDown onFinish={() => { |
|||
setIsBuy(true) |
|||
}} time={data.start_time * 1000 - Date.now()} className="fz-20 white" style={{ letterSpacing: 2 }} /> |
|||
) |
|||
} |
|||
</Button> |
|||
<Modal |
|||
visible={buyVisible} |
|||
setVisible={setBuyVisible} |
|||
title="購買" |
|||
showConfirmButton |
|||
> |
|||
<div className='fz-wb-550' style={{ fontStyle: 'italic' }}> |
|||
<div className='tac mt-3'>您將以 <span className={`${data.symbol}-text`}>{toThousands(data.price)}</span> {data.symbol}</div> |
|||
<div className='tac mt-5px'>購買一張 "{data.name}" NFT</div> |
|||
<div className="mt-3 row-center"> |
|||
<Button onClick={buyNft} className={`mark-button ${data.symbol}-bg`}>確定出價</Button> |
|||
</div> |
|||
</div> |
|||
</Modal > |
|||
{/* success */} |
|||
<Modal |
|||
visible={successVisible} |
|||
setVisible={setSuccessVisible} |
|||
showConfirmButton |
|||
title='' |
|||
> |
|||
<div> |
|||
<div className='row-center'> |
|||
<img src={require('~/assets/buy-success.png')} alt="" className='success-img' /> |
|||
</div> |
|||
<div className='fz-18 fz-wb-550 tac'>恭喜購買成功</div> |
|||
<div className='fz-24 fz-wb-550 tac mt-1'>{data.name}</div> |
|||
</div> |
|||
</Modal> |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
export default IssuanceRushNFT |
@ -0,0 +1,58 @@ |
|||
import { useState } from 'react' |
|||
import { Button, Toast } from 'react-vant' |
|||
import { un_shelves_nft } from '~/api' |
|||
import Modal from '~/components/Modal' |
|||
import { useRouter } from '~/hooks/useRouter' |
|||
import store from '~/store' |
|||
import '~/styles/detail.scss' |
|||
import { MarketNFTData } from '~/types/store' |
|||
|
|||
interface RemoveMarketMyNFTProps { |
|||
data: MarketNFTData |
|||
} |
|||
|
|||
const RemoveMarketMyNFT = (props: RemoveMarketMyNFTProps) => { |
|||
|
|||
const { data } = props |
|||
const { push } = useRouter() |
|||
const [visible, setVisible] = useState(false) |
|||
|
|||
const confirm = async () => { |
|||
setVisible(false) |
|||
const res: any = await un_shelves_nft(data.id) |
|||
if (res && res.code === 0) { |
|||
Toast.success('下架成功') |
|||
store.getMarketNft("auctionNft") |
|||
store.getMarketNft("sellNft") |
|||
store.getMyNft("myNft") |
|||
store.getUserInfo() |
|||
push(-1) |
|||
} |
|||
} |
|||
|
|||
return ( |
|||
<div className='mt-4 buy-nft'> |
|||
<Button |
|||
className='button' |
|||
style={{ |
|||
background: 'linear-gradient(103deg, #1DD0DF -1%, #1DD0DF -1%, #1BEDFF -1%, #14BDEB 108%)', |
|||
borderRadius: 10, |
|||
}} |
|||
onClick={() => setVisible(true)} |
|||
> |
|||
<span className="fz-20 fz-wb-550">下架</span> |
|||
</Button> |
|||
<Modal |
|||
title='確定下架嗎?' |
|||
visible={visible} |
|||
setVisible={setVisible} |
|||
buttonText={<div className='fz-wb-550 black'>確定下架</div>} |
|||
buttonClick={confirm} |
|||
> |
|||
<></> |
|||
</Modal> |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
export default RemoveMarketMyNFT |
@ -0,0 +1,104 @@ |
|||
import '~/styles/share.scss' |
|||
import { Tabs, Toast } from 'react-vant' |
|||
import ProductItem from '~/components/ProductItem' |
|||
import store from '~/store' |
|||
import { toFixed2 } from '~/utils' |
|||
import { useEffect, useMemo, useState } from 'react' |
|||
import { no_user_info } from '~/api' |
|||
import { useRouter } from '~/hooks/useRouter' |
|||
import { UserInfo } from '~/types/store' |
|||
|
|||
const NoShare = () => { |
|||
|
|||
const { location } = useRouter() |
|||
const state = location.state |
|||
const [userInfo, setUserInfo] = useState({} as UserInfo) |
|||
const tabs = useMemo(() => ['我的NFT', '我喜歡的NFT'], []) |
|||
|
|||
const getLength = (index: number) => { |
|||
if(!userInfo.my_like || !userInfo.my_nft ) return 0 |
|||
if(index === 0) return userInfo.my_nft.length |
|||
return userInfo.my_like.length |
|||
} |
|||
|
|||
useEffect(() => { |
|||
const getData = async () => { |
|||
const res: any = await no_user_info(state.id) |
|||
res && res.code === 0 && setUserInfo(res.data) |
|||
} |
|||
|
|||
if (state && state.id) { |
|||
getData() |
|||
} |
|||
}, [state]) |
|||
|
|||
return ( |
|||
<div className="share"> |
|||
<div className='box'> |
|||
<img src={require('~/assets/personal.png')} alt="" className='bg-cover'></img> |
|||
<div className='row-center avatar'> |
|||
<img src={userInfo.url} className="img" alt="" /> |
|||
</div> |
|||
</div> |
|||
<div className='box-block'></div> |
|||
<div className='row-center mt-1 name-box'> |
|||
<div className='fz-wb-550 shar-name-overflow'>{userInfo.name}</div> |
|||
</div> |
|||
<div className='row-between plr-5 mt-3'> |
|||
<div className='tac'> |
|||
<div className='fz-20 fz-wb-550'>{userInfo.sell || 0}</div> |
|||
<div className='mt-8px'>售卖作品</div> |
|||
</div> |
|||
<div className='tac'> |
|||
<div className='fz-20 fz-wb-550'>{userInfo.auction || 0}</div> |
|||
<div className='mt-8px'>拍卖作品</div> |
|||
</div> |
|||
<div className='tac'> |
|||
<div className='fz-20 fz-wb-550'>{toFixed2(userInfo.income, 2)} FIL</div> |
|||
<div className='mt-8px'>收入</div> |
|||
</div> |
|||
</div> |
|||
<div className='mt-3'> |
|||
<Tabs |
|||
lineWidth={100} |
|||
background="none" |
|||
titleInactiveColor='#000' |
|||
titleActiveColor='#000' |
|||
color='#11C0CB' |
|||
animated |
|||
swipeable |
|||
> |
|||
{ |
|||
tabs.map((item, index) => ( |
|||
<Tabs.TabPane |
|||
key={index} |
|||
title={<div> |
|||
<span>{item}</span> |
|||
<span className='ml-5px' style={{ color: '#11C0CB' }}>{getLength(index)}</span> |
|||
</div>} |
|||
titleClass='fz-wb-550' |
|||
> |
|||
<div> |
|||
{ |
|||
userInfo.my_like && userInfo.my_nft && <div className='row-between flex-wrap plr-3'> |
|||
{(index === 0 ? userInfo.my_nft : userInfo.my_like).map((item, index) => ( |
|||
<div key={index}> |
|||
<ProductItem data={item} /> |
|||
</div> |
|||
))} |
|||
</div> |
|||
} |
|||
</div> |
|||
</Tabs.TabPane> |
|||
)) |
|||
} |
|||
|
|||
</Tabs> |
|||
</div> |
|||
|
|||
|
|||
</div> |
|||
) |
|||
} |
|||
|
|||
export default NoShare |
Write
Preview
Loading…
Cancel
Save
Reference in new issue