2025-01-24 00:19:02 +08:00
|
|
|
|
import { useContext } from "react";
|
|
|
|
|
import { PostDetailContext } from "./context/PostDetailContext";
|
|
|
|
|
import { motion } from "framer-motion";
|
|
|
|
|
import {
|
|
|
|
|
CalendarIcon,
|
|
|
|
|
UserCircleIcon,
|
|
|
|
|
LockClosedIcon,
|
|
|
|
|
LockOpenIcon,
|
|
|
|
|
StarIcon,
|
|
|
|
|
ClockIcon,
|
|
|
|
|
EyeIcon,
|
|
|
|
|
ChatBubbleLeftIcon,
|
|
|
|
|
} from "@heroicons/react/24/outline";
|
2025-01-25 00:46:59 +08:00
|
|
|
|
import { Button, Typography, Space, Tooltip } from "antd";
|
2025-01-24 00:19:02 +08:00
|
|
|
|
import { useVisitor } from "@nice/client";
|
2025-01-24 17:39:41 +08:00
|
|
|
|
import { PostState, VisitType } from "@nice/common";
|
|
|
|
|
import {
|
2025-01-25 00:46:59 +08:00
|
|
|
|
CalendarOutlined,
|
|
|
|
|
ClockCircleOutlined,
|
2025-01-24 17:39:41 +08:00
|
|
|
|
CommentOutlined,
|
|
|
|
|
EyeOutlined,
|
2025-01-25 00:46:59 +08:00
|
|
|
|
FileTextOutlined,
|
|
|
|
|
FolderOutlined,
|
2025-01-24 17:39:41 +08:00
|
|
|
|
LikeFilled,
|
|
|
|
|
LikeOutlined,
|
2025-01-25 00:46:59 +08:00
|
|
|
|
LockOutlined,
|
|
|
|
|
UnlockOutlined,
|
|
|
|
|
UserOutlined,
|
2025-01-24 17:39:41 +08:00
|
|
|
|
} from "@ant-design/icons";
|
|
|
|
|
import dayjs from "dayjs";
|
|
|
|
|
import { TitleSection } from "./PostHeader/TitleSection";
|
|
|
|
|
import {
|
|
|
|
|
AuthorBadge,
|
|
|
|
|
DateBadge,
|
|
|
|
|
TermBadge,
|
|
|
|
|
UpdatedBadge,
|
|
|
|
|
VisibilityBadge,
|
|
|
|
|
} from "./PostHeader/InfoBadge";
|
|
|
|
|
import { StatsSection } from "./PostHeader/StatsSection";
|
2025-01-25 00:46:59 +08:00
|
|
|
|
import { PostBadge } from "./badge/PostBadge";
|
2025-01-24 00:19:02 +08:00
|
|
|
|
|
2025-01-25 00:46:59 +08:00
|
|
|
|
const { Title, Paragraph, Text } = Typography;
|
2025-01-24 00:19:02 +08:00
|
|
|
|
export default function PostHeader() {
|
|
|
|
|
const { post, user } = useContext(PostDetailContext);
|
2025-01-24 17:39:41 +08:00
|
|
|
|
const { like, unLike } = useVisitor();
|
2025-01-24 00:19:02 +08:00
|
|
|
|
|
|
|
|
|
function likeThisPost() {
|
|
|
|
|
if (!post?.liked) {
|
2025-01-24 17:39:41 +08:00
|
|
|
|
post.likes += 1;
|
|
|
|
|
post.liked = true;
|
2025-01-24 00:19:02 +08:00
|
|
|
|
like.mutateAsync({
|
|
|
|
|
data: {
|
|
|
|
|
visitorId: user?.id || null,
|
|
|
|
|
postId: post.id,
|
|
|
|
|
type: VisitType.LIKE,
|
|
|
|
|
},
|
|
|
|
|
});
|
2025-01-24 17:39:41 +08:00
|
|
|
|
} else {
|
|
|
|
|
post.likes -= 1;
|
|
|
|
|
post.liked = false;
|
|
|
|
|
unLike.mutateAsync({
|
|
|
|
|
where: {
|
|
|
|
|
visitorId: user?.id || null,
|
|
|
|
|
postId: post.id,
|
|
|
|
|
type: VisitType.LIKE,
|
|
|
|
|
},
|
|
|
|
|
});
|
2025-01-24 00:19:02 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<motion.div
|
|
|
|
|
initial={{ opacity: 0, y: -20 }}
|
|
|
|
|
animate={{ opacity: 1, y: 0 }}
|
|
|
|
|
transition={{ duration: 0.5 }}
|
2025-01-25 00:46:59 +08:00
|
|
|
|
className="relative bg-gradient-to-br from-primary-250 via-primary-150 to--primary-350 rounded-lg p-6 shadow-lg border border-[#97A9C4]/30">
|
2025-01-24 00:19:02 +08:00
|
|
|
|
{/* Corner Decorations */}
|
2025-01-25 00:46:59 +08:00
|
|
|
|
<div className="absolute top-0 left-0 w-5 h-5 border-t-4 border-l-4 border-primary rounded-tl-lg" />
|
|
|
|
|
<div className="absolute bottom-0 right-0 w-5 h-5 border-b-4 border-r-4 border-primary rounded-br-lg" />
|
2025-01-24 00:19:02 +08:00
|
|
|
|
|
|
|
|
|
{/* Title Section */}
|
2025-01-24 17:39:41 +08:00
|
|
|
|
|
2025-01-25 00:46:59 +08:00
|
|
|
|
<TitleSection></TitleSection>
|
2025-01-24 00:19:02 +08:00
|
|
|
|
|
|
|
|
|
<div className="space-y-4">
|
2025-01-25 00:46:59 +08:00
|
|
|
|
{/* 收件人信息行 */}
|
|
|
|
|
<Space>
|
|
|
|
|
<UserOutlined className="text-secondary-400" />
|
|
|
|
|
<span className="text-secondary-400">收件人:</span>
|
|
|
|
|
<Text strong>
|
|
|
|
|
{post?.receivers?.map((receiver) => receiver?.showname)}
|
|
|
|
|
</Text>
|
|
|
|
|
</Space>
|
|
|
|
|
|
2025-01-24 00:19:02 +08:00
|
|
|
|
{/* First Row - Basic Info */}
|
2025-01-25 00:46:59 +08:00
|
|
|
|
<div className="flex flex-wrap items-center gap-1">
|
2025-01-24 00:19:02 +08:00
|
|
|
|
{/* Author Info Badge */}
|
2025-01-25 00:46:59 +08:00
|
|
|
|
<Space>
|
|
|
|
|
<UserOutlined className="text-secondary-400" />
|
|
|
|
|
<span className="text-secondary-400">发件人:</span>
|
|
|
|
|
<Text strong>
|
|
|
|
|
{post?.author?.showname || "匿名用户"}
|
|
|
|
|
</Text>
|
|
|
|
|
</Space>
|
|
|
|
|
<Text type="secondary">|</Text>
|
2025-01-24 00:19:02 +08:00
|
|
|
|
{/* Date Info Badge */}
|
2025-01-25 00:46:59 +08:00
|
|
|
|
<Space>
|
|
|
|
|
<CalendarOutlined className="text-secondary-400" />
|
|
|
|
|
|
|
|
|
|
<Text>
|
|
|
|
|
创建于:
|
|
|
|
|
{dayjs(post?.createdAt).format("YYYY-MM-DD")}
|
|
|
|
|
</Text>
|
|
|
|
|
</Space>
|
|
|
|
|
<Text type="secondary">|</Text>
|
2025-01-24 00:19:02 +08:00
|
|
|
|
{/* Last Updated Badge */}
|
2025-01-25 00:46:59 +08:00
|
|
|
|
<Space>
|
|
|
|
|
<ClockCircleOutlined className="text-secondary-400" />
|
|
|
|
|
<Text>
|
|
|
|
|
更新于:
|
|
|
|
|
{dayjs(post?.updatedAt).format("YYYY-MM-DD")}
|
|
|
|
|
</Text>
|
|
|
|
|
</Space>
|
|
|
|
|
<Text type="secondary">|</Text>
|
2025-01-24 00:19:02 +08:00
|
|
|
|
{/* Visibility Status Badge */}
|
2025-01-25 00:46:59 +08:00
|
|
|
|
<Space>
|
|
|
|
|
{post?.isPublic ? (
|
|
|
|
|
<UnlockOutlined className="text-secondary-400" />
|
|
|
|
|
) : (
|
|
|
|
|
<LockOutlined className="text-secondary-400" />
|
|
|
|
|
)}
|
|
|
|
|
<Text>{post?.isPublic ? "公开" : "私信"}</Text>
|
|
|
|
|
</Space>
|
2025-01-24 00:19:02 +08:00
|
|
|
|
</div>
|
|
|
|
|
{/* Second Row - Term and Tags */}
|
2025-01-25 00:46:59 +08:00
|
|
|
|
<div className="flex flex-wrap gap-1">
|
2025-01-24 00:19:02 +08:00
|
|
|
|
{/* Tags Badges */}
|
2025-01-24 17:39:41 +08:00
|
|
|
|
{post?.meta?.tags &&
|
|
|
|
|
post.meta.tags.length > 0 &&
|
|
|
|
|
post.meta.tags.map((tag, index) => (
|
2025-01-25 00:46:59 +08:00
|
|
|
|
<Space key={index}>
|
|
|
|
|
<PostBadge
|
|
|
|
|
type="tag"
|
|
|
|
|
value={`#${tag}`}></PostBadge>
|
|
|
|
|
</Space>
|
2025-01-24 17:39:41 +08:00
|
|
|
|
))}
|
2025-01-24 00:19:02 +08:00
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Content Section */}
|
|
|
|
|
<motion.div
|
|
|
|
|
initial={{ opacity: 0 }}
|
|
|
|
|
animate={{ opacity: 1 }}
|
|
|
|
|
transition={{ delay: 0.6 }}
|
2025-01-25 00:46:59 +08:00
|
|
|
|
className="mt-6 text-secondary-700">
|
2025-01-24 00:19:02 +08:00
|
|
|
|
<div
|
2025-01-25 00:46:59 +08:00
|
|
|
|
className="ql-editor p-0 space-y-4 leading-relaxed duration-300"
|
2025-01-24 00:19:02 +08:00
|
|
|
|
dangerouslySetInnerHTML={{ __html: post?.content || "" }}
|
|
|
|
|
/>
|
|
|
|
|
</motion.div>
|
|
|
|
|
|
|
|
|
|
{/* Stats Section */}
|
2025-01-25 00:46:59 +08:00
|
|
|
|
<StatsSection></StatsSection>
|
2025-01-24 00:19:02 +08:00
|
|
|
|
</motion.div>
|
|
|
|
|
);
|
|
|
|
|
}
|