2026-06-25 23:13:42 +08:00
|
|
|
|
import React, { useCallback, useEffect, useState } from 'react';
|
2026-06-25 12:47:53 +08:00
|
|
|
|
import { history, useAccess } from '@umijs/max';
|
2026-07-23 10:52:38 +08:00
|
|
|
|
import { Button, Card, Descriptions, Divider, Image, Tag, message } from 'antd';
|
2026-06-25 23:13:42 +08:00
|
|
|
|
import { FormOutlined } from '@ant-design/icons';
|
2026-06-25 12:47:53 +08:00
|
|
|
|
import type { OutdoorFairDictForm, OutdoorFairItem } from '@/services/jobportal/outdoorFair';
|
|
|
|
|
|
import { addOutdoorFairDictOption, updateOutdoorFair } from '@/services/jobportal/outdoorFair';
|
2026-06-25 23:13:42 +08:00
|
|
|
|
import { getVenueInfo, getVenueInfoList } from '@/services/jobportal/venueInfo';
|
|
|
|
|
|
import type { VenueInfoItem } from '@/services/jobportal/venueInfo';
|
|
|
|
|
|
import { getDictSelectOption, getDictValueEnum } from '@/services/system/dict';
|
2026-06-18 02:30:11 +08:00
|
|
|
|
import EditModal from '@/pages/Jobfair/Outdoorfair/components/EditModal';
|
|
|
|
|
|
|
2026-06-25 12:47:53 +08:00
|
|
|
|
const OUTDOOR_FAIR_TYPE_DICT = 'outdoor_fair_type';
|
|
|
|
|
|
const OUTDOOR_FAIR_REGION_DICT = 'outdoor_fair_region';
|
2026-06-25 23:13:42 +08:00
|
|
|
|
const VENUE_INFO_TYPE_DICT = 'venue_info_type';
|
2026-06-25 12:47:53 +08:00
|
|
|
|
|
2026-06-18 02:30:11 +08:00
|
|
|
|
interface Props {
|
|
|
|
|
|
fairId: number;
|
|
|
|
|
|
fairInfo: OutdoorFairItem;
|
|
|
|
|
|
onFairUpdate: () => void;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-25 23:13:42 +08:00
|
|
|
|
const FairInfoTab: React.FC<Props> = ({ fairInfo, onFairUpdate }) => {
|
2026-06-18 02:30:11 +08:00
|
|
|
|
const access = useAccess();
|
|
|
|
|
|
const [editModalOpen, setEditModalOpen] = useState(false);
|
2026-06-25 12:47:53 +08:00
|
|
|
|
const [fairTypeOptions, setFairTypeOptions] = useState<any[]>([]);
|
|
|
|
|
|
const [regionOptions, setRegionOptions] = useState<any[]>([]);
|
|
|
|
|
|
const [venueOptions, setVenueOptions] = useState<any[]>([]);
|
2026-06-25 23:13:42 +08:00
|
|
|
|
const [venueDetail, setVenueDetail] = useState<VenueInfoItem>();
|
|
|
|
|
|
const [venueTypeValueEnum, setVenueTypeValueEnum] = useState<Record<string, any>>({});
|
2026-06-25 12:47:53 +08:00
|
|
|
|
|
|
|
|
|
|
const refreshEditModalOptions = useCallback(async () => {
|
2026-06-25 23:13:42 +08:00
|
|
|
|
const [typeOptions, regionSelectOptions, venueRes, venueTypeValueEnum] = await Promise.all([
|
2026-06-25 12:47:53 +08:00
|
|
|
|
getDictSelectOption(OUTDOOR_FAIR_TYPE_DICT),
|
|
|
|
|
|
getDictSelectOption(OUTDOOR_FAIR_REGION_DICT),
|
|
|
|
|
|
getVenueInfoList({ current: 1, pageSize: 999 }),
|
2026-06-25 23:13:42 +08:00
|
|
|
|
getDictValueEnum(VENUE_INFO_TYPE_DICT),
|
2026-06-25 12:47:53 +08:00
|
|
|
|
]);
|
|
|
|
|
|
setFairTypeOptions(typeOptions);
|
|
|
|
|
|
setRegionOptions(regionSelectOptions);
|
2026-06-25 23:13:42 +08:00
|
|
|
|
setVenueTypeValueEnum(venueTypeValueEnum);
|
2026-06-25 12:47:53 +08:00
|
|
|
|
if (venueRes.code === 200) {
|
|
|
|
|
|
setVenueOptions(
|
|
|
|
|
|
(venueRes.rows || []).map((item) => ({
|
|
|
|
|
|
label: item.venueName,
|
|
|
|
|
|
value: item.id,
|
|
|
|
|
|
venueAddress: item.venueAddress,
|
|
|
|
|
|
floorCount: item.floorCount,
|
|
|
|
|
|
})),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
refreshEditModalOptions();
|
|
|
|
|
|
}, [refreshEditModalOptions]);
|
|
|
|
|
|
|
2026-06-25 23:13:42 +08:00
|
|
|
|
// 拉取绑定场地的详细信息
|
2026-06-25 16:07:43 +08:00
|
|
|
|
useEffect(() => {
|
2026-06-25 23:13:42 +08:00
|
|
|
|
const fetchVenueDetail = async () => {
|
|
|
|
|
|
if (!fairInfo.venueId) {
|
|
|
|
|
|
setVenueDetail(undefined);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await getVenueInfo(fairInfo.venueId);
|
|
|
|
|
|
if (res.code === 200 && res.data) {
|
|
|
|
|
|
setVenueDetail(res.data);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
setVenueDetail(undefined);
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
setVenueDetail(undefined);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
fetchVenueDetail();
|
|
|
|
|
|
}, [fairInfo.venueId]);
|
2026-06-25 16:07:43 +08:00
|
|
|
|
|
2026-06-25 12:47:53 +08:00
|
|
|
|
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> = {
|
2026-06-25 12:47:53 +08:00
|
|
|
|
综合类: 'blue',
|
|
|
|
|
|
校园招聘: 'green',
|
|
|
|
|
|
行业专场: 'orange',
|
|
|
|
|
|
高新技术专场: 'purple',
|
|
|
|
|
|
智能制造专场: 'cyan',
|
|
|
|
|
|
文旅专场: 'magenta',
|
|
|
|
|
|
其他: 'default',
|
2026-06-18 02:30:11 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
2026-06-25 23:13:42 +08:00
|
|
|
|
<>
|
2026-06-18 02:30:11 +08:00
|
|
|
|
<Card
|
|
|
|
|
|
title="基本信息"
|
|
|
|
|
|
extra={
|
|
|
|
|
|
<Button
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
icon={<FormOutlined />}
|
|
|
|
|
|
hidden={!access.hasPerms('cms:outdoorFair:edit')}
|
|
|
|
|
|
onClick={() => setEditModalOpen(true)}
|
|
|
|
|
|
>
|
|
|
|
|
|
编辑信息
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Descriptions column={3} bordered size="small">
|
2026-06-25 12:47:53 +08:00
|
|
|
|
<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>
|
2026-06-25 12:47:53 +08:00
|
|
|
|
<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>
|
2026-06-25 12:47:53 +08:00
|
|
|
|
<Descriptions.Item label="线上申请">
|
|
|
|
|
|
{fairInfo.onlineApply ? '是' : '否'}
|
|
|
|
|
|
</Descriptions.Item>
|
2026-06-25 23:13:42 +08:00
|
|
|
|
<Descriptions.Item label="招聘会照片" span={3}>
|
|
|
|
|
|
{fairInfo.photoUrl ? (
|
|
|
|
|
|
<Image
|
|
|
|
|
|
src={fairInfo.photoUrl}
|
|
|
|
|
|
alt={fairInfo.title}
|
|
|
|
|
|
width={240}
|
|
|
|
|
|
style={{ objectFit: 'cover', borderRadius: 4 }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
'--'
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Descriptions.Item>
|
2026-07-23 10:21:59 +08:00
|
|
|
|
<Descriptions.Item label="招聘会简介" span={3}>
|
|
|
|
|
|
<div style={{ whiteSpace: 'pre-wrap' }}>{fairInfo.fairIntroduction || '--'}</div>
|
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="招聘会整体流程" span={3}>
|
|
|
|
|
|
<div style={{ whiteSpace: 'pre-wrap' }}>{fairInfo.fairProcess || '--'}</div>
|
|
|
|
|
|
</Descriptions.Item>
|
2026-06-18 02:30:11 +08:00
|
|
|
|
</Descriptions>
|
2026-07-23 10:52:38 +08:00
|
|
|
|
<Divider orientation="left" plain>
|
|
|
|
|
|
企业参会专属信息(仅报名企业可见)
|
|
|
|
|
|
</Divider>
|
|
|
|
|
|
<Descriptions column={3} bordered size="small">
|
|
|
|
|
|
<Descriptions.Item label="需携带资料" span={3}>
|
|
|
|
|
|
<div style={{ whiteSpace: 'pre-wrap' }}>{fairInfo.requiredMaterials || '--'}</div>
|
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="到场须知" span={3}>
|
|
|
|
|
|
<div style={{ whiteSpace: 'pre-wrap' }}>{fairInfo.attendanceNotes || '--'}</div>
|
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
|
</Descriptions>
|
2026-06-18 02:30:11 +08:00
|
|
|
|
</Card>
|
|
|
|
|
|
|
2026-06-25 23:13:42 +08:00
|
|
|
|
<Card title="场地信息" style={{ marginTop: 16 }}>
|
|
|
|
|
|
{venueDetail ? (
|
|
|
|
|
|
<Descriptions column={3} bordered size="small">
|
|
|
|
|
|
<Descriptions.Item label="场地名称" span={3}>
|
|
|
|
|
|
{venueDetail.venueName || '--'}
|
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="场地类型">
|
|
|
|
|
|
{venueTypeValueEnum[venueDetail.venueType]?.text ||
|
|
|
|
|
|
venueTypeValueEnum[venueDetail.venueType]?.label ||
|
|
|
|
|
|
venueDetail.venueType ||
|
|
|
|
|
|
'--'}
|
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="场地面积">
|
|
|
|
|
|
{venueDetail.venueArea != null ? `${venueDetail.venueArea} ㎡` : '--'}
|
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="展位数量">
|
|
|
|
|
|
{venueDetail.floorCount ?? '--'}
|
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="场地地址" span={2}>
|
|
|
|
|
|
{venueDetail.venueAddress || '--'}
|
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="联系电话">
|
|
|
|
|
|
{venueDetail.contactPhone || '--'}
|
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="乘坐线路" span={3}>
|
|
|
|
|
|
{venueDetail.routeLineNames || '--'}
|
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="经办日期">
|
|
|
|
|
|
{venueDetail.handleDate || '--'}
|
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="经办机构">
|
|
|
|
|
|
{venueDetail.handleOrg || '--'}
|
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="经办人">
|
|
|
|
|
|
{venueDetail.handler || '--'}
|
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="备注" span={3}>
|
|
|
|
|
|
{venueDetail.remark || '--'}
|
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
|
</Descriptions>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<div style={{ color: '#999' }}>该招聘会未绑定场地信息</div>
|
|
|
|
|
|
)}
|
2026-06-18 02:30:11 +08:00
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
|
|
<EditModal
|
|
|
|
|
|
open={editModalOpen}
|
|
|
|
|
|
values={fairInfo}
|
2026-06-25 12:47:53 +08:00
|
|
|
|
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 || '操作失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
2026-06-25 23:13:42 +08:00
|
|
|
|
</>
|
2026-06-18 02:30:11 +08:00
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export default FairInfoTab;
|