初始化

This commit is contained in:
2025-08-21 10:43:04 +08:00
commit 29a79b1c6b
68 changed files with 13314 additions and 0 deletions
+91
View File
@@ -0,0 +1,91 @@
import axios, { type AxiosInstance, type AxiosRequestConfig, type AxiosResponse } from 'axios';
import type { ApiResponse } from '@/types';
// API配置
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000';
class ApiService {
private client: AxiosInstance;
constructor() {
this.client = axios.create({
baseURL: API_BASE_URL,
timeout: 30000,
headers: {
'Content-Type': 'application/json',
},
});
// 请求拦截器
this.client.interceptors.request.use(
(config) => {
// 可以在这里添加认证token等
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器
this.client.interceptors.response.use(
(response: AxiosResponse<ApiResponse>) => {
return response;
},
(error) => {
// 统一错误处理
if (error.response) {
const { status, data } = error.response;
console.error(`API Error ${status}:`, data);
} else if (error.request) {
console.error('Network Error:', error.request);
} else {
console.error('Request Error:', error.message);
}
return Promise.reject(error);
}
);
}
/**
* GET请求
*/
async get<T = any>(url: string, config?: AxiosRequestConfig): Promise<ApiResponse<T>> {
const response = await this.client.get<ApiResponse<T>>(url, config);
return response.data;
}
/**
* POST请求
*/
async post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<ApiResponse<T>> {
const response = await this.client.post<ApiResponse<T>>(url, data, config);
return response.data;
}
/**
* PUT请求
*/
async put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<ApiResponse<T>> {
const response = await this.client.put<ApiResponse<T>>(url, data, config);
return response.data;
}
/**
* DELETE请求
*/
async delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<ApiResponse<T>> {
const response = await this.client.delete<ApiResponse<T>>(url, config);
return response.data;
}
/**
* 健康检查
*/
async healthCheck(): Promise<ApiResponse> {
return this.get('/health');
}
}
export const apiService = new ApiService();
export default apiService;
+81
View File
@@ -0,0 +1,81 @@
import apiService from './api';
import type { ApiResponse, Artist, User } from '@/types';
export interface ArtistArtworksOptions {
type?: 'art' | 'manga' | 'novel';
filter?: 'for_ios' | 'for_android';
offset?: number;
limit?: number;
}
export interface ArtistFollowingOptions {
restrict?: 'public' | 'private';
offset?: number;
limit?: number;
}
export interface ArtistFollowersOptions {
offset?: number;
limit?: number;
}
class ArtistService {
/**
* 获取作者信息
*/
async getArtistInfo(id: number): Promise<ApiResponse<Artist>> {
return apiService.get<Artist>(`/api/artist/${id}`);
}
/**
* 获取作者作品列表
*/
async getArtistArtworks(id: number, options: ArtistArtworksOptions = {}): Promise<ApiResponse<{ artworks: any[]; next_url?: string; total: number }>> {
const params = new URLSearchParams();
if (options.type) params.append('type', options.type);
if (options.filter) params.append('filter', options.filter);
if (options.offset !== undefined) params.append('offset', options.offset.toString());
if (options.limit !== undefined) params.append('limit', options.limit.toString());
const query = params.toString();
const url = query ? `/api/artist/${id}/artworks?${query}` : `/api/artist/${id}/artworks`;
return apiService.get<{ artworks: any[]; next_url?: string; total: number }>(url);
}
/**
* 获取作者关注列表
*/
async getArtistFollowing(id: number, options: ArtistFollowingOptions = {}): Promise<ApiResponse<{ users: User[]; next_url?: string; total: number }>> {
const params = new URLSearchParams();
if (options.restrict) params.append('restrict', options.restrict);
if (options.offset !== undefined) params.append('offset', options.offset.toString());
if (options.limit !== undefined) params.append('limit', options.limit.toString());
const query = params.toString();
const url = query ? `/api/artist/${id}/following?${query}` : `/api/artist/${id}/following`;
return apiService.get<{ users: User[]; next_url?: string; total: number }>(url);
}
/**
* 获取作者粉丝列表
*/
async getArtistFollowers(id: number, options: ArtistFollowersOptions = {}): Promise<ApiResponse<{ users: User[]; next_url?: string; total: number }>> {
const params = new URLSearchParams();
if (options.offset !== undefined) params.append('offset', options.offset.toString());
if (options.limit !== undefined) params.append('limit', options.limit.toString());
const query = params.toString();
const url = query ? `/api/artist/${id}/followers?${query}` : `/api/artist/${id}/followers`;
return apiService.get<{ users: User[]; next_url?: string; total: number }>(url);
}
/**
* 关注/取消关注作者
*/
async followArtist(id: number, action: 'follow' | 'unfollow'): Promise<ApiResponse> {
return apiService.post(`/api/artist/${id}/follow`, { action });
}
}
export const artistService = new ArtistService();
export default artistService;
+71
View File
@@ -0,0 +1,71 @@
import apiService from './api';
import type { ApiResponse, Artwork, SearchParams, PaginatedResponse } from '@/types';
export interface ArtworkDetailOptions {
include_user?: boolean;
include_series?: boolean;
}
export interface ArtworkImagesResponse {
artwork_id: number;
total_pages: number;
images: Array<{
page: number;
original: string;
large: string;
medium: string;
square_medium: string;
}>;
selected_size: string;
}
class ArtworkService {
/**
* 获取作品详情
*/
async getArtworkDetail(id: number, options: ArtworkDetailOptions = {}): Promise<ApiResponse<Artwork>> {
const params = new URLSearchParams();
if (options.include_user !== undefined) {
params.append('include_user', options.include_user.toString());
}
if (options.include_series !== undefined) {
params.append('include_series', options.include_series.toString());
}
const query = params.toString();
const url = query ? `/api/artwork/${id}?${query}` : `/api/artwork/${id}`;
return apiService.get<Artwork>(url);
}
/**
* 获取作品预览信息
*/
async getArtworkPreview(id: number): Promise<ApiResponse<Artwork>> {
return apiService.get<Artwork>(`/api/artwork/${id}/preview`);
}
/**
* 获取作品图片URL
*/
async getArtworkImages(id: number, size: string = 'medium'): Promise<ApiResponse<ArtworkImagesResponse>> {
return apiService.get<ArtworkImagesResponse>(`/api/artwork/${id}/images?size=${size}`);
}
/**
* 搜索作品
*/
async searchArtworks(params: SearchParams): Promise<ApiResponse<{ artworks: Artwork[]; next_url?: string; total: number }>> {
const queryParams = new URLSearchParams();
queryParams.append('keyword', params.keyword);
if (params.type) queryParams.append('type', params.type);
if (params.sort) queryParams.append('sort', params.sort);
if (params.duration) queryParams.append('duration', params.duration);
if (params.offset !== undefined) queryParams.append('offset', params.offset.toString());
if (params.limit !== undefined) queryParams.append('limit', params.limit.toString());
return apiService.get<{ artworks: Artwork[]; next_url?: string; total: number }>(`/api/artwork/search?${queryParams.toString()}`);
}
}
export const artworkService = new ArtworkService();
export default artworkService;
+59
View File
@@ -0,0 +1,59 @@
import apiService from './api';
import type { ApiResponse, LoginStatus } from '@/types';
export interface LoginUrlResponse {
login_url: string;
code_verifier: string;
}
export interface LoginCallbackRequest {
code: string;
}
export interface LoginCallbackResponse {
user: {
id: number;
name: string;
account: string;
};
}
class AuthService {
/**
* 获取登录状态
*/
async getLoginStatus(): Promise<ApiResponse<LoginStatus>> {
return apiService.get<LoginStatus>('/api/auth/status');
}
/**
* 获取登录URL
*/
async getLoginUrl(): Promise<ApiResponse<LoginUrlResponse>> {
return apiService.get<LoginUrlResponse>('/api/auth/login-url');
}
/**
* 处理登录回调
*/
async handleLoginCallback(code: string): Promise<ApiResponse<LoginCallbackResponse>> {
return apiService.post<LoginCallbackResponse>('/api/auth/callback', { code });
}
/**
* 重新登录
*/
async relogin(): Promise<ApiResponse> {
return apiService.post('/api/auth/relogin');
}
/**
* 登出
*/
async logout(): Promise<ApiResponse> {
return apiService.post('/api/auth/logout');
}
}
export const authService = new AuthService();
export default authService;
+66
View File
@@ -0,0 +1,66 @@
import apiService from './api';
import type { ApiResponse, DownloadTask, DownloadParams } from '@/types';
export interface DownloadArtworkRequest extends DownloadParams {
size?: 'original' | 'large' | 'medium' | 'square_medium';
quality?: 'high' | 'medium' | 'low';
format?: 'auto' | 'jpg' | 'png';
}
export interface DownloadMultipleRequest extends DownloadParams {
artworkIds: number[];
concurrent?: number;
}
export interface DownloadArtistRequest extends DownloadParams {
type?: 'art' | 'manga' | 'novel';
filter?: 'for_ios' | 'for_android';
limit?: number;
}
class DownloadService {
/**
* 下载单个作品
*/
async downloadArtwork(id: number, params: DownloadArtworkRequest = {}): Promise<ApiResponse<any>> {
return apiService.post(`/api/download/artwork/${id}`, params);
}
/**
* 批量下载作品
*/
async downloadMultipleArtworks(params: DownloadMultipleRequest): Promise<ApiResponse<any>> {
return apiService.post('/api/download/artworks', params);
}
/**
* 下载作者作品
*/
async downloadArtistArtworks(id: number, params: DownloadArtistRequest = {}): Promise<ApiResponse<any>> {
return apiService.post(`/api/download/artist/${id}`, params);
}
/**
* 获取下载进度
*/
async getDownloadProgress(taskId: string): Promise<ApiResponse<DownloadTask>> {
return apiService.get<DownloadTask>(`/api/download/progress/${taskId}`);
}
/**
* 取消下载任务
*/
async cancelDownload(taskId: string): Promise<ApiResponse> {
return apiService.delete(`/api/download/cancel/${taskId}`);
}
/**
* 获取下载历史
*/
async getDownloadHistory(offset: number = 0, limit: number = 20): Promise<ApiResponse<{ tasks: DownloadTask[]; total: number; offset: number; limit: number }>> {
return apiService.get<{ tasks: DownloadTask[]; total: number; offset: number; limit: number }>(`/api/download/history?offset=${offset}&limit=${limit}`);
}
}
export const downloadService = new DownloadService();
export default downloadService;