Logo
RavenSaaS Docs

添加表格视图

RavenSaaS 内置了表格视图组件,可以很方便的渲染一个表格视图,用于展示各类数据。

比如 用户列表 / 订单列表 / 积分记录 等。

创建新的表格视图

以下步骤演示如何快速在用户中心渲染一个表格视图,显示用户支付订单列表。

1. 数据层操作

先在 model 层操作操作数据库,实现一个获取用户订单列表的方法

models/order.ts
1export async function getOrdersByUserUuid(
2  userUuid: string
3): Promise<(typeof orders.$inferSelect)[] | undefined> {
4
5  const data = await db()
6    .select()
7    .from(orders)
8    .where(eq(orders.user_uuid, userUuid))
9    .orderBy(desc(orders.created_at));
10
11  return data;
12}

2. 创建页面

在用户中心模块创建一个新的页面,获取登录用户信息

getUserUuid getUserUuid 方法可以从 session 中获取当前登录用户的 uuid

如果用户未登录,则跳转到登录页面。

app/[locale]/(default)/(console)/my-orders/page.tsx
1import { serverGetUserUuid } from "@/lib/common";
2import { redirect } from "next/navigation";
3
4export default async function MyOrdersPage() {
5  const t = await getTranslations();
6
7  const user_uuid = await serverGetUserUuid();
8
9  const callbackUrl = `${process.env.NEXT_PUBLIC_WEB_URL}/my-orders`;
10  if (!user_uuid) {
11    redirect(`/auth/signin?callbackUrl=${encodeURIComponent(callbackUrl)}`);
12  }
13
14  return <div>My Orders With User UUID: {user_uuid}</div>;
15}

预览新页面,可以看到登录后的用户 uuid。

用户页面显示
用户页面显示

3. 定义表格视图的数据表头

在页面文件中,

定义要显示的表头列表 columns: TableColumn[]

引入表格视图组件: import TableSlot from "@/components/console/slots/table";

显示表头:return (<Suspense fallback={<TableSkeleton columns={6} />}><TableSlot {...table} /></Suspense>);

完整代码:

app/[locale]/(console)/users/my-orders/page.tsx
1import { getOrdersByUserUuid } from "@/models/order";
2import { serverGetUserUuid } from "@/lib/common";
3
4import { TableColumn } from "@/types/blocks/table";
5import TableSlot from "@/components/console/slots/table";
6import { Table as TableSlotType } from "@/types/slots/table";
7import { getTranslations } from "next-intl/server";
8import { redirect } from "next/navigation";
9import { Suspense } from "react";
10import { TableSkeleton } from "@/components/skeletons/table-skeleton";
11
12export default async function MyOrdersPage() {
13  const t = await getTranslations();
14
15  const user_uuid = await serverGetUserUuid();
16
17  const callbackUrl = `${process.env.NEXT_PUBLIC_WEB_URL}/my-orders`;
18  if (!user_uuid) {
19    redirect(`/auth/signin?callbackUrl=${encodeURIComponent(callbackUrl)}`);
20  }
21
22  const columns: TableColumn[] = [
23    { name: "order_no", title: t("my_orders.table.order_no") },
24    { name: "user_email", title: t("my_orders.table.email") },
25    { name: "product_name", title: t("my_orders.table.product_name") },
26    { name: "amount", title: t("my_orders.table.amount") },
27    { name: "status", title: t("my_orders.table.status") },
28    { name: "created_at", title: t("my_orders.table.created_at") },
29    { name: "actions", title: t("my_orders.table.actions") },
30  ];
31
32  const table: TableSlotType = {
33    title: t("my_orders.title"),
34    description: t("my_orders.description"),
35    columns: columns,
36    data: null,
37  };
38
39  return (
40    <Suspense fallback={<TableSkeleton columns={6} />}>
41      <TableSlot {...table} />
42    </Suspense>
43  );
44}

预览效果:

表格列显示
表格列显示

4. 在表格视图中显示数据

调用步骤 1 实现的读取数据的函数,把获取到的数据传递给表格视图组件进行显示。

完整代码:

