优化: 大文件拆分+代码分割+按需加载+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
+123 -28
View File
@@ -1,6 +1,6 @@
import { useState, useEffect } from 'react'
import { useNavigate, Link } from 'react-router-dom'
import { Eye, EyeOff } from 'lucide-react'
import { Eye, EyeOff, Shield, Zap, Users, FileText, ArrowRight } from 'lucide-react'
import Logo from '../../components/ui/Logo'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
@@ -67,27 +67,99 @@ export default function Login() {
}
}
return (
<div className="min-h-screen flex items-center justify-center bg-surface px-4">
<div className="w-full max-w-sm">
<div className="flex items-center justify-center gap-2 mb-8">
<Logo className="w-8 h-8 text-primary" />
<span className="text-xl font-bold"></span>
</div>
const features = [
{ icon: Users, title: '智能花名册', desc: '员工档案全生命周期管理' },
{ icon: FileText, title: '合同自动化', desc: '电子签约 + 到期预警' },
{ icon: Shield, title: '合规风控', desc: 'AI 驱动的用工风险检测' },
{ icon: Zap, title: '薪税一键算', desc: '工资社保个税自动计算' },
]
<div className="card">
<h1 className="text-lg font-semibold mb-4"></h1>
return (
<div className="min-h-screen flex bg-surface-page">
{/* 左侧品牌展示区 */}
<div className="hidden lg:flex lg:w-[480px] xl:w-[540px] relative overflow-hidden bg-gradient-to-br from-brand-700 via-brand-600 to-brand-800">
{/* 装饰性几何图形 */}
<div className="absolute inset-0 opacity-10">
<div className="absolute top-20 left-20 w-72 h-72 rounded-full border-[3px] border-white" />
<div className="absolute bottom-32 right-10 w-48 h-48 rounded-full border-[2px] border-white" />
<div className="absolute top-1/2 left-1/3 w-96 h-96 rounded-full border-[1px] border-white" />
</div>
<div className="absolute top-0 right-0 w-40 h-40 bg-white/5 rounded-bl-[80px]" />
<div className="absolute bottom-0 left-0 w-32 h-32 bg-white/5 rounded-tr-[64px]" />
<div className="relative z-10 flex flex-col justify-between p-12 xl:p-16 text-white w-full">
{/* Logo + 品牌名 */}
<div className="flex items-center gap-3">
<div className="w-11 h-11 rounded-xl bg-white/15 backdrop-blur flex items-center justify-center">
<Logo className="w-7 h-7 text-white" />
</div>
<div>
<div className="text-xl font-bold tracking-tight"></div>
<div className="text-xs text-white/60 mt-0.5">TurboHR · </div>
</div>
</div>
{/* 标语 */}
<div>
<h2 className="text-3xl xl:text-4xl font-bold leading-tight tracking-tight">
<br />
</h2>
<p className="mt-4 text-sm text-white/70 leading-relaxed max-w-sm">
AI
</p>
</div>
{/* 功能亮点 */}
<div className="space-y-3">
{features.map((f, i) => (
<div key={i} className="flex items-center gap-3 group">
<div className="w-9 h-9 rounded-lg bg-white/10 backdrop-blur flex items-center justify-center flex-shrink-0 group-hover:bg-white/20 transition-colors">
<f.icon className="w-4.5 h-4.5 text-white" strokeWidth={1.8} />
</div>
<div>
<div className="text-sm font-semibold text-white/95">{f.title}</div>
<div className="text-xs text-white/55 mt-0.5">{f.desc}</div>
</div>
</div>
))}
</div>
{/* 底部版权 */}
<div className="text-xs text-white/40">
© 2026 TurboHR ·
</div>
</div>
</div>
{/* 右侧登录表单区 */}
<div className="flex-1 flex items-center justify-center px-4 py-12">
<div className="w-full max-w-sm">
{/* 移动端 Logo */}
<div className="flex lg:hidden items-center justify-center gap-2 mb-8">
<Logo className="w-8 h-8 text-primary" />
<span className="text-xl font-bold"></span>
</div>
{/* 欢迎语 */}
<div className="mb-8">
<h1 className="text-2xl font-bold text-gray-900"></h1>
<p className="text-sm text-gray-500 mt-1.5"></p>
</div>
{error && (
<div className="mb-4 px-3 py-2 rounded-md bg-red-50 text-red-700 text-sm">{error}</div>
<div className="mb-5 px-4 py-3 rounded-lg bg-red-50 border border-red-100 text-red-700 text-sm flex items-start gap-2">
<span className="flex-shrink-0 mt-0.5"></span>
<span>{error}</span>
</div>
)}
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<form onSubmit={handleSubmit(onSubmit)} className="space-y-5">
<div>
<Label></Label>
<Input
type="tel"
placeholder="请输入手机号"
className="h-11"
{...register('phone')}
maxLength={11}
/>
@@ -100,38 +172,61 @@ export default function Login() {
<Input
type={showPassword ? 'text' : 'password'}
placeholder="请输入密码"
className="h-11 pr-10"
{...register('password')}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400"
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 transition-colors"
>
{showPassword ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
{showPassword ? <EyeOff className="w-4.5 h-4.5" /> : <Eye className="w-4.5 h-4.5" />}
</button>
</div>
{errors.password && <p className="text-xs text-red-500 mt-1">{errors.password.message}</p>}
</div>
<label className="flex items-center gap-2 cursor-pointer select-none">
<input
type="checkbox"
checked={remember}
onChange={(e) => setRemember(e.target.checked)}
className="w-4 h-4 rounded border-gray-300 text-primary focus:ring-primary focus:ring-offset-0"
/>
<span className="text-sm text-gray-500"></span>
</label>
<div className="flex items-center justify-between">
<label className="flex items-center gap-2 cursor-pointer select-none">
<input
type="checkbox"
checked={remember}
onChange={(e) => setRemember(e.target.checked)}
className="w-4 h-4 rounded border-gray-300 text-primary focus:ring-primary focus:ring-offset-0"
/>
<span className="text-sm text-gray-500"></span>
</label>
<Link to="/forgot-password" className="text-sm text-primary hover:text-primary-dark transition-colors"></Link>
</div>
<Button type="submit" className="w-full" disabled={loading}>
{loading ? '登录中...' : '登录'}
<Button type="submit" size="lg" className="w-full h-11" disabled={loading}>
{loading ? (
<span className="flex items-center gap-2">
<span className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />
...
</span>
) : (
<span className="flex items-center gap-2">
<ArrowRight className="w-4 h-4" />
</span>
)}
</Button>
</form>
<div className="mt-4 flex items-center justify-between text-sm">
<Link to="/forgot-password" className="text-primary hover:underline"></Link>
<Link to="/register" className="text-primary hover:underline"></Link>
{/* 分割线 */}
<div className="mt-8 mb-6 flex items-center gap-4">
<div className="flex-1 h-px bg-gray-200" />
<span className="text-xs text-gray-400"></span>
<div className="flex-1 h-px bg-gray-200" />
</div>
<Link
to="/register"
className="block w-full h-11 leading-[44px] text-center rounded-md border border-gray-300 text-sm font-medium text-gray-700 hover:bg-gray-50 hover:border-gray-400 transition-colors"
>
</Link>
</div>
</div>
</div>