fix: bug修复

This commit is contained in:
landaiqing
2024-07-24 00:59:28 +08:00
parent 8fa1c50fb3
commit 190fda426c
16 changed files with 1512 additions and 1301 deletions

View File

@@ -156,7 +156,10 @@ export const getUserInfoApi = (userId: string): any => {
export const updateUserInfo = (data: any): any => { export const updateUserInfo = (data: any): any => {
return web.request({ return web.request({
url: "/auth/auth/user/update", url: "/auth/auth/user/update",
method: "get", method: "post",
headers: {
"Content-Type": "application/json;charset=UTF-8",
},
data: data, data: data,
}); });
}; };
@@ -195,3 +198,29 @@ export const logout = (userId: any): any => {
}, },
}); });
}; };
/**
* 检查用户名是否已存在
* @param userName
*/
export const checkUserName = (userName: any): any => {
return web.request({
url: "/auth/auth/user/checkUserName",
method: "post",
params: {
userName: userName,
},
});
};
/**
* 获取最近分享
* @param userId
*/
export const getRecentShare = (userId: any): any => {
return web.request({
url: "/share/share/detail/getRecentShare",
method: "get",
params: {
userId: userId,
},
});
};

View File

@@ -29,10 +29,7 @@ const FileUpload = (props: any) => {
const uploadFile = async (options: any) => { const uploadFile = async (options: any) => {
const { onSuccess, onError, file }: { onSuccess: any; onError: any; file: any } = options; const { onSuccess, onError, file }: { onSuccess: any; onError: any; file: any } = options;
if ( if (store.getUploadFileBucket() === null || store.getUploadFileStorage() === null) {
store.getUploadFilePath() === null ||
(store.getUploadFileBucket() === null && store.getUploadFileStorage() === null)
) {
message.open({ message.open({
content: "请选择存储桶和存储路径", content: "请选择存储桶和存储路径",
type: "error", type: "error",

View File

@@ -339,7 +339,7 @@ const MainHome: React.FC = observer(() => {
color: "coral", color: "coral",
fontWeight: "bolder", fontWeight: "bolder",
}}> }}>
{storageCount} {storageCount || 0}
</span> </span>
} }
/ /
@@ -400,7 +400,7 @@ const MainHome: React.FC = observer(() => {
color: "lightblue", color: "lightblue",
fontWeight: "bolder", fontWeight: "bolder",
}}> }}>
{bucketCount} {bucketCount || 0}
</span> </span>
} }
/ /
@@ -467,7 +467,7 @@ const MainHome: React.FC = observer(() => {
color: "orange", color: "orange",
marginLeft: 5, marginLeft: 5,
}}> }}>
{uploadFile} {uploadFile || 0}
</span> </span>
</Flex> </Flex>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
@@ -483,7 +483,7 @@ const MainHome: React.FC = observer(() => {
color: "green", color: "green",
marginLeft: 5, marginLeft: 5,
}}> }}>
{downloadFile} {downloadFile || 0}
</span> </span>
</Flex> </Flex>
</Flex> </Flex>
@@ -531,7 +531,7 @@ const MainHome: React.FC = observer(() => {
color: "orange", color: "orange",
marginLeft: 5, marginLeft: 5,
}}> }}>
{uploadFlow} {uploadFlow || 0}
</span> </span>
</Flex> </Flex>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
@@ -547,7 +547,7 @@ const MainHome: React.FC = observer(() => {
color: "green", color: "green",
marginLeft: 5, marginLeft: 5,
}}> }}>
{downloadFlow} {downloadFlow || 0}
</span> </span>
</Flex> </Flex>
</Flex> </Flex>

View File

@@ -31,7 +31,7 @@ import { cyan, generate, green, presetPalettes, red } from "@ant-design/colors";
type Presets = Required<ColorPickerProps>["presets"][number]; type Presets = Required<ColorPickerProps>["presets"][number];
const ShareAdd: React.FunctionComponent = observer(() => { const ShareAdd: React.FunctionComponent = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const divRef = useRef(null); const divRef = useRef(null);
const [form] = Form.useForm(); const [form] = Form.useForm();
@@ -373,5 +373,5 @@ const ShareAdd: React.FunctionComponent = observer(() => {
</div> </div>
</> </>
); );
}); };
export default observer(ShareAdd); export default observer(ShareAdd);

View File

