-
Notifications
You must be signed in to change notification settings - Fork 902
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #50 from guanweiwang/main
feat: add immediate consultation
- Loading branch information
Showing
14 changed files
with
612 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { useState, useEffect } from "react"; | ||
import { Message, Modal } from "@/components"; | ||
import { Box, TextField, Typography, Button } from "@mui/material"; | ||
|
||
function Consultation() { | ||
const [text, setText] = useState(""); | ||
const [wrongPhoneNumber, setWrongPhoneNumber] = useState(false); | ||
const [consultOpen, setConsultOpen] = useState(false); | ||
|
||
const consultHandler = () => { | ||
const valid = /^1[3-9]\d{9}$/.test(text); | ||
setWrongPhoneNumber(!valid); | ||
if (!valid) { | ||
Message.error("手机号格式不正确"); | ||
return; | ||
} | ||
fetch("https://leads.chaitin.net/api/trial", { | ||
// fetch('http://116.62.230.26:8999/api/trial', { // 测试用地址 | ||
method: "POST", | ||
mode: "cors", | ||
headers: { "Content-Type": "application/json" }, | ||
body: JSON.stringify({ | ||
phone: text, | ||
platform_source: "product-official-site", | ||
product_source: "safeline-ce", | ||
source_detail: "来自雷池社区版官网", | ||
}), | ||
}) | ||
.then((d) => d.json()) | ||
.then((d) => { | ||
if (d.code == 0) { | ||
Message.success("提交成功"); | ||
} else { | ||
Message.error("提交失败"); | ||
} | ||
setConsultOpen(false); | ||
}); | ||
}; | ||
|
||
const textHandler = (v: string) => { | ||
setText(v); | ||
}; | ||
|
||
useEffect(() => { | ||
if (!consultOpen) { | ||
setText(""); | ||
} | ||
}, [consultOpen]); | ||
|
||
return ( | ||
<> | ||
<Button | ||
variant="outlined" | ||
sx={{ | ||
width: 200, | ||
my: 2, | ||
"&:hover": { | ||
borderColor: "primary.main", | ||
backgroundColor: "primary.main", | ||
color: "#fff", | ||
}, | ||
}} | ||
onClick={() => setConsultOpen(true)} | ||
> | ||
立即咨询 | ||
</Button> | ||
<Modal | ||
open={consultOpen} | ||
onCancel={() => setConsultOpen(false)} | ||
title="咨询企业版" | ||
okText="提交" | ||
onOk={consultHandler} | ||
sx={{ width: 600 }} | ||
> | ||
<Box sx={{}}> | ||
<div style={{ margin: "10px 0 15px" }}> | ||
<TextField | ||
error={wrongPhoneNumber} | ||
fullWidth | ||
size="small" | ||
label="手机号" | ||
helperText={ | ||
wrongPhoneNumber ? "手机号格式不正确" : "请输入您的手机号" | ||
} | ||
variant="outlined" | ||
value={text} | ||
onChange={(e) => textHandler(e.target.value)} | ||
/> | ||
</div> | ||
<Typography> | ||
我们将在工作时间 2 小时内联系您,您的手机号不会用于其他目的 | ||
</Typography> | ||
{/* <Typography id='modal-modal-description' sx={{ mt: 2 }}> | ||
Duis mollis, est non commodo luctus, nisi erat porttitor ligula. | ||
</Typography> */} | ||
咨询企业版 | ||
</Box> | ||
</Modal> | ||
</> | ||
); | ||
} | ||
|
||
export default Consultation; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import React, { type FC, useRef, useEffect } from 'react' | ||
|
||
import { Alert as MAlert, type AlertColor } from '@mui/material' | ||
|
||
interface AlertProps { | ||
duration?: number | ||
onClose?(key: React.Key): void | ||
noticeKey: React.Key | ||
content?: React.ReactNode | ||
severity: AlertColor | ||
} | ||
|
||
const Alert: FC<AlertProps> = (props) => { | ||
const { duration, severity, content, noticeKey, onClose } = props | ||
const closeTimer = useRef<number | null>(null) | ||
|
||
const startCloseTimer = () => { | ||
if (duration) { | ||
closeTimer.current = window.setTimeout(() => { | ||
close() | ||
}, duration * 1000) | ||
} | ||
} | ||
|
||
const clearCloseTimer = () => { | ||
if (closeTimer.current) { | ||
clearTimeout(closeTimer.current) | ||
closeTimer.current = null | ||
} | ||
} | ||
|
||
const close = (e?: React.MouseEvent<HTMLAnchorElement>) => { | ||
if (e) { | ||
e.stopPropagation() | ||
} | ||
clearCloseTimer() | ||
if (onClose) { | ||
onClose(noticeKey) | ||
} | ||
} | ||
|
||
useEffect(() => { | ||
startCloseTimer() | ||
return () => { | ||
clearCloseTimer() | ||
} | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, []) | ||
|
||
return ( | ||
<MAlert severity={severity} sx={{ mb: '10px' }}> | ||
{content} | ||
</MAlert> | ||
) | ||
} | ||
|
||
export default Alert |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import React, { useState, forwardRef, useImperativeHandle } from "react"; | ||
|
||
import { Snackbar, Box, type AlertColor } from "@mui/material"; | ||
|
||
import { ThemeProvider } from "@/components"; | ||
import { render, unmount } from "@/utils"; | ||
|
||
import Alert from "./Alert"; | ||
|
||
export interface Notice { | ||
key?: React.Key; | ||
content?: React.ReactNode; | ||
severity: AlertColor; | ||
onClose?: () => void; | ||
} | ||
|
||
interface MessageProps {} | ||
|
||
let seed = 0; | ||
const now = Date.now(); | ||
|
||
function getUuid() { | ||
const id = seed; | ||
seed += 1; | ||
return `ctMessage_${now}_${id}`; | ||
} | ||
|
||
// eslint-disable-next-line react/display-name | ||
const Message = forwardRef<any, MessageProps>((props, ref) => { | ||
const [notices, setNotices] = useState<Notice[]>([]); | ||
const add = (notice: Notice) => { | ||
const key = notice.key ?? getUuid(); | ||
setNotices((state) => { | ||
state.push({ ...notice, key }); | ||
return [...state]; | ||
}); | ||
}; | ||
|
||
const remove = (key: React.Key) => { | ||
setNotices((state) => state.filter((s) => s.key !== key)); | ||
}; | ||
|
||
useImperativeHandle(ref, () => ({ | ||
add, | ||
remove, | ||
})); | ||
|
||
return ( | ||
<ThemeProvider> | ||
<Snackbar open anchorOrigin={{ vertical: "top", horizontal: "center" }}> | ||
<Box> | ||
{notices.map((item) => { | ||
const alertProps = { | ||
...item, | ||
noticeKey: item.key!, | ||
onClose: (noticeKey: React.Key) => { | ||
remove(noticeKey); | ||
}, | ||
}; | ||
return <Alert {...alertProps} key={item.key} />; | ||
})} | ||
</Box> | ||
</Snackbar> | ||
</ThemeProvider> | ||
); | ||
}); | ||
|
||
// @ts-ignore | ||
Message.newInstance = (properties: MessageProps, callback) => { | ||
const { ...props } = properties || {}; | ||
const div = document?.createElement("div"); | ||
document.body.appendChild(div); | ||
let called = false; | ||
function ref(notification: any) { | ||
if (called) { | ||
return; | ||
} | ||
called = true; | ||
callback({ | ||
notice(noticeProps: MessageProps) { | ||
notification.add(noticeProps); | ||
}, | ||
removeNotice(key: React.Key) { | ||
notification.remove(key); | ||
}, | ||
component: notification, | ||
destroy() { | ||
unmount(div); | ||
if (div.parentNode) { | ||
div.parentNode.removeChild(div); | ||
} | ||
}, | ||
}); | ||
} | ||
setTimeout(() => { | ||
render(<Message {...props} ref={ref} />, div); | ||
}); | ||
}; | ||
|
||
export default Message; |
Oops, something went wrong.