import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useAccess, history, useModel } from '@umijs/max'; import { Button, message, Modal, QRCode, Tag, Typography } from 'antd'; import { ActionType, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components'; import { PlusOutlined, DeleteOutlined, FormOutlined, EyeOutlined, QrcodeOutlined, UnorderedListOutlined, } from '@ant-design/icons'; import { getOutdoorFairList, addOutdoorFair, updateOutdoorFair, deleteOutdoorFair, addOutdoorFairDictOption, signupOutdoorFair, getMyOutdoorFairQrCode, } from '@/services/jobportal/outdoorFair'; import type { OutdoorFairItem, OutdoorFairListParams, OutdoorFairForm, OutdoorFairDictForm, OutdoorFairCompanyQrCodeInfo, } from '@/services/jobportal/outdoorFair'; import { getParticipatingJobs } from '@/services/jobportal/outdoorFairDetail'; import { getDictSelectOption, getDictValueEnum } from '@/services/system/dict'; import { getVenueInfoList } from '@/services/jobportal/venueInfo'; import { normalizePublicUrl } from '@/utils/publicUrl'; import EditModal from './components/EditModal'; import JobEditModal from './Detail/components/JobEditModal'; const OUTDOOR_FAIR_TYPE_DICT = 'outdoor_fair_type'; const OUTDOOR_FAIR_REGION_DICT = 'outdoor_fair_region'; const OutdoorFairList: React.FC = () => { const access = useAccess(); const { initialState } = useModel('@@initialState'); const isEnterprise = initialState?.currentUser?.userType === 'view'; const actionRef = useRef(); const jobActionRef = useRef(); const [modalVisible, setModalVisible] = useState(false); const [currentRow, setCurrentRow] = useState(); const [selectedFair, setSelectedFair] = useState(null); const [jobListModalOpen, setJobListModalOpen] = useState(false); const [jobEditModalOpen, setJobEditModalOpen] = useState(false); const [qrModalOpen, setQrModalOpen] = useState(false); const [qrInfo, setQrInfo] = useState(null); const [rejectReasonModalOpen, setRejectReasonModalOpen] = useState(false); const [rejectReason, setRejectReason] = useState(''); const [fairTypeValueEnum, setFairTypeValueEnum] = useState>({}); const [fairTypeOptions, setFairTypeOptions] = useState([]); const [regionValueEnum, setRegionValueEnum] = useState>({}); const [regionOptions, setRegionOptions] = useState([]); const [venueOptions, setVenueOptions] = useState([]); const [educationEnum, setEducationEnum] = useState({}); const [experienceEnum, setExperienceEnum] = useState({}); const [areaEnum, setAreaEnum] = useState({}); const [jobTypeEnum, setJobTypeEnum] = useState({}); const renderReviewStatus = (status?: string) => { const map: Record = { '0': { color: 'processing', text: '待审核' }, '1': { color: 'success', text: '已通过' }, '2': { color: 'error', text: '已驳回' }, }; const item = map[status || '0'] || map['0']; return {item.text}; }; const refreshDictOptions = useCallback(async () => { const [typeValueEnum, typeOptions, regionEnum, regionSelectOptions] = await Promise.all([ getDictValueEnum(OUTDOOR_FAIR_TYPE_DICT), getDictSelectOption(OUTDOOR_FAIR_TYPE_DICT), getDictValueEnum(OUTDOOR_FAIR_REGION_DICT), getDictSelectOption(OUTDOOR_FAIR_REGION_DICT), ]); setFairTypeValueEnum(typeValueEnum); setFairTypeOptions(typeOptions); setRegionValueEnum(regionEnum); setRegionOptions(regionSelectOptions); }, []); useEffect(() => { refreshDictOptions(); }, [refreshDictOptions]); useEffect(() => { Promise.all([ getDictValueEnum('education', true, true), getDictValueEnum('experience', true, true), getDictValueEnum('area', true, true), getDictValueEnum('job_type', true, true), ]).then(([education, experience, area, jobType]) => { setEducationEnum(education); setExperienceEnum(experience); setAreaEnum(area); setJobTypeEnum(jobType); }); }, []); const refreshVenueOptions = useCallback(async () => { const res = await getVenueInfoList({ current: 1, pageSize: 999 }); if (res.code === 200) { setVenueOptions( (res.rows || []).map((item) => ({ label: item.venueName, value: item.id, venueAddress: item.venueAddress, floorCount: item.floorCount, })), ); } }, []); useEffect(() => { if (!isEnterprise) { refreshVenueOptions(); } }, [isEnterprise, refreshVenueOptions]); const handleCreateDictOption = async (values: OutdoorFairDictForm) => { const res = await addOutdoorFairDictOption(values); if (res.code === 200) { await refreshDictOptions(); message.success('字典项新增成功'); return true; } message.error(res.msg || '字典项新增失败'); return false; }; const handleDelete = async (id: number) => { Modal.confirm({ title: '确认删除', content: '确定要删除该户外招聘会吗?', onOk: async () => { const res = await deleteOutdoorFair(id); if (res.code === 200) { message.success('删除成功'); actionRef.current?.reload(); } else { message.error(res.msg || '删除失败'); } }, }); }; const getCurrentCompany = ( record: OutdoorFairItem, ): API.OutdoorFairDetail.ParticipatingCompany => ({ id: 0, fairId: record.id, companyId: record.companyId!, companyName: record.companyName || '本企业', industry: '', scale: '', contactPerson: '', contactPhone: '', jobCount: 0, }); const handleSignup = async (record: OutdoorFairItem) => { const res = await signupOutdoorFair(record.id); if (res.code === 200) { message.success('报名成功,等待审核'); actionRef.current?.reload(); } else { message.error(res.msg || '报名失败'); } }; const openQrCode = async (record: OutdoorFairItem) => { const res = await getMyOutdoorFairQrCode(record.id); if (res.code === 200) { setQrInfo({ ...res.data, h5Url: normalizePublicUrl(res.data.h5Url) || res.data.h5Url, qrCodeContent: normalizePublicUrl(res.data.qrCodeContent) || res.data.qrCodeContent, }); setQrModalOpen(true); } else { message.error(res.msg || '二维码加载失败'); } }; const openJobList = (record: OutdoorFairItem) => { setSelectedFair(record); setJobListModalOpen(true); }; const openJobEditor = (record: OutdoorFairItem) => { setSelectedFair(record); setJobEditModalOpen(true); }; const openRejectReason = (record: OutdoorFairItem) => { setRejectReason(record.reviewRemark || '暂无驳回原因'); setRejectReasonModalOpen(true); }; 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: 'jobLocation', hideInSearch: true, ellipsis: true }, ]; const columns: ProColumns[] = [ { title: '招聘会标题', dataIndex: 'title', ellipsis: true, }, { title: '举办单位', dataIndex: 'hostUnit', ellipsis: true, }, { title: '招聘会类型', dataIndex: 'fairType', ellipsis: true, valueType: 'select', valueEnum: fairTypeValueEnum, }, { title: '举办区域', dataIndex: 'region', ellipsis: true, valueType: 'select', valueEnum: regionValueEnum, }, { title: '场地名称', dataIndex: 'venueName', ellipsis: true, valueType: 'select', fieldProps: { options: venueOptions, allowClear: true, showSearch: true, optionFilterProp: 'label', }, search: { transform: (value) => ({ venueId: value }), }, render: (_, record) => record.venueName || '--', }, { title: '举办地址', dataIndex: 'address', ellipsis: true, hideInSearch: true, }, { title: '展位数量', dataIndex: 'boothCount', hideInSearch: true, }, { title: '举办时间', dataIndex: 'holdTime', valueType: 'dateTime', hideInSearch: true, }, { title: '截止时间', dataIndex: 'endTime', valueType: 'dateTime', hideInSearch: true, }, { title: '开放申请', dataIndex: 'applyStartTime', valueType: 'dateTime', hideInSearch: true, }, { title: '截止申请', dataIndex: 'applyEndTime', valueType: 'dateTime', hideInSearch: true, }, { title: '线上申请', dataIndex: 'onlineApply', valueType: 'select', valueEnum: { true: { text: '是' }, false: { text: '否' }, }, render: (_, record) => (record.onlineApply ? '是' : '否'), }, { title: '报名审核状态', dataIndex: 'reviewStatus', hideInSearch: true, hideInTable: !isEnterprise, render: (_, record) => record.signedUp ? ( record.reviewStatus === '2' ? ( ) : ( renderReviewStatus(record.reviewStatus) ) ) : ( '--' ), }, { title: '招聘会照片', dataIndex: 'photoUrl', hideInSearch: true, render: (_, record) => record.photoUrl ? ( {record.title} ) : ( '--' ), }, { title: '操作', valueType: 'option', width: isEnterprise ? 280 : 160, fixed: 'right', render: (_, record) => [ isEnterprise && !record.signedUp ? ( ) : null, isEnterprise && record.signedUp && record.reviewStatus === '1' ? ( ) : null, isEnterprise && record.signedUp && record.reviewStatus === '1' ? ( ) : null, isEnterprise && record.signedUp && record.reviewStatus === '1' ? ( ) : null, , , , ], }, ]; return ( headerTitle="户外招聘会管理列表" actionRef={actionRef} rowKey="id" columns={columns} scroll={{ x: 'max-content' }} request={async (params) => { const res = await getOutdoorFairList({ current: params.current, pageSize: params.pageSize, title: params.title, hostUnit: params.hostUnit, fairType: params.fairType, region: params.region, venueId: params.venueId, onlineApply: params.onlineApply, } as OutdoorFairListParams); return { data: res.rows, total: res.total, success: true }; }} toolBarRender={() => [ , ]} /> { setModalVisible(false); setCurrentRow(undefined); }} onSubmit={async (values: OutdoorFairForm) => { const res = values.id ? await updateOutdoorFair(values) : await addOutdoorFair(values); if (res.code === 200) { message.success(values.id ? '修改成功' : '新增成功'); setModalVisible(false); setCurrentRow(undefined); actionRef.current?.reload(); } else { message.error(res.msg || '操作失败'); } }} /> qrInfo?.h5Url && window.open(qrInfo.h5Url)} > 打开链接 , ]} destroyOnClose onCancel={() => { setQrModalOpen(false); setQrInfo(null); }} > {qrInfo?.qrCodeContent && (
{qrInfo.qrCodeContent}
)}
{ setRejectReasonModalOpen(false); setRejectReason(''); }} > {rejectReason} { setJobListModalOpen(false); setSelectedFair(null); }} > {selectedFair && ( actionRef={jobActionRef} rowKey="jobId" options={false} search={{ labelWidth: 'auto', defaultCollapsed: false }} pagination={{ pageSize: 10 }} toolBarRender={() => [ , ]} request={async (params) => { const res = await getParticipatingJobs(selectedFair.id, { 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} /> )} setJobEditModalOpen(false)} onSuccess={() => { setJobEditModalOpen(false); jobActionRef.current?.reload(); actionRef.current?.reload(); }} />
); }; export default OutdoorFairList;