添加表格视图
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
预览效果:
