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
-
41src/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 '~/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 ( |
return ( |
||||
<div className='product-info'> |
<div className='product-info'> |
||||
<div className='row-center'> |
<div className='row-center'> |
||||
<div className='cover-box row-center'> |
<div className='cover-box row-center'> |
||||
<img src={require('~/assets/nft/dragon.jpg')} className="img" alt="" /> |
|
||||
|
<img src={data.url} className="img" alt="" /> |
||||
</div> |
</div> |
||||
</div> |
</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 className='mt-3 row-between'> |
||||
<div> |
<div> |
||||
<div>铸造者</div> |
<div>铸造者</div> |
||||
<div className='user-tag mt-2 plr-1 row-items'> |
<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> |
||||
<div>售卖者</div> |
<div>售卖者</div> |
||||
<div className='user-tag mt-2 plr-1 row-items'> |
<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> |
</div> |
||||
</div> |
</div> |
||||
|
|
||||
|
<Modal |
||||
|
visible={visible} |
||||
|
title="分享給朋友" |
||||
|
setVisible={setVisible} |
||||
|
showConfirmButton |
||||
|
backgroundColosed |
||||
|
> |
||||
|
<div className='tac mt-2 like-text'> |
||||
|
已複製鏈接 |
||||
|
</div> |
||||
|
</Modal> |
||||
</div> |
</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 ProductInfo from "~/components/ProductInfo" |
||||
|
import { useRouter } from "~/hooks/useRouter" |
||||
import '~/styles/detail.scss' |
import '~/styles/detail.scss' |
||||
|
import { MarketNFTData } from "~/types/store" |
||||
|
import BuyNft from "./BuyNft" |
||||
|
import NFTProperties from "./NFTProperties" |
||||
|
import OnShelvesNFT from "./OnShelvesNFT" |
||||
|
|
||||
const Detail = () => { |
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 ( |
return ( |
||||
<div className="plr-3 detail"> |
<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> |
</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> |
</div> |
||||
) |
) |
||||
} |
} |
||||
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue