Initial commit
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
import axios from 'axios';
|
||||
import type { ApiResponse, AuthResponse, User } from '@/types';
|
||||
|
||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '/api';
|
||||
|
||||
const apiClient = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
apiClient.interceptors.request.use((config) => {
|
||||
const token = localStorage.getItem('authToken');
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
||||
apiClient.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
if (error.response?.status === 401) {
|
||||
localStorage.removeItem('authToken');
|
||||
localStorage.removeItem('currentUser');
|
||||
window.location.href = '/login';
|
||||
}
|
||||
if (error.response?.status === 404) {
|
||||
const url = error.config?.url || '';
|
||||
if (url.includes('/query')) {
|
||||
const customError = new Error('未找到该序列号,请检查输入是否正确');
|
||||
customError.name = 'NotFoundError';
|
||||
return Promise.reject(customError);
|
||||
}
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export const authApi = {
|
||||
login: async (username: string, password: string) => {
|
||||
const response = await apiClient.post('/auth/login', { username, password });
|
||||
// 后端返回的是 accessToken,前端期望的是 token
|
||||
const token = response.data.accessToken || response.data.token;
|
||||
const user = response.data.user;
|
||||
|
||||
if (token && user) {
|
||||
localStorage.setItem('authToken', token);
|
||||
localStorage.setItem('currentUser', JSON.stringify(user));
|
||||
return user;
|
||||
}
|
||||
throw new Error('登录失败');
|
||||
},
|
||||
|
||||
logout: async () => {
|
||||
try {
|
||||
await apiClient.post('/auth/logout');
|
||||
} catch (error) {
|
||||
console.error('Logout error:', error);
|
||||
}
|
||||
localStorage.removeItem('authToken');
|
||||
localStorage.removeItem('currentUser');
|
||||
},
|
||||
|
||||
getCurrentUser: (): User | null => {
|
||||
const user = localStorage.getItem('currentUser');
|
||||
return user ? JSON.parse(user) : null;
|
||||
},
|
||||
|
||||
updateProfile: async (data: { name?: string; email?: string }) => {
|
||||
const response = await apiClient.put('/auth/profile', data);
|
||||
const user = response.data;
|
||||
if (user) {
|
||||
localStorage.setItem('currentUser', JSON.stringify(user));
|
||||
return user;
|
||||
}
|
||||
throw new Error('更新资料失败');
|
||||
},
|
||||
|
||||
changePassword: async (currentPassword: string, newPassword: string) => {
|
||||
const response = await apiClient.post('/auth/change-password', { currentPassword, newPassword });
|
||||
if (response.data.message) {
|
||||
return true;
|
||||
}
|
||||
throw new Error(response.data.error || '修改密码失败');
|
||||
},
|
||||
};
|
||||
|
||||
export const serialApi = {
|
||||
generate: async (data: {
|
||||
companyName: string;
|
||||
serialOption: 'auto' | 'custom';
|
||||
serialPrefix?: string;
|
||||
quantity: number;
|
||||
validOption: 'days' | 'date';
|
||||
validDays?: number;
|
||||
validUntil?: string;
|
||||
}) => {
|
||||
// 根据后端接口调整参数
|
||||
const payload = {
|
||||
companyName: data.companyName,
|
||||
quantity: data.quantity,
|
||||
validDays: data.validOption === 'days' ? data.validDays : undefined,
|
||||
serialPrefix: data.serialOption === 'custom' ? data.serialPrefix : undefined,
|
||||
};
|
||||
|
||||
const response = await apiClient.post('/serials/generate', payload);
|
||||
if (response.data.serials) {
|
||||
return response.data;
|
||||
}
|
||||
throw new Error(response.data.error || '生成序列号失败');
|
||||
},
|
||||
|
||||
query: async (serialNumber: string) => {
|
||||
// 后端路径是正确的: /api/serials/:serialNumber/query
|
||||
const response = await apiClient.get(`/serials/${encodeURIComponent(serialNumber)}/query`);
|
||||
if (response.data.serial) {
|
||||
return response.data.serial;
|
||||
}
|
||||
throw new Error(response.data.error || '查询序列号失败');
|
||||
},
|
||||
|
||||
list: async (companyId?: number) => {
|
||||
let url = '/serials';
|
||||
if (companyId) url += `?companyId=${companyId}`;
|
||||
const response = await apiClient.get(url);
|
||||
if (response.data.data) {
|
||||
return response.data.data;
|
||||
}
|
||||
throw new Error('获取序列号列表失败');
|
||||
},
|
||||
|
||||
delete: async (id: number) => {
|
||||
// 后端没有单个删除接口,需要使用企业接口下的删除
|
||||
const response = await apiClient.delete(`/serials/${id}`);
|
||||
if (response.data) {
|
||||
return true;
|
||||
}
|
||||
throw new Error(response.data.error || '删除序列号失败');
|
||||
},
|
||||
};
|
||||
|
||||
export const companyApi = {
|
||||
list: async (filter?: { search?: string; status?: 'all' | 'active' | 'expired' }) => {
|
||||
let url = '/companies';
|
||||
if (filter?.search || filter?.status) {
|
||||
const params = new URLSearchParams();
|
||||
if (filter.search) params.append('search', filter.search);
|
||||
if (filter.status && filter.status !== 'all') params.append('status', filter.status);
|
||||
url += `?${params.toString()}`;
|
||||
}
|
||||
const response = await apiClient.get(url);
|
||||
if (response.data.data) {
|
||||
return response.data.data;
|
||||
}
|
||||
throw new Error('获取企业列表失败');
|
||||
},
|
||||
|
||||
get: async (companyName: string) => {
|
||||
const response = await apiClient.get(`/companies/${encodeURIComponent(companyName)}`);
|
||||
if (response.data.data) {
|
||||
return response.data.data;
|
||||
}
|
||||
throw new Error('获取企业详情失败');
|
||||
},
|
||||
|
||||
delete: async (companyName: string) => {
|
||||
const response = await apiClient.delete(`/companies/${encodeURIComponent(companyName)}`);
|
||||
if (response.data) {
|
||||
return true;
|
||||
}
|
||||
throw new Error(response.data.error || '删除企业失败');
|
||||
},
|
||||
};
|
||||
|
||||
export const dashboardApi = {
|
||||
getStats: async () => {
|
||||
// 后端路径是 /api/companies/stats/overview
|
||||
const response = await apiClient.get('/companies/stats/overview');
|
||||
if (response.data.data) {
|
||||
const data = response.data.data;
|
||||
// 转换数据格式以匹配前端期望
|
||||
return {
|
||||
totalCompanies: data.overview?.totalCompanies || 0,
|
||||
totalSerials: data.overview?.totalSerials || 0,
|
||||
activeSerials: data.overview?.activeSerials || 0,
|
||||
inactiveSerials: data.overview?.inactiveSerials || 0,
|
||||
monthlyData: data.monthlyStats || [],
|
||||
recentCompanies: data.recentCompanies?.map((c: any) => ({
|
||||
id: c.companyName,
|
||||
name: c.companyName,
|
||||
status: 'active' as const,
|
||||
createdAt: c.lastCreated,
|
||||
})) || [],
|
||||
recentSerials: data.recentSerials?.map((s: any) => ({
|
||||
id: s.serialNumber,
|
||||
serialNumber: s.serialNumber,
|
||||
companyName: s.companyName,
|
||||
status: s.isActive ? 'active' : 'inactive',
|
||||
createdAt: s.createdAt,
|
||||
})) || [],
|
||||
};
|
||||
}
|
||||
throw new Error('获取统计数据失败');
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user