mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-24 12:44:18 +08:00
feat(usage): improve pagination with first/last 3 pages and page jump input
Show first 3 and last 3 page buttons instead of just first/last, with Set-based deduplication for clean edge merging. Add a page number input field with Go button for direct page navigation.
This commit is contained in:
@@ -57,6 +57,7 @@ export function RequestLogTable({
|
||||
const [appliedFilters, setAppliedFilters] = useState<LogFilters>({});
|
||||
const [draftFilters, setDraftFilters] = useState<LogFilters>({});
|
||||
const [page, setPage] = useState(0);
|
||||
const [pageInput, setPageInput] = useState("");
|
||||
const pageSize = 20;
|
||||
const [validationError, setValidationError] = useState<string | null>(null);
|
||||
|
||||
@@ -131,6 +132,15 @@ export function RequestLogTable({
|
||||
setPage(0);
|
||||
};
|
||||
|
||||
const handleGoToPage = () => {
|
||||
const trimmed = pageInput.trim();
|
||||
if (!/^\d+$/.test(trimmed)) return;
|
||||
const parsed = Number(trimmed);
|
||||
if (parsed < 1 || parsed > totalPages) return;
|
||||
setPage(parsed - 1);
|
||||
setPageInput("");
|
||||
};
|
||||
|
||||
const handleRefresh = () => {
|
||||
const key = {
|
||||
timeMode: appliedTimeMode,
|
||||
@@ -561,30 +571,33 @@ export function RequestLogTable({
|
||||
>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
{/* 页码按钮 */}
|
||||
{(() => {
|
||||
const pages: (number | string)[] = [];
|
||||
if (totalPages <= 7) {
|
||||
// 3 head + 3 tail + 3 neighborhood = 9 max distinct pages
|
||||
if (totalPages <= 9) {
|
||||
for (let i = 0; i < totalPages; i++) pages.push(i);
|
||||
} else {
|
||||
pages.push(0);
|
||||
if (page > 2) pages.push("...");
|
||||
const pageSet = new Set<number>();
|
||||
for (let i = 0; i < 3; i++) pageSet.add(i);
|
||||
for (let i = totalPages - 3; i < totalPages; i++)
|
||||
pageSet.add(i);
|
||||
for (
|
||||
let i = Math.max(1, page - 1);
|
||||
i <= Math.min(totalPages - 2, page + 1);
|
||||
let i = Math.max(0, page - 1);
|
||||
i <= Math.min(totalPages - 1, page + 1);
|
||||
i++
|
||||
) {
|
||||
pages.push(i);
|
||||
)
|
||||
pageSet.add(i);
|
||||
const sorted = Array.from(pageSet).sort((a, b) => a - b);
|
||||
for (let i = 0; i < sorted.length; i++) {
|
||||
if (i > 0 && sorted[i] - sorted[i - 1] > 1) {
|
||||
pages.push(`ellipsis-${i}`);
|
||||
}
|
||||
pages.push(sorted[i]);
|
||||
}
|
||||
if (page < totalPages - 3) pages.push("...");
|
||||
pages.push(totalPages - 1);
|
||||
}
|
||||
return pages.map((p, idx) =>
|
||||
return pages.map((p) =>
|
||||
typeof p === "string" ? (
|
||||
<span
|
||||
key={`ellipsis-${idx}`}
|
||||
className="px-2 text-muted-foreground"
|
||||
>
|
||||
<span key={p} className="px-2 text-muted-foreground">
|
||||
...
|
||||
</span>
|
||||
) : (
|
||||
@@ -608,6 +621,21 @@ export function RequestLogTable({
|
||||
>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
<div className="flex items-center gap-1 ml-2">
|
||||
<Input
|
||||
type="text"
|
||||
value={pageInput}
|
||||
onChange={(e) => setPageInput(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") handleGoToPage();
|
||||
}}
|
||||
placeholder={t("usage.pageInputPlaceholder")}
|
||||
className="h-8 w-16 text-center text-xs"
|
||||
/>
|
||||
<Button variant="outline" size="sm" onClick={handleGoToPage}>
|
||||
{t("usage.goToPage")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1107,6 +1107,8 @@
|
||||
"failed": "Session sync failed"
|
||||
},
|
||||
"totalRecords": "{{total}} records total",
|
||||
"goToPage": "Go",
|
||||
"pageInputPlaceholder": "Page",
|
||||
"modelPricing": "Model Pricing",
|
||||
"loadPricingError": "Failed to load pricing data",
|
||||
"modelPricingDesc": "Configure token costs for each model",
|
||||
|
||||
@@ -1107,6 +1107,8 @@
|
||||
"failed": "セッション同期に失敗しました"
|
||||
},
|
||||
"totalRecords": "全 {{total}} 件",
|
||||
"goToPage": "移動",
|
||||
"pageInputPlaceholder": "ページ",
|
||||
"modelPricing": "モデル料金",
|
||||
"loadPricingError": "料金データの読み込みに失敗しました",
|
||||
"modelPricingDesc": "各モデルのトークンコストを設定",
|
||||
|
||||
@@ -1108,6 +1108,8 @@
|
||||
"failed": "会话同步失败"
|
||||
},
|
||||
"totalRecords": "共 {{total}} 条记录",
|
||||
"goToPage": "跳转",
|
||||
"pageInputPlaceholder": "页码",
|
||||
"modelPricing": "模型定价",
|
||||
"loadPricingError": "加载定价数据失败",
|
||||
"modelPricingDesc": "配置各模型的 Token 成本",
|
||||
|
||||
Reference in New Issue
Block a user