Browse Source

commit

master
yyy9608 9 months ago
parent
commit
eada4f8822
  1. 1
      .env
  2. 5
      package.json
  3. 3
      public/index.html
  4. 80
      src/api/index.ts
  5. 10
      src/api/service.ts
  6. 26
      src/assets/iconfont/iconfont.css
  7. 2
      src/assets/iconfont/iconfont.js
  8. 35
      src/assets/iconfont/iconfont.json
  9. BIN
      src/assets/iconfont/iconfont.ttf
  10. BIN
      src/assets/iconfont/iconfont.woff
  11. BIN
      src/assets/iconfont/iconfont.woff2
  12. BIN
      src/assets/share.png
  13. 46
      src/components/CoinPicker.tsx
  14. 51
      src/components/Modal.tsx
  15. 23
      src/components/ProductItem.tsx
  16. 37
      src/hooks/useWs.ts
  17. 6
      src/index.tsx
  18. 42
      src/pages/home/index.tsx
  19. 13
      src/pages/personal/AccountAssetsCard.tsx
  20. 42
      src/pages/personal/index.tsx
  21. 5
      src/pages/product/index.tsx
  22. 79
      src/pages/recharge/index.tsx
  23. 129
      src/pages/record/index.tsx
  24. 209
      src/pages/share/index.tsx
  25. 252
      src/pages/team/index.tsx
  26. 64
      src/pages/withdraw/index.tsx
  27. 131
      src/router/layout/index.tsx
  28. 123
      src/router/layout/ui.tsx
  29. 10
      src/router/routes.tsx
  30. 24
      src/store/index.ts
  31. 19
      src/styles/components.scss
  32. 27
      src/styles/layout.scss
  33. 89
      src/styles/personal.scss
  34. 3
      src/styles/recharge.scss
  35. 20
      src/styles/share.scss
  36. 2
      src/styles/theme.scss
  37. 14
      src/types/api.d.ts
  38. 49
      src/types/store.d.ts
  39. 2
      src/utils/copy.ts
  40. 33
      src/utils/index.ts
  41. 1
      src/utils/sign/sign.ts
  42. 109
      yarn.lock

1
.env

@ -1,5 +1,6 @@
SKIP_PREFLIGHT_CHECK=true
GENERATE_SOURCEMAP=false
REACT_APP_BASE_URL='http://14.29.101.215:30307'
REACT_APP_WS_URL='ws://14.29.101.215:30307'
REACT_APP_SHARE_LINK='http://14.29.101.215:30305/#/'
REACT_APP_SIGN_KEY='9527nft9527_@fsdgfsx'

5
package.json

