feat: 更新简历详情页面教育经历为苏州信息职业技术学院
- 统一所有岗位简历的教育经历显示为苏州信息职业技术学院 - 更新简历详情页面组件,确保教育经历信息一致性 - 优化简历信息展示格式和样式 - 添加新的面试题库和项目库数据 - 完善文旅产业相关简历模板
This commit is contained in:
@@ -116,6 +116,23 @@
|
||||
white-space: nowrap; /* 禁止换行 */
|
||||
text-overflow: ellipsis; /* 文本溢出显示省略号 */
|
||||
}
|
||||
|
||||
.version-selector {
|
||||
margin-top: 8px;
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.arco-select {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.arco-select-view-single {
|
||||
height: 28px !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useCallback, useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { Input } from "@arco-design/web-react";
|
||||
import { Input, Select } from "@arco-design/web-react";
|
||||
import Modal from "@/components/Modal";
|
||||
import InfiniteScroll from "@/components/InfiniteScroll";
|
||||
import toast from "@/components/Toast";
|
||||
@@ -22,6 +22,7 @@ export default ({ visible, onClose, data, directToResume = false, hideDeliverBut
|
||||
const [listPage, setListPage] = useState(1);
|
||||
const [listHasMore, setListHasMore] = useState(true);
|
||||
const [permissionModalVisible, setPermissionModalVisible] = useState(false);
|
||||
const [selectedVersion, setSelectedVersion] = useState("2"); // 默认选择个人修改版
|
||||
|
||||
// 处理directToResume参数变化
|
||||
useEffect(() => {
|
||||
@@ -82,12 +83,14 @@ export default ({ visible, onClose, data, directToResume = false, hideDeliverBut
|
||||
studentId: studentInfo?.id,
|
||||
resumeTitle: item.title,
|
||||
jobPosition: data?.position,
|
||||
company: data?.company
|
||||
company: data?.company,
|
||||
resumeVersion: selectedVersion // 添加版本信息
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
// 投递成功,显示成功提示
|
||||
toast.success(`简历"${item.title}"投递成功!`);
|
||||
const versionText = selectedVersion === "1" ? "原始版" : "个人修改版";
|
||||
toast.success(`简历"${item.title}"(${versionText})投递成功!`);
|
||||
|
||||
// 关闭模态框
|
||||
handleCloseModal();
|
||||
@@ -123,26 +126,32 @@ export default ({ visible, onClose, data, directToResume = false, hideDeliverBut
|
||||
if (pageDataResponse.success) {
|
||||
const pageData = pageDataResponse.data;
|
||||
|
||||
// 直接使用简历列表中的模板数据
|
||||
const selectedTemplate = item.template;
|
||||
|
||||
// 找到对应的行业信息
|
||||
const matchedIndustry = pageData.industries?.find(industry =>
|
||||
industry.name === item.industry
|
||||
);
|
||||
|
||||
// 传递数据给 ResumeInfoModal
|
||||
// 从resumeTemplates中查找对应岗位的模板
|
||||
const industryTemplates = pageData.resumeTemplates?.[item.industry] || [];
|
||||
const positionTemplate = industryTemplates.find(template =>
|
||||
template.position === item.position
|
||||
);
|
||||
|
||||
// 构造简历数据,使用与ResumeInterviewPage相同的格式
|
||||
const resumeData = {
|
||||
selectedTemplate,
|
||||
studentResume: pageData.myResume,
|
||||
industry: matchedIndustry,
|
||||
jobPosition: item.position
|
||||
title: item.position, // 使用岗位名称作为标题
|
||||
content: positionTemplate?.content || null, // 这里包含原始版和修改版数据
|
||||
studentResume: pageData.myResume
|
||||
};
|
||||
|
||||
console.log('加载简历数据:', {
|
||||
resumeTitle: item.title,
|
||||
position: item.position,
|
||||
industry: item.industry
|
||||
industry: item.industry,
|
||||
selectedVersion: selectedVersion,
|
||||
hasContent: !!positionTemplate?.content,
|
||||
hasOriginal: !!positionTemplate?.content?.original,
|
||||
hasModified: !!positionTemplate?.content?.modified
|
||||
});
|
||||
|
||||
setResumeInfoData(resumeData);
|
||||
@@ -186,11 +195,18 @@ export default ({ visible, onClose, data, directToResume = false, hideDeliverBut
|
||||
<p className="file-info-targetPosition">
|
||||
{item.title}
|
||||
</p>
|
||||
{item?.skills?.length > 0 && (
|
||||
<p className="file-info-skills">
|
||||
{item?.skills?.join("/")}
|
||||
</p>
|
||||
)}
|
||||
<div className="version-selector">
|
||||
<Select
|
||||
placeholder="选择版本"
|
||||
value={selectedVersion}
|
||||
style={{ width: 120, fontSize: '12px' }}
|
||||
onChange={(value) => setSelectedVersion(value)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Select.Option value="1">原始版</Select.Option>
|
||||
<Select.Option value="2">个人修改版</Select.Option>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -298,6 +314,7 @@ export default ({ visible, onClose, data, directToResume = false, hideDeliverBut
|
||||
<ResumeInfoModal
|
||||
visible={resumeInfoModalShow}
|
||||
data={resumeInfoData}
|
||||
initialVersion={selectedVersion}
|
||||
onClose={() => {
|
||||
setResumeInfoModalShow(false);
|
||||
setResumeInfoData(null);
|
||||
|
||||
@@ -21,12 +21,18 @@
|
||||
z-index: 10;
|
||||
cursor: pointer;
|
||||
}
|
||||
.resume-info-modal-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.resume-info-modal-radio-group {
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.resume-info-modal-title {
|
||||
|
||||
@@ -1,190 +1,484 @@
|
||||
import { useState } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { Radio } from "@arco-design/web-react";
|
||||
import Modal from "@/components/Modal";
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import "./index.css";
|
||||
|
||||
export default ({ visible, onClose, data }) => {
|
||||
const [position, setPosition] = useState("1");
|
||||
const onRadioChange = (value) => {
|
||||
setPosition(value);
|
||||
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();
|
||||
};
|
||||
|
||||
// 获取当前简历数据
|
||||
const currentTemplate = data?.selectedTemplate;
|
||||
const studentInfo = currentTemplate?.studentInfo;
|
||||
const positionTitle = currentTemplate?.position || "岗位名称";
|
||||
|
||||
// 转换数据格式
|
||||
let resumeData = {
|
||||
educational_experience: ["相关专业大学 本科"],
|
||||
project_experience: [],
|
||||
core_skills: [],
|
||||
compound_skills: [],
|
||||
personal_summary: "暂无个人总结"
|
||||
|
||||
// 判断是否应该使用markdown渲染(有真实修改版的岗位)
|
||||
const shouldUseMarkdown = (positionTitle) => {
|
||||
const markdownPositions = [
|
||||
"会展策划师",
|
||||
"会展讲解员",
|
||||
"活动执行",
|
||||
"活动策划师",
|
||||
"漫展策划师",
|
||||
"会展执行助理",
|
||||
"旅游规划师",
|
||||
"旅游计调专员",
|
||||
"景区运营专员",
|
||||
"文旅运营总监助理"
|
||||
];
|
||||
return markdownPositions.includes(positionTitle);
|
||||
};
|
||||
|
||||
if (studentInfo) {
|
||||
// 处理教育经历
|
||||
if (studentInfo.education) {
|
||||
resumeData.educational_experience = [
|
||||
`${studentInfo.education.university || '苏州信息职业技术学院'} ${studentInfo.education.period || '2020.9 - 2023.6'}`
|
||||
];
|
||||
} else {
|
||||
resumeData.educational_experience = studentInfo.educational_experience || ["相关专业大学 本科"];
|
||||
|
||||
// Markdown解析器 - 解析简历内容
|
||||
const parseResumeMarkdown = (markdownContent) => {
|
||||
if (!markdownContent || typeof markdownContent !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 处理项目经历 - 支持新格式
|
||||
if (studentInfo.projectExperience) {
|
||||
// 新格式:projectExperience是字符串
|
||||
resumeData.project_experience = [{
|
||||
name: "项目经历",
|
||||
description: studentInfo.projectExperience
|
||||
}];
|
||||
} else if (studentInfo.project_experience) {
|
||||
// 旧格式兼容
|
||||
if (Array.isArray(studentInfo.project_experience)) {
|
||||
resumeData.project_experience = studentInfo.project_experience;
|
||||
} else if (typeof studentInfo.project_experience === 'object') {
|
||||
const proj = studentInfo.project_experience;
|
||||
resumeData.project_experience = [
|
||||
{
|
||||
name: proj.project_name || proj.position || "实习项目",
|
||||
description: proj.description || "参与项目实施"
|
||||
}
|
||||
];
|
||||
|
||||
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() : ""
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
// 处理专业技能 - 支持新格式
|
||||
if (studentInfo.skills) {
|
||||
// 新格式:skills是字符串
|
||||
resumeData.skills_text = studentInfo.skills;
|
||||
} else {
|
||||
// 旧格式兼容
|
||||
resumeData.core_skills = studentInfo.core_skills || [];
|
||||
resumeData.compound_skills = studentInfo.compound_skills || [];
|
||||
|
||||
// 提取专业技能
|
||||
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;
|
||||
};
|
||||
|
||||
// 渲染markdown分块内容
|
||||
const renderMarkdownSections = (markdownContent) => {
|
||||
if (!markdownContent) return null;
|
||||
|
||||
// 处理个人总结 - 支持新格式
|
||||
resumeData.personal_summary = studentInfo.personalSummary || studentInfo.personal_summary || "具有扎实的专业基础和实践经验";
|
||||
const sections = [];
|
||||
|
||||
// 首先添加教育经历板块
|
||||
sections.push(
|
||||
<li key="education" className="resume-info-moda-item">
|
||||
<p className="resume-info-moda-item-title">教育经历</p>
|
||||
<ul className="educational-experience-list">
|
||||
<li className="educational-experience-list-item">
|
||||
<p className="school-name">
|
||||
苏州信息职业技术学院 - 旅游管理
|
||||
</p>
|
||||
<p className="study-time">2020.9-2023.6</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
);
|
||||
|
||||
// 按H1标题分割markdown内容(# 开头的行)
|
||||
const markdownSections = markdownContent.split(/^# /gm).filter(section => section.trim());
|
||||
|
||||
// 添加markdown渲染的section
|
||||
markdownSections.forEach((section, index) => {
|
||||
// 为每个section添加回# 前缀(除了第一个如果不是以#开头)
|
||||
const fullSection = section.startsWith('#') ? section : `# ${section}`;
|
||||
|
||||
// 过滤掉"对应岗位"板块
|
||||
if (fullSection.includes('对应岗位:') || fullSection.includes('对应岗位:')) {
|
||||
return;
|
||||
}
|
||||
|
||||
sections.push(
|
||||
<li key={`markdown-${index}`} className="resume-info-moda-item">
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
components={{
|
||||
// 标题样式
|
||||
h1: ({node, ...props}) => <p className="resume-info-moda-item-title" {...props} />,
|
||||
h2: ({node, ...props}) => <p className="resume-info-moda-item-title" style={{
|
||||
fontSize: '18px',
|
||||
marginTop: '15px'
|
||||
}} {...props} />,
|
||||
h3: ({node, ...props}) => <p style={{
|
||||
fontSize: '16px',
|
||||
fontWeight: '600',
|
||||
color: '#4e5969',
|
||||
marginBottom: '10px',
|
||||
marginTop: '15px',
|
||||
textAlign: 'left'
|
||||
}} {...props} />,
|
||||
// 段落样式
|
||||
p: ({node, ...props}) => <p style={{
|
||||
fontSize: '14px',
|
||||
lineHeight: '1.6',
|
||||
color: '#1d2129',
|
||||
marginBottom: '10px',
|
||||
textAlign: 'left',
|
||||
width: '100%',
|
||||
wordWrap: 'break-word'
|
||||
}} {...props} />,
|
||||
// 列表样式
|
||||
ol: ({node, ...props}) => <ol style={{
|
||||
paddingLeft: '20px',
|
||||
marginBottom: '15px'
|
||||
}} {...props} />,
|
||||
ul: ({node, ...props}) => <ul style={{
|
||||
paddingLeft: '20px',
|
||||
marginBottom: '15px'
|
||||
}} {...props} />,
|
||||
li: ({node, ...props}) => <li style={{
|
||||
fontSize: '14px',
|
||||
lineHeight: '1.6',
|
||||
color: '#1d2129',
|
||||
marginBottom: '8px',
|
||||
textAlign: 'left'
|
||||
}} {...props} />,
|
||||
// 加粗和删除线样式
|
||||
strong: ({node, ...props}) => <strong style={{color: '#ff4d4f', fontWeight: 'bold'}} {...props} />,
|
||||
del: ({node, ...props}) => <del style={{textDecoration: 'line-through', color: '#999'}} {...props} />
|
||||
}}
|
||||
>
|
||||
{fullSection}
|
||||
</ReactMarkdown>
|
||||
</li>
|
||||
);
|
||||
});
|
||||
|
||||
return sections;
|
||||
};
|
||||
|
||||
// 获取简历数据 - 支持新的数据结构
|
||||
let resumeContent = {};
|
||||
|
||||
if (data?.content) {
|
||||
// 新的数据结构 - 来自resume-interview页面
|
||||
if (data.content.original && data.content.modified) {
|
||||
const selectedContent = 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">
|
||||
<i className="close-icon" onClick={handleCloseModal} />
|
||||
<Radio.Group
|
||||
type="button"
|
||||
name="position"
|
||||
className="resume-info-modal-radio-group"
|
||||
value={position}
|
||||
onChange={onRadioChange}
|
||||
>
|
||||
<Radio value="1">原始版</Radio>
|
||||
<Radio value="2">个人修改版</Radio>
|
||||
<Radio value="3">个人修改版</Radio>
|
||||
</Radio.Group>
|
||||
<p className="resume-info-modal-title">{positionTitle}</p>
|
||||
<ul className="resume-info-moda-list">
|
||||
{/* 教育经历 */}
|
||||
{resumeData.educational_experience && resumeData.educational_experience.length > 0 && (
|
||||
<li className="resume-info-moda-item">
|
||||
<p className="resume-info-moda-item-title">教育经历</p>
|
||||
<ul className="educational-experience-list">
|
||||
{resumeData.educational_experience.map((edu, index) => (
|
||||
<li key={index} className="educational-experience-list-item">
|
||||
<p className="school-name">{edu}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
<>
|
||||
<Modal visible={visible} onClose={handleCloseModal}>
|
||||
<div className="resume-info-modal" onClick={(e) => e.stopPropagation()}>
|
||||
<i className="close-icon" onClick={handleCloseModal} />
|
||||
{data?.content?.original && data?.content?.modified && (
|
||||
<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>
|
||||
<Radio value="2">个人修改版</Radio>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
)}
|
||||
<p className="resume-info-modal-title">
|
||||
{data?.title || resumeContent.personalInfo?.name || "职位名称"}
|
||||
</p>
|
||||
|
||||
{/* 判断是否使用markdown渲染 */}
|
||||
{data && shouldUseMarkdown(data?.title) && data?.content?.original && data?.content?.modified ? (
|
||||
<ul className="resume-info-moda-list">
|
||||
{renderMarkdownSections(version === "1" ? data.content.original : data.content.modified)}
|
||||
</ul>
|
||||
) : (
|
||||
<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>
|
||||
{/* 项目经历 */}
|
||||
{resumeData.project_experience && resumeData.project_experience.length > 0 && (
|
||||
<li className="resume-info-moda-item">
|
||||
<p className="resume-info-moda-item-title">项目经历</p>
|
||||
<ul className="project-experience-list">
|
||||
{resumeData.project_experience.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 || `项目${index + 1}`}</p>
|
||||
</div>
|
||||
<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-desc">
|
||||
{project.description}
|
||||
</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
)}
|
||||
<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>
|
||||
{resumeData.skills_text ? (
|
||||
<div className="professional-skills-content" style={{ whiteSpace: 'pre-wrap', lineHeight: '1.8', padding: '0 20px' }}>
|
||||
{resumeData.skills_text}
|
||||
</div>
|
||||
) : (
|
||||
<ul className="professional-skills-list">
|
||||
{resumeData.core_skills && resumeData.core_skills.length > 0 && (
|
||||
<li className="professional-skills-list-item">
|
||||
<p className="skill-name">核心能力</p>
|
||||
<div className="core-capabilities-list">
|
||||
{resumeData.core_skills.map((skill, index) => (
|
||||
<p key={index} className="core-capabilities-list-item">
|
||||
{skill}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
{resumeData.compound_skills && resumeData.compound_skills.length > 0 && (
|
||||
<li className="professional-skills-list-item">
|
||||
<p className="skill-name">复合能力</p>
|
||||
<div className="core-capabilities-list">
|
||||
{resumeData.compound_skills.map((skill, index) => (
|
||||
<p key={index} className="core-capabilities-list-item">
|
||||
{skill}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
)}
|
||||
<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>
|
||||
{/* 个人总结 */}
|
||||
{resumeData.personal_summary && resumeData.personal_summary.trim() !== '' && (
|
||||
{(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">
|
||||
<p>{resumeData.personal_summary}</p>
|
||||
{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>
|
||||
)}
|
||||
{/* 对应课程单元 */}
|
||||
<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">方向1</div>
|
||||
<ul className="course-units-list">
|
||||
<li className="course-units-list-item">课程单元名称</li>
|
||||
<li className="course-units-list-item">课程单元名称</li>
|
||||
<li className="course-units-list-item">课程单元名称</li>
|
||||
<li className="course-units-list-item">课程单元名称</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
};
|
||||
@@ -403,4 +403,90 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 项目图片展示样式 */
|
||||
.project-cases-modal-images {
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
padding: 20px;
|
||||
background-color: #f7f8fa;
|
||||
border-radius: 8px;
|
||||
|
||||
/* 默认两列布局 */
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
|
||||
/* 单张图片时全宽显示 */
|
||||
&:has(.project-cases-modal-image-wrapper:only-child) {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
.project-cases-modal-image-wrapper {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* 三张图片时三列布局 */
|
||||
&:has(.project-cases-modal-image-wrapper:nth-child(3)):not(:has(.project-cases-modal-image-wrapper:nth-child(4))) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
/* 四张及以上图片时两列布局 */
|
||||
&:has(.project-cases-modal-image-wrapper:nth-child(4)) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.project-cases-modal-image-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.project-cases-modal-image-title {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent);
|
||||
color: #ffffff;
|
||||
padding: 20px 12px 12px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.5px;
|
||||
transition: all 0.3s ease;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&:hover .project-cases-modal-image-title {
|
||||
background: linear-gradient(to top, rgba(64, 128, 255, 0.9), transparent);
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.project-cases-modal-image {
|
||||
width: 100%;
|
||||
height: 280px;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e5e6eb;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
||||
border-color: #4080ff;
|
||||
}
|
||||
|
||||
/* 对于PNG图片保持contain以避免裁剪 */
|
||||
&[src$=".png"] {
|
||||
object-fit: contain;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,5 +203,41 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 可点击的特殊项目样式 */
|
||||
.clickable-project-item {
|
||||
background-color: #e5f1ff !important;
|
||||
border-color: #e5e6eb !important;
|
||||
cursor: pointer !important;
|
||||
|
||||
&:hover {
|
||||
border-color: #4080ff !important;
|
||||
box-shadow: 0 4px 12px rgba(44, 127, 255, 0.2) !important;
|
||||
transform: translateY(-2px) !important;
|
||||
background-color: #f0f7ff !important;
|
||||
}
|
||||
|
||||
.project-library-item-title {
|
||||
color: #4080ff !important;
|
||||
border-color: #4080ff !important;
|
||||
background-color: #ffffff !important;
|
||||
}
|
||||
|
||||
> div {
|
||||
> p {
|
||||
color: #1d2129 !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
> span {
|
||||
color: #2c7aff !important;
|
||||
cursor: pointer !important;
|
||||
|
||||
&:hover {
|
||||
color: #1d4ed8 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import TagMiddle from "@/assets/images/ResumeInterviewPage/Tag2.png";
|
||||
import TagOrdinary from "@/assets/images/ResumeInterviewPage/Tag3.png";
|
||||
import QuestionIcon from "@/assets/images/ResumeInterviewPage/question_icon2.png";
|
||||
|
||||
|
||||
import "./index.css";
|
||||
|
||||
const ResumeInterviewPage = () => {
|
||||
@@ -25,6 +26,23 @@ const ResumeInterviewPage = () => {
|
||||
const sectionsRef = useRef({});
|
||||
const navRef = useRef(null);
|
||||
|
||||
// 有真实修改版的岗位列表(来自修改后简历文件夹)
|
||||
const hasRealModifiedVersion = (positionTitle) => {
|
||||
const modifiedPositions = [
|
||||
"会展策划师",
|
||||
"会展讲解员",
|
||||
"活动执行",
|
||||
"活动策划师",
|
||||
"漫展策划师",
|
||||
"会展执行助理",
|
||||
"旅游规划师",
|
||||
"旅游计调专员",
|
||||
"景区运营专员",
|
||||
"文旅运营总监助理"
|
||||
];
|
||||
return modifiedPositions.includes(positionTitle);
|
||||
};
|
||||
|
||||
// 获取岗位头像和级别信息
|
||||
const getPositionInfo = (positionTitle) => {
|
||||
const jobData = jobLevelData.data;
|
||||
@@ -94,10 +112,14 @@ const ResumeInterviewPage = () => {
|
||||
templates.find((t) => t.level === position.level) ||
|
||||
templates[0];
|
||||
|
||||
setResumeModalData({
|
||||
selectedTemplate,
|
||||
// 构造符合ResumeInfoModal期望的数据格式
|
||||
const resumeData = {
|
||||
title: position.title,
|
||||
content: selectedTemplate?.content || selectedTemplate?.oldContent || null,
|
||||
studentResume: pageData.myResume,
|
||||
});
|
||||
};
|
||||
|
||||
setResumeModalData(resumeData);
|
||||
setResumeModalVisible(true);
|
||||
};
|
||||
|
||||
@@ -287,7 +309,7 @@ const ResumeInterviewPage = () => {
|
||||
const positionInfo = getPositionInfo(position.title);
|
||||
return (
|
||||
<li
|
||||
className="job-item job-item-change"
|
||||
className={`job-item ${hasRealModifiedVersion(position.title) ? 'job-item-change' : ''}`}
|
||||
key={position.id}
|
||||
onClick={() => handlePositionClick(position, item)}
|
||||
>
|
||||
@@ -308,6 +330,7 @@ const ResumeInterviewPage = () => {
|
||||
alt={positionInfo.levelName}
|
||||
className="job-level-tag"
|
||||
/>
|
||||
|
||||
<div className="job-name">
|
||||
<p>{position.title}</p>
|
||||
<span className="job-arrow">›</span>
|
||||
|
||||
Reference in New Issue
Block a user