140 lines
3.7 KiB
TypeScript
140 lines
3.7 KiB
TypeScript
import Taro from '@tarojs/taro'
|
|
import { getToken, redirectToLogin } from './auth'
|
|
import type { ResponseData } from '@/api/types'
|
|
|
|
type RequestMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'
|
|
|
|
interface RequestOptions {
|
|
method: RequestMethod
|
|
url: string
|
|
data?: unknown
|
|
params?: object
|
|
showLoading?: boolean
|
|
}
|
|
|
|
interface UploadOptions {
|
|
url: string
|
|
filePath: string
|
|
fileName?: string
|
|
name?: string
|
|
formData?: Record<string, string | number | boolean>
|
|
showLoading?: boolean
|
|
}
|
|
|
|
const baseURL = process.env.TARO_APP_API_BASE || 'http://localhost:3000'
|
|
|
|
export async function request<T>(options: RequestOptions) {
|
|
if (options.showLoading !== false) {
|
|
Taro.showLoading({ title: '加载中', mask: true })
|
|
}
|
|
try {
|
|
return await sendRequest<T>(options)
|
|
} finally {
|
|
if (options.showLoading !== false) {
|
|
Taro.hideLoading()
|
|
}
|
|
}
|
|
}
|
|
|
|
async function sendRequest<T>(options: RequestOptions) {
|
|
const response = await Taro.request<ResponseData<T>>({
|
|
url: buildUrl(options.url, options.params),
|
|
method: options.method,
|
|
data: options.data,
|
|
header: buildHeaders()
|
|
})
|
|
return handleResponse(response.data)
|
|
}
|
|
|
|
export async function uploadFile<T>(options: UploadOptions) {
|
|
if (options.showLoading !== false) {
|
|
Taro.showLoading({ title: '上传中', mask: true })
|
|
}
|
|
try {
|
|
return await sendUpload<T>(options)
|
|
} finally {
|
|
if (options.showLoading !== false) {
|
|
Taro.hideLoading()
|
|
}
|
|
}
|
|
}
|
|
|
|
async function sendUpload<T>(options: UploadOptions) {
|
|
const response = await Taro.uploadFile({
|
|
url: buildUrl(options.url),
|
|
filePath: options.filePath,
|
|
name: options.name || 'file',
|
|
fileName: options.fileName,
|
|
formData: options.formData,
|
|
header: buildUploadHeaders()
|
|
})
|
|
validateUploadStatus(response.statusCode, response.data)
|
|
return handleResponse(parseUploadData<T>(response.data))
|
|
}
|
|
|
|
function validateUploadStatus(statusCode: number, data: string) {
|
|
if (statusCode >= 200 && statusCode < 300) return
|
|
const message = getUploadErrorMessage(data) || `上传失败(${statusCode})`
|
|
throw new Error(message)
|
|
}
|
|
|
|
function buildUrl(url: string, params?: object) {
|
|
const target = `${baseURL}${url}`
|
|
const query = new URLSearchParams()
|
|
Object.entries(params || {}).forEach(([key, value]) => appendQuery(query, key, value))
|
|
const queryString = query.toString()
|
|
return queryString ? `${target}?${queryString}` : target
|
|
}
|
|
|
|
function appendQuery(query: URLSearchParams, key: string, value: unknown) {
|
|
if (value === undefined || value === null || value === '') return
|
|
query.append(key, String(value))
|
|
}
|
|
|
|
function buildHeaders() {
|
|
return { ...buildUploadHeaders(), 'Content-Type': 'application/json' }
|
|
}
|
|
|
|
function buildUploadHeaders() {
|
|
const token = getToken()
|
|
const headers: Record<string, string> = {
|
|
Accept: 'application/json, text/plain, */*'
|
|
}
|
|
if (token) {
|
|
headers.Authorization = `Bearer ${token}`
|
|
}
|
|
return headers
|
|
}
|
|
|
|
function parseUploadData<T>(data: string | object) {
|
|
if (typeof data !== 'string') return data as ResponseData<T>
|
|
try {
|
|
return JSON.parse(data) as ResponseData<T>
|
|
} catch {
|
|
throw new Error(getUploadErrorMessage(data) || '服务器返回数据结构有误')
|
|
}
|
|
}
|
|
|
|
function getUploadErrorMessage(data: string) {
|
|
try {
|
|
return (JSON.parse(data) as { msg?: string }).msg || ''
|
|
} catch {
|
|
return data.slice(0, 60)
|
|
}
|
|
}
|
|
|
|
function handleResponse<T>(data: ResponseData<T>) {
|
|
if (!data || data.code === undefined) {
|
|
throw new Error('服务器返回数据结构有误')
|
|
}
|
|
if (data.code === 106) {
|
|
redirectToLogin()
|
|
throw new Error(data.msg || '登录状态已过期')
|
|
}
|
|
if (data.code !== 0) {
|
|
Taro.showToast({ title: data.msg || '请求失败', icon: 'none' })
|
|
throw new Error(data.msg || '请求失败')
|
|
}
|
|
return data
|
|
}
|