Files
x-server-mobile/frontend/src/services/api.ts
T

183 lines
5.2 KiB
TypeScript
Raw Normal View History

import axios from 'axios'
import { Toast } from 'antd-mobile'
const API_BASE = '/api/v1'
// 标记是否正在处理登出(防止重复跳转)
let isLoggingOut = false
// 处理 401 未授权错误
const handleUnauthorized = () => {
if (isLoggingOut) return
isLoggingOut = true
// 清除本地存储
localStorage.removeItem('token')
localStorage.removeItem('user-storage')
// 显示提示
Toast.show({
content: '登录已过期,请重新登录',
duration: 2000,
})
// 延迟跳转,确保提示显示
setTimeout(() => {
isLoggingOut = false
// 跳转到登录页,并清除历史记录
window.location.href = '/login'
}, 2000)
}
// 创建 axios 实例
const api = axios.create({
baseURL: API_BASE,
timeout: 30000,
headers: {
'Content-Type': 'application/json',
},
})
2026-02-25 18:21:05 +08:00
// 请求拦截器:添加 token 和语言
api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
2026-02-25 18:21:05 +08:00
// 添加语言头
const lang = localStorage.getItem('i18nextLng') || 'zh'
config.headers['Accept-Language'] = lang
return config
},
(error) => {
return Promise.reject(error)
}
)
// 响应拦截器:处理错误
api.interceptors.response.use(
(response) => {
const { data } = response
if (data.code === 0) {
return data.data
}
return Promise.reject(new Error(data.message || '请求失败'))
},
(error) => {
// 处理 401 未授权错误
if (error.response?.status === 401 || error.response?.data?.code === 401) {
handleUnauthorized()
return Promise.reject(new Error('登录已过期'))
}
// 处理其他错误
const message = error.response?.data?.message || error.message || '网络错误'
return Promise.reject(new Error(message))
}
)
// ========== 认证 API ==========
export const authApi = {
login: (username: string, password: string) =>
api.post('/auth/login', { username, password }),
// 飞书免登(支持 app_id 或 tenant_key)
feishuLogin: (authCode: string, options?: { appId?: string; tenantKey?: string }) =>
api.post('/auth/feishu-login', {
auth_code: authCode,
app_id: options?.appId,
tenant_key: options?.tenantKey,
}),
// 生成二维码登录会话
qrcodeLogin: (appId?: string, tenantKey?: string) =>
api.post('/auth/qrcode-login', {
app_id: appId,
tenant_key: tenantKey,
}),
// 轮询二维码登录状态
qrcodeLoginStatus: (sessionId: string) =>
api.get('/auth/qrcode-login-status', { params: { session_id: sessionId } }),
}
// ========== 用户 API ==========
export const userApi = {
getInfo: () => api.get('/user/info'),
getMenu: () => api.get('/user/menu'),
getCompanies: () => api.get('/user/companies'),
switchCompany: (companyId: number) =>
api.post('/user/switch-company', { company_id: companyId }),
// 获取用户列表
getUsers: (params: { keyword?: string; page?: number; page_size?: number }) =>
api.get('/user/list', { params }),
// 修改密码
changePassword: (data: { old_password: string; new_password: string; confirm_password: string }) =>
api.post('/user/change-password', data),
}
// ========== 出差申请 API ==========
export const businessTripApi = {
getList: (params: { type?: string; page?: number; page_size?: number }) =>
api.get('/business-trip/list', { params }),
getDetail: (id: number) => api.get(`/business-trip/detail/${id}`),
// 获取默认审批人和抄送人
getDefaultApprovers: () => api.get('/business-trip/default-approvers'),
create: (data: any) => api.post('/business-trip/create', data),
update: (id: number, data: any) =>
api.post(`/business-trip/update/${id}`, data),
submit: (id: number, approvers: number[], ccUsers: number[] = []) =>
api.post(`/business-trip/submit/${id}`, {
approvers,
cc_users: ccUsers,
}),
cancel: (id: number) => api.post(`/business-trip/cancel/${id}`),
deleteDraft: (id: number) => api.delete(`/business-trip/${id}`),
approve: (id: number, action: 'approve' | 'reject', comment?: string) =>
api.post(`/business-trip/approve/${id}`, { action, comment }),
}
// ========== 审批 API ==========
export const approvalApi = {
getList: (params: { type?: string; page?: number; page_size?: number }) => {
console.log('🚀 API Request approval/list:', params)
return api.get('/approval/list', { params })
.then(res => {
console.log('✅ API Response approval/list:', {
requestParams: params,
responseData: res,
listCount: res?.list?.length,
listIds: res?.list?.map((item: any) => ({ id: item.id, title: item.title, status: item.status }))
})
return res
})
.catch(err => {
console.error('❌ API Error approval/list:', err)
throw err
})
},
getDetail: (id: number) => api.get(`/approval/detail/${id}`),
approve: (id: number, comment?: string) =>
api.post(`/approval/approve/${id}`, { comment }),
reject: (id: number, comment?: string) =>
api.post(`/approval/reject/${id}`, { comment }),
cancel: (id: number) => api.post(`/approval/cancel/${id}`),
}
export default api