first commit
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:
chenshaohua
2025-11-10 16:28:01 +08:00
commit 30c8a7e8cf
465 changed files with 102912 additions and 0 deletions

View File

@@ -0,0 +1,74 @@
import { Modal } from 'antd';
import React, { useEffect, useRef } from 'react';
import * as echarts from 'echarts';
export type ListFormProps = {
onClose: (flag?: boolean, formVals?: unknown) => void;
open: boolean;
values?: Partial<API.MobileUser.ListRow>;
matching?: any;
};
const listEdit: React.FC<ListFormProps> = (props) => {
const { open, values, matching } = props;
const mapRef = useRef(null);
useEffect(() => {
console.log(matching);
if (matching && open) {
const myCharts = echarts.init(mapRef.current);
const { educationMatch, maxSimilarity, salaryMatch, areaMatch } = matching;
// educationMatch
// maxSimilarity
// salaryMatch
// areaMatch
myCharts.setOption({
radar: {
shape: 'circle',
indicator: [
{ name: '学历', max: 1 },
{ name: '薪资', max: 1 },
{ name: '区域', max: 1 },
{ name: '内容', max: 1 },
],
},
series: [
{
name: '综合匹配度',
type: 'radar',
data: [
{
value: [educationMatch, salaryMatch, areaMatch, maxSimilarity],
name: 'Allocated Budget',
},
],
},
],
});
}
}, [matching]);
return (
<Modal
title="匹配详情"
width={400}
open={props.open}
footer={''}
onCancel={() => props.onClose()}
maskClosable={true}
>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<div style={{ width: '300px', height: '300px' }} ref={mapRef}></div>
</div>
</Modal>
);
};
export default listEdit;

View File

@@ -0,0 +1,94 @@
import {
ModalForm,
ProForm,
ProFormSelect,
ProFormText,
} from '@ant-design/pro-components';
import { Form } from 'antd';
import React, {useEffect, useState} from 'react';
export type ListFormProps = {
onSubmit: (values: API.ManagementList.Manage) => Promise<void>;
open: boolean;
values?: Partial<API.ManagementList.Manage>;
mode?: 'view' | 'edit' | 'create';
onClose: (flag?: boolean, formVals?: unknown) => void;
contractTermEnum: any;
jobId:String;
};
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>();
useEffect(() => {
if (props.open && props.values) {
const defaultValues = {
entryDate: props.values.entryDate || new Date().toISOString().split('T')[0],
contactPerson:props.values.name,
idCard:props.values.idCard,
contactPersonPhone:props.values.phone,
jobId:props.jobId,
applyId:props.values.applyId,
};
form.setFieldsValue(defaultValues);
} else if (!props.open) {
form.resetFields();
}
}, [form, props.values,props.open]);
const handleCancel = () => {
props.onClose();
form.resetFields();
};
const handleFinish = async (values: Record<string, any>) => {
props.onSubmit(values as API.Management.Hire);
};
const handleChange = (_: string, value: any) => {
form.setFieldValue('companyName', value.label);
};
return (
<ModalForm<API.ManagementList.Manage>
title="企业新增就业人员"
form={form}
autoFocusFirstInput
open={props.open}
modalProps={{
destroyOnClose: true,
onCancel: () => handleCancel(),
}}
submitTimeout={2000}
onFinish={handleFinish}
>
<ProForm.Group>
<ProFormText width="md" hidden name="companyId" />
<ProFormText width="md" hidden name="jobId" />
<ProFormText width="md" hidden name="applyId" />
<ProFormText width="md" name="contactPerson" label="姓名" placeholder="请输入姓名" />
<ProFormText width="md" name="idCard" label="身份证号" placeholder="请输入身份证号" />
<ProFormText width="md" name="entryDate" label="入职日期" placeholder="请输入入职日期" />
<ProFormText width="md" name="contactPersonPhone" label="联系电话" placeholder="请输入联系电话" />
<ProFormSelect
width="md"
name="contractTerm"
label={'劳动合同期限'}
valueEnum={props.contractTermEnum}
placeholder="请选择期限"
rules={[{ required: true, message: '请选择劳动合同期限!' }]}
/>
</ProForm.Group>
</ModalForm>
);
};
export default listEdit;

View File

