优化: 大文件拆分+代码分割+按需加载+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
@@ -44,9 +44,9 @@ export default function AcceptanceTestModal({ open, onClose }: { open: boolean;
<X className="w-4 h-4" />
</button>
</div>
{/* iframe 加载验收测试页面 */}
{/* iframe 加载验收测试页面 — 带版本参数防止缓存 */}
<iframe
src="/acceptance-test.html"
src={`/acceptance-test.html?v=${__APP_VERSION__}`}
className="w-full"
style={{ height: 'calc(95vh - 42px)', border: 'none' }}
title="验收测试清单"
-59
View File
@@ -1,59 +0,0 @@
import { Component, ErrorInfo, ReactNode } from 'react'
interface Props {
children: ReactNode
}
interface State {
hasError: boolean
error: Error | null
}
export default class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = { hasError: false, error: null }
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error }
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('ErrorBoundary caught:', error, errorInfo)
}
handleReset = () => {
this.setState({ hasError: false, error: null })
}
render() {
if (this.state.hasError) {
return (
<div className="min-h-[60vh] flex flex-col items-center justify-center gap-4 px-4">
<div className="text-6xl">😵</div>
<h2 className="text-xl font-semibold text-gray-800"></h2>
<p className="text-sm text-gray-500 text-center max-w-md">
{this.state.error?.message || '发生了未知错误,请刷新页面重试'}
</p>
<div className="flex gap-3">
<button
onClick={this.handleReset}
className="px-4 py-2 text-sm font-medium text-gray-600 bg-gray-100 rounded-lg hover:bg-gray-200 transition-colors"
>
</button>
<button
onClick={() => window.location.reload()}
className="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition-colors"
>
</button>
</div>
</div>
)
}
return this.props.children
}
}
+2 -2
View File
@@ -1,7 +1,7 @@
import { useState, useEffect, useRef } from 'react'
import { HelpCircle, Search, ChevronDown, ChevronRight, Sparkles,
Home, Users, FileText, Calculator, Shield, UserX, Bot,
Bell, Settings, Lightbulb, AlertTriangle, CheckCircle, Phone } from 'lucide-react'
Home, Users, FileText, Calculator, Bot,
Settings, Lightbulb, AlertTriangle, CheckCircle, Phone } from 'lucide-react'
import Modal from './ui/Modal'
import { aiApi } from '../lib/api-services'
import clsx from 'clsx'
+1 -1
View File
@@ -1,5 +1,5 @@
import { useState, useMemo, useEffect, useCallback } from 'react'
import { Star, ChevronDown, ChevronRight, Check, Search, ClipboardList, Send, Save, RotateCcw } from 'lucide-react'
import { Star, ChevronDown, ChevronRight, Search, ClipboardList, Send, Save, RotateCcw } from 'lucide-react'
import Modal from './ui/Modal'
import { toast } from 'sonner'
import { surveyApi } from '../lib/api-services'
@@ -17,6 +17,7 @@ const ROUTE_MAP: Record<string, BreadcrumbItem> = {
'/roster': { group: '员工管理', label: '花名册' },
'/work-process': { group: '员工管理', label: '用工办理' },
'/attendance': { group: '员工管理', label: '考勤确认' },
'/leave-approval': { group: '员工管理', label: '休假审批' },
'/termination': { group: '员工管理', label: '解聘补偿' },
'/special-status': { group: '员工管理', label: '特殊状态' },
'/money': { group: '薪税社保', label: '薪税管理' },
@@ -4,7 +4,7 @@
*/
import { Link, useLocation, useNavigate } from 'react-router-dom'
import { DollarSign, FileText, ScrollText, LogOut, CalendarCheck, Home, UserX, ClipboardList } from 'lucide-react'
import { DollarSign, FileText, ScrollText, LogOut, CalendarCheck, Home } from 'lucide-react'
import Logo from '../../components/ui/Logo'
const tabItems = [
@@ -13,7 +13,7 @@ import {
Bot, BookMarked,
Bell, ScrollText, Settings,
ChevronDown, ChevronRight,
Building2, CalendarDays, ClipboardList, Heart,
Building2, CalendarDays, ClipboardList, Heart, CalendarClock,
} from 'lucide-react'
import Logo from '../ui/Logo'
@@ -33,7 +33,7 @@ const navGroups: NavGroup[] = [
title: '首页',
items: [
{ path: '/', label: '工作台', icon: LayoutDashboard },
{ path: '/calendar', label: '工作日历', icon: CalendarDays },
{ path: '/calendar', label: '日历', icon: CalendarDays },
],
},
{
@@ -57,6 +57,7 @@ const navGroups: NavGroup[] = [
title: '时间',
items: [
{ path: '/attendance', label: '考勤排班', icon: CalendarCheck },
{ path: '/leave-approval', label: '休假审批', icon: CalendarClock },
],
},
{
@@ -66,14 +67,14 @@ const navGroups: NavGroup[] = [
{ path: '/evidence', label: '证据链', icon: FileSearch },
{ path: '/policies', label: '规章制度', icon: FileText },
{ path: '/tools/health-check', label: '用工体检', icon: Stethoscope },
{ path: '/tools/medical-period', label: '医疗期计算', icon: HeartPulse },
{ path: '/tools/medical-period', label: '医疗期', icon: HeartPulse },
{ path: '/tools/annual-value', label: '年度价值', icon: Award },
],
},
{
title: '更多',
items: [
{ path: '/ai-assistant', label: 'AI 顾问', icon: Bot },
{ path: '/ai-assistant', label: 'AI顾问', icon: Bot },
{ path: '/templates', label: '文本模板', icon: BookMarked },
{ path: '/notifications', label: '通知管理', icon: Bell },
{ path: '/audit', label: '操作日志', icon: ScrollText },
@@ -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 (