diff --git a/apps/web/src/components/models/post/detail/PostCommentEditor.tsx b/apps/web/src/components/models/post/detail/PostCommentEditor.tsx index 74df927..7a65b1d 100644 --- a/apps/web/src/components/models/post/detail/PostCommentEditor.tsx +++ b/apps/web/src/components/models/post/detail/PostCommentEditor.tsx @@ -3,7 +3,7 @@ import { motion } from "framer-motion"; import { Button, Input, Tabs } from "antd"; import QuillEditor from "@web/src/components/common/editor/quill/QuillEditor"; import { PostDetailContext } from "./context/PostDetailContext"; -import { usePost } from "@nice/client"; +import { useEntity } from "@nice/client"; import { PostType } from "@nice/common"; import toast from "react-hot-toast"; import { isContentEmpty } from "./utils"; @@ -18,8 +18,7 @@ export default function PostCommentEditor() { const [content, setContent] = useState(""); const [signature, setSignature] = useState(undefined); const [fileIds, setFileIds] = useState([]); - const { create } = usePost(); - + const { create } = useEntity("post") const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (isContentEmpty(content)) { diff --git a/apps/web/src/components/models/post/detail/PostHeader/Content.tsx b/apps/web/src/components/models/post/detail/PostHeader/Content.tsx index 0dd9ac6..cc75d39 100644 --- a/apps/web/src/components/models/post/detail/PostHeader/Content.tsx +++ b/apps/web/src/components/models/post/detail/PostHeader/Content.tsx @@ -1,7 +1,6 @@ import { useContext } from "react"; import { useState, useRef, useEffect } from "react"; import { PostDetailContext } from "../context/PostDetailContext"; -import { motion } from "framer-motion"; import { StatsSection } from "./StatsSection"; import PostResources from "../PostResources"; @@ -10,7 +9,7 @@ export default function Content() { const [isExpanded, setIsExpanded] = useState(false); const contentWrapperRef = useRef(null); const [shouldCollapse, setShouldCollapse] = useState(false); - const maxHeight = 125; + useEffect(() => { if (contentWrapperRef.current) { const shouldCollapse = contentWrapperRef.current.scrollHeight > 150; diff --git a/apps/web/src/components/models/post/detail/PostResources.tsx b/apps/web/src/components/models/post/detail/PostResources.tsx index 714b549..572bc8a 100644 --- a/apps/web/src/components/models/post/detail/PostResources.tsx +++ b/apps/web/src/components/models/post/detail/PostResources.tsx @@ -4,7 +4,9 @@ import { DownloadOutlined } from "@ant-design/icons"; import { PostDto } from "@nice/common"; import { env } from "@web/src/env"; import { getFileIcon } from "./utils"; -import { formatFileSize } from "@nice/utils"; +import { formatFileSize, getCompressedImageUrl } from '@nice/utils'; + + export default function PostResources({ post }: { post: PostDto }) { const { resources } = useMemo(() => { if (!post?.resources) return { resources: [] }; @@ -12,13 +14,16 @@ export default function PostResources({ post }: { post: PostDto }) { const isImage = (url: string) => /\.(png|jpg|jpeg|gif|webp)$/i.test(url); - const sortedResources = post.resources - .map((resource) => ({ + const sortedResources = post.resources.map(resource => { + const original = `http://${env.SERVER_IP}/uploads/${resource.url}` + const isImg = isImage(resource.url) + return { ...resource, - url: `http://${env.SERVER_IP}/uploads/${resource.url}`, - isImage: isImage(resource.url), - })) - .sort((a, b) => (a.isImage === b.isImage ? 0 : a.isImage ? -1 : 1)); + url: isImg ? getCompressedImageUrl(original) : original, + originalUrl: original, + isImage: isImg + } + }).sort((a, b) => a.isImage === b.isImage ? 0 : a.isImage ? -1 : 1) return { resources: sortedResources }; }, [post]); @@ -46,6 +51,7 @@ export default function PostResources({ post }: { post: PostDto }) { src={resource.url} alt={resource.title} preview={{ + src: resource.originalUrl, mask: (
点击预览 diff --git a/packages/client/src/api/hooks/index.ts b/packages/client/src/api/hooks/index.ts index 9f4f998..3c543ed 100644 --- a/packages/client/src/api/hooks/index.ts +++ b/packages/client/src/api/hooks/index.ts @@ -9,3 +9,4 @@ export * from "./useTaxonomy" export * from "./useVisitor" export * from "./useMessage" export * from "./usePost" +export * from "./useEntity" \ No newline at end of file diff --git a/packages/common/prisma/schema.prisma b/packages/common/prisma/schema.prisma index 9e49147..e36573e 100644 --- a/packages/common/prisma/schema.prisma +++ b/packages/common/prisma/schema.prisma @@ -70,11 +70,11 @@ model TermAncestry { } model Staff { - id String @id @default(cuid()) - showname String? @map("showname") - username String @unique @map("username") - avatar String? @map("avatar") - password String? @map("password") + id String @id @default(cuid()) + showname String? @map("showname") + username String @unique @map("username") + avatar String? @map("avatar") + password String? @map("password") phoneNumber String? @unique @map("phone_number") @@ -196,16 +196,16 @@ model Post { domainId String? @map("domain_id") terms Term[] @relation("post_term") // 日期时间类型字段 - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @map("updated_at") + createdAt DateTime? @default(now()) @map("created_at") + updatedAt DateTime? @map("updated_at") deletedAt DateTime? @map("deleted_at") // 删除时间,可为空 // 关系类型字段 authorId String? @map("author_id") author Staff? @relation("post_author", fields: [authorId], references: [id]) // 帖子作者,关联 Staff 模型 visits Visit[] // 访问记录,关联 Visit 模型 - views Int @default(0) - likes Int @default(0) - hates Int @default(0) + views Int? @default(0) + likes Int? @default(0) + hates Int? @default(0) receivers Staff[] @relation("post_receiver") parentId String? @map("parent_id") parent Post? @relation("PostChildren", fields: [parentId], references: [id]) // 父级帖子,关联 Post 模型 diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 51f2b94..b08b0b5 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -27,5 +27,10 @@ export const formatFileSize = (bytes: number) => { if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`; }; - +// 压缩图片路径生成函数 +export const getCompressedImageUrl = (originalUrl: string): string => { + const cleanUrl = originalUrl.split(/[?#]/)[0] // 移除查询参数和哈希 + const lastSlashIndex = cleanUrl.lastIndexOf('/') + return `${cleanUrl.slice(0, lastSlashIndex)}/compressed/${cleanUrl.slice(lastSlashIndex + 1).replace(/\.[^.]+$/, '.webp')}` +} export * from "./types" \ No newline at end of file