feat: Enhance Company and Job Portal functionality
- Added company nature selection to ManagementList and JobDetailPage. - Implemented detailed tags for job benefits and schedules in JobDetailPage and JobListPage. - Introduced CompanyInfoPage for managing company details, including nature, scale, and contacts. - Enhanced ManagementList to support salary composition, welfare benefits, and work schedule fields. - Created utility functions for parsing and serializing comma-separated tags. - Updated API services to fetch and update current company information. - Added tests for new utility functions to ensure correct parsing and serialization of tags.
This commit is contained in:
@@ -42,6 +42,7 @@ import JobComplaintModal from '@/components/JobComplaintModal';
|
||||
import { getDictValueEnum } from '@/services/system/dict';
|
||||
import { getCmsIndustryTreeList } from '@/services/classify/industry';
|
||||
import type { DictValueEnumObj } from '@/components/DictTag';
|
||||
import { parseCommaSeparatedTags } from '@/utils/jobDetailTags';
|
||||
|
||||
const { Title, Text, Paragraph } = Typography;
|
||||
|
||||
@@ -87,9 +88,13 @@ const transformJobData = (jobData: any) => {
|
||||
? formatSalary(jobData.minSalary, jobData.maxSalary)
|
||||
: (jobData.salary || '面议'),
|
||||
location: jobData.jobLocation || jobData.location,
|
||||
companyNature: jobData.companyNature || jobData.company?.companyNature || '',
|
||||
experience: jobData.experience ? experienceMap[jobData.experience] || jobData.experience : '不限',
|
||||
education: jobData.education ? educationMap[jobData.education] || jobData.education : '不限',
|
||||
tags: jobData.tags || ['五险一金', '带薪年假', '年终奖'],
|
||||
tags: Array.isArray(jobData.tags) ? jobData.tags : [],
|
||||
salaryComposition: parseCommaSeparatedTags(jobData.salaryComposition),
|
||||
welfareBenefits: parseCommaSeparatedTags(jobData.welfareBenefits),
|
||||
workSchedule: parseCommaSeparatedTags(jobData.workSchedule),
|
||||
publishTime: jobData.publishTime || new Date().toISOString().split('T')[0],
|
||||
description: jobData.description || `
|
||||
<h3>职位职责:</h3>
|
||||
@@ -137,7 +142,11 @@ const mockJobDetail = {
|
||||
location: '青岛·李沧区',
|
||||
experience: '3-5年',
|
||||
education: '本科',
|
||||
tags: ['五险一金', '带薪年假', '年终奖', '定期体检'],
|
||||
companyNature: '',
|
||||
tags: [],
|
||||
salaryComposition: [] as string[],
|
||||
welfareBenefits: [] as string[],
|
||||
workSchedule: [] as string[],
|
||||
publishTime: '2024-03-15',
|
||||
description: `
|
||||
<h3>职位职责:</h3>
|
||||
@@ -193,15 +202,27 @@ const JobDetailPage: React.FC = () => {
|
||||
{ item: '工作地', score: 0 }
|
||||
]);
|
||||
const [scaleEnum, setScaleEnum] = useState<DictValueEnumObj>({});
|
||||
const [companyNatureEnum, setCompanyNatureEnum] = useState<DictValueEnumObj>({});
|
||||
const [industryTree, setIndustryTree] = useState<any[]>([]);
|
||||
const [complaintVisible, setComplaintVisible] = useState(false);
|
||||
|
||||
const detailTagGroups = useMemo(
|
||||
() => [
|
||||
{ label: '薪资范围与构成', values: jobDetail.salaryComposition },
|
||||
{ label: '福利待遇', values: jobDetail.welfareBenefits },
|
||||
{ label: '工作时间安排', values: jobDetail.workSchedule },
|
||||
].filter((group) => group.values.length > 0),
|
||||
[jobDetail.salaryComposition, jobDetail.welfareBenefits, jobDetail.workSchedule],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([
|
||||
getDictValueEnum('scale', true, true),
|
||||
getDictValueEnum('company_nature', false, true),
|
||||
getCmsIndustryTreeList(),
|
||||
]).then(([scaleData, industryRes]) => {
|
||||
]).then(([scaleData, companyNatureData, industryRes]) => {
|
||||
setScaleEnum(scaleData);
|
||||
setCompanyNatureEnum(companyNatureData);
|
||||
if (industryRes?.code === 200 && industryRes?.data) {
|
||||
setIndustryTree(industryRes.data);
|
||||
}
|
||||
@@ -243,6 +264,11 @@ const JobDetailPage: React.FC = () => {
|
||||
setJobDetail(transformedJobData);
|
||||
// 根据 isCollection 字段设置收藏状态
|
||||
setIsFavorited(passedJobData?.isCollection !== null && passedJobData?.isCollection !== 0 && passedJobData?.isCollection !== undefined);
|
||||
// 列表卡片不携带详情标签;进入详情页后始终请求详情接口补齐完整岗位数据。
|
||||
const passedJobId = passedJobData.jobId || passedJobData.id || id;
|
||||
if (passedJobId) {
|
||||
fetchJobDetailFromApi(String(passedJobId));
|
||||
}
|
||||
} else {
|
||||
// 直接刷新页面时,从 URL 参数或 route param 获取 jobId,调 API 获取数据
|
||||
const jobIdFromUrl = searchParams.get('jobId');
|
||||
@@ -507,6 +533,11 @@ const JobDetailPage: React.FC = () => {
|
||||
</Text>
|
||||
</Space>
|
||||
<Space wrap>
|
||||
{jobDetail.companyNature && (
|
||||
<Tag color="blue">
|
||||
{getDictLabel(companyNatureEnum, jobDetail.companyNature)}
|
||||
</Tag>
|
||||
)}
|
||||
{jobDetail.tags.map((tag, index) => (
|
||||
<Tag key={index} color="blue">{tag}</Tag>
|
||||
))}
|
||||
@@ -549,6 +580,23 @@ const JobDetailPage: React.FC = () => {
|
||||
<Row gutter={16}>
|
||||
{/* 左侧内容区 */}
|
||||
<Col span={16}>
|
||||
{detailTagGroups.length > 0 && (
|
||||
<Card title="岗位待遇与工作安排" className="info-card job-detail-tags-card">
|
||||
<div className="job-detail-tag-groups">
|
||||
{detailTagGroups.map((group) => (
|
||||
<div className="job-detail-tag-group" key={group.label}>
|
||||
<div className="job-detail-tag-label">{group.label}</div>
|
||||
<Space wrap size={[8, 8]}>
|
||||
{group.values.map((value) => (
|
||||
<Tag color="blue" key={`${group.label}-${value}`}>{value}</Tag>
|
||||
))}
|
||||
</Space>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* 职位描述 */}
|
||||
<Card title="职位描述" className="info-card">
|
||||
<div
|
||||
@@ -697,4 +745,3 @@ const JobDetailPage: React.FC = () => {
|
||||
};
|
||||
|
||||
export default JobDetailPage;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user