chore: remove unused hooks and utils

This commit is contained in:
LTbinglingfeng
2026-05-21 01:29:27 +08:00
parent eab19952e0
commit 0fa6b741b2
8 changed files with 0 additions and 364 deletions
-3
View File
@@ -2,10 +2,7 @@
* Hooks 统一导出
*/
export { useApi } from './useApi';
export { useDebounce } from './useDebounce';
export { useLocalStorage } from './useLocalStorage';
export { useInterval } from './useInterval';
export { useMediaQuery } from './useMediaQuery';
export { usePagination } from './usePagination';
export { useHeaderRefresh } from './useHeaderRefresh';
-66
View File
@@ -1,66 +0,0 @@
/**
* 通用 API 调用 Hook
*/
import { useState, useCallback } from 'react';
import { useNotificationStore } from '@/stores';
interface UseApiOptions<T> {
onSuccess?: (data: T) => void;
onError?: (error: Error) => void;
showSuccessNotification?: boolean;
showErrorNotification?: boolean;
successMessage?: string;
}
export function useApi<T = unknown, Args extends unknown[] = unknown[]>(
apiFunction: (...args: Args) => Promise<T>,
options: UseApiOptions<T> = {}
) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const { showNotification } = useNotificationStore();
const execute = useCallback(
async (...args: Args) => {
setLoading(true);
setError(null);
try {
const result = await apiFunction(...args);
setData(result);
if (options.showSuccessNotification && options.successMessage) {
showNotification(options.successMessage, 'success');
}
options.onSuccess?.(result);
return result;
} catch (err: unknown) {
const errorObj =
err instanceof Error ? err : new Error(typeof err === 'string' ? err : 'Unknown error');
setError(errorObj);
if (options.showErrorNotification !== false) {
showNotification(errorObj.message, 'error');
}
options.onError?.(errorObj);
throw errorObj;
} finally {
setLoading(false);
}
},
[apiFunction, options, showNotification]
);
const reset = useCallback(() => {
setData(null);
setError(null);
setLoading(false);
}, []);
return { data, loading, error, execute, reset };
}
-21
View File
@@ -1,21 +0,0 @@
/**
* 防抖 Hook
*/
import { useEffect, useState } from 'react';
export function useDebounce<T>(value: T, delay: number = 500): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
-59
View File
@@ -1,59 +0,0 @@
/**
* 分页 Hook
*/
import { useState, useMemo } from 'react';
import type { PaginationState } from '@/types';
export function usePagination<T>(
items: T[],
initialPageSize: number = 20
): PaginationState & {
currentItems: T[];
goToPage: (page: number) => void;
nextPage: () => void;
prevPage: () => void;
setPageSize: (size: number) => void;
} {
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(initialPageSize);
const totalItems = items.length;
const totalPages = Math.ceil(totalItems / pageSize) || 1;
const currentItems = useMemo(() => {
const start = (currentPage - 1) * pageSize;
const end = start + pageSize;
return items.slice(start, end);
}, [items, currentPage, pageSize]);
const goToPage = (page: number) => {
const validPage = Math.max(1, Math.min(page, totalPages));
setCurrentPage(validPage);
};
const nextPage = () => {
goToPage(currentPage + 1);
};
const prevPage = () => {
goToPage(currentPage - 1);
};
const handleSetPageSize = (size: number) => {
setPageSize(size);
setCurrentPage(1); // 重置到第一页
};
return {
currentPage,
pageSize,
totalPages,
totalItems,
currentItems,
goToPage,
nextPage,
prevPage,
setPageSize: handleSetPageSize
};
}
-38
View File
@@ -18,26 +18,17 @@ export const MANAGEMENT_API_PREFIX = '/v0/management';
export const REQUEST_TIMEOUT_MS = 30 * 1000;
export const VERSION_HEADER_KEYS = ['x-cpa-version', 'x-server-version'];
export const BUILD_DATE_HEADER_KEYS = ['x-cpa-build-date', 'x-server-build-date'];
export const STATUS_UPDATE_INTERVAL_MS = 1000;
export const LOG_REFRESH_DELAY_MS = 500;
// 日志相关
export const MAX_LOG_LINES = 2000;
export const LOG_FETCH_LIMIT = 2500;
export const LOGS_TIMEOUT_MS = 60 * 1000;
// 认证文件分页
export const DEFAULT_AUTH_FILES_PAGE_SIZE = 20;
export const MIN_AUTH_FILES_PAGE_SIZE = 10;
export const MAX_AUTH_FILES_PAGE_SIZE = 100;
export const MAX_AUTH_FILE_SIZE = 10 * 1024 * 1024;
// 本地存储键名
export const STORAGE_KEY_AUTH = 'cli-proxy-auth';
export const STORAGE_KEY_THEME = 'cli-proxy-theme';
export const STORAGE_KEY_LANGUAGE = 'cli-proxy-language';
export const STORAGE_KEY_SIDEBAR = 'cli-proxy-sidebar-collapsed';
export const STORAGE_KEY_AUTH_FILES_PAGE_SIZE = 'cli-proxy-auth-files-page-size';
// 语言配置
export const LANGUAGE_ORDER = defineLanguageOrder(['zh-CN', 'zh-TW', 'en', 'ru'] as const);
@@ -51,32 +42,3 @@ export const SUPPORTED_LANGUAGES = LANGUAGE_ORDER;
// 通知持续时间
export const NOTIFICATION_DURATION_MS = 3000;
// OAuth 卡片 ID 列表
export const OAUTH_CARD_IDS = [
'codex-oauth-card',
'anthropic-oauth-card',
'antigravity-oauth-card',
'gemini-cli-oauth-card',
'kimi-oauth-card',
'xai-oauth-card'
];
export const OAUTH_PROVIDERS = {
CODEX: 'codex',
ANTHROPIC: 'anthropic',
ANTIGRAVITY: 'antigravity',
GEMINI_CLI: 'gemini-cli',
KIMI: 'kimi',
XAI: 'xai'
} as const;
// API 端点
export const API_ENDPOINTS = {
CONFIG: '/config',
LOGIN: '/login',
API_KEYS: '/api-keys',
PROVIDERS: '/providers',
AUTH_FILES: '/auth-files',
OAUTH: '/oauth',
LOGS: '/logs'
} as const;
-47
View File
@@ -5,14 +5,6 @@ import { parseTimestamp } from './timestamp';
* 从原项目 src/utils/string.js 迁移
*/
const resolveDefaultLocale = (): string | undefined => {
const fromDocument =
typeof document !== 'undefined' ? document.documentElement?.lang?.trim() : '';
if (fromDocument) return fromDocument;
const fromNavigator = typeof navigator !== 'undefined' ? navigator.language?.trim() : '';
return fromNavigator || undefined;
};
/**
* 隐藏 API Key 中间部分,仅保留前后两位
*/
@@ -45,27 +37,6 @@ export function formatFileSize(bytes: number): string {
return `${(bytes / Math.pow(k, i)).toFixed(2)} ${units[i]}`;
}
/**
* 格式化日期时间
*/
export function formatDateTime(date: string | Date, locale?: string): string {
const d = typeof date === 'string' ? parseTimestamp(date) ?? new Date(date) : date;
if (isNaN(d.getTime())) {
return 'Invalid Date';
}
const resolvedLocale = locale?.trim() || resolveDefaultLocale();
return d.toLocaleString(resolvedLocale, {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
}
/**
* 将 Unix 时间戳(秒/毫秒/微秒/纳秒)格式化为本地时间字符串
*/
@@ -96,21 +67,3 @@ export function formatUnixTimestamp(value: unknown, locale?: string): string {
if (Number.isNaN(date.getTime())) return '';
return locale ? date.toLocaleString(locale) : date.toLocaleString();
}
/**
* 格式化数字(添加千位分隔符)
*/
export function formatNumber(num: number, locale?: string): string {
const resolvedLocale = locale?.trim() || resolveDefaultLocale();
return num.toLocaleString(resolvedLocale);
}
/**
* 截断长文本
*/
export function truncateText(text: string, maxLength: number): string {
if (text.length <= maxLength) {
return text;
}
return text.slice(0, maxLength) + '...';
}
-77
View File
@@ -3,86 +3,9 @@
* 从原项目 src/utils/array.js, dom.js, html.js 迁移
*/
/**
* 规范化数组响应(处理后端可能返回非数组的情况)
*/
export function normalizeArrayResponse<T>(data: T | T[] | null | undefined): T[] {
if (!data) return [];
if (Array.isArray(data)) return data;
return [data];
}
/**
* 防抖函数
*/
export function debounce<This, Args extends unknown[], Return>(
func: (this: This, ...args: Args) => Return,
delay: number
): (this: This, ...args: Args) => void {
let timeoutId: ReturnType<typeof setTimeout>;
return function (this: This, ...args: Args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
/**
* 节流函数
*/
export function throttle<This, Args extends unknown[], Return>(
func: (this: This, ...args: Args) => Return,
limit: number
): (this: This, ...args: Args) => void {
let inThrottle: boolean;
return function (this: This, ...args: Args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}
/**
* HTML 转义(防 XSS
*/
export function escapeHtml(text: string): string {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
/**
* 生成唯一 ID
*/
export function generateId(): string {
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
/**
* 深拷贝对象
*/
export function deepClone<T>(obj: T): T {
if (obj === null || typeof obj !== 'object') return obj;
if (obj instanceof Date) return new Date(obj.getTime()) as unknown as T;
if (Array.isArray(obj)) return obj.map((item) => deepClone(item)) as unknown as T;
const source = obj as Record<string, unknown>;
const cloned: Record<string, unknown> = {};
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
cloned[key] = deepClone(source[key]);
}
}
return cloned as unknown as T;
}
/**
* 延迟函数
*/
export function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
-53
View File
@@ -2,39 +2,6 @@
* 验证工具函数
*/
/**
* 验证 URL 格式
*/
export function isValidUrl(url: string): boolean {
try {
new URL(url);
return true;
} catch {
return false;
}
}
/**
* 验证 API Base URL
*/
export function isValidApiBase(apiBase: string): boolean {
if (!apiBase) return false;
// 允许 http/https 协议
const urlPattern = /^https?:\/\/.+/i;
return urlPattern.test(apiBase);
}
/**
* 验证 API Key 格式
*/
export function isValidApiKey(key: string): boolean {
if (!key || key.length < 8) return false;
// 基础验证:不包含空格
return !/\s/.test(key);
}
/**
* 验证 API Key 字符集(仅允许 ASCII 可见字符)
*/
@@ -42,23 +9,3 @@ export function isValidApiKeyCharset(key: string): boolean {
if (!key) return false;
return /^[\x21-\x7E]+$/.test(key);
}
/**
* 验证 JSON 格式
*/
export function isValidJson(str: string): boolean {
try {
JSON.parse(str);
return true;
} catch {
return false;
}
}
/**
* 验证 Email 格式
*/
export function isValidEmail(email: string): boolean {
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailPattern.test(email);
}