初始化模版工程

This commit is contained in:
Cloud Bot
2026-03-20 07:33:46 +00:00
commit 23717e0ecd
386 changed files with 51675 additions and 0 deletions

59
http/http.ts Normal file
View File

@@ -0,0 +1,59 @@
import { defaultGetResult, HttpDefine, HttpInjectInfo } from './type'
export async function http<R = any>(define: HttpDefine, inject?: HttpInjectInfo): Promise<R> {
let sureDefined = define
if (inject?.onRequest) {
sureDefined = await inject.onRequest(sureDefined)
}
const url = HttpDefine.getUrl(sureDefined)
const headers = HttpDefine.getHeader(sureDefined)
const body = HttpDefine.getBody(sureDefined)
const fetchOption: RequestInit = {
method: (sureDefined.method || 'get').toUpperCase(),
headers,
credentials: sureDefined.credentials ?? 'same-origin',
}
if (sureDefined.abort) {
fetchOption.signal = sureDefined.abort.signal
}
if (sureDefined.timeout) {
const abort = sureDefined.abort || new AbortController()
setTimeout(() => {
abort.abort()
}, sureDefined.timeout)
fetchOption.signal = abort.signal
}
if (
!['get', 'head', 'options', 'GET', 'HEAD', 'OPTIONS'].includes(fetchOption.method!) &&
body != null
) {
fetchOption.body = body
}
try {
const response = await fetch(url, fetchOption)
if (!response.ok) {
const errorMessage = sureDefined.customErrorMessage || `HTTP error! status: ${response.status}`
throw new Error(errorMessage)
}
if (inject?.onResponse) {
await inject.onResponse(response, define)
}
return defaultGetResult(response)
} catch (error) {
if (inject?.onError) {
inject.onError(error as Error, define)
}
throw error
}
}

60
http/index.ts Normal file
View File

@@ -0,0 +1,60 @@
import type { HttpDefine, HttpInjectInfo } from './type'
import { http } from './http'
export class HTTPClient {
base: HttpDefine
inject: HttpInjectInfo
constructor(base: HttpDefine, inject?: HttpInjectInfo) {
this.base = base
this.inject = inject || {}
}
request<R = any>(config: HttpDefine): Promise<R> {
return http({ ...this.base, ...config }, this.inject)
}
get<R = any>(url: string, query?: any, config?: HttpDefine): Promise<R> {
return this.request({ ...config, url, method: 'get', query })
}
delete<R = any>(url: string, query?: any, config?: HttpDefine): Promise<R> {
return this.request({ ...config, url, method: 'delete', query })
}
head<R = any>(url: string, config?: HttpDefine): Promise<R> {
return this.request({ ...config, url, method: 'head' })
}
options<R = any>(url: string, config?: HttpDefine): Promise<R> {
return this.request({ ...config, url, method: 'options' })
}
post<R = any>(url: string, body?: any, config?: HttpDefine): Promise<R> {
return this.request({
...config,
url,
method: 'post',
body,
})
}
put<R = any>(url: string, body?: any, config?: HttpDefine): Promise<R> {
return this.request({
...config,
url,
method: 'put',
body,
})
}
patch<R = any>(url: string, body?: any, config?: HttpDefine): Promise<R> {
return this.request({
...config,
url,
method: 'patch',
body,
})
}
}

22
http/request.ts Normal file
View File

@@ -0,0 +1,22 @@
import { HTTPClient } from '@/http'
const CLIENT_API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || '/api'
export const request = new HTTPClient(
{
baseURL: CLIENT_API_BASE_URL,
headers: {
'Content-Type': 'application/json',
},
},
{
onRequest: async config => config,
onError: (error, config) => {
console.error('request error', {
method: config.method,
url: config.url,
error,
})
},
},
)

141
http/type.ts Normal file
View File

@@ -0,0 +1,141 @@
import { get, hasIn, isNil, isString, merge, omitBy } from 'lodash-es'
import type { IStringifyOptions } from 'qs'
import qs from 'qs'
export async function defaultGetResult(response: Response) {
if (response.headers.get('content-type')?.includes('application/json')) {
const jsonData = await response.json()
if (hasIn(jsonData, 'data')) {
return jsonData.data
}
return jsonData
}
return response
}
export class HttpDefine implements Omit<RequestInit, 'body'> {
baseURL?: string
url?: string
method?: string
abort?: AbortController
timeout?: number
credentials?: RequestCredentials
ignoreError?: boolean
noRedirect?: boolean
customErrorMessage?: string
ignoreShowLimitedModal?: boolean
headers?: Record<string, any> = {
'Content-Type': 'application/json',
}
query?: Record<string, any>
// qs 序列化参数使用
querySerializer?: IStringifyOptions
body?: Record<string, any> | FormData
static hasOrigin(url: string) {
try {
const parsedUrl = new URL(url)
return parsedUrl.hostname !== ''
} catch {
// 如果 URL 无效,返回 false
return false
}
}
static mergeUrl(baseURL: string, url: string) {
if (this.hasOrigin(url)) return url
if (!baseURL) return url
let prefix = baseURL
let last = url
if (baseURL[baseURL.length - 1] === '/') {
prefix = baseURL.substring(0, baseURL.length - 1)
}
if (url[0] === '/') {
last = url.substring(1)
}
return `${prefix}/${last}`
}
static getUrl<T extends HttpDefine>(define: T = {} as any) {
const urlObj = new URL(
this.mergeUrl(define.baseURL ?? '', define.url ?? ''),
typeof window === 'object' ? window.origin : undefined,
)
const searchStr = urlObj.searchParams.toString()
const searchObj = merge(qs.parse(searchStr), define.query)
const sureSearchStr = qs.stringify(searchObj, define.querySerializer)
const base = `${urlObj.origin}${urlObj.pathname}`
if (sureSearchStr) {
return `${base}?${sureSearchStr}`
}
return base
}
static getHeader<T extends HttpDefine>(define: T = {} as any) {
const sureHeaders = omitBy(
merge({}, define.headers ?? {}),
isNil,
) as Record<string, any>
if (
get(sureHeaders, 'Content-Type', '').includes('multipart/form-data') ||
define.body instanceof FormData
) {
delete sureHeaders['Content-Type']
} else if (!get(sureHeaders, 'Content-Type', '')) {
sureHeaders['Content-Type'] = 'application/json'
}
return sureHeaders
}
static getBody<T extends HttpDefine>(define: T = {} as any) {
if (isString(define.body)) {
return define.body
}
if (define.body instanceof FormData) {
return define.body
}
if (define.body == null) return null
if (
get(define.headers, 'Content-Type', '').includes('multipart/form-data')
) {
const formData = new FormData()
Object.entries(define.body ?? []).forEach(([key, value]) => {
formData.append(key, value)
})
return formData
}
return JSON.stringify(define.body || null)
}
}
export interface HttpInjectInfo {
onRequest?: (define: HttpDefine) => Promise<HttpDefine>
onResponse?: (response: Response, define: HttpDefine) => Promise<void>
onError?: (error: Error, define: HttpDefine) => void
getResult?: (response: Response, define: HttpDefine) => Promise<any>
}