Files
teach_sys_Demo/src/pages/CompanyJobsPage/components/ResumeInfoModal/index.jsx
KQL 228abc5f4a feat: 优化简历详情显示和版本管理
- 删除38个没有真实修改版岗位的modified字段
- 保留10个有真实修改版岗位的original和modified字段
- 修复ResumeInfoModal组件,支持只有original字段的岗位显示
- 所有岗位显示版本切换按钮,无修改版的只显示原始版按钮

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-12 10:48:30 +08:00

371 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, useEffect } from "react";
import { Radio } from "@arco-design/web-react";
import Modal from "@/components/Modal";
import "./index.css";
export default ({ visible, onClose, data, initialVersion = "2" }) => {
const [version, setVersion] = useState(initialVersion); // 使用传入的初始版本
// 响应initialVersion变化
useEffect(() => {
setVersion(initialVersion);
}, [initialVersion]);
const onRadioChange = (value, e) => {
e?.stopPropagation();
setVersion(value);
};
const handleCloseModal = () => {
onClose();
};
// Markdown解析器 - 解析简历内容
const parseResumeMarkdown = (markdownContent) => {
if (!markdownContent || typeof markdownContent !== 'string') {
return null;
}
const result = {
personalInfo: { name: "岗位名称" },
education: [{
school: '苏州信息职业技术学院',
major: '旅游管理',
period: '2020.9-2023.6'
}],
projects: [],
skills: { core: [], additional: [] },
personalSummary: []
};
// 提取岗位名称
const positionMatch = markdownContent.match(/# 对应岗位:(.+)/);
if (positionMatch) {
result.personalInfo.name = positionMatch[1].trim();
}
// 提取项目经历
const projectSectionMatch = markdownContent.match(/# 一、项目经历\s+([\s\S]*?)(?=# 二、专业技能|$)/);
if (projectSectionMatch) {
const projectContent = projectSectionMatch[1];
// 提取项目名称
const projectNameMatch = projectContent.match(/### (一)项目名称:(.+)/);
const roleMatch = projectContent.match(/### (二)实习岗位:(.+)/);
const timeMatch = projectContent.match(/### (三)实习时间:(.+)/);
const companyMatch = projectContent.match(/### (四)实习单位:(.+)/);
// 提取岗位职责内容 - 从"### (五)岗位职责:"开始到下一个section
const responsibilityMatch = projectContent.match(/### (五)岗位职责:\s+([\s\S]*?)$/);
if (projectNameMatch) {
result.projects = [{
name: projectNameMatch[1].trim(),
role: roleMatch ? roleMatch[1].trim() : "参与者",
period: timeMatch ? timeMatch[1].trim() : "",
company: companyMatch ? companyMatch[1].trim() : "",
description: responsibilityMatch ? responsibilityMatch[1].trim() : ""
}];
}
}
// 提取专业技能
const skillsSectionMatch = markdownContent.match(/# 二、专业技能\s+([\s\S]*?)$/);
if (skillsSectionMatch) {
const skillsContent = skillsSectionMatch[1];
// 提取核心能力
const coreSkillsMatch = skillsContent.match(/### (一)核心能力\s+([\s\S]*?)(?=### (二)复合能力|$)/);
if (coreSkillsMatch) {
const coreSkills = coreSkillsMatch[1]
.split(/\d+\.\s+/)
.filter(skill => skill.trim())
.map(skill => skill.trim().replace(/\s*$/, ''));
result.skills.core = coreSkills;
}
// 提取复合能力
const additionalSkillsMatch = skillsContent.match(/### (二)复合能力\s+([\s\S]*?)$/);
if (additionalSkillsMatch) {
const additionalSkills = additionalSkillsMatch[1]
.split(/\d+\.\s+/)
.filter(skill => skill.trim())
.map(skill => skill.trim().replace(/。\s*$/, ''));
result.skills.additional = additionalSkills;
}
}
return result;
};
// 获取简历数据 - 支持新的数据结构
let resumeContent = {};
if (data?.content) {
// 新的数据结构 - 来自resume-interview页面
if (data.content.original) {
// 有original字段可能有或没有modified字段
const hasModified = !!data.content.modified;
const selectedContent = (!hasModified || version === "1") ? data.content.original : data.content.modified;
// 如果是markdown格式的字符串使用解析器
if (typeof selectedContent === 'string') {
resumeContent = parseResumeMarkdown(selectedContent);
} else if (selectedContent.personalInfo) {
// 如果已经是结构化数据
resumeContent = selectedContent;
}
} else if (data.content.personalInfo) {
// 兼容旧的数据结构(单一版本)
resumeContent = data.content;
} else if (typeof data.content === 'string') {
// 如果content直接是markdown字符串
resumeContent = parseResumeMarkdown(data.content);
}
} else {
// 旧的数据结构 - 兼容现有的company jobs页面
const currentTemplate = data?.selectedTemplate;
const studentInfo = currentTemplate?.studentInfo;
// 检查是否是来自resume-interview页面的数据 (有content和personal_summary字段)
if (currentTemplate && currentTemplate.content && currentTemplate.position) {
// 来自resume-interview页面的新数据结构
const parsedContent = parseResumeMarkdown(currentTemplate.content);
if (parsedContent) {
resumeContent = parsedContent;
// 添加个人总结 - 检查多个可能的位置
if (currentTemplate.personal_summary) {
resumeContent.personalSummary = [currentTemplate.personal_summary];
} else if (currentTemplate.studentInfo?.personalSummary) {
resumeContent.personalSummary = [currentTemplate.studentInfo.personalSummary];
} else if (currentTemplate.studentInfo?.personal_summary) {
resumeContent.personalSummary = [currentTemplate.studentInfo.personal_summary];
}
// 更新岗位名称
resumeContent.personalInfo.name = currentTemplate.position;
}
} else if (studentInfo) {
// 构造兼容格式
resumeContent = {
personalInfo: { name: currentTemplate?.position || "岗位名称" },
education: [{
school: studentInfo.education?.university || '苏州信息职业技术学院',
major: '旅游管理',
period: studentInfo.education?.period || '2020.9-2023.6'
}],
projects: [],
skills: { core: [], additional: [] },
personalSummary: [studentInfo.personalSummary || studentInfo.personal_summary || "具有扎实的专业基础和实践经验"]
};
// 处理项目经历
if (studentInfo.projectExperience) {
resumeContent.projects = [{
name: "项目经历",
role: "参与者",
period: "",
description: studentInfo.projectExperience
}];
} else if (studentInfo.project_experience) {
if (Array.isArray(studentInfo.project_experience)) {
resumeContent.projects = studentInfo.project_experience.map(proj => ({
name: proj.name || "实习项目",
role: proj.role || "参与者",
period: proj.period || "",
description: proj.description || "参与项目实施"
}));
} else if (typeof studentInfo.project_experience === 'object') {
const proj = studentInfo.project_experience;
resumeContent.projects = [{
name: proj.project_name || proj.position || "实习项目",
role: proj.role || "参与者",
period: proj.period || "",
description: proj.description || "参与项目实施"
}];
}
}
// 处理专业技能
if (studentInfo.skills) {
// 新格式skills是字符串转换为数组
const skillsArray = studentInfo.skills.split('\n').filter(s => s.trim());
const midPoint = Math.ceil(skillsArray.length / 2);
resumeContent.skills = {
core: skillsArray.slice(0, midPoint),
additional: skillsArray.slice(midPoint)
};
} else {
resumeContent.skills = {
core: studentInfo.core_skills || [],
additional: studentInfo.compound_skills || []
};
}
}
}
// 数据校验:确保必要字段存在
const isValidData = resumeContent && Object.keys(resumeContent).length > 0 && resumeContent.personalInfo;
// 调试日志 (可以移除)
// console.log('ResumeInfoModal Debug:', { data, resumeContent });
// 如果数据无效,提供默认值防止渲染异常
if (!isValidData) {
console.warn('ResumeInfoModal: Invalid resume data received', { data, resumeContent });
resumeContent = {
personalInfo: { name: '数据加载中...' },
education: [],
projects: [],
skills: { core: [], additional: [] }
};
}
return (
<>
<Modal visible={visible} onClose={handleCloseModal}>
<div className="resume-info-modal" onClick={(e) => e.stopPropagation()}>
<i className="close-icon" onClick={handleCloseModal} />
{data?.content?.original && (
<div className="resume-info-modal-header">
<Radio.Group
type="button"
name="position"
className="resume-info-modal-radio-group"
value={version}
onChange={onRadioChange}
onClick={(e) => e.stopPropagation()}
>
<Radio value="1">原始版</Radio>
{data?.content?.modified && (
<Radio value="2">个人修改版</Radio>
)}
</Radio.Group>
</div>
)}
<p className="resume-info-modal-title">
{data?.title || resumeContent.personalInfo?.name || "职位名称"}
</p>
{/* 统一使用结构化样式展示所有岗位 */}
<ul className="resume-info-moda-list">
{/* 教育经历 */}
<li className="resume-info-moda-item">
<p className="resume-info-moda-item-title">教育经历</p>
<ul className="educational-experience-list">
{resumeContent.education?.map((edu, index) => (
<li key={index} className="educational-experience-list-item">
<p className="school-name">
{edu.school} - {edu.major}
</p>
<p className="study-time">{edu.period}</p>
</li>
))}
</ul>
</li>
{/* 项目经历 */}
<li className="resume-info-moda-item">
<p className="resume-info-moda-item-title">项目经历</p>
<ul className="project-experience-list">
{resumeContent.projects?.map((project, index) => (
<li key={index} className="project-experience-list-item">
<div className="project-info-wrapper">
<div className="project-info">
<p className="project-name">{project.name}</p>
<p className="project-company">
{project.company && `${project.company} - `}角色{project.role}
</p>
</div>
<p className="project-time">{project.period}</p>
</div>
<p className="project-desc" style={{ whiteSpace: 'pre-wrap', lineHeight: '1.6' }}>
{project.description}
</p>
{project.highlights && (
<ul className="job-responsibilities-list">
<p>主要成果</p>
{project.highlights.map((highlight, idx) => (
<li key={idx}>{highlight}</li>
))}
</ul>
)}
</li>
))}
</ul>
</li>
{/* 专业技能 */}
<li className="resume-info-moda-item">
<p className="resume-info-moda-item-title">专业技能</p>
<ul className="professional-skills-list">
{resumeContent.skills?.core && (
<li className="professional-skills-list-item">
<p className="skill-name">核心能力</p>
<div className="core-capabilities-list">
{resumeContent.skills.core.map((skill, index) => (
<p key={index} className="core-capabilities-list-item">
{index + 1}. {skill}
</p>
))}
</div>
</li>
)}
{resumeContent.skills?.additional && (
<li className="professional-skills-list-item">
<p className="skill-name">复合技能</p>
<div className="core-capabilities-list">
{resumeContent.skills.additional.map((skill, index) => (
<p key={index} className="core-capabilities-list-item">
{index + 1}. {skill}
</p>
))}
</div>
</li>
)}
</ul>
</li>
{/* 个人总结 */}
{(resumeContent.personalSummary && resumeContent.personalSummary.length > 0) ||
(resumeContent.personalSummary && typeof resumeContent.personalSummary === 'string') ? (
<li className="resume-info-moda-item">
<p className="resume-info-moda-item-title">个人总结</p>
<div className="personal-summary-content">
{Array.isArray(resumeContent.personalSummary) ? (
<ul className="personal-summary-list">
{resumeContent.personalSummary.map((summary, index) => (
<li key={index} style={{ whiteSpace: 'pre-wrap', lineHeight: '1.6' }}>
{summary}
</li>
))}
</ul>
) : (
<p style={{ whiteSpace: 'pre-wrap', lineHeight: '1.6' }}>
{resumeContent.personalSummary}
</p>
)}
</div>
</li>
) : null}
{/* 对应课程单元 - 暂时保留静态展示,后续可根据需要动态化 */}
{resumeContent.courses && (
<li className="resume-info-moda-item">
<p className="resume-info-moda-item-title">对应课程单元</p>
<ul className="corresponding-course-units-list">
<li className="corresponding-course-units-list-item">
<div className="tag">相关课程</div>
<ul className="course-units-list">
{resumeContent.courses.map((course, index) => (
<li key={index} className="course-units-list-item">
{course}
</li>
))}
</ul>
</li>
</ul>
</li>
)}
</ul>
</div>
</Modal>
</>
);
};