yyy9608
11 months ago
29 changed files with 687 additions and 131 deletions
-
36src/api/index.ts
-
BINsrc/assets/buy-success.png
-
10src/assets/iconfont/iconfont.css
-
2src/assets/iconfont/iconfont.js
-
7src/assets/iconfont/iconfont.json
-
BINsrc/assets/iconfont/iconfont.ttf
-
BINsrc/assets/iconfont/iconfont.woff
-
BINsrc/assets/iconfont/iconfont.woff2
-
24src/components/Modal.tsx
-
63src/components/ProductInfo.tsx
-
41src/hooks/useLike.ts
-
95src/pages/detail/BuyNft.tsx
-
35src/pages/detail/NFTProperties.tsx
-
18src/pages/detail/OnShelvesNFT.tsx
-
106src/pages/detail/index.tsx
-
93src/pages/home/index.tsx
-
63src/pages/product/index.tsx
-
35src/pages/share/index.tsx
-
23src/router/layout/index.tsx
-
21src/router/layout/ui.tsx
-
42src/store/index.ts
-
9src/styles/components.scss
-
8src/styles/detail.scss
-
11src/styles/global.scss
-
47src/styles/home.scss
-
2src/styles/product.scss
-
3src/styles/theme.scss
-
1src/types/store.d.ts
-
3src/utils/index.ts
After Width: 251 | Height: 294 | Size: 48 KiB |
2
src/assets/iconfont/iconfont.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,39 +1,72 @@ |
|||
import { observer } from 'mobx-react' |
|||
import { useState } from 'react' |
|||
import useLike from '~/hooks/useLike' |
|||
import store from '~/store' |
|||
import '~/styles/components.scss' |
|||
import { MarketNFTData } from '~/types/store' |
|||
import { toThousands } from '~/utils' |
|||
import { copy } from '~/utils/copy' |
|||
import Modal from './Modal' |
|||
|
|||
interface ProductInfoProps { |
|||
data: MarketNFTData |
|||
} |
|||
|
|||
const ProductInfo = (props: ProductInfoProps) => { |
|||
|
|||
const { data } = props |
|||
const { likeNft } = store.state |
|||
const { setLike, isLike } = useLike() |
|||
const [visible, setVisible] = useState(false) |
|||
|
|||
const share = () => { |
|||
const pathname = `${process.env.REACT_APP_SHARE_LINK}detail?id=${data.id}&type=${data.type}` |
|||
copy(pathname) |
|||
setVisible(true) |
|||
} |
|||
|
|||
const ProductInfo = () => { |
|||
return ( |
|||
<div className='product-info'> |
|||
<div className='row-center'> |
|||
<div className='cover-box row-center'> |
|||
<img src={require('~/assets/nft/dragon.jpg')} className="img" alt="" /> |
|||
</div> |
|||
<img src={data.url} className="img" alt="" /> |
|||
</div> |
|||
<div className='mt-1 row-center'> |
|||
<i className='iconfont icon-shoucang fz-20' /> |
|||
<i className='iconfont icon-shangchuandaochu fz-24 ml-4' /> |
|||
<i className='iconfont icon-caidan fz-24 ml-4' /> |
|||
</div> |
|||
<div className='mt-3 fz-30 fz-wb-550'>生肖唐彩-龙</div> |
|||
<div className='price-tag mt-1'>USDT 21,100.15</div> |
|||
{data.type !== 0 && <div className='mt-1 row-center'> |
|||
<i className={`iconfont icon-daohangshoucangyishoucang fz-20 ${isLike(data.id, likeNft) ? 'like-text' : 'sub-text'}`} onClick={() => setLike(data.id)} /> |
|||
<i className='iconfont icon-shangchuandaochu fz-24 ml-4' onClick={share} /> |
|||
</div>} |
|||
<div className='mt-3 fz-30 fz-wb-550'>{data.name}</div> |
|||
<div className={`price-tag mt-1 ${data.symbol}-bg`}>{data.symbol} {toThousands(data.price)}</div> |
|||
<div className='mt-3 row-between'> |
|||
<div> |
|||
<div>铸造者</div> |
|||
<div className='user-tag mt-2 plr-1 row-items'> |
|||
<img src={require('~/assets/user.png')} alt="" /> |
|||
<div className='fz-12 tac flex-1'>Filefast</div> |
|||
<img src={data.cast_url} alt="" /> |
|||
<div className='fz-12 tac flex-1 name-overflow'>{data.cast_name}</div> |
|||
</div> |
|||
</div> |
|||
<div> |
|||
<div>售卖者</div> |
|||
<div className='user-tag mt-2 plr-1 row-items'> |
|||
<img src={require('~/assets/user.png')} alt="" /> |
|||
<div className='fz-12 tac flex-1'>Filefast</div> |
|||
<img src={data.sell_url} alt="" /> |
|||
<div className='fz-12 tac flex-1 name-overflow'>{data.sell_name}</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<Modal |
|||
visible={visible} |
|||
title="分享給朋友" |
|||
setVisible={setVisible} |
|||
showConfirmButton |
|||
backgroundColosed |
|||
> |
|||
<div className='tac mt-2 like-text'> |
|||
已複製鏈接 |
|||
</div> |
|||
</Modal> |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
export default ProductInfo |
|||
export default observer(ProductInfo) |
@ -0,0 +1,41 @@ |
|||
import { useRef } from "react"; |
|||
import { set_like } from "~/api"; |
|||
import store from "~/store"; |
|||
import { MarketNFTData } from "~/types/store"; |
|||
|
|||
const useLike = () => { |
|||
const throttle = useRef(false) |
|||
/** |
|||
* @description 設置喜歡的NFT |
|||
* @param id nftid |
|||
* 1.喜歡 2.取消喜歡 |
|||
*/ |
|||
const setLike = async (id: number) => { |
|||
if (!store.state.token) return store.setVisibleUnLogin(true); |
|||
if(throttle.current)return |
|||
throttle.current = true |
|||
let item = store.state.likeNft.find((v) => v.id === id); |
|||
let status = item ? 2 : 1; |
|||
let res: any = await set_like(id, status); |
|||
throttle.current = false |
|||
if (res && res.code === 0) { |
|||
store.getMyNft("likeNft", 2); |
|||
} |
|||
}; |
|||
|
|||
/** |
|||
* 判斷喜歡的NFt |
|||
*/ |
|||
const isLike = (id: number, likeNft: MarketNFTData[]) => { |
|||
const item = likeNft.find((v) => v.id === id); |
|||
if (item) return true; |
|||
return false; |
|||
}; |
|||
|
|||
return { |
|||
setLike, |
|||
isLike, |
|||
}; |
|||
}; |
|||
|
|||
export default useLike; |
@ -0,0 +1,95 @@ |
|||
import '~/styles/detail.scss' |
|||
import { Button, Toast } from 'react-vant' |
|||
import { MarketNFTData } from '~/types/store' |
|||
import Modal from '~/components/Modal' |
|||
import { useState } from 'react' |
|||
import { observer } from 'mobx-react' |
|||
import store from '~/store' |
|||
import { toThousands } from '~/utils' |
|||
import { buy_nft } from '~/api' |
|||
|
|||
interface BuyNftProps { |
|||
data: MarketNFTData |
|||
} |
|||
|
|||
const BuyNft = (props: BuyNftProps) => { |
|||
|
|||
const { data } = props |
|||
const { userInfo, token } = store.state |
|||
const [buyVisible, setBuyVisible] = useState(false) |
|||
const [successVisible, setSuccessVisible] = useState(false) |
|||
|
|||
const openBuyModal = () => { |
|||
if (!token) return store.setVisibleUnLogin(true) |
|||
let symbol = data.symbol |
|||
if (symbol === 'USDT' && Number(userInfo.balance_Usdt) < Number(data.price)) { |
|||
Toast.fail('餘額不足') |
|||
return |
|||
} |
|||
if (symbol === 'FIL' && Number(userInfo.balance_fil) < Number(data.price)) { |
|||
Toast.fail('餘額不足') |
|||
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) |
|||
store.getMyNft("myNft", 1) |
|||
store.getMarketNft("issueNft") |
|||
store.getMarketNft("sellNft") |
|||
} |
|||
} |
|||
|
|||
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={openBuyModal} |
|||
> |
|||
<span className="fz-20 fz-wb-550">购买</span> |
|||
</Button> |
|||
{/* Buy Modal */} |
|||
<Modal |
|||
visible={buyVisible} |
|||
setVisible={setBuyVisible} |
|||
title="購買" |
|||
buttonText={<div className='black fz-wb-550'>確定購買</div>} |
|||
buttonClick={buyNft} |
|||
> |
|||
<div className='fz-wb-550' style={{ fontStyle: 'italic' }}> |
|||
<div className='tac mt-3'>您將以 <span className='primary'>{toThousands(data.price)}</span> {data.symbol}</div> |
|||
<div className='tac mt-5px'>購買一張 "{data.name}" NFT</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 observer(BuyNft) |
@ -0,0 +1,35 @@ |
|||
import { MarketNFTData } from "~/types/store" |
|||
import { getTime } from "~/utils" |
|||
|
|||
interface NFTPropertiesProps { |
|||
data: MarketNFTData |
|||
} |
|||
|
|||
const NFTProperties = (props: NFTPropertiesProps) => { |
|||
|
|||
const { data } = props |
|||
|
|||
return ( |
|||
<div> |
|||
<div className="fz-wb-550">NFT屬性:</div> |
|||
<div className="row fz-14 mt-1"> |
|||
<div className="flex-1">鑄造日期:{getTime(data.time * 1000, 'day')}</div> |
|||
<div className="flex-1">NFT名稱:{data.name}</div> |
|||
</div> |
|||
<div className="row fz-14 mt-1"> |
|||
<div className="flex-1">NFT編號:{data.number}</div> |
|||
<div className="flex-1">NFT發行數:{data.total_stock}</div> |
|||
</div> |
|||
<div className="row fz-14 mt-1"> |
|||
<div className="flex-1">NFT功能:{data.function}</div> |
|||
<div className="flex-1">歸屬平臺:{data.platform}</div> |
|||
</div> |
|||
<div className="row fz-14 mt-1"> |
|||
<div className="flex-1">獎勵分配:{data.award}</div> |
|||
<div className="flex-1">分配天數:{data.day}</div> |
|||
</div> |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
export default NFTProperties |
@ -0,0 +1,18 @@ |
|||
import { MarketNFTData } from "~/types/store" |
|||
|
|||
interface OnShelvesNFTProps { |
|||
data: MarketNFTData |
|||
} |
|||
|
|||
const OnShelvesNFT = (props: OnShelvesNFTProps) => { |
|||
|
|||
const { data } = props |
|||
|
|||
return ( |
|||
<div className="mt-2"> |
|||
售賣 拍賣 |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
export default OnShelvesNFT |
@ -1,22 +1,104 @@ |
|||
import { Button } from "react-vant" |
|||
import { useEffect, useMemo, useState } from "react" |
|||
import { Button, Empty, Loading } from "react-vant" |
|||
import { nft_detail } from "~/api" |
|||
import BackBar from "~/components/BackBar" |
|||
import ProductInfo from "~/components/ProductInfo" |
|||
import { useRouter } from "~/hooks/useRouter" |
|||
import '~/styles/detail.scss' |
|||
import { MarketNFTData } from "~/types/store" |
|||
import BuyNft from "./BuyNft" |
|||
import NFTProperties from "./NFTProperties" |
|||
import OnShelvesNFT from "./OnShelvesNFT" |
|||
|
|||
const Detail = () => { |
|||
|
|||
const { location } = useRouter() |
|||
const query = useMemo(() => { |
|||
let obj = {} as { [key: string]: number } |
|||
if (location.state) { |
|||
obj.id = Number(location.state.id) |
|||
obj.type = Number(location.state.type) |
|||
} |
|||
if (location.search) { |
|||
let val = location.search.substring(1).split('&') |
|||
val.forEach(item => { |
|||
let value = item.split('=') |
|||
obj[value[0]] = Number(value[1]) |
|||
}) |
|||
} |
|||
return obj |
|||
}, [location]) |
|||
|
|||
const [product, setProduct] = useState({} as MarketNFTData) |
|||
const [loading, setLoading] = useState(false) |
|||
console.log(product); |
|||
|
|||
const titles = useMemo(() => ({ |
|||
"0": "上架", |
|||
"1": "拍賣", |
|||
"2": "售賣", |
|||
"3": "發行" |
|||
} as any), []) |
|||
|
|||
useEffect(() => { |
|||
const getData = async () => { |
|||
setLoading(true) |
|||
const res: any = await nft_detail(query) |
|||
setLoading(false) |
|||
res && res.code === 0 && setProduct(res.data) |
|||
} |
|||
|
|||
Object.keys(query).length === 2 && getData() |
|||
}, []) |
|||
|
|||
if (Object.keys(query).length < 2) return ( |
|||
<div className="plr-3"> |
|||
<BackBar |
|||
title={titles[product.type] || "詳情"} |
|||
/> |
|||
<div className="row-center" style={{ height: '70vh' }}> |
|||
<Empty image="error" description="沒有該NFT" imageSize={200} /> |
|||
</div> |
|||
</div> |
|||
) |
|||
|
|||
if (loading) return ( |
|||
<div className="row-center" style={{ height: '70vh' }}> |
|||
<div className="tac"> |
|||
<Loading type="ball" /> |
|||
<div className="fz-12 sub-text ml-1">數據獲取中...</div> |
|||
</div> |
|||
</div> |
|||
) |
|||
|
|||
return ( |
|||
<div className="plr-3 detail"> |
|||
<ProductInfo /> |
|||
<div className='mt-4'> |
|||
<Button |
|||
className='button' |
|||
style={{ |
|||
background: 'linear-gradient(103deg, #1DD0DF -1%, #1DD0DF -1%, #1BEDFF -1%, #14BDEB 108%)', |
|||
borderRadius: 10, |
|||
}} |
|||
> |
|||
<span className="fz-20 fz-wb-550">购买</span> |
|||
</Button> |
|||
<BackBar |
|||
title={titles[`${product.type}`] || "詳情"} |
|||
/> |
|||
<div className="mt-2"> |
|||
<ProductInfo data={product} /> |
|||
</div> |
|||
{/* type 0.表示上架NFT 2.購買NFT 3.搶購 */} |
|||
{ |
|||
product.type === 0 && ( |
|||
<div className="mt-3"> |
|||
<NFTProperties data={product} /> |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
{ |
|||
product.type === 0 && ( |
|||
<OnShelvesNFT data={product} /> |
|||
) |
|||
} |
|||
|
|||
{ |
|||
product.type === 2 && ( |
|||
<BuyNft data={product} /> |
|||
) |
|||
} |
|||
</div> |
|||
) |
|||
} |
|||
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue