2025-01-24 15:05:03 +08:00
|
|
|
import { EyeOutlined, LikeOutlined, LikeFilled, UserOutlined, BankOutlined, CalendarOutlined, FileTextOutlined } from '@ant-design/icons';
|
|
|
|
import { Button, Typography, Space, Tooltip } from 'antd';
|
|
|
|
import toast from 'react-hot-toast';
|
|
|
|
import { useState } from 'react';
|
2025-01-25 00:52:30 +08:00
|
|
|
import { getBadgeStyle } from '@web/src/app/main/letter/list/utils';
|
|
|
|
import { PostDto } from '@nice/common';
|
|
|
|
import dayjs from 'dayjs';
|
2025-01-24 15:05:03 +08:00
|
|
|
const { Title, Paragraph, Text } = Typography;
|
2025-01-22 23:19:58 +08:00
|
|
|
|
|
|
|
interface LetterCardProps {
|
2025-01-25 00:52:30 +08:00
|
|
|
letter: PostDto;
|
2025-01-22 23:19:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
export function LetterCard({ letter }: LetterCardProps) {
|
2025-01-25 00:46:59 +08:00
|
|
|
const [likes, setLikes] = useState(0);
|
|
|
|
const [liked, setLiked] = useState(false);
|
|
|
|
const [views] = useState(Math.floor(Math.random() * 100)); // 模拟浏览量数据
|
2025-01-24 15:05:03 +08:00
|
|
|
|
2025-01-25 00:46:59 +08:00
|
|
|
const handleLike = () => {
|
|
|
|
if (!liked) {
|
|
|
|
setLikes((prev) => prev + 1);
|
|
|
|
setLiked(true);
|
|
|
|
toast.success("已点赞!", {
|
|
|
|
icon: <LikeFilled className="text-blue-500" />,
|
|
|
|
className: "custom-message",
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
setLikes((prev) => prev - 1);
|
|
|
|
setLiked(false);
|
|
|
|
toast("已取消点赞", {
|
|
|
|
className: "custom-message",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
2025-01-24 15:05:03 +08:00
|
|
|
|
2025-01-25 00:46:59 +08:00
|
|
|
return (
|
|
|
|
<div className="w-full p-4 bg-white transition-all duration-300 ease-in-out group">
|
|
|
|
<div className="flex flex-col gap-3">
|
|
|
|
{/* Title & Priority */}
|
|
|
|
<div className="flex justify-between items-start">
|
|
|
|
<Title level={4} className="!mb-0 flex-1">
|
|
|
|
<a
|
|
|
|
href={`/letters/${letter.id}`}
|
|
|
|
target="_blank"
|
|
|
|
className="text-primary transition-all duration-300 relative
|
2025-01-24 17:37:51 +08:00
|
|
|
before:absolute before:bottom-0 before:left-0 before:w-0 before:h-[2px] before:bg-primary-600
|
2025-01-24 15:05:03 +08:00
|
|
|
group-hover:before:w-full before:transition-all before:duration-300
|
2025-01-24 17:37:51 +08:00
|
|
|
group-hover:text-primary-600 group-hover:scale-105 group-hover:drop-shadow-md"
|
2025-01-22 23:19:58 +08:00
|
|
|
>
|
|
|
|
{letter.title}
|
|
|
|
</a>
|
2025-01-24 15:05:03 +08:00
|
|
|
</Title>
|
2025-01-25 00:52:30 +08:00
|
|
|
|
2025-01-22 23:19:58 +08:00
|
|
|
</div>
|
|
|
|
|
2025-01-24 15:05:03 +08:00
|
|
|
{/* Meta Info */}
|
2025-01-24 17:37:51 +08:00
|
|
|
<div className="flex justify-between items-center text-sm text-secondary">
|
2025-01-24 15:05:03 +08:00
|
|
|
<Space size="middle">
|
|
|
|
<Space>
|
2025-01-24 17:37:51 +08:00
|
|
|
<UserOutlined className="text-secondary-400" />
|
2025-01-25 00:52:30 +08:00
|
|
|
<Text strong>{letter.author.showname}</Text>
|
2025-01-24 15:05:03 +08:00
|
|
|
</Space>
|
|
|
|
<Text type="secondary">|</Text>
|
|
|
|
<Space>
|
2025-01-24 17:37:51 +08:00
|
|
|
<BankOutlined className="text-secondary-400" />
|
2025-01-25 00:52:30 +08:00
|
|
|
<Text>{letter.author.department.name}</Text>
|
2025-01-24 15:05:03 +08:00
|
|
|
</Space>
|
|
|
|
</Space>
|
|
|
|
<Space>
|
2025-01-24 17:37:51 +08:00
|
|
|
<CalendarOutlined className="text-secondary-400" />
|
2025-01-25 00:52:30 +08:00
|
|
|
<Text type="secondary">{dayjs(letter.createdAt).format('YYYY-MM-DD')}</Text>
|
2025-01-24 15:05:03 +08:00
|
|
|
</Space>
|
2025-01-22 23:19:58 +08:00
|
|
|
</div>
|
|
|
|
|
2025-01-25 00:46:59 +08:00
|
|
|
{/* Content Preview */}
|
|
|
|
{letter.content && (
|
|
|
|
<div className="flex items-start gap-2">
|
|
|
|
<FileTextOutlined className="text-gray-400 mt-1" />
|
|
|
|
<Paragraph
|
|
|
|
ellipsis={{ rows: 2 }}
|
|
|
|
className="!mb-3 text-gray-600 flex-1">
|
|
|
|
{letter.content}
|
|
|
|
</Paragraph>
|
|
|
|
</div>
|
|
|
|
)}
|
2025-01-24 15:05:03 +08:00
|
|
|
|
|
|
|
{/* Badges & Interactions */}
|
|
|
|
<div className="flex justify-between items-center">
|
|
|
|
<Space size="small" wrap className="flex-1">
|
2025-01-25 00:52:30 +08:00
|
|
|
<Badge type="category" value={'11'} />
|
|
|
|
<Badge type="status" value={'22'} />
|
2025-01-24 15:05:03 +08:00
|
|
|
</Space>
|
|
|
|
|
2025-01-25 00:46:59 +08:00
|
|
|
<div className="flex items-center gap-4">
|
|
|
|
<div className="flex items-center gap-1 text-gray-500">
|
|
|
|
<EyeOutlined className="text-lg" />
|
|
|
|
<span className="text-sm">{views}</span>
|
|
|
|
</div>
|
|
|
|
<Tooltip
|
|
|
|
title={liked ? "取消点赞" : "点赞"}
|
|
|
|
placement="top">
|
|
|
|
<Button
|
|
|
|
type={liked ? "primary" : "default"}
|
|
|
|
shape="round"
|
|
|
|
size="small"
|
|
|
|
icon={liked ? <LikeFilled /> : <LikeOutlined />}
|
|
|
|
onClick={handleLike}
|
|
|
|
className={`
|
2025-01-24 15:05:03 +08:00
|
|
|
flex items-center gap-1 px-3 transform transition-all duration-300
|
|
|
|
hover:scale-105 hover:shadow-md
|
2025-01-25 00:46:59 +08:00
|
|
|
${liked ? "bg-blue-500 hover:bg-blue-600" : "hover:border-blue-500 hover:text-blue-500"}
|
|
|
|
`}>
|
|
|
|
<span className={liked ? "text-white" : ""}>
|
|
|
|
{likes}
|
|
|
|
</span>
|
|
|
|
</Button>
|
|
|
|
</Tooltip>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
2025-01-22 23:19:58 +08:00
|
|
|
}
|
|
|
|
|
2025-01-25 00:46:59 +08:00
|
|
|
export function Badge({
|
|
|
|
type,
|
|
|
|
value,
|
|
|
|
className = "",
|
2025-01-24 15:05:03 +08:00
|
|
|
}: {
|
2025-01-25 00:46:59 +08:00
|
|
|
type: "priority" | "category" | "status";
|
|
|
|
value: string;
|
|
|
|
className?: string;
|
2025-01-24 15:05:03 +08:00
|
|
|
}) {
|
2025-01-25 00:46:59 +08:00
|
|
|
return (
|
|
|
|
value && (
|
|
|
|
<span
|
|
|
|
className={`
|
2025-01-24 15:05:03 +08:00
|
|
|
inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
2025-01-22 23:19:58 +08:00
|
|
|
${getBadgeStyle(type, value)}
|
2025-01-24 15:05:03 +08:00
|
|
|
transition-all duration-200 ease-in-out transform hover:scale-105
|
|
|
|
${className}
|
2025-01-25 00:46:59 +08:00
|
|
|
`}>
|
|
|
|
|
|
|
|
{value?.toUpperCase()}
|
|
|
|
</span>
|
|
|
|
)
|
|
|
|
);
|
2025-01-22 23:19:58 +08:00
|
|
|
}
|