@ -50,6 +50,7 @@
"postcss-normalize": "^10.0.1",
"postcss-preset-env": "^7.0.1",
"prompts": "^2.4.2",
"qrcode": "^1.5.3",
"react": "^18.2.0",
"react-app-polyfill": "^3.0.0",
"react-dev-utils": "^12.0.1",
@ -70,7 +71,8 @@
"webpack": "^5.64.4",
"webpack-dev-server": "^4.6.0",
"webpack-manifest-plugin": "^4.0.2",
"workbox-webpack-plugin": "^6.4.1"
"workbox-webpack-plugin": "^6.4.1",
"ws": "^8.16.0"
},
"scripts": {
"start": "node scripts/start.js",
@ -97,6 +99,7 @@
},
"devDependencies": {
"@types/lodash": "^4.14.202",
"@types/qrcode": "^1.5.5",
"postcss-px-to-viewport": "^1.1.1",
"sass": "^1.69.5",
"sass-resources-loader": "^2.2.5"

3
public/index.html

@ -4,7 +4,8 @@
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;" name="viewport" />
<meta name="x5-fullscreen" content="true">
<meta name="theme-color" content="#000000" />
<meta name="description" content="9527 description" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />

80
src/api/index.ts

@ -1,8 +1,9 @@
import { PerformSignin, PerformSNonce } from "~/types";
import { HTTPHistoryParams, HTTPWithdrawParams } from "~/types/api";
import request from "./service";
/**
* @description
* @description random
*/
export const getNonce = (query: PerformSNonce) =>
request({
@ -11,28 +12,87 @@ export const getNonce = (query: PerformSNonce) =>
});
/**
* @description
* @description Sign
*/
export const performSignin = (query: PerformSignin) =>
request({ url: "/v1/signin", data: query });
/**
* @description
* @description Upload avatar
*/
export const uploadImage = (file: File) => {
export const upload_image = (file: File) => {
const body = new FormData();
console.log(body);
body.append("img", file);
return request({ url: "/v1/uploadImg", data: body })
return request({ url: "/v1/uploadImg", data: body });
};
/**
* @description
* @description
*/
export const accountInfo = () => request({ url: '/v1/account' })
export const accountInfo = () => request({ url: "/v1/account" });
/**
* @description coin list
*/
export const coin_list = () => request({ url: '/v1/tokenList' })
export const coin_list = () => request({ url: "/v1/tokenList" });
/**
* @description market list
* @param {type} 1. 2.
*/
export const market_list = (type: 1 | 2) =>
request({ url: "/v1/market", data: { type } });
/**
* @description
* @param {name}
*/
export const reset_name = (name: string) =>
request({ url: "/v1/rename", data: { name } });
/**
* @description
* @param {type} 1. 2.
*/
export const open_page = (type: 1 | 2) =>
request({ url: "/v1/assetsShow", data: { show: type } });
/**
* @description NFT
* @param {type} 1. 2.
*/
export const personal_nft = (type: number) =>
request({ url: "/v1/myNft", data: { type: type } });
/**
* @description
* @param {type} 1. 2.
*/
export const user_withdraw = (query: HTTPWithdrawParams) =>
request({ url: "/v1/withdraw", data: query });
/**
* @description
* @param {type} 0. 1. 2.
* @param {page_size}
* @param {page}
*/
export const history_record = (query: HTTPHistoryParams) =>
request({ url: "/v1/hisory", data: query });
/**
* @description
*/
export const team_info = () => request({ url: "/v1/invitiInfo" });
/**
* @description
*/
export const my_invite = (query: { page: number; page_size: number }) =>
request({ url: "/v1/invitiDetails", data: query });
/**
* @description
*/
export const bind_rmd = (address: string) =>
request({ url: "/v1/binding", data: { address } });

10
src/api/service.ts

@ -1,5 +1,5 @@
import axiosConfig from "./axios_config";
import axios from "axios";
import axios, { AxiosResponse } from "axios";
import signGenerator from "../utils/sign/sign";
import { Toast } from "react-vant";
import sortParam from "../utils/sign/sort";
@ -10,8 +10,6 @@ const service = axios.create(axiosConfig);
// 请求拦截
service.interceptors.request.use(
(config) => {
console.log(config);
(config.headers as any).token = store.state.token;
if (!config.data) config.data = {};
let ps = config.params ? sortParam(config.params) : "";
@ -24,9 +22,9 @@ service.interceptors.request.use(
let sign = signGenerator(signData);
(config.headers as any).sign = sign;
(config.headers as any).timestamp = timestamp;
if(config.data instanceof FormData) return config
if (config.data instanceof FormData) return config;
config.data = JSON.stringify(config.data);
return config;
},

26
src/assets/iconfont/iconfont.css

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 4379626 */
src: url('iconfont.woff2?t=1703840169958') format('woff2'),
url('iconfont.woff?t=1703840169958') format('woff'),
url('iconfont.ttf?t=1703840169958') format('truetype');
src: url('iconfont.woff2?t=1704271682012') format('woff2'),
url('iconfont.woff?t=1704271682012') format('woff'),
url('iconfont.ttf?t=1704271682012') format('truetype');
}
.iconfont {
@ -13,6 +13,26 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-close:before {
content: "\e6a7";
}
.icon-download:before {
content: "\e60d";
}
.icon-fuzhi_copy:before {
content: "\e618";
}
.icon-chenggong:before {
content: "\e63c";
}
.icon-bianji:before {
content: "\e67f";
}
.icon-datijilu:before {
content: "\e606";
}

2
src/assets/iconfont/iconfont.js
File diff suppressed because it is too large
View File

35
src/assets/iconfont/iconfont.json

@ -5,6 +5,41 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "257406",
"name": "close",
"font_class": "close",
"unicode": "e6a7",
"unicode_decimal": 59047
},
{
"icon_id": "598164",
"name": "download",
"font_class": "download",
"unicode": "e60d",
"unicode_decimal": 58893
},
{
"icon_id": "23870326",
"name": "复制_copy",
"font_class": "fuzhi_copy",
"unicode": "e618",
"unicode_decimal": 58904
},
{
"icon_id": "7408311",
"name": "成功",
"font_class": "chenggong",
"unicode": "e63c",
"unicode_decimal": 58940
},
{
"icon_id": "7443382",
"name": "编辑",
"font_class": "bianji",
"unicode": "e67f",
"unicode_decimal": 59007
},
{
"icon_id": "1240492",
"name": "答题记录",

BIN
src/assets/iconfont/iconfont.ttf

BIN
src/assets/iconfont/iconfont.woff

BIN
src/assets/iconfont/iconfont.woff2

BIN
src/assets/share.png

After

Width: 444  |  Height: 465  |  Size: 11 KiB

46
src/components/CoinPicker.tsx

@ -0,0 +1,46 @@
import { useMemo } from "react";
import { Picker } from "react-vant";
import { CoinList } from "~/types/store";
interface CoinPickerProps {
setIndex: Function;
list: CoinList[];
index: number
}
const CoinPicker = ({ setIndex, list, index }: CoinPickerProps) => {
const coinList = useMemo(() => list.map(item => item.symbol), [list]);
return (
<Picker
columns={coinList}
popup={{
round: true
}}
onConfirm={(_val: string, _item: Object, _index: number) => {
setIndex(_index);
}}
placeholder=""
value={coinList[index]}
>
{(_val, _, _action) => (
<div onClick={() => _action.open()} style={{ width: '100%' }}>
{
list[index] && (
<div className='row-between'>
<div className='row-items'>
<img src={require(`~/assets/${list[index].symbol}.png`)} alt="" className="img-20" />
<div className='ml-1 mt-3px fz-wb-550'>{list[index].symbol}</div>
</div>
<div className='iconfont icon-arrow-right-bold fz-20 fz-wb-550'></div>
</div>
)
}
</div>
)}
</Picker>
)
}
export default CoinPicker;

51
src/components/Modal.tsx

@ -0,0 +1,51 @@
import { Overlay } from 'react-vant';
import '~/styles/components.scss'
interface ModalProps {
buttonText: string;
title: string;
children: JSX.Element;
buttonClick: Function;
visible: boolean;
setVisible: Function;
hiddenCloseIcon?: boolean;
showCancelButton?: boolean;
showCancelButtonText?: string;
showCancelButtonClick?: Function;
}
const Modal = (
{ title, buttonClick, visible, setVisible, children, buttonText, hiddenCloseIcon, showCancelButton, showCancelButtonText, showCancelButtonClick }: ModalProps
) => {
return (
<Overlay
visible={visible}
className='row-center'
style={{ height: '100%' }}
>
<div className='modal-content'>
<div className='text-center row-center'>
<div style={{ flex: 1 }}></div>
<div style={{ flex: 1, whiteSpace: 'nowrap' }} className='fz-20 fz-wb-550'>{title}</div>
<div style={{ flex: 1 }} className="tae">
{
!hiddenCloseIcon && (
<i className='iconfont icon-guanbi2 fz-24 fz-wb-550' onClick={() => setVisible(false)} />
)
}
</div>
</div>
{children}
<div className='mt-3 row-center'>
<div className='modal-button row-center' onClick={() => buttonClick()}>{buttonText}</div>
{
showCancelButton &&
<div className='modal-button row-center ml-2' onClick={() => showCancelButtonClick && showCancelButtonClick()}>{showCancelButtonText || '關閉'}</div>
}
</div>
</div>
</Overlay>
)
}
export default Modal

23
src/components/ProductItem.tsx

@ -1,17 +1,26 @@
import '~/styles/components.scss'
import { MarketNFTData } from '~/types/store'
import { splitAddress, toThousands } from '~/utils'
interface ProductItemProps {
data: MarketNFTData
}
const ProductItem = (props: ProductItemProps) => {
const { data } = props
const ProductItem = () => {
return (
<div className='product-item mt-2'>
{/* <img src={require('~/assets/cover.png')} className='img' alt="" /> */}
<div className='img'></div>
<img src={data?.url} className='img' alt="" />
{/* <div className='img'></div> */}
<div className='desc-box p-1'>
<div className='tar fz-12'></div>
<div className='tar fz-12'>{data.name}</div>
<div className='row-justify-end mt-3px'>
<div className='price-tag fz-10'>USDT 10000</div>
<div className='price-tag fz-10'>{data?.symbol} {toThousands(data?.price)}</div>
<div className='user-tag'>
<img src={require('~/assets/user.png')} className="user-img" alt="" />
<div className='fz-10 ml-3px'>jackasddf</div>
<img src={data.sell_url} className="user-img" alt="" />
<div className='fz-10 ml-3px'>{splitAddress(data.sell_name,4)}</div>
</div>
</div>
</div>

37
src/hooks/useWs.ts

@ -0,0 +1,37 @@
import { useRef } from "react";
import signGenerator from "~/utils/sign/sign";
const useWs = (path: string) => {
const baseUrl = `ws://14.29.101.215:30307/api/v1/${path}`;
// const baseUrl = `ws://192.168.124.52:8083/api/v1/${path}`;
const ws = useRef<any>(null);
const connect = (token: string) => {
let timestamp = Date.now();
let signData = {
uri: `/api/v1/${path}`,
timestamp: timestamp,
args: "",
};
const sign = signGenerator(signData);
ws.current = new WebSocket(
`${baseUrl}?Token=${token}&sign=${sign}&timestamp=${timestamp}`
);
ws.current.onMessage = (data: any) => {
console.log(data);
};
};
const disconnect = () => {
ws.current && ws.current.close();
ws.current = null;
};
return {
connect,
disconnect,
};
};
export default useWs;

6
src/index.tsx

@ -8,7 +8,7 @@ const root = ReactDOM.createRoot(
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
// <React.StrictMode>
<App />
// </React.StrictMode>
);

42
src/pages/home/index.tsx

@ -2,39 +2,60 @@ import '~/styles/home.scss'
import ProductItem from '~/components/ProductItem'
import { useRouter } from '~/hooks/useRouter'
import { Button } from 'react-vant'
import { useEffect, useState } from 'react'
import { market_list } from '~/api'
import { MarketNFTData } from '~/types/store'
import { splitAddress } from '~/utils'
import { observer } from 'mobx-react'
const Home = () => {
const { push } = useRouter()
const [marketData, setMarketData] = useState([] as MarketNFTData[])
const [auctionData, setAuctionData] = useState([] as MarketNFTData[])
useEffect(() => {
const getMarketData = async () => {
const res: any = await market_list(2)
res && res.code === 0 && res.data && setMarketData(res.data)
}
const getAuctionData = async () => {
const res: any = await market_list(1)
res && res.code === 0 && res.data && setAuctionData(res.data)
}
getMarketData()
getAuctionData()
}, [])
return (
<div className="home">
<div className='plr-3 fz-20 mt-2'></div>
<div className='mt-2 row-items swiper'>
{
Array.from({ length: 5 }).map((_, index) => (
auctionData.map((item, index) => (
<div
key={index}
className={`swiper-item ${index === 0 ? 'ml-3' : 'ml-1'} ${index === 4 && 'mr-3'}`}
>
<img src={require('~/assets/nft/dragon.jpg')} alt="" className='swiper-img' onClick={() => push('/detail')} />
<img src={item.url} alt="" className='swiper-img' onClick={() => push('/detail', { id: item.id })} />
<div className='cover-box'>
<div className='cover-box-item p-1 fz-12'>
<div>
<div>-</div>
<div>{item.name}</div>
<div className='row-items mt-2px'>
<div className='flex-1'>
<div className='fz-8 fz-wb-550'></div>
<div className='user-tag row-items mt-2px'>
<img src={require('~/assets/user.png')} alt="" />
<div className='flex-1 ml-5px'>Filefast</div>
<img src={item.cast_url} alt="" />
<div className='flex-1 ml-5px'>{splitAddress(item.cast_name, 4)}</div>
</div>
</div>
<div className='flex-1'>
<div className='fz-8 fz-wb-550'></div>
<div className='user-tag row-items mt-2px'>
<img src={require('~/assets/user.png')} alt="" />
<div className='flex-1 ml-5px'>Filefast</div>
<img src={item.sell_url} alt="" />
<div className='flex-1 ml-5px'>{splitAddress(item.sell_name, 4)}</div>
</div>
</div>
</div>
@ -55,12 +76,15 @@ const Home = () => {
<div className='plr-3'>
<div className='row-between flex-wrap'>
{
Array.from({ length: 10 }).map((_, index) => (
<ProductItem key={index} />
marketData.map((item, index) => (
<div key={index} onClick={() => push('/detail', { id: item.id })}>
<ProductItem data={item} />
</div>
))
}
</div>
</div>
</div>
)
}

13
src/pages/personal/AccountAssetsCard.tsx

@ -1,15 +1,18 @@
import '~/styles/personal.scss'
import { useMemo } from "react";
import { useRouter } from "~/hooks/useRouter";
import { observer } from 'mobx-react';
import store from '~/store';
const AccountAssetsCard = () => {
const { push } = useRouter();
const { userInfo } = store.state
const balanceList = useMemo(() => [
{ title: 'USDT资产', value: 0, symbol: 'USDT' },
{ title: 'FIL资产', value: 0, symbol: 'FIL' },
], []);
{ title: 'USDT资产', value: userInfo.balance_Usdt || 0, symbol: 'USDT' },
{ title: 'FIL资产', value: userInfo.balance_fil || 0, symbol: 'FIL' },
], [userInfo]);
const menu = useMemo(() => [
{ title: '充值', icon: require('~/assets/recharge.png'), path: '/recharge' },
@ -24,7 +27,7 @@ const AccountAssetsCard = () => {
<div className='card'>
<div>
<div className='sub-color'></div>
<div className='mt-5px'><span className='fz-24 fz-wb-550'>1429.32</span> <span>USDT</span></div>
<div className='mt-5px'><span className='fz-24 fz-wb-550'>{userInfo.total}</span> <span>USDT</span></div>
</div>
<div className='row-items'>
{
@ -58,4 +61,4 @@ const AccountAssetsCard = () => {
)
};
export default AccountAssetsCard;
export default observer(AccountAssetsCard);

42
src/pages/personal/index.tsx

@ -1,17 +1,15 @@
import { observer } from 'mobx-react'
import { Divider } from 'react-vant'
import store from '~/store'
import '~/styles/personal.scss'
import AccountAssetsCard from './AccountAssetsCard'
const Personal = () => {
const { userInfo } = store.state
return (
<div className='personal'>
{/* <Uploader
maxCount={1}
maxSize={500 * 1024}
onOversize={() => Toast.info('文件大小不能超过500kb')}
upload={upload}
/> */}
<div className='plr-2 '>
<div className='mt-3'>
<AccountAssetsCard />
@ -25,18 +23,24 @@ const Personal = () => {
<div></div>
<div></div>
</div>
<div className='row-between mt-2'>
<div className='row'>
<img src={require('~/assets/USDT.png')} className='symbol-img' alt="" />
<div className='ml-1'>
<div className='fz-wb-550'>USDT</div>
<div className='fz-14 sub-text mt-5px'>TetherUS</div>
</div>
</div>
<div className='tar'>
<div className='fz-wb-550'>100</div>
<div className='sub-text fz-14 mt-5px'>$429.32</div>
</div>
<div>
{
userInfo.assets && userInfo.assets.map(item => (
<div className='row-between mt-2' style={{ alignItems: 'flex-start' }} key={item.id}>
<div className='row'>
<img src={require(`~/assets/${item.symbol}.png`)} className='symbol-img' alt="" />
<div className='ml-1'>
<div className='fz-wb-550'>{item.symbol}</div>
<div className='fz-14 sub-text mt-5px'>{item.name}</div>
</div>
</div>
<div className='tar'>
<div className='fz-wb-550'>{item.amount}</div>
<div className='sub-text fz-14 mt-5px'>{item.symbol !== 'USDT' && item.amount_usdt}</div>
</div>
</div>
))
}
</div>
</div>
@ -44,4 +48,4 @@ const Personal = () => {
)
}
export default Personal
export default observer(Personal)

5
src/pages/product/index.tsx

@ -1,5 +1,6 @@
import { Divider } from 'react-vant'
import '~/styles/product.scss'
import { MarketNFTData } from '~/types/store'
import ProductItem from '../../components/ProductItem'
const Product = () => {
@ -18,11 +19,11 @@ const Product = () => {
<div className='row-between flex-wrap'>
{
Array.from({ length: 6 }).map((_, index) => (
<ProductItem key={index} />
<ProductItem key={index} data={{} as MarketNFTData} />
))
}
</div>
</div>
)
}

79
src/pages/recharge/index.tsx

@ -1,22 +1,25 @@
import '~/styles/recharge.scss'
import { observer } from 'mobx-react'
import store from '../../store'
import { useMemo, useRef, useState } from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useRouter } from '../../hooks/useRouter'
import { ethers } from 'ethers'
import { BigNumber, ethers } from 'ethers'
import { Button, Toast } from 'react-vant'
import { switchNetWork } from '../../utils'
import { switchNetWork, toFixed2 } from '../../utils'
import { IERC20__factory } from '../../contract/IERC20__factory'
import { toWei } from '../../utils/wei'
import BackBar from '~/components/BackBar'
import CoinPicker from '~/components/CoinPicker'
const Recharge = () => {
const { coinIndex, coinList, userInfo } = store.state
const { coinList, userInfo, walletAddress, token } = store.state
const { push } = useRouter()
const inputRef = useRef<HTMLInputElement>(null);
const inputRef = useRef<HTMLInputElement>(null)
const [coinIndex, setCoinIndex] = useState(0)
const currentCoin = useMemo(() => coinList[coinIndex], [coinIndex, coinList])
const [loading, setLoading] = useState(false);
const [loading, setLoading] = useState(false)
const [balance, setBalance] = useState({} as { [key: string]: string })
const provider = useMemo(() => {
if (window.ethereum) {
@ -75,6 +78,7 @@ const Recharge = () => {
const handleExchange = async () => {
if (!provider || loading) return;
let value = inputRef.current?.value;
if (!value) {
Toast.info('請輸入數量');
return;
@ -83,8 +87,10 @@ const Recharge = () => {
setLoading(true);
try {
let isNode = await switchNetWork();
if (!isNode) return;
let data = await handleTransferParams();
if (data == -1) {
// Toast.info('请检查网络是否正常');
setLoading(false);
@ -109,27 +115,55 @@ const Recharge = () => {
Toast.fail('充值失敗');
}
} catch (error) {
console.log(error);
setLoading(false);
}
};
useEffect(() => {
const getBalance = async () => {
if (!provider) return
try {
const balance = {} as { [key: string]: string }
const ps = coinList.map((item, index) => {
let _contract = IERC20__factory.connect(item.address, provider);
balance[item.symbol] = ''
return _contract.balanceOf(walletAddress)
})
const res = await Promise.all(ps)
Array.isArray(res) && res.forEach((item, index) => {
BigNumber.isBigNumber(item) && (balance[coinList[index].symbol] = toFixed2(ethers.utils.formatUnits(item, 18), 4))
})
setBalance(balance)
} catch (error) {
}
}
token && getBalance()
}, [coinList, provider, token, walletAddress])
useEffect(() => {
inputRef.current && (inputRef.current.value = '')
}, [coinIndex])
return (
<div className='recharge plr-2'>
<BackBar title='充值' />
<div className='container row-center mt-2'>
<div className='box plr-2 row-between' onClick={() => push('/choose')}>
<div className='row-items'>
{
currentCoin && <img src={require(`~/assets/${currentCoin.symbol}.png`)} alt="" />
}
<div className='fz-wb-550 ml-1'>{currentCoin && currentCoin.symbol}</div>
</div>
<div>
<i className='iconfont icon-arrow-right-bold'></i>
</div>
<div className='box plr-2 row-items'>
<CoinPicker
setIndex={setCoinIndex}
index={coinIndex}
list={coinList}
/>
</div>
<div className='box-img row-center'>
<div className='box-img row-center' onClick={() => push('/record')}>
<i className='iconfont icon-datijilu fz-22'></i>
</div>
</div>
@ -142,14 +176,11 @@ const Recharge = () => {
<div className='fz-14' style={{ color: '#828284' }}></div>
<div className='mt-1 input-box plr-1'>
<input type="number" placeholder='請輸入數量' ref={inputRef} />
<div className='row-items'>
{
currentCoin && <img src={require(`~/assets/${currentCoin.symbol}.png`)} alt="" />
}
<div className='ml-5px'>{currentCoin && currentCoin.symbol}</div>
{/* <div className='primary-color ml-5px'>最大</div> */}
</div>
<div className='primary fz-14 ml-5px' onClick={() => {
balance[currentCoin.symbol] && (inputRef.current!.value = balance[currentCoin.symbol])
}}>ALL</div>
</div>
<div className='tar mt-5px fz-14 sub-text'>{currentCoin.symbol ? balance[currentCoin.symbol] : 0}</div>
</div>
<div className='mt-3'>
<Button className='button' onClick={handleExchange} loading={loading} loadingType="ball" ></Button>

129
src/pages/record/index.tsx

@ -8,14 +8,15 @@ import BackBar from "~/components/BackBar";
import { Cell, Empty, List, Tabs } from "react-vant";
import { debounce } from 'lodash';
import '~/styles/personal.scss';
import { history_record } from "~/api";
const Record = () => {
const { token } = store.state;
const { push } = useRouter()
const recordTabs = useMemo(() => ['充值', '提现', '收益'], []);
const [recordIndex, setRecordIndex] = useState(0);
const { copyVal } = useCopyLink();
const { push } = useRouter();
const [query, setQuery] = useState([
{ page: 1, page_size: 20 },
{ page: 1, page_size: 20 },
@ -30,8 +31,7 @@ const Record = () => {
const getAssetsRecord = debounce(async () => {
// let res: any = await assets_record({ type: recordIndex + 1, ...query[recordIndex] });
let res: any = {};
let res: any = await history_record({ type: recordIndex + 1, ...query[recordIndex] });
if (res.code === 0) {
if (res.data.length < 20) {
if (assetsRecord[recordIndex].length <= 0) {
@ -62,17 +62,14 @@ const Record = () => {
<RechargeRecord list={assetsRecord[recordIndex]} copy={copyVal} finished={finished[recordIndex]} getData={getAssetsRecord} />,
<WithdrawRecord list={assetsRecord[recordIndex]} copy={copyVal} finished={finished[recordIndex]} getData={getAssetsRecord} />,
<TransferRecord list={assetsRecord[recordIndex]} finished={finished[recordIndex]} getData={getAssetsRecord} />,
<OrderRecord list={assetsRecord[recordIndex]} finished={finished[recordIndex]} getData={getAssetsRecord} />,
<ProfitPositionRecord list={assetsRecord[recordIndex]} finished={finished[recordIndex]} getData={getAssetsRecord} />,
<MonthlyFeeRecord list={assetsRecord[recordIndex]} finished={finished[recordIndex]} getData={getAssetsRecord} />
];
return el[index];
};
useEffect(() => {
// token && assetsRecord[recordIndex].length <= 0 && getAssetsRecord();
// !token && push('/', null, true);
// !token && setFinished([true, true, true, true, true, true]);
token && assetsRecord[recordIndex].length <= 0 && getAssetsRecord();
!token && push('/', null, true);
!token && setFinished([true, true, true, true, true, true]);
}, [recordIndex, token]);
@ -124,30 +121,32 @@ const RechargeRecord = (
return (
<List finished={finished} onLoad={() => getData()} errorText="请求失败,点击重新加载" offset={10}>
{
list.map(item => (
<Cell key={item.id} className="ptb-1">
list.map((item) => (
<Cell key={item.id} className="ptb-1" style={{ background: 'none' }}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div>
<div className='fz-wb-550'>{item.status === -3 ? '激活赠送' : '充值'}</div>
<div className='sub-color fz-14 mt-6px'>{getTime(item.time * 1000)}</div>
<div className='sub-color fz-14'>
{splitAddress(item.order, 10)}
<i className='iconfont icon-fuzhi ml-5px' onClick={() => copy && copy(item.order)}></i>
<div className='fz-wb-550'>{item.name}</div>
<div className='sub-text fz-14 mt-6px'>{getTime(item.time * 1000)}</div>
<div className='sub-text fz-14'>
{splitAddress(item.tx_hash, 10)}
{
item.tx_hash && (
<i className='iconfont icon-fuzhi_copy black ml-5px' onClick={() => copy && copy(item.tx_hash)}></i>
)
}
</div>
</div>
<div className='column-justify-between tae'>
<div className='green-color'>+{item.amount} {item.symbol}</div>
<div
className={`${item.status >= 0 ? 'blue-color' : 'red-color'}`}
>
{item.status !== -3 && (item.status === 3 ? '完成' : item.type >= 0 ? '确认中' : '失败')}
<div className='green'>+{item.amount} {item.symbol}</div>
<div className={`tar ${item.status === 1 && 'green'}`}>
{item.status === 1 ? '完成' : '確認中'}
</div>
</div>
</div>
</Cell>
))
}
</List>
</List >
)
}
@ -159,7 +158,7 @@ const WithdrawRecord = ({ list, finished, getData, copy }: ChildProps) => {
<List finished={finished} onLoad={() => getData()} errorText="请求失败,点击重新加载" offset={10}>
{
list.map((item, index) => (
<Cell className='ptb-1' key={item.id}>
<Cell className='ptb-1' key={item.id} style={{ background: 'none' }}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div>
<div className='fz-wb-550'></div>
@ -196,7 +195,7 @@ const TransferRecord = ({ list, finished, getData }: ChildProps) => {
<List finished={finished} onLoad={() => getData()} errorText="请求失败,点击重新加载" offset={10}>
{
list.map((item, index) => (
<Cell className="ptb-1" key={item.id}>
<Cell className="ptb-1" key={item.id} style={{ background: 'none' }}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div>
<div className='fz-wb-550'> {item.name}</div>
@ -218,86 +217,4 @@ const TransferRecord = ({ list, finished, getData }: ChildProps) => {
)
}
const OrderRecord = (
{ list, finished, getData }: ChildProps
) => {
if (list.length <= 0 || Object.keys(list).length <= 0) return <Empty description="暂无记录" />;
return (
<>
<List finished={finished} onLoad={() => getData()} errorText="请求失败,点击重新加载" offset={10}>
{
list.map(item => (
<Cell className="ptb-1" key={item.id}>
<div className="row-between">
<div className="fz-14">ID {item.order}</div>
<div className="sub-color fz-14">{getTime(item.time * 1000)}</div>
</div>
<div className="row-between mt-6px fz-wb-550">
<div>{item.type === 1 ? '主单盈亏' : '对冲单盈亏'}</div>
<div className={Number(item.amount) >= 0 ? "green-color" : 'red-color'}>{Number(item.amount) >= 0 && '+'}{item.amount} {item.symbol}</div>
</div>
<div className="row-between mt-6px fz-wb-550">
<div></div>
<div className={Number(item.rebate_amount) >= 0 ? "green-color" : 'red-color'}>{Number(item.rebate_amount) >= 0 && '+'}{item.rebate_amount} {item.symbol}</div>
</div>
<div className="row-between mt-6px fz-wb-550">
<div></div>
<div className="red-color">{item.settle_fee} {item.symbol_fee}</div>
</div>
</Cell>
))
}
</List>
</>
)
}
const ProfitPositionRecord = ({ list, getData, finished }: ChildProps) => {
if (list.length <= 0 || Object.keys(list).length <= 0) return <Empty description="暂无记录" />;
return (
<List finished={finished} onLoad={() => getData()} errorText="请求失败,点击重新加载" offset={10}>
{
list.map(item => (
<Cell className="ptb-1" key={item.id}>
<div className="row-between">
<div className="fz-14">ID {item.order}</div>
<div className="sub-color fz-14">{getTime(item.time * 1000)}</div>
</div>
<div className="row-between mt-6px fz-wb-550">
<div>{item.name}</div>
<div className="green-color">{Number(item.amount) >= 0 && '+'}{item.amount} {item.symbol}</div>
</div>
</Cell>
))
}
</List>
)
}
const MonthlyFeeRecord = ({ list, finished, getData }: ChildProps) => {
if (list.length <= 0 || Object.keys(list).length <= 0) return <Empty description="暂无记录" />;
return (
<List finished={finished} onLoad={() => getData()} errorText="请求失败,点击重新加载" offset={10}>
{
list.map(item => (
<Cell className="ptb-1" key={item.id}>
<div className="row-between fz-wb-550">
<div>{item.name}</div>
<div className="red-color">{Number(item.amount) >= 0 && '+'}{item.amount} {item.symbol}</div>
</div>
<div className="fz-14 sub-color">{getTime(item.time * 1000)}</div>
</Cell>
))
}
</List>
)
}
export default observer(Record);

209
src/pages/share/index.tsx

@ -1,32 +1,166 @@
import { Tabs } from 'react-vant'
import '~/styles/share.scss'
import { Popup, Tabs, Toast } from 'react-vant'
import ProductItem from '~/components/ProductItem'
import { MarketNFTData } from '~/types/store'
import store from '~/store'
import { observer } from 'mobx-react'
import { toFixed2 } from '~/utils'
import { useEffect, useMemo, useRef, useState } from 'react'
import { open_page, personal_nft, reset_name, upload_image } from '~/api'
const Share = () => {
const { userInfo, token } = store.state
const [visible, setVisible] = useState(false)
const inputRef = useRef<HTMLInputElement>(null)
const nameRef = useRef<HTMLInputElement>(null)
const [edit, setEdit] = useState(false)
const prevent = useRef(false) //阻止重複點擊
const fileRef = useRef(null as any)
const [tempUrl, setTempUrl] = useState(null as any)
const [nftList, setNftList] = useState([
[], //我的
[],//我喜歡的
] as MarketNFTData[][])
const tabs = useMemo(() => ['我的NFT', '我喜歡的NFT'], [])
const avatarmenuClick = (event: any) => {
const dataType = event.target.dataset.type;
switch (dataType) {
case "photo": //相册
inputRef.current?.click()
break
case "save": //保存
!fileRef.current && Toast.info('請選擇圖片')
fileRef.current && uploader(fileRef.current)
break
case "cancel": //取消
closePopup()
break
}
}
const uploader = async (file: File | null) => {
if (!file) return
setVisible(false)
const res: any = await upload_image(file)
res.code === 0 && await store.getUserInfo()
res.code === 0 && Toast.success('保存成功')
closePopup()
}
const saveName = async () => {
if (nameRef.current?.value === userInfo.name) return
const res: any = await reset_name(nameRef.current!.value)
res.code === 0 && store.getUserInfo()
setEdit(false)
}
const openPages = async () => {
if (prevent.current) return
prevent.current = true
const res: any = await open_page(userInfo.show === 1 ? 2 : 1)
res.code === 0 && store.getUserInfo()
res.code === 0 && Toast.success({
message: userInfo.show === 1 ? '已隐藏' : '已开放',
forbidClick: true
})
prevent.current = false
}
const onChangeImage = (event: React.ChangeEvent<HTMLInputElement>) => {
let file = event.target.files ? event.target.files[0] : null
if (!file) return
fileRef.current = file
const reader = new FileReader()
reader.onloadend = function () {
const base64String = reader.result
setTempUrl(base64String)
}
reader.readAsDataURL(file)
inputRef.current!.value = ''
}
const closePopup = () => {
setVisible(false)
setTempUrl('')
fileRef.current = null
inputRef.current!.value = ''
}
useEffect(() => {
const getData = async () => {
const res: any = await personal_nft(1)
if (res && res.code === 0) {
nftList[0] = res.data
setNftList([...nftList])
}
}
token && getData()
}, [token])
return (
<div className="personal">
<div className="share">
<div className='box'>
<img src={require('~/assets/personal.png')} alt="" className='bg-cover'></img>
<div className='row-center avatar'>
<img src={require('~/assets/avatar.png')} className="img" alt="" />
<img src={tempUrl || userInfo.url} className="img" alt="" onClick={() => {
setVisible(true)
}} />
</div>
</div>
<div className='box-block'></div>
<div className='tac fz-wb-550 mt-1'>IamjackRider</div>
{
token ? (
<div className='row-center mt-1 name-box'>
{
edit ? (
<input type="text" ref={nameRef} onBlur={() => {
setTimeout(() => {
setEdit(false)
}, 100)
}} />
) : (
<div className='fz-wb-550 text-overflow'>{userInfo.name}</div>
)
}
<div className='ml-1'>
{
edit ? (
<i className='iconfont icon-chenggong green fz-18' onClick={saveName} />
) : (
<i className='iconfont icon-bianji' onClick={() => {
setEdit(true)
setTimeout(() => {
nameRef.current!.value = userInfo.name
nameRef.current!.focus()
}, 20)
}} />
)
}
</div>
</div>
) : (
<div className='tac sub-text'></div>
)
}
<div className='row-center'>
<div className='tag fz-wb-550 mt-5px'></div>
<div className='tag fz-wb-550 mt-5px' onClick={openPages}>{userInfo.show === 2 ? '开放我的艺术页面' : '隐藏我的艺术页面'}</div>
</div>
<div className='row-between plr-5 mt-3'>
<div className='tac'>
<div className='fz-20 fz-wb-550'>5</div>
<div className='fz-20 fz-wb-550'>{userInfo.sell}</div>
<div className='mt-8px'></div>
</div>
<div className='tac'>
<div className='fz-20 fz-wb-550'>15</div>
<div className='fz-20 fz-wb-550'>{userInfo.auction}</div>
<div className='mt-8px'></div>
</div>
<div className='tac'>
<div className='fz-20 fz-wb-550'>255 FIL</div>
<div className='fz-20 fz-wb-550'>{toFixed2(userInfo.income, 2)} FIL</div>
<div className='mt-8px'></div>
</div>
</div>
@ -40,32 +174,45 @@ const Share = () => {
animated
swipeable
>
<Tabs.TabPane
title={<div>
<span>NFT</span>
<span className='ml-5px' style={{ color: '#11C0CB' }}>25</span>
</div>}
titleClass='fz-wb-550'
>
<div className='row-between flex-wrap plr-3'>
{Array.from({ length: 10 }).map((_, index) => <ProductItem key={index} />)}
</div>
</Tabs.TabPane>
<Tabs.TabPane
title={<div>
<span>NFT</span>
<span className='ml-5px' style={{ color: '#11C0CB' }}>999+ </span>
</div>}
titleClass='fz-wb-550'
>
<div className='row-between flex-wrap plr-3'>
{Array.from({ length: 10 }).map((_, index) => <ProductItem key={index} />)}
</div>
</Tabs.TabPane>
{
tabs.map((item, index) => (
<Tabs.TabPane
key={index}
title={<div>
<span>{item}</span>
<span className='ml-5px' style={{ color: '#11C0CB' }}>{nftList[index].length}</span>
</div>}
titleClass='fz-wb-550'
>
<div className='row-between flex-wrap plr-3'>
{nftList[index].map((item, index) => <ProductItem key={index} data={item} />)}
</div>
</Tabs.TabPane>
))
}
</Tabs>
</div>
<input type="file" onChange={onChangeImage} multiple={false} accept="image/*" ref={inputRef} style={{ display: 'none' }} />
<Popup
visible={visible}
onClickOverlay={closePopup}
overlayStyle={{ backgroundColor: 'rgba(0,0,0,0)' }}
position='bottom'
className='white'
style={{ backgroundColor: 'rgba(60,60,60,0.9)' }}
>
<div className='fz-18' onClick={avatarmenuClick}>
<div className='p-2 tac'>
<div className='' data-type='photo'></div>
<div className='mt-2' data-type='save'></div>
</div>
<div style={{ height: 2, width: '100%' }} className="background-bg"></div>
<div className='p-2 tac' data-type="cancel"></div>
</div>
</Popup>
</div>
)
}
export default Share
export default observer(Share)

252
src/pages/team/index.tsx

@ -0,0 +1,252 @@
import '~/styles/personal.scss'
import { Button, Divider, List, Overlay, Swiper, SwiperInstance, Toast } from "react-vant"
import BackBar from "~/components/BackBar"
import { useEffect, useRef, useState } from 'react'
import { getTime, splitAddress } from '~/utils'
import { observer } from 'mobx-react'
import store from '~/store'
import { my_invite, team_info } from '~/api'
import useCopyLink from '~/hooks/useCopy'
import { InviteRecordData } from '~/types/store'
import { useRouter } from '~/hooks/useRouter'
import { copy } from '~/utils/copy'
import QRCode from 'qrcode'
const Team = () => {
const { push } = useRouter()
const { token } = store.state
const { copyVal } = useCopyLink()
const [address, setAddress] = useState('')
const [inviteList, setInviteList] = useState([] as InviteRecordData[])
const [finished, setFinished] = useState(true);
const [visible, setVisible] = useState(false);
const [cardState, setCardState] = useState([
[
{ title: '我的推薦人', value: '000000000', id: 1 },
{ title: '級別獎勵', value: '10% 5%', id: 2 },
],
[
{ title: '獎勵金額', value: '0U', id: 4 },
{ title: '直推人數', value: '0人', id: 6 },
{ title: '閒推人數', value: '0人', id: 5 },
],
])
const query = useRef({ page: 1, page_size: 20 })
const getInviteData = async () => {
let res: any = await my_invite(query.current);
if (res && res.code === 0 && res.data) {
if (res.data.length < 20) {
if (inviteList.length <= 0) {
setInviteList(res.data)
} else {
setInviteList([...inviteList, ...res.data])
}
setFinished(true)
return
}
query.current.page = query.current.page + 1
if (inviteList.length <= 0) {
setInviteList(res.data)
} else {
setInviteList([...inviteList, ...res.data])
};
setFinished(false)
}
}
useEffect(() => {
const getData = async () => {
const res: any = await team_info()
if (res && res.code === 0) {
cardState[0][0].value = res.data.inviti_address || '000000000000000'
cardState[1][0].value = res.data.award ? res.data.award + "U" : "0U"
cardState[1][1].value = res.data.direct_count ? res.data.direct_count + '人' : '0人'
cardState[1][2].value = res.data.indirect_count ? res.data.indirect_count + '人' : '0人'
setCardState([...cardState])
setAddress(res.data.address)
}
console.log(res);
}
token && getData()
token && getInviteData();
!token && push('/', null, true)
}, [token])
return (
<div className="plr-2 team">
<BackBar
title='团队'
/>
<Button className="mt-2 button" onClick={() => setVisible(true)}></Button>
<div className="row-between" style={{ alignItems: 'flex-end' }}>
<div className='fz-24 fz-wb-550 mb-6px'></div>
<div className="tar">
<div className="fz-wb-550"></div>
<div className="fz-wb-550 fz-60">10%</div>
</div>
</div>
<div className='card-box'>
{
cardState.map((v, index) => (
<div key={index} className="row-between" style={{ justifyContent: 'space-around' }}>
{
v.map(item => (
<div key={item.id} className={`tac ${index === 1 && 'mt-2'}`}>
<div className='fz-14'>{item.title}</div>
<div className='mt-1 fz-wb-550'>
{item.id === 1 ? splitAddress(item.value) : item.value}
{
item.id === 1 && (
<i className='iconfont icon-fuzhi_copy white ml-5px' onClick={() => copyVal(item.value)}></i>
)
}
</div>
</div>
))
}
</div>
))
}
</div>
<div className='mt-3'>
<div className='fz-18'></div>
<div className='mt-5px'>
<Divider style={{ margin: 0, borderColor: '#2A2C24' }} />
</div>
<div className='row mt-1 tac fz-14'>
<div style={{ flex: 1 }}></div>
<div style={{ flex: 1 }}></div>
<div style={{ flex: 1 }}></div>
</div>
<List
finished={finished}
onLoad={getInviteData}
errorText="請求失敗,點擊重新加載"
finishedText="已經到底了"
offset={10}
>
<div>
{
inviteList.map((item, index) => (
<div className='row mt-2 tac fz-14' key={index}>
<div style={{ flex: 1 }}>{splitAddress(item.address)}</div>
<div style={{ flex: 1 }}></div>
<div style={{ flex: 1 }}>{getTime(item.time * 1000)}</div>
</div>
))
}
</div>
</List>
</div>
{
visible && <ShareModal visible={visible} setVisible={setVisible} address={address} />
}
</div>
)
}
const ShareModal = (
{ visible, setVisible, address }: { visible: boolean, setVisible: Function, address: string }
) => {
const [swiperIndex, setSwiperIndex] = useState(0)
const swiperRef = useRef<SwiperInstance>(null)
const [qrcodeUri, setQrcodeUri] = useState('')
const handleSwiper = () => {
if (swiperIndex === 0) {
swiperRef.current?.swipeNext()
} else {
swiperRef.current?.swipePrev()
}
}
const downloadQrcode = () => {
if (!qrcodeUri) return
const link = document.createElement('a')
link.href = qrcodeUri
link.download = `${Date.now()}.png`
link.click()
link.remove()
}
useEffect(() => {
copy(process.env.REACT_APP_SHARE_LINK + address)
QRCode.toDataURL(process.env.REACT_APP_SHARE_LINK + address, (err, url) => {
if (url) {
setQrcodeUri(url)
}
})
}, [])
return (
<Overlay visible={visible}>
<div className='mt-5 white-color overlay'>
<div className='row-justify-end pr-3'>
<i className='iconfont icon-close fz-36' onClick={() => setVisible(false)}></i>
</div>
<Swiper
ref={swiperRef}
indicator={() => <></>}
onChange={(index) => setSwiperIndex(index)}
initialSwipe={swiperIndex}
touchable={false}
>
<Swiper.Item>
<div className='swiper-height'>
<div className='row-center'>
<div className='mt-5'>
<img src={require('~/assets/share.png')} alt="" className='share-img' />
</div>
</div>
<div className='url mt-2 row-center'>
{splitAddress(process.env.REACT_APP_SHARE_LINK + address, 14)}
<i className='iconfont icon-fuzhi_copy ml-1 fz-24' onClick={() => {
copy(process.env.REACT_APP_SHARE_LINK + address, () => {
Toast.success('复制成功')
})
}}></i>
</div>
<div className='mt-2 tac fz-wb-550 fz-26'></div>
<div className='mt-2 tac'></div>
</div>
</Swiper.Item>
<Swiper.Item>
<div className='swiper-height'>
<div className='row-center mt-5'>
<img src={qrcodeUri} alt="" className='share-img' />
</div>
<div className='url mt-2 row-center'>
<i className='iconfont icon-download ml-1 fz-24' onClick={downloadQrcode}></i>
</div>
<div className='mt-2 tac fz-wb-550 fz-26'></div>
<div className='mt-2 tac'></div>
</div>
</Swiper.Item>
</Swiper>
<div className='row-center' >
<div className='button' onClick={handleSwiper}>
<div className='text-index plr-2'>
<div>
{swiperIndex === 0 ? '切換二維碼' : '切換分享鏈接'}
</div>
<i className='iconfont icon-arrow-right-bold fz-20'></i>
</div>
</div>
</div>
</div>
</Overlay>
)
}
export default observer(Team)

64
src/pages/withdraw/index.tsx

@ -3,33 +3,59 @@ import { observer } from 'mobx-react'
import store from '../../store'
import { useMemo, useRef, useState } from 'react'
import { useRouter } from '../../hooks/useRouter'
import { Button } from 'react-vant'
import { Button, Toast } from 'react-vant'
import BackBar from '~/components/BackBar'
import CoinPicker from '~/components/CoinPicker'
import { user_withdraw } from '~/api'
import { getChainId } from '~/utils'
const Recharge = () => {
const { coinIndex, coinList } = store.state
const { coinList, walletAddress } = store.state
const [coinIndex, setCoinIndex] = useState(0)
const { push } = useRouter()
const inputRef = useRef<HTMLInputElement>(null);
const currentCoin = useMemo(() => coinList[coinIndex], [coinIndex, coinList])
const [loading, setLoading] = useState(false);
const withdraw = async () => {
const amount = inputRef.current?.value
const balance = currentCoin.balance
if (!balance) return
if (!amount) return Toast.fail("請輸入數量")
if (Number(balance) < Number(amount)) return Toast.fail("餘額不足")
setLoading(true)
try {
const chainId = await getChainId()
let res: any = await user_withdraw({
symbol: currentCoin.symbol,
chainId: chainId || -1,
amount
});
if (res.code === 0 || res.code === 101) {
res.code === 0 && Toast.success('提交成功');
setTimeout(() => {
push('/record', { index: 1 }, true)
}, 1000)
};
} catch (error) {
setLoading(false)
}
};
return (
<div className='recharge plr-2'>
<BackBar title='提现' />
<div className='container row-center mt-2'>
<div className='box plr-2 row-between' onClick={() => push('/choose')}>
<div className='row-items'>
{
currentCoin && <img src={require(`~/assets/${currentCoin.symbol}.png`)} alt="" />
}
<div className='fz-wb-550 ml-1'>{currentCoin && currentCoin.symbol}</div>
</div>
<div>
<i className='iconfont icon-arrow-right-bold'></i>
</div>
<div className='box plr-2 row-between'>
<CoinPicker
setIndex={setCoinIndex}
index={coinIndex}
list={coinList}
/>
</div>
<div className='box-img row-center'>
<div className='box-img row-center' onClick={() => push('/record', { index: 1 })}>
<i className='iconfont icon-datijilu fz-22'></i>
</div>
</div>
@ -38,7 +64,7 @@ const Recharge = () => {
<div className='mt-2 white-bg r-1 p-2'>
<div className='fz-14'></div>
<div className='mt-1 input-box plr-1'>
<div className='fz-14'>0xasdasd;lasdljaslkdjklasdjlkasjdlk</div>
<div className='fz-14'>{walletAddress}</div>
</div>
</div>
@ -51,22 +77,22 @@ const Recharge = () => {
<div className='mt-2 white-bg r-1 p-2'>
<div className='fz-14'></div>
<div className='mt-1 input-box plr-1'>
<input />
<input placeholder='請輸入數量' ref={inputRef} />
<div className='row-items'>
<div className='ml-1 fz-14'>FIL</div>
<div className='ml-1 fz-14'>{currentCoin && currentCoin.symbol}</div>
<div className='ml-2 fz-14 primary'></div>
</div>
</div>
<div className='tar sub-text fz-14 mt-5px'>0 USDT</div>
<div className='tar sub-text fz-14 mt-5px'>{currentCoin ? currentCoin.balance : 0} {currentCoin && currentCoin.symbol}</div>
</div>
<div className='mt-2 fz-14'>
<div></div>
<div className='mt-5px'>0.1000 FIL</div>
<div className='mt-5px'>{currentCoin ? currentCoin.withdraw_fee : 0} {currentCoin && currentCoin.symbol}</div>
</div>
<div className='mt-3'>
<Button className='button' loading={loading} loadingType="ball" ></Button>
<Button className='button' loading={loading} loadingType="ball" onClick={withdraw}></Button>
</div>
</div>
</div>

131
src/router/layout/index.tsx

@ -1,17 +1,93 @@
import '~/styles/layout.scss'
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { useRouter } from '~/hooks/useRouter';
import Notify from './Notify';
import RenderRouter from './RenderRouter';
import { tabbarData } from '../routes';
import routes, { tabbarData } from '../routes';
import Navbar from './Navbar';
import Tabbar from './Tabbar';
import { observer } from 'mobx-react';
import store from '~/store';
import useWs from '~/hooks/useWs';
import { ethers } from 'ethers';
import { bind_rmd } from '~/api';
import { AlreadyBind, BindRmd, BindSuccess, DefaultBind, UnLogin, VaildLink } from './ui';
const LayoutRouter = () => {
const { location, push } = useRouter()
const messageWs = useWs('getMessage')
const { token, walletAddress, userInfo, visibleUnLogin } = store.state
const [visible, setVisible] = useState(false)
const [visibleVaildLink, setVisibleVaildLink] = useState(false) //无效的链接
const [visibleAlreadyBind, setVisibleAlreadyBind] = useState(false) //已绑定
const [visibleBindRmd, setVisibleBindRmd] = useState(false) //直接绑定
const [shareAddress, setShareAddress] = useState('')
const [visibleBindSuccess, setVisibleBindSuccess] = useState(false) //綁定成功
const [visibleDefault, setVisibleDefault] = useState(false) //默認綁定
const bindRecommend = async () => {
const res: any = await bind_rmd(shareAddress)
if (res.code === 0) {
setVisibleBindRmd(false)
store.getUserInfo()
setTimeout(() => {
push('/', null, true)
setVisibleBindSuccess(true)
}, 200)
}
}
useEffect(() => {
token && store.getUserInfo()
token && store.getCoinList()
!token && store.resetCoinList()
!token && store.resetUserInfo()
// token && messageWs.connect(token)
// !token && messageWs.disconnect()
}, [token])
useEffect(() => {
const isModal = async () => {
let user = userInfo
let isRouter = routes.find(v => v.path === location.pathname)
const address = location.pathname.substring(1, location.pathname.length)
if (user.is_bound) return
if (isRouter) return
if (Object.keys(user).length <= 0) {
const res: any = await store.getUserInfo()
if (res) {
user = res
}
}
if (!ethers.utils.isAddress(address)) {
// 无效的分享链接
setVisibleVaildLink(true)
return
}
// 地址相同
if (address.toLocaleLowerCase() === walletAddress.toLocaleLowerCase()) return
setShareAddress(address)
// 绑定推荐人
if (!user.is_bound && ethers.utils.isAddress(address)) {
setVisibleBindRmd(true)
return
}
// 已有推荐人
if (user.is_bound) {
setVisibleAlreadyBind(true)
}
}
token && isModal()
!token && setVisibleBindRmd(false)
}, [token, walletAddress])
return (
<div className='layout'>
<Navbar
@ -34,8 +110,57 @@ const LayoutRouter = () => {
)
}
<Notify visible={visible} setVisible={setVisible} />
<div className="ui">
{/* 無效鏈接 */}
{visibleVaildLink && <VaildLink visible={visibleVaildLink} setVisible={setVisibleVaildLink} />}
{/* 已經綁定 */}
{visibleAlreadyBind && (
<AlreadyBind
onClick={() => {
push('/', null, true)
setVisibleAlreadyBind(false)
push('/share')
}}
visible={visibleAlreadyBind}
setVisible={setVisibleAlreadyBind}
/>
)}
{/* 綁定推薦人 */}
{visibleBindRmd && (
<BindRmd
onClick={bindRecommend}
address={shareAddress}
visible={visibleBindRmd}
setVisible={setVisibleBindRmd}
/>
)}
{
visibleUnLogin && (
<UnLogin
visible={visibleUnLogin}
setVisible={() => store.setVisibleUnLogin(false)}
/>
)
}
{visibleDefault && (
<DefaultBind
visible={visibleDefault}
setVisible={() => setVisibleDefault(false)}
onClick={bindRecommend}
/>
)}
{visibleBindSuccess && (
<BindSuccess
visible={visibleBindSuccess}
setVisible={() => setVisibleBindSuccess(false)}
address={shareAddress}
/>
)}
</div>
</div>
);
}
export default LayoutRouter
export default observer(LayoutRouter)

123
src/router/layout/ui.tsx

@ -0,0 +1,123 @@
import { ethers } from "ethers";
import { useRef } from "react";
import { Toast } from "react-vant";
import Modal from "../../components/Modal";
interface UIProps {
visible: boolean,
setVisible: Function,
onClick?: Function,
address?: string
}
export const UnLogin = ({ visible, setVisible }: UIProps) => (
<Modal
title="拒絕訪問"
buttonClick={setVisible}
setVisible={setVisible}
visible={visible}
buttonText="關閉"
>
<div>
<div className="mt-2 fz-14 tac fz-wb-550" style={{ color: '#FF5300' }}></div>
<div className="unlogin-box row-center fz-14"></div>
</div>
</Modal>
)
export const VaildLink = ({ visible, setVisible }: UIProps) => (
<Modal
title="綁定推薦人"
buttonClick={() => setVisible(false)}
setVisible={() => setVisible(false)}
visible={visible}
buttonText="關閉"
>
<div>
<div className="mt-2 fz-14 tac fz-wb-550" style={{ color: '#FF5300' }}></div>
<div className="unlogin-box row-center fz-14"></div>
</div>
</Modal>
)
export const DefaultBind = ({ visible, setVisible, onClick }: UIProps) => {
const addressRefs = useRef<HTMLInputElement>(null)
const confirm = () => {
let value = addressRefs.current?.value
if (!value) return Toast.info('請輸入推薦鏈接!')
let newValue = value?.split('/#/')
if (!ethers.utils.isAddress(newValue[1])) return Toast.info('無效的分享鏈接')
onClick && onClick(newValue[1])
}
return (
<Modal
visible={visible}
setVisible={setVisible}
title="綁定推薦人"
buttonClick={confirm}
buttonText="確認綁定"
>
<div>
<div className="tac mt-2 fz-14"></div>
<input
type="text"
className="default-bind-input"
placeholder="請輸入推薦鏈接"
ref={addressRefs}
/>
</div>
</Modal>
)
}
export const BindRmd = ({ visible, setVisible, address, onClick }: UIProps) => (
<Modal
visible={visible}
setVisible={() => setVisible(false)}
title="綁定推薦人"
buttonClick={() => onClick && onClick(address)}
buttonText="確認綁定"
hiddenCloseIcon
>
<div>
<div className="tac mt-2 fz-14"></div>
<div className="default-bind-input row-center">{address}</div>
</div>
</Modal>
)
export const BindSuccess = ({ visible, setVisible, address }: UIProps) => (
<Modal
visible={visible}
setVisible={setVisible}
title="綁定推薦人"
buttonClick={setVisible}
buttonText="關閉"
hiddenCloseIcon
>
<div>
<div className="tac mt-2 fz-14"></div>
<div className="default-bind-input row-center" style={{ color: '#1BA27A' }}>{address}</div>
</div>
</Modal>
)
export const AlreadyBind = ({ visible, setVisible, onClick }: UIProps) => (
<Modal
title="綁定推薦人"
buttonClick={() => onClick && onClick()}
setVisible={() => setVisible(false)}
visible={visible}
buttonText='查看綁定人'
showCancelButton
showCancelButtonClick={() => setVisible(false)}
>
<div>
<div className="mt-2 fz-14 tac fz-wb-550" style={{ color: '#FF5300' }}></div>
<div className="unlogin-box row-center fz-14"></div>
</div>
</Modal>
)

10
src/router/routes.tsx

@ -9,12 +9,17 @@ const Detail = lazy(() => import("~/pages/detail"));
const Recharge = lazy(() => import("~/pages/recharge"));
const Withdraw = lazy(() => import("~/pages/withdraw"));
const Record = lazy(() => import("~/pages/record"));
const Team = lazy(() => import("~/pages/team"))
const routes = [
{
path: "/",
element: <Home />
},
{
path: '/:id',
component: <Home />
},
{
path: "/product",
element: <Product />,
@ -42,8 +47,11 @@ const routes = [
{
path: "/record",
element: <Record />,
},
{
path: '/team',
element: <Team />
}
] as RouteObject[];
export const tabbarData = [

24
src/store/index.ts

@ -14,6 +14,7 @@ class AppStore implements Store {
coinIndex: 0,
coinList: [] as CoinList[],
userInfo: {} as UserInfo,
visibleUnLogin: false,
};
constructor() {
@ -48,12 +49,15 @@ class AppStore implements Store {
}
async getUserInfo(): Promise<UserInfo> {
const res: any = await accountInfo()
const res: any = await accountInfo();
if (res && res.code === 0) {
this.state.userInfo = res.data
window.sessionStorage.setItem('userInfo', JSON.stringify(res.data))
this.state.userInfo = res.data;
}
return res && res.data
return res && res.data;
}
resetUserInfo() {
this.state.userInfo = {} as UserInfo;
}
/**
@ -73,18 +77,22 @@ class AppStore implements Store {
}
async getCoinList(): Promise<void> {
const res: any = await coin_list()
const res: any = await coin_list();
if (res && res.code === 0 && res.data) {
this.state.coinList = res.data
this.state.coinList = res.data;
}
}
resetCoinList(): void {
this.state.coinList = []
this.state.coinList = [];
}
setCoinIndex(index: number): void {
this.state.coinIndex = index
this.state.coinIndex = index;
}
setVisibleUnLogin(bool: boolean): void {
this.state.visibleUnLogin = bool
}
}

19
src/styles/components.scss

@ -6,8 +6,8 @@
width: 171px;
height: 132px;
margin-left: 5px;
object-fit:cover ;
border-radius: 10px;
background: $button-background;
box-shadow: 8px 8px 20px 0px rgba(0, 0, 0, 0.1);
}
.desc-box{
@ -92,4 +92,21 @@
top: -15px;
}
}
}
.modal-content{
width: 340px;
border-radius: 5px;
background: $page;
padding: 20px;
color:$black;
.modal-button{
position: relative;
width: 130px;
height: 44px;
overflow: hidden;
border-radius: 10px;
background: $button-background;
color: $white;
}
}

27
src/styles/layout.scss

@ -36,9 +36,6 @@
.pages{
height: 100%;
width: 100%;
overflow: hidden;
overflow-y: scroll;
&::-webkit-scrollbar{display: none;}
}
.tabbar{
@ -72,6 +69,30 @@
height: 100px;
width: 100%;
}
.ui{
.unlogin-box{
width: 100%;
height: 32px;
border-radius: 5px;
background-color: $white;
margin-top: 10px;
color: #E45546;
font-size: 14;
}
.default-bind-input{
width: 100%;
border: none;
background-color: $white;
color: $black;
height: 31px;
border-radius: 5px;
text-align: center;
font-size: 12px;
margin-top: 10px;
}
}
}
.rv-popup::-webkit-scrollbar{

89
src/styles/personal.scss

@ -49,8 +49,93 @@
.tabs{
padding: 10px 0px;
.rv-tabs__nav--capsule .rv-tab.rv-tab--active .rv-tab__text {
background-color: #EAEAEA;
color: #727272;
background-color: $primary;
color: $white;
}
}
}
.team{
.mb-6px{
margin-bottom: 8px;
}
.fz-60{
font-size: 60px;
}
.card-box{
width: 100%;
padding: 30px;
background-color: $primary;
margin-top: 10px;
border-radius: 10px;
color: $white;
}
.button{
width: 145px;
height: 45px;
border-radius: 10px;
background-color: $primary;
color: $white;
font-weight: bold;
}
}
.overlay{
color:$white;
.text{
font-family:'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
}
.solid{
flex:5;
width: 100%;
height: 2px;
background-color: rgba(244, 244, 244, 0.3);
}
.share-img{
width: 220px;
height: 220px;
object-fit: cover;
}
.url{
font-size: 18px;
font-family: Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;
text-align: center;
}
.button{
margin-top: 30px;
width: 228px;
height: 52px;
border-radius: 28px;
background: $button-background;
color: $white;
position: relative;
overflow: hidden;
.text-index{
position: relative;
z-index: 1;
display: flex;
justify-content: space-between;
align-items: center;
height: 100%;
font-family:Georgia, 'Times New Roman', Times, serif;
}
&:active{
opacity: 0.85;
}
}
.swiper-height{
// height: 460px;
}
}

3
src/styles/recharge.scss

@ -67,5 +67,8 @@
height: 53px;
background-color: $primary;
border-radius: 16px;
color: $white;
font-size: 18px;
font-weight: bold;
}
}

20
src/styles/share.scss

@ -1,4 +1,4 @@
.personal{
.share{
.box{
@ -28,12 +28,30 @@
}
}
.name-box{
height: 30px;
input{
border:none;
background: none;
font-weight: bold;
max-width: 200px;
text-align: center;
}
}
.box-block{
display: block;
height: 71px;
width: 100%;
}
.text-overflow{
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
}
.tag{
background: linear-gradient(104deg, #1DD0DF -2%, #1DD0DF -2%, #1BEDFF -2%, #14BDEB 108%);
padding: 5px 15px;

2
src/styles/theme.scss

@ -1,4 +1,4 @@
$primary:#1BEDFF;
$primary:#0FC6D4;
$background:#fff;
$primary-text:#000;
$sub-text:#727272;

14
src/types/api.d.ts

@ -9,4 +9,16 @@ interface PerformSignin {
signature: string;
}
export { PerformSNonce, PerformSignin }
interface HTTPWithdrawParams {
amount: string;
chainId: number;
symbol: string;
}
interface HTTPHistoryParams {
page: number;
type: number;
type: number;
}
export { PerformSNonce, PerformSignin, HTTPWithdrawParams, HTTPHistoryParams };

49
src/types/store.d.ts

@ -15,6 +15,15 @@ interface UserInfo {
show: number;
total: string;
url: string;
assets: Assets[];
}
interface Assets {
amount: string;
amount_usdt: string;
id: number;
symbol: string;
name: string;
}
interface CoinList {
@ -26,4 +35,42 @@ interface CoinList {
withdraw_fee: string;
}
export { StoreLocalStorageKey, UserInfo, CoinList };
interface MarketNFTData {
auction_price: string;
award: string;
cast_id: number;
cast_name: string;
cast_show: number;
cast_url: string;
day: number;
end_Time: number;
function: string;
id: number;
mark: number;
name: string;
number: number;
platform: string;
price: string;
sell_id: number;
sell_name: string;
sell_show: number;
sell_url: string;
symbol: string;
time: number;
total_stock: number;
url: string;
}
interface InviteRecordData {
address: string;
time: number;
id: number;
}
export {
StoreLocalStorageKey,
UserInfo,
CoinList,
MarketNFTData,
InviteRecordData,
};

2
src/utils/copy.ts

@ -1,5 +1,5 @@
export function copy(value: string, cb: Function) {
export function copy(value: string, cb?: Function) {
// 动态创建 textarea 标签
const textarea: any = document.createElement('textarea')
// 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域

33
src/utils/index.ts

@ -15,16 +15,23 @@ const splitAddress = (address: string, index?: number) => {
}
};
export const getChainId = async () => {
return toBig(
await window.ethereum.request({
method: "eth_chainId",
})
).toNumber();
};
const switchNetWork = () => {
return new Promise((resolve) => {
return new Promise(async (resolve) => {
if (!window.ethereum) {
resolve(false);
return;
}
if (
toBig(window.ethereum.chainId).toNumber() === 97 ||
toBig(window.ethereum.chainId).toNumber() === 56
) {
let chainId = await getChainId();
if (chainId === 97 || chainId === 56) {
//测试
resolve(true);
return;
@ -72,4 +79,18 @@ const getTime = (value: number) => {
return `${yy}-${mm}-${dd} ${xs}:${ff}`;
};
export { splitAddress, switchNetWork, toThousands, getTime };
const toFixed2 = (val: string, double: number) => {
try {
if (val.indexOf(".") > -1) {
return val.substring(
0,
val.indexOf(".") + (double === 0 ? double : double + 1)
);
}
return val;
} catch (error) {
return "0";
}
};
export { splitAddress, switchNetWork, toThousands, getTime, toFixed2 };

1
src/utils/sign/sign.ts

@ -14,6 +14,7 @@ var signGenerator = (data: any) => {
ptxt += keys[i] + data[keys[i]];
}
ptxt = signkey + ptxt + signkey;
var signval = md5(ptxt).toLowerCase()
return signval;
}

109
yarn.lock

@ -2552,6 +2552,13 @@
resolved "https://registry.npmmirror.com/@types/q/-/q-1.5.8.tgz#95f6c6a08f2ad868ba230ead1d2d7f7be3db3837"
integrity sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==
"@types/qrcode@^1.5.5":
version "1.5.5"
resolved "https://registry.npmmirror.com/@types/qrcode/-/qrcode-1.5.5.tgz#993ff7c6b584277eee7aac0a20861eab682f9dac"
integrity sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==
dependencies:
"@types/node" "*"
"@types/qs@*":
version "6.9.10"
resolved "https://registry.npmmirror.com/@types/qs/-/qs-6.9.10.tgz#0af26845b5067e1c9a622658a51f60a3934d51e8"
@ -3617,7 +3624,7 @@ camelcase-css@^2.0.1:
resolved "https://registry.npmmirror.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
camelcase@^5.3.1:
camelcase@^5.0.0, camelcase@^5.3.1:
version "5.3.1"
resolved "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
@ -3724,6 +3731,15 @@ clean-css@^5.2.2:
dependencies:
source-map "~0.6.0"
cliui@^6.0.0:
version "6.0.0"
resolved "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
dependencies:
string-width "^4.2.0"
strip-ansi "^6.0.0"
wrap-ansi "^6.2.0"
cliui@^7.0.2:
version "7.0.4"
resolved "https://registry.npmmirror.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
@ -4188,6 +4204,11 @@ debug@^3.2.7:
dependencies:
ms "^2.1.1"
decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==
decimal.js@^10.2.1:
version "10.4.3"
resolved "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
@ -4320,6 +4341,11 @@ diff-sequences@^29.6.3:
resolved "https://registry.npmmirror.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921"
integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==
dijkstrajs@^1.0.1:
version "1.0.3"
resolved "https://registry.npmmirror.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz#4c8dbdea1f0f6478bff94d9c49c784d623e4fc23"
integrity sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==
dir-glob@^3.0.1:
version "3.0.1"
resolved "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
@ -4514,6 +4540,11 @@ emojis-list@^3.0.0:
resolved "https://registry.npmmirror.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
encode-utf8@^1.0.3:
version "1.0.3"
resolved "https://registry.npmmirror.com/encode-utf8/-/encode-utf8-1.0.3.tgz#f30fdd31da07fb596f281beb2f6b027851994cda"
integrity sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
@ -5365,7 +5396,7 @@ gensync@^1.0.0-beta.2:
resolved "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
get-caller-file@^2.0.5:
get-caller-file@^2.0.1, get-caller-file@^2.0.5:
version "2.0.5"
resolved "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
@ -7662,6 +7693,11 @@ pkg-up@^3.1.0:
dependencies:
find-up "^3.0.0"
pngjs@^5.0.0:
version "5.0.0"
resolved "https://registry.npmmirror.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb"
integrity sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==
postcss-attribute-case-insensitive@^5.0.2:
version "5.0.2"
resolved "https://registry.npmmirror.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz#03d761b24afc04c09e757e92ff53716ae8ea2741"
@ -8330,6 +8366,16 @@ q@^1.1.2:
resolved "https://registry.npmmirror.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==
qrcode@^1.5.3:
version "1.5.3"
resolved "https://registry.npmmirror.com/qrcode/-/qrcode-1.5.3.tgz#03afa80912c0dccf12bc93f615a535aad1066170"
integrity sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==
dependencies:
dijkstrajs "^1.0.1"
encode-utf8 "^1.0.3"
pngjs "^5.0.0"
yargs "^15.3.1"
qs@6.11.0:
version "6.11.0"
resolved "https://registry.npmmirror.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
@ -8666,6 +8712,11 @@ require-from-string@^2.0.2:
resolved "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
require-main-filename@^2.0.0:
version "2.0.0"
resolved "https://registry.npmmirror.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
requires-port@^1.0.0:
version "1.0.0"
resolved "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
@ -8971,6 +9022,11 @@ serve-static@1.15.0:
parseurl "~1.3.3"
send "0.18.0"
set-blocking@^2.0.0:
version "2.0.0"
resolved "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
set-function-length@^1.1.1:
version "1.1.1"
resolved "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed"
@ -10109,6 +10165,11 @@ which-collection@^1.0.1:
is-weakmap "^2.0.1"
is-weakset "^2.0.1"
which-module@^2.0.0:
version "2.0.1"
resolved "https://registry.npmmirror.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409"
integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==
which-typed-array@^1.1.11, which-typed-array@^1.1.13, which-typed-array@^1.1.9:
version "1.1.13"
resolved "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36"
@ -10308,6 +10369,15 @@ workbox-window@6.6.1:
"@types/trusted-types" "^2.0.2"
workbox-core "6.6.1"
wrap-ansi@^6.2.0:
version "6.2.0"
resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
@ -10347,6 +10417,11 @@ ws@^8.13.0:
resolved "https://registry.npmmirror.com/ws/-/ws-8.15.1.tgz#271ba33a45ca0cc477940f7f200cd7fba7ee1997"
integrity sha512-W5OZiCjXEmk0yZ66ZN82beM5Sz7l7coYxpRkzS+p9PP+ToQry8szKh+61eNktr7EA9DOwvFGhfC605jDHbP6QQ==
ws@^8.16.0:
version "8.16.0"
resolved "https://registry.npmmirror.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4"
integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==
xml-name-validator@^3.0.0:
version "3.0.0"
resolved "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
@ -10357,6 +10432,11 @@ xmlchars@^2.2.0:
resolved "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
y18n@^4.0.0:
version "4.0.3"
resolved "https://registry.npmmirror.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==
y18n@^5.0.5:
version "5.0.8"
resolved "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
@ -10382,11 +10462,36 @@ yaml@^2.3.4:
resolved "https://registry.npmmirror.com/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2"
integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==
yargs-parser@^18.1.2:
version "18.1.3"
resolved "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
dependencies:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs-parser@^20.2.2:
version "20.2.9"
resolved "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
yargs@^15.3.1:
version "15.4.1"
resolved "https://registry.npmmirror.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
dependencies:
cliui "^6.0.0"
decamelize "^1.2.0"
find-up "^4.1.0"
get-caller-file "^2.0.1"
require-directory "^2.1.1"
require-main-filename "^2.0.0"
set-blocking "^2.0.0"
string-width "^4.2.0"
which-module "^2.0.0"
y18n "^4.0.0"
yargs-parser "^18.1.2"
yargs@^16.2.0:
version "16.2.0"
resolved "https://registry.npmmirror.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"

Loading…
Cancel
Save