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 { 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="" /> | 
				
			|||
          <img src={data.url} className="img" alt="" /> | 
				
			|||
        </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> | 
				
			|||
          <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