@@ -0,0 +1,311 @@
import React, { Fragment, useEffect, useRef, useState } from 'react';
import { FormattedMessage, useAccess, useParams } from '@umijs/max';
import { Button, FormInstance, message } from 'antd';
import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
import { FormOutlined, PlusOutlined,AlignLeftOutlined } from '@ant-design/icons';
import { getDictValueEnum } from '@/services/system/dict';
import DictTag from '@/components/DictTag';
import { exportCmsAppUserExport } from '@/services/mobileusers/list';
import {
addCmsEmployeeConfirmList,
exportCmsJobCandidates,
getCmsJobIds,
} from '@/services/Management/list';
import similarityJobs from '@/utils/similarity_Job';
import Detail from './detail';
import Hire from './hire';
import ResumeView from './resumeView';
const handleExport = async (values: API.MobileUser.ListParams) => {
const hide = message.loading('正在导出');
try {
await exportCmsAppUserExport(values);
hide();
message.success('导出成功');
return true;
} catch (error) {
hide();
message.error('导出失败,请重试');
return false;
}
};
function ManagementList() {
const access = useAccess();
const formTableRef = useRef<FormInstance>();
const actionRef = useRef<ActionType>();
const [jobId, setJobId] = useState<string>();
const [educationEnum, setEducationEnum] = useState<any>([]);
const [experienceEnum, setExperienceEnum] = useState<any>([]);
const [areaEnum, setAreaEnum] = useState<any>([]);
const [sexEnum, setSexEnum] = useState<any>([]);
const [hotEnum, setHotEnum] = useState<any>([]);
const [politicalEnum, setPoliticalEnum] = useState<any>([]);
const [contractTermEnum, setContractTermEnum] = useState<any>([]);
const [currentRow, setCurrentRow] = useState<API.MobileUser.ListRow>();
const [modalVisible, setModalVisible] = useState<boolean>(false);
const [hireVisible, setHireVisible] = useState<boolean>(false);
const [jobInfo, setJobInfo] = useState({});
const [matchingDegree, setMatchingDegree] = useState<any>(null);
const [resumeVisible, setResumeVisible] = useState<boolean>(false);
const [hireAdd, setHireAdd] = useState<any>(null);
const [mode, setMode] = useState<'view' | 'edit' | 'create'>('create');
const params = useParams();
const id = params.id || '0';
useEffect(() => {
if (jobId !== id) {
setJobId(id);
getJobInfo(id);
}
}, [jobId, params]);
const getJobInfo = async (jobId: string) => {
let resData = await getCmsJobIds(jobId);
if (resData.code === 200) {
similarityJobs.setJobInfo(resData.data);
setJobInfo(resData.data);
}
};
useEffect(() => {
getDictValueEnum('education', true, true).then((data) => {
setEducationEnum(data);
});
getDictValueEnum('experience', true, true).then((data) => {
setExperienceEnum(data);
});
getDictValueEnum('area', true, true).then((data) => {
setAreaEnum(data);
});
getDictValueEnum('sys_user_sex', true).then((data) => {
setSexEnum(data);
});
getDictValueEnum('political_affiliation', true, true).then((data) => {
setPoliticalEnum(data);
});
getDictValueEnum('contract_term', true, true).then((data) => {
setContractTermEnum(data);
});
}, []);
const columns: ProColumns<API.MobileUser.ListRow>[] = [
{
title: '用户名',
dataIndex: 'name',
valueType: 'text',
align: 'center',
},
{
title: '期望薪资',
dataIndex: 'minSalary',
valueType: 'text',
hideInSearch: true,
align: 'center',
render: (_, record) => (
<>
{record.salaryMin}-{record.salaryMax}
</>
),
},
{
title: '出生日期',
dataIndex: 'birthDate',
valueType: 'text',
align: 'center',
hideInSearch: true,
},
{
title: '学历要求',
dataIndex: 'education',
valueType: 'select',
align: 'center',
valueEnum: educationEnum,
render: (_, record) => {
return <DictTag enums={educationEnum} value={record.education} />;
},
},
{
title: '区域',
dataIndex: 'area',
valueType: 'select',
align: 'center',
valueEnum: areaEnum,
render: (_, record) => {
return <DictTag enums={areaEnum} value={record.area} />;
},
},
{
title: '性别',
dataIndex: 'sex',
valueType: 'select',
align: 'center',
valueEnum: sexEnum,
render: (_, record) => {
return <DictTag enums={sexEnum} value={record.sex} />;
},
},
{
title: '政治面貌',
dataIndex: 'politicalAffiliation',
valueType: 'select',
align: 'center',
valueEnum: politicalEnum,
render: (_, record) => {
return <DictTag enums={politicalEnum} value={record.politicalAffiliation} />;
},
},
{
title: '匹配度',
dataIndex: 'minSalary',
valueType: 'text',
hideInSearch: true,
align: 'center',
render: (_, record) => (
<>{similarityJobs.calculationMatchingDegreeJob(record).overallMatch}</>
),
},
{
title: '操作',
hideInSearch: true,
align: 'center',
dataIndex: 'jobId',
width: 200,
render: (jobId, record) => [
<div key="first-row" style={{marginBottom: 8, display: 'flex', justifyContent: 'center'}}>
<Button
type="link"
size="small"
key="view-resume"
icon={<AlignLeftOutlined/>}
hidden={!access.hasPerms('area:business:List.add')}
onClick={() => {
setCurrentRow(record);
setResumeVisible(true);
}}
>
</Button>
<Button
type="link"
size="small"
key="edit"
icon={<FormOutlined/>}
hidden={!access.hasPerms('area:business:List.add')}
onClick={() => {
setCurrentRow(record);
setHireVisible(true);
setMode('create');
}}
>
</Button>
<Button
type="link"
size="small"
key="edit"
icon={<AlignLeftOutlined/>}
hidden={!access.hasPerms('area:business:List.update')}
onClick={() => {
setModalVisible(true);
setCurrentRow(record);
const val = similarityJobs.calculationMatchingDegreeJob(record);
setMatchingDegree(val);
}}
>
</Button>
</div>
],
},
];
return (
<Fragment>
<div style={{width: '100%', float: 'right'}}>
<ProTable<API.MobileUser.ListRow>
// params 是需要自带的参数
// 这个参数优先级更高,会覆盖查询表单的参数
actionRef={actionRef}
formRef={formTableRef}
rowKey="jobId"
key="index"
columns={columns}
request={(params) =>
exportCmsJobCandidates(jobId).then((res) => {
// const v = similarityJobs.calculationMatchingDegreeJob(res.rows[0]);
// console.log(v);
const result = {
data: res.rows,
total: res.total,
success: true,
};
return result;
})
}
toolBarRender={() => [
<Button
type="primary"
key="export"
hidden={!access.hasPerms('system:user:export')}
onClick={async () => {
const searchVal = formTableRef.current && formTableRef.current.getFieldsValue();
handleExport(searchVal as API.MobileUser.ListParams);
}}
>
<PlusOutlined/>
<FormattedMessage id="pages.searchTable.export" defaultMessage="导出"/>
</Button>,
]}
/>
<Detail
values={currentRow}
open={modalVisible}
matching={matchingDegree}
onClose={() => {
console.log('close');
setModalVisible(false);
setCurrentRow(undefined);
setMatchingDegree(null);
}}
></Detail>
<Hire
values={currentRow}
open={hireVisible}
contractTermEnum={contractTermEnum}
jobId={jobId}
onClose={() => {
console.log('close');
setHireVisible(false);
setCurrentRow(undefined);
setHireAdd(null);
}}
onSubmit={async (values) => {
let resData;
resData = await addCmsEmployeeConfirmList(values);
if (resData.code === 200) {
setHireVisible(false);
setCurrentRow(undefined);
message.success('新增成功');
if (actionRef.current) {
actionRef.current.reload();
}
}
}}
></Hire>
<ResumeView
values={currentRow}
open={resumeVisible}
onClose={() => {
setResumeVisible(false);
setCurrentRow(undefined);
}}
/>
</div>
</Fragment>
);
}
export default ManagementList;

View File

@@ -0,0 +1,105 @@
import {Modal, Card, Descriptions, List, Button} from 'antd';
import React, { useEffect, useRef,useState } from 'react';
import { ProDescriptions} from '@ant-design/pro-components';
import {
addCmsUserWorkExperiencesList
} from '@/services/Management/list';
export type ListFormProps = {
onClose: (flag?: boolean, formVals?: unknown) => void;
open: boolean;
values?: Partial<API.MobileUser.ListRow>;
matching?: any;
};
const listEdit: React.FC<ListFormProps> = (props) => {
const mapRef = useRef(null);
const [loading, setLoading] = useState(false);
const [contactInfoList, setContactInfoList] = useState<API.Management.WorkExperence[]>([]);
useEffect(() => {
if (props.open && props.values) {
console.log(props.values);
fetchResumeDetail();
}
}, [props.values,props.open]);
const fetchResumeDetail = async () => {
if (!props.values?.userId) return;
setLoading(true);
try {
const parm={userId:props.values.userId};
const response = await addCmsUserWorkExperiencesList(parm);
if (response.code === 200) {
setContactInfoList(response.rows);
}
} catch (error) {
console.error('获取经历详情失败:', error);
} finally {
setLoading(false);
}
};
return (
<Modal
open={props.open}
onCancel={() => props.onClose()}
title="简历详情"
width={800}
footer={[
<Button key="cancel" onClick={() => props.onClose()}>
</Button>
]}
>
<ProDescriptions
column={2}
loading={loading}
>
{
<ProDescriptions.Item
dataIndex="contactInfoList"
label=""
span={2}
render={() => {
if (loading) return '加载中...';
if (!Array.isArray(contactInfoList) || contactInfoList.length === 0) {
return <span style={{ color: '#999' }}></span>;
}
return (
<List
itemLayout="vertical"
dataSource={contactInfoList}
renderItem={(item, index) => (
<List.Item
style={{ marginBottom: 16 }}
>
<Card
title={`经历信息 #${index + 1}`}
bordered
style={{ width: '100%', boxShadow: '0 2px 8px rgba(0,0,0,0.1)' }}
>
<Descriptions column={2} size="small">
<Descriptions.Item label="公司名称">{item.companyName || '未填写'}</Descriptions.Item>
<Descriptions.Item label="职务">{item.position || '未填写'}</Descriptions.Item>
<Descriptions.Item label="开始时间">{item.startDate || '未填写'}</Descriptions.Item>
<Descriptions.Item label="截止时间">{item.endDate || '未填写'}</Descriptions.Item>
<Descriptions.Item label="描述" span={2}>
{item.description || '未填写'}
</Descriptions.Item>
</Descriptions>
</Card>
</List.Item>
)}
/>
);
}}
/>
}
</ProDescriptions>
</Modal>
);
};
export default listEdit;

View File

@@ -0,0 +1,432 @@
import {
ModalForm,
ProForm,
ProFormDigit,
ProFormSelect,
ProFormText,
ProFormTextArea,
ProDescriptions,
} from '@ant-design/pro-components';
import { Form, Button, Space } from 'antd';
import { PlusOutlined, MinusCircleOutlined } from '@ant-design/icons';
import React, { useEffect, 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 [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>) => {
props.onSubmit(values as API.ManagementList.Manage);
};
const handleChange = (_: string, value: any) => {
form.setFieldValue('companyName', value.label);
};
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' },
bodyStyle: {
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"
onChange={handleChange}
request={async ({ keyWords }) => {
let resData = await getCmsCompanyList({ name: keyWords });
return resData.rows.map((item) => ({ label: item.name, value: item.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 }}
>
<ProFormText 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 }}
>
<ProFormText placeholder="请输入联系电话" />
</Form.Item>
<Form.Item
{...restField}
name={[name, 'position']}
label="职务"
rules={[{ required: true, message: '请输入职务' }]}
style={{ marginBottom: 0, flex: 1 }}
>
<ProFormText 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;

View File

@@ -0,0 +1,400 @@
import React, { Fragment, useEffect, useRef, useState } from 'react';
import { FormattedMessage, history, useAccess } from '@umijs/max';
import {
addCmsJobList,
delCmsJobIds,
exportCmsJob,
exportJobApply,
getCmsJobList,
updateCmsJobList,
} from '@/services/Management/list';
import { Button, FormInstance, message, Modal, Switch } from 'antd';
import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
import { AlignLeftOutlined, BarChartOutlined, DeleteOutlined, FormOutlined, PlusOutlined,DownloadOutlined } from '@ant-design/icons';
import EditManageRow from './edit';
import { getDictValueEnum } from '@/services/system/dict';
import DictTag from '@/components/DictTag';
const handleExport = async (values: API.ManagementList.ListParams) => {
const hide = message.loading('正在导出');
try {
await exportCmsJob(values);
hide();
message.success('导出成功');
return true;
} catch (error) {
hide();
message.error('导出失败,请重试');
return false;
}
};
const lyrhandleExport = async (values: API.ManagementList.ListParams) => {
const hide = message.loading('正在导出');
try {
await exportJobApply(values);
hide();
message.success('导出成功');
return true;
} catch (error) {
hide();
message.error('导出失败,请重试');
return false;
}
};
const handleRemoveOne = async (jobId: string) => {
const hide = message.loading('正在删除');
if (!jobId) return true;
try {
const resp = await delCmsJobIds(jobId);
hide();
if (resp.code === 200) {
message.success('删除成功,即将刷新');
} else {
message.error(resp.msg);
}
return true;
} catch (error) {
hide();
message.error('删除失败,请重试');
return false;
}
};
function ManagementList() {
const access = useAccess();
const formTableRef = useRef<FormInstance>();
const actionRef = useRef<ActionType>();
const [educationEnum, setEducationEnum] = useState<any>([]);
const [experienceEnum, setExperienceEnum] = useState<any>([]);
const [areaEnum, setAreaEnum] = useState<any>([]);
const [isPublishEnum, setIsPublishEnum] = useState<any>([]);
const [jobTypeEnum, setJobTypeEnum] = useState<any>([]);
const [compensationEnum, setCompensationEnum] = useState<any>([]);
const [currentRow, setCurrentRow] = useState<API.ManagementList.Manage>();
const [modalVisible, setModalVisible] = useState<boolean>(false);
const [mode, setMode] = useState<'view' | 'edit' | 'create'>('create');
useEffect(() => {
getDictValueEnum('education', true, true).then((data) => {
setEducationEnum(data);
});
getDictValueEnum('experience', true, true).then((data) => {
setExperienceEnum(data);
});
getDictValueEnum('area', true, true).then((data) => {
setAreaEnum(data);
});
getDictValueEnum('is_publish', true).then((data) => {
setIsPublishEnum(data);
});
getDictValueEnum('compensation',true,true).then((data) => {
setCompensationEnum(data)
})
getDictValueEnum('job_type',true,true).then((data) => {
setJobTypeEnum(data)
})
}, []);
async function changeRelease(record: API.ManagementList.Manage) {
let resData = await updateCmsJobList({
jobId: record.jobId,
isPublish: record.isPublish === 1 ? 0 : 1,
});
if (resData.code === 200) {
message.success('修改成功');
if (actionRef.current) {
actionRef.current.reload();
}
}
}
const columns: ProColumns<API.ManagementList.Manage>[] = [
{
title: '岗位名称',
dataIndex: 'jobTitle',
valueType: 'text',
align: 'center',
},
{
title: '最大最小薪资',
dataIndex: 'maxSalary',
valueType: 'text',
hideInSearch: true,
align: 'center',
render: (_, record) => (
<>
{record.minSalary}-{record.maxSalary}
</>
),
},
{
title: '单位名称',
dataIndex: 'companyName',
valueType: 'text',
align: 'center',
},
{
title: '学历要求',
dataIndex: 'education',
valueType: 'select',
align: 'center',
valueEnum: educationEnum,
render: (_, record) => {
return <DictTag enums={educationEnum} value={record.education} />;
},
},
{
title: '经验要求',
dataIndex: 'experience',
valueType: 'select',
align: 'center',
valueEnum: experienceEnum,
render: (_, record) => {
return <DictTag enums={experienceEnum} value={record.experience} />;
},
},
{
title: '薪酬',
dataIndex: 'compensation',
valueType: 'select',
align: 'center',
hideInTable:true,
valueEnum: compensationEnum,
render: (_, record) => {
return (<DictTag enums={compensationEnum} value={record.isHot} />);
},
},
{
title: '信用代码',
dataIndex: 'code',
valueType: 'text',
hideInTable:true
},
{
title: '是否发布',
dataIndex: 'isPublish',
valueType: 'select',
align: 'center',
valueEnum: isPublishEnum,
render: (text, record) => (
<Switch checked={record.isPublish === 1} onChange={changeRelease.bind(null, record)} />
),
},
{
title: '招聘人数',
dataIndex: 'vacancies',
valueType: 'text',
align: 'center',
hideInSearch: true,
},
{
title: '岗位来源',
dataIndex: 'jobType',
valueType: 'text',
align: 'center',
hideInSearch: true,
render: (_, record) => {
const isJiangNei = record.jobType === '0';
return (
<span
style={{
padding: '4px 8px',
borderRadius: '4px',
fontSize: '12px',
fontWeight: 500,
backgroundColor: isJiangNei ? '#e6f7ff' : '#fff2e8',
color: isJiangNei ? '#1890ff' : '#fa8c16',
border: isJiangNei ? '1px solid #91d5ff' : '1px solid #ffd591',
}}
>
{isJiangNei ? '疆内' : '疆外'}
</span>
);
},
},
{
title: '浏览量',
dataIndex: 'view',
valueType: 'text',
align: 'center',
hideInSearch: true,
},
{
title: '操作',
hideInSearch: true,
align: 'center',
dataIndex: 'jobId',
width: 300,
render: (jobId, record) => [
<div key="first-row" style={{ marginBottom: 8, display: 'flex', justifyContent: 'center' }}>
<Button
type="link"
size="small"
key="view"
icon={<AlignLeftOutlined />}
hidden={!access.hasPerms('bussiness:job:query')}
onClick={() => {
setCurrentRow(record);
setModalVisible(true);
setMode('view');
}}
>
</Button>
<Button
type="link"
size="small"
key="edit"
icon={<BarChartOutlined />}
hidden={!access.hasPerms('bussiness:job:candidates')}
onClick={() => history.push(`/management/see-matching/index/${record.jobId}`)}
>
</Button>
</div>,
<div key="second-row" style={{ display: 'flex', justifyContent: 'space-evenly'}}>
<Button
type="link"
size="small"
key="edit"
icon={<FormOutlined />}
hidden={!access.hasPerms('bussiness:job:edit')}
onClick={() => {
setCurrentRow(record);
setModalVisible(true);
setMode('edit');
}}
>
</Button>
<Button
type="link"
size="small"
danger
key="batchRemove"
icon={<DeleteOutlined />}
hidden={!access.hasPerms('bussiness:job:remove')}
onClick={async () => {
Modal.confirm({
title: '删除',
content: '确定删除该项吗?',
okText: '确认',
cancelText: '取消',
onOk: async () => {
const success = await handleRemoveOne(jobId as string);
if (success) {
if (actionRef.current) {
actionRef.current.reload();
}
}
},
});
}}
>
</Button>
</div>
],
},
];
return (
<Fragment>
<div style={{ width: '100%', float: 'right' }}>
<ProTable<API.ManagementList.Manage>
// params 是需要自带的参数
// 这个参数优先级更高,会覆盖查询表单的参数
actionRef={actionRef}
formRef={formTableRef}
rowKey="jobId"
key="index"
columns={columns}
request={(params) =>
getCmsJobList({ ...params } as API.ManagementList.ListParams).then((res) => {
console.log(params);
const result = {
data: res.rows,
total: res.total,
success: true,
};
return result;
})
}
toolBarRender={() => [
<Button
type="primary"
key="add"
hidden={!access.hasPerms('manage:List:add')}
onClick={async () => {
setCurrentRow(undefined);
setModalVisible(true);
setMode('create');
}}
>
<PlusOutlined />
</Button>,
<Button
type="primary"
key="export"
hidden={!access.hasPerms('system:user:export')}
onClick={async () => {
const searchVal = formTableRef.current && formTableRef.current.getFieldsValue();
handleExport(searchVal as API.ManagementList.ListParams);
}}
>
<DownloadOutlined />
<FormattedMessage id="pages.searchTable.export" defaultMessage="导出" />
</Button>,
<Button
type="primary"
key="export"
hidden={!access.hasPerms('cms:jobApply:export')}
onClick={async () => {
const searchVal = formTableRef.current && formTableRef.current.getFieldsValue();
lyrhandleExport(searchVal as API.ManagementList.ListParams);
}}
>
<DownloadOutlined />
</Button>,
]}
/>
</div>
<EditManageRow
open={modalVisible}
mode={mode}
onSubmit={async (values) => {
let resData;
if (values.jobId) {
resData = await updateCmsJobList(values);
} else {
resData = await addCmsJobList(values);
}
if (resData.code === 200) {
setModalVisible(false);
setCurrentRow(undefined);
if (values.jobId) {
message.success('修改成功');
} else {
message.success('新增成功');
}
if (actionRef.current) {
actionRef.current.reload();
}
}
}}
values={currentRow}
onCancel={() => {
setModalVisible(false);
setCurrentRow(undefined);
}}
educationEnum={educationEnum}
experienceEnum={experienceEnum}
areaEnum={areaEnum}
jobTypeEnum={jobTypeEnum}
></EditManageRow>
</Fragment>
);
}
export default ManagementList;