Files
shz-admin/src/pages/Jobfair/Outdoorfair/Detail/components/FairInfoTab.tsx

362 lines
13 KiB
TypeScript
Raw Normal View History

import React, { useCallback, useEffect, useRef, useState } from 'react';
import { history, useAccess } from '@umijs/max';
2026-06-18 02:30:11 +08:00
import { Button, Card, Descriptions, List, Tag, message, Modal, Space } from 'antd';
import { ActionType, ProTable } from '@ant-design/pro-components';
import { PlusOutlined, DeleteOutlined, FormOutlined } from '@ant-design/icons';
import type { OutdoorFairDictForm, OutdoorFairItem } from '@/services/jobportal/outdoorFair';
import { addOutdoorFairDictOption, updateOutdoorFair } from '@/services/jobportal/outdoorFair';
import { getVenueInfoList } from '@/services/jobportal/venueInfo';
import { getDictSelectOption } from '@/services/system/dict';
2026-06-18 02:30:11 +08:00
import {
getParticipatingCompanies,
addParticipatingCompany,
removeParticipatingCompany,
addJobForCompany,
updateJobForCompany,
removeJobFromCompany,
} from '@/services/jobportal/outdoorFairDetail';
import EditModal from '@/pages/Jobfair/Outdoorfair/components/EditModal';
import CompanyAddModal from './CompanyAddModal';
import JobEditModal from './JobEditModal';
const OUTDOOR_FAIR_TYPE_DICT = 'outdoor_fair_type';
const OUTDOOR_FAIR_REGION_DICT = 'outdoor_fair_region';
2026-06-18 02:30:11 +08:00
interface Props {
fairId: number;
fairInfo: OutdoorFairItem;
refreshKey?: number;
2026-06-18 02:30:11 +08:00
onFairUpdate: () => void;
}
const FairInfoTab: React.FC<Props> = ({ fairId, fairInfo, refreshKey, onFairUpdate }) => {
2026-06-18 02:30:11 +08:00
const access = useAccess();
const actionRef = useRef<ActionType>();
const [editModalOpen, setEditModalOpen] = useState(false);
const [companyModalOpen, setCompanyModalOpen] = useState(false);
const [jobModalOpen, setJobModalOpen] = useState(false);
const [selectedCompany, setSelectedCompany] = useState<any>(null);
const [editingJob, setEditingJob] = useState<any>(null);
const [fairTypeOptions, setFairTypeOptions] = useState<any[]>([]);
const [regionOptions, setRegionOptions] = useState<any[]>([]);
const [venueOptions, setVenueOptions] = useState<any[]>([]);
const refreshEditModalOptions = useCallback(async () => {
const [typeOptions, regionSelectOptions, venueRes] = await Promise.all([
getDictSelectOption(OUTDOOR_FAIR_TYPE_DICT),
getDictSelectOption(OUTDOOR_FAIR_REGION_DICT),
getVenueInfoList({ current: 1, pageSize: 999 }),
]);
setFairTypeOptions(typeOptions);
setRegionOptions(regionSelectOptions);
if (venueRes.code === 200) {
setVenueOptions(
(venueRes.rows || []).map((item) => ({
label: item.venueName,
value: item.id,
venueAddress: item.venueAddress,
floorCount: item.floorCount,
})),
);
}
}, []);
useEffect(() => {
refreshEditModalOptions();
}, [refreshEditModalOptions]);
useEffect(() => {
actionRef.current?.reload();
}, [refreshKey]);
const handleCreateDictOption = async (values: OutdoorFairDictForm) => {
const res = await addOutdoorFairDictOption(values);
if (res.code === 200) {
await refreshEditModalOptions();
message.success('字典项新增成功');
return true;
}
message.error(res.msg || '字典项新增失败');
return false;
};
2026-06-18 02:30:11 +08:00
const fairTypeColors: Record<string, string> = {
: 'blue',
: 'green',
: 'orange',
: 'purple',
: 'cyan',
: 'magenta',
: 'default',
2026-06-18 02:30:11 +08:00
};
return (
<div>
{/* 基本信息卡片 */}
<Card
title="基本信息"
extra={
<Button
type="primary"
size="small"
icon={<FormOutlined />}
hidden={!access.hasPerms('cms:outdoorFair:edit')}
onClick={() => setEditModalOpen(true)}
>
</Button>
}
style={{ marginBottom: 16 }}
>
<Descriptions column={3} bordered size="small">
<Descriptions.Item label="招聘会标题" span={3}>
{fairInfo.title}
</Descriptions.Item>
2026-06-18 02:30:11 +08:00
<Descriptions.Item label="举办单位">{fairInfo.hostUnit}</Descriptions.Item>
<Descriptions.Item label="招聘会类型">
<Tag color={fairTypeColors[fairInfo.fairType] || 'default'}>{fairInfo.fairType}</Tag>
</Descriptions.Item>
<Descriptions.Item label="举办区域">{fairInfo.region}</Descriptions.Item>
<Descriptions.Item label="绑定场地">
{fairInfo.venueId && fairInfo.venueName ? (
<Button
type="link"
size="small"
onClick={() => history.push(`/jobfair/venue-info/detail?id=${fairInfo.venueId}`)}
>
{fairInfo.venueName}
</Button>
) : (
'--'
)}
</Descriptions.Item>
<Descriptions.Item label="举办地址" span={2}>
{fairInfo.address}
</Descriptions.Item>
2026-06-18 02:30:11 +08:00
<Descriptions.Item label="展位数量">{fairInfo.boothCount}</Descriptions.Item>
<Descriptions.Item label="举办时间">{fairInfo.holdTime}</Descriptions.Item>
<Descriptions.Item label="截止时间">{fairInfo.endTime}</Descriptions.Item>
<Descriptions.Item label="开放申请">{fairInfo.applyStartTime}</Descriptions.Item>
<Descriptions.Item label="截止申请">{fairInfo.applyEndTime}</Descriptions.Item>
<Descriptions.Item label="线上申请">
{fairInfo.onlineApply ? '是' : '否'}
</Descriptions.Item>
2026-06-18 02:30:11 +08:00
</Descriptions>
</Card>
{/* 参会企业及岗位 */}
<Card
title="参会企业及岗位"
extra={
<Button type="primary" icon={<PlusOutlined />} onClick={() => setCompanyModalOpen(true)}>
2026-06-18 02:30:11 +08:00
</Button>
}
>
<ProTable
actionRef={actionRef}
rowKey="id"
search={false}
options={false}
pagination={{ pageSize: 10 }}
request={async (params) => {
const res = await getParticipatingCompanies(fairId, {
current: params.current,
pageSize: params.pageSize,
companyName: params.companyName,
});
return { data: res.rows, total: res.total, success: true };
}}
expandable={{
expandedRowRender: (record) => (
<div style={{ padding: '0 24px' }}>
{record.jobList.length === 0 ? (
<p style={{ color: '#999' }}></p>
) : (
<List
dataSource={record.jobList}
renderItem={(job: any) => (
<List.Item
actions={[
<Button
key="edit"
type="link"
size="small"
onClick={() => {
setEditingJob(job);
setSelectedCompany(record);
setJobModalOpen(true);
}}
2026-06-18 02:30:11 +08:00
>
</Button>,
<Button
key="delete"
type="link"
size="small"
danger
2026-06-18 02:30:11 +08:00
onClick={() => {
Modal.confirm({
title: '确认删除',
content: `确定删除岗位「${job.jobTitle}」吗?`,
onOk: async () => {
const res = await removeJobFromCompany(job.id);
if (res.code === 200) {
message.success('删除成功');
actionRef.current?.reload();
}
2026-06-18 02:30:11 +08:00
},
});
}}
>
</Button>,
]}
>
<List.Item.Meta
title={job.jobTitle}
description={
<Space>
<Tag color="blue">
{job.minSalary}-{job.maxSalary}K
</Tag>
2026-06-18 02:30:11 +08:00
<Tag>{job.education}</Tag>
<Tag>{job.experience}</Tag>
<Tag> {job.vacancies} </Tag>
<Tag color={job.status === 'active' ? 'green' : 'red'}>
{job.status === 'active' ? '在招' : '已关闭'}
</Tag>
</Space>
}
/>
</List.Item>
)}
/>
)}
</div>
),
}}
columns={[
{ title: '企业名称', dataIndex: 'companyName', ellipsis: true },
{
title: '行业',
dataIndex: 'industry',
width: 100,
hideInSearch: true,
render: (_, r: any) => <Tag>{r.industry}</Tag>,
},
2026-06-18 02:30:11 +08:00
{ title: '规模', dataIndex: 'scale', width: 110, hideInSearch: true },
{ title: '联系人', dataIndex: 'contactPerson', width: 100, hideInSearch: true },
{ title: '联系电话', dataIndex: 'contactPhone', width: 130, hideInSearch: true },
{
title: '展位号',
dataIndex: 'boothNumber',
width: 80,
hideInSearch: true,
render: (_, r: any) => r.boothNumber || '--',
},
{
title: '岗位数',
width: 80,
hideInSearch: true,
render: (_, r: any) => r.jobList?.length || 0,
},
{
title: '操作',
valueType: 'option',
width: 200,
2026-06-18 02:30:11 +08:00
render: (_, record: any) => [
<Button
key="addJob"
type="link"
size="small"
onClick={() => {
setEditingJob(null);
setSelectedCompany(record);
setJobModalOpen(true);
}}
2026-06-18 02:30:11 +08:00
>
</Button>,
<Button
key="remove"
type="link"
size="small"
danger
2026-06-18 02:30:11 +08:00
onClick={() => {
Modal.confirm({
title: '确认移除',
content: `确定移除「${record.companyName}」吗?`,
2026-06-18 02:30:11 +08:00
onOk: async () => {
const res = await removeParticipatingCompany(record.id, fairId);
if (res.code === 200) {
message.success('移除成功');
actionRef.current?.reload();
}
2026-06-18 02:30:11 +08:00
},
});
}}
>
</Button>,
],
},
]}
/>
</Card>
{/* 编辑招聘会基本信息弹窗 */}
<EditModal
open={editModalOpen}
values={fairInfo}
fairTypeOptions={fairTypeOptions}
regionOptions={regionOptions}
venueOptions={venueOptions}
onCreateDictOption={handleCreateDictOption}
2026-06-18 02:30:11 +08:00
onCancel={() => setEditModalOpen(false)}
onSubmit={async (values) => {
const res = await updateOutdoorFair(values as any);
if (res.code === 200) {
message.success('修改成功');
setEditModalOpen(false);
onFairUpdate();
} else {
message.error(res.msg || '操作失败');
}
}}
/>
{/* 添加企业弹窗 */}
<CompanyAddModal
open={companyModalOpen}
fairId={fairId}
onCancel={() => setCompanyModalOpen(false)}
onSuccess={() => {
setCompanyModalOpen(false);
actionRef.current?.reload();
}}
2026-06-18 02:30:11 +08:00
/>
{/* 添加/编辑岗位弹窗 */}
<JobEditModal
open={jobModalOpen}
fairId={fairId}
company={selectedCompany}
job={editingJob}
onCancel={() => {
setJobModalOpen(false);
setEditingJob(null);
setSelectedCompany(null);
}}
onSuccess={() => {
setJobModalOpen(false);
setEditingJob(null);
actionRef.current?.reload();
}}
2026-06-18 02:30:11 +08:00
/>
</div>
);
};
export default FairInfoTab;