feat: UI优化和功能完善

- 添加公共分页组件,支持上边显示、分页大小10
- 侧边栏/Header按Liner风格优化,添加动画效果
- 异常处理列表支持分页和明细弹窗
- 任务结果页面支持已匹配列表分页
- 修复Dialog弹窗背景透明问题
- 新增dropdown-menu组件
- 添加parsed_file_record模型和回填脚本
- 前端枚举中文化
This commit is contained in:
freedakgmail
2026-07-07 12:57:15 +08:00
parent 78bd27a59a
commit b93929eb1a
24 changed files with 3155 additions and 608 deletions
+138
View File
@@ -0,0 +1,138 @@
"use client";
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
export interface PaginationProps {
/** 当前页码(从 1 开始) */
page: number;
/** 每页条数 */
pageSize: number;
/** 总条数 */
total: number;
/** 页码或每页条数变化时的回调 */
onChange: (page: number, pageSize: number) => void;
/** 可选的每页条数选项,默认 [10, 20, 50] */
pageSizeOptions?: number[];
/** 是否显示每页条数选择器,默认 true */
showPageSizeSelector?: boolean;
}
export function Pagination({
page,
pageSize,
total,
onChange,
pageSizeOptions = [10, 20, 50],
showPageSizeSelector = true,
}: PaginationProps) {
const totalPages = Math.ceil(total / pageSize);
const start = total > 0 ? (page - 1) * pageSize + 1 : 0;
const end = Math.min(page * pageSize, total);
return (
<div className="flex items-center justify-between px-4 py-3 border-t">
<div className="flex items-center gap-4">
{showPageSizeSelector && (
<div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground"></span>
<Select
value={String(pageSize)}
onValueChange={(val) => onChange(1, Number(val))}
>
<SelectTrigger className="h-8 w-[70px]">
<SelectValue />
</SelectTrigger>
<SelectContent side="top">
{pageSizeOptions.map((size) => (
<SelectItem key={size} value={String(size)}>
{size}
</SelectItem>
))}
</SelectContent>
</Select>
<span className="text-sm text-muted-foreground"></span>
</div>
)}
{total > 0 && (
<span className="text-sm text-muted-foreground">
{start}-{end} {total}
</span>
)}
</div>
<div className="flex items-center gap-1">
<Button
variant="outline"
size="icon"
className="h-8 w-8"
onClick={() => onChange(1, pageSize)}
disabled={page <= 1}
>
<ChevronsLeft className="h-4 w-4" />
</Button>
<Button
variant="outline"
size="icon"
className="h-8 w-8"
onClick={() => onChange(page - 1, pageSize)}
disabled={page <= 1}
>
<ChevronLeft className="h-4 w-4" />
</Button>
<div className="flex items-center gap-1 mx-1">
{Array.from({ length: Math.min(totalPages, 5) }, (_, i) => {
let pageNum: number;
if (totalPages <= 5) {
pageNum = i + 1;
} else if (page <= 3) {
pageNum = i + 1;
} else if (page >= totalPages - 2) {
pageNum = totalPages - 4 + i;
} else {
pageNum = page - 2 + i;
}
return (
<Button
key={pageNum}
variant={pageNum === page ? "default" : "outline"}
size="sm"
className="h-8 w-9"
onClick={() => onChange(pageNum, pageSize)}
>
{pageNum}
</Button>
);
})}
</div>
<Button
variant="outline"
size="icon"
className="h-8 w-8"
onClick={() => onChange(page + 1, pageSize)}
disabled={page >= totalPages}
>
<ChevronRight className="h-4 w-4" />
</Button>
<Button
variant="outline"
size="icon"
className="h-8 w-8"
onClick={() => onChange(totalPages, pageSize)}
disabled={page >= totalPages}
>
<ChevronsRight className="h-4 w-4" />
</Button>
</div>
</div>
);
}