2026-06-25 23:13:42 +08:00
|
|
|
|
import React, { useEffect, useRef, useState } from 'react';
|
2026-06-27 22:45:16 +08:00
|
|
|
|
import { Button, Form, Input, Modal, QRCode, Select, Tag, Typography, message } from 'antd';
|
2026-06-25 23:13:42 +08:00
|
|
|
|
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';
|
2026-07-15 08:46:40 +08:00
|
|
|
|
import { normalizePublicUrl } from '@/utils/publicUrl';
|
2026-06-25 23:13:42 +08:00
|
|
|
|
import {
|
2026-06-27 22:45:16 +08:00
|
|
|
|
getParticipatingCompanyQrCode,
|
2026-06-25 23:13:42 +08:00
|
|
|
|
getParticipatingCompanies,
|
|
|
|
|
|
getParticipatingJobDetail,
|
|
|
|
|
|
getParticipatingJobs,
|
|
|
|
|
|
removeJobFromCompany,
|
|
|
|
|
|
removeParticipatingCompany,
|
2026-06-27 22:45:16 +08:00
|
|
|
|
reviewParticipatingCompany,
|
|
|
|
|
|
reviewParticipatingJob,
|
2026-06-25 23:13:42 +08:00
|
|
|
|
} from '@/services/jobportal/outdoorFairDetail';
|
|
|
|
|
|
import CompanyAddModal from './CompanyAddModal';
|
|
|
|
|
|
import JobEditModal from './JobEditModal';
|
|
|
|
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
|
fairId: number;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-27 22:45:16 +08:00
|
|
|
|
type ReviewTarget =
|
|
|
|
|
|
| {
|
|
|
|
|
|
type: 'company';
|
|
|
|
|
|
record: API.OutdoorFairDetail.ParticipatingCompany;
|
|
|
|
|
|
}
|
|
|
|
|
|
| {
|
|
|
|
|
|
type: 'job';
|
|
|
|
|
|
record: API.OutdoorFairDetail.PostedJob;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-25 22:16:55 +08:00
|
|
|
|
const REVIEW_STATUS_ENUM = {
|
|
|
|
|
|
'0': { text: '待审核', status: 'Processing' },
|
|
|
|
|
|
'1': { text: '已通过', status: 'Success' },
|
|
|
|
|
|
'2': { text: '已驳回', status: 'Error' },
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const formatSearchDateTime = (value: any) =>
|
|
|
|
|
|
value && typeof value.format === 'function' ? value.format('YYYY-MM-DD HH:mm:ss') : value;
|
|
|
|
|
|
|
2026-06-25 23:13:42 +08:00
|
|
|
|
const ParticipatingCompaniesTab: React.FC<Props> = ({ fairId }) => {
|
|
|
|
|
|
const companyActionRef = useRef<ActionType>();
|
|
|
|
|
|
const jobActionRef = useRef<ActionType>();
|
2026-06-27 22:45:16 +08:00
|
|
|
|
const [reviewForm] = Form.useForm();
|
2026-06-25 23:13:42 +08:00
|
|
|
|
const [companyModalOpen, setCompanyModalOpen] = useState(false);
|
|
|
|
|
|
const [jobListModalOpen, setJobListModalOpen] = useState(false);
|
|
|
|
|
|
const [jobEditModalOpen, setJobEditModalOpen] = useState(false);
|
|
|
|
|
|
const [selectedCompany, setSelectedCompany] =
|
|
|
|
|
|
useState<API.OutdoorFairDetail.ParticipatingCompany | null>(null);
|
2026-06-27 22:45:16 +08:00
|
|
|
|
const [qrModalOpen, setQrModalOpen] = useState(false);
|
|
|
|
|
|
const [qrInfo, setQrInfo] = useState<API.OutdoorFairDetail.CompanyQrCodeInfo | null>(null);
|
|
|
|
|
|
const [qrCompanyName, setQrCompanyName] = useState('');
|
2026-06-25 23:13:42 +08:00
|
|
|
|
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>({});
|
2026-06-27 22:45:16 +08:00
|
|
|
|
const [reviewModalOpen, setReviewModalOpen] = useState(false);
|
|
|
|
|
|
const [reviewTarget, setReviewTarget] = useState<ReviewTarget | null>(null);
|
|
|
|
|
|
|
|
|
|
|
|
const renderReviewStatus = (status?: string) => {
|
|
|
|
|
|
const map: Record<string, { color: string; text: string }> = {
|
|
|
|
|
|
'0': { color: 'processing', text: '待审核' },
|
|
|
|
|
|
'1': { color: 'success', text: '已通过' },
|
|
|
|
|
|
'2': { color: 'error', text: '已驳回' },
|
|
|
|
|
|
};
|
|
|
|
|
|
const item = map[status || '1'] || map['1'];
|
|
|
|
|
|
return <Tag color={item.color}>{item.text}</Tag>;
|
|
|
|
|
|
};
|
2026-06-25 23:13:42 +08:00
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-06-27 22:45:16 +08:00
|
|
|
|
const openQrCodeModal = async (record: API.OutdoorFairDetail.ParticipatingCompany) => {
|
|
|
|
|
|
const res = await getParticipatingCompanyQrCode(fairId, record.companyId);
|
|
|
|
|
|
if (res.code !== 200) {
|
|
|
|
|
|
message.error(res.msg || '二维码内容获取失败');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-07-15 08:46:40 +08:00
|
|
|
|
setQrInfo({
|
|
|
|
|
|
...res.data,
|
|
|
|
|
|
h5Url: normalizePublicUrl(res.data.h5Url) || res.data.h5Url,
|
|
|
|
|
|
qrCodeContent: normalizePublicUrl(res.data.qrCodeContent) || res.data.qrCodeContent,
|
|
|
|
|
|
});
|
2026-06-27 22:45:16 +08:00
|
|
|
|
setQrCompanyName(record.companyName);
|
|
|
|
|
|
setQrModalOpen(true);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const openReviewModal = (target: ReviewTarget) => {
|
|
|
|
|
|
setReviewTarget(target);
|
|
|
|
|
|
reviewForm.setFieldsValue({
|
|
|
|
|
|
reviewStatus:
|
|
|
|
|
|
target.type === 'company'
|
|
|
|
|
|
? target.record.reviewStatus || '0'
|
|
|
|
|
|
: target.record.fairReviewStatus || '0',
|
|
|
|
|
|
reviewRemark:
|
|
|
|
|
|
target.type === 'company'
|
|
|
|
|
|
? target.record.reviewRemark || ''
|
|
|
|
|
|
: target.record.fairReviewRemark || '',
|
|
|
|
|
|
});
|
|
|
|
|
|
setReviewModalOpen(true);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const submitReview = async () => {
|
|
|
|
|
|
const values = await reviewForm.validateFields();
|
|
|
|
|
|
if (!reviewTarget) return;
|
|
|
|
|
|
const payload = {
|
|
|
|
|
|
reviewStatus: values.reviewStatus as '0' | '1' | '2',
|
|
|
|
|
|
reviewRemark: values.reviewRemark,
|
|
|
|
|
|
};
|
|
|
|
|
|
const res =
|
|
|
|
|
|
reviewTarget.type === 'company'
|
|
|
|
|
|
? await reviewParticipatingCompany({
|
|
|
|
|
|
fairId,
|
|
|
|
|
|
id: reviewTarget.record.id,
|
|
|
|
|
|
...payload,
|
|
|
|
|
|
})
|
|
|
|
|
|
: await reviewParticipatingJob({
|
|
|
|
|
|
fairId,
|
|
|
|
|
|
jobId: reviewTarget.record.jobId!,
|
|
|
|
|
|
...payload,
|
|
|
|
|
|
});
|
|
|
|
|
|
if (res.code === 200) {
|
|
|
|
|
|
message.success('审核成功');
|
|
|
|
|
|
setReviewModalOpen(false);
|
|
|
|
|
|
setReviewTarget(null);
|
|
|
|
|
|
reviewForm.resetFields();
|
|
|
|
|
|
if (reviewTarget.type === 'company') {
|
|
|
|
|
|
companyActionRef.current?.reload();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
jobActionRef.current?.reload();
|
|
|
|
|
|
companyActionRef.current?.reload();
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
message.error(res.msg || '审核失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-06-25 23:13:42 +08:00
|
|
|
|
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 },
|
2026-06-27 22:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: '审核状态',
|
|
|
|
|
|
dataIndex: 'fairReviewStatus',
|
|
|
|
|
|
hideInSearch: true,
|
|
|
|
|
|
width: 100,
|
|
|
|
|
|
render: (_, record) => renderReviewStatus(record.fairReviewStatus),
|
|
|
|
|
|
},
|
2026-06-25 23:13:42 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: '发布状态',
|
|
|
|
|
|
dataIndex: 'isPublish',
|
|
|
|
|
|
hideInSearch: true,
|
|
|
|
|
|
width: 90,
|
|
|
|
|
|
render: (_, record) => (
|
|
|
|
|
|
<Tag color={record.isPublish === 1 ? 'green' : 'red'}>
|
|
|
|
|
|
{record.isPublish === 1 ? '已发布' : '未发布'}
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '操作',
|
|
|
|
|
|
valueType: 'option',
|
2026-06-27 22:45:16 +08:00
|
|
|
|
width: 240,
|
2026-06-25 23:13:42 +08:00
|
|
|
|
render: (_, record) => [
|
|
|
|
|
|
<Button key="edit" type="link" size="small" onClick={() => openJobEditor(record)}>
|
|
|
|
|
|
编辑
|
|
|
|
|
|
</Button>,
|
2026-06-27 22:45:16 +08:00
|
|
|
|
<Button
|
|
|
|
|
|
key="review"
|
|
|
|
|
|
type="link"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
onClick={() => openReviewModal({ type: 'job', record })}
|
|
|
|
|
|
>
|
|
|
|
|
|
审核
|
|
|
|
|
|
</Button>,
|
2026-06-25 23:13:42 +08:00
|
|
|
|
<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={() => [
|
2026-06-27 22:45:16 +08:00
|
|
|
|
<Button
|
|
|
|
|
|
key="add"
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
icon={<PlusOutlined />}
|
|
|
|
|
|
onClick={() => setCompanyModalOpen(true)}
|
|
|
|
|
|
>
|
2026-06-25 23:13:42 +08:00
|
|
|
|
添加企业
|
|
|
|
|
|
</Button>,
|
|
|
|
|
|
]}
|
|
|
|
|
|
request={async (params) => {
|
|
|
|
|
|
const res = await getParticipatingCompanies(fairId, {
|
|
|
|
|
|
current: params.current,
|
|
|
|
|
|
pageSize: params.pageSize,
|
|
|
|
|
|
companyName: params.companyName,
|
2026-07-25 22:16:55 +08:00
|
|
|
|
reviewStatus: params.reviewStatus,
|
|
|
|
|
|
startTime: formatSearchDateTime(params.startTime),
|
|
|
|
|
|
endTime: formatSearchDateTime(params.endTime),
|
2026-06-25 23:13:42 +08:00
|
|
|
|
});
|
|
|
|
|
|
return { data: res.rows || [], total: res.total || 0, success: res.code === 200 };
|
|
|
|
|
|
}}
|
|
|
|
|
|
columns={[
|
2026-07-25 22:16:55 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: '企业名称',
|
|
|
|
|
|
dataIndex: 'companyName',
|
|
|
|
|
|
ellipsis: true,
|
|
|
|
|
|
fieldProps: {
|
|
|
|
|
|
id: 'outdoor-participating-company-name-search',
|
|
|
|
|
|
name: 'companyName',
|
|
|
|
|
|
'aria-label': '企业名称',
|
|
|
|
|
|
autoComplete: 'off',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '报名开始时间',
|
|
|
|
|
|
dataIndex: 'startTime',
|
|
|
|
|
|
hideInTable: true,
|
|
|
|
|
|
valueType: 'dateTime',
|
|
|
|
|
|
fieldProps: {
|
|
|
|
|
|
id: 'outdoor-participating-company-start-time-search',
|
|
|
|
|
|
name: 'startTime',
|
|
|
|
|
|
'aria-label': '报名开始时间',
|
|
|
|
|
|
format: 'YYYY-MM-DD HH:mm:ss',
|
|
|
|
|
|
},
|
|
|
|
|
|
search: {
|
|
|
|
|
|
transform: (value) => ({ startTime: formatSearchDateTime(value) }),
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '报名结束时间',
|
|
|
|
|
|
dataIndex: 'endTime',
|
|
|
|
|
|
hideInTable: true,
|
|
|
|
|
|
valueType: 'dateTime',
|
|
|
|
|
|
fieldProps: {
|
|
|
|
|
|
id: 'outdoor-participating-company-end-time-search',
|
|
|
|
|
|
name: 'endTime',
|
|
|
|
|
|
'aria-label': '报名结束时间',
|
|
|
|
|
|
format: 'YYYY-MM-DD HH:mm:ss',
|
|
|
|
|
|
},
|
|
|
|
|
|
search: {
|
|
|
|
|
|
transform: (value) => ({ endTime: formatSearchDateTime(value) }),
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
2026-06-25 23:13:42 +08:00
|
|
|
|
{
|
|
|
|
|
|
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 },
|
2026-06-27 22:45:16 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: '审核状态',
|
|
|
|
|
|
dataIndex: 'reviewStatus',
|
2026-07-25 22:16:55 +08:00
|
|
|
|
valueType: 'select',
|
|
|
|
|
|
valueEnum: REVIEW_STATUS_ENUM,
|
|
|
|
|
|
fieldProps: {
|
|
|
|
|
|
id: 'outdoor-participating-company-review-status-search',
|
|
|
|
|
|
name: 'reviewStatus',
|
|
|
|
|
|
'aria-label': '审核状态',
|
|
|
|
|
|
},
|
2026-06-27 22:45:16 +08:00
|
|
|
|
render: (_, record) => renderReviewStatus(record.reviewStatus),
|
|
|
|
|
|
},
|
2026-07-25 22:16:55 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: '报名时间',
|
|
|
|
|
|
dataIndex: 'registrationTime',
|
|
|
|
|
|
valueType: 'dateTime',
|
|
|
|
|
|
hideInSearch: true,
|
|
|
|
|
|
width: 170,
|
|
|
|
|
|
},
|
2026-06-25 23:13:42 +08:00
|
|
|
|
{
|
|
|
|
|
|
title: '展位号',
|
|
|
|
|
|
dataIndex: 'boothNumber',
|
|
|
|
|
|
hideInSearch: true,
|
|
|
|
|
|
render: (_, record) => record.boothNumber || '--',
|
|
|
|
|
|
},
|
|
|
|
|
|
{ title: '岗位数', dataIndex: 'jobCount', hideInSearch: true, width: 80 },
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '操作',
|
|
|
|
|
|
valueType: 'option',
|
2026-06-27 22:45:16 +08:00
|
|
|
|
width: 460,
|
2026-06-25 23:13:42 +08:00
|
|
|
|
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>,
|
2026-06-27 22:45:16 +08:00
|
|
|
|
<Button key="qrcode" type="link" size="small" onClick={() => openQrCodeModal(record)}>
|
|
|
|
|
|
查看二维码
|
|
|
|
|
|
</Button>,
|
|
|
|
|
|
<Button
|
|
|
|
|
|
key="review"
|
|
|
|
|
|
type="link"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
onClick={() => openReviewModal({ type: 'company', record })}
|
|
|
|
|
|
>
|
|
|
|
|
|
审核
|
|
|
|
|
|
</Button>,
|
2026-06-25 23:13:42 +08:00
|
|
|
|
<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={() => [
|
2026-06-27 22:45:16 +08:00
|
|
|
|
<Button
|
|
|
|
|
|
key="add"
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
icon={<PlusOutlined />}
|
|
|
|
|
|
onClick={() => openJobEditor()}
|
|
|
|
|
|
>
|
2026-06-25 23:13:42 +08:00
|
|
|
|
添加岗位
|
|
|
|
|
|
</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>
|
|
|
|
|
|
|
2026-06-27 22:45:16 +08:00
|
|
|
|
<Modal
|
|
|
|
|
|
title={qrCompanyName ? `${qrCompanyName} — 企业二维码` : '企业二维码'}
|
|
|
|
|
|
open={qrModalOpen}
|
|
|
|
|
|
width={420}
|
|
|
|
|
|
footer={[
|
|
|
|
|
|
<Button
|
|
|
|
|
|
key="open"
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
onClick={() => qrInfo?.h5Url && window.open(qrInfo.h5Url)}
|
|
|
|
|
|
>
|
|
|
|
|
|
打开链接
|
|
|
|
|
|
</Button>,
|
|
|
|
|
|
]}
|
|
|
|
|
|
destroyOnClose
|
|
|
|
|
|
onCancel={() => {
|
|
|
|
|
|
setQrModalOpen(false);
|
|
|
|
|
|
setQrInfo(null);
|
|
|
|
|
|
setQrCompanyName('');
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{qrInfo?.qrCodeContent && (
|
|
|
|
|
|
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 16 }}>
|
|
|
|
|
|
<QRCode value={qrInfo.qrCodeContent} size={220} />
|
|
|
|
|
|
<Typography.Paragraph
|
|
|
|
|
|
copyable={{ text: qrInfo.qrCodeContent }}
|
|
|
|
|
|
style={{
|
|
|
|
|
|
width: '100%',
|
|
|
|
|
|
marginBottom: 0,
|
|
|
|
|
|
wordBreak: 'break-all',
|
|
|
|
|
|
textAlign: 'center',
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
{qrInfo.qrCodeContent}
|
|
|
|
|
|
</Typography.Paragraph>
|
|
|
|
|
|
{!qrInfo.deviceCode && (
|
|
|
|
|
|
<Typography.Text type="secondary">
|
|
|
|
|
|
当前企业未绑定设备码,二维码使用企业公共页链接。
|
|
|
|
|
|
</Typography.Text>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Modal>
|
|
|
|
|
|
|
|
|
|
|
|
<Modal
|
|
|
|
|
|
title={
|
|
|
|
|
|
reviewTarget?.type === 'company'
|
|
|
|
|
|
? `审核报名 — ${reviewTarget.record.companyName}`
|
|
|
|
|
|
: `审核岗位 — ${reviewTarget?.record.jobTitle || ''}`
|
|
|
|
|
|
}
|
|
|
|
|
|
open={reviewModalOpen}
|
|
|
|
|
|
destroyOnClose
|
|
|
|
|
|
onCancel={() => {
|
|
|
|
|
|
setReviewModalOpen(false);
|
|
|
|
|
|
setReviewTarget(null);
|
|
|
|
|
|
reviewForm.resetFields();
|
|
|
|
|
|
}}
|
|
|
|
|
|
onOk={submitReview}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Form form={reviewForm} layout="vertical">
|
|
|
|
|
|
<Form.Item
|
|
|
|
|
|
name="reviewStatus"
|
|
|
|
|
|
label="审核状态"
|
|
|
|
|
|
rules={[{ required: true, message: '请选择审核状态' }]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Select
|
|
|
|
|
|
options={[
|
|
|
|
|
|
{ label: '待审核', value: '0' },
|
|
|
|
|
|
{ label: '已通过', value: '1' },
|
|
|
|
|
|
{ label: '已驳回', value: '2' },
|
|
|
|
|
|
]}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</Form.Item>
|
|
|
|
|
|
<Form.Item noStyle shouldUpdate={(prev, cur) => prev.reviewStatus !== cur.reviewStatus}>
|
|
|
|
|
|
{({ getFieldValue }) => (
|
|
|
|
|
|
<Form.Item
|
|
|
|
|
|
name="reviewRemark"
|
|
|
|
|
|
label="审核原因"
|
|
|
|
|
|
rules={[
|
|
|
|
|
|
{
|
|
|
|
|
|
required: getFieldValue('reviewStatus') === '2',
|
|
|
|
|
|
message: '驳回时请填写原因',
|
|
|
|
|
|
},
|
|
|
|
|
|
]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Input.TextArea rows={4} placeholder="驳回时请输入原因" maxLength={500} showCount />
|
|
|
|
|
|
</Form.Item>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Form.Item>
|
|
|
|
|
|
</Form>
|
|
|
|
|
|
</Modal>
|
|
|
|
|
|
|
2026-06-25 23:13:42 +08:00
|
|
|
|
<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;
|