Files
yingqing/core/frontend/src/components/pager.tsx
T
seaislee1209andClaude Opus 4.8 42c3c046ea feat(core): Wave 1 part2 — 双主题清污染/mono分级/富分页器/hero字重
S1 双主题: styles.css Dashboard 区(.recent-row/.shortcut/.tip)迁入 design-restraint.css,
  暖米旧 token→restraint(--orange→--heat 等);修 .queue-chip svg --ink-3→--black-alpha-56。
  实测 .shortcut .ic 橙 = rgb(250,93,25)=--heat(原泄漏成暖橙 #E55B26)
S2 mono: design-restraint.css 加 .mono-10/11/12(10.5/11/11.5px)工具类(逐页应用并入 Wave 4)
S7 分页器: pager.tsx 加可选 onPageSizeChange(条数循环)/跳页输入/sticky,旧调用方零破坏;
  CSS 加 .size-cycle/.jump/.sticky(接线 products/library + 服务端分页放 Wave 3)
S8 hero 字重: 核 V1 实测 account/team hero 数字均 font-weight:700 → account 3 处 + team 2 处
  改 700(据实还原 V1 标准答案;记录与 design.md §2.5 的内部不一致)

S6 批量栏共享挪 Wave 3(与 projects/library 重建一起做)

验证: tsc 0 · build 0 · computed-style 橙=heat/字重=700 · pixelmatch account 1.23%/team 2.13%
  · 14 路由回归 boot 0 报错 · 登录 639ms

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 04:07:34 +08:00

85 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 通用列表分页器:mono 总数 + 页码窗口 + 每页条数 + 跳页输入。
// 父组件持有 page state;纯前端切片或服务端分页都适用(onChange 回页码)。
// 新增能力全部可选 —— 旧调用方只传 page/total/pageSize/onChange 时退化为原只读分页器:
// onPageSizeChange 传则「每页 N 条」变成可点击循环按钮(默认循环 12/24/48/96)
// pageSizeOptions 自定义每页条数候选
// sticky 传则分页器吸底浮动
// total <= pageSize 且不可改每页条数时不渲染(单页无需分页器)。
import { useState } from "react";
// 页码窗口:超过 7 页折叠成 1 … cur-1 cur cur+1 … last
export function pageWindow(current: number, total: number): Array<number | "ellipsis"> {
if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1);
const items: Array<number | "ellipsis"> = [1];
const start = Math.max(2, current - 1);
const end = Math.min(total - 1, current + 1);
if (start > 2) items.push("ellipsis");
for (let p = start; p <= end; p++) items.push(p);
if (end < total - 1) items.push("ellipsis");
items.push(total);
return items;
}
export function Pager({ page, total, pageSize, onChange, onPageSizeChange, pageSizeOptions = [12, 24, 48, 96], sticky = false }: {
page: number;
total: number;
pageSize: number;
onChange: (page: number) => void;
onPageSizeChange?: (size: number) => void;
pageSizeOptions?: number[];
sticky?: boolean;
}) {
const totalPages = Math.max(1, Math.ceil(total / pageSize));
const cur = Math.min(Math.max(1, page), totalPages);
const [jump, setJump] = useState("");
// 单页且不可改每页条数 → 不渲染;可改条数时仍渲染(让用户能切回更小分页)
if (total <= pageSize && !onPageSizeChange) return null;
function cycleSize() {
if (!onPageSizeChange) return;
const idx = pageSizeOptions.indexOf(pageSize);
onPageSizeChange(pageSizeOptions[(idx + 1) % pageSizeOptions.length]);
}
function commitJump() {
const n = Number.parseInt(jump, 10);
if (Number.isFinite(n)) onChange(Math.min(Math.max(1, n), totalPages));
setJump("");
}
return (
<div className={`list-pager${sticky ? " sticky" : ""}`}>
<span className="total">// 共 {total} 条 · 第 {cur} / {totalPages} 页</span>
<div className="pages">
<button type="button" disabled={cur === 1} onClick={() => onChange(cur - 1)} aria-label="上一页">‹</button>
{pageWindow(cur, totalPages).map((p, i) => (
p === "ellipsis"
? <span key={`e${i}`} className="ellipsis">…</span>
: <button type="button" key={p} className={p === cur ? "active" : ""} onClick={() => onChange(p)}>{p}</button>
))}
<button type="button" disabled={cur === totalPages} onClick={() => onChange(cur + 1)} aria-label="下一页">›</button>
</div>
{totalPages > 7 && (
<span className="jump">
跳至
<input
type="number"
min={1}
max={totalPages}
value={jump}
placeholder={String(cur)}
onChange={(event) => setJump(event.target.value)}
onKeyDown={(event) => { if (event.key === "Enter") commitJump(); }}
onBlur={() => { if (jump) commitJump(); }}
aria-label="跳至页码"
/>
页
</span>
)}
{onPageSizeChange
? <button type="button" className="page-size size-cycle" onClick={cycleSize} title="点击切换每页条数">每页 {pageSize} 条</button>
: <span className="page-size">每页 {pageSize} 条</span>}
</div>
);
}