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