-
3package.json
-
12src/api/index.ts
-
BINsrc/assets/avatar.png
-
BINsrc/assets/buy-success.png
-
BINsrc/assets/cover.png
-
BINsrc/assets/nft/dragon.jpg
-
BINsrc/assets/nft/兔.jpg
-
BINsrc/assets/nft/牛.jpg
-
BINsrc/assets/nft/狗.jpg
-
BINsrc/assets/nft/猪.jpg
-
BINsrc/assets/nft/猴.jpg
-
BINsrc/assets/nft/羊.jpg
-
BINsrc/assets/nft/虎.jpg
-
BINsrc/assets/nft/蛇.jpg
-
BINsrc/assets/nft/马.jpg
-
BINsrc/assets/nft/鸡.jpg
-
BINsrc/assets/nft/鼠.jpg
-
BINsrc/assets/personal.png
-
91src/hooks/useNotify.ts
-
37src/hooks/useWs.ts
-
27src/router/layout/Navbar.tsx
-
116src/router/layout/Notify.tsx
-
20src/router/layout/index.tsx
-
1src/styles/home.scss
-
45src/styles/layout.scss
-
10src/types/store.d.ts
Before Width: 285 | Height: 287 | Size: 86 KiB |
Before Width: 251 | Height: 294 | Size: 48 KiB After Width: 251 | Height: 294 | Size: 12 KiB |
Before Width: 307 | Height: 320 | Size: 126 KiB |
Before Width: 1080 | Height: 1528 | Size: 326 KiB |
Before Width: 1080 | Height: 1528 | Size: 290 KiB |
Before Width: 1080 | Height: 1528 | Size: 298 KiB |
Before Width: 1080 | Height: 1528 | Size: 308 KiB |
Before Width: 1080 | Height: 1528 | Size: 310 KiB |
Before Width: 1080 | Height: 1528 | Size: 282 KiB |
Before Width: 1080 | Height: 1528 | Size: 308 KiB |
Before Width: 1080 | Height: 1528 | Size: 352 KiB |
Before Width: 1080 | Height: 1528 | Size: 259 KiB |
Before Width: 1080 | Height: 1528 | Size: 281 KiB |
Before Width: 1080 | Height: 1528 | Size: 269 KiB |
Before Width: 1080 | Height: 1528 | Size: 300 KiB |
Before Width: 860 | Height: 433 | Size: 342 KiB After Width: 860 | Height: 433 | Size: 94 KiB |
@ -0,0 +1,91 @@ |
|||||
|
import { useRef, useState } from "react"; |
||||
|
import { Toast } from "react-vant"; |
||||
|
import { clear_msg, read_all_msg } from "~/api"; |
||||
|
import { MessageType } from "~/types/store"; |
||||
|
import signGenerator from "~/utils/sign/sign"; |
||||
|
|
||||
|
const useNotify = (path: string, isNotity: boolean) => { |
||||
|
console.log(isNotity); |
||||
|
|
||||
|
const baseUrl = `${process.env.REACT_APP_WS_URL}/api/v1/${path}`; |
||||
|
const ws = useRef<any>(null); |
||||
|
const filterMessage = useRef([] as MessageType[]); |
||||
|
const [messages, setMessages] = useState([] as MessageType[]); |
||||
|
|
||||
|
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}×tamp=${timestamp}` |
||||
|
); |
||||
|
|
||||
|
ws.current.onmessage = (event: any) => { |
||||
|
if ( |
||||
|
event && |
||||
|
event.data && |
||||
|
event.origin === process.env.REACT_APP_WS_URL |
||||
|
) { |
||||
|
let message = JSON.parse(event.data) as MessageType; |
||||
|
let item = filterMessage.current.find((v) => v.id === message.id); |
||||
|
if (item) return; |
||||
|
filterMessage.current.push(message); |
||||
|
setMessages((_) => [...filterMessage.current]); |
||||
|
// 如果打開了消息列表,則推送直接設置為已讀
|
||||
|
if (isNotity) { |
||||
|
readAllMsg(); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
// 斷開連接
|
||||
|
const disconnect = () => { |
||||
|
ws.current && ws.current.close(); |
||||
|
ws.current = null; |
||||
|
resetMessages(); |
||||
|
}; |
||||
|
|
||||
|
// 重置消息列表
|
||||
|
const resetMessages = () => { |
||||
|
filterMessage.current = []; |
||||
|
setMessages([]); |
||||
|
}; |
||||
|
|
||||
|
// 刪除所有消息
|
||||
|
const deleteAllMsg = async () => { |
||||
|
if (messages.length <= 0) return; |
||||
|
const res: any = await clear_msg(); |
||||
|
if (res && res.code === 0) { |
||||
|
Toast.success("清除成功"); |
||||
|
resetMessages(); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 設置所有消息為已讀
|
||||
|
const readAllMsg = async () => { |
||||
|
const res: any = await read_all_msg(); |
||||
|
if (res && res.code === 0) { |
||||
|
// 把消息改爲已讀
|
||||
|
filterMessage.current.forEach((item) => { |
||||
|
item.status = 2; |
||||
|
}); |
||||
|
setMessages([...filterMessage.current]); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
return { |
||||
|
connect, |
||||
|
disconnect, |
||||
|
messages, |
||||
|
resetMessages, |
||||
|
readAllMsg, |
||||
|
deleteAllMsg, |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
export default useNotify; |
@ -1,37 +0,0 @@ |
|||||
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}×tamp=${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; |
|
@ -1,66 +1,84 @@ |
|||||
import '~/styles/layout.scss' |
import '~/styles/layout.scss' |
||||
import { Button, Divider, Popup } from "react-vant" |
|
||||
|
import { Button, Dialog, Divider, Popup } from "react-vant" |
||||
|
import { MessageType } from '~/types/store' |
||||
|
|
||||
interface NotifyProps { |
interface NotifyProps { |
||||
visible: boolean, |
visible: boolean, |
||||
setVisible: Function |
|
||||
|
setVisible: Function, |
||||
|
messages: MessageType[], |
||||
|
deleteAllMsg: Function |
||||
} |
} |
||||
|
|
||||
const Notify = (props: NotifyProps) => { |
const Notify = (props: NotifyProps) => { |
||||
|
|
||||
const { visible, setVisible } = props |
|
||||
|
const { visible, setVisible, messages, deleteAllMsg } = props |
||||
|
|
||||
const data = [ |
|
||||
{ title: '提醒', color: '#F96900', imgName: 'register-success', desc: '您挂单的“生肖唐彩-狗”NFT, @Miner 出价 4,153.00 USDT,您挂单的“生肖唐彩-狗”NFT, @Miner 出价 4,153.00 USDT,您挂单的“生肖唐彩-狗”NFT, @Miner 出价 4,153.00 USDT' }, |
|
||||
{ title: '注册成功', color: '#11C0CB', imgName: 'warn', desc: '恭喜,您已成功注册,9527NFT数字交易平台.' }, |
|
||||
] |
|
||||
|
const confirmDelete = async () => { |
||||
|
if (messages.length <= 0) return |
||||
|
Dialog.alert({ |
||||
|
showCancelButton: true, |
||||
|
showConfirmButton: true, |
||||
|
title: '提示', |
||||
|
message: '確定清除所有歷史消息嗎?', |
||||
|
confirmButtonText: '確定', |
||||
|
confirmButtonColor: '#0FC6D4', |
||||
|
onConfirm: () => deleteAllMsg() |
||||
|
}) |
||||
|
} |
||||
|
|
||||
return ( |
return ( |
||||
<Popup |
|
||||
visible={visible} |
|
||||
onClose={() => setVisible(false)} |
|
||||
position='right' |
|
||||
style={{ |
|
||||
height: '100%', |
|
||||
width: '70%', |
|
||||
borderTopLeftRadius: 20, |
|
||||
borderBottomLeftRadius: 20, |
|
||||
boxShadow: '8px 8px 20px 0px rgba(0, 0, 0, 0.1)' |
|
||||
}} |
|
||||
overlayStyle={{ backgroundColor: 'rgba(0,0,0,0.05)' }} |
|
||||
> |
|
||||
<div className="p-2"> |
|
||||
<div className="fz-wb-550">消息提醒</div> |
|
||||
<Divider className="" style={{ borderColor: '#EEE' }} /> |
|
||||
<div> |
|
||||
{ |
|
||||
data.map((item, index) => ( |
|
||||
<div className="notify-box row mt-2" key={index}> |
|
||||
<img src={require(`~/assets/${item.imgName}.png`)} className="img" alt="" /> |
|
||||
<div className='ml-2'> |
|
||||
<div className='fz-14 fz-wb-550' style={{ color: item.color }}>{item.title}</div> |
|
||||
<div className='fz-12 sub-text mt-4px'>{item.desc}</div> |
|
||||
|
<> |
||||
|
<Popup |
||||
|
visible={visible} |
||||
|
onClose={() => setVisible(false)} |
||||
|
position='right' |
||||
|
style={{ |
||||
|
height: '100%', |
||||
|
width: '70%', |
||||
|
borderTopLeftRadius: 20, |
||||
|
borderBottomLeftRadius: 20, |
||||
|
boxShadow: '8px 8px 20px 0px rgba(0, 0, 0, 0.1)' |
||||
|
}} |
||||
|
overlayStyle={{ backgroundColor: 'rgba(0,0,0,0.05)' }} |
||||
|
> |
||||
|
<div className="notify"> |
||||
|
<div className='header plr-2'> |
||||
|
<div style={{ width: '100%' }}> |
||||
|
<div className="fz-wb-550">消息提醒</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div className='notify-box'> |
||||
|
{ |
||||
|
messages.map((item) => ( |
||||
|
<div className="notify-item-box row mt-2" key={item.id}> |
||||
|
<img src={item.url} className="img" alt="" /> |
||||
|
<div className='ml-2'> |
||||
|
<div className='fz-14 fz-wb-550' style={{ color: item.color }}>{item.title}</div> |
||||
|
<div className='fz-12 sub-text mt-4px' style={{ wordBreak: 'break-word' }}>{item.message}</div> |
||||
|
</div> |
||||
</div> |
</div> |
||||
</div> |
|
||||
)) |
|
||||
} |
|
||||
</div> |
|
||||
<div className='mt-5 row-center'> |
|
||||
<Button |
|
||||
round |
|
||||
size="small" |
|
||||
className='fz-wb-550' |
|
||||
color='#EEEEEE' |
|
||||
> |
|
||||
<div className='black'>清除所有</div> |
|
||||
</Button> |
|
||||
</div> |
|
||||
|
)) |
||||
|
} |
||||
|
|
||||
<div className='close'> |
|
||||
<i className='iconfont icon-right-double-arrow fz-22 sub-text' onClick={() => setVisible(false)}></i> |
|
||||
|
<div className='mtb-3 row-center'> |
||||
|
<Button |
||||
|
round |
||||
|
size="small" |
||||
|
className='fz-wb-550' |
||||
|
color='#EEEEEE' |
||||
|
onClick={() => confirmDelete()} |
||||
|
> |
||||
|
<div className='black'>清除所有</div> |
||||
|
</Button> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div className='close'> |
||||
|
<i className='iconfont icon-right-double-arrow fz-22 sub-text' onClick={() => setVisible(false)}></i> |
||||
|
</div> |
||||
</div> |
</div> |
||||
</div> |
|
||||
</Popup> |
|
||||
|
</Popup> |
||||
|
|
||||
|
</> |
||||
) |
) |
||||
} |
} |
||||
|
|