"use client"; import { useCallback, useEffect, useState } from "react"; import Link from "next/link"; import { ChevronRight } from "lucide-react"; import { apiGetPoints, apiGetPointsLedger, type PointLedgerItem, } from "@/lib/api"; import { pointReasonLabel } from "@/lib/points"; import { formatRelative } from "@/lib/format"; import Pagination from "@/components/Pagination"; export default function PointsClient() { const [balance, setBalance] = useState(null); const [items, setItems] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [loading, setLoading] = useState(true); const size = 20; const load = useCallback(async (p: number) => { setLoading(true); try { const [bal, led] = await Promise.all([ apiGetPoints(), apiGetPointsLedger(p, size), ]); setBalance(typeof bal.points === "number" ? bal.points : 0); setItems(led.items || []); setTotal(led.total || 0); setPage(led.page || p); } finally { setLoading(false); } }, []); useEffect(() => { void load(1); }, [load]); return (

可用余额

{balance === null ? "—" : balance.toLocaleString("zh-CN")}

签到、悬赏采纳与附件收益会增加余额;解锁正文、悬赏托管与积分附件会扣减。

积分流水

{total} 条
{loading && items.length === 0 ? (

加载中…

) : items.length === 0 ? (

暂无流水,去首页签到赚第一笔积分吧

) : (
    {items.map((it) => (
  • {pointReasonLabel(it.reason)}

    {it.note && (

    {it.note}

    )}

    {formatRelative(it.created_at)}

    = 0 ? "var(--accent)" : "var(--danger)" }} > {it.delta >= 0 ? `+${it.delta}` : it.delta}

    余 {it.balance}

  • ))}
)} {total > size && (
void load(p)} />
)}
); }