app/[locale]/(console)/users/my-orders/page.tsx
1import { getOrdersByUserUuid } from "@/models/order";
2import { serverGetUserUuid } from "@/lib/common";
3
4import { TableColumn } from "@/types/blocks/table";
5import TableSlot from "@/components/console/slots/table";
6import { Table as TableSlotType } from "@/types/slots/table";
7import { getTranslations } from "next-intl/server";
8import moment from "moment";
9import { redirect } from "next/navigation";
10import { Order } from "@/types/order";
11import { Suspense } from "react";
12import { TableSkeleton } from "@/components/skeletons/table-skeleton";
13import { ContinuePaymentButton } from "@/components/payment/continue-payment-button";
14
15export default async function MyOrdersPage() {
16  const t = await getTranslations();
17
18  const user_uuid = await serverGetUserUuid();
19
20  const callbackUrl = `${process.env.NEXT_PUBLIC_WEB_URL}/my-orders`;
21  if (!user_uuid) {
22    redirect(`/auth/signin?callbackUrl=${encodeURIComponent(callbackUrl)}`);
23  }
24
25  const orders = await getOrdersByUserUuid(user_uuid);
26
27  const columns: TableColumn[] = [
28    { name: "order_no", title: t("my_orders.table.order_no") },
29    { name: "user_email", title: t("my_orders.table.email") },
30    { name: "product_name", title: t("my_orders.table.product_name") },
31    {
32      name: "amount",
33      title: t("my_orders.table.amount"),
34      callback: (item: Order) =>
35        `${item.currency.toUpperCase() === "CNY" ? "¥" : "$"} ${item.amount / 100}`,
36    },
37    {
38      name: "status",
39      title: t("my_orders.table.status"),
40      callback: (item: Order) => {
41        return item.status === "pending" ? (
42          <span className="text-yellow-500 font-bold">Pending</span>
43        ) : item.status === "paid" ? (
44          <span className="text-green-500 font-bold">Paid</span>
45        ) : item.status === "activated" ? (
46          <span className="text-green-500 font-bold">Activated</span>
47        ) : item.status === "closed" ? (
48          <span className="text-gray-400 font-bold">Closed</span>
49        ) : (
50          <span className="text-gray-400">-</span>
51        );
52      },
53    },
54    {
55      name: "created_at",
56      title: t("my_orders.table.created_at"),
57      callback: (item: Order) =>
58        moment(item.created_at || "").format("YYYY-MM-DD HH:mm:ss"),
59    },
60    {
61      name: "actions",
62      title: t("my_orders.table.actions"),
63      callback: (item: Order) => {
64        if (item.status === "paid") {
65          return (
66            <a
67              href={`/users/activate-order?orderId=${item.order_no}`}
68              className="text-blue-600 underline hover:no-underline"
69            >
70              To Activate
71            </a>
72          );
73        }
74        if (item.status === "activated") {
75           return <span className="text-gray-400">-</span>;
76        }
77        if (item.status === "pending") {
78          return (
79            <ContinuePaymentButton orderNo={item.order_no} />
80          );
81        }
82        if (item.status === "paid") {
83          return <span className="text-green-500">Paid</span>;
84        }
85        return <span className="text-gray-400">-</span>;
86      },
87    },
88  ];
89
90  const table: TableSlotType = {
91    title: t("my_orders.title"),
92    description: t("my_orders.description"),
93    toolbar: {
94      items: [
95        {
96          title: t("my_orders.read_docs"),
97          icon: "RiBookLine",
98          url: "https://docs.RavenSaaS.ai",
99          target: "_blank",
100          variant: "outline",
101        },
102        {
103          title: t("my_orders.join_discord"),
104          icon: "RiDiscordFill",
105          url: "https://discord.gg/HQNnrzjZQS",
106          target: "_blank",
107        },
108      ],
109    },
110    columns: columns,
111    data: orders || null,
112  };
113
114  return (
115    <Suspense fallback={<TableSkeleton columns={6} />}>
116      <TableSlot {...table} />
117    </Suspense>
118  );
119}
120

预览效果:

用户订单表格
用户订单表格