feat: 新增户外招聘会企业退订功能
Some checks failed
Node CI / build (14.x, macOS-latest) (push) Has been cancelled
Node CI / build (14.x, ubuntu-latest) (push) Has been cancelled
Node CI / build (14.x, windows-latest) (push) Has been cancelled
Node CI / build (16.x, macOS-latest) (push) Has been cancelled
Node CI / build (16.x, ubuntu-latest) (push) Has been cancelled
Node CI / build (16.x, windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
coverage CI / build (push) Has been cancelled
Node pnpm CI / build (16.x, macOS-latest) (push) Has been cancelled
Node pnpm CI / build (16.x, ubuntu-latest) (push) Has been cancelled
Node pnpm CI / build (16.x, windows-latest) (push) Has been cancelled

This commit is contained in:
2026-07-24 11:52:39 +08:00
parent 4db3255997
commit 25ba8dc0da
6 changed files with 188 additions and 1 deletions

View File

@@ -0,0 +1,63 @@
import React, { useRef } from 'react';
import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
import { getCompanyUnsubscribes } from '@/services/jobportal/outdoorFairDetail';
interface Props {
fairId: number;
}
const CompanyUnsubscribeTab: React.FC<Props> = ({ fairId }) => {
const actionRef = useRef<ActionType>();
const columns: ProColumns<API.OutdoorFairDetail.CompanyUnsubscribe>[] = [
{
title: '企业名称',
dataIndex: 'companyName',
ellipsis: true,
order: 1,
},
{ title: '联系人', dataIndex: 'contactPerson', width: 120, hideInSearch: true },
{ title: '联系电话', dataIndex: 'contactPhone', width: 150, hideInSearch: true },
{
title: '退订原因',
dataIndex: 'reason',
ellipsis: true,
hideInSearch: true,
render: (_, record) => <span title={record.reason}>{record.reason}</span>,
},
{
title: '退订时间',
dataIndex: 'unsubscribeTime',
valueType: 'dateTime',
width: 180,
hideInSearch: true,
},
];
return (
<ProTable<API.OutdoorFairDetail.CompanyUnsubscribe>
headerTitle="企业退订记录"
actionRef={actionRef}
rowKey="id"
columns={columns}
search={{ labelWidth: 'auto' }}
options={false}
scroll={{ x: 'max-content' }}
pagination={{ pageSize: 10 }}
request={async (params) => {
const res = await getCompanyUnsubscribes(fairId, {
current: params.current,
pageSize: params.pageSize,
companyName: params.companyName,
});
return {
data: res.rows || [],
total: res.total || 0,
success: res.code === 200,
};
}}
/>
);
};
export default CompanyUnsubscribeTab;

View File

@@ -9,6 +9,7 @@ import FairInfoTab from './components/FairInfoTab';
import BoothTab from './components/BoothTab'; import BoothTab from './components/BoothTab';
import ParticipatingCompaniesTab from './components/ParticipatingCompaniesTab'; import ParticipatingCompaniesTab from './components/ParticipatingCompaniesTab';
import ParticipatingJobsTab from './components/ParticipatingJobsTab'; import ParticipatingJobsTab from './components/ParticipatingJobsTab';
import CompanyUnsubscribeTab from './components/CompanyUnsubscribeTab';
import AttendeeTab from './components/AttendeeTab'; import AttendeeTab from './components/AttendeeTab';
import DeviceTab from './components/DeviceTab'; import DeviceTab from './components/DeviceTab';
@@ -42,6 +43,7 @@ const OutdoorFairDetail: React.FC = () => {
const tabItems = [ const tabItems = [
{ key: 'fair-info', label: '招聘会管理' }, { key: 'fair-info', label: '招聘会管理' },
{ key: 'participating-companies', label: '参会企业' }, { key: 'participating-companies', label: '参会企业' },
{ key: 'company-unsubscribe', label: '企业退订' },
{ key: 'participating-jobs', label: '参会岗位' }, { key: 'participating-jobs', label: '参会岗位' },
{ key: 'attendee', label: '参会人员管理' }, { key: 'attendee', label: '参会人员管理' },
{ key: 'device', label: '设备管理' }, { key: 'device', label: '设备管理' },
@@ -86,6 +88,8 @@ const OutdoorFairDetail: React.FC = () => {
return <BoothTab fairId={fairId} fairInfo={fairInfo} />; return <BoothTab fairId={fairId} fairInfo={fairInfo} />;
case 'participating-companies': case 'participating-companies':
return <ParticipatingCompaniesTab fairId={fairId} />; return <ParticipatingCompaniesTab fairId={fairId} />;
case 'company-unsubscribe':
return <CompanyUnsubscribeTab fairId={fairId} />;
case 'participating-jobs': case 'participating-jobs':
return <ParticipatingJobsTab fairId={fairId} />; return <ParticipatingJobsTab fairId={fairId} />;
case 'attendee': case 'attendee':

View File

@@ -1,6 +1,6 @@
import React, { useCallback, useEffect, useRef, useState } from 'react'; import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useAccess, history, useModel } from '@umijs/max'; import { useAccess, history, useModel } from '@umijs/max';
import { Button, message, Modal, QRCode, Tag, Typography } from 'antd'; import { Button, Form, Input, message, Modal, QRCode, Tag, Typography } from 'antd';
import { ActionType, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components'; import { ActionType, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
import { import {
PlusOutlined, PlusOutlined,
@@ -18,6 +18,7 @@ import {
deleteOutdoorFair, deleteOutdoorFair,
addOutdoorFairDictOption, addOutdoorFairDictOption,
signupOutdoorFair, signupOutdoorFair,
unsubscribeOutdoorFair,
getMyOutdoorFairQrCode, getMyOutdoorFairQrCode,
getOutdoorFairMiniProgramQrCode, getOutdoorFairMiniProgramQrCode,
exportOutdoorFair, exportOutdoorFair,
@@ -63,6 +64,10 @@ const OutdoorFairList: React.FC = () => {
const [miniProgramQrLoadingFairId, setMiniProgramQrLoadingFairId] = useState<number>(); const [miniProgramQrLoadingFairId, setMiniProgramQrLoadingFairId] = useState<number>();
const [rejectReasonModalOpen, setRejectReasonModalOpen] = useState(false); const [rejectReasonModalOpen, setRejectReasonModalOpen] = useState(false);
const [rejectReason, setRejectReason] = useState(''); const [rejectReason, setRejectReason] = useState('');
const [unsubscribeModalOpen, setUnsubscribeModalOpen] = useState(false);
const [unsubscribeFair, setUnsubscribeFair] = useState<OutdoorFairItem | null>(null);
const [unsubscribeSubmitting, setUnsubscribeSubmitting] = useState(false);
const [unsubscribeForm] = Form.useForm();
const [companyDetailFair, setCompanyDetailFair] = useState<OutdoorFairItem | null>(null); const [companyDetailFair, setCompanyDetailFair] = useState<OutdoorFairItem | null>(null);
const [companyBoothFair, setCompanyBoothFair] = useState<OutdoorFairItem | null>(null); const [companyBoothFair, setCompanyBoothFair] = useState<OutdoorFairItem | null>(null);
const [fairTypeValueEnum, setFairTypeValueEnum] = useState<Record<string, any>>({}); const [fairTypeValueEnum, setFairTypeValueEnum] = useState<Record<string, any>>({});
@@ -234,6 +239,39 @@ const OutdoorFairList: React.FC = () => {
setRejectReasonModalOpen(true); setRejectReasonModalOpen(true);
}; };
const openUnsubscribe = (record: OutdoorFairItem) => {
setUnsubscribeFair(record);
unsubscribeForm.resetFields();
setUnsubscribeModalOpen(true);
};
const handleUnsubscribe = async () => {
let values: { reason: string };
try {
values = await unsubscribeForm.validateFields();
} catch {
return;
}
if (!unsubscribeFair) return;
setUnsubscribeSubmitting(true);
try {
const res = await unsubscribeOutdoorFair(unsubscribeFair.id, values.reason);
if (res.code === 200) {
message.success('退订成功');
setUnsubscribeModalOpen(false);
setUnsubscribeFair(null);
unsubscribeForm.resetFields();
actionRef.current?.reload();
} else {
message.error(res.msg || '退订失败');
}
} catch {
message.error('退订失败,请稍后重试');
} finally {
setUnsubscribeSubmitting(false);
}
};
const openCompanyDetail = (record: OutdoorFairItem) => { const openCompanyDetail = (record: OutdoorFairItem) => {
setCompanyDetailFair(record); setCompanyDetailFair(record);
}; };
@@ -448,6 +486,20 @@ const OutdoorFairList: React.FC = () => {
</Button> </Button>
) : null, ) : null,
isEnterprise &&
record.signedUp &&
record.reviewStatus === '1' &&
access.hasPerms('cms:outdoorFair:unsubscribe') ? (
<Button
key="unsubscribe"
type="link"
size="small"
danger
onClick={() => openUnsubscribe(record)}
>
退
</Button>
) : null,
isEnterprise ? ( isEnterprise ? (
<Button <Button
key="companyDetail" key="companyDetail"
@@ -683,6 +735,31 @@ const OutdoorFairList: React.FC = () => {
{rejectReason} {rejectReason}
</Typography.Paragraph> </Typography.Paragraph>
</Modal> </Modal>
<Modal
title={unsubscribeFair ? `退订招聘会 — ${unsubscribeFair.title}` : '退订招聘会'}
open={unsubscribeModalOpen}
destroyOnClose
confirmLoading={unsubscribeSubmitting}
onOk={handleUnsubscribe}
okText="确认退订"
cancelText="取消"
onCancel={() => {
if (unsubscribeSubmitting) return;
setUnsubscribeModalOpen(false);
setUnsubscribeFair(null);
unsubscribeForm.resetFields();
}}
>
<Form form={unsubscribeForm} layout="vertical">
<Form.Item
name="reason"
label="退订原因"
rules={[{ required: true, whitespace: true, message: '请填写退订原因' }]}
>
<Input.TextArea rows={5} maxLength={500} showCount placeholder="请说明退订原因" />
</Form.Item>
</Form>
</Modal>
<CompanyFairDetailModal <CompanyFairDetailModal
fair={companyDetailFair} fair={companyDetailFair}
open={!!companyDetailFair} open={!!companyDetailFair}

View File

@@ -321,6 +321,17 @@ export async function signupOutdoorFair(fairId: number) {
); );
} }
/** 当前企业退订已审核通过的户外招聘会。 */
export async function unsubscribeOutdoorFair(fairId: number, reason: string) {
return request<{ code: number; msg?: string }>(
`${CMS_OUTDOOR_FAIR_BASE}/${fairId}/company/unsubscribe`,
{
method: 'POST',
data: { reason },
},
);
}
/** /**
* 获取当前企业户外招聘会二维码 * 获取当前企业户外招聘会二维码
* GET /api/cms/outdoor-fair/{fairId}/company/qrcode * GET /api/cms/outdoor-fair/{fairId}/company/qrcode

View File

@@ -1027,6 +1027,26 @@ export async function getParticipatingCompanies(
); );
} }
/** 获取招聘会企业退订记录。 */
export async function getCompanyUnsubscribes(
fairId: number,
params?: {
current?: number;
pageSize?: number;
companyName?: string;
},
) {
return request<{
code: number;
msg?: string;
total: number;
rows: API.OutdoorFairDetail.CompanyUnsubscribe[];
}>(`/api/cms/outdoor-fair/${fairId}/company-unsubscribes`, {
method: 'GET',
params,
});
}
/** 分页获取招聘会参会岗位,可按企业懒加载。 */ /** 分页获取招聘会参会岗位,可按企业懒加载。 */
export async function getParticipatingJobs( export async function getParticipatingJobs(
fairId: number, fairId: number,

View File

@@ -34,6 +34,18 @@ declare namespace API.OutdoorFairDetail {
jobList?: PostedJob[]; jobList?: PostedJob[];
} }
/** 企业退订记录 */
export interface CompanyUnsubscribe {
id: number;
fairId: number;
companyId: number;
companyName: string;
contactPerson?: string;
contactPhone?: string;
reason: string;
unsubscribeTime: string;
}
/** 参会企业二维码信息 */ /** 参会企业二维码信息 */
export interface CompanyQrCodeInfo { export interface CompanyQrCodeInfo {
fairId: number; fairId: number;