Some checks failed
Node CI / build (14.x, macOS-latest) (push) Has been cancelled
Node CI / build (14.x, ubuntu-latest) (push) Has been cancelled
Node CI / build (14.x, windows-latest) (push) Has been cancelled
Node CI / build (16.x, macOS-latest) (push) Has been cancelled
Node CI / build (16.x, ubuntu-latest) (push) Has been cancelled
Node CI / build (16.x, windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
coverage CI / build (push) Has been cancelled
Node pnpm CI / build (16.x, macOS-latest) (push) Has been cancelled
Node pnpm CI / build (16.x, ubuntu-latest) (push) Has been cancelled
Node pnpm CI / build (16.x, windows-latest) (push) Has been cancelled
342 lines
10 KiB
TypeScript
342 lines
10 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
||
import {
|
||
Input,
|
||
Select,
|
||
Button,
|
||
Typography,
|
||
Tag,
|
||
Avatar,
|
||
Space,
|
||
message,
|
||
Dropdown,
|
||
MenuProps,
|
||
Badge
|
||
} from 'antd';
|
||
import {
|
||
SearchOutlined,
|
||
EnvironmentOutlined,
|
||
UserOutlined,
|
||
FileTextOutlined,
|
||
HomeOutlined,
|
||
LogoutOutlined,
|
||
BellOutlined
|
||
} from '@ant-design/icons';
|
||
import { history, useLocation, useModel } from '@umijs/max';
|
||
import { getJobRecommend } from '@/services/common/jobTitle';
|
||
import { logout } from '@/services/system/auth';
|
||
import { clearSessionToken } from '@/access';
|
||
import { setRemoteMenu } from '@/services/session';
|
||
import { getMessageTotal } from '@/services/jobportal/user';
|
||
import topBg1 from '@/assets/images/top-bg1.png';
|
||
import topBg2 from '@/assets/images/top-bg2.png';
|
||
import topBg3 from '@/assets/images/top-bg3.png';
|
||
import topBg4 from '@/assets/images/top-bg4.png';
|
||
import './index.less';
|
||
|
||
const { Text } = Typography;
|
||
const { Option } = Select;
|
||
|
||
interface JobPortalHeaderProps {
|
||
showSearch?: boolean; // 是否显示搜索区域
|
||
showHotJobs?: boolean; // 是否显示热门职位
|
||
}
|
||
|
||
const JobPortalHeader: React.FC<JobPortalHeaderProps> = ({
|
||
showSearch = true,
|
||
showHotJobs = true
|
||
}) => {
|
||
const [searchValue, setSearchValue] = useState<string>('');
|
||
const [jobRecommendData, setJobRecommendData] = useState<any>(null);
|
||
const [currentBgIndex, setCurrentBgIndex] = useState<number>(0);
|
||
const [unreadCount, setUnreadCount] = useState<number>(0);
|
||
|
||
const location = useLocation();
|
||
|
||
// 获取当前登录用户信息
|
||
const { initialState } = useModel('@@initialState');
|
||
const currentUser = initialState?.currentUser;
|
||
|
||
// 获取用户名,优先使用 initialState,其次使用 localStorage 中的 userInfo
|
||
const getUserName = (): string => {
|
||
if (currentUser?.nickName) {
|
||
return currentUser.nickName;
|
||
}
|
||
// 尝试从 localStorage 获取用户信息
|
||
try {
|
||
const cachedUserInfo = localStorage.getItem('userInfo');
|
||
if (cachedUserInfo) {
|
||
const userInfo = JSON.parse(cachedUserInfo);
|
||
if (userInfo?.name) {
|
||
return userInfo.name;
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('读取用户信息失败:', error);
|
||
}
|
||
return '我的';
|
||
};
|
||
|
||
const userName = getUserName();
|
||
|
||
// 判断激活导航(简化为 startsWith 匹配)
|
||
const isHome = /^\/job-portal(\/(list|detail))?$/.test(location.pathname);
|
||
const isResume = location.pathname.startsWith('/job-portal/resume');
|
||
const isMine = location.pathname.startsWith('/job-portal/personal-center') || location.pathname.startsWith('/job-portal/profile');
|
||
const isMessage = location.pathname.startsWith('/job-portal/message');
|
||
|
||
// 获取未读消息数量
|
||
useEffect(() => {
|
||
const fetchUnreadCount = async () => {
|
||
try {
|
||
const response = await getMessageTotal();
|
||
if (response?.code === 200 && response?.data) {
|
||
setUnreadCount(response.data.wdxx || 0);
|
||
}
|
||
} catch (error) {
|
||
// 静默处理错误,不影响用户体验
|
||
console.error('获取未读消息数量失败:', error);
|
||
}
|
||
};
|
||
|
||
// 监听消息数量更新事件
|
||
const handleMessageCountUpdate = () => {
|
||
fetchUnreadCount();
|
||
};
|
||
window.addEventListener('messageCountUpdated', handleMessageCountUpdate);
|
||
|
||
// 每30秒刷新一次未读消息数量
|
||
fetchUnreadCount();
|
||
const interval = setInterval(fetchUnreadCount, 30000);
|
||
|
||
return () => {
|
||
clearInterval(interval);
|
||
window.removeEventListener('messageCountUpdated', handleMessageCountUpdate);
|
||
};
|
||
}, []);
|
||
|
||
// 背景图片配置
|
||
const backgroundImages = [topBg1, topBg2, topBg3, topBg4];
|
||
|
||
// 热门职位
|
||
const hotJobs = ['Java', '产品经理', '前端开发工程师', '测试工程师', '运维工程师', '数据分析师', '平面设计'];
|
||
|
||
// 背景图轮播效果
|
||
useEffect(() => {
|
||
const interval = setInterval(() => {
|
||
setCurrentBgIndex((prevIndex) => (prevIndex + 1) % backgroundImages.length);
|
||
}, 2000); // 每2秒切换一次
|
||
|
||
return () => clearInterval(interval);
|
||
}, [backgroundImages.length]);
|
||
|
||
// 获取推荐职位数据
|
||
React.useEffect(() => {
|
||
const fetchJobRecommendData = async () => {
|
||
try {
|
||
const response = await getJobRecommend();
|
||
setJobRecommendData(response);
|
||
} catch (error) {
|
||
// 静默处理错误,不影响用户体验
|
||
}
|
||
};
|
||
|
||
if (showHotJobs) {
|
||
fetchJobRecommendData();
|
||
}
|
||
}, [showHotJobs]);
|
||
|
||
// 处理搜索功能
|
||
const handleSearch = () => {
|
||
if (searchValue.trim()) {
|
||
// 跳转到职位列表页面,传递搜索参数
|
||
history.push(`/job-portal/list`, { queryParams: { name: searchValue.trim() } });
|
||
} else {
|
||
message.warning('请输入搜索关键词');
|
||
}
|
||
};
|
||
|
||
// 处理搜索框输入
|
||
const handleSearchInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||
setSearchValue(e.target.value);
|
||
};
|
||
|
||
// 处理回车搜索
|
||
const handleSearchKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||
if (e.key === 'Enter') {
|
||
handleSearch();
|
||
}
|
||
};
|
||
|
||
// 处理热门职位点击
|
||
const handleHotJobClick = (job: string) => {
|
||
history.push(`/job-portal/list`, { queryParams: { name: job } });
|
||
};
|
||
|
||
// 处理导航点击
|
||
const handleNavClick = (path: string) => {
|
||
history.push(path);
|
||
};
|
||
|
||
// 退出登录
|
||
const handleLogout = async () => {
|
||
try {
|
||
await logout();
|
||
clearSessionToken();
|
||
setRemoteMenu(null);
|
||
message.success('退出登录成功');
|
||
history.push('/user/login');
|
||
} catch (error) {
|
||
message.error('退出登录失败');
|
||
}
|
||
};
|
||
|
||
// 下拉菜单点击处理
|
||
const handleMenuClick: MenuProps['onClick'] = ({ key }) => {
|
||
if (key === 'logout') {
|
||
handleLogout();
|
||
} else if (key === 'personal-center') {
|
||
handleNavClick('/job-portal/personal-center');
|
||
}
|
||
};
|
||
|
||
// 下拉菜单项
|
||
const menuItems: MenuProps['items'] = [
|
||
{
|
||
key: 'personal-center',
|
||
icon: <UserOutlined />,
|
||
label: '个人中心',
|
||
},
|
||
{
|
||
type: 'divider',
|
||
},
|
||
{
|
||
key: 'logout',
|
||
icon: <LogoutOutlined />,
|
||
label: '退出登录',
|
||
danger: true,
|
||
},
|
||
];
|
||
|
||
return (
|
||
<div className="job-portal-header">
|
||
{/* 背景图片轮播 */}
|
||
<div className="background-carousel">
|
||
{backgroundImages.map((image, index) => (
|
||
<div
|
||
key={index}
|
||
className={`background-slide ${index === currentBgIndex ? 'active' : ''}`}
|
||
style={{
|
||
backgroundImage: `url(${image})`
|
||
}}
|
||
/>
|
||
))}
|
||
</div>
|
||
|
||
{/* 顶部导航栏 */}
|
||
<div className="header-nav">
|
||
<div className="nav-container">
|
||
<div className="nav-left">
|
||
<div className="logo" onClick={() => handleNavClick('/job-portal')}>
|
||
<span className="logo-text">喀什智慧就业服务平台</span>
|
||
</div>
|
||
</div>
|
||
<div className="nav-right">
|
||
<Space size="large">
|
||
<Button
|
||
type="text"
|
||
icon={<HomeOutlined />}
|
||
onClick={() => handleNavClick('/job-portal')}
|
||
className={`nav-btn${isHome?' active':''}`}
|
||
>
|
||
首页
|
||
</Button>
|
||
<Button
|
||
type="text"
|
||
icon={<FileTextOutlined />}
|
||
onClick={() => handleNavClick('/job-portal/resume')}
|
||
className={`nav-btn${isResume?' active':''}`}
|
||
>
|
||
简历
|
||
</Button>
|
||
<Badge count={unreadCount > 0 ? unreadCount : 0} offset={[10, 0]} size="small">
|
||
<Button
|
||
type="text"
|
||
icon={<BellOutlined />}
|
||
onClick={() => handleNavClick('/job-portal/message')}
|
||
className={`nav-btn${isMessage?' active':''}`}
|
||
>
|
||
消息
|
||
</Button>
|
||
</Badge>
|
||
<Dropdown
|
||
menu={{
|
||
items: menuItems,
|
||
onClick: handleMenuClick,
|
||
}}
|
||
placement="bottomRight"
|
||
>
|
||
<Button
|
||
type="text"
|
||
icon={<UserOutlined />}
|
||
className={`nav-btn user-btn${isMine?' active':''}`}
|
||
>
|
||
{userName}
|
||
</Button>
|
||
</Dropdown>
|
||
</Space>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 搜索区域 */}
|
||
{showSearch && (
|
||
<div className="search-section">
|
||
<div className="search-container">
|
||
<div className="search-bar">
|
||
<Select
|
||
defaultValue="职位类型"
|
||
style={{ width: 120 }}
|
||
bordered={false}
|
||
>
|
||
<Option value="job-type">职位类型</Option>
|
||
</Select>
|
||
<Input
|
||
placeholder="搜索职位、公司"
|
||
style={{ flex: 1, border: 'none' }}
|
||
bordered={false}
|
||
value={searchValue}
|
||
onChange={handleSearchInputChange}
|
||
onKeyPress={handleSearchKeyPress}
|
||
/>
|
||
<Button type="text" icon={<EnvironmentOutlined />} />
|
||
<Button type="primary" icon={<SearchOutlined />} onClick={handleSearch}>
|
||
搜索
|
||
</Button>
|
||
</div>
|
||
|
||
{/* 热门职位 */}
|
||
{showHotJobs && (
|
||
<div className="hot-jobs">
|
||
<Text strong>热门职位:</Text>
|
||
<div className="hot-jobs-container">
|
||
{(jobRecommendData?.data?.slice(0, 8) || hotJobs.slice(0, 7)).map((job: any, index: number) => (
|
||
<Tag
|
||
key={job.jobId || job}
|
||
className="hot-job-tag"
|
||
onClick={() => handleHotJobClick(job.jobTitle || job)}
|
||
>
|
||
{job.jobTitle || job}
|
||
</Tag>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default JobPortalHeader;
|