import React, { useState, useRef, useCallback, useEffect } from 'react'; import { Input, Button, Select, message, ConfigProvider } from 'antd'; import zhCN from 'antd/locale/zh_CN'; import { PlusOutlined, DeleteOutlined, UserOutlined, BankOutlined, ReadOutlined, FileSearchOutlined, ArrowLeftOutlined, LoadingOutlined, CheckCircleFilled, ClockCircleOutlined, RocketOutlined, } from '@ant-design/icons'; import { useNavigate, useSearchParams, request } from '@umijs/max'; import Navbar from '../../Home/components/Navbar'; import Footer from '../../Home/components/Footer'; import styles from './index.less'; const { TextArea } = Input; interface WorkExp { company: string; position: string; startDate: string; endDate: string; description: string; } interface Education { school: string; major: string; degree: string; startDate: string; endDate: string; description: string; } const PROGRESS_STEPS = [ { label: '提交表单数据', duration: 2000 }, { label: 'AI 正在分析岗位要求', duration: 6000 }, { label: 'AI 正在生成简历内容', duration: 12000 }, { label: '正在填充 Word 模板', duration: 8000 }, { label: '正在上传文件', duration: 3000 }, ]; const degreeOptions = [ { value: '博士', label: '博士' }, { value: '硕士', label: '硕士' }, { value: '本科', label: '本科' }, { value: '大专', label: '大专' }, ]; const emptyWork = (): WorkExp => ({ company: '', position: '', startDate: '', endDate: '', description: '' }); const emptyEdu = (): Education => ({ school: '', major: '', degree: '', startDate: '', endDate: '', description: '' }); const ResumeCreate: React.FC = () => { const navigate = useNavigate(); const [searchParams] = useSearchParams(); const templateId = searchParams.get('templateId') || '1'; const templateTitle = searchParams.get('templateTitle') || ''; // Form fields const [resumeName, setResumeName] = useState(''); const [jobIntention, setJobIntention] = useState(''); const [expectedSalary, setExpectedSalary] = useState(''); const [preferredCity, setPreferredCity] = useState(''); const [language, setLanguage] = useState('中文'); const [jobDescription, setJobDescription] = useState(''); const [workList, setWorkList] = useState([emptyWork()]); const [eduList, setEduList] = useState([emptyEdu()]); // Generation state const [generating, setGenerating] = useState(false); const [progressStep, setProgressStep] = useState(0); const timerRef = useRef(null); // Cleanup timers on unmount useEffect(() => { return () => { if (timerRef.current) clearTimeout(timerRef.current); }; }, []); // Work experience helpers const addWork = () => setWorkList([...workList, emptyWork()]); const removeWork = (i: number) => setWorkList(workList.filter((_, idx) => idx !== i)); const updateWork = (i: number, field: keyof WorkExp, val: string) => { const list = [...workList]; list[i] = { ...list[i], [field]: val }; setWorkList(list); }; // Education helpers const addEdu = () => setEduList([...eduList, emptyEdu()]); const removeEdu = (i: number) => setEduList(eduList.filter((_, idx) => idx !== i)); const updateEdu = (i: number, field: keyof Education, val: string) => { const list = [...eduList]; list[i] = { ...list[i], [field]: val }; setEduList(list); }; // Animate progress steps const animateProgress = useCallback(() => { let step = 0; setProgressStep(0); const next = () => { if (step < PROGRESS_STEPS.length - 1) { timerRef.current = setTimeout(() => { step++; setProgressStep(step); next(); }, PROGRESS_STEPS[step].duration); } }; next(); }, []); // Generate resume const handleGenerate = async () => { if (!resumeName.trim()) { message.warning('请输入简历名称'); return; } if (!jobIntention.trim()) { message.warning('请输入求职意向'); return; } if (!jobDescription.trim()) { message.warning('请粘贴岗位描述/JD'); return; } setGenerating(true); animateProgress(); try { const payload: any = { resumeName: resumeName.trim(), jobIntention: jobIntention.trim(), jobDescription: jobDescription.trim(), templateId, }; if (expectedSalary.trim()) payload.expectedSalary = expectedSalary.trim(); if (preferredCity.trim()) payload.preferredCity = preferredCity.trim(); if (language) payload.language = language; const validWork = workList.filter(w => w.company.trim() || w.position.trim()); if (validWork.length) payload.workExperience = validWork; const validEdu = eduList.filter(e => e.school.trim() || e.major.trim()); if (validEdu.length) payload.education = validEdu; const res = await request('/api/resume/student/ai/generate', { method: 'POST', data: payload, timeout: 120000, }); if (res.code === 0 && res.data?.url) { setProgressStep(PROGRESS_STEPS.length - 1); if (timerRef.current) clearTimeout(timerRef.current); message.success('简历生成成功'); // 跳转到预览页 navigate(`/resume/preview?url=${encodeURIComponent(res.data.url)}&name=${encodeURIComponent(resumeName.trim())}`); } else { message.error(res.message || '生成失败,请重试'); } } catch (err: any) { message.error(err?.message || '请求超时或网络异常,请重试'); } finally { setGenerating(false); if (timerRef.current) clearTimeout(timerRef.current); } }; const handleCancel = () => navigate(-1); return (
{/* Top bar */}
返回模板列表
{/* Header */}

AI 智能生成简历

{templateTitle && 模板:{decodeURIComponent(templateTitle)}}

填写以下信息,AI 将根据岗位要求为你量身定制简历

{/* Loading overlay */} {generating && (

AI 正在为你生成简历

预计需要 10~30 秒,请耐心等待

{PROGRESS_STEPS.map((s, i) => (
{i < progressStep ? : i === progressStep ? : } {s.label}
))}
)} {/* Section 1: Basic Info */}
基本信息 带 * 为必填项
setResumeName(e.target.value)} />
setJobIntention(e.target.value)} />
setExpectedSalary(e.target.value)} />
setPreferredCity(e.target.value)} />
updateWork(i, 'company', e.target.value)} />
updateWork(i, 'position', e.target.value)} />
updateWork(i, 'startDate', e.target.value)} />
updateWork(i, 'endDate', e.target.value)} />