|
|
@@ -1,4 +1,6 @@
|
|
|
import Taro from '@tarojs/taro'
|
|
|
+import { hc } from 'hono/client'
|
|
|
+import type { AuthRoutes, UserRoutes, RoleRoutes, FileRoutes } from '@/server/api'
|
|
|
|
|
|
// API配置
|
|
|
const API_BASE_URL = process.env.TARO_APP_API_BASE_URL || 'http://localhost:3000'
|
|
|
@@ -7,8 +9,22 @@ const API_VERSION = process.env.TARO_APP_API_VERSION || 'v1'
|
|
|
// 完整的API地址
|
|
|
const BASE_URL = `${API_BASE_URL}/api/${API_VERSION}`
|
|
|
|
|
|
-// 请求拦截器
|
|
|
-const requestInterceptor = (options: Taro.request.Option) => {
|
|
|
+// 创建自定义fetch函数,适配Taro.request
|
|
|
+const taroFetch: typeof fetch = async (input, init) => {
|
|
|
+ const url = typeof input === 'string' ? input : input.url
|
|
|
+ const method = init?.method || 'GET'
|
|
|
+
|
|
|
+ // 构建Taro请求选项
|
|
|
+ const options: Taro.request.Option = {
|
|
|
+ url,
|
|
|
+ method: method as any,
|
|
|
+ data: init?.body,
|
|
|
+ header: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ ...Object.fromEntries(new Headers(init?.headers || {}))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// 添加token
|
|
|
const token = Taro.getStorageSync('token')
|
|
|
if (token) {
|
|
|
@@ -17,133 +33,68 @@ const requestInterceptor = (options: Taro.request.Option) => {
|
|
|
'Authorization': `Bearer ${token}`
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- // 设置基础URL
|
|
|
- options.url = `${BASE_URL}${options.url}`
|
|
|
-
|
|
|
- // 设置默认header
|
|
|
- options.header = {
|
|
|
- 'Content-Type': 'application/json',
|
|
|
- ...options.header
|
|
|
- }
|
|
|
-
|
|
|
- return options
|
|
|
-}
|
|
|
-
|
|
|
-// 响应拦截器
|
|
|
-const responseInterceptor = (response: Taro.request.SuccessCallbackResult<any>) => {
|
|
|
- const { statusCode, data } = response
|
|
|
-
|
|
|
- if (statusCode === 200 || statusCode === 201) {
|
|
|
- // 检查数据结构,支持后端返回的格式
|
|
|
- if (data && data.data) {
|
|
|
- return data.data
|
|
|
- }
|
|
|
- return data
|
|
|
- } else if (statusCode === 401) {
|
|
|
- // 未授权,清除token并跳转到登录页
|
|
|
- Taro.removeStorageSync('token')
|
|
|
- Taro.removeStorageSync('userInfo')
|
|
|
- Taro.navigateTo({ url: '/pages/login/index' })
|
|
|
- throw new Error('请重新登录')
|
|
|
- } else {
|
|
|
- throw new Error(data.message || '请求失败')
|
|
|
- }
|
|
|
-}
|
|
|
|
|
|
-// 错误处理
|
|
|
-const errorHandler = (error: any) => {
|
|
|
- console.error('API Error:', error)
|
|
|
- Taro.showToast({
|
|
|
- title: error.message || '网络错误',
|
|
|
- icon: 'none'
|
|
|
- })
|
|
|
- throw error
|
|
|
-}
|
|
|
-
|
|
|
-// 封装请求方法
|
|
|
-class ApiClient {
|
|
|
- async request(options: Taro.request.Option) {
|
|
|
- try {
|
|
|
- const finalOptions = requestInterceptor(options)
|
|
|
- const response = await Taro.request(finalOptions)
|
|
|
- return responseInterceptor(response)
|
|
|
- } catch (error) {
|
|
|
- errorHandler(error)
|
|
|
+ try {
|
|
|
+ const response = await Taro.request(options)
|
|
|
+
|
|
|
+ // 处理401未授权
|
|
|
+ if (response.statusCode === 401) {
|
|
|
+ Taro.removeStorageSync('token')
|
|
|
+ Taro.removeStorageSync('userInfo')
|
|
|
+ Taro.navigateTo({ url: '/pages/login/index' })
|
|
|
+ throw new Error('请重新登录')
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- async get(url: string, params?: any) {
|
|
|
- return this.request({
|
|
|
- url,
|
|
|
- method: 'GET',
|
|
|
- data: params
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- async post(url: string, data?: any) {
|
|
|
- return this.request({
|
|
|
- url,
|
|
|
- method: 'POST',
|
|
|
- data
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- async put(url: string, data?: any) {
|
|
|
- return this.request({
|
|
|
- url,
|
|
|
- method: 'PUT',
|
|
|
- data
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- async delete(url: string) {
|
|
|
- return this.request({
|
|
|
- url,
|
|
|
- method: 'DELETE'
|
|
|
+ // 适配fetch响应格式
|
|
|
+ return {
|
|
|
+ ok: response.statusCode >= 200 && response.statusCode < 300,
|
|
|
+ status: response.statusCode,
|
|
|
+ statusText: response.errMsg || 'OK',
|
|
|
+ headers: new Headers(response.header || {}),
|
|
|
+ url: response.data?.url || url,
|
|
|
+ json: async () => response.data,
|
|
|
+ text: async () => JSON.stringify(response.data),
|
|
|
+ blob: async () => new Blob([JSON.stringify(response.data)]),
|
|
|
+ arrayBuffer: async () => new TextEncoder().encode(JSON.stringify(response.data)),
|
|
|
+ clone: () => ({}) as Response,
|
|
|
+ body: null as any,
|
|
|
+ bodyUsed: true
|
|
|
+ } as Response
|
|
|
+ } catch (error) {
|
|
|
+ console.error('API Error:', error)
|
|
|
+ Taro.showToast({
|
|
|
+ title: error.message || '网络错误',
|
|
|
+ icon: 'none'
|
|
|
})
|
|
|
+ throw error
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 创建单例实例
|
|
|
-export const apiClient = new ApiClient()
|
|
|
-
|
|
|
-// 认证相关API
|
|
|
-export const authApi = {
|
|
|
- // 密码登录
|
|
|
- login: (data: { username: string; password: string }) =>
|
|
|
- apiClient.post('/auth/login', data),
|
|
|
-
|
|
|
- // 用户注册
|
|
|
- register: (data: { username: string; password: string; email?: string }) =>
|
|
|
- apiClient.post('/auth/register', data),
|
|
|
-
|
|
|
- // 获取当前用户信息
|
|
|
- getCurrentUser: () =>
|
|
|
- apiClient.get('/auth/me'),
|
|
|
-
|
|
|
- // 退出登录
|
|
|
- logout: () =>
|
|
|
- apiClient.post('/auth/logout')
|
|
|
+// 创建Hono RPC客户端
|
|
|
+const createRpcClient = <T>(basePath: string) => {
|
|
|
+ return hc<T>(`${BASE_URL}${basePath}`, {
|
|
|
+ fetch: taroFetch
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
-// 用户相关API
|
|
|
-export const userApi = {
|
|
|
- // 获取用户列表
|
|
|
- getUsers: (params?: any) =>
|
|
|
- apiClient.get('/users', params),
|
|
|
-
|
|
|
- // 获取单个用户信息
|
|
|
- getUser: (id: number) =>
|
|
|
- apiClient.get(`/users/${id}`),
|
|
|
-
|
|
|
- // 更新用户信息
|
|
|
- updateUser: (id: number, data: any) =>
|
|
|
- apiClient.put(`/users/${id}`, data),
|
|
|
-
|
|
|
- // 删除用户
|
|
|
- deleteUser: (id: number) =>
|
|
|
- apiClient.delete(`/users/${id}`)
|
|
|
+// 创建各个模块的RPC客户端
|
|
|
+export const authClient = createRpcClient<AuthRoutes>('/auth')
|
|
|
+export const userClient = createRpcClient<UserRoutes>('/users')
|
|
|
+export const roleClient = createRpcClient<RoleRoutes>('/roles')
|
|
|
+export const fileClient = createRpcClient<FileRoutes>('/files')
|
|
|
+
|
|
|
+// 类型定义
|
|
|
+export type {
|
|
|
+ AuthRoutes,
|
|
|
+ UserRoutes,
|
|
|
+ RoleRoutes,
|
|
|
+ FileRoutes
|
|
|
}
|
|
|
|
|
|
-export default apiClient
|
|
|
+// 默认导出RPC客户端
|
|
|
+export default {
|
|
|
+ auth: authClient,
|
|
|
+ users: userClient,
|
|
|
+ roles: roleClient,
|
|
|
+ files: fileClient,
|
|
|
+}
|