feat: 改为卡片下方直接展开动画效果
- 移除弹窗式展示,改为在面试状态卡片下方直接展开 - 创建新的InterviewStatusAnimation组件,只展示动画 - 点击面试状态按钮后在卡片下方展开动画区域 - 移除标题和描述文字,界面更简洁 - 支持点击同一按钮收起动画 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,33 @@
|
|||||||
|
.interview-status-animation-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 0;
|
||||||
|
background: linear-gradient(135deg, #f7faff 0%, #ffffff 100%);
|
||||||
|
border-top: 1px solid #e5e6eb;
|
||||||
|
animation: slideDown 0.3s ease-out;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideDown {
|
||||||
|
from {
|
||||||
|
max-height: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
max-height: 200px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animation-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 180px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animation-container svg {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
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 ({ statusText, isOpen }) => {
|
||||||
|
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-animation-wrapper">
|
||||||
|
<div className="animation-container" ref={animationContainer} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -136,12 +136,18 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
.interview-item-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
.company-jobs-page-interview-item {
|
.company-jobs-page-interview-item {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid #e5e6eb;
|
border: 1px solid #e5e6eb;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Spin, Empty } from "@arco-design/web-react";
|
|||||||
import InfiniteScroll from "@/components/InfiniteScroll";
|
import InfiniteScroll from "@/components/InfiniteScroll";
|
||||||
import toast from "@/components/Toast";
|
import toast from "@/components/Toast";
|
||||||
import JobList from "./components/JobList";
|
import JobList from "./components/JobList";
|
||||||
import InterviewStatusDropdown from "./components/InterviewStatusDropdown";
|
import InterviewStatusAnimation from "./components/InterviewStatusAnimation";
|
||||||
import {
|
import {
|
||||||
getCompanyJobsPageData,
|
getCompanyJobsPageData,
|
||||||
getJobsList,
|
getJobsList,
|
||||||
@@ -26,9 +26,7 @@ const CompanyJobsPage = () => {
|
|||||||
const [interviewsHasMore, setInterviewsHasMore] = useState(true);
|
const [interviewsHasMore, setInterviewsHasMore] = useState(true);
|
||||||
const [initialDataLoaded, setInitialDataLoaded] = useState(false);
|
const [initialDataLoaded, setInitialDataLoaded] = useState(false);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
const [expandedItemId, setExpandedItemId] = useState(null);
|
||||||
const [selectedInterview, setSelectedInterview] = useState(null);
|
|
||||||
const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0 });
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
// 初始化页面数据 - 使用聚合接口
|
// 初始化页面数据 - 使用聚合接口
|
||||||
@@ -127,25 +125,12 @@ const CompanyJobsPage = () => {
|
|||||||
// 处理面试状态点击
|
// 处理面试状态点击
|
||||||
const handleStatusClick = (e, item) => {
|
const handleStatusClick = (e, item) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const rect = e.currentTarget.getBoundingClientRect();
|
// 如果点击的是已展开的项,则收起;否则展开新项
|
||||||
|
if (expandedItemId === item.id) {
|
||||||
// 计算下拉栏位置,让它在按钮下方居中
|
setExpandedItemId(null);
|
||||||
const dropdownWidth = 360;
|
} else {
|
||||||
const buttonCenter = rect.left + rect.width / 2;
|
setExpandedItemId(item.id);
|
||||||
const left = Math.max(10, buttonCenter - dropdownWidth / 2);
|
}
|
||||||
|
|
||||||
setDropdownPosition({
|
|
||||||
top: rect.bottom + 5,
|
|
||||||
left: left
|
|
||||||
});
|
|
||||||
setSelectedInterview(item);
|
|
||||||
setDropdownOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 关闭下拉栏
|
|
||||||
const handleCloseDropdown = () => {
|
|
||||||
setDropdownOpen(false);
|
|
||||||
setSelectedInterview(null);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取企业内推岗位 - 用于分页加载更多
|
// 获取企业内推岗位 - 用于分页加载更多
|
||||||
@@ -259,10 +244,8 @@ const CompanyJobsPage = () => {
|
|||||||
className="company-jobs-page-interview-list"
|
className="company-jobs-page-interview-list"
|
||||||
>
|
>
|
||||||
{interviews.map((item) => (
|
{interviews.map((item) => (
|
||||||
<li
|
<div key={item.id} className="interview-item-wrapper">
|
||||||
className="company-jobs-page-interview-item"
|
<li className="company-jobs-page-interview-item">
|
||||||
key={item.id}
|
|
||||||
>
|
|
||||||
<div className="company-jobs-page-interview-item-info">
|
<div className="company-jobs-page-interview-item-info">
|
||||||
<p className="company-jobs-page-interview-item-info-position">
|
<p className="company-jobs-page-interview-item-info-position">
|
||||||
{item.position}
|
{item.position}
|
||||||
@@ -297,6 +280,11 @@ const CompanyJobsPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
<InterviewStatusAnimation
|
||||||
|
statusText={item.statusText}
|
||||||
|
isOpen={expandedItemId === item.id}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
</div>
|
</div>
|
||||||
@@ -304,14 +292,7 @@ const CompanyJobsPage = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* 面试状态下拉栏 */}
|
|
||||||
<InterviewStatusDropdown
|
|
||||||
status={selectedInterview?.status}
|
|
||||||
statusText={selectedInterview?.statusText}
|
|
||||||
isOpen={dropdownOpen}
|
|
||||||
onClose={handleCloseDropdown}
|
|
||||||
position={dropdownPosition}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user