yyy9608
1 year ago
28 changed files with 876 additions and 130 deletions
-
30src/api/index.ts
-
13src/components/Modal.tsx
-
65src/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
-
144src/pages/detail/OnShelvesNFT.tsx
-
58src/pages/detail/RemoveMarketMyNFT.tsx
-
82src/pages/detail/index.tsx
-
82src/pages/home/index.tsx
-
104src/pages/no-share/index.tsx
-
5src/pages/personal/AccountAssetsCard.tsx
-
40src/pages/record/index.tsx
-
2src/pages/share/index.tsx
-
3src/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