import React, { useEffect, useRef, useState } from 'react'; import { Button, Form, Input, Modal, QRCode, Select, Tag, Typography, 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 { normalizePublicUrl } from '@/utils/publicUrl'; import { getParticipatingCompanyQrCode, getParticipatingCompanies, getParticipatingJobDetail, getParticipatingJobs, removeJobFromCompany, removeParticipatingCompany, reviewParticipatingCompany, reviewParticipatingJob, } from '@/services/jobportal/outdoorFairDetail'; import CompanyAddModal from './CompanyAddModal'; import JobEditModal from './JobEditModal'; interface Props { fairId: number; } type ReviewTarget = | { type: 'company'; record: API.OutdoorFairDetail.ParticipatingCompany; } | { type: 'job'; record: API.OutdoorFairDetail.PostedJob; }; 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; const ParticipatingCompaniesTab: React.FC = ({ fairId }) => { const companyActionRef = useRef(); const jobActionRef = useRef(); const [reviewForm] = Form.useForm(); const [companyModalOpen, setCompanyModalOpen] = useState(false); const [jobListModalOpen, setJobListModalOpen] = useState(false); const [jobEditModalOpen, setJobEditModalOpen] = useState(false); const [selectedCompany, setSelectedCompany] = useState(null); const [qrModalOpen, setQrModalOpen] = useState(false); const [qrInfo, setQrInfo] = useState(null); const [qrCompanyName, setQrCompanyName] = useState(''); const [editingJob, setEditingJob] = useState(null); const [educationEnum, setEducationEnum] = useState({}); const [experienceEnum, setExperienceEnum] = useState({}); const [areaEnum, setAreaEnum] = useState({}); const [jobTypeEnum, setJobTypeEnum] = useState({}); const [scaleEnum, setScaleEnum] = useState({}); const [reviewModalOpen, setReviewModalOpen] = useState(false); const [reviewTarget, setReviewTarget] = useState(null); const renderReviewStatus = (status?: string) => { const map: Record = { '0': { color: 'processing', text: '待审核' }, '1': { color: 'success', text: '已通过' }, '2': { color: 'error', text: '已驳回' }, }; const item = map[status || '1'] || map['1']; return {item.text}; }; 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 openQrCodeModal = async (record: API.OutdoorFairDetail.ParticipatingCompany) => { const res = await getParticipatingCompanyQrCode(fairId, record.companyId); if (res.code !== 200) { message.error(res.msg || '二维码内容获取失败'); return; } setQrInfo({ ...res.data, h5Url: normalizePublicUrl(res.data.h5Url) || res.data.h5Url, qrCodeContent: normalizePublicUrl(res.data.qrCodeContent) || res.data.qrCodeContent, }); 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 || '审核失败'); } }; const jobColumns: ProColumns[] = [ { 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: 'fairReviewStatus', hideInSearch: true, width: 100, render: (_, record) => renderReviewStatus(record.fairReviewStatus), }, { title: '发布状态', dataIndex: 'isPublish', hideInSearch: true, width: 90, render: (_, record) => ( {record.isPublish === 1 ? '已发布' : '未发布'} ), }, { title: '操作', valueType: 'option', width: 240, render: (_, record) => [ , , , ], }, ]; return ( <> actionRef={companyActionRef} rowKey="id" headerTitle="参会企业" search={{ labelWidth: 'auto' }} options={false} pagination={{ pageSize: 10 }} toolBarRender={() => [ , ]} request={async (params) => { const res = await getParticipatingCompanies(fairId, { current: params.current, pageSize: params.pageSize, companyName: params.companyName, reviewStatus: params.reviewStatus, startTime: formatSearchDateTime(params.startTime), endTime: formatSearchDateTime(params.endTime), }); return { data: res.rows || [], total: res.total || 0, success: res.code === 200 }; }} columns={[ { 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) }), }, }, { title: '行业', dataIndex: 'industry', hideInSearch: true, render: (_, record) => { const v = record.industry; if (!v) return '--'; // 行业分类 id 均为 6 位编号,纯数字裸码(如 4、6)无对应分类项,兜底为“其他” if (/^\d+$/.test(String(v))) return 其他; return {v}; }, }, { 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 ? {label} : '其他'; }, }, { title: '联系人', dataIndex: 'contactPerson', hideInSearch: true }, { title: '联系电话', dataIndex: 'contactPhone', hideInSearch: true }, { title: '审核状态', dataIndex: 'reviewStatus', valueType: 'select', valueEnum: REVIEW_STATUS_ENUM, fieldProps: { id: 'outdoor-participating-company-review-status-search', name: 'reviewStatus', 'aria-label': '审核状态', }, render: (_, record) => renderReviewStatus(record.reviewStatus), }, { title: '报名时间', dataIndex: 'registrationTime', valueType: 'dateTime', hideInSearch: true, width: 170, }, { title: '展位号', dataIndex: 'boothNumber', hideInSearch: true, render: (_, record) => record.boothNumber || '--', }, { title: '岗位数', dataIndex: 'jobCount', hideInSearch: true, width: 80 }, { title: '操作', valueType: 'option', width: 460, render: (_, record) => [ , , , , , ], }, ]} /> setJobListModalOpen(false)} > {selectedCompany && ( actionRef={jobActionRef} rowKey="jobId" search={{ labelWidth: 'auto' }} options={false} pagination={{ pageSize: 10 }} toolBarRender={() => [ , ]} 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} /> )} qrInfo?.h5Url && window.open(qrInfo.h5Url)} > 打开链接 , ]} destroyOnClose onCancel={() => { setQrModalOpen(false); setQrInfo(null); setQrCompanyName(''); }} > {qrInfo?.qrCodeContent && (
{qrInfo.qrCodeContent} {!qrInfo.deviceCode && ( 当前企业未绑定设备码,二维码使用企业公共页链接。 )}
)}
{ setReviewModalOpen(false); setReviewTarget(null); reviewForm.resetFields(); }} onOk={submitReview} >