9 changed files with 182 additions and 15 deletions
@ -0,0 +1,95 @@ |
|||||
|
import { createUseFetch } from '#imports' |
||||
|
import type { AsyncData, UseFetchOptions } from '#app' |
||||
|
import type { KeysOf, PickFrom } from '#app/composables/asyncData' |
||||
|
import type { NitroFetchRequest, TypedInternalResponse, AvailableRouterMethod } from 'nitropack/types' |
||||
|
import type { FetchError } from 'ofetch' |
||||
|
import type { Ref } from 'vue' |
||||
|
|
||||
|
/** 与 `R.success` / `R.error` 返回结构对齐 */ |
||||
|
export type ApiResponse<T = unknown> = { |
||||
|
code: number |
||||
|
message: string |
||||
|
data: T |
||||
|
} |
||||
|
|
||||
|
/** 从 Nitro 推断的响应体上剥离一层 `ApiResponse`,得到 `data` 字段类型 */ |
||||
|
export type UnwrapApiResponse<T> = T extends ApiResponse<infer D> ? D : T |
||||
|
|
||||
|
export function unwrapApiBody<T>(payload: ApiResponse<T>): T { |
||||
|
if (payload.code !== 0) { |
||||
|
throw new Error(payload.message) |
||||
|
} |
||||
|
return payload.data |
||||
|
} |
||||
|
|
||||
|
export const request = $fetch.create({}) |
||||
|
|
||||
|
const httpFetchDefaults = { |
||||
|
retry: 0, |
||||
|
$fetch: request, |
||||
|
transform: unwrapApiBody, |
||||
|
} |
||||
|
|
||||
|
const _useHttpFetch = createUseFetch(httpFetchDefaults) |
||||
|
const _useLazyHttpFetch = createUseFetch({ |
||||
|
...httpFetchDefaults, |
||||
|
lazy: true, |
||||
|
}) |
||||
|
|
||||
|
type DefaultMethod<ReqT extends NitroFetchRequest> = 'get' extends AvailableRouterMethod<ReqT> |
||||
|
? 'get' |
||||
|
: AvailableRouterMethod<ReqT> |
||||
|
|
||||
|
type HttpFetchOptions< |
||||
|
_ResT, |
||||
|
DataT, |
||||
|
PickKeys extends KeysOf<DataT>, |
||||
|
DefaultT, |
||||
|
ReqT extends NitroFetchRequest, |
||||
|
Method extends AvailableRouterMethod<ReqT>, |
||||
|
> = Omit<UseFetchOptions<_ResT, DataT, PickKeys, DefaultT, ReqT, Method>, 'transform'> |
||||
|
|
||||
|
/** |
||||
|
* 带项目默认选项的 `useFetch`,并在类型上将 `data` 视为接口 `{ code, data, message }` 中的内层 `data`。 |
||||
|
* |
||||
|
* 说明:Nuxt的 `createUseFetch` 在类型上不会把工厂里的 `transform` 的出参当作 `AsyncData['data']`, |
||||
|
* 因此这里用薄包装修正 `DataT`(见 `UnwrapApiResponse`)。 |
||||
|
*/ |
||||
|
export function useHttpFetch< |
||||
|
ResT = void, |
||||
|
ErrorT = FetchError, |
||||
|
ReqT extends NitroFetchRequest = NitroFetchRequest, |
||||
|
Method extends AvailableRouterMethod<ReqT> = ResT extends void |
||||
|
? DefaultMethod<ReqT> |
||||
|
: AvailableRouterMethod<ReqT>, |
||||
|
_ResT = ResT extends void ? TypedInternalResponse<ReqT, unknown, Lowercase<Method>> : ResT, |
||||
|
DataT = UnwrapApiResponse<_ResT>, |
||||
|
PickKeys extends KeysOf<DataT> = KeysOf<DataT>, |
||||
|
DefaultT = undefined, |
||||
|
>( |
||||
|
url: ReqT | Ref<ReqT, ReqT> | (() => ReqT), |
||||
|
opts?: HttpFetchOptions<_ResT, DataT, PickKeys, DefaultT, ReqT, Method>, |
||||
|
): AsyncData<DefaultT | PickFrom<DataT, PickKeys>, ErrorT | undefined> { |
||||
|
return _useHttpFetch(url, opts) as AsyncData<DefaultT | PickFrom<DataT, PickKeys>, ErrorT | undefined> |
||||
|
} |
||||
|
|
||||
|
export function useLazyHttpFetch< |
||||
|
ResT = void, |
||||
|
ErrorT = FetchError, |
||||
|
ReqT extends NitroFetchRequest = NitroFetchRequest, |
||||
|
Method extends AvailableRouterMethod<ReqT> = ResT extends void |
||||
|
? DefaultMethod<ReqT> |
||||
|
: AvailableRouterMethod<ReqT>, |
||||
|
_ResT = ResT extends void ? TypedInternalResponse<ReqT, unknown, Lowercase<Method>> : ResT, |
||||
|
DataT = UnwrapApiResponse<_ResT>, |
||||
|
PickKeys extends KeysOf<DataT> = KeysOf<DataT>, |
||||
|
DefaultT = undefined, |
||||
|
>( |
||||
|
url: ReqT | Ref<ReqT, ReqT> | (() => ReqT), |
||||
|
opts?: HttpFetchOptions<_ResT, DataT, PickKeys, DefaultT, ReqT, Method>, |
||||
|
): AsyncData<DefaultT | PickFrom<DataT, PickKeys>, ErrorT | undefined> { |
||||
|
return _useLazyHttpFetch(url, opts) as AsyncData< |
||||
|
DefaultT | PickFrom<DataT, PickKeys>, |
||||
|
ErrorT | undefined |
||||
|
> |
||||
|
} |
||||
@ -1,8 +1,18 @@ |
|||||
import { integer, pgTable, varchar } from "drizzle-orm/pg-core"; |
import { sql } from "drizzle-orm"; |
||||
|
import { integer, pgTable, timestamp, varchar } from "drizzle-orm/pg-core"; |
||||
|
|
||||
export const usersTable = pgTable("users", { |
export const usersTable = pgTable("users", { |
||||
id: integer().primaryKey().generatedAlwaysAsIdentity(), |
id: integer().primaryKey(), |
||||
name: varchar().notNull(), |
email: varchar().unique(), |
||||
age: integer().notNull(), |
username: varchar().notNull().unique(), |
||||
email: varchar().notNull().unique(), |
nickname: varchar(), |
||||
|
password: varchar().notNull(), |
||||
|
avatar: varchar(), |
||||
|
tel: varchar(), |
||||
|
role: varchar().notNull().default('user').$type<('user' | 'admin')>(), |
||||
|
createdAt: timestamp('created_at').defaultNow().notNull(), |
||||
|
updatedAt: timestamp('updated_at') |
||||
|
.defaultNow() |
||||
|
.$onUpdate(() => sql`CURRENT_TIMESTAMP`) |
||||
|
.notNull(), |
||||
}); |
}); |
||||
@ -0,0 +1,24 @@ |
|||||
|
|
||||
|
export const R = { |
||||
|
success: <T>(data: T) => { |
||||
|
return { |
||||
|
code: 0, |
||||
|
message: 'success', |
||||
|
data: data, |
||||
|
} as const |
||||
|
}, |
||||
|
error: <T>(message: string, data: T) => { |
||||
|
return { |
||||
|
code: 1, |
||||
|
message: message, |
||||
|
data: null, |
||||
|
} as const |
||||
|
}, |
||||
|
throwError: <T>(code: number, message: string, data: T) => { |
||||
|
throw createError({ |
||||
|
statusCode: code, |
||||
|
statusMessage: message, |
||||
|
data: data, |
||||
|
}) as never |
||||
|
} |
||||
|
} as const |
||||
Loading…
Reference in new issue