优化: 大文件拆分+代码分割+按需加载+console清理+any类型替换

- Money.tsx (2260行→54行): 拆分为 money/ 子目录4个组件, React.lazy二级分割
- AIAssistant.tsx (2038行→63行): 拆分为 ai-assistant/ 子目录6个组件, React.lazy二级分割
- xlsx改为动态导入, OvertimeTab从345KB降至12.7KB
- api-services.ts: 请求参数 any→Record<string,unknown>
- 移除前端3处console.log残留
- 后端console替换为pino logger
- 前后端未使用import/变量清理
- Zod schema验证: termination/platform/special-status/work-process
- 新增 leave.routes.ts, acceptance-test.routes.ts
- UI组件: PageGuide, QueryError, Stepper
This commit is contained in:
freedakgmail
2026-08-04 07:53:37 +08:00
parent 1da385cd5d
commit 2968484d2d
109 changed files with 8950 additions and 5926 deletions
@@ -24,6 +24,7 @@ const QUICK_PAGES: SearchResult[] = [
{ type: 'page', id: 'social', title: '社保公积金', link: '/social', icon: 'shield' },
{ type: 'page', id: 'termination', title: '离职管理', link: '/termination', icon: 'userX' },
{ type: 'page', id: 'attendance', title: '考勤排班', link: '/attendance', icon: 'calendar' },
{ type: 'page', id: 'leave-approval', title: '休假审批', link: '/leave-approval', icon: 'calendar' },
{ type: 'page', id: 'risk-center', title: '风险中心', link: '/risk-center', icon: 'alert' },
{ type: 'page', id: 'salary-dashboard', title: '薪酬分析', link: '/salary-dashboard', icon: 'chart' },
{ type: 'page', id: 'policies', title: '规章制度', link: '/policies', icon: 'file' },
+2 -2
View File
@@ -23,8 +23,8 @@ export default class ErrorBoundary extends Component<Props, State> {
return { hasError: true, error }
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('ErrorBoundary caught:', error, errorInfo)
componentDidCatch(_error: Error, _errorInfo: React.ErrorInfo) {
// 错误已通过 getDerivedStateFromError 捕获并展示降级 UI
}
handleReset = () => {
+37
View File
@@ -0,0 +1,37 @@
/**
* PageGuide 页面操作指导组件 — 折叠式,默认收起
* 统一用于各页面/Tab的操作说明,点击展开查看
*/
import { useState, ReactNode } from 'react'
import { ChevronRight, Lightbulb } from 'lucide-react'
interface PageGuideProps {
/** 指导标题,默认"操作说明" */
title?: string
/** 指导内容,支持字符串或自定义JSX */
children: ReactNode
/** 额外类名 */
className?: string
}
export default function PageGuide({ title = '操作说明', children, className = '' }: PageGuideProps) {
const [open, setOpen] = useState(false)
return (
<div className={`rounded-md border border-blue-100 bg-blue-50/50 ${className}`}>
<button
onClick={() => setOpen(!open)}
className="flex items-center gap-1.5 w-full px-3 py-1.5 text-xs font-medium text-blue-600 hover:text-blue-700 transition-colors"
>
<ChevronRight className={`w-3 h-3 transition-transform ${open ? 'rotate-90' : ''}`} />
<Lightbulb className="w-3.5 h-3.5" />
{title}
</button>
{open && (
<div className="px-3 pb-2.5 text-xs text-blue-600/80 leading-relaxed">
{children}
</div>
)}
</div>
)
}
+36
View File
@@ -0,0 +1,36 @@
import { AlertCircle, RefreshCw } from 'lucide-react'
interface QueryErrorProps {
error?: unknown
onRetry?: () => void
message?: string
}
/**
* 查询错误状态组件
* 在 useQuery 的 isError 状态下显示错误提示和重试按钮
*/
export default function QueryError({ error, onRetry, message }: QueryErrorProps) {
const errMsg = message
|| (error as any)?.response?.data?.error?.message
|| (error as any)?.message
|| '数据加载失败,请稍后重试'
return (
<div className="flex flex-col items-center justify-center py-12 px-4 text-center">
<div className="w-12 h-12 rounded-full bg-red-50 flex items-center justify-center mb-3">
<AlertCircle className="w-6 h-6 text-red-500" />
</div>
<p className="text-sm text-gray-600 mb-3 max-w-md">{errMsg}</p>
{onRetry && (
<button
onClick={onRetry}
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-primary bg-primary/5 rounded-lg hover:bg-primary/10 transition-colors"
>
<RefreshCw className="w-3.5 h-3.5" />
</button>
)}
</div>
)
}
-2
View File
@@ -2,7 +2,6 @@
* Stepper 步骤条组件 — 用于多步骤工作流引导
* 支持横向/纵向布局、可点击步骤导航、完成/当前/待办状态
*/
import { ReactNode } from 'react'
import clsx from 'clsx'
import { Check } from 'lucide-react'
@@ -24,7 +23,6 @@ interface StepperProps {
* Stepper 步骤条 — 展示工作流进度和步骤导航
*/
export function Stepper({ steps, orientation = 'horizontal', onStepClick, className }: StepperProps) {
const currentIndex = steps.findIndex(s => s.status === 'current')
if (orientation === 'vertical') {
return (