大改动

This commit is contained in:
宇阳
2025-01-11 23:26:36 +08:00
parent 2a1391da72
commit 34198b212b
6 changed files with 512 additions and 387 deletions

View File

@@ -1,8 +1,8 @@
import { useEffect, useState } from "react";
import { Button, Card, Dropdown, message, Modal } from "antd";
import { getLinkListAPI, delLinkDataAPI, auditWebDataAPI } from '@/api/Web';
import { getCommentListAPI, auditCommentDataAPI, delCommentDataAPI, addCommentDataAPI } from "@/api/Comment";
import { getWallListAPI, auditWallDataAPI, delWallDataAPI } from "@/api/Wall";
import { Card, Spin } from "antd";
import { getLinkListAPI } from '@/api/Web';
import { getCommentListAPI } from "@/api/Comment";
import { getWallListAPI } from "@/api/Wall";
import Title from "@/components/Title";
@@ -10,213 +10,14 @@ import comment from './image/comment.svg';
import info from './image/message.svg';
import link from './image/link.svg';
import dayjs from 'dayjs';
import RandomAvatar from "@/components/RandomAvatar";
import Empty from "@/components/Empty";
import { useUserStore, useWebStore } from '@/stores';
import TextArea from "antd/es/input/TextArea";
import { sendDismissEmailAPI } from "@/api/Email";
import List from "./components/List";
type Menu = "comment" | "link" | "wall";
interface ListItemProps {
item: any;
type: Menu;
fetchData: (type: Menu) => void;
}
export default () => {
const [loading, setLoading] = useState(false)
const ListItem = ({ item, type, fetchData }: ListItemProps) => {
const web = useWebStore(state => state.web)
const user = useUserStore(state => state.user)
const [btnType, setBtnType] = useState<"reply" | "dismiss" | string>("")
// 通过
const handleApproval = async () => {
if (type === "link") {
await auditWebDataAPI(item.id);
} else if (type === "comment") {
await auditCommentDataAPI(item.id);
} else if (type === "wall") {
await auditWallDataAPI(item.id);
}
btnType != "reply" && message.success('🎉 审核成功');
fetchData(type);
};
// 回复
const [isModalOpen, setIsModalOpen] = useState(false);
const [replyInfo, setReplyInfo] = useState("")
const handleReply = async () => {
// 审核通过评论
await handleApproval()
// 发送回复内容
await addCommentDataAPI({
avatar: user.avatar,
url: web.url,
content: replyInfo,
commentId: item?.id!,
auditStatus: 1,
email: user.email ? user.email : null,
name: user.name,
articleId: item?.articleId!,
createTime: new Date().getTime().toString(),
})
message.success('🎉 回复成功');
setIsModalOpen(false)
fetchData(type);
setReplyInfo("")
setBtnType("")
}
// 驳回
const [dismissInfo, setDismissInfo] = useState("")
const handleDismiss = async () => {
if (type === "link") {
await delLinkDataAPI(item.id);
} else if (type === "comment") {
await delCommentDataAPI(item.id);
} else if (type === "wall") {
await delWallDataAPI(item.id);
}
// 有内容就发送驳回通知邮件,反之直接删除
if (dismissInfo.trim().length) await sendDismissEmail()
message.success('🎉 驳回成功');
setIsModalOpen(false)
fetchData(type);
setDismissInfo("")
setBtnType("")
};
// 发送驳回通知邮件
const sendDismissEmail = async () => {
// 类型名称
let email_info = {
name: "",
type: "",
url: ""
}
switch (type) {
case "link":
email_info = {
name: item.title,
type: "友链",
url: `${web.url}/friend`,
}
break;
case "comment":
email_info = {
name: item.name,
type: "评论",
url: `${web.url}/article/${item.articleId}`,
}
break;
case "wall":
email_info = {
name: item.name,
type: "留言",
url: `${web.url}/wall/all`,
}
break;
}
// 有邮箱才会邮件通知
item.email != null && await sendDismissEmailAPI({
to: item.email,
content: dismissInfo,
recipient: email_info.name,
subject: `${email_info.type}驳回通知`,
time: dayjs(Date.now()).format('YYYY年MM月DD日 HH:mm'),
type: email_info.type,
url: email_info.url
})
}
return (
<div key={item.id}>
<div className="text-center text-xs text-[#e0e0e0] mb-4">
{dayjs(+item.createTime!).format('YYYY-MM-DD HH:mm:ss')}
</div>
<div className="flex justify-between md:p-7 rounded-md transition-colors">
<div className="flex mr-10">
{type !== "wall" ? (
<img src={item.avatar || item.image} alt="" className="w-13 h-13 border border-[#eee] rounded-full" />
) : <RandomAvatar className="w-13 h-13 border border-[#eee] rounded-full" />}
<div className="flex flex-col justify-center ml-4 px-4 py-2 min-w-[210px] text-xs md:text-sm bg-[#F9F9FD] dark:bg-[#4e5969] rounded-md">
{type === "link" ? (
<>
<div>{item.title}</div>
<div>{item.description}</div>
<div>{item.type.name}</div>
<div>{item?.url ? <a href={item?.url} target='_blank' className="hover:text-primary font-bold">{item?.url}</a> : '无网站'}</div>
</>
) : type === "comment" ? (
<>
<div>{item.name}</div>
<div>{item.content}</div>
<div>{item?.url ? <a href={item?.url} target='_blank' className="hover:text-primary font-bold">{item?.url}</a> : '无网站'}</div>
<div><a href={`${web.url}/article/${item.articleId}`} target='_blank' className="hover:text-primary">{item.articleTitle || '暂无'}</a></div>
</>
) : (
<>
<div>{item.name}</div>
<div>{item.content}</div>
</>
)}
<div>{item.email || '暂无'}</div>
</div>
</div>
<div className="flex items-end">
<Dropdown menu={{
items: type === "comment"
? [
{ key: 'ok', label: "通过", onClick: handleApproval },
{ key: 'reply', label: "回复", onClick: () => [setIsModalOpen(true), setBtnType("reply")] },
{ key: 'dismiss', label: "驳回", onClick: () => [setIsModalOpen(true), , setBtnType("dismiss")] }
]
: [
{ key: 'ok', label: "通过", onClick: handleApproval },
{ key: 'dismiss', label: "驳回", onClick: () => [setIsModalOpen(true), , setBtnType("dismiss")] }
]
}}>
<div className="flex justify-evenly items-center bg-[#F9F9FD] dark:bg-[#4e5969] w-11 h-5 rounded-md cursor-pointer">
<span className="inline-block w-2 h-2 bg-[#b5c2d3] rounded-full"></span>
<span className="inline-block w-2 h-2 bg-[#b5c2d3] rounded-full"></span>
</div>
</Dropdown>
</div>
</div>
<Modal title={btnType === "reply" ? "回复内容" : "驳回原因"} open={isModalOpen} footer={null} onCancel={() => setIsModalOpen(false)} onClose={() => setIsModalOpen(false)}>
<TextArea
value={btnType === "reply" ? replyInfo : dismissInfo}
onChange={(e) => (btnType === "reply" ? setReplyInfo(e.target.value) : setDismissInfo(e.target.value))}
placeholder={btnType === "reply" ? "请输入回复内容" : "请输入驳回原因"}
autoSize={{ minRows: 3, maxRows: 5 }}
/>
<div className="flex space-x-4">
<Button className="w-full mt-2" onClick={() => setIsModalOpen(false)}></Button>
<Button type="primary" className="w-full mt-2" onClick={btnType === "reply" ? handleReply : handleDismiss}></Button>
</div>
</Modal>
</div>
)
}
const WorkPage = () => {
const activeSty = "bg-[#f9f9ff] dark:bg-[#3c5370] text-primary";
const [active, setActive] = useState<Menu>("comment");
const [commentList, setCommentList] = useState<any[]>([]);
@@ -225,19 +26,26 @@ const WorkPage = () => {
// 重新获取最新数据
const fetchData = async (type: Menu) => {
if (type === "comment") {
const { data } = await getCommentListAPI({ query: { status: 0 }, pattern: "list" });
setCommentList(data);
} else if (type === "link") {
const { data } = await getLinkListAPI({ query: { status: 0 } });
setLinkList(data);
} else if (type === "wall") {
const { data } = await getWallListAPI({ query: { status: 0 } });
setWallList(data);
try {
if (type === "comment") {
const { data } = await getCommentListAPI({ query: { status: 0 }, pattern: "list" });
setCommentList(data);
} else if (type === "link") {
const { data } = await getLinkListAPI({ query: { status: 0 } });
setLinkList(data);
} else if (type === "wall") {
const { data } = await getWallListAPI({ query: { status: 0 } });
setWallList(data);
}
} catch (error) {
setLoading(false)
}
setLoading(false)
};
useEffect(() => {
setLoading(true)
fetchData(active);
}, [active]);
@@ -246,39 +54,40 @@ const WorkPage = () => {
return <Empty />;
}
return list.map(item => (
<ListItem key={item.id} item={item} type={type} fetchData={(type) => fetchData(type)} />
<List key={item.id} item={item} type={type} fetchData={(type) => fetchData(type)} />
));
};
return (
<>
<Title value="工作台" />
<Card className="mt-2 min-h-[calc(100vh-180px)]">
<div className="flex flex-col md:flex-row w-full">
<div className="w-full min-w-[200px] md:w-2/12 md:min-h-96 mb-5 md:mb-0 pr-4 md:border-b-transparent md:border-r border-[#eee] dark:border-strokedark">
<ul className="space-y-1">
{(["comment", "link", "wall"] as Menu[]).map((menu) => (
<li
key={menu}
className={`flex items-center w-full py-3 px-4 hover:bg-[#f9f9ff] dark:hover:bg-[#3c5370] hover:text-primary ${active === menu ? activeSty : ''} rounded-md text-base cursor-pointer transition-colors`}
onClick={() => setActive(menu)}
>
<img src={menu === "comment" ? comment : menu === "link" ? link : info} alt="" className="w-8 mr-4" />
<span>{menu === "comment" ? "评论" : menu === "link" ? "友联" : "留言"}</span>
</li>
))}
</ul>
</div>
<div className="w-full md:w-10/12 md:pl-6 py-4 space-y-10">
{active === "link" && renderList(linkList, "link")}
{active === "comment" && renderList(commentList, "comment")}
{active === "wall" && renderList(wallList, "wall")}
<Spin spinning={loading}>
<Card className="mt-2 min-h-[calc(100vh-180px)]">
<div className="flex flex-col md:flex-row w-full">
<div className="w-full min-w-[200px] md:w-2/12 md:min-h-96 mb-5 md:mb-0 pr-4 md:border-b-transparent md:border-r border-[#eee] dark:border-strokedark">
<ul className="space-y-1">
{(["comment", "link", "wall"] as Menu[]).map((menu) => (
<li
key={menu}
className={`flex items-center w-full py-3 px-4 hover:bg-[#f9f9ff] dark:hover:bg-[#3c5370] hover:text-primary ${active === menu ? activeSty : ''} rounded-md text-base cursor-pointer transition-colors`}
onClick={() => setActive(menu)}
>
<img src={menu === "comment" ? comment : menu === "link" ? link : info} alt="" className="w-8 mr-4" />
<span>{menu === "comment" ? "评论" : menu === "link" ? "友联" : "留言"}</span>
</li>
))}
</ul>
</div>
<div className="w-full md:w-10/12 md:pl-6 py-4 space-y-10">
{active === "link" && renderList(linkList, "link")}
{active === "comment" && renderList(commentList, "comment")}
{active === "wall" && renderList(wallList, "wall")}
</div>
</div>
</div>
</Card>
</Card>
</Spin>
</>
);
}
export default WorkPage;
}