tr]:last:border-b-0",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
+ return (
+
+ )
+}
+
+function TableHead({ className, ...props }: React.ComponentProps<"th">) {
+ return (
+ [role=checkbox]]:translate-y-[2px]",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function TableCell({ className, ...props }: React.ComponentProps<"td">) {
+ return (
+ | [role=checkbox]]:translate-y-[2px]",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function TableCaption({
+ className,
+ ...props
+}: React.ComponentProps<"caption">) {
+ return (
+
+ )
+}
+
+export {
+ Table,
+ TableHeader,
+ TableBody,
+ TableFooter,
+ TableHead,
+ TableRow,
+ TableCell,
+ TableCaption,
+}
diff --git a/tone-page-web/hooks/admin/user/use-user-list.ts b/tone-page-web/hooks/admin/user/use-user-list.ts
new file mode 100644
index 0000000..30ab4b7
--- /dev/null
+++ b/tone-page-web/hooks/admin/user/use-user-list.ts
@@ -0,0 +1,32 @@
+"use client"
+
+import { list, UserListParams, UserListResponse } from '@/lib/api/admin/user'
+import { ApiError } from '@/lib/api/fetcher'
+import { toast } from 'sonner'
+import useSWR from 'swr'
+
+export function useUserList(params?: UserListParams) {
+ const { data, error, isLoading, mutate } = useSWR(
+ ['/api/admin/user', params],
+ () => list(params),
+ {
+ onError: (err) => {
+ if (err instanceof ApiError) {
+ toast.error(err.message)
+ } else {
+ toast.error('请求失败')
+ }
+ }
+ }
+ )
+
+ return {
+ users: data?.items ?? [],
+ total: data?.total ?? 0,
+ page: data?.page ?? 1,
+ pageSize: data?.pageSize ?? 20,
+ isLoading,
+ error,
+ mutate,
+ }
+}
\ No newline at end of file
diff --git a/tone-page-web/lib/api/admin/index.ts b/tone-page-web/lib/api/admin/index.ts
new file mode 100644
index 0000000..e687d88
--- /dev/null
+++ b/tone-page-web/lib/api/admin/index.ts
@@ -0,0 +1 @@
+export * as user from './user/index';
\ No newline at end of file
diff --git a/tone-page-web/lib/api/admin/user/index.ts b/tone-page-web/lib/api/admin/user/index.ts
new file mode 100644
index 0000000..fd7991d
--- /dev/null
+++ b/tone-page-web/lib/api/admin/user/index.ts
@@ -0,0 +1 @@
+export * from './list';
\ No newline at end of file
diff --git a/tone-page-web/lib/api/admin/user/list.ts b/tone-page-web/lib/api/admin/user/list.ts
new file mode 100644
index 0000000..220b122
--- /dev/null
+++ b/tone-page-web/lib/api/admin/user/list.ts
@@ -0,0 +1,22 @@
+import { User } from "@/lib/types/user"
+import fetcher from "../../fetcher"
+
+export interface UserListParams {
+ page?: number
+ pageSize?: number
+}
+
+export interface UserListResponse {
+ items: User[],
+ total: number
+ page: number
+ pageSize: number
+}
+
+export function list(params?: UserListParams): Promise {
+ const searchParams = new URLSearchParams()
+ if (params?.page) searchParams.set('page', params.page.toString())
+ if (params?.pageSize) searchParams.set('pageSize', params.pageSize.toString())
+
+ return fetcher('/api/admin/user')
+}
\ No newline at end of file
diff --git a/tone-page-web/lib/api/auth/login.ts b/tone-page-web/lib/api/auth/login.ts
index af650bf..9b8d0ba 100644
--- a/tone-page-web/lib/api/auth/login.ts
+++ b/tone-page-web/lib/api/auth/login.ts
@@ -1,4 +1,4 @@
-import fetcher, { StanderResponse } from "../fetcher"
+import fetcher, { ApiError } from "../fetcher"
interface LoginParams {
type: 'password' | 'phone' | 'email';
@@ -9,39 +9,39 @@ interface LoginParams {
code?: string;
}
-export const login = async (data: LoginParams): Promise> => {
+export const login = async (data: LoginParams): Promise<{ token: string }> => {
if (data.type === 'password') {
if (!data.account || !data.password) {
- return { statusCode: 400, message: '请输入账户和密码' }
+ throw new ApiError(400, '请输入账户和密码')
}
if (data.account.length < 1 || data.account.length > 254) {
- return { statusCode: 400, message: '请输入正确的账户' }
+ throw new ApiError(400, '请输入正确的账户')
}
if (data.password.length < 6 || data.password.length > 32) {
- return { statusCode: 400, message: '请输入正确的密码' }
+ throw new ApiError(400, '请输入正确的密码')
}
} else if (data.type === 'phone') {
if (!data.phone || !data.code) {
- return { statusCode: 400, message: '请输入手机号和验证码' }
+ throw new ApiError(400, '请输入手机号和验证码')
}
if (data.phone.length !== 11) {
- return { statusCode: 400, message: '请输入正确的手机号' }
+ throw new ApiError(400, '请输入正确的手机号')
}
if (data.code.length != 6) {
- return { statusCode: 400, message: '请输入正确的验证码' }
+ throw new ApiError(400, '请输入正确的验证码')
}
} else if (data.type === 'email') {
if (!data.email || !data.code) {
- return { statusCode: 400, message: '请输入邮箱和验证码' }
+ throw new ApiError(400, '请输入邮箱和验证码')
}
if (data.email.length < 1 || data.email.length > 254) {
- return { statusCode: 400, message: '请输入正确的邮箱' }
+ throw new ApiError(400, '请输入正确的邮箱')
}
if (data.code.length != 6) {
- return { statusCode: 400, message: '请输入正确的验证码' }
+ throw new ApiError(400, '请输入正确的验证码')
}
} else {
- return { statusCode: 400, message: '登录方式异常' }
+ throw new ApiError(400, '登录方式异常')
}
return fetcher<{
diff --git a/tone-page-web/lib/api/fetcher.ts b/tone-page-web/lib/api/fetcher.ts
index 2c01d34..22d7824 100644
--- a/tone-page-web/lib/api/fetcher.ts
+++ b/tone-page-web/lib/api/fetcher.ts
@@ -4,7 +4,18 @@ export interface StanderResponse {
data?: T;
}
-const fetcher = async(url: string, options?: RequestInit): Promise> => {
+export class ApiError extends Error {
+ constructor(
+ public statusCode: number,
+ message: string,
+ public data?: unknown,
+ ) {
+ super(message);
+ this.name = 'ApiError';
+ }
+}
+
+const fetcher = async(url: string, options?: RequestInit): Promise => {
const res = await fetch(url, {
method: 'GET',
headers: {
@@ -17,11 +28,12 @@ const fetcher = async(url: string, options?: RequestInit): Promise {
- return fetcher('/api/verification/send', {
+ return fetcher('/api/verification/send', {
method: 'POST',
body: JSON.stringify(data),
})
diff --git a/tone-page-web/lib/types/user.ts b/tone-page-web/lib/types/user.ts
new file mode 100644
index 0000000..fcfca8d
--- /dev/null
+++ b/tone-page-web/lib/types/user.ts
@@ -0,0 +1,10 @@
+export interface User {
+ userId: string;
+ username: string;
+ nickname: string;
+ email?: string;
+ phone?: string;
+ avatar?: string;
+ createdAt: string;
+ updatedAt: string;
+}
\ No newline at end of file
|