// src/db/pointer_log.rs
pub async fn list_all<'a>(c: impl PgExecutor<'a>, user_id: &'a str) -> Result<Vec<Model>> {
let mut q = QueryBuilder::new(
r#"SELECT id, user_id, dateline, pointer_amount, before_pointer, after_pointer, "note" FROM pointer_logs WHERE user_id="#,
);
q.push_bind(user_id)
.push(" ORDER BY id DESC")
.push(" LIMIT 50");
q.build_query_as().fetch_all(c).await
}
// src/handler/api/user.rs
pub async fn pointer_log(
State(state): State<ArcAppState>,
auth: mid::Auth,
) -> Result<resp::JsonResp<Vec<model::pointer_log::PointerLog>>> {
let user = auth.get_user()?;
let data = db::pointer_log::list_all(&*state.pool, &user.id).await?;
Ok(resp::Resp::success(data).to_json())
}
前端
// src/pages/user/Pointer.tsx
import UserPageTitle from "@/components/UserPageTitle";
import { Component as PointerIcon } from "lucide-react";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { useEffect, useState } from "react";
import { $get, $ifApiErrorMsg, $isApiUnauthorizedError } from "@/lib/$fetch";
import use$status from "@/hooks/use$status";
import { useNavigate } from "react-router-dom";
import dayjs from "dayjs";
export default function UserPointerPage() {
const [list, setList] = useState<PointerLog[]>([]);
const { $setLoading, $setToast } = use$status();
const navigate = useNavigate();
const loadData = async () => {
try {
$setLoading(true);
const res = await $get<PointerLog[]>("/user/pointer");
if (res) {
setList(res);
}
} catch (e) {
if ($isApiUnauthorizedError(e)) {
$setToast(e.message);
return navigate("/login");
}
$setToast($ifApiErrorMsg(e));
} finally {
$setLoading(false);
}
};
useEffect(() => {
loadData();
}, []);
return (
<>
<div className="p-3 space-y-6">
<section className="flex justify-between items-center gap-x-2 ">
<UserPageTitle icon={<PointerIcon className="w-6" />}>
积分记录
</UserPageTitle>
</section>
<section className="my-6 bg-white rounded-md p-3">
<Table>
<TableHeader>
<TableRow>
<TableHead>#</TableHead>
<TableHead>类型</TableHead>
<TableHead>变动</TableHead>
<TableHead>可用积分</TableHead>
<TableHead>详情</TableHead>
<TableHead>时间</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{list.map((i) => (
<TableRow key={i.id}>
<TableCell>
<div className="text-xs font-mono">{i.id}</div>
</TableCell>
<TableCell>
{i.pointer_amount > 0 ? "收入" : "消费"}
</TableCell>
<TableCell>
{i.pointer_amount > 0 ? (
<div className="text-green-600">+{i.pointer_amount}</div>
) : (
<div className="text-red-600">{i.pointer_amount}</div>
)}
</TableCell>
<TableCell>{i.after_pointer}</TableCell>
<TableCell>{i.note}</TableCell>
<TableCell>
{dayjs(i.dateline).format("YYYY-MM-DD HH:mm:ss")}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</section>
</div>
</>
);
}
loadData()
:从 API 接口中获取数据
useEffect()
:组件加载后,调用 loadData()