This commit is contained in:
longdayi 2025-01-25 21:22:20 +08:00
parent a2da55bd9e
commit 30968f4fab
6 changed files with 132 additions and 146 deletions

View File

@ -6,6 +6,7 @@ import {
BankOutlined, BankOutlined,
CalendarOutlined, CalendarOutlined,
FileTextOutlined, FileTextOutlined,
SendOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { Button, Typography, Space, Tooltip } from "antd"; import { Button, Typography, Space, Tooltip } from "antd";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
@ -13,6 +14,7 @@ import { useState } from "react";
import { getBadgeStyle } from "@web/src/app/main/letter/list/utils"; import { getBadgeStyle } from "@web/src/app/main/letter/list/utils";
import { PostDto } from "@nice/common"; import { PostDto } from "@nice/common";
import dayjs from "dayjs"; import dayjs from "dayjs";
import PostLikeButton from "./detail/PostHeader/PostLikeButton";
const { Title, Paragraph, Text } = Typography; const { Title, Paragraph, Text } = Typography;
interface LetterCardProps { interface LetterCardProps {
@ -20,29 +22,11 @@ interface LetterCardProps {
} }
export function LetterCard({ letter }: LetterCardProps) { export function LetterCard({ letter }: LetterCardProps) {
const [likes, setLikes] = useState(0);
const [liked, setLiked] = useState(false);
const [views] = useState(Math.floor(Math.random() * 100)); // 模拟浏览量数据
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",
});
}
};
return ( return (
<div className="w-full p-4 bg-white transition-all duration-300 ease-in-out group"> <div className="w-full border-2 p-4 bg-white rounded-xl transition-all duration-300 ease-in-out group">
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
{/* Title & Priority */} {/* Title & Priority */}
<div className="flex justify-between items-start"> <div className="flex justify-between items-start">
@ -63,16 +47,20 @@ export function LetterCard({ letter }: LetterCardProps) {
<div className="flex justify-between items-center text-sm text-secondary"> <div className="flex justify-between items-center text-sm text-secondary">
<Space size="middle"> <Space size="middle">
<Space> <Space>
<UserOutlined className="text-secondary-400" /> <UserOutlined className=" text-secondary-400"></UserOutlined>
<Text strong> <Text>
{letter.author?.showname || {letter.author?.showname || '匿名用户'}
letter?.author?.username}
</Text> </Text>
</Space> </Space>
<Text type="secondary">|</Text>
<Space> <Space>
<BankOutlined className="text-secondary-400" /> <BankOutlined className="text-secondary-400" />
<Text>{letter.author?.department?.name}</Text> <Text>{letter.receivers.map(item => item.department?.name).toString()}</Text>
</Space>
<Space>
<SendOutlined className=" text-secondary-400"></SendOutlined>
<Text >
{letter.receivers.map(item => item.showname).toString()}
</Text>
</Space> </Space>
</Space> </Space>
<Space> <Space>
@ -105,27 +93,9 @@ export function LetterCard({ letter }: LetterCardProps) {
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<div className="flex items-center gap-1 text-gray-500"> <div className="flex items-center gap-1 text-gray-500">
<EyeOutlined className="text-lg" /> <EyeOutlined className="text-lg" />
<span className="text-sm">{views}</span> <span className="text-sm">{letter.views}</span>
</div> </div>
<Tooltip <PostLikeButton post={letter as any}></PostLikeButton>
title={liked ? "取消点赞" : "点赞"}
placement="top">
<Button
type={liked ? "primary" : "default"}
shape="round"
size="small"
icon={liked ? <LikeFilled /> : <LikeOutlined />}
onClick={handleLike}
className={`
flex items-center gap-1 px-3 transform transition-all duration-300
hover:scale-105 hover:shadow-md
${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>
</div> </div>

View File

@ -4,11 +4,11 @@ import { useContext } from "react";
import { PostDetailContext } from "../context/PostDetailContext"; import { PostDetailContext } from "../context/PostDetailContext";
import { Button, Tooltip } from "antd"; import { Button, Tooltip } from "antd";
import { LikeFilled, LikeOutlined } from "@ant-design/icons"; import { LikeFilled, LikeOutlined } from "@ant-design/icons";
import { useAuth } from "@web/src/providers/auth-provider";
export default function PostLikeButton({ post }: { post: PostDto }) { export default function PostLikeButton({ post }: { post: PostDto }) {
const { user } = useContext(PostDetailContext); const { user } = useAuth();
const { like, unLike } = useVisitor(); const { like, unLike } = useVisitor();
function likeThisPost() { function likeThisPost() {
if (!post?.liked) { if (!post?.liked) {
post.likes += 1; post.likes += 1;

View File

@ -4,6 +4,7 @@ import { api, usePost } from "@nice/client";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { PostState, PostType } from "@nice/common"; import { PostState, PostType } from "@nice/common";
import dayjs from "dayjs";
export interface LetterFormData { export interface LetterFormData {
title: string; title: string;
@ -79,9 +80,25 @@ export function LetterFormProvider({
: undefined, : undefined,
}, },
}); });
// console.log(123); const formattedDateTime = dayjs().format('YYYY-MM-DD HH:mm:ss')
// 创建包含信件编号和提交时间的文本
const fileContent = `信件编号: ${result.id}\n投递时间: ${formattedDateTime}`;
// 创建包含信件编号和提交时间的Blob对象
const blob = new Blob([fileContent], { type: 'text/plain' });
// 创建下载链接
const downloadUrl = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = downloadUrl;
link.download = `信件编号-${result.id}.txt`; // 设置下载文件名
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(downloadUrl);
toast.success(`信件投递成功!信件编号已保存到本地,请妥善保管用于进度查询`, {
duration: 5000 // 10秒
});
navigate(`/${result.id}/detail`, { replace: true }); navigate(`/${result.id}/detail`, { replace: true });
toast.success("发送成功!");
form.resetFields(); form.resetFields();
} catch (error) { } catch (error) {
console.error("Error submitting form:", error); console.error("Error submitting form:", error);

View File

@ -5,6 +5,7 @@ import { LetterCard } from "../LetterCard";
import { NonVoid } from "@nice/utils"; import { NonVoid } from "@nice/utils";
import { SearchOutlined } from '@ant-design/icons'; import { SearchOutlined } from '@ant-design/icons';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import { postDetailSelect } from '@nice/common';
export default function LetterList({ params }: { params: NonVoid<RouterInputs["post"]["findManyWithPagination"]> }) { export default function LetterList({ params }: { params: NonVoid<RouterInputs["post"]["findManyWithPagination"]> }) {
const [searchText, setSearchText] = useState(''); const [searchText, setSearchText] = useState('');
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
@ -20,9 +21,14 @@ export default function LetterList({ params }: { params: NonVoid<RouterInputs["p
}], }],
...params?.where ...params?.where
}, },
select: params.select select: {
...postDetailSelect,
...params.select
}
}); });
useEffect(() => {
console.log(data)
}, [data])
// Debounced search function // Debounced search function
const debouncedSearch = useMemo( const debouncedSearch = useMemo(
() => () =>
@ -73,7 +79,7 @@ export default function LetterList({ params }: { params: NonVoid<RouterInputs["p
</div> </div>
) : data?.items.length ? ( ) : data?.items.length ? (
<> <>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-6"> <div className="grid grid-cols-1 md:grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
{data.items.map((letter: any) => ( {data.items.map((letter: any) => (
<LetterCard key={letter.id} letter={letter} /> <LetterCard key={letter.id} letter={letter} />
))} ))}

View File

@ -192,11 +192,7 @@ model Post {
state String? // 状态 未读、处理中、已回答 state String? // 状态 未读、处理中、已回答
title String? // 帖子标题,可为空 title String? // 帖子标题,可为空
content String? // 帖子内容,可为空 content String? // 帖子内容,可为空
domainId String? @map("domain_id") domainId String? @map("domain_id")
// term Term? @relation(fields: [termId], references: [id])
// termId String? @map("term_id")
// 添加多对多关系
terms Term[] @relation("post_term") terms Term[] @relation("post_term")
// 日期时间类型字段 // 日期时间类型字段
createdAt DateTime @default(now()) @map("created_at") createdAt DateTime @default(now()) @map("created_at")
@ -208,7 +204,6 @@ model Post {
visits Visit[] // 访问记录,关联 Visit 模型 visits Visit[] // 访问记录,关联 Visit 模型
views Int @default(0) views Int @default(0)
likes Int @default(0) likes Int @default(0)
receivers Staff[] @relation("post_receiver") receivers Staff[] @relation("post_receiver")
parentId String? @map("parent_id") parentId String? @map("parent_id")
parent Post? @relation("PostChildren", fields: [parentId], references: [id]) // 父级帖子,关联 Post 模型 parent Post? @relation("PostChildren", fields: [parentId], references: [id]) // 父级帖子,关联 Post 模型

View File

@ -14,9 +14,7 @@ export const postDetailSelect: Prisma.PostSelect = {
updatedAt: true, updatedAt: true,
terms: { terms: {
include: { select: { id: true, name: true },
},
}, },
authorId: true, authorId: true,
author: { author: {