2026-01-11 10:00:56 +08:00
|
|
|
|
import React, { useRef, useState, useEffect } from 'react';
|
|
|
|
|
|
import { useAccess, history } from '@umijs/max';
|
2026-07-15 15:46:15 +08:00
|
|
|
|
import { Button, FormInstance, message, Modal, Tag } from 'antd';
|
2026-01-11 10:00:56 +08:00
|
|
|
|
import { ActionType, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
|
2026-07-15 15:46:15 +08:00
|
|
|
|
import {
|
|
|
|
|
|
PlusOutlined,
|
|
|
|
|
|
DeleteOutlined,
|
|
|
|
|
|
FormOutlined,
|
|
|
|
|
|
ExportOutlined,
|
|
|
|
|
|
EyeOutlined,
|
|
|
|
|
|
} from '@ant-design/icons';
|
2026-01-11 10:00:56 +08:00
|
|
|
|
import { getDictValueEnum } from '@/services/system/dict';
|
|
|
|
|
|
import DictTag from '@/components/DictTag';
|
|
|
|
|
|
import {
|
|
|
|
|
|
getPublicJobFairList,
|
|
|
|
|
|
getPublicJobFairDetail,
|
2026-07-15 15:46:15 +08:00
|
|
|
|
signupPublicJobFair,
|
2026-01-11 10:00:56 +08:00
|
|
|
|
addPublicJobFair,
|
|
|
|
|
|
updatePublicJobFair,
|
|
|
|
|
|
deletePublicJobFair,
|
|
|
|
|
|
exportPublicJobFair,
|
|
|
|
|
|
} from '@/services/jobfair/publicJobFair';
|
|
|
|
|
|
import EditModal from './components/EditModal';
|
2026-07-15 15:46:15 +08:00
|
|
|
|
import CompanyJobManageModal from './components/CompanyJobManageModal';
|
2026-07-23 18:35:18 +08:00
|
|
|
|
import FairExportModal from '../components/FairExportModal';
|
|
|
|
|
|
import type { RecruitmentFairExportParams } from '@/services/jobfair/recruitmentFairExport';
|
2026-01-11 10:00:56 +08:00
|
|
|
|
|
|
|
|
|
|
const PublicJobFairList: React.FC = () => {
|
|
|
|
|
|
const access = useAccess();
|
2026-07-15 15:46:15 +08:00
|
|
|
|
const isEnterprise = Boolean(access.isEnterprise);
|
2026-06-09 16:08:39 +08:00
|
|
|
|
const formRef = useRef<FormInstance>();
|
2026-01-11 10:00:56 +08:00
|
|
|
|
const actionRef = useRef<ActionType>();
|
|
|
|
|
|
const [modalVisible, setModalVisible] = useState(false);
|
|
|
|
|
|
const [currentRow, setCurrentRow] = useState<API.PublicJobFair.JobFairItem>();
|
2026-07-15 15:46:15 +08:00
|
|
|
|
const [companyJobModalOpen, setCompanyJobModalOpen] = useState(false);
|
|
|
|
|
|
const [currentJobFair, setCurrentJobFair] = useState<API.PublicJobFair.JobFairItem>();
|
2026-01-11 10:00:56 +08:00
|
|
|
|
const [jobFairTypeEnum, setJobFairTypeEnum] = useState<any>({});
|
2026-07-21 14:18:58 +08:00
|
|
|
|
const [jobFairRegionTypeEnum, setJobFairRegionTypeEnum] = useState<any>({});
|
2026-06-27 22:45:16 +08:00
|
|
|
|
const [yesNoEnum, setYesNoEnum] = useState<any>({});
|
2026-01-11 10:00:56 +08:00
|
|
|
|
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
2026-07-23 18:35:18 +08:00
|
|
|
|
const [exportModalOpen, setExportModalOpen] = useState(false);
|
2026-01-11 10:00:56 +08:00
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
getDictValueEnum('job_fair_type', true).then(setJobFairTypeEnum);
|
2026-07-21 14:18:58 +08:00
|
|
|
|
getDictValueEnum('job_fair_region_type').then(setJobFairRegionTypeEnum);
|
2026-06-27 22:45:16 +08:00
|
|
|
|
getDictValueEnum('sys_yes_no').then(setYesNoEnum);
|
2026-01-11 10:00:56 +08:00
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
const handleDelete = async (ids: string) => {
|
|
|
|
|
|
Modal.confirm({
|
|
|
|
|
|
title: '确认删除',
|
|
|
|
|
|
content: '确定要删除选中的招聘会吗?',
|
|
|
|
|
|
onOk: async () => {
|
|
|
|
|
|
const res = await deletePublicJobFair(ids);
|
|
|
|
|
|
if (res.code === 200) {
|
|
|
|
|
|
message.success('删除成功');
|
|
|
|
|
|
actionRef.current?.reload();
|
|
|
|
|
|
setSelectedRowKeys([]);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
message.error(res.msg || '删除失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-15 15:46:15 +08:00
|
|
|
|
const handleSignup = (record: API.PublicJobFair.JobFairItem) => {
|
|
|
|
|
|
Modal.confirm({
|
|
|
|
|
|
title: '报名线上招聘会',
|
|
|
|
|
|
content: `确定报名「${record.jobFairTitle}」吗?报名后需要等待管理员审核。`,
|
|
|
|
|
|
onOk: async () => {
|
|
|
|
|
|
const res = await signupPublicJobFair(record.jobFairId);
|
|
|
|
|
|
if (res.code === 200) {
|
|
|
|
|
|
message.success('报名成功,请等待管理员审核');
|
|
|
|
|
|
actionRef.current?.reload();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
message.error(res.msg || '报名失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-23 18:35:18 +08:00
|
|
|
|
const handleExport = async (params: RecruitmentFairExportParams) => {
|
2026-01-11 10:00:56 +08:00
|
|
|
|
message.loading('正在导出...');
|
|
|
|
|
|
try {
|
2026-07-23 18:35:18 +08:00
|
|
|
|
await exportPublicJobFair(params);
|
2026-01-11 10:00:56 +08:00
|
|
|
|
message.success('导出成功');
|
2026-07-23 18:35:18 +08:00
|
|
|
|
setExportModalOpen(false);
|
2026-01-11 10:00:56 +08:00
|
|
|
|
} catch {
|
|
|
|
|
|
message.error('导出失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-23 18:35:18 +08:00
|
|
|
|
const dictOptions = (valueEnum: Record<string, any>) =>
|
|
|
|
|
|
Object.entries(valueEnum).map(([value, item]) => ({
|
|
|
|
|
|
value,
|
|
|
|
|
|
label: item?.text || item?.label || value,
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
2026-01-11 10:00:56 +08:00
|
|
|
|
const columns: ProColumns<API.PublicJobFair.JobFairItem>[] = [
|
2026-06-09 16:08:39 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: '时间范围',
|
|
|
|
|
|
dataIndex: 'dateRange',
|
|
|
|
|
|
hideInTable: true,
|
|
|
|
|
|
valueType: 'dateRange',
|
|
|
|
|
|
fieldProps: {
|
|
|
|
|
|
format: 'YYYY-MM-DD',
|
|
|
|
|
|
},
|
|
|
|
|
|
search: {
|
|
|
|
|
|
transform: (value) => ({
|
|
|
|
|
|
startDate: value[0],
|
|
|
|
|
|
endDate: value[1],
|
|
|
|
|
|
}),
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
2026-01-11 10:00:56 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: '招聘会标题',
|
|
|
|
|
|
dataIndex: 'jobFairTitle',
|
|
|
|
|
|
ellipsis: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '地址',
|
|
|
|
|
|
dataIndex: 'jobFairAddress',
|
|
|
|
|
|
hideInSearch: true,
|
|
|
|
|
|
ellipsis: true,
|
|
|
|
|
|
},
|
2026-07-24 09:46:54 +08:00
|
|
|
|
/* 暂时隐藏线上招聘会列表中的招聘会类型字段,保留原实现便于后续恢复。
|
2026-01-11 10:00:56 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: '类型',
|
|
|
|
|
|
dataIndex: 'jobFairType',
|
|
|
|
|
|
valueType: 'select',
|
|
|
|
|
|
valueEnum: jobFairTypeEnum,
|
|
|
|
|
|
render: (_, record) => <DictTag enums={jobFairTypeEnum} value={record.jobFairType} />,
|
|
|
|
|
|
},
|
2026-07-24 09:46:54 +08:00
|
|
|
|
*/
|
2026-07-21 14:18:58 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: '区域类型',
|
|
|
|
|
|
dataIndex: 'jobFairRegionType',
|
|
|
|
|
|
valueType: 'select',
|
|
|
|
|
|
valueEnum: jobFairRegionTypeEnum,
|
|
|
|
|
|
render: (_, record) => (
|
|
|
|
|
|
<DictTag enums={jobFairRegionTypeEnum} value={record.jobFairRegionType} />
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '举办状态',
|
|
|
|
|
|
dataIndex: 'jobFairStatus',
|
|
|
|
|
|
hideInSearch: true,
|
|
|
|
|
|
render: (_, record) => {
|
|
|
|
|
|
const status = record.jobFairStatus;
|
|
|
|
|
|
if (!status) return '-';
|
|
|
|
|
|
const color = status === '未举办' ? 'blue' : status === '正在举办' ? 'green' : 'default';
|
|
|
|
|
|
return <Tag color={color}>{status}</Tag>;
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
2026-07-15 15:46:15 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: '报名审核状态',
|
|
|
|
|
|
dataIndex: 'enterpriseReviewStatus',
|
|
|
|
|
|
hideInSearch: true,
|
|
|
|
|
|
hideInTable: !isEnterprise,
|
|
|
|
|
|
render: (_, record) => {
|
|
|
|
|
|
if (record.isSignUp === undefined) return '-';
|
|
|
|
|
|
if (record.isSignUp !== '1') return <Tag>未报名</Tag>;
|
|
|
|
|
|
const statusMap: Record<string, { text: string; color: string }> = {
|
|
|
|
|
|
'0': { text: '待审核', color: 'gold' },
|
|
|
|
|
|
'1': { text: '已通过', color: 'green' },
|
|
|
|
|
|
'2': { text: '已驳回', color: 'red' },
|
|
|
|
|
|
};
|
|
|
|
|
|
const status = statusMap[record.enterpriseReviewStatus || '0'] || statusMap['0'];
|
|
|
|
|
|
return (
|
|
|
|
|
|
<span>
|
|
|
|
|
|
<Tag color={status.color}>{status.text}</Tag>
|
|
|
|
|
|
{record.enterpriseReviewComments && (
|
|
|
|
|
|
<span title={record.enterpriseReviewComments} style={{ color: '#999' }}>
|
|
|
|
|
|
(有意见)
|
|
|
|
|
|
</span>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
2026-06-27 22:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: '是否跨域',
|
|
|
|
|
|
dataIndex: 'isCrossDomain',
|
|
|
|
|
|
valueType: 'select',
|
|
|
|
|
|
valueEnum: yesNoEnum,
|
|
|
|
|
|
render: (_, record) => <DictTag enums={yesNoEnum} value={record.isCrossDomain} />,
|
|
|
|
|
|
},
|
2026-01-11 10:00:56 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: '开始时间',
|
|
|
|
|
|
dataIndex: 'jobFairStartTime',
|
|
|
|
|
|
valueType: 'dateTime',
|
|
|
|
|
|
hideInSearch: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '结束时间',
|
|
|
|
|
|
dataIndex: 'jobFairEndTime',
|
|
|
|
|
|
valueType: 'dateTime',
|
|
|
|
|
|
hideInSearch: true,
|
|
|
|
|
|
},
|
2026-06-09 16:08:39 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: '创建时间',
|
|
|
|
|
|
dataIndex: 'createTime',
|
|
|
|
|
|
valueType: 'dateTime',
|
|
|
|
|
|
hideInSearch: true,
|
|
|
|
|
|
},
|
2026-01-11 10:00:56 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: '操作',
|
|
|
|
|
|
valueType: 'option',
|
2026-07-15 15:46:15 +08:00
|
|
|
|
width: 360,
|
2026-01-11 11:35:06 +08:00
|
|
|
|
fixed: 'right',
|
2026-07-15 15:46:15 +08:00
|
|
|
|
render: (_, record) => {
|
|
|
|
|
|
const canViewDetail = !isEnterprise && access.hasPerms('cms:publicJobFair:query');
|
|
|
|
|
|
const canSignUp =
|
|
|
|
|
|
isEnterprise &&
|
|
|
|
|
|
access.hasPerms('cms:publicJobFair:signup') &&
|
|
|
|
|
|
(record.canSignUp ?? (record.isSignUp !== '1' || record.enterpriseReviewStatus === '2'));
|
|
|
|
|
|
const canEditCompanyJobs =
|
|
|
|
|
|
isEnterprise &&
|
|
|
|
|
|
access.hasPerms('cms:publicJobFair:job:edit') &&
|
|
|
|
|
|
(record.canEditJobs ?? record.enterpriseReviewStatus === '1');
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
canViewDetail && (
|
|
|
|
|
|
<Button
|
|
|
|
|
|
key="detail"
|
|
|
|
|
|
type="link"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
icon={<EyeOutlined />}
|
|
|
|
|
|
onClick={() => history.push(`/jobfair/public-job-fair/detail?id=${record.jobFairId}`)}
|
|
|
|
|
|
>
|
|
|
|
|
|
详情
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
),
|
|
|
|
|
|
canSignUp && (
|
|
|
|
|
|
<Button
|
|
|
|
|
|
key="signup"
|
|
|
|
|
|
type="link"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
icon={<PlusOutlined />}
|
|
|
|
|
|
onClick={() => handleSignup(record)}
|
|
|
|
|
|
>
|
|
|
|
|
|
报名
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
),
|
|
|
|
|
|
canEditCompanyJobs && (
|
|
|
|
|
|
<Button
|
|
|
|
|
|
key="companyJobs"
|
|
|
|
|
|
type="link"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
icon={<FormOutlined />}
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setCurrentJobFair(record);
|
|
|
|
|
|
setCompanyJobModalOpen(true);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
编辑报名岗位
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
),
|
|
|
|
|
|
<Button
|
|
|
|
|
|
key="edit"
|
|
|
|
|
|
type="link"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
icon={<FormOutlined />}
|
|
|
|
|
|
hidden={!access.hasPerms('cms:publicJobFair:edit')}
|
|
|
|
|
|
onClick={async () => {
|
|
|
|
|
|
const res = await getPublicJobFairDetail(record.jobFairId);
|
|
|
|
|
|
if (res.code === 200) {
|
|
|
|
|
|
setCurrentRow(res.data);
|
|
|
|
|
|
setModalVisible(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
编辑
|
|
|
|
|
|
</Button>,
|
|
|
|
|
|
<Button
|
|
|
|
|
|
key="delete"
|
|
|
|
|
|
type="link"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
danger
|
|
|
|
|
|
icon={<DeleteOutlined />}
|
|
|
|
|
|
hidden={!access.hasPerms('cms:publicJobFair:remove')}
|
|
|
|
|
|
onClick={() => handleDelete(record.jobFairId)}
|
|
|
|
|
|
>
|
|
|
|
|
|
删除
|
|
|
|
|
|
</Button>,
|
|
|
|
|
|
];
|
|
|
|
|
|
},
|
2026-01-11 10:00:56 +08:00
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<PageContainer>
|
|
|
|
|
|
<ProTable<API.PublicJobFair.JobFairItem>
|
|
|
|
|
|
headerTitle="公共招聘会列表"
|
|
|
|
|
|
actionRef={actionRef}
|
2026-06-09 16:08:39 +08:00
|
|
|
|
formRef={formRef}
|
2026-01-11 10:00:56 +08:00
|
|
|
|
rowKey="jobFairId"
|
|
|
|
|
|
columns={columns}
|
2026-01-11 11:35:06 +08:00
|
|
|
|
scroll={{ x: 'max-content' }}
|
2026-01-11 10:00:56 +08:00
|
|
|
|
rowSelection={{
|
|
|
|
|
|
selectedRowKeys,
|
|
|
|
|
|
onChange: setSelectedRowKeys,
|
|
|
|
|
|
}}
|
|
|
|
|
|
request={async (params) => {
|
|
|
|
|
|
const res = await getPublicJobFairList({
|
|
|
|
|
|
pageNum: params.current,
|
|
|
|
|
|
pageSize: params.pageSize,
|
|
|
|
|
|
jobFairTitle: params.jobFairTitle,
|
|
|
|
|
|
jobFairType: params.jobFairType,
|
2026-07-21 14:18:58 +08:00
|
|
|
|
jobFairRegionType: params.jobFairRegionType,
|
2026-06-09 16:08:39 +08:00
|
|
|
|
startDate: params.startDate,
|
|
|
|
|
|
endDate: params.endDate,
|
2026-01-11 10:00:56 +08:00
|
|
|
|
});
|
|
|
|
|
|
return { data: res.rows, total: res.total, success: true };
|
|
|
|
|
|
}}
|
|
|
|
|
|
toolBarRender={() => [
|
|
|
|
|
|
<Button
|
|
|
|
|
|
key="add"
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
icon={<PlusOutlined />}
|
|
|
|
|
|
hidden={!access.hasPerms('cms:publicJobFair:add')}
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setCurrentRow(undefined);
|
|
|
|
|
|
setModalVisible(true);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
新增
|
|
|
|
|
|
</Button>,
|
|
|
|
|
|
selectedRowKeys.length > 0 && (
|
|
|
|
|
|
<Button
|
|
|
|
|
|
key="batchDelete"
|
|
|
|
|
|
danger
|
|
|
|
|
|
icon={<DeleteOutlined />}
|
|
|
|
|
|
hidden={!access.hasPerms('cms:publicJobFair:remove')}
|
|
|
|
|
|
onClick={() => handleDelete(selectedRowKeys.join(','))}
|
|
|
|
|
|
>
|
|
|
|
|
|
批量删除
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
),
|
2026-07-23 18:35:18 +08:00
|
|
|
|
<Button
|
|
|
|
|
|
key="export"
|
|
|
|
|
|
icon={<ExportOutlined />}
|
|
|
|
|
|
hidden={!access.hasPerms('cms:publicJobFair:export')}
|
|
|
|
|
|
onClick={() => setExportModalOpen(true)}
|
|
|
|
|
|
>
|
2026-01-11 10:00:56 +08:00
|
|
|
|
导出
|
|
|
|
|
|
</Button>,
|
|
|
|
|
|
]}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<EditModal
|
|
|
|
|
|
open={modalVisible}
|
|
|
|
|
|
values={currentRow}
|
|
|
|
|
|
jobFairTypeEnum={jobFairTypeEnum}
|
2026-07-21 14:18:58 +08:00
|
|
|
|
jobFairRegionTypeEnum={jobFairRegionTypeEnum}
|
2026-01-11 10:00:56 +08:00
|
|
|
|
onCancel={() => {
|
|
|
|
|
|
setModalVisible(false);
|
|
|
|
|
|
setCurrentRow(undefined);
|
|
|
|
|
|
}}
|
|
|
|
|
|
onSubmit={async (values) => {
|
|
|
|
|
|
const res = values.jobFairId
|
|
|
|
|
|
? await updatePublicJobFair(values)
|
|
|
|
|
|
: await addPublicJobFair(values);
|
|
|
|
|
|
if (res.code === 200) {
|
|
|
|
|
|
message.success(values.jobFairId ? '修改成功' : '新增成功');
|
|
|
|
|
|
setModalVisible(false);
|
|
|
|
|
|
setCurrentRow(undefined);
|
|
|
|
|
|
actionRef.current?.reload();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
message.error(res.msg || '操作失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
2026-07-15 15:46:15 +08:00
|
|
|
|
<CompanyJobManageModal
|
|
|
|
|
|
open={companyJobModalOpen}
|
|
|
|
|
|
jobFairId={currentJobFair?.jobFairId || ''}
|
|
|
|
|
|
jobFairTitle={currentJobFair?.jobFairTitle}
|
|
|
|
|
|
onCancel={() => {
|
|
|
|
|
|
setCompanyJobModalOpen(false);
|
|
|
|
|
|
setCurrentJobFair(undefined);
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
2026-07-23 18:35:18 +08:00
|
|
|
|
<FairExportModal
|
|
|
|
|
|
open={exportModalOpen}
|
|
|
|
|
|
channel="online"
|
|
|
|
|
|
fairTypeOptions={dictOptions(jobFairTypeEnum)}
|
|
|
|
|
|
regionOptions={dictOptions(jobFairRegionTypeEnum)}
|
|
|
|
|
|
onCancel={() => setExportModalOpen(false)}
|
|
|
|
|
|
onExport={handleExport}
|
|
|
|
|
|
/>
|
2026-01-11 10:00:56 +08:00
|
|
|
|
</PageContainer>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export default PublicJobFairList;
|