114 lines
3.3 KiB
TypeScript
114 lines
3.3 KiB
TypeScript
import { Button, LinkButton } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { buildPageHref } from "@/modules/settings/lib/pagination";
|
|
|
|
type TableToolbarProps = {
|
|
actionLabel?: string;
|
|
pathname: string;
|
|
query: string;
|
|
searchPlaceholder: string;
|
|
total: number;
|
|
};
|
|
|
|
export function TableToolbar({
|
|
actionLabel,
|
|
pathname,
|
|
query,
|
|
searchPlaceholder,
|
|
total,
|
|
}: TableToolbarProps): React.ReactElement {
|
|
return (
|
|
<div className="flex flex-col gap-3 border-b p-4 lg:flex-row lg:items-center lg:justify-between">
|
|
<form className="flex w-full max-w-xl gap-2" action={pathname}>
|
|
<Input name="q" placeholder={searchPlaceholder} defaultValue={query} />
|
|
<Button type="submit" variant="outline">
|
|
搜索
|
|
</Button>
|
|
</form>
|
|
<span className="text-sm text-muted-foreground">{actionLabel ?? `共 ${total} 条`}</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
type TablePaginationProps = {
|
|
page: number;
|
|
pageCount: number;
|
|
pathname: string;
|
|
query: string;
|
|
total: number;
|
|
};
|
|
|
|
export function TablePagination({
|
|
page,
|
|
pageCount,
|
|
pathname,
|
|
query,
|
|
total,
|
|
}: TablePaginationProps): React.ReactElement {
|
|
const safePage = Math.min(page, pageCount);
|
|
const isFirstPage = safePage <= 1;
|
|
const isLastPage = safePage >= pageCount;
|
|
|
|
return (
|
|
<div className="flex flex-col gap-3 border-t p-4 text-sm text-muted-foreground lg:flex-row lg:items-center lg:justify-between">
|
|
<span>共 {total} 条</span>
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
{isFirstPage ? (
|
|
<Button disabled size="sm" variant="outline">
|
|
首页
|
|
</Button>
|
|
) : (
|
|
<LinkButton href={buildPageHref(pathname, query, 1)} size="sm" variant="outline">
|
|
首页
|
|
</LinkButton>
|
|
)}
|
|
{isFirstPage ? (
|
|
<Button disabled size="sm" variant="outline">
|
|
上一页
|
|
</Button>
|
|
) : (
|
|
<LinkButton href={buildPageHref(pathname, query, safePage - 1)} size="sm" variant="outline">
|
|
上一页
|
|
</LinkButton>
|
|
)}
|
|
<span className="min-w-14 text-center text-foreground">
|
|
{safePage}/{pageCount}
|
|
</span>
|
|
<form action={pathname} className="flex items-center gap-2">
|
|
{query.trim() ? <input name="q" type="hidden" value={query.trim()} /> : null}
|
|
<Input
|
|
aria-label="跳转页码"
|
|
className="h-9 w-20"
|
|
defaultValue={safePage}
|
|
max={pageCount}
|
|
min={1}
|
|
name="page"
|
|
type="number"
|
|
/>
|
|
<Button size="sm" type="submit" variant="outline">
|
|
跳转
|
|
</Button>
|
|
</form>
|
|
{isLastPage ? (
|
|
<Button disabled size="sm" variant="outline">
|
|
下一页
|
|
</Button>
|
|
) : (
|
|
<LinkButton href={buildPageHref(pathname, query, safePage + 1)} size="sm" variant="outline">
|
|
下一页
|
|
</LinkButton>
|
|
)}
|
|
{isLastPage ? (
|
|
<Button disabled size="sm" variant="outline">
|
|
末页
|
|
</Button>
|
|
) : (
|
|
<LinkButton href={buildPageHref(pathname, query, pageCount)} size="sm" variant="outline">
|
|
末页
|
|
</LinkButton>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|