Compare commits
2 Commits
5213215c65
...
4db3255997
| Author | SHA1 | Date | |
|---|---|---|---|
| 4db3255997 | |||
| c6d665c370 |
@@ -301,6 +301,11 @@ export default [
|
|||||||
path: '/jobfair/venue-info',
|
path: '/jobfair/venue-info',
|
||||||
component: './Jobfair/Venueinfo',
|
component: './Jobfair/Venueinfo',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: '机构管理',
|
||||||
|
path: '/jobfair/institution',
|
||||||
|
component: './Jobfair/Institution',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: '场地信息详情',
|
name: '场地信息详情',
|
||||||
path: '/jobfair/venue-info/detail',
|
path: '/jobfair/venue-info/detail',
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
import { useAccess } from '@umijs/max';
|
import { useAccess } from '@umijs/max';
|
||||||
import {
|
import {
|
||||||
PageContainer,
|
PageContainer,
|
||||||
@@ -11,6 +12,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
Tabs,
|
Tabs,
|
||||||
Card,
|
Card,
|
||||||
|
DatePicker,
|
||||||
Row,
|
Row,
|
||||||
Col,
|
Col,
|
||||||
Statistic,
|
Statistic,
|
||||||
@@ -32,6 +34,7 @@ import {
|
|||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
EditOutlined,
|
EditOutlined,
|
||||||
SettingOutlined,
|
SettingOutlined,
|
||||||
|
EyeOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { getDictValueEnum } from '@/services/system/dict';
|
import { getDictValueEnum } from '@/services/system/dict';
|
||||||
import {
|
import {
|
||||||
@@ -44,13 +47,21 @@ import type {
|
|||||||
CrossCityAllianceItem,
|
CrossCityAllianceItem,
|
||||||
CrossCityAllianceForm,
|
CrossCityAllianceForm,
|
||||||
} from '@/services/jobfair/crossCityAlliance';
|
} from '@/services/jobfair/crossCityAlliance';
|
||||||
import { getCrossDomainJobs } from '@/services/jobfair/publicJobFair';
|
import { getCrossDomainJobs, getCrossDomainStatistics } from '@/services/jobfair/publicJobFair';
|
||||||
import type { CrossDomainJobItem } from '@/services/jobfair/publicJobFair';
|
import type { CrossDomainJobItem } from '@/services/jobfair/publicJobFair';
|
||||||
|
|
||||||
const { Title, Text } = Typography;
|
const { Title, Text } = Typography;
|
||||||
|
const { RangePicker } = DatePicker;
|
||||||
|
|
||||||
const PAGE_SIZE_FOR_STATS = 10000;
|
const PAGE_SIZE_FOR_STATS = 10000;
|
||||||
|
|
||||||
|
const getDefaultDateRange = (): [Dayjs, Dayjs] => [dayjs().subtract(1, 'month'), dayjs()];
|
||||||
|
|
||||||
|
const getDateRangePresets = () => [
|
||||||
|
{ label: '近7天', value: [dayjs().subtract(6, 'day'), dayjs()] as [Dayjs, Dayjs] },
|
||||||
|
{ label: '近1个月', value: getDefaultDateRange() },
|
||||||
|
];
|
||||||
|
|
||||||
const getValueEnumText = (valueEnum: any, value?: string | number) => {
|
const getValueEnumText = (valueEnum: any, value?: string | number) => {
|
||||||
if (value === undefined || value === null || value === '') return '-';
|
if (value === undefined || value === null || value === '') return '-';
|
||||||
const option = valueEnum[String(value)] || valueEnum[Number(value)];
|
const option = valueEnum[String(value)] || valueEnum[Number(value)];
|
||||||
@@ -273,7 +284,8 @@ const CitySelectTab: React.FC<CitySelectTabProps> = ({
|
|||||||
</Spin>
|
</Spin>
|
||||||
<div style={{ marginTop: 12, color: '#888' }}>
|
<div style={{ marginTop: 12, color: '#888' }}>
|
||||||
已选择 {selectedCities.length > 0 ? selectedCities.length + ' 个区域' : '全部区域'}
|
已选择 {selectedCities.length > 0 ? selectedCities.length + ' 个区域' : '全部区域'}
|
||||||
<span style={{ marginLeft: 8 }}>(选择区域后,到「各地市就业信息」查看对应岗位)</span>
|
{/* 暂时隐藏各地市就业信息栏目,保留原提示便于后续恢复。 */}
|
||||||
|
{/* <span style={{ marginLeft: 8 }}>(选择区域后,到「各地市就业信息」查看对应岗位)</span> */}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
@@ -379,12 +391,13 @@ const JobsTab: React.FC<{ selectedCities: string[] }> = ({ selectedCities }) =>
|
|||||||
// ==================== 数据统计 Tab ====================
|
// ==================== 数据统计 Tab ====================
|
||||||
|
|
||||||
const DataStatsTab: React.FC = () => {
|
const DataStatsTab: React.FC = () => {
|
||||||
|
const [dateRange, setDateRange] = useState<[Dayjs, Dayjs]>(getDefaultDateRange());
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [stats, setStats] = useState({
|
const [stats, setStats] = useState({
|
||||||
totalCities: 0,
|
|
||||||
totalCompanies: 0,
|
totalCompanies: 0,
|
||||||
totalJobs: 0,
|
totalJobs: 0,
|
||||||
totalVacancies: 0,
|
totalVacancies: 0,
|
||||||
|
totalViews: 0,
|
||||||
industryDistribution: [] as { industry: string; count: number; percentage: string }[],
|
industryDistribution: [] as { industry: string; count: number; percentage: string }[],
|
||||||
monthlyJobs: [] as { month: string; count: number }[],
|
monthlyJobs: [] as { month: string; count: number }[],
|
||||||
});
|
});
|
||||||
@@ -393,17 +406,26 @@ const DataStatsTab: React.FC = () => {
|
|||||||
const loadStats = async () => {
|
const loadStats = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const [cityRes, jobRes] = await Promise.all([
|
const startDate = dateRange[0].format('YYYY-MM-DD');
|
||||||
getCrossCityAllianceList({ pageSize: PAGE_SIZE_FOR_STATS }),
|
const endDate = dateRange[1].format('YYYY-MM-DD');
|
||||||
getCrossDomainJobs({ current: 1, pageSize: PAGE_SIZE_FOR_STATS }),
|
const [statisticsRes, jobRes] = await Promise.all([
|
||||||
|
getCrossDomainStatistics({ startDate, endDate }),
|
||||||
|
getCrossDomainJobs({
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
current: 1,
|
||||||
|
pageSize: PAGE_SIZE_FOR_STATS,
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
const jobs = jobRes.rows || [];
|
if (statisticsRes.code !== 200 || !statisticsRes.data) {
|
||||||
const companyIds = new Set(
|
throw new Error(statisticsRes.msg || '跨域招聘会统计加载失败');
|
||||||
jobs
|
}
|
||||||
.map((job) => job.companyId)
|
|
||||||
.filter((companyId) => companyId !== undefined && companyId !== null),
|
// 岗位可能被多场跨域招聘会引用,图表也必须按岗位 ID 去重。
|
||||||
|
const jobs = Array.from(
|
||||||
|
new Map((jobRes.rows || []).map((job) => [String(job.jobId), job])).values(),
|
||||||
);
|
);
|
||||||
const totalJobs = jobRes.total || jobs.length;
|
const totalJobs = statisticsRes.data.jobCount;
|
||||||
const industryCount = jobs.reduce<Record<string, number>>((acc, job) => {
|
const industryCount = jobs.reduce<Record<string, number>>((acc, job) => {
|
||||||
const industry = job.industry || '其他';
|
const industry = job.industry || '其他';
|
||||||
acc[industry] = (acc[industry] || 0) + 1;
|
acc[industry] = (acc[industry] || 0) + 1;
|
||||||
@@ -417,10 +439,10 @@ const DataStatsTab: React.FC = () => {
|
|||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
setStats({
|
setStats({
|
||||||
totalCities: cityRes.total || cityRes.rows?.length || 0,
|
totalCompanies: statisticsRes.data.companyCount,
|
||||||
totalCompanies: companyIds.size,
|
|
||||||
totalJobs,
|
totalJobs,
|
||||||
totalVacancies: jobs.reduce((sum, job) => sum + (Number(job.vacancies) || 0), 0),
|
totalVacancies: statisticsRes.data.demandCount,
|
||||||
|
totalViews: statisticsRes.data.viewCount,
|
||||||
industryDistribution: Object.entries(industryCount).map(([industry, count]) => ({
|
industryDistribution: Object.entries(industryCount).map(([industry, count]) => ({
|
||||||
industry,
|
industry,
|
||||||
count,
|
count,
|
||||||
@@ -430,12 +452,14 @@ const DataStatsTab: React.FC = () => {
|
|||||||
.sort(([a], [b]) => a.localeCompare(b))
|
.sort(([a], [b]) => a.localeCompare(b))
|
||||||
.map(([month, count]) => ({ month, count })),
|
.map(([month, count]) => ({ month, count })),
|
||||||
});
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error?.message || '跨域招聘会统计加载失败,请稍后重试');
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
loadStats();
|
loadStats();
|
||||||
}, []);
|
}, [dateRange]);
|
||||||
|
|
||||||
const industryColumns = [
|
const industryColumns = [
|
||||||
{ title: '行业', dataIndex: 'industry', key: 'industry' },
|
{ title: '行业', dataIndex: 'industry', key: 'industry' },
|
||||||
@@ -457,38 +481,44 @@ const DataStatsTab: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<Spin spinning={loading}>
|
<Spin spinning={loading}>
|
||||||
<div style={{ padding: '0 0 24px 0' }}>
|
<div style={{ padding: '0 0 24px 0' }}>
|
||||||
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
|
<Card style={{ marginBottom: 16 }}>
|
||||||
<Col xs={24} sm={6}>
|
<Space wrap>
|
||||||
<Card>
|
<span>统计时间</span>
|
||||||
<Statistic
|
<RangePicker
|
||||||
title="联盟城市数"
|
value={dateRange}
|
||||||
value={stats.totalCities}
|
allowClear={false}
|
||||||
prefix={<EnvironmentOutlined />}
|
presets={getDateRangePresets()}
|
||||||
suffix="个"
|
onChange={(value) => {
|
||||||
|
if (value?.[0] && value?.[1]) {
|
||||||
|
setDateRange(value as [Dayjs, Dayjs]);
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
</Space>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
|
||||||
<Col xs={24} sm={6}>
|
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
|
||||||
|
<Col xs={24} sm={12} lg={6}>
|
||||||
<Card>
|
<Card>
|
||||||
<Statistic
|
<Statistic
|
||||||
title="参会企业总数"
|
title="参会企业"
|
||||||
value={stats.totalCompanies}
|
value={stats.totalCompanies}
|
||||||
prefix={<BankOutlined />}
|
prefix={<BankOutlined />}
|
||||||
suffix="家"
|
suffix="家"
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={24} sm={6}>
|
<Col xs={24} sm={12} lg={6}>
|
||||||
<Card>
|
<Card>
|
||||||
<Statistic
|
<Statistic
|
||||||
title="发布岗位总数"
|
title="发布岗位数"
|
||||||
value={stats.totalJobs}
|
value={stats.totalJobs}
|
||||||
prefix={<FileTextOutlined />}
|
prefix={<FileTextOutlined />}
|
||||||
suffix="个"
|
suffix="个"
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={24} sm={6}>
|
<Col xs={24} sm={12} lg={6}>
|
||||||
<Card>
|
<Card>
|
||||||
<Statistic
|
<Statistic
|
||||||
title="招聘需求人数"
|
title="招聘需求人数"
|
||||||
@@ -498,6 +528,16 @@ const DataStatsTab: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
|
<Col xs={24} sm={12} lg={6}>
|
||||||
|
<Card>
|
||||||
|
<Statistic
|
||||||
|
title="岗位浏览量"
|
||||||
|
value={stats.totalViews}
|
||||||
|
prefix={<EyeOutlined />}
|
||||||
|
suffix="次"
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
@@ -524,6 +564,11 @@ const DataStatsTab: React.FC = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
|
<Card size="small" title="统计口径">
|
||||||
|
仅统计时间范围内招聘会类型为“线上”且标记为“跨域”的招聘会;参会企业按有效报名企业统计,即使企业未选择岗位也计入。岗位、招聘需求人数和浏览量均按岗位
|
||||||
|
ID 去重,招聘数为空、填写“若干”或非正数时按 1 人计算。
|
||||||
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</Spin>
|
</Spin>
|
||||||
);
|
);
|
||||||
@@ -563,6 +608,7 @@ const CrossCityFair: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const tabItems = [
|
const tabItems = [
|
||||||
|
/* 暂时隐藏各地市就业信息栏目,保留原实现便于后续恢复。
|
||||||
{
|
{
|
||||||
key: 'city-jobs',
|
key: 'city-jobs',
|
||||||
label: (
|
label: (
|
||||||
@@ -573,6 +619,7 @@ const CrossCityFair: React.FC = () => {
|
|||||||
),
|
),
|
||||||
children: <JobsTab selectedCities={selectedCities} />,
|
children: <JobsTab selectedCities={selectedCities} />,
|
||||||
},
|
},
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
key: 'statistics',
|
key: 'statistics',
|
||||||
label: (
|
label: (
|
||||||
@@ -612,7 +659,7 @@ const CrossCityFair: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Card>
|
<Card>
|
||||||
<Tabs defaultActiveKey="city-jobs" items={tabItems} tabBarStyle={{ marginBottom: 0 }} />
|
<Tabs defaultActiveKey="statistics" items={tabItems} tabBarStyle={{ marginBottom: 0 }} />
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<AllianceManageModal
|
<AllianceManageModal
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
@@ -37,12 +38,19 @@ interface FairOption {
|
|||||||
const formatDate = (value: any): string | undefined =>
|
const formatDate = (value: any): string | undefined =>
|
||||||
value && typeof value.format === 'function' ? value.format('YYYY-MM-DD') : undefined;
|
value && typeof value.format === 'function' ? value.format('YYYY-MM-DD') : undefined;
|
||||||
|
|
||||||
|
const getDefaultDateRange = (): [Dayjs, Dayjs] => [dayjs().subtract(1, 'month'), dayjs()];
|
||||||
|
|
||||||
|
const getDateRangePresets = () => [
|
||||||
|
{ label: '近7天', value: [dayjs().subtract(6, 'day'), dayjs()] as [Dayjs, Dayjs] },
|
||||||
|
{ label: '近1个月', value: getDefaultDateRange() },
|
||||||
|
];
|
||||||
|
|
||||||
const FairStatistics: React.FC = () => {
|
const FairStatistics: React.FC = () => {
|
||||||
const [fairType, setFairType] = useState<RecruitmentFairType>('online');
|
const [fairType, setFairType] = useState<RecruitmentFairType>('online');
|
||||||
const [scope, setScope] = useState<StatisticsScope>('single');
|
const [scope, setScope] = useState<StatisticsScope>('range');
|
||||||
const [fairOptions, setFairOptions] = useState<FairOption[]>([]);
|
const [fairOptions, setFairOptions] = useState<FairOption[]>([]);
|
||||||
const [selectedFairId, setSelectedFairId] = useState<string>();
|
const [selectedFairId, setSelectedFairId] = useState<string>();
|
||||||
const [dateRange, setDateRange] = useState<any>(null);
|
const [dateRange, setDateRange] = useState<any>(getDefaultDateRange());
|
||||||
const [loadingFairs, setLoadingFairs] = useState(false);
|
const [loadingFairs, setLoadingFairs] = useState(false);
|
||||||
const [loadingSummary, setLoadingSummary] = useState(false);
|
const [loadingSummary, setLoadingSummary] = useState(false);
|
||||||
const [summary, setSummary] = useState<FairStatisticsSummary>();
|
const [summary, setSummary] = useState<FairStatisticsSummary>();
|
||||||
@@ -97,7 +105,7 @@ const FairStatistics: React.FC = () => {
|
|||||||
|
|
||||||
// 切换类型时同步清空条件,避免新类型携带旧类型招聘会 ID 发起查询。
|
// 切换类型时同步清空条件,避免新类型携带旧类型招聘会 ID 发起查询。
|
||||||
setSelectedFairId(undefined);
|
setSelectedFairId(undefined);
|
||||||
setDateRange(null);
|
setDateRange(getDefaultDateRange());
|
||||||
setSummary(undefined);
|
setSummary(undefined);
|
||||||
setFairOptions([]);
|
setFairOptions([]);
|
||||||
setFairType(type);
|
setFairType(type);
|
||||||
@@ -190,7 +198,11 @@ const FairStatistics: React.FC = () => {
|
|||||||
{ label: '按起始时间统计', value: 'range' },
|
{ label: '按起始时间统计', value: 'range' },
|
||||||
]}
|
]}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
setScope(value as StatisticsScope);
|
const nextScope = value as StatisticsScope;
|
||||||
|
if (nextScope === 'range' && !dateRange) {
|
||||||
|
setDateRange(getDefaultDateRange());
|
||||||
|
}
|
||||||
|
setScope(nextScope);
|
||||||
setSummary(undefined);
|
setSummary(undefined);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -211,6 +223,8 @@ const FairStatistics: React.FC = () => {
|
|||||||
) : (
|
) : (
|
||||||
<RangePicker
|
<RangePicker
|
||||||
value={dateRange}
|
value={dateRange}
|
||||||
|
allowClear={false}
|
||||||
|
presets={getDateRangePresets()}
|
||||||
onChange={(value) => setDateRange(value)}
|
onChange={(value) => setDateRange(value)}
|
||||||
style={{ width: '100%', minWidth: 320 }}
|
style={{ width: '100%', minWidth: 320 }}
|
||||||
placeholder={['起始日期', '结束日期']}
|
placeholder={['起始日期', '结束日期']}
|
||||||
|
|||||||
172
src/pages/Jobfair/Institution/index.tsx
Normal file
172
src/pages/Jobfair/Institution/index.tsx
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
import React, { useRef, useState } from 'react';
|
||||||
|
import { useAccess } from '@umijs/max';
|
||||||
|
import { Button, message, Modal } from 'antd';
|
||||||
|
import {
|
||||||
|
ActionType,
|
||||||
|
ModalForm,
|
||||||
|
PageContainer,
|
||||||
|
ProColumns,
|
||||||
|
ProFormText,
|
||||||
|
ProFormTextArea,
|
||||||
|
ProTable,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import { DeleteOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons';
|
||||||
|
import {
|
||||||
|
addJobFairInstitution,
|
||||||
|
deleteJobFairInstitution,
|
||||||
|
getJobFairInstitutionList,
|
||||||
|
updateJobFairInstitution,
|
||||||
|
} from '@/services/jobportal/jobFairInstitution';
|
||||||
|
import type {
|
||||||
|
JobFairInstitutionForm,
|
||||||
|
JobFairInstitutionItem,
|
||||||
|
} from '@/services/jobportal/jobFairInstitution';
|
||||||
|
|
||||||
|
const InstitutionManagement: React.FC = () => {
|
||||||
|
const access = useAccess();
|
||||||
|
const actionRef = useRef<ActionType>();
|
||||||
|
const [formOpen, setFormOpen] = useState(false);
|
||||||
|
const [currentRow, setCurrentRow] = useState<JobFairInstitutionItem>();
|
||||||
|
|
||||||
|
const handleDelete = (record: JobFairInstitutionItem) => {
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认删除',
|
||||||
|
content: `确定要删除机构「${record.institutionName}」吗?如果该机构已被场地使用,需要先解除场地关联。`,
|
||||||
|
onOk: async () => {
|
||||||
|
const res = await deleteJobFairInstitution(record.id);
|
||||||
|
if (res.code === 200) {
|
||||||
|
message.success('删除成功');
|
||||||
|
actionRef.current?.reload();
|
||||||
|
} else {
|
||||||
|
message.error(res.msg || '删除失败');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns: ProColumns<JobFairInstitutionItem>[] = [
|
||||||
|
{
|
||||||
|
title: '机构名称',
|
||||||
|
dataIndex: 'institutionName',
|
||||||
|
ellipsis: true,
|
||||||
|
width: 260,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '机构简介',
|
||||||
|
dataIndex: 'institutionIntro',
|
||||||
|
ellipsis: true,
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
hideInSearch: true,
|
||||||
|
width: 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
valueType: 'option',
|
||||||
|
width: 160,
|
||||||
|
fixed: 'right',
|
||||||
|
render: (_, record) => [
|
||||||
|
<Button
|
||||||
|
key="edit"
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
icon={<EditOutlined />}
|
||||||
|
hidden={!access.hasPerms('cms:jobFairInstitution:edit')}
|
||||||
|
onClick={() => {
|
||||||
|
setCurrentRow(record);
|
||||||
|
setFormOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</Button>,
|
||||||
|
<Button
|
||||||
|
key="delete"
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
danger
|
||||||
|
icon={<DeleteOutlined />}
|
||||||
|
hidden={!access.hasPerms('cms:jobFairInstitution:remove')}
|
||||||
|
onClick={() => handleDelete(record)}
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</Button>,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageContainer>
|
||||||
|
<ProTable<JobFairInstitutionItem>
|
||||||
|
headerTitle="机构列表"
|
||||||
|
actionRef={actionRef}
|
||||||
|
rowKey="id"
|
||||||
|
columns={columns}
|
||||||
|
search={{ labelWidth: 'auto' }}
|
||||||
|
scroll={{ x: 'max-content' }}
|
||||||
|
request={async (params) => {
|
||||||
|
const res = await getJobFairInstitutionList({
|
||||||
|
institutionName: params.institutionName,
|
||||||
|
current: params.current,
|
||||||
|
pageSize: params.pageSize,
|
||||||
|
});
|
||||||
|
return { data: res.rows || [], total: res.total || 0, success: res.code === 200 };
|
||||||
|
}}
|
||||||
|
toolBarRender={() => [
|
||||||
|
<Button
|
||||||
|
key="add"
|
||||||
|
type="primary"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
hidden={!access.hasPerms('cms:jobFairInstitution:add')}
|
||||||
|
onClick={() => {
|
||||||
|
setCurrentRow(undefined);
|
||||||
|
setFormOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
新增机构
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ModalForm<JobFairInstitutionForm>
|
||||||
|
title={currentRow ? '编辑机构' : '新增机构'}
|
||||||
|
open={formOpen}
|
||||||
|
initialValues={currentRow}
|
||||||
|
modalProps={{ destroyOnClose: true, onCancel: () => setFormOpen(false) }}
|
||||||
|
onFinish={async (values) => {
|
||||||
|
const res = currentRow
|
||||||
|
? await updateJobFairInstitution({ ...values, id: currentRow.id })
|
||||||
|
: await addJobFairInstitution(values);
|
||||||
|
if (res.code === 200) {
|
||||||
|
message.success(currentRow ? '修改成功' : '新增成功');
|
||||||
|
setFormOpen(false);
|
||||||
|
setCurrentRow(undefined);
|
||||||
|
actionRef.current?.reload();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
message.error(res.msg || '操作失败');
|
||||||
|
return false;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ProFormText
|
||||||
|
name="institutionName"
|
||||||
|
label="机构名称"
|
||||||
|
rules={[{ required: true, message: '请输入机构名称' }]}
|
||||||
|
fieldProps={{ maxLength: 200, showCount: true }}
|
||||||
|
placeholder="请输入机构名称"
|
||||||
|
/>
|
||||||
|
<ProFormTextArea
|
||||||
|
name="institutionIntro"
|
||||||
|
label="机构简介"
|
||||||
|
fieldProps={{ maxLength: 2000, showCount: true, rows: 5 }}
|
||||||
|
placeholder="请输入机构简介"
|
||||||
|
/>
|
||||||
|
</ModalForm>
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InstitutionManagement;
|
||||||
@@ -123,6 +123,7 @@ const PublicJobFairList: React.FC = () => {
|
|||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
},
|
},
|
||||||
|
/* 暂时隐藏线上招聘会列表中的招聘会类型字段,保留原实现便于后续恢复。
|
||||||
{
|
{
|
||||||
title: '类型',
|
title: '类型',
|
||||||
dataIndex: 'jobFairType',
|
dataIndex: 'jobFairType',
|
||||||
@@ -130,6 +131,7 @@ const PublicJobFairList: React.FC = () => {
|
|||||||
valueEnum: jobFairTypeEnum,
|
valueEnum: jobFairTypeEnum,
|
||||||
render: (_, record) => <DictTag enums={jobFairTypeEnum} value={record.jobFairType} />,
|
render: (_, record) => <DictTag enums={jobFairTypeEnum} value={record.jobFairType} />,
|
||||||
},
|
},
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
title: '区域类型',
|
title: '区域类型',
|
||||||
dataIndex: 'jobFairRegionType',
|
dataIndex: 'jobFairRegionType',
|
||||||
|
|||||||
@@ -8,9 +8,21 @@ import {
|
|||||||
getVenueInfo,
|
getVenueInfo,
|
||||||
updateVenueInfo,
|
updateVenueInfo,
|
||||||
} from '@/services/jobportal/venueInfo';
|
} from '@/services/jobportal/venueInfo';
|
||||||
import type { VenueInfoDictForm, VenueInfoForm, VenueInfoItem } from '@/services/jobportal/venueInfo';
|
import type {
|
||||||
|
VenueInfoDictForm,
|
||||||
|
VenueInfoForm,
|
||||||
|
VenueInfoItem,
|
||||||
|
} from '@/services/jobportal/venueInfo';
|
||||||
import { getCmsLineList } from '@/services/area/subway';
|
import { getCmsLineList } from '@/services/area/subway';
|
||||||
import { getDictSelectOption, getDictValueEnum } from '@/services/system/dict';
|
import { getDictSelectOption, getDictValueEnum } from '@/services/system/dict';
|
||||||
|
import {
|
||||||
|
addJobFairInstitution,
|
||||||
|
getJobFairInstitutionList,
|
||||||
|
} from '@/services/jobportal/jobFairInstitution';
|
||||||
|
import type {
|
||||||
|
JobFairInstitutionForm,
|
||||||
|
JobFairInstitutionItem,
|
||||||
|
} from '@/services/jobportal/jobFairInstitution';
|
||||||
import EditModal from '../components/EditModal';
|
import EditModal from '../components/EditModal';
|
||||||
|
|
||||||
const VENUE_TYPE_DICT = 'venue_info_type';
|
const VENUE_TYPE_DICT = 'venue_info_type';
|
||||||
@@ -25,6 +37,7 @@ const VenueInfoDetail: React.FC = () => {
|
|||||||
const [venueTypeOptions, setVenueTypeOptions] = useState<any[]>([]);
|
const [venueTypeOptions, setVenueTypeOptions] = useState<any[]>([]);
|
||||||
const [venueTypeValueEnum, setVenueTypeValueEnum] = useState<Record<string, any>>({});
|
const [venueTypeValueEnum, setVenueTypeValueEnum] = useState<Record<string, any>>({});
|
||||||
const [lineOptions, setLineOptions] = useState<any[]>([]);
|
const [lineOptions, setLineOptions] = useState<any[]>([]);
|
||||||
|
const [institutionOptions, setInstitutionOptions] = useState<JobFairInstitutionItem[]>([]);
|
||||||
|
|
||||||
const fetchVenueInfo = useCallback(async () => {
|
const fetchVenueInfo = useCallback(async () => {
|
||||||
if (!venueId) {
|
if (!venueId) {
|
||||||
@@ -45,10 +58,11 @@ const VenueInfoDetail: React.FC = () => {
|
|||||||
}, [venueId]);
|
}, [venueId]);
|
||||||
|
|
||||||
const refreshOptions = useCallback(async () => {
|
const refreshOptions = useCallback(async () => {
|
||||||
const [valueEnum, options, lineRes] = await Promise.all([
|
const [valueEnum, options, lineRes, institutionRes] = await Promise.all([
|
||||||
getDictValueEnum(VENUE_TYPE_DICT),
|
getDictValueEnum(VENUE_TYPE_DICT),
|
||||||
getDictSelectOption(VENUE_TYPE_DICT),
|
getDictSelectOption(VENUE_TYPE_DICT),
|
||||||
getCmsLineList({ current: '1', pageSize: '999' } as any),
|
getCmsLineList({ current: '1', pageSize: '999' } as any),
|
||||||
|
getJobFairInstitutionList({ current: 1, pageSize: 9999 }),
|
||||||
]);
|
]);
|
||||||
setVenueTypeValueEnum(valueEnum);
|
setVenueTypeValueEnum(valueEnum);
|
||||||
setVenueTypeOptions(options);
|
setVenueTypeOptions(options);
|
||||||
@@ -60,8 +74,23 @@ const VenueInfoDetail: React.FC = () => {
|
|||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (institutionRes.code === 200) {
|
||||||
|
setInstitutionOptions(institutionRes.rows || []);
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleCreateInstitution = async (values: JobFairInstitutionForm) => {
|
||||||
|
const res = await addJobFairInstitution(values);
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
const listRes = await getJobFairInstitutionList({ current: 1, pageSize: 9999 });
|
||||||
|
if (listRes.code === 200) setInstitutionOptions(listRes.rows || []);
|
||||||
|
message.success('机构新增成功');
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
message.error(res.msg || '机构新增失败');
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchVenueInfo();
|
fetchVenueInfo();
|
||||||
refreshOptions();
|
refreshOptions();
|
||||||
@@ -80,11 +109,7 @@ const VenueInfoDetail: React.FC = () => {
|
|||||||
|
|
||||||
const venueTypeLabel = (val?: string) => {
|
const venueTypeLabel = (val?: string) => {
|
||||||
if (!val) return '--';
|
if (!val) return '--';
|
||||||
return (
|
return venueTypeValueEnum[val]?.text || venueTypeValueEnum[val]?.label || val;
|
||||||
venueTypeValueEnum[val]?.text ||
|
|
||||||
venueTypeValueEnum[val]?.label ||
|
|
||||||
val
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -120,27 +145,20 @@ const VenueInfoDetail: React.FC = () => {
|
|||||||
<Descriptions.Item label="场地面积">
|
<Descriptions.Item label="场地面积">
|
||||||
{venueInfo.venueArea != null ? `${venueInfo.venueArea} ㎡` : '--'}
|
{venueInfo.venueArea != null ? `${venueInfo.venueArea} ㎡` : '--'}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label="展位数量">
|
<Descriptions.Item label="展位数量">{venueInfo.floorCount ?? '--'}</Descriptions.Item>
|
||||||
{venueInfo.floorCount ?? '--'}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="场地地址" span={2}>
|
<Descriptions.Item label="场地地址" span={2}>
|
||||||
{venueInfo.venueAddress || '--'}
|
{venueInfo.venueAddress || '--'}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label="联系电话">
|
<Descriptions.Item label="联系电话">{venueInfo.contactPhone || '--'}</Descriptions.Item>
|
||||||
{venueInfo.contactPhone || '--'}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="乘坐线路" span={3}>
|
<Descriptions.Item label="乘坐线路" span={3}>
|
||||||
{venueInfo.routeLineNames || '--'}
|
{venueInfo.routeLineNames || '--'}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label="经办日期">
|
<Descriptions.Item label="经办日期">{venueInfo.handleDate || '--'}</Descriptions.Item>
|
||||||
{venueInfo.handleDate || '--'}
|
<Descriptions.Item label="经办机构">{venueInfo.handleOrg || '--'}</Descriptions.Item>
|
||||||
</Descriptions.Item>
|
<Descriptions.Item label="所属机构" span={3}>
|
||||||
<Descriptions.Item label="经办机构">
|
{venueInfo.institutionNames || '--'}
|
||||||
{venueInfo.handleOrg || '--'}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="经办人">
|
|
||||||
{venueInfo.handler || '--'}
|
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="经办人">{venueInfo.handler || '--'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="备注" span={3}>
|
<Descriptions.Item label="备注" span={3}>
|
||||||
{venueInfo.remark || '--'}
|
{venueInfo.remark || '--'}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
@@ -155,6 +173,9 @@ const VenueInfoDetail: React.FC = () => {
|
|||||||
values={venueInfo}
|
values={venueInfo}
|
||||||
venueTypeOptions={venueTypeOptions}
|
venueTypeOptions={venueTypeOptions}
|
||||||
lineOptions={lineOptions}
|
lineOptions={lineOptions}
|
||||||
|
institutionOptions={institutionOptions}
|
||||||
|
canCreateInstitution={access.hasPerms('cms:jobFairInstitution:add')}
|
||||||
|
onCreateInstitution={handleCreateInstitution}
|
||||||
onCreateDictOption={handleCreateDictOption}
|
onCreateDictOption={handleCreateDictOption}
|
||||||
onCancel={() => setEditModalOpen(false)}
|
onCancel={() => setEditModalOpen(false)}
|
||||||
onSubmit={async (values: VenueInfoForm) => {
|
onSubmit={async (values: VenueInfoForm) => {
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ import {
|
|||||||
} from '@ant-design/pro-components';
|
} from '@ant-design/pro-components';
|
||||||
import { Button, Divider, Form, Input, Modal, Space } from 'antd';
|
import { Button, Divider, Form, Input, Modal, Space } from 'antd';
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
|
import type {
|
||||||
|
JobFairInstitutionForm,
|
||||||
|
JobFairInstitutionItem,
|
||||||
|
} from '@/services/jobportal/jobFairInstitution';
|
||||||
import type {
|
import type {
|
||||||
VenueInfoDictForm,
|
VenueInfoDictForm,
|
||||||
VenueInfoForm,
|
VenueInfoForm,
|
||||||
@@ -21,8 +25,13 @@ interface EditModalProps {
|
|||||||
values?: VenueInfoItem;
|
values?: VenueInfoItem;
|
||||||
venueTypeOptions: any[];
|
venueTypeOptions: any[];
|
||||||
lineOptions: any[];
|
lineOptions: any[];
|
||||||
|
institutionOptions: JobFairInstitutionItem[];
|
||||||
|
canCreateInstitution: boolean;
|
||||||
defaultHandler?: string;
|
defaultHandler?: string;
|
||||||
onCreateDictOption: (values: VenueInfoDictForm) => Promise<boolean>;
|
onCreateDictOption: (values: VenueInfoDictForm) => Promise<boolean>;
|
||||||
|
onCreateInstitution: (
|
||||||
|
values: JobFairInstitutionForm,
|
||||||
|
) => Promise<JobFairInstitutionItem | undefined>;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
onSubmit: (values: VenueInfoForm) => Promise<void>;
|
onSubmit: (values: VenueInfoForm) => Promise<void>;
|
||||||
}
|
}
|
||||||
@@ -44,8 +53,11 @@ const EditModal: React.FC<EditModalProps> = ({
|
|||||||
values,
|
values,
|
||||||
venueTypeOptions,
|
venueTypeOptions,
|
||||||
lineOptions,
|
lineOptions,
|
||||||
|
institutionOptions,
|
||||||
|
canCreateInstitution,
|
||||||
defaultHandler,
|
defaultHandler,
|
||||||
onCreateDictOption,
|
onCreateDictOption,
|
||||||
|
onCreateInstitution,
|
||||||
onCancel,
|
onCancel,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
}) => {
|
}) => {
|
||||||
@@ -53,6 +65,9 @@ const EditModal: React.FC<EditModalProps> = ({
|
|||||||
const [dictModalOpen, setDictModalOpen] = useState(false);
|
const [dictModalOpen, setDictModalOpen] = useState(false);
|
||||||
const [dictInputValue, setDictInputValue] = useState('');
|
const [dictInputValue, setDictInputValue] = useState('');
|
||||||
const [dictSubmitLoading, setDictSubmitLoading] = useState(false);
|
const [dictSubmitLoading, setDictSubmitLoading] = useState(false);
|
||||||
|
const [institutionModalOpen, setInstitutionModalOpen] = useState(false);
|
||||||
|
const [institutionSubmitLoading, setInstitutionSubmitLoading] = useState(false);
|
||||||
|
const [institutionForm] = Form.useForm<JobFairInstitutionForm>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
@@ -61,6 +76,7 @@ const EditModal: React.FC<EditModalProps> = ({
|
|||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
...values,
|
...values,
|
||||||
routeLineIds: splitRouteLineIds(values.routeLineIds),
|
routeLineIds: splitRouteLineIds(values.routeLineIds),
|
||||||
|
institutionIds: values.institutionIds || [],
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
@@ -111,6 +127,45 @@ const EditModal: React.FC<EditModalProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderInstitutionDropdown = (menu: React.ReactElement): React.ReactElement => (
|
||||||
|
<>
|
||||||
|
{menu}
|
||||||
|
{canCreateInstitution && (
|
||||||
|
<>
|
||||||
|
<Divider style={{ margin: '8px 0' }} />
|
||||||
|
<Space style={{ padding: '0 8px 8px' }}>
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
onMouseDown={(event) => event.preventDefault()}
|
||||||
|
onClick={() => {
|
||||||
|
institutionForm.resetFields();
|
||||||
|
setInstitutionModalOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
快速新增机构
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleInstitutionModalOk = async () => {
|
||||||
|
try {
|
||||||
|
const values = await institutionForm.validateFields();
|
||||||
|
setInstitutionSubmitLoading(true);
|
||||||
|
const created = await onCreateInstitution(values);
|
||||||
|
if (created) {
|
||||||
|
const selectedIds = (form.getFieldValue('institutionIds') || []) as number[];
|
||||||
|
form.setFieldValue('institutionIds', [...selectedIds, created.id]);
|
||||||
|
setInstitutionModalOpen(false);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setInstitutionSubmitLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalForm
|
<ModalForm
|
||||||
title={values ? '编辑场地' : '添加场地'}
|
title={values ? '编辑场地' : '添加场地'}
|
||||||
@@ -190,6 +245,21 @@ const EditModal: React.FC<EditModalProps> = ({
|
|||||||
}}
|
}}
|
||||||
placeholder="请选择乘坐线路"
|
placeholder="请选择乘坐线路"
|
||||||
/>
|
/>
|
||||||
|
<ProFormSelect
|
||||||
|
name="institutionIds"
|
||||||
|
label="所属机构"
|
||||||
|
mode="multiple"
|
||||||
|
options={institutionOptions.map((item) => ({
|
||||||
|
label: item.institutionName,
|
||||||
|
value: item.id,
|
||||||
|
}))}
|
||||||
|
fieldProps={{
|
||||||
|
showSearch: true,
|
||||||
|
optionFilterProp: 'label',
|
||||||
|
dropdownRender: renderInstitutionDropdown,
|
||||||
|
}}
|
||||||
|
placeholder="请选择所属机构,可多选"
|
||||||
|
/>
|
||||||
<ProFormTextArea
|
<ProFormTextArea
|
||||||
name="remark"
|
name="remark"
|
||||||
label="备注"
|
label="备注"
|
||||||
@@ -224,6 +294,27 @@ const EditModal: React.FC<EditModalProps> = ({
|
|||||||
onPressEnter={handleDictModalOk}
|
onPressEnter={handleDictModalOk}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
<Modal
|
||||||
|
title="快速新增机构"
|
||||||
|
open={institutionModalOpen}
|
||||||
|
confirmLoading={institutionSubmitLoading}
|
||||||
|
onOk={handleInstitutionModalOk}
|
||||||
|
onCancel={() => setInstitutionModalOpen(false)}
|
||||||
|
destroyOnClose
|
||||||
|
>
|
||||||
|
<Form form={institutionForm} layout="vertical">
|
||||||
|
<Form.Item
|
||||||
|
name="institutionName"
|
||||||
|
label="机构名称"
|
||||||
|
rules={[{ required: true, message: '请输入机构名称' }]}
|
||||||
|
>
|
||||||
|
<Input maxLength={200} showCount placeholder="请输入机构名称" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="institutionIntro" label="机构简介">
|
||||||
|
<Input.TextArea maxLength={2000} showCount rows={4} placeholder="请输入机构简介" />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
</ModalForm>
|
</ModalForm>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,6 +18,14 @@ import type {
|
|||||||
} from '@/services/jobportal/venueInfo';
|
} from '@/services/jobportal/venueInfo';
|
||||||
import { getCmsLineList } from '@/services/area/subway';
|
import { getCmsLineList } from '@/services/area/subway';
|
||||||
import { getDictSelectOption, getDictValueEnum } from '@/services/system/dict';
|
import { getDictSelectOption, getDictValueEnum } from '@/services/system/dict';
|
||||||
|
import {
|
||||||
|
addJobFairInstitution,
|
||||||
|
getJobFairInstitutionList,
|
||||||
|
} from '@/services/jobportal/jobFairInstitution';
|
||||||
|
import type {
|
||||||
|
JobFairInstitutionForm,
|
||||||
|
JobFairInstitutionItem,
|
||||||
|
} from '@/services/jobportal/jobFairInstitution';
|
||||||
import EditModal from './components/EditModal';
|
import EditModal from './components/EditModal';
|
||||||
import VenueBoothMapModal from './components/VenueBoothMapModal';
|
import VenueBoothMapModal from './components/VenueBoothMapModal';
|
||||||
|
|
||||||
@@ -33,6 +41,7 @@ const VenueInfoList: React.FC = () => {
|
|||||||
const [venueTypeValueEnum, setVenueTypeValueEnum] = useState<Record<string, any>>({});
|
const [venueTypeValueEnum, setVenueTypeValueEnum] = useState<Record<string, any>>({});
|
||||||
const [venueTypeOptions, setVenueTypeOptions] = useState<any[]>([]);
|
const [venueTypeOptions, setVenueTypeOptions] = useState<any[]>([]);
|
||||||
const [lineOptions, setLineOptions] = useState<any[]>([]);
|
const [lineOptions, setLineOptions] = useState<any[]>([]);
|
||||||
|
const [institutionOptions, setInstitutionOptions] = useState<JobFairInstitutionItem[]>([]);
|
||||||
|
|
||||||
const defaultHandler =
|
const defaultHandler =
|
||||||
initialState?.currentUser?.nickName || initialState?.currentUser?.userName || undefined;
|
initialState?.currentUser?.nickName || initialState?.currentUser?.userName || undefined;
|
||||||
@@ -58,10 +67,29 @@ const VenueInfoList: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const refreshInstitutionOptions = useCallback(async () => {
|
||||||
|
const res = await getJobFairInstitutionList({ current: 1, pageSize: 9999 });
|
||||||
|
if (res.code === 200) {
|
||||||
|
setInstitutionOptions(res.rows || []);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refreshVenueTypeOptions();
|
refreshVenueTypeOptions();
|
||||||
refreshLineOptions();
|
refreshLineOptions();
|
||||||
}, [refreshVenueTypeOptions, refreshLineOptions]);
|
refreshInstitutionOptions();
|
||||||
|
}, [refreshVenueTypeOptions, refreshLineOptions, refreshInstitutionOptions]);
|
||||||
|
|
||||||
|
const handleCreateInstitution = async (values: JobFairInstitutionForm) => {
|
||||||
|
const res = await addJobFairInstitution(values);
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
await refreshInstitutionOptions();
|
||||||
|
message.success('机构新增成功');
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
message.error(res.msg || '机构新增失败');
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
const handleCreateDictOption = async (values: VenueInfoDictForm) => {
|
const handleCreateDictOption = async (values: VenueInfoDictForm) => {
|
||||||
const res = await addVenueInfoDictOption(values);
|
const res = await addVenueInfoDictOption(values);
|
||||||
@@ -118,6 +146,12 @@ const VenueInfoList: React.FC = () => {
|
|||||||
dataIndex: 'venueAddress',
|
dataIndex: 'venueAddress',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '所属机构',
|
||||||
|
dataIndex: 'institutionNames',
|
||||||
|
ellipsis: true,
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '联系电话',
|
title: '联系电话',
|
||||||
dataIndex: 'contactPhone',
|
dataIndex: 'contactPhone',
|
||||||
@@ -234,6 +268,9 @@ const VenueInfoList: React.FC = () => {
|
|||||||
values={currentRow}
|
values={currentRow}
|
||||||
venueTypeOptions={venueTypeOptions}
|
venueTypeOptions={venueTypeOptions}
|
||||||
lineOptions={lineOptions}
|
lineOptions={lineOptions}
|
||||||
|
institutionOptions={institutionOptions}
|
||||||
|
canCreateInstitution={access.hasPerms('cms:jobFairInstitution:add')}
|
||||||
|
onCreateInstitution={handleCreateInstitution}
|
||||||
defaultHandler={defaultHandler}
|
defaultHandler={defaultHandler}
|
||||||
onCreateDictOption={handleCreateDictOption}
|
onCreateDictOption={handleCreateDictOption}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export interface PublicJobFairJobForm {
|
|||||||
export interface CrossDomainJobItem {
|
export interface CrossDomainJobItem {
|
||||||
jobId: number;
|
jobId: number;
|
||||||
jobTitle: string;
|
jobTitle: string;
|
||||||
|
view?: number;
|
||||||
minSalary?: number;
|
minSalary?: number;
|
||||||
maxSalary?: number;
|
maxSalary?: number;
|
||||||
education?: string;
|
education?: string;
|
||||||
@@ -49,6 +50,10 @@ export interface CrossDomainJobItem {
|
|||||||
export interface CrossDomainJobParams {
|
export interface CrossDomainJobParams {
|
||||||
/** 逗号分隔的城市名称 */
|
/** 逗号分隔的城市名称 */
|
||||||
cityNames?: string;
|
cityNames?: string;
|
||||||
|
/** 招聘会起始日期(含当天) */
|
||||||
|
startDate?: string;
|
||||||
|
/** 招聘会起始日期(含当天) */
|
||||||
|
endDate?: string;
|
||||||
current?: number;
|
current?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
}
|
}
|
||||||
@@ -69,6 +74,30 @@ export async function getCrossDomainJobs(params?: CrossDomainJobParams) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 查询跨域线上招聘会的去重统计。 */
|
||||||
|
export interface CrossDomainStatisticsParams {
|
||||||
|
startDate: string;
|
||||||
|
endDate: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CrossDomainStatistics {
|
||||||
|
fairCount: number;
|
||||||
|
companyCount: number;
|
||||||
|
jobCount: number;
|
||||||
|
demandCount: number;
|
||||||
|
viewCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCrossDomainStatistics(params: CrossDomainStatisticsParams) {
|
||||||
|
return request<{ code: number; msg?: string; data?: CrossDomainStatistics }>(
|
||||||
|
`${BASE_URL}/cross-domain-statistics`,
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
params,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** 查询招聘会列表 */
|
/** 查询招聘会列表 */
|
||||||
export async function getPublicJobFairList(params?: API.PublicJobFair.ListParams) {
|
export async function getPublicJobFairList(params?: API.PublicJobFair.ListParams) {
|
||||||
return request<API.PublicJobFair.ListResult>(`${BASE_URL}/list`, {
|
return request<API.PublicJobFair.ListResult>(`${BASE_URL}/list`, {
|
||||||
|
|||||||
72
src/services/jobportal/jobFairInstitution.ts
Normal file
72
src/services/jobportal/jobFairInstitution.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { request } from '@umijs/max';
|
||||||
|
|
||||||
|
export interface JobFairInstitutionItem {
|
||||||
|
id: number;
|
||||||
|
/** 机构名称 */
|
||||||
|
institutionName: string;
|
||||||
|
/** 机构简介 */
|
||||||
|
institutionIntro?: string;
|
||||||
|
createTime?: string;
|
||||||
|
updateTime?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JobFairInstitutionForm {
|
||||||
|
id?: number;
|
||||||
|
institutionName: string;
|
||||||
|
institutionIntro?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JobFairInstitutionListParams {
|
||||||
|
institutionName?: string;
|
||||||
|
current?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JobFairInstitutionListResult {
|
||||||
|
code: number;
|
||||||
|
msg?: string;
|
||||||
|
total: number;
|
||||||
|
rows: JobFairInstitutionItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JobFairInstitutionResult {
|
||||||
|
code: number;
|
||||||
|
msg?: string;
|
||||||
|
data?: JobFairInstitutionItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BASE_URL = '/api/cms/job-fair-institution';
|
||||||
|
|
||||||
|
export async function getJobFairInstitutionList(params?: JobFairInstitutionListParams) {
|
||||||
|
return request<JobFairInstitutionListResult>(`${BASE_URL}/list`, {
|
||||||
|
method: 'GET',
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getJobFairInstitution(id: number) {
|
||||||
|
return request<JobFairInstitutionResult>(`${BASE_URL}/${id}`, {
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addJobFairInstitution(data: JobFairInstitutionForm) {
|
||||||
|
return request<JobFairInstitutionResult>(BASE_URL, {
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateJobFairInstitution(data: JobFairInstitutionForm) {
|
||||||
|
return request<JobFairInstitutionResult>(BASE_URL, {
|
||||||
|
method: 'PUT',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteJobFairInstitution(ids: number | number[]) {
|
||||||
|
const idPath = Array.isArray(ids) ? ids.join(',') : ids;
|
||||||
|
return request<JobFairInstitutionResult>(`${BASE_URL}/${idPath}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -27,6 +27,10 @@ export interface VenueInfoItem {
|
|||||||
handler?: string;
|
handler?: string;
|
||||||
/** 备注 */
|
/** 备注 */
|
||||||
remark?: string;
|
remark?: string;
|
||||||
|
/** 所属机构ID,支持多个 */
|
||||||
|
institutionIds?: number[];
|
||||||
|
/** 所属机构名称,多个用顿号分隔 */
|
||||||
|
institutionNames?: string;
|
||||||
createTime?: string;
|
createTime?: string;
|
||||||
updateTime?: string;
|
updateTime?: string;
|
||||||
}
|
}
|
||||||
@@ -63,6 +67,7 @@ export interface VenueInfoForm {
|
|||||||
handleOrg?: string;
|
handleOrg?: string;
|
||||||
handler?: string;
|
handler?: string;
|
||||||
remark?: string;
|
remark?: string;
|
||||||
|
institutionIds?: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 通用响应 */
|
/** 通用响应 */
|
||||||
@@ -103,6 +108,7 @@ const formatDate = (value: any) => {
|
|||||||
const toApiPayload = (data: VenueInfoForm) => ({
|
const toApiPayload = (data: VenueInfoForm) => ({
|
||||||
...data,
|
...data,
|
||||||
routeLineIds: Array.isArray(data.routeLineIds) ? data.routeLineIds.join(',') : data.routeLineIds,
|
routeLineIds: Array.isArray(data.routeLineIds) ? data.routeLineIds.join(',') : data.routeLineIds,
|
||||||
|
institutionIds: Array.isArray(data.institutionIds) ? data.institutionIds : [],
|
||||||
handleDate: formatDate(data.handleDate),
|
handleDate: formatDate(data.handleDate),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user