diff --git a/apps/backend/src/admin/dto/admin-web/create-blog.dto.ts b/apps/backend/src/admin/dto/admin-web/create-blog.dto.ts index 78df068..fe9e947 100644 --- a/apps/backend/src/admin/dto/admin-web/create-blog.dto.ts +++ b/apps/backend/src/admin/dto/admin-web/create-blog.dto.ts @@ -5,6 +5,9 @@ export class CreateBlogDto { @IsString() title: string; + @IsString() + slug: string;// 允许空串,但如果为空则需要手动设置为null,防止数据库唯一键冲突 + @IsString() description: string; diff --git a/apps/backend/src/blog/blog.service.ts b/apps/backend/src/blog/blog.service.ts index 0970d45..41bb138 100644 --- a/apps/backend/src/blog/blog.service.ts +++ b/apps/backend/src/blog/blog.service.ts @@ -57,6 +57,9 @@ export class BlogService { .digest('hex'); } } + if (typeof blog.slug === 'string' && blog.slug.trim().length === 0) { + blog.slug = null; + } const newBlog = this.blogRepository.create(blog); return this.blogRepository.save(newBlog); diff --git a/apps/frontend/app/console/(with-menu)/web/blog/components/AddBlog.tsx b/apps/frontend/app/console/(with-menu)/web/blog/components/AddBlog.tsx index 8c98c0b..753e2fc 100644 --- a/apps/frontend/app/console/(with-menu)/web/blog/components/AddBlog.tsx +++ b/apps/frontend/app/console/(with-menu)/web/blog/components/AddBlog.tsx @@ -27,12 +27,37 @@ export default function AddBlog({ children, onRefresh }: AddBlogProps) { const [open, setOpen] = useState(false); const [blog, setBlog] = useState({ title: "", + slug: "", description: "", contentUrl: "", permissions: [] as BlogPermission[], password: "", }); + const handleCopyShareURL = () => { + const slug = blog.slug.trim(); + if (slug.length === 0) { + return toast.warning('请先填写Slug') + } + + let url = `${window.location.origin}/blog/${slug}`; + + const password = blog.password.trim(); + if (blog.permissions.includes(BlogPermission.ByPassword)) { + if (password.length === 0) { + return toast.warning('开启了密码保护,但没有填写有效的密码,无法生成有效URL') + } else { + url += `?p=${blog.password.trim()}`; + } + } + + navigator.clipboard.writeText(url).then(() => { + toast.success('复制成功'); + }, () => { + toast.error('复制失败,请手动复制'); + }); + }; + const handleSubmit = async () => { try { const res = await AdminAPI.createBlog({ @@ -44,6 +69,7 @@ export default function AddBlog({ children, onRefresh }: AddBlogProps) { toast.success("添加成功"); setBlog({ title: '', + slug: '', description: '', contentUrl: '', permissions: [], @@ -92,6 +118,17 @@ export default function AddBlog({ children, onRefresh }: AddBlogProps) { onChange={(e) => setBlog({ ...blog, description: e.target.value })} /> +
+ + setBlog({ ...blog, slug: e.target.value })} + /> +
} - - - + +
+ +
+ + +
+
diff --git a/apps/frontend/lib/api/endpoints/admin.client.ts b/apps/frontend/lib/api/endpoints/admin.client.ts index dddcc27..d8a30a5 100644 --- a/apps/frontend/lib/api/endpoints/admin.client.ts +++ b/apps/frontend/lib/api/endpoints/admin.client.ts @@ -3,6 +3,7 @@ import { clientFetch } from "../client"; import { Blog } from "@/lib/types/blog"; import { BlogPermission } from "@/lib/types/Blog.Permission.enum"; import { Role } from "@/lib/types/role"; +import { APIError } from "../common"; export interface UserEntity { userId: string; @@ -87,9 +88,28 @@ export async function updateResource(id: string, data: UpdateResourceParams) { interface CreateBlogParams { title: string; description: string; + slug: string; contentUrl: string; + permissions: BlogPermission[]; + password: string; } export async function createBlog(data: CreateBlogParams) { + data.title = data.title.trim() + data.description = data.description.trim() + data.slug = data.slug.trim() + data.contentUrl = data.contentUrl.trim() + data.password = data.password.trim() + + if (data.title.length === 0) { + throw new APIError('标题不得为空') + } + if (data.description.length === 0) { + throw new APIError('描述不得为空') + } + if (data.contentUrl.length === 0) { + throw new APIError('文章链接不得为空') + } + return clientFetch('/api/admin/web/blog', { method: 'POST', body: JSON.stringify(data)