01261936
This commit is contained in:
parent
003208a5f5
commit
5695c59a01
|
@ -9,6 +9,7 @@ import toast from "react-hot-toast";
|
||||||
import { isContentEmpty } from "./utils";
|
import { isContentEmpty } from "./utils";
|
||||||
import { SendOutlined } from "@ant-design/icons";
|
import { SendOutlined } from "@ant-design/icons";
|
||||||
import { TusUploader } from "@web/src/components/common/uploader/TusUploader";
|
import { TusUploader } from "@web/src/components/common/uploader/TusUploader";
|
||||||
|
import { useEntity } from '../../../../../../../packages/client/src/api/hooks/useEntity';
|
||||||
|
|
||||||
const { TabPane } = Tabs;
|
const { TabPane } = Tabs;
|
||||||
|
|
||||||
|
@ -17,8 +18,8 @@ export default function PostCommentEditor() {
|
||||||
const [content, setContent] = useState("");
|
const [content, setContent] = useState("");
|
||||||
const [isPreview, setIsPreview] = useState(false);
|
const [isPreview, setIsPreview] = useState(false);
|
||||||
const [fileIds, setFileIds] = useState<string[]>([]);
|
const [fileIds, setFileIds] = useState<string[]>([]);
|
||||||
const { create } = usePost();
|
// const { create } = usePost();
|
||||||
|
const {create}=useEntity("post")
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (isContentEmpty(content)) {
|
if (isContentEmpty(content)) {
|
||||||
|
@ -38,6 +39,7 @@ export default function PostCommentEditor() {
|
||||||
fileId: id,
|
fileId: id,
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
toast.success("发布成功!");
|
toast.success("发布成功!");
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
import { useState, useRef, useEffect } from "react";
|
import { useState, useRef, useEffect } from "react";
|
||||||
import { PostDetailContext } from "../context/PostDetailContext";
|
import { PostDetailContext } from "../context/PostDetailContext";
|
||||||
import { motion } from "framer-motion";
|
|
||||||
import { StatsSection } from "./StatsSection";
|
import { StatsSection } from "./StatsSection";
|
||||||
import PostResources from "../PostResources";
|
import PostResources from "../PostResources";
|
||||||
|
|
||||||
|
@ -10,7 +9,7 @@ export default function Content() {
|
||||||
const [isExpanded, setIsExpanded] = useState(false);
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
const contentWrapperRef = useRef(null);
|
const contentWrapperRef = useRef(null);
|
||||||
const [shouldCollapse, setShouldCollapse] = useState(false);
|
const [shouldCollapse, setShouldCollapse] = useState(false);
|
||||||
const maxHeight = 125;
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (contentWrapperRef.current) {
|
if (contentWrapperRef.current) {
|
||||||
const shouldCollapse = contentWrapperRef.current.scrollHeight > 150;
|
const shouldCollapse = contentWrapperRef.current.scrollHeight > 150;
|
||||||
|
|
|
@ -4,7 +4,9 @@ import { DownloadOutlined } from "@ant-design/icons";
|
||||||
import { PostDto } from "@nice/common";
|
import { PostDto } from "@nice/common";
|
||||||
import { env } from "@web/src/env";
|
import { env } from "@web/src/env";
|
||||||
import { getFileIcon } from "./utils";
|
import { getFileIcon } from "./utils";
|
||||||
import { formatFileSize } from '@nice/utils';
|
import { formatFileSize, getCompressedImageUrl } from '@nice/utils';
|
||||||
|
|
||||||
|
|
||||||
export default function PostResources({ post }: { post: PostDto }) {
|
export default function PostResources({ post }: { post: PostDto }) {
|
||||||
const { resources } = useMemo(() => {
|
const { resources } = useMemo(() => {
|
||||||
if (!post?.resources) return { resources: [] };
|
if (!post?.resources) return { resources: [] };
|
||||||
|
@ -12,13 +14,16 @@ export default function PostResources({ post }: { post: PostDto }) {
|
||||||
const isImage = (url: string) =>
|
const isImage = (url: string) =>
|
||||||
/\.(png|jpg|jpeg|gif|webp)$/i.test(url);
|
/\.(png|jpg|jpeg|gif|webp)$/i.test(url);
|
||||||
|
|
||||||
const sortedResources = post.resources
|
const sortedResources = post.resources.map(resource => {
|
||||||
.map((resource) => ({
|
const original = `http://${env.SERVER_IP}/uploads/${resource.url}`
|
||||||
|
const isImg = isImage(resource.url)
|
||||||
|
return {
|
||||||
...resource,
|
...resource,
|
||||||
url: `http://${env.SERVER_IP}/uploads/${resource.url}`,
|
url: isImg ? getCompressedImageUrl(original) : original,
|
||||||
isImage: isImage(resource.url),
|
originalUrl: original,
|
||||||
}))
|
isImage: isImg
|
||||||
.sort((a, b) => (a.isImage === b.isImage ? 0 : a.isImage ? -1 : 1));
|
}
|
||||||
|
}).sort((a, b) => a.isImage === b.isImage ? 0 : a.isImage ? -1 : 1)
|
||||||
|
|
||||||
return { resources: sortedResources };
|
return { resources: sortedResources };
|
||||||
}, [post]);
|
}, [post]);
|
||||||
|
@ -46,6 +51,7 @@ export default function PostResources({ post }: { post: PostDto }) {
|
||||||
src={resource.url}
|
src={resource.url}
|
||||||
alt={resource.title}
|
alt={resource.title}
|
||||||
preview={{
|
preview={{
|
||||||
|
src: resource.originalUrl,
|
||||||
mask: (
|
mask: (
|
||||||
<div className="flex items-center justify-center text-white">
|
<div className="flex items-center justify-center text-white">
|
||||||
点击预览
|
点击预览
|
||||||
|
|
|
@ -93,7 +93,7 @@ export function LetterBasicForm() {
|
||||||
</TabPane>
|
</TabPane>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
{/* Footer Actions */}
|
{/* Footer Actions */}
|
||||||
<div className="flex flex-col-reverse sm:flex-row items-center justify-between gap-4 mt-6">
|
<div className="flex flex-col-reverse sm:flex-row items-center justify-between gap-4 ">
|
||||||
<Form.Item name="isPublic" valuePropName="checked">
|
<Form.Item name="isPublic" valuePropName="checked">
|
||||||
<Checkbox className="text-gray-600 hover:text-gray-900 transition-colors text-sm">
|
<Checkbox className="text-gray-600 hover:text-gray-900 transition-colors text-sm">
|
||||||
是否公开
|
是否公开
|
||||||
|
|
|
@ -70,11 +70,11 @@ model TermAncestry {
|
||||||
}
|
}
|
||||||
|
|
||||||
model Staff {
|
model Staff {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
showname String? @map("showname")
|
showname String? @map("showname")
|
||||||
username String @unique @map("username")
|
username String @unique @map("username")
|
||||||
avatar String? @map("avatar")
|
avatar String? @map("avatar")
|
||||||
password String? @map("password")
|
password String? @map("password")
|
||||||
|
|
||||||
phoneNumber String? @unique @map("phone_number")
|
phoneNumber String? @unique @map("phone_number")
|
||||||
|
|
||||||
|
@ -196,16 +196,16 @@ model Post {
|
||||||
domainId String? @map("domain_id")
|
domainId String? @map("domain_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")
|
||||||
updatedAt DateTime @map("updated_at")
|
updatedAt DateTime? @map("updated_at")
|
||||||
deletedAt DateTime? @map("deleted_at") // 删除时间,可为空
|
deletedAt DateTime? @map("deleted_at") // 删除时间,可为空
|
||||||
// 关系类型字段
|
// 关系类型字段
|
||||||
authorId String? @map("author_id")
|
authorId String? @map("author_id")
|
||||||
author Staff? @relation("post_author", fields: [authorId], references: [id]) // 帖子作者,关联 Staff 模型
|
author Staff? @relation("post_author", fields: [authorId], references: [id]) // 帖子作者,关联 Staff 模型
|
||||||
visits Visit[] // 访问记录,关联 Visit 模型
|
visits Visit[] // 访问记录,关联 Visit 模型
|
||||||
views Int @default(0)
|
views Int? @default(0)
|
||||||
likes Int @default(0)
|
likes Int? @default(0)
|
||||||
hates Int @default(0)
|
hates 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 模型
|
||||||
|
|
|
@ -27,5 +27,10 @@ export const formatFileSize = (bytes: number) => {
|
||||||
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
||||||
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
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"
|
export * from "./types"
|
Loading…
Reference in New Issue