feat: 优化班级排名展示并集成Lottie动画

- 限制班级排名详情页只展示前10名学员
- 替换面试状态数据为文旅产业15个岗位数据
- 将面试状态展开动画从静态图片改为Lottie动画
- 添加5个面试状态的Lottie动画文件

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
KQL
2025-09-23 19:57:04 +08:00
parent 6cae2e36f9
commit 3a054c4208
10 changed files with 1762 additions and 67 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -24,15 +24,15 @@ const ClassRankModal = ({ visible, onClose }) => {
rankings[0] || null, // 第1名 rankings[0] || null, // 第1名
rankings[2] || null, // 第3名 rankings[2] || null, // 第3名
]; ];
// 获取第4名及以后的数据 // 获取第4-10名的数据只展示前10名
const restRankings = rankings.slice(3); const restRankings = rankings.slice(3, 10);
return ( return (
<Modal visible={visible} onClose={onClose}> <Modal visible={visible} onClose={onClose}>
<div className="class-rank-modal"> <div className="class-rank-modal">
<div className="class-rank-modal-header"> <div className="class-rank-modal-header">
<h2 className="class-rank-modal-title"> <h2 className="class-rank-modal-title">
<span>班级排名</span> <span>班级排名前10名</span>
</h2> </h2>
<i className="close-icon" onClick={onClose} /> <i className="close-icon" onClick={onClose} />
</div> </div>

View File

@@ -1,57 +1,77 @@
[ [
{
"查询岗位名称": "HR人事专员",
"阶段日期": "面试未通过2025/8/22 14:12",
"面试状态": "面试未通过,岗位内推结束"
},
{ {
"查询岗位名称": "商业会展执行专员", "查询岗位名称": "商业会展执行专员",
"阶段日期": "2025/7/30", "阶段日期": "Offer已拒绝2025/7/30 09:45",
"面试状态": "拒绝Offer" "面试状态": "Offer已拒绝,岗位内推结束"
}, },
{ {
"查询岗位名称": "活动策划师", "查询岗位名称": "活动策划师",
"阶段日期": "2025/7/26", "阶段日期": "Offer已拒绝2025/7/16 14:33",
"面试状态": "收到Offer" "面试状态": "Offer已拒绝,岗位内推结束"
}, },
{ {
"查询岗位名称": "SEO专员", "查询岗位名称": "SEO专员",
"阶段日期": "2025/7/28", "阶段日期": "Offer已拒绝2025/7/28 17:01",
"面试状态": "拒绝Offer" "面试状态": "Offer已拒绝,岗位内推结束"
}, },
{ {
"查询岗位名称": "新媒体运营专员", "查询岗位名称": "新媒体运营专员",
"阶段日期": "2025/7/31", "阶段日期": "Offer已拒绝2025/7/31 14:38",
"面试状态": "拒绝Offer" "面试状态": "Offer已拒绝,岗位内推结束"
}, },
{ {
"查询岗位名称": "ip运营", "查询岗位名称": "ip运营",
"阶段日期": "2025/7/10", "阶段日期": "Offer已拒绝2025/7/10 11:13",
"面试状态": "收到Offer" "面试状态": "Offer已拒绝,岗位内推结束"
}, },
{ {
"查询岗位名称": "二次元周边店店长", "查询岗位名称": "二次元周边店店长",
"阶段日期": "2025/6/27", "阶段日期": "简历未通过:2025/6/27 15:45",
"面试状态": "HR初筛未通过" "面试状态": "简历未通过,岗位内推结束"
}, },
{ {
"查询岗位名称": "社群运营", "查询岗位名称": "社群运营",
"阶段日期": "2025/7/23", "阶段日期": "简历未通过:2025/7/23 11:10",
"面试状态": "收到Offer" "面试状态": "简历未通过,岗位内推结束"
},
{
"查询岗位名称": "品牌推广专员",
"阶段日期": "面试未通过2025/8/2 13:32",
"面试状态": "面试未通过,岗位内推结束"
}, },
{ {
"查询岗位名称": "会展策划师", "查询岗位名称": "会展策划师",
"阶段日期": "回复截至2025-09-12", "阶段日期": "Offer已接收2025/9/11 16:39",
"面试状态": "等待HR通知" "面试状态": "Offer已接收岗位内推结束"
}, },
{ {
"查询岗位名称": "客服", "查询岗位名称": "客服",
"阶段日期": "2025/8/2", "阶段日期": "Offer已拒绝2025/8/2 09:31",
"面试状态": "未参与面试" "面试状态": "Offer已拒绝岗位内推结束"
},
{
"查询岗位名称": "境外展会操作助理",
"阶段日期": "面试未通过2025/8/9 12:01",
"面试状态": "面试未通过,岗位内推结束"
}, },
{ {
"查询岗位名称": "海外活动策划专员", "查询岗位名称": "海外活动策划专员",
"阶段日期": "2025/9/2", "阶段日期": "面试未通过2025/7/17 20:18",
"面试状态": "等待回复" "面试状态": "面试未通过,岗位内推结束"
}, },
{ {
"查询岗位名称": "品牌公关", "查询岗位名称": "品牌公关",
"阶段日期": "面试完成2025-09-02", "阶段日期": "面试日期:2025/9/7 18:35",
"面试状态": "面试完成" "面试状态": "面试日期已确定,等待面试"
},
{
"查询岗位名称": "景区运营专员",
"阶段日期": "面试未通过2025/8/12 14:53",
"面试状态": "面试未通过,岗位内推结束"
} }
] ]

View File

@@ -40,7 +40,23 @@
} }
} }
.animation-container { .lottie-container {
width: 100%;
height: 136px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
transform: scale(0.9);
transition: transform 0.3s ease;
}
.interview-status-animation-wrapper.expanding .lottie-container {
transform: scale(1);
}
/* 保留原有的图片样式作为后备方案 */
.image-container {
width: 100%; width: 100%;
height: 180px; height: 180px;
display: flex; display: flex;
@@ -51,11 +67,14 @@
transition: transform 0.3s ease; transition: transform 0.3s ease;
} }
.interview-status-animation-wrapper.expanding .animation-container { .interview-status-animation-wrapper.expanding .image-container {
transform: scale(1); transform: scale(1);
} }
.animation-container svg { .status-image {
max-width: 100%; max-width: 100%;
max-height: 100%; max-height: 100%;
object-fit: contain;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
} }

