fix: resolve all ESLint warnings for clean lint output #128

Merged
xiaoxia merged 1 commits from fix/eslint-errors into develop 2026-06-30 18:06:41 +08:00
28 changed files with 92 additions and 90 deletions
+2 -2
View File
@@ -43,8 +43,8 @@ export default function CreateIssueForm({ taskId, projectId, workspaceId, onSucc
}
onSuccess();
} catch (err: any) {
setError(err.message);
} catch (err: unknown) {
setError(err instanceof Error ? err.message : String(err));
} finally {
setLoading(false);
}
+2 -2
View File
@@ -50,8 +50,8 @@ export default function CreateTaskForm({ projectId, workspaceId, onSuccess, onCa
} else {
router.push('/projects');
}
} catch (err: any) {
setError(err.message);
} catch (err: unknown) {
setError(err instanceof Error ? err.message : String(err));
} finally {
setLoading(false);
}
+3 -5
View File
@@ -1,7 +1,6 @@
'use client';
import { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';
@@ -18,7 +17,6 @@ interface EditTaskFormProps {
}
export default function EditTaskForm({ taskId, initialData, onSuccess, onCancel }: EditTaskFormProps) {
const router = useRouter();
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const [formData, setFormData] = useState(initialData);
@@ -41,8 +39,8 @@ export default function EditTaskForm({ taskId, initialData, onSuccess, onCancel
}
if (onSuccess) onSuccess();
} catch (err: any) {
setError(err.message);
} catch (err: unknown) {
setError(err instanceof Error ? err.message : String(err));
} finally {
setLoading(false);
}
+1
View File
@@ -1,6 +1,7 @@
import type { Metadata } from "next";
import "./globals.css";
// eslint-disable-next-line react-refresh/only-export-components
export const metadata: Metadata = {
title: "小虾 SaaS - 项目推进器",
description: "AI 视频自动化剪辑系统 - 项目管理",
+4 -4
View File
@@ -36,8 +36,8 @@ export default function MilestonesPage() {
if (!res.ok) throw new Error('获取里程碑列表失败');
const data = await res.json();
setMilestones(data);
} catch (err: any) {
setError(err.message);
} catch (err: unknown) {
setError(err instanceof Error ? err.message : String(err));
} finally {
setLoading(false);
}
@@ -59,8 +59,8 @@ export default function MilestonesPage() {
setFormData({ name: '', description: '' });
setShowCreateForm(false);
fetchMilestones();
} catch (err: any) {
alert(err.message);
} catch (err: unknown) {
alert(err instanceof Error ? err.message : String(err));
}
};
+2 -2
View File
@@ -38,8 +38,8 @@ export default function ProjectsPage() {
if (!res.ok) throw new Error('获取任务列表失败');
const data = await res.json();
setTasks(data);
} catch (err: any) {
setError(err.message);
} catch (err: unknown) {
setError(err instanceof Error ? err.message : String(err));
} finally {
setLoading(false);
}
+19 -20
View File
@@ -1,8 +1,8 @@
'use client';
import { useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import Link from 'next/link';
import { useParams, useRouter } from 'next/navigation';
import { useParams } from 'next/navigation';
import CreateIssueForm from '../../components/CreateIssueForm';
import EditTaskForm from '../../components/EditTaskForm';
@@ -39,7 +39,6 @@ const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';
export default function TaskDetailPage() {
const params = useParams();
const router = useRouter();
const taskId = params.id as string;
const [task, setTask] = useState<Task | null>(null);
@@ -50,12 +49,7 @@ export default function TaskDetailPage() {
const [showIssueForm, setShowIssueForm] = useState(false);
const [showEditForm, setShowEditForm] = useState(false);
useEffect(() => {
fetchTaskDetail();
fetchTaskIssues();
}, [taskId]);
const fetchTaskDetail = async () => {
const fetchTaskDetail = useCallback(async () => {
try {
const res = await fetch(`${API_BASE}/api/v1/project-management/tasks/${taskId}`);
if (!res.ok) {
@@ -66,14 +60,14 @@ export default function TaskDetailPage() {
}
const data = await res.json();
setTask(data);
} catch (err: any) {
setError(err.message);
} catch (err: unknown) {
setError(err instanceof Error ? err.message : String(err));
} finally {
setLoading(false);
}
};
}, [taskId]);
const fetchTaskIssues = async () => {
const fetchTaskIssues = useCallback(async () => {
try {
const res = await fetch(`${API_BASE}/api/v1/project-management/issues?task_id=${taskId}`);
if (res.ok) {
@@ -83,7 +77,12 @@ export default function TaskDetailPage() {
} catch (err) {
console.error('获取问题列表失败:', err);
}
};
}, [taskId]);
useEffect(() => {
fetchTaskDetail();
fetchTaskIssues();
}, [fetchTaskDetail, fetchTaskIssues]);
const resolveIssue = async (issueId: string) => {
setUpdating(true);
@@ -93,8 +92,8 @@ export default function TaskDetailPage() {
});
if (!res.ok) throw new Error('解决问题失败');
await fetchTaskIssues();
} catch (err: any) {
alert(err.message);
} catch (err: unknown) {
alert(err instanceof Error ? err.message : String(err));
} finally {
setUpdating(false);
}
@@ -110,8 +109,8 @@ export default function TaskDetailPage() {
});
if (!res.ok) throw new Error('更新状态失败');
await fetchTaskDetail();
} catch (err: any) {
alert(err.message);
} catch (err: unknown) {
alert(err instanceof Error ? err.message : String(err));
} finally {
setUpdating(false);
}
@@ -127,8 +126,8 @@ export default function TaskDetailPage() {
});
if (!res.ok) throw new Error('更新进度失败');
await fetchTaskDetail();
} catch (err: any) {
alert(err.message);
} catch (err: unknown) {
alert(err instanceof Error ? err.message : String(err));
} finally {
setUpdating(false);
}
+1
View File
@@ -72,6 +72,7 @@ apiClient.interceptors.response.use(
// 标记已展示过提示,组件 onError 可据此跳过重复 toast
if (handled) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(error as any).__msgShown = true;
}
+4 -4
View File
@@ -102,7 +102,7 @@ const AssetLibrary: React.FC = () => {
setNewLibName('');
queryClient.invalidateQueries({ queryKey: ['asset-libraries'] });
},
onError: (err: any) => { if (!err?.__msgShown) message.error('创建失败') },
onError: (err: unknown) => { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('创建失败') },
});
// 上传素材(小文件表单上传)
@@ -113,7 +113,7 @@ const AssetLibrary: React.FC = () => {
queryClient.invalidateQueries({ queryKey: ['assets', activeLibrary] });
queryClient.invalidateQueries({ queryKey: ['asset-libraries'] });
},
onError: (err: any) => { if (!err?.__msgShown) message.error('上传失败') },
onError: (err: unknown) => { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('上传失败') },
});
// 大文件直传(OSS 预签名)
@@ -124,7 +124,7 @@ const AssetLibrary: React.FC = () => {
queryClient.invalidateQueries({ queryKey: ['assets', activeLibrary] });
queryClient.invalidateQueries({ queryKey: ['asset-libraries'] });
},
onError: (err: any) => { if (!err?.__msgShown) message.error('上传失败') },
onError: (err: unknown) => { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('上传失败') },
});
// 删除素材
@@ -135,7 +135,7 @@ const AssetLibrary: React.FC = () => {
queryClient.invalidateQueries({ queryKey: ['assets', activeLibrary] });
queryClient.invalidateQueries({ queryKey: ['asset-libraries'] });
},
onError: (err: any) => { if (!err?.__msgShown) message.error('删除失败') },
onError: (err: unknown) => { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('删除失败') },
});
/** 处理上传 */
+2 -2
View File
@@ -19,8 +19,8 @@ const ForgotPassword: React.FC = () => {
setEmailSent(true);
message.success('重置邮件已发送!');
},
onError: (error: any) => {
if (!error?.__msgShown) message.error(error.response?.data?.message || '发送失败,请重试');
onError: (error: unknown) => {
if (!(error as { __msgShown?: boolean })?.__msgShown) message.error('发送失败,请重试');
},
});
+2 -2
View File
@@ -26,8 +26,8 @@ const Login: React.FC = () => {
});
message.success('登录成功!');
navigate('/');
} catch (error: any) {
if (!error?.__msgShown) message.error(error.response?.data?.message || '登录失败,请检查邮箱和密码');
} catch (error: unknown) {
if (!(error as { __msgShown?: boolean })?.__msgShown) message.error('登录失败,请检查邮箱和密码');
}
};
+2 -2
View File
@@ -28,8 +28,8 @@ const Register: React.FC = () => {
display_name: values.username,
});
message.success('注册成功!请查收验证邮件。');
} catch (error: any) {
if (!error?.__msgShown) message.error(error.response?.data?.message || '注册失败,请重试');
} catch (error: unknown) {
if (!(error as { __msgShown?: boolean })?.__msgShown) message.error('注册失败,请重试');
}
};
+2 -2
View File
@@ -21,8 +21,8 @@ const ResetPassword: React.FC = () => {
message.success('密码重置成功!');
setTimeout(() => navigate('/login'), 2000);
},
onError: (error: any) => {
if (!error?.__msgShown) message.error(error.response?.data?.message || '重置失败,请重试');
onError: (error: unknown) => {
if (!(error as { __msgShown?: boolean })?.__msgShown) message.error('重置失败,请重试');
},
});
@@ -105,7 +105,7 @@ const DuplicationResults: React.FC = () => {
message.success('已删除');
queryClient.invalidateQueries({ queryKey: ['duplication-records'] });
},
onError: (err: any) => { if (!err?.__msgShown) message.error('删除失败') },
onError: (err: unknown) => { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('删除失败') },
});
// 重新查重
@@ -115,7 +115,7 @@ const DuplicationResults: React.FC = () => {
message.success('已重新提交查重');
queryClient.invalidateQueries({ queryKey: ['duplication-records'] });
},
onError: (err: any) => { if (!err?.__msgShown) message.error('重新查重失败') },
onError: (err: unknown) => { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('重新查重失败') },
});
/** 批量删除 */
@@ -53,9 +53,9 @@ const DuplicationUpload: React.FC = () => {
});
message.success('查重任务已提交');
},
onError: (err: any) => {
onError: (err: unknown) => {
setUploading(false);
if (!err?.__msgShown) message.error('上传失败,请重试');
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('上传失败,请重试');
},
});
@@ -147,20 +147,20 @@ const EditingPlanner: React.FC = () => {
queryClient.invalidateQueries({ queryKey: ['editing-templates'] });
setSaveModalOpen(false);
},
onError: (err: any) => {
if (!err?.__msgShown) message.error('保存失败');
onError: (err: unknown) => {
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('保存失败');
},
});
const updateMutation = useMutation({
mutationFn: ({ id, data }: { id: string; data: any }) => updateEditingTemplate(id, data),
mutationFn: ({ id, data }: { id: string; data: Record<string, unknown> }) => updateEditingTemplate(id, data),
onSuccess: () => {
message.success('模板已更新');
queryClient.invalidateQueries({ queryKey: ['editing-templates'] });
setSaveModalOpen(false);
},
onError: (err: any) => {
if (!err?.__msgShown) message.error('保存失败');
onError: (err: unknown) => {
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('保存失败');
},
});
@@ -172,8 +172,8 @@ const EditingPlanner: React.FC = () => {
message.success(msg);
setGenerateModalOpen(false);
},
onError: (err: any) => {
if (!err?.__msgShown) message.error('生成失败');
onError: (err: unknown) => {
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('生成失败');
},
});
+2 -2
View File
@@ -94,8 +94,8 @@ const GeneratePage: React.FC = () => {
setGenerated(true);
setGenerating(false);
},
onError: (err: any) => {
if (!err?.__msgShown) message.error('生成失败');
onError: (err: unknown) => {
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('生成失败');
setGenerating(false);
},
});
+1 -1
View File
@@ -61,7 +61,7 @@ const TaskHistory: React.FC = () => {
message.success('任务已重新提交');
queryClient.invalidateQueries({ queryKey: ['user-tasks'] });
},
onError: (err: any) => { if (!err?.__msgShown) message.error('重试失败') },
onError: (err: unknown) => { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('重试失败') },
});
/** 过滤后的任务 */
@@ -74,8 +74,8 @@ const MyTemplates: React.FC = () => {
message.success('模板已删除');
queryClient.invalidateQueries({ queryKey: ['editing-templates'] });
},
onError: (err: any) => {
if (!err?.__msgShown) message.error('删除失败');
onError: (err: unknown) => {
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('删除失败');
},
});
@@ -96,8 +96,8 @@ const MyTemplates: React.FC = () => {
message.success('模板已复制');
queryClient.invalidateQueries({ queryKey: ['editing-templates'] });
},
onError: (err: any) => {
if (!err?.__msgShown) message.error('复制失败');
onError: (err: unknown) => {
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('复制失败');
},
});
@@ -64,7 +64,7 @@ const ProductLibrary: React.FC = () => {
message.success('已删除');
queryClient.invalidateQueries({ queryKey: ['products'] });
},
onError: (err: any) => { if (!err?.__msgShown) message.error('删除失败') },
onError: (err: unknown) => { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('删除失败') },
});
// 下载
@@ -72,8 +72,8 @@ const ProductLibrary: React.FC = () => {
try {
const { url } = await getProductDownloadUrl(productId);
window.open(url, '_blank');
} catch (err: any) {
if (!err?.__msgShown) message.error('获取下载链接失败');
} catch (err: unknown) {
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('获取下载链接失败');
}
};
+4 -4
View File
@@ -29,8 +29,8 @@ const Billing: React.FC = () => {
const data = await getCurrentSubscription();
setSubscription(data);
setAutoRenewChecked(data.auto_renew);
} catch (err: any) {
if (!err?.__msgShown) message.error('加载订阅数据失败');
} catch (err: unknown) {
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('加载订阅数据失败');
} finally {
setLoading(false);
}
@@ -45,8 +45,8 @@ const Billing: React.FC = () => {
if (subscription) {
setSubscription({ ...subscription, auto_renew: checked });
}
} catch (err: any) {
if (!err?.__msgShown) message.error('操作失败');
} catch (err: unknown) {
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('操作失败');
} finally {
setAutoRenewLoading(false);
}
+1 -1
View File
@@ -72,7 +72,7 @@ const Plans: React.FC = () => {
try {
setSubscribing(true);
message.success('订阅成功');
} catch (error: any) {
} catch (error: unknown) {
message.error('订阅失败');
} finally {
setSubscribing(false);
@@ -32,8 +32,8 @@ const UpgradeSubscription: React.FC = () => {
const data = await getCurrentSubscription();
setSubscription(data);
setSelectedPlan(data.plan_id);
} catch (err: any) {
if (!err?.__msgShown) message.error('获取订阅信息失败');
} catch (err: unknown) {
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('获取订阅信息失败');
} finally {
setLoading(false);
}
@@ -64,8 +64,8 @@ const UpgradeSubscription: React.FC = () => {
} else {
message.error(res.message);
}
} catch (err: any) {
if (!err?.__msgShown) message.error('套餐变更失败,请重试');
} catch (err: unknown) {
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('套餐变更失败,请重试');
} finally {
setSubmitting(false);
}
@@ -80,8 +80,8 @@ const UpgradeSubscription: React.FC = () => {
if (subscription) {
setSubscription({ ...subscription, auto_renew: enabled });
}
} catch (err: any) {
if (!err?.__msgShown) message.error('操作失败');
} catch (err: unknown) {
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('操作失败');
}
};
@@ -97,8 +97,8 @@ const UpgradeSubscription: React.FC = () => {
const res = await cancelSubscription();
message.success(res.message);
navigate('/subscription');
} catch (err: any) {
if (!err?.__msgShown) message.error('取消失败');
} catch (err: unknown) {
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('取消失败');
}
},
});
@@ -55,7 +55,7 @@ const TemplateLibrary: React.FC = () => {
message.success(data.is_favorite ? '已收藏' : '已取消收藏');
queryClient.invalidateQueries({ queryKey: ['templates'] });
},
onError: (err: any) => { if (!err?.__msgShown) message.error('操作失败') },
onError: (err: unknown) => { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('操作失败') },
});
/** 提取所有分类 */
+4 -4
View File
@@ -62,7 +62,7 @@ const TitleLibrary: React.FC = () => {
resetForm();
queryClient.invalidateQueries({ queryKey: ['titles'] });
},
onError: (err: any) => { if (!err?.__msgShown) message.error('创建失败') },
onError: (err: unknown) => { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('创建失败') },
});
// 更新标题
@@ -75,7 +75,7 @@ const TitleLibrary: React.FC = () => {
resetForm();
queryClient.invalidateQueries({ queryKey: ['titles'] });
},
onError: (err: any) => { if (!err?.__msgShown) message.error('更新失败') },
onError: (err: unknown) => { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('更新失败') },
});
// 删除标题
@@ -85,7 +85,7 @@ const TitleLibrary: React.FC = () => {
message.success('已删除');
queryClient.invalidateQueries({ queryKey: ['titles'] });
},
onError: (err: any) => { if (!err?.__msgShown) message.error('删除失败') },
onError: (err: unknown) => { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('删除失败') },
});
// 批量导入
@@ -98,7 +98,7 @@ const TitleLibrary: React.FC = () => {
setImportText('');
queryClient.invalidateQueries({ queryKey: ['titles'] });
},
onError: (err: any) => { if (!err?.__msgShown) message.error('导入失败') },
onError: (err: unknown) => { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('导入失败') },
});
const resetForm = () => {
+4 -4
View File
@@ -80,7 +80,7 @@ const VoiceLibrary: React.FC = () => {
resetForm();
queryClient.invalidateQueries({ queryKey: ['voices'] });
},
onError: (err: any) => { if (!err?.__msgShown) message.error('创建失败') },
onError: (err: unknown) => { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('创建失败') },
});
// 更新配音
@@ -93,7 +93,7 @@ const VoiceLibrary: React.FC = () => {
resetForm();
queryClient.invalidateQueries({ queryKey: ['voices'] });
},
onError: (err: any) => { if (!err?.__msgShown) message.error('更新失败') },
onError: (err: unknown) => { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('更新失败') },
});
// 删除配音
@@ -103,7 +103,7 @@ const VoiceLibrary: React.FC = () => {
message.success('已删除');
queryClient.invalidateQueries({ queryKey: ['voices'] });
},
onError: (err: any) => { if (!err?.__msgShown) message.error('删除失败') },
onError: (err: unknown) => { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('删除失败') },
});
// AI 生成配音
@@ -115,7 +115,7 @@ const VoiceLibrary: React.FC = () => {
setAiText('');
queryClient.invalidateQueries({ queryKey: ['voices'] });
},
onError: (err: any) => { if (!err?.__msgShown) message.error('AI 生成失败') },
onError: (err: unknown) => { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error('AI 生成失败') },
});
const resetForm = () => {
+1
View File
@@ -12,6 +12,7 @@ import ResetPassword from '@/pages/auth/ResetPassword';
import { useAuthStore } from '@/store/authStore';
/** 受保护的路由组件 */
// eslint-disable-next-line react-refresh/only-export-components
const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
const hasAccessToken = Boolean(localStorage.getItem('access_token'));
+2
View File
@@ -17,7 +17,9 @@ afterEach(() => {
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Vi {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
interface Assertion<T = any> extends jest.Matchers<void, T> {}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
interface AsymmetricMatchersContaining extends jest.Matchers<void, any> {}
}
}