feat(project): init

This commit is contained in:
2026-02-03 18:05:47 +08:00
commit f37a119eff
188 changed files with 29246 additions and 0 deletions

295
api/fetch.ts Normal file
View File

@@ -0,0 +1,295 @@
/*
Fetch 接口
fetch() 包含了 fetch() 方法,用于获取资源。
Headers 表示响应/请求的标头信息,允许你查询它们,或者针对不同的结果做不同的操作。
Request 相当于一个资源请求。
Response 相当于请求的响应
fetch 参数:
method: 请求使用的方法,如 GET、POST。
headers: 请求的头信息,形式为 Headers 的对象或包含 ByteString 值的对象字面量
body: 请求的 body 信息:可能是一个 Blob、BufferSource(x禁止)、FormData、URLSearchParams 或者 USVString 对象。注意 GET 或 HEAD 方法的请求不能包含 body 信息。
mode: 请求的模式,如 cors、no-cors 或者 same-origin。
credentials: 请求的 credentials如 omit、same-origin 或者 include。为了在当前域名内自动发送 cookie必须提供这个选项从 Chrome 50 开始,这个属性也可以接受 FederatedCredential (en-US) 实例或是一个 PasswordCredential (en-US) 实例。
*/
"use client"
import { useUserStore } from "@/app/store/user"
import { deleteCookie, refreshKey } from "@/components/login/api"
import { isNumber } from "@/libs/util"
interface OptionsProps {
url: string
method: string
body?: BodyInit
data?: any
needCredentials?: boolean
isUpload?: boolean
isDownload?: boolean
isFormData?: boolean
isLogin?: boolean
isRefresh?: boolean
isDeleteCookie?: boolean
notJson?: boolean
headerAttr?: {
[key: string]: string
}
}
async function httpfetch<T>(options: OptionsProps): Promise<T> {
const { removeUserInfo, refreshToken } = useUserStore.getState()
const access_token = useUserStore.getState().access_token
const refresh_token = useUserStore.getState().refresh_token
let Message = {
error: (params: { content: string }) => {
console.log(params.content)
},
}
const createSsrRequest: (options: OptionsProps, token?: string) => Request = (
options
) => {
// 设置请求头
let myHeaders = new Headers()
if (!options.isUpload && !options.isFormData && !options.notJson) {
myHeaders.append("Content-Type", "application/json")
}
if (options.isDownload) {
myHeaders.append("Response-Type", "blob")
}
if (options.isFormData) {
options.body &&
(options.body as FormData)?.append &&
(options.body as FormData).append("options", JSON.stringify(options))
}
// 配置常用设置 method 请求方式 headers 请求头
let myInit: RequestInit = {
method: "POST",
headers: myHeaders,
mode: "cors",
cache: "default",
body: options.isFormData
? options.body
: (JSON.stringify({ data: options.body, ...options }) as BodyInit),
}
const pathList = options.url.split("/")
const request = new Request(
`/api/${options.isFormData ? "transferFormData" : "transfer"}/${pathList[pathList.length - 1]}`,
myInit
)
return request
}
const createCsrRequest: (options: OptionsProps, token?: string) => Request = (
options,
token = ""
) => {
let request = new Request(options.url)
// 设置请求头
let myHeaders = new Headers()
myHeaders.append("Authorization", `bearer ${token}`)
if (!options.isUpload && !options.isFormData && !options.notJson) {
myHeaders.append("Content-Type", "application/json")
}
if (options.isDownload) {
myHeaders.append("Response-Type", "blob")
}
if (options.headerAttr) {
Object.entries(options.headerAttr).map(([key, value]) => {
myHeaders.append(key, value)
})
}
// myHeaders.append("X-Machine-Code", "machine_code0")
// 配置常用设置 method 请求方式 headers 请求头
let myInit: RequestInit = {
method: options.method,
headers: myHeaders,
mode: "cors",
cache: "default",
credentials: options.needCredentials ? "include" : undefined,
body: options.body as BodyInit,
}
let publicUrl = options.url.includes("http")
? ""
: process.env.NEXT_PUBLIC_URL
if (options.method === "GET") {
let searchParams = new URLSearchParams()
Object.entries(options.data || {})?.map((m) => {
searchParams.append(m[0], m[1] as string)
})
let concatenatedURL = searchParams.toString()
? options.url + "?" + searchParams.toString()
: options.url
request = new Request(publicUrl + concatenatedURL, myInit)
} else {
request = new Request(publicUrl + options.url, myInit)
}
return request
}
const createNewRequest =
process.env.NEXT_PUBLIC_OUTPUT_TYPE === "standalone"
? createSsrRequest
: createCsrRequest
const request = createNewRequest(options, access_token)
return new Promise((resolved, rejected) => {
fetch(request)
.then(async (response) => {
if (response.status === 200) {
if (options.isDownload) {
return response.blob()
} else {
return response.headers
.get("Content-Type")
?.includes("application/json")
? response.json()
: response.text()
}
} else {
const { status } = response
if (status === 400) {
if (
response.headers.get("Content-Type")?.includes("application/json")
) {
const data = await response.json()
const { message: errInfo } = data
Message.error({ content: errInfo || "" })
throw new Error(errInfo)
} else {
const errInfo = await response.text()
Message.error({ content: errInfo || "" })
throw new Error(errInfo)
}
} else if (status === 403) {
if (
response.headers.get("Content-Type")?.includes("application/json")
) {
const data = await response.json()
const { message: errInfo } = data
Message.error({ content: errInfo || "" })
location.href = `${process.env.NEXT_PUBLIC_HOME_URL}`
throw new Error(errInfo)
} else {
const errInfo = await response.text()
Message.error({ content: errInfo || "" })
location.href = `${process.env.NEXT_PUBLIC_HOME_URL}`
throw new Error(errInfo)
}
} else if (status === 500) {
if (
response.headers.get("Content-Type")?.includes("application/json")
) {
const data = await response.json()
const { message: errInfo } = data
Message.error({ content: errInfo || "" })
throw new Error(errInfo)
} else {
const errInfo = await response.text()
Message.error({ content: errInfo || "" })
throw new Error(errInfo)
}
} else if (status === 406) {
const data = await response.json()
const { message: errInfo } = data
Message.error({ content: errInfo || "" })
throw new Error(errInfo)
} else if (status >= 510) {
Message.error({ content: "申请资源不足!" })
throw new Error("申请资源不足!")
} else {
Message.error({ content: "网络服务有误!" })
throw new Error("Something went wrong on API server!")
}
}
})
.then(async (response) => {
const { code, message: errInfo } = response
if (response) {
if (isNumber(code)) {
let status = code
// 日后做相关处理
if (status === 401) {
try {
const refreshResponse = await refreshKey({
token: refresh_token,
})
if (refreshResponse.code === 200) {
let access_token = refreshResponse.message?.access || ""
let refresh_token = refreshResponse.message?.refresh || ""
refreshToken({
refresh_token,
access_token,
})
const retry_request = createNewRequest(options, access_token)
const retry_response = await fetch(retry_request)
return options.isDownload
? retry_response.blob()
: retry_response.json()
} else {
const { code } = refreshResponse
if (code === 406) {
try {
await deleteCookie()
} catch (error) {
rejected(error)
} finally {
// 清空身份信息并跳转至登录页面
removeUserInfo()
window.location.href = "/login"
}
} else {
throw new Error("重新获取token失败")
}
}
} catch (error) {
rejected(error)
}
} else if (status === 400) {
const { message: errInfo } = response
Message.error({ content: errInfo || "" })
throw new Error(errInfo)
} else if (status === 403) {
const { message: errInfo } = response
Message.error({ content: errInfo || "" })
// location.href = `${process.env.NEXT_PUBLIC_HOME_URL}`
throw new Error(errInfo)
} else if (status === 500) {
const { message: errInfo } = response
Message.error({ content: errInfo || "" })
throw new Error(errInfo)
} else if (status === 406) {
const { message: errInfo } = response
Message.error({ content: errInfo || "" })
throw new Error(errInfo)
} else if (status >= 510) {
Message.error({ content: "申请资源不足!" })
throw new Error("申请资源不足!")
} else if (status === 200) {
resolved(response)
} else {
Message.error({ content: "网络服务有误!" })
throw new Error("Something went wrong on API server!")
}
}
resolved(response)
} else {
throw new Error(errInfo)
}
})
.catch((error) => {
rejected(error)
})
})
}
export default httpfetch