View File

@@ -2,27 +2,25 @@ import React, { useState, useEffect, useRef } from 'react';
import lottie from 'lottie-web'; import lottie from 'lottie-web';
import './index.css'; import './index.css';
// 状态动画映射 // 状态到lottie动画文件的映射
const statusAnimationMap = { const statusLottieMap = {
'HR初筛未通过': () => import('@/assets/animations/interviewStatus/1-off_初筛未通过.json'), 'Offer已拒绝岗位内推结束': '/lottie/interview-status/4-off_拒绝Offer.json',
'HR评估中': () => import('@/assets/animations/interviewStatus/1-on_hr评估中.json'), 'Offer已接收岗位内推结束': '/lottie/interview-status/4-on_收到Offer.json',
'面试未通过': () => import('@/assets/animations/interviewStatus/2-off_面试未通过.json'), 'Offer已接受岗位内推结束': '/lottie/interview-status/4-on_收到Offer.json',
'到场面试': () => import('@/assets/animations/interviewStatus/2-on_到场面试.json'), '已收到Offer请于2天内答复': '/lottie/interview-status/4-on_收到Offer.json',
'收到通知': () => import('@/assets/animations/interviewStatus/2-on_收到通知.json'), '面试日期已确定,等待面试': '/lottie/interview-status/4-on_收到Offer.json',
'等待面试': () => import('@/assets/animations/interviewStatus/2-wati_等待面试.json'), '简历未通过,岗位内推结束': '/lottie/interview-status/1-off_初筛未通过.json',
'未参与面试': () => import('@/assets/animations/interviewStatus/3-off_未参与面试.json'), '未参与面试,岗位内推结束': '/lottie/interview-status/3-off_未参与面试.json',
'等待HR通知': () => import('@/assets/animations/interviewStatus/3-wati_等待HR通知.json'), '面试未通过,岗位内推结束': '/lottie/interview-status/2-off_面试未通过.json',
'拒绝Offer': () => import('@/assets/animations/interviewStatus/4-off_拒绝Offer.json'), // 兼容其他可能的状态文本
'收到Offer': () => import('@/assets/animations/interviewStatus/4-on_收到Offer.json'), '岗位内推结束': '/lottie/interview-status/1-off_初筛未通过.json', // 默认动画
'等待回复': () => import('@/assets/animations/interviewStatus/4-wati_等待回复.json'),
}; };
export default ({ statusText, isOpen }) => { export default ({ statusText, isOpen, stageDate }) => {
const animationContainer = useRef(null);
const lottieInstance = useRef(null);
const [animationData, setAnimationData] = useState(null);
const [isVisible, setIsVisible] = useState(false); const [isVisible, setIsVisible] = useState(false);
const [animationClass, setAnimationClass] = useState(''); const [animationClass, setAnimationClass] = useState('');
const lottieContainer = useRef(null);
const lottieInstance = useRef(null);
useEffect(() => { useEffect(() => {
if (isOpen) { if (isOpen) {
@@ -38,47 +36,87 @@ export default ({ statusText, isOpen }) => {
} }
}, [isOpen, isVisible]); }, [isOpen, isVisible]);
useEffect(() => { // 获取对应的lottie动画路径
if (isOpen && statusText) { const getLottiePath = (status, stageDate) => {
// 加载对应的动画数据 // 首先尝试精确匹配
const loadAnimation = statusAnimationMap[statusText]; if (statusLottieMap[status]) {
if (loadAnimation) { return statusLottieMap[status];
loadAnimation().then(module => {
setAnimationData(module.default);
});
} }
}
}, [isOpen, statusText]);
// 如果精确匹配失败,尝试模糊匹配
if (status && status.includes('简历未通过')) {
return statusLottieMap['简历未通过,岗位内推结束'];
}
if (status && status.includes('未参与面试')) {
return statusLottieMap['未参与面试,岗位内推结束'];
}
if (status && status.includes('面试未通过')) {
return statusLottieMap['面试未通过,岗位内推结束'];
}
if (status && status.includes('Offer已拒绝')) {
return statusLottieMap['Offer已拒绝岗位内推结束'];
}
if (status && (status.includes('Offer已接受') || status.includes('Offer已接收'))) {
return statusLottieMap['Offer已接收岗位内推结束'];
}
if (status && status.includes('面试日期已确定')) {
return statusLottieMap['面试日期已确定,等待面试'];
}
if (status && status.includes('已收到Offer')) {
return statusLottieMap['已收到Offer请于2天内答复'];
}
// 对于通用的"岗位内推结束"状态,根据阶段日期判断具体原因
if (status === '岗位内推结束' && stageDate) {
if (stageDate.includes('简历未通过')) {
return statusLottieMap['简历未通过,岗位内推结束'];
}
if (stageDate.includes('未参与面试')) {
return statusLottieMap['未参与面试,岗位内推结束'];
}
if (stageDate.includes('面试未通过')) {
return statusLottieMap['面试未通过,岗位内推结束'];
}
}
// 默认返回通用动画
return statusLottieMap['岗位内推结束'];
};
// 加载lottie动画
useEffect(() => { useEffect(() => {
if (animationData && animationContainer.current && isVisible) { if (isVisible && lottieContainer.current) {
// 清之前的动画实例 // 清之前的动画实例
if (lottieInstance.current) { if (lottieInstance.current) {
lottieInstance.current.destroy(); lottieInstance.current.destroy();
} }
// 创建新的动画实例 const lottiePath = getLottiePath(statusText, stageDate);
// 加载新的lottie动画
lottieInstance.current = lottie.loadAnimation({ lottieInstance.current = lottie.loadAnimation({
container: animationContainer.current, container: lottieContainer.current,
renderer: 'svg', renderer: 'svg',
loop: true, loop: true,
autoplay: true, autoplay: true,
animationData: animationData path: lottiePath
}); });
}
// 清理函数
return () => { return () => {
if (lottieInstance.current) { if (lottieInstance.current) {
lottieInstance.current.destroy(); lottieInstance.current.destroy();
lottieInstance.current = null;
} }
}; };
} }, [isVisible, statusText, stageDate]);
}, [animationData, isVisible]);
if (!isVisible) return null; if (!isVisible) return null;
return ( return (
<div className={`interview-status-animation-wrapper ${animationClass}`}> <div className={`interview-status-animation-wrapper ${animationClass}`}>
<div className="animation-container" ref={animationContainer} /> <div className="lottie-container" ref={lottieContainer} />
</div> </div>
); );
}; };

File diff suppressed because it is too large Load Diff