UI优化更新:面试模拟、简历面试、项目库、求职策略等多个页面改进
主要更新: - 面试模拟页:移除上滑查看评价,添加渐进式评分(72→81→89) - 简历面试页:添加岗位头像、标签背景、面试题加粗等视觉优化 - 项目库页:添加"我完成的项目库"板块,增加hover效果 - 求职策略详情页:优化圆柱体和矩形对齐,添加CSV岗位数据,调整批次文字位置 - 企业岗位列表页:添加返回按钮功能 - 全局:统一岗位级别术语(普通岗/技术骨干岗/储备干部岗) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,11 @@ import toast from "@/components/Toast";
|
||||
import InterviewQuestionsModal from "./components/InterviewQuestionsModal";
|
||||
import ResumeInfoModal from "@/pages/CompanyJobsPage/components/ResumeInfoModal";
|
||||
import { getPageData } from "@/services/resumeInterview";
|
||||
import jobLevelData from "@/data/joblevel.json";
|
||||
import TagHigh from "@/assets/images/ResumeInterviewPage/Tag.png";
|
||||
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";
|
||||
|
||||
@@ -15,7 +20,50 @@ const ResumeInterviewPage = () => {
|
||||
const [resumeModalData, setResumeModalData] = useState(undefined);
|
||||
const [pageData, setPageData] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [showLeftBtn, setShowLeftBtn] = useState(false);
|
||||
const [showRightBtn, setShowRightBtn] = useState(true);
|
||||
const sectionsRef = useRef({});
|
||||
const navRef = useRef(null);
|
||||
|
||||
// 获取岗位头像和级别信息
|
||||
const getPositionInfo = (positionTitle) => {
|
||||
const jobData = jobLevelData.data;
|
||||
let positionInfo = null;
|
||||
let levelName = "";
|
||||
let levelKey = "";
|
||||
|
||||
// 遍历所有级别查找匹配的岗位
|
||||
for (const [key, levelData] of Object.entries(jobData)) {
|
||||
const found = levelData.list.find(item => item.position_name === positionTitle);
|
||||
if (found) {
|
||||
positionInfo = found;
|
||||
levelName = levelData.name;
|
||||
levelKey = key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 根据级别返回对应的标签图片
|
||||
let tagImage = "";
|
||||
switch(levelKey) {
|
||||
case "high":
|
||||
tagImage = TagHigh;
|
||||
break;
|
||||
case "middle":
|
||||
tagImage = TagMiddle;
|
||||
break;
|
||||
case "ordinary":
|
||||
default:
|
||||
tagImage = TagOrdinary;
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
avatar: positionInfo?.img || null,
|
||||
levelName: levelName || "普通岗",
|
||||
tagImage: tagImage
|
||||
};
|
||||
};
|
||||
|
||||
// 导航到指定行业段落
|
||||
const handleNavClick = (industryId) => {
|
||||
@@ -114,28 +162,76 @@ const ResumeInterviewPage = () => {
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
return () => window.removeEventListener("scroll", handleScroll);
|
||||
}, [pageData?.industries]);
|
||||
|
||||
// 监听数据变化,更新滚动按钮状态
|
||||
useEffect(() => {
|
||||
if (pageData?.industries && navRef.current) {
|
||||
setTimeout(() => {
|
||||
checkScrollButtons();
|
||||
}, 100);
|
||||
}
|
||||
}, [pageData?.industries]);
|
||||
|
||||
// 检查滚动按钮的显示状态
|
||||
const checkScrollButtons = () => {
|
||||
if (!navRef.current) return;
|
||||
const { scrollLeft, scrollWidth, clientWidth } = navRef.current;
|
||||
setShowLeftBtn(scrollLeft > 5); // 添加5px的容差
|
||||
setShowRightBtn(scrollLeft < scrollWidth - clientWidth - 5); // 添加5px的容差,确保能滚动到最后
|
||||
};
|
||||
|
||||
// 左右滚动函数
|
||||
const scrollNav = (direction) => {
|
||||
if (!navRef.current) return;
|
||||
const scrollAmount = 200; // 每次滚动的距离
|
||||
navRef.current.scrollTo({
|
||||
left: navRef.current.scrollLeft + (direction === 'left' ? -scrollAmount : scrollAmount),
|
||||
behavior: 'smooth'
|
||||
});
|
||||
};
|
||||
|
||||
// 添加鼠标滚轮横向滚动功能
|
||||
useEffect(() => {
|
||||
const navigation = document.querySelector('.resume-interview-navigation');
|
||||
if (!navigation) return;
|
||||
// 延迟执行以确保DOM已经渲染
|
||||
const timer = setTimeout(() => {
|
||||
const navigation = navRef.current;
|
||||
if (!navigation) return;
|
||||
|
||||
const handleWheel = (e) => {
|
||||
const tabs = navigation.querySelector('.navigation-tabs');
|
||||
if (!tabs) return;
|
||||
|
||||
// 检查是否在导航栏区域
|
||||
if (e.currentTarget === navigation || navigation.contains(e.target)) {
|
||||
if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) {
|
||||
const handleWheel = (e) => {
|
||||
// 检查是否鼠标在导航栏上
|
||||
if (navigation.contains(e.target)) {
|
||||
// 阻止默认垂直滚动
|
||||
e.preventDefault();
|
||||
tabs.scrollLeft += e.deltaY * 0.5; // 减慢滚动速度使其更平滑
|
||||
e.stopPropagation();
|
||||
|
||||
// 转换为横向滚动
|
||||
const delta = e.deltaY || e.deltaX;
|
||||
navigation.scrollLeft += delta;
|
||||
|
||||
// 更新按钮状态
|
||||
checkScrollButtons();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
navigation.addEventListener('wheel', handleWheel, { passive: false });
|
||||
return () => navigation.removeEventListener('wheel', handleWheel);
|
||||
}, []);
|
||||
const handleScroll = () => {
|
||||
checkScrollButtons();
|
||||
};
|
||||
|
||||
// 使用捕获阶段以确保事件被正确处理
|
||||
window.addEventListener('wheel', handleWheel, { passive: false, capture: true });
|
||||
navigation.addEventListener('scroll', handleScroll);
|
||||
|
||||
// 初始检查
|
||||
checkScrollButtons();
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('wheel', handleWheel, { capture: true });
|
||||
navigation.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}, 100);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [pageData]);
|
||||
|
||||
return (
|
||||
<div className="resume-interview-page">
|
||||
@@ -143,21 +239,39 @@ const ResumeInterviewPage = () => {
|
||||
<Spin size={80} className="resume-interview-spin" />
|
||||
) : pageData ? (
|
||||
<>
|
||||
<ul className="resume-interview-navigation">
|
||||
<div className="navigation-tabs">
|
||||
{pageData.industries.map((industry) => (
|
||||
<li
|
||||
key={industry.id}
|
||||
className={`resume-interview-navigation-item ${
|
||||
activeIndustry === industry.id ? "active" : ""
|
||||
}`}
|
||||
onClick={() => handleNavClick(industry.id)}
|
||||
>
|
||||
{industry.name}
|
||||
</li>
|
||||
))}
|
||||
</div>
|
||||
</ul>
|
||||
<div className="resume-interview-navigation-wrapper">
|
||||
{showLeftBtn && (
|
||||
<button
|
||||
className="nav-scroll-btn nav-scroll-btn-left"
|
||||
onClick={() => scrollNav('left')}
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
)}
|
||||
<ul className="resume-interview-navigation" ref={navRef}>
|
||||
<div className="navigation-tabs">
|
||||
{pageData.industries.map((industry) => (
|
||||
<li
|
||||
key={industry.id}
|
||||
className={`resume-interview-navigation-item ${
|
||||
activeIndustry === industry.id ? "active" : ""
|
||||
}`}
|
||||
onClick={() => handleNavClick(industry.id)}
|
||||
>
|
||||
{industry.name}
|
||||
</li>
|
||||
))}
|
||||
</div>
|
||||
</ul>
|
||||
{showRightBtn && (
|
||||
<button
|
||||
className="nav-scroll-btn nav-scroll-btn-right"
|
||||
onClick={() => scrollNav('right')}
|
||||
>
|
||||
›
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<ul className="resume-interview-content-wrapper">
|
||||
{pageData.industries.map((item) => (
|
||||
<li
|
||||
@@ -169,19 +283,39 @@ const ResumeInterviewPage = () => {
|
||||
<p className="item-subtitle">简历与面试题</p>
|
||||
<div className="item-content-wrapper">
|
||||
<ul className="jobs-list">
|
||||
{filterPositions(item.positions).map((position) => (
|
||||
<li
|
||||
className="job-item job-item-change"
|
||||
key={position.id}
|
||||
onClick={() => handlePositionClick(position, item)}
|
||||
>
|
||||
<span>{position.level}</span>
|
||||
<div className="job-name">
|
||||
<p>{position.title}</p>
|
||||
<span>详情 ></span>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
{filterPositions(item.positions).map((position) => {
|
||||
const positionInfo = getPositionInfo(position.title);
|
||||
return (
|
||||
<li
|
||||
className="job-item job-item-change"
|
||||
key={position.id}
|
||||
onClick={() => handlePositionClick(position, item)}
|
||||
>
|
||||
<div className="job-avatar-wrapper">
|
||||
{positionInfo.avatar ? (
|
||||
<img
|
||||
src={positionInfo.avatar}
|
||||
alt={position.title}
|
||||
className="job-avatar"
|
||||
/>
|
||||
) : (
|
||||
<div className="job-avatar-placeholder" />
|
||||
)}
|
||||
</div>
|
||||
<div className="job-info">
|
||||
<img
|
||||
src={positionInfo.tagImage}
|
||||
alt={positionInfo.levelName}
|
||||
className="job-level-tag"
|
||||
/>
|
||||
<div className="job-name">
|
||||
<p>岗位名称:{position.title}</p>
|
||||
<span className="job-arrow">›</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<ul className="resumes-list">
|
||||
{item.questions.map((question) => (
|
||||
@@ -192,6 +326,11 @@ const ResumeInterviewPage = () => {
|
||||
handleQuestionClick({ ...item, questions: question.subQuestions || [question] })
|
||||
}
|
||||
>
|
||||
<img
|
||||
src={QuestionIcon}
|
||||
alt="question"
|
||||
className="question-icon"
|
||||
/>
|
||||
<p>{question.question}</p>
|
||||
</li>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user