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

View File

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

View File

@@ -1,57 +1,77 @@
[
{
"查询岗位名称": "HR人事专员",
"阶段日期": "面试未通过2025/8/22 14:12",
"面试状态": "面试未通过,岗位内推结束"
},
{
"查询岗位名称": "商业会展执行专员",
"阶段日期": "2025/7/30",
"面试状态": "拒绝Offer"
"阶段日期": "Offer已拒绝2025/7/30 09:45",
"面试状态": "Offer已拒绝,岗位内推结束"
},
{
"查询岗位名称": "活动策划师",
"阶段日期": "2025/7/26",
"面试状态": "收到Offer"
"阶段日期": "Offer已拒绝2025/7/16 14:33",
"面试状态": "Offer已拒绝,岗位内推结束"
},
{
"查询岗位名称": "SEO专员",
"阶段日期": "2025/7/28",
"面试状态": "拒绝Offer"
"阶段日期": "Offer已拒绝2025/7/28 17:01",
"面试状态": "Offer已拒绝,岗位内推结束"
},
{
"查询岗位名称": "新媒体运营专员",
"阶段日期": "2025/7/31",
"面试状态": "拒绝Offer"
"阶段日期": "Offer已拒绝2025/7/31 14:38",
"面试状态": "Offer已拒绝,岗位内推结束"
},
{
"查询岗位名称": "ip运营",
"阶段日期": "2025/7/10",
"面试状态": "收到Offer"
"阶段日期": "Offer已拒绝2025/7/10 11:13",
"面试状态": "Offer已拒绝,岗位内推结束"
},
{
"查询岗位名称": "二次元周边店店长",
"阶段日期": "2025/6/27",
"面试状态": "HR初筛未通过"
"阶段日期": "简历未通过:2025/6/27 15:45",
"面试状态": "简历未通过,岗位内推结束"
},
{
"查询岗位名称": "社群运营",
"阶段日期": "2025/7/23",
"面试状态": "收到Offer"
"阶段日期": "简历未通过:2025/7/23 11:10",
"面试状态": "简历未通过,岗位内推结束"
},
{
"查询岗位名称": "品牌推广专员",
"阶段日期": "面试未通过2025/8/2 13:32",
"面试状态": "面试未通过,岗位内推结束"
},
{
"查询岗位名称": "会展策划师",
"阶段日期": "回复截至2025-09-12",
"面试状态": "等待HR通知"
"阶段日期": "Offer已接收2025/9/11 16:39",
"面试状态": "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%;
height: 180px;
display: flex;
@@ -51,11 +67,14 @@
transition: transform 0.3s ease;
}
.interview-status-animation-wrapper.expanding .animation-container {
.interview-status-animation-wrapper.expanding .image-container {
transform: scale(1);
}
.animation-container svg {
.status-image {
max-width: 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 './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'),
// 状态到lottie动画文件的映射
const statusLottieMap = {
'Offer已拒绝岗位内推结束': '/lottie/interview-status/4-off_拒绝Offer.json',
'Offer已接收岗位内推结束': '/lottie/interview-status/4-on_收到Offer.json',
'Offer已接受岗位内推结束': '/lottie/interview-status/4-on_收到Offer.json',
'已收到Offer请于2天内答复': '/lottie/interview-status/4-on_收到Offer.json',
'面试日期已确定,等待面试': '/lottie/interview-status/4-on_收到Offer.json',
'简历未通过,岗位内推结束': '/lottie/interview-status/1-off_初筛未通过.json',
'未参与面试,岗位内推结束': '/lottie/interview-status/3-off_未参与面试.json',
'面试未通过,岗位内推结束': '/lottie/interview-status/2-off_面试未通过.json',
// 兼容其他可能的状态文本
'岗位内推结束': '/lottie/interview-status/1-off_初筛未通过.json', // 默认动画
};
export default ({ statusText, isOpen }) => {
const animationContainer = useRef(null);
const lottieInstance = useRef(null);
const [animationData, setAnimationData] = useState(null);
export default ({ statusText, isOpen, stageDate }) => {
const [isVisible, setIsVisible] = useState(false);
const [animationClass, setAnimationClass] = useState('');
const lottieContainer = useRef(null);
const lottieInstance = useRef(null);
useEffect(() => {
if (isOpen) {
@@ -38,47 +36,87 @@ export default ({ statusText, isOpen }) => {
}
}, [isOpen, isVisible]);
useEffect(() => {
if (isOpen && statusText) {
// 加载对应的动画数据
const loadAnimation = statusAnimationMap[statusText];
if (loadAnimation) {
loadAnimation().then(module => {
setAnimationData(module.default);
});
// 获取对应的lottie动画路径
const getLottiePath = (status, stageDate) => {
// 首先尝试精确匹配
if (statusLottieMap[status]) {
return statusLottieMap[status];
}
// 如果精确匹配失败,尝试模糊匹配
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['面试未通过,岗位内推结束'];
}
}
}, [isOpen, statusText]);
// 默认返回通用动画
return statusLottieMap['岗位内推结束'];
};
// 加载lottie动画
useEffect(() => {
if (animationData && animationContainer.current && isVisible) {
// 清之前的动画实例
if (isVisible && lottieContainer.current) {
// 清之前的动画实例
if (lottieInstance.current) {
lottieInstance.current.destroy();
}
// 创建新的动画实例
const lottiePath = getLottiePath(statusText, stageDate);
// 加载新的lottie动画
lottieInstance.current = lottie.loadAnimation({
container: animationContainer.current,
container: lottieContainer.current,
renderer: 'svg',
loop: true,
autoplay: true,
animationData: animationData
path: lottiePath
});
return () => {
if (lottieInstance.current) {
lottieInstance.current.destroy();
}
};
}
}, [animationData, isVisible]);
// 清理函数
return () => {
if (lottieInstance.current) {
lottieInstance.current.destroy();
lottieInstance.current = null;
}
};
}, [isVisible, statusText, stageDate]);
if (!isVisible) return null;
return (
<div className={`interview-status-animation-wrapper ${animationClass}`}>
<div className="animation-container" ref={animationContainer} />
<div className="lottie-container" ref={lottieContainer} />
</div>
);
};