mirror of
https://github.com/DrizzleTime/Foxel.git
synced 2026-05-13 09:00:31 +08:00
refactor(api): streamline imports and enhance type definitions across multiple files
This commit is contained in:
@@ -1,13 +1,42 @@
|
||||
import type {
|
||||
PaginatedResult,
|
||||
AlbumResponse,
|
||||
CreateAlbumRequest,
|
||||
UpdateAlbumRequest,
|
||||
AlbumPictureRequest,
|
||||
AlbumPicturesRequest,
|
||||
BaseResult
|
||||
} from './types';
|
||||
import { fetchApi, BASE_URL } from './fetchClient';
|
||||
import { fetchApi, type PaginatedResult, type BaseResult } from './fetchClient';
|
||||
|
||||
// 相册响应数据
|
||||
export interface AlbumResponse {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
coverImageUrl?: string;
|
||||
pictureCount: number;
|
||||
userId: number;
|
||||
username?: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
// 创建相册请求
|
||||
export interface CreateAlbumRequest {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
// 更新相册请求
|
||||
export interface UpdateAlbumRequest {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
// 相册图片操作请求
|
||||
export interface AlbumPictureRequest {
|
||||
albumId: number;
|
||||
pictureId: number;
|
||||
}
|
||||
|
||||
// 批量添加图片到相册请求
|
||||
export interface AlbumPicturesRequest {
|
||||
albumId: number;
|
||||
pictureIds: number[];
|
||||
}
|
||||
|
||||
// 获取相册列表
|
||||
export async function getAlbums(
|
||||
@@ -16,14 +45,6 @@ export async function getAlbums(
|
||||
userId?: number
|
||||
): Promise<PaginatedResult<AlbumResponse>> {
|
||||
try {
|
||||
const token = localStorage.getItem('token');
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
const queryParams = new URLSearchParams();
|
||||
queryParams.append('page', page.toString());
|
||||
queryParams.append('pageSize', pageSize.toString());
|
||||
@@ -31,19 +52,31 @@ export async function getAlbums(
|
||||
queryParams.append('userId', userId.toString());
|
||||
}
|
||||
|
||||
const url = `${BASE_URL}/album/get_albums?${queryParams.toString()}`;
|
||||
const response = await fetch(url, { headers });
|
||||
const data = await response.json();
|
||||
|
||||
return data as PaginatedResult<AlbumResponse>;
|
||||
const url = `/album/get_albums?${queryParams.toString()}`;
|
||||
const result = await fetchApi<PaginatedResult<AlbumResponse>>(url);
|
||||
if (result.success) {
|
||||
return result as unknown as PaginatedResult<AlbumResponse>;
|
||||
} else {
|
||||
console.error('获取相册列表失败:', result.message);
|
||||
return {
|
||||
success: false,
|
||||
message: result.message || '获取相册列表失败',
|
||||
data: [],
|
||||
page: page,
|
||||
pageSize: pageSize,
|
||||
totalCount: 0,
|
||||
totalPages: 0,
|
||||
code: result.code || 500,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取相册列表失败:', error);
|
||||
console.error('获取相册列表时发生意外错误:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: '网络请求失败,请检查您的网络连接',
|
||||
data: [],
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
page: page,
|
||||
pageSize: pageSize,
|
||||
totalCount: 0,
|
||||
totalPages: 0,
|
||||
code: 500,
|
||||
|
||||
@@ -1,10 +1,58 @@
|
||||
import {type BaseResult, type AuthResponse, type LoginRequest, type RegisterRequest, type UserProfile, type UpdateUserRequest, type BindAccountRequest} from './types';
|
||||
import {fetchApi, BASE_URL} from './fetchClient';
|
||||
import {fetchApi, BASE_URL, type BaseResult} from './fetchClient';
|
||||
import { type UserRole } from './userManagementApi';
|
||||
|
||||
// 认证数据本地存储键
|
||||
const TOKEN_KEY = 'token';
|
||||
const USER_KEY = 'user';
|
||||
|
||||
// 登录请求参数
|
||||
export interface LoginRequest {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
// 注册请求参数
|
||||
export interface RegisterRequest {
|
||||
username: string;
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
// 用户信息
|
||||
export interface UserProfile {
|
||||
id: number;
|
||||
userName: string;
|
||||
email: string;
|
||||
roleName: UserRole | string; // roleName can be UserRole or a string from server
|
||||
}
|
||||
|
||||
// 认证响应
|
||||
export interface AuthResponse {
|
||||
token: string;
|
||||
user: UserProfile;
|
||||
}
|
||||
|
||||
export interface UpdateUserRequest {
|
||||
userName?: string;
|
||||
email?: string;
|
||||
currentPassword?: string;
|
||||
newPassword?: string;
|
||||
}
|
||||
|
||||
export type BindType = 0 | 1;
|
||||
|
||||
export const BindType = {
|
||||
GitHub: 0 as BindType,
|
||||
LinuxDo: 1 as BindType,
|
||||
};
|
||||
|
||||
export interface BindAccountRequest {
|
||||
email: string;
|
||||
password: string;
|
||||
bindType: BindType;
|
||||
thirdPartyUserId: string;
|
||||
}
|
||||
|
||||
// 用户注册
|
||||
export async function register(data: RegisterRequest): Promise<BaseResult<AuthResponse>> {
|
||||
return fetchApi<AuthResponse>('/auth/register', {
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
import { fetchApi } from './fetchClient';
|
||||
import type { BaseResult, PictureProcessingTask } from './types';
|
||||
import { fetchApi, type BaseResult } from './fetchClient';
|
||||
import type { ProcessingStatus } from './pictureApi';
|
||||
|
||||
// 图片处理任务
|
||||
export interface PictureProcessingTask {
|
||||
pictureId: number;
|
||||
taskId: string;
|
||||
pictureName: string;
|
||||
status: ProcessingStatus;
|
||||
progress: number; // 0-100
|
||||
error?: string;
|
||||
createdAt: Date;
|
||||
completedAt?: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户的所有处理任务
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
import { UserRole, type BaseResult, type ConfigResponse, type SetConfigRequest } from './types';
|
||||
import { fetchApi } from './fetchClient';
|
||||
import { fetchApi, type BaseResult } from './fetchClient';
|
||||
import { UserRole } from './userManagementApi';
|
||||
|
||||
|
||||
// 配置响应数据
|
||||
export interface ConfigResponse {
|
||||
id: number;
|
||||
key: string;
|
||||
value: string;
|
||||
description: string;
|
||||
isSecret: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
export interface SetConfigRequest {
|
||||
key: string;
|
||||
value: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
// 获取所有配置
|
||||
export const getAllConfigs = async (): Promise<BaseResult<ConfigResponse[]>> => {
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
import type { BaseResult } from './types';
|
||||
import { clearAuthData } from './index';
|
||||
|
||||
// API响应的基础结构
|
||||
export interface BaseResult<T> {
|
||||
success: boolean;
|
||||
message: string;
|
||||
data?: T;
|
||||
code: number;
|
||||
}
|
||||
|
||||
// 分页结果通用结构
|
||||
export interface PaginatedResult<T> {
|
||||
success: boolean;
|
||||
message: string;
|
||||
data: T[];
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalCount: number;
|
||||
totalPages: number;
|
||||
code: number;
|
||||
}
|
||||
|
||||
export const BASE_URL = import.meta.env.PROD ? '/api' : 'http://localhost:5153/api';
|
||||
|
||||
export async function fetchApi<T = any>(
|
||||
|
||||
@@ -1,100 +1,11 @@
|
||||
// 重新导出类型
|
||||
export * from './authApi';
|
||||
export * from './types';
|
||||
|
||||
// 导出fetch客户端
|
||||
export { fetchApi, BASE_URL } from './fetchClient';
|
||||
|
||||
// 导出Auth API
|
||||
export {
|
||||
register,
|
||||
login,
|
||||
getCurrentUser,
|
||||
updateUserInfo,
|
||||
saveAuthData,
|
||||
clearAuthData,
|
||||
isAuthenticated,
|
||||
getStoredUser,
|
||||
bindAccount,
|
||||
getGitHubLoginUrl,
|
||||
getLinuxDoLoginUrl
|
||||
} from './authApi';
|
||||
|
||||
// 导出Picture API
|
||||
export {
|
||||
getPictures,
|
||||
favoritePicture,
|
||||
unfavoritePicture,
|
||||
getUserFavorites,
|
||||
uploadPicture,
|
||||
deleteMultiplePictures,
|
||||
updatePicture,
|
||||
} from './pictureApi';
|
||||
|
||||
// 导出Album API
|
||||
export {
|
||||
getAlbums,
|
||||
getAlbumById,
|
||||
createAlbum,
|
||||
updateAlbum,
|
||||
deleteAlbum,
|
||||
addPictureToAlbum,
|
||||
addPicturesToAlbum,
|
||||
removePictureFromAlbum
|
||||
} from './albumApi';
|
||||
|
||||
// 导出BackgroundTask API
|
||||
export {
|
||||
getUserTasks,
|
||||
getPictureProcessingStatus,
|
||||
} from './backgroundTaskApi';
|
||||
|
||||
// 导出Config API
|
||||
export {
|
||||
getAllConfigs,
|
||||
getConfig,
|
||||
setConfig,
|
||||
deleteConfig,
|
||||
hasRole,
|
||||
backupConfigs,
|
||||
restoreConfigs
|
||||
} from './configApi';
|
||||
|
||||
// 导出UserManagement API
|
||||
export {
|
||||
getUsers,
|
||||
getUserById,
|
||||
createUser,
|
||||
updateUser,
|
||||
deleteUser,
|
||||
batchDeleteUsers,
|
||||
getUserDetail
|
||||
} from './userManagementApi';
|
||||
|
||||
// 导出PictureManagement API
|
||||
export {
|
||||
getManagementPictures,
|
||||
getManagementPictureById,
|
||||
deleteManagementPicture,
|
||||
batchDeleteManagementPictures,
|
||||
getManagementPicturesByUserId
|
||||
} from './pictureManagementApi';
|
||||
|
||||
// 导出向量数据库 API
|
||||
export {
|
||||
getCurrentVectorDb,
|
||||
switchVectorDb,
|
||||
clearVectors,
|
||||
rebuildVectors
|
||||
} from './vectorDbApi';
|
||||
|
||||
// 导出LogManagement API
|
||||
export {
|
||||
getLogs,
|
||||
getLogById,
|
||||
deleteLog,
|
||||
batchDeleteLogs,
|
||||
clearLogs,
|
||||
getLogStatistics
|
||||
} from './logManagementApi';
|
||||
|
||||
export * from './albumApi';
|
||||
export * from './backgroundTaskApi';
|
||||
export * from './configApi';
|
||||
export * from './fetchClient';
|
||||
export * from './logManagementApi';
|
||||
export * from './pictureApi';
|
||||
export * from './pictureManagementApi';
|
||||
export * from './tagApi';
|
||||
export * from './userManagementApi';
|
||||
export * from './vectorDbApi';
|
||||
@@ -1,13 +1,60 @@
|
||||
import { fetchApi } from './fetchClient';
|
||||
import {
|
||||
type BaseResult,
|
||||
type PaginatedResult,
|
||||
type LogResponse,
|
||||
type LogFilterRequest,
|
||||
type ClearLogsRequest,
|
||||
type BatchDeleteResult,
|
||||
type LogStatistics
|
||||
} from './types';
|
||||
import { fetchApi, type BaseResult, type PaginatedResult } from './fetchClient';
|
||||
import { type BatchDeleteResult } from './userManagementApi';
|
||||
|
||||
|
||||
// 日志级别枚举
|
||||
export type LogLevel = 'Trace' | 'Debug' | 'Information' | 'Warning' | 'Error' | 'Critical';
|
||||
|
||||
export const LogLevel = {
|
||||
Trace: 'Trace' as LogLevel,
|
||||
Debug: 'Debug' as LogLevel,
|
||||
Information: 'Information' as LogLevel,
|
||||
Warning: 'Warning' as LogLevel,
|
||||
Error: 'Error' as LogLevel,
|
||||
Critical: 'Critical' as LogLevel
|
||||
};
|
||||
|
||||
// 日志响应数据
|
||||
export interface LogResponse {
|
||||
id: number;
|
||||
level: LogLevel | number; // 支持数字和字符串两种形式
|
||||
message: string;
|
||||
category: string;
|
||||
eventId?: number;
|
||||
timestamp: Date;
|
||||
exception?: string;
|
||||
requestPath?: string;
|
||||
requestMethod?: string;
|
||||
statusCode?: number;
|
||||
ipAddress?: string;
|
||||
userId?: string;
|
||||
properties?: string;
|
||||
}
|
||||
|
||||
// 日志筛选请求参数
|
||||
export interface LogFilterRequest {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
searchQuery?: string;
|
||||
level?: LogLevel | number; // 支持数字和字符串两种形式
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
}
|
||||
|
||||
// 清空日志请求
|
||||
export interface ClearLogsRequest {
|
||||
clearAll?: boolean;
|
||||
beforeDate?: Date;
|
||||
}
|
||||
|
||||
// 日志统计信息
|
||||
export interface LogStatistics {
|
||||
totalCount: number;
|
||||
todayCount: number;
|
||||
errorCount: number;
|
||||
warningCount: number;
|
||||
}
|
||||
|
||||
|
||||
// 获取日志列表
|
||||
export const getLogs = async (
|
||||
|
||||
@@ -1,11 +1,108 @@
|
||||
import type { PaginatedResult, PictureResponse, FilteredPicturesRequest, BaseResult, UpdatePictureRequest } from './types';
|
||||
import { fetchApi, BASE_URL } from './fetchClient';
|
||||
import { fetchApi, BASE_URL, type PaginatedResult, type BaseResult } from './fetchClient';
|
||||
|
||||
// 图片请求参数
|
||||
export interface FilteredPicturesRequest {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
searchQuery?: string;
|
||||
tags?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
userId?: number;
|
||||
sortBy?: string;
|
||||
onlyWithGps?: boolean;
|
||||
useVectorSearch?: boolean;
|
||||
similarityThreshold?: number;
|
||||
excludeAlbumId?: number;
|
||||
albumId?: number;
|
||||
onlyFavorites?: boolean;
|
||||
ownerId?: number;
|
||||
includeAllPublic?: boolean;
|
||||
}
|
||||
|
||||
// 将类型定义改为枚举,这样既可以作为类型也可以作为值使用
|
||||
export type ProcessingStatus = 'Pending' | 'Processing' | 'Completed' | 'Failed';
|
||||
|
||||
// 添加常量对象提供运行时值
|
||||
export const ProcessingStatus = {
|
||||
Pending: 'Pending' as ProcessingStatus,
|
||||
Processing: 'Processing' as ProcessingStatus,
|
||||
Completed: 'Completed' as ProcessingStatus,
|
||||
Failed: 'Failed' as ProcessingStatus
|
||||
};
|
||||
|
||||
// 图片响应数据
|
||||
export interface PictureResponse {
|
||||
id: number;
|
||||
name: string;
|
||||
path: string;
|
||||
thumbnailPath: string;
|
||||
description: string;
|
||||
takenAt?: Date;
|
||||
createdAt: Date;
|
||||
exifInfo?: any;
|
||||
tags?: string[];
|
||||
userId: number;
|
||||
username?: string;
|
||||
isFavorited: boolean;
|
||||
favoriteCount: number;
|
||||
permission: number;
|
||||
albumId?: number;
|
||||
albumName?: string;
|
||||
processingStatus: ProcessingStatus;
|
||||
processingError?: string;
|
||||
processingProgress: number;
|
||||
}
|
||||
|
||||
// 收藏请求
|
||||
export interface FavoriteRequest {
|
||||
pictureId: number;
|
||||
}
|
||||
|
||||
// 上传队列中的文件项
|
||||
export interface UploadFile {
|
||||
id: string; // 本地ID,用于跟踪状态
|
||||
file: File; // 原始文件
|
||||
status: 'pending' | 'uploading' | 'success' | 'error'; // 上传状态
|
||||
percent: number; // 上传进度百分比 0-100
|
||||
error?: string; // 错误信息
|
||||
response?: PictureResponse; // 上传成功后的响应
|
||||
}
|
||||
|
||||
// 图片格式类型
|
||||
export type ImageFormat = 0 | 1 | 2 | 3;
|
||||
|
||||
// 添加常量对象提供运行时值
|
||||
export const ImageFormat = {
|
||||
Original: 0 as ImageFormat,
|
||||
Jpeg: 1 as ImageFormat,
|
||||
Png: 2 as ImageFormat,
|
||||
WebP: 3 as ImageFormat
|
||||
};
|
||||
|
||||
// 上传图片参数
|
||||
export interface UploadPictureParams {
|
||||
permission?: number;
|
||||
albumId?: number;
|
||||
convertToFormat?: ImageFormat;
|
||||
quality?: number;
|
||||
onProgress?: (percent: number) => void;
|
||||
}
|
||||
|
||||
// 删除多张图片请求
|
||||
export interface DeleteMultiplePicturesRequest {
|
||||
pictureIds: number[];
|
||||
}
|
||||
|
||||
export interface UpdatePictureRequest {
|
||||
id: number;
|
||||
name?: string;
|
||||
description?: string;
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
// 获取图片列表
|
||||
export async function getPictures(params: FilteredPicturesRequest = {}): Promise<PaginatedResult<PictureResponse>> {
|
||||
// 添加调试日志
|
||||
console.log("Search API 请求参数:", params);
|
||||
|
||||
// 构建查询参数
|
||||
const queryParams = new URLSearchParams();
|
||||
|
||||
@@ -27,38 +124,34 @@ export async function getPictures(params: FilteredPicturesRequest = {}): Promise
|
||||
if (params.ownerId !== undefined) queryParams.append('ownerId', params.ownerId.toString());
|
||||
if (params.includeAllPublic !== undefined) queryParams.append('includeAllPublic', params.includeAllPublic.toString());
|
||||
|
||||
// 最终URL调试日志
|
||||
const url = `${BASE_URL}/picture/get_pictures?${queryParams.toString()}`;
|
||||
console.log("发送API请求:", url);
|
||||
const url = `/picture/get_pictures?${queryParams.toString()}`;
|
||||
|
||||
try {
|
||||
const token = localStorage.getItem('token');
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
const result = await fetchApi<PaginatedResult<PictureResponse>>(url);
|
||||
|
||||
if (result.success) {
|
||||
return result as unknown as PaginatedResult<PictureResponse>;
|
||||
} else {
|
||||
console.error('获取图片列表失败:', result.message);
|
||||
return {
|
||||
success: false,
|
||||
message: result.message || '获取图片列表失败',
|
||||
data: [],
|
||||
page: params.page || 1,
|
||||
pageSize: params.pageSize || 10,
|
||||
totalCount: 0,
|
||||
totalPages: 0,
|
||||
code: result.code || 500,
|
||||
};
|
||||
}
|
||||
|
||||
const response = await fetch(url, { headers });
|
||||
const data = await response.json();
|
||||
|
||||
// 添加结果日志
|
||||
console.log("API 响应结果:", {
|
||||
success: data.success,
|
||||
totalCount: data.totalCount,
|
||||
resultsCount: data.data?.length || 0
|
||||
});
|
||||
|
||||
return data as PaginatedResult<PictureResponse>;
|
||||
} catch (error) {
|
||||
console.error('获取图片列表失败:', error);
|
||||
console.error('获取图片列表时发生意外错误:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: '网络请求失败,请检查您的网络连接',
|
||||
data: [],
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
page: params.page || 1,
|
||||
pageSize: params.pageSize || 10,
|
||||
totalCount: 0,
|
||||
totalPages: 0,
|
||||
code: 500,
|
||||
@@ -85,27 +178,33 @@ export async function unfavoritePicture(pictureId: number): Promise<BaseResult<b
|
||||
// 获取用户收藏的图片
|
||||
export async function getUserFavorites(page: number = 1, pageSize: number = 8): Promise<PaginatedResult<PictureResponse>> {
|
||||
try {
|
||||
const token = localStorage.getItem('token');
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
const url = `/picture/favorites?page=${page}&pageSize=${pageSize}`;
|
||||
// 使用 fetchApi 替换原生 fetch
|
||||
const result = await fetchApi<PaginatedResult<PictureResponse>>(url);
|
||||
|
||||
if (result.success) {
|
||||
return result as unknown as PaginatedResult<PictureResponse>;
|
||||
} else {
|
||||
console.error('获取收藏图片失败:', result.message);
|
||||
return {
|
||||
success: false,
|
||||
message: result.message || '获取收藏图片失败',
|
||||
data: [],
|
||||
page: page,
|
||||
pageSize: pageSize,
|
||||
totalCount: 0,
|
||||
totalPages: 0,
|
||||
code: result.code || 500,
|
||||
};
|
||||
}
|
||||
|
||||
const url = `${BASE_URL}/picture/favorites?page=${page}&pageSize=${pageSize}`;
|
||||
const response = await fetch(url, { headers });
|
||||
const data = await response.json();
|
||||
|
||||
return data as PaginatedResult<PictureResponse>;
|
||||
} catch (error) {
|
||||
console.error('获取收藏图片失败:', error);
|
||||
console.error('获取收藏图片时发生意外错误:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: '网络请求失败,请检查您的网络连接',
|
||||
data: [],
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
page: page,
|
||||
pageSize: pageSize,
|
||||
totalCount: 0,
|
||||
totalPages: 0,
|
||||
code: 500,
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { fetchApi } from './fetchClient';
|
||||
import {
|
||||
type BaseResult,
|
||||
type PaginatedResult,
|
||||
type PictureResponse,
|
||||
type BatchDeleteResult
|
||||
} from './types';
|
||||
import { fetchApi, type BaseResult, type PaginatedResult } from './fetchClient';
|
||||
import { type PictureResponse } from './pictureApi';
|
||||
import { type BatchDeleteResult } from './userManagementApi';
|
||||
|
||||
|
||||
// 获取图片列表
|
||||
export const getManagementPictures = async (
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { BaseResult, PaginatedResult } from './types';
|
||||
import { fetchApi, BASE_URL } from './fetchClient';
|
||||
import { fetchApi, type BaseResult, type PaginatedResult } from './fetchClient';
|
||||
|
||||
// 标签响应类型
|
||||
export interface TagResponse {
|
||||
@@ -50,28 +49,31 @@ export async function getFilteredTags(params: FilteredTagsRequest = {}): Promise
|
||||
if (params.sortDirection) queryParams.append('sortDirection', params.sortDirection);
|
||||
|
||||
try {
|
||||
const token = localStorage.getItem('token');
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
const url = `/tag/get_tags?${queryParams.toString()}`;
|
||||
const result = await fetchApi<PaginatedResult<TagResponse>>(url);
|
||||
if (result.success) {
|
||||
return result as unknown as PaginatedResult<TagResponse>;
|
||||
} else {
|
||||
console.error('获取标签列表失败:', result.message);
|
||||
return {
|
||||
success: false,
|
||||
message: result.message || '获取标签列表失败',
|
||||
data: [],
|
||||
page: params.page || 1,
|
||||
pageSize: params.pageSize || 10,
|
||||
totalCount: 0,
|
||||
totalPages: 0,
|
||||
code: result.code || 500,
|
||||
};
|
||||
}
|
||||
|
||||
const url = `${BASE_URL}/tag/get_tags?${queryParams.toString()}`;
|
||||
const response = await fetch(url, { headers });
|
||||
const data = await response.json();
|
||||
|
||||
return data as PaginatedResult<TagResponse>;
|
||||
} catch (error) {
|
||||
console.error('获取标签列表失败:', error);
|
||||
console.error('获取标签列表时发生意外错误:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: '网络请求失败,请检查您的网络连接',
|
||||
data: [],
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
page: params.page || 1,
|
||||
pageSize: params.pageSize || 10,
|
||||
totalCount: 0,
|
||||
totalPages: 0,
|
||||
code: 500,
|
||||
|
||||
@@ -1,370 +0,0 @@
|
||||
// API响应的基础结构
|
||||
export interface BaseResult<T> {
|
||||
success: boolean;
|
||||
message: string;
|
||||
data?: T;
|
||||
code: number;
|
||||
}
|
||||
|
||||
// 分页结果通用结构
|
||||
export interface PaginatedResult<T> {
|
||||
success: boolean;
|
||||
message: string;
|
||||
data: T[];
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalCount: number;
|
||||
totalPages: number;
|
||||
code: number;
|
||||
}
|
||||
|
||||
// 登录请求参数
|
||||
export interface LoginRequest {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
// 注册请求参数
|
||||
export interface RegisterRequest {
|
||||
username: string;
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
// 用户信息
|
||||
export interface UserProfile {
|
||||
id: number;
|
||||
userName: string;
|
||||
email: string;
|
||||
roleName: string;
|
||||
}
|
||||
|
||||
// 认证响应
|
||||
export interface AuthResponse {
|
||||
token: string;
|
||||
user: UserProfile;
|
||||
}
|
||||
|
||||
// 图片请求参数
|
||||
export interface FilteredPicturesRequest {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
searchQuery?: string;
|
||||
tags?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
userId?: number;
|
||||
sortBy?: string;
|
||||
onlyWithGps?: boolean;
|
||||
useVectorSearch?: boolean;
|
||||
similarityThreshold?: number;
|
||||
excludeAlbumId?: number;
|
||||
albumId?: number;
|
||||
onlyFavorites?: boolean;
|
||||
ownerId?: number;
|
||||
includeAllPublic?: boolean;
|
||||
}
|
||||
|
||||
// 图片响应数据
|
||||
export interface PictureResponse {
|
||||
id: number;
|
||||
name: string;
|
||||
path: string;
|
||||
thumbnailPath: string;
|
||||
description: string;
|
||||
takenAt?: Date;
|
||||
createdAt: Date;
|
||||
exifInfo?: any;
|
||||
tags?: string[];
|
||||
userId: number;
|
||||
username?: string;
|
||||
isFavorited: boolean;
|
||||
favoriteCount: number;
|
||||
permission: number;
|
||||
albumId?: number;
|
||||
albumName?: string;
|
||||
processingStatus: ProcessingStatus;
|
||||
processingError?: string;
|
||||
processingProgress: number;
|
||||
}
|
||||
|
||||
// 收藏请求
|
||||
export interface FavoriteRequest {
|
||||
pictureId: number;
|
||||
}
|
||||
|
||||
// 上传队列中的文件项
|
||||
export interface UploadFile {
|
||||
id: string; // 本地ID,用于跟踪状态
|
||||
file: File; // 原始文件
|
||||
status: 'pending' | 'uploading' | 'success' | 'error'; // 上传状态
|
||||
percent: number; // 上传进度百分比 0-100
|
||||
error?: string; // 错误信息
|
||||
response?: PictureResponse; // 上传成功后的响应
|
||||
}
|
||||
|
||||
// 图片格式类型
|
||||
export type ImageFormat = 0 | 1 | 2 | 3;
|
||||
|
||||
// 添加常量对象提供运行时值
|
||||
export const ImageFormat = {
|
||||
Original: 0 as ImageFormat,
|
||||
Jpeg: 1 as ImageFormat,
|
||||
Png: 2 as ImageFormat,
|
||||
WebP: 3 as ImageFormat
|
||||
};
|
||||
|
||||
// 上传图片参数
|
||||
export interface UploadPictureParams {
|
||||
permission?: number;
|
||||
albumId?: number;
|
||||
convertToFormat?: ImageFormat;
|
||||
quality?: number;
|
||||
onProgress?: (percent: number) => void;
|
||||
}
|
||||
|
||||
// 相册响应数据
|
||||
export interface AlbumResponse {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
coverImageUrl?: string;
|
||||
pictureCount: number;
|
||||
userId: number;
|
||||
username?: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
// 创建相册请求
|
||||
export interface CreateAlbumRequest {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
// 更新相册请求
|
||||
export interface UpdateAlbumRequest {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
// 相册图片操作请求
|
||||
export interface AlbumPictureRequest {
|
||||
albumId: number;
|
||||
pictureId: number;
|
||||
}
|
||||
|
||||
// 批量添加图片到相册请求
|
||||
export interface AlbumPicturesRequest {
|
||||
albumId: number;
|
||||
pictureIds: number[];
|
||||
}
|
||||
|
||||
// 删除多张图片请求
|
||||
export interface DeleteMultiplePicturesRequest {
|
||||
pictureIds: number[];
|
||||
}
|
||||
|
||||
// 将类型定义改为枚举,这样既可以作为类型也可以作为值使用
|
||||
export type ProcessingStatus = 'Pending' | 'Processing' | 'Completed' | 'Failed';
|
||||
|
||||
// 添加常量对象提供运行时值
|
||||
export const ProcessingStatus = {
|
||||
Pending: 'Pending' as ProcessingStatus,
|
||||
Processing: 'Processing' as ProcessingStatus,
|
||||
Completed: 'Completed' as ProcessingStatus,
|
||||
Failed: 'Failed' as ProcessingStatus
|
||||
};
|
||||
|
||||
// 图片处理任务
|
||||
export interface PictureProcessingTask {
|
||||
pictureId: number;
|
||||
taskId: string;
|
||||
pictureName: string;
|
||||
status: ProcessingStatus;
|
||||
progress: number; // 0-100
|
||||
error?: string;
|
||||
createdAt: Date;
|
||||
completedAt?: Date;
|
||||
}
|
||||
|
||||
// 配置响应数据
|
||||
export interface ConfigResponse {
|
||||
id: number;
|
||||
key: string;
|
||||
value: string;
|
||||
description: string;
|
||||
isSecret: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
export interface SetConfigRequest {
|
||||
key: string;
|
||||
value: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export type UserRole = "Administrator" | "User" | "";
|
||||
|
||||
export const UserRole = {
|
||||
Administrator: "Administrator" as UserRole,
|
||||
User: "User" as UserRole,
|
||||
Guest: "" as UserRole
|
||||
};
|
||||
|
||||
export interface UpdateUserRequest {
|
||||
userName?: string;
|
||||
email?: string;
|
||||
currentPassword?: string;
|
||||
newPassword?: string;
|
||||
}
|
||||
|
||||
export interface UpdatePictureRequest {
|
||||
id: number;
|
||||
name?: string;
|
||||
description?: string;
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
// 用户管理相关类型
|
||||
export interface UserResponse {
|
||||
id: number;
|
||||
userName: string;
|
||||
email: string;
|
||||
role: string;
|
||||
createdAt: Date;
|
||||
lastLoginAt?: Date;
|
||||
}
|
||||
|
||||
// 管理员创建用户请求
|
||||
export interface CreateUserRequest {
|
||||
userName: string;
|
||||
email: string;
|
||||
password: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
// 管理员更新用户请求
|
||||
export interface AdminUpdateUserRequest {
|
||||
id: number;
|
||||
userName?: string;
|
||||
email?: string;
|
||||
role?: string;
|
||||
}
|
||||
|
||||
// 批量删除结果
|
||||
export interface BatchDeleteResult {
|
||||
successCount: number;
|
||||
failedCount: number;
|
||||
failedIds?: number[];
|
||||
}
|
||||
|
||||
export type VectorDbType = "InMemory" | "Qdrant";
|
||||
|
||||
export const VectorDbType = {
|
||||
InMemory: "InMemory" as VectorDbType,
|
||||
Qdrant: "Qdrant" as VectorDbType,
|
||||
};
|
||||
|
||||
export interface VectorDbInfo {
|
||||
type: string;
|
||||
}
|
||||
|
||||
export type BindType = 0 | 1;
|
||||
|
||||
export const BindType = {
|
||||
GitHub: 0 as BindType,
|
||||
LinuxDo: 1 as BindType,
|
||||
};
|
||||
|
||||
export interface BindAccountRequest {
|
||||
email: string;
|
||||
password: string;
|
||||
bindType: BindType;
|
||||
thirdPartyUserId: string;
|
||||
}
|
||||
|
||||
// 用户筛选请求参数
|
||||
export interface UserFilterRequest {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
searchQuery?: string;
|
||||
role?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
}
|
||||
|
||||
// 用户统计信息
|
||||
export interface UserStatistics {
|
||||
totalPictures: number;
|
||||
totalAlbums: number;
|
||||
totalFavorites: number;
|
||||
favoriteReceivedCount: number;
|
||||
diskUsageMB: number;
|
||||
accountAgeDays: number;
|
||||
}
|
||||
|
||||
// 用户详情响应
|
||||
export interface UserDetailResponse {
|
||||
id: number;
|
||||
userName: string;
|
||||
email: string;
|
||||
role: string;
|
||||
createdAt: Date;
|
||||
statistics: UserStatistics;
|
||||
}
|
||||
|
||||
// 日志级别枚举
|
||||
export type LogLevel = 'Trace' | 'Debug' | 'Information' | 'Warning' | 'Error' | 'Critical';
|
||||
|
||||
export const LogLevel = {
|
||||
Trace: 'Trace' as LogLevel,
|
||||
Debug: 'Debug' as LogLevel,
|
||||
Information: 'Information' as LogLevel,
|
||||
Warning: 'Warning' as LogLevel,
|
||||
Error: 'Error' as LogLevel,
|
||||
Critical: 'Critical' as LogLevel
|
||||
};
|
||||
|
||||
// 日志响应数据
|
||||
export interface LogResponse {
|
||||
id: number;
|
||||
level: LogLevel | number; // 支持数字和字符串两种形式
|
||||
message: string;
|
||||
category: string;
|
||||
eventId?: number;
|
||||
timestamp: Date;
|
||||
exception?: string;
|
||||
requestPath?: string;
|
||||
requestMethod?: string;
|
||||
statusCode?: number;
|
||||
ipAddress?: string;
|
||||
userId?: string;
|
||||
properties?: string;
|
||||
}
|
||||
|
||||
// 日志筛选请求参数
|
||||
export interface LogFilterRequest {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
searchQuery?: string;
|
||||
level?: LogLevel | number; // 支持数字和字符串两种形式
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
}
|
||||
|
||||
// 清空日志请求
|
||||
export interface ClearLogsRequest {
|
||||
clearAll?: boolean;
|
||||
beforeDate?: Date;
|
||||
}
|
||||
|
||||
// 日志统计信息
|
||||
export interface LogStatistics {
|
||||
totalCount: number;
|
||||
todayCount: number;
|
||||
errorCount: number;
|
||||
warningCount: number;
|
||||
}
|
||||
@@ -1,14 +1,75 @@
|
||||
import { fetchApi } from './fetchClient';
|
||||
import {
|
||||
type BaseResult,
|
||||
type PaginatedResult,
|
||||
type UserResponse,
|
||||
type CreateUserRequest,
|
||||
type AdminUpdateUserRequest,
|
||||
type BatchDeleteResult,
|
||||
type UserFilterRequest,
|
||||
type UserDetailResponse
|
||||
} from './types';
|
||||
import { fetchApi, type BaseResult, type PaginatedResult } from './fetchClient';
|
||||
|
||||
export type UserRole = "Administrator" | "User" | "";
|
||||
|
||||
export const UserRole = {
|
||||
Administrator: "Administrator" as UserRole,
|
||||
User: "User" as UserRole,
|
||||
Guest: "" as UserRole
|
||||
};
|
||||
|
||||
// 用户管理相关类型
|
||||
export interface UserResponse {
|
||||
id: number;
|
||||
userName: string;
|
||||
email: string;
|
||||
role: string;
|
||||
createdAt: Date;
|
||||
lastLoginAt?: Date;
|
||||
}
|
||||
|
||||
// 管理员创建用户请求
|
||||
export interface CreateUserRequest {
|
||||
userName: string;
|
||||
email: string;
|
||||
password: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
// 管理员更新用户请求
|
||||
export interface AdminUpdateUserRequest {
|
||||
id: number;
|
||||
userName?: string;
|
||||
email?: string;
|
||||
role?: string;
|
||||
}
|
||||
|
||||
// 批量删除结果
|
||||
export interface BatchDeleteResult {
|
||||
successCount: number;
|
||||
failedCount: number;
|
||||
failedIds?: number[];
|
||||
}
|
||||
|
||||
// 用户筛选请求参数
|
||||
export interface UserFilterRequest {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
searchQuery?: string;
|
||||
role?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
}
|
||||
|
||||
// 用户统计信息
|
||||
export interface UserStatistics {
|
||||
totalPictures: number;
|
||||
totalAlbums: number;
|
||||
totalFavorites: number;
|
||||
favoriteReceivedCount: number;
|
||||
diskUsageMB: number;
|
||||
accountAgeDays: number;
|
||||
}
|
||||
|
||||
// 用户详情响应
|
||||
export interface UserDetailResponse {
|
||||
id: number;
|
||||
userName: string;
|
||||
email: string;
|
||||
role: string;
|
||||
createdAt: Date;
|
||||
statistics: UserStatistics;
|
||||
}
|
||||
|
||||
// 获取用户列表
|
||||
export const getUsers = async (
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
import { type BaseResult, type VectorDbInfo, VectorDbType } from './types';
|
||||
import { fetchApi } from './fetchClient';
|
||||
import { fetchApi, type BaseResult } from './fetchClient';
|
||||
|
||||
export type VectorDbType = "InMemory" | "Qdrant";
|
||||
|
||||
export const VectorDbType = {
|
||||
InMemory: "InMemory" as VectorDbType,
|
||||
Qdrant: "Qdrant" as VectorDbType,
|
||||
};
|
||||
|
||||
export interface VectorDbInfo {
|
||||
type: string;
|
||||
}
|
||||
|
||||
// 获取当前向量数据库类型
|
||||
export const getCurrentVectorDb = async (): Promise<BaseResult<VectorDbInfo>> => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { createContext, useState, useEffect, useContext, useCallback } from 'react';
|
||||
import { getCurrentUser, isAuthenticated, clearAuthData, getStoredUser } from '../api/index';
|
||||
import type { UserProfile } from '../api/types';
|
||||
import { UserRole } from '../api/types';
|
||||
import type { UserProfile } from '../api';
|
||||
import { UserRole } from '../api';
|
||||
|
||||
interface AuthContextType {
|
||||
user: UserProfile | null;
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
CheckCircleOutlined,
|
||||
CloseCircleOutlined
|
||||
} from '@ant-design/icons';
|
||||
import { ProcessingStatus } from '../api/types';
|
||||
import { ProcessingStatus } from '../api';
|
||||
|
||||
interface TaskProgressBarProps {
|
||||
status: ProcessingStatus;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Divider } from 'antd';
|
||||
import { CloseOutlined, DownOutlined, UpOutlined } from '@ant-design/icons';
|
||||
import type { PictureResponse } from '../../api/types';
|
||||
import type { PictureResponse } from '../../api';
|
||||
import './ImageViewer.css';
|
||||
|
||||
interface ImageInfoProps {
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
RotateRightOutlined, HeartOutlined, HeartFilled, DownloadOutlined,
|
||||
ShareAltOutlined, FolderAddOutlined
|
||||
} from '@ant-design/icons';
|
||||
import type { PictureResponse, AlbumResponse } from '../../api/types';
|
||||
import type { PictureResponse, AlbumResponse } from '../../api';
|
||||
import { getAlbums, addPicturesToAlbum, favoritePicture, unfavoritePicture } from '../../api';
|
||||
import ImageInfo from './ImageInfo';
|
||||
import ShareImageDialog from './ShareImageDialog';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { Modal, Tabs, Input, Button, message, Radio, Space, Typography } from 'antd';
|
||||
import { CopyOutlined, CheckOutlined } from '@ant-design/icons';
|
||||
import type { PictureResponse } from '../../api/types';
|
||||
import type { PictureResponse } from '../../api';
|
||||
import './ShareImageDialog.css';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useAuth } from '../auth/AuthContext';
|
||||
import Sidebar from './components/Sidebar';
|
||||
import Header from './components/Header';
|
||||
import Footer from './components/Footer';
|
||||
import { UserRole } from '../api/types';
|
||||
import { UserRole } from '../api';
|
||||
import { getAdminRoutes, type RouteConfig } from '../routes';
|
||||
|
||||
const { Content } = Layout;
|
||||
|
||||
@@ -12,7 +12,7 @@ import type { ColumnsType } from 'antd/es/table';
|
||||
import { useOutletContext } from 'react-router';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { getUsers, getManagementPictures } from '../../../api';
|
||||
import type { UserResponse, PictureResponse } from '../../../api/types';
|
||||
import type { UserResponse, PictureResponse } from '../../../api';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ import {useOutletContext} from 'react-router';
|
||||
import dayjs from 'dayjs';
|
||||
import type {Dayjs} from 'dayjs';
|
||||
import {getLogs, deleteLog, batchDeleteLogs, clearLogs, getLogById, getLogStatistics} from '../../../api';
|
||||
import type {LogResponse, LogLevel, LogFilterRequest, LogStatistics} from '../../../api/types';
|
||||
import type {LogResponse, LogLevel, LogFilterRequest, LogStatistics} from '../../../api';
|
||||
|
||||
const {Title, Text, Paragraph} = Typography;
|
||||
const {RangePicker} = DatePicker;
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
getManagementPictures, deleteManagementPicture, batchDeleteManagementPictures,
|
||||
getUsers
|
||||
} from '../../../api';
|
||||
import type { PictureResponse } from '../../../api/types';
|
||||
import type { PictureResponse } from '../../../api';
|
||||
import { useOutletContext } from 'react-router';
|
||||
import type { Breakpoint } from 'antd';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
||||
import { Card, Radio, Button, message, Spin, Space, Typography, notification, Form, Input, Modal } from 'antd';
|
||||
import { DatabaseOutlined, SyncOutlined, CheckCircleOutlined, InfoCircleOutlined, SaveOutlined, DeleteOutlined, ReloadOutlined } from '@ant-design/icons';
|
||||
import { getCurrentVectorDb, switchVectorDb, setConfig, clearVectors, rebuildVectors } from '../../../api';
|
||||
import { VectorDbType } from '../../../api/types';
|
||||
import { VectorDbType } from '../../../api';
|
||||
|
||||
const { Title, Paragraph } = Typography;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
import {
|
||||
getUsers, deleteUser, createUser, updateUser, batchDeleteUsers, UserRole
|
||||
} from '../../../api';
|
||||
import type { UserResponse, CreateUserRequest, AdminUpdateUserRequest, UserFilterRequest } from '../../../api/types';
|
||||
import type { UserResponse, CreateUserRequest, AdminUpdateUserRequest, UserFilterRequest } from '../../../api';
|
||||
import { useOutletContext } from 'react-router';
|
||||
import { useNavigate } from 'react-router';
|
||||
import type { Breakpoint } from 'antd';
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from '@ant-design/icons';
|
||||
import { useParams, useNavigate } from 'react-router';
|
||||
import { getUserDetail } from '../../../api';
|
||||
import type { UserDetailResponse } from '../../../api/types';
|
||||
import type { UserDetailResponse } from '../../../api';
|
||||
import UserAvatar from '../../../components/UserAvatar';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
@@ -24,7 +24,7 @@ import { Link } from 'react-router';
|
||||
|
||||
import ShareImageDialog from '../../components/image/ShareImageDialog';
|
||||
import { uploadPicture } from '../../api/pictureApi';
|
||||
import type { PictureResponse } from '../../api/types';
|
||||
import type { PictureResponse } from '../../api';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
const { Dragger } = Upload;
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { Typography, Table, Card, Tag, Space, Button, Empty, message, Modal } from 'antd';
|
||||
import { SyncOutlined, EyeOutlined } from '@ant-design/icons';
|
||||
import { getUserTasks } from '../../api';
|
||||
import { type PictureProcessingTask, ProcessingStatus } from '../../api/types';
|
||||
import { type PictureProcessingTask, ProcessingStatus } from '../../api';
|
||||
import TaskProgressBar from '../../components/TaskProgressBar';
|
||||
import dayjs from 'dayjs';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
} from '@ant-design/icons';
|
||||
|
||||
import ImageGrid from '../../components/image/ImageGrid/ImageGrid';
|
||||
import type { PictureResponse } from '../../api/types';
|
||||
import type { PictureResponse } from '../../api';
|
||||
import { getFilteredTags } from '../../api/tagApi';
|
||||
import useIsMobile from '../../hooks/useIsMobile';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user