feat: 为岗位面试状态添加可点击下拉栏功能
- 创建InterviewStatusDropdown组件,显示面试状态动画 - 集成Lottie动画播放器,加载对应状态的动画文件 - 点击面试状态按钮后弹出下拉栏,展示动画和状态说明 - 添加11种不同面试状态的动画映射和描述文字 - 下拉栏支持点击遮罩层关闭 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1
src/assets/animations/interviewStatus/1-off_初筛未通过.json
Normal file
1
src/assets/animations/interviewStatus/1-off_初筛未通过.json
Normal file
File diff suppressed because one or more lines are too long
1
src/assets/animations/interviewStatus/1-on_hr评估中.json
Normal file
1
src/assets/animations/interviewStatus/1-on_hr评估中.json
Normal file
File diff suppressed because one or more lines are too long
1
src/assets/animations/interviewStatus/2-off_面试未通过.json
Normal file
1
src/assets/animations/interviewStatus/2-off_面试未通过.json
Normal file
File diff suppressed because one or more lines are too long
1
src/assets/animations/interviewStatus/2-on_到场面试.json
Normal file
1
src/assets/animations/interviewStatus/2-on_到场面试.json
Normal file
File diff suppressed because one or more lines are too long
1
src/assets/animations/interviewStatus/2-on_收到通知.json
Normal file
1
src/assets/animations/interviewStatus/2-on_收到通知.json
Normal file
File diff suppressed because one or more lines are too long
1
src/assets/animations/interviewStatus/2-wati_等待面试.json
Normal file
1
src/assets/animations/interviewStatus/2-wati_等待面试.json
Normal file
File diff suppressed because one or more lines are too long
1
src/assets/animations/interviewStatus/3-off_未参与面试.json
Normal file
1
src/assets/animations/interviewStatus/3-off_未参与面试.json
Normal file
File diff suppressed because one or more lines are too long
1
src/assets/animations/interviewStatus/3-wati_等待HR通知.json
Normal file
1
src/assets/animations/interviewStatus/3-wati_等待HR通知.json
Normal file
File diff suppressed because one or more lines are too long
1
src/assets/animations/interviewStatus/4-off_拒绝Offer.json
Normal file
1
src/assets/animations/interviewStatus/4-off_拒绝Offer.json
Normal file
File diff suppressed because one or more lines are too long
1
src/assets/animations/interviewStatus/4-on_收到Offer.json
Normal file
1
src/assets/animations/interviewStatus/4-on_收到Offer.json
Normal file
File diff suppressed because one or more lines are too long
1
src/assets/animations/interviewStatus/4-wati_等待回复.json
Normal file
1
src/assets/animations/interviewStatus/4-wati_等待回复.json
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,94 @@
|
||||
.interview-status-dropdown-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.interview-status-dropdown {
|
||||
position: absolute;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
|
||||
width: 360px;
|
||||
z-index: 1000;
|
||||
animation: slideDown 0.3s ease-out;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.interview-status-dropdown-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #e5e6eb;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
}
|
||||
|
||||
.interview-status-dropdown-header h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.interview-status-dropdown-header .close-btn {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.interview-status-dropdown-header .close-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.interview-status-dropdown-content {
|
||||
padding: 20px;
|
||||
background: #f7f8fa;
|
||||
}
|
||||
|
||||
.animation-container {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.status-description {
|
||||
padding: 16px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: #4e5969;
|
||||
border-left: 4px solid #667eea;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import lottie from 'lottie-web';
|
||||
import './index.css';
|
||||
|
||||
// 状态动画映射
|
||||
const statusAnimationMap = {
|
||||
'HR初筛未通过': () => import('@/assets/animations/interviewStatus/1-off_初筛未通过.json'),
|
||||
'HR评估中': () => import('@/assets/animations/interviewStatus/1-on_hr评估中.json'),
|
||||
'面试未通过': () => import('@/assets/animations/interviewStatus/2-off_面试未通过.json'),
|
||||
'到场面试': () => import('@/assets/animations/interviewStatus/2-on_到场面试.json'),
|
||||
'收到通知': () => import('@/assets/animations/interviewStatus/2-on_收到通知.json'),
|
||||
'等待面试': () => import('@/assets/animations/interviewStatus/2-wati_等待面试.json'),
|
||||
'未参与面试': () => import('@/assets/animations/interviewStatus/3-off_未参与面试.json'),
|
||||
'等待HR通知': () => import('@/assets/animations/interviewStatus/3-wati_等待HR通知.json'),
|
||||
'拒绝Offer': () => import('@/assets/animations/interviewStatus/4-off_拒绝Offer.json'),
|
||||
'收到Offer': () => import('@/assets/animations/interviewStatus/4-on_收到Offer.json'),
|
||||
'等待回复': () => import('@/assets/animations/interviewStatus/4-wati_等待回复.json'),
|
||||
};
|
||||
|
||||
export default ({ status, statusText, isOpen, onClose, position }) => {
|
||||
const animationContainer = useRef(null);
|
||||
const lottieInstance = useRef(null);
|
||||
const [animationData, setAnimationData] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen && statusText) {
|
||||
// 加载对应的动画数据
|
||||
const loadAnimation = statusAnimationMap[statusText];
|
||||
if (loadAnimation) {
|
||||
loadAnimation().then(module => {
|
||||
setAnimationData(module.default);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [isOpen, statusText]);
|
||||
|
||||
useEffect(() => {
|
||||
if (animationData && animationContainer.current && isOpen) {
|
||||
// 清除之前的动画实例
|
||||
if (lottieInstance.current) {
|
||||
lottieInstance.current.destroy();
|
||||
}
|
||||
|
||||
// 创建新的动画实例
|
||||
lottieInstance.current = lottie.loadAnimation({
|
||||
container: animationContainer.current,
|
||||
renderer: 'svg',
|
||||
loop: true,
|
||||
autoplay: true,
|
||||
animationData: animationData
|
||||
});
|
||||
|
||||
return () => {
|
||||
if (lottieInstance.current) {
|
||||
lottieInstance.current.destroy();
|
||||
}
|
||||
};
|
||||
}
|
||||
}, [animationData, isOpen]);
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="interview-status-dropdown-overlay" onClick={onClose} />
|
||||
<div className="interview-status-dropdown" style={{ top: position?.top, left: position?.left }}>
|
||||
<div className="interview-status-dropdown-header">
|
||||
<h3>{statusText}</h3>
|
||||
<button className="close-btn" onClick={onClose}>×</button>
|
||||
</div>
|
||||
<div className="interview-status-dropdown-content">
|
||||
<div className="animation-container" ref={animationContainer} />
|
||||
<div className="status-description">
|
||||
{getStatusDescription(statusText)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// 获取状态描述
|
||||
const getStatusDescription = (statusText) => {
|
||||
const descriptions = {
|
||||
'HR初筛未通过': '您的简历未通过HR初步筛选,建议优化简历内容后重新投递其他岗位。',
|
||||
'HR评估中': 'HR正在评估您的简历,请耐心等待结果通知。',
|
||||
'面试未通过': '很遗憾,您未通过本次面试。建议总结经验,继续努力!',
|
||||
'到场面试': '请准时到达面试地点,祝您面试顺利!',
|
||||
'收到通知': '恭喜!您已收到面试通知,请查看具体时间和地点。',
|
||||
'等待面试': '面试安排中,请保持手机畅通,等待具体通知。',
|
||||
'未参与面试': '您未能参加预定的面试,如需重新安排请联系HR。',
|
||||
'等待HR通知': '面试已完成,HR正在评估结果,请耐心等待。',
|
||||
'拒绝Offer': '您已拒绝该职位的Offer,祝您找到更合适的机会。',
|
||||
'收到Offer': '恭喜!您已收到正式Offer,请尽快确认是否接受。',
|
||||
'等待回复': '企业正在等待您对Offer的回复,请尽快做出决定。',
|
||||
};
|
||||
|
||||
return descriptions[statusText] || '状态更新中,请稍后查看。';
|
||||
};
|
||||
@@ -6,6 +6,7 @@ import { Spin, Empty } from "@arco-design/web-react";
|
||||
import InfiniteScroll from "@/components/InfiniteScroll";
|
||||
import toast from "@/components/Toast";
|
||||
import JobList from "./components/JobList";
|
||||
import InterviewStatusDropdown from "./components/InterviewStatusDropdown";
|
||||
import {
|
||||
getCompanyJobsPageData,
|
||||
getJobsList,
|
||||
@@ -25,6 +26,9 @@ const CompanyJobsPage = () => {
|
||||
const [interviewsHasMore, setInterviewsHasMore] = useState(true);
|
||||
const [initialDataLoaded, setInitialDataLoaded] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
const [selectedInterview, setSelectedInterview] = useState(null);
|
||||
const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0 });
|
||||
const navigate = useNavigate();
|
||||
|
||||
// 初始化页面数据 - 使用聚合接口
|
||||
@@ -120,6 +124,24 @@ const CompanyJobsPage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 处理面试状态点击
|
||||
const handleStatusClick = (e, item) => {
|
||||
e.stopPropagation();
|
||||
const rect = e.currentTarget.getBoundingClientRect();
|
||||
setDropdownPosition({
|
||||
top: rect.bottom + 5,
|
||||
left: rect.left
|
||||
});
|
||||
setSelectedInterview(item);
|
||||
setDropdownOpen(true);
|
||||
};
|
||||
|
||||
// 关闭下拉栏
|
||||
const handleCloseDropdown = () => {
|
||||
setDropdownOpen(false);
|
||||
setSelectedInterview(null);
|
||||
};
|
||||
|
||||
// 获取企业内推岗位 - 用于分页加载更多
|
||||
const fetchJobsList = async () => {
|
||||
// 如果初始数据还没加载完成,或者是第一页且已有初始数据,则跳过
|
||||
@@ -262,6 +284,8 @@ const CompanyJobsPage = () => {
|
||||
item.status !== "COMPLETED" &&
|
||||
"company-jobs-page-interview-item-btn-active"
|
||||
}`}
|
||||
onClick={(e) => handleStatusClick(e, item)}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
{item.statusText}
|
||||
</div>
|
||||
@@ -274,6 +298,14 @@ const CompanyJobsPage = () => {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{/* 面试状态下拉栏 */}
|
||||
<InterviewStatusDropdown
|
||||
status={selectedInterview?.status}
|
||||
statusText={selectedInterview?.statusText}
|
||||
isOpen={dropdownOpen}
|
||||
onClose={handleCloseDropdown}
|
||||
position={dropdownPosition}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user