域名 AXUM.RS 将于2025年10月到期。我们无意再对其进行续费,我们希望你能够接续这个域名,让更多 AXUM 开发者继续受益。
  • 方案1️⃣AXUM.RS 域名 = 3000
  • 方案2️⃣方案1️⃣ + 本站所有专题原始 Markdown 文档 = 5000
  • 方案3️⃣方案2️⃣ + 本站原始数据库 = 5500
如果你有意接续这份 AXUM 情怀,请与我们取得联系。
说明:
  1. 如果有人购买 AXUM.RS 域名(方案1️⃣),或者该域名到期,本站将启用新的免费域名继续提供服务。
  2. 如果有人购买了 AXUM.RS 域名,且同时购买了内容和/或数据库(方案2️⃣/方案3️⃣),本站将关闭。届时我们或许会以另一种方式与你再相遇。

用户积分变动记录

本章将实现用户查看本账号积分变动功能。本章包含后端和前端功能。

// 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
}
  • 根据用户ID列出所有积分变动记录
  • 获取当前登录用户
  • 从数据库中查找该用户积分变动记录

前端

// 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>
    </>
  );
}
要查看完整内容,请先登录