doctor-mail/apps/web/src/components/models/post/detail/PostHeader.tsx

167 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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";
import { Button, Typography, Space, Tooltip } from "antd";
import { useVisitor } from "@nice/client";
import { PostState, VisitType } from "@nice/common";
import {
CalendarOutlined,
ClockCircleOutlined,
CommentOutlined,
EyeOutlined,
FileTextOutlined,
FolderOutlined,
LikeFilled,
LikeOutlined,
LockOutlined,
UnlockOutlined,
UserOutlined,
} 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";
import { PostBadge } from "./badge/PostBadge";
const { Title, Paragraph, Text } = Typography;
export default function PostHeader() {
const { post, user } = useContext(PostDetailContext);
const { like, unLike } = useVisitor();
function likeThisPost() {
if (!post?.liked) {
post.likes += 1;
post.liked = true;
like.mutateAsync({
data: {
visitorId: user?.id || null,
postId: post.id,
type: VisitType.LIKE,
},
});
} else {
post.likes -= 1;
post.liked = false;
unLike.mutateAsync({
where: {
visitorId: user?.id || null,
postId: post.id,
type: VisitType.LIKE,
},
});
}
}
return (
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
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">
{/* Corner Decorations */}
<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" />
{/* Title Section */}
<TitleSection></TitleSection>
<div className="space-y-4">
{/* 收件人信息行 */}
<Space>
<UserOutlined className="text-secondary-400" />
<span className="text-secondary-400"></span>
<Text strong>
{post?.receivers?.map((receiver) => receiver?.showname)}
</Text>
</Space>
{/* First Row - Basic Info */}
<div className="flex flex-wrap items-center gap-1">
{/* Author Info Badge */}
<Space>
<UserOutlined className="text-secondary-400" />
<span className="text-secondary-400"></span>
<Text strong>
{post?.author?.showname || "匿名用户"}
</Text>
</Space>
<Text type="secondary">|</Text>
{/* Date Info Badge */}
<Space>
<CalendarOutlined className="text-secondary-400" />
<Text>
:
{dayjs(post?.createdAt).format("YYYY-MM-DD")}
</Text>
</Space>
<Text type="secondary">|</Text>
{/* Last Updated Badge */}
<Space>
<ClockCircleOutlined className="text-secondary-400" />
<Text>
:
{dayjs(post?.updatedAt).format("YYYY-MM-DD")}
</Text>
</Space>
<Text type="secondary">|</Text>
{/* Visibility Status Badge */}
<Space>
{post?.isPublic ? (
<UnlockOutlined className="text-secondary-400" />
) : (
<LockOutlined className="text-secondary-400" />
)}
<Text>{post?.isPublic ? "公开" : "私信"}</Text>
</Space>
</div>
{/* Second Row - Term and Tags */}
<div className="flex flex-wrap gap-1">
{/* Tags Badges */}
{post?.meta?.tags &&
post.meta.tags.length > 0 &&
post.meta.tags.map((tag, index) => (
<Space key={index}>
<PostBadge
type="tag"
value={`#${tag}`}></PostBadge>
</Space>
))}
</div>
</div>
{/* Content Section */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.6 }}
className="mt-6 text-secondary-700">
<div
className="ql-editor p-0 space-y-4 leading-relaxed duration-300"
dangerouslySetInnerHTML={{ __html: post?.content || "" }}
/>
</motion.div>
{/* Stats Section */}
<StatsSection></StatsSection>
</motion.div>
);
}