314 lines
10 KiB
TypeScript
314 lines
10 KiB
TypeScript
|
|
import React, { useEffect, useRef, useState } from 'react';
|
|||
|
|
import { Button, Modal, Tag, message } from 'antd';
|
|||
|
|
import { PlusOutlined } from '@ant-design/icons';
|
|||
|
|
import type { ActionType, ProColumns } from '@ant-design/pro-components';
|
|||
|
|
import { ProTable } from '@ant-design/pro-components';
|
|||
|
|
import { getDictValueEnum } from '@/services/system/dict';
|
|||
|
|
import {
|
|||
|
|
getParticipatingCompanies,
|
|||
|
|
getParticipatingJobDetail,
|
|||
|
|
getParticipatingJobs,
|
|||
|
|
removeJobFromCompany,
|
|||
|
|
removeParticipatingCompany,
|
|||
|
|
} from '@/services/jobportal/outdoorFairDetail';
|
|||
|
|
import CompanyAddModal from './CompanyAddModal';
|
|||
|
|
import JobEditModal from './JobEditModal';
|
|||
|
|
|
|||
|
|
interface Props {
|
|||
|
|
fairId: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const ParticipatingCompaniesTab: React.FC<Props> = ({ fairId }) => {
|
|||
|
|
const companyActionRef = useRef<ActionType>();
|
|||
|
|
const jobActionRef = useRef<ActionType>();
|
|||
|
|
const [companyModalOpen, setCompanyModalOpen] = useState(false);
|
|||
|
|
const [jobListModalOpen, setJobListModalOpen] = useState(false);
|
|||
|
|
const [jobEditModalOpen, setJobEditModalOpen] = useState(false);
|
|||
|
|
const [selectedCompany, setSelectedCompany] =
|
|||
|
|
useState<API.OutdoorFairDetail.ParticipatingCompany | null>(null);
|
|||
|
|
const [editingJob, setEditingJob] = useState<API.OutdoorFairDetail.PostedJob | null>(null);
|
|||
|
|
const [educationEnum, setEducationEnum] = useState<any>({});
|
|||
|
|
const [experienceEnum, setExperienceEnum] = useState<any>({});
|
|||
|
|
const [areaEnum, setAreaEnum] = useState<any>({});
|
|||
|
|
const [jobTypeEnum, setJobTypeEnum] = useState<any>({});
|
|||
|
|
const [scaleEnum, setScaleEnum] = useState<any>({});
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
Promise.all([
|
|||
|
|
getDictValueEnum('education', true, true),
|
|||
|
|
getDictValueEnum('experience', true, true),
|
|||
|
|
getDictValueEnum('area', true, true),
|
|||
|
|
getDictValueEnum('job_type', true, true),
|
|||
|
|
getDictValueEnum('scale', true, true),
|
|||
|
|
]).then(([education, experience, area, jobType, scale]) => {
|
|||
|
|
setEducationEnum(education);
|
|||
|
|
setExperienceEnum(experience);
|
|||
|
|
setAreaEnum(area);
|
|||
|
|
setJobTypeEnum(jobType);
|
|||
|
|
setScaleEnum(scale);
|
|||
|
|
});
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
const openJobEditor = async (job?: API.OutdoorFairDetail.PostedJob) => {
|
|||
|
|
if (job?.jobId) {
|
|||
|
|
const res = await getParticipatingJobDetail(fairId, job.jobId);
|
|||
|
|
if (res.code !== 200) {
|
|||
|
|
message.error(res.msg || '岗位详情加载失败');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
setEditingJob(res.data);
|
|||
|
|
} else {
|
|||
|
|
setEditingJob(null);
|
|||
|
|
}
|
|||
|
|
setJobEditModalOpen(true);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const jobColumns: ProColumns<API.OutdoorFairDetail.PostedJob>[] = [
|
|||
|
|
{ title: '岗位名称', dataIndex: 'jobTitle', ellipsis: true },
|
|||
|
|
{
|
|||
|
|
title: '薪资范围',
|
|||
|
|
hideInSearch: true,
|
|||
|
|
render: (_, record) => `${record.minSalary}-${record.maxSalary} 元/月`,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
title: '学历要求',
|
|||
|
|
dataIndex: 'education',
|
|||
|
|
valueType: 'select',
|
|||
|
|
valueEnum: educationEnum,
|
|||
|
|
width: 110,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
title: '工作经验',
|
|||
|
|
dataIndex: 'experience',
|
|||
|
|
valueType: 'select',
|
|||
|
|
valueEnum: experienceEnum,
|
|||
|
|
width: 110,
|
|||
|
|
},
|
|||
|
|
{ title: '招聘人数', dataIndex: 'vacancies', hideInSearch: true, width: 90 },
|
|||
|
|
{
|
|||
|
|
title: '发布状态',
|
|||
|
|
dataIndex: 'isPublish',
|
|||
|
|
hideInSearch: true,
|
|||
|
|
width: 90,
|
|||
|
|
render: (_, record) => (
|
|||
|
|
<Tag color={record.isPublish === 1 ? 'green' : 'red'}>
|
|||
|
|
{record.isPublish === 1 ? '已发布' : '未发布'}
|
|||
|
|
</Tag>
|
|||
|
|
),
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
title: '操作',
|
|||
|
|
valueType: 'option',
|
|||
|
|
width: 130,
|
|||
|
|
render: (_, record) => [
|
|||
|
|
<Button key="edit" type="link" size="small" onClick={() => openJobEditor(record)}>
|
|||
|
|
编辑
|
|||
|
|
</Button>,
|
|||
|
|
<Button
|
|||
|
|
key="delete"
|
|||
|
|
type="link"
|
|||
|
|
size="small"
|
|||
|
|
danger
|
|||
|
|
onClick={() =>
|
|||
|
|
Modal.confirm({
|
|||
|
|
title: '确认删除',
|
|||
|
|
content: `确定删除岗位「${record.jobTitle}」吗?`,
|
|||
|
|
onOk: async () => {
|
|||
|
|
const res = await removeJobFromCompany(fairId, record.jobId!);
|
|||
|
|
if (res.code === 200) {
|
|||
|
|
message.success('删除成功');
|
|||
|
|
jobActionRef.current?.reload();
|
|||
|
|
companyActionRef.current?.reload();
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
>
|
|||
|
|
删除
|
|||
|
|
</Button>,
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<>
|
|||
|
|
<ProTable<API.OutdoorFairDetail.ParticipatingCompany>
|
|||
|
|
actionRef={companyActionRef}
|
|||
|
|
rowKey="id"
|
|||
|
|
headerTitle="参会企业"
|
|||
|
|
search={{ labelWidth: 'auto' }}
|
|||
|
|
options={false}
|
|||
|
|
pagination={{ pageSize: 10 }}
|
|||
|
|
toolBarRender={() => [
|
|||
|
|
<Button key="add" type="primary" icon={<PlusOutlined />} onClick={() => setCompanyModalOpen(true)}>
|
|||
|
|
添加企业
|
|||
|
|
</Button>,
|
|||
|
|
]}
|
|||
|
|
request={async (params) => {
|
|||
|
|
const res = await getParticipatingCompanies(fairId, {
|
|||
|
|
current: params.current,
|
|||
|
|
pageSize: params.pageSize,
|
|||
|
|
companyName: params.companyName,
|
|||
|
|
});
|
|||
|
|
return { data: res.rows || [], total: res.total || 0, success: res.code === 200 };
|
|||
|
|
}}
|
|||
|
|
columns={[
|
|||
|
|
{ title: '企业名称', dataIndex: 'companyName', ellipsis: true },
|
|||
|
|
{
|
|||
|
|
title: '行业',
|
|||
|
|
dataIndex: 'industry',
|
|||
|
|
hideInSearch: true,
|
|||
|
|
render: (_, record) => {
|
|||
|
|
const v = record.industry;
|
|||
|
|
if (!v) return '--';
|
|||
|
|
// 行业分类 id 均为 6 位编号,纯数字裸码(如 4、6)无对应分类项,兜底为“其他”
|
|||
|
|
if (/^\d+$/.test(String(v))) return <Tag>其他</Tag>;
|
|||
|
|
return <Tag>{v}</Tag>;
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
title: '规模',
|
|||
|
|
dataIndex: 'scale',
|
|||
|
|
hideInSearch: true,
|
|||
|
|
render: (_, record) => {
|
|||
|
|
const v = record.scale;
|
|||
|
|
if (v === undefined || v === null || v === '') return '其他';
|
|||
|
|
// 规模为字典码,渲染为标签;匹配不到时兜底“其他”,不显示裸数字
|
|||
|
|
const label = scaleEnum[v]?.label || scaleEnum[v]?.text;
|
|||
|
|
return label ? <Tag>{label}</Tag> : '其他';
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{ title: '联系人', dataIndex: 'contactPerson', hideInSearch: true },
|
|||
|
|
{ title: '联系电话', dataIndex: 'contactPhone', hideInSearch: true },
|
|||
|
|
{
|
|||
|
|
title: '展位号',
|
|||
|
|
dataIndex: 'boothNumber',
|
|||
|
|
hideInSearch: true,
|
|||
|
|
render: (_, record) => record.boothNumber || '--',
|
|||
|
|
},
|
|||
|
|
{ title: '岗位数', dataIndex: 'jobCount', hideInSearch: true, width: 80 },
|
|||
|
|
{
|
|||
|
|
title: '操作',
|
|||
|
|
valueType: 'option',
|
|||
|
|
width: 220,
|
|||
|
|
render: (_, record) => [
|
|||
|
|
<Button
|
|||
|
|
key="jobs"
|
|||
|
|
type="link"
|
|||
|
|
size="small"
|
|||
|
|
onClick={() => {
|
|||
|
|
setSelectedCompany(record);
|
|||
|
|
setJobListModalOpen(true);
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
查看岗位
|
|||
|
|
</Button>,
|
|||
|
|
<Button
|
|||
|
|
key="addJob"
|
|||
|
|
type="link"
|
|||
|
|
size="small"
|
|||
|
|
onClick={() => {
|
|||
|
|
setSelectedCompany(record);
|
|||
|
|
openJobEditor();
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
添加岗位
|
|||
|
|
</Button>,
|
|||
|
|
<Button
|
|||
|
|
key="remove"
|
|||
|
|
type="link"
|
|||
|
|
size="small"
|
|||
|
|
danger
|
|||
|
|
onClick={() =>
|
|||
|
|
Modal.confirm({
|
|||
|
|
title: '确认移除',
|
|||
|
|
content: `确定移除「${record.companyName}」吗?`,
|
|||
|
|
onOk: async () => {
|
|||
|
|
const res = await removeParticipatingCompany(record.id, fairId);
|
|||
|
|
if (res.code === 200) {
|
|||
|
|
message.success('移除成功');
|
|||
|
|
companyActionRef.current?.reload();
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
>
|
|||
|
|
移除
|
|||
|
|
</Button>,
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
]}
|
|||
|
|
/>
|
|||
|
|
|
|||
|
|
<Modal
|
|||
|
|
title={selectedCompany ? `${selectedCompany.companyName} — 发布岗位` : '企业岗位'}
|
|||
|
|
open={jobListModalOpen}
|
|||
|
|
width={1100}
|
|||
|
|
footer={null}
|
|||
|
|
destroyOnClose
|
|||
|
|
onCancel={() => setJobListModalOpen(false)}
|
|||
|
|
>
|
|||
|
|
{selectedCompany && (
|
|||
|
|
<ProTable<API.OutdoorFairDetail.PostedJob>
|
|||
|
|
actionRef={jobActionRef}
|
|||
|
|
rowKey="jobId"
|
|||
|
|
search={{ labelWidth: 'auto' }}
|
|||
|
|
options={false}
|
|||
|
|
pagination={{ pageSize: 10 }}
|
|||
|
|
toolBarRender={() => [
|
|||
|
|
<Button key="add" type="primary" icon={<PlusOutlined />} onClick={() => openJobEditor()}>
|
|||
|
|
添加岗位
|
|||
|
|
</Button>,
|
|||
|
|
]}
|
|||
|
|
request={async (params) => {
|
|||
|
|
const res = await getParticipatingJobs(fairId, {
|
|||
|
|
companyId: selectedCompany.companyId,
|
|||
|
|
current: params.current,
|
|||
|
|
pageSize: params.pageSize,
|
|||
|
|
jobTitle: params.jobTitle,
|
|||
|
|
education: params.education,
|
|||
|
|
experience: params.experience,
|
|||
|
|
});
|
|||
|
|
return { data: res.rows || [], total: res.total || 0, success: res.code === 200 };
|
|||
|
|
}}
|
|||
|
|
columns={jobColumns}
|
|||
|
|
/>
|
|||
|
|
)}
|
|||
|
|
</Modal>
|
|||
|
|
|
|||
|
|
<CompanyAddModal
|
|||
|
|
open={companyModalOpen}
|
|||
|
|
fairId={fairId}
|
|||
|
|
onCancel={() => setCompanyModalOpen(false)}
|
|||
|
|
onSuccess={() => {
|
|||
|
|
setCompanyModalOpen(false);
|
|||
|
|
companyActionRef.current?.reload();
|
|||
|
|
}}
|
|||
|
|
/>
|
|||
|
|
|
|||
|
|
<JobEditModal
|
|||
|
|
open={jobEditModalOpen}
|
|||
|
|
fairId={fairId}
|
|||
|
|
company={selectedCompany}
|
|||
|
|
job={editingJob}
|
|||
|
|
educationEnum={educationEnum}
|
|||
|
|
experienceEnum={experienceEnum}
|
|||
|
|
areaEnum={areaEnum}
|
|||
|
|
jobTypeEnum={jobTypeEnum}
|
|||
|
|
onCancel={() => {
|
|||
|
|
setJobEditModalOpen(false);
|
|||
|
|
setEditingJob(null);
|
|||
|
|
}}
|
|||
|
|
onSuccess={() => {
|
|||
|
|
setJobEditModalOpen(false);
|
|||
|
|
setEditingJob(null);
|
|||
|
|
jobActionRef.current?.reload();
|
|||
|
|
companyActionRef.current?.reload();
|
|||
|
|
}}
|
|||
|
|
/>
|
|||
|
|
</>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export default ParticipatingCompaniesTab;
|