@@ -8,6 +8,8 @@ import {
HeartOutlined, HeartOutlined,
InfoCircleOutlined, InfoCircleOutlined,
LeftOutlined, LeftOutlined,
LikeOutlined,
StarOutlined,
TagsOutlined, TagsOutlined,
WarningOutlined, WarningOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
@@ -22,10 +24,10 @@ const { Paragraph } = Typography;
import like from "@/assets/icons/like.svg"; import like from "@/assets/icons/like.svg";
import favorite from "@/assets/icons/favorite.svg"; import favorite from "@/assets/icons/favorite.svg";
import useStore from "@/utils/store/useStore.tsx"; import useStore from "@/utils/store/useStore.tsx";
import { addLikeDetail, getShareDetail } from "@/api/share"; import { addLikeDetail, deletedLikeDetail, getShareDetail } from "@/api/share";
import StorageIcon from "@/constant/stroage-icon.ts"; import StorageIcon from "@/constant/stroage-icon.ts";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { addFavorites } from "@/api/user"; import { addFavorites, deleteFavorites } from "@/api/user";
const ShareDetail: React.FunctionComponent = () => { const ShareDetail: React.FunctionComponent = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const store = useStore("share"); const store = useStore("share");
@@ -44,18 +46,20 @@ const ShareDetail: React.FunctionComponent = () => {
} }
async function addLike() { async function addLike() {
const data: any = { const data: any = {
circleId: store.getCircleId(),
userId: userId, userId: userId,
detailId: params.id, detailId: params.id,
}; };
addLikeDetail(data).then(); addLikeDetail(data).then();
} }
// async function delLike() { async function delLike() {
// const data: any = { const data: any = {
// userId: userId, circleId: store.getCircleId(),
// detailId: params.id, userId: userId,
// }; detailId: params.id,
// deletedLikeDetail(data).then(); };
// } deletedLikeDetail(data).then();
}
async function setFavorites() { async function setFavorites() {
const data: any = { const data: any = {
userId: userId, userId: userId,
@@ -68,6 +72,7 @@ const ShareDetail: React.FunctionComponent = () => {
type: "success", type: "success",
content: "收藏成功", content: "收藏成功",
}); });
getDetail().then();
} else { } else {
message.open({ message.open({
type: "error", type: "error",
@@ -76,6 +81,27 @@ const ShareDetail: React.FunctionComponent = () => {
} }
}); });
} }
async function cancelFavorites() {
const data: any = {
userId: userId,
detailId: params.id,
circleId: store.getCircleId(),
};
deleteFavorites(data).then((res: any) => {
if (res && res.success) {
message.open({
type: "success",
content: "取消成功",
});
getDetail().then();
} else {
message.open({
type: "error",
content: "取消失败",
});
}
});
}
useEffect(() => { useEffect(() => {
getDetail().then(); getDetail().then();
@@ -109,7 +135,7 @@ const ShareDetail: React.FunctionComponent = () => {
color: "gray", color: "gray",
overflow: "hidden", overflow: "hidden",
}}> }}>
{detail.nickname} {detail.nickname || "unknown"}
</span> </span>
</Flex> </Flex>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
@@ -119,7 +145,7 @@ const ShareDetail: React.FunctionComponent = () => {
fontSize: 12, fontSize: 12,
color: "gray", color: "gray",
}}> }}>
{detail.likesCount} {detail.likesCount || 0}
</span> </span>
</Flex> </Flex>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
@@ -129,7 +155,7 @@ const ShareDetail: React.FunctionComponent = () => {
fontSize: 12, fontSize: 12,
color: "gray", color: "gray",
}}> }}>
{detail.commentCount} {detail.commentCount || 0}
</span> </span>
</Flex> </Flex>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
@@ -139,7 +165,7 @@ const ShareDetail: React.FunctionComponent = () => {
fontSize: 12, fontSize: 12,
color: "gray", color: "gray",
}}> }}>
{detail.views} {detail.views || 0}
</span> </span>
</Flex> </Flex>
</Flex> </Flex>
@@ -218,7 +244,7 @@ const ShareDetail: React.FunctionComponent = () => {
color: "#1677FF", color: "#1677FF",
fontSize: 16, fontSize: 16,
}}> }}>
{url.typeName}
</span> </span>
</div> </div>
<ExportOutlined <ExportOutlined
@@ -277,7 +303,7 @@ const ShareDetail: React.FunctionComponent = () => {
Array.from(detail.tags).map((tag: any, index: number) => { Array.from(detail.tags).map((tag: any, index: number) => {
return ( return (
<div key={index}> <div key={index}>
<Tag bordered={false} color={getRandomColor()}> <Tag bordered={false} color={"#" + detail.color}>
{tag.tagName} {tag.tagName}
</Tag> </Tag>
</div> </div>
@@ -290,21 +316,54 @@ const ShareDetail: React.FunctionComponent = () => {
align={"center"} align={"center"}
justify={"center"} justify={"center"}
style={{ height: 50 }}> style={{ height: 50 }}>
<Avatar {detail.isLike === true ? (
className={styles.like_icon} <>
src={like as any} <Tooltip title={"取消点赞"} placement={"bottom"}>
onClick={() => { <Avatar
addLike().then(); className={styles.like_icon}
}} src={like as any}
size={"large"}></Avatar> onClick={() => {
{/*isFavor*/} delLike().then();
<Avatar }}
className={styles.favtorie_icon} size={"large"}></Avatar>
src={favorite as any} </Tooltip>
onClick={() => { </>
setFavorites().then(); ) : (
}} <>
size={"large"}></Avatar> <Tooltip title={"点赞"} placement={"bottom"}>
<LikeOutlined
className={styles.detail_link_btn}
onClick={() => {
addLike().then();
}}></LikeOutlined>
</Tooltip>
</>
)}
{detail.isFavor === true ? (
<>
<Tooltip title={"取消收藏"} placement={"bottom"}>
<Avatar
className={styles.favtorie_icon}
src={favorite as any}
onClick={() => {
cancelFavorites().then();
}}
size={"large"}></Avatar>
</Tooltip>
</>
) : (
<>
<Tooltip title={"收藏"} placement={"bottom"}>
<StarOutlined
onClick={() => {
setFavorites().then();
}}
className={styles.detail_link_btn}
/>
</Tooltip>
</>
)}
</Flex> </Flex>
</ProCard> </ProCard>
</div> </div>

View File

@@ -44,33 +44,38 @@ const Comment = observer((props: any) => {
const userStore = useStore("user"); const userStore = useStore("user");
const userId: any = userStore.getUserId(); const userId: any = userStore.getUserId();
async function listComments() { async function listComments() {
setComment([]);
listComment(params.id, userId).then((res: any) => { listComment(params.id, userId).then((res: any) => {
console.log(res);
if (res && res.success && res.data) { if (res && res.success && res.data) {
setComment([]);
setComment(res.data); setComment(res.data);
setLoading(false); setLoading(false);
} }
}); });
} }
async function addLike(commentId: any) { async function addLike(commentId: any) {
setComment([]);
const data: any = { const data: any = {
detailId: params.id,
userId: userId, userId: userId,
commentId: commentId, commentId: commentId,
}; };
addLikeComment(data).then(); addLikeComment(data).then();
listComments().then();
} }
async function delLike(commentId: any) { async function delLike(commentId: any) {
setComment([]);
const data: any = { const data: any = {
detailId: params.id,
userId: userId, userId: userId,
commentId: commentId, commentId: commentId,
}; };
deletedLikeComment(data).then(); deletedLikeComment(data).then();
listComments().then();
} }
async function getCommentHot() { async function getCommentHot() {
setComment([]);
listCommentHot(params.id, userId).then((res: any) => { listCommentHot(params.id, userId).then((res: any) => {
if (res && res.success && res.data) { if (res && res.success && res.data) {
setComment([]);
setComment(res.data); setComment(res.data);
setLoading(false); setLoading(false);
} }
@@ -85,7 +90,7 @@ const Comment = observer((props: any) => {
} }
async function addComments(data: any) { async function addComments(data: any) {
addComment(data).then((res: any) => { addComment(data).then((res: any) => {
if (res && res.success && res.data) { if (res && res.success) {
message.open({ message.open({
content: "评论成功", content: "评论成功",
type: "success", type: "success",
@@ -108,7 +113,7 @@ const Comment = observer((props: any) => {
} }
async function addReplies(data: any) { async function addReplies(data: any) {
addReply(data).then((res: any) => { addReply(data).then((res: any) => {
if (res && res.success && res.data) { if (res && res.success) {
message.open({ message.open({
content: "回复成功", content: "回复成功",
type: "success", type: "success",
@@ -132,7 +137,7 @@ const Comment = observer((props: any) => {
setIsReplyComment(isReplyComment === index ? null : index); setIsReplyComment(isReplyComment === index ? null : index);
}; };
useEffect(() => { useEffect(() => {
listComments().then(); getCommentHot().then();
getAllCommentAndReply(params.id).then(); getAllCommentAndReply(params.id).then();
}, []); }, []);
return ( return (
@@ -264,99 +269,136 @@ const Comment = observer((props: any) => {
<Flex <Flex
vertical={false} vertical={false}
align={"center"} align={"center"}
style={{ justify={"space-between"}>
marginTop: 10, <Flex
width: "100%", vertical={false}
}}> align={"center"}
<span
style={{ style={{
fontSize: 13, marginTop: 10,
color: "grey", width: "100%",
}}> }}>
{item.createdTime} <span
</span> style={{
<Flex fontSize: 13,
vertical={false} color: "grey",
align={"center"}> }}>
{item.isLike === false ? ( {item.createdTime}
<LikeOutlined </span>
onClick={() => { <Flex
delLike( vertical={false}
item.id, align={"center"}>
).then(); {item.isLike ===
}} true ? (
className={ <LikeFilled
styles.like_icon onClick={() => {
} delLike(
/> item.id,
) : ( ).then();
<LikeFilled }}
className={ style={{
styles.like_icon color: "red",
} }}
/>
) : (
<LikeOutlined
className={
styles.like_icon
}
onClick={() => {
addLike(
item.id,
).then();
}}
/>
)}
<span
style={{ style={{
color: "red", fontSize: 13,
}} color: "grey",
}}>
{item.likes}
</span>
</Flex>
<Flex
vertical={false}
align={"center"}>
<CommentOutlined
onClick={() => { onClick={() => {
addLike( setReply([]);
item.id, if (item.id) {
).then(); replyComment(
}} item.id,
/> ).then(
)} () => {
<span handleExpandReplyCommentClick(
style={{ index,
fontSize: 13, );
color: "grey", },
}}>
{item.likes}
</span>
</Flex>
<Flex
vertical={false}
align={"center"}>
<CommentOutlined
onClick={() => {
setReply([]);
if (item.id) {
replyComment(
item.id,
).then(() => {
handleExpandReplyCommentClick(
index,
); );
}); }
}}
className={
styles.comment_icon
} }
}} />
className={ <span
styles.comment_icon style={{
} fontSize: 13,
/> color: "grey",
<span }}>
style={{ {item.replyCount}
fontSize: 13, </span>
color: "grey", </Flex>
}}> <Flex
{item.replyCount} vertical={false}
</span> align={"center"}>
<Button
type="text"
size={"small"}
onClick={() => {
handleExpandClick(
index,
);
}}
style={{
fontSize: 13,
color: "grey",
}}>
</Button>
</Flex>
</Flex> </Flex>
<Flex <Flex
vertical={false} vertical={false}
align={"center"}> align={"center"}>
<Button <span
type="text"
size={"small"}
onClick={() => {
handleExpandClick(
index,
);
}}
style={{ style={{
textWrap: "nowrap",
fontSize: 12,
color: "grey",
textAlign: "end",
}}>
{item.location ||
"unknown"}
</span>
<span
style={{
marginLeft: 10,
fontSize: 13, fontSize: 13,
color: "grey", color: "grey",
}}> }}>
{item.browser ||
</Button> "unknown"}
</span>
<span
style={{
marginLeft: 10,
fontSize: 13,
color: "grey",
}}>
{item.browserVersion ||
"unknown"}
</span>
</Flex> </Flex>
</Flex> </Flex>
</ProCard> </ProCard>
@@ -573,62 +615,97 @@ const Comment = observer((props: any) => {
vertical={ vertical={
false false
} }
style={{ align={
marginTop: 10, "center"
width: "100%", }
}}> justify={
<span "space-between"
style={{ }>
fontSize: 13,
color: "grey",
}}>
{
replyItem.createdTime
}
</span>
<Flex <Flex
vertical={ vertical={
false false
} }
align={ style={{
"center" marginTop: 10,
}> width: "100%",
{replyItem.isLike === }}>
false ? (
<LikeOutlined
onClick={() => {
delLike(
replyItem.id,
).then();
}}
className={
styles.like_icon
}
/>
) : (
<LikeFilled
className={
styles.like_icon
}
style={{
color: "red",
}}
onClick={() => {
addLike(
replyItem.id,
).then();
}}
/>
)}
<span <span
style={{ style={{
fontSize: 13, fontSize: 13,
color: "grey", color: "grey",
}}> }}>
{ {
replyItem.likes replyItem.createdTime
} }
</span> </span>
<Flex
vertical={
false
}
align={
"center"
}>
{replyItem.isLike ===
false ? (
<LikeOutlined
onClick={() => {
addLike(
replyItem.id,
).then();
}}
className={
styles.like_icon
}
/>
) : (
<LikeFilled
className={
styles.like_icon
}
style={{
color: "red",
}}
onClick={() => {
delLike(
replyItem.id,
).then();
}}
/>
)}
<span
style={{
fontSize: 13,
color: "grey",
}}>
{
replyItem.likes
}
</span>
</Flex>
<Flex
vertical={
false
}
align={
"center"
}>
<Button
type="text"
size={
"small"
}
onClick={() => {
handleExpandReplyReplyClick(
replyItem.id,
);
}}
style={{
fontSize: 13,
color: "grey",
}}>
</Button>
</Flex>
</Flex> </Flex>
<Flex <Flex
vertical={ vertical={
@@ -637,22 +714,36 @@ const Comment = observer((props: any) => {
align={ align={
"center" "center"
}> }>
<Button <span
type="text"
size={
"small"
}
onClick={() => {
handleExpandReplyReplyClick(
replyItem.id,
);
}}
style={{ style={{
textWrap:
"nowrap",
fontSize: 12,
color: "grey",
textAlign:
"end",
}}>
{replyItem.location ||
"unknown"}
</span>
<span
style={{
marginLeft: 10,
fontSize: 13, fontSize: 13,
color: "grey", color: "grey",
}}> }}>
{replyItem.browser ||
</Button> "unknown"}
</span>
<span
style={{
marginLeft: 10,
fontSize: 13,
color: "grey",
}}>
{replyItem.browserVersion ||
"unknown"}
</span>
</Flex> </Flex>
</Flex> </Flex>
</ProCard> </ProCard>

View File

@@ -21,3 +21,10 @@
width: 50px; width: 50px;
height: auto; height: auto;
} }
.detail_link_btn{
font-size: 30px;
}
.detail_link_btn:hover{
font-size: 35px;
color: red;
}

View File

@@ -1,7 +1,7 @@
/** @format */ /** @format */
import { ProCard } from "@ant-design/pro-components"; import { ProCard } from "@ant-design/pro-components";
import { Avatar, Button, Flex, Input, List, Skeleton, Tag } from "antd"; import { Avatar, Button, Empty, Flex, Input, List, Skeleton, Tag } from "antd";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import styles from "./index.module.less"; import styles from "./index.module.less";
@@ -25,24 +25,7 @@ export default observer(() => {
} }
}); });
} }
// async function cancelFavorites(id: any) {
// const data: any = {
// id: id,
// };
// deleteFavorites(data).then((res: any) => {
// if (res && res.success) {
// message.open({
// type: "success",
// content: "取消成功",
// });
// } else {
// message.open({
// type: "error",
// content: "取消失败",
// });
// }
// });
// }
useEffect(() => { useEffect(() => {
getMyShare().then(); getMyShare().then();
}, []); }, []);
@@ -75,110 +58,128 @@ export default observer(() => {
</div> </div>
</ProCard> </ProCard>
<ProCard bordered={false} boxShadow={false}> <ProCard bordered={false} boxShadow={false}>
<Skeleton loading={loading} active={true} paragraph={{ rows: 14 }}> {data.length === 0 ? (
<List <Empty description={"暂无数据"}></Empty>
dataSource={data} ) : (
header={ <>
<> <Skeleton loading={loading} active={true} paragraph={{ rows: 14 }}>
<h4></h4> <List
</> dataSource={data}
} header={
renderItem={(item: any) => ( <>
<List.Item key={item.id}> <h4></h4>
<List.Item.Meta </>
avatar={<Avatar src={item.icon} />} }
title={ renderItem={(item: any) => (
<Flex vertical={false} align={"center"}> <List.Item key={item.id}>
<Link to={"/main/share/detail/" + item.id}> <List.Item.Meta
{item.title} avatar={<Avatar src={item.icon} />}
</Link> title={
{item.tags && <Flex vertical={false} align={"center"}>
Array.from(item.tags).map( <Link to={"/main/share/detail/" + item.id}>
(tag: any, index: number) => { {item.title}
return ( </Link>
{item.tags &&
Array.from(item.tags).map(
(tag: any, index: number) => {
return (
<Flex
vertical={false}
align={"center"}
key={index}>
<Tag
bordered={false}
color={
"#" + tag.color
}
style={{
marginLeft: 10,
}}>
{tag.tagName}
</Tag>
</Flex>
);
},
)}
</Flex>
}
description={
<>
<Flex
vertical={false}
justify={"space-between"}
align={"center"}>
{item.description}
<Flex
vertical={false}
align={"center"}
justify={"space-between"}
style={{ width: "300px" }}>
<Flex <Flex
vertical={false} vertical={false}
align={"center"} align={"center"}>
key={index}> <Avatar
<Tag src={item.avatar as any}
bordered={false} size={"small"}
color={"#" + tag.color} />
style={{ marginLeft: 10 }}> <span
{tag.tagName} style={{
</Tag> fontSize: 12,
color: "gray",
overflow: "hidden",
}}>
{item.nickname}
</span>
</Flex> </Flex>
); <Flex
}, vertical={false}
)} align={"center"}>
</Flex> <HeartOutlined />
} <span
description={ style={{
<> fontSize: 12,
<Flex color: "gray",
vertical={false} }}>
justify={"space-between"} {item.likesCount}
align={"center"}> </span>
{item.description} </Flex>
<Flex <Flex
vertical={false} vertical={false}
align={"center"} align={"center"}>
justify={"space-between"} <CommentOutlined />
style={{ width: "300px" }}> <span
<Flex vertical={false} align={"center"}> style={{
<Avatar fontSize: 12,
src={item.avatar as any} color: "gray",
size={"small"} }}>
/> {item.commentCount}
<span </span>
style={{ </Flex>
fontSize: 12, <Flex
color: "gray", vertical={false}
overflow: "hidden", align={"center"}>
}}> <EyeOutlined
{item.nickname} style={{ color: "gray" }}
</span> />{" "}
<span
style={{
fontSize: 12,
color: "gray",
}}>
{item.views}
</span>
</Flex>
</Flex>
</Flex> </Flex>
<Flex vertical={false} align={"center"}> </>
<HeartOutlined /> }
<span />
style={{ </List.Item>
fontSize: 12, )}
color: "gray", />
}}> </Skeleton>
{item.likesCount} </>
</span> )}
</Flex>
<Flex vertical={false} align={"center"}>
<CommentOutlined />
<span
style={{
fontSize: 12,
color: "gray",
}}>
{item.commentCount}
</span>
</Flex>
<Flex vertical={false} align={"center"}>
<EyeOutlined
style={{ color: "gray" }}
/>{" "}
<span
style={{
fontSize: 12,
color: "gray",
}}>
{item.views}
</span>
</Flex>
</Flex>
</Flex>
</>
}
/>
</List.Item>
)}
/>
</Skeleton>
</ProCard> </ProCard>
</div> </div>
</> </>

View File

@@ -1,7 +1,7 @@
/** @format */ /** @format */
import { ProCard } from "@ant-design/pro-components"; import { ProCard } from "@ant-design/pro-components";
import { Avatar, Button, Flex, Input, List, Skeleton, Tag } from "antd"; import { Avatar, Button, Empty, Flex, Input, List, Skeleton, Tag } from "antd";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import styles from "./index.module.less"; import styles from "./index.module.less";
@@ -58,110 +58,128 @@ export default observer(() => {
</div> </div>
</ProCard> </ProCard>
<ProCard bordered={false} boxShadow={false}> <ProCard bordered={false} boxShadow={false}>
<Skeleton loading={loading} active={true} paragraph={{ rows: 14 }}> {data.length === 0 ? (
<List <Empty description={"暂无数据"}></Empty>
dataSource={data} ) : (
header={ <>
<> <Skeleton loading={loading} active={true} paragraph={{ rows: 14 }}>
<h4></h4> <List
</> dataSource={data}
} header={
renderItem={(item: any) => ( <>
<List.Item key={item.id}> <h4></h4>
<List.Item.Meta </>
avatar={<Avatar src={item.icon} />} }
title={ renderItem={(item: any) => (
<Flex vertical={false} align={"center"}> <List.Item key={item.id}>
<Link to={"/main/share/detail/" + item.id}> <List.Item.Meta
{item.title} avatar={<Avatar src={item.icon} />}
</Link> title={
{item.tags && <Flex vertical={false} align={"center"}>
Array.from(item.tags).map( <Link to={"/main/share/detail/" + item.id}>
(tag: any, index: number) => { {item.title}
return ( </Link>
{item.tags &&
Array.from(item.tags).map(
(tag: any, index: number) => {
return (
<Flex
vertical={false}
align={"center"}
key={index}>
<Tag
bordered={false}
color={
"#" + tag.color
}
style={{
marginLeft: 10,
}}>
{tag.tagName}
</Tag>
</Flex>
);
},
)}
</Flex>
}
description={
<>
<Flex
vertical={false}
justify={"space-between"}
align={"center"}>
{item.description}
<Flex
vertical={false}
align={"center"}
justify={"space-between"}
style={{ width: "300px" }}>
<Flex <Flex
vertical={false} vertical={false}
align={"center"} align={"center"}>
key={index}> <Avatar
<Tag src={item.avatar as any}
bordered={false} size={"small"}
color={"#" + tag.color} />
style={{ marginLeft: 10 }}> <span
{tag.tagName} style={{
</Tag> fontSize: 12,
color: "gray",
overflow: "hidden",
}}>
{item.nickname}
</span>
</Flex> </Flex>
); <Flex
}, vertical={false}
)} align={"center"}>
</Flex> <HeartOutlined />
} <span
description={ style={{
<> fontSize: 12,
<Flex color: "gray",
vertical={false} }}>
justify={"space-between"} {item.likesCount}
align={"center"}> </span>
{item.description} </Flex>
<Flex <Flex
vertical={false} vertical={false}
align={"center"} align={"center"}>
justify={"space-between"} <CommentOutlined />
style={{ width: "300px" }}> <span
<Flex vertical={false} align={"center"}> style={{
<Avatar fontSize: 12,
src={item.avatar as any} color: "gray",
size={"small"} }}>
/> {item.commentCount}
<span </span>
style={{ </Flex>
fontSize: 12, <Flex
color: "gray", vertical={false}
overflow: "hidden", align={"center"}>
}}> <EyeOutlined
{item.nickname} style={{ color: "gray" }}
</span> />{" "}
<span
style={{
fontSize: 12,
color: "gray",
}}>
{item.views}
</span>
</Flex>
</Flex>
</Flex> </Flex>
<Flex vertical={false} align={"center"}> </>
<HeartOutlined /> }
<span />
style={{ </List.Item>
fontSize: 12, )}
color: "gray", />
}}> </Skeleton>
{item.likesCount} </>
</span> )}
</Flex>
<Flex vertical={false} align={"center"}>
<CommentOutlined />
<span
style={{
fontSize: 12,
color: "gray",
}}>
{item.commentCount}
</span>
</Flex>
<Flex vertical={false} align={"center"}>
<EyeOutlined
style={{ color: "gray" }}
/>{" "}
<span
style={{
fontSize: 12,
color: "gray",
}}>
{item.views}
</span>
</Flex>
</Flex>
</Flex>
</>
}
/>
</List.Item>
)}
/>
</Skeleton>
</ProCard> </ProCard>
</div> </div>
</> </>

View File

@@ -1,46 +1,37 @@
/** @format */ /** @format */
import { FunctionComponent, useEffect, useState } from "react"; import { FunctionComponent, useEffect, useState } from "react";
import { Avatar, Card, Empty, Flex, List, Skeleton } from "antd"; import { Avatar, Card, Empty, Flex, List, Skeleton } from "antd";
import { import { BankOutlined, BulbOutlined, EnvironmentOutlined } from "@ant-design/icons";
BankOutlined,
BulbOutlined,
EnvironmentOutlined,
} from "@ant-design/icons";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { ProCard } from "@ant-design/pro-components"; import { ProCard } from "@ant-design/pro-components";
import Meta from "antd/es/card/Meta"; import Meta from "antd/es/card/Meta";
import { Link, useNavigate } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import { getAllStorage } from "@/api/oss"; import { getAllStorage } from "@/api/oss";
import StorageIcon from "@/constant/stroage-icon.ts"; import StorageIcon from "@/constant/stroage-icon.ts";
import { getUserInfoApi } from "@/api/user"; import { getRecentShare, getUserInfoApi } from "@/api/user";
import useStore from "@/utils/store/useStore.tsx"; import useStore from "@/utils/store/useStore.tsx";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
const UserInfo: FunctionComponent = observer(() => { const UserInfo: FunctionComponent = observer(() => {
const navigate = useNavigate(); const navigate = useNavigate();
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [loadingStorage, setLoadingStorage] = useState(true);
const [userStorage, setUserStorage] = useState([]); const [userStorage, setUserStorage] = useState([]);
const [recentShare, setRecentShare] = useState([]);
const [userInfo, setUserInfo] = useState<any>({} as any); const [userInfo, setUserInfo] = useState<any>({} as any);
const store = useStore("user"); const store = useStore("user");
const userId: any = store.getUserId(); const userId: any = store.getUserId();
const data = [
{
title: "Ant Design Title 1",
},
{
title: "Ant Design Title 2",
},
{
title: "Ant Design Title 3",
},
{
title: "Ant Design Title 4",
},
];
async function getUserStorage() { async function getUserStorage() {
const res: any = await getAllStorage(userId); const res: any = await getAllStorage(userId);
if (res && res.success && res.data) { if (res && res.success && res.data) {
setUserStorage(res.data); setUserStorage(res.data);
setLoadingStorage(false);
}
}
async function getUserRecentShare() {
const res: any = await getRecentShare(userId);
if (res && res.success && res.data) {
setRecentShare(res.data);
setLoading(false); setLoading(false);
} }
} }
@@ -53,6 +44,7 @@ const UserInfo: FunctionComponent = observer(() => {
useEffect(() => { useEffect(() => {
getUserInfo().then(); getUserInfo().then();
getUserStorage().then(); getUserStorage().then();
getUserRecentShare().then();
}, []); }, []);
return ( return (
<> <>
@@ -79,81 +71,92 @@ const UserInfo: FunctionComponent = observer(() => {
<div className={styles.user_info_center_content}> <div className={styles.user_info_center_content}>
<ProCard <ProCard
bordered bordered
style={{ maxWidth: "64%" }} style={{ maxWidth: "100%" }}
title="我的存储商" title="我的存储商"
boxShadow boxShadow
extra={<Link to={"/main/setting"}></Link>}> extra={<Link to={"/main/setting"}></Link>}>
<Flex vertical={false} align={"center"} justify={"space-between"} wrap={true}> <Flex vertical={false} align={"center"} justify={"space-between"} wrap={true}>
<Skeleton loading={loading} active> {userStorage && userStorage.length === 0 ? (
{userStorage.map((item: any, index: number) => { <Empty description={"暂无数据"}></Empty>
return ( ) : (
<> <>
<Card <Skeleton loading={loadingStorage} active>
key={index} {userStorage.map((item: any, index: number) => {
onClick={() => { return (
navigate(`/main/setting/${item.ossType}`); <div key={index}>
}} <Card
style={{ width: 350, marginTop: 16, marginLeft: 10 }} onClick={() => {
hoverable={true}> navigate(`/main/setting/${item.ossType}`);
<Meta }}
avatar={<Avatar src={StorageIcon[item.ossType]} />} style={{
title={item.name} width: 350,
description={ marginTop: 16,
<> marginLeft: 10,
<span> {item.configCount}</span> }}
<span style={{ marginLeft: 10 }}> hoverable={true}>
{item.bucketCount} <Meta
</span> avatar={
</> <Avatar
}></Meta> src={StorageIcon[item.ossType]}
</Card> />
</> }
); title={item.name}
})} description={
</Skeleton> <>
<span>
{" "}
{item.configCount}
</span>
<span style={{ marginLeft: 10 }}>
{item.bucketCount}
</span>
</>
}></Meta>
</Card>
</div>
);
})}
</Skeleton>
</>
)}
</Flex> </Flex>
</ProCard> </ProCard>
<ProCard bordered style={{ maxWidth: "34%" }} title="我的存储桶" boxShadow> {/*<ProCard bordered style={{ maxWidth: "34%" }} title="我的存储桶" boxShadow>*/}
<Skeleton loading={loading} active> {/* <Skeleton loading={loading} active>*/}
<Card style={{ width: 300, marginTop: 16 }} hoverable={true}> {/* <Card style={{ width: 300, marginTop: 16 }} hoverable={true}>*/}
<Meta {/* <Meta*/}
avatar={ {/* avatar={*/}
<Avatar src="https://api.dicebear.com/7.x/miniavs/svg?seed=1" /> {/* <Avatar src="https://api.dicebear.com/7.x/miniavs/svg?seed=1" />*/}
} {/* }*/}
title="Card title" {/* title="Card title"*/}
description="This is the description" {/* description="This is the description"*/}
/> {/* />*/}
</Card> {/* </Card>*/}
</Skeleton> {/* </Skeleton>*/}
</ProCard> {/*</ProCard>*/}
</div> </div>
<div className={styles.user_info_bottom_content}> <div className={styles.user_info_bottom_content}>
<ProCard <ProCard bordered style={{ maxWidth: "64%" }} title="最近动态" boxShadow>
bordered {recentShare && recentShare.length === 0 ? (
style={{ maxWidth: "64%" }} <Empty description={"暂无数据"}></Empty>
title="最近动态" ) : (
boxShadow <Skeleton loading={loading} active avatar>
extra={<Link to={"#"}></Link>}> <List
<Skeleton loading={loading} active avatar> itemLayout="horizontal"
<List dataSource={recentShare}
itemLayout="horizontal" renderItem={(item: any) => (
dataSource={data} <List.Item>
renderItem={(item, index) => ( <List.Item.Meta
<List.Item> avatar={<Avatar src={userInfo.avatar} />}
<List.Item.Meta title={item.title}
avatar={ description={item.date}
<Avatar />
src={`https://api.dicebear.com/7.x/miniavs/svg?seed=${index}`} </List.Item>
/> )}
} />
title={<a href="https://ant.design">{item.title}</a>} </Skeleton>
description="Ant Design, a design language for background applications, is refined by Ant UED Team" )}
/>
</List.Item>
)}
/>
</Skeleton>
</ProCard> </ProCard>
<ProCard bordered style={{ maxWidth: "34%" }} title="站内通知" boxShadow> <ProCard bordered style={{ maxWidth: "34%" }} title="站内通知" boxShadow>
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty> <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty>

View File

@@ -33,6 +33,7 @@ const UserSetting: FunctionComponent = observer(() => {
const userId: any = store.getUserId(); const userId: any = store.getUserId();
async function getUserInfo() { async function getUserInfo() {
getUserInfoApi(userId).then((res: any) => { getUserInfoApi(userId).then((res: any) => {
console.log(res);
if (res && res.success && res.data) { if (res && res.success && res.data) {
setUserInfo(res.data); setUserInfo(res.data);
setLoading(false); setLoading(false);
@@ -40,8 +41,8 @@ const UserSetting: FunctionComponent = observer(() => {
}); });
} }
type FieldType = { type FieldType = {
nickname?: string; nickName?: string;
location?: string; // location?: string;
introduce?: string; introduce?: string;
gender?: string; gender?: string;
company?: string; company?: string;
@@ -64,7 +65,7 @@ const UserSetting: FunctionComponent = observer(() => {
} }
const onFinish: FormProps<FieldType>["onFinish"] = (values) => { const onFinish: FormProps<FieldType>["onFinish"] = (values) => {
const data: any = { const data: any = {
userId: userId, id: userId,
...values, ...values,
}; };
updateUserInfo(data).then((res: any) => { updateUserInfo(data).then((res: any) => {
@@ -92,24 +93,22 @@ const UserSetting: FunctionComponent = observer(() => {
getUserInfo().then(); getUserInfo().then();
}, []); }, []);
const prefixSelector = ( const prefixSelector = (
<Form.Item name="prefix" noStyle> <Select style={{ width: 90 }}>
<Select style={{ width: 90 }}> <Select.Option value="https://">https://</Select.Option>
<Select.Option value="https://">https://</Select.Option> <Select.Option value="http://">http://</Select.Option>
<Select.Option value="http://">http://</Select.Option> </Select>
</Select>
</Form.Item>
); );
const ProFormText = (props: any) => { // const ProFormText = (props: any) => {
return ( // return (
<ProForm.Item {...props} style={{ height: "10px" }}> // <ProForm.Item {...props} style={{ height: "10px" }}>
<ProFormCascader // <ProFormCascader
request={async () => city} // request={async () => city}
width="md" // width="md"
name="location" // name="location"
disabled={disable}></ProFormCascader> // disabled={disable}></ProFormCascader>
</ProForm.Item> // </ProForm.Item>
); // );
}; // };
useEffect(() => {}, []); useEffect(() => {}, []);
const TabItems = [ const TabItems = [
{ {
@@ -142,7 +141,7 @@ const UserSetting: FunctionComponent = observer(() => {
{/*</Form.Item>*/} {/*</Form.Item>*/}
<Form.Item<FieldType> <Form.Item<FieldType>
label="昵称" label="昵称"
name="nickname" name="nickName"
rules={[ rules={[
{ {
type: "string", type: "string",
@@ -167,12 +166,12 @@ const UserSetting: FunctionComponent = observer(() => {
<Select.Option value="UNKNOWN"></Select.Option> <Select.Option value="UNKNOWN"></Select.Option>
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item<FieldType> {/*<Form.Item<FieldType>*/}
label="地区" {/* label="地区"*/}
name="location" {/* name="location"*/}
rules={[{ required: true, message: "请输入地区!" }]}> {/* rules={[{ required: true, message: "请输入地区!" }]}>*/}
<ProFormText /> {/* <ProFormText />*/}
</Form.Item> {/*</Form.Item>*/}
<Form.Item<FieldType> label="公司" name="company"> <Form.Item<FieldType> label="公司" name="company">
<Input allowClear disabled={disable} /> <Input allowClear disabled={disable} />
</Form.Item> </Form.Item>
@@ -240,7 +239,7 @@ const UserSetting: FunctionComponent = observer(() => {
type="text" type="text"
onClick={async () => { onClick={async () => {
const data: any = { const data: any = {
userId: userId, id: userId,
password: password, password: password,
}; };
await updateUser(data); await updateUser(data);
@@ -264,7 +263,7 @@ const UserSetting: FunctionComponent = observer(() => {
type="text" type="text"
onClick={async () => { onClick={async () => {
const data: any = { const data: any = {
userId: userId, id: userId,
phone: phone, phone: phone,
}; };
await updateUser(data); await updateUser(data);
@@ -288,7 +287,7 @@ const UserSetting: FunctionComponent = observer(() => {
type="text" type="text"
onClick={async () => { onClick={async () => {
const data: any = { const data: any = {
userId: userId, id: userId,
email: email, email: email,
}; };
await updateUser(data); await updateUser(data);
@@ -323,14 +322,14 @@ const UserSetting: FunctionComponent = observer(() => {
<Flex vertical={true} justify={"flex-start"}> <Flex vertical={true} justify={"flex-start"}>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
<span style={{ color: "grey" }}></span> <span style={{ color: "grey" }}></span>
<span style={{ width: 130 }}>{userInfo.userName}</span> <span style={{ width: 200 }}>{userInfo.userName}</span>
</Flex> </Flex>
<Flex <Flex
vertical={false} vertical={false}
align={"center"} align={"center"}
style={{ marginTop: 20 }}> style={{ marginTop: 20 }}>
<span style={{ color: "grey" }}>ID</span> <span style={{ color: "grey" }}>ID</span>
<span style={{ width: 130 }}>{userInfo.id}</span> <span style={{ width: 200 }}>{userInfo.id}</span>
</Flex> </Flex>
</Flex> </Flex>
<Flex vertical={true} style={{ marginLeft: 50 }}> <Flex vertical={true} style={{ marginLeft: 50 }}>

View File

@@ -4,7 +4,7 @@ import Request from "./request";
const web: Request = new Request({ const web: Request = new Request({
baseURL: import.meta.env.VITE_APP_BASE_API, baseURL: import.meta.env.VITE_APP_BASE_API,
timeout: 10000, // timeout: 10000,
}); });
export default web; export default web;

View File

@@ -16,6 +16,8 @@ import { logout } from "@/api/user";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import useStore from "@/utils/store/useStore.tsx"; import useStore from "@/utils/store/useStore.tsx";
import { getUserMenuPermission } from "@/api/user"; import { getUserMenuPermission } from "@/api/user";
import { clearStorage, getStorageFromKey, removeStorageFromKey } from "@/utils/localStorage/config.ts";
import localforage from "localforage";
const Layout = () => { const Layout = () => {
const location = useLocation(); const location = useLocation();
@@ -75,7 +77,14 @@ const Layout = () => {
label: "退出登录", label: "退出登录",
onClick: () => { onClick: () => {
logout(store.getUserId()); logout(store.getUserId());
navigate("/login"); clearStorage();
store.setToken("");
store.setUserId("");
store.setAvatar("");
store.setNickName("");
setTimeout(() => {
navigate("/login");
}, 1000);
}, },
}, },
], ],

View File

@@ -1,375 +1,373 @@
/** @format */ /** @format */
import { LockOutlined, MobileOutlined, SafetyOutlined, WechatOutlined } from "@ant-design/icons"; import { LockOutlined, MobileOutlined, SafetyOutlined, WechatOutlined } from "@ant-design/icons";
import { CaptFieldRef, ProFormCaptcha, ProFormText } from "@ant-design/pro-components"; import { CaptFieldRef, ProFormCaptcha, ProFormText } from "@ant-design/pro-components";
import { Alert, Button, ConfigProvider, Form, Image, message, Space, Spin, Tabs } from "antd"; import { Alert, Button, ConfigProvider, Form, Image, message, Space, Spin, Tabs } from "antd";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { TinyColor } from "@ctrl/tinycolor"; import { TinyColor } from "@ctrl/tinycolor";
import logo from "@/assets/images/logo.png"; import logo from "@/assets/images/logo.png";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import FooterComponent from "@/components/Footer"; import FooterComponent from "@/components/Footer";
import { createClientId, findPassword, generateQRCode, getClientToken, getSms } from "@/api/user"; import { createClientId, findPassword, generateQRCode, getClientToken, getSms } from "@/api/user";
import RotateCaptcha, { CaptchaInstance, type TicketInfoType } from "react-rotate-captcha"; import RotateCaptcha, { CaptchaInstance, type TicketInfoType } from "react-rotate-captcha";
import { get, load } from "@/api/captcha"; import { get, load } from "@/api/captcha";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import useStore from "@/utils/store/useStore.tsx"; import useStore from "@/utils/store/useStore.tsx";
import { setStorage } from "@/utils/localStorage/config.ts"; import { setStorage } from "@/utils/localStorage/config.ts";
import { TimerManager } from "timer-manager-lib"; import { TimerManager } from "timer-manager-lib";
type LoginType = "phone"; type LoginType = "phone";
export default observer(() => { export default observer(() => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const captchaRef = useRef<CaptFieldRef | null | undefined>(); const captchaRef = useRef<CaptFieldRef | null | undefined>();
const smsCaptcha = useRef<CaptchaInstance>(null); const smsCaptcha = useRef<CaptchaInstance>(null);
const findPasswordCaptcha = useRef<CaptchaInstance>(null); const findPasswordCaptcha = useRef<CaptchaInstance>(null);
const [loginType, setLoginType] = useState<LoginType>("phone"); const [loginType, setLoginType] = useState<LoginType>("phone");
const colors = ["#fc6076", "#ff9a44", "#ef9d43", "#e75516"]; const colors = ["#fc6076", "#ff9a44", "#ef9d43", "#e75516"];
const [QRCode, setQRCode] = useState<string>(""); const [QRCode, setQRCode] = useState<string>("");
const navigate = useNavigate(); const navigate = useNavigate();
const store = useStore("user"); const store = useStore("user");
const timerManager = new TimerManager(); const timerManager = new TimerManager();
const [loading, setLoading] = useState<boolean>(true); const [loading, setLoading] = useState<boolean>(true);
const getHoverColors = (colors: string[]) => const getHoverColors = (colors: string[]) =>
colors.map((color) => new TinyColor(color).lighten(5).toString()); colors.map((color) => new TinyColor(color).lighten(5).toString());
const getActiveColors = (colors: string[]) => const getActiveColors = (colors: string[]) =>
colors.map((color) => new TinyColor(color).darken(5).toString()); colors.map((color) => new TinyColor(color).darken(5).toString());
async function smsVerify(token: string, deg: number): Promise<TicketInfoType> { async function smsVerify(token: string, deg: number): Promise<TicketInfoType> {
const phone = form.getFieldValue("phone"); const phone = form.getFieldValue("phone");
const data: any = { const data: any = {
token: token, token: token,
deg: deg, deg: deg,
phone: phone, phone: phone,
}; };
const res: any = await getSms(data); const res: any = await getSms(data);
if (res && res.code === 0) { if (res && res.code === 0) {
message.open({ message.open({
content: res.data, content: res.data,
type: "success", type: "success",
duration: 5, duration: 5,
}); });
} else { } else {
message.open({ message.open({
content: res.data, content: res.data,
type: "warning", type: "warning",
duration: 5, duration: 5,
}); });
} }
return res; return res;
} }
async function findPasswordVerify(token: string, deg: number): Promise<TicketInfoType> { async function findPasswordVerify(token: string, deg: number): Promise<TicketInfoType> {
const mobile = form.getFieldValue("phone"); const mobile = form.getFieldValue("phone");
const captcha = form.getFieldValue("activeCode"); const captcha = form.getFieldValue("activeCode");
const password = form.getFieldValue("password"); const password = form.getFieldValue("password");
const confirmPassword = form.getFieldValue("confirmPassword"); const confirmPassword = form.getFieldValue("confirmPassword");
const data: API.findPasswordRequest = { const data: API.findPasswordRequest = {
token: token, token: token,
deg: deg, deg: deg,
phone: mobile, phone: mobile,
activeCode: captcha, activeCode: captcha,
password: password, password: password,
confirmPassword: confirmPassword, confirmPassword: confirmPassword,
}; };
const res: any = await findPassword(data); const res: any = await findPassword(data);
if (res && res.success && res.code === 0) { if (res && res.success && res.code === 0) {
message.open({ message.open({
content: res.data, content: res.data,
type: "success", type: "success",
duration: 5, duration: 5,
}); });
} else if (res.code === 0 && !res.success) { } else if (res.code === 0 && !res.success) {
message.open({ message.open({
content: res.data, content: res.data,
type: "error", type: "error",
duration: 5, duration: 5,
}); });
} }
return res; return res;
} }
async function openSmsCaptcha() { async function openSmsCaptcha() {
smsCaptcha.current!.open(); smsCaptcha.current!.open();
} }
async function fopenFindPasswordCaptcha() { async function fopenFindPasswordCaptcha() {
findPasswordCaptcha.current!.open(); findPasswordCaptcha.current!.open();
} }
const items = [ const items = [
{ {
key: "phone", key: "phone",
label: ( label: (
<span> <span>
<SafetyOutlined /> <SafetyOutlined />
</span> </span>
), ),
}, },
]; ];
async function wechatLogin() { async function wechatLogin() {
createClientId().then((res: any) => { createClientId().then((res: any) => {
generateQRCode(res.data).then((response: any) => { generateQRCode(res.data).then((response: any) => {
if (response.success) { if (res && response.success && res.data) {
setQRCode(response.data.qrCodeUrl); setQRCode(response.data.qrCodeUrl);
setLoading(false); setLoading(false);
timerManager.add(() => { timerManager.add(() => {
getClientToken(res.data).then((result: any) => { getClientToken(res.data).then((result: any) => {
if (result.success) { if (result.success) {
timerManager.clear(); timerManager.clear();
store.setToken(result.data.tokenValue); store.setToken(result.data.tokenValue);
store.setUserId(result.data.loginId); store.setUserId(result.data.loginId);
setStorage("token", result.data.tokenValue, 24 * 60 * 30); setStorage("token", result.data.tokenValue, 24 * 60 * 30);
setStorage("userId", result.data.loginId, 24 * 60 * 30); setStorage("userId", result.data.loginId, 24 * 60 * 30);
message message
.open({ .open({
content: "登录成功!", content: "登录成功!",
type: "success", type: "success",
}) })
.then(); .then();
if (store.getToken() !== null || store.getUserId() !== null) { if (store.getToken() !== null || store.getUserId() !== null) {
setTimeout(() => { setTimeout(() => {
navigate("/main"); navigate("/main");
}, 2000); }, 2000);
} }
} }
}); });
}, 3000); }, 3000);
} else { } else {
message message
.open({ .open({
content: response.data, content: response.data,
type: "error", type: "error",
}) })
.then(); .then();
} }
}); });
}); });
} }
useEffect(() => { useEffect(() => {
wechatLogin().then(); wechatLogin().then();
}, []); }, []);
return ( return (
<div className={styles.container}> <div className={styles.container}>
<RotateCaptcha get={get} load={load} verify={smsVerify} limit={2} ref={smsCaptcha} /> <RotateCaptcha get={get} load={load} verify={smsVerify} limit={2} ref={smsCaptcha} />
<RotateCaptcha <RotateCaptcha
get={get} get={get}
load={load} load={load}
verify={findPasswordVerify} verify={findPasswordVerify}
limit={2} limit={2}
ref={findPasswordCaptcha} ref={findPasswordCaptcha}
/> />
<div className={styles.content}> <div className={styles.content}>
<Space> <Space>
<Space className={styles.login_content}> <Space className={styles.login_content}>
<Space align="center" className={styles.mp_code}> <Space align="center" className={styles.mp_code}>
<Space direction="vertical" align="center"> <Space direction="vertical" align="center">
<span className={styles.mp_code_title}></span> <span className={styles.mp_code_title}></span>
<Spin tip="Loading" size="large" spinning={loading}> <Spin tip="Loading" size="large" spinning={loading}>
<Image <Image
preview={false} preview={false}
height={200} height={200}
width={200} width={200}
className={styles.mp_code_img} className={styles.mp_code_img}
// src={generateMpRegCodeData.data?.qrCodeUrl} // src={generateMpRegCodeData.data?.qrCodeUrl}
src={QRCode} src={QRCode}
fallback="data:image/png;base64,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" />
</Spin>
/> <Alert
</Spin> // message={(<span>微信扫码<span>关注公众号</span></span>)}
<Alert description={
// message={(<span>微信扫码<span>关注公众号</span></span>)} <div>
description={ <span>
<div>
<span> <span className={styles.mp_tips}></span>
</span>
<span className={styles.mp_tips}></span> <br />
</span>
<br /> </div>
}
</div> // type="success"
} showIcon={true}
// type="success" className={styles.alert}
showIcon={true} icon={<WechatOutlined />}
className={styles.alert} />
icon={<WechatOutlined />} </Space>
/> </Space>
</Space> <Form
</Space> form={form}
<Form className={styles.login_form}
form={form} initialValues={{
className={styles.login_form} autoLogin: true,
initialValues={{ }}>
autoLogin: true, <Space direction="vertical" align="center">
}}> <Space className={styles.logo}>
<Space direction="vertical" align="center"> <img
<Space className={styles.logo}> alt="logo"
<img src={logo}
alt="logo" style={{ width: "44px", height: "44px" }}
src={logo} />
style={{ width: "44px", height: "44px" }} <span></span>
/> </Space>
<span></span> <div className={styles.subTitle}></div>
</Space> </Space>
<div className={styles.subTitle}></div>
</Space> <Tabs
centered={true}
<Tabs items={items}
centered={true} activeKey={loginType}
items={items} onChange={(activeKey) =>
activeKey={loginType} setLoginType(activeKey as LoginType)
onChange={(activeKey) => }></Tabs>
setLoginType(activeKey as LoginType)
}></Tabs> <>
<ProFormText
<> fieldProps={{
<ProFormText size: "large",
fieldProps={{ prefix: <MobileOutlined className={"prefixIcon"} />,
size: "large", autoComplete: "off",
prefix: <MobileOutlined className={"prefixIcon"} />, }}
autoComplete: "off", name="phone"
}} placeholder="请输入手机号"
name="phone" rules={[
placeholder="请输入手机号" {
rules={[ required: true,
{ message: "请输入手机号!",
required: true, },
message: "请输入手机号!", {
}, pattern: /^1\d{10}$/,
{ message: "手机号格式错误!",
pattern: /^1\d{10}$/, },
message: "手机号格式错误!", ]}
}, />
]}
/> <ProFormCaptcha
fieldProps={{
<ProFormCaptcha size: "large",
fieldProps={{ prefix: <SafetyOutlined className={"prefixIcon"} />,
size: "large", }}
prefix: <SafetyOutlined className={"prefixIcon"} />, captchaProps={{
}} size: "large",
captchaProps={{ }}
size: "large", placeholder={"请输入验证码"}
}} captchaTextRender={(timing: boolean, count: number) => {
placeholder={"请输入验证码"} if (timing) {
captchaTextRender={(timing: boolean, count: number) => { return `${count} ${"获取验证码"}`;
if (timing) { // return `${"获取验证码"}`;
return `${count} ${"获取验证码"}`; }
// return `${"获取验证码"}`; return "获取验证码";
} }}
return "获取验证码"; name="activeCode"
}} phoneName={"phone"}
name="activeCode" countDown={60}
phoneName={"phone"} rules={[
countDown={60} {
rules={[ required: true,
{ message: "请输入验证码!",
required: true, },
message: "请输入验证码!", ]}
}, fieldRef={captchaRef}
]} onGetCaptcha={async () => {
fieldRef={captchaRef} await openSmsCaptcha();
onGetCaptcha={async () => { }}
await openSmsCaptcha(); />
}}
/> <ProFormText.Password
name="password"
<ProFormText.Password fieldProps={{
name="password" size: "large",
fieldProps={{ prefix: <LockOutlined className={"prefixIcon"} />,
size: "large", }}
prefix: <LockOutlined className={"prefixIcon"} />, placeholder="请输入新密码"
}} rules={[
placeholder="请输入新密码" {
rules={[ required: true,
{ message: "请输入新密码!",
required: true, },
message: "请输入新密码!", {
}, pattern:
{ /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z\\W]{6,18}$/,
pattern: message:
/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z\\W]{6,18}$/, "密码长度需在6~18位字符且必须包含字母和数字",
message: },
"密码长度需在6~18位字符且必须包含字母和数字", ]}
}, />
]}
/> <ProFormText.Password
name="confirmPassword"
<ProFormText.Password dependencies={["password"]}
name="confirmPassword" fieldProps={{
dependencies={["password"]} size: "large",
fieldProps={{ prefix: <LockOutlined className={"prefixIcon"} />,
size: "large", }}
prefix: <LockOutlined className={"prefixIcon"} />, placeholder="请再次确认密码"
}} rules={[
placeholder="请再次确认密码" {
rules={[ required: true,
{ message: "请再次确认密码!",
required: true, },
message: "请再次确认密码!", ({ getFieldValue }) => ({
}, validator(_, value) {
({ getFieldValue }) => ({ if (!value || getFieldValue("password") === value) {
validator(_, value) { return Promise.resolve();
if (!value || getFieldValue("password") === value) { }
return Promise.resolve(); return Promise.reject(
} new Error("两次输入的密码不一致!"),
return Promise.reject( );
new Error("两次输入的密码不一致!"), },
); }),
}, ]}
}), />
]} </>
/> <ConfigProvider
</> theme={{
<ConfigProvider components: {
theme={{ Button: {
components: { colorPrimary: `linear-gradient(135deg, ${colors.join(", ")})`,
Button: { colorPrimaryHover: `linear-gradient(135deg, ${getHoverColors(colors).join(", ")})`,
colorPrimary: `linear-gradient(135deg, ${colors.join(", ")})`, colorPrimaryActive: `linear-gradient(135deg, ${getActiveColors(colors).join(", ")})`,
colorPrimaryHover: `linear-gradient(135deg, ${getHoverColors(colors).join(", ")})`, lineWidth: 0,
colorPrimaryActive: `linear-gradient(135deg, ${getActiveColors(colors).join(", ")})`, },
lineWidth: 0, },
}, }}>
}, <Button
}}> type="primary"
<Button block
type="primary" size="large"
block onClick={async () => {
size="large" const validateFields = [
onClick={async () => { "phone",
const validateFields = [ "password",
"phone", "activeCode",
"password", "confirmPassword",
"activeCode", ];
"confirmPassword", await form
]; .validateFields(validateFields)
await form .then(async () => {
.validateFields(validateFields) await fopenFindPasswordCaptcha();
.then(async () => { })
await fopenFindPasswordCaptcha(); .catch((error) => {
}) console.error(error);
.catch((error) => { });
console.error(error); }}>
});
}}> </Button>
</ConfigProvider>
</Button> </Form>
</ConfigProvider> <a href="/login" className={styles.go_to_register}>
</Form> <span></span>
<a href="/login" className={styles.go_to_register}> </a>
<span></span> </Space>
</a> </Space>
</Space> </div>
</Space> <FooterComponent />
</div> </div>
<FooterComponent /> );
</div> });
);
});

View File

@@ -120,6 +120,7 @@ export default observer(() => {
store.setAvatar(res.data.user.avatar); store.setAvatar(res.data.user.avatar);
store.setNickName(res.data.user.nickName); store.setNickName(res.data.user.nickName);
setStorage("token", res.data.token, 24 * 60 * 30); setStorage("token", res.data.token, 24 * 60 * 30);
setStorage("userId", res.data.user.id, 24 * 60 * 30);
if (store.getToken() !== null || store.getUserId() !== null) { if (store.getToken() !== null || store.getUserId() !== null) {
setTimeout(() => { setTimeout(() => {
navigate("/main/home"); navigate("/main/home");
@@ -154,6 +155,7 @@ export default observer(() => {
store.setToken(res.data.token); store.setToken(res.data.token);
store.setUserId(res.data.user.id); store.setUserId(res.data.user.id);
setStorage("token", res.data.token, 24 * 60 * 30); setStorage("token", res.data.token, 24 * 60 * 30);
setStorage("userId", res.data.user.id, 24 * 60 * 30);
if (store.getToken() !== null || store.getUserId() !== null) { if (store.getToken() !== null || store.getUserId() !== null) {
setTimeout(() => { setTimeout(() => {
navigate("/main"); navigate("/main");
@@ -217,7 +219,7 @@ export default observer(() => {
async function wechatLogin() { async function wechatLogin() {
createClientId().then((res: any) => { createClientId().then((res: any) => {
generateQRCode(res.data).then((response: any) => { generateQRCode(res.data).then((response: any) => {
if (response.success) { if (res && response.success && res.data) {
setQRCode(response.data.qrCodeUrl); setQRCode(response.data.qrCodeUrl);
setLoading(false); setLoading(false);
timerManager.add(() => { timerManager.add(() => {
@@ -289,7 +291,6 @@ export default observer(() => {
className={styles.mp_code_img} className={styles.mp_code_img}
// src={generateMpRegCodeData.data?.qrCodeUrl} // src={generateMpRegCodeData.data?.qrCodeUrl}
src={QRCode} src={QRCode}
fallback="data:image/png;base64,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"
/> />
</Spin> </Spin>
<Alert <Alert

View File

@@ -1,400 +1,399 @@
/** @format */ /** @format */
import { import {
LockOutlined, LockOutlined,
MobileOutlined, MobileOutlined,
SafetyOutlined, SafetyOutlined,
UserOutlined, UserOutlined,
WechatOutlined, WechatOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { CaptFieldRef, ProFormCaptcha, ProFormText } from "@ant-design/pro-components"; import { CaptFieldRef, ProFormCaptcha, ProFormText } from "@ant-design/pro-components";
import { Alert, Button, ConfigProvider, Form, Image, message, Space, Spin, Tabs } from "antd"; import { Alert, Button, ConfigProvider, Form, Image, message, Space, Spin, Tabs } from "antd";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import logo from "@/assets/images/logo.png"; import logo from "@/assets/images/logo.png";
// import background from '@/assets/images/background.png' // import background from '@/assets/images/background.png'
import styles from "./index.module.less"; import styles from "./index.module.less";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import FooterComponent from "@/components/Footer"; import FooterComponent from "@/components/Footer";
import { createClientId, generateQRCode, getClientToken, getSms, register } from "@/api/user"; import { createClientId, generateQRCode, getClientToken, getSms, register } from "@/api/user";
import { TinyColor } from "@ctrl/tinycolor"; import { TinyColor } from "@ctrl/tinycolor";
import { get, load } from "@/api/captcha"; import { get, load } from "@/api/captcha";
import RotateCaptcha, { CaptchaInstance, type TicketInfoType } from "react-rotate-captcha"; import RotateCaptcha, { CaptchaInstance, type TicketInfoType } from "react-rotate-captcha";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { setStorage } from "@/utils/localStorage/config.ts"; import { setStorage } from "@/utils/localStorage/config.ts";
import useStore from "@/utils/store/useStore.tsx"; import useStore from "@/utils/store/useStore.tsx";
import { TimerManager } from "timer-manager-lib"; import { TimerManager } from "timer-manager-lib";
// import useStore from '@/utils/store/useStore.tsx' // import useStore from '@/utils/store/useStore.tsx'
type LoginType = "phone"; type LoginType = "phone";
export default observer(() => { export default observer(() => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const registerCaptcha = useRef<CaptchaInstance>(null); const registerCaptcha = useRef<CaptchaInstance>(null);
const smsCaptcha = useRef<CaptchaInstance>(null); const smsCaptcha = useRef<CaptchaInstance>(null);
const captchaRef = useRef<CaptFieldRef | null | undefined>(); const captchaRef = useRef<CaptFieldRef | null | undefined>();
const [QRCode, setQRCode] = useState<string>(""); const [QRCode, setQRCode] = useState<string>("");
const navigate = useNavigate(); const navigate = useNavigate();
const store = useStore("user"); const store = useStore("user");
const colors = ["#6253E1", "#04BEFE"]; const colors = ["#6253E1", "#04BEFE"];
const timerManager = new TimerManager(); const timerManager = new TimerManager();
const [loading, setLoading] = useState<boolean>(true); const [loading, setLoading] = useState<boolean>(true);
const getHoverColors = (colors: string[]) => const getHoverColors = (colors: string[]) =>
colors.map((color) => new TinyColor(color).lighten(5).toString()); colors.map((color) => new TinyColor(color).lighten(5).toString());
const getActiveColors = (colors: string[]) => const getActiveColors = (colors: string[]) =>
colors.map((color) => new TinyColor(color).darken(5).toString()); colors.map((color) => new TinyColor(color).darken(5).toString());
const items = [ const items = [
{ {
key: "phone", key: "phone",
label: ( label: (
<span> <span>
<MobileOutlined /> <MobileOutlined />
</span> </span>
), ),
}, },
]; ];
const [loginType, setLoginType] = useState<LoginType>("phone"); const [loginType, setLoginType] = useState<LoginType>("phone");
async function smsVerify(token: string, deg: number): Promise<TicketInfoType> { async function smsVerify(token: string, deg: number): Promise<TicketInfoType> {
const phone = form.getFieldValue("phone"); const phone = form.getFieldValue("phone");
const data: any = { const data: any = {
token: token, token: token,
deg: deg, deg: deg,
phone: phone, phone: phone,
}; };
const res: any = await getSms(data); const res: any = await getSms(data);
if (res && res.code === 0) { if (res && res.code === 0) {
message.open({ message.open({
content: res.data, content: res.data,
type: "success", type: "success",
duration: 5, duration: 5,
}); });
} else { } else {
message.open({ message.open({
content: res.data, content: res.data,
type: "warning", type: "warning",
duration: 5, duration: 5,
}); });
} }
return res; return res;
} }
async function openRegisterCaptcha() { async function openRegisterCaptcha() {
registerCaptcha.current!.open(); registerCaptcha.current!.open();
} }
async function openSmsCaptcha() { async function openSmsCaptcha() {
smsCaptcha.current!.open(); smsCaptcha.current!.open();
} }
async function registerVerify(token: string, deg: number): Promise<TicketInfoType> { async function registerVerify(token: string, deg: number): Promise<TicketInfoType> {
const userName = form.getFieldValue("username"); const userName = form.getFieldValue("username");
const password = form.getFieldValue("password"); const password = form.getFieldValue("password");
const phone = form.getFieldValue("phone"); const phone = form.getFieldValue("phone");
const activeCode = form.getFieldValue("activeCode"); const activeCode = form.getFieldValue("activeCode");
const data: any = { const data: any = {
token: token, token: token,
deg: deg, deg: deg,
userName: userName, userName: userName,
password: password, password: password,
phone: phone, phone: phone,
activeCode: activeCode, activeCode: activeCode,
}; };
const res: any = await register(data); const res: any = await register(data);
if (res && res.success && res.code === 0) { if (res && res.success && res.code === 0) {
message message
.open({ .open({
content: res.data, content: res.data,
type: "success", type: "success",
duration: 5, duration: 5,
}) })
.then(() => { .then(() => {
navigate("/login"); navigate("/login");
}); });
} else if (res.code === 0 && !res.success) { } else if (res.code === 0 && !res.success) {
message.open({ message.open({
content: res.data, content: res.data,
type: "error", type: "error",
duration: 5, duration: 5,
}); });
} }
return res; return res;
} }
async function wechatLogin() { async function wechatLogin() {
createClientId().then((res: any) => { createClientId().then((res: any) => {
generateQRCode(res.data).then((response: any) => { generateQRCode(res.data).then((response: any) => {
if (response.success) { if (res && response.success && res.data) {
setQRCode(response.data.qrCodeUrl); setQRCode(response.data.qrCodeUrl);
setLoading(false); setLoading(false);
timerManager.add(() => { timerManager.add(() => {
getClientToken(res.data).then((result: any) => { getClientToken(res.data).then((result: any) => {
if (result.success) { if (result.success) {
timerManager.clear(); timerManager.clear();
store.setToken(result.data.tokenValue); store.setToken(result.data.tokenValue);
store.setUserId(result.data.loginId); store.setUserId(result.data.loginId);
setStorage("token", result.data.tokenValue, 24 * 60 * 30); setStorage("token", result.data.tokenValue, 24 * 60 * 30);
setStorage("userId", result.data.loginId, 24 * 60 * 30); setStorage("userId", result.data.loginId, 24 * 60 * 30);
message message
.open({ .open({
content: "登录成功!", content: "登录成功!",
type: "success", type: "success",
}) })
.then(); .then();
if (store.getToken() !== null || store.getUserId() !== null) { if (store.getToken() !== null || store.getUserId() !== null) {
setTimeout(() => { setTimeout(() => {
navigate("/main"); navigate("/main");
}, 2000); }, 2000);
} }
} }
}); });
}, 3000); }, 3000);
} else { } else {
message message
.open({ .open({
content: response.data, content: response.data,
type: "error", type: "error",
}) })
.then(); .then();
} }
}); });
}); });
} }
useEffect(() => { useEffect(() => {
wechatLogin().then(); wechatLogin().then();
}, []); }, []);
return ( return (
<div className={styles.container}> <div className={styles.container}>
<RotateCaptcha get={get} load={load} verify={registerVerify} ref={registerCaptcha} /> <RotateCaptcha get={get} load={load} verify={registerVerify} ref={registerCaptcha} />
<RotateCaptcha get={get} load={load} verify={smsVerify} ref={smsCaptcha} /> <RotateCaptcha get={get} load={load} verify={smsVerify} ref={smsCaptcha} />
<div className={styles.content}> <div className={styles.content}>
<Space> <Space>
<Space className={styles.login_content}> <Space className={styles.login_content}>
<Space align="center" className={styles.mp_code}> <Space align="center" className={styles.mp_code}>
<Space direction="vertical" align="center"> <Space direction="vertical" align="center">
<span className={styles.mp_code_title}></span> <span className={styles.mp_code_title}></span>
<Spin tip="Loading" size="large" spinning={loading}> <Spin tip="Loading" size="large" spinning={loading}>
<Image <Image
preview={false} preview={false}
height={200} height={200}
width={200} width={200}
className={styles.mp_code_img} className={styles.mp_code_img}
// src={generateMpRegCodeData.data?.qrCodeUrl} // src={generateMpRegCodeData.data?.qrCodeUrl}
src={QRCode} src={QRCode}
fallback="data:image/png;base64,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" />
/> </Spin>
</Spin> <Alert
<Alert // message={(<span>微信扫码<span>关注公众号</span></span>)}
// message={(<span>微信扫码<span>关注公众号</span></span>)} description={
description={ <div>
<div> <span>
<span>
<span className={styles.mp_tips}></span>
<span className={styles.mp_tips}></span> </span>
</span> <br />
<br />
</div>
</div> }
} // type="success"
// type="success" showIcon={true}
showIcon={true} className={styles.alert}
className={styles.alert} icon={<WechatOutlined />}
icon={<WechatOutlined />} />
/> </Space>
</Space> </Space>
</Space> <Form
<Form form={form}
form={form} className={styles.login_form}
className={styles.login_form} initialValues={{
initialValues={{ autoLogin: true,
autoLogin: true, }}>
}}> <Space direction="vertical" align="center">
<Space direction="vertical" align="center"> <Space className={styles.logo}>
<Space className={styles.logo}> <img
<img alt="logo"
alt="logo" src={logo}
src={logo} style={{ width: "44px", height: "44px" }}
style={{ width: "44px", height: "44px" }} />
/> <span></span>
<span></span> </Space>
</Space> <div className={styles.subTitle}></div>
<div className={styles.subTitle}></div> </Space>
</Space>
<Tabs
<Tabs centered={true}
centered={true} items={items}
items={items} activeKey={loginType}
activeKey={loginType} onChange={(activeKey) =>
onChange={(activeKey) => setLoginType(activeKey as LoginType)
setLoginType(activeKey as LoginType) }></Tabs>
}></Tabs>
<>
<> <ProFormText
<ProFormText fieldProps={{
fieldProps={{ size: "large",
size: "large", prefix: <UserOutlined className={"prefixIcon"} />,
prefix: <UserOutlined className={"prefixIcon"} />, autoComplete: "off",
autoComplete: "off", }}
}} name="username"
name="username" placeholder="请输入用户名"
placeholder="请输入用户名" rules={[
rules={[ {
{ required: true,
required: true, message: "请输入用户名!",
message: "请输入用户名!", },
}, {
{ pattern: /^[a-zA-Z0-9_-]{3,16}$/,
pattern: /^[a-zA-Z0-9_-]{3,16}$/, message:
message: "用户名只能是3到16位字母数字下划线减号",
"用户名只能是3到16位字母数字下划线减号", },
}, ]}
]} />
/>
<ProFormText
<ProFormText fieldProps={{
fieldProps={{ size: "large",
size: "large", prefix: <MobileOutlined className={"prefixIcon"} />,
prefix: <MobileOutlined className={"prefixIcon"} />, autoComplete: "off",
autoComplete: "off", }}
}} name="phone"
name="phone" placeholder="请输入手机号"
placeholder="请输入手机号" rules={[
rules={[ {
{ required: true,
required: true, message: "请输入手机号!",
message: "请输入手机号!", },
}, {
{ pattern: /^1\d{10}$/,
pattern: /^1\d{10}$/, message: "手机号格式错误!",
message: "手机号格式错误!", },
}, ]}
]} />
/>
<ProFormCaptcha
<ProFormCaptcha fieldProps={{
fieldProps={{ size: "large",
size: "large", prefix: <SafetyOutlined className={"prefixIcon"} />,
prefix: <SafetyOutlined className={"prefixIcon"} />, }}
}} captchaProps={{
captchaProps={{ size: "large",
size: "large", }}
}} placeholder={"请输入验证码"}
placeholder={"请输入验证码"} captchaTextRender={(timing: boolean, count: number) => {
captchaTextRender={(timing: boolean, count: number) => { if (timing) {
if (timing) { return `${count} ${"获取验证码"}`;
return `${count} ${"获取验证码"}`; // return `${"获取验证码"}`;
// return `${"获取验证码"}`; }
} return "获取验证码";
return "获取验证码"; }}
}} name="activeCode"
name="activeCode" phoneName={"phone"}
phoneName={"phone"} countDown={60}
countDown={60} rules={[
rules={[ {
{ required: true,
required: true, message: "请输入验证码!",
message: "请输入验证码!", },
}, ]}
]} fieldRef={captchaRef}
fieldRef={captchaRef} onGetCaptcha={async () => {
onGetCaptcha={async () => { await openSmsCaptcha();
await openSmsCaptcha(); }}
}} />
/>
<ProFormText.Password
<ProFormText.Password name="password"
name="password" fieldProps={{
fieldProps={{ size: "large",
size: "large", prefix: <LockOutlined className={"prefixIcon"} />,
prefix: <LockOutlined className={"prefixIcon"} />, }}
}} placeholder="请输入密码"
placeholder="请输入密码" rules={[
rules={[ {
{ required: true,
required: true, message: "请输入密码!",
message: "请输入密码!", },
}, {
{ pattern:
pattern: /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z\\W]{6,18}$/,
/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z\\W]{6,18}$/, message:
message: "密码长度需在6~18位字符且必须包含字母和数字",
"密码长度需在6~18位字符且必须包含字母和数字", },
}, ]}
]} />
/>
<ProFormText.Password
<ProFormText.Password name="confirmPassword"
name="confirmPassword" dependencies={["password"]}
dependencies={["password"]} fieldProps={{
fieldProps={{ size: "large",
size: "large", prefix: <LockOutlined className={"prefixIcon"} />,
prefix: <LockOutlined className={"prefixIcon"} />, }}
}} placeholder="请再次确认密码"
placeholder="请再次确认密码" rules={[
rules={[ {
{ required: true,
required: true, message: "请再次确认密码!",
message: "请再次确认密码!", },
}, ({ getFieldValue }) => ({
({ getFieldValue }) => ({ validator(_, value) {
validator(_, value) { if (!value || getFieldValue("password") === value) {
if (!value || getFieldValue("password") === value) { return Promise.resolve();
return Promise.resolve(); }
} return Promise.reject(
return Promise.reject( new Error("两次输入的密码不一致!"),
new Error("两次输入的密码不一致!"), );
); },
}, }),
}), ]}
]} />
/> </>
</> <ConfigProvider
<ConfigProvider theme={{
theme={{ components: {
components: { Button: {
Button: { colorPrimary: `linear-gradient(90deg, ${colors.join(", ")})`,
colorPrimary: `linear-gradient(90deg, ${colors.join(", ")})`, colorPrimaryHover: `linear-gradient(90deg, ${getHoverColors(colors).join(", ")})`,
colorPrimaryHover: `linear-gradient(90deg, ${getHoverColors(colors).join(", ")})`, colorPrimaryActive: `linear-gradient(90deg, ${getActiveColors(colors).join(", ")})`,
colorPrimaryActive: `linear-gradient(90deg, ${getActiveColors(colors).join(", ")})`, lineWidth: 0,
lineWidth: 0, },
}, },
}, }}>
}}> <Button
<Button type="primary"
type="primary" block
block size="large"
size="large" onClick={async () => {
onClick={async () => { const validateFields = [
const validateFields = [ "phone",
"phone", "username",
"username", "password",
"password", "activeCode",
"activeCode", "confirmPassword",
"confirmPassword", ];
]; await form
await form .validateFields(validateFields)
.validateFields(validateFields) .then(async () => {
.then(async () => { await openRegisterCaptcha();
await openRegisterCaptcha(); })
}) .catch((error) => {
.catch((error) => { console.error(error);
console.error(error); });
}); }}>
}}>
</Button>
</Button> </ConfigProvider>
</ConfigProvider> </Form>
</Form> <a href="/login" className={styles.go_to_register}>
<a href="/login" className={styles.go_to_register}> <span></span>
<span></span> </a>
</a> </Space>
</Space> </Space>
</Space> </div>
</div> <FooterComponent />
<FooterComponent /> </div>
</div> );
); });
});