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
465 lines
16 KiB
TypeScript
465 lines
16 KiB
TypeScript
import {
|
||
ModalForm,
|
||
ProForm,
|
||
ProFormDigit,
|
||
ProFormSelect,
|
||
ProFormText,
|
||
ProFormTextArea,
|
||
ProDescriptions,
|
||
} from '@ant-design/pro-components';
|
||
import { Form, Button, Input } from 'antd';
|
||
import { PlusOutlined, MinusCircleOutlined } from '@ant-design/icons';
|
||
import React, { useEffect, useRef, useState } from 'react';
|
||
import { DictValueEnumObj } from '@/components/DictTag';
|
||
import { getCmsCompanyList } from '@/services/company/list';
|
||
import { getCmsJobDetail } from '@/services/Management/list';
|
||
|
||
export type ListFormProps = {
|
||
onCancel: (flag?: boolean, formVals?: unknown) => void;
|
||
onSubmit: (values: API.ManagementList.Manage) => Promise<void>;
|
||
open: boolean;
|
||
values?: Partial<API.ManagementList.Manage>;
|
||
educationEnum: DictValueEnumObj;
|
||
experienceEnum: DictValueEnumObj;
|
||
areaEnum: DictValueEnumObj;
|
||
jobTypeEnum: DictValueEnumObj;
|
||
mode?: 'view' | 'edit' | 'create';
|
||
};
|
||
|
||
const waitTime = (time: number = 100) => {
|
||
return new Promise((resolve) => {
|
||
setTimeout(() => {
|
||
resolve(true);
|
||
}, time);
|
||
});
|
||
};
|
||
|
||
const listEdit: React.FC<ListFormProps> = (props) => {
|
||
const [form] = Form.useForm<API.ManagementList.Manage>();
|
||
const companyNameMap = useRef<Record<number, string>>({});
|
||
const [jobDetail, setJobDetail] = useState<API.ManagementList.Manage | null>(null);
|
||
const [loading, setLoading] = useState(false);
|
||
const { educationEnum, experienceEnum, areaEnum, jobTypeEnum } = props;
|
||
const { mode = props.values ? 'edit' : 'create' } = props;
|
||
useEffect(() => {
|
||
if(props.open){
|
||
form.resetFields();
|
||
if (props.values) {
|
||
form.setFieldsValue({
|
||
...props.values,
|
||
jobLocationAreaCode: String(props.values.jobLocationAreaCode || ''),
|
||
});
|
||
}
|
||
}
|
||
}, [form, props.values?.jobId,props.open]);
|
||
|
||
// 在查看模式和编辑模式下调用API获取详情
|
||
useEffect(() => {
|
||
if (props.open && (mode === 'view' || mode === 'edit') && props.values?.jobId) {
|
||
fetchJobDetail();
|
||
}
|
||
}, [props.open, mode, props.values?.jobId]);
|
||
|
||
const fetchJobDetail = async () => {
|
||
if (!props.values?.jobId) return;
|
||
|
||
setLoading(true);
|
||
try {
|
||
const response = await getCmsJobDetail(props.values.jobId.toString());
|
||
if (response.code === 200) {
|
||
setJobDetail(response.data);
|
||
// 如果是编辑模式,将获取到的详情数据设置到表单中
|
||
if (mode === 'edit') {
|
||
form.setFieldsValue({
|
||
...response.data,
|
||
jobLocationAreaCode: String(response.data.jobLocationAreaCode || ''),
|
||
});
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('获取岗位详情失败:', error);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
const handleCancel = () => {
|
||
props.onCancel();
|
||
form.resetFields();
|
||
};
|
||
|
||
const handleFinish = async (values: Record<string, any>) => {
|
||
const payload: API.ManagementList.Manage = {
|
||
jobTitle: values.jobTitle,
|
||
companyId: values.companyId,
|
||
companyName:
|
||
typeof values.companyName === 'string'
|
||
? values.companyName
|
||
: companyNameMap.current[values.companyId] ?? '',
|
||
minSalary: values.minSalary,
|
||
maxSalary: values.maxSalary,
|
||
education: values.education,
|
||
experience: values.experience,
|
||
jobLocationAreaCode: values.jobLocationAreaCode,
|
||
vacancies: values.vacancies,
|
||
jobType: values.jobType,
|
||
jobLocation: values.jobLocation,
|
||
description: values.description,
|
||
jobContactList: (values.jobContactList ?? []).map((item: API.ManagementList.ContactPerson) => ({
|
||
contactPerson: item.contactPerson,
|
||
contactPersonPhone: item.contactPersonPhone,
|
||
position: item.position,
|
||
})),
|
||
};
|
||
if (mode === 'edit' && values.jobId) {
|
||
payload.jobId = values.jobId;
|
||
}
|
||
await props.onSubmit(payload);
|
||
return true;
|
||
};
|
||
if (mode === 'view') {
|
||
return (
|
||
<ModalForm
|
||
title="岗位详情"
|
||
open={props.open}
|
||
width={800}
|
||
modalProps={{
|
||
destroyOnClose: true,
|
||
onCancel: () => handleCancel(),
|
||
footer: null,
|
||
}}
|
||
submitter={false}
|
||
>
|
||
<ProDescriptions<API.ManagementList.Manage>
|
||
column={2}
|
||
dataSource={jobDetail || props.values || {}}
|
||
loading={loading}
|
||
>
|
||
<ProDescriptions.Item dataIndex="jobTitle" label="岗位名称" />
|
||
<ProDescriptions.Item dataIndex="companyName" label="招聘公司" />
|
||
<ProDescriptions.Item dataIndex="minSalary" label="最低薪资(元/月)" />
|
||
<ProDescriptions.Item dataIndex="maxSalary" label="最高薪资(元/月)" />
|
||
<ProDescriptions.Item
|
||
dataIndex="education"
|
||
label="学历要求"
|
||
valueEnum={educationEnum}
|
||
/>
|
||
<ProDescriptions.Item
|
||
dataIndex="experience"
|
||
label="工作经验"
|
||
valueEnum={experienceEnum}
|
||
/>
|
||
<ProDescriptions.Item
|
||
dataIndex="jobLocationAreaCode"
|
||
label="工作区县"
|
||
valueEnum={areaEnum}
|
||
/>
|
||
<ProDescriptions.Item dataIndex="vacancies" label="招聘人数" />
|
||
<ProDescriptions.Item dataIndex="jobLocation" label="工作地点" />
|
||
<ProDescriptions.Item
|
||
dataIndex="jobType"
|
||
label="岗位类型"
|
||
valueEnum={jobTypeEnum}
|
||
/>
|
||
<ProDescriptions.Item
|
||
dataIndex="description"
|
||
label="岗位描述"
|
||
span={2} // 跨两列显示
|
||
/>
|
||
<ProDescriptions.Item
|
||
dataIndex="company"
|
||
label="公司信息"
|
||
span={2}
|
||
render={(_, record) => {
|
||
const company = record?.company;
|
||
if (!company) {
|
||
return <span style={{ color: '#999' }}>暂无公司信息</span>;
|
||
}
|
||
return (
|
||
<div style={{ width: '100%' }}>
|
||
<div style={{ marginBottom: 8 }}>
|
||
<strong style={{ color: '#000', marginRight: 16 }}>公司名称:</strong>
|
||
<span style={{ color: '#000' }}>{company.name}</span>
|
||
</div>
|
||
<div style={{ marginBottom: 8 }}>
|
||
<strong style={{ color: '#000', marginRight: 16 }}>公司地址:</strong>
|
||
<span style={{ color: '#000' }}>{company.location}</span>
|
||
</div>
|
||
<div style={{ marginBottom: 8 }}>
|
||
<strong style={{ color: '#000', marginRight: 16 }}>公司规模:</strong>
|
||
<span style={{ color: '#000' }}>{company.scale}</span>
|
||
</div>
|
||
<div style={{ marginBottom: 8 }}>
|
||
<strong style={{ color: '#000', marginRight: 16 }}>公司性质:</strong>
|
||
<span style={{ color: '#000' }}>{company.nature}</span>
|
||
</div>
|
||
{company.description && (
|
||
<div style={{ marginBottom: 8 }}>
|
||
<strong style={{ color: '#000', marginRight: 16 }}>公司描述:</strong>
|
||
<span style={{ color: '#000' }}>{company.description}</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}}
|
||
/>
|
||
<ProDescriptions.Item
|
||
dataIndex="jobContactList"
|
||
label="联系人信息"
|
||
span={2}
|
||
render={(_, record) => {
|
||
const contactList = record?.jobContactList || [];
|
||
if (!contactList || contactList.length === 0) {
|
||
return <span style={{ color: '#999' }}>暂无联系人信息</span>;
|
||
}
|
||
return (
|
||
<div style={{ width: '100%' }}>
|
||
<table style={{
|
||
width: '100%',
|
||
borderCollapse: 'collapse',
|
||
border: '1px solid #d9d9d9',
|
||
borderRadius: '6px',
|
||
overflow: 'hidden'
|
||
}}>
|
||
<thead>
|
||
<tr style={{ backgroundColor: '#fafafa' }}>
|
||
<th style={{
|
||
padding: '12px 16px',
|
||
textAlign: 'left',
|
||
borderBottom: '1px solid #d9d9d9',
|
||
fontWeight: 600,
|
||
color: '#000'
|
||
}}>
|
||
联系人姓名
|
||
</th>
|
||
<th style={{
|
||
padding: '12px 16px',
|
||
textAlign: 'left',
|
||
borderBottom: '1px solid #d9d9d9',
|
||
fontWeight: 600,
|
||
color: '#000'
|
||
}}>
|
||
职务
|
||
</th>
|
||
<th style={{
|
||
padding: '12px 16px',
|
||
textAlign: 'left',
|
||
borderBottom: '1px solid #d9d9d9',
|
||
fontWeight: 600,
|
||
color: '#000'
|
||
}}>
|
||
联系电话
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{contactList.map((contact: any, index: number) => (
|
||
<tr key={index} style={{ backgroundColor: index % 2 === 0 ? '#fff' : '#fafafa' }}>
|
||
<td style={{
|
||
padding: '12px 16px',
|
||
borderBottom: '1px solid #f0f0f0',
|
||
color: '#000'
|
||
}}>
|
||
{contact.contactPerson}
|
||
</td>
|
||
<td style={{
|
||
padding: '12px 16px',
|
||
borderBottom: '1px solid #f0f0f0',
|
||
color: '#000'
|
||
}}>
|
||
{contact.position}
|
||
</td>
|
||
<td style={{
|
||
padding: '12px 16px',
|
||
borderBottom: '1px solid #f0f0f0',
|
||
color: '#000',
|
||
fontWeight: 500
|
||
}}>
|
||
{contact.contactPersonPhone}
|
||
</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
);
|
||
}}
|
||
/>
|
||
</ProDescriptions>
|
||
</ModalForm>
|
||
);
|
||
}
|
||
return (
|
||
<ModalForm<API.ManagementList.Manage>
|
||
title={mode === 'edit' ? '编辑岗位' : '新建岗位'}
|
||
form={form}
|
||
autoFocusFirstInput
|
||
open={props.open}
|
||
width={900}
|
||
grid={true}
|
||
rowProps={{ gutter: [16, 16] }}
|
||
colProps={{ span: 12 }}
|
||
modalProps={{
|
||
destroyOnClose: true,
|
||
onCancel: () => handleCancel(),
|
||
style: { height: '80vh' },
|
||
styles: {
|
||
body: {
|
||
height: 'calc(80vh - 120px)',
|
||
overflowY: 'auto',
|
||
padding: '16px 24px',
|
||
},
|
||
},
|
||
}}
|
||
submitTimeout={2000}
|
||
onFinish={handleFinish}
|
||
>
|
||
<ProFormText width="md" hidden name="jobId" />
|
||
<ProFormText width="md" hidden name="companyName" />
|
||
<ProFormText width="md" name="jobTitle" label="岗位名称" placeholder="请输入岗位名称" />
|
||
<ProFormSelect
|
||
showSearch
|
||
width="md"
|
||
name="companyId"
|
||
request={async ({ keyWords }) => {
|
||
const resData = await getCmsCompanyList({ name: keyWords });
|
||
return resData.rows.map((item) => {
|
||
companyNameMap.current[item.companyId] = item.name;
|
||
return { label: item.name, value: item.companyId };
|
||
});
|
||
}}
|
||
fieldProps={{
|
||
onChange: (companyId: number) => {
|
||
form.setFieldValue('companyName', companyNameMap.current[companyId] ?? '');
|
||
},
|
||
}}
|
||
placeholder="请输入公司名称选择公司"
|
||
rules={[{ required: true, message: '请输入公司名称选择公司!' }]}
|
||
label="招聘公司"
|
||
/>
|
||
<ProFormDigit
|
||
name="minSalary"
|
||
width="md"
|
||
min={0}
|
||
label="最小薪资(元/月)"
|
||
placeholder="请输入最小薪资(元)"
|
||
/>
|
||
<ProFormDigit
|
||
name="maxSalary"
|
||
width="md"
|
||
min={0}
|
||
label="最大薪资(元/月)"
|
||
placeholder="请输入最大薪资(元)"
|
||
/>
|
||
<ProFormSelect
|
||
width="md"
|
||
name="education"
|
||
label={'学历要求'}
|
||
valueEnum={educationEnum}
|
||
placeholder="请选择学历要求"
|
||
rules={[{ required: true, message: '请选择学历要求!' }]}
|
||
/>
|
||
<ProFormSelect
|
||
width="md"
|
||
name="experience"
|
||
label={'工作经验'}
|
||
valueEnum={experienceEnum}
|
||
placeholder="请选择岗位"
|
||
rules={[{ required: true, message: '请选择工作经验!' }]}
|
||
/>
|
||
<ProFormSelect
|
||
width="md"
|
||
name="jobLocationAreaCode"
|
||
label={'工作区县'}
|
||
valueEnum={areaEnum}
|
||
placeholder="请选择区县"
|
||
rules={[{ required: true, message: '请选择区县!' }]}
|
||
/>
|
||
<ProFormDigit
|
||
label="招聘人数"
|
||
name="vacancies"
|
||
width="md"
|
||
min={0}
|
||
placeholder="请输入招聘人数"
|
||
/>
|
||
<ProFormSelect
|
||
width="md"
|
||
name="jobType"
|
||
label={'岗位类型'}
|
||
valueEnum={jobTypeEnum}
|
||
placeholder="请选择类型"
|
||
rules={[{ required: true, message: '请选择类型!' }]}
|
||
/>
|
||
<ProFormText width="md" name="jobLocation" label="工作地点" placeholder="请输入工作地点" />
|
||
|
||
<ProFormTextArea
|
||
width="xl"
|
||
name="description"
|
||
label="岗位描述"
|
||
placeholder="请输入岗位描述"
|
||
colProps={{ span: 24 }}
|
||
/>
|
||
|
||
{/* 岗位联系人动态表单 - 绕过网格系统 */}
|
||
<div style={{ width: '100%', marginBottom: 16 }}>
|
||
<div style={{ marginBottom: 8, fontWeight: 500 }}>岗位联系人</div>
|
||
<Form.List name="jobContactList" initialValue={[{}]}>
|
||
{(fields, { add, remove }) => (
|
||
<>
|
||
{fields.map(({ key, name, ...restField }) => (
|
||
<div key={key} style={{ display: 'flex', width: '100%', marginBottom: 8, alignItems: 'center', gap: '8px' }}>
|
||
<Form.Item
|
||
{...restField}
|
||
name={[name, 'contactPerson']}
|
||
label="联系人姓名"
|
||
rules={[{ required: true, message: '请输入联系人姓名' }]}
|
||
style={{ marginBottom: 0, flex: 1 }}
|
||
>
|
||
<Input placeholder="请输入联系人姓名" />
|
||
</Form.Item>
|
||
<Form.Item
|
||
{...restField}
|
||
name={[name, 'contactPersonPhone']}
|
||
label="联系电话"
|
||
rules={[
|
||
{ required: true, message: '请输入联系人电话' },
|
||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码' }
|
||
]}
|
||
style={{ marginBottom: 0, flex: 1 }}
|
||
>
|
||
<Input placeholder="请输入联系电话" />
|
||
</Form.Item>
|
||
<Form.Item
|
||
{...restField}
|
||
name={[name, 'position']}
|
||
label="职务"
|
||
rules={[{ required: true, message: '请输入职务' }]}
|
||
style={{ marginBottom: 0, flex: 1 }}
|
||
>
|
||
<Input placeholder="请输入职务" />
|
||
</Form.Item>
|
||
<Button
|
||
type="text"
|
||
danger
|
||
icon={<MinusCircleOutlined />}
|
||
onClick={() => remove(name)}
|
||
style={{ marginBottom: 0 }}
|
||
/>
|
||
</div>
|
||
))}
|
||
<Form.Item>
|
||
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
|
||
添加联系人
|
||
</Button>
|
||
</Form.Item>
|
||
</>
|
||
)}
|
||
</Form.List>
|
||
</div>
|
||
|
||
</ModalForm>
|
||
);
|
||
};
|
||
|
||
export default listEdit;
|