import React, { useState, useEffect } from 'react'; import { Card, Row, Col, Tag, Typography, Button, message } from 'antd'; import { RightOutlined, LeftOutlined, EnvironmentOutlined } from '@ant-design/icons'; import { history } from '@umijs/max'; import { getJobTitleTreeSelect, getJobRecommend } from '@/services/common/jobTitle'; import { getGetInfoCache, saveGetInfoCache } from '@/utils/jobPortalAuth'; import { getAccessToken } from '@/access'; import { getUserInfo } from '@/services/session'; import JobPortalHeader from '@/components/JobPortalHeader'; import { getDictValueEnum } from '@/services/system/dict'; import { getDictLabel } from '@/utils/jobPortalDict'; import type { DictValueEnumObj } from '@/components/DictTag'; import './index.less'; const { Title, Text } = Typography; const educationMap: { [key: string]: string } = { '1': '大专', '2': '本科', '3': '硕士', '4': '博士', '0': '不限' }; const experienceMap: { [key: string]: string } = { '1': '1年以下', '2': '1-3年', '3': '3-5年', '4': '5-10年', '5': '10年以上', '0': '不限' }; const defaultJobs = [ { id: 1, title: 'Java高级开发工程师', company: '青岛科技发展有限公司', salary: '15k-25k', location: '青岛·李沧区', experience: '3-5年', education: '本科', tags: ['五险一金', '带薪年假', '年终奖'] }, { id: 2, title: '前端开发工程师', company: '青岛智能科技有限公司', salary: '12k-20k', location: '青岛·市南区', experience: '2-4年', education: '本科', tags: ['双休', '弹性工作', '股票期权'] }, { id: 3, title: 'Python开发工程师', company: '青岛数据科技公司', salary: '14k-22k', location: '青岛·崂山区', experience: '3-5年', education: '本科', tags: ['五险一金', '技术培训', '团建活动'] }, { id: 4, title: '产品经理', company: '青岛互联网科技有限公司', salary: '16k-30k', location: '青岛·市北区', experience: '3-6年', education: '本科', tags: ['五险一金', '双休', '带薪年假'] }, { id: 5, title: '测试工程师', company: '青岛软件科技有限公司', salary: '10k-18k', location: '青岛·黄岛区', experience: '2-5年', education: '本科', tags: ['五险一金', '带薪年假', '节日福利'] }, { id: 6, title: 'UI设计师', company: '青岛设计有限公司', salary: '11k-20k', location: '青岛·市南区', experience: '2-4年', education: '大专', tags: ['五险一金', '年终奖', '员工旅游'] }, { id: 7, title: '数据分析师', company: '青岛大数据科技公司', salary: '13k-25k', location: '青岛·崂山区', experience: '2-5年', education: '本科', tags: ['五险一金', '双休', '技能培训'] }, { id: 8, title: '算法工程师', company: '青岛人工智能科技公司', salary: '20k-35k', location: '青岛·崂山区', experience: '3-7年', education: '硕士', tags: ['五险一金', '股票期权', '技术交流'] } ]; const JobPortalPage: React.FC = () => { const [hoveredIndustry, setHoveredIndustry] = useState(''); const [currentPage, setCurrentPage] = useState(1); const [jobTitleData, setJobTitleData] = useState(null); const [jobRecommendData, setJobRecommendData] = useState(null); const [companyNatureEnum, setCompanyNatureEnum] = useState({}); const [loading, setLoading] = useState(false); const [itemsPerPage] = useState(6); const [totalPages, setTotalPages] = useState(1); const [activeJobTitle, setActiveJobTitle] = useState(''); const [jobTitles, setJobTitles] = useState(() => { // 初始化时先尝试从缓存读取 return (getGetInfoCache()?.user as any)?.jobTitles || []; }); // 缓存可能因 SSO 登录时序问题尚未写入,主动拉取一次 useEffect(() => { if (jobTitles.length > 0) return; const cached = (getGetInfoCache()?.user as any)?.jobTitles; if (cached?.length) { setJobTitles(cached); return; } // 未登录用户不请求 /api/getInfo,避免 401 触发强制跳转登录页 if (!getAccessToken()) return; getUserInfo({ skipErrorHandler: true }) .then((res: any) => { saveGetInfoCache(res as Record); const titles = (res?.user as any)?.jobTitles; if (titles?.length) { setJobTitles(titles); } }) .catch(() => { // 静默处理 }); }, []); useEffect(() => { const fetchJobTitleData = async () => { try { setLoading(true); const response = await getJobTitleTreeSelect(); setJobTitleData(response); if (response?.data && response.data.length > 0) { const pages = Math.ceil(response.data.length / itemsPerPage); setTotalPages(pages); setHoveredIndustry(response.data[0].id.toString()); } } catch (error) { message.error('获取职位数据失败'); } finally { setLoading(false); } }; const fetchJobRecommendData = async (jobTitle?: string) => { try { const response = await getJobRecommend(jobTitle ? { jobTitle } : undefined); setJobRecommendData(response); } catch { // 静默处理 } }; fetchJobTitleData(); fetchJobRecommendData(); getDictValueEnum('company_nature', false, true) .then(setCompanyNatureEnum) .catch(() => setCompanyNatureEnum({})); }, [itemsPerPage]); const handleIndustryHover = (industryId: string) => { setHoveredIndustry(industryId); }; const getCurrentProfessions = () => { if (!jobTitleData?.data) return []; const industry = jobTitleData.data.find((item: any) => item.id.toString() === hoveredIndustry); return industry?.children || []; }; const getCurrentPageIndustries = () => { if (!jobTitleData?.data) return []; const startIndex = (currentPage - 1) * itemsPerPage; return jobTitleData.data.slice(startIndex, startIndex + itemsPerPage); }; const getHoveredIndustryLabel = () => { return jobTitleData?.data?.find((item: any) => item.id.toString() === hoveredIndustry)?.label || ''; }; const formatSalary = (minSalary: number, maxSalary: number) => { if (minSalary && maxSalary) { return `${(minSalary / 1000).toFixed(0)}k-${(maxSalary / 1000).toFixed(0)}k`; } else if (minSalary) { return `${(minSalary / 1000).toFixed(0)}k+`; } else if (maxSalary) { return `面议-${(maxSalary / 1000).toFixed(0)}k`; } return '面议'; }; const handlePageChange = (direction: 'prev' | 'next') => { let nextPage = currentPage; if (direction === 'prev' && currentPage > 1) { nextPage = currentPage - 1; } else if (direction === 'next' && currentPage < totalPages) { nextPage = currentPage + 1; } else { return; } setCurrentPage(nextPage); if (jobTitleData?.data) { const firstOnPage = jobTitleData.data[(nextPage - 1) * itemsPerPage]; if (firstOnPage) { setHoveredIndustry(firstOnPage.id.toString()); } } }; const handleJobClick = (job: any) => { history.push(`/job-portal/detail?jobId=${job.jobId || job.id}`, { jobData: job }); }; const handleProfessionTagClick = (name: string) => { history.push(`/job-portal/list`, { queryParams: { name } }); }; const handleExpectationTagClick = (jobTitle: string) => { const newActive = activeJobTitle === jobTitle ? '' : jobTitle; setActiveJobTitle(newActive); const fetchJobRecommendData = async () => { try { const response = await getJobRecommend(newActive ? { jobTitle: newActive } : undefined); setJobRecommendData(response); } catch { // 静默处理 } }; fetchJobRecommendData(); }; const renderJobCards = () => { const jobs = jobRecommendData?.data; if (jobs?.length) { return jobs.map((job: any) => ( handleJobClick(job)} >
{job.isUrgent === 1 && ( <Tag color="red" style={{ marginRight: 8, fontSize: 12, lineHeight: '20px' }}>急聘</Tag> )} {job.jobTitle} {formatSalary(job.minSalary, job.maxSalary)}
{job.companyName}
 {job.jobLocation} {experienceMap[job.experience] || '不限'} {educationMap[job.education] || '不限'}
{job.jobCategory && {job.jobCategory}} {(job.companyNature || job.company?.companyNature) && ( {getDictLabel(companyNatureEnum, job.companyNature || job.company?.companyNature)} )} {job.vacancies && 招聘{job.vacancies}人}
)); } return defaultJobs.map(job => ( handleJobClick(job)} >
{job.title} {job.salary}
{job.company}
{job.location} {job.experience} {job.education}
{job.tags.map((tag, index) => ( {tag} ))}
)); }; return (
行业分类
{getCurrentPageIndustries().map((industry: any) => (
handleIndustryHover(industry.id.toString())} > {industry.label}
))}
{jobTitleData?.data && (
共 {jobTitleData.data.length} 个行业
)}
{getHoveredIndustryLabel() ? `${getHoveredIndustryLabel()} · 职位分类` : '职位分类'} 点击标签查看相关职位
{getCurrentProfessions().length > 0 ? ( getCurrentProfessions().map((profession: any) => (
{profession.label}
{profession.children?.map((subProfession: any) => ( handleProfessionTagClick(subProfession.label)} > {subProfession.label} ))}
)) ) : (
请从左侧选择行业查看职位分类
)}
热门职位推荐 {jobTitles.length > 0 && (
求职期望 {jobTitles.map((title) => ( handleExpectationTagClick(title)} > {title} ))}
)}
history.push('/job-portal/list')}> 查看更多 >
{renderJobCards()}
); }; export default JobPortalPage;