11
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
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
This commit is contained in:
@@ -3,39 +3,39 @@ import {
|
||||
Input,
|
||||
Select,
|
||||
Button,
|
||||
Typography,
|
||||
Tag,
|
||||
Avatar,
|
||||
Space,
|
||||
message,
|
||||
Dropdown,
|
||||
MenuProps,
|
||||
Badge
|
||||
} from 'antd';
|
||||
import {
|
||||
SearchOutlined,
|
||||
EnvironmentOutlined,
|
||||
UserOutlined,
|
||||
FileTextOutlined,
|
||||
HomeOutlined,
|
||||
LogoutOutlined,
|
||||
BellOutlined
|
||||
BellOutlined,
|
||||
ReadOutlined,
|
||||
LoginOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { history, useLocation, useModel } from '@umijs/max';
|
||||
import {
|
||||
ensureJobPortalLogin,
|
||||
isJobPortalLoggedIn,
|
||||
PORTAL_LOGIN_URL,
|
||||
} from '@/utils/jobPortalAuth';
|
||||
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 portalLogo from '@/assets/logo.png';
|
||||
import './index.less';
|
||||
|
||||
const { Text } = Typography;
|
||||
const { Option } = Select;
|
||||
|
||||
/** 人社门户首页(顶部 Logo 点击跳转) */
|
||||
const PORTAL_HOME_URL = 'http://218.31.252.15:9081/hrss-web-vue/home';
|
||||
|
||||
interface JobPortalHeaderProps {
|
||||
showSearch?: boolean; // 是否显示搜索区域
|
||||
showHotJobs?: boolean; // 是否显示热门职位
|
||||
@@ -77,34 +77,42 @@ const JobPortalHeader: React.FC<JobPortalHeaderProps> = ({
|
||||
};
|
||||
|
||||
const userName = getUserName();
|
||||
const loggedIn = isJobPortalLoggedIn();
|
||||
|
||||
// 判断激活导航(简化为 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');
|
||||
const isPolicy = location.pathname.startsWith('/job-portal/policy');
|
||||
|
||||
// 获取未读消息数量
|
||||
// 获取未读消息数量(仅登录用户)
|
||||
useEffect(() => {
|
||||
if (!loggedIn) {
|
||||
setUnreadCount(0);
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchUnreadCount = async () => {
|
||||
if (!isJobPortalLoggedIn()) {
|
||||
setUnreadCount(0);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
|
||||
@@ -112,13 +120,35 @@ const JobPortalHeader: React.FC<JobPortalHeaderProps> = ({
|
||||
clearInterval(interval);
|
||||
window.removeEventListener('messageCountUpdated', handleMessageCountUpdate);
|
||||
};
|
||||
}, []);
|
||||
}, [loggedIn]);
|
||||
|
||||
// 背景图片配置
|
||||
const backgroundImages = [topBg1, topBg2, topBg3, topBg4];
|
||||
|
||||
// 热门职位
|
||||
const hotJobs = ['Java', '产品经理', '前端开发工程师', '测试工程师', '运维工程师', '数据分析师', '平面设计'];
|
||||
// 热门职位(兜底)
|
||||
const hotJobs = ['Java', '产品经理', '前端开发', '测试工程师', '运维工程师', '数据分析'];
|
||||
|
||||
const HOT_JOB_DISPLAY_MAX = 12;
|
||||
const HOT_JOB_SHOW_COUNT = 6;
|
||||
|
||||
const formatHotJobLabel = (title: string) => {
|
||||
const text = (title || '').trim();
|
||||
if (text.length <= HOT_JOB_DISPLAY_MAX) return text;
|
||||
return `${text.slice(0, HOT_JOB_DISPLAY_MAX)}…`;
|
||||
};
|
||||
|
||||
const getHotJobItems = (): { key: string | number; title: string }[] => {
|
||||
if (jobRecommendData?.data?.length) {
|
||||
return jobRecommendData.data.slice(0, HOT_JOB_SHOW_COUNT).map((job: any) => ({
|
||||
key: job.jobId ?? job.jobTitle,
|
||||
title: job.jobTitle || '',
|
||||
}));
|
||||
}
|
||||
return hotJobs.slice(0, HOT_JOB_SHOW_COUNT).map((title) => ({
|
||||
key: title,
|
||||
title,
|
||||
}));
|
||||
};
|
||||
|
||||
// 背景图轮播效果
|
||||
useEffect(() => {
|
||||
@@ -181,47 +211,18 @@ const JobPortalHeader: React.FC<JobPortalHeaderProps> = ({
|
||||
history.push(path);
|
||||
};
|
||||
|
||||
// 退出登录
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
await logout();
|
||||
clearSessionToken();
|
||||
setRemoteMenu(null);
|
||||
message.success('退出登录成功');
|
||||
// history.push('/user/login');
|
||||
window.location.href = 'http://218.31.252.15:9081/hrss-web-vue/home';
|
||||
} catch (error) {
|
||||
message.error('退出登录失败');
|
||||
}
|
||||
const handleGoToLogin = () => {
|
||||
window.location.href = PORTAL_LOGIN_URL;
|
||||
};
|
||||
|
||||
// 下拉菜单点击处理
|
||||
const handleMenuClick: MenuProps['onClick'] = ({ key }) => {
|
||||
if (key === 'logout') {
|
||||
handleLogout();
|
||||
} else if (key === 'personal-center') {
|
||||
handleNavClick('/job-portal/personal-center');
|
||||
/** 需登录的导航:未登录弹窗提示 */
|
||||
const handleAuthNavClick = (path: string, actionHint: string) => {
|
||||
if (!ensureJobPortalLogin(actionHint)) {
|
||||
return;
|
||||
}
|
||||
handleNavClick(path);
|
||||
};
|
||||
|
||||
// 下拉菜单项
|
||||
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">
|
||||
{/* 背景图片轮播 */}
|
||||
@@ -237,108 +238,127 @@ const JobPortalHeader: React.FC<JobPortalHeaderProps> = ({
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 顶部导航栏 */}
|
||||
<div className="header-nav">
|
||||
<div className="nav-container">
|
||||
<div className="nav-left">
|
||||
{/* <div className="logo" onClick={() => handleNavClick('/job-portal')}> */}
|
||||
<div className="logo" onClick={() => handleNavClick('http://218.31.252.15:9081/hrss-web-vue/home', true)}>
|
||||
<span className="logo-text">石河子智慧就业</span>
|
||||
</div>
|
||||
{/* 顶部栏:问候 + 导航 + 快捷操作 */}
|
||||
<div className="top-utility-bar">
|
||||
<div className="utility-container">
|
||||
<div className="header-brand" onClick={() => { window.location.href = PORTAL_HOME_URL; }}>
|
||||
<img className="header-logo" src={portalLogo} alt="石城智慧就业" />
|
||||
<span className="header-title">石城智慧就业</span>
|
||||
</div>
|
||||
<div className="nav-right">
|
||||
<Space size="large">
|
||||
<nav className="top-nav">
|
||||
<Button
|
||||
type="text"
|
||||
icon={<HomeOutlined />}
|
||||
onClick={() => handleNavClick('/job-portal')}
|
||||
className={`nav-btn${isHome ? ' active' : ''}`}
|
||||
>
|
||||
找工作
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<FileTextOutlined />}
|
||||
onClick={() => handleAuthNavClick('/job-portal/resume', '查看简历')}
|
||||
className={`nav-btn${isResume ? ' active' : ''}`}
|
||||
>
|
||||
简历
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<ReadOutlined />}
|
||||
onClick={() => handleNavClick('/job-portal/policy')}
|
||||
className={`nav-btn${isPolicy ? ' active' : ''}`}
|
||||
>
|
||||
政策
|
||||
</Button>
|
||||
<Badge count={unreadCount > 0 ? unreadCount : 0} offset={[8, 0]} size="small">
|
||||
<Button
|
||||
type="text"
|
||||
icon={<HomeOutlined />}
|
||||
onClick={() => handleNavClick('/job-portal')}
|
||||
className={`nav-btn${isHome?' active':''}`}
|
||||
icon={<BellOutlined />}
|
||||
onClick={() => handleAuthNavClick('/job-portal/message', '查看消息')}
|
||||
className={`nav-btn${isMessage ? ' active' : ''}`}
|
||||
>
|
||||
首页
|
||||
消息
|
||||
</Button>
|
||||
</Badge>
|
||||
</nav>
|
||||
<div className="utility-actions">
|
||||
{loggedIn ? (
|
||||
<Button
|
||||
type="text"
|
||||
icon={<FileTextOutlined />}
|
||||
onClick={() => handleNavClick('/job-portal/resume')}
|
||||
className={`nav-btn${isResume?' active':''}`}
|
||||
icon={<UserOutlined />}
|
||||
onClick={() => handleNavClick('/job-portal/personal-center')}
|
||||
className={`nav-btn user-btn${isMine ? ' active' : ''}`}
|
||||
>
|
||||
简历
|
||||
{userName}
|
||||
</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={<LoginOutlined />}
|
||||
onClick={handleGoToLogin}
|
||||
className="nav-btn login-btn"
|
||||
>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<UserOutlined />}
|
||||
className={`nav-btn user-btn${isMine?' active':''}`}
|
||||
>
|
||||
{userName}
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</Space>
|
||||
登录
|
||||
</Button>
|
||||
)}
|
||||
</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 className="banner-section">
|
||||
<div className="banner-inner">
|
||||
<div className="search-section">
|
||||
<div className="search-panel">
|
||||
<div className="search-bar">
|
||||
<Select
|
||||
defaultValue="job-type"
|
||||
className="search-type-select"
|
||||
popupMatchSelectWidth={false}
|
||||
bordered={false}
|
||||
>
|
||||
<Option value="job-type">找工作</Option>
|
||||
</Select>
|
||||
<span className="search-divider" />
|
||||
<Input
|
||||
className="search-input"
|
||||
placeholder="搜索职位、公司"
|
||||
bordered={false}
|
||||
value={searchValue}
|
||||
onChange={handleSearchInputChange}
|
||||
onKeyPress={handleSearchKeyPress}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
className="search-submit"
|
||||
icon={<SearchOutlined />}
|
||||
onClick={handleSearch}
|
||||
>
|
||||
搜索
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{showHotJobs && (
|
||||
<div className="hot-jobs">
|
||||
<span className="hot-jobs-label">热门职位</span>
|
||||
<div className="hot-jobs-container">
|
||||
{getHotJobItems().map((item) => (
|
||||
<Tag
|
||||
key={item.key}
|
||||
className="hot-job-tag"
|
||||
title={item.title}
|
||||
onClick={() => handleHotJobClick(item.title)}
|
||||
>
|
||||
{formatHotJobLabel(item.title)}
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user