实现添加博客
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog"
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { AdminApi } from "@/lib/api";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
interface AddBlogProps {
|
||||
children: React.ReactNode;
|
||||
onRefresh: () => void;
|
||||
}
|
||||
|
||||
export default function AddBlog({ children, onRefresh }: AddBlogProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [blog, setBlog] = useState({
|
||||
title: "",
|
||||
description: "",
|
||||
contentUrl: "",
|
||||
});
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
const res = await AdminApi.web.blog.create({
|
||||
...blog,
|
||||
});
|
||||
if (res) {
|
||||
setOpen(false);
|
||||
onRefresh();
|
||||
toast.success("添加成功");
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error((error as Error).message || "添加失败");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
{children}
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>添加博客</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid gap-4 py-4">
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="title" className="text-right">
|
||||
标题
|
||||
</Label>
|
||||
<Input
|
||||
id="title"
|
||||
className="col-span-3"
|
||||
value={blog.title}
|
||||
onChange={(e) => setBlog({ ...blog, title: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="description" className="text-right">
|
||||
描述
|
||||
</Label>
|
||||
<Input
|
||||
id="description"
|
||||
className="col-span-3"
|
||||
value={blog.description}
|
||||
onChange={(e) => setBlog({ ...blog, description: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="contentUrl" className="text-right">
|
||||
文章URL
|
||||
</Label>
|
||||
<Input
|
||||
id="contentUrl"
|
||||
className="col-span-3"
|
||||
value={blog.contentUrl}
|
||||
onChange={(e) => setBlog({ ...blog, contentUrl: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button type="button" variant='secondary' onClick={() => setOpen(false)}>取消</Button>
|
||||
<Button type="button" onClick={handleSubmit}>保存</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
@@ -8,41 +8,58 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table"
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
|
||||
import { Blog } from "@/lib/types/blog"
|
||||
|
||||
interface BlogTableProps {
|
||||
blogs: {
|
||||
|
||||
}[]
|
||||
blogs: Blog[],
|
||||
error?: string,
|
||||
onRefresh?: () => void,
|
||||
}
|
||||
|
||||
export default function BlogTable({ blogs }: BlogTableProps) {
|
||||
export default function BlogTable({ blogs, error, onRefresh }: BlogTableProps) {
|
||||
return (
|
||||
<Table>
|
||||
<TableCaption>A list of your recent invoices.</TableCaption>
|
||||
{
|
||||
error && (
|
||||
<TableCaption>{error}</TableCaption>
|
||||
)
|
||||
}
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-[100px]">Invoice</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>Method</TableHead>
|
||||
<TableHead className="text-right">Amount</TableHead>
|
||||
<TableHead className="w-[100px]">Id</TableHead>
|
||||
<TableHead>标题</TableHead>
|
||||
<TableHead>描述</TableHead>
|
||||
<TableHead>文章URL</TableHead>
|
||||
<TableHead className="text-right">操作</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{/* {invoices.map((invoice) => (
|
||||
<TableRow key={invoice.invoice}>
|
||||
<TableCell className="font-medium">{invoice.invoice}</TableCell>
|
||||
<TableCell>{invoice.paymentStatus}</TableCell>
|
||||
<TableCell>{invoice.paymentMethod}</TableCell>
|
||||
<TableCell className="text-right">{invoice.totalAmount}</TableCell>
|
||||
{blogs.map((blog) => (
|
||||
<TableRow key={blog.id}>
|
||||
<TableCell className="font-medium">
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="max-w-[100px] overflow-hidden text-ellipsis">{blog.id}</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{blog.id}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</TableCell>
|
||||
<TableCell>{blog.title}</TableCell>
|
||||
<TableCell>{blog.description}</TableCell>
|
||||
<TableCell>{blog.contentUrl}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
{/* <ResourceEdit id={resource.id} onRefresh={() => onRefresh()}>
|
||||
<Button variant={'outline'} size={'sm'}>编辑</Button>
|
||||
</ResourceEdit> */}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))} */}
|
||||
))}
|
||||
</TableBody>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TableCell colSpan={3}>Total</TableCell>
|
||||
<TableCell className="text-right">$2,500.00</TableCell>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
</Table>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,21 @@
|
||||
"use client"
|
||||
|
||||
import { useBlogList } from "@/hooks/admin/web/blog/use-blog-list"
|
||||
import BlogTable from "./components/BlogTable"
|
||||
import AddBlog from "./components/AddBlog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
export default function Page() {
|
||||
const { blogs, error, isLoading, refresh } = useBlogList();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<div>
|
||||
<AddBlog onRefresh={refresh}>
|
||||
<Button>添加博客</Button>
|
||||
</AddBlog>
|
||||
</div>
|
||||
<BlogTable blogs={blogs || []} onRefresh={refresh} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
24
tone-page-web/hooks/admin/web/blog/use-blog-list.ts
Normal file
24
tone-page-web/hooks/admin/web/blog/use-blog-list.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
"use client"
|
||||
|
||||
import { AdminApi } from "@/lib/api";
|
||||
import { useCallback } from "react";
|
||||
import useSWR from "swr";
|
||||
|
||||
export function useBlogList() {
|
||||
const { data, error, isLoading, mutate } = useSWR(
|
||||
['/admin/web/blog'],
|
||||
() => AdminApi.web.blog.list()
|
||||
)
|
||||
|
||||
const refresh = useCallback(() => {
|
||||
return mutate()
|
||||
}, [mutate])
|
||||
|
||||
return {
|
||||
blogs: data,
|
||||
error,
|
||||
isLoading,
|
||||
mutate,
|
||||
refresh,
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import fetcher from "@/lib/api/fetcher";
|
||||
import { Blog } from "@/lib/types/blog";
|
||||
|
||||
export async function list() {
|
||||
return fetcher('/api/admin/web/blog')
|
||||
return fetcher<Blog[]>('/api/admin/web/blog')
|
||||
}
|
||||
@@ -1,12 +1,6 @@
|
||||
export type BlogPermission =
|
||||
'public' |
|
||||
'password' |
|
||||
'listed';
|
||||
|
||||
export interface Blog {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
publish_at: string;
|
||||
permissions: BlogPermission[];
|
||||
contentUrl: string;
|
||||
}
|
||||
Reference in New Issue